diff --git a/.github/CODEOWNERS b/.github/CODEOWNERS deleted file mode 100644 index 00763eb56e0c6..0000000000000 --- a/.github/CODEOWNERS +++ /dev/null @@ -1,206 +0,0 @@ -/app/code/Magento/AdminNotification/ @paliarush -/app/code/Magento/Backend/ @paliarush -/app/code/Magento/User/ @paliarush -/lib/internal/Magento/Framework/App/ @buskamuza -/lib/internal/Magento/Framework/Controller/ @buskamuza -/lib/internal/Magento/Framework/Flag/ @buskamuza -/lib/internal/Magento/Framework/HTTP/ @buskamuza -/lib/internal/Magento/Framework/Logger/ @buskamuza -/lib/internal/Magento/Framework/Message/ @buskamuza -/lib/internal/Magento/Framework/Notification/ @buskamuza -/lib/internal/Magento/Framework/Session/ @buskamuza -/lib/internal/Magento/Framework/Url/ @buskamuza -/app/code/Magento/Cms/ @melnikovi -/app/code/Magento/CmsUrlRewrite/ @melnikovi -/app/code/Magento/Contact/ @melnikovi -/app/code/Magento/Email/ @melnikovi -/app/code/Magento/Variable/ @melnikovi -/app/code/Magento/Widget/ @melnikovi -/lib/internal/Magento/Framework/Cache/ @kokoc -/app/code/Magento/CacheInvalidate/ @kokoc -/app/code/Magento/CatalogInventory/ @tariqjawed83 @maghamed -/app/code/Magento/Bundle/ @akaplya -/app/code/Magento/BundleImportExport/ @akaplya -/app/code/Magento/Catalog/ @akaplya -/app/code/Magento/CatalogAnalytics/ @akaplya -/app/code/Magento/CatalogImportExport/ @akaplya -/app/code/Magento/CatalogSearch/ @kokoc -/app/code/Magento/CatalogUrlRewrite/ @akaplya -/app/code/Magento/ConfigurableImportExport/ @akaplya -/app/code/Magento/ConfigurableProduct/ @akaplya -/app/code/Magento/Downloadable/ @akaplya -/app/code/Magento/DownloadableImportExport/ @akaplya -/app/code/Magento/GroupedImportExport/ @akaplya -/app/code/Magento/GroupedProduct/ @akaplya -/app/code/Magento/LayeredNavigation/ @kokoc -/app/code/Magento/ProductVideo/ @akaplya -/app/code/Magento/Review/ @akaplya -/app/code/Magento/Swatches/ @akaplya -/app/code/Magento/SwatchesLayeredNavigation/ @kokoc -/app/code/Magento/Checkout/ @paliarush -/app/code/Magento/CheckoutAgreements/ @paliarush -/app/code/Magento/GiftMessage/ @paliarush -/app/code/Magento/InstantPurchase/ @paliarush -/app/code/Magento/Multishipping/ @joni-jones -/app/code/Magento/Quote/ @paliarush -/app/code/Magento/QuoteAnalytics/ @paliarush -/lib/internal/Magento/Framework/Code/ @joni-jones -/lib/internal/Magento/Framework/Reflection/ @joni-jones -/lib/internal/Magento/Framework/Component/ @buskamuza -/app/code/Magento/Version/ @buskamuza -/lib/internal/Magento/Framework/Config/ @paliarush -/app/code/Magento/Config/ @paliarush -/lib/internal/Magento/Framework/Console/ @joni-jones -/lib/internal/Magento/Framework/Process/ @joni-jones -/lib/internal/Magento/Framework/Shell/ @joni-jones -/app/code/Magento/Cookie/ @kokoc -/lib/internal/Magento/Framework/Crontab/ @tariqjawed83 @buskamuza -/app/code/Magento/Cron/ @tariqjawed83 @buskamuza -/app/code/Magento/Customer/ @paliarush -/app/code/Magento/CustomerAnalytics/ @paliarush -/app/code/Magento/CustomerImportExport/ @paliarush -/app/code/Magento/Persistent/ @paliarush -/app/code/Magento/Wishlist/ @paliarush -/lib/internal/Magento/Framework/DB/ @akaplya -/lib/internal/Magento/Framework/EntityManager/ @akaplya -/lib/internal/Magento/Framework/Indexer/ @akaplya -/lib/internal/Magento/Framework/Model/ @akaplya -/lib/internal/Magento/Framework/Mview/ @akaplya -/app/code/Magento/Eav/ @akaplya -/app/code/Magento/Indexer/ @akaplya -/lib/internal/Magento/Framework/Archive/ @joni-jones -/lib/internal/Magento/Framework/Convert/ @joni-jones -/lib/internal/Magento/Framework/Data/ @joni-jones -/lib/internal/Magento/Framework/DomDocument/ @joni-jones -/lib/internal/Magento/Framework/Json/ @joni-jones -/lib/internal/Magento/Framework/Math/ @joni-jones -/lib/internal/Magento/Framework/Parse/ @joni-jones -/lib/internal/Magento/Framework/Serialize/ @joni-jones -/lib/internal/Magento/Framework/Simplexml/ @joni-jones -/lib/internal/Magento/Framework/Stdlib/ @joni-jones -/lib/internal/Magento/Framework/Unserialize/ @joni-jones -/lib/internal/Magento/Framework/Xml/ @joni-jones -/lib/internal/Magento/Framework/XsltProcessor/ @joni-jones -/app/code/Magento/Deploy/ @kandy @buskamuza -/lib/internal/Magento/Framework/Profiler/ @kandy -/app/code/Magento/Developer/ @buskamuza -/app/code/Magento/Directory/ @buskamuza -/lib/internal/Magento/Framework/Exception/ @paliarush -/lib/internal/Magento/Framework/File/ @buskamuza -/lib/internal/Magento/Framework/Filesystem/ @buskamuza -/lib/internal/Magento/Framework/System/ @buskamuza -/lib/internal/Magento/Framework/Css/ @DrewML -/lib/internal/Magento/Framework/Option/ @DrewML -/lib/internal/Magento/Framework/RequireJs/ @DrewML -/lib/internal/Magento/Framework/View/ @melnikovi -/dev/tests/js/ @DrewML -/app/code/Magento/RequireJs/ @DrewML -/app/code/Magento/Theme/ @melnikovi -/app/code/Magento/Ui/ @melnikovi -/lib/internal/Magento/Framework/Intl/ @melnikovi -/lib/internal/Magento/Framework/Locale/ @melnikovi -/lib/internal/Magento/Framework/Phrase/ @melnikovi -/lib/internal/Magento/Framework/Translate/ @melnikovi -/app/code/Magento/Translation/ @melnikovi -/app/code/Magento/ImportExport/ @akaplya -/app/code/Magento/GoogleAdwords/ @buskamuza @melnikovi -/app/code/Magento/Newsletter/ @buskamuza @melnikovi -/app/code/Magento/ProductAlert/ @buskamuza @melnikovi -/app/code/Magento/Rss/ @buskamuza @melnikovi -/app/code/Magento/SendFriend/ @buskamuza @melnikovi -/app/code/Magento/Marketplace/ @buskamuza -/app/code/Magento/MediaStorage/ @buskamuza -/lib/internal/Magento/Framework/Amqp/ @tariqjawed83 @paliarush -/lib/internal/Magento/Framework/Bulk/ @tariqjawed83 @paliarush -/lib/internal/Magento/Framework/Communication/ @tariqjawed83 @paliarush -/app/code/Magento/Amqp/ @tariqjawed83 @paliarush -/app/code/Magento/AsynchronousOperations/ @tariqjawed83 @paliarush -/app/code/Magento/MessageQueue/ @tariqjawed83 @paliarush -/app/code/Magento/MysqlMq/ @tariqjawed83 @paliarush -/app/code/Magento/Sales/ @joni-jones -/app/code/Magento/SalesInventory/ @joni-jones -/app/code/Magento/SalesSequence/ @joni-jones -/lib/internal/Magento/Framework/Event/ @buskamuza @kandy -/lib/internal/Magento/Framework/Interception/ @buskamuza @kandy -/lib/internal/Magento/Framework/ObjectManager/ @buskamuza @kandy -/app/code/Magento/PageCache/ @Andrey @kokoc @paliarush -/app/code/Magento/Authorizenet/ @joni-jones -/app/code/Magento/Braintree/ @joni-jones -/app/code/Magento/OfflinePayments/ @joni-jones -/app/code/Magento/Payment/ @joni-jones -/app/code/Magento/Paypal/ @joni-jones -/app/code/Magento/Signifyd/ @joni-jones -/app/code/Magento/Vault/ @joni-jones -/lib/internal/Magento/Framework/Pricing/ @akaplya -/app/code/Magento/AdvancedPricingImportExport/ @akaplya -/app/code/Magento/CurrencySymbol/ @akaplya -/app/code/Magento/Msrp/ @akaplya -/app/code/Magento/Tax/ @akaplya -/app/code/Magento/TaxImportExport/ @akaplya -/app/code/Magento/Weee/ @akaplya -/app/code/Magento/CatalogRule/ @kokoc -/app/code/Magento/CatalogRuleConfigurable/ @kokoc -/app/code/Magento/CatalogWidget/ @kokoc -/app/code/Magento/Rule/ @kokoc -/app/code/Magento/SalesRule/ @akaplya -/app/code/Magento/ReleaseNotification/ @paliarush -/app/code/Magento/Analytics/ @tariqjawed83 @buskamuza -/app/code/Magento/GoogleAnalytics/ @tariqjawed83 @buskamuza -/app/code/Magento/NewRelicReporting/ @tariqjawed83 @buskamuza -/app/code/Magento/Reports/ @tariqjawed83 @buskamuza -/app/code/Magento/ReviewAnalytics/ @tariqjawed83 @buskamuza -/app/code/Magento/SalesAnalytics/ @tariqjawed83 @buskamuza -/app/code/Magento/WishlistAnalytics/ @tariqjawed83 @buskamuza -/app/code/Magento/GoogleOptimizer/ @paliarush -/app/code/Magento/Robots/ @paliarush -/app/code/Magento/Sitemap/ @paliarush -/lib/internal/Magento/Framework/Search/ @kokoc -/app/code/Magento/AdvancedSearch/ @kokoc -/app/code/Magento/Elasticsearch/ @kokoc -/app/code/Magento/Search/ @kokoc -/lib/internal/Magento/Framework/Acl/ @kokoc -/lib/internal/Magento/Framework/Authorization/ @kokoc -/lib/internal/Magento/Framework/Encryption/ @kokoc -/app/code/Magento/Authorization/ @kokoc -/app/code/Magento/Captcha/ @kokoc -/app/code/Magento/EncryptionKey/ @kokoc -/app/code/Magento/Security/ @kokoc -/lib/internal/Magento/Framework/Autoload/ @buskamuza -/lib/internal/Magento/Framework/Backup/ @buskamuza -/lib/internal/Magento/Framework/Composer/ @buskamuza -/lib/internal/Magento/Framework/Setup/ @buskamuza -/app/code/Magento/Backup/ @buskamuza -/setup/ @buskamuza -/app/code/Magento/Dhl/ @joni-jones -/app/code/Magento/Fedex/ @joni-jones -/app/code/Magento/OfflineShipping/ @joni-jones -/app/code/Magento/Shipping/ @joni-jones -/app/code/Magento/Ups/ @joni-jones -/app/code/Magento/Usps/ @joni-jones -/app/code/Magento/Store/ @akaplya -/lib/internal/Magento/Framework/TestFramework/ @paliarush -/dev/tests/integration/framework/ @buskamuza -/dev/tests/setup-integration/framework/ @paliarush -/dev/tests/static/framework/ @paliarush -/dev/tests/unit/ @paliarush -/dev/tests/api-functional/ @paliarush -/app/code/Magento/UrlRewrite/ @kokoc -/lib/internal/Magento/Framework/Image/ @buskamuza -/lib/internal/Magento/Framework/Mail/ @melnikovi -/lib/internal/Magento/Framework/Filter/ @melnikovi -/lib/internal/Magento/Framework/Validation/ @melnikovi -/lib/internal/Magento/Framework/Validator/ @melnikovi -/lib/internal/Magento/Framework/Api/ @paliarush -/lib/internal/Magento/Framework/GraphQL/ @paliarush -/lib/internal/Magento/Framework/Oauth/ @paliarush -/lib/internal/Magento/Framework/Webapi/ @paliarush -/app/code/Magento/GraphQL/ @paliarush -/app/code/Magento/Integration/ @paliarush -/app/code/Magento/Swagger/ @paliarush -/app/code/Magento/Webapi/ @paliarush -/app/code/Magento/WebapiSecurity/ @paliarush - -composer.json @buskamuza -*.js @DrewML -.htaccess* @akaplya -nginx.conf* @akaplya diff --git a/.github/CONTRIBUTING.md b/.github/CONTRIBUTING.md index 89aea7299855c..37b7bc2ca8c3a 100644 --- a/.github/CONTRIBUTING.md +++ b/.github/CONTRIBUTING.md @@ -31,7 +31,7 @@ If you are a new GitHub user, we recommend that you create your own [free github This will allow you to collaborate with the Magento 2 development team, fork the Magento 2 project and send pull requests. 1. Search current [listed issues](https://github.com/magento/magento2/issues) (open or closed) for similar proposals of intended contribution before starting work on a new contribution. -2. Review the [Contributor License Agreement](https://magento.com/legaldocuments/mca) if this is your first time contributing. +2. Review the [Contributor License Agreement](https://opensource.adobe.com/cla.html) if this is your first time contributing. 3. Create and test your work. 4. Fork the Magento 2 repository according to the [Fork A Repository instructions](https://devdocs.magento.com/guides/v2.3/contributor-guide/contributing.html#fork) and when you are ready to send us a pull request – follow the [Create A Pull Request instructions](https://devdocs.magento.com/guides/v2.3/contributor-guide/contributing.html#pull_request). 5. Once your contribution is received the Magento 2 development team will review the contribution and collaborate with you as needed. diff --git a/.github/ISSUE_TEMPLATE/developer-experience-issue.md b/.github/ISSUE_TEMPLATE/developer-experience-issue.md index 423d4818fb31c..713ce410a16e1 100644 --- a/.github/ISSUE_TEMPLATE/developer-experience-issue.md +++ b/.github/ISSUE_TEMPLATE/developer-experience-issue.md @@ -1,6 +1,7 @@ --- name: Developer experience issue about: Issues related to customization, extensibility, modularity +labels: 'Triage: Dev.Experience' --- diff --git a/.github/ISSUE_TEMPLATE/feature_request.md b/.github/ISSUE_TEMPLATE/feature_request.md index f64185773cab4..7b6a8d199f28f 100644 --- a/.github/ISSUE_TEMPLATE/feature_request.md +++ b/.github/ISSUE_TEMPLATE/feature_request.md @@ -1,6 +1,7 @@ --- name: Feature request about: Please consider reporting directly to https://github.com/magento/community-features +labels: 'feature request' --- diff --git a/.github/PULL_REQUEST_TEMPLATE.md b/.github/PULL_REQUEST_TEMPLATE.md index 11da06ee704c6..5d6620ce19228 100644 --- a/.github/PULL_REQUEST_TEMPLATE.md +++ b/.github/PULL_REQUEST_TEMPLATE.md @@ -15,6 +15,9 @@ Letting us know what has changed and why it needed changing will help us validate this pull request. --> +### Related Pull Requests + + ### Fixed Issues (if relevant) + + + + + + diff --git a/app/code/Magento/AdminAnalytics/Test/Mftf/ActionGroup/LoginAdminWithCredentialsActionGroup.xml b/app/code/Magento/AdminAnalytics/Test/Mftf/ActionGroup/LoginAdminWithCredentialsActionGroup.xml deleted file mode 100644 index d9f5e5dbcb106..0000000000000 --- a/app/code/Magento/AdminAnalytics/Test/Mftf/ActionGroup/LoginAdminWithCredentialsActionGroup.xml +++ /dev/null @@ -1,14 +0,0 @@ - - - - - - - - diff --git a/app/code/Magento/AdminAnalytics/Test/Mftf/ActionGroup/LoginAsAdminActionGroup.xml b/app/code/Magento/AdminAnalytics/Test/Mftf/ActionGroup/LoginAsAdminActionGroup.xml deleted file mode 100644 index 5cf7be8a6fe11..0000000000000 --- a/app/code/Magento/AdminAnalytics/Test/Mftf/ActionGroup/LoginAsAdminActionGroup.xml +++ /dev/null @@ -1,14 +0,0 @@ - - - - - - - - diff --git a/app/code/Magento/AdminAnalytics/Test/Mftf/Test/TrackingScriptTest.xml b/app/code/Magento/AdminAnalytics/Test/Mftf/Test/TrackingScriptTest.xml index 58bcacc190cff..e02c34fd8868e 100644 --- a/app/code/Magento/AdminAnalytics/Test/Mftf/Test/TrackingScriptTest.xml +++ b/app/code/Magento/AdminAnalytics/Test/Mftf/Test/TrackingScriptTest.xml @@ -21,6 +21,6 @@ - + \ No newline at end of file diff --git a/app/code/Magento/AdminAnalytics/Test/Unit/Condition/CanViewNotificationTest.php b/app/code/Magento/AdminAnalytics/Test/Unit/Condition/CanViewNotificationTest.php index 7819f2f017a01..19c4e7a7327d0 100644 --- a/app/code/Magento/AdminAnalytics/Test/Unit/Condition/CanViewNotificationTest.php +++ b/app/code/Magento/AdminAnalytics/Test/Unit/Condition/CanViewNotificationTest.php @@ -11,28 +11,27 @@ use Magento\Framework\App\ProductMetadataInterface; use Magento\Framework\TestFramework\Unit\Helper\ObjectManager; use Magento\Framework\App\CacheInterface; +use PHPUnit\Framework\MockObject\MockObject; +use PHPUnit\Framework\TestCase; -/** - * Class CanViewNotificationTest - */ -class CanViewNotificationTest extends \PHPUnit\Framework\TestCase +class CanViewNotificationTest extends TestCase { /** @var CanViewNotification */ private $canViewNotification; - /** @var Logger|\PHPUnit_Framework_MockObject_MockObject */ + /** @var Logger|MockObject */ private $viewerLoggerMock; - /** @var ProductMetadataInterface|\PHPUnit_Framework_MockObject_MockObject */ + /** @var ProductMetadataInterface|MockObject */ private $productMetadataMock; - /** @var Log|\PHPUnit_Framework_MockObject_MockObject */ + /** @var Log|MockObject */ private $logMock; - /** @var $cacheStorageMock \PHPUnit_Framework_MockObject_MockObject|CacheInterface */ + /** @var $cacheStorageMock MockObject|CacheInterface */ private $cacheStorageMock; - public function setUp() + protected function setUp(): void { $this->cacheStorageMock = $this->getMockBuilder(CacheInterface::class) ->getMockForAbstractClass(); diff --git a/app/code/Magento/AdminAnalytics/etc/adminhtml/system.xml b/app/code/Magento/AdminAnalytics/etc/adminhtml/system.xml index d6867e74c4760..a79635de5d1cd 100644 --- a/app/code/Magento/AdminAnalytics/etc/adminhtml/system.xml +++ b/app/code/Magento/AdminAnalytics/etc/adminhtml/system.xml @@ -8,9 +8,9 @@
- + - + Magento\Config\Model\Config\Source\Yesno Allow Magento to track admin usage in order to improve the quality and user experience. diff --git a/app/code/Magento/AdminAnalytics/registration.php b/app/code/Magento/AdminAnalytics/registration.php index 65c9955d396a8..7d45f9f2b82e5 100644 --- a/app/code/Magento/AdminAnalytics/registration.php +++ b/app/code/Magento/AdminAnalytics/registration.php @@ -4,6 +4,6 @@ * See COPYING.txt for license details. */ -use \Magento\Framework\Component\ComponentRegistrar; +use Magento\Framework\Component\ComponentRegistrar; ComponentRegistrar::register(ComponentRegistrar::MODULE, 'Magento_AdminAnalytics', __DIR__); diff --git a/app/code/Magento/AdminNotification/Block/Grid/Renderer/Actions.php b/app/code/Magento/AdminNotification/Block/Grid/Renderer/Actions.php index 82f70d92e4930..f74f62ef000e6 100644 --- a/app/code/Magento/AdminNotification/Block/Grid/Renderer/Actions.php +++ b/app/code/Magento/AdminNotification/Block/Grid/Renderer/Actions.php @@ -1,4 +1,6 @@ _urlHelper = $urlHelper; parent::__construct($context, $data); } @@ -40,7 +43,7 @@ public function __construct( * @param \Magento\Framework\DataObject $row * @return string */ - public function render(\Magento\Framework\DataObject $row) + public function render(DataObject $row) { $readDetailsHtml = $row->getUrl() ? '' . __( 'Mark as Read' ) . '' : ''; @@ -63,8 +66,8 @@ public function render(\Magento\Framework\DataObject $row) '*/*/remove/', [ '_current' => true, - 'id' => $row->getId(), - \Magento\Framework\App\ActionInterface::PARAM_NAME_URL_ENCODED => $encodedUrl + 'id' => $row->getNotificationId(), + ActionInterface::PARAM_NAME_URL_ENCODED => $encodedUrl ] ), __('Are you sure?'), diff --git a/app/code/Magento/AdminNotification/Block/Grid/Renderer/Notice.php b/app/code/Magento/AdminNotification/Block/Grid/Renderer/Notice.php index ccc1b98a228ce..4aa5d90e08014 100644 --- a/app/code/Magento/AdminNotification/Block/Grid/Renderer/Notice.php +++ b/app/code/Magento/AdminNotification/Block/Grid/Renderer/Notice.php @@ -1,4 +1,6 @@ ' . $this->escapeHtml($row->getTitle()) . diff --git a/app/code/Magento/AdminNotification/Block/Grid/Renderer/Severity.php b/app/code/Magento/AdminNotification/Block/Grid/Renderer/Severity.php index 033fa52c55081..fd3d5c584d6fa 100644 --- a/app/code/Magento/AdminNotification/Block/Grid/Renderer/Severity.php +++ b/app/code/Magento/AdminNotification/Block/Grid/Renderer/Severity.php @@ -1,4 +1,6 @@ _notice = $notice; } @@ -36,12 +42,14 @@ public function __construct( * @param \Magento\Framework\DataObject $row * @return string */ - public function render(\Magento\Framework\DataObject $row) + public function render(DataObject $row) { $class = ''; $value = ''; - switch ($row->getData($this->getColumn()->getIndex())) { + $column = $this->getColumn(); + $index = $column->getIndex(); + switch ($row->getData($index)) { case MessageInterface::SEVERITY_CRITICAL: $class = 'critical'; $value = $this->_notice->getSeverities(MessageInterface::SEVERITY_CRITICAL); @@ -59,6 +67,7 @@ public function render(\Magento\Framework\DataObject $row) $value = $this->_notice->getSeverities(MessageInterface::SEVERITY_NOTICE); break; } + return '' . $value . ''; } } diff --git a/app/code/Magento/AdminNotification/Block/System/Messages.php b/app/code/Magento/AdminNotification/Block/System/Messages.php index b950f5583e599..c9b3a0b8844cc 100644 --- a/app/code/Magento/AdminNotification/Block/System/Messages.php +++ b/app/code/Magento/AdminNotification/Block/System/Messages.php @@ -5,45 +5,54 @@ */ namespace Magento\AdminNotification\Block\System; -class Messages extends \Magento\Backend\Block\Template +use Magento\AdminNotification\Model\ResourceModel\System\Message\Collection\Synchronized; +use Magento\Backend\Block\Template; +use Magento\Backend\Block\Template\Context as TemplateContext; +use Magento\Framework\Json\Helper\Data as JsonDataHelper; +use Magento\Framework\Notification\MessageInterface; +use Magento\Framework\Serialize\Serializer\Json as JsonSerializer; + +/** + * AdminNotification Messages class + */ +class Messages extends Template { /** - * Message list + * Synchronized Message collection * - * @var \Magento\AdminNotification\Model\ResourceModel\System\Message\Collection\Synchronized + * @var Synchronized */ protected $_messages; /** - * @var \Magento\Framework\Json\Helper\Data + * @var JsonDataHelper * @deprecated */ protected $jsonHelper; /** - * @var \Magento\Framework\Serialize\Serializer\Json + * @var JsonSerializer */ private $serializer; /** - * @param \Magento\Backend\Block\Template\Context $context - * @param \Magento\AdminNotification\Model\ResourceModel\System\Message\Collection\Synchronized $messages - * @param \Magento\Framework\Json\Helper\Data $jsonHelper + * @param TemplateContext $context + * @param Synchronized $messages + * @param JsonDataHelper $jsonHelper + * @param JsonSerializer $serializer * @param array $data - * @param \Magento\Framework\Serialize\Serializer\Json|null $serializer */ public function __construct( - \Magento\Backend\Block\Template\Context $context, - \Magento\AdminNotification\Model\ResourceModel\System\Message\Collection\Synchronized $messages, - \Magento\Framework\Json\Helper\Data $jsonHelper, - array $data = [], - \Magento\Framework\Serialize\Serializer\Json $serializer = null + TemplateContext $context, + Synchronized $messages, + JsonDataHelper $jsonHelper, + JsonSerializer $serializer, + array $data = [] ) { $this->jsonHelper = $jsonHelper; parent::__construct($context, $data); $this->_messages = $messages; - $this->serializer = $serializer ?: \Magento\Framework\App\ObjectManager::getInstance() - ->get(\Magento\Framework\Serialize\Serializer\Json::class); + $this->serializer = $serializer; } /** @@ -62,16 +71,14 @@ protected function _toHtml() /** * Retrieve message list * - * @return \Magento\Framework\Notification\MessageInterface[] + * @return MessageInterface[]|null */ public function getLastCritical() { $items = array_values($this->_messages->getItems()); - if (isset( - $items[0] - ) && $items[0]->getSeverity() == \Magento\Framework\Notification\MessageInterface::SEVERITY_CRITICAL - ) { - return $items[0]; + + if (!empty($items) && current($items)->getSeverity() === MessageInterface::SEVERITY_CRITICAL) { + return current($items); } return null; } @@ -83,9 +90,7 @@ public function getLastCritical() */ public function getCriticalCount() { - return $this->_messages->getCountBySeverity( - \Magento\Framework\Notification\MessageInterface::SEVERITY_CRITICAL - ); + return $this->_messages->getCountBySeverity(MessageInterface::SEVERITY_CRITICAL); } /** @@ -95,9 +100,7 @@ public function getCriticalCount() */ public function getMajorCount() { - return $this->_messages->getCountBySeverity( - \Magento\Framework\Notification\MessageInterface::SEVERITY_MAJOR - ); + return $this->_messages->getCountBySeverity(MessageInterface::SEVERITY_MAJOR); } /** diff --git a/app/code/Magento/AdminNotification/Controller/Adminhtml/Notification/AjaxMarkAsRead.php b/app/code/Magento/AdminNotification/Controller/Adminhtml/Notification/AjaxMarkAsRead.php index da797fe12e75a..be128435b62a1 100644 --- a/app/code/Magento/AdminNotification/Controller/Adminhtml/Notification/AjaxMarkAsRead.php +++ b/app/code/Magento/AdminNotification/Controller/Adminhtml/Notification/AjaxMarkAsRead.php @@ -1,33 +1,34 @@ notificationService = $notificationService?: \Magento\Framework\App\ObjectManager::getInstance() - ->get(\Magento\AdminNotification\Model\NotificationService::class); + $this->notificationService = $notificationService; } /** diff --git a/app/code/Magento/AdminNotification/Controller/Adminhtml/Notification/MarkAsRead.php b/app/code/Magento/AdminNotification/Controller/Adminhtml/Notification/MarkAsRead.php index 6b5e0681139cf..7b3f99ae8e2c1 100644 --- a/app/code/Magento/AdminNotification/Controller/Adminhtml/Notification/MarkAsRead.php +++ b/app/code/Magento/AdminNotification/Controller/Adminhtml/Notification/MarkAsRead.php @@ -1,12 +1,20 @@ notificationService = $notificationService; + } + + /** + * @inheritdoc */ public function execute() { $notificationId = (int)$this->getRequest()->getParam('id'); if ($notificationId) { try { - $this->_objectManager->create( - \Magento\AdminNotification\Model\NotificationService::class - )->markAsRead( - $notificationId - ); + $this->notificationService->markAsRead($notificationId); $this->messageManager->addSuccessMessage(__('The message has been marked as Read.')); - } catch (\Magento\Framework\Exception\LocalizedException $e) { + } catch (LocalizedException $e) { $this->messageManager->addErrorMessage($e->getMessage()); } catch (\Exception $e) { $this->messageManager->addExceptionMessage( @@ -38,9 +57,8 @@ public function execute() ); } - $this->getResponse()->setRedirect($this->_redirect->getRedirectUrl($this->getUrl('*'))); - return; + return $this->getResponse()->setRedirect($this->_redirect->getRedirectUrl($this->getUrl('*'))); } - $this->_redirect('adminhtml/*/'); + return $this->_redirect('adminhtml/*/'); } } diff --git a/app/code/Magento/AdminNotification/Controller/Adminhtml/Notification/MassMarkAsRead.php b/app/code/Magento/AdminNotification/Controller/Adminhtml/Notification/MassMarkAsRead.php index 9ae4a7cdac0b9..12198d05f6ae7 100644 --- a/app/code/Magento/AdminNotification/Controller/Adminhtml/Notification/MassMarkAsRead.php +++ b/app/code/Magento/AdminNotification/Controller/Adminhtml/Notification/MassMarkAsRead.php @@ -1,14 +1,20 @@ inboxModelFactory = $inboxModelFactory; + } + + /** + * @inheritdoc */ public function execute() { @@ -27,7 +48,7 @@ public function execute() } else { try { foreach ($ids as $id) { - $model = $this->_objectManager->create(\Magento\AdminNotification\Model\Inbox::class)->load($id); + $model = $this->inboxModelFactory->create()->load($id); if ($model->getId()) { $model->setIsRead(1)->save(); } @@ -44,6 +65,6 @@ public function execute() ); } } - $this->_redirect('adminhtml/*/'); + return $this->_redirect('adminhtml/*/'); } } diff --git a/app/code/Magento/AdminNotification/Controller/Adminhtml/Notification/MassRemove.php b/app/code/Magento/AdminNotification/Controller/Adminhtml/Notification/MassRemove.php index 06659b8452cab..0ca114ac4021c 100644 --- a/app/code/Magento/AdminNotification/Controller/Adminhtml/Notification/MassRemove.php +++ b/app/code/Magento/AdminNotification/Controller/Adminhtml/Notification/MassRemove.php @@ -1,12 +1,19 @@ inboxModelFactory = $inboxModelFactory; + } /** - * @return void + * @inheritdoc */ public function execute() { @@ -27,7 +48,7 @@ public function execute() } else { try { foreach ($ids as $id) { - $model = $this->_objectManager->create(\Magento\AdminNotification\Model\Inbox::class)->load($id); + $model = $this->inboxModelFactory->create()->load($id); if ($model->getId()) { $model->setIsRemove(1)->save(); } @@ -42,6 +63,6 @@ public function execute() ); } } - $this->_redirect('adminhtml/*/'); + return $this->_redirect('adminhtml/*/'); } } diff --git a/app/code/Magento/AdminNotification/Controller/Adminhtml/Notification/Remove.php b/app/code/Magento/AdminNotification/Controller/Adminhtml/Notification/Remove.php index f0724a9587c50..fe699cd692160 100644 --- a/app/code/Magento/AdminNotification/Controller/Adminhtml/Notification/Remove.php +++ b/app/code/Magento/AdminNotification/Controller/Adminhtml/Notification/Remove.php @@ -1,14 +1,20 @@ inboxModelFactory = $inboxModelFactory; + } + + /** + * @inheritdoc */ public function execute() { if ($id = $this->getRequest()->getParam('id')) { - $model = $this->_objectManager->create(\Magento\AdminNotification\Model\Inbox::class)->load($id); + $model = $this->inboxModelFactory->create()->load($id); if (!$model->getId()) { - $this->_redirect('adminhtml/*/'); - return; + return $this->_redirect('adminhtml/*/'); } try { @@ -41,9 +61,8 @@ public function execute() ); } - $this->_redirect('adminhtml/*/'); - return; + return $this->_redirect('adminhtml/*/'); } - $this->_redirect('adminhtml/*/'); + return $this->_redirect('adminhtml/*/'); } } diff --git a/app/code/Magento/AdminNotification/Test/Mftf/Test/AdminSystemNotificationNavigateMenuTest.xml b/app/code/Magento/AdminNotification/Test/Mftf/Test/AdminSystemNotificationNavigateMenuTest.xml index 75dceb4028622..77c8f02bfb777 100644 --- a/app/code/Magento/AdminNotification/Test/Mftf/Test/AdminSystemNotificationNavigateMenuTest.xml +++ b/app/code/Magento/AdminNotification/Test/Mftf/Test/AdminSystemNotificationNavigateMenuTest.xml @@ -20,10 +20,10 @@ - + - + diff --git a/app/code/Magento/AdminNotification/Test/Unit/Block/Grid/Renderer/ActionsTest.php b/app/code/Magento/AdminNotification/Test/Unit/Block/Grid/Renderer/ActionsTest.php new file mode 100644 index 0000000000000..7199d1f44222d --- /dev/null +++ b/app/code/Magento/AdminNotification/Test/Unit/Block/Grid/Renderer/ActionsTest.php @@ -0,0 +1,74 @@ +getMockBuilder(Escaper::class)->disableOriginalConstructor()->getMock(); + $escaperMock->expects($this->once())->method('escapeUrl')->willReturn('https://magento.com'); + + /** @var UrlInterface|MockObject $urlBuilder */ + $urlBuilder = $this->getMockBuilder(UrlInterface::class)->getMock(); + $urlBuilder->expects($this->once())->method('getUrl')->willReturn('http://magento.com'); + + /** @var Context|MockObject $contextMock */ + $contextMock = $this->getMockBuilder(Context::class)->disableOriginalConstructor()->getMock(); + $contextMock->expects($this->once())->method('getEscaper')->willReturn($escaperMock); + $contextMock->expects($this->once())->method('getUrlBuilder')->willReturn($urlBuilder); + + /** @var Data|MockObject $urlHelperMock */ + $urlHelperMock = $this->getMockBuilder(Data::class)->disableOriginalConstructor()->getMock(); + $urlHelperMock->expects($this->once())->method('getEncodedUrl')->willReturn('http://magento.com'); + + $this->sut = new Actions($contextMock, $urlHelperMock); + } + + public function testShouldRenderMessageWhenUrlIsGiven() : void + { + $dataObject = new DataObject(); + $dataObject->setdata('url', 'https://magento.com'); + $dataObject->setdata('is_read', true); + $dataObject->setdata('id', 1); + + $actual = $this->sut->render($dataObject); + + // Ignoring Code Style at this point due to the long HEREDOC + // phpcs:disable + $expected = <<Read DetailsRemove +HTML; + // phpcs:enable + + $this->assertEquals($actual, $expected); + } +} diff --git a/app/code/Magento/AdminNotification/Test/Unit/Block/Grid/Renderer/NoticeTest.php b/app/code/Magento/AdminNotification/Test/Unit/Block/Grid/Renderer/NoticeTest.php new file mode 100644 index 0000000000000..a4fb6227ecdc7 --- /dev/null +++ b/app/code/Magento/AdminNotification/Test/Unit/Block/Grid/Renderer/NoticeTest.php @@ -0,0 +1,56 @@ +getMockBuilder(Escaper::class)->disableOriginalConstructor()->getMock(); + $escaperMock->expects($this->exactly(2))->method('escapeHtml')->willReturn('
Some random html
'); + + /** @var Context|MockObject $contextMock */ + $contextMock = $this->getMockBuilder(Context::class)->disableOriginalConstructor()->getMock(); + $contextMock->expects($this->once())->method('getEscaper')->willReturn($escaperMock); + + $this->sut = new Notice($contextMock); + } + + public function testShouldRenderNotice() : void + { + $dataObject = new DataObject(); + $dataObject->setData('title', 'A great Title'); + $dataObject->setData('description', 'Handy description right here'); + + $actual = $this->sut->render($dataObject); + $expected = '
Some random html

Some random html
'; + + $this->assertEquals($actual, $expected); + } +} diff --git a/app/code/Magento/AdminNotification/Test/Unit/Block/Grid/Renderer/SeverityTest.php b/app/code/Magento/AdminNotification/Test/Unit/Block/Grid/Renderer/SeverityTest.php new file mode 100644 index 0000000000000..c2e109cff130e --- /dev/null +++ b/app/code/Magento/AdminNotification/Test/Unit/Block/Grid/Renderer/SeverityTest.php @@ -0,0 +1,91 @@ +getMockBuilder(Inbox::class)->disableOriginalConstructor()->getMock(); + + /** @var Context|MockObject $contextMock */ + $contextMock = $this->getMockBuilder(Context::class)->disableOriginalConstructor()->getMock(); + + $this->sut = new Severity($contextMock, $inboxMock); + } + + public function testShouldRenderSeverity() : void + { + /** @var Column|MockObject $columnMock */ + $columnMock = $this->getMockBuilder(Column::class) + ->disableOriginalConstructor() + ->setMethods(['getIndex']) + ->getMock(); + $columnMock->expects($this->exactly(5))->method('getIndex')->willReturn('index'); + $this->sut->setColumn($columnMock); + $dataObject = new DataObject(); + + // Test critical severity + $dataObject->setData('index', 1); + $actual = $this->sut->render($dataObject); + $expected = ''; + + $this->assertEquals($actual, $expected); + + // Test major severity + $dataObject->setData('index', 2); + $actual = $this->sut->render($dataObject); + $expected = ''; + + $this->assertEquals($actual, $expected); + + // Test minor severity + $dataObject->setData('index', 3); + $actual = $this->sut->render($dataObject); + $expected = ''; + + $this->assertEquals($actual, $expected); + + // Test notice severity + $dataObject->setData('index', 4); + $actual = $this->sut->render($dataObject); + $expected = ''; + + $this->assertEquals($actual, $expected); + + // Test default severity + $dataObject->setData('index', 5); + $actual = $this->sut->render($dataObject); + $expected = ''; + + $this->assertEquals($actual, $expected); + } +} diff --git a/app/code/Magento/AdminNotification/Test/Unit/Block/ToolbarEntryTest.php b/app/code/Magento/AdminNotification/Test/Unit/Block/ToolbarEntryTest.php index 2afa9eced1d95..eb45a9af6beb2 100644 --- a/app/code/Magento/AdminNotification/Test/Unit/Block/ToolbarEntryTest.php +++ b/app/code/Magento/AdminNotification/Test/Unit/Block/ToolbarEntryTest.php @@ -9,26 +9,31 @@ */ namespace Magento\AdminNotification\Test\Unit\Block; -class ToolbarEntryTest extends \PHPUnit\Framework\TestCase +use Magento\AdminNotification\Block\ToolbarEntry; +use Magento\AdminNotification\Model\ResourceModel\Inbox\Collection\Unread; +use Magento\Framework\TestFramework\Unit\Helper\ObjectManager; +use PHPUnit\Framework\TestCase; + +class ToolbarEntryTest extends TestCase { /** * Retrieve toolbar entry block instance * * @param int $unreadNotifications number of unread notifications - * @return \Magento\AdminNotification\Block\ToolbarEntry + * @return ToolbarEntry */ protected function _getBlockInstance($unreadNotifications) { - $objectManagerHelper = new \Magento\Framework\TestFramework\Unit\Helper\ObjectManager($this); + $objectManagerHelper = new ObjectManager($this); // mock collection of unread notifications $notificationList = $this->createPartialMock( - \Magento\AdminNotification\Model\ResourceModel\Inbox\Collection\Unread::class, + Unread::class, ['getSize', 'setCurPage', 'setPageSize'] ); $notificationList->expects($this->any())->method('getSize')->will($this->returnValue($unreadNotifications)); $block = $objectManagerHelper->getObject( - \Magento\AdminNotification\Block\ToolbarEntry::class, + ToolbarEntry::class, ['notificationList' => $notificationList] ); @@ -44,25 +49,23 @@ public function testGetUnreadNotificationCount() public function testGetLatestUnreadNotifications() { - $helper = new \Magento\Framework\TestFramework\Unit\Helper\ObjectManager($this); + $helper = new ObjectManager($this); // 1. Create mocks - $notificationList = $this->getMockBuilder( - \Magento\AdminNotification\Model\ResourceModel\Inbox\Collection\Unread::class - ) + $notificationList = $this->getMockBuilder(Unread::class) ->disableOriginalConstructor() ->getMock(); - /** @var \Magento\AdminNotification\Block\ToolbarEntry $model */ + /** @var ToolbarEntry $model */ $model = $helper->getObject( - \Magento\AdminNotification\Block\ToolbarEntry::class, + ToolbarEntry::class, ['notificationList' => $notificationList] ); // 2. Set expectations $notificationList->expects($this->atLeastOnce()) ->method('setPageSize') - ->with(\Magento\AdminNotification\Block\ToolbarEntry::NOTIFICATIONS_NUMBER) + ->with(ToolbarEntry::NOTIFICATIONS_NUMBER) ->will($this->returnSelf()); // 3. Run tested method diff --git a/app/code/Magento/AdminNotification/Test/Unit/Model/FeedTest.php b/app/code/Magento/AdminNotification/Test/Unit/Model/FeedTest.php index 2b3eb6247e899..604d33ccf9092 100644 --- a/app/code/Magento/AdminNotification/Test/Unit/Model/FeedTest.php +++ b/app/code/Magento/AdminNotification/Test/Unit/Model/FeedTest.php @@ -6,66 +6,79 @@ namespace Magento\AdminNotification\Test\Unit\Model; +use Magento\AdminNotification\Model\Feed; +use Magento\AdminNotification\Model\Inbox; +use Magento\AdminNotification\Model\InboxFactory; +use Magento\Backend\App\ConfigInterface; +use Magento\Framework\App\CacheInterface; +use Magento\Framework\App\DeploymentConfig; +use Magento\Framework\App\ProductMetadata; +use Magento\Framework\App\State; use Magento\Framework\Config\ConfigOptionsListConstants; +use Magento\Framework\HTTP\Adapter\Curl; +use Magento\Framework\HTTP\Adapter\CurlFactory; use Magento\Framework\TestFramework\Unit\Helper\ObjectManager as ObjectManagerHelper; +use Magento\Framework\UrlInterface; +use PHPUnit\Framework\MockObject\MockObject; +use PHPUnit\Framework\TestCase; /** * @SuppressWarnings(PHPMD.CouplingBetweenObjects) */ -class FeedTest extends \PHPUnit\Framework\TestCase +class FeedTest extends TestCase { - /** @var \Magento\AdminNotification\Model\Feed */ + /** @var Feed */ protected $feed; /** @var ObjectManagerHelper */ protected $objectManagerHelper; - /** @var \Magento\AdminNotification\Model\InboxFactory|\PHPUnit_Framework_MockObject_MockObject */ + /** @var InboxFactory|MockObject */ protected $inboxFactory; - /** @var \Magento\AdminNotification\Model\Inbox|\PHPUnit_Framework_MockObject_MockObject */ + /** @var Inbox|MockObject */ protected $inboxModel; - /** @var \Magento\Framework\HTTP\Adapter\CurlFactory|\PHPUnit_Framework_MockObject_MockObject */ + /** @var CurlFactory|MockObject */ protected $curlFactory; - /** @var \Magento\Framework\HTTP\Adapter\Curl|\PHPUnit_Framework_MockObject_MockObject */ + /** @var Curl|MockObject */ protected $curl; - /** @var \Magento\Backend\App\ConfigInterface|\PHPUnit_Framework_MockObject_MockObject */ + /** @var ConfigInterface|MockObject */ protected $backendConfig; - /** @var \Magento\Framework\App\CacheInterface|\PHPUnit_Framework_MockObject_MockObject */ + /** @var CacheInterface|MockObject */ protected $cacheManager; - /** @var \Magento\Framework\App\State|\PHPUnit_Framework_MockObject_MockObject */ + /** @var State|MockObject */ protected $appState; - /** @var \Magento\Framework\App\DeploymentConfig|\PHPUnit_Framework_MockObject_MockObject */ + /** @var DeploymentConfig|MockObject */ protected $deploymentConfig; - /** @var \Magento\Framework\App\ProductMetadata|\PHPUnit_Framework_MockObject_MockObject */ + /** @var ProductMetadata|MockObject */ protected $productMetadata; - /** @var \Magento\Framework\UrlInterface|\PHPUnit_Framework_MockObject_MockObject */ + /** @var UrlInterface|MockObject */ protected $urlBuilder; - protected function setUp() + protected function setUp(): void { $this->inboxFactory = $this->createPartialMock( - \Magento\AdminNotification\Model\InboxFactory::class, + InboxFactory::class, ['create'] ); - $this->curlFactory = $this->createPartialMock(\Magento\Framework\HTTP\Adapter\CurlFactory::class, ['create']); - $this->curl = $this->getMockBuilder(\Magento\Framework\HTTP\Adapter\Curl::class) + $this->curlFactory = $this->createPartialMock(CurlFactory::class, ['create']); + $this->curl = $this->getMockBuilder(Curl::class) ->disableOriginalConstructor()->getMock(); - $this->appState = $this->createPartialMock(\Magento\Framework\App\State::class, ['getInstallDate']); - $this->inboxModel = $this->createPartialMock(\Magento\AdminNotification\Model\Inbox::class, [ + $this->appState = $this->createPartialMock(State::class, []); + $this->inboxModel = $this->createPartialMock(Inbox::class, [ '__wakeup', 'parse' ]); $this->backendConfig = $this->createPartialMock( - \Magento\Backend\App\ConfigInterface::class, + ConfigInterface::class, [ 'getValue', 'setValue', @@ -73,7 +86,7 @@ protected function setUp() ] ); $this->cacheManager = $this->createPartialMock( - \Magento\Framework\App\CacheInterface::class, + CacheInterface::class, [ 'load', 'getFrontend', @@ -83,18 +96,18 @@ protected function setUp() ] ); - $this->deploymentConfig = $this->getMockBuilder(\Magento\Framework\App\DeploymentConfig::class) + $this->deploymentConfig = $this->getMockBuilder(DeploymentConfig::class) ->disableOriginalConstructor()->getMock(); $this->objectManagerHelper = new ObjectManagerHelper($this); - $this->productMetadata = $this->getMockBuilder(\Magento\Framework\App\ProductMetadata::class) + $this->productMetadata = $this->getMockBuilder(ProductMetadata::class) ->disableOriginalConstructor()->getMock(); - $this->urlBuilder = $this->createMock(\Magento\Framework\UrlInterface::class); + $this->urlBuilder = $this->createMock(UrlInterface::class); $this->feed = $this->objectManagerHelper->getObject( - \Magento\AdminNotification\Model\Feed::class, + Feed::class, [ 'backendConfig' => $this->backendConfig, 'cacheManager' => $this->cacheManager, diff --git a/app/code/Magento/AdminNotification/Test/Unit/Model/NotificationServiceTest.php b/app/code/Magento/AdminNotification/Test/Unit/Model/NotificationServiceTest.php index f8485847ccae2..68070add6cd98 100644 --- a/app/code/Magento/AdminNotification/Test/Unit/Model/NotificationServiceTest.php +++ b/app/code/Magento/AdminNotification/Test/Unit/Model/NotificationServiceTest.php @@ -9,27 +9,33 @@ */ namespace Magento\AdminNotification\Test\Unit\Model; -class NotificationServiceTest extends \PHPUnit\Framework\TestCase +use Magento\AdminNotification\Model\Inbox; +use Magento\AdminNotification\Model\InboxFactory; +use Magento\AdminNotification\Model\NotificationService; +use Magento\Framework\Exception\LocalizedException; +use PHPUnit\Framework\MockObject\MockObject; +use PHPUnit\Framework\TestCase; + +class NotificationServiceTest extends TestCase { /** * Retrieve instance of notification service model * * @param $notificationId - * @return \Magento\AdminNotification\Model\NotificationService + * @return NotificationService */ protected function _getServiceInstanceForMarkAsReadTest($notificationId) { /** - * @var - * $notificationFactory \PHPUnit_Framework_MockObject_MockObject|\Magento\AdminNotification\Model\InboxFactory + * @var $notificationFactory MockObject|InboxFactory */ $notificationFactory = $this->createPartialMock( - \Magento\AdminNotification\Model\InboxFactory::class, + InboxFactory::class, ['create'] ); $notification = $this->createPartialMock( - \Magento\AdminNotification\Model\Inbox::class, - ['load', 'getId', 'save', 'setIsRead', '__sleep', '__wakeup'] + Inbox::class, + ['load', 'getId', 'save', 'setData', '__sleep', '__wakeup'] ); $notification->expects($this->once())->method('load')->with($notificationId)->will($this->returnSelf()); $notification->expects($this->once())->method('getId')->will($this->returnValue($notificationId)); @@ -37,11 +43,12 @@ protected function _getServiceInstanceForMarkAsReadTest($notificationId) // when notification Id is valid, add additional expectations if ($notificationId) { $notification->expects($this->once())->method('save')->will($this->returnSelf()); - $notification->expects($this->once())->method('setIsRead')->with(1)->will($this->returnSelf()); + $notification->expects($this->once())->method('setData') + ->with('is_read', 1)->will($this->returnSelf()); } $notificationFactory->expects($this->once())->method('create')->will($this->returnValue($notification)); - return new \Magento\AdminNotification\Model\NotificationService($notificationFactory); + return new NotificationService($notificationFactory); } public function testMarkAsRead() @@ -51,12 +58,11 @@ public function testMarkAsRead() $service->markAsRead($notificationId); } - /** - * @expectedException \Magento\Framework\Exception\LocalizedException - * @expectedExceptionMessage Wrong notification ID specified. - */ public function testMarkAsReadThrowsExceptionWhenNotificationIdIsInvalid() { + $this->expectException(LocalizedException::class); + $this->expectExceptionMessage('Wrong notification ID specified.'); + $notificationId = null; $service = $this->_getServiceInstanceForMarkAsReadTest($notificationId); $service->markAsRead($notificationId); diff --git a/app/code/Magento/AdminNotification/Test/Unit/Model/System/Message/CacheOutdatedTest.php b/app/code/Magento/AdminNotification/Test/Unit/Model/System/Message/CacheOutdatedTest.php index f49911c3e7a93..381e3fff3d080 100644 --- a/app/code/Magento/AdminNotification/Test/Unit/Model/System/Message/CacheOutdatedTest.php +++ b/app/code/Magento/AdminNotification/Test/Unit/Model/System/Message/CacheOutdatedTest.php @@ -5,42 +5,50 @@ */ namespace Magento\AdminNotification\Test\Unit\Model\System\Message; -class CacheOutdatedTest extends \PHPUnit\Framework\TestCase +use Magento\AdminNotification\Model\System\Message\CacheOutdated; +use Magento\Framework\App\Cache\TypeListInterface; +use Magento\Framework\AuthorizationInterface; +use Magento\Framework\TestFramework\Unit\Helper\ObjectManager; +use Magento\Framework\UrlInterface; +use PHPUnit\Framework\MockObject\MockObject; +use PHPUnit\Framework\TestCase; + +class CacheOutdatedTest extends TestCase { /** - * @var \PHPUnit_Framework_MockObject_MockObject + * @var MockObject */ protected $_authorizationMock; /** - * @var \PHPUnit_Framework_MockObject_MockObject + * @var MockObject */ protected $_cacheTypeListMock; /** - * @var \PHPUnit_Framework_MockObject_MockObject + * @var MockObject */ protected $_urlInterfaceMock; /** - * @var \Magento\AdminNotification\Model\System\Message\CacheOutdated + * @var CacheOutdated */ protected $_messageModel; - protected function setUp() + protected function setUp(): void { - $this->_authorizationMock = $this->createMock(\Magento\Framework\AuthorizationInterface::class); - $this->_urlInterfaceMock = $this->createMock(\Magento\Framework\UrlInterface::class); - $this->_cacheTypeListMock = $this->createMock(\Magento\Framework\App\Cache\TypeListInterface::class); + $this->_authorizationMock = $this->createMock(AuthorizationInterface::class); + $this->_urlInterfaceMock = $this->createMock(UrlInterface::class); + $this->_cacheTypeListMock = $this->createMock(TypeListInterface::class); - $objectManagerHelper = new \Magento\Framework\TestFramework\Unit\Helper\ObjectManager($this); + $objectManagerHelper = new ObjectManager($this); $arguments = [ 'authorization' => $this->_authorizationMock, 'urlBuilder' => $this->_urlInterfaceMock, 'cacheTypeList' => $this->_cacheTypeListMock, ]; $this->_messageModel = $objectManagerHelper->getObject( - \Magento\AdminNotification\Model\System\Message\CacheOutdated::class, + CacheOutdated::class, $arguments ); } diff --git a/app/code/Magento/AdminNotification/Test/Unit/Model/System/Message/Media/Synchronization/ErrorTest.php b/app/code/Magento/AdminNotification/Test/Unit/Model/System/Message/Media/Synchronization/ErrorTest.php index b490efd8e9683..91595732bdb58 100644 --- a/app/code/Magento/AdminNotification/Test/Unit/Model/System/Message/Media/Synchronization/ErrorTest.php +++ b/app/code/Magento/AdminNotification/Test/Unit/Model/System/Message/Media/Synchronization/ErrorTest.php @@ -5,37 +5,44 @@ */ namespace Magento\AdminNotification\Test\Unit\Model\System\Message\Media\Synchronization; -class ErrorTest extends \PHPUnit\Framework\TestCase +use Magento\AdminNotification\Model\System\Message\Media\Synchronization\Error; +use Magento\Framework\Notification\MessageInterface; +use Magento\Framework\TestFramework\Unit\Helper\ObjectManager; +use Magento\MediaStorage\Model\File\Storage\Flag; +use PHPUnit\Framework\MockObject\MockObject; +use PHPUnit\Framework\TestCase; + +class ErrorTest extends TestCase { /** - * @var \PHPUnit_Framework_MockObject_MockObject + * @var MockObject */ protected $_syncFlagMock; /** - * @var \PHPUnit_Framework_MockObject_MockObject + * @var MockObject */ protected $_fileStorage; /** - * @var \Magento\AdminNotification\Model\System\Message\Media\Synchronization\Error + * @var Error */ protected $_model; - protected function setUp() + protected function setUp(): void { $this->_syncFlagMock = $this->createPartialMock( - \Magento\MediaStorage\Model\File\Storage\Flag::class, - ['setState', 'save', 'getFlagData'] + Flag::class, + ['save', 'getFlagData'] ); - $this->_fileStorage = $this->createMock(\Magento\MediaStorage\Model\File\Storage\Flag::class); + $this->_fileStorage = $this->createMock(Flag::class); $this->_fileStorage->expects($this->any())->method('loadSelf')->will($this->returnValue($this->_syncFlagMock)); - $objectManagerHelper = new \Magento\Framework\TestFramework\Unit\Helper\ObjectManager($this); + $objectManagerHelper = new ObjectManager($this); $arguments = ['fileStorage' => $this->_fileStorage]; $this->_model = $objectManagerHelper->getObject( - \Magento\AdminNotification\Model\System\Message\Media\Synchronization\Error::class, + Error::class, $arguments ); } @@ -43,7 +50,6 @@ protected function setUp() public function testGetText() { $messageText = 'We were unable to synchronize one or more media files.'; - $this->assertContains($messageText, (string)$this->_model->getText()); } @@ -55,15 +61,14 @@ public function testGetText() public function testIsDisplayed($expectedFirstRun, $data) { $arguments = ['fileStorage' => $this->_fileStorage]; - $objectManagerHelper = new \Magento\Framework\TestFramework\Unit\Helper\ObjectManager($this); + $objectManagerHelper = new ObjectManager($this); // create new instance to ensure that it hasn't been displayed yet (var $this->_isDisplayed is unset) - /** @var $model \Magento\AdminNotification\Model\System\Message\Media\Synchronization\Error */ + /** @var $model Error */ $model = $objectManagerHelper->getObject( - \Magento\AdminNotification\Model\System\Message\Media\Synchronization\Error::class, + Error::class, $arguments ); - $this->_syncFlagMock->expects($this->any())->method('setState'); $this->_syncFlagMock->expects($this->any())->method('save'); $this->_syncFlagMock->expects($this->any())->method('getFlagData')->will($this->returnValue($data)); //check first call @@ -92,7 +97,7 @@ public function testGetIdentity() public function testGetSeverity() { - $severity = \Magento\Framework\Notification\MessageInterface::SEVERITY_MAJOR; + $severity = MessageInterface::SEVERITY_MAJOR; $this->assertEquals($severity, $this->_model->getSeverity()); } } diff --git a/app/code/Magento/AdminNotification/Test/Unit/Model/System/Message/SecurityTest.php b/app/code/Magento/AdminNotification/Test/Unit/Model/System/Message/SecurityTest.php index c6f61fee862ba..a0e09728cbacf 100644 --- a/app/code/Magento/AdminNotification/Test/Unit/Model/System/Message/SecurityTest.php +++ b/app/code/Magento/AdminNotification/Test/Unit/Model/System/Message/SecurityTest.php @@ -5,51 +5,55 @@ */ namespace Magento\AdminNotification\Test\Unit\Model\System\Message; -class SecurityTest extends \PHPUnit\Framework\TestCase -{ - /** - * @var \PHPUnit_Framework_MockObject_MockObject - */ - protected $_cacheMock; +use Magento\AdminNotification\Model\System\Message\Security; +use Magento\Framework\App\CacheInterface; +use Magento\Framework\App\Config\ScopeConfigInterface; +use Magento\Framework\HTTP\Adapter\Curl; +use Magento\Framework\HTTP\Adapter\CurlFactory; +use Magento\Framework\TestFramework\Unit\Helper\ObjectManager; +use PHPUnit\Framework\MockObject\MockObject; +use PHPUnit\Framework\TestCase; +class SecurityTest extends TestCase +{ /** - * @var \PHPUnit_Framework_MockObject_MockObject + * @var CacheInterface|MockObject */ - protected $_scopeConfigMock; + private $cacheMock; /** - * @var \PHPUnit_Framework_MockObject_MockObject + * @var ScopeConfigInterface|MockObject */ - protected $_configMock; + private $scopeConfigMock; /** - * @var \PHPUnit_Framework_MockObject_MockObject + * @var CurlFactory|MockObject */ - protected $_curlFactoryMock; + private $curlFactoryMock; /** - * @var \Magento\AdminNotification\Model\System\Message\Security + * @var Security */ - protected $_messageModel; + private $messageModel; - protected function setUp() + protected function setUp(): void { //Prepare objects for constructor - $this->_cacheMock = $this->createMock(\Magento\Framework\App\CacheInterface::class); - $this->_scopeConfigMock = $this->createMock(\Magento\Framework\App\Config\ScopeConfigInterface::class); - $this->_curlFactoryMock = $this->createPartialMock( - \Magento\Framework\HTTP\Adapter\CurlFactory::class, + $this->cacheMock = $this->createMock(CacheInterface::class); + $this->scopeConfigMock = $this->createMock(ScopeConfigInterface::class); + $this->curlFactoryMock = $this->createPartialMock( + CurlFactory::class, ['create'] ); - $objectManagerHelper = new \Magento\Framework\TestFramework\Unit\Helper\ObjectManager($this); + $objectManagerHelper = new ObjectManager($this); $arguments = [ - 'cache' => $this->_cacheMock, - 'scopeConfig' => $this->_scopeConfigMock, - 'curlFactory' => $this->_curlFactoryMock, + 'cache' => $this->cacheMock, + 'scopeConfig' => $this->scopeConfigMock, + 'curlFactory' => $this->curlFactoryMock, ]; - $this->_messageModel = $objectManagerHelper->getObject( - \Magento\AdminNotification\Model\System\Message\Security::class, + $this->messageModel = $objectManagerHelper->getObject( + Security::class, $arguments ); } @@ -64,16 +68,16 @@ protected function setUp() */ public function testIsDisplayed($expectedResult, $cached, $response) { - $this->_cacheMock->expects($this->any())->method('load')->will($this->returnValue($cached)); - $this->_cacheMock->expects($this->any())->method('save')->will($this->returnValue(null)); + $this->cacheMock->expects($this->any())->method('load')->will($this->returnValue($cached)); + $this->cacheMock->expects($this->any())->method('save')->will($this->returnValue(null)); - $httpAdapterMock = $this->createMock(\Magento\Framework\HTTP\Adapter\Curl::class); + $httpAdapterMock = $this->createMock(Curl::class); $httpAdapterMock->expects($this->any())->method('read')->will($this->returnValue($response)); - $this->_curlFactoryMock->expects($this->any())->method('create')->will($this->returnValue($httpAdapterMock)); + $this->curlFactoryMock->expects($this->any())->method('create')->will($this->returnValue($httpAdapterMock)); - $this->_scopeConfigMock->expects($this->any())->method('getValue')->will($this->returnValue(null)); + $this->scopeConfigMock->expects($this->any())->method('getValue')->will($this->returnValue(null)); - $this->assertEquals($expectedResult, $this->_messageModel->isDisplayed()); + $this->assertEquals($expectedResult, $this->messageModel->isDisplayed()); } /** @@ -92,6 +96,6 @@ public function testGetText() { $messageStart = 'Your web server is set up incorrectly'; - $this->assertStringStartsWith($messageStart, (string)$this->_messageModel->getText()); + $this->assertStringStartsWith($messageStart, (string)$this->messageModel->getText()); } } diff --git a/app/code/Magento/AdminNotification/Test/Unit/Observer/PredispatchAdminActionControllerObserverTest.php b/app/code/Magento/AdminNotification/Test/Unit/Observer/PredispatchAdminActionControllerObserverTest.php new file mode 100644 index 0000000000000..3209572ab0d70 --- /dev/null +++ b/app/code/Magento/AdminNotification/Test/Unit/Observer/PredispatchAdminActionControllerObserverTest.php @@ -0,0 +1,134 @@ +objectManager = new ObjectManager($this); + $this->observerMock = $this->createMock(Observer::class); + + $this->backendAuthSessionMock = $this->getMockBuilder(Session::class) + ->disableOriginalConstructor() + ->setMethods(['isLoggedIn']) + ->getMock(); + + $this->feedMock = $this->getMockBuilder(Feed::class) + ->disableOriginalConstructor() + ->setMethods(['checkUpdate']) + ->getMock(); + + $this->feedFactoryMock = $this->getMockBuilder(FeedFactory::class) + ->disableOriginalConstructor() + ->setMethods(['create']) + ->getMock(); + + $this->observer = $this->objectManager->getObject( + PredispatchAdminActionControllerObserver::class, + [ + '_feedFactory' => $this->feedFactoryMock, + '_backendAuthSession' => $this->backendAuthSessionMock, + ] + ); + } + + /** + * Test observer when admin user is logged in + */ + public function testPredispatchObserverWhenAdminLoggedIn() + { + $this->backendAuthSessionMock + ->expects($this->once()) + ->method('isLoggedIn') + ->willReturn(self::STATUS_ADMIN_LOGGED_IN); + + $this->feedFactoryMock + ->expects($this->once()) + ->method('create') + ->willReturn($this->feedMock); + + $this->feedMock + ->expects($this->once()) + ->method('checkUpdate') + ->willReturn($this->feedMock); + + $this->observer->execute($this->observerMock); + } + + /** + * Test observer when admin user is not logged in + */ + public function testPredispatchObserverWhenAdminIsNotLoggedIn() + { + $this->backendAuthSessionMock + ->expects($this->once()) + ->method('isLoggedIn') + ->willReturn(self::STATUS_ADMIN_IS_NOT_LOGGED); + + $this->feedFactoryMock + ->expects($this->never()) + ->method('create'); + + $this->feedMock + ->expects($this->never()) + ->method('checkUpdate'); + + $this->observer->execute($this->observerMock); + } +} diff --git a/app/code/Magento/AdminNotification/etc/adminhtml/system.xml b/app/code/Magento/AdminNotification/etc/adminhtml/system.xml index 71f25741ca851..1df75be1316db 100644 --- a/app/code/Magento/AdminNotification/etc/adminhtml/system.xml +++ b/app/code/Magento/AdminNotification/etc/adminhtml/system.xml @@ -8,17 +8,17 @@
- + - + Magento\Config\Model\Config\Source\Yesno - + Magento\AdminNotification\Model\Config\Source\Frequency - + Magento\Config\Block\System\Config\Form\Field\Notification diff --git a/app/code/Magento/AdminNotification/registration.php b/app/code/Magento/AdminNotification/registration.php index 8d427a458c18d..de8995efdb137 100644 --- a/app/code/Magento/AdminNotification/registration.php +++ b/app/code/Magento/AdminNotification/registration.php @@ -4,6 +4,6 @@ * See COPYING.txt for license details. */ -use \Magento\Framework\Component\ComponentRegistrar; +use Magento\Framework\Component\ComponentRegistrar; ComponentRegistrar::register(ComponentRegistrar::MODULE, 'Magento_AdminNotification', __DIR__); diff --git a/app/code/Magento/AdvancedPricingImportExport/etc/module.xml b/app/code/Magento/AdvancedPricingImportExport/etc/module.xml index 230fb17ae5544..ac7e6f860382b 100644 --- a/app/code/Magento/AdvancedPricingImportExport/etc/module.xml +++ b/app/code/Magento/AdvancedPricingImportExport/etc/module.xml @@ -6,6 +6,5 @@ */ --> - - + diff --git a/app/code/Magento/AdvancedPricingImportExport/registration.php b/app/code/Magento/AdvancedPricingImportExport/registration.php index 7a1d2feeccd45..8429e82b27fda 100644 --- a/app/code/Magento/AdvancedPricingImportExport/registration.php +++ b/app/code/Magento/AdvancedPricingImportExport/registration.php @@ -4,6 +4,6 @@ * See COPYING.txt for license details. */ -use \Magento\Framework\Component\ComponentRegistrar; +use Magento\Framework\Component\ComponentRegistrar; ComponentRegistrar::register(ComponentRegistrar::MODULE, 'Magento_AdvancedPricingImportExport', __DIR__); diff --git a/app/code/Magento/AdvancedSearch/etc/adminhtml/system.xml b/app/code/Magento/AdvancedSearch/etc/adminhtml/system.xml index 905dd3e7d1819..ecf8e328f51cb 100644 --- a/app/code/Magento/AdvancedSearch/etc/adminhtml/system.xml +++ b/app/code/Magento/AdvancedSearch/etc/adminhtml/system.xml @@ -10,37 +10,19 @@
- + When you enable this option your site may slow down. Magento\Config\Model\Config\Source\Yesno - + validate-digits 1 - + Magento\Config\Model\Config\Source\Yesno @@ -48,19 +30,19 @@ - + When you enable this option your site may slow down. Magento\Config\Model\Config\Source\Yesno - + validate-digits validate-zero-or-greater 1 - + Magento\Config\Model\Config\Source\Yesno When you enable this option your site may slow down. diff --git a/app/code/Magento/AdvancedSearch/registration.php b/app/code/Magento/AdvancedSearch/registration.php index c82ffa8e7e4d6..2c600f550dcf5 100644 --- a/app/code/Magento/AdvancedSearch/registration.php +++ b/app/code/Magento/AdvancedSearch/registration.php @@ -4,6 +4,6 @@ * See COPYING.txt for license details. */ -use \Magento\Framework\Component\ComponentRegistrar; +use Magento\Framework\Component\ComponentRegistrar; ComponentRegistrar::register(ComponentRegistrar::MODULE, 'Magento_AdvancedSearch', __DIR__); diff --git a/app/code/Magento/Amqp/registration.php b/app/code/Magento/Amqp/registration.php index 17d8382c698e8..2e4a4f362f1a3 100644 --- a/app/code/Magento/Amqp/registration.php +++ b/app/code/Magento/Amqp/registration.php @@ -4,6 +4,6 @@ * See COPYING.txt for license details. */ -use \Magento\Framework\Component\ComponentRegistrar; +use Magento\Framework\Component\ComponentRegistrar; ComponentRegistrar::register(ComponentRegistrar::MODULE, 'Magento_Amqp', __DIR__); diff --git a/app/code/Magento/AmqpStore/registration.php b/app/code/Magento/AmqpStore/registration.php index 4922879bfbf16..22ce677dc8302 100644 --- a/app/code/Magento/AmqpStore/registration.php +++ b/app/code/Magento/AmqpStore/registration.php @@ -4,6 +4,6 @@ * See COPYING.txt for license details. */ -use \Magento\Framework\Component\ComponentRegistrar; +use Magento\Framework\Component\ComponentRegistrar; ComponentRegistrar::register(ComponentRegistrar::MODULE, 'Magento_AmqpStore', __DIR__); diff --git a/app/code/Magento/Analytics/Block/Adminhtml/System/Config/CollectionTimeLabel.php b/app/code/Magento/Analytics/Block/Adminhtml/System/Config/CollectionTimeLabel.php index 34f2b7d53d9be..664278debd655 100644 --- a/app/code/Magento/Analytics/Block/Adminhtml/System/Config/CollectionTimeLabel.php +++ b/app/code/Magento/Analytics/Block/Adminhtml/System/Config/CollectionTimeLabel.php @@ -3,42 +3,47 @@ * Copyright © Magento, Inc. All rights reserved. * See COPYING.txt for license details. */ +declare(strict_types=1); + namespace Magento\Analytics\Block\Adminhtml\System\Config; -use Magento\Framework\App\ObjectManager; +use Magento\Backend\Block\Template\Context; +use Magento\Config\Block\System\Config\Form\Field; +use Magento\Framework\Data\Form\Element\AbstractElement; +use Magento\Framework\Locale\ResolverInterface; /** * Provides label with default Time Zone */ -class CollectionTimeLabel extends \Magento\Config\Block\System\Config\Form\Field +class CollectionTimeLabel extends Field { /** - * @var \Magento\Framework\Locale\ResolverInterface + * @var ResolverInterface */ private $localeResolver; /** - * @param \Magento\Backend\Block\Template\Context $context + * @param Context $context + * @param ResolverInterface $localeResolver * @param array $data - * @param \Magento\Framework\Locale\ResolverInterface|null $localeResolver */ public function __construct( - \Magento\Backend\Block\Template\Context $context, - array $data = [], - \Magento\Framework\Locale\ResolverInterface $localeResolver = null + Context $context, + ResolverInterface $localeResolver, + array $data = [] ) { - $this->localeResolver = $localeResolver ?: - ObjectManager::getInstance()->get(\Magento\Framework\Locale\ResolverInterface::class); parent::__construct($context, $data); + $this->localeResolver = $localeResolver; } /** * Add current time zone to comment, properly translated according to locale * - * @param \Magento\Framework\Data\Form\Element\AbstractElement $element + * @param AbstractElement $element + * * @return string */ - public function render(\Magento\Framework\Data\Form\Element\AbstractElement $element) + public function render(AbstractElement $element): string { $timeZoneCode = $this->_localeDate->getConfigTimezone(); $locale = $this->localeResolver->getLocale(); @@ -46,7 +51,7 @@ public function render(\Magento\Framework\Data\Form\Element\AbstractElement $ele ->getDisplayName(false, \IntlTimeZone::DISPLAY_LONG, $locale); $element->setData( 'comment', - sprintf("%s (%s)", $getLongTimeZoneName, $timeZoneCode) + sprintf('%s (%s)', $getLongTimeZoneName, $timeZoneCode) ); return parent::render($element); } diff --git a/app/code/Magento/Analytics/Cron/SignUp.php b/app/code/Magento/Analytics/Cron/SignUp.php index 8f97b839ec8ee..2588b87e84c1c 100644 --- a/app/code/Magento/Analytics/Cron/SignUp.php +++ b/app/code/Magento/Analytics/Cron/SignUp.php @@ -7,6 +7,7 @@ use Magento\Analytics\Model\Config\Backend\Enabled\SubscriptionHandler; use Magento\Analytics\Model\Connector; +use Magento\Framework\Exception\NotFoundException; use Magento\Framework\FlagManager; use Magento\Framework\App\Config\ReinitableConfigInterface; use Magento\Framework\App\Config\Storage\WriterInterface; @@ -57,22 +58,24 @@ public function __construct( } /** - * Execute scheduled subscription operation + * Execute scheduled subscription operation. + * * In case of failure writes message to notifications inbox * * @return bool + * @throws NotFoundException */ public function execute() { - $attemptsCount = $this->flagManager->getFlagData(SubscriptionHandler::ATTEMPTS_REVERSE_COUNTER_FLAG_CODE); + $attemptsCount = (int)$this->flagManager->getFlagData(SubscriptionHandler::ATTEMPTS_REVERSE_COUNTER_FLAG_CODE); - if (($attemptsCount === null) || ($attemptsCount <= 0)) { + if ($attemptsCount <= 0) { $this->deleteAnalyticsCronExpr(); $this->flagManager->deleteFlag(SubscriptionHandler::ATTEMPTS_REVERSE_COUNTER_FLAG_CODE); return false; } - $attemptsCount -= 1; + $attemptsCount--; $this->flagManager->saveFlag(SubscriptionHandler::ATTEMPTS_REVERSE_COUNTER_FLAG_CODE, $attemptsCount); $signUpResult = $this->connector->execute('signUp'); if ($signUpResult === false) { diff --git a/app/code/Magento/Analytics/Cron/Update.php b/app/code/Magento/Analytics/Cron/Update.php index 9062a7bac7551..b5e4b82a0777e 100644 --- a/app/code/Magento/Analytics/Cron/Update.php +++ b/app/code/Magento/Analytics/Cron/Update.php @@ -8,6 +8,7 @@ use Magento\Analytics\Model\AnalyticsToken; use Magento\Analytics\Model\Config\Backend\Baseurl\SubscriptionUpdateHandler; use Magento\Analytics\Model\Connector; +use Magento\Framework\Exception\NotFoundException; use Magento\Framework\FlagManager; use Magento\Framework\App\Config\ReinitableConfigInterface; use Magento\Framework\App\Config\Storage\WriterInterface; @@ -67,26 +68,37 @@ public function __construct( * Execute scheduled update operation * * @return bool + * @throws NotFoundException */ public function execute() { $result = false; - $attemptsCount = $this->flagManager + $attemptsCount = (int)$this->flagManager ->getFlagData(SubscriptionUpdateHandler::SUBSCRIPTION_UPDATE_REVERSE_COUNTER_FLAG_CODE); - if ($attemptsCount) { - $attemptsCount -= 1; + if (($attemptsCount > 0) && $this->analyticsToken->isTokenExist()) { + $attemptsCount--; + $this->flagManager + ->saveFlag(SubscriptionUpdateHandler::SUBSCRIPTION_UPDATE_REVERSE_COUNTER_FLAG_CODE, $attemptsCount); $result = $this->connector->execute('update'); } if ($result || ($attemptsCount <= 0) || (!$this->analyticsToken->isTokenExist())) { - $this->flagManager - ->deleteFlag(SubscriptionUpdateHandler::SUBSCRIPTION_UPDATE_REVERSE_COUNTER_FLAG_CODE); - $this->flagManager->deleteFlag(SubscriptionUpdateHandler::PREVIOUS_BASE_URL_FLAG_CODE); - $this->configWriter->delete(SubscriptionUpdateHandler::UPDATE_CRON_STRING_PATH); - $this->reinitableConfig->reinit(); + $this->exitFromUpdateProcess(); } return $result; } + + /** + * Clean-up flags and refresh configuration + */ + private function exitFromUpdateProcess(): void + { + $this->flagManager + ->deleteFlag(SubscriptionUpdateHandler::SUBSCRIPTION_UPDATE_REVERSE_COUNTER_FLAG_CODE); + $this->flagManager->deleteFlag(SubscriptionUpdateHandler::PREVIOUS_BASE_URL_FLAG_CODE); + $this->configWriter->delete(SubscriptionUpdateHandler::UPDATE_CRON_STRING_PATH); + $this->reinitableConfig->reinit(); + } } diff --git a/app/code/Magento/Analytics/Model/Connector.php b/app/code/Magento/Analytics/Model/Connector.php index 23b0ffa213b6e..c48f7f6b41946 100644 --- a/app/code/Magento/Analytics/Model/Connector.php +++ b/app/code/Magento/Analytics/Model/Connector.php @@ -53,7 +53,7 @@ public function __construct( public function execute($commandName) { if (!array_key_exists($commandName, $this->commands)) { - throw new NotFoundException(__('Command was not found.')); + throw new NotFoundException(__('Command "%1" was not found.', $commandName)); } /** @var \Magento\Analytics\Model\Connector\CommandInterface $command */ diff --git a/app/code/Magento/Analytics/ReportXml/QueryFactory.php b/app/code/Magento/Analytics/ReportXml/QueryFactory.php index 5da7adf794215..285ddb1f99e29 100644 --- a/app/code/Magento/Analytics/ReportXml/QueryFactory.php +++ b/app/code/Magento/Analytics/ReportXml/QueryFactory.php @@ -3,14 +3,18 @@ * Copyright © Magento, Inc. All rights reserved. * See COPYING.txt for license details. */ + namespace Magento\Analytics\ReportXml; use Magento\Analytics\ReportXml\DB\SelectBuilderFactory; use Magento\Framework\App\CacheInterface; +use Magento\Framework\DB\Select; use Magento\Framework\ObjectManagerInterface; +use Magento\Framework\Serialize\Serializer\Json; /** * Creates Query object according to configuration + * * Factory for @see \Magento\Analytics\ReportXml\Query */ class QueryFactory @@ -45,6 +49,11 @@ class QueryFactory */ private $selectHydrator; + /** + * @var Json + */ + private $jsonSerializer; + /** * QueryFactory constructor. * @@ -54,6 +63,7 @@ class QueryFactory * @param SelectBuilderFactory $selectBuilderFactory * @param Config $config * @param array $assemblers + * @param Json $jsonSerializer */ public function __construct( CacheInterface $queryCache, @@ -61,7 +71,8 @@ public function __construct( ObjectManagerInterface $objectManager, SelectBuilderFactory $selectBuilderFactory, Config $config, - array $assemblers + array $assemblers, + Json $jsonSerializer ) { $this->config = $config; $this->selectBuilderFactory = $selectBuilderFactory; @@ -69,6 +80,7 @@ public function __construct( $this->queryCache = $queryCache; $this->objectManager = $objectManager; $this->selectHydrator = $selectHydrator; + $this->jsonSerializer = $jsonSerializer; } /** @@ -101,14 +113,10 @@ private function constructQuery($queryName) $selectBuilder = $assembler->assemble($selectBuilder, $queryConfig); } $select = $selectBuilder->create(); - return $this->objectManager->create( - Query::class, - [ - 'select' => $select, - 'selectHydrator' => $this->selectHydrator, - 'connectionName' => $selectBuilder->getConnectionName(), - 'config' => $queryConfig - ] + return $this->createQueryObject( + $select, + $selectBuilder->getConnectionName(), + $queryConfig ); } @@ -122,19 +130,42 @@ public function create($queryName) { $cached = $this->queryCache->load($queryName); if ($cached) { - $queryData = json_decode($cached, true); - return $this->objectManager->create( - Query::class, - [ - 'select' => $this->selectHydrator->recreate($queryData['select_parts']), - 'selectHydrator' => $this->selectHydrator, - 'connectionName' => $queryData['connectionName'], - 'config' => $queryData['config'] - ] + $queryData = $this->jsonSerializer->unserialize($cached); + return $this->createQueryObject( + $this->selectHydrator->recreate($queryData['select_parts']), + $queryData['connectionName'], + $queryData['config'] ); } $query = $this->constructQuery($queryName); - $this->queryCache->save(json_encode($query), $queryName); + $this->queryCache->save( + $this->jsonSerializer->serialize($query), + $queryName + ); return $query; } + + /** + * Create query class using objectmanger + * + * @param Select $select + * @param string $connection + * @param array $queryConfig + * @return Query + */ + private function createQueryObject( + Select $select, + string $connection, + array $queryConfig + ) { + return $this->objectManager->create( + Query::class, + [ + 'select' => $select, + 'selectHydrator' => $this->selectHydrator, + 'connectionName' => $connection, + 'config' => $queryConfig + ] + ); + } } diff --git a/app/code/Magento/Analytics/Setup/Patch/Data/ActivateDataCollection.php b/app/code/Magento/Analytics/Setup/Patch/Data/ActivateDataCollection.php new file mode 100644 index 0000000000000..dd60d74b53d09 --- /dev/null +++ b/app/code/Magento/Analytics/Setup/Patch/Data/ActivateDataCollection.php @@ -0,0 +1,95 @@ +scopeConfig = $scopeConfig; + $this->subscriptionStatusProvider = $subscriptionStatusProvider; + $this->collectionTimeBackendModel = $collectionTimeBackendModel; + } + + /** + * @inheritDoc + * + * @throws LocalizedException + */ + public function apply() + { + $subscriptionStatus = $this->subscriptionStatusProvider->getStatus(); + $isCollectionProcessActivated = $this->scopeConfig->getValue(CollectionTime::CRON_SCHEDULE_PATH); + if ($subscriptionStatus !== $this->subscriptionStatusProvider->getStatusForDisabledSubscription() + && !$isCollectionProcessActivated + ) { + $this->collectionTimeBackendModel + ->setValue($this->scopeConfig->getValue($this->analyticsCollectionTimeConfigPath)); + $this->collectionTimeBackendModel->setPath($this->analyticsCollectionTimeConfigPath); + $this->collectionTimeBackendModel->afterSave(); + } + + return $this; + } + + /** + * @inheritDoc + */ + public function getAliases() + { + return []; + } + + /** + * @inheritDoc + */ + public static function getDependencies() + { + return [ + PrepareInitialConfig::class, + ]; + } +} diff --git a/app/code/Magento/Analytics/Setup/Patch/Data/PrepareInitialConfig.php b/app/code/Magento/Analytics/Setup/Patch/Data/PrepareInitialConfig.php index a352854a8b77b..97ac340f9d491 100644 --- a/app/code/Magento/Analytics/Setup/Patch/Data/PrepareInitialConfig.php +++ b/app/code/Magento/Analytics/Setup/Patch/Data/PrepareInitialConfig.php @@ -4,17 +4,18 @@ * See COPYING.txt for license details. */ +declare(strict_types=1); + namespace Magento\Analytics\Setup\Patch\Data; use Magento\Analytics\Model\Config\Backend\Enabled\SubscriptionHandler; +use Magento\Config\Model\Config\Source\Enabledisable; use Magento\Framework\Setup\ModuleDataSetupInterface; use Magento\Framework\Setup\Patch\DataPatchInterface; use Magento\Framework\Setup\Patch\PatchVersionInterface; /** - * Initial patch. - * - * @package Magento\Analytics\Setup\Patch + * Active subscription process for Advanced Reporting */ class PrepareInitialConfig implements DataPatchInterface, PatchVersionInterface { @@ -24,50 +25,47 @@ class PrepareInitialConfig implements DataPatchInterface, PatchVersionInterface private $moduleDataSetup; /** - * PrepareInitialConfig constructor. + * @var SubscriptionHandler + */ + private $subscriptionHandler; + + /** + * @var string + */ + private $subscriptionEnabledConfigPath = 'analytics/subscription/enabled'; + + /** * @param ModuleDataSetupInterface $moduleDataSetup + * @param SubscriptionHandler $subscriptionHandler */ public function __construct( - ModuleDataSetupInterface $moduleDataSetup + ModuleDataSetupInterface $moduleDataSetup, + SubscriptionHandler $subscriptionHandler ) { $this->moduleDataSetup = $moduleDataSetup; + $this->subscriptionHandler = $subscriptionHandler; } /** - * {@inheritdoc} + * @inheritDoc */ public function apply() { - $this->moduleDataSetup->getConnection()->insertMultiple( + $this->moduleDataSetup->getConnection()->insert( $this->moduleDataSetup->getTable('core_config_data'), [ - [ - 'scope' => 'default', - 'scope_id' => 0, - 'path' => 'analytics/subscription/enabled', - 'value' => 1 - ], - [ - 'scope' => 'default', - 'scope_id' => 0, - 'path' => SubscriptionHandler::CRON_STRING_PATH, - 'value' => join(' ', SubscriptionHandler::CRON_EXPR_ARRAY) - ] + 'path' => $this->subscriptionEnabledConfigPath, + 'value' => Enabledisable::ENABLE_VALUE, ] ); - $this->moduleDataSetup->getConnection()->insert( - $this->moduleDataSetup->getTable('flag'), - [ - 'flag_code' => SubscriptionHandler::ATTEMPTS_REVERSE_COUNTER_FLAG_CODE, - 'state' => 0, - 'flag_data' => 24, - ] - ); + $this->subscriptionHandler->processEnabled(); + + return $this; } /** - * {@inheritdoc} + * @inheritDoc */ public static function getDependencies() { @@ -75,7 +73,7 @@ public static function getDependencies() } /** - * {@inheritdoc} + * @inheritDoc */ public static function getVersion() { @@ -83,7 +81,7 @@ public static function getVersion() } /** - * {@inheritdoc} + * @inheritDoc */ public function getAliases() { diff --git a/app/code/Magento/Analytics/Test/Mftf/Test/AdminAdvancedReportingButtonTest.xml b/app/code/Magento/Analytics/Test/Mftf/Test/AdminAdvancedReportingButtonTest.xml index e660a2eb8d428..cbcbb3a5dd64c 100644 --- a/app/code/Magento/Analytics/Test/Mftf/Test/AdminAdvancedReportingButtonTest.xml +++ b/app/code/Magento/Analytics/Test/Mftf/Test/AdminAdvancedReportingButtonTest.xml @@ -22,10 +22,10 @@ - + - + @@ -35,4 +35,4 @@ - \ No newline at end of file + diff --git a/app/code/Magento/Analytics/Test/Mftf/Test/AdminAdvancedReportingNavigateMenuTest.xml b/app/code/Magento/Analytics/Test/Mftf/Test/AdminAdvancedReportingNavigateMenuTest.xml index 67d6715285697..ee25e80fcab30 100644 --- a/app/code/Magento/Analytics/Test/Mftf/Test/AdminAdvancedReportingNavigateMenuTest.xml +++ b/app/code/Magento/Analytics/Test/Mftf/Test/AdminAdvancedReportingNavigateMenuTest.xml @@ -20,16 +20,17 @@ - + - + + diff --git a/app/code/Magento/Analytics/Test/Mftf/Test/AdminConfigurationBlankIndustryTest.xml b/app/code/Magento/Analytics/Test/Mftf/Test/AdminConfigurationBlankIndustryTest.xml index 914cb59b64e4e..17d463030d91c 100644 --- a/app/code/Magento/Analytics/Test/Mftf/Test/AdminConfigurationBlankIndustryTest.xml +++ b/app/code/Magento/Analytics/Test/Mftf/Test/AdminConfigurationBlankIndustryTest.xml @@ -18,9 +18,9 @@ - + - + diff --git a/app/code/Magento/Analytics/Test/Mftf/Test/AdminConfigurationEnableDisableAnalyticsTest.xml b/app/code/Magento/Analytics/Test/Mftf/Test/AdminConfigurationEnableDisableAnalyticsTest.xml index 1c1a3b27b06af..b03488c240604 100644 --- a/app/code/Magento/Analytics/Test/Mftf/Test/AdminConfigurationEnableDisableAnalyticsTest.xml +++ b/app/code/Magento/Analytics/Test/Mftf/Test/AdminConfigurationEnableDisableAnalyticsTest.xml @@ -18,9 +18,9 @@ - + - + diff --git a/app/code/Magento/Analytics/Test/Mftf/Test/AdminConfigurationIndustryTest.xml b/app/code/Magento/Analytics/Test/Mftf/Test/AdminConfigurationIndustryTest.xml index bb682c4468012..c19fddc6aa0ce 100644 --- a/app/code/Magento/Analytics/Test/Mftf/Test/AdminConfigurationIndustryTest.xml +++ b/app/code/Magento/Analytics/Test/Mftf/Test/AdminConfigurationIndustryTest.xml @@ -19,7 +19,7 @@ - + diff --git a/app/code/Magento/Analytics/Test/Mftf/Test/AdminConfigurationPermissionTest.xml b/app/code/Magento/Analytics/Test/Mftf/Test/AdminConfigurationPermissionTest.xml index 58e809ec45c4a..40f80ce52c2de 100644 --- a/app/code/Magento/Analytics/Test/Mftf/Test/AdminConfigurationPermissionTest.xml +++ b/app/code/Magento/Analytics/Test/Mftf/Test/AdminConfigurationPermissionTest.xml @@ -21,10 +21,10 @@ - + - + @@ -35,8 +35,9 @@ - + + @@ -51,7 +52,7 @@ - + diff --git a/app/code/Magento/Analytics/Test/Mftf/Test/AdminConfigurationTimeToSendDataTest.xml b/app/code/Magento/Analytics/Test/Mftf/Test/AdminConfigurationTimeToSendDataTest.xml index 8ebd8cb594bee..6231b17c17b02 100644 --- a/app/code/Magento/Analytics/Test/Mftf/Test/AdminConfigurationTimeToSendDataTest.xml +++ b/app/code/Magento/Analytics/Test/Mftf/Test/AdminConfigurationTimeToSendDataTest.xml @@ -19,9 +19,9 @@ - + - + diff --git a/app/code/Magento/Analytics/Test/Unit/Block/Adminhtml/System/Config/CollectionTimeLabelTest.php b/app/code/Magento/Analytics/Test/Unit/Block/Adminhtml/System/Config/CollectionTimeLabelTest.php index 08ee3c356937a..3a98fd6acbf4d 100644 --- a/app/code/Magento/Analytics/Test/Unit/Block/Adminhtml/System/Config/CollectionTimeLabelTest.php +++ b/app/code/Magento/Analytics/Test/Unit/Block/Adminhtml/System/Config/CollectionTimeLabelTest.php @@ -3,17 +3,25 @@ * Copyright © Magento, Inc. All rights reserved. * See COPYING.txt for license details. */ +declare(strict_types=1); + namespace Magento\Analytics\Test\Unit\Block\Adminhtml\System\Config; use Magento\Analytics\Block\Adminhtml\System\Config\CollectionTimeLabel; use Magento\Backend\Block\Template\Context; use Magento\Framework\Data\Form; use Magento\Framework\Data\Form\Element\AbstractElement; +use Magento\Framework\Escaper; use Magento\Framework\Locale\ResolverInterface; use Magento\Framework\Stdlib\DateTime\TimezoneInterface; use Magento\Framework\TestFramework\Unit\Helper\ObjectManager; +use PHPUnit\Framework\MockObject\MockObject; +use PHPUnit\Framework\TestCase; -class CollectionTimeLabelTest extends \PHPUnit\Framework\TestCase +/** + * Test class for \Magento\Analytics\Block\Adminhtml\System\Config\CollectionTimeLabel + */ +class CollectionTimeLabelTest extends TestCase { /** * @var CollectionTimeLabel @@ -21,25 +29,33 @@ class CollectionTimeLabelTest extends \PHPUnit\Framework\TestCase private $collectionTimeLabel; /** - * @var Context|\PHPUnit_Framework_MockObject_MockObject + * @var Context|MockObject */ private $contextMock; /** - * @var TimezoneInterface|\PHPUnit_Framework_MockObject_MockObject + * @var ResolverInterface|MockObject + */ + private $localeResolverMock; + + /** + * @var Form|MockObject + */ + private $formMock; + + /** + * @var TimezoneInterface|MockObject */ private $timeZoneMock; /** - * @var AbstractElement|\PHPUnit_Framework_MockObject_MockObject + * @var AbstractElement|MockObject */ private $abstractElementMock; /** - * @var ResolverInterface|\PHPUnit_Framework_MockObject_MockObject + * @inheritDoc */ - private $localeResolver; - protected function setUp() { $this->abstractElementMock = $this->getMockBuilder(AbstractElement::class) @@ -48,7 +64,7 @@ protected function setUp() ->getMock(); $objectManager = new ObjectManager($this); - $escaper = $objectManager->getObject(\Magento\Framework\Escaper::class); + $escaper = $objectManager->getObject(Escaper::class); $reflection = new \ReflectionClass($this->abstractElementMock); $reflection_property = $reflection->getProperty('_escaper'); $reflection_property->setAccessible(true); @@ -64,35 +80,35 @@ protected function setUp() $this->timeZoneMock = $this->getMockBuilder(TimezoneInterface::class) ->disableOriginalConstructor() ->getMock(); - $this->contextMock->expects($this->any()) - ->method('getLocaleDate') + $this->contextMock->method('getLocaleDate') ->willReturn($this->timeZoneMock); - $this->localeResolver = $this->getMockBuilder(ResolverInterface::class) + $this->localeResolverMock = $this->getMockBuilder(ResolverInterface::class) ->disableOriginalConstructor() ->setMethods(['getLocale']) ->getMockForAbstractClass(); - $objectManager = new ObjectManager($this); $this->collectionTimeLabel = $objectManager->getObject( CollectionTimeLabel::class, [ 'context' => $this->contextMock, - 'localeResolver' => $this->localeResolver + 'localeResolver' => $this->localeResolverMock ] ); } + /** + * Test for \Magento\Analytics\Block\Adminhtml\System\Config\CollectionTimeLabel::render() + */ public function testRender() { - $timeZone = "America/New_York"; + $timeZone = 'America/New_York'; $this->abstractElementMock->setForm($this->formMock); $this->timeZoneMock->expects($this->once()) ->method('getConfigTimezone') ->willReturn($timeZone); - $this->abstractElementMock->expects($this->any()) - ->method('getComment') + $this->abstractElementMock->method('getComment') ->willReturn('Eastern Standard Time (America/New_York)'); - $this->localeResolver->expects($this->once()) + $this->localeResolverMock->expects($this->once()) ->method('getLocale') ->willReturn('en_US'); $this->assertRegExp( diff --git a/app/code/Magento/Analytics/Test/Unit/Cron/UpdateTest.php b/app/code/Magento/Analytics/Test/Unit/Cron/UpdateTest.php index aa3011ffc94f6..fa007268474c4 100644 --- a/app/code/Magento/Analytics/Test/Unit/Cron/UpdateTest.php +++ b/app/code/Magento/Analytics/Test/Unit/Cron/UpdateTest.php @@ -11,6 +11,7 @@ use Magento\Analytics\Model\Connector; use Magento\Framework\App\Config\ReinitableConfigInterface; use Magento\Framework\App\Config\Storage\WriterInterface; +use Magento\Framework\Exception\NotFoundException; use Magento\Framework\FlagManager; class UpdateTest extends \PHPUnit\Framework\TestCase @@ -45,6 +46,9 @@ class UpdateTest extends \PHPUnit\Framework\TestCase */ private $update; + /** + * @inheritDoc + */ protected function setUp() { $this->connectorMock = $this->getMockBuilder(Connector::class) @@ -74,6 +78,7 @@ protected function setUp() /** * @return void + * @throws NotFoundException */ public function testExecuteWithoutToken() { @@ -82,12 +87,12 @@ public function testExecuteWithoutToken() ->with(SubscriptionUpdateHandler::SUBSCRIPTION_UPDATE_REVERSE_COUNTER_FLAG_CODE) ->willReturn(10); $this->connectorMock - ->expects($this->once()) + ->expects($this->never()) ->method('execute') ->with('update') ->willReturn(false); $this->analyticsTokenMock - ->expects($this->once()) + ->expects($this->any()) ->method('isTokenExist') ->willReturn(false); $this->addFinalOutputAsserts(); @@ -120,6 +125,7 @@ private function addFinalOutputAsserts(bool $isExecuted = true) * @param $counterData * @return void * @dataProvider executeWithEmptyReverseCounterDataProvider + * @throws NotFoundException */ public function testExecuteWithEmptyReverseCounter($counterData) { @@ -159,6 +165,7 @@ public function executeWithEmptyReverseCounterDataProvider() * @param bool $functionResult * @return void * @dataProvider executeRegularScenarioDataProvider + * @throws NotFoundException */ public function testExecuteRegularScenario( int $reverseCount, @@ -170,6 +177,10 @@ public function testExecuteRegularScenario( ->method('getFlagData') ->with(SubscriptionUpdateHandler::SUBSCRIPTION_UPDATE_REVERSE_COUNTER_FLAG_CODE) ->willReturn($reverseCount); + $this->flagManagerMock + ->expects($this->once()) + ->method('saveFlag') + ->with(SubscriptionUpdateHandler::SUBSCRIPTION_UPDATE_REVERSE_COUNTER_FLAG_CODE, $reverseCount - 1); $this->connectorMock ->expects($this->once()) ->method('execute') diff --git a/app/code/Magento/Analytics/Test/Unit/Model/ConnectorTest.php b/app/code/Magento/Analytics/Test/Unit/Model/ConnectorTest.php index 714d0daf5c419..90f6fa1643660 100644 --- a/app/code/Magento/Analytics/Test/Unit/Model/ConnectorTest.php +++ b/app/code/Magento/Analytics/Test/Unit/Model/ConnectorTest.php @@ -57,9 +57,13 @@ public function testExecute() } /** + * Executing non-existing command + * * @expectedException \Magento\Framework\Exception\NotFoundException + * @expectedExceptionMessage Command "register" was not found. + * @return void */ - public function testExecuteCommandNotFound() + public function testExecuteCommandNotFound(): void { $commandName = 'register'; $this->connector->execute($commandName); diff --git a/app/code/Magento/Analytics/Test/Unit/ReportXml/QueryFactoryTest.php b/app/code/Magento/Analytics/Test/Unit/ReportXml/QueryFactoryTest.php index 9a3805a50f167..032ef1e107825 100644 --- a/app/code/Magento/Analytics/Test/Unit/ReportXml/QueryFactoryTest.php +++ b/app/code/Magento/Analytics/Test/Unit/ReportXml/QueryFactoryTest.php @@ -3,161 +3,171 @@ * Copyright © Magento, Inc. All rights reserved. * See COPYING.txt for license details. */ + +declare(strict_types=1); + namespace Magento\Analytics\Test\Unit\ReportXml; +use Magento\Analytics\ReportXml\QueryFactory; +use Magento\Analytics\ReportXml\Query; +use Magento\Analytics\ReportXml\Config; +use Magento\Framework\DB\Select; +use Magento\Analytics\ReportXml\DB\Assembler\AssemblerInterface; +use Magento\Framework\App\CacheInterface; +use Magento\Framework\ObjectManagerInterface; +use Magento\Analytics\ReportXml\SelectHydrator; +use Magento\Analytics\ReportXml\DB\SelectBuilder; +use Magento\Analytics\ReportXml\DB\SelectBuilderFactory; +use Magento\Framework\Serialize\Serializer\Json; +use Magento\Framework\TestFramework\Unit\Helper\ObjectManager as ObjectManagerHelper; +use PHPUnit\Framework\MockObject\MockObject; +use PHPUnit\Framework\TestCase; + /** * A unit test for testing of the query factory. + * + * @SuppressWarnings(PHPMD.CouplingBetweenObjects) */ -class QueryFactoryTest extends \PHPUnit\Framework\TestCase +class QueryFactoryTest extends TestCase { + const STUB_QUERY_NAME = 'test_query'; + const STUB_CONNECTION = 'default'; + /** - * @var \Magento\Analytics\ReportXml\QueryFactory + * @var QueryFactory */ private $subject; /** - * @var \Magento\Analytics\ReportXml\Query|\PHPUnit_Framework_MockObject_MockObject + * @var Query|MockObject */ private $queryMock; /** - * @var \Magento\Analytics\ReportXml\Config|\PHPUnit_Framework_MockObject_MockObject + * @var Config|MockObject */ private $configMock; /** - * @var \Magento\Framework\DB\Select|\PHPUnit_Framework_MockObject_MockObject + * @var Select|MockObject */ private $selectMock; /** - * @var \Magento\Analytics\ReportXml\DB\Assembler\AssemblerInterface|\PHPUnit_Framework_MockObject_MockObject + * @var AssemblerInterface|MockObject */ private $assemblerMock; /** - * @var \Magento\Framework\App\CacheInterface|\PHPUnit_Framework_MockObject_MockObject + * @var CacheInterface|MockObject */ private $queryCacheMock; /** - * @var \Magento\Framework\ObjectManagerInterface|\PHPUnit_Framework_MockObject_MockObject + * @var ObjectManagerInterface|MockObject */ private $objectManagerMock; /** - * @var \Magento\Analytics\ReportXml\SelectHydrator|\PHPUnit_Framework_MockObject_MockObject + * @var SelectHydrator|MockObject */ private $selectHydratorMock; /** - * @var \Magento\Framework\TestFramework\Unit\Helper\ObjectManager + * @var ObjectManagerHelper */ private $objectManagerHelper; /** - * @var \Magento\Analytics\ReportXml\DB\SelectBuilderFactory|\PHPUnit_Framework_MockObject_MockObject + * @var SelectBuilderFactory|MockObject */ private $selectBuilderFactoryMock; + /** + * @var Json|MockObject + */ + private $jsonSerializerMock; + /** * @return void */ - protected function setUp() + protected function setUp(): void { - $this->queryMock = $this->getMockBuilder( - \Magento\Analytics\ReportXml\Query::class - ) - ->disableOriginalConstructor() - ->getMock(); - - $this->configMock = $this->getMockBuilder( - \Magento\Analytics\ReportXml\Config::class - ) - ->disableOriginalConstructor() - ->getMock(); - - $this->selectMock = $this->getMockBuilder( - \Magento\Framework\DB\Select::class - ) - ->disableOriginalConstructor() - ->getMock(); - - $this->assemblerMock = $this->getMockBuilder( - \Magento\Analytics\ReportXml\DB\Assembler\AssemblerInterface::class - ) - ->disableOriginalConstructor() - ->getMock(); - - $this->queryCacheMock = $this->getMockBuilder( - \Magento\Framework\App\CacheInterface::class - ) - ->disableOriginalConstructor() - ->getMock(); - - $this->objectManagerMock = $this->getMockBuilder( - \Magento\Framework\ObjectManagerInterface::class - ) - ->disableOriginalConstructor() - ->getMock(); - - $this->selectHydratorMock = $this->getMockBuilder( - \Magento\Analytics\ReportXml\SelectHydrator::class - ) - ->disableOriginalConstructor() - ->getMock(); - - $this->selectBuilderFactoryMock = $this->getMockBuilder( - \Magento\Analytics\ReportXml\DB\SelectBuilderFactory::class - ) - ->disableOriginalConstructor() - ->getMock(); - - $this->objectManagerHelper = - new \Magento\Framework\TestFramework\Unit\Helper\ObjectManager($this); + $this->queryMock = $this->getMockBuilder(Query::class) + ->disableOriginalConstructor() + ->getMock(); + + $this->configMock = $this->getMockBuilder(Config::class) + ->disableOriginalConstructor() + ->getMock(); + $this->selectMock = $this->getMockBuilder(Select::class) + ->disableOriginalConstructor() + ->getMock(); + + $this->assemblerMock = $this->getMockBuilder(AssemblerInterface::class) + ->disableOriginalConstructor() + ->getMock(); + + $this->queryCacheMock = $this->getMockBuilder(CacheInterface::class) + ->disableOriginalConstructor() + ->getMock(); + + $this->objectManagerMock = $this->getMockBuilder(ObjectManagerInterface::class) + ->disableOriginalConstructor() + ->getMock(); + + $this->selectHydratorMock = $this->getMockBuilder(SelectHydrator::class) + ->disableOriginalConstructor() + ->getMock(); + + $this->selectBuilderFactoryMock = $this->getMockBuilder(SelectBuilderFactory::class) + ->disableOriginalConstructor() + ->getMock(); + + $this->jsonSerializerMock = $this->createMock(Json::class); + + $this->objectManagerHelper = new ObjectManagerHelper($this); $this->subject = $this->objectManagerHelper->getObject( - \Magento\Analytics\ReportXml\QueryFactory::class, + QueryFactory::class, [ - 'config' => $this->configMock, - 'selectBuilderFactory' => $this->selectBuilderFactoryMock, - 'assemblers' => [$this->assemblerMock], 'queryCache' => $this->queryCacheMock, + 'selectHydrator' => $this->selectHydratorMock, 'objectManager' => $this->objectManagerMock, - 'selectHydrator' => $this->selectHydratorMock + 'selectBuilderFactory' => $this->selectBuilderFactoryMock, + 'config' => $this->configMock, + 'assemblers' => [$this->assemblerMock], + 'jsonSerializer' => $this->jsonSerializerMock ] ); } /** + * Test create() if query cached + * * @return void + * @dataProvider queryDataProvider */ - public function testCreateCached() + public function testCreateIfQueryCached(array $queryDataMock, string $jsonEncodeData): void { - $queryName = 'test_query'; + $queryConfigMock = $queryDataMock['config']; + $queryName = $queryConfigMock['name']; $this->queryCacheMock->expects($this->any()) ->method('load') ->with($queryName) - ->willReturn('{"connectionName":"sales","config":{},"select_parts":{}}'); + ->willReturn($jsonEncodeData); + + $this->jsonSerializerMock->expects($this->once()) + ->method('unserialize') + ->willReturn($queryDataMock); $this->selectHydratorMock->expects($this->any()) ->method('recreate') ->with([]) ->willReturn($this->selectMock); - $this->objectManagerMock->expects($this->once()) - ->method('create') - ->with( - \Magento\Analytics\ReportXml\Query::class, - [ - 'select' => $this->selectMock, - 'selectHydrator' => $this->selectHydratorMock, - 'connectionName' => 'sales', - 'config' => [] - ] - ) - ->willReturn($this->queryMock); + $this->createQueryObjectMock($queryDataMock); $this->queryCacheMock->expects($this->never()) ->method('save'); @@ -169,22 +179,19 @@ public function testCreateCached() } /** + * Test create() if query not cached + * * @return void + * @dataProvider queryDataProvider */ - public function testCreateNotCached() + public function testCreateIfQueryNotCached(array $queryDataMock, string $jsonEncodeData): void { - $queryName = 'test_query'; - - $queryConfigMock = [ - 'name' => 'test_query', - 'connection' => 'sales' - ]; + $queryConfigMock = $queryDataMock['config']; + $queryName = $queryConfigMock['name']; - $selectBuilderMock = $this->getMockBuilder( - \Magento\Analytics\ReportXml\DB\SelectBuilder::class - ) - ->disableOriginalConstructor() - ->getMock(); + $selectBuilderMock = $this->getMockBuilder(SelectBuilder::class) + ->disableOriginalConstructor() + ->getMock(); $selectBuilderMock->expects($this->once()) ->method('setConnectionName') ->with($queryConfigMock['connection']); @@ -214,26 +221,65 @@ public function testCreateNotCached() ->with($selectBuilderMock, $queryConfigMock) ->willReturn($selectBuilderMock); + $this->createQueryObjectMock($queryDataMock); + + $this->jsonSerializerMock->expects($this->once()) + ->method('serialize') + ->willReturn($jsonEncodeData); + + $this->queryCacheMock->expects($this->once()) + ->method('save') + ->with($jsonEncodeData, $queryName); + + $this->assertEquals( + $this->queryMock, + $this->subject->create($queryName) + ); + } + + /** + * Get Query Data Provider + * + * @return array + */ + public function queryDataProvider(): array + { + return [ + [ + 'getQueryDataMock' => [ + 'connectionName' => self::STUB_CONNECTION, + 'config' => [ + 'name' => self::STUB_QUERY_NAME, + 'connection' => self::STUB_CONNECTION + ], + 'select_parts' => [] + ], + 'getQueryDataJsonEncodeMock' => '{"connectionName":"default",'. + '"config":{"name":"test_query",'. + '"connection":"default"},"select_parts":[]}' + ] + ]; + } + + /** + * ObjectManager Mock with Query class + * + * @param array $queryDataMock + * @return void + */ + private function createQueryObjectMock($queryDataMock): void + { $this->objectManagerMock->expects($this->once()) ->method('create') ->with( - \Magento\Analytics\ReportXml\Query::class, + Query::class, [ 'select' => $this->selectMock, 'selectHydrator' => $this->selectHydratorMock, - 'connectionName' => $queryConfigMock['connection'], - 'config' => $queryConfigMock + 'connectionName' => $queryDataMock['connectionName'], + 'config' => $queryDataMock['config'] ] ) ->willReturn($this->queryMock); - - $this->queryCacheMock->expects($this->once()) - ->method('save') - ->with(json_encode($this->queryMock), $queryName); - - $this->assertEquals( - $this->queryMock, - $this->subject->create($queryName) - ); } } diff --git a/app/code/Magento/Analytics/etc/adminhtml/system.xml b/app/code/Magento/Analytics/etc/adminhtml/system.xml index 2a04128099345..0aba6e4dd00ed 100644 --- a/app/code/Magento/Analytics/etc/adminhtml/system.xml +++ b/app/code/Magento/Analytics/etc/adminhtml/system.xml @@ -7,40 +7,43 @@ --> -
+
general Magento_Analytics::analytics_settings - + For more information, see our terms and conditions.]]> - + Magento\Config\Model\Config\Source\Enabledisable Magento\Analytics\Model\Config\Backend\Enabled Magento\Analytics\Block\Adminhtml\System\Config\SubscriptionStatusLabel analytics/subscription/enabled - + Magento\Analytics\Block\Adminhtml\System\Config\CollectionTimeLabel Magento\Analytics\Model\Config\Backend\CollectionTime + + 1 + - + Industry Data In order to personalize your Advanced Reporting experience, please select your industry. Magento\Analytics\Model\Config\Source\Vertical Magento\Analytics\Model\Config\Backend\Vertical Magento\Analytics\Block\Adminhtml\System\Config\Vertical - - 1 - + + 1 + - + Learn more about - + @@ -161,15 +161,13 @@ payment/banktransfer/active payment/cashondelivery/title payment/cashondelivery/active - payment/authorizenet_directpost/title - payment/authorizenet_directpost/active - payment/paypal_billing_agreement/title - payment/paypal_billing_agreement/active - payment/braintree/title - payment/braintree/active - payment/braintree_paypal/title - payment/braintree_paypal/active - analytics/general/vertical + payment/paypal_billing_agreement/title + payment/paypal_billing_agreement/active + payment/braintree/title + payment/braintree/active + payment/braintree_paypal/title + payment/braintree_paypal/active + analytics/general/vertical diff --git a/app/code/Magento/Analytics/i18n/en_US.csv b/app/code/Magento/Analytics/i18n/en_US.csv index 4faa48fb73709..9e86e8ce072c5 100644 --- a/app/code/Magento/Analytics/i18n/en_US.csv +++ b/app/code/Magento/Analytics/i18n/en_US.csv @@ -7,7 +7,7 @@ "There was an error save new configuration value.","There was an error save new configuration value." "Please select an industry.","Please select an industry." "--Please Select--","--Please Select--" -"Command was not found.","Command was not found." +"Command "%1" was not found.","Command "%1" was not found." "Input data must be string or convertible into string.","Input data must be string or convertible into string." "Input data must be non-empty string.","Input data must be non-empty string." "Not valid cipher method.","Not valid cipher method." diff --git a/app/code/Magento/AsynchronousOperations/Api/SaveMultipleOperationsInterface.php b/app/code/Magento/AsynchronousOperations/Api/SaveMultipleOperationsInterface.php new file mode 100644 index 0000000000000..8563ab6541a0c --- /dev/null +++ b/app/code/Magento/AsynchronousOperations/Api/SaveMultipleOperationsInterface.php @@ -0,0 +1,27 @@ +entityManager = $entityManager; $this->bulkSummaryFactory= $bulkSummaryFactory; @@ -91,7 +92,7 @@ public function __construct( $this->resourceConnection = $resourceConnection; $this->publisher = $publisher; $this->logger = $logger; - $this->userContext = $userContext ?: ObjectManager::getInstance()->get(UserContextInterface::class); + $this->userContext = $userContext; } /** @@ -116,7 +117,6 @@ public function scheduleBulk($bulkUuid, array $operations, $description, $userId $bulkSummary->setUserId($userId); $bulkSummary->setUserType($userType); $bulkSummary->setOperationCount((int)$bulkSummary->getOperationCount() + count($operations)); - $this->entityManager->save($bulkSummary); $connection->commit(); diff --git a/app/code/Magento/AsynchronousOperations/Model/MassConsumer.php b/app/code/Magento/AsynchronousOperations/Model/MassConsumer.php index e3ba8b0681971..e2f756a9e8fcd 100644 --- a/app/code/Magento/AsynchronousOperations/Model/MassConsumer.php +++ b/app/code/Magento/AsynchronousOperations/Model/MassConsumer.php @@ -3,17 +3,16 @@ * Copyright © Magento, Inc. All rights reserved. * See COPYING.txt for license details. */ - declare(strict_types=1); namespace Magento\AsynchronousOperations\Model; -use Magento\Framework\Registry; use Magento\Framework\MessageQueue\CallbackInvokerInterface; use Magento\Framework\MessageQueue\ConsumerConfigurationInterface; +use Magento\Framework\MessageQueue\ConsumerInterface; use Magento\Framework\MessageQueue\EnvelopeInterface; use Magento\Framework\MessageQueue\QueueInterface; -use Magento\Framework\MessageQueue\ConsumerInterface; +use Magento\Framework\Registry; /** * Class Consumer used to process OperationInterface messages. @@ -28,19 +27,19 @@ class MassConsumer implements ConsumerInterface private $invoker; /** - * @var \Magento\Framework\MessageQueue\ConsumerConfigurationInterface + * @var ConsumerConfigurationInterface */ private $configuration; /** - * @var Registry + * @var MassConsumerEnvelopeCallbackFactory */ - private $registry; + private $massConsumerEnvelopeCallback; /** - * @var MassConsumerEnvelopeCallbackFactory + * @var Registry */ - private $massConsumerEnvelopeCallback; + private $registry; /** * Initialize dependencies. @@ -54,13 +53,12 @@ public function __construct( CallbackInvokerInterface $invoker, ConsumerConfigurationInterface $configuration, MassConsumerEnvelopeCallbackFactory $massConsumerEnvelopeCallback, - Registry $registry = null + Registry $registry ) { $this->invoker = $invoker; $this->configuration = $configuration; $this->massConsumerEnvelopeCallback = $massConsumerEnvelopeCallback; - $this->registry = $registry ?? \Magento\Framework\App\ObjectManager::getInstance() - ->get(Registry::class); + $this->registry = $registry; } /** diff --git a/app/code/Magento/AsynchronousOperations/Model/MassSchedule.php b/app/code/Magento/AsynchronousOperations/Model/MassSchedule.php index 89d468159c6e9..4dcaf7279a570 100644 --- a/app/code/Magento/AsynchronousOperations/Model/MassSchedule.php +++ b/app/code/Magento/AsynchronousOperations/Model/MassSchedule.php @@ -3,24 +3,23 @@ * Copyright © Magento, Inc. All rights reserved. * See COPYING.txt for license details. */ - declare(strict_types=1); namespace Magento\AsynchronousOperations\Model; -use Magento\Framework\App\ObjectManager; -use Magento\Framework\DataObject\IdentityGeneratorInterface; -use Magento\Framework\Exception\LocalizedException; -use Magento\AsynchronousOperations\Api\Data\ItemStatusInterfaceFactory; use Magento\AsynchronousOperations\Api\Data\AsyncResponseInterface; use Magento\AsynchronousOperations\Api\Data\AsyncResponseInterfaceFactory; use Magento\AsynchronousOperations\Api\Data\ItemStatusInterface; -use Magento\Framework\Bulk\BulkManagementInterface; -use Magento\Framework\Exception\BulkException; -use Psr\Log\LoggerInterface; +use Magento\AsynchronousOperations\Api\Data\ItemStatusInterfaceFactory; use Magento\AsynchronousOperations\Model\ResourceModel\Operation\OperationRepository; use Magento\Authorization\Model\UserContextInterface; +use Magento\Framework\Bulk\BulkManagementInterface; +use Magento\Framework\DataObject\IdentityGeneratorInterface; use Magento\Framework\Encryption\Encryptor; +use Magento\AsynchronousOperations\Api\SaveMultipleOperationsInterface; +use Magento\Framework\Exception\BulkException; +use Magento\Framework\Exception\LocalizedException; +use Psr\Log\LoggerInterface; /** * Class MassSchedule used for adding multiple entities as Operations to Bulk Management with the status tracking @@ -30,7 +29,7 @@ class MassSchedule { /** - * @var \Magento\Framework\DataObject\IdentityGeneratorInterface + * @var IdentityGeneratorInterface */ private $identityService; @@ -45,7 +44,7 @@ class MassSchedule private $itemStatusInterfaceFactory; /** - * @var \Magento\Framework\Bulk\BulkManagementInterface + * @var BulkManagementInterface */ private $bulkManagement; @@ -55,12 +54,12 @@ class MassSchedule private $logger; /** - * @var OperationRepository + * @var OperationRepositoryInterface */ private $operationRepository; /** - * @var \Magento\Authorization\Model\UserContextInterface + * @var UserContextInterface */ private $userContext; @@ -69,6 +68,11 @@ class MassSchedule */ private $encryptor; + /** + * @var SaveMultipleOperationsInterface + */ + private $saveMultipleOperations; + /** * Initialize dependencies. * @@ -77,9 +81,10 @@ class MassSchedule * @param AsyncResponseInterfaceFactory $asyncResponseFactory * @param BulkManagementInterface $bulkManagement * @param LoggerInterface $logger - * @param OperationRepository $operationRepository + * @param OperationRepositoryInterface $operationRepository * @param UserContextInterface $userContext - * @param Encryptor|null $encryptor + * @param Encryptor $encryptor + * @param SaveMultipleOperationsInterface $saveMultipleOperations */ public function __construct( IdentityGeneratorInterface $identityService, @@ -87,9 +92,10 @@ public function __construct( AsyncResponseInterfaceFactory $asyncResponseFactory, BulkManagementInterface $bulkManagement, LoggerInterface $logger, - OperationRepository $operationRepository, - UserContextInterface $userContext = null, - Encryptor $encryptor = null + OperationRepositoryInterface $operationRepository, + UserContextInterface $userContext, + Encryptor $encryptor, + SaveMultipleOperationsInterface $saveMultipleOperations ) { $this->identityService = $identityService; $this->itemStatusInterfaceFactory = $itemStatusInterfaceFactory; @@ -97,8 +103,9 @@ public function __construct( $this->bulkManagement = $bulkManagement; $this->logger = $logger; $this->operationRepository = $operationRepository; - $this->userContext = $userContext ?: ObjectManager::getInstance()->get(UserContextInterface::class); - $this->encryptor = $encryptor ?: ObjectManager::getInstance()->get(Encryptor::class); + $this->userContext = $userContext; + $this->encryptor = $encryptor; + $this->saveMultipleOperations = $saveMultipleOperations; } /** @@ -139,7 +146,7 @@ public function publishMass($topicName, array $entitiesArray, $groupId = null, $ $requestItem = $this->itemStatusInterfaceFactory->create(); try { - $operation = $this->operationRepository->createByTopic($topicName, $entityParams, $groupId); + $operation = $this->operationRepository->create($topicName, $entityParams, $groupId, $key); $operations[] = $operation; $requestItem->setId($key); $requestItem->setStatus(ItemStatusInterface::STATUS_ACCEPTED); @@ -161,6 +168,7 @@ public function publishMass($topicName, array $entitiesArray, $groupId = null, $ } } + $this->saveMultipleOperations->execute($operations); if (!$this->bulkManagement->scheduleBulk($groupId, $operations, $bulkDescription, $userId)) { throw new LocalizedException( __('Something went wrong while processing the request.') diff --git a/app/code/Magento/AsynchronousOperations/Model/OperationManagement.php b/app/code/Magento/AsynchronousOperations/Model/OperationManagement.php index f204f63ed032b..74740cba9a6d8 100644 --- a/app/code/Magento/AsynchronousOperations/Model/OperationManagement.php +++ b/app/code/Magento/AsynchronousOperations/Model/OperationManagement.php @@ -10,7 +10,7 @@ use Magento\Framework\EntityManager\EntityManager; /** - * Class OperationManagement + * Class for managing Bulk Operations */ class OperationManagement implements \Magento\Framework\Bulk\OperationManagementInterface { @@ -45,7 +45,7 @@ public function __construct( $this->operationFactory = $operationFactory; $this->logger = $logger; } - + /** * @inheritDoc */ diff --git a/app/code/Magento/AsynchronousOperations/Model/OperationProcessor.php b/app/code/Magento/AsynchronousOperations/Model/OperationProcessor.php index 6826c34fd35f0..453f786bdf47b 100644 --- a/app/code/Magento/AsynchronousOperations/Model/OperationProcessor.php +++ b/app/code/Magento/AsynchronousOperations/Model/OperationProcessor.php @@ -8,25 +8,24 @@ namespace Magento\AsynchronousOperations\Model; -use Magento\Framework\Serialize\Serializer\Json; use Magento\AsynchronousOperations\Api\Data\OperationInterface; -use Magento\Framework\Bulk\OperationManagementInterface; use Magento\AsynchronousOperations\Model\ConfigInterface as AsyncConfig; -use Magento\Framework\MessageQueue\MessageValidator; -use Magento\Framework\MessageQueue\MessageEncoder; -use Magento\Framework\Exception\NoSuchEntityException; -use Magento\Framework\MessageQueue\ConsumerConfigurationInterface; -use Psr\Log\LoggerInterface; -use Magento\Framework\Exception\LocalizedException; -use Magento\Framework\Exception\TemporaryStateExceptionInterface; +use Magento\Framework\Bulk\OperationManagementInterface; +use Magento\Framework\Communication\ConfigInterface as CommunicationConfig; use Magento\Framework\DB\Adapter\ConnectionException; use Magento\Framework\DB\Adapter\DeadlockException; use Magento\Framework\DB\Adapter\LockWaitException; +use Magento\Framework\Exception\LocalizedException; +use Magento\Framework\Exception\NoSuchEntityException; +use Magento\Framework\MessageQueue\ConsumerConfigurationInterface; +use Magento\Framework\MessageQueue\MessageEncoder; +use Magento\Framework\MessageQueue\MessageValidator; +use Magento\Framework\Serialize\Serializer\Json; use Magento\Framework\Webapi\ServiceOutputProcessor; -use Magento\Framework\Communication\ConfigInterface as CommunicationConfig; +use Psr\Log\LoggerInterface; /** - * Class OperationProcessor + * Proccess operation * * @SuppressWarnings(PHPMD.CouplingBetweenObjects) */ @@ -80,9 +79,9 @@ class OperationProcessor * @param ConsumerConfigurationInterface $configuration * @param Json $jsonHelper * @param OperationManagementInterface $operationManagement - * @param LoggerInterface $logger * @param \Magento\Framework\Webapi\ServiceOutputProcessor $serviceOutputProcessor * @param \Magento\Framework\Communication\ConfigInterface $communicationConfig + * @param LoggerInterface $logger */ public function __construct( MessageValidator $messageValidator, @@ -137,7 +136,9 @@ public function process(string $encodedMessage) $result = $this->executeHandler($callback, $entityParams); $status = $result['status']; $errorCode = $result['error_code']; + // phpcs:disable Magento2.Performance.ForeachArrayMerge $messages = array_merge($messages, $result['messages']); + // phpcs:enable Magento2.Performance.ForeachArrayMerge $outputData = $result['output_data']; } } @@ -174,8 +175,8 @@ public function process(string $encodedMessage) /** * Execute topic handler * - * @param $callback - * @param $entityParams + * @param callable $callback + * @param array $entityParams * @return array */ private function executeHandler($callback, $entityParams) @@ -187,7 +188,9 @@ private function executeHandler($callback, $entityParams) 'output_data' => null ]; try { + // phpcs:disable Magento2.Functions.DiscouragedFunction $result['output_data'] = call_user_func_array($callback, $entityParams); + // phpcs:enable Magento2.Functions.DiscouragedFunction $result['messages'][] = sprintf('Service execution success %s::%s', get_class($callback[0]), $callback[1]); } catch (\Zend_Db_Adapter_Exception $e) { $this->logger->critical($e->getMessage()); @@ -206,9 +209,7 @@ private function executeHandler($callback, $entityParams) } } catch (NoSuchEntityException $e) { $this->logger->error($e->getMessage()); - $result['status'] = ($e instanceof TemporaryStateExceptionInterface) ? - OperationInterface::STATUS_TYPE_NOT_RETRIABLY_FAILED : - OperationInterface::STATUS_TYPE_NOT_RETRIABLY_FAILED; + $result['status'] = OperationInterface::STATUS_TYPE_NOT_RETRIABLY_FAILED; $result['error_code'] = $e->getCode(); $result['messages'][] = $e->getMessage(); } catch (LocalizedException $e) { diff --git a/app/code/Magento/AsynchronousOperations/Model/OperationRepositoryInterface.php b/app/code/Magento/AsynchronousOperations/Model/OperationRepositoryInterface.php new file mode 100644 index 0000000000000..601ab44af5023 --- /dev/null +++ b/app/code/Magento/AsynchronousOperations/Model/OperationRepositoryInterface.php @@ -0,0 +1,31 @@ +' => '', + * '' => '', + * ) + * @param string $groupId + * @param int $operationId + * @return OperationInterface + */ + public function create($topicName, $entityParams, $groupId, $operationId): OperationInterface; +} diff --git a/app/code/Magento/AsynchronousOperations/Model/ResourceModel/Operation.php b/app/code/Magento/AsynchronousOperations/Model/ResourceModel/Operation.php index 061d0917e7ab0..0eaa5315af614 100644 --- a/app/code/Magento/AsynchronousOperations/Model/ResourceModel/Operation.php +++ b/app/code/Magento/AsynchronousOperations/Model/ResourceModel/Operation.php @@ -7,10 +7,14 @@ namespace Magento\AsynchronousOperations\Model\ResourceModel; /** - * Class Operation + * Resource class for Bulk Operations */ class Operation extends \Magento\Framework\Model\ResourceModel\Db\AbstractDb { + + public const TABLE_NAME = "magento_operation"; + public const TABLE_PRIMARY_KEY = "id"; + /** * Initialize banner sales rule resource model * @@ -18,6 +22,6 @@ class Operation extends \Magento\Framework\Model\ResourceModel\Db\AbstractDb */ protected function _construct() { - $this->_init('magento_operation', 'id'); + $this->_init(self::TABLE_NAME, self::TABLE_PRIMARY_KEY); } } diff --git a/app/code/Magento/AsynchronousOperations/Model/ResourceModel/Operation/OperationRepository.php b/app/code/Magento/AsynchronousOperations/Model/ResourceModel/Operation/OperationRepository.php index 54e65cc3470dd..b189d81d31636 100644 --- a/app/code/Magento/AsynchronousOperations/Model/ResourceModel/Operation/OperationRepository.php +++ b/app/code/Magento/AsynchronousOperations/Model/ResourceModel/Operation/OperationRepository.php @@ -10,6 +10,7 @@ use Magento\AsynchronousOperations\Api\Data\OperationInterface; use Magento\AsynchronousOperations\Api\Data\OperationInterfaceFactory; +use Magento\AsynchronousOperations\Model\OperationRepositoryInterface; use Magento\Framework\MessageQueue\MessageValidator; use Magento\Framework\MessageQueue\MessageEncoder; use Magento\Framework\Serialize\Serializer\Json; @@ -18,10 +19,10 @@ /** * Create operation for list of bulk operations. */ -class OperationRepository +class OperationRepository implements OperationRepositoryInterface { /** - * @var \Magento\AsynchronousOperations\Api\Data\OperationInterfaceFactory + * @var OperationInterfaceFactory */ private $operationFactory; @@ -67,10 +68,14 @@ public function __construct( } /** - * @param $topicName - * @param $entityParams - * @param $groupId - * @return mixed + * Create operation by topic, parameters and group ID + * + * @param string $topicName + * @param array $entityParams + * @param string $groupId + * @return OperationInterface + * @deprecated No longer used. + * @see create() */ public function createByTopic($topicName, $entityParams, $groupId) { @@ -91,8 +96,16 @@ public function createByTopic($topicName, $entityParams, $groupId) ], ]; - /** @var \Magento\AsynchronousOperations\Api\Data\OperationInterface $operation */ + /** @var OperationInterface $operation */ $operation = $this->operationFactory->create($data); - return $this->entityManager->save($operation); + return $operation; + } + + /** + * @inheritDoc + */ + public function create($topicName, $entityParams, $groupId, $operationId): OperationInterface + { + return $this->createByTopic($topicName, $entityParams, $groupId); } } diff --git a/app/code/Magento/AsynchronousOperations/Model/SaveMultipleOperations.php b/app/code/Magento/AsynchronousOperations/Model/SaveMultipleOperations.php new file mode 100644 index 0000000000000..88f44c64117f2 --- /dev/null +++ b/app/code/Magento/AsynchronousOperations/Model/SaveMultipleOperations.php @@ -0,0 +1,56 @@ +operationResource = $operationResource; + } + + /** + * @inheritDoc + */ + public function execute(array $operations): void + { + try { + $operationsToInsert = array_map(function ($operation) { + return $operation->getData(); + }, $operations); + + $connection = $this->operationResource->getConnection(); + $connection->insertMultiple( + $this->operationResource->getTable(OperationResource::TABLE_NAME), + $operationsToInsert + ); + } catch (\Exception $exception) { + throw new CouldNotSaveException(__($exception->getMessage())); + } + } +} diff --git a/app/code/Magento/AsynchronousOperations/etc/adminhtml/system.xml b/app/code/Magento/AsynchronousOperations/etc/adminhtml/system.xml index e373a4fc78b13..77469ff8ad0a0 100644 --- a/app/code/Magento/AsynchronousOperations/etc/adminhtml/system.xml +++ b/app/code/Magento/AsynchronousOperations/etc/adminhtml/system.xml @@ -9,9 +9,9 @@
advanced - + - + validate-zero-or-greater validate-digits diff --git a/app/code/Magento/AsynchronousOperations/etc/di.xml b/app/code/Magento/AsynchronousOperations/etc/di.xml index 171a01cedf289..820bdd26e62b2 100644 --- a/app/code/Magento/AsynchronousOperations/etc/di.xml +++ b/app/code/Magento/AsynchronousOperations/etc/di.xml @@ -7,6 +7,7 @@ --> + @@ -18,6 +19,7 @@ + diff --git a/app/code/Magento/AsynchronousOperations/registration.php b/app/code/Magento/AsynchronousOperations/registration.php index d384df583fb5a..748f9d35aad5c 100644 --- a/app/code/Magento/AsynchronousOperations/registration.php +++ b/app/code/Magento/AsynchronousOperations/registration.php @@ -4,6 +4,6 @@ * See COPYING.txt for license details. */ -use \Magento\Framework\Component\ComponentRegistrar; +use Magento\Framework\Component\ComponentRegistrar; ComponentRegistrar::register(ComponentRegistrar::MODULE, 'Magento_AsynchronousOperations', __DIR__); diff --git a/app/code/Magento/Authorization/Model/Acl/Loader/Role.php b/app/code/Magento/Authorization/Model/Acl/Loader/Role.php index a5f8fbd6eefec..e4b1103491230 100644 --- a/app/code/Magento/Authorization/Model/Acl/Loader/Role.php +++ b/app/code/Magento/Authorization/Model/Acl/Loader/Role.php @@ -3,14 +3,23 @@ * Copyright © Magento, Inc. All rights reserved. * See COPYING.txt for license details. */ +declare(strict_types=1); + namespace Magento\Authorization\Model\Acl\Loader; use Magento\Authorization\Model\Acl\Role\Group as RoleGroup; +use Magento\Authorization\Model\Acl\Role\GroupFactory; use Magento\Authorization\Model\Acl\Role\User as RoleUser; -use Magento\Framework\App\ObjectManager; +use Magento\Authorization\Model\Acl\Role\UserFactory; +use Magento\Framework\Acl\Data\CacheInterface; +use Magento\Framework\Acl\LoaderInterface; +use Magento\Framework\App\ResourceConnection; use Magento\Framework\Serialize\Serializer\Json; -class Role implements \Magento\Framework\Acl\LoaderInterface +/** + * Acl Role Loader + */ +class Role implements LoaderInterface { /** * Cache key for ACL roles cache @@ -18,22 +27,22 @@ class Role implements \Magento\Framework\Acl\LoaderInterface const ACL_ROLES_CACHE_KEY = 'authorization_role_cached_data'; /** - * @var \Magento\Framework\App\ResourceConnection + * @var ResourceConnection */ protected $_resource; /** - * @var \Magento\Authorization\Model\Acl\Role\GroupFactory + * @var GroupFactory */ protected $_groupFactory; /** - * @var \Magento\Authorization\Model\Acl\Role\UserFactory + * @var UserFactory */ protected $_roleFactory; /** - * @var \Magento\Framework\Acl\Data\CacheInterface + * @var CacheInterface */ private $aclDataCache; @@ -48,28 +57,26 @@ class Role implements \Magento\Framework\Acl\LoaderInterface private $cacheKey; /** - * @param \Magento\Authorization\Model\Acl\Role\GroupFactory $groupFactory - * @param \Magento\Authorization\Model\Acl\Role\UserFactory $roleFactory - * @param \Magento\Framework\App\ResourceConnection $resource - * @param \Magento\Framework\Acl\Data\CacheInterface $aclDataCache + * @param GroupFactory $groupFactory + * @param UserFactory $roleFactory + * @param ResourceConnection $resource + * @param CacheInterface $aclDataCache * @param Json $serializer * @param string $cacheKey */ public function __construct( - \Magento\Authorization\Model\Acl\Role\GroupFactory $groupFactory, - \Magento\Authorization\Model\Acl\Role\UserFactory $roleFactory, - \Magento\Framework\App\ResourceConnection $resource, - \Magento\Framework\Acl\Data\CacheInterface $aclDataCache = null, - Json $serializer = null, + GroupFactory $groupFactory, + UserFactory $roleFactory, + ResourceConnection $resource, + CacheInterface $aclDataCache, + Json $serializer, $cacheKey = self::ACL_ROLES_CACHE_KEY ) { - $this->_resource = $resource; $this->_groupFactory = $groupFactory; $this->_roleFactory = $roleFactory; - $this->aclDataCache = $aclDataCache ?: ObjectManager::getInstance()->get( - \Magento\Framework\Acl\Data\CacheInterface::class - ); - $this->serializer = $serializer ?: ObjectManager::getInstance()->get(Json::class); + $this->_resource = $resource; + $this->aclDataCache = $aclDataCache; + $this->serializer = $serializer; $this->cacheKey = $cacheKey; } diff --git a/app/code/Magento/Authorization/Model/Acl/Loader/Rule.php b/app/code/Magento/Authorization/Model/Acl/Loader/Rule.php index 57bdcdbb3d9b8..b8fd974c5da6c 100644 --- a/app/code/Magento/Authorization/Model/Acl/Loader/Rule.php +++ b/app/code/Magento/Authorization/Model/Acl/Loader/Rule.php @@ -3,12 +3,20 @@ * Copyright © Magento, Inc. All rights reserved. * See COPYING.txt for license details. */ +declare(strict_types=1); + namespace Magento\Authorization\Model\Acl\Loader; -use Magento\Framework\App\ObjectManager; +use Magento\Framework\Acl\Data\CacheInterface; +use Magento\Framework\Acl\LoaderInterface; +use Magento\Framework\Acl\RootResource; +use Magento\Framework\App\ResourceConnection; use Magento\Framework\Serialize\Serializer\Json; -class Rule implements \Magento\Framework\Acl\LoaderInterface +/** + * Acl Rule Loader + */ +class Rule implements LoaderInterface { /** * Rules array cache key @@ -16,17 +24,17 @@ class Rule implements \Magento\Framework\Acl\LoaderInterface const ACL_RULE_CACHE_KEY = 'authorization_rule_cached_data'; /** - * @var \Magento\Framework\App\ResourceConnection + * @var ResourceConnection */ protected $_resource; /** - * @var \Magento\Framework\Acl\RootResource + * @var RootResource */ private $_rootResource; /** - * @var \Magento\Framework\Acl\Data\CacheInterface + * @var CacheInterface */ private $aclDataCache; @@ -41,28 +49,26 @@ class Rule implements \Magento\Framework\Acl\LoaderInterface private $cacheKey; /** - * @param \Magento\Framework\Acl\RootResource $rootResource - * @param \Magento\Framework\App\ResourceConnection $resource - * @param array $data - * @param \Magento\Framework\Acl\Data\CacheInterface $aclDataCache + * @param RootResource $rootResource + * @param ResourceConnection $resource + * @param CacheInterface $aclDataCache * @param Json $serializer + * @param array $data * @param string $cacheKey * @SuppressWarnings(PHPMD.UnusedFormalParameter): */ public function __construct( - \Magento\Framework\Acl\RootResource $rootResource, - \Magento\Framework\App\ResourceConnection $resource, + RootResource $rootResource, + ResourceConnection $resource, + CacheInterface $aclDataCache, + Json $serializer, array $data = [], - \Magento\Framework\Acl\Data\CacheInterface $aclDataCache = null, - Json $serializer = null, $cacheKey = self::ACL_RULE_CACHE_KEY ) { - $this->_resource = $resource; $this->_rootResource = $rootResource; - $this->aclDataCache = $aclDataCache ?: ObjectManager::getInstance()->get( - \Magento\Framework\Acl\Data\CacheInterface::class - ); - $this->serializer = $serializer ?: ObjectManager::getInstance()->get(Json::class); + $this->_resource = $resource; + $this->aclDataCache = $aclDataCache; + $this->serializer = $serializer; $this->cacheKey = $cacheKey; } @@ -104,7 +110,7 @@ private function getRulesArray() return $this->serializer->unserialize($rulesCachedData); } - $ruleTable = $this->_resource->getTableName("authorization_rule"); + $ruleTable = $this->_resource->getTableName('authorization_rule'); $connection = $this->_resource->getConnection(); $select = $connection->select() ->from(['r' => $ruleTable]); diff --git a/app/code/Magento/Authorization/Model/ResourceModel/Rules.php b/app/code/Magento/Authorization/Model/ResourceModel/Rules.php index 5e4ae77731a0f..46470d885650b 100644 --- a/app/code/Magento/Authorization/Model/ResourceModel/Rules.php +++ b/app/code/Magento/Authorization/Model/ResourceModel/Rules.php @@ -6,58 +6,63 @@ namespace Magento\Authorization\Model\ResourceModel; -use Magento\Framework\App\ObjectManager; +use Magento\Backend\App\AbstractAction; +use Magento\Framework\Acl\Builder; +use Magento\Framework\Acl\Data\CacheInterface; +use Magento\Framework\Acl\RootResource; +use Magento\Framework\Exception\LocalizedException; +use Magento\Framework\Model\ResourceModel\Db\AbstractDb; +use Magento\Framework\Model\ResourceModel\Db\Context; +use Psr\Log\LoggerInterface; /** * Admin rule resource model */ -class Rules extends \Magento\Framework\Model\ResourceModel\Db\AbstractDb +class Rules extends AbstractDb { /** * Root ACL resource * - * @var \Magento\Framework\Acl\RootResource + * @var RootResource */ protected $_rootResource; /** - * @var \Magento\Framework\Acl\Builder + * @var Builder */ protected $_aclBuilder; /** - * @var \Psr\Log\LoggerInterface + * @var LoggerInterface */ protected $_logger; /** - * @var \Magento\Framework\Acl\Data\CacheInterface + * @var CacheInterface */ private $aclDataCache; /** - * @param \Magento\Framework\Model\ResourceModel\Db\Context $context - * @param \Magento\Framework\Acl\Builder $aclBuilder - * @param \Psr\Log\LoggerInterface $logger - * @param \Magento\Framework\Acl\RootResource $rootResource + * @param Context $context + * @param Builder $aclBuilder + * @param LoggerInterface $logger + * @param RootResource $rootResource + * @param CacheInterface $aclDataCache * @param string $connectionName - * @param \Magento\Framework\Acl\Data\CacheInterface $aclDataCache */ public function __construct( - \Magento\Framework\Model\ResourceModel\Db\Context $context, - \Magento\Framework\Acl\Builder $aclBuilder, - \Psr\Log\LoggerInterface $logger, - \Magento\Framework\Acl\RootResource $rootResource, - $connectionName = null, - \Magento\Framework\Acl\Data\CacheInterface $aclDataCache = null + Context $context, + Builder $aclBuilder, + LoggerInterface $logger, + RootResource $rootResource, + CacheInterface $aclDataCache, + $connectionName = null ) { $this->_aclBuilder = $aclBuilder; parent::__construct($context, $connectionName); $this->_rootResource = $rootResource; $this->_logger = $logger; - $this->aclDataCache = $aclDataCache ?: ObjectManager::getInstance()->get( - \Magento\Framework\Acl\Data\CacheInterface::class - ); + $this->aclDataCache = $aclDataCache; } /** @@ -75,7 +80,7 @@ protected function _construct() * * @param \Magento\Authorization\Model\Rules $rule * @return void - * @throws \Magento\Framework\Exception\LocalizedException + * @throws LocalizedException */ public function saveRel(\Magento\Authorization\Model\Rules $rule) { @@ -107,7 +112,7 @@ public function saveRel(\Magento\Authorization\Model\Rules $rule) $connection->insert($this->getMainTable(), $insertData); } else { /** Give basic admin permissions to any admin */ - $postedResources[] = \Magento\Backend\App\AbstractAction::ADMIN_RESOURCE; + $postedResources[] = AbstractAction::ADMIN_RESOURCE; $acl = $this->_aclBuilder->getAcl(); /** @var $resource \Magento\Framework\Acl\AclResource */ foreach ($acl->getResources() as $resourceId) { @@ -125,7 +130,7 @@ public function saveRel(\Magento\Authorization\Model\Rules $rule) $connection->commit(); $this->aclDataCache->clean(); - } catch (\Magento\Framework\Exception\LocalizedException $e) { + } catch (LocalizedException $e) { $connection->rollBack(); throw $e; } catch (\Exception $e) { diff --git a/app/code/Magento/Authorization/Test/Unit/Model/Acl/Loader/RoleTest.php b/app/code/Magento/Authorization/Test/Unit/Model/Acl/Loader/RoleTest.php index a5ae7f8e86a6e..e1841b895dd07 100644 --- a/app/code/Magento/Authorization/Test/Unit/Model/Acl/Loader/RoleTest.php +++ b/app/code/Magento/Authorization/Test/Unit/Model/Acl/Loader/RoleTest.php @@ -3,199 +3,221 @@ * Copyright © Magento, Inc. All rights reserved. * See COPYING.txt for license details. */ +declare(strict_types=1); + namespace Magento\Authorization\Test\Unit\Model\Acl\Loader; -class RoleTest extends \PHPUnit\Framework\TestCase -{ - /** - * @var \Magento\Authorization\Model\Acl\Loader\Role - */ - protected $_model; +use Magento\Authorization\Model\Acl\Loader\Role; +use Magento\Authorization\Model\Acl\Role\GroupFactory; +use Magento\Authorization\Model\Acl\Role\UserFactory; +use Magento\Framework\Acl; +use Magento\Framework\Acl\Data\CacheInterface; +use Magento\Framework\App\ResourceConnection; +use Magento\Framework\DB\Adapter\Pdo\Mysql; +use Magento\Framework\DB\Select; +use Magento\Framework\Serialize\Serializer\Json; +use Magento\Framework\TestFramework\Unit\Helper\ObjectManager; +use PHPUnit\Framework\MockObject\MockObject; +use PHPUnit\Framework\TestCase; +/** + * Test class for \Magento\Authorization\Model\Acl\Loader\Role + */ +class RoleTest extends TestCase +{ /** - * @var \PHPUnit_Framework_MockObject_MockObject + * @var Role */ - protected $_resourceMock; + private $model; /** - * @var \PHPUnit_Framework_MockObject_MockObject + * @var GroupFactory|MockObject */ - protected $_adapterMock; + private $groupFactoryMock; /** - * @var \PHPUnit_Framework_MockObject_MockObject + * @var UserFactory|MockObject */ - protected $_roleFactoryMock; + private $roleFactoryMock; /** - * @var \PHPUnit_Framework_MockObject_MockObject + * @var ResourceConnection|MockObject */ - protected $_groupFactoryMock; + private $resourceMock; /** - * @var \Magento\Framework\Acl\Data\CacheInterface|\PHPUnit_Framework_MockObject_MockObject + * @var CacheInterface|MockObject */ private $aclDataCacheMock; /** - * @var \Magento\Framework\Serialize\Serializer\Json|\PHPUnit_Framework_MockObject_MockObject + * @var Json|MockObject */ private $serializerMock; /** - * @var \Magento\Framework\DB\Select|\PHPUnit_Framework_MockObject_MockObject + * @var Select|MockObject */ private $selectMock; + /** + * @var Mysql|MockObject + */ + private $adapterMock; + + /** + * @inheritDoc + */ protected function setUp() { - $this->_resourceMock = $this->createMock(\Magento\Framework\App\ResourceConnection::class); - $this->_groupFactoryMock = $this->getMockBuilder(\Magento\Authorization\Model\Acl\Role\GroupFactory::class) + $this->groupFactoryMock = $this->getMockBuilder(GroupFactory::class) ->setMethods(['create', 'getModelInstance']) ->disableOriginalConstructor() ->getMock(); - $this->_roleFactoryMock = $this->getMockBuilder(\Magento\Authorization\Model\Acl\Role\UserFactory::class) + $this->roleFactoryMock = $this->getMockBuilder(UserFactory::class) ->setMethods(['create', 'getModelInstance']) ->disableOriginalConstructor() ->getMock(); - - $this->selectMock = $this->createMock(\Magento\Framework\DB\Select::class); - $this->selectMock->expects($this->any()) - ->method('from') - ->will($this->returnValue($this->selectMock)); - - $this->_adapterMock = $this->createMock(\Magento\Framework\DB\Adapter\Pdo\Mysql::class); - + $this->resourceMock = $this->createMock(ResourceConnection::class); + $this->aclDataCacheMock = $this->createMock(CacheInterface::class); $this->serializerMock = $this->createPartialMock( - \Magento\Framework\Serialize\Serializer\Json::class, + Json::class, ['serialize', 'unserialize'] ); - $this->serializerMock->expects($this->any()) - ->method('serialize') - ->will( - $this->returnCallback( - function ($value) { - return json_encode($value); - } - ) - ); - $this->serializerMock->expects($this->any()) - ->method('unserialize') - ->will( - $this->returnCallback( - function ($value) { - return json_decode($value, true); - } - ) + $this->serializerMock->method('serialize') + ->willReturnCallback( + static function ($value) { + return json_encode($value); + } ); - $this->aclDataCacheMock = $this->createMock(\Magento\Framework\Acl\Data\CacheInterface::class); + $this->serializerMock->method('unserialize') + ->willReturnCallback( + static function ($value) { + return json_decode($value, true); + } + ); - $this->_model = new \Magento\Authorization\Model\Acl\Loader\Role( - $this->_groupFactoryMock, - $this->_roleFactoryMock, - $this->_resourceMock, - $this->aclDataCacheMock, - $this->serializerMock + $this->selectMock = $this->createMock(Select::class); + $this->selectMock->method('from') + ->willReturn($this->selectMock); + + $this->adapterMock = $this->createMock(Mysql::class); + + $objectManager = new ObjectManager($this); + $this->model = $objectManager->getObject( + Role::class, + [ + 'groupFactory' => $this->groupFactoryMock, + 'roleFactory' => $this->roleFactoryMock, + 'resource' => $this->resourceMock, + 'aclDataCache' => $this->aclDataCacheMock, + 'serializer' => $this->serializerMock + ] ); } + /** + * Test populating acl roles with children + */ public function testPopulateAclAddsRolesAndTheirChildren() { - $this->_resourceMock->expects($this->once()) + $this->resourceMock->expects($this->once()) ->method('getTableName') ->with($this->equalTo('authorization_role')) - ->will($this->returnArgument(1)); + ->willReturnArgument(1); - $this->_adapterMock->expects($this->once()) + $this->adapterMock->expects($this->once()) ->method('select') - ->will($this->returnValue($this->selectMock)); + ->willReturn($this->selectMock); - $this->_resourceMock->expects($this->once()) + $this->resourceMock->expects($this->once()) ->method('getConnection') - ->will($this->returnValue($this->_adapterMock)); + ->willReturn($this->adapterMock); - $this->_adapterMock->expects($this->once()) + $this->adapterMock->expects($this->once()) ->method('fetchAll') - ->will( - $this->returnValue( - [ - ['role_id' => 1, 'role_type' => 'G', 'parent_id' => null], - ['role_id' => 2, 'role_type' => 'U', 'parent_id' => 1, 'user_id' => 1], - ] - ) + ->willReturn( + [ + ['role_id' => 1, 'role_type' => 'G', 'parent_id' => null], + ['role_id' => 2, 'role_type' => 'U', 'parent_id' => 1, 'user_id' => 1], + ] ); - $this->_groupFactoryMock->expects($this->once())->method('create')->with(['roleId' => '1']); - $this->_roleFactoryMock->expects($this->once())->method('create')->with(['roleId' => '2']); + $this->groupFactoryMock->expects($this->once())->method('create')->with(['roleId' => '1']); + $this->roleFactoryMock->expects($this->once())->method('create')->with(['roleId' => '2']); - $aclMock = $this->createMock(\Magento\Framework\Acl::class); + $aclMock = $this->createMock(Acl::class); $aclMock->expects($this->at(0))->method('addRole')->with($this->anything(), null); $aclMock->expects($this->at(2))->method('addRole')->with($this->anything(), '1'); - $this->_model->populateAcl($aclMock); + $this->model->populateAcl($aclMock); } + /** + * Test populating acl role with multiple parents + */ public function testPopulateAclAddsMultipleParents() { - $this->_resourceMock->expects($this->once()) + $this->resourceMock->expects($this->once()) ->method('getTableName') ->with($this->equalTo('authorization_role')) - ->will($this->returnArgument(1)); + ->willReturnArgument(1); - $this->_adapterMock->expects($this->once()) + $this->adapterMock->expects($this->once()) ->method('select') - ->will($this->returnValue($this->selectMock)); + ->willReturn($this->selectMock); - $this->_resourceMock->expects($this->once()) + $this->resourceMock->expects($this->once()) ->method('getConnection') - ->will($this->returnValue($this->_adapterMock)); + ->willReturn($this->adapterMock); - $this->_adapterMock->expects($this->once()) + $this->adapterMock->expects($this->once()) ->method('fetchAll') - ->will($this->returnValue([['role_id' => 1, 'role_type' => 'U', 'parent_id' => 2, 'user_id' => 3]])); + ->willReturn([['role_id' => 1, 'role_type' => 'U', 'parent_id' => 2, 'user_id' => 3]]); - $this->_roleFactoryMock->expects($this->never())->method('getModelInstance'); - $this->_groupFactoryMock->expects($this->never())->method('getModelInstance'); + $this->roleFactoryMock->expects($this->never())->method('getModelInstance'); + $this->groupFactoryMock->expects($this->never())->method('getModelInstance'); - $aclMock = $this->createMock(\Magento\Framework\Acl::class); - $aclMock->expects($this->at(0))->method('hasRole')->with('1')->will($this->returnValue(true)); + $aclMock = $this->createMock(Acl::class); + $aclMock->expects($this->at(0))->method('hasRole')->with('1')->willReturn(true); $aclMock->expects($this->at(1))->method('addRoleParent')->with('1', '2'); - $this->_model->populateAcl($aclMock); + $this->model->populateAcl($aclMock); } + /** + * Test populating acl role from cache + */ public function testPopulateAclFromCache() { - $this->_resourceMock->expects($this->never())->method('getConnection'); - $this->_resourceMock->expects($this->never())->method('getTableName'); - $this->_adapterMock->expects($this->never())->method('fetchAll'); + $this->resourceMock->expects($this->never())->method('getConnection'); + $this->resourceMock->expects($this->never())->method('getTableName'); + $this->adapterMock->expects($this->never())->method('fetchAll'); $this->aclDataCacheMock->expects($this->once()) ->method('load') - ->with(\Magento\Authorization\Model\Acl\Loader\Role::ACL_ROLES_CACHE_KEY) - ->will( - $this->returnValue( - json_encode( + ->with(Role::ACL_ROLES_CACHE_KEY) + ->willReturn( + json_encode( + [ [ - [ - 'role_id' => 1, - 'role_type' => 'U', - 'parent_id' => 2, - 'user_id' => 3 - ] + 'role_id' => 1, + 'role_type' => 'U', + 'parent_id' => 2, + 'user_id' => 3 ] - ) + ] ) ); - $this->_roleFactoryMock->expects($this->never())->method('getModelInstance'); - $this->_groupFactoryMock->expects($this->never())->method('getModelInstance'); + $this->roleFactoryMock->expects($this->never())->method('getModelInstance'); + $this->groupFactoryMock->expects($this->never())->method('getModelInstance'); - $aclMock = $this->createMock(\Magento\Framework\Acl::class); - $aclMock->expects($this->at(0))->method('hasRole')->with('1')->will($this->returnValue(true)); + $aclMock = $this->createMock(Acl::class); + $aclMock->expects($this->at(0))->method('hasRole')->with('1')->willReturn(true); $aclMock->expects($this->at(1))->method('addRoleParent')->with('1', '2'); - $this->_model->populateAcl($aclMock); + $this->model->populateAcl($aclMock); } } diff --git a/app/code/Magento/Authorization/Test/Unit/Model/Acl/Loader/RuleTest.php b/app/code/Magento/Authorization/Test/Unit/Model/Acl/Loader/RuleTest.php index 51801e1842eff..c7a9b80c6597c 100644 --- a/app/code/Magento/Authorization/Test/Unit/Model/Acl/Loader/RuleTest.php +++ b/app/code/Magento/Authorization/Test/Unit/Model/Acl/Loader/RuleTest.php @@ -3,105 +3,121 @@ * Copyright © Magento, Inc. All rights reserved. * See COPYING.txt for license details. */ +declare(strict_types=1); + namespace Magento\Authorization\Test\Unit\Model\Acl\Loader; -class RuleTest extends \PHPUnit\Framework\TestCase +use Magento\Authorization\Model\Acl\Loader\Rule; +use Magento\Framework\Acl; +use Magento\Framework\Acl\Data\CacheInterface; +use Magento\Framework\Acl\RootResource; +use Magento\Framework\App\ResourceConnection; +use Magento\Framework\Serialize\Serializer\Json; +use Magento\Framework\TestFramework\Unit\Helper\ObjectManager; +use PHPUnit\Framework\MockObject\MockObject; +use PHPUnit\Framework\TestCase; + +/** + * Test class for \Magento\Authorization\Model\Acl\Loader\Rule + */ +class RuleTest extends TestCase { /** - * @var \Magento\Authorization\Model\Acl\Loader\Rule + * @var Rule */ - protected $_model; + private $model; /** - * @var \PHPUnit_Framework_MockObject_MockObject + * @var RootResource */ - protected $_resourceMock; + private $rootResource; /** - * @var \Magento\Framework\Acl\RootResource + * @var ResourceConnection|MockObject */ - protected $_rootResourceMock; + private $resourceMock; /** - * @var \Magento\Framework\Acl\Data\CacheInterface|\PHPUnit_Framework_MockObject_MockObject + * @var CacheInterface|MockObject */ private $aclDataCacheMock; /** - * @var \Magento\Framework\Serialize\Serializer\Json|\PHPUnit_Framework_MockObject_MockObject + * @var Json|MockObject */ private $serializerMock; + /** + * @inheritDoc + */ protected function setUp() { - $this->_resourceMock = $this->createPartialMock( - \Magento\Framework\App\ResourceConnection::class, + $this->rootResource = new RootResource('Magento_Backend::all'); + $this->resourceMock = $this->createPartialMock( + ResourceConnection::class, ['getTable', 'getConnection'] ); + $this->aclDataCacheMock = $this->createMock(CacheInterface::class); $this->serializerMock = $this->createPartialMock( - \Magento\Framework\Serialize\Serializer\Json::class, + Json::class, ['serialize', 'unserialize'] ); - $this->serializerMock->expects($this->any()) - ->method('serialize') - ->will( - $this->returnCallback( - function ($value) { - return json_encode($value); - } - ) - ); - $this->serializerMock->expects($this->any()) - ->method('unserialize') - ->will( - $this->returnCallback( - function ($value) { - return json_decode($value, true); - } - ) + $this->serializerMock->method('serialize') + ->willReturnCallback( + static function ($value) { + return json_encode($value); + } ); - $this->aclDataCacheMock = $this->createMock(\Magento\Framework\Acl\Data\CacheInterface::class); + $this->serializerMock->method('unserialize') + ->willReturnCallback( + static function ($value) { + return json_decode($value, true); + } + ); - $this->_rootResourceMock = new \Magento\Framework\Acl\RootResource('Magento_Backend::all'); - $this->_model = new \Magento\Authorization\Model\Acl\Loader\Rule( - $this->_rootResourceMock, - $this->_resourceMock, - [], - $this->aclDataCacheMock, - $this->serializerMock + $objectManager = new ObjectManager($this); + $this->model = $objectManager->getObject( + Rule::class, + [ + 'rootResource' => $this->rootResource, + 'resource' => $this->resourceMock, + 'aclDataCache' => $this->aclDataCacheMock, + 'serializer' => $this->serializerMock + ] ); } + /** + * Test populating acl rule from cache + */ public function testPopulateAclFromCache() { - $this->_resourceMock->expects($this->never())->method('getTable'); - $this->_resourceMock->expects($this->never()) + $this->resourceMock->expects($this->never())->method('getTable'); + $this->resourceMock->expects($this->never()) ->method('getConnection'); $this->aclDataCacheMock->expects($this->once()) ->method('load') - ->with(\Magento\Authorization\Model\Acl\Loader\Rule::ACL_RULE_CACHE_KEY) - ->will( - $this->returnValue( - json_encode( - [ - ['role_id' => 1, 'resource_id' => 'Magento_Backend::all', 'permission' => 'allow'], - ['role_id' => 2, 'resource_id' => 1, 'permission' => 'allow'], - ['role_id' => 3, 'resource_id' => 1, 'permission' => 'deny'], - ] - ) + ->with(Rule::ACL_RULE_CACHE_KEY) + ->willReturn( + json_encode( + [ + ['role_id' => 1, 'resource_id' => 'Magento_Backend::all', 'permission' => 'allow'], + ['role_id' => 2, 'resource_id' => 1, 'permission' => 'allow'], + ['role_id' => 3, 'resource_id' => 1, 'permission' => 'deny'], + ] ) ); - $aclMock = $this->createMock(\Magento\Framework\Acl::class); - $aclMock->expects($this->any())->method('has')->will($this->returnValue(true)); + $aclMock = $this->createMock(Acl::class); + $aclMock->method('has')->willReturn(true); $aclMock->expects($this->at(1))->method('allow')->with('1', null, null); $aclMock->expects($this->at(2))->method('allow')->with('1', 'Magento_Backend::all', null); $aclMock->expects($this->at(4))->method('allow')->with('2', 1, null); $aclMock->expects($this->at(6))->method('deny')->with('3', 1, null); - $this->_model->populateAcl($aclMock); + $this->model->populateAcl($aclMock); } } diff --git a/app/code/Magento/Authorization/Test/Unit/Model/ResourceModel/RulesTest.php b/app/code/Magento/Authorization/Test/Unit/Model/ResourceModel/RulesTest.php index 260691608537e..36cd789042f43 100644 --- a/app/code/Magento/Authorization/Test/Unit/Model/ResourceModel/RulesTest.php +++ b/app/code/Magento/Authorization/Test/Unit/Model/ResourceModel/RulesTest.php @@ -3,9 +3,23 @@ * Copyright © Magento, Inc. All rights reserved. * See COPYING.txt for license details. */ +declare(strict_types=1); namespace Magento\Authorization\Test\Unit\Model\ResourceModel; +use Magento\Authorization\Model\ResourceModel\Rules; +use Magento\Framework\Acl\Builder; +use Magento\Framework\Acl\Data\CacheInterface; +use Magento\Framework\Acl\RootResource; +use Magento\Framework\App\ResourceConnection; +use Magento\Framework\DB\Adapter\AdapterInterface; +use Magento\Framework\Model\ResourceModel\Db\Context; +use Magento\Framework\Phrase; +use Magento\Framework\TestFramework\Unit\Helper\ObjectManager; +use PHPUnit\Framework\MockObject\MockObject; +use PHPUnit\Framework\TestCase; +use Psr\Log\LoggerInterface; + /** * Unit test for Rules resource model. * @@ -14,7 +28,7 @@ * * @SuppressWarnings(PHPMD.CouplingBetweenObjects) */ -class RulesTest extends \PHPUnit\Framework\TestCase +class RulesTest extends TestCase { /** * Test constants @@ -22,121 +36,125 @@ class RulesTest extends \PHPUnit\Framework\TestCase const TEST_ROLE_ID = 13; /** - * @var \Magento\Authorization\Model\ResourceModel\Rules + * @var Rules */ private $model; /** - * @var \Magento\Framework\Model\ResourceModel\Db\Context|\PHPUnit_Framework_MockObject_MockObject + * @var Context|MockObject */ private $contextMock; /** - * @var \Magento\Framework\Acl\Builder|\PHPUnit_Framework_MockObject_MockObject + * @var Builder|MockObject */ private $aclBuilderMock; /** - * @var \Psr\Log\LoggerInterface|\PHPUnit_Framework_MockObject_MockObject + * @var LoggerInterface|MockObject */ private $loggerMock; /** - * @var \Magento\Framework\Acl\RootResource|\PHPUnit_Framework_MockObject_MockObject + * @var RootResource|MockObject */ private $rootResourceMock; /** - * @var \Magento\Framework\Acl\Data\CacheInterface|\PHPUnit_Framework_MockObject_MockObject + * @var CacheInterface|MockObject */ private $aclDataCacheMock; /** - * @var \Magento\Framework\App\ResourceConnection|\PHPUnit_Framework_MockObject_MockObject + * @var ResourceConnection|MockObject */ private $resourceConnectionMock; /** - * @var \Magento\Framework\DB\Adapter\AdapterInterface|\PHPUnit_Framework_MockObject_MockObject + * @var AdapterInterface|MockObject */ private $connectionMock; /** - * @var \Magento\Authorization\Model\Rules|\PHPUnit_Framework_MockObject_MockObject + * @var \Magento\Authorization\Model\Rules|MockObject */ private $ruleMock; + /** + * @inheritDoc + */ protected function setUp() { - $this->contextMock = $this->getMockBuilder(\Magento\Framework\Model\ResourceModel\Db\Context::class) + $this->contextMock = $this->getMockBuilder(Context::class) ->disableOriginalConstructor() ->setMethods(['getResources']) ->getMock(); - $this->resourceConnectionMock = $this->getMockBuilder(\Magento\Framework\App\ResourceConnection::class) + $this->resourceConnectionMock = $this->getMockBuilder(ResourceConnection::class) ->disableOriginalConstructor() ->setMethods(['getConnection', 'getTableName']) ->getMock(); $this->contextMock->expects($this->once()) ->method('getResources') - ->will($this->returnValue($this->resourceConnectionMock)); + ->willReturn($this->resourceConnectionMock); - $this->connectionMock = $this->getMockBuilder(\Magento\Framework\DB\Adapter\AdapterInterface::class) + $this->connectionMock = $this->getMockBuilder(AdapterInterface::class) ->disableOriginalConstructor() ->setMethods([]) ->getMock(); $this->resourceConnectionMock->expects($this->once()) ->method('getConnection') - ->with('connection') - ->will($this->returnValue($this->connectionMock)); + ->with('default') + ->willReturn($this->connectionMock); - $this->resourceConnectionMock->expects($this->any()) - ->method('getTableName') - ->with('authorization_rule', 'connection') + $this->resourceConnectionMock->method('getTableName') + ->with('authorization_rule', 'default') ->will($this->returnArgument(0)); - $this->aclBuilderMock = $this->getMockBuilder(\Magento\Framework\Acl\Builder::class) + $this->aclBuilderMock = $this->getMockBuilder(Builder::class) ->disableOriginalConstructor() ->setMethods(['getConfigCache']) ->getMock(); - $this->loggerMock = $this->getMockBuilder(\Psr\Log\LoggerInterface::class) + $this->loggerMock = $this->getMockBuilder(LoggerInterface::class) ->disableOriginalConstructor() ->setMethods([]) ->getMock(); - $this->rootResourceMock = $this->getMockBuilder(\Magento\Framework\Acl\RootResource::class) + $this->rootResourceMock = $this->getMockBuilder(RootResource::class) ->disableOriginalConstructor() ->setMethods([]) ->getMock(); - $this->aclDataCacheMock = $this->getMockBuilder(\Magento\Framework\Acl\Data\CacheInterface::class) + $this->aclDataCacheMock = $this->getMockBuilder(CacheInterface::class) ->disableOriginalConstructor() ->setMethods([]) ->getMock(); - $this->aclBuilderMock->expects($this->any()) - ->method('getConfigCache') - ->will($this->returnValue($this->aclDataCacheMock)); + $this->aclBuilderMock->method('getConfigCache') + ->willReturn($this->aclDataCacheMock); $this->ruleMock = $this->getMockBuilder(\Magento\Authorization\Model\Rules::class) ->disableOriginalConstructor() ->setMethods(['getRoleId']) ->getMock(); - $this->ruleMock->expects($this->any()) - ->method('getRoleId') - ->will($this->returnValue(self::TEST_ROLE_ID)); - - $this->model = new \Magento\Authorization\Model\ResourceModel\Rules( - $this->contextMock, - $this->aclBuilderMock, - $this->loggerMock, - $this->rootResourceMock, - 'connection', - $this->aclDataCacheMock + $this->ruleMock->method('getRoleId') + ->willReturn(self::TEST_ROLE_ID); + + $objectManager = new ObjectManager($this); + $this->model = $objectManager->getObject( + Rules::class, + [ + 'context' => $this->contextMock, + 'aclBuilder' => $this->aclBuilderMock, + 'logger' => $this->loggerMock, + 'rootResource' => $this->rootResourceMock, + 'aclDataCache' => $this->aclDataCacheMock, + 'default' + ] ); } @@ -167,12 +185,12 @@ public function testSaveRelNoResources() */ public function testLocalizedExceptionOccurance() { - $exceptionPhrase = $this->getMockBuilder(\Magento\Framework\Phrase::class) + $exceptionPhrase = $this->getMockBuilder(Phrase::class) ->disableOriginalConstructor() ->setMethods(['render']) ->getMock(); - $exceptionPhrase->expects($this->any())->method('render')->will($this->returnValue('TestException')); + $exceptionPhrase->method('render')->willReturn('TestException'); $exception = new \Magento\Framework\Exception\LocalizedException($exceptionPhrase); diff --git a/app/code/Magento/Authorization/registration.php b/app/code/Magento/Authorization/registration.php index 0007aeba9a1ba..a4520bdcdb26b 100644 --- a/app/code/Magento/Authorization/registration.php +++ b/app/code/Magento/Authorization/registration.php @@ -4,6 +4,6 @@ * See COPYING.txt for license details. */ -use \Magento\Framework\Component\ComponentRegistrar; +use Magento\Framework\Component\ComponentRegistrar; ComponentRegistrar::register(ComponentRegistrar::MODULE, 'Magento_Authorization', __DIR__); diff --git a/app/code/Magento/Authorizenet/Block/Adminhtml/Order/View/Info/FraudDetails.php b/app/code/Magento/Authorizenet/Block/Adminhtml/Order/View/Info/FraudDetails.php deleted file mode 100644 index c693ebe95d52b..0000000000000 --- a/app/code/Magento/Authorizenet/Block/Adminhtml/Order/View/Info/FraudDetails.php +++ /dev/null @@ -1,60 +0,0 @@ -registry = $registry; - parent::__construct($context, $data); - } - - /** - * Return payment method model - * - * @return \Magento\Sales\Model\Order\Payment - */ - public function getPayment() - { - $order = $this->registry->registry('current_order'); - return $order->getPayment(); - } - - /** - * Produce and return the block's HTML output - * - * @return string - */ - protected function _toHtml() - { - return ($this->getPayment()->getMethod() === Directpost::METHOD_CODE) ? parent::_toHtml() : ''; - } -} diff --git a/app/code/Magento/Authorizenet/Block/Adminhtml/Order/View/Info/PaymentDetails.php b/app/code/Magento/Authorizenet/Block/Adminhtml/Order/View/Info/PaymentDetails.php deleted file mode 100644 index 23034270640dd..0000000000000 --- a/app/code/Magento/Authorizenet/Block/Adminhtml/Order/View/Info/PaymentDetails.php +++ /dev/null @@ -1,29 +0,0 @@ -dataFactory = $dataFactory; - $this->messageManager = $messageManager; - parent::__construct($context, $registry, $data); - } - - /** - * Get helper data - * - * @param string $area - * @return \Magento\Authorizenet\Helper\Backend\Data|\Magento\Authorizenet\Helper\Data - */ - public function getHelper($area) - { - return $this->dataFactory->create($area); - } - - /** - * {inheritdoc} - */ - protected function _beforeToHtml() - { - $this->addSuccessMessage(); - return parent::_beforeToHtml(); - } - - /** - * Add success message - * - * @return void - */ - private function addSuccessMessage() - { - $params = $this->getParams(); - if (isset($params['redirect_parent'])) { - $this->messageManager->addSuccess(__('You created the order.')); - } - } -} diff --git a/app/code/Magento/Authorizenet/Controller/Adminhtml/Authorizenet/Directpost/Payment/AddConfigured.php b/app/code/Magento/Authorizenet/Controller/Adminhtml/Authorizenet/Directpost/Payment/AddConfigured.php deleted file mode 100644 index f71314613fc1f..0000000000000 --- a/app/code/Magento/Authorizenet/Controller/Adminhtml/Authorizenet/Directpost/Payment/AddConfigured.php +++ /dev/null @@ -1,20 +0,0 @@ -helper = $helper; - parent::__construct($context, $productHelper, $escaper, $resultPageFactory, $resultForwardFactory); - } - - /** - * Send request to authorize.net - * - * @return void - * @SuppressWarnings(PHPMD.CyclomaticComplexity) - * @SuppressWarnings(PHPMD.UnusedLocalVariable) - */ - public function execute() - { - $paymentParam = $this->getRequest()->getParam('payment'); - $controller = $this->getRequest()->getParam('controller'); - $this->getRequest()->setPostValue('collect_shipping_rates', 1); - $this->_processActionData('save'); - - //get confirmation by email flag - $orderData = $this->getRequest()->getPost('order'); - $sendConfirmationFlag = 0; - if ($orderData) { - $sendConfirmationFlag = !empty($orderData['send_confirmation']) ? 1 : 0; - } else { - $orderData = []; - } - - if (isset($paymentParam['method'])) { - $result = []; - //create order partially - $this->_getOrderCreateModel()->setPaymentData($paymentParam); - $this->_getOrderCreateModel()->getQuote()->getPayment()->addData($paymentParam); - - $orderData['send_confirmation'] = 0; - $this->getRequest()->setPostValue('order', $orderData); - - try { - //do not cancel old order. - $oldOrder = $this->_getOrderCreateModel()->getSession()->getOrder(); - $oldOrder->setActionFlag(\Magento\Sales\Model\Order::ACTION_FLAG_CANCEL, false); - - $order = $this->_getOrderCreateModel()->setIsValidate( - true - )->importPostData( - $this->getRequest()->getPost('order') - )->createOrder(); - - $payment = $order->getPayment(); - if ($payment && $payment->getMethod() == $this->_objectManager->create( - \Magento\Authorizenet\Model\Directpost::class - )->getCode() - ) { - //return json with data. - $session = $this->_objectManager->get(\Magento\Authorizenet\Model\Directpost\Session::class); - $session->addCheckoutOrderIncrementId($order->getIncrementId()); - $session->setLastOrderIncrementId($order->getIncrementId()); - - /** @var \Magento\Authorizenet\Model\Directpost $method */ - $method = $payment->getMethodInstance(); - $method->setDataHelper($this->helper); - $requestToAuthorizenet = $method->generateRequestFromOrder($order); - $requestToAuthorizenet->setControllerActionName($controller); - $requestToAuthorizenet->setOrderSendConfirmation($sendConfirmationFlag); - $requestToAuthorizenet->setStoreId($this->_getOrderCreateModel()->getQuote()->getStoreId()); - - $adminUrl = $this->_objectManager->get(\Magento\Backend\Model\UrlInterface::class); - if ($adminUrl->useSecretKey()) { - $requestToAuthorizenet->setKey( - $adminUrl->getSecretKey('adminhtml', 'authorizenet_directpost_payment', 'redirect') - ); - } - $result['directpost'] = ['fields' => $requestToAuthorizenet->getData()]; - } - - $result['success'] = 1; - $isError = false; - } catch (\Magento\Framework\Exception\LocalizedException $e) { - $message = $e->getMessage(); - if (!empty($message)) { - $this->messageManager->addErrorMessage($message); - } - $isError = true; - } catch (\Exception $e) { - $this->messageManager->addExceptionMessage($e, __('Order saving error: %1', $e->getMessage())); - $isError = true; - } - - if ($isError) { - $result['success'] = 0; - $result['error'] = 1; - $result['redirect'] = $this->_objectManager->get( - \Magento\Backend\Model\UrlInterface::class - )->getUrl( - 'sales/order_create/' - ); - } - - $this->getResponse()->representJson( - $this->_objectManager->get(\Magento\Framework\Json\Helper\Data::class)->jsonEncode($result) - ); - } else { - $result = ['error_messages' => __('Please choose a payment method.')]; - $this->getResponse()->representJson( - $this->_objectManager->get(\Magento\Framework\Json\Helper\Data::class)->jsonEncode($result) - ); - } - } -} diff --git a/app/code/Magento/Authorizenet/Controller/Adminhtml/Authorizenet/Directpost/Payment/ProcessData.php b/app/code/Magento/Authorizenet/Controller/Adminhtml/Authorizenet/Directpost/Payment/ProcessData.php deleted file mode 100644 index 3d0d572bd6265..0000000000000 --- a/app/code/Magento/Authorizenet/Controller/Adminhtml/Authorizenet/Directpost/Payment/ProcessData.php +++ /dev/null @@ -1,20 +0,0 @@ -_coreRegistry = $coreRegistry; - $this->resultLayoutFactory = $resultLayoutFactory; - $this->helper = $helper; - parent::__construct( - $context, - $productHelper, - $escaper, - $resultPageFactory, - $resultForwardFactory - ); - } - - /** - * Return quote - * - * @param bool $cancelOrder - * @param string $errorMsg - * @return void - */ - protected function _returnQuote($cancelOrder, $errorMsg) - { - $directpostSession = $this->_objectManager->get(\Magento\Authorizenet\Model\Directpost\Session::class); - $incrementId = $directpostSession->getLastOrderIncrementId(); - if ($incrementId && $directpostSession->isCheckoutOrderIncrementIdExist($incrementId)) { - /* @var $order \Magento\Sales\Model\Order */ - $order = $this->_objectManager->create(\Magento\Sales\Model\Order::class)->loadByIncrementId($incrementId); - if ($order->getId()) { - $directpostSession->removeCheckoutOrderIncrementId($order->getIncrementId()); - if ($cancelOrder && $order->getState() == \Magento\Sales\Model\Order::STATE_PENDING_PAYMENT) { - $order->registerCancellation($errorMsg)->save(); - } - } - } - } - - /** - * Retrieve params and put javascript into iframe - * - * @return \Magento\Framework\View\Result\Layout - */ - public function execute() - { - $redirectParams = $this->getRequest()->getParams(); - $params = []; - if (!empty($redirectParams['success']) - && isset($redirectParams['x_invoice_num']) - && isset($redirectParams['controller_action_name']) - ) { - $params['redirect_parent'] = $this->helper->getSuccessOrderUrl($redirectParams); - $directpostSession = $this->_objectManager->get(\Magento\Authorizenet\Model\Directpost\Session::class); - $directpostSession->unsetData('quote_id'); - //cancel old order - $oldOrder = $this->_getOrderCreateModel()->getSession()->getOrder(); - if ($oldOrder->getId()) { - /* @var $order \Magento\Sales\Model\Order */ - $order = $this->_objectManager->create(\Magento\Sales\Model\Order::class) - ->loadByIncrementId($redirectParams['x_invoice_num']); - - if ($order->getId()) { - $oldOrder->cancel()->save(); - $order->save(); - $this->_getOrderCreateModel()->getSession()->unsOrderId(); - } - } - //clear sessions - $this->_getSession()->clearStorage(); - $directpostSession->removeCheckoutOrderIncrementId($redirectParams['x_invoice_num']); - $this->_objectManager->get(\Magento\Backend\Model\Session::class)->clearStorage(); - $this->messageManager->addSuccess(__('You created the order.')); - } - - if (!empty($redirectParams['error_msg'])) { - $cancelOrder = empty($redirectParams['x_invoice_num']); - $this->_returnQuote($cancelOrder, $redirectParams['error_msg']); - } - - $this->_coreRegistry->register(Iframe::REGISTRY_KEY, array_merge($params, $redirectParams)); - return $this->resultLayoutFactory->create(); - } -} diff --git a/app/code/Magento/Authorizenet/Controller/Adminhtml/Authorizenet/Directpost/Payment/Reorder.php b/app/code/Magento/Authorizenet/Controller/Adminhtml/Authorizenet/Directpost/Payment/Reorder.php deleted file mode 100644 index 06a6403915ff1..0000000000000 --- a/app/code/Magento/Authorizenet/Controller/Adminhtml/Authorizenet/Directpost/Payment/Reorder.php +++ /dev/null @@ -1,20 +0,0 @@ -_objectManager->get(\Magento\Authorizenet\Model\Directpost\Session::class); - $incrementId = $directpostSession->getLastOrderIncrementId(); - if ($incrementId && $directpostSession->isCheckoutOrderIncrementIdExist($incrementId)) { - /* @var $order \Magento\Sales\Model\Order */ - $order = $this->_objectManager->create(\Magento\Sales\Model\Order::class)->loadByIncrementId($incrementId); - if ($order->getId()) { - $directpostSession->removeCheckoutOrderIncrementId($order->getIncrementId()); - } - } - } - - /** - * Return order quote by ajax - * - * @return void - */ - public function execute() - { - $this->_returnQuote(); - $this->getResponse()->representJson( - $this->_objectManager->get(\Magento\Framework\Json\Helper\Data::class)->jsonEncode(['success' => 1]) - ); - } -} diff --git a/app/code/Magento/Authorizenet/Controller/Adminhtml/Authorizenet/Directpost/Payment/Save.php b/app/code/Magento/Authorizenet/Controller/Adminhtml/Authorizenet/Directpost/Payment/Save.php deleted file mode 100644 index cc93ce5daedeb..0000000000000 --- a/app/code/Magento/Authorizenet/Controller/Adminhtml/Authorizenet/Directpost/Payment/Save.php +++ /dev/null @@ -1,17 +0,0 @@ -_coreRegistry = $coreRegistry; - $this->dataFactory = $dataFactory; - parent::__construct($context); - } - - /** - * Get checkout model - * - * @return \Magento\Checkout\Model\Session - */ - protected function _getCheckout() - { - return $this->_objectManager->get(\Magento\Checkout\Model\Session::class); - } - - /** - * Get session model - * - * @return \Magento\Authorizenet\Model\Directpost\Session - */ - protected function _getDirectPostSession() - { - return $this->_objectManager->get(\Magento\Authorizenet\Model\Directpost\Session::class); - } - - /** - * Response action. - * - * Action for Authorize.net SIM Relay Request. - * - * @param string $area - * @return void - */ - protected function _responseAction($area = 'frontend') - { - $helper = $this->dataFactory->create($area); - - $params = []; - $data = $this->getRequest()->getParams(); - - /* @var $paymentMethod \Magento\Authorizenet\Model\DirectPost */ - $paymentMethod = $this->_objectManager->create(\Magento\Authorizenet\Model\Directpost::class); - - $result = []; - if (!empty($data['x_invoice_num'])) { - $result['x_invoice_num'] = $data['x_invoice_num']; - $params['order_success'] = $helper->getSuccessOrderUrl($result); - } - - try { - if (!empty($data['store_id'])) { - $paymentMethod->setStore($data['store_id']); - } - $paymentMethod->process($data); - $result['success'] = 1; - } catch (\Magento\Framework\Exception\LocalizedException $e) { - $this->_objectManager->get(\Psr\Log\LoggerInterface::class)->critical($e); - $result['success'] = 0; - $result['error_msg'] = $e->getMessage(); - } catch (\Exception $e) { - $this->_objectManager->get(\Psr\Log\LoggerInterface::class)->critical($e); - $result['success'] = 0; - $result['error_msg'] = __('We can\'t process your order right now. Please try again later.'); - } - - if (!empty($data['controller_action_name']) - && strpos($data['controller_action_name'], 'sales_order_') === false - ) { - if (!empty($data['key'])) { - $result['key'] = $data['key']; - } - $result['controller_action_name'] = $data['controller_action_name']; - $result['is_secure'] = isset($data['is_secure']) ? $data['is_secure'] : false; - $params['redirect'] = $helper->getRedirectIframeUrl($result); - } - - //registering parameter for iframe content - $this->_coreRegistry->register(Iframe::REGISTRY_KEY, $params); - } - - /** - * Return customer quote - * - * @param bool $cancelOrder - * @param string $errorMsg - * @return void - */ - protected function _returnCustomerQuote($cancelOrder = false, $errorMsg = '') - { - $incrementId = $this->_getDirectPostSession()->getLastOrderIncrementId(); - if ($incrementId && $this->_getDirectPostSession()->isCheckoutOrderIncrementIdExist($incrementId)) { - /* @var $order \Magento\Sales\Model\Order */ - $order = $this->_objectManager->create(\Magento\Sales\Model\Order::class)->loadByIncrementId($incrementId); - if ($order->getId()) { - try { - /** @var \Magento\Quote\Api\CartRepositoryInterface $quoteRepository */ - $quoteRepository = $this->_objectManager->create(\Magento\Quote\Api\CartRepositoryInterface::class); - /** @var \Magento\Quote\Model\Quote $quote */ - $quote = $quoteRepository->get($order->getQuoteId()); - - $quote->setIsActive(1)->setReservedOrderId(null); - $quoteRepository->save($quote); - $this->_getCheckout()->replaceQuote($quote); - } catch (\Magento\Framework\Exception\NoSuchEntityException $e) { - } - $this->_getDirectPostSession()->removeCheckoutOrderIncrementId($incrementId); - $this->_getDirectPostSession()->unsetData('quote_id'); - if ($cancelOrder) { - $order->registerCancellation($errorMsg)->save(); - } - } - } - } -} diff --git a/app/code/Magento/Authorizenet/Controller/Directpost/Payment/BackendResponse.php b/app/code/Magento/Authorizenet/Controller/Directpost/Payment/BackendResponse.php deleted file mode 100644 index e0610a92feb6a..0000000000000 --- a/app/code/Magento/Authorizenet/Controller/Directpost/Payment/BackendResponse.php +++ /dev/null @@ -1,107 +0,0 @@ -directpostFactory = $directpostFactory ?: $this->_objectManager->create(DirectpostFactory::class); - $this->logger = $logger ?: $this->_objectManager->get(LoggerInterface::class); - } - - /** - * @inheritDoc - */ - public function createCsrfValidationException( - RequestInterface $request - ): ?InvalidRequestException { - return null; - } - - /** - * @inheritDoc - */ - public function validateForCsrf(RequestInterface $request): ?bool - { - return true; - } - - /** - * Response action. - * - * Action for Authorize.net SIM Relay Request. - * - * @return \Magento\Framework\Controller\ResultInterface - */ - public function execute() - { - $data = $this->getRequest()->getParams(); - /** @var Directpost $paymentMethod */ - $paymentMethod = $this->directpostFactory->create(); - if (!empty($data['store_id'])) { - $paymentMethod->setStore($data['store_id']); - } - $paymentMethod->setResponseData($data); - try { - $paymentMethod->validateResponse(); - } catch (LocalizedException $e) { - $this->logger->critical($e->getMessage()); - return $this->_redirect('noroute'); - } - $this->_responseAction('adminhtml'); - return $this->resultFactory->create(ResultFactory::TYPE_PAGE); - } -} diff --git a/app/code/Magento/Authorizenet/Controller/Directpost/Payment/Place.php b/app/code/Magento/Authorizenet/Controller/Directpost/Payment/Place.php deleted file mode 100644 index 7d672a75f5b17..0000000000000 --- a/app/code/Magento/Authorizenet/Controller/Directpost/Payment/Place.php +++ /dev/null @@ -1,161 +0,0 @@ -eventManager = $context->getEventManager(); - $this->cartManagement = $cartManagement; - $this->onepageCheckout = $onepageCheckout; - $this->jsonHelper = $jsonHelper; - $this->logger = $logger ?: ObjectManager::getInstance()->get(LoggerInterface::class); - parent::__construct($context, $coreRegistry, $dataFactory); - } - - /** - * Send request to authorize.net - * - * @return string - */ - public function execute() - { - $paymentParam = $this->getRequest()->getParam('payment'); - $controller = $this->getRequest()->getParam('controller'); - $response = $this->getResponse(); - - if (isset($paymentParam['method'])) { - $this->_getDirectPostSession()->setQuoteId($this->_getCheckout()->getQuote()->getId()); - /** - * Current workaround depends on Onepage checkout model defect - * Method Onepage::getCheckoutMethod performs setCheckoutMethod - */ - $this->onepageCheckout->getCheckoutMethod(); - - if ($controller == IframeConfigProvider::CHECKOUT_IDENTIFIER) { - return $this->placeCheckoutOrder(); - } - - $params = $this->dataFactory - ->create(DataFactory::AREA_FRONTEND) - ->getSaveOrderUrlParams($controller); - $this->_forward( - $params['action'], - $params['controller'], - $params['module'], - $this->getRequest()->getParams() - ); - } else { - $result = ['error_messages' => __('Please choose a payment method.'), 'goto_section' => 'payment']; - if ($response instanceof Http) { - $response->representJson($this->jsonHelper->jsonEncode($result)); - } - } - } - - /** - * Place order for checkout flow - * - * @return void - */ - protected function placeCheckoutOrder() - { - $result = new DataObject(); - $response = $this->getResponse(); - try { - $this->cartManagement->placeOrder($this->_getCheckout()->getQuote()->getId()); - $result->setData('success', true); - $this->eventManager->dispatch( - 'checkout_directpost_placeOrder', - [ - 'result' => $result, - 'action' => $this - ] - ); - } catch (LocalizedException $exception) { - $this->logger->critical($exception); - $result->setData('error', true); - $result->setData('error_messages', $exception->getMessage()); - } catch (\Exception $exception) { - $this->logger->critical($exception); - $result->setData('error', true); - $result->setData( - 'error_messages', - __('A server error stopped your order from being placed. Please try to place your order again.') - ); - } - if ($response instanceof Http) { - $response->representJson($this->jsonHelper->jsonEncode($result)); - } - } -} diff --git a/app/code/Magento/Authorizenet/Controller/Directpost/Payment/Redirect.php b/app/code/Magento/Authorizenet/Controller/Directpost/Payment/Redirect.php deleted file mode 100644 index 8c9510243f610..0000000000000 --- a/app/code/Magento/Authorizenet/Controller/Directpost/Payment/Redirect.php +++ /dev/null @@ -1,58 +0,0 @@ -dataFactory->create('frontend'); - - $redirectParams = $this->getRequest()->getParams(); - $params = []; - if (!empty($redirectParams['success']) - && isset($redirectParams['x_invoice_num']) - && isset($redirectParams['controller_action_name']) - ) { - $this->_getDirectPostSession()->unsetData('quote_id'); - $params['redirect_parent'] = $helper->getSuccessOrderUrl([]); - } - - if (!empty($redirectParams['error_msg'])) { - $cancelOrder = empty($redirectParams['x_invoice_num']); - $this->_returnCustomerQuote($cancelOrder, $redirectParams['error_msg']); - $params['error_msg'] = $redirectParams['error_msg']; - } - - if (isset($redirectParams['controller_action_name']) - && strpos($redirectParams['controller_action_name'], 'sales_order_') !== false - ) { - unset($redirectParams['controller_action_name']); - unset($params['redirect_parent']); - } - - $this->_coreRegistry->register(Iframe::REGISTRY_KEY, $params); - $this->_view->addPageLayoutHandles(); - $this->_view->loadLayout(false)->renderLayout(); - } -} diff --git a/app/code/Magento/Authorizenet/Controller/Directpost/Payment/Response.php b/app/code/Magento/Authorizenet/Controller/Directpost/Payment/Response.php deleted file mode 100644 index 17fc3cb72e454..0000000000000 --- a/app/code/Magento/Authorizenet/Controller/Directpost/Payment/Response.php +++ /dev/null @@ -1,53 +0,0 @@ -_responseAction('frontend'); - return $this->resultFactory->create(\Magento\Framework\Controller\ResultFactory::TYPE_PAGE); - } -} diff --git a/app/code/Magento/Authorizenet/Controller/Directpost/Payment/ReturnQuote.php b/app/code/Magento/Authorizenet/Controller/Directpost/Payment/ReturnQuote.php deleted file mode 100644 index c974632f584b0..0000000000000 --- a/app/code/Magento/Authorizenet/Controller/Directpost/Payment/ReturnQuote.php +++ /dev/null @@ -1,33 +0,0 @@ -_returnCustomerQuote(); - $this->getResponse()->representJson( - $this->_objectManager->get(\Magento\Framework\Json\Helper\Data::class)->jsonEncode(['success' => 1]) - ); - } -} diff --git a/app/code/Magento/Authorizenet/Helper/Backend/Data.php b/app/code/Magento/Authorizenet/Helper/Backend/Data.php deleted file mode 100644 index d291125ccae06..0000000000000 --- a/app/code/Magento/Authorizenet/Helper/Backend/Data.php +++ /dev/null @@ -1,109 +0,0 @@ -_urlBuilder = $backendUrl; - } - - /** - * Return URL for admin area - * - * @param string $route - * @param array $params - * @return string - */ - protected function _getUrl($route, $params = []) - { - return $this->_urlBuilder->getUrl($route, $params); - } - - /** - * Retrieve place order url in admin - * - * @return string - */ - public function getPlaceOrderAdminUrl() - { - return $this->_getUrl('adminhtml/authorizenet_directpost_payment/place', []); - } - - /** - * Retrieve place order url - * - * @param array $params - * @return string - */ - public function getSuccessOrderUrl($params) - { - $param = []; - $route = 'sales/order/view'; - $order = $this->orderFactory->create()->loadByIncrementId($params['x_invoice_num']); - $param['order_id'] = $order->getId(); - return $this->_getUrl($route, $param); - } - - /** - * Retrieve redirect iframe url - * - * @param array $params - * @return string - */ - public function getRedirectIframeUrl($params) - { - return $this->_getUrl('adminhtml/authorizenet_directpost_payment/redirect', $params); - } - - /** - * Get direct post relay url - * - * @param null|int|string $storeId - * @return string - * - * @SuppressWarnings(PHPMD.UnusedFormalParameter) - */ - public function getRelayUrl($storeId = null) - { - $defaultStore = $this->storeManager->getDefaultStoreView(); - if (!$defaultStore) { - $allStores = $this->storeManager->getStores(); - if (isset($allStores[0])) { - $defaultStore = $allStores[0]; - } - } - $baseUrl = $defaultStore->getBaseUrl(\Magento\Framework\UrlInterface::URL_TYPE_LINK); - return $baseUrl . 'authorizenet/directpost_payment/backendResponse'; - } -} diff --git a/app/code/Magento/Authorizenet/Helper/Data.php b/app/code/Magento/Authorizenet/Helper/Data.php deleted file mode 100644 index e240cd692a13f..0000000000000 --- a/app/code/Magento/Authorizenet/Helper/Data.php +++ /dev/null @@ -1,344 +0,0 @@ - 'Authorized/Pending Capture', - 'capturedPendingSettlement' => 'Captured/Pending Settlement', - 'refundSettledSuccessfully' => 'Refund/Settled Successfully', - 'refundPendingSettlement' => 'Refund/Pending Settlement', - 'declined' => 'Declined', - 'expired' => 'Expired', - 'voided' => 'Voided', - 'FDSPendingReview' => 'FDS - Pending Review', - 'FDSAuthorizedPendingReview' => 'FDS - Authorized/Pending Review' - ]; - - /** - * Fraud filter actions key to value map - * - * @var array - */ - protected $fdsFilterActions = [ - 'decline ' => 'Decline', - 'hold' => 'Hold', - 'authAndHold' => 'Authorize and Hold', - 'report' => 'Report Only' - ]; - - /** - * @param \Magento\Framework\App\Helper\Context $context - * @param \Magento\Store\Model\StoreManagerInterface $storeManager - * @param \Magento\Sales\Model\OrderFactory $orderFactory - */ - public function __construct( - Context $context, - StoreManagerInterface $storeManager, - OrderFactory $orderFactory - ) { - $this->storeManager = $storeManager; - $this->orderFactory = $orderFactory; - parent::__construct($context); - } - - /** - * Set secure url checkout is secure for current store. - * - * @param string $route - * @param array $params - * @return string - */ - protected function _getUrl($route, $params = []) - { - $params['_type'] = \Magento\Framework\UrlInterface::URL_TYPE_LINK; - if (isset($params['is_secure'])) { - $params['_secure'] = (bool)$params['is_secure']; - } elseif ($this->storeManager->getStore()->isCurrentlySecure()) { - $params['_secure'] = true; - } - return parent::_getUrl($route, $params); - } - - /** - * Retrieve save order url params - * - * @param string $controller - * @return array - */ - public function getSaveOrderUrlParams($controller) - { - $route = []; - switch ($controller) { - case 'onepage': - $route['action'] = 'saveOrder'; - $route['controller'] = 'onepage'; - $route['module'] = 'checkout'; - break; - - case 'sales_order_create': - case 'sales_order_edit': - $route['action'] = 'save'; - $route['controller'] = 'sales_order_create'; - $route['module'] = 'admin'; - break; - - default: - break; - } - - return $route; - } - - /** - * Retrieve redirect iframe url - * - * @param array $params - * @return string - */ - public function getRedirectIframeUrl($params) - { - return $this->_getUrl('authorizenet/directpost_payment/redirect', $params); - } - - /** - * Retrieve place order url - * - * @param array $params - * @return string - */ - public function getSuccessOrderUrl($params) - { - return $this->_getUrl('checkout/onepage/success', $params); - } - - /** - * Update all child and parent order's edit increment numbers. - * - * Needed for Admin area. - * - * @param \Magento\Sales\Model\Order $order - * @return void - */ - public function updateOrderEditIncrements(\Magento\Sales\Model\Order $order) - { - if ($order->getId() && $order->getOriginalIncrementId()) { - $collection = $order->getCollection(); - $quotedIncrId = $collection->getConnection()->quote($order->getOriginalIncrementId()); - $collection->getSelect()->where( - "original_increment_id = {$quotedIncrId} OR increment_id = {$quotedIncrId}" - ); - - foreach ($collection as $orderToUpdate) { - $orderToUpdate->setEditIncrement($order->getEditIncrement()); - $orderToUpdate->save(); - } - } - } - - /** - * Converts a lot of messages to message - * - * @param array $messages - * @return string - */ - public function convertMessagesToMessage($messages) - { - return implode(' | ', $messages); - } - - /** - * Return message for gateway transaction request - * - * @param \Magento\Payment\Model\InfoInterface $payment - * @param string $requestType - * @param string $lastTransactionId - * @param \Magento\Framework\DataObject $card - * @param bool|float $amount - * @param bool|string $exception - * @param bool|string $additionalMessage - * @return bool|string - */ - public function getTransactionMessage( - $payment, - $requestType, - $lastTransactionId, - $card, - $amount = false, - $exception = false, - $additionalMessage = false - ) { - $message[] = __('Credit Card: xxxx-%1', $card->getCcLast4()); - if ($amount) { - $message[] = __('amount %1', $this->formatPrice($payment, $amount)); - } - $operation = $this->getOperation($requestType); - if (!$operation) { - return false; - } else { - $message[] = $operation; - } - $message[] = ($exception) ? '- ' . __('failed.') : '- ' . __('successful.'); - if ($lastTransactionId !== null) { - $message[] = __('Authorize.Net Transaction ID %1.', $lastTransactionId); - } - if ($additionalMessage) { - $message[] = $additionalMessage; - } - if ($exception) { - $message[] = $exception; - } - return implode(' ', $message); - } - - /** - * Return operation name for request type - * - * @param string $requestType - * @return \Magento\Framework\Phrase|bool - */ - protected function getOperation($requestType) - { - switch ($requestType) { - case Authorizenet::REQUEST_TYPE_AUTH_ONLY: - return __('authorize'); - case Authorizenet::REQUEST_TYPE_AUTH_CAPTURE: - return __('authorize and capture'); - case Authorizenet::REQUEST_TYPE_PRIOR_AUTH_CAPTURE: - return __('capture'); - case Authorizenet::REQUEST_TYPE_CREDIT: - return __('refund'); - case Authorizenet::REQUEST_TYPE_VOID: - return __('void'); - default: - return false; - } - } - - /** - * Format price with currency sign - * - * @param \Magento\Payment\Model\InfoInterface $payment - * @param float $amount - * @return string - */ - protected function formatPrice($payment, $amount) - { - return $payment->getOrder()->getBaseCurrency()->formatTxt($amount); - } - - /** - * Get payment method step html - * - * @param \Magento\Framework\App\ViewInterface $view - * @return string - */ - public function getPaymentMethodsHtml(\Magento\Framework\App\ViewInterface $view) - { - $layout = $view->getLayout(); - $update = $layout->getUpdate(); - $update->load('checkout_onepage_paymentmethod'); - $layout->generateXml(); - $layout->generateElements(); - $output = $layout->getOutput(); - return $output; - } - - /** - * Get direct post relay url - * - * @param null|int|string $storeId - * @return string - */ - public function getRelayUrl($storeId = null) - { - $baseUrl = $this->storeManager->getStore($storeId) - ->getBaseUrl(\Magento\Framework\UrlInterface::URL_TYPE_LINK); - return $baseUrl . 'authorizenet/directpost_payment/response'; - } - - /** - * Get allowed currencies - * - * @return array - */ - public function getAllowedCurrencyCodes() - { - return $this->allowedCurrencyCodes; - } - - /** - * Get translated filter action label - * - * @param string $key - * @return \Magento\Framework\Phrase|string - */ - public function getFdsFilterActionLabel($key) - { - return isset($this->fdsFilterActions[$key]) ? __($this->fdsFilterActions[$key]) : $key; - } - - /** - * Get translated transaction status label - * - * @param string $key - * @return \Magento\Framework\Phrase|string - */ - public function getTransactionStatusLabel($key) - { - return isset($this->transactionStatuses[$key]) ? __($this->transactionStatuses[$key]) : $key; - } - - /** - * Gateway error response wrapper - * - * @param string $text - * @return \Magento\Framework\Phrase - */ - public function wrapGatewayError($text) - { - return __('Gateway error: %1', $text); - } -} diff --git a/app/code/Magento/Authorizenet/Helper/DataFactory.php b/app/code/Magento/Authorizenet/Helper/DataFactory.php deleted file mode 100644 index 71f16ab4af646..0000000000000 --- a/app/code/Magento/Authorizenet/Helper/DataFactory.php +++ /dev/null @@ -1,60 +0,0 @@ - \Magento\Authorizenet\Helper\Data::class, - self::AREA_BACKEND => \Magento\Authorizenet\Helper\Backend\Data::class - ]; - - /** - * Constructor - * - * @param ObjectManagerInterface $objectManager - */ - public function __construct(ObjectManagerInterface $objectManager) - { - $this->objectManager = $objectManager; - } - - /** - * Create data helper - * - * @param string $area - * @return \Magento\Authorizenet\Helper\Backend\Data|\Magento\Authorizenet\Helper\Data - * @throws LocalizedException - */ - public function create($area) - { - if (!isset($this->helperMap[$area])) { - throw new LocalizedException(__(sprintf('For this area <%s> no suitable helper', $area))); - } - - return $this->objectManager->get($this->helperMap[$area]); - } -} diff --git a/app/code/Magento/Authorizenet/Model/Authorizenet.php b/app/code/Magento/Authorizenet/Model/Authorizenet.php deleted file mode 100644 index 9370b649a23c7..0000000000000 --- a/app/code/Magento/Authorizenet/Model/Authorizenet.php +++ /dev/null @@ -1,449 +0,0 @@ -dataHelper = $dataHelper; - $this->requestFactory = $requestFactory; - $this->responseFactory = $responseFactory; - $this->transactionService = $transactionService; - $this->httpClientFactory = $httpClientFactory; - - parent::__construct( - $context, - $registry, - $extensionFactory, - $customAttributeFactory, - $paymentData, - $scopeConfig, - $logger, - $moduleList, - $localeDate, - $resource, - $resourceCollection, - $data - ); - } - - /** - * Check method for processing with base currency - * - * @param string $currencyCode - * @return bool - */ - public function canUseForCurrency($currencyCode) - { - if (!in_array($currencyCode, $this->getAcceptedCurrencyCodes())) { - return false; - } - return true; - } - - /** - * Return array of currency codes supplied by Payment Gateway - * - * @return array - */ - public function getAcceptedCurrencyCodes() - { - if (!$this->hasData('_accepted_currency')) { - $acceptedCurrencyCodes = $this->dataHelper->getAllowedCurrencyCodes(); - $acceptedCurrencyCodes[] = $this->getConfigData('currency'); - $this->setData('_accepted_currency', $acceptedCurrencyCodes); - } - return $this->_getData('_accepted_currency'); - } - - /** - * Cancel the payment through gateway - * - * @param \Magento\Payment\Model\InfoInterface $payment - * @return $this - */ - public function cancel(\Magento\Payment\Model\InfoInterface $payment) - { - return $this->void($payment); - } - - /** - * Fetch fraud details - * - * @param string $transactionId - * @return \Magento\Framework\DataObject - * @throws \Magento\Framework\Exception\LocalizedException - */ - public function fetchTransactionFraudDetails($transactionId) - { - $responseXmlDocument = $this->transactionService->getTransactionDetails($this, $transactionId); - $response = new \Magento\Framework\DataObject(); - - if (empty($responseXmlDocument->transaction->FDSFilters->FDSFilter)) { - return $response; - } - - $response->setFdsFilterAction( - $this->dataHelper->getFdsFilterActionLabel((string)$responseXmlDocument->transaction->FDSFilterAction) - ); - $response->setAvsResponse((string)$responseXmlDocument->transaction->AVSResponse); - $response->setCardCodeResponse((string)$responseXmlDocument->transaction->cardCodeResponse); - $response->setCavvResponse((string)$responseXmlDocument->transaction->CAVVResponse); - $response->setFraudFilters($this->getFraudFilters($responseXmlDocument->transaction->FDSFilters)); - - return $response; - } - - /** - * Get fraud filters - * - * @param \Magento\Framework\Simplexml\Element $fraudFilters - * @return array - */ - protected function getFraudFilters($fraudFilters) - { - $result = []; - - foreach ($fraudFilters->FDSFilter as $filer) { - $result[] = [ - 'name' => (string)$filer->name, - 'action' => $this->dataHelper->getFdsFilterActionLabel((string)$filer->action) - ]; - } - - return $result; - } - - /** - * Return authorize payment request - * - * @return \Magento\Authorizenet\Model\Request - */ - protected function getRequest() - { - $request = $this->requestFactory->create() - ->setXVersion(3.1) - ->setXDelimData('True') - ->setXRelayResponse('False') - ->setXTestRequest($this->getConfigData('test') ? 'TRUE' : 'FALSE') - ->setXLogin($this->getConfigData('login')) - ->setXTranKey($this->getConfigData('trans_key')); - return $request; - } - - /** - * Prepare request to gateway - * - * @param \Magento\Framework\DataObject|\Magento\Payment\Model\InfoInterface $payment - * @return \Magento\Authorizenet\Model\Request - * @link http://www.authorize.net/support/AIM_guide.pdf - * @SuppressWarnings(PHPMD.CyclomaticComplexity) - * @SuppressWarnings(PHPMD.NPathComplexity) - * @SuppressWarnings(PHPMD.ExcessiveMethodLength) - */ - protected function buildRequest(\Magento\Framework\DataObject $payment) - { - /** @var \Magento\Sales\Model\Order $order */ - $order = $payment->getOrder(); - $this->setStore($order->getStoreId()); - $request = $this->getRequest() - ->setXType($payment->getAnetTransType()) - ->setXMethod(self::REQUEST_METHOD_CC); - - if ($order && $order->getIncrementId()) { - $request->setXInvoiceNum($order->getIncrementId()); - } - - if ($payment->getAmount()) { - $request->setXAmount($payment->getAmount(), 2); - $request->setXCurrencyCode($order->getBaseCurrencyCode()); - } - - switch ($payment->getAnetTransType()) { - case self::REQUEST_TYPE_AUTH_CAPTURE: - $request->setXAllowPartialAuth($this->getConfigData('allow_partial_authorization') ? 'True' : 'False'); - break; - case self::REQUEST_TYPE_AUTH_ONLY: - $request->setXAllowPartialAuth($this->getConfigData('allow_partial_authorization') ? 'True' : 'False'); - break; - case self::REQUEST_TYPE_CREDIT: - /** - * Send last 4 digits of credit card number to authorize.net - * otherwise it will give an error - */ - $request->setXCardNum($payment->getCcLast4()); - $request->setXTransId($payment->getXTransId()); - break; - case self::REQUEST_TYPE_VOID: - $request->setXTransId($payment->getXTransId()); - break; - case self::REQUEST_TYPE_PRIOR_AUTH_CAPTURE: - $request->setXTransId($payment->getXTransId()); - break; - case self::REQUEST_TYPE_CAPTURE_ONLY: - $request->setXAuthCode($payment->getCcAuthCode()); - break; - } - - if (!empty($order)) { - $billing = $order->getBillingAddress(); - if (!empty($billing)) { - $request->setXFirstName($billing->getFirstname()) - ->setXLastName($billing->getLastname()) - ->setXCompany($billing->getCompany()) - ->setXAddress($billing->getStreetLine(1)) - ->setXCity($billing->getCity()) - ->setXState($billing->getRegion()) - ->setXZip($billing->getPostcode()) - ->setXCountry($billing->getCountryId()) - ->setXPhone($billing->getTelephone()) - ->setXFax($billing->getFax()) - ->setXCustId($order->getCustomerId()) - ->setXCustomerIp($order->getRemoteIp()) - ->setXCustomerTaxId($billing->getTaxId()) - ->setXEmail($order->getCustomerEmail()) - ->setXEmailCustomer($this->getConfigData('email_customer')) - ->setXMerchantEmail($this->getConfigData('merchant_email')); - } - - $shipping = $order->getShippingAddress(); - if (!empty($shipping)) { - $request->setXShipToFirstName($shipping->getFirstname()) - ->setXShipToLastName($shipping->getLastname()) - ->setXShipToCompany($shipping->getCompany()) - ->setXShipToAddress($shipping->getStreetLine(1)) - ->setXShipToCity($shipping->getCity()) - ->setXShipToState($shipping->getRegion()) - ->setXShipToZip($shipping->getPostcode()) - ->setXShipToCountry($shipping->getCountryId()); - } - - $request->setXPoNum($payment->getPoNumber()) - ->setXTax($order->getBaseTaxAmount()) - ->setXFreight($order->getBaseShippingAmount()); - } - - if ($payment->getCcNumber()) { - $request->setXCardNum($payment->getCcNumber()) - ->setXExpDate(sprintf('%02d-%04d', $payment->getCcExpMonth(), $payment->getCcExpYear())) - ->setXCardCode($payment->getCcCid()); - } - - return $request; - } - - /** - * Post request to gateway and return response - * - * @param \Magento\Authorizenet\Model\Request $request - * @return \Magento\Authorizenet\Model\Response - * @throws \Magento\Framework\Exception\LocalizedException - */ - protected function postRequest(\Magento\Authorizenet\Model\Request $request) - { - $result = $this->responseFactory->create(); - /** @var \Magento\Framework\HTTP\ZendClient $client */ - $client = $this->httpClientFactory->create(); - $url = $this->getConfigData('cgi_url') ?: self::CGI_URL; - $debugData = ['url' => $url, 'request' => $request->getData()]; - $client->setUri($url); - $client->setConfig(['maxredirects' => 0, 'timeout' => 30]); - - foreach ($request->getData() as $key => $value) { - $request->setData($key, str_replace(self::RESPONSE_DELIM_CHAR, '', $value)); - } - - $request->setXDelimChar(self::RESPONSE_DELIM_CHAR); - $client->setParameterPost($request->getData()); - $client->setMethod(\Zend_Http_Client::POST); - - try { - $response = $client->request(); - $responseBody = $response->getBody(); - $debugData['response'] = $responseBody; - } catch (\Exception $e) { - $result->setXResponseCode(-1) - ->setXResponseReasonCode($e->getCode()) - ->setXResponseReasonText($e->getMessage()); - - throw new \Magento\Framework\Exception\LocalizedException( - $this->dataHelper->wrapGatewayError($e->getMessage()) - ); - } finally { - $this->_debug($debugData); - } - - $r = explode(self::RESPONSE_DELIM_CHAR, $responseBody); - if ($r) { - $result->setXResponseCode((int)str_replace('"', '', $r[0])) - ->setXResponseReasonCode((int)str_replace('"', '', $r[2])) - ->setXResponseReasonText($r[3]) - ->setXAvsCode($r[5]) - ->setXTransId($r[6]) - ->setXInvoiceNum($r[7]) - ->setXAmount($r[9]) - ->setXMethod($r[10]) - ->setXType($r[11]) - ->setData('x_MD5_Hash', $r[37]) - ->setXAccountNumber($r[50]); - } else { - throw new \Magento\Framework\Exception\LocalizedException( - __('Something went wrong in the payment gateway.') - ); - } - return $result; - } - - /** - * If gateway actions are locked return true - * - * @param \Magento\Payment\Model\InfoInterface $payment - * @return bool - */ - protected function isGatewayActionsLocked($payment) - { - return $payment->getAdditionalInformation(self::GATEWAY_ACTIONS_LOCKED_STATE_KEY); - } -} diff --git a/app/code/Magento/Authorizenet/Model/Debug.php b/app/code/Magento/Authorizenet/Model/Debug.php deleted file mode 100644 index 93d508cc744e1..0000000000000 --- a/app/code/Magento/Authorizenet/Model/Debug.php +++ /dev/null @@ -1,38 +0,0 @@ -_init(\Magento\Authorizenet\Model\ResourceModel\Debug::class); - } -} diff --git a/app/code/Magento/Authorizenet/Model/Directpost.php b/app/code/Magento/Authorizenet/Model/Directpost.php deleted file mode 100644 index 946ec8ba01a0e..0000000000000 --- a/app/code/Magento/Authorizenet/Model/Directpost.php +++ /dev/null @@ -1,1070 +0,0 @@ -orderFactory = $orderFactory; - $this->storeManager = $storeManager; - $this->quoteRepository = $quoteRepository; - $this->response = $responseFactory->create(); - $this->orderSender = $orderSender; - $this->transactionRepository = $transactionRepository; - $this->_code = static::METHOD_CODE; - $this->paymentFailures = $paymentFailures ? : ObjectManager::getInstance() - ->get(\Magento\Sales\Api\PaymentFailuresInterface::class); - - parent::__construct( - $context, - $registry, - $extensionFactory, - $customAttributeFactory, - $paymentData, - $scopeConfig, - $logger, - $moduleList, - $localeDate, - $dataHelper, - $requestFactory, - $responseFactory, - $transactionService, - $httpClientFactory, - $resource, - $resourceCollection, - $data - ); - } - - /** - * Set data helper - * - * @param \Magento\Authorizenet\Helper\Data $dataHelper - * @return void - */ - public function setDataHelper(\Magento\Authorizenet\Helper\Data $dataHelper) - { - $this->dataHelper = $dataHelper; - } - - /** - * Do not validate payment form using server methods - * - * @return bool - */ - public function validate() - { - return true; - } - - /** - * Send authorize request to gateway - * - * @param \Magento\Framework\DataObject|\Magento\Payment\Model\InfoInterface $payment - * @param float $amount - * @return void - * @SuppressWarnings(PHPMD.UnusedFormalParameter) - */ - public function authorize(\Magento\Payment\Model\InfoInterface $payment, $amount) - { - $payment->setAdditionalInformation('payment_type', $this->getConfigData('payment_action')); - } - - /** - * Send capture request to gateway - * - * @param \Magento\Framework\DataObject|\Magento\Payment\Model\InfoInterface $payment - * @param float $amount - * @return $this - * @throws \Magento\Framework\Exception\LocalizedException - */ - public function capture(\Magento\Payment\Model\InfoInterface $payment, $amount) - { - if ($amount <= 0) { - throw new \Magento\Framework\Exception\LocalizedException(__('Invalid amount for capture.')); - } - - $payment->setAmount($amount); - - if ($payment->getParentTransactionId()) { - $payment->setAnetTransType(self::REQUEST_TYPE_PRIOR_AUTH_CAPTURE); - $payment->setXTransId($this->getRealParentTransactionId($payment)); - } else { - $payment->setAnetTransType(self::REQUEST_TYPE_AUTH_CAPTURE); - } - - $result = $this->getResponse(); - if (empty($result->getData())) { - $request = $this->buildRequest($payment); - $result = $this->postRequest($request); - } - - return $this->processCapture($result, $payment); - } - - /** - * Process capture request - * - * @param \Magento\Authorizenet\Model\Directpost\Response $result - * @param \Magento\Payment\Model\InfoInterface $payment - * @return $this - * @throws \Magento\Framework\Exception\LocalizedException - */ - protected function processCapture($result, $payment) - { - switch ($result->getXResponseCode()) { - case self::RESPONSE_CODE_APPROVED: - case self::RESPONSE_CODE_HELD: - if (in_array( - $result->getXResponseReasonCode(), - [ - self::RESPONSE_REASON_CODE_APPROVED, - self::RESPONSE_REASON_CODE_PENDING_REVIEW, - self::RESPONSE_REASON_CODE_PENDING_REVIEW_AUTHORIZED - ] - ) - ) { - if (!$payment->getParentTransactionId() - || $result->getXTransId() != $payment->getParentTransactionId() - ) { - $payment->setTransactionId($result->getXTransId()); - } - $payment->setIsTransactionClosed(0) - ->setTransactionAdditionalInfo( - self::REAL_TRANSACTION_ID_KEY, - $result->getXTransId() - ); - return $this; - } - throw new \Magento\Framework\Exception\LocalizedException( - $this->dataHelper->wrapGatewayError($result->getXResponseReasonText()) - ); - case self::RESPONSE_CODE_DECLINED: - case self::RESPONSE_CODE_ERROR: - throw new \Magento\Framework\Exception\LocalizedException( - $this->dataHelper->wrapGatewayError($result->getXResponseReasonText()) - ); - default: - throw new \Magento\Framework\Exception\LocalizedException(__('Payment capturing error.')); - } - } - - /** - * Void the payment through gateway - * - * @param \Magento\Framework\DataObject|\Magento\Payment\Model\InfoInterface $payment - * @return $this - * @throws \Magento\Framework\Exception\LocalizedException - */ - public function void(\Magento\Payment\Model\InfoInterface $payment) - { - if (!$payment->getParentTransactionId()) { - throw new \Magento\Framework\Exception\LocalizedException(__('Invalid transaction ID.')); - } - - $payment->setAnetTransType(self::REQUEST_TYPE_VOID); - $payment->setXTransId($this->getRealParentTransactionId($payment)); - - $request = $this->buildRequest($payment); - $result = $this->postRequest($request); - - switch ($result->getXResponseCode()) { - case self::RESPONSE_CODE_APPROVED: - if ($result->getXResponseReasonCode() == self::RESPONSE_REASON_CODE_APPROVED) { - if ($result->getXTransId() != $payment->getParentTransactionId()) { - $payment->setTransactionId($result->getXTransId()); - } - $payment->setIsTransactionClosed(1) - ->setShouldCloseParentTransaction(1) - ->setTransactionAdditionalInfo(self::REAL_TRANSACTION_ID_KEY, $result->getXTransId()); - return $this; - } - throw new \Magento\Framework\Exception\LocalizedException( - $this->dataHelper->wrapGatewayError($result->getXResponseReasonText()) - ); - case self::RESPONSE_CODE_DECLINED: - case self::RESPONSE_CODE_ERROR: - throw new \Magento\Framework\Exception\LocalizedException( - $this->dataHelper->wrapGatewayError($result->getXResponseReasonText()) - ); - default: - throw new \Magento\Framework\Exception\LocalizedException(__('Payment voiding error.')); - } - } - - /** - * Refund the amount need to decode last 4 digits for request. - * - * @param \Magento\Framework\DataObject|\Magento\Payment\Model\InfoInterface $payment - * @param float $amount - * @return $this - * @throws \Exception - */ - public function refund(\Magento\Payment\Model\InfoInterface $payment, $amount) - { - $last4 = $payment->getCcLast4(); - $payment->setCcLast4($payment->decrypt($last4)); - try { - $this->processRefund($payment, $amount); - } catch (\Exception $e) { - $payment->setCcLast4($last4); - throw $e; - } - $payment->setCcLast4($last4); - return $this; - } - - /** - * Refund the amount with transaction id - * - * @param \Magento\Framework\DataObject $payment - * @param float $amount - * @return $this - * @throws \Magento\Framework\Exception\LocalizedException - */ - protected function processRefund(\Magento\Framework\DataObject $payment, $amount) - { - if ($amount <= 0) { - throw new \Magento\Framework\Exception\LocalizedException(__('Invalid amount for refund.')); - } - - if (!$payment->getParentTransactionId()) { - throw new \Magento\Framework\Exception\LocalizedException(__('Invalid transaction ID.')); - } - - $payment->setAnetTransType(self::REQUEST_TYPE_CREDIT); - $payment->setAmount($amount); - $payment->setXTransId($this->getRealParentTransactionId($payment)); - - $request = $this->buildRequest($payment); - $result = $this->postRequest($request); - - switch ($result->getXResponseCode()) { - case self::RESPONSE_CODE_APPROVED: - if ($result->getXResponseReasonCode() == self::RESPONSE_REASON_CODE_APPROVED) { - if ($result->getXTransId() != $payment->getParentTransactionId()) { - $payment->setTransactionId($result->getXTransId()); - } - $payment->setIsTransactionClosed(true) - ->setTransactionAdditionalInfo(self::REAL_TRANSACTION_ID_KEY, $result->getXTransId()); - return $this; - } - throw new \Magento\Framework\Exception\LocalizedException( - $this->dataHelper->wrapGatewayError($result->getXResponseReasonText()) - ); - case self::RESPONSE_CODE_DECLINED: - case self::RESPONSE_CODE_ERROR: - throw new \Magento\Framework\Exception\LocalizedException( - $this->dataHelper->wrapGatewayError($result->getXResponseReasonText()) - ); - default: - throw new \Magento\Framework\Exception\LocalizedException(__('Payment refunding error.')); - } - } - - /** - * Get CGI url - * - * @return string - */ - public function getCgiUrl() - { - $uri = $this->getConfigData('cgi_url'); - return $uri ? $uri : self::CGI_URL; - } - - /** - * Return URL on which Authorize.net server will return payment result data in hidden request. - * - * @param int $storeId - * @return string - */ - public function getRelayUrl($storeId = null) - { - if ($storeId == null && $this->getStore()) { - $storeId = $this->getStore(); - } - return $this->dataHelper->getRelayUrl($storeId); - } - - /** - * Return response. - * - * @return \Magento\Authorizenet\Model\Directpost\Response - */ - public function getResponse() - { - return $this->response; - } - - /** - * Instantiate state and set it to state object - * - * @param string $paymentAction - * @param \Magento\Framework\DataObject $stateObject - * @return void - * @SuppressWarnings(PHPMD.UnusedFormalParameter) - */ - public function initialize($paymentAction, $stateObject) - { - $requestType = null; - switch ($paymentAction) { - case self::ACTION_AUTHORIZE: - $requestType = self::REQUEST_TYPE_AUTH_ONLY; - //intentional - case self::ACTION_AUTHORIZE_CAPTURE: - $requestType = $requestType ?: self::REQUEST_TYPE_AUTH_CAPTURE; - $payment = $this->getInfoInstance(); - $order = $payment->getOrder(); - $order->setCanSendNewEmailFlag(false); - $payment->setBaseAmountAuthorized($order->getBaseTotalDue()); - $payment->setAmountAuthorized($order->getTotalDue()); - $payment->setAnetTransType($requestType); - break; - default: - break; - } - } - - /** - * Generate request object and fill its fields from Quote or Order object - * - * @param \Magento\Sales\Model\Order $order Quote or order object. - * @return \Magento\Authorizenet\Model\Directpost\Request - */ - public function generateRequestFromOrder(\Magento\Sales\Model\Order $order) - { - $request = $this->requestFactory->create() - ->setConstantData($this) - ->setDataFromOrder($order, $this) - ->signRequestData(); - - $this->_debug(['request' => $request->getData()]); - - return $request; - } - - /** - * Fill response with data. - * - * @param array $postData - * @return $this - */ - public function setResponseData(array $postData) - { - $this->getResponse()->setData($postData); - return $this; - } - - /** - * Validate response data. Needed in controllers. - * - * @return bool true in case of validation success. - * @throws \Magento\Framework\Exception\LocalizedException In case of validation error - */ - public function validateResponse() - { - $response = $this->getResponse(); - $hashConfigKey = !empty($response->getData('x_SHA2_Hash')) ? 'signature_key' : 'trans_md5'; - - //hash check - if (!$response->isValidHash($this->getConfigData($hashConfigKey), $this->getConfigData('login')) - ) { - throw new \Magento\Framework\Exception\LocalizedException( - __('The transaction was declined because the response hash validation failed.') - ); - } - - return true; - } - - /** - * Operate with order using data from $_POST which came from authorize.net by Relay URL. - * - * @param array $responseData data from Authorize.net from $_POST - * @return void - * @throws \Magento\Framework\Exception\LocalizedException In case of validation error or order creation error - */ - public function process(array $responseData) - { - $this->_debug(['response' => $responseData]); - - $this->setResponseData($responseData); - - //check MD5 error or others response errors - //throws exception on false. - $this->validateResponse(); - - $response = $this->getResponse(); - $responseText = $this->dataHelper->wrapGatewayError($response->getXResponseReasonText()); - $isError = false; - if ($this->getOrderIncrementId()) { - $order = $this->getOrderFromResponse(); - //check payment method - $payment = $order->getPayment(); - if (!$payment || $payment->getMethod() != $this->getCode()) { - throw new \Magento\Framework\Exception\LocalizedException( - __('This payment didn\'t work out because we can\'t find this order.') - ); - } - if ($order->getId()) { - //operate with order - $this->processOrder($order); - } else { - $isError = true; - } - } else { - $isError = true; - } - - if ($isError) { - $responseText = $responseText && !$response->isApproved() - ? $responseText - : __('This payment didn\'t work out because we can\'t find this order.'); - throw new \Magento\Framework\Exception\LocalizedException($responseText); - } - } - - /** - * Fill payment with credit card data from response from Authorize.net. - * - * @param \Magento\Framework\DataObject $payment - * @return void - */ - protected function fillPaymentByResponse(\Magento\Framework\DataObject $payment) - { - $response = $this->getResponse(); - $payment->setTransactionId($response->getXTransId()) - ->setParentTransactionId(null) - ->setIsTransactionClosed(0) - ->setTransactionAdditionalInfo(self::REAL_TRANSACTION_ID_KEY, $response->getXTransId()); - - if ($response->getXMethod() == self::REQUEST_METHOD_CC) { - $payment->setCcAvsStatus($response->getXAvsCode()) - ->setCcLast4($payment->encrypt(substr($response->getXAccountNumber(), -4))); - } - - if ($response->getXResponseCode() == self::RESPONSE_CODE_HELD) { - $payment->setIsTransactionPending(true) - ->setIsFraudDetected(true); - } - - $additionalInformationKeys = explode(',', $this->getValue('paymentInfoKeys')); - foreach ($additionalInformationKeys as $paymentInfoKey) { - $paymentInfoValue = $response->getDataByKey($paymentInfoKey); - if ($paymentInfoValue !== null) { - $payment->setAdditionalInformation($paymentInfoKey, $paymentInfoValue); - } - } - } - - /** - * Check response code came from Authorize.net. - * - * @return true in case of Approved response - * @throws \Magento\Framework\Exception\LocalizedException In case of Declined or Error response from Authorize.net - */ - public function checkResponseCode() - { - switch ($this->getResponse()->getXResponseCode()) { - case self::RESPONSE_CODE_APPROVED: - case self::RESPONSE_CODE_HELD: - return true; - case self::RESPONSE_CODE_DECLINED: - case self::RESPONSE_CODE_ERROR: - $errorMessage = $this->dataHelper->wrapGatewayError($this->getResponse()->getXResponseReasonText()); - $order = $this->getOrderFromResponse(); - $this->paymentFailures->handle((int)$order->getQuoteId(), (string)$errorMessage); - throw new \Magento\Framework\Exception\LocalizedException($errorMessage); - default: - throw new \Magento\Framework\Exception\LocalizedException( - __('There was a payment authorization error.') - ); - } - } - - /** - * Check transaction id came from Authorize.net - * - * @return true in case of right transaction id - * @throws \Magento\Framework\Exception\LocalizedException In case of bad transaction id. - */ - public function checkTransId() - { - if (!$this->getResponse()->getXTransId()) { - throw new \Magento\Framework\Exception\LocalizedException( - __('Please enter a transaction ID to authorize this payment.') - ); - } - return true; - } - - /** - * Compare amount with amount from the response from Authorize.net. - * - * @param float $amount - * @return bool - */ - protected function matchAmount($amount) - { - return sprintf('%.2F', $amount) == sprintf('%.2F', $this->getResponse()->getXAmount()); - } - - /** - * Operate with order using information from Authorize.net. - * - * Authorize order or authorize and capture it. - * - * @param \Magento\Sales\Model\Order $order - * @return void - * @throws \Magento\Framework\Exception\LocalizedException - * @throws \Exception - * @SuppressWarnings(PHPMD.NPathComplexity) - */ - protected function processOrder(\Magento\Sales\Model\Order $order) - { - try { - $this->checkResponseCode(); - $this->checkTransId(); - } catch (\Exception $e) { - //decline the order (in case of wrong response code) but don't return money to customer. - $message = $e->getMessage(); - $this->declineOrder($order, $message, false); - - throw $e; - } - - $response = $this->getResponse(); - - //create transaction. need for void if amount will not match. - $payment = $order->getPayment(); - $this->fillPaymentByResponse($payment); - $payment->getMethodInstance()->setIsInitializeNeeded(false); - $payment->getMethodInstance()->setResponseData($response->getData()); - $this->processPaymentFraudStatus($payment); - $payment->place(); - $this->addStatusComment($payment); - $order->save(); - - //match amounts. should be equals for authorization. - //decline the order if amount does not match. - if (!$this->matchAmount($payment->getBaseAmountAuthorized())) { - $message = __( - 'Something went wrong: the paid amount doesn\'t match the order amount.' - . ' Please correct this and try again.' - ); - $this->declineOrder($order, $message, true); - throw new \Magento\Framework\Exception\LocalizedException($message); - } - - try { - if (!$response->hasOrderSendConfirmation() || $response->getOrderSendConfirmation()) { - $this->orderSender->send($order); - } - - $quote = $this->quoteRepository->get($order->getQuoteId())->setIsActive(false); - $this->quoteRepository->save($quote); - } catch (\Exception $e) { - // do not cancel order if we couldn't send email - } - } - - /** - * Process fraud status - * - * @param \Magento\Sales\Model\Order\Payment $payment - * @return $this - */ - protected function processPaymentFraudStatus(\Magento\Sales\Model\Order\Payment $payment) - { - try { - $fraudDetailsResponse = $payment->getMethodInstance() - ->fetchTransactionFraudDetails($this->getResponse()->getXTransId()); - $fraudData = $fraudDetailsResponse->getData(); - - if (empty($fraudData)) { - $payment->setIsFraudDetected(false); - return $this; - } - - $fdsFilterAction = (string)$fraudDetailsResponse->getFdsFilterAction(); - if ($this->fdsFilterActionIsReportOnly($fdsFilterAction) === false) { - $payment->setIsFraudDetected(true); - } - - $payment->setAdditionalInformation('fraud_details', $fraudData); - } catch (\Exception $e) { - //this request is optional - } - - return $this; - } - - /** - * Add status comment to history - * - * @param \Magento\Sales\Model\Order\Payment $payment - * @return $this - */ - protected function addStatusComment(\Magento\Sales\Model\Order\Payment $payment) - { - try { - $transactionId = $this->getResponse()->getXTransId(); - $data = $this->transactionService->getTransactionDetails($this, $transactionId); - $transactionStatus = (string)$data->transaction->transactionStatus; - $fdsFilterAction = (string)$data->transaction->FDSFilterAction; - - if ($payment->getIsTransactionPending()) { - $message = 'Amount of %1 is pending approval on the gateway.
' - . 'Transaction "%2" status is "%3".
' - . 'Transaction FDS Filter Action is "%4"'; - $message = __( - $message, - $payment->getOrder()->getBaseCurrency()->formatTxt($this->getResponse()->getXAmount()), - $transactionId, - $this->dataHelper->getTransactionStatusLabel($transactionStatus), - $this->dataHelper->getFdsFilterActionLabel($fdsFilterAction) - ); - $payment->getOrder()->addStatusHistoryComment($message); - } - } catch (\Exception $e) { - $this->getPsrLogger()->critical($e); - //this request is optional - } - return $this; - } - - /** - * Register order cancellation. Return money to customer if needed. - * - * @param \Magento\Sales\Model\Order $order - * @param string $message - * @param bool $voidPayment - * @return void - */ - protected function declineOrder(\Magento\Sales\Model\Order $order, $message = '', $voidPayment = true) - { - try { - $response = $this->getResponse(); - if ($voidPayment - && $response->getXTransId() - && strtoupper($response->getXType()) == self::REQUEST_TYPE_AUTH_ONLY - ) { - $order->getPayment() - ->setTransactionId(null) - ->setParentTransactionId($response->getXTransId()) - ->void($response); - } - $order->registerCancellation($message)->save(); - $this->_eventManager->dispatch('order_cancel_after', ['order' => $order ]); - } catch (\Exception $e) { - //quiet decline - $this->getPsrLogger()->critical($e); - } - } - - /** - * Return additional information`s transaction_id value of parent transaction model - * - * @param \Magento\Sales\Model\Order\Payment $payment - * @return string - */ - protected function getRealParentTransactionId($payment) - { - $transaction = $this->transactionRepository->getByTransactionId( - $payment->getParentTransactionId(), - $payment->getId(), - $payment->getOrder()->getId() - ); - return $transaction->getAdditionalInformation(self::REAL_TRANSACTION_ID_KEY); - } - - /** - * {inheritdoc} - */ - public function getConfigInterface() - { - return $this; - } - - /** - * Getter for specified value according to set payment method code - * - * @param mixed $key - * @param mixed $storeId - * @return mixed - */ - public function getValue($key, $storeId = null) - { - return $this->getConfigData($key, $storeId); - } - - /** - * Set initialization requirement state - * - * @param bool $isInitializeNeeded - * @return void - */ - public function setIsInitializeNeeded($isInitializeNeeded = true) - { - $this->_isInitializeNeeded = (bool)$isInitializeNeeded; - } - - /** - * Get whether it is possible to capture - * - * @return bool - */ - public function canCapture() - { - return !$this->isGatewayActionsLocked($this->getInfoInstance()); - } - - /** - * Fetch transaction details info - * - * Update transaction info if there is one placing transaction only - * - * @param \Magento\Payment\Model\InfoInterface $payment - * @param string $transactionId - * @return array - */ - public function fetchTransactionInfo(\Magento\Payment\Model\InfoInterface $payment, $transactionId) - { - $transaction = $this->transactionRepository->getByTransactionId( - $transactionId, - $payment->getId(), - $payment->getOrder()->getId() - ); - - $response = $this->getTransactionResponse($transactionId); - if ($response->getXResponseCode() == self::RESPONSE_CODE_APPROVED) { - if ($response->getTransactionStatus() == 'voided') { - $payment->setIsTransactionDenied(true); - $payment->setIsTransactionClosed(true); - $transaction->close(); - } else { - $transaction->setAdditionalInformation(self::TRANSACTION_FRAUD_STATE_KEY, false); - $payment->setIsTransactionApproved(true); - } - } elseif ($response->getXResponseReasonCode() == self::RESPONSE_REASON_CODE_PENDING_REVIEW_DECLINED) { - $payment->setIsTransactionDenied(true); - } - $this->addStatusCommentOnUpdate($payment, $response, $transactionId); - return $response->getData(); - } - - /** - * Add status comment on update - * - * @param \Magento\Sales\Model\Order\Payment $payment - * @param \Magento\Framework\DataObject $response - * @param string $transactionId - * @return $this - */ - protected function addStatusCommentOnUpdate( - \Magento\Sales\Model\Order\Payment $payment, - \Magento\Framework\DataObject $response, - $transactionId - ) { - if ($payment->getIsTransactionApproved()) { - $message = __( - 'Transaction %1 has been approved. Amount %2. Transaction status is "%3"', - $transactionId, - $payment->getOrder()->getBaseCurrency()->formatTxt($payment->getAmountAuthorized()), - $this->dataHelper->getTransactionStatusLabel($response->getTransactionStatus()) - ); - $payment->getOrder()->addStatusHistoryComment($message); - } elseif ($payment->getIsTransactionDenied()) { - $message = __( - 'Transaction %1 has been voided/declined. Transaction status is "%2". Amount %3.', - $transactionId, - $this->dataHelper->getTransactionStatusLabel($response->getTransactionStatus()), - $payment->getOrder()->getBaseCurrency()->formatTxt($payment->getAmountAuthorized()) - ); - $payment->getOrder()->addStatusHistoryComment($message); - } - return $this; - } - - /** - * Sets method code - * - * @param string $methodCode - * @SuppressWarnings(PHPMD.UnusedFormalParameter) - * @return void - */ - public function setMethodCode($methodCode) - { - } - - /** - * Sets path pattern - * - * @param string $pathPattern - * @SuppressWarnings(PHPMD.UnusedFormalParameter) - * @return void - */ - public function setPathPattern($pathPattern) - { - } - - /** - * This function returns full transaction details for a specified transaction ID. - * - * @param string $transactionId - * @return \Magento\Framework\DataObject - * @throws \Magento\Framework\Exception\LocalizedException - * @link http://www.authorize.net/support/ReportingGuide_XML.pdf - * @link http://developer.authorize.net/api/transaction_details/ - */ - protected function getTransactionResponse($transactionId) - { - $responseXmlDocument = $this->transactionService->getTransactionDetails($this, $transactionId); - - $response = new \Magento\Framework\DataObject(); - $response->setXResponseCode((string)$responseXmlDocument->transaction->responseCode) - ->setXResponseReasonCode((string)$responseXmlDocument->transaction->responseReasonCode) - ->setTransactionStatus((string)$responseXmlDocument->transaction->transactionStatus); - - return $response; - } - - /** - * Get psr logger. - * - * @return \Psr\Log\LoggerInterface - * @deprecated 100.1.0 - */ - private function getPsrLogger() - { - if (null === $this->psrLogger) { - $this->psrLogger = ObjectManager::getInstance() - ->get(\Psr\Log\LoggerInterface::class); - } - return $this->psrLogger; - } - - /** - * Fetch order by increment id from response. - * - * @return \Magento\Sales\Model\Order - */ - private function getOrderFromResponse(): \Magento\Sales\Model\Order - { - if (!$this->order) { - $this->order = $this->orderFactory->create(); - - if ($incrementId = $this->getOrderIncrementId()) { - $this->order = $this->order->loadByIncrementId($incrementId); - } - } - - return $this->order; - } - - /** - * Fetch order increment id from response. - * - * @return string - */ - private function getOrderIncrementId(): string - { - return $this->getResponse()->getXInvoiceNum(); - } - - /** - * Checks if filter action is Report Only. - * - * Transactions that trigger this filter are processed as normal, - * but are also reported in the Merchant Interface as triggering this filter. - * - * @param string $fdsFilterAction - * @return bool - */ - private function fdsFilterActionIsReportOnly($fdsFilterAction) - { - return $fdsFilterAction === (string)$this->dataHelper->getFdsFilterActionLabel('report'); - } -} diff --git a/app/code/Magento/Authorizenet/Model/Directpost/Request.php b/app/code/Magento/Authorizenet/Model/Directpost/Request.php deleted file mode 100644 index 10be4cd5febf6..0000000000000 --- a/app/code/Magento/Authorizenet/Model/Directpost/Request.php +++ /dev/null @@ -1,280 +0,0 @@ -dateTimeFactory = $dateTimeFactory ?? ObjectManager::getInstance() - ->get(DateTimeFactory::class); - parent::__construct($data); - } - - /** - * Return merchant transaction key. - * - * Needed to generate MD5 sign. - * - * @return string - */ - protected function _getTransactionKey() - { - return $this->_transKey; - } - - /** - * Set merchant transaction key. - * - * Needed to generate MD5 sign. - * - * @param string $transKey - * @return $this - */ - protected function _setTransactionKey($transKey) - { - $this->_transKey = $transKey; - return $this; - } - - /** - * Generates the MD5 fingerprint for request. - * - * @param string $merchantApiLoginId - * @param string $merchantTransactionKey - * @param string $amount - * @param string $currencyCode - * @param string $fpSequence An invoice number or random number. - * @param string $fpTimestamp - * @return string The fingerprint. - */ - public function generateRequestSign( - $merchantApiLoginId, - $merchantTransactionKey, - $amount, - $currencyCode, - $fpSequence, - $fpTimestamp - ) { - return hash_hmac( - "md5", - $merchantApiLoginId . '^' . $fpSequence . '^' . $fpTimestamp . '^' . $amount . '^' . $currencyCode, - $merchantTransactionKey - ); - } - - /** - * Set Authorizenet data to request. - * - * @param \Magento\Authorizenet\Model\Directpost $paymentMethod - * @return $this - */ - public function setConstantData(\Magento\Authorizenet\Model\Directpost $paymentMethod) - { - $this->setXVersion('3.1')->setXDelimData('FALSE')->setXRelayResponse('TRUE'); - - $this->setSignatureKey($paymentMethod->getConfigData('signature_key')); - - $this->setXLogin($paymentMethod->getConfigData('login')) - ->setXMethod(\Magento\Authorizenet\Model\Authorizenet::REQUEST_METHOD_CC) - ->setXRelayUrl($paymentMethod->getRelayUrl()); - - $this->_setTransactionKey($paymentMethod->getConfigData('trans_key')); - return $this; - } - - /** - * Set entity data to request - * - * @param \Magento\Sales\Model\Order $order - * @param \Magento\Authorizenet\Model\Directpost $paymentMethod - * @return $this - */ - public function setDataFromOrder( - \Magento\Sales\Model\Order $order, - \Magento\Authorizenet\Model\Directpost $paymentMethod - ) { - $payment = $order->getPayment(); - - $this->setXType($payment->getAnetTransType()); - $this->setXFpSequence($order->getQuoteId()); - $this->setXInvoiceNum($order->getIncrementId()); - $this->setXAmount($payment->getBaseAmountAuthorized()); - $this->setXCurrencyCode($order->getBaseCurrencyCode()); - $this->setXTax( - sprintf('%.2F', $order->getBaseTaxAmount()) - )->setXFreight( - sprintf('%.2F', $order->getBaseShippingAmount()) - ); - - //need to use (string) because NULL values IE6-8 decodes as "null" in JSON in JavaScript, - //but we need "" for null values. - $billing = $order->getBillingAddress(); - if (!empty($billing)) { - $this->setXFirstName((string)$billing->getFirstname()) - ->setXLastName((string)$billing->getLastname()) - ->setXCompany((string)$billing->getCompany()) - ->setXAddress((string)$billing->getStreetLine(1)) - ->setXCity((string)$billing->getCity()) - ->setXState((string)$billing->getRegion()) - ->setXZip((string)$billing->getPostcode()) - ->setXCountry((string)$billing->getCountryId()) - ->setXPhone((string)$billing->getTelephone()) - ->setXFax((string)$billing->getFax()) - ->setXCustId((string)$billing->getCustomerId()) - ->setXCustomerIp((string)$order->getRemoteIp()) - ->setXCustomerTaxId((string)$billing->getTaxId()) - ->setXEmail((string)$order->getCustomerEmail()) - ->setXEmailCustomer((string)$paymentMethod->getConfigData('email_customer')) - ->setXMerchantEmail((string)$paymentMethod->getConfigData('merchant_email')); - } - - $shipping = $order->getShippingAddress(); - if (!empty($shipping)) { - $this->setXShipToFirstName( - (string)$shipping->getFirstname() - )->setXShipToLastName( - (string)$shipping->getLastname() - )->setXShipToCompany( - (string)$shipping->getCompany() - )->setXShipToAddress( - (string)$shipping->getStreetLine(1) - )->setXShipToCity( - (string)$shipping->getCity() - )->setXShipToState( - (string)$shipping->getRegion() - )->setXShipToZip( - (string)$shipping->getPostcode() - )->setXShipToCountry( - (string)$shipping->getCountryId() - ); - } - - $this->setXPoNum((string)$payment->getPoNumber()); - - return $this; - } - - /** - * Set sign hash into the request object. - * - * All needed fields should be placed in the object first. - * - * @return $this - */ - public function signRequestData() - { - $fpDate = $this->dateTimeFactory->create('now', new \DateTimeZone('UTC')); - $fpTimestamp = $fpDate->getTimestamp(); - - if (!empty($this->getSignatureKey())) { - $hash = $this->generateSha2RequestSign( - (string)$this->getXLogin(), - (string)$this->getSignatureKey(), - (string)$this->getXAmount(), - (string)$this->getXCurrencyCode(), - (string)$this->getXFpSequence(), - $fpTimestamp - ); - } else { - $hash = $this->generateRequestSign( - $this->getXLogin(), - $this->_getTransactionKey(), - $this->getXAmount(), - $this->getXCurrencyCode(), - $this->getXFpSequence(), - $fpTimestamp - ); - } - - $this->setXFpTimestamp($fpTimestamp); - $this->setXFpHash($hash); - - return $this; - } - - /** - * Generates the SHA2 fingerprint for request. - * - * @param string $merchantApiLoginId - * @param string $merchantSignatureKey - * @param string $amount - * @param string $currencyCode - * @param string $fpSequence An invoice number or random number. - * @param int $fpTimestamp - * @return string The fingerprint. - */ - private function generateSha2RequestSign( - string $merchantApiLoginId, - string $merchantSignatureKey, - string $amount, - string $currencyCode, - string $fpSequence, - int $fpTimestamp - ): string { - $message = $merchantApiLoginId . '^' . $fpSequence . '^' . $fpTimestamp . '^' . $amount . '^' . $currencyCode; - - return strtoupper(hash_hmac('sha512', $message, pack('H*', $merchantSignatureKey))); - } - - /** - * Return merchant hexadecimal signature key. - * - * Needed to generate SHA2 sign. - * - * @return string - */ - private function getSignatureKey(): string - { - return $this->signatureKey; - } - - /** - * Set merchant hexadecimal signature key. - * - * Needed to generate SHA2 sign. - * - * @param string $signatureKey - * @return void - */ - private function setSignatureKey(string $signatureKey) - { - $this->signatureKey = $signatureKey; - } -} diff --git a/app/code/Magento/Authorizenet/Model/Directpost/Request/Factory.php b/app/code/Magento/Authorizenet/Model/Directpost/Request/Factory.php deleted file mode 100644 index 6036935f57be1..0000000000000 --- a/app/code/Magento/Authorizenet/Model/Directpost/Request/Factory.php +++ /dev/null @@ -1,30 +0,0 @@ -getData('x_amount'))) { - $this->setData('x_amount', '0.00'); - } - - if (!empty($this->getData('x_SHA2_Hash'))) { - $hash = $this->generateSha2Hash($storedHash); - return Security::compareStrings($hash, $this->getData('x_SHA2_Hash')); - } elseif (!empty($this->getData('x_MD5_Hash'))) { - $hash = $this->generateHash($storedHash, $merchantApiLogin, $this->getXAmount(), $this->getXTransId()); - return Security::compareStrings($hash, $this->getData('x_MD5_Hash')); - } - - return false; - } - - /** - * Return if this is approved response from Authorize.net auth request. - * - * @return bool - */ - public function isApproved() - { - return $this->getXResponseCode() == \Magento\Authorizenet\Model\Directpost::RESPONSE_CODE_APPROVED; - } - - /** - * Generates an SHA2 hash to compare against AuthNet's. - * - * @param string $signatureKey - * @return string - * @see https://support.authorize.net/s/article/MD5-Hash-End-of-Life-Signature-Key-Replacement - */ - private function generateSha2Hash(string $signatureKey): string - { - $hashFields = [ - 'x_trans_id', - 'x_test_request', - 'x_response_code', - 'x_auth_code', - 'x_cvv2_resp_code', - 'x_cavv_response', - 'x_avs_code', - 'x_method', - 'x_account_number', - 'x_amount', - 'x_company', - 'x_first_name', - 'x_last_name', - 'x_address', - 'x_city', - 'x_state', - 'x_zip', - 'x_country', - 'x_phone', - 'x_fax', - 'x_email', - 'x_ship_to_company', - 'x_ship_to_first_name', - 'x_ship_to_last_name', - 'x_ship_to_address', - 'x_ship_to_city', - 'x_ship_to_state', - 'x_ship_to_zip', - 'x_ship_to_country', - 'x_invoice_num', - ]; - - $message = '^'; - foreach ($hashFields as $field) { - $message .= ($this->getData($field) ?? '') . '^'; - } - - return strtoupper(hash_hmac('sha512', $message, pack('H*', $signatureKey))); - } -} diff --git a/app/code/Magento/Authorizenet/Model/Directpost/Response/Factory.php b/app/code/Magento/Authorizenet/Model/Directpost/Response/Factory.php deleted file mode 100644 index 4fda5ac62b498..0000000000000 --- a/app/code/Magento/Authorizenet/Model/Directpost/Response/Factory.php +++ /dev/null @@ -1,30 +0,0 @@ -getDirectPostOrderIncrementIds(); - if (!$orderIncIds) { - $orderIncIds = []; - } - $orderIncIds[$orderIncrementId] = 1; - $this->setDirectPostOrderIncrementIds($orderIncIds); - } - - /** - * Remove order IncrementId from session - * - * @param string $orderIncrementId - * @return void - */ - public function removeCheckoutOrderIncrementId($orderIncrementId) - { - $orderIncIds = $this->getDirectPostOrderIncrementIds(); - - if (!is_array($orderIncIds)) { - return; - } - - if (isset($orderIncIds[$orderIncrementId])) { - unset($orderIncIds[$orderIncrementId]); - } - $this->setDirectPostOrderIncrementIds($orderIncIds); - } - - /** - * Return if order incrementId is in session. - * - * @param string $orderIncrementId - * @return bool - */ - public function isCheckoutOrderIncrementIdExist($orderIncrementId) - { - $orderIncIds = $this->getDirectPostOrderIncrementIds(); - if (is_array($orderIncIds) && isset($orderIncIds[$orderIncrementId])) { - return true; - } - return false; - } - - /** - * Set quote id to session - * - * @param int|string $id - * @return $this - */ - public function setQuoteId($id) - { - $this->storage->setQuoteId($id); - return $this; - } -} diff --git a/app/code/Magento/Authorizenet/Model/Request.php b/app/code/Magento/Authorizenet/Model/Request.php deleted file mode 100644 index 552439fc8bb9b..0000000000000 --- a/app/code/Magento/Authorizenet/Model/Request.php +++ /dev/null @@ -1,18 +0,0 @@ -objectManager = $objectManager; - $this->instanceName = $instanceName; - } - - /** - * Create class instance with specified parameters - * - * @param array $data - * @return \Magento\Authorizenet\Model\Request - */ - public function create(array $data = []) - { - return $this->objectManager->create($this->instanceName, $data); - } -} diff --git a/app/code/Magento/Authorizenet/Model/ResourceModel/Debug.php b/app/code/Magento/Authorizenet/Model/ResourceModel/Debug.php deleted file mode 100644 index 2c21d0e2e28e0..0000000000000 --- a/app/code/Magento/Authorizenet/Model/ResourceModel/Debug.php +++ /dev/null @@ -1,25 +0,0 @@ -_init('authorizenet_debug', 'debug_id'); - } -} diff --git a/app/code/Magento/Authorizenet/Model/ResourceModel/Debug/Collection.php b/app/code/Magento/Authorizenet/Model/ResourceModel/Debug/Collection.php deleted file mode 100644 index b84ee1e72a2d4..0000000000000 --- a/app/code/Magento/Authorizenet/Model/ResourceModel/Debug/Collection.php +++ /dev/null @@ -1,28 +0,0 @@ -_init( - \Magento\Authorizenet\Model\Debug::class, - \Magento\Authorizenet\Model\ResourceModel\Debug::class - ); - } -} diff --git a/app/code/Magento/Authorizenet/Model/Response.php b/app/code/Magento/Authorizenet/Model/Response.php deleted file mode 100644 index c552663a15373..0000000000000 --- a/app/code/Magento/Authorizenet/Model/Response.php +++ /dev/null @@ -1,18 +0,0 @@ -objectManager = $objectManager; - $this->instanceName = $instanceName; - } - - /** - * Create class instance with specified parameters - * - * @param array $data - * @return \Magento\Authorizenet\Model\Response - */ - public function create(array $data = []) - { - return $this->objectManager->create($this->instanceName, $data); - } -} diff --git a/app/code/Magento/Authorizenet/Model/Source/Cctype.php b/app/code/Magento/Authorizenet/Model/Source/Cctype.php deleted file mode 100644 index ffb3584722450..0000000000000 --- a/app/code/Magento/Authorizenet/Model/Source/Cctype.php +++ /dev/null @@ -1,27 +0,0 @@ - \Magento\Authorizenet\Model\Authorizenet::ACTION_AUTHORIZE, - 'label' => __('Authorize Only'), - ], - [ - 'value' => \Magento\Authorizenet\Model\Authorizenet::ACTION_AUTHORIZE_CAPTURE, - 'label' => __('Authorize and Capture') - ] - ]; - } -} diff --git a/app/code/Magento/Authorizenet/Model/TransactionService.php b/app/code/Magento/Authorizenet/Model/TransactionService.php deleted file mode 100644 index af0b02e94cf45..0000000000000 --- a/app/code/Magento/Authorizenet/Model/TransactionService.php +++ /dev/null @@ -1,183 +0,0 @@ -xmlSecurityHelper = $xmlSecurityHelper; - $this->logger = $logger; - $this->httpClientFactory = $httpClientFactory; - } - - /** - * Get transaction information - * - * @param \Magento\Authorizenet\Model\Authorizenet $context - * @param string $transactionId - * @return \Magento\Framework\Simplexml\Element - * @throws \Magento\Framework\Exception\LocalizedException - */ - public function getTransactionDetails(Authorizenet $context, $transactionId) - { - return isset($this->transactionDetails[$transactionId]) - ? $this->transactionDetails[$transactionId] - : $this->loadTransactionDetails($context, $transactionId); - } - - /** - * Load transaction details - * - * @param \Magento\Authorizenet\Model\Authorizenet $context - * @param string $transactionId - * @return \Magento\Framework\Simplexml\Element - * @throws \Magento\Framework\Exception\LocalizedException - */ - protected function loadTransactionDetails(Authorizenet $context, $transactionId) - { - - $requestBody = $this->getRequestBody( - $context->getConfigData('login'), - $context->getConfigData('trans_key'), - $transactionId - ); - - /** @var \Magento\Framework\HTTP\ZendClient $client */ - $client = $this->httpClientFactory->create(); - $url = $context->getConfigData('cgi_url_td') ?: self::CGI_URL_TD; - $client->setUri($url); - $client->setConfig(['timeout' => self::CONNECTION_TIMEOUT]); - $client->setHeaders(['Content-Type: text/xml']); - $client->setMethod(\Zend_Http_Client::POST); - $client->setRawData($requestBody); - - $debugData = ['url' => $url, 'request' => $this->removePrivateDataFromXml($requestBody)]; - - try { - $responseBody = $client->request()->getBody(); - if (!$this->xmlSecurityHelper->scan($responseBody)) { - $this->logger->critical('Attempt loading of external XML entities in response from Authorizenet.'); - throw new \Exception(); - } - $debugData['response'] = $responseBody; - libxml_use_internal_errors(true); - $responseXmlDocument = new Element($responseBody); - libxml_use_internal_errors(false); - } catch (\Exception $e) { - throw new LocalizedException(__('The transaction details are unavailable. Please try again later.')); - } finally { - $context->debugData($debugData); - } - - if (!isset($responseXmlDocument->messages->resultCode) - || $responseXmlDocument->messages->resultCode != static::PAYMENT_UPDATE_STATUS_CODE_SUCCESS - ) { - throw new LocalizedException(__('The transaction details are unavailable. Please try again later.')); - } - - $this->transactionDetails[$transactionId] = $responseXmlDocument; - return $responseXmlDocument; - } - - /** - * Create request body to get transaction details - * - * @param string $login - * @param string $transactionKey - * @param string $transactionId - * @return string - */ - private function getRequestBody($login, $transactionKey, $transactionId) - { - $requestBody = sprintf( - '' . - '' . - '%s%s' . - '%s' . - '', - $login, - $transactionKey, - $transactionId - ); - return $requestBody; - } - - /** - * Remove nodes with private data from XML string - * - * Uses values from $_debugReplacePrivateDataKeys property - * - * @param string $xml - * @return string - */ - private function removePrivateDataFromXml($xml) - { - foreach ($this->debugReplacePrivateDataKeys as $key) { - $xml = preg_replace(sprintf('~(?<=<%s>).*?(?=)~', $key, $key), Logger::DEBUG_KEYS_MASK, $xml); - } - return $xml; - } -} diff --git a/app/code/Magento/Authorizenet/Observer/AddFieldsToResponseObserver.php b/app/code/Magento/Authorizenet/Observer/AddFieldsToResponseObserver.php deleted file mode 100644 index bdd10437927c8..0000000000000 --- a/app/code/Magento/Authorizenet/Observer/AddFieldsToResponseObserver.php +++ /dev/null @@ -1,109 +0,0 @@ -coreRegistry = $coreRegistry; - $this->payment = $payment; - $this->session = $session; - $this->storeManager = $storeManager; - } - - /** - * Save order into registry to use it in the overloaded controller. - * - * @param \Magento\Framework\Event\Observer $observer - * @return $this - */ - public function execute(\Magento\Framework\Event\Observer $observer) - { - /* @var $order Order */ - $order = $this->coreRegistry->registry('directpost_order'); - - if (!$order || !$order->getId()) { - return $this; - } - - $payment = $order->getPayment(); - - if (!$payment || $payment->getMethod() != $this->payment->getCode()) { - return $this; - } - - $result = $observer->getData('result')->getData(); - - if (!empty($result['error'])) { - return $this; - } - - // if success, then set order to session and add new fields - $this->session->addCheckoutOrderIncrementId($order->getIncrementId()); - $this->session->setLastOrderIncrementId($order->getIncrementId()); - - $requestToAuthorizenet = $payment->getMethodInstance() - ->generateRequestFromOrder($order); - $requestToAuthorizenet->setControllerActionName( - $observer->getData('action') - ->getRequest() - ->getControllerName() - ); - $requestToAuthorizenet->setIsSecure( - (string)$this->storeManager->getStore() - ->isCurrentlySecure() - ); - - $result[$this->payment->getCode()] = ['fields' => $requestToAuthorizenet->getData()]; - - $observer->getData('result')->setData($result); - - return $this; - } -} diff --git a/app/code/Magento/Authorizenet/Observer/SaveOrderAfterSubmitObserver.php b/app/code/Magento/Authorizenet/Observer/SaveOrderAfterSubmitObserver.php deleted file mode 100644 index 45f0adfa96f4f..0000000000000 --- a/app/code/Magento/Authorizenet/Observer/SaveOrderAfterSubmitObserver.php +++ /dev/null @@ -1,48 +0,0 @@ -coreRegistry = $coreRegistry; - } - - /** - * Save order into registry to use it in the overloaded controller. - * - * @param \Magento\Framework\Event\Observer $observer - * @return $this - */ - public function execute(\Magento\Framework\Event\Observer $observer) - { - /* @var $order Order */ - $order = $observer->getEvent()->getData('order'); - $this->coreRegistry->register('directpost_order', $order, true); - - return $this; - } -} diff --git a/app/code/Magento/Authorizenet/Observer/UpdateAllEditIncrementsObserver.php b/app/code/Magento/Authorizenet/Observer/UpdateAllEditIncrementsObserver.php deleted file mode 100644 index d6cc51eb63c01..0000000000000 --- a/app/code/Magento/Authorizenet/Observer/UpdateAllEditIncrementsObserver.php +++ /dev/null @@ -1,48 +0,0 @@ -authorizenetData = $authorizenetData; - } - - /** - * Save order into registry to use it in the overloaded controller. - * - * @param \Magento\Framework\Event\Observer $observer - * @return $this - */ - public function execute(\Magento\Framework\Event\Observer $observer) - { - /* @var $order Order */ - $order = $observer->getEvent()->getData('order'); - $this->authorizenetData->updateOrderEditIncrements($order); - - return $this; - } -} diff --git a/app/code/Magento/Authorizenet/README.md b/app/code/Magento/Authorizenet/README.md deleted file mode 100644 index 62598837bee6d..0000000000000 --- a/app/code/Magento/Authorizenet/README.md +++ /dev/null @@ -1,42 +0,0 @@ -# Magento_Authorizenet module - -The Magento_Authorizenet module implements the integration with the Authorize.Net payment gateway and makes the latter available as a payment method in Magento. - -## Extensibility - -Extension developers can interact with the Magento_Authorizenet module. For more information about the Magento extension mechanism, see [Magento plug-ins](https://devdocs.magento.com/guides/v2.3/extension-dev-guide/plugins.html). - -[The Magento dependency injection mechanism](https://devdocs.magento.com/guides/v2.3/extension-dev-guide/depend-inj.html) enables you to override the functionality of the Magento_Authorizenet module. - -### Events - -This module dispatches the following events: - - - `checkout_directpost_placeOrder` event in the `\Magento\Authorizenet\Controller\Directpost\Payment\Place::placeCheckoutOrder()` method. Parameters: - - `result` is a data object (`\Magento\Framework\DataObject` class). - - `action` is a controller object (`\Magento\Authorizenet\Controller\Directpost\Payment\Place`). - - - `order_cancel_after` event in the `\Magento\Authorizenet\Model\Directpost::declineOrder()` method. Parameters: - - `order` is an order object (`\Magento\Sales\Model\Order` class). - - -This module observes the following events: - - - `checkout_submit_all_after` event in the `Magento\Authorizenet\Observer\SaveOrderAfterSubmitObserver` file. - - `checkout_directpost_placeOrder` event in the `Magento\Authorizenet\Observer\AddFieldsToResponseObserver` file. - -For information about events in Magento 2, see [Events and observers](http://devdocs.magento.com/guides/v2.3/extension-dev-guide/events-and-observers.html#events). - -### Layouts - -This module introduces the following layouts and layout handles in the `view/adminhtml/layout` directory: - -- `adminhtml_authorizenet_directpost_payment_redirect` - -This module introduces the following layouts and layout handles in the `view/frontend/layout` directory: - -- `authorizenet_directpost_payment_backendresponse` -- `authorizenet_directpost_payment_redirect` -- `authorizenet_directpost_payment_response` - -For more information about layouts in Magento 2, see the [Layout documentation](https://devdocs.magento.com/guides/v2.3/frontend-dev-guide/layouts/layout-overview.html). diff --git a/app/code/Magento/Authorizenet/Test/Mftf/README.md b/app/code/Magento/Authorizenet/Test/Mftf/README.md deleted file mode 100644 index 9391126a85c94..0000000000000 --- a/app/code/Magento/Authorizenet/Test/Mftf/README.md +++ /dev/null @@ -1,3 +0,0 @@ -# Authorizenet Functional Tests - -The Functional Test Module for **Magento Authorizenet** module. diff --git a/app/code/Magento/Authorizenet/Test/Unit/Controller/Adminhtml/Authorizenet/Directpost/Payment/RedirectTest.php b/app/code/Magento/Authorizenet/Test/Unit/Controller/Adminhtml/Authorizenet/Directpost/Payment/RedirectTest.php deleted file mode 100644 index b26c9bf22f153..0000000000000 --- a/app/code/Magento/Authorizenet/Test/Unit/Controller/Adminhtml/Authorizenet/Directpost/Payment/RedirectTest.php +++ /dev/null @@ -1,312 +0,0 @@ -directpostSessionMock = $this->getMockBuilder(\Magento\Authorizenet\Model\Directpost\Session::class) - ->setMethods([ - 'getLastOrderIncrementId', - 'removeCheckoutOrderIncrementId', - 'isCheckoutOrderIncrementIdExist', - 'unsetData' - ]) - ->disableOriginalConstructor() - ->getMock(); - $this->objectManagerMock = $this->createMock(\Magento\Framework\ObjectManagerInterface::class); - $this->orderMock = $this->getMockBuilder(\Magento\Sales\Model\Order::class) - ->setMethods(['getId', 'getState', 'getIncrementId', 'registerCancellation', 'loadByIncrementId', 'save']) - ->disableOriginalConstructor() - ->getMock(); - $this->adminOrderCreateMock = $this->getMockBuilder(\Magento\Sales\Model\AdminOrder\Create::class) - ->setMethods(['getSession']) - ->disableOriginalConstructor() - ->getMock(); - $sessionMock = $this->getMockBuilder(\Magento\Backend\Model\Session::class) - ->disableOriginalConstructor() - ->getMock(); - $this->sessionQuoteMock = $this->getMockBuilder(\Magento\Backend\Model\Session\Quote::class) - ->setMethods(['getOrder', 'clearStorage']) - ->disableOriginalConstructor() - ->getMock(); - $this->objectManagerMock->expects($this->atLeastOnce()) - ->method('get') - ->willReturnMap([ - [\Magento\Authorizenet\Model\Directpost\Session::class, $this->directpostSessionMock], - [\Magento\Sales\Model\AdminOrder\Create::class, $this->adminOrderCreateMock], - [\Magento\Backend\Model\Session\Quote::class, $this->sessionQuoteMock], - [\Magento\Backend\Model\Session::class, $sessionMock], - ]); - $this->objectManagerMock->expects($this->any()) - ->method('create') - ->with(\Magento\Sales\Model\Order::class) - ->willReturn($this->orderMock); - $this->requestMock = $this->getMockBuilder(\Magento\Framework\App\RequestInterface::class) - ->setMethods(['getParams']) - ->getMockForAbstractClass(); - $responseMock = $this->getMockForAbstractClass(\Magento\Framework\App\ResponseInterface::class); - $redirectMock = $this->createMock(\Magento\Framework\App\Response\RedirectInterface::class); - $this->messageManagerMock = $this->createMock(\Magento\Framework\Message\ManagerInterface::class); - - $this->contextMock = $this->getMockBuilder(\Magento\Backend\App\Action\Context::class) - ->setMethods(['getObjectManager', 'getRequest', 'getResponse', 'getRedirect', 'getMessageManager']) - ->disableOriginalConstructor() - ->getMock(); - $this->contextMock->expects($this->any())->method('getObjectManager')->willReturn($this->objectManagerMock); - $this->contextMock->expects($this->any())->method('getRequest')->willReturn($this->requestMock); - $this->contextMock->expects($this->any())->method('getResponse')->willReturn($responseMock); - $this->contextMock->expects($this->any())->method('getRedirect')->willReturn($redirectMock); - $this->contextMock->expects($this->any())->method('getMessageManager')->willReturn($this->messageManagerMock); - - $this->productHelperMock = $this->getMockBuilder(\Magento\Catalog\Helper\Product::class) - ->disableOriginalConstructor() - ->getMock(); - $this->escaperMock = $this->getMockBuilder(\Magento\Framework\Escaper::class) - ->disableOriginalConstructor() - ->getMock(); - $this->resultPageFactoryMock = $this->getMockBuilder(\Magento\Framework\View\Result\PageFactory::class) - ->disableOriginalConstructor() - ->getMock(); - $this->forwardFactoryMock = $this->getMockBuilder(\Magento\Backend\Model\View\Result\ForwardFactory::class) - ->setMethods(['create']) - ->disableOriginalConstructor() - ->getMock(); - $this->coreRegistryMock = $this->getMockBuilder(\Magento\Framework\Registry::class) - ->setMethods(['register']) - ->disableOriginalConstructor() - ->getMock(); - $resultLayoutMock = $this->getMockBuilder(\Magento\Framework\View\Result\Layout::class) - ->disableOriginalConstructor() - ->getMock(); - $this->resultLayoutFactoryMock = $this->getMockBuilder(\Magento\Framework\View\Result\LayoutFactory::class) - ->setMethods(['create']) - ->disableOriginalConstructor() - ->getMock(); - $this->resultLayoutFactoryMock->expects($this->once()) - ->method('create') - ->willReturn($resultLayoutMock); - $this->helperMock = $this->getMockBuilder(\Magento\Authorizenet\Helper\Backend\Data::class) - ->setMethods(['getSuccessOrderUrl']) - ->disableOriginalConstructor() - ->getMock(); - - $this->controller = new Redirect( - $this->contextMock, - $this->productHelperMock, - $this->escaperMock, - $this->resultPageFactoryMock, - $this->forwardFactoryMock, - $this->coreRegistryMock, - $this->resultLayoutFactoryMock, - $this->helperMock - ); - } - - public function testExecuteErrorMsgWithoutCancelOrder() - { - $params = ['success' => 0, 'error_msg' => 'Error message']; - $incrementId = 1; - $this->requestMock->expects($this->once()) - ->method('getParams') - ->willReturn($params); - $this->directpostSessionMock->expects($this->once()) - ->method('getLastOrderIncrementId') - ->willReturn($incrementId); - $this->directpostSessionMock->expects($this->once()) - ->method('isCheckoutOrderIncrementIdExist') - ->with($incrementId) - ->willReturn(true); - - $this->orderMock->expects($this->once()) - ->method('loadByIncrementId') - ->with($incrementId) - ->willReturnSelf(); - $this->orderMock->expects($this->once()) - ->method('getId') - ->willReturn(true); - $this->orderMock->expects($this->once()) - ->method('getIncrementId') - ->willReturn($incrementId); - $this->orderMock->expects($this->once()) - ->method('getState') - ->willReturn(\Magento\Sales\Model\Order::STATE_PENDING_PAYMENT); - $this->orderMock->expects($this->once()) - ->method('registerCancellation') - ->with($params['error_msg']) - ->willReturnSelf(); - $this->orderMock->expects($this->once()) - ->method('save'); - - $this->directpostSessionMock->expects($this->once()) - ->method('removeCheckoutOrderIncrementId') - ->with($incrementId); - $this->coreRegistryMock->expects($this->once()) - ->method('register') - ->with(Iframe::REGISTRY_KEY); - - $this->assertInstanceOf(\Magento\Framework\View\Result\Layout::class, $this->controller->execute()); - } - - public function testExecuteErrorMsgWithCancelOrder() - { - $params = ['success' => 0, 'error_msg' => 'Error message', 'x_invoice_num' => 1]; - $incrementId = 1; - $this->requestMock->expects($this->once()) - ->method('getParams') - ->willReturn($params); - $this->directpostSessionMock->expects($this->once()) - ->method('getLastOrderIncrementId') - ->willReturn($incrementId); - $this->directpostSessionMock->expects($this->once()) - ->method('isCheckoutOrderIncrementIdExist') - ->with($incrementId) - ->willReturn(true); - $this->orderMock->expects($this->once()) - ->method('loadByIncrementId') - ->with($incrementId) - ->willReturnSelf(); - $this->orderMock->expects($this->once()) - ->method('getId') - ->willReturn(true); - $this->orderMock->expects($this->once()) - ->method('getIncrementId') - ->willReturn($incrementId); - $this->directpostSessionMock->expects($this->once()) - ->method('removeCheckoutOrderIncrementId') - ->with($incrementId); - - $this->coreRegistryMock->expects($this->once()) - ->method('register') - ->with(Iframe::REGISTRY_KEY); - - $this->assertInstanceOf(\Magento\Framework\View\Result\Layout::class, $this->controller->execute()); - } - - public function testExecuteSuccess() - { - $params = ['success' => 1, 'controller_action_name' => 'action', 'x_invoice_num' => 1]; - $this->requestMock->expects($this->once()) - ->method('getParams') - ->willReturn($params); - - $this->helperMock->expects($this->once()) - ->method('getSuccessOrderUrl') - ->willReturn('redirect_parent_url'); - - $this->directpostSessionMock->expects($this->once()) - ->method('unsetData') - ->with('quote_id'); - - $this->orderMock->expects($this->once()) - ->method('getId') - ->willReturn(null); - - $this->sessionQuoteMock->expects($this->atLeastOnce()) - ->method('getOrder') - ->willReturn($this->orderMock); - - $this->adminOrderCreateMock->expects($this->atLeastOnce()) - ->method('getSession') - ->willReturn($this->sessionQuoteMock); - - $this->coreRegistryMock->expects($this->once()) - ->method('register') - ->with(Iframe::REGISTRY_KEY); - - $this->assertInstanceOf(\Magento\Framework\View\Result\Layout::class, $this->controller->execute()); - } -} diff --git a/app/code/Magento/Authorizenet/Test/Unit/Controller/Directpost/Payment/PlaceTest.php b/app/code/Magento/Authorizenet/Test/Unit/Controller/Directpost/Payment/PlaceTest.php deleted file mode 100644 index c0a50e66759ba..0000000000000 --- a/app/code/Magento/Authorizenet/Test/Unit/Controller/Directpost/Payment/PlaceTest.php +++ /dev/null @@ -1,332 +0,0 @@ -directpostSessionMock = $this - ->getMockBuilder(\Magento\Authorizenet\Model\Directpost\Session::class) - ->disableOriginalConstructor() - ->getMock(); - $this->quoteMock = $this - ->getMockBuilder(\Magento\Quote\Model\Quote::class) - ->disableOriginalConstructor() - ->getMock(); - $this->checkoutSessionMock = $this - ->getMockBuilder(\Magento\Checkout\Model\Session::class) - ->disableOriginalConstructor() - ->getMock(); - $this->checkoutSessionMock->expects($this->any()) - ->method('getQuote') - ->will($this->returnValue($this->quoteMock)); - $this->objectManagerMock = $this - ->getMockBuilder(\Magento\Framework\ObjectManagerInterface::class) - ->getMockForAbstractClass(); - $this->objectManagerMock->expects($this->any()) - ->method('get') - ->willReturnMap([ - [\Magento\Authorizenet\Model\Directpost\Session::class, $this->directpostSessionMock], - [\Magento\Checkout\Model\Session::class, $this->checkoutSessionMock], - ]); - $this->coreRegistryMock = $this - ->getMockBuilder(\Magento\Framework\Registry::class) - ->disableOriginalConstructor() - ->getMock(); - $this->dataFactoryMock = $this - ->getMockBuilder(\Magento\Authorizenet\Helper\DataFactory::class) - ->disableOriginalConstructor() - ->getMock(); - $this->cartManagementMock = $this - ->getMockBuilder(\Magento\Quote\Api\CartManagementInterface::class) - ->disableOriginalConstructor() - ->getMock(); - $this->onepageCheckout = $this - ->getMockBuilder(\Magento\Checkout\Model\Type\Onepage::class) - ->disableOriginalConstructor() - ->getMock(); - $this->jsonHelperMock = $this - ->getMockBuilder(\Magento\Framework\Json\Helper\Data::class) - ->disableOriginalConstructor() - ->getMock(); - $this->requestMock = $this - ->getMockBuilder(\Magento\Framework\App\RequestInterface::class) - ->getMockForAbstractClass(); - $this->responseMock = $this - ->getMockBuilder(\Magento\Framework\App\Response\Http::class) - ->disableOriginalConstructor() - ->getMockForAbstractClass(); - $this->loggerMock = $this - ->getMockBuilder(\Psr\Log\LoggerInterface::class) - ->getMock(); - - $this->objectManager = new ObjectManager($this); - $this->placeOrderController = $this->objectManager->getObject( - \Magento\Authorizenet\Controller\Directpost\Payment\Place::class, - [ - 'request' => $this->requestMock, - 'response' => $this->responseMock, - 'objectManager' => $this->objectManagerMock, - 'coreRegistry' => $this->coreRegistryMock, - 'dataFactory' => $this->dataFactoryMock, - 'cartManagement' => $this->cartManagementMock, - 'onepageCheckout' => $this->onepageCheckout, - 'jsonHelper' => $this->jsonHelperMock, - 'logger' => $this->loggerMock, - ] - ); - } - - /** - * @param $paymentMethod - * @param $controller - * @param $quoteId - * @param $orderId - * @param $result - * @dataProvider textExecuteDataProvider - */ - public function testExecute( - $paymentMethod, - $controller, - $quoteId, - $orderId, - $result - ) { - $this->requestMock->expects($this->at(0)) - ->method('getParam') - ->with('payment') - ->will($this->returnValue($paymentMethod)); - - $this->requestMock->expects($this->at(1)) - ->method('getParam') - ->with('controller') - ->will($this->returnValue($controller)); - - $this->quoteMock->expects($this->any()) - ->method('getId') - ->will($this->returnValue($quoteId)); - - $this->cartManagementMock->expects($this->any()) - ->method('placeOrder') - ->will($this->returnValue($orderId)); - - $this->jsonHelperMock->expects($this->any()) - ->method('jsonEncode') - ->with($result); - - $this->placeOrderController->execute(); - } - - /** - * @param $paymentMethod - * @param $controller - * @param $quoteId - * @param $result - * @param \Exception $exception Exception to check - * @dataProvider textExecuteFailedPlaceOrderDataProvider - */ - public function testExecuteFailedPlaceOrder( - $paymentMethod, - $controller, - $quoteId, - $result, - $exception - ) { - $this->requestMock->expects($this->at(0)) - ->method('getParam') - ->with('payment') - ->will($this->returnValue($paymentMethod)); - - $this->requestMock->expects($this->at(1)) - ->method('getParam') - ->with('controller') - ->will($this->returnValue($controller)); - - $this->quoteMock->expects($this->any()) - ->method('getId') - ->will($this->returnValue($quoteId)); - - $this->cartManagementMock->expects($this->once()) - ->method('placeOrder') - ->willThrowException($exception); - - $this->loggerMock->expects($this->once()) - ->method('critical') - ->with($exception); - - $this->jsonHelperMock->expects($this->any()) - ->method('jsonEncode') - ->with($result); - - $this->placeOrderController->execute(); - } - - /** - * @return array - */ - public function textExecuteDataProvider() - { - $objectSuccess = new \Magento\Framework\DataObject(); - $objectSuccess->setData('success', true); - - return [ - [ - ['method' => null], - IframeConfigProvider::CHECKOUT_IDENTIFIER, - 1, - 1, - ['error_messages' => __('Please choose a payment method.'), 'goto_section' => 'payment'] - ], - [ - ['method' => 'authorizenet_directpost'], - IframeConfigProvider::CHECKOUT_IDENTIFIER, - 1, - 1, - $objectSuccess - ], - ]; - } - - /** - * @return array - */ - public function textExecuteFailedPlaceOrderDataProvider() - { - $objectFailed1 = new \Magento\Framework\DataObject( - [ - 'error' => true, - 'error_messages' => __( - 'A server error stopped your order from being placed. Please try to place your order again.' - ) - ] - ); - $generalException = new \Exception('Exception logging will save the world!'); - $localizedException = new LocalizedException(__('Electronic payments save the trees.')); - $objectFailed2 = new \Magento\Framework\DataObject( - [ - 'error' => true, - 'error_messages' => $localizedException->getMessage() - ] - ); - - return [ - [ - ['method' => 'authorizenet_directpost'], - IframeConfigProvider::CHECKOUT_IDENTIFIER, - 1, - $objectFailed1, - $generalException, - ], - [ - ['method' => 'authorizenet_directpost'], - IframeConfigProvider::CHECKOUT_IDENTIFIER, - 1, - $objectFailed2, - $localizedException, - ], - ]; - } -} diff --git a/app/code/Magento/Authorizenet/Test/Unit/Controller/Directpost/Payment/RedirectTest.php b/app/code/Magento/Authorizenet/Test/Unit/Controller/Directpost/Payment/RedirectTest.php deleted file mode 100644 index 0801d3c020119..0000000000000 --- a/app/code/Magento/Authorizenet/Test/Unit/Controller/Directpost/Payment/RedirectTest.php +++ /dev/null @@ -1,89 +0,0 @@ -request = static::getMockForAbstractClass(RequestInterface::class); - - $this->view = static::getMockForAbstractClass(ViewInterface::class); - - $this->coreRegistry = static::getMockBuilder(Registry::class) - ->disableOriginalConstructor() - ->setMethods(['register']) - ->getMock(); - - $this->controller = $objectManager->getObject(Redirect::class, [ - 'request' => $this->request, - 'view' => $this->view, - 'coreRegistry' => $this->coreRegistry - ]); - } - - /** - * @covers \Magento\Authorizenet\Controller\Directpost\Payment\Redirect::execute - */ - public function testExecute() - { - $url = 'http://test.com/redirect?=test'; - $params = [ - 'order_success' => $url - ]; - $this->request->expects(static::once()) - ->method('getParams') - ->willReturn($params); - - $this->coreRegistry->expects(static::once()) - ->method('register') - ->with(Iframe::REGISTRY_KEY, []); - - $this->view->expects(static::once()) - ->method('addPageLayoutHandles'); - $this->view->expects(static::once()) - ->method('loadLayout') - ->with(false) - ->willReturnSelf(); - $this->view->expects(static::once()) - ->method('renderLayout'); - - $this->controller->execute(); - } -} diff --git a/app/code/Magento/Authorizenet/Test/Unit/Helper/Backend/DataTest.php b/app/code/Magento/Authorizenet/Test/Unit/Helper/Backend/DataTest.php deleted file mode 100644 index 14ecd7debc972..0000000000000 --- a/app/code/Magento/Authorizenet/Test/Unit/Helper/Backend/DataTest.php +++ /dev/null @@ -1,135 +0,0 @@ -urlBuilderMock = $this->createPartialMock(\Magento\Backend\Model\Url::class, ['getUrl']); - - $contextMock = $this->createMock(\Magento\Framework\App\Helper\Context::class); - $contextMock->expects($this->any()) - ->method('getUrlBuilder') - ->willReturn($this->urlBuilderMock); - - $this->orderFactoryMock = $this->createPartialMock(\Magento\Sales\Model\OrderFactory::class, ['create']); - $this->storeManagerMock = $this->createMock(\Magento\Store\Model\StoreManager::class); - - $this->dataHelper = $helper->getObject( - \Magento\Authorizenet\Helper\Backend\Data::class, - [ - 'context' => $contextMock, - 'storeManager' =>$this->storeManagerMock, - 'orderFactory' =>$this->orderFactoryMock, - 'backendUrl' =>$this->urlBuilderMock - ] - ); - } - - public function testGetPlaceOrderAdminUrl() - { - $this->urlBuilderMock->expects($this->once()) - ->method('getUrl') - ->with('adminhtml/authorizenet_directpost_payment/place') - ->willReturn('some value'); - - $this->assertEquals('some value', $this->dataHelper->getPlaceOrderAdminUrl()); - } - - public function testGetSuccessOrderUrl() - { - $orderMock = $this->createPartialMock( - \Magento\Sales\Model\Order::class, - ['loadByIncrementId', 'getId', '__wakeup'] - ); - $orderMock->expects($this->once()) - ->method('loadByIncrementId') - ->with('invoice number') - ->willReturnSelf(); - - $orderMock->expects($this->once()) - ->method('getId') - ->willReturn('order id'); - - $this->orderFactoryMock->expects($this->once()) - ->method('create') - ->willReturn($orderMock); - - $this->urlBuilderMock->expects($this->once()) - ->method('getUrl') - ->with('sales/order/view', ['order_id' => 'order id']) - ->willReturn('some value'); - - $this->assertEquals( - 'some value', - $this->dataHelper->getSuccessOrderUrl(['x_invoice_num' => 'invoice number', 'some param']) - ); - } - - public function testGetRedirectIframeUrl() - { - $params = ['some params', '_secure' => true]; - $this->urlBuilderMock->expects($this->once()) - ->method('getUrl') - ->with('adminhtml/authorizenet_directpost_payment/redirect', $params) - ->willReturn('some value'); - - $this->assertEquals('some value', $this->dataHelper->getRedirectIframeUrl($params)); - } - - public function testGetRelayUrl() - { - $baseUrl = 'http://base.url/'; - - $defaultStoreMock = $this->getMockBuilder(\Magento\Store\Model\Store::class) - ->disableOriginalConstructor() - ->getMock(); - - $defaultStoreMock->expects($this->once()) - ->method('getBaseUrl') - ->with(\Magento\Framework\UrlInterface::URL_TYPE_LINK) - ->willReturn($baseUrl); - - $this->storeManagerMock->expects($this->once()) - ->method('getDefaultStoreView') - ->willReturn(null); - - $this->storeManagerMock->expects($this->once()) - ->method('getStores') - ->willReturn([$defaultStoreMock]); - - $this->assertSame( - 'http://base.url/authorizenet/directpost_payment/backendResponse', - $this->dataHelper->getRelayUrl() - ); - } -} diff --git a/app/code/Magento/Authorizenet/Test/Unit/Helper/DataTest.php b/app/code/Magento/Authorizenet/Test/Unit/Helper/DataTest.php deleted file mode 100644 index 28dd5f866a609..0000000000000 --- a/app/code/Magento/Authorizenet/Test/Unit/Helper/DataTest.php +++ /dev/null @@ -1,193 +0,0 @@ -storeManagerMock = $this->getMockBuilder(\Magento\Store\Model\StoreManagerInterface::class) - ->getMockForAbstractClass(); - - $this->dataHelper = $helper->getObject( - \Magento\Authorizenet\Helper\Data::class, - ['storeManager' => $this->storeManagerMock] - ); - } - - /** - * @param $type - * @param $amount - * @param $exception - * @param $additionalMessage - * @param $expected - * @dataProvider getMessagesParamDataProvider - */ - public function testGetTransactionMessage($type, $amount, $exception, $additionalMessage, $expected) - { - $currency = $this->createPartialMock(\Magento\Directory\Model\Currency::class, ['formatTxt', '__wakeup']); - $currency->expects($this->any()) - ->method('formatTxt') - ->will($this->returnValue($amount)); - $order = $this->createPartialMock(\Magento\Sales\Model\Order::class, ['getBaseCurrency', '__wakeup']); - $order->expects($this->any()) - ->method('getBaseCurrency') - ->will($this->returnValue($currency)); - $payment = $this->createPartialMock(\Magento\Payment\Model\Info::class, ['getOrder', '__wakeup']); - $payment->expects($this->any()) - ->method('getOrder') - ->will($this->returnValue($order)); - $card = new \Magento\Framework\DataObject(['cc_last_4' => self::LAST4]); - $message = $this->dataHelper->getTransactionMessage( - $payment, - $type, - self::TRID, - $card, - $amount, - $exception, - $additionalMessage - ); - - $this->assertEquals($expected, $message); - } - - /** - * @return array - */ - public function getMessagesParamDataProvider() - { - $amount = 12.30; - $additionalMessage = 'Addition message.'; - return [ - [ - 'AUTH_ONLY', - $amount, - false, - $additionalMessage, - 'Credit Card: xxxx-' . self::LAST4 . ' amount 12.3 authorize - successful. ' - . 'Authorize.Net Transaction ID ' . self::TRID . '. Addition message.', - ], - [ - 'AUTH_CAPTURE', - $amount, - 'some exception', - false, - 'Credit Card: xxxx-' . self::LAST4 . ' amount 12.3 authorize and capture - failed. ' - . 'Authorize.Net Transaction ID ' . self::TRID . '. some exception' - ], - [ - 'CREDIT', - false, - false, - $additionalMessage, - 'Credit Card: xxxx-' . self::LAST4 . ' refund - successful. ' - . 'Authorize.Net Transaction ID ' . self::TRID . '. Addition message.' - ], - ]; - } - - public function testGetRelayUrl() - { - $storeId = 10; - $baseUrl = 'http://base.url/'; - - $storeMock = $this->getMockBuilder(\Magento\Store\Model\Store::class) - ->disableOriginalConstructor() - ->getMock(); - - $storeMock->expects($this->once()) - ->method('getBaseUrl') - ->with(\Magento\Framework\UrlInterface::URL_TYPE_LINK) - ->willReturn($baseUrl); - - $this->storeManagerMock->expects($this->once()) - ->method('getStore') - ->with($storeId) - ->willReturn($storeMock); - - $this->assertSame( - 'http://base.url/authorizenet/directpost_payment/response', - $this->dataHelper->getRelayUrl($storeId) - ); - } - - /** - * @param string $code - * @param string $expected - * - * @dataProvider getFdsFilterActionLabelDataProvider - */ - public function testGetFdsFilterActionLabel($code, $expected) - { - $this->assertSame($expected, (string)$this->dataHelper->getFdsFilterActionLabel($code)); - } - - /** - * @return array - */ - public function getFdsFilterActionLabelDataProvider() - { - return [ - ['decline ', 'Decline'], - ['hold', 'Hold'], - ['authAndHold', 'Authorize and Hold'], - ['report', 'Report Only'], - ['unknown_status', 'unknown_status'] - ]; - } - - /** - * @param string $code - * @param string $expected - * - * @dataProvider getTransactionStatusLabelDataProvider - */ - public function testGetTransactionStatusLabel($code, $expected) - { - $this->assertSame($expected, (string)$this->dataHelper->getTransactionStatusLabel($code)); - } - - /** - * @return array - */ - public function getTransactionStatusLabelDataProvider() - { - return [ - ['authorizedPendingCapture', 'Authorized/Pending Capture'], - ['capturedPendingSettlement', 'Captured/Pending Settlement'], - ['refundSettledSuccessfully', 'Refund/Settled Successfully'], - ['refundPendingSettlement', 'Refund/Pending Settlement'], - ['declined', 'Declined'], - ['expired', 'Expired'], - ['voided', 'Voided'], - ['FDSPendingReview', 'FDS - Pending Review'], - ['FDSAuthorizedPendingReview', 'FDS - Authorized/Pending Review'], - ['unknown_status', 'unknown_status'] - ]; - } -} diff --git a/app/code/Magento/Authorizenet/Test/Unit/Model/Directpost/Request/FactoryTest.php b/app/code/Magento/Authorizenet/Test/Unit/Model/Directpost/Request/FactoryTest.php deleted file mode 100644 index 99f2729fcdddf..0000000000000 --- a/app/code/Magento/Authorizenet/Test/Unit/Model/Directpost/Request/FactoryTest.php +++ /dev/null @@ -1,49 +0,0 @@ -requestMock = $this->createMock(\Magento\Authorizenet\Model\Directpost\Request::class); - - $this->objectManagerMock = $this->createMock(\Magento\Framework\ObjectManagerInterface::class); - $this->objectManagerMock->expects($this->once()) - ->method('create') - ->with(\Magento\Authorizenet\Model\Directpost\Request::class, []) - ->willReturn($this->requestMock); - - $this->requestFactory = $objectManager->getObject( - \Magento\Authorizenet\Model\Directpost\Request\Factory::class, - ['objectManager' => $this->objectManagerMock] - ); - } - - public function testCreate() - { - $this->assertSame($this->requestMock, $this->requestFactory->create()); - } -} diff --git a/app/code/Magento/Authorizenet/Test/Unit/Model/Directpost/RequestTest.php b/app/code/Magento/Authorizenet/Test/Unit/Model/Directpost/RequestTest.php deleted file mode 100644 index 94d8f3a0d27a7..0000000000000 --- a/app/code/Magento/Authorizenet/Test/Unit/Model/Directpost/RequestTest.php +++ /dev/null @@ -1,80 +0,0 @@ -dateTimeFactory = $this->getMockBuilder(DateTimeFactory::class) - ->disableOriginalConstructor() - ->getMock(); - $dateTime = new \DateTime('2016-07-05 00:00:00', new \DateTimeZone('UTC')); - $this->dateTimeFactory->method('create') - ->willReturn($dateTime); - - $this->requestModel = new Request([], $this->dateTimeFactory); - } - - /** - * @param string $signatureKey - * @param string $expectedHash - * @dataProvider signRequestDataProvider - */ - public function testSignRequestData(string $signatureKey, string $expectedHash) - { - /** @var \Magento\Authorizenet\Model\Directpost $paymentMethod */ - $paymentMethod = $this->createMock(\Magento\Authorizenet\Model\Directpost::class); - $paymentMethod->method('getConfigData') - ->willReturnMap( - [ - ['test', null, true], - ['login', null, 'login'], - ['trans_key', null, 'trans_key'], - ['signature_key', null, $signatureKey], - ] - ); - - $this->requestModel->setConstantData($paymentMethod); - $this->requestModel->signRequestData(); - $signHash = $this->requestModel->getXFpHash(); - - $this->assertEquals($expectedHash, $signHash); - } - - /** - * @return array - */ - public function signRequestDataProvider() - { - return [ - [ - 'signatureKey' => '3EAFCE5697C1B4B9748385C1FCD29D86F3B9B41C7EED85A3A01DFF65' . - '70C8C29373C2A153355C3313CDF4AF723C0036DBF244A0821713A910024EE85547CEF37F', - 'expectedHash' => '719ED94DF5CF3510CB5531E8115462C8F12CBCC8E917BD809E8D40B4FF06' . - '1E14953554403DD9813CCCE0F31B184EB4DEF558E9C0747505A0C25420372DB00BE1' - ], - [ - 'signatureKey' => '', - 'expectedHash' => '3656211f2c41d1e4c083606f326c0460' - ], - ]; - } -} diff --git a/app/code/Magento/Authorizenet/Test/Unit/Model/Directpost/Response/FactoryTest.php b/app/code/Magento/Authorizenet/Test/Unit/Model/Directpost/Response/FactoryTest.php deleted file mode 100644 index bf59a73c14940..0000000000000 --- a/app/code/Magento/Authorizenet/Test/Unit/Model/Directpost/Response/FactoryTest.php +++ /dev/null @@ -1,49 +0,0 @@ -responseMock = $this->createMock(\Magento\Authorizenet\Model\Directpost\Response::class); - - $this->objectManagerMock = $this->createMock(\Magento\Framework\ObjectManagerInterface::class); - $this->objectManagerMock->expects($this->once()) - ->method('create') - ->with(\Magento\Authorizenet\Model\Directpost\Response::class, []) - ->willReturn($this->responseMock); - - $this->responseFactory = $objectManager->getObject( - \Magento\Authorizenet\Model\Directpost\Response\Factory::class, - ['objectManager' => $this->objectManagerMock] - ); - } - - public function testCreate() - { - $this->assertSame($this->responseMock, $this->responseFactory->create()); - } -} diff --git a/app/code/Magento/Authorizenet/Test/Unit/Model/Directpost/ResponseTest.php b/app/code/Magento/Authorizenet/Test/Unit/Model/Directpost/ResponseTest.php deleted file mode 100644 index ff4aa8b5ee361..0000000000000 --- a/app/code/Magento/Authorizenet/Test/Unit/Model/Directpost/ResponseTest.php +++ /dev/null @@ -1,128 +0,0 @@ -responseModel = $objectManager->getObject( - \Magento\Authorizenet\Model\Directpost\Response::class - ); - } - - /** - * @param $merchantMd5 - * @param $merchantApiLogin - * @param $amount - * @param $transactionId - * @return string - */ - protected function generateHash($merchantMd5, $merchantApiLogin, $amount, $transactionId) - { - return strtoupper(md5($merchantMd5 . $merchantApiLogin . $transactionId . $amount)); - } - - /** - * @param string $storedHash - * @param string $hashKey - * @param string $merchantApiLogin - * @param float|null $amount - * @param string $transactionId - * @param string $hash - * @param bool $expectedValue - * @dataProvider isValidHashDataProvider - */ - public function testIsValidHash( - string $storedHash, - string $hashKey, - string $merchantApiLogin, - $amount, - string $transactionId, - string $hash, - bool $expectedValue - ) { - $this->responseModel->setXAmount($amount); - $this->responseModel->setXTransId($transactionId); - $this->responseModel->setData($hashKey, $hash); - $result = $this->responseModel->isValidHash($storedHash, $merchantApiLogin); - - $this->assertEquals($expectedValue, $result); - } - - /** - * @return array - */ - public function isValidHashDataProvider() - { - $signatureKey = '3EAFCE5697C1B4B9748385C1FCD29D86F3B9B41C7EED85A3A01DFF6570C8C' . - '29373C2A153355C3313CDF4AF723C0036DBF244A0821713A910024EE85547CEF37F'; - $expectedSha2Hash = '368D48E0CD1274BF41C059138DA69985594021A4AD5B4C5526AE88C8F' . - '7C5769B13C5E1E4358900F3E51076FB69D14B0A797904C22E8A11A52AA49CDE5FBB703C'; - return [ - [ - 'merchantMd5' => 'FCD7F001E9274FDEFB14BFF91C799306', - 'hashKey' => 'x_MD5_Hash', - 'merchantApiLogin' => 'Magento', - 'amount' => null, - 'transactionId' => '1', - 'hash' => '1F24A4EC9A169B2B2A072A5F168E16DC', - 'expectedValue' => true - ], - [ - 'merchantMd5' => '8AEF4E508261A287C3E2F544720FCA3A', - 'hashKey' => 'x_MD5_Hash', - 'merchantApiLogin' => 'Magento2', - 'amount' => 100.50, - 'transactionId' => '2', - 'hash' => '1F24A4EC9A169B2B2A072A5F168E16DC', - 'expectedValue' => false - ], - [ - 'signatureKey' => $signatureKey, - 'hashKey' => 'x_SHA2_Hash', - 'merchantApiLogin' => 'Magento2', - 'amount' => 100.50, - 'transactionId' => '2', - 'hash' => $expectedSha2Hash, - 'expectedValue' => true - ] - ]; - } - - /** - * @param int $xResponseCode - * @param bool $expectedValue - * @dataProvider isApprovedDataProvider - */ - public function testIsApproved($xResponseCode, $expectedValue) - { - $this->responseModel->setXResponseCode($xResponseCode); - $this->assertSame($expectedValue, $this->responseModel->isApproved()); - } - - /** - * @return array - */ - public function isApprovedDataProvider() - { - return [ - [Directpost::RESPONSE_CODE_APPROVED, true], - [Directpost::RESPONSE_CODE_DECLINED, false], - [Directpost::RESPONSE_CODE_ERROR, false], - [Directpost::RESPONSE_CODE_HELD, false], - ]; - } -} diff --git a/app/code/Magento/Authorizenet/Test/Unit/Model/Directpost/SessionTest.php b/app/code/Magento/Authorizenet/Test/Unit/Model/Directpost/SessionTest.php deleted file mode 100644 index 35f7a4e15219c..0000000000000 --- a/app/code/Magento/Authorizenet/Test/Unit/Model/Directpost/SessionTest.php +++ /dev/null @@ -1,58 +0,0 @@ -storageMock = $this - ->getMockBuilder(\Magento\Framework\Session\StorageInterface::class) - ->setMethods(['setQuoteId']) - ->getMockForAbstractClass(); - - $this->objectManager = new ObjectManager($this); - $this->session = $this->objectManager->getObject( - \Magento\Authorizenet\Model\Directpost\Session::class, - [ - 'storage' => $this->storageMock, - ] - ); - } - - public function testSetQuoteId() - { - $quoteId = 1; - - $this->storageMock->expects($this->once()) - ->method('setQuoteId') - ->with($quoteId); - - $this->assertInstanceOf( - \Magento\Authorizenet\Model\Directpost\Session::class, - $this->session->setQuoteId($quoteId) - ); - } -} diff --git a/app/code/Magento/Authorizenet/Test/Unit/Model/DirectpostTest.php b/app/code/Magento/Authorizenet/Test/Unit/Model/DirectpostTest.php deleted file mode 100644 index a1547a0563461..0000000000000 --- a/app/code/Magento/Authorizenet/Test/Unit/Model/DirectpostTest.php +++ /dev/null @@ -1,885 +0,0 @@ -initPaymentMock(); - $this->initResponseFactoryMock(); - $this->initHttpClientMock(); - - $this->scopeConfigMock = $this->getMockBuilder(ScopeConfigInterface::class)->getMock(); - $this->dataHelperMock = $this->getMockBuilder(HelperData::class)->disableOriginalConstructor()->getMock(); - $this->transactionRepositoryMock = $this->getMockBuilder(TransactionRepository::class) - ->disableOriginalConstructor() - ->setMethods(['getByTransactionId']) - ->getMock(); - $this->transactionServiceMock = $this->getMockBuilder(TransactionService::class) - ->disableOriginalConstructor() - ->setMethods(['getTransactionDetails']) - ->getMock(); - $this->paymentFailures = $this->getMockBuilder(PaymentFailuresInterface::class) - ->disableOriginalConstructor() - ->getMock(); - $this->requestFactory = $this->getMockBuilder(Factory::class) - ->disableOriginalConstructor() - ->setMethods(['create']) - ->getMock(); - $this->httpClientFactoryMock = $this->getMockBuilder(ZendClientFactory::class) - ->disableOriginalConstructor() - ->setMethods(['create']) - ->getMock(); - - $helper = new ObjectManagerHelper($this); - $this->directpost = $helper->getObject( - Directpost::class, - [ - 'scopeConfig' => $this->scopeConfigMock, - 'dataHelper' => $this->dataHelperMock, - 'requestFactory' => $this->requestFactory, - 'responseFactory' => $this->responseFactoryMock, - 'transactionRepository' => $this->transactionRepositoryMock, - 'transactionService' => $this->transactionServiceMock, - 'httpClientFactory' => $this->httpClientFactoryMock, - 'paymentFailures' => $this->paymentFailures, - ] - ); - } - - /** - * Create mock for response factory - * - * @return void - */ - private function initResponseFactoryMock() - { - $this->responseFactoryMock = $this->getMockBuilder(ResponseFactory::class) - ->disableOriginalConstructor() - ->getMock(); - $this->responseMock = $this->getMockBuilder(Response::class) - ->setMethods( - [ - 'isValidHash', - 'getXTransId', - 'getXResponseCode', - 'getXResponseReasonCode', - 'getXResponseReasonText', - 'getXAmount', - 'setXResponseCode', - 'setXResponseReasonCode', - 'setXAvsCode', - 'setXResponseReasonText', - 'setXTransId', - 'setXInvoiceNum', - 'setXAmount', - 'setXMethod', - 'setXType', - 'setData', - 'getData', - 'setXAccountNumber', - '__wakeup' - ] - ) - ->disableOriginalConstructor() - ->getMock(); - - $this->responseFactoryMock->expects($this->any())->method('create')->willReturn($this->responseMock); - } - - /** - * Create mock for payment - * - * @return void - */ - private function initPaymentMock() - { - $this->paymentMock = $this->getMockBuilder(Payment::class) - ->disableOriginalConstructor() - ->setMethods( - [ - 'getOrder', - 'setAmount', - 'setAnetTransType', - 'setXTransId', - 'getId', - 'setAdditionalInformation', - 'getAdditionalInformation', - 'setIsTransactionDenied', - 'setIsTransactionClosed', - 'decrypt', - 'getCcLast4', - 'getParentTransactionId', - 'getPoNumber' - ] - ) - ->getMock(); - } - - /** - * Create a mock for http client - * - * @return void - */ - private function initHttpClientMock() - { - $this->httpClientMock = $this->getMockBuilder(ZendClient::class) - ->disableOriginalConstructor() - ->setMethods(['request', 'getBody', '__wakeup']) - ->getMock(); - } - - public function testGetConfigInterface() - { - $this->assertInstanceOf(ConfigInterface::class, $this->directpost->getConfigInterface()); - } - - public function testGetConfigValue() - { - $field = 'some_field'; - $returnValue = 'expected'; - $this->scopeConfigMock->expects($this->once()) - ->method('getValue') - ->with('payment/authorizenet_directpost/' . $field) - ->willReturn($returnValue); - $this->assertEquals($returnValue, $this->directpost->getValue($field)); - } - - public function testSetDataHelper() - { - $storeId = 'store-id'; - $expectedResult = 'relay-url'; - - $helperDataMock = $this->getMockBuilder(Data::class) - ->disableOriginalConstructor() - ->getMock(); - - $helperDataMock->expects($this->once()) - ->method('getRelayUrl') - ->with($storeId) - ->willReturn($expectedResult); - - $this->directpost->setDataHelper($helperDataMock); - $this->assertEquals($expectedResult, $this->directpost->getRelayUrl($storeId)); - } - - public function testAuthorize() - { - $paymentAction = 'some_action'; - - $this->scopeConfigMock->expects($this->once()) - ->method('getValue') - ->with('payment/authorizenet_directpost/payment_action', 'store', null) - ->willReturn($paymentAction); - $this->paymentMock->expects($this->once()) - ->method('setAdditionalInformation') - ->with('payment_type', $paymentAction); - - $this->directpost->authorize($this->paymentMock, 10); - } - - /** - * @dataProvider dataProviderCaptureWithInvalidAmount - * @expectedExceptionMessage Invalid amount for capture. - * @expectedException \Magento\Framework\Exception\LocalizedException - * - * @param int $invalidAmount - */ - public function testCaptureWithInvalidAmount($invalidAmount) - { - $this->directpost->capture($this->paymentMock, $invalidAmount); - } - - /** - * @return array - */ - public function dataProviderCaptureWithInvalidAmount() - { - return [ - [0], - [0.000], - [-1.000], - [-1], - [null], - ]; - } - - /** - * Test capture has parent transaction id. - * - * @expectedException \Magento\Framework\Exception\LocalizedException - */ - public function testCaptureHasParentTransactionId() - { - $amount = 10; - - $this->paymentMock->expects($this->once())->method('setAmount')->with($amount); - $this->paymentMock->expects($this->exactly(2))->method('getParentTransactionId')->willReturn(1); - $this->paymentMock->expects($this->once())->method('setAnetTransType')->willReturn('PRIOR_AUTH_CAPTURE'); - - $this->paymentMock->expects($this->once())->method('getId')->willReturn(1); - $orderMock = $this->getOrderMock(); - $orderMock->expects($this->once())->method('getId')->willReturn(1); - $this->paymentMock->expects($this->once())->method('getOrder')->willReturn($orderMock); - - $transactionMock = $this->getMockBuilder(Transaction::class)->disableOriginalConstructor()->getMock(); - $this->transactionRepositoryMock->expects($this->once()) - ->method('getByTransactionId') - ->with(1, 1, 1) - ->willReturn($transactionMock); - - $this->paymentMock->expects($this->once())->method('setXTransId'); - $this->responseMock->expects($this->once())->method('getData')->willReturn([1]); - - $this->directpost->capture($this->paymentMock, 10); - } - - /** - * @@expectedException \Magento\Framework\Exception\LocalizedException - */ - public function testCaptureWithoutParentTransactionId() - { - $amount = 10; - - $this->paymentMock->expects($this->once())->method('setAmount')->with($amount); - $this->paymentMock->expects($this->once())->method('getParentTransactionId')->willReturn(null); - $this->responseMock->expects($this->once())->method('getData')->willReturn([1]); - - $this->directpost->capture($this->paymentMock, 10); - } - - public function testCaptureWithoutParentTransactionIdWithoutData() - { - $amount = 10; - - $this->paymentMock->expects($this->once())->method('setAmount')->with($amount); - $this->paymentMock->expects($this->exactly(2))->method('getParentTransactionId')->willReturn(null); - $this->responseMock->expects($this->once())->method('getData')->willReturn([]); - - $this->paymentMock->expects($this->once()) - ->method('setIsTransactionClosed') - ->with(0) - ->willReturnSelf(); - - $this->httpClientFactoryMock->expects($this->once())->method('create')->willReturn($this->httpClientMock); - $this->httpClientMock->expects($this->once())->method('request')->willReturnSelf(); - - $this->buildRequestTest(); - $this->postRequestTest(); - - $this->directpost->capture($this->paymentMock, 10); - } - - private function buildRequestTest() - { - $orderMock = $this->getOrderMock(); - $orderMock->expects($this->once())->method('getStoreId')->willReturn(1); - $orderMock->expects($this->exactly(2))->method('getIncrementId')->willReturn(self::INVOICE_NUM); - $this->paymentMock->expects($this->once())->method('getOrder')->willReturn($orderMock); - - $this->addRequestMockToRequestFactoryMock(); - } - - private function postRequestTest() - { - $this->httpClientFactoryMock->expects($this->once())->method('create')->willReturn($this->httpClientMock); - $this->httpClientMock->expects($this->once())->method('request')->willReturnSelf(); - $this->responseMock->expects($this->once())->method('setXResponseCode')->willReturnSelf(); - $this->responseMock->expects($this->once())->method('setXResponseReasonCode')->willReturnSelf(); - $this->responseMock->expects($this->once())->method('setXResponseReasonText')->willReturnSelf(); - $this->responseMock->expects($this->once())->method('setXAvsCode')->willReturnSelf(); - $this->responseMock->expects($this->once())->method('setXTransId')->willReturnSelf(); - $this->responseMock->expects($this->once())->method('setXInvoiceNum')->willReturnSelf(); - $this->responseMock->expects($this->once())->method('setXAmount')->willReturnSelf(); - $this->responseMock->expects($this->once())->method('setXMethod')->willReturnSelf(); - $this->responseMock->expects($this->once())->method('setXType')->willReturnSelf(); - $this->responseMock->expects($this->once())->method('setData')->willReturnSelf(); - - $response = $this->getRefundResponseBody( - Directpost::RESPONSE_CODE_APPROVED, - Directpost::RESPONSE_REASON_CODE_APPROVED, - 'Successful' - ); - $this->httpClientMock->expects($this->once())->method('getBody')->willReturn($response); - $this->responseMock->expects($this->once()) - ->method('getXResponseCode') - ->willReturn(Directpost::RESPONSE_CODE_APPROVED); - $this->responseMock->expects($this->once()) - ->method('getXResponseReasonCode') - ->willReturn(Directpost::RESPONSE_REASON_CODE_APPROVED); - $this->dataHelperMock->expects($this->never())->method('wrapGatewayError'); - } - - public function testGetCgiUrl() - { - $url = 'cgi/url'; - - $this->scopeConfigMock->expects($this->once()) - ->method('getValue') - ->with('payment/authorizenet_directpost/cgi_url', 'store', null) - ->willReturn($url); - - $this->assertEquals($url, $this->directpost->getCgiUrl()); - } - - public function testGetCgiUrlWithEmptyConfigValue() - { - $this->scopeConfigMock->expects($this->once()) - ->method('getValue') - ->with('payment/authorizenet_directpost/cgi_url', 'store', null) - ->willReturn(null); - - $this->assertEquals(Directpost::CGI_URL, $this->directpost->getCgiUrl()); - } - - public function testGetRelayUrl() - { - $storeId = 100; - $url = 'relay/url'; - $this->directpost->setData('store', $storeId); - - $this->dataHelperMock->expects($this->exactly(2)) - ->method('getRelayUrl') - ->with($storeId) - ->willReturn($url); - - $this->assertEquals($url, $this->directpost->getRelayUrl()); - $this->assertEquals($url, $this->directpost->getRelayUrl($storeId)); - } - - public function testGetResponse() - { - $this->assertSame($this->responseMock, $this->directpost->getResponse()); - } - - public function testSetResponseData() - { - $data = [ - 'key' => 'value' - ]; - - $this->responseMock->expects($this->once()) - ->method('setData') - ->with($data) - ->willReturnSelf(); - - $this->assertSame($this->directpost, $this->directpost->setResponseData($data)); - } - - public function testValidateResponseSuccess() - { - $this->prepareTestValidateResponse('some_md5', 'login', true); - $this->assertEquals(true, $this->directpost->validateResponse()); - } - - /** - * @expectedException \Magento\Framework\Exception\LocalizedException - */ - public function testValidateResponseFailure() - { - $this->prepareTestValidateResponse('some_md5', 'login', false); - $this->directpost->validateResponse(); - } - - /** - * @param string $transMd5 - * @param string $login - * @param bool $isValidHash - */ - protected function prepareTestValidateResponse($transMd5, $login, $isValidHash) - { - $this->scopeConfigMock->expects($this->exactly(2)) - ->method('getValue') - ->willReturnMap( - [ - ['payment/authorizenet_directpost/trans_md5', 'store', null, $transMd5], - ['payment/authorizenet_directpost/login', 'store', null, $login] - ] - ); - $this->responseMock->expects($this->exactly(1)) - ->method('isValidHash') - ->with($transMd5, $login) - ->willReturn($isValidHash); - } - - public function testCheckTransIdSuccess() - { - $this->responseMock->expects($this->once()) - ->method('getXTransId') - ->willReturn('111'); - - $this->assertEquals(true, $this->directpost->checkTransId()); - } - - /** - * @expectedException \Magento\Framework\Exception\LocalizedException - */ - public function testCheckTransIdFailure() - { - $this->responseMock->expects($this->once()) - ->method('getXTransId') - ->willReturn(null); - - $this->directpost->checkTransId(); - } - - /** - * @param bool $responseCode - * - * @dataProvider checkResponseCodeSuccessDataProvider - */ - public function testCheckResponseCodeSuccess($responseCode) - { - $this->responseMock->expects($this->once()) - ->method('getXResponseCode') - ->willReturn($responseCode); - - $this->assertEquals(true, $this->directpost->checkResponseCode()); - } - - /** - * @return array - */ - public function checkResponseCodeSuccessDataProvider() - { - return [ - ['responseCode' => Directpost::RESPONSE_CODE_APPROVED], - ['responseCode' => Directpost::RESPONSE_CODE_HELD] - ]; - } - - /** - * Checks response failures behaviour. - * - * @return void - * @expectedException \Magento\Framework\Exception\LocalizedException - */ - public function testCheckResponseCodeFailureDefault() - { - $responseCode = 999999; - $this->responseMock->expects($this->once())->method('getXResponseCode')->willReturn($responseCode); - - $this->directpost->checkResponseCode(); - } - - /** - * Checks response failures behaviour. - * - * @param int $responseCode - * @param int $failuresHandlerCalls - * @return void - * - * @expectedException \Magento\Framework\Exception\LocalizedException - * @dataProvider checkResponseCodeFailureDataProvider - */ - public function testCheckResponseCodeFailureDeclinedOrError(int $responseCode, int $failuresHandlerCalls): void - { - $reasonText = 'reason text'; - - $this->responseMock->expects($this->once()) - ->method('getXResponseCode') - ->willReturn($responseCode); - $this->responseMock->expects($this->once())->method('getXResponseReasonText')->willReturn($reasonText); - $this->dataHelperMock->expects($this->once()) - ->method('wrapGatewayError') - ->with($reasonText) - ->willReturn(__('Gateway error: %1', $reasonText)); - - $this->paymentFailures->expects($this->exactly($failuresHandlerCalls))->method('handle')->with(1); - $orderMock = $this->getOrderMock($failuresHandlerCalls); - - $orderMock->expects($this->exactly($failuresHandlerCalls))->method('getQuoteId')->willReturn(1); - $reflection = new ReflectionClass($this->directpost); - $order = $reflection->getProperty('order'); - $order->setAccessible(true); - $order->setValue($this->directpost, $orderMock); - - $this->directpost->checkResponseCode(); - } - - /** - * @return array - */ - public function checkResponseCodeFailureDataProvider(): array - { - return [ - ['responseCode' => Directpost::RESPONSE_CODE_DECLINED, 1], - ['responseCode' => Directpost::RESPONSE_CODE_ERROR, 1], - ]; - } - - /** - * @param bool $isInitializeNeeded - * - * @dataProvider setIsInitializeNeededDataProvider - */ - public function testSetIsInitializeNeeded($isInitializeNeeded) - { - $this->directpost->setIsInitializeNeeded($isInitializeNeeded); - $this->assertEquals($isInitializeNeeded, $this->directpost->isInitializeNeeded()); - } - - /** - * @return array - */ - public function setIsInitializeNeededDataProvider() - { - return [ - ['isInitializationNeeded' => true], - ['isInitializationNeeded' => false] - ]; - } - - /** - * @param bool $isGatewayActionsLocked - * @param bool $canCapture - * - * @dataProvider canCaptureDataProvider - */ - public function testCanCapture($isGatewayActionsLocked, $canCapture) - { - $this->directpost->setData('info_instance', $this->paymentMock); - - $this->paymentMock->expects($this->once()) - ->method('getAdditionalInformation') - ->with(Directpost::GATEWAY_ACTIONS_LOCKED_STATE_KEY) - ->willReturn($isGatewayActionsLocked); - - $this->assertEquals($canCapture, $this->directpost->canCapture()); - } - - /** - * @return array - */ - public function canCaptureDataProvider() - { - return [ - ['isGatewayActionsLocked' => false, 'canCapture' => true], - ['isGatewayActionsLocked' => true, 'canCapture' => false] - ]; - } - - /** - * @covers \Magento\Authorizenet\Model\Directpost::fetchTransactionInfo - * - * @param $transactionId - * @param $resultStatus - * @param $responseStatus - * @param $responseCode - * @return void - * - * @dataProvider dataProviderTransaction - */ - public function testFetchVoidedTransactionInfo($transactionId, $resultStatus, $responseStatus, $responseCode) - { - $paymentId = 36; - $orderId = 36; - - $this->paymentMock->expects($this->once())->method('getId')->willReturn($paymentId); - - $orderMock = $this->getOrderMock(); - $orderMock->expects($this->once())->method('getId')->willReturn($orderId); - $this->paymentMock->expects($this->once())->method('getOrder')->willReturn($orderMock); - $transactionMock = $this->getMockBuilder(Transaction::class)->disableOriginalConstructor()->getMock(); - $this->transactionRepositoryMock->expects($this->once()) - ->method('getByTransactionId') - ->with($transactionId, $paymentId, $orderId) - ->willReturn($transactionMock); - $document = $this->getTransactionXmlDocument( - $transactionId, - TransactionService::PAYMENT_UPDATE_STATUS_CODE_SUCCESS, - $resultStatus, - $responseStatus, - $responseCode - ); - $this->transactionServiceMock->expects($this->once()) - ->method('getTransactionDetails') - ->with($this->directpost, $transactionId) - ->willReturn($document); - - // transaction should be closed - $this->paymentMock->expects($this->once())->method('setIsTransactionDenied')->with(true); - $this->paymentMock->expects($this->once())->method('setIsTransactionClosed')->with(true); - $transactionMock->expects($this->once())->method('close'); - - $this->directpost->fetchTransactionInfo($this->paymentMock, $transactionId); - } - - /** - * @covers \Magento\Authorizenet\Model\Directpost::refund() - * @return void - */ - public function testSuccessRefund() - { - $card = 1111; - - $this->paymentMock->expects($this->exactly(1))->method('getCcLast4')->willReturn($card); - $this->paymentMock->expects($this->once())->method('decrypt')->willReturn($card); - $this->paymentMock->expects($this->exactly(3)) - ->method('getParentTransactionId') - ->willReturn(self::TRANSACTION_ID . '-capture'); - $this->paymentMock->expects($this->once())->method('getPoNumber')->willReturn(self::INVOICE_NUM); - $this->paymentMock->expects($this->once()) - ->method('setIsTransactionClosed') - ->with(true) - ->willReturnSelf(); - - $this->addRequestMockToRequestFactoryMock(); - - $orderMock = $this->getOrderMock(); - - $orderMock->expects($this->once())->method('getId')->willReturn(1); - $orderMock->expects($this->exactly(2))->method('getIncrementId')->willReturn(self::INVOICE_NUM); - $orderMock->expects($this->once())->method('getStoreId')->willReturn(1); - - $this->paymentMock->expects($this->exactly(2))->method('getOrder')->willReturn($orderMock); - - $transactionMock = $this->getMockBuilder(Transaction::class) - ->disableOriginalConstructor() - ->setMethods(['getAdditionalInformation']) - ->getMock(); - $transactionMock->expects($this->once()) - ->method('getAdditionalInformation') - ->with(Directpost::REAL_TRANSACTION_ID_KEY) - ->willReturn(self::TRANSACTION_ID); - - $this->transactionRepositoryMock->expects($this->once()) - ->method('getByTransactionId') - ->willReturn($transactionMock); - - $this->postRequestTest(); - - $this->directpost->refund($this->paymentMock, self::TOTAL_AMOUNT); - } - - /** - * Get data for tests - * @return array - */ - public function dataProviderTransaction() - { - return [ - [ - 'transactionId' => '9941997799', - 'resultStatus' => 'Successful.', - 'responseStatus' => 'voided', - 'responseCode' => 1 - ] - ]; - } - - /** - * Get transaction data - * @param $transactionId - * @param $resultCode - * @param $resultStatus - * @param $responseStatus - * @param $responseCode - * @return Element - */ - private function getTransactionXmlDocument( - $transactionId, - $resultCode, - $resultStatus, - $responseStatus, - $responseCode - ) { - $body = sprintf( - ' - - - %s - - I00001 - %s - - - - %s - authOnlyTransaction - %s - %s - %s - - ', - $resultCode, - $resultStatus, - $transactionId, - $responseStatus, - $responseCode, - $responseCode - ); - libxml_use_internal_errors(true); - $document = new Element($body); - libxml_use_internal_errors(false); - return $document; - } - - /** - * Get mock for authorize.net request factory - */ - private function addRequestMockToRequestFactoryMock() - { - $request = $this->getMockBuilder(Request::class) - ->disableOriginalConstructor() - ->setMethods(['__wakeup']) - ->getMock(); - $this->requestFactory->expects($this->once()) - ->method('create') - ->willReturn($request); - } - - /** - * Get mock for order - * @return PHPUnit_Framework_MockObject_MockObject - */ - private function getOrderMock() - { - return $this->getMockBuilder(Order::class) - ->disableOriginalConstructor() - ->setMethods( - [ - 'getId', - 'getQuoteId', - 'getIncrementId', - 'getStoreId', - 'getBillingAddress', - 'getShippingAddress', - 'getBaseCurrencyCode', - 'getBaseTaxAmount', - '__wakeup' - ] - ) - ->getMock(); - } - - /** - * Get mocked response for refund transaction - * @param $code - * @param $reasonCode - * @param $reasonText - * @return string - */ - private function getRefundResponseBody($code, $reasonCode, $reasonText) - { - $result = array_fill(0, 50, ''); - $result[0] = $code; // XResponseCode - $result[2] = $reasonCode; // XResponseReasonCode - $result[3] = $reasonText; // XResponseReasonText - $result[6] = self::TRANSACTION_ID; // XTransId - $result[7] = self::INVOICE_NUM; // XInvoiceNum - $result[9] = self::TOTAL_AMOUNT; // XAmount - $result[10] = Directpost::REQUEST_METHOD_CC; // XMethod - $result[11] = Directpost::REQUEST_TYPE_CREDIT; // XType - // @codingStandardsIgnoreStart - $result[37] = md5(self::TRANSACTION_ID); // x_MD5_Hash - // @codingStandardsIgnoreEnd - $result[50] = '48329483921'; // setXAccountNumber - return implode(Directpost::RESPONSE_DELIM_CHAR, $result); - } -} diff --git a/app/code/Magento/Authorizenet/Test/Unit/Model/Request/FactoryTest.php b/app/code/Magento/Authorizenet/Test/Unit/Model/Request/FactoryTest.php deleted file mode 100644 index 4ac98238f9ed1..0000000000000 --- a/app/code/Magento/Authorizenet/Test/Unit/Model/Request/FactoryTest.php +++ /dev/null @@ -1,49 +0,0 @@ -requestMock = $this->createMock(\Magento\Authorizenet\Model\Request::class); - - $this->objectManagerMock = $this->createMock(\Magento\Framework\ObjectManagerInterface::class); - $this->objectManagerMock->expects($this->once()) - ->method('create') - ->with(\Magento\Authorizenet\Model\Request::class, []) - ->willReturn($this->requestMock); - - $this->requestFactory = $objectManager->getObject( - \Magento\Authorizenet\Model\Request\Factory::class, - ['objectManager' => $this->objectManagerMock] - ); - } - - public function testCreate() - { - $this->assertSame($this->requestMock, $this->requestFactory->create()); - } -} diff --git a/app/code/Magento/Authorizenet/Test/Unit/Model/Response/FactoryTest.php b/app/code/Magento/Authorizenet/Test/Unit/Model/Response/FactoryTest.php deleted file mode 100644 index ddc4f28de8b81..0000000000000 --- a/app/code/Magento/Authorizenet/Test/Unit/Model/Response/FactoryTest.php +++ /dev/null @@ -1,49 +0,0 @@ -responseMock = $this->createMock(\Magento\Authorizenet\Model\Response::class); - - $this->objectManagerMock = $this->createMock(\Magento\Framework\ObjectManagerInterface::class); - $this->objectManagerMock->expects($this->once()) - ->method('create') - ->with(\Magento\Authorizenet\Model\Response::class, []) - ->willReturn($this->responseMock); - - $this->responseFactory = $objectManager->getObject( - \Magento\Authorizenet\Model\Response\Factory::class, - ['objectManager' => $this->objectManagerMock] - ); - } - - public function testCreate() - { - $this->assertSame($this->responseMock, $this->responseFactory->create()); - } -} diff --git a/app/code/Magento/Authorizenet/Test/Unit/Model/TransactionServiceTest.php b/app/code/Magento/Authorizenet/Test/Unit/Model/TransactionServiceTest.php deleted file mode 100644 index 092d633648482..0000000000000 --- a/app/code/Magento/Authorizenet/Test/Unit/Model/TransactionServiceTest.php +++ /dev/null @@ -1,167 +0,0 @@ -getHttpClientFactoryMock(); - - $this->authorizenetMock = $this->getMockBuilder(\Magento\Authorizenet\Model\Directpost::class) - ->disableOriginalConstructor() - ->getMock(); - - $this->authorizenetMock->method('getConfigData') - ->willReturnMap([ - ['login', 'test login'], - ['trans_key', 'test key'], - ['cgi_url_td', 'https://apitest.authorize.net/xml/v1/request.api'] - ]); - - $objectManagerHelper = new ObjectManager($this); - $xmlSecurity = $objectManagerHelper->getObject(\Magento\Framework\Xml\Security::class); - $this->transactionService = $objectManagerHelper->getObject( - \Magento\Authorizenet\Model\TransactionService::class, - [ - 'xmlSecurityHelper' => $xmlSecurity, - 'httpClientFactory' => $httpClientFactoryMock - ] - ); - } - - /** - * @covers \Magento\Authorizenet\Model\TransactionService::loadTransactionDetails - * @param $transactionId - * @param $resultStatus - * @param $responseStatus - * @param $responseCode - * @return void - * - * @dataProvider dataProviderTransaction - */ - public function testLoadVoidedTransactionDetails($transactionId, $resultStatus, $responseStatus, $responseCode) - { - $document = $this->getResponseBody( - $transactionId, - TransactionService::PAYMENT_UPDATE_STATUS_CODE_SUCCESS, - $resultStatus, - $responseStatus, - $responseCode - ); - $this->httpClientMock->expects(static::once()) - ->method('getBody') - ->willReturn($document); - - $result = $this->transactionService->getTransactionDetails($this->authorizenetMock, $transactionId); - - static::assertEquals($responseCode, (string)$result->transaction->responseCode); - static::assertEquals($responseCode, (string)$result->transaction->responseReasonCode); - static::assertEquals($responseStatus, (string)$result->transaction->transactionStatus); - } - - /** - * Get data for tests - * @return array - */ - public function dataProviderTransaction() - { - return [ - [ - 'transactionId' => '9941997799', - 'resultStatus' => 'Successful.', - 'responseStatus' => 'voided', - 'responseCode' => 1 - ] - ]; - } - - /** - * Create and return mock for http client factory - * @return \PHPUnit_Framework_MockObject_MockObject - */ - private function getHttpClientFactoryMock() - { - $this->httpClientMock = $this->getMockBuilder(\Magento\Framework\HTTP\ZendClient::class) - ->disableOriginalConstructor() - ->setMethods(['request', 'getBody', '__wakeup']) - ->getMock(); - - $this->httpClientMock->expects(static::once()) - ->method('request') - ->willReturnSelf(); - - $httpClientFactoryMock = $this->getMockBuilder(\Magento\Framework\HTTP\ZendClientFactory::class) - ->disableOriginalConstructor() - ->setMethods(['create']) - ->getMock(); - - $httpClientFactoryMock->expects(static::once()) - ->method('create') - ->willReturn($this->httpClientMock); - return $httpClientFactoryMock; - } - - /** - * Get body for xml request - * @param string $transactionId - * @param int $resultCode - * @param string $resultStatus - * @param string $responseStatus - * @param string $responseCode - * @return string - */ - private function getResponseBody($transactionId, $resultCode, $resultStatus, $responseStatus, $responseCode) - { - return sprintf( - ' - - - %s - - I00001 - %s - - - - %s - authOnlyTransaction - %s - %s - %s - - ', - $resultCode, - $resultStatus, - $transactionId, - $responseStatus, - $responseCode, - $responseCode - ); - } -} diff --git a/app/code/Magento/Authorizenet/Test/Unit/Observer/AddFieldsToResponseObserverTest.php b/app/code/Magento/Authorizenet/Test/Unit/Observer/AddFieldsToResponseObserverTest.php deleted file mode 100644 index d59d5eeaa5cae..0000000000000 --- a/app/code/Magento/Authorizenet/Test/Unit/Observer/AddFieldsToResponseObserverTest.php +++ /dev/null @@ -1,237 +0,0 @@ -coreRegistryMock = $this->getMockBuilder(\Magento\Framework\Registry::class) - ->disableOriginalConstructor() - ->getMock(); - $this->paymentMock = $this->getMockBuilder(\Magento\Authorizenet\Model\Directpost::class) - ->disableOriginalConstructor() - ->getMock(); - $this->sessionMock = $this->getMockBuilder(\Magento\Authorizenet\Model\Directpost\Session::class) - ->disableOriginalConstructor() - ->setMethods(['setLastOrderIncrementId', 'addCheckoutOrderIncrementId']) - ->getMock(); - $this->storeManagerMock = $this->getMockBuilder(\Magento\Store\Model\StoreManagerInterface::class) - ->disableOriginalConstructor() - ->getMockForAbstractClass(); - $this->actionMock = $this->getMockBuilder(\Magento\Checkout\Controller\Onepage\SaveOrder::class) - ->disableOriginalConstructor() - ->getMock(); - $this->resultMock = $this->getMockBuilder(\Magento\Framework\DataObject::class) - ->disableOriginalConstructor() - ->getMock(); - - $this->addFieldsToResponseObserver = $helper->getObject( - \Magento\Authorizenet\Observer\AddFieldsToResponseObserver::class, - [ - 'coreRegistry' => $this->coreRegistryMock, - 'payment' => $this->paymentMock, - 'session' => $this->sessionMock, - 'storeManager' => $this->storeManagerMock, - ] - ); - } - - /** - * Test for addFieldsToResponse method - * - * @return void - */ - public function testAddFieldsToResponseSuccess() - { - $testData = $this->getAddFieldsToResponseSuccessTestData(); - - $observerMock = $this->getMockBuilder(\Magento\Framework\Event\Observer::class) - ->disableOriginalConstructor() - ->getMock(); - $orderMock = $this->getMockBuilder(\Magento\Sales\Model\Order::class) - ->disableOriginalConstructor() - ->getMock(); - $orderPaymentMock = $this->getMockBuilder(\Magento\Sales\Model\Order\Payment::class) - ->disableOriginalConstructor() - ->getMock(); - $instanceMock = $this->getMockBuilder(\Magento\Authorizenet\Model\Directpost::class) - ->disableOriginalConstructor() - ->getMock(); - $requestToAuthorizenetMock = $this->getMockBuilder(\Magento\Authorizenet\Model\Directpost\Request::class) - ->disableOriginalConstructor() - ->setMethods(['setControllerActionName', 'setIsSecure', 'getData']) - ->getMock(); - $requestMock = $this->getMockBuilder(\Magento\Framework\App\RequestInterface::class) - ->disableOriginalConstructor() - ->setMethods(['getControllerName']) - ->getMockForAbstractClass(); - $storeMock = $this->getMockBuilder(\Magento\Store\Model\Store::class) - ->disableOriginalConstructor() - ->getMock(); - - $this->coreRegistryMock->expects($this->once()) - ->method('registry') - ->with('directpost_order') - ->willReturn($orderMock); - $orderMock->expects($this->once()) - ->method('getId') - ->willReturn($testData['order.getId']); - $orderMock->expects($this->once()) - ->method('getPayment') - ->willReturn($orderPaymentMock); - $orderPaymentMock->expects($this->once()) - ->method('getMethod') - ->willReturn($testData['orderPayment.getMethod']); - $this->paymentMock->expects($this->exactly(2)) - ->method('getCode') - ->willReturn($testData['payment.getCode']); - $observerMock->expects($this->atLeastOnce()) - ->method('getData') - ->willReturnMap($testData['observer.getData']); - $this->resultMock->expects($this->once()) - ->method('getData') - ->willReturn($testData['result.getData']); - $orderMock->expects($this->atLeastOnce()) - ->method('getIncrementId') - ->willReturn($testData['order.getIncrementId']); - $this->sessionMock->expects($this->once()) - ->method('addCheckoutOrderIncrementId') - ->with($testData['session.addCheckoutOrderIncrementId']); - $this->sessionMock->expects($this->once()) - ->method('setLastOrderIncrementId') - ->with($testData['session.setLastOrderIncrementId']); - $orderPaymentMock->expects($this->once()) - ->method('getMethodInstance') - ->willReturn($instanceMock); - $instanceMock->expects($this->once()) - ->method('generateRequestFromOrder') - ->with($orderMock) - ->willReturn($requestToAuthorizenetMock); - $this->actionMock->expects($this->once()) - ->method('getRequest') - ->willReturn($requestMock); - $requestMock->expects($this->once()) - ->method('getControllerName') - ->willReturn($testData['request.getControllerName']); - $requestToAuthorizenetMock->expects($this->once()) - ->method('setControllerActionName') - ->with($testData['requestToAuthorizenet.setControllerActionName']); - $this->storeManagerMock->expects($this->once()) - ->method('getStore') - ->willReturn($storeMock); - $storeMock->expects($this->once()) - ->method('isCurrentlySecure') - ->willReturn($testData['store.isCurrentlySecure']); - $requestToAuthorizenetMock->expects($this->once()) - ->method('setIsSecure') - ->with($testData['requestToAuthorizenet.setIsSecure']); - $requestToAuthorizenetMock->expects($this->once()) - ->method('getData') - ->willReturn($testData['requestToAuthorizenet.getData']); - $this->resultMock->expects($this->once()) - ->method('setData') - ->with($testData['result.setData']); - - $this->addFieldsToResponseObserver->execute($observerMock); - } - - /** - * Get data for test testAddFieldsToResponseSuccess - * - * @return array - */ - protected function getAddFieldsToResponseSuccessTestData() - { - $requestFields = [ - 'field-1' => 'field-value-1', - 'field-2' => 'field-value-2', - 'field-3' => 'field-value-3', - ]; - $secure = 'test-currently-secure'; - $controllerName = 'test-controller-name'; - $incrementId = '0000000001'; - $paymentCode = 'test-payment-code'; - - return [ - 'order.getId' => 77, - 'orderPayment.getMethod' => $paymentCode, - 'payment.getCode' => $paymentCode, - 'observer.getData' => [ - ['action', null, $this->actionMock], - ['result', null, $this->resultMock], - ], - 'result.getData' => [ - 'error' => false - ], - 'order.getIncrementId' => $incrementId, - 'session.addCheckoutOrderIncrementId' => $incrementId, - 'session.setLastOrderIncrementId' => $incrementId, - 'request.getControllerName' => $controllerName, - 'requestToAuthorizenet.setControllerActionName' => $controllerName, - 'store.isCurrentlySecure' => $secure, - 'requestToAuthorizenet.setIsSecure' => $secure, - 'requestToAuthorizenet.getData' => $requestFields, - 'result.setData' => [ - 'error' => false, - 'test-payment-code' => [ - 'fields' => $requestFields - ] - ] - ]; - } -} diff --git a/app/code/Magento/Authorizenet/composer.json b/app/code/Magento/Authorizenet/composer.json deleted file mode 100644 index 9dcbc7ec7dfb3..0000000000000 --- a/app/code/Magento/Authorizenet/composer.json +++ /dev/null @@ -1,34 +0,0 @@ -{ - "name": "magento/module-authorizenet", - "description": "N/A", - "config": { - "sort-packages": true - }, - "require": { - "php": "~7.1.3||~7.2.0||~7.3.0", - "magento/framework": "*", - "magento/module-backend": "*", - "magento/module-catalog": "*", - "magento/module-checkout": "*", - "magento/module-payment": "*", - "magento/module-quote": "*", - "magento/module-sales": "*", - "magento/module-store": "*" - }, - "suggest": { - "magento/module-config": "*" - }, - "type": "magento2-module", - "license": [ - "OSL-3.0", - "AFL-3.0" - ], - "autoload": { - "files": [ - "registration.php" - ], - "psr-4": { - "Magento\\Authorizenet\\": "" - } - } -} diff --git a/app/code/Magento/Authorizenet/etc/adminhtml/di.xml b/app/code/Magento/Authorizenet/etc/adminhtml/di.xml deleted file mode 100644 index 9305ac521c50a..0000000000000 --- a/app/code/Magento/Authorizenet/etc/adminhtml/di.xml +++ /dev/null @@ -1,22 +0,0 @@ - - - - - - Magento\Backend\Model\Session\Quote - - Magento\Payment\Block\Adminhtml\Transparent\Form - - - - - - Magento\Backend\Model\Session\Quote - - - diff --git a/app/code/Magento/Authorizenet/etc/adminhtml/events.xml b/app/code/Magento/Authorizenet/etc/adminhtml/events.xml deleted file mode 100644 index 085598d3fa95c..0000000000000 --- a/app/code/Magento/Authorizenet/etc/adminhtml/events.xml +++ /dev/null @@ -1,12 +0,0 @@ - - - - - - - diff --git a/app/code/Magento/Authorizenet/etc/adminhtml/routes.xml b/app/code/Magento/Authorizenet/etc/adminhtml/routes.xml deleted file mode 100644 index 00bb0475766f9..0000000000000 --- a/app/code/Magento/Authorizenet/etc/adminhtml/routes.xml +++ /dev/null @@ -1,14 +0,0 @@ - - - - - - - - - diff --git a/app/code/Magento/Authorizenet/etc/adminhtml/system.xml b/app/code/Magento/Authorizenet/etc/adminhtml/system.xml deleted file mode 100644 index 3f2037f70b2df..0000000000000 --- a/app/code/Magento/Authorizenet/etc/adminhtml/system.xml +++ /dev/null @@ -1,101 +0,0 @@ - - - - -
- - - - - Magento\Config\Model\Config\Source\Yesno - - - - Magento\Authorizenet\Model\Source\PaymentAction - - - - - - - Magento\Config\Model\Config\Backend\Encrypted - - - - Magento\Config\Model\Config\Backend\Encrypted - - - - Magento\Config\Model\Config\Backend\Encrypted - - - - Magento\Config\Model\Config\Backend\Encrypted - - - - Magento\Sales\Model\Config\Source\Order\Status\Processing - - - - Magento\Config\Model\Config\Source\Yesno - - - - - - - - - - Magento\Config\Model\Config\Source\Locale\Currency - - - - Magento\Config\Model\Config\Source\Yesno - - - - Magento\Config\Model\Config\Source\Yesno - - - - validate-email - - - - Magento\Authorizenet\Model\Source\Cctype - - - - Magento\Config\Model\Config\Source\Yesno - - - - Magento\Payment\Model\Config\Source\Allspecificcountries - - - - Magento\Directory\Model\Config\Source\Country - - - - validate-number validate-zero-or-greater - - - - validate-number validate-zero-or-greater - - - - validate-number - - -
-
-
diff --git a/app/code/Magento/Authorizenet/etc/config.xml b/app/code/Magento/Authorizenet/etc/config.xml deleted file mode 100644 index 60356460f553f..0000000000000 --- a/app/code/Magento/Authorizenet/etc/config.xml +++ /dev/null @@ -1,41 +0,0 @@ - - - - - - - 0 - AE,VI,MC,DI,JCB,DN - 0 - 0 - - - Magento\Authorizenet\Model\Directpost - processing - authorize - 1 - Credit Card Direct Post (Authorize.Net) - - - - 0 - USD - 1 - / - x_card_code,x_exp_date,x_card_num - authorizenet/directpost_payment/place - https://test.authorize.net/gateway/transact.dll - https://secure.authorize.net/gateway/transact.dll - https://apitest.authorize.net/xml/v1/request.api - https://api2.authorize.net/xml/v1/request.api - x_card_type,x_account_number,x_avs_code,x_auth_code,x_response_reason_text,x_cvv2_resp_code - authorizenet - - - - diff --git a/app/code/Magento/Authorizenet/etc/di.xml b/app/code/Magento/Authorizenet/etc/di.xml deleted file mode 100644 index 69d24019f2fb7..0000000000000 --- a/app/code/Magento/Authorizenet/etc/di.xml +++ /dev/null @@ -1,43 +0,0 @@ - - - - - - authorizenet_directpost - - - - - Magento\Authorizenet\Model\Directpost\Session\Storage - - - - - - 1 - 1 - 1 - 1 - 1 - 1 - 1 - - - 1 - 1 - 1 - 1 - - - - - - Magento\Authorizenet\Model\Directpost - - - diff --git a/app/code/Magento/Authorizenet/etc/frontend/di.xml b/app/code/Magento/Authorizenet/etc/frontend/di.xml deleted file mode 100644 index a0e5d907b15aa..0000000000000 --- a/app/code/Magento/Authorizenet/etc/frontend/di.xml +++ /dev/null @@ -1,45 +0,0 @@ - - - - - - Magento\Checkout\Model\Session - - - - - - /authorizenet/ - - - - - - Magento\Checkout\Model\Session - - - - - - Magento\Authorizenet\Model\Directpost::METHOD_CODE - - - - - - Magento\Authorizenet\Model\Directpost::METHOD_CODE - - - - - - DirectpostIframeCcConfigProvider - - - - diff --git a/app/code/Magento/Authorizenet/etc/frontend/events.xml b/app/code/Magento/Authorizenet/etc/frontend/events.xml deleted file mode 100644 index 9910dc4776fbe..0000000000000 --- a/app/code/Magento/Authorizenet/etc/frontend/events.xml +++ /dev/null @@ -1,15 +0,0 @@ - - - - - - - - - - diff --git a/app/code/Magento/Authorizenet/etc/frontend/page_types.xml b/app/code/Magento/Authorizenet/etc/frontend/page_types.xml deleted file mode 100644 index 56bfca71231cd..0000000000000 --- a/app/code/Magento/Authorizenet/etc/frontend/page_types.xml +++ /dev/null @@ -1,11 +0,0 @@ - - - - - - diff --git a/app/code/Magento/Authorizenet/etc/frontend/routes.xml b/app/code/Magento/Authorizenet/etc/frontend/routes.xml deleted file mode 100644 index e570f24f66c9c..0000000000000 --- a/app/code/Magento/Authorizenet/etc/frontend/routes.xml +++ /dev/null @@ -1,14 +0,0 @@ - - - - - - - - - diff --git a/app/code/Magento/Authorizenet/etc/frontend/sections.xml b/app/code/Magento/Authorizenet/etc/frontend/sections.xml deleted file mode 100644 index 1239398b06a10..0000000000000 --- a/app/code/Magento/Authorizenet/etc/frontend/sections.xml +++ /dev/null @@ -1,14 +0,0 @@ - - - - -
-
- - diff --git a/app/code/Magento/Authorizenet/etc/module.xml b/app/code/Magento/Authorizenet/etc/module.xml deleted file mode 100644 index a30fd34927746..0000000000000 --- a/app/code/Magento/Authorizenet/etc/module.xml +++ /dev/null @@ -1,16 +0,0 @@ - - - - - - - - - - - diff --git a/app/code/Magento/Authorizenet/etc/payment.xml b/app/code/Magento/Authorizenet/etc/payment.xml deleted file mode 100644 index 1d2cac374d8dc..0000000000000 --- a/app/code/Magento/Authorizenet/etc/payment.xml +++ /dev/null @@ -1,15 +0,0 @@ - - - - - - - - - diff --git a/app/code/Magento/Authorizenet/i18n/en_US.csv b/app/code/Magento/Authorizenet/i18n/en_US.csv deleted file mode 100644 index d724bd960d310..0000000000000 --- a/app/code/Magento/Authorizenet/i18n/en_US.csv +++ /dev/null @@ -1,75 +0,0 @@ -"You created the order.","You created the order." -"Order saving error: %1","Order saving error: %1" -"Please choose a payment method.","Please choose a payment method." -"We can't process your order right now. Please try again later.","We can't process your order right now. Please try again later." -"An error occurred on the server. Please try to place the order again.","An error occurred on the server. Please try to place the order again." -"Credit Card: xxxx-%1","Credit Card: xxxx-%1" -"amount %1","amount %1" -failed.,failed. -successful.,successful. -"Authorize.Net Transaction ID %1.","Authorize.Net Transaction ID %1." -authorize,authorize -"authorize and capture","authorize and capture" -capture,capture -refund,refund -void,void -"Gateway error: %1","Gateway error: %1" -"Something went wrong in the payment gateway.","Something went wrong in the payment gateway." -"Invalid amount for capture.","Invalid amount for capture." -"Payment capturing error.","Payment capturing error." -"Invalid transaction ID.","Invalid transaction ID." -"Payment voiding error.","Payment voiding error." -"Invalid amount for refund.","Invalid amount for refund." -"Payment refunding error.","Payment refunding error." -"The transaction was declined because the response hash validation failed.","The transaction was declined because the response hash validation failed." -"This payment didn't work out because we can't find this order.","This payment didn't work out because we can't find this order." -"There was a payment authorization error.","There was a payment authorization error." -"Please enter a transaction ID to authorize this payment.","Please enter a transaction ID to authorize this payment." -"Something went wrong: the paid amount doesn't match the order amount. Please correct this and try again.","Something went wrong: the paid amount doesn't match the order amount. Please correct this and try again." -"Transaction %1 has been approved. Amount %2. Transaction status is ""%3""","Transaction %1 has been approved. Amount %2. Transaction status is ""%3""" -"Transaction %1 has been voided/declined. Transaction status is ""%2"". Amount %3.","Transaction %1 has been voided/declined. Transaction status is ""%2"". Amount %3." -"Authorize Only","Authorize Only" -"Authorize and Capture","Authorize and Capture" -"Unable to get transaction details. Try again later.","Unable to get transaction details. Try again later." -"Electronic payments save the trees.","Electronic payments save the trees." -"Credit Card Type","Credit Card Type" -"Please Select","Please Select" -"Credit Card Number","Credit Card Number" -"Expiration Date","Expiration Date" -"Card Verification Number","Card Verification Number" -"Fraud Detection ","Fraud Detection " -"FDS Filter Action","FDS Filter Action" -"AVS Response","AVS Response" -"Card Code Response","Card Code Response" -"CAVV Response","CAVV Response" -"Fraud Filters","Fraud Filters" -"Place Order","Place Order" -"Sorry, but something went wrong. Please contact the seller.","Sorry, but something went wrong. Please contact the seller." -"Authorize.Net Direct Post (Deprecated)","Authorize.Net Direct Post (Deprecated)" -Enabled,Enabled -"Payment Action","Payment Action" -Title,Title -"API Login ID","API Login ID" -"Transaction Key","Transaction Key" -"Merchant MD5","Merchant MD5" -"New Order Status","New Order Status" -"Test Mode","Test Mode" -"Gateway URL","Gateway URL" -"Transaction Details URL","Transaction Details URL" -"Accepted Currency","Accepted Currency" -Debug,Debug -"Email Customer","Email Customer" -"Merchant's Email","Merchant's Email" -"Credit Card Types","Credit Card Types" -"Credit Card Verification","Credit Card Verification" -"Payment from Applicable Countries","Payment from Applicable Countries" -"Payment from Specific Countries","Payment from Specific Countries" -"Minimum Order Total","Minimum Order Total" -"Maximum Order Total","Maximum Order Total" -"Sort Order","Sort Order" -"x_card_type","Credit Card Type" -"x_account_number", "Credit Card Number" -"x_avs_code","AVS Response Code" -"x_auth_code","Processor Authentication Code" -"x_response_reason_text","Processor Response Text" -"x_cvv2_resp_code","CVV2 Response Code" diff --git a/app/code/Magento/Authorizenet/registration.php b/app/code/Magento/Authorizenet/registration.php deleted file mode 100644 index b96b8227bbbf8..0000000000000 --- a/app/code/Magento/Authorizenet/registration.php +++ /dev/null @@ -1,9 +0,0 @@ - - - - - - - diff --git a/app/code/Magento/Authorizenet/view/adminhtml/layout/sales_order_create_index.xml b/app/code/Magento/Authorizenet/view/adminhtml/layout/sales_order_create_index.xml deleted file mode 100644 index b675e49d72618..0000000000000 --- a/app/code/Magento/Authorizenet/view/adminhtml/layout/sales_order_create_index.xml +++ /dev/null @@ -1,17 +0,0 @@ - - - - - - - authorizenet_directpost - Magento_Authorizenet::directpost/info.phtml - - - - diff --git a/app/code/Magento/Authorizenet/view/adminhtml/layout/sales_order_create_load_block_billing_method.xml b/app/code/Magento/Authorizenet/view/adminhtml/layout/sales_order_create_load_block_billing_method.xml deleted file mode 100644 index ae14700836667..0000000000000 --- a/app/code/Magento/Authorizenet/view/adminhtml/layout/sales_order_create_load_block_billing_method.xml +++ /dev/null @@ -1,17 +0,0 @@ - - - - - - - authorizenet_directpost - Magento_Authorizenet::directpost/info.phtml - - - - diff --git a/app/code/Magento/Authorizenet/view/adminhtml/layout/sales_order_view.xml b/app/code/Magento/Authorizenet/view/adminhtml/layout/sales_order_view.xml deleted file mode 100644 index 57aeffce62c27..0000000000000 --- a/app/code/Magento/Authorizenet/view/adminhtml/layout/sales_order_view.xml +++ /dev/null @@ -1,14 +0,0 @@ - - - - - - - - - diff --git a/app/code/Magento/Authorizenet/view/adminhtml/templates/directpost/iframe.phtml b/app/code/Magento/Authorizenet/view/adminhtml/templates/directpost/iframe.phtml deleted file mode 100644 index 3088713989453..0000000000000 --- a/app/code/Magento/Authorizenet/view/adminhtml/templates/directpost/iframe.phtml +++ /dev/null @@ -1,31 +0,0 @@ -getParams(); -$helper = $block->getHelper('adminhtml'); -?> - - - - - - diff --git a/app/code/Magento/Authorizenet/view/adminhtml/templates/directpost/info.phtml b/app/code/Magento/Authorizenet/view/adminhtml/templates/directpost/info.phtml deleted file mode 100644 index bec87738a83c1..0000000000000 --- a/app/code/Magento/Authorizenet/view/adminhtml/templates/directpost/info.phtml +++ /dev/null @@ -1,152 +0,0 @@ -escapeHtml($block->getMethodCode()); -$method = $block->getMethod(); -$controller = $block->escapeHtml($block->getRequest()->getControllerName()); -$orderUrl = $block->escapeUrl($block->getHelper('adminhtml')->getPlaceOrderAdminUrl()); -$ccType = $block->getInfoData('cc_type'); -$ccExpMonth = $block->getInfoData('cc_exp_month'); -$ccExpYear = $block->getInfoData('cc_exp_year'); -?> - - - - - - diff --git a/app/code/Magento/Authorizenet/view/adminhtml/templates/order/view/info/fraud_details.phtml b/app/code/Magento/Authorizenet/view/adminhtml/templates/order/view/info/fraud_details.phtml deleted file mode 100644 index 15325e15de1e1..0000000000000 --- a/app/code/Magento/Authorizenet/view/adminhtml/templates/order/view/info/fraud_details.phtml +++ /dev/null @@ -1,56 +0,0 @@ -getPayment(); -$fraudDetails = $payment->getAdditionalInformation('fraud_details'); -?> - - -
- escapeHtml(__('Fraud Detection ')) ?> -
- -
-
- - escapeHtml(__('FDS Filter Action')) ?>: - escapeHtml($fraudDetails['fds_filter_action']) ?> -
- - - - escapeHtml(__('AVS Response')) ?>: - escapeHtml($fraudDetails['avs_response']) ?> -
- - - - escapeHtml(__('Card Code Response')) ?>: - escapeHtml($fraudDetails['card_code_response']) ?> -
- - - - escapeHtml(__('CAVV Response')) ?>: - escapeHtml($fraudDetails['cavv_response']) ?> -
- - - - escapeHtml(__('Fraud Filters')) ?>: -
- - escapeHtml($filter['name']) ?>: - escapeHtml($filter['action']) ?> -
- - -
-
- diff --git a/app/code/Magento/Authorizenet/view/adminhtml/web/js/direct-post.js b/app/code/Magento/Authorizenet/view/adminhtml/web/js/direct-post.js deleted file mode 100644 index eb162034bc04d..0000000000000 --- a/app/code/Magento/Authorizenet/view/adminhtml/web/js/direct-post.js +++ /dev/null @@ -1,345 +0,0 @@ -/** - * Copyright © Magento, Inc. All rights reserved. - * See COPYING.txt for license details. - */ - -define([ - 'jquery', - 'mage/backend/validation', - 'prototype' -], function (jQuery) { - window.directPost = Class.create(); - directPost.prototype = { - initialize: function (methodCode, iframeId, controller, orderSaveUrl, cgiUrl, nativeAction) { - var prepare = function (event, method) { - if (method === 'authorizenet_directpost') { - this.preparePayment(); - } else { - jQuery('#edit_form') - .off('submitOrder.authorizenet'); - } - }; - - this.iframeId = iframeId; - this.controller = controller; - this.orderSaveUrl = orderSaveUrl; - this.nativeAction = nativeAction; - this.cgiUrl = cgiUrl; - this.code = methodCode; - this.inputs = ['cc_type', 'cc_number', 'expiration', 'expiration_yr', 'cc_cid']; - this.headers = []; - this.isValid = true; - this.paymentRequestSent = false; - this.orderIncrementId = false; - this.successUrl = false; - this.hasError = false; - this.tmpForm = false; - - this.onLoadIframe = this.loadIframe.bindAsEventListener(this); - this.onLoadOrderIframe = this.loadOrderIframe.bindAsEventListener(this); - this.onSubmitAdminOrder = this.submitAdminOrder.bindAsEventListener(this); - - jQuery('#edit_form').on('changePaymentMethod', prepare.bind(this)); - - jQuery('#edit_form').trigger( - 'changePaymentMethod', - [ - jQuery('#edit_form').find(':radio[name="payment[method]"]:checked').val() - ] - ); - }, - - validate: function () { - this.isValid = true; - this.inputs.each(function (elemIndex) { - if ($(this.code + '_' + elemIndex)) { - if (!jQuery.validator.validateElement($(this.code + '_' + elemIndex))) { - this.isValid = false; - } - } - }, this); - - return this.isValid; - }, - - changeInputOptions: function (param, value) { - this.inputs.each(function (elemIndex) { - if ($(this.code + '_' + elemIndex)) { - $(this.code + '_' + elemIndex).writeAttribute(param, value); - } - }, this); - }, - - preparePayment: function () { - this.changeInputOptions('autocomplete', 'off'); - jQuery('#edit_form') - .off('submitOrder') - .on('submitOrder.authorizenet', this.submitAdminOrder.bind(this)); - - if ($(this.iframeId)) { - // Temporary solution will be removed after refactoring Authorize.Net (sales) functionality - jQuery('.scalable.save:not(disabled)').removeAttr('onclick'); - jQuery(document).off('click.directPost'); - jQuery(document).on( - 'click.directPost', - '.scalable.save:not(disabled)', - jQuery.proxy(this.onSubmitAdminOrder, this) - ); - $('order-' + this.iframeId).observe('load', this.onLoadOrderIframe); - $(this.iframeId).observe('load', this.onLoadIframe); - } - }, - - loadIframe: function () { - if (this.paymentRequestSent) { - if (!this.orderRequestSent) { - this.paymentRequestSent = false; - - if (!this.hasError) { - this.returnQuote(); - } else { - this.changeInputOptions('disabled', false); - jQuery('body').trigger('processStop'); - enableElements('save'); - } - } - - if (this.tmpForm) { - document.body.removeChild(this.tmpForm); - } - } - }, - - loadOrderIframe: function () { - if (this.orderRequestSent) { - $(this.iframeId).hide(); - var data = $('order-' + this.iframeId).contentWindow.document.body.getElementsByTagName('pre')[0].innerHTML; - - this.saveAdminOrderSuccess(data); - this.orderRequestSent = false; - } - }, - - showError: function (msg) { - this.hasError = true; - - if (this.controller == 'onepage') { - $(this.iframeId).hide(); - this.resetLoadWaiting(); - } - alert(msg); - }, - - returnQuote: function () { - var url = this.orderSaveUrl.replace('place', 'returnQuote'); - - new Ajax.Request(url, { - onSuccess: function (transport) { - try { - response = transport.responseText.evalJSON(true); - } catch (e) { - response = {}; - } - - if (response.error_message) { - alert(response.error_message); - } - $(this.iframeId).show(); - this.changeInputOptions('disabled', false); - jQuery('body').trigger('processStop'); - enableElements('save'); - }.bind(this) - }); - }, - - setLoadWaiting: function () { - this.headers.each(function (header) { - header.removeClassName('allow'); - }); - checkout.setLoadWaiting('review'); - }, - - resetLoadWaiting: function () { - this.headers.each(function (header) { - header.addClassName('allow'); - }); - checkout.setLoadWaiting(false); - }, - - submitAdminOrder: function () { - // Temporary solution will be removed after refactoring Authorize.Net (sales) functionality - var editForm = jQuery('#edit_form'); - - if (editForm.valid()) { - // Temporary solution will be removed after refactoring Authorize.Net (sales) functionality - paymentMethodEl = editForm.find(':radio[name="payment[method]"]:checked'); - this.hasError = false; - - if (paymentMethodEl.val() == this.code) { - jQuery('body').trigger('processStart'); - setLoaderPosition(); - this.changeInputOptions('disabled', 'disabled'); - this.paymentRequestSent = true; - this.orderRequestSent = true; - // Temporary solutions will be removed after refactoring Authorize.Net (sales) functionality - editForm.attr('action', this.orderSaveUrl); - editForm.attr('target', - jQuery('#order-' + this.iframeId).attr('name')); - editForm.append(this.createHiddenElement('controller', this.controller)); - disableElements('save'); - // Temporary solutions will be removed after refactoring Authorize.Net (sales) functionality - order._realSubmit(); - } else { - editForm.attr('action', this.nativeAction); - editForm.attr('target', '_top'); - disableElements('save'); - // Temporary solutions will be removed after refactoring Authorize.Net (sales) functionality - order._realSubmit(); - } - } - }, - - recollectQuote: function () { - var area = ['sidebar', 'items', 'shipping_method', 'billing_method', 'totals', 'giftmessage']; - - area = order.prepareArea(area); - var url = order.loadBaseUrl + 'block/' + area; - var info = $('order-items_grid').select('input', 'select', 'textarea'); - var data = {}; - - for (var i = 0; i < info.length; i++) { - if (!info[i].disabled && (info[i].type != 'checkbox' || info[i].checked)) { - data[info[i].name] = info[i].getValue(); - } - } - data.reset_shipping = true; - data.update_items = true; - - if ($('coupons:code') && $F('coupons:code')) { - data['order[coupon][code]'] = $F('coupons:code'); - } - data.json = true; - new Ajax.Request(url, { - parameters: data, - loaderArea: 'html-body', - onSuccess: function (transport) { - jQuery('#edit_form').submit(); - } - }); - - }, - - saveAdminOrderSuccess: function (data) { - try { - response = data.evalJSON(true); - } catch (e) { - response = {}; - } - - if (response.directpost) { - this.orderIncrementId = response.directpost.fields.x_invoice_num; - var paymentData = {}; - - for (var key in response.directpost.fields) { - paymentData[key] = response.directpost.fields[key]; - } - var preparedData = this.preparePaymentRequest(paymentData); - - this.sendPaymentRequest(preparedData); - } else { - if (response.redirect) { - window.location = response.redirect; - } - - if (response.error_messages) { - var msg = response.error_messages; - - if (typeof msg == 'object') { - msg = msg.join('\n'); - } - - if (msg) { - alert(msg); - } - } - } - }, - - preparePaymentRequest: function (data) { - if ($(this.code + '_cc_cid')) { - data.x_card_code = $(this.code + '_cc_cid').value; - } - var year = $(this.code + '_expiration_yr').value; - - if (year.length > 2) { - year = year.substring(2); - } - var month = parseInt($(this.code + '_expiration').value, 10); - - if (month < 10) { - month = '0' + month; - } - - data.x_exp_date = month + '/' + year; - data.x_card_num = $(this.code + '_cc_number').value; - - return data; - }, - - sendPaymentRequest: function (preparedData) { - this.recreateIframe(); - this.tmpForm = document.createElement('form'); - this.tmpForm.style.display = 'none'; - this.tmpForm.enctype = 'application/x-www-form-urlencoded'; - this.tmpForm.method = 'POST'; - document.body.appendChild(this.tmpForm); - this.tmpForm.action = this.cgiUrl; - this.tmpForm.target = $(this.iframeId).readAttribute('name'); - this.tmpForm.setAttribute('target', $(this.iframeId).readAttribute('name')); - - for (var param in preparedData) { - this.tmpForm.appendChild(this.createHiddenElement(param, preparedData[param])); - } - - this.paymentRequestSent = true; - this.tmpForm.submit(); - }, - - createHiddenElement: function (name, value) { - var field; - - if (isIE) { - field = document.createElement('input'); - field.setAttribute('type', 'hidden'); - field.setAttribute('name', name); - field.setAttribute('value', value); - } else { - field = document.createElement('input'); - field.type = 'hidden'; - field.name = name; - field.value = value; - } - - return field; - }, - - recreateIframe: function () { - if ($(this.iframeId)) { - var nextElement = $(this.iframeId).next(); - var src = $(this.iframeId).readAttribute('src'); - var name = $(this.iframeId).readAttribute('name'); - - $(this.iframeId).stopObserving(); - $(this.iframeId).remove(); - var iframe = ' -
- -
-
- - - -
- -
-
- -
-
- - diff --git a/app/code/Magento/AuthorizenetAcceptjs/Block/Form.php b/app/code/Magento/AuthorizenetAcceptjs/Block/Form.php deleted file mode 100644 index f669ead967c59..0000000000000 --- a/app/code/Magento/AuthorizenetAcceptjs/Block/Form.php +++ /dev/null @@ -1,64 +0,0 @@ -config = $config; - $this->sessionQuote = $sessionQuote; - } - - /** - * Check if cvv validation is available - * - * @return boolean - */ - public function isCvvEnabled(): bool - { - return $this->config->isCvvEnabled($this->sessionQuote->getStoreId()); - } -} diff --git a/app/code/Magento/AuthorizenetAcceptjs/Block/Info.php b/app/code/Magento/AuthorizenetAcceptjs/Block/Info.php deleted file mode 100644 index 1876685998643..0000000000000 --- a/app/code/Magento/AuthorizenetAcceptjs/Block/Info.php +++ /dev/null @@ -1,33 +0,0 @@ -config = $config; - $this->json = $json; - } - - /** - * Retrieves the config that should be used by the block - * - * @return string - */ - public function getPaymentConfig(): string - { - $payment = $this->config->getConfig()['payment']; - $config = $payment[$this->getMethodCode()]; - $config['code'] = $this->getMethodCode(); - - return $this->json->serialize($config); - } - - /** - * Returns the method code for this payment method - * - * @return string - */ - public function getMethodCode(): string - { - return Config::METHOD; - } -} diff --git a/app/code/Magento/AuthorizenetAcceptjs/Gateway/Command/AcceptPaymentStrategyCommand.php b/app/code/Magento/AuthorizenetAcceptjs/Gateway/Command/AcceptPaymentStrategyCommand.php deleted file mode 100644 index d59edde212760..0000000000000 --- a/app/code/Magento/AuthorizenetAcceptjs/Gateway/Command/AcceptPaymentStrategyCommand.php +++ /dev/null @@ -1,77 +0,0 @@ -commandPool = $commandPool; - $this->subjectReader = $subjectReader; - } - - /** - * @inheritdoc - */ - public function execute(array $commandSubject): void - { - if ($this->shouldAcceptInGateway($commandSubject)) { - $this->commandPool->get(self::ACCEPT_FDS) - ->execute($commandSubject); - } - } - - /** - * Determines if the transaction needs to be accepted in the gateway - * - * @param array $commandSubject - * @return bool - * @throws CommandException - */ - private function shouldAcceptInGateway(array $commandSubject): bool - { - $details = $this->commandPool->get('get_transaction_details') - ->execute($commandSubject) - ->get(); - - return in_array($details['transaction']['transactionStatus'], self::NEEDS_APPROVAL_STATUSES); - } -} diff --git a/app/code/Magento/AuthorizenetAcceptjs/Gateway/Command/CaptureStrategyCommand.php b/app/code/Magento/AuthorizenetAcceptjs/Gateway/Command/CaptureStrategyCommand.php deleted file mode 100644 index 4318441014ad7..0000000000000 --- a/app/code/Magento/AuthorizenetAcceptjs/Gateway/Command/CaptureStrategyCommand.php +++ /dev/null @@ -1,143 +0,0 @@ -commandPool = $commandPool; - $this->transactionRepository = $repository; - $this->filterBuilder = $filterBuilder; - $this->searchCriteriaBuilder = $searchCriteriaBuilder; - $this->subjectReader = $subjectReader; - } - - /** - * @inheritdoc - */ - public function execute(array $commandSubject): void - { - /** @var PaymentDataObjectInterface $paymentDO */ - $paymentDO = $this->subjectReader->readPayment($commandSubject); - - $command = $this->getCommand($paymentDO); - $this->commandPool->get($command) - ->execute($commandSubject); - } - - /** - * Get execution command name. - * - * @param PaymentDataObjectInterface $paymentDO - * @return string - */ - private function getCommand(PaymentDataObjectInterface $paymentDO): string - { - $payment = $paymentDO->getPayment(); - ContextHelper::assertOrderPayment($payment); - - // If auth transaction does not exist then execute authorize&capture command - $captureExists = $this->captureTransactionExists($payment); - if (!$payment->getAuthorizationTransaction() && !$captureExists) { - return self::SALE; - } - - return self::CAPTURE; - } - - /** - * Check if capture transaction already exists - * - * @param OrderPaymentInterface $payment - * @return bool - */ - private function captureTransactionExists(OrderPaymentInterface $payment): bool - { - $this->searchCriteriaBuilder->addFilters( - [ - $this->filterBuilder - ->setField('payment_id') - ->setValue($payment->getId()) - ->create(), - ] - ); - - $this->searchCriteriaBuilder->addFilters( - [ - $this->filterBuilder - ->setField('txn_type') - ->setValue(TransactionInterface::TYPE_CAPTURE) - ->create(), - ] - ); - - $searchCriteria = $this->searchCriteriaBuilder->create(); - $count = $this->transactionRepository->getList($searchCriteria) - ->getTotalCount(); - - return $count > 0; - } -} diff --git a/app/code/Magento/AuthorizenetAcceptjs/Gateway/Command/FetchTransactionInfoCommand.php b/app/code/Magento/AuthorizenetAcceptjs/Gateway/Command/FetchTransactionInfoCommand.php deleted file mode 100644 index d0c1ceac81378..0000000000000 --- a/app/code/Magento/AuthorizenetAcceptjs/Gateway/Command/FetchTransactionInfoCommand.php +++ /dev/null @@ -1,90 +0,0 @@ -commandPool = $commandPool; - $this->subjectReader = $subjectReader; - $this->config = $config; - $this->handler = $handler; - } - - /** - * @inheritdoc - */ - public function execute(array $commandSubject): array - { - $paymentDO = $this->subjectReader->readPayment($commandSubject); - $order = $paymentDO->getOrder(); - - $command = $this->commandPool->get('get_transaction_details'); - $result = $command->execute($commandSubject); - $response = $result->get(); - - if ($this->handler) { - $this->handler->handle($commandSubject, $response); - } - - $additionalInformationKeys = $this->config->getTransactionInfoSyncKeys($order->getStoreId()); - $rawDetails = []; - foreach ($additionalInformationKeys as $key) { - if (isset($response['transaction'][$key])) { - $rawDetails[$key] = $response['transaction'][$key]; - } - } - - return $rawDetails; - } -} diff --git a/app/code/Magento/AuthorizenetAcceptjs/Gateway/Command/GatewayQueryCommand.php b/app/code/Magento/AuthorizenetAcceptjs/Gateway/Command/GatewayQueryCommand.php deleted file mode 100644 index 7185639936fa4..0000000000000 --- a/app/code/Magento/AuthorizenetAcceptjs/Gateway/Command/GatewayQueryCommand.php +++ /dev/null @@ -1,103 +0,0 @@ -requestBuilder = $requestBuilder; - $this->transferFactory = $transferFactory; - $this->client = $client; - $this->validator = $validator; - $this->logger = $logger; - } - - /** - * @inheritdoc - * - * @throws Exception - */ - public function execute(array $commandSubject): ResultInterface - { - $transferO = $this->transferFactory->create( - $this->requestBuilder->build($commandSubject) - ); - - try { - $response = $this->client->placeRequest($transferO); - } catch (Exception $e) { - $this->logger->critical($e); - - throw new CommandException(__('There was an error while trying to process the request.')); - } - - $result = $this->validator->validate( - array_merge($commandSubject, ['response' => $response]) - ); - if (!$result->isValid()) { - throw new CommandException(__('There was an error while trying to process the request.')); - } - - return new ArrayResult($response); - } -} diff --git a/app/code/Magento/AuthorizenetAcceptjs/Gateway/Command/RefundTransactionStrategyCommand.php b/app/code/Magento/AuthorizenetAcceptjs/Gateway/Command/RefundTransactionStrategyCommand.php deleted file mode 100644 index de3ded6515ae0..0000000000000 --- a/app/code/Magento/AuthorizenetAcceptjs/Gateway/Command/RefundTransactionStrategyCommand.php +++ /dev/null @@ -1,106 +0,0 @@ -commandPool = $commandPool; - $this->subjectReader = $subjectReader; - } - - /** - * @inheritdoc - */ - public function execute(array $commandSubject): void - { - $command = $this->getCommand($commandSubject); - - $this->commandPool->get($command) - ->execute($commandSubject); - } - - /** - * Determines the command that should be used based on the status of the transaction - * - * @param array $commandSubject - * @return string - * @throws CommandException - * @throws \Magento\Framework\Exception\NotFoundException - */ - private function getCommand(array $commandSubject): string - { - $details = $this->commandPool->get('get_transaction_details') - ->execute($commandSubject) - ->get(); - - if ($this->canVoid($details, $commandSubject)) { - return self::VOID; - } - - if ($details['transaction']['transactionStatus'] !== 'settledSuccessfully') { - throw new CommandException(__('This transaction cannot be refunded with its current status.')); - } - - return self::REFUND; - } - - /** - * Checks if void command can be performed. - * - * @param array $details - * @param array $commandSubject - * @return bool - * @throws CommandException - */ - private function canVoid(array $details, array $commandSubject) :bool - { - if ($details['transaction']['transactionStatus'] === 'capturedPendingSettlement') { - if ((float) $details['transaction']['authAmount'] !== (float) $commandSubject['amount']) { - throw new CommandException( - __('The transaction has not been settled, a partial refund is not yet available.') - ); - } - - return true; - } - - return false; - } -} diff --git a/app/code/Magento/AuthorizenetAcceptjs/Gateway/Config.php b/app/code/Magento/AuthorizenetAcceptjs/Gateway/Config.php deleted file mode 100644 index f41eb1660da55..0000000000000 --- a/app/code/Magento/AuthorizenetAcceptjs/Gateway/Config.php +++ /dev/null @@ -1,189 +0,0 @@ -getValue(Config::KEY_LOGIN_ID, $storeId); - } - - /** - * Gets the current environment - * - * @param int|null $storeId - * @return string - */ - public function getEnvironment($storeId = null): string - { - return $this->getValue(Config::KEY_ENVIRONMENT, $storeId); - } - - /** - * Gets the transaction key - * - * @param int|null $storeId - * @return string - */ - public function getTransactionKey($storeId = null): ?string - { - return $this->getValue(Config::KEY_TRANSACTION_KEY, $storeId); - } - - /** - * Gets the API endpoint URL - * - * @param int|null $storeId - * @return string - */ - public function getApiUrl($storeId = null): string - { - $environment = $this->getValue(Config::KEY_ENVIRONMENT, $storeId); - - return $environment === Environment::ENVIRONMENT_SANDBOX - ? self::ENDPOINT_URL_SANDBOX - : self::ENDPOINT_URL_PRODUCTION; - } - - /** - * Gets the configured signature key - * - * @param int|null $storeId - * @return string - */ - public function getTransactionSignatureKey($storeId = null): ?string - { - return $this->getValue(Config::KEY_SIGNATURE_KEY, $storeId); - } - - /** - * Gets the configured legacy transaction hash - * - * @param int|null $storeId - * @return string - */ - public function getLegacyTransactionHash($storeId = null): ?string - { - return $this->getValue(Config::KEY_LEGACY_TRANSACTION_HASH, $storeId); - } - - /** - * Gets the configured payment action - * - * @param int|null $storeId - * @return string - */ - public function getPaymentAction($storeId = null): ?string - { - return $this->getValue(Config::KEY_PAYMENT_ACTION, $storeId); - } - - /** - * Gets the configured client key - * - * @param int|null $storeId - * @return string - */ - public function getClientKey($storeId = null): ?string - { - return $this->getValue(Config::KEY_CLIENT_KEY, $storeId); - } - - /** - * Should authorize.net email the customer their receipt. - * - * @param int|null $storeId - * @return bool - */ - public function shouldEmailCustomer($storeId = null): bool - { - return (bool)$this->getValue(Config::KEY_SHOULD_EMAIL_CUSTOMER, $storeId); - } - - /** - * Should the cvv field be shown - * - * @param int|null $storeId - * @return bool - */ - public function isCvvEnabled($storeId = null): bool - { - return (bool)$this->getValue(Config::KEY_CVV_ENABLED, $storeId); - } - - /** - * Retrieves the solution id for the given store based on environment - * - * @param int|null $storeId - * @return string - */ - public function getSolutionId($storeId = null): ?string - { - $environment = $this->getValue(Config::KEY_ENVIRONMENT, $storeId); - - return $environment === Environment::ENVIRONMENT_SANDBOX - ? self::SOLUTION_ID_SANDBOX - : self::SOLUTION_ID_PRODUCTION; - } - - /** - * Returns the keys to be pulled from the transaction and displayed - * - * @param int|null $storeId - * @return string[] - */ - public function getAdditionalInfoKeys($storeId = null): array - { - return explode(',', $this->getValue(Config::KEY_ADDITIONAL_INFO_KEYS, $storeId) ?? ''); - } - - /** - * Returns the keys to be pulled from the transaction and displayed when syncing the transaction - * - * @param int|null $storeId - * @return string[] - */ - public function getTransactionInfoSyncKeys($storeId = null): array - { - return explode(',', $this->getValue(Config::KEY_TRANSACTION_SYNC_KEYS, $storeId) ?? ''); - } -} diff --git a/app/code/Magento/AuthorizenetAcceptjs/Gateway/Http/Client.php b/app/code/Magento/AuthorizenetAcceptjs/Gateway/Http/Client.php deleted file mode 100644 index ebd4240108a09..0000000000000 --- a/app/code/Magento/AuthorizenetAcceptjs/Gateway/Http/Client.php +++ /dev/null @@ -1,131 +0,0 @@ -httpClientFactory = $httpClientFactory; - $this->config = $config; - $this->paymentLogger = $paymentLogger; - $this->logger = $logger; - $this->json = $json; - } - - /** - * Places request to gateway. Returns result as ENV array - * - * @param TransferInterface $transferObject - * @return array - * @throws \Magento\Payment\Gateway\Http\ClientException - */ - public function placeRequest(TransferInterface $transferObject) - { - $request = $transferObject->getBody(); - $log = [ - 'request' => $request, - ]; - $client = $this->httpClientFactory->create(); - $url = $this->config->getApiUrl(); - - $type = $request['payload_type']; - unset($request['payload_type']); - $request = [$type => $request]; - - try { - $client->setUri($url); - $client->setConfig(['maxredirects' => 0, 'timeout' => 30]); - $client->setRawData($this->json->serialize($request), 'application/json'); - $client->setMethod(ZendClient::POST); - - $responseBody = $client->request() - ->getBody(); - - // Strip BOM because Authorize.net sends it in the response - if ($responseBody && substr($responseBody, 0, 3) === pack('CCC', 0xef, 0xbb, 0xbf)) { - $responseBody = substr($responseBody, 3); - } - - $log['response'] = $responseBody; - - try { - $data = $this->json->unserialize($responseBody); - } catch (InvalidArgumentException $e) { - // phpcs:ignore Magento2.Exceptions.DirectThrow - throw new \Exception('Invalid JSON was returned by the gateway'); - } - - return $data; - // phpcs:ignore Magento2.Exceptions.ThrowCatch - } catch (\Exception $e) { - $this->logger->critical($e); - - throw new ClientException( - __('Something went wrong in the payment gateway.') - ); - } finally { - $this->paymentLogger->debug($log); - } - } -} diff --git a/app/code/Magento/AuthorizenetAcceptjs/Gateway/Http/Payload/Filter/RemoveFieldsFilter.php b/app/code/Magento/AuthorizenetAcceptjs/Gateway/Http/Payload/Filter/RemoveFieldsFilter.php deleted file mode 100644 index cce878cfbbb16..0000000000000 --- a/app/code/Magento/AuthorizenetAcceptjs/Gateway/Http/Payload/Filter/RemoveFieldsFilter.php +++ /dev/null @@ -1,45 +0,0 @@ -fields = $fields; - } - - /** - * @inheritdoc - */ - public function filter(array $data): array - { - foreach ($this->fields as $field) { - unset($data[$field]); - } - - return $data; - } -} diff --git a/app/code/Magento/AuthorizenetAcceptjs/Gateway/Http/Payload/FilterInterface.php b/app/code/Magento/AuthorizenetAcceptjs/Gateway/Http/Payload/FilterInterface.php deleted file mode 100644 index dade4bd4ee1f3..0000000000000 --- a/app/code/Magento/AuthorizenetAcceptjs/Gateway/Http/Payload/FilterInterface.php +++ /dev/null @@ -1,26 +0,0 @@ -transferBuilder = $transferBuilder; - $this->payloadFilters = $payloadFilters; - } - - /** - * Builds gateway transfer object - * - * @param array $request - * @return TransferInterface - */ - public function create(array $request) - { - foreach ($this->payloadFilters as $filter) { - $request = $filter->filter($request); - } - - return $this->transferBuilder - ->setBody($request) - ->build(); - } -} diff --git a/app/code/Magento/AuthorizenetAcceptjs/Gateway/Request/AcceptFdsDataBuilder.php b/app/code/Magento/AuthorizenetAcceptjs/Gateway/Request/AcceptFdsDataBuilder.php deleted file mode 100644 index 4a673112e6a5f..0000000000000 --- a/app/code/Magento/AuthorizenetAcceptjs/Gateway/Request/AcceptFdsDataBuilder.php +++ /dev/null @@ -1,68 +0,0 @@ -subjectReader = $subjectReader; - } - - /** - * @inheritdoc - */ - public function build(array $buildSubject): array - { - $paymentDO = $this->subjectReader->readPayment($buildSubject); - $payment = $paymentDO->getPayment(); - $data = []; - - if ($payment instanceof Payment) { - $authorizationTransaction = $payment->getAuthorizationTransaction(); - - if (empty($authorizationTransaction)) { - $transactionId = $payment->getLastTransId(); - } else { - $transactionId = $authorizationTransaction->getParentTxnId(); - - if (empty($transactionId)) { - $transactionId = $authorizationTransaction->getTxnId(); - } - } - - $data = [ - 'heldTransactionRequest' => [ - 'action' => 'approve', - 'refTransId' => $transactionId, - ] - ]; - } - - return $data; - } -} diff --git a/app/code/Magento/AuthorizenetAcceptjs/Gateway/Request/AddressDataBuilder.php b/app/code/Magento/AuthorizenetAcceptjs/Gateway/Request/AddressDataBuilder.php deleted file mode 100644 index 07a4921b7d60b..0000000000000 --- a/app/code/Magento/AuthorizenetAcceptjs/Gateway/Request/AddressDataBuilder.php +++ /dev/null @@ -1,80 +0,0 @@ -subjectReader = $subjectReader; - } - - /** - * @inheritdoc - */ - public function build(array $buildSubject): array - { - $paymentDO = $this->subjectReader->readPayment($buildSubject); - $order = $paymentDO->getOrder(); - $billingAddress = $order->getBillingAddress(); - $shippingAddress = $order->getShippingAddress(); - $result = [ - 'transactionRequest' => [] - ]; - - if ($billingAddress) { - $result['transactionRequest']['billTo'] = [ - 'firstName' => $billingAddress->getFirstname(), - 'lastName' => $billingAddress->getLastname(), - 'company' => $billingAddress->getCompany() ?? '', - 'address' => $billingAddress->getStreetLine1(), - 'city' => $billingAddress->getCity(), - 'state' => $billingAddress->getRegionCode(), - 'zip' => $billingAddress->getPostcode(), - 'country' => $billingAddress->getCountryId() - ]; - } - - if ($shippingAddress) { - $result['transactionRequest']['shipTo'] = [ - 'firstName' => $shippingAddress->getFirstname(), - 'lastName' => $shippingAddress->getLastname(), - 'company' => $shippingAddress->getCompany() ?? '', - 'address' => $shippingAddress->getStreetLine1(), - 'city' => $shippingAddress->getCity(), - 'state' => $shippingAddress->getRegionCode(), - 'zip' => $shippingAddress->getPostcode(), - 'country' => $shippingAddress->getCountryId() - ]; - } - - if ($order->getRemoteIp()) { - $result['transactionRequest']['customerIP'] = $order->getRemoteIp(); - } - - return $result; - } -} diff --git a/app/code/Magento/AuthorizenetAcceptjs/Gateway/Request/AmountDataBuilder.php b/app/code/Magento/AuthorizenetAcceptjs/Gateway/Request/AmountDataBuilder.php deleted file mode 100644 index 07fae5e536a28..0000000000000 --- a/app/code/Magento/AuthorizenetAcceptjs/Gateway/Request/AmountDataBuilder.php +++ /dev/null @@ -1,49 +0,0 @@ -subjectReader = $subjectReader; - } - - /** - * @inheritdoc - */ - public function build(array $buildSubject): array - { - return [ - 'transactionRequest' => [ - 'amount' => $this->formatPrice($this->subjectReader->readAmount($buildSubject)), - ] - ]; - } -} diff --git a/app/code/Magento/AuthorizenetAcceptjs/Gateway/Request/AuthenticationDataBuilder.php b/app/code/Magento/AuthorizenetAcceptjs/Gateway/Request/AuthenticationDataBuilder.php deleted file mode 100644 index dec6626dc7524..0000000000000 --- a/app/code/Magento/AuthorizenetAcceptjs/Gateway/Request/AuthenticationDataBuilder.php +++ /dev/null @@ -1,62 +0,0 @@ -subjectReader = $subjectReader; - $this->config = $config; - } - - /** - * Adds authentication information to the request - * - * @param array $buildSubject - * @return array - */ - public function build(array $buildSubject): array - { - $storeId = $this->subjectReader->readStoreId($buildSubject); - - return [ - 'merchantAuthentication' => [ - 'name' => $this->config->getLoginId($storeId), - 'transactionKey' => $this->config->getTransactionKey($storeId) - ] - ]; - } -} diff --git a/app/code/Magento/AuthorizenetAcceptjs/Gateway/Request/AuthorizeDataBuilder.php b/app/code/Magento/AuthorizenetAcceptjs/Gateway/Request/AuthorizeDataBuilder.php deleted file mode 100644 index c440da3ca9f4f..0000000000000 --- a/app/code/Magento/AuthorizenetAcceptjs/Gateway/Request/AuthorizeDataBuilder.php +++ /dev/null @@ -1,72 +0,0 @@ -subjectReader = $subjectReader; - $this->passthroughData = $passthroughData; - } - - /** - * @inheritdoc - */ - public function build(array $buildSubject): array - { - $paymentDO = $this->subjectReader->readPayment($buildSubject); - $payment = $paymentDO->getPayment(); - $data = []; - - if ($payment instanceof Payment) { - $data = [ - 'transactionRequest' => [ - 'transactionType' => self::REQUEST_AUTH_ONLY, - ] - ]; - - $this->passthroughData->setData( - 'transactionType', - $data['transactionRequest']['transactionType'] - ); - } - - return $data; - } -} diff --git a/app/code/Magento/AuthorizenetAcceptjs/Gateway/Request/CaptureDataBuilder.php b/app/code/Magento/AuthorizenetAcceptjs/Gateway/Request/CaptureDataBuilder.php deleted file mode 100644 index 1e2a8617907a0..0000000000000 --- a/app/code/Magento/AuthorizenetAcceptjs/Gateway/Request/CaptureDataBuilder.php +++ /dev/null @@ -1,76 +0,0 @@ -subjectReader = $subjectReader; - $this->passthroughData = $passthroughData; - } - - /** - * @inheritdoc - */ - public function build(array $buildSubject): array - { - $paymentDO = $this->subjectReader->readPayment($buildSubject); - $payment = $paymentDO->getPayment(); - $data = []; - - if ($payment instanceof Payment) { - $authTransaction = $payment->getAuthorizationTransaction(); - $refId = $authTransaction->getAdditionalInformation('real_transaction_id'); - - $data = [ - 'transactionRequest' => [ - 'transactionType' => self::REQUEST_TYPE_PRIOR_AUTH_CAPTURE, - 'refTransId' => $refId - ] - ]; - - $this->passthroughData->setData( - 'transactionType', - $data['transactionRequest']['transactionType'] - ); - } - - return $data; - } -} diff --git a/app/code/Magento/AuthorizenetAcceptjs/Gateway/Request/CustomSettingsBuilder.php b/app/code/Magento/AuthorizenetAcceptjs/Gateway/Request/CustomSettingsBuilder.php deleted file mode 100644 index 31246497fca92..0000000000000 --- a/app/code/Magento/AuthorizenetAcceptjs/Gateway/Request/CustomSettingsBuilder.php +++ /dev/null @@ -1,65 +0,0 @@ -subjectReader = $subjectReader; - $this->config = $config; - } - - /** - * @inheritdoc - */ - public function build(array $buildSubject): array - { - $result = []; - - if ($this->config->shouldEmailCustomer($this->subjectReader->readStoreId($buildSubject))) { - $result['transactionRequest'] = [ - 'transactionSettings' => [ - 'setting' => [ - [ - 'settingName' => 'emailCustomer', - 'settingValue' => 'true' - ] - ] - ] - ]; - } - - return $result; - } -} diff --git a/app/code/Magento/AuthorizenetAcceptjs/Gateway/Request/CustomerDataBuilder.php b/app/code/Magento/AuthorizenetAcceptjs/Gateway/Request/CustomerDataBuilder.php deleted file mode 100644 index cfdaa31552960..0000000000000 --- a/app/code/Magento/AuthorizenetAcceptjs/Gateway/Request/CustomerDataBuilder.php +++ /dev/null @@ -1,55 +0,0 @@ -subjectReader = $subjectReader; - } - - /** - * @inheritdoc - */ - public function build(array $buildSubject): array - { - $paymentDO = $this->subjectReader->readPayment($buildSubject); - $order = $paymentDO->getOrder(); - $billingAddress = $order->getBillingAddress(); - $result = [ - 'transactionRequest' => [ - 'customer' => [ - 'id' => $order->getCustomerId(), - 'email' => $billingAddress->getEmail() - ] - ] - ]; - - return $result; - } -} diff --git a/app/code/Magento/AuthorizenetAcceptjs/Gateway/Request/OrderDataBuilder.php b/app/code/Magento/AuthorizenetAcceptjs/Gateway/Request/OrderDataBuilder.php deleted file mode 100644 index bf0a15f552e6c..0000000000000 --- a/app/code/Magento/AuthorizenetAcceptjs/Gateway/Request/OrderDataBuilder.php +++ /dev/null @@ -1,51 +0,0 @@ -subjectReader = $subjectReader; - } - - /** - * @inheritdoc - */ - public function build(array $buildSubject): array - { - $paymentDO = $this->subjectReader->readPayment($buildSubject); - $order = $paymentDO->getOrder(); - - return [ - 'transactionRequest' => [ - 'order' => [ - 'invoiceNumber' => $order->getOrderIncrementId() - ] - ] - ]; - } -} diff --git a/app/code/Magento/AuthorizenetAcceptjs/Gateway/Request/PassthroughDataBuilder.php b/app/code/Magento/AuthorizenetAcceptjs/Gateway/Request/PassthroughDataBuilder.php deleted file mode 100644 index 6e6ef04972c78..0000000000000 --- a/app/code/Magento/AuthorizenetAcceptjs/Gateway/Request/PassthroughDataBuilder.php +++ /dev/null @@ -1,61 +0,0 @@ -passthroughData = $passthroughData; - } - - /** - * @inheritdoc - */ - public function build(array $buildSubject): array - { - $fields = []; - - foreach ($this->passthroughData->getData() as $key => $value) { - $fields[] = [ - 'name' => $key, - 'value' => $value - ]; - } - - if (!empty($fields)) { - return [ - 'transactionRequest' => [ - 'userFields' => [ - 'userField' => $fields - ] - ] - ]; - } - - return []; - } -} diff --git a/app/code/Magento/AuthorizenetAcceptjs/Gateway/Request/PaymentDataBuilder.php b/app/code/Magento/AuthorizenetAcceptjs/Gateway/Request/PaymentDataBuilder.php deleted file mode 100644 index 99955e9724577..0000000000000 --- a/app/code/Magento/AuthorizenetAcceptjs/Gateway/Request/PaymentDataBuilder.php +++ /dev/null @@ -1,59 +0,0 @@ -subjectReader = $subjectReader; - } - - /** - * @inheritdoc - */ - public function build(array $buildSubject): array - { - $paymentDO = $this->subjectReader->readPayment($buildSubject); - $payment = $paymentDO->getPayment(); - $data = []; - - if ($payment instanceof Payment) { - $dataDescriptor = $payment->getAdditionalInformation('opaqueDataDescriptor'); - $dataValue = $payment->getAdditionalInformation('opaqueDataValue'); - - $data['transactionRequest']['payment'] = [ - 'opaqueData' => [ - 'dataDescriptor' => $dataDescriptor, - 'dataValue' => $dataValue - ] - ]; - } - - return $data; - } -} diff --git a/app/code/Magento/AuthorizenetAcceptjs/Gateway/Request/PoDataBuilder.php b/app/code/Magento/AuthorizenetAcceptjs/Gateway/Request/PoDataBuilder.php deleted file mode 100644 index 9b56e0852af01..0000000000000 --- a/app/code/Magento/AuthorizenetAcceptjs/Gateway/Request/PoDataBuilder.php +++ /dev/null @@ -1,55 +0,0 @@ -subjectReader = $subjectReader; - } - - /** - * @inheritdoc - */ - public function build(array $buildSubject): array - { - $paymentDO = $this->subjectReader->readPayment($buildSubject); - $payment = $paymentDO->getPayment(); - $data = []; - - if ($payment instanceof Payment) { - $data = [ - 'transactionRequest' => [ - 'poNumber' => $payment->getPoNumber() - ] - ]; - } - - return $data; - } -} diff --git a/app/code/Magento/AuthorizenetAcceptjs/Gateway/Request/RefundPaymentDataBuilder.php b/app/code/Magento/AuthorizenetAcceptjs/Gateway/Request/RefundPaymentDataBuilder.php deleted file mode 100644 index ac5bcb08cb04a..0000000000000 --- a/app/code/Magento/AuthorizenetAcceptjs/Gateway/Request/RefundPaymentDataBuilder.php +++ /dev/null @@ -1,61 +0,0 @@ -subjectReader = $subjectReader; - } - - /** - * @inheritdoc - * @throws \Exception - */ - public function build(array $buildSubject): array - { - $paymentDO = $this->subjectReader->readPayment($buildSubject); - $payment = $paymentDO->getPayment(); - $data = []; - - if ($payment instanceof Payment) { - $data = [ - 'transactionRequest' => [ - 'payment' => [ - 'creditCard' => [ - 'cardNumber' => $payment->getAdditionalInformation('ccLast4'), - 'expirationDate' => 'XXXX' - ] - ] - ] - ]; - } - - return $data; - } -} diff --git a/app/code/Magento/AuthorizenetAcceptjs/Gateway/Request/RefundReferenceTransactionDataBuilder.php b/app/code/Magento/AuthorizenetAcceptjs/Gateway/Request/RefundReferenceTransactionDataBuilder.php deleted file mode 100644 index 65842354b7e2a..0000000000000 --- a/app/code/Magento/AuthorizenetAcceptjs/Gateway/Request/RefundReferenceTransactionDataBuilder.php +++ /dev/null @@ -1,56 +0,0 @@ -subjectReader = $subjectReader; - } - - /** - * @inheritdoc - */ - public function build(array $buildSubject): array - { - $paymentDO = $this->subjectReader->readPayment($buildSubject); - $payment = $paymentDO->getPayment(); - $data = []; - - if ($payment instanceof Payment) { - $transactionId = $payment->getAuthorizationTransaction()->getParentTxnId(); - $data = [ - 'transactionRequest' => [ - 'refTransId' => $transactionId - ] - ]; - } - - return $data; - } -} diff --git a/app/code/Magento/AuthorizenetAcceptjs/Gateway/Request/RefundTransactionTypeDataBuilder.php b/app/code/Magento/AuthorizenetAcceptjs/Gateway/Request/RefundTransactionTypeDataBuilder.php deleted file mode 100644 index 0f74299ebf5bd..0000000000000 --- a/app/code/Magento/AuthorizenetAcceptjs/Gateway/Request/RefundTransactionTypeDataBuilder.php +++ /dev/null @@ -1,34 +0,0 @@ - [ - 'transactionType' => self::REQUEST_TYPE_REFUND - ] - ]; - } -} diff --git a/app/code/Magento/AuthorizenetAcceptjs/Gateway/Request/RequestTypeBuilder.php b/app/code/Magento/AuthorizenetAcceptjs/Gateway/Request/RequestTypeBuilder.php deleted file mode 100644 index d20add70846b8..0000000000000 --- a/app/code/Magento/AuthorizenetAcceptjs/Gateway/Request/RequestTypeBuilder.php +++ /dev/null @@ -1,48 +0,0 @@ -type = $type; - } - - /** - * Adds the type of the request to the build subject - * - * @param array $buildSubject - * @return array - * - * @SuppressWarnings(PHPMD.UnusedFormalParameter) - */ - public function build(array $buildSubject): array - { - return [ - 'payload_type' => $this->type - ]; - } -} diff --git a/app/code/Magento/AuthorizenetAcceptjs/Gateway/Request/SaleDataBuilder.php b/app/code/Magento/AuthorizenetAcceptjs/Gateway/Request/SaleDataBuilder.php deleted file mode 100644 index 4402fb5af8c82..0000000000000 --- a/app/code/Magento/AuthorizenetAcceptjs/Gateway/Request/SaleDataBuilder.php +++ /dev/null @@ -1,72 +0,0 @@ -subjectReader = $subjectReader; - $this->passthroughData = $passthroughData; - } - - /** - * @inheritdoc - */ - public function build(array $buildSubject): array - { - $paymentDO = $this->subjectReader->readPayment($buildSubject); - $payment = $paymentDO->getPayment(); - $data = []; - - if ($payment instanceof Payment) { - $data = [ - 'transactionRequest' => [ - 'transactionType' => self::REQUEST_AUTH_AND_CAPTURE, - ] - ]; - - $this->passthroughData->setData( - 'transactionType', - $data['transactionRequest']['transactionType'] - ); - } - - return $data; - } -} diff --git a/app/code/Magento/AuthorizenetAcceptjs/Gateway/Request/ShippingDataBuilder.php b/app/code/Magento/AuthorizenetAcceptjs/Gateway/Request/ShippingDataBuilder.php deleted file mode 100644 index ea2cb89971fb5..0000000000000 --- a/app/code/Magento/AuthorizenetAcceptjs/Gateway/Request/ShippingDataBuilder.php +++ /dev/null @@ -1,59 +0,0 @@ -subjectReader = $subjectReader; - } - - /** - * @inheritdoc - */ - public function build(array $buildSubject): array - { - $paymentDO = $this->subjectReader->readPayment($buildSubject); - $payment = $paymentDO->getPayment(); - $order = $paymentDO->getOrder(); - $data = []; - - if ($payment instanceof Payment && $order instanceof Order) { - $data = [ - 'transactionRequest' => [ - 'shipping' => [ - 'amount' => $order->getBaseShippingAmount() - ] - ] - ]; - } - - return $data; - } -} diff --git a/app/code/Magento/AuthorizenetAcceptjs/Gateway/Request/SolutionDataBuilder.php b/app/code/Magento/AuthorizenetAcceptjs/Gateway/Request/SolutionDataBuilder.php deleted file mode 100644 index 8734c0ab454ce..0000000000000 --- a/app/code/Magento/AuthorizenetAcceptjs/Gateway/Request/SolutionDataBuilder.php +++ /dev/null @@ -1,56 +0,0 @@ -config = $config; - $this->subjectReader = $subjectReader; - } - - /** - * @inheritdoc - */ - public function build(array $buildSubject): array - { - return [ - 'transactionRequest' => [ - 'solution' => [ - 'id' => $this->config->getSolutionId($this->subjectReader->readStoreId($buildSubject)), - ] - ] - ]; - } -} diff --git a/app/code/Magento/AuthorizenetAcceptjs/Gateway/Request/StoreConfigBuilder.php b/app/code/Magento/AuthorizenetAcceptjs/Gateway/Request/StoreConfigBuilder.php deleted file mode 100644 index 396ad143466cd..0000000000000 --- a/app/code/Magento/AuthorizenetAcceptjs/Gateway/Request/StoreConfigBuilder.php +++ /dev/null @@ -1,46 +0,0 @@ -subjectReader = $subjectReader; - } - - /** - * @inheritdoc - */ - public function build(array $buildSubject): array - { - $paymentDO = $this->subjectReader->readPayment($buildSubject); - $order = $paymentDO->getOrder(); - - return [ - 'store_id' => $order->getStoreId() - ]; - } -} diff --git a/app/code/Magento/AuthorizenetAcceptjs/Gateway/Request/StubDataBuilder.php b/app/code/Magento/AuthorizenetAcceptjs/Gateway/Request/StubDataBuilder.php deleted file mode 100644 index a2766d97d9299..0000000000000 --- a/app/code/Magento/AuthorizenetAcceptjs/Gateway/Request/StubDataBuilder.php +++ /dev/null @@ -1,31 +0,0 @@ -subjectReader = $subjectReader; - } - - /** - * @inheritdoc - */ - public function build(array $buildSubject): array - { - $data = []; - - if (!empty($buildSubject['transactionId'])) { - $data = [ - 'transId' => $buildSubject['transactionId'] - ]; - } else { - $paymentDO = $this->subjectReader->readPayment($buildSubject); - $payment = $paymentDO->getPayment(); - - if ($payment instanceof Payment) { - $authorizationTransaction = $payment->getAuthorizationTransaction(); - - if (empty($authorizationTransaction)) { - $transactionId = $payment->getLastTransId(); - } else { - $transactionId = $authorizationTransaction->getParentTxnId(); - - if (empty($transactionId)) { - $transactionId = $authorizationTransaction->getTxnId(); - } - } - - $data = [ - 'transId' => $transactionId - ]; - } - } - - return $data; - } -} diff --git a/app/code/Magento/AuthorizenetAcceptjs/Gateway/Request/VoidDataBuilder.php b/app/code/Magento/AuthorizenetAcceptjs/Gateway/Request/VoidDataBuilder.php deleted file mode 100644 index c830f1f23d17c..0000000000000 --- a/app/code/Magento/AuthorizenetAcceptjs/Gateway/Request/VoidDataBuilder.php +++ /dev/null @@ -1,63 +0,0 @@ -subjectReader = $subjectReader; - } - - /** - * @inheritdoc - */ - public function build(array $buildSubject): array - { - $paymentDO = $this->subjectReader->readPayment($buildSubject); - $payment = $paymentDO->getPayment(); - $transactionData = []; - - if ($payment instanceof Payment) { - $authorizationTransaction = $payment->getAuthorizationTransaction(); - $refId = $authorizationTransaction->getAdditionalInformation('real_transaction_id'); - if (empty($refId)) { - $refId = $authorizationTransaction->getParentTxnId(); - } - - $transactionData['transactionRequest'] = [ - 'transactionType' => self::REQUEST_TYPE_VOID, - 'refTransId' => $refId - ]; - } - - return $transactionData; - } -} diff --git a/app/code/Magento/AuthorizenetAcceptjs/Gateway/Response/CloseParentTransactionHandler.php b/app/code/Magento/AuthorizenetAcceptjs/Gateway/Response/CloseParentTransactionHandler.php deleted file mode 100644 index 60c5bb21c0865..0000000000000 --- a/app/code/Magento/AuthorizenetAcceptjs/Gateway/Response/CloseParentTransactionHandler.php +++ /dev/null @@ -1,48 +0,0 @@ -subjectReader = $subjectReader; - } - - /** - * @inheritdoc - */ - public function handle(array $handlingSubject, array $response): void - { - $paymentDO = $this->subjectReader->readPayment($handlingSubject); - $payment = $paymentDO->getPayment(); - - if ($payment instanceof Payment) { - $payment->setShouldCloseParentTransaction(true); - } - } -} diff --git a/app/code/Magento/AuthorizenetAcceptjs/Gateway/Response/ClosePartialTransactionHandler.php b/app/code/Magento/AuthorizenetAcceptjs/Gateway/Response/ClosePartialTransactionHandler.php deleted file mode 100644 index 5279df56b5e28..0000000000000 --- a/app/code/Magento/AuthorizenetAcceptjs/Gateway/Response/ClosePartialTransactionHandler.php +++ /dev/null @@ -1,30 +0,0 @@ -getCreditmemo()->getInvoice()->canRefund(); - } -} diff --git a/app/code/Magento/AuthorizenetAcceptjs/Gateway/Response/CloseTransactionHandler.php b/app/code/Magento/AuthorizenetAcceptjs/Gateway/Response/CloseTransactionHandler.php deleted file mode 100644 index 2cccf255ab8e9..0000000000000 --- a/app/code/Magento/AuthorizenetAcceptjs/Gateway/Response/CloseTransactionHandler.php +++ /dev/null @@ -1,68 +0,0 @@ -subjectReader = $subjectReader; - $this->closeTransaction = $closeTransaction; - } - - /** - * @inheritdoc - */ - public function handle(array $handlingSubject, array $response): void - { - $paymentDO = $this->subjectReader->readPayment($handlingSubject); - $payment = $paymentDO->getPayment(); - - if ($payment instanceof Payment) { - $payment->setIsTransactionClosed($this->closeTransaction); - $payment->setShouldCloseParentTransaction($this->shouldCloseParentTransaction($payment)); - } - } - - /** - * Whether parent transaction should be closed. - * - * @param Payment $payment - * @return bool - * @SuppressWarnings(PHPMD.UnusedFormalParameter) - */ - public function shouldCloseParentTransaction(Payment $payment) - { - return true; - } -} diff --git a/app/code/Magento/AuthorizenetAcceptjs/Gateway/Response/PaymentResponseHandler.php b/app/code/Magento/AuthorizenetAcceptjs/Gateway/Response/PaymentResponseHandler.php deleted file mode 100644 index e0b192205012f..0000000000000 --- a/app/code/Magento/AuthorizenetAcceptjs/Gateway/Response/PaymentResponseHandler.php +++ /dev/null @@ -1,58 +0,0 @@ -subjectReader = $subjectReader; - } - - /** - * @inheritdoc - */ - public function handle(array $handlingSubject, array $response): void - { - $paymentDO = $this->subjectReader->readPayment($handlingSubject); - $payment = $paymentDO->getPayment(); - $transactionResponse = $response['transactionResponse']; - - if ($payment instanceof Payment) { - $payment->setCcLast4($payment->getAdditionalInformation('ccLast4')); - $payment->setCcAvsStatus($transactionResponse['avsResultCode']); - $payment->setIsTransactionClosed(false); - - if ($transactionResponse['responseCode'] == self::RESPONSE_CODE_HELD) { - $payment->setIsTransactionPending(true) - ->setIsFraudDetected(true); - } - } - } -} diff --git a/app/code/Magento/AuthorizenetAcceptjs/Gateway/Response/PaymentReviewStatusHandler.php b/app/code/Magento/AuthorizenetAcceptjs/Gateway/Response/PaymentReviewStatusHandler.php deleted file mode 100644 index 41c2ddd2b3271..0000000000000 --- a/app/code/Magento/AuthorizenetAcceptjs/Gateway/Response/PaymentReviewStatusHandler.php +++ /dev/null @@ -1,66 +0,0 @@ -subjectReader = $subjectReader; - } - - /** - * @inheritdoc - */ - public function handle(array $handlingSubject, array $response): void - { - $paymentDO = $this->subjectReader->readPayment($handlingSubject); - $payment = $paymentDO->getPayment(); - - if ($payment instanceof Payment) { - $paymentDO = $this->subjectReader->readPayment($handlingSubject); - $payment = $paymentDO->getPayment(); - - $status = $response['transaction']['transactionStatus']; - // This data is only used when updating the order payment via Get Payment Update - if (!in_array($status, self::REVIEW_PENDING_STATUSES)) { - $denied = in_array($status, self::REVIEW_DECLINED_STATUSES); - $payment->setData('is_transaction_denied', $denied); - $payment->setData('is_transaction_approved', !$denied); - } - } - } -} diff --git a/app/code/Magento/AuthorizenetAcceptjs/Gateway/Response/TransactionDetailsResponseHandler.php b/app/code/Magento/AuthorizenetAcceptjs/Gateway/Response/TransactionDetailsResponseHandler.php deleted file mode 100644 index 81bb9c92b15ed..0000000000000 --- a/app/code/Magento/AuthorizenetAcceptjs/Gateway/Response/TransactionDetailsResponseHandler.php +++ /dev/null @@ -1,68 +0,0 @@ -subjectReader = $subjectReader; - $this->config = $config; - } - - /** - * @inheritdoc - */ - public function handle(array $handlingSubject, array $response): void - { - $storeId = $this->subjectReader->readStoreId($handlingSubject); - $paymentDO = $this->subjectReader->readPayment($handlingSubject); - $payment = $paymentDO->getPayment(); - $transactionResponse = $response['transactionResponse']; - - if ($payment instanceof Payment) { - // Add the keys that should show in the transaction details interface - $additionalInformationKeys = $this->config->getAdditionalInfoKeys($storeId); - $rawDetails = []; - foreach ($additionalInformationKeys as $paymentInfoKey) { - if (isset($transactionResponse[$paymentInfoKey])) { - $rawDetails[$paymentInfoKey] = $transactionResponse[$paymentInfoKey]; - $payment->setAdditionalInformation($paymentInfoKey, $transactionResponse[$paymentInfoKey]); - } - } - $payment->setTransactionAdditionalInfo(Payment\Transaction::RAW_DETAILS, $rawDetails); - } - } -} diff --git a/app/code/Magento/AuthorizenetAcceptjs/Gateway/Response/TransactionIdHandler.php b/app/code/Magento/AuthorizenetAcceptjs/Gateway/Response/TransactionIdHandler.php deleted file mode 100644 index f3a9a0a1c4466..0000000000000 --- a/app/code/Magento/AuthorizenetAcceptjs/Gateway/Response/TransactionIdHandler.php +++ /dev/null @@ -1,57 +0,0 @@ -subjectReader = $subjectReader; - } - - /** - * @inheritdoc - */ - public function handle(array $handlingSubject, array $response): void - { - $paymentDO = $this->subjectReader->readPayment($handlingSubject); - $payment = $paymentDO->getPayment(); - $transactionResponse = $response['transactionResponse']; - - if ($payment instanceof Payment) { - if (!$payment->getParentTransactionId() - || $transactionResponse['transId'] != $payment->getParentTransactionId() - ) { - $payment->setTransactionId($transactionResponse['transId']); - } - $payment->setTransactionAdditionalInfo( - 'real_transaction_id', - $transactionResponse['transId'] - ); - } - } -} diff --git a/app/code/Magento/AuthorizenetAcceptjs/Gateway/Response/VoidResponseHandler.php b/app/code/Magento/AuthorizenetAcceptjs/Gateway/Response/VoidResponseHandler.php deleted file mode 100644 index 7bcb8c6c8dba1..0000000000000 --- a/app/code/Magento/AuthorizenetAcceptjs/Gateway/Response/VoidResponseHandler.php +++ /dev/null @@ -1,52 +0,0 @@ -subjectReader = $subjectReader; - } - - /** - * @inheritdoc - */ - public function handle(array $handlingSubject, array $response): void - { - $paymentDO = $this->subjectReader->readPayment($handlingSubject); - $payment = $paymentDO->getPayment(); - $transactionId = $response['transactionResponse']['transId']; - - if ($payment instanceof Payment) { - $payment->setIsTransactionClosed(true); - $payment->setShouldCloseParentTransaction(true); - $payment->setTransactionAdditionalInfo('real_transaction_id', $transactionId); - } - } -} diff --git a/app/code/Magento/AuthorizenetAcceptjs/Gateway/SubjectReader.php b/app/code/Magento/AuthorizenetAcceptjs/Gateway/SubjectReader.php deleted file mode 100644 index b5f1cef94ea46..0000000000000 --- a/app/code/Magento/AuthorizenetAcceptjs/Gateway/SubjectReader.php +++ /dev/null @@ -1,100 +0,0 @@ -readPayment($subject) - ->getOrder() - ->getStoreId(); - // phpcs:ignore Magento2.CodeAnalysis.EmptyBlock - } catch (\InvalidArgumentException $e) { - // No store id is current set - } - } - - return $storeId ? (int)$storeId : null; - } - - /** - * Reads amount from subject - * - * @param array $subject - * @return string - */ - public function readAmount(array $subject): string - { - return (string)Helper\SubjectReader::readAmount($subject); - } - - /** - * Reads response from subject - * - * @param array $subject - * @return array - */ - public function readResponse(array $subject): ?array - { - return Helper\SubjectReader::readResponse($subject); - } - - /** - * Reads login id from subject - * - * @param array $subject - * @return string|null - */ - public function readLoginId(array $subject): ?string - { - return $subject['merchantAuthentication']['name'] ?? null; - } - - /** - * Reads transaction key from subject - * - * @param array $subject - * @return string|null - */ - public function readTransactionKey(array $subject): ?string - { - return $subject['merchantAuthentication']['transactionKey'] ?? null; - } -} diff --git a/app/code/Magento/AuthorizenetAcceptjs/Gateway/Validator/GeneralResponseValidator.php b/app/code/Magento/AuthorizenetAcceptjs/Gateway/Validator/GeneralResponseValidator.php deleted file mode 100644 index 47065ed96c240..0000000000000 --- a/app/code/Magento/AuthorizenetAcceptjs/Gateway/Validator/GeneralResponseValidator.php +++ /dev/null @@ -1,82 +0,0 @@ -resultFactory = $resultFactory; - $this->subjectReader = $subjectReader; - } - - /** - * @inheritdoc - */ - public function validate(array $validationSubject): ResultInterface - { - $response = $this->subjectReader->readResponse($validationSubject); - $isValid = (isset($response['messages']['resultCode']) - && $response['messages']['resultCode'] === self::RESULT_CODE_SUCCESS); - $errorCodes = []; - $errorMessages = []; - - if (!$isValid) { - if (isset($response['messages']['message']['code'])) { - $errorCodes[] = $response['messages']['message']['code']; - $errorMessages[] = $response['messages']['message']['text']; - } elseif (isset($response['messages']['message'])) { - foreach ($response['messages']['message'] as $message) { - $errorCodes[] = $message['code']; - $errorMessages[] = $message['text']; - } - } elseif (isset($response['errors']['error'])) { - foreach ($response['errors']['error'] as $message) { - $errorCodes[] = $message['errorCode']; - $errorMessages[] = $message['errorText']; - } - } - } - - return $this->createResult($isValid, $errorMessages, $errorCodes); - } -} diff --git a/app/code/Magento/AuthorizenetAcceptjs/Gateway/Validator/TransactionHashValidator.php b/app/code/Magento/AuthorizenetAcceptjs/Gateway/Validator/TransactionHashValidator.php deleted file mode 100644 index c11e22110d952..0000000000000 --- a/app/code/Magento/AuthorizenetAcceptjs/Gateway/Validator/TransactionHashValidator.php +++ /dev/null @@ -1,201 +0,0 @@ -subjectReader = $subjectReader; - $this->config = $config; - } - - /** - * Validates the transaction hash matches the configured hash - * - * @param array $validationSubject - * @return ResultInterface - */ - public function validate(array $validationSubject): ResultInterface - { - $response = $this->subjectReader->readResponse($validationSubject); - $storeId = $this->subjectReader->readStoreId($validationSubject); - - if (!empty($response['transactionResponse']['transHashSha2'])) { - return $this->validateHash( - $validationSubject, - $this->config->getTransactionSignatureKey($storeId), - 'transHashSha2', - 'generateSha512Hash' - ); - } elseif (!empty($response['transactionResponse']['transHash'])) { - return $this->validateHash( - $validationSubject, - $this->config->getLegacyTransactionHash($storeId), - 'transHash', - 'generateMd5Hash' - ); - } - - return $this->createResult( - false, - [ - __('The authenticity of the gateway response could not be verified.') - ], - [self::ERROR_TRANSACTION_HASH] - ); - } - - /** - * Validates the response again the legacy MD5 spec - * - * @param array $validationSubject - * @param string $storedHash - * @param string $hashField - * @param string $generateFunction - * @return ResultInterface - */ - private function validateHash( - array $validationSubject, - string $storedHash, - string $hashField, - string $generateFunction - ): ResultInterface { - $storeId = $this->subjectReader->readStoreId($validationSubject); - $response = $this->subjectReader->readResponse($validationSubject); - $transactionResponse = $response['transactionResponse']; - - /* - * Authorize.net is inconsistent with how they hash and heuristically trying to detect whether or not they used - * the amount to calculate the hash is risky because their responses are incorrect in some cases. - * Refund uses the amount when referencing a transaction but will use 0 when refunding without a reference. - * Non-refund reference transactions such as (void/capture) don't use the amount. Authorize/auth&capture - * transactions will use amount but if there is an AVS error the response will indicate the transaction was a - * reference transaction so this can't be heuristically detected by looking at combinations of refTransID - * and transId (yes they also mixed the letter casing for "id"). Their documentation doesn't talk about this - * and to make this even better, none of their official SDKs support the new hash field to compare - * implementations. Therefore the only way to safely validate this hash without failing for even more - * unexpected corner cases we simply need to validate with and without the amount. - */ - try { - $amount = $this->subjectReader->readAmount($validationSubject); - } catch (\InvalidArgumentException $e) { - $amount = 0; - } - - $hash = $this->{$generateFunction}( - $storedHash, - $this->config->getLoginId($storeId), - sprintf('%.2F', $amount), - $transactionResponse['transId'] ?? '' - ); - $valid = Security::compareStrings($hash, $transactionResponse[$hashField]); - - if (!$valid && $amount > 0) { - $hash = $this->{$generateFunction}( - $storedHash, - $this->config->getLoginId($storeId), - '0.00', - $transactionResponse['transId'] ?? '' - ); - $valid = Security::compareStrings($hash, $transactionResponse[$hashField]); - } - - if ($valid) { - return $this->createResult(true); - } - - return $this->createResult( - false, - [ - __('The authenticity of the gateway response could not be verified.') - ], - [self::ERROR_TRANSACTION_HASH] - ); - } - - /** - * Generates a Md5 hash to compare against AuthNet's. - * - * @param string $merchantMd5 - * @param string $merchantApiLogin - * @param string $amount - * @param string $transactionId - * @return string - * @SuppressWarnings(PHPMD.UnusedPrivateMethod) - */ - private function generateMd5Hash( - $merchantMd5, - $merchantApiLogin, - $amount, - $transactionId - ) { - // phpcs:disable Magento2.Security.InsecureFunction - return strtoupper(md5($merchantMd5 . $merchantApiLogin . $transactionId . $amount)); - } - - /** - * Generates a SHA-512 hash to compare against AuthNet's. - * - * @param string $merchantKey - * @param string $merchantApiLogin - * @param string $amount - * @param string $transactionId - * @return string - * @SuppressWarnings(PHPMD.UnusedPrivateMethod) - */ - private function generateSha512Hash( - $merchantKey, - $merchantApiLogin, - $amount, - $transactionId - ) { - $message = '^' . $merchantApiLogin . '^' . $transactionId . '^' . $amount . '^'; - - return strtoupper(hash_hmac('sha512', $message, pack('H*', $merchantKey))); - } -} diff --git a/app/code/Magento/AuthorizenetAcceptjs/Gateway/Validator/TransactionResponseValidator.php b/app/code/Magento/AuthorizenetAcceptjs/Gateway/Validator/TransactionResponseValidator.php deleted file mode 100644 index 8238aa37dcc0a..0000000000000 --- a/app/code/Magento/AuthorizenetAcceptjs/Gateway/Validator/TransactionResponseValidator.php +++ /dev/null @@ -1,102 +0,0 @@ -subjectReader = $subjectReader; - } - - /** - * @inheritdoc - */ - public function validate(array $validationSubject): ResultInterface - { - $response = $this->subjectReader->readResponse($validationSubject); - $transactionResponse = $response['transactionResponse']; - - if ($this->isResponseCodeAnError($transactionResponse)) { - $errorCodes = []; - $errorMessages = []; - - if (isset($transactionResponse['messages']['message']['code'])) { - $errorCodes[] = $transactionResponse['messages']['message']['code']; - $errorMessages[] = $transactionResponse['messages']['message']['text']; - } elseif (isset($transactionResponse['messages']['message'])) { - foreach ($transactionResponse['messages']['message'] as $message) { - $errorCodes[] = $message['code']; - $errorMessages[] = $message['description']; - } - } elseif (isset($transactionResponse['errors'])) { - foreach ($transactionResponse['errors'] as $message) { - $errorCodes[] = $message['errorCode']; - $errorMessages[] = $message['errorText']; - } - } - - return $this->createResult(false, $errorMessages, $errorCodes); - } - - return $this->createResult(true); - } - - /** - * Determines if the response code is actually an error - * - * @param array $transactionResponse - * @return bool - */ - private function isResponseCodeAnError(array $transactionResponse): bool - { - $code = $transactionResponse['messages']['message']['code'] - ?? $transactionResponse['messages']['message'][0]['code'] - ?? $transactionResponse['errors'][0]['errorCode'] - ?? null; - - return !in_array($transactionResponse['responseCode'], [self::RESPONSE_CODE_APPROVED, self::RESPONSE_CODE_HELD]) - || $code - && !in_array( - $code, - [ - self::RESPONSE_REASON_CODE_APPROVED, - self::RESPONSE_REASON_CODE_PENDING_REVIEW, - self::RESPONSE_REASON_CODE_PENDING_REVIEW_AUTHORIZED - ] - ); - } -} diff --git a/app/code/Magento/AuthorizenetAcceptjs/Model/Adminhtml/Source/Cctype.php b/app/code/Magento/AuthorizenetAcceptjs/Model/Adminhtml/Source/Cctype.php deleted file mode 100644 index cdd1745a6bc1e..0000000000000 --- a/app/code/Magento/AuthorizenetAcceptjs/Model/Adminhtml/Source/Cctype.php +++ /dev/null @@ -1,28 +0,0 @@ - self::ENVIRONMENT_SANDBOX, - 'label' => 'Sandbox', - ], - [ - 'value' => self::ENVIRONMENT_PRODUCTION, - 'label' => 'Production' - ] - ]; - } -} diff --git a/app/code/Magento/AuthorizenetAcceptjs/Model/Adminhtml/Source/PaymentAction.php b/app/code/Magento/AuthorizenetAcceptjs/Model/Adminhtml/Source/PaymentAction.php deleted file mode 100644 index 953841604bfee..0000000000000 --- a/app/code/Magento/AuthorizenetAcceptjs/Model/Adminhtml/Source/PaymentAction.php +++ /dev/null @@ -1,35 +0,0 @@ - 'authorize', - 'label' => __('Authorize Only'), - ], - [ - 'value' => 'authorize_capture', - 'label' => __('Authorize and Capture') - ] - ]; - } -} diff --git a/app/code/Magento/AuthorizenetAcceptjs/Model/PassthroughDataObject.php b/app/code/Magento/AuthorizenetAcceptjs/Model/PassthroughDataObject.php deleted file mode 100644 index 145d8c000e8f7..0000000000000 --- a/app/code/Magento/AuthorizenetAcceptjs/Model/PassthroughDataObject.php +++ /dev/null @@ -1,22 +0,0 @@ -config = $config; - $this->cart = $cart; - } - - /** - * Retrieve assoc array of checkout configuration - * - * @return array - */ - public function getConfig() - { - $storeId = $this->cart->getStoreId(); - - return [ - 'payment' => [ - Config::METHOD => [ - 'clientKey' => $this->config->getClientKey($storeId), - 'apiLoginID' => $this->config->getLoginId($storeId), - 'environment' => $this->config->getEnvironment($storeId), - 'useCvv' => $this->config->isCvvEnabled($storeId), - ] - ] - ]; - } -} diff --git a/app/code/Magento/AuthorizenetAcceptjs/Observer/DataAssignObserver.php b/app/code/Magento/AuthorizenetAcceptjs/Observer/DataAssignObserver.php deleted file mode 100644 index 0f989bb032175..0000000000000 --- a/app/code/Magento/AuthorizenetAcceptjs/Observer/DataAssignObserver.php +++ /dev/null @@ -1,55 +0,0 @@ -readDataArgument($observer); - - $additionalData = $data->getData(PaymentInterface::KEY_ADDITIONAL_DATA); - if (!is_array($additionalData)) { - return; - } - - $paymentInfo = $this->readPaymentModelArgument($observer); - - foreach ($this->additionalInformationList as $additionalInformationKey) { - if (isset($additionalData[$additionalInformationKey])) { - $paymentInfo->setAdditionalInformation( - $additionalInformationKey, - $additionalData[$additionalInformationKey] - ); - } - } - } -} diff --git a/app/code/Magento/AuthorizenetAcceptjs/README.md b/app/code/Magento/AuthorizenetAcceptjs/README.md deleted file mode 100644 index b507f97a5a223..0000000000000 --- a/app/code/Magento/AuthorizenetAcceptjs/README.md +++ /dev/null @@ -1,29 +0,0 @@ -# Magento_AuthorizenetAcceptjs module - -The Magento_AuthorizenetAcceptjs module implements the integration with the Authorize.Net payment gateway and makes the latter available as a payment method in Magento. - -## Installation details - -Before disabling or uninstalling this module, note that the `Magento_AuthorizenetCardinal` module depends on this module. - -For information about module installation in Magento 2, see [Enable or disable modules](https://devdocs.magento.com/guides/v2.3/install-gde/install/cli/install-cli-subcommands-enable.html). - -## Structure - -`Gateway/` - the directory that contains payment gateway command interfaces and service classes. - -For information about typical file structure of a module in Magento 2, see [Module file structure](http://devdocs.magento.com/guides/v2.3/extension-dev-guide/build/module-file-structure.html#module-file-structure). - -## Extensibility - -Extension developers can interact with the Magento_AuthorizenetAcceptjs module. For more information about the Magento extension mechanism, see [Magento plug-ins](https://devdocs.magento.com/guides/v2.3/extension-dev-guide/plugins.html). - -[The Magento dependency injection mechanism](https://devdocs.magento.com/guides/v2.3/extension-dev-guide/depend-inj.html) enables you to override the functionality of the Magento_AuthorizenetAcceptjs module. - -### Events - -This module observes the following events: - -- `payment_method_assign_data_authorizenet_acceptjs` event in the `Magento\AuthorizenetAcceptjs\Observer\DataAssignObserver` file. - -For information about an event in Magento 2, see [Events and observers](https://devdocs.magento.com/guides/v2.3/extension-dev-guide/events-and-observers.html#events). diff --git a/app/code/Magento/AuthorizenetAcceptjs/Setup/Patch/Data/CopyCurrentConfig.php b/app/code/Magento/AuthorizenetAcceptjs/Setup/Patch/Data/CopyCurrentConfig.php deleted file mode 100644 index aa699569c61f6..0000000000000 --- a/app/code/Magento/AuthorizenetAcceptjs/Setup/Patch/Data/CopyCurrentConfig.php +++ /dev/null @@ -1,233 +0,0 @@ -scopeConfig = $scopeConfig; - $this->resourceConfig = $resourceConfig; - $this->encryptor = $encryptor; - $this->moduleDataSetup = $moduleDataSetup; - $this->storeManager = $storeManager; - } - - /** - * @inheritdoc - */ - public function apply(): void - { - $this->moduleDataSetup->startSetup(); - $this->migrateDefaultValues(); - $this->migrateWebsiteValues(); - $this->moduleDataSetup->endSetup(); - } - - /** - * Migrate configuration values from DirectPost to Accept.js on default scope - * - * @return void - */ - private function migrateDefaultValues(): void - { - foreach ($this->configFieldsToMigrate as $field) { - $configValue = $this->getOldConfigValue($field); - - if (!empty($configValue)) { - $this->saveNewConfigValue($field, $configValue); - } - } - - foreach ($this->encryptedConfigFieldsToMigrate as $field) { - $configValue = $this->getOldConfigValue($field); - - if (!empty($configValue)) { - $this->saveNewConfigValue( - $field, - $configValue, - ScopeConfigInterface::SCOPE_TYPE_DEFAULT, - 0, - true - ); - } - } - } - - /** - * Migrate configuration values from DirectPost to Accept.js on all website scopes - * - * @return void - */ - private function migrateWebsiteValues(): void - { - foreach ($this->storeManager->getWebsites() as $website) { - $websiteID = (int) $website->getId(); - - foreach ($this->configFieldsToMigrate as $field) { - $configValue = $this->getOldConfigValue($field, ScopeInterface::SCOPE_WEBSITES, $websiteID); - - if (!empty($configValue)) { - $this->saveNewConfigValue($field, $configValue, ScopeInterface::SCOPE_WEBSITES, $websiteID); - } - } - - foreach ($this->encryptedConfigFieldsToMigrate as $field) { - $configValue = $this->getOldConfigValue($field, ScopeInterface::SCOPE_WEBSITES, $websiteID); - - if (!empty($configValue)) { - $this->saveNewConfigValue($field, $configValue, ScopeInterface::SCOPE_WEBSITES, $websiteID, true); - } - } - } - } - - /** - * Get old configuration value from the DirectPost module's configuration on the store scope - * - * @param string $field - * @param string $scope - * @param int $scopeID - * @return mixed - */ - private function getOldConfigValue( - string $field, - string $scope = ScopeConfigInterface::SCOPE_TYPE_DEFAULT, - int $scopeID = null - ) { - return $this->scopeConfig->getValue( - sprintf(self::PAYMENT_PATH_FORMAT, self::DIRECTPOST_PATH, $field), - $scope, - $scopeID - ); - } - - /** - * Save configuration value for AcceptJS - * - * @param string $field - * @param mixed $value - * @param string $scope - * @param int $scopeID - * @param bool $isEncrypted - * @return void - */ - private function saveNewConfigValue( - string $field, - $value, - string $scope = ScopeConfigInterface::SCOPE_TYPE_DEFAULT, - int $scopeID = 0, - bool $isEncrypted = false - ): void { - $value = $isEncrypted ? $this->encryptor->encrypt($value) : $value; - - $this->resourceConfig->saveConfig( - sprintf(self::PAYMENT_PATH_FORMAT, self::ACCEPTJS_PATH, $field), - $value, - $scope, - $scopeID - ); - } - - /** - * @inheritdoc - */ - public static function getDependencies() - { - return []; - } - - /** - * @inheritdoc - */ - public function getAliases() - { - return []; - } -} diff --git a/app/code/Magento/AuthorizenetAcceptjs/Test/Mftf/LICENSE.txt b/app/code/Magento/AuthorizenetAcceptjs/Test/Mftf/LICENSE.txt deleted file mode 100644 index 49525fd99da9c..0000000000000 --- a/app/code/Magento/AuthorizenetAcceptjs/Test/Mftf/LICENSE.txt +++ /dev/null @@ -1,48 +0,0 @@ - -Open Software License ("OSL") v. 3.0 - -This Open Software License (the "License") applies to any original work of authorship (the "Original Work") whose owner (the "Licensor") has placed the following licensing notice adjacent to the copyright notice for the Original Work: - -Licensed under the Open Software License version 3.0 - - 1. Grant of Copyright License. Licensor grants You a worldwide, royalty-free, non-exclusive, sublicensable license, for the duration of the copyright, to do the following: - - 1. to reproduce the Original Work in copies, either alone or as part of a collective work; - - 2. to translate, adapt, alter, transform, modify, or arrange the Original Work, thereby creating derivative works ("Derivative Works") based upon the Original Work; - - 3. to distribute or communicate copies of the Original Work and Derivative Works to the public, with the proviso that copies of Original Work or Derivative Works that You distribute or communicate shall be licensed under this Open Software License; - - 4. to perform the Original Work publicly; and - - 5. to display the Original Work publicly. - - 2. Grant of Patent License. Licensor grants You a worldwide, royalty-free, non-exclusive, sublicensable license, under patent claims owned or controlled by the Licensor that are embodied in the Original Work as furnished by the Licensor, for the duration of the patents, to make, use, sell, offer for sale, have made, and import the Original Work and Derivative Works. - - 3. Grant of Source Code License. The term "Source Code" means the preferred form of the Original Work for making modifications to it and all available documentation describing how to modify the Original Work. Licensor agrees to provide a machine-readable copy of the Source Code of the Original Work along with each copy of the Original Work that Licensor distributes. Licensor reserves the right to satisfy this obligation by placing a machine-readable copy of the Source Code in an information repository reasonably calculated to permit inexpensive and convenient access by You for as long as Licensor continues to distribute the Original Work. - - 4. Exclusions From License Grant. Neither the names of Licensor, nor the names of any contributors to the Original Work, nor any of their trademarks or service marks, may be used to endorse or promote products derived from this Original Work without express prior permission of the Licensor. Except as expressly stated herein, nothing in this License grants any license to Licensor's trademarks, copyrights, patents, trade secrets or any other intellectual property. No patent license is granted to make, use, sell, offer for sale, have made, or import embodiments of any patent claims other than the licensed claims defined in Section 2. No license is granted to the trademarks of Licensor even if such marks are included in the Original Work. Nothing in this License shall be interpreted to prohibit Licensor from licensing under terms different from this License any Original Work that Licensor otherwise would have a right to license. - - 5. External Deployment. The term "External Deployment" means the use, distribution, or communication of the Original Work or Derivative Works in any way such that the Original Work or Derivative Works may be used by anyone other than You, whether those works are distributed or communicated to those persons or made available as an application intended for use over a network. As an express condition for the grants of license hereunder, You must treat any External Deployment by You of the Original Work or a Derivative Work as a distribution under section 1(c). - - 6. Attribution Rights. You must retain, in the Source Code of any Derivative Works that You create, all copyright, patent, or trademark notices from the Source Code of the Original Work, as well as any notices of licensing and any descriptive text identified therein as an "Attribution Notice." You must cause the Source Code for any Derivative Works that You create to carry a prominent Attribution Notice reasonably calculated to inform recipients that You have modified the Original Work. - - 7. Warranty of Provenance and Disclaimer of Warranty. Licensor warrants that the copyright in and to the Original Work and the patent rights granted herein by Licensor are owned by the Licensor or are sublicensed to You under the terms of this License with the permission of the contributor(s) of those copyrights and patent rights. Except as expressly stated in the immediately preceding sentence, the Original Work is provided under this License on an "AS IS" BASIS and WITHOUT WARRANTY, either express or implied, including, without limitation, the warranties of non-infringement, merchantability or fitness for a particular purpose. THE ENTIRE RISK AS TO THE QUALITY OF THE ORIGINAL WORK IS WITH YOU. This DISCLAIMER OF WARRANTY constitutes an essential part of this License. No license to the Original Work is granted by this License except under this disclaimer. - - 8. Limitation of Liability. Under no circumstances and under no legal theory, whether in tort (including negligence), contract, or otherwise, shall the Licensor be liable to anyone for any indirect, special, incidental, or consequential damages of any character arising as a result of this License or the use of the Original Work including, without limitation, damages for loss of goodwill, work stoppage, computer failure or malfunction, or any and all other commercial damages or losses. This limitation of liability shall not apply to the extent applicable law prohibits such limitation. - - 9. Acceptance and Termination. If, at any time, You expressly assented to this License, that assent indicates your clear and irrevocable acceptance of this License and all of its terms and conditions. If You distribute or communicate copies of the Original Work or a Derivative Work, You must make a reasonable effort under the circumstances to obtain the express assent of recipients to the terms of this License. This License conditions your rights to undertake the activities listed in Section 1, including your right to create Derivative Works based upon the Original Work, and doing so without honoring these terms and conditions is prohibited by copyright law and international treaty. Nothing in this License is intended to affect copyright exceptions and limitations (including 'fair use' or 'fair dealing'). This License shall terminate immediately and You may no longer exercise any of the rights granted to You by this License upon your failure to honor the conditions in Section 1(c). - - 10. Termination for Patent Action. This License shall terminate automatically and You may no longer exercise any of the rights granted to You by this License as of the date You commence an action, including a cross-claim or counterclaim, against Licensor or any licensee alleging that the Original Work infringes a patent. This termination provision shall not apply for an action alleging patent infringement by combinations of the Original Work with other software or hardware. - - 11. Jurisdiction, Venue and Governing Law. Any action or suit relating to this License may be brought only in the courts of a jurisdiction wherein the Licensor resides or in which Licensor conducts its primary business, and under the laws of that jurisdiction excluding its conflict-of-law provisions. The application of the United Nations Convention on Contracts for the International Sale of Goods is expressly excluded. Any use of the Original Work outside the scope of this License or after its termination shall be subject to the requirements and penalties of copyright or patent law in the appropriate jurisdiction. This section shall survive the termination of this License. - - 12. Attorneys' Fees. In any action to enforce the terms of this License or seeking damages relating thereto, the prevailing party shall be entitled to recover its costs and expenses, including, without limitation, reasonable attorneys' fees and costs incurred in connection with such action, including any appeal of such action. This section shall survive the termination of this License. - - 13. Miscellaneous. If any provision of this License is held to be unenforceable, such provision shall be reformed only to the extent necessary to make it enforceable. - - 14. Definition of "You" in This License. "You" throughout this License, whether in upper or lower case, means an individual or a legal entity exercising rights under, and complying with all of the terms of, this License. For legal entities, "You" includes any entity that controls, is controlled by, or is under common control with you. For purposes of this definition, "control" means (i) the power, direct or indirect, to cause the direction or management of such entity, whether by contract or otherwise, or (ii) ownership of fifty percent (50%) or more of the outstanding shares, or (iii) beneficial ownership of such entity. - - 15. Right to Use. You may use the Original Work in all ways not otherwise restricted or conditioned by this License or by law, and Licensor promises not to interfere with or be responsible for such uses by You. - - 16. Modification of This License. This License is Copyright (C) 2005 Lawrence Rosen. Permission is granted to copy, distribute, or communicate this License without modification. Nothing in this License permits You to modify this License as applied to the Original Work or to Derivative Works. However, You may modify the text of this License and copy, distribute or communicate your modified version (the "Modified License") and apply it to other original works of authorship subject to the following conditions: (i) You may not indicate in any way that your Modified License is the "Open Software License" or "OSL" and you may not use those names in the name of your Modified License; (ii) You must replace the notice specified in the first paragraph above with the notice "Licensed under " or with a notice of your own that is not confusingly similar to the notice in this License; and (iii) You may not claim that your original works are open source software unless your Modified License has been approved by Open Source Initiative (OSI) and You comply with its license review and certification process. \ No newline at end of file diff --git a/app/code/Magento/AuthorizenetAcceptjs/Test/Mftf/LICENSE_AFL.txt b/app/code/Magento/AuthorizenetAcceptjs/Test/Mftf/LICENSE_AFL.txt deleted file mode 100644 index f39d641b18a19..0000000000000 --- a/app/code/Magento/AuthorizenetAcceptjs/Test/Mftf/LICENSE_AFL.txt +++ /dev/null @@ -1,48 +0,0 @@ - -Academic Free License ("AFL") v. 3.0 - -This Academic Free License (the "License") applies to any original work of authorship (the "Original Work") whose owner (the "Licensor") has placed the following licensing notice adjacent to the copyright notice for the Original Work: - -Licensed under the Academic Free License version 3.0 - - 1. Grant of Copyright License. Licensor grants You a worldwide, royalty-free, non-exclusive, sublicensable license, for the duration of the copyright, to do the following: - - 1. to reproduce the Original Work in copies, either alone or as part of a collective work; - - 2. to translate, adapt, alter, transform, modify, or arrange the Original Work, thereby creating derivative works ("Derivative Works") based upon the Original Work; - - 3. to distribute or communicate copies of the Original Work and Derivative Works to the public, under any license of your choice that does not contradict the terms and conditions, including Licensor's reserved rights and remedies, in this Academic Free License; - - 4. to perform the Original Work publicly; and - - 5. to display the Original Work publicly. - - 2. Grant of Patent License. Licensor grants You a worldwide, royalty-free, non-exclusive, sublicensable license, under patent claims owned or controlled by the Licensor that are embodied in the Original Work as furnished by the Licensor, for the duration of the patents, to make, use, sell, offer for sale, have made, and import the Original Work and Derivative Works. - - 3. Grant of Source Code License. The term "Source Code" means the preferred form of the Original Work for making modifications to it and all available documentation describing how to modify the Original Work. Licensor agrees to provide a machine-readable copy of the Source Code of the Original Work along with each copy of the Original Work that Licensor distributes. Licensor reserves the right to satisfy this obligation by placing a machine-readable copy of the Source Code in an information repository reasonably calculated to permit inexpensive and convenient access by You for as long as Licensor continues to distribute the Original Work. - - 4. Exclusions From License Grant. Neither the names of Licensor, nor the names of any contributors to the Original Work, nor any of their trademarks or service marks, may be used to endorse or promote products derived from this Original Work without express prior permission of the Licensor. Except as expressly stated herein, nothing in this License grants any license to Licensor's trademarks, copyrights, patents, trade secrets or any other intellectual property. No patent license is granted to make, use, sell, offer for sale, have made, or import embodiments of any patent claims other than the licensed claims defined in Section 2. No license is granted to the trademarks of Licensor even if such marks are included in the Original Work. Nothing in this License shall be interpreted to prohibit Licensor from licensing under terms different from this License any Original Work that Licensor otherwise would have a right to license. - - 5. External Deployment. The term "External Deployment" means the use, distribution, or communication of the Original Work or Derivative Works in any way such that the Original Work or Derivative Works may be used by anyone other than You, whether those works are distributed or communicated to those persons or made available as an application intended for use over a network. As an express condition for the grants of license hereunder, You must treat any External Deployment by You of the Original Work or a Derivative Work as a distribution under section 1(c). - - 6. Attribution Rights. You must retain, in the Source Code of any Derivative Works that You create, all copyright, patent, or trademark notices from the Source Code of the Original Work, as well as any notices of licensing and any descriptive text identified therein as an "Attribution Notice." You must cause the Source Code for any Derivative Works that You create to carry a prominent Attribution Notice reasonably calculated to inform recipients that You have modified the Original Work. - - 7. Warranty of Provenance and Disclaimer of Warranty. Licensor warrants that the copyright in and to the Original Work and the patent rights granted herein by Licensor are owned by the Licensor or are sublicensed to You under the terms of this License with the permission of the contributor(s) of those copyrights and patent rights. Except as expressly stated in the immediately preceding sentence, the Original Work is provided under this License on an "AS IS" BASIS and WITHOUT WARRANTY, either express or implied, including, without limitation, the warranties of non-infringement, merchantability or fitness for a particular purpose. THE ENTIRE RISK AS TO THE QUALITY OF THE ORIGINAL WORK IS WITH YOU. This DISCLAIMER OF WARRANTY constitutes an essential part of this License. No license to the Original Work is granted by this License except under this disclaimer. - - 8. Limitation of Liability. Under no circumstances and under no legal theory, whether in tort (including negligence), contract, or otherwise, shall the Licensor be liable to anyone for any indirect, special, incidental, or consequential damages of any character arising as a result of this License or the use of the Original Work including, without limitation, damages for loss of goodwill, work stoppage, computer failure or malfunction, or any and all other commercial damages or losses. This limitation of liability shall not apply to the extent applicable law prohibits such limitation. - - 9. Acceptance and Termination. If, at any time, You expressly assented to this License, that assent indicates your clear and irrevocable acceptance of this License and all of its terms and conditions. If You distribute or communicate copies of the Original Work or a Derivative Work, You must make a reasonable effort under the circumstances to obtain the express assent of recipients to the terms of this License. This License conditions your rights to undertake the activities listed in Section 1, including your right to create Derivative Works based upon the Original Work, and doing so without honoring these terms and conditions is prohibited by copyright law and international treaty. Nothing in this License is intended to affect copyright exceptions and limitations (including "fair use" or "fair dealing"). This License shall terminate immediately and You may no longer exercise any of the rights granted to You by this License upon your failure to honor the conditions in Section 1(c). - - 10. Termination for Patent Action. This License shall terminate automatically and You may no longer exercise any of the rights granted to You by this License as of the date You commence an action, including a cross-claim or counterclaim, against Licensor or any licensee alleging that the Original Work infringes a patent. This termination provision shall not apply for an action alleging patent infringement by combinations of the Original Work with other software or hardware. - - 11. Jurisdiction, Venue and Governing Law. Any action or suit relating to this License may be brought only in the courts of a jurisdiction wherein the Licensor resides or in which Licensor conducts its primary business, and under the laws of that jurisdiction excluding its conflict-of-law provisions. The application of the United Nations Convention on Contracts for the International Sale of Goods is expressly excluded. Any use of the Original Work outside the scope of this License or after its termination shall be subject to the requirements and penalties of copyright or patent law in the appropriate jurisdiction. This section shall survive the termination of this License. - - 12. Attorneys' Fees. In any action to enforce the terms of this License or seeking damages relating thereto, the prevailing party shall be entitled to recover its costs and expenses, including, without limitation, reasonable attorneys' fees and costs incurred in connection with such action, including any appeal of such action. This section shall survive the termination of this License. - - 13. Miscellaneous. If any provision of this License is held to be unenforceable, such provision shall be reformed only to the extent necessary to make it enforceable. - - 14. Definition of "You" in This License. "You" throughout this License, whether in upper or lower case, means an individual or a legal entity exercising rights under, and complying with all of the terms of, this License. For legal entities, "You" includes any entity that controls, is controlled by, or is under common control with you. For purposes of this definition, "control" means (i) the power, direct or indirect, to cause the direction or management of such entity, whether by contract or otherwise, or (ii) ownership of fifty percent (50%) or more of the outstanding shares, or (iii) beneficial ownership of such entity. - - 15. Right to Use. You may use the Original Work in all ways not otherwise restricted or conditioned by this License or by law, and Licensor promises not to interfere with or be responsible for such uses by You. - - 16. Modification of This License. This License is Copyright © 2005 Lawrence Rosen. Permission is granted to copy, distribute, or communicate this License without modification. Nothing in this License permits You to modify this License as applied to the Original Work or to Derivative Works. However, You may modify the text of this License and copy, distribute or communicate your modified version (the "Modified License") and apply it to other original works of authorship subject to the following conditions: (i) You may not indicate in any way that your Modified License is the "Academic Free License" or "AFL" and you may not use those names in the name of your Modified License; (ii) You must replace the notice specified in the first paragraph above with the notice "Licensed under " or with a notice of your own that is not confusingly similar to the notice in this License; and (iii) You may not claim that your original works are open source software unless your Modified License has been approved by Open Source Initiative (OSI) and You comply with its license review and certification process. diff --git a/app/code/Magento/AuthorizenetAcceptjs/Test/Mftf/README.md b/app/code/Magento/AuthorizenetAcceptjs/Test/Mftf/README.md deleted file mode 100644 index aba235e2cfad9..0000000000000 --- a/app/code/Magento/AuthorizenetAcceptjs/Test/Mftf/README.md +++ /dev/null @@ -1,3 +0,0 @@ -# AuthorizenetAcceptjs Functional Tests - -The Functional Test Module for **Magento AuthorizenetAcceptjs** module. diff --git a/app/code/Magento/AuthorizenetAcceptjs/Test/Unit/Block/FormTest.php b/app/code/Magento/AuthorizenetAcceptjs/Test/Unit/Block/FormTest.php deleted file mode 100644 index 020b651aaaf17..0000000000000 --- a/app/code/Magento/AuthorizenetAcceptjs/Test/Unit/Block/FormTest.php +++ /dev/null @@ -1,59 +0,0 @@ -createMock(Context::class); - $this->configMock = $this->createMock(Config::class); - $quoteMock = $this->getMockBuilder(Quote::class) - ->disableOriginalConstructor() - ->setMethods(['getStoreId']) - ->getMock(); - $quoteMock->method('getStoreId') - ->willReturn('123'); - $paymentConfig = $this->createMock(PaymentConfig::class); - - $this->block = new Form( - $contextMock, - $paymentConfig, - $this->configMock, - $quoteMock - ); - } - - public function testIsCvvEnabled() - { - $this->configMock->method('isCvvEnabled') - ->with('123') - ->willReturn(true); - $this->assertTrue($this->block->isCvvEnabled()); - } -} diff --git a/app/code/Magento/AuthorizenetAcceptjs/Test/Unit/Block/InfoTest.php b/app/code/Magento/AuthorizenetAcceptjs/Test/Unit/Block/InfoTest.php deleted file mode 100644 index 70dfb140e1576..0000000000000 --- a/app/code/Magento/AuthorizenetAcceptjs/Test/Unit/Block/InfoTest.php +++ /dev/null @@ -1,80 +0,0 @@ -createMock(Context::class); - /** @var Config|MockObject|InvocationMocker $configMock */ - $configMock = $this->createMock(ConfigInterface::class); - $block = new Info($contextMock, $configMock); - /** @var InfoInterface|MockObject|InvocationMocker $payment */ - $payment = $this->createMock(InfoInterface::class); - /** @var RendererInterface|MockObject|InvocationMocker $translationRenderer */ - $translationRenderer = $this->createMock(RendererInterface::class); - - // only foo should be used - $configMock->method('getValue') - ->willReturnMap([ - ['paymentInfoKeys', null, 'foo'], - ['privateInfoKeys', null, ''] - ]); - - // Give more info to ensure only foo is translated - $payment->method('getAdditionalInformation') - ->willReturnCallback(function ($name = null) { - $info = [ - 'foo' => 'bar', - 'baz' => 'bash' - ]; - - if (empty($name)) { - return $info; - } - - return $info[$name]; - }); - - // Foo should be translated to Super Cool String - $translationRenderer->method('render') - ->with(['foo'], []) - ->willReturn('Super Cool String'); - - $previousRenderer = Phrase::getRenderer(); - Phrase::setRenderer($translationRenderer); - - try { - $block->setData('info', $payment); - - $info = $block->getSpecificInformation(); - } finally { - // No matter what, restore the renderer - Phrase::setRenderer($previousRenderer); - } - - // Assert the label was correctly translated - $this->assertSame($info['Super Cool String'], 'bar'); - $this->assertArrayNotHasKey('foo', $info); - $this->assertArrayNotHasKey('baz', $info); - } -} diff --git a/app/code/Magento/AuthorizenetAcceptjs/Test/Unit/Block/PaymentTest.php b/app/code/Magento/AuthorizenetAcceptjs/Test/Unit/Block/PaymentTest.php deleted file mode 100644 index 11ae27f9d2ea7..0000000000000 --- a/app/code/Magento/AuthorizenetAcceptjs/Test/Unit/Block/PaymentTest.php +++ /dev/null @@ -1,53 +0,0 @@ -createMock(Context::class); - $this->configMock = $this->createMock(ConfigProvider::class); - $this->block = new Payment($contextMock, $this->configMock, new Json()); - } - - public function testConfigIsCreated() - { - $this->configMock->method('getConfig') - ->willReturn([ - 'payment' => [ - 'authorizenet_acceptjs' => [ - 'foo' => 'bar' - ] - ] - ]); - - $result = $this->block->getPaymentConfig(); - $expected = '{"foo":"bar","code":"authorizenet_acceptjs"}'; - $this->assertEquals($expected, $result); - } -} diff --git a/app/code/Magento/AuthorizenetAcceptjs/Test/Unit/Gateway/Command/AcceptPaymentStrategyCommandTest.php b/app/code/Magento/AuthorizenetAcceptjs/Test/Unit/Gateway/Command/AcceptPaymentStrategyCommandTest.php deleted file mode 100644 index 316fef5443360..0000000000000 --- a/app/code/Magento/AuthorizenetAcceptjs/Test/Unit/Gateway/Command/AcceptPaymentStrategyCommandTest.php +++ /dev/null @@ -1,131 +0,0 @@ -transactionDetailsCommandMock = $this->createMock(CommandInterface::class); - $this->commandMock = $this->createMock(CommandInterface::class); - $this->transactionResultMock = $this->createMock(ResultInterface::class); - $this->commandPoolMock = $this->createMock(CommandPoolInterface::class); - $this->command = new AcceptPaymentStrategyCommand( - $this->commandPoolMock, - new SubjectReader() - ); - } - - /** - * @param string $status - * @dataProvider inReviewStatusesProvider - */ - public function testCommandWillAcceptInTheGatewayWhenInFDSReview(string $status) - { - // Assert command is executed - $this->commandMock->expects($this->once()) - ->method('execute'); - - $this->commandPoolMock->method('get') - ->willReturnMap([ - ['get_transaction_details', $this->transactionDetailsCommandMock], - ['accept_fds', $this->commandMock] - ]); - - $this->transactionResultMock->method('get') - ->willReturn([ - 'transaction' => [ - 'transactionStatus' => $status - ] - ]); - - $buildSubject = [ - 'foo' => '123' - ]; - - $this->transactionDetailsCommandMock->expects($this->once()) - ->method('execute') - ->with($buildSubject) - ->willReturn($this->transactionResultMock); - - $this->command->execute($buildSubject); - } - - public function testCommandWillDoNothingWhenTransactionHasAlreadyBeenAuthorized() - { - // Assert command is never executed - $this->commandMock->expects($this->never()) - ->method('execute'); - - $this->commandPoolMock->method('get') - ->willReturnMap([ - ['get_transaction_details', $this->transactionDetailsCommandMock], - ]); - - $this->transactionResultMock->method('get') - ->willReturn([ - 'transaction' => [ - 'transactionStatus' => 'anythingelseisfine' - ] - ]); - - $buildSubject = [ - 'foo' => '123' - ]; - - $this->transactionDetailsCommandMock->expects($this->once()) - ->method('execute') - ->with($buildSubject) - ->willReturn($this->transactionResultMock); - - $this->command->execute($buildSubject); - } - - public function inReviewStatusesProvider() - { - return [ - ['FDSPendingReview'], - ['FDSAuthorizedPendingReview'] - ]; - } -} diff --git a/app/code/Magento/AuthorizenetAcceptjs/Test/Unit/Gateway/Command/CaptureStrategyCommandTest.php b/app/code/Magento/AuthorizenetAcceptjs/Test/Unit/Gateway/Command/CaptureStrategyCommandTest.php deleted file mode 100644 index 4cbded9764793..0000000000000 --- a/app/code/Magento/AuthorizenetAcceptjs/Test/Unit/Gateway/Command/CaptureStrategyCommandTest.php +++ /dev/null @@ -1,181 +0,0 @@ -paymentDOMock = $this->createMock(PaymentDataObject::class); - $this->paymentMock = $this->createMock(Payment::class); - $this->paymentDOMock->method('getPayment') - ->willReturn($this->paymentMock); - $this->commandMock = $this->createMock(GatewayCommand::class); - $this->commandPoolMock = $this->createMock(CommandPoolInterface::class); - $this->searchCriteriaBuilderMock = $this->createMock(SearchCriteriaBuilder::class); - $this->transactionRepositoryMock = $this->createMock(TransactionRepositoryInterface::class); - - // The search criteria builder should return the criteria with the specified filters - $this->filterBuilderMock = $this->createMock(FilterBuilder::class); - // We aren't coupling the implementation to the test. The test only cares how the result is processed - $this->filterBuilderMock->method('setField') - ->willReturnSelf(); - $this->filterBuilderMock->method('setValue') - ->willReturnSelf(); - $searchCriteria = new SearchCriteria(); - $this->searchCriteriaBuilderMock->method('addFilters') - ->willReturnSelf(); - $this->searchCriteriaBuilderMock->method('create') - ->willReturn($searchCriteria); - // The transaction result can be customized per test to simulate different scenarios - $this->transactionsResult = $this->createMock(TransactionSearchResultInterface::class); - $this->transactionRepositoryMock->method('getList') - ->with($searchCriteria) - ->willReturn($this->transactionsResult); - - $this->strategyCommand = new CaptureStrategyCommand( - $this->commandPoolMock, - $this->transactionRepositoryMock, - $this->filterBuilderMock, - $this->searchCriteriaBuilderMock, - new SubjectReader() - ); - } - - public function testExecuteWillAuthorizeWhenNotAuthorizedAndNotCaptured() - { - $subject = ['payment' => $this->paymentDOMock]; - - // Hasn't been authorized - $this->paymentMock->method('getAuthorizationTransaction') - ->willReturn(false); - // Hasn't been captured - $this->transactionsResult->method('getTotalCount') - ->willReturn(0); - // Assert authorize command was used - $this->commandPoolMock->expects($this->once()) - ->method('get') - ->with('sale') - ->willReturn($this->commandMock); - // Assert execute was called and with correct data - $this->commandMock->expects($this->once()) - ->method('execute') - ->with($subject); - - $this->strategyCommand->execute($subject); - // Assertions are performed via mock expects above - } - - public function testExecuteWillAuthorizeAndCaptureWhenAlreadyCaptured() - { - $subject = ['payment' => $this->paymentDOMock]; - - // Already authorized - $this->paymentMock->method('getAuthorizationTransaction') - ->willReturn(true); - // And already captured - $this->transactionsResult->method('getTotalCount') - ->willReturn(1); - // Assert authorize command was used - $this->commandPoolMock->expects($this->once()) - ->method('get') - ->with('settle') - ->willReturn($this->commandMock); - // Assert execute was called and with correct data - $this->commandMock->expects($this->once()) - ->method('execute') - ->with($subject); - - $this->strategyCommand->execute($subject); - // Assertions are performed via mock expects above - } - - public function testExecuteWillCaptureWhenAlreadyAuthorizedButNotCaptured() - { - $subject = ['payment' => $this->paymentDOMock]; - - // Was already authorized - $this->paymentMock->method('getAuthorizationTransaction') - ->willReturn(true); - // But, hasn't been captured - $this->transactionsResult->method('getTotalCount') - ->willReturn(0); - // Assert authorize command was used - $this->commandPoolMock->expects($this->once()) - ->method('get') - ->with('settle') - ->willReturn($this->commandMock); - // Assert execute was called and with correct data - $this->commandMock->expects($this->once()) - ->method('execute') - ->with($subject); - - $this->strategyCommand->execute($subject); - // Assertions are performed via mock expects above - } -} diff --git a/app/code/Magento/AuthorizenetAcceptjs/Test/Unit/Gateway/Command/FetchTransactionInfoCommandTest.php b/app/code/Magento/AuthorizenetAcceptjs/Test/Unit/Gateway/Command/FetchTransactionInfoCommandTest.php deleted file mode 100644 index 757500c7e50eb..0000000000000 --- a/app/code/Magento/AuthorizenetAcceptjs/Test/Unit/Gateway/Command/FetchTransactionInfoCommandTest.php +++ /dev/null @@ -1,132 +0,0 @@ -paymentDOMock = $this->createMock(PaymentDataObject::class); - $this->paymentMock = $this->createMock(Payment::class); - $this->paymentDOMock->method('getPayment') - ->willReturn($this->paymentMock); - $this->configMock = $this->createMock(Config::class); - $this->configMock->method('getTransactionInfoSyncKeys') - ->willReturn(['foo', 'bar']); - $orderMock = $this->createMock(Order::class); - $this->paymentDOMock->method('getOrder') - ->willReturn($orderMock); - $this->transactionDetailsCommandMock = $this->createMock(CommandInterface::class); - $this->transactionResultMock = $this->createMock(ResultInterface::class); - $this->commandPoolMock = $this->createMock(CommandPoolInterface::class); - $this->handlerMock = $this->createMock(HandlerInterface::class); - $this->command = new FetchTransactionInfoCommand( - $this->commandPoolMock, - new SubjectReader(), - $this->configMock, - $this->handlerMock - ); - } - - public function testCommandWillMarkTransactionAsApprovedWhenNotVoid() - { - $response = [ - 'transaction' => [ - 'transactionStatus' => 'authorizedPendingCapture', - 'foo' => 'abc', - 'bar' => 'cba', - 'dontreturnme' => 'justdont' - ] - ]; - - $this->commandPoolMock->method('get') - ->willReturnMap([ - ['get_transaction_details', $this->transactionDetailsCommandMock], - ]); - - $this->transactionResultMock->method('get') - ->willReturn($response); - - $buildSubject = [ - 'payment' => $this->paymentDOMock - ]; - - $this->transactionDetailsCommandMock->expects($this->once()) - ->method('execute') - ->with($buildSubject) - ->willReturn($this->transactionResultMock); - - $this->handlerMock->expects($this->once()) - ->method('handle') - ->with($buildSubject, $response) - ->willReturn($this->transactionResultMock); - - $result = $this->command->execute($buildSubject); - - $expected = [ - 'foo' => 'abc', - 'bar' => 'cba' - ]; - - $this->assertSame($expected, $result); - } -} diff --git a/app/code/Magento/AuthorizenetAcceptjs/Test/Unit/Gateway/Command/GatewayQueryCommandTest.php b/app/code/Magento/AuthorizenetAcceptjs/Test/Unit/Gateway/Command/GatewayQueryCommandTest.php deleted file mode 100644 index e37db34936385..0000000000000 --- a/app/code/Magento/AuthorizenetAcceptjs/Test/Unit/Gateway/Command/GatewayQueryCommandTest.php +++ /dev/null @@ -1,196 +0,0 @@ -requestBuilderMock = $this->createMock(BuilderInterface::class); - $this->transferFactoryMock = $this->createMock(TransferFactoryInterface::class); - $this->transferMock = $this->createMock(TransferInterface::class); - $this->clientMock = $this->createMock(ClientInterface::class); - $this->loggerMock = $this->createMock(LoggerInterface::class); - $this->validatorMock = $this->createMock(ValidatorInterface::class); - - $this->command = new GatewayQueryCommand( - $this->requestBuilderMock, - $this->transferFactoryMock, - $this->clientMock, - $this->loggerMock, - $this->validatorMock - ); - } - - public function testNormalExecution() - { - $buildSubject = [ - 'foo' => '123' - ]; - - $request = [ - 'bar' => '321' - ]; - - $response = [ - 'transaction' => [ - 'transactionType' => 'foo', - 'transactionStatus' => 'bar', - 'responseCode' => 'baz' - ] - ]; - - $validationSubject = $buildSubject; - $validationSubject['response'] = $response; - - $this->requestBuilderMock->method('build') - ->with($buildSubject) - ->willReturn($request); - - $this->transferFactoryMock->method('create') - ->with($request) - ->willReturn($this->transferMock); - - $this->clientMock->method('placeRequest') - ->with($this->transferMock) - ->willReturn($response); - - $this->validatorMock->method('validate') - ->with($validationSubject) - ->willReturn(new Result(true)); - - $result = $this->command->execute($buildSubject); - - $this->assertInstanceOf(ArrayResult::class, $result); - $this->assertEquals($response, $result->get()); - } - - /** - * @expectedExceptionMessage There was an error while trying to process the request. - * @expectedException \Magento\Payment\Gateway\Command\CommandException - */ - public function testExceptionIsThrownAndLoggedWhenRequestFails() - { - $buildSubject = [ - 'foo' => '123' - ]; - - $request = [ - 'bar' => '321' - ]; - - $this->requestBuilderMock->method('build') - ->with($buildSubject) - ->willReturn($request); - - $this->transferFactoryMock->method('create') - ->with($request) - ->willReturn($this->transferMock); - - $e = new \Exception('foobar'); - - $this->clientMock->method('placeRequest') - ->with($this->transferMock) - ->willThrowException($e); - - // assert the exception is logged - $this->loggerMock->expects($this->once()) - ->method('critical') - ->with($e); - - $this->command->execute($buildSubject); - } - /** - * @expectedExceptionMessage There was an error while trying to process the request. - * @expectedException \Magento\Payment\Gateway\Command\CommandException - */ - public function testExceptionIsThrownWhenResponseIsInvalid() - { - $buildSubject = [ - 'foo' => '123' - ]; - - $request = [ - 'bar' => '321' - ]; - - $response = [ - 'baz' => '456' - ]; - - $validationSubject = $buildSubject; - $validationSubject['response'] = $response; - - $this->requestBuilderMock->method('build') - ->with($buildSubject) - ->willReturn($request); - - $this->transferFactoryMock->method('create') - ->with($request) - ->willReturn($this->transferMock); - - $this->clientMock->method('placeRequest') - ->with($this->transferMock) - ->willReturn($response); - - $this->validatorMock->method('validate') - ->with($validationSubject) - ->willReturn(new Result(false)); - - $this->command->execute($buildSubject); - } -} diff --git a/app/code/Magento/AuthorizenetAcceptjs/Test/Unit/Gateway/Command/RefundTransactionStrategyCommandTest.php b/app/code/Magento/AuthorizenetAcceptjs/Test/Unit/Gateway/Command/RefundTransactionStrategyCommandTest.php deleted file mode 100644 index 79477b06e0e6c..0000000000000 --- a/app/code/Magento/AuthorizenetAcceptjs/Test/Unit/Gateway/Command/RefundTransactionStrategyCommandTest.php +++ /dev/null @@ -1,225 +0,0 @@ -transactionDetailsCommandMock = $this->createMock(CommandInterface::class); - $this->commandMock = $this->createMock(CommandInterface::class); - $this->transactionResultMock = $this->createMock(ResultInterface::class); - $this->commandPoolMock = $this->createMock(CommandPoolInterface::class); - $this->command = new RefundTransactionStrategyCommand( - $this->commandPoolMock, - new SubjectReader() - ); - } - - public function testCommandWillVoidWhenTransactionIsPendingSettlement() - { - // Assert command is executed - $this->commandMock->expects($this->once()) - ->method('execute'); - - $this->commandPoolMock->method('get') - ->willReturnMap( - [ - [ - 'get_transaction_details', - $this->transactionDetailsCommandMock - ], - [ - 'void', - $this->commandMock - ] - ] - ); - - $this->transactionResultMock->method('get') - ->willReturn( - [ - 'transaction' => [ - 'transactionStatus' => 'capturedPendingSettlement', - 'authAmount' => '20.19', - ] - ] - ); - - $buildSubject = [ - 'foo' => '123', - 'amount' => '20.19', - ]; - - $this->transactionDetailsCommandMock->expects($this->once()) - ->method('execute') - ->with($buildSubject) - ->willReturn($this->transactionResultMock); - - $this->command->execute($buildSubject); - } - - /** - * @expectedException \Magento\Payment\Gateway\Command\CommandException - * @expectedExceptionMessage The transaction has not been settled, a partial refund is not yet available. - */ - public function testCommandWillThrowExceptionWhenVoidTransactionIsPartial() - { - // Assert command is executed - $this->commandMock->expects($this->never()) - ->method('execute'); - - $this->commandPoolMock->method('get') - ->willReturnMap( - [ - [ - 'get_transaction_details', - $this->transactionDetailsCommandMock - ], - ] - ); - - $this->transactionResultMock->method('get') - ->willReturn( - [ - 'transaction' => [ - 'transactionStatus' => 'capturedPendingSettlement', - 'authAmount' => '20.19', - ] - ] - ); - - $buildSubject = [ - 'foo' => '123', - 'amount' => '10.19', - ]; - - $this->transactionDetailsCommandMock->expects($this->once()) - ->method('execute') - ->with($buildSubject) - ->willReturn($this->transactionResultMock); - - $this->command->execute($buildSubject); - } - - public function testCommandWillRefundWhenTransactionIsSettled() - { - // Assert command is executed - $this->commandMock->expects($this->once()) - ->method('execute'); - - $this->commandPoolMock->method('get') - ->willReturnMap( - [ - [ - 'get_transaction_details', - $this->transactionDetailsCommandMock - ], - [ - 'refund_settled', - $this->commandMock - ] - ] - ); - - $this->transactionResultMock->method('get') - ->willReturn( - [ - 'transaction' => [ - 'transactionStatus' => 'settledSuccessfully' - ] - ] - ); - - $buildSubject = [ - 'foo' => '123' - ]; - - $this->transactionDetailsCommandMock->expects($this->once()) - ->method('execute') - ->with($buildSubject) - ->willReturn($this->transactionResultMock); - - $this->command->execute($buildSubject); - } - - /** - * @expectedException \Magento\Payment\Gateway\Command\CommandException - * @expectedExceptionMessage This transaction cannot be refunded with its current status. - */ - public function testCommandWillThrowExceptionWhenTransactionIsInInvalidState() - { - // Assert command is never executed - $this->commandMock->expects($this->never()) - ->method('execute'); - - $this->commandPoolMock->method('get') - ->willReturnMap( - [ - [ - 'get_transaction_details', - $this->transactionDetailsCommandMock - ], - ] - ); - - $this->transactionResultMock->method('get') - ->willReturn( - [ - 'transaction' => [ - 'transactionStatus' => 'somethingIsWrong' - ] - ] - ); - - $buildSubject = [ - 'foo' => '123' - ]; - - $this->transactionDetailsCommandMock->expects($this->once()) - ->method('execute') - ->with($buildSubject) - ->willReturn($this->transactionResultMock); - - $this->command->execute($buildSubject); - } -} diff --git a/app/code/Magento/AuthorizenetAcceptjs/Test/Unit/Gateway/ConfigTest.php b/app/code/Magento/AuthorizenetAcceptjs/Test/Unit/Gateway/ConfigTest.php deleted file mode 100644 index 646ad4f195b9d..0000000000000 --- a/app/code/Magento/AuthorizenetAcceptjs/Test/Unit/Gateway/ConfigTest.php +++ /dev/null @@ -1,136 +0,0 @@ -scopeConfigMock = $this->createMock(ScopeConfigInterface::class); - - $objectManager = new ObjectManager($this); - $this->model = $objectManager->getObject( - Config::class, - [ - 'scopeConfig' => $this->scopeConfigMock, - 'methodCode' => Config::METHOD, - ] - ); - } - - /** - * @param $getterName - * @param $configField - * @param $configValue - * @param $expectedValue - * @dataProvider configMapProvider - */ - public function testConfigGetters($getterName, $configField, $configValue, $expectedValue) - { - $this->scopeConfigMock->method('getValue') - ->with($this->getPath($configField), ScopeInterface::SCOPE_STORE, 123) - ->willReturn($configValue); - $this->assertEquals($expectedValue, $this->model->{$getterName}(123)); - } - - /** - * @dataProvider environmentUrlProvider - * @param $environment - * @param $expectedUrl - */ - public function testGetApiUrl($environment, $expectedUrl) - { - $this->scopeConfigMock->method('getValue') - ->with($this->getPath('environment'), ScopeInterface::SCOPE_STORE, 123) - ->willReturn($environment); - $this->assertEquals($expectedUrl, $this->model->getApiUrl(123)); - } - - /** - * @dataProvider environmentSolutionProvider - * @param $environment - * @param $expectedSolution - */ - public function testGetSolutionIdSandbox($environment, $expectedSolution) - { - $this->scopeConfigMock->method('getValue') - ->with($this->getPath('environment'), ScopeInterface::SCOPE_STORE, 123) - ->willReturn($environment); - $this->assertEquals($expectedSolution, $this->model->getSolutionId(123)); - } - - /** - * @return array - */ - public function configMapProvider() - { - return [ - ['getLoginId', 'login', 'username', 'username'], - ['getEnvironment', 'environment', 'production', 'production'], - ['getClientKey', 'public_client_key', 'abc', 'abc'], - ['getTransactionKey', 'trans_key', 'password', 'password'], - ['getLegacyTransactionHash', 'trans_md5', 'abc123', 'abc123'], - ['getTransactionSignatureKey', 'trans_signature_key', 'abc123', 'abc123'], - ['getPaymentAction', 'payment_action', 'authorize', 'authorize'], - ['shouldEmailCustomer', 'email_customer', true, true], - ['isCvvEnabled', 'cvv_enabled', true, true], - ['getAdditionalInfoKeys', 'paymentInfoKeys', 'a,b,c', ['a', 'b', 'c']], - ['getTransactionInfoSyncKeys', 'transactionSyncKeys', 'a,b,c', ['a', 'b', 'c']], - ]; - } - - /** - * @return array - */ - public function environmentUrlProvider() - { - return [ - ['sandbox', 'https://apitest.authorize.net/xml/v1/request.api'], - ['production', 'https://api.authorize.net/xml/v1/request.api'], - ]; - } - - /** - * @return array - */ - public function environmentSolutionProvider() - { - return [ - ['sandbox', 'AAA102993'], - ['production', 'AAA175350'], - ]; - } - - /** - * Return config path - * - * @param string $field - * @return string - */ - private function getPath($field) - { - return sprintf(Config::DEFAULT_PATH_PATTERN, Config::METHOD, $field); - } -} diff --git a/app/code/Magento/AuthorizenetAcceptjs/Test/Unit/Gateway/Http/ClientTest.php b/app/code/Magento/AuthorizenetAcceptjs/Test/Unit/Gateway/Http/ClientTest.php deleted file mode 100644 index 4086195ff4c95..0000000000000 --- a/app/code/Magento/AuthorizenetAcceptjs/Test/Unit/Gateway/Http/ClientTest.php +++ /dev/null @@ -1,218 +0,0 @@ -objectManager = new ObjectManager($this); - $this->paymentLogger = $this->createMock(Logger::class); - $this->httpClientFactory = $this->createMock(ZendClientFactory::class); - $this->httpClient = $this->createMock(Zend_Http_Client::class); - $this->httpResponse = $this->createMock(Zend_Http_Response::class); - $this->httpClientFactory->method('create')->will($this->returnValue($this->httpClient)); - $this->httpClient->method('request') - ->willReturn($this->httpResponse); - /** @var MockObject $logger */ - $this->logger = $this->createMock(LoggerInterface::class); - } - - public function testCanSendRequest() - { - // Assert the raw data was set on the client - $this->httpClient->expects($this->once()) - ->method('setRawData') - ->with( - '{"doSomeThing":{"foobar":"baz"}}', - 'application/json' - ); - - $request = [ - 'payload_type' => 'doSomeThing', - 'foobar' => 'baz' - ]; - // Authorize.net returns a BOM and refuses to fix it - $response = pack('CCC', 0xef, 0xbb, 0xbf) . '{"foo":{"bar":"baz"}}'; - - $this->httpResponse->method('getBody') - ->willReturn($response); - - // Assert the logger was given the data - $this->paymentLogger->expects($this->once()) - ->method('debug') - ->with(['request' => $request, 'response' => '{"foo":{"bar":"baz"}}']); - - /** - * @var $apiClient Client - */ - $apiClient = $this->objectManager->getObject(Client::class, [ - 'httpClientFactory' => $this->httpClientFactory, - 'paymentLogger' => $this->paymentLogger, - 'json' => new Json() - ]); - - $result = $apiClient->placeRequest($this->getTransferObjectMock($request)); - - $this->assertSame('baz', $result['foo']['bar']); - } - - /** - * @expectedException \Magento\Framework\Exception\LocalizedException - * @expectedExceptionMessage Something went wrong in the payment gateway. - */ - public function testExceptionIsThrownWhenEmptyResponseIsReceived() - { - // Assert the client has the raw data set - $this->httpClient->expects($this->once()) - ->method('setRawData') - ->with( - '{"doSomeThing":{"foobar":"baz"}}', - 'application/json' - ); - - $this->httpResponse->method('getBody') - ->willReturn(''); - - // Assert the exception is given to the logger - $this->logger->expects($this->once()) - ->method('critical') - ->with($this->callback(function ($e) { - return $e instanceof \Exception - && $e->getMessage() === 'Invalid JSON was returned by the gateway'; - })); - - $request = [ - 'payload_type' => 'doSomeThing', - 'foobar' => 'baz' - ]; - - // Assert the logger was given the data - $this->paymentLogger->expects($this->once()) - ->method('debug') - ->with(['request' => $request, 'response' => '']); - - /** - * @var $apiClient Client - */ - $apiClient = $this->objectManager->getObject(Client::class, [ - 'httpClientFactory' => $this->httpClientFactory, - 'paymentLogger' => $this->paymentLogger, - 'logger' => $this->logger, - 'json' => new Json() - ]); - - $apiClient->placeRequest($this->getTransferObjectMock($request)); - } - - /** - * @expectedException \Magento\Framework\Exception\LocalizedException - * @expectedExceptionMessage Something went wrong in the payment gateway. - */ - public function testExceptionIsThrownWhenInvalidResponseIsReceived() - { - // Assert the client was given the raw data - $this->httpClient->expects($this->once()) - ->method('setRawData') - ->with( - '{"doSomeThing":{"foobar":"baz"}}', - 'application/json' - ); - - $this->httpResponse->method('getBody') - ->willReturn('bad'); - - $request = [ - 'payload_type' => 'doSomeThing', - 'foobar' => 'baz' - ]; - - // Assert the logger was given the data - $this->paymentLogger->expects($this->once()) - ->method('debug') - ->with(['request' => $request, 'response' => 'bad']); - - // Assert the exception was given to the logger - $this->logger->expects($this->once()) - ->method('critical') - ->with($this->callback(function ($e) { - return $e instanceof \Exception - && $e->getMessage() === 'Invalid JSON was returned by the gateway'; - })); - - /** - * @var $apiClient Client - */ - $apiClient = $this->objectManager->getObject(Client::class, [ - 'httpClientFactory' => $this->httpClientFactory, - 'paymentLogger' => $this->paymentLogger, - 'logger' => $this->logger, - 'json' => new Json() - ]); - - $apiClient->placeRequest($this->getTransferObjectMock($request)); - } - - /** - * Creates mock object for TransferInterface. - * - * @return TransferInterface|MockObject - */ - private function getTransferObjectMock(array $data) - { - $transferObjectMock = $this->createMock(TransferInterface::class); - $transferObjectMock->method('getBody') - ->willReturn($data); - - return $transferObjectMock; - } -} diff --git a/app/code/Magento/AuthorizenetAcceptjs/Test/Unit/Gateway/Http/Payload/Filter/RemoveFieldsFilterTest.php b/app/code/Magento/AuthorizenetAcceptjs/Test/Unit/Gateway/Http/Payload/Filter/RemoveFieldsFilterTest.php deleted file mode 100644 index bcc6279f5b1fe..0000000000000 --- a/app/code/Magento/AuthorizenetAcceptjs/Test/Unit/Gateway/Http/Payload/Filter/RemoveFieldsFilterTest.php +++ /dev/null @@ -1,36 +0,0 @@ -filter([ - 'some' => 123, - 'data' => 321, - 'foo' => 'to', - 'filter' => ['blah'], - 'bar' => 'fields from' - ]); - - $expected = [ - 'some' => 123, - 'data' => 321, - 'filter' => ['blah'], - ]; - - $this->assertEquals($expected, $actual); - } -} diff --git a/app/code/Magento/AuthorizenetAcceptjs/Test/Unit/Gateway/Http/TransferFactoryTest.php b/app/code/Magento/AuthorizenetAcceptjs/Test/Unit/Gateway/Http/TransferFactoryTest.php deleted file mode 100644 index 954fd9782bd3f..0000000000000 --- a/app/code/Magento/AuthorizenetAcceptjs/Test/Unit/Gateway/Http/TransferFactoryTest.php +++ /dev/null @@ -1,73 +0,0 @@ -transferBuilder = $this->createMock(TransferBuilder::class); - $this->transferMock = $this->createMock(TransferInterface::class); - $this->filterMock = $this->createMock(RemoveFieldsFilter::class); - - $this->transferFactory = new TransferFactory( - $this->transferBuilder, - [$this->filterMock] - ); - } - - public function testCreate() - { - $request = ['data1', 'data2']; - - // Assert the filter was created - $this->filterMock->expects($this->once()) - ->method('filter') - ->with($request) - ->willReturn($request); - - // Assert the body of the transfer was set - $this->transferBuilder->expects($this->once()) - ->method('setBody') - ->with($request) - ->willReturnSelf(); - - $this->transferBuilder->method('build') - ->willReturn($this->transferMock); - - $this->assertEquals($this->transferMock, $this->transferFactory->create($request)); - } -} diff --git a/app/code/Magento/AuthorizenetAcceptjs/Test/Unit/Gateway/Request/AcceptFdsDataBuilderTest.php b/app/code/Magento/AuthorizenetAcceptjs/Test/Unit/Gateway/Request/AcceptFdsDataBuilderTest.php deleted file mode 100644 index 00bb7ee84f98b..0000000000000 --- a/app/code/Magento/AuthorizenetAcceptjs/Test/Unit/Gateway/Request/AcceptFdsDataBuilderTest.php +++ /dev/null @@ -1,70 +0,0 @@ -paymentDOMock = $this->createMock(PaymentDataObjectInterface::class); - $this->paymentMock = $this->createMock(Payment::class); - $this->orderMock = $this->createMock(Order::class); - $this->paymentDOMock->method('getPayment') - ->willReturn($this->paymentMock); - - $this->builder = new AcceptFdsDataBuilder(new SubjectReader()); - } - - public function testBuild() - { - $transactionMock = $this->createMock(Transaction::class); - - $this->paymentMock->method('getAuthorizationTransaction') - ->willReturn($transactionMock); - - $transactionMock->method('getTxnId') - ->willReturn('foo'); - - $expected = [ - 'heldTransactionRequest' => [ - 'action' => 'approve', - 'refTransId' => 'foo' - ] - ]; - - $buildSubject = [ - 'payment' => $this->paymentDOMock, - ]; - - $this->assertEquals($expected, $this->builder->build($buildSubject)); - } -} diff --git a/app/code/Magento/AuthorizenetAcceptjs/Test/Unit/Gateway/Request/AddressDataBuilderTest.php b/app/code/Magento/AuthorizenetAcceptjs/Test/Unit/Gateway/Request/AddressDataBuilderTest.php deleted file mode 100644 index 84c2f19040e16..0000000000000 --- a/app/code/Magento/AuthorizenetAcceptjs/Test/Unit/Gateway/Request/AddressDataBuilderTest.php +++ /dev/null @@ -1,138 +0,0 @@ - [ - 'method' => 'getFirstname', - 'sampleData' => 'John' - ], - 'lastName' => [ - 'method' => 'getLastname', - 'sampleData' => 'Doe' - ], - 'company' => [ - 'method' => 'getCompany', - 'sampleData' => 'Magento' - ], - 'address' => [ - 'method' => 'getStreetLine1', - 'sampleData' => '11501 Domain Dr' - ], - 'city' => [ - 'method' => 'getCity', - 'sampleData' => 'Austin' - ], - 'state' => [ - 'method' => 'getRegionCode', - 'sampleData' => 'TX' - ], - 'zip' => [ - 'method' => 'getPostcode', - 'sampleData' => '78758' - ], - 'country' => [ - 'method' => 'getCountryId', - 'sampleData' => 'US' - ], - ]; - - protected function setUp() - { - $this->paymentDOMock = $this->createMock(PaymentDataObjectInterface::class); - $this->paymentMock = $this->createMock(Payment::class); - $this->paymentDOMock->method('getPayment') - ->willReturn($this->paymentMock); - $this->orderMock = $this->createMock(OrderAdapterInterface::class); - $this->paymentDOMock->method('getOrder') - ->willReturn($this->orderMock); - - $this->builder = new AddressDataBuilder(new SubjectReader()); - } - - public function testBuildWithBothAddresses() - { - $billingAddress = $this->createAddressMock('billing'); - $shippingAddress = $this->createAddressMock('shipping'); - $this->orderMock->method('getBillingAddress') - ->willReturn($billingAddress); - $this->orderMock->method('getShippingAddress') - ->willReturn($shippingAddress); - $this->orderMock->method('getRemoteIp') - ->willReturn('abc'); - - $buildSubject = [ - 'payment' => $this->paymentDOMock - ]; - - $result = $this->builder->build($buildSubject); - - $this->validateAddressData($result['transactionRequest']['billTo'], 'billing'); - $this->validateAddressData($result['transactionRequest']['shipTo'], 'shipping'); - $this->assertEquals('abc', $result['transactionRequest']['customerIP']); - } - - /** - * @param $responseData - * @param $addressPrefix - */ - private function validateAddressData($responseData, $addressPrefix) - { - foreach ($this->mockAddressData as $fieldValue => $field) { - $this->assertEquals($addressPrefix . $field['sampleData'], $responseData[$fieldValue]); - } - } - - /** - * @param $prefix - * - * @return \PHPUnit\Framework\MockObject\MockObject - */ - private function createAddressMock($prefix) - { - $addressAdapterMock = $this->createMock(AddressAdapterInterface::class); - - foreach ($this->mockAddressData as $field) { - $addressAdapterMock->method($field['method']) - ->willReturn($prefix . $field['sampleData']); - } - - return $addressAdapterMock; - } -} diff --git a/app/code/Magento/AuthorizenetAcceptjs/Test/Unit/Gateway/Request/AmountDataBuilderTest.php b/app/code/Magento/AuthorizenetAcceptjs/Test/Unit/Gateway/Request/AmountDataBuilderTest.php deleted file mode 100644 index 9da0139302a30..0000000000000 --- a/app/code/Magento/AuthorizenetAcceptjs/Test/Unit/Gateway/Request/AmountDataBuilderTest.php +++ /dev/null @@ -1,42 +0,0 @@ -builder = new AmountDataBuilder( - new SubjectReader() - ); - } - - public function testBuild() - { - $expected = [ - 'transactionRequest' => [ - 'amount' => '123.45', - ] - ]; - - $buildSubject = [ - 'amount' => 123.45 - ]; - - $this->assertEquals($expected, $this->builder->build($buildSubject)); - } -} diff --git a/app/code/Magento/AuthorizenetAcceptjs/Test/Unit/Gateway/Request/AuthenticationDataBuilderTest.php b/app/code/Magento/AuthorizenetAcceptjs/Test/Unit/Gateway/Request/AuthenticationDataBuilderTest.php deleted file mode 100644 index e9588e51b0fc8..0000000000000 --- a/app/code/Magento/AuthorizenetAcceptjs/Test/Unit/Gateway/Request/AuthenticationDataBuilderTest.php +++ /dev/null @@ -1,78 +0,0 @@ -configMock = $this->createMock(Config::class); - $this->paymentDOMock = $this->createMock(PaymentDataObjectInterface::class); - $this->paymentMock = $this->createMock(Payment::class); - /** @var MockObject|SubjectReader subjectReaderMock */ - $this->subjectReaderMock = $this->createMock(SubjectReader::class); - - $this->builder = new AuthenticationDataBuilder($this->subjectReaderMock, $this->configMock); - } - - public function testBuild() - { - $this->configMock->method('getLoginId') - ->willReturn('myloginid'); - $this->configMock->method('getTransactionKey') - ->willReturn('mytransactionkey'); - - $expected = [ - 'merchantAuthentication' => [ - 'name' => 'myloginid', - 'transactionKey' => 'mytransactionkey' - ] - ]; - - $buildSubject = []; - - $this->subjectReaderMock->method('readStoreId') - ->with($buildSubject) - ->willReturn(123); - - $this->assertEquals($expected, $this->builder->build($buildSubject)); - } -} diff --git a/app/code/Magento/AuthorizenetAcceptjs/Test/Unit/Gateway/Request/AuthorizationDataBuilderTest.php b/app/code/Magento/AuthorizenetAcceptjs/Test/Unit/Gateway/Request/AuthorizationDataBuilderTest.php deleted file mode 100644 index 438d681a2b5b2..0000000000000 --- a/app/code/Magento/AuthorizenetAcceptjs/Test/Unit/Gateway/Request/AuthorizationDataBuilderTest.php +++ /dev/null @@ -1,70 +0,0 @@ -paymentDOMock = $this->createMock(PaymentDataObjectInterface::class); - $this->paymentMock = $this->createMock(Payment::class); - $this->paymentDOMock->method('getPayment') - ->willReturn($this->paymentMock); - $this->passthroughData = new PassthroughDataObject(); - - $this->builder = new AuthorizeDataBuilder( - new SubjectReader(), - $this->passthroughData - ); - } - - public function testBuildWillAddTransactionType() - { - $expected = [ - 'transactionRequest' => [ - 'transactionType' => 'authOnlyTransaction' - ] - ]; - - $buildSubject = [ - 'store_id' => 123, - 'payment' => $this->paymentDOMock, - ]; - - $this->assertEquals($expected, $this->builder->build($buildSubject)); - $this->assertEquals('authOnlyTransaction', $this->passthroughData->getData('transactionType')); - } -} diff --git a/app/code/Magento/AuthorizenetAcceptjs/Test/Unit/Gateway/Request/CaptureDataBuilderTest.php b/app/code/Magento/AuthorizenetAcceptjs/Test/Unit/Gateway/Request/CaptureDataBuilderTest.php deleted file mode 100644 index 537a685f1ff7f..0000000000000 --- a/app/code/Magento/AuthorizenetAcceptjs/Test/Unit/Gateway/Request/CaptureDataBuilderTest.php +++ /dev/null @@ -1,77 +0,0 @@ -paymentDOMock = $this->createMock(PaymentDataObjectInterface::class); - $this->paymentMock = $this->createMock(Payment::class); - $this->paymentDOMock->method('getPayment') - ->willReturn($this->paymentMock); - $this->passthroughData = new PassthroughDataObject(); - - $this->builder = new CaptureDataBuilder( - new SubjectReader(), - $this->passthroughData - ); - } - - public function testBuildWillCaptureWhenAuthorizeTransactionExists() - { - $transactionMock = $this->createMock(Payment\Transaction::class); - $transactionMock->method('getAdditionalInformation') - ->with('real_transaction_id') - ->willReturn('prevtrans'); - $this->paymentMock->method('getAuthorizationTransaction') - ->willReturn($transactionMock); - - $expected = [ - 'transactionRequest' => [ - 'transactionType' => 'priorAuthCaptureTransaction', - 'refTransId' => 'prevtrans' - ] - ]; - - $buildSubject = [ - 'payment' => $this->paymentDOMock, - ]; - - $this->assertEquals($expected, $this->builder->build($buildSubject)); - $this->assertEquals('priorAuthCaptureTransaction', $this->passthroughData->getData('transactionType')); - } -} diff --git a/app/code/Magento/AuthorizenetAcceptjs/Test/Unit/Gateway/Request/CustomSettingsBuilderTest.php b/app/code/Magento/AuthorizenetAcceptjs/Test/Unit/Gateway/Request/CustomSettingsBuilderTest.php deleted file mode 100644 index be7dd7eca1761..0000000000000 --- a/app/code/Magento/AuthorizenetAcceptjs/Test/Unit/Gateway/Request/CustomSettingsBuilderTest.php +++ /dev/null @@ -1,74 +0,0 @@ -configMock = $this->createMock(Config::class); - /** @var MockObject|SubjectReader subjectReaderMock */ - $this->subjectReaderMock = $this->createMock(SubjectReader::class); - $this->subjectReaderMock->method('readStoreId') - ->willReturn('123'); - - $this->builder = new CustomSettingsBuilder($this->subjectReaderMock, $this->configMock); - } - - public function testBuildWithEmailCustomerDisabled() - { - $this->configMock->method('shouldEmailCustomer') - ->with('123') - ->willReturn(false); - - $this->assertEquals([], $this->builder->build([])); - } - - public function testBuildWithEmailCustomerEnabled() - { - $this->configMock->method('shouldEmailCustomer') - ->with('123') - ->willReturn(true); - - $expected = [ - 'transactionRequest' => [ - 'transactionSettings' => [ - 'setting' => [ - [ - 'settingName' => 'emailCustomer', - 'settingValue' => 'true' - ] - ] - ] - ] - ]; - - $this->assertEquals($expected, $this->builder->build([])); - } -} diff --git a/app/code/Magento/AuthorizenetAcceptjs/Test/Unit/Gateway/Request/CustomerDataBuilderTest.php b/app/code/Magento/AuthorizenetAcceptjs/Test/Unit/Gateway/Request/CustomerDataBuilderTest.php deleted file mode 100644 index 7c9116cad54b1..0000000000000 --- a/app/code/Magento/AuthorizenetAcceptjs/Test/Unit/Gateway/Request/CustomerDataBuilderTest.php +++ /dev/null @@ -1,79 +0,0 @@ -paymentDOMock = $this->createMock(PaymentDataObjectInterface::class); - $this->paymentMock = $this->createMock(Payment::class); - $this->paymentDOMock->method('getPayment') - ->willReturn($this->paymentMock); - $this->orderMock = $this->createMock(OrderAdapterInterface::class); - $this->paymentDOMock->method('getOrder') - ->willReturn($this->orderMock); - - $this->builder = new CustomerDataBuilder(new SubjectReader()); - } - - public function testBuild() - { - $addressAdapterMock = $this->createMock(AddressAdapterInterface::class); - $addressAdapterMock->method('getEmail') - ->willReturn('foo@bar.com'); - $this->orderMock->method('getBillingAddress') - ->willReturn($addressAdapterMock); - $this->orderMock->method('getCustomerId') - ->willReturn('123'); - - $expected = [ - 'transactionRequest' => [ - 'customer' => [ - 'id' => '123', - 'email' => 'foo@bar.com' - ] - ] - ]; - - $buildSubject = [ - 'payment' => $this->paymentDOMock, - ]; - - $this->assertEquals($expected, $this->builder->build($buildSubject)); - } -} diff --git a/app/code/Magento/AuthorizenetAcceptjs/Test/Unit/Gateway/Request/OrderDataBuilderTest.php b/app/code/Magento/AuthorizenetAcceptjs/Test/Unit/Gateway/Request/OrderDataBuilderTest.php deleted file mode 100644 index d66421d48ca8b..0000000000000 --- a/app/code/Magento/AuthorizenetAcceptjs/Test/Unit/Gateway/Request/OrderDataBuilderTest.php +++ /dev/null @@ -1,74 +0,0 @@ -paymentDOMock = $this->createMock(PaymentDataObjectInterface::class); - $this->paymentMock = $this->createMock(Payment::class); - $this->paymentDOMock->method('getPayment') - ->willReturn($this->paymentMock); - $this->orderMock = $this->createMock(OrderAdapterInterface::class); - $this->paymentDOMock->method('getOrder') - ->willReturn($this->orderMock); - - $this->builder = new OrderDataBuilder(new SubjectReader()); - } - - public function testBuild() - { - $this->orderMock->method('getOrderIncrementId') - ->willReturn('10000015'); - - $expected = [ - 'transactionRequest' => [ - 'order' => [ - 'invoiceNumber' => '10000015' - ] - ] - ]; - - $buildSubject = [ - 'payment' => $this->paymentDOMock, - 'order' => $this->orderMock, - ]; - - $this->assertEquals($expected, $this->builder->build($buildSubject)); - } -} diff --git a/app/code/Magento/AuthorizenetAcceptjs/Test/Unit/Gateway/Request/PassthroughDataBuilderTest.php b/app/code/Magento/AuthorizenetAcceptjs/Test/Unit/Gateway/Request/PassthroughDataBuilderTest.php deleted file mode 100644 index f4c5f56efe890..0000000000000 --- a/app/code/Magento/AuthorizenetAcceptjs/Test/Unit/Gateway/Request/PassthroughDataBuilderTest.php +++ /dev/null @@ -1,52 +0,0 @@ - 'bar', - 'baz' => 'bash' - ]); - $builder = new PassthroughDataBuilder($passthroughData); - - $expected = [ - 'transactionRequest' => [ - 'userFields' => [ - 'userField' => [ - [ - 'name' => 'foo', - 'value' => 'bar' - ], - [ - 'name' => 'baz', - 'value' => 'bash' - ], - ] - ] - ] - ]; - - $this->assertEquals($expected, $builder->build([])); - } - - public function testBuildWithNoData() - { - $passthroughData = new PassthroughDataObject(); - $builder = new PassthroughDataBuilder($passthroughData); - $expected = []; - - $this->assertEquals($expected, $builder->build([])); - } -} diff --git a/app/code/Magento/AuthorizenetAcceptjs/Test/Unit/Gateway/Request/PaymentDataBuilderTest.php b/app/code/Magento/AuthorizenetAcceptjs/Test/Unit/Gateway/Request/PaymentDataBuilderTest.php deleted file mode 100644 index cf3842b8947bb..0000000000000 --- a/app/code/Magento/AuthorizenetAcceptjs/Test/Unit/Gateway/Request/PaymentDataBuilderTest.php +++ /dev/null @@ -1,72 +0,0 @@ -paymentDOMock = $this->createMock(PaymentDataObjectInterface::class); - $this->paymentMock = $this->createMock(Payment::class); - $this->orderMock = $this->createMock(Order::class); - $this->paymentDOMock->method('getPayment') - ->willReturn($this->paymentMock); - - $this->builder = new PaymentDataBuilder(new SubjectReader()); - } - - public function testBuild() - { - $this->paymentMock->method('getAdditionalInformation') - ->willReturnMap([ - ['opaqueDataDescriptor', 'foo'], - ['opaqueDataValue', 'bar'] - ]); - - $expected = [ - 'transactionRequest' => [ - 'payment' => [ - 'opaqueData' => [ - 'dataDescriptor' => 'foo', - 'dataValue' => 'bar' - ] - ] - ] - ]; - - $buildSubject = [ - 'payment' => $this->paymentDOMock, - 'amount' => 123.45 - ]; - - $this->assertEquals($expected, $this->builder->build($buildSubject)); - } -} diff --git a/app/code/Magento/AuthorizenetAcceptjs/Test/Unit/Gateway/Request/PoDataBuilderTest.php b/app/code/Magento/AuthorizenetAcceptjs/Test/Unit/Gateway/Request/PoDataBuilderTest.php deleted file mode 100644 index 97b51c1e1807c..0000000000000 --- a/app/code/Magento/AuthorizenetAcceptjs/Test/Unit/Gateway/Request/PoDataBuilderTest.php +++ /dev/null @@ -1,61 +0,0 @@ -paymentDOMock = $this->createMock(PaymentDataObjectInterface::class); - $this->paymentMock = $this->createMock(Payment::class); - $this->paymentDOMock->method('getPayment') - ->willReturn($this->paymentMock); - - $this->builder = new PoDataBuilder(new SubjectReader()); - } - - public function testBuild() - { - $this->paymentMock->method('getPoNumber') - ->willReturn('abc'); - - $expected = [ - 'transactionRequest' => [ - 'poNumber' => 'abc' - ] - ]; - - $buildSubject = [ - 'payment' => $this->paymentDOMock, - ]; - $this->assertEquals($expected, $this->builder->build($buildSubject)); - } -} diff --git a/app/code/Magento/AuthorizenetAcceptjs/Test/Unit/Gateway/Request/RefundPaymentDataBuilderTest.php b/app/code/Magento/AuthorizenetAcceptjs/Test/Unit/Gateway/Request/RefundPaymentDataBuilderTest.php deleted file mode 100644 index c1879b3df83a3..0000000000000 --- a/app/code/Magento/AuthorizenetAcceptjs/Test/Unit/Gateway/Request/RefundPaymentDataBuilderTest.php +++ /dev/null @@ -1,70 +0,0 @@ -paymentDOMock = $this->createMock(PaymentDataObjectInterface::class); - $this->paymentMock = $this->createMock(Payment::class); - $this->paymentDOMock->method('getPayment') - ->willReturn($this->paymentMock); - - $this->builder = new RefundPaymentDataBuilder( - new SubjectReader() - ); - } - - public function testBuild() - { - $this->paymentMock->method('getAdditionalInformation') - ->with('ccLast4') - ->willReturn('1111'); - - $expected = [ - 'transactionRequest' => [ - 'payment' => [ - 'creditCard' => [ - 'cardNumber' => '1111', - 'expirationDate' => 'XXXX' - ] - ] - ] - ]; - - $buildSubject = [ - 'payment' => $this->paymentDOMock, - 'amount' => 123.45 - ]; - - $this->assertEquals($expected, $this->builder->build($buildSubject)); - } -} diff --git a/app/code/Magento/AuthorizenetAcceptjs/Test/Unit/Gateway/Request/RefundReferenceTransactionDataBuilderTest.php b/app/code/Magento/AuthorizenetAcceptjs/Test/Unit/Gateway/Request/RefundReferenceTransactionDataBuilderTest.php deleted file mode 100644 index cf1803005acee..0000000000000 --- a/app/code/Magento/AuthorizenetAcceptjs/Test/Unit/Gateway/Request/RefundReferenceTransactionDataBuilderTest.php +++ /dev/null @@ -1,69 +0,0 @@ -paymentDOMock = $this->createMock(PaymentDataObjectInterface::class); - $this->paymentMock = $this->createMock(Payment::class); - $this->orderMock = $this->createMock(Order::class); - $this->paymentDOMock->method('getPayment') - ->willReturn($this->paymentMock); - - $this->builder = new RefundReferenceTransactionDataBuilder(new SubjectReader()); - } - - public function testBuild() - { - $transactionMock = $this->createMock(Transaction::class); - - $this->paymentMock->method('getAuthorizationTransaction') - ->willReturn($transactionMock); - - $transactionMock->method('getParentTxnId') - ->willReturn('foo'); - - $expected = [ - 'transactionRequest' => [ - 'refTransId' => 'foo' - ] - ]; - - $buildSubject = [ - 'payment' => $this->paymentDOMock, - ]; - - $this->assertEquals($expected, $this->builder->build($buildSubject)); - } -} diff --git a/app/code/Magento/AuthorizenetAcceptjs/Test/Unit/Gateway/Request/RefundTransactionTypeDataBuilderTest.php b/app/code/Magento/AuthorizenetAcceptjs/Test/Unit/Gateway/Request/RefundTransactionTypeDataBuilderTest.php deleted file mode 100644 index 4e0f5f75fb944..0000000000000 --- a/app/code/Magento/AuthorizenetAcceptjs/Test/Unit/Gateway/Request/RefundTransactionTypeDataBuilderTest.php +++ /dev/null @@ -1,30 +0,0 @@ - [ - 'transactionType' => self::REQUEST_TYPE_REFUND - ] - ]; - - $this->assertEquals($expected, $builder->build([])); - } -} diff --git a/app/code/Magento/AuthorizenetAcceptjs/Test/Unit/Gateway/Request/RequestTypeBuilderTest.php b/app/code/Magento/AuthorizenetAcceptjs/Test/Unit/Gateway/Request/RequestTypeBuilderTest.php deleted file mode 100644 index cb03dfc3dac5e..0000000000000 --- a/app/code/Magento/AuthorizenetAcceptjs/Test/Unit/Gateway/Request/RequestTypeBuilderTest.php +++ /dev/null @@ -1,36 +0,0 @@ -builder = new RequestTypeBuilder('foo'); - } - - public function testBuild() - { - $expected = [ - 'payload_type' => 'foo' - ]; - - $buildSubject = []; - $this->assertEquals($expected, $this->builder->build($buildSubject)); - } -} diff --git a/app/code/Magento/AuthorizenetAcceptjs/Test/Unit/Gateway/Request/SaleDataBuilderTest.php b/app/code/Magento/AuthorizenetAcceptjs/Test/Unit/Gateway/Request/SaleDataBuilderTest.php deleted file mode 100644 index 407b9bc85a2c5..0000000000000 --- a/app/code/Magento/AuthorizenetAcceptjs/Test/Unit/Gateway/Request/SaleDataBuilderTest.php +++ /dev/null @@ -1,70 +0,0 @@ -paymentDOMock = $this->createMock(PaymentDataObjectInterface::class); - $this->paymentMock = $this->createMock(Payment::class); - $this->paymentDOMock->method('getPayment') - ->willReturn($this->paymentMock); - $this->passthroughData = new PassthroughDataObject(); - - $this->builder = new SaleDataBuilder( - new SubjectReader(), - $this->passthroughData - ); - } - - public function testBuildWillAddTransactionType() - { - $expected = [ - 'transactionRequest' => [ - 'transactionType' => 'authCaptureTransaction' - ] - ]; - - $buildSubject = [ - 'store_id' => 123, - 'payment' => $this->paymentDOMock, - ]; - - $this->assertEquals($expected, $this->builder->build($buildSubject)); - $this->assertEquals('authCaptureTransaction', $this->passthroughData->getData('transactionType')); - } -} diff --git a/app/code/Magento/AuthorizenetAcceptjs/Test/Unit/Gateway/Request/ShippingDataBuilderTest.php b/app/code/Magento/AuthorizenetAcceptjs/Test/Unit/Gateway/Request/ShippingDataBuilderTest.php deleted file mode 100644 index d6525e610a285..0000000000000 --- a/app/code/Magento/AuthorizenetAcceptjs/Test/Unit/Gateway/Request/ShippingDataBuilderTest.php +++ /dev/null @@ -1,75 +0,0 @@ -paymentDOMock = $this->createMock(PaymentDataObjectInterface::class); - $this->paymentMock = $this->createMock(Payment::class); - $this->orderMock = $this->createMock(Order::class); - $this->paymentDOMock->method('getPayment') - ->willReturn($this->paymentMock); - $this->paymentDOMock->method('getOrder') - ->willReturn($this->orderMock); - - $this->builder = new ShippingDataBuilder( - new SubjectReader() - ); - } - - public function testBuild() - { - $this->orderMock->method('getBaseShippingAmount') - ->willReturn('43.12'); - - $expected = [ - 'transactionRequest' => [ - 'shipping' => [ - 'amount' => '43.12' - ] - ] - ]; - - $buildSubject = [ - 'payment' => $this->paymentDOMock, - 'order' => $this->orderMock, - ]; - - $this->assertEquals($expected, $this->builder->build($buildSubject)); - } -} diff --git a/app/code/Magento/AuthorizenetAcceptjs/Test/Unit/Gateway/Request/SolutionDataBuilderTest.php b/app/code/Magento/AuthorizenetAcceptjs/Test/Unit/Gateway/Request/SolutionDataBuilderTest.php deleted file mode 100644 index 1b06546c2ea8f..0000000000000 --- a/app/code/Magento/AuthorizenetAcceptjs/Test/Unit/Gateway/Request/SolutionDataBuilderTest.php +++ /dev/null @@ -1,75 +0,0 @@ -configMock = $this->createMock(Config::class); - $this->paymentDOMock = $this->createMock(PaymentDataObjectInterface::class); - $this->paymentMock = $this->createMock(Payment::class); - /** @var MockObject|SubjectReader subjectReaderMock */ - $this->subjectReaderMock = $this->createMock(SubjectReader::class); - - $this->builder = new SolutionDataBuilder($this->subjectReaderMock, $this->configMock); - } - - public function testBuild() - { - $this->subjectReaderMock->method('readStoreId') - ->willReturn('123'); - $this->configMock->method('getSolutionId') - ->with('123') - ->willReturn('solutionid'); - - $expected = [ - 'transactionRequest' => [ - 'solution' => [ - 'id' => 'solutionid', - ] - ] - ]; - - $buildSubject = []; - $this->assertEquals($expected, $this->builder->build($buildSubject)); - } -} diff --git a/app/code/Magento/AuthorizenetAcceptjs/Test/Unit/Gateway/Request/StoreConfigBuilderTest.php b/app/code/Magento/AuthorizenetAcceptjs/Test/Unit/Gateway/Request/StoreConfigBuilderTest.php deleted file mode 100644 index 2ed0cb13ed624..0000000000000 --- a/app/code/Magento/AuthorizenetAcceptjs/Test/Unit/Gateway/Request/StoreConfigBuilderTest.php +++ /dev/null @@ -1,68 +0,0 @@ -paymentDOMock = $this->createMock(PaymentDataObjectInterface::class); - $this->paymentMock = $this->createMock(InfoInterface::class); - $this->paymentDOMock->method('getPayment') - ->willReturn($this->paymentMock); - $this->orderMock = $this->createMock(OrderAdapterInterface::class); - $this->paymentDOMock->method('getOrder') - ->willReturn($this->orderMock); - - $this->builder = new StoreConfigBuilder(new SubjectReader()); - } - - public function testBuild() - { - $this->orderMock->method('getStoreID') - ->willReturn(123); - - $expected = [ - 'store_id' => 123 - ]; - - $buildSubject = [ - 'payment' => $this->paymentDOMock, - ]; - $this->assertEquals($expected, $this->builder->build($buildSubject)); - } -} diff --git a/app/code/Magento/AuthorizenetAcceptjs/Test/Unit/Gateway/Request/TransactionDetailsDataBuilderTest.php b/app/code/Magento/AuthorizenetAcceptjs/Test/Unit/Gateway/Request/TransactionDetailsDataBuilderTest.php deleted file mode 100644 index 03c036c027147..0000000000000 --- a/app/code/Magento/AuthorizenetAcceptjs/Test/Unit/Gateway/Request/TransactionDetailsDataBuilderTest.php +++ /dev/null @@ -1,89 +0,0 @@ -paymentDOMock = $this->createMock(PaymentDataObjectInterface::class); - $this->paymentMock = $this->createMock(Payment::class); - $this->orderMock = $this->createMock(Order::class); - $this->paymentDOMock->method('getPayment') - ->willReturn($this->paymentMock); - - $this->builder = new TransactionDetailsDataBuilder(new SubjectReader()); - } - - public function testBuild() - { - $transactionMock = $this->createMock(Transaction::class); - - $this->paymentMock->method('getAuthorizationTransaction') - ->willReturn($transactionMock); - - $transactionMock->method('getParentTxnId') - ->willReturn('foo'); - - $expected = [ - 'transId' => 'foo' - ]; - - $buildSubject = [ - 'payment' => $this->paymentDOMock, - ]; - - $this->assertEquals($expected, $this->builder->build($buildSubject)); - } - - public function testBuildWithIncludedTransactionId() - { - $transactionMock = $this->createMock(Transaction::class); - - $this->paymentMock->expects($this->never()) - ->method('getAuthorizationTransaction'); - - $transactionMock->expects($this->never()) - ->method('getParentTxnId'); - - $expected = [ - 'transId' => 'foo' - ]; - - $buildSubject = [ - 'payment' => $this->paymentDOMock, - 'transactionId' => 'foo' - ]; - - $this->assertEquals($expected, $this->builder->build($buildSubject)); - } -} diff --git a/app/code/Magento/AuthorizenetAcceptjs/Test/Unit/Gateway/Request/VoidDataBuilderTest.php b/app/code/Magento/AuthorizenetAcceptjs/Test/Unit/Gateway/Request/VoidDataBuilderTest.php deleted file mode 100644 index 84460a1c744b9..0000000000000 --- a/app/code/Magento/AuthorizenetAcceptjs/Test/Unit/Gateway/Request/VoidDataBuilderTest.php +++ /dev/null @@ -1,68 +0,0 @@ -paymentDOMock = $this->createMock(PaymentDataObjectInterface::class); - $this->paymentMock = $this->createMock(Payment::class); - $this->paymentDOMock->method('getPayment') - ->willReturn($this->paymentMock); - - $this->builder = new VoidDataBuilder(new SubjectReader()); - } - - public function testBuild() - { - $transactionMock = $this->createMock(Transaction::class); - $this->paymentMock->method('getAuthorizationTransaction') - ->willReturn($transactionMock); - $transactionMock->method('getParentTxnId') - ->willReturn('myref'); - - $buildSubject = [ - 'payment' => $this->paymentDOMock - ]; - - $expected = [ - 'transactionRequest' => [ - 'transactionType' => self::REQUEST_TYPE_VOID, - 'refTransId' => 'myref', - ] - ]; - $this->assertEquals($expected, $this->builder->build($buildSubject)); - } -} diff --git a/app/code/Magento/AuthorizenetAcceptjs/Test/Unit/Gateway/Response/CloseParentTransactionHandlerTest.php b/app/code/Magento/AuthorizenetAcceptjs/Test/Unit/Gateway/Response/CloseParentTransactionHandlerTest.php deleted file mode 100644 index e9929c631eb15..0000000000000 --- a/app/code/Magento/AuthorizenetAcceptjs/Test/Unit/Gateway/Response/CloseParentTransactionHandlerTest.php +++ /dev/null @@ -1,62 +0,0 @@ -paymentDOMock = $this->createMock(PaymentDataObjectInterface::class); - $this->paymentMock = $this->createMock(Payment::class); - $this->paymentDOMock->method('getPayment') - ->willReturn($this->paymentMock); - - $this->handler = new CloseParentTransactionHandler(new SubjectReader()); - } - - public function testHandleClosesTransactionByDefault() - { - $subject = [ - 'payment' => $this->paymentDOMock - ]; - $response = [ - 'transactionResponse' => [] - ]; - - // Assert the parent transaction i closed - $this->paymentMock->expects($this->once()) - ->method('setShouldCloseParentTransaction') - ->with(true); - - $this->handler->handle($subject, $response); - // Assertions are via mock expects above - } -} diff --git a/app/code/Magento/AuthorizenetAcceptjs/Test/Unit/Gateway/Response/CloseTransactionHandlerTest.php b/app/code/Magento/AuthorizenetAcceptjs/Test/Unit/Gateway/Response/CloseTransactionHandlerTest.php deleted file mode 100644 index a7093f0dac889..0000000000000 --- a/app/code/Magento/AuthorizenetAcceptjs/Test/Unit/Gateway/Response/CloseTransactionHandlerTest.php +++ /dev/null @@ -1,62 +0,0 @@ -paymentDOMock = $this->createMock(PaymentDataObjectInterface::class); - $this->paymentMock = $this->createMock(Payment::class); - $this->paymentDOMock->method('getPayment') - ->willReturn($this->paymentMock); - - $this->handler = new CloseTransactionHandler(new SubjectReader()); - } - - public function testHandleClosesTransactionByDefault() - { - $subject = [ - 'payment' => $this->paymentDOMock - ]; - $response = [ - 'transactionResponse' => [] - ]; - - // Assert the transaction is closed - $this->paymentMock->expects($this->once()) - ->method('setIsTransactionClosed') - ->with(true); - - $this->handler->handle($subject, $response); - // Assertions are via mock expects above - } -} diff --git a/app/code/Magento/AuthorizenetAcceptjs/Test/Unit/Gateway/Response/PaymentResponseHandlerTest.php b/app/code/Magento/AuthorizenetAcceptjs/Test/Unit/Gateway/Response/PaymentResponseHandlerTest.php deleted file mode 100644 index d051c7d2910a5..0000000000000 --- a/app/code/Magento/AuthorizenetAcceptjs/Test/Unit/Gateway/Response/PaymentResponseHandlerTest.php +++ /dev/null @@ -1,112 +0,0 @@ -paymentDOMock = $this->createMock(PaymentDataObjectInterface::class); - $this->paymentMock = $this->createMock(Payment::class); - $this->paymentDOMock->method('getPayment') - ->willReturn($this->paymentMock); - - $this->builder = new PaymentResponseHandler(new SubjectReader()); - } - - public function testHandleDefaultResponse() - { - $this->paymentMock->method('getAdditionalInformation') - ->with('ccLast4') - ->willReturn('1234'); - // Assert the avs code is saved - $this->paymentMock->expects($this->once()) - ->method('setCcAvsStatus') - ->with('avshurray'); - $this->paymentMock->expects($this->once()) - ->method('setCcLast4') - ->with('1234'); - $this->paymentMock->expects($this->once()) - ->method('setIsTransactionClosed') - ->with(false); - - $response = [ - 'transactionResponse' => [ - 'avsResultCode' => 'avshurray', - 'responseCode' => self::RESPONSE_CODE_APPROVED, - ] - ]; - $subject = [ - 'payment' => $this->paymentDOMock - ]; - - $this->builder->handle($subject, $response); - // Assertions are part of mocking above - } - - public function testHandleHeldResponse() - { - // Assert the avs code is saved - $this->paymentMock->expects($this->once()) - ->method('setCcAvsStatus') - ->with('avshurray'); - $this->paymentMock->expects($this->once()) - ->method('setIsTransactionClosed') - ->with(false); - // opaque data wasn't provided - $this->paymentMock->expects($this->never()) - ->method('setAdditionalInformation'); - // Assert the payment is flagged for review - $this->paymentMock->expects($this->once()) - ->method('setIsTransactionPending') - ->with(true) - ->willReturnSelf(); - $this->paymentMock->expects($this->once()) - ->method('setIsFraudDetected') - ->with(true); - - $response = [ - 'transactionResponse' => [ - 'avsResultCode' => 'avshurray', - 'responseCode' => self::RESPONSE_CODE_HELD, - ] - ]; - $subject = [ - 'payment' => $this->paymentDOMock - ]; - - $this->builder->handle($subject, $response); - // Assertions are part of mocking above - } -} diff --git a/app/code/Magento/AuthorizenetAcceptjs/Test/Unit/Gateway/Response/PaymentReviewStatusHandlerTest.php b/app/code/Magento/AuthorizenetAcceptjs/Test/Unit/Gateway/Response/PaymentReviewStatusHandlerTest.php deleted file mode 100644 index 197dc209ece66..0000000000000 --- a/app/code/Magento/AuthorizenetAcceptjs/Test/Unit/Gateway/Response/PaymentReviewStatusHandlerTest.php +++ /dev/null @@ -1,136 +0,0 @@ -paymentDOMock = $this->createMock(PaymentDataObjectInterface::class); - $this->paymentMock = $this->createMock(Payment::class); - $this->paymentDOMock->method('getPayment') - ->willReturn($this->paymentMock); - - $this->handler = new PaymentReviewStatusHandler(new SubjectReader()); - } - - public function testApprovesPayment() - { - $subject = [ - 'payment' => $this->paymentDOMock - ]; - $response = [ - 'transaction' => [ - 'transactionStatus' => 'approvedOrSomething', - ] - ]; - - // Assert payment is handled correctly - $this->paymentMock->expects($this->exactly(2)) - ->method('setData') - ->withConsecutive( - ['is_transaction_denied', false], - ['is_transaction_approved', true] - ); - - $this->handler->handle($subject, $response); - // Assertions are via mock expects above - } - - /** - * @param string $status - * @dataProvider declinedTransactionStatusesProvider - */ - public function testDeniesPayment(string $status) - { - $subject = [ - 'payment' => $this->paymentDOMock - ]; - $response = [ - 'transaction' => [ - 'transactionStatus' => $status, - ] - ]; - - // Assert payment is handled correctly - $this->paymentMock->expects($this->exactly(2)) - ->method('setData') - ->withConsecutive( - ['is_transaction_denied', true], - ['is_transaction_approved', false] - ); - $this->handler->handle($subject, $response); - } - - /** - * @param string $status - * @dataProvider pendingTransactionStatusesProvider - */ - public function testDoesNothingWhenPending(string $status) - { - $subject = [ - 'payment' => $this->paymentDOMock - ]; - $response = [ - 'transaction' => [ - 'transactionStatus' => $status, - ] - ]; - - // Assert payment is handled correctly - $this->paymentMock->expects($this->never()) - ->method('setData'); - - $this->handler->handle($subject, $response); - } - - /** - * @return array - */ - public function pendingTransactionStatusesProvider() - { - return [ - ['FDSPendingReview'], - ['FDSAuthorizedPendingReview'] - ]; - } - - /** - * @return array - */ - public function declinedTransactionStatusesProvider() - { - return [ - ['void'], - ['declined'] - ]; - } -} diff --git a/app/code/Magento/AuthorizenetAcceptjs/Test/Unit/Gateway/Response/TransactionDetailsResponseHandlerTest.php b/app/code/Magento/AuthorizenetAcceptjs/Test/Unit/Gateway/Response/TransactionDetailsResponseHandlerTest.php deleted file mode 100644 index 016e3a1e95383..0000000000000 --- a/app/code/Magento/AuthorizenetAcceptjs/Test/Unit/Gateway/Response/TransactionDetailsResponseHandlerTest.php +++ /dev/null @@ -1,82 +0,0 @@ -paymentDOMock = $this->createMock(PaymentDataObjectInterface::class); - $this->paymentMock = $this->createMock(Payment::class); - $this->configMock = $this->createMock(Config::class); - $this->paymentDOMock->method('getPayment') - ->willReturn($this->paymentMock); - - $this->handler = new TransactionDetailsResponseHandler(new SubjectReader(), $this->configMock); - } - - public function testHandle() - { - $subject = [ - 'payment' => $this->paymentDOMock, - 'store_id' => 123, - ]; - $response = [ - 'transactionResponse' => [ - 'dontsaveme' => 'dontdoti', - 'abc' => 'foobar', - ] - ]; - - // Assert the information comes from the right store config - $this->configMock->method('getAdditionalInfoKeys') - ->with(123) - ->willReturn(['abc']); - - // Assert the payment has the most recent information always set on it - $this->paymentMock->expects($this->once()) - ->method('setAdditionalInformation') - ->with('abc', 'foobar'); - // Assert the transaction has the raw details from the transaction - $this->paymentMock->expects($this->once()) - ->method('setTransactionAdditionalInfo') - ->with('raw_details_info', ['abc' => 'foobar']); - - $this->handler->handle($subject, $response); - // Assertions are via mock expects above - } -} diff --git a/app/code/Magento/AuthorizenetAcceptjs/Test/Unit/Gateway/Response/TransactionIdHandlerTest.php b/app/code/Magento/AuthorizenetAcceptjs/Test/Unit/Gateway/Response/TransactionIdHandlerTest.php deleted file mode 100644 index 710f995918495..0000000000000 --- a/app/code/Magento/AuthorizenetAcceptjs/Test/Unit/Gateway/Response/TransactionIdHandlerTest.php +++ /dev/null @@ -1,92 +0,0 @@ -paymentDOMock = $this->createMock(PaymentDataObjectInterface::class); - $this->paymentMock = $this->createMock(Payment::class); - $this->paymentDOMock->method('getPayment') - ->willReturn($this->paymentMock); - - $this->builder = new TransactionIdHandler(new SubjectReader()); - } - - public function testHandleDefaultResponse() - { - $this->paymentMock->method('getParentTransactionId') - ->willReturn(null); - // Assert the id is set - $this->paymentMock->expects($this->once()) - ->method('setTransactionId') - ->with('thetransid'); - // Assert the id is set in the additional info for later - $this->paymentMock->expects($this->once()) - ->method('setTransactionAdditionalInfo') - ->with('real_transaction_id', 'thetransid'); - - $response = [ - 'transactionResponse' => [ - 'transId' => 'thetransid', - ] - ]; - $subject = [ - 'payment' => $this->paymentDOMock - ]; - - $this->builder->handle($subject, $response); - // Assertions are part of mocking above - } - - public function testHandleDifferenceInTransactionId() - { - $this->paymentMock->method('getParentTransactionId') - ->willReturn('somethingElse'); - // Assert the id is set - $this->paymentMock->expects($this->once()) - ->method('setTransactionId') - ->with('thetransid'); - - $response = [ - 'transactionResponse' => [ - 'transId' => 'thetransid', - ] - ]; - $subject = [ - 'payment' => $this->paymentDOMock - ]; - - $this->builder->handle($subject, $response); - // Assertions are part of mocking above - } -} diff --git a/app/code/Magento/AuthorizenetAcceptjs/Test/Unit/Gateway/Response/VoidResponseHandlerTest.php b/app/code/Magento/AuthorizenetAcceptjs/Test/Unit/Gateway/Response/VoidResponseHandlerTest.php deleted file mode 100644 index f99da2b2ec90b..0000000000000 --- a/app/code/Magento/AuthorizenetAcceptjs/Test/Unit/Gateway/Response/VoidResponseHandlerTest.php +++ /dev/null @@ -1,72 +0,0 @@ -paymentDOMock = $this->createMock(PaymentDataObjectInterface::class); - $this->paymentMock = $this->createMock(Payment::class); - $this->paymentDOMock->method('getPayment') - ->willReturn($this->paymentMock); - - $this->handler = new VoidResponseHandler(new SubjectReader()); - } - - public function testHandle() - { - $subject = [ - 'payment' => $this->paymentDOMock - ]; - $response = [ - 'transactionResponse' => [ - 'transId' => 'abc123', - ] - ]; - - // Assert the transaction is closed - $this->paymentMock->expects($this->once()) - ->method('setIsTransactionClosed') - ->with(true); - // Assert the parent transaction is closed - $this->paymentMock->expects($this->once()) - ->method('setShouldCloseParentTransaction') - ->with(true); - // Assert the authorize.net transaction id is saved - $this->paymentMock->expects($this->once()) - ->method('setTransactionAdditionalInfo') - ->with('real_transaction_id', 'abc123'); - - $this->handler->handle($subject, $response); - // Assertions are via mock expects above - } -} diff --git a/app/code/Magento/AuthorizenetAcceptjs/Test/Unit/Gateway/SubjectReaderTest.php b/app/code/Magento/AuthorizenetAcceptjs/Test/Unit/Gateway/SubjectReaderTest.php deleted file mode 100644 index 42219024badbf..0000000000000 --- a/app/code/Magento/AuthorizenetAcceptjs/Test/Unit/Gateway/SubjectReaderTest.php +++ /dev/null @@ -1,119 +0,0 @@ -subjectReader = new SubjectReader(); - } - - public function testReadPayment(): void - { - $paymentDO = $this->createMock(PaymentDataObjectInterface::class); - - $this->assertSame($paymentDO, $this->subjectReader->readPayment(['payment' => $paymentDO])); - } - - /** - * @expectedException \InvalidArgumentException - * @expectedExceptionMessage Payment data object should be provided - */ - public function testReadPaymentThrowsExceptionWhenNotAPaymentObject(): void - { - $this->subjectReader->readPayment(['payment' => 'nope']); - } - - /** - * @expectedException \InvalidArgumentException - * @expectedExceptionMessage Payment data object should be provided - */ - public function testReadPaymentThrowsExceptionWhenNotSet(): void - { - $this->subjectReader->readPayment([]); - } - - public function testReadResponse(): void - { - $expected = ['foo' => 'bar']; - - $this->assertSame($expected, $this->subjectReader->readResponse(['response' => $expected])); - } - - /** - * @expectedException \InvalidArgumentException - * @expectedExceptionMessage Response does not exist - */ - public function testReadResponseThrowsExceptionWhenNotAvailable(): void - { - $this->subjectReader->readResponse([]); - } - - public function testReadStoreId(): void - { - $this->assertEquals(123, $this->subjectReader->readStoreId(['store_id' => '123'])); - } - - public function testReadStoreIdFromOrder(): void - { - $paymentDOMock = $this->createMock(PaymentDataObjectInterface::class); - $orderMock = $this->createMock(OrderAdapterInterface::class); - $paymentDOMock->method('getOrder') - ->willReturn($orderMock); - $orderMock->method('getStoreID') - ->willReturn('123'); - - $result = $this->subjectReader->readStoreId([ - 'payment' => $paymentDOMock - ]); - - $this->assertEquals(123, $result); - } - - public function testReadLoginId(): void - { - $this->assertEquals('abc', $this->subjectReader->readLoginId([ - 'merchantAuthentication' => ['name' => 'abc'] - ])); - } - - public function testReadTransactionKey(): void - { - $this->assertEquals('abc', $this->subjectReader->readTransactionKey([ - 'merchantAuthentication' => ['transactionKey' => 'abc'] - ])); - } - - public function testReadAmount(): void - { - $this->assertSame('123.12', $this->subjectReader->readAmount(['amount' => 123.12])); - } - - /** - * @expectedException \InvalidArgumentException - * @expectedExceptionMessage Amount should be provided - */ - public function testReadAmountThrowsExceptionWhenNotAvailable(): void - { - $this->subjectReader->readAmount([]); - } -} diff --git a/app/code/Magento/AuthorizenetAcceptjs/Test/Unit/Gateway/Validator/GeneralResponseValidatorTest.php b/app/code/Magento/AuthorizenetAcceptjs/Test/Unit/Gateway/Validator/GeneralResponseValidatorTest.php deleted file mode 100644 index 347cd071acc3a..0000000000000 --- a/app/code/Magento/AuthorizenetAcceptjs/Test/Unit/Gateway/Validator/GeneralResponseValidatorTest.php +++ /dev/null @@ -1,161 +0,0 @@ -resultFactoryMock = $this->createMock(ResultInterfaceFactory::class); - $this->validator = new GeneralResponseValidator($this->resultFactoryMock, new SubjectReader()); - } - - public function testValidateParsesSuccess() - { - $args = []; - - $this->resultFactoryMock->method('create') - ->with($this->callback(function ($a) use (&$args) { - // Spy on method call - $args = $a; - - return true; - })) - ->willReturn($this->createMock(ResultInterface::class)); - - $this->validator->validate([ - 'response' => [ - 'messages' => [ - 'resultCode' => 'Ok', - 'message' => [ - [ - 'code' => 'foo', - 'text' => 'bar' - ] - ] - ] - ] - ]); - - $this->assertTrue($args['isValid']); - $this->assertEmpty($args['errorCodes']); - $this->assertEmpty($args['failsDescription']); - } - - public function testValidateParsesErrors() - { - $args = []; - - $this->resultFactoryMock->method('create') - ->with($this->callback(function ($a) use (&$args) { - // Spy on method call - $args = $a; - - return true; - })) - ->willReturn($this->createMock(ResultInterface::class)); - - $this->validator->validate([ - 'response' => [ - 'errors' => [ - 'resultCode' => 'Error', - 'error' => [ - [ - 'errorCode' => 'foo', - 'errorText' => 'bar' - ] - ] - ] - ] - ]); - - $this->assertFalse($args['isValid']); - $this->assertSame(['foo'], $args['errorCodes']); - $this->assertSame(['bar'], $args['failsDescription']); - } - - public function testValidateParsesMessages() - { - $args = []; - - $this->resultFactoryMock->method('create') - ->with($this->callback(function ($a) use (&$args) { - // Spy on method call - $args = $a; - - return true; - })) - ->willReturn($this->createMock(ResultInterface::class)); - - $this->validator->validate([ - 'response' => [ - 'messages' => [ - 'resultCode' => 'Error', - 'message' => [ - [ - 'code' => 'foo', - 'text' => 'bar' - ] - ] - ] - ] - ]); - - $this->assertFalse($args['isValid']); - $this->assertSame(['foo'], $args['errorCodes']); - $this->assertSame(['bar'], $args['failsDescription']); - } - - public function testValidateParsesErrorsWhenOnlyOneIsReturned() - { - $args = []; - - $this->resultFactoryMock->method('create') - ->with($this->callback(function ($a) use (&$args) { - // Spy on method call - $args = $a; - - return true; - })) - ->willReturn($this->createMock(ResultInterface::class)); - - $this->validator->validate([ - 'response' => [ - 'messages' => [ - 'resultCode' => 'Error', - 'message' => [ - 'code' => 'foo', - 'text' => 'bar' - ] - ] - ] - ]); - - $this->assertFalse($args['isValid']); - $this->assertSame(['foo'], $args['errorCodes']); - $this->assertSame(['bar'], $args['failsDescription']); - } -} diff --git a/app/code/Magento/AuthorizenetAcceptjs/Test/Unit/Gateway/Validator/TransactionHashValidatorTest.php b/app/code/Magento/AuthorizenetAcceptjs/Test/Unit/Gateway/Validator/TransactionHashValidatorTest.php deleted file mode 100644 index fb3f9d0520d49..0000000000000 --- a/app/code/Magento/AuthorizenetAcceptjs/Test/Unit/Gateway/Validator/TransactionHashValidatorTest.php +++ /dev/null @@ -1,280 +0,0 @@ -resultFactoryMock = $this->createMock(ResultInterfaceFactory::class); - $this->configMock = $this->createMock(Config::class); - $this->resultMock = $this->createMock(ResultInterface::class); - - $this->validator = new TransactionHashValidator( - $this->resultFactoryMock, - new SubjectReader(), - $this->configMock - ); - } - - /** - * @param $response - * @param $isValid - * @param $errorCodes - * @param $errorDescriptions - * @dataProvider sha512ResponseProvider - */ - public function testValidateSha512HashScenarios( - $response, - $isValid, - $errorCodes, - $errorDescriptions - ) { - $args = []; - - $this->resultFactoryMock->method('create') - ->with($this->callback(function ($a) use (&$args) { - // Spy on method call - $args = $a; - - return true; - })) - ->willReturn($this->resultMock); - - $this->configMock->method('getTransactionSignatureKey') - ->willReturn('abc'); - $this->configMock->method('getLoginId') - ->willReturn('username'); - - $this->validator->validate($response); - - $this->assertSame($isValid, $args['isValid']); - $this->assertEquals($errorCodes, $args['errorCodes']); - $this->assertEquals($errorDescriptions, $args['failsDescription']); - } - - /** - * @param $response - * @param $isValid - * @param $errorCodes - * @param $errorDescriptions - * @dataProvider md5ResponseProvider - */ - public function testValidateMd5HashScenarios( - $response, - $isValid, - $errorCodes, - $errorDescriptions - ) { - $args = []; - - $this->resultFactoryMock->method('create') - ->with($this->callback(function ($a) use (&$args) { - // Spy on method call - $args = $a; - - return true; - })) - ->willReturn($this->resultMock); - - $this->configMock->method('getLegacyTransactionHash') - ->willReturn('abc'); - $this->configMock->method('getLoginId') - ->willReturn('username'); - - $this->validator->validate($response); - - $this->assertSame($isValid, $args['isValid']); - $this->assertEquals($errorCodes, $args['errorCodes']); - $this->assertEquals($errorDescriptions, $args['failsDescription']); - } - - public function md5ResponseProvider() - { - return [ - [ - [ - 'response' => [ - 'transactionResponse' => [ - 'transId' => '123', - 'transHash' => 'C8675D9F7BE7BE4A04C18EA1B6F7B6FD' - ] - ] - ], - true, - [], - [] - ], - [ - [ - 'response' => [ - 'transactionResponse' => [ - 'transId' => '123', - 'transHash' => 'C8675D9F7BE7BE4A04C18EA1B6F7B6FD' - ] - ] - ], - true, - [], - [] - ], - [ - [ - 'amount' => '123.00', - 'response' => [ - 'transactionResponse' => [ - 'transHash' => 'bad' - ] - ] - ], - false, - ['ETHV'], - ['The authenticity of the gateway response could not be verified.'] - ], - [ - [ - 'amount' => '123.00', - 'response' => [ - 'transactionResponse' => [ - 'refTransID' => '123', - 'transId' => '123', - 'transHash' => 'C8675D9F7BE7BE4A04C18EA1B6F7B6FD' - ] - ] - ], - true, - [], - [] - ], - ]; - } - - public function sha512ResponseProvider() - { - return [ - [ - [ - 'response' => [ - 'transactionResponse' => [ - 'transId' => '123', - 'refTransID' => '123', - 'transHashSha2' => 'CC0FF465A081D98FFC6E502C40B2DCC7655ACF591F859135B6E66558D' - . '41E3A2C654D5A2ACF4749104F3133711175C232C32676F79F70211C2984B21A33D30DEE' - ] - ] - ], - true, - [], - [] - ], - [ - [ - 'response' => [ - 'transactionResponse' => [ - 'transId' => '0', - 'refTransID' => '123', - 'transHashSha2' => '563D42F4A5189F74334088EF6A02E84F320CD8C005FB0DC436EF96084D' - . 'FAC0C76DE081DFC58A3BF825465C63B7F38E4D463025EAC44597A68C024CBBCE7A3159' - ] - ] - ], - true, - [], - [] - ], - [ - [ - 'amount' => '123.00', - 'response' => [ - 'transactionResponse' => [ - 'transId' => '0', - 'transHashSha2' => 'DEE5309078D9F7A68BA4F706FB3E58618D3991A6A5E4C39DCF9C49E693' - . '673C38BD6BB15C235263C549A6B5F0B6D7019EC729E0C275C9FEA37FB91F8B612D0A5D' - ] - ] - ], - true, - [], - [] - ], - [ - [ - 'amount' => '123.00', - 'response' => [ - 'transactionResponse' => [ - 'transId' => '123', - 'transHashSha2' => '1DBD16DED0DA02F52A22A9AD71A49F70BD2ECD42437552889912DD5CE' - . 'CBA0E09A5E8E6221DA74D98A46E5F77F7774B6D9C39CADF3E9A33D85870A6958DA7C8B2' - ] - ] - ], - true, - [], - [] - ], - [ - [ - 'amount' => '123.00', - 'response' => [ - 'transactionResponse' => [ - 'transId' => '123', - 'refTransID' => '0', - 'transHashSha2' => '1DBD16DED0DA02F52A22A9AD71A49F70BD2ECD42437552889912DD5CE' - . 'CBA0E09A5E8E6221DA74D98A46E5F77F7774B6D9C39CADF3E9A33D85870A6958DA7C8B2' - ] - ] - ], - true, - [], - [] - ], - [ - [ - 'amount' => '123.00', - 'response' => [ - 'transactionResponse' => [ - 'transHashSha2' => 'bad' - ] - ] - ], - false, - ['ETHV'], - ['The authenticity of the gateway response could not be verified.'] - ], - ]; - } -} diff --git a/app/code/Magento/AuthorizenetAcceptjs/Test/Unit/Gateway/Validator/TransactionResponseValidatorTest.php b/app/code/Magento/AuthorizenetAcceptjs/Test/Unit/Gateway/Validator/TransactionResponseValidatorTest.php deleted file mode 100644 index c59cf00899af2..0000000000000 --- a/app/code/Magento/AuthorizenetAcceptjs/Test/Unit/Gateway/Validator/TransactionResponseValidatorTest.php +++ /dev/null @@ -1,231 +0,0 @@ -resultFactoryMock = $this->createMock(ResultInterfaceFactory::class); - $this->resultMock = $this->createMock(ResultInterface::class); - - $this->validator = new TransactionResponseValidator( - $this->resultFactoryMock, - new SubjectReader() - ); - } - - /** - * @param $transactionResponse - * @param $isValid - * @param $errorCodes - * @param $errorMessages - * @dataProvider scenarioProvider - */ - public function testValidateScenarios($transactionResponse, $isValid, $errorCodes, $errorMessages) - { - $args = []; - - $this->resultFactoryMock->method('create') - ->with($this->callback(function ($a) use (&$args) { - // Spy on method call - $args = $a; - - return true; - })) - ->willReturn($this->resultMock); - - $this->validator->validate([ - 'response' => [ - 'transactionResponse' => $transactionResponse - ] - ]); - - $this->assertEquals($isValid, $args['isValid']); - $this->assertEquals($errorCodes, $args['errorCodes']); - $this->assertEquals($errorMessages, $args['failsDescription']); - } - - /** - * @SuppressWarnings(PHPMD.ExcessiveMethodLength) - */ - public function scenarioProvider() - { - return [ - // Test for acceptable reason codes - [ - [ - 'responseCode' => self::RESPONSE_CODE_APPROVED, - 'messages' => [ - 'message' => [ - 'code' => self::RESPONSE_REASON_CODE_APPROVED, - ] - ] - ], - true, - [], - [] - ], - [ - [ - 'responseCode' => self::RESPONSE_CODE_APPROVED, - 'messages' => [ - 'message' => [ - 'code' => self::RESPONSE_REASON_CODE_PENDING_REVIEW, - ] - ] - ], - true, - [], - [] - ], - [ - [ - 'responseCode' => self::RESPONSE_CODE_APPROVED, - 'messages' => [ - 'message' => [ - 'code' => self::RESPONSE_REASON_CODE_PENDING_REVIEW_AUTHORIZED, - ] - ] - ], - true, - [], - [] - ], - [ - [ - 'responseCode' => self::RESPONSE_CODE_HELD, - 'messages' => [ - 'message' => [ - 'code' => self::RESPONSE_REASON_CODE_APPROVED, - ] - ] - ], - true, - [], - [] - ], - [ - [ - 'responseCode' => self::RESPONSE_CODE_HELD, - 'messages' => [ - 'message' => [ - 'code' => self::RESPONSE_REASON_CODE_PENDING_REVIEW, - ] - ] - ], - true, - [], - [] - ], - [ - [ - 'responseCode' => self::RESPONSE_CODE_HELD, - 'messages' => [ - 'message' => [ - 'code' => self::RESPONSE_REASON_CODE_PENDING_REVIEW_AUTHORIZED, - ] - ] - ], - true, - [], - [] - ], - - // Test for reason codes that aren't acceptable - [ - [ - 'responseCode' => self::RESPONSE_CODE_APPROVED, - 'messages' => [ - 'message' => [ - [ - 'description' => 'bar', - 'code' => 'foo', - ] - ] - ] - ], - false, - ['foo'], - ['bar'] - ], - [ - [ - 'responseCode' => self::RESPONSE_CODE_APPROVED, - 'messages' => [ - 'message' => [ - // Alternate, non-array sytax - 'text' => 'bar', - 'code' => 'foo', - ] - ] - ], - false, - ['foo'], - ['bar'] - ], - [ - [ - 'responseCode' => self::RESPONSE_CODE_DENIED, - 'errors' => [ - [ - 'errorCode' => self::ERROR_CODE_AVS_MISMATCH, - 'errorText' => 'bar' - ] - ] - ], - false, - [self::ERROR_CODE_AVS_MISMATCH], - ['bar'] - ], - // This validator only cares about successful edge cases so test for default behavior - [ - [ - 'responseCode' => 'foo', - ], - false, - [], - [] - ], - ]; - } -} diff --git a/app/code/Magento/AuthorizenetAcceptjs/Test/Unit/Model/Ui/ConfigProviderTest.php b/app/code/Magento/AuthorizenetAcceptjs/Test/Unit/Model/Ui/ConfigProviderTest.php deleted file mode 100644 index dea4557fd584c..0000000000000 --- a/app/code/Magento/AuthorizenetAcceptjs/Test/Unit/Model/Ui/ConfigProviderTest.php +++ /dev/null @@ -1,76 +0,0 @@ -cart = $this->createMock(CartInterface::class); - $this->config = $this->createMock(Config::class); - $this->provider = new ConfigProvider($this->config, $this->cart); - } - - public function testProviderRetrievesValues() - { - $this->cart->method('getStoreId') - ->willReturn('123'); - - $this->config->method('getClientKey') - ->with('123') - ->willReturn('foo'); - - $this->config->method('getLoginId') - ->with('123') - ->willReturn('bar'); - - $this->config->method('getEnvironment') - ->with('123') - ->willReturn('baz'); - - $this->config->method('isCvvEnabled') - ->with('123') - ->willReturn(false); - - $expected = [ - 'payment' => [ - Config::METHOD => [ - 'clientKey' => 'foo', - 'apiLoginID' => 'bar', - 'environment' => 'baz', - 'useCvv' => false, - ] - ] - ]; - - $this->assertEquals($expected, $this->provider->getConfig()); - } -} diff --git a/app/code/Magento/AuthorizenetAcceptjs/Test/Unit/Observer/DataAssignObserverTest.php b/app/code/Magento/AuthorizenetAcceptjs/Test/Unit/Observer/DataAssignObserverTest.php deleted file mode 100644 index bd439a336786b..0000000000000 --- a/app/code/Magento/AuthorizenetAcceptjs/Test/Unit/Observer/DataAssignObserverTest.php +++ /dev/null @@ -1,80 +0,0 @@ - 'foo', - 'opaqueDataValue' => 'bar', - 'ccLast4' => '1234' - ]; - - $observerContainer = $this->createMock(Observer::class); - $event = $this->createMock(Event::class); - $paymentInfoModel = $this->createMock(InfoInterface::class); - $dataObject = new DataObject([PaymentInterface::KEY_ADDITIONAL_DATA => $additionalInfo]); - $observerContainer->method('getEvent') - ->willReturn($event); - $event->method('getDataByKey') - ->willReturnMap( - [ - [AbstractDataAssignObserver::MODEL_CODE, $paymentInfoModel], - [AbstractDataAssignObserver::DATA_CODE, $dataObject] - ] - ); - $paymentInfoModel->expects($this->at(0)) - ->method('setAdditionalInformation') - ->with('opaqueDataDescriptor', 'foo'); - $paymentInfoModel->expects($this->at(1)) - ->method('setAdditionalInformation') - ->with('opaqueDataValue', 'bar'); - $paymentInfoModel->expects($this->at(2)) - ->method('setAdditionalInformation') - ->with('ccLast4', '1234'); - - $observer = new DataAssignObserver(); - $observer->execute($observerContainer); - } - - public function testDoestSetDataWhenEmpty() - { - $observerContainer = $this->createMock(Observer::class); - $event = $this->createMock(Event::class); - $paymentInfoModel = $this->createMock(InfoInterface::class); - $observerContainer->method('getEvent') - ->willReturn($event); - $event->method('getDataByKey') - ->willReturnMap( - [ - [AbstractDataAssignObserver::MODEL_CODE, $paymentInfoModel], - [AbstractDataAssignObserver::DATA_CODE, new DataObject()] - ] - ); - $paymentInfoModel->expects($this->never()) - ->method('setAdditionalInformation'); - - $observer = new DataAssignObserver(); - $observer->execute($observerContainer); - } -} diff --git a/app/code/Magento/AuthorizenetAcceptjs/Test/Unit/Setup/Patch/Data/CopyCurrentConfigTest.php b/app/code/Magento/AuthorizenetAcceptjs/Test/Unit/Setup/Patch/Data/CopyCurrentConfigTest.php deleted file mode 100644 index 5ac8a6ca9b3f6..0000000000000 --- a/app/code/Magento/AuthorizenetAcceptjs/Test/Unit/Setup/Patch/Data/CopyCurrentConfigTest.php +++ /dev/null @@ -1,149 +0,0 @@ -scopeConfig = $this->createMock(Config::class); - $this->resourceConfig = $this->createMock(ResourceConfig::class); - $this->encryptor = $this->createMock(Encryptor::class); - $this->setup = $this->createMock(DataSetup::class); - - $this->setup->expects($this->once()) - ->method('startSetup') - ->willReturn(null); - - $this->setup->expects($this->once()) - ->method('endSetup') - ->willReturn(null); - - $this->context = $this->createMock(ModuleContext::class); - $this->storeManager = $this->createMock(StoreManagerInterface::class); - $this->website = $this->createMock(Website::class); - } - - public function testMigrateData(): void - { - $this->scopeConfig->expects($this->exactly(26)) - ->method('getValue') - ->willReturn('TestValue'); - - $this->resourceConfig->expects($this->exactly(26)) - ->method('saveConfig') - ->willReturn(null); - - $this->encryptor->expects($this->exactly(6)) - ->method('encrypt') - ->willReturn('TestValue'); - - $this->website->expects($this->once()) - ->method('getId') - ->willReturn(1); - - $this->storeManager->expects($this->once()) - ->method('getWebsites') - ->willReturn([$this->website]); - - $objectManager = new ObjectManager($this); - - $installer = $objectManager->getObject( - CopyCurrentConfig::class, - [ - 'moduleDataSetup' => $this->setup, - 'scopeConfig' => $this->scopeConfig, - 'resourceConfig' => $this->resourceConfig, - 'encryptor' => $this->encryptor, - 'storeManager' => $this->storeManager - ] - ); - - $installer->apply($this->context); - } - - public function testMigrateDataNullFields(): void - { - $this->scopeConfig->expects($this->exactly(13)) - ->method('getValue') - ->will($this->onConsecutiveCalls(1, 2, 3, 4, 5, 6, 7, 8, 9, 10)); - - $this->resourceConfig->expects($this->exactly(10)) - ->method('saveConfig') - ->willReturn(null); - - $this->encryptor->expects($this->never()) - ->method('encrypt'); - - $this->storeManager->expects($this->once()) - ->method('getWebsites') - ->willReturn([]); - - $objectManager = new ObjectManager($this); - - $installer = $objectManager->getObject( - CopyCurrentConfig::class, - [ - 'moduleDataSetup' => $this->setup, - 'scopeConfig' => $this->scopeConfig, - 'resourceConfig' => $this->resourceConfig, - 'encryptor' => $this->encryptor, - 'storeManager' => $this->storeManager - ] - ); - - $installer->apply($this->context); - } -} diff --git a/app/code/Magento/AuthorizenetAcceptjs/composer.json b/app/code/Magento/AuthorizenetAcceptjs/composer.json deleted file mode 100644 index a54387ca6de28..0000000000000 --- a/app/code/Magento/AuthorizenetAcceptjs/composer.json +++ /dev/null @@ -1,31 +0,0 @@ -{ - "name": "magento/module-authorizenet-acceptjs", - "description": "N/A", - "config": { - "sort-packages": true - }, - "require": { - "php": "~7.1.3||~7.2.0||~7.3.0", - "magento/framework": "*", - "magento/module-payment": "*", - "magento/module-sales": "*", - "magento/module-config": "*", - "magento/module-backend": "*", - "magento/module-checkout": "*", - "magento/module-store": "*", - "magento/module-quote": "*" - }, - "type": "magento2-module", - "license": [ - "OSL-3.0", - "AFL-3.0" - ], - "autoload": { - "files": [ - "registration.php" - ], - "psr-4": { - "Magento\\AuthorizenetAcceptjs\\": "" - } - } -} diff --git a/app/code/Magento/AuthorizenetAcceptjs/etc/adminhtml/di.xml b/app/code/Magento/AuthorizenetAcceptjs/etc/adminhtml/di.xml deleted file mode 100644 index f4059aebbe3e3..0000000000000 --- a/app/code/Magento/AuthorizenetAcceptjs/etc/adminhtml/di.xml +++ /dev/null @@ -1,39 +0,0 @@ - - - - - - Magento\AuthorizenetAcceptjs\Model\Ui\ConfigProvider - - - - - - AuthorizenetAcceptjsTransactionRequestTypeBuilder - Magento\AuthorizenetAcceptjs\Gateway\Request\StoreConfigBuilder - Magento\AuthorizenetAcceptjs\Gateway\Request\AuthenticationDataBuilder - Magento\AuthorizenetAcceptjs\Gateway\Request\AuthorizeDataBuilder - Magento\AuthorizenetAcceptjs\Gateway\Request\AmountDataBuilder - Magento\AuthorizenetAcceptjs\Gateway\Request\PaymentDataBuilder - Magento\AuthorizenetAcceptjs\Gateway\Request\ShippingDataBuilder - Magento\AuthorizenetAcceptjs\Gateway\Request\SolutionDataBuilder - Magento\AuthorizenetAcceptjs\Gateway\Request\OrderDataBuilder - Magento\AuthorizenetAcceptjs\Gateway\Request\PoDataBuilder - Magento\AuthorizenetAcceptjs\Gateway\Request\CustomerDataBuilder - Magento\AuthorizenetAcceptjs\Gateway\Request\AddressDataBuilder - Magento\AuthorizenetAcceptjs\Gateway\Request\CustomSettingsBuilder - Magento\AuthorizenetAcceptjs\Gateway\Request\PassthroughDataBuilder - - - - - - AuthorizenetAcceptjsTransactionValidator - - - diff --git a/app/code/Magento/AuthorizenetAcceptjs/etc/adminhtml/system.xml b/app/code/Magento/AuthorizenetAcceptjs/etc/adminhtml/system.xml deleted file mode 100644 index 7cd00959d9772..0000000000000 --- a/app/code/Magento/AuthorizenetAcceptjs/etc/adminhtml/system.xml +++ /dev/null @@ -1,139 +0,0 @@ - - - - -
- - - - - Magento\Config\Model\Config\Source\Yesno - - - - - - - 1 - Magento\Config\Block\System\Config\Form\Fieldset - - - payment/authorizenet_acceptjs/title - - - - Magento\AuthorizenetAcceptjs\Model\Adminhtml\Source\Environment - payment/authorizenet_acceptjs/environment - - - - Magento\AuthorizenetAcceptjs\Model\Adminhtml\Source\PaymentAction - payment/authorizenet_acceptjs/payment_action - - - - Magento\Config\Model\Config\Backend\Encrypted - payment/authorizenet_acceptjs/login - required-entry - - 1 - - - - - Magento\Config\Model\Config\Backend\Encrypted - payment/authorizenet_acceptjs/trans_key - required-entry - - 1 - - - - - payment/authorizenet_acceptjs/public_client_key - required-entry - - 1 - - - - - Magento\Config\Model\Config\Backend\Encrypted - payment/authorizenet_acceptjs/trans_signature_key - required-entry - - 1 - - - - - Magento\Config\Model\Config\Backend\Encrypted - payment/authorizenet_acceptjs/trans_md5 - - 1 - - - - - - 0 - - - Magento\Config\Model\Config\Source\Locale\Currency - payment/authorizenet_acceptjs/currency - - - - Magento\Config\Model\Config\Source\Yesno - payment/authorizenet_acceptjs/debug - - - - Magento\Config\Model\Config\Source\Yesno - payment/authorizenet_acceptjs/email_customer - - - - Magento\Config\Model\Config\Source\Yesno - payment/authorizenet_acceptjs/cvv_enabled - - - - Magento\AuthorizenetAcceptjs\Model\Adminhtml\Source\Cctype - payment/authorizenet_acceptjs/cctypes - - - - Magento\Payment\Model\Config\Source\Allspecificcountries - payment/authorizenet_acceptjs/allowspecific - - - - Magento\Directory\Model\Config\Source\Country - payment/authorizenet_acceptjs/specificcountry - - - - payment/authorizenet_acceptjs/min_order_total - validate-number validate-zero-or-greater - - - - payment/authorizenet_acceptjs/max_order_total - validate-number validate-zero-or-greater - - - - validate-number - payment/authorizenet_acceptjs/sort_order - - - -
-
-
diff --git a/app/code/Magento/AuthorizenetAcceptjs/etc/authorizenet_acceptjs_error_mapping.xml b/app/code/Magento/AuthorizenetAcceptjs/etc/authorizenet_acceptjs_error_mapping.xml deleted file mode 100644 index 507a9b14f917b..0000000000000 --- a/app/code/Magento/AuthorizenetAcceptjs/etc/authorizenet_acceptjs_error_mapping.xml +++ /dev/null @@ -1,15 +0,0 @@ - - - - - Invalid request to gateway. - Invalid gateway credentials. - Transaction has been declined. Please try again later. - The authenticity of the gateway response could not be verified. - - diff --git a/app/code/Magento/AuthorizenetAcceptjs/etc/config.xml b/app/code/Magento/AuthorizenetAcceptjs/etc/config.xml deleted file mode 100644 index 6fdbb98a78f8b..0000000000000 --- a/app/code/Magento/AuthorizenetAcceptjs/etc/config.xml +++ /dev/null @@ -1,57 +0,0 @@ - - - - - - - - \.authorize\.net/v1/Accept - - - - - - 0 - AE,VI,MC,DI,JCB,DN - 0 - 1 - 1 - 0 - 1 - 1 - 1 - 1 - 1 - 1 - 1 - 1 - 1 - 1 - 1 - 1 - AuthorizenetAcceptjsFacade - 0 - - processing - authorize - Credit Card (Authorize.Net) - 1 - - - - - 0 - USD - production - authCode,avsResultCode,cvvResultCode,cavvResultCode - accountType,ccLast4,authCode,avsResultCode,cvvResultCode,cavvResultCode - transactionStatus,responseCode,responseReasonCode,authCode,AVSResponse,cardCodeResponse,CAVVResponse - - - - diff --git a/app/code/Magento/AuthorizenetAcceptjs/etc/di.xml b/app/code/Magento/AuthorizenetAcceptjs/etc/di.xml deleted file mode 100644 index 1bff19e15a65f..0000000000000 --- a/app/code/Magento/AuthorizenetAcceptjs/etc/di.xml +++ /dev/null @@ -1,435 +0,0 @@ - - - - - - Magento\AuthorizenetAcceptjs\Gateway\Config::METHOD - - - - - authorizenet_acceptjs - Magento\AuthorizenetAcceptjs\Block\Form - AuthorizenetAcceptjsInfoBlock - AuthorizenetAcceptjsValueHandlerPool - AuthorizenetAcceptjsValidatorPool - AuthorizenetAcceptjsCommandPool - - - - - - AuthorizenetAcceptjsAuthorizeCommand - AuthorizenetAcceptjsCaptureCommand - AuthorizenetAcceptjsSaleCommand - AuthorizenetAcceptjsSettleCommand - AuthorizenetAcceptjsVoidCommand - AuthorizenetAcceptjsRefundCommand - AuthorizenetAcceptjsRefundSettledCommand - AuthorizenetAcceptjsCancelCommand - AuthorizenetAcceptjsAcceptPaymentCommand - AuthorizenetAcceptjsAcceptFdsCommand - AuthorizenetAcceptjsCancelCommand - AuthorizenetAcceptjsTransactionDetailsCommand - AuthorizenetAcceptjsFetchTransactionInfoCommand - - - - - - - - - Magento\AuthorizenetAcceptjs\Gateway\Validator\GeneralResponseValidator - - - - - - - true - - - Magento\AuthorizenetAcceptjs\Gateway\Validator\GeneralResponseValidator - Magento\AuthorizenetAcceptjs\Gateway\Validator\TransactionResponseValidator - Magento\AuthorizenetAcceptjs\Gateway\Validator\TransactionHashValidator - - - - - - - Magento\AuthorizenetAcceptjs\Gateway\Validator\GeneralResponseValidator - - - - - - Magento\AuthorizenetAcceptjs\Gateway\Config - - - - - - AuthorizenetAcceptjsCountryValidator - - - - - - - - - AuthorizenetAcceptjsCommandPool - - - - - AuthorizenetAcceptjsTransactionDetailsRequest - AuthorizenetAcceptjsDefaultTransferFactory - Magento\AuthorizenetAcceptjs\Gateway\Http\Client - AuthorizenetAcceptjsTransactionDetailsValidator - - - - - AuthorizenetAcceptjsAuthorizeRequest - AuthorizenetAcceptjsDefaultTransferFactory - Magento\AuthorizenetAcceptjs\Gateway\Http\Client - AuthorizenetAcceptjsAuthorizationHandler - AuthorizenetAcceptjsTransactionValidator - AuthorizenetAcceptjsVirtualErrorMessageMapper - - - - - AuthorizenetAcceptjsAcceptsFdsRequest - AuthorizenetAcceptjsDefaultTransferFactory - Magento\AuthorizenetAcceptjs\Gateway\Http\Client - AuthorizenetAcceptjsAcceptsFdsRequestValidator - - - - - AuthorizenetAcceptjsCommandPool - - - - - AuthorizenetAcceptjsSaleRequest - AuthorizenetAcceptjsSaleHandler - - - - - AuthorizenetAcceptjsCommandPool - - - - - AuthorizenetAcceptjsRefundRequest - AuthorizenetAcceptjsRefundSettledHandler - AuthorizenetAcceptjsTransactionValidator - - - - - AuthorizenetAcceptjsCommandPool - - - - - AuthorizenetAcceptjsCaptureRequest - AuthorizenetAcceptjsCaptureTransactionHandler - AuthorizenetAcceptjsTransactionValidator - - - - - AuthorizenetAcceptjsVoidRequest - AuthorizenetAcceptjsDefaultTransferFactory - Magento\AuthorizenetAcceptjs\Gateway\Http\Client - AuthorizenetAcceptjsVoidHandler - AuthorizenetAcceptjsTransactionValidator - AuthorizenetAcceptjsVirtualErrorMessageMapper - - - - - AuthorizenetAcceptjsCancelHandler - - - - - - - - - Magento\AuthorizenetAcceptjs\Gateway\Response\PaymentReviewStatusHandler - - - - - - - Magento\AuthorizenetAcceptjs\Gateway\Response\TransactionIdHandler - Magento\AuthorizenetAcceptjs\Gateway\Response\PaymentResponseHandler - Magento\AuthorizenetAcceptjs\Gateway\Response\TransactionDetailsResponseHandler - - - - - - - Magento\AuthorizenetAcceptjs\Gateway\Response\CloseParentTransactionHandler - - - - - - false - - - - - - CloseCaptureTransactionHandler - - - - - - - Magento\AuthorizenetAcceptjs\Gateway\Response\TransactionIdHandler - Magento\AuthorizenetAcceptjs\Gateway\Response\ClosePartialTransactionHandler - - - - - - - Magento\AuthorizenetAcceptjs\Gateway\Response\VoidResponseHandler - - - - - - - Magento\AuthorizenetAcceptjs\Gateway\Response\CloseTransactionHandler - Magento\AuthorizenetAcceptjs\Gateway\Response\CloseParentTransactionHandler - - - - - - - - - - - AuthorizenetAcceptjsTransactionDetailsRequestTypeBuilder - Magento\AuthorizenetAcceptjs\Gateway\Request\StoreConfigBuilder - Magento\AuthorizenetAcceptjs\Gateway\Request\AuthenticationDataBuilder - Magento\AuthorizenetAcceptjs\Gateway\Request\TransactionDetailsDataBuilder - - - - - - - AuthorizenetAcceptjsAcceptsFdsRequestTypeBuilder - Magento\AuthorizenetAcceptjs\Gateway\Request\StoreConfigBuilder - Magento\AuthorizenetAcceptjs\Gateway\Request\AuthenticationDataBuilder - Magento\AuthorizenetAcceptjs\Gateway\Request\AcceptFdsDataBuilder - - - - - - - AuthorizenetAcceptjsTransactionRequestTypeBuilder - Magento\AuthorizenetAcceptjs\Gateway\Request\StoreConfigBuilder - Magento\AuthorizenetAcceptjs\Gateway\Request\AuthenticationDataBuilder - Magento\AuthorizenetAcceptjs\Gateway\Request\AuthorizeDataBuilder - Magento\AuthorizenetAcceptjs\Gateway\Request\AmountDataBuilder - Magento\AuthorizenetAcceptjs\Gateway\Request\PaymentDataBuilder - Magento\AuthorizenetAcceptjs\Gateway\Request\ShippingDataBuilder - Magento\AuthorizenetAcceptjs\Gateway\Request\SolutionDataBuilder - Magento\AuthorizenetAcceptjs\Gateway\Request\OrderDataBuilder - Magento\AuthorizenetAcceptjs\Gateway\Request\PoDataBuilder - Magento\AuthorizenetAcceptjs\Gateway\Request\CustomerDataBuilder - Magento\AuthorizenetAcceptjs\Gateway\Request\AddressDataBuilder - Magento\AuthorizenetAcceptjs\Gateway\Request\StubDataBuilder - Magento\AuthorizenetAcceptjs\Gateway\Request\CustomSettingsBuilder - Magento\AuthorizenetAcceptjs\Gateway\Request\PassthroughDataBuilder - - - - - - - Magento\AuthorizenetAcceptjs\Gateway\Request\SaleDataBuilder - - - - - - - AuthorizenetAcceptjsTransactionRequestTypeBuilder - Magento\AuthorizenetAcceptjs\Gateway\Request\StoreConfigBuilder - Magento\AuthorizenetAcceptjs\Gateway\Request\AuthenticationDataBuilder - Magento\AuthorizenetAcceptjs\Gateway\Request\RefundTransactionTypeDataBuilder - Magento\AuthorizenetAcceptjs\Gateway\Request\AmountDataBuilder - Magento\AuthorizenetAcceptjs\Gateway\Request\RefundPaymentDataBuilder - Magento\AuthorizenetAcceptjs\Gateway\Request\ShippingDataBuilder - Magento\AuthorizenetAcceptjs\Gateway\Request\RefundReferenceTransactionDataBuilder - Magento\AuthorizenetAcceptjs\Gateway\Request\OrderDataBuilder - Magento\AuthorizenetAcceptjs\Gateway\Request\PoDataBuilder - Magento\AuthorizenetAcceptjs\Gateway\Request\CustomerDataBuilder - Magento\AuthorizenetAcceptjs\Gateway\Request\AddressDataBuilder - Magento\AuthorizenetAcceptjs\Gateway\Request\CustomSettingsBuilder - Magento\AuthorizenetAcceptjs\Gateway\Request\PassthroughDataBuilder - - - - - - - AuthorizenetAcceptjsTransactionRequestTypeBuilder - Magento\AuthorizenetAcceptjs\Gateway\Request\StoreConfigBuilder - Magento\AuthorizenetAcceptjs\Gateway\Request\AuthenticationDataBuilder - Magento\AuthorizenetAcceptjs\Gateway\Request\CaptureDataBuilder - Magento\AuthorizenetAcceptjs\Gateway\Request\PassthroughDataBuilder - - - - - - - AuthorizenetAcceptjsTransactionRequestTypeBuilder - Magento\AuthorizenetAcceptjs\Gateway\Request\StoreConfigBuilder - Magento\AuthorizenetAcceptjs\Gateway\Request\AuthenticationDataBuilder - Magento\AuthorizenetAcceptjs\Gateway\Request\VoidDataBuilder - Magento\AuthorizenetAcceptjs\Gateway\Request\PassthroughDataBuilder - - - - - - - - - createTransactionRequest - - - - - getTransactionDetailsRequest - - - - - updateHeldTransactionRequest - - - - - - - - authorizenet_acceptjs_error_mapping.xml - - - - - AuthorizenetAcceptjsErrorMappingConfigReader - authorizenet_acceptjs_error_mapper - - - - - AuthorizenetAcceptjsErrorMappingData - - - - - - - AuthorizenetAcceptjsPaymentReviewStatusHandler - - - - - - AuthorizenetAcceptjsCommandManager - - - - - - - 1 - 1 - 1 - 1 - 1 - 1 - - - 1 - 1 - - - - - - Magento\AuthorizenetAcceptjs\Gateway\Config - - - - - - AuthorizenetAcceptjsDefaultValueHandler - - - - - - AuthorizenetAcceptjsCommandPool - - - - - Magento\AuthorizenetAcceptjs\Gateway\Config - - - - - AuthorizenetAcceptjsLogger - - - - - - store_id - - - - - - - AuthorizenetAcceptjsRemoveStoreConfigFilter - - - - - - Magento\AuthorizenetAcceptjs\Gateway\Config - - - diff --git a/app/code/Magento/AuthorizenetAcceptjs/etc/events.xml b/app/code/Magento/AuthorizenetAcceptjs/etc/events.xml deleted file mode 100644 index 93dc448d1d895..0000000000000 --- a/app/code/Magento/AuthorizenetAcceptjs/etc/events.xml +++ /dev/null @@ -1,13 +0,0 @@ - - - - - - - - diff --git a/app/code/Magento/AuthorizenetAcceptjs/etc/frontend/di.xml b/app/code/Magento/AuthorizenetAcceptjs/etc/frontend/di.xml deleted file mode 100644 index 8b0e570abbd2e..0000000000000 --- a/app/code/Magento/AuthorizenetAcceptjs/etc/frontend/di.xml +++ /dev/null @@ -1,30 +0,0 @@ - - - - - - - 1 - - - - - - - Magento\AuthorizenetAcceptjs\Model\Ui\ConfigProvider - - - - - - - Magento\AuthorizenetAcceptjs\Gateway\Config::METHOD - - - - diff --git a/app/code/Magento/AuthorizenetAcceptjs/etc/module.xml b/app/code/Magento/AuthorizenetAcceptjs/etc/module.xml deleted file mode 100644 index 6bc8fe3c4daee..0000000000000 --- a/app/code/Magento/AuthorizenetAcceptjs/etc/module.xml +++ /dev/null @@ -1,19 +0,0 @@ - - - - - - - - - - - - - - diff --git a/app/code/Magento/AuthorizenetAcceptjs/etc/payment.xml b/app/code/Magento/AuthorizenetAcceptjs/etc/payment.xml deleted file mode 100644 index b9f8d40b03006..0000000000000 --- a/app/code/Magento/AuthorizenetAcceptjs/etc/payment.xml +++ /dev/null @@ -1,15 +0,0 @@ - - - - - - 0 - - - diff --git a/app/code/Magento/AuthorizenetAcceptjs/i18n/en_US.csv b/app/code/Magento/AuthorizenetAcceptjs/i18n/en_US.csv deleted file mode 100644 index a8b5dbd2df525..0000000000000 --- a/app/code/Magento/AuthorizenetAcceptjs/i18n/en_US.csv +++ /dev/null @@ -1,22 +0,0 @@ -"Authorize.Net (Deprecated)","Authorize.Net (Deprecated)" -"Gateway URL","Gateway URL" -"Invalid payload type.","Invalid payload type." -"Something went wrong in the payment gateway.","Something went wrong in the payment gateway." -"Merchant MD5 (deprecated","Merchant MD5 (deprecated" -"Signature Key","Signature Key" -"Basic Authorize.Net Settings","Basic Authorize.Net Settings" -"Advanced Authorize.Net Settings","Advanced Authorize.Net Settings" -"Public Client Key","Public Client Key" -"Environment","Environment" -"Production","Production" -"Sandbox","Sandbox" -"accountType","Account Type" -"authCode", "Processor Response Text" -"avsResultCode", "AVS Response Code" -"cvvResultCode","CVV Response Code" -"cavvResultCode","CAVV Response Code" -"Enable Credit Card Verification Field","Enable Credit Card Verification Field" -"ccLast4","Last 4 Digits of Card" -"There was an error while trying to process the refund.","There was an error while trying to process the refund." -"This transaction cannot be refunded with its current status.","This transaction cannot be refunded with its current status." -"The transaction has not been settled, a partial refund is not yet available.","The transaction has not been settled, a partial refund is not yet available." diff --git a/app/code/Magento/AuthorizenetAcceptjs/registration.php b/app/code/Magento/AuthorizenetAcceptjs/registration.php deleted file mode 100644 index 5338c9a4ddc80..0000000000000 --- a/app/code/Magento/AuthorizenetAcceptjs/registration.php +++ /dev/null @@ -1,11 +0,0 @@ - - - - - - - Magento\AuthorizenetAcceptjs\Gateway\Config::METHOD - Magento_AuthorizenetAcceptjs::form/cc.phtml - - - - - - - diff --git a/app/code/Magento/AuthorizenetAcceptjs/view/adminhtml/layout/sales_order_create_load_block_billing_method.xml b/app/code/Magento/AuthorizenetAcceptjs/view/adminhtml/layout/sales_order_create_load_block_billing_method.xml deleted file mode 100644 index 13f6d38e2b81a..0000000000000 --- a/app/code/Magento/AuthorizenetAcceptjs/view/adminhtml/layout/sales_order_create_load_block_billing_method.xml +++ /dev/null @@ -1,17 +0,0 @@ - - - - - - - Magento\AuthorizenetAcceptjs\Gateway\Config::METHOD - Magento_AuthorizenetAcceptjs::form/cc.phtml - - - - diff --git a/app/code/Magento/AuthorizenetAcceptjs/view/adminhtml/templates/form/cc.phtml b/app/code/Magento/AuthorizenetAcceptjs/view/adminhtml/templates/form/cc.phtml deleted file mode 100644 index b757e55aaddee..0000000000000 --- a/app/code/Magento/AuthorizenetAcceptjs/view/adminhtml/templates/form/cc.phtml +++ /dev/null @@ -1,92 +0,0 @@ -escapeHtml($block->getMethodCode()); -$ccType = $block->getInfoData('cc_type'); -$ccExpMonth = $block->getInfoData('cc_exp_month'); -$ccExpYear = $block->getInfoData('cc_exp_year'); -?> - diff --git a/app/code/Magento/AuthorizenetAcceptjs/view/adminhtml/templates/payment/script.phtml b/app/code/Magento/AuthorizenetAcceptjs/view/adminhtml/templates/payment/script.phtml deleted file mode 100644 index 6be6008dba507..0000000000000 --- a/app/code/Magento/AuthorizenetAcceptjs/view/adminhtml/templates/payment/script.phtml +++ /dev/null @@ -1,24 +0,0 @@ - - diff --git a/app/code/Magento/AuthorizenetAcceptjs/view/adminhtml/web/js/authorizenet.js b/app/code/Magento/AuthorizenetAcceptjs/view/adminhtml/web/js/authorizenet.js deleted file mode 100644 index 0eb865d7666b3..0000000000000 --- a/app/code/Magento/AuthorizenetAcceptjs/view/adminhtml/web/js/authorizenet.js +++ /dev/null @@ -1,196 +0,0 @@ -/** - * Copyright © Magento, Inc. All rights reserved. - * See COPYING.txt for license details. - */ -define([ - 'jquery', - 'uiComponent', - 'Magento_Ui/js/modal/alert', - 'Magento_AuthorizenetAcceptjs/js/view/payment/acceptjs-client' -], function ($, Class, alert, AcceptjsClient) { - 'use strict'; - - return Class.extend({ - defaults: { - acceptjsClient: null, - $selector: null, - selector: 'edit_form', - container: 'payment_form_authorizenet_acceptjs', - active: false, - imports: { - onActiveChange: 'active' - } - }, - - /** - * @{inheritdoc} - */ - initConfig: function (config) { - this._super(); - - this.acceptjsClient = AcceptjsClient({ - environment: config.environment - }); - - return this; - }, - - /** - * @{inheritdoc} - */ - initObservable: function () { - this.$selector = $('#' + this.selector); - this._super() - .observe('active'); - - // re-init payment method events - this.$selector.off('changePaymentMethod.' + this.code) - .on('changePaymentMethod.' + this.code, this.changePaymentMethod.bind(this)); - - return this; - }, - - /** - * Enable/disable current payment method - * - * @param {Object} event - * @param {String} method - * @returns {Object} - */ - changePaymentMethod: function (event, method) { - this.active(method === this.code); - - return this; - }, - - /** - * Triggered when payment changed - * - * @param {Boolean} isActive - */ - onActiveChange: function (isActive) { - if (!isActive) { - - return; - } - - this.disableEventListeners(); - - window.order.addExcludedPaymentMethod(this.code); - - this.enableEventListeners(); - }, - - /** - * Sets the payment details on the form - * - * @param {Object} tokens - */ - setPaymentDetails: function (tokens) { - var $ccNumber = $(this.getSelector('cc_number')), - ccLast4 = $ccNumber.val().replace(/[^\d]/g, '').substr(-4); - - $(this.getSelector('opaque_data_descriptor')).val(tokens.opaqueDataDescriptor); - $(this.getSelector('opaque_data_value')).val(tokens.opaqueDataValue); - $(this.getSelector('cc_last_4')).val(ccLast4); - $ccNumber.val(''); - $(this.getSelector('cc_exp_month')).val(''); - $(this.getSelector('cc_exp_year')).val(''); - - if (this.useCvv) { - $(this.getSelector('cc_cid')).val(''); - } - }, - - /** - * Trigger order submit - */ - submitOrder: function () { - var authData = {}, - cardData = {}, - secureData = {}; - - this.$selector.validate().form(); - this.$selector.trigger('afterValidate.beforeSubmit'); - - authData.clientKey = this.clientKey; - authData.apiLoginID = this.apiLoginID; - - cardData.cardNumber = $(this.getSelector('cc_number')).val(); - cardData.month = $(this.getSelector('cc_exp_month')).val(); - cardData.year = $(this.getSelector('cc_exp_year')).val(); - - if (this.useCvv) { - cardData.cardCode = $(this.getSelector('cc_cid')).val(); - } - - secureData.authData = authData; - secureData.cardData = cardData; - - this.disableEventListeners(); - - this.acceptjsClient.createTokens(secureData) - .always(function () { - $('body').trigger('processStop'); - this.enableEventListeners(); - }.bind(this)) - .done(function (tokens) { - this.setPaymentDetails(tokens); - this.placeOrder(); - }.bind(this)) - .fail(function (messages) { - this.tokens = null; - - if (messages.length > 0) { - this._showError(messages[0]); - } - }.bind(this)); - - return false; - }, - - /** - * Place order - */ - placeOrder: function () { - this.$selector.trigger('realOrder'); - }, - - /** - * Get jQuery selector - * - * @param {String} field - * @returns {String} - */ - getSelector: function (field) { - return '#' + this.code + '_' + field; - }, - - /** - * Show alert message - * - * @param {String} message - */ - _showError: function (message) { - alert({ - content: message - }); - }, - - /** - * Enable form event listeners - */ - enableEventListeners: function () { - this.$selector.on('submitOrder.authorizenetacceptjs', this.submitOrder.bind(this)); - }, - - /** - * Disable form event listeners - */ - disableEventListeners: function () { - this.$selector.off('submitOrder'); - this.$selector.off('submit'); - } - - }); -}); diff --git a/app/code/Magento/AuthorizenetAcceptjs/view/adminhtml/web/js/payment-form.js b/app/code/Magento/AuthorizenetAcceptjs/view/adminhtml/web/js/payment-form.js deleted file mode 100644 index e3a0886797d63..0000000000000 --- a/app/code/Magento/AuthorizenetAcceptjs/view/adminhtml/web/js/payment-form.js +++ /dev/null @@ -1,17 +0,0 @@ -/** - * Copyright © Magento, Inc. All rights reserved. - * See COPYING.txt for license details. - */ -define([ - 'Magento_AuthorizenetAcceptjs/js/authorizenet', - 'jquery' -], function (AuthorizenetAcceptjs, $) { - 'use strict'; - - return function (config, element) { - var $form = $(element); - - config.active = $form.length > 0 && !$form.is(':hidden'); - new AuthorizenetAcceptjs(config); - }; -}); diff --git a/app/code/Magento/AuthorizenetAcceptjs/view/base/requirejs-config.js b/app/code/Magento/AuthorizenetAcceptjs/view/base/requirejs-config.js deleted file mode 100644 index 83ddd1094ea1a..0000000000000 --- a/app/code/Magento/AuthorizenetAcceptjs/view/base/requirejs-config.js +++ /dev/null @@ -1,19 +0,0 @@ -/** - * Copyright © Magento, Inc. All rights reserved. - * See COPYING.txt for license details. - */ - -var config = { - shim: { - acceptjs: { - exports: 'Accept' - }, - acceptjssandbox: { - exports: 'Accept' - } - }, - paths: { - acceptjssandbox: 'https://jstest.authorize.net/v1/Accept', - acceptjs: 'https://js.authorize.net/v1/Accept' - } -}; diff --git a/app/code/Magento/AuthorizenetAcceptjs/view/base/web/js/view/payment/acceptjs-client.js b/app/code/Magento/AuthorizenetAcceptjs/view/base/web/js/view/payment/acceptjs-client.js deleted file mode 100644 index 935465f5298eb..0000000000000 --- a/app/code/Magento/AuthorizenetAcceptjs/view/base/web/js/view/payment/acceptjs-client.js +++ /dev/null @@ -1,73 +0,0 @@ -/** - * Copyright © Magento, Inc. All rights reserved. - * See COPYING.txt for license details. - */ - -define([ - 'jquery', - 'uiClass', - 'Magento_AuthorizenetAcceptjs/js/view/payment/acceptjs-factory', - 'Magento_AuthorizenetAcceptjs/js/view/payment/validator-handler' -], function ($, Class, acceptjsFactory, validatorHandler) { - 'use strict'; - - return Class.extend({ - defaults: { - environment: 'production' - }, - - /** - * @{inheritdoc} - */ - initialize: function () { - validatorHandler.initialize(); - - this._super(); - - return this; - }, - - /** - * Creates the token pair with the provided data - * - * @param {Object} data - * @return {jQuery.Deferred} - */ - createTokens: function (data) { - var deferred = $.Deferred(); - - if (this.acceptjsClient) { - this._createTokens(deferred, data); - } else { - acceptjsFactory(this.environment) - .done(function (client) { - this.acceptjsClient = client; - this._createTokens(deferred, data); - }.bind(this)); - } - - return deferred.promise(); - }, - - /** - * Creates a token from the payment information in the form - * - * @param {jQuery.Deferred} deferred - * @param {Object} data - */ - _createTokens: function (deferred, data) { - this.acceptjsClient.dispatchData(data, function (response) { - validatorHandler.validate(response, function (valid, messages) { - if (valid) { - deferred.resolve({ - opaqueDataDescriptor: response.opaqueData.dataDescriptor, - opaqueDataValue: response.opaqueData.dataValue - }); - } else { - deferred.reject(messages); - } - }); - }); - } - }); -}); diff --git a/app/code/Magento/AuthorizenetAcceptjs/view/base/web/js/view/payment/acceptjs-factory.js b/app/code/Magento/AuthorizenetAcceptjs/view/base/web/js/view/payment/acceptjs-factory.js deleted file mode 100644 index e98a204e36cee..0000000000000 --- a/app/code/Magento/AuthorizenetAcceptjs/view/base/web/js/view/payment/acceptjs-factory.js +++ /dev/null @@ -1,38 +0,0 @@ -/** - * Copyright © Magento, Inc. All rights reserved. - * See COPYING.txt for license details. - */ - -define([ - 'jquery' -], function ($) { - 'use strict'; - - return function (environment) { - var deferred = $.Deferred(), - dependency = 'acceptjs'; - - if (environment === 'sandbox') { - dependency = 'acceptjssandbox'; - } - - require([dependency], function (accept) { - var $body = $('body'); - - /* - * Acceptjs doesn't safely load dependent files which leads to a race condition when trying to use - * the sdk right away. - * @see https://community.developer.authorize.net/t5/Integration-and-Testing/ - * Dynamically-loading-Accept-js-E-WC-03-Accept-js-is-not-loaded/td-p/63283 - */ - $body.on('handshake.acceptjs', function () { - deferred.resolve(accept); - $body.off('handshake.acceptjs'); - }); - }, - deferred.reject - ); - - return deferred.promise(); - }; -}); diff --git a/app/code/Magento/AuthorizenetAcceptjs/view/base/web/js/view/payment/response-validator.js b/app/code/Magento/AuthorizenetAcceptjs/view/base/web/js/view/payment/response-validator.js deleted file mode 100644 index 3c44ca2f9e490..0000000000000 --- a/app/code/Magento/AuthorizenetAcceptjs/view/base/web/js/view/payment/response-validator.js +++ /dev/null @@ -1,38 +0,0 @@ -/** - * Copyright © Magento, Inc. All rights reserved. - * See COPYING.txt for license details. - */ - -define([ - 'jquery', - 'mage/translate' -], function ($, $t) { - 'use strict'; - - return { - /** - * Validate Authorizenet-Acceptjs response - * - * @param {Object} context - * @returns {jQuery.Deferred} - */ - validate: function (context) { - var state = $.Deferred(), - messages = []; - - if (context.messages.resultCode === 'Ok') { - state.resolve(); - } else { - if (context.messages.message.length > 0) { - $.each(context.messages.message, function (index, element) { - messages.push($t(element.text)); - }); - } - state.reject(messages); - } - - return state.promise(); - } - }; -}); - diff --git a/app/code/Magento/AuthorizenetAcceptjs/view/base/web/js/view/payment/validator-handler.js b/app/code/Magento/AuthorizenetAcceptjs/view/base/web/js/view/payment/validator-handler.js deleted file mode 100644 index 109f159c9a77c..0000000000000 --- a/app/code/Magento/AuthorizenetAcceptjs/view/base/web/js/view/payment/validator-handler.js +++ /dev/null @@ -1,59 +0,0 @@ -/** - * Copyright © Magento, Inc. All rights reserved. - * See COPYING.txt for license details. - */ - -define([ - 'jquery', - 'Magento_AuthorizenetAcceptjs/js/view/payment/response-validator' -], function ($, responseValidator) { - 'use strict'; - - return { - validators: [], - - /** - * Init list of validators - */ - initialize: function () { - this.add(responseValidator); - }, - - /** - * Add new validator - * @param {Object} validator - */ - add: function (validator) { - this.validators.push(validator); - }, - - /** - * Run pull of validators - * @param {Object} context - * @param {Function} callback - */ - validate: function (context, callback) { - var self = this, - deferred; - - // no available validators - if (!self.validators.length) { - callback(true); - - return; - } - - // get list of deferred validators - deferred = $.map(self.validators, function (current) { - return current.validate(context); - }); - - $.when.apply($, deferred) - .done(function () { - callback(true); - }).fail(function (error) { - callback(false, error); - }); - } - }; -}); diff --git a/app/code/Magento/AuthorizenetAcceptjs/view/frontend/layout/checkout_index_index.xml b/app/code/Magento/AuthorizenetAcceptjs/view/frontend/layout/checkout_index_index.xml deleted file mode 100644 index f31b06c9be9b9..0000000000000 --- a/app/code/Magento/AuthorizenetAcceptjs/view/frontend/layout/checkout_index_index.xml +++ /dev/null @@ -1,49 +0,0 @@ - - - - - - - - - - - - - - uiComponent - - - - - - - - Magento_AuthorizenetAcceptjs/js/view/payment/authorizenet - - - true - - - - - - - - - - - - - - - - - - - \ No newline at end of file diff --git a/app/code/Magento/AuthorizenetAcceptjs/view/frontend/web/js/view/payment/authorizenet.js b/app/code/Magento/AuthorizenetAcceptjs/view/frontend/web/js/view/payment/authorizenet.js deleted file mode 100644 index a05fe739a444a..0000000000000 --- a/app/code/Magento/AuthorizenetAcceptjs/view/frontend/web/js/view/payment/authorizenet.js +++ /dev/null @@ -1,20 +0,0 @@ -/** - * Copyright © Magento, Inc. All rights reserved. - * See COPYING.txt for license details. - */ - -define([ - 'uiComponent', - 'Magento_Checkout/js/model/payment/renderer-list' -], -function (Component, rendererList) { - 'use strict'; - - rendererList.push({ - type: 'authorizenet_acceptjs', - component: 'Magento_AuthorizenetAcceptjs/js/view/payment/method-renderer/authorizenet-accept' - }); - - /** Add view logic here if needed */ - return Component.extend({}); -}); diff --git a/app/code/Magento/AuthorizenetAcceptjs/view/frontend/web/js/view/payment/method-renderer/authorizenet-accept.js b/app/code/Magento/AuthorizenetAcceptjs/view/frontend/web/js/view/payment/method-renderer/authorizenet-accept.js deleted file mode 100644 index bba1290a9eedd..0000000000000 --- a/app/code/Magento/AuthorizenetAcceptjs/view/frontend/web/js/view/payment/method-renderer/authorizenet-accept.js +++ /dev/null @@ -1,148 +0,0 @@ -/** - * Copyright © Magento, Inc. All rights reserved. - * See COPYING.txt for license details. - */ - -define([ - 'jquery', - 'Magento_Payment/js/view/payment/cc-form', - 'Magento_AuthorizenetAcceptjs/js/view/payment/acceptjs-client', - 'Magento_Checkout/js/model/full-screen-loader', - 'Magento_Ui/js/model/messageList', - 'Magento_Payment/js/model/credit-card-validation/validator' -], function ($, Component, AcceptjsClient, fullScreenLoader, globalMessageList) { - 'use strict'; - - return Component.extend({ - defaults: { - active: false, - template: 'Magento_AuthorizenetAcceptjs/payment/authorizenet-acceptjs', - tokens: null, - ccForm: 'Magento_Payment/payment/cc-form', - acceptjsClient: null - }, - - /** - * Set list of observable attributes - * - * @returns {exports.initObservable} - */ - initObservable: function () { - this._super() - .observe(['active']); - - return this; - }, - - /** - * @returns {String} - */ - getCode: function () { - return 'authorizenet_acceptjs'; - }, - - /** - * Initialize form elements for validation - */ - initFormElement: function (element) { - this.formElement = element; - this.acceptjsClient = AcceptjsClient({ - environment: window.checkoutConfig.payment[this.getCode()].environment - }); - $(this.formElement).validation(); - }, - - /** - * @returns {Object} - */ - getData: function () { - return { - method: this.getCode(), - 'additional_data': { - opaqueDataDescriptor: this.tokens ? this.tokens.opaqueDataDescriptor : null, - opaqueDataValue: this.tokens ? this.tokens.opaqueDataValue : null, - ccLast4: this.creditCardNumber().substr(-4) - } - }; - }, - - /** - * Check if payment is active - * - * @returns {Boolean} - */ - isActive: function () { - var active = this.getCode() === this.isChecked(); - - this.active(active); - - return active; - }, - - /** - * Prepare data to place order - */ - beforePlaceOrder: function () { - var authData = {}, - cardData = {}, - secureData = {}; - - if (!$(this.formElement).valid()) { - return; - } - - authData.clientKey = window.checkoutConfig.payment[this.getCode()].clientKey !== null ? - window.checkoutConfig.payment[this.getCode()].clientKey : ''; - authData.apiLoginID = window.checkoutConfig.payment[this.getCode()].apiLoginID !== null ? - window.checkoutConfig.payment[this.getCode()].apiLoginID : ''; - - cardData.cardNumber = this.creditCardNumber(); - cardData.month = this.creditCardExpMonth(); - cardData.year = this.creditCardExpYear(); - - if (this.hasVerification()) { - cardData.cardCode = this.creditCardVerificationNumber(); - } - - secureData.authData = authData; - secureData.cardData = cardData; - - fullScreenLoader.startLoader(); - - this.acceptjsClient.createTokens(secureData) - .always(function () { - fullScreenLoader.stopLoader(); - }) - .done(function (tokens) { - this.tokens = tokens; - this.placeOrder(); - }.bind(this)) - .fail(function (messages) { - this.tokens = null; - this._showErrors(messages); - }.bind(this)); - }, - - /** - * Should the cvv field be used - * - * @return {Boolean} - */ - hasVerification: function () { - return window.checkoutConfig.payment[this.getCode()].useCvv; - }, - - /** - * Show error messages - * - * @param {String[]} errorMessages - */ - _showErrors: function (errorMessages) { - $.each(errorMessages, function (index, message) { - globalMessageList.addErrorMessage({ - message: message - }); - }); - } - }); -}); diff --git a/app/code/Magento/AuthorizenetAcceptjs/view/frontend/web/template/payment/authorizenet-acceptjs.html b/app/code/Magento/AuthorizenetAcceptjs/view/frontend/web/template/payment/authorizenet-acceptjs.html deleted file mode 100644 index 1e41c2b49adba..0000000000000 --- a/app/code/Magento/AuthorizenetAcceptjs/view/frontend/web/template/payment/authorizenet-acceptjs.html +++ /dev/null @@ -1,46 +0,0 @@ - -
-
- - -
-
- -
- -
-
- - -
- -
-
-
- -
-
-
-
diff --git a/app/code/Magento/AuthorizenetCardinal/Gateway/Request/Authorize3DSecureBuilder.php b/app/code/Magento/AuthorizenetCardinal/Gateway/Request/Authorize3DSecureBuilder.php deleted file mode 100644 index bf8e1661a3f61..0000000000000 --- a/app/code/Magento/AuthorizenetCardinal/Gateway/Request/Authorize3DSecureBuilder.php +++ /dev/null @@ -1,85 +0,0 @@ -subjectReader = $subjectReader; - $this->config = $config; - $this->jwtParser = $jwtParser; - } - - /** - * @inheritdoc - */ - public function build(array $buildSubject): array - { - if ($this->config->isActive() === false) { - return []; - } - - $paymentDO = $this->subjectReader->readPayment($buildSubject); - $payment = $paymentDO->getPayment(); - $data = []; - - if ($payment instanceof Payment) { - $cardinalJwt = (string)$payment->getAdditionalInformation('cardinalJWT'); - $jwtPayload = $this->jwtParser->execute($cardinalJwt); - $eciFlag = $jwtPayload['Payload']['Payment']['ExtendedData']['ECIFlag'] ?? ''; - $cavv = $jwtPayload['Payload']['Payment']['ExtendedData']['CAVV'] ?? ''; - $data = [ - 'transactionRequest' => [ - 'cardholderAuthentication' => [ - 'authenticationIndicator' => $eciFlag, - 'cardholderAuthenticationValue' => $cavv - ], - ] - ]; - } - - return $data; - } -} diff --git a/app/code/Magento/AuthorizenetCardinal/Gateway/Validator/CavvResponseValidator.php b/app/code/Magento/AuthorizenetCardinal/Gateway/Validator/CavvResponseValidator.php deleted file mode 100644 index 35287406a12d5..0000000000000 --- a/app/code/Magento/AuthorizenetCardinal/Gateway/Validator/CavvResponseValidator.php +++ /dev/null @@ -1,86 +0,0 @@ -resultFactory = $resultFactory; - $this->subjectReader = $subjectReader; - $this->config = $config; - } - - /** - * @inheritdoc - */ - public function validate(array $validationSubject): ResultInterface - { - if ($this->config->isActive() === false) { - return $this->createResult(true); - } - - $response = $this->subjectReader->readResponse($validationSubject); - $transactionResponse = $response['transactionResponse']; - - $cavvResultCode = $transactionResponse['cavvResultCode'] ?? ''; - $isValid = $cavvResultCode === self::RESULT_CODE_SUCCESS; - $errorCodes = []; - $errorMessages = []; - - if (!$isValid) { - $errorCodes[] = $transactionResponse['cavvResultCode']; - $errorMessages[] = 'CAVV failed validation'; - } - - return $this->createResult($isValid, $errorMessages, $errorCodes); - } -} diff --git a/app/code/Magento/AuthorizenetCardinal/LICENSE.txt b/app/code/Magento/AuthorizenetCardinal/LICENSE.txt deleted file mode 100644 index 49525fd99da9c..0000000000000 --- a/app/code/Magento/AuthorizenetCardinal/LICENSE.txt +++ /dev/null @@ -1,48 +0,0 @@ - -Open Software License ("OSL") v. 3.0 - -This Open Software License (the "License") applies to any original work of authorship (the "Original Work") whose owner (the "Licensor") has placed the following licensing notice adjacent to the copyright notice for the Original Work: - -Licensed under the Open Software License version 3.0 - - 1. Grant of Copyright License. Licensor grants You a worldwide, royalty-free, non-exclusive, sublicensable license, for the duration of the copyright, to do the following: - - 1. to reproduce the Original Work in copies, either alone or as part of a collective work; - - 2. to translate, adapt, alter, transform, modify, or arrange the Original Work, thereby creating derivative works ("Derivative Works") based upon the Original Work; - - 3. to distribute or communicate copies of the Original Work and Derivative Works to the public, with the proviso that copies of Original Work or Derivative Works that You distribute or communicate shall be licensed under this Open Software License; - - 4. to perform the Original Work publicly; and - - 5. to display the Original Work publicly. - - 2. Grant of Patent License. Licensor grants You a worldwide, royalty-free, non-exclusive, sublicensable license, under patent claims owned or controlled by the Licensor that are embodied in the Original Work as furnished by the Licensor, for the duration of the patents, to make, use, sell, offer for sale, have made, and import the Original Work and Derivative Works. - - 3. Grant of Source Code License. The term "Source Code" means the preferred form of the Original Work for making modifications to it and all available documentation describing how to modify the Original Work. Licensor agrees to provide a machine-readable copy of the Source Code of the Original Work along with each copy of the Original Work that Licensor distributes. Licensor reserves the right to satisfy this obligation by placing a machine-readable copy of the Source Code in an information repository reasonably calculated to permit inexpensive and convenient access by You for as long as Licensor continues to distribute the Original Work. - - 4. Exclusions From License Grant. Neither the names of Licensor, nor the names of any contributors to the Original Work, nor any of their trademarks or service marks, may be used to endorse or promote products derived from this Original Work without express prior permission of the Licensor. Except as expressly stated herein, nothing in this License grants any license to Licensor's trademarks, copyrights, patents, trade secrets or any other intellectual property. No patent license is granted to make, use, sell, offer for sale, have made, or import embodiments of any patent claims other than the licensed claims defined in Section 2. No license is granted to the trademarks of Licensor even if such marks are included in the Original Work. Nothing in this License shall be interpreted to prohibit Licensor from licensing under terms different from this License any Original Work that Licensor otherwise would have a right to license. - - 5. External Deployment. The term "External Deployment" means the use, distribution, or communication of the Original Work or Derivative Works in any way such that the Original Work or Derivative Works may be used by anyone other than You, whether those works are distributed or communicated to those persons or made available as an application intended for use over a network. As an express condition for the grants of license hereunder, You must treat any External Deployment by You of the Original Work or a Derivative Work as a distribution under section 1(c). - - 6. Attribution Rights. You must retain, in the Source Code of any Derivative Works that You create, all copyright, patent, or trademark notices from the Source Code of the Original Work, as well as any notices of licensing and any descriptive text identified therein as an "Attribution Notice." You must cause the Source Code for any Derivative Works that You create to carry a prominent Attribution Notice reasonably calculated to inform recipients that You have modified the Original Work. - - 7. Warranty of Provenance and Disclaimer of Warranty. Licensor warrants that the copyright in and to the Original Work and the patent rights granted herein by Licensor are owned by the Licensor or are sublicensed to You under the terms of this License with the permission of the contributor(s) of those copyrights and patent rights. Except as expressly stated in the immediately preceding sentence, the Original Work is provided under this License on an "AS IS" BASIS and WITHOUT WARRANTY, either express or implied, including, without limitation, the warranties of non-infringement, merchantability or fitness for a particular purpose. THE ENTIRE RISK AS TO THE QUALITY OF THE ORIGINAL WORK IS WITH YOU. This DISCLAIMER OF WARRANTY constitutes an essential part of this License. No license to the Original Work is granted by this License except under this disclaimer. - - 8. Limitation of Liability. Under no circumstances and under no legal theory, whether in tort (including negligence), contract, or otherwise, shall the Licensor be liable to anyone for any indirect, special, incidental, or consequential damages of any character arising as a result of this License or the use of the Original Work including, without limitation, damages for loss of goodwill, work stoppage, computer failure or malfunction, or any and all other commercial damages or losses. This limitation of liability shall not apply to the extent applicable law prohibits such limitation. - - 9. Acceptance and Termination. If, at any time, You expressly assented to this License, that assent indicates your clear and irrevocable acceptance of this License and all of its terms and conditions. If You distribute or communicate copies of the Original Work or a Derivative Work, You must make a reasonable effort under the circumstances to obtain the express assent of recipients to the terms of this License. This License conditions your rights to undertake the activities listed in Section 1, including your right to create Derivative Works based upon the Original Work, and doing so without honoring these terms and conditions is prohibited by copyright law and international treaty. Nothing in this License is intended to affect copyright exceptions and limitations (including 'fair use' or 'fair dealing'). This License shall terminate immediately and You may no longer exercise any of the rights granted to You by this License upon your failure to honor the conditions in Section 1(c). - - 10. Termination for Patent Action. This License shall terminate automatically and You may no longer exercise any of the rights granted to You by this License as of the date You commence an action, including a cross-claim or counterclaim, against Licensor or any licensee alleging that the Original Work infringes a patent. This termination provision shall not apply for an action alleging patent infringement by combinations of the Original Work with other software or hardware. - - 11. Jurisdiction, Venue and Governing Law. Any action or suit relating to this License may be brought only in the courts of a jurisdiction wherein the Licensor resides or in which Licensor conducts its primary business, and under the laws of that jurisdiction excluding its conflict-of-law provisions. The application of the United Nations Convention on Contracts for the International Sale of Goods is expressly excluded. Any use of the Original Work outside the scope of this License or after its termination shall be subject to the requirements and penalties of copyright or patent law in the appropriate jurisdiction. This section shall survive the termination of this License. - - 12. Attorneys' Fees. In any action to enforce the terms of this License or seeking damages relating thereto, the prevailing party shall be entitled to recover its costs and expenses, including, without limitation, reasonable attorneys' fees and costs incurred in connection with such action, including any appeal of such action. This section shall survive the termination of this License. - - 13. Miscellaneous. If any provision of this License is held to be unenforceable, such provision shall be reformed only to the extent necessary to make it enforceable. - - 14. Definition of "You" in This License. "You" throughout this License, whether in upper or lower case, means an individual or a legal entity exercising rights under, and complying with all of the terms of, this License. For legal entities, "You" includes any entity that controls, is controlled by, or is under common control with you. For purposes of this definition, "control" means (i) the power, direct or indirect, to cause the direction or management of such entity, whether by contract or otherwise, or (ii) ownership of fifty percent (50%) or more of the outstanding shares, or (iii) beneficial ownership of such entity. - - 15. Right to Use. You may use the Original Work in all ways not otherwise restricted or conditioned by this License or by law, and Licensor promises not to interfere with or be responsible for such uses by You. - - 16. Modification of This License. This License is Copyright (C) 2005 Lawrence Rosen. Permission is granted to copy, distribute, or communicate this License without modification. Nothing in this License permits You to modify this License as applied to the Original Work or to Derivative Works. However, You may modify the text of this License and copy, distribute or communicate your modified version (the "Modified License") and apply it to other original works of authorship subject to the following conditions: (i) You may not indicate in any way that your Modified License is the "Open Software License" or "OSL" and you may not use those names in the name of your Modified License; (ii) You must replace the notice specified in the first paragraph above with the notice "Licensed under " or with a notice of your own that is not confusingly similar to the notice in this License; and (iii) You may not claim that your original works are open source software unless your Modified License has been approved by Open Source Initiative (OSI) and You comply with its license review and certification process. \ No newline at end of file diff --git a/app/code/Magento/AuthorizenetCardinal/LICENSE_AFL.txt b/app/code/Magento/AuthorizenetCardinal/LICENSE_AFL.txt deleted file mode 100644 index f39d641b18a19..0000000000000 --- a/app/code/Magento/AuthorizenetCardinal/LICENSE_AFL.txt +++ /dev/null @@ -1,48 +0,0 @@ - -Academic Free License ("AFL") v. 3.0 - -This Academic Free License (the "License") applies to any original work of authorship (the "Original Work") whose owner (the "Licensor") has placed the following licensing notice adjacent to the copyright notice for the Original Work: - -Licensed under the Academic Free License version 3.0 - - 1. Grant of Copyright License. Licensor grants You a worldwide, royalty-free, non-exclusive, sublicensable license, for the duration of the copyright, to do the following: - - 1. to reproduce the Original Work in copies, either alone or as part of a collective work; - - 2. to translate, adapt, alter, transform, modify, or arrange the Original Work, thereby creating derivative works ("Derivative Works") based upon the Original Work; - - 3. to distribute or communicate copies of the Original Work and Derivative Works to the public, under any license of your choice that does not contradict the terms and conditions, including Licensor's reserved rights and remedies, in this Academic Free License; - - 4. to perform the Original Work publicly; and - - 5. to display the Original Work publicly. - - 2. Grant of Patent License. Licensor grants You a worldwide, royalty-free, non-exclusive, sublicensable license, under patent claims owned or controlled by the Licensor that are embodied in the Original Work as furnished by the Licensor, for the duration of the patents, to make, use, sell, offer for sale, have made, and import the Original Work and Derivative Works. - - 3. Grant of Source Code License. The term "Source Code" means the preferred form of the Original Work for making modifications to it and all available documentation describing how to modify the Original Work. Licensor agrees to provide a machine-readable copy of the Source Code of the Original Work along with each copy of the Original Work that Licensor distributes. Licensor reserves the right to satisfy this obligation by placing a machine-readable copy of the Source Code in an information repository reasonably calculated to permit inexpensive and convenient access by You for as long as Licensor continues to distribute the Original Work. - - 4. Exclusions From License Grant. Neither the names of Licensor, nor the names of any contributors to the Original Work, nor any of their trademarks or service marks, may be used to endorse or promote products derived from this Original Work without express prior permission of the Licensor. Except as expressly stated herein, nothing in this License grants any license to Licensor's trademarks, copyrights, patents, trade secrets or any other intellectual property. No patent license is granted to make, use, sell, offer for sale, have made, or import embodiments of any patent claims other than the licensed claims defined in Section 2. No license is granted to the trademarks of Licensor even if such marks are included in the Original Work. Nothing in this License shall be interpreted to prohibit Licensor from licensing under terms different from this License any Original Work that Licensor otherwise would have a right to license. - - 5. External Deployment. The term "External Deployment" means the use, distribution, or communication of the Original Work or Derivative Works in any way such that the Original Work or Derivative Works may be used by anyone other than You, whether those works are distributed or communicated to those persons or made available as an application intended for use over a network. As an express condition for the grants of license hereunder, You must treat any External Deployment by You of the Original Work or a Derivative Work as a distribution under section 1(c). - - 6. Attribution Rights. You must retain, in the Source Code of any Derivative Works that You create, all copyright, patent, or trademark notices from the Source Code of the Original Work, as well as any notices of licensing and any descriptive text identified therein as an "Attribution Notice." You must cause the Source Code for any Derivative Works that You create to carry a prominent Attribution Notice reasonably calculated to inform recipients that You have modified the Original Work. - - 7. Warranty of Provenance and Disclaimer of Warranty. Licensor warrants that the copyright in and to the Original Work and the patent rights granted herein by Licensor are owned by the Licensor or are sublicensed to You under the terms of this License with the permission of the contributor(s) of those copyrights and patent rights. Except as expressly stated in the immediately preceding sentence, the Original Work is provided under this License on an "AS IS" BASIS and WITHOUT WARRANTY, either express or implied, including, without limitation, the warranties of non-infringement, merchantability or fitness for a particular purpose. THE ENTIRE RISK AS TO THE QUALITY OF THE ORIGINAL WORK IS WITH YOU. This DISCLAIMER OF WARRANTY constitutes an essential part of this License. No license to the Original Work is granted by this License except under this disclaimer. - - 8. Limitation of Liability. Under no circumstances and under no legal theory, whether in tort (including negligence), contract, or otherwise, shall the Licensor be liable to anyone for any indirect, special, incidental, or consequential damages of any character arising as a result of this License or the use of the Original Work including, without limitation, damages for loss of goodwill, work stoppage, computer failure or malfunction, or any and all other commercial damages or losses. This limitation of liability shall not apply to the extent applicable law prohibits such limitation. - - 9. Acceptance and Termination. If, at any time, You expressly assented to this License, that assent indicates your clear and irrevocable acceptance of this License and all of its terms and conditions. If You distribute or communicate copies of the Original Work or a Derivative Work, You must make a reasonable effort under the circumstances to obtain the express assent of recipients to the terms of this License. This License conditions your rights to undertake the activities listed in Section 1, including your right to create Derivative Works based upon the Original Work, and doing so without honoring these terms and conditions is prohibited by copyright law and international treaty. Nothing in this License is intended to affect copyright exceptions and limitations (including "fair use" or "fair dealing"). This License shall terminate immediately and You may no longer exercise any of the rights granted to You by this License upon your failure to honor the conditions in Section 1(c). - - 10. Termination for Patent Action. This License shall terminate automatically and You may no longer exercise any of the rights granted to You by this License as of the date You commence an action, including a cross-claim or counterclaim, against Licensor or any licensee alleging that the Original Work infringes a patent. This termination provision shall not apply for an action alleging patent infringement by combinations of the Original Work with other software or hardware. - - 11. Jurisdiction, Venue and Governing Law. Any action or suit relating to this License may be brought only in the courts of a jurisdiction wherein the Licensor resides or in which Licensor conducts its primary business, and under the laws of that jurisdiction excluding its conflict-of-law provisions. The application of the United Nations Convention on Contracts for the International Sale of Goods is expressly excluded. Any use of the Original Work outside the scope of this License or after its termination shall be subject to the requirements and penalties of copyright or patent law in the appropriate jurisdiction. This section shall survive the termination of this License. - - 12. Attorneys' Fees. In any action to enforce the terms of this License or seeking damages relating thereto, the prevailing party shall be entitled to recover its costs and expenses, including, without limitation, reasonable attorneys' fees and costs incurred in connection with such action, including any appeal of such action. This section shall survive the termination of this License. - - 13. Miscellaneous. If any provision of this License is held to be unenforceable, such provision shall be reformed only to the extent necessary to make it enforceable. - - 14. Definition of "You" in This License. "You" throughout this License, whether in upper or lower case, means an individual or a legal entity exercising rights under, and complying with all of the terms of, this License. For legal entities, "You" includes any entity that controls, is controlled by, or is under common control with you. For purposes of this definition, "control" means (i) the power, direct or indirect, to cause the direction or management of such entity, whether by contract or otherwise, or (ii) ownership of fifty percent (50%) or more of the outstanding shares, or (iii) beneficial ownership of such entity. - - 15. Right to Use. You may use the Original Work in all ways not otherwise restricted or conditioned by this License or by law, and Licensor promises not to interfere with or be responsible for such uses by You. - - 16. Modification of This License. This License is Copyright © 2005 Lawrence Rosen. Permission is granted to copy, distribute, or communicate this License without modification. Nothing in this License permits You to modify this License as applied to the Original Work or to Derivative Works. However, You may modify the text of this License and copy, distribute or communicate your modified version (the "Modified License") and apply it to other original works of authorship subject to the following conditions: (i) You may not indicate in any way that your Modified License is the "Academic Free License" or "AFL" and you may not use those names in the name of your Modified License; (ii) You must replace the notice specified in the first paragraph above with the notice "Licensed under " or with a notice of your own that is not confusingly similar to the notice in this License; and (iii) You may not claim that your original works are open source software unless your Modified License has been approved by Open Source Initiative (OSI) and You comply with its license review and certification process. diff --git a/app/code/Magento/AuthorizenetCardinal/Model/Checkout/ConfigProvider.php b/app/code/Magento/AuthorizenetCardinal/Model/Checkout/ConfigProvider.php deleted file mode 100644 index 8f09395874dce..0000000000000 --- a/app/code/Magento/AuthorizenetCardinal/Model/Checkout/ConfigProvider.php +++ /dev/null @@ -1,48 +0,0 @@ -config = $config; - } - - /** - * @inheritdoc - */ - public function getConfig(): array - { - $config['cardinal'] = [ - 'isActiveFor' => [ - 'authorizenet' => $this->config->isActive() - ] - ]; - - return $config; - } -} diff --git a/app/code/Magento/AuthorizenetCardinal/Model/Config.php b/app/code/Magento/AuthorizenetCardinal/Model/Config.php deleted file mode 100644 index 798fb846c160e..0000000000000 --- a/app/code/Magento/AuthorizenetCardinal/Model/Config.php +++ /dev/null @@ -1,52 +0,0 @@ -scopeConfig = $scopeConfig; - } - - /** - * If this config option set to false no AuthorizenetCardinal integration should be available - * - * @param int|null $storeId - * @return bool - */ - public function isActive(?int $storeId = null): bool - { - $enabled = $this->scopeConfig->isSetFlag( - 'three_d_secure/cardinal/enabled_authorizenet', - ScopeInterface::SCOPE_STORE, - $storeId - ); - - return $enabled; - } -} diff --git a/app/code/Magento/AuthorizenetCardinal/Observer/DataAssignObserver.php b/app/code/Magento/AuthorizenetCardinal/Observer/DataAssignObserver.php deleted file mode 100644 index aa5fbee327fe5..0000000000000 --- a/app/code/Magento/AuthorizenetCardinal/Observer/DataAssignObserver.php +++ /dev/null @@ -1,66 +0,0 @@ -config = $config; - } - - /** - * @inheritdoc - */ - public function execute(Observer $observer) - { - if ($this->config->isActive() === false) { - return; - } - - $data = $this->readDataArgument($observer); - $additionalData = $data->getData(PaymentInterface::KEY_ADDITIONAL_DATA); - if (!is_array($additionalData)) { - return; - } - - $paymentInfo = $this->readPaymentModelArgument($observer); - if (isset($additionalData[self::JWT_KEY])) { - $paymentInfo->setAdditionalInformation( - self::JWT_KEY, - $additionalData[self::JWT_KEY] - ); - } - } -} diff --git a/app/code/Magento/AuthorizenetCardinal/README.md b/app/code/Magento/AuthorizenetCardinal/README.md deleted file mode 100644 index 0bd63130471bd..0000000000000 --- a/app/code/Magento/AuthorizenetCardinal/README.md +++ /dev/null @@ -1,23 +0,0 @@ -# Magento_AuthorizenetCardinal module - -Use the Magento_AuthorizenetCardinal module to enable 3D Secure 2.0 support for AuthorizenetAcceptjs payment integrations. - -## Structure - -`Gateway/` - the directory that contains payment gateway command interfaces and service classes. - -For information about typical file structure of a module in Magento 2, see [Module file structure](https://devdocs.magento.com/guides/v2.3/extension-dev-guide/build/module-file-structure.html#module-file-structure). - -## Extensibility - -Extension developers can interact with the Magento_AuthorizenetCardinal module. For more information about the Magento extension mechanism, see [Magento plug-ins](https://devdocs.magento.com/guides/v2.3/extension-dev-guide/plugins.html). - -[The Magento dependency injection mechanism](https://devdocs.magento.com/guides/v2.3/extension-dev-guide/depend-inj.html) enables you to override the functionality of the Magento_AuthorizenetCardinal module. - -### Events - -This module observes the following events: - -- `payment_method_assign_data_authorizenet_acceptjs` event in the `Magento\AuthorizenetCardinal\Observer\DataAssignObserver` file. - -For information about an event in Magento 2, see [Events and observers](https://devdocs.magento.com/guides/v2.3/extension-dev-guide/events-and-observers.html#events). diff --git a/app/code/Magento/AuthorizenetCardinal/Test/Unit/Observer/DataAssignObserverTest.php b/app/code/Magento/AuthorizenetCardinal/Test/Unit/Observer/DataAssignObserverTest.php deleted file mode 100644 index 9f560507e34db..0000000000000 --- a/app/code/Magento/AuthorizenetCardinal/Test/Unit/Observer/DataAssignObserverTest.php +++ /dev/null @@ -1,100 +0,0 @@ - 'foo' - ]; - - $config = $this->createMock(Config::class); - $config->method('isActive') - ->willReturn(true); - $observerContainer = $this->createMock(Observer::class); - $event = $this->createMock(Event::class); - $paymentInfoModel = $this->createMock(InfoInterface::class); - $dataObject = new DataObject([PaymentInterface::KEY_ADDITIONAL_DATA => $additionalInfo]); - $observerContainer->method('getEvent') - ->willReturn($event); - $event->method('getDataByKey') - ->willReturnMap( - [ - [AbstractDataAssignObserver::MODEL_CODE, $paymentInfoModel], - [AbstractDataAssignObserver::DATA_CODE, $dataObject] - ] - ); - $paymentInfoModel->expects($this->once()) - ->method('setAdditionalInformation') - ->with('cardinalJWT', 'foo'); - - $observer = new DataAssignObserver($config); - $observer->execute($observerContainer); - } - - /** - * Tests case when Cardinal JWT is absent. - */ - public function testDoesntSetDataWhenEmpty() - { - $config = $this->createMock(Config::class); - $config->method('isActive') - ->willReturn(true); - $observerContainer = $this->createMock(Observer::class); - $event = $this->createMock(Event::class); - $paymentInfoModel = $this->createMock(InfoInterface::class); - $observerContainer->method('getEvent') - ->willReturn($event); - $event->method('getDataByKey') - ->willReturnMap( - [ - [AbstractDataAssignObserver::MODEL_CODE, $paymentInfoModel], - [AbstractDataAssignObserver::DATA_CODE, new DataObject()] - ] - ); - $paymentInfoModel->expects($this->never()) - ->method('setAdditionalInformation'); - - $observer = new DataAssignObserver($config); - $observer->execute($observerContainer); - } - - /** - * Tests case when CardinalCommerce is disabled. - */ - public function testDoesntSetDataWhenDisabled() - { - $config = $this->createMock(Config::class); - $config->method('isActive') - ->willReturn(false); - $observerContainer = $this->createMock(Observer::class); - $observerContainer->expects($this->never()) - ->method('getEvent'); - $observer = new DataAssignObserver($config); - $observer->execute($observerContainer); - } -} diff --git a/app/code/Magento/AuthorizenetCardinal/composer.json b/app/code/Magento/AuthorizenetCardinal/composer.json deleted file mode 100644 index 8b913f7056033..0000000000000 --- a/app/code/Magento/AuthorizenetCardinal/composer.json +++ /dev/null @@ -1,31 +0,0 @@ -{ - "name": "magento/module-authorizenet-cardinal", - "description": "Provides a possibility to enable 3-D Secure 2.0 support for Authorize.Net Acceptjs.", - "config": { - "sort-packages": true - }, - "require": { - "php": "~7.1.3||~7.2.0||~7.3.0", - "magento/module-authorizenet-acceptjs": "*", - "magento/framework": "*", - "magento/module-cardinal-commerce": "*", - "magento/module-payment": "*", - "magento/module-sales": "*", - "magento/module-quote": "*", - "magento/module-checkout": "*", - "magento/module-store": "*" - }, - "type": "magento2-module", - "license": [ - "OSL-3.0", - "AFL-3.0" - ], - "autoload": { - "files": [ - "registration.php" - ], - "psr-4": { - "Magento\\AuthorizenetCardinal\\": "" - } - } -} diff --git a/app/code/Magento/AuthorizenetCardinal/etc/adminhtml/system.xml b/app/code/Magento/AuthorizenetCardinal/etc/adminhtml/system.xml deleted file mode 100644 index 2be287a5e8743..0000000000000 --- a/app/code/Magento/AuthorizenetCardinal/etc/adminhtml/system.xml +++ /dev/null @@ -1,22 +0,0 @@ - - - - -
- - - - - Magento\Config\Model\Config\Source\Yesno - three_d_secure/cardinal/enabled_authorizenet - - - -
-
-
diff --git a/app/code/Magento/AuthorizenetCardinal/etc/config.xml b/app/code/Magento/AuthorizenetCardinal/etc/config.xml deleted file mode 100644 index d94bcdc479008..0000000000000 --- a/app/code/Magento/AuthorizenetCardinal/etc/config.xml +++ /dev/null @@ -1,16 +0,0 @@ - - - - - - - 0 - - - - diff --git a/app/code/Magento/AuthorizenetCardinal/etc/di.xml b/app/code/Magento/AuthorizenetCardinal/etc/di.xml deleted file mode 100644 index 568cb6f4cfc4c..0000000000000 --- a/app/code/Magento/AuthorizenetCardinal/etc/di.xml +++ /dev/null @@ -1,32 +0,0 @@ - - - - - - - Magento\AuthorizenetCardinal\Gateway\Request\Authorize3DSecureBuilder - - - - - - - true - - - AuthorizenetAcceptjsTransactionValidator - Magento\AuthorizenetCardinal\Gateway\Validator\CavvResponseValidator - - - - - - Magento\AuthorizenetCardinal\Gateway\Validator\VirtualTransactionValidator - - - diff --git a/app/code/Magento/AuthorizenetCardinal/etc/events.xml b/app/code/Magento/AuthorizenetCardinal/etc/events.xml deleted file mode 100644 index 5b0afbe684699..0000000000000 --- a/app/code/Magento/AuthorizenetCardinal/etc/events.xml +++ /dev/null @@ -1,13 +0,0 @@ - - - - - - - - diff --git a/app/code/Magento/AuthorizenetCardinal/etc/frontend/di.xml b/app/code/Magento/AuthorizenetCardinal/etc/frontend/di.xml deleted file mode 100644 index 13c7a223e82d9..0000000000000 --- a/app/code/Magento/AuthorizenetCardinal/etc/frontend/di.xml +++ /dev/null @@ -1,18 +0,0 @@ - - - - - - - - Magento\AuthorizenetCardinal\Model\Checkout\ConfigProvider - - - - - diff --git a/app/code/Magento/AuthorizenetCardinal/etc/module.xml b/app/code/Magento/AuthorizenetCardinal/etc/module.xml deleted file mode 100644 index fdf8151311f43..0000000000000 --- a/app/code/Magento/AuthorizenetCardinal/etc/module.xml +++ /dev/null @@ -1,15 +0,0 @@ - - - - - - - - - - diff --git a/app/code/Magento/AuthorizenetCardinal/registration.php b/app/code/Magento/AuthorizenetCardinal/registration.php deleted file mode 100644 index 0153e9eaa4d29..0000000000000 --- a/app/code/Magento/AuthorizenetCardinal/registration.php +++ /dev/null @@ -1,9 +0,0 @@ -arrayManager = $arrayManager; - } - - /** - * Return additional data - * - * @param array $data - * @return array - * @throws GraphQlInputException - */ - public function getData(array $data): array - { - if (!isset($data[self::PATH_ADDITIONAL_DATA])) { - throw new GraphQlInputException( - __('Required parameter "authorizenet_acceptjs" for "payment_method" is missing.') - ); - } - if (!isset($data[self::PATH_ADDITIONAL_DATA]['opaque_data_descriptor'])) { - throw new GraphQlInputException( - __('Required parameter "opaque_data_descriptor" for "authorizenet_acceptjs" is missing.') - ); - } - if (!isset($data[self::PATH_ADDITIONAL_DATA]['opaque_data_value'])) { - throw new GraphQlInputException( - __('Required parameter "opaque_data_value" for "authorizenet_acceptjs" is missing.') - ); - } - if (!isset($data[self::PATH_ADDITIONAL_DATA]['cc_last_4'])) { - throw new GraphQlInputException( - __('Required parameter "cc_last_4" for "authorizenet_acceptjs" is missing.') - ); - } - - $additionalData = $this->arrayManager->get(static::PATH_ADDITIONAL_DATA, $data); - foreach ($additionalData as $key => $value) { - $additionalData[$this->convertSnakeCaseToCamelCase($key)] = $value; - unset($additionalData[$key]); - } - return $additionalData; - } - - /** - * Convert an input string from snake_case to camelCase. - * - * @param string $input - * @return string - */ - private function convertSnakeCaseToCamelCase($input): string - { - return lcfirst(str_replace('_', '', ucwords($input, '_'))); - } -} diff --git a/app/code/Magento/AuthorizenetGraphQl/README.md b/app/code/Magento/AuthorizenetGraphQl/README.md deleted file mode 100644 index 2af2b6a1024af..0000000000000 --- a/app/code/Magento/AuthorizenetGraphQl/README.md +++ /dev/null @@ -1,9 +0,0 @@ -# Magento_AuthorizenetGraphQl module - -The Magento_AuthorizenetGraphQl module defines the data types needed to pass payment information data from the client to Magento. - -## Extensibility - -Extension developers can interact with the Magento_AuthorizenetGraphQl module. For more information about the Magento extension mechanism, see [Magento plug-ins](https://devdocs.magento.com/guides/v2.3/extension-dev-guide/plugins.html). - -[The Magento dependency injection mechanism](https://devdocs.magento.com/guides/v2.3/extension-dev-guide/depend-inj.html) enables you to override the functionality of the Magento_AuthorizenetGraphQl module. diff --git a/app/code/Magento/AuthorizenetGraphQl/composer.json b/app/code/Magento/AuthorizenetGraphQl/composer.json deleted file mode 100644 index 2b54049bab434..0000000000000 --- a/app/code/Magento/AuthorizenetGraphQl/composer.json +++ /dev/null @@ -1,25 +0,0 @@ -{ - "name": "magento/module-authorizenet-graph-ql", - "description": "N/A", - "type": "magento2-module", - "require": { - "php": "~7.1.3||~7.2.0||~7.3.0", - "magento/framework": "*", - "magento/module-quote-graph-ql": "*" - }, - "suggest": { - "magento/module-graph-ql": "*" - }, - "license": [ - "OSL-3.0", - "AFL-3.0" - ], - "autoload": { - "files": [ - "registration.php" - ], - "psr-4": { - "Magento\\AuthorizenetGraphQl\\": "" - } - } -} diff --git a/app/code/Magento/AuthorizenetGraphQl/etc/graphql/di.xml b/app/code/Magento/AuthorizenetGraphQl/etc/graphql/di.xml deleted file mode 100644 index e8ea45091c044..0000000000000 --- a/app/code/Magento/AuthorizenetGraphQl/etc/graphql/di.xml +++ /dev/null @@ -1,16 +0,0 @@ - - - - - - - Magento\AuthorizenetGraphQl\Model\AuthorizenetDataProvider - - - - diff --git a/app/code/Magento/AuthorizenetGraphQl/etc/module.xml b/app/code/Magento/AuthorizenetGraphQl/etc/module.xml deleted file mode 100644 index 85a780a881975..0000000000000 --- a/app/code/Magento/AuthorizenetGraphQl/etc/module.xml +++ /dev/null @@ -1,10 +0,0 @@ - - - - - diff --git a/app/code/Magento/AuthorizenetGraphQl/etc/schema.graphqls b/app/code/Magento/AuthorizenetGraphQl/etc/schema.graphqls deleted file mode 100644 index b6e817cc91d61..0000000000000 --- a/app/code/Magento/AuthorizenetGraphQl/etc/schema.graphqls +++ /dev/null @@ -1,12 +0,0 @@ -# Copyright © Magento, Inc. All rights reserved. -# See COPYING.txt for license details. - -input PaymentMethodInput { - authorizenet_acceptjs: AuthorizenetInput @doc(description: "Defines the required attributes for Authorize.Net payments") -} - -input AuthorizenetInput { - opaque_data_descriptor: String! @doc(description: "Authorize.Net's description of the transaction request") - opaque_data_value: String! @doc(description: "The nonce returned by Authorize.Net") - cc_last_4: Int! @doc(description: "The last four digits of the credit or debit card") -} \ No newline at end of file diff --git a/app/code/Magento/AuthorizenetGraphQl/registration.php b/app/code/Magento/AuthorizenetGraphQl/registration.php deleted file mode 100644 index 2e50f9fe92aaa..0000000000000 --- a/app/code/Magento/AuthorizenetGraphQl/registration.php +++ /dev/null @@ -1,10 +0,0 @@ -_authorization = $context->getAuthorization(); @@ -101,6 +116,95 @@ public function __construct(Action\Context $context) } /** + * Dispatches the Action + * + * @param RequestInterface $request + * @return \Magento\Framework\App\ResponseInterface + */ + public function dispatch(RequestInterface $request) + { + if ($request->isDispatched() && $request->getActionName() !== 'denied' && !$this->_isAllowed()) { + $this->_response->setStatusHeader(403, '1.1', 'Forbidden'); + if (!$this->_auth->isLoggedIn()) { + return $this->_redirect('*/auth/login'); + } + + $this->_view->loadLayout(['default', 'adminhtml_denied'], true, true, false); + $this->_view->renderLayout(); + $this->_request->setDispatched(true); + + return $this->_response; + } + + if ($this->_isUrlChecked()) { + $this->_actionFlag->set('', self::FLAG_IS_URLS_CHECKED, true); + } + + $this->_processLocaleSettings(); + + // Need to preload isFirstPageAfterLogin (see https://github.com/magento/magento2/issues/15510) + if ($this->_auth->isLoggedIn()) { + $this->_auth->getAuthStorage()->isFirstPageAfterLogin(); + } + + return parent::dispatch($request); + } + + /** + * Check url keys. If non valid - redirect + * + * @return bool + * + * @see \Magento\Backend\App\Request\BackendValidator for default request validation. + */ + public function _processUrlKeys() + { + $_isValidFormKey = true; + $_isValidSecretKey = true; + $_keyErrorMsg = ''; + if ($this->_auth->isLoggedIn()) { + if ($this->getRequest()->isPost()) { + $_isValidFormKey = $this->_formKeyValidator->validate($this->getRequest()); + $_keyErrorMsg = __('Invalid Form Key. Please refresh the page.'); + } elseif ($this->_backendUrl->useSecretKey()) { + $_isValidSecretKey = $this->_validateSecretKey(); + $_keyErrorMsg = __('You entered an invalid Secret Key. Please refresh the page.'); + } + } + if (!$_isValidFormKey || !$_isValidSecretKey) { + $this->_actionFlag->set('', self::FLAG_NO_DISPATCH, true); + $this->_actionFlag->set('', self::FLAG_NO_POST_DISPATCH, true); + if ($this->getRequest()->getQuery('isAjax', false) || $this->getRequest()->getQuery('ajax', false)) { + $this->getResponse()->representJson( + $this->_objectManager->get( + \Magento\Framework\Json\Helper\Data::class + )->jsonEncode( + ['error' => true, 'message' => $_keyErrorMsg] + ) + ); + } else { + $this->_redirect($this->_backendUrl->getStartupPageUrl()); + } + return false; + } + return true; + } + + /** + * Generate url by route and parameters + * + * @param string $route + * @param array $params + * @return string + */ + public function getUrl($route = '', $params = []) + { + return $this->_helper->getUrl($route, $params); + } + + /** + * Determines whether current user is allowed to access Action + * * @return bool */ protected function _isAllowed() @@ -119,6 +223,8 @@ protected function _getSession() } /** + * Returns instantiated Message\ManagerInterface. + * * @return \Magento\Framework\Message\ManagerInterface */ protected function getMessageManager() @@ -146,6 +252,8 @@ protected function _setActiveMenu($itemId) } /** + * Adds element to Breadcrumbs block + * * @param string $label * @param string $title * @param string|null $link @@ -158,79 +266,51 @@ protected function _addBreadcrumb($label, $title, $link = null) } /** - * @param \Magento\Framework\View\Element\AbstractBlock $block + * Adds block to `content` block + * + * @param AbstractBlock $block * @return $this */ - protected function _addContent(\Magento\Framework\View\Element\AbstractBlock $block) + protected function _addContent(AbstractBlock $block) { return $this->_moveBlockToContainer($block, 'content'); } /** - * @param \Magento\Framework\View\Element\AbstractBlock $block + * Moves Block to `left` container + * + * @param AbstractBlock $block * @return $this */ - protected function _addLeft(\Magento\Framework\View\Element\AbstractBlock $block) + protected function _addLeft(AbstractBlock $block) { return $this->_moveBlockToContainer($block, 'left'); } /** - * @param \Magento\Framework\View\Element\AbstractBlock $block + * Adds Block to `js` container + * + * @param AbstractBlock $block * @return $this */ - protected function _addJs(\Magento\Framework\View\Element\AbstractBlock $block) + protected function _addJs(AbstractBlock $block) { return $this->_moveBlockToContainer($block, 'js'); } /** - * Set specified block as an anonymous child to specified container - * - * The block will be moved to the container from previous parent after all other elements + * Set specified block as an anonymous child to specified container. * - * @param \Magento\Framework\View\Element\AbstractBlock $block + * @param AbstractBlock $block * @param string $containerName * @return $this */ - private function _moveBlockToContainer(\Magento\Framework\View\Element\AbstractBlock $block, $containerName) + private function _moveBlockToContainer(AbstractBlock $block, $containerName) { $this->_view->getLayout()->setChild($containerName, $block->getNameInLayout(), ''); return $this; } - /** - * @param \Magento\Framework\App\RequestInterface $request - * @return \Magento\Framework\App\ResponseInterface - */ - public function dispatch(\Magento\Framework\App\RequestInterface $request) - { - if ($request->isDispatched() && $request->getActionName() !== 'denied' && !$this->_isAllowed()) { - $this->_response->setStatusHeader(403, '1.1', 'Forbidden'); - if (!$this->_auth->isLoggedIn()) { - return $this->_redirect('*/auth/login'); - } - $this->_view->loadLayout(['default', 'adminhtml_denied'], true, true, false); - $this->_view->renderLayout(); - $this->_request->setDispatched(true); - - return $this->_response; - } - - if ($this->_isUrlChecked()) { - $this->_actionFlag->set('', self::FLAG_IS_URLS_CHECKED, true); - } - - $this->_processLocaleSettings(); - - // Need to preload isFirstPageAfterLogin (see https://github.com/magento/magento2/issues/15510) - if ($this->_auth->isLoggedIn()) { - $this->_auth->getAuthStorage()->isFirstPageAfterLogin(); - } - - return parent::dispatch($request); - } - /** * Check whether url is checked * @@ -239,55 +319,13 @@ public function dispatch(\Magento\Framework\App\RequestInterface $request) protected function _isUrlChecked() { return !$this->_actionFlag->get('', self::FLAG_IS_URLS_CHECKED) - && !$this->getRequest()->isForwarded() - && !$this->_getSession()->getIsUrlNotice(true) - && !$this->_canUseBaseUrl; - } - - /** - * Check url keys. If non valid - redirect - * - * @return bool - * - * @see \Magento\Backend\App\Request\BackendValidator for default - * request validation. - */ - public function _processUrlKeys() - { - $_isValidFormKey = true; - $_isValidSecretKey = true; - $_keyErrorMsg = ''; - if ($this->_auth->isLoggedIn()) { - if ($this->getRequest()->isPost()) { - $_isValidFormKey = $this->_formKeyValidator->validate($this->getRequest()); - $_keyErrorMsg = __('Invalid Form Key. Please refresh the page.'); - } elseif ($this->_backendUrl->useSecretKey()) { - $_isValidSecretKey = $this->_validateSecretKey(); - $_keyErrorMsg = __('You entered an invalid Secret Key. Please refresh the page.'); - } - } - if (!$_isValidFormKey || !$_isValidSecretKey) { - $this->_actionFlag->set('', self::FLAG_NO_DISPATCH, true); - $this->_actionFlag->set('', self::FLAG_NO_POST_DISPATCH, true); - if ($this->getRequest()->getQuery('isAjax', false) || $this->getRequest()->getQuery('ajax', false)) { - $this->getResponse()->representJson( - $this->_objectManager->get( - \Magento\Framework\Json\Helper\Data::class - )->jsonEncode( - ['error' => true, 'message' => $_keyErrorMsg] - ) - ); - } else { - $this->_redirect($this->_backendUrl->getStartupPageUrl()); - } - return false; - } - return true; + && !$this->getRequest()->isForwarded() + && !$this->_getSession()->getIsUrlNotice(true) + && !$this->_canUseBaseUrl; } /** - * Set session locale, - * process force locale set through url params + * Set session locale, process force locale set through url params * * @return $this */ @@ -309,8 +347,8 @@ protected function _processLocaleSettings() * Set redirect into response * * @TODO MAGETWO-28356: Refactor controller actions to new ResultInterface - * @param string $path - * @param array $arguments + * @param string $path + * @param array $arguments * @return \Magento\Framework\App\ResponseInterface */ protected function _redirect($path, $arguments = []) @@ -333,19 +371,7 @@ protected function _redirect($path, $arguments = []) protected function _forward($action, $controller = null, $module = null, array $params = null) { $this->_getSession()->setIsUrlNotice($this->_actionFlag->get('', self::FLAG_IS_URLS_CHECKED)); - return parent::_forward($action, $controller, $module, $params); - } - - /** - * Generate url by route and parameters - * - * @param string $route - * @param array $params - * @return string - */ - public function getUrl($route = '', $params = []) - { - return $this->_helper->getUrl($route, $params); + parent::_forward($action, $controller, $module, $params); } /** @@ -359,7 +385,7 @@ protected function _validateSecretKey() return true; } - $secretKey = $this->getRequest()->getParam(\Magento\Backend\Model\UrlInterface::SECRET_KEY_PARAM_NAME, null); + $secretKey = $this->getRequest()->getParam(UrlInterface::SECRET_KEY_PARAM_NAME, null); if (!$secretKey || $secretKey != $this->_backendUrl->getSecretKey()) { return false; } diff --git a/app/code/Magento/Backend/App/Action/Plugin/LoadDesignPlugin.php b/app/code/Magento/Backend/App/Action/Plugin/LoadDesignPlugin.php new file mode 100644 index 0000000000000..7075e1b05e7db --- /dev/null +++ b/app/code/Magento/Backend/App/Action/Plugin/LoadDesignPlugin.php @@ -0,0 +1,46 @@ +designLoader = $designLoader; + } + + /** + * Initiates design before dispatching Backend Actions. + * + * @param AbstractAction $backendAction + * @param RequestInterface $request + * @return void + * @SuppressWarnings(PHPMD.UnusedFormalParameter) + */ + public function beforeDispatch(AbstractAction $backendAction, RequestInterface $request) + { + $this->designLoader->load(); + } +} diff --git a/app/code/Magento/Backend/App/Area/FrontNameResolver.php b/app/code/Magento/Backend/App/Area/FrontNameResolver.php index f03e97e32d2ab..6c586781f2d81 100644 --- a/app/code/Magento/Backend/App/Area/FrontNameResolver.php +++ b/app/code/Magento/Backend/App/Area/FrontNameResolver.php @@ -14,7 +14,7 @@ use Magento\Framework\App\RequestInterface; use Magento\Store\Model\ScopeInterface; use Magento\Store\Model\Store; -use Zend\Uri\Uri; +use Laminas\Uri\Uri; /** * Class to get area front name. diff --git a/app/code/Magento/Backend/Block/Dashboard.php b/app/code/Magento/Backend/Block/Dashboard.php index e1e87d8d4c5a3..28d3eeae9a1c6 100644 --- a/app/code/Magento/Backend/Block/Dashboard.php +++ b/app/code/Magento/Backend/Block/Dashboard.php @@ -3,14 +3,19 @@ * Copyright © Magento, Inc. All rights reserved. * See COPYING.txt for license details. */ +declare(strict_types=1); namespace Magento\Backend\Block; /** + * Class used to initialize layout for MBO Dashboard + * @deprecated dashboard graphs were migrated to dynamic chart.js solution + * @see dashboard in adminhtml_dashboard_index.xml + * * @api * @since 100.0.2 */ -class Dashboard extends \Magento\Backend\Block\Template +class Dashboard extends Template { /** * Location of the "Enable Chart" config param @@ -23,42 +28,8 @@ class Dashboard extends \Magento\Backend\Block\Template protected $_template = 'Magento_Backend::dashboard/index.phtml'; /** - * @return void - */ - protected function _prepareLayout() - { - $this->addChild('lastOrders', \Magento\Backend\Block\Dashboard\Orders\Grid::class); - - $this->addChild('totals', \Magento\Backend\Block\Dashboard\Totals::class); - - $this->addChild('sales', \Magento\Backend\Block\Dashboard\Sales::class); - - $isChartEnabled = $this->_scopeConfig->getValue( - self::XML_PATH_ENABLE_CHARTS, - \Magento\Store\Model\ScopeInterface::SCOPE_STORE - ); - if ($isChartEnabled) { - $block = $this->getLayout()->createBlock(\Magento\Backend\Block\Dashboard\Diagrams::class); - } else { - $block = $this->getLayout()->createBlock( - \Magento\Backend\Block\Template::class - )->setTemplate( - 'dashboard/graph/disabled.phtml' - )->setConfigUrl( - $this->getUrl( - 'adminhtml/system_config/edit', - ['section' => 'admin', '_fragment' => 'admin_dashboard-link'] - ) - ); - } - $this->setChild('diagrams', $block); - - $this->addChild('grids', \Magento\Backend\Block\Dashboard\Grids::class); - - parent::_prepareLayout(); - } - - /** + * Get url for switch action + * * @return string */ public function getSwitchUrl() diff --git a/app/code/Magento/Backend/Block/Dashboard/Diagrams.php b/app/code/Magento/Backend/Block/Dashboard/Diagrams.php index 12770bc12268d..7ebb81e3e2fbf 100644 --- a/app/code/Magento/Backend/Block/Dashboard/Diagrams.php +++ b/app/code/Magento/Backend/Block/Dashboard/Diagrams.php @@ -7,6 +7,8 @@ /** * Adminhtml dashboard diagram tabs + * @deprecated dashboard graphs were migrated to dynamic chart.js solution + * @see dashboard.diagrams in adminhtml_dashboard_index.xml * * @author Magento Core Team */ @@ -18,6 +20,8 @@ class Diagrams extends \Magento\Backend\Block\Widget\Tabs protected $_template = 'Magento_Backend::widget/tabshoriz.phtml'; /** + * Internal constructor, that is called from real constructor + * * @return void */ protected function _construct() @@ -28,6 +32,8 @@ protected function _construct() } /** + * Preparing global layout + * * @return $this */ protected function _prepareLayout() diff --git a/app/code/Magento/Backend/Block/Dashboard/Graph.php b/app/code/Magento/Backend/Block/Dashboard/Graph.php index b76421e4e6f67..db95a64636c3a 100644 --- a/app/code/Magento/Backend/Block/Dashboard/Graph.php +++ b/app/code/Magento/Backend/Block/Dashboard/Graph.php @@ -9,14 +9,13 @@ /** * Adminhtml dashboard google chart block + * @deprecated dashboard graphs were migrated to dynamic chart.js solution + * @see dashboard.chart.amounts and dashboard.chart.orders in adminhtml_dashboard_index.xml * * @author Magento Core Team */ class Graph extends \Magento\Backend\Block\Dashboard\AbstractDashboard { - /** - * Api URL - */ const API_URL = 'https://image-charts.com/chart'; /** @@ -190,8 +189,8 @@ public function getChartUrl($directUrl = true) $params = [ 'cht' => 'lc', 'chls' => '7', - 'chf' => 'bg,s,f4f4f4|c,lg,90,ffffff,0.1,ededed,0', - 'chm' => 'B,f4d4b2,0,0,0', + 'chf' => 'bg,s,f4f4f4|c,lg,90,ffffff,0.1,ededed,0', + 'chm' => 'B,f4d4b2,0,0,0', 'chco' => 'db4814', 'chxs' => '0,0,11|1,0,11', 'chma' => '15,15,15,15' @@ -237,7 +236,7 @@ public function getChartUrl($directUrl = true) case '1y': case '2y': $d = $dateStart->format('Y-m'); - $dateStart->modify('+1 month'); + $dateStart->modify('first day of next month'); break; default: $d = $dateStart->format('Y-m-d H:00'); @@ -300,20 +299,23 @@ public function getChartUrl($directUrl = true) $minvalue = min($localminvalue); // default values - $yLabels = []; $miny = 0; $maxy = 0; $yorigin = 0; + $xAxis = 'x'; + $xAxisIndex = 0; + $yAxisIndex = 1; if ($minvalue >= 0 && $maxvalue >= 0) { if ($maxvalue > 10) { - $p = pow(10, $this->_getPow((int) $maxvalue)); + $p = pow(10, $this->_getPow((int)$maxvalue)); $maxy = ceil($maxvalue / $p) * $p; - $yLabels = range($miny, $maxy, $p); + $yRange = "$yAxisIndex,$miny,$maxy,$p"; } else { $maxy = ceil($maxvalue + 1); - $yLabels = range($miny, $maxy, 1); + $yRange = "$yAxisIndex,$miny,$maxy,1"; } + $params['chxr'] = $yRange; $yorigin = 0; } @@ -341,22 +343,11 @@ public function getChartUrl($directUrl = true) $params['chd'] .= $buffer; - $valueBuffer = []; - if (count($this->_axisLabels) > 0) { $params['chxt'] = implode(',', array_keys($this->_axisLabels)); - $indexid = 0; - foreach ($this->_axisLabels as $idx => $labels) { - if ($idx == 'x') { - $this->formatAxisLabelDate((string) $idx, (string) $timezoneLocal); - $tmpstring = implode('|', $this->_axisLabels[$idx]); - $valueBuffer[] = $indexid . ":|" . $tmpstring; - } elseif ($idx == 'y') { - $valueBuffer[] = $indexid . ":|" . implode('|', $yLabels); - } - $indexid++; - } - $params['chxl'] = implode('|', $valueBuffer); + $this->formatAxisLabelDate($xAxis, (string)$timezoneLocal); + $customAxisLabels = $xAxisIndex . ":|" . implode('|', $this->_axisLabels[$xAxis]); + $params['chxl'] = $customAxisLabels . $dataSetdelimiter; } // chart size @@ -368,7 +359,7 @@ public function getChartUrl($directUrl = true) foreach ($params as $name => $value) { $p[] = $name . '=' . urlencode($value); } - return (string) self::API_URL . '?' . implode('&', $p); + return (string)self::API_URL . '?' . implode('&', $p); } $gaData = urlencode(base64_encode(json_encode($params))); $gaHash = $this->_dashboardData->getChartDataHash($gaData); @@ -392,7 +383,7 @@ private function formatAxisLabelDate($idx, $timezoneLocal) switch ($this->getDataHelper()->getParam('period')) { case '24h': $this->_axisLabels[$idx][$_index] = $this->_localeDate->formatDateTime( - $period->setTime((int) $period->format('H'), 0, 0), + $period->setTime((int)$period->format('H'), 0, 0), \IntlDateFormatter::NONE, \IntlDateFormatter::SHORT ); diff --git a/app/code/Magento/Backend/Block/Dashboard/Grids.php b/app/code/Magento/Backend/Block/Dashboard/Grids.php index 986854da93cf8..f40aaaf33fed7 100644 --- a/app/code/Magento/Backend/Block/Dashboard/Grids.php +++ b/app/code/Magento/Backend/Block/Dashboard/Grids.php @@ -3,14 +3,20 @@ * Copyright © Magento, Inc. All rights reserved. * See COPYING.txt for license details. */ +declare(strict_types=1); + namespace Magento\Backend\Block\Dashboard; +use Magento\Backend\Block\Dashboard\Tab\Products\Ordered; +use Magento\Backend\Block\Widget\Tabs; + /** * Adminhtml dashboard bottom tabs * + * @api * @author Magento Core Team */ -class Grids extends \Magento\Backend\Block\Widget\Tabs +class Grids extends Tabs { /** * @var string @@ -18,6 +24,8 @@ class Grids extends \Magento\Backend\Block\Widget\Tabs protected $_template = 'Magento_Backend::widget/tabshoriz.phtml'; /** + * Internal constructor, that is called from real constructor + * * @return void */ protected function _construct() @@ -49,7 +57,7 @@ protected function _prepareLayout() [ 'label' => __('Bestsellers'), 'content' => $this->getLayout()->createBlock( - \Magento\Backend\Block\Dashboard\Tab\Products\Ordered::class + Ordered::class )->toHtml(), 'active' => true ] diff --git a/app/code/Magento/Backend/Block/Dashboard/Orders/Grid.php b/app/code/Magento/Backend/Block/Dashboard/Orders/Grid.php index 0a73430aad0f3..8b3574e223236 100644 --- a/app/code/Magento/Backend/Block/Dashboard/Orders/Grid.php +++ b/app/code/Magento/Backend/Block/Dashboard/Orders/Grid.php @@ -5,36 +5,42 @@ */ namespace Magento\Backend\Block\Dashboard\Orders; +use Magento\Backend\Block\Template\Context; +use Magento\Backend\Helper\Data; +use Magento\Framework\Module\Manager; +use Magento\Reports\Model\ResourceModel\Order\CollectionFactory; + /** * Adminhtml dashboard recent orders grid * + * @api * @author Magento Core Team * @SuppressWarnings(PHPMD.DepthOfInheritance) */ class Grid extends \Magento\Backend\Block\Dashboard\Grid { /** - * @var \Magento\Reports\Model\ResourceModel\Order\CollectionFactory + * @var CollectionFactory */ protected $_collectionFactory; /** - * @var \Magento\Framework\Module\Manager + * @var Manager */ protected $_moduleManager; /** - * @param \Magento\Backend\Block\Template\Context $context - * @param \Magento\Backend\Helper\Data $backendHelper - * @param \Magento\Framework\Module\Manager $moduleManager - * @param \Magento\Reports\Model\ResourceModel\Order\CollectionFactory $collectionFactory + * @param Context $context + * @param Data $backendHelper + * @param Manager $moduleManager + * @param CollectionFactory $collectionFactory * @param array $data */ public function __construct( - \Magento\Backend\Block\Template\Context $context, - \Magento\Backend\Helper\Data $backendHelper, - \Magento\Framework\Module\Manager $moduleManager, - \Magento\Reports\Model\ResourceModel\Order\CollectionFactory $collectionFactory, + Context $context, + Data $backendHelper, + Manager $moduleManager, + CollectionFactory $collectionFactory, array $data = [] ) { $this->_moduleManager = $moduleManager; diff --git a/app/code/Magento/Backend/Block/Dashboard/Sales.php b/app/code/Magento/Backend/Block/Dashboard/Sales.php index b388339460102..ebe0932c3fa3b 100644 --- a/app/code/Magento/Backend/Block/Dashboard/Sales.php +++ b/app/code/Magento/Backend/Block/Dashboard/Sales.php @@ -3,14 +3,21 @@ * Copyright © Magento, Inc. All rights reserved. * See COPYING.txt for license details. */ +declare(strict_types=1); + namespace Magento\Backend\Block\Dashboard; +use Magento\Backend\Block\Template\Context; +use Magento\Framework\Module\Manager; +use Magento\Reports\Model\ResourceModel\Order\CollectionFactory; + /** * Adminhtml dashboard sales statistics bar * + * @api * @author Magento Core Team */ -class Sales extends \Magento\Backend\Block\Dashboard\Bar +class Sales extends Bar { /** * @var string @@ -18,20 +25,20 @@ class Sales extends \Magento\Backend\Block\Dashboard\Bar protected $_template = 'Magento_Backend::dashboard/salebar.phtml'; /** - * @var \Magento\Framework\Module\Manager + * @var Manager */ protected $_moduleManager; /** - * @param \Magento\Backend\Block\Template\Context $context - * @param \Magento\Reports\Model\ResourceModel\Order\CollectionFactory $collectionFactory - * @param \Magento\Framework\Module\Manager $moduleManager + * @param Context $context + * @param CollectionFactory $collectionFactory + * @param Manager $moduleManager * @param array $data */ public function __construct( - \Magento\Backend\Block\Template\Context $context, - \Magento\Reports\Model\ResourceModel\Order\CollectionFactory $collectionFactory, - \Magento\Framework\Module\Manager $moduleManager, + Context $context, + CollectionFactory $collectionFactory, + Manager $moduleManager, array $data = [] ) { $this->_moduleManager = $moduleManager; diff --git a/app/code/Magento/Backend/Block/Dashboard/Tab/Amounts.php b/app/code/Magento/Backend/Block/Dashboard/Tab/Amounts.php index 715d399fa1c7a..26243891fa007 100644 --- a/app/code/Magento/Backend/Block/Dashboard/Tab/Amounts.php +++ b/app/code/Magento/Backend/Block/Dashboard/Tab/Amounts.php @@ -4,13 +4,15 @@ * See COPYING.txt for license details. */ +namespace Magento\Backend\Block\Dashboard\Tab; + /** * Adminhtml dashboard order amounts diagram + * @deprecated dashboard graphs were migrated to dynamic chart.js solution + * @see dashboard.chart.amounts in adminhtml_dashboard_index.xml * * @author Magento Core Team */ -namespace Magento\Backend\Block\Dashboard\Tab; - class Amounts extends \Magento\Backend\Block\Dashboard\Graph { /** diff --git a/app/code/Magento/Backend/Block/Dashboard/Tab/Orders.php b/app/code/Magento/Backend/Block/Dashboard/Tab/Orders.php index 72863f573c3bc..f88e6bb694671 100644 --- a/app/code/Magento/Backend/Block/Dashboard/Tab/Orders.php +++ b/app/code/Magento/Backend/Block/Dashboard/Tab/Orders.php @@ -4,13 +4,15 @@ * See COPYING.txt for license details. */ +namespace Magento\Backend\Block\Dashboard\Tab; + /** * Adminhtml dashboard orders diagram + * @deprecated dashboard graphs were migrated to dynamic chart.js solution + * @see dashboard.chart.orders in adminhtml_dashboard_index.xml * * @author Magento Core Team */ -namespace Magento\Backend\Block\Dashboard\Tab; - class Orders extends \Magento\Backend\Block\Dashboard\Graph { /** diff --git a/app/code/Magento/Backend/Block/Dashboard/Totals.php b/app/code/Magento/Backend/Block/Dashboard/Totals.php index 20bcfebe31a8d..7da109c2fb602 100644 --- a/app/code/Magento/Backend/Block/Dashboard/Totals.php +++ b/app/code/Magento/Backend/Block/Dashboard/Totals.php @@ -3,18 +3,22 @@ * Copyright © Magento, Inc. All rights reserved. * See COPYING.txt for license details. */ +declare(strict_types=1); -/** - * Adminhtml dashboard totals bar - * - * @author Magento Core Team - */ namespace Magento\Backend\Block\Dashboard; +use Magento\Backend\Block\Template\Context; +use Magento\Backend\Model\Dashboard\Period; +use Magento\Framework\Module\Manager; +use Magento\Reports\Model\ResourceModel\Order\Collection; +use Magento\Reports\Model\ResourceModel\Order\CollectionFactory; +use Magento\Store\Model\Store; + /** - * Totals block. + * Adminhtml dashboard totals bar + * @api */ -class Totals extends \Magento\Backend\Block\Dashboard\Bar +class Totals extends Bar { /** * @var string @@ -22,20 +26,20 @@ class Totals extends \Magento\Backend\Block\Dashboard\Bar protected $_template = 'Magento_Backend::dashboard/totalbar.phtml'; /** - * @var \Magento\Framework\Module\Manager + * @var Manager */ protected $_moduleManager; /** - * @param \Magento\Backend\Block\Template\Context $context - * @param \Magento\Reports\Model\ResourceModel\Order\CollectionFactory $collectionFactory - * @param \Magento\Framework\Module\Manager $moduleManager + * @param Context $context + * @param CollectionFactory $collectionFactory + * @param Manager $moduleManager * @param array $data */ public function __construct( - \Magento\Backend\Block\Template\Context $context, - \Magento\Reports\Model\ResourceModel\Order\CollectionFactory $collectionFactory, - \Magento\Framework\Module\Manager $moduleManager, + Context $context, + CollectionFactory $collectionFactory, + Manager $moduleManager, array $data = [] ) { $this->_moduleManager = $moduleManager; @@ -58,9 +62,9 @@ protected function _prepareLayout() ) || $this->getRequest()->getParam( 'group' ); - $period = $this->getRequest()->getParam('period', '24h'); + $period = $this->getRequest()->getParam('period', Period::PERIOD_24_HOURS); - /* @var $collection \Magento\Reports\Model\ResourceModel\Order\Collection */ + /* @var $collection Collection */ $collection = $this->_collectionFactory->create()->addCreateAtPeriodFilter( $period )->calculateTotals( @@ -80,7 +84,7 @@ protected function _prepareLayout() } elseif (!$collection->isLive()) { $collection->addFieldToFilter( 'store_id', - ['eq' => $this->_storeManager->getStore(\Magento\Store\Model\Store::ADMIN_CODE)->getId()] + ['eq' => $this->_storeManager->getStore(Store::ADMIN_CODE)->getId()] ); } } @@ -94,5 +98,7 @@ protected function _prepareLayout() $this->addTotal(__('Tax'), $totals->getTax()); $this->addTotal(__('Shipping'), $totals->getShipping()); $this->addTotal(__('Quantity'), $totals->getQuantity() * 1, true); + + return $this; } } diff --git a/app/code/Magento/Backend/Block/Media/Uploader.php b/app/code/Magento/Backend/Block/Media/Uploader.php index 84fa487281ac8..e95b6397cd244 100644 --- a/app/code/Magento/Backend/Block/Media/Uploader.php +++ b/app/code/Magento/Backend/Block/Media/Uploader.php @@ -87,7 +87,7 @@ protected function _construct() $this->setId($this->getId() . '_Uploader'); - $uploadUrl = $this->_urlBuilder->addSessionParam()->getUrl('adminhtml/*/upload'); + $uploadUrl = $this->_urlBuilder->getUrl('adminhtml/*/upload'); $this->getConfig()->setUrl($uploadUrl); $this->getConfig()->setParams(['form_key' => $this->getFormKey()]); $this->getConfig()->setFileField('file'); diff --git a/app/code/Magento/Backend/Block/Widget/Grid/Column/Renderer/AbstractRenderer.php b/app/code/Magento/Backend/Block/Widget/Grid/Column/Renderer/AbstractRenderer.php index 623a75015eb2f..415ce7c4c21fc 100644 --- a/app/code/Magento/Backend/Block/Widget/Grid/Column/Renderer/AbstractRenderer.php +++ b/app/code/Magento/Backend/Block/Widget/Grid/Column/Renderer/AbstractRenderer.php @@ -9,6 +9,9 @@ use Magento\Framework\DataObject; /** + * Produce html output using the given data source. + * + * phpcs:disable Magento2.Classes.AbstractApi * Backend grid item abstract renderer * @api * @SuppressWarnings(PHPMD.NumberOfChildren) @@ -53,7 +56,7 @@ public function getColumn() * Renders grid column * * @param DataObject $row - * @return string + * @return string */ public function render(DataObject $row) { @@ -62,7 +65,7 @@ public function render(DataObject $row) $result .= $this->getColumn()->getEditOnly() ? '' : '' . $this->_getValue($row) . ''; - return $result . $this->_getInputValueElement($row) . '' ; + return $result . $this->_getInputValueElement($row) . ''; } return $this->_getValue($row); } @@ -90,6 +93,7 @@ protected function _getValue(DataObject $row) if (is_string($getter)) { return $row->{$getter}(); } elseif (is_callable($getter)) { + //phpcs:ignore Magento2.Functions.DiscouragedFunction return call_user_func($getter, $row); } return ''; diff --git a/app/code/Magento/Backend/Controller/Adminhtml/Dashboard/AjaxBlock.php b/app/code/Magento/Backend/Controller/Adminhtml/Dashboard/AjaxBlock.php index 0b0b707557035..3ad0ab592f445 100644 --- a/app/code/Magento/Backend/Controller/Adminhtml/Dashboard/AjaxBlock.php +++ b/app/code/Magento/Backend/Controller/Adminhtml/Dashboard/AjaxBlock.php @@ -1,32 +1,45 @@ resultRawFactory = $resultRawFactory; @@ -34,23 +47,22 @@ public function __construct( } /** - * @return \Magento\Framework\Controller\Result\Raw + * Retrieve block content via ajax + * + * @return Raw */ public function execute() { $output = ''; $blockTab = $this->getRequest()->getParam('block'); - $blockClassSuffix = str_replace( - ' ', - '\\', - ucwords(str_replace('_', ' ', $blockTab)) - ); - if (in_array($blockTab, ['tab_orders', 'tab_amounts', 'totals'])) { + + if ($blockTab === 'totals') { $output = $this->layoutFactory->create() - ->createBlock('Magento\\Backend\\Block\\Dashboard\\' . $blockClassSuffix) + ->createBlock(Totals::class) ->toHtml(); } - /** @var \Magento\Framework\Controller\Result\Raw $resultRaw */ + + /** @var Raw $resultRaw */ $resultRaw = $this->resultRawFactory->create(); return $resultRaw->setContents($output); } diff --git a/app/code/Magento/Backend/Controller/Adminhtml/Dashboard/Chart/Amounts.php b/app/code/Magento/Backend/Controller/Adminhtml/Dashboard/Chart/Amounts.php new file mode 100644 index 0000000000000..a668296f5bf6f --- /dev/null +++ b/app/code/Magento/Backend/Controller/Adminhtml/Dashboard/Chart/Amounts.php @@ -0,0 +1,70 @@ +resultJsonFactory = $resultJsonFactory; + $this->chart = $chart; + } + + /** + * Get chart data + * + * @return Json + */ + public function execute(): Json + { + $data = [ + 'data' => $this->chart->getByPeriod( + $this->_request->getParam('period'), + 'revenue', + $this->_request->getParam('store'), + $this->_request->getParam('website'), + $this->_request->getParam('group') + ), + 'label' => __('Revenue') + ]; + + return $this->resultJsonFactory->create() + ->setData($data); + } +} diff --git a/app/code/Magento/Backend/Controller/Adminhtml/Dashboard/Chart/Orders.php b/app/code/Magento/Backend/Controller/Adminhtml/Dashboard/Chart/Orders.php new file mode 100644 index 0000000000000..b9f7c86f17dc0 --- /dev/null +++ b/app/code/Magento/Backend/Controller/Adminhtml/Dashboard/Chart/Orders.php @@ -0,0 +1,70 @@ +resultJsonFactory = $resultJsonFactory; + $this->chart = $chart; + } + + /** + * Get chart data + * + * @return Json + */ + public function execute(): Json + { + $data = [ + 'data' => $this->chart->getByPeriod( + $this->_request->getParam('period'), + 'quantity', + $this->_request->getParam('store'), + $this->_request->getParam('website'), + $this->_request->getParam('group') + ), + 'label' => __('Quantity') + ]; + + return $this->resultJsonFactory->create() + ->setData($data); + } +} diff --git a/app/code/Magento/Backend/Controller/Adminhtml/Dashboard/Tunnel.php b/app/code/Magento/Backend/Controller/Adminhtml/Dashboard/Tunnel.php index eb991baf13f41..2e0ed47a97bbf 100644 --- a/app/code/Magento/Backend/Controller/Adminhtml/Dashboard/Tunnel.php +++ b/app/code/Magento/Backend/Controller/Adminhtml/Dashboard/Tunnel.php @@ -1,16 +1,21 @@ getChartDataHash($gaData); if (Security::compareStrings($newHash, $gaHash)) { $params = null; + // phpcs:ignore Magento2.Functions.DiscouragedFunction $paramsJson = base64_decode(urldecode($gaData)); if ($paramsJson) { $params = json_decode($paramsJson, true); diff --git a/app/code/Magento/Backend/Controller/Adminhtml/Denied/Index.php b/app/code/Magento/Backend/Controller/Adminhtml/Denied/Index.php new file mode 100644 index 0000000000000..c06c0a0f01650 --- /dev/null +++ b/app/code/Magento/Backend/Controller/Adminhtml/Denied/Index.php @@ -0,0 +1,27 @@ +_installDate = $deploymentConfig->get(ConfigOptionsListConstants::CONFIG_PATH_INSTALL_DATE); $this->_storeManager = $storeManager; + $this->period = $period ?? ObjectManager::getInstance()->get(Period::class); } /** @@ -74,17 +88,14 @@ public function countStores() /** * Prepare array with periods for dashboard graphs * + * @deprecated periods were moved to it's own class + * @see Period::getDatePeriods() + * * @return array */ public function getDatePeriods() { - return [ - '24h' => __('Last 24 Hours'), - '7d' => __('Last 7 Days'), - '1m' => __('Current Month'), - '1y' => __('YTD'), - '2y' => __('2YTD') - ]; + return $this->period->getDatePeriods(); } /** diff --git a/app/code/Magento/Backend/Model/Dashboard/Chart.php b/app/code/Magento/Backend/Model/Dashboard/Chart.php new file mode 100644 index 0000000000000..346c1153ac74e --- /dev/null +++ b/app/code/Magento/Backend/Model/Dashboard/Chart.php @@ -0,0 +1,95 @@ +dateRetriever = $dateRetriever; + $this->orderHelper = $orderHelper; + $this->period = $period; + } + + /** + * Get chart data by period and chart type parameter, with possibility to pass scope parameters + * + * @param string $period + * @param string $chartParam + * @param string|null $store + * @param string|null $website + * @param string|null $group + * + * @return array + */ + public function getByPeriod( + string $period, + string $chartParam, + string $store = null, + string $website = null, + string $group = null + ): array { + $this->orderHelper->setParam('store', $store); + $this->orderHelper->setParam('website', $website); + $this->orderHelper->setParam('group', $group); + + $availablePeriods = array_keys($this->period->getDatePeriods()); + $this->orderHelper->setParam( + 'period', + $period && in_array($period, $availablePeriods, false) ? $period : Period::PERIOD_24_HOURS + ); + + $dates = $this->dateRetriever->getByPeriod($period); + $collection = $this->orderHelper->getCollection(); + + $data = []; + + if ($collection->count() > 0) { + foreach ($dates as $date) { + $item = $collection->getItemByColumnValue('range', $date); + + $data[] = [ + 'x' => $date, + 'y' => $item ? (float)$item->getData($chartParam) : 0 + ]; + } + } + + return $data; + } +} diff --git a/app/code/Magento/Backend/Model/Dashboard/Chart/Date.php b/app/code/Magento/Backend/Model/Dashboard/Chart/Date.php new file mode 100644 index 0000000000000..7e3f853714a34 --- /dev/null +++ b/app/code/Magento/Backend/Model/Dashboard/Chart/Date.php @@ -0,0 +1,95 @@ +collectionFactory = $collectionFactory; + $this->localeDate = $localeDate; + } + + /** + * Get chart dates data by period + * + * @param string $period + * + * @return array + */ + public function getByPeriod(string $period): array + { + [$dateStart, $dateEnd] = $this->collectionFactory->create()->getDateRange( + $period, + '', + '', + true + ); + + $timezoneLocal = $this->localeDate->getConfigTimezone(); + + $dateStart->setTimezone(new DateTimeZone($timezoneLocal)); + $dateEnd->setTimezone(new DateTimeZone($timezoneLocal)); + + if ($period === Period::PERIOD_24_HOURS) { + $dateEnd->modify('-1 hour'); + } else { + $dateEnd->setTime(23, 59, 59); + $dateStart->setTime(0, 0, 0); + } + + $dates = []; + + while ($dateStart <= $dateEnd) { + switch ($period) { + case Period::PERIOD_7_DAYS: + case Period::PERIOD_1_MONTH: + $d = $dateStart->format('Y-m-d'); + $dateStart->modify('+1 day'); + break; + case Period::PERIOD_1_YEAR: + case Period::PERIOD_2_YEARS: + $d = $dateStart->format('Y-m'); + $dateStart->modify('first day of next month'); + break; + default: + $d = $dateStart->format('Y-m-d H:00'); + $dateStart->modify('+1 hour'); + } + + $dates[] = $d; + } + + return $dates; + } +} diff --git a/app/code/Magento/Backend/Model/Dashboard/Period.php b/app/code/Magento/Backend/Model/Dashboard/Period.php new file mode 100644 index 0000000000000..28286129e8a68 --- /dev/null +++ b/app/code/Magento/Backend/Model/Dashboard/Period.php @@ -0,0 +1,56 @@ + __('Last 24 Hours'), + static::PERIOD_7_DAYS => __('Last 7 Days'), + static::PERIOD_1_MONTH => __('Current Month'), + static::PERIOD_1_YEAR => __('YTD'), + static::PERIOD_2_YEARS => __('2YTD') + ]; + } + + /** + * Prepare array with periods mapping to chart units + * + * @return array + */ + public function getPeriodChartUnits(): array + { + return [ + static::PERIOD_24_HOURS => static::PERIOD_UNIT_HOUR, + static::PERIOD_7_DAYS => static::PERIOD_UNIT_DAY, + static::PERIOD_1_MONTH => static::PERIOD_UNIT_DAY, + static::PERIOD_1_YEAR => static::PERIOD_UNIT_MONTH, + static::PERIOD_2_YEARS => static::PERIOD_UNIT_MONTH + ]; + } +} diff --git a/app/code/Magento/Backend/Model/Url.php b/app/code/Magento/Backend/Model/Url.php index ba0c3d1cfacee..97f82647d9445 100644 --- a/app/code/Magento/Backend/Model/Url.php +++ b/app/code/Magento/Backend/Model/Url.php @@ -349,7 +349,7 @@ public function findFirstAvailableMenu() if ($user) { $user->setHasAvailableResources(false); } - $action = '*/*/denied'; + $action = '*/denied'; } return $action; } diff --git a/app/code/Magento/Backend/README.md b/app/code/Magento/Backend/README.md index 205051809328a..f70dc9f676236 100644 --- a/app/code/Magento/Backend/README.md +++ b/app/code/Magento/Backend/README.md @@ -16,7 +16,6 @@ Before disabling or uninstalling this module, note that the following modules de - Magento_ReleaseNotification - Magento_Search - Magento_Security -- Magento_Signifyd - Magento_Swatches - Magento_Ui - Magento_User diff --git a/app/code/Magento/Backend/Test/Mftf/ActionGroup/AdminClickLogoActionGroup.xml b/app/code/Magento/Backend/Test/Mftf/ActionGroup/AdminClickLogoActionGroup.xml new file mode 100644 index 0000000000000..5fe89c3b8f6f4 --- /dev/null +++ b/app/code/Magento/Backend/Test/Mftf/ActionGroup/AdminClickLogoActionGroup.xml @@ -0,0 +1,15 @@ + + + + + + + + + diff --git a/app/code/Magento/Backend/Test/Mftf/ActionGroup/AdminLoginActionGroup.xml b/app/code/Magento/Backend/Test/Mftf/ActionGroup/AdminLoginActionGroup.xml new file mode 100644 index 0000000000000..0320862b99e63 --- /dev/null +++ b/app/code/Magento/Backend/Test/Mftf/ActionGroup/AdminLoginActionGroup.xml @@ -0,0 +1,27 @@ + + + + + + + Login to Backend Admin using provided User Data. PLEASE NOTE: This Action Group does NOT validate that you are Logged In. + + + + + + + + + + + + + + diff --git a/app/code/Magento/Backend/Test/Mftf/ActionGroup/AdminLogoutActionGroup.xml b/app/code/Magento/Backend/Test/Mftf/ActionGroup/AdminLogoutActionGroup.xml new file mode 100644 index 0000000000000..de0def48a1f52 --- /dev/null +++ b/app/code/Magento/Backend/Test/Mftf/ActionGroup/AdminLogoutActionGroup.xml @@ -0,0 +1,14 @@ + + + + + + + + diff --git a/app/code/Magento/Backend/Test/Mftf/ActionGroup/AdminNavigateToPersistentShoppingCartSettingsActionGroup.xml b/app/code/Magento/Backend/Test/Mftf/ActionGroup/AdminNavigateToPersistentShoppingCartSettingsActionGroup.xml new file mode 100644 index 0000000000000..811187b9fe95e --- /dev/null +++ b/app/code/Magento/Backend/Test/Mftf/ActionGroup/AdminNavigateToPersistentShoppingCartSettingsActionGroup.xml @@ -0,0 +1,15 @@ + + + + + + + + + diff --git a/app/code/Magento/Backend/Test/Mftf/ActionGroup/AdminNavigateToSetupWizardPageActionGroup.xml b/app/code/Magento/Backend/Test/Mftf/ActionGroup/AdminNavigateToSetupWizardPageActionGroup.xml new file mode 100644 index 0000000000000..5d5a233186c1f --- /dev/null +++ b/app/code/Magento/Backend/Test/Mftf/ActionGroup/AdminNavigateToSetupWizardPageActionGroup.xml @@ -0,0 +1,18 @@ + + + + + + + Open Setup Wizard Page. + + + + + diff --git a/app/code/Magento/Backend/Test/Mftf/ActionGroup/AdminSetStoreInformationConfigurationActionGroup.xml b/app/code/Magento/Backend/Test/Mftf/ActionGroup/AdminSetStoreInformationConfigurationActionGroup.xml new file mode 100644 index 0000000000000..9de4b684f6f5c --- /dev/null +++ b/app/code/Magento/Backend/Test/Mftf/ActionGroup/AdminSetStoreInformationConfigurationActionGroup.xml @@ -0,0 +1,36 @@ + + + + + + + Set Store Information configurations + + + + + + + + + + + + + + + + + + + + + + + diff --git a/app/code/Magento/Backend/Test/Mftf/ActionGroup/AssertAdminPersistentShoppingCartOptionsAvailableActionGroup.xml b/app/code/Magento/Backend/Test/Mftf/ActionGroup/AssertAdminPersistentShoppingCartOptionsAvailableActionGroup.xml new file mode 100644 index 0000000000000..3b75602e6d89c --- /dev/null +++ b/app/code/Magento/Backend/Test/Mftf/ActionGroup/AssertAdminPersistentShoppingCartOptionsAvailableActionGroup.xml @@ -0,0 +1,18 @@ + + + + + + + + + + + + diff --git a/app/code/Magento/Backend/Test/Mftf/ActionGroup/AssertOrderGraphImageOnDashboardActionGroup.xml b/app/code/Magento/Backend/Test/Mftf/ActionGroup/AssertOrderGraphImageOnDashboardActionGroup.xml index 3e3b0bc6a8a43..4be01fda862f8 100644 --- a/app/code/Magento/Backend/Test/Mftf/ActionGroup/AssertOrderGraphImageOnDashboardActionGroup.xml +++ b/app/code/Magento/Backend/Test/Mftf/ActionGroup/AssertOrderGraphImageOnDashboardActionGroup.xml @@ -10,6 +10,6 @@ xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/actionGroupSchema.xsd"> - + diff --git a/app/code/Magento/Backend/Test/Mftf/ActionGroup/LoginActionGroup.xml b/app/code/Magento/Backend/Test/Mftf/ActionGroup/LoginActionGroup.xml deleted file mode 100644 index 7068478bb4790..0000000000000 --- a/app/code/Magento/Backend/Test/Mftf/ActionGroup/LoginActionGroup.xml +++ /dev/null @@ -1,21 +0,0 @@ - - - - - - - Login to Backend Admin using ENV Admin credentials. PLEASE NOTE: This Action Group does NOT validate that you are Logged In. - - - - - - - - diff --git a/app/code/Magento/Backend/Test/Mftf/ActionGroup/LoginAdminWithCredentialsActionGroup.xml b/app/code/Magento/Backend/Test/Mftf/ActionGroup/LoginAdminWithCredentialsActionGroup.xml deleted file mode 100644 index 995a5e7130e0a..0000000000000 --- a/app/code/Magento/Backend/Test/Mftf/ActionGroup/LoginAdminWithCredentialsActionGroup.xml +++ /dev/null @@ -1,26 +0,0 @@ - - - - - - - Login to Backend Admin using provided Admin credentials. PLEASE NOTE: This Action Group does NOT validate that you are Logged In. - - - - - - - - - - - - - diff --git a/app/code/Magento/Backend/Test/Mftf/ActionGroup/LoginAsAdminActionGroup.xml b/app/code/Magento/Backend/Test/Mftf/ActionGroup/LoginAsAdminActionGroup.xml deleted file mode 100644 index 7e1dfe4cf381b..0000000000000 --- a/app/code/Magento/Backend/Test/Mftf/ActionGroup/LoginAsAdminActionGroup.xml +++ /dev/null @@ -1,25 +0,0 @@ - - - - - - - Login to Backend Admin using provided User Data. PLEASE NOTE: This Action Group does NOT validate that you are Logged In. - - - - - - - - - - - - diff --git a/app/code/Magento/Backend/Test/Mftf/ActionGroup/LogoutActionGroup.xml b/app/code/Magento/Backend/Test/Mftf/ActionGroup/LogoutActionGroup.xml deleted file mode 100644 index 4c265d08dd041..0000000000000 --- a/app/code/Magento/Backend/Test/Mftf/ActionGroup/LogoutActionGroup.xml +++ /dev/null @@ -1,18 +0,0 @@ - - - - - - - Logout of the Backend Admin. PLEASE NOTE: This Action Group does NOT validate that you are Logged Out. - - - - - diff --git a/app/code/Magento/Backend/Test/Mftf/Data/AdminGeneralStoreInfomationConfigData.xml b/app/code/Magento/Backend/Test/Mftf/Data/AdminGeneralStoreInfomationConfigData.xml index a8db2f94d69ab..2c50bc0fc75f4 100644 --- a/app/code/Magento/Backend/Test/Mftf/Data/AdminGeneralStoreInfomationConfigData.xml +++ b/app/code/Magento/Backend/Test/Mftf/Data/AdminGeneralStoreInfomationConfigData.xml @@ -30,4 +30,11 @@ general/store_information/street_line2 + + general/store_information/region_id + + + general/store_information/hours + 8AM-8PM + diff --git a/app/code/Magento/Backend/Test/Mftf/Page/AdminConfigurationPersistentShoppingCartPage.xml b/app/code/Magento/Backend/Test/Mftf/Page/AdminConfigurationPersistentShoppingCartPage.xml new file mode 100644 index 0000000000000..f7e579c57e21c --- /dev/null +++ b/app/code/Magento/Backend/Test/Mftf/Page/AdminConfigurationPersistentShoppingCartPage.xml @@ -0,0 +1,14 @@ + + + + + +
+ + diff --git a/app/code/Magento/Backend/Test/Mftf/Page/AdminConfigurationStoresPage.xml b/app/code/Magento/Backend/Test/Mftf/Page/AdminConfigurationStoresPage.xml deleted file mode 100644 index 8afc2c5bbb32f..0000000000000 --- a/app/code/Magento/Backend/Test/Mftf/Page/AdminConfigurationStoresPage.xml +++ /dev/null @@ -1,20 +0,0 @@ - - - - - -
- - -
- - -
- - diff --git a/app/code/Magento/Backend/Test/Mftf/Page/AdminConfigurationStoresPage/ConfigurationStoresPage.xml b/app/code/Magento/Backend/Test/Mftf/Page/AdminConfigurationStoresPage/ConfigurationStoresPage.xml new file mode 100644 index 0000000000000..81eb04692cc57 --- /dev/null +++ b/app/code/Magento/Backend/Test/Mftf/Page/AdminConfigurationStoresPage/ConfigurationStoresPage.xml @@ -0,0 +1,13 @@ + + + + +
+ + diff --git a/app/code/Magento/Backend/Test/Mftf/Page/AdminConfigurationStoresPage/GeneralConfigurationPage.xml b/app/code/Magento/Backend/Test/Mftf/Page/AdminConfigurationStoresPage/GeneralConfigurationPage.xml new file mode 100644 index 0000000000000..59b461fb08a4d --- /dev/null +++ b/app/code/Magento/Backend/Test/Mftf/Page/AdminConfigurationStoresPage/GeneralConfigurationPage.xml @@ -0,0 +1,13 @@ + + + + +
+ + diff --git a/app/code/Magento/Backend/Test/Mftf/Page/AdminConfigurationStoresPage/WebConfigurationPage.xml b/app/code/Magento/Backend/Test/Mftf/Page/AdminConfigurationStoresPage/WebConfigurationPage.xml new file mode 100644 index 0000000000000..b5429b091d755 --- /dev/null +++ b/app/code/Magento/Backend/Test/Mftf/Page/AdminConfigurationStoresPage/WebConfigurationPage.xml @@ -0,0 +1,13 @@ + + + + +
+ + diff --git a/app/code/Magento/Backend/Test/Mftf/Page/AdminSetupWizardPage.xml b/app/code/Magento/Backend/Test/Mftf/Page/AdminSetupWizardPage.xml new file mode 100644 index 0000000000000..40076ccd42b3a --- /dev/null +++ b/app/code/Magento/Backend/Test/Mftf/Page/AdminSetupWizardPage.xml @@ -0,0 +1,12 @@ + + + + + + diff --git a/app/code/Magento/Backend/Test/Mftf/Section/AdminDashboardSection.xml b/app/code/Magento/Backend/Test/Mftf/Section/AdminDashboardSection.xml index 61fe7ffa48e23..e67025cfa68d5 100644 --- a/app/code/Magento/Backend/Test/Mftf/Section/AdminDashboardSection.xml +++ b/app/code/Magento/Backend/Test/Mftf/Section/AdminDashboardSection.xml @@ -10,7 +10,7 @@ xsi:noNamespaceSchemaLocation="urn:magento:mftf:Page/etc/SectionObject.xsd">
- + diff --git a/app/code/Magento/Backend/Test/Mftf/Section/AdminMenuSection.xml b/app/code/Magento/Backend/Test/Mftf/Section/AdminMenuSection.xml index 8498ad8c52e41..e6782dca897d7 100644 --- a/app/code/Magento/Backend/Test/Mftf/Section/AdminMenuSection.xml +++ b/app/code/Magento/Backend/Test/Mftf/Section/AdminMenuSection.xml @@ -9,6 +9,7 @@
+ diff --git a/app/code/Magento/Backend/Test/Mftf/Section/AdminMessagesSection.xml b/app/code/Magento/Backend/Test/Mftf/Section/AdminMessagesSection.xml index bb1123d01c867..7983554051f89 100644 --- a/app/code/Magento/Backend/Test/Mftf/Section/AdminMessagesSection.xml +++ b/app/code/Magento/Backend/Test/Mftf/Section/AdminMessagesSection.xml @@ -9,6 +9,8 @@
+ + @@ -16,8 +18,7 @@ - - + diff --git a/app/code/Magento/Backend/Test/Mftf/Test/AdminAttributeTextSwatchesCanBeFiledTest.xml b/app/code/Magento/Backend/Test/Mftf/Test/AdminAttributeTextSwatchesCanBeFiledTest.xml index 2c061e54f5509..8ac7af096da0a 100644 --- a/app/code/Magento/Backend/Test/Mftf/Test/AdminAttributeTextSwatchesCanBeFiledTest.xml +++ b/app/code/Magento/Backend/Test/Mftf/Test/AdminAttributeTextSwatchesCanBeFiledTest.xml @@ -22,7 +22,7 @@ - + @@ -58,7 +58,7 @@ - + @@ -110,7 +110,11 @@ - - + + $grabSwatchForAdmin + + + $grabDescriptionForAdmin + diff --git a/app/code/Magento/Backend/Test/Mftf/Test/AdminCheckLocaleAndDeveloperConfigInDeveloperModeTest.xml b/app/code/Magento/Backend/Test/Mftf/Test/AdminCheckLocaleAndDeveloperConfigInDeveloperModeTest.xml index 47b8715b5541c..cbe0f0b5f4fe9 100644 --- a/app/code/Magento/Backend/Test/Mftf/Test/AdminCheckLocaleAndDeveloperConfigInDeveloperModeTest.xml +++ b/app/code/Magento/Backend/Test/Mftf/Test/AdminCheckLocaleAndDeveloperConfigInDeveloperModeTest.xml @@ -21,7 +21,7 @@ - + diff --git a/app/code/Magento/Backend/Test/Mftf/Test/AdminCheckLocaleAndDeveloperConfigInProductionModeTest.xml b/app/code/Magento/Backend/Test/Mftf/Test/AdminCheckLocaleAndDeveloperConfigInProductionModeTest.xml index ae7722b225cdd..323ae324d4950 100644 --- a/app/code/Magento/Backend/Test/Mftf/Test/AdminCheckLocaleAndDeveloperConfigInProductionModeTest.xml +++ b/app/code/Magento/Backend/Test/Mftf/Test/AdminCheckLocaleAndDeveloperConfigInProductionModeTest.xml @@ -21,14 +21,16 @@ - + - + + + diff --git a/app/code/Magento/Backend/Test/Mftf/Test/AdminContentScheduleNavigateMenuTest.xml b/app/code/Magento/Backend/Test/Mftf/Test/AdminContentScheduleNavigateMenuTest.xml index bead59653eee8..8ae6d6de79c0b 100644 --- a/app/code/Magento/Backend/Test/Mftf/Test/AdminContentScheduleNavigateMenuTest.xml +++ b/app/code/Magento/Backend/Test/Mftf/Test/AdminContentScheduleNavigateMenuTest.xml @@ -20,10 +20,10 @@ - + - + diff --git a/app/code/Magento/Backend/Test/Mftf/Test/AdminDashboardNavigateMenuTest.xml b/app/code/Magento/Backend/Test/Mftf/Test/AdminDashboardNavigateMenuTest.xml index 6434d74b28754..efcc6da30199a 100644 --- a/app/code/Magento/Backend/Test/Mftf/Test/AdminDashboardNavigateMenuTest.xml +++ b/app/code/Magento/Backend/Test/Mftf/Test/AdminDashboardNavigateMenuTest.xml @@ -20,10 +20,10 @@ - + - + diff --git a/app/code/Magento/Backend/Test/Mftf/Test/AdminDashboardWithChartsChart.xml b/app/code/Magento/Backend/Test/Mftf/Test/AdminDashboardWithChartsChart.xml index 3ba965f746722..147f2fa1dfaed 100644 --- a/app/code/Magento/Backend/Test/Mftf/Test/AdminDashboardWithChartsChart.xml +++ b/app/code/Magento/Backend/Test/Mftf/Test/AdminDashboardWithChartsChart.xml @@ -28,6 +28,7 @@ John1 Doe1 + @@ -37,11 +38,11 @@ - + - + @@ -120,6 +121,9 @@ - + + $grabEndQuantity + $grabStartQuantity + diff --git a/app/code/Magento/Backend/Test/Mftf/Test/AdminExpireAdminSessionTest.xml b/app/code/Magento/Backend/Test/Mftf/Test/AdminExpireAdminSessionTest.xml index 88d26c052b59b..2469151337bfe 100644 --- a/app/code/Magento/Backend/Test/Mftf/Test/AdminExpireAdminSessionTest.xml +++ b/app/code/Magento/Backend/Test/Mftf/Test/AdminExpireAdminSessionTest.xml @@ -27,7 +27,7 @@ - + diff --git a/app/code/Magento/Backend/Test/Mftf/Test/AdminExpireCustomerSessionTest.xml b/app/code/Magento/Backend/Test/Mftf/Test/AdminExpireCustomerSessionTest.xml index 88646401e3a99..0e3bf07d32441 100644 --- a/app/code/Magento/Backend/Test/Mftf/Test/AdminExpireCustomerSessionTest.xml +++ b/app/code/Magento/Backend/Test/Mftf/Test/AdminExpireCustomerSessionTest.xml @@ -24,10 +24,10 @@ - + - + diff --git a/app/code/Magento/Backend/Test/Mftf/Test/AdminLoginAfterChangeCookieDomainTest.xml b/app/code/Magento/Backend/Test/Mftf/Test/AdminLoginAfterChangeCookieDomainTest.xml index 93d411c8827ed..be734205e1f5b 100644 --- a/app/code/Magento/Backend/Test/Mftf/Test/AdminLoginAfterChangeCookieDomainTest.xml +++ b/app/code/Magento/Backend/Test/Mftf/Test/AdminLoginAfterChangeCookieDomainTest.xml @@ -27,8 +27,8 @@ - + - + diff --git a/app/code/Magento/Backend/Test/Mftf/Test/AdminLoginAfterJSMinificationTest.xml b/app/code/Magento/Backend/Test/Mftf/Test/AdminLoginAfterJSMinificationTest.xml index 38749dfd792ca..d2c628ed13701 100644 --- a/app/code/Magento/Backend/Test/Mftf/Test/AdminLoginAfterJSMinificationTest.xml +++ b/app/code/Magento/Backend/Test/Mftf/Test/AdminLoginAfterJSMinificationTest.xml @@ -18,19 +18,18 @@ - - - - + + - + - + + diff --git a/app/code/Magento/Backend/Test/Mftf/Test/AdminLoginFailedTest.xml b/app/code/Magento/Backend/Test/Mftf/Test/AdminLoginFailedTest.xml new file mode 100644 index 0000000000000..1239fc471f59e --- /dev/null +++ b/app/code/Magento/Backend/Test/Mftf/Test/AdminLoginFailedTest.xml @@ -0,0 +1,28 @@ + + + + + + + + + + <description value="Admin should not be able to log into the backend with invalid credentials"/> + <severity value="CRITICAL"/> + <testCaseId value="MAGETWO-71572"/> + <group value="example"/> + <group value="login"/> + </annotations> + + <actionGroup ref="AdminLoginActionGroup" stepKey="loginAsAdmin"> + <argument name="password" value="INVALID!{{_ENV.MAGENTO_ADMIN_PASSWORD}}"/> + </actionGroup> + <actionGroup ref="AssertMessageOnAdminLoginActionGroup" stepKey="assertErrorMessage"/> + </test> +</tests> diff --git a/app/code/Magento/Backend/Test/Mftf/Test/AdminLoginSuccessfulTest.xml b/app/code/Magento/Backend/Test/Mftf/Test/AdminLoginSuccessfulTest.xml new file mode 100644 index 0000000000000..a8de04f4342de --- /dev/null +++ b/app/code/Magento/Backend/Test/Mftf/Test/AdminLoginSuccessfulTest.xml @@ -0,0 +1,27 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + /** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +--> + +<tests xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/testSchema.xsd"> + <test name="AdminLoginSuccessfulTest"> + <annotations> + <features value="Backend"/> + <stories value="Login on the Admin Login page"/> + <title value="Admin should be able to log into the Magento Admin backend successfully"/> + <description value="Admin should be able to log into the Magento Admin backend successfully"/> + <severity value="CRITICAL"/> + <testCaseId value="MAGETWO-71572"/> + <group value="example"/> + <group value="login"/> + </annotations> + + <actionGroup ref="AdminLoginActionGroup" stepKey="loginAsAdmin"/> + <actionGroup ref="AssertAdminSuccessLoginActionGroup" stepKey="assertLoggedIn"/> + <actionGroup ref="AdminLogoutActionGroup" stepKey="logoutFromAdmin"/> + </test> +</tests> diff --git a/app/code/Magento/Backend/Test/Mftf/Test/AdminLoginTest.xml b/app/code/Magento/Backend/Test/Mftf/Test/AdminLoginTest.xml deleted file mode 100644 index ce33f01c60141..0000000000000 --- a/app/code/Magento/Backend/Test/Mftf/Test/AdminLoginTest.xml +++ /dev/null @@ -1,27 +0,0 @@ -<?xml version="1.0" encoding="UTF-8"?> -<!-- - /** - * Copyright © Magento, Inc. All rights reserved. - * See COPYING.txt for license details. - */ ---> - -<tests xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" - xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/testSchema.xsd"> - <test name="AdminLoginTest"> - <annotations> - <features value="Backend"/> - <stories value="Login on the Admin Login page"/> - <title value="Admin should be able to log into the Magento Admin backend"/> - <description value="Admin should be able to log into the Magento Admin backend"/> - <severity value="CRITICAL"/> - <testCaseId value="MAGETWO-71572"/> - <group value="example"/> - <group value="login"/> - </annotations> - - <actionGroup ref="LoginAsAdmin" stepKey="loginAsAdmin"/> - <seeInCurrentUrl url="{{AdminLoginPage.url}}" stepKey="seeAdminLoginUrl"/> - <actionGroup ref="logout" stepKey="logoutFromAdmin"/> - </test> -</tests> \ No newline at end of file diff --git a/app/code/Magento/Backend/Test/Mftf/Test/AdminLoginWithRestrictPermissionTest.xml b/app/code/Magento/Backend/Test/Mftf/Test/AdminLoginWithRestrictPermissionTest.xml new file mode 100644 index 0000000000000..b3797b0720400 --- /dev/null +++ b/app/code/Magento/Backend/Test/Mftf/Test/AdminLoginWithRestrictPermissionTest.xml @@ -0,0 +1,62 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + /** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +--> + +<tests xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/testSchema.xsd"> + <test name="AdminLoginWithRestrictPermissionTest"> + <annotations> + <features value="Backend"/> + <title value="Login with restrict role."/> + <stories value="Login on the Admin Login page" /> + <testCaseId value="MC-29321" /> + <severity value="MAJOR" /> + <description value="Check login with restrict role."/> + <group value="login"/> + </annotations> + + <before> + <actionGroup ref="AdminLoginActionGroup" stepKey="logIn"/> + <!--Create user role--> + <actionGroup ref="AdminFillUserRoleRequiredDataActionGroup" stepKey="fillUserRoleRequiredData"> + <argument name="User" value="adminRole"/> + <argument name="restrictedRole" value="Global Search"/> + </actionGroup> + <actionGroup ref="AdminUserClickRoleResourceTabActionGroup" stepKey="switchToRoleResourceTab"/> + <actionGroup ref="AdminAddRestrictedRoleActionGroup" stepKey="addRestrictedRoleStores"> + <argument name="User" value="adminRole"/> + <argument name="restrictedRole" value="Global Search"/> + </actionGroup> + <actionGroup ref="AdminUserSaveRoleActionGroup" stepKey="saveRole"/> + <!--Create user and assign role to it--> + <actionGroup ref="AdminCreateUserActionGroup" stepKey="createAdminUser"> + <argument name="role" value="adminRole"/> + <argument name="User" value="admin2"/> + </actionGroup> + </before> + <after> + <actionGroup ref="AdminLogoutActionGroup" stepKey="logoutAsSaleRoleUser"/> + <actionGroup ref="AdminLoginActionGroup" stepKey="loginAsAdmin"/> + <!--Delete created data--> + <actionGroup ref="AdminUserOpenAdminRolesPageActionGroup" stepKey="navigateToUserRoleGrid"/> + <actionGroup ref="AdminDeleteRoleActionGroup" stepKey="deleteUserRole"> + <argument name="role" value="adminRole"/> + </actionGroup> + <actionGroup ref="AdminOpenAdminUsersPageActionGroup" stepKey="goToAllUsersPage"/> + <actionGroup ref="AdminDeleteNewUserActionGroup" stepKey="deleteUser"> + <argument name="userName" value="{{admin2.username}}"/> + </actionGroup> + </after> + <!--Log out of admin and login with newly created user--> + <actionGroup ref="AdminLogoutActionGroup" stepKey="logoutOfAdmin"/> + <actionGroup ref="AdminLoginActionGroup" stepKey="loginAsNewUser"> + <argument name="username" value="{{admin2.username}}"/> + <argument name="password" value="{{admin2.password}}"/> + </actionGroup> + <actionGroup ref="AssertUserRoleRestrictedAccessActionGroup" stepKey="assertRestrictPage"/> + </test> +</tests> diff --git a/app/code/Magento/Backend/Test/Mftf/Test/AdminMenuNavigationWithSecretKeysTest.xml b/app/code/Magento/Backend/Test/Mftf/Test/AdminMenuNavigationWithSecretKeysTest.xml index c9a3b8089cc1d..812158948d85f 100644 --- a/app/code/Magento/Backend/Test/Mftf/Test/AdminMenuNavigationWithSecretKeysTest.xml +++ b/app/code/Magento/Backend/Test/Mftf/Test/AdminMenuNavigationWithSecretKeysTest.xml @@ -21,12 +21,12 @@ <before> <magentoCLI command="config:set admin/security/use_form_key 1" stepKey="enableUrlSecretKeys"/> <magentoCLI command="cache:clean config full_page" stepKey="cleanInvalidatedCaches1"/> - <actionGroup ref="LoginAsAdmin" stepKey="loginAsAdmin"/> + <actionGroup ref="AdminLoginActionGroup" stepKey="loginAsAdmin"/> </before> <after> <magentoCLI command="config:set admin/security/use_form_key 0" stepKey="disableUrlSecretKeys"/> <magentoCLI command="cache:clean config full_page" stepKey="cleanInvalidatedCaches2"/> - <actionGroup ref="logout" stepKey="logout"/> + <actionGroup ref="AdminLogoutActionGroup" stepKey="logout"/> </after> <click selector="{{AdminMenuSection.stores}}" stepKey="clickStoresMenuOption1"/> diff --git a/app/code/Magento/Backend/Test/Mftf/Test/AdminPersistentShoppingCartSettingsTest.xml b/app/code/Magento/Backend/Test/Mftf/Test/AdminPersistentShoppingCartSettingsTest.xml new file mode 100644 index 0000000000000..cf1ed2d63034f --- /dev/null +++ b/app/code/Magento/Backend/Test/Mftf/Test/AdminPersistentShoppingCartSettingsTest.xml @@ -0,0 +1,34 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + /** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +--> + +<tests xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/testSchema.xsd"> + <test name="AdminPersistentShoppingCartSettingsTest"> + <annotations> + <features value="Backend"/> + <stories value="Enable Persistent Shopping cart"/> + <title value="Admin should be able to manage persistent shopping cart settings"/> + <description value="Admin should be able to enable persistent shopping cart in Magento Admin backend and see additional options"/> + <group value="backend"/> + </annotations> + + <before> + <actionGroup ref="AdminLoginActionGroup" stepKey="loginAsAdmin"/> + <magentoCLI stepKey="enablePersistentShoppingCart" command="config:set persistent/options/enabled 1"/> + <magentoCLI stepKey="cacheClean" command="cache:clean config"/> + </before> + <after> + <magentoCLI stepKey="disablePersistentShoppingCart" command="config:set persistent/options/enabled 0"/> + <magentoCLI stepKey="cacheClean" command="cache:clean config"/> + <actionGroup ref="AdminLogoutActionGroup" stepKey="logout"/> + </after> + + <actionGroup ref="AdminNavigateToPersistentShoppingCartSettingsActionGroup" stepKey="navigateToPersistenceSettings"/> + <actionGroup ref="AssertAdminPersistentShoppingCartOptionsAvailableActionGroup" stepKey="assertOptions"/> + </test> +</tests> diff --git a/app/code/Magento/Backend/Test/Mftf/Test/AdminPrivacyPolicyTest.xml b/app/code/Magento/Backend/Test/Mftf/Test/AdminPrivacyPolicyTest.xml index 4f215d20a7a36..b0fbdb8b5b596 100644 --- a/app/code/Magento/Backend/Test/Mftf/Test/AdminPrivacyPolicyTest.xml +++ b/app/code/Magento/Backend/Test/Mftf/Test/AdminPrivacyPolicyTest.xml @@ -21,7 +21,7 @@ </annotations> <!-- Logging in Magento admin and checking for Privacy policy footer in dashboard --> - <actionGroup ref="LoginAsAdmin" stepKey="loginAsAdmin"/> + <actionGroup ref="AdminLoginActionGroup" stepKey="loginAsAdmin"/> <closeAdminNotification stepKey="closeAdminNotification"/> <seeLink userInput="Privacy Policy" url="https://magento.com/sites/default/files/REVISED-MAGENTO-PRIVACY-POLICY.pdf" stepKey="seePrivacyPolicyLinkDashboard"/> diff --git a/app/code/Magento/Backend/Test/Mftf/Test/AdminRedirectToAdminPanelOnLogoClickFromWizardPageTest.xml b/app/code/Magento/Backend/Test/Mftf/Test/AdminRedirectToAdminPanelOnLogoClickFromWizardPageTest.xml new file mode 100644 index 0000000000000..bf74674a2c9c8 --- /dev/null +++ b/app/code/Magento/Backend/Test/Mftf/Test/AdminRedirectToAdminPanelOnLogoClickFromWizardPageTest.xml @@ -0,0 +1,29 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + /** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +--> + +<tests xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/testSchema.xsd"> + <test name="AdminRedirectToAdminPanelOnLogoClickFromWizardPageTest"> + <annotations> + <features value="Backend"/> + <stories value="Navigate to dashboard from Setup Wizard Page"/> + <title value="Navigate to dashboard after click on logo on Setup Wizard Page"/> + <description value="Check navigate to dashboard after click on logo on Setup Wizard Page"/> + </annotations> + <before> + <actionGroup ref="AdminLoginActionGroup" stepKey="loginToAdminPanel"/> + </before> + <after> + <actionGroup ref="AdminLogoutActionGroup" stepKey="logoutFromAdmin"/> + </after> + + <actionGroup ref="AdminNavigateToSetupWizardPageActionGroup" stepKey="navigateToSetupWizardPage"/> + <actionGroup ref="AdminClickLogoActionGroup" stepKey="clickOnLogo"/> + <actionGroup ref="AssertAdminDashboardPageIsVisibleActionGroup" stepKey="checkTheDashboardPage"/> + </test> +</tests> diff --git a/app/code/Magento/Backend/Test/Mftf/Test/AdminStoresAllStoresNavigateMenuTest.xml b/app/code/Magento/Backend/Test/Mftf/Test/AdminStoresAllStoresNavigateMenuTest.xml index 7758b387e393b..97fdcdf9596a3 100644 --- a/app/code/Magento/Backend/Test/Mftf/Test/AdminStoresAllStoresNavigateMenuTest.xml +++ b/app/code/Magento/Backend/Test/Mftf/Test/AdminStoresAllStoresNavigateMenuTest.xml @@ -20,10 +20,10 @@ <group value="mtf_migrated"/> </annotations> <before> - <actionGroup ref="LoginAsAdmin" stepKey="LoginAsAdmin"/> + <actionGroup ref="AdminLoginActionGroup" stepKey="LoginAsAdmin"/> </before> <after> - <actionGroup ref="logout" stepKey="logout"/> + <actionGroup ref="AdminLogoutActionGroup" stepKey="logout"/> </after> <actionGroup ref="AdminNavigateMenuActionGroup" stepKey="navigateToStoresAllStoresPage"> <argument name="menuUiId" value="{{AdminMenuStores.dataUiId}}"/> diff --git a/app/code/Magento/Backend/Test/Mftf/Test/AdminStoresConfigurationNavigateMenuTest.xml b/app/code/Magento/Backend/Test/Mftf/Test/AdminStoresConfigurationNavigateMenuTest.xml index a54269b186ba0..39b08eeb2e42a 100644 --- a/app/code/Magento/Backend/Test/Mftf/Test/AdminStoresConfigurationNavigateMenuTest.xml +++ b/app/code/Magento/Backend/Test/Mftf/Test/AdminStoresConfigurationNavigateMenuTest.xml @@ -20,10 +20,10 @@ <group value="mtf_migrated"/> </annotations> <before> - <actionGroup ref="LoginAsAdmin" stepKey="LoginAsAdmin"/> + <actionGroup ref="AdminLoginActionGroup" stepKey="LoginAsAdmin"/> </before> <after> - <actionGroup ref="logout" stepKey="logout"/> + <actionGroup ref="AdminLogoutActionGroup" stepKey="logout"/> </after> <actionGroup ref="AdminNavigateMenuActionGroup" stepKey="navigateToStoresConfigurationPage"> <argument name="menuUiId" value="{{AdminMenuStores.dataUiId}}"/> diff --git a/app/code/Magento/Backend/Test/Mftf/Test/AdminSystemCacheManagementNavigateMenuTest.xml b/app/code/Magento/Backend/Test/Mftf/Test/AdminSystemCacheManagementNavigateMenuTest.xml index 516631c1bd166..81fef1afc282c 100644 --- a/app/code/Magento/Backend/Test/Mftf/Test/AdminSystemCacheManagementNavigateMenuTest.xml +++ b/app/code/Magento/Backend/Test/Mftf/Test/AdminSystemCacheManagementNavigateMenuTest.xml @@ -20,10 +20,10 @@ <group value="mtf_migrated"/> </annotations> <before> - <actionGroup ref="LoginAsAdmin" stepKey="LoginAsAdmin"/> + <actionGroup ref="AdminLoginActionGroup" stepKey="LoginAsAdmin"/> </before> <after> - <actionGroup ref="logout" stepKey="logout"/> + <actionGroup ref="AdminLogoutActionGroup" stepKey="logout"/> </after> <actionGroup ref="AdminNavigateMenuActionGroup" stepKey="navigateToSystemCacheManagementPage"> <argument name="menuUiId" value="{{AdminMenuSystem.dataUiId}}"/> diff --git a/app/code/Magento/Backend/Test/Mftf/Test/AdminUserLoginWithStoreCodeInUrlTest.xml b/app/code/Magento/Backend/Test/Mftf/Test/AdminUserLoginWithStoreCodeInUrlTest.xml index df5ca6d037813..e9050fa3ccd6e 100644 --- a/app/code/Magento/Backend/Test/Mftf/Test/AdminUserLoginWithStoreCodeInUrlTest.xml +++ b/app/code/Magento/Backend/Test/Mftf/Test/AdminUserLoginWithStoreCodeInUrlTest.xml @@ -26,7 +26,7 @@ <magentoCLI command="config:set {{StorefrontDisableAddStoreCodeToUrls.path}} {{StorefrontDisableAddStoreCodeToUrls.value}}" stepKey="addStoreCodeToUrlDisable"/> </after> - <actionGroup ref="LoginAsAdmin" stepKey="loginAsAdmin"/> + <actionGroup ref="AdminLoginActionGroup" stepKey="loginAsAdmin"/> <actionGroup ref="AssertAdminDashboardPageIsVisibleActionGroup" stepKey="seeDashboardPage"/> </test> </tests> diff --git a/app/code/Magento/Backend/Test/Unit/App/Area/FrontNameResolverTest.php b/app/code/Magento/Backend/Test/Unit/App/Area/FrontNameResolverTest.php index ba0b01d4055de..9483de741f169 100644 --- a/app/code/Magento/Backend/Test/Unit/App/Area/FrontNameResolverTest.php +++ b/app/code/Magento/Backend/Test/Unit/App/Area/FrontNameResolverTest.php @@ -29,7 +29,7 @@ class FrontNameResolverTest extends \PHPUnit\Framework\TestCase protected $scopeConfigMock; /** - * @var \PHPUnit_Framework_MockObject_MockObject|\Zend\Uri\Uri + * @var \PHPUnit_Framework_MockObject_MockObject|\Laminas\Uri\Uri */ protected $uri; @@ -51,7 +51,7 @@ protected function setUp() ->method('get') ->with(ConfigOptionsList::CONFIG_PATH_BACKEND_FRONTNAME) ->will($this->returnValue($this->_defaultFrontName)); - $this->uri = $this->createMock(\Zend\Uri\Uri::class); + $this->uri = $this->createMock(\Laminas\Uri\Uri::class); $this->request = $this->createMock(\Magento\Framework\App\Request\Http::class); diff --git a/app/code/Magento/Backend/Test/Unit/Controller/Adminhtml/Dashboard/TunnelTest.php b/app/code/Magento/Backend/Test/Unit/Controller/Adminhtml/Dashboard/TunnelTest.php deleted file mode 100644 index b7713078863cb..0000000000000 --- a/app/code/Magento/Backend/Test/Unit/Controller/Adminhtml/Dashboard/TunnelTest.php +++ /dev/null @@ -1,196 +0,0 @@ -<?php -/** - * Copyright © Magento, Inc. All rights reserved. - * See COPYING.txt for license details. - */ -namespace Magento\Backend\Test\Unit\Controller\Adminhtml\Dashboard; - -/** - * @SuppressWarnings(PHPMD.CouplingBetweenObjects) - */ -class TunnelTest extends \PHPUnit\Framework\TestCase -{ - /** - * @var \PHPUnit_Framework_MockObject_MockObject - */ - protected $_request; - - /** - * @var \PHPUnit_Framework_MockObject_MockObject - */ - protected $_response; - - /** - * @var \PHPUnit_Framework_MockObject_MockObject - */ - protected $_objectManager; - - /** - * @var \PHPUnit_Framework_MockObject_MockObject - */ - protected $resultRaw; - - protected function setUp() - { - $this->_request = $this->createMock(\Magento\Framework\App\Request\Http::class); - $this->_response = $this->createMock(\Magento\Framework\App\Response\Http::class); - $this->_objectManager = $this->createMock(\Magento\Framework\ObjectManagerInterface::class); - } - - protected function tearDown() - { - $this->_request = null; - $this->_response = null; - $this->_objectManager = null; - } - - public function testTunnelAction() - { - $fixture = uniqid(); - $this->_request->expects($this->at(0)) - ->method('getParam') - ->with('ga') - ->will($this->returnValue(urlencode(base64_encode(json_encode([1]))))); - $this->_request->expects($this->at(1))->method('getParam')->with('h')->will($this->returnValue($fixture)); - $tunnelResponse = $this->createMock(\Magento\Framework\App\Response\Http::class); - $httpClient = $this->createPartialMock( - \Magento\Framework\HTTP\ZendClient::class, - ['setUri', 'setParameterGet', 'setConfig', 'request', 'getHeaders'] - ); - /** @var $helper \Magento\Backend\Helper\Dashboard\Data|\PHPUnit_Framework_MockObject_MockObject */ - $helper = $this->createPartialMock(\Magento\Backend\Helper\Dashboard\Data::class, ['getChartDataHash']); - $helper->expects($this->any())->method('getChartDataHash')->will($this->returnValue($fixture)); - - $this->_objectManager->expects($this->at(0)) - ->method('get') - ->with(\Magento\Backend\Helper\Dashboard\Data::class) - ->will($this->returnValue($helper)); - $this->_objectManager->expects($this->at(1)) - ->method('create') - ->with(\Magento\Framework\HTTP\ZendClient::class) - ->will($this->returnValue($httpClient)); - $httpClient->expects($this->once())->method('setUri')->will($this->returnValue($httpClient)); - $httpClient->expects($this->once())->method('setParameterGet')->will($this->returnValue($httpClient)); - $httpClient->expects($this->once())->method('setConfig')->will($this->returnValue($httpClient)); - $httpClient->expects($this->once())->method('request')->with('GET')->will($this->returnValue($tunnelResponse)); - $tunnelResponse->expects($this->any())->method('getHeaders') - ->will($this->returnValue(['Content-type' => 'test_header'])); - $tunnelResponse->expects($this->any())->method('getBody')->will($this->returnValue('success_msg')); - $this->_response->expects($this->any())->method('getBody')->will($this->returnValue('success_msg')); - - $controller = $this->_factory($this->_request, $this->_response); - $this->resultRaw->expects($this->once()) - ->method('setHeader') - ->with('Content-type', 'test_header') - ->willReturnSelf(); - $this->resultRaw->expects($this->once()) - ->method('setContents') - ->with('success_msg') - ->willReturnSelf(); - - $controller->execute(); - $this->assertEquals('success_msg', $controller->getResponse()->getBody()); - } - - public function testTunnelAction400() - { - $controller = $this->_factory($this->_request, $this->_response); - - $this->resultRaw->expects($this->once()) - ->method('setHeader') - ->willReturnSelf(); - $this->resultRaw->expects($this->once()) - ->method('setHttpResponseCode') - ->with(400) - ->willReturnSelf(); - $this->resultRaw->expects($this->once()) - ->method('setContents') - ->with('Service unavailable: invalid request') - ->willReturnSelf(); - - $controller->execute(); - } - - public function testTunnelAction503() - { - $fixture = uniqid(); - $this->_request->expects($this->at(0)) - ->method('getParam') - ->with('ga') - ->will($this->returnValue(urlencode(base64_encode(json_encode([1]))))); - $this->_request->expects($this->at(1))->method('getParam')->with('h')->will($this->returnValue($fixture)); - /** @var $helper \Magento\Backend\Helper\Dashboard\Data|\PHPUnit_Framework_MockObject_MockObject */ - $helper = $this->createPartialMock(\Magento\Backend\Helper\Dashboard\Data::class, ['getChartDataHash']); - $helper->expects($this->any())->method('getChartDataHash')->will($this->returnValue($fixture)); - - $this->_objectManager->expects($this->at(0)) - ->method('get') - ->with(\Magento\Backend\Helper\Dashboard\Data::class) - ->will($this->returnValue($helper)); - $exceptionMock = new \Exception(); - $this->_objectManager->expects($this->at(1)) - ->method('create') - ->with(\Magento\Framework\HTTP\ZendClient::class) - ->will($this->throwException($exceptionMock)); - $loggerMock = $this->createMock(\Psr\Log\LoggerInterface::class); - $loggerMock->expects($this->once())->method('critical')->with($exceptionMock); - $this->_objectManager->expects($this->at(2)) - ->method('get') - ->with(\Psr\Log\LoggerInterface::class) - ->will($this->returnValue($loggerMock)); - - $controller = $this->_factory($this->_request, $this->_response); - - $this->resultRaw->expects($this->once()) - ->method('setHeader') - ->willReturnSelf(); - $this->resultRaw->expects($this->once()) - ->method('setHttpResponseCode') - ->with(503) - ->willReturnSelf(); - $this->resultRaw->expects($this->once()) - ->method('setContents') - ->with('Service unavailable: see error log for details') - ->willReturnSelf(); - - $controller->execute(); - } - - /** - * Create the tested object - * - * @param \Magento\Framework\App\Request\Http $request - * @param \Magento\Framework\App\Response\Http|null $response - * @return \Magento\Backend\Controller\Adminhtml\Dashboard|\PHPUnit_Framework_MockObject_MockObject - */ - protected function _factory($request, $response = null) - { - if (!$response) { - /** @var $response \Magento\Framework\App\ResponseInterface|\PHPUnit_Framework_MockObject_MockObject */ - $response = $this->createMock(\Magento\Framework\App\Response\Http::class); - $response->headersSentThrowsException = false; - } - $helper = new \Magento\Framework\TestFramework\Unit\Helper\ObjectManager($this); - $varienFront = $helper->getObject(\Magento\Framework\App\FrontController::class); - - $arguments = [ - 'request' => $request, - 'response' => $response, - 'objectManager' => $this->_objectManager, - 'frontController' => $varienFront, - ]; - $this->resultRaw = $this->getMockBuilder(\Magento\Framework\Controller\Result\Raw::class) - ->disableOriginalConstructor() - ->getMock(); - - $resultRawFactory = $this->getMockBuilder(\Magento\Framework\Controller\Result\RawFactory::class) - ->disableOriginalConstructor() - ->setMethods(['create']) - ->getMock(); - $resultRawFactory->expects($this->atLeastOnce()) - ->method('create') - ->willReturn($this->resultRaw); - $context = $helper->getObject(\Magento\Backend\App\Action\Context::class, $arguments); - return new \Magento\Backend\Controller\Adminhtml\Dashboard\Tunnel($context, $resultRawFactory); - } -} diff --git a/app/code/Magento/Backend/Test/Unit/Helper/Dashboard/DataTest.php b/app/code/Magento/Backend/Test/Unit/Helper/Dashboard/DataTest.php index 21c72cb6b4477..7943c5e7fdcb1 100644 --- a/app/code/Magento/Backend/Test/Unit/Helper/Dashboard/DataTest.php +++ b/app/code/Magento/Backend/Test/Unit/Helper/Dashboard/DataTest.php @@ -3,7 +3,6 @@ * Copyright © Magento, Inc. All rights reserved. * See COPYING.txt for license details. */ - declare(strict_types=1); namespace Magento\Backend\Test\Unit\Helper\Dashboard; @@ -31,7 +30,7 @@ class DataTest extends TestCase private const STUB_CHART_DATA_HASH = '52870842b23068a78220e01eb9d4404d'; /** - * @var \Magento\Backend\Helper\Dashboard\Data + * @var HelperData */ private $helper; @@ -54,7 +53,7 @@ protected function setUp() $this->deploymentConfigMock = $this->createMock(DeploymentConfig::class); $this->deploymentConfigMock->expects($this->once())->method('get') ->with(ConfigOptionsListConstants::CONFIG_PATH_INSTALL_DATE) - ->will($this->returnValue(self::STUB_PATH_INSTALL)); + ->willReturn(self::STUB_PATH_INSTALL); $objectManager = new ObjectManager($this); $this->helper = $objectManager->getObject( @@ -81,23 +80,6 @@ public function testGetStoresWhenStoreAttributeIsNull() $this->assertEquals($storeCollectionMock, $this->helper->getStores()); } - /** - * Test getDatePeriods() method - */ - public function testGetDatePeriods() - { - $this->assertEquals( - [ - '24h' => (string)__('Last 24 Hours'), - '7d' => (string)__('Last 7 Days'), - '1m' => (string)__('Current Month'), - '1y' => (string)__('YTD'), - '2y' => (string)__('2YTD') - ], - $this->helper->getDatePeriods() - ); - } - /** * Test getChartDataHash() method */ diff --git a/app/code/Magento/Backend/Test/Unit/Model/Dashboard/ChartTest.php b/app/code/Magento/Backend/Test/Unit/Model/Dashboard/ChartTest.php new file mode 100644 index 0000000000000..fadd9e170e0b0 --- /dev/null +++ b/app/code/Magento/Backend/Test/Unit/Model/Dashboard/ChartTest.php @@ -0,0 +1,203 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +declare(strict_types=1); + +namespace Magento\Backend\Test\Unit\Model\Dashboard; + +use Magento\Backend\Helper\Dashboard\Order as OrderHelper; +use Magento\Backend\Model\Dashboard\Chart; +use Magento\Backend\Model\Dashboard\Chart\Date as DateRetriever; +use Magento\Backend\Model\Dashboard\Period; +use Magento\Framework\DataObject; +use Magento\Framework\TestFramework\Unit\Helper\ObjectManager; +use Magento\Reports\Model\ResourceModel\Order\Collection; +use PHPUnit\Framework\MockObject\MockObject; +use PHPUnit\Framework\TestCase; + +class ChartTest extends TestCase +{ + /** + * @var Chart + */ + private $model; + + /** + * @var ObjectManager + */ + private $objectManagerHelper; + + /** + * @var DateRetriever|MockObject + */ + private $dateRetrieverMock; + + /** + * @var OrderHelper|MockObject + */ + private $orderHelperMock; + + /** + * @var Collection|MockObject + */ + private $collectionMock; + + protected function setUp() + { + $this->objectManagerHelper = new ObjectManager($this); + + $this->dateRetrieverMock = $this->getMockBuilder(DateRetriever::class) + ->disableOriginalConstructor() + ->getMock(); + + $this->orderHelperMock = $this->getMockBuilder(OrderHelper::class) + ->disableOriginalConstructor() + ->getMock(); + + $this->collectionMock = $this->getMockBuilder(Collection::class) + ->disableOriginalConstructor() + ->getMock(); + $this->orderHelperMock->method('getCollection') + ->willReturn($this->collectionMock); + + $period = $this->objectManagerHelper->getObject(Period::class); + + $this->model = $this->objectManagerHelper->getObject( + Chart::class, + [ + 'dateRetriever' => $this->dateRetrieverMock, + 'orderHelper' => $this->orderHelperMock, + 'period' => $period + ] + ); + } + + /** + * @param string $period + * @param string $chartParam + * @param array $result + * @dataProvider getByPeriodDataProvider + */ + public function testGetByPeriod($period, $chartParam, $result) + { + $this->orderHelperMock->expects($this->at(0)) + ->method('setParam') + ->with('store', null); + $this->orderHelperMock->expects($this->at(1)) + ->method('setParam') + ->with('website', null); + $this->orderHelperMock->expects($this->at(2)) + ->method('setParam') + ->with('group', null); + $this->orderHelperMock->expects($this->at(3)) + ->method('setParam') + ->with('period', $period); + + $this->dateRetrieverMock->expects($this->once()) + ->method('getByPeriod') + ->with($period) + ->willReturn(array_map(static function ($item) { + return $item['x']; + }, $result)); + + $this->collectionMock->method('count') + ->willReturn(2); + + $valueMap = []; + foreach ($result as $resultItem) { + $dataObjectMock = $this->getMockBuilder(DataObject::class) + ->disableOriginalConstructor() + ->getMock(); + $dataObjectMock->method('getData') + ->with($chartParam) + ->willReturn($resultItem['y']); + + $valueMap[] = [ + 'range', + $resultItem['x'], + $dataObjectMock + ]; + } + $this->collectionMock->method('getItemByColumnValue') + ->willReturnMap($valueMap); + + $this->assertEquals( + $result, + $this->model->getByPeriod($period, $chartParam) + ); + } + + public function getByPeriodDataProvider(): array + { + return [ + [ + Period::PERIOD_7_DAYS, + 'revenue', + [ + [ + 'x' => '2020-01-21', + 'y' => 0 + ], + [ + 'x' => '2020-01-22', + 'y' => 2 + ], + [ + 'x' => '2020-01-23', + 'y' => 0 + ], + [ + 'x' => '2020-01-24', + 'y' => 7 + ] + ] + ], + [ + Period::PERIOD_1_MONTH, + 'quantity', + [ + [ + 'x' => '2020-01-21', + 'y' => 0 + ], + [ + 'x' => '2020-01-22', + 'y' => 2 + ], + [ + 'x' => '2020-01-23', + 'y' => 0 + ], + [ + 'x' => '2020-01-24', + 'y' => 7 + ] + ] + ], + [ + Period::PERIOD_1_YEAR, + 'quantity', + [ + [ + 'x' => '2020-01', + 'y' => 0 + ], + [ + 'x' => '2020-02', + 'y' => 2 + ], + [ + 'x' => '2020-03', + 'y' => 0 + ], + [ + 'x' => '2020-04', + 'y' => 7 + ] + ] + ] + ]; + } +} diff --git a/app/code/Magento/Backend/Test/Unit/Model/Dashboard/PeriodTest.php b/app/code/Magento/Backend/Test/Unit/Model/Dashboard/PeriodTest.php new file mode 100644 index 0000000000000..5c71afdde3109 --- /dev/null +++ b/app/code/Magento/Backend/Test/Unit/Model/Dashboard/PeriodTest.php @@ -0,0 +1,49 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +declare(strict_types=1); + +namespace Magento\Backend\Test\Unit\Model\Dashboard; + +use Magento\Backend\Model\Dashboard\Period; +use Magento\Framework\TestFramework\Unit\Helper\ObjectManager; +use PHPUnit\Framework\TestCase; + +class PeriodTest extends TestCase +{ + /** + * @var Period + */ + private $model; + + /** + * @inheritDoc + */ + protected function setUp() + { + $objectManager = new ObjectManager($this); + $this->model = $objectManager->getObject( + Period::class, + [] + ); + } + + /** + * Test getDatePeriods() method + */ + public function testGetDatePeriods() + { + $this->assertEquals( + [ + Period::PERIOD_24_HOURS => (string)__('Last 24 Hours'), + Period::PERIOD_7_DAYS => (string)__('Last 7 Days'), + Period::PERIOD_1_MONTH => (string)__('Current Month'), + Period::PERIOD_1_YEAR => (string)__('YTD'), + Period::PERIOD_2_YEARS => (string)__('2YTD') + ], + $this->model->getDatePeriods() + ); + } +} diff --git a/app/code/Magento/Backend/Test/Unit/Model/UrlTest.php b/app/code/Magento/Backend/Test/Unit/Model/UrlTest.php index ad42108cb5eea..1ef3b3980441e 100644 --- a/app/code/Magento/Backend/Test/Unit/Model/UrlTest.php +++ b/app/code/Magento/Backend/Test/Unit/Model/UrlTest.php @@ -190,7 +190,7 @@ public function testFindFirstAvailableMenuDenied() $this->_menuMock->expects($this->any())->method('getFirstAvailableChild')->will($this->returnValue(null)); - $this->assertEquals('*/*/denied', $this->_model->findFirstAvailableMenu()); + $this->assertEquals('*/denied', $this->_model->findFirstAvailableMenu()); } public function testFindFirstAvailableMenu() diff --git a/app/code/Magento/Backend/ViewModel/ChartDisabled.php b/app/code/Magento/Backend/ViewModel/ChartDisabled.php new file mode 100644 index 0000000000000..f71a2d26441ef --- /dev/null +++ b/app/code/Magento/Backend/ViewModel/ChartDisabled.php @@ -0,0 +1,77 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +declare(strict_types=1); + +namespace Magento\Backend\ViewModel; + +use Magento\Framework\App\Config\ScopeConfigInterface; +use Magento\Framework\UrlInterface; +use Magento\Framework\View\Element\Block\ArgumentInterface; +use Magento\Store\Model\ScopeInterface; + +/** + * View model for dashboard chart disabled notice + */ +class ChartDisabled implements ArgumentInterface +{ + /** + * Location of the "Enable Chart" config param + */ + private const XML_PATH_ENABLE_CHARTS = 'admin/dashboard/enable_charts'; + + /** + * Route to Stores -> Configuration section + */ + private const ROUTE_SYSTEM_CONFIG = 'adminhtml/system_config/edit'; + + /** + * @var UrlInterface + */ + private $urlBuilder; + + /** + * @var ScopeConfigInterface + */ + private $scopeConfig; + + /** + * @param UrlInterface $urlBuilder + * @param ScopeConfigInterface $scopeConfig + */ + public function __construct( + UrlInterface $urlBuilder, + ScopeConfigInterface $scopeConfig + ) { + $this->urlBuilder = $urlBuilder; + $this->scopeConfig = $scopeConfig; + } + + /** + * Get url to dashboard chart configuration + * + * @return string + */ + public function getConfigUrl(): string + { + return $this->urlBuilder->getUrl( + static::ROUTE_SYSTEM_CONFIG, + ['section' => 'admin', '_fragment' => 'admin_dashboard-link'] + ); + } + + /** + * Check if dashboard chart is enabled + * + * @return bool + */ + public function isChartEnabled(): bool + { + return $this->scopeConfig->isSetFlag( + static::XML_PATH_ENABLE_CHARTS, + ScopeInterface::SCOPE_STORE + ); + } +} diff --git a/app/code/Magento/Backend/ViewModel/ChartsPeriod.php b/app/code/Magento/Backend/ViewModel/ChartsPeriod.php new file mode 100644 index 0000000000000..effce0dc60585 --- /dev/null +++ b/app/code/Magento/Backend/ViewModel/ChartsPeriod.php @@ -0,0 +1,60 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +declare(strict_types=1); + +namespace Magento\Backend\ViewModel; + +use Magento\Backend\Model\Dashboard\Period; +use Magento\Framework\Serialize\Serializer\Json; +use Magento\Framework\View\Element\Block\ArgumentInterface; + +/** + * View model for dashboard charts period select + */ +class ChartsPeriod implements ArgumentInterface +{ + /** + * @var Period + */ + private $period; + + /** + * @var Json + */ + private $serializer; + + /** + * @param Period $period + * @param Json $serializer + */ + public function __construct( + Period $period, + Json $serializer + ) { + $this->period = $period; + $this->serializer = $serializer; + } + + /** + * Get chart date periods + * + * @return array + */ + public function getDatePeriods(): array + { + return $this->period->getDatePeriods(); + } + + /** + * Get json-encoded chart period units + * + * @return string + */ + public function getPeriodUnits(): string + { + return $this->serializer->serialize($this->period->getPeriodChartUnits()); + } +} diff --git a/app/code/Magento/Backend/etc/adminhtml/di.xml b/app/code/Magento/Backend/etc/adminhtml/di.xml index 5f566396ab500..1bfc504cf50e9 100644 --- a/app/code/Magento/Backend/etc/adminhtml/di.xml +++ b/app/code/Magento/Backend/etc/adminhtml/di.xml @@ -60,8 +60,9 @@ </arguments> </type> <type name="Magento\Backend\App\AbstractAction"> - <plugin name="adminAuthentication" type="Magento\Backend\App\Action\Plugin\Authentication" sortOrder="100" /> - <plugin name="adminMassactionKey" type="Magento\Backend\App\Action\Plugin\MassactionKey" sortOrder="11" /> + <plugin name="adminAuthentication" type="Magento\Backend\App\Action\Plugin\Authentication" sortOrder="100"/> + <plugin name="adminMassactionKey" type="Magento\Backend\App\Action\Plugin\MassactionKey" sortOrder="11"/> + <plugin name="adminLoadDesign" type="Magento\Backend\App\Action\Plugin\LoadDesignPlugin" sortOrder="101"/> </type> <type name="Magento\Store\App\Response\Redirect"> <arguments> diff --git a/app/code/Magento/Backend/etc/adminhtml/system.xml b/app/code/Magento/Backend/etc/adminhtml/system.xml index 4a92ed8124bf8..1e8ec226d6b88 100644 --- a/app/code/Magento/Backend/etc/adminhtml/system.xml +++ b/app/code/Magento/Backend/etc/adminhtml/system.xml @@ -20,7 +20,7 @@ @deprecated Magento does not support custom disabling/enabling modules output since 2.2.0 version. Section 'Advanced' was disabled. This section will be removed from code in one release. --> - <section id="advanced" translate="label" type="text" sortOrder="910" showInDefault="0" showInWebsite="0" showInStore="0"> + <section id="advanced" translate="label" type="text" sortOrder="910"> <label>Advanced</label> <tab>advanced</tab> <resource>Magento_Config::advanced</resource> @@ -115,7 +115,7 @@ <label>Enable Template Path Hints for Storefront</label> <source_model>Magento\Config\Model\Config\Source\Yesno</source_model> </field> - <field id="template_hints_storefront_show_with_parameter" translate="label" type="select" sortOrder="20" showInDefault="1" showInWebsite="1" showInStore="1"> + <field id="template_hints_storefront_show_with_parameter" translate="label comment" type="select" sortOrder="20" showInDefault="1" showInWebsite="1" showInStore="1"> <label>Enable Hints for Storefront with URL Parameter</label> <depends> <field id="*/*/template_hints_storefront">1</field> @@ -123,7 +123,7 @@ <source_model>Magento\Config\Model\Config\Source\Yesno</source_model> <comment>Use URL parameter to enable template path hints for Storefront</comment> </field> - <field id="template_hints_parameter_value" translate="label" type="text" sortOrder="20" showInDefault="1" showInWebsite="1" showInStore="1" canRestore="1"> + <field id="template_hints_parameter_value" translate="label comment" type="text" sortOrder="20" showInDefault="1" showInWebsite="1" showInStore="1" canRestore="1"> <label>Parameter Value</label> <depends> <field id="*/*/template_hints_storefront">1</field> @@ -131,7 +131,7 @@ </depends> <comment>Add the following parameter to the URL to show template hints ?templatehints=[parameter_value]</comment> </field> - <field id="template_hints_admin" translate="label" type="select" sortOrder="20" showInDefault="1" showInWebsite="0" showInStore="0"> + <field id="template_hints_admin" translate="label" type="select" sortOrder="20" showInDefault="1"> <label>Enable Template Path Hints for Admin</label> <source_model>Magento\Config\Model\Config\Source\Yesno</source_model> </field> @@ -162,7 +162,7 @@ <source_model>Magento\Config\Model\Config\Source\Yesno</source_model> <backend_model>Magento\Config\Model\Config\Backend\Translate</backend_model> </field> - <field id="active_admin" translate="label comment" type="select" sortOrder="20" showInDefault="1" showInWebsite="0" showInStore="0"> + <field id="active_admin" translate="label comment" type="select" sortOrder="20" showInDefault="1"> <label>Enabled for Admin</label> <source_model>Magento\Config\Model\Config\Source\Yesno</source_model> <backend_model>Magento\Config\Model\Config\Backend\Translate</backend_model> @@ -197,18 +197,18 @@ <comment>Minification is not applied in developer mode.</comment> </field> </group> - <group id="image" translate="label" type="text" sortOrder="120" showInDefault="1" showInWebsite="0" showInStore="0"> + <group id="image" translate="label" type="text" sortOrder="120" showInDefault="1"> <label>Image Processing Settings</label> - <field id="default_adapter" translate="label comment" type="select" sortOrder="10" showInDefault="1" showInWebsite="0" showInStore="0" canRestore="1"> + <field id="default_adapter" translate="label comment" type="select" sortOrder="10" showInDefault="1" canRestore="1"> <label>Image Adapter</label> <source_model>Magento\Config\Model\Config\Source\Image\Adapter</source_model> <backend_model>Magento\Config\Model\Config\Backend\Image\Adapter</backend_model> <comment>When the adapter was changed, please flush Catalog Images Cache.</comment> </field> </group> - <group id="static" translate="label" type="text" sortOrder="130" showInDefault="1" showInWebsite="0" showInStore="0"> + <group id="static" translate="label" type="text" sortOrder="130" showInDefault="1"> <label>Static Files Settings</label> - <field id="sign" translate="label" type="select" sortOrder="10" showInDefault="1" showInWebsite="0" showInStore="0"> + <field id="sign" translate="label" type="select" sortOrder="10" showInDefault="1" canRestore="1"> <label>Sign Static Files</label> <source_model>Magento\Config\Model\Config\Source\Yesno</source_model> </field> @@ -220,7 +220,7 @@ <resource>Magento_Config::config_general</resource> <group id="country" translate="label" type="text" sortOrder="1" showInDefault="1" showInWebsite="1" showInStore="1"> <label>Country Options</label> - <field id="allow" translate="label" type="multiselect" sortOrder="2" showInDefault="1" showInWebsite="1" showInStore="0" canRestore="1"> + <field id="allow" translate="label" type="multiselect" sortOrder="2" showInDefault="1" showInWebsite="1" canRestore="1"> <label>Allow Countries</label> <source_model>Magento\Directory\Model\Config\Source\Country</source_model> <can_be_empty>1</can_be_empty> @@ -229,7 +229,7 @@ <label>Default Country</label> <source_model>Magento\Directory\Model\Config\Source\Country</source_model> </field> - <field id="eu_countries" translate="label" type="multiselect" sortOrder="30" showInDefault="1" showInWebsite="0" showInStore="0" canRestore="1"> + <field id="eu_countries" translate="label" type="multiselect" sortOrder="30" showInDefault="1" canRestore="1"> <label>European Union Countries</label> <source_model>Magento\Directory\Model\Config\Source\Country</source_model> </field> @@ -241,7 +241,7 @@ </group> <group id="locale" translate="label" type="text" sortOrder="8" showInDefault="1" showInWebsite="1" showInStore="1"> <label>Locale Options</label> - <field id="timezone" translate="label" type="select" sortOrder="1" showInDefault="1" showInWebsite="1" showInStore="0"> + <field id="timezone" translate="label" type="select" sortOrder="1" showInDefault="1" showInWebsite="1"> <label>Timezone</label> <source_model>Magento\Config\Model\Config\Source\Locale\Timezone</source_model> <backend_model>Magento\Config\Model\Config\Backend\Locale\Timezone</backend_model> @@ -271,35 +271,35 @@ <field id="hours" translate="label" type="text" sortOrder="22" showInDefault="1" showInWebsite="1" showInStore="1"> <label>Store Hours of Operation</label> </field> - <field id="country_id" translate="label" type="select" sortOrder="25" showInDefault="1" showInWebsite="1" showInStore="0"> + <field id="country_id" translate="label" type="select" sortOrder="25" showInDefault="1" showInWebsite="1"> <label>Country</label> <source_model>Magento\Directory\Model\Config\Source\Country</source_model> <frontend_class>countries</frontend_class> <can_be_empty>1</can_be_empty> </field> - <field id="region_id" translate="label" type="text" sortOrder="27" showInDefault="1" showInWebsite="1" showInStore="0"> + <field id="region_id" translate="label" type="text" sortOrder="27" showInDefault="1" showInWebsite="1"> <label>Region/State</label> </field> - <field id="postcode" translate="label" type="text" sortOrder="30" showInDefault="1" showInWebsite="1" showInStore="0"> + <field id="postcode" translate="label" type="text" sortOrder="30" showInDefault="1" showInWebsite="1"> <label>ZIP/Postal Code</label> </field> - <field id="city" translate="label" type="text" sortOrder="45" showInDefault="1" showInWebsite="1" showInStore="0"> + <field id="city" translate="label" type="text" sortOrder="45" showInDefault="1" showInWebsite="1"> <label>City</label> </field> - <field id="street_line1" translate="label" type="text" sortOrder="55" showInDefault="1" showInWebsite="1" showInStore="0"> + <field id="street_line1" translate="label" type="text" sortOrder="55" showInDefault="1" showInWebsite="1"> <label>Street Address</label> </field> - <field id="street_line2" translate="label" type="text" sortOrder="60" showInDefault="1" showInWebsite="1" showInStore="0"> + <field id="street_line2" translate="label" type="text" sortOrder="60" showInDefault="1" showInWebsite="1"> <label>Street Address Line 2</label> </field> - <field id="merchant_vat_number" translate="label" type="text" sortOrder="61" showInDefault="1" showInWebsite="1" showInStore="0"> + <field id="merchant_vat_number" translate="label" type="text" sortOrder="61" showInDefault="1" showInWebsite="1"> <label>VAT Number</label> <can_be_empty>1</can_be_empty> </field> </group> - <group id="single_store_mode" translate="label" type="text" sortOrder="150" showInDefault="1" showInWebsite="0" showInStore="0"> + <group id="single_store_mode" translate="label" type="text" sortOrder="150" showInDefault="1"> <label>Single-Store Mode</label> - <field id="enabled" translate="label comment" type="select" sortOrder="10" showInDefault="1" showInWebsite="0" showInStore="0"> + <field id="enabled" translate="label comment" type="select" sortOrder="10" showInDefault="1"> <label>Enable Single-Store Mode</label> <source_model>Magento\Config\Model\Config\Source\Yesno</source_model> <comment>This setting will not be taken into account if system has more than one store view.</comment> @@ -326,7 +326,7 @@ <validate>validate-digits validate-digits-range digits-range-0-65535</validate> <comment>Please enter at least 0 and at most 65535 (For Windows server only).</comment> </field> - <field id="set_return_path" translate="label" type="select" sortOrder="70" showInDefault="1" showInWebsite="1" showInStore="1"> + <field id="set_return_path" translate="label" type="select" sortOrder="70" showInDefault="1" showInWebsite="1" showInStore="1" canRestore="1"> <label>Set Return-Path</label> <source_model>Magento\Config\Model\Config\Source\Yesnocustom</source_model> </field> @@ -341,12 +341,12 @@ </group> <group id="upload_configuration" translate="label" type="text" sortOrder="1000" showInDefault="1" showInWebsite="1" showInStore="1"> <label>Images Upload Configuration</label> - <field id="enable_resize" translate="label" type="select" sortOrder="200" showInDefault="1" showInWebsite="0" showInStore="0" canRestore="1"> + <field id="enable_resize" translate="label" type="select" sortOrder="200" showInDefault="1" canRestore="1"> <label>Enable Frontend Resize</label> <source_model>Magento\Config\Model\Config\Source\Yesno</source_model> <comment>Resize performed via javascript before file upload.</comment> </field> - <field id="max_width" translate="label comment" type="text" sortOrder="300" showInDefault="1" showInWebsite="0" showInStore="0" canRestore="1"> + <field id="max_width" translate="label comment" type="text" sortOrder="300" showInDefault="1" canRestore="1"> <label>Maximum Width</label> <validate>validate-greater-than-zero validate-number required-entry</validate> <comment>Maximum allowed width for uploaded image.</comment> @@ -354,7 +354,7 @@ <field id="enable_resize">1</field> </depends> </field> - <field id="max_height" translate="label comment" type="text" sortOrder="400" showInDefault="1" showInWebsite="0" showInStore="0" canRestore="1"> + <field id="max_height" translate="label comment" type="text" sortOrder="400" showInDefault="1" canRestore="1"> <label>Maximum Height</label> <validate>validate-greater-than-zero validate-number required-entry</validate> <comment>Maximum allowed height for uploaded image.</comment> @@ -364,37 +364,37 @@ </field> </group> </section> - <section id="admin" translate="label" type="text" sortOrder="20" showInDefault="1" showInWebsite="0" showInStore="0"> + <section id="admin" translate="label" type="text" sortOrder="20" showInDefault="1"> <label>Admin</label> <tab>advanced</tab> <resource>Magento_Config::config_admin</resource> - <group id="emails" translate="label" type="text" sortOrder="10" showInDefault="1" showInWebsite="0" showInStore="0"> + <group id="emails" translate="label" type="text" sortOrder="10" showInDefault="1"> <label>Admin User Emails</label> - <field id="forgot_email_template" translate="label comment" type="select" sortOrder="10" showInDefault="1" showInWebsite="0" showInStore="0" canRestore="1"> + <field id="forgot_email_template" translate="label comment" type="select" sortOrder="10" showInDefault="1" canRestore="1"> <label>Forgot Password Email Template</label> <comment>Email template chosen based on theme fallback when "Default" option is selected.</comment> <source_model>Magento\Config\Model\Config\Source\Email\Template</source_model> </field> - <field id="forgot_email_identity" translate="label" type="select" sortOrder="20" showInDefault="1" showInWebsite="0" showInStore="0" canRestore="1"> + <field id="forgot_email_identity" translate="label" type="select" sortOrder="20" showInDefault="1" canRestore="1"> <label>Forgot and Reset Email Sender</label> <source_model>Magento\Config\Model\Config\Source\Email\Identity</source_model> </field> </group> - <group id="startup" translate="label" type="text" sortOrder="20" showInDefault="1" showInWebsite="0" showInStore="0"> + <group id="startup" translate="label" type="text" sortOrder="20" showInDefault="1"> <label>Startup Page</label> - <field id="menu_item_id" translate="label" type="select" sortOrder="1" showInDefault="1" showInWebsite="0" showInStore="0" canRestore="1"> + <field id="menu_item_id" translate="label" type="select" sortOrder="1" showInDefault="1" canRestore="1"> <label>Startup Page</label> <source_model>Magento\Config\Model\Config\Source\Admin\Page</source_model> </field> </group> - <group id="url" translate="label" type="text" sortOrder="30" showInDefault="1" showInWebsite="0" showInStore="0"> + <group id="url" translate="label" type="text" sortOrder="30" showInDefault="1"> <label>Admin Base URL</label> - <field id="use_custom" translate="label" type="select" sortOrder="1" showInDefault="1" showInWebsite="0" showInStore="0" canRestore="1"> + <field id="use_custom" translate="label" type="select" sortOrder="1" showInDefault="1" canRestore="1"> <label>Use Custom Admin URL</label> <source_model>Magento\Config\Model\Config\Source\Yesno</source_model> <backend_model>Magento\Config\Model\Config\Backend\Admin\Usecustom</backend_model> </field> - <field id="custom" translate="label comment" type="text" sortOrder="2" showInDefault="1" showInWebsite="0" showInStore="0" canRestore="1"> + <field id="custom" translate="label comment" type="text" sortOrder="2" showInDefault="1" canRestore="1"> <label>Custom Admin URL</label> <backend_model>Magento\Config\Model\Config\Backend\Admin\Custom</backend_model> <depends> @@ -402,12 +402,12 @@ </depends> <comment>Make sure that base URL ends with '/' (slash), e.g. http://yourdomain/magento/</comment> </field> - <field id="use_custom_path" translate="label" type="select" sortOrder="3" showInDefault="1" showInWebsite="0" showInStore="0" canRestore="1"> + <field id="use_custom_path" translate="label" type="select" sortOrder="3" showInDefault="1" canRestore="1"> <label>Use Custom Admin Path</label> <source_model>Magento\Config\Model\Config\Source\Yesno</source_model> <backend_model>Magento\Config\Model\Config\Backend\Admin\Custompath</backend_model> </field> - <field id="custom_path" translate="label comment" type="text" sortOrder="4" showInDefault="1" showInWebsite="0" showInStore="0" canRestore="1"> + <field id="custom_path" translate="label comment" type="text" sortOrder="4" showInDefault="1" canRestore="1"> <label>Custom Admin Path</label> <validate>required-entry validate-alphanum</validate> <backend_model>Magento\Config\Model\Config\Backend\Admin\Custompath</backend_model> @@ -417,33 +417,33 @@ <comment>You will have to sign in after you save your custom admin path.</comment> </field> </group> - <group id="security" translate="label" type="text" sortOrder="35" showInDefault="1" showInWebsite="0" showInStore="0"> + <group id="security" translate="label" type="text" sortOrder="35" showInDefault="1"> <label>Security</label> - <field id="password_reset_link_expiration_period" translate="label comment" type="text" sortOrder="7" showInDefault="1" showInWebsite="0" showInStore="0" canRestore="1"> + <field id="password_reset_link_expiration_period" translate="label comment" type="text" sortOrder="7" showInDefault="1" canRestore="1"> <label>Recovery Link Expiration Period (hours)</label> <comment>Please enter a number 1 or greater in this field.</comment> <validate>required-entry integer validate-greater-than-zero</validate> <backend_model>Magento\Config\Model\Config\Backend\Admin\Password\Link\Expirationperiod</backend_model> </field> - <field id="use_form_key" translate="label" type="select" sortOrder="10" showInDefault="1" showInWebsite="0" showInStore="0" canRestore="1"> + <field id="use_form_key" translate="label" type="select" sortOrder="10" showInDefault="1" canRestore="1"> <label>Add Secret Key to URLs</label> <source_model>Magento\Config\Model\Config\Source\Yesno</source_model> <backend_model>Magento\Config\Model\Config\Backend\Admin\Usesecretkey</backend_model> </field> - <field id="use_case_sensitive_login" translate="label" type="select" sortOrder="20" showInDefault="1" showInWebsite="0" showInStore="0"> + <field id="use_case_sensitive_login" translate="label" type="select" sortOrder="20" showInDefault="1" canRestore="1"> <label>Login is Case Sensitive</label> <source_model>Magento\Config\Model\Config\Source\Yesno</source_model> </field> - <field id="session_lifetime" translate="label comment" sortOrder="30" showInDefault="1" showInWebsite="0" showInStore="0" canRestore="1"> + <field id="session_lifetime" translate="label comment" sortOrder="30" showInDefault="1" canRestore="1"> <label>Admin Session Lifetime (seconds)</label> <comment>Please enter at least 60 and at most 31536000 (one year).</comment> <backend_model>Magento\Backend\Model\Config\SessionLifetime\BackendModel</backend_model> <validate>validate-digits validate-digits-range digits-range-60-31536000</validate> </field> </group> - <group id="dashboard" translate="label" sortOrder="40" showInDefault="1" showInWebsite="0" showInStore="0"> + <group id="dashboard" translate="label" sortOrder="40" showInDefault="1"> <label>Dashboard</label> - <field id="enable_charts" translate="label" type="select" sortOrder="1" showInDefault="1" showInWebsite="0" showInStore="0" canRestore="1"> + <field id="enable_charts" translate="label" type="select" sortOrder="1" showInDefault="1" canRestore="1"> <label>Enable Charts</label> <source_model>Magento\Config\Model\Config\Source\Yesno</source_model> </field> @@ -455,7 +455,7 @@ <resource>Magento_Config::web</resource> <group id="url" translate="label" type="text" sortOrder="3" showInDefault="1" showInWebsite="1" showInStore="1"> <label>Url Options</label> - <field id="use_store" translate="label comment" type="select" sortOrder="10" showInDefault="1" showInWebsite="0" showInStore="0" canRestore="1"> + <field id="use_store" translate="label comment" type="select" sortOrder="10" showInDefault="1" canRestore="1"> <label>Add Store Code to Urls</label> <source_model>Magento\Config\Model\Config\Source\Yesno</source_model> <backend_model>Magento\Config\Model\Config\Backend\Store</backend_model> @@ -529,7 +529,7 @@ <backend_model>Magento\Config\Model\Config\Backend\Secure</backend_model> <comment>Enter https protocol to use Secure URLs on Storefront.</comment> </field> - <field id="use_in_adminhtml" translate="label comment" type="select" sortOrder="60" showInDefault="1" showInWebsite="0" showInStore="0" canRestore="1"> + <field id="use_in_adminhtml" translate="label comment" type="select" sortOrder="60" showInDefault="1" canRestore="1"> <label>Use Secure URLs in Admin</label> <source_model>Magento\Config\Model\Config\Source\Yesno</source_model> <backend_model>Magento\Config\Model\Config\Backend\Secure</backend_model> @@ -555,7 +555,7 @@ <field id="use_in_adminhtml">1</field> </depends> </field> - <field id="offloader_header" translate="label" type="text" sortOrder="90" showInDefault="1" showInWebsite="0" showInStore="0" canRestore="1"> + <field id="offloader_header" translate="label" type="text" sortOrder="90" showInDefault="1" canRestore="1"> <label>Offloader header</label> </field> </group> @@ -568,21 +568,21 @@ <label>Default No-route URL</label> </field> </group> - <group id="session" translate="label" type="text" sortOrder="60" showInDefault="1" showInWebsite="1" showInStore="0"> + <group id="session" translate="label" type="text" sortOrder="60" showInDefault="1" showInWebsite="1"> <label>Session Validation Settings</label> - <field id="use_remote_addr" translate="label" type="select" sortOrder="1" showInDefault="1" showInWebsite="0" showInStore="0" canRestore="1"> + <field id="use_remote_addr" translate="label" type="select" sortOrder="1" showInDefault="1" canRestore="1"> <label>Validate REMOTE_ADDR</label> <source_model>Magento\Config\Model\Config\Source\Yesno</source_model> </field> - <field id="use_http_via" translate="label" type="select" sortOrder="20" showInDefault="1" showInWebsite="0" showInStore="0" canRestore="1"> + <field id="use_http_via" translate="label" type="select" sortOrder="20" showInDefault="1" canRestore="1"> <label>Validate HTTP_VIA</label> <source_model>Magento\Config\Model\Config\Source\Yesno</source_model> </field> - <field id="use_http_x_forwarded_for" translate="label" type="select" sortOrder="30" showInDefault="1" showInWebsite="0" showInStore="0" canRestore="1"> + <field id="use_http_x_forwarded_for" translate="label" type="select" sortOrder="30" showInDefault="1" canRestore="1"> <label>Validate HTTP_X_FORWARDED_FOR</label> <source_model>Magento\Config\Model\Config\Source\Yesno</source_model> </field> - <field id="use_http_user_agent" translate="label" type="select" sortOrder="40" showInDefault="1" showInWebsite="0" showInStore="0" canRestore="1"> + <field id="use_http_user_agent" translate="label" type="select" sortOrder="40" showInDefault="1" canRestore="1"> <label>Validate HTTP_USER_AGENT</label> <source_model>Magento\Config\Model\Config\Source\Yesno</source_model> </field> diff --git a/app/code/Magento/Backend/etc/config.xml b/app/code/Magento/Backend/etc/config.xml index 8283fa18dd370..6c568370d9610 100644 --- a/app/code/Magento/Backend/etc/config.xml +++ b/app/code/Magento/Backend/etc/config.xml @@ -11,9 +11,6 @@ <template> <minify_html>0</minify_html> </template> - <static> - <sign>1</sign> - </static> </dev> <system> <media_storage_configuration> diff --git a/app/code/Magento/Backend/i18n/en_US.csv b/app/code/Magento/Backend/i18n/en_US.csv index 51fe8bfe542a2..53f7fe90cbbe5 100644 --- a/app/code/Magento/Backend/i18n/en_US.csv +++ b/app/code/Magento/Backend/i18n/en_US.csv @@ -461,3 +461,5 @@ Pagination,Pagination "Alternative text for the next pages link in the pagination menu. If empty, default arrow image is used.","Alternative text for the next pages link in the pagination menu. If empty, default arrow image is used." "Anchor Text for Next","Anchor Text for Next" "Theme Name","Theme Name" +"Use URL parameter to enable template path hints for Storefront","Use URL parameter to enable template path hints for Storefront" +"Add the following parameter to the URL to show template hints ?templatehints=[parameter_value]","Add the following parameter to the URL to show template hints ?templatehints=[parameter_value]" diff --git a/app/code/Magento/Backend/registration.php b/app/code/Magento/Backend/registration.php index a7a5a58ca0d9c..8f0c19af6e89a 100644 --- a/app/code/Magento/Backend/registration.php +++ b/app/code/Magento/Backend/registration.php @@ -4,6 +4,6 @@ * See COPYING.txt for license details. */ -use \Magento\Framework\Component\ComponentRegistrar; +use Magento\Framework\Component\ComponentRegistrar; ComponentRegistrar::register(ComponentRegistrar::MODULE, 'Magento_Backend', __DIR__); diff --git a/app/code/Magento/Backend/view/adminhtml/layout/adminhtml_cache_block.xml b/app/code/Magento/Backend/view/adminhtml/layout/adminhtml_cache_block.xml index 6d2ecd8d36a99..0fbf777bdf607 100644 --- a/app/code/Magento/Backend/view/adminhtml/layout/adminhtml_cache_block.xml +++ b/app/code/Magento/Backend/view/adminhtml/layout/adminhtml_cache_block.xml @@ -80,6 +80,7 @@ <argument name="type" xsi:type="string">options</argument> <argument name="width" xsi:type="string">120</argument> <argument name="align" xsi:type="string">left</argument> + <argument name="sortable" xsi:type="string">0</argument> <argument name="options" xsi:type="array"> <item name="disabled" xsi:type="array"> <item name="value" xsi:type="string">0</item> diff --git a/app/code/Magento/Backend/view/adminhtml/layout/adminhtml_dashboard_index.xml b/app/code/Magento/Backend/view/adminhtml/layout/adminhtml_dashboard_index.xml index 803eb1dbe903b..4a255134ab062 100644 --- a/app/code/Magento/Backend/view/adminhtml/layout/adminhtml_dashboard_index.xml +++ b/app/code/Magento/Backend/view/adminhtml/layout/adminhtml_dashboard_index.xml @@ -17,7 +17,68 @@ <block class="Magento\Backend\Block\Template" name="refresh_statistics" after="store_switcher" template="Magento_Backend::dashboard/totalbar/refreshstatistics.phtml"/> </referenceContainer> <referenceContainer name="content"> - <block class="Magento\Backend\Block\Dashboard" name="dashboard"/> + <block name="dashboard" template="Magento_Backend::dashboard/index.phtml"> + <block class="Magento\Backend\Block\Dashboard\Orders\Grid" name="dashboard.lastOrders" as="lastOrders"/> + <block class="Magento\Backend\Block\Dashboard\Totals" name="dashboard.totals" as="totals"> + <block template="Magento_Backend::dashboard/totalbar/script.phtml" name="dashboard.totals.script"/> + </block> + <block class="Magento\Backend\Block\Dashboard\Sales" name="dashboard.sales" as="sales"/> + <block class="Magento\Backend\Block\Dashboard\Grids" name="dashboard.grids" as="grids"/> + <block name="dashboard.chart.disabled" + as="chartDisabled" + template="Magento_Backend::dashboard/chart/disabled.phtml"> + <arguments> + <argument name="view_model" xsi:type="object">Magento\Backend\ViewModel\ChartDisabled</argument> + </arguments> + </block> + <block class="Magento\Backend\Block\Widget\Tabs" + name="dashboard.diagrams" + as="diagrams" + template="Magento_Backend::widget/tabshoriz.phtml" + ifconfig="admin/dashboard/enable_charts"> + <action method="setId"> + <argument name="id" xsi:type="string">diagram_tab</argument> + </action> + <action method="setDestElementId"> + <argument name="dest_element_id" xsi:type="string">diagram_tab_content</argument> + </action> + <action method="addTab"> + <argument name="name" xsi:type="string">orders</argument> + <argument name="block" xsi:type="string">orders</argument> + </action> + <action method="addTab"> + <argument name="name" xsi:type="string">amounts</argument> + <argument name="block" xsi:type="string">amounts</argument> + </action> + <block class="Magento\Backend\Block\Widget\Tab" name="dashboard.chart.orders" as="orders" template="Magento_Backend::dashboard/chart.phtml"> + <arguments> + <argument name="html_id" xsi:type="string">orders</argument> + <argument name="label" xsi:type="string" translate="true">Orders</argument> + <argument name="title" xsi:type="string" translate="true">Orders</argument> + <argument name="update_url" xsi:type="string">adminhtml/dashboard_chart/orders</argument> + <argument name="view_model" xsi:type="object">Magento\Backend\ViewModel\ChartsPeriod</argument> + </arguments> + </block> + <block class="Magento\Backend\Block\Widget\Tab" name="dashboard.chart.amounts" as="amounts" template="Magento_Backend::dashboard/chart.phtml"> + <arguments> + <argument name="chart_precision" xsi:type="number">2</argument> + <argument name="html_id" xsi:type="string">amounts</argument> + <argument name="label" xsi:type="string" translate="true">Amounts</argument> + <argument name="title" xsi:type="string" translate="true">Amounts</argument> + <argument name="update_url" xsi:type="string">adminhtml/dashboard_chart/amounts</argument> + <argument name="view_model" xsi:type="object">Magento\Backend\ViewModel\ChartsPeriod</argument> + </arguments> + </block> + </block> + <block name="dashboard.diagrams.period" + as="diagramsPeriod" + template="Magento_Backend::dashboard/chart/period.phtml" + ifconfig="admin/dashboard/enable_charts"> + <arguments> + <argument name="view_model" xsi:type="object">Magento\Backend\ViewModel\ChartsPeriod</argument> + </arguments> + </block> + </block> </referenceContainer> </body> </page> diff --git a/app/code/Magento/Backend/view/adminhtml/templates/dashboard/chart.phtml b/app/code/Magento/Backend/view/adminhtml/templates/dashboard/chart.phtml new file mode 100644 index 0000000000000..65c0d292ee187 --- /dev/null +++ b/app/code/Magento/Backend/view/adminhtml/templates/dashboard/chart.phtml @@ -0,0 +1,43 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ + +use Magento\Backend\ViewModel\ChartsPeriod; +use Magento\Framework\Escaper; +use Magento\Framework\View\Element\Template; + +/** + * @var Template $block + * @var Escaper $escaper + * @var ChartsPeriod $viewModel + */ +$viewModel = $block->getViewModel(); +?> +<div class="dashboard-diagram"> + <div class="dashboard-diagram-graph"> + <canvas id="chart_<?= $escaper->escapeHtmlAttr($block->getData('html_id')) ?>_period" + style="display: none;"></canvas> + <div class="dashboard-diagram-nodata"> + <span><?= $escaper->escapeHtml(__('No Data Found')) ?></span> + </div> + </div> + <script type="text/x-magento-init"> + { + "#chart_<?= $escaper->escapeJs($block->getData('html_id')) ?>_period": { + "Magento_Backend/js/dashboard/chart": { + "updateUrl": "<?= $escaper->escapeUrl($block->getUrl($block->getData('update_url'), [ + '_current' => true + ])) ?>", + "periodSelect": "#dashboard_chart_period", + "periodUnits": <?= /** @noEscape */ $viewModel->getPeriodUnits() ?>, + <?php if ($precision = $block->getData('chart_precision')): ?> + "precision": <?= (int)$precision ?>, + <?php endif; ?> + "type": "<?= $escaper->escapeJs($block->getData('html_id')) ?>" + } + } + } + </script> +</div> diff --git a/app/code/Magento/Backend/view/adminhtml/templates/dashboard/chart/disabled.phtml b/app/code/Magento/Backend/view/adminhtml/templates/dashboard/chart/disabled.phtml new file mode 100644 index 0000000000000..ac600c67e662f --- /dev/null +++ b/app/code/Magento/Backend/view/adminhtml/templates/dashboard/chart/disabled.phtml @@ -0,0 +1,22 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +use Magento\Backend\ViewModel\ChartDisabled; +use Magento\Framework\Escaper; +use Magento\Framework\View\Element\Template; + +/** + * @var Template $block + * @var Escaper $escaper + * @var ChartDisabled $viewModel + * @phpcs:ignoreFile + */ +$viewModel = $block->getViewModel(); +?> +<?php if (!$viewModel->isChartEnabled()): ?> + <div class="dashboard-diagram-disabled"> + <?= $escaper->escapeHtml(__('Chart is disabled. To enable the chart, click <a href="%1">here</a>.', $escaper->escapeUrl($viewModel->getConfigUrl())), ['a']) ?> + </div> +<?php endif; ?> diff --git a/app/code/Magento/Backend/view/adminhtml/templates/dashboard/chart/period.phtml b/app/code/Magento/Backend/view/adminhtml/templates/dashboard/chart/period.phtml new file mode 100644 index 0000000000000..f30bfc7a57fb9 --- /dev/null +++ b/app/code/Magento/Backend/view/adminhtml/templates/dashboard/chart/period.phtml @@ -0,0 +1,32 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ + +use Magento\Backend\ViewModel\ChartsPeriod; +use Magento\Framework\Escaper; +use Magento\Framework\View\Element\Template; + +/** + * @var Template $block + * @var Escaper $escaper + * @var ChartsPeriod $viewModel + */ +$viewModel = $block->getViewModel(); +?> +<div class="dashboard-diagram-switcher"> + <label for="dashboard_chart_period" class="label"><?= $escaper->escapeHtml(__('Select Range:')) ?></label> + <select name="period" id="dashboard_chart_period" class="admin__control-select"> + <?php foreach ($viewModel->getDatePeriods() as $value => $label): ?> + <?php + if ($value === 'custom') { + continue; + } + ?> + <option value="<?= $escaper->escapeHtmlAttr($value) ?>" + <?php if ($block->getRequest()->getParam('period') === $value): ?> selected="selected"<?php endif; ?> + ><?= $escaper->escapeHtml($label) ?></option> + <?php endforeach; ?> + </select> +</div> diff --git a/app/code/Magento/Backend/view/adminhtml/templates/dashboard/graph.phtml b/app/code/Magento/Backend/view/adminhtml/templates/dashboard/graph.phtml index 12b388c210774..21fd3f45a2965 100644 --- a/app/code/Magento/Backend/view/adminhtml/templates/dashboard/graph.phtml +++ b/app/code/Magento/Backend/view/adminhtml/templates/dashboard/graph.phtml @@ -3,6 +3,11 @@ * Copyright © Magento, Inc. All rights reserved. * See COPYING.txt for license details. */ +/** + * @deprecated dashboard graphs were migrated to dynamic chart.js solution + * @see Magento_Backend::dashboard/chart.phtml + * @phpcs:ignoreFile + */ ?> <div class="dashboard-diagram"> <div class="dashboard-diagram-switcher"> diff --git a/app/code/Magento/Backend/view/adminhtml/templates/dashboard/graph/disabled.phtml b/app/code/Magento/Backend/view/adminhtml/templates/dashboard/graph/disabled.phtml index f8e584ce5b9cd..513a76cde29c0 100644 --- a/app/code/Magento/Backend/view/adminhtml/templates/dashboard/graph/disabled.phtml +++ b/app/code/Magento/Backend/view/adminhtml/templates/dashboard/graph/disabled.phtml @@ -3,6 +3,11 @@ * Copyright © Magento, Inc. All rights reserved. * See COPYING.txt for license details. */ +/** + * @deprecated dashboard graphs were migrated to dynamic chart.js solution + * @see Magento_Backend::dashboard/chart/disabled.phtml + * @phpcs:ignoreFile + */ ?> <div class="dashboard-diagram-disabled"> <?= /* @noEscape */ __('Chart is disabled. To enable the chart, click <a href="%1">here</a>.', $block->escapeUrl($block->getConfigUrl())) ?> diff --git a/app/code/Magento/Backend/view/adminhtml/templates/dashboard/index.phtml b/app/code/Magento/Backend/view/adminhtml/templates/dashboard/index.phtml index 6152c8fe1cff1..40df857968070 100644 --- a/app/code/Magento/Backend/view/adminhtml/templates/dashboard/index.phtml +++ b/app/code/Magento/Backend/view/adminhtml/templates/dashboard/index.phtml @@ -5,78 +5,17 @@ */ ?> -<?php if (is_array($block->getChildBlock('diagrams')->getTabsIds())) : ?> -<script> -require([ - 'Magento_Ui/js/modal/alert', - 'prototype' -], function(alert){ - -window.changeDiagramsPeriod = function(periodObj) { - periodParam = periodObj.value ? 'period/' + periodObj.value + '/' : ''; - <?php foreach ($block->getChildBlock('diagrams')->getTabsIds() as $tabId) : ?> - ajaxBlockParam = 'block/tab_<?= $block->escapeJs($tabId) ?>/'; - ajaxBlockUrl = '<?= $block->escapeJs($block->getUrl('adminhtml/*/ajaxBlock', ['_current' => true, 'block' => '', 'period' => ''])) ?>' + ajaxBlockParam + periodParam; - new Ajax.Request(ajaxBlockUrl, { - parameters: {isAjax: 'true', form_key: FORM_KEY}, - onSuccess: function(transport) { - tabContentElementId = '<?= $block->escapeJs($block->getChildBlock('diagrams')->getId()) ?>_<?= $block->escapeJs($tabId) ?>_content'; - try { - if (transport.responseText.isJSON()) { - var response = transport.responseText.evalJSON() - if (response.error) { - alert({ - content: response.message - }); - } - if(response.ajaxExpired && response.ajaxRedirect) { - setLocation(response.ajaxRedirect); - } - } else { - $(tabContentElementId).update(transport.responseText); - } - } - catch (e) { - $(tabContentElementId).update(transport.responseText); - } - } - }); - <?php endforeach; ?> - ajaxBlockUrl = '<?= $block->escapeJs($block->getUrl('adminhtml/*/ajaxBlock', ['_current' => true, 'block' => 'totals', 'period' => ''])) ?>' + periodParam; - new Ajax.Request(ajaxBlockUrl, { - parameters: {isAjax: 'true', form_key: FORM_KEY}, - onSuccess: function(transport) { - tabContentElementId = 'dashboard_diagram_totals'; - try { - if (transport.responseText.isJSON()) { - var response = transport.responseText.evalJSON(); - if (response.error) { - alert({ - content: response.message - }); - } - if(response.ajaxExpired && response.ajaxRedirect) { - setLocation(response.ajaxRedirect); - } - } else { - $(tabContentElementId).replace(transport.responseText); - } - } - catch (e) { - $(tabContentElementId).replace(transport.responseText); - } - } - }); -} - -}); -</script> -<?php endif; ?> <div class="dashboard-container row"> <div class="dashboard-main col-m-8 col-m-push-4"> <div class="dashboard-diagram-container"> + <?= $block->getChildHtml('chartDisabled') ?> <?= $block->getChildHtml('diagrams') ?> - <?php if (is_array($block->getChildBlock('diagrams')->getTabsIds())) : ?> + <?php + $diagramsBlock = $block->getChildBlock('diagrams'); + $tabsIds = $diagramsBlock ? $diagramsBlock->getTabsIds() : []; + ?> + <?php if (is_array($tabsIds)): ?> + <?= $block->getChildHtml('diagramsPeriod') ?> <div id="diagram_tab_content" class="dashboard-diagram-tab-content"></div> <?php endif; ?> </div> diff --git a/app/code/Magento/Backend/view/adminhtml/templates/dashboard/totalbar.phtml b/app/code/Magento/Backend/view/adminhtml/templates/dashboard/totalbar.phtml index 918eea75fab99..44320e247f9d3 100644 --- a/app/code/Magento/Backend/view/adminhtml/templates/dashboard/totalbar.phtml +++ b/app/code/Magento/Backend/view/adminhtml/templates/dashboard/totalbar.phtml @@ -3,19 +3,28 @@ * Copyright © Magento, Inc. All rights reserved. * See COPYING.txt for license details. */ + +use Magento\Backend\Block\Dashboard\Totals; +use Magento\Framework\Escaper; + +/** + * @var Totals $block + * @var Escaper $escaper + */ ?> -<?php if (count($block->getTotals()) > 0) : ?> -<div class="dashboard-totals" id="dashboard_diagram_totals"> - <ul class="dashboard-totals-list"> - <?php foreach ($block->getTotals() as $_total) : ?> - <li class="dashboard-totals-item"> - <span class="dashboard-totals-label"><?= $block->escapeHtml($_total['label']) ?></span> - <strong class="dashboard-totals-value"> - <?= /* @noEscape */ $_total['value'] ?> - <span class="dashboard-totals-decimals"><?= /* @noEscape */ $_total['decimals'] ?></span> - </strong> - </li> - <?php endforeach; ?> - </ul> -</div> +<?php if (count($block->getTotals()) > 0): ?> + <div class="dashboard-totals" id="dashboard_diagram_totals"> + <ul class="dashboard-totals-list"> + <?php foreach ($block->getTotals() as $total): ?> + <li class="dashboard-totals-item"> + <span class="dashboard-totals-label"><?= $escaper->escapeHtml($total['label']) ?></span> + <strong class="dashboard-totals-value"> + <?= /* @noEscape */ $total['value'] ?> + <span class="dashboard-totals-decimals"><?= /* @noEscape */ $total['decimals'] ?></span> + </strong> + </li> + <?php endforeach; ?> + </ul> + </div> + <?= $block->getChildHtml() ?> <?php endif; ?> diff --git a/app/code/Magento/Backend/view/adminhtml/templates/dashboard/totalbar/script.phtml b/app/code/Magento/Backend/view/adminhtml/templates/dashboard/totalbar/script.phtml new file mode 100644 index 0000000000000..7e8f12d19b7fd --- /dev/null +++ b/app/code/Magento/Backend/view/adminhtml/templates/dashboard/totalbar/script.phtml @@ -0,0 +1,27 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +use Magento\Framework\Escaper; +use Magento\Framework\View\Element\Template; + +/** + * @var Template $block + * @var Escaper $escaper + */ +?> +<script type="text/x-magento-init"> +{ + "#dashboard_diagram_totals": { + "Magento_Backend/js/dashboard/totals": { + "updateUrl": "<?= $escaper->escapeUrl($block->getUrl('adminhtml/*/ajaxBlock', [ + '_current' => true, + 'block' => 'totals', + 'period' => '' + ])) ?>", + "periodSelect": "#dashboard_chart_period" + } + } +} +</script> diff --git a/app/code/Magento/Backend/view/adminhtml/web/js/dashboard/chart.js b/app/code/Magento/Backend/view/adminhtml/web/js/dashboard/chart.js new file mode 100644 index 0000000000000..9177939dcfa9f --- /dev/null +++ b/app/code/Magento/Backend/view/adminhtml/web/js/dashboard/chart.js @@ -0,0 +1,132 @@ +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ + +/*global define*/ +/*global FORM_KEY*/ +define([ + 'jquery', + 'chartJs', + 'jquery-ui-modules/widget', + 'moment' +], function ($, Chart) { + 'use strict'; + + $.widget('mage.dashboardChart', { + options: { + updateUrl: '', + periodSelect: null, + periodUnits: [], + precision: 0, + type: '' + }, + chart: null, + + /** + * @private + */ + _create: function () { + this.createChart(); + + if (this.options.periodSelect) { + $(document).on('change', this.options.periodSelect, this.refreshChartData.bind(this)); + + this.period = $(this.options.periodSelect).val(); + } + }, + + /** + * @public + */ + createChart: function () { + this.chart = new Chart(this.element, this.getChartSettings()); + this.refreshChartData(); + }, + + /** + * @public + */ + refreshChartData: function () { + var data = { + 'form_key': FORM_KEY + }; + + if (this.options.periodSelect) { + this.period = data.period = $(this.options.periodSelect).val(); + } + + $.ajax({ + url: this.options.updateUrl, + showLoader: true, + data: data, + dataType: 'json', + type: 'POST', + success: this.updateChart.bind(this) + }); + }, + + /** + * @public + * @param {Object} response + */ + updateChart: function (response) { + $(this.element).toggle(response.data.length > 0); + $(this.element).next('.dashboard-diagram-nodata').toggle(response.data.length === 0); + + this.chart.options.scales.xAxes[0].time.unit = this.options.periodUnits[this.period] ? + this.options.periodUnits[this.period] : 'hour'; + this.chart.data.datasets[0].data = response.data; + this.chart.data.datasets[0].label = response.label; + this.chart.update(); + }, + + /** + * @returns {Object} chart object configuration + */ + getChartSettings: function () { + return { + type: 'bar', + data: { + datasets: [{ + data: [], + backgroundColor: '#f1d4b3', + borderColor: '#eb5202', + borderWidth: 1 + }] + }, + options: { + legend: { + onClick: this.handleChartLegendClick, + position: 'bottom' + }, + scales: { + xAxes: [{ + offset: true, + type: 'time', + ticks: { + autoSkip: true, + source: 'data' + } + }], + yAxes: [{ + ticks: { + beginAtZero: true, + precision: this.options.precision + } + }] + } + } + }; + }, + + /** + * @public + */ + handleChartLegendClick: function () { + // don't hide dataset on clicking into legend item + } + }); + + return $.mage.dashboardChart; +}); diff --git a/app/code/Magento/Backend/view/adminhtml/web/js/dashboard/totals.js b/app/code/Magento/Backend/view/adminhtml/web/js/dashboard/totals.js new file mode 100644 index 0000000000000..2a696fe7bb38c --- /dev/null +++ b/app/code/Magento/Backend/view/adminhtml/web/js/dashboard/totals.js @@ -0,0 +1,60 @@ +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ + +/*global define*/ +/*global FORM_KEY*/ +define([ + 'jquery', + 'jquery-ui-modules/widget' +], function ($) { + 'use strict'; + + $.widget('mage.dashboardTotals', { + options: { + updateUrl: '', + periodSelect: null + }, + elementId: null, + + /** + * @private + */ + _create: function () { + this.elementId = $(this.element).attr('id'); + + if (this.options.periodSelect) { + $(document).on('change', this.options.periodSelect, $.proxy(function () { + this.refreshTotals(); + }, this)); + } + }, + + /** + * @public + */ + refreshTotals: function () { + var periodParam = ''; + + if (this.options.periodSelect && $(this.options.periodSelect).val()) { + periodParam = 'period/' + $(this.options.periodSelect).val() + '/'; + } + + $.ajax({ + url: this.options.updateUrl + periodParam, + showLoader: true, + data: { + 'form_key': FORM_KEY + }, + dataType: 'html', + type: 'POST', + success: $.proxy(function (response) { + $('#' + this.elementId).replaceWith(response); + }, this) + }); + } + }); + + return $.mage.dashboardTotals; +}); diff --git a/app/code/Magento/Backend/view/adminhtml/web/js/translate.js b/app/code/Magento/Backend/view/adminhtml/web/js/translate.js index d6e1547600c4e..eae1394c15027 100644 --- a/app/code/Magento/Backend/view/adminhtml/web/js/translate.js +++ b/app/code/Magento/Backend/view/adminhtml/web/js/translate.js @@ -35,7 +35,7 @@ define([ * @return {String} */ this.translate = function (text) { - return _data[text] ? _data[text] : text; + return typeof _data[text] === 'string' ? _data[text] : text; }; return this; diff --git a/app/code/Magento/Backup/Test/Mftf/Test/AdminCreateAndDeleteBackupsTest.xml b/app/code/Magento/Backup/Test/Mftf/Test/AdminCreateAndDeleteBackupsTest.xml index 778c6d5112b6a..383c1122ee07f 100644 --- a/app/code/Magento/Backup/Test/Mftf/Test/AdminCreateAndDeleteBackupsTest.xml +++ b/app/code/Magento/Backup/Test/Mftf/Test/AdminCreateAndDeleteBackupsTest.xml @@ -23,7 +23,7 @@ </annotations> <!--Login to admin area--> - <actionGroup ref="LoginActionGroup" stepKey="loginAsAdmin"/> + <actionGroup ref="AdminLoginActionGroup" stepKey="loginAsAdmin"/> <!--Go to backup index page--> <amOnPage url="{{BackupIndexPage.url}}" stepKey="goToBackupPage"/> diff --git a/app/code/Magento/Backup/etc/adminhtml/system.xml b/app/code/Magento/Backup/etc/adminhtml/system.xml index 78e0aae1dd4c2..577762037ceb4 100644 --- a/app/code/Magento/Backup/etc/adminhtml/system.xml +++ b/app/code/Magento/Backup/etc/adminhtml/system.xml @@ -8,21 +8,21 @@ <config xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="urn:magento:module:Magento_Config:etc/system_file.xsd"> <system> <section id="system"> - <group id="backup" translate="label" type="text" sortOrder="500" showInDefault="1" showInWebsite="0" showInStore="0"> + <group id="backup" translate="label" type="text" sortOrder="500" showInDefault="1"> <label>Backup Settings</label> - <field id="functionality_enabled" translate="label" type="select" sortOrder="5" showInDefault="1" showInWebsite="0" showInStore="0"> + <field id="functionality_enabled" translate="label" type="select" sortOrder="5" showInDefault="1"> <label>Enable Backup</label> <comment>Disabled by default for security reasons.</comment> <source_model>Magento\Config\Model\Config\Source\Yesno</source_model> </field> - <field id="enabled" translate="label" type="select" sortOrder="10" showInDefault="1" showInWebsite="0" showInStore="0"> + <field id="enabled" translate="label" type="select" sortOrder="10" showInDefault="1"> <label>Enable Scheduled Backup</label> <source_model>Magento\Config\Model\Config\Source\Yesno</source_model> <depends> <field id="functionality_enabled">1</field> </depends> </field> - <field id="type" translate="label" type="select" sortOrder="20" showInDefault="1" showInWebsite="0" showInStore="0"> + <field id="type" translate="label" type="select" sortOrder="20" showInDefault="1"> <label>Scheduled Backup Type</label> <depends> <field id="enabled">1</field> @@ -30,14 +30,14 @@ </depends> <source_model>Magento\Backup\Model\Config\Source\Type</source_model> </field> - <field id="time" translate="label" type="time" sortOrder="30" showInDefault="1" showInWebsite="0" showInStore="0"> + <field id="time" translate="label" type="time" sortOrder="30" showInDefault="1"> <label>Start Time</label> <depends> <field id="enabled">1</field> <field id="functionality_enabled">1</field> </depends> </field> - <field id="frequency" translate="label" type="select" sortOrder="40" showInDefault="1" showInWebsite="0" showInStore="0"> + <field id="frequency" translate="label" type="select" sortOrder="40" showInDefault="1"> <label>Frequency</label> <depends> <field id="enabled">1</field> @@ -46,7 +46,7 @@ <source_model>Magento\Cron\Model\Config\Source\Frequency</source_model> <backend_model>Magento\Backup\Model\Config\Backend\Cron</backend_model> </field> - <field id="maintenance" translate="label comment" type="select" sortOrder="50" showInDefault="1" showInWebsite="0" showInStore="0"> + <field id="maintenance" translate="label comment" type="select" sortOrder="50" showInDefault="1"> <label>Maintenance Mode</label> <comment>Please put your store into maintenance mode during backup.</comment> <depends> diff --git a/app/code/Magento/Backup/registration.php b/app/code/Magento/Backup/registration.php index 811e3c5657484..59864b84d8217 100644 --- a/app/code/Magento/Backup/registration.php +++ b/app/code/Magento/Backup/registration.php @@ -4,6 +4,6 @@ * See COPYING.txt for license details. */ -use \Magento\Framework\Component\ComponentRegistrar; +use Magento\Framework\Component\ComponentRegistrar; ComponentRegistrar::register(ComponentRegistrar::MODULE, 'Magento_Backup', __DIR__); diff --git a/app/code/Magento/Braintree/Gateway/Command/CaptureStrategyCommand.php b/app/code/Magento/Braintree/Gateway/Command/CaptureStrategyCommand.php index de439aad2defd..d35906c929b2a 100644 --- a/app/code/Magento/Braintree/Gateway/Command/CaptureStrategyCommand.php +++ b/app/code/Magento/Braintree/Gateway/Command/CaptureStrategyCommand.php @@ -21,7 +21,8 @@ use Magento\Payment\Gateway\Data\PaymentDataObjectInterface; /** - * Class CaptureStrategyCommand + * Braintree capture implementation. + * * @SuppressWarnings(PHPMD) */ class CaptureStrategyCommand implements CommandInterface @@ -84,7 +85,7 @@ class CaptureStrategyCommand implements CommandInterface * @param FilterBuilder $filterBuilder * @param SearchCriteriaBuilder $searchCriteriaBuilder * @param SubjectReader $subjectReader - * @param BraintreeAdapterFactory $braintreeAdapterFactory, + * @param BraintreeAdapterFactory $braintreeAdapterFactory * @param BraintreeSearchAdapter $braintreeSearchAdapter */ public function __construct( @@ -112,9 +113,9 @@ public function execute(array $commandSubject) { /** @var \Magento\Payment\Gateway\Data\PaymentDataObjectInterface $paymentDO */ $paymentDO = $this->subjectReader->readPayment($commandSubject); - $command = $this->getCommand($paymentDO); - $this->commandPool->get($command)->execute($commandSubject); + + return $this->commandPool->get($command)->execute($commandSubject); } /** @@ -128,11 +129,8 @@ private function getCommand(PaymentDataObjectInterface $paymentDO) $payment = $paymentDO->getPayment(); ContextHelper::assertOrderPayment($payment); - // if auth transaction does not exist then execute authorize&capture command + // if capture transaction does not exist then execute capture command $existsCapture = $this->isExistsCaptureTransaction($payment); - if (!$payment->getAuthorizationTransaction() && !$existsCapture) { - return self::SALE; - } // do capture for authorization transaction if (!$existsCapture && !$this->isExpiredAuthorization($payment, $paymentDO->getOrder())) { diff --git a/app/code/Magento/Braintree/Test/Mftf/Data/BraintreeData.xml b/app/code/Magento/Braintree/Test/Mftf/Data/BraintreeData.xml index f95ba2eb590dc..cba402f8f43fc 100644 --- a/app/code/Magento/Braintree/Test/Mftf/Data/BraintreeData.xml +++ b/app/code/Magento/Braintree/Test/Mftf/Data/BraintreeData.xml @@ -53,13 +53,13 @@ <data key="value">sandbox</data> </entity> <entity name="MerchantId" type="merchant_id"> - <data key="value">MERCH_ID</data> + <data key="value">{{_CREDS.magento/braintree_enabled_fraud_merchant_id}}</data> </entity> <entity name="PublicKey" type="public_key"> - <data key="value">PUBLIC_KEY</data> + <data key="value">{{_CREDS.magento/braintree_enabled_fraud_public_key}}</data> </entity> <entity name="PrivateKey" type="private_key"> - <data key="value">PRIVATE_KEY</data> + <data key="value">{{_CREDS.magento/braintree_enabled_fraud_private_key}}</data> </entity> <!-- default configuration used to restore Magento config --> @@ -141,6 +141,16 @@ <data key="cardNumberEnding">5100</data> <data key="cardExpire">12/2020</data> </entity> + <entity name="VisaDefaultCard" type="data"> + <data key="cardNumber">4111111111111111</data> + <data key="month">01</data> + <data key="year">30</data> + <data key="cvv">123</data> + </entity> + <entity name="VisaDefaultCardInfo"> + <data key="cardNumberEnding">1111</data> + <data key="cardExpire">01/2030</data> + </entity> <entity name="BraintreeConfigurationData" type="data"> <data key="title">Credit Card (Braintree)</data> diff --git a/app/code/Magento/Braintree/Test/Mftf/Data/ConfigData.xml b/app/code/Magento/Braintree/Test/Mftf/Data/ConfigData.xml new file mode 100644 index 0000000000000..a5bc8b7d8054c --- /dev/null +++ b/app/code/Magento/Braintree/Test/Mftf/Data/ConfigData.xml @@ -0,0 +1,23 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + /** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +--> + +<entities xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:noNamespaceSchemaLocation="urn:magento:mftf:DataGenerator/etc/dataProfileSchema.xsd"> + <entity name="Enable3DSecureBraintree"> + <data key="path">payment/braintree/verify_3dsecure</data> + <data key="value">1</data> + </entity> + <entity name="Disable3DSecureBraintree"> + <data key="path">payment/braintree/verify_3dsecure</data> + <data key="value">0</data> + </entity> + <entity name="DisableVaultBraintree"> + <data key="path">payment/braintree_cc_vault/active</data> + <data key="value">0</data> + </entity> +</entities> diff --git a/app/code/Magento/Braintree/Test/Mftf/Metadata/braintree_config-meta.xml b/app/code/Magento/Braintree/Test/Mftf/Metadata/BraintreeConfigMeta.xml similarity index 100% rename from app/code/Magento/Braintree/Test/Mftf/Metadata/braintree_config-meta.xml rename to app/code/Magento/Braintree/Test/Mftf/Metadata/BraintreeConfigMeta.xml diff --git a/app/code/Magento/Braintree/Test/Mftf/Section/BraintreeConfigurationPaymentSection.xml b/app/code/Magento/Braintree/Test/Mftf/Section/BraintreeConfigurationPaymentSection.xml index d9f2b14a40e2f..37d5204efb2c1 100644 --- a/app/code/Magento/Braintree/Test/Mftf/Section/BraintreeConfigurationPaymentSection.xml +++ b/app/code/Magento/Braintree/Test/Mftf/Section/BraintreeConfigurationPaymentSection.xml @@ -7,7 +7,7 @@ --> <sections xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" - xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/testSchema.xsd"> + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Page/etc/SectionObject.xsd"> <section name="BraintreeConfigurationPaymentSection"> <element name="creditCart" type="radio" selector="#braintree"/> <element name="paymentMethodContainer" type="block" selector=".payment-method-braintree"/> diff --git a/app/code/Magento/Braintree/Test/Mftf/Test/BraintreeCreditCardOnCheckoutTest.xml b/app/code/Magento/Braintree/Test/Mftf/Test/BraintreeCreditCardOnCheckoutTest.xml index 9a1110bfda29a..f06e63a2a29c1 100644 --- a/app/code/Magento/Braintree/Test/Mftf/Test/BraintreeCreditCardOnCheckoutTest.xml +++ b/app/code/Magento/Braintree/Test/Mftf/Test/BraintreeCreditCardOnCheckoutTest.xml @@ -30,6 +30,7 @@ <createData entity="Simple_US_Customer" stepKey="customer"/> <createData entity="BraintreeConfig" stepKey="BraintreeConfigurationData"/> <createData entity="CustomBraintreeConfigurationData" stepKey="CustomBraintreeConfigurationData"/> + <magentoCron stepKey="runCronIndex" groups="index"/> </before> <after> @@ -43,10 +44,15 @@ <!--Go to storefront--> <amOnPage url="" stepKey="DoToStorefront"/> <!--Create account--> - <actionGroup ref="SignUpNewUserFromStorefrontActionGroup" stepKey="SignUpNewUserFromStorefrontActionGroup"> - <argument name="Customer" value="Simple_US_Customer"/> + <actionGroup ref="StorefrontOpenCustomerAccountCreatePageActionGroup" stepKey="openCreateAccountPage"/> + <actionGroup ref="StorefrontFillCustomerAccountCreationFormActionGroup" stepKey="fillCreateAccountForm"> + <argument name="customer" value="Simple_US_Customer"/> + </actionGroup> + <actionGroup ref="StorefrontClickCreateAnAccountCustomerAccountCreationFormActionGroup" stepKey="submitCreateAccountForm"/> + <actionGroup ref="AssertMessageCustomerCreateAccountActionGroup" stepKey="seeSuccessMessage"> + <argument name="messageType" value="success"/> + <argument name="message" value="Thank you for registering with Main Website Store."/> </actionGroup> - <!--Add product to cart--> <amOnPage url="$$product.sku$$.html" stepKey="goToProductPage"/> <waitForPageLoad stepKey="waitForPageLoad"/> @@ -105,7 +111,13 @@ <grabTextFrom selector="{{CheckoutOrderSummarySection.additionalAddress}}" stepKey="additionalAddress"/> <see userInput="Shipping Address" stepKey="seeShippingAddress"/> <see userInput="Billing Address" stepKey="seeBillingAddress"/> - <assertEquals stepKey="assertValuesAreEqual" actual="$billingAddr" expected="$shippingAddr"/> - <assertNotEquals stepKey="assertValuesAreNotEqual" actual="$billingAddr" expected="$additionalAddress"/> + <assertEquals stepKey="assertValuesAreEqual"> + <actualResult type="const">$billingAddr</actualResult> + <expectedResult type="const">$shippingAddr</expectedResult> + </assertEquals> + <assertNotEquals stepKey="assertValuesAreNotEqual"> + <actualResult type="const">$billingAddr</actualResult> + <expectedResult type="const">$additionalAddress</expectedResult> + </assertNotEquals> </test> </tests> diff --git a/app/code/Magento/Braintree/Test/Mftf/Test/CreateAnAdminOrderUsingBraintreePaymentTest.xml b/app/code/Magento/Braintree/Test/Mftf/Test/CreateAnAdminOrderUsingBraintreePaymentTest.xml new file mode 100644 index 0000000000000..3052bba3c5ae9 --- /dev/null +++ b/app/code/Magento/Braintree/Test/Mftf/Test/CreateAnAdminOrderUsingBraintreePaymentTest.xml @@ -0,0 +1,114 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + /** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +--> + +<tests xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/testSchema.xsd"> + <test name="CreateAnAdminOrderUsingBraintreePaymentTest1Test"> + <annotations> + <features value="Backend"/> + <stories value="Creation an admin order using Braintree payment"/> + <title value="Create order using Braintree payment"/> + <description value="Admin should be able to create order using Braintree payment"/> + <severity value="CRITICAL"/> + <testCaseId value="MAGETWO-93677"/> + <group value="braintree"/> + <skip> + <issueId value="MQE-1576"/> + </skip> + </annotations> + <before> + <!--Login As Admin--> + <actionGroup ref="AdminLoginActionGroup" stepKey="loginAsAdmin"/> + + <!--CreateNewProduct--> + <createData entity="_defaultCategory" stepKey="createCategory"/> + <createData entity="_defaultProduct" stepKey="createProduct"> + <requiredEntity createDataKey="createCategory"/> + </createData> + + <!--Create New Customer--> + <createData stepKey="createCustomer" entity="Simple_US_Customer"/> + <magentoCron stepKey="runCronIndex" groups="index"/> + </before> + + + <!--Configure Braintree--> + <actionGroup ref="ConfigureBraintreeActionGroup" stepKey="configureBraintree"/> + + <!--Create New Role--> + <actionGroup ref="GoToUserRolesActionGroup" stepKey="GoToUserRoles"/> + <waitForPageLoad stepKey="waitForAllRoles" time="15"/> + <actionGroup ref="AdminCreateNewRoleActionGroup" stepKey="AdminCreateNewRole"/> + + <!--Create new admin user--> + <actionGroup ref="GoToAllUsersActionGroup" stepKey="GoToAllUsers"/> + <waitForPageLoad stepKey="waitForUsers" time="15"/> + <actionGroup ref="AdminCreateUserActionGroup" stepKey="AdminCreateNewUser"> + <argument name="role" value="role"/> + <argument name="User" value="NewAdmin"/> + </actionGroup> + + <!--SignOut--> + <actionGroup ref="AdminLogoutActionGroup" stepKey="signOutFromAdmin"/> + + <!--Log in as new user--> + <actionGroup ref="LoginNewUser" stepKey="signInNewUser"/> + <waitForPageLoad stepKey="waitForLogin" time="3"/> + + <!--Create New Order--> + <actionGroup ref="NavigateToNewOrderPageExistingCustomerActionGroup" stepKey="navigateToNewOrder"> + <argument name="customer" value="Simple_US_Customer"/> + </actionGroup> + + <!--Add Product to Order--> + <actionGroup ref="AddSimpleProductToOrderActionGroup" stepKey="addProduct"> + <argument name="product" value="_defaultProduct"/> + </actionGroup> + + <!--Fill Order Customer Information--> + <actionGroup ref="FillOrderCustomerInformationActionGroup" stepKey="fillCustomerAddress"> + <argument name="customer" value="Simple_US_Customer"/> + <argument name="address" value="US_Address_TX"/> + </actionGroup> + + <!--Select Shipping--> + <actionGroup ref="OrderSelectFlatRateShippingActionGroup" stepKey="selectFlatRateShipping"/> + <waitForPageLoad stepKey="waitForShippingToFinish"/> + + <!--Pay with Braintree --> + <actionGroup ref="useBraintreeForMasterCard" stepKey="selectCardWithBraintree"/> + + <!--Submit Order--> + <click stepKey="submitOrder" selector="{{NewOrderSection.submitOrder}}"/> + <waitForPageLoad stepKey="waitForSaveConfig"/> + <waitForElementVisible selector="{{NewOrderSection.successMessage}}" stepKey="waitForSuccessMessage"/> + + <after> + <!-- Disable BrainTree --> + <actionGroup ref="DisableBraintreeActionGroup" stepKey="disableBrainTree"/> + + <!--SignOut--> + <actionGroup ref="SignOut" stepKey="signOutFromNewUser"/> + <actionGroup ref="AdminLoginActionGroup" stepKey="loginAsAdmin"/> + + <!--Delete Product--> + <deleteData stepKey="deleteProduct" createDataKey="createProduct"/> + + <!--Delete Customer--> + <deleteData stepKey="deleteCustomer" createDataKey="createCustomer"/> + + <!--Delete User --> + <actionGroup ref="GoToAllUsersActionGroup" stepKey="GoBackToAllUsers"/> + <actionGroup ref="AdminDeleteNewUserActionGroup" stepKey="AdminDeleteUserActionGroup"/> + + <!--Delete Role--> + <actionGroup ref="GoToUserRolesActionGroup" stepKey="GoBackToUserRoles"/> + <actionGroup ref="AdminDeleteRoleActionGroup" stepKey="AdminDeleteRoleActionGroup"/> + </after> + </test> +</tests> diff --git a/app/code/Magento/Braintree/Test/Mftf/Test/CreateAnAdminOrderUsingBraintreePaymentTest1.xml b/app/code/Magento/Braintree/Test/Mftf/Test/CreateAnAdminOrderUsingBraintreePaymentTest1.xml deleted file mode 100644 index 16c8c93e94cbe..0000000000000 --- a/app/code/Magento/Braintree/Test/Mftf/Test/CreateAnAdminOrderUsingBraintreePaymentTest1.xml +++ /dev/null @@ -1,113 +0,0 @@ -<?xml version="1.0" encoding="UTF-8"?> -<!-- - /** - * Copyright © Magento, Inc. All rights reserved. - * See COPYING.txt for license details. - */ ---> - -<tests xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" - xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/testSchema.xsd"> - <test name="CreateAnAdminOrderUsingBraintreePaymentTest1"> - <annotations> - <features value="Backend"/> - <stories value="Creation an admin order using Braintree payment"/> - <title value="Create order using Braintree payment"/> - <description value="Admin should be able to create order using Braintree payment"/> - <severity value="CRITICAL"/> - <testCaseId value="MAGETWO-93677"/> - <group value="braintree"/> - <skip> - <issueId value="MQE-1576"/> - </skip> - </annotations> - <before> - <!--Login As Admin--> - <actionGroup ref="LoginAsAdmin" stepKey="loginAsAdmin"/> - - <!--CreateNewProduct--> - <createData entity="_defaultCategory" stepKey="createCategory"/> - <createData entity="_defaultProduct" stepKey="createProduct"> - <requiredEntity createDataKey="createCategory"/> - </createData> - - <!--Create New Customer--> - <createData stepKey="createCustomer" entity="Simple_US_Customer"/> - </before> - - - <!--Configure Braintree--> - <actionGroup ref="ConfigureBraintreeActionGroup" stepKey="configureBraintree"/> - - <!--Create New Role--> - <actionGroup ref="GoToUserRolesActionGroup" stepKey="GoToUserRoles"/> - <waitForPageLoad stepKey="waitForAllRoles" time="15"/> - <actionGroup ref="AdminCreateNewRoleActionGroup" stepKey="AdminCreateNewRole"/> - - <!--Create new admin user--> - <actionGroup ref="GoToAllUsersActionGroup" stepKey="GoToAllUsers"/> - <waitForPageLoad stepKey="waitForUsers" time="15"/> - <actionGroup ref="AdminCreateUserActionGroup" stepKey="AdminCreateNewUser"> - <argument name="role" value="role"/> - <argument name="User" value="NewAdmin"/> - </actionGroup> - - <!--SignOut--> - <actionGroup ref="logout" stepKey="signOutFromAdmin"/> - - <!--Log in as new user--> - <actionGroup ref="LoginNewUser" stepKey="signInNewUser"/> - <waitForPageLoad stepKey="waitForLogin" time="3"/> - - <!--Create New Order--> - <actionGroup ref="NavigateToNewOrderPageExistingCustomerActionGroup" stepKey="navigateToNewOrder"> - <argument name="customer" value="Simple_US_Customer"/> - </actionGroup> - - <!--Add Product to Order--> - <actionGroup ref="AddSimpleProductToOrderActionGroup" stepKey="addProduct"> - <argument name="product" value="_defaultProduct"/> - </actionGroup> - - <!--Fill Order Customer Information--> - <actionGroup ref="FillOrderCustomerInformationActionGroup" stepKey="fillCustomerAddress"> - <argument name="customer" value="Simple_US_Customer"/> - <argument name="address" value="US_Address_TX"/> - </actionGroup> - - <!--Select Shipping--> - <actionGroup ref="OrderSelectFlatRateShippingActionGroup" stepKey="selectFlatRateShipping"/> - <waitForPageLoad stepKey="waitForShippingToFinish"/> - - <!--Pay with Braintree --> - <actionGroup ref="useBraintreeForMasterCard" stepKey="selectCardWithBraintree"/> - - <!--Submit Order--> - <click stepKey="submitOrder" selector="{{NewOrderSection.submitOrder}}"/> - <waitForPageLoad stepKey="waitForSaveConfig"/> - <waitForElementVisible selector="{{NewOrderSection.successMessage}}" stepKey="waitForSuccessMessage"/> - - <after> - <!-- Disable BrainTree --> - <actionGroup ref="DisableBraintreeActionGroup" stepKey="disableBrainTree"/> - - <!--SignOut--> - <actionGroup ref="SignOut" stepKey="signOutFromNewUser"/> - <actionGroup ref="LoginAsAdmin" stepKey="loginAsAdmin"/> - - <!--Delete Product--> - <deleteData stepKey="deleteProduct" createDataKey="createProduct"/> - - <!--Delete Customer--> - <deleteData stepKey="deleteCustomer" createDataKey="createCustomer"/> - - <!--Delete User --> - <actionGroup ref="GoToAllUsersActionGroup" stepKey="GoBackToAllUsers"/> - <actionGroup ref="AdminDeleteNewUserActionGroup" stepKey="AdminDeleteUserActionGroup"/> - - <!--Delete Role--> - <actionGroup ref="GoToUserRolesActionGroup" stepKey="GoBackToUserRoles"/> - <actionGroup ref="AdminDeleteRoleActionGroup" stepKey="AdminDeleteRoleActionGroup"/> - </after> - </test> -</tests> diff --git a/app/code/Magento/Braintree/Test/Mftf/Test/CretateAdminOrderWithOnlinePaymentIncludingTaxAndDiscount.xml b/app/code/Magento/Braintree/Test/Mftf/Test/CretateAdminOrderWithOnlinePaymentIncludingTaxAndDiscount.xml deleted file mode 100644 index 371b59418e4a9..0000000000000 --- a/app/code/Magento/Braintree/Test/Mftf/Test/CretateAdminOrderWithOnlinePaymentIncludingTaxAndDiscount.xml +++ /dev/null @@ -1,129 +0,0 @@ -<?xml version="1.0" encoding="UTF-8"?> -<!-- - /** - * Copyright © Magento, Inc. All rights reserved. - * See COPYING.txt for license details. - */ ---> - -<tests xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" - xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/testSchema.xsd"> - <test name="CreateAdminOrderPayedWithOnlinePaymentIncludingTaxAndDiscount"> - <annotations> - <features value="Braintree"/> - <stories value="Get access to a New Credit Memo Page from Invoice for Order payed with online payment via Admin"/> - <title value="Admin should be able to open a New Credit Memo Page from Invoice Page for Order with tax and discount and payed using online payment method"/> - <description value="Admin should be able to open a New Credit Memo Page from Invoice Page for Order with tax and discount and payed using online payment method"/> - <severity value="CRITICAL"/> - <testCaseId value="MAGETWO-94472"/> - <group value="braintree"/> - <skip> - <issueId value="MQE-1576"/> - </skip> - </annotations> - - <before> - <!--Create Default Category--> - <createData entity="SimpleSubCategory" stepKey="createCategory"/> - - <!--Create Simple product--> - <createData entity="_defaultProduct" stepKey="simpleProduct"> - <requiredEntity createDataKey="createCategory"/> - </createData> - - <!--Create Tax Rule is based on default tax rates (Stores>Tax Rule) US-CA-*-Rate 1 = 8.2500 US-NY-*-Rate 1 = 8.3750 --> - <createData entity="SimpleTaxRule" stepKey="createTaxRule"/> - - <!--Configure Braintree Payment method--> - <createData entity="BraintreeConfig" stepKey="BraintreeConfigurationData"/> - <createData entity="CustomBraintreeConfigurationData" stepKey="enableBraintree"/> - - <!--Create Retailer Customer with US_CA address--> - <createData entity="Simple_US_Customer_CA" stepKey="simpleCustomer"> - <field key="group_id">3</field> - </createData> - - <!--Login as Admin User--> - <actionGroup ref="LoginAsAdmin" stepKey="loginAsAdmin"/> - </before> - - <after> - <!--Delete Cart Price Rule--> - <actionGroup ref="AdminDeleteCartPriceRuleForRetailerActionGroup" stepKey="deleteSalesRule"/> - - <!--Set to default configuration Tax Shipping Class--> - <actionGroup ref="setDefaultShippingTaxClass" stepKey="setdefaultClass"/> - - <!--Delete Simple Sub Category--> - <deleteData createDataKey="createCategory" stepKey="deleteCategory"/> - - <!--Delete Simple Product--> - <deleteData createDataKey="simpleProduct" stepKey="deleteSimpleProduct"/> - - <!-- Delete Tax Rule --> - <deleteData createDataKey="createTaxRule" stepKey="deleteTaxRule"/> - - <!-- Rollback Braintree to Default --> - <createData entity="RollBackCustomBraintreeConfigurationData" stepKey="rollbackBraintreeConfig"/> - - <!--Delete Customer--> - <deleteData createDataKey="simpleCustomer" stepKey="deleteSimpleCustomer"/> - - <!--Log Out--> - <actionGroup ref="logout" stepKey="logout"/> - </after> - - <!-- Create a cart price rule with 10% discount for whole cart --> - <click selector="{{AdminMenuSection.marketing}}" stepKey="clickOnMarketing" /> - <waitForPageLoad stepKey="waitForMarketing"/> - <click selector="{{CartPriceRulesSubmenuSection.cartPriceRules}}" stepKey="clickOnCartPriceRules"/> - <waitForPageLoad stepKey="waitForCartPriceRules"/> - <click selector="{{AdminCartPriceRulesSection.addNewRuleButton}}" stepKey="clickAddNewRule"/> - <fillField selector="{{AdminCartPriceRulesFormSection.ruleName}}" userInput="{{SimpleSalesRule.name}}" stepKey="fillRuleName"/> - <selectOption selector="{{AdminCartPriceRulesFormSection.websites}}" userInput="Main Website" stepKey="selectWebsites"/> - <actionGroup ref="SelectRetailerCustomerGroupActionGroup" stepKey="selectRetailerCustomerGroup"/> - <click selector="{{AdminCartPriceRulesFormSection.actionsHeader}}" stepKey="clickToExpandActions"/> - <selectOption selector="{{AdminCartPriceRulesFormSection.apply}}" userInput="Percent of product price discount" stepKey="selectActionType"/> - <fillField selector="{{AdminCartPriceRulesFormSection.discountAmount}}" userInput="10" stepKey="fillDiscountAmount"/> - <click selector="{{AdminCartPriceRulesFormSection.save}}" stepKey="clickSaveButton"/> - <waitForPageLoad stepKey="waitForCartRuleLoad"/> - <see selector="{{AdminCartPriceRulesSection.messages}}" userInput="You saved the rule." stepKey="seeSuccessMessage"/> - - <!--Set Taxable Goods for Shipping Tax Class--> - <actionGroup ref="ChangeShippingTaxClassActionGroup" stepKey="changeShippingTaxClass"/> - - <!--Adding Special price to product--> - <amOnPage url="{{AdminProductEditPage.url($$simpleProduct.id$$)}}" stepKey="openAdminProductEditPage"/> - <actionGroup ref="AddSpecialPriceToProductActionGroup" stepKey="addSpecialPrice"/> - <actionGroup ref="SaveProductFormActionGroup" stepKey="saveProductForm"/> - - <!--Create New Order--> - <actionGroup ref="NavigateToNewOrderPageExistingCustomerActionGroup" stepKey="navigateToNewOrderWithExistingCustomer"> - <argument name="customer" value="$$simpleCustomer$$"/> - </actionGroup> - - <!--Add a product to order--> - <actionGroup ref="AddSimpleProductToOrderActionGroup" stepKey="addProductToOrder"> - <argument name="product" value="$$simpleProduct$$"/> - </actionGroup> - - <!--Select FlatRate shipping method--> - <actionGroup ref="OrderSelectFlatRateShippingActionGroup" stepKey="orderSelectFlatRateShippingMethod"/> - - <!--Select Braintree online Payment method --> - <actionGroup ref="AdminOrderBraintreeFillActionGroup" stepKey="selectCreditCardPayment"/> - - <!--Submit Order--> - <click stepKey="submitOrder" selector="{{NewOrderSection.submitOrder}}"/> - <waitForPageLoad stepKey="waitForSubmitOrder"/> - <see selector="{{AdminOrderDetailsMessagesSection.successMessage}}" userInput="You created the order." stepKey="seeOrderSuccessMessage" after="waitForSubmitOrder"/> - - <!-- Create New invoice--> - <actionGroup ref="AdminFastCreateInvoiceActionGroup" stepKey="createInvoice"/> - - <!--Get access to Credit Memo page from Invoice page--> - <click selector="{{AdminInvoiceMainActionsSection.openNewCreditMemoFromInvoice}}" stepKey="clickCreateNewCreditMemo"/> - <waitForPageLoad stepKey="waitForLoadNewCreditMemoPage"/> - <see selector="{{AdminCreditMemoOrderInformationSection.orderStatus}}" userInput="Processing" stepKey="seeNewCreditMemo"/> - </test> -</tests> diff --git a/app/code/Magento/Braintree/Test/Mftf/Test/CretateAdminOrderWithOnlinePaymentIncludingTaxAndDiscountTest.xml b/app/code/Magento/Braintree/Test/Mftf/Test/CretateAdminOrderWithOnlinePaymentIncludingTaxAndDiscountTest.xml new file mode 100644 index 0000000000000..9a843370f2c8e --- /dev/null +++ b/app/code/Magento/Braintree/Test/Mftf/Test/CretateAdminOrderWithOnlinePaymentIncludingTaxAndDiscountTest.xml @@ -0,0 +1,133 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + /** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +--> + +<tests xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/testSchema.xsd"> + <test name="CreateAdminOrderPayedWithOnlinePaymentIncludingTaxAndDiscountTest"> + <annotations> + <features value="Braintree"/> + <stories value="Get access to a New Credit Memo Page from Invoice for Order payed with online payment via Admin"/> + <title value="Admin should be able to open a New Credit Memo Page from Invoice Page for Order with tax and discount and payed using online payment method"/> + <description value="Admin should be able to open a New Credit Memo Page from Invoice Page for Order with tax and discount and payed using online payment method"/> + <severity value="CRITICAL"/> + <testCaseId value="MAGETWO-94472"/> + <group value="braintree"/> + <skip> + <issueId value="MQE-1576"/> + </skip> + </annotations> + + <before> + <!--Create Default Category--> + <createData entity="SimpleSubCategory" stepKey="createCategory"/> + + <!--Create Simple product--> + <createData entity="_defaultProduct" stepKey="simpleProduct"> + <requiredEntity createDataKey="createCategory"/> + </createData> + + <!--Create Tax Rule is based on default tax rates (Stores>Tax Rule) US-CA-*-Rate 1 = 8.2500 US-NY-*-Rate 1 = 8.3750 --> + <createData entity="SimpleTaxRule" stepKey="createTaxRule"/> + + <!--Configure Braintree Payment method--> + <createData entity="BraintreeConfig" stepKey="BraintreeConfigurationData"/> + <createData entity="CustomBraintreeConfigurationData" stepKey="enableBraintree"/> + + <!--Create Retailer Customer with US_CA address--> + <createData entity="Simple_US_Customer_CA" stepKey="simpleCustomer"> + <field key="group_id">3</field> + </createData> + + <magentoCron stepKey="runCronIndex" groups="index"/> + + <!--Login as Admin User--> + <actionGroup ref="AdminLoginActionGroup" stepKey="loginAsAdmin"/> + </before> + + <after> + <!--Delete Cart Price Rule--> + <actionGroup ref="AdminDeleteCartPriceRuleForRetailerActionGroup" stepKey="deleteSalesRule"/> + + <!--Set to default configuration Tax Shipping Class--> + <actionGroup ref="setDefaultShippingTaxClass" stepKey="setdefaultClass"/> + + <!--Delete Simple Sub Category--> + <deleteData createDataKey="createCategory" stepKey="deleteCategory"/> + + <!--Delete Simple Product--> + <deleteData createDataKey="simpleProduct" stepKey="deleteSimpleProduct"/> + + <!-- Delete Tax Rule --> + <deleteData createDataKey="createTaxRule" stepKey="deleteTaxRule"/> + + <!-- Rollback Braintree to Default --> + <createData entity="RollBackCustomBraintreeConfigurationData" stepKey="rollbackBraintreeConfig"/> + + <!--Delete Customer--> + <deleteData createDataKey="simpleCustomer" stepKey="deleteSimpleCustomer"/> + + <!--Log Out--> + <actionGroup ref="AdminLogoutActionGroup" stepKey="logout"/> + </after> + + <!-- Create a cart price rule with 10% discount for whole cart --> + <click selector="{{AdminMenuSection.marketing}}" stepKey="clickOnMarketing"/> + <waitForPageLoad stepKey="waitForMarketing"/> + <click selector="{{CartPriceRulesSubmenuSection.cartPriceRules}}" stepKey="clickOnCartPriceRules"/> + <waitForPageLoad stepKey="waitForCartPriceRules"/> + <click selector="{{AdminCartPriceRulesSection.addNewRuleButton}}" stepKey="clickAddNewRule"/> + <fillField selector="{{AdminCartPriceRulesFormSection.ruleName}}" userInput="{{SimpleSalesRule.name}}" stepKey="fillRuleName"/> + <selectOption selector="{{AdminCartPriceRulesFormSection.websites}}" userInput="Main Website" stepKey="selectWebsites"/> + <actionGroup ref="SelectRetailerCustomerGroupActionGroup" stepKey="selectRetailerCustomerGroup"/> + <click selector="{{AdminCartPriceRulesFormSection.actionsHeader}}" stepKey="clickToExpandActions"/> + <selectOption selector="{{AdminCartPriceRulesFormSection.apply}}" userInput="Percent of product price discount" stepKey="selectActionType"/> + <fillField selector="{{AdminCartPriceRulesFormSection.discountAmount}}" userInput="10" stepKey="fillDiscountAmount"/> + <click selector="{{AdminCartPriceRulesFormSection.save}}" stepKey="clickSaveButton"/> + <waitForPageLoad stepKey="waitForCartRuleLoad"/> + <see selector="{{AdminCartPriceRulesSection.messages}}" userInput="You saved the rule." stepKey="seeSuccessMessage"/> + + <!--Set Taxable Goods for Shipping Tax Class--> + <actionGroup ref="ChangeShippingTaxClassActionGroup" stepKey="changeShippingTaxClass"/> + + <!--Adding Special price to product--> + <actionGroup ref="AdminProductPageOpenByIdActionGroup" stepKey="openAdminProductEditPage"> + <argument name="productId" value="$$simpleProduct.id$$"/> + </actionGroup> + <actionGroup ref="AddSpecialPriceToProductActionGroup" stepKey="addSpecialPrice"/> + <actionGroup ref="SaveProductFormActionGroup" stepKey="saveProductForm"/> + + <!--Create New Order--> + <actionGroup ref="NavigateToNewOrderPageExistingCustomerActionGroup" stepKey="navigateToNewOrderWithExistingCustomer"> + <argument name="customer" value="$$simpleCustomer$$"/> + </actionGroup> + + <!--Add a product to order--> + <actionGroup ref="AddSimpleProductToOrderActionGroup" stepKey="addProductToOrder"> + <argument name="product" value="$$simpleProduct$$"/> + </actionGroup> + + <!--Select FlatRate shipping method--> + <actionGroup ref="OrderSelectFlatRateShippingActionGroup" stepKey="orderSelectFlatRateShippingMethod"/> + + <!--Select Braintree online Payment method --> + <actionGroup ref="AdminOrderBraintreeFillActionGroup" stepKey="selectCreditCardPayment"/> + + <!--Submit Order--> + <click stepKey="submitOrder" selector="{{NewOrderSection.submitOrder}}"/> + <waitForPageLoad stepKey="waitForSubmitOrder"/> + <see selector="{{AdminOrderDetailsMessagesSection.successMessage}}" userInput="You created the order." stepKey="seeOrderSuccessMessage" after="waitForSubmitOrder"/> + + <!-- Create New invoice--> + <actionGroup ref="AdminFastCreateInvoiceActionGroup" stepKey="createInvoice"/> + + <!--Get access to Credit Memo page from Invoice page--> + <click selector="{{AdminInvoiceMainActionsSection.openNewCreditMemoFromInvoice}}" stepKey="clickCreateNewCreditMemo"/> + <waitForPageLoad stepKey="waitForLoadNewCreditMemoPage"/> + <see selector="{{AdminCreditMemoOrderInformationSection.orderStatus}}" userInput="Processing" stepKey="seeNewCreditMemo"/> + </test> +</tests> diff --git a/app/code/Magento/Braintree/Test/Unit/Gateway/Command/CaptureStrategyCommandTest.php b/app/code/Magento/Braintree/Test/Unit/Gateway/Command/CaptureStrategyCommandTest.php index 845a02930d709..969894a20f9de 100644 --- a/app/code/Magento/Braintree/Test/Unit/Gateway/Command/CaptureStrategyCommandTest.php +++ b/app/code/Magento/Braintree/Test/Unit/Gateway/Command/CaptureStrategyCommandTest.php @@ -24,8 +24,6 @@ use PHPUnit_Framework_MockObject_MockObject as MockObject; /** - * Tests \Magento\Braintree\Gateway\Command\CaptureStrategyCommand. - * * @SuppressWarnings(PHPMD.CouplingBetweenObjects) */ class CaptureStrategyCommandTest extends \PHPUnit\Framework\TestCase @@ -38,42 +36,42 @@ class CaptureStrategyCommandTest extends \PHPUnit\Framework\TestCase /** * @var CommandPoolInterface|MockObject */ - private $commandPoolMock; + private $commandPool; /** * @var TransactionRepositoryInterface|MockObject */ - private $transactionRepositoryMock; + private $transactionRepository; /** * @var FilterBuilder|MockObject */ - private $filterBuilderMock; + private $filterBuilder; /** * @var SearchCriteriaBuilder|MockObject */ - private $searchCriteriaBuilderMock; + private $searchCriteriaBuilder; /** * @var Payment|MockObject */ - private $paymentMock; + private $payment; /** * @var GatewayCommand|MockObject */ - private $commandMock; + private $command; /** * @var SubjectReader|MockObject */ - private $subjectReaderMock; + private $subjectReader; /** * @var BraintreeAdapter|MockObject */ - private $braintreeAdapterMock; + private $braintreeAdapter; /** * @var BraintreeSearchAdapter @@ -82,12 +80,12 @@ class CaptureStrategyCommandTest extends \PHPUnit\Framework\TestCase protected function setUp() { - $this->commandPoolMock = $this->getMockBuilder(CommandPoolInterface::class) + $this->commandPool = $this->getMockBuilder(CommandPoolInterface::class) ->disableOriginalConstructor() ->setMethods(['get', '__wakeup']) ->getMock(); - $this->subjectReaderMock = $this->getMockBuilder(SubjectReader::class) + $this->subjectReader = $this->getMockBuilder(SubjectReader::class) ->disableOriginalConstructor() ->getMock(); @@ -96,106 +94,60 @@ protected function setUp() $this->initFilterBuilderMock(); $this->initSearchCriteriaBuilderMock(); - $this->braintreeAdapterMock = $this->getMockBuilder(BraintreeAdapter::class) + $this->braintreeAdapter = $this->getMockBuilder(BraintreeAdapter::class) ->disableOriginalConstructor() ->getMock(); - /** @var BraintreeAdapterFactory|MockObject $adapterFactoryMock */ - $adapterFactoryMock = $this->getMockBuilder(BraintreeAdapterFactory::class) + /** @var BraintreeAdapterFactory|MockObject $adapterFactory */ + $adapterFactory = $this->getMockBuilder(BraintreeAdapterFactory::class) ->disableOriginalConstructor() ->getMock(); - $adapterFactoryMock->expects(self::any()) - ->method('create') - ->willReturn($this->braintreeAdapterMock); + $adapterFactory->method('create') + ->willReturn($this->braintreeAdapter); $this->braintreeSearchAdapter = new BraintreeSearchAdapter(); $this->strategyCommand = new CaptureStrategyCommand( - $this->commandPoolMock, - $this->transactionRepositoryMock, - $this->filterBuilderMock, - $this->searchCriteriaBuilderMock, - $this->subjectReaderMock, - $adapterFactoryMock, + $this->commandPool, + $this->transactionRepository, + $this->filterBuilder, + $this->searchCriteriaBuilder, + $this->subjectReader, + $adapterFactory, $this->braintreeSearchAdapter ); } - /** - * @covers \Magento\Braintree\Gateway\Command\CaptureStrategyCommand::execute - */ - public function testSaleExecute() - { - $paymentData = $this->getPaymentDataObjectMock(); - $subject['payment'] = $paymentData; - - $this->subjectReaderMock->expects(self::once()) - ->method('readPayment') - ->with($subject) - ->willReturn($paymentData); - - $this->paymentMock->expects(static::once()) - ->method('getAuthorizationTransaction') - ->willReturn(false); - - $this->paymentMock->expects(static::once()) - ->method('getId') - ->willReturn(1); - - $this->buildSearchCriteria(); - - $this->transactionRepositoryMock->expects(static::once()) - ->method('getTotalCount') - ->willReturn(0); - - $this->commandPoolMock->expects(static::once()) - ->method('get') - ->with(CaptureStrategyCommand::SALE) - ->willReturn($this->commandMock); - - $this->strategyCommand->execute($subject); - } - - /** - * @covers \Magento\Braintree\Gateway\Command\CaptureStrategyCommand::execute - */ public function testCaptureExecute() { $paymentData = $this->getPaymentDataObjectMock(); $subject['payment'] = $paymentData; $lastTransId = 'txnds'; - $this->subjectReaderMock->expects(self::once()) - ->method('readPayment') + $this->subjectReader->method('readPayment') ->with($subject) ->willReturn($paymentData); - $this->paymentMock->expects(static::once()) - ->method('getAuthorizationTransaction') + $this->payment->method('getAuthorizationTransaction') ->willReturn(true); - $this->paymentMock->expects(static::once()) - ->method('getLastTransId') + $this->payment->method('getLastTransId') ->willReturn($lastTransId); - $this->paymentMock->expects(static::once()) - ->method('getId') + $this->payment->method('getId') ->willReturn(1); $this->buildSearchCriteria(); - $this->transactionRepositoryMock->expects(static::once()) - ->method('getTotalCount') + $this->transactionRepository->method('getTotalCount') ->willReturn(0); // authorization transaction was not expired $collection = $this->getNotExpiredExpectedCollection($lastTransId); - $collection->expects(static::once()) - ->method('maximumCount') + $collection->method('maximumCount') ->willReturn(0); - $this->commandPoolMock->expects(static::once()) - ->method('get') + $this->commandPool->method('get') ->with(CaptureStrategyCommand::CAPTURE) - ->willReturn($this->commandMock); + ->willReturn($this->command); $this->strategyCommand->execute($subject); } @@ -215,10 +167,9 @@ private function getNotExpiredExpectedCollection($lastTransactionId) ->disableOriginalConstructor() ->getMock(); - $this->braintreeAdapterMock->expects(static::once()) - ->method('search') + $this->braintreeAdapter->method('search') ->with( - static::callback( + self::callback( function (array $filters) use ($isExpectations) { foreach ($filters as $filter) { /** @var IsNode $filter */ @@ -240,82 +191,64 @@ function (array $filters) use ($isExpectations) { return $collection; } - /** - * @covers \Magento\Braintree\Gateway\Command\CaptureStrategyCommand::execute - */ public function testExpiredAuthorizationPerformVaultCaptureExecute() { $paymentData = $this->getPaymentDataObjectMock(); $subject['payment'] = $paymentData; $lastTransId = 'txnds'; - $this->subjectReaderMock->expects(self::once()) - ->method('readPayment') + $this->subjectReader->method('readPayment') ->with($subject) ->willReturn($paymentData); - $this->paymentMock->expects(static::once()) - ->method('getAuthorizationTransaction') + $this->payment->method('getAuthorizationTransaction') ->willReturn(true); - $this->paymentMock->expects(static::once()) - ->method('getLastTransId') + $this->payment->method('getLastTransId') ->willReturn($lastTransId); - $this->paymentMock->expects(static::once()) - ->method('getId') + $this->payment->method('getId') ->willReturn(1); $this->buildSearchCriteria(); - $this->transactionRepositoryMock->expects(static::once()) - ->method('getTotalCount') + $this->transactionRepository->method('getTotalCount') ->willReturn(0); // authorization transaction was expired $collection = $this->getNotExpiredExpectedCollection($lastTransId); - $collection->expects(static::once()) - ->method('maximumCount') + $collection->method('maximumCount') ->willReturn(1); - $this->commandPoolMock->expects(static::once()) - ->method('get') + $this->commandPool->method('get') ->with(CaptureStrategyCommand::VAULT_CAPTURE) - ->willReturn($this->commandMock); + ->willReturn($this->command); $this->strategyCommand->execute($subject); } - /** - * @covers \Magento\Braintree\Gateway\Command\CaptureStrategyCommand::execute - */ public function testVaultCaptureExecute() { $paymentData = $this->getPaymentDataObjectMock(); $subject['payment'] = $paymentData; - $this->subjectReaderMock->expects(self::once()) - ->method('readPayment') + $this->subjectReader->method('readPayment') ->with($subject) ->willReturn($paymentData); - $this->paymentMock->expects(static::once()) - ->method('getAuthorizationTransaction') + $this->payment->method('getAuthorizationTransaction') ->willReturn(true); - $this->paymentMock->expects(static::once()) - ->method('getId') + $this->payment->method('getId') ->willReturn(1); $this->buildSearchCriteria(); - $this->transactionRepositoryMock->expects(static::once()) - ->method('getTotalCount') + $this->transactionRepository->method('getTotalCount') ->willReturn(1); - $this->commandPoolMock->expects(static::once()) - ->method('get') + $this->commandPool->method('get') ->with(CaptureStrategyCommand::VAULT_CAPTURE) - ->willReturn($this->commandMock); + ->willReturn($this->command); $this->strategyCommand->execute($subject); } @@ -326,7 +259,7 @@ public function testVaultCaptureExecute() */ private function getPaymentDataObjectMock() { - $this->paymentMock = $this->getMockBuilder(Payment::class) + $this->payment = $this->getMockBuilder(Payment::class) ->disableOriginalConstructor() ->getMock(); @@ -335,16 +268,15 @@ private function getPaymentDataObjectMock() ->disableOriginalConstructor() ->getMock(); - $mock->expects(static::once()) - ->method('getPayment') - ->willReturn($this->paymentMock); + $mock->method('getPayment') + ->willReturn($this->payment); - $orderMock = $this->getMockBuilder(OrderAdapterInterface::class) + $order = $this->getMockBuilder(OrderAdapterInterface::class) ->disableOriginalConstructor() ->getMock(); $mock->method('getOrder') - ->willReturn($orderMock); + ->willReturn($order); return $mock; } @@ -354,13 +286,12 @@ private function getPaymentDataObjectMock() */ private function initCommandMock() { - $this->commandMock = $this->getMockBuilder(GatewayCommand::class) + $this->command = $this->getMockBuilder(GatewayCommand::class) ->disableOriginalConstructor() ->setMethods(['execute']) ->getMock(); - $this->commandMock->expects(static::once()) - ->method('execute') + $this->command->method('execute') ->willReturn([]); } @@ -369,7 +300,7 @@ private function initCommandMock() */ private function initFilterBuilderMock() { - $this->filterBuilderMock = $this->getMockBuilder(FilterBuilder::class) + $this->filterBuilder = $this->getMockBuilder(FilterBuilder::class) ->disableOriginalConstructor() ->setMethods(['setField', 'setValue', 'create', '__wakeup']) ->getMock(); @@ -380,23 +311,21 @@ private function initFilterBuilderMock() */ private function buildSearchCriteria() { - $this->filterBuilderMock->expects(static::exactly(2)) + $this->filterBuilder->expects(self::exactly(2)) ->method('setField') ->willReturnSelf(); - $this->filterBuilderMock->expects(static::exactly(2)) + $this->filterBuilder->expects(self::exactly(2)) ->method('setValue') ->willReturnSelf(); $searchCriteria = new SearchCriteria(); - $this->searchCriteriaBuilderMock->expects(static::exactly(2)) + $this->searchCriteriaBuilder->expects(self::exactly(2)) ->method('addFilters') ->willReturnSelf(); - $this->searchCriteriaBuilderMock->expects(static::once()) - ->method('create') + $this->searchCriteriaBuilder->method('create') ->willReturn($searchCriteria); - $this->transactionRepositoryMock->expects(static::once()) - ->method('getList') + $this->transactionRepository->method('getList') ->with($searchCriteria) ->willReturnSelf(); } @@ -406,7 +335,7 @@ private function buildSearchCriteria() */ private function initSearchCriteriaBuilderMock() { - $this->searchCriteriaBuilderMock = $this->getMockBuilder(SearchCriteriaBuilder::class) + $this->searchCriteriaBuilder = $this->getMockBuilder(SearchCriteriaBuilder::class) ->disableOriginalConstructor() ->setMethods(['addFilters', 'create', '__wakeup']) ->getMock(); @@ -417,7 +346,7 @@ private function initSearchCriteriaBuilderMock() */ private function initTransactionRepositoryMock() { - $this->transactionRepositoryMock = $this->getMockBuilder(TransactionRepositoryInterface::class) + $this->transactionRepository = $this->getMockBuilder(TransactionRepositoryInterface::class) ->disableOriginalConstructor() ->setMethods(['getList', 'getTotalCount', 'delete', 'get', 'save', 'create', '__wakeup']) ->getMock(); diff --git a/app/code/Magento/Braintree/Test/Unit/Gateway/Response/PayPal/VaultDetailsHandlerTest.php b/app/code/Magento/Braintree/Test/Unit/Gateway/Response/PayPal/VaultDetailsHandlerTest.php index b3a7f8b9ee76a..657aa930a841f 100644 --- a/app/code/Magento/Braintree/Test/Unit/Gateway/Response/PayPal/VaultDetailsHandlerTest.php +++ b/app/code/Magento/Braintree/Test/Unit/Gateway/Response/PayPal/VaultDetailsHandlerTest.php @@ -8,8 +8,8 @@ use Braintree\Result\Successful; use Braintree\Transaction; use Braintree\Transaction\PayPalDetails; -use Magento\Braintree\Gateway\SubjectReader; use Magento\Braintree\Gateway\Response\PayPal\VaultDetailsHandler; +use Magento\Braintree\Gateway\SubjectReader; use Magento\Framework\Intl\DateTimeFactory; use Magento\Framework\TestFramework\Unit\Helper\ObjectManager; use Magento\Payment\Gateway\Data\PaymentDataObjectInterface; @@ -99,7 +99,12 @@ protected function setUp() ->getMock(); $this->paymentExtensionMock = $this->getMockBuilder(OrderPaymentExtensionInterface::class) - ->setMethods(['setVaultPaymentToken', 'getVaultPaymentToken']) + ->setMethods([ + 'setVaultPaymentToken', + 'getVaultPaymentToken', + 'setNotificationMessage', + 'getNotificationMessage' + ]) ->disableOriginalConstructor() ->getMock(); $this->paymentExtensionFactoryMock = $this->getMockBuilder(OrderPaymentExtensionInterfaceFactory::class) @@ -119,7 +124,7 @@ protected function setUp() ->disableOriginalConstructor() ->setMethods(['create']) ->getMock(); - + $this->handler = new VaultDetailsHandler( $this->paymentTokenFactoryMock, $this->paymentExtensionFactoryMock, @@ -139,7 +144,7 @@ public function testHandle() ->with($this->paymentTokenMock); $this->paymentExtensionMock->method('getVaultPaymentToken') ->willReturn($this->paymentTokenMock); - + $this->paymentDataObjectMock->method('getPayment') ->willReturn($this->paymentInfoMock); @@ -154,7 +159,7 @@ public function testHandle() $expirationDate = '2017-07-05 00:00:00'; $this->dateTimeFactoryMock->method('create') ->willReturn($dateTime); - + $this->handler->handle($this->subject, $response); $extensionAttributes = $this->paymentInfoMock->getExtensionAttributes(); diff --git a/app/code/Magento/Braintree/Test/Unit/Gateway/Response/VaultDetailsHandlerTest.php b/app/code/Magento/Braintree/Test/Unit/Gateway/Response/VaultDetailsHandlerTest.php index c8ec52560be29..131d94f3206c8 100644 --- a/app/code/Magento/Braintree/Test/Unit/Gateway/Response/VaultDetailsHandlerTest.php +++ b/app/code/Magento/Braintree/Test/Unit/Gateway/Response/VaultDetailsHandlerTest.php @@ -25,7 +25,7 @@ use PHPUnit_Framework_MockObject_MockObject as MockObject; /** - * VaultDetailsHandler Test + * Verify class VaultDetailsHandler * * @SuppressWarnings(PHPMD.CouplingBetweenObjects) */ @@ -216,7 +216,12 @@ private function getConfigMock(): Config private function initPaymentExtensionAttributesMock() { $this->paymentExtension = $this->getMockBuilder(OrderPaymentExtensionInterface::class) - ->setMethods(['setVaultPaymentToken', 'getVaultPaymentToken']) + ->setMethods([ + 'setVaultPaymentToken', + 'getVaultPaymentToken', + 'setNotificationMessage', + 'getNotificationMessage' + ]) ->disableOriginalConstructor() ->getMock(); diff --git a/app/code/Magento/Braintree/etc/adminhtml/system.xml b/app/code/Magento/Braintree/etc/adminhtml/system.xml index bd4346e095c6d..06268536e880e 100644 --- a/app/code/Magento/Braintree/etc/adminhtml/system.xml +++ b/app/code/Magento/Braintree/etc/adminhtml/system.xml @@ -8,7 +8,7 @@ <config xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="urn:magento:module:Magento_Config:etc/system_file.xsd"> <system> <section id="payment"> - <group id="braintree_section" sortOrder="6" showInDefault="0" showInWebsite="0" showInStore="0"> + <group id="braintree_section" sortOrder="6"> <group id="braintree" translate="label comment" type="text" showInDefault="1" showInWebsite="1" showInStore="1"> <label>Braintree</label> <comment><![CDATA[Accept credit/debit cards and PayPal in your Magento store.<br/>No setup or monthly fees and your customers never leave your store to complete the purchase.]]></comment> @@ -16,7 +16,7 @@ <frontend_model>Magento\Paypal\Block\Adminhtml\System\Config\Fieldset\Payment</frontend_model> <attribute type="activity_path">payment/braintree/active</attribute> <attribute type="displayIn">recommended_solutions</attribute> - <field id="active" translate="label" type="select" sortOrder="10" showInDefault="1" showInWebsite="1" showInStore="0"> + <field id="active" translate="label" type="select" sortOrder="10" showInDefault="1" showInWebsite="1"> <label>Enable this Solution</label> <source_model>Magento\Config\Model\Config\Source\Yesno</source_model> <config_path>payment/braintree/active</config_path> @@ -24,7 +24,7 @@ <group id="braintree_required"/> </requires> </field> - <field id="active_braintree_paypal" translate="label" type="select" sortOrder="11" showInDefault="1" showInWebsite="1" showInStore="0"> + <field id="active_braintree_paypal" translate="label" type="select" sortOrder="11" showInDefault="1" showInWebsite="1"> <label>Enable PayPal through Braintree</label> <source_model>Magento\Config\Model\Config\Source\Yesno</source_model> <config_path>payment/braintree_paypal/active</config_path> @@ -32,7 +32,7 @@ <group id="braintree_required"/> </requires> </field> - <field id="braintree_cc_vault_active" translate="label" type="select" sortOrder="12" showInDefault="1" showInWebsite="1" showInStore="0"> + <field id="braintree_cc_vault_active" translate="label" type="select" sortOrder="12" showInDefault="1" showInWebsite="1"> <label>Vault Enabled</label> <source_model>Magento\Config\Model\Config\Source\Yesno</source_model> <config_path>payment/braintree_cc_vault/active</config_path> @@ -52,86 +52,86 @@ <label>Title</label> <config_path>payment/braintree/title</config_path> </field> - <field id="environment" translate="label" type="select" sortOrder="30" showInDefault="1" showInWebsite="1" showInStore="0"> + <field id="environment" translate="label" type="select" sortOrder="30" showInDefault="1" showInWebsite="1"> <label>Environment</label> <source_model>Magento\Braintree\Model\Adminhtml\Source\Environment</source_model> <config_path>payment/braintree/environment</config_path> </field> - <field id="payment_action" translate="label" type="select" sortOrder="50" showInDefault="1" showInWebsite="1" showInStore="0"> + <field id="payment_action" translate="label" type="select" sortOrder="50" showInDefault="1" showInWebsite="1"> <label>Payment Action</label> <source_model>Magento\Braintree\Model\Adminhtml\Source\PaymentAction</source_model> <config_path>payment/braintree/payment_action</config_path> </field> - <field id="merchant_id" translate="label" sortOrder="90" showInDefault="1" showInWebsite="1" showInStore="0"> + <field id="merchant_id" translate="label" sortOrder="90" showInDefault="1" showInWebsite="1"> <label>Merchant ID</label> <config_path>payment/braintree/merchant_id</config_path> </field> - <field id="public_key" translate="label" type="obscure" sortOrder="100" showInDefault="1" showInWebsite="1" showInStore="0"> + <field id="public_key" translate="label" type="obscure" sortOrder="100" showInDefault="1" showInWebsite="1"> <label>Public Key</label> <config_path>payment/braintree/public_key</config_path> <backend_model>Magento\Config\Model\Config\Backend\Encrypted</backend_model> </field> - <field id="private_key" translate="label" type="obscure" sortOrder="110" showInDefault="1" showInWebsite="1" showInStore="0"> + <field id="private_key" translate="label" type="obscure" sortOrder="110" showInDefault="1" showInWebsite="1"> <label>Private Key</label> <config_path>payment/braintree/private_key</config_path> <backend_model>Magento\Config\Model\Config\Backend\Encrypted</backend_model> </field> </group> - <group id="braintree_advanced" translate="label" showInDefault="1" showInWebsite="1" showInStore="0" sortOrder="20"> + <group id="braintree_advanced" translate="label" showInDefault="1" showInWebsite="1" sortOrder="20"> <label>Advanced Braintree Settings</label> <frontend_model>Magento\Config\Block\System\Config\Form\Fieldset</frontend_model> - <field id="braintree_cc_vault_title" translate="label" type="text" sortOrder="20" showInDefault="1" showInWebsite="1" showInStore="0"> + <field id="braintree_cc_vault_title" translate="label" type="text" sortOrder="20" showInDefault="1" showInWebsite="1"> <label>Vault Title</label> <config_path>payment/braintree_cc_vault/title</config_path> </field> - <field id="merchant_account_id" translate="label comment" sortOrder="30" showInDefault="1" showInWebsite="1" showInStore="0"> + <field id="merchant_account_id" translate="label comment" sortOrder="30" showInDefault="1" showInWebsite="1"> <label>Merchant Account ID</label> <comment>If you don't specify the merchant account to use to process a transaction, Braintree will process it using your default merchant account.</comment> <config_path>payment/braintree/merchant_account_id</config_path> </field> - <field id="fraudprotection" translate="label comment" type="select" sortOrder="34" showInDefault="1" showInWebsite="1" showInStore="0"> + <field id="fraudprotection" translate="label comment" type="select" sortOrder="34" showInDefault="1" showInWebsite="1"> <label>Advanced Fraud Protection</label> <source_model>Magento\Config\Model\Config\Source\Yesno</source_model> <comment>Be sure to Enable Advanced Fraud Protection in Your Braintree Account in Settings/Processing Section</comment> <config_path>payment/braintree/fraudprotection</config_path> </field> - <field id="debug" translate="label" type="select" sortOrder="40" showInDefault="1" showInWebsite="1" showInStore="0"> + <field id="debug" translate="label" type="select" sortOrder="40" showInDefault="1" showInWebsite="1"> <label>Debug</label> <source_model>Magento\Config\Model\Config\Source\Yesno</source_model> <config_path>payment/braintree/debug</config_path> </field> - <field id="useccv" translate="label comment" type="select" sortOrder="150" showInDefault="1" showInWebsite="1" showInStore="0"> + <field id="useccv" translate="label comment" type="select" sortOrder="150" showInDefault="1" showInWebsite="1"> <label>CVV Verification</label> <source_model>Magento\Config\Model\Config\Source\Yesno</source_model> <comment>Be sure to Enable AVS and/or CVV in Your Braintree Account in Settings/Processing Section.</comment> <config_path>payment/braintree/useccv</config_path> </field> - <field id="cctypes" translate="label" type="multiselect" sortOrder="160" showInDefault="1" showInWebsite="1" showInStore="0"> + <field id="cctypes" translate="label" type="multiselect" sortOrder="160" showInDefault="1" showInWebsite="1"> <label>Credit Card Types</label> <source_model>Magento\Braintree\Model\Adminhtml\Source\CcType</source_model> <config_path>payment/braintree/cctypes</config_path> </field> - <field id="sort_order" translate="label" type="text" sortOrder="230" showInDefault="1" showInWebsite="1" showInStore="0"> + <field id="sort_order" translate="label" type="text" sortOrder="230" showInDefault="1" showInWebsite="1"> <label>Sort Order</label> <frontend_class>validate-number</frontend_class> <config_path>payment/braintree/sort_order</config_path> </field> </group> - <group id="braintree_country_specific" translate="label" showInDefault="1" showInWebsite="1" showInStore="0" sortOrder="30"> + <group id="braintree_country_specific" translate="label" showInDefault="1" showInWebsite="1" sortOrder="30"> <label>Country Specific Settings</label> <frontend_model>Magento\Config\Block\System\Config\Form\Fieldset</frontend_model> - <field id="allowspecific" translate="label" type="allowspecific" sortOrder="200" showInDefault="1" showInWebsite="1" showInStore="0"> + <field id="allowspecific" translate="label" type="allowspecific" sortOrder="200" showInDefault="1" showInWebsite="1"> <label>Payment from Applicable Countries</label> <source_model>Magento\Payment\Model\Config\Source\Allspecificcountries</source_model> <config_path>payment/braintree/allowspecific</config_path> </field> - <field id="specificcountry" translate="label" type="multiselect" sortOrder="210" showInDefault="1" showInWebsite="1" showInStore="0"> + <field id="specificcountry" translate="label" type="multiselect" sortOrder="210" showInDefault="1" showInWebsite="1"> <label>Payment from Specific Countries</label> <source_model>Magento\Braintree\Model\Adminhtml\System\Config\Country</source_model> <can_be_empty>1</can_be_empty> <config_path>payment/braintree/specificcountry</config_path> </field> - <field id="countrycreditcard" translate="label" sortOrder="220" showInDefault="1" showInWebsite="1" showInStore="0"> + <field id="countrycreditcard" translate="label" sortOrder="220" showInDefault="1" showInWebsite="1"> <label>Country Specific Credit Card Types</label> <frontend_model>Magento\Braintree\Block\Adminhtml\Form\Field\CountryCreditCard</frontend_model> <backend_model>Magento\Braintree\Model\Adminhtml\System\Config\CountryCreditCard</backend_model> @@ -146,7 +146,7 @@ <config_path>payment/braintree_paypal/title</config_path> <comment>It is recommended to set this value to "PayPal" per store views.</comment> </field> - <field id="braintree_paypal_vault_active" translate="label" type="select" sortOrder="21" showInDefault="1" showInWebsite="1" showInStore="0"> + <field id="braintree_paypal_vault_active" translate="label" type="select" sortOrder="21" showInDefault="1" showInWebsite="1"> <label>Vault Enabled</label> <source_model>Magento\Config\Model\Config\Source\Yesno</source_model> <config_path>payment/braintree_paypal_vault/active</config_path> @@ -154,7 +154,7 @@ <group id="braintree_required"/> </requires> </field> - <field id="sort_order" translate="label" type="text" sortOrder="30" showInDefault="1" showInWebsite="1" showInStore="0"> + <field id="sort_order" translate="label" type="text" sortOrder="30" showInDefault="1" showInWebsite="1"> <label>Sort Order</label> <frontend_class>validate-number</frontend_class> <config_path>payment/braintree_paypal/sort_order</config_path> @@ -163,68 +163,68 @@ <label>Override Merchant Name</label> <config_path>payment/braintree_paypal/merchant_name_override</config_path> </field> - <field id="payment_action" translate="label" type="select" sortOrder="50" showInDefault="1" showInWebsite="1" showInStore="0"> + <field id="payment_action" translate="label" type="select" sortOrder="50" showInDefault="1" showInWebsite="1"> <label>Payment Action</label> <source_model>Magento\Braintree\Model\Adminhtml\Source\PaymentAction</source_model> <config_path>payment/braintree_paypal/payment_action</config_path> </field> - <field id="allowspecific" translate="label" type="allowspecific" sortOrder="70" showInDefault="1" showInWebsite="1" showInStore="0"> + <field id="allowspecific" translate="label" type="allowspecific" sortOrder="70" showInDefault="1" showInWebsite="1"> <label>Payment from Applicable Countries</label> <source_model>Magento\Payment\Model\Config\Source\Allspecificcountries</source_model> <config_path>payment/braintree_paypal/allowspecific</config_path> </field> - <field id="specificcountry" translate="label" type="multiselect" sortOrder="80" showInDefault="1" showInWebsite="1" showInStore="0"> + <field id="specificcountry" translate="label" type="multiselect" sortOrder="80" showInDefault="1" showInWebsite="1"> <label>Payment from Specific Countries</label> <source_model>Magento\Braintree\Model\Adminhtml\System\Config\Country</source_model> <can_be_empty>1</can_be_empty> <config_path>payment/braintree_paypal/specificcountry</config_path> </field> - <field id="require_billing_address" translate="label comment" type="select" sortOrder="90" showInDefault="1" showInWebsite="1" showInStore="0"> + <field id="require_billing_address" translate="label comment" type="select" sortOrder="90" showInDefault="1" showInWebsite="1"> <label>Require Customer's Billing Address</label> <source_model>Magento\Config\Model\Config\Source\Yesno</source_model> <config_path>payment/braintree_paypal/require_billing_address</config_path> <comment>This feature needs be enabled first for the merchant account through PayPal technical support.</comment> </field> - <field id="allow_shipping_address_override" translate="label" type="select" sortOrder="100" showInDefault="1" showInWebsite="1" showInStore="0"> + <field id="allow_shipping_address_override" translate="label" type="select" sortOrder="100" showInDefault="1" showInWebsite="1"> <label>Allow to Edit Shipping Address Entered During Checkout on PayPal Side</label> <source_model>Magento\Config\Model\Config\Source\Yesno</source_model> <config_path>payment/braintree_paypal/allow_shipping_address_override</config_path> </field> - <field id="debug" translate="label" type="select" sortOrder="110" showInDefault="1" showInWebsite="1" showInStore="0"> + <field id="debug" translate="label" type="select" sortOrder="110" showInDefault="1" showInWebsite="1"> <label>Debug</label> <source_model>Magento\Config\Model\Config\Source\Yesno</source_model> <config_path>payment/braintree_paypal/debug</config_path> </field> - <field id="display_on_shopping_cart" translate="label comment" type="select" sortOrder="120" showInDefault="1" showInWebsite="1" showInStore="0"> + <field id="display_on_shopping_cart" translate="label comment" type="select" sortOrder="120" showInDefault="1" showInWebsite="1"> <label>Display on Shopping Cart</label> <source_model>Magento\Config\Model\Config\Source\Yesno</source_model> <config_path>payment/braintree_paypal/display_on_shopping_cart</config_path> <comment>Also affects mini-shopping cart.</comment> </field> - <field id="skip_order_review" translate="label" type="select" sortOrder="120" showInDefault="1" showInWebsite="1" showInStore="0"> + <field id="skip_order_review" translate="label" type="select" sortOrder="120" showInDefault="1" showInWebsite="1"> <label>Skip Order Review</label> <source_model>Magento\Config\Model\Config\Source\Yesno</source_model> <config_path>payment/braintree_paypal/skip_order_review</config_path> </field> </group> - <group id="braintree_3dsecure" translate="label" showInDefault="1" showInWebsite="1" showInStore="0" sortOrder="41"> + <group id="braintree_3dsecure" translate="label" showInDefault="1" showInWebsite="1" sortOrder="41"> <label>3D Secure Verification Settings</label> <frontend_model>Magento\Config\Block\System\Config\Form\Fieldset</frontend_model> - <field id="verify_3dsecure" translate="label" type="select" sortOrder="150" showInDefault="1" showInWebsite="1" showInStore="0"> + <field id="verify_3dsecure" translate="label" type="select" sortOrder="150" showInDefault="1" showInWebsite="1"> <label>3D Secure Verification</label> <source_model>Magento\Config\Model\Config\Source\Yesno</source_model> <config_path>payment/braintree/verify_3dsecure</config_path> </field> - <field id="threshold_amount" translate="label" type="text" sortOrder="151" showInDefault="1" showInWebsite="1" showInStore="0"> + <field id="threshold_amount" translate="label" type="text" sortOrder="151" showInDefault="1" showInWebsite="1"> <label>Threshold Amount</label> <config_path>payment/braintree/threshold_amount</config_path> </field> - <field id="allowspecific" translate="label" type="allowspecific" sortOrder="152" showInDefault="1" showInWebsite="1" showInStore="0"> + <field id="allowspecific" translate="label" type="allowspecific" sortOrder="152" showInDefault="1" showInWebsite="1"> <label>Verify for Applicable Countries</label> <source_model>Magento\Payment\Model\Config\Source\Allspecificcountries</source_model> <config_path>payment/braintree/verify_all_countries</config_path> </field> - <field id="specificcountry" translate="label" type="multiselect" sortOrder="153" showInDefault="1" showInWebsite="1" showInStore="0"> + <field id="specificcountry" translate="label" type="multiselect" sortOrder="153" showInDefault="1" showInWebsite="1"> <label>Verify for Specific Countries</label> <source_model>Magento\Braintree\Model\Adminhtml\System\Config\Country</source_model> <can_be_empty>1</can_be_empty> diff --git a/app/code/Magento/Braintree/etc/config.xml b/app/code/Magento/Braintree/etc/config.xml index 522d32302168e..b681100dc4dd0 100644 --- a/app/code/Magento/Braintree/etc/config.xml +++ b/app/code/Magento/Braintree/etc/config.xml @@ -15,6 +15,7 @@ <active>0</active> <is_gateway>1</is_gateway> <can_use_checkout>1</can_use_checkout> + <can_sale>1</can_sale> <can_authorize>1</can_authorize> <can_capture>1</can_capture> <can_capture_partial>1</can_capture_partial> diff --git a/app/code/Magento/Braintree/registration.php b/app/code/Magento/Braintree/registration.php index 9a266e9706c7d..1a0d00ec6557d 100644 --- a/app/code/Magento/Braintree/registration.php +++ b/app/code/Magento/Braintree/registration.php @@ -4,6 +4,6 @@ * See COPYING.txt for license details. */ -use \Magento\Framework\Component\ComponentRegistrar; +use Magento\Framework\Component\ComponentRegistrar; ComponentRegistrar::register(ComponentRegistrar::MODULE, 'Magento_Braintree', __DIR__); diff --git a/app/code/Magento/Braintree/view/adminhtml/web/js/braintree.js b/app/code/Magento/Braintree/view/adminhtml/web/js/braintree.js index 845ed03725bfb..393b07fc30403 100644 --- a/app/code/Magento/Braintree/view/adminhtml/web/js/braintree.js +++ b/app/code/Magento/Braintree/view/adminhtml/web/js/braintree.js @@ -309,6 +309,7 @@ define([ self.hostedFieldsInstance.tokenize(function (err, payload) { if (err) { + $('body').trigger('processStop'); self.error($t('Some payment input fields are invalid.')); return false; diff --git a/app/code/Magento/Braintree/view/frontend/web/js/view/payment/3d-secure.js b/app/code/Magento/Braintree/view/frontend/web/js/view/payment/3d-secure.js index 43aec27508ce9..b66725c063414 100644 --- a/app/code/Magento/Braintree/view/frontend/web/js/view/payment/3d-secure.js +++ b/app/code/Magento/Braintree/view/frontend/web/js/view/payment/3d-secure.js @@ -117,7 +117,7 @@ define([ options.bin = context.paymentPayload.details.bin; } - if (shippingAddress) { + if (shippingAddress && this.isValidShippingAddress(shippingAddress)) { options.additionalInformation = { shippingGivenName: shippingAddress.firstname, shippingSurname: shippingAddress.lastname, @@ -206,6 +206,25 @@ define([ } return false; + }, + + /** + * Validate shipping address + * + * @param {Object} shippingAddress + * @return {Boolean} + */ + isValidShippingAddress: function (shippingAddress) { + var isValid = false; + + // check that required fields are not empty + if (shippingAddress.firstname && shippingAddress.lastname && shippingAddress.telephone && + shippingAddress.street && shippingAddress.city && shippingAddress.regionCode && + shippingAddress.postcode && shippingAddress.countryId) { + isValid = true; + } + + return isValid; } }; }); diff --git a/app/code/Magento/Braintree/view/frontend/web/js/view/payment/method-renderer/cc-form.js b/app/code/Magento/Braintree/view/frontend/web/js/view/payment/method-renderer/cc-form.js index 5f06d26e2acfc..21809f186d252 100644 --- a/app/code/Magento/Braintree/view/frontend/web/js/view/payment/method-renderer/cc-form.js +++ b/app/code/Magento/Braintree/view/frontend/web/js/view/payment/method-renderer/cc-form.js @@ -18,7 +18,6 @@ define( 'Magento_Vault/js/view/payment/vault-enabler', 'Magento_Braintree/js/view/payment/kount', 'mage/translate', - 'prototype', 'domReady!' ], function ( @@ -92,7 +91,7 @@ define( }) .then(function (hostedFieldsInstance) { self.hostedFieldsInstance = hostedFieldsInstance; - self.isPlaceOrderActionAllowed(true); + self.isPlaceOrderActionAllowed(false); self.initFormValidationEvents(hostedFieldsInstance); return self.hostedFieldsInstance; diff --git a/app/code/Magento/Braintree/view/frontend/web/js/view/payment/method-renderer/paypal.js b/app/code/Magento/Braintree/view/frontend/web/js/view/payment/method-renderer/paypal.js index ea5200e4ba51f..c0ad38173c52d 100644 --- a/app/code/Magento/Braintree/view/frontend/web/js/view/payment/method-renderer/paypal.js +++ b/app/code/Magento/Braintree/view/frontend/web/js/view/payment/method-renderer/paypal.js @@ -269,6 +269,7 @@ define([ */ onError: function () { self.showError($t('Payment ' + self.getTitle() + ' can\'t be initialized')); + self.reInitPayPal(); } }, self.paypalButtonSelector); }, diff --git a/app/code/Magento/BraintreeGraphQl/Model/BraintreeDataProvider.php b/app/code/Magento/BraintreeGraphQl/Model/BraintreeDataProvider.php index 23ca1d88e3625..cb5c4a31837b4 100644 --- a/app/code/Magento/BraintreeGraphQl/Model/BraintreeDataProvider.php +++ b/app/code/Magento/BraintreeGraphQl/Model/BraintreeDataProvider.php @@ -31,19 +31,6 @@ public function getData(array $args): array __('Required parameter "braintree" for "payment_method" is missing.') ); } - - if (!isset($args[self::PATH_ADDITIONAL_DATA]['payment_method_nonce'])) { - throw new GraphQlInputException( - __('Required parameter "payment_method_nonce" for "braintree" is missing.') - ); - } - - if (!isset($args[self::PATH_ADDITIONAL_DATA]['is_active_payment_token_enabler'])) { - throw new GraphQlInputException( - __('Required parameter "is_active_payment_token_enabler" for "braintree" is missing.') - ); - } - return $args[self::PATH_ADDITIONAL_DATA]; } } diff --git a/app/code/Magento/Bundle/Block/Catalog/Product/View/Type/Bundle.php b/app/code/Magento/Bundle/Block/Catalog/Product/View/Type/Bundle.php index fa488b073f515..2e9b8ba413af3 100644 --- a/app/code/Magento/Bundle/Block/Catalog/Product/View/Type/Bundle.php +++ b/app/code/Magento/Bundle/Block/Catalog/Product/View/Type/Bundle.php @@ -6,20 +6,30 @@ namespace Magento\Bundle\Block\Catalog\Product\View\Type; use Magento\Bundle\Model\Option; +use Magento\Bundle\Model\Product\Price; +use Magento\Bundle\Model\Product\PriceFactory; +use Magento\Bundle\Model\Product\Type; +use Magento\Catalog\Block\Product\Context; +use Magento\Catalog\Block\Product\View\AbstractView; use Magento\Catalog\Model\Product; +use Magento\Catalog\Pricing\Price\FinalPrice; +use Magento\Catalog\Pricing\Price\RegularPrice; +use Magento\CatalogRule\Model\ResourceModel\Product\CollectionProcessor; +use Magento\Framework\App\ObjectManager; use Magento\Framework\DataObject; +use Magento\Framework\Json\EncoderInterface; +use Magento\Framework\Locale\FormatInterface; +use Magento\Framework\Stdlib\ArrayUtils; /** * Catalog bundle product info block * * @api - * @SuppressWarnings(PHPMD.CouplingBetweenObjects) - * @api * @since 100.0.2 + * @SuppressWarnings(PHPMD.CouplingBetweenObjects) */ -class Bundle extends \Magento\Catalog\Block\Product\View\AbstractView +class Bundle extends AbstractView { - /** * @var array */ @@ -33,17 +43,17 @@ class Bundle extends \Magento\Catalog\Block\Product\View\AbstractView protected $catalogProduct; /** - * @var \Magento\Bundle\Model\Product\PriceFactory + * @var PriceFactory */ protected $productPriceFactory; /** - * @var \Magento\Framework\Json\EncoderInterface + * @var EncoderInterface */ protected $jsonEncoder; /** - * @var \Magento\Framework\Locale\FormatInterface + * @var FormatInterface */ protected $localeFormat; @@ -63,22 +73,24 @@ class Bundle extends \Magento\Catalog\Block\Product\View\AbstractView private $optionsPosition = []; /** - * @param \Magento\Catalog\Block\Product\Context $context - * @param \Magento\Framework\Stdlib\ArrayUtils $arrayUtils + * @param Context $context + * @param ArrayUtils $arrayUtils * @param \Magento\Catalog\Helper\Product $catalogProduct - * @param \Magento\Bundle\Model\Product\PriceFactory $productPrice - * @param \Magento\Framework\Json\EncoderInterface $jsonEncoder - * @param \Magento\Framework\Locale\FormatInterface $localeFormat + * @param PriceFactory $productPrice + * @param EncoderInterface $jsonEncoder + * @param FormatInterface $localeFormat * @param array $data + * @param CollectionProcessor|null $catalogRuleProcessor */ public function __construct( - \Magento\Catalog\Block\Product\Context $context, - \Magento\Framework\Stdlib\ArrayUtils $arrayUtils, + Context $context, + ArrayUtils $arrayUtils, \Magento\Catalog\Helper\Product $catalogProduct, - \Magento\Bundle\Model\Product\PriceFactory $productPrice, - \Magento\Framework\Json\EncoderInterface $jsonEncoder, - \Magento\Framework\Locale\FormatInterface $localeFormat, - array $data = [] + PriceFactory $productPrice, + EncoderInterface $jsonEncoder, + FormatInterface $localeFormat, + array $data = [], + ?CollectionProcessor $catalogRuleProcessor = null ) { $this->catalogProduct = $catalogProduct; $this->productPriceFactory = $productPrice; @@ -89,22 +101,8 @@ public function __construct( $arrayUtils, $data ); - } - - /** - * Return catalog rule processor or creates processor if it does not exist - * - * @deprecated 100.2.0 - * @return \Magento\CatalogRule\Model\ResourceModel\Product\CollectionProcessor - */ - private function getCatalogRuleProcessor() - { - if ($this->catalogRuleProcessor === null) { - $this->catalogRuleProcessor = \Magento\Framework\App\ObjectManager::getInstance() - ->get(\Magento\CatalogRule\Model\ResourceModel\Product\CollectionProcessor::class); - } - - return $this->catalogRuleProcessor; + $this->catalogRuleProcessor = $catalogRuleProcessor ?? ObjectManager::getInstance() + ->get(CollectionProcessor::class); } /** @@ -120,7 +118,7 @@ public function getOptions($stripSelection = false) { if (!$this->options) { $product = $this->getProduct(); - /** @var \Magento\Bundle\Model\Product\Type $typeInstance */ + /** @var Type $typeInstance */ $typeInstance = $product->getTypeInstance(); $typeInstance->setStoreFilter($product->getStoreId(), $product); @@ -130,7 +128,7 @@ public function getOptions($stripSelection = false) $typeInstance->getOptionsIds($product), $product ); - $this->getCatalogRuleProcessor()->addPriceData($selectionCollection); + $this->catalogRuleProcessor->addPriceData($selectionCollection); $selectionCollection->addTierPriceData(); $this->options = $optionCollection->appendSelections( @@ -151,10 +149,7 @@ public function getOptions($stripSelection = false) public function hasOptions() { $this->getOptions(); - if (empty($this->options) || !$this->getProduct()->isSalable()) { - return false; - } - return true; + return !(empty($this->options) || !$this->getProduct()->isSalable()); } /** @@ -255,7 +250,7 @@ private function getSelectionItemData(Product $product, Product $selection) ->getOptionSelectionAmount($selection) ->getValue(); - $selection = [ + return [ 'qty' => $qty, 'customQty' => $selection->getSelectionCanChangeQty(), 'optionId' => $selection->getId(), @@ -275,8 +270,6 @@ private function getSelectionItemData(Product $product, Product $selection) 'name' => $selection->getName(), 'canApplyMsrp' => false, ]; - - return $selection; } /** @@ -371,16 +364,16 @@ private function getOptionItemData(Option $option, Product $product, $position) */ private function getConfigData(Product $product, array $options) { - $isFixedPrice = $this->getProduct()->getPriceType() == \Magento\Bundle\Model\Product\Price::PRICE_TYPE_FIXED; + $isFixedPrice = $this->getProduct()->getPriceType() == Price::PRICE_TYPE_FIXED; $productAmount = $product ->getPriceInfo() - ->getPrice(\Magento\Catalog\Pricing\Price\FinalPrice::PRICE_CODE) + ->getPrice(FinalPrice::PRICE_CODE) ->getPriceWithoutOption(); $baseProductAmount = $product ->getPriceInfo() - ->getPrice(\Magento\Catalog\Pricing\Price\RegularPrice::PRICE_CODE) + ->getPrice(RegularPrice::PRICE_CODE) ->getAmount(); $config = [ diff --git a/app/code/Magento/Bundle/Helper/Data.php b/app/code/Magento/Bundle/Helper/Data.php index dbd4a6a039c27..944876cbdad72 100644 --- a/app/code/Magento/Bundle/Helper/Data.php +++ b/app/code/Magento/Bundle/Helper/Data.php @@ -38,6 +38,6 @@ public function getAllowedSelectionTypes() { $configData = $this->config->getType(\Magento\Catalog\Model\Product\Type::TYPE_BUNDLE); - return isset($configData['allowed_selection_types']) ? $configData['allowed_selection_types'] : []; + return $configData['allowed_selection_types'] ?? []; } } diff --git a/app/code/Magento/Bundle/Model/LinkManagement.php b/app/code/Magento/Bundle/Model/LinkManagement.php index 8c85c06c7342d..43a6532a16b2d 100644 --- a/app/code/Magento/Bundle/Model/LinkManagement.php +++ b/app/code/Magento/Bundle/Model/LinkManagement.php @@ -1,36 +1,49 @@ <?php /** - * * Copyright © Magento, Inc. All rights reserved. * See COPYING.txt for license details. */ +declare(strict_types=1); namespace Magento\Bundle\Model; +use Magento\Bundle\Api\Data\LinkInterface; +use Magento\Bundle\Api\Data\LinkInterfaceFactory; +use Magento\Bundle\Api\Data\OptionInterface; +use Magento\Bundle\Api\ProductLinkManagementInterface; +use Magento\Bundle\Model\Product\Type; +use Magento\Bundle\Model\ResourceModel\Bundle; +use Magento\Bundle\Model\ResourceModel\BundleFactory; +use Magento\Bundle\Model\ResourceModel\Option\CollectionFactory; use Magento\Catalog\Api\Data\ProductInterface; use Magento\Catalog\Api\ProductRepositoryInterface; -use Magento\Framework\App\ObjectManager; +use Magento\Catalog\Model\Product; +use Magento\Framework\Api\DataObjectHelper; use Magento\Framework\Exception\CouldNotSaveException; use Magento\Framework\Exception\InputException; use Magento\Framework\EntityManager\MetadataPool; +use Magento\Framework\Exception\NoSuchEntityException; +use Magento\Store\Model\StoreManagerInterface; /** + * Class used to manage bundle products links. + * * @SuppressWarnings(PHPMD.CouplingBetweenObjects) */ -class LinkManagement implements \Magento\Bundle\Api\ProductLinkManagementInterface +class LinkManagement implements ProductLinkManagementInterface { /** - * @var \Magento\Catalog\Api\ProductRepositoryInterface + * @var ProductRepositoryInterface */ protected $productRepository; /** - * @var \Magento\Bundle\Api\Data\LinkInterfaceFactory + * @var LinkInterfaceFactory */ protected $linkFactory; /** - * @var \Magento\Bundle\Model\ResourceModel\BundleFactory + * @var BundleFactory */ protected $bundleFactory; @@ -40,12 +53,17 @@ class LinkManagement implements \Magento\Bundle\Api\ProductLinkManagementInterfa protected $bundleSelection; /** - * @var \Magento\Bundle\Model\ResourceModel\Option\CollectionFactory + * @var CollectionFactory */ protected $optionCollection; /** - * @var \Magento\Framework\Api\DataObjectHelper + * @var StoreManagerInterface + */ + private $storeManager; + + /** + * @var DataObjectHelper */ protected $dataObjectHelper; @@ -56,21 +74,23 @@ class LinkManagement implements \Magento\Bundle\Api\ProductLinkManagementInterfa /** * @param ProductRepositoryInterface $productRepository - * @param \Magento\Bundle\Api\Data\LinkInterfaceFactory $linkFactory - * @param \Magento\Bundle\Model\SelectionFactory $bundleSelection - * @param \Magento\Bundle\Model\ResourceModel\BundleFactory $bundleFactory - * @param \Magento\Bundle\Model\ResourceModel\Option\CollectionFactory $optionCollection - * @param \Magento\Store\Model\StoreManagerInterface $storeManager - * @param \Magento\Framework\Api\DataObjectHelper $dataObjectHelper + * @param LinkInterfaceFactory $linkFactory + * @param SelectionFactory $bundleSelection + * @param BundleFactory $bundleFactory + * @param CollectionFactory $optionCollection + * @param StoreManagerInterface $storeManager + * @param DataObjectHelper $dataObjectHelper + * @param MetadataPool $metadataPool */ public function __construct( ProductRepositoryInterface $productRepository, - \Magento\Bundle\Api\Data\LinkInterfaceFactory $linkFactory, - \Magento\Bundle\Model\SelectionFactory $bundleSelection, - \Magento\Bundle\Model\ResourceModel\BundleFactory $bundleFactory, - \Magento\Bundle\Model\ResourceModel\Option\CollectionFactory $optionCollection, - \Magento\Store\Model\StoreManagerInterface $storeManager, - \Magento\Framework\Api\DataObjectHelper $dataObjectHelper + LinkInterfaceFactory $linkFactory, + SelectionFactory $bundleSelection, + BundleFactory $bundleFactory, + CollectionFactory $optionCollection, + StoreManagerInterface $storeManager, + DataObjectHelper $dataObjectHelper, + MetadataPool $metadataPool ) { $this->productRepository = $productRepository; $this->linkFactory = $linkFactory; @@ -79,15 +99,16 @@ public function __construct( $this->optionCollection = $optionCollection; $this->storeManager = $storeManager; $this->dataObjectHelper = $dataObjectHelper; + $this->metadataPool = $metadataPool; } /** - * {@inheritdoc} + * @inheritDoc */ public function getChildren($productSku, $optionId = null) { $product = $this->productRepository->get($productSku, true); - if ($product->getTypeId() != \Magento\Catalog\Model\Product\Type::TYPE_BUNDLE) { + if ($product->getTypeId() != Product\Type::TYPE_BUNDLE) { throw new InputException(__('This is implemented for bundle products only.')); } @@ -96,7 +117,7 @@ public function getChildren($productSku, $optionId = null) if (!$option->getSelections() || ($optionId !== null && $option->getOptionId() != $optionId)) { continue; } - /** @var \Magento\Catalog\Model\Product $selection */ + /** @var Product $selection */ foreach ($option->getSelections() as $selection) { $childrenList[] = $this->buildLink($selection, $product); } @@ -105,32 +126,33 @@ public function getChildren($productSku, $optionId = null) } /** - * {@inheritdoc} + * @inheritDoc */ - public function addChildByProductSku($sku, $optionId, \Magento\Bundle\Api\Data\LinkInterface $linkedProduct) + public function addChildByProductSku($sku, $optionId, LinkInterface $linkedProduct) { - /** @var \Magento\Catalog\Model\Product $product */ + /** @var Product $product */ $product = $this->productRepository->get($sku, true); return $this->addChild($product, $optionId, $linkedProduct); } /** - * {@inheritdoc} + * @inheritDoc + * * @SuppressWarnings(PHPMD.CyclomaticComplexity) * @SuppressWarnings(PHPMD.NPathComplexity) */ public function saveChild( $sku, - \Magento\Bundle\Api\Data\LinkInterface $linkedProduct + LinkInterface $linkedProduct ) { $product = $this->productRepository->get($sku, true); - if ($product->getTypeId() != \Magento\Catalog\Model\Product\Type::TYPE_BUNDLE) { + if ($product->getTypeId() != Product\Type::TYPE_BUNDLE) { throw new InputException( __('The product with the "%1" SKU isn\'t a bundle product.', [$product->getSku()]) ); } - /** @var \Magento\Catalog\Model\Product $linkProductModel */ + /** @var Product $linkProductModel */ $linkProductModel = $this->productRepository->get($linkedProduct->getSku()); if ($linkProductModel->isComposite()) { throw new InputException(__('The bundle product can\'t contain another composite product.')); @@ -140,7 +162,7 @@ public function saveChild( throw new InputException(__('The product link needs an ID field entered. Enter and try again.')); } - /** @var \Magento\Bundle\Model\Selection $selectionModel */ + /** @var Selection $selectionModel */ $selectionModel = $this->bundleSelection->create(); $selectionModel->load($linkedProduct->getId()); if (!$selectionModel->getId()) { @@ -151,7 +173,7 @@ public function saveChild( ) ); } - $linkField = $this->getMetadataPool()->getMetadata(ProductInterface::class)->getLinkField(); + $linkField = $this->metadataPool->getMetadata(ProductInterface::class)->getLinkField(); $selectionModel = $this->mapProductLinkToSelectionModel( $selectionModel, $linkedProduct, @@ -169,17 +191,21 @@ public function saveChild( } /** - * @param \Magento\Bundle\Model\Selection $selectionModel - * @param \Magento\Bundle\Api\Data\LinkInterface $productLink + * Fill selection model with product link data + * + * @param Selection $selectionModel + * @param LinkInterface $productLink * @param string $linkedProductId * @param string $parentProductId - * @return \Magento\Bundle\Model\Selection + * + * @return Selection + * * @SuppressWarnings(PHPMD.CyclomaticComplexity) * @SuppressWarnings(PHPMD.NPathComplexity) */ protected function mapProductLinkToSelectionModel( - \Magento\Bundle\Model\Selection $selectionModel, - \Magento\Bundle\Api\Data\LinkInterface $productLink, + Selection $selectionModel, + LinkInterface $productLink, $linkedProductId, $parentProductId ) { @@ -214,21 +240,22 @@ protected function mapProductLinkToSelectionModel( } /** - * {@inheritdoc} + * @inheritDoc + * * @SuppressWarnings(PHPMD.CyclomaticComplexity) */ public function addChild( - \Magento\Catalog\Api\Data\ProductInterface $product, + ProductInterface $product, $optionId, - \Magento\Bundle\Api\Data\LinkInterface $linkedProduct + LinkInterface $linkedProduct ) { - if ($product->getTypeId() != \Magento\Catalog\Model\Product\Type::TYPE_BUNDLE) { + if ($product->getTypeId() != Product\Type::TYPE_BUNDLE) { throw new InputException( __('The product with the "%1" SKU isn\'t a bundle product.', $product->getSku()) ); } - $linkField = $this->getMetadataPool()->getMetadata(ProductInterface::class)->getLinkField(); + $linkField = $this->metadataPool->getMetadata(ProductInterface::class)->getLinkField(); $options = $this->optionCollection->create(); @@ -246,10 +273,10 @@ public function addChild( ); } - /* @var $resource \Magento\Bundle\Model\ResourceModel\Bundle */ + /* @var $resource Bundle */ $resource = $this->bundleFactory->create(); $selections = $resource->getSelectionsData($product->getData($linkField)); - /** @var \Magento\Catalog\Model\Product $linkProductModel */ + /** @var Product $linkProductModel */ $linkProductModel = $this->productRepository->get($linkedProduct->getSku()); if ($linkProductModel->isComposite()) { throw new InputException(__('The bundle product can\'t contain another composite product.')); @@ -267,9 +294,9 @@ public function addChild( [$linkedProduct->getSku(), $product->getSku()] ) ); - } else { - return $this->bundleSelection->create()->load($linkProductModel->getEntityId()); } + + return $this->bundleSelection->create()->load($linkProductModel->getEntityId()); } } } @@ -294,13 +321,13 @@ public function addChild( } /** - * {@inheritdoc} + * @inheritDoc */ public function removeChild($sku, $optionId, $childSku) { $product = $this->productRepository->get($sku, true); - if ($product->getTypeId() != \Magento\Catalog\Model\Product\Type::TYPE_BUNDLE) { + if ($product->getTypeId() != Product\Type::TYPE_BUNDLE) { throw new InputException(__('The product with the "%1" SKU isn\'t a bundle product.', $sku)); } @@ -308,7 +335,7 @@ public function removeChild($sku, $optionId, $childSku) $usedProductIds = []; $removeSelectionIds = []; foreach ($this->getOptions($product) as $option) { - /** @var \Magento\Bundle\Model\Selection $selection */ + /** @var Selection $selection */ foreach ($option->getSelections() as $selection) { if ((strcasecmp($selection->getSku(), $childSku) == 0) && ($selection->getOptionId() == $optionId)) { $removeSelectionIds[] = $selection->getSelectionId(); @@ -319,12 +346,12 @@ public function removeChild($sku, $optionId, $childSku) } } if (empty($removeSelectionIds)) { - throw new \Magento\Framework\Exception\NoSuchEntityException( + throw new NoSuchEntityException( __("The bundle product doesn't exist. Review your request and try again.") ); } - $linkField = $this->getMetadataPool()->getMetadata(ProductInterface::class)->getLinkField(); - /* @var $resource \Magento\Bundle\Model\ResourceModel\Bundle */ + $linkField = $this->metadataPool->getMetadata(ProductInterface::class)->getLinkField(); + /* @var $resource Bundle */ $resource = $this->bundleFactory->create(); $resource->dropAllUnneededSelections($product->getData($linkField), $excludeSelectionIds); $resource->removeProductRelations($product->getData($linkField), array_unique($usedProductIds)); @@ -333,26 +360,29 @@ public function removeChild($sku, $optionId, $childSku) } /** - * @param \Magento\Catalog\Model\Product $selection - * @param \Magento\Catalog\Model\Product $product - * @return \Magento\Bundle\Api\Data\LinkInterface + * Build bundle link between two products + * + * @param Product $selection + * @param Product $product + * + * @return LinkInterface */ - private function buildLink(\Magento\Catalog\Model\Product $selection, \Magento\Catalog\Model\Product $product) + private function buildLink(Product $selection, Product $product) { $selectionPriceType = $selectionPrice = null; - /** @var \Magento\Bundle\Model\Selection $product */ + /** @var Selection $product */ if ($product->getPriceType()) { $selectionPriceType = $selection->getSelectionPriceType(); $selectionPrice = $selection->getSelectionPriceValue(); } - /** @var \Magento\Bundle\Api\Data\LinkInterface $link */ + /** @var LinkInterface $link */ $link = $this->linkFactory->create(); $this->dataObjectHelper->populateWithArray( $link, $selection->getData(), - \Magento\Bundle\Api\Data\LinkInterface::class + LinkInterface::class ); $link->setIsDefault($selection->getIsDefault()) ->setId($selection->getSelectionId()) @@ -364,12 +394,15 @@ private function buildLink(\Magento\Catalog\Model\Product $selection, \Magento\C } /** - * @param \Magento\Catalog\Api\Data\ProductInterface $product - * @return \Magento\Bundle\Api\Data\OptionInterface[] + * Get bundle product options + * + * @param ProductInterface $product + * + * @return OptionInterface[] */ - private function getOptions(\Magento\Catalog\Api\Data\ProductInterface $product) + private function getOptions(ProductInterface $product) { - /** @var \Magento\Bundle\Model\Product\Type $productTypeInstance */ + /** @var Type $productTypeInstance */ $productTypeInstance = $product->getTypeInstance(); $productTypeInstance->setStoreFilter( $product->getStoreId(), @@ -383,19 +416,6 @@ private function getOptions(\Magento\Catalog\Api\Data\ProductInterface $product) $product ); - $options = $optionCollection->appendSelections($selectionCollection, true); - return $options; - } - - /** - * Get MetadataPool instance - * @return MetadataPool - */ - private function getMetadataPool() - { - if (!$this->metadataPool) { - $this->metadataPool = ObjectManager::getInstance()->get(MetadataPool::class); - } - return $this->metadataPool; + return $optionCollection->appendSelections($selectionCollection, true); } } diff --git a/app/code/Magento/Bundle/Model/Product/SaveHandler.php b/app/code/Magento/Bundle/Model/Product/SaveHandler.php index 99e8188146bbb..8c2727a71aac1 100644 --- a/app/code/Magento/Bundle/Model/Product/SaveHandler.php +++ b/app/code/Magento/Bundle/Model/Product/SaveHandler.php @@ -3,58 +3,59 @@ * Copyright © Magento, Inc. All rights reserved. * See COPYING.txt for license details. */ +declare(strict_types=1); + namespace Magento\Bundle\Model\Product; +use Magento\Bundle\Api\Data\OptionInterface; use Magento\Bundle\Model\Option\SaveAction; use Magento\Catalog\Api\Data\ProductInterface; use Magento\Bundle\Api\ProductOptionRepositoryInterface as OptionRepository; use Magento\Bundle\Api\ProductLinkManagementInterface; -use Magento\Framework\App\ObjectManager; use Magento\Framework\EntityManager\MetadataPool; use Magento\Framework\EntityManager\Operation\ExtensionInterface; /** - * Class SaveHandler + * Bundle product save handler */ class SaveHandler implements ExtensionInterface { /** * @var OptionRepository */ - protected $optionRepository; + private $optionRepository; /** * @var ProductLinkManagementInterface */ - protected $productLinkManagement; + private $productLinkManagement; /** - * @var MetadataPool + * @var SaveAction */ - private $metadataPool; + private $optionSave; /** - * @var SaveAction + * @var MetadataPool */ - private $optionSave; + private $metadataPool; /** * @param OptionRepository $optionRepository * @param ProductLinkManagementInterface $productLinkManagement * @param SaveAction $optionSave - * @param MetadataPool|null $metadataPool + * @param MetadataPool $metadataPool */ public function __construct( OptionRepository $optionRepository, ProductLinkManagementInterface $productLinkManagement, SaveAction $optionSave, - MetadataPool $metadataPool = null + MetadataPool $metadataPool ) { $this->optionRepository = $optionRepository; $this->productLinkManagement = $productLinkManagement; $this->optionSave = $optionSave; - $this->metadataPool = $metadataPool - ?: ObjectManager::getInstance()->get(MetadataPool::class); + $this->metadataPool = $metadataPool; } /** @@ -69,7 +70,7 @@ public function __construct( */ public function execute($entity, $arguments = []) { - /** @var \Magento\Bundle\Api\Data\OptionInterface[] $bundleProductOptions */ + /** @var OptionInterface[] $bundleProductOptions */ $bundleProductOptions = $entity->getExtensionAttributes()->getBundleProductOptions() ?: []; //Only processing bundle products. if ($entity->getTypeId() !== Type::TYPE_CODE || empty($bundleProductOptions)) { @@ -101,7 +102,8 @@ public function execute($entity, $arguments = []) * Remove option product links * * @param string $entitySku - * @param \Magento\Bundle\Api\Data\OptionInterface $option + * @param OptionInterface $option + * * @return void */ protected function removeOptionLinks($entitySku, $option) @@ -120,6 +122,7 @@ protected function removeOptionLinks($entitySku, $option) * @param object $entity * @param array $options * @param array $newOptionsIds + * * @return void */ private function saveOptions($entity, array $options, array $newOptionsIds = []): void @@ -137,6 +140,7 @@ private function saveOptions($entity, array $options, array $newOptionsIds = []) * Get options ids from array of the options entities. * * @param array $options + * * @return array */ private function getOptionIds(array $options): array @@ -144,7 +148,7 @@ private function getOptionIds(array $options): array $optionIds = []; if (!empty($options)) { - /** @var \Magento\Bundle\Api\Data\OptionInterface $option */ + /** @var OptionInterface $option */ foreach ($options as $option) { if ($option->getOptionId()) { $optionIds[] = $option->getOptionId(); @@ -161,6 +165,7 @@ private function getOptionIds(array $options): array * @param ProductInterface $entity * @param array $existingOptionsIds * @param array $optionIds + * * @return void */ private function processRemovedOptions(ProductInterface $entity, array $existingOptionsIds, array $optionIds): void diff --git a/app/code/Magento/Bundle/Model/ResourceModel/Indexer/Price.php b/app/code/Magento/Bundle/Model/ResourceModel/Indexer/Price.php index 077ebd4422aab..a2fff5739f2f9 100644 --- a/app/code/Magento/Bundle/Model/ResourceModel/Indexer/Price.php +++ b/app/code/Magento/Bundle/Model/ResourceModel/Indexer/Price.php @@ -7,6 +7,7 @@ use Magento\Catalog\Api\Data\ProductInterface; use Magento\Catalog\Model\ResourceModel\Product\Indexer\Price\BasePriceModifier; +use Magento\Framework\DB\Select; use Magento\Framework\Indexer\DimensionalIndexerInterface; use Magento\Framework\EntityManager\MetadataPool; use Magento\Catalog\Model\Indexer\Product\Price\TableMaintainer; @@ -394,8 +395,8 @@ private function calculateBundleOptionPrice($priceTable, $dimensions) $connection = $this->getConnection(); $this->prepareBundleSelectionTable(); - $this->calculateBundleSelectionPrice($dimensions, \Magento\Bundle\Model\Product\Price::PRICE_TYPE_FIXED); - $this->calculateBundleSelectionPrice($dimensions, \Magento\Bundle\Model\Product\Price::PRICE_TYPE_DYNAMIC); + $this->calculateFixedBundleSelectionPrice(); + $this->calculateDynamicBundleSelectionPrice($dimensions); $this->prepareBundleOptionTable(); @@ -426,84 +427,17 @@ private function calculateBundleOptionPrice($priceTable, $dimensions) } /** - * Calculate bundle product selections price by product type + * Get base select for bundle selection price * - * @param array $dimensions - * @param int $priceType - * @return void + * @return Select * @throws \Exception - * @SuppressWarnings(PHPMD.ExcessiveMethodLength) */ - private function calculateBundleSelectionPrice($dimensions, $priceType) + private function getBaseBundleSelectionPriceSelect(): Select { - $connection = $this->getConnection(); - - if ($priceType == \Magento\Bundle\Model\Product\Price::PRICE_TYPE_FIXED) { - $selectionPriceValue = $connection->getCheckSql( - 'bsp.selection_price_value IS NULL', - 'bs.selection_price_value', - 'bsp.selection_price_value' - ); - $selectionPriceType = $connection->getCheckSql( - 'bsp.selection_price_type IS NULL', - 'bs.selection_price_type', - 'bsp.selection_price_type' - ); - $priceExpr = new \Zend_Db_Expr( - $connection->getCheckSql( - $selectionPriceType . ' = 1', - 'ROUND(i.price * (' . $selectionPriceValue . ' / 100),4)', - $connection->getCheckSql( - 'i.special_price > 0 AND i.special_price < 100', - 'ROUND(' . $selectionPriceValue . ' * (i.special_price / 100),4)', - $selectionPriceValue - ) - ) . '* bs.selection_qty' - ); - - $tierExpr = $connection->getCheckSql( - 'i.base_tier IS NOT NULL', - $connection->getCheckSql( - $selectionPriceType . ' = 1', - 'ROUND(i.base_tier - (i.base_tier * (' . $selectionPriceValue . ' / 100)),4)', - $connection->getCheckSql( - 'i.tier_percent > 0', - 'ROUND((1 - i.tier_percent / 100) * ' . $selectionPriceValue . ',4)', - $selectionPriceValue - ) - ) . ' * bs.selection_qty', - 'NULL' - ); - - $priceExpr = $connection->getLeastSql( - [ - $priceExpr, - $connection->getIfNullSql($tierExpr, $priceExpr), - ] - ); - } else { - $price = 'idx.min_price * bs.selection_qty'; - $specialExpr = $connection->getCheckSql( - 'i.special_price > 0 AND i.special_price < 100', - 'ROUND(' . $price . ' * (i.special_price / 100), 4)', - $price - ); - $tierExpr = $connection->getCheckSql( - 'i.tier_percent IS NOT NULL', - 'ROUND((1 - i.tier_percent / 100) * ' . $price . ', 4)', - 'NULL' - ); - $priceExpr = $connection->getLeastSql( - [ - $specialExpr, - $connection->getIfNullSql($tierExpr, $price), - ] - ); - } - $metadata = $this->metadataPool->getMetadata(ProductInterface::class); $linkField = $metadata->getLinkField(); - $select = $connection->select()->from( + + $select = $this->getConnection()->select()->from( ['i' => $this->getBundlePriceTable()], ['entity_id', 'customer_group_id', 'website_id'] )->join( @@ -518,22 +452,173 @@ private function calculateBundleSelectionPrice($dimensions, $priceType) ['bs' => $this->getTable('catalog_product_bundle_selection')], 'bs.option_id = bo.option_id', ['selection_id'] - )->joinLeft( + ); + + return $select; + } + + /** + * Apply selections price for fixed bundles + * + * @return void + * @throws \Exception + */ + private function applyFixedBundleSelectionPrice() + { + $connection = $this->getConnection(); + + $selectionPriceValue = 'bsp.selection_price_value'; + $selectionPriceType = 'bsp.selection_price_type'; + $priceExpr = new \Zend_Db_Expr( + $connection->getCheckSql( + $selectionPriceType . ' = 1', + 'ROUND(i.price * (' . $selectionPriceValue . ' / 100),4)', + $connection->getCheckSql( + 'i.special_price > 0 AND i.special_price < 100', + 'ROUND(' . $selectionPriceValue . ' * (i.special_price / 100),4)', + $selectionPriceValue + ) + ) . '* bs.selection_qty' + ); + $tierExpr = $connection->getCheckSql( + 'i.base_tier IS NOT NULL', + $connection->getCheckSql( + $selectionPriceType . ' = 1', + 'ROUND(i.base_tier - (i.base_tier * (' . $selectionPriceValue . ' / 100)),4)', + $connection->getCheckSql( + 'i.tier_percent > 0', + 'ROUND((1 - i.tier_percent / 100) * ' . $selectionPriceValue . ',4)', + $selectionPriceValue + ) + ) . ' * bs.selection_qty', + 'NULL' + ); + $priceExpr = $connection->getLeastSql( + [ + $priceExpr, + $connection->getIfNullSql($tierExpr, $priceExpr), + ] + ); + + $select = $this->getBaseBundleSelectionPriceSelect(); + $select->joinInner( ['bsp' => $this->getTable('catalog_product_bundle_selection_price')], 'bs.selection_id = bsp.selection_id AND bsp.website_id = i.website_id', - [''] - )->join( + [] + )->where( + 'i.price_type=?', + \Magento\Bundle\Model\Product\Price::PRICE_TYPE_FIXED + )->columns( + [ + 'group_type' => $connection->getCheckSql("bo.type = 'select' OR bo.type = 'radio'", '0', '1'), + 'is_required' => 'bo.required', + 'price' => $priceExpr, + 'tier_price' => $tierExpr, + ] + ); + $query = $select->crossUpdateFromSelect($this->getBundleSelectionTable()); + $connection->query($query); + } + + /** + * Calculate selections price for fixed bundles + * + * @return void + * @throws \Exception + */ + private function calculateFixedBundleSelectionPrice() + { + $connection = $this->getConnection(); + + $selectionPriceValue = 'bs.selection_price_value'; + $selectionPriceType = 'bs.selection_price_type'; + $priceExpr = new \Zend_Db_Expr( + $connection->getCheckSql( + $selectionPriceType . ' = 1', + 'ROUND(i.price * (' . $selectionPriceValue . ' / 100),4)', + $connection->getCheckSql( + 'i.special_price > 0 AND i.special_price < 100', + 'ROUND(' . $selectionPriceValue . ' * (i.special_price / 100),4)', + $selectionPriceValue + ) + ) . '* bs.selection_qty' + ); + $tierExpr = $connection->getCheckSql( + 'i.base_tier IS NOT NULL', + $connection->getCheckSql( + $selectionPriceType . ' = 1', + 'ROUND(i.base_tier - (i.base_tier * (' . $selectionPriceValue . ' / 100)),4)', + $connection->getCheckSql( + 'i.tier_percent > 0', + 'ROUND((1 - i.tier_percent / 100) * ' . $selectionPriceValue . ',4)', + $selectionPriceValue + ) + ) . ' * bs.selection_qty', + 'NULL' + ); + $priceExpr = $connection->getLeastSql( + [ + $priceExpr, + $connection->getIfNullSql($tierExpr, $priceExpr), + ] + ); + + $select = $this->getBaseBundleSelectionPriceSelect(); + $select->where( + 'i.price_type=?', + \Magento\Bundle\Model\Product\Price::PRICE_TYPE_FIXED + )->columns( + [ + 'group_type' => $connection->getCheckSql("bo.type = 'select' OR bo.type = 'radio'", '0', '1'), + 'is_required' => 'bo.required', + 'price' => $priceExpr, + 'tier_price' => $tierExpr, + ] + ); + $query = $select->insertFromSelect($this->getBundleSelectionTable()); + $connection->query($query); + + $this->applyFixedBundleSelectionPrice(); + } + + /** + * Calculate selections price for dynamic bundles + * + * @param array $dimensions + * @return void + * @throws \Exception + */ + private function calculateDynamicBundleSelectionPrice($dimensions) + { + $connection = $this->getConnection(); + + $price = 'idx.min_price * bs.selection_qty'; + $specialExpr = $connection->getCheckSql( + 'i.special_price > 0 AND i.special_price < 100', + 'ROUND(' . $price . ' * (i.special_price / 100), 4)', + $price + ); + $tierExpr = $connection->getCheckSql( + 'i.tier_percent IS NOT NULL', + 'ROUND((1 - i.tier_percent / 100) * ' . $price . ', 4)', + 'NULL' + ); + $priceExpr = $connection->getLeastSql( + [ + $specialExpr, + $connection->getIfNullSql($tierExpr, $price), + ] + ); + + $select = $this->getBaseBundleSelectionPriceSelect(); + $select->join( ['idx' => $this->getMainTable($dimensions)], 'bs.product_id = idx.entity_id AND i.customer_group_id = idx.customer_group_id' . ' AND i.website_id = idx.website_id', [] - )->join( - ['e' => $this->getTable('catalog_product_entity')], - 'bs.product_id = e.entity_id AND e.required_options=0', - [] )->where( 'i.price_type=?', - $priceType + \Magento\Bundle\Model\Product\Price::PRICE_TYPE_DYNAMIC )->columns( [ 'group_type' => $connection->getCheckSql("bo.type = 'select' OR bo.type = 'radio'", '0', '1'), @@ -542,7 +627,6 @@ private function calculateBundleSelectionPrice($dimensions, $priceType) 'tier_price' => $tierExpr, ] ); - $query = $select->insertFromSelect($this->getBundleSelectionTable()); $connection->query($query); } diff --git a/app/code/Magento/Bundle/Model/ResourceModel/Indexer/Stock.php b/app/code/Magento/Bundle/Model/ResourceModel/Indexer/Stock.php index 803fbae067e9d..6808081506dd7 100644 --- a/app/code/Magento/Bundle/Model/ResourceModel/Indexer/Stock.php +++ b/app/code/Magento/Bundle/Model/ResourceModel/Indexer/Stock.php @@ -3,65 +3,65 @@ * Copyright © Magento, Inc. All rights reserved. * See COPYING.txt for license details. */ +declare(strict_types=1); + namespace Magento\Bundle\Model\ResourceModel\Indexer; +use Magento\Catalog\Model\ResourceModel\Indexer\ActiveTableSwitcher; use Magento\CatalogInventory\Model\Indexer\Stock\Action\Full; -use Magento\Framework\App\ObjectManager; +use Magento\CatalogInventory\Model\ResourceModel\Indexer\Stock\DefaultStock; +use Magento\Eav\Model\Config; +use Magento\Framework\App\Config\ScopeConfigInterface; +use Magento\Framework\Indexer\Table\StrategyInterface; +use Magento\Framework\Model\ResourceModel\Db\Context; /** * Bundle Stock Status Indexer Resource Model * - * @author Magento Core Team <core@magentocommerce.com> + * @SuppressWarnings(PHPMD.CouplingBetweenObjects) */ -class Stock extends \Magento\CatalogInventory\Model\ResourceModel\Indexer\Stock\DefaultStock +class Stock extends DefaultStock { /** - * @var \Magento\Catalog\Model\ResourceModel\Indexer\ActiveTableSwitcher + * @var ActiveTableSwitcher */ private $activeTableSwitcher; /** - * @var \Magento\Bundle\Model\ResourceModel\Indexer\StockStatusSelectBuilder + * @var StockStatusSelectBuilder */ private $stockStatusSelectBuilder; /** - * @var \Magento\Bundle\Model\ResourceModel\Indexer\BundleOptionStockDataSelectBuilder + * @var BundleOptionStockDataSelectBuilder */ private $bundleOptionStockDataSelectBuilder; /** - * Class constructor - * - * @param \Magento\Framework\Model\ResourceModel\Db\Context $context - * @param \Magento\Framework\Indexer\Table\StrategyInterface $tableStrategy - * @param \Magento\Eav\Model\Config $eavConfig - * @param \Magento\Framework\App\Config\ScopeConfigInterface $scopeConfig - * @param null $connectionName - * @param \Magento\Catalog\Model\ResourceModel\Indexer\ActiveTableSwitcher|null $activeTableSwitcher - * @param StockStatusSelectBuilder|null $stockStatusSelectBuilder - * @param BundleOptionStockDataSelectBuilder|null $bundleOptionStockDataSelectBuilder + * @param Context $context + * @param StrategyInterface $tableStrategy + * @param Config $eavConfig + * @param ScopeConfigInterface $scopeConfig + * @param ActiveTableSwitcher $activeTableSwitcher + * @param StockStatusSelectBuilder $stockStatusSelectBuilder + * @param BundleOptionStockDataSelectBuilder $bundleOptionStockDataSelectBuilder + * @param string $connectionName */ public function __construct( - \Magento\Framework\Model\ResourceModel\Db\Context $context, - \Magento\Framework\Indexer\Table\StrategyInterface $tableStrategy, - \Magento\Eav\Model\Config $eavConfig, - \Magento\Framework\App\Config\ScopeConfigInterface $scopeConfig, - $connectionName = null, - \Magento\Catalog\Model\ResourceModel\Indexer\ActiveTableSwitcher $activeTableSwitcher = null, - StockStatusSelectBuilder $stockStatusSelectBuilder = null, - BundleOptionStockDataSelectBuilder $bundleOptionStockDataSelectBuilder = null + Context $context, + StrategyInterface $tableStrategy, + Config $eavConfig, + ScopeConfigInterface $scopeConfig, + ActiveTableSwitcher $activeTableSwitcher, + StockStatusSelectBuilder $stockStatusSelectBuilder, + BundleOptionStockDataSelectBuilder $bundleOptionStockDataSelectBuilder, + $connectionName = null ) { parent::__construct($context, $tableStrategy, $eavConfig, $scopeConfig, $connectionName); - $this->activeTableSwitcher = $activeTableSwitcher ?: ObjectManager::getInstance() - ->get(\Magento\Catalog\Model\ResourceModel\Indexer\ActiveTableSwitcher::class); - - $this->stockStatusSelectBuilder = $stockStatusSelectBuilder ?: ObjectManager::getInstance() - ->get(StockStatusSelectBuilder::class); - - $this->bundleOptionStockDataSelectBuilder = $bundleOptionStockDataSelectBuilder ?: ObjectManager::getInstance() - ->get(BundleOptionStockDataSelectBuilder::class); + $this->activeTableSwitcher = $activeTableSwitcher; + $this->stockStatusSelectBuilder = $stockStatusSelectBuilder; + $this->bundleOptionStockDataSelectBuilder = $bundleOptionStockDataSelectBuilder; } /** @@ -79,6 +79,7 @@ protected function _getBundleOptionTable() * * @param int|array $entityIds * @param bool $usePrimaryTable use primary or temporary index table + * * @return $this */ protected function _prepareBundleOptionStockData($entityIds = null, $usePrimaryTable = false) @@ -122,6 +123,7 @@ protected function _prepareBundleOptionStockData($entityIds = null, $usePrimaryT * * @param int|array $entityIds * @param bool $usePrimaryTable use primary or temporary index table + * * @return \Magento\Framework\DB\Select */ protected function _getStockStatusSelect($entityIds = null, $usePrimaryTable = false) @@ -171,6 +173,7 @@ protected function _prepareIndexTable($entityIds = null) * Update Stock status index by product ids * * @param array|int $entityIds + * * @return $this */ protected function _updateIndex($entityIds) diff --git a/app/code/Magento/Bundle/Model/ResourceModel/Option.php b/app/code/Magento/Bundle/Model/ResourceModel/Option.php index 7babd0b349f02..1260233b5bcb3 100644 --- a/app/code/Magento/Bundle/Model/ResourceModel/Option.php +++ b/app/code/Magento/Bundle/Model/ResourceModel/Option.php @@ -3,20 +3,26 @@ * Copyright © Magento, Inc. All rights reserved. * See COPYING.txt for license details. */ +declare(strict_types=1); + namespace Magento\Bundle\Model\ResourceModel; +use Magento\Bundle\Model\Option\Validator; use Magento\Catalog\Api\Data\ProductInterface; -use Magento\Framework\EntityManager\MetadataPool; -use Magento\Framework\App\ObjectManager; +use Magento\Framework\DataObject; use Magento\Framework\EntityManager\EntityManager; +use Magento\Framework\EntityManager\MetadataPool; +use Magento\Framework\Model\AbstractModel; +use Magento\Framework\Model\ResourceModel\Db\AbstractDb; +use Magento\Framework\Model\ResourceModel\Db\Context; /** * Bundle Option Resource Model */ -class Option extends \Magento\Framework\Model\ResourceModel\Db\AbstractDb +class Option extends AbstractDb { /** - * @var \Magento\Bundle\Model\Option\Validator + * @var Validator */ private $validator; @@ -31,22 +37,23 @@ class Option extends \Magento\Framework\Model\ResourceModel\Db\AbstractDb private $entityManager; /** - * @param \Magento\Framework\Model\ResourceModel\Db\Context $context - * @param \Magento\Bundle\Model\Option\Validator $validator + * @param Context $context + * @param Validator $validator + * @param MetadataPool $metadataPool + * @param EntityManager $entityManager * @param string $connectionName - * @param EntityManager|null $entityManager */ public function __construct( - \Magento\Framework\Model\ResourceModel\Db\Context $context, - \Magento\Bundle\Model\Option\Validator $validator, - $connectionName = null, - EntityManager $entityManager = null + Context $context, + Validator $validator, + MetadataPool $metadataPool, + EntityManager $entityManager, + $connectionName = null ) { parent::__construct($context, $connectionName); $this->validator = $validator; - - $this->entityManager = $entityManager - ?: ObjectManager::getInstance()->get(EntityManager::class); + $this->metadataPool = $metadataPool; + $this->entityManager = $entityManager; } /** @@ -60,7 +67,10 @@ protected function _construct() } /** + * Remove selections by option id + * * @param int $optionId + * * @return int */ public function removeOptionSelections($optionId) @@ -74,10 +84,11 @@ public function removeOptionSelections($optionId) /** * After save process * - * @param \Magento\Framework\Model\AbstractModel $object + * @param AbstractModel $object + * * @return $this */ - protected function _afterSave(\Magento\Framework\Model\AbstractModel $object) + protected function _afterSave(AbstractModel $object) { parent::_afterSave($object); @@ -90,7 +101,7 @@ protected function _afterSave(\Magento\Framework\Model\AbstractModel $object) $connection = $this->getConnection(); $connection->delete($this->getTable('catalog_product_bundle_option_value'), $condition); - $data = new \Magento\Framework\DataObject(); + $data = new DataObject(); $data->setOptionId($object->getId()) ->setStoreId($object->getStoreId()) ->setParentProductId($object->getParentId()) @@ -112,10 +123,10 @@ protected function _afterSave(\Magento\Framework\Model\AbstractModel $object) /** * After delete process * - * @param \Magento\Framework\Model\AbstractModel $object + * @param AbstractModel $object * @return $this */ - protected function _afterDelete(\Magento\Framework\Model\AbstractModel $object) + protected function _afterDelete(AbstractModel $object) { parent::_afterDelete($object); @@ -136,6 +147,7 @@ protected function _afterDelete(\Magento\Framework\Model\AbstractModel $object) * * @param int $productId * @param int $storeId + * * @return array */ public function getSearchableData($productId, $storeId) @@ -148,7 +160,7 @@ public function getSearchableData($productId, $storeId) 'option_title_default.title' ); $bind = ['store_id' => $storeId, 'product_id' => $productId]; - $linkField = $this->getMetadataPool()->getMetadata(ProductInterface::class)->getLinkField(); + $linkField = $this->metadataPool->getMetadata(ProductInterface::class)->getLinkField(); $select = $connection->select() ->from( ['opt' => $this->getMainTable()], @@ -180,7 +192,7 @@ public function getSearchableData($productId, $storeId) } /** - * {@inheritdoc} + * @inheritDoc */ public function getValidationRulesBeforeSave() { @@ -188,21 +200,9 @@ public function getValidationRulesBeforeSave() } /** - * Get MetadataPool instance - * @return MetadataPool - */ - private function getMetadataPool() - { - if (!$this->metadataPool) { - $this->metadataPool = ObjectManager::getInstance()->get(MetadataPool::class); - } - return $this->metadataPool; - } - - /** - * {@inheritdoc} + * @inheritDoc */ - public function save(\Magento\Framework\Model\AbstractModel $object) + public function save(AbstractModel $object) { $this->entityManager->save($object); diff --git a/app/code/Magento/Bundle/Model/Sales/Order/Pdf/Items/AbstractItems.php b/app/code/Magento/Bundle/Model/Sales/Order/Pdf/Items/AbstractItems.php index 30e37e54a21db..441bc0dd9de89 100644 --- a/app/code/Magento/Bundle/Model/Sales/Order/Pdf/Items/AbstractItems.php +++ b/app/code/Magento/Bundle/Model/Sales/Order/Pdf/Items/AbstractItems.php @@ -6,8 +6,14 @@ namespace Magento\Bundle\Model\Sales\Order\Pdf\Items; use Magento\Catalog\Model\Product\Type\AbstractType; -use Magento\Framework\App\ObjectManager; +use Magento\Framework\Data\Collection\AbstractDb; +use Magento\Framework\Filesystem; +use Magento\Framework\Filter\FilterManager; +use Magento\Framework\Model\Context; +use Magento\Framework\Model\ResourceModel\AbstractResource; +use Magento\Framework\Registry; use Magento\Framework\Serialize\Serializer\Json; +use Magento\Tax\Helper\Data; /** * Order pdf items renderer @@ -24,30 +30,28 @@ abstract class AbstractItems extends \Magento\Sales\Model\Order\Pdf\Items\Abstra private $serializer; /** - * Constructor - * - * @param \Magento\Framework\Model\Context $context - * @param \Magento\Framework\Registry $registry - * @param \Magento\Tax\Helper\Data $taxData - * @param \Magento\Framework\Filesystem $filesystem - * @param \Magento\Framework\Filter\FilterManager $filterManager - * @param \Magento\Framework\Model\ResourceModel\AbstractResource $resource - * @param \Magento\Framework\Data\Collection\AbstractDb $resourceCollection + * @param Context $context + * @param Registry $registry + * @param Data $taxData + * @param Filesystem $filesystem + * @param FilterManager $filterManager + * @param Json $serializer + * @param AbstractResource $resource + * @param AbstractDb $resourceCollection * @param array $data - * @param \Magento\Framework\Serialize\Serializer\Json $serializer */ public function __construct( - \Magento\Framework\Model\Context $context, - \Magento\Framework\Registry $registry, - \Magento\Tax\Helper\Data $taxData, - \Magento\Framework\Filesystem $filesystem, - \Magento\Framework\Filter\FilterManager $filterManager, - \Magento\Framework\Model\ResourceModel\AbstractResource $resource = null, - \Magento\Framework\Data\Collection\AbstractDb $resourceCollection = null, - array $data = [], - Json $serializer = null + Context $context, + Registry $registry, + Data $taxData, + Filesystem $filesystem, + FilterManager $filterManager, + Json $serializer, + AbstractResource $resource = null, + AbstractDb $resourceCollection = null, + array $data = [] ) { - $this->serializer = $serializer ?: ObjectManager::getInstance()->get(Json::class); + $this->serializer = $serializer; parent::__construct( $context, $registry, diff --git a/app/code/Magento/Bundle/Model/Sales/Order/Pdf/Items/Creditmemo.php b/app/code/Magento/Bundle/Model/Sales/Order/Pdf/Items/Creditmemo.php index 3d7d05396e0fc..1ed3fc76ddee0 100644 --- a/app/code/Magento/Bundle/Model/Sales/Order/Pdf/Items/Creditmemo.php +++ b/app/code/Magento/Bundle/Model/Sales/Order/Pdf/Items/Creditmemo.php @@ -3,10 +3,19 @@ * Copyright © Magento, Inc. All rights reserved. * See COPYING.txt for license details. */ +declare(strict_types=1); + namespace Magento\Bundle\Model\Sales\Order\Pdf\Items; -use Magento\Framework\App\ObjectManager; +use Magento\Framework\Data\Collection\AbstractDb; +use Magento\Framework\Filesystem; +use Magento\Framework\Filter\FilterManager; +use Magento\Framework\Model\Context; +use Magento\Framework\Model\ResourceModel\AbstractResource; +use Magento\Framework\Registry; use Magento\Framework\Serialize\Serializer\Json; +use Magento\Framework\Stdlib\StringUtils; +use Magento\Tax\Helper\Data; /** * Order creditmemo pdf default items renderer @@ -16,36 +25,34 @@ class Creditmemo extends AbstractItems /** * Core string * - * @var \Magento\Framework\Stdlib\StringUtils + * @var StringUtils */ protected $string; /** - * Constructor - * - * @param \Magento\Framework\Model\Context $context - * @param \Magento\Framework\Registry $registry - * @param \Magento\Tax\Helper\Data $taxData - * @param \Magento\Framework\Filesystem $filesystem - * @param \Magento\Framework\Filter\FilterManager $filterManager - * @param \Magento\Framework\Stdlib\StringUtils $string - * @param \Magento\Framework\Model\ResourceModel\AbstractResource $resource - * @param \Magento\Framework\Data\Collection\AbstractDb $resourceCollection + * @param Context $context + * @param Registry $registry + * @param Data $taxData + * @param Filesystem $filesystem + * @param FilterManager $filterManager + * @param Json $serializer + * @param StringUtils $string + * @param AbstractResource $resource + * @param AbstractDb $resourceCollection * @param array $data - * @param \Magento\Framework\Serialize\Serializer\Json|null $serializer * @SuppressWarnings(PHPMD.ExcessiveParameterList) */ public function __construct( - \Magento\Framework\Model\Context $context, - \Magento\Framework\Registry $registry, - \Magento\Tax\Helper\Data $taxData, - \Magento\Framework\Filesystem $filesystem, - \Magento\Framework\Filter\FilterManager $filterManager, - \Magento\Framework\Stdlib\StringUtils $string, - \Magento\Framework\Model\ResourceModel\AbstractResource $resource = null, - \Magento\Framework\Data\Collection\AbstractDb $resourceCollection = null, - array $data = [], - Json $serializer = null + Context $context, + Registry $registry, + Data $taxData, + Filesystem $filesystem, + FilterManager $filterManager, + Json $serializer, + StringUtils $string, + AbstractResource $resource = null, + AbstractDb $resourceCollection = null, + array $data = [] ) { $this->string = $string; parent::__construct( @@ -54,10 +61,10 @@ public function __construct( $taxData, $filesystem, $filterManager, + $serializer, $resource, $resourceCollection, - $data, - $serializer + $data ); } @@ -98,19 +105,17 @@ public function draw() } // draw selection attributes - if ($childItem->getOrderItem()->getParentItem()) { - if ($prevOptionId != $attributes['option_id']) { - $line[0] = [ - 'font' => 'italic', - 'text' => $this->string->split($attributes['option_label'], 38, true, true), - 'feed' => $x, - ]; + if ($childItem->getOrderItem()->getParentItem() && $prevOptionId != $attributes['option_id']) { + $line[0] = [ + 'font' => 'italic', + 'text' => $this->string->split($attributes['option_label'], 38, true, true), + 'feed' => $x, + ]; - $drawItems[$optionId] = ['lines' => [$line], 'height' => 15]; + $drawItems[$optionId] = ['lines' => [$line], 'height' => 15]; - $line = []; - $prevOptionId = $attributes['option_id']; - } + $line = []; + $prevOptionId = $attributes['option_id']; } // draw product titles @@ -152,7 +157,7 @@ public function draw() // draw QTY $text = $childItem->getQty() * 1; $line[] = [ - 'text' => $childItem->getQty() * 1, + 'text' => $text, 'feed' => $x, 'font' => 'bold', 'align' => 'center', @@ -177,40 +182,34 @@ public function draw() // custom options $options = $item->getOrderItem()->getProductOptions(); - if ($options) { - if (isset($options['options'])) { - foreach ($options['options'] as $option) { - $lines = []; - $lines[][] = [ - 'text' => $this->string->split( - $this->filterManager->stripTags($option['label']), - 40, - true, - true - ), - 'font' => 'italic', - 'feed' => $leftBound, - ]; - - if ($option['value']) { - $text = []; - $printValue = isset( - $option['print_value'] - ) ? $option['print_value'] : $this->filterManager->stripTags( - $option['value'] - ); - $values = explode(', ', $printValue); - foreach ($values as $value) { - foreach ($this->string->split($value, 30, true, true) as $subValue) { - $text[] = $subValue; - } - } + if ($options && isset($options['options'])) { + foreach ($options['options'] as $option) { + $lines = []; + $lines[][] = [ + 'text' => $this->string->split( + $this->filterManager->stripTags($option['label']), + 40, + true, + true + ), + 'font' => 'italic', + 'feed' => $leftBound, + ]; - $lines[][] = ['text' => $text, 'feed' => $leftBound + 5]; + if ($option['value']) { + $text = []; + $printValue = $option['print_value'] ?? $this->filterManager->stripTags($option['value']); + $values = explode(', ', $printValue); + foreach ($values as $value) { + foreach ($this->string->split($value, 30, true, true) as $subValue) { + $text[] = $subValue; + } } - $drawItems[] = ['lines' => $lines, 'height' => 15]; + $lines[][] = ['text' => $text, 'feed' => $leftBound + 5]; } + + $drawItems[] = ['lines' => $lines, 'height' => 15]; } } diff --git a/app/code/Magento/Bundle/Model/Sales/Order/Pdf/Items/Invoice.php b/app/code/Magento/Bundle/Model/Sales/Order/Pdf/Items/Invoice.php index 1827c2249dda3..64e9f56dd65bc 100644 --- a/app/code/Magento/Bundle/Model/Sales/Order/Pdf/Items/Invoice.php +++ b/app/code/Magento/Bundle/Model/Sales/Order/Pdf/Items/Invoice.php @@ -3,10 +3,19 @@ * Copyright © Magento, Inc. All rights reserved. * See COPYING.txt for license details. */ +declare(strict_types=1); + namespace Magento\Bundle\Model\Sales\Order\Pdf\Items; -use Magento\Framework\App\ObjectManager; +use Magento\Framework\Data\Collection\AbstractDb; +use Magento\Framework\Filesystem; +use Magento\Framework\Filter\FilterManager; +use Magento\Framework\Model\Context; +use Magento\Framework\Model\ResourceModel\AbstractResource; +use Magento\Framework\Registry; use Magento\Framework\Serialize\Serializer\Json; +use Magento\Framework\Stdlib\StringUtils; +use Magento\Tax\Helper\Data; /** * Order invoice pdf default items renderer @@ -14,36 +23,36 @@ class Invoice extends AbstractItems { /** - * @var \Magento\Framework\Stdlib\StringUtils + * @var StringUtils */ protected $string; /** * Constructor * - * @param \Magento\Framework\Model\Context $context - * @param \Magento\Framework\Registry $registry - * @param \Magento\Tax\Helper\Data $taxData - * @param \Magento\Framework\Filesystem $filesystem - * @param \Magento\Framework\Filter\FilterManager $filterManager - * @param \Magento\Framework\Stdlib\StringUtils $coreString - * @param \Magento\Framework\Model\ResourceModel\AbstractResource $resource - * @param \Magento\Framework\Data\Collection\AbstractDb $resourceCollection + * @param Context $context + * @param Registry $registry + * @param Data $taxData + * @param Filesystem $filesystem + * @param FilterManager $filterManager + * @param StringUtils $coreString + * @param Json $serializer + * @param AbstractResource $resource + * @param AbstractDb $resourceCollection * @param array $data - * @param \Magento\Framework\Serialize\Serializer\Json|null $serializer * @SuppressWarnings(PHPMD.ExcessiveParameterList) */ public function __construct( - \Magento\Framework\Model\Context $context, - \Magento\Framework\Registry $registry, - \Magento\Tax\Helper\Data $taxData, - \Magento\Framework\Filesystem $filesystem, - \Magento\Framework\Filter\FilterManager $filterManager, - \Magento\Framework\Stdlib\StringUtils $coreString, - \Magento\Framework\Model\ResourceModel\AbstractResource $resource = null, - \Magento\Framework\Data\Collection\AbstractDb $resourceCollection = null, - array $data = [], - Json $serializer = null + Context $context, + Registry $registry, + Data $taxData, + Filesystem $filesystem, + FilterManager $filterManager, + StringUtils $coreString, + Json $serializer, + AbstractResource $resource = null, + AbstractDb $resourceCollection = null, + array $data = [] ) { $this->string = $coreString; parent::__construct( @@ -52,10 +61,10 @@ public function __construct( $taxData, $filesystem, $filterManager, + $serializer, $resource, $resourceCollection, - $data, - $serializer + $data ); } @@ -94,19 +103,17 @@ public function draw() $drawItems[$optionId] = ['lines' => [], 'height' => 15]; } - if ($childItem->getOrderItem()->getParentItem()) { - if ($prevOptionId != $attributes['option_id']) { - $line[0] = [ - 'font' => 'italic', - 'text' => $this->string->split($attributes['option_label'], 45, true, true), - 'feed' => 35, - ]; + if ($childItem->getOrderItem()->getParentItem() && $prevOptionId != $attributes['option_id']) { + $line[0] = [ + 'font' => 'italic', + 'text' => $this->string->split($attributes['option_label'], 45, true, true), + 'feed' => 35, + ]; - $drawItems[$optionId] = ['lines' => [$line], 'height' => 15]; + $drawItems[$optionId] = ['lines' => [$line], 'height' => 15]; - $line = []; - $prevOptionId = $attributes['option_id']; - } + $line = []; + $prevOptionId = $attributes['option_id']; } /* in case Product name is longer than 80 chars - it is written in a few lines */ @@ -146,40 +153,34 @@ public function draw() // custom options $options = $item->getOrderItem()->getProductOptions(); - if ($options) { - if (isset($options['options'])) { - foreach ($options['options'] as $option) { - $lines = []; - $lines[][] = [ - 'text' => $this->string->split( - $this->filterManager->stripTags($option['label']), - 40, - true, - true - ), - 'font' => 'italic', - 'feed' => 35, - ]; - - if ($option['value']) { - $text = []; - $printValue = isset( - $option['print_value'] - ) ? $option['print_value'] : $this->filterManager->stripTags( - $option['value'] - ); - $values = explode(', ', $printValue); - foreach ($values as $value) { - foreach ($this->string->split($value, 30, true, true) as $subValue) { - $text[] = $subValue; - } + if ($options && isset($options['options'])) { + foreach ($options['options'] as $option) { + $lines = []; + $lines[][] = [ + 'text' => $this->string->split( + $this->filterManager->stripTags($option['label']), + 40, + true, + true + ), + 'font' => 'italic', + 'feed' => 35, + ]; + + if ($option['value']) { + $text = []; + $printValue = $option['print_value'] ?? $this->filterManager->stripTags($option['value']); + $values = explode(', ', $printValue); + foreach ($values as $value) { + foreach ($this->string->split($value, 30, true, true) as $subValue) { + $text[] = $subValue; } - - $lines[][] = ['text' => $text, 'feed' => 40]; } - $drawItems[] = ['lines' => $lines, 'height' => 15]; + $lines[][] = ['text' => $text, 'feed' => 40]; } + + $drawItems[] = ['lines' => $lines, 'height' => 15]; } } diff --git a/app/code/Magento/Bundle/Model/Sales/Order/Pdf/Items/Shipment.php b/app/code/Magento/Bundle/Model/Sales/Order/Pdf/Items/Shipment.php index 08ccf0618d03e..8a293b63541a2 100644 --- a/app/code/Magento/Bundle/Model/Sales/Order/Pdf/Items/Shipment.php +++ b/app/code/Magento/Bundle/Model/Sales/Order/Pdf/Items/Shipment.php @@ -3,10 +3,19 @@ * Copyright © Magento, Inc. All rights reserved. * See COPYING.txt for license details. */ +declare(strict_types=1); + namespace Magento\Bundle\Model\Sales\Order\Pdf\Items; -use Magento\Framework\App\ObjectManager; +use Magento\Framework\Data\Collection\AbstractDb; +use Magento\Framework\Filesystem; +use Magento\Framework\Filter\FilterManager; +use Magento\Framework\Model\Context; +use Magento\Framework\Model\ResourceModel\AbstractResource; +use Magento\Framework\Registry; use Magento\Framework\Serialize\Serializer\Json; +use Magento\Framework\Stdlib\StringUtils; +use Magento\Tax\Helper\Data; /** * Order shipment pdf items renderer @@ -14,36 +23,34 @@ class Shipment extends AbstractItems { /** - * @var \Magento\Framework\Stdlib\StringUtils + * @var StringUtils */ protected $string; /** - * Constructor - * - * @param \Magento\Framework\Model\Context $context - * @param \Magento\Framework\Registry $registry - * @param \Magento\Tax\Helper\Data $taxData - * @param \Magento\Framework\Filesystem $filesystem - * @param \Magento\Framework\Filter\FilterManager $filterManager - * @param \Magento\Framework\Stdlib\StringUtils $string - * @param \Magento\Framework\Model\ResourceModel\AbstractResource $resource - * @param \Magento\Framework\Data\Collection\AbstractDb $resourceCollection + * @param Context $context + * @param Registry $registry + * @param Data $taxData + * @param Filesystem $filesystem + * @param FilterManager $filterManager + * @param StringUtils $string + * @param Json $serializer + * @param AbstractResource $resource + * @param AbstractDb $resourceCollection * @param array $data - * @param \Magento\Framework\Serialize\Serializer\Json|null $serializer * @SuppressWarnings(PHPMD.ExcessiveParameterList) */ public function __construct( - \Magento\Framework\Model\Context $context, - \Magento\Framework\Registry $registry, - \Magento\Tax\Helper\Data $taxData, - \Magento\Framework\Filesystem $filesystem, - \Magento\Framework\Filter\FilterManager $filterManager, - \Magento\Framework\Stdlib\StringUtils $string, - \Magento\Framework\Model\ResourceModel\AbstractResource $resource = null, - \Magento\Framework\Data\Collection\AbstractDb $resourceCollection = null, - array $data = [], - Json $serializer = null + Context $context, + Registry $registry, + Data $taxData, + Filesystem $filesystem, + FilterManager $filterManager, + StringUtils $string, + Json $serializer, + AbstractResource $resource = null, + AbstractDb $resourceCollection = null, + array $data = [] ) { $this->string = $string; parent::__construct( @@ -52,10 +59,10 @@ public function __construct( $taxData, $filesystem, $filterManager, + $serializer, $resource, $resourceCollection, - $data, - $serializer + $data ); } @@ -95,24 +102,22 @@ public function draw() $drawItems[$optionId] = ['lines' => [], 'height' => 15]; } - if ($childItem->getParentItem()) { - if ($prevOptionId != $attributes['option_id']) { - $line[0] = [ - 'font' => 'italic', - 'text' => $this->string->split($attributes['option_label'], 60, true, true), - 'feed' => 60, - ]; + if ($childItem->getParentItem() && $prevOptionId != $attributes['option_id']) { + $line[0] = [ + 'font' => 'italic', + 'text' => $this->string->split($attributes['option_label'], 60, true, true), + 'feed' => 60, + ]; - $drawItems[$optionId] = ['lines' => [$line], 'height' => 15]; + $drawItems[$optionId] = ['lines' => [$line], 'height' => 15]; - $line = []; + $line = []; - $prevOptionId = $attributes['option_id']; - } + $prevOptionId = $attributes['option_id']; } - if ($this->isShipmentSeparately() && $childItem->getParentItem() || - !$this->isShipmentSeparately() && !$childItem->getParentItem() + if (($this->isShipmentSeparately() && $childItem->getParentItem()) || + (!$this->isShipmentSeparately() && !$childItem->getParentItem()) ) { if (isset($shipItems[$childItem->getId()])) { $qty = $shipItems[$childItem->getId()]->getQty() * 1; @@ -153,40 +158,34 @@ public function draw() // custom options $options = $item->getOrderItem()->getProductOptions(); - if ($options) { - if (isset($options['options'])) { - foreach ($options['options'] as $option) { - $lines = []; - $lines[][] = [ - 'text' => $this->string->split( - $this->filterManager->stripTags($option['label']), - 70, - true, - true - ), - 'font' => 'italic', - 'feed' => 60, - ]; - - if ($option['value']) { - $text = []; - $printValue = isset( - $option['print_value'] - ) ? $option['print_value'] : $this->filterManager->stripTags( - $option['value'] - ); - $values = explode(', ', $printValue); - foreach ($values as $value) { - foreach ($this->string->split($value, 50, true, true) as $subValue) { - $text[] = $subValue; - } + if ($options && isset($options['options'])) { + foreach ($options['options'] as $option) { + $lines = []; + $lines[][] = [ + 'text' => $this->string->split( + $this->filterManager->stripTags($option['label']), + 70, + true, + true + ), + 'font' => 'italic', + 'feed' => 60, + ]; + + if ($option['value']) { + $text = []; + $printValue = $option['print_value'] ?? $this->filterManager->stripTags($option['value']); + $values = explode(', ', $printValue); + foreach ($values as $value) { + foreach ($this->string->split($value, 50, true, true) as $subValue) { + $text[] = $subValue; } - - $lines[][] = ['text' => $text, 'feed' => 65]; } - $drawItems[] = ['lines' => $lines, 'height' => 15]; + $lines[][] = ['text' => $text, 'feed' => 65]; } + + $drawItems[] = ['lines' => $lines, 'height' => 15]; } } diff --git a/app/code/Magento/Bundle/Observer/AppendUpsellProductsObserver.php b/app/code/Magento/Bundle/Observer/AppendUpsellProductsObserver.php index 63ae3cef7a33e..fe5eaebbf7f25 100644 --- a/app/code/Magento/Bundle/Observer/AppendUpsellProductsObserver.php +++ b/app/code/Magento/Bundle/Observer/AppendUpsellProductsObserver.php @@ -7,6 +7,9 @@ use Magento\Framework\Event\ObserverInterface; +/** + * Class adds bundle products into up-sell products collection + */ class AppendUpsellProductsObserver implements ObserverInterface { /** @@ -98,9 +101,15 @@ public function execute(\Magento\Framework\Event\Observer $observer) } /* @var $bundleCollection \Magento\Catalog\Model\ResourceModel\Product\Collection */ - $bundleCollection = $product->getCollection()->addAttributeToSelect( + $bundleCollection = $product->getCollection(); + $bundleCollection->addAttributeToSelect( $this->config->getProductAttributes() - )->addStoreFilter()->addMinimalPrice()->addFinalPrice()->addTaxPercents()->setVisibility( + ); + $bundleCollection->addStoreFilter(); + $bundleCollection->addMinimalPrice(); + $bundleCollection->addFinalPrice(); + $bundleCollection->addTaxPercents(); + $bundleCollection->setVisibility( $this->productVisibility->getVisibleInCatalogIds() ); diff --git a/app/code/Magento/Bundle/Observer/InitOptionRendererObserver.php b/app/code/Magento/Bundle/Observer/InitOptionRendererObserver.php index 008ab1d84ac37..377b8a7dea466 100644 --- a/app/code/Magento/Bundle/Observer/InitOptionRendererObserver.php +++ b/app/code/Magento/Bundle/Observer/InitOptionRendererObserver.php @@ -5,20 +5,25 @@ */ namespace Magento\Bundle\Observer; +use Magento\Bundle\Helper\Catalog\Product\Configuration; +use Magento\Framework\Event\Observer; use Magento\Framework\Event\ObserverInterface; +/** + * Initiates render options + */ class InitOptionRendererObserver implements ObserverInterface { /** * Initialize product options renderer with bundle specific params * - * @param \Magento\Framework\Event\Observer $observer + * @param Observer $observer * @return $this */ - public function execute(\Magento\Framework\Event\Observer $observer) + public function execute(Observer $observer) { $block = $observer->getBlock(); - $block->addOptionsRenderCfg('bundle', \Magento\Bundle\Helper\Catalog\Product\Configuration::class); + $block->addOptionsRenderCfg('bundle', Configuration::class); return $this; } } diff --git a/app/code/Magento/Bundle/Pricing/Adjustment/Calculator.php b/app/code/Magento/Bundle/Pricing/Adjustment/Calculator.php index 04a6ee0bd459b..3051394eaf512 100644 --- a/app/code/Magento/Bundle/Pricing/Adjustment/Calculator.php +++ b/app/code/Magento/Bundle/Pricing/Adjustment/Calculator.php @@ -3,14 +3,18 @@ * Copyright © Magento, Inc. All rights reserved. * See COPYING.txt for license details. */ +declare(strict_types=1); namespace Magento\Bundle\Pricing\Adjustment; +use Magento\Bundle\Model\Option; use Magento\Bundle\Model\Product\Price; use Magento\Bundle\Pricing\Price\BundleSelectionFactory; +use Magento\Bundle\Pricing\Price\BundleSelectionPrice; use Magento\Catalog\Model\Product; use Magento\Framework\Pricing\Adjustment\Calculator as CalculatorBase; use Magento\Framework\Pricing\Amount\AmountFactory; +use Magento\Framework\Pricing\Amount\AmountInterface; use Magento\Framework\Pricing\SaleableInterface; use Magento\Framework\Pricing\PriceCurrencyInterface; use Magento\Store\Model\Store; @@ -51,7 +55,7 @@ class Calculator implements BundleCalculatorInterface protected $priceCurrency; /** - * @var \Magento\Framework\Pricing\Amount\AmountInterface[] + * @var AmountInterface[] */ private $optionAmount = []; @@ -66,7 +70,7 @@ class Calculator implements BundleCalculatorInterface * @param BundleSelectionFactory $bundleSelectionFactory * @param TaxHelper $taxHelper * @param PriceCurrencyInterface $priceCurrency - * @param SelectionPriceListProviderInterface|null $selectionPriceListProvider + * @param SelectionPriceListProviderInterface $selectionPriceListProvider */ public function __construct( CalculatorBase $calculator, @@ -74,7 +78,7 @@ public function __construct( BundleSelectionFactory $bundleSelectionFactory, TaxHelper $taxHelper, PriceCurrencyInterface $priceCurrency, - SelectionPriceListProviderInterface $selectionPriceListProvider = null + SelectionPriceListProviderInterface $selectionPriceListProvider ) { $this->calculator = $calculator; $this->amountFactory = $amountFactory; @@ -91,7 +95,9 @@ public function __construct( * @param SaleableInterface $saleableItem * @param null|bool|string|array $exclude * @param null|array $context - * @return \Magento\Framework\Pricing\Amount\AmountInterface + * + * @return AmountInterface + * * @SuppressWarnings(PHPMD.UnusedFormalParameter) */ public function getAmount($amount, SaleableInterface $saleableItem, $exclude = null, $context = []) @@ -105,7 +111,8 @@ public function getAmount($amount, SaleableInterface $saleableItem, $exclude = n * @param float $amount * @param Product $saleableItem * @param null|bool|string|array $exclude - * @return \Magento\Framework\Pricing\Amount\AmountInterface + * + * @return AmountInterface */ public function getMinRegularAmount($amount, Product $saleableItem, $exclude = null) { @@ -118,7 +125,8 @@ public function getMinRegularAmount($amount, Product $saleableItem, $exclude = n * @param float $amount * @param Product $saleableItem * @param null|bool|string|array $exclude - * @return \Magento\Framework\Pricing\Amount\AmountInterface + * + * @return AmountInterface */ public function getMaxAmount($amount, Product $saleableItem, $exclude = null) { @@ -131,7 +139,8 @@ public function getMaxAmount($amount, Product $saleableItem, $exclude = null) * @param float $amount * @param Product $saleableItem * @param null|bool|string|array $exclude - * @return \Magento\Framework\Pricing\Amount\AmountInterface + * + * @return AmountInterface */ public function getMaxRegularAmount($amount, Product $saleableItem, $exclude = null) { @@ -146,7 +155,8 @@ public function getMaxRegularAmount($amount, Product $saleableItem, $exclude = n * @param bool $searchMin * @param float $baseAmount * @param bool $useRegularPrice - * @return \Magento\Framework\Pricing\Amount\AmountInterface + * + * @return AmountInterface */ public function getOptionsAmount( Product $saleableItem, @@ -173,7 +183,8 @@ public function getOptionsAmount( * * @param float $amount * @param Product $saleableItem - * @return \Magento\Framework\Pricing\Amount\AmountInterface|void + * + * @return AmountInterface|void */ public function getAmountWithoutOption($amount, Product $saleableItem) { @@ -194,29 +205,13 @@ public function getAmountWithoutOption($amount, Product $saleableItem) */ protected function getSelectionAmounts(Product $bundleProduct, $searchMin, $useRegularPrice = false) { - return $this->getSelectionPriceListProvider()->getPriceList($bundleProduct, $searchMin, $useRegularPrice); - } - - /** - * Get selection price list provider. - * - * @return SelectionPriceListProviderInterface - * @deprecated 100.2.0 - */ - private function getSelectionPriceListProvider() - { - if (null === $this->selectionPriceListProvider) { - $this->selectionPriceListProvider = \Magento\Framework\App\ObjectManager::getInstance() - ->get(SelectionPriceListProviderInterface::class); - } - - return $this->selectionPriceListProvider; + return $this->selectionPriceListProvider->getPriceList($bundleProduct, $searchMin, $useRegularPrice); } /** * Check this option if it should be skipped * - * @param \Magento\Bundle\Model\Option $option + * @param Option $option * @param bool $canSkipRequiredOption * @return bool * @deprecated 100.2.0 @@ -265,9 +260,9 @@ protected function getBundleOptions(Product $saleableItem) * * @param float $basePriceValue * @param Product $bundleProduct - * @param \Magento\Bundle\Pricing\Price\BundleSelectionPrice[] $selectionPriceList + * @param BundleSelectionPrice[] $selectionPriceList * @param null|bool|string|array $exclude - * @return \Magento\Framework\Pricing\Amount\AmountInterface + * @return AmountInterface */ public function calculateBundleAmount($basePriceValue, $bundleProduct, $selectionPriceList, $exclude = null) { @@ -282,14 +277,14 @@ public function calculateBundleAmount($basePriceValue, $bundleProduct, $selectio * * @param float $basePriceValue * @param Product $bundleProduct - * @param \Magento\Bundle\Pricing\Price\BundleSelectionPrice[] $selectionPriceList + * @param BundleSelectionPrice[] $selectionPriceList * @param null|bool|string|array $exclude - * @return \Magento\Framework\Pricing\Amount\AmountInterface + * @return AmountInterface */ protected function calculateFixedBundleAmount($basePriceValue, $bundleProduct, $selectionPriceList, $exclude) { $fullAmount = $basePriceValue; - /** @var $option \Magento\Bundle\Model\Option */ + /** @var $option Option */ foreach ($selectionPriceList as $selectionPrice) { $fullAmount += ($selectionPrice->getValue() * $selectionPrice->getQuantity()); } @@ -301,9 +296,9 @@ protected function calculateFixedBundleAmount($basePriceValue, $bundleProduct, $ * * @param float $basePriceValue * @param Product $bundleProduct - * @param \Magento\Bundle\Pricing\Price\BundleSelectionPrice[] $selectionPriceList + * @param BundleSelectionPrice[] $selectionPriceList * @param null|bool|string|array $exclude - * @return \Magento\Framework\Pricing\Amount\AmountInterface + * @return AmountInterface * @SuppressWarnings(PHPMD.CyclomaticComplexity) */ protected function calculateDynamicBundleAmount($basePriceValue, $bundleProduct, $selectionPriceList, $exclude) @@ -328,7 +323,7 @@ protected function calculateDynamicBundleAmount($basePriceValue, $bundleProduct, $store = $bundleProduct->getStore(); $roundingMethod = $this->taxHelper->getCalculationAlgorithm($store); foreach ($amountList as $amountInfo) { - /** @var \Magento\Framework\Pricing\Amount\AmountInterface $itemAmount */ + /** @var AmountInterface $itemAmount */ $itemAmount = $amountInfo['amount']; $qty = $amountInfo['quantity']; @@ -366,10 +361,10 @@ protected function calculateDynamicBundleAmount($basePriceValue, $bundleProduct, /** * Create selection price list for the retrieved options * - * @param \Magento\Bundle\Model\Option $option + * @param Option $option * @param Product $bundleProduct * @param bool $useRegularPrice - * @return \Magento\Bundle\Pricing\Price\BundleSelectionPrice[] + * @return BundleSelectionPrice[] */ public function createSelectionPriceList($option, $bundleProduct, $useRegularPrice = false) { @@ -399,10 +394,10 @@ public function createSelectionPriceList($option, $bundleProduct, $useRegularPri /** * Find minimal or maximal price for existing options * - * @param \Magento\Bundle\Model\Option $option - * @param \Magento\Bundle\Pricing\Price\BundleSelectionPrice[] $selectionPriceList + * @param Option $option + * @param BundleSelectionPrice[] $selectionPriceList * @param bool $searchMin - * @return \Magento\Bundle\Pricing\Price\BundleSelectionPrice[] + * @return BundleSelectionPrice[] * @SuppressWarnings(PHPMD.CyclomaticComplexity) */ public function processOptions($option, $selectionPriceList, $searchMin = true) diff --git a/app/code/Magento/Bundle/Pricing/Price/BundleOptionPrice.php b/app/code/Magento/Bundle/Pricing/Price/BundleOptionPrice.php index 1c724caaa28d8..a76112da0ca4e 100644 --- a/app/code/Magento/Bundle/Pricing/Price/BundleOptionPrice.php +++ b/app/code/Magento/Bundle/Pricing/Price/BundleOptionPrice.php @@ -3,12 +3,17 @@ * Copyright © Magento, Inc. All rights reserved. * See COPYING.txt for license details. */ +declare(strict_types=1); + namespace Magento\Bundle\Pricing\Price; +use Magento\Bundle\Model\ResourceModel\Option\Collection; +use Magento\Bundle\Model\Selection; use Magento\Bundle\Pricing\Adjustment\BundleCalculatorInterface; use Magento\Catalog\Model\Product; +use Magento\Framework\Pricing\Amount\AmountInterface; use Magento\Framework\Pricing\Price\AbstractPrice; -use Magento\Framework\App\ObjectManager; +use Magento\Framework\Pricing\PriceCurrencyInterface; /** * Bundle option price model with final price. @@ -25,12 +30,6 @@ class BundleOptionPrice extends AbstractPrice implements BundleOptionPriceInterf */ protected $calculator; - /** - * @var BundleSelectionFactory - * @deprecated - */ - protected $selectionFactory; - /** * @var float|bool|null */ @@ -45,26 +44,23 @@ class BundleOptionPrice extends AbstractPrice implements BundleOptionPriceInterf * @param Product $saleableItem * @param float $quantity * @param BundleCalculatorInterface $calculator - * @param \Magento\Framework\Pricing\PriceCurrencyInterface $priceCurrency - * @param BundleSelectionFactory $bundleSelectionFactory - * @param BundleOptions|null $bundleOptions + * @param PriceCurrencyInterface $priceCurrency + * @param BundleOptions $bundleOptions */ public function __construct( Product $saleableItem, $quantity, BundleCalculatorInterface $calculator, - \Magento\Framework\Pricing\PriceCurrencyInterface $priceCurrency, - BundleSelectionFactory $bundleSelectionFactory, - BundleOptions $bundleOptions = null + PriceCurrencyInterface $priceCurrency, + BundleOptions $bundleOptions ) { - $this->selectionFactory = $bundleSelectionFactory; parent::__construct($saleableItem, $quantity, $calculator, $priceCurrency); $this->product->setQty($this->quantity); - $this->bundleOptions = $bundleOptions ?: ObjectManager::getInstance()->get(BundleOptions::class); + $this->bundleOptions = $bundleOptions; } /** - * {@inheritdoc} + * @inheritDoc */ public function getValue() { @@ -75,25 +71,10 @@ public function getValue() return $this->value; } - /** - * Getter for maximal price of options. - * - * @return bool|float - * @deprecated - */ - public function getMaxValue() - { - if (null === $this->maximalPrice) { - $this->maximalPrice = $this->bundleOptions->calculateOptions($this->product, false); - } - - return $this->maximalPrice; - } - /** * Get Options with attached Selections collection. * - * @return \Magento\Bundle\Model\ResourceModel\Option\Collection + * @return Collection */ public function getOptions() { @@ -103,8 +84,9 @@ public function getOptions() /** * Get selection amount. * - * @param \Magento\Bundle\Model\Selection $selection - * @return \Magento\Framework\Pricing\Amount\AmountInterface + * @param Selection $selection + * + * @return AmountInterface */ public function getOptionSelectionAmount($selection) { @@ -119,6 +101,7 @@ public function getOptionSelectionAmount($selection) * Calculate maximal or minimal options value. * * @param bool $searchMin + * * @return bool|float */ protected function calculateOptions($searchMin = true) @@ -129,7 +112,7 @@ protected function calculateOptions($searchMin = true) /** * Get minimal amount of bundle price with options * - * @return \Magento\Framework\Pricing\Amount\AmountInterface + * @return AmountInterface */ public function getAmount() { diff --git a/app/code/Magento/Bundle/Pricing/Price/FinalPrice.php b/app/code/Magento/Bundle/Pricing/Price/FinalPrice.php index c41f6a4868ab1..66f618c0fcfa5 100644 --- a/app/code/Magento/Bundle/Pricing/Price/FinalPrice.php +++ b/app/code/Magento/Bundle/Pricing/Price/FinalPrice.php @@ -3,15 +3,17 @@ * Copyright © Magento, Inc. All rights reserved. * See COPYING.txt for license details. */ +declare(strict_types=1); namespace Magento\Bundle\Pricing\Price; -use Magento\Catalog\Model\Product; -use Magento\Framework\Pricing\Adjustment\CalculatorInterface; -use Magento\Catalog\Pricing\Price\CustomOptionPrice; use Magento\Bundle\Model\Product\Price; -use Magento\Framework\App\ObjectManager; use Magento\Catalog\Api\ProductCustomOptionRepositoryInterface; +use Magento\Catalog\Model\Product; +use Magento\Catalog\Pricing\Price\CustomOptionPrice; +use Magento\Framework\Pricing\Adjustment\CalculatorInterface; +use Magento\Framework\Pricing\Amount\AmountInterface; +use Magento\Framework\Pricing\PriceCurrencyInterface; /** * Final price model @@ -19,27 +21,27 @@ class FinalPrice extends \Magento\Catalog\Pricing\Price\FinalPrice implements FinalPriceInterface { /** - * @var \Magento\Framework\Pricing\Amount\AmountInterface + * @var AmountInterface */ protected $maximalPrice; /** - * @var \Magento\Framework\Pricing\Amount\AmountInterface + * @var AmountInterface */ protected $minimalPrice; /** - * @var \Magento\Framework\Pricing\Amount\AmountInterface + * @var AmountInterface */ protected $priceWithoutOption; /** - * @var \Magento\Bundle\Pricing\Price\BundleOptionPrice + * @var BundleOptionPrice */ protected $bundleOptionPrice; /** - * @var \Magento\Catalog\Api\ProductCustomOptionRepositoryInterface + * @var ProductCustomOptionRepositoryInterface */ private $productOptionRepository; @@ -47,15 +49,18 @@ class FinalPrice extends \Magento\Catalog\Pricing\Price\FinalPrice implements Fi * @param Product $saleableItem * @param float $quantity * @param CalculatorInterface $calculator - * @param \Magento\Framework\Pricing\PriceCurrencyInterface $priceCurrency + * @param PriceCurrencyInterface $priceCurrency + * @param ProductCustomOptionRepositoryInterface $productOptionRepository */ public function __construct( Product $saleableItem, $quantity, CalculatorInterface $calculator, - \Magento\Framework\Pricing\PriceCurrencyInterface $priceCurrency + PriceCurrencyInterface $priceCurrency, + ProductCustomOptionRepositoryInterface $productOptionRepository ) { parent::__construct($saleableItem, $quantity, $calculator, $priceCurrency); + $this->productOptionRepository = $productOptionRepository; } /** @@ -65,49 +70,33 @@ public function __construct( */ public function getValue() { - return parent::getValue() + - $this->getBundleOptionPrice()->getValue(); + return parent::getValue() + $this->getBundleOptionPrice()->getValue(); } /** * Returns max price * - * @return \Magento\Framework\Pricing\Amount\AmountInterface + * @return AmountInterface */ public function getMaximalPrice() { if (!$this->maximalPrice) { $price = $this->getBasePrice()->getValue(); if ($this->product->getPriceType() == Price::PRICE_TYPE_FIXED) { - /** @var \Magento\Catalog\Pricing\Price\CustomOptionPrice $customOptionPrice */ + /** @var CustomOptionPrice $customOptionPrice */ $customOptionPrice = $this->priceInfo->getPrice(CustomOptionPrice::PRICE_CODE); $price += $customOptionPrice->getCustomOptionRange(false); } $this->maximalPrice = $this->calculator->getMaxAmount($price, $this->product); } - return $this->maximalPrice; - } - /** - * Return ProductCustomOptionRepository - * - * @return ProductCustomOptionRepositoryInterface - * @deprecated 100.1.0 - */ - private function getProductOptionRepository() - { - if (!$this->productOptionRepository) { - $this->productOptionRepository = ObjectManager::getInstance()->get( - ProductCustomOptionRepositoryInterface::class - ); - } - return $this->productOptionRepository; + return $this->maximalPrice; } /** * Returns min price * - * @return \Magento\Framework\Pricing\Amount\AmountInterface + * @return AmountInterface */ public function getMinimalPrice() { @@ -117,7 +106,7 @@ public function getMinimalPrice() /** * Returns price amount * - * @return \Magento\Framework\Pricing\Amount\AmountInterface + * @return AmountInterface */ public function getAmount() { @@ -125,7 +114,7 @@ public function getAmount() $price = parent::getValue(); if ($this->product->getPriceType() == Price::PRICE_TYPE_FIXED) { $this->loadProductCustomOptions(); - /** @var \Magento\Catalog\Pricing\Price\CustomOptionPrice $customOptionPrice */ + /** @var CustomOptionPrice $customOptionPrice */ $customOptionPrice = $this->priceInfo->getPrice(CustomOptionPrice::PRICE_CODE); $price += $customOptionPrice->getCustomOptionRange(true); } @@ -143,7 +132,7 @@ private function loadProductCustomOptions() { if (!$this->product->getOptions()) { $options = []; - foreach ($this->getProductOptionRepository()->getProductOptions($this->product) as $option) { + foreach ($this->productOptionRepository->getProductOptions($this->product) as $option) { $option->setProduct($this->product); $options[] = $option; } @@ -152,9 +141,9 @@ private function loadProductCustomOptions() } /** - * get bundle product price without any option + * Get bundle product price without any option * - * @return \Magento\Framework\Pricing\Amount\AmountInterface + * @return AmountInterface */ public function getPriceWithoutOption() { @@ -167,7 +156,7 @@ public function getPriceWithoutOption() /** * Returns option price * - * @return \Magento\Bundle\Pricing\Price\BundleOptionPrice + * @return BundleOptionPrice */ protected function getBundleOptionPrice() { diff --git a/app/code/Magento/Bundle/Test/Mftf/ActionGroup/AdminCreateApiDynamicBundleProductActionGroup.xml b/app/code/Magento/Bundle/Test/Mftf/ActionGroup/AdminCreateApiDynamicBundleProductActionGroup.xml index 7e17822710a18..952ae69d887d4 100644 --- a/app/code/Magento/Bundle/Test/Mftf/ActionGroup/AdminCreateApiDynamicBundleProductActionGroup.xml +++ b/app/code/Magento/Bundle/Test/Mftf/ActionGroup/AdminCreateApiDynamicBundleProductActionGroup.xml @@ -61,5 +61,7 @@ <requiredEntity createDataKey="createBundleOption1_2"/> <requiredEntity createDataKey="simpleProduct4"/> </createData> + + <magentoCron stepKey="runCronIndex" groups="index"/> </actionGroup> </actionGroups> diff --git a/app/code/Magento/Bundle/Test/Mftf/ActionGroup/AdminCreateApiDynamicBundleProductAllOptionTypesActionGroup.xml b/app/code/Magento/Bundle/Test/Mftf/ActionGroup/AdminCreateApiDynamicBundleProductAllOptionTypesActionGroup.xml index c9f5c52c05736..84b0dc1449878 100644 --- a/app/code/Magento/Bundle/Test/Mftf/ActionGroup/AdminCreateApiDynamicBundleProductAllOptionTypesActionGroup.xml +++ b/app/code/Magento/Bundle/Test/Mftf/ActionGroup/AdminCreateApiDynamicBundleProductAllOptionTypesActionGroup.xml @@ -70,5 +70,6 @@ <requiredEntity createDataKey="createBundleRadioButtonsOption"/> <requiredEntity createDataKey="simpleProduct2"/> </createData> + <magentoCron stepKey="runCronIndex" groups="index"/> </actionGroup> </actionGroups> diff --git a/app/code/Magento/Bundle/Test/Mftf/ActionGroup/AdminCreateApiFixedBundleProductActionGroup.xml b/app/code/Magento/Bundle/Test/Mftf/ActionGroup/AdminCreateApiFixedBundleProductActionGroup.xml index 13c31cf2e7127..bfeb5c6bcb4b9 100644 --- a/app/code/Magento/Bundle/Test/Mftf/ActionGroup/AdminCreateApiFixedBundleProductActionGroup.xml +++ b/app/code/Magento/Bundle/Test/Mftf/ActionGroup/AdminCreateApiFixedBundleProductActionGroup.xml @@ -61,5 +61,6 @@ <requiredEntity createDataKey="createBundleOption1_2"/> <requiredEntity createDataKey="simpleProduct4"/> </createData> + <magentoCron stepKey="runCronIndex" groups="index"/> </actionGroup> </actionGroups> diff --git a/app/code/Magento/Bundle/Test/Mftf/Metadata/bundle_link-meta.xml b/app/code/Magento/Bundle/Test/Mftf/Metadata/BundleLinkMeta.xml similarity index 100% rename from app/code/Magento/Bundle/Test/Mftf/Metadata/bundle_link-meta.xml rename to app/code/Magento/Bundle/Test/Mftf/Metadata/BundleLinkMeta.xml diff --git a/app/code/Magento/Bundle/Test/Mftf/Metadata/bundle_option-meta.xml b/app/code/Magento/Bundle/Test/Mftf/Metadata/BundleOptionMeta.xml similarity index 100% rename from app/code/Magento/Bundle/Test/Mftf/Metadata/bundle_option-meta.xml rename to app/code/Magento/Bundle/Test/Mftf/Metadata/BundleOptionMeta.xml diff --git a/app/code/Magento/Bundle/Test/Mftf/Metadata/bundle_options-meta.xml b/app/code/Magento/Bundle/Test/Mftf/Metadata/BundleOptionsMeta.xml similarity index 100% rename from app/code/Magento/Bundle/Test/Mftf/Metadata/bundle_options-meta.xml rename to app/code/Magento/Bundle/Test/Mftf/Metadata/BundleOptionsMeta.xml diff --git a/app/code/Magento/Bundle/Test/Mftf/Test/AdminAddBundleItemsTest.xml b/app/code/Magento/Bundle/Test/Mftf/Test/AdminAddBundleItemsTest.xml index bf428440a24eb..4a78aeb752ca7 100644 --- a/app/code/Magento/Bundle/Test/Mftf/Test/AdminAddBundleItemsTest.xml +++ b/app/code/Magento/Bundle/Test/Mftf/Test/AdminAddBundleItemsTest.xml @@ -14,7 +14,7 @@ <stories value="Create/Edit bundle product in Admin"/> <title value="Admin should be able to add/edit bundle items when creating/editing a bundle product"/> <description value="Admin should be able to add/edit bundle items when creating/editing a bundle product"/> - <severity value="MAJOR"/> + <severity value="BLOCKER"/> <testCaseId value="MC-223"/> <group value="Bundle"/> </annotations> @@ -25,8 +25,9 @@ <createData entity="SimpleProduct2" stepKey="simpleProduct1"/> <createData entity="SimpleProduct2" stepKey="simpleProduct2"/> <createData entity="SimpleProduct2" stepKey="simpleProduct3"/> + <magentoCron stepKey="runCronIndex" groups="index"/> <!--Admin login--> - <actionGroup stepKey="loginToAdminPanel" ref="LoginAsAdmin"/> + <actionGroup stepKey="loginToAdminPanel" ref="AdminLoginActionGroup"/> </before> <after> <!--Deleting data--> @@ -36,7 +37,7 @@ <deleteData createDataKey="simpleProduct2" stepKey="deleteSimpleProduct2"/> <deleteData createDataKey="simpleProduct3" stepKey="deleteSimpleProduct3"/> <!--Logging out--> - <actionGroup ref="logout" stepKey="logout"/> + <actionGroup ref="AdminLogoutActionGroup" stepKey="logout"/> </after> <!--Go to bundle product creation page--> <amOnPage url="{{AdminProductCreatePage.url(BundleProduct.set, BundleProduct.type)}}" stepKey="goToBundleProductCreationPage"/> diff --git a/app/code/Magento/Bundle/Test/Mftf/Test/AdminAddBundleProductToCartFromWishListPageTest.xml b/app/code/Magento/Bundle/Test/Mftf/Test/AdminAddBundleProductToCartFromWishListPageTest.xml index b2d3c376d9b5a..9722835b201c1 100644 --- a/app/code/Magento/Bundle/Test/Mftf/Test/AdminAddBundleProductToCartFromWishListPageTest.xml +++ b/app/code/Magento/Bundle/Test/Mftf/Test/AdminAddBundleProductToCartFromWishListPageTest.xml @@ -21,7 +21,7 @@ <before> <!-- Login as Admin --> <comment userInput="Login as Admin" stepKey="commentLoginAsAdmin"/> - <actionGroup ref="LoginAsAdmin" stepKey="loginAsAdmin"/> + <actionGroup ref="AdminLoginActionGroup" stepKey="loginAsAdmin"/> <!-- Create customer on Storefront and bundle product --> <comment userInput="Create customer on Storefront and bundle product" stepKey="commentCreateData"/> <createData entity="SimpleSubCategory" stepKey="createCategory"/> @@ -50,17 +50,21 @@ <requiredEntity createDataKey="bundleOption"/> <requiredEntity createDataKey="createSimpleProduct2"/> </createData> - <amOnPage url="{{AdminProductEditPage.url($$createProduct.id$$)}}" stepKey="goToProductEditPage"/> + <magentoCron stepKey="runCronIndex" groups="index"/> + + <actionGroup ref="AdminProductPageOpenByIdActionGroup" stepKey="goToProductEditPage"> + <argument name="productId" value="$$createProduct.id$$"/> + </actionGroup> <actionGroup ref="SaveProductFormActionGroup" stepKey="saveProduct"/> </before> <after> <!-- Delete created data --> <comment userInput="Delete created data" stepKey="commentDeleteCreatedData"/> <deleteData createDataKey="createCustomerViaTheStorefront" stepKey="deleteCustomerViaTheStorefront"/> - <deleteData createDataKey="createProduct" stepKey="deleteProduct" /> + <deleteData createDataKey="createProduct" stepKey="deleteProduct"/> <!-- Log out --> <comment userInput="Log out" stepKey="commentLogOut"/> - <actionGroup ref="logout" stepKey="logout"/> + <actionGroup ref="AdminLogoutActionGroup" stepKey="logout"/> </after> <!-- Login to the Storefront as created customer --> <comment userInput="Login to the Storefront as created customer" stepKey="commentLoginAsCustomer"/> @@ -74,8 +78,8 @@ <actionGroup ref="StorefrontCustomerAddProductToWishlistActionGroup" stepKey="addToWishlistProduct"> <argument name="productVar" value="$$createProduct$$"/> </actionGroup> - <moveMouseOver selector="{{StorefrontCategoryProductSection.ProductInfoByName($$createProduct.name$$)}}" stepKey="moveMouseOverProduct" /> - <click selector="{{StorefrontCategoryProductSection.ProductAddToCartByName($$createProduct.name$$)}}" stepKey="clickAddToCart" /> + <moveMouseOver selector="{{StorefrontCategoryProductSection.ProductInfoByName($$createProduct.name$$)}}" stepKey="moveMouseOverProduct"/> + <click selector="{{StorefrontCategoryProductSection.ProductAddToCartByName($$createProduct.name$$)}}" stepKey="clickAddToCart"/> <waitForPageLoad stepKey="waitForProductBundlePage"/> <!-- See error message --> <comment userInput="See error message" stepKey="commentSeeErrorMessage"/> diff --git a/app/code/Magento/Bundle/Test/Mftf/Test/AdminAddDefaultImageBundleProductTest.xml b/app/code/Magento/Bundle/Test/Mftf/Test/AdminAddDefaultImageBundleProductTest.xml index 3770e47079c98..55038b0c68c44 100644 --- a/app/code/Magento/Bundle/Test/Mftf/Test/AdminAddDefaultImageBundleProductTest.xml +++ b/app/code/Magento/Bundle/Test/Mftf/Test/AdminAddDefaultImageBundleProductTest.xml @@ -19,9 +19,10 @@ <group value="Bundle"/> </annotations> <before> - <actionGroup ref="LoginAsAdmin" stepKey="loginAsAdmin"/> + <actionGroup ref="AdminLoginActionGroup" stepKey="loginAsAdmin"/> <createData entity="SimpleProduct2" stepKey="simpleProduct1"/> <createData entity="SimpleProduct2" stepKey="simpleProduct2"/> + <magentoCron stepKey="runCronIndex" groups="index"/> </before> <after> <amOnPage url="{{AdminLogoutPage.url}}" stepKey="amOnLogoutPage"/> diff --git a/app/code/Magento/Bundle/Test/Mftf/Test/AdminAddDefaultVideoBundleProductTest.xml b/app/code/Magento/Bundle/Test/Mftf/Test/AdminAddDefaultVideoBundleProductTest.xml index 66443e130ed08..3caa2c9681f55 100644 --- a/app/code/Magento/Bundle/Test/Mftf/Test/AdminAddDefaultVideoBundleProductTest.xml +++ b/app/code/Magento/Bundle/Test/Mftf/Test/AdminAddDefaultVideoBundleProductTest.xml @@ -17,10 +17,14 @@ <severity value="MAJOR"/> <testCaseId value="MC-110"/> <group value="Bundle"/> + <skip> + <issueId value="MC-32197"/> + </skip> </annotations> <before> <createData entity="SimpleProduct2" stepKey="simpleProduct1"/> <createData entity="SimpleProduct2" stepKey="simpleProduct2"/> + <magentoCron stepKey="runCronIndex" groups="index"/> </before> <after> <deleteData createDataKey="simpleProduct1" stepKey="deleteSimpleProduct1"/> diff --git a/app/code/Magento/Bundle/Test/Mftf/Test/AdminAssociateBundleProductToWebsitesTest.xml b/app/code/Magento/Bundle/Test/Mftf/Test/AdminAssociateBundleProductToWebsitesTest.xml index b58637cf2e81d..30922839a191d 100644 --- a/app/code/Magento/Bundle/Test/Mftf/Test/AdminAssociateBundleProductToWebsitesTest.xml +++ b/app/code/Magento/Bundle/Test/Mftf/Test/AdminAssociateBundleProductToWebsitesTest.xml @@ -15,7 +15,7 @@ <title value="Admin should be able to associate bundle product to websites"/> <description value="Admin should be able to associate bundle product to websites"/> <testCaseId value="MC-3344"/> - <severity value="CRITICAL"/> + <severity value="BLOCKER"/> <group value="bundle"/> <group value="catalog"/> </annotations> @@ -46,7 +46,7 @@ <magentoCLI command="indexer:reindex" stepKey="reindex"/> <!-- Login as admin --> - <actionGroup ref="LoginAsAdmin" stepKey="loginAsAdmin"/> + <actionGroup ref="AdminLoginActionGroup" stepKey="loginAsAdmin"/> <!--Create website--> <actionGroup ref="AdminCreateWebsiteActionGroup" stepKey="createSecondWebsite"> @@ -82,7 +82,7 @@ <actionGroup ref="NavigateToAndResetProductGridToDefaultViewActionGroup" stepKey="resetProductGridFilter"/> <!-- Admin logout --> - <actionGroup ref="logout" stepKey="adminLogout"/> + <actionGroup ref="AdminLogoutActionGroup" stepKey="adminLogout"/> </after> <!-- Open product page and assign grouped project to second website --> diff --git a/app/code/Magento/Bundle/Test/Mftf/Test/AdminAttributeSetSelectionTest.xml b/app/code/Magento/Bundle/Test/Mftf/Test/AdminAttributeSetSelectionTest.xml index c8977cbae1957..06a05e7a29cd9 100644 --- a/app/code/Magento/Bundle/Test/Mftf/Test/AdminAttributeSetSelectionTest.xml +++ b/app/code/Magento/Bundle/Test/Mftf/Test/AdminAttributeSetSelectionTest.xml @@ -19,7 +19,7 @@ <group value="Bundle"/> </annotations> <before> - <actionGroup ref="LoginAsAdmin" stepKey="loginAsAdmin"/> + <actionGroup ref="AdminLoginActionGroup" stepKey="loginAsAdmin"/> </before> <after> <amOnPage url="{{AdminLogoutPage.url}}" stepKey="amOnLogoutPage"/> diff --git a/app/code/Magento/Bundle/Test/Mftf/Test/AdminBasicBundleProductAttributesTest.xml b/app/code/Magento/Bundle/Test/Mftf/Test/AdminBasicBundleProductAttributesTest.xml index fcfd80ac8533c..7b65b2db0f160 100644 --- a/app/code/Magento/Bundle/Test/Mftf/Test/AdminBasicBundleProductAttributesTest.xml +++ b/app/code/Magento/Bundle/Test/Mftf/Test/AdminBasicBundleProductAttributesTest.xml @@ -18,7 +18,7 @@ <group value="Bundle"/> </annotations> <before> - <actionGroup ref="LoginAsAdmin" stepKey="loginAsAdmin"/> + <actionGroup ref="AdminLoginActionGroup" stepKey="loginAsAdmin"/> </before> <after> <amOnPage url="{{AdminLogoutPage.url}}" stepKey="amOnLogoutPage"/> diff --git a/app/code/Magento/Bundle/Test/Mftf/Test/AdminCreateAndEditBundleProductOptionsNegativeTest.xml b/app/code/Magento/Bundle/Test/Mftf/Test/AdminCreateAndEditBundleProductOptionsNegativeTest.xml new file mode 100644 index 0000000000000..82da228e040dc --- /dev/null +++ b/app/code/Magento/Bundle/Test/Mftf/Test/AdminCreateAndEditBundleProductOptionsNegativeTest.xml @@ -0,0 +1,122 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + /** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +--> + +<tests xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/testSchema.xsd"> + <test name="AdminCreateAndEditBundleProductOptionsNegativeTest"> + <annotations> + <features value="Bundle"/> + <stories value="Modify bundle product in Admin"/> + <title value="Admin should be able to remove any bundle option a bundle product"/> + <description value="Admin should be able to set/edit other product information when creating/editing a bundle product"/> + <severity value="MAJOR"/> + <testCaseId value="MC-224"/> + <skip> + <issueId value="https://github.com/magento/magento2/issues/25468"/> + </skip> + <group value="Catalog"/> + </annotations> + <before> + <!-- Create a Website --> + <createData entity="customWebsite" stepKey="createWebsite"/> + + <!-- Create first simple product for a bundle option --> + <createData entity="SimpleProduct2" stepKey="createFirstSimpleProduct"/> + + <!-- Create second simple product for a bundle option --> + <createData entity="SimpleProduct2" stepKey="createSecondSimpleProduct"/> + + <!-- Login as admin --> + <actionGroup ref="AdminLoginActionGroup" stepKey="loginAsAdmin"/> + </before> + <after> + <!-- Delete the simple product --> + <deleteData createDataKey="createFirstSimpleProduct" stepKey="deleteFirstSimpleProduct"/> + + <!-- Delete the simple product --> + <deleteData createDataKey="createSecondSimpleProduct" stepKey="deleteSecondSimpleProduct"/> + + <!-- Delete a Website --> + <actionGroup ref="AdminDeleteWebsiteActionGroup" stepKey="deleteWebsite"> + <argument name="websiteName" value="Second Website"/> + </actionGroup> + + <!-- Log out --> + <actionGroup ref="AdminLogoutActionGroup" stepKey="logout"/> + </after> + + <!-- Create new bundle product --> + <actionGroup ref="GoToSpecifiedCreateProductPageActionGroup" stepKey="createBundleProduct"> + <argument name="productType" value="bundle"/> + </actionGroup> + + <!-- Fill all main fields --> + <actionGroup ref="FillMainBundleProductFormActionGroup" stepKey="fillMainProductFields"/> + + <!-- Add first bundle option to the product --> + <actionGroup ref="AddBundleOptionWithTwoProductsActionGroup" stepKey="addFirstBundleOption"> + <argument name="x" value="0"/> + <argument name="n" value="1"/> + <argument name="prodOneSku" value="$$createFirstSimpleProduct.sku$$"/> + <argument name="prodTwoSku" value="$$createSecondSimpleProduct.sku$$"/> + <argument name="optionTitle" value="{{RadioButtonsOption.title}}"/> + <argument name="inputType" value="{{RadioButtonsOption.type}}"/> + </actionGroup> + + <!-- Add second bundle option to the product --> + <actionGroup ref="AddBundleOptionWithTwoProductsActionGroup" stepKey="addSecondBundleOption"> + <argument name="x" value="1"/> + <argument name="n" value="2"/> + <argument name="prodOneSku" value="$$createFirstSimpleProduct.sku$$"/> + <argument name="prodTwoSku" value="$$createSecondSimpleProduct.sku$$"/> + <argument name="optionTitle" value="{{CheckboxOption.title}}"/> + <argument name="inputType" value="{{CheckboxOption.type}}"/> + </actionGroup> + + <!-- Add third bundle option to the product --> + <actionGroup ref="AddBundleOptionWithTwoProductsActionGroup" stepKey="addThirdBundleOption"> + <argument name="x" value="2"/> + <argument name="n" value="3"/> + <argument name="prodOneSku" value="$$createFirstSimpleProduct.sku$$"/> + <argument name="prodTwoSku" value="$$createSecondSimpleProduct.sku$$"/> + <argument name="optionTitle" value="{{RadioButtonsOption.title}}"/> + <argument name="inputType" value="{{RadioButtonsOption.type}}"/> + </actionGroup> + + <!-- Set product in created Website --> + <actionGroup ref="AdminAssignProductInWebsiteActionGroup" stepKey="selectProductInWebsites"> + <argument name="website" value="$createWebsite.website[name]$"/> + </actionGroup> + + <!-- Save product form --> + <actionGroup ref="SaveProductFormActionGroup" stepKey="saveWithThreeOptions"/> + + <!-- Open created product --> + <actionGroup ref="SearchForProductOnBackendActionGroup" stepKey="searchForSimpleProduct"> + <argument name="product" value="BundleProduct"/> + </actionGroup> + <actionGroup ref="OpenEditProductOnBackendActionGroup" stepKey="openEditProduct"> + <argument name="product" value="BundleProduct"/> + </actionGroup> + + <!-- Remove second option --> + <actionGroup ref="DeleteBundleOptionByIndexActionGroup" stepKey="deleteSecondOption"> + <argument name="deleteIndex" value="1"/> + </actionGroup> + + <!-- Save product form --> + <actionGroup ref="SaveProductFormActionGroup" stepKey="clickSaveProduct"/> + <click selector="{{AdminProductFormActionSection.saveButton}}" stepKey="saveWithTwoOptions"/> + <seeElement selector="{{AdminCategoryMessagesSection.SuccessMessage}}" stepKey="messageYouSavedTheProductIsShown"/> + + <!-- Delete created bundle product --> + <actionGroup ref="DeleteProductUsingProductGridActionGroup" stepKey="deleteProduct"> + <argument name="product" value="BundleProduct"/> + </actionGroup> + </test> +</tests> diff --git a/app/code/Magento/Bundle/Test/Mftf/Test/AdminCreateAndEditBundleProductSettingsTest.xml b/app/code/Magento/Bundle/Test/Mftf/Test/AdminCreateAndEditBundleProductSettingsTest.xml index a80d5f040f825..26d7dddbcc378 100644 --- a/app/code/Magento/Bundle/Test/Mftf/Test/AdminCreateAndEditBundleProductSettingsTest.xml +++ b/app/code/Magento/Bundle/Test/Mftf/Test/AdminCreateAndEditBundleProductSettingsTest.xml @@ -26,7 +26,7 @@ <createData entity="SimpleProduct2" stepKey="createSimpleProduct"/> <!-- Login as admin --> - <actionGroup ref="LoginAsAdmin" stepKey="loginAsAdmin"/> + <actionGroup ref="AdminLoginActionGroup" stepKey="loginAsAdmin"/> </before> <after> <!-- Delete the simple product --> @@ -38,7 +38,7 @@ </actionGroup> <!-- Log out --> - <actionGroup ref="logout" stepKey="logout"/> + <actionGroup ref="AdminLogoutActionGroup" stepKey="logout"/> </after> <!-- Create new bundle product --> @@ -126,7 +126,7 @@ <!-- Verify Url Key after changing --> <actionGroup ref="StorefrontOpenProductPageActionGroup" stepKey="openProductPage"> - <argument name="productUrl" value="{{ApiBundleProduct.name}}"/> + <argument name="productUrl" value="{{ApiBundleProduct.urlKey}}"/> </actionGroup> <!-- Assert product design settings "Layout empty" --> @@ -136,117 +136,6 @@ <actionGroup ref="StorefrontOpenCartFromMinicartActionGroup" stepKey="openCart"/> <dontSeeElement selector="{{StorefrontProductCartGiftOptionSection.giftOptions}}" stepKey="dontSeeGiftOptionBtn"/> - <!-- Delete created bundle product --> - <actionGroup ref="DeleteProductUsingProductGridActionGroup" stepKey="deleteProduct"> - <argument name="product" value="BundleProduct"/> - </actionGroup> - </test> - <test name="AdminCreateAndEditBundleProductOptionsNegativeTest"> - <annotations> - <features value="Bundle"/> - <stories value="Modify bundle product in Admin"/> - <title value="Admin should be able to remove any bundle option a bundle product"/> - <description value="Admin should be able to set/edit other product information when creating/editing a bundle product"/> - <severity value="MAJOR"/> - <testCaseId value="MC-224"/> - <skip> - <issueId value="https://github.com/magento/magento2/issues/25468"/> - </skip> - <group value="Catalog"/> - </annotations> - <before> - <!-- Create a Website --> - <createData entity="customWebsite" stepKey="createWebsite"/> - - <!-- Create first simple product for a bundle option --> - <createData entity="SimpleProduct2" stepKey="createFirstSimpleProduct"/> - - <!-- Create second simple product for a bundle option --> - <createData entity="SimpleProduct2" stepKey="createSecondSimpleProduct"/> - - <!-- Login as admin --> - <actionGroup ref="LoginAsAdmin" stepKey="loginAsAdmin"/> - </before> - <after> - <!-- Delete the simple product --> - <deleteData createDataKey="createFirstSimpleProduct" stepKey="deleteFirstSimpleProduct"/> - - <!-- Delete the simple product --> - <deleteData createDataKey="createSecondSimpleProduct" stepKey="deleteSecondSimpleProduct"/> - - <!-- Delete a Website --> - <actionGroup ref="AdminDeleteWebsiteActionGroup" stepKey="deleteWebsite"> - <argument name="websiteName" value="Second Website"/> - </actionGroup> - - <!-- Log out --> - <actionGroup ref="logout" stepKey="logout"/> - </after> - - <!-- Create new bundle product --> - <actionGroup ref="GoToSpecifiedCreateProductPageActionGroup" stepKey="createBundleProduct"> - <argument name="productType" value="bundle"/> - </actionGroup> - - <!-- Fill all main fields --> - <actionGroup ref="FillMainBundleProductFormActionGroup" stepKey="fillMainProductFields"/> - - <!-- Add first bundle option to the product --> - <actionGroup ref="AddBundleOptionWithTwoProductsActionGroup" stepKey="addFirstBundleOption"> - <argument name="x" value="0"/> - <argument name="n" value="1"/> - <argument name="prodOneSku" value="$$createFirstSimpleProduct.sku$$"/> - <argument name="prodTwoSku" value="$$createSecondSimpleProduct.sku$$"/> - <argument name="optionTitle" value="{{RadioButtonsOption.title}}"/> - <argument name="inputType" value="{{RadioButtonsOption.type}}"/> - </actionGroup> - - <!-- Add second bundle option to the product --> - <actionGroup ref="AddBundleOptionWithTwoProductsActionGroup" stepKey="addSecondBundleOption"> - <argument name="x" value="1"/> - <argument name="n" value="2"/> - <argument name="prodOneSku" value="$$createFirstSimpleProduct.sku$$"/> - <argument name="prodTwoSku" value="$$createSecondSimpleProduct.sku$$"/> - <argument name="optionTitle" value="{{CheckboxOption.title}}"/> - <argument name="inputType" value="{{CheckboxOption.type}}"/> - </actionGroup> - - <!-- Add third bundle option to the product --> - <actionGroup ref="AddBundleOptionWithTwoProductsActionGroup" stepKey="addThirdBundleOption"> - <argument name="x" value="2"/> - <argument name="n" value="3"/> - <argument name="prodOneSku" value="$$createFirstSimpleProduct.sku$$"/> - <argument name="prodTwoSku" value="$$createSecondSimpleProduct.sku$$"/> - <argument name="optionTitle" value="{{RadioButtonsOption.title}}"/> - <argument name="inputType" value="{{RadioButtonsOption.type}}"/> - </actionGroup> - - <!-- Set product in created Website --> - <actionGroup ref="AdminAssignProductInWebsiteActionGroup" stepKey="selectProductInWebsites"> - <argument name="website" value="$createWebsite.website[name]$"/> - </actionGroup> - - <!-- Save product form --> - <actionGroup ref="SaveProductFormActionGroup" stepKey="saveWithThreeOptions"/> - - <!-- Open created product --> - <actionGroup ref="SearchForProductOnBackendActionGroup" stepKey="searchForSimpleProduct"> - <argument name="product" value="BundleProduct"/> - </actionGroup> - <actionGroup ref="OpenEditProductOnBackendActionGroup" stepKey="openEditProduct"> - <argument name="product" value="BundleProduct"/> - </actionGroup> - - <!-- Remove second option --> - <actionGroup ref="DeleteBundleOptionByIndexActionGroup" stepKey="deleteSecondOption"> - <argument name="deleteIndex" value="1"/> - </actionGroup> - - <!-- Save product form --> - <actionGroup ref="SaveProductFormActionGroup" stepKey="clickSaveProduct"/> - <click selector="{{AdminProductFormActionSection.saveButton}}" stepKey="saveWithTwoOptions"/> - <seeElement selector="{{AdminCategoryMessagesSection.SuccessMessage}}" stepKey="messageYouSavedTheProductIsShown"/> - <!-- Delete created bundle product --> <actionGroup ref="DeleteProductUsingProductGridActionGroup" stepKey="deleteProduct"> <argument name="product" value="BundleProduct"/> diff --git a/app/code/Magento/Bundle/Test/Mftf/Test/AdminDeleteABundleProduct.xml b/app/code/Magento/Bundle/Test/Mftf/Test/AdminDeleteABundleProduct.xml deleted file mode 100644 index f272f3f98a8c9..0000000000000 --- a/app/code/Magento/Bundle/Test/Mftf/Test/AdminDeleteABundleProduct.xml +++ /dev/null @@ -1,87 +0,0 @@ -<?xml version="1.0" encoding="UTF-8"?> -<!-- - /** - * Copyright © Magento, Inc. All rights reserved. - * See COPYING.txt for license details. - */ ---> - -<tests xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" - xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/testSchema.xsd"> - <test name="AdminDeleteABundleProduct"> - <annotations> - <features value="Bundle"/> - <stories value="Admin list bundle products"/> - <title value="Admin should be able to delete a bundle product"/> - <description value="Admin should be able to delete a bundle product"/> - <severity value="CRITICAL"/> - <testCaseId value="MC-216"/> - <group value="Bundle"/> - </annotations> - <before> - <actionGroup ref="LoginAsAdmin" stepKey="loginAsAdmin"/> - <createData entity="SimpleProduct2" stepKey="simpleProduct1"/> - <createData entity="SimpleProduct2" stepKey="simpleProduct2"/> - </before> - <after> - <amOnPage url="{{AdminLogoutPage.url}}" stepKey="amOnLogoutPage"/> - <deleteData createDataKey="simpleProduct1" stepKey="deleteSimpleProduct1"/> - <deleteData createDataKey="simpleProduct2" stepKey="deleteSimpleProduct2"/> - </after> - - <!--Create bundle product--> - <amOnPage url="{{AdminProductCreatePage.url(BundleProduct.set, BundleProduct.type)}}" stepKey="goToBundleProductCreationPage"/> - <waitForPageLoad stepKey="waitForBundleProductCreationPage"/> - <conditionalClick selector="{{AdminProductFormBundleSection.bundleItemsToggle}}" dependentSelector="{{AdminProductFormBundleSection.bundleItemsToggle}}" visible="false" stepKey="conditionallyOpenSectionBundleItems"/> - <click selector="{{AdminProductFormBundleSection.addOption}}" stepKey="clickAddOption3"/> - <waitForElementVisible selector="{{AdminProductFormBundleSection.bundleOptionXTitle('0')}}" stepKey="waitForBundleOptions"/> - <fillField selector="{{AdminProductFormBundleSection.bundleOptionXTitle('0')}}" userInput="{{BundleProduct.optionTitle1}}" stepKey="fillOptionTitle"/> - <selectOption selector="{{AdminProductFormBundleSection.bundleOptionXInputType('0')}}" userInput="{{BundleProduct.optionInputType1}}" stepKey="selectInputType"/> - <waitForElementVisible selector="{{AdminProductFormBundleSection.addProductsToOption}}" stepKey="waitForAddProductsToBundle"/> - <click selector="{{AdminProductFormBundleSection.addProductsToOption}}" stepKey="clickAddProductsToOption"/> - <waitForPageLoad stepKey="waitForPageLoadAfterBundleProducts"/> - <actionGroup ref="FilterProductGridBySkuActionGroup" stepKey="filterBundleProductOptions"> - <argument name="product" value="$$simpleProduct1$$"/> - </actionGroup> - <checkOption selector="{{AdminAddProductsToOptionPanel.firstCheckbox}}" stepKey="selectFirstGridRow"/> - <actionGroup ref="FilterProductGridBySkuActionGroup" stepKey="filterBundleProductOptions2"> - <argument name="product" value="$$simpleProduct2$$"/> - </actionGroup> - <checkOption selector="{{AdminAddProductsToOptionPanel.firstCheckbox}}" stepKey="selectFirstGridRow2"/> - <click selector="{{AdminAddProductsToOptionPanel.addSelectedProducts}}" stepKey="clickAddSelectedBundleProducts"/> - <fillField selector="{{AdminProductFormBundleSection.bundleOptionXProductYQuantity('0', '0')}}" userInput="{{BundleProduct.defaultQuantity}}" stepKey="fillProductDefaultQty1"/> - <fillField selector="{{AdminProductFormBundleSection.bundleOptionXProductYQuantity('0', '1')}}" userInput="{{BundleProduct.defaultQuantity}}" stepKey="fillProductDefaultQty2"/> - - <!--Fill out ancillary data on bundle product--> - <actionGroup ref="AncillaryPrepBundleProductActionGroup" stepKey="createBundledProductForTwoSimpleProducts"/> - - <!--Save the product--> - <click selector="{{AdminProductFormActionSection.saveButton}}" stepKey="clickSaveButton"/> - <seeElement selector="{{AdminCategoryMessagesSection.SuccessMessage}}" stepKey="messageYouSavedTheProductIsShown"/> - - <!--Go to catalog deletion page--> - <amOnPage url="{{AdminCatalogProductPage.url}}" stepKey="goToCatalogPage"/> - <waitForPageLoad stepKey="Loading"/> - - <!--Apply Name Filter--> - <actionGroup ref="FilterProductGridByNameActionGroup" stepKey="filterBundleProductOptionsDownToName"> - <argument name="product" value="BundleProduct"/> - </actionGroup> - <click selector="{{AdminProductFiltersSection.allCheckbox}}" stepKey="SelectAllOnly1"/> - <waitForPageLoad stepKey="loading2"/> - - <!--Delete--> - <click selector="{{AdminProductFiltersSection.actions}}" stepKey="ClickOnActionsChangingView"/> - <click selector="{{AdminProductFiltersSection.delete}}" stepKey="ClickDelete"/> - <click selector="//button[@class='action-primary action-accept']" stepKey="ConfirmDelete"/> - <waitForPageLoad stepKey="loading3"/> - - <!--Locating delete message--> - <seeElement selector="{{AdminCategoryMessagesSection.SuccessMessage}}" stepKey="deleteMessage"/> - - <!--Testing deletion of product--> - <amOnPage url="{{BundleProduct.urlKey}}.html" stepKey="GoToProductPageAgain"/> - <waitForPageLoad stepKey="WaitForProductPageToLoadToShowElement"/> - <dontSeeElement selector="{{StorefrontBundledSection.bundleProductName}}" stepKey="LookingForNameOfProductTwo"/> - </test> -</tests> diff --git a/app/code/Magento/Bundle/Test/Mftf/Test/AdminDeleteABundleProductTest.xml b/app/code/Magento/Bundle/Test/Mftf/Test/AdminDeleteABundleProductTest.xml new file mode 100644 index 0000000000000..51c30ef86242c --- /dev/null +++ b/app/code/Magento/Bundle/Test/Mftf/Test/AdminDeleteABundleProductTest.xml @@ -0,0 +1,88 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + /** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +--> + +<tests xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/testSchema.xsd"> + <test name="AdminDeleteABundleProductTest"> + <annotations> + <features value="Bundle"/> + <stories value="Admin list bundle products"/> + <title value="Admin should be able to delete a bundle product"/> + <description value="Admin should be able to delete a bundle product"/> + <severity value="CRITICAL"/> + <testCaseId value="MC-216"/> + <group value="Bundle"/> + </annotations> + <before> + <actionGroup ref="AdminLoginActionGroup" stepKey="loginAsAdmin"/> + <createData entity="SimpleProduct2" stepKey="simpleProduct1"/> + <createData entity="SimpleProduct2" stepKey="simpleProduct2"/> + <magentoCron stepKey="runCronIndex" groups="index"/> + </before> + <after> + <amOnPage url="{{AdminLogoutPage.url}}" stepKey="amOnLogoutPage"/> + <deleteData createDataKey="simpleProduct1" stepKey="deleteSimpleProduct1"/> + <deleteData createDataKey="simpleProduct2" stepKey="deleteSimpleProduct2"/> + </after> + + <!--Create bundle product--> + <amOnPage url="{{AdminProductCreatePage.url(BundleProduct.set, BundleProduct.type)}}" stepKey="goToBundleProductCreationPage"/> + <waitForPageLoad stepKey="waitForBundleProductCreationPage"/> + <conditionalClick selector="{{AdminProductFormBundleSection.bundleItemsToggle}}" dependentSelector="{{AdminProductFormBundleSection.bundleItemsToggle}}" visible="false" stepKey="conditionallyOpenSectionBundleItems"/> + <click selector="{{AdminProductFormBundleSection.addOption}}" stepKey="clickAddOption3"/> + <waitForElementVisible selector="{{AdminProductFormBundleSection.bundleOptionXTitle('0')}}" stepKey="waitForBundleOptions"/> + <fillField selector="{{AdminProductFormBundleSection.bundleOptionXTitle('0')}}" userInput="{{BundleProduct.optionTitle1}}" stepKey="fillOptionTitle"/> + <selectOption selector="{{AdminProductFormBundleSection.bundleOptionXInputType('0')}}" userInput="{{BundleProduct.optionInputType1}}" stepKey="selectInputType"/> + <waitForElementVisible selector="{{AdminProductFormBundleSection.addProductsToOption}}" stepKey="waitForAddProductsToBundle"/> + <click selector="{{AdminProductFormBundleSection.addProductsToOption}}" stepKey="clickAddProductsToOption"/> + <waitForPageLoad stepKey="waitForPageLoadAfterBundleProducts"/> + <actionGroup ref="FilterProductGridBySkuActionGroup" stepKey="filterBundleProductOptions"> + <argument name="product" value="$$simpleProduct1$$"/> + </actionGroup> + <checkOption selector="{{AdminAddProductsToOptionPanel.firstCheckbox}}" stepKey="selectFirstGridRow"/> + <actionGroup ref="FilterProductGridBySkuActionGroup" stepKey="filterBundleProductOptions2"> + <argument name="product" value="$$simpleProduct2$$"/> + </actionGroup> + <checkOption selector="{{AdminAddProductsToOptionPanel.firstCheckbox}}" stepKey="selectFirstGridRow2"/> + <click selector="{{AdminAddProductsToOptionPanel.addSelectedProducts}}" stepKey="clickAddSelectedBundleProducts"/> + <fillField selector="{{AdminProductFormBundleSection.bundleOptionXProductYQuantity('0', '0')}}" userInput="{{BundleProduct.defaultQuantity}}" stepKey="fillProductDefaultQty1"/> + <fillField selector="{{AdminProductFormBundleSection.bundleOptionXProductYQuantity('0', '1')}}" userInput="{{BundleProduct.defaultQuantity}}" stepKey="fillProductDefaultQty2"/> + + <!--Fill out ancillary data on bundle product--> + <actionGroup ref="AncillaryPrepBundleProductActionGroup" stepKey="createBundledProductForTwoSimpleProducts"/> + + <!--Save the product--> + <click selector="{{AdminProductFormActionSection.saveButton}}" stepKey="clickSaveButton"/> + <seeElement selector="{{AdminCategoryMessagesSection.SuccessMessage}}" stepKey="messageYouSavedTheProductIsShown"/> + + <!--Go to catalog deletion page--> + <amOnPage url="{{AdminCatalogProductPage.url}}" stepKey="goToCatalogPage"/> + <waitForPageLoad stepKey="Loading"/> + + <!--Apply Name Filter--> + <actionGroup ref="FilterProductGridByNameActionGroup" stepKey="filterBundleProductOptionsDownToName"> + <argument name="product" value="BundleProduct"/> + </actionGroup> + <click selector="{{AdminProductFiltersSection.allCheckbox}}" stepKey="SelectAllOnly1"/> + <waitForPageLoad stepKey="loading2"/> + + <!--Delete--> + <click selector="{{AdminProductFiltersSection.actions}}" stepKey="ClickOnActionsChangingView"/> + <click selector="{{AdminProductFiltersSection.delete}}" stepKey="ClickDelete"/> + <click selector="//button[@class='action-primary action-accept']" stepKey="ConfirmDelete"/> + <waitForPageLoad stepKey="loading3"/> + + <!--Locating delete message--> + <seeElement selector="{{AdminCategoryMessagesSection.SuccessMessage}}" stepKey="deleteMessage"/> + + <!--Testing deletion of product--> + <amOnPage url="{{BundleProduct.urlKey}}.html" stepKey="GoToProductPageAgain"/> + <waitForPageLoad stepKey="WaitForProductPageToLoadToShowElement"/> + <dontSeeElement selector="{{StorefrontBundledSection.bundleProductName}}" stepKey="LookingForNameOfProductTwo"/> + </test> +</tests> diff --git a/app/code/Magento/Bundle/Test/Mftf/Test/AdminDeleteBundleDynamicProductTest.xml b/app/code/Magento/Bundle/Test/Mftf/Test/AdminDeleteBundleDynamicProductTest.xml index 51821b136ba26..5b603ef2f0a44 100644 --- a/app/code/Magento/Bundle/Test/Mftf/Test/AdminDeleteBundleDynamicProductTest.xml +++ b/app/code/Magento/Bundle/Test/Mftf/Test/AdminDeleteBundleDynamicProductTest.xml @@ -21,15 +21,16 @@ </skip> </annotations> <before> - <actionGroup ref="LoginAsAdmin" stepKey="loginAsAdmin"/> + <actionGroup ref="AdminLoginActionGroup" stepKey="loginAsAdmin"/> <createData entity="_defaultCategory" stepKey="createCategory"/> <createData entity="ApiBundleProductPriceViewRange" stepKey="createDynamicBundleProduct"> <requiredEntity createDataKey="createCategory"/> </createData> + <magentoCron stepKey="runCronIndex" groups="index"/> </before> <after> <deleteData createDataKey="createCategory" stepKey="deleteCategory"/> - <actionGroup ref="logout" stepKey="logout"/> + <actionGroup ref="AdminLogoutActionGroup" stepKey="logout"/> </after> <actionGroup ref="DeleteProductUsingProductGridActionGroup" stepKey="deleteBundleProductFilteredBySkuAndName"> <argument name="product" value="$$createDynamicBundleProduct$$"/> diff --git a/app/code/Magento/Bundle/Test/Mftf/Test/AdminDeleteBundleFixedProductTest.xml b/app/code/Magento/Bundle/Test/Mftf/Test/AdminDeleteBundleFixedProductTest.xml index dcd53fff6f6dd..46244603f2868 100644 --- a/app/code/Magento/Bundle/Test/Mftf/Test/AdminDeleteBundleFixedProductTest.xml +++ b/app/code/Magento/Bundle/Test/Mftf/Test/AdminDeleteBundleFixedProductTest.xml @@ -18,15 +18,16 @@ <group value="mtf_migrated"/> </annotations> <before> - <actionGroup ref="LoginAsAdmin" stepKey="loginAsAdmin"/> + <actionGroup ref="AdminLoginActionGroup" stepKey="loginAsAdmin"/> <createData entity="_defaultCategory" stepKey="createCategory"/> <createData entity="FixedBundleProduct" stepKey="createFixedBundleProduct"> <requiredEntity createDataKey="createCategory"/> </createData> + <magentoCron stepKey="runCronIndex" groups="index"/> </before> <after> <deleteData createDataKey="createCategory" stepKey="deleteCategory"/> - <actionGroup ref="logout" stepKey="logout"/> + <actionGroup ref="AdminLogoutActionGroup" stepKey="logout"/> </after> <actionGroup ref="DeleteProductUsingProductGridActionGroup" stepKey="deleteBundleProductFilteredBySkuAndName"> <argument name="product" value="$$createFixedBundleProduct$$"/> diff --git a/app/code/Magento/Bundle/Test/Mftf/Test/AdminEditRelatedBundleProductTest.xml b/app/code/Magento/Bundle/Test/Mftf/Test/AdminEditRelatedBundleProductTest.xml index 632ba194cf8de..4ba5d0f66e096 100644 --- a/app/code/Magento/Bundle/Test/Mftf/Test/AdminEditRelatedBundleProductTest.xml +++ b/app/code/Magento/Bundle/Test/Mftf/Test/AdminEditRelatedBundleProductTest.xml @@ -20,9 +20,10 @@ </annotations> <before> <!--Admin login--> - <actionGroup ref="LoginAsAdmin" stepKey="loginAsAdmin"/> + <actionGroup ref="AdminLoginActionGroup" stepKey="loginAsAdmin"/> <createData entity="SimpleProduct2" stepKey="simpleProduct0"/> <createData entity="SimpleProduct2" stepKey="simpleProduct1"/> + <magentoCron stepKey="runCronIndex" groups="index"/> </before> <after> <!-- Delete the bundled product --> @@ -70,6 +71,8 @@ <conditionalClick selector="{{AdminProductFormRelatedUpSellCrossSellSection.relatedDropdown}}" dependentSelector="{{AdminProductFormRelatedUpSellCrossSellSection.relatedDependent}}" visible="false" stepKey="openDropDownIfClosedRelatedSee"/> <see selector="{{AdminProductFormRelatedUpSellCrossSellSection.selectedRelatedProduct}}" userInput="$$simpleProduct1.sku$$" stepKey="seeRelatedProduct"/> + <magentoCLI command="cron:run --group=index" stepKey="runCronIndexer"/> + <!--See related product in storefront--> <amOnPage url="{{BundleProduct.sku}}.html" stepKey="goToStorefront"/> <waitForPageLoad stepKey="waitForStorefront"/> diff --git a/app/code/Magento/Bundle/Test/Mftf/Test/AdminFilterProductListByBundleProduct.xml b/app/code/Magento/Bundle/Test/Mftf/Test/AdminFilterProductListByBundleProduct.xml deleted file mode 100644 index 5aa72fb651985..0000000000000 --- a/app/code/Magento/Bundle/Test/Mftf/Test/AdminFilterProductListByBundleProduct.xml +++ /dev/null @@ -1,73 +0,0 @@ -<?xml version="1.0" encoding="UTF-8"?> -<!-- - /** - * Copyright © Magento, Inc. All rights reserved. - * See COPYING.txt for license details. - */ ---> - -<tests xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" - xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/testSchema.xsd"> - <test name="AdminFilterProductListByBundleProduct"> - <annotations> - <features value="Bundle"/> - <stories value="Admin list bundle products"/> - <title value="Admin should be able to filter product list by type = Bundle Product"/> - <description value="Admin should be able to filter product list by type = Bundle Product"/> - <severity value="CRITICAL"/> - <testCaseId value="MC-214"/> - <group value="Bundle"/> - </annotations> - <before> - <actionGroup ref="LoginAsAdmin" stepKey="loginAsAdmin"/> - <createData entity="SimpleProduct2" stepKey="simpleProduct1"/> - <createData entity="SimpleProduct2" stepKey="simpleProduct2"/> - </before> - <after> - <amOnPage url="{{AdminLogoutPage.url}}" stepKey="amOnLogoutPage"/> - <deleteData createDataKey="simpleProduct1" stepKey="deleteSimpleProduct1"/> - <deleteData createDataKey="simpleProduct2" stepKey="deleteSimpleProduct2"/> - </after> - - <!--Create bundle product--> - <amOnPage url="{{AdminProductCreatePage.url(BundleProduct.set, BundleProduct.type)}}" stepKey="goToBundleProductCreationPage"/> - <waitForPageLoad stepKey="waitForBundleProductCreationPage"/> - <conditionalClick selector="{{AdminProductFormBundleSection.bundleItemsToggle}}" dependentSelector="{{AdminProductFormBundleSection.bundleItemsToggle}}" visible="false" stepKey="conditionallyOpenSectionBundleItems"/> - <click selector="{{AdminProductFormBundleSection.addOption}}" stepKey="clickAddOption3"/> - <waitForElementVisible selector="{{AdminProductFormBundleSection.bundleOptionXTitle('0')}}" stepKey="waitForBundleOptions"/> - <fillField selector="{{AdminProductFormBundleSection.bundleOptionXTitle('0')}}" userInput="{{BundleProduct.optionTitle1}}" stepKey="fillOptionTitle"/> - <selectOption selector="{{AdminProductFormBundleSection.bundleOptionXInputType('0')}}" userInput="{{BundleProduct.optionInputType1}}" stepKey="selectInputType"/> - <waitForElementVisible selector="{{AdminProductFormBundleSection.addProductsToOption}}" stepKey="waitForAddProductsToBundle"/> - <click selector="{{AdminProductFormBundleSection.addProductsToOption}}" stepKey="clickAddProductsToOption"/> - <waitForPageLoad stepKey="waitForPageLoadAfterBundleProducts"/> - <actionGroup ref="FilterProductGridBySkuActionGroup" stepKey="filterBundleProductOptions"> - <argument name="product" value="$$simpleProduct1$$"/> - </actionGroup> - <checkOption selector="{{AdminAddProductsToOptionPanel.firstCheckbox}}" stepKey="selectFirstGridRow"/> - <actionGroup ref="FilterProductGridBySkuActionGroup" stepKey="filterBundleProductOptions2"> - <argument name="product" value="$$simpleProduct2$$"/> - </actionGroup> - <checkOption selector="{{AdminAddProductsToOptionPanel.firstCheckbox}}" stepKey="selectFirstGridRow2"/> - <click selector="{{AdminAddProductsToOptionPanel.addSelectedProducts}}" stepKey="clickAddSelectedBundleProducts"/> - <fillField selector="{{AdminProductFormBundleSection.bundleOptionXProductYQuantity('0', '0')}}" userInput="{{BundleProduct.defaultQuantity}}" stepKey="fillProductDefaultQty1"/> - <fillField selector="{{AdminProductFormBundleSection.bundleOptionXProductYQuantity('0', '1')}}" userInput="{{BundleProduct.defaultQuantity}}" stepKey="fillProductDefaultQty2"/> - - <!--Fill out ancillary data on bundle product--> - <actionGroup ref="AncillaryPrepBundleProductActionGroup" stepKey="createBundledProductForTwoSimpleProducts"/> - - <!--Save the product--> - <click selector="{{AdminProductFormActionSection.saveButton}}" stepKey="clickSaveButton"/> - <seeElement selector="{{AdminCategoryMessagesSection.SuccessMessage}}" stepKey="messageYouSavedTheProductIsShown"/> - - <!--Apply Bundle Product Filter--> - <!--Clear Filters--> - <actionGroup ref="AdminClearFiltersActionGroup" stepKey="ClearFilters"/> - - <!--Setting filter--> - <actionGroup ref="BundleProductFilter" stepKey="FilterForOnlyBundleProducts"/> - - <!--Testing application of filter--> - <see selector="{{AdminProductFiltersSection.productType('0')}}" userInput="Bundle Product" stepKey="correcType0"/> - <dontSeeElement selector="{{AdminProductFiltersSection.AllProductsNotOfBundleType}}" stepKey="checkingRowsForIncorrectType"/> - </test> -</tests> diff --git a/app/code/Magento/Bundle/Test/Mftf/Test/AdminFilterProductListByBundleProductTest.xml b/app/code/Magento/Bundle/Test/Mftf/Test/AdminFilterProductListByBundleProductTest.xml new file mode 100644 index 0000000000000..f8914656cc32b --- /dev/null +++ b/app/code/Magento/Bundle/Test/Mftf/Test/AdminFilterProductListByBundleProductTest.xml @@ -0,0 +1,74 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + /** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +--> + +<tests xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/testSchema.xsd"> + <test name="AdminFilterProductListByBundleProductTest"> + <annotations> + <features value="Bundle"/> + <stories value="Admin list bundle products"/> + <title value="Admin should be able to filter product list by type = Bundle Product"/> + <description value="Admin should be able to filter product list by type = Bundle Product"/> + <severity value="CRITICAL"/> + <testCaseId value="MC-214"/> + <group value="Bundle"/> + </annotations> + <before> + <actionGroup ref="AdminLoginActionGroup" stepKey="loginAsAdmin"/> + <createData entity="SimpleProduct2" stepKey="simpleProduct1"/> + <createData entity="SimpleProduct2" stepKey="simpleProduct2"/> + <magentoCron stepKey="runCronIndex" groups="index"/> + </before> + <after> + <amOnPage url="{{AdminLogoutPage.url}}" stepKey="amOnLogoutPage"/> + <deleteData createDataKey="simpleProduct1" stepKey="deleteSimpleProduct1"/> + <deleteData createDataKey="simpleProduct2" stepKey="deleteSimpleProduct2"/> + </after> + + <!--Create bundle product--> + <amOnPage url="{{AdminProductCreatePage.url(BundleProduct.set, BundleProduct.type)}}" stepKey="goToBundleProductCreationPage"/> + <waitForPageLoad stepKey="waitForBundleProductCreationPage"/> + <conditionalClick selector="{{AdminProductFormBundleSection.bundleItemsToggle}}" dependentSelector="{{AdminProductFormBundleSection.bundleItemsToggle}}" visible="false" stepKey="conditionallyOpenSectionBundleItems"/> + <click selector="{{AdminProductFormBundleSection.addOption}}" stepKey="clickAddOption3"/> + <waitForElementVisible selector="{{AdminProductFormBundleSection.bundleOptionXTitle('0')}}" stepKey="waitForBundleOptions"/> + <fillField selector="{{AdminProductFormBundleSection.bundleOptionXTitle('0')}}" userInput="{{BundleProduct.optionTitle1}}" stepKey="fillOptionTitle"/> + <selectOption selector="{{AdminProductFormBundleSection.bundleOptionXInputType('0')}}" userInput="{{BundleProduct.optionInputType1}}" stepKey="selectInputType"/> + <waitForElementVisible selector="{{AdminProductFormBundleSection.addProductsToOption}}" stepKey="waitForAddProductsToBundle"/> + <click selector="{{AdminProductFormBundleSection.addProductsToOption}}" stepKey="clickAddProductsToOption"/> + <waitForPageLoad stepKey="waitForPageLoadAfterBundleProducts"/> + <actionGroup ref="FilterProductGridBySkuActionGroup" stepKey="filterBundleProductOptions"> + <argument name="product" value="$$simpleProduct1$$"/> + </actionGroup> + <checkOption selector="{{AdminAddProductsToOptionPanel.firstCheckbox}}" stepKey="selectFirstGridRow"/> + <actionGroup ref="FilterProductGridBySkuActionGroup" stepKey="filterBundleProductOptions2"> + <argument name="product" value="$$simpleProduct2$$"/> + </actionGroup> + <checkOption selector="{{AdminAddProductsToOptionPanel.firstCheckbox}}" stepKey="selectFirstGridRow2"/> + <click selector="{{AdminAddProductsToOptionPanel.addSelectedProducts}}" stepKey="clickAddSelectedBundleProducts"/> + <fillField selector="{{AdminProductFormBundleSection.bundleOptionXProductYQuantity('0', '0')}}" userInput="{{BundleProduct.defaultQuantity}}" stepKey="fillProductDefaultQty1"/> + <fillField selector="{{AdminProductFormBundleSection.bundleOptionXProductYQuantity('0', '1')}}" userInput="{{BundleProduct.defaultQuantity}}" stepKey="fillProductDefaultQty2"/> + + <!--Fill out ancillary data on bundle product--> + <actionGroup ref="AncillaryPrepBundleProductActionGroup" stepKey="createBundledProductForTwoSimpleProducts"/> + + <!--Save the product--> + <click selector="{{AdminProductFormActionSection.saveButton}}" stepKey="clickSaveButton"/> + <seeElement selector="{{AdminCategoryMessagesSection.SuccessMessage}}" stepKey="messageYouSavedTheProductIsShown"/> + + <!--Apply Bundle Product Filter--> + <!--Clear Filters--> + <actionGroup ref="AdminClearFiltersActionGroup" stepKey="ClearFilters"/> + + <!--Setting filter--> + <actionGroup ref="BundleProductFilter" stepKey="FilterForOnlyBundleProducts"/> + + <!--Testing application of filter--> + <see selector="{{AdminProductFiltersSection.productType('0')}}" userInput="Bundle Product" stepKey="correcType0"/> + <dontSeeElement selector="{{AdminProductFiltersSection.AllProductsNotOfBundleType}}" stepKey="checkingRowsForIncorrectType"/> + </test> +</tests> diff --git a/app/code/Magento/Bundle/Test/Mftf/Test/AdminMassDeleteBundleProducts.xml b/app/code/Magento/Bundle/Test/Mftf/Test/AdminMassDeleteBundleProducts.xml index 28abd06253393..2c1fcb6d7de42 100644 --- a/app/code/Magento/Bundle/Test/Mftf/Test/AdminMassDeleteBundleProducts.xml +++ b/app/code/Magento/Bundle/Test/Mftf/Test/AdminMassDeleteBundleProducts.xml @@ -19,11 +19,12 @@ <group value="Bundle"/> </annotations> <before> - <actionGroup ref="LoginAsAdmin" stepKey="loginAsAdmin"/> + <actionGroup ref="AdminLoginActionGroup" stepKey="loginAsAdmin"/> <createData entity="SimpleProduct2" stepKey="simpleProduct1"/> <createData entity="SimpleProduct2" stepKey="simpleProduct2"/> <createData entity="SimpleProduct2" stepKey="simpleProduct3"/> <createData entity="SimpleProduct2" stepKey="simpleProduct4"/> + <magentoCron stepKey="runCronIndex" groups="index"/> </before> <after> <!--Clear Filters--> diff --git a/app/code/Magento/Bundle/Test/Mftf/Test/AdminProductBundleCreationTest.xml b/app/code/Magento/Bundle/Test/Mftf/Test/AdminProductBundleCreationTest.xml index 78bc85da6f69b..17235c531de8f 100644 --- a/app/code/Magento/Bundle/Test/Mftf/Test/AdminProductBundleCreationTest.xml +++ b/app/code/Magento/Bundle/Test/Mftf/Test/AdminProductBundleCreationTest.xml @@ -14,7 +14,7 @@ <stories value="Create/Edit bundle product in Admin"/> <title value="Admin should be able to save and publish a bundle product"/> <description value="Admin should be able to save and publish a bundle product"/> - <severity value="CRITICAL"/> + <severity value="BLOCKER"/> <testCaseId value="MC-225"/> <group value="Bundle"/> </annotations> @@ -23,9 +23,9 @@ <createData entity="_defaultCategory" stepKey="createPreReqCategory"/> <createData entity="SimpleProduct2" stepKey="simpleProduct1"/> <createData entity="SimpleProduct2" stepKey="simpleProduct2"/> - + <magentoCron stepKey="runCronIndex" groups="index"/> <!-- Admin Login--> - <actionGroup stepKey="loginToAdminPanel" ref="LoginAsAdmin"/> + <actionGroup stepKey="loginToAdminPanel" ref="AdminLoginActionGroup"/> </before> <after> <deleteData createDataKey="createPreReqCategory" stepKey="deletePreReqCategory"/> @@ -33,7 +33,7 @@ <deleteData createDataKey="simpleProduct2" stepKey="deleteSimpleProduct2"/> <actionGroup ref="AdminOpenProductIndexPageActionGroup" stepKey="navigateToProductIndexPage"/> <actionGroup ref="DeleteProductsIfTheyExistActionGroup" stepKey="deleteAllProducts"/> - <actionGroup ref="logout" stepKey="logout"/> + <actionGroup ref="AdminLogoutActionGroup" stepKey="logout"/> </after> <!-- go to bundle product creation page--> <amOnPage url="{{AdminProductCreatePage.url(BundleProduct.set, BundleProduct.type)}}" stepKey="goToBundleProductCreationPage" /> diff --git a/app/code/Magento/Bundle/Test/Mftf/Test/AdminRemoveDefaultImageBundleProductTest.xml b/app/code/Magento/Bundle/Test/Mftf/Test/AdminRemoveDefaultImageBundleProductTest.xml index 77be5b879b1c6..ab1d4bb5ce68a 100644 --- a/app/code/Magento/Bundle/Test/Mftf/Test/AdminRemoveDefaultImageBundleProductTest.xml +++ b/app/code/Magento/Bundle/Test/Mftf/Test/AdminRemoveDefaultImageBundleProductTest.xml @@ -19,9 +19,10 @@ <group value="Bundle"/> </annotations> <before> - <actionGroup ref="LoginAsAdmin" stepKey="loginAsAdmin"/> + <actionGroup ref="AdminLoginActionGroup" stepKey="loginAsAdmin"/> <createData entity="SimpleProduct2" stepKey="simpleProduct1"/> <createData entity="SimpleProduct2" stepKey="simpleProduct2"/> + <magentoCron stepKey="runCronIndex" groups="index"/> </before> <after> <!-- Delete the bundled product we created in the test body --> diff --git a/app/code/Magento/Bundle/Test/Mftf/Test/AdminRemoveDefaultVideoBundleProductTest.xml b/app/code/Magento/Bundle/Test/Mftf/Test/AdminRemoveDefaultVideoBundleProductTest.xml index 0de9f4ee75a4d..06ddd43d0e2cc 100644 --- a/app/code/Magento/Bundle/Test/Mftf/Test/AdminRemoveDefaultVideoBundleProductTest.xml +++ b/app/code/Magento/Bundle/Test/Mftf/Test/AdminRemoveDefaultVideoBundleProductTest.xml @@ -17,10 +17,14 @@ <severity value="MAJOR"/> <testCaseId value="MC-205"/> <group value="Bundle"/> + <skip> + <issueId value="MC-32197"/> + </skip> </annotations> <before> <createData entity="SimpleProduct2" stepKey="simpleProduct1"/> <createData entity="SimpleProduct2" stepKey="simpleProduct2"/> + <magentoCron stepKey="runCronIndex" groups="index"/> </before> <after> <deleteData createDataKey="simpleProduct1" stepKey="deleteSimpleProduct1"/> diff --git a/app/code/Magento/Bundle/Test/Mftf/Test/AdminShouldBeAbleToMassUpdateAttributesForBundleProductsTest.xml b/app/code/Magento/Bundle/Test/Mftf/Test/AdminShouldBeAbleToMassUpdateAttributesForBundleProductsTest.xml index 994a10ae02692..caf500762883c 100644 --- a/app/code/Magento/Bundle/Test/Mftf/Test/AdminShouldBeAbleToMassUpdateAttributesForBundleProductsTest.xml +++ b/app/code/Magento/Bundle/Test/Mftf/Test/AdminShouldBeAbleToMassUpdateAttributesForBundleProductsTest.xml @@ -34,6 +34,7 @@ <requiredEntity createDataKey="createBundleOption"/> <requiredEntity createDataKey="createSimpleProduct"/> </createData> + <magentoCron stepKey="runCronIndex" groups="index"/> </before> <after> <!-- Delete Simple Product --> @@ -43,10 +44,10 @@ <!-- Clear Filter --> <actionGroup ref="ClearProductsFilterActionGroup" stepKey="clearProductFilter"/> <!--Log Out Admin--> - <actionGroup ref="logout" stepKey="logoutAsAdmin"/> + <actionGroup ref="AdminLogoutActionGroup" stepKey="logoutAsAdmin"/> </after> <!-- Login as Admin --> - <actionGroup ref="LoginAsAdmin" stepKey="loginAsAdmin"/> + <actionGroup ref="AdminLoginActionGroup" stepKey="loginAsAdmin"/> <!-- Go to Catalog -> Catalog -> Products and Search created product in precondition and choose it --> <actionGroup ref="SearchForProductOnBackendActionGroup" stepKey="searchProduct"> <argument name="product" value="$$createFixedBundleProduct$$"/> diff --git a/app/code/Magento/Bundle/Test/Mftf/Test/AdvanceCatalogSearchBundleByDescriptionMysqlTest.xml b/app/code/Magento/Bundle/Test/Mftf/Test/AdvanceCatalogSearchBundleByDescriptionMysqlTest.xml new file mode 100644 index 0000000000000..e9525e2a144fb --- /dev/null +++ b/app/code/Magento/Bundle/Test/Mftf/Test/AdvanceCatalogSearchBundleByDescriptionMysqlTest.xml @@ -0,0 +1,47 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + /** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +--> + +<tests xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/testSchema.xsd"> + <test name="AdvanceCatalogSearchBundleByDescriptionMysqlTest" extends="AdvanceCatalogSearchSimpleProductByDescriptionTest"> + <annotations> + <features value="Bundle"/> + <stories value="Advanced Catalog Product Search for all product types"/> + <title value="Guest customer should be able to advance search Bundle product with product description using the MySQL search engine"/> + <description value="Guest customer should be able to advance search Bundle product with product description using the MySQL search engine"/> + <severity value="MAJOR"/> + <testCaseId value="MC-20473"/> + <group value="Bundle"/> + <group value="SearchEngineMysql"/> + </annotations> + <before> + <createData entity="ApiProductWithDescription" stepKey="simple1" before="simple2"/> + <createData entity="ApiProductWithDescription" stepKey="simple2" before="product"/> + <createData entity="ApiBundleProduct" stepKey="product"/> + <createData entity="DropDownBundleOption" stepKey="bundleOption"> + <requiredEntity createDataKey="product"/> + </createData> + <createData entity="ApiBundleLink" stepKey="createBundleLink1"> + <requiredEntity createDataKey="product"/> + <requiredEntity createDataKey="bundleOption"/> + <requiredEntity createDataKey="simple1"/> + </createData> + <createData entity="ApiBundleLink" stepKey="createBundleLink2"> + <requiredEntity createDataKey="product"/> + <requiredEntity createDataKey="bundleOption"/> + <requiredEntity createDataKey="simple2"/> + </createData> + + <magentoCron stepKey="runCronReindex" groups="index"/> + </before> + <after> + <deleteData createDataKey="simple1" stepKey="deleteSimple1" before="deleteSimple2"/> + <deleteData createDataKey="simple2" stepKey="deleteSimple2" before="delete"/> + </after> + </test> +</tests> diff --git a/app/code/Magento/Bundle/Test/Mftf/Test/AdvanceCatalogSearchBundleByDescriptionTest.xml b/app/code/Magento/Bundle/Test/Mftf/Test/AdvanceCatalogSearchBundleByDescriptionTest.xml new file mode 100644 index 0000000000000..95b4e06678af2 --- /dev/null +++ b/app/code/Magento/Bundle/Test/Mftf/Test/AdvanceCatalogSearchBundleByDescriptionTest.xml @@ -0,0 +1,51 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + /** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +--> + +<tests xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/testSchema.xsd"> + <test name="AdvanceCatalogSearchBundleByDescriptionTest" extends="AdvanceCatalogSearchSimpleProductByDescriptionTest"> + <annotations> + <features value="Bundle"/> + <stories value="Advanced Catalog Product Search for all product types"/> + <title value="Guest customer should be able to advance search Bundle product with product description"/> + <description value="Guest customer should be able to advance search Bundle product with product description"/> + <severity value="MAJOR"/> + <testCaseId value="MC-242"/> + <group value="Bundle"/> + <group value="SearchEngineElasticsearch"/> + </annotations> + <before> + <createData entity="ApiProductWithDescription" stepKey="simple1" before="simple2"/> + <createData entity="ApiProductWithDescription" stepKey="simple2" before="product"/> + <createData entity="ApiBundleProduct" stepKey="product"/> + <createData entity="DropDownBundleOption" stepKey="bundleOption"> + <requiredEntity createDataKey="product"/> + </createData> + <createData entity="ApiBundleLink" stepKey="createBundleLink1"> + <requiredEntity createDataKey="product"/> + <requiredEntity createDataKey="bundleOption"/> + <requiredEntity createDataKey="simple1"/> + </createData> + <createData entity="ApiBundleLink" stepKey="createBundleLink2"> + <requiredEntity createDataKey="product"/> + <requiredEntity createDataKey="bundleOption"/> + <requiredEntity createDataKey="simple2"/> + </createData> + + <magentoCron stepKey="runCronReindex" groups="index"/> + </before> + <after> + <deleteData createDataKey="simple1" stepKey="deleteSimple1" before="deleteSimple2"/> + <deleteData createDataKey="simple2" stepKey="deleteSimple2" before="delete"/> + </after> + <see userInput="3 items" selector="{{StorefrontCatalogSearchAdvancedResultMainSection.itemFound}}" stepKey="see"/> + <see userInput="$$product.name$$" selector="{{StorefrontCatalogSearchAdvancedResultMainSection.nthProductName('1')}}" stepKey="seeProductName"/> + <see userInput="$$simple1.name$$" selector="{{StorefrontCatalogSearchAdvancedResultMainSection.nthProductName('2')}}" stepKey="seeSimple1ProductName"/> + <see userInput="$$simple2.name$$" selector="{{StorefrontCatalogSearchAdvancedResultMainSection.nthProductName('3')}}" stepKey="seeSimple2ProductName"/> + </test> +</tests> diff --git a/app/code/Magento/Bundle/Test/Mftf/Test/AdvanceCatalogSearchBundleByNameMysqlTest.xml b/app/code/Magento/Bundle/Test/Mftf/Test/AdvanceCatalogSearchBundleByNameMysqlTest.xml new file mode 100644 index 0000000000000..05fe8dd7de27e --- /dev/null +++ b/app/code/Magento/Bundle/Test/Mftf/Test/AdvanceCatalogSearchBundleByNameMysqlTest.xml @@ -0,0 +1,47 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + /** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +--> + +<tests xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/testSchema.xsd"> + <test name="AdvanceCatalogSearchBundleByNameMysqlTest" extends="AdvanceCatalogSearchSimpleProductByNameTest"> + <annotations> + <features value="Bundle"/> + <stories value="Advanced Catalog Product Search for all product types"/> + <title value="Guest customer should be able to advance search Bundle product with product name using the MySQL search engine"/> + <description value="Guest customer should be able to advance search Bundle product with product name using the MySQL search engine"/> + <severity value="MAJOR"/> + <testCaseId value="MC-20472"/> + <group value="Bundle"/> + <group value="SearchEngineMysql"/> + </annotations> + <before> + <createData entity="ApiProductWithDescription" stepKey="simple1" before="simple2"/> + <createData entity="ApiProductWithDescription" stepKey="simple2" before="product"/> + <createData entity="ApiBundleProduct" stepKey="product"/> + <createData entity="DropDownBundleOption" stepKey="bundleOption"> + <requiredEntity createDataKey="product"/> + </createData> + <createData entity="ApiBundleLink" stepKey="createBundleLink1"> + <requiredEntity createDataKey="product"/> + <requiredEntity createDataKey="bundleOption"/> + <requiredEntity createDataKey="simple1"/> + </createData> + <createData entity="ApiBundleLink" stepKey="createBundleLink2"> + <requiredEntity createDataKey="product"/> + <requiredEntity createDataKey="bundleOption"/> + <requiredEntity createDataKey="simple2"/> + </createData> + + <magentoCron stepKey="runCronReindex" groups="index"/> + </before> + <after> + <deleteData createDataKey="simple1" stepKey="deleteSimple1" before="deleteSimple2"/> + <deleteData createDataKey="simple2" stepKey="deleteSimple2" before="delete"/> + </after> + </test> +</tests> diff --git a/app/code/Magento/Bundle/Test/Mftf/Test/AdvanceCatalogSearchBundleByPriceMysqlTest.xml b/app/code/Magento/Bundle/Test/Mftf/Test/AdvanceCatalogSearchBundleByPriceMysqlTest.xml new file mode 100644 index 0000000000000..c70e9a95ab532 --- /dev/null +++ b/app/code/Magento/Bundle/Test/Mftf/Test/AdvanceCatalogSearchBundleByPriceMysqlTest.xml @@ -0,0 +1,56 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + /** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +--> + +<tests xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/testSchema.xsd"> + <test name="AdvanceCatalogSearchBundleByPriceMysqlTest" extends="AdvanceCatalogSearchSimpleProductByPriceTest"> + <annotations> + <features value="Bundle"/> + <stories value="Advanced Catalog Product Search for all product types"/> + <title value="Guest customer should be able to advance search Bundle product with product price using the MySQL search engine"/> + <description value="Guest customer should be able to advance search Bundle product with product price the MySQL search engine"/> + <severity value="MAJOR"/> + <testCaseId value="MC-20475"/> + <group value="Bundle"/> + <group value="SearchEngineMysql"/> + </annotations> + <before> + <createData entity="ApiProductWithDescription" stepKey="simple1" before="simple2"/> + <createData entity="ApiProductWithDescription" stepKey="simple2" before="product"/> + <createData entity="ApiBundleProduct" stepKey="product"/> + <createData entity="DropDownBundleOption" stepKey="bundleOption"> + <requiredEntity createDataKey="product"/> + </createData> + <createData entity="ApiBundleLink" stepKey="createBundleLink1"> + <requiredEntity createDataKey="product"/> + <requiredEntity createDataKey="bundleOption"/> + <requiredEntity createDataKey="simple1"/> + </createData> + <createData entity="ApiBundleLink" stepKey="createBundleLink2"> + <requiredEntity createDataKey="product"/> + <requiredEntity createDataKey="bundleOption"/> + <requiredEntity createDataKey="simple2"/> + </createData> + <getData entity="GetProduct" stepKey="arg1"> + <requiredEntity createDataKey="product"/> + </getData> + <getData entity="GetProduct" stepKey="arg2"> + <requiredEntity createDataKey="simple1"/> + </getData> + <getData entity="GetProduct" stepKey="arg3"> + <requiredEntity createDataKey="simple2"/> + </getData> + + <magentoCron stepKey="runCronReindex" groups="index"/> + </before> + <after> + <deleteData createDataKey="simple1" stepKey="deleteSimple1" before="deleteSimple2"/> + <deleteData createDataKey="simple2" stepKey="deleteSimple2" before="delete"/> + </after> + </test> +</tests> diff --git a/app/code/Magento/Bundle/Test/Mftf/Test/AdvanceCatalogSearchBundleByPriceTest.xml b/app/code/Magento/Bundle/Test/Mftf/Test/AdvanceCatalogSearchBundleByPriceTest.xml new file mode 100644 index 0000000000000..f45c9ceba635f --- /dev/null +++ b/app/code/Magento/Bundle/Test/Mftf/Test/AdvanceCatalogSearchBundleByPriceTest.xml @@ -0,0 +1,60 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + /** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +--> + +<tests xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/testSchema.xsd"> + <test name="AdvanceCatalogSearchBundleByPriceTest" extends="AdvanceCatalogSearchSimpleProductByPriceTest"> + <annotations> + <features value="Bundle"/> + <stories value="Advanced Catalog Product Search for all product types"/> + <title value="Guest customer should be able to advance search Bundle product with product price"/> + <description value="Guest customer should be able to advance search Bundle product with product price"/> + <severity value="MAJOR"/> + <testCaseId value="MC-251"/> + <group value="Bundle"/> + <group value="SearchEngineElasticsearch"/> + </annotations> + <before> + <createData entity="ApiProductWithDescription" stepKey="simple1" before="simple2"/> + <createData entity="ApiProductWithDescription" stepKey="simple2" before="product"/> + <createData entity="ApiBundleProduct" stepKey="product"/> + <createData entity="DropDownBundleOption" stepKey="bundleOption"> + <requiredEntity createDataKey="product"/> + </createData> + <createData entity="ApiBundleLink" stepKey="createBundleLink1"> + <requiredEntity createDataKey="product"/> + <requiredEntity createDataKey="bundleOption"/> + <requiredEntity createDataKey="simple1"/> + </createData> + <createData entity="ApiBundleLink" stepKey="createBundleLink2"> + <requiredEntity createDataKey="product"/> + <requiredEntity createDataKey="bundleOption"/> + <requiredEntity createDataKey="simple2"/> + </createData> + <getData entity="GetProduct" stepKey="arg1"> + <requiredEntity createDataKey="product"/> + </getData> + <getData entity="GetProduct" stepKey="arg2"> + <requiredEntity createDataKey="simple1"/> + </getData> + <getData entity="GetProduct" stepKey="arg3"> + <requiredEntity createDataKey="simple2"/> + </getData> + + <magentoCron stepKey="runCronReindex" groups="index"/> + </before> + <after> + <deleteData createDataKey="simple1" stepKey="deleteSimple1" before="deleteSimple2"/> + <deleteData createDataKey="simple2" stepKey="deleteSimple2" before="delete"/> + </after> + <see userInput="3 items" selector="{{StorefrontCatalogSearchAdvancedResultMainSection.itemFound}}" stepKey="see"/> + <see userInput="$$product.name$$" selector="{{StorefrontCatalogSearchAdvancedResultMainSection.nthProductName('1')}}" stepKey="seeProductName"/> + <see userInput="$$simple1.name$$" selector="{{StorefrontCatalogSearchAdvancedResultMainSection.nthProductName('2')}}" stepKey="seeSimple1ProductName"/> + <see userInput="$$simple2.name$$" selector="{{StorefrontCatalogSearchAdvancedResultMainSection.nthProductName('3')}}" stepKey="seeSimple2ProductName"/> + </test> +</tests> diff --git a/app/code/Magento/Bundle/Test/Mftf/Test/AdvanceCatalogSearchBundleByShortDescriptionMysqlTest.xml b/app/code/Magento/Bundle/Test/Mftf/Test/AdvanceCatalogSearchBundleByShortDescriptionMysqlTest.xml new file mode 100644 index 0000000000000..2bb2974b78555 --- /dev/null +++ b/app/code/Magento/Bundle/Test/Mftf/Test/AdvanceCatalogSearchBundleByShortDescriptionMysqlTest.xml @@ -0,0 +1,47 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + /** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +--> + +<tests xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/testSchema.xsd"> + <test name="AdvanceCatalogSearchBundleByShortDescriptionMysqlTest" extends="AdvanceCatalogSearchSimpleProductByShortDescriptionTest"> + <annotations> + <features value="Bundle"/> + <stories value="Advanced Catalog Product Search for all product types"/> + <title value="Guest customer should be able to advance search Bundle product with product short description using the MySQL search engine"/> + <description value="Guest customer should be able to advance search Bundle product with product short description using the MySQL search engine"/> + <severity value="MAJOR"/> + <testCaseId value="MC-20474"/> + <group value="Bundle"/> + <group value="SearchEngineMysql"/> + </annotations> + <before> + <createData entity="ApiProductWithDescription" stepKey="simple1" before="simple2"/> + <createData entity="ApiProductWithDescription" stepKey="simple2" before="product"/> + <createData entity="ApiBundleProduct" stepKey="product"/> + <createData entity="DropDownBundleOption" stepKey="bundleOption"> + <requiredEntity createDataKey="product"/> + </createData> + <createData entity="ApiBundleLink" stepKey="createBundleLink1"> + <requiredEntity createDataKey="product"/> + <requiredEntity createDataKey="bundleOption"/> + <requiredEntity createDataKey="simple1"/> + </createData> + <createData entity="ApiBundleLink" stepKey="createBundleLink2"> + <requiredEntity createDataKey="product"/> + <requiredEntity createDataKey="bundleOption"/> + <requiredEntity createDataKey="simple2"/> + </createData> + + <magentoCron stepKey="runCronReindex" groups="index"/> + </before> + <after> + <deleteData createDataKey="simple1" stepKey="deleteSimple1" before="deleteSimple2"/> + <deleteData createDataKey="simple2" stepKey="deleteSimple2" before="delete"/> + </after> + </test> +</tests> diff --git a/app/code/Magento/Bundle/Test/Mftf/Test/AdvanceCatalogSearchBundleByShortDescriptionTest.xml b/app/code/Magento/Bundle/Test/Mftf/Test/AdvanceCatalogSearchBundleByShortDescriptionTest.xml new file mode 100644 index 0000000000000..05e14174d14a5 --- /dev/null +++ b/app/code/Magento/Bundle/Test/Mftf/Test/AdvanceCatalogSearchBundleByShortDescriptionTest.xml @@ -0,0 +1,51 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + /** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +--> + +<tests xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/testSchema.xsd"> + <test name="AdvanceCatalogSearchBundleByShortDescriptionTest" extends="AdvanceCatalogSearchSimpleProductByShortDescriptionTest"> + <annotations> + <features value="Bundle"/> + <stories value="Advanced Catalog Product Search for all product types"/> + <title value="Guest customer should be able to advance search Bundle product with product short description"/> + <description value="Guest customer should be able to advance search Bundle product with product short description"/> + <severity value="MAJOR"/> + <testCaseId value="MC-250"/> + <group value="Bundle"/> + <group value="SearchEngineElasticsearch"/> + </annotations> + <before> + <createData entity="ApiProductWithDescription" stepKey="simple1" before="simple2"/> + <createData entity="ApiProductWithDescription" stepKey="simple2" before="product"/> + <createData entity="ApiBundleProduct" stepKey="product"/> + <createData entity="DropDownBundleOption" stepKey="bundleOption"> + <requiredEntity createDataKey="product"/> + </createData> + <createData entity="ApiBundleLink" stepKey="createBundleLink1"> + <requiredEntity createDataKey="product"/> + <requiredEntity createDataKey="bundleOption"/> + <requiredEntity createDataKey="simple1"/> + </createData> + <createData entity="ApiBundleLink" stepKey="createBundleLink2"> + <requiredEntity createDataKey="product"/> + <requiredEntity createDataKey="bundleOption"/> + <requiredEntity createDataKey="simple2"/> + </createData> + + <magentoCron stepKey="runCronReindex" groups="index"/> + </before> + <after> + <deleteData createDataKey="simple1" stepKey="deleteSimple1" before="deleteSimple2"/> + <deleteData createDataKey="simple2" stepKey="deleteSimple2" before="delete"/> + </after> + <see userInput="3 items" selector="{{StorefrontCatalogSearchAdvancedResultMainSection.itemFound}}" stepKey="see"/> + <see userInput="$$product.name$$" selector="{{StorefrontCatalogSearchAdvancedResultMainSection.nthProductName('1')}}" stepKey="seeProductName"/> + <see userInput="$$simple1.name$$" selector="{{StorefrontCatalogSearchAdvancedResultMainSection.nthProductName('2')}}" stepKey="seeSimple1ProductName"/> + <see userInput="$$simple2.name$$" selector="{{StorefrontCatalogSearchAdvancedResultMainSection.nthProductName('3')}}" stepKey="seeSimple2ProductName"/> + </test> +</tests> diff --git a/app/code/Magento/Bundle/Test/Mftf/Test/AdvanceCatalogSearchBundleBySkuTest.xml b/app/code/Magento/Bundle/Test/Mftf/Test/AdvanceCatalogSearchBundleBySkuTest.xml new file mode 100644 index 0000000000000..eadf7667b010b --- /dev/null +++ b/app/code/Magento/Bundle/Test/Mftf/Test/AdvanceCatalogSearchBundleBySkuTest.xml @@ -0,0 +1,46 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + /** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +--> + +<tests xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/testSchema.xsd"> + <test name="AdvanceCatalogSearchBundleBySkuTest" extends="AdvanceCatalogSearchSimpleProductBySkuTest"> + <annotations> + <features value="Bundle"/> + <stories value="Advanced Catalog Product Search for all product types"/> + <title value="Guest customer should be able to advance search Bundle product with product sku"/> + <description value="Guest customer should be able to advance search Bundle product with product sku"/> + <severity value="MAJOR"/> + <testCaseId value="MC-143"/> + <group value="Bundle"/> + </annotations> + <before> + <createData entity="ApiProductWithDescription" stepKey="simple1" before="simple2"/> + <createData entity="ApiProductWithDescription" stepKey="simple2" before="product"/> + <createData entity="ApiBundleProductUnderscoredSku" stepKey="product"/> + <createData entity="DropDownBundleOption" stepKey="bundleOption"> + <requiredEntity createDataKey="product"/> + </createData> + <createData entity="ApiBundleLink" stepKey="createBundleLink1"> + <requiredEntity createDataKey="product"/> + <requiredEntity createDataKey="bundleOption"/> + <requiredEntity createDataKey="simple1"/> + </createData> + <createData entity="ApiBundleLink" stepKey="createBundleLink2"> + <requiredEntity createDataKey="product"/> + <requiredEntity createDataKey="bundleOption"/> + <requiredEntity createDataKey="simple2"/> + </createData> + + <magentoCron stepKey="runCronReindex" groups="index"/> + </before> + <after> + <deleteData createDataKey="simple1" stepKey="deleteSimple1" before="deleteSimple2"/> + <deleteData createDataKey="simple2" stepKey="deleteSimple2" before="delete"/> + </after> + </test> +</tests> diff --git a/app/code/Magento/Bundle/Test/Mftf/Test/AdvanceCatalogSearchBundleProductTest.xml b/app/code/Magento/Bundle/Test/Mftf/Test/AdvanceCatalogSearchBundleProductTest.xml index c6aab0ea54ea2..e6af89181e558 100644 --- a/app/code/Magento/Bundle/Test/Mftf/Test/AdvanceCatalogSearchBundleProductTest.xml +++ b/app/code/Magento/Bundle/Test/Mftf/Test/AdvanceCatalogSearchBundleProductTest.xml @@ -36,280 +36,8 @@ <requiredEntity createDataKey="bundleOption"/> <requiredEntity createDataKey="simple2"/> </createData> - <magentoCLI command="indexer:reindex" stepKey="reindex"/> - <magentoCLI command="cache:flush" stepKey="flushCache"/> - </before> - <after> - <deleteData createDataKey="simple1" stepKey="deleteSimple1" before="deleteSimple2"/> - <deleteData createDataKey="simple2" stepKey="deleteSimple2" before="delete"/> - </after> - <see userInput="3 items" selector="{{StorefrontCatalogSearchAdvancedResultMainSection.itemFound}}" stepKey="see"/> - <see userInput="$$product.name$$" selector="{{StorefrontCatalogSearchAdvancedResultMainSection.nthProductName('1')}}" stepKey="seeProductName"/> - <see userInput="$$simple1.name$$" selector="{{StorefrontCatalogSearchAdvancedResultMainSection.nthProductName('2')}}" stepKey="seeSimple1ProductName"/> - <see userInput="$$simple2.name$$" selector="{{StorefrontCatalogSearchAdvancedResultMainSection.nthProductName('3')}}" stepKey="seeSimple2ProductName"/> - </test> - <test name="AdvanceCatalogSearchBundleByNameMysqlTest" extends="AdvanceCatalogSearchSimpleProductByNameTest"> - <annotations> - <features value="Bundle"/> - <stories value="Advanced Catalog Product Search for all product types"/> - <title value="Guest customer should be able to advance search Bundle product with product name using the MySQL search engine"/> - <description value="Guest customer should be able to advance search Bundle product with product name using the MySQL search engine"/> - <severity value="MAJOR"/> - <testCaseId value="MC-20472"/> - <group value="Bundle"/> - <group value="SearchEngineMysql"/> - </annotations> - <before> - <createData entity="ApiProductWithDescription" stepKey="simple1" before="simple2"/> - <createData entity="ApiProductWithDescription" stepKey="simple2" before="product"/> - <createData entity="ApiBundleProduct" stepKey="product"/> - <createData entity="DropDownBundleOption" stepKey="bundleOption"> - <requiredEntity createDataKey="product"/> - </createData> - <createData entity="ApiBundleLink" stepKey="createBundleLink1"> - <requiredEntity createDataKey="product"/> - <requiredEntity createDataKey="bundleOption"/> - <requiredEntity createDataKey="simple1"/> - </createData> - <createData entity="ApiBundleLink" stepKey="createBundleLink2"> - <requiredEntity createDataKey="product"/> - <requiredEntity createDataKey="bundleOption"/> - <requiredEntity createDataKey="simple2"/> - </createData> - <magentoCLI command="indexer:reindex" stepKey="reindex"/> - <magentoCLI command="cache:flush" stepKey="flushCache"/> - </before> - <after> - <deleteData createDataKey="simple1" stepKey="deleteSimple1" before="deleteSimple2"/> - <deleteData createDataKey="simple2" stepKey="deleteSimple2" before="delete"/> - </after> - </test> - <test name="AdvanceCatalogSearchBundleBySkuTest" extends="AdvanceCatalogSearchSimpleProductBySkuTest"> - <annotations> - <features value="Bundle"/> - <stories value="Advanced Catalog Product Search for all product types"/> - <title value="Guest customer should be able to advance search Bundle product with product sku"/> - <description value="Guest customer should be able to advance search Bundle product with product sku"/> - <severity value="MAJOR"/> - <testCaseId value="MC-143"/> - <group value="Bundle"/> - </annotations> - <before> - <createData entity="ApiProductWithDescription" stepKey="simple1" before="simple2"/> - <createData entity="ApiProductWithDescription" stepKey="simple2" before="product"/> - <createData entity="ApiBundleProductUnderscoredSku" stepKey="product"/> - <createData entity="DropDownBundleOption" stepKey="bundleOption"> - <requiredEntity createDataKey="product"/> - </createData> - <createData entity="ApiBundleLink" stepKey="createBundleLink1"> - <requiredEntity createDataKey="product"/> - <requiredEntity createDataKey="bundleOption"/> - <requiredEntity createDataKey="simple1"/> - </createData> - <createData entity="ApiBundleLink" stepKey="createBundleLink2"> - <requiredEntity createDataKey="product"/> - <requiredEntity createDataKey="bundleOption"/> - <requiredEntity createDataKey="simple2"/> - </createData> - <magentoCLI command="indexer:reindex" stepKey="reindex"/> - <magentoCLI command="cache:flush" stepKey="flushCache"/> - </before> - <after> - <deleteData createDataKey="simple1" stepKey="deleteSimple1" before="deleteSimple2"/> - <deleteData createDataKey="simple2" stepKey="deleteSimple2" before="delete"/> - </after> - </test> - <test name="AdvanceCatalogSearchBundleByDescriptionTest" extends="AdvanceCatalogSearchSimpleProductByDescriptionTest"> - <annotations> - <features value="Bundle"/> - <stories value="Advanced Catalog Product Search for all product types"/> - <title value="Guest customer should be able to advance search Bundle product with product description"/> - <description value="Guest customer should be able to advance search Bundle product with product description"/> - <severity value="MAJOR"/> - <testCaseId value="MC-242"/> - <group value="Bundle"/> - <group value="SearchEngineElasticsearch"/> - </annotations> - <before> - <createData entity="ApiProductWithDescription" stepKey="simple1" before="simple2"/> - <createData entity="ApiProductWithDescription" stepKey="simple2" before="product"/> - <createData entity="ApiBundleProduct" stepKey="product"/> - <createData entity="DropDownBundleOption" stepKey="bundleOption"> - <requiredEntity createDataKey="product"/> - </createData> - <createData entity="ApiBundleLink" stepKey="createBundleLink1"> - <requiredEntity createDataKey="product"/> - <requiredEntity createDataKey="bundleOption"/> - <requiredEntity createDataKey="simple1"/> - </createData> - <createData entity="ApiBundleLink" stepKey="createBundleLink2"> - <requiredEntity createDataKey="product"/> - <requiredEntity createDataKey="bundleOption"/> - <requiredEntity createDataKey="simple2"/> - </createData> - <magentoCLI command="indexer:reindex" stepKey="reindex"/> - <magentoCLI command="cache:flush" stepKey="flushCache"/> - </before> - <after> - <deleteData createDataKey="simple1" stepKey="deleteSimple1" before="deleteSimple2"/> - <deleteData createDataKey="simple2" stepKey="deleteSimple2" before="delete"/> - </after> - <see userInput="3 items" selector="{{StorefrontCatalogSearchAdvancedResultMainSection.itemFound}}" stepKey="see"/> - <see userInput="$$product.name$$" selector="{{StorefrontCatalogSearchAdvancedResultMainSection.nthProductName('1')}}" stepKey="seeProductName"/> - <see userInput="$$simple1.name$$" selector="{{StorefrontCatalogSearchAdvancedResultMainSection.nthProductName('2')}}" stepKey="seeSimple1ProductName"/> - <see userInput="$$simple2.name$$" selector="{{StorefrontCatalogSearchAdvancedResultMainSection.nthProductName('3')}}" stepKey="seeSimple2ProductName"/> - </test> - <test name="AdvanceCatalogSearchBundleByDescriptionMysqlTest" extends="AdvanceCatalogSearchSimpleProductByDescriptionTest"> - <annotations> - <features value="Bundle"/> - <stories value="Advanced Catalog Product Search for all product types"/> - <title value="Guest customer should be able to advance search Bundle product with product description using the MySQL search engine"/> - <description value="Guest customer should be able to advance search Bundle product with product description using the MySQL search engine"/> - <severity value="MAJOR"/> - <testCaseId value="MC-20473"/> - <group value="Bundle"/> - <group value="SearchEngineMysql"/> - </annotations> - <before> - <createData entity="ApiProductWithDescription" stepKey="simple1" before="simple2"/> - <createData entity="ApiProductWithDescription" stepKey="simple2" before="product"/> - <createData entity="ApiBundleProduct" stepKey="product"/> - <createData entity="DropDownBundleOption" stepKey="bundleOption"> - <requiredEntity createDataKey="product"/> - </createData> - <createData entity="ApiBundleLink" stepKey="createBundleLink1"> - <requiredEntity createDataKey="product"/> - <requiredEntity createDataKey="bundleOption"/> - <requiredEntity createDataKey="simple1"/> - </createData> - <createData entity="ApiBundleLink" stepKey="createBundleLink2"> - <requiredEntity createDataKey="product"/> - <requiredEntity createDataKey="bundleOption"/> - <requiredEntity createDataKey="simple2"/> - </createData> - <magentoCLI command="indexer:reindex" stepKey="reindex"/> - <magentoCLI command="cache:flush" stepKey="flushCache"/> - </before> - <after> - <deleteData createDataKey="simple1" stepKey="deleteSimple1" before="deleteSimple2"/> - <deleteData createDataKey="simple2" stepKey="deleteSimple2" before="delete"/> - </after> - </test> - <test name="AdvanceCatalogSearchBundleByShortDescriptionTest" extends="AdvanceCatalogSearchSimpleProductByShortDescriptionTest"> - <annotations> - <features value="Bundle"/> - <stories value="Advanced Catalog Product Search for all product types"/> - <title value="Guest customer should be able to advance search Bundle product with product short description"/> - <description value="Guest customer should be able to advance search Bundle product with product short description"/> - <severity value="MAJOR"/> - <testCaseId value="MC-250"/> - <group value="Bundle"/> - <group value="SearchEngineElasticsearch"/> - </annotations> - <before> - <createData entity="ApiProductWithDescription" stepKey="simple1" before="simple2"/> - <createData entity="ApiProductWithDescription" stepKey="simple2" before="product"/> - <createData entity="ApiBundleProduct" stepKey="product"/> - <createData entity="DropDownBundleOption" stepKey="bundleOption"> - <requiredEntity createDataKey="product"/> - </createData> - <createData entity="ApiBundleLink" stepKey="createBundleLink1"> - <requiredEntity createDataKey="product"/> - <requiredEntity createDataKey="bundleOption"/> - <requiredEntity createDataKey="simple1"/> - </createData> - <createData entity="ApiBundleLink" stepKey="createBundleLink2"> - <requiredEntity createDataKey="product"/> - <requiredEntity createDataKey="bundleOption"/> - <requiredEntity createDataKey="simple2"/> - </createData> - <magentoCLI command="indexer:reindex" stepKey="reindex"/> - <magentoCLI command="cache:flush" stepKey="flushCache"/> - </before> - <after> - <deleteData createDataKey="simple1" stepKey="deleteSimple1" before="deleteSimple2"/> - <deleteData createDataKey="simple2" stepKey="deleteSimple2" before="delete"/> - </after> - <see userInput="3 items" selector="{{StorefrontCatalogSearchAdvancedResultMainSection.itemFound}}" stepKey="see"/> - <see userInput="$$product.name$$" selector="{{StorefrontCatalogSearchAdvancedResultMainSection.nthProductName('1')}}" stepKey="seeProductName"/> - <see userInput="$$simple1.name$$" selector="{{StorefrontCatalogSearchAdvancedResultMainSection.nthProductName('2')}}" stepKey="seeSimple1ProductName"/> - <see userInput="$$simple2.name$$" selector="{{StorefrontCatalogSearchAdvancedResultMainSection.nthProductName('3')}}" stepKey="seeSimple2ProductName"/> - </test> - <test name="AdvanceCatalogSearchBundleByShortDescriptionMysqlTest" extends="AdvanceCatalogSearchSimpleProductByShortDescriptionTest"> - <annotations> - <features value="Bundle"/> - <stories value="Advanced Catalog Product Search for all product types"/> - <title value="Guest customer should be able to advance search Bundle product with product short description using the MySQL search engine"/> - <description value="Guest customer should be able to advance search Bundle product with product short description using the MySQL search engine"/> - <severity value="MAJOR"/> - <testCaseId value="MC-20474"/> - <group value="Bundle"/> - <group value="SearchEngineMysql"/> - </annotations> - <before> - <createData entity="ApiProductWithDescription" stepKey="simple1" before="simple2"/> - <createData entity="ApiProductWithDescription" stepKey="simple2" before="product"/> - <createData entity="ApiBundleProduct" stepKey="product"/> - <createData entity="DropDownBundleOption" stepKey="bundleOption"> - <requiredEntity createDataKey="product"/> - </createData> - <createData entity="ApiBundleLink" stepKey="createBundleLink1"> - <requiredEntity createDataKey="product"/> - <requiredEntity createDataKey="bundleOption"/> - <requiredEntity createDataKey="simple1"/> - </createData> - <createData entity="ApiBundleLink" stepKey="createBundleLink2"> - <requiredEntity createDataKey="product"/> - <requiredEntity createDataKey="bundleOption"/> - <requiredEntity createDataKey="simple2"/> - </createData> - <magentoCLI command="indexer:reindex" stepKey="reindex"/> - <magentoCLI command="cache:flush" stepKey="flushCache"/> - </before> - <after> - <deleteData createDataKey="simple1" stepKey="deleteSimple1" before="deleteSimple2"/> - <deleteData createDataKey="simple2" stepKey="deleteSimple2" before="delete"/> - </after> - </test> - <test name="AdvanceCatalogSearchBundleByPriceTest" extends="AdvanceCatalogSearchSimpleProductByPriceTest"> - <annotations> - <features value="Bundle"/> - <stories value="Advanced Catalog Product Search for all product types"/> - <title value="Guest customer should be able to advance search Bundle product with product price"/> - <description value="Guest customer should be able to advance search Bundle product with product price"/> - <severity value="MAJOR"/> - <testCaseId value="MC-251"/> - <group value="Bundle"/> - <group value="SearchEngineElasticsearch"/> - </annotations> - <before> - <createData entity="ApiProductWithDescription" stepKey="simple1" before="simple2"/> - <createData entity="ApiProductWithDescription" stepKey="simple2" before="product"/> - <createData entity="ApiBundleProduct" stepKey="product"/> - <createData entity="DropDownBundleOption" stepKey="bundleOption"> - <requiredEntity createDataKey="product"/> - </createData> - <createData entity="ApiBundleLink" stepKey="createBundleLink1"> - <requiredEntity createDataKey="product"/> - <requiredEntity createDataKey="bundleOption"/> - <requiredEntity createDataKey="simple1"/> - </createData> - <createData entity="ApiBundleLink" stepKey="createBundleLink2"> - <requiredEntity createDataKey="product"/> - <requiredEntity createDataKey="bundleOption"/> - <requiredEntity createDataKey="simple2"/> - </createData> - <getData entity="GetProduct" stepKey="arg1"> - <requiredEntity createDataKey="product"/> - </getData> - <getData entity="GetProduct" stepKey="arg2"> - <requiredEntity createDataKey="simple1"/> - </getData> - <getData entity="GetProduct" stepKey="arg3"> - <requiredEntity createDataKey="simple2"/> - </getData> - <magentoCLI command="indexer:reindex" stepKey="reindex"/> - <magentoCLI command="cache:flush" stepKey="flushCache"/> + + <magentoCron stepKey="runCronReindex" groups="index"/> </before> <after> <deleteData createDataKey="simple1" stepKey="deleteSimple1" before="deleteSimple2"/> @@ -320,49 +48,4 @@ <see userInput="$$simple1.name$$" selector="{{StorefrontCatalogSearchAdvancedResultMainSection.nthProductName('2')}}" stepKey="seeSimple1ProductName"/> <see userInput="$$simple2.name$$" selector="{{StorefrontCatalogSearchAdvancedResultMainSection.nthProductName('3')}}" stepKey="seeSimple2ProductName"/> </test> - <test name="AdvanceCatalogSearchBundleByPriceMysqlTest" extends="AdvanceCatalogSearchSimpleProductByPriceTest"> - <annotations> - <features value="Bundle"/> - <stories value="Advanced Catalog Product Search for all product types"/> - <title value="Guest customer should be able to advance search Bundle product with product price using the MySQL search engine"/> - <description value="Guest customer should be able to advance search Bundle product with product price the MySQL search engine"/> - <severity value="MAJOR"/> - <testCaseId value="MC-20475"/> - <group value="Bundle"/> - <group value="SearchEngineMysql"/> - </annotations> - <before> - <createData entity="ApiProductWithDescription" stepKey="simple1" before="simple2"/> - <createData entity="ApiProductWithDescription" stepKey="simple2" before="product"/> - <createData entity="ApiBundleProduct" stepKey="product"/> - <createData entity="DropDownBundleOption" stepKey="bundleOption"> - <requiredEntity createDataKey="product"/> - </createData> - <createData entity="ApiBundleLink" stepKey="createBundleLink1"> - <requiredEntity createDataKey="product"/> - <requiredEntity createDataKey="bundleOption"/> - <requiredEntity createDataKey="simple1"/> - </createData> - <createData entity="ApiBundleLink" stepKey="createBundleLink2"> - <requiredEntity createDataKey="product"/> - <requiredEntity createDataKey="bundleOption"/> - <requiredEntity createDataKey="simple2"/> - </createData> - <getData entity="GetProduct" stepKey="arg1"> - <requiredEntity createDataKey="product"/> - </getData> - <getData entity="GetProduct" stepKey="arg2"> - <requiredEntity createDataKey="simple1"/> - </getData> - <getData entity="GetProduct" stepKey="arg3"> - <requiredEntity createDataKey="simple2"/> - </getData> - <magentoCLI command="indexer:reindex" stepKey="reindex"/> - <magentoCLI command="cache:flush" stepKey="flushCache"/> - </before> - <after> - <deleteData createDataKey="simple1" stepKey="deleteSimple1" before="deleteSimple2"/> - <deleteData createDataKey="simple2" stepKey="deleteSimple2" before="delete"/> - </after> - </test> </tests> diff --git a/app/code/Magento/Bundle/Test/Mftf/Test/BundleProductFixedPricingTest.xml b/app/code/Magento/Bundle/Test/Mftf/Test/BundleProductFixedPricingTest.xml index 2b36458caa182..b8eef5c1b406f 100644 --- a/app/code/Magento/Bundle/Test/Mftf/Test/BundleProductFixedPricingTest.xml +++ b/app/code/Magento/Bundle/Test/Mftf/Test/BundleProductFixedPricingTest.xml @@ -14,7 +14,7 @@ <stories value="Bundle Product Pricing"/> <title value="Admin should be able to apply fixed pricing for Bundled Product"/> <description value="Admin should be able to apply fixed pricing for Bundled Product"/> - <severity value="CRITICAL"/> + <severity value="BLOCKER"/> <testCaseId value="MC-186"/> <group value="Bundle"/> </annotations> @@ -23,9 +23,9 @@ <createData entity="_defaultCategory" stepKey="createPreReqCategory"/> <createData entity="SimpleProduct2" stepKey="simpleProduct1"/> <createData entity="SimpleProduct2" stepKey="simpleProduct2"/> - + <magentoCron stepKey="runCronIndex" groups="index"/> <!--Admin login--> - <actionGroup stepKey="loginToAdminPanel" ref="LoginAsAdmin"/> + <actionGroup stepKey="loginToAdminPanel" ref="AdminLoginActionGroup"/> </before> <after> <deleteData createDataKey="createPreReqCategory" stepKey="deletePreReqCategory"/> @@ -35,7 +35,7 @@ <actionGroup ref="DeleteProductBySkuActionGroup" stepKey="deleteBundleProduct"> <argument name="sku" value="{{BundleProduct.sku}}"/> </actionGroup> - <actionGroup ref="logout" stepKey="logout"/> + <actionGroup ref="AdminLogoutActionGroup" stepKey="logout"/> </after> <!--Go to bundle product creation page--> <amOnPage url="{{AdminProductCreatePage.url(BundleProduct.set, BundleProduct.type)}}" stepKey="goToBundleProductCreationPage" /> diff --git a/app/code/Magento/Bundle/Test/Mftf/Test/BundleProductWithTierPriceInCartTest.xml b/app/code/Magento/Bundle/Test/Mftf/Test/BundleProductWithTierPriceInCartTest.xml index 91a2d15287033..b5812817b5640 100644 --- a/app/code/Magento/Bundle/Test/Mftf/Test/BundleProductWithTierPriceInCartTest.xml +++ b/app/code/Magento/Bundle/Test/Mftf/Test/BundleProductWithTierPriceInCartTest.xml @@ -20,7 +20,8 @@ <before> <createData entity="SimpleProduct2" stepKey="simpleProduct1"/> <createData entity="SimpleProduct2" stepKey="simpleProduct2"/> - <actionGroup ref="LoginAsAdmin" stepKey="login"/> + <magentoCron stepKey="runCronIndex" groups="index"/> + <actionGroup ref="AdminLoginActionGroup" stepKey="login"/> </before> <after> <deleteData createDataKey="simpleProduct1" stepKey="deleteSimpleProduct1"/> @@ -61,8 +62,14 @@ <argument name="price" value="Discount"/> <argument name="amount" value="50"/> </actionGroup> - <actionGroup ref="SignUpNewUserFromStorefrontActionGroup" stepKey="signUpNewUser"> - <argument name="Customer" value="CustomerEntityOne"/> + <actionGroup ref="StorefrontOpenCustomerAccountCreatePageActionGroup" stepKey="openCreateAccountPage"/> + <actionGroup ref="StorefrontFillCustomerAccountCreationFormActionGroup" stepKey="fillCreateAccountForm"> + <argument name="customer" value="CustomerEntityOne"/> + </actionGroup> + <actionGroup ref="StorefrontClickCreateAnAccountCustomerAccountCreationFormActionGroup" stepKey="submitCreateAccountForm"/> + <actionGroup ref="AssertMessageCustomerCreateAccountActionGroup" stepKey="seeSuccessMessage"> + <argument name="messageType" value="success"/> + <argument name="message" value="Thank you for registering with Main Website Store."/> </actionGroup> <amOnPage url="{{StorefrontProductPage.url(BundleProduct.urlKey)}}" stepKey="goToStorefront"/> <waitForPageLoad stepKey="waitForStorefront"/> diff --git a/app/code/Magento/Bundle/Test/Mftf/Test/CurrencyChangingBundleProductInCartTest.xml b/app/code/Magento/Bundle/Test/Mftf/Test/CurrencyChangingBundleProductInCartTest.xml index 4efacbb267a0b..ada91d068efcf 100644 --- a/app/code/Magento/Bundle/Test/Mftf/Test/CurrencyChangingBundleProductInCartTest.xml +++ b/app/code/Magento/Bundle/Test/Mftf/Test/CurrencyChangingBundleProductInCartTest.xml @@ -13,14 +13,15 @@ <stories value="Check that after changing currency price of cart is correct when the bundle product added to the cart"/> <title value="User should be able change the currency and get right price in cart when the bundle product added to the cart"/> <description value="User should be able change the currency and add one more product in cart and get right price in previous currency"/> - <severity value="MAJOR"/> + <severity value="BLOCKER"/> <testCaseId value="MAGETWO-94467"/> <group value="Bundle"/> </annotations> <before> - <actionGroup ref="LoginAsAdmin" stepKey="login"/> + <actionGroup ref="AdminLoginActionGroup" stepKey="login"/> <createData entity="SimpleProduct2" stepKey="simpleProduct1"/> <createData entity="SimpleProduct2" stepKey="simpleProduct2"/> + <magentoCron stepKey="runCronIndex" groups="index"/> </before> <after> <!-- Delete the bundled product --> @@ -76,8 +77,7 @@ <argument name="product" value="$$simpleProduct2$$"/> <argument name="currency" value="USD - US Dollar"/> </actionGroup> - <click stepKey="openMiniCart" selector="{{StorefrontMinicartSection.showCart}}"/> - <waitForPageLoad stepKey="waitForMiniCart"/> + <actionGroup ref="StorefrontClickOnMiniCartActionGroup" stepKey="openMiniCart"/> <see stepKey="seeCartSubtotal" userInput="$12,300.00"/> </test> </tests> diff --git a/app/code/Magento/Bundle/Test/Mftf/Test/EnableDisableBundleProductStatusTest.xml b/app/code/Magento/Bundle/Test/Mftf/Test/EnableDisableBundleProductStatusTest.xml index f73818a86a025..90619eeeadae9 100644 --- a/app/code/Magento/Bundle/Test/Mftf/Test/EnableDisableBundleProductStatusTest.xml +++ b/app/code/Magento/Bundle/Test/Mftf/Test/EnableDisableBundleProductStatusTest.xml @@ -23,15 +23,16 @@ <createData entity="_defaultCategory" stepKey="createPreReqCategory"/> <createData entity="SimpleProduct2" stepKey="simpleProduct1"/> <createData entity="SimpleProduct2" stepKey="simpleProduct2"/> + <magentoCron stepKey="runCronIndex" groups="index"/> <!--Admin login--> - <actionGroup stepKey="loginToAdminPanel" ref="LoginAsAdmin"/> + <actionGroup stepKey="loginToAdminPanel" ref="AdminLoginActionGroup"/> </before> <after> <deleteData createDataKey="createPreReqCategory" stepKey="deletePreReqCategory"/> <deleteData createDataKey="simpleProduct1" stepKey="deleteSimpleProduct1"/> <deleteData createDataKey="simpleProduct2" stepKey="deleteSimpleProduct2"/> - <actionGroup ref="logout" stepKey="logout"/> + <actionGroup ref="AdminLogoutActionGroup" stepKey="logout"/> </after> <!--Go to bundle product creation page--> <amOnPage url="{{AdminProductCreatePage.url(BundleProduct.set, BundleProduct.type)}}" stepKey="goToBundleProductCreationPage" /> diff --git a/app/code/Magento/Bundle/Test/Mftf/Test/MassEnableDisableBundleProductsTest.xml b/app/code/Magento/Bundle/Test/Mftf/Test/MassEnableDisableBundleProductsTest.xml index 97e509db39fa7..fd94ca93b1600 100644 --- a/app/code/Magento/Bundle/Test/Mftf/Test/MassEnableDisableBundleProductsTest.xml +++ b/app/code/Magento/Bundle/Test/Mftf/Test/MassEnableDisableBundleProductsTest.xml @@ -19,11 +19,12 @@ <group value="Bundle"/> </annotations> <before> - <actionGroup ref="LoginAsAdmin" stepKey="loginAsAdmin"/> + <actionGroup ref="AdminLoginActionGroup" stepKey="loginAsAdmin"/> <createData entity="SimpleProduct2" stepKey="simpleProduct1"/> <createData entity="SimpleProduct2" stepKey="simpleProduct2"/> <createData entity="SimpleProduct2" stepKey="simpleProduct3"/> <createData entity="SimpleProduct2" stepKey="simpleProduct4"/> + <magentoCron stepKey="runCronIndex" groups="index"/> </before> <after> <!--Clear Filters--> @@ -120,9 +121,7 @@ <click selector="{{AdminProductFiltersSection.disable}}" stepKey="ClickOnDisable"/> <waitForPageLoad stepKey="waitForPageloadToExecute"/> - <!--Clear Cache - reindex - resets products according to enabled/disabled view--> - <magentoCLI command="indexer:reindex" stepKey="reindex"/> - <magentoCLI command="cache:flush" stepKey="flushCache"/> + <magentoCron stepKey="runCronReindex" groups="index"/> <actionGroup ref="ClearPageCacheActionGroup" stepKey="clearing"/> <!--Confirm bundle products have been disabled--> diff --git a/app/code/Magento/Bundle/Test/Mftf/Test/NewBundleProductSelectionTest.xml b/app/code/Magento/Bundle/Test/Mftf/Test/NewBundleProductSelectionTest.xml index 2b948ff02d38c..efef033f9d974 100644 --- a/app/code/Magento/Bundle/Test/Mftf/Test/NewBundleProductSelectionTest.xml +++ b/app/code/Magento/Bundle/Test/Mftf/Test/NewBundleProductSelectionTest.xml @@ -19,7 +19,7 @@ <group value="Bundle"/> </annotations> <before> - <actionGroup ref="LoginAsAdmin" stepKey="loginAsAdmin"/> + <actionGroup ref="AdminLoginActionGroup" stepKey="loginAsAdmin"/> </before> <after> <amOnPage url="{{AdminLogoutPage.url}}" stepKey="amOnLogoutPage"/> diff --git a/app/code/Magento/Bundle/Test/Mftf/Test/NewProductsListWidgetBundleProductTest.xml b/app/code/Magento/Bundle/Test/Mftf/Test/NewProductsListWidgetBundleProductTest.xml index 27369d38f0c35..cc2aeb0602d36 100644 --- a/app/code/Magento/Bundle/Test/Mftf/Test/NewProductsListWidgetBundleProductTest.xml +++ b/app/code/Magento/Bundle/Test/Mftf/Test/NewProductsListWidgetBundleProductTest.xml @@ -23,6 +23,7 @@ <before> <createData entity="SimpleProduct2" stepKey="simpleProduct1"/> <createData entity="SimpleProduct2" stepKey="simpleProduct2"/> + <magentoCron stepKey="runCronIndex" groups="index"/> </before> <after> diff --git a/app/code/Magento/Bundle/Test/Mftf/Test/StorefrontAddBundleOptionsToCartTest.xml b/app/code/Magento/Bundle/Test/Mftf/Test/StorefrontAddBundleOptionsToCartTest.xml index 7ad97a8991349..8e0197697e691 100644 --- a/app/code/Magento/Bundle/Test/Mftf/Test/StorefrontAddBundleOptionsToCartTest.xml +++ b/app/code/Magento/Bundle/Test/Mftf/Test/StorefrontAddBundleOptionsToCartTest.xml @@ -19,7 +19,7 @@ <group value="Bundle"/> </annotations> <before> - <actionGroup ref="LoginAsAdmin" stepKey="login"/> + <actionGroup ref="AdminLoginActionGroup" stepKey="login"/> <createData entity="SimpleProduct2" stepKey="simpleProduct1"/> <createData entity="SimpleProduct2" stepKey="simpleProduct2"/> <createData entity="SimpleProduct2" stepKey="simpleProduct3"/> @@ -30,6 +30,7 @@ <createData entity="SimpleProduct2" stepKey="simpleProduct8"/> <createData entity="SimpleProduct2" stepKey="simpleProduct9"/> <createData entity="SimpleProduct2" stepKey="simpleProduct10"/> + <magentoCron stepKey="runCronIndex" groups="index"/> </before> <after> <deleteData createDataKey="simpleProduct1" stepKey="deleteSimpleProduct1"/> @@ -48,7 +49,7 @@ </actionGroup> <conditionalClick selector="{{AdminProductGridFilterSection.clearFilters}}" dependentSelector="{{AdminProductGridFilterSection.clearFilters}}" visible="true" stepKey="clickClearFilters"/> - <actionGroup ref="logout" stepKey="logout"/> + <actionGroup ref="AdminLogoutActionGroup" stepKey="logout"/> </after> <!-- Start creating a bundle product --> @@ -108,7 +109,10 @@ <!--Assert Bundle Product Price--> <grabTextFrom selector="{{StorefrontBundledSection.bundleProductsPrice}}" stepKey="grabProductsPrice"/> - <assertEquals expected='$123.00' expectedType="string" actual="$grabProductsPrice" message="ExpectedPrice" stepKey="assertBundleProductPrice"/> + <assertEquals message="ExpectedPrice" stepKey="assertBundleProductPrice"> + <actualResult type="const">$grabProductsPrice</actualResult> + <expectedResult type="string">$123.00</expectedResult> + </assertEquals> <!--Chose all products from 1st & 3rd options --> <click stepKey="selectProduct1" selector="{{StorefrontBundledSection.productCheckbox('1','1')}}"/> diff --git a/app/code/Magento/Bundle/Test/Mftf/Test/StorefrontAddBundleProductWithZeroPriceToShoppingCartTest.xml b/app/code/Magento/Bundle/Test/Mftf/Test/StorefrontAddBundleProductWithZeroPriceToShoppingCartTest.xml index 64b786ac4ed7c..1a40489387197 100644 --- a/app/code/Magento/Bundle/Test/Mftf/Test/StorefrontAddBundleProductWithZeroPriceToShoppingCartTest.xml +++ b/app/code/Magento/Bundle/Test/Mftf/Test/StorefrontAddBundleProductWithZeroPriceToShoppingCartTest.xml @@ -48,7 +48,7 @@ <deleteData createDataKey="apiBundleProduct" stepKey="deleteBundleProduct"/> <deleteData createDataKey="createSubCategory" stepKey="deleteCategory"/> <actionGroup ref="AdminOrdersGridClearFiltersActionGroup" stepKey="clearFilters"/> - <actionGroup ref="logout" stepKey="logout"/> + <actionGroup ref="AdminLogoutActionGroup" stepKey="logout"/> </after> <!--Open category page--> <amOnPage url="{{StorefrontCategoryPage.url($$createSubCategory.custom_attributes[url_key]$$)}}" stepKey="amOnCategoryPage"/> @@ -66,7 +66,7 @@ <grabTextFrom selector="{{CheckoutSuccessMainSection.orderNumber}}" stepKey="grabOrderNumber"/> <!--Check subtotal in created order--> - <actionGroup ref="LoginAsAdmin" stepKey="loginAsAdmin"/> + <actionGroup ref="AdminLoginActionGroup" stepKey="loginAsAdmin"/> <actionGroup ref="FilterOrderGridByIdActionGroup" stepKey="filterOrderById"> <argument name="orderId" value="$grabOrderNumber"/> </actionGroup> diff --git a/app/code/Magento/Bundle/Test/Mftf/Test/StorefrontAdminEditDataTest.xml b/app/code/Magento/Bundle/Test/Mftf/Test/StorefrontAdminEditDataTest.xml index 75e1fa5d7cd4d..a37ec0cffbbb8 100644 --- a/app/code/Magento/Bundle/Test/Mftf/Test/StorefrontAdminEditDataTest.xml +++ b/app/code/Magento/Bundle/Test/Mftf/Test/StorefrontAdminEditDataTest.xml @@ -19,9 +19,10 @@ <group value="Bundle"/> </annotations> <before> - <actionGroup ref="LoginAsAdmin" stepKey="loginAsAdmin"/> + <actionGroup ref="AdminLoginActionGroup" stepKey="loginAsAdmin"/> <createData entity="SimpleProduct2" stepKey="simpleProduct1"/> <createData entity="SimpleProduct2" stepKey="simpleProduct2"/> + <magentoCron stepKey="runCronIndex" groups="index"/> </before> <after> <amOnPage url="{{AdminLogoutPage.url}}" stepKey="amOnLogoutPage"/> @@ -79,7 +80,7 @@ <waitForPageLoad stepKey="waitForElementAdded"/> <!-- Go to the shopping cart page and grab the value of the option title --> - <amOnPage url="/checkout/cart/" stepKey="onPageShoppingCart"/> + <amOnPage url="{{CheckoutCartPage.url}}" stepKey="onPageShoppingCart"/> <waitForPageLoad stepKey="waitForCartPageLoad"/> <grabTextFrom selector="{{CheckoutCartProductSection.nthBundleOptionName('1')}}" stepKey="grabTotalBefore"/> @@ -100,10 +101,13 @@ <see stepKey="assertSuccess2" selector="{{AdminProductMessagesSection.successMessage}}" userInput="You saved the product."/> <!-- Go to the shopping cart page and make sure the title has changed --> - <amOnPage url="/checkout/cart/" stepKey="onPageShoppingCart1"/> + <amOnPage url="{{CheckoutCartPage.url}}" stepKey="onPageShoppingCart1"/> <waitForPageLoad stepKey="waitForCartPageLoad1"/> <grabTextFrom selector="{{CheckoutCartProductSection.nthBundleOptionName('1')}}" stepKey="grabTotalAfter"/> - <assertNotEquals expected="{$grabTotalBefore}" expectedType="string" actual="{$grabTotalAfter}" actualType="string" stepKey="assertNotEquals"/> + <assertNotEquals stepKey="assertNotEquals"> + <actualResult type="string">{$grabTotalAfter}</actualResult> + <expectedResult type="string">{$grabTotalBefore}</expectedResult> + </assertNotEquals> <!-- Delete the bundled product --> <actionGroup stepKey="deleteBundle" ref="DeleteProductUsingProductGridActionGroup"> diff --git a/app/code/Magento/Bundle/Test/Mftf/Test/StorefrontAdvanceCatalogSearchBundleBySkuWithHyphenTest.xml b/app/code/Magento/Bundle/Test/Mftf/Test/StorefrontAdvanceCatalogSearchBundleBySkuWithHyphenTest.xml index d8d6034cd1a21..07be59b998dec 100644 --- a/app/code/Magento/Bundle/Test/Mftf/Test/StorefrontAdvanceCatalogSearchBundleBySkuWithHyphenTest.xml +++ b/app/code/Magento/Bundle/Test/Mftf/Test/StorefrontAdvanceCatalogSearchBundleBySkuWithHyphenTest.xml @@ -36,8 +36,7 @@ <requiredEntity createDataKey="bundleOption"/> <requiredEntity createDataKey="simple2"/> </createData> - <magentoCLI command="indexer:reindex" stepKey="reindex"/> - <magentoCLI command="cache:flush" stepKey="flushCache"/> + <magentoCron stepKey="runCronReindex" groups="index"/> </before> <after> <deleteData createDataKey="simple1" stepKey="deleteSimple1" before="deleteSimple2"/> diff --git a/app/code/Magento/Bundle/Test/Mftf/Test/StorefrontBundleAddToCartSuccessTest.xml b/app/code/Magento/Bundle/Test/Mftf/Test/StorefrontBundleAddToCartSuccessTest.xml new file mode 100644 index 0000000000000..85be636ec269c --- /dev/null +++ b/app/code/Magento/Bundle/Test/Mftf/Test/StorefrontBundleAddToCartSuccessTest.xml @@ -0,0 +1,114 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + /** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +--> + +<tests xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/testSchema.xsd"> + <test name="StorefrontBundleAddToCartSuccessTest"> + <annotations> + <features value="Bundle"/> + <stories value="Bundle product details page"/> + <title value="Customer should be able to add the bundle product to the cart"/> + <description value="Customer should be able to add the bundle product to the cart"/> + <severity value="CRITICAL"/> + <testCaseId value="MC-232"/> + <group value="Bundle"/> + </annotations> + <before> + <actionGroup ref="AdminLoginActionGroup" stepKey="login"/> + <createData entity="SimpleProduct2" stepKey="simpleProduct1"/> + <createData entity="SimpleProduct2" stepKey="simpleProduct2"/> + </before> + <after> + <amOnPage url="{{AdminLogoutPage.url}}" stepKey="logout"/> + <deleteData createDataKey="simpleProduct1" stepKey="deleteSimpleProduct1"/> + <deleteData createDataKey="simpleProduct2" stepKey="deleteSimpleProduct2"/> + </after> + + <!-- Start creating a bundle product --> + <amOnPage url="{{AdminProductIndexPage.url}}" stepKey="goToProductList"/> + <waitForPageLoad stepKey="waitForProductList"/> + <actionGroup ref="GoToCreateProductPageActionGroup" stepKey="goToCreateProduct"> + <argument name="product" value="BundleProduct"/> + </actionGroup> + <actionGroup ref="FillProductNameAndSkuInProductFormActionGroup" stepKey="fillNameAndSku"> + <argument name="product" value="BundleProduct"/> + </actionGroup> + + <!-- Add Option One, a "Drop-down" type option --> + <actionGroup ref="AddBundleOptionWithTwoProductsActionGroup" stepKey="addBundleOptionWithTwoProducts1"> + <argument name="x" value="0"/> + <argument name="n" value="1"/> + <argument name="prodOneSku" value="$$simpleProduct1.sku$$"/> + <argument name="prodTwoSku" value="$$simpleProduct2.sku$$"/> + <argument name="optionTitle" value="Option One"/> + <argument name="inputType" value="select"/> + </actionGroup> + + <!-- Add Option Two, a "Radio Buttons" type option --> + <actionGroup ref="AddBundleOptionWithTwoProductsActionGroup" stepKey="addBundleOptionWithTwoProducts2"> + <argument name="x" value="1"/> + <argument name="n" value="2"/> + <argument name="prodOneSku" value="$$simpleProduct1.sku$$"/> + <argument name="prodTwoSku" value="$$simpleProduct2.sku$$"/> + <argument name="optionTitle" value="Option Two"/> + <argument name="inputType" value="radio"/> + </actionGroup> + + <!-- Add Option Three, a "Checkbox" type option --> + <actionGroup ref="AddBundleOptionWithTwoProductsActionGroup" stepKey="addBundleOptionWithTwoProducts3"> + <argument name="x" value="2"/> + <argument name="n" value="3"/> + <argument name="prodOneSku" value="$$simpleProduct1.sku$$"/> + <argument name="prodTwoSku" value="$$simpleProduct2.sku$$"/> + <argument name="optionTitle" value="Option Three"/> + <argument name="inputType" value="checkbox"/> + </actionGroup> + + <!-- Add Option Four, a "Multi Select" type option --> + <actionGroup ref="AddBundleOptionWithTwoProductsActionGroup" stepKey="addBundleOptionWithTwoProducts4"> + <argument name="x" value="3"/> + <argument name="n" value="4"/> + <argument name="prodOneSku" value="$$simpleProduct1.sku$$"/> + <argument name="prodTwoSku" value="$$simpleProduct2.sku$$"/> + <argument name="optionTitle" value="Option Four"/> + <argument name="inputType" value="multi"/> + </actionGroup> + + <!-- Save product and go to storefront --> + <actionGroup ref="SaveProductFormActionGroup" stepKey="saveProduct"/> + <amOnPage url="{{BundleProduct.sku}}.html" stepKey="goToStorefront"/> + <waitForPageLoad stepKey="waitForStorefront"/> + <click selector="{{StorefrontBundledSection.addToCart}}" stepKey="clickCustomize"/> + + <!-- Select all applicable options --> + <selectOption selector="select.bundle-option-select" userInput="$$simpleProduct1.name$$ +$123.00" stepKey="selectOption1"/> + <click selector="input[type='radio']:nth-of-type(1)" stepKey="selectOption2"/> + <checkOption selector="input[type='checkbox']:nth-of-type(1)" stepKey="selectOption3"/> + <selectOption selector="select[multiple='multiple']" userInput="$$simpleProduct1.name$$ +$123.00" stepKey="selectOption4"/> + + <!-- Customize and add the bundle product to our cart --> + <click selector="{{StorefrontBundledSection.addToCartConfigured}}" stepKey="clickAddToCart1"/> + <dontSee selector="{{StorefrontBundledSection.nthOptionDiv('1')}}" userInput="This is a required field." stepKey="validForm1"/> + <dontSee selector="{{StorefrontBundledSection.nthOptionDiv('2')}}" userInput="Please select one of the options." stepKey="validForm2"/> + <dontSee selector="{{StorefrontBundledSection.nthOptionDiv('3')}}" userInput="Please select one of the options." stepKey="validForm3"/> + <dontSee selector="{{StorefrontBundledSection.nthOptionDiv('4')}}" userInput="This is a required field." stepKey="validForm4"/> + <see selector="{{StorefrontMessagesSection.success}}" userInput="You added {{BundleProduct.name}} to your shopping cart." stepKey="seeAddToCartSuccessMessage"/> + + <!-- Verify cart contents --> + <amOnPage url="{{CheckoutCartPage.url}}" stepKey="goToCart"/> + <waitForPageLoad stepKey="waitForCart"/> + <see selector="{{StorefrontBundledSection.nthItemOptionsTitle('1')}}" userInput="Option One" stepKey="seeOption1"/> + <see selector="{{StorefrontBundledSection.nthItemOptionsTitle('2')}}" userInput="Option Two" stepKey="seeOption2"/> + <see selector="{{StorefrontBundledSection.nthItemOptionsTitle('3')}}" userInput="Option Three" stepKey="seeOption3"/> + <see selector="{{StorefrontBundledSection.nthItemOptionsTitle('4')}}" userInput="Option Four" stepKey="seeOption4"/> + <see selector="{{StorefrontBundledSection.nthItemOptionsValue('1')}}" userInput="50 x $$simpleProduct1.name$$ $123.00" stepKey="seeOptionValue1"/> + <see selector="{{StorefrontBundledSection.nthItemOptionsValue('2')}}" userInput="50 x $$simpleProduct1.name$$ $123.00" stepKey="seeOptionValue2"/> + <see selector="{{StorefrontBundledSection.nthItemOptionsValue('3')}}" userInput="50 x $$simpleProduct1.name$$ $123.00" stepKey="seeOptionValue3"/> + <see selector="{{StorefrontBundledSection.nthItemOptionsValue('4')}}" userInput="50 x $$simpleProduct1.name$$ $123.00" stepKey="seeOptionValue4"/> + </test> +</tests> diff --git a/app/code/Magento/Bundle/Test/Mftf/Test/StorefrontBundleCartTest.xml b/app/code/Magento/Bundle/Test/Mftf/Test/StorefrontBundleCartTest.xml index d617ced82074e..966082739aa68 100644 --- a/app/code/Magento/Bundle/Test/Mftf/Test/StorefrontBundleCartTest.xml +++ b/app/code/Magento/Bundle/Test/Mftf/Test/StorefrontBundleCartTest.xml @@ -19,9 +19,10 @@ <group value="Bundle"/> </annotations> <before> - <actionGroup ref="LoginAsAdmin" stepKey="login"/> + <actionGroup ref="AdminLoginActionGroup" stepKey="login"/> <createData entity="SimpleProduct2" stepKey="simpleProduct1"/> <createData entity="SimpleProduct2" stepKey="simpleProduct2"/> + <magentoCron stepKey="runCronIndex" groups="index"/> </before> <after> <amOnPage url="{{AdminLogoutPage.url}}" stepKey="logout"/> @@ -124,108 +125,4 @@ <dontSee selector="{{StorefrontBundledSection.nthOptionDiv('3')}}" userInput="Please select one of the options." stepKey="error19"/> <dontSee selector="{{StorefrontBundledSection.nthOptionDiv('4')}}" userInput="This is a required field." stepKey="error20"/> </test> - - <test name="StorefrontBundleAddToCartSuccessTest"> - <annotations> - <features value="Bundle"/> - <stories value="Bundle product details page"/> - <title value="Customer should be able to add the bundle product to the cart"/> - <description value="Customer should be able to add the bundle product to the cart"/> - <severity value="CRITICAL"/> - <testCaseId value="MC-232"/> - <group value="Bundle"/> - </annotations> - <before> - <actionGroup ref="LoginAsAdmin" stepKey="login"/> - <createData entity="SimpleProduct2" stepKey="simpleProduct1"/> - <createData entity="SimpleProduct2" stepKey="simpleProduct2"/> - </before> - <after> - <amOnPage url="{{AdminLogoutPage.url}}" stepKey="logout"/> - <deleteData createDataKey="simpleProduct1" stepKey="deleteSimpleProduct1"/> - <deleteData createDataKey="simpleProduct2" stepKey="deleteSimpleProduct2"/> - </after> - - <!-- Start creating a bundle product --> - <amOnPage url="{{AdminProductIndexPage.url}}" stepKey="goToProductList"/> - <waitForPageLoad stepKey="waitForProductList"/> - <actionGroup ref="GoToCreateProductPageActionGroup" stepKey="goToCreateProduct"> - <argument name="product" value="BundleProduct"/> - </actionGroup> - <actionGroup ref="FillProductNameAndSkuInProductFormActionGroup" stepKey="fillNameAndSku"> - <argument name="product" value="BundleProduct"/> - </actionGroup> - - <!-- Add Option One, a "Drop-down" type option --> - <actionGroup ref="AddBundleOptionWithTwoProductsActionGroup" stepKey="addBundleOptionWithTwoProducts1"> - <argument name="x" value="0"/> - <argument name="n" value="1"/> - <argument name="prodOneSku" value="$$simpleProduct1.sku$$"/> - <argument name="prodTwoSku" value="$$simpleProduct2.sku$$"/> - <argument name="optionTitle" value="Option One"/> - <argument name="inputType" value="select"/> - </actionGroup> - - <!-- Add Option Two, a "Radio Buttons" type option --> - <actionGroup ref="AddBundleOptionWithTwoProductsActionGroup" stepKey="addBundleOptionWithTwoProducts2"> - <argument name="x" value="1"/> - <argument name="n" value="2"/> - <argument name="prodOneSku" value="$$simpleProduct1.sku$$"/> - <argument name="prodTwoSku" value="$$simpleProduct2.sku$$"/> - <argument name="optionTitle" value="Option Two"/> - <argument name="inputType" value="radio"/> - </actionGroup> - - <!-- Add Option Three, a "Checkbox" type option --> - <actionGroup ref="AddBundleOptionWithTwoProductsActionGroup" stepKey="addBundleOptionWithTwoProducts3"> - <argument name="x" value="2"/> - <argument name="n" value="3"/> - <argument name="prodOneSku" value="$$simpleProduct1.sku$$"/> - <argument name="prodTwoSku" value="$$simpleProduct2.sku$$"/> - <argument name="optionTitle" value="Option Three"/> - <argument name="inputType" value="checkbox"/> - </actionGroup> - - <!-- Add Option Four, a "Multi Select" type option --> - <actionGroup ref="AddBundleOptionWithTwoProductsActionGroup" stepKey="addBundleOptionWithTwoProducts4"> - <argument name="x" value="3"/> - <argument name="n" value="4"/> - <argument name="prodOneSku" value="$$simpleProduct1.sku$$"/> - <argument name="prodTwoSku" value="$$simpleProduct2.sku$$"/> - <argument name="optionTitle" value="Option Four"/> - <argument name="inputType" value="multi"/> - </actionGroup> - - <!-- Save product and go to storefront --> - <actionGroup ref="SaveProductFormActionGroup" stepKey="saveProduct"/> - <amOnPage url="{{BundleProduct.sku}}.html" stepKey="goToStorefront"/> - <waitForPageLoad stepKey="waitForStorefront"/> - <click selector="{{StorefrontBundledSection.addToCart}}" stepKey="clickCustomize"/> - - <!-- Select all applicable options --> - <selectOption selector="select.bundle-option-select" userInput="$$simpleProduct1.name$$ +$123.00" stepKey="selectOption1"/> - <click selector="input[type='radio']:nth-of-type(1)" stepKey="selectOption2"/> - <checkOption selector="input[type='checkbox']:nth-of-type(1)" stepKey="selectOption3"/> - <selectOption selector="select[multiple='multiple']" userInput="$$simpleProduct1.name$$ +$123.00" stepKey="selectOption4"/> - - <!-- Customize and add the bundle product to our cart --> - <click selector="{{StorefrontBundledSection.addToCartConfigured}}" stepKey="clickAddToCart1"/> - <dontSee selector="{{StorefrontBundledSection.nthOptionDiv('1')}}" userInput="This is a required field." stepKey="validForm1"/> - <dontSee selector="{{StorefrontBundledSection.nthOptionDiv('2')}}" userInput="Please select one of the options." stepKey="validForm2"/> - <dontSee selector="{{StorefrontBundledSection.nthOptionDiv('3')}}" userInput="Please select one of the options." stepKey="validForm3"/> - <dontSee selector="{{StorefrontBundledSection.nthOptionDiv('4')}}" userInput="This is a required field." stepKey="validForm4"/> - <see selector="{{StorefrontMessagesSection.success}}" userInput="You added {{BundleProduct.name}} to your shopping cart." stepKey="seeAddToCartSuccessMessage"/> - - <!-- Verify cart contents --> - <amOnPage url="{{CheckoutCartPage.url}}" stepKey="goToCart"/> - <waitForPageLoad stepKey="waitForCart"/> - <see selector="{{StorefrontBundledSection.nthItemOptionsTitle('1')}}" userInput="Option One" stepKey="seeOption1"/> - <see selector="{{StorefrontBundledSection.nthItemOptionsTitle('2')}}" userInput="Option Two" stepKey="seeOption2"/> - <see selector="{{StorefrontBundledSection.nthItemOptionsTitle('3')}}" userInput="Option Three" stepKey="seeOption3"/> - <see selector="{{StorefrontBundledSection.nthItemOptionsTitle('4')}}" userInput="Option Four" stepKey="seeOption4"/> - <see selector="{{StorefrontBundledSection.nthItemOptionsValue('1')}}" userInput="50 x $$simpleProduct1.name$$ $123.00" stepKey="seeOptionValue1"/> - <see selector="{{StorefrontBundledSection.nthItemOptionsValue('2')}}" userInput="50 x $$simpleProduct1.name$$ $123.00" stepKey="seeOptionValue2"/> - <see selector="{{StorefrontBundledSection.nthItemOptionsValue('3')}}" userInput="50 x $$simpleProduct1.name$$ $123.00" stepKey="seeOptionValue3"/> - <see selector="{{StorefrontBundledSection.nthItemOptionsValue('4')}}" userInput="50 x $$simpleProduct1.name$$ $123.00" stepKey="seeOptionValue4"/> - </test> </tests> diff --git a/app/code/Magento/Bundle/Test/Mftf/Test/StorefrontBundleProductDetailsTest.xml b/app/code/Magento/Bundle/Test/Mftf/Test/StorefrontBundleProductDetailsTest.xml index cc0064e87c3c1..786040d16b7e2 100644 --- a/app/code/Magento/Bundle/Test/Mftf/Test/StorefrontBundleProductDetailsTest.xml +++ b/app/code/Magento/Bundle/Test/Mftf/Test/StorefrontBundleProductDetailsTest.xml @@ -14,7 +14,7 @@ <stories value="Bundle product details page"/> <title value="Customer should be able to see basic bundle product details"/> <description value="Customer should be able to see basic bundle product details"/> - <severity value="CRITICAL"/> + <severity value="BLOCKER"/> <testCaseId value="MC-230"/> <group value="Bundle"/> </annotations> @@ -23,9 +23,9 @@ <createData entity="_defaultCategory" stepKey="createPreReqCategory"/> <createData entity="SimpleProduct2" stepKey="simpleProduct1"/> <createData entity="SimpleProduct2" stepKey="simpleProduct2"/> - + <magentoCron stepKey="runCronIndex" groups="index"/> <!-- Admin Login--> - <actionGroup stepKey="loginToAdminPanel" ref="LoginAsAdmin"/> + <actionGroup stepKey="loginToAdminPanel" ref="AdminLoginActionGroup"/> <actionGroup ref="DisabledWYSIWYGActionGroup" stepKey="disableWYSIWYG"/> </before> <after> @@ -34,7 +34,7 @@ <deleteData createDataKey="simpleProduct2" stepKey="deleteSimpleProduct2"/> <actionGroup ref="AdminOpenProductIndexPageActionGroup" stepKey="navigateToProductIndexPage"/> <actionGroup ref="DeleteProductsIfTheyExistActionGroup" stepKey="deleteAllProducts"/> - <actionGroup ref="logout" stepKey="logout"/> + <actionGroup ref="AdminLogoutActionGroup" stepKey="logout"/> </after> <!-- go to bundle product creation page--> <amOnPage url="{{AdminProductCreatePage.url(BundleProduct.set, BundleProduct.type)}}" stepKey="goToBundleProductCreationPage" /> diff --git a/app/code/Magento/Bundle/Test/Mftf/Test/StorefrontBundleProductShownInCategoryListAndGrid.xml b/app/code/Magento/Bundle/Test/Mftf/Test/StorefrontBundleProductShownInCategoryListAndGrid.xml deleted file mode 100644 index 364d4fa68e590..0000000000000 --- a/app/code/Magento/Bundle/Test/Mftf/Test/StorefrontBundleProductShownInCategoryListAndGrid.xml +++ /dev/null @@ -1,105 +0,0 @@ -<?xml version="1.0" encoding="UTF-8"?> -<!-- - /** - * Copyright © Magento, Inc. All rights reserved. - * See COPYING.txt for license details. - */ ---> - -<tests xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" - xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/testSchema.xsd"> - <test name="StorefrontBundleProductShownInCategoryListAndGrid"> - <annotations> - <features value="Bundle"/> - <stories value="Bundle products list on Storefront"/> - <title value="Customer should be able to see bundle products in the category products list and grid views"/> - <description value="Customer should be able to see bundle products in the category products list and grid views"/> - <severity value="CRITICAL"/> - <testCaseId value="MC-226"/> - <group value="Bundle"/> - </annotations> - <before> - <!--Admin login--> - <actionGroup ref="LoginAsAdmin" stepKey="loginAsAdmin"/> - <createData entity="SimpleProduct2" stepKey="simpleProduct1"/> - <createData entity="SimpleProduct2" stepKey="simpleProduct2"/> - <createData entity="SimpleProduct2" stepKey="simpleProduct3"/> - <createData entity="SimpleProduct2" stepKey="simpleProduct4"/> - </before> - <after> - <!--Logging out--> - <amOnPage url="{{AdminLogoutPage.url}}" stepKey="amOnLogoutPage"/> - <deleteData createDataKey="simpleProduct1" stepKey="deleteSimpleProduct1"/> - <deleteData createDataKey="simpleProduct2" stepKey="deleteSimpleProduct2"/> - <deleteData createDataKey="simpleProduct3" stepKey="deleteSimpleProduct3"/> - <deleteData createDataKey="simpleProduct4" stepKey="deleteSimpleProduct4"/> - </after> - <!--Make category--> - <amOnPage url="{{AdminCategoryPage.url}}" stepKey="goToCategoryPage"/> - <waitForPageLoad stepKey="waitForCategoryPageLoad"/> - <actionGroup ref="CreateCategoryActionGroup" stepKey="createASubcategory"> - <argument name="categoryEntity" value="SimpleSubCategory"/> - </actionGroup> - - <!--Go to bundle product creation page--> - <amOnPage url="{{AdminProductCreatePage.url(BundleProduct.set, BundleProduct.type)}}" stepKey="goToBundleProductCreationPage" /> - <waitForPageLoad stepKey="waitForBundleProductCreatePageToLoad"/> - - <!--Categories--> - <click selector="{{AdminProductFormBundleSection.categoriesDropDown}}" stepKey="dropDownCategories"/> - <fillField selector="{{AdminProductFormBundleSection.searchForCategory}}" userInput="{{SimpleSubCategory.name}}" stepKey="searchForCategory"/> - <click selector="{{AdminProductFormBundleSection.selectCategory}}" stepKey="selectCategory"/> - <click selector="{{AdminProductFormBundleSection.categoriesLabel}}" stepKey="clickOnCategoriesLabelToCloseOptions"/> - - <!--Create bundle product--> - <conditionalClick selector="{{AdminProductFormBundleSection.bundleItemsToggle}}" dependentSelector="{{AdminProductFormBundleSection.bundleItemsToggle}}" visible="false" stepKey="conditionallyOpenSectionBundleItems"/> - <click selector="{{AdminProductFormBundleSection.addOption}}" stepKey="clickAddOption3"/> - <waitForElementVisible selector="{{AdminProductFormBundleSection.bundleOptionXTitle('0')}}" stepKey="waitForBundleOptions"/> - <fillField selector="{{AdminProductFormBundleSection.bundleOptionXTitle('0')}}" userInput="{{BundleProduct.optionTitle1}}" stepKey="fillOptionTitle"/> - <selectOption selector="{{AdminProductFormBundleSection.bundleOptionXInputType('0')}}" userInput="{{BundleProduct.optionInputType1}}" stepKey="selectInputType"/> - <waitForElementVisible selector="{{AdminProductFormBundleSection.addProductsToOption}}" stepKey="waitForAddProductsToBundle"/> - <click selector="{{AdminProductFormBundleSection.addProductsToOption}}" stepKey="clickAddProductsToOption"/> - <actionGroup ref="FilterProductGridBySkuActionGroup" stepKey="filterBundleProductOptions"> - <argument name="product" value="$$simpleProduct1$$"/> - </actionGroup> - <checkOption selector="{{AdminAddProductsToOptionPanel.firstCheckbox}}" stepKey="selectFirstGridRow"/> - <actionGroup ref="FilterProductGridBySkuActionGroup" stepKey="filterBundleProductOptions2"> - <argument name="product" value="$$simpleProduct2$$"/> - </actionGroup> - <checkOption selector="{{AdminAddProductsToOptionPanel.firstCheckbox}}" stepKey="selectFirstGridRow2"/> - <click selector="{{AdminAddProductsToOptionPanel.addSelectedProducts}}" stepKey="clickAddSelectedBundleProducts"/> - <fillField selector="{{AdminProductFormBundleSection.bundleOptionXProductYQuantity('0', '0')}}" userInput="{{BundleProduct.defaultQuantity}}" stepKey="fillProductDefaultQty1"/> - <fillField selector="{{AdminProductFormBundleSection.bundleOptionXProductYQuantity('0', '1')}}" userInput="{{BundleProduct.defaultQuantity}}" stepKey="fillProductDefaultQty2"/> - <actionGroup ref="AncillaryPrepBundleProductActionGroup" stepKey="createBundledProductForTwoSimpleProducts"/> - - <!--Save the product--> - <click selector="{{AdminProductFormActionSection.saveButton}}" stepKey="clickSaveButton"/> - <seeElement selector="{{AdminCategoryMessagesSection.SuccessMessage}}" stepKey="messageYouSavedTheProductIsShown"/> - - <!-- Perform reindex and flush cache --> - <magentoCLI command="indexer:reindex" stepKey="reindex"/> - <magentoCLI command="cache:flush" stepKey="flushCache"/> - - <!--Go to category page--> - <amOnPage url="{{StorefrontHomePage.url}}" stepKey="goToHomePage"/> - <waitForPageLoad stepKey="waitForHomePageToload"/> - <click selector="{{StorefrontHeaderSection.NavigationCategoryByName(SimpleSubCategory.name)}}" stepKey="cartClickCategory"/> - - <!--Check in grid view--> - <seeInTitle userInput="{{SimpleSubCategory.name}}" stepKey="assertCategoryNameInTitle"/> - <see userInput="{{SimpleSubCategory.name}}" selector="{{StorefrontCategoryMainSection.CategoryTitle}}" stepKey="assertCategoryName"/> - <see userInput="1" selector="{{StorefrontCategoryMainSection.productCount}} span" stepKey="assertProductCount"/> - <seeElement selector="{{StorefrontCategoryProductSection.listedProduct('1')}}" stepKey="assertBundleProductPresence"/> - <see userInput="{{BundleProduct.name}}" selector="{{StorefrontCategoryProductSection.ProductTitleByNumber('1')}}" stepKey="checkTitle"/> - <see userInput="$1,230.00" selector="{{StorefrontCategoryProductSection.ProductPriceByNumber('1')}}" stepKey="checkPrice"/> - - <!--Check in list view--> - <click selector="{{StorefrontCategoryProductSection.categoryListView}}" stepKey="switchToListView"/> - <seeInTitle userInput="{{SimpleSubCategory.name}}" stepKey="assertCategoryNameInTitleAgain"/> - <see userInput="{{SimpleSubCategory.name}}" selector="{{StorefrontCategoryMainSection.CategoryTitle}}" stepKey="assertCategoryNamAgain"/> - <see userInput="1" selector="{{StorefrontCategoryMainSection.productCount}} span" stepKey="assertProductCountAgain"/> - <seeElement selector="{{StorefrontCategoryProductSection.listedProduct('1')}}" stepKey="assertBundleProductPresenceAgain"/> - <see userInput="{{BundleProduct.name}}" selector="{{StorefrontCategoryProductSection.ProductTitleByNumber('1')}}" stepKey="checkTitleAgain"/> - <see userInput="$1,230.00" selector="{{StorefrontCategoryProductSection.ProductPriceByNumber('1')}}" stepKey="checkPriceAgain"/> - </test> -</tests> diff --git a/app/code/Magento/Bundle/Test/Mftf/Test/StorefrontBundleProductShownInCategoryListAndGridTest.xml b/app/code/Magento/Bundle/Test/Mftf/Test/StorefrontBundleProductShownInCategoryListAndGridTest.xml new file mode 100644 index 0000000000000..822b42af4c5a8 --- /dev/null +++ b/app/code/Magento/Bundle/Test/Mftf/Test/StorefrontBundleProductShownInCategoryListAndGridTest.xml @@ -0,0 +1,103 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + /** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +--> + +<tests xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/testSchema.xsd"> + <test name="StorefrontBundleProductShownInCategoryListAndGridTest"> + <annotations> + <features value="Bundle"/> + <stories value="Bundle products list on Storefront"/> + <title value="Customer should be able to see bundle products in the category products list and grid views"/> + <description value="Customer should be able to see bundle products in the category products list and grid views"/> + <severity value="BLOCKER"/> + <testCaseId value="MC-226"/> + <group value="Bundle"/> + </annotations> + <before> + <!--Admin login--> + <actionGroup ref="AdminLoginActionGroup" stepKey="loginAsAdmin"/> + <createData entity="SimpleProduct2" stepKey="simpleProduct1"/> + <createData entity="SimpleProduct2" stepKey="simpleProduct2"/> + <createData entity="SimpleProduct2" stepKey="simpleProduct3"/> + <createData entity="SimpleProduct2" stepKey="simpleProduct4"/> + <magentoCron stepKey="runCronIndex" groups="index"/> + </before> + <after> + <!--Logging out--> + <amOnPage url="{{AdminLogoutPage.url}}" stepKey="amOnLogoutPage"/> + <deleteData createDataKey="simpleProduct1" stepKey="deleteSimpleProduct1"/> + <deleteData createDataKey="simpleProduct2" stepKey="deleteSimpleProduct2"/> + <deleteData createDataKey="simpleProduct3" stepKey="deleteSimpleProduct3"/> + <deleteData createDataKey="simpleProduct4" stepKey="deleteSimpleProduct4"/> + </after> + <!--Make category--> + <amOnPage url="{{AdminCategoryPage.url}}" stepKey="goToCategoryPage"/> + <waitForPageLoad stepKey="waitForCategoryPageLoad"/> + <actionGroup ref="CreateCategoryActionGroup" stepKey="createASubcategory"> + <argument name="categoryEntity" value="SimpleSubCategory"/> + </actionGroup> + + <!--Go to bundle product creation page--> + <amOnPage url="{{AdminProductCreatePage.url(BundleProduct.set, BundleProduct.type)}}" stepKey="goToBundleProductCreationPage" /> + <waitForPageLoad stepKey="waitForBundleProductCreatePageToLoad"/> + + <!--Categories--> + <click selector="{{AdminProductFormBundleSection.categoriesDropDown}}" stepKey="dropDownCategories"/> + <fillField selector="{{AdminProductFormBundleSection.searchForCategory}}" userInput="{{SimpleSubCategory.name}}" stepKey="searchForCategory"/> + <click selector="{{AdminProductFormBundleSection.selectCategory}}" stepKey="selectCategory"/> + <click selector="{{AdminProductFormBundleSection.categoriesLabel}}" stepKey="clickOnCategoriesLabelToCloseOptions"/> + + <!--Create bundle product--> + <conditionalClick selector="{{AdminProductFormBundleSection.bundleItemsToggle}}" dependentSelector="{{AdminProductFormBundleSection.bundleItemsToggle}}" visible="false" stepKey="conditionallyOpenSectionBundleItems"/> + <click selector="{{AdminProductFormBundleSection.addOption}}" stepKey="clickAddOption3"/> + <waitForElementVisible selector="{{AdminProductFormBundleSection.bundleOptionXTitle('0')}}" stepKey="waitForBundleOptions"/> + <fillField selector="{{AdminProductFormBundleSection.bundleOptionXTitle('0')}}" userInput="{{BundleProduct.optionTitle1}}" stepKey="fillOptionTitle"/> + <selectOption selector="{{AdminProductFormBundleSection.bundleOptionXInputType('0')}}" userInput="{{BundleProduct.optionInputType1}}" stepKey="selectInputType"/> + <waitForElementVisible selector="{{AdminProductFormBundleSection.addProductsToOption}}" stepKey="waitForAddProductsToBundle"/> + <click selector="{{AdminProductFormBundleSection.addProductsToOption}}" stepKey="clickAddProductsToOption"/> + <actionGroup ref="FilterProductGridBySkuActionGroup" stepKey="filterBundleProductOptions"> + <argument name="product" value="$$simpleProduct1$$"/> + </actionGroup> + <checkOption selector="{{AdminAddProductsToOptionPanel.firstCheckbox}}" stepKey="selectFirstGridRow"/> + <actionGroup ref="FilterProductGridBySkuActionGroup" stepKey="filterBundleProductOptions2"> + <argument name="product" value="$$simpleProduct2$$"/> + </actionGroup> + <checkOption selector="{{AdminAddProductsToOptionPanel.firstCheckbox}}" stepKey="selectFirstGridRow2"/> + <click selector="{{AdminAddProductsToOptionPanel.addSelectedProducts}}" stepKey="clickAddSelectedBundleProducts"/> + <fillField selector="{{AdminProductFormBundleSection.bundleOptionXProductYQuantity('0', '0')}}" userInput="{{BundleProduct.defaultQuantity}}" stepKey="fillProductDefaultQty1"/> + <fillField selector="{{AdminProductFormBundleSection.bundleOptionXProductYQuantity('0', '1')}}" userInput="{{BundleProduct.defaultQuantity}}" stepKey="fillProductDefaultQty2"/> + <actionGroup ref="AncillaryPrepBundleProductActionGroup" stepKey="createBundledProductForTwoSimpleProducts"/> + + <!--Save the product--> + <click selector="{{AdminProductFormActionSection.saveButton}}" stepKey="clickSaveButton"/> + <seeElement selector="{{AdminCategoryMessagesSection.SuccessMessage}}" stepKey="messageYouSavedTheProductIsShown"/> + + <magentoCron stepKey="runCronReindex" groups="index"/> + + <!--Go to category page--> + <actionGroup ref="StorefrontOpenHomePageActionGroup" stepKey="goToHomePage"/> + <click selector="{{StorefrontHeaderSection.NavigationCategoryByName(SimpleSubCategory.name)}}" stepKey="cartClickCategory"/> + + <!--Check in grid view--> + <seeInTitle userInput="{{SimpleSubCategory.name}}" stepKey="assertCategoryNameInTitle"/> + <see userInput="{{SimpleSubCategory.name}}" selector="{{StorefrontCategoryMainSection.CategoryTitle}}" stepKey="assertCategoryName"/> + <see userInput="1" selector="{{StorefrontCategoryMainSection.productCount}} span" stepKey="assertProductCount"/> + <seeElement selector="{{StorefrontCategoryProductSection.listedProduct('1')}}" stepKey="assertBundleProductPresence"/> + <see userInput="{{BundleProduct.name}}" selector="{{StorefrontCategoryProductSection.ProductTitleByNumber('1')}}" stepKey="checkTitle"/> + <see userInput="$1,230.00" selector="{{StorefrontCategoryProductSection.ProductPriceByNumber('1')}}" stepKey="checkPrice"/> + + <!--Check in list view--> + <click selector="{{StorefrontCategoryProductSection.categoryListView}}" stepKey="switchToListView"/> + <seeInTitle userInput="{{SimpleSubCategory.name}}" stepKey="assertCategoryNameInTitleAgain"/> + <see userInput="{{SimpleSubCategory.name}}" selector="{{StorefrontCategoryMainSection.CategoryTitle}}" stepKey="assertCategoryNamAgain"/> + <see userInput="1" selector="{{StorefrontCategoryMainSection.productCount}} span" stepKey="assertProductCountAgain"/> + <seeElement selector="{{StorefrontCategoryProductSection.listedProduct('1')}}" stepKey="assertBundleProductPresenceAgain"/> + <see userInput="{{BundleProduct.name}}" selector="{{StorefrontCategoryProductSection.ProductTitleByNumber('1')}}" stepKey="checkTitleAgain"/> + <see userInput="$1,230.00" selector="{{StorefrontCategoryProductSection.ProductPriceByNumber('1')}}" stepKey="checkPriceAgain"/> + </test> +</tests> diff --git a/app/code/Magento/Bundle/Test/Mftf/Test/StorefrontCheckBundleProductOptionTierPrices.xml b/app/code/Magento/Bundle/Test/Mftf/Test/StorefrontCheckBundleProductOptionTierPrices.xml deleted file mode 100644 index 0487668c10094..0000000000000 --- a/app/code/Magento/Bundle/Test/Mftf/Test/StorefrontCheckBundleProductOptionTierPrices.xml +++ /dev/null @@ -1,106 +0,0 @@ -<?xml version="1.0" encoding="UTF-8"?> -<!-- - /** - * Copyright © Magento, Inc. All rights reserved. - * See COPYING.txt for license details. - */ ---> - -<tests xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" - xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/testSchema.xsd"> - <test name="StorefrontCheckBundleProductOptionTierPrices"> - <annotations> - <features value="Bundle"/> - <stories value="View bundle products"/> - <title value="Check tier prices for bundle options"/> - <description value="Check tier prices for bundle options"/> - <testCaseId value="MAGETWO-98968"/> - <useCaseId value="MAGETWO-98603"/> - <severity value="AVERAGE"/> - <group value="catalog"/> - <group value="bundle"/> - </annotations> - <before> - <!-- Create Dynamic Bundle product --> - <actionGroup ref="AdminCreateApiDynamicBundleProductAllOptionTypesActionGroup" stepKey="createBundleProduct"/> - - <!-- Add tier prices to simple products --> - <!-- Simple product 1 --> - <actionGroup ref="LoginAsAdmin" stepKey="loginAsAdmin"/> - <amOnPage url="{{AdminProductEditPage.url($$simpleProduct1CreateBundleProduct.id$$)}}" stepKey="openAdminEditPageProduct1"/> - <actionGroup ref="ProductSetAdvancedPricingActionGroup" stepKey="addTierPriceProduct1"> - <argument name="group" value="ALL GROUPS"/> - <argument name="quantity" value="5"/> - <argument name="price" value="Discount"/> - <argument name="amount" value="50"/> - </actionGroup> - <!-- Simple product 2 --> - <amOnPage url="{{AdminProductEditPage.url($$simpleProduct2CreateBundleProduct.id$$)}}" stepKey="openAdminEditPageProduct2"/> - <actionGroup ref="ProductSetAdvancedPricingActionGroup" stepKey="addTierPriceProduct2"> - <argument name="group" value="ALL GROUPS"/> - <argument name="quantity" value="7"/> - <argument name="price" value="Discount"/> - <argument name="amount" value="25"/> - </actionGroup> - <actionGroup ref="logout" stepKey="logoutAsAdmin"/> - - <!-- Run reindex --> - <magentoCLI command="indexer:reindex" stepKey="reindex"/> - </before> - <after> - <deleteData createDataKey="createBundleProductCreateBundleProduct" stepKey="deleteDynamicBundleProduct"/> - <deleteData createDataKey="simpleProduct1CreateBundleProduct" stepKey="deleteSimpleProduct1"/> - <deleteData createDataKey="simpleProduct2CreateBundleProduct" stepKey="deleteSimpleProduct2"/> - </after> - - <!-- Go to storefront product page --> - <amOnPage url="{{StorefrontProductPage.url($$createBundleProductCreateBundleProduct.custom_attributes[url_key]$$)}}" stepKey="navigateToBundleProductPage"/> - <click selector="{{StorefrontBundledSection.addToCart}}" stepKey="clickCustomize"/> - - <!--"Drop-down" type option--> - <!-- Check Tier Prices for product 1 --> - <selectOption selector="{{StorefrontBundledSection.dropDownOptionOneProducts('Drop-down Option')}}" userInput="$$simpleProduct1CreateBundleProduct.sku$$ +$$$simpleProduct1CreateBundleProduct.price$$.00" stepKey="selectDropDownOptionProduct1"/> - <seeOptionIsSelected selector="{{StorefrontBundledSection.dropDownOptionOneProducts('Drop-down Option')}}" userInput="$$simpleProduct1CreateBundleProduct.sku$$ +$$$simpleProduct1CreateBundleProduct.price$$.00" stepKey="checkDropDownOptionProduct1"/> - <grabTextFrom selector="{{StorefrontBundledSection.dropDownOptionTierPrices('Drop-down Option')}}" stepKey="DropDownTierPriceTextProduct1"/> - <assertContains stepKey="assertDropDownTierPriceTextProduct1"> - <expectedResult type="string">Buy 5 for $5.00 each and save 50%</expectedResult> - <actualResult type="variable">DropDownTierPriceTextProduct1</actualResult> - </assertContains> - <!-- Check Tier Prices for product 2 --> - <selectOption selector="{{StorefrontBundledSection.dropDownOptionOneProducts('Drop-down Option')}}" userInput="$$simpleProduct2CreateBundleProduct.sku$$ +$$$simpleProduct2CreateBundleProduct.price$$.00" stepKey="selectDropDownOptionProduct2"/> - <seeOptionIsSelected selector="{{StorefrontBundledSection.dropDownOptionOneProducts('Drop-down Option')}}" userInput="$$simpleProduct2CreateBundleProduct.sku$$ +$$$simpleProduct2CreateBundleProduct.price$$.00" stepKey="checkDropDownOptionProduct2"/> - <grabTextFrom selector="{{StorefrontBundledSection.dropDownOptionTierPrices('Drop-down Option')}}" stepKey="dropDownTierPriceTextProduct2"/> - <assertContains stepKey="assertDropDownTierPriceTextProduct2"> - <expectedResult type="string">Buy 7 for $15.00 each and save 25%</expectedResult> - <actualResult type="variable">dropDownTierPriceTextProduct2</actualResult> - </assertContains> - - <!--"Radio Buttons" type option--> - <!-- Check Tier Prices for product 1 --> - <grabTextFrom selector="{{StorefrontBundledSection.radioButtonOptionLabel('Radio Buttons Option', '$$simpleProduct1CreateBundleProduct.sku$$')}}" stepKey="radioButtonsOptionTierPriceTextProduct1"/> - <assertContains stepKey="assertRadioButtonsOptionTierPriceTextProduct1"> - <expectedResult type="string">Buy 5 for $5.00 each and save 50%</expectedResult> - <actualResult type="variable">radioButtonsOptionTierPriceTextProduct1</actualResult> - </assertContains> - <!-- Check Tier Prices for product 2 --> - <grabTextFrom selector="{{StorefrontBundledSection.radioButtonOptionLabel('Radio Buttons Option', '$$simpleProduct2CreateBundleProduct.sku$$')}}" stepKey="radioButtonsOptionTierPriceTextProduct2"/> - <assertContains stepKey="assertRadioButtonsOptionTierPriceTextProduct2"> - <expectedResult type="string">Buy 7 for $15.00 each and save 25%</expectedResult> - <actualResult type="variable">radioButtonsOptionTierPriceTextProduct2</actualResult> - </assertContains> - - <!--"Checkbox" type option--> - <!-- Check Tier Prices for product 1 --> - <grabTextFrom selector="{{StorefrontBundledSection.checkboxOptionLabel('Checkbox Option', '$$simpleProduct1CreateBundleProduct.sku$$')}}" stepKey="checkBoxOptionTierPriceTextProduct1"/> - <assertContains stepKey="assertCheckBoxOptionTierPriceTextProduct1"> - <expectedResult type="string">Buy 5 for $5.00 each and save 50%</expectedResult> - <actualResult type="variable">checkBoxOptionTierPriceTextProduct1</actualResult> - </assertContains> - <!-- Check Tier Prices for product 2 --> - <grabTextFrom selector="{{StorefrontBundledSection.checkboxOptionLabel('Checkbox Option', '$$simpleProduct2CreateBundleProduct.sku$$')}}" stepKey="checkBoxOptionTierPriceTextProduct2"/> - <assertContains stepKey="assertCheckBoxOptionTierPriceTextProduct2"> - <expectedResult type="string">Buy 7 for $15.00 each and save 25%</expectedResult> - <actualResult type="variable">checkBoxOptionTierPriceTextProduct2</actualResult> - </assertContains> - </test> -</tests> diff --git a/app/code/Magento/Bundle/Test/Mftf/Test/StorefrontCheckBundleProductOptionTierPricesTest.xml b/app/code/Magento/Bundle/Test/Mftf/Test/StorefrontCheckBundleProductOptionTierPricesTest.xml new file mode 100644 index 0000000000000..753a36dc1e613 --- /dev/null +++ b/app/code/Magento/Bundle/Test/Mftf/Test/StorefrontCheckBundleProductOptionTierPricesTest.xml @@ -0,0 +1,109 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + /** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +--> + +<tests xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/testSchema.xsd"> + <test name="StorefrontCheckBundleProductOptionTierPricesTest"> + <annotations> + <features value="Bundle"/> + <stories value="View bundle products"/> + <title value="Check tier prices for bundle options"/> + <description value="Check tier prices for bundle options"/> + <testCaseId value="MAGETWO-98968"/> + <useCaseId value="MAGETWO-98603"/> + <severity value="AVERAGE"/> + <group value="catalog"/> + <group value="bundle"/> + </annotations> + <before> + <!-- Create Dynamic Bundle product --> + <actionGroup ref="AdminCreateApiDynamicBundleProductAllOptionTypesActionGroup" stepKey="createBundleProduct"/> + + <!-- Add tier prices to simple products --> + <!-- Simple product 1 --> + <actionGroup ref="AdminLoginActionGroup" stepKey="loginAsAdmin"/> + <actionGroup ref="AdminProductPageOpenByIdActionGroup" stepKey="openAdminEditPageProduct1"> + <argument name="productId" value="$$simpleProduct1CreateBundleProduct.id$$"/> + </actionGroup> + <actionGroup ref="ProductSetAdvancedPricingActionGroup" stepKey="addTierPriceProduct1"> + <argument name="group" value="ALL GROUPS"/> + <argument name="quantity" value="5"/> + <argument name="price" value="Discount"/> + <argument name="amount" value="50"/> + </actionGroup> + <!-- Simple product 2 --> + <actionGroup ref="AdminProductPageOpenByIdActionGroup" stepKey="openAdminEditPageProduct2"> + <argument name="productId" value="$$simpleProduct2CreateBundleProduct.id$$"/> + </actionGroup> + <actionGroup ref="ProductSetAdvancedPricingActionGroup" stepKey="addTierPriceProduct2"> + <argument name="group" value="ALL GROUPS"/> + <argument name="quantity" value="7"/> + <argument name="price" value="Discount"/> + <argument name="amount" value="25"/> + </actionGroup> + <actionGroup ref="AdminLogoutActionGroup" stepKey="logoutAsAdmin"/> + + <magentoCLI stepKey="runCronIndex" command="cron:run --group=index"/> + </before> + <after> + <deleteData createDataKey="createBundleProductCreateBundleProduct" stepKey="deleteDynamicBundleProduct"/> + <deleteData createDataKey="simpleProduct1CreateBundleProduct" stepKey="deleteSimpleProduct1"/> + <deleteData createDataKey="simpleProduct2CreateBundleProduct" stepKey="deleteSimpleProduct2"/> + </after> + + <!-- Go to storefront product page --> + <amOnPage url="{{StorefrontProductPage.url($$createBundleProductCreateBundleProduct.custom_attributes[url_key]$$)}}" stepKey="navigateToBundleProductPage"/> + <click selector="{{StorefrontBundledSection.addToCart}}" stepKey="clickCustomize"/> + + <!--"Drop-down" type option--> + <!-- Check Tier Prices for product 1 --> + <selectOption selector="{{StorefrontBundledSection.dropDownOptionOneProducts('Drop-down Option')}}" userInput="$$simpleProduct1CreateBundleProduct.sku$$ +$$$simpleProduct1CreateBundleProduct.price$$.00" stepKey="selectDropDownOptionProduct1"/> + <seeOptionIsSelected selector="{{StorefrontBundledSection.dropDownOptionOneProducts('Drop-down Option')}}" userInput="$$simpleProduct1CreateBundleProduct.sku$$ +$$$simpleProduct1CreateBundleProduct.price$$.00" stepKey="checkDropDownOptionProduct1"/> + <grabTextFrom selector="{{StorefrontBundledSection.dropDownOptionTierPrices('Drop-down Option')}}" stepKey="DropDownTierPriceTextProduct1"/> + <assertContains stepKey="assertDropDownTierPriceTextProduct1"> + <expectedResult type="string">Buy 5 for $5.00 each and save 50%</expectedResult> + <actualResult type="variable">DropDownTierPriceTextProduct1</actualResult> + </assertContains> + <!-- Check Tier Prices for product 2 --> + <selectOption selector="{{StorefrontBundledSection.dropDownOptionOneProducts('Drop-down Option')}}" userInput="$$simpleProduct2CreateBundleProduct.sku$$ +$$$simpleProduct2CreateBundleProduct.price$$.00" stepKey="selectDropDownOptionProduct2"/> + <seeOptionIsSelected selector="{{StorefrontBundledSection.dropDownOptionOneProducts('Drop-down Option')}}" userInput="$$simpleProduct2CreateBundleProduct.sku$$ +$$$simpleProduct2CreateBundleProduct.price$$.00" stepKey="checkDropDownOptionProduct2"/> + <grabTextFrom selector="{{StorefrontBundledSection.dropDownOptionTierPrices('Drop-down Option')}}" stepKey="dropDownTierPriceTextProduct2"/> + <assertContains stepKey="assertDropDownTierPriceTextProduct2"> + <expectedResult type="string">Buy 7 for $15.00 each and save 25%</expectedResult> + <actualResult type="variable">dropDownTierPriceTextProduct2</actualResult> + </assertContains> + + <!--"Radio Buttons" type option--> + <!-- Check Tier Prices for product 1 --> + <grabTextFrom selector="{{StorefrontBundledSection.radioButtonOptionLabel('Radio Buttons Option', '$$simpleProduct1CreateBundleProduct.sku$$')}}" stepKey="radioButtonsOptionTierPriceTextProduct1"/> + <assertContains stepKey="assertRadioButtonsOptionTierPriceTextProduct1"> + <expectedResult type="string">Buy 5 for $5.00 each and save 50%</expectedResult> + <actualResult type="variable">radioButtonsOptionTierPriceTextProduct1</actualResult> + </assertContains> + <!-- Check Tier Prices for product 2 --> + <grabTextFrom selector="{{StorefrontBundledSection.radioButtonOptionLabel('Radio Buttons Option', '$$simpleProduct2CreateBundleProduct.sku$$')}}" stepKey="radioButtonsOptionTierPriceTextProduct2"/> + <assertContains stepKey="assertRadioButtonsOptionTierPriceTextProduct2"> + <expectedResult type="string">Buy 7 for $15.00 each and save 25%</expectedResult> + <actualResult type="variable">radioButtonsOptionTierPriceTextProduct2</actualResult> + </assertContains> + + <!--"Checkbox" type option--> + <!-- Check Tier Prices for product 1 --> + <grabTextFrom selector="{{StorefrontBundledSection.checkboxOptionLabel('Checkbox Option', '$$simpleProduct1CreateBundleProduct.sku$$')}}" stepKey="checkBoxOptionTierPriceTextProduct1"/> + <assertContains stepKey="assertCheckBoxOptionTierPriceTextProduct1"> + <expectedResult type="string">Buy 5 for $5.00 each and save 50%</expectedResult> + <actualResult type="variable">checkBoxOptionTierPriceTextProduct1</actualResult> + </assertContains> + <!-- Check Tier Prices for product 2 --> + <grabTextFrom selector="{{StorefrontBundledSection.checkboxOptionLabel('Checkbox Option', '$$simpleProduct2CreateBundleProduct.sku$$')}}" stepKey="checkBoxOptionTierPriceTextProduct2"/> + <assertContains stepKey="assertCheckBoxOptionTierPriceTextProduct2"> + <expectedResult type="string">Buy 7 for $15.00 each and save 25%</expectedResult> + <actualResult type="variable">checkBoxOptionTierPriceTextProduct2</actualResult> + </assertContains> + </test> +</tests> diff --git a/app/code/Magento/Bundle/Test/Mftf/Test/StorefrontCustomerSearchBundleProductsByKeywordsTest.xml b/app/code/Magento/Bundle/Test/Mftf/Test/StorefrontCustomerSearchBundleProductsByKeywordsTest.xml index ae9d01a50f46e..de6718dfd9f31 100644 --- a/app/code/Magento/Bundle/Test/Mftf/Test/StorefrontCustomerSearchBundleProductsByKeywordsTest.xml +++ b/app/code/Magento/Bundle/Test/Mftf/Test/StorefrontCustomerSearchBundleProductsByKeywordsTest.xml @@ -48,7 +48,7 @@ <deleteData createDataKey="createSimpleProductTwo" stepKey="createSimpleProductTwo"/> </after> <!-- 1. Go to storefront home page --> - <amOnPage url="{{StorefrontHomePage.url}}" stepKey="goToStorefrontPage"/> + <actionGroup ref="StorefrontOpenHomePageActionGroup" stepKey="goToHomePage"/> <!-- 2. Fill quick search bar with test values unique for dynamic bundle product and click search --> <actionGroup ref="StorefrontCheckQuickSearchStringActionGroup" stepKey="quickSearchDynamic"> <argument name="phrase" value="Dynamic"/> diff --git a/app/code/Magento/Bundle/Test/Mftf/Test/StorefrontCustomerSelectAndSetBundleOptionsTest.xml b/app/code/Magento/Bundle/Test/Mftf/Test/StorefrontCustomerSelectAndSetBundleOptionsTest.xml index dc8cb24246567..ffcf5ba35dce8 100644 --- a/app/code/Magento/Bundle/Test/Mftf/Test/StorefrontCustomerSelectAndSetBundleOptionsTest.xml +++ b/app/code/Magento/Bundle/Test/Mftf/Test/StorefrontCustomerSelectAndSetBundleOptionsTest.xml @@ -19,9 +19,11 @@ <group value="Bundle"/> </annotations> <before> - <actionGroup ref="LoginAsAdmin" stepKey="login"/> + <actionGroup ref="AdminLoginActionGroup" stepKey="login"/> <createData entity="SimpleProduct2" stepKey="simpleProduct1"/> <createData entity="SimpleProduct2" stepKey="simpleProduct2"/> + + <magentoCron stepKey="runCronIndex" groups="index"/> </before> <after> <!-- Delete the bundled product --> @@ -89,6 +91,10 @@ <!-- Save product and go to storefront --> <actionGroup ref="SaveProductFormActionGroup" stepKey="saveProduct"/> + + <wait stepKey="waitBeforeIndexerAfterBundle" time="60"/> + <magentoCLI command="cron:run --group=index" stepKey="runCronIndexerAfterBundle"/> + <amOnPage url="{{BundleProduct.sku}}.html" stepKey="goToStorefront"/> <waitForPageLoad stepKey="waitForStorefront"/> <click selector="{{StorefrontBundledSection.addToCart}}" stepKey="clickCustomize"/> diff --git a/app/code/Magento/Bundle/Test/Mftf/Test/StorefrontEditBundleProductTest.xml b/app/code/Magento/Bundle/Test/Mftf/Test/StorefrontEditBundleProductTest.xml index 0ac47a27f1f95..3082e467ec734 100644 --- a/app/code/Magento/Bundle/Test/Mftf/Test/StorefrontEditBundleProductTest.xml +++ b/app/code/Magento/Bundle/Test/Mftf/Test/StorefrontEditBundleProductTest.xml @@ -14,14 +14,15 @@ <stories value="Bundle products list on Storefront"/> <title value="Customer should be able to change chosen options for Bundle Product when clicking Edit button in Shopping Cart page"/> <description value="Customer should be able to change chosen options for Bundle Product when clicking Edit button in Shopping Cart page"/> - <severity value="MAJOR"/> + <severity value="BLOCKER"/> <testCaseId value="MC-290"/> <group value="Bundle"/> </annotations> <before> - <actionGroup ref="LoginAsAdmin" stepKey="loginAsAdmin"/> + <actionGroup ref="AdminLoginActionGroup" stepKey="loginAsAdmin"/> <createData entity="SimpleProduct2" stepKey="simpleProduct1"/> <createData entity="SimpleProduct2" stepKey="simpleProduct2"/> + <magentoCron stepKey="runCronIndex" groups="index"/> </before> <after> <amOnPage url="{{AdminLogoutPage.url}}" stepKey="amOnLogoutPage"/> @@ -65,6 +66,8 @@ <click stepKey="saveProductBundle" selector="{{AdminProductFormActionSection.saveButton}}"/> <see stepKey="assertSuccess" selector="{{AdminProductMessagesSection.successMessage}}" userInput="You saved the product."/> + <magentoCLI command="cron:run --group=index" stepKey="runCronIndexer"/> + <!-- Go to the storefront bundled product page --> <amOnPage url="/{{BundleProduct.urlKey}}.html" stepKey="visitStoreFrontBundle"/> <waitForPageLoad stepKey="waitForStorefront"/> @@ -86,7 +89,7 @@ <waitForPageLoad stepKey="waitForElementAdded2"/> <!-- Go to the shopping cart page and edit the first product --> - <amOnPage url="/checkout/cart/" stepKey="onPageShoppingCart"/> + <amOnPage url="{{CheckoutCartPage.url}}" stepKey="onPageShoppingCart"/> <waitForPageLoad stepKey="waitForCartPageLoad"/> <waitForElementVisible stepKey="waitForInfoDropdown" selector="{{CheckoutCartSummarySection.total}}"/> <waitForPageLoad stepKey="waitForCartPageLoad3"/> @@ -104,7 +107,7 @@ <waitForPageLoad stepKey="waitForElementAdded3"/> <!-- Go to the shopping cart page --> - <amOnPage url="/checkout/cart/" stepKey="onPageShoppingCart2"/> + <amOnPage url="{{CheckoutCartPage.url}}" stepKey="onPageShoppingCart2"/> <waitForPageLoad stepKey="waitForCartPageLoad2"/> <!-- Assert that the options are both there and the proce no longer matches --> @@ -113,7 +116,10 @@ <waitForElementVisible stepKey="waitForInfoDropdown2" selector="{{CheckoutCartSummarySection.total}}"/> <waitForPageLoad stepKey="waitForCartPageLoad4"/> <grabTextFrom selector="{{CheckoutCartSummarySection.total}}" stepKey="grabTotalAfter"/> - <assertNotEquals expected="{$grabTotalBefore}" expectedType="string" actual="{$grabTotalAfter}" actualType="string" stepKey="assertNotEquals"/> + <assertNotEquals stepKey="assertNotEquals"> + <actualResult type="string">{$grabTotalAfter}</actualResult> + <expectedResult type="string">{$grabTotalBefore}</expectedResult> + </assertNotEquals> <!-- Delete the bundled product --> <actionGroup stepKey="deleteBundle" ref="DeleteProductUsingProductGridActionGroup"> diff --git a/app/code/Magento/Bundle/Test/Mftf/Test/StorefrontGoToDetailsPageWhenAddingToCartTest.xml b/app/code/Magento/Bundle/Test/Mftf/Test/StorefrontGoToDetailsPageWhenAddingToCartTest.xml index 7ced26bab2c96..add819e2d3f14 100644 --- a/app/code/Magento/Bundle/Test/Mftf/Test/StorefrontGoToDetailsPageWhenAddingToCartTest.xml +++ b/app/code/Magento/Bundle/Test/Mftf/Test/StorefrontGoToDetailsPageWhenAddingToCartTest.xml @@ -8,7 +8,7 @@ <tests xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/testSchema.xsd"> - <test name="StorefrontGoToDetailsPageWhenAddingToCart"> + <test name="StorefrontGoToDetailsPageWhenAddingToCartTest"> <annotations> <features value="Bundle"/> <stories value="Bundle products list on Storefront"/> @@ -19,10 +19,11 @@ <group value="Bundle"/> </annotations> <before> - <actionGroup ref="LoginAsAdmin" stepKey="loginAsAdmin"/> + <actionGroup ref="AdminLoginActionGroup" stepKey="loginAsAdmin"/> <createData entity="SimpleProduct2" stepKey="simpleProduct1"/> <createData entity="SimpleProduct2" stepKey="simpleProduct2"/> <createData entity="_defaultCategory" stepKey="createCategory"/> + <magentoCron stepKey="runCronIndex" groups="index"/> </before> <after> <amOnPage url="{{AdminLogoutPage.url}}" stepKey="amOnLogoutPage"/> @@ -66,8 +67,7 @@ <seeElement selector="{{AdminCategoryMessagesSection.SuccessMessage}}" stepKey="messageYouSavedTheProductIsShown"/> <!--Go to category page--> - <amOnPage url="{{StorefrontHomePage.url}}" stepKey="goToHomePage"/> - <waitForPageLoad stepKey="waitForHomePageToload"/> + <actionGroup ref="StorefrontOpenHomePageActionGroup" stepKey="goToHomePage"/> <click selector="{{StorefrontHeaderSection.NavigationCategoryByName($$createCategory.name$$)}}" stepKey="cartClickCategory"/> <!--Click add to cart--> diff --git a/app/code/Magento/Bundle/Test/Mftf/Test/StorefrontSortBundleProductsByPriceTest.xml b/app/code/Magento/Bundle/Test/Mftf/Test/StorefrontSortBundleProductsByPriceTest.xml index 8bf0de7530e64..1c7cb39d7746f 100644 --- a/app/code/Magento/Bundle/Test/Mftf/Test/StorefrontSortBundleProductsByPriceTest.xml +++ b/app/code/Magento/Bundle/Test/Mftf/Test/StorefrontSortBundleProductsByPriceTest.xml @@ -109,6 +109,9 @@ <deleteData createDataKey="createFirstProductForBundle" stepKey="deleteFirstProductForBundle"/> <deleteData createDataKey="createSecondProductForBundle" stepKey="deleteSecondProductForBundle"/> <deleteData createDataKey="createThirdBundleProduct" stepKey="deleteThirdBundleProduct"/> + + <!-- Reindex invalidated indices after product attribute has been created/deleted --> + <magentoCron groups="index" stepKey="reindexInvalidatedIndices"/> </after> <!-- Open created category on Storefront --> @@ -117,8 +120,8 @@ </actionGroup> <!-- Assert first bundle products in category product grid --> - <actionGroup ref="AssertProductOnCategoryPageActionGroup" stepKey="assertFirstBundleProduct"> - <argument name="product" value="$$createFirstBundleProduct$$"/> + <actionGroup ref="AssertStorefrontProductIsPresentOnCategoryPageActionGroup" stepKey="assertFirstBundleProduct"> + <argument name="productName" value="$$createFirstBundleProduct.name$$"/> </actionGroup> <actionGroup ref="AssertStorefrontElementVisibleActionGroup" stepKey="seePriceRangeFromForFirstBundleProduct"> <argument name="selector" value="{{StorefrontCategoryProductSection.priceFromByProductId($$createFirstBundleProduct.id$$)}}"/> @@ -130,8 +133,8 @@ </actionGroup> <!-- Assert second bundle products in category product grid --> - <actionGroup ref="AssertProductOnCategoryPageActionGroup" stepKey="assertSecondBundleProduct"> - <argument name="product" value="$$createSecondBundleProduct$$"/> + <actionGroup ref="AssertStorefrontProductIsPresentOnCategoryPageActionGroup" stepKey="assertSecondBundleProduct"> + <argument name="productName" value="$$createSecondBundleProduct.name$$"/> </actionGroup> <actionGroup ref="AssertStorefrontElementVisibleActionGroup" stepKey="seePriceRangeFromForSecondBundleProduct"> <argument name="selector" value="{{StorefrontCategoryProductSection.priceFromByProductId($$createSecondBundleProduct.id$$)}}"/> @@ -143,8 +146,8 @@ </actionGroup> <!-- Assert third bundle products in category product grid --> - <actionGroup ref="AssertProductOnCategoryPageActionGroup" stepKey="assertThirdBundleProduct"> - <argument name="product" value="$$createThirdBundleProduct$$"/> + <actionGroup ref="AssertStorefrontProductIsPresentOnCategoryPageActionGroup" stepKey="assertThirdBundleProduct"> + <argument name="productName" value="$$createThirdBundleProduct.name$$"/> </actionGroup> <actionGroup ref="AssertStorefrontElementVisibleActionGroup" stepKey="seePriceRangeFromForThirdBundleProduct"> <argument name="selector" value="{{StorefrontCategoryProductSection.priceFromByProductId($$createThirdBundleProduct.id$$)}}"/> diff --git a/app/code/Magento/Bundle/Test/Mftf/Test/StorefrontSpecialPriceBundleProductInCartTest.xml b/app/code/Magento/Bundle/Test/Mftf/Test/StorefrontSpecialPriceBundleProductInCartTest.xml index 44ac68a2759f3..32662321a611e 100644 --- a/app/code/Magento/Bundle/Test/Mftf/Test/StorefrontSpecialPriceBundleProductInCartTest.xml +++ b/app/code/Magento/Bundle/Test/Mftf/Test/StorefrontSpecialPriceBundleProductInCartTest.xml @@ -13,7 +13,7 @@ <stories value="Add bundle product to cart on storefront"/> <title value="Customer should not be able to add a Bundle Product to the cart when added a special price for associated products"/> <description value="Customer should not be able to add a Bundle Product to the cart when added a special price for associated products"/> - <severity value="MAJOR"/> + <severity value="BLOCKER"/> <testCaseId value="MC-19134"/> <useCaseId value="MC-18963"/> <group value="bundle"/> diff --git a/app/code/Magento/Bundle/Test/Mftf/Test/StorefrontVerifyDynamicBundleProductPricesForCombinationOfOptionsTest.xml b/app/code/Magento/Bundle/Test/Mftf/Test/StorefrontVerifyDynamicBundleProductPricesForCombinationOfOptionsTest.xml index c78796d2fd8b4..ac2ab4806fd44 100644 --- a/app/code/Magento/Bundle/Test/Mftf/Test/StorefrontVerifyDynamicBundleProductPricesForCombinationOfOptionsTest.xml +++ b/app/code/Magento/Bundle/Test/Mftf/Test/StorefrontVerifyDynamicBundleProductPricesForCombinationOfOptionsTest.xml @@ -39,8 +39,10 @@ </createData> <!--Add special price to simple product--> - <actionGroup ref="LoginAsAdmin" stepKey="loginAsAdmin"/> - <amOnPage url="{{AdminProductEditPage.url($$simpleProduct5.id$$)}}" stepKey="openAdminEditPage"/> + <actionGroup ref="AdminLoginActionGroup" stepKey="loginAsAdmin"/> + <actionGroup ref="AdminProductPageOpenByIdActionGroup" stepKey="openAdminEditPage"> + <argument name="productId" value="$$simpleProduct5.id$$"/> + </actionGroup> <actionGroup ref="AddSpecialPriceToProductActionGroup" stepKey="addSpecialPrice"/> <actionGroup ref="SaveProductFormActionGroup" stepKey="saveProductForm"/> @@ -192,7 +194,7 @@ <waitForPageLoad stepKey="waitForTaxSaved"/> <see userInput="You saved the configuration." selector="{{AdminCategoryMessagesSection.SuccessMessage}}" stepKey="seeSuccess"/> - <actionGroup ref="logout" stepKey="logout"/> + <actionGroup ref="AdminLogoutActionGroup" stepKey="logout"/> <deleteData createDataKey="createSubCategory" stepKey="deleteSubCategory1"/> <deleteData createDataKey="simpleProduct1" stepKey="deleteSimpleProduct1"/> diff --git a/app/code/Magento/Bundle/Test/Unit/Model/LinkManagementTest.php b/app/code/Magento/Bundle/Test/Unit/Model/LinkManagementTest.php index ccc8c52d5022f..b31b82a6a72bf 100644 --- a/app/code/Magento/Bundle/Test/Unit/Model/LinkManagementTest.php +++ b/app/code/Magento/Bundle/Test/Unit/Model/LinkManagementTest.php @@ -7,174 +7,198 @@ namespace Magento\Bundle\Test\Unit\Model; +use Exception; +use Magento\Bundle\Api\Data\LinkInterface; +use Magento\Bundle\Api\Data\LinkInterfaceFactory; use Magento\Bundle\Model\LinkManagement; +use Magento\Bundle\Model\Option; +use Magento\Bundle\Model\ResourceModel\Bundle; +use Magento\Bundle\Model\ResourceModel\BundleFactory; +use Magento\Bundle\Model\ResourceModel\Option\Collection as OptionCollection; +use Magento\Bundle\Model\ResourceModel\Option\CollectionFactory as OptionCollectionFactory; +use Magento\Bundle\Model\ResourceModel\Selection\Collection as SelectionCollection; +use Magento\Bundle\Model\Selection; +use Magento\Bundle\Model\SelectionFactory; +use Magento\Catalog\Api\Data\ProductInterface; +use Magento\Catalog\Model\Product; +use Magento\Catalog\Model\Product\Type; +use Magento\Catalog\Model\ProductRepository; +use Magento\Framework\Api\DataObjectHelper; +use Magento\Framework\EntityManager\EntityMetadata; +use Magento\Framework\EntityManager\MetadataPool; use Magento\Framework\TestFramework\Unit\Helper\ObjectManager; +use Magento\Store\Model\Store; +use Magento\Store\Model\StoreManagerInterface; +use PHPUnit\Framework\MockObject\MockObject; +use PHPUnit\Framework\TestCase; /** - * Class LinkManagementTest + * Test class for \Magento\Bundle\Model\LinkManagement * * @SuppressWarnings(PHPMD.TooManyFields) * @SuppressWarnings(PHPMD.CouplingBetweenObjects) */ -class LinkManagementTest extends \PHPUnit\Framework\TestCase +class LinkManagementTest extends TestCase { /** - * @var \Magento\Bundle\Model\LinkManagement + * @var LinkManagement */ - protected $model; + private $model; /** - * @var \Magento\Catalog\Model\ProductRepository|\PHPUnit_Framework_MockObject_MockObject + * @var ProductRepository|MockObject */ - protected $productRepository; + private $productRepository; /** - * @var \Magento\Catalog\Model\Product|\PHPUnit_Framework_MockObject_MockObject + * @var Product|MockObject */ - protected $product; + private $product; /** - * @var \PHPUnit_Framework_MockObject_MockObject + * @var LinkInterfaceFactory|MockObject */ - protected $linkFactory; + private $linkFactory; /** - * @var \Magento\Catalog\Model\Product\Type\Interceptor|\PHPUnit_Framework_MockObject_MockObject + * @var Type|MockObject */ - protected $productType; + private $productType; /** - * @var \Magento\Bundle\Model\ResourceModel\Option\Collection|\PHPUnit_Framework_MockObject_MockObject + * @var OptionCollection|MockObject */ - protected $optionCollection; + private $optionCollection; /** - * @var \Magento\Bundle\Model\ResourceModel\Selection\Collection|\PHPUnit_Framework_MockObject_MockObject + * @var SelectionCollection|MockObject */ - protected $selectionCollection; + private $selectionCollection; /** - * @var \Magento\Bundle\Model\Option|\PHPUnit_Framework_MockObject_MockObject + * @var Option|MockObject */ - protected $option; + private $option; /** - * @var \PHPUnit_Framework_MockObject_MockObject | \Magento\Bundle\Model\SelectionFactory + * @var SelectionFactory|MockObject */ - protected $bundleSelectionMock; + private $bundleSelectionMock; /** - * @var \PHPUnit_Framework_MockObject_MockObject | \Magento\Bundle\Model\ResourceModel\BundleFactory + * @var BundleFactory|MockObject */ - protected $bundleFactoryMock; + private $bundleFactoryMock; /** - * @var \PHPUnit_Framework_MockObject_MockObject | \Magento\Bundle\Model\ResourceModel\Option\CollectionFactory + * @var OptionCollectionFactory|MockObject */ - protected $optionCollectionFactoryMock; + private $optionCollectionFactoryMock; /** - * @var \PHPUnit_Framework_MockObject_MockObject | \Magento\Store\Model\StoreManagerInterface + * @var StoreManagerInterface|MockObject */ - protected $storeManagerMock; + private $storeManagerMock; /** - * @var \PHPUnit_Framework_MockObject_MockObject + * @var LinkInterface|MockObject */ - protected $link; + private $link; /** - * @var int + * @var MockObject */ - protected $storeId = 2; + private $dataObjectHelperMock; /** - * @var array + * @var MetadataPool|MockObject */ - protected $optionIds = [1, 2, 3]; + private $metadataPoolMock; /** - * @var \PHPUnit_Framework_MockObject_MockObject + * @var EntityMetadata|MockObject */ - protected $dataObjectHelperMock; + private $metadataMock; /** - * @var \Magento\Framework\EntityManager\MetadataPool|\PHPUnit_Framework_MockObject_MockObject + * @var int */ - protected $metadataPoolMock; + private $storeId = 2; /** - * @var \Magento\Framework\EntityManager\EntityMetadata|\PHPUnit_Framework_MockObject_MockObject + * @var array */ - protected $metadataMock; + private $optionIds = [1, 2, 3]; /** * @var string */ - protected $linkField = 'product_id'; + private $linkField = 'product_id'; + /** + * @inheritDoc + */ protected function setUp() { $helper = new ObjectManager($this); - $this->productRepository = $this->getMockBuilder(\Magento\Catalog\Model\ProductRepository::class) + $this->productRepository = $this->getMockBuilder(ProductRepository::class) ->setMethods(['get']) ->disableOriginalConstructor() ->getMock(); - $this->productType = $this->getMockBuilder(\Magento\Bundle\Model\Product\Type\Interceptor::class) + $this->productType = $this->getMockBuilder(\Magento\Bundle\Model\Product\Type::class) ->setMethods(['getOptionsCollection', 'setStoreFilter', 'getSelectionsCollection', 'getOptionsIds']) ->disableOriginalConstructor() ->getMock(); - $this->option = $this->getMockBuilder(\Magento\Bundle\Model\Option::class) + $this->option = $this->getMockBuilder(Option::class) ->setMethods(['getSelections', 'getOptionId', '__wakeup']) ->disableOriginalConstructor() ->getMock(); - $this->optionCollection = $this->getMockBuilder(\Magento\Bundle\Model\ResourceModel\Option\Collection::class) + $this->optionCollection = $this->getMockBuilder(OptionCollection::class) ->setMethods(['appendSelections']) ->disableOriginalConstructor() ->getMock(); $this->selectionCollection = $this->getMockBuilder( - \Magento\Bundle\Model\ResourceModel\Selection\Collection::class + SelectionCollection::class )->disableOriginalConstructor()->getMock(); - $this->product = $this->getMockBuilder(\Magento\Catalog\Model\Product::class) + $this->product = $this->getMockBuilder(Product::class) ->setMethods(['getTypeInstance', 'getStoreId', 'getTypeId', '__wakeup', 'getId', 'getData']) ->disableOriginalConstructor() ->getMock(); - $this->link = $this->getMockBuilder(\Magento\Bundle\Api\Data\LinkInterface::class) + $this->link = $this->getMockBuilder(LinkInterface::class) ->disableOriginalConstructor() ->getMock(); - $this->linkFactory = $this->getMockBuilder(\Magento\Bundle\Api\Data\LinkInterfaceFactory::class) + $this->linkFactory = $this->getMockBuilder(LinkInterfaceFactory::class) ->setMethods(['create']) ->disableOriginalConstructor() ->getMock(); $this->bundleSelectionMock = $this->createPartialMock( - \Magento\Bundle\Model\SelectionFactory::class, + SelectionFactory::class, ['create'] ); $this->bundleFactoryMock = $this->createPartialMock( - \Magento\Bundle\Model\ResourceModel\BundleFactory::class, + BundleFactory::class, ['create'] ); $this->optionCollectionFactoryMock = $this->createPartialMock( - \Magento\Bundle\Model\ResourceModel\Option\CollectionFactory::class, + OptionCollectionFactory::class, ['create'] ); - $this->storeManagerMock = $this->createMock(\Magento\Store\Model\StoreManagerInterface::class); - $this->metadataPoolMock = $this->getMockBuilder(\Magento\Framework\EntityManager\MetadataPool::class) + $this->storeManagerMock = $this->createMock(StoreManagerInterface::class); + $this->metadataPoolMock = $this->getMockBuilder(MetadataPool::class) ->disableOriginalConstructor() ->getMock(); - $this->metadataMock = $this->getMockBuilder(\Magento\Framework\EntityManager\EntityMetadata::class) + $this->metadataMock = $this->getMockBuilder(EntityMetadata::class) ->disableOriginalConstructor() ->getMock(); - $this->metadataPoolMock->expects($this->any())->method('getMetadata') - ->with(\Magento\Catalog\Api\Data\ProductInterface::class) + $this->metadataPoolMock->method('getMetadata') + ->with(ProductInterface::class) ->willReturn($this->metadataMock); - $this->dataObjectHelperMock = $this->getMockBuilder(\Magento\Framework\Api\DataObjectHelper::class) + $this->dataObjectHelperMock = $this->getMockBuilder(DataObjectHelper::class) ->disableOriginalConstructor() ->getMock(); $this->model = $helper->getObject( - LinkManagement::class, [ 'productRepository' => $this->productRepository, @@ -184,12 +208,9 @@ protected function setUp() 'optionCollection' => $this->optionCollectionFactoryMock, 'storeManager' => $this->storeManagerMock, 'dataObjectHelper' => $this->dataObjectHelperMock, + 'metadataPool' => $this->metadataPoolMock ] ); - $refClass = new \ReflectionClass(LinkManagement::class); - $refProperty = $refClass->getProperty('metadataPool'); - $refProperty->setAccessible(true); - $refProperty->setValue($this->model, $this->metadataPoolMock); } public function testGetChildren() @@ -198,31 +219,45 @@ public function testGetChildren() $this->getOptions(); - $this->productRepository->expects($this->any())->method('get')->with($this->equalTo($productSku)) - ->will($this->returnValue($this->product)); + $this->productRepository->method('get') + ->with($this->equalTo($productSku)) + ->willReturn($this->product); - $this->product->expects($this->once())->method('getTypeId')->will($this->returnValue('bundle')); + $this->product->expects($this->once()) + ->method('getTypeId') + ->willReturn('bundle'); - $this->productType->expects($this->once())->method('setStoreFilter')->with( - $this->equalTo($this->storeId), - $this->product - ); - $this->productType->expects($this->once())->method('getSelectionsCollection') - ->with($this->equalTo($this->optionIds), $this->equalTo($this->product)) - ->will($this->returnValue($this->selectionCollection)); - $this->productType->expects($this->once())->method('getOptionsIds')->with($this->equalTo($this->product)) - ->will($this->returnValue($this->optionIds)); + $this->productType->expects($this->once()) + ->method('setStoreFilter') + ->with( + $this->equalTo($this->storeId), + $this->product + ); + $this->productType->expects($this->once()) + ->method('getSelectionsCollection') + ->with( + $this->equalTo($this->optionIds), + $this->equalTo($this->product) + ) + ->willReturn($this->selectionCollection); + $this->productType->expects($this->once()) + ->method('getOptionsIds') + ->with($this->equalTo($this->product)) + ->willReturn($this->optionIds); - $this->optionCollection->expects($this->once())->method('appendSelections') + $this->optionCollection->expects($this->once()) + ->method('appendSelections') ->with($this->equalTo($this->selectionCollection)) - ->will($this->returnValue([$this->option])); + ->willReturn([$this->option]); - $this->option->expects($this->any())->method('getSelections')->willReturn([$this->product]); - $this->product->expects($this->any())->method('getData')->willReturn([]); + $this->option->method('getSelections') + ->willReturn([$this->product]); + $this->product->method('getData') + ->willReturn([]); $this->dataObjectHelperMock->expects($this->once()) ->method('populateWithArray') - ->with($this->link, $this->anything(), \Magento\Bundle\Api\Data\LinkInterface::class) + ->with($this->link, $this->anything(), LinkInterface::class) ->willReturnSelf(); $this->link->expects($this->once())->method('setIsDefault')->willReturnSelf(); $this->link->expects($this->once())->method('setQty')->willReturnSelf(); @@ -241,27 +276,42 @@ public function testGetChildrenWithOptionId() $this->getOptions(); - $this->productRepository->expects($this->any())->method('get')->with($this->equalTo($productSku)) - ->will($this->returnValue($this->product)); + $this->productRepository->method('get') + ->with($this->equalTo($productSku)) + ->willReturn($this->product); - $this->product->expects($this->once())->method('getTypeId')->will($this->returnValue('bundle')); + $this->product->expects($this->once()) + ->method('getTypeId') + ->willReturn('bundle'); - $this->productType->expects($this->once())->method('setStoreFilter')->with( - $this->equalTo($this->storeId), - $this->product - ); - $this->productType->expects($this->once())->method('getSelectionsCollection') - ->with($this->equalTo($this->optionIds), $this->equalTo($this->product)) - ->will($this->returnValue($this->selectionCollection)); - $this->productType->expects($this->once())->method('getOptionsIds')->with($this->equalTo($this->product)) - ->will($this->returnValue($this->optionIds)); + $this->productType->expects($this->once()) + ->method('setStoreFilter') + ->with( + $this->equalTo($this->storeId), + $this->product + ); + $this->productType->expects($this->once()) + ->method('getSelectionsCollection') + ->with( + $this->equalTo($this->optionIds), + $this->equalTo($this->product) + ) + ->willReturn($this->selectionCollection); + $this->productType->expects($this->once()) + ->method('getOptionsIds') + ->with($this->equalTo($this->product)) + ->willReturn($this->optionIds); - $this->optionCollection->expects($this->once())->method('appendSelections') + $this->optionCollection->expects($this->once()) + ->method('appendSelections') ->with($this->equalTo($this->selectionCollection)) - ->will($this->returnValue([$this->option])); + ->willReturn([$this->option]); - $this->option->expects($this->any())->method('getOptionId')->will($this->returnValue(10)); - $this->option->expects($this->once())->method('getSelections')->willReturn([1, 2]); + $this->option->method('getOptionId') + ->willReturn(10); + $this->option->expects($this->once()) + ->method('getSelections') + ->willReturn([1, 2]); $this->dataObjectHelperMock->expects($this->never())->method('populateWithArray'); @@ -275,10 +325,14 @@ public function testGetChildrenException() { $productSku = 'productSku'; - $this->productRepository->expects($this->once())->method('get')->with($this->equalTo($productSku)) - ->will($this->returnValue($this->product)); + $this->productRepository->expects($this->once()) + ->method('get') + ->with($this->equalTo($productSku)) + ->willReturn($this->product); - $this->product->expects($this->once())->method('getTypeId')->will($this->returnValue('simple')); + $this->product->expects($this->once()) + ->method('getTypeId') + ->willReturn('simple'); $this->assertEquals([$this->link], $this->model->getChildren($productSku)); } @@ -288,13 +342,14 @@ public function testGetChildrenException() */ public function testAddChildToNotBundleProduct() { - $productLink = $this->createMock(\Magento\Bundle\Api\Data\LinkInterface::class); - $productLink->expects($this->any())->method('getOptionId')->will($this->returnValue(1)); + $productLink = $this->createMock(LinkInterface::class); + $productLink->method('getOptionId') + ->willReturn(1); - $productMock = $this->createMock(\Magento\Catalog\Model\Product::class); - $productMock->expects($this->once())->method('getTypeId')->will($this->returnValue( - \Magento\Catalog\Model\Product\Type::TYPE_SIMPLE - )); + $productMock = $this->createMock(Product::class); + $productMock->expects($this->once()) + ->method('getTypeId') + ->willReturn(Type::TYPE_SIMPLE); $this->model->addChild($productMock, 1, $productLink); } @@ -303,37 +358,35 @@ public function testAddChildToNotBundleProduct() */ public function testAddChildNonExistingOption() { - $productLink = $this->createMock(\Magento\Bundle\Api\Data\LinkInterface::class); - $productLink->expects($this->any())->method('getOptionId')->will($this->returnValue(1)); + $productLink = $this->createMock(LinkInterface::class); + $productLink->method('getOptionId')->willReturn(1); - $productMock = $this->createMock(\Magento\Catalog\Model\Product::class); - $productMock->expects($this->once())->method('getTypeId')->will($this->returnValue( - \Magento\Catalog\Model\Product\Type::TYPE_BUNDLE - )); + $productMock = $this->createMock(Product::class); + $productMock->expects($this->once())->method('getTypeId')->willReturn(Type::TYPE_BUNDLE); - $store = $this->createMock(\Magento\Store\Model\Store::class); - $this->storeManagerMock->expects($this->any())->method('getStore')->will($this->returnValue($store)); - $store->expects($this->any())->method('getId')->will($this->returnValue(0)); + $store = $this->createMock(Store::class); + $this->storeManagerMock->method('getStore')->willReturn($store); + $store->method('getId')->willReturn(0); - $emptyOption = $this->getMockBuilder(\Magento\Bundle\Model\Option::class)->disableOriginalConstructor() + $emptyOption = $this->getMockBuilder(Option::class)->disableOriginalConstructor() ->setMethods(['getId', '__wakeup']) ->getMock(); $emptyOption->expects($this->once()) ->method('getId') - ->will($this->returnValue(null)); + ->willReturn(null); - $optionsCollectionMock = $this->createMock(\Magento\Bundle\Model\ResourceModel\Option\Collection::class); + $optionsCollectionMock = $this->createMock(OptionCollection::class); $optionsCollectionMock->expects($this->once()) ->method('setIdFilter') ->with($this->equalTo(1)) - ->will($this->returnSelf()); + ->willReturnSelf(); $optionsCollectionMock->expects($this->once()) ->method('getFirstItem') - ->will($this->returnValue($emptyOption)); + ->willReturn($emptyOption); - $this->optionCollectionFactoryMock->expects($this->any())->method('create')->will( - $this->returnValue($optionsCollectionMock) - ); + $this->optionCollectionFactoryMock + ->method('create') + ->willReturn($optionsCollectionMock); $this->model->addChild($productMock, 1, $productLink); } @@ -343,53 +396,55 @@ public function testAddChildNonExistingOption() */ public function testAddChildLinkedProductIsComposite() { - $productLink = $this->createMock(\Magento\Bundle\Api\Data\LinkInterface::class); - $productLink->expects($this->any())->method('getSku')->will($this->returnValue('linked_product_sku')); - $productLink->expects($this->any())->method('getOptionId')->will($this->returnValue(1)); + $productLink = $this->createMock(LinkInterface::class); + $productLink->method('getSku')->willReturn('linked_product_sku'); + $productLink->method('getOptionId')->willReturn(1); $this->metadataMock->expects($this->once())->method('getLinkField')->willReturn($this->linkField); - $productMock = $this->createMock(\Magento\Catalog\Model\Product::class); - $productMock->expects($this->once())->method('getTypeId')->will($this->returnValue( - \Magento\Catalog\Model\Product\Type::TYPE_BUNDLE - )); - $productMock->expects($this->any()) - ->method('getData') + $productMock = $this->createMock(Product::class); + $productMock->expects($this->once()) + ->method('getTypeId') + ->willReturn(Type::TYPE_BUNDLE); + $productMock->method('getData') ->with($this->linkField) ->willReturn($this->linkField); - $linkedProductMock = $this->createMock(\Magento\Catalog\Model\Product::class); - $linkedProductMock->expects($this->any())->method('getId')->will($this->returnValue(13)); - $linkedProductMock->expects($this->once())->method('isComposite')->will($this->returnValue(true)); - $this->productRepository - ->expects($this->once()) + $linkedProductMock = $this->createMock(Product::class); + $linkedProductMock->method('getId')->willReturn(13); + $linkedProductMock->expects($this->once()) + ->method('isComposite') + ->willReturn(true); + $this->productRepository->expects($this->once()) ->method('get') ->with('linked_product_sku') - ->will($this->returnValue($linkedProductMock)); + ->willReturn($linkedProductMock); - $store = $this->createMock(\Magento\Store\Model\Store::class); - $this->storeManagerMock->expects($this->any())->method('getStore')->will($this->returnValue($store)); - $store->expects($this->any())->method('getId')->will($this->returnValue(0)); + $store = $this->createMock(Store::class); + $this->storeManagerMock + ->method('getStore') + ->willReturn($store); + $store->method('getId') + ->willReturn(0); - $option = $this->getMockBuilder(\Magento\Bundle\Model\Option::class)->disableOriginalConstructor() + $option = $this->getMockBuilder(Option::class)->disableOriginalConstructor() ->setMethods(['getId', '__wakeup']) ->getMock(); - $option->expects($this->once())->method('getId')->will($this->returnValue(1)); + $option->expects($this->once())->method('getId')->willReturn(1); - $optionsCollectionMock = $this->createMock(\Magento\Bundle\Model\ResourceModel\Option\Collection::class); + $optionsCollectionMock = $this->createMock(OptionCollection::class); $optionsCollectionMock->expects($this->once()) ->method('setIdFilter') ->with($this->equalTo('1')) - ->will($this->returnSelf()); + ->willReturnSelf(); $optionsCollectionMock->expects($this->once()) ->method('getFirstItem') - ->will($this->returnValue($option)); - $this->optionCollectionFactoryMock->expects($this->any())->method('create')->will( - $this->returnValue($optionsCollectionMock) - ); + ->willReturn($option); + $this->optionCollectionFactoryMock->method('create') + ->willReturn($optionsCollectionMock); - $bundle = $this->createMock(\Magento\Bundle\Model\ResourceModel\Bundle::class); + $bundle = $this->createMock(Bundle::class); $bundle->expects($this->once())->method('getSelectionsData')->with($this->linkField)->willReturn([]); - $this->bundleFactoryMock->expects($this->once())->method('create')->will($this->returnValue($bundle)); + $this->bundleFactoryMock->expects($this->once())->method('create')->willReturn($bundle); $this->model->addChild($productMock, 1, $productLink); } @@ -398,68 +453,74 @@ public function testAddChildLinkedProductIsComposite() */ public function testAddChildProductAlreadyExistsInOption() { - $productLink = $this->getMockBuilder(\Magento\Bundle\Api\Data\LinkInterface::class) + $productLink = $this->getMockBuilder(LinkInterface::class) ->setMethods(['getSku', 'getOptionId', 'getSelectionId']) ->disableOriginalConstructor() ->getMockForAbstractClass(); - $productLink->expects($this->any())->method('getSku')->will($this->returnValue('linked_product_sku')); - $productLink->expects($this->any())->method('getOptionId')->will($this->returnValue(1)); - $productLink->expects($this->any())->method('getSelectionId')->will($this->returnValue(1)); + $productLink->method('getSku')->willReturn('linked_product_sku'); + $productLink->method('getOptionId')->willReturn(1); + $productLink->method('getSelectionId')->willReturn(1); $this->metadataMock->expects($this->once())->method('getLinkField')->willReturn($this->linkField); $productMock = $this->createPartialMock( - \Magento\Catalog\Model\Product::class, + Product::class, ['getTypeId', 'getCopyFromView', 'getData', 'getTypeInstance', 'getSku'] ); $productMock->expects($this->once())->method('getTypeId')->willReturn( - \Magento\Catalog\Model\Product\Type::TYPE_BUNDLE + Type::TYPE_BUNDLE ); - $productMock->expects($this->any()) - ->method('getData') + $productMock->method('getData') ->with($this->linkField) ->willReturn($this->linkField); - $productMock->expects($this->any())->method('getCopyFromView')->will($this->returnValue(false)); - - $linkedProductMock = $this->createMock(\Magento\Catalog\Model\Product::class); - $linkedProductMock->expects($this->any())->method('getEntityId')->will($this->returnValue(13)); - $linkedProductMock->expects($this->once())->method('isComposite')->will($this->returnValue(false)); - $this->productRepository - ->expects($this->once()) + $productMock->method('getCopyFromView') + ->willReturn(false); + + $linkedProductMock = $this->createMock(Product::class); + $linkedProductMock->method('getEntityId') + ->willReturn(13); + $linkedProductMock->expects($this->once()) + ->method('isComposite') + ->willReturn(false); + $this->productRepository->expects($this->once()) ->method('get') ->with('linked_product_sku') - ->will($this->returnValue($linkedProductMock)); + ->willReturn($linkedProductMock); - $store = $this->createMock(\Magento\Store\Model\Store::class); - $this->storeManagerMock->expects($this->any())->method('getStore')->will($this->returnValue($store)); - $store->expects($this->any())->method('getId')->will($this->returnValue(0)); + $store = $this->createMock(Store::class); + $this->storeManagerMock->method('getStore')->willReturn($store); + $store->method('getId')->willReturn(0); - $option = $this->getMockBuilder(\Magento\Bundle\Model\Option::class)->disableOriginalConstructor() + $option = $this->getMockBuilder(Option::class)->disableOriginalConstructor() ->setMethods(['getId', '__wakeup']) ->getMock(); - $option->expects($this->once())->method('getId')->will($this->returnValue(1)); + $option->expects($this->once()) + ->method('getId') + ->willReturn(1); - $optionsCollectionMock = $this->createMock(\Magento\Bundle\Model\ResourceModel\Option\Collection::class); + $optionsCollectionMock = $this->createMock(OptionCollection::class); $optionsCollectionMock->expects($this->once()) ->method('setIdFilter') ->with($this->equalTo(1)) - ->will($this->returnSelf()); + ->willReturnSelf(); $optionsCollectionMock->expects($this->once()) ->method('getFirstItem') - ->will($this->returnValue($option)); - $this->optionCollectionFactoryMock->expects($this->any())->method('create')->will( - $this->returnValue($optionsCollectionMock) - ); + ->willReturn($option); + $this->optionCollectionFactoryMock->method('create') + ->willReturn($optionsCollectionMock); $selections = [ ['option_id' => 1, 'product_id' => 12, 'parent_product_id' => 'product_id'], ['option_id' => 1, 'product_id' => 13, 'parent_product_id' => 'product_id'], ]; - $bundle = $this->createMock(\Magento\Bundle\Model\ResourceModel\Bundle::class); - $bundle->expects($this->once())->method('getSelectionsData') + $bundle = $this->createMock(Bundle::class); + $bundle->expects($this->once()) + ->method('getSelectionsData') ->with($this->linkField) - ->will($this->returnValue($selections)); - $this->bundleFactoryMock->expects($this->once())->method('create')->will($this->returnValue($bundle)); + ->willReturn($selections); + $this->bundleFactoryMock->expects($this->once()) + ->method('create') + ->willReturn($bundle); $this->model->addChild($productMock, 1, $productLink); } @@ -468,141 +529,141 @@ public function testAddChildProductAlreadyExistsInOption() */ public function testAddChildCouldNotSave() { - $productLink = $this->getMockBuilder(\Magento\Bundle\Api\Data\LinkInterface::class) + $productLink = $this->getMockBuilder(LinkInterface::class) ->setMethods(['getSku', 'getOptionId', 'getSelectionId']) ->disableOriginalConstructor() ->getMockForAbstractClass(); - $productLink->expects($this->any())->method('getSku')->will($this->returnValue('linked_product_sku')); - $productLink->expects($this->any())->method('getOptionId')->will($this->returnValue(1)); - $productLink->expects($this->any())->method('getSelectionId')->will($this->returnValue(1)); + $productLink->method('getSku')->willReturn('linked_product_sku'); + $productLink->method('getOptionId')->willReturn(1); + $productLink->method('getSelectionId')->willReturn(1); $this->metadataMock->expects($this->once())->method('getLinkField')->willReturn($this->linkField); - $productMock = $this->createMock(\Magento\Catalog\Model\Product::class); - $productMock->expects($this->once())->method('getTypeId')->will($this->returnValue( - \Magento\Catalog\Model\Product\Type::TYPE_BUNDLE - )); - $productMock->expects($this->any()) + $productMock = $this->createMock(Product::class); + $productMock->expects($this->once()) + ->method('getTypeId') + ->willReturn(Type::TYPE_BUNDLE); + $productMock ->method('getData') ->with($this->linkField) ->willReturn($this->linkField); - $linkedProductMock = $this->createMock(\Magento\Catalog\Model\Product::class); - $linkedProductMock->expects($this->any())->method('getId')->will($this->returnValue(13)); - $linkedProductMock->expects($this->once())->method('isComposite')->will($this->returnValue(false)); + $linkedProductMock = $this->createMock(Product::class); + $linkedProductMock->method('getId')->willReturn(13); + $linkedProductMock->expects($this->once()) + ->method('isComposite') + ->willReturn(false); $this->productRepository ->expects($this->once()) ->method('get') ->with('linked_product_sku') - ->will($this->returnValue($linkedProductMock)); + ->willReturn($linkedProductMock); - $store = $this->createMock(\Magento\Store\Model\Store::class); - $this->storeManagerMock->expects($this->any())->method('getStore')->will($this->returnValue($store)); - $store->expects($this->any())->method('getId')->will($this->returnValue(0)); + $store = $this->createMock(Store::class); + $this->storeManagerMock->method('getStore')->willReturn($store); + $store->method('getId')->willReturn(0); - $option = $this->getMockBuilder(\Magento\Bundle\Model\Option::class)->disableOriginalConstructor() + $option = $this->getMockBuilder(Option::class)->disableOriginalConstructor() ->setMethods(['getId', '__wakeup']) ->getMock(); - $option->expects($this->once())->method('getId')->will($this->returnValue(1)); + $option->expects($this->once())->method('getId')->willReturn(1); - $optionsCollectionMock = $this->createMock(\Magento\Bundle\Model\ResourceModel\Option\Collection::class); + $optionsCollectionMock = $this->createMock(OptionCollection::class); $optionsCollectionMock->expects($this->once()) ->method('setIdFilter') ->with($this->equalTo(1)) - ->will($this->returnSelf()); + ->willReturnSelf(); $optionsCollectionMock->expects($this->once()) ->method('getFirstItem') - ->will($this->returnValue($option)); - $this->optionCollectionFactoryMock->expects($this->any())->method('create')->will( - $this->returnValue($optionsCollectionMock) - ); + ->willReturn($option); + $this->optionCollectionFactoryMock->method('create') + ->willReturn($optionsCollectionMock); $selections = [ ['option_id' => 1, 'product_id' => 11], ['option_id' => 1, 'product_id' => 12], ]; - $bundle = $this->createMock(\Magento\Bundle\Model\ResourceModel\Bundle::class); + $bundle = $this->createMock(Bundle::class); $bundle->expects($this->once())->method('getSelectionsData') ->with($this->linkField) - ->will($this->returnValue($selections)); - $this->bundleFactoryMock->expects($this->once())->method('create')->will($this->returnValue($bundle)); + ->willReturn($selections); + $this->bundleFactoryMock->expects($this->once()) + ->method('create') + ->willReturn($bundle); - $selection = $this->createPartialMock(\Magento\Bundle\Model\Selection::class, ['save']); + $selection = $this->createPartialMock(Selection::class, ['save']); $selection->expects($this->once())->method('save') - ->will( - $this->returnCallback( - function () { - throw new \Exception('message'); - } - ) + ->willReturnCallback( + static function () { + throw new Exception('message'); + } ); - $this->bundleSelectionMock->expects($this->once())->method('create')->will($this->returnValue($selection)); + $this->bundleSelectionMock->expects($this->once()) + ->method('create') + ->willReturn($selection); $this->model->addChild($productMock, 1, $productLink); } public function testAddChild() { - $productLink = $this->getMockBuilder(\Magento\Bundle\Api\Data\LinkInterface::class) + $productLink = $this->getMockBuilder(LinkInterface::class) ->setMethods(['getSku', 'getOptionId', 'getSelectionId']) ->disableOriginalConstructor() ->getMockForAbstractClass(); - $productLink->expects($this->any())->method('getSku')->will($this->returnValue('linked_product_sku')); - $productLink->expects($this->any())->method('getOptionId')->will($this->returnValue(1)); - $productLink->expects($this->any())->method('getSelectionId')->will($this->returnValue(1)); + $productLink->method('getSku')->willReturn('linked_product_sku'); + $productLink->method('getOptionId')->willReturn(1); + $productLink->method('getSelectionId')->willReturn(1); $this->metadataMock->expects($this->once())->method('getLinkField')->willReturn($this->linkField); - $productMock = $this->createMock(\Magento\Catalog\Model\Product::class); - $productMock->expects($this->once())->method('getTypeId')->will($this->returnValue( - \Magento\Catalog\Model\Product\Type::TYPE_BUNDLE - )); - $productMock->expects($this->any()) + $productMock = $this->createMock(Product::class); + $productMock->expects($this->once())->method('getTypeId')->willReturn(Type::TYPE_BUNDLE); + $productMock ->method('getData') ->with($this->linkField) ->willReturn($this->linkField); - $linkedProductMock = $this->createMock(\Magento\Catalog\Model\Product::class); - $linkedProductMock->expects($this->any())->method('getId')->will($this->returnValue(13)); - $linkedProductMock->expects($this->once())->method('isComposite')->will($this->returnValue(false)); + $linkedProductMock = $this->createMock(Product::class); + $linkedProductMock->method('getId')->willReturn(13); + $linkedProductMock->expects($this->once())->method('isComposite')->willReturn(false); $this->productRepository ->expects($this->once()) ->method('get') ->with('linked_product_sku') - ->will($this->returnValue($linkedProductMock)); + ->willReturn($linkedProductMock); - $store = $this->createMock(\Magento\Store\Model\Store::class); - $this->storeManagerMock->expects($this->any())->method('getStore')->will($this->returnValue($store)); - $store->expects($this->any())->method('getId')->will($this->returnValue(0)); + $store = $this->createMock(Store::class); + $this->storeManagerMock->method('getStore')->willReturn($store); + $store->method('getId')->willReturn(0); - $option = $this->getMockBuilder(\Magento\Bundle\Model\Option::class)->disableOriginalConstructor() + $option = $this->getMockBuilder(Option::class)->disableOriginalConstructor() ->setMethods(['getId', '__wakeup']) ->getMock(); - $option->expects($this->once())->method('getId')->will($this->returnValue(1)); + $option->expects($this->once())->method('getId')->willReturn(1); - $optionsCollectionMock = $this->createMock(\Magento\Bundle\Model\ResourceModel\Option\Collection::class); + $optionsCollectionMock = $this->createMock(OptionCollection::class); $optionsCollectionMock->expects($this->once()) ->method('setIdFilter') ->with($this->equalTo(1)) - ->will($this->returnSelf()); + ->willReturnSelf(); $optionsCollectionMock->expects($this->once()) ->method('getFirstItem') - ->will($this->returnValue($option)); - $this->optionCollectionFactoryMock->expects($this->any())->method('create')->will( - $this->returnValue($optionsCollectionMock) - ); + ->willReturn($option); + $this->optionCollectionFactoryMock->method('create') + ->willReturn($optionsCollectionMock); $selections = [ ['option_id' => 1, 'product_id' => 11], ['option_id' => 1, 'product_id' => 12], ]; - $bundle = $this->createMock(\Magento\Bundle\Model\ResourceModel\Bundle::class); + $bundle = $this->createMock(Bundle::class); $bundle->expects($this->once())->method('getSelectionsData') ->with($this->linkField) - ->will($this->returnValue($selections)); - $this->bundleFactoryMock->expects($this->once())->method('create')->will($this->returnValue($bundle)); + ->willReturn($selections); + $this->bundleFactoryMock->expects($this->once())->method('create')->willReturn($bundle); - $selection = $this->createPartialMock(\Magento\Bundle\Model\Selection::class, ['save', 'getId']); + $selection = $this->createPartialMock(Selection::class, ['save', 'getId']); $selection->expects($this->once())->method('save'); - $selection->expects($this->once())->method('getId')->will($this->returnValue(42)); - $this->bundleSelectionMock->expects($this->once())->method('create')->will($this->returnValue($selection)); + $selection->expects($this->once())->method('getId')->willReturn(42); + $this->bundleSelectionMock->expects($this->once())->method('create')->willReturn($selection); $result = $this->model->addChild($productMock, 1, $productLink); $this->assertEquals(42, $result); } @@ -621,51 +682,51 @@ public function testSaveChild() $parentProductId = 32; $bundleProductSku = 'bundleProductSku'; - $productLink = $this->getMockBuilder(\Magento\Bundle\Api\Data\LinkInterface::class) + $productLink = $this->getMockBuilder(LinkInterface::class) ->setMethods(['getSku', 'getOptionId', 'getSelectionId']) ->disableOriginalConstructor() ->getMockForAbstractClass(); - $productLink->expects($this->any())->method('getSku')->will($this->returnValue('linked_product_sku')); - $productLink->expects($this->any())->method('getId')->will($this->returnValue($id)); - $productLink->expects($this->any())->method('getOptionId')->will($this->returnValue($optionId)); - $productLink->expects($this->any())->method('getPosition')->will($this->returnValue($position)); - $productLink->expects($this->any())->method('getQty')->will($this->returnValue($qty)); - $productLink->expects($this->any())->method('getPriceType')->will($this->returnValue($priceType)); - $productLink->expects($this->any())->method('getPrice')->will($this->returnValue($price)); - $productLink->expects($this->any())->method('getCanChangeQuantity') - ->will($this->returnValue($canChangeQuantity)); - $productLink->expects($this->any())->method('getIsDefault')->will($this->returnValue($isDefault)); - $productLink->expects($this->any())->method('getSelectionId')->will($this->returnValue($optionId)); + $productLink->method('getSku')->willReturn('linked_product_sku'); + $productLink->method('getId')->willReturn($id); + $productLink->method('getOptionId')->willReturn($optionId); + $productLink->method('getPosition')->willReturn($position); + $productLink->method('getQty')->willReturn($qty); + $productLink->method('getPriceType')->willReturn($priceType); + $productLink->method('getPrice')->willReturn($price); + $productLink->method('getCanChangeQuantity') + ->willReturn($canChangeQuantity); + $productLink->method('getIsDefault')->willReturn($isDefault); + $productLink->method('getSelectionId')->willReturn($optionId); $this->metadataMock->expects($this->once())->method('getLinkField')->willReturn($this->linkField); - $productMock = $this->createMock(\Magento\Catalog\Model\Product::class); - $productMock->expects($this->once())->method('getTypeId')->will($this->returnValue( - \Magento\Catalog\Model\Product\Type::TYPE_BUNDLE - )); - $productMock->expects($this->any()) + $productMock = $this->createMock(Product::class); + $productMock->expects($this->once())->method('getTypeId')->willReturn(Type::TYPE_BUNDLE); + $productMock ->method('getData') ->with($this->linkField) ->willReturn($parentProductId); - $linkedProductMock = $this->createMock(\Magento\Catalog\Model\Product::class); - $linkedProductMock->expects($this->any())->method('getId')->will($this->returnValue($linkProductId)); - $linkedProductMock->expects($this->once())->method('isComposite')->will($this->returnValue(false)); + $linkedProductMock = $this->createMock(Product::class); + $linkedProductMock->method('getId')->willReturn($linkProductId); + $linkedProductMock->expects($this->once())->method('isComposite')->willReturn(false); $this->productRepository ->expects($this->at(0)) ->method('get') ->with($bundleProductSku) - ->will($this->returnValue($productMock)); + ->willReturn($productMock); $this->productRepository ->expects($this->at(1)) ->method('get') ->with('linked_product_sku') - ->will($this->returnValue($linkedProductMock)); + ->willReturn($linkedProductMock); - $store = $this->createMock(\Magento\Store\Model\Store::class); - $this->storeManagerMock->expects($this->any())->method('getStore')->will($this->returnValue($store)); - $store->expects($this->any())->method('getId')->will($this->returnValue(0)); + $store = $this->createMock(Store::class); + $this->storeManagerMock->method('getStore')->willReturn($store); + $store->method('getId')->willReturn(0); - $selection = $this->createPartialMock(\Magento\Bundle\Model\Selection::class, [ + $selection = $this->createPartialMock( + Selection::class, + [ 'save', 'getId', 'load', @@ -678,10 +739,11 @@ public function testSaveChild() 'setSelectionPriceValue', 'setSelectionCanChangeQty', 'setIsDefault' - ]); + ] + ); $selection->expects($this->once())->method('save'); - $selection->expects($this->once())->method('load')->with($id)->will($this->returnSelf()); - $selection->expects($this->any())->method('getId')->will($this->returnValue($id)); + $selection->expects($this->once())->method('load')->with($id)->willReturnSelf(); + $selection->method('getId')->willReturn($id); $selection->expects($this->once())->method('setProductId')->with($linkProductId); $selection->expects($this->once())->method('setParentProductId')->with($parentProductId); $selection->expects($this->once())->method('setOptionId')->with($optionId); @@ -692,7 +754,7 @@ public function testSaveChild() $selection->expects($this->once())->method('setSelectionCanChangeQty')->with($canChangeQuantity); $selection->expects($this->once())->method('setIsDefault')->with($isDefault); - $this->bundleSelectionMock->expects($this->once())->method('create')->will($this->returnValue($selection)); + $this->bundleSelectionMock->expects($this->once())->method('create')->willReturn($selection); $this->assertTrue($this->model->saveChild($bundleProductSku, $productLink)); } @@ -705,40 +767,45 @@ public function testSaveChildFailedToSave() $linkProductId = 45; $parentProductId = 32; - $productLink = $this->getMockBuilder(\Magento\Bundle\Api\Data\LinkInterface::class) + $productLink = $this->getMockBuilder(LinkInterface::class) ->setMethods(['getSku', 'getOptionId', 'getSelectionId']) ->disableOriginalConstructor() ->getMockForAbstractClass(); - $productLink->expects($this->any())->method('getSku')->will($this->returnValue('linked_product_sku')); - $productLink->expects($this->any())->method('getId')->will($this->returnValue($id)); - $productLink->expects($this->any())->method('getSelectionId')->will($this->returnValue(1)); + $productLink->method('getSku')->willReturn('linked_product_sku'); + $productLink->method('getId')->willReturn($id); + $productLink->method('getSelectionId')->willReturn(1); $bundleProductSku = 'bundleProductSku'; - $productMock = $this->createMock(\Magento\Catalog\Model\Product::class); - $productMock->expects($this->once())->method('getTypeId')->will($this->returnValue( - \Magento\Catalog\Model\Product\Type::TYPE_BUNDLE - )); - $productMock->expects($this->any())->method('getId')->will($this->returnValue($parentProductId)); + $productMock = $this->createMock(Product::class); + $productMock->expects($this->once()) + ->method('getTypeId') + ->willReturn(Type::TYPE_BUNDLE); + $productMock->method('getId') + ->willReturn($parentProductId); - $linkedProductMock = $this->createMock(\Magento\Catalog\Model\Product::class); - $linkedProductMock->expects($this->any())->method('getId')->will($this->returnValue($linkProductId)); - $linkedProductMock->expects($this->once())->method('isComposite')->will($this->returnValue(false)); - $this->productRepository - ->expects($this->at(0)) + $linkedProductMock = $this->createMock(Product::class); + $linkedProductMock->method('getId')->willReturn($linkProductId); + $linkedProductMock->expects($this->once()) + ->method('isComposite') + ->willReturn(false); + $this->productRepository->expects($this->at(0)) ->method('get') ->with($bundleProductSku) - ->will($this->returnValue($productMock)); - $this->productRepository - ->expects($this->at(1)) + ->willReturn($productMock); + $this->productRepository->expects($this->at(1)) ->method('get') ->with('linked_product_sku') - ->will($this->returnValue($linkedProductMock)); + ->willReturn($linkedProductMock); - $store = $this->createMock(\Magento\Store\Model\Store::class); - $this->storeManagerMock->expects($this->any())->method('getStore')->will($this->returnValue($store)); - $store->expects($this->any())->method('getId')->will($this->returnValue(0)); + $store = $this->createMock(Store::class); + $this->storeManagerMock->method('getStore') + ->willReturn($store); + $store->method('getId') + ->willReturn(0); - $selection = $this->createPartialMock(\Magento\Bundle\Model\Selection::class, [ + $selection = $this->createPartialMock( + Selection::class, + [ 'save', 'getId', 'load', @@ -752,14 +819,25 @@ public function testSaveChildFailedToSave() 'setSelectionPriceValue', 'setSelectionCanChangeQty', 'setIsDefault' - ]); - $mockException = $this->createMock(\Exception::class); - $selection->expects($this->once())->method('save')->will($this->throwException($mockException)); - $selection->expects($this->once())->method('load')->with($id)->will($this->returnSelf()); - $selection->expects($this->any())->method('getId')->will($this->returnValue($id)); - $selection->expects($this->once())->method('setProductId')->with($linkProductId); - - $this->bundleSelectionMock->expects($this->once())->method('create')->will($this->returnValue($selection)); + ] + ); + $mockException = $this->createMock(Exception::class); + $selection->expects($this->once()) + ->method('save') + ->willThrowException($mockException); + $selection->expects($this->once()) + ->method('load') + ->with($id) + ->willReturnSelf(); + $selection->method('getId') + ->willReturn($id); + $selection->expects($this->once()) + ->method('setProductId') + ->with($linkProductId); + + $this->bundleSelectionMock->expects($this->once()) + ->method('create') + ->willReturn($selection); $this->model->saveChild($bundleProductSku, $productLink); } @@ -768,29 +846,29 @@ public function testSaveChildFailedToSave() */ public function testSaveChildWithoutId() { - $bundleProductSku = "bundleSku"; + $bundleProductSku = 'bundleSku'; $linkedProductSku = 'simple'; - $productLink = $this->createMock(\Magento\Bundle\Api\Data\LinkInterface::class); - $productLink->expects($this->any())->method('getId')->will($this->returnValue(null)); - $productLink->expects($this->any())->method('getSku')->will($this->returnValue($linkedProductSku)); + $productLink = $this->createMock(LinkInterface::class); + $productLink->method('getId')->willReturn(null); + $productLink->method('getSku')->willReturn($linkedProductSku); - $productMock = $this->createMock(\Magento\Catalog\Model\Product::class); - $productMock->expects($this->once())->method('getTypeId')->will($this->returnValue( - \Magento\Catalog\Model\Product\Type::TYPE_BUNDLE - )); + $productMock = $this->createMock(Product::class); + $productMock->expects($this->once()) + ->method('getTypeId') + ->willReturn(Type::TYPE_BUNDLE); - $linkedProductMock = $this->createMock(\Magento\Catalog\Model\Product::class); - $linkedProductMock->expects($this->once())->method('isComposite')->will($this->returnValue(false)); - $this->productRepository - ->expects($this->at(0)) + $linkedProductMock = $this->createMock(Product::class); + $linkedProductMock->expects($this->once()) + ->method('isComposite') + ->willReturn(false); + $this->productRepository->expects($this->at(0)) ->method('get') ->with($bundleProductSku) - ->will($this->returnValue($productMock)); - $this->productRepository - ->expects($this->at(1)) + ->willReturn($productMock); + $this->productRepository->expects($this->at(1)) ->method('get') ->with($linkedProductSku) - ->will($this->returnValue($linkedProductMock)); + ->willReturn($linkedProductMock); $this->model->saveChild($bundleProductSku, $productLink); } @@ -803,37 +881,45 @@ public function testSaveChildWithInvalidId() { $id = 12345; $linkedProductSku = 'simple'; - $bundleProductSku = "bundleProductSku"; - $productLink = $this->createMock(\Magento\Bundle\Api\Data\LinkInterface::class); - $productLink->expects($this->any())->method('getId')->will($this->returnValue($id)); - $productLink->expects($this->any())->method('getSku')->will($this->returnValue($linkedProductSku)); - - $productMock = $this->createMock(\Magento\Catalog\Model\Product::class); - $productMock->expects($this->once())->method('getTypeId')->will($this->returnValue( - \Magento\Catalog\Model\Product\Type::TYPE_BUNDLE - )); - - $linkedProductMock = $this->createMock(\Magento\Catalog\Model\Product::class); - $linkedProductMock->expects($this->once())->method('isComposite')->will($this->returnValue(false)); - $this->productRepository - ->expects($this->at(0)) + $bundleProductSku = 'bundleProductSku'; + $productLink = $this->createMock(LinkInterface::class); + $productLink->method('getId')->willReturn($id); + $productLink->method('getSku')->willReturn($linkedProductSku); + + $productMock = $this->createMock(Product::class); + $productMock->expects($this->once()) + ->method('getTypeId') + ->willReturn(Type::TYPE_BUNDLE); + + $linkedProductMock = $this->createMock(Product::class); + $linkedProductMock->expects($this->once()) + ->method('isComposite') + ->willReturn(false); + $this->productRepository->expects($this->at(0)) ->method('get') ->with($bundleProductSku) - ->will($this->returnValue($productMock)); - $this->productRepository - ->expects($this->at(1)) + ->willReturn($productMock); + $this->productRepository->expects($this->at(1)) ->method('get') ->with($linkedProductSku) - ->will($this->returnValue($linkedProductMock)); + ->willReturn($linkedProductMock); - $selection = $this->createPartialMock(\Magento\Bundle\Model\Selection::class, [ + $selection = $this->createPartialMock( + Selection::class, + [ 'getId', 'load', - ]); - $selection->expects($this->once())->method('load')->with($id)->will($this->returnSelf()); - $selection->expects($this->any())->method('getId')->will($this->returnValue(null)); + ] + ); + $selection->expects($this->once()) + ->method('load') + ->with($id) + ->willReturnSelf(); + $selection->method('getId')->willReturn(null); - $this->bundleSelectionMock->expects($this->once())->method('create')->will($this->returnValue($selection)); + $this->bundleSelectionMock->expects($this->once()) + ->method('create') + ->willReturn($selection); $this->model->saveChild($bundleProductSku, $productLink); } @@ -843,30 +929,26 @@ public function testSaveChildWithInvalidId() */ public function testSaveChildWithCompositeProductLink() { - $bundleProductSku = "bundleProductSku"; + $bundleProductSku = 'bundleProductSku'; $id = 12; $linkedProductSku = 'simple'; - $productLink = $this->createMock(\Magento\Bundle\Api\Data\LinkInterface::class); - $productLink->expects($this->any())->method('getId')->will($this->returnValue($id)); - $productLink->expects($this->any())->method('getSku')->will($this->returnValue($linkedProductSku)); + $productLink = $this->createMock(LinkInterface::class); + $productLink->method('getId')->willReturn($id); + $productLink->method('getSku')->willReturn($linkedProductSku); - $productMock = $this->createMock(\Magento\Catalog\Model\Product::class); - $productMock->expects($this->once())->method('getTypeId')->will($this->returnValue( - \Magento\Catalog\Model\Product\Type::TYPE_BUNDLE - )); + $productMock = $this->createMock(Product::class); + $productMock->expects($this->once())->method('getTypeId')->willReturn(Type::TYPE_BUNDLE); - $linkedProductMock = $this->createMock(\Magento\Catalog\Model\Product::class); - $linkedProductMock->expects($this->once())->method('isComposite')->will($this->returnValue(true)); - $this->productRepository - ->expects($this->at(0)) + $linkedProductMock = $this->createMock(Product::class); + $linkedProductMock->expects($this->once())->method('isComposite')->willReturn(true); + $this->productRepository->expects($this->at(0)) ->method('get') ->with($bundleProductSku) - ->will($this->returnValue($productMock)); - $this->productRepository - ->expects($this->at(1)) + ->willReturn($productMock); + $this->productRepository->expects($this->at(1)) ->method('get') ->with($linkedProductSku) - ->will($this->returnValue($linkedProductMock)); + ->willReturn($linkedProductMock); $this->model->saveChild($bundleProductSku, $productLink); } @@ -878,18 +960,18 @@ public function testSaveChildWithSimpleProduct() { $id = 12; $linkedProductSku = 'simple'; - $bundleProductSku = "bundleProductSku"; + $bundleProductSku = 'bundleProductSku'; - $productLink = $this->createMock(\Magento\Bundle\Api\Data\LinkInterface::class); - $productLink->expects($this->any())->method('getId')->will($this->returnValue($id)); - $productLink->expects($this->any())->method('getSku')->will($this->returnValue($linkedProductSku)); + $productLink = $this->createMock(LinkInterface::class); + $productLink->method('getId')->willReturn($id); + $productLink->method('getSku')->willReturn($linkedProductSku); - $productMock = $this->createMock(\Magento\Catalog\Model\Product::class); - $productMock->expects($this->once())->method('getTypeId')->will($this->returnValue( - \Magento\Catalog\Model\Product\Type::TYPE_SIMPLE - )); + $productMock = $this->createMock(Product::class); + $productMock->expects($this->once())->method('getTypeId')->willReturn(Type::TYPE_SIMPLE); - $this->productRepository->expects($this->once())->method('get')->with($bundleProductSku) + $this->productRepository->expects($this->once()) + ->method('get') + ->with($bundleProductSku) ->willReturn($productMock); $this->model->saveChild($bundleProductSku, $productLink); @@ -897,34 +979,33 @@ public function testSaveChildWithSimpleProduct() public function testRemoveChild() { - $this->productRepository->expects($this->any())->method('get')->will($this->returnValue($this->product)); - $bundle = $this->createMock(\Magento\Bundle\Model\ResourceModel\Bundle::class); - $this->bundleFactoryMock->expects($this->once())->method('create')->will($this->returnValue($bundle)); + $this->productRepository->method('get')->willReturn($this->product); + $bundle = $this->createMock(Bundle::class); + $this->bundleFactoryMock->expects($this->once()) + ->method('create') + ->willReturn($bundle); $productSku = 'productSku'; $optionId = 1; $productId = 1; $childSku = 'childSku'; - $this->product - ->expects($this->any()) - ->method('getTypeId') - ->will($this->returnValue(\Magento\Catalog\Model\Product\Type::TYPE_BUNDLE)); + $this->product->method('getTypeId') + ->willReturn(Type::TYPE_BUNDLE); $this->getRemoveOptions(); - $selection = $this->getMockBuilder(\Magento\Bundle\Model\Selection::class) + $selection = $this->getMockBuilder(Selection::class) ->setMethods(['getSku', 'getOptionId', 'getSelectionId', 'getProductId', '__wakeup']) ->disableOriginalConstructor() ->getMock(); - $selection->expects($this->any())->method('getSku')->will($this->returnValue($childSku)); - $selection->expects($this->any())->method('getOptionId')->will($this->returnValue($optionId)); - $selection->expects($this->any())->method('getSelectionId')->will($this->returnValue(55)); - $selection->expects($this->any())->method('getProductId')->willReturn($productId); - - $this->option->expects($this->any())->method('getSelections')->will($this->returnValue([$selection])); - $this->metadataMock->expects($this->any())->method('getLinkField')->willReturn($this->linkField); - $this->product->expects($this->any()) - ->method('getData') + $selection->method('getSku')->willReturn($childSku); + $selection->method('getOptionId')->willReturn($optionId); + $selection->method('getSelectionId')->willReturn(55); + $selection->method('getProductId')->willReturn($productId); + + $this->option->method('getSelections')->willReturn([$selection]); + $this->metadataMock->method('getLinkField')->willReturn($this->linkField); + $this->product->method('getData') ->with($this->linkField) ->willReturn(3); @@ -939,14 +1020,12 @@ public function testRemoveChild() */ public function testRemoveChildForbidden() { - $this->productRepository->expects($this->any())->method('get')->will($this->returnValue($this->product)); + $this->productRepository->method('get')->willReturn($this->product); $productSku = 'productSku'; $optionId = 1; $childSku = 'childSku'; - $this->product - ->expects($this->any()) - ->method('getTypeId') - ->will($this->returnValue(\Magento\Catalog\Model\Product\Type::TYPE_SIMPLE)); + $this->product->method('getTypeId') + ->willReturn(Type::TYPE_SIMPLE); $this->model->removeChild($productSku, $optionId, $childSku); } @@ -955,28 +1034,26 @@ public function testRemoveChildForbidden() */ public function testRemoveChildInvalidOptionId() { - $this->productRepository->expects($this->any())->method('get')->will($this->returnValue($this->product)); + $this->productRepository->method('get')->willReturn($this->product); $productSku = 'productSku'; $optionId = 1; $childSku = 'childSku'; - $this->product - ->expects($this->any()) - ->method('getTypeId') - ->will($this->returnValue(\Magento\Catalog\Model\Product\Type::TYPE_BUNDLE)); + $this->product->method('getTypeId') + ->willReturn(Type::TYPE_BUNDLE); $this->getRemoveOptions(); - $selection = $this->getMockBuilder(\Magento\Bundle\Model\Selection::class) + $selection = $this->getMockBuilder(Selection::class) ->setMethods(['getSku', 'getOptionId', 'getSelectionId', 'getProductId', '__wakeup']) ->disableOriginalConstructor() ->getMock(); - $selection->expects($this->any())->method('getSku')->will($this->returnValue($childSku)); - $selection->expects($this->any())->method('getOptionId')->will($this->returnValue($optionId + 1)); - $selection->expects($this->any())->method('getSelectionId')->will($this->returnValue(55)); - $selection->expects($this->any())->method('getProductId')->will($this->returnValue(1)); + $selection->method('getSku')->willReturn($childSku); + $selection->method('getOptionId')->willReturn($optionId + 1); + $selection->method('getSelectionId')->willReturn(55); + $selection->method('getProductId')->willReturn(1); - $this->option->expects($this->any())->method('getSelections')->will($this->returnValue([$selection])); + $this->option->method('getSelections')->willReturn([$selection]); $this->model->removeChild($productSku, $optionId, $childSku); } @@ -985,61 +1062,71 @@ public function testRemoveChildInvalidOptionId() */ public function testRemoveChildInvalidChildSku() { - $this->productRepository->expects($this->any())->method('get')->will($this->returnValue($this->product)); + $this->productRepository->method('get')->willReturn($this->product); $productSku = 'productSku'; $optionId = 1; $childSku = 'childSku'; - $this->product - ->expects($this->any()) - ->method('getTypeId') - ->will($this->returnValue(\Magento\Catalog\Model\Product\Type::TYPE_BUNDLE)); + $this->product->method('getTypeId') + ->willReturn(Type::TYPE_BUNDLE); $this->getRemoveOptions(); - $selection = $this->getMockBuilder(\Magento\Bundle\Model\Selection::class) + $selection = $this->getMockBuilder(Selection::class) ->setMethods(['getSku', 'getOptionId', 'getSelectionId', 'getProductId', '__wakeup']) ->disableOriginalConstructor() ->getMock(); - $selection->expects($this->any())->method('getSku')->will($this->returnValue($childSku . '_invalid')); - $selection->expects($this->any())->method('getOptionId')->will($this->returnValue($optionId)); - $selection->expects($this->any())->method('getSelectionId')->will($this->returnValue(55)); - $selection->expects($this->any())->method('getProductId')->will($this->returnValue(1)); + $selection->method('getSku')->willReturn($childSku . '_invalid'); + $selection->method('getOptionId')->willReturn($optionId); + $selection->method('getSelectionId')->willReturn(55); + $selection->method('getProductId')->willReturn(1); - $this->option->expects($this->any())->method('getSelections')->will($this->returnValue([$selection])); + $this->option->method('getSelections') + ->willReturn([$selection]); $this->model->removeChild($productSku, $optionId, $childSku); } private function getOptions() { - $this->product->expects($this->any())->method('getTypeInstance')->will($this->returnValue($this->productType)); - $this->product->expects($this->once())->method('getStoreId')->will($this->returnValue($this->storeId)); - $this->productType->expects($this->once())->method('setStoreFilter') + $this->product->method('getTypeInstance') + ->willReturn($this->productType); + $this->product->expects($this->once()) + ->method('getStoreId') + ->willReturn($this->storeId); + $this->productType->expects($this->once()) + ->method('setStoreFilter') ->with($this->equalTo($this->storeId), $this->equalTo($this->product)); - $this->productType->expects($this->once())->method('getOptionsCollection') + $this->productType->expects($this->once()) + ->method('getOptionsCollection') ->with($this->equalTo($this->product)) - ->will($this->returnValue($this->optionCollection)); + ->willReturn($this->optionCollection); } public function getRemoveOptions() { - $this->product->expects($this->any())->method('getTypeInstance')->will($this->returnValue($this->productType)); - $this->product->expects($this->once())->method('getStoreId')->will($this->returnValue(1)); + $this->product->method('getTypeInstance') + ->willReturn($this->productType); + $this->product->expects($this->once()) + ->method('getStoreId') + ->willReturn(1); $this->productType->expects($this->once())->method('setStoreFilter'); $this->productType->expects($this->once())->method('getOptionsCollection') ->with($this->equalTo($this->product)) - ->will($this->returnValue($this->optionCollection)); + ->willReturn($this->optionCollection); - $this->productType->expects($this->once())->method('getOptionsIds')->with($this->equalTo($this->product)) - ->will($this->returnValue([1, 2, 3])); + $this->productType->expects($this->once()) + ->method('getOptionsIds') + ->with($this->equalTo($this->product)) + ->willReturn([1, 2, 3]); - $this->productType->expects($this->once())->method('getSelectionsCollection') - ->will($this->returnValue([])); + $this->productType->expects($this->once()) + ->method('getSelectionsCollection') + ->willReturn([]); - $this->optionCollection->expects($this->any())->method('appendSelections') + $this->optionCollection->method('appendSelections') ->with($this->equalTo([]), true) - ->will($this->returnValue([$this->option])); + ->willReturn([$this->option]); } } diff --git a/app/code/Magento/Bundle/Test/Unit/Model/Sales/Order/Pdf/Items/AbstractItemsTest.php b/app/code/Magento/Bundle/Test/Unit/Model/Sales/Order/Pdf/Items/AbstractItemsTest.php index 3e9aeaed5c5b4..3c4dbf2e85274 100644 --- a/app/code/Magento/Bundle/Test/Unit/Model/Sales/Order/Pdf/Items/AbstractItemsTest.php +++ b/app/code/Magento/Bundle/Test/Unit/Model/Sales/Order/Pdf/Items/AbstractItemsTest.php @@ -3,50 +3,71 @@ * Copyright © Magento, Inc. All rights reserved. * See COPYING.txt for license details. */ +declare(strict_types=1); + namespace Magento\Bundle\Test\Unit\Model\Sales\Order\Pdf\Items; -class AbstractItemsTest extends \PHPUnit\Framework\TestCase +use Magento\Bundle\Model\Sales\Order\Pdf\Items\Shipment; +use Magento\Framework\Serialize\Serializer\Json; +use Magento\Framework\TestFramework\Unit\Helper\ObjectManager; +use Magento\Sales\Model\Order\Creditmemo; +use Magento\Sales\Model\Order\Invoice; +use Magento\Sales\Model\Order\Item; +use PHPUnit\Framework\MockObject\MockObject; +use PHPUnit\Framework\TestCase; + +class AbstractItemsTest extends TestCase { - /** @var \Magento\Sales\Model\Order\Item|\PHPUnit_Framework_MockObject_MockObject */ - protected $orderItem; + /** + * @var Shipment + */ + private $model; - /** @var \Magento\Bundle\Model\Sales\Order\Pdf\Items\Shipment $model */ - protected $model; + /** + * @var Json|MockObject + */ + private $serializerMock; - /** @var \Magento\Framework\Serialize\Serializer\Json $serializer */ - protected $serializer; + /** + * @var Item|MockObject + */ + private $orderItemMock; protected function setUp() { - $this->orderItem = $this->createPartialMock( - \Magento\Sales\Model\Order\Item::class, + $this->orderItemMock = $this->createPartialMock( + Item::class, ['getProductOptions', '__wakeup', 'getParentItem', 'getOrderItem', 'getOrderItemId', 'getId'] ); - $objectManager = new \Magento\Framework\TestFramework\Unit\Helper\ObjectManager($this); - $this->serializer = $this->createMock(\Magento\Framework\Serialize\Serializer\Json::class); + $objectManager = new ObjectManager($this); + $this->serializerMock = $this->createMock(Json::class); $this->model = $objectManager->getObject( - \Magento\Bundle\Model\Sales\Order\Pdf\Items\Shipment::class, + Shipment::class, [ - 'serializer' => $this->serializer + 'serializer' => $this->serializerMock ] ); } /** * @dataProvider getChildrenEmptyItemsDataProvider + * + * @param string $class + * @param string $method + * @param string $returnClass */ public function testGetChildrenEmptyItems($class, $method, $returnClass) { $salesModel = $this->createPartialMock($returnClass, ['getAllItems', '__wakeup']); - $salesModel->expects($this->once())->method('getAllItems')->will($this->returnValue([])); + $salesModel->expects($this->once())->method('getAllItems')->willReturn([]); $item = $this->createPartialMock($class, [$method, 'getOrderItem', '__wakeup']); - $item->expects($this->once())->method($method)->will($this->returnValue($salesModel)); - $item->expects($this->once())->method('getOrderItem')->will($this->returnValue($this->orderItem)); - $this->orderItem->expects($this->any())->method('getId')->will($this->returnValue(1)); + $item->expects($this->once())->method($method)->willReturn($salesModel); + $item->expects($this->once())->method('getOrderItem')->willReturn($this->orderItemMock); + $this->orderItemMock->method('getId')->willReturn(1); - $this->assertSame(null, $this->model->getChildren($item)); + $this->assertNull($this->model->getChildren($item)); } /** @@ -56,9 +77,9 @@ public function getChildrenEmptyItemsDataProvider() { return [ [ - \Magento\Sales\Model\Order\Invoice\Item::class, + Invoice\Item::class, 'getInvoice', - \Magento\Sales\Model\Order\Invoice::class + Invoice::class ], [ \Magento\Sales\Model\Order\Shipment\Item::class, @@ -66,38 +87,40 @@ public function getChildrenEmptyItemsDataProvider() \Magento\Sales\Model\Order\Shipment::class ], [ - \Magento\Sales\Model\Order\Creditmemo\Item::class, + Creditmemo\Item::class, 'getCreditmemo', - \Magento\Sales\Model\Order\Creditmemo::class + Creditmemo::class ] ]; } /** * @dataProvider getChildrenDataProvider + * + * @param bool $parentItem */ public function testGetChildren($parentItem) { if ($parentItem) { - $parentItem = $this->createPartialMock(\Magento\Sales\Model\Order\Item::class, ['getId', '__wakeup']); - $parentItem->expects($this->any())->method('getId')->will($this->returnValue(1)); + $parentItem = $this->createPartialMock(Item::class, ['getId', '__wakeup']); + $parentItem->method('getId')->willReturn(1); } - $this->orderItem->expects($this->any())->method('getOrderItem')->will($this->returnSelf()); - $this->orderItem->expects($this->any())->method('getParentItem')->will($this->returnValue($parentItem)); - $this->orderItem->expects($this->any())->method('getOrderItemId')->will($this->returnValue(2)); - $this->orderItem->expects($this->any())->method('getId')->will($this->returnValue(1)); + $this->orderItemMock->method('getOrderItem')->willReturnSelf(); + $this->orderItemMock->method('getParentItem')->willReturn($parentItem); + $this->orderItemMock->method('getOrderItemId')->willReturn(2); + $this->orderItemMock->method('getId')->willReturn(1); - $salesModel = $this->createPartialMock(\Magento\Sales\Model\Order\Invoice::class, ['getAllItems', '__wakeup']); - $salesModel->expects($this->once())->method('getAllItems')->will($this->returnValue([$this->orderItem])); + $salesModel = $this->createPartialMock(Invoice::class, ['getAllItems', '__wakeup']); + $salesModel->expects($this->once())->method('getAllItems')->willReturn([$this->orderItemMock]); $item = $this->createPartialMock( - \Magento\Sales\Model\Order\Invoice\Item::class, + Invoice\Item::class, ['getInvoice', 'getOrderItem', '__wakeup'] ); - $item->expects($this->once())->method('getInvoice')->will($this->returnValue($salesModel)); - $item->expects($this->any())->method('getOrderItem')->will($this->returnValue($this->orderItem)); + $item->expects($this->once())->method('getInvoice')->willReturn($salesModel); + $item->method('getOrderItem')->willReturn($this->orderItemMock); - $this->assertSame([2 => $this->orderItem], $this->model->getChildren($item)); + $this->assertSame([2 => $this->orderItemMock], $this->model->getChildren($item)); } /** @@ -113,11 +136,14 @@ public function getChildrenDataProvider() /** * @dataProvider isShipmentSeparatelyWithoutItemDataProvider + * + * @param array $productOptions + * @param bool $result */ public function testIsShipmentSeparatelyWithoutItem($productOptions, $result) { - $this->model->setItem($this->orderItem); - $this->orderItem->expects($this->any())->method('getProductOptions')->will($this->returnValue($productOptions)); + $this->model->setItem($this->orderItemMock); + $this->orderItemMock->method('getProductOptions')->willReturn($productOptions); $this->assertSame($result, $this->model->isShipmentSeparately()); } @@ -136,23 +162,27 @@ public function isShipmentSeparatelyWithoutItemDataProvider() /** * @dataProvider isShipmentSeparatelyWithItemDataProvider + * + * @param array $productOptions + * @param bool $result + * @param bool $parentItem */ public function testIsShipmentSeparatelyWithItem($productOptions, $result, $parentItem) { if ($parentItem) { $parentItem = $this->createPartialMock( - \Magento\Sales\Model\Order\Item::class, + Item::class, ['getProductOptions', '__wakeup'] ); - $parentItem->expects($this->any())->method('getProductOptions')->will($this->returnValue($productOptions)); + $parentItem->method('getProductOptions')->willReturn($productOptions); } else { - $this->orderItem->expects($this->any())->method('getProductOptions') - ->will($this->returnValue($productOptions)); + $this->orderItemMock->method('getProductOptions') + ->willReturn($productOptions); } - $this->orderItem->expects($this->any())->method('getParentItem')->will($this->returnValue($parentItem)); - $this->orderItem->expects($this->any())->method('getOrderItem')->will($this->returnSelf()); + $this->orderItemMock->method('getParentItem')->willReturn($parentItem); + $this->orderItemMock->method('getOrderItem')->willReturnSelf(); - $this->assertSame($result, $this->model->isShipmentSeparately($this->orderItem)); + $this->assertSame($result, $this->model->isShipmentSeparately($this->orderItemMock)); } /** @@ -170,11 +200,14 @@ public function isShipmentSeparatelyWithItemDataProvider() /** * @dataProvider isChildCalculatedWithoutItemDataProvider + * + * @param array $productOptions + * @param bool $result */ public function testIsChildCalculatedWithoutItem($productOptions, $result) { - $this->model->setItem($this->orderItem); - $this->orderItem->expects($this->any())->method('getProductOptions')->will($this->returnValue($productOptions)); + $this->model->setItem($this->orderItemMock); + $this->orderItemMock->method('getProductOptions')->willReturn($productOptions); $this->assertSame($result, $this->model->isChildCalculated()); } @@ -193,23 +226,27 @@ public function isChildCalculatedWithoutItemDataProvider() /** * @dataProvider isChildCalculatedWithItemDataProvider + * + * @param array $productOptions + * @param bool $result + * @param bool $parentItem */ public function testIsChildCalculatedWithItem($productOptions, $result, $parentItem) { if ($parentItem) { $parentItem = $this->createPartialMock( - \Magento\Sales\Model\Order\Item::class, + Item::class, ['getProductOptions', '__wakeup'] ); - $parentItem->expects($this->any())->method('getProductOptions')->will($this->returnValue($productOptions)); + $parentItem->method('getProductOptions')->willReturn($productOptions); } else { - $this->orderItem->expects($this->any())->method('getProductOptions') - ->will($this->returnValue($productOptions)); + $this->orderItemMock->method('getProductOptions') + ->willReturn($productOptions); } - $this->orderItem->expects($this->any())->method('getParentItem')->will($this->returnValue($parentItem)); - $this->orderItem->expects($this->any())->method('getOrderItem')->will($this->returnSelf()); + $this->orderItemMock->method('getParentItem')->willReturn($parentItem); + $this->orderItemMock->method('getOrderItem')->willReturnSelf(); - $this->assertSame($result, $this->model->isChildCalculated($this->orderItem)); + $this->assertSame($result, $this->model->isChildCalculated($this->orderItemMock)); } /** @@ -227,11 +264,13 @@ public function isChildCalculatedWithItemDataProvider() /** * @dataProvider getBundleOptionsDataProvider + * @param array $productOptions + * @param array|string $result */ public function testGetBundleOptions($productOptions, $result) { - $this->model->setItem($this->orderItem); - $this->orderItem->expects($this->any())->method('getProductOptions')->will($this->returnValue($productOptions)); + $this->model->setItem($this->orderItemMock); + $this->orderItemMock->method('getProductOptions')->willReturn($productOptions); $this->assertSame($result, $this->model->getBundleOptions()); } @@ -248,8 +287,8 @@ public function getBundleOptionsDataProvider() public function testGetSelectionAttributes() { - $this->orderItem->expects($this->any())->method('getProductOptions')->will($this->returnValue([])); - $this->assertNull($this->model->getSelectionAttributes($this->orderItem)); + $this->orderItemMock->method('getProductOptions')->willReturn([]); + $this->assertNull($this->model->getSelectionAttributes($this->orderItemMock)); } public function testGetSelectionAttributesWithBundle() @@ -258,13 +297,12 @@ public function testGetSelectionAttributesWithBundle() $options = ['bundle_selection_attributes' => $bundleAttributes]; $unserializedResult = 'result of "bundle_selection_attributes" unserialization'; - $this->serializer->expects($this->any()) - ->method('unserialize') + $this->serializerMock->method('unserialize') ->with($bundleAttributes) - ->will($this->returnValue($unserializedResult)); - $this->orderItem->expects($this->any())->method('getProductOptions')->will($this->returnValue($options)); + ->willReturn($unserializedResult); + $this->orderItemMock->method('getProductOptions')->willReturn($options); - $this->assertEquals($unserializedResult, $this->model->getSelectionAttributes($this->orderItem)); + $this->assertEquals($unserializedResult, $this->model->getSelectionAttributes($this->orderItemMock)); } public function testGetOrderOptions() @@ -274,28 +312,32 @@ public function testGetOrderOptions() 'additional_options' => ['additional_options'], 'attributes_info' => ['attributes_info'], ]; - $this->model->setItem($this->orderItem); - $this->orderItem->expects($this->any())->method('getProductOptions')->will($this->returnValue($productOptions)); + $this->model->setItem($this->orderItemMock); + $this->orderItemMock->method('getProductOptions')->willReturn($productOptions); $this->assertEquals(['attributes_info', 'options', 'additional_options'], $this->model->getOrderOptions()); } public function testGetOrderItem() { - $this->model->setItem($this->orderItem); - $this->assertSame($this->orderItem, $this->model->getOrderItem()); + $this->model->setItem($this->orderItemMock); + $this->assertSame($this->orderItemMock, $this->model->getOrderItem()); } /** * @dataProvider canShowPriceInfoDataProvider + * + * @param bool $parentItem + * @param array $productOptions + * @param bool $result */ public function testCanShowPriceInfo($parentItem, $productOptions, $result) { - $this->model->setItem($this->orderItem); - $this->orderItem->expects($this->any())->method('getOrderItem')->will($this->returnSelf()); - $this->orderItem->expects($this->any())->method('getParentItem')->will($this->returnValue($parentItem)); - $this->orderItem->expects($this->any())->method('getProductOptions')->will($this->returnValue($productOptions)); + $this->model->setItem($this->orderItemMock); + $this->orderItemMock->method('getOrderItem')->willReturnSelf(); + $this->orderItemMock->method('getParentItem')->willReturn($parentItem); + $this->orderItemMock->method('getProductOptions')->willReturn($productOptions); - $this->assertSame($result, $this->model->canShowPriceInfo($this->orderItem)); + $this->assertSame($result, $this->model->canShowPriceInfo($this->orderItemMock)); } /** diff --git a/app/code/Magento/Bundle/Test/Unit/Observer/AppendUpsellProductsObserverTest.php b/app/code/Magento/Bundle/Test/Unit/Observer/AppendUpsellProductsObserverTest.php new file mode 100644 index 0000000000000..d1058ff56270c --- /dev/null +++ b/app/code/Magento/Bundle/Test/Unit/Observer/AppendUpsellProductsObserverTest.php @@ -0,0 +1,410 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +namespace Magento\Bundle\Test\Unit\Observer; + +use Magento\Bundle\Helper\Data as BundleHelper; +use Magento\Bundle\Model\ResourceModel\Selection; +use Magento\Bundle\Observer\AppendUpsellProductsObserver; +use Magento\Catalog\Model\Config as CatalogConfig; +use Magento\Catalog\Model\Product; +use Magento\Catalog\Model\Product\Type as ProductType; +use Magento\Catalog\Model\Product\Visibility; +use Magento\Catalog\Model\ResourceModel\Product\Collection as ProductCollection; +use Magento\Catalog\Model\ResourceModel\Product\Link\Product\Collection as ProductLinkCollection; +use Magento\Framework\Event; +use Magento\Framework\Event\Observer; +use Magento\Framework\TestFramework\Unit\Helper\ObjectManager; +use Magento\GroupedProduct\Model\Product\Type\Grouped; +use PHPUnit\Framework\MockObject\MockObject; +use PHPUnit\Framework\TestCase; + +/** + * Test class for \Magento\Bundle\Observer\AppendUpsellProductsObserver + * + * @SuppressWarnings(PHPMD.CouplingBetweenObjects) + */ +class AppendUpsellProductsObserverTest extends TestCase +{ + /** + * @var ProductCollection|MockObject + */ + private $bundleCollectionMock; + + /** + * @var BundleHelper|MockObject + */ + private $bundleDataMock; + + /** + * @var Selection|MockObject + */ + private $bundleSelectionMock; + + /** + * @var CatalogConfig|MockObject + */ + private $configMock; + + /** + * @var Event|MockObject + */ + private $eventMock; + + /** + * Object Manager Instance + * + * @var ObjectManager + */ + private $objectManager; + + /** + * Testable Object + * + * @var AppendUpsellProductsObserver + */ + private $observer; + + /** + * @var Observer|MockObject + */ + private $observerMock; + + /** + * @var ProductLinkCollection|MockObject + */ + private $collectionMock; + + /** + * @var Product|MockObject + */ + private $productMock; + + /** + * @var Visibility|MockObject + */ + private $productVisibilityMock; + + /** + * @inheritdoc + */ + protected function setUp() : void + { + $this->objectManager = new ObjectManager($this); + $this->observerMock = $this->createMock(Observer::class); + + $this->bundleCollectionMock = $this->getMockBuilder(ProductCollection::class) + ->disableOriginalConstructor() + ->setMethods([ + 'addAttributeToSelect', + 'addFieldToFilter', + 'addFinalPrice', + 'addMinimalPrice', + 'addStoreFilter', + 'addTaxPercents', + 'load', + 'setFlag', + 'setPageSize', + 'setVisibility' + ]) + ->getMock(); + + $this->bundleDataMock = $this->getMockBuilder(BundleHelper::class) + ->disableOriginalConstructor() + ->setMethods(['getAllowedSelectionTypes']) + ->getMock(); + + $this->bundleSelectionMock = $this->getMockBuilder(Selection::class) + ->disableOriginalConstructor() + ->setMethods(['getParentIdsByChild']) + ->getMock(); + + $this->configMock = $this->getMockBuilder(CatalogConfig::class) + ->disableOriginalConstructor() + ->setMethods(['getProductAttributes']) + ->getMock(); + + $this->eventMock = $this->getMockBuilder(Event::class) + ->disableOriginalConstructor() + ->setMethods(['getProduct', 'getCollection', 'getLimit']) + ->getMock(); + + $this->collectionMock = $this->getMockBuilder(Event::class) + ->disableOriginalConstructor() + ->setMethods(['setItems', 'getItems']) + ->getMock(); + + $this->productMock = $this->getMockBuilder(Product::class) + ->disableOriginalConstructor() + ->setMethods(['getCollection', 'getId', 'getTypeId']) + ->getMock(); + + $this->productVisibilityMock = $this->getMockBuilder(Visibility::class) + ->disableOriginalConstructor() + ->setMethods(['getVisibleInCatalogIds']) + ->getMock(); + + $this->observer = $this->objectManager->getObject( + AppendUpsellProductsObserver::class, + [ + 'bundleData' => $this->bundleDataMock, + 'productVisibility' => $this->productVisibilityMock, + 'config' => $this->configMock, + 'bundleSelection' => $this->bundleSelectionMock, + ] + ); + } + + /** + * Test observer execute method + */ + public function testAddBundleCollectionItemsToEventCollection() + { + $collectionItems = [ + 1 => 1 + ]; + $limit = 2; + $parentIds = [1, 3]; + $productId = 2; + $productAttributes = ['attribute1', 'attribute2']; + $visibleInCatalogIds = [10, 11, 12]; + + $this->observerMock + ->expects($this->exactly(3)) + ->method('getEvent') + ->willReturn($this->eventMock); + + $this->eventMock + ->expects($this->once()) + ->method('getProduct') + ->willReturn($this->productMock); + + $this->bundleDataMock + ->expects($this->once()) + ->method('getAllowedSelectionTypes') + ->willReturn($this->getAllowedSelectionTypes()); + + $this->productMock + ->expects($this->once()) + ->method('getTypeId') + ->willReturn(ProductType::TYPE_SIMPLE); + + $this->eventMock + ->expects($this->once()) + ->method('getCollection') + ->willReturn($this->collectionMock); + + $this->eventMock + ->expects($this->once()) + ->method('getLimit') + ->willReturn($limit); + + $this->collectionMock + ->expects($this->exactly(2)) + ->method('getItems') + ->willReturn($collectionItems); + + $this->productMock + ->expects($this->once()) + ->method('getId') + ->willReturn($productId); + + $this->bundleSelectionMock + ->expects($this->once()) + ->method('getParentIdsByChild') + ->willReturn($parentIds); + + $this->productMock + ->expects($this->once()) + ->method('getCollection') + ->willReturn($this->bundleCollectionMock); + + $this->bundleCollectionMock + ->expects($this->once()) + ->method('addAttributeToSelect') + ->willReturn($this->bundleCollectionMock); + + $this->configMock + ->expects($this->once()) + ->method('getProductAttributes') + ->willReturn($productAttributes); + + $this->productVisibilityMock + ->expects($this->once()) + ->method('getVisibleInCatalogIds') + ->willReturn($visibleInCatalogIds); + + $this->bundleCollectionMock + ->expects($this->once()) + ->method('setPageSize') + ->willReturn($this->bundleCollectionMock); + + $this->bundleCollectionMock + ->expects($this->once()) + ->method('addFieldToFilter') + ->willReturn($this->bundleCollectionMock); + + $this->bundleCollectionMock + ->expects($this->once()) + ->method('setFlag') + ->willReturn($this->bundleCollectionMock); + + $this->collectionMock + ->expects($this->once()) + ->method('setItems') + ->willReturn($collectionItems); + + $this->observer->execute($this->observerMock); + } + + /** + * Test observer when collection contains a parent product of a current product + */ + public function testObserverWithoutBundleIds() + { + $collectionItems = [ + 1 => 1 + ]; + $parentIds = [1]; + $limit = 2; + $productId = 2; + + $this->observerMock + ->expects($this->exactly(3)) + ->method('getEvent') + ->willReturn($this->eventMock); + + $this->eventMock + ->expects($this->once()) + ->method('getProduct') + ->willReturn($this->productMock); + + $this->bundleDataMock + ->expects($this->once()) + ->method('getAllowedSelectionTypes') + ->willReturn($this->getAllowedSelectionTypes()); + + $this->productMock + ->expects($this->once()) + ->method('getTypeId') + ->willReturn(ProductType::TYPE_SIMPLE); + + $this->eventMock + ->expects($this->once()) + ->method('getCollection') + ->willReturn($this->collectionMock); + + $this->eventMock + ->expects($this->once()) + ->method('getLimit') + ->willReturn($limit); + + $this->collectionMock + ->expects($this->once()) + ->method('getItems') + ->willReturn($collectionItems); + + $this->productMock + ->expects($this->once()) + ->method('getId') + ->willReturn($productId); + + $this->bundleSelectionMock + ->expects($this->once()) + ->method('getParentIdsByChild') + ->willReturn($parentIds); + + $this->observer->execute($this->observerMock); + } + + /** + * Test observer when count of products is equal to limit. + */ + public function testObserverWithoutLinkedProducts() + { + $collectionItems = [ + 1 => 1 + ]; + $limit = 1; + + $this->observerMock + ->expects($this->exactly(3)) + ->method('getEvent') + ->willReturn($this->eventMock); + + $this->eventMock + ->expects($this->once()) + ->method('getProduct') + ->willReturn($this->productMock); + + $this->bundleDataMock + ->expects($this->once()) + ->method('getAllowedSelectionTypes') + ->willReturn($this->getAllowedSelectionTypes()); + + $this->productMock + ->expects($this->once()) + ->method('getTypeId') + ->willReturn(ProductType::TYPE_SIMPLE); + + $this->eventMock + ->expects($this->once()) + ->method('getCollection') + ->willReturn($this->collectionMock); + + $this->eventMock + ->expects($this->once()) + ->method('getLimit') + ->willReturn($limit); + + $this->collectionMock + ->expects($this->once()) + ->method('getItems') + ->willReturn($collectionItems); + + $this->observer->execute($this->observerMock); + } + + /** + * Test observer when a current product type is allowed for bundle selection + */ + public function testCurrentProductIsNotAllowedForBundleSelection() + { + $this->bundleDataMock + ->expects($this->once()) + ->method('getAllowedSelectionTypes') + ->willReturn($this->getAllowedSelectionTypes()); + + $this->eventMock + ->expects($this->once()) + ->method('getProduct') + ->willReturn($this->productMock); + + $this->observerMock + ->expects($this->once()) + ->method('getEvent') + ->willReturn($this->eventMock); + + $this->productMock + ->expects($this->once()) + ->method('getTypeId') + ->willReturn(Grouped::TYPE_CODE); + + $this->observer->execute($this->observerMock); + } + + /** + * Returns allowed products types + * + * @return array + */ + private function getAllowedSelectionTypes(): array + { + return [ + 'simple' => ProductType::TYPE_SIMPLE, + 'virtual' => ProductType::TYPE_VIRTUAL, + ]; + } +} diff --git a/app/code/Magento/Bundle/Test/Unit/Observer/InitOptionRendererObserverTest.php b/app/code/Magento/Bundle/Test/Unit/Observer/InitOptionRendererObserverTest.php new file mode 100644 index 0000000000000..78f1d97e888c7 --- /dev/null +++ b/app/code/Magento/Bundle/Test/Unit/Observer/InitOptionRendererObserverTest.php @@ -0,0 +1,81 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ + +namespace Magento\Bundle\Test\Unit\Observer; + +use Magento\Bundle\Observer\InitOptionRendererObserver; +use Magento\Framework\Event\Observer; +use Magento\Framework\TestFramework\Unit\Helper\ObjectManager; +use Magento\Wishlist\Block\Customer\Wishlist\Item\Options; +use PHPUnit\Framework\MockObject\MockObject; +use PHPUnit\Framework\TestCase; + +/** + * Test class for \Magento\Bundle\Observer\InitOptionRendererObserver + */ +class InitOptionRendererObserverTest extends TestCase +{ + /** + * @var Options|MockObject + */ + private $blockMock; + + /** + * Object Manager Instance + * + * @var ObjectManager + */ + private $objectManager; + + /** + * Testable Object + * + * @var InitOptionRendererObserver + */ + private $observer; + + /** + * @var Observer|MockObject + */ + private $observerMock; + + /** + * @inheritdoc + */ + protected function setUp() : void + { + $this->objectManager = new ObjectManager($this); + $this->observerMock = $this->getMockBuilder(Observer::class) + ->disableOriginalConstructor() + ->setMethods(['getBlock']) + ->getMock(); + + $this->blockMock = $this->getMockBuilder(Options::class) + ->disableOriginalConstructor() + ->setMethods(['addOptionsRenderCfg']) + ->getMock(); + + $this->observer = $this->objectManager->getObject(InitOptionRendererObserver::class); + } + + /** + * Test observer execute method + */ + public function testProductOptionRendererInit() + { + $this->observerMock + ->expects($this->once()) + ->method('getBlock') + ->willReturn($this->blockMock); + + $this->blockMock + ->expects($this->once()) + ->method('addOptionsRenderCfg') + ->willReturn($this->blockMock); + + $this->observer->execute($this->observerMock); + } +} diff --git a/app/code/Magento/Bundle/Test/Unit/Observer/SetAttributeTabBlockObserverTest.php b/app/code/Magento/Bundle/Test/Unit/Observer/SetAttributeTabBlockObserverTest.php new file mode 100644 index 0000000000000..08f6a05bd10bf --- /dev/null +++ b/app/code/Magento/Bundle/Test/Unit/Observer/SetAttributeTabBlockObserverTest.php @@ -0,0 +1,117 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +declare(strict_types=1); + +namespace Magento\Bundle\Test\Unit\Observer; + +use Magento\Bundle\Block\Adminhtml\Catalog\Product\Edit\Tab\Attributes; +use Magento\Bundle\Observer\SetAttributeTabBlockObserver; +use Magento\Catalog\Helper\Catalog; +use Magento\Catalog\Model\Product; +use Magento\Catalog\Model\Product\Type; +use Magento\Framework\Event; +use Magento\Framework\Event\Observer; +use Magento\Framework\TestFramework\Unit\Helper\ObjectManager; +use PHPUnit\Framework\MockObject\MockObject; +use PHPUnit\Framework\TestCase; + +/** + * Class SetAttributeTabBlockObserverTest + * + * Test setting attribute tab block for bundle products + */ +class SetAttributeTabBlockObserverTest extends TestCase +{ + /** + * @var SetAttributeTabBlockObserver + */ + private $observer; + + /** + * @var Catalog|MockObject + */ + private $helperCatalogMock; + + /** + * @var Observer|MockObject + */ + private $observerMock; + + /** + * @var Event|MockObject + */ + private $eventMock; + + /** + * @var Product|MockObject + */ + private $productMock; + + /** + * Set Up + */ + public function setUp() + { + $objectManager = new ObjectManager($this); + $this->helperCatalogMock = $this->createMock(Catalog::class); + $this->observerMock = $this->createMock(Observer::class); + $this->eventMock = $this->getMockBuilder(Event::class) + ->disableOriginalConstructor() + ->setMethods(['getProduct']) + ->getMock(); + $this->productMock = $this->getMockBuilder(Product::class) + ->disableOriginalConstructor() + ->getMock(); + + $this->observer = $objectManager->getObject( + SetAttributeTabBlockObserver::class, + [ + 'helperCatalog' => $this->helperCatalogMock + ] + ); + } + + /** + * Test setting attribute tab block for bundle product + */ + public function testAddingAttributeTabForBundleProduct() + { + $this->productMock->expects($this->any()) + ->method('getTypeId') + ->willReturn(Type::TYPE_BUNDLE); + $this->eventMock->expects($this->any()) + ->method('getProduct') + ->willReturn($this->productMock); + $this->observerMock->expects($this->any()) + ->method('getEvent') + ->willReturn($this->eventMock); + $this->helperCatalogMock->expects($this->once()) + ->method('setAttributeTabBlock') + ->with(Attributes::class); + + $this->observer->execute($this->observerMock); + } + + /** + * Test setting attribute tab block for a non bundle product + */ + public function testAddingAttributeTabForNonBundleProduct() + { + $this->productMock->expects($this->any()) + ->method('getTypeId') + ->willReturn(Type::TYPE_VIRTUAL); + $this->eventMock->expects($this->any()) + ->method('getProduct') + ->willReturn($this->productMock); + $this->observerMock->expects($this->any()) + ->method('getEvent') + ->willReturn($this->eventMock); + $this->helperCatalogMock->expects($this->never()) + ->method('setAttributeTabBlock'); + + $this->observer->execute($this->observerMock); + } +} diff --git a/app/code/Magento/Bundle/Test/Unit/Pricing/Price/BundleOptionsTest.php b/app/code/Magento/Bundle/Test/Unit/Pricing/Price/BundleOptionsTest.php index 37973b9b8ae28..d795af793675b 100644 --- a/app/code/Magento/Bundle/Test/Unit/Pricing/Price/BundleOptionsTest.php +++ b/app/code/Magento/Bundle/Test/Unit/Pricing/Price/BundleOptionsTest.php @@ -7,23 +7,24 @@ namespace Magento\Bundle\Test\Unit\Pricing\Price; -use Magento\Framework\TestFramework\Unit\Helper\ObjectManager as ObjectManagerHelper; -use PHPUnit_Framework_MockObject_MockObject as MockObject; -use Magento\Framework\Pricing\Amount\AmountFactory; -use Magento\Framework\Pricing\Adjustment\Calculator as AdjustmentCalculator; -use Magento\Framework\Pricing\PriceInfo\Base as BasePriceInfo; -use Magento\Framework\Pricing\PriceCurrencyInterface; -use Magento\Framework\Pricing\Amount\AmountInterface; -use Magento\Framework\Pricing\Amount\Base as BaseAmount; -use Magento\Bundle\Pricing\Price\BundleOptions; -use Magento\Bundle\Pricing\Price\BundleSelectionPrice; -use Magento\Bundle\Pricing\Price\BundleSelectionFactory; -use Magento\Bundle\Pricing\Adjustment\Calculator as BundleAdjustmentCalculator; use Magento\Bundle\Model\Option as BundleOption; use Magento\Bundle\Model\Product\Type as BundleProductType; use Magento\Bundle\Model\ResourceModel\Option\Collection as BundleOptionCollection; +use Magento\Bundle\Pricing\Adjustment\Calculator as BundleAdjustmentCalculator; +use Magento\Bundle\Pricing\Adjustment\SelectionPriceListProviderInterface; +use Magento\Bundle\Pricing\Price\BundleOptions; +use Magento\Bundle\Pricing\Price\BundleSelectionFactory; +use Magento\Bundle\Pricing\Price\BundleSelectionPrice; use Magento\Catalog\Model\Product; +use Magento\Framework\Pricing\Adjustment\Calculator as AdjustmentCalculator; +use Magento\Framework\Pricing\Amount\AmountFactory; +use Magento\Framework\Pricing\Amount\AmountInterface; +use Magento\Framework\Pricing\Amount\Base as BaseAmount; +use Magento\Framework\Pricing\PriceCurrencyInterface; +use Magento\Framework\Pricing\PriceInfo\Base as BasePriceInfo; +use Magento\Framework\TestFramework\Unit\Helper\ObjectManager as ObjectManagerHelper; use Magento\Tax\Helper\Data as TaxHelperData; +use PHPUnit_Framework_MockObject_MockObject as MockObject; /** * Test for Magento\Bundle\Pricing\Price\BundleOptions @@ -71,6 +72,11 @@ class BundleOptionsTest extends \PHPUnit\Framework\TestCase */ private $priceInfoMock; + /** + * @var SelectionPriceListProviderInterface + */ + private $selectionPriceListProviderMock; + protected function setUp() { $this->priceInfoMock = $this->getMockBuilder(BasePriceInfo::class) @@ -102,9 +108,20 @@ function ($fullAmount, $adjustments) { ->disableOriginalConstructor() ->getMock(); + $this->selectionPriceListProviderMock = $this->getMockBuilder(SelectionPriceListProviderInterface::class) + ->disableOriginalConstructor() + ->getMock(); + $this->bundleCalculatorMock = $this->getMockBuilder(BundleAdjustmentCalculator::class) ->setConstructorArgs( - [$this->baseCalculator, $this->amountFactory, $this->selectionFactoryMock, $taxData, $priceCurrency] + [ + $this->baseCalculator, + $this->amountFactory, + $this->selectionFactoryMock, + $taxData, + $priceCurrency, + $this->selectionPriceListProviderMock + ] ) ->setMethods(['getOptionsAmount']) ->getMock(); diff --git a/app/code/Magento/Bundle/Test/Unit/Pricing/Price/FinalPriceTest.php b/app/code/Magento/Bundle/Test/Unit/Pricing/Price/FinalPriceTest.php index 4463709391102..d21971b821691 100644 --- a/app/code/Magento/Bundle/Test/Unit/Pricing/Price/FinalPriceTest.php +++ b/app/code/Magento/Bundle/Test/Unit/Pricing/Price/FinalPriceTest.php @@ -3,121 +3,156 @@ * Copyright © Magento, Inc. All rights reserved. * See COPYING.txt for license details. */ +declare(strict_types=1); namespace Magento\Bundle\Test\Unit\Pricing\Price; +use Magento\Bundle\Pricing\Adjustment\BundleCalculatorInterface; use Magento\Bundle\Pricing\Price\BundleOptionPrice; +use Magento\Bundle\Pricing\Price\FinalPrice; +use Magento\Catalog\Api\Data\ProductCustomOptionInterface; +use Magento\Catalog\Model\Product; +use Magento\Catalog\Pricing\Price\BasePrice; use Magento\Catalog\Pricing\Price\CustomOptionPrice; use Magento\Bundle\Model\Product\Price; use Magento\Catalog\Api\ProductCustomOptionRepositoryInterface; use Magento\Framework\Pricing\PriceCurrencyInterface; +use Magento\Framework\Pricing\PriceInfo\Base; +use Magento\Framework\Pricing\SaleableInterface; use Magento\Framework\TestFramework\Unit\Helper\ObjectManager as ObjectManagerHelper; +use PHPUnit\Framework\MockObject\MockObject; +use PHPUnit\Framework\TestCase; /** - * @SuppressWarnings(PHPMD) + * Test class for \Magento\Bundle\Pricing\Price\FinalPrice + * + * @SuppressWarnings(PHPMD.CouplingBetweenObjects) */ -class FinalPriceTest extends \PHPUnit\Framework\TestCase +class FinalPriceTest extends TestCase { - /** @var \Magento\Bundle\Pricing\Price\FinalPrice */ - protected $finalPrice; + /** + * @var FinalPrice + */ + private $finalPrice; - /** @var ObjectManagerHelper */ - protected $objectManagerHelper; + /** + * @var ObjectManagerHelper + */ + private $objectManagerHelper; - /** @var \Magento\Framework\Pricing\SaleableInterface|\PHPUnit_Framework_MockObject_MockObject */ - protected $saleableInterfaceMock; + /** + * @var SaleableInterface|MockObject + */ + private $saleableInterfaceMock; - /** @var float */ - protected $quantity = 1.; + /** + * @var float + */ + private $quantity = 1.; - /** @var float*/ - protected $baseAmount; + /** + * @var BundleCalculatorInterface|MockObject + */ + private $bundleCalculatorMock; - /** @var \Magento\Bundle\Pricing\Adjustment\BundleCalculatorInterface|\PHPUnit_Framework_MockObject_MockObject */ - protected $bundleCalculatorMock; + /** + * @var PriceCurrencyInterface|MockObject + */ + private $priceCurrencyMock; - /** @var \Magento\Framework\Pricing\PriceInfo\Base |\PHPUnit_Framework_MockObject_MockObject */ - protected $priceInfoMock; + /** + * @var ProductCustomOptionRepositoryInterface|MockObject + */ + private $productOptionRepositoryMock; - /** @var \Magento\Catalog\Pricing\Price\BasePrice|\PHPUnit_Framework_MockObject_MockObject */ - protected $basePriceMock; + /** + * @var float + */ + private $baseAmount; - /** @var BundleOptionPrice|\PHPUnit_Framework_MockObject_MockObject */ - protected $bundleOptionMock; + /** + * @var Base|MockObject + */ + private $priceInfoMock; - /** @var CustomOptionPrice|\PHPUnit_Framework_MockObject_MockObject */ - protected $customOptionPriceMock; + /** + * @var BasePrice|MockObject + */ + private $basePriceMock; /** - * @var PriceCurrencyInterface|\PHPUnit_Framework_MockObject_MockObject + * @var BundleOptionPrice|MockObject */ - protected $priceCurrencyMock; + private $bundleOptionMock; /** - * @var ProductCustomOptionRepositoryInterface|\PHPUnit_Framework_MockObject_MockObject + * @var CustomOptionPrice|MockObject */ - private $productOptionRepositoryMock; + private $customOptionPriceMock; /** * @return void */ protected function prepareMock() { - $this->saleableInterfaceMock = $this->getMockBuilder(\Magento\Catalog\Model\Product::class) + $this->saleableInterfaceMock = $this->getMockBuilder(Product::class) ->disableOriginalConstructor() ->setMethods(['getPriceType', 'getPriceInfo']) ->getMock(); $this->bundleCalculatorMock = $this->createMock( - \Magento\Bundle\Pricing\Adjustment\BundleCalculatorInterface::class + BundleCalculatorInterface::class ); - $this->basePriceMock = $this->createMock(\Magento\Catalog\Pricing\Price\BasePrice::class); - $this->basePriceMock->expects($this->any()) - ->method('getValue') - ->will($this->returnValue($this->baseAmount)); + $this->basePriceMock = $this->createMock(BasePrice::class); + $this->basePriceMock->method('getValue') + ->willReturn($this->baseAmount); - $this->bundleOptionMock = $this->getMockBuilder(\Magento\Bundle\Pricing\Price\BundleOptionPrice::class) + $this->bundleOptionMock = $this->getMockBuilder(BundleOptionPrice::class) ->disableOriginalConstructor() ->getMock(); - $this->customOptionPriceMock = $this->getMockBuilder(\Magento\Catalog\Pricing\Price\CustomOptionPrice::class) + $this->customOptionPriceMock = $this->getMockBuilder(CustomOptionPrice::class) ->disableOriginalConstructor() ->getMock(); - $this->priceInfoMock = $this->createMock(\Magento\Framework\Pricing\PriceInfo\Base::class); + $this->priceInfoMock = $this->createMock(Base::class); $this->priceInfoMock->expects($this->atLeastOnce()) ->method('getPrice') - ->will($this->returnValueMap([ - [\Magento\Catalog\Pricing\Price\BasePrice::PRICE_CODE, $this->basePriceMock], - [BundleOptionPrice::PRICE_CODE, $this->bundleOptionMock], - [CustomOptionPrice::PRICE_CODE, $this->customOptionPriceMock], - ])); + ->willReturnMap( + [ + [BasePrice::PRICE_CODE, $this->basePriceMock], + [BundleOptionPrice::PRICE_CODE, $this->bundleOptionMock], + [CustomOptionPrice::PRICE_CODE, $this->customOptionPriceMock], + ] + ); $this->saleableInterfaceMock->expects($this->once()) ->method('getPriceInfo') - ->will($this->returnValue($this->priceInfoMock)); - - $this->priceCurrencyMock = $this->createMock(\Magento\Framework\Pricing\PriceCurrencyInterface::class); - - $this->objectManagerHelper = new ObjectManagerHelper($this); - $this->finalPrice = new \Magento\Bundle\Pricing\Price\FinalPrice( - $this->saleableInterfaceMock, - $this->quantity, - $this->bundleCalculatorMock, - $this->priceCurrencyMock - ); + ->willReturn($this->priceInfoMock); + $this->priceCurrencyMock = $this->createMock(PriceCurrencyInterface::class); $this->productOptionRepositoryMock = $this->getMockForAbstractClass( ProductCustomOptionRepositoryInterface::class ); - $reflection = new \ReflectionClass(get_class($this->finalPrice)); - $reflectionProperty = $reflection->getProperty('productOptionRepository'); - $reflectionProperty->setAccessible(true); - $reflectionProperty->setValue($this->finalPrice, $this->productOptionRepositoryMock); + + $this->objectManagerHelper = new ObjectManagerHelper($this); + $this->finalPrice = $this->objectManagerHelper->getObject( + FinalPrice::class, + [ + 'saleableItem' => $this->saleableInterfaceMock, + 'quantity' => $this->quantity, + 'calculator' => $this->bundleCalculatorMock, + 'priceCurrency' => $this->priceCurrencyMock, + 'productOptionRepository' => $this->productOptionRepositoryMock + ] + ); } /** + * @param $baseAmount + * @param $optionsValue + * @param $result * @dataProvider getValueDataProvider */ public function testGetValue($baseAmount, $optionsValue, $result) @@ -126,7 +161,7 @@ public function testGetValue($baseAmount, $optionsValue, $result) $this->prepareMock(); $this->bundleOptionMock->expects($this->once()) ->method('getValue') - ->will($this->returnValue($optionsValue)); + ->willReturn($optionsValue); $this->assertSame($result, $this->finalPrice->getValue()); } @@ -144,6 +179,7 @@ public function getValueDataProvider() } /** + * @param $baseAmount * @dataProvider getValueDataProvider */ public function testGetMaximalPrice($baseAmount) @@ -155,7 +191,7 @@ public function testGetMaximalPrice($baseAmount) $this->bundleCalculatorMock->expects($this->once()) ->method('getMaxAmount') ->with($this->equalTo($this->baseAmount), $this->equalTo($this->saleableInterfaceMock)) - ->will($this->returnValue($result)); + ->willReturn($result); $this->assertSame($result, $this->finalPrice->getMaximalPrice()); //The second call should use cached value $this->assertSame($result, $this->finalPrice->getMaximalPrice()); @@ -179,7 +215,7 @@ public function testGetMaximalPriceFixedBundleWithOption() $this->bundleCalculatorMock->expects($this->once()) ->method('getMaxAmount') ->with($this->equalTo($this->baseAmount + $optionMaxPrice), $this->equalTo($this->saleableInterfaceMock)) - ->will($this->returnValue($result)); + ->willReturn($result); $this->assertSame($result, $this->finalPrice->getMaximalPrice()); //The second call should use cached value $this->assertSame($result, $this->finalPrice->getMaximalPrice()); @@ -192,7 +228,7 @@ public function testGetMinimalPriceFixedBundleWithOption() $result = 7; $this->prepareMock(); $customOptions = [ - $this->getMockBuilder(\Magento\Catalog\Api\Data\ProductCustomOptionInterface::class) + $this->getMockBuilder(ProductCustomOptionInterface::class) ->setMethods(['setProduct']) ->getMockForAbstractClass() ]; @@ -213,7 +249,7 @@ public function testGetMinimalPriceFixedBundleWithOption() $this->bundleCalculatorMock->expects($this->once()) ->method('getAmount') ->with($this->equalTo($this->baseAmount + $optionMaxPrice), $this->equalTo($this->saleableInterfaceMock)) - ->will($this->returnValue($result)); + ->willReturn($result); $this->assertSame($result, $this->finalPrice->getMinimalPrice()); //The second call should use cached value $this->assertSame($result, $this->finalPrice->getMinimalPrice()); @@ -231,7 +267,7 @@ public function testGetMinimalPrice($baseAmount) $this->bundleCalculatorMock->expects($this->once()) ->method('getAmount') ->with($this->equalTo($this->baseAmount), $this->equalTo($this->saleableInterfaceMock)) - ->will($this->returnValue($result)); + ->willReturn($result); $this->assertSame($result, $this->finalPrice->getMinimalPrice()); //The second call should use cached value $this->assertSame($result, $this->finalPrice->getMinimalPrice()); @@ -244,7 +280,7 @@ public function testGetPriceWithoutOption() $this->bundleCalculatorMock->expects($this->once()) ->method('getAmountWithoutOption') ->with($this->equalTo($this->baseAmount), $this->equalTo($this->saleableInterfaceMock)) - ->will($this->returnValue($result)); + ->willReturn($result); $this->assertSame($result, $this->finalPrice->getPriceWithoutOption()); //The second call should use cached value $this->assertSame($result, $this->finalPrice->getPriceWithoutOption()); diff --git a/app/code/Magento/Bundle/Test/Unit/Ui/DataProvider/Product/Form/Modifier/AbstractModifierTest.php b/app/code/Magento/Bundle/Test/Unit/Ui/DataProvider/Product/Form/Modifier/AbstractModifierTest.php index ccb6226ccd833..bfe0f04d59cb2 100644 --- a/app/code/Magento/Bundle/Test/Unit/Ui/DataProvider/Product/Form/Modifier/AbstractModifierTest.php +++ b/app/code/Magento/Bundle/Test/Unit/Ui/DataProvider/Product/Form/Modifier/AbstractModifierTest.php @@ -45,6 +45,7 @@ protected function setUp() $this->locatorMock = $this->getMockBuilder(LocatorInterface::class) ->getMockForAbstractClass(); $this->productMock = $this->getMockBuilder(ProductInterface::class) + ->setMethods(['getPriceType']) ->getMockForAbstractClass(); $this->locatorMock->expects($this->any()) diff --git a/app/code/Magento/Bundle/Test/Unit/Ui/DataProvider/Product/Form/Modifier/BundlePriceTest.php b/app/code/Magento/Bundle/Test/Unit/Ui/DataProvider/Product/Form/Modifier/BundlePriceTest.php new file mode 100644 index 0000000000000..b0519f1ebddba --- /dev/null +++ b/app/code/Magento/Bundle/Test/Unit/Ui/DataProvider/Product/Form/Modifier/BundlePriceTest.php @@ -0,0 +1,173 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +declare(strict_types=1); + +namespace Magento\Bundle\Test\Unit\Ui\DataProvider\Product\Form\Modifier; + +use Magento\Bundle\Ui\DataProvider\Product\Form\Modifier\BundlePrice; +use Magento\Catalog\Api\Data\ProductAttributeInterface; +use Magento\Framework\Stdlib\ArrayManager; + +class BundlePriceTest extends AbstractModifierTest +{ + /** + * @return BundlePrice + */ + protected function createModel() + { + return $this->objectManager->getObject( + BundlePrice::class, + [ + 'locator' => $this->locatorMock, + 'arrayManager' => $this->arrayManagerMock + ] + ); + } + + /** + * @SuppressWarnings(PHPMD.ExcessiveMethodLength) + */ + public function testModifyMeta() + { + $this->productMock->expects($this->any()) + ->method('getId') + ->willReturn(true); + $this->productMock->expects($this->any()) + ->method('getPriceType') + ->willReturn(0); + $priceTypePath = 'bundle-items/children/' . BundlePrice::CODE_PRICE_TYPE; + $priceTypeConfigPath = $priceTypePath . BundlePrice::META_CONFIG_PATH; + $pricePath = 'product-details/children/' . ProductAttributeInterface::CODE_PRICE; + $priceConfigPath = $pricePath . BundlePrice::META_CONFIG_PATH; + $sourceMeta = [ + 'bundle-items' => [ + 'children' => [ + BundlePrice::CODE_PRICE_TYPE => [] + ] + ] + ]; + $priceTypeParams = [ + 'disabled' => true, + 'valueMap' => [ + 'false' => '1', + 'true' => '0' + ], + 'validation' => [ + 'required-entry' => false + ] + ]; + $priceTypeMeta = [ + 'bundle-items' => [ + 'children' => [ + BundlePrice::CODE_PRICE_TYPE => $priceTypeParams + ] + ] + ]; + $priceParams = [ + 'imports' => [ + 'disabled' => 'ns = ${ $.ns }, index = ' . BundlePrice::CODE_PRICE_TYPE . ':checked' + ] + ]; + $priceMeta = [ + 'product-details' => [ + 'children' => [ + BundlePrice::CODE_PRICE_TYPE => [] + ] + ], + 'bundle-items' => [ + 'children' => [ + ProductAttributeInterface::CODE_PRICE => $priceParams + ] + ] + ]; + $taxParams = [ + 'service' => [ + 'template' => '' + ] + ]; + + $this->arrayManagerMock->expects($this->any()) + ->method('findPath') + ->willReturnMap( + [ + [ + BundlePrice::CODE_PRICE_TYPE, + $sourceMeta, + null, + 'children', + ArrayManager::DEFAULT_PATH_DELIMITER, + $priceTypePath + ], + [ + ProductAttributeInterface::CODE_PRICE, + $priceTypeMeta, + BundlePrice::DEFAULT_GENERAL_PANEL . '/children', + 'children', + ArrayManager::DEFAULT_PATH_DELIMITER, + $pricePath + ], + [ + BundlePrice::CODE_TAX_CLASS_ID, + $priceMeta, + null, + 'children', + ArrayManager::DEFAULT_PATH_DELIMITER, + $pricePath + ], + [ + BundlePrice::CODE_TAX_CLASS_ID, + $priceMeta, + null, + 'children', + ArrayManager::DEFAULT_PATH_DELIMITER, + $pricePath + ] + ] + ); + $this->arrayManagerMock->expects($this->exactly(4)) + ->method('merge') + ->willReturnMap( + [ + [ + $priceTypeConfigPath, + $sourceMeta, + $priceTypeParams, + ArrayManager::DEFAULT_PATH_DELIMITER, + $priceTypeMeta + ], + [ + $priceConfigPath, + $priceTypeMeta, + $priceParams, + ArrayManager::DEFAULT_PATH_DELIMITER, + $priceMeta + ], + [ + $priceConfigPath, + $priceMeta, + $priceParams, + ArrayManager::DEFAULT_PATH_DELIMITER, + $priceMeta + ], + [ + $priceConfigPath, + $priceMeta, + $taxParams, + ArrayManager::DEFAULT_PATH_DELIMITER, + $priceMeta + ] + ] + ); + + $this->assertSame($priceMeta, $this->getModel()->modifyMeta($sourceMeta)); + } + + public function testModifyData() + { + $expectedData = []; + $this->assertEquals($expectedData, $this->getModel()->modifyData($expectedData)); + } +} diff --git a/app/code/Magento/Bundle/Ui/DataProvider/Product/Form/Modifier/BundlePrice.php b/app/code/Magento/Bundle/Ui/DataProvider/Product/Form/Modifier/BundlePrice.php index 92326bb1521b4..d7da7513c3aac 100644 --- a/app/code/Magento/Bundle/Ui/DataProvider/Product/Form/Modifier/BundlePrice.php +++ b/app/code/Magento/Bundle/Ui/DataProvider/Product/Form/Modifier/BundlePrice.php @@ -5,6 +5,7 @@ */ namespace Magento\Bundle\Ui\DataProvider\Product\Form\Modifier; +use Magento\Bundle\Model\Product\Price; use Magento\Catalog\Ui\DataProvider\Product\Form\Modifier\AbstractModifier; use Magento\Catalog\Api\Data\ProductAttributeInterface; use Magento\Framework\Stdlib\ArrayManager; @@ -39,7 +40,7 @@ public function __construct( $this->locator = $locator; $this->arrayManager = $arrayManager; } - + /** * @inheritdoc */ @@ -89,7 +90,22 @@ public function modifyMeta(array $meta) ] ] ); - + if ($this->locator->getProduct()->getPriceType() == Price::PRICE_TYPE_DYNAMIC) { + $meta = $this->arrayManager->merge( + $this->arrayManager->findPath( + static::CODE_TAX_CLASS_ID, + $meta, + null, + 'children' + ) . static::META_CONFIG_PATH, + $meta, + [ + 'service' => [ + 'template' => '' + ] + ] + ); + } return $meta; } diff --git a/app/code/Magento/Bundle/registration.php b/app/code/Magento/Bundle/registration.php index 85d0b8d5c7a3e..bd7dc39cb4ad6 100644 --- a/app/code/Magento/Bundle/registration.php +++ b/app/code/Magento/Bundle/registration.php @@ -4,6 +4,6 @@ * See COPYING.txt for license details. */ -use \Magento\Framework\Component\ComponentRegistrar; +use Magento\Framework\Component\ComponentRegistrar; ComponentRegistrar::register(ComponentRegistrar::MODULE, 'Magento_Bundle', __DIR__); diff --git a/app/code/Magento/Bundle/view/base/web/js/price-bundle.js b/app/code/Magento/Bundle/view/base/web/js/price-bundle.js index 49ee253ad1e88..207a97c270eeb 100644 --- a/app/code/Magento/Bundle/view/base/web/js/price-bundle.js +++ b/app/code/Magento/Bundle/view/base/web/js/price-bundle.js @@ -28,7 +28,8 @@ define([ controlContainer: 'dd', // should be eliminated priceFormat: {}, isFixedPrice: false, - optionTierPricesBlocksSelector: '#option-tier-prices-{1} [data-role="selection-tier-prices"]' + optionTierPricesBlocksSelector: '#option-tier-prices-{1} [data-role="selection-tier-prices"]', + isOptionsInitialized: false }; $.widget('mage.priceBundle', { @@ -53,20 +54,37 @@ define([ priceBox = $(this.options.priceBoxSelector, form), qty = $(this.options.qtyFieldSelector, form); - if (priceBox.data('magePriceBox') && - priceBox.priceBox('option') && - priceBox.priceBox('option').priceConfig - ) { - if (priceBox.priceBox('option').priceConfig.optionTemplate) { - this._setOption('optionTemplate', priceBox.priceBox('option').priceConfig.optionTemplate); + this._updatePriceBox(); + priceBox.on('price-box-initialized', this._updatePriceBox.bind(this)); + options.on('change', this._onBundleOptionChanged.bind(this)); + qty.on('change', this._onQtyFieldChanged.bind(this)); + }, + + /** + * Update price box config with bundle option prices + * @private + */ + _updatePriceBox: function () { + var form = this.element, + options = $(this.options.productBundleSelector, form), + priceBox = $(this.options.priceBoxSelector, form); + + if (!this.options.isOptionsInitialized) { + if (priceBox.data('magePriceBox') && + priceBox.priceBox('option') && + priceBox.priceBox('option').priceConfig + ) { + if (priceBox.priceBox('option').priceConfig.optionTemplate) { //eslint-disable-line max-depth + this._setOption('optionTemplate', priceBox.priceBox('option').priceConfig.optionTemplate); + } + this._setOption('priceFormat', priceBox.priceBox('option').priceConfig.priceFormat); + priceBox.priceBox('setDefault', this.options.optionConfig.prices); + this.options.isOptionsInitialized = true; } - this._setOption('priceFormat', priceBox.priceBox('option').priceConfig.priceFormat); - priceBox.priceBox('setDefault', this.options.optionConfig.prices); + this._applyOptionNodeFix(options); } - this._applyOptionNodeFix(options); - options.on('change', this._onBundleOptionChanged.bind(this)); - qty.on('change', this._onQtyFieldChanged.bind(this)); + return this; }, /** diff --git a/app/code/Magento/BundleGraphQl/Model/Resolver/Options/Collection.php b/app/code/Magento/BundleGraphQl/Model/Resolver/Options/Collection.php index 7608d6e9e4d97..c8e2384fcb99c 100644 --- a/app/code/Magento/BundleGraphQl/Model/Resolver/Options/Collection.php +++ b/app/code/Magento/BundleGraphQl/Model/Resolver/Options/Collection.php @@ -78,11 +78,7 @@ public function addParentFilterData(int $parentId, int $parentEntityId, string $ public function getOptionsByParentId(int $parentId) : array { $options = $this->fetch(); - if (!isset($options[$parentId])) { - return []; - } - - return $options[$parentId]; + return $options[$parentId] ?? []; } /** @@ -115,7 +111,7 @@ private function fetch() : array $this->extensionAttributesJoinProcessor->process($optionsCollection); if (empty($optionsCollection->getData())) { - return null; + return []; } /** @var \Magento\Bundle\Model\Option $option */ diff --git a/app/code/Magento/BundleImportExport/Model/Import/Product/Type/Bundle.php b/app/code/Magento/BundleImportExport/Model/Import/Product/Type/Bundle.php index 81a47d72602b7..e6522054d9f94 100644 --- a/app/code/Magento/BundleImportExport/Model/Import/Product/Type/Bundle.php +++ b/app/code/Magento/BundleImportExport/Model/Import/Product/Type/Bundle.php @@ -6,35 +6,31 @@ * Copyright © Magento, Inc. All rights reserved. * See COPYING.txt for license details. */ + namespace Magento\BundleImportExport\Model\Import\Product\Type; -use Magento\Catalog\Model\ResourceModel\Product\Attribute\CollectionFactory as AttributeCollectionFactory; -use Magento\Eav\Model\ResourceModel\Entity\Attribute\Set\CollectionFactory as AttributeSetCollectionFactory; -use Magento\Framework\App\ObjectManager; use Magento\Bundle\Model\Product\Price as BundlePrice; use Magento\Catalog\Model\Product\Type\AbstractType; +use Magento\Catalog\Model\ResourceModel\Product\Attribute\CollectionFactory as AttributeCollectionFactory; use Magento\CatalogImportExport\Model\Import\Product; +use Magento\Eav\Model\ResourceModel\Entity\Attribute\Set\CollectionFactory as AttributeSetCollectionFactory; +use Magento\Framework\App\ObjectManager; use Magento\Framework\App\ResourceConnection; use Magento\Framework\EntityManager\MetadataPool; use Magento\Store\Model\StoreManagerInterface; /** - * Class Bundle + * Import entity Bundle product type. * - * @package Magento\BundleImportExport\Model\Import\Product\Type * @SuppressWarnings(PHPMD.ExcessiveClassComplexity) */ class Bundle extends \Magento\CatalogImportExport\Model\Import\Product\Type\AbstractType { - /** * Delimiter before product option value. */ const BEFORE_OPTION_VALUE_DELIMITER = ';'; - /** - * Pair value separator. - */ const PAIR_VALUE_SEPARATOR = '='; /** @@ -47,19 +43,10 @@ class Bundle extends \Magento\CatalogImportExport\Model\Import\Product\Type\Abst */ const VALUE_FIXED = 'fixed'; - /** - * Not fixed dynamic attribute. - */ const NOT_FIXED_DYNAMIC_ATTRIBUTE = 'price_view'; - /** - * Selection price type fixed. - */ const SELECTION_PRICE_TYPE_FIXED = 0; - /** - * Selection price type percent. - */ const SELECTION_PRICE_TYPE_PERCENT = 1; /** @@ -133,7 +120,7 @@ class Bundle extends \Magento\CatalogImportExport\Model\Import\Product\Type\Abst protected $_optionTypeMapping = [ 'dropdown' => 'select', 'radiobutton' => 'radio', - 'checkbox' => 'checkbox', + 'checkbox' => 'checkbox', 'multiselect' => 'multi', ]; @@ -543,7 +530,7 @@ protected function populateExistingSelections($existingOptions) ? $this->_bundleFieldMapping[$origKey] : $origKey; if ( - !isset($this->_cachedOptions[$existingSelection['parent_product_id']][$optionTitle]['selections'][$selectIndex][$key]) + !isset($this->_cachedOptions[$existingSelection['parent_product_id']][$optionTitle]['selections'][$selectIndex][$key]) ) { $this->_cachedOptions[$existingSelection['parent_product_id']][$optionTitle]['selections'][$selectIndex][$key] = $existingSelection[$origKey]; @@ -616,6 +603,7 @@ protected function populateInsertOptionValues(array $optionIds): array if ($assoc['position'] == $this->_cachedOptions[$entityId][$key]['index'] && $assoc['parent_id'] == $entityId) { $option['parent_id'] = $entityId; + //phpcs:ignore Magento2.Performance.ForeachArrayMerge $optionValues = array_merge( $optionValues, $this->populateOptionValueTemplate($option, $optionId) @@ -675,10 +663,7 @@ private function insertParentChildRelations() $childIds = []; foreach ($options as $option) { foreach ($option['selections'] as $selection) { - if (!isset($selection['parent_product_id'])) { - if (!isset($this->_cachedSkuToProducts[$selection['sku']])) { - continue; - } + if (isset($this->_cachedSkuToProducts[$selection['sku']])) { $childIds[] = $this->_cachedSkuToProducts[$selection['sku']]; } } @@ -717,6 +702,8 @@ protected function _initAttributes() } } } + + return $this; } /** @@ -735,17 +722,19 @@ protected function deleteOptionsAndSelections($productIds) $optionTable = $this->_resource->getTableName('catalog_product_bundle_option'); $optionValueTable = $this->_resource->getTableName('catalog_product_bundle_option_value'); $selectionTable = $this->_resource->getTableName('catalog_product_bundle_selection'); - $valuesIds = $this->connection->fetchAssoc($this->connection->select()->from( - ['bov' => $optionValueTable], - ['value_id'] - )->joinLeft( - ['bo' => $optionTable], - 'bo.option_id = bov.option_id', - ['option_id'] - )->where( - 'parent_id IN (?)', - $productIds - )); + $valuesIds = $this->connection->fetchAssoc( + $this->connection->select()->from( + ['bov' => $optionValueTable], + ['value_id'] + )->joinLeft( + ['bo' => $optionTable], + 'bo.option_id = bov.option_id', + ['option_id'] + )->where( + 'parent_id IN (?)', + $productIds + ) + ); $this->connection->delete( $optionValueTable, $this->connection->quoteInto('value_id IN (?)', array_keys($valuesIds)) diff --git a/app/code/Magento/BundleImportExport/Test/Mftf/Test/UpdateBundleProductViaImportTest.xml b/app/code/Magento/BundleImportExport/Test/Mftf/Test/UpdateBundleProductViaImportTest.xml new file mode 100644 index 0000000000000..23d24369cad4a --- /dev/null +++ b/app/code/Magento/BundleImportExport/Test/Mftf/Test/UpdateBundleProductViaImportTest.xml @@ -0,0 +1,70 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +--> + +<tests xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/testSchema.xsd"> + <test name="UpdateBundleProductViaImportTest"> + <annotations> + <features value="Import/Export"/> + <title value="Update Bundle product via import"/> + <description + value="Check that Bundle products are displaying on the storefront after updating product via importing CSV"/> + <severity value="MAJOR"/> + <group value="importExport"/> + </annotations> + <before> + <actionGroup ref="AdminLoginActionGroup" stepKey="loginAsAdmin"/> + </before> + <after> + <!-- Delete products created via import --> + <actionGroup ref="DeleteProductBySkuActionGroup" stepKey="deleteBundleProduct"> + <argument name="sku" value="Bundle"/> + </actionGroup> + <actionGroup ref="DeleteProductBySkuActionGroup" stepKey="deleteSimpleProduct"> + <argument name="sku" value="Simple"/> + </actionGroup> + <actionGroup ref="AdminLogoutActionGroup" stepKey="logoutFromAdmin"/> + </after> + + <!-- Create Bundle product via import --> + <actionGroup ref="AdminImportProductsActionGroup" stepKey="adminImportProductsCreate"> + <argument name="behavior" value="Add/Update"/> + <argument name="importFile" value="catalog_product_import_bundle.csv"/> + <argument name="importNoticeMessage" value="Created: 2, Updated: 0, Deleted: 0"/> + </actionGroup> + <magentoCLI command="cache:flush" arguments="full_page" stepKey="flushCacheAfterCreate"/> + <magentoCLI command="indexer:reindex" stepKey="indexerReindexAfterCreate"/> + + <!-- Check Bundle product is visible on the storefront--> + <actionGroup ref="StorefrontGoToCategoryPageActionGroup" stepKey="openCategoryPageAfterCreation"> + <argument name="categoryName" value="New"/> + </actionGroup> + <actionGroup ref="AssertStorefrontProductIsPresentOnCategoryPageActionGroup" + stepKey="assertBundleProductInStockAfterCreation"> + <argument name="productName" value="Bundle"/> + </actionGroup> + + <!-- Update Bundle product via import --> + <actionGroup ref="AdminImportProductsActionGroup" stepKey="adminImportProductsUpdate"> + <argument name="behavior" value="Add/Update"/> + <argument name="importFile" value="catalog_product_import_bundle.csv"/> + <argument name="importNoticeMessage" value="Created: 0, Updated: 2, Deleted: 0"/> + </actionGroup> + <magentoCLI command="cache:flush" arguments="full_page" stepKey="flushCacheAfterUpdate"/> + <magentoCLI command="indexer:reindex" stepKey="indexerReindexAfterUpdate"/> + + <!-- Check Bundle product is still visible on the storefront--> + <actionGroup ref="StorefrontGoToCategoryPageActionGroup" stepKey="openCategoryPageAfterUpdate"> + <argument name="categoryName" value="New"/> + </actionGroup> + <actionGroup ref="AssertStorefrontProductIsPresentOnCategoryPageActionGroup" + stepKey="assertBundleProductInStockAfterUpdate"> + <argument name="productName" value="Bundle"/> + </actionGroup> + </test> +</tests> diff --git a/app/code/Magento/BundleImportExport/registration.php b/app/code/Magento/BundleImportExport/registration.php index 2f68e2e05c036..db96b4d9dd470 100644 --- a/app/code/Magento/BundleImportExport/registration.php +++ b/app/code/Magento/BundleImportExport/registration.php @@ -4,6 +4,6 @@ * See COPYING.txt for license details. */ -use \Magento\Framework\Component\ComponentRegistrar; +use Magento\Framework\Component\ComponentRegistrar; ComponentRegistrar::register(ComponentRegistrar::MODULE, 'Magento_BundleImportExport', __DIR__); diff --git a/app/code/Magento/CacheInvalidate/Model/PurgeCache.php b/app/code/Magento/CacheInvalidate/Model/PurgeCache.php index b2aa0d000e9cf..aeef8d00f720a 100644 --- a/app/code/Magento/CacheInvalidate/Model/PurgeCache.php +++ b/app/code/Magento/CacheInvalidate/Model/PurgeCache.php @@ -8,7 +8,7 @@ use Magento\Framework\Cache\InvalidateLogger; /** - * Class PurgeCache + * PurgeCache model */ class PurgeCache { @@ -110,8 +110,8 @@ private function splitTags($tagsPattern) /** * Send curl purge request to servers to invalidate cache by tags pattern * - * @param \Zend\Http\Client\Adapter\Socket $socketAdapter - * @param \Zend\Uri\Uri[] $servers + * @param \Laminas\Http\Client\Adapter\Socket $socketAdapter + * @param \Laminas\Uri\Uri[] $servers * @param string $formattedTagsChunk * @return bool Return true if successful; otherwise return false */ diff --git a/app/code/Magento/CacheInvalidate/Model/SocketFactory.php b/app/code/Magento/CacheInvalidate/Model/SocketFactory.php index b69788bf829de..5a2d602308e92 100644 --- a/app/code/Magento/CacheInvalidate/Model/SocketFactory.php +++ b/app/code/Magento/CacheInvalidate/Model/SocketFactory.php @@ -3,15 +3,22 @@ * Copyright © Magento, Inc. All rights reserved. * See COPYING.txt for license details. */ +declare(strict_types=1); + namespace Magento\CacheInvalidate\Model; +/** + * Factory for the \Laminas\Http\Client\Adapter\Socket + */ class SocketFactory { /** - * @return \Zend\Http\Client\Adapter\Socket + * Create object + * + * @return \Laminas\Http\Client\Adapter\Socket */ public function create() { - return new \Zend\Http\Client\Adapter\Socket(); + return new \Laminas\Http\Client\Adapter\Socket(); } } diff --git a/app/code/Magento/CacheInvalidate/Observer/InvalidateVarnishObserver.php b/app/code/Magento/CacheInvalidate/Observer/InvalidateVarnishObserver.php index 3527d4f6cdf4e..a3062ea9d6a76 100644 --- a/app/code/Magento/CacheInvalidate/Observer/InvalidateVarnishObserver.php +++ b/app/code/Magento/CacheInvalidate/Observer/InvalidateVarnishObserver.php @@ -3,61 +3,76 @@ * Copyright © Magento, Inc. All rights reserved. * See COPYING.txt for license details. */ +declare(strict_types=1); + namespace Magento\CacheInvalidate\Observer; +use Magento\CacheInvalidate\Model\PurgeCache; +use Magento\Framework\App\Config\ScopeConfigInterface; +use Magento\Framework\Event\Observer; use Magento\Framework\Event\ObserverInterface; +use Magento\Framework\App\Cache\Tag\Resolver; +use Magento\PageCache\Model\Config; +/** + * Observer used to invalidate varnish cache once Magento cache was cleaned + */ class InvalidateVarnishObserver implements ObserverInterface { /** * Application config object * - * @var \Magento\Framework\App\Config\ScopeConfigInterface + * @var ScopeConfigInterface */ - protected $config; + private $config; /** - * @var \Magento\CacheInvalidate\Model\PurgeCache + * @var PurgeCache */ - protected $purgeCache; + private $purgeCache; /** * Invalidation tags resolver * - * @var \Magento\Framework\App\Cache\Tag\Resolver + * @var Resolver */ private $tagResolver; /** - * @param \Magento\PageCache\Model\Config $config - * @param \Magento\CacheInvalidate\Model\PurgeCache $purgeCache + * @param Config $config + * @param PurgeCache $purgeCache + * @param Resolver $tagResolver */ public function __construct( - \Magento\PageCache\Model\Config $config, - \Magento\CacheInvalidate\Model\PurgeCache $purgeCache + Config $config, + PurgeCache $purgeCache, + Resolver $tagResolver ) { $this->config = $config; $this->purgeCache = $purgeCache; + $this->tagResolver = $tagResolver; } /** - * If Varnish caching is enabled it collects array of tags - * of incoming object and asks to clean cache. + * If Varnish caching is enabled it collects array of tags of incoming object and asks to clean cache. + * + * @param Observer $observer * - * @param \Magento\Framework\Event\Observer $observer * @return void */ - public function execute(\Magento\Framework\Event\Observer $observer) + public function execute(Observer $observer) { $object = $observer->getEvent()->getObject(); + if (!is_object($object)) { return; } - if ($this->config->getType() == \Magento\PageCache\Model\Config::VARNISH && $this->config->isEnabled()) { - $bareTags = $this->getTagResolver()->getTags($object); + + if ((int)$this->config->getType() === Config::VARNISH && $this->config->isEnabled()) { + $bareTags = $this->tagResolver->getTags($object); $tags = []; - $pattern = "((^|,)%s(,|$))"; + $pattern = '((^|,)%s(,|$))'; foreach ($bareTags as $tag) { $tags[] = sprintf($pattern, $tag); } @@ -66,17 +81,4 @@ public function execute(\Magento\Framework\Event\Observer $observer) } } } - - /** - * @deprecated 100.1.2 - * @return \Magento\Framework\App\Cache\Tag\Resolver - */ - private function getTagResolver() - { - if ($this->tagResolver === null) { - $this->tagResolver = \Magento\Framework\App\ObjectManager::getInstance() - ->get(\Magento\Framework\App\Cache\Tag\Resolver::class); - } - return $this->tagResolver; - } } diff --git a/app/code/Magento/CacheInvalidate/Test/Unit/Model/PurgeCacheTest.php b/app/code/Magento/CacheInvalidate/Test/Unit/Model/PurgeCacheTest.php index c66e27ea41025..956c8802f597f 100644 --- a/app/code/Magento/CacheInvalidate/Test/Unit/Model/PurgeCacheTest.php +++ b/app/code/Magento/CacheInvalidate/Test/Unit/Model/PurgeCacheTest.php @@ -5,14 +5,14 @@ */ namespace Magento\CacheInvalidate\Test\Unit\Model; -use Zend\Uri\UriFactory; +use Laminas\Uri\UriFactory; class PurgeCacheTest extends \PHPUnit\Framework\TestCase { /** @var \Magento\CacheInvalidate\Model\PurgeCache */ protected $model; - /** @var \PHPUnit_Framework_MockObject_MockObject | \Zend\Http\Client\Adapter\Socket */ + /** @var \PHPUnit_Framework_MockObject_MockObject | \Laminas\Http\Client\Adapter\Socket */ protected $socketAdapterMock; /** @var \PHPUnit_Framework_MockObject_MockObject | \Magento\Framework\Cache\InvalidateLogger */ @@ -24,7 +24,7 @@ class PurgeCacheTest extends \PHPUnit\Framework\TestCase protected function setUp() { $socketFactoryMock = $this->createMock(\Magento\CacheInvalidate\Model\SocketFactory::class); - $this->socketAdapterMock = $this->createMock(\Zend\Http\Client\Adapter\Socket::class); + $this->socketAdapterMock = $this->createMock(\Laminas\Http\Client\Adapter\Socket::class); $this->socketAdapterMock->expects($this->once()) ->method('setOptions') ->with(['timeout' => 10]); @@ -113,7 +113,7 @@ public function testSendPurgeRequestWithException() ->method('getUris') ->willReturn($uris); $this->socketAdapterMock->method('connect') - ->willThrowException(new \Zend\Http\Client\Adapter\Exception\RuntimeException()); + ->willThrowException(new \Laminas\Http\Client\Adapter\Exception\RuntimeException()); $this->loggerMock->expects($this->never()) ->method('execute'); $this->loggerMock->expects($this->once()) diff --git a/app/code/Magento/CacheInvalidate/Test/Unit/Model/SocketFactoryTest.php b/app/code/Magento/CacheInvalidate/Test/Unit/Model/SocketFactoryTest.php index c69c7f4d8543e..f72abc2760794 100644 --- a/app/code/Magento/CacheInvalidate/Test/Unit/Model/SocketFactoryTest.php +++ b/app/code/Magento/CacheInvalidate/Test/Unit/Model/SocketFactoryTest.php @@ -10,6 +10,6 @@ class SocketFactoryTest extends \PHPUnit\Framework\TestCase public function testCreate() { $factory = new \Magento\CacheInvalidate\Model\SocketFactory(); - $this->assertInstanceOf(\Zend\Http\Client\Adapter\Socket::class, $factory->create()); + $this->assertInstanceOf(\Laminas\Http\Client\Adapter\Socket::class, $factory->create()); } } diff --git a/app/code/Magento/CacheInvalidate/Test/Unit/Observer/InvalidateVarnishObserverTest.php b/app/code/Magento/CacheInvalidate/Test/Unit/Observer/InvalidateVarnishObserverTest.php index b333fcaaa678a..56684ed564dde 100644 --- a/app/code/Magento/CacheInvalidate/Test/Unit/Observer/InvalidateVarnishObserverTest.php +++ b/app/code/Magento/CacheInvalidate/Test/Unit/Observer/InvalidateVarnishObserverTest.php @@ -3,49 +3,77 @@ * Copyright © Magento, Inc. All rights reserved. * See COPYING.txt for license details. */ +declare(strict_types=1); + namespace Magento\CacheInvalidate\Test\Unit\Observer; +use Magento\CacheInvalidate\Model\PurgeCache; +use Magento\CacheInvalidate\Observer\InvalidateVarnishObserver; +use Magento\Framework\App\Cache\Tag\Resolver; +use Magento\Framework\Event; +use Magento\Framework\Event\Observer; use Magento\Framework\TestFramework\Unit\Helper\ObjectManager; +use Magento\PageCache\Model\Config; +use Magento\Store\Model\Store; +use PHPUnit\Framework\MockObject\MockObject; +use PHPUnit\Framework\TestCase; -class InvalidateVarnishObserverTest extends \PHPUnit\Framework\TestCase +/** + * Test class for \Magento\CacheInvalidate\Observer\InvalidateVarnishObserver + */ +class InvalidateVarnishObserverTest extends TestCase { - /** @var \PHPUnit_Framework_MockObject_MockObject | \Magento\CacheInvalidate\Observer\InvalidateVarnishObserver */ - protected $model; + /** + * @var InvalidateVarnishObserver + */ + private $model; - /** @var \PHPUnit_Framework_MockObject_MockObject | \Magento\Framework\Event\Observer */ - protected $observerMock; + /** + * @var Config|MockObject + */ + private $configMock; - /** @var \PHPUnit_Framework_MockObject_MockObject | \Magento\PageCache\Model\Config */ - protected $configMock; + /** + * @var PurgeCache|MockObject + */ + private $purgeCacheMock; - /** @var \PHPUnit_Framework_MockObject_MockObject | \Magento\CacheInvalidate\Model\PurgeCache */ - protected $purgeCache; + /** + * @var Resolver|MockObject + */ + private $tagResolverMock; - /** @var \PHPUnit_Framework_MockObject_MockObject | \Magento\Framework\DataObject\ */ - protected $observerObject; + /** + * @var Observer|MockObject + */ + private $observerMock; - /** @var \PHPUnit_Framework_MockObject_MockObject | \Magento\Framework\App\Cache\Tag\Resolver */ - private $tagResolver; + /** + * @var Store|MockObject + */ + private $observerObject; /** - * Set up all mocks and data for test + * @inheritDoc */ protected function setUp() { - $helper = new \Magento\Framework\TestFramework\Unit\Helper\ObjectManager($this); - - $this->configMock = $this->createPartialMock(\Magento\PageCache\Model\Config::class, ['getType', 'isEnabled']); - $this->purgeCache = $this->createMock(\Magento\CacheInvalidate\Model\PurgeCache::class); - $this->model = new \Magento\CacheInvalidate\Observer\InvalidateVarnishObserver( - $this->configMock, - $this->purgeCache - ); + $this->configMock = $this->createPartialMock(Config::class, ['getType', 'isEnabled']); + $this->purgeCacheMock = $this->createMock(PurgeCache::class); + $this->tagResolverMock = $this->createMock(Resolver::class); - $this->tagResolver = $this->createMock(\Magento\Framework\App\Cache\Tag\Resolver::class); - $helper->setBackwardCompatibleProperty($this->model, 'tagResolver', $this->tagResolver); + $this->observerMock = $this->createPartialMock(Observer::class, ['getEvent']); + $this->observerObject = $this->createMock(Store::class); - $this->observerMock = $this->createPartialMock(\Magento\Framework\Event\Observer::class, ['getEvent']); - $this->observerObject = $this->createMock(\Magento\Store\Model\Store::class); + $objectManager = new ObjectManager($this); + $this->model = $objectManager->getObject( + InvalidateVarnishObserver::class, + [ + 'config' => $this->configMock, + 'purgeCache' => $this->purgeCacheMock, + 'tagResolver' => $this->tagResolverMock + ] + ); } /** @@ -56,21 +84,21 @@ public function testInvalidateVarnish() $tags = ['cache_1', 'cache_group']; $pattern = '((^|,)cache_1(,|$))|((^|,)cache_group(,|$))'; - $this->configMock->expects($this->once())->method('isEnabled')->will($this->returnValue(true)); + $this->configMock->expects($this->once())->method('isEnabled')->willReturn(true); $this->configMock->expects( $this->once() )->method( 'getType' - )->will( - $this->returnValue(\Magento\PageCache\Model\Config::VARNISH) + )->willReturn( + Config::VARNISH ); - $eventMock = $this->createPartialMock(\Magento\Framework\Event::class, ['getObject']); - $eventMock->expects($this->once())->method('getObject')->will($this->returnValue($this->observerObject)); - $this->observerMock->expects($this->once())->method('getEvent')->will($this->returnValue($eventMock)); - $this->tagResolver->expects($this->once())->method('getTags')->with($this->observerObject) - ->will($this->returnValue($tags)); - $this->purgeCache->expects($this->once())->method('sendPurgeRequest')->with($pattern); + $eventMock = $this->createPartialMock(Event::class, ['getObject']); + $eventMock->expects($this->once())->method('getObject')->willReturn($this->observerObject); + $this->observerMock->expects($this->once())->method('getEvent')->willReturn($eventMock); + $this->tagResolverMock->expects($this->once())->method('getTags')->with($this->observerObject) + ->willReturn($tags); + $this->purgeCacheMock->expects($this->once())->method('sendPurgeRequest')->with($pattern); $this->model->execute($this->observerMock); } diff --git a/app/code/Magento/CacheInvalidate/registration.php b/app/code/Magento/CacheInvalidate/registration.php index 5910edade1092..1c1ac92e330b0 100644 --- a/app/code/Magento/CacheInvalidate/registration.php +++ b/app/code/Magento/CacheInvalidate/registration.php @@ -4,6 +4,6 @@ * See COPYING.txt for license details. */ -use \Magento\Framework\Component\ComponentRegistrar; +use Magento\Framework\Component\ComponentRegistrar; ComponentRegistrar::register(ComponentRegistrar::MODULE, 'Magento_CacheInvalidate', __DIR__); diff --git a/app/code/Magento/Captcha/Controller/Refresh/Index.php b/app/code/Magento/Captcha/Controller/Refresh/Index.php index e401e03e9551f..968f02a87290f 100644 --- a/app/code/Magento/Captcha/Controller/Refresh/Index.php +++ b/app/code/Magento/Captcha/Controller/Refresh/Index.php @@ -1,65 +1,107 @@ <?php /** - * Refreshes captcha and returns JSON encoded URL to image (AJAX action) - * Example: {'imgSrc': 'http://example.com/media/captcha/67842gh187612ngf8s.png'} - * * Copyright © Magento, Inc. All rights reserved. * See COPYING.txt for license details. */ +declare(strict_types=1); + namespace Magento\Captcha\Controller\Refresh; -use Magento\Framework\App\Action\HttpPostActionInterface as HttpPostActionInterface; -use Magento\Framework\App\Action\Context; +use Magento\Captcha\Helper\Data as CaptchaHelper; +use Magento\Framework\App\Action\HttpPostActionInterface; +use Magento\Framework\App\RequestInterface; +use Magento\Framework\Controller\Result\JsonFactory as JsonResultFactory; +use Magento\Framework\Serialize\Serializer\Json as JsonSerializer; +use Magento\Framework\View\LayoutInterface; -class Index extends \Magento\Framework\App\Action\Action implements HttpPostActionInterface +/** + * Refreshes captcha and returns JSON encoded URL to image (AJAX action) + * Example: {'imgSrc': 'http://example.com/media/captcha/67842gh187612ngf8s.png'} + */ +class Index implements HttpPostActionInterface { /** - * @var \Magento\Captcha\Helper\Data + * @var CaptchaHelper + */ + private $captchaHelper; + + /** + * @var JsonSerializer + */ + private $serializer; + + /** + * @var RequestInterface + */ + private $request; + + /** + * @var LayoutInterface */ - protected $captchaHelper; + private $layout; /** - * @var \Magento\Framework\Serialize\Serializer\Json + * @var JsonResultFactory */ - protected $serializer; + private $jsonResultFactory; /** - * @param Context $context - * @param \Magento\Captcha\Helper\Data $captchaHelper - * @param \Magento\Framework\Serialize\Serializer\Json|null $serializer - * @throws \RuntimeException + * @param RequestInterface $request + * @param JsonResultFactory $jsonFactory + * @param CaptchaHelper $captchaHelper + * @param LayoutInterface $layout + * @param JsonSerializer $serializer */ public function __construct( - Context $context, - \Magento\Captcha\Helper\Data $captchaHelper, - \Magento\Framework\Serialize\Serializer\Json $serializer = null + RequestInterface $request, + JsonResultFactory $jsonFactory, + CaptchaHelper $captchaHelper, + LayoutInterface $layout, + JsonSerializer $serializer ) { + $this->request = $request; + $this->jsonResultFactory = $jsonFactory; $this->captchaHelper = $captchaHelper; - $this->serializer = $serializer ?: \Magento\Framework\App\ObjectManager::getInstance() - ->get(\Magento\Framework\Serialize\Serializer\Json::class); - parent::__construct($context); + $this->layout = $layout; + $this->serializer = $serializer; } /** - * {@inheritdoc} + * @inheritdoc */ public function execute() { - $formId = $this->_request->getPost('formId'); + $formId = $this->getRequestFormId(); + + $captchaModel = $this->captchaHelper->getCaptcha($formId); + $captchaModel->generate(); + + $block = $this->layout->createBlock($captchaModel->getBlockName()); + $block->setFormId($formId)->setIsAjax(true)->toHtml(); + + $result = $this->jsonResultFactory->create(); + + return $result->setData(['imgSrc' => $captchaModel->getImgSrc()]); + } + + /** + * Returns requested Form ID + * + * @return string|null + */ + private function getRequestFormId(): ?string + { + $formId = $this->request->getPost('formId'); if (null === $formId) { $params = []; - $content = $this->_request->getContent(); + $content = $this->request->getContent(); if ($content) { $params = $this->serializer->unserialize($content); } - $formId = isset($params['formId']) ? $params['formId'] : null; + + $formId = $params['formId'] ?? null; } - $captchaModel = $this->captchaHelper->getCaptcha($formId); - $captchaModel->generate(); - $block = $this->_view->getLayout()->createBlock($captchaModel->getBlockName()); - $block->setFormId($formId)->setIsAjax(true)->toHtml(); - $this->_response->representJson($this->serializer->serialize(['imgSrc' => $captchaModel->getImgSrc()])); - $this->_actionFlag->set('', self::FLAG_NO_POST_DISPATCH, true); + return $formId !== null ? (string)$formId : null; } } diff --git a/app/code/Magento/Captcha/Model/DefaultModel.php b/app/code/Magento/Captcha/Model/DefaultModel.php index bbbbfb0a36e08..32614be560893 100644 --- a/app/code/Magento/Captcha/Model/DefaultModel.php +++ b/app/code/Magento/Captcha/Model/DefaultModel.php @@ -11,14 +11,14 @@ use Magento\Framework\Math\Random; /** - * Implementation of \Zend\Captcha\Image + * Implementation of \Laminas\Captcha\Image * * @SuppressWarnings(PHPMD.CookieAndSessionMisuse) * * @api * @since 100.0.2 */ -class DefaultModel extends \Zend\Captcha\Image implements \Magento\Captcha\Model\CaptchaInterface +class DefaultModel extends \Laminas\Captcha\Image implements \Magento\Captcha\Model\CaptchaInterface { /** * Key in session for captcha code @@ -51,7 +51,7 @@ class DefaultModel extends \Zend\Captcha\Image implements \Magento\Captcha\Model /** * Override default value to prevent a captcha cut off * @var int - * @see \Zend\Captcha\Image::$fsize + * @see \Laminas\Captcha\Image::$fsize * @since 100.2.0 */ protected $fsize = 22; @@ -99,7 +99,7 @@ class DefaultModel extends \Zend\Captcha\Image implements \Magento\Captcha\Model * @param ResourceModel\LogFactory $resLogFactory * @param string $formId * @param Random $randomMath - * @throws \Zend\Captcha\Exception\ExtensionNotLoadedException + * @throws \Laminas\Captcha\Exception\ExtensionNotLoadedException */ public function __construct( \Magento\Framework\Session\SessionManagerInterface $session, @@ -537,7 +537,7 @@ private function clearWord() /** * Override function to generate less curly captcha that will not cut off * - * @see \Zend\Captcha\Image::_randomSize() + * @see \Laminas\Captcha\Image::_randomSize() * @return int * @throws \Magento\Framework\Exception\LocalizedException * @since 100.2.0 diff --git a/app/code/Magento/Captcha/Model/ResourceModel/Log.php b/app/code/Magento/Captcha/Model/ResourceModel/Log.php index 95b7d3a5a0a03..83d055da7c26d 100644 --- a/app/code/Magento/Captcha/Model/ResourceModel/Log.php +++ b/app/code/Magento/Captcha/Model/ResourceModel/Log.php @@ -13,7 +13,7 @@ class Log extends \Magento\Framework\Model\ResourceModel\Db\AbstractDb { /** - * Type Remote Address + * Remote Address log type */ const TYPE_REMOTE_ADDRESS = 1; @@ -79,7 +79,7 @@ public function logAttempt($login) 'count' => 1, 'updated_at' => $this->_coreDate->gmtDate() ], - ['count' => new \Zend\Db\Sql\Expression('count+1'), 'updated_at'] + ['count' => new \Laminas\Db\Sql\Expression('count+1'), 'updated_at'] ); } $ip = $this->_remoteAddress->getRemoteAddress(); @@ -92,7 +92,7 @@ public function logAttempt($login) 'count' => 1, 'updated_at' => $this->_coreDate->gmtDate() ], - ['count' => new \Zend\Db\Sql\Expression('count+1'), 'updated_at'] + ['count' => new \Laminas\Db\Sql\Expression('count+1'), 'updated_at'] ); } return $this; diff --git a/app/code/Magento/Captcha/Observer/CheckContactUsFormObserver.php b/app/code/Magento/Captcha/Observer/CheckContactUsFormObserver.php index 8c1da0e1ef104..2d0a6479bbcd6 100644 --- a/app/code/Magento/Captcha/Observer/CheckContactUsFormObserver.php +++ b/app/code/Magento/Captcha/Observer/CheckContactUsFormObserver.php @@ -3,34 +3,41 @@ * Copyright © Magento, Inc. All rights reserved. * See COPYING.txt for license details. */ +declare(strict_types=1); + namespace Magento\Captcha\Observer; +use Magento\Captcha\Helper\Data; +use Magento\Framework\App\Action\Action; +use Magento\Framework\App\ActionFlag; +use Magento\Framework\App\Response\RedirectInterface; +use Magento\Framework\Event\Observer; use Magento\Framework\Event\ObserverInterface; use Magento\Framework\App\Request\DataPersistorInterface; -use Magento\Framework\App\ObjectManager; +use Magento\Framework\Message\ManagerInterface; /** - * Class CheckContactUsFormObserver + * Check captcha on contact us form submit observer. */ class CheckContactUsFormObserver implements ObserverInterface { /** - * @var \Magento\Captcha\Helper\Data + * @var Data */ protected $_helper; /** - * @var \Magento\Framework\App\ActionFlag + * @var ActionFlag */ protected $_actionFlag; /** - * @var \Magento\Framework\Message\ManagerInterface + * @var ManagerInterface */ protected $messageManager; /** - * @var \Magento\Framework\App\Response\RedirectInterface + * @var RedirectInterface */ protected $redirect; @@ -45,60 +52,48 @@ class CheckContactUsFormObserver implements ObserverInterface private $dataPersistor; /** - * @param \Magento\Captcha\Helper\Data $helper - * @param \Magento\Framework\App\ActionFlag $actionFlag - * @param \Magento\Framework\Message\ManagerInterface $messageManager - * @param \Magento\Framework\App\Response\RedirectInterface $redirect + * @param Data $helper + * @param ActionFlag $actionFlag + * @param ManagerInterface $messageManager + * @param RedirectInterface $redirect * @param CaptchaStringResolver $captchaStringResolver + * @param DataPersistorInterface $dataPersistor */ public function __construct( - \Magento\Captcha\Helper\Data $helper, - \Magento\Framework\App\ActionFlag $actionFlag, - \Magento\Framework\Message\ManagerInterface $messageManager, - \Magento\Framework\App\Response\RedirectInterface $redirect, - CaptchaStringResolver $captchaStringResolver + Data $helper, + ActionFlag $actionFlag, + ManagerInterface $messageManager, + RedirectInterface $redirect, + CaptchaStringResolver $captchaStringResolver, + DataPersistorInterface $dataPersistor ) { $this->_helper = $helper; $this->_actionFlag = $actionFlag; $this->messageManager = $messageManager; $this->redirect = $redirect; $this->captchaStringResolver = $captchaStringResolver; + $this->dataPersistor = $dataPersistor; } /** * Check CAPTCHA on Contact Us page * - * @param \Magento\Framework\Event\Observer $observer + * @param Observer $observer * @return void */ - public function execute(\Magento\Framework\Event\Observer $observer) + public function execute(Observer $observer) { $formId = 'contact_us'; $captcha = $this->_helper->getCaptcha($formId); if ($captcha->isRequired()) { - /** @var \Magento\Framework\App\Action\Action $controller */ + /** @var Action $controller */ $controller = $observer->getControllerAction(); if (!$captcha->isCorrect($this->captchaStringResolver->resolve($controller->getRequest(), $formId))) { $this->messageManager->addErrorMessage(__('Incorrect CAPTCHA.')); - $this->getDataPersistor()->set($formId, $controller->getRequest()->getPostValue()); - $this->_actionFlag->set('', \Magento\Framework\App\Action\Action::FLAG_NO_DISPATCH, true); + $this->dataPersistor->set($formId, $controller->getRequest()->getPostValue()); + $this->_actionFlag->set('', Action::FLAG_NO_DISPATCH, true); $this->redirect->redirect($controller->getResponse(), 'contact/index/index'); } } } - - /** - * Get Data Persistor - * - * @return DataPersistorInterface - */ - private function getDataPersistor() - { - if ($this->dataPersistor === null) { - $this->dataPersistor = ObjectManager::getInstance() - ->get(DataPersistorInterface::class); - } - - return $this->dataPersistor; - } } diff --git a/app/code/Magento/Captcha/Observer/CheckUserForgotPasswordBackendObserver.php b/app/code/Magento/Captcha/Observer/CheckUserForgotPasswordBackendObserver.php index e11e48a527169..21d7e2f1993b7 100644 --- a/app/code/Magento/Captcha/Observer/CheckUserForgotPasswordBackendObserver.php +++ b/app/code/Magento/Captcha/Observer/CheckUserForgotPasswordBackendObserver.php @@ -3,19 +3,28 @@ * Copyright © Magento, Inc. All rights reserved. * See COPYING.txt for license details. */ + namespace Magento\Captcha\Observer; +use Magento\Captcha\Helper\Data as CaptchaHelper; +use Magento\Framework\App\Action\Action; +use Magento\Framework\App\ActionFlag; +use Magento\Framework\App\ObjectManager; +use Magento\Framework\App\RequestInterface; +use Magento\Framework\Event\Observer as Event; use Magento\Framework\Event\ObserverInterface; +use Magento\Framework\Message\ManagerInterface; +use Magento\Framework\Session\SessionManagerInterface; /** - * Class CheckUserForgotPasswordBackendObserver + * Handle request for Forgot Password * * @SuppressWarnings(PHPMD.CookieAndSessionMisuse) */ class CheckUserForgotPasswordBackendObserver implements ObserverInterface { /** - * @var \Magento\Captcha\Helper\Data + * @var CaptchaHelper */ protected $_helper; @@ -25,62 +34,70 @@ class CheckUserForgotPasswordBackendObserver implements ObserverInterface protected $captchaStringResolver; /** - * @var \Magento\Framework\Session\SessionManagerInterface + * @var SessionManagerInterface */ protected $_session; /** - * @var \Magento\Framework\App\ActionFlag + * @var ActionFlag */ protected $_actionFlag; /** - * @var \Magento\Framework\Message\ManagerInterface + * @var ManagerInterface */ protected $messageManager; /** - * @param \Magento\Captcha\Helper\Data $helper + * @var RequestInterface + */ + private $request; + + /** + * @param CaptchaHelper $helper * @param CaptchaStringResolver $captchaStringResolver - * @param \Magento\Framework\Session\SessionManagerInterface $session - * @param \Magento\Framework\App\ActionFlag $actionFlag - * @param \Magento\Framework\Message\ManagerInterface $messageManager + * @param SessionManagerInterface $session + * @param ActionFlag $actionFlag + * @param ManagerInterface $messageManager + * @param RequestInterface|null $request */ public function __construct( - \Magento\Captcha\Helper\Data $helper, + CaptchaHelper $helper, CaptchaStringResolver $captchaStringResolver, - \Magento\Framework\Session\SessionManagerInterface $session, - \Magento\Framework\App\ActionFlag $actionFlag, - \Magento\Framework\Message\ManagerInterface $messageManager + SessionManagerInterface $session, + ActionFlag $actionFlag, + ManagerInterface $messageManager, + RequestInterface $request = null ) { $this->_helper = $helper; $this->captchaStringResolver = $captchaStringResolver; $this->_session = $session; $this->_actionFlag = $actionFlag; $this->messageManager = $messageManager; + $this->request = $request ?? ObjectManager::getInstance()->get(RequestInterface::class); } /** * Check Captcha On User Login Backend Page * - * @param \Magento\Framework\Event\Observer $observer - * @throws \Magento\Framework\Exception\Plugin\AuthenticationException + * @param Event $observer * @return $this + * @throws \Magento\Framework\Exception\Plugin\AuthenticationException */ - public function execute(\Magento\Framework\Event\Observer $observer) + public function execute(Event $observer) { $formId = 'backend_forgotpassword'; $captchaModel = $this->_helper->getCaptcha($formId); $controller = $observer->getControllerAction(); - $email = (string)$observer->getControllerAction()->getRequest()->getParam('email'); - $params = $observer->getControllerAction()->getRequest()->getParams(); - if (!empty($email) - && !empty($params) + $params = $this->request->getParams(); + $email = (string)$this->request->getParam('email'); + if (!empty($params) + && !empty($email) && $captchaModel->isRequired() - && !$captchaModel->isCorrect($this->captchaStringResolver->resolve($controller->getRequest(), $formId)) + && !$captchaModel->isCorrect($this->captchaStringResolver->resolve($this->request, $formId)) ) { - $this->_session->setEmail((string)$controller->getRequest()->getPost('email')); - $this->_actionFlag->set('', \Magento\Framework\App\Action\Action::FLAG_NO_DISPATCH, true); + $this->_session->setEmail($email); + $this->_actionFlag->set('', Action::FLAG_NO_DISPATCH, true); $this->messageManager->addErrorMessage(__('Incorrect CAPTCHA')); $controller->getResponse()->setRedirect( $controller->getUrl('*/*/forgotpassword', ['_nosecret' => true]) diff --git a/app/code/Magento/Captcha/Observer/ResetAttemptForBackendObserver.php b/app/code/Magento/Captcha/Observer/ResetAttemptForBackendObserver.php index 376bffbd9a6a5..165aef3d7f587 100644 --- a/app/code/Magento/Captcha/Observer/ResetAttemptForBackendObserver.php +++ b/app/code/Magento/Captcha/Observer/ResetAttemptForBackendObserver.php @@ -5,20 +5,27 @@ */ namespace Magento\Captcha\Observer; +use Magento\Captcha\Model\ResourceModel\Log; +use Magento\Captcha\Model\ResourceModel\LogFactory; +use Magento\Framework\Event\Observer; use Magento\Framework\Event\ObserverInterface; +use Magento\Framework\Exception\LocalizedException; +/** + * Reset captcha attempts for Backend + */ class ResetAttemptForBackendObserver implements ObserverInterface { /** - * @var \Magento\Captcha\Model\ResourceModel\LogFactory + * @var LogFactory */ public $resLogFactory; /** - * @param \Magento\Captcha\Model\ResourceModel\LogFactory $resLogFactory + * @param LogFactory $resLogFactory */ public function __construct( - \Magento\Captcha\Model\ResourceModel\LogFactory $resLogFactory + LogFactory $resLogFactory ) { $this->resLogFactory = $resLogFactory; } @@ -26,10 +33,11 @@ public function __construct( /** * Reset Attempts For Backend * - * @param \Magento\Framework\Event\Observer $observer - * @return \Magento\Captcha\Observer\ResetAttemptForBackendObserver + * @param Observer $observer + * @return Log + * @throws LocalizedException */ - public function execute(\Magento\Framework\Event\Observer $observer) + public function execute(Observer $observer) { return $this->resLogFactory->create()->deleteUserAttempts($observer->getUser()->getUsername()); } diff --git a/app/code/Magento/Captcha/Observer/ResetAttemptForFrontendObserver.php b/app/code/Magento/Captcha/Observer/ResetAttemptForFrontendObserver.php index dedb57ad52581..e65793cd3508e 100644 --- a/app/code/Magento/Captcha/Observer/ResetAttemptForFrontendObserver.php +++ b/app/code/Magento/Captcha/Observer/ResetAttemptForFrontendObserver.php @@ -5,20 +5,28 @@ */ namespace Magento\Captcha\Observer; +use Magento\Captcha\Model\ResourceModel\Log; +use Magento\Captcha\Model\ResourceModel\LogFactory; +use Magento\Customer\Model\Customer; +use Magento\Framework\Event\Observer; use Magento\Framework\Event\ObserverInterface; +use Magento\Framework\Exception\LocalizedException; +/** + * Reset captcha attempts for Frontend + */ class ResetAttemptForFrontendObserver implements ObserverInterface { /** - * @var \Magento\Captcha\Model\ResourceModel\LogFactory + * @var LogFactory */ public $resLogFactory; /** - * @param \Magento\Captcha\Model\ResourceModel\LogFactory $resLogFactory + * @param LogFactory $resLogFactory */ public function __construct( - \Magento\Captcha\Model\ResourceModel\LogFactory $resLogFactory + LogFactory $resLogFactory ) { $this->resLogFactory = $resLogFactory; } @@ -26,12 +34,13 @@ public function __construct( /** * Reset Attempts For Frontend * - * @param \Magento\Framework\Event\Observer $observer - * @return \Magento\Captcha\Observer\ResetAttemptForFrontendObserver + * @param Observer $observer + * @return Log + * @throws LocalizedException */ - public function execute(\Magento\Framework\Event\Observer $observer) + public function execute(Observer $observer) { - /** @var \Magento\Customer\Model\Customer $model */ + /** @var Customer $model */ $model = $observer->getModel(); return $this->resLogFactory->create()->deleteUserAttempts($model->getEmail()); diff --git a/app/code/Magento/Captcha/Test/Mftf/ActionGroup/AdminLoginWithCaptchaActionGroup.xml b/app/code/Magento/Captcha/Test/Mftf/ActionGroup/AdminLoginWithCaptchaActionGroup.xml index b84977ba4fcd8..8d09944576538 100644 --- a/app/code/Magento/Captcha/Test/Mftf/ActionGroup/AdminLoginWithCaptchaActionGroup.xml +++ b/app/code/Magento/Captcha/Test/Mftf/ActionGroup/AdminLoginWithCaptchaActionGroup.xml @@ -8,7 +8,7 @@ <actionGroups xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/actionGroupSchema.xsd"> - <actionGroup name="AdminLoginWithCaptchaActionGroup" extends="LoginAsAdmin"> + <actionGroup name="AdminLoginWithCaptchaActionGroup" extends="AdminLoginActionGroup"> <annotations> <description>EXTENDS: LoginAsAdmin. Fills in the Captcha field on the Backend Admin Login page.</description> </annotations> diff --git a/app/code/Magento/Captcha/Test/Mftf/Section/CaptchaFormsDisplayingSection.xml b/app/code/Magento/Captcha/Test/Mftf/Section/CaptchaFormsDisplayingSection.xml index 4c974e6fced05..9103c4191544c 100644 --- a/app/code/Magento/Captcha/Test/Mftf/Section/CaptchaFormsDisplayingSection.xml +++ b/app/code/Magento/Captcha/Test/Mftf/Section/CaptchaFormsDisplayingSection.xml @@ -7,7 +7,7 @@ --> <sections xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" - xsi:noNamespaceSchemaLocation="../../../../../../../vendor/magento/magento2-functional-testing-framework/src/Magento/FunctionalTestingFramework/Page/etc/SectionObject.xsd"> + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Page/etc/SectionObject.xsd"> <section name="CaptchaFormsDisplayingSection"> <element name="store" type="button" selector="#menu-magento-backend-stores"/> <element name="config" type="button" selector="//li[@data-ui-id='menu-magento-config-system-config']//span"/> diff --git a/app/code/Magento/Captcha/Test/Mftf/Test/AdminLoginWithCaptchaTest.xml b/app/code/Magento/Captcha/Test/Mftf/Test/AdminLoginWithCaptchaTest.xml index e5ee55910df65..28253fb3c00ef 100644 --- a/app/code/Magento/Captcha/Test/Mftf/Test/AdminLoginWithCaptchaTest.xml +++ b/app/code/Magento/Captcha/Test/Mftf/Test/AdminLoginWithCaptchaTest.xml @@ -30,19 +30,22 @@ <magentoCLI command="config:set {{AdminCaptchaDefaultSymbolsConfigData.path}} {{AdminCaptchaDefaultSymbolsConfigData.value}}" stepKey="setDefaultCaptchaSymbols" /> <magentoCLI command="cache:clean config full_page" stepKey="cleanInvalidatedCaches"/> </after> - - <actionGroup ref="LoginAsAdmin" stepKey="loginAsAdminWithWrongCredentialsFirstAttempt"> - <argument name="adminUser" value="AdminUserWrongCredentials" /> + + <actionGroup ref="AdminLoginActionGroup" stepKey="loginAsAdminWithWrongCredentialsFirstAttempt"> + <argument name="username" value="{{AdminUserWrongCredentials.username}}"/> + <argument name="password" value="{{AdminUserWrongCredentials.password}}"/> </actionGroup> <actionGroup ref="AssertMessageOnAdminLoginActionGroup" stepKey="seeFirstLoginErrorMessage" /> - <actionGroup ref="LoginAsAdmin" stepKey="loginAsAdminWithWrongCredentialsSecondAttempt"> - <argument name="adminUser" value="AdminUserWrongCredentials" /> + <actionGroup ref="AdminLoginActionGroup" stepKey="loginAsAdminWithWrongCredentialsSecondAttempt"> + <argument name="username" value="{{AdminUserWrongCredentials.username}}"/> + <argument name="password" value="{{AdminUserWrongCredentials.password}}"/> </actionGroup> <actionGroup ref="AssertMessageOnAdminLoginActionGroup" stepKey="seeSecondLoginErrorMessage" /> - <actionGroup ref="LoginAsAdmin" stepKey="loginAsAdminWithWrongCredentialsThirdAttempt"> - <argument name="adminUser" value="AdminUserWrongCredentials" /> + <actionGroup ref="AdminLoginActionGroup" stepKey="loginAsAdminWithWrongCredentialsThirdAttempt"> + <argument name="username" value="{{AdminUserWrongCredentials.username}}"/> + <argument name="password" value="{{AdminUserWrongCredentials.password}}"/> </actionGroup> <actionGroup ref="AssertMessageOnAdminLoginActionGroup" stepKey="seeThirdLoginErrorMessage" /> @@ -51,17 +54,19 @@ <!-- Submit form with incorrect captcha --> <actionGroup ref="AdminLoginWithCaptchaActionGroup" stepKey="loginAsAdminWithIncorrectCaptcha"> - <argument name="adminUser" value="DefaultAdminUser" /> - <argument name="captcha" value="{{WrongCaptcha.value}}" /> + <argument name="username" value="{{DefaultAdminUser.username}}"/> + <argument name="password" value="{{DefaultAdminUser.password}}"/> + <argument name="captcha" value="{{WrongCaptcha.value}}"/> </actionGroup> <actionGroup ref="AssertMessageOnAdminLoginActionGroup" stepKey="seeIncorrectCaptchaErrorMessage"> - <argument name="message" value="Incorrect CAPTCHA." /> + <argument name="message" value="Incorrect CAPTCHA."/> </actionGroup> <actionGroup ref="AssertCaptchaVisibleOnAdminLoginFormActionGroup" stepKey="assertCaptchaVisibleAfterIncorrectCaptcha" /> <actionGroup ref="AdminLoginWithCaptchaActionGroup" stepKey="loginAsAdminWithCorrectCaptcha"> - <argument name="adminUser" value="DefaultAdminUser" /> - <argument name="captcha" value="{{PreconfiguredCaptcha.value}}" /> + <argument name="username" value="{{DefaultAdminUser.username}}"/> + <argument name="password" value="{{DefaultAdminUser.password}}"/> + <argument name="captcha" value="{{PreconfiguredCaptcha.value}}"/> </actionGroup> <actionGroup ref="AssertAdminSuccessLoginActionGroup" stepKey="verifyAdminLoggedIn" /> </test> diff --git a/app/code/Magento/Captcha/Test/Mftf/Test/CaptchaFormsDisplayingTest.xml b/app/code/Magento/Captcha/Test/Mftf/Test/CaptchaFormsDisplayingTest.xml deleted file mode 100644 index 977ee78c0d201..0000000000000 --- a/app/code/Magento/Captcha/Test/Mftf/Test/CaptchaFormsDisplayingTest.xml +++ /dev/null @@ -1,117 +0,0 @@ -<?xml version="1.0" encoding="UTF-8"?> -<!-- - /** - * Copyright © Magento, Inc. All rights reserved. - * See COPYING.txt for license details. - */ ---> - -<tests xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" - xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/testSchema.xsd"> - <test name="CaptchaFormsDisplayingTest"> - <annotations> - <features value="Captcha"/> - <stories value="MAGETWO-91552 - [github] CAPTCHA doesn't show when check out as guest"/> - <title value="Captcha forms displaying"/> - <description value="Captcha forms displaying"/> - <severity value="MAJOR"/> - <testCaseId value="MAGETWO-93941"/> - <group value="captcha"/> - </annotations> - - <!--Login as admin--> - <actionGroup ref="LoginAsAdmin" stepKey="LoginAsAdmin"/> - <!--Go to Captcha--> - <actionGroup ref="CaptchaFormsDisplayingActionGroup" stepKey="CaptchaFormsDisplayingActionGroup"/> - <waitForPageLoad stepKey="WaitForPageLoaded"/> - <!--Verify fields removed--> - <grabTextFrom selector="{{CaptchaFormsDisplayingSection.forms}}" stepKey="formItems"/> - <assertNotContains stepKey="checkoutAsGuest"> - <expectedResult type="string">{{CaptchaData.checkoutAsGuest}}</expectedResult> - <actualResult type="variable">$formItems</actualResult> - </assertNotContains> - <assertNotContains stepKey="register"> - <expectedResult type="string">{{CaptchaData.register}}</expectedResult> - <actualResult type="variable">$formItems</actualResult> - </assertNotContains> - <!--Verify fields existence--> - <grabTextFrom selector="{{CaptchaFormsDisplayingSection.createUser}}" stepKey="createUser"/> - <assertEquals stepKey="CreateUserFieldIsPresent"> - <expectedResult type="string">{{CaptchaData.createUser}}</expectedResult> - <actualResult type="variable">$createUser</actualResult> - </assertEquals> - <grabTextFrom selector="{{CaptchaFormsDisplayingSection.userLogin}}" stepKey="login"/> - <assertEquals stepKey="LoginFieldIsPresent"> - <expectedResult type="string">{{CaptchaData.login}}</expectedResult> - <actualResult type="variable">login</actualResult> - </assertEquals> - <grabTextFrom selector="{{CaptchaFormsDisplayingSection.forgotpassword}}" stepKey="forgotpassword"/> - <assertEquals stepKey="PasswordFieldIsPresent"> - <expectedResult type="string">{{CaptchaData.passwd}}</expectedResult> - <actualResult type="variable">$forgotpassword</actualResult> - </assertEquals> - <grabTextFrom selector="{{CaptchaFormsDisplayingSection.contactUs}}" stepKey="contactUs"/> - <assertEquals stepKey="contactUsFieldIsPresent"> - <expectedResult type="string">{{CaptchaData.contactUs}}</expectedResult> - <actualResult type="variable">$contactUs</actualResult> - </assertEquals> - <grabTextFrom selector="{{CaptchaFormsDisplayingSection.userEdit}}" stepKey="userEdit"/> - <assertEquals stepKey="userEditFieldIsPresent"> - <expectedResult type="string">{{CaptchaData.changePasswd}}</expectedResult> - <actualResult type="variable">$userEdit</actualResult> - </assertEquals> - - <!--Roll back configuration--> - <scrollToTopOfPage stepKey="ScrollToTop"/> - <click selector="{{CaptchaFormsDisplayingSection.captcha}}" stepKey="ClickToCloseCaptcha"/> - </test> - <test name="CaptchaWithDisabledGuestCheckout"> - <annotations> - <features value="Captcha"/> - <stories value="MC-5602 - CAPTCHA doesn't appear in login popup after refreshing page."/> - <title value="Captcha is displaying on login form with disabled guest checkout"/> - <description value="Captcha is displaying on login form with disabled guest checkout"/> - <severity value="MAJOR"/> - <testCaseId value="MAGETWO-96691"/> - <group value="captcha"/> - </annotations> - <before> - <magentoCLI command="config:set checkout/options/guest_checkout 0" stepKey="disableGuestCheckout"/> - <magentoCLI command="config:set customer/captcha/failed_attempts_login 1" stepKey="decreaseLoginAttempt"/> - <createData entity="ApiCategory" stepKey="createCategory"/> - <createData entity="ApiSimpleProduct" stepKey="createSimpleProduct"> - <requiredEntity createDataKey="createCategory"/> - </createData> - </before> - <after> - <magentoCLI command="config:set checkout/options/guest_checkout 1" stepKey="enableGuestCheckout"/> - <magentoCLI command="config:set customer/captcha/failed_attempts_login 3" stepKey="increaseLoginAttempt"/> - <deleteData createDataKey="createCategory" stepKey="deleteCategory"/> - <deleteData createDataKey="createSimpleProduct" stepKey="deleteSimpleProduct1"/> - </after> - <amOnPage url="{{StorefrontProductPage.url($$createSimpleProduct.sku$$)}}" stepKey="openProductPage"/> - <waitForPageLoad stepKey="waitForPageLoad"/> - <click selector="{{StorefrontProductActionSection.addToCart}}" stepKey="addToCart" /> - <waitForPageLoad stepKey="waitForAddToCart"/> - <waitForElementVisible selector="{{StorefrontMessagesSection.success}}" stepKey="waitForSuccessMessage"/> - <waitForText userInput="You added $$createSimpleProduct.name$$ to your shopping cart." stepKey="waitForText"/> - <click selector="{{StorefrontMinicartSection.showCart}}" stepKey="clickCart"/> - <click selector="{{StorefrontMinicartSection.goToCheckout}}" stepKey="goToCheckout"/> - <waitForElementVisible selector="{{StorefrontCustomerSignInPopupFormSection.email}}" stepKey="waitEmailFieldVisible"/> - <fillField selector="{{StorefrontCustomerSignInPopupFormSection.email}}" userInput="{{Simple_US_Customer.email}}" stepKey="fillCustomerEmail"/> - <fillField selector="{{StorefrontCustomerSignInPopupFormSection.password}}" userInput="incorrectPassword" stepKey="fillIncorrectCustomerPassword"/> - <click selector="{{StorefrontCustomerSignInPopupFormSection.signIn}}" stepKey="clickSignIn"/> - <waitForElementVisible selector="{{StorefrontCustomerSignInPopupFormSection.errorMessage}}" stepKey="seeErrorMessage"/> - <waitForElementVisible selector="{{StorefrontCustomerSignInPopupFormSection.captchaField}}" stepKey="seeCaptchaField"/> - <waitForElementVisible selector="{{StorefrontCustomerSignInPopupFormSection.captchaImg}}" stepKey="seeCaptchaImage"/> - <waitForElementVisible selector="{{StorefrontCustomerSignInPopupFormSection.captchaReload}}" stepKey="seeCaptchaReloadButton"/> - <reloadPage stepKey="refreshPage"/> - <waitForPageLoad stepKey="waitForPageLoad2"/> - <click selector="{{StorefrontMinicartSection.showCart}}" stepKey="clickCart2"/> - <click selector="{{StorefrontMinicartSection.goToCheckout}}" stepKey="goToCheckout2"/> - <waitForElementVisible selector="{{StorefrontCustomerSignInPopupFormSection.email}}" stepKey="waitEmailFieldVisible2"/> - <waitForElementVisible selector="{{StorefrontCustomerSignInPopupFormSection.captchaField}}" stepKey="seeCaptchaField2"/> - <waitForElementVisible selector="{{StorefrontCustomerSignInPopupFormSection.captchaImg}}" stepKey="seeCaptchaImage2"/> - <waitForElementVisible selector="{{StorefrontCustomerSignInPopupFormSection.captchaReload}}" stepKey="seeCaptchaReloadButton2"/> - </test> -</tests> diff --git a/app/code/Magento/Captcha/Test/Mftf/Test/CaptchaFormsDisplayingTest/CaptchaFormsDisplayingTest.xml b/app/code/Magento/Captcha/Test/Mftf/Test/CaptchaFormsDisplayingTest/CaptchaFormsDisplayingTest.xml new file mode 100644 index 0000000000000..3aa57917c43a8 --- /dev/null +++ b/app/code/Magento/Captcha/Test/Mftf/Test/CaptchaFormsDisplayingTest/CaptchaFormsDisplayingTest.xml @@ -0,0 +1,67 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + /** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +--> +<tests xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/testSchema.xsd"> + <test name="CaptchaFormsDisplayingTest"> + <annotations> + <features value="Captcha"/> + <stories value="MAGETWO-91552 - [github] CAPTCHA doesn't show when check out as guest"/> + <title value="Captcha forms displaying"/> + <description value="Captcha forms displaying"/> + <severity value="MAJOR"/> + <testCaseId value="MAGETWO-93941"/> + <group value="captcha"/> + </annotations> + + <!--Login as admin--> + <actionGroup ref="AdminLoginActionGroup" stepKey="LoginAsAdmin"/> + <!--Go to Captcha--> + <actionGroup ref="CaptchaFormsDisplayingActionGroup" stepKey="CaptchaFormsDisplayingActionGroup"/> + <waitForPageLoad stepKey="WaitForPageLoaded"/> + <!--Verify fields removed--> + <grabTextFrom selector="{{CaptchaFormsDisplayingSection.forms}}" stepKey="formItems"/> + <assertNotContains stepKey="checkoutAsGuest"> + <expectedResult type="string">{{CaptchaData.checkoutAsGuest}}</expectedResult> + <actualResult type="variable">$formItems</actualResult> + </assertNotContains> + <assertNotContains stepKey="register"> + <expectedResult type="string">{{CaptchaData.register}}</expectedResult> + <actualResult type="variable">$formItems</actualResult> + </assertNotContains> + <!--Verify fields existence--> + <grabTextFrom selector="{{CaptchaFormsDisplayingSection.createUser}}" stepKey="createUser"/> + <assertEquals stepKey="CreateUserFieldIsPresent"> + <expectedResult type="string">{{CaptchaData.createUser}}</expectedResult> + <actualResult type="variable">$createUser</actualResult> + </assertEquals> + <grabTextFrom selector="{{CaptchaFormsDisplayingSection.userLogin}}" stepKey="login"/> + <assertEquals stepKey="LoginFieldIsPresent"> + <expectedResult type="string">{{CaptchaData.login}}</expectedResult> + <actualResult type="variable">login</actualResult> + </assertEquals> + <grabTextFrom selector="{{CaptchaFormsDisplayingSection.forgotpassword}}" stepKey="forgotpassword"/> + <assertEquals stepKey="PasswordFieldIsPresent"> + <expectedResult type="string">{{CaptchaData.passwd}}</expectedResult> + <actualResult type="variable">$forgotpassword</actualResult> + </assertEquals> + <grabTextFrom selector="{{CaptchaFormsDisplayingSection.contactUs}}" stepKey="contactUs"/> + <assertEquals stepKey="contactUsFieldIsPresent"> + <expectedResult type="string">{{CaptchaData.contactUs}}</expectedResult> + <actualResult type="variable">$contactUs</actualResult> + </assertEquals> + <grabTextFrom selector="{{CaptchaFormsDisplayingSection.userEdit}}" stepKey="userEdit"/> + <assertEquals stepKey="userEditFieldIsPresent"> + <expectedResult type="string">{{CaptchaData.changePasswd}}</expectedResult> + <actualResult type="variable">$userEdit</actualResult> + </assertEquals> + + <!--Roll back configuration--> + <scrollToTopOfPage stepKey="ScrollToTop"/> + <click selector="{{CaptchaFormsDisplayingSection.captcha}}" stepKey="ClickToCloseCaptcha"/> + </test> +</tests> diff --git a/app/code/Magento/Captcha/Test/Mftf/Test/CaptchaFormsDisplayingTest/CaptchaWithDisabledGuestCheckoutTest.xml b/app/code/Magento/Captcha/Test/Mftf/Test/CaptchaFormsDisplayingTest/CaptchaWithDisabledGuestCheckoutTest.xml new file mode 100644 index 0000000000000..bfea4e99996c3 --- /dev/null +++ b/app/code/Magento/Captcha/Test/Mftf/Test/CaptchaFormsDisplayingTest/CaptchaWithDisabledGuestCheckoutTest.xml @@ -0,0 +1,60 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + /** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +--> +<tests xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/testSchema.xsd"> + <test name="CaptchaWithDisabledGuestCheckoutTest"> + <annotations> + <features value="Captcha"/> + <stories value="MC-5602 - CAPTCHA doesn't appear in login popup after refreshing page."/> + <title value="Captcha is displaying on login form with disabled guest checkout"/> + <description value="Captcha is displaying on login form with disabled guest checkout"/> + <severity value="MAJOR"/> + <testCaseId value="MAGETWO-96691"/> + <group value="captcha"/> + </annotations> + <before> + <magentoCLI command="config:set checkout/options/guest_checkout 0" stepKey="disableGuestCheckout"/> + <magentoCLI command="config:set customer/captcha/failed_attempts_login 1" stepKey="decreaseLoginAttempt"/> + <createData entity="ApiCategory" stepKey="createCategory"/> + <createData entity="ApiSimpleProduct" stepKey="createSimpleProduct"> + <requiredEntity createDataKey="createCategory"/> + </createData> + <magentoCron stepKey="runCronIndex" groups="index"/> + </before> + <after> + <magentoCLI command="config:set checkout/options/guest_checkout 1" stepKey="enableGuestCheckout"/> + <magentoCLI command="config:set customer/captcha/failed_attempts_login 3" stepKey="increaseLoginAttempt"/> + <deleteData createDataKey="createCategory" stepKey="deleteCategory"/> + <deleteData createDataKey="createSimpleProduct" stepKey="deleteSimpleProduct1"/> + </after> + <amOnPage url="{{StorefrontProductPage.url($$createSimpleProduct.sku$$)}}" stepKey="openProductPage"/> + <waitForPageLoad stepKey="waitForPageLoad"/> + <click selector="{{StorefrontProductActionSection.addToCart}}" stepKey="addToCart"/> + <waitForPageLoad stepKey="waitForAddToCart"/> + <waitForElementVisible selector="{{StorefrontMessagesSection.success}}" stepKey="waitForSuccessMessage"/> + <waitForText userInput="You added $$createSimpleProduct.name$$ to your shopping cart." stepKey="waitForText"/> + <actionGroup ref="StorefrontClickOnMiniCartActionGroup" stepKey="clickCart"/> + <click selector="{{StorefrontMinicartSection.goToCheckout}}" stepKey="goToCheckout"/> + <waitForElementVisible selector="{{StorefrontCustomerSignInPopupFormSection.email}}" stepKey="waitEmailFieldVisible"/> + <fillField selector="{{StorefrontCustomerSignInPopupFormSection.email}}" userInput="{{Simple_US_Customer.email}}" stepKey="fillCustomerEmail"/> + <fillField selector="{{StorefrontCustomerSignInPopupFormSection.password}}" userInput="incorrectPassword" stepKey="fillIncorrectCustomerPassword"/> + <click selector="{{StorefrontCustomerSignInPopupFormSection.signIn}}" stepKey="clickSignIn"/> + <waitForElementVisible selector="{{StorefrontCustomerSignInPopupFormSection.errorMessage}}" stepKey="seeErrorMessage"/> + <waitForElementVisible selector="{{StorefrontCustomerSignInPopupFormSection.captchaField}}" stepKey="seeCaptchaField"/> + <waitForElementVisible selector="{{StorefrontCustomerSignInPopupFormSection.captchaImg}}" stepKey="seeCaptchaImage"/> + <waitForElementVisible selector="{{StorefrontCustomerSignInPopupFormSection.captchaReload}}" stepKey="seeCaptchaReloadButton"/> + <reloadPage stepKey="refreshPage"/> + <waitForPageLoad stepKey="waitForPageLoad2"/> + <actionGroup ref="StorefrontClickOnMiniCartActionGroup" stepKey="clickCart2"/> + <click selector="{{StorefrontMinicartSection.goToCheckout}}" stepKey="goToCheckout2"/> + <waitForElementVisible selector="{{StorefrontCustomerSignInPopupFormSection.email}}" stepKey="waitEmailFieldVisible2"/> + <waitForElementVisible selector="{{StorefrontCustomerSignInPopupFormSection.captchaField}}" stepKey="seeCaptchaField2"/> + <waitForElementVisible selector="{{StorefrontCustomerSignInPopupFormSection.captchaImg}}" stepKey="seeCaptchaImage2"/> + <waitForElementVisible selector="{{StorefrontCustomerSignInPopupFormSection.captchaReload}}" stepKey="seeCaptchaReloadButton2"/> + </test> +</tests> diff --git a/app/code/Magento/Captcha/Test/Unit/Controller/Refresh/IndexTest.php b/app/code/Magento/Captcha/Test/Unit/Controller/Refresh/IndexTest.php index 99ac2e2d8fccc..c99d4a996adec 100644 --- a/app/code/Magento/Captcha/Test/Unit/Controller/Refresh/IndexTest.php +++ b/app/code/Magento/Captcha/Test/Unit/Controller/Refresh/IndexTest.php @@ -5,133 +5,140 @@ */ namespace Magento\Captcha\Test\Unit\Controller\Refresh; -class IndexTest extends \PHPUnit\Framework\TestCase +use Magento\Captcha\Controller\Refresh\Index; +use Magento\Captcha\Helper\Data as CaptchaHelper; +use Magento\Captcha\Model\CaptchaInterface; +use Magento\Framework\App\RequestInterface; +use Magento\Framework\Controller\Result\Json as ResultJson; +use Magento\Framework\Controller\Result\JsonFactory as ResultJsonFactory; +use Magento\Framework\Serialize\Serializer\Json as JsonSerializer; +use Magento\Framework\View\Element\BlockInterface; +use Magento\Framework\View\LayoutInterface; +use PHPUnit\Framework\MockObject\MockObject; +use PHPUnit\Framework\TestCase; + +class IndexTest extends TestCase { - /** - * @var \PHPUnit_Framework_MockObject_MockObject - */ - protected $captchaHelperMock; + private const STUB_FORM_ID = 'StubFormId'; + private const STUB_CAPTCHA_SOURCE = '/stub-captcha-source.jpg'; - /** - * @var \PHPUnit_Framework_MockObject_MockObject - */ - protected $captchaMock; - - /** - * @var \PHPUnit_Framework_MockObject_MockObject - */ - protected $requestMock; + /** @var MockObject|RequestInterface */ + private $requestMock; - /** - * @var \PHPUnit_Framework_MockObject_MockObject - */ - protected $responseMock; + /** @var MockObject|ResultJsonFactory */ + private $jsonResultFactoryMock; - /** - * @var \PHPUnit_Framework_MockObject_MockObject - */ - protected $contextMock; + /** @var MockObject|ResultJson */ + private $jsonResultMock; - /** - * @var \PHPUnit_Framework_MockObject_MockObject - */ - protected $viewMock; + /** @var MockObject|CaptchaHelper */ + private $captchaHelperMock; - /** - * @var \PHPUnit_Framework_MockObject_MockObject - */ - protected $layoutMock; + /** @var MockObject|LayoutInterface */ + private $layoutMock; - /** - * @var \PHPUnit_Framework_MockObject_MockObject - */ - protected $flagMock; + /** @var MockObject|BlockInterface */ + private $blockMock; - /** - * @var \PHPUnit_Framework_MockObject_MockObject - */ - protected $serializerMock; + /** @var MockObject|JsonSerializer */ + private $jsonSerializerMock; - /** - * @var \Magento\Captcha\Controller\Refresh\Index - */ - protected $model; + /** @var Index */ + private $refreshAction; protected function setUp() { - $this->captchaHelperMock = $this->createMock(\Magento\Captcha\Helper\Data::class); - $this->captchaMock = $this->createMock(\Magento\Captcha\Model\DefaultModel::class); - $this->requestMock = $this->createMock(\Magento\Framework\App\Request\Http::class); - $this->responseMock = $this->createMock(\Magento\Framework\App\Response\Http::class); - $this->contextMock = $this->createMock(\Magento\Framework\App\Action\Context::class); - $this->viewMock = $this->createMock(\Magento\Framework\App\ViewInterface::class); - $this->layoutMock = $this->createMock(\Magento\Framework\View\LayoutInterface::class); - $this->flagMock = $this->createMock(\Magento\Framework\App\ActionFlag::class); - $this->serializerMock = $this->createMock(\Magento\Framework\Serialize\Serializer\Json::class); - - $this->contextMock->expects($this->any())->method('getRequest')->will($this->returnValue($this->requestMock)); - $this->contextMock->expects($this->any())->method('getView')->will($this->returnValue($this->viewMock)); - $this->contextMock->expects($this->any())->method('getResponse')->will($this->returnValue($this->responseMock)); - $this->contextMock->expects($this->any())->method('getActionFlag')->will($this->returnValue($this->flagMock)); - $this->viewMock->expects($this->any())->method('getLayout')->will($this->returnValue($this->layoutMock)); - - $this->model = new \Magento\Captcha\Controller\Refresh\Index( - $this->contextMock, + $this->requestMock = $this->getMockBuilder(RequestInterface::class) + ->setMethods(['getPost', 'getContent']) + ->getMockForAbstractClass(); + $this->layoutMock = $this->getMockBuilder(LayoutInterface::class) + ->setMethods(['createBlock']) + ->getMockForAbstractClass(); + $this->blockMock = $this->getMockBuilder(BlockInterface::class) + ->setMethods(['setFormId', 'setIsAjax', 'toHtml']) + ->getMockForAbstractClass(); + $this->jsonResultFactoryMock = $this->createMock(ResultJsonFactory::class); + $this->jsonResultMock = $this->createMock(ResultJson::class); + $this->jsonResultFactoryMock->method('create') + ->willReturn($this->jsonResultMock); + $this->jsonSerializerMock = $this->createMock(JsonSerializer::class); + $this->captchaHelperMock = $this->createMock(CaptchaHelper::class); + + $this->blockMock->method('setIsAjax') + ->willReturnSelf(); + + $this->layoutMock->method('createBlock') + ->willReturn($this->blockMock); + + $this->refreshAction = new Index( + $this->requestMock, + $this->jsonResultFactoryMock, $this->captchaHelperMock, - $this->serializerMock + $this->layoutMock, + $this->jsonSerializerMock ); } - /** - * @dataProvider executeDataProvider - * @param int $formId - * @param int $callsNumber - */ - public function testExecute($formId, $callsNumber) + public function testCaptchaGeneratedWhenPostDataContainsFormId() { - $content = ['formId' => $formId]; - $imgSource = ['imgSrc' => 'source']; - - $blockMethods = ['setFormId', 'setIsAjax', 'toHtml']; - $blockMock = $this->createPartialMock(\Magento\Captcha\Block\Captcha::class, $blockMethods); - - $this->requestMock->expects($this->any())->method('getPost')->with('formId')->will($this->returnValue($formId)); - $this->requestMock->expects($this->exactly($callsNumber))->method('getContent') - ->will($this->returnValue(json_encode($content))); - $this->captchaHelperMock->expects($this->any())->method('getCaptcha')->with($formId) - ->will($this->returnValue($this->captchaMock)); - $this->captchaMock->expects($this->once())->method('generate'); - $this->captchaMock->expects($this->once())->method('getBlockName')->will($this->returnValue('block')); - $this->captchaMock->expects($this->once())->method('getImgSrc')->will($this->returnValue('source')); - $this->layoutMock->expects($this->once())->method('createBlock')->with('block') - ->will($this->returnValue($blockMock)); - $blockMock->expects($this->any())->method('setFormId')->with($formId)->will($this->returnValue($blockMock)); - $blockMock->expects($this->any())->method('setIsAjax')->with(true)->will($this->returnValue($blockMock)); - $blockMock->expects($this->once())->method('toHtml'); - $this->responseMock->expects($this->once())->method('representJson')->with(json_encode($imgSource)); - $this->flagMock->expects($this->once())->method('set')->with('', 'no-postDispatch', true); - $this->serializerMock->expects($this->exactly($callsNumber)) - ->method('unserialize')->will($this->returnValue($content)); - $this->serializerMock->expects($this->once()) - ->method('serialize')->will($this->returnValue(json_encode($imgSource))); - - $this->model->execute(); + // Given + $this->requestMock->method('getPost') + ->with('formId') + ->willReturn(self::STUB_FORM_ID); + $this->blockMock->method('setFormId') + ->willReturnSelf(); + + // Expect + $this->requestMock->expects($this->never()) + ->method('getContent'); + $this->captchaHelperMock->expects($this->once()) + ->method('getCaptcha') + ->with(self::STUB_FORM_ID) + ->willReturn( + $this->getCaptchaModelMock(self::STUB_CAPTCHA_SOURCE) + ); + + // When + $this->refreshAction->execute(); + } + + public function testCaptchaFallsBackToRequestContentIfPostMissing() + { + // Given + $this->requestMock->method('getPost') + ->with('formId') + ->willReturn(null); + $this->blockMock->method('setFormId') + ->willReturnSelf(); + + // Expect + $this->requestMock->expects(self::once()) + ->method('getContent') + ->willReturn(null); + $this->captchaHelperMock->expects($this->once()) + ->method('getCaptcha') + ->with(null) + ->willReturn( + $this->getCaptchaModelMock(self::STUB_CAPTCHA_SOURCE) + ); + + // When + $this->refreshAction->execute(); } /** - * @return array + * @param string $imageSource + * @return MockObject|CaptchaInterface */ - public function executeDataProvider() + private function getCaptchaModelMock(string $imageSource): CaptchaInterface { - return [ - [ - 'formId' => null, - 'callsNumber' => 1, - ], - [ - 'formId' => 1, - 'callsNumber' => 0, - ] - ]; + $modelMock = $this->getMockBuilder(CaptchaInterface::class) + ->setMethods(['generate', 'getBlockName', 'getImgSrc']) + ->getMockForAbstractClass(); + + $modelMock->method('getImgSrc') + ->willReturn($imageSource); + + return $modelMock; } } diff --git a/app/code/Magento/Captcha/Test/Unit/Observer/CheckContactUsFormObserverTest.php b/app/code/Magento/Captcha/Test/Unit/Observer/CheckContactUsFormObserverTest.php index 83bfb2910f9f8..6bb21e9432d70 100644 --- a/app/code/Magento/Captcha/Test/Unit/Observer/CheckContactUsFormObserverTest.php +++ b/app/code/Magento/Captcha/Test/Unit/Observer/CheckContactUsFormObserverTest.php @@ -5,94 +5,108 @@ */ namespace Magento\Captcha\Test\Unit\Observer; +use Magento\Captcha\Helper\Data; +use Magento\Captcha\Model\DefaultModel; +use Magento\Captcha\Observer\CaptchaStringResolver; +use Magento\Captcha\Observer\CheckContactUsFormObserver; +use Magento\Framework\App\Action\Action; +use Magento\Framework\App\ActionFlag; +use Magento\Framework\App\Request\DataPersistorInterface; +use Magento\Framework\App\Request\Http; +use Magento\Framework\App\Response\RedirectInterface; +use Magento\Framework\Event\Observer; +use Magento\Framework\Message\ManagerInterface; +use Magento\Framework\Session\SessionManager; +use Magento\Framework\TestFramework\Unit\Helper\ObjectManager; +use PHPUnit\Framework\MockObject\MockObject; +use PHPUnit\Framework\TestCase; + /** + * Test class for \Magento\Captcha\Observer\CheckContactUsFormObserver + * * @SuppressWarnings(PHPMD.CouplingBetweenObjects) */ -class CheckContactUsFormObserverTest extends \PHPUnit\Framework\TestCase +class CheckContactUsFormObserverTest extends TestCase { /** - * @var \Magento\Captcha\Observer\CheckContactUsFormObserver + * @var ObjectManager */ - protected $checkContactUsFormObserver; + private $objectManagerHelper; /** - * @var \Magento\Captcha\Helper\Data|\PHPUnit_Framework_MockObject_MockObject + * @var CheckContactUsFormObserver */ - protected $helperMock; + private $checkContactUsFormObserver; /** - * @var \Magento\Framework\App\ActionFlag|\PHPUnit_Framework_MockObject_MockObject + * @var Data|MockObject */ - protected $actionFlagMock; + private $helperMock; - /* - * @var \Magento\Framework\Message\ManagerInterface|\PHPUnit_Framework_MockObject_MockObject + /** + * @var ActionFlag|MockObject */ - protected $messageManagerMock; + private $actionFlagMock; /** - * @var \Magento\Framework\App\Response\RedirectInterface|\PHPUnit_Framework_MockObject_MockObject + * @var ManagerInterface|MockObject */ - protected $redirectMock; + private $messageManagerMock; /** - * @var \Magento\Framework\TestFramework\Unit\Helper\ObjectManager + * @var RedirectInterface|MockObject */ - protected $objectManagerHelper; + private $redirectMock; /** - * @var \Magento\Captcha\Observer\CaptchaStringResolver|\PHPUnit_Framework_MockObject_MockObject + * @var CaptchaStringResolver|MockObject */ - protected $captchaStringResolverMock; + private $captchaStringResolverMock; /** - * @var \Magento\Framework\Session\SessionManager|\PHPUnit_Framework_MockObject_MockObject + * @var DataPersistorInterface|MockObject */ - protected $sessionMock; + private $dataPersistorMock; /** - * @var \Magento\Captcha\Model\DefaultModel|\PHPUnit_Framework_MockObject_MockObject + * @var SessionManager|MockObject */ - protected $captchaMock; + private $sessionMock; /** - * @var \Magento\Framework\App\Request\DataPersistorInterface|\PHPUnit_Framework_MockObject_MockObject + * @var DefaultModel|MockObject */ - protected $dataPersistorMock; + private $captchaMock; protected function setUp() { - $this->objectManagerHelper = new \Magento\Framework\TestFramework\Unit\Helper\ObjectManager($this); + $this->objectManagerHelper = new ObjectManager($this); + + $this->helperMock = $this->createMock(Data::class); + $this->actionFlagMock = $this->createMock(ActionFlag::class); + $this->messageManagerMock = $this->createMock(ManagerInterface::class); + $this->redirectMock = $this->createMock(RedirectInterface::class); + $this->captchaStringResolverMock = $this->createMock(CaptchaStringResolver::class); + $this->dataPersistorMock = $this->getMockBuilder(DataPersistorInterface::class) + ->getMockForAbstractClass(); - $this->helperMock = $this->createMock(\Magento\Captcha\Helper\Data::class); - $this->actionFlagMock = $this->createMock(\Magento\Framework\App\ActionFlag::class); - $this->messageManagerMock = $this->createMock(\Magento\Framework\Message\ManagerInterface::class); - $this->redirectMock = $this->createMock(\Magento\Framework\App\Response\RedirectInterface::class); - $this->captchaStringResolverMock = $this->createMock(\Magento\Captcha\Observer\CaptchaStringResolver::class); $this->sessionMock = $this->createPartialMock( - \Magento\Framework\Session\SessionManager::class, + SessionManager::class, ['addErrorMessage'] ); - $this->dataPersistorMock = $this->getMockBuilder(\Magento\Framework\App\Request\DataPersistorInterface::class) - ->getMockForAbstractClass(); + $this->captchaMock = $this->createMock(DefaultModel::class); $this->checkContactUsFormObserver = $this->objectManagerHelper->getObject( - \Magento\Captcha\Observer\CheckContactUsFormObserver::class, + CheckContactUsFormObserver::class, [ 'helper' => $this->helperMock, 'actionFlag' => $this->actionFlagMock, 'messageManager' => $this->messageManagerMock, 'redirect' => $this->redirectMock, - 'captchaStringResolver' => $this->captchaStringResolverMock + 'captchaStringResolver' => $this->captchaStringResolverMock, + 'dataPersistor' => $this->dataPersistorMock ] ); - $this->objectManagerHelper->setBackwardCompatibleProperty( - $this->checkContactUsFormObserver, - 'dataPersistor', - $this->dataPersistorMock - ); - - $this->captchaMock = $this->createMock(\Magento\Captcha\Model\DefaultModel::class); } public function testCheckContactUsFormWhenCaptchaIsRequiredAndValid() @@ -100,14 +114,13 @@ public function testCheckContactUsFormWhenCaptchaIsRequiredAndValid() $formId = 'contact_us'; $captchaValue = 'some-value'; - $controller = $this->createMock(\Magento\Framework\App\Action\Action::class); - $request = $this->createMock(\Magento\Framework\App\Request\Http::class); - $request->expects($this->any()) - ->method('getPost') - ->with(\Magento\Captcha\Helper\Data::INPUT_NAME_FIELD_VALUE, null) + $controller = $this->createMock(Action::class); + $request = $this->createMock(Http::class); + $request->method('getPost') + ->with(Data::INPUT_NAME_FIELD_VALUE, null) ->willReturn([$formId => $captchaValue]); - $controller->expects($this->any())->method('getRequest')->willReturn($request); - $this->captchaMock->expects($this->any())->method('isRequired')->willReturn(true); + $controller->method('getRequest')->willReturn($request); + $this->captchaMock->method('isRequired')->willReturn(true); $this->captchaMock->expects($this->once()) ->method('isCorrect') ->with($captchaValue) @@ -116,13 +129,13 @@ public function testCheckContactUsFormWhenCaptchaIsRequiredAndValid() ->method('resolve') ->with($request, $formId) ->willReturn($captchaValue); - $this->helperMock->expects($this->any()) - ->method('getCaptcha') - ->with($formId)->willReturn($this->captchaMock); + $this->helperMock->method('getCaptcha') + ->with($formId) + ->willReturn($this->captchaMock); $this->sessionMock->expects($this->never())->method('addErrorMessage'); $this->checkContactUsFormObserver->execute( - new \Magento\Framework\Event\Observer(['controller_action' => $controller]) + new Observer(['controller_action' => $controller]) ); } @@ -135,11 +148,10 @@ public function testCheckContactUsFormRedirectsCustomerWithWarningMessageWhenCap $redirectUrl = 'http://magento.com/contacts/'; $postData = ['name' => 'Some Name']; - $request = $this->createMock(\Magento\Framework\App\Request\Http::class); + $request = $this->createMock(Http::class); $response = $this->createMock(\Magento\Framework\App\Response\Http::class); - $request->expects($this->any()) - ->method('getPost') - ->with(\Magento\Captcha\Helper\Data::INPUT_NAME_FIELD_VALUE, null) + $request->method('getPost') + ->with(Data::INPUT_NAME_FIELD_VALUE, null) ->willReturn([$formId => $captchaValue]); $request->expects($this->once()) ->method('getPostValue') @@ -150,10 +162,10 @@ public function testCheckContactUsFormRedirectsCustomerWithWarningMessageWhenCap ->with($response, $redirectRoutePath, []) ->willReturn($redirectUrl); - $controller = $this->createMock(\Magento\Framework\App\Action\Action::class); - $controller->expects($this->any())->method('getRequest')->willReturn($request); - $controller->expects($this->any())->method('getResponse')->willReturn($response); - $this->captchaMock->expects($this->any())->method('isRequired')->willReturn(true); + $controller = $this->createMock(Action::class); + $controller->method('getRequest')->willReturn($request); + $controller->method('getResponse')->willReturn($response); + $this->captchaMock->method('isRequired')->willReturn(true); $this->captchaMock->expects($this->once()) ->method('isCorrect') ->with($captchaValue) @@ -162,32 +174,32 @@ public function testCheckContactUsFormRedirectsCustomerWithWarningMessageWhenCap ->method('resolve') ->with($request, $formId) ->willReturn($captchaValue); - $this->helperMock->expects($this->any()) - ->method('getCaptcha') + $this->helperMock->method('getCaptcha') ->with($formId) ->willReturn($this->captchaMock); - $this->messageManagerMock->expects($this->once())->method('addErrorMessage')->with($warningMessage); + $this->messageManagerMock->expects($this->once()) + ->method('addErrorMessage') + ->with($warningMessage); $this->actionFlagMock->expects($this->once()) ->method('set') - ->with('', \Magento\Framework\App\Action\Action::FLAG_NO_DISPATCH, true); + ->with('', Action::FLAG_NO_DISPATCH, true); $this->dataPersistorMock->expects($this->once()) ->method('set') ->with($formId, $postData); $this->checkContactUsFormObserver->execute( - new \Magento\Framework\Event\Observer(['controller_action' => $controller]) + new Observer(['controller_action' => $controller]) ); } public function testCheckContactUsFormDoesNotCheckCaptchaWhenItIsNotRequired() { - $this->helperMock->expects($this->any()) - ->method('getCaptcha') + $this->helperMock->method('getCaptcha') ->with('contact_us') ->willReturn($this->captchaMock); - $this->captchaMock->expects($this->any())->method('isRequired')->willReturn(false); + $this->captchaMock->method('isRequired')->willReturn(false); $this->captchaMock->expects($this->never())->method('isCorrect'); - $this->checkContactUsFormObserver->execute(new \Magento\Framework\Event\Observer()); + $this->checkContactUsFormObserver->execute(new Observer()); } } diff --git a/app/code/Magento/Captcha/Test/Unit/Observer/CheckUserForgotPasswordBackendObserverTest.php b/app/code/Magento/Captcha/Test/Unit/Observer/CheckUserForgotPasswordBackendObserverTest.php new file mode 100644 index 0000000000000..9d3aa88ed9be4 --- /dev/null +++ b/app/code/Magento/Captcha/Test/Unit/Observer/CheckUserForgotPasswordBackendObserverTest.php @@ -0,0 +1,228 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ + +declare(strict_types=1); + +namespace Magento\Captcha\Test\Unit\Observer; + +use Magento\Captcha\Helper\Data as DataHelper; +use Magento\Captcha\Model\CaptchaInterface; +use Magento\Captcha\Observer\CaptchaStringResolver; +use Magento\Captcha\Observer\CheckUserForgotPasswordBackendObserver; +use Magento\Framework\App\Action\Action; +use Magento\Framework\App\ActionFlag; +use Magento\Framework\App\Request\Http as HttpRequest; +use Magento\Framework\App\Response\Http as HttpResponse; +use Magento\Framework\Event\Observer; +use Magento\Framework\Message\ManagerInterface; +use Magento\Framework\Session\SessionManagerInterface; +use Magento\Framework\TestFramework\Unit\Helper\ObjectManager as ObjectManagerHelper; +use PHPUnit\Framework\MockObject\MockObject; +use PHPUnit\Framework\TestCase; + +/** + * Unit Test for \Magento\Captcha\Observer\CheckUserForgotPasswordBackendObserver + * @SuppressWarnings(PHPMD.CouplingBetweenObjects) + */ +class CheckUserForgotPasswordBackendObserverTest extends TestCase +{ + const STUB_EMAIL = 'stub@test.mail'; + const STUB_REQUEST_PARAMS = ['STUB_PARAM']; + + /** + * @var MockObject|DataHelper + */ + private $helperMock; + + /** + * @var MockObject|CaptchaStringResolver + */ + private $captchaStringResolverMock; + + /** + * @var MockObject|SessionManagerInterface + */ + private $sessionMock; + + /** + * @var MockObject|ActionFlag + */ + private $actionFlagMock; + + /** + * @var MockObject|ManagerInterface + */ + private $messageManagerMock; + + /** + * @var CheckUserForgotPasswordBackendObserver + */ + private $observer; + + /** + * @var MockObject|CaptchaInterface + */ + private $captchaMock; + + /** + * @var MockObject|Observer + */ + private $eventObserverMock; + + /** + * @var MockObject|Action + */ + private $controllerMock; + + /** + * @var MockObject|HttpResponse + */ + private $httpResponseMock; + + /** + * @var MockObject|HttpRequest + */ + private $requestMock; + + /** + * @inheritDoc + */ + protected function setUp() + { + $formId = 'backend_forgotpassword'; + + $this->helperMock = $this->createMock(DataHelper::class); + $this->captchaStringResolverMock = $this->createMock(CaptchaStringResolver::class); + $this->sessionMock = $this->getMockBuilder(SessionManagerInterface::class) + ->setMethods(['setEmail']) + ->getMockForAbstractClass(); + $this->actionFlagMock = $this->createMock(ActionFlag::class); + $this->messageManagerMock = $this->createMock(ManagerInterface::class); + $this->requestMock = $this->createMock(HttpRequest::class); + + $objectManager = new ObjectManagerHelper($this); + $this->observer = $objectManager->getObject( + CheckUserForgotPasswordBackendObserver::class, + [ + '_helper' => $this->helperMock, + 'captchaStringResolver' => $this->captchaStringResolverMock, + '_session' => $this->sessionMock, + '_actionFlag' => $this->actionFlagMock, + 'messageManager' => $this->messageManagerMock, + 'request' => $this->requestMock + ] + ); + + $this->captchaMock = $this->getMockBuilder(CaptchaInterface::class) + ->setMethods(['isRequired', 'isCorrect']) + ->getMockForAbstractClass(); + $this->helperMock->expects($this->once()) + ->method('getCaptcha') + ->with($formId) + ->willReturn($this->captchaMock); + + $this->httpResponseMock = $this->createMock(HttpResponse::class); + + $this->controllerMock = $this->getMockBuilder(Action::class) + ->disableOriginalConstructor() + ->setMethods(['getUrl', 'getResponse']) + ->getMockForAbstractClass(); + $this->controllerMock->expects($this->any()) + ->method('getResponse') + ->willReturn($this->httpResponseMock); + + $this->eventObserverMock = $this->createPartialMock(Observer::class, ['getControllerAction']); + $this->eventObserverMock->expects($this->any()) + ->method('getControllerAction') + ->willReturn($this->controllerMock); + } + + /** + * Test case when Captcha is required and was entered correctly. + */ + public function testExecuteWhenCaptchaIsCorrect() + { + $this->configureRequestMockWithStubValues(); + $this->captchaMock->expects($this->once())->method('isRequired')->willReturn(true); + $this->captchaMock->expects($this->once())->method('isCorrect')->willReturn(true); + + $this->executeOriginalMethodExpectsNoError(); + } + + /** + * Test case when Captcha is required and was entered incorrectly. + */ + public function testExecuteWhenCaptchaIsIncorrect() + { + $this->configureRequestMockWithStubValues(); + $this->captchaMock->expects($this->once())->method('isRequired')->willReturn(true); + $this->captchaMock->expects($this->once())->method('isCorrect')->willReturn(false); + + $this->sessionMock->expects($this->once())->method('setEmail'); + $this->actionFlagMock->expects($this->once())->method('set'); + $this->controllerMock->expects($this->once())->method('getUrl'); + $this->messageManagerMock->expects($this->once()) + ->method('addErrorMessage') + ->with(__('Incorrect CAPTCHA')); + $this->httpResponseMock->expects($this->once())->method('setRedirect')->willReturnSelf(); + + $this->observer->execute($this->eventObserverMock); + } + + /** + * Test case when Captcha is not required. + */ + public function testExecuteWhenCaptchaIsNotRequired() + { + $this->configureRequestMockWithStubValues(); + $this->captchaMock->expects($this->once())->method('isRequired')->willReturn(false); + + $this->executeOriginalMethodExpectsNoError(); + } + + /** + * Test case when email is not provided + */ + public function testExecuteWhenEmailParamIsNotPresent() + { + $this->requestMock->expects($this->any()) + ->method('getParam') + ->with('email') + ->willReturn(null); + $this->requestMock->expects($this->any()) + ->method('getParams') + ->willReturn(self::STUB_REQUEST_PARAMS); + $this->captchaMock->expects($this->never())->method('isRequired'); + $this->captchaMock->expects($this->never())->method('isCorrect'); + + $this->executeOriginalMethodExpectsNoError(); + } + + /** + * Stub params for Request Mock + */ + private function configureRequestMockWithStubValues() + { + $this->requestMock->expects($this->any()) + ->method('getParam') + ->with('email') + ->willReturn(self::STUB_EMAIL); + $this->requestMock->expects($this->any()) + ->method('getParams') + ->willReturn(self::STUB_REQUEST_PARAMS); + } + + /** + * Run original method, expect there is no error + */ + private function executeOriginalMethodExpectsNoError() + { + $this->messageManagerMock->expects($this->never())->method('addErrorMessage'); + $this->httpResponseMock->expects($this->never())->method('setRedirect'); + + $this->observer->execute($this->eventObserverMock); + } +} diff --git a/app/code/Magento/Captcha/Test/Unit/Observer/ResetAttemptForBackendObserverTest.php b/app/code/Magento/Captcha/Test/Unit/Observer/ResetAttemptForBackendObserverTest.php new file mode 100644 index 0000000000000..b984daa8998f3 --- /dev/null +++ b/app/code/Magento/Captcha/Test/Unit/Observer/ResetAttemptForBackendObserverTest.php @@ -0,0 +1,53 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ + +declare(strict_types=1); + +namespace Magento\Captcha\Test\Unit\Observer; + +use Magento\Captcha\Model\ResourceModel\Log; +use Magento\Captcha\Model\ResourceModel\LogFactory; +use Magento\Captcha\Observer\ResetAttemptForBackendObserver; +use Magento\Framework\Event; +use Magento\Framework\Event\Observer; +use Magento\Framework\TestFramework\Unit\Helper\ObjectManager as ObjectManagerHelper; +use PHPUnit\Framework\MockObject\MockObject; +use PHPUnit\Framework\TestCase; + +/** + * Unit test for \Magento\Captcha\Observer\ResetAttemptForBackendObserver + */ +class ResetAttemptForBackendObserverTest extends TestCase +{ + /** + * Test that the method resets attempts for Backend + */ + public function testExecuteExpectsDeleteUserAttemptsCalled() + { + $logMock = $this->createMock(Log::class); + $logMock->expects($this->once())->method('deleteUserAttempts')->willReturnSelf(); + + $resLogFactoryMock = $this->createMock(LogFactory::class); + $resLogFactoryMock->expects($this->once()) + ->method('create') + ->willReturn($logMock); + + /** @var MockObject|Observer $eventObserverMock */ + $eventObserverMock = $this->createPartialMock(Observer::class, ['getUser']); + $eventMock = $this->createMock(Event::class); + $eventObserverMock->expects($this->once()) + ->method('getUser') + ->willReturn($eventMock); + + $objectManager = new ObjectManagerHelper($this); + /** @var ResetAttemptForBackendObserver $observer */ + $observer = $objectManager->getObject( + ResetAttemptForBackendObserver::class, + ['resLogFactory' => $resLogFactoryMock] + ); + $this->assertInstanceOf(Log::class, $observer->execute($eventObserverMock)); + } +} diff --git a/app/code/Magento/Captcha/Test/Unit/Observer/ResetAttemptForFrontendObserverTest.php b/app/code/Magento/Captcha/Test/Unit/Observer/ResetAttemptForFrontendObserverTest.php new file mode 100644 index 0000000000000..11866c266845e --- /dev/null +++ b/app/code/Magento/Captcha/Test/Unit/Observer/ResetAttemptForFrontendObserverTest.php @@ -0,0 +1,52 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ + +declare(strict_types=1); + +namespace Magento\Captcha\Test\Unit\Observer; + +use Magento\Captcha\Model\ResourceModel\Log; +use Magento\Captcha\Model\ResourceModel\LogFactory; +use Magento\Captcha\Observer\ResetAttemptForFrontendObserver; +use Magento\Customer\Model\Customer; +use Magento\Framework\Event\Observer; +use Magento\Framework\TestFramework\Unit\Helper\ObjectManager as ObjectManagerHelper; +use PHPUnit\Framework\MockObject\MockObject; +use PHPUnit\Framework\TestCase; + +/** + * Unit test for \Magento\Captcha\Observer\ResetAttemptForFrontendObserver + */ +class ResetAttemptForFrontendObserverTest extends TestCase +{ + /** + * Test that the method resets attempts for Frontend + */ + public function testExecuteExpectsDeleteUserAttemptsCalled() + { + $logMock = $this->createMock(Log::class); + $logMock->expects($this->once())->method('deleteUserAttempts')->willReturnSelf(); + + $resLogFactoryMock = $this->createMock(LogFactory::class); + $resLogFactoryMock->expects($this->once()) + ->method('create') + ->willReturn($logMock); + + /** @var MockObject|Observer $eventObserverMock */ + $eventObserverMock = $this->createPartialMock(Observer::class, ['getModel']); + $eventObserverMock->expects($this->once()) + ->method('getModel') + ->willReturn($this->createMock(Customer::class)); + + $objectManager = new ObjectManagerHelper($this); + /** @var ResetAttemptForFrontendObserver $observer */ + $observer = $objectManager->getObject( + ResetAttemptForFrontendObserver::class, + ['resLogFactory' => $resLogFactoryMock] + ); + $this->assertInstanceOf(Log::class, $observer->execute($eventObserverMock)); + } +} diff --git a/app/code/Magento/Captcha/composer.json b/app/code/Magento/Captcha/composer.json index 294961118e93a..c6bbcc8e91c3e 100644 --- a/app/code/Magento/Captcha/composer.json +++ b/app/code/Magento/Captcha/composer.json @@ -11,9 +11,9 @@ "magento/module-checkout": "*", "magento/module-customer": "*", "magento/module-store": "*", - "zendframework/zend-captcha": "^2.7.1", - "zendframework/zend-db": "^2.8.2", - "zendframework/zend-session": "^2.7.3" + "laminas/laminas-captcha": "^2.7.1", + "laminas/laminas-db": "^2.8.2", + "laminas/laminas-session": "^2.7.3" }, "type": "magento2-module", "license": [ diff --git a/app/code/Magento/Captcha/etc/adminhtml/system.xml b/app/code/Magento/Captcha/etc/adminhtml/system.xml index a05430989418d..ac4197c976ea0 100644 --- a/app/code/Magento/Captcha/etc/adminhtml/system.xml +++ b/app/code/Magento/Captcha/etc/adminhtml/system.xml @@ -8,34 +8,34 @@ <config xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="urn:magento:module:Magento_Config:etc/system_file.xsd"> <system> <section id="admin"> - <group id="captcha" translate="label" type="text" sortOrder="50" showInDefault="1" showInWebsite="1" showInStore="0"> + <group id="captcha" translate="label" type="text" sortOrder="50" showInDefault="1" showInWebsite="1"> <label>CAPTCHA</label> - <field id="enable" translate="label" type="select" sortOrder="1" showInDefault="1" showInWebsite="0" showInStore="0" canRestore="1"> + <field id="enable" translate="label" type="select" sortOrder="1" showInDefault="1" canRestore="1"> <label>Enable CAPTCHA in Admin</label> <source_model>Magento\Config\Model\Config\Source\Yesno</source_model> </field> - <field id="font" translate="label" type="select" sortOrder="2" showInDefault="1" showInWebsite="0" showInStore="0" canRestore="1"> + <field id="font" translate="label" type="select" sortOrder="2" showInDefault="1" canRestore="1"> <label>Font</label> <source_model>Magento\Captcha\Model\Config\Font</source_model> <depends> <field id="enable">1</field> </depends> </field> - <field id="forms" translate="label" type="multiselect" sortOrder="3" showInDefault="1" showInWebsite="0" showInStore="0" canRestore="1"> + <field id="forms" translate="label" type="multiselect" sortOrder="3" showInDefault="1" canRestore="1"> <label>Forms</label> <source_model>Magento\Captcha\Model\Config\Form\Backend</source_model> <depends> <field id="enable">1</field> </depends> </field> - <field id="mode" translate="label" type="select" sortOrder="4" showInDefault="1" showInWebsite="0" showInStore="0" canRestore="1"> + <field id="mode" translate="label" type="select" sortOrder="4" showInDefault="1" canRestore="1"> <label>Displaying Mode</label> <source_model>Magento\Captcha\Model\Config\Mode</source_model> <depends> <field id="enable">1</field> </depends> </field> - <field id="failed_attempts_login" translate="label comment" type="text" sortOrder="5" showInDefault="1" showInWebsite="0" showInStore="0" canRestore="1"> + <field id="failed_attempts_login" translate="label comment" type="text" sortOrder="5" showInDefault="1" canRestore="1"> <label>Number of Unsuccessful Attempts to Login</label> <comment>If 0 is specified, CAPTCHA on the Login form will be always available.</comment> <depends> @@ -44,14 +44,14 @@ </depends> <frontend_class>required-entry validate-digits</frontend_class> </field> - <field id="timeout" translate="label" type="text" sortOrder="6" showInDefault="1" showInWebsite="0" showInStore="0" canRestore="1"> + <field id="timeout" translate="label" type="text" sortOrder="6" showInDefault="1" canRestore="1"> <label>CAPTCHA Timeout (minutes)</label> <depends> <field id="enable">1</field> </depends> <frontend_class>required-entry validate-digits</frontend_class> </field> - <field id="length" translate="label comment" type="text" sortOrder="7" showInDefault="1" showInWebsite="0" showInStore="0" canRestore="1"> + <field id="length" translate="label comment" type="text" sortOrder="7" showInDefault="1" canRestore="1"> <label>Number of Symbols</label> <comment>Please specify 8 symbols at the most. Range allowed (e.g. 3-5)</comment> <depends> @@ -59,7 +59,7 @@ </depends> <frontend_class>required-entry</frontend_class> </field> - <field id="symbols" translate="label comment" type="text" sortOrder="8" showInDefault="1" showInWebsite="0" showInStore="0" canRestore="1"> + <field id="symbols" translate="label comment" type="text" sortOrder="8" showInDefault="1" canRestore="1"> <label>Symbols Used in CAPTCHA</label> <comment> <![CDATA[Please use only letters (a-z or A-Z) or numbers (0-9) in this field. No spaces or other characters are allowed.<br />Similar looking characters (e.g. "i", "l", "1") decrease chance of correct recognition by customer.]]> @@ -69,7 +69,7 @@ </depends> <frontend_class>required-entry validate-alphanum</frontend_class> </field> - <field id="case_sensitive" translate="label" type="select" sortOrder="9" showInDefault="1" showInWebsite="0" showInStore="0" canRestore="1"> + <field id="case_sensitive" translate="label" type="select" sortOrder="9" showInDefault="1" canRestore="1"> <label>Case Sensitive</label> <source_model>Magento\Config\Model\Config\Source\Yesno</source_model> <depends> @@ -79,20 +79,20 @@ </group> </section> <section id="customer"> - <group id="captcha" translate="label" type="text" sortOrder="110" showInDefault="1" showInWebsite="1" showInStore="0"> + <group id="captcha" translate="label" type="text" sortOrder="110" showInDefault="1" showInWebsite="1"> <label>CAPTCHA</label> - <field id="enable" translate="label" type="select" sortOrder="1" showInDefault="1" showInWebsite="1" showInStore="0" canRestore="1"> + <field id="enable" translate="label" type="select" sortOrder="1" showInDefault="1" showInWebsite="1" canRestore="1"> <label>Enable CAPTCHA on Storefront</label> <source_model>Magento\Config\Model\Config\Source\Yesno</source_model> </field> - <field id="font" translate="label" type="select" sortOrder="2" showInDefault="1" showInWebsite="1" showInStore="0" canRestore="1"> + <field id="font" translate="label" type="select" sortOrder="2" showInDefault="1" showInWebsite="1" canRestore="1"> <label>Font</label> <source_model>Magento\Captcha\Model\Config\Font</source_model> <depends> <field id="enable">1</field> </depends> </field> - <field id="forms" translate="label comment" type="multiselect" sortOrder="3" showInDefault="1" showInWebsite="1" showInStore="0" canRestore="1"> + <field id="forms" translate="label comment" type="multiselect" sortOrder="3" showInDefault="1" showInWebsite="1" canRestore="1"> <label>Forms</label> <source_model>Magento\Captcha\Model\Config\Form\Frontend</source_model> <comment>CAPTCHA for "Create user" and "Forgot password" forms is always enabled if chosen.</comment> @@ -100,14 +100,14 @@ <field id="enable">1</field> </depends> </field> - <field id="mode" translate="label" type="select" sortOrder="4" showInDefault="1" showInWebsite="1" showInStore="0" canRestore="1"> + <field id="mode" translate="label" type="select" sortOrder="4" showInDefault="1" showInWebsite="1" canRestore="1"> <label>Displaying Mode</label> <source_model>Magento\Captcha\Model\Config\Mode</source_model> <depends> <field id="enable">1</field> </depends> </field> - <field id="failed_attempts_login" translate="label comment" type="text" sortOrder="5" showInDefault="1" showInWebsite="1" showInStore="0" canRestore="1"> + <field id="failed_attempts_login" translate="label comment" type="text" sortOrder="5" showInDefault="1" showInWebsite="1" canRestore="1"> <label>Number of Unsuccessful Attempts to Login</label> <comment>If 0 is specified, CAPTCHA on the Login form will be always available.</comment> <depends> @@ -116,14 +116,14 @@ </depends> <frontend_class>required-entry validate-digits</frontend_class> </field> - <field id="timeout" translate="label" type="text" sortOrder="6" showInDefault="1" showInWebsite="1" showInStore="0" canRestore="1"> + <field id="timeout" translate="label" type="text" sortOrder="6" showInDefault="1" showInWebsite="1" canRestore="1"> <label>CAPTCHA Timeout (minutes)</label> <depends> <field id="enable">1</field> </depends> <frontend_class>required-entry validate-digits</frontend_class> </field> - <field id="length" translate="label comment" type="text" sortOrder="7" showInDefault="1" showInWebsite="1" showInStore="0" canRestore="1"> + <field id="length" translate="label comment" type="text" sortOrder="7" showInDefault="1" showInWebsite="1" canRestore="1"> <label>Number of Symbols</label> <comment>Please specify 8 symbols at the most. Range allowed (e.g. 3-5)</comment> <depends> @@ -131,7 +131,7 @@ </depends> <frontend_class>required-entry validate-range range-1-8</frontend_class> </field> - <field id="symbols" translate="label comment" type="text" sortOrder="8" showInDefault="1" showInWebsite="1" showInStore="0" canRestore="1"> + <field id="symbols" translate="label comment" type="text" sortOrder="8" showInDefault="1" showInWebsite="1" canRestore="1"> <label>Symbols Used in CAPTCHA</label> <comment> <![CDATA[Please use only letters (a-z or A-Z) or numbers (0-9) in this field. No spaces or other characters are allowed.<br />Similar looking characters (e.g. "i", "l", "1") decrease chance of correct recognition by customer.]]> @@ -141,7 +141,7 @@ </depends> <frontend_class>required-entry validate-alphanum</frontend_class> </field> - <field id="case_sensitive" translate="label" type="select" sortOrder="9" showInDefault="1" showInWebsite="1" showInStore="0" canRestore="1"> + <field id="case_sensitive" translate="label" type="select" sortOrder="9" showInDefault="1" showInWebsite="1" canRestore="1"> <label>Case Sensitive</label> <source_model>Magento\Config\Model\Config\Source\Yesno</source_model> <depends> diff --git a/app/code/Magento/Captcha/registration.php b/app/code/Magento/Captcha/registration.php index d6c49c719c969..6721e4abcec61 100644 --- a/app/code/Magento/Captcha/registration.php +++ b/app/code/Magento/Captcha/registration.php @@ -4,6 +4,6 @@ * See COPYING.txt for license details. */ -use \Magento\Framework\Component\ComponentRegistrar; +use Magento\Framework\Component\ComponentRegistrar; ComponentRegistrar::register(ComponentRegistrar::MODULE, 'Magento_Captcha', __DIR__); diff --git a/app/code/Magento/CardinalCommerce/Test/Mftf/Test/AdminCardinalCommerceSettingsHiddenTest.xml b/app/code/Magento/CardinalCommerce/Test/Mftf/Test/AdminCardinalCommerceSettingsHiddenTest.xml deleted file mode 100644 index a41b96f0db6e4..0000000000000 --- a/app/code/Magento/CardinalCommerce/Test/Mftf/Test/AdminCardinalCommerceSettingsHiddenTest.xml +++ /dev/null @@ -1,33 +0,0 @@ -<?xml version="1.0" encoding="UTF-8"?> -<!-- - /** - * Copyright © Magento, Inc. All rights reserved. - * See COPYING.txt for license details. - */ ---> -<tests xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" - xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/testSchema.xsd"> - <test name="AdminCardinalCommerceSettingsHiddenTest"> - <annotations> - <features value="CardinalCommerce"/> - <title value="CardinalCommerce settings hidden" /> - <description value="CardinalCommerce config shouldn't be visible if the 3D secure is disabled for Authorize.Net."/> - <severity value="MINOR"/> - </annotations> - <before> - <actionGroup ref="LoginAsAdmin" stepKey="loginAsAdmin"/> - <magentoCLI command="config:set three_d_secure/cardinal/enabled_authorizenet 1" stepKey="enableCardinalCommerce"/> - </before> - - <after> - <actionGroup ref="logout" stepKey="logout"/> - <magentoCLI command="config:set three_d_secure/cardinal/enabled_authorizenet 0" stepKey="disableCardinalCommerce"/> - </after> - - <amOnPage url="{{AdminThreeDSecurePage.url}}" stepKey="openCurrencyOptionsPage" /> - <conditionalClick dependentSelector="{{AdminCardinalCommerceSection.enabled}}" visible="false" selector="{{AdminCardinalCommerceSection.head}}" stepKey="openCollapsibleBlock"/> - <see selector="{{AdminCardinalCommerceSection.environment}}" userInput="Production" stepKey="seeEnvironmentProduction"/> - <selectOption selector="{{AdminCardinalCommerceSection.enabled}}" userInput="0" stepKey="disableCardinalCommerceOption"/> - <dontSeeElement selector="{{AdminCardinalCommerceSection.environment}}" stepKey="dontSeeEnvironmentProduction"/> - </test> -</tests> diff --git a/app/code/Magento/CardinalCommerce/etc/adminhtml/system.xml b/app/code/Magento/CardinalCommerce/etc/adminhtml/system.xml index 046475baba676..6312696ba97e0 100644 --- a/app/code/Magento/CardinalCommerce/etc/adminhtml/system.xml +++ b/app/code/Magento/CardinalCommerce/etc/adminhtml/system.xml @@ -11,11 +11,11 @@ <label>3D Secure</label> <tab>sales</tab> <resource>Magento_Sales::three_d_secure</resource> - <group id="cardinal" type="text" sortOrder="13" showInDefault="1" showInWebsite="1" showInStore="0"> - <group id="config" translate="label comment" sortOrder="15" showInDefault="1" showInWebsite="1" showInStore="0"> + <group id="cardinal" type="text" sortOrder="13" showInDefault="1" showInWebsite="1"> + <group id="config" translate="label comment" sortOrder="15" showInDefault="1" showInWebsite="1"> <label>CardinalCommerce</label> <comment><![CDATA[Please visit <a href="https://www.cardinalcommerce.com/" target="_blank">www.cardinalcommerce.com</a> to get the CardinalCommerce credentials and find out more details about PSD2 SCA requirements. For support contact <a href="mailto:support@cardinalcommerce.com">support@cardinalcommerce.com</a>.]]></comment> - <field id="environment" translate="label" type="select" sortOrder="20" showInDefault="1" showInWebsite="1" showInStore="0"> + <field id="environment" translate="label" type="select" sortOrder="20" showInDefault="1" showInWebsite="1"> <label>Environment</label> <source_model>Magento\CardinalCommerce\Model\Adminhtml\Source\Environment</source_model> <config_path>three_d_secure/cardinal/environment</config_path> @@ -23,7 +23,7 @@ <field id="enabled_authorize">1</field> </depends> </field> - <field id="org_unit_id" translate="label" type="obscure" sortOrder="30" showInDefault="1" showInWebsite="1" showInStore="0"> + <field id="org_unit_id" translate="label" type="obscure" sortOrder="30" showInDefault="1" showInWebsite="1"> <label>Org Unit Id</label> <config_path>three_d_secure/cardinal/org_unit_id</config_path> <backend_model>Magento\Config\Model\Config\Backend\Encrypted</backend_model> @@ -31,7 +31,7 @@ <field id="enabled_authorize">1</field> </depends> </field> - <field id="api_key" translate="label" type="obscure" sortOrder="40" showInDefault="1" showInWebsite="1" showInStore="0"> + <field id="api_key" translate="label" type="obscure" sortOrder="40" showInDefault="1" showInWebsite="1"> <label>API Key</label> <config_path>three_d_secure/cardinal/api_key</config_path> <backend_model>Magento\Config\Model\Config\Backend\Encrypted</backend_model> @@ -39,7 +39,7 @@ <field id="enabled_authorize">1</field> </depends> </field> - <field id="api_identifier" translate="label" type="obscure" sortOrder="50" showInDefault="1" showInWebsite="1" showInStore="0"> + <field id="api_identifier" translate="label" type="obscure" sortOrder="50" showInDefault="1" showInWebsite="1"> <label>API Identifier</label> <config_path>three_d_secure/cardinal/api_identifier</config_path> <backend_model>Magento\Config\Model\Config\Backend\Encrypted</backend_model> @@ -47,7 +47,7 @@ <field id="enabled_authorize">1</field> </depends> </field> - <field id="debug" translate="label" type="select" sortOrder="60" showInDefault="1" showInWebsite="1" showInStore="0"> + <field id="debug" translate="label" type="select" sortOrder="60" showInDefault="1" showInWebsite="1"> <label>Debug</label> <source_model>Magento\Config\Model\Config\Source\Yesno</source_model> <config_path>three_d_secure/cardinal/debug</config_path> diff --git a/app/code/Magento/CardinalCommerce/registration.php b/app/code/Magento/CardinalCommerce/registration.php index 26fb168fb0ae2..714c7692cf4b5 100644 --- a/app/code/Magento/CardinalCommerce/registration.php +++ b/app/code/Magento/CardinalCommerce/registration.php @@ -4,6 +4,6 @@ * See COPYING.txt for license details. */ -use \Magento\Framework\Component\ComponentRegistrar; +use Magento\Framework\Component\ComponentRegistrar; ComponentRegistrar::register(ComponentRegistrar::MODULE, 'Magento_CardinalCommerce', __DIR__); diff --git a/app/code/Magento/Catalog/Api/CategoryListDeleteBySkuInterface.php b/app/code/Magento/Catalog/Api/CategoryListDeleteBySkuInterface.php new file mode 100644 index 0000000000000..62eba5987c35d --- /dev/null +++ b/app/code/Magento/Catalog/Api/CategoryListDeleteBySkuInterface.php @@ -0,0 +1,27 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ + +namespace Magento\Catalog\Api; + +/** + * @api + * @since 100.0.2 + */ +interface CategoryListDeleteBySkuInterface +{ + /** + * Delete by skus list + * + * @param int $categoryId + * @param string[] $productSkuList + * @return bool + * + * @throws \Magento\Framework\Exception\CouldNotSaveException + * @throws \Magento\Framework\Exception\NoSuchEntityException + * @throws \Magento\Framework\Exception\InputException + */ + public function deleteBySkus(int $categoryId, array $productSkuList): bool; +} diff --git a/app/code/Magento/Catalog/Api/Data/ProductRender/FormattedPriceInfoInterface.php b/app/code/Magento/Catalog/Api/Data/ProductRender/FormattedPriceInfoInterface.php index 43e0de4f20176..d111de1b04b94 100644 --- a/app/code/Magento/Catalog/Api/Data/ProductRender/FormattedPriceInfoInterface.php +++ b/app/code/Magento/Catalog/Api/Data/ProductRender/FormattedPriceInfoInterface.php @@ -29,6 +29,7 @@ public function getFinalPrice(); /** * Set the final price: usually it calculated as minimal price of the product + * * Can be different depends on type of product * * @param string $finalPrice @@ -39,6 +40,7 @@ public function setFinalPrice($finalPrice); /** * Retrieve max price of a product + * * E.g. for product with custom options is price with the most expensive custom option * * @return string @@ -57,6 +59,7 @@ public function setMaxPrice($maxPrice); /** * Retrieve the minimal price of the product or variation + * * The minimal price is for example, the lowest price of all variations for complex product * * @return string @@ -66,7 +69,7 @@ public function getMinimalPrice(); /** * Set max regular price - * Max regular price is the same, as maximum price, except of excluding calculating special price and catalogules + * Max regular price is the same, as maximum price, except of excluding calculating special price and catalog rules * in it * * @param string $maxRegularPrice @@ -130,6 +133,7 @@ public function setMinimalPrice($minimalPrice); /** * Regular price - is price of product without discounts and special price with taxes and fixed product tax + * * Usually this price is corresponding to price in admin panel of product * * @return string diff --git a/app/code/Magento/Catalog/Api/ProductLinkManagementInterface.php b/app/code/Magento/Catalog/Api/ProductLinkManagementInterface.php index 8286175123fe2..84cb853ad5982 100644 --- a/app/code/Magento/Catalog/Api/ProductLinkManagementInterface.php +++ b/app/code/Magento/Catalog/Api/ProductLinkManagementInterface.php @@ -17,6 +17,7 @@ interface ProductLinkManagementInterface * * @param string $sku * @param string $type + * @throws \Magento\Framework\Exception\NoSuchEntityException * @return \Magento\Catalog\Api\Data\ProductLinkInterface[] */ public function getLinkedItemsByType($sku, $type); @@ -28,6 +29,7 @@ public function getLinkedItemsByType($sku, $type); * @param \Magento\Catalog\Api\Data\ProductLinkInterface[] $items * @throws \Magento\Framework\Exception\NoSuchEntityException * @throws \Magento\Framework\Exception\CouldNotSaveException + * @throws \Magento\Framework\Exception\InputException * @return bool */ public function setProductLinks($sku, array $items); diff --git a/app/code/Magento/Catalog/Block/Adminhtml/Product/Edit/Action/Attribute/Tab/Attributes.php b/app/code/Magento/Catalog/Block/Adminhtml/Product/Edit/Action/Attribute/Tab/Attributes.php index 2df0ff0b6cd7c..1ebfa14200364 100644 --- a/app/code/Magento/Catalog/Block/Adminhtml/Product/Edit/Action/Attribute/Tab/Attributes.php +++ b/app/code/Magento/Catalog/Block/Adminhtml/Product/Edit/Action/Attribute/Tab/Attributes.php @@ -13,7 +13,18 @@ namespace Magento\Catalog\Block\Adminhtml\Product\Edit\Action\Attribute\Tab; +use Magento\Backend\Block\Template\Context; +use Magento\Backend\Block\Widget\Tab\TabInterface; +use Magento\Catalog\Block\Adminhtml\Form; +use Magento\Catalog\Block\Adminhtml\Product\Helper\Form\Boolean; +use Magento\Catalog\Block\Adminhtml\Product\Helper\Form\Image; +use Magento\Catalog\Block\Adminhtml\Product\Helper\Form\Price; +use Magento\Catalog\Block\Adminhtml\Product\Helper\Form\Weight; +use Magento\Catalog\Helper\Product\Edit\Action\Attribute; +use Magento\Catalog\Model\ProductFactory; use Magento\Framework\Data\Form\Element\AbstractElement; +use Magento\Framework\Data\FormFactory; +use Magento\Framework\Registry; /** * Attributes tab block @@ -23,37 +34,38 @@ * @SuppressWarnings(PHPMD.CouplingBetweenObjects) * @since 100.0.2 */ -class Attributes extends \Magento\Catalog\Block\Adminhtml\Form implements - \Magento\Backend\Block\Widget\Tab\TabInterface +class Attributes extends Form implements TabInterface { /** - * @var \Magento\Catalog\Model\ProductFactory + * @var ProductFactory */ protected $_productFactory; /** - * @var \Magento\Catalog\Helper\Product\Edit\Action\Attribute + * @var Attribute */ protected $_attributeAction; - /** @var array */ + /** + * @var array + */ private $excludeFields; /** - * @param \Magento\Backend\Block\Template\Context $context - * @param \Magento\Framework\Registry $registry - * @param \Magento\Framework\Data\FormFactory $formFactory - * @param \Magento\Catalog\Model\ProductFactory $productFactory - * @param \Magento\Catalog\Helper\Product\Edit\Action\Attribute $attributeAction + * @param Context $context + * @param Registry $registry + * @param FormFactory $formFactory + * @param ProductFactory $productFactory + * @param Attribute $attributeAction * @param array $data * @param array|null $excludeFields */ public function __construct( - \Magento\Backend\Block\Template\Context $context, - \Magento\Framework\Registry $registry, - \Magento\Framework\Data\FormFactory $formFactory, - \Magento\Catalog\Model\ProductFactory $productFactory, - \Magento\Catalog\Helper\Product\Edit\Action\Attribute $attributeAction, + Context $context, + Registry $registry, + FormFactory $formFactory, + ProductFactory $productFactory, + Attribute $attributeAction, array $data = [], array $excludeFields = null ) { @@ -72,7 +84,7 @@ public function __construct( */ protected function _prepareForm(): void { - $this->setFormExcludedFieldList($this->getExcludedFields()); + $this->setFormExcludedFieldList($this->excludeFields); $this->_eventManager->dispatch( 'adminhtml_catalog_product_form_prepare_excluded_field_list', ['object' => $this] @@ -110,10 +122,10 @@ public function getAttributes() protected function _getAdditionalElementTypes() { return [ - 'price' => \Magento\Catalog\Block\Adminhtml\Product\Helper\Form\Price::class, - 'weight' => \Magento\Catalog\Block\Adminhtml\Product\Helper\Form\Weight::class, - 'image' => \Magento\Catalog\Block\Adminhtml\Product\Helper\Form\Image::class, - 'boolean' => \Magento\Catalog\Block\Adminhtml\Product\Helper\Form\Boolean::class + 'price' => Price::class, + 'weight' => Weight::class, + 'image' => Image::class, + 'boolean' => Boolean::class ]; } @@ -129,7 +141,7 @@ protected function _getAdditionalElementHtml($element) $nameAttributeHtml = $element->getExtType() === 'multiple' ? 'name="' . $element->getId() . '_checkbox"' : ''; $elementId = $element->getId(); $dataAttribute = "data-disable='{$elementId}'"; - $dataCheckboxName = "toggle_" . "{$elementId}"; + $dataCheckboxName = "toggle_{$elementId}"; $checkboxLabel = __('Change'); // @codingStandardsIgnoreStart $html = <<<HTML @@ -140,14 +152,8 @@ protected function _getAdditionalElementHtml($element) </label> </span> HTML; - if ($elementId === 'weight') { - $html .= <<<HTML -<script>require(['Magento_Catalog/js/product/weight-handler'], function (weightHandle) { - weightHandle.hideWeightSwitcher(); -});</script> -HTML; - // @codingStandardsIgnoreEnd - } + + // @codingStandardsIgnoreEnd return $html; } @@ -190,14 +196,4 @@ public function isHidden() { return false; } - - /** - * Returns excluded fields - * - * @return array - */ - private function getExcludedFields(): array - { - return $this->excludeFields; - } } diff --git a/app/code/Magento/Catalog/Block/Adminhtml/Product/Edit/Tab/Price/Group/AbstractGroup.php b/app/code/Magento/Catalog/Block/Adminhtml/Product/Edit/Tab/Price/Group/AbstractGroup.php index 5ffd3d1dda38d..df989c5f77174 100644 --- a/app/code/Magento/Catalog/Block/Adminhtml/Product/Edit/Tab/Price/Group/AbstractGroup.php +++ b/app/code/Magento/Catalog/Block/Adminhtml/Product/Edit/Tab/Price/Group/AbstractGroup.php @@ -212,7 +212,7 @@ public function getCustomerGroups($groupId = null) } if ($groupId !== null) { - return isset($this->_customerGroups[$groupId]) ? $this->_customerGroups[$groupId] : []; + return $this->_customerGroups[$groupId] ?? []; } return $this->_customerGroups; diff --git a/app/code/Magento/Catalog/Block/Adminhtml/Product/Helper/Form/Gallery/Content.php b/app/code/Magento/Catalog/Block/Adminhtml/Product/Helper/Form/Gallery/Content.php index f5d0ec7da617e..8e6011c09a27f 100644 --- a/app/code/Magento/Catalog/Block/Adminhtml/Product/Helper/Form/Gallery/Content.php +++ b/app/code/Magento/Catalog/Block/Adminhtml/Product/Helper/Form/Gallery/Content.php @@ -95,7 +95,7 @@ protected function _prepareLayout() ); $this->getUploader()->getConfig()->setUrl( - $this->_urlBuilder->addSessionParam()->getUrl('catalog/product_gallery/upload') + $this->_urlBuilder->getUrl('catalog/product_gallery/upload') )->setFileField( 'image' )->setFilters( diff --git a/app/code/Magento/Catalog/Block/Adminhtml/Product/Helper/Form/Weight.php b/app/code/Magento/Catalog/Block/Adminhtml/Product/Helper/Form/Weight.php index fec3daeeacd13..70b2948501d2d 100644 --- a/app/code/Magento/Catalog/Block/Adminhtml/Product/Helper/Form/Weight.php +++ b/app/code/Magento/Catalog/Block/Adminhtml/Product/Helper/Form/Weight.php @@ -3,55 +3,64 @@ * Copyright © Magento, Inc. All rights reserved. * See COPYING.txt for license details. */ +declare(strict_types=1); -/** - * Product form weight field helper - */ namespace Magento\Catalog\Block\Adminhtml\Product\Helper\Form; +use Magento\Catalog\Api\Data\ProductAttributeInterface; +use Magento\Directory\Helper\Data; use Magento\Framework\Data\Form; use Magento\Catalog\Model\Product\Edit\WeightResolver; +use Magento\Framework\Data\Form\Element\CollectionFactory; +use Magento\Framework\Data\Form\Element\Factory; +use Magento\Framework\Data\Form\Element\Radios; +use Magento\Framework\Data\Form\Element\Text; +use Magento\Framework\Escaper; +use Magento\Framework\Locale\Format; -class Weight extends \Magento\Framework\Data\Form\Element\Text +/** + * Product form weight field helper + */ +class Weight extends Text { /** * Weight switcher radio-button element * - * @var \Magento\Framework\Data\Form\Element\Checkbox + * @var Radios */ protected $weightSwitcher; /** - * @var \Magento\Framework\Locale\Format + * @var Format */ protected $localeFormat; /** - * @var \Magento\Directory\Helper\Data + * @var Data */ protected $directoryHelper; /** - * @param \Magento\Framework\Data\Form\Element\Factory $factoryElement - * @param \Magento\Framework\Data\Form\Element\CollectionFactory $factoryCollection - * @param \Magento\Framework\Escaper $escaper - * @param \Magento\Framework\Locale\Format $localeFormat - * @param \Magento\Directory\Helper\Data $directoryHelper + * @param Factory $factoryElement + * @param CollectionFactory $factoryCollection + * @param Escaper $escaper + * @param Format $localeFormat + * @param Data $directoryHelper * @param array $data */ public function __construct( - \Magento\Framework\Data\Form\Element\Factory $factoryElement, - \Magento\Framework\Data\Form\Element\CollectionFactory $factoryCollection, - \Magento\Framework\Escaper $escaper, - \Magento\Framework\Locale\Format $localeFormat, - \Magento\Directory\Helper\Data $directoryHelper, + Factory $factoryElement, + CollectionFactory $factoryCollection, + Escaper $escaper, + Format $localeFormat, + Data $directoryHelper, array $data = [] ) { $this->directoryHelper = $directoryHelper; $this->localeFormat = $localeFormat; $this->weightSwitcher = $factoryElement->create('radios'); $this->weightSwitcher->setValue( - WeightResolver::HAS_WEIGHT + WeightResolver::HAS_NO_WEIGHT )->setValues( [ ['value' => WeightResolver::HAS_WEIGHT, 'label' => __('Yes')], @@ -75,28 +84,48 @@ public function __construct( */ public function getElementHtml() { - if (!$this->getForm()->getDataObject()->getTypeInstance()->hasWeight()) { - $this->weightSwitcher->setValue(WeightResolver::HAS_NO_WEIGHT); + if ($this->getForm()->getDataObject()->getTypeInstance()->hasWeight()) { + $this->weightSwitcher->setValue(WeightResolver::HAS_WEIGHT); } + if ($this->getDisabled()) { $this->weightSwitcher->setDisabled($this->getDisabled()); } - return '<div class="admin__field-control weight-switcher">' . - '<div class="admin__control-switcher" data-role="weight-switcher">' . - $this->weightSwitcher->getLabelHtml() . - '<div class="admin__field-control-group">' . - $this->weightSwitcher->getElementHtml() . - '</div>' . - '</div>' . - '<div class="admin__control-addon">' . - parent::getElementHtml() . - '<label class="admin__addon-suffix" for="' . - $this->getHtmlId() . - '"><span>' . - $this->directoryHelper->getWeightUnit() . - '</span></label>' . - '</div>' . - '</div>'; + + $htmlId = $this->getHtmlId(); + $html = ''; + + if ($beforeElementHtml = $this->getBeforeElementHtml()) { + $html .= '<label class="addbefore" for="' . $htmlId . '">' . $beforeElementHtml . '</label>'; + } + + $html .= '<div class="admin__control-addon">'; + + if (is_array($this->getValue())) { + foreach ($this->getValue() as $value) { + $html .= $this->getHtmlForInputByValue($this->_escape($value)); + } + } else { + $html .= $this->getHtmlForInputByValue($this->getEscapedValue()); + } + + $html .= '<label class="admin__addon-suffix" for="' . + $this->getHtmlId() . + '"><span>' . + $this->directoryHelper->getWeightUnit() . + '</span></label></div>'; + + if ($afterElementJs = $this->getAfterElementJs()) { + $html .= $afterElementJs; + } + + if ($afterElementHtml = $this->getAfterElementHtml()) { + $html .= '<label class="addafter" for="' . $htmlId . '">' . $afterElementHtml . '</label>'; + } + + $html .= $this->getHtmlForWeightSwitcher(); + + return $html; } /** @@ -112,8 +141,7 @@ public function setForm($form) } /** - * @param null|int|string $index - * @return null|string + * @inheritDoc * @SuppressWarnings(PHPMD.UnusedFormalParameter) */ public function getEscapedValue($index = null) @@ -134,4 +162,53 @@ public function getEscapedValue($index = null) return $value; } + + /** + * Get input html by sting value. + * + * @param string|null $value + * + * @return string + */ + private function getHtmlForInputByValue($value) + { + return '<input id="' . $this->getHtmlId() . '" name="' . $this->getName() . '" ' . $this->_getUiId() + . ' value="' . $value . '" ' . $this->serialize($this->getHtmlAttributes()) . '/>'; + } + + /** + * Get weight switcher html. + * + * @return string + */ + private function getHtmlForWeightSwitcher() + { + $html = '<div class="admin__control-addon">'; + $html .= '<div class="admin__field-control weight-switcher">' . + '<div class="admin__control-switcher" data-role="weight-switcher">' . + $this->weightSwitcher->getLabelHtml() . + '<div class="admin__field-control-group">' . + $this->weightSwitcher->getElementHtml() . + '</div>' . + '</div>'; + + $html .= '<label class="addafter">'; + $elementId = ProductAttributeInterface::CODE_HAS_WEIGHT; + $nameAttributeHtml = 'name="' . $elementId . '_checkbox"'; + $dataCheckboxName = "toggle_{$elementId}"; + $checkboxLabel = __('Change'); + $html .= <<<HTML +<span class="attribute-change-checkbox"> + <input type="checkbox" id="$dataCheckboxName" name="$dataCheckboxName" class="checkbox" $nameAttributeHtml + onclick="toogleFieldEditMode(this, 'weight-switcher1'); toogleFieldEditMode(this, 'weight-switcher0');" /> + <label class="label" for="$dataCheckboxName"> + {$checkboxLabel} + </label> +</span> +HTML; + + $html .= '</label></div></div>'; + + return $html; + } } diff --git a/app/code/Magento/Catalog/Block/Product/Image.php b/app/code/Magento/Catalog/Block/Product/Image.php index 7a7f9c0affc7d..ccc37029bedf7 100644 --- a/app/code/Magento/Catalog/Block/Product/Image.php +++ b/app/code/Magento/Catalog/Block/Product/Image.php @@ -14,7 +14,7 @@ * @method string getHeight() * @method string getLabel() * @method float getRatio() - * @method string getCustomAttributes() + * @method array getCustomAttributes() * @method string getClass() * @since 100.0.2 */ diff --git a/app/code/Magento/Catalog/Block/Product/ImageFactory.php b/app/code/Magento/Catalog/Block/Product/ImageFactory.php index 172cd794edfb9..d43a77695e363 100644 --- a/app/code/Magento/Catalog/Block/Product/ImageFactory.php +++ b/app/code/Magento/Catalog/Block/Product/ImageFactory.php @@ -68,20 +68,17 @@ public function __construct( } /** - * Retrieve image custom attributes for HTML element + * Remove class from custom attributes * * @param array $attributes - * @return string + * @return array */ - private function getStringCustomAttributes(array $attributes): string + private function filterCustomAttributes(array $attributes): array { - $result = []; - foreach ($attributes as $name => $value) { - if ($name != 'class') { - $result[] = $name . '="' . $value . '"'; - } + if (isset($attributes['class'])) { + unset($attributes['class']); } - return !empty($result) ? implode(' ', $result) : ''; + return $attributes; } /** @@ -123,7 +120,7 @@ private function getLabel(Product $product, string $imageType): string if (empty($label)) { $label = $product->getName(); } - return (string) $label; + return (string)$label; } /** @@ -161,7 +158,7 @@ public function create(Product $product, string $imageId, array $attributes = nu } $attributes = $attributes === null ? [] : $attributes; - + $data = [ 'data' => [ 'template' => 'Magento_Catalog::product/image_with_borders.phtml', @@ -169,8 +166,8 @@ public function create(Product $product, string $imageId, array $attributes = nu 'width' => $imageMiscParams['image_width'], 'height' => $imageMiscParams['image_height'], 'label' => $this->getLabel($product, $imageMiscParams['image_type']), - 'ratio' => $this->getRatio($imageMiscParams['image_width'], $imageMiscParams['image_height']), - 'custom_attributes' => $this->getStringCustomAttributes($attributes), + 'ratio' => $this->getRatio($imageMiscParams['image_width'] ?? 0, $imageMiscParams['image_height'] ?? 0), + 'custom_attributes' => $this->filterCustomAttributes($attributes), 'class' => $this->getClass($attributes), 'product_id' => $product->getId() ], diff --git a/app/code/Magento/Catalog/Block/Product/ListProduct.php b/app/code/Magento/Catalog/Block/Product/ListProduct.php index 144cb682e2d22..59f5cc1b53b26 100644 --- a/app/code/Magento/Catalog/Block/Product/ListProduct.php +++ b/app/code/Magento/Catalog/Block/Product/ListProduct.php @@ -355,6 +355,7 @@ public function getIdentities() } foreach ($this->_getProductCollection() as $item) { + // phpcs:ignore Magento2.Performance.ForeachArrayMerge $identities = array_merge($identities, $item->getIdentities()); } @@ -475,8 +476,6 @@ private function initializeProductCollection() $layer->setCurrentCategory($origCategory); } - $this->addToolbarBlock($collection); - $this->_eventManager->dispatch( 'catalog_block_product_list_collection', ['collection' => $collection] diff --git a/app/code/Magento/Catalog/Block/Product/ProductList/Related.php b/app/code/Magento/Catalog/Block/Product/ProductList/Related.php index 24811d61a7715..387fac770c5bc 100644 --- a/app/code/Magento/Catalog/Block/Product/ProductList/Related.php +++ b/app/code/Magento/Catalog/Block/Product/ProductList/Related.php @@ -6,7 +6,15 @@ namespace Magento\Catalog\Block\Product\ProductList; +use Magento\Catalog\Block\Product\AbstractProduct; +use Magento\Catalog\Block\Product\Context; +use Magento\Catalog\Model\Product; +use Magento\Catalog\Model\Product\Visibility as ProductVisibility; use Magento\Catalog\Model\ResourceModel\Product\Collection; +use Magento\Checkout\Model\ResourceModel\Cart as CartResourceModel; +use Magento\Checkout\Model\Session as CheckoutSession; +use Magento\Framework\DataObject\IdentityInterface; +use Magento\Framework\Module\Manager; use Magento\Framework\View\Element\AbstractBlock; /** @@ -16,8 +24,7 @@ * @SuppressWarnings(PHPMD.LongVariable) * @since 100.0.2 */ -class Related extends \Magento\Catalog\Block\Product\AbstractProduct implements - \Magento\Framework\DataObject\IdentityInterface +class Related extends AbstractProduct implements IdentityInterface { /** * @var Collection @@ -27,53 +34,50 @@ class Related extends \Magento\Catalog\Block\Product\AbstractProduct implements /** * Checkout session * - * @var \Magento\Checkout\Model\Session + * @var CheckoutSession */ protected $_checkoutSession; /** * Catalog product visibility * - * @var \Magento\Catalog\Model\Product\Visibility + * @var ProductVisibility */ protected $_catalogProductVisibility; /** * Checkout cart * - * @var \Magento\Checkout\Model\ResourceModel\Cart + * @var CartResourceModel */ protected $_checkoutCart; /** - * @var \Magento\Framework\Module\Manager + * @var Manager */ protected $moduleManager; /** - * @param \Magento\Catalog\Block\Product\Context $context - * @param \Magento\Checkout\Model\ResourceModel\Cart $checkoutCart - * @param \Magento\Catalog\Model\Product\Visibility $catalogProductVisibility - * @param \Magento\Checkout\Model\Session $checkoutSession - * @param \Magento\Framework\Module\Manager $moduleManager + * @param Context $context + * @param CartResourceModel $checkoutCart + * @param ProductVisibility $catalogProductVisibility + * @param CheckoutSession $checkoutSession + * @param Manager $moduleManager * @param array $data */ public function __construct( - \Magento\Catalog\Block\Product\Context $context, - \Magento\Checkout\Model\ResourceModel\Cart $checkoutCart, - \Magento\Catalog\Model\Product\Visibility $catalogProductVisibility, - \Magento\Checkout\Model\Session $checkoutSession, - \Magento\Framework\Module\Manager $moduleManager, + Context $context, + CartResourceModel $checkoutCart, + ProductVisibility $catalogProductVisibility, + CheckoutSession $checkoutSession, + Manager $moduleManager, array $data = [] ) { $this->_checkoutCart = $checkoutCart; $this->_catalogProductVisibility = $catalogProductVisibility; $this->_checkoutSession = $checkoutSession; $this->moduleManager = $moduleManager; - parent::__construct( - $context, - $data - ); + parent::__construct($context, $data); } /** @@ -84,7 +88,7 @@ public function __construct( protected function _prepareData() { $product = $this->getProduct(); - /* @var $product \Magento\Catalog\Model\Product */ + /* @var $product Product */ $this->_itemCollection = $product->getRelatedProductCollection()->addAttributeToSelect( 'required_options' @@ -139,12 +143,11 @@ public function getItems() */ public function getIdentities() { - $identities = []; + $identities = [[]]; foreach ($this->getItems() as $item) { - // phpcs:ignore Magento2.Performance.ForeachArrayMerge - $identities = array_merge($identities, $item->getIdentities()); + $identities[] = $item->getIdentities(); } - return $identities; + return array_merge(...$identities); } /** diff --git a/app/code/Magento/Catalog/Block/Product/ProductList/Toolbar.php b/app/code/Magento/Catalog/Block/Product/ProductList/Toolbar.php index c530ba4785ad9..48725331b27da 100644 --- a/app/code/Magento/Catalog/Block/Product/ProductList/Toolbar.php +++ b/app/code/Magento/Catalog/Block/Product/ProductList/Toolbar.php @@ -263,7 +263,7 @@ public function getCurrentPage() } /** - * Get grit products sort order field + * Get grid products sort order field * * @return string */ diff --git a/app/code/Magento/Catalog/Block/Product/ProductList/Upsell.php b/app/code/Magento/Catalog/Block/Product/ProductList/Upsell.php index fa1beaf6e0ea8..ac66392efe5dc 100644 --- a/app/code/Magento/Catalog/Block/Product/ProductList/Upsell.php +++ b/app/code/Magento/Catalog/Block/Product/ProductList/Upsell.php @@ -6,7 +6,16 @@ namespace Magento\Catalog\Block\Product\ProductList; +use Magento\Catalog\Block\Product\AbstractProduct; +use Magento\Catalog\Block\Product\Context; +use Magento\Catalog\Model\Product; +use Magento\Catalog\Model\Product\Visibility as ProductVisibility; use Magento\Catalog\Model\ResourceModel\Product\Collection; +use Magento\Checkout\Model\ResourceModel\Cart as CartResourceModel; +use Magento\Checkout\Model\Session as CheckoutSession; +use Magento\Framework\DataObject; +use Magento\Framework\DataObject\IdentityInterface; +use Magento\Framework\Module\Manager; /** * Catalog product upsell items block @@ -15,8 +24,7 @@ * @SuppressWarnings(PHPMD.LongVariable) * @since 100.0.2 */ -class Upsell extends \Magento\Catalog\Block\Product\AbstractProduct implements - \Magento\Framework\DataObject\IdentityInterface +class Upsell extends AbstractProduct implements IdentityInterface { /** * @var int @@ -24,7 +32,7 @@ class Upsell extends \Magento\Catalog\Block\Product\AbstractProduct implements protected $_columnCount = 4; /** - * @var \Magento\Framework\DataObject[] + * @var DataObject[] */ protected $_items; @@ -41,53 +49,50 @@ class Upsell extends \Magento\Catalog\Block\Product\AbstractProduct implements /** * Checkout session * - * @var \Magento\Checkout\Model\Session + * @var CheckoutSession */ protected $_checkoutSession; /** * Catalog product visibility * - * @var \Magento\Catalog\Model\Product\Visibility + * @var ProductVisibility */ protected $_catalogProductVisibility; /** * Checkout cart * - * @var \Magento\Checkout\Model\ResourceModel\Cart + * @var CartResourceModel */ protected $_checkoutCart; /** - * @var \Magento\Framework\Module\Manager + * @var Manager */ protected $moduleManager; /** - * @param \Magento\Catalog\Block\Product\Context $context - * @param \Magento\Checkout\Model\ResourceModel\Cart $checkoutCart - * @param \Magento\Catalog\Model\Product\Visibility $catalogProductVisibility - * @param \Magento\Checkout\Model\Session $checkoutSession - * @param \Magento\Framework\Module\Manager $moduleManager + * @param Context $context + * @param CartResourceModel $checkoutCart + * @param ProductVisibility $catalogProductVisibility + * @param CheckoutSession $checkoutSession + * @param Manager $moduleManager * @param array $data */ public function __construct( - \Magento\Catalog\Block\Product\Context $context, - \Magento\Checkout\Model\ResourceModel\Cart $checkoutCart, - \Magento\Catalog\Model\Product\Visibility $catalogProductVisibility, - \Magento\Checkout\Model\Session $checkoutSession, - \Magento\Framework\Module\Manager $moduleManager, + Context $context, + CartResourceModel $checkoutCart, + ProductVisibility $catalogProductVisibility, + CheckoutSession $checkoutSession, + Manager $moduleManager, array $data = [] ) { $this->_checkoutCart = $checkoutCart; $this->_catalogProductVisibility = $catalogProductVisibility; $this->_checkoutSession = $checkoutSession; $this->moduleManager = $moduleManager; - parent::__construct( - $context, - $data - ); + parent::__construct($context, $data); } /** @@ -98,7 +103,7 @@ public function __construct( protected function _prepareData() { $product = $this->getProduct(); - /* @var $product \Magento\Catalog\Model\Product */ + /* @var $product Product */ $this->_itemCollection = $product->getUpSellProductCollection()->setPositionOrder()->addStoreFilter(); if ($this->moduleManager->isEnabled('Magento_Checkout')) { $this->_addProductAttributesAndPrices($this->_itemCollection); @@ -181,8 +186,8 @@ public function getRowCount() */ public function setColumnCount($columns) { - if ((int) $columns > 0) { - $this->_columnCount = (int) $columns; + if ((int)$columns > 0) { + $this->_columnCount = (int)$columns; } return $this; } @@ -231,8 +236,8 @@ public function getIterableItem() */ public function setItemLimit($type, $limit) { - if ((int) $limit > 0) { - $this->_itemLimits[$type] = (int) $limit; + if ((int)$limit > 0) { + $this->_itemLimits[$type] = (int)$limit; } return $this; } @@ -250,9 +255,9 @@ public function getItemLimit($type = '') } if (isset($this->_itemLimits[$type])) { return $this->_itemLimits[$type]; - } else { - return 0; } + + return 0; } /** @@ -262,11 +267,10 @@ public function getItemLimit($type = '') */ public function getIdentities() { - $identities = []; - foreach ($this->getItems() as $item) { - // phpcs:ignore Magento2.Performance.ForeachArrayMerge - $identities = array_merge($identities, $item->getIdentities()); - } - return $identities; + $identities = array_map(function (DataObject $item) { + return $item->getIdentities(); + }, $this->getItems()) ?: [[]]; + + return array_merge(...$identities); } } diff --git a/app/code/Magento/Catalog/Block/Product/View.php b/app/code/Magento/Catalog/Block/Product/View.php index ed6278c2b585d..437171bcb4bc6 100644 --- a/app/code/Magento/Catalog/Block/Product/View.php +++ b/app/code/Magento/Catalog/Block/Product/View.php @@ -323,9 +323,9 @@ public function getQuantityValidators() */ public function getIdentities() { - $identities = $this->getProduct()->getIdentities(); + $product = $this->getProduct(); - return $identities; + return $product ? $product->getIdentities() : []; } /** diff --git a/app/code/Magento/Catalog/Block/Product/View/Options/AbstractOptions.php b/app/code/Magento/Catalog/Block/Product/View/Options/AbstractOptions.php index 030b6e1d2204c..0bfdcc678e9f7 100644 --- a/app/code/Magento/Catalog/Block/Product/View/Options/AbstractOptions.php +++ b/app/code/Magento/Catalog/Block/Product/View/Options/AbstractOptions.php @@ -3,16 +3,14 @@ * Copyright © Magento, Inc. All rights reserved. * See COPYING.txt for license details. */ - -/** - * Product options abstract type block - * - * @author Magento Core Team <core@magentocommerce.com> - */ +declare(strict_types=1); namespace Magento\Catalog\Block\Product\View\Options; +use Magento\Catalog\Pricing\Price\BasePrice; +use Magento\Catalog\Pricing\Price\CalculateCustomOptionCatalogRule; use Magento\Catalog\Pricing\Price\CustomOptionPriceInterface; +use Magento\Framework\App\ObjectManager; /** * Product options section abstract block. @@ -47,20 +45,29 @@ abstract class AbstractOptions extends \Magento\Framework\View\Element\Template */ protected $_catalogHelper; + /** + * @var CalculateCustomOptionCatalogRule + */ + private $calculateCustomOptionCatalogRule; + /** * @param \Magento\Framework\View\Element\Template\Context $context * @param \Magento\Framework\Pricing\Helper\Data $pricingHelper * @param \Magento\Catalog\Helper\Data $catalogData * @param array $data + * @param CalculateCustomOptionCatalogRule $calculateCustomOptionCatalogRule */ public function __construct( \Magento\Framework\View\Element\Template\Context $context, \Magento\Framework\Pricing\Helper\Data $pricingHelper, \Magento\Catalog\Helper\Data $catalogData, - array $data = [] + array $data = [], + CalculateCustomOptionCatalogRule $calculateCustomOptionCatalogRule = null ) { $this->pricingHelper = $pricingHelper; $this->_catalogHelper = $catalogData; + $this->calculateCustomOptionCatalogRule = $calculateCustomOptionCatalogRule + ?? ObjectManager::getInstance()->get(CalculateCustomOptionCatalogRule::class); parent::__construct($context, $data); } @@ -161,6 +168,15 @@ protected function _formatPrice($value, $flag = true) $priceStr = $sign; $customOptionPrice = $this->getProduct()->getPriceInfo()->getPrice('custom_option_price'); + + if (!$value['is_percent']) { + $value['pricing_value'] = $this->calculateCustomOptionCatalogRule->execute( + $this->getProduct(), + (float)$value['pricing_value'], + (bool)$value['is_percent'] + ); + } + $context = [CustomOptionPriceInterface::CONFIGURATION_OPTION_FLAG => true]; $optionAmount = $customOptionPrice->getCustomAmount($value['pricing_value'], null, $context); $priceStr .= $this->getLayout()->getBlock('product.price.render.default')->renderAmount( diff --git a/app/code/Magento/Catalog/Block/Ui/ProductViewCounter.php b/app/code/Magento/Catalog/Block/Ui/ProductViewCounter.php index dd2e23e67f3d7..6d96ba8e1880e 100644 --- a/app/code/Magento/Catalog/Block/Ui/ProductViewCounter.php +++ b/app/code/Magento/Catalog/Block/Ui/ProductViewCounter.php @@ -6,15 +6,17 @@ namespace Magento\Catalog\Block\Ui; use Magento\Catalog\Api\Data\ProductInterface; +use Magento\Catalog\Model\ProductRenderFactory; +use Magento\Catalog\Model\ProductRepository; use Magento\Catalog\Ui\DataProvider\Product\ProductRenderCollectorComposite; +use Magento\Framework\App\Config\ScopeConfigInterface; +use Magento\Framework\App\ObjectManager; +use Magento\Framework\EntityManager\Hydrator; use Magento\Framework\Registry; use Magento\Framework\Serialize\SerializerInterface; use Magento\Framework\Url; use Magento\Framework\View\Element\Template; use Magento\Store\Model\Store; -use Magento\Catalog\Model\ProductRenderFactory; -use Magento\Catalog\Model\ProductRepository; -use Magento\Framework\EntityManager\Hydrator; use Magento\Store\Model\StoreManager; /** @@ -25,6 +27,7 @@ * * @api * @since 101.1.0 + * @SuppressWarnings(PHPMD.CouplingBetweenObjects) */ class ProductViewCounter extends Template { @@ -68,6 +71,13 @@ class ProductViewCounter extends Template */ private $registry; + /** + * Core store config + * + * @var ScopeConfigInterface + */ + private $scopeConfig; + /** * @param Template\Context $context * @param ProductRepository $productRepository @@ -78,6 +88,8 @@ class ProductViewCounter extends Template * @param SerializerInterface $serialize * @param Url $url * @param Registry $registry + * @param ScopeConfigInterface|null $scopeConfig + * @SuppressWarnings(PHPMD.ExcessiveParameterList) */ public function __construct( Template\Context $context, @@ -88,7 +100,8 @@ public function __construct( Hydrator $hydrator, SerializerInterface $serialize, Url $url, - Registry $registry + Registry $registry, + ?ScopeConfigInterface $scopeConfig = null ) { parent::__construct($context); $this->productRepository = $productRepository; @@ -99,6 +112,7 @@ public function __construct( $this->serialize = $serialize; $this->url = $url; $this->registry = $registry; + $this->scopeConfig = $scopeConfig ?? ObjectManager::getInstance()->get(ScopeConfigInterface::class); } /** @@ -116,6 +130,10 @@ public function getCurrentProductData() { /** @var ProductInterface $product */ $product = $this->registry->registry('product'); + $productsScope = $this->scopeConfig->getValue( + 'catalog/recently_products/scope', + \Magento\Store\Model\ScopeInterface::SCOPE_WEBSITE + ); /** @var Store $store */ $store = $this->storeManager->getStore(); @@ -123,7 +141,8 @@ public function getCurrentProductData() return $this->serialize->serialize([ 'items' => [], 'store' => $store->getId(), - 'currency' => $store->getCurrentCurrency()->getCode() + 'currency' => $store->getCurrentCurrency()->getCode(), + 'productCurrentScope' => $productsScope ]); } @@ -140,7 +159,8 @@ public function getCurrentProductData() $product->getId() => $data ], 'store' => $store->getId(), - 'currency' => $store->getCurrentCurrency()->getCode() + 'currency' => $store->getCurrentCurrency()->getCode(), + 'productCurrentScope' => $productsScope ]; return $this->serialize->serialize($currentProductData); diff --git a/app/code/Magento/Catalog/Controller/Adminhtml/Product/Action/Attribute/Save.php b/app/code/Magento/Catalog/Controller/Adminhtml/Product/Action/Attribute/Save.php index 2d10d7148fb71..696401e5430d6 100644 --- a/app/code/Magento/Catalog/Controller/Adminhtml/Product/Action/Attribute/Save.php +++ b/app/code/Magento/Catalog/Controller/Adminhtml/Product/Action/Attribute/Save.php @@ -7,6 +7,7 @@ namespace Magento\Catalog\Controller\Adminhtml\Product\Action\Attribute; use Magento\AsynchronousOperations\Api\Data\OperationInterface; +use Magento\Catalog\Api\Data\ProductAttributeInterface; use Magento\Eav\Model\Config; use Magento\Framework\App\Action\HttpPostActionInterface; use Magento\Backend\App\Action; @@ -14,7 +15,7 @@ use Magento\Framework\Stdlib\DateTime\TimezoneInterface; /** - * Class Save + * Class used for saving mass updated products attributes. * @SuppressWarnings(PHPMD.CouplingBetweenObjects) */ class Save extends \Magento\Catalog\Controller\Adminhtml\Product\Action\Attribute implements HttpPostActionInterface @@ -146,6 +147,10 @@ private function sanitizeProductAttributes($attributesData) $dateFormat = $this->timezone->getDateFormat(\IntlDateFormatter::SHORT); foreach ($attributesData as $attributeCode => $value) { + if ($attributeCode === ProductAttributeInterface::CODE_HAS_WEIGHT) { + continue; + } + $attribute = $this->eavConfig->getAttribute(\Magento\Catalog\Model\Product::ENTITY, $attributeCode); if (!$attribute->getAttributeId()) { unset($attributesData[$attributeCode]); diff --git a/app/code/Magento/Catalog/Controller/Adminhtml/Product/AddAttributeToTemplate.php b/app/code/Magento/Catalog/Controller/Adminhtml/Product/AddAttributeToTemplate.php index a05602403e08f..21312e4614dc3 100644 --- a/app/code/Magento/Catalog/Controller/Adminhtml/Product/AddAttributeToTemplate.php +++ b/app/code/Magento/Catalog/Controller/Adminhtml/Product/AddAttributeToTemplate.php @@ -17,9 +17,11 @@ use Magento\Eav\Api\Data\AttributeGroupInterfaceFactory; use Magento\Eav\Api\Data\AttributeInterface; use Magento\Eav\Api\Data\AttributeSetInterface; +use Magento\Eav\Model\Cache\Type as CacheType; use Magento\Framework\Api\ExtensionAttributesFactory; use Magento\Framework\Api\SearchCriteriaBuilder; use Magento\Framework\App\Action\HttpPostActionInterface; +use Magento\Framework\App\CacheInterface; use Magento\Framework\App\ObjectManager; use Magento\Framework\Controller\Result\Json; use Magento\Framework\Controller\Result\JsonFactory; @@ -29,7 +31,7 @@ use Psr\Log\LoggerInterface; /** - * Class AddAttributeToTemplate + * Assign attribute to attribute set. * * @SuppressWarnings(PHPMD.CouplingBetweenObjects) */ @@ -80,6 +82,11 @@ class AddAttributeToTemplate extends Product implements HttpPostActionInterface */ protected $extensionAttributesFactory; + /** + * @var CacheInterface + */ + private $cache; + /** * Constructor * @@ -94,8 +101,8 @@ class AddAttributeToTemplate extends Product implements HttpPostActionInterface * @param AttributeManagementInterface $attributeManagement * @param LoggerInterface $logger * @param ExtensionAttributesFactory $extensionAttributesFactory + * @param CacheInterface|null $cache * @SuppressWarnings(PHPMD.ExcessiveParameterList) - * @SuppressWarnings(PHPMD.LongVariable) * @SuppressWarnings(PHPMD.NPathComplexity) */ public function __construct( @@ -109,7 +116,8 @@ public function __construct( SearchCriteriaBuilder $searchCriteriaBuilder = null, AttributeManagementInterface $attributeManagement = null, LoggerInterface $logger = null, - ExtensionAttributesFactory $extensionAttributesFactory = null + ExtensionAttributesFactory $extensionAttributesFactory = null, + CacheInterface $cache = null ) { parent::__construct($context, $productBuilder); $this->resultJsonFactory = $resultJsonFactory; @@ -129,6 +137,7 @@ public function __construct( ->get(LoggerInterface::class); $this->extensionAttributesFactory = $extensionAttributesFactory ?: ObjectManager::getInstance() ->get(ExtensionAttributesFactory::class); + $this->cache = $cache ?? ObjectManager::getInstance()->get(CacheInterface::class); } /** @@ -203,6 +212,7 @@ function (AttributeInterface $attribute) use ($attributeSet, $attributeGroup) { ); } ); + $this->cache->clean([CacheType::CACHE_TAG]); } catch (LocalizedException $e) { $response->setError(true); $response->setMessage($e->getMessage()); @@ -223,7 +233,7 @@ function (AttributeInterface $attribute) use ($attributeSet, $attributeGroup) { */ private function getBasicAttributeSearchCriteriaBuilder() { - $attributeIds = (array) $this->getRequest()->getParam('attributeIds', []); + $attributeIds = (array)$this->getRequest()->getParam('attributeIds', []); if (empty($attributeIds['selected'])) { throw new LocalizedException(__('Attributes were missing and must be specified.')); diff --git a/app/code/Magento/Catalog/Controller/Adminhtml/Product/MassDelete.php b/app/code/Magento/Catalog/Controller/Adminhtml/Product/MassDelete.php index 7fa9d36163502..c779c01cd7d71 100644 --- a/app/code/Magento/Catalog/Controller/Adminhtml/Product/MassDelete.php +++ b/app/code/Magento/Catalog/Controller/Adminhtml/Product/MassDelete.php @@ -8,15 +8,13 @@ namespace Magento\Catalog\Controller\Adminhtml\Product; -use Magento\Framework\App\Action\HttpPostActionInterface as HttpPostActionInterface; -use Magento\Framework\Controller\ResultFactory; use Magento\Backend\App\Action\Context; -use Magento\Ui\Component\MassAction\Filter; -use Magento\Catalog\Model\ResourceModel\Product\CollectionFactory; use Magento\Catalog\Api\ProductRepositoryInterface; -use Magento\Framework\Exception\CouldNotSaveException; -use Magento\Framework\Exception\StateException; +use Magento\Catalog\Model\ResourceModel\Product\CollectionFactory; +use Magento\Framework\App\Action\HttpPostActionInterface as HttpPostActionInterface; +use Magento\Framework\Controller\ResultFactory; use Magento\Framework\Exception\LocalizedException; +use Magento\Ui\Component\MassAction\Filter; use Psr\Log\LoggerInterface; /** diff --git a/app/code/Magento/Catalog/Controller/Category/View.php b/app/code/Magento/Catalog/Controller/Category/View.php index eea448e0fdb0b..552af244f0097 100644 --- a/app/code/Magento/Catalog/Controller/Category/View.php +++ b/app/code/Magento/Catalog/Controller/Category/View.php @@ -9,6 +9,7 @@ use Magento\Catalog\Api\CategoryRepositoryInterface; use Magento\Catalog\Helper\Category as CategoryHelper; use Magento\Catalog\Model\Category; +use Magento\Catalog\Model\Category\Attribute\LayoutUpdateManager; use Magento\Catalog\Model\Design; use Magento\Catalog\Model\Layer\Resolver; use Magento\Catalog\Model\Product\ProductList\ToolbarMemorizer; @@ -30,7 +31,6 @@ use Magento\Framework\View\Result\PageFactory; use Magento\Store\Model\StoreManagerInterface; use Psr\Log\LoggerInterface; -use Magento\Catalog\Model\Category\Attribute\LayoutUpdateManager; /** * View a category on storefront. Needs to be accessible by POST because of the store switching. @@ -208,8 +208,10 @@ protected function _initCategory() * @return ResultInterface * @throws NoSuchEntityException */ - public function execute() + public function execute(): ?ResultInterface { + $result = null; + if ($this->_request->getParam(ActionInterface::PARAM_NAME_URL_ENCODED)) { return $this->resultRedirectFactory->create()->setUrl($this->_redirect->getRedirectUrl()); } @@ -239,6 +241,7 @@ public function execute() $page->addPageLayoutHandles(['type' => $parentPageType], null, false); } $page->addPageLayoutHandles(['type' => $pageType], null, false); + $page->addPageLayoutHandles(['displaymode' => strtolower($category->getDisplayMode())], null, false); $page->addPageLayoutHandles(['id' => $category->getId()]); // apply custom layout update once layout is loaded @@ -250,8 +253,9 @@ public function execute() return $page; } elseif (!$this->getResponse()->isRedirect()) { - return $this->resultForwardFactory->create()->forward('noroute'); + $result = $this->resultForwardFactory->create()->forward('noroute'); } + return $result; } /** diff --git a/app/code/Magento/Catalog/Controller/Product/Compare/Index.php b/app/code/Magento/Catalog/Controller/Product/Compare/Index.php index c0aa32a56ed17..3a1b4e7702f70 100644 --- a/app/code/Magento/Catalog/Controller/Product/Compare/Index.php +++ b/app/code/Magento/Catalog/Controller/Product/Compare/Index.php @@ -12,6 +12,8 @@ use Magento\Framework\View\Result\PageFactory; /** + * View products compare in frontend + * * @SuppressWarnings(PHPMD.CouplingBetweenObjects) */ class Index extends \Magento\Catalog\Controller\Product\Compare implements HttpGetActionInterface @@ -74,23 +76,12 @@ public function __construct( */ public function execute() { - $items = $this->getRequest()->getParam('items'); - $beforeUrl = $this->getRequest()->getParam(self::PARAM_NAME_URL_ENCODED); if ($beforeUrl) { $this->_catalogSession->setBeforeCompareUrl( $this->urlDecoder->decode($beforeUrl) ); } - - if ($items) { - $items = explode(',', $items); - /** @var \Magento\Catalog\Model\Product\Compare\ListCompare $list */ - $list = $this->_catalogProductCompareList; - $list->addProducts($items); - $resultRedirect = $this->resultRedirectFactory->create(); - return $resultRedirect->setPath('*/*/*'); - } return $this->resultPageFactory->create(); } } diff --git a/app/code/Magento/Catalog/Controller/Product/Frontend/Action/Synchronize.php b/app/code/Magento/Catalog/Controller/Product/Frontend/Action/Synchronize.php index 58f4b9a4bb51e..6e76d3510103a 100644 --- a/app/code/Magento/Catalog/Controller/Product/Frontend/Action/Synchronize.php +++ b/app/code/Magento/Catalog/Controller/Product/Frontend/Action/Synchronize.php @@ -7,12 +7,13 @@ use Magento\Catalog\Model\Product\ProductFrontendAction\Synchronizer; use Magento\Framework\App\Action\Context; +use Magento\Framework\App\Action\HttpPostActionInterface; use Magento\Framework\Controller\Result\JsonFactory; /** * Synchronizes Product Frontend Actions with database */ -class Synchronize extends \Magento\Framework\App\Action\Action +class Synchronize extends \Magento\Framework\App\Action\Action implements HttpPostActionInterface { /** * @var Context @@ -46,6 +47,8 @@ public function __construct( } /** + * @inheritDoc + * * This is handle for synchronizing between frontend and backend product actions: * - visit product page (recently_viewed) * - compare products (recently_compared) @@ -57,9 +60,6 @@ public function __construct( * 'added_at' => "JS_TIMESTAMP" * ] * ] - * - * - * @inheritdoc */ public function execute() { @@ -71,8 +71,8 @@ public function execute() $this->synchronizer->syncActions($productsData, $typeId); } catch (\Exception $e) { $resultJson->setStatusHeader( - \Zend\Http\Response::STATUS_CODE_400, - \Zend\Http\AbstractMessage::VERSION_11, + \Laminas\Http\Response::STATUS_CODE_400, + \Laminas\Http\AbstractMessage::VERSION_11, 'Bad Request' ); } diff --git a/app/code/Magento/Catalog/CustomerData/CompareProducts.php b/app/code/Magento/Catalog/CustomerData/CompareProducts.php index afbeab8c9070e..bdac4dfde64d1 100644 --- a/app/code/Magento/Catalog/CustomerData/CompareProducts.php +++ b/app/code/Magento/Catalog/CustomerData/CompareProducts.php @@ -6,7 +6,13 @@ namespace Magento\Catalog\CustomerData; use Magento\Customer\CustomerData\SectionSourceInterface; +use Magento\Framework\App\Config\ScopeConfigInterface; +use Magento\Framework\App\ObjectManager; +use Magento\Framework\Exception\LocalizedException; +/** + * Catalog Product Compare Widget + */ class CompareProducts implements SectionSourceInterface { /** @@ -24,23 +30,33 @@ class CompareProducts implements SectionSourceInterface */ private $outputHelper; + /** + * Core store config + * + * @var ScopeConfigInterface + */ + private $scopeConfig; + /** * @param \Magento\Catalog\Helper\Product\Compare $helper * @param \Magento\Catalog\Model\Product\Url $productUrl * @param \Magento\Catalog\Helper\Output $outputHelper + * @param ScopeConfigInterface|null $scopeConfig */ public function __construct( \Magento\Catalog\Helper\Product\Compare $helper, \Magento\Catalog\Model\Product\Url $productUrl, - \Magento\Catalog\Helper\Output $outputHelper + \Magento\Catalog\Helper\Output $outputHelper, + ?ScopeConfigInterface $scopeConfig = null ) { $this->helper = $helper; $this->productUrl = $productUrl; $this->outputHelper = $outputHelper; + $this->scopeConfig = $scopeConfig ?? ObjectManager::getInstance()->get(ScopeConfigInterface::class); } /** - * {@inheritdoc} + * @inheritdoc */ public function getSectionData() { @@ -54,11 +70,18 @@ public function getSectionData() } /** + * Get the list of compared product items + * * @return array + * @throws LocalizedException */ protected function getItems() { $items = []; + $productsScope = $this->scopeConfig->getValue( + 'catalog/recently_products/scope', + \Magento\Store\Model\ScopeInterface::SCOPE_WEBSITE + ); /** @var \Magento\Catalog\Model\Product $item */ foreach ($this->helper->getItemCollection() as $item) { $items[] = [ @@ -66,6 +89,7 @@ protected function getItems() 'product_url' => $this->productUrl->getUrl($item), 'name' => $this->outputHelper->productAttribute($item, $item->getName(), 'name'), 'remove_url' => $this->helper->getPostDataRemove($item), + 'productScope' => $productsScope ]; } return $items; diff --git a/app/code/Magento/Catalog/Helper/Product/Compare.php b/app/code/Magento/Catalog/Helper/Product/Compare.php index d6d35c5c76dd8..49a90c590a440 100644 --- a/app/code/Magento/Catalog/Helper/Product/Compare.php +++ b/app/code/Magento/Catalog/Helper/Product/Compare.php @@ -14,6 +14,7 @@ * @api * @SuppressWarnings(PHPMD.LongVariable) * @SuppressWarnings(PHPMD.CouplingBetweenObjects) + * @SuppressWarnings(PHPMD.CookieAndSessionMisuse) * @since 100.0.2 */ class Compare extends \Magento\Framework\Url\Helper\Data @@ -145,16 +146,9 @@ public function __construct( */ public function getListUrl() { - $itemIds = []; - foreach ($this->getItemCollection() as $item) { - $itemIds[] = $item->getId(); - } - $params = [ - 'items' => implode(',', $itemIds), \Magento\Framework\App\ActionInterface::PARAM_NAME_URL_ENCODED => $this->getEncodedUrl() ]; - return $this->_getUrl('catalog/product_compare', $params); } diff --git a/app/code/Magento/Catalog/Model/Category/Attribute/Backend/Image.php b/app/code/Magento/Catalog/Model/Category/Attribute/Backend/Image.php index 4880214e5c6a6..865160da14a6f 100644 --- a/app/code/Magento/Catalog/Model/Category/Attribute/Backend/Image.php +++ b/app/code/Magento/Catalog/Model/Category/Attribute/Backend/Image.php @@ -5,8 +5,12 @@ */ namespace Magento\Catalog\Model\Category\Attribute\Backend; +use Magento\Catalog\Model\ImageUploader; use Magento\Framework\App\Filesystem\DirectoryList; +use Magento\Framework\App\ObjectManager; use Magento\Framework\File\Uploader; +use Magento\Store\Api\Data\StoreInterface; +use Magento\Store\Model\StoreManagerInterface; /** * Catalog category image attribute backend model @@ -45,7 +49,7 @@ class Image extends \Magento\Eav\Model\Entity\Attribute\Backend\AbstractBackend protected $_logger; /** - * @var \Magento\Catalog\Model\ImageUploader + * @var ImageUploader */ private $imageUploader; @@ -54,19 +58,32 @@ class Image extends \Magento\Eav\Model\Entity\Attribute\Backend\AbstractBackend */ private $additionalData = '_additional_data_'; + /** + * @var StoreManagerInterface + */ + private $storeManager; + /** * @param \Psr\Log\LoggerInterface $logger * @param \Magento\Framework\Filesystem $filesystem * @param \Magento\MediaStorage\Model\File\UploaderFactory $fileUploaderFactory + * @param StoreManagerInterface $storeManager + * @param ImageUploader $imageUploader */ public function __construct( \Psr\Log\LoggerInterface $logger, \Magento\Framework\Filesystem $filesystem, - \Magento\MediaStorage\Model\File\UploaderFactory $fileUploaderFactory + \Magento\MediaStorage\Model\File\UploaderFactory $fileUploaderFactory, + StoreManagerInterface $storeManager = null, + ImageUploader $imageUploader = null ) { $this->_filesystem = $filesystem; $this->_fileUploaderFactory = $fileUploaderFactory; $this->_logger = $logger; + $this->storeManager = $storeManager ?? + ObjectManager::getInstance()->get(StoreManagerInterface::class); + $this->imageUploader = $imageUploader ?? + ObjectManager::getInstance()->get(ImageUploader::class); } /** @@ -94,13 +111,13 @@ private function getUploadedImageName($value) */ private function checkUniqueImageName(string $imageName): string { - $imageUploader = $this->getImageUploader(); $mediaDirectory = $this->_filesystem->getDirectoryWrite(DirectoryList::MEDIA); $imageAbsolutePath = $mediaDirectory->getAbsolutePath( - $imageUploader->getBasePath() . DIRECTORY_SEPARATOR . $imageName + $this->imageUploader->getBasePath() . DIRECTORY_SEPARATOR . $imageName ); - $imageName = Uploader::getNewFilename($imageAbsolutePath); + // phpcs:ignore Magento2.Functions.DiscouragedFunction + $imageName = call_user_func([Uploader::class, 'getNewFilename'], $imageAbsolutePath); return $imageName; } @@ -119,7 +136,18 @@ public function beforeSave($object) $attributeName = $this->getAttribute()->getName(); $value = $object->getData($attributeName); - if ($this->fileResidesOutsideCategoryDir($value)) { + if ($this->isTmpFileAvailable($value) && $imageName = $this->getUploadedImageName($value)) { + try { + /** @var StoreInterface $store */ + $store = $this->storeManager->getStore(); + $baseMediaDir = $store->getBaseMediaDir(); + $newImgRelativePath = $this->imageUploader->moveFileFromTmp($imageName, true); + $value[0]['url'] = '/' . $baseMediaDir . '/' . $newImgRelativePath; + $value[0]['name'] = $value[0]['url']; + } catch (\Exception $e) { + $this->_logger->critical($e); + } + } elseif ($this->fileResidesOutsideCategoryDir($value)) { // use relative path for image attribute so we know it's outside of category dir when we fetch it // phpcs:ignore Magento2.Functions.DiscouragedFunction $value[0]['url'] = parse_url($value[0]['url'], PHP_URL_PATH); @@ -139,23 +167,6 @@ public function beforeSave($object) return parent::beforeSave($object); } - /** - * Get Instance of Category Image Uploader. - * - * @return \Magento\Catalog\Model\ImageUploader - * - * @deprecated 101.0.0 - */ - private function getImageUploader() - { - if ($this->imageUploader === null) { - $this->imageUploader = \Magento\Framework\App\ObjectManager::getInstance() - ->get(\Magento\Catalog\CategoryImageUpload::class); - } - - return $this->imageUploader; - } - /** * Check if temporary file is available for new image upload. * @@ -194,19 +205,10 @@ private function fileResidesOutsideCategoryDir($value) * * @param \Magento\Framework\DataObject $object * @return \Magento\Catalog\Model\Category\Attribute\Backend\Image + * @SuppressWarnings(PHPMD.UnusedFormalParameter) */ public function afterSave($object) { - $value = $object->getData($this->additionalData . $this->getAttribute()->getName()); - - if ($this->isTmpFileAvailable($value) && $imageName = $this->getUploadedImageName($value)) { - try { - $this->getImageUploader()->moveFileFromTmp($imageName); - } catch (\Exception $e) { - $this->_logger->critical($e); - } - } - return $this; } } diff --git a/app/code/Magento/Catalog/Model/Category/DataProvider.php b/app/code/Magento/Catalog/Model/Category/DataProvider.php index 283e3f87686b9..d8c79c485e3e5 100644 --- a/app/code/Magento/Catalog/Model/Category/DataProvider.php +++ b/app/code/Magento/Catalog/Model/Category/DataProvider.php @@ -12,6 +12,7 @@ use Magento\Catalog\Model\Attribute\ScopeOverriddenValue; use Magento\Catalog\Model\Category; use Magento\Catalog\Model\Category\Attribute\Backend\Image as ImageBackendModel; +use Magento\Catalog\Model\Category\Attribute\Backend\LayoutUpdate; use Magento\Catalog\Model\CategoryFactory; use Magento\Catalog\Model\ResourceModel\Category\CollectionFactory as CategoryCollectionFactory; use Magento\Catalog\Model\ResourceModel\Eav\Attribute as EavAttribute; @@ -20,8 +21,10 @@ use Magento\Eav\Model\Entity\Attribute\Source\SpecificSourceInterface; use Magento\Eav\Model\Entity\Type; use Magento\Framework\App\ObjectManager; +use Magento\Framework\App\RequestInterface; +use Magento\Framework\Exception\LocalizedException; use Magento\Framework\Exception\NoSuchEntityException; -use Magento\Framework\Filesystem; +use Magento\Framework\Registry; use Magento\Framework\Stdlib\ArrayManager; use Magento\Framework\Stdlib\ArrayUtils; use Magento\Store\Model\Store; @@ -30,6 +33,7 @@ use Magento\Ui\DataProvider\EavValidationRules; use Magento\Ui\DataProvider\Modifier\PoolInterface; use Magento\Framework\AuthorizationInterface; +use Magento\Ui\DataProvider\ModifierPoolDataProvider; /** * Category form data provider. @@ -37,9 +41,10 @@ * @api * @SuppressWarnings(PHPMD.CouplingBetweenObjects) * @SuppressWarnings(PHPMD.TooManyFields) + * @SuppressWarnings(PHPMD.ExcessiveClassComplexity) * @since 101.0.0 */ -class DataProvider extends \Magento\Ui\DataProvider\ModifierPoolDataProvider +class DataProvider extends ModifierPoolDataProvider { /** * @var string @@ -106,6 +111,15 @@ class DataProvider extends \Magento\Ui\DataProvider\ModifierPoolDataProvider 'position' ]; + /** + * Elements with currency symbol + * + * @var array + */ + private $elementsWithCurrencySymbol = [ + 'filter_price_range', + ]; + /** * @var EavValidationRules * @since 101.0.0 @@ -113,13 +127,13 @@ class DataProvider extends \Magento\Ui\DataProvider\ModifierPoolDataProvider protected $eavValidationRules; /** - * @var \Magento\Framework\Registry + * @var Registry * @since 101.0.0 */ protected $registry; /** - * @var \Magento\Framework\App\RequestInterface + * @var RequestInterface * @since 101.0.0 */ protected $request; @@ -155,7 +169,7 @@ class DataProvider extends \Magento\Ui\DataProvider\ModifierPoolDataProvider private $arrayUtils; /** - * @var Filesystem + * @var FileInfo */ private $fileInfo; @@ -163,6 +177,10 @@ class DataProvider extends \Magento\Ui\DataProvider\ModifierPoolDataProvider * @var AuthorizationInterface */ private $auth; + /** + * @var Image + */ + private $categoryImage; /** * @param string $name @@ -171,16 +189,19 @@ class DataProvider extends \Magento\Ui\DataProvider\ModifierPoolDataProvider * @param EavValidationRules $eavValidationRules * @param CategoryCollectionFactory $categoryCollectionFactory * @param StoreManagerInterface $storeManager - * @param \Magento\Framework\Registry $registry + * @param Registry $registry * @param Config $eavConfig - * @param \Magento\Framework\App\RequestInterface $request + * @param RequestInterface $request * @param CategoryFactory $categoryFactory * @param array $meta * @param array $data * @param PoolInterface|null $pool * @param AuthorizationInterface|null $auth * @param ArrayUtils|null $arrayUtils - * @throws \Magento\Framework\Exception\LocalizedException + * @param ScopeOverriddenValue|null $scopeOverriddenValue + * @param ArrayManager|null $arrayManager + * @param FileInfo|null $fileInfo + * @param Image|null $categoryImage * @SuppressWarnings(PHPMD.ExcessiveParameterList) */ public function __construct( @@ -190,15 +211,19 @@ public function __construct( EavValidationRules $eavValidationRules, CategoryCollectionFactory $categoryCollectionFactory, StoreManagerInterface $storeManager, - \Magento\Framework\Registry $registry, + Registry $registry, Config $eavConfig, - \Magento\Framework\App\RequestInterface $request, + RequestInterface $request, CategoryFactory $categoryFactory, array $meta = [], array $data = [], PoolInterface $pool = null, ?AuthorizationInterface $auth = null, - ?ArrayUtils $arrayUtils = null + ?ArrayUtils $arrayUtils = null, + ScopeOverriddenValue $scopeOverriddenValue = null, + ArrayManager $arrayManager = null, + FileInfo $fileInfo = null, + ?Image $categoryImage = null ) { $this->eavValidationRules = $eavValidationRules; $this->collection = $categoryCollectionFactory->create(); @@ -210,6 +235,11 @@ public function __construct( $this->categoryFactory = $categoryFactory; $this->auth = $auth ?? ObjectManager::getInstance()->get(AuthorizationInterface::class); $this->arrayUtils = $arrayUtils ?? ObjectManager::getInstance()->get(ArrayUtils::class); + $this->scopeOverriddenValue = $scopeOverriddenValue ?: + ObjectManager::getInstance()->get(ScopeOverriddenValue::class); + $this->arrayManager = $arrayManager ?: ObjectManager::getInstance()->get(ArrayManager::class); + $this->fileInfo = $fileInfo ?: ObjectManager::getInstance()->get(FileInfo::class); + $this->categoryImage = $categoryImage ?? ObjectManager::getInstance()->get(Image::class); parent::__construct($name, $primaryFieldName, $requestFieldName, $meta, $data, $pool); } @@ -247,7 +277,7 @@ private function addUseDefaultValueCheckbox(Category $category, array $meta): ar $canDisplayUseDefault = $attribute->getScope() != EavAttributeInterface::SCOPE_GLOBAL_TEXT && $category->getId() && $category->getStoreId(); - $attributePath = $this->getArrayManager()->findPath($attributeCode, $meta); + $attributePath = $this->arrayManager->findPath($attributeCode, $meta); if (!$attributePath || !$canDisplayUseDefault @@ -256,14 +286,14 @@ private function addUseDefaultValueCheckbox(Category $category, array $meta): ar continue; } - $meta = $this->getArrayManager()->merge( + $meta = $this->arrayManager->merge( [$attributePath, 'arguments/data/config'], $meta, [ 'service' => [ 'template' => 'ui/form/element/helper/service', ], - 'disabled' => !$this->getScopeOverriddenValue()->containsValue( + 'disabled' => !$this->scopeOverriddenValue->containsValue( CategoryInterface::class, $category, $attributeCode, @@ -354,7 +384,7 @@ public function getData() * * @param Type $entityType * @return array - * @throws \Magento\Framework\Exception\LocalizedException + * @throws LocalizedException * @SuppressWarnings(PHPMD.NPathComplexity) * @SuppressWarnings(PHPMD.CyclomaticComplexity) * @since 101.0.0 @@ -407,11 +437,22 @@ public function getAttributesMeta(Type $entityType) if ($category) { $attributeIsLocked = $category->isLockedAttribute($code); $meta[$code]['disabled'] = $attributeIsLocked; - $hasUseConfigField = (bool) array_search('use_config.' . $code, $fields, true); + $hasUseConfigField = (bool)array_search('use_config.' . $code, $fields, true); if ($hasUseConfigField && $meta[$code]['disabled']) { $meta['use_config.' . $code]['disabled'] = true; } } + + if (in_array($code, $this->elementsWithCurrencySymbol, false)) { + $requestScope = $this->request->getParam( + $this->requestScopeFieldName, + Store::DEFAULT_STORE_ID + ); + + $meta[$code]['addbefore'] = $this->storeManager->getStore($requestScope) + ->getBaseCurrency() + ->getCurrencySymbol(); + } } $result = []; @@ -451,7 +492,7 @@ protected function addUseConfigSettings($categoryData) /** * Add use default settings * - * @param \Magento\Catalog\Model\Category $category + * @param Category $category * @param array $categoryData * @return array * @deprecated 101.1.0 @@ -539,7 +580,7 @@ protected function filterFields($categoryData) /** * Converts category image data to acceptable for rendering format * - * @param \Magento\Catalog\Model\Category $category + * @param Category $category * @param array $categoryData * @return array */ @@ -549,7 +590,7 @@ private function convertValues($category, $categoryData): array if ($attributeCode === 'custom_layout_update_file') { if (!empty($categoryData['custom_layout_update'])) { $categoryData['custom_layout_update_file'] - = \Magento\Catalog\Model\Category\Attribute\Backend\LayoutUpdate::VALUE_USE_UPDATE_XML; + = LayoutUpdate::VALUE_USE_UPDATE_XML; } } if (!isset($categoryData[$attributeCode])) { @@ -560,20 +601,15 @@ private function convertValues($category, $categoryData): array unset($categoryData[$attributeCode]); $fileName = $category->getData($attributeCode); - $fileInfo = $this->getFileInfo(); - if ($fileInfo->isExist($fileName)) { - $stat = $fileInfo->getStat($fileName); - $mime = $fileInfo->getMimeType($fileName); + if ($this->fileInfo->isExist($fileName)) { + $stat = $this->fileInfo->getStat($fileName); + $mime = $this->fileInfo->getMimeType($fileName); // phpcs:ignore Magento2.Functions.DiscouragedFunction $categoryData[$attributeCode][0]['name'] = basename($fileName); - if ($fileInfo->isBeginsWithMediaDirectoryPath($fileName)) { - $categoryData[$attributeCode][0]['url'] = $fileName; - } else { - $categoryData[$attributeCode][0]['url'] = $category->getImageUrl($attributeCode); - } + $categoryData[$attributeCode][0]['url'] = $this->categoryImage->getUrl($category, $attributeCode); $categoryData[$attributeCode][0]['size'] = isset($stat) ? $stat['size'] : 0; $categoryData[$attributeCode][0]['type'] = $mime; @@ -611,53 +647,53 @@ protected function getFieldsMap() { return [ 'general' => [ - 'parent', - 'path', - 'is_active', - 'include_in_menu', - 'name', - ], + 'parent', + 'path', + 'is_active', + 'include_in_menu', + 'name', + ], 'content' => [ - 'image', - 'description', - 'landing_page', - ], + 'image', + 'description', + 'landing_page', + ], 'display_settings' => [ - 'display_mode', - 'is_anchor', - 'available_sort_by', - 'use_config.available_sort_by', - 'default_sort_by', - 'use_config.default_sort_by', - 'filter_price_range', - 'use_config.filter_price_range', - ], + 'display_mode', + 'is_anchor', + 'available_sort_by', + 'use_config.available_sort_by', + 'default_sort_by', + 'use_config.default_sort_by', + 'filter_price_range', + 'use_config.filter_price_range', + ], 'search_engine_optimization' => [ - 'url_key', - 'url_key_create_redirect', - 'url_key_group', - 'meta_title', - 'meta_keywords', - 'meta_description', - ], + 'url_key', + 'url_key_create_redirect', + 'url_key_group', + 'meta_title', + 'meta_keywords', + 'meta_description', + ], 'assign_products' => [ - ], + ], 'design' => [ - 'custom_use_parent_settings', - 'custom_apply_to_products', - 'custom_design', - 'page_layout', - 'custom_layout_update', - 'custom_layout_update_file' - ], + 'custom_use_parent_settings', + 'custom_apply_to_products', + 'custom_design', + 'page_layout', + 'custom_layout_update', + 'custom_layout_update_file' + ], 'schedule_design_update' => [ - 'custom_design_from', - 'custom_design_to', - ], + 'custom_design_from', + 'custom_design_to', + ], 'category_view_optimization' => [ - ], + ], 'category_permissions' => [ - ], + ], ]; } @@ -671,53 +707,4 @@ private function getFields(): array $fieldsMap = $this->getFieldsMap(); return $this->arrayUtils->flatten($fieldsMap); } - - /** - * Retrieve scope overridden value - * - * @return ScopeOverriddenValue - * @deprecated 101.1.0 - */ - private function getScopeOverriddenValue(): ScopeOverriddenValue - { - if (null === $this->scopeOverriddenValue) { - $this->scopeOverriddenValue = \Magento\Framework\App\ObjectManager::getInstance()->get( - ScopeOverriddenValue::class - ); - } - - return $this->scopeOverriddenValue; - } - - /** - * Retrieve array manager - * - * @return ArrayManager - * @deprecated 101.1.0 - */ - private function getArrayManager(): ArrayManager - { - if (null === $this->arrayManager) { - $this->arrayManager = \Magento\Framework\App\ObjectManager::getInstance()->get( - ArrayManager::class - ); - } - - return $this->arrayManager; - } - - /** - * Get FileInfo instance - * - * @return FileInfo - * - * @deprecated 101.1.0 - */ - private function getFileInfo(): FileInfo - { - if ($this->fileInfo === null) { - $this->fileInfo = ObjectManager::getInstance()->get(FileInfo::class); - } - return $this->fileInfo; - } } diff --git a/app/code/Magento/Catalog/Model/Category/FileInfo.php b/app/code/Magento/Catalog/Model/Category/FileInfo.php index 76b6a2e75d0ea..7d679f2645be1 100644 --- a/app/code/Magento/Catalog/Model/Category/FileInfo.php +++ b/app/code/Magento/Catalog/Model/Category/FileInfo.php @@ -245,4 +245,15 @@ private function getMediaDirectoryPathRelativeToBaseDirectoryPath(string $filePa return $mediaDirectoryRelativeSubpath; } + + /** + * Get file relative path to media directory + * + * @param string $filename + * @return string + */ + public function getRelativePathToMediaDirectory(string $filename): string + { + return $this->getFilePath($filename); + } } diff --git a/app/code/Magento/Catalog/Model/Category/Image.php b/app/code/Magento/Catalog/Model/Category/Image.php new file mode 100644 index 0000000000000..ea5700cb386d0 --- /dev/null +++ b/app/code/Magento/Catalog/Model/Category/Image.php @@ -0,0 +1,75 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +declare(strict_types=1); + +namespace Magento\Catalog\Model\Category; + +use Magento\Catalog\Model\Category; +use Magento\Framework\Exception\LocalizedException; +use Magento\Framework\UrlInterface; +use Magento\Store\Model\StoreManagerInterface; + +/** + * Category Image Service + */ +class Image +{ + private const ATTRIBUTE_NAME = 'image'; + /** + * @var FileInfo + */ + private $fileInfo; + /** + * @var StoreManagerInterface + */ + private $storeManager; + + /** + * Initialize dependencies. + * + * @param FileInfo $fileInfo + * @param StoreManagerInterface $storeManager + */ + public function __construct( + FileInfo $fileInfo, + StoreManagerInterface $storeManager + ) { + $this->fileInfo = $fileInfo; + $this->storeManager = $storeManager; + } + /** + * Resolve category image URL + * + * @param Category $category + * @param string $attributeCode + * @return string + * @throws LocalizedException + */ + public function getUrl(Category $category, string $attributeCode = self::ATTRIBUTE_NAME): string + { + $url = ''; + $image = $category->getData($attributeCode); + if ($image) { + if (is_string($image)) { + $store = $this->storeManager->getStore(); + $mediaBaseUrl = $store->getBaseUrl(UrlInterface::URL_TYPE_MEDIA); + if ($this->fileInfo->isBeginsWithMediaDirectoryPath($image)) { + $relativePath = $this->fileInfo->getRelativePathToMediaDirectory($image); + $url = rtrim($mediaBaseUrl, '/') . '/' . ltrim($relativePath, '/'); + } elseif (substr($image, 0, 1) !== '/') { + $url = rtrim($mediaBaseUrl, '/') . '/' . ltrim(FileInfo::ENTITY_MEDIA_PATH, '/') . '/' . $image; + } else { + $url = $image; + } + } else { + throw new LocalizedException( + __('Something went wrong while getting the image url.') + ); + } + } + return $url; + } +} diff --git a/app/code/Magento/Catalog/Model/CategoryLinkRepository.php b/app/code/Magento/Catalog/Model/CategoryLinkRepository.php index fb0ea680569a8..f8de9a37f4ed7 100644 --- a/app/code/Magento/Catalog/Model/CategoryLinkRepository.php +++ b/app/code/Magento/Catalog/Model/CategoryLinkRepository.php @@ -6,10 +6,19 @@ namespace Magento\Catalog\Model; -use Magento\Framework\Exception\InputException; +use Magento\Catalog\Api\CategoryLinkRepositoryInterface; +use Magento\Catalog\Api\CategoryListDeleteBySkuInterface; +use Magento\Catalog\Api\CategoryRepositoryInterface; +use Magento\Catalog\Api\ProductRepositoryInterface; +use Magento\Catalog\Model\ResourceModel\Product; +use Magento\Framework\App\ObjectManager; use Magento\Framework\Exception\CouldNotSaveException; +use Magento\Framework\Exception\InputException; -class CategoryLinkRepository implements \Magento\Catalog\Api\CategoryLinkRepositoryInterface +/** + * @inheritdoc + */ +class CategoryLinkRepository implements CategoryLinkRepositoryInterface, CategoryListDeleteBySkuInterface { /** * @var CategoryRepository @@ -17,24 +26,32 @@ class CategoryLinkRepository implements \Magento\Catalog\Api\CategoryLinkReposit protected $categoryRepository; /** - * @var \Magento\Catalog\Api\ProductRepositoryInterface + * @var ProductRepositoryInterface */ protected $productRepository; /** - * @param \Magento\Catalog\Api\CategoryRepositoryInterface $categoryRepository - * @param \Magento\Catalog\Api\ProductRepositoryInterface $productRepository + * @var Product + */ + private $productResource; + + /** + * @param CategoryRepositoryInterface $categoryRepository + * @param ProductRepositoryInterface $productRepository + * @param Product $productResource */ public function __construct( - \Magento\Catalog\Api\CategoryRepositoryInterface $categoryRepository, - \Magento\Catalog\Api\ProductRepositoryInterface $productRepository + CategoryRepositoryInterface $categoryRepository, + ProductRepositoryInterface $productRepository, + Product $productResource = null ) { $this->categoryRepository = $categoryRepository; $this->productRepository = $productRepository; + $this->productResource = $productResource ?? ObjectManager::getInstance()->get(Product::class); } /** - * {@inheritdoc} + * @inheritdoc */ public function save(\Magento\Catalog\Api\Data\CategoryProductLinkInterface $productLink) { @@ -60,7 +77,7 @@ public function save(\Magento\Catalog\Api\Data\CategoryProductLinkInterface $pro } /** - * {@inheritdoc} + * @inheritdoc */ public function delete(\Magento\Catalog\Api\Data\CategoryProductLinkInterface $productLink) { @@ -68,7 +85,7 @@ public function delete(\Magento\Catalog\Api\Data\CategoryProductLinkInterface $p } /** - * {@inheritdoc} + * @inheritdoc */ public function deleteByIds($categoryId, $sku) { @@ -101,4 +118,44 @@ public function deleteByIds($categoryId, $sku) } return true; } + + /** + * @inheritdoc + */ + public function deleteBySkus(int $categoryId, array $productSkuList): bool + { + $category = $this->categoryRepository->get($categoryId); + $products = $this->productResource->getProductsIdsBySkus($productSkuList); + + if (!$products) { + throw new InputException(__("The category doesn't contain the specified products.")); + } + + $productPositions = $category->getProductsPosition(); + + foreach ($products as $productId) { + if (isset($productPositions[$productId])) { + unset($productPositions[$productId]); + } + } + + $category->setPostedProducts($productPositions); + + try { + $category->save(); + } catch (\Exception $e) { + throw new CouldNotSaveException( + __( + 'Could not save products "%products" to category %category', + [ + "products" => implode(',', $productSkuList), + "category" => $category->getId() + ] + ), + $e + ); + } + + return true; + } } diff --git a/app/code/Magento/Catalog/Model/CategoryRepository.php b/app/code/Magento/Catalog/Model/CategoryRepository.php index a8636306f5e5b..0ce52b966c32c 100644 --- a/app/code/Magento/Catalog/Model/CategoryRepository.php +++ b/app/code/Magento/Catalog/Model/CategoryRepository.php @@ -108,6 +108,7 @@ public function save(\Magento\Catalog\Api\Data\CategoryInterface $category) $parentCategory = $this->get($parentId, $storeId); $existingData['path'] = $parentCategory->getPath(); $existingData['parent_id'] = $parentId; + $existingData['level'] = null; } $category->addData($existingData); try { diff --git a/app/code/Magento/Catalog/Model/Config/LayerCategoryConfig.php b/app/code/Magento/Catalog/Model/Config/LayerCategoryConfig.php new file mode 100644 index 0000000000000..0e8565e3b25d1 --- /dev/null +++ b/app/code/Magento/Catalog/Model/Config/LayerCategoryConfig.php @@ -0,0 +1,80 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +declare(strict_types=1); + +namespace Magento\Catalog\Model\Config; + +use Magento\Framework\App\Config\ScopeConfigInterface; +use Magento\Framework\Exception\NoSuchEntityException; +use Magento\Store\Model\ScopeInterface; +use Magento\Store\Model\StoreManagerInterface; + +/** + * Config for category in the layered navigation + */ +class LayerCategoryConfig +{ + private const XML_PATH_CATALOG_LAYERED_NAVIGATION_DISPLAY_CATEGORY = 'catalog/layered_navigation/display_category'; + + /** + * @var ScopeConfigInterface + */ + private $scopeConfig; + + /** + * @var StoreManagerInterface + */ + private $storeManager; + + /** + * LayerCategoryConfig constructor + * + * @param ScopeConfigInterface $scopeConfig + * @param StoreManagerInterface $storeManager + */ + public function __construct( + ScopeConfigInterface $scopeConfig, + StoreManagerInterface $storeManager + ) { + $this->scopeConfig = $scopeConfig; + $this->storeManager = $storeManager; + } + + /** + * Check if category filter item should be added in the layered navigation + * + * @param string $scopeType + * @param null|int|string $scopeCode + * + * @return bool + */ + public function isCategoryFilterVisibleInLayerNavigation( + $scopeType = ScopeInterface::SCOPE_STORES, + $scopeCode = null + ): bool { + if (!$scopeCode) { + $scopeCode = $this->getStoreId(); + } + + return $this->scopeConfig->isSetFlag( + static::XML_PATH_CATALOG_LAYERED_NAVIGATION_DISPLAY_CATEGORY, + $scopeType, + $scopeCode + ); + } + + /** + * Get the current store ID + * + * @return int + * + * @throws NoSuchEntityException + */ + private function getStoreId(): int + { + return (int) $this->storeManager->getStore()->getId(); + } +} diff --git a/app/code/Magento/Catalog/Model/ImageUploader.php b/app/code/Magento/Catalog/Model/ImageUploader.php index 825823276261f..b0c8d56057431 100644 --- a/app/code/Magento/Catalog/Model/ImageUploader.php +++ b/app/code/Magento/Catalog/Model/ImageUploader.php @@ -191,12 +191,12 @@ public function getFilePath($path, $imageName) * Checking file for moving and move it * * @param string $imageName - * + * @param bool $returnRelativePath * @return string * * @throws \Magento\Framework\Exception\LocalizedException */ - public function moveFileFromTmp($imageName) + public function moveFileFromTmp($imageName, $returnRelativePath = false) { $baseTmpPath = $this->getBaseTmpPath(); $basePath = $this->getBasePath(); @@ -221,12 +221,14 @@ public function moveFileFromTmp($imageName) $baseImagePath ); } catch (\Exception $e) { + $this->logger->critical($e); throw new \Magento\Framework\Exception\LocalizedException( - __('Something went wrong while saving the file(s).') + __('Something went wrong while saving the file(s).'), + $e ); } - return $imageName; + return $returnRelativePath ? $baseImagePath : $imageName; } /** @@ -276,7 +278,8 @@ public function saveFileToTmpDir($fileId) } catch (\Exception $e) { $this->logger->critical($e); throw new \Magento\Framework\Exception\LocalizedException( - __('Something went wrong while saving the file(s).') + __('Something went wrong while saving the file(s).'), + $e ); } } diff --git a/app/code/Magento/Catalog/Model/Indexer/Category/Flat/Action/Full.php b/app/code/Magento/Catalog/Model/Indexer/Category/Flat/Action/Full.php index a62e3d8f83b85..57fb3d64dea4a 100644 --- a/app/code/Magento/Catalog/Model/Indexer/Category/Flat/Action/Full.php +++ b/app/code/Magento/Catalog/Model/Indexer/Category/Flat/Action/Full.php @@ -79,6 +79,7 @@ protected function populateFlatTables(array $stores) } $category['store_id'] = $store->getId(); $data[] = $this->prepareValuesToInsert( + // phpcs:ignore Magento2.Performance.ForeachArrayMerge array_merge($category, $attributesData[$category[$linkField]]) ); } @@ -183,7 +184,7 @@ private function getActualStoreTablesForCategoryFlat(): array foreach ($this->storeManager->getStores() as $store) { $actualStoreTables[] = sprintf( '%s_store_%s', - $this->connection->getTableName('catalog_category_flat'), + $this->connection->getTableName($this->getTableName('catalog_category_flat')), $store->getId() ); } @@ -199,7 +200,7 @@ private function getActualStoreTablesForCategoryFlat(): array private function deleteAbandonedStoreCategoryFlatTables(): void { $existentTables = $this->connection->getTables( - $this->connection->getTableName('catalog_category_flat_store_%') + $this->connection->getTableName($this->getTableName('catalog_category_flat_store_%')) ); $actualStoreTables = $this->getActualStoreTablesForCategoryFlat(); diff --git a/app/code/Magento/Catalog/Model/Indexer/Product/Flat/AbstractAction.php b/app/code/Magento/Catalog/Model/Indexer/Product/Flat/AbstractAction.php index a0acacd4dfd2f..565ac1b121980 100644 --- a/app/code/Magento/Catalog/Model/Indexer/Product/Flat/AbstractAction.php +++ b/app/code/Magento/Catalog/Model/Indexer/Product/Flat/AbstractAction.php @@ -222,7 +222,7 @@ protected function _updateRelationProducts($storeId, $productIds = null) ['t' => $this->_productIndexerHelper->getTable($relation->getTable())], ['entity_table.entity_id', $relation->getChildFieldName(), new \Zend_Db_Expr('1')] )->join( - ['entity_table' => $this->_connection->getTableName('catalog_product_entity')], + ['entity_table' => $this->_productIndexerHelper->getTable('catalog_product_entity')], "entity_table.{$metadata->getLinkField()} = t.{$relation->getParentFieldName()}", [] )->join( diff --git a/app/code/Magento/Catalog/Model/Indexer/Product/Flat/Action/Eraser.php b/app/code/Magento/Catalog/Model/Indexer/Product/Flat/Action/Eraser.php index c4d807667bfbc..ba209884fbb53 100644 --- a/app/code/Magento/Catalog/Model/Indexer/Product/Flat/Action/Eraser.php +++ b/app/code/Magento/Catalog/Model/Indexer/Product/Flat/Action/Eraser.php @@ -99,7 +99,7 @@ public function removeDisabledProducts(array &$ids, $storeId) ['status_global_attr' => $statusAttribute->getBackendTable()], ' status_global_attr.attribute_id = ' . (int)$statusAttribute->getAttributeId() . ' AND status_global_attr.store_id = ' . Store::DEFAULT_STORE_ID - . ' AND status_global_attr.' . $statusAttribute->getEntityIdField() . '=' + . ' AND status_global_attr.' . $metadata->getLinkField() . '=' . 'product_table.' . $metadata->getLinkField(), [] ); @@ -107,7 +107,7 @@ public function removeDisabledProducts(array &$ids, $storeId) ['status_attr' => $statusAttribute->getBackendTable()], ' status_attr.attribute_id = ' . (int)$statusAttribute->getAttributeId() . ' AND status_attr.store_id = ' . $storeId - . ' AND status_attr.' . $statusAttribute->getEntityIdField() . '=' + . ' AND status_attr.' . $metadata->getLinkField() . '=' . 'product_table.' . $metadata->getLinkField(), [] ); diff --git a/app/code/Magento/Catalog/Model/Indexer/Product/Flat/Action/Row.php b/app/code/Magento/Catalog/Model/Indexer/Product/Flat/Action/Row.php index 64a7c4be4e03c..5ea2b636933a3 100644 --- a/app/code/Magento/Catalog/Model/Indexer/Product/Flat/Action/Row.php +++ b/app/code/Magento/Catalog/Model/Indexer/Product/Flat/Action/Row.php @@ -85,10 +85,16 @@ public function execute($id = null) $ids = [$id]; $linkField = $this->metadataPool->getMetadata(ProductInterface::class)->getLinkField(); + $storeIds = $this->getAssignedStoreIdsOfProduct($id); + $stores = $this->_storeManager->getStores(); foreach ($stores as $store) { $tableExists = $this->_isFlatTableExists($store->getId()); if ($tableExists) { + if (!in_array($store->getId(), $storeIds)) { + $this->flatItemEraser->deleteProductsFromStore($id, $store->getId()); + continue; + } $this->flatItemEraser->removeDeletedProducts($ids, $store->getId()); $this->flatItemEraser->removeDisabledProducts($ids, $store->getId()); } @@ -129,4 +135,23 @@ public function execute($id = null) return $this; } + + /** + * Get list store id where the product is enable + * + * @param int $productId + * @return array + */ + private function getAssignedStoreIdsOfProduct($productId) + { + $select = $this->_connection->select(); + $select->from(['e' => $this->_productIndexerHelper->getTable('store')], ['e.store_id']) + ->where('c.product_id = ' . $productId) + ->joinLeft( + ['c' => $this->_productIndexerHelper->getTable('catalog_product_website')], + 'e.website_id = c.website_id', + [] + ); + return $this->_connection->fetchCol($select); + } } diff --git a/app/code/Magento/Catalog/Model/Indexer/Product/Flat/Action/Rows.php b/app/code/Magento/Catalog/Model/Indexer/Product/Flat/Action/Rows.php index 7b2a6272c7052..6c7b69b884f8d 100644 --- a/app/code/Magento/Catalog/Model/Indexer/Product/Flat/Action/Rows.php +++ b/app/code/Magento/Catalog/Model/Indexer/Product/Flat/Action/Rows.php @@ -5,14 +5,14 @@ */ namespace Magento\Catalog\Model\Indexer\Product\Flat\Action; +use Magento\Catalog\Model\Indexer\Product\Flat\AbstractAction; use Magento\Catalog\Model\Indexer\Product\Flat\FlatTableBuilder; use Magento\Catalog\Model\Indexer\Product\Flat\TableBuilder; /** * Class Rows reindex action for mass actions - * */ -class Rows extends \Magento\Catalog\Model\Indexer\Product\Flat\AbstractAction +class Rows extends AbstractAction { /** * @var Eraser @@ -67,6 +67,7 @@ public function execute($ids) foreach ($idsBatches as $changedIds) { if ($tableExists) { $this->flatItemEraser->removeDeletedProducts($changedIds, $store->getId()); + $this->flatItemEraser->removeDisabledProducts($changedIds, $store->getId()); } if (!empty($changedIds)) { $this->_reindex($store->getId(), $changedIds); diff --git a/app/code/Magento/Catalog/Model/Indexer/Product/Price/Plugin/CustomerGroup.php b/app/code/Magento/Catalog/Model/Indexer/Product/Price/Plugin/CustomerGroup.php index 9b99ee8c8dc8c..e1f4cd28d0090 100644 --- a/app/code/Magento/Catalog/Model/Indexer/Product/Price/Plugin/CustomerGroup.php +++ b/app/code/Magento/Catalog/Model/Indexer/Product/Price/Plugin/CustomerGroup.php @@ -15,6 +15,9 @@ use Magento\Customer\Model\Indexer\CustomerGroupDimensionProvider; use Magento\Store\Model\Indexer\WebsiteDimensionProvider; +/** + * Update catalog_product_index_price table after delete or save customer group + */ class CustomerGroup { /** @@ -80,7 +83,7 @@ public function aroundSave( \Closure $proceed, GroupInterface $group ) { - $isGroupNew = !$group->getId(); + $isGroupNew = $group->getId() === null; $group = $proceed($group); if ($isGroupNew) { foreach ($this->getAffectedDimensions((string)$group->getId()) as $dimensions) { diff --git a/app/code/Magento/Catalog/Model/Layer/FilterList.php b/app/code/Magento/Catalog/Model/Layer/FilterList.php index b8e9b8ad4aaa5..86a7d1fb61938 100644 --- a/app/code/Magento/Catalog/Model/Layer/FilterList.php +++ b/app/code/Magento/Catalog/Model/Layer/FilterList.php @@ -7,6 +7,9 @@ namespace Magento\Catalog\Model\Layer; +use Magento\Catalog\Model\Config\LayerCategoryConfig; +use Magento\Framework\App\ObjectManager; + /** * Layer navigation filters */ @@ -44,18 +47,26 @@ class FilterList */ protected $filters = []; + /** + * @var LayerCategoryConfig + */ + private $layerCategoryConfig; + /** * @param \Magento\Framework\ObjectManagerInterface $objectManager * @param FilterableAttributeListInterface $filterableAttributes + * @param LayerCategoryConfig $layerCategoryConfig * @param array $filters */ public function __construct( \Magento\Framework\ObjectManagerInterface $objectManager, FilterableAttributeListInterface $filterableAttributes, + LayerCategoryConfig $layerCategoryConfig, array $filters = [] ) { $this->objectManager = $objectManager; $this->filterableAttributes = $filterableAttributes; + $this->layerCategoryConfig = $layerCategoryConfig; /** Override default filter type models */ $this->filterTypes = array_merge($this->filterTypes, $filters); @@ -70,9 +81,11 @@ public function __construct( public function getFilters(\Magento\Catalog\Model\Layer $layer) { if (!count($this->filters)) { - $this->filters = [ - $this->objectManager->create($this->filterTypes[self::CATEGORY_FILTER], ['layer' => $layer]), - ]; + if ($this->layerCategoryConfig->isCategoryFilterVisibleInLayerNavigation()) { + $this->filters = [ + $this->objectManager->create($this->filterTypes[self::CATEGORY_FILTER], ['layer' => $layer]), + ]; + } foreach ($this->filterableAttributes->getList() as $attribute) { $this->filters[] = $this->createAttributeFilter($attribute, $layer); } @@ -110,9 +123,9 @@ protected function getAttributeFilterClass(\Magento\Catalog\Model\ResourceModel\ { $filterClassName = $this->filterTypes[self::ATTRIBUTE_FILTER]; - if ($attribute->getFrontendInput() === 'price') { + if ($attribute->getAttributeCode() == 'price') { $filterClassName = $this->filterTypes[self::PRICE_FILTER]; - } elseif ($attribute->getBackendType() === 'decimal') { + } elseif ($attribute->getBackendType() == 'decimal') { $filterClassName = $this->filterTypes[self::DECIMAL_FILTER]; } diff --git a/app/code/Magento/Catalog/Model/Layout/DepersonalizePlugin.php b/app/code/Magento/Catalog/Model/Layout/DepersonalizePlugin.php index 50ef65520c87d..5edb148e501d5 100644 --- a/app/code/Magento/Catalog/Model/Layout/DepersonalizePlugin.php +++ b/app/code/Magento/Catalog/Model/Layout/DepersonalizePlugin.php @@ -1,55 +1,55 @@ <?php /** - * Depersonalize catalog session data - * * Copyright © Magento, Inc. All rights reserved. * See COPYING.txt for license details. */ +declare(strict_types=1); + namespace Magento\Catalog\Model\Layout; +use Magento\Catalog\Model\Session as CatalogSession; +use Magento\Framework\View\LayoutInterface; use Magento\PageCache\Model\DepersonalizeChecker; /** - * Class DepersonalizePlugin + * Depersonalize customer data. + * + * @SuppressWarnings(PHPMD.CookieAndSessionMisuse) */ class DepersonalizePlugin { /** * @var DepersonalizeChecker */ - protected $depersonalizeChecker; + private $depersonalizeChecker; /** - * Catalog session - * - * @var \Magento\Catalog\Model\Session + * @var CatalogSession */ - protected $catalogSession; + private $catalogSession; /** * @param DepersonalizeChecker $depersonalizeChecker - * @param \Magento\Catalog\Model\Session $catalogSession + * @param CatalogSession $catalogSession */ public function __construct( DepersonalizeChecker $depersonalizeChecker, - \Magento\Catalog\Model\Session $catalogSession + CatalogSession $catalogSession ) { - $this->catalogSession = $catalogSession; $this->depersonalizeChecker = $depersonalizeChecker; + $this->catalogSession = $catalogSession; } /** - * After generate Xml + * Change sensitive customer data if the depersonalization is needed. * - * @param \Magento\Framework\View\LayoutInterface $subject - * @param \Magento\Framework\View\LayoutInterface $result - * @return \Magento\Framework\View\LayoutInterface + * @param LayoutInterface $subject + * @return void */ - public function afterGenerateXml(\Magento\Framework\View\LayoutInterface $subject, $result) + public function afterGenerateElements(LayoutInterface $subject) { if ($this->depersonalizeChecker->checkIfDepersonalize($subject)) { $this->catalogSession->clearStorage(); } - return $result; } } diff --git a/app/code/Magento/Catalog/Model/Product.php b/app/code/Magento/Catalog/Model/Product.php index 7015fa0295cfb..a9907c1661bd8 100644 --- a/app/code/Magento/Catalog/Model/Product.php +++ b/app/code/Magento/Catalog/Model/Product.php @@ -10,6 +10,7 @@ use Magento\Catalog\Api\Data\ProductInterface; use Magento\Catalog\Api\ProductLinkRepositoryInterface; use Magento\Catalog\Model\Product\Attribute\Backend\Media\EntryConverterPool; +use Magento\Catalog\Model\Product\Configuration\Item\Option\OptionInterface; use Magento\Catalog\Model\FilterProductCustomAttribute; use Magento\Framework\Api\AttributeValueFactory; use Magento\Framework\App\Filesystem\DirectoryList; @@ -108,7 +109,7 @@ class Product extends \Magento\Catalog\Model\AbstractModel implements /** * Product object customization (not stored in DB) * - * @var array + * @var OptionInterface[] */ protected $_customOptions = []; @@ -460,6 +461,9 @@ public function __construct( $this->mediaGalleryEntryConverterPool = $mediaGalleryEntryConverterPool; $this->dataObjectHelper = $dataObjectHelper; $this->joinProcessor = $joinProcessor; + $this->eavConfig = $config ?? ObjectManager::getInstance()->get(\Magento\Eav\Model\Config::class); + $this->filterCustomAttribute = $filterCustomAttribute + ?? ObjectManager::getInstance()->get(FilterProductCustomAttribute::class); parent::__construct( $context, $registry, @@ -470,9 +474,6 @@ public function __construct( $resourceCollection, $data ); - $this->eavConfig = $config ?? ObjectManager::getInstance()->get(\Magento\Eav\Model\Config::class); - $this->filterCustomAttribute = $filterCustomAttribute - ?? ObjectManager::getInstance()->get(FilterProductCustomAttribute::class); } /** @@ -2062,7 +2063,7 @@ public function addCustomOption($code, $value, $product = null) /** * Sets custom options for the product * - * @param array $options Array of options + * @param OptionInterface[] $options Array of options * @return void */ public function setCustomOptions(array $options) @@ -2073,7 +2074,7 @@ public function setCustomOptions(array $options) /** * Get all custom options of the product * - * @return array + * @return OptionInterface[] */ public function getCustomOptions() { @@ -2084,14 +2085,11 @@ public function getCustomOptions() * Get product custom option info * * @param string $code - * @return array + * @return OptionInterface|null */ public function getCustomOption($code) { - if (isset($this->_customOptions[$code])) { - return $this->_customOptions[$code]; - } - return null; + return $this->_customOptions[$code] ?? null; } /** @@ -2101,11 +2099,7 @@ public function getCustomOption($code) */ public function hasCustomOptions() { - if (count($this->_customOptions)) { - return true; - } else { - return false; - } + return (bool)count($this->_customOptions); } /** @@ -2160,6 +2154,7 @@ public function reset() */ public function getCacheIdTags() { + // phpstan:ignore $tags = parent::getCacheIdTags(); $affectedCategoryIds = $this->getAffectedCategoryIds(); if (!$affectedCategoryIds) { @@ -2340,6 +2335,7 @@ public function isDisabled() public function getImage() { $this->getTypeInstance()->setImageFromChildProduct($this); + // phpstan:ignore return parent::getImage(); } @@ -2409,7 +2405,7 @@ public function reloadPriceInfo() * @return array * @todo refactor with converter for AbstractExtensibleModel */ - public function __toArray() + public function __toArray() //phpcs:ignore PHPCompatibility.FunctionNameRestrictions.ReservedFunctionNames { $data = $this->_data; $hasToArray = function ($model) { diff --git a/app/code/Magento/Catalog/Model/Product/Attribute/Backend/GroupPrice/AbstractGroupPrice.php b/app/code/Magento/Catalog/Model/Product/Attribute/Backend/GroupPrice/AbstractGroupPrice.php index e26717e47274c..68aeabfc70d34 100644 --- a/app/code/Magento/Catalog/Model/Product/Attribute/Backend/GroupPrice/AbstractGroupPrice.php +++ b/app/code/Magento/Catalog/Model/Product/Attribute/Backend/GroupPrice/AbstractGroupPrice.php @@ -97,17 +97,16 @@ protected function _getWebsiteCurrencyRates() ); foreach ($this->_storeManager->getWebsites() as $website) { /* @var $website \Magento\Store\Model\Website */ - if ($website->getBaseCurrencyCode() != $baseCurrency) { + $websiteBaseCurrency = $website->getBaseCurrencyCode(); + if ($websiteBaseCurrency !== $baseCurrency) { $rate = $this->_currencyFactory->create()->load( $baseCurrency - )->getRate( - $website->getBaseCurrencyCode() - ); + )->getRate($websiteBaseCurrency); if (!$rate) { $rate = 1; } $this->_rates[$website->getId()] = [ - 'code' => $website->getBaseCurrencyCode(), + 'code' => $websiteBaseCurrency, 'rate' => $rate, ]; } else { @@ -187,6 +186,7 @@ public function validate($object) } $compare = implode( '-', + // phpcs:ignore Magento2.Performance.ForeachArrayMerge array_merge( [$priceRow['website_id'], $priceRow['cust_group']], $this->_getAdditionalUniqueFields($priceRow) @@ -210,6 +210,7 @@ public function validate($object) if ($price['website_id'] == 0) { $compare = implode( '-', + // phpcs:ignore Magento2.Performance.ForeachArrayMerge array_merge( [$price['website_id'], $price['cust_group']], $this->_getAdditionalUniqueFields($price) @@ -234,6 +235,7 @@ public function validate($object) $globalCompare = implode( '-', + // phpcs:ignore Magento2.Performance.ForeachArrayMerge array_merge([0, $priceRow['cust_group']], $this->_getAdditionalUniqueFields($priceRow)) ); $websiteCurrency = $rates[$priceRow['website_id']]['code']; @@ -279,6 +281,7 @@ public function preparePriceData(array $priceData, $productTypeId, $websiteId) if (!array_filter($v)) { continue; } + // phpcs:ignore Magento2.Performance.ForeachArrayMerge $key = implode('-', array_merge([$v['cust_group']], $this->_getAdditionalUniqueFields($v))); if ($v['website_id'] == $websiteId) { $data[$key] = $v; diff --git a/app/code/Magento/Catalog/Model/Product/Attribute/Backend/TierPrice/UpdateHandler.php b/app/code/Magento/Catalog/Model/Product/Attribute/Backend/TierPrice/UpdateHandler.php index 0daa1dfb5c8eb..065eb4abc5e8f 100644 --- a/app/code/Magento/Catalog/Model/Product/Attribute/Backend/TierPrice/UpdateHandler.php +++ b/app/code/Magento/Catalog/Model/Product/Attribute/Backend/TierPrice/UpdateHandler.php @@ -129,7 +129,7 @@ private function updateValues(array $valuesToUpdate, array $oldValues): bool { $isChanged = false; foreach ($valuesToUpdate as $key => $value) { - if ((!empty($value['value']) + if ((($value['value'])!== null && (float)$oldValues[$key]['price'] !== $this->localeFormat->getNumber($value['value']) ) || $this->getPercentage($oldValues[$key]) !== $this->getPercentage($value) ) { diff --git a/app/code/Magento/Catalog/Model/Product/Attribute/Source/Status.php b/app/code/Magento/Catalog/Model/Product/Attribute/Source/Status.php index 24abb8471d477..0da6da94fde5c 100644 --- a/app/code/Magento/Catalog/Model/Product/Attribute/Source/Status.php +++ b/app/code/Magento/Catalog/Model/Product/Attribute/Source/Status.php @@ -37,8 +37,7 @@ public function getVisibleStatusIds() } /** - * Retrieve Saleable Status Ids - * Default Product Enable status + * Retrieve Saleable Status Ids, default Product Enable status * * @return int[] */ @@ -51,6 +50,7 @@ public function getSaleableStatusIds() * Retrieve option array * * @return string[] + * phpcs:disable Magento2.Functions.StaticFunction */ public static function getOptionArray() { @@ -83,7 +83,7 @@ public function getOptionText($optionId) { $options = self::getOptionArray(); - return isset($options[$optionId]) ? $options[$optionId] : null; + return $options[$optionId] ?? null; } /** diff --git a/app/code/Magento/Catalog/Model/Product/Copier.php b/app/code/Magento/Catalog/Model/Product/Copier.php index a7f7bad1a5167..b04d3da8f0223 100644 --- a/app/code/Magento/Catalog/Model/Product/Copier.php +++ b/app/code/Magento/Catalog/Model/Product/Copier.php @@ -8,7 +8,13 @@ use Magento\Catalog\Api\Data\ProductInterface; use Magento\Catalog\Model\Attribute\ScopeOverriddenValue; use Magento\Catalog\Model\Product; +use Magento\Catalog\Model\Product\Attribute\Source\Status; +use Magento\Catalog\Model\Product\Option\Repository as OptionRepository; use Magento\Catalog\Model\ProductFactory; +use Magento\Framework\App\ObjectManager; +use Magento\Framework\EntityManager\MetadataPool; +use Magento\Store\Model\Store; +use Magento\UrlRewrite\Model\Exception\UrlAlreadyExistsException; /** * Catalog product copier. @@ -35,9 +41,10 @@ class Copier protected $productFactory; /** - * @var \Magento\Framework\EntityManager\MetadataPool + * @var MetadataPool */ protected $metadataPool; + /** * @var ScopeOverriddenValue */ @@ -47,52 +54,57 @@ class Copier * @param CopyConstructorInterface $copyConstructor * @param ProductFactory $productFactory * @param ScopeOverriddenValue $scopeOverriddenValue + * @param OptionRepository|null $optionRepository + * @param MetadataPool|null $metadataPool */ public function __construct( CopyConstructorInterface $copyConstructor, ProductFactory $productFactory, - ScopeOverriddenValue $scopeOverriddenValue + ScopeOverriddenValue $scopeOverriddenValue, + OptionRepository $optionRepository, + MetadataPool $metadataPool ) { $this->productFactory = $productFactory; $this->copyConstructor = $copyConstructor; $this->scopeOverriddenValue = $scopeOverriddenValue; + $this->optionRepository = $optionRepository; + $this->metadataPool = $metadataPool; } /** * Create product duplicate * - * @param \Magento\Catalog\Model\Product $product - * @return \Magento\Catalog\Model\Product + * @param Product $product + * @return Product */ - public function copy(Product $product) + public function copy(Product $product): Product { $product->getWebsiteIds(); $product->getCategoryIds(); - /** @var \Magento\Framework\EntityManager\EntityMetadataInterface $metadata */ - $metadata = $this->getMetadataPool()->getMetadata(ProductInterface::class); + $metadata = $this->metadataPool->getMetadata(ProductInterface::class); - /** @var \Magento\Catalog\Model\Product $duplicate */ + /** @var Product $duplicate */ $duplicate = $this->productFactory->create(); $productData = $product->getData(); $productData = $this->removeStockItem($productData); $duplicate->setData($productData); $duplicate->setOptions([]); + $duplicate->setMetaTitle(null); + $duplicate->setMetaKeyword(null); + $duplicate->setMetaDescription(null); $duplicate->setIsDuplicate(true); $duplicate->setOriginalLinkId($product->getData($metadata->getLinkField())); - $duplicate->setStatus(\Magento\Catalog\Model\Product\Attribute\Source\Status::STATUS_DISABLED); + $duplicate->setStatus(Status::STATUS_DISABLED); $duplicate->setCreatedAt(null); $duplicate->setUpdatedAt(null); $duplicate->setId(null); - $duplicate->setStoreId(\Magento\Store\Model\Store::DEFAULT_STORE_ID); + $duplicate->setStoreId(Store::DEFAULT_STORE_ID); $this->copyConstructor->build($product, $duplicate); $this->setDefaultUrl($product, $duplicate); $this->setStoresUrl($product, $duplicate); - $this->getOptionRepository()->duplicate($product, $duplicate); - $product->getResource()->duplicate( - $product->getData($metadata->getLinkField()), - $duplicate->getData($metadata->getLinkField()) - ); + $this->optionRepository->duplicate($product, $duplicate); + return $duplicate; } @@ -105,11 +117,11 @@ public function copy(Product $product) */ private function setDefaultUrl(Product $product, Product $duplicate) : void { - $duplicate->setStoreId(\Magento\Store\Model\Store::DEFAULT_STORE_ID); + $duplicate->setStoreId(Store::DEFAULT_STORE_ID); $resource = $product->getResource(); $attribute = $resource->getAttribute('url_key'); $productId = $product->getId(); - $urlKey = $resource->getAttributeRawValue($productId, 'url_key', \Magento\Store\Model\Store::DEFAULT_STORE_ID); + $urlKey = $resource->getAttributeRawValue($productId, 'url_key', Store::DEFAULT_STORE_ID); do { $urlKey = $this->modifyUrl($urlKey); $duplicate->setUrlKey($urlKey); @@ -123,13 +135,16 @@ private function setDefaultUrl(Product $product, Product $duplicate) : void * * @param Product $product * @param Product $duplicate + * * @return void + * @throws UrlAlreadyExistsException */ private function setStoresUrl(Product $product, Product $duplicate) : void { $storeIds = $duplicate->getStoreIds(); $productId = $product->getId(); $productResource = $product->getResource(); + $attribute = $productResource->getAttribute('url_key'); $duplicate->setData('save_rewrites_history', false); foreach ($storeIds as $storeId) { $useDefault = !$this->scopeOverriddenValue->containsValue( @@ -141,22 +156,25 @@ private function setStoresUrl(Product $product, Product $duplicate) : void if ($useDefault) { continue; } - $isDuplicateSaved = false; + $duplicate->setStoreId($storeId); $urlKey = $productResource->getAttributeRawValue($productId, 'url_key', $storeId); + $iteration = 0; + do { + if ($iteration === 10) { + throw new UrlAlreadyExistsException(); + } + $urlKey = $this->modifyUrl($urlKey); $duplicate->setUrlKey($urlKey); - $duplicate->setData('url_path', null); - try { - $duplicate->save(); - $isDuplicateSaved = true; - // phpcs:ignore Magento2.CodeAnalysis.EmptyBlock - } catch (\Magento\Framework\Exception\AlreadyExistsException $e) { - } - } while (!$isDuplicateSaved); + $iteration++; + } while (!$attribute->getEntity()->checkAttributeUniqueValue($attribute, $duplicate)); + $duplicate->setData('url_path', null); + $productResource->saveAttribute($duplicate, 'url_path'); + $productResource->saveAttribute($duplicate, 'url_key'); } - $duplicate->setStoreId(\Magento\Store\Model\Store::DEFAULT_STORE_ID); + $duplicate->setStoreId(Store::DEFAULT_STORE_ID); } /** @@ -168,38 +186,8 @@ private function setStoresUrl(Product $product, Product $duplicate) : void private function modifyUrl(string $urlKey) : string { return preg_match('/(.*)-(\d+)$/', $urlKey, $matches) - ? $matches[1] . '-' . ($matches[2] + 1) - : $urlKey . '-1'; - } - - /** - * Returns product option repository. - * - * @return Option\Repository - * @deprecated 101.0.0 - */ - private function getOptionRepository() - { - if (null === $this->optionRepository) { - $this->optionRepository = \Magento\Framework\App\ObjectManager::getInstance() - ->get(\Magento\Catalog\Model\Product\Option\Repository::class); - } - return $this->optionRepository; - } - - /** - * Returns metadata pool. - * - * @return \Magento\Framework\EntityManager\MetadataPool - * @deprecated 101.0.0 - */ - private function getMetadataPool() - { - if (null === $this->metadataPool) { - $this->metadataPool = \Magento\Framework\App\ObjectManager::getInstance() - ->get(\Magento\Framework\EntityManager\MetadataPool::class); - } - return $this->metadataPool; + ? $matches[1] . '-' . ($matches[2] + 1) + : $urlKey . '-1'; } /** @@ -208,7 +196,7 @@ private function getMetadataPool() * @param array $productData * @return array */ - private function removeStockItem(array $productData) + private function removeStockItem(array $productData): array { if (isset($productData[ProductInterface::EXTENSION_ATTRIBUTES_KEY])) { $extensionAttributes = $productData[ProductInterface::EXTENSION_ATTRIBUTES_KEY]; diff --git a/app/code/Magento/Catalog/Model/Product/Gallery/EntryResolver.php b/app/code/Magento/Catalog/Model/Product/Gallery/EntryResolver.php index 36eb71abe2a5d..1f36b37c34f6f 100644 --- a/app/code/Magento/Catalog/Model/Product/Gallery/EntryResolver.php +++ b/app/code/Magento/Catalog/Model/Product/Gallery/EntryResolver.php @@ -9,6 +9,9 @@ use Magento\Catalog\Model\Product; +/** + * Manage entryes + */ class EntryResolver { /** @@ -27,7 +30,7 @@ public function getEntryFilePathById(Product $product, $entryId) foreach ($mediaGalleryData['images'] as $image) { if (isset($image['value_id']) && $image['value_id'] == $entryId) { - return isset($image['file']) ? $image['file'] : null; + return $image['file'] ?? null; } } return null; @@ -49,7 +52,7 @@ public function getEntryIdByFilePath(Product $product, $filePath) foreach ($mediaGalleryData['images'] as $image) { if (isset($image['file']) && $image['file'] == $filePath) { - return isset($image['value_id']) ? $image['value_id'] : null; + return $image['value_id'] ?? null; } } return null; diff --git a/app/code/Magento/Catalog/Model/Product/Gallery/GalleryManagement.php b/app/code/Magento/Catalog/Model/Product/Gallery/GalleryManagement.php index a9afb7cec45e2..6a078a915119c 100644 --- a/app/code/Magento/Catalog/Model/Product/Gallery/GalleryManagement.php +++ b/app/code/Magento/Catalog/Model/Product/Gallery/GalleryManagement.php @@ -61,6 +61,10 @@ public function create($sku, ProductAttributeMediaGalleryEntryInterface $entry) $existingMediaGalleryEntries = $product->getMediaGalleryEntries(); $existingEntryIds = []; if ($existingMediaGalleryEntries == null) { + // set all media types if not specified + if ($entry->getTypes() == null) { + $entry->setTypes(array_keys($product->getMediaAttributes())); + } $existingMediaGalleryEntries = [$entry]; } else { foreach ($existingMediaGalleryEntries as $existingEntries) { diff --git a/app/code/Magento/Catalog/Model/Product/Image/ParamsBuilder.php b/app/code/Magento/Catalog/Model/Product/Image/ParamsBuilder.php index c0f4e83ef3de4..ecdb3b2829b90 100644 --- a/app/code/Magento/Catalog/Model/Product/Image/ParamsBuilder.php +++ b/app/code/Magento/Catalog/Model/Product/Image/ParamsBuilder.php @@ -132,9 +132,10 @@ private function getWatermark(string $type, int $scopeId = null): array if ($file) { $size = explode( 'x', - $this->scopeConfig->getValue( + (string) $this->scopeConfig->getValue( "design/watermark/{$type}_size", - ScopeInterface::SCOPE_STORE + ScopeInterface::SCOPE_STORE, + $scopeId ) ); $opacity = $this->scopeConfig->getValue( diff --git a/app/code/Magento/Catalog/Model/Product/Option.php b/app/code/Magento/Catalog/Model/Product/Option.php index 3a0920fb1c530..128f420e033c2 100644 --- a/app/code/Magento/Catalog/Model/Product/Option.php +++ b/app/code/Magento/Catalog/Model/Product/Option.php @@ -3,6 +3,7 @@ * Copyright © Magento, Inc. All rights reserved. * See COPYING.txt for license details. */ +declare(strict_types=1); namespace Magento\Catalog\Model\Product; @@ -16,8 +17,10 @@ use Magento\Catalog\Model\Product\Option\Type\File; use Magento\Catalog\Model\Product\Option\Type\Select; use Magento\Catalog\Model\Product\Option\Type\Text; +use Magento\Catalog\Model\Product\Option\Value; use Magento\Catalog\Model\ResourceModel\Product\Option\Value\Collection; -use Magento\Catalog\Pricing\Price\BasePrice; +use Magento\Catalog\Pricing\Price\CalculateCustomOptionCatalogRule; +use Magento\Framework\App\ObjectManager; use Magento\Framework\EntityManager\MetadataPool; use Magento\Framework\Exception\LocalizedException; use Magento\Framework\Model\AbstractExtensibleModel; @@ -123,6 +126,11 @@ class Option extends AbstractExtensibleModel implements ProductCustomOptionInter */ private $customOptionValuesFactory; + /** + * @var CalculateCustomOptionCatalogRule + */ + private $calculateCustomOptionCatalogRule; + /** * @param \Magento\Framework\Model\Context $context * @param \Magento\Framework\Registry $registry @@ -138,6 +146,7 @@ class Option extends AbstractExtensibleModel implements ProductCustomOptionInter * @param ProductCustomOptionValuesInterfaceFactory|null $customOptionValuesFactory * @param array $optionGroups * @param array $optionTypesToGroups + * @param CalculateCustomOptionCatalogRule $calculateCustomOptionCatalogRule * @SuppressWarnings(PHPMD.ExcessiveParameterList) */ public function __construct( @@ -154,14 +163,17 @@ public function __construct( array $data = [], ProductCustomOptionValuesInterfaceFactory $customOptionValuesFactory = null, array $optionGroups = [], - array $optionTypesToGroups = [] + array $optionTypesToGroups = [], + CalculateCustomOptionCatalogRule $calculateCustomOptionCatalogRule = null ) { $this->productOptionValue = $productOptionValue; $this->optionTypeFactory = $optionFactory; $this->string = $string; $this->validatorPool = $validatorPool; $this->customOptionValuesFactory = $customOptionValuesFactory ?: - \Magento\Framework\App\ObjectManager::getInstance()->get(ProductCustomOptionValuesInterfaceFactory::class); + ObjectManager::getInstance()->get(ProductCustomOptionValuesInterfaceFactory::class); + $this->calculateCustomOptionCatalogRule = $calculateCustomOptionCatalogRule ?? + ObjectManager::getInstance()->get(CalculateCustomOptionCatalogRule::class); $this->optionGroups = $optionGroups ?: [ self::OPTION_GROUP_DATE => Date::class, self::OPTION_GROUP_FILE => File::class, @@ -462,10 +474,12 @@ public function afterSave() */ public function getPrice($flag = false) { - if ($flag && $this->getPriceType() == self::$typePercent) { - $basePrice = $this->getProduct()->getPriceInfo()->getPrice(BasePrice::PRICE_CODE)->getValue(); - $price = $basePrice * ($this->_getData(self::KEY_PRICE) / 100); - return $price; + if ($flag) { + return $this->calculateCustomOptionCatalogRule->execute( + $this->getProduct(), + (float)$this->getData(self::KEY_PRICE), + $this->getPriceType() === Value::TYPE_PERCENT + ); } return $this->_getData(self::KEY_PRICE); } @@ -952,7 +966,7 @@ public function setExtensionAttributes( private function getOptionRepository() { if (null === $this->optionRepository) { - $this->optionRepository = \Magento\Framework\App\ObjectManager::getInstance() + $this->optionRepository = ObjectManager::getInstance() ->get(\Magento\Catalog\Model\Product\Option\Repository::class); } return $this->optionRepository; @@ -966,7 +980,7 @@ private function getOptionRepository() private function getMetadataPool() { if (null === $this->metadataPool) { - $this->metadataPool = \Magento\Framework\App\ObjectManager::getInstance() + $this->metadataPool = ObjectManager::getInstance() ->get(\Magento\Framework\EntityManager\MetadataPool::class); } return $this->metadataPool; diff --git a/app/code/Magento/Catalog/Model/Product/Option/Type/DefaultType.php b/app/code/Magento/Catalog/Model/Product/Option/Type/DefaultType.php index c388be8b6f394..be7f1921afccf 100644 --- a/app/code/Magento/Catalog/Model/Product/Option/Type/DefaultType.php +++ b/app/code/Magento/Catalog/Model/Product/Option/Type/DefaultType.php @@ -3,17 +3,26 @@ * Copyright © Magento, Inc. All rights reserved. * See COPYING.txt for license details. */ +declare(strict_types=1); namespace Magento\Catalog\Model\Product\Option\Type; -use Magento\Framework\Exception\LocalizedException; use Magento\Catalog\Api\Data\ProductCustomOptionInterface; +use Magento\Catalog\Model\Product; +use Magento\Catalog\Model\Product\Configuration\Item\ItemInterface; +use Magento\Catalog\Model\Product\Configuration\Item\Option\OptionInterface; +use Magento\Catalog\Model\Product\Option; +use Magento\Catalog\Model\Product\Option\Value; +use Magento\Catalog\Pricing\Price\CalculateCustomOptionCatalogRule; +use Magento\Framework\App\ObjectManager; +use Magento\Framework\Exception\LocalizedException; /** * Catalog product option default type * * @api * @author Magento Core Team <core@magentocommerce.com> + * @SuppressWarnings(PHPMD.CookieAndSessionMisuse) * @SuppressWarnings(PHPMD.CouplingBetweenObjects) * @since 100.0.2 */ @@ -22,14 +31,14 @@ class DefaultType extends \Magento\Framework\DataObject /** * Option Instance * - * @var \Magento\Catalog\Model\Product\Option + * @var Option */ protected $_option; /** * Product Instance * - * @var \Magento\Catalog\Model\Product + * @var Product */ protected $_product; @@ -54,27 +63,36 @@ class DefaultType extends \Magento\Framework\DataObject */ protected $_checkoutSession; + /** + * @var CalculateCustomOptionCatalogRule + */ + private $calculateCustomOptionCatalogRule; + /** * Construct * * @param \Magento\Checkout\Model\Session $checkoutSession * @param \Magento\Framework\App\Config\ScopeConfigInterface $scopeConfig * @param array $data + * @param CalculateCustomOptionCatalogRule $calculateCustomOptionCatalogRule */ public function __construct( \Magento\Checkout\Model\Session $checkoutSession, \Magento\Framework\App\Config\ScopeConfigInterface $scopeConfig, - array $data = [] + array $data = [], + CalculateCustomOptionCatalogRule $calculateCustomOptionCatalogRule = null ) { $this->_checkoutSession = $checkoutSession; parent::__construct($data); $this->_scopeConfig = $scopeConfig; + $this->calculateCustomOptionCatalogRule = $calculateCustomOptionCatalogRule ?? ObjectManager::getInstance() + ->get(CalculateCustomOptionCatalogRule::class); } /** * Option Instance setter * - * @param \Magento\Catalog\Model\Product\Option $option + * @param Option $option * @return $this */ public function setOption($option) @@ -86,12 +104,12 @@ public function setOption($option) /** * Option Instance getter * + * @return Option * @throws \Magento\Framework\Exception\LocalizedException - * @return \Magento\Catalog\Model\Product\Option */ public function getOption() { - if ($this->_option instanceof \Magento\Catalog\Model\Product\Option) { + if ($this->_option instanceof Option) { return $this->_option; } throw new LocalizedException(__('The option instance type in options group is incorrect.')); @@ -100,7 +118,7 @@ public function getOption() /** * Product Instance setter * - * @param \Magento\Catalog\Model\Product $product + * @param Product $product * @return $this */ public function setProduct($product) @@ -112,12 +130,12 @@ public function setProduct($product) /** * Product Instance getter * + * @return Product * @throws \Magento\Framework\Exception\LocalizedException - * @return \Magento\Catalog\Model\Product */ public function getProduct() { - if ($this->_product instanceof \Magento\Catalog\Model\Product) { + if ($this->_product instanceof Product) { return $this->_product; } throw new LocalizedException(__('The product instance type in options group is incorrect.')); @@ -126,15 +144,12 @@ public function getProduct() /** * Getter for Configuration Item Option * - * @return \Magento\Catalog\Model\Product\Configuration\Item\Option\OptionInterface + * @return OptionInterface * @throws LocalizedException */ public function getConfigurationItemOption() { - if ($this->_getData( - 'configuration_item_option' - ) instanceof \Magento\Catalog\Model\Product\Configuration\Item\Option\OptionInterface - ) { + if ($this->_getData('configuration_item_option') instanceof OptionInterface) { return $this->_getData('configuration_item_option'); } @@ -149,14 +164,12 @@ public function getConfigurationItemOption() /** * Getter for Configuration Item * - * @return \Magento\Catalog\Model\Product\Configuration\Item\ItemInterface + * @return ItemInterface * @throws \Magento\Framework\Exception\LocalizedException */ public function getConfigurationItem() { - if ($this->_getData( - 'configuration_item' - ) instanceof \Magento\Catalog\Model\Product\Configuration\Item\ItemInterface + if ($this->_getData('configuration_item') instanceof ItemInterface ) { return $this->_getData('configuration_item'); } @@ -341,7 +354,11 @@ public function getOptionPrice($optionValue, $basePrice) { $option = $this->getOption(); - return $this->_getChargeableOptionPrice($option->getPrice(), $option->getPriceType() == 'percent', $basePrice); + return $this->calculateCustomOptionCatalogRule->execute( + $option->getProduct(), + (float)$option->getPrice(), + $option->getPriceType() === Value::TYPE_PERCENT + ); } /** @@ -368,14 +385,14 @@ public function getProductOptions() $options = $this->getProduct()->getOptions(); if ($options != null) { foreach ($options as $_option) { - /* @var $option \Magento\Catalog\Model\Product\Option */ + /* @var $option Option */ $this->_productOptions[$this->getProduct()->getId()][$_option->getTitle()] = [ 'option_id' => $_option->getId(), ]; if ($_option->getGroupByType() == ProductCustomOptionInterface::OPTION_GROUP_SELECT) { $optionValues = []; foreach ($_option->getValues() as $_value) { - /* @var $value \Magento\Catalog\Model\Product\Option\Value */ + /* @var $value Value */ $optionValues[$_value->getTitle()] = $_value->getId(); } $this->_productOptions[$this @@ -395,12 +412,14 @@ public function getProductOptions() } /** + * Return final chargeable price for option + * * @param float $price Price of option * @param boolean $isPercent Price type - percent or fixed * @param float $basePrice For percent price type * @return float * @deprecated 102.0.4 typo in method name - * @see _getChargeableOptionPrice + * @see CalculateCustomOptionCatalogRule::execute */ protected function _getChargableOptionPrice($price, $isPercent, $basePrice) { @@ -414,6 +433,8 @@ protected function _getChargableOptionPrice($price, $isPercent, $basePrice) * @param boolean $isPercent Price type - percent or fixed * @param float $basePrice For percent price type * @return float + * @deprecated + * @see CalculateCustomOptionCatalogRule::execute */ protected function _getChargeableOptionPrice($price, $isPercent, $basePrice) { diff --git a/app/code/Magento/Catalog/Model/Product/Option/Type/Select.php b/app/code/Magento/Catalog/Model/Product/Option/Type/Select.php index d2766b1bbb054..8eebd3e91c2ee 100644 --- a/app/code/Magento/Catalog/Model/Product/Option/Type/Select.php +++ b/app/code/Magento/Catalog/Model/Product/Option/Type/Select.php @@ -3,9 +3,13 @@ * Copyright © Magento, Inc. All rights reserved. * See COPYING.txt for license details. */ +declare(strict_types=1); namespace Magento\Catalog\Model\Product\Option\Type; +use Magento\Catalog\Model\Product\Option\Value; +use Magento\Catalog\Pricing\Price\CalculateCustomOptionCatalogRule; +use Magento\Framework\App\ObjectManager; use Magento\Framework\Exception\LocalizedException; /** @@ -37,6 +41,11 @@ class Select extends \Magento\Catalog\Model\Product\Option\Type\DefaultType */ private $singleSelectionTypes; + /** + * @var CalculateCustomOptionCatalogRule + */ + private $calculateCustomOptionCatalogRule; + /** * @param \Magento\Checkout\Model\Session $checkoutSession * @param \Magento\Framework\App\Config\ScopeConfigInterface $scopeConfig @@ -44,6 +53,7 @@ class Select extends \Magento\Catalog\Model\Product\Option\Type\DefaultType * @param \Magento\Framework\Escaper $escaper * @param array $data * @param array $singleSelectionTypes + * @param CalculateCustomOptionCatalogRule $calculateCustomOptionCatalogRule */ public function __construct( \Magento\Checkout\Model\Session $checkoutSession, @@ -51,7 +61,8 @@ public function __construct( \Magento\Framework\Stdlib\StringUtils $string, \Magento\Framework\Escaper $escaper, array $data = [], - array $singleSelectionTypes = [] + array $singleSelectionTypes = [], + CalculateCustomOptionCatalogRule $calculateCustomOptionCatalogRule = null ) { $this->string = $string; $this->_escaper = $escaper; @@ -61,6 +72,8 @@ public function __construct( 'drop_down' => \Magento\Catalog\Api\Data\ProductCustomOptionInterface::OPTION_TYPE_DROP_DOWN, 'radio' => \Magento\Catalog\Api\Data\ProductCustomOptionInterface::OPTION_TYPE_RADIO, ]; + $this->calculateCustomOptionCatalogRule = $calculateCustomOptionCatalogRule ?? ObjectManager::getInstance() + ->get(CalculateCustomOptionCatalogRule::class); } /** @@ -248,10 +261,10 @@ public function getOptionPrice($optionValue, $basePrice) foreach (explode(',', $optionValue) as $value) { $_result = $option->getValueById($value); if ($_result) { - $result += $this->_getChargeableOptionPrice( - $_result->getPrice(), - $_result->getPriceType() == 'percent', - $basePrice + $result += $this->calculateCustomOptionCatalogRule->execute( + $option->getProduct(), + (float)$_result->getPrice(), + $_result->getPriceType() === Value::TYPE_PERCENT ); } else { if ($this->getListener()) { @@ -263,10 +276,10 @@ public function getOptionPrice($optionValue, $basePrice) } elseif ($this->_isSingleSelection()) { $_result = $option->getValueById($optionValue); if ($_result) { - $result = $this->_getChargeableOptionPrice( - $_result->getPrice(), - $_result->getPriceType() == 'percent', - $basePrice + $result = $this->calculateCustomOptionCatalogRule->execute( + $option->getProduct(), + (float)$_result->getPrice(), + $_result->getPriceType() === Value::TYPE_PERCENT ); } else { if ($this->getListener()) { diff --git a/app/code/Magento/Catalog/Model/Product/Option/Type/Text.php b/app/code/Magento/Catalog/Model/Product/Option/Type/Text.php index 9ffe75e513bce..71a6556dc8858 100644 --- a/app/code/Magento/Catalog/Model/Product/Option/Type/Text.php +++ b/app/code/Magento/Catalog/Model/Product/Option/Type/Text.php @@ -10,6 +10,8 @@ /** * Catalog product option text type + * + * @SuppressWarnings(PHPMD.CookieAndSessionMisuse) */ class Text extends \Magento\Catalog\Model\Product\Option\Type\DefaultType { @@ -68,6 +70,7 @@ public function validateUserValue($values) // Check maximal length limit $maxCharacters = $option->getMaxCharacters(); + $value = $this->normalizeNewLineSymbols($value); if ($maxCharacters > 0 && $this->string->strlen($value) > $maxCharacters) { $this->setIsValid(false); throw new LocalizedException(__('The text is too long. Shorten the text and try again.')); @@ -101,4 +104,15 @@ public function getFormattedOptionValue($value) { return $this->_escaper->escapeHtml($value); } + + /** + * Normalize newline symbols + * + * @param string $value + * @return string + */ + private function normalizeNewLineSymbols(string $value) + { + return str_replace(["\r\n", "\n\r", "\r"], "\n", $value); + } } diff --git a/app/code/Magento/Catalog/Model/Product/Option/Value.php b/app/code/Magento/Catalog/Model/Product/Option/Value.php index ebbc060c99edf..783bda4699792 100644 --- a/app/code/Magento/Catalog/Model/Product/Option/Value.php +++ b/app/code/Magento/Catalog/Model/Product/Option/Value.php @@ -3,13 +3,16 @@ * Copyright © Magento, Inc. All rights reserved. * See COPYING.txt for license details. */ +declare(strict_types=1); namespace Magento\Catalog\Model\Product\Option; use Magento\Catalog\Model\Product; use Magento\Catalog\Model\Product\Option; -use Magento\Framework\Model\AbstractModel; use Magento\Catalog\Pricing\Price\BasePrice; +use Magento\Catalog\Pricing\Price\CalculateCustomOptionCatalogRule; +use Magento\Framework\App\ObjectManager; +use Magento\Framework\Model\AbstractModel; use Magento\Catalog\Pricing\Price\CustomOptionPriceCalculator; use Magento\Catalog\Pricing\Price\RegularPrice; @@ -69,6 +72,11 @@ class Value extends AbstractModel implements \Magento\Catalog\Api\Data\ProductCu */ private $customOptionPriceCalculator; + /** + * @var CalculateCustomOptionCatalogRule + */ + private $calculateCustomOptionCatalogRule; + /** * @param \Magento\Framework\Model\Context $context * @param \Magento\Framework\Registry $registry @@ -77,6 +85,7 @@ class Value extends AbstractModel implements \Magento\Catalog\Api\Data\ProductCu * @param \Magento\Framework\Data\Collection\AbstractDb $resourceCollection * @param array $data * @param CustomOptionPriceCalculator|null $customOptionPriceCalculator + * @param CalculateCustomOptionCatalogRule|null $CalculateCustomOptionCatalogRule */ public function __construct( \Magento\Framework\Model\Context $context, @@ -85,11 +94,14 @@ public function __construct( \Magento\Framework\Model\ResourceModel\AbstractResource $resource = null, \Magento\Framework\Data\Collection\AbstractDb $resourceCollection = null, array $data = [], - CustomOptionPriceCalculator $customOptionPriceCalculator = null + CustomOptionPriceCalculator $customOptionPriceCalculator = null, + CalculateCustomOptionCatalogRule $CalculateCustomOptionCatalogRule = null ) { $this->_valueCollectionFactory = $valueCollectionFactory; $this->customOptionPriceCalculator = $customOptionPriceCalculator - ?? \Magento\Framework\App\ObjectManager::getInstance()->get(CustomOptionPriceCalculator::class); + ?? ObjectManager::getInstance()->get(CustomOptionPriceCalculator::class); + $this->calculateCustomOptionCatalogRule = $CalculateCustomOptionCatalogRule + ?? ObjectManager::getInstance()->get(CalculateCustomOptionCatalogRule::class); parent::__construct( $context, @@ -101,6 +113,8 @@ public function __construct( } /** + * Override parent _construct method + * * @return void */ protected function _construct() @@ -109,6 +123,8 @@ protected function _construct() } /** + * Add value. + * * @codeCoverageIgnoreStart * @param mixed $value * @return $this @@ -120,6 +136,8 @@ public function addValue($value) } /** + * Get values. + * * @return array */ public function getValues() @@ -128,6 +146,8 @@ public function getValues() } /** + * Set values. + * * @param array $values * @return $this */ @@ -138,6 +158,8 @@ public function setValues($values) } /** + * Unset values. + * * @return $this */ public function unsetValues() @@ -147,6 +169,8 @@ public function unsetValues() } /** + * Set option. + * * @param Option $option * @return $this */ @@ -157,6 +181,8 @@ public function setOption(Option $option) } /** + * Unset option. + * * @return $this */ public function unsetOption() @@ -166,7 +192,7 @@ public function unsetOption() } /** - * Enter description here... + * Get option. * * @return Option */ @@ -176,6 +202,8 @@ public function getOption() } /** + * Set product. + * * @param Product $product * @return $this */ @@ -188,6 +216,8 @@ public function setProduct($product) //@codeCoverageIgnoreEnd /** + * Get product. + * * @return Product */ public function getProduct() @@ -199,7 +229,10 @@ public function getProduct() } /** + * Save values. + * * @return $this + * @throws \Exception */ public function saveValues() { @@ -225,8 +258,9 @@ public function saveValues() } /** - * Return price. If $flag is true and price is percent - * return converted percent to price + * Return price. + * + * If $flag is true and price is percent return converted percent to price * * @param bool $flag * @return float|int @@ -234,7 +268,11 @@ public function saveValues() public function getPrice($flag = false) { if ($flag) { - return $this->customOptionPriceCalculator->getOptionPriceByPriceCode($this, BasePrice::PRICE_CODE); + return $this->calculateCustomOptionCatalogRule->execute( + $this->getProduct(), + (float)$this->getData(self::KEY_PRICE), + $this->getPriceType() === self::TYPE_PERCENT + ); } return $this->_getData(self::KEY_PRICE); } @@ -250,7 +288,7 @@ public function getRegularPrice() } /** - * Enter description here... + * Get values collection. * * @param Option $option * @return \Magento\Catalog\Model\ResourceModel\Product\Option\Value\Collection @@ -268,6 +306,8 @@ public function getValuesCollection(Option $option) } /** + * Get values by option. + * * @param array $optionIds * @param int $option_id * @param int $store_id @@ -287,6 +327,8 @@ public function getValuesByOption($optionIds, $option_id, $store_id) } /** + * Delete value. + * * @param int $option_id * @return $this */ @@ -297,6 +339,8 @@ public function deleteValue($option_id) } /** + * Delete values. + * * @param int $option_type_id * @return $this */ diff --git a/app/code/Magento/Catalog/Model/Product/ProductFrontendAction/Synchronizer.php b/app/code/Magento/Catalog/Model/Product/ProductFrontendAction/Synchronizer.php index 24775a791e59f..b736700a4481d 100644 --- a/app/code/Magento/Catalog/Model/Product/ProductFrontendAction/Synchronizer.php +++ b/app/code/Magento/Catalog/Model/Product/ProductFrontendAction/Synchronizer.php @@ -20,6 +20,8 @@ * * Service which allows to sync product widget information, such as product id with db. In order to reuse this info * on different devices + * + * @SuppressWarnings(PHPMD.CookieAndSessionMisuse) */ class Synchronizer { @@ -94,6 +96,7 @@ public function __construct( * * @param string $namespace * @return int + * @throws \Magento\Framework\Exception\LocalizedException */ private function getLifeTimeByNamespace($namespace) { @@ -119,15 +122,23 @@ private function getLifeTimeByNamespace($namespace) * @param array $productsData (product action data, that came from frontend) * @param string $typeId namespace (type of action) * @return array + * @throws \Magento\Framework\Exception\LocalizedException */ private function filterNewestActions(array $productsData, $typeId) { $lifetime = $this->getLifeTimeByNamespace($typeId); $actionsNumber = $lifetime * self::TIME_TO_DO_ONE_ACTION; - uasort($productsData, function (array $firstProduct, array $secondProduct) { - return $firstProduct['added_at'] > $secondProduct['added_at']; - }); + uasort( + $productsData, + function (array $firstProduct, array $secondProduct) { + if (isset($firstProduct['added_at'], $secondProduct['added_at'])) { + return $firstProduct['added_at'] > $secondProduct['added_at']; + } + + return false; + } + ); return array_slice($productsData, 0, $actionsNumber, true); } @@ -159,6 +170,7 @@ private function getProductIdsByActions(array $actions) * @param array $productsData * @param string $typeId * @return void + * @throws \Exception */ public function syncActions(array $productsData, $typeId) { @@ -182,18 +194,19 @@ public function syncActions(array $productsData, $typeId) foreach ($collection as $item) { $this->entityManager->delete($item); } - - foreach ($productsData as $productId => $productData) { + foreach ($productsData as $productData) { /** @var ProductFrontendActionInterface $action */ - $action = $this->productFrontendActionFactory->create([ - 'data' => [ - 'visitor_id' => $customerId ? null : $visitorId, - 'customer_id' => $this->session->getCustomerId(), - 'added_at' => $productData['added_at'], - 'product_id' => $productId, - 'type_id' => $typeId + $action = $this->productFrontendActionFactory->create( + [ + 'data' => [ + 'visitor_id' => $customerId ? null : $visitorId, + 'customer_id' => $this->session->getCustomerId(), + 'added_at' => $productData['added_at'], + 'product_id' => $productData['product_id'], + 'type_id' => $typeId + ] ] - ]); + ); $this->entityManager->save($action); } diff --git a/app/code/Magento/Catalog/Model/Product/Type.php b/app/code/Magento/Catalog/Model/Product/Type.php index 4c973be20dee5..d7dc74e0d0cc3 100644 --- a/app/code/Magento/Catalog/Model/Product/Type.php +++ b/app/code/Magento/Catalog/Model/Product/Type.php @@ -6,43 +6,37 @@ namespace Magento\Catalog\Model\Product; use Magento\Catalog\Model\Product; +use Magento\Catalog\Model\Product\Type\Pool; +use Magento\Catalog\Model\Product\Type\Price; +use Magento\Catalog\Model\Product\Type\Price\Factory as PriceFactory; +use Magento\Catalog\Model\Product\Type\Simple; +use Magento\Catalog\Model\ProductTypes\ConfigInterface; use Magento\Framework\Data\OptionSourceInterface; +use Magento\Framework\Pricing\PriceInfo\Factory as PriceInfoFactory; /** * Product type model * + * * @api * @since 100.0.2 */ class Type implements OptionSourceInterface { - /**#@+ - * Available product types - */ const TYPE_SIMPLE = 'simple'; const TYPE_BUNDLE = 'bundle'; const TYPE_VIRTUAL = 'virtual'; - /**#@-*/ - /** - * Default product type - */ const DEFAULT_TYPE = 'simple'; - /** - * Default product type model - */ - const DEFAULT_TYPE_MODEL = \Magento\Catalog\Model\Product\Type\Simple::class; + const DEFAULT_TYPE_MODEL = Simple::class; - /** - * Default price model - */ - const DEFAULT_PRICE_MODEL = \Magento\Catalog\Model\Product\Type\Price::class; + const DEFAULT_PRICE_MODEL = Price::class; /** - * @var \Magento\Catalog\Model\ProductTypes\ConfigInterface + * @var ConfigInterface */ protected $_config; @@ -77,35 +71,35 @@ class Type implements OptionSourceInterface /** * Product type factory * - * @var \Magento\Catalog\Model\Product\Type\Pool + * @var Pool */ protected $_productTypePool; /** * Price model factory * - * @var \Magento\Catalog\Model\Product\Type\Price\Factory + * @var PriceFactory */ protected $_priceFactory; /** - * @var \Magento\Framework\Pricing\PriceInfo\Factory + * @var PriceInfoFactory */ protected $_priceInfoFactory; /** * Construct * - * @param \Magento\Catalog\Model\ProductTypes\ConfigInterface $config - * @param \Magento\Catalog\Model\Product\Type\Pool $productTypePool - * @param \Magento\Catalog\Model\Product\Type\Price\Factory $priceFactory - * @param \Magento\Framework\Pricing\PriceInfo\Factory $priceInfoFactory + * @param ConfigInterface $config + * @param Pool $productTypePool + * @param PriceFactory $priceFactory + * @param PriceInfoFactory $priceInfoFactory */ public function __construct( - \Magento\Catalog\Model\ProductTypes\ConfigInterface $config, - \Magento\Catalog\Model\Product\Type\Pool $productTypePool, - \Magento\Catalog\Model\Product\Type\Price\Factory $priceFactory, - \Magento\Framework\Pricing\PriceInfo\Factory $priceInfoFactory + ConfigInterface $config, + Pool $productTypePool, + PriceFactory $priceFactory, + PriceInfoFactory $priceInfoFactory ) { $this->_config = $config; $this->_productTypePool = $productTypePool; @@ -116,8 +110,8 @@ public function __construct( /** * Factory to product singleton product type instances * - * @param \Magento\Catalog\Model\Product $product - * @return \Magento\Catalog\Model\Product\Type\AbstractType + * @param \Magento\Catalog\Api\Data\ProductInterface $product + * @return \Magento\Catalog\Model\Product\Type\AbstractType */ public function factory($product) { @@ -139,8 +133,8 @@ public function factory($product) /** * Product type price model factory * - * @param string $productType - * @return \Magento\Catalog\Model\Product\Type\Price + * @param string $productType + * @return \Magento\Catalog\Model\Product\Type\Price */ public function priceFactory($productType) { diff --git a/app/code/Magento/Catalog/Model/Product/Type/Price.php b/app/code/Magento/Catalog/Model/Product/Type/Price.php index 814865fa75917..e702965270639 100644 --- a/app/code/Magento/Catalog/Model/Product/Type/Price.php +++ b/app/code/Magento/Catalog/Model/Product/Type/Price.php @@ -262,7 +262,7 @@ protected function _applyTierPrice($product, $qty, $finalPrice) $tierPrice = $product->getTierPrice($qty); if (is_numeric($tierPrice)) { - $finalPrice = min($finalPrice, $tierPrice); + $finalPrice = min($finalPrice, (float) $tierPrice); } return $finalPrice; } @@ -645,7 +645,7 @@ public function calculateSpecialPrice( ) { if ($specialPrice !== null && $specialPrice != false) { if ($this->_localeDate->isScopeDateInInterval($store, $specialPriceFrom, $specialPriceTo)) { - $finalPrice = min($finalPrice, $specialPrice); + $finalPrice = min($finalPrice, (float) $specialPrice); } } return $finalPrice; diff --git a/app/code/Magento/Catalog/Model/Product/Url.php b/app/code/Magento/Catalog/Model/Product/Url.php index 2760b0f9fddb6..8867cc7a690c1 100644 --- a/app/code/Magento/Catalog/Model/Product/Url.php +++ b/app/code/Magento/Catalog/Model/Product/Url.php @@ -101,15 +101,10 @@ public function getUrlInStore(\Magento\Catalog\Model\Product $product, $params = */ public function getProductUrl($product, $useSid = null) { - if ($useSid === null) { - $useSid = $this->sidResolver->getUseSessionInUrl(); - } - $params = []; if (!$useSid) { $params['_nosid'] = true; } - return $this->getUrl($product, $params); } diff --git a/app/code/Magento/Catalog/Model/Product/Visibility.php b/app/code/Magento/Catalog/Model/Product/Visibility.php index c05bda7838d78..cd1500406df44 100644 --- a/app/code/Magento/Catalog/Model/Product/Visibility.php +++ b/app/code/Magento/Catalog/Model/Product/Visibility.php @@ -133,7 +133,7 @@ public static function getAllOptions() public static function getOptionText($optionId) { $options = self::getOptionArray(); - return isset($options[$optionId]) ? $options[$optionId] : null; + return $options[$optionId] ?? null; } //phpcs:enable Magento2.Functions.StaticFunction diff --git a/app/code/Magento/Catalog/Model/Product/Webapi/ProductOutputProcessor.php b/app/code/Magento/Catalog/Model/Product/Webapi/ProductOutputProcessor.php new file mode 100644 index 0000000000000..4d683829219ba --- /dev/null +++ b/app/code/Magento/Catalog/Model/Product/Webapi/ProductOutputProcessor.php @@ -0,0 +1,101 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +declare(strict_types=1); + +namespace Magento\Catalog\Model\Product\Webapi; + +use Magento\Catalog\Api\Data\ProductInterface; +use Magento\Framework\Webapi\Request; +use Magento\Framework\Webapi\Rest\Request\DeserializerInterface; + +/** + * Class for checking empty array and remove it from the output result + */ +class ProductOutputProcessor +{ + /** + * @var Request + */ + private $request; + + /** + * @var DeserializerInterface + */ + private $deserializer; + + /** + * @param Request $request + * @param DeserializerInterface $deserializer + */ + public function __construct( + Request $request, + DeserializerInterface $deserializer + ) { + $this->request = $request; + $this->deserializer = $deserializer; + } + + /** + * Removing attribute from the result array if its null or empty + * + * @param ProductInterface $product + * @param array $result + * @return array + */ + public function execute( + ProductInterface $product, + array $result + ): array { + $requestContent = $this->request->getContent() ?? []; + if (empty($requestContent)) { + return $result; + } + $requestContentDetails = (array)$this->deserializer->deserialize($requestContent); + $requestProductList = $this->extractProductList($requestContentDetails); + + $requestProductList = array_filter( + $requestProductList, + function ($requestProduct) use ($product) { + return isset($requestProduct['sku']) && $requestProduct['sku'] === $product->getSku(); + } + ); + + if (empty($requestProductList)) { + return $result; + } + + $requestProduct = current($requestProductList); + + if (empty($product->getTierPrices()) && !array_key_exists('tier_prices', $requestProduct)) { + unset($result['tier_prices']); + } + + if (empty($product->getProductLinks()) && !array_key_exists('product_links', $requestProduct)) { + unset($result['product_links']); + } + + return $result; + } + + /** + * Extract product list from the request content details + * + * @param array $contentDetails + * @return array + */ + private function extractProductList(array $contentDetails): array + { + $productList = []; + $arrayIterator = new \RecursiveArrayIterator($contentDetails); + $iterator = new \RecursiveIteratorIterator($arrayIterator, \RecursiveIteratorIterator::SELF_FIRST); + foreach ($iterator as $iteratorKey => $iteratorValue) { + if ($iteratorKey === 'product') { + array_push($productList, $iteratorValue); + } + } + return $productList; + } +} diff --git a/app/code/Magento/Catalog/Model/ProductLink/Converter/ConverterPool.php b/app/code/Magento/Catalog/Model/ProductLink/Converter/ConverterPool.php index f58d1d6b69be8..a50c650e8b264 100644 --- a/app/code/Magento/Catalog/Model/ProductLink/Converter/ConverterPool.php +++ b/app/code/Magento/Catalog/Model/ProductLink/Converter/ConverterPool.php @@ -6,6 +6,9 @@ namespace Magento\Catalog\Model\ProductLink\Converter; +/** + * Return converter by link type + */ class ConverterPool { /** @@ -34,8 +37,6 @@ public function __construct(array $converters) */ public function getConverter($linkType) { - return isset($this->converters[$linkType]) - ? $this->converters[$linkType] - : $this->converters[$this->defaultConverterCode]; + return $this->converters[$linkType] ?? $this->converters[$this->defaultConverterCode]; } } diff --git a/app/code/Magento/Catalog/Model/ProductLink/Link.php b/app/code/Magento/Catalog/Model/ProductLink/Link.php index ebf90c0523818..48945fbef9d52 100644 --- a/app/code/Magento/Catalog/Model/ProductLink/Link.php +++ b/app/code/Magento/Catalog/Model/ProductLink/Link.php @@ -7,6 +7,8 @@ namespace Magento\Catalog\Model\ProductLink; /** + * @inheritdoc + * * @codeCoverageIgnore */ class Link extends \Magento\Framework\Model\AbstractExtensibleModel implements @@ -30,7 +32,7 @@ class Link extends \Magento\Framework\Model\AbstractExtensibleModel implements */ protected function _get($key) { - return isset($this->_data[$key]) ? $this->_data[$key] : null; + return $this->_data[$key] ?? null; } /** @@ -38,9 +40,11 @@ protected function _get($key) * * @return array * @todo refactor with converter for AbstractExtensibleModel + * phpcs:disable */ public function __toArray() { + //phpcs:enable $data = $this->_data; $hasToArray = function ($model) { return is_object($model) && method_exists($model, '__toArray') && is_callable([$model, '__toArray']); @@ -169,7 +173,7 @@ public function setPosition($position) } /** - * {@inheritdoc} + * @inheritdoc * * @return \Magento\Catalog\Api\Data\ProductLinkExtensionInterface|null */ @@ -184,7 +188,7 @@ public function getExtensionAttributes() } /** - * {@inheritdoc} + * @inheritdoc * * @param \Magento\Catalog\Api\Data\ProductLinkExtensionInterface $extensionAttributes * @return $this diff --git a/app/code/Magento/Catalog/Model/ProductLink/Management.php b/app/code/Magento/Catalog/Model/ProductLink/Management.php index 066549274b07c..017985e3f549f 100644 --- a/app/code/Magento/Catalog/Model/ProductLink/Management.php +++ b/app/code/Magento/Catalog/Model/ProductLink/Management.php @@ -6,37 +6,42 @@ namespace Magento\Catalog\Model\ProductLink; -use Magento\Catalog\Api\Data; use Magento\Framework\Exception\CouldNotSaveException; use Magento\Framework\Exception\NoSuchEntityException; use Magento\Framework\Exception\InputException; +use Magento\Catalog\Api\ProductRepositoryInterface; +use Magento\Catalog\Model\Product\LinkTypeProvider; +use Magento\Catalog\Api\ProductLinkManagementInterface; -class Management implements \Magento\Catalog\Api\ProductLinkManagementInterface +/** + * Manage product links from api + */ +class Management implements ProductLinkManagementInterface { /** - * @var \Magento\Catalog\Api\ProductRepositoryInterface + * @var ProductRepositoryInterface */ protected $productRepository; /** - * @var \Magento\Catalog\Model\Product\LinkTypeProvider + * @var LinkTypeProvider */ protected $linkTypeProvider; /** - * @param \Magento\Catalog\Api\ProductRepositoryInterface $productRepository - * @param \Magento\Catalog\Model\Product\LinkTypeProvider $linkTypeProvider + * @param ProductRepositoryInterface $productRepository + * @param LinkTypeProvider $linkTypeProvider */ public function __construct( - \Magento\Catalog\Api\ProductRepositoryInterface $productRepository, - \Magento\Catalog\Model\Product\LinkTypeProvider $linkTypeProvider + ProductRepositoryInterface $productRepository, + LinkTypeProvider $linkTypeProvider ) { $this->productRepository = $productRepository; $this->linkTypeProvider = $linkTypeProvider; } /** - * {@inheritdoc} + * @inheritdoc */ public function getLinkedItemsByType($sku, $type) { @@ -63,47 +68,42 @@ public function getLinkedItemsByType($sku, $type) } /** - * {@inheritdoc} + * @inheritdoc */ public function setProductLinks($sku, array $items) { + + if (empty($items)) { + throw InputException::invalidFieldValue('items', 'empty array'); + } + $linkTypes = $this->linkTypeProvider->getLinkTypes(); // Check if product link type is set and correct - if (!empty($items)) { - foreach ($items as $newLink) { - $type = $newLink->getLinkType(); - if ($type == null) { - throw InputException::requiredField("linkType"); - } - if (!isset($linkTypes[$type])) { - throw new NoSuchEntityException( - __('The "%1" link type wasn\'t found. Verify the type and try again.', $type) - ); - } + foreach ($items as $newLink) { + $type = $newLink->getLinkType(); + if ($type == null) { + throw InputException::requiredField("linkType"); + } + if (!isset($linkTypes[$type])) { + throw new NoSuchEntityException( + __('The "%1" link type wasn\'t found. Verify the type and try again.', $type) + ); } } $product = $this->productRepository->get($sku); - // Replace only links of the specified type $existingLinks = $product->getProductLinks(); - $newLinks = []; - if (!empty($existingLinks)) { - foreach ($existingLinks as $link) { - if ($link->getLinkType() != $type) { - $newLinks[] = $link; - } - } - $newLinks = array_merge($newLinks, $items); - } else { - $newLinks = $items; - } + $newLinks = array_merge($existingLinks, $items); + $product->setProductLinks($newLinks); try { $this->productRepository->save($product); } catch (\Exception $exception) { - throw new CouldNotSaveException(__('The linked products data is invalid. Verify the data and try again.')); + throw new CouldNotSaveException( + __('The linked products data is invalid. Verify the data and try again.') + ); } return true; diff --git a/app/code/Magento/Catalog/Model/ProductRepository/MediaGalleryProcessor.php b/app/code/Magento/Catalog/Model/ProductRepository/MediaGalleryProcessor.php index 2aa92b8f0316e..ecb7322ac10d2 100644 --- a/app/code/Magento/Catalog/Model/ProductRepository/MediaGalleryProcessor.php +++ b/app/code/Magento/Catalog/Model/ProductRepository/MediaGalleryProcessor.php @@ -224,7 +224,15 @@ private function processEntries(ProductInterface $product, array $newEntries, ar $this->processNewMediaGalleryEntry($product, $newEntry); $finalGallery = $product->getData('media_gallery'); - $newEntryId = key(array_diff_key($product->getData('media_gallery')['images'], $entriesById)); + + $entryIds = array_keys( + array_diff_key( + $product->getData('media_gallery')['images'], + $entriesById + ) + ); + $newEntryId = array_pop($entryIds); + $newEntry = array_replace_recursive($newEntry, $finalGallery['images'][$newEntryId]); $entriesById[$newEntryId] = $newEntry; $finalGallery['images'][$newEntryId] = $newEntry; diff --git a/app/code/Magento/Catalog/Model/ResourceModel/Category/Collection.php b/app/code/Magento/Catalog/Model/ResourceModel/Category/Collection.php index 657daca13055e..4711828d8f78d 100644 --- a/app/code/Magento/Catalog/Model/ResourceModel/Category/Collection.php +++ b/app/code/Magento/Catalog/Model/ResourceModel/Category/Collection.php @@ -5,8 +5,11 @@ */ namespace Magento\Catalog\Model\ResourceModel\Category; +use Magento\Catalog\Model\Category; +use Magento\Catalog\Model\Product\Visibility; use Magento\CatalogUrlRewrite\Model\CategoryUrlRewriteGenerator; use Magento\Framework\App\Config\ScopeConfigInterface; +use Magento\Framework\DB\Select; use Magento\Store\Model\ScopeInterface; /** @@ -68,6 +71,11 @@ class Collection extends \Magento\Catalog\Model\ResourceModel\Collection\Abstrac */ private $scopeConfig; + /** + * @var Visibility + */ + private $catalogProductVisibility; + /** * Constructor * @param \Magento\Framework\Data\Collection\EntityFactory $entityFactory @@ -82,6 +90,7 @@ class Collection extends \Magento\Catalog\Model\ResourceModel\Collection\Abstrac * @param \Magento\Store\Model\StoreManagerInterface $storeManager * @param \Magento\Framework\DB\Adapter\AdapterInterface $connection * @param \Magento\Framework\App\Config\ScopeConfigInterface $scopeConfig + * @param Visibility|null $catalogProductVisibility * @SuppressWarnings(PHPMD.ExcessiveParameterList) */ public function __construct( @@ -96,7 +105,8 @@ public function __construct( \Magento\Framework\Validator\UniversalFactory $universalFactory, \Magento\Store\Model\StoreManagerInterface $storeManager, \Magento\Framework\DB\Adapter\AdapterInterface $connection = null, - \Magento\Framework\App\Config\ScopeConfigInterface $scopeConfig = null + \Magento\Framework\App\Config\ScopeConfigInterface $scopeConfig = null, + Visibility $catalogProductVisibility = null ) { parent::__construct( $entityFactory, @@ -113,6 +123,8 @@ public function __construct( ); $this->scopeConfig = $scopeConfig ?: \Magento\Framework\App\ObjectManager::getInstance()->get(ScopeConfigInterface::class); + $this->catalogProductVisibility = $catalogProductVisibility ?: + \Magento\Framework\App\ObjectManager::getInstance()->get(Visibility::class); } /** @@ -122,7 +134,7 @@ public function __construct( */ protected function _construct() { - $this->_init(\Magento\Catalog\Model\Category::class, \Magento\Catalog\Model\ResourceModel\Category::class); + $this->_init(Category::class, \Magento\Catalog\Model\ResourceModel\Category::class); } /** @@ -259,6 +271,7 @@ protected function _loadProductCount() * @return $this * @SuppressWarnings(PHPMD.CyclomaticComplexity) * @SuppressWarnings(PHPMD.UnusedLocalVariable) + * @throws \Magento\Framework\Exception\NoSuchEntityException */ public function loadProductCount($items, $countRegular = true, $countAnchor = true) { @@ -310,34 +323,14 @@ public function loadProductCount($items, $countRegular = true, $countAnchor = tr if ($countAnchor) { // Retrieve Anchor categories product counts + $categoryIds = array_keys($anchor); + $countSelect = $this->getProductsCountQuery($categoryIds, (bool)$websiteId); + $categoryProductsCount = $this->_conn->fetchPairs($countSelect); foreach ($anchor as $item) { - if ($allChildren = $item->getAllChildren()) { - $bind = ['entity_id' => $item->getId(), 'c_path' => $item->getPath() . '/%']; - $select = $this->_conn->select(); - $select->from( - ['main_table' => $this->getProductTable()], - new \Zend_Db_Expr('COUNT(DISTINCT main_table.product_id)') - )->joinInner( - ['e' => $this->getTable('catalog_category_entity')], - 'main_table.category_id=e.entity_id', - [] - )->where( - '(e.entity_id = :entity_id OR e.path LIKE :c_path)' - ); - if ($websiteId) { - $select->join( - ['w' => $this->getProductWebsiteTable()], - 'main_table.product_id = w.product_id', - [] - )->where( - 'w.website_id = ?', - $websiteId - ); - } - $item->setProductCount((int)$this->_conn->fetchOne($select, $bind)); - } else { - $item->setProductCount(0); - } + $productsCount = isset($categoriesProductsCount[$item->getId()]) + ? (int)$categoryProductsCount[$item->getId()] + : $this->getProductsCountFromCategoryTable($item, $websiteId); + $item->setProductCount($productsCount); } } return $this; @@ -513,4 +506,69 @@ public function getProductTable() } return $this->_productTable; } + + /** + * Get products count using catalog_category_entity table + * + * @param Category $item + * @param string $websiteId + * @return int + */ + private function getProductsCountFromCategoryTable(Category $item, string $websiteId): int + { + $productCount = 0; + + if ($item->getAllChildren()) { + $bind = ['entity_id' => $item->getId(), 'c_path' => $item->getPath() . '/%']; + $select = $this->_conn->select(); + $select->from( + ['main_table' => $this->getProductTable()], + new \Zend_Db_Expr('COUNT(DISTINCT main_table.product_id)') + )->joinInner( + ['e' => $this->getTable('catalog_category_entity')], + 'main_table.category_id=e.entity_id', + [] + )->where( + '(e.entity_id = :entity_id OR e.path LIKE :c_path)' + ); + if ($websiteId) { + $select->join( + ['w' => $this->getProductWebsiteTable()], + 'main_table.product_id = w.product_id', + [] + )->where( + 'w.website_id = ?', + $websiteId + ); + } + $productCount = (int)$this->_conn->fetchOne($select, $bind); + } + return $productCount; + } + + /** + * Get query for retrieve count of products per category + * + * @param array $categoryIds + * @param bool $addVisibilityFilter + * @return Select + */ + private function getProductsCountQuery(array $categoryIds, $addVisibilityFilter = true): Select + { + $categoryTable = $this->getTable('catalog_category_product_index'); + $select = $this->_conn->select() + ->from( + ['cat_index' => $categoryTable], + ['category_id' => 'cat_index.category_id', 'count' => 'count(cat_index.product_id)'] + ) + ->where('cat_index.category_id in (?)', \array_map('\intval', $categoryIds)); + if (true === $addVisibilityFilter) { + $select->where('cat_index.visibility in (?)', $this->catalogProductVisibility->getVisibleInSiteIds()); + } + if (count($categoryIds) > 1) { + $select->group('cat_index.category_id'); + } + + return $select; + } } diff --git a/app/code/Magento/Catalog/Model/ResourceModel/Eav/Attribute.php b/app/code/Magento/Catalog/Model/ResourceModel/Eav/Attribute.php index a396ae57c2ccd..e1c90017327cd 100644 --- a/app/code/Magento/Catalog/Model/ResourceModel/Eav/Attribute.php +++ b/app/code/Magento/Catalog/Model/ResourceModel/Eav/Attribute.php @@ -38,6 +38,18 @@ class Attribute extends \Magento\Eav\Model\Entity\Attribute implements const KEY_IS_GLOBAL = 'is_global'; + private const ALLOWED_INPUT_TYPES = [ + 'boolean' => true, + 'date' => true, + 'datetime' => true, + 'multiselect' => true, + 'price' => true, + 'select' => true, + 'text' => true, + 'textarea' => true, + 'weight' => true, + ]; + /** * @var LockValidatorInterface */ @@ -383,7 +395,7 @@ public function getApplyTo() /** * Retrieve source model * - * @return \Magento\Eav\Model\Entity\Attribute\Source\AbstractSource + * @return \Magento\Eav\Model\Entity\Attribute\Source\AbstractSource|string|null */ public function getSourceModel() { @@ -403,18 +415,7 @@ public function getSourceModel() */ public function isAllowedForRuleCondition() { - $allowedInputTypes = [ - 'boolean', - 'date', - 'datetime', - 'multiselect', - 'price', - 'select', - 'text', - 'textarea', - 'weight', - ]; - return $this->getIsVisible() && in_array($this->getFrontendInput(), $allowedInputTypes); + return $this->getIsVisible() && isset(self::ALLOWED_INPUT_TYPES[$this->getFrontendInput()]); } /** diff --git a/app/code/Magento/Catalog/Model/ResourceModel/Product/Action.php b/app/code/Magento/Catalog/Model/ResourceModel/Product/Action.php index ca20f57c5d00e..71ab9413a0d09 100644 --- a/app/code/Magento/Catalog/Model/ResourceModel/Product/Action.php +++ b/app/code/Magento/Catalog/Model/ResourceModel/Product/Action.php @@ -3,17 +3,83 @@ * Copyright © Magento, Inc. All rights reserved. * See COPYING.txt for license details. */ +declare(strict_types=1); + namespace Magento\Catalog\Model\ResourceModel\Product; +use Magento\Catalog\Api\Data\ProductInterface; +use Magento\Catalog\Api\Data\ProductAttributeInterface; +use Magento\Catalog\Model\AbstractModel; +use Magento\Catalog\Model\Factory; +use Magento\Catalog\Model\Product; +use Magento\Catalog\Model\Product\Type; +use Magento\Catalog\Model\Product\TypeTransitionManager; +use Magento\Catalog\Model\ResourceModel\AbstractResource; +use Magento\Catalog\Model\ResourceModel\Product\CollectionFactory as ProductCollectionFactory; use Magento\Eav\Model\Entity\Attribute\AbstractAttribute; +use Magento\Eav\Model\Entity\Attribute\UniqueValidationInterface; +use Magento\Eav\Model\Entity\Context; +use Magento\Framework\DataObject; +use Magento\Framework\Stdlib\DateTime\DateTime; +use Magento\Store\Model\StoreManagerInterface; /** * Catalog Product Mass processing resource model * * @author Magento Core Team <core@magentocommerce.com> + * @SuppressWarnings(PHPMD.CouplingBetweenObjects) */ -class Action extends \Magento\Catalog\Model\ResourceModel\AbstractResource +class Action extends AbstractResource { + /** + * @var DateTime + */ + private $dateTime; + + /** + * @var ProductCollectionFactory + */ + private $productCollectionFactory; + + /** + * @var TypeTransitionManager + */ + private $typeTransitionManager; + + /** + * Entity type id values to save + * + * @var array + */ + private $typeIdValuesToSave = []; + + /** + * @param Context $context + * @param StoreManagerInterface $storeManager + * @param Factory $modelFactory + * @param UniqueValidationInterface $uniqueValidator + * @param DateTime $dateTime + * @param CollectionFactory $productCollectionFactory + * @param TypeTransitionManager $typeTransitionManager + * @param array $data + */ + public function __construct( + Context $context, + StoreManagerInterface $storeManager, + Factory $modelFactory, + UniqueValidationInterface $uniqueValidator, + DateTime $dateTime, + ProductCollectionFactory $productCollectionFactory, + TypeTransitionManager $typeTransitionManager, + $data = [] + ) { + parent::__construct($context, $storeManager, $modelFactory, $data, $uniqueValidator); + + $this->dateTime = $dateTime; + $this->productCollectionFactory = $productCollectionFactory; + $this->typeTransitionManager = $typeTransitionManager; + } + /** * Initialize connection * @@ -23,7 +89,7 @@ protected function _construct() { $resource = $this->_resource; $this->setType( - \Magento\Catalog\Model\Product::ENTITY + Product::ENTITY )->setConnection( $resource->getConnection('catalog') ); @@ -40,12 +106,18 @@ protected function _construct() */ public function updateAttributes($entityIds, $attrData, $storeId) { - $object = new \Magento\Framework\DataObject(); + $object = new DataObject(); $object->setStoreId($storeId); + $attrData[ProductInterface::UPDATED_AT] = $this->dateTime->gmtDate(); $this->getConnection()->beginTransaction(); try { foreach ($attrData as $attrCode => $value) { + if ($attrCode === ProductAttributeInterface::CODE_HAS_WEIGHT) { + $this->updateHasWeightAttribute($entityIds, $value); + continue; + } + $attribute = $this->getAttribute($attrCode); if (!$attribute->getAttributeId()) { continue; @@ -77,7 +149,7 @@ public function updateAttributes($entityIds, $attrData, $storeId) /** * Insert or Update attribute data * - * @param \Magento\Catalog\Model\AbstractModel $object + * @param AbstractModel $object * @param AbstractAttribute $attribute * @param mixed $value * @return $this @@ -95,7 +167,7 @@ protected function _saveAttributeValue($object, $attribute, $value) * for default store id * In this case we clear all not default values */ - if ($this->_storeManager->hasSingleStore()) { + if ($this->_storeManager->hasSingleStore() && !$attribute->isStatic()) { $storeId = $this->getDefaultStoreId(); $connection->delete( $table, @@ -107,17 +179,24 @@ protected function _saveAttributeValue($object, $attribute, $value) ); } - $data = new \Magento\Framework\DataObject( - [ - 'attribute_id' => $attribute->getAttributeId(), - 'store_id' => $storeId, - $this->getLinkField() => $entityId, - 'value' => $this->_prepareValueForSave($value, $attribute), - ] - ); + $data = $attribute->isStatic() + ? new DataObject( + [ + $this->getLinkField() => $entityId, + $attribute->getAttributeCode() => $this->_prepareValueForSave($value, $attribute), + ] + ) + : new DataObject( + [ + 'attribute_id' => $attribute->getAttributeId(), + 'store_id' => $storeId, + $this->getLinkField() => $entityId, + 'value' => $this->_prepareValueForSave($value, $attribute), + ] + ); $bind = $this->_prepareDataForTable($data, $table); - if ($attribute->isScopeStore()) { + if ($attribute->isScopeStore() || $attribute->isStatic()) { /** * Update attribute value for store */ @@ -143,7 +222,10 @@ protected function _saveAttributeValue($object, $attribute, $value) } /** + * Resolve entity id for current entity + * * @param int $entityId + * * @return int */ protected function resolveEntityId($entityId) @@ -157,4 +239,87 @@ protected function resolveEntityId($entityId) ->where('entity_id = ?', $entityId); return $this->getConnection()->fetchOne($select); } + + /** + * Process product_has_weight attribute update + * + * @param array $entityIds + * @param string $value + */ + private function updateHasWeightAttribute($entityIds, $value): void + { + $productCollection = $this->productCollectionFactory->create(); + $productCollection->addIdFilter($entityIds); + // Type can be changed depending on weight only between simple and virtual products + $productCollection->addFieldToFilter( + Product::TYPE_ID, + [ + 'in' => [ + Type::TYPE_SIMPLE, + Type::TYPE_VIRTUAL + ] + ] + ); + $productCollection->addFieldToSelect(Product::TYPE_ID); + $i = 0; + + foreach ($productCollection->getItems() as $product) { + $product->setData(ProductAttributeInterface::CODE_HAS_WEIGHT, $value); + $oldTypeId = $product->getTypeId(); + $this->typeTransitionManager->processProduct($product); + + if ($oldTypeId !== $product->getTypeId()) { + $i++; + $this->saveTypeIdValue($product); + + // save collected data every 1000 rows + if ($i % 1000 === 0) { + $this->processTypeIdValues(); + } + } + } + + $this->processTypeIdValues(); + } + + /** + * Save type id value to be updated + * + * @param Product $product + * @return $this + */ + private function saveTypeIdValue($product): self + { + $typeId = $product->getTypeId(); + + if (!array_key_exists($typeId, $this->typeIdValuesToSave)) { + $this->typeIdValuesToSave[$typeId] = []; + } + + $this->typeIdValuesToSave[$typeId][] = $product->getId(); + + return $this; + } + + /** + * Update type id values + * + * @return $this + */ + private function processTypeIdValues(): self + { + $connection = $this->getConnection(); + $table = $this->getTable('catalog_product_entity'); + + foreach ($this->typeIdValuesToSave as $typeId => $entityIds) { + $connection->update( + $table, + ['type_id' => $typeId], + ['entity_id IN (?)' => $entityIds] + ); + } + $this->typeIdValuesToSave = []; + + return $this; + } } diff --git a/app/code/Magento/Catalog/Model/ResourceModel/Product/Collection.php b/app/code/Magento/Catalog/Model/ResourceModel/Product/Collection.php index e31180d4ff6cf..6e1efbc9db003 100644 --- a/app/code/Magento/Catalog/Model/ResourceModel/Product/Collection.php +++ b/app/code/Magento/Catalog/Model/ResourceModel/Product/Collection.php @@ -1180,9 +1180,33 @@ protected function _getSelectCountSql(?Select $select = null, $resetLeftJoins = if ($resetLeftJoins) { $countSelect->resetJoinLeft(); } + + $this->removeEntityIdentifierFromGroupBy($countSelect); + return $countSelect; } + /** + * Using `entity_id` for `GROUP BY` causes COUNT() return {n} rows of value = 1 instead of 1 row of value {n} + * + * @param Select $select + * @throws \Zend_Db_Select_Exception + */ + private function removeEntityIdentifierFromGroupBy(Select $select): void + { + $originalGroupBy = $select->getPart(Select::GROUP); + + if (!is_array($originalGroupBy)) { + return; + } + + $groupBy = array_filter($originalGroupBy, function ($field) { + return false === strpos($field, $this->getIdFieldName()); + }); + + $select->setPart(Select::GROUP, $groupBy); + } + /** * Prepare statistics data * @@ -1717,7 +1741,10 @@ public function addAttributeToSort($attribute, $dir = self::SORT_ORDER_ASC) // optimize if using cat index $filters = $this->_productLimitationFilters; if (isset($filters['category_id']) || isset($filters['visibility'])) { - $this->getSelect()->order('cat_index.position ' . $dir); + $this->getSelect()->order([ + 'cat_index.position ' . $dir, + 'e.entity_id ' . \Magento\Framework\DB\Select::SQL_DESC + ]); } else { $this->getSelect()->order('e.entity_id ' . $dir); } @@ -1732,7 +1759,9 @@ public function addAttributeToSort($attribute, $dir = self::SORT_ORDER_ASC) if ($attribute == 'price' && $storeId != 0) { $this->addPriceData(); if ($this->_productLimitationFilters->isUsingPriceIndex()) { - $this->getSelect()->order("price_index.min_price {$dir}"); + $this->getSelect()->order( + new \Zend_Db_Expr("price_index.min_price = 0, price_index.min_price {$dir}") + ); return $this; } } @@ -1765,30 +1794,19 @@ public function addAttributeToSort($attribute, $dir = self::SORT_ORDER_ASC) */ protected function _prepareProductLimitationFilters() { - if (isset( - $this->_productLimitationFilters['visibility'] - ) && !isset( - $this->_productLimitationFilters['store_id'] - ) - ) { + if (isset($this->_productLimitationFilters['visibility']) + && !isset($this->_productLimitationFilters['store_id'])) { $this->_productLimitationFilters['store_id'] = $this->getStoreId(); } - if (isset( - $this->_productLimitationFilters['category_id'] - ) && !isset( - $this->_productLimitationFilters['store_id'] - ) - ) { + + if (isset($this->_productLimitationFilters['category_id']) + && !isset($this->_productLimitationFilters['store_id'])) { $this->_productLimitationFilters['store_id'] = $this->getStoreId(); } - if (isset( - $this->_productLimitationFilters['store_id'] - ) && isset( - $this->_productLimitationFilters['visibility'] - ) && !isset( - $this->_productLimitationFilters['category_id'] - ) - ) { + + if (isset($this->_productLimitationFilters['store_id']) + && isset($this->_productLimitationFilters['visibility']) + && !isset($this->_productLimitationFilters['category_id'])) { $this->_productLimitationFilters['category_id'] = $this->_storeManager->getStore( $this->_productLimitationFilters['store_id'] )->getRootCategoryId(); @@ -1819,14 +1837,8 @@ protected function _productLimitationJoinWebsite() $filters['website_ids'], 'int' ); - } elseif (isset( - $filters['store_id'] - ) && (!isset( - $filters['visibility'] - ) && !isset( - $filters['category_id'] - )) && !$this->isEnabledFlat() - ) { + } elseif (isset($filters['store_id']) && !$this->isEnabledFlat() + && (!isset($filters['visibility']) && !isset($filters['category_id']))) { $joinWebsite = true; $websiteId = $this->_storeManager->getStore($filters['store_id'])->getWebsiteId(); $conditions[] = $this->getConnection()->quoteInto('product_website.website_id = ?', $websiteId, 'int'); @@ -1901,9 +1913,10 @@ protected function _productLimitationJoinPrice() /** * Join Product Price Table with left-join possibility * - * @see \Magento\Catalog\Model\ResourceModel\Product\Collection::_productLimitationJoinPrice() * @param bool $joinLeft * @return $this + * @see \Magento\Catalog\Model\ResourceModel\Product\Collection::_productLimitationJoinPrice() + * @SuppressWarnings(PHPMD.CyclomaticComplexity) */ protected function _productLimitationPrice($joinLeft = false) { @@ -1922,14 +1935,14 @@ protected function _productLimitationPrice($joinLeft = false) $connection = $this->getConnection(); $select = $this->getSelect(); - $joinCond = join( - ' AND ', - [ - 'price_index.entity_id = e.entity_id', - $connection->quoteInto('price_index.website_id = ?', $filters['website_id']), - $connection->quoteInto('price_index.customer_group_id = ?', $filters['customer_group_id']) - ] - ); + $joinCondArray = []; + $joinCondArray[] = 'price_index.entity_id = e.entity_id'; + $joinCondArray[] = $connection->quoteInto('price_index.customer_group_id = ?', $filters['customer_group_id']); + // Add website condition only if it's different from admin scope + if (((int) $filters['website_id']) !== Store::DEFAULT_STORE_ID) { + $joinCondArray[] = $connection->quoteInto('price_index.website_id = ?', $filters['website_id']); + } + $joinCond = join(' AND ', $joinCondArray); $fromPart = $select->getPart(\Magento\Framework\DB\Select::FROM); if (!isset($fromPart['price_index'])) { @@ -2121,7 +2134,9 @@ private function getChildrenCategories(int $categoryId): array if (in_array($category['parent_id'], $categoryIds) && in_array($category['parent_id'], $anchorCategory)) { $categoryIds[] = (int)$category[$linkField]; - if ($category['is_anchor'] == 1) { + // Storefront approach is to treat non-anchor children of anchor category as anchors. + // Adding their's IDs to $anchorCategory for consistency. + if ($category['is_anchor'] == 1 || in_array($category['parent_id'], $anchorCategory)) { $anchorCategory[] = (int)$category[$linkField]; } } diff --git a/app/code/Magento/Catalog/Model/ResourceModel/Product/Indexer/LinkedProductSelectBuilderByIndexPrice.php b/app/code/Magento/Catalog/Model/ResourceModel/Product/Indexer/LinkedProductSelectBuilderByIndexPrice.php index ebe04fb63b217..f477c11896688 100644 --- a/app/code/Magento/Catalog/Model/ResourceModel/Product/Indexer/LinkedProductSelectBuilderByIndexPrice.php +++ b/app/code/Magento/Catalog/Model/ResourceModel/Product/Indexer/LinkedProductSelectBuilderByIndexPrice.php @@ -15,6 +15,13 @@ use Magento\Store\Model\Indexer\WebsiteDimensionProvider; use Magento\Framework\Search\Request\IndexScopeResolverInterface; +/** + * Class LinkedProductSelectBuilderByIndexPrice + * + * Provide Select object for retrieve product id by index price. + * + * @SuppressWarnings(PHPMD.CookieAndSessionMisuse) + */ class LinkedProductSelectBuilderByIndexPrice implements LinkedProductSelectBuilderInterface { /** @@ -83,13 +90,13 @@ public function __construct( } /** - * {@inheritdoc} + * @inheritdoc */ - public function build($productId) + public function build(int $productId, int $storeId) : array { $linkField = $this->metadataPool->getMetadata(ProductInterface::class)->getLinkField(); $productTable = $this->resource->getTableName('catalog_product_entity'); - $websiteId = $this->storeManager->getStore()->getWebsiteId(); + $websiteId = $this->storeManager->getStore($storeId)->getWebsiteId(); $customerGroupId = $this->customerSession->getCustomerGroupId(); $priceSelect = $this->resource->getConnection()->select() diff --git a/app/code/Magento/Catalog/Model/ResourceModel/Product/LinkedProductSelectBuilderByBasePrice.php b/app/code/Magento/Catalog/Model/ResourceModel/Product/LinkedProductSelectBuilderByBasePrice.php index 841fe17bdcf05..77e886f12723c 100644 --- a/app/code/Magento/Catalog/Model/ResourceModel/Product/LinkedProductSelectBuilderByBasePrice.php +++ b/app/code/Magento/Catalog/Model/ResourceModel/Product/LinkedProductSelectBuilderByBasePrice.php @@ -74,7 +74,7 @@ public function __construct( /** * @inheritdoc */ - public function build($productId) + public function build(int $productId, int $storeId) : array { $linkField = $this->metadataPool->getMetadata(ProductInterface::class)->getLinkField(); $priceAttribute = $this->eavConfig->getAttribute(Product::ENTITY, 'price'); @@ -104,7 +104,7 @@ public function build($productId) if (!$this->catalogHelper->isPriceGlobal()) { $priceSelectStore = clone $priceSelect; - $priceSelectStore->where('t.store_id = ?', $this->storeManager->getStore()->getId()); + $priceSelectStore->where('t.store_id = ?', $storeId); $selects[] = $priceSelectStore; } diff --git a/app/code/Magento/Catalog/Model/ResourceModel/Product/LinkedProductSelectBuilderBySpecialPrice.php b/app/code/Magento/Catalog/Model/ResourceModel/Product/LinkedProductSelectBuilderBySpecialPrice.php index 5c47185a85bf4..ef0eeca10502a 100644 --- a/app/code/Magento/Catalog/Model/ResourceModel/Product/LinkedProductSelectBuilderBySpecialPrice.php +++ b/app/code/Magento/Catalog/Model/ResourceModel/Product/LinkedProductSelectBuilderBySpecialPrice.php @@ -12,6 +12,10 @@ use Magento\Store\Model\Store; /** + * LinkedProductSelectBuilderBySpecialPrice + * + * Provide Select object for retrieve product id by special price + * * @SuppressWarnings(PHPMD.CouplingBetweenObjects) */ class LinkedProductSelectBuilderBySpecialPrice implements LinkedProductSelectBuilderInterface @@ -88,16 +92,16 @@ public function __construct( } /** - * {@inheritdoc} + * @inheritdoc */ - public function build($productId) + public function build(int $productId, int $storeId) : array { $linkField = $this->metadataPool->getMetadata(ProductInterface::class)->getLinkField(); $connection = $this->resource->getConnection(); $specialPriceAttribute = $this->eavConfig->getAttribute(Product::ENTITY, 'special_price'); $specialPriceFromDate = $this->eavConfig->getAttribute(Product::ENTITY, 'special_from_date'); $specialPriceToDate = $this->eavConfig->getAttribute(Product::ENTITY, 'special_to_date'); - $timestamp = $this->localeDate->scopeTimeStamp($this->storeManager->getStore()); + $timestamp = $this->localeDate->scopeTimeStamp($this->storeManager->getStore($storeId)); $currentDate = $this->dateTime->formatDate($timestamp, false); $productTable = $this->resource->getTableName('catalog_product_entity'); @@ -145,7 +149,7 @@ public function build($productId) if (!$this->catalogHelper->isPriceGlobal()) { $priceSelectStore = clone $specialPrice; - $priceSelectStore->where('t.store_id = ?', $this->storeManager->getStore()->getId()); + $priceSelectStore->where('t.store_id = ?', $storeId); $selects[] = $priceSelectStore; } diff --git a/app/code/Magento/Catalog/Model/ResourceModel/Product/LinkedProductSelectBuilderByTierPrice.php b/app/code/Magento/Catalog/Model/ResourceModel/Product/LinkedProductSelectBuilderByTierPrice.php index 37281193d6a1b..f104f92ea86c5 100644 --- a/app/code/Magento/Catalog/Model/ResourceModel/Product/LinkedProductSelectBuilderByTierPrice.php +++ b/app/code/Magento/Catalog/Model/ResourceModel/Product/LinkedProductSelectBuilderByTierPrice.php @@ -9,10 +9,19 @@ use Magento\Framework\App\ObjectManager; use Magento\Framework\DB\Select; +/** + * LinkedProductSelectBuilderByTierPrice + * + * Provide Select object for retrieve product id by tier price + * + * @SuppressWarnings(PHPMD.CookieAndSessionMisuse) + */ class LinkedProductSelectBuilderByTierPrice implements LinkedProductSelectBuilderInterface { /** * Default website id + * + * Constant represents default website id */ const DEFAULT_WEBSITE_ID = 0; @@ -72,9 +81,9 @@ public function __construct( } /** - * {@inheritdoc} + * @inheritdoc */ - public function build($productId) + public function build(int $productId, int $storeId) : array { $linkField = $this->metadataPool->getMetadata(ProductInterface::class)->getLinkField(); $productTable = $this->resource->getTableName('catalog_product_entity'); @@ -103,7 +112,7 @@ public function build($productId) if (!$this->catalogHelper->isPriceGlobal()) { $priceSelectStore = clone $priceSelect; - $priceSelectStore->where('t.website_id = ?', $this->storeManager->getStore()->getWebsiteId()); + $priceSelectStore->where('t.website_id = ?', $this->storeManager->getStore($storeId)->getWebsiteId()); $selects[] = $priceSelectStore; } diff --git a/app/code/Magento/Catalog/Model/ResourceModel/Product/LinkedProductSelectBuilderComposite.php b/app/code/Magento/Catalog/Model/ResourceModel/Product/LinkedProductSelectBuilderComposite.php index 5782834c06d85..17ca389777c5b 100644 --- a/app/code/Magento/Catalog/Model/ResourceModel/Product/LinkedProductSelectBuilderComposite.php +++ b/app/code/Magento/Catalog/Model/ResourceModel/Product/LinkedProductSelectBuilderComposite.php @@ -6,6 +6,9 @@ namespace Magento\Catalog\Model\ResourceModel\Product; +/** + * Collect Select object for list of products + */ class LinkedProductSelectBuilderComposite implements LinkedProductSelectBuilderInterface { /** @@ -22,14 +25,15 @@ public function __construct($linkedProductSelectBuilder) } /** - * {@inheritdoc} + * @inheritdoc */ - public function build($productId) + public function build(int $productId, int $storeId) : array { $selects = []; foreach ($this->linkedProductSelectBuilder as $productSelectBuilder) { - $selects = array_merge($selects, $productSelectBuilder->build($productId)); + $selects[] = $productSelectBuilder->build($productId, $storeId); } + $selects = array_merge(...$selects); return $selects; } diff --git a/app/code/Magento/Catalog/Model/ResourceModel/Product/LinkedProductSelectBuilderInterface.php b/app/code/Magento/Catalog/Model/ResourceModel/Product/LinkedProductSelectBuilderInterface.php index 3b870ed61f36d..3411c124bf5ce 100644 --- a/app/code/Magento/Catalog/Model/ResourceModel/Product/LinkedProductSelectBuilderInterface.php +++ b/app/code/Magento/Catalog/Model/ResourceModel/Product/LinkedProductSelectBuilderInterface.php @@ -11,8 +11,11 @@ interface LinkedProductSelectBuilderInterface { /** + * Build Select objects + * * @param int $productId + * @param int $storeId * @return \Magento\Framework\DB\Select[] */ - public function build($productId); + public function build(int $productId, int $storeId) : array; } diff --git a/app/code/Magento/Catalog/Model/ResourceModel/Product/Website/Link.php b/app/code/Magento/Catalog/Model/ResourceModel/Product/Website/Link.php index de4ffc5d862f9..d9016d8a852e8 100644 --- a/app/code/Magento/Catalog/Model/ResourceModel/Product/Website/Link.php +++ b/app/code/Magento/Catalog/Model/ResourceModel/Product/Website/Link.php @@ -7,8 +7,10 @@ use Magento\Catalog\Api\Data\ProductInterface; use Magento\Framework\App\ResourceConnection; -use Magento\Framework\EntityManager\MetadataPool; +/** + * Class Link used for assign website to the product + */ class Link { /** @@ -28,6 +30,7 @@ public function __construct( /** * Retrieve associated with product websites ids + * * @param int $productId * @return array */ @@ -48,29 +51,53 @@ public function getWebsiteIdsByProductId($productId) /** * Return true - if websites was changed, and false - if not + * * @param ProductInterface $product * @param array $websiteIds * @return bool */ public function saveWebsiteIds(ProductInterface $product, array $websiteIds) + { + $productId = (int) $product->getId(); + return $this->updateProductWebsite($productId, $websiteIds); + } + + /** + * Get Product website table + * + * @return string + */ + private function getProductWebsiteTable() + { + return $this->resourceConnection->getTableName('catalog_product_website'); + } + + /** + * Update product website table + * + * @param int $productId + * @param array $websiteIds + * @return bool + */ + public function updateProductWebsite(int $productId, array $websiteIds): bool { $connection = $this->resourceConnection->getConnection(); - $oldWebsiteIds = $this->getWebsiteIdsByProductId($product->getId()); + $oldWebsiteIds = $this->getWebsiteIdsByProductId($productId); $insert = array_diff($websiteIds, $oldWebsiteIds); $delete = array_diff($oldWebsiteIds, $websiteIds); if (!empty($insert)) { $data = []; foreach ($insert as $websiteId) { - $data[] = ['product_id' => (int) $product->getId(), 'website_id' => (int) $websiteId]; + $data[] = ['product_id' => $productId, 'website_id' => (int)$websiteId]; } $connection->insertMultiple($this->getProductWebsiteTable(), $data); } if (!empty($delete)) { foreach ($delete as $websiteId) { - $condition = ['product_id = ?' => (int) $product->getId(), 'website_id = ?' => (int) $websiteId]; + $condition = ['product_id = ?' => $productId, 'website_id = ?' => (int)$websiteId]; $connection->delete($this->getProductWebsiteTable(), $condition); } } @@ -81,12 +108,4 @@ public function saveWebsiteIds(ProductInterface $product, array $websiteIds) return false; } - - /** - * @return string - */ - private function getProductWebsiteTable() - { - return $this->resourceConnection->getTableName('catalog_product_website'); - } } diff --git a/app/code/Magento/Catalog/Model/ResourceModel/ProductWebsiteAssignmentHandler.php b/app/code/Magento/Catalog/Model/ResourceModel/ProductWebsiteAssignmentHandler.php new file mode 100644 index 0000000000000..10e0a09593208 --- /dev/null +++ b/app/code/Magento/Catalog/Model/ResourceModel/ProductWebsiteAssignmentHandler.php @@ -0,0 +1,56 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +declare(strict_types=1); +namespace Magento\Catalog\Model\ResourceModel; + +use Magento\Catalog\Model\ResourceModel\Product\Website\Link; +use Magento\Framework\EntityManager\Operation\AttributeInterface; + +/** + * Class purpose is to handle product websites assignment + */ +class ProductWebsiteAssignmentHandler implements AttributeInterface +{ + /** + * @var Link + */ + private $productLink; + + /** + * ProductWebsiteAssignmentHandler constructor + * + * @param Link $productLink + */ + public function __construct( + Link $productLink + ) { + $this->productLink = $productLink; + } + + /** + * Assign product website entity to the product repository + * + * @param string $entityType + * @param array $entityData + * @param array $arguments + * @return array + * @SuppressWarnings(PHPMD.UnusedFormalParameter) + * @throws \Exception + */ + public function execute($entityType, $entityData, $arguments = []): array + { + $websiteIds = array_key_exists('website_ids', $entityData) ? + array_filter($entityData['website_ids'], function ($websiteId) { + return $websiteId !== null; + }) : []; + $productId = array_key_exists('entity_id', $entityData) ? (int) $entityData['entity_id'] : null; + + if (!empty($productId) && !empty($websiteIds)) { + $this->productLink->updateProductWebsite($productId, $websiteIds); + } + return $entityData; + } +} diff --git a/app/code/Magento/Catalog/Plugin/Model/ResourceModel/Config.php b/app/code/Magento/Catalog/Plugin/Model/ResourceModel/Config.php index b942f5570f57d..a3e7a417e6e47 100644 --- a/app/code/Magento/Catalog/Plugin/Model/ResourceModel/Config.php +++ b/app/code/Magento/Catalog/Plugin/Model/ResourceModel/Config.php @@ -3,9 +3,14 @@ * Copyright © Magento, Inc. All rights reserved. * See COPYING.txt for license details. */ +declare(strict_types=1); + namespace Magento\Catalog\Plugin\Model\ResourceModel; -use Magento\Framework\App\ObjectManager; +use Magento\Eav\Model\Cache\Type; +use Magento\Eav\Model\Entity\Attribute; +use Magento\Framework\App\Cache\StateInterface; +use Magento\Framework\App\CacheInterface; use Magento\Framework\Serialize\SerializerInterface; /** @@ -21,12 +26,12 @@ class Config /**#@-*/ /**#@-*/ - protected $cache; + private $cache; /** - * @var bool|null + * @var bool */ - protected $isCacheEnabled = null; + private $isCacheEnabled; /** * @var SerializerInterface @@ -34,30 +39,30 @@ class Config private $serializer; /** - * @param \Magento\Framework\App\CacheInterface $cache - * @param \Magento\Framework\App\Cache\StateInterface $cacheState + * @param CacheInterface $cache + * @param StateInterface $cacheState * @param SerializerInterface $serializer */ public function __construct( - \Magento\Framework\App\CacheInterface $cache, - \Magento\Framework\App\Cache\StateInterface $cacheState, - SerializerInterface $serializer = null + CacheInterface $cache, + StateInterface $cacheState, + SerializerInterface $serializer ) { $this->cache = $cache; - $this->isCacheEnabled = $cacheState->isEnabled(\Magento\Eav\Model\Cache\Type::TYPE_IDENTIFIER); - $this->serializer = $serializer ?: ObjectManager::getInstance()->get(SerializerInterface::class); + $this->isCacheEnabled = $cacheState->isEnabled(Type::TYPE_IDENTIFIER); + $this->serializer = $serializer; } /** * Cache attribute used in listing. * * @param \Magento\Catalog\Model\ResourceModel\Config $config - * @param \Closure $proceed + * @param callable $proceed * @return array */ public function aroundGetAttributesUsedInListing( \Magento\Catalog\Model\ResourceModel\Config $config, - \Closure $proceed + callable $proceed ) { $cacheId = self::PRODUCT_LISTING_ATTRIBUTES_CACHE_ID . $config->getEntityTypeId() . '_' . $config->getStoreId(); if ($this->isCacheEnabled && ($attributes = $this->cache->load($cacheId))) { @@ -69,8 +74,8 @@ public function aroundGetAttributesUsedInListing( $this->serializer->serialize($attributes), $cacheId, [ - \Magento\Eav\Model\Cache\Type::CACHE_TAG, - \Magento\Eav\Model\Entity\Attribute::CACHE_TAG + Type::CACHE_TAG, + Attribute::CACHE_TAG ] ); } @@ -81,12 +86,12 @@ public function aroundGetAttributesUsedInListing( * Cache attributes used for sorting. * * @param \Magento\Catalog\Model\ResourceModel\Config $config - * @param \Closure $proceed + * @param callable $proceed * @return array */ public function aroundGetAttributesUsedForSortBy( \Magento\Catalog\Model\ResourceModel\Config $config, - \Closure $proceed + callable $proceed ) { $cacheId = self::PRODUCT_LISTING_SORT_BY_ATTRIBUTES_CACHE_ID . $config->getEntityTypeId() . '_' . $config->getStoreId(); @@ -99,8 +104,8 @@ public function aroundGetAttributesUsedForSortBy( $this->serializer->serialize($attributes), $cacheId, [ - \Magento\Eav\Model\Cache\Type::CACHE_TAG, - \Magento\Eav\Model\Entity\Attribute::CACHE_TAG + Type::CACHE_TAG, + Attribute::CACHE_TAG ] ); } diff --git a/app/code/Magento/Catalog/Pricing/Price/CalculateCustomOptionCatalogRule.php b/app/code/Magento/Catalog/Pricing/Price/CalculateCustomOptionCatalogRule.php new file mode 100644 index 0000000000000..b3f3ac7bf68ef --- /dev/null +++ b/app/code/Magento/Catalog/Pricing/Price/CalculateCustomOptionCatalogRule.php @@ -0,0 +1,119 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +declare(strict_types=1); + +namespace Magento\Catalog\Pricing\Price; + +use Magento\Catalog\Model\Product; +use Magento\Catalog\Model\Product\PriceModifierInterface; +use Magento\CatalogRule\Pricing\Price\CatalogRulePrice; +use Magento\Framework\Pricing\Price\BasePriceProviderInterface; +use Magento\Framework\Pricing\PriceCurrencyInterface; + +/** + * Calculates prices of custom options of the product with catalog rules applied. + */ +class CalculateCustomOptionCatalogRule +{ + /** + * @var PriceCurrencyInterface + */ + private $priceCurrency; + + /** + * @var PriceModifierInterface + */ + private $priceModifier; + + /** + * @param PriceCurrencyInterface $priceCurrency + * @param PriceModifierInterface $priceModifier + */ + public function __construct( + PriceCurrencyInterface $priceCurrency, + PriceModifierInterface $priceModifier + ) { + $this->priceModifier = $priceModifier; + $this->priceCurrency = $priceCurrency; + } + + /** + * Calculate prices of custom options of the product with catalog rules applied. + * + * @param Product $product + * @param float $optionPriceValue + * @param bool $isPercent + * @return float + */ + public function execute( + Product $product, + float $optionPriceValue, + bool $isPercent + ): float { + $regularPrice = (float)$product->getPriceInfo() + ->getPrice(RegularPrice::PRICE_CODE) + ->getValue(); + $catalogRulePrice = $this->priceModifier->modifyPrice( + $regularPrice, + $product + ); + $basePriceWithOutCatalogRules = (float)$this->getGetBasePriceWithOutCatalogRules($product); + // Apply catalog price rules to product options only if catalog price rules are applied to product. + if ($catalogRulePrice < $basePriceWithOutCatalogRules) { + $optionPrice = $this->getOptionPriceWithoutPriceRule($optionPriceValue, $isPercent, $regularPrice); + $totalCatalogRulePrice = $this->priceModifier->modifyPrice( + $regularPrice + $optionPrice, + $product + ); + $finalOptionPrice = $totalCatalogRulePrice - $catalogRulePrice; + } else { + $finalOptionPrice = $this->getOptionPriceWithoutPriceRule( + $optionPriceValue, + $isPercent, + $this->getGetBasePriceWithOutCatalogRules($product) + ); + } + + return $this->priceCurrency->convertAndRound($finalOptionPrice); + } + + /** + * Get product base price without catalog rules applied. + * + * @param Product $product + * @return float + */ + private function getGetBasePriceWithOutCatalogRules(Product $product): float + { + $basePrice = null; + foreach ($product->getPriceInfo()->getPrices() as $price) { + if ($price instanceof BasePriceProviderInterface + && $price->getPriceCode() !== CatalogRulePrice::PRICE_CODE + && $price->getValue() !== false + ) { + $basePrice = min( + $price->getValue(), + $basePrice ?? $price->getValue() + ); + } + } + + return $basePrice ?? $product->getPrice(); + } + + /** + * Calculate option price without catalog price rule discount. + * + * @param float $optionPriceValue + * @param bool $isPercent + * @param float $basePrice + * @return float + */ + private function getOptionPriceWithoutPriceRule(float $optionPriceValue, bool $isPercent, float $basePrice): float + { + return $isPercent ? $basePrice * $optionPriceValue / 100 : $optionPriceValue; + } +} diff --git a/app/code/Magento/Catalog/Setup/Patch/Data/UpdateDefaultAttributeValue.php b/app/code/Magento/Catalog/Setup/Patch/Data/UpdateDefaultAttributeValue.php index 1d58de1994a11..4500d86ad4a6c 100644 --- a/app/code/Magento/Catalog/Setup/Patch/Data/UpdateDefaultAttributeValue.php +++ b/app/code/Magento/Catalog/Setup/Patch/Data/UpdateDefaultAttributeValue.php @@ -3,19 +3,18 @@ * Copyright © Magento, Inc. All rights reserved. * See COPYING.txt for license details. */ +declare(strict_types=1); namespace Magento\Catalog\Setup\Patch\Data; use Magento\Catalog\Setup\CategorySetup; use Magento\Catalog\Setup\CategorySetupFactory; -use Magento\Framework\App\ResourceConnection; use Magento\Framework\Setup\ModuleDataSetupInterface; use Magento\Framework\Setup\Patch\DataPatchInterface; use Magento\Framework\Setup\Patch\PatchVersionInterface; /** - * Class UpdateDefaultAttributeValue - * @package Magento\Catalog\Setup\Patch + * Updates 'is_anchor' attribute default value. */ class UpdateDefaultAttributeValue implements DataPatchInterface, PatchVersionInterface { @@ -30,7 +29,6 @@ class UpdateDefaultAttributeValue implements DataPatchInterface, PatchVersionInt private $categorySetupFactory; /** - * PatchInitial constructor. * @param ModuleDataSetupInterface $moduleDataSetup * @param CategorySetupFactory $categorySetupFactory */ @@ -43,17 +41,24 @@ public function __construct( } /** - * {@inheritdoc} + * @inheritdoc */ public function apply() { /** @var CategorySetup $categorySetup */ $categorySetup = $this->categorySetupFactory->create(['setup' => $this->moduleDataSetup]); - $categorySetup->updateAttribute(3, 54, 'default_value', 1); + $categorySetup->updateAttribute( + CategorySetup::CATEGORY_ENTITY_TYPE_ID, + 'is_anchor', + 'default_value', + 1 + ); + + return $this; } /** - * {@inheritdoc} + * @inheritdoc */ public static function getDependencies() { @@ -63,7 +68,7 @@ public static function getDependencies() } /** - * {@inheritdoc} + * @inheritdoc */ public static function getVersion() { @@ -71,7 +76,7 @@ public static function getVersion() } /** - * {@inheritdoc} + * @inheritdoc */ public function getAliases() { diff --git a/app/code/Magento/Catalog/Test/Mftf/ActionGroup/AddSimpleProductToCartActionGroup.xml b/app/code/Magento/Catalog/Test/Mftf/ActionGroup/AddSimpleProductToCartActionGroup.xml index 81e3b8c99d9d2..b4f7571429151 100644 --- a/app/code/Magento/Catalog/Test/Mftf/ActionGroup/AddSimpleProductToCartActionGroup.xml +++ b/app/code/Magento/Catalog/Test/Mftf/ActionGroup/AddSimpleProductToCartActionGroup.xml @@ -10,7 +10,7 @@ xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/actionGroupSchema.xsd"> <actionGroup name="AddSimpleProductToCartActionGroup"> <annotations> - <description>Navigates to the Storefront Product page. Then adds the Product to the Cart. Validates that the Success Message is present and correct.</description> + <description>Only works on Storefront Product Page - Add Simple Product to Cart</description> </annotations> <arguments> <argument name="product" defaultValue="product"/> diff --git a/app/code/Magento/Catalog/Test/Mftf/ActionGroup/AdminAddProductToCategoryActionGroup.xml b/app/code/Magento/Catalog/Test/Mftf/ActionGroup/AdminAddProductToCategoryActionGroup.xml new file mode 100644 index 0000000000000..bedd55e183290 --- /dev/null +++ b/app/code/Magento/Catalog/Test/Mftf/ActionGroup/AdminAddProductToCategoryActionGroup.xml @@ -0,0 +1,25 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + /** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +--> + +<actionGroups xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/actionGroupSchema.xsd"> + <actionGroup name="AdminAddProductToCategoryActionGroup"> + <annotations> + <description>Add Product to Category</description> + </annotations> + <arguments> + <argument name="product"/> + </arguments> + + <scrollTo selector="{{AdminCategoryBasicFieldSection.productsInCategory}}" x="0" y="-80" stepKey="scrollToProductInCategory"/> + <click selector="{{AdminCategoryBasicFieldSection.productsInCategory}}" stepKey="clickOnProductInCategory"/> + <fillField selector="{{AdminCategoryContentSection.productTableColumnName}}" userInput="{{product.name}}" stepKey="selectProduct"/> + <click selector="{{AdminCategoryContentSection.productSearch}}" stepKey="clickSearchButton"/> + <click selector="{{AdminCategoryContentSection.productTableRow}}" stepKey="selectProductFromTableRow"/> + </actionGroup> +</actionGroups> diff --git a/app/code/Magento/Catalog/Test/Mftf/ActionGroup/AdminAssertParentChildCategoryTreeElementsActionGroup.xml b/app/code/Magento/Catalog/Test/Mftf/ActionGroup/AdminAssertParentChildCategoryTreeElementsActionGroup.xml new file mode 100644 index 0000000000000..b1ed08db05b9a --- /dev/null +++ b/app/code/Magento/Catalog/Test/Mftf/ActionGroup/AdminAssertParentChildCategoryTreeElementsActionGroup.xml @@ -0,0 +1,22 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + /** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +--> + +<actionGroups xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/actionGroupSchema.xsd"> + <actionGroup name="AdminAssertParentChildCategoryTreeElementsActionGroup"> + <annotations> + <description>Checks category tree, parent category has child category element.</description> + </annotations> + <arguments> + <argument name="parentCategoryName" type="string" defaultValue="parent"/> + <argument name="childCategoryName" type="string" defaultValue="child"/> + </arguments> + + <seeElement selector="{{AdminCategorySidebarTreeSection.childCategoryUnderParent(parentCategoryName, childCategoryName)}}" stepKey="seeSubcategoryIsUnderParent"/> + </actionGroup> +</actionGroups> diff --git a/app/code/Magento/Catalog/Test/Mftf/ActionGroup/AdminAssignImageBaseRoleActionGroup.xml b/app/code/Magento/Catalog/Test/Mftf/ActionGroup/AdminAssignImageBaseRoleActionGroup.xml new file mode 100644 index 0000000000000..aa5311d389d7a --- /dev/null +++ b/app/code/Magento/Catalog/Test/Mftf/ActionGroup/AdminAssignImageBaseRoleActionGroup.xml @@ -0,0 +1,25 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + /** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +--> + +<actionGroups xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/actionGroupSchema.xsd"> + <actionGroup name="AdminAssignImageBaseRoleActionGroup"> + <annotations> + <description>Requires the navigation to the Product Creation page. Checks the Base Role area for image.</description> + </annotations> + <arguments> + <argument name="image"/> + </arguments> + <conditionalClick selector="{{AdminProductImagesSection.productImagesToggle}}" dependentSelector="{{AdminProductImagesSection.imageFile(image.fileName)}}" visible="false" stepKey="expandImages"/> + <waitForElementVisible selector="{{AdminProductImagesSection.imageFile(image.fileName)}}" stepKey="seeProductImageName"/> + <click selector="{{AdminProductImagesSection.imageFile(image.fileName)}}" stepKey="clickProductImage"/> + <waitForElementVisible selector="{{AdminProductImagesSection.altText}}" stepKey="seeAltTextSection"/> + <checkOption selector="{{AdminProductImagesSection.roleBase}}" stepKey="checkRoles"/> + <click selector="{{AdminSlideOutDialogSection.closeButton}}" stepKey="clickCloseButton"/> + </actionGroup> +</actionGroups> diff --git a/app/code/Magento/Catalog/Test/Mftf/ActionGroup/AdminAssignImageRolesActionGroup.xml b/app/code/Magento/Catalog/Test/Mftf/ActionGroup/AdminAssignImageRolesActionGroup.xml index e5cefda0aca96..12602615db8ef 100644 --- a/app/code/Magento/Catalog/Test/Mftf/ActionGroup/AdminAssignImageRolesActionGroup.xml +++ b/app/code/Magento/Catalog/Test/Mftf/ActionGroup/AdminAssignImageRolesActionGroup.xml @@ -25,14 +25,4 @@ <checkOption selector="{{AdminProductImagesSection.roleSwatch}}" stepKey="checkRoleSwatch"/> <click selector="{{AdminSlideOutDialogSection.closeButton}}" stepKey="clickCloseButton"/> </actionGroup> - <actionGroup name="AdminAssignImageRolesIfUnassignedActionGroup" extends="AdminAssignImageRolesActionGroup"> - <annotations> - <description>Requires the navigation to the Product Creation page. Assign the Base, Small, Thumbnail, and Swatch Roles to image.</description> - </annotations> - - <conditionalClick selector="{{AdminProductImagesSection.roleBase}}" dependentSelector="{{AdminProductImagesSection.isRoleChecked('Base')}}" visible="false" stepKey="checkRoleBase"/> - <conditionalClick selector="{{AdminProductImagesSection.roleSmall}}" dependentSelector="{{AdminProductImagesSection.isRoleChecked('Small')}}" visible="false" stepKey="checkRoleSmall"/> - <conditionalClick selector="{{AdminProductImagesSection.roleThumbnail}}" dependentSelector="{{AdminProductImagesSection.isRoleChecked('Thumbnail')}}" visible="false" stepKey="checkRoleThumbnail"/> - <conditionalClick selector="{{AdminProductImagesSection.roleSwatch}}" dependentSelector="{{AdminProductImagesSection.isRoleChecked('Swatch')}}" visible="false" stepKey="checkRoleSwatch"/> - </actionGroup> </actionGroups> diff --git a/app/code/Magento/Catalog/Test/Mftf/ActionGroup/AdminAssignImageRolesIfUnassignedActionGroup.xml b/app/code/Magento/Catalog/Test/Mftf/ActionGroup/AdminAssignImageRolesIfUnassignedActionGroup.xml new file mode 100644 index 0000000000000..ca82882b141cb --- /dev/null +++ b/app/code/Magento/Catalog/Test/Mftf/ActionGroup/AdminAssignImageRolesIfUnassignedActionGroup.xml @@ -0,0 +1,21 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + /** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +--> + +<actionGroups xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/actionGroupSchema.xsd"> + <actionGroup name="AdminAssignImageRolesIfUnassignedActionGroup" extends="AdminAssignImageRolesActionGroup"> + <annotations> + <description>Requires the navigation to the Product Creation page. Assign the Base, Small, Thumbnail, and Swatch Roles to image.</description> + </annotations> + + <conditionalClick selector="{{AdminProductImagesSection.roleBase}}" dependentSelector="{{AdminProductImagesSection.isRoleChecked('Base')}}" visible="false" stepKey="checkRoleBase"/> + <conditionalClick selector="{{AdminProductImagesSection.roleSmall}}" dependentSelector="{{AdminProductImagesSection.isRoleChecked('Small')}}" visible="false" stepKey="checkRoleSmall"/> + <conditionalClick selector="{{AdminProductImagesSection.roleThumbnail}}" dependentSelector="{{AdminProductImagesSection.isRoleChecked('Thumbnail')}}" visible="false" stepKey="checkRoleThumbnail"/> + <conditionalClick selector="{{AdminProductImagesSection.roleSwatch}}" dependentSelector="{{AdminProductImagesSection.isRoleChecked('Swatch')}}" visible="false" stepKey="checkRoleSwatch"/> + </actionGroup> +</actionGroups> diff --git a/app/code/Magento/Catalog/Test/Mftf/ActionGroup/AdminCategoriesExpandAllActionGroup.xml b/app/code/Magento/Catalog/Test/Mftf/ActionGroup/AdminCategoriesExpandAllActionGroup.xml new file mode 100644 index 0000000000000..aa4a69f8abf62 --- /dev/null +++ b/app/code/Magento/Catalog/Test/Mftf/ActionGroup/AdminCategoriesExpandAllActionGroup.xml @@ -0,0 +1,13 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + /** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +--> +<actionGroups xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/actionGroupSchema.xsd"> + <actionGroup name="AdminCategoriesExpandAllActionGroup"> + <click selector="{{AdminCategorySidebarTreeSection.expandAll}}" stepKey="clickExpandAll"/> + </actionGroup> +</actionGroups> \ No newline at end of file diff --git a/app/code/Magento/Catalog/Test/Mftf/ActionGroup/AdminCategoriesOpenCategoryActionGroup.xml b/app/code/Magento/Catalog/Test/Mftf/ActionGroup/AdminCategoriesOpenCategoryActionGroup.xml new file mode 100644 index 0000000000000..3903615597eb3 --- /dev/null +++ b/app/code/Magento/Catalog/Test/Mftf/ActionGroup/AdminCategoriesOpenCategoryActionGroup.xml @@ -0,0 +1,17 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + /** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +--> +<actionGroups xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/actionGroupSchema.xsd"> + <actionGroup name="AdminCategoriesOpenCategoryActionGroup"> + <arguments> + <argument name="category" defaultValue="_defaultCategory"/> + </arguments> + <click selector="{{AdminCategorySidebarTreeSection.categoryInTree(category.name)}}" stepKey="clickCategoryLink"/> + <waitForPageLoad stepKey="waitForCategoryPageLoad"/> + </actionGroup> +</actionGroups> \ No newline at end of file diff --git a/app/code/Magento/Catalog/Test/Mftf/ActionGroup/AdminCategoriesOpenContentSectionActionGroup.xml b/app/code/Magento/Catalog/Test/Mftf/ActionGroup/AdminCategoriesOpenContentSectionActionGroup.xml new file mode 100644 index 0000000000000..edc3d11bebe66 --- /dev/null +++ b/app/code/Magento/Catalog/Test/Mftf/ActionGroup/AdminCategoriesOpenContentSectionActionGroup.xml @@ -0,0 +1,15 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + /** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +--> +<actionGroups xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/actionGroupSchema.xsd"> + <actionGroup name="AdminCategoriesOpenContentSectionActionGroup"> + <waitForElementVisible selector="{{AdminCategoryContentSection.sectionHeader}}" stepKey="waitForContentSection"/> + <conditionalClick selector="{{AdminCategoryContentSection.sectionHeader}}" dependentSelector="{{AdminCategoryContentSection.uploadButton}}" visible="false" stepKey="openContentSection"/> + <waitForPageLoad stepKey="waitForContentSectionLoad"/> + </actionGroup> +</actionGroups> \ No newline at end of file diff --git a/app/code/Magento/Catalog/Test/Mftf/ActionGroup/AdminCategoriesSetDisplayModeActionGroup.xml b/app/code/Magento/Catalog/Test/Mftf/ActionGroup/AdminCategoriesSetDisplayModeActionGroup.xml new file mode 100644 index 0000000000000..7e3877bb76ffc --- /dev/null +++ b/app/code/Magento/Catalog/Test/Mftf/ActionGroup/AdminCategoriesSetDisplayModeActionGroup.xml @@ -0,0 +1,19 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + /** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +--> +<actionGroups xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/actionGroupSchema.xsd"> + <actionGroup name="AdminCategoriesSetDisplayModeActionGroup"> + <arguments> + <argument name="value" type="string"/> + </arguments> + <waitForElementVisible selector="{{AdminCategoryDisplaySettingsSection.settingsHeader}}" stepKey="waitForDisplaySettingsSection"/> + <conditionalClick selector="{{AdminCategoryDisplaySettingsSection.settingsHeader}}" dependentSelector="{{AdminCategoryDisplaySettingsSection.displayMode}}" visible="false" stepKey="openDisplaySettingsSection"/> + <waitForPageLoad stepKey="waitForDisplaySettingsLoad"/> + <selectOption stepKey="selectStaticBlockOnlyOption" userInput="{{value}}" selector="{{AdminCategoryDisplaySettingsSection.displayMode}}"/> + </actionGroup> +</actionGroups> \ No newline at end of file diff --git a/app/code/Magento/Catalog/Test/Mftf/ActionGroup/AdminCategoriesSetStaticBlockActionGroup.xml b/app/code/Magento/Catalog/Test/Mftf/ActionGroup/AdminCategoriesSetStaticBlockActionGroup.xml new file mode 100644 index 0000000000000..1d5308a639d72 --- /dev/null +++ b/app/code/Magento/Catalog/Test/Mftf/ActionGroup/AdminCategoriesSetStaticBlockActionGroup.xml @@ -0,0 +1,16 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + /** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +--> +<actionGroups xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/actionGroupSchema.xsd"> + <actionGroup name="AdminCategoriesSetStaticBlockActionGroup"> + <arguments> + <argument name="block" defaultValue="_defaultBlock"/> + </arguments> + <selectOption selector="{{AdminCategoryContentSection.AddCMSBlock}}" stepKey="selectBlock" userInput="{{block.title}}"/> + </actionGroup> +</actionGroups> \ No newline at end of file diff --git a/app/code/Magento/Catalog/Test/Mftf/ActionGroup/AdminCategoryPageOpenProductsInCategorySectionActionGroup.xml b/app/code/Magento/Catalog/Test/Mftf/ActionGroup/AdminCategoryPageOpenProductsInCategorySectionActionGroup.xml new file mode 100644 index 0000000000000..67c68e03fcfa1 --- /dev/null +++ b/app/code/Magento/Catalog/Test/Mftf/ActionGroup/AdminCategoryPageOpenProductsInCategorySectionActionGroup.xml @@ -0,0 +1,20 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + /** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +--> + +<actionGroups xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/actionGroupSchema.xsd"> + <actionGroup name="AdminCategoryPageOpenProductsInCategorySectionActionGroup"> + <annotations> + <description>Open 'Products in Category' section on category edit page in Admin.</description> + </annotations> + + <conditionalClick selector="{{AdminCategoryProductsSection.sectionHeader}}" dependentSelector="{{AdminCategoryProductsSection.sectionBody}}" visible="false" stepKey="openSectionIfHidden" /> + <scrollTo selector="{{AdminCategoryProductsSection.sectionHeader}}" stepKey="scrollToSection" /> + <waitForPageLoad stepKey="waitSectionFullyLoaded"/> + </actionGroup> +</actionGroups> diff --git a/app/code/Magento/Catalog/Test/Mftf/ActionGroup/AdminChangeSeoUrlKeyForSubCategoryWithoutRedirectActionGroup.xml b/app/code/Magento/Catalog/Test/Mftf/ActionGroup/AdminChangeSeoUrlKeyForSubCategoryWithoutRedirectActionGroup.xml new file mode 100644 index 0000000000000..fc010cec4cb65 --- /dev/null +++ b/app/code/Magento/Catalog/Test/Mftf/ActionGroup/AdminChangeSeoUrlKeyForSubCategoryWithoutRedirectActionGroup.xml @@ -0,0 +1,21 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + /** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +--> + +<actionGroups xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/actionGroupSchema.xsd"> + <actionGroup name="AdminChangeSeoUrlKeyForSubCategoryWithoutRedirectActionGroup" extends="ChangeSeoUrlKeyForSubCategoryActionGroup"> + <annotations> + <description>Requires navigation to subcategory creation/edit. Updates the Search Engine Optimization with uncheck Redirect Checkbox .</description> + </annotations> + <arguments> + <argument name="value" type="string"/> + </arguments> + + <uncheckOption selector="{{AdminCategorySEOSection.UrlKeyRedirectCheckbox}}" stepKey="uncheckRedirectCheckbox" after="enterURLKey"/> + </actionGroup> +</actionGroups> diff --git a/app/code/Magento/Catalog/Test/Mftf/ActionGroup/AdminDeleteAllProductCustomOptionsActionGroup.xml b/app/code/Magento/Catalog/Test/Mftf/ActionGroup/AdminDeleteAllProductCustomOptionsActionGroup.xml deleted file mode 100644 index 103c25b2c6f50..0000000000000 --- a/app/code/Magento/Catalog/Test/Mftf/ActionGroup/AdminDeleteAllProductCustomOptionsActionGroup.xml +++ /dev/null @@ -1,26 +0,0 @@ -<?xml version="1.0" encoding="UTF-8"?> -<!-- - /** - * Copyright © Magento, Inc. All rights reserved. - * See COPYING.txt for license details. - */ ---> - -<actionGroups xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" - xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/actionGroupSchema.xsd"> - <actionGroup name="AdminDeleteAllProductCustomOptionsActionGroup"> - <conditionalClick selector="{{AdminProductCustomizableOptionsSection.customizableOptions}}" dependentSelector="{{AdminProductCustomizableOptionsSection.checkIfCustomizableOptionsTabOpen}}" visible="true" stepKey="expandContentTab"/> - <waitForPageLoad time="10" stepKey="waitCustomizableOptionsTabOpened"/> - <executeInSelenium function="function($webdriver) use ($I) { - $buttons = $webdriver->findElements(\Facebook\WebDriver\WebDriverBy::cssSelector('[data-index=\'options\'] [data-index=\'delete_button\']')); - while(!empty($buttons)) { - $button = reset($buttons); - $I->executeJS('arguments[0].scrollIntoView(false)', [$button]); - $button->click(); - $webdriver->wait()->until(\Facebook\WebDriver\WebDriverExpectedCondition::stalenessOf($button)); - $buttons = $webdriver->findElements(\Facebook\WebDriver\WebDriverBy::cssSelector('[data-index=\'options\'] [data-index=\'delete_button\']')); - } - }" stepKey="deleteCustomOptions"/> - <dontSeeElement selector="{{AdminProductCustomizableOptionsSection.customOptionButtonDelete}}" stepKey="assertNoCustomOptions"/> - </actionGroup> -</actionGroups> diff --git a/app/code/Magento/Catalog/Test/Mftf/ActionGroup/AdminDeleteProductAttributeByLabelActionGroup.xml b/app/code/Magento/Catalog/Test/Mftf/ActionGroup/AdminDeleteProductAttributeByLabelActionGroup.xml new file mode 100644 index 0000000000000..7b453735994d7 --- /dev/null +++ b/app/code/Magento/Catalog/Test/Mftf/ActionGroup/AdminDeleteProductAttributeByLabelActionGroup.xml @@ -0,0 +1,31 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + /** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +--> + +<actionGroups xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/actionGroupSchema.xsd"> + <actionGroup name="AdminDeleteProductAttributeByLabelActionGroup"> + <annotations> + <description>Goes to the Admin Product Attributes grid page. Filters the grid for the provided Product Attribute (Label). Deletes the Product Attribute from the grid. Validates that the Success Message is present.</description> + </annotations> + <arguments> + <argument name="productAttributeLabel" type="string" defaultValue="{{ProductAttributeFrontendLabel.label}}"/> + </arguments> + + <amOnPage url="{{AdminProductAttributeGridPage.url}}" stepKey="navigateToProductAttributeGrid"/> + <waitForPageLoad stepKey="waitForProductAttributeGridPageLoad"/> + <click selector="{{AdminProductAttributeGridSection.ResetFilter}}" stepKey="resetFiltersOnGrid"/> + <fillField selector="{{AdminProductAttributeGridSection.attributeLabelFilter}}" userInput="{{productAttributeLabel}}" stepKey="setAttributeLabelFilter"/> + <click selector="{{AdminProductAttributeGridSection.Search}}" stepKey="searchForAttributeLabelFromTheGrid"/> + <click selector="{{AdminProductAttributeGridSection.FirstRow}}" stepKey="clickOnAttributeRow"/> + <click selector="{{AttributePropertiesSection.DeleteAttribute}}" stepKey="clickOnDeleteAttributeButton"/> + <waitForElementVisible selector="{{AdminConfirmationModalSection.message}}" stepKey="waitForConfirmationPopUpVisible"/> + <click selector="{{AdminConfirmationModalSection.ok}}" stepKey="clickOnConfirmationButton"/> + <waitForElementVisible selector="{{AdminMessagesSection.success}}" stepKey="waitForSuccessMessageVisible"/> + <see selector="{{AdminMessagesSection.success}}" userInput="{{ProductAttributeMessages.remove_success}}" stepKey="seeAttributeDeleteSuccessMessage"/> + </actionGroup> +</actionGroups> diff --git a/app/code/Magento/Catalog/Test/Mftf/ActionGroup/AdminExpandCategoryTreeActionGroup.xml b/app/code/Magento/Catalog/Test/Mftf/ActionGroup/AdminExpandCategoryTreeActionGroup.xml new file mode 100644 index 0000000000000..f2cce9b9a42fe --- /dev/null +++ b/app/code/Magento/Catalog/Test/Mftf/ActionGroup/AdminExpandCategoryTreeActionGroup.xml @@ -0,0 +1,19 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + /** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +--> + +<actionGroups xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/actionGroupSchema.xsd"> + <actionGroup name="AdminExpandCategoryTreeActionGroup"> + <annotations> + <description>Expands category tree.</description> + </annotations> + + <click selector="{{AdminCategorySidebarTreeSection.expandAll}}" stepKey="clickOnExpandTree"/> + <waitForPageLoad stepKey="waitForCategoryToLoad"/> + </actionGroup> +</actionGroups> diff --git a/app/code/Magento/Catalog/Test/Mftf/ActionGroup/AdminExpandProductAttributesTabActionGroup.xml b/app/code/Magento/Catalog/Test/Mftf/ActionGroup/AdminExpandProductAttributesTabActionGroup.xml new file mode 100644 index 0000000000000..a423681c0a490 --- /dev/null +++ b/app/code/Magento/Catalog/Test/Mftf/ActionGroup/AdminExpandProductAttributesTabActionGroup.xml @@ -0,0 +1,19 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + /** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +--> + +<actionGroups xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/actionGroupSchema.xsd"> + <actionGroup name="AdminExpandProductAttributesTabActionGroup"> + <annotations> + <description>Expands the 'Attributes' tab on the Admin Product page.</description> + </annotations> + + <scrollTo selector="{{AdminProductAttributeSection.attributeSectionHeader}}" stepKey="scrollToAttributesTab"/> + <conditionalClick selector="{{AdminProductAttributeSection.attributeSectionHeader}}" dependentSelector="{{AdminProductAttributeSection.attributeSection}}" visible="false" stepKey="expandAttributesTab"/> + </actionGroup> +</actionGroups> diff --git a/app/code/Magento/Catalog/Test/Mftf/ActionGroup/AdminFillProductAttributeDefaultStoreViewActionGroup.xml b/app/code/Magento/Catalog/Test/Mftf/ActionGroup/AdminFillProductAttributeDefaultStoreViewActionGroup.xml new file mode 100644 index 0000000000000..42d205f0fb397 --- /dev/null +++ b/app/code/Magento/Catalog/Test/Mftf/ActionGroup/AdminFillProductAttributeDefaultStoreViewActionGroup.xml @@ -0,0 +1,16 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + /** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +--> +<actionGroups xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/actionGroupSchema.xsd"> + <actionGroup name="AdminFillProductAttributeDefaultStoreViewActionGroup"> + <arguments> + <argument name="value" type="string"/> + </arguments> + <fillField selector="{{AdminProductAttributeManageLabelsSection.DefaultStoreLabel}}" userInput="{{value}}" stepKey="fillDefaultStoreViewLabel"/> + </actionGroup> +</actionGroups> diff --git a/app/code/Magento/Catalog/Test/Mftf/ActionGroup/AdminNavigateToNewProductAttributePageActionGroup.xml b/app/code/Magento/Catalog/Test/Mftf/ActionGroup/AdminNavigateToNewProductAttributePageActionGroup.xml new file mode 100644 index 0000000000000..b7dbcc2c86dc4 --- /dev/null +++ b/app/code/Magento/Catalog/Test/Mftf/ActionGroup/AdminNavigateToNewProductAttributePageActionGroup.xml @@ -0,0 +1,19 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + /** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +--> + +<actionGroups xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/actionGroupSchema.xsd"> + <actionGroup name="AdminNavigateToNewProductAttributePageActionGroup"> + <annotations> + <description>Go to the create new product attribute page</description> + </annotations> + + <amOnPage url="{{ProductAttributePage.url}}" stepKey="goToNewProductAttributePage"/> + <waitForPageLoad stepKey="waitForAttributePageLoad"/> + </actionGroup> +</actionGroups> diff --git a/app/code/Magento/Catalog/Test/Mftf/ActionGroup/AdminOpenCategoriesPageActionGroup.xml b/app/code/Magento/Catalog/Test/Mftf/ActionGroup/AdminOpenCategoriesPageActionGroup.xml new file mode 100644 index 0000000000000..7f41a0c4eb1a3 --- /dev/null +++ b/app/code/Magento/Catalog/Test/Mftf/ActionGroup/AdminOpenCategoriesPageActionGroup.xml @@ -0,0 +1,14 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + /** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +--> +<actionGroups xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/actionGroupSchema.xsd"> + <actionGroup name="AdminOpenCategoriesPageActionGroup"> + <amOnPage url="{{AdminCategoryPage.url}}" stepKey="onCategoryIndexPage"/> + <waitForPageLoad stepKey="waitForPageLoad" after="onCategoryIndexPage"/> + </actionGroup> +</actionGroups> \ No newline at end of file diff --git a/app/code/Magento/Catalog/Test/Mftf/ActionGroup/AdminOpenCategoryPageActionGroup.xml b/app/code/Magento/Catalog/Test/Mftf/ActionGroup/AdminOpenCategoryPageActionGroup.xml new file mode 100644 index 0000000000000..14c4f5234ba67 --- /dev/null +++ b/app/code/Magento/Catalog/Test/Mftf/ActionGroup/AdminOpenCategoryPageActionGroup.xml @@ -0,0 +1,19 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + /** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +--> + +<actionGroups xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/actionGroupSchema.xsd"> + <actionGroup name="AdminOpenCategoryPageActionGroup"> + <annotations> + <description>Navigates to category page.</description> + </annotations> + + <amOnPage url="{{AdminCategoryPage.url}}" stepKey="openAdminCategoryIndexPage"/> + <waitForPageLoad stepKey="waitForCategoryPageToLoad"/> + </actionGroup> +</actionGroups> diff --git a/app/code/Magento/Catalog/Test/Mftf/ActionGroup/AdminOpenProductImagesSectionActionGroup.xml b/app/code/Magento/Catalog/Test/Mftf/ActionGroup/AdminOpenProductImagesSectionActionGroup.xml new file mode 100644 index 0000000000000..4d49b13a8bf5a --- /dev/null +++ b/app/code/Magento/Catalog/Test/Mftf/ActionGroup/AdminOpenProductImagesSectionActionGroup.xml @@ -0,0 +1,18 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + /** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +--> + +<actionGroups xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/actionGroupSchema.xsd"> + <actionGroup name="AdminOpenProductImagesSectionActionGroup"> + <annotations> + <description>Requires the navigation to the Product page. Opens 'Image and Videos' section.</description> + </annotations> + <conditionalClick selector="{{AdminProductImagesSection.productImagesToggle}}" dependentSelector="{{AdminProductImagesSection.imageUploadButton}}" visible="false" stepKey="openProductImagesSection"/> + <waitForElementVisible selector="{{AdminProductImagesSection.imageUploadButton}}" stepKey="waitForImageUploadButton"/> + </actionGroup> +</actionGroups> diff --git a/app/code/Magento/Catalog/Test/Mftf/ActionGroup/AdminProductAttributePageSwitchTabActionGroup.xml b/app/code/Magento/Catalog/Test/Mftf/ActionGroup/AdminProductAttributePageSwitchTabActionGroup.xml new file mode 100644 index 0000000000000..cbef1b61fe44c --- /dev/null +++ b/app/code/Magento/Catalog/Test/Mftf/ActionGroup/AdminProductAttributePageSwitchTabActionGroup.xml @@ -0,0 +1,21 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + /** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +--> + +<actionGroups xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/actionGroupSchema.xsd"> + <actionGroup name="AdminProductAttributePageSwitchTabActionGroup"> + <annotations> + <description>Switches the active tab on the Product Attribute New/Edit Page</description> + </annotations> + <arguments> + <argument name="tabName" type="string"/> + </arguments> + + <click selector="{{AdminEditProductAttributesSection.tabButton(tabName)}}" stepKey="changeProductAttributeActiveTab"/> + </actionGroup> +</actionGroups> diff --git a/app/code/Magento/Catalog/Test/Mftf/ActionGroup/AdminProductFormAdvancedPricingAddTierPriceActionGroup.xml b/app/code/Magento/Catalog/Test/Mftf/ActionGroup/AdminProductFormAdvancedPricingAddTierPriceActionGroup.xml new file mode 100644 index 0000000000000..f823db0a86548 --- /dev/null +++ b/app/code/Magento/Catalog/Test/Mftf/ActionGroup/AdminProductFormAdvancedPricingAddTierPriceActionGroup.xml @@ -0,0 +1,34 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + /** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +--> + +<actionGroups xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/actionGroupSchema.xsd"> + <actionGroup name="AdminProductFormAdvancedPricingAddTierPriceActionGroup"> + <annotations> + <description>Add new tier price on Advanced Pricing dialog on the Admin Product creation/edit page.</description> + </annotations> + <arguments> + <argument name="website" type="string" defaultValue="All Websites [USD]"/> + <argument name="customerGroup" type="string" defaultValue="ALL GROUPS"/> + <argument name="quantity" type="string" defaultValue="1"/> + <argument name="priceType" type="string" defaultValue="Fixed"/> + <argument name="amount" type="string" defaultValue="10"/> + </arguments> + + <waitForElementVisible selector="{{AdminProductFormAdvancedPricingSection.customerGroupPriceAddButton}}" stepKey="waitForGroupPriceAddButtonAppears"/> + <click selector="{{AdminProductFormAdvancedPricingSection.customerGroupPriceAddButton}}" stepKey="clickCustomerGroupPriceAddButton"/> + <waitForElementVisible selector="{{AdminProductFormAdvancedPricingSection.lastTierPriceWebsite}}" stepKey="waitForPriceWebsiteInputAppears"/> + <selectOption selector="{{AdminProductFormAdvancedPricingSection.lastTierPriceWebsite}}" userInput="{{website}}" stepKey="selectWebsite"/> + <selectOption selector="{{AdminProductFormAdvancedPricingSection.lastTierPriceCustomerGroup}}" userInput="{{customerGroup}}" stepKey="selectCustomerGroup"/> + <fillField selector="{{AdminProductFormAdvancedPricingSection.lastTierPriceQty}}" userInput="{{quantity}}" stepKey="fillQuantity"/> + <selectOption selector="{{AdminProductFormAdvancedPricingSection.lastTierPriceType}}" userInput="{{priceType}}" stepKey="selectPriceType"/> + <executeJS function="return '{{priceType}}' == 'Discount' ? "{{AdminProductFormAdvancedPricingSection.lastTierPriceDiscountAmount}}" : "{{AdminProductFormAdvancedPricingSection.lastTierPriceFixedAmount}}"" stepKey="priceAmountSelector"/> + <waitForElementVisible selector="{$priceAmountSelector}" stepKey="waitPriceAmountFieldAppers"/> + <fillField selector="{$priceAmountSelector}" userInput="{{amount}}" stepKey="fillPriceAmount"/> + </actionGroup> +</actionGroups> diff --git a/app/code/Magento/Catalog/Test/Mftf/ActionGroup/AdminProductFormCloseAdvancedPricingDialogActionGroup.xml b/app/code/Magento/Catalog/Test/Mftf/ActionGroup/AdminProductFormCloseAdvancedPricingDialogActionGroup.xml new file mode 100644 index 0000000000000..03c98c1cb17b7 --- /dev/null +++ b/app/code/Magento/Catalog/Test/Mftf/ActionGroup/AdminProductFormCloseAdvancedPricingDialogActionGroup.xml @@ -0,0 +1,19 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + /** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +--> + +<actionGroups xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/actionGroupSchema.xsd"> + <actionGroup name="AdminProductFormCloseAdvancedPricingDialogActionGroup"> + <annotations> + <description>Close Advanced Pricing dialog from product form.</description> + </annotations> + + <scrollToTopOfPage stepKey="scrollToTopOfThePage"/> + <click selector="{{AdminProductFormAdvancedPricingSection.advancedPricingCloseButton}}" stepKey="clickCloseButton"/> + </actionGroup> +</actionGroups> diff --git a/app/code/Magento/Catalog/Test/Mftf/ActionGroup/AdminProductFormDoneAdvancedPricingDialogActionGroup.xml b/app/code/Magento/Catalog/Test/Mftf/ActionGroup/AdminProductFormDoneAdvancedPricingDialogActionGroup.xml new file mode 100644 index 0000000000000..10f2d32799200 --- /dev/null +++ b/app/code/Magento/Catalog/Test/Mftf/ActionGroup/AdminProductFormDoneAdvancedPricingDialogActionGroup.xml @@ -0,0 +1,19 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + /** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +--> + +<actionGroups xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/actionGroupSchema.xsd"> + <actionGroup name="AdminProductFormDoneAdvancedPricingDialogActionGroup"> + <annotations> + <description>Done Advanced Pricing dialog from product form.</description> + </annotations> + + <scrollToTopOfPage stepKey="scrollToTopOfThePage"/> + <click selector="{{AdminProductFormAdvancedPricingSection.doneButton}}" stepKey="clickDoneButton"/> + </actionGroup> +</actionGroups> diff --git a/app/code/Magento/Catalog/Test/Mftf/ActionGroup/AdminProductFormOpenAdvancedPricingDialogActionGroup.xml b/app/code/Magento/Catalog/Test/Mftf/ActionGroup/AdminProductFormOpenAdvancedPricingDialogActionGroup.xml new file mode 100644 index 0000000000000..1c96ce3469485 --- /dev/null +++ b/app/code/Magento/Catalog/Test/Mftf/ActionGroup/AdminProductFormOpenAdvancedPricingDialogActionGroup.xml @@ -0,0 +1,20 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + /** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +--> + +<actionGroups xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/actionGroupSchema.xsd"> + <actionGroup name="AdminProductFormOpenAdvancedPricingDialogActionGroup"> + <annotations> + <description>Open Advanced Pricing dialog from product form.</description> + </annotations> + + <click selector="{{AdminProductFormSection.advancedPricingLink}}" stepKey="clickAdvancedPricingLink"/> + <waitForElementVisible selector="{{AdminProductFormAdvancedPricingSection.modalTitle}}" stepKey="waitForModalTitleAppears"/> + <see selector="{{AdminProductFormAdvancedPricingSection.modalTitle}}" userInput="Advanced Pricing" stepKey="checkModalTitle"/> + </actionGroup> +</actionGroups> diff --git a/app/code/Magento/Catalog/Test/Mftf/ActionGroup/AdminProductPageOpenByIdActionGroup.xml b/app/code/Magento/Catalog/Test/Mftf/ActionGroup/AdminProductPageOpenByIdActionGroup.xml new file mode 100644 index 0000000000000..61626c0ea5662 --- /dev/null +++ b/app/code/Magento/Catalog/Test/Mftf/ActionGroup/AdminProductPageOpenByIdActionGroup.xml @@ -0,0 +1,18 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + /** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +--> + +<actionGroups xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/actionGroupSchema.xsd"> + <actionGroup name="AdminProductPageOpenByIdActionGroup"> + <arguments> + <argument name="productId" type="string"/> + </arguments> + + <amOnPage url="{{AdminProductEditPage.url(productId)}}" stepKey="goToProduct"/> + </actionGroup> +</actionGroups> diff --git a/app/code/Magento/Catalog/Test/Mftf/ActionGroup/AdminSaveCategoryActionGroup.xml b/app/code/Magento/Catalog/Test/Mftf/ActionGroup/AdminSaveCategoryActionGroup.xml new file mode 100644 index 0000000000000..535a08382aabb --- /dev/null +++ b/app/code/Magento/Catalog/Test/Mftf/ActionGroup/AdminSaveCategoryActionGroup.xml @@ -0,0 +1,15 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + /** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +--> +<actionGroups xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/actionGroupSchema.xsd"> + <actionGroup name="AdminSaveCategoryActionGroup"> + <click selector="{{AdminCategoryMainActionsSection.SaveButton}}" stepKey="saveCategoryWithProducts"/> + <waitForPageLoad stepKey="waitForCategorySaved"/> + <see userInput="You saved the category." stepKey="seeSuccessMessage"/> + </actionGroup> +</actionGroups> \ No newline at end of file diff --git a/app/code/Magento/Catalog/Test/Mftf/ActionGroup/AdminSaveCategoryFormActionGroup.xml b/app/code/Magento/Catalog/Test/Mftf/ActionGroup/AdminSaveCategoryFormActionGroup.xml new file mode 100644 index 0000000000000..5521cc2a7d0b2 --- /dev/null +++ b/app/code/Magento/Catalog/Test/Mftf/ActionGroup/AdminSaveCategoryFormActionGroup.xml @@ -0,0 +1,22 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + /** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +--> + +<actionGroups xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/actionGroupSchema.xsd"> + <actionGroup name="AdminSaveCategoryFormActionGroup"> + <annotations> + <description>Save category edit form in Admin and check success message.</description> + </annotations> + + <seeInCurrentUrl url="{{AdminCategoryPage.url}}" stepKey="seeOnCategoryPage"/> + <scrollToTopOfPage stepKey="scrollToTopOfTheCategoryPage"/> + <click selector="{{AdminMainActionsSection.save}}" stepKey="saveCategory"/> + <waitForElementVisible selector="{{AdminMessagesSection.success}}" stepKey="waitForSuccessMessageAppears"/> + <see userInput="You saved the category." selector="{{AdminMessagesSection.success}}" stepKey="assertSuccessMessage"/> + </actionGroup> +</actionGroups> diff --git a/app/code/Magento/Catalog/Test/Mftf/ActionGroup/AdminSaveProductAttributeActionGroup.xml b/app/code/Magento/Catalog/Test/Mftf/ActionGroup/AdminSaveProductAttributeActionGroup.xml new file mode 100644 index 0000000000000..956dc3bf6fa52 --- /dev/null +++ b/app/code/Magento/Catalog/Test/Mftf/ActionGroup/AdminSaveProductAttributeActionGroup.xml @@ -0,0 +1,20 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + /** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +--> + +<actionGroups xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/actionGroupSchema.xsd"> + <actionGroup name="AdminSaveProductAttributeActionGroup"> + <annotations> + <description>Clicks on Save button to save the attribute.</description> + </annotations> + + <waitForElementVisible selector="{{AttributePropertiesSection.Save}}" stepKey="waitForSaveButton"/> + <click selector="{{AttributePropertiesSection.Save}}" stepKey="clickSaveButton"/> + <waitForPageLoad stepKey="waitForAttributeToSave"/> + </actionGroup> +</actionGroups> diff --git a/app/code/Magento/Catalog/Test/Mftf/ActionGroup/AdminSetUseInSearchValueForProductAttributeActionGroup.xml b/app/code/Magento/Catalog/Test/Mftf/ActionGroup/AdminSetUseInSearchValueForProductAttributeActionGroup.xml new file mode 100644 index 0000000000000..d5c2ed847e33f --- /dev/null +++ b/app/code/Magento/Catalog/Test/Mftf/ActionGroup/AdminSetUseInSearchValueForProductAttributeActionGroup.xml @@ -0,0 +1,23 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + /** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +--> +<actionGroups xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/actionGroupSchema.xsd"> + <actionGroup name="AdminSetUseInSearchValueForProductAttributeActionGroup"> + <annotations> + <description>Set 'Use In Search' value for product attribute</description> + </annotations> + <arguments> + <argument name="useInSearchValue" type="string" defaultValue="Yes"/> + </arguments> + + <scrollToTopOfPage stepKey="scrollToTopOfPage"/> + <click selector="{{StorefrontPropertiesSection.StoreFrontPropertiesTab}}" stepKey="clickStorefrontPropertiesTab"/> + <waitForElementVisible selector="{{AdvancedAttributePropertiesSection.UseInSearch}}" stepKey="waitForUseInSearchElementVisible"/> + <selectOption selector="{{AdvancedAttributePropertiesSection.UseInSearch}}" userInput="{{useInSearchValue}}" stepKey="setUseInSearchValue"/> + </actionGroup> +</actionGroups> diff --git a/app/code/Magento/Catalog/Test/Mftf/ActionGroup/AssertAdminCategoryLevelByParentCategoryLevelActionGroup.xml b/app/code/Magento/Catalog/Test/Mftf/ActionGroup/AssertAdminCategoryLevelByParentCategoryLevelActionGroup.xml new file mode 100644 index 0000000000000..bf683a7baec1d --- /dev/null +++ b/app/code/Magento/Catalog/Test/Mftf/ActionGroup/AssertAdminCategoryLevelByParentCategoryLevelActionGroup.xml @@ -0,0 +1,25 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + /** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +--> + +<actionGroups xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/actionGroupSchema.xsd"> + <actionGroup name="AssertAdminCategoryLevelByParentCategoryLevelActionGroup"> + <annotations> + <description>Checks category level by parent category level.</description> + </annotations> + <arguments> + <argument name="parentCategoryLevel" type="string" defaultValue="2"/> + <argument name="categoryLevel" type="string" defaultValue="3"/> + </arguments> + + <assertEquals message="wrongCategoryLevel" stepKey="compareCategoryLevel"> + <actualResult type="const">{{categoryLevel}}</actualResult> + <expectedResult type="const">{{parentCategoryLevel}} + 1</expectedResult> + </assertEquals> + </actionGroup> +</actionGroups> diff --git a/app/code/Magento/Catalog/Test/Mftf/ActionGroup/AssertAdminProductAttributeByCodeOnProductFormActionGroup.xml b/app/code/Magento/Catalog/Test/Mftf/ActionGroup/AssertAdminProductAttributeByCodeOnProductFormActionGroup.xml new file mode 100644 index 0000000000000..2432c974b79f4 --- /dev/null +++ b/app/code/Magento/Catalog/Test/Mftf/ActionGroup/AssertAdminProductAttributeByCodeOnProductFormActionGroup.xml @@ -0,0 +1,21 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + /** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +--> + +<actionGroups xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/actionGroupSchema.xsd"> + <actionGroup name="AssertAdminProductAttributeByCodeOnProductFormActionGroup"> + <annotations> + <description>Requires the navigation to the Product page. Provided dropdown attribute presents on the page.</description> + </annotations> + <arguments> + <argument name="productAttributeCode" type="string" defaultValue="{{textSwatchProductAttribute.attribute_code}}"/> + </arguments> + + <seeElement selector="{{AdminProductAttributesSection.attributeDropdownByCode(productAttributeCode)}}" stepKey="assertAttributeIsPresentOnForm"/> + </actionGroup> +</actionGroups> diff --git a/app/code/Magento/Catalog/Test/Mftf/ActionGroup/AssertAdminProductFormAdvancedPricingCheckTierPriceActionGroup.xml b/app/code/Magento/Catalog/Test/Mftf/ActionGroup/AssertAdminProductFormAdvancedPricingCheckTierPriceActionGroup.xml new file mode 100644 index 0000000000000..42aee90882400 --- /dev/null +++ b/app/code/Magento/Catalog/Test/Mftf/ActionGroup/AssertAdminProductFormAdvancedPricingCheckTierPriceActionGroup.xml @@ -0,0 +1,32 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + /** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +--> + +<actionGroups xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/actionGroupSchema.xsd"> + <actionGroup name="AssertAdminProductFormAdvancedPricingCheckTierPriceActionGroup"> + <annotations> + <description>Check AdvancedPricing tier price row.</description> + </annotations> + <arguments> + <argument name="rowNumber" type="string" defaultValue="0"/> + <argument name="website" type="string" defaultValue="All Websites [USD]"/> + <argument name="customerGroup" type="string" defaultValue="ALL GROUPS"/> + <argument name="quantity" type="string" defaultValue="1"/> + <argument name="priceType" type="string" defaultValue="Fixed"/> + <argument name="amount" type="string" defaultValue="10"/> + </arguments> + + <waitForElementVisible selector="{{AdminProductFormAdvancedPricingSection.productTierPriceWebsiteSelect(rowNumber)}}" stepKey="waitForPricesGridAppears"/> + <seeInField selector="{{AdminProductFormAdvancedPricingSection.productTierPriceWebsiteSelect(rowNumber)}}" userInput="{{website}}" stepKey="seeWebsite"/> + <seeInField selector="{{AdminProductFormAdvancedPricingSection.productTierPriceCustGroupSelect(rowNumber)}}" userInput="{{customerGroup}}" stepKey="seeCustomerGroup"/> + <seeInField selector="{{AdminProductFormAdvancedPricingSection.productTierPriceQtyInput(rowNumber)}}" userInput="{{quantity}}" stepKey="seeQuantity"/> + <seeInField selector="{{AdminProductFormAdvancedPricingSection.productTierPriceValueTypeSelect(rowNumber)}}" userInput="{{priceType}}" stepKey="seePriceType"/> + <executeJS function="return '{{priceType}}' == 'Discount' ? "{{AdminProductFormAdvancedPricingSection.productTierPricePercentageValuePriceInput(rowNumber)}}" : "{{AdminProductFormAdvancedPricingSection.productTierPriceFixedPriceInput(rowNumber)}}"" stepKey="priceAmountSelector"/> + <seeInField selector="{$priceAmountSelector}" userInput="{{amount}}" stepKey="seePriceAmount"/> + </actionGroup> +</actionGroups> diff --git a/app/code/Magento/Catalog/Test/Mftf/ActionGroup/AssertAdminProductGridCellActionGroup.xml b/app/code/Magento/Catalog/Test/Mftf/ActionGroup/AssertAdminProductGridCellActionGroup.xml new file mode 100644 index 0000000000000..724c852e7b0be --- /dev/null +++ b/app/code/Magento/Catalog/Test/Mftf/ActionGroup/AssertAdminProductGridCellActionGroup.xml @@ -0,0 +1,23 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + /** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +--> + +<actionGroups xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/actionGroupSchema.xsd"> + <actionGroup name="AssertAdminProductGridCellActionGroup"> + <annotations> + <description>Checks value for Admin Product Grid cell by provided row and column.</description> + </annotations> + <arguments> + <argument name="row" type="string" defaultValue="1"/> + <argument name="column" type="string" defaultValue="Name"/> + <argument name="value" type="string" defaultValue="1"/> + </arguments> + + <see selector="{{AdminProductGridSection.productGridCell(row,column)}}" userInput="{{value}}" stepKey="seeProductGridCellWithProvidedValue"/> + </actionGroup> +</actionGroups> diff --git a/app/code/Magento/Catalog/Test/Mftf/ActionGroup/AssertAdminProductImageRolesSelectedActionGroup.xml b/app/code/Magento/Catalog/Test/Mftf/ActionGroup/AssertAdminProductImageRolesSelectedActionGroup.xml new file mode 100644 index 0000000000000..3bb6210d6b824 --- /dev/null +++ b/app/code/Magento/Catalog/Test/Mftf/ActionGroup/AssertAdminProductImageRolesSelectedActionGroup.xml @@ -0,0 +1,26 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + /** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +--> + +<actionGroups xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/actionGroupSchema.xsd"> + <actionGroup name="AssertAdminProductImageRolesSelectedActionGroup"> + <annotations> + <description>Requires the navigation to the Product page and opened 'Image and Videos' section. + Checks the Base, Small, Thumbnail and Swatch Roles are selected for provided image.</description> + </annotations> + <arguments> + <argument name="imageFileName" type="string" defaultValue="test_image"/> + </arguments> + <waitForElementVisible selector="{{AdminProductImagesSection.imageFile(imageFileName)}}" stepKey="seeProductImageName"/> + <click selector="{{AdminProductImagesSection.imageFile(imageFileName)}}" stepKey="clickProductImage"/> + <waitForElementVisible selector="{{AdminProductImagesSection.isBaseSelected}}" stepKey="checkRoleBaseSelected"/> + <waitForElementVisible selector="{{AdminProductImagesSection.isSmallSelected}}" stepKey="checkRoleSmallSelected"/> + <waitForElementVisible selector="{{AdminProductImagesSection.isThumbnailSelected}}" stepKey="checkRoleThumbnailSelected"/> + <waitForElementVisible selector="{{AdminProductImagesSection.isSwatchSelected}}" stepKey="checkRoleSwatchSelected"/> + </actionGroup> +</actionGroups> diff --git a/app/code/Magento/Catalog/Test/Mftf/ActionGroup/AssertProductOnCategoryPageActionGroup.xml b/app/code/Magento/Catalog/Test/Mftf/ActionGroup/AssertProductOnCategoryPageActionGroup.xml deleted file mode 100644 index d35d6a4778dd6..0000000000000 --- a/app/code/Magento/Catalog/Test/Mftf/ActionGroup/AssertProductOnCategoryPageActionGroup.xml +++ /dev/null @@ -1,19 +0,0 @@ -<?xml version="1.0" encoding="UTF-8"?> -<!-- - /** - * Copyright © Magento, Inc. All rights reserved. - * See COPYING.txt for license details. - */ ---> - -<actionGroups xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" - xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/actionGroupSchema.xsd"> - <actionGroup name="AssertProductOnCategoryPageActionGroup" extends="StorefrontCheckCategorySimpleProductActionGroup"> - <annotations> - <description>EXTENDS:StorefrontCheckCategorySimpleProduct. Removes 'AssertProductPrice', 'moveMouseOverProduct', 'AssertAddToCart'</description> - </annotations> - <remove keyForRemoval="AssertProductPrice"/> - <remove keyForRemoval="moveMouseOverProduct"/> - <remove keyForRemoval="AssertAddToCart"/> - </actionGroup> -</actionGroups> diff --git a/app/code/Magento/Catalog/Test/Mftf/ActionGroup/AssertSeeProductAttributeValidationErrorOnManageLabelsTabActionGroup.xml b/app/code/Magento/Catalog/Test/Mftf/ActionGroup/AssertSeeProductAttributeValidationErrorOnManageLabelsTabActionGroup.xml new file mode 100644 index 0000000000000..370d30003baf8 --- /dev/null +++ b/app/code/Magento/Catalog/Test/Mftf/ActionGroup/AssertSeeProductAttributeValidationErrorOnManageLabelsTabActionGroup.xml @@ -0,0 +1,22 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + /** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +--> + +<actionGroups xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/actionGroupSchema.xsd"> + <actionGroup name="AssertSeeProductAttributeValidationErrorOnManageLabelsTabActionGroup"> + <annotations> + <description>Check whenever the validation error is present for the product attribute in the "Manage Labels" Tab</description> + </annotations> + + <arguments> + <argument name="message" type="string"/> + </arguments> + + <see userInput="{{message}}" selector="{{AdminProductAttributeManageLabelsSection.attributeStoreLabelValidationError}}" stepKey="seeValidationMessage"/> + </actionGroup> +</actionGroups> diff --git a/app/code/Magento/Catalog/Test/Mftf/ActionGroup/AssertSeeProductAttributeValidationErrorOnPropertiesTabActionGroup.xml b/app/code/Magento/Catalog/Test/Mftf/ActionGroup/AssertSeeProductAttributeValidationErrorOnPropertiesTabActionGroup.xml new file mode 100644 index 0000000000000..5e0da379bdccb --- /dev/null +++ b/app/code/Magento/Catalog/Test/Mftf/ActionGroup/AssertSeeProductAttributeValidationErrorOnPropertiesTabActionGroup.xml @@ -0,0 +1,22 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + /** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +--> + +<actionGroups xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/actionGroupSchema.xsd"> + <actionGroup name="AssertSeeProductAttributeValidationErrorOnPropertiesTabActionGroup"> + <annotations> + <description>Check whenever the validation error is present for the product attribute in the "Properties" Tab</description> + </annotations> + + <arguments> + <argument name="message" type="string"/> + </arguments> + + <see userInput="{{message}}" selector="{{AttributePropertiesSection.attributeLabelValidationError}}" stepKey="seeValidationMessage"/> + </actionGroup> +</actionGroups> diff --git a/app/code/Magento/Catalog/Test/Mftf/ActionGroup/AssertSeeProductDetailsOnStorefrontRecentlyViewedWidgetActionGroup.xml b/app/code/Magento/Catalog/Test/Mftf/ActionGroup/AssertSeeProductDetailsOnStorefrontRecentlyViewedWidgetActionGroup.xml new file mode 100644 index 0000000000000..d2f04629e1fe8 --- /dev/null +++ b/app/code/Magento/Catalog/Test/Mftf/ActionGroup/AssertSeeProductDetailsOnStorefrontRecentlyViewedWidgetActionGroup.xml @@ -0,0 +1,25 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + /** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +--> + +<actionGroups xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/actionGroupSchema.xsd"> + <actionGroup name="AssertSeeProductDetailsOnStorefrontRecentlyViewedWidgetActionGroup"> + <annotations> + <description>Goes to the home Page Recently VIewed Product and Grab the Prdouct name and Position from it.</description> + </annotations> + <arguments> + <argument name="productName" type="string"/> + <argument name="productPosition" type="string"/> + </arguments> + <grabTextFrom selector="{{StoreFrontRecentlyViewedProductSection.ProductName(productPosition)}}" stepKey="grabRelatedProductPosition"/> + <assertContains stepKey="assertRelatedProductName"> + <actualResult type="const">$grabRelatedProductPosition</actualResult> + <expectedResult type="const">{{productName}}</expectedResult> + </assertContains> + </actionGroup> +</actionGroups> \ No newline at end of file diff --git a/app/code/Magento/Catalog/Test/Mftf/ActionGroup/AssertStorefrontAttributeOptionPresentInLayeredNavigationActionGroup.xml b/app/code/Magento/Catalog/Test/Mftf/ActionGroup/AssertStorefrontAttributeOptionPresentInLayeredNavigationActionGroup.xml new file mode 100644 index 0000000000000..46d017b9094e8 --- /dev/null +++ b/app/code/Magento/Catalog/Test/Mftf/ActionGroup/AssertStorefrontAttributeOptionPresentInLayeredNavigationActionGroup.xml @@ -0,0 +1,26 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + /** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +--> + +<actionGroups xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/actionGroupSchema.xsd"> + <actionGroup name="AssertStorefrontAttributeOptionPresentInLayeredNavigationActionGroup"> + <annotations> + <description>Clicks on the attribute label. Checks for attribute option presence.</description> + </annotations> + <arguments> + <argument name="attributeLabel" type="string" defaultValue="{{ProductAttributeFrontendLabel.label}}"/> + <argument name="attributeOptionLabel" type="string" defaultValue="{{Option1Store0.label}}"/> + <argument name="attributeOptionPosition" type="string" defaultValue="1"/> + </arguments> + + <waitForElementVisible selector="{{StorefrontCategorySidebarSection.filterOptionsTitle(attributeLabel)}}" stepKey="waitForAttributeVisible"/> + <conditionalClick selector="{{StorefrontCategorySidebarSection.filterOptionsTitle(attributeLabel)}}" dependentSelector="{{StorefrontCategorySidebarSection.activeFilterOptions}}" visible="false" stepKey="clickToExpandAttribute"/> + <waitForElementVisible selector="{{StorefrontCategorySidebarSection.activeFilterOptions}}" stepKey="waitForAttributeOptionsVisible"/> + <see selector="{{StorefrontCategorySidebarSection.activeFilterOptionItemByPosition(attributeOptionPosition)}}" userInput="{{attributeOptionLabel}}" stepKey="assertAttributeOptionInLayeredNavigation"/> + </actionGroup> +</actionGroups> diff --git a/app/code/Magento/Catalog/Test/Mftf/ActionGroup/AssertStorefrontCategorySimpleProductShownActionGroup.xml b/app/code/Magento/Catalog/Test/Mftf/ActionGroup/AssertStorefrontCategorySimpleProductShownActionGroup.xml new file mode 100644 index 0000000000000..a2940708eeb84 --- /dev/null +++ b/app/code/Magento/Catalog/Test/Mftf/ActionGroup/AssertStorefrontCategorySimpleProductShownActionGroup.xml @@ -0,0 +1,26 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. +--> + +<actionGroups xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/actionGroupSchema.xsd"> + <!-- Check simple product on the category page --> + <actionGroup name="AssertStorefrontCategorySimpleProductShownActionGroup"> + <annotations> + <description>Validate that the provided Simple Product is present and correct on a Category page.</description> + </annotations> + <arguments> + <argument name="productName" type="string" defaultValue="{{ApiSimpleOne.name}}"/> + <argument name="productPrice" type="string" defaultValue="{{ApiSimpleOne.price}}"/> + </arguments> + + <waitForElementVisible selector="{{StorefrontCategoryProductSection.ProductTitleByName(productName)}}" stepKey="waitForProduct"/> + <seeElement selector="{{StorefrontCategoryProductSection.ProductTitleByName(productName)}}" stepKey="assertProductName"/> + <see userInput="${{productPrice}}" selector="{{StorefrontCategoryProductSection.ProductPriceByName(productName)}}" stepKey="assertProductPrice"/> + <moveMouseOver selector="{{StorefrontCategoryProductSection.ProductInfoByName(productName)}}" stepKey="moveMouseOverProduct"/> + <seeElement selector="{{StorefrontCategoryProductSection.ProductAddToCartByName(productName)}}" stepKey="assertAddToCartButton"/> + </actionGroup> +</actionGroups> diff --git a/app/code/Magento/Catalog/Test/Mftf/ActionGroup/AssertStorefrontLayeredNavigationCategoryFilterNotVisibleActionGroup.xml b/app/code/Magento/Catalog/Test/Mftf/ActionGroup/AssertStorefrontLayeredNavigationCategoryFilterNotVisibleActionGroup.xml new file mode 100644 index 0000000000000..c892594201f17 --- /dev/null +++ b/app/code/Magento/Catalog/Test/Mftf/ActionGroup/AssertStorefrontLayeredNavigationCategoryFilterNotVisibleActionGroup.xml @@ -0,0 +1,14 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. +--> + +<actionGroups xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/actionGroupSchema.xsd"> + <!-- On a category page with layered navigation, verify if the category filter item is NOT present --> + <actionGroup name="AssertStorefrontLayeredNavigationCategoryFilterNotVisibleActionGroup"> + <!-- Verify category filter item is NOT present --> + <dontSee selector="{{StorefrontCategorySidebarSection.layeredFilterBlock}}" userInput="Category" stepKey="seeCategoryFilterInLayeredNav"/> + </actionGroup> +</actionGroups> diff --git a/app/code/Magento/Catalog/Test/Mftf/ActionGroup/AssertStorefrontLayeredNavigationCategoryFilterVisibleActionGroup.xml b/app/code/Magento/Catalog/Test/Mftf/ActionGroup/AssertStorefrontLayeredNavigationCategoryFilterVisibleActionGroup.xml new file mode 100644 index 0000000000000..8eebafd3cd8ab --- /dev/null +++ b/app/code/Magento/Catalog/Test/Mftf/ActionGroup/AssertStorefrontLayeredNavigationCategoryFilterVisibleActionGroup.xml @@ -0,0 +1,14 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. +--> + +<actionGroups xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/actionGroupSchema.xsd"> + <!-- On a category page with layered navigation, verify if the category filter item is present --> + <actionGroup name="AssertStorefrontLayeredNavigationCategoryFilterVisibleActionGroup"> + <!-- Verify category filter item is present --> + <see selector="{{StorefrontCategorySidebarSection.layeredFilterBlock}}" userInput="Category" stepKey="seeCategoryFilterInLayeredNav"/> + </actionGroup> +</actionGroups> diff --git a/app/code/Magento/Catalog/Test/Mftf/ActionGroup/AssertStorefrontNoProductsFoundActionGroup.xml b/app/code/Magento/Catalog/Test/Mftf/ActionGroup/AssertStorefrontNoProductsFoundActionGroup.xml new file mode 100644 index 0000000000000..f0a4faeb68e4b --- /dev/null +++ b/app/code/Magento/Catalog/Test/Mftf/ActionGroup/AssertStorefrontNoProductsFoundActionGroup.xml @@ -0,0 +1,13 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + /** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +--> +<actionGroups xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/actionGroupSchema.xsd"> + <actionGroup name="AssertStorefrontNoProductsFoundActionGroup"> + <see userInput="We can't find products matching the selection." stepKey="seeEmptyNotice"/> + </actionGroup> +</actionGroups> diff --git a/app/code/Magento/Catalog/Test/Mftf/ActionGroup/AssertStorefrontProductAbsentOnCategoryPageActionGroup.xml b/app/code/Magento/Catalog/Test/Mftf/ActionGroup/AssertStorefrontProductAbsentOnCategoryPageActionGroup.xml new file mode 100644 index 0000000000000..f98229f8aaada --- /dev/null +++ b/app/code/Magento/Catalog/Test/Mftf/ActionGroup/AssertStorefrontProductAbsentOnCategoryPageActionGroup.xml @@ -0,0 +1,24 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + /** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +--> + +<actionGroups xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/actionGroupSchema.xsd"> + <actionGroup name="AssertStorefrontProductAbsentOnCategoryPageActionGroup"> + <annotations> + <description>Navigate to category page and verify product is absent.</description> + </annotations> + <arguments> + <argument name="categoryUrlKey" defaultValue="{{_defaultCategory.url_key}}"/> + <argument name="productName" defaultValue="{{SimpleProduct.name}}"/> + </arguments> + + <amOnPage url="{{StorefrontCategoryPage.url(categoryUrlKey)}}" stepKey="navigateToCategoryPage"/> + <waitForPageLoad stepKey="waitForCategoryPageLoad"/> + <dontSee selector="{{StorefrontCategoryMainSection.productName}}" userInput="{{productName}}" stepKey="assertProductIsNotPresent"/> + </actionGroup> +</actionGroups> diff --git a/app/code/Magento/Catalog/Test/Mftf/ActionGroup/AssertStorefrontProductControlsAreNotVisibleWithoutHoverActionGroup.xml b/app/code/Magento/Catalog/Test/Mftf/ActionGroup/AssertStorefrontProductControlsAreNotVisibleWithoutHoverActionGroup.xml new file mode 100644 index 0000000000000..226bb8468d62e --- /dev/null +++ b/app/code/Magento/Catalog/Test/Mftf/ActionGroup/AssertStorefrontProductControlsAreNotVisibleWithoutHoverActionGroup.xml @@ -0,0 +1,19 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. +--> + +<actionGroups xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/actionGroupSchema.xsd"> + <actionGroup name="AssertStorefrontProductControlsAreNotVisibleWithoutHoverActionGroup"> + <annotations> + <description>Validate that the Product Controls Are Not Visible On Category Page Without Hover on Product</description> + </annotations> + + <dontSeeElement selector="{{StorefrontCategoryMainSection.addToCartButtonProductInfoHover}}" stepKey="assertAddToCartButtonElementIsNotVisible"/> + <dontSeeElement selector="{{StorefrontCategoryMainSection.addToWishListIconProductInfoHover}}" stepKey="assertAddToWishListIconIsNotVisible"/> + <dontSeeElement selector="{{StorefrontCategoryMainSection.addToCompareIconProductInfoHover}}" stepKey="assertAddToCompareIconIsNotVisible"/> + </actionGroup> +</actionGroups> diff --git a/app/code/Magento/Catalog/Test/Mftf/ActionGroup/AssertStorefrontProductControlsAreVisibleOnHoverActionGroup.xml b/app/code/Magento/Catalog/Test/Mftf/ActionGroup/AssertStorefrontProductControlsAreVisibleOnHoverActionGroup.xml new file mode 100644 index 0000000000000..60d56df04bd2f --- /dev/null +++ b/app/code/Magento/Catalog/Test/Mftf/ActionGroup/AssertStorefrontProductControlsAreVisibleOnHoverActionGroup.xml @@ -0,0 +1,19 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. +--> + +<actionGroups xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/actionGroupSchema.xsd"> + <actionGroup name="AssertStorefrontProductControlsAreVisibleOnHoverActionGroup"> + <annotations> + <description>Validate that the Product Controls Are Visible on Category Page on Hover on Product</description> + </annotations> + + <seeElement selector="{{StorefrontCategoryMainSection.addToCartButtonProductInfoHover}}" stepKey="assertAddToCartButtonElementIsVisible"/> + <seeElement selector="{{StorefrontCategoryMainSection.addToWishListIconProductInfoHover}}" stepKey="assertAddToWishListIconIsVisible"/> + <seeElement selector="{{StorefrontCategoryMainSection.addToCompareIconProductInfoHover}}" stepKey="assertAddToCompareIconIsVisible"/> + </actionGroup> +</actionGroups> diff --git a/app/code/Magento/Catalog/Test/Mftf/ActionGroup/AssertStorefrontProductDetailPageFinalPriceActionGroup.xml b/app/code/Magento/Catalog/Test/Mftf/ActionGroup/AssertStorefrontProductDetailPageFinalPriceActionGroup.xml new file mode 100644 index 0000000000000..cf327266909d3 --- /dev/null +++ b/app/code/Magento/Catalog/Test/Mftf/ActionGroup/AssertStorefrontProductDetailPageFinalPriceActionGroup.xml @@ -0,0 +1,20 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + /** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +--> +<actionGroups xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/actionGroupSchema.xsd"> + <actionGroup name="AssertStorefrontProductDetailPageFinalPriceActionGroup"> + <arguments> + <argument name="finalProductPrice" type="string"/> + </arguments> + <grabTextFrom selector="{{StorefrontProductInfoMainSection.price}}" stepKey="productPriceText"/> + <assertEquals stepKey="assertProductPriceOnProductPage"> + <expectedResult type="string">${{finalProductPrice}}</expectedResult> + <actualResult type="variable">productPriceText</actualResult> + </assertEquals> + </actionGroup> +</actionGroups> diff --git a/app/code/Magento/Catalog/Test/Mftf/ActionGroup/AssertStorefrontProductDetailPageNameActionGroup.xml b/app/code/Magento/Catalog/Test/Mftf/ActionGroup/AssertStorefrontProductDetailPageNameActionGroup.xml new file mode 100644 index 0000000000000..7ed38153cc446 --- /dev/null +++ b/app/code/Magento/Catalog/Test/Mftf/ActionGroup/AssertStorefrontProductDetailPageNameActionGroup.xml @@ -0,0 +1,20 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + /** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +--> +<actionGroups xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/actionGroupSchema.xsd"> + <actionGroup name="AssertStorefrontProductDetailPageNameActionGroup"> + <arguments> + <argument name="productName" type="string"/> + </arguments> + <grabTextFrom selector="{{StorefrontProductInfoMainSection.productName}}" stepKey="productNameText"/> + <assertEquals stepKey="assertProductNameOnProductPage"> + <expectedResult type="string">{{productName}}</expectedResult> + <actualResult type="variable">productNameText</actualResult> + </assertEquals> + </actionGroup> +</actionGroups> diff --git a/app/code/Magento/Catalog/Test/Mftf/ActionGroup/AssertStorefrontProductDetailPageNameAndUrlActionGroup.xml b/app/code/Magento/Catalog/Test/Mftf/ActionGroup/AssertStorefrontProductDetailPageNameAndUrlActionGroup.xml new file mode 100644 index 0000000000000..e31fef0f463e1 --- /dev/null +++ b/app/code/Magento/Catalog/Test/Mftf/ActionGroup/AssertStorefrontProductDetailPageNameAndUrlActionGroup.xml @@ -0,0 +1,24 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + /** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +--> + +<actionGroups xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/actionGroupSchema.xsd"> + <actionGroup name="AssertStorefrontProductDetailPageNameAndUrlActionGroup"> + <annotations> + <description>Validates that the Product name and Url are correct.</description> + </annotations> + <arguments> + <argument name="productName" defaultValue="{{ApiSimpleProduct.name}}" type="string"/> + <argument name="productUrl" defaultValue="{{ApiSimpleProduct.urlKey}}" type="string"/> + </arguments> + + <seeInCurrentUrl url="{{StorefrontProductPage.url(productUrl)}}" stepKey="checkUrl"/> + <seeInTitle userInput="{{productName}}" stepKey="assertProductNameTitle"/> + <see userInput="{{productName}}" selector="{{StorefrontProductInfoMainSection.productName}}" stepKey="assertProductName"/> + </actionGroup> +</actionGroups> diff --git a/app/code/Magento/Catalog/Test/Mftf/ActionGroup/AssertStorefrontProductDetailPageTierPriceActionGroup.xml b/app/code/Magento/Catalog/Test/Mftf/ActionGroup/AssertStorefrontProductDetailPageTierPriceActionGroup.xml new file mode 100644 index 0000000000000..47ef781271ade --- /dev/null +++ b/app/code/Magento/Catalog/Test/Mftf/ActionGroup/AssertStorefrontProductDetailPageTierPriceActionGroup.xml @@ -0,0 +1,22 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + /** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +--> +<actionGroups xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/actionGroupSchema.xsd"> + <actionGroup name="AssertStorefrontProductDetailPageTierPriceActionGroup"> + <arguments> + <argument name="tierProductPriceDiscountQuantity" type="string"/> + <argument name="productPriceWithAppliedTierPriceDiscount" type="string"/> + <argument name="productSavedPricePercent" type="string"/> + </arguments> + <grabTextFrom selector="{{StorefrontProductInfoMainSection.tierPriceText}}" stepKey="tierPriceText"/> + <assertEquals stepKey="assertTierPriceTextOnProductPage"> + <expectedResult type="string">Buy {{tierProductPriceDiscountQuantity}} for ${{productPriceWithAppliedTierPriceDiscount}} each and save {{productSavedPricePercent}}%</expectedResult> + <actualResult type="variable">tierPriceText</actualResult> + </assertEquals> + </actionGroup> +</actionGroups> diff --git a/app/code/Magento/Catalog/Test/Mftf/ActionGroup/AssertStorefrontProductImageAppearsOnProductPagePreviewActionGroup.xml b/app/code/Magento/Catalog/Test/Mftf/ActionGroup/AssertStorefrontProductImageAppearsOnProductPagePreviewActionGroup.xml new file mode 100644 index 0000000000000..2aa3a608a0760 --- /dev/null +++ b/app/code/Magento/Catalog/Test/Mftf/ActionGroup/AssertStorefrontProductImageAppearsOnProductPagePreviewActionGroup.xml @@ -0,0 +1,22 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. +--> + +<actionGroups xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/actionGroupSchema.xsd"> + <actionGroup name="AssertStorefrontProductImageAppearsOnProductPagePreviewActionGroup"> + <annotations> + <description>Validate that the Product Image is present on preview and correct on frontend product page.</description> + </annotations> + <arguments> + <argument name="productImage" type="string" defaultValue="{{MagentoLogo.file}}"/> + </arguments> + + <waitForElementNotVisible selector="{{StorefrontProductMediaSection.gallerySpinner}}" stepKey="waitGallerySpinnerDisappear"/> + <seeElement selector="{{StorefrontProductMediaSection.gallery}}" stepKey="seeProductGallery"/> + <seeElement selector="{{StorefrontProductMediaSection.productImageActive(productImage)}}" stepKey="seeCorrectProductImage"/> + </actionGroup> +</actionGroups> diff --git a/app/code/Magento/Catalog/Test/Mftf/ActionGroup/AssertStorefrontProductIsPresentOnCategoryPageActionGroup.xml b/app/code/Magento/Catalog/Test/Mftf/ActionGroup/AssertStorefrontProductIsPresentOnCategoryPageActionGroup.xml new file mode 100644 index 0000000000000..7c10be74b1b79 --- /dev/null +++ b/app/code/Magento/Catalog/Test/Mftf/ActionGroup/AssertStorefrontProductIsPresentOnCategoryPageActionGroup.xml @@ -0,0 +1,21 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + /** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +--> + +<actionGroups xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/actionGroupSchema.xsd"> + <actionGroup name="AssertStorefrontProductIsPresentOnCategoryPageActionGroup"> + <annotations> + <description>Validate that the provided Product is present and has correct name on a Category page.</description> + </annotations> + <arguments> + <argument name="productName" type="string" defaultValue="{{ApiSimpleOne.name}}"/> + </arguments> + + <waitForElementVisible selector="{{StorefrontCategoryProductSection.ProductTitleByName(productName)}}" stepKey="assertProductName"/> + </actionGroup> +</actionGroups> diff --git a/app/code/Magento/Catalog/Test/Mftf/ActionGroup/AssertStorefrontProductPriceInCategoryPageActionGroup.xml b/app/code/Magento/Catalog/Test/Mftf/ActionGroup/AssertStorefrontProductPriceInCategoryPageActionGroup.xml new file mode 100644 index 0000000000000..87f6e6e705263 --- /dev/null +++ b/app/code/Magento/Catalog/Test/Mftf/ActionGroup/AssertStorefrontProductPriceInCategoryPageActionGroup.xml @@ -0,0 +1,25 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + /** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +--> + +<actionGroups xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/actionGroupSchema.xsd"> + <actionGroup name="AssertStorefrontProductPriceInCategoryPageActionGroup"> + <annotations> + <description>Goes to Storefront Category page for the provided Category. Validates that the Product price is present and correct.</description> + </annotations> + <arguments> + <argument name="categoryUrl" type="string" defaultValue="{{SimpleRootSubCategory.url_key}}"/> + <argument name="productName" type="string" defaultValue="{{productWithHTMLEntityOne.name}}"/> + <argument name="productPrice" type="string" defaultValue="{{productWithHTMLEntityOne.price}}"/> + </arguments> + + <amOnPage url="{{StorefrontCategoryPage.url(categoryUrl)}}" stepKey="navigateToCategoryPage"/> + <waitForPageLoad stepKey="waitForProductPageLoad"/> + <see userInput="{{productPrice}}" selector="{{StorefrontCategoryProductSection.ProductPriceByName(productName)}}" stepKey="assertProductPrice"/> + </actionGroup> +</actionGroups> diff --git a/app/code/Magento/Catalog/Test/Mftf/ActionGroup/AssertStorefrontProductSpecialPriceInCategoryPageActionGroup.xml b/app/code/Magento/Catalog/Test/Mftf/ActionGroup/AssertStorefrontProductSpecialPriceInCategoryPageActionGroup.xml new file mode 100644 index 0000000000000..b2a7d5f96ec94 --- /dev/null +++ b/app/code/Magento/Catalog/Test/Mftf/ActionGroup/AssertStorefrontProductSpecialPriceInCategoryPageActionGroup.xml @@ -0,0 +1,21 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + /** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +--> + +<actionGroups xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/actionGroupSchema.xsd"> + <actionGroup name="AssertStorefrontProductSpecialPriceInCategoryPageActionGroup" extends="AssertStorefrontProductPriceInCategoryPageActionGroup"> + <annotations> + <description>Goes to Storefront Category page for the provided Category. Validates that the Product price and special price are correct.</description> + </annotations> + <arguments> + <argument name="productSpecialPrice" type="string" defaultValue="{{updateVirtualProductSpecialPrice.special_price}}"/> + </arguments> + + <see userInput="{{productSpecialPrice}}" selector="{{StorefrontCategoryProductSection.ProductCatalogRuleSpecialPriceTitleByName(productName)}}" after="assertProductPrice" stepKey="assertProductSpecialPrice"/> + </actionGroup> +</actionGroups> diff --git a/app/code/Magento/Catalog/Test/Mftf/ActionGroup/ChangeSeoUrlKeyActionGroup.xml b/app/code/Magento/Catalog/Test/Mftf/ActionGroup/ChangeSeoUrlKeyActionGroup.xml index 7107cc2a560d1..cf2e809fefa5e 100644 --- a/app/code/Magento/Catalog/Test/Mftf/ActionGroup/ChangeSeoUrlKeyActionGroup.xml +++ b/app/code/Magento/Catalog/Test/Mftf/ActionGroup/ChangeSeoUrlKeyActionGroup.xml @@ -16,7 +16,7 @@ <argument name="value" type="string"/> </arguments> - <click selector="{{AdminCategorySEOSection.SectionHeader}}" stepKey="openSeoSection"/> + <conditionalClick selector="{{AdminCategorySEOSection.SectionHeader}}" dependentSelector="{{AdminCategorySEOSection.sectionBody}}" visible="false" stepKey="openSeoSection"/> <fillField selector="{{AdminCategorySEOSection.UrlKeyInput}}" userInput="{{value}}" stepKey="enterURLKey"/> <click selector="{{AdminCategoryMainActionsSection.SaveButton}}" stepKey="saveCategory"/> <seeElement selector="{{AdminCategoryMessagesSection.SuccessMessage}}" stepKey="assertSuccessMessage"/> diff --git a/app/code/Magento/Catalog/Test/Mftf/ActionGroup/ChangeSeoUrlKeyForSubCategoryActionGroup.xml b/app/code/Magento/Catalog/Test/Mftf/ActionGroup/ChangeSeoUrlKeyForSubCategoryActionGroup.xml index 42813aef05be5..a65bb297971c7 100644 --- a/app/code/Magento/Catalog/Test/Mftf/ActionGroup/ChangeSeoUrlKeyForSubCategoryActionGroup.xml +++ b/app/code/Magento/Catalog/Test/Mftf/ActionGroup/ChangeSeoUrlKeyForSubCategoryActionGroup.xml @@ -16,7 +16,7 @@ <argument name="value" type="string"/> </arguments> - <click selector="{{AdminCategorySEOSection.SectionHeader}}" stepKey="openSeoSection"/> + <conditionalClick selector="{{AdminCategorySEOSection.SectionHeader}}" dependentSelector="{{AdminCategorySEOSection.sectionBody}}" visible="false" stepKey="openSeoSection"/> <uncheckOption selector="{{AdminCategorySEOSection.UrlKeyDefaultValueCheckbox}}" stepKey="uncheckDefaultValue"/> <fillField selector="{{AdminCategorySEOSection.UrlKeyInput}}" userInput="{{value}}" stepKey="enterURLKey"/> <click selector="{{AdminCategoryMainActionsSection.SaveButton}}" stepKey="saveCategory"/> diff --git a/app/code/Magento/Catalog/Test/Mftf/ActionGroup/CheckAdminProductGridColumnOptionActionGroup.xml b/app/code/Magento/Catalog/Test/Mftf/ActionGroup/CheckAdminProductGridColumnOptionActionGroup.xml new file mode 100644 index 0000000000000..dc10933150617 --- /dev/null +++ b/app/code/Magento/Catalog/Test/Mftf/ActionGroup/CheckAdminProductGridColumnOptionActionGroup.xml @@ -0,0 +1,21 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + /** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +--> + +<actionGroups xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/actionGroupSchema.xsd"> + <actionGroup name="CheckAdminProductGridColumnOptionActionGroup"> + <annotations> + <description>Checks Admin Product Grid 'Columns' option.</description> + </annotations> + <arguments> + <argument name="optionName" type="string" defaultValue="Name"/> + </arguments> + + <checkOption selector="{{AdminProductGridFilterSection.viewColumnOption(optionName)}}" stepKey="checkColumn"/> + </actionGroup> +</actionGroups> diff --git a/app/code/Magento/Catalog/Test/Mftf/ActionGroup/CheckCustomizableOptionImportActionGroup.xml b/app/code/Magento/Catalog/Test/Mftf/ActionGroup/CheckCustomizableOptionImportActionGroup.xml index efd0986efca06..d2d03600afbbd 100644 --- a/app/code/Magento/Catalog/Test/Mftf/ActionGroup/CheckCustomizableOptionImportActionGroup.xml +++ b/app/code/Magento/Catalog/Test/Mftf/ActionGroup/CheckCustomizableOptionImportActionGroup.xml @@ -20,8 +20,17 @@ <grabValueFrom selector="{{AdminProductCustomizableOptionsSection.optionTitleInput(optionIndex)}}" stepKey="grabOptionTitle"/> <grabValueFrom selector="{{AdminProductCustomizableOptionsSection.optionPrice(optionIndex)}}" stepKey="grabOptionPrice"/> <grabValueFrom selector="{{AdminProductCustomizableOptionsSection.optionSku(optionIndex)}}" stepKey="grabOptionSku"/> - <assertEquals expected="{{option.title}}" expectedType="string" actual="$grabOptionTitle" stepKey="assertOptionTitle"/> - <assertEquals expected="{{option.price}}" expectedType="string" actual="$grabOptionPrice" stepKey="assertOptionPrice"/> - <assertEquals expected="{{option.title}}" expectedType="string" actual="$grabOptionSku" stepKey="assertOptionSku"/> + <assertEquals stepKey="assertOptionTitle"> + <actualResult type="const">$grabOptionTitle</actualResult> + <expectedResult type="string">{{option.title}}</expectedResult> + </assertEquals> + <assertEquals stepKey="assertOptionPrice"> + <actualResult type="const">$grabOptionPrice</actualResult> + <expectedResult type="string">{{option.price}}</expectedResult> + </assertEquals> + <assertEquals stepKey="assertOptionSku"> + <actualResult type="const">$grabOptionSku</actualResult> + <expectedResult type="string">{{option.title}}</expectedResult> + </assertEquals> </actionGroup> </actionGroups> diff --git a/app/code/Magento/Catalog/Test/Mftf/ActionGroup/CheckProductsOrderActionGroup.xml b/app/code/Magento/Catalog/Test/Mftf/ActionGroup/CheckProductsOrderActionGroup.xml index 6f58299fe1446..d804958caad44 100644 --- a/app/code/Magento/Catalog/Test/Mftf/ActionGroup/CheckProductsOrderActionGroup.xml +++ b/app/code/Magento/Catalog/Test/Mftf/ActionGroup/CheckProductsOrderActionGroup.xml @@ -25,8 +25,14 @@ <waitForPageLoad stepKey="waitForPageLoad5AfterCacheCleared" time="60"/> <waitForElement selector="{{StorefrontCategoryProductSection.ProductImageByNumber('1')}}" time="120" stepKey="waitCompareWidgetLoad" /> <grabAttributeFrom selector="{{StorefrontCategoryProductSection.ProductImageByNumber('1')}}" userInput="alt" stepKey="grabFirstProductName1_1"/> - <assertEquals expected="{{product_1.name}}" actual="($grabFirstProductName1_1)" message="notExpectedOrder" stepKey="compare1"/> + <assertEquals message="notExpectedOrder" stepKey="compare1"> + <actualResult type="const">($grabFirstProductName1_1)</actualResult> + <expectedResult type="const">{{product_1.name}}</expectedResult> + </assertEquals> <grabAttributeFrom selector="{{StorefrontCategoryProductSection.ProductImageByNumber('2')}}" userInput="alt" stepKey="grabFirstProductName2_2"/> - <assertEquals expected="{{product_2.name}}" actual="($grabFirstProductName2_2)" message="notExpectedOrder" stepKey="compare2"/> + <assertEquals message="notExpectedOrder" stepKey="compare2"> + <actualResult type="const">($grabFirstProductName2_2)</actualResult> + <expectedResult type="const">{{product_2.name}}</expectedResult> + </assertEquals> </actionGroup> </actionGroups> diff --git a/app/code/Magento/Catalog/Test/Mftf/ActionGroup/ClearFiltersAdminProductGridActionGroup.xml b/app/code/Magento/Catalog/Test/Mftf/ActionGroup/ClearFiltersAdminProductGridActionGroup.xml new file mode 100644 index 0000000000000..fb75b65120287 --- /dev/null +++ b/app/code/Magento/Catalog/Test/Mftf/ActionGroup/ClearFiltersAdminProductGridActionGroup.xml @@ -0,0 +1,19 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + /** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +--> + +<actionGroups xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/actionGroupSchema.xsd"> + <actionGroup name="ClearFiltersAdminProductGridActionGroup"> + <annotations> + <description>Clicks on 'Clear Filters'.</description> + </annotations> + + <conditionalClick selector="{{AdminProductGridFilterSection.clearFilters}}" dependentSelector="{{AdminProductGridFilterSection.clearFilters}}" visible="true" stepKey="clickClearFilters"/> + <waitForPageLoad stepKey="waitForGridLoad"/> + </actionGroup> +</actionGroups> diff --git a/app/code/Magento/Catalog/Test/Mftf/ActionGroup/DeleteDefaultCategoryChildrenActionGroup.xml b/app/code/Magento/Catalog/Test/Mftf/ActionGroup/DeleteDefaultCategoryChildrenActionGroup.xml deleted file mode 100644 index 2fb4e0e05887a..0000000000000 --- a/app/code/Magento/Catalog/Test/Mftf/ActionGroup/DeleteDefaultCategoryChildrenActionGroup.xml +++ /dev/null @@ -1,35 +0,0 @@ -<?xml version="1.0" encoding="UTF-8"?> -<!-- - /** - * Copyright © Magento, Inc. All rights reserved. - * See COPYING.txt for license details. - */ ---> - -<actionGroups xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" - xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/actionGroupSchema.xsd"> - <actionGroup name="DeleteDefaultCategoryChildrenActionGroup"> - <annotations> - <description>Deletes all children categories of Default Root Category.</description> - </annotations> - - <amOnPage url="{{AdminCategoryPage.url}}" stepKey="navigateToAdminCategoryPage"/> - <executeInSelenium function="function ($webdriver) use ($I) { - $children = $webdriver->findElements(\Facebook\WebDriver\WebDriverBy::xpath('//ul[contains(@class, \'x-tree-node-ct\')]/li[@class=\'x-tree-node\' and contains(., - \'{{DefaultCategory.name}}\')]/ul[contains(@class, \'x-tree-node-ct\')]/li//a')); - while (!empty($children)) { - $I->click('//ul[contains(@class, \'x-tree-node-ct\')]/li[@class=\'x-tree-node\' and contains(., - \'{{DefaultCategory.name}}\')]/ul[contains(@class, \'x-tree-node-ct\')]/li//a'); - $I->waitForPageLoad(30); - $I->click('#delete'); - $I->waitForElementVisible('aside.confirm .modal-footer button.action-accept'); - $I->click('aside.confirm .modal-footer button.action-accept'); - $I->waitForPageLoad(30); - $I->waitForElementVisible('#messages div.message-success', 30); - $I->see('You deleted the category.', '#messages div.message-success'); - $children = $webdriver->findElements(\Facebook\WebDriver\WebDriverBy::xpath('//ul[contains(@class, \'x-tree-node-ct\')]/li[@class=\'x-tree-node\' and contains(., - \'{{DefaultCategory.name}}\')]/ul[contains(@class, \'x-tree-node-ct\')]/li//a')); - } - }" stepKey="deleteAllChildCategories"/> - </actionGroup> -</actionGroups> diff --git a/app/code/Magento/Catalog/Test/Mftf/ActionGroup/DeleteProductActionGroup.xml b/app/code/Magento/Catalog/Test/Mftf/ActionGroup/DeleteProductActionGroup.xml index aa44a517593af..22209b61d5316 100644 --- a/app/code/Magento/Catalog/Test/Mftf/ActionGroup/DeleteProductActionGroup.xml +++ b/app/code/Magento/Catalog/Test/Mftf/ActionGroup/DeleteProductActionGroup.xml @@ -27,5 +27,6 @@ <waitForPageLoad stepKey="waitForDeleteItemPopup" time="10"/> <click stepKey="clickOnOk" selector="{{ProductsPageSection.ok}}"/> <waitForElementVisible stepKey="waitForSuccessfullyDeletedMessage" selector="{{ProductsPageSection.deletedSuccessMessage}}" time="10"/> + <waitForLoadingMaskToDisappear stepKey="waitForLoadingMaskDisappear"/> </actionGroup> </actionGroups> diff --git a/app/code/Magento/Catalog/Test/Mftf/ActionGroup/DeleteProductAttributeByLabelActionGroup.xml b/app/code/Magento/Catalog/Test/Mftf/ActionGroup/DeleteProductAttributeByLabelActionGroup.xml deleted file mode 100644 index fb78909eab0b6..0000000000000 --- a/app/code/Magento/Catalog/Test/Mftf/ActionGroup/DeleteProductAttributeByLabelActionGroup.xml +++ /dev/null @@ -1,29 +0,0 @@ -<?xml version="1.0" encoding="UTF-8"?> -<!-- - /** - * Copyright © Magento, Inc. All rights reserved. - * See COPYING.txt for license details. - */ ---> - -<actionGroups xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" - xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/actionGroupSchema.xsd"> - <actionGroup name="DeleteProductAttributeByLabelActionGroup"> - <annotations> - <description>Goes to the Admin Product Attributes grid page. Filters the grid for the provided Product Attribute (Label). Deletes the Product Attribute from the grid. Validates that the Success Message is present.</description> - </annotations> - <arguments> - <argument name="ProductAttribute"/> - </arguments> - - <amOnPage url="{{AdminProductAttributeGridPage.url}}" stepKey="navigateToProductAttributeGrid"/> - <fillField selector="{{AdminProductAttributeGridSection.FilterByAttributeCode}}" userInput="{{ProductAttribute.default_label}}" stepKey="setAttributeCode"/> - <click selector="{{AdminProductAttributeGridSection.Search}}" stepKey="searchForAttributeFromTheGrid"/> - <click selector="{{AdminProductAttributeGridSection.FirstRow}}" stepKey="clickOnAttributeRow"/> - <waitForPageLoad stepKey="waitForPageLoad"/> - <click selector="{{AttributePropertiesSection.DeleteAttribute}}" stepKey="deleteAttribute"/> - <click selector="{{ModalConfirmationSection.OkButton}}" stepKey="ClickOnDeleteButton"/> - <waitForPageLoad stepKey="waitForPageLoad1"/> - <seeElement selector="{{AdminProductMessagesSection.successMessage}}" stepKey="waitForSuccessMessage"/> - </actionGroup> -</actionGroups> diff --git a/app/code/Magento/Catalog/Test/Mftf/ActionGroup/FillAdminSimpleProductFormActionGroup.xml b/app/code/Magento/Catalog/Test/Mftf/ActionGroup/FillAdminSimpleProductFormActionGroup.xml index 2abfc546e6bb3..ef4f30e353946 100644 --- a/app/code/Magento/Catalog/Test/Mftf/ActionGroup/FillAdminSimpleProductFormActionGroup.xml +++ b/app/code/Magento/Catalog/Test/Mftf/ActionGroup/FillAdminSimpleProductFormActionGroup.xml @@ -24,6 +24,7 @@ <fillField userInput="{{simpleProduct.sku}}" selector="{{AdminProductFormSection.productSku}}" stepKey="fillSKU"/> <fillField userInput="{{simpleProduct.price}}" selector="{{AdminProductFormSection.productPrice}}" stepKey="fillPrice"/> <fillField userInput="{{simpleProduct.quantity}}" selector="{{AdminProductFormSection.productQuantity}}" stepKey="fillQuantity"/> + <selectOption userInput="{{simpleProduct.visibility}}" selector="{{AdminProductFormSection.visibility}}" stepKey="fillVisibility"/> <searchAndMultiSelectOption selector="{{AdminProductFormSection.categoriesDropdown}}" parameterArray="[{{category.name}}]" stepKey="searchAndSelectCategory"/> <click selector="{{AdminProductSEOSection.sectionHeader}}" stepKey="openSeoSection"/> <fillField userInput="{{simpleProduct.urlKey}}" selector="{{AdminProductSEOSection.urlKeyInput}}" stepKey="fillUrlKey"/> diff --git a/app/code/Magento/Catalog/Test/Mftf/ActionGroup/GoToProductPageViaIDActionGroup.xml b/app/code/Magento/Catalog/Test/Mftf/ActionGroup/GoToProductPageViaIDActionGroup.xml deleted file mode 100644 index 104ef83771e9d..0000000000000 --- a/app/code/Magento/Catalog/Test/Mftf/ActionGroup/GoToProductPageViaIDActionGroup.xml +++ /dev/null @@ -1,21 +0,0 @@ -<?xml version="1.0" encoding="UTF-8"?> -<!-- - /** - * Copyright © Magento, Inc. All rights reserved. - * See COPYING.txt for license details. - */ ---> - -<actionGroups xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" - xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/actionGroupSchema.xsd"> - <actionGroup name="GoToProductPageViaIDActionGroup"> - <annotations> - <description>Goes to the Product edit page for the provided Product ID.</description> - </annotations> - <arguments> - <argument name="productId" type="string"/> - </arguments> - - <amOnPage url="{{AdminProductEditPage.url(productId)}}" stepKey="goToProduct"/> - </actionGroup> -</actionGroups> diff --git a/app/code/Magento/Catalog/Test/Mftf/ActionGroup/ProductSetAdvancedPricingActionGroup.xml b/app/code/Magento/Catalog/Test/Mftf/ActionGroup/ProductSetAdvancedPricingActionGroup.xml index 95bda64202159..0f7145b607f6e 100644 --- a/app/code/Magento/Catalog/Test/Mftf/ActionGroup/ProductSetAdvancedPricingActionGroup.xml +++ b/app/code/Magento/Catalog/Test/Mftf/ActionGroup/ProductSetAdvancedPricingActionGroup.xml @@ -21,7 +21,7 @@ </arguments> <click selector="{{AdminProductFormSection.advancedPricingLink}}" stepKey="clickOnAdvancedPricingButton"/> - <waitForElement selector="{{AdminProductFormAdvancedPricingSection.customerGroupPriceAddButton}}" stepKey="waitForCustomerGroupPriceAddButton"/> + <waitForElementVisible selector="{{AdminProductFormAdvancedPricingSection.customerGroupPriceAddButton}}" stepKey="waitForCustomerGroupPriceAddButton"/> <click selector="{{AdminProductFormAdvancedPricingSection.customerGroupPriceAddButton}}" stepKey="addCustomerGroupAllGroupsQty1PriceDiscountAnd10percent"/> <waitForElement selector="{{AdminProductFormAdvancedPricingSection.productTierPriceCustGroupSelect('0')}}" stepKey="waitForSelectCustomerGroupNameAttribute2"/> <selectOption selector="{{AdminProductFormAdvancedPricingSection.productTierPriceWebsiteSelect('0')}}" userInput="{{website}}" stepKey="selectProductWebsiteValue"/> diff --git a/app/code/Magento/Catalog/Test/Mftf/ActionGroup/ResetAdminProductGridColumnsActionGroup.xml b/app/code/Magento/Catalog/Test/Mftf/ActionGroup/ResetAdminProductGridColumnsActionGroup.xml new file mode 100644 index 0000000000000..8e10da66a83af --- /dev/null +++ b/app/code/Magento/Catalog/Test/Mftf/ActionGroup/ResetAdminProductGridColumnsActionGroup.xml @@ -0,0 +1,18 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + /** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +--> + +<actionGroups xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/actionGroupSchema.xsd"> + <actionGroup name="ResetAdminProductGridColumnsActionGroup"> + <annotations> + <description>Clicks 'reset' for Admin Product Grid 'Columns' dropdown.</description> + </annotations> + + <click selector="{{AdminProductGridFilterSection.resetGridColumns}}" stepKey="resetProductGridColumns"/> + </actionGroup> +</actionGroups> diff --git a/app/code/Magento/Catalog/Test/Mftf/ActionGroup/SaveCategoryFormActionGroup.xml b/app/code/Magento/Catalog/Test/Mftf/ActionGroup/SaveCategoryFormActionGroup.xml deleted file mode 100644 index ff6afb4aaf0e9..0000000000000 --- a/app/code/Magento/Catalog/Test/Mftf/ActionGroup/SaveCategoryFormActionGroup.xml +++ /dev/null @@ -1,20 +0,0 @@ -<?xml version="1.0" encoding="UTF-8"?> -<!-- - /** - * Copyright © Magento, Inc. All rights reserved. - * See COPYING.txt for license details. - */ ---> - -<actionGroups xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" - xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/actionGroupSchema.xsd"> - <actionGroup name="SaveCategoryFormActionGroup"> - <annotations> - <description>Requires navigation to the Category creation/edit page. Checks that the url contains the AdminCategoryPage url. Saves the Category.</description> - </annotations> - - <seeInCurrentUrl url="{{AdminCategoryPage.url}}" stepKey="seeOnCategoryPage"/> - <click selector="{{AdminCategoryMainActionsSection.SaveButton}}" stepKey="saveCategory"/> - <seeElement selector="{{AdminCategoryMessagesSection.SuccessMessage}}" stepKey="assertSuccess"/> - </actionGroup> -</actionGroups> diff --git a/app/code/Magento/Catalog/Test/Mftf/ActionGroup/SetProductUrlKeyByStringActionGroup.xml b/app/code/Magento/Catalog/Test/Mftf/ActionGroup/SetProductUrlKeyByStringActionGroup.xml index d4c654523a40b..1882063081f04 100644 --- a/app/code/Magento/Catalog/Test/Mftf/ActionGroup/SetProductUrlKeyByStringActionGroup.xml +++ b/app/code/Magento/Catalog/Test/Mftf/ActionGroup/SetProductUrlKeyByStringActionGroup.xml @@ -16,7 +16,7 @@ <argument name="urlKey" type="string"/> </arguments> - <click selector="{{AdminProductSEOSection.sectionHeader}}" stepKey="openSeoSection"/> + <conditionalClick selector="{{AdminProductSEOSection.sectionHeader}}" dependentSelector="{{AdminProductSEOSection.urlKeyInput}}" visible="false" stepKey="openSeoSection"/> <fillField userInput="{{urlKey}}" selector="{{AdminProductSEOSection.urlKeyInput}}" stepKey="fillUrlKey"/> </actionGroup> </actionGroups> diff --git a/app/code/Magento/Catalog/Test/Mftf/ActionGroup/StorefrontAssertNotExistProductInRecentlyComparedWidgetActionGroup.xml b/app/code/Magento/Catalog/Test/Mftf/ActionGroup/StorefrontAssertNotExistProductInRecentlyComparedWidgetActionGroup.xml new file mode 100644 index 0000000000000..800685861ea3b --- /dev/null +++ b/app/code/Magento/Catalog/Test/Mftf/ActionGroup/StorefrontAssertNotExistProductInRecentlyComparedWidgetActionGroup.xml @@ -0,0 +1,21 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + /** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +--> +<actionGroups xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/actionGroupSchema.xsd"> + <!-- Check the product not exist in recently compared widget --> + <actionGroup name="StorefrontAssertNotExistProductInRecentlyComparedWidgetActionGroup"> + <annotations> + <description>Validate that the provided Product does not appear in the Recently Compared Products widget.</description> + </annotations> + <arguments> + <argument name="product"/> + </arguments> + + <waitForElementVisible selector="{{StorefrontWidgetsSection.widgetRecentlyComparedProductsGrid}}" stepKey="waitLoadingRecentlyComparedProductsGrid"/> + <dontSee selector="{{StorefrontWidgetsSection.widgetRecentlyComparedProductsGrid}}" userInput="{{product.name}}" stepKey="dontSeeProductInRecentlyComparedWidget"/> + </actionGroup> +</actionGroups> diff --git a/app/code/Magento/Catalog/Test/Mftf/ActionGroup/StorefrontAssertProductAbsentOnCategoryPageActionGroup.xml b/app/code/Magento/Catalog/Test/Mftf/ActionGroup/StorefrontAssertProductAbsentOnCategoryPageActionGroup.xml deleted file mode 100644 index 4c641b621a504..0000000000000 --- a/app/code/Magento/Catalog/Test/Mftf/ActionGroup/StorefrontAssertProductAbsentOnCategoryPageActionGroup.xml +++ /dev/null @@ -1,24 +0,0 @@ -<?xml version="1.0" encoding="UTF-8"?> -<!-- - /** - * Copyright © Magento, Inc. All rights reserved. - * See COPYING.txt for license details. - */ ---> - -<actionGroups xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" - xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/actionGroupSchema.xsd"> - <actionGroup name="StorefrontAssertProductAbsentOnCategoryPageActionGroup"> - <annotations> - <description>Navigate to category page and verify product is absent.</description> - </annotations> - <arguments> - <argument name="category" defaultValue="_defaultCategory"/> - <argument name="product" defaultValue="SimpleProduct"/> - </arguments> - <amOnPage url="{{StorefrontCategoryPage.url(category.name)}}" stepKey="navigateToCategoryPage"/> - <waitForPageLoad stepKey="waitForCategoryPageLoad"/> - <dontSee selector="{{StorefrontCategoryMainSection.productName}}" userInput="{{product.name}}" stepKey="assertProductIsNotPresent"/> - <dontSee selector="{{StorefrontCategoryMainSection.productPrice}}" userInput="{{product.price}}" stepKey="assertProductIsNotPricePresent"/> - </actionGroup> -</actionGroups> diff --git a/app/code/Magento/Catalog/Test/Mftf/ActionGroup/StorefrontAssertProductSpecialPriceOnProductPageActionGroup.xml b/app/code/Magento/Catalog/Test/Mftf/ActionGroup/StorefrontAssertProductSpecialPriceOnProductPageActionGroup.xml index cb2753fa64fc6..39184c1c37cc9 100644 --- a/app/code/Magento/Catalog/Test/Mftf/ActionGroup/StorefrontAssertProductSpecialPriceOnProductPageActionGroup.xml +++ b/app/code/Magento/Catalog/Test/Mftf/ActionGroup/StorefrontAssertProductSpecialPriceOnProductPageActionGroup.xml @@ -20,6 +20,9 @@ <waitForPageLoad stepKey="waitForFirstProductPage"/> <waitForElementVisible selector="{{StorefrontProductInfoMainSection.specialPriceValue}}" stepKey="waitForProductSpecialPrice"/> <grabTextFrom selector="{{StorefrontProductInfoMainSection.specialPriceValue}}" stepKey="grabProductSpecialPrice"/> - <assertEquals actual="$grabProductSpecialPrice" expectedType="string" expected="{{specialPrice}}" stepKey="assertProductPriceValuesAreEqual"/> + <assertEquals stepKey="assertProductPriceValuesAreEqual"> + <actualResult type="const">$grabProductSpecialPrice</actualResult> + <expectedResult type="string">{{specialPrice}}</expectedResult> + </assertEquals> </actionGroup> </actionGroups> diff --git a/app/code/Magento/Catalog/Test/Mftf/ActionGroup/StorefrontCheckCategorySimpleProductActionGroup.xml b/app/code/Magento/Catalog/Test/Mftf/ActionGroup/StorefrontCheckCategorySimpleProductActionGroup.xml index 1f8234498ffa7..b8bc3197d1a11 100644 --- a/app/code/Magento/Catalog/Test/Mftf/ActionGroup/StorefrontCheckCategorySimpleProductActionGroup.xml +++ b/app/code/Magento/Catalog/Test/Mftf/ActionGroup/StorefrontCheckCategorySimpleProductActionGroup.xml @@ -16,11 +16,10 @@ <argument name="product"/> </arguments> + <waitForElementVisible selector="{{StorefrontCategoryProductSection.ProductTitleByName(product.name)}}" stepKey="waitForProduct"/> <seeElement selector="{{StorefrontCategoryProductSection.ProductTitleByName(product.name)}}" stepKey="assertProductName"/> <see userInput="${{product.price}}.00" selector="{{StorefrontCategoryProductSection.ProductPriceByName(product.name)}}" stepKey="AssertProductPrice"/> - <!-- @TODO: MAGETWO-80272 Move to Magento_Checkout --> <moveMouseOver selector="{{StorefrontCategoryProductSection.ProductInfoByName(product.name)}}" stepKey="moveMouseOverProduct"/> - <!-- @TODO: MAGETWO-80272 Move to Magento_Checkout --> <seeElement selector="{{StorefrontCategoryProductSection.ProductAddToCartByName(product.name)}}" stepKey="AssertAddToCart"/> </actionGroup> </actionGroups> diff --git a/app/code/Magento/Catalog/Test/Mftf/ActionGroup/StorefrontNavigateCategoryPageActionGroup.xml b/app/code/Magento/Catalog/Test/Mftf/ActionGroup/StorefrontNavigateCategoryPageActionGroup.xml index 39cb9ef1a63d4..dab5142c557ca 100644 --- a/app/code/Magento/Catalog/Test/Mftf/ActionGroup/StorefrontNavigateCategoryPageActionGroup.xml +++ b/app/code/Magento/Catalog/Test/Mftf/ActionGroup/StorefrontNavigateCategoryPageActionGroup.xml @@ -13,5 +13,6 @@ </arguments> <!-- Open category page on storefront --> <amOnPage url="{{StorefrontCategoryPage.url(category.custom_attributes[url_key])}}" stepKey="navigateStorefrontCategoryPage"/> + <waitForPageLoad stepKey="waitForCategoryPageLoad"/> </actionGroup> </actionGroups> diff --git a/app/code/Magento/Catalog/Test/Mftf/ActionGroup/StorefrontOpenProductEntityPageActionGroup.xml b/app/code/Magento/Catalog/Test/Mftf/ActionGroup/StorefrontOpenProductEntityPageActionGroup.xml new file mode 100644 index 0000000000000..88dcaff646799 --- /dev/null +++ b/app/code/Magento/Catalog/Test/Mftf/ActionGroup/StorefrontOpenProductEntityPageActionGroup.xml @@ -0,0 +1,21 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. +--> + +<actionGroups xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/actionGroupSchema.xsd"> + <actionGroup name="StorefrontOpenProductEntityPageActionGroup"> + <annotations> + <description>Opens Storefront Product page for the provided Product Entity</description> + </annotations> + <arguments> + <argument name="product" type="entity"/> + </arguments> + + <amOnPage url="{{StorefrontProductPage.url(product.custom_attributes[url_key])}}" stepKey="goToProductPage"/> + <waitForPageLoad stepKey="waitForProductPageLoaded"/> + </actionGroup> +</actionGroups> diff --git a/app/code/Magento/Catalog/Test/Mftf/ActionGroup/StorefrontOpenProductFromCategoryPageActionGroup.xml b/app/code/Magento/Catalog/Test/Mftf/ActionGroup/StorefrontOpenProductFromCategoryPageActionGroup.xml new file mode 100644 index 0000000000000..39bffe006d987 --- /dev/null +++ b/app/code/Magento/Catalog/Test/Mftf/ActionGroup/StorefrontOpenProductFromCategoryPageActionGroup.xml @@ -0,0 +1,21 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. +--> + +<actionGroups xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/actionGroupSchema.xsd"> + <actionGroup name="StorefrontOpenProductFromCategoryPageActionGroup"> + <annotations> + <description>Click on the provided product on category page.</description> + </annotations> + <arguments> + <argument name="productName" type="string" defaultValue="{{ApiSimpleProduct.name}}"/> + </arguments> + + <click selector="{{StorefrontCategoryMainSection.specifiedProductItemInfo(productName)}}" stepKey="openProductPage"/> + <waitForPageLoad stepKey="waitForProductPageLoad"/> + </actionGroup> +</actionGroups> diff --git a/app/code/Magento/Catalog/Test/Mftf/ActionGroup/StorefrontProductPageCloseFullscreenGalleryActionGroup.xml b/app/code/Magento/Catalog/Test/Mftf/ActionGroup/StorefrontProductPageCloseFullscreenGalleryActionGroup.xml new file mode 100644 index 0000000000000..d7dde159f4bdd --- /dev/null +++ b/app/code/Magento/Catalog/Test/Mftf/ActionGroup/StorefrontProductPageCloseFullscreenGalleryActionGroup.xml @@ -0,0 +1,18 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. +--> + +<actionGroups xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/actionGroupSchema.xsd"> + <actionGroup name="StorefrontProductPageCloseFullscreenGalleryActionGroup"> + <annotations> + <description>Closes a product image gallery full-screen page.</description> + </annotations> + + <click selector="{{StorefrontProductMediaSection.closeFullscreenImage}}" stepKey="closeFullScreenPage"/> + <waitForPageLoad stepKey="waitsForCloseFullScreenPage"/> + </actionGroup> +</actionGroups> diff --git a/app/code/Magento/Catalog/Test/Mftf/ActionGroup/StorefrontProductPageOpenImageFullscreenActionGroup.xml b/app/code/Magento/Catalog/Test/Mftf/ActionGroup/StorefrontProductPageOpenImageFullscreenActionGroup.xml new file mode 100644 index 0000000000000..14bd47a234fd2 --- /dev/null +++ b/app/code/Magento/Catalog/Test/Mftf/ActionGroup/StorefrontProductPageOpenImageFullscreenActionGroup.xml @@ -0,0 +1,22 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. +--> + +<actionGroups xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/actionGroupSchema.xsd"> + <actionGroup name="StorefrontProductPageOpenImageFullscreenActionGroup"> + <annotations> + <description>Finds image of the product in thumbnails and open a full-screen review.</description> + </annotations> + <arguments> + <argument name="imageNumber" type="string" defaultValue="2"/> + </arguments> + + <waitForElementVisible selector="{{StorefrontProductMediaSection.fotoramaImageThumbnail(imageNumber)}}" stepKey="waitThumbnailAppears"/> + <conditionalClick selector="{{StorefrontProductMediaSection.fotoramaImageThumbnail(imageNumber)}}" dependentSelector="{{StorefrontProductMediaSection.fotoramaImageThumbnailActive(imageNumber)}}" visible="false" stepKey="clickOnThumbnailImage"/> + <click selector="{{StorefrontProductMediaSection.gallery}}" stepKey="openFullScreenPage"/> + </actionGroup> +</actionGroups> diff --git a/app/code/Magento/Catalog/Test/Mftf/ActionGroup/StorefrontProductPageSelectDropDownOptionValueActionGroup.xml b/app/code/Magento/Catalog/Test/Mftf/ActionGroup/StorefrontProductPageSelectDropDownOptionValueActionGroup.xml new file mode 100644 index 0000000000000..31b18e1f0d37e --- /dev/null +++ b/app/code/Magento/Catalog/Test/Mftf/ActionGroup/StorefrontProductPageSelectDropDownOptionValueActionGroup.xml @@ -0,0 +1,22 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + /** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +--> + +<actionGroups xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/actionGroupSchema.xsd"> + <actionGroup name="StorefrontProductPageSelectDropDownOptionValueActionGroup"> + <annotations> + <description>Selects the provided Product Option Value under the provided DropDown Product Option Title on a Storefront Product page.</description> + </annotations> + <arguments> + <argument name="attributeLabel" type="string" defaultValue="{{ProductAttributeFrontendLabel.label}}"/> + <argument name="optionLabel" type="string" defaultValue="{{productAttributeOption1.label}}"/> + </arguments> + + <selectOption selector="{{StorefrontProductInfoMainSection.productOptionSelect(attributeLabel)}}" userInput="{{optionLabel}}" stepKey="fillDropDownAttributeOption"/> + </actionGroup> +</actionGroups> diff --git a/app/code/Magento/Catalog/Test/Mftf/ActionGroup/StorefrontProductPageSelectRadioButtonOptionValueActionGroup.xml b/app/code/Magento/Catalog/Test/Mftf/ActionGroup/StorefrontProductPageSelectRadioButtonOptionValueActionGroup.xml new file mode 100644 index 0000000000000..5f2a130956cbe --- /dev/null +++ b/app/code/Magento/Catalog/Test/Mftf/ActionGroup/StorefrontProductPageSelectRadioButtonOptionValueActionGroup.xml @@ -0,0 +1,22 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + /** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +--> + +<actionGroups xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/actionGroupSchema.xsd"> + <actionGroup name="StorefrontProductPageSelectRadioButtonOptionValueActionGroup"> + <annotations> + <description>Selects the provided Product Option Value under the provided Radio Button Product Option Title on a Storefront Product page.</description> + </annotations> + <arguments> + <argument name="attributeLabel" type="string" defaultValue="{{ProductAttributeFrontendLabel.label}}"/> + <argument name="optionLabel" type="string" defaultValue="{{productAttributeOption1.label}}"/> + </arguments> + + <checkOption selector="{{StorefrontProductInfoMainSection.productAttributeOptionsRadioButtonByName(attributeLabel, optionLabel)}}" stepKey="fillRadioButtonAttributeOption"/> + </actionGroup> +</actionGroups> diff --git a/app/code/Magento/Catalog/Test/Mftf/ActionGroup/ToggleAdminProductGridColumnsDropdownActionGroup.xml b/app/code/Magento/Catalog/Test/Mftf/ActionGroup/ToggleAdminProductGridColumnsDropdownActionGroup.xml new file mode 100644 index 0000000000000..783c05797c6e1 --- /dev/null +++ b/app/code/Magento/Catalog/Test/Mftf/ActionGroup/ToggleAdminProductGridColumnsDropdownActionGroup.xml @@ -0,0 +1,18 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + /** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +--> + +<actionGroups xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/actionGroupSchema.xsd"> + <actionGroup name="ToggleAdminProductGridColumnsDropdownActionGroup"> + <annotations> + <description>Toggles Admin Product Grid 'Columns' dropdown.</description> + </annotations> + + <click selector="{{AdminProductGridFilterSection.columnsDropdown}}" stepKey="toggleColumnsDropdown"/> + </actionGroup> +</actionGroups> diff --git a/app/code/Magento/Catalog/Test/Mftf/Data/AdminProductAttributeMessageData.xml b/app/code/Magento/Catalog/Test/Mftf/Data/AdminProductAttributeMessageData.xml new file mode 100644 index 0000000000000..834abdad497b8 --- /dev/null +++ b/app/code/Magento/Catalog/Test/Mftf/Data/AdminProductAttributeMessageData.xml @@ -0,0 +1,14 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + /** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +--> + +<entities xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:noNamespaceSchemaLocation="urn:magento:mftf:DataGenerator/etc/dataProfileSchema.xsd"> + <entity name="ProductAttributeMessages"> + <data key="remove_success">You deleted the product attribute.</data> + </entity> +</entities> diff --git a/app/code/Magento/Catalog/Test/Mftf/Data/CatalogStorefrontConfigData.xml b/app/code/Magento/Catalog/Test/Mftf/Data/CatalogStorefrontConfigData.xml index abf01f00dbbcc..f3ccc00192a43 100644 --- a/app/code/Magento/Catalog/Test/Mftf/Data/CatalogStorefrontConfigData.xml +++ b/app/code/Magento/Catalog/Test/Mftf/Data/CatalogStorefrontConfigData.xml @@ -1,63 +1,80 @@ -<?xml version="1.0" encoding="UTF-8"?> -<!-- - /** - * Copyright © Magento, Inc. All rights reserved. - * See COPYING.txt for license details. - */ ---> - -<entities xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" - xsi:noNamespaceSchemaLocation="urn:magento:mftf:DataGenerator/etc/dataProfileSchema.xsd"> - <entity name="RememberPaginationCatalogStorefrontConfig" type="catalog_storefront_config"> - <requiredEntity type="grid_per_page_values">GridPerPageValues</requiredEntity> - <requiredEntity type="remember_pagination">RememberCategoryPagination</requiredEntity> - </entity> - - <entity name="GridPerPageValues" type="grid_per_page_values"> - <data key="value">9,12,20,24</data> - </entity> - - <entity name="RememberCategoryPagination" type="remember_pagination"> - <data key="value">1</data> - </entity> - - <entity name="DefaultCatalogStorefrontConfiguration" type="default_catalog_storefront_config"> - <requiredEntity type="catalogStorefrontFlagZero">DefaultCatalogStorefrontFlagZero</requiredEntity> - <data key="list_allow_all">DefaultListAllowAll</data> - <data key="flat_catalog_product">DefaultFlatCatalogProduct</data> - </entity> - - <entity name="DefaultCatalogStorefrontFlagZero" type="catalogStorefrontFlagZero"> - <data key="value">0</data> - </entity> - - <entity name="DefaultListAllowAll" type="list_allow_all"> - <data key="value">0</data> - </entity> - - <entity name="DefaultFlatCatalogProduct" type="flat_catalog_product"> - <data key="value">0</data> - </entity> - - <entity name="UseFlatCatalogCategoryAndProduct" type="catalog_storefront_config"> - <requiredEntity type="flat_catalog_product">UseFlatCatalogProduct</requiredEntity> - <requiredEntity type="flat_catalog_category">UseFlatCatalogCategory</requiredEntity> - </entity> - - <entity name="UseFlatCatalogProduct" type="flat_catalog_product"> - <data key="value">1</data> - </entity> - - <entity name="UseFlatCatalogCategory" type="flat_catalog_category"> - <data key="value">1</data> - </entity> - - <entity name="DefaultFlatCatalogCategoryAndProduct" type="catalog_storefront_config"> - <requiredEntity type="flat_catalog_product">DefaultFlatCatalogProduct</requiredEntity> - <requiredEntity type="flat_catalog_category">DefaultFlatCatalogCategory</requiredEntity> - </entity> - - <entity name="DefaultFlatCatalogCategory" type="flat_catalog_category"> - <data key="value">0</data> - </entity> -</entities> +<?xml version="1.0" encoding="UTF-8"?> +<!-- + /** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +--> + +<entities xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:noNamespaceSchemaLocation="urn:magento:mftf:DataGenerator/etc/dataProfileSchema.xsd"> + <entity name="RememberPaginationCatalogStorefrontConfig" type="catalog_storefront_config"> + <requiredEntity type="grid_per_page_values">GridPerPageValues</requiredEntity> + <requiredEntity type="remember_pagination">RememberCategoryPagination</requiredEntity> + </entity> + + <entity name="GridPerPageValues" type="grid_per_page_values"> + <data key="value">9,12,20,24</data> + </entity> + + <entity name="RememberCategoryPagination" type="remember_pagination"> + <data key="value">1</data> + </entity> + + <entity name="DefaultCatalogStorefrontConfiguration" type="default_catalog_storefront_config"> + <requiredEntity type="catalogStorefrontFlagZero">DefaultCatalogStorefrontFlagZero</requiredEntity> + <data key="list_allow_all">DefaultListAllowAll</data> + <data key="flat_catalog_product">DefaultFlatCatalogProduct</data> + </entity> + + <entity name="DefaultCatalogStorefrontFlagZero" type="catalogStorefrontFlagZero"> + <data key="value">0</data> + </entity> + + <entity name="DefaultListAllowAll" type="list_allow_all"> + <data key="value">0</data> + </entity> + + <entity name="DefaultFlatCatalogProduct" type="flat_catalog_product"> + <data key="value">0</data> + </entity> + + <entity name="UseFlatCatalogCategoryAndProduct" type="catalog_storefront_config"> + <requiredEntity type="flat_catalog_product">UseFlatCatalogProduct</requiredEntity> + <requiredEntity type="flat_catalog_category">UseFlatCatalogCategory</requiredEntity> + </entity> + + <entity name="UseFlatCatalogProduct" type="flat_catalog_product"> + <data key="value">1</data> + </entity> + + <entity name="UseFlatCatalogCategory" type="flat_catalog_category"> + <data key="value">1</data> + </entity> + + <entity name="DefaultFlatCatalogCategoryAndProduct" type="catalog_storefront_config"> + <requiredEntity type="flat_catalog_product">DefaultFlatCatalogProduct</requiredEntity> + <requiredEntity type="flat_catalog_category">DefaultFlatCatalogCategory</requiredEntity> + </entity> + + <entity name="DefaultFlatCatalogCategory" type="flat_catalog_category"> + <data key="value">0</data> + </entity> + + <entity name="DefaultGridPerPageValuesConfigData"> + <data key="path">catalog/frontend/grid_per_page_values</data> + <data key="value">12,24,36</data> + </entity> + <entity name="CustomGridPerPageValuesConfigData"> + <data key="path">catalog/frontend/grid_per_page_values</data> + <data key="value">1,2</data> + </entity> + <entity name="DefaultGridPerPageDefaultConfigData"> + <data key="path">catalog/frontend/grid_per_page</data> + <data key="value">12</data> + </entity> + <entity name="CustomGridPerPageDefaultConfigData"> + <data key="path">catalog/frontend/grid_per_page</data> + <data key="value">1</data> + </entity> +</entities> diff --git a/app/code/Magento/Catalog/Test/Mftf/Data/CategoryData.xml b/app/code/Magento/Catalog/Test/Mftf/Data/CategoryData.xml index 6ffb4e1902424..daf4809a4781a 100644 --- a/app/code/Magento/Catalog/Test/Mftf/Data/CategoryData.xml +++ b/app/code/Magento/Catalog/Test/Mftf/Data/CategoryData.xml @@ -110,6 +110,22 @@ <data key="is_active">false</data> <data key="include_in_menu">false</data> </entity> + <entity name="_defaultCategoryDifferentUrlStore" type="category"> + <data key="name" unique="suffix">SimpleCategory</data> + <data key="name_lwr" unique="suffix">simplecategory</data> + <data key="is_active">true</data> + <data key="url_key_default_store" unique="suffix">default-simplecategory</data> + <data key="url_key_custom_store" unique="suffix">custom-simplecategory</data> + </entity> + <entity name="SimpleSubCategoryDifferentUrlStore" type="category"> + <data key="name" unique="suffix">SimpleSubCategory</data> + <data key="name_lwr" unique="suffix">simplesubcategory</data> + <data key="is_active">true</data> + <data key="url_key_default_store" unique="suffix">default-simplesubcategory</data> + <data key="url_key_custom_store" unique="suffix">custom-simplesubcategory</data> + <data key="include_in_menu">true</data> + <var key="parent_id" entityType="category" entityKey="id" /> + </entity> <!-- Category from file "prepared-for-sample-data.csv"--> <entity name="Gear" type="category"> <data key="name">Gear</data> @@ -127,4 +143,126 @@ <entity name="SubCategoryNonAnchor" extends="SubCategoryWithParent"> <requiredEntity type="custom_attribute">CustomAttributeCategoryNonAnchor</requiredEntity> </entity> + <entity name="ApiCategoryA" type="category"> + <data key="name" unique="suffix">Category A</data> + <data key="is_active">true</data> + </entity> + <entity name="ApiCategoryTest" type="category"> + <data key="name" unique="suffix">TEST</data> + <data key="is_active">true</data> + </entity> + <entity name="ApiCategoryTest2" type="category"> + <data key="name" unique="suffix">_test2</data> + <data key="is_active">true</data> + </entity> + <entity name="ApiCategoryTest3" type="category"> + <data key="name" unique="suffix">test 3</data> + <data key="is_active">true</data> + </entity> + <entity name="ApiCategorySeveralProducts" type="category"> + <data key="name" unique="suffix">Category with several products</data> + <data key="is_active">true</data> + </entity> + <entity name="ApiCategoryTest4" type="category"> + <data key="name" unique="suffix">test 4</data> + <data key="is_active">true</data> + </entity> + <entity name="ApiCategoryTest5" type="category"> + <data key="name" unique="suffix">test 5</data> + <data key="is_active">true</data> + </entity> + <entity name="ApiCategoryTest8" type="category"> + <data key="name" unique="suffix">test 8</data> + <data key="is_active">true</data> + </entity> + <entity name="ApiCategoryTest6" type="category"> + <data key="name" unique="suffix">test 6</data> + <data key="is_active">true</data> + </entity> + <entity name="ApiCategoryTest7" type="category"> + <data key="name" unique="suffix">test 7</data> + <data key="is_active">true</data> + </entity> + <entity name="ApiCategoryLongTitle" type="category"> + <data key="name" unique="suffix">This is a very very very very very looong title</data> + <data key="is_active">true</data> + </entity> + <entity name="ApiCategoryWithImage" type="category"> + <data key="name" unique="suffix">Category with image</data> + <data key="is_active">true</data> + </entity> + <entity name="ApiCategoryTest0" type="category"> + <data key="name" unique="suffix">test 0</data> + <data key="is_active">true</data> + </entity> + <entity name="ApiCategoryWithDescription" type="category"> + <data key="name" unique="suffix">Category with description & custom title</data> + <data key="is_active">true</data> + </entity> + <entity name="ApiCategoryWithChildren" type="category"> + <data key="name" unique="suffix">Category with children</data> + <data key="is_active">true</data> + </entity> + <entity name="ApiSubCategoryWithParentLongName" type="category"> + <data key="name" unique="suffix">level 1 test category very very very long name</data> + <data key="name_lwr" unique="suffix">level 1 test category very very very long name</data> + <data key="is_active">true</data> + <data key="include_in_menu">true</data> + <var key="parent_id" entityType="category" entityKey="id"/> + </entity> + <entity name="ApiSubCategoryWithParentLevel1" type="category"> + <data key="name" unique="suffix">level 1 test category name</data> + <data key="name_lwr" unique="suffix">level 1 test category name</data> + <data key="is_active">true</data> + <data key="include_in_menu">true</data> + <var key="parent_id" entityType="category" entityKey="id"/> + </entity> + <entity name="ApiSubCategoryWithChildrenLevel1" type="category"> + <data key="name" unique="suffix">level 1 with children</data> + <data key="name_lwr" unique="suffix">level 1 with children</data> + <data key="is_active">true</data> + <data key="include_in_menu">true</data> + <var key="parent_id" entityType="category" entityKey="id"/> + </entity> + <entity name="ApiSubCategoryWithChildrenLevel2" type="category"> + <data key="name" unique="suffix">level 2 with children</data> + <data key="name_lwr" unique="suffix">level 2 with children</data> + <data key="is_active">true</data> + <data key="include_in_menu">true</data> + <var key="parent_id" entityType="category" entityKey="id"/> + </entity> + <entity name="ApiSubCategoryLevel3" type="category"> + <data key="name" unique="suffix">level 3 test</data> + <data key="name_lwr" unique="suffix">level 3 test</data> + <data key="is_active">true</data> + <data key="include_in_menu">true</data> + <var key="parent_id" entityType="category" entityKey="id"/> + </entity> + <entity name="ApiSubCategoryLevel4" type="category"> + <data key="name" unique="suffix">level 4</data> + <data key="name_lwr" unique="suffix">level 4</data> + <data key="is_active">true</data> + <data key="include_in_menu">true</data> + <var key="parent_id" entityType="category" entityKey="id"/> + </entity> + <entity name="ApiSubCategoryLevel4Test" type="category"> + <data key="name" unique="suffix">level 4 test</data> + <data key="name_lwr" unique="suffix">level 4 test</data> + <data key="is_active">true</data> + <data key="include_in_menu">true</data> + <var key="parent_id" entityType="category" entityKey="id"/> + </entity> + <entity name="ApiSubCategoryLevel5" type="category"> + <data key="name" unique="suffix">level 5</data> + <data key="name_lwr" unique="suffix">level 5</data> + <data key="is_active">true</data> + <data key="include_in_menu">true</data> + <var key="parent_id" entityType="category" entityKey="id"/> + </entity> + <entity name="ApiSubCategoryWithLevelZero" type="category"> + <data key="name" unique="suffix">cat with level 1</data> + <data key="is_active">true</data> + <data key="level">0</data> + <var key="parent_id" entityType="category" entityKey="id"/> + </entity> </entities> diff --git a/app/code/Magento/Catalog/Test/Mftf/Data/ConfigData.xml b/app/code/Magento/Catalog/Test/Mftf/Data/ConfigData.xml new file mode 100644 index 0000000000000..35c5c8ac3c866 --- /dev/null +++ b/app/code/Magento/Catalog/Test/Mftf/Data/ConfigData.xml @@ -0,0 +1,19 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + /** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +--> + +<entities xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:noNamespaceSchemaLocation="urn:magento:mftf:DataGenerator/etc/dataProfileSchema.xsd"> + <entity name="EnableCategoryFilterOnCategoryPageConfigData"> + <data key="path">catalog/layered_navigation/display_category</data> + <data key="value">1</data> + </entity> + <entity name="DisableCategoryFilterOnCategoryPageConfigData"> + <data key="path">catalog/layered_navigation/display_category</data> + <data key="value">0</data> + </entity> +</entities> \ No newline at end of file diff --git a/app/code/Magento/Catalog/Test/Mftf/Data/ConstData.xml b/app/code/Magento/Catalog/Test/Mftf/Data/ConstData.xml index d09880f14afbf..f44bfb217d0ae 100644 --- a/app/code/Magento/Catalog/Test/Mftf/Data/ConstData.xml +++ b/app/code/Magento/Catalog/Test/Mftf/Data/ConstData.xml @@ -16,5 +16,7 @@ <entity name="prodNameWithSpecChars"> <data key="trademark">"Pursuit Lumaflex™ Tone Band"</data> <data key="skumark">"x™"</data> + <data key="trademark_without_quotes">Pursuit Lumaflex™ Tone Band</data> + <data key="skumark_without_quotes">x™</data> </entity> </entities> diff --git a/app/code/Magento/Catalog/Test/Mftf/Data/CustomAttributeData.xml b/app/code/Magento/Catalog/Test/Mftf/Data/CustomAttributeData.xml index 1effb4ed0664e..9b35ceba0494c 100644 --- a/app/code/Magento/Catalog/Test/Mftf/Data/CustomAttributeData.xml +++ b/app/code/Magento/Catalog/Test/Mftf/Data/CustomAttributeData.xml @@ -31,6 +31,14 @@ <data key="attribute_code">short_description</data> <data key="value" unique="suffix">API Product Short Description</data> </entity> + <entity name="ApiProductSpecialPrice" type="custom_attribute"> + <data key="attribute_code">special_price</data> + <data key="value">51.51</data> + </entity> + <entity name="ApiProductCost" type="custom_attribute"> + <data key="attribute_code">cost</data> + <data key="value">50.05</data> + </entity> <entity name="ApiProductNewsFromDate" type="custom_attribute"> <data key="attribute_code">news_from_date</data> <data key="value">2018-05-17 00:00:00</data> diff --git a/app/code/Magento/Catalog/Test/Mftf/Data/ProductAttributeData.xml b/app/code/Magento/Catalog/Test/Mftf/Data/ProductAttributeData.xml index e4a91902cb79b..ffee02080503e 100644 --- a/app/code/Magento/Catalog/Test/Mftf/Data/ProductAttributeData.xml +++ b/app/code/Magento/Catalog/Test/Mftf/Data/ProductAttributeData.xml @@ -282,28 +282,6 @@ <data key="used_for_sort_by">false</data> <requiredEntity type="FrontendLabel">ProductAttributeFrontendLabel</requiredEntity> </entity> - <entity name="productAttributeTypeOfPrice" type="ProductAttribute"> - <data key="attribute_code" unique="suffix">attribute</data> - <data key="frontend_input">price</data> - <data key="scope">global</data> - <data key="is_required">false</data> - <data key="is_unique">false</data> - <data key="is_searchable">false</data> - <data key="is_visible">true</data> - <data key="is_wysiwyg_enabled">false</data> - <data key="is_visible_in_advanced_search">false</data> - <data key="is_visible_on_front">true</data> - <data key="is_filterable">true</data> - <data key="is_filterable_in_search">false</data> - <data key="used_in_product_listing">false</data> - <data key="is_used_for_promo_rules">false</data> - <data key="is_comparable">true</data> - <data key="is_used_in_grid">false</data> - <data key="is_visible_in_grid">false</data> - <data key="is_filterable_in_grid">false</data> - <data key="used_for_sort_by">false</data> - <requiredEntity type="FrontendLabel">ProductAttributeFrontendLabel</requiredEntity> - </entity> <entity name="textProductAttribute" extends="productAttributeWysiwyg" type="ProductAttribute"> <data key="frontend_input">text</data> <data key="default_value" unique="suffix">defaultValue</data> @@ -394,10 +372,6 @@ <data key="used_for_sort_by">true</data> <requiredEntity type="FrontendLabel">ProductAttributeFrontendLabel</requiredEntity> </entity> - <entity name="VisualSwatchProductAttribute" type="ProductAttribute"> - <data key="frontend_input">swatch_visual</data> - <data key="attribute_code" unique="suffix">visual_swatch</data> - </entity> <entity name="ProductColorAttribute" type="ProductAttribute"> <data key="frontend_label">Color</data> <data key="attribute_code" unique="suffix">color_attr</data> @@ -406,6 +380,11 @@ <data key="frontend_label">Size</data> <data key="attribute_code" unique="suffix">size_attr</data> </entity> + <entity name="productAttributeWithHtmlTagsInLabel" extends="newProductAttribute" type="ProductAttribute"> + <data key="default_label" unique="suffix">Attribute Default label <span></data> + <data key="default_store_label" unique="suffix">Attribute Store label <span> </data> + <data key="frontend_input">text</data> + </entity> <!-- Product attribute from file "export_import_configurable_product.csv" --> <entity name="ProductAttributeWithTwoOptionsForExportImport" extends="productAttributeDropdownTwoOptions" type="ProductAttribute"> <data key="attribute_code">attribute</data> diff --git a/app/code/Magento/Catalog/Test/Mftf/Data/ProductAttributeMediaGalleryEntryData.xml b/app/code/Magento/Catalog/Test/Mftf/Data/ProductAttributeMediaGalleryEntryData.xml index 75b4ef773a934..7016a1c1d0358 100644 --- a/app/code/Magento/Catalog/Test/Mftf/Data/ProductAttributeMediaGalleryEntryData.xml +++ b/app/code/Magento/Catalog/Test/Mftf/Data/ProductAttributeMediaGalleryEntryData.xml @@ -35,4 +35,11 @@ <data key="label">Magento Logo</data> <requiredEntity type="ImageContent">MagentoLogoImageContentExportImport</requiredEntity> </entity> + <entity name="ApiProductAttributeMediaGalleryEntryWithoutTypesTestImage" type="ProductAttributeMediaGalleryEntry"> + <data key="media_type">image</data> + <data key="label" unique="suffix">Test Image</data> + <data key="position">0</data> + <data key="disabled">false</data> + <requiredEntity type="ImageContent">TestImageContent</requiredEntity> + </entity> </entities> diff --git a/app/code/Magento/Catalog/Test/Mftf/Data/ProductAttributeOptionData.xml b/app/code/Magento/Catalog/Test/Mftf/Data/ProductAttributeOptionData.xml index a8646a58ae39c..34fee2f3445b2 100644 --- a/app/code/Magento/Catalog/Test/Mftf/Data/ProductAttributeOptionData.xml +++ b/app/code/Magento/Catalog/Test/Mftf/Data/ProductAttributeOptionData.xml @@ -101,4 +101,28 @@ <entity name="ProductAttributeOptionTwoForExportImport" extends="productAttributeOption2" type="ProductAttributeOption"> <data key="label">option2</data> </entity> + <entity name="ProductAttributeOption10" type="ProductAttributeOption"> + <var key="attribute_code" entityKey="attribute_code" entityType="ProductAttribute"/> + <data key="label" unique="suffix">3.5</data> + <data key="value" unique="suffix">3.5</data> + <data key="is_default">false</data> + <data key="sort_order">1</data> + <requiredEntity type="StoreLabel">Option12Store1</requiredEntity> + </entity> + <entity name="ProductAttributeOption11" type="ProductAttributeOption"> + <var key="attribute_code" entityKey="attribute_code" entityType="ProductAttribute"/> + <data key="label" unique="suffix">10.12</data> + <data key="value" unique="suffix">10.12</data> + <data key="is_default">false</data> + <data key="sort_order">2</data> + <requiredEntity type="StoreLabel">Option13Store1</requiredEntity> + </entity> + <entity name="ProductAttributeOption12" type="ProductAttributeOption"> + <var key="attribute_code" entityKey="attribute_code" entityType="ProductAttribute"/> + <data key="label" unique="suffix">36</data> + <data key="value" unique="suffix">36</data> + <data key="is_default">false</data> + <data key="sort_order">3</data> + <requiredEntity type="StoreLabel">Option14Store1</requiredEntity> + </entity> </entities> diff --git a/app/code/Magento/Catalog/Test/Mftf/Data/ProductData.xml b/app/code/Magento/Catalog/Test/Mftf/Data/ProductData.xml index bd02bc0e4b340..d5962619984ef 100644 --- a/app/code/Magento/Catalog/Test/Mftf/Data/ProductData.xml +++ b/app/code/Magento/Catalog/Test/Mftf/Data/ProductData.xml @@ -9,13 +9,13 @@ <entities xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="urn:magento:mftf:DataGenerator/etc/dataProfileSchema.xsd"> <entity name="_defaultProduct" type="product"> + <data key="name" unique="suffix">testProductName</data> <data key="sku" unique="suffix">testSku</data> + <data key="urlKey" unique="suffix">testurlkey</data> <data key="type_id">simple</data> <data key="attribute_set_id">4</data> <data key="visibility">4</data> - <data key="name" unique="suffix">testProductName</data> <data key="price">123.00</data> - <data key="urlKey" unique="suffix">testurlkey</data> <data key="status">1</data> <data key="quantity">100</data> <data key="weight">1</data> @@ -23,13 +23,13 @@ <requiredEntity type="custom_attribute_array">CustomAttributeCategoryIds</requiredEntity> </entity> <entity name="ApiSimpleProduct" type="product"> + <data key="name" unique="suffix">Api Simple Product</data> <data key="sku" unique="suffix">api-simple-product</data> + <data key="urlKey" unique="suffix">api-simple-product</data> <data key="type_id">simple</data> <data key="attribute_set_id">4</data> <data key="visibility">4</data> - <data key="name" unique="suffix">Api Simple Product</data> <data key="price">123.00</data> - <data key="urlKey" unique="suffix">api-simple-product</data> <data key="status">1</data> <data key="quantity">100</data> <requiredEntity type="product_extension_attribute">EavStockItem</requiredEntity> @@ -55,15 +55,15 @@ <data key="urlKey" unique="suffix">api-simple-product</data> </entity> <entity name="SimpleProduct" type="product"> + <data key="name" unique="suffix">SimpleProduct</data> <data key="sku" unique="suffix">SimpleProduct</data> + <data key="urlKey" unique="suffix">simpleproduct</data> <data key="type_id">simple</data> <data key="attribute_set_id">4</data> - <data key="name" unique="suffix">SimpleProduct</data> <data key="price">123.00</data> <data key="visibility">4</data> <data key="status">1</data> <data key="quantity">1000</data> - <data key="urlKey" unique="suffix">simpleproduct</data> <data key="weight">1</data> <requiredEntity type="product_extension_attribute">EavStockItem</requiredEntity> <requiredEntity type="custom_attribute_array">CustomAttributeCategoryIds</requiredEntity> @@ -72,10 +72,10 @@ <data key="name">SimpleProduct -+~/\\<>\’“:*\$#@()!,.?`=%&^</data> </entity> <entity name="SimpleProductBeforeUpdate" type="product"> + <data key="name" unique="suffix">SimpleProduct</data> <data key="sku">simpleProduct</data> <data key="type_id">simple</data> <data key="attribute_set_id">4</data> - <data key="name" unique="suffix">SimpleProduct</data> <data key="price">123.00</data> <data key="visibility">4</data> <data key="status">1</data> @@ -86,10 +86,10 @@ <requiredEntity type="custom_attribute_array">CustomAttributeCategoryIds</requiredEntity> </entity> <entity name="SimpleProductAfterImport1" type="product"> + <data key="name">SimpleProductAfterImport1</data> <data key="sku">SimpleProductForTest1</data> <data key="type_id">simple</data> <data key="attribute_set_id">4</data> - <data key="name">SimpleProductAfterImport1</data> <data key="price">250.00</data> <data key="visibility">4</data> <data key="status">1</data> @@ -100,49 +100,50 @@ <requiredEntity type="custom_attribute_array">CustomAttributeCategoryIds</requiredEntity> </entity> <entity name="SimpleProductAfterImport2" type="product"> + <data key="name">SimpleProductAfterImport2</data> <data key="sku">SimpleProductForTest2</data> + <data key="urlKey">simple-product-for-test-2</data> <data key="type_id">simple</data> <data key="attribute_set_id">4</data> - <data key="name">SimpleProductAfterImport2</data> <data key="price">300.00</data> <data key="visibility">4</data> <data key="status">1</data> <data key="quantity">100</data> - <data key="urlKey">simple-product-for-test-2</data> <data key="weight">1</data> <requiredEntity type="product_extension_attribute">EavStockItem</requiredEntity> <requiredEntity type="custom_attribute_array">CustomAttributeCategoryIds</requiredEntity> </entity> <entity name="SimpleProductAfterImport3" type="product"> + <data key="name">SimpleProductAfterImport3</data> <data key="sku">SimpleProductForTest3</data> + <data key="urlKey">simple-product-for-test-3</data> <data key="type_id">simple</data> <data key="attribute_set_id">4</data> - <data key="name">SimpleProductAfterImport3</data> <data key="price">350.00</data> <data key="visibility">4</data> <data key="status">1</data> <data key="quantity">100</data> - <data key="urlKey">simple-product-for-test-3</data> <data key="weight">1</data> <requiredEntity type="product_extension_attribute">EavStockItem</requiredEntity> <requiredEntity type="custom_attribute_array">CustomAttributeCategoryIds</requiredEntity> </entity> <entity name="SimpleProduct2" type="product"> + <data key="name" unique="suffix">SimpleProduct</data> <data key="sku" unique="suffix">SimpleProduct</data> <data key="type_id">simple</data> <data key="attribute_set_id">4</data> - <data key="name" unique="suffix">SimpleProduct</data> <data key="price">123.00</data> <data key="visibility">4</data> <data key="status">1</data> <data key="quantity">1000</data> + <data key="weight">1</data> <requiredEntity type="product_extension_attribute">EavStockItem</requiredEntity> </entity> <entity name="SimpleProduct3" type="product"> + <data key="name" unique="suffix">simple</data> <data key="sku" unique="suffix">simple</data> <data key="type_id">simple</data> <data key="attribute_set_id">4</data> - <data key="name" unique="suffix">simple</data> <data key="price">123.00</data> <data key="visibility">4</data> <data key="status">1</data> @@ -152,70 +153,85 @@ <requiredEntity type="custom_attribute_array">CustomAttributeCategoryIds</requiredEntity> </entity> <entity name="SimpleProduct4" type="product"> + <data key="name" unique="suffix">OutOfStockProduct</data> <data key="sku" unique="suffix">testSku</data> + <data key="urlKey" unique="suffix">testurlkey</data> <data key="type_id">simple</data> <data key="attribute_set_id">4</data> <data key="visibility">4</data> - <data key="name" unique="suffix">OutOfStockProduct</data> <data key="price">123.00</data> - <data key="urlKey" unique="suffix">testurlkey</data> <data key="status">1</data> <data key="quantity">0</data> <requiredEntity type="custom_attribute_array">CustomAttributeCategoryIds</requiredEntity> </entity> <entity name="SimpleOutOfStockProduct" type="product"> + <data key="name" unique="suffix">OutOfStockProduct</data> <data key="sku" unique="suffix">testSku</data> + <data key="urlKey" unique="suffix">testurlkey</data> <data key="type_id">simple</data> <data key="attribute_set_id">4</data> <data key="visibility">4</data> - <data key="name" unique="suffix">OutOfStockProduct</data> <data key="price">123.00</data> - <data key="urlKey" unique="suffix">testurlkey</data> <data key="status">1</data> <data key="quantity">0</data> </entity> + <entity name="SimpleOutOfStockProductWithSpecialPriceAndCost" type="product" extends="SimpleOutOfStockProduct"> + <requiredEntity type="custom_attribute_array">ApiProductSpecialPrice</requiredEntity> + <requiredEntity type="custom_attribute_array">ApiProductCost</requiredEntity> + </entity> <entity name="SimpleProductInStockQuantityZero" type="product"> + <data key="name" unique="suffix">SimpleProductInStockQuantityZero</data> <data key="sku" unique="suffix">testSku</data> + <data key="urlKey" unique="suffix">SimpleProductInStockQuantityZero</data> <data key="type_id">simple</data> <data key="attribute_set_id">4</data> <data key="visibility">4</data> - <data key="name" unique="suffix">SimpleProductInStockQuantityZero</data> <data key="price">123.00</data> - <data key="urlKey" unique="suffix">SimpleProductInStockQuantityZero</data> <data key="status">1</data> <data key="quantity">0</data> <requiredEntity type="product_extension_attribute">EavStock0</requiredEntity> </entity> <!-- Simple Product Disabled --> <entity name="SimpleProductOffline" type="product2"> + <data key="name" unique="suffix">SimpleOffline</data> <data key="sku" unique="suffix">testSku</data> + <data key="urlKey" unique="suffix">testurlkey</data> <data key="type_id">simple</data> <data key="attribute_set_id">4</data> <data key="visibility">4</data> - <data key="name" unique="suffix">SimpleOffline</data> <data key="price">123.00</data> <data key="status">2</data> <data key="quantity">100</data> - <data key="urlKey" unique="suffix">testurlkey</data> <requiredEntity type="product_extension_attribute">EavStockItem</requiredEntity> <requiredEntity type="custom_attribute">CustomAttributeProductAttribute</requiredEntity> </entity> <entity name="SimpleProductDisabled" type="product"> + <data key="name" unique="suffix">Simple Product Disabled</data> <data key="sku" unique="suffix">simple_product_disabled</data> <data key="type_id">simple</data> <data key="attribute_set_id">4</data> - <data key="name" unique="suffix">Simple Product Disabled</data> <data key="price">123.00</data> <data key="visibility">4</data> <data key="status">2</data> <data key="quantity">1001</data> <requiredEntity type="product_extension_attribute">EavStockItem</requiredEntity> </entity> + <entity name="SimpleProductDisabledStockQuantityZero" type="product"> + <data key="sku" unique="suffix">testSku</data> + <data key="type_id">simple</data> + <data key="attribute_set_id">4</data> + <data key="name" unique="suffix">Simple Product Disabled Quantity Zero</data> + <data key="price">123.00</data> + <data key="visibility">4</data> + <data key="status">2</data> + <data key="quantity">0</data> + <requiredEntity type="product_extension_attribute">EavStock0</requiredEntity> + </entity> <entity name="SimpleProductNotVisibleIndividually" type="product"> + <data key="name" unique="suffix">Simple Product Not Visible Individually</data> <data key="sku" unique="suffix">simple_product_not_visible_individually</data> <data key="type_id">simple</data> <data key="attribute_set_id">4</data> - <data key="name" unique="suffix">Simple Product Not Visible Individually</data> <data key="price">123.00</data> <data key="visibility">1</data> <data key="status">1</data> @@ -226,10 +242,10 @@ <data key="price">321.00</data> </entity> <entity name="SimpleOne" type="product2"> + <data key="name" unique="suffix">SimpleProduct</data> <data key="sku" unique="suffix">SimpleOne</data> <data key="type_id">simple</data> <data key="attribute_set_id">4</data> - <data key="name" unique="suffix">SimpleProduct</data> <data key="price">1.23</data> <data key="visibility">4</data> <data key="status">1</data> @@ -237,25 +253,25 @@ <requiredEntity type="custom_attribute">CustomAttributeProductAttribute</requiredEntity> </entity> <entity name="ApiSimpleOutOfStock" type="product2"> + <data key="name" unique="suffix">Api Simple Out Of Stock Product</data> <data key="sku" unique="suffix">api-simple-product</data> + <data key="urlKey" unique="suffix">api-simple-product</data> <data key="type_id">simple</data> <data key="attribute_set_id">4</data> <data key="visibility">4</data> - <data key="name" unique="suffix">Api Simple Out Of Stock Product</data> <data key="price">123.00</data> - <data key="urlKey" unique="suffix">api-simple-product</data> <data key="status">1</data> <data key="quantity">100</data> <requiredEntity type="custom_attribute">CustomAttributeProductAttribute</requiredEntity> </entity> <entity name="ApiSimpleOne" type="product2"> + <data key="name" unique="suffix">Api Simple Product</data> <data key="sku" unique="suffix">api-simple-product</data> + <data key="urlKey" unique="suffix">api-simple-product</data> <data key="type_id">simple</data> <data key="attribute_set_id">4</data> <data key="visibility">4</data> - <data key="name" unique="suffix">Api Simple Product</data> <data key="price">123.00</data> - <data key="urlKey" unique="suffix">api-simple-product</data> <data key="status">1</data> <data key="quantity">100</data> <requiredEntity type="product_extension_attribute">EavStockItem</requiredEntity> @@ -265,26 +281,26 @@ <data key="sku" unique="suffix">pr</data> </entity> <entity name="ApiSimpleOneHidden" type="product2"> + <data key="name" unique="suffix">Api Simple Product</data> <data key="sku" unique="suffix">api-simple-product</data> + <data key="urlKey" unique="suffix">api-simple-product</data> <data key="type_id">simple</data> <data key="attribute_set_id">4</data> <data key="visibility">1</data> - <data key="name" unique="suffix">Api Simple Product</data> <data key="price">123.00</data> - <data key="urlKey" unique="suffix">api-simple-product</data> <data key="status">1</data> <data key="quantity">100</data> <requiredEntity type="product_extension_attribute">EavStockItem</requiredEntity> <requiredEntity type="custom_attribute">CustomAttributeProductAttribute</requiredEntity> </entity> <entity name="ApiSimpleTwo" type="product2"> + <data key="name" unique="suffix">Api Simple Product Two</data> <data key="sku" unique="suffix">api-simple-product-two</data> + <data key="urlKey" unique="suffix">api-simple-product-two</data> <data key="type_id">simple</data> <data key="attribute_set_id">4</data> <data key="visibility">4</data> - <data key="name" unique="suffix">Api Simple Product Two</data> <data key="price">234.00</data> - <data key="urlKey" unique="suffix">api-simple-product-two</data> <data key="status">1</data> <data key="quantity">100</data> <requiredEntity type="product_extension_attribute">EavStockItem</requiredEntity> @@ -300,23 +316,23 @@ <data key="price">70</data> </entity> <entity name="ApiSimpleTwoHidden" type="product2"> + <data key="name" unique="suffix">Api Simple Product Two</data> <data key="sku" unique="suffix">api-simple-product-two</data> + <data key="urlKey" unique="suffix">api-simple-product-two</data> <data key="type_id">simple</data> <data key="attribute_set_id">4</data> <data key="visibility">1</data> - <data key="name" unique="suffix">Api Simple Product Two</data> <data key="price">234.00</data> - <data key="urlKey" unique="suffix">api-simple-product-two</data> <data key="status">1</data> <data key="quantity">100</data> <requiredEntity type="product_extension_attribute">EavStockItem</requiredEntity> <requiredEntity type="custom_attribute">CustomAttributeProductAttribute</requiredEntity> </entity> <entity name="VirtualProduct" type="product"> + <data key="name" unique="suffix">VirtualProduct</data> <data key="sku" unique="suffix">virtualproduct</data> <data key="type_id">virtual</data> <data key="attribute_set_id">4</data> - <data key="name" unique="suffix">VirtualProduct</data> <data key="price">99.99</data> <data key="quantity">250</data> <data key="weight">0</data> @@ -324,10 +340,10 @@ <requiredEntity type="product_extension_attribute">EavStockItem</requiredEntity> </entity> <entity name="SimpleTwo" type="product2"> + <data key="name" unique="suffix">SimpleProduct</data> <data key="sku" unique="suffix">SimpleTwo</data> <data key="type_id">simple</data> <data key="attribute_set_id">4</data> - <data key="name" unique="suffix">SimpleProduct</data> <data key="price">1.23</data> <data key="visibility">4</data> <data key="status">1</data> @@ -378,52 +394,52 @@ <data key="filename">test_image</data> </entity> <entity name="ProductWithUnicode" type="product"> + <data key="name" unique="suffix">霁产品</data> <data key="sku" unique="suffix">霁产品</data> + <data key="urlKey" unique="suffix">testurlkey</data> <data key="type_id">simple</data> <data key="attribute_set_id">4</data> <data key="visibility">4</data> - <data key="name" unique="suffix">霁产品</data> <data key="price">123.00</data> - <data key="urlKey" unique="suffix">testurlkey</data> <data key="status">1</data> <data key="quantity">100</data> <requiredEntity type="product_extension_attribute">EavStockItem</requiredEntity> <requiredEntity type="custom_attribute_array">CustomAttributeCategoryIds</requiredEntity> </entity> <entity name="productWithHTMLEntityOne" type="product"> + <data key="name" unique="suffix">SimpleOne™Product</data> <data key="sku" unique="suffix">SimpleOne™Product</data> + <data key="urlKey" unique="suffix">testurlkey</data> <data key="type_id">simple</data> <data key="attribute_set_id">4</data> <data key="visibility">4</data> - <data key="name" unique="suffix">SimpleOne™Product</data> <data key="price">50.00</data> - <data key="urlKey" unique="suffix">testurlkey</data> <data key="status">1</data> <data key="quantity">100</data> <requiredEntity type="product_extension_attribute">EavStockItem</requiredEntity> <requiredEntity type="custom_attribute_array">CustomAttributeCategoryIds</requiredEntity> </entity> <entity name="productWithHTMLEntityTwo" type="product"> + <data key="name" unique="suffix">SimpleTwo霁产品<カネボウPro</data> <data key="sku" unique="suffix">SimpleTwo霁产品<カネボウPro</data> + <data key="urlKey" unique="suffix">testurlkey</data> <data key="type_id">simple</data> <data key="attribute_set_id">4</data> <data key="visibility">4</data> - <data key="name" unique="suffix">SimpleTwo霁产品<カネボウPro</data> <data key="price">50.00</data> - <data key="urlKey" unique="suffix">testurlkey</data> <data key="status">1</data> <data key="quantity">100</data> <requiredEntity type="product_extension_attribute">EavStockItem</requiredEntity> <requiredEntity type="custom_attribute_array">CustomAttributeCategoryIds</requiredEntity> </entity> <entity name="defaultVirtualProduct" type="product"> + <data key="name" unique="suffix">virtualProduct</data> <data key="sku" unique="suffix">virtualProduct</data> + <data key="urlKey" unique="suffix">virtualproduct</data> <data key="type_id">virtual</data> <data key="attribute_set_id">4</data> <data key="visibility">4</data> - <data key="name" unique="suffix">virtualProduct</data> <data key="price">12.34</data> - <data key="urlKey" unique="suffix">virtualproduct</data> <data key="status">1</data> <data key="quantity">100</data> <data key="weight">0</data> @@ -431,13 +447,13 @@ <requiredEntity type="custom_attribute_array">CustomAttributeCategoryIds</requiredEntity> </entity> <entity name="productWithDescription" type="product"> + <data key="name" unique="suffix">testProductWithDescriptionName</data> <data key="sku" unique="suffix">testProductWithDescriptionSku</data> + <data key="urlKey" unique="suffix">testproductwithdescriptionurlkey</data> <data key="type_id">simple</data> <data key="attribute_set_id">4</data> <data key="visibility">4</data> - <data key="name" unique="suffix">testProductWithDescriptionName</data> <data key="price">123.00</data> - <data key="urlKey" unique="suffix">testproductwithdescriptionurlkey</data> <data key="status">1</data> <data key="quantity">100</data> <requiredEntity type="product_extension_attribute">EavStockItem</requiredEntity> @@ -446,13 +462,13 @@ <requiredEntity type="custom_attribute_array">ApiProductShortDescription</requiredEntity> </entity> <entity name="ApiProductWithDescription" type="product"> + <data key="name" unique="suffix">Api Simple Product</data> <data key="sku" unique="suffix">api-simple-product</data> + <data key="urlKey" unique="suffix">api-simple-product</data> <data key="type_id">simple</data> <data key="attribute_set_id">4</data> <data key="visibility">4</data> - <data key="name" unique="suffix">Api Simple Product</data> <data key="price">123.00</data> - <data key="urlKey" unique="suffix">api-simple-product</data> <data key="status">1</data> <data key="quantity">100</data> <requiredEntity type="product_extension_attribute">EavStockItem</requiredEntity> @@ -460,13 +476,13 @@ <requiredEntity type="custom_attribute_array">ApiProductShortDescription</requiredEntity> </entity> <entity name="ApiProductNameWithNoSpaces" type="product"> + <data key="name" unique="suffix">ApiSimpleProduct</data> <data key="sku" unique="suffix">api-simple-product</data> + <data key="urlKey" unique="suffix">api-simple-product</data> <data key="type_id">simple</data> <data key="attribute_set_id">4</data> <data key="visibility">4</data> - <data key="name" unique="suffix">ApiSimpleProduct</data> <data key="price">123.00</data> - <data key="urlKey" unique="suffix">api-simple-product</data> <data key="status">1</data> <data key="quantity">100</data> <requiredEntity type="product_extension_attribute">EavStockItem</requiredEntity> @@ -474,13 +490,13 @@ <requiredEntity type="custom_attribute_array">ApiProductShortDescription</requiredEntity> </entity> <entity name="ApiProductWithDescriptionAndUnderscoredSku" type="product"> + <data key="name" unique="suffix">Api Simple Product</data> <data key="sku" unique="suffix">api_simple_product</data> + <data key="urlKey" unique="suffix">api-simple-product</data> <data key="type_id">simple</data> <data key="attribute_set_id">4</data> <data key="visibility">4</data> - <data key="name" unique="suffix">Api Simple Product</data> <data key="price">123.00</data> - <data key="urlKey" unique="suffix">api-simple-product</data> <data key="status">1</data> <data key="quantity">100</data> <requiredEntity type="product_extension_attribute">EavStockItem</requiredEntity> @@ -488,26 +504,26 @@ <requiredEntity type="custom_attribute_array">ApiProductShortDescription</requiredEntity> </entity> <entity name="_newDefaultProduct" type="product"> + <data key="name" unique="suffix">testproductname</data> <data key="sku" unique="suffix">testSku</data> + <data key="urlKey" unique="suffix">testurlkey</data> <data key="type_id">simple</data> <data key="attribute_set_id">4</data> <data key="visibility">4</data> - <data key="name" unique="suffix">testproductname</data> <data key="price">123.00</data> - <data key="urlKey" unique="suffix">testurlkey</data> <data key="status">1</data> <data key="quantity">100</data> <requiredEntity type="product_extension_attribute">EavStockItem</requiredEntity> <requiredEntity type="custom_attribute_array">CustomAttributeCategoryIds</requiredEntity> </entity> <entity name="SimpleProductWithCustomAttributeSet" type="product"> + <data key="name" unique="suffix">testProductName</data> <data key="sku" unique="suffix">testSku</data> + <data key="urlKey" unique="suffix">testurlkey</data> <data key="type_id">simple</data> <var key="attribute_set_id" entityKey="attribute_set_id" entityType="CatalogAttributeSet"/> <data key="visibility">4</data> - <data key="name" unique="suffix">testProductName</data> <data key="price">123.00</data> - <data key="urlKey" unique="suffix">testurlkey</data> <data key="status">1</data> <data key="weight">1</data> <data key="quantity">100</data> @@ -528,6 +544,11 @@ <requiredEntity type="product_option">ProductOptionDateTime</requiredEntity> <requiredEntity type="product_option">ProductOptionTime</requiredEntity> </entity> + <entity name="productWithDateTimeOption" type="product"> + <var key="sku" entityType="product" entityKey="sku" /> + <data key="file">magento.jpg</data> + <requiredEntity type="product_option">ProductOptionDateTime</requiredEntity> + </entity> <entity name="productWithOptionRadiobutton" type="product"> <var key="sku" entityType="product" entityKey="sku" /> <requiredEntity type="product_option">ProductOptionRadiobuttonWithTwoFixedOptions</requiredEntity> @@ -567,13 +588,13 @@ <requiredEntity type="product_option">ProductOptionFile</requiredEntity> </entity> <entity name="ApiVirtualProductWithDescription" type="product"> + <data key="name" unique="suffix">Api Virtual Product</data> <data key="sku" unique="suffix">api-virtual-product</data> + <data key="urlKey" unique="suffix">api-virtual-product</data> <data key="type_id">virtual</data> <data key="attribute_set_id">4</data> <data key="visibility">4</data> - <data key="name" unique="suffix">Api Virtual Product</data> <data key="price">123.00</data> - <data key="urlKey" unique="suffix">api-virtual-product</data> <data key="status">1</data> <data key="quantity">100</data> <requiredEntity type="product_extension_attribute">EavStockItem</requiredEntity> @@ -581,13 +602,13 @@ <requiredEntity type="custom_attribute_array">ApiProductShortDescription</requiredEntity> </entity> <entity name="ApiVirtualProductWithDescriptionAndUnderscoredSku" type="product"> + <data key="name" unique="suffix">Api Virtual Product</data> <data key="sku" unique="suffix">api_virtual_product</data> + <data key="urlKey" unique="suffix">api-virtual-product</data> <data key="type_id">virtual</data> <data key="attribute_set_id">4</data> <data key="visibility">4</data> - <data key="name" unique="suffix">Api Virtual Product</data> <data key="price">123.00</data> - <data key="urlKey" unique="suffix">api-virtual-product</data> <data key="status">1</data> <data key="quantity">100</data> <requiredEntity type="product_extension_attribute">EavStockItem</requiredEntity> @@ -595,15 +616,15 @@ <requiredEntity type="custom_attribute_array">ApiProductShortDescription</requiredEntity> </entity> <entity name="SimpleProductWithNewFromDate" type="product"> + <data key="name" unique="suffix">SimpleProduct</data> <data key="sku" unique="suffix">SimpleProduct</data> + <data key="urlKey" unique="suffix">simpleproduct</data> <data key="type_id">simple</data> <data key="attribute_set_id">4</data> - <data key="name" unique="suffix">SimpleProduct</data> <data key="price">125.00</data> <data key="visibility">4</data> <data key="status">1</data> <data key="quantity">1000</data> - <data key="urlKey" unique="suffix">simpleproduct</data> <data key="weight">1</data> <requiredEntity type="product_extension_attribute">EavStockItem</requiredEntity> <requiredEntity type="custom_attribute_array">ApiProductNewsFromDate</requiredEntity> @@ -630,7 +651,7 @@ <var key="sku" entityKey="sku" entityType="product3"/> </entity> <entity name="ApiSimplePrice1" type="product"> - <data key="sku" unique="suffix">api-simple-product</data> + <data key="sku" unique="suffix">apisimpleproduct</data> <data key="type_id">simple</data> <data key="attribute_set_id">4</data> <data key="visibility">4</data> @@ -638,7 +659,7 @@ <data key="price">1.00</data> </entity> <entity name="ApiSimplePrice100" type="product"> - <data key="sku" unique="suffix">api-simple-product</data> + <data key="sku" unique="suffix">apisimpleproduct</data> <data key="type_id">simple</data> <data key="attribute_set_id">4</data> <data key="visibility">4</data> @@ -751,6 +772,7 @@ <data key="status">In Stock</data> <data key="visibility">Catalog, Search</data> <data key="urlKey" unique="suffix">virtual-product</data> + <data key="storefrontStatus">IN STOCK</data> <data key="type_id">virtual</data> </entity> <entity name="virtualProductCustomImportOptions" type="product"> @@ -858,6 +880,14 @@ <data key="weight">5</data> <requiredEntity type="product_extension_attribute">EavStock100</requiredEntity> </entity> + <entity name="Magento2" type="image"> + <data key="title" unique="suffix">Magento2</data> + <data key="file_type">Upload File</data> + <data key="shareable">Yes</data> + <data key="file">magento2.jpg</data> + <data key="filename">magento2</data> + <data key="file_extension">jpg</data> + </entity> <entity name="Magento3" type="image"> <data key="title" unique="suffix">Magento3</data> <data key="price">1.00</data> diff --git a/app/code/Magento/Catalog/Test/Mftf/Data/RecentlyViewedProductStoreData.xml b/app/code/Magento/Catalog/Test/Mftf/Data/RecentlyViewedProductStoreData.xml new file mode 100644 index 0000000000000..15eec8495234b --- /dev/null +++ b/app/code/Magento/Catalog/Test/Mftf/Data/RecentlyViewedProductStoreData.xml @@ -0,0 +1,23 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + /** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +--> + +<entities xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:noNamespaceSchemaLocation="urn:magento:mftf:DataGenerator/etc/dataProfileSchema.xsd"> + <entity name="RecentlyViewedProductScopeStore"> + <data key="path">catalog/recently_products/scope</data> + <data key="value">store</data> + </entity> + <entity name="RecentlyViewedProductScopeWebsite"> + <data key="path">catalog/recently_products/scope</data> + <data key="value">website</data> + </entity> + <entity name="RecentlyViewedProductScopeStoreGroup"> + <data key="path">catalog/recently_products/scope</data> + <data key="value">group</data> + </entity> +</entities> \ No newline at end of file diff --git a/app/code/Magento/Catalog/Test/Mftf/Data/StoreLabelData.xml b/app/code/Magento/Catalog/Test/Mftf/Data/StoreLabelData.xml index dcd7fde92283c..2ae473cde5108 100644 --- a/app/code/Magento/Catalog/Test/Mftf/Data/StoreLabelData.xml +++ b/app/code/Magento/Catalog/Test/Mftf/Data/StoreLabelData.xml @@ -80,4 +80,16 @@ <data key="store_id">1</data> <data key="label">Blue</data> </entity> + <entity name="Option12Store1" type="StoreLabel"> + <data key="store_id">1</data> + <data key="label">3.5</data> + </entity> + <entity name="Option13Store1" type="StoreLabel"> + <data key="store_id">1</data> + <data key="label">10.12</data> + </entity> + <entity name="Option14Store1" type="StoreLabel"> + <data key="store_id">1</data> + <data key="label">36</data> + </entity> </entities> diff --git a/app/code/Magento/Catalog/Test/Mftf/Data/TierPriceData.xml b/app/code/Magento/Catalog/Test/Mftf/Data/TierPriceData.xml index 0c88c666a20ca..c79756507794a 100644 --- a/app/code/Magento/Catalog/Test/Mftf/Data/TierPriceData.xml +++ b/app/code/Magento/Catalog/Test/Mftf/Data/TierPriceData.xml @@ -64,4 +64,12 @@ <data key="quantity">30</data> <var key="sku" entityType="product" entityKey="sku" /> </entity> -</entities> \ No newline at end of file + <entity name="tierProductPriceDiscount" type="catalogTierPrice"> + <data key="price">36.00</data> + <data key="price_type">discount</data> + <data key="website_id">0</data> + <data key="customer_group">ALL GROUPS</data> + <data key="quantity">3</data> + <var key="sku" entityType="product" entityKey="sku" /> + </entity> +</entities> diff --git a/app/code/Magento/Catalog/Test/Mftf/Metadata/catalog_attribute_set-meta.xml b/app/code/Magento/Catalog/Test/Mftf/Metadata/CatalogAttributeSetMeta.xml similarity index 100% rename from app/code/Magento/Catalog/Test/Mftf/Metadata/catalog_attribute_set-meta.xml rename to app/code/Magento/Catalog/Test/Mftf/Metadata/CatalogAttributeSetMeta.xml diff --git a/app/code/Magento/Catalog/Test/Mftf/Metadata/catalog_configuration-meta.xml b/app/code/Magento/Catalog/Test/Mftf/Metadata/CatalogConfigurationMeta.xml similarity index 100% rename from app/code/Magento/Catalog/Test/Mftf/Metadata/catalog_configuration-meta.xml rename to app/code/Magento/Catalog/Test/Mftf/Metadata/CatalogConfigurationMeta.xml diff --git a/app/code/Magento/Catalog/Test/Mftf/Metadata/catalog_price-meta.xml b/app/code/Magento/Catalog/Test/Mftf/Metadata/CatalogPriceMeta.xml similarity index 100% rename from app/code/Magento/Catalog/Test/Mftf/Metadata/catalog_price-meta.xml rename to app/code/Magento/Catalog/Test/Mftf/Metadata/CatalogPriceMeta.xml diff --git a/app/code/Magento/Catalog/Test/Mftf/Metadata/catalog_recently_products-meta.xml b/app/code/Magento/Catalog/Test/Mftf/Metadata/CatalogRecentlyProductsMeta.xml similarity index 100% rename from app/code/Magento/Catalog/Test/Mftf/Metadata/catalog_recently_products-meta.xml rename to app/code/Magento/Catalog/Test/Mftf/Metadata/CatalogRecentlyProductsMeta.xml diff --git a/app/code/Magento/Catalog/Test/Mftf/Metadata/catalog_special_price-meta.xml b/app/code/Magento/Catalog/Test/Mftf/Metadata/CatalogSpecialPriceMeta.xml similarity index 100% rename from app/code/Magento/Catalog/Test/Mftf/Metadata/catalog_special_price-meta.xml rename to app/code/Magento/Catalog/Test/Mftf/Metadata/CatalogSpecialPriceMeta.xml diff --git a/app/code/Magento/Catalog/Test/Mftf/Metadata/catalog_tier_price-meta.xml b/app/code/Magento/Catalog/Test/Mftf/Metadata/CatalogTierPriceMeta.xml similarity index 100% rename from app/code/Magento/Catalog/Test/Mftf/Metadata/catalog_tier_price-meta.xml rename to app/code/Magento/Catalog/Test/Mftf/Metadata/CatalogTierPriceMeta.xml diff --git a/app/code/Magento/Catalog/Test/Mftf/Metadata/category-meta.xml b/app/code/Magento/Catalog/Test/Mftf/Metadata/CategoryMeta.xml similarity index 100% rename from app/code/Magento/Catalog/Test/Mftf/Metadata/category-meta.xml rename to app/code/Magento/Catalog/Test/Mftf/Metadata/CategoryMeta.xml diff --git a/app/code/Magento/Catalog/Test/Mftf/Metadata/custom_attribute-meta.xml b/app/code/Magento/Catalog/Test/Mftf/Metadata/CustomAttributeMeta.xml similarity index 100% rename from app/code/Magento/Catalog/Test/Mftf/Metadata/custom_attribute-meta.xml rename to app/code/Magento/Catalog/Test/Mftf/Metadata/CustomAttributeMeta.xml diff --git a/app/code/Magento/Catalog/Test/Mftf/Metadata/empty_extension_attribute-meta.xml b/app/code/Magento/Catalog/Test/Mftf/Metadata/EmptyExtensionAttributeMeta.xml similarity index 100% rename from app/code/Magento/Catalog/Test/Mftf/Metadata/empty_extension_attribute-meta.xml rename to app/code/Magento/Catalog/Test/Mftf/Metadata/EmptyExtensionAttributeMeta.xml diff --git a/app/code/Magento/Catalog/Test/Mftf/Metadata/frontend_label-meta.xml b/app/code/Magento/Catalog/Test/Mftf/Metadata/FrontendLabelMeta.xml similarity index 100% rename from app/code/Magento/Catalog/Test/Mftf/Metadata/frontend_label-meta.xml rename to app/code/Magento/Catalog/Test/Mftf/Metadata/FrontendLabelMeta.xml diff --git a/app/code/Magento/Catalog/Test/Mftf/Metadata/ImageContentMeta.xml b/app/code/Magento/Catalog/Test/Mftf/Metadata/ImageContentMeta.xml new file mode 100644 index 0000000000000..ac75a819548ae --- /dev/null +++ b/app/code/Magento/Catalog/Test/Mftf/Metadata/ImageContentMeta.xml @@ -0,0 +1,21 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + /** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +--> + +<operations xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:noNamespaceSchemaLocation="urn:magento:mftf:DataGenerator/etc/dataOperation.xsd"> + <operation name="CreateImageContent" dataType="ImageContent" type="create"> + <field key="base64_encoded_data" required="true">string</field> + <field key="type" required="true">string</field> + <field key="name" required="true">string</field> + </operation> + <operation name="UpdateImageContent" dataType="ImageContent" type="update"> + <field key="base64_encoded_data" required="true">string</field> + <field key="type" required="true">string</field> + <field key="name" required="true">string</field> + </operation> +</operations> diff --git a/app/code/Magento/Catalog/Test/Mftf/Metadata/product_attribute_media_gallery_entry-meta.xml b/app/code/Magento/Catalog/Test/Mftf/Metadata/ProductAttributeMediaGalleryEntryMeta.xml similarity index 100% rename from app/code/Magento/Catalog/Test/Mftf/Metadata/product_attribute_media_gallery_entry-meta.xml rename to app/code/Magento/Catalog/Test/Mftf/Metadata/ProductAttributeMediaGalleryEntryMeta.xml diff --git a/app/code/Magento/Catalog/Test/Mftf/Metadata/product_attribute-meta.xml b/app/code/Magento/Catalog/Test/Mftf/Metadata/ProductAttributeMeta.xml similarity index 100% rename from app/code/Magento/Catalog/Test/Mftf/Metadata/product_attribute-meta.xml rename to app/code/Magento/Catalog/Test/Mftf/Metadata/ProductAttributeMeta.xml diff --git a/app/code/Magento/Catalog/Test/Mftf/Metadata/product_attribute_option-meta.xml b/app/code/Magento/Catalog/Test/Mftf/Metadata/ProductAttributeOptionMeta.xml similarity index 100% rename from app/code/Magento/Catalog/Test/Mftf/Metadata/product_attribute_option-meta.xml rename to app/code/Magento/Catalog/Test/Mftf/Metadata/ProductAttributeOptionMeta.xml diff --git a/app/code/Magento/Catalog/Test/Mftf/Metadata/product_attribute_set-meta.xml b/app/code/Magento/Catalog/Test/Mftf/Metadata/ProductAttributeSetMeta.xml similarity index 100% rename from app/code/Magento/Catalog/Test/Mftf/Metadata/product_attribute_set-meta.xml rename to app/code/Magento/Catalog/Test/Mftf/Metadata/ProductAttributeSetMeta.xml diff --git a/app/code/Magento/Catalog/Test/Mftf/Metadata/product_extension_attribute-meta.xml b/app/code/Magento/Catalog/Test/Mftf/Metadata/ProductExtensionAttributeMeta.xml similarity index 100% rename from app/code/Magento/Catalog/Test/Mftf/Metadata/product_extension_attribute-meta.xml rename to app/code/Magento/Catalog/Test/Mftf/Metadata/ProductExtensionAttributeMeta.xml diff --git a/app/code/Magento/Catalog/Test/Mftf/Metadata/product_link_extension_attribute-meta.xml b/app/code/Magento/Catalog/Test/Mftf/Metadata/ProductLinkExtensionAttributeMeta.xml similarity index 100% rename from app/code/Magento/Catalog/Test/Mftf/Metadata/product_link_extension_attribute-meta.xml rename to app/code/Magento/Catalog/Test/Mftf/Metadata/ProductLinkExtensionAttributeMeta.xml diff --git a/app/code/Magento/Catalog/Test/Mftf/Metadata/product_link-meta.xml b/app/code/Magento/Catalog/Test/Mftf/Metadata/ProductLinkMeta.xml similarity index 100% rename from app/code/Magento/Catalog/Test/Mftf/Metadata/product_link-meta.xml rename to app/code/Magento/Catalog/Test/Mftf/Metadata/ProductLinkMeta.xml diff --git a/app/code/Magento/Catalog/Test/Mftf/Metadata/product_links-meta.xml b/app/code/Magento/Catalog/Test/Mftf/Metadata/ProductLinksMeta.xml similarity index 100% rename from app/code/Magento/Catalog/Test/Mftf/Metadata/product_links-meta.xml rename to app/code/Magento/Catalog/Test/Mftf/Metadata/ProductLinksMeta.xml diff --git a/app/code/Magento/Catalog/Test/Mftf/Metadata/product-meta.xml b/app/code/Magento/Catalog/Test/Mftf/Metadata/ProductMeta.xml similarity index 100% rename from app/code/Magento/Catalog/Test/Mftf/Metadata/product-meta.xml rename to app/code/Magento/Catalog/Test/Mftf/Metadata/ProductMeta.xml diff --git a/app/code/Magento/Catalog/Test/Mftf/Metadata/product_option-meta.xml b/app/code/Magento/Catalog/Test/Mftf/Metadata/ProductOptionMeta.xml similarity index 100% rename from app/code/Magento/Catalog/Test/Mftf/Metadata/product_option-meta.xml rename to app/code/Magento/Catalog/Test/Mftf/Metadata/ProductOptionMeta.xml diff --git a/app/code/Magento/Catalog/Test/Mftf/Metadata/product_option_value-meta.xml b/app/code/Magento/Catalog/Test/Mftf/Metadata/ProductOptionValueMeta.xml similarity index 100% rename from app/code/Magento/Catalog/Test/Mftf/Metadata/product_option_value-meta.xml rename to app/code/Magento/Catalog/Test/Mftf/Metadata/ProductOptionValueMeta.xml diff --git a/app/code/Magento/Catalog/Test/Mftf/Metadata/stock_item-meta.xml b/app/code/Magento/Catalog/Test/Mftf/Metadata/StockItemMeta.xml similarity index 100% rename from app/code/Magento/Catalog/Test/Mftf/Metadata/stock_item-meta.xml rename to app/code/Magento/Catalog/Test/Mftf/Metadata/StockItemMeta.xml diff --git a/app/code/Magento/Catalog/Test/Mftf/Metadata/store_label-meta.xml b/app/code/Magento/Catalog/Test/Mftf/Metadata/StoreLabelMeta.xml similarity index 100% rename from app/code/Magento/Catalog/Test/Mftf/Metadata/store_label-meta.xml rename to app/code/Magento/Catalog/Test/Mftf/Metadata/StoreLabelMeta.xml diff --git a/app/code/Magento/Catalog/Test/Mftf/Metadata/validation_rule-meta.xml b/app/code/Magento/Catalog/Test/Mftf/Metadata/ValidationRuleMeta.xml similarity index 100% rename from app/code/Magento/Catalog/Test/Mftf/Metadata/validation_rule-meta.xml rename to app/code/Magento/Catalog/Test/Mftf/Metadata/ValidationRuleMeta.xml diff --git a/app/code/Magento/Catalog/Test/Mftf/Metadata/image_content-meta.xml b/app/code/Magento/Catalog/Test/Mftf/Metadata/image_content-meta.xml deleted file mode 100644 index 8b12a2fbbbc5d..0000000000000 --- a/app/code/Magento/Catalog/Test/Mftf/Metadata/image_content-meta.xml +++ /dev/null @@ -1,21 +0,0 @@ -<?xml version="1.0" encoding="UTF-8"?> -<!-- - /** - * Copyright © Magento, Inc. All rights reserved. - * See COPYING.txt for license details. - */ ---> - -<operations xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" - xsi:noNamespaceSchemaLocation="../../../../../../vendor/magento/magento2-functional-testing-framework/src/Magento/FunctionalTestingFramework/DataGenerator/etc/dataOperation.xsd"> - <operation name="CreateImageContent" dataType="ImageContent" type="create"> - <field key="base64_encoded_data" required="true">string</field> - <field key="type" required="true">string</field> - <field key="name" required="true">string</field> - </operation> - <operation name="UpdateImageContent" dataType="ImageContent" type="update"> - <field key="base64_encoded_data" required="true">string</field> - <field key="type" required="true">string</field> - <field key="name" required="true">string</field> - </operation> -</operations> diff --git a/app/code/Magento/Catalog/Test/Mftf/Page/AdminProductAttributeFormPage.xml b/app/code/Magento/Catalog/Test/Mftf/Page/AdminProductAttributeFormPage.xml index fab87f90f86dd..3acaaf4e1775d 100644 --- a/app/code/Magento/Catalog/Test/Mftf/Page/AdminProductAttributeFormPage.xml +++ b/app/code/Magento/Catalog/Test/Mftf/Page/AdminProductAttributeFormPage.xml @@ -9,5 +9,6 @@ xsi:noNamespaceSchemaLocation="urn:magento:mftf:Page/etc/PageObject.xsd"> <page name="ProductAttributePage" url="catalog/product_attribute/new/" area="admin" module="Catalog"> <section name="AdminCreateProductAttributeSection"/> + <section name="AdminProductAttributeManageLabelsSection"/> </page> </pages> diff --git a/app/code/Magento/Catalog/Test/Mftf/Section/AdminCategoryBasicFieldSection.xml b/app/code/Magento/Catalog/Test/Mftf/Section/AdminCategoryBasicFieldSection.xml deleted file mode 100644 index 92961cc48212a..0000000000000 --- a/app/code/Magento/Catalog/Test/Mftf/Section/AdminCategoryBasicFieldSection.xml +++ /dev/null @@ -1,86 +0,0 @@ -<?xml version="1.0" encoding="UTF-8"?> -<!-- - /** - * Copyright © Magento, Inc. All rights reserved. - * See COPYING.txt for license details. - */ ---> - -<sections xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" - xsi:noNamespaceSchemaLocation="urn:magento:mftf:Page/etc/SectionObject.xsd"> - <section name="AdminCategoryBasicFieldSection"> - <element name="IncludeInMenu" type="checkbox" selector="input[name='include_in_menu']"/> - <element name="includeInMenuLabel" type="text" selector="input[name='include_in_menu']+label"/> - <element name="includeInMenuUseDefault" type="checkbox" selector="input[name='use_default[include_in_menu]']"/> - <element name="EnableCategory" type="checkbox" selector="input[name='is_active']"/> - <element name="enableCategoryLabel" type="text" selector="input[name='is_active']+label"/> - <element name="enableUseDefault" type="checkbox" selector="input[name='use_default[is_active]']"/> - <element name="CategoryNameInput" type="input" selector="input[name='name']"/> - <element name="RequiredFieldIndicator" type="text" selector=" return window.getComputedStyle(document.querySelector('._required[data-index=name]>.admin__field-label span'), ':after').getPropertyValue('content');"/> - <element name="RequiredFieldIndicatorColor" type="text" selector=" return window.getComputedStyle(document.querySelector('._required[data-index=name]>.admin__field-label span'), ':after').getPropertyValue('color');"/> - <element name="categoryNameUseDefault" type="checkbox" selector="input[name='use_default[name]']"/> - <element name="ContentTab" type="input" selector="input[name='name']"/> - <element name="FieldError" type="text" selector=".admin__field-error[data-bind='attr: {for: {{field}}}, text: error']" parameterized="true"/> - <element name="panelFieldControl" type="input" selector='//aside//div[@data-index="{{arg1}}"]/descendant::*[@name="{{arg2}}"]' parameterized="true"/> - <element name="productsInCategory" type="input" selector="div[data-index='assign_products']" timeout="30"/> - </section> - <section name="CategoryContentSection"> - <element name="SelectFromGalleryBtn" type="button" selector="//label[text()='Select from Gallery']"/> - <element name="ImagePlaceHolder" type="button" selector=".file-uploader-summary.product-image-wrapper"/> - <element name="Upload" type="button" selector=".file-uploader-area input"/> - </section> - <section name="CategoryDesignSection"> - <element name="DesignTab" type="button" selector="//strong[@class='admin__collapsible-title']//span[text()='Design']"/> - <element name="LayoutDropdown" type="select" selector="select[name='page_layout']"/> - </section> - <section name="CategoryDisplaySettingsSection"> - <element name="DisplaySettingTab" type="button" selector="//strong[@class='admin__collapsible-title']//span[text()='Display Settings']"/> - <element name="layeredNavigationPriceInput" type="input" selector="input[name='filter_price_range']"/> - <element name="FieldError" type="text" selector=".admin__field-error[data-bind='attr: {for: {{field}}}, text: error']" parameterized="true"/> - <element name="filterPriceRangeUseConfig" type="checkbox" selector="input[name='use_config[filter_price_range]']"/> - <element name="RequiredFieldIndicator" type="text" selector=" return window.getComputedStyle(document.querySelector('._required[data-index={{arg1}}]>.admin__field-label span'), ':after').getPropertyValue('content');" parameterized="true"/> - <element name="displayMode" type="button" selector="select[name='display_mode']"/> - <element name="anchor" type="checkbox" selector="input[name='is_anchor']"/> - <element name="productListCheckBox" type="checkbox" selector="input[name='use_config[available_sort_by]']" /> - <element name="productList" type="text" selector="select[name='available_sort_by']"/> - <element name="defaultProductLisCheckBox" type="checkbox" selector="input[name='use_config[default_sort_by]']"/> - <element name="defaultProductList" type="text" selector="select[name='default_sort_by']"/> - <element name="layeredNavigationPriceCheckBox" type="checkbox" selector="input[name='use_config[filter_price_range]']"/> - </section> - <section name="CatalogWYSIWYGSection"> - <element name="ShowHideBtn" type="button" selector="#togglecategory_form_description"/> - <element name="TinyMCE4" type="text" selector=".mce-branding"/> - <element name="Style" type="button" selector=".mce-txt" /> - <element name="Bold" type="button" selector=".mce-i-bold" /> - <element name="Italic" type="button" selector=".mce-i-italic" /> - <element name="Underline" type="button" selector=".mce-i-underline" /> - <element name="AlignLeft" type="button" selector=".mce-i-alignleft" /> - <element name="AlignCenter" type="button" selector=".mce-i-aligncenter" /> - <element name="AlignRight" type="button" selector=".mce-i-alignright" /> - <element name="Bullet" type="button" selector=".mce-i-bullist" /> - <element name="Numlist" type="button" selector=".mce-i-numlist" /> - <element name="InsertLink" type="button" selector=".mce-i-link" /> - <element name="InsertImage" type="button" selector=".mce-i-image" /> - <element name="InsertTable" type="button" selector=".mce-i-table" /> - <element name="SpecialCharacter" type="button" selector=".mce-i-charmap"/> - <element name="InsertImageIcon" type="button" selector=".mce-i-image"/> - <element name="Browse" type="button" selector=".mce-i-browse"/> - <element name="BrowseUploadImage" type="file" selector=".fileupload" /> - <element name="image" type="text" selector="//small[text()='{{var1}}']" parameterized="true"/> - <element name="imageSelected" type="text" selector="//small[text()='{{var1}}']/parent::*[@class='filecnt selected']" parameterized="true"/> - <element name="ImageSource" type="input" selector=".mce-combobox.mce-abs-layout-item.mce-last.mce-has-open" /> - <element name="ImageDescription" type="input" selector=".mce-textbox.mce-abs-layout-item.mce-last" /> - <element name="Height" type="input" selector=".mce-textbox.mce-abs-layout-item.mce-first" /> - <element name="UploadImage" type="file" selector=".fileupload" /> - <element name="OkBtn" type="button" selector="//span[text()='Ok']"/> - <element name="InsertFile" type="text" selector="#insert_files"/> - <element name="CreateFolder" type="button" selector="#new_folder" /> - <element name="DeleteSelectedBtn" type="text" selector="#delete_files"/> - <element name="CancelBtn" type="button" selector="#cancel" /> - <element name="FolderName" type="button" selector="input[data-role='promptField']" /> - <element name="AcceptFolderName" type="button" selector=".action-primary.action-accept" /> - <element name="StorageRootArrow" type="button" selector="#root > .jstree-icon" /> - <element name="checkIfArrowExpand" type="button" selector="//li[@id='root' and contains(@class,'jstree-closed')]" /> - <element name="confirmDelete" type="button" selector=".action-primary.action-accept" /> - </section> -</sections> diff --git a/app/code/Magento/Catalog/Test/Mftf/Section/AdminCategoryBasicFieldSection/AdminCategoryBasicFieldSection.xml b/app/code/Magento/Catalog/Test/Mftf/Section/AdminCategoryBasicFieldSection/AdminCategoryBasicFieldSection.xml new file mode 100644 index 0000000000000..aff7ffe4d5763 --- /dev/null +++ b/app/code/Magento/Catalog/Test/Mftf/Section/AdminCategoryBasicFieldSection/AdminCategoryBasicFieldSection.xml @@ -0,0 +1,26 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + /** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +--> +<sections xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Page/etc/SectionObject.xsd"> + <section name="AdminCategoryBasicFieldSection"> + <element name="IncludeInMenu" type="checkbox" selector="input[name='include_in_menu']"/> + <element name="includeInMenuLabel" type="text" selector="input[name='include_in_menu']+label"/> + <element name="includeInMenuUseDefault" type="checkbox" selector="input[name='use_default[include_in_menu]']"/> + <element name="EnableCategory" type="checkbox" selector="input[name='is_active']"/> + <element name="enableCategoryLabel" type="text" selector="input[name='is_active']+label"/> + <element name="enableUseDefault" type="checkbox" selector="input[name='use_default[is_active]']"/> + <element name="CategoryNameInput" type="input" selector="input[name='name']"/> + <element name="RequiredFieldIndicator" type="text" selector=" return window.getComputedStyle(document.querySelector('._required[data-index=name]>.admin__field-label span'), ':after').getPropertyValue('content');"/> + <element name="RequiredFieldIndicatorColor" type="text" selector=" return window.getComputedStyle(document.querySelector('._required[data-index=name]>.admin__field-label span'), ':after').getPropertyValue('color');"/> + <element name="categoryNameUseDefault" type="checkbox" selector="input[name='use_default[name]']"/> + <element name="ContentTab" type="input" selector="input[name='name']"/> + <element name="FieldError" type="text" selector=".admin__field-error[data-bind='attr: {for: {{field}}}, text: error']" parameterized="true"/> + <element name="panelFieldControl" type="input" selector="//aside//div[@data-index="{{arg1}}"]/descendant::*[@name="{{arg2}}"]" parameterized="true"/> + <element name="productsInCategory" type="input" selector="div[data-index='assign_products']" timeout="30"/> + </section> +</sections> diff --git a/app/code/Magento/Catalog/Test/Mftf/Section/AdminCategoryBasicFieldSection/CatalogWYSIWYGSection.xml b/app/code/Magento/Catalog/Test/Mftf/Section/AdminCategoryBasicFieldSection/CatalogWYSIWYGSection.xml new file mode 100644 index 0000000000000..00656608ed9b8 --- /dev/null +++ b/app/code/Magento/Catalog/Test/Mftf/Section/AdminCategoryBasicFieldSection/CatalogWYSIWYGSection.xml @@ -0,0 +1,46 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + /** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +--> +<sections xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Page/etc/SectionObject.xsd"> + <section name="CatalogWYSIWYGSection"> + <element name="ShowHideBtn" type="button" selector="#togglecategory_form_description"/> + <element name="TinyMCE4" type="text" selector=".mce-branding"/> + <element name="Style" type="button" selector=".mce-txt"/> + <element name="Bold" type="button" selector=".mce-i-bold"/> + <element name="Italic" type="button" selector=".mce-i-italic"/> + <element name="Underline" type="button" selector=".mce-i-underline"/> + <element name="AlignLeft" type="button" selector=".mce-i-alignleft"/> + <element name="AlignCenter" type="button" selector=".mce-i-aligncenter"/> + <element name="AlignRight" type="button" selector=".mce-i-alignright"/> + <element name="Bullet" type="button" selector=".mce-i-bullist"/> + <element name="Numlist" type="button" selector=".mce-i-numlist"/> + <element name="InsertLink" type="button" selector=".mce-i-link"/> + <element name="InsertImage" type="button" selector=".mce-i-image"/> + <element name="InsertTable" type="button" selector=".mce-i-table"/> + <element name="SpecialCharacter" type="button" selector=".mce-i-charmap"/> + <element name="InsertImageIcon" type="button" selector=".mce-i-image"/> + <element name="Browse" type="button" selector=".mce-i-browse"/> + <element name="BrowseUploadImage" type="file" selector=".fileupload"/> + <element name="image" type="text" selector="//small[text()='{{var1}}']" parameterized="true"/> + <element name="imageSelected" type="text" selector="//small[text()='{{var1}}']/parent::*[@class='filecnt selected']" parameterized="true"/> + <element name="ImageSource" type="input" selector=".mce-combobox.mce-abs-layout-item.mce-last.mce-has-open"/> + <element name="ImageDescription" type="input" selector=".mce-textbox.mce-abs-layout-item.mce-last"/> + <element name="Height" type="input" selector=".mce-textbox.mce-abs-layout-item.mce-first"/> + <element name="UploadImage" type="file" selector=".fileupload"/> + <element name="OkBtn" type="button" selector="//span[text()='Ok']"/> + <element name="InsertFile" type="text" selector="#insert_files"/> + <element name="CreateFolder" type="button" selector="#new_folder"/> + <element name="DeleteSelectedBtn" type="text" selector="#delete_files"/> + <element name="CancelBtn" type="button" selector="#cancel"/> + <element name="FolderName" type="button" selector="input[data-role='promptField']"/> + <element name="AcceptFolderName" type="button" selector=".action-primary.action-accept"/> + <element name="StorageRootArrow" type="button" selector="#root > .jstree-icon"/> + <element name="checkIfArrowExpand" type="button" selector="//li[@id='root' and contains(@class,'jstree-closed')]"/> + <element name="confirmDelete" type="button" selector=".action-primary.action-accept"/> + </section> +</sections> diff --git a/app/code/Magento/Catalog/Test/Mftf/Section/AdminCategoryBasicFieldSection/CategoryContentSection.xml b/app/code/Magento/Catalog/Test/Mftf/Section/AdminCategoryBasicFieldSection/CategoryContentSection.xml new file mode 100644 index 0000000000000..f1876bc7c5056 --- /dev/null +++ b/app/code/Magento/Catalog/Test/Mftf/Section/AdminCategoryBasicFieldSection/CategoryContentSection.xml @@ -0,0 +1,15 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + /** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +--> +<sections xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Page/etc/SectionObject.xsd"> + <section name="CategoryContentSection"> + <element name="SelectFromGalleryBtn" type="button" selector="//label[text()='Select from Gallery']"/> + <element name="ImagePlaceHolder" type="button" selector=".file-uploader-summary.product-image-wrapper"/> + <element name="Upload" type="button" selector=".file-uploader-area input"/> + </section> +</sections> diff --git a/app/code/Magento/Catalog/Test/Mftf/Section/AdminCategoryBasicFieldSection/CategoryDesignSection.xml b/app/code/Magento/Catalog/Test/Mftf/Section/AdminCategoryBasicFieldSection/CategoryDesignSection.xml new file mode 100644 index 0000000000000..3e6e00b845401 --- /dev/null +++ b/app/code/Magento/Catalog/Test/Mftf/Section/AdminCategoryBasicFieldSection/CategoryDesignSection.xml @@ -0,0 +1,14 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + /** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +--> +<sections xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Page/etc/SectionObject.xsd"> + <section name="CategoryDesignSection"> + <element name="DesignTab" type="button" selector="//strong[@class='admin__collapsible-title']//span[text()='Design']"/> + <element name="LayoutDropdown" type="select" selector="select[name='page_layout']"/> + </section> +</sections> diff --git a/app/code/Magento/Catalog/Test/Mftf/Section/AdminCategoryBasicFieldSection/CategoryDisplaySettingsSection.xml b/app/code/Magento/Catalog/Test/Mftf/Section/AdminCategoryBasicFieldSection/CategoryDisplaySettingsSection.xml new file mode 100644 index 0000000000000..65cc6084d7509 --- /dev/null +++ b/app/code/Magento/Catalog/Test/Mftf/Section/AdminCategoryBasicFieldSection/CategoryDisplaySettingsSection.xml @@ -0,0 +1,25 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + /** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +--> +<sections xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Page/etc/SectionObject.xsd"> + <section name="CategoryDisplaySettingsSection"> + <element name="DisplaySettingTab" type="button" selector="//strong[@class='admin__collapsible-title']//span[text()='Display Settings']"/> + <element name="layeredNavigationPriceInput" type="input" selector="input[name='filter_price_range']"/> + <element name="FieldError" type="text" selector=".admin__field-error[data-bind='attr: {for: {{field}}}, text: error']" parameterized="true"/> + <element name="filterPriceRangeUseConfig" type="checkbox" selector="input[name='use_config[filter_price_range]']"/> + <element name="RequiredFieldIndicator" type="text" selector=" return window.getComputedStyle(document.querySelector('._required[data-index={{arg1}}]>.admin__field-label span'), ':after').getPropertyValue('content');" parameterized="true"/> + <element name="displayMode" type="button" selector="select[name='display_mode']"/> + <element name="anchor" type="checkbox" selector="input[name='is_anchor']"/> + <element name="anchorLabel" type="text" selector="input[name='is_anchor']+label"/> + <element name="productListCheckBox" type="checkbox" selector="input[name='use_config[available_sort_by]']"/> + <element name="productList" type="text" selector="select[name='available_sort_by']"/> + <element name="defaultProductLisCheckBox" type="checkbox" selector="input[name='use_config[default_sort_by]']"/> + <element name="defaultProductList" type="text" selector="select[name='default_sort_by']"/> + <element name="layeredNavigationPriceCheckBox" type="checkbox" selector="input[name='use_config[filter_price_range]']"/> + </section> +</sections> diff --git a/app/code/Magento/Catalog/Test/Mftf/Section/AdminCategoryProductsSection.xml b/app/code/Magento/Catalog/Test/Mftf/Section/AdminCategoryProductsSection.xml index 8a993a74a58d1..1627fb9512e64 100644 --- a/app/code/Magento/Catalog/Test/Mftf/Section/AdminCategoryProductsSection.xml +++ b/app/code/Magento/Catalog/Test/Mftf/Section/AdminCategoryProductsSection.xml @@ -9,8 +9,7 @@ <sections xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="urn:magento:mftf:Page/etc/SectionObject.xsd"> <section name="AdminCategoryProductsSection"> - <element name="sectionHeader" type="button" selector="div[data-index='assign_products']" timeout="30"/> - <element name="addProducts" type="button" selector="#catalog_category_add_product_tabs" timeout="30"/> - <element name="addProductsDisabled" type="button" selector="#catalog_category_add_product_tabs[disabled]" timeout="30"/> + <element name="sectionHeader" type="button" selector="div[data-index='assign_products'] .fieldset-wrapper-title" timeout="30"/> + <element name="sectionBody" type="button" selector="div[data-index='assign_products'] .admin__fieldset-wrapper-content" timeout="30"/> </section> -</sections> \ No newline at end of file +</sections> diff --git a/app/code/Magento/Catalog/Test/Mftf/Section/AdminCategorySEOSection.xml b/app/code/Magento/Catalog/Test/Mftf/Section/AdminCategorySEOSection.xml index b5d5d61f6468b..bde7a94662d04 100644 --- a/app/code/Magento/Catalog/Test/Mftf/Section/AdminCategorySEOSection.xml +++ b/app/code/Magento/Catalog/Test/Mftf/Section/AdminCategorySEOSection.xml @@ -9,7 +9,8 @@ <sections xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="urn:magento:mftf:Page/etc/SectionObject.xsd"> <section name="AdminCategorySEOSection"> - <element name="SectionHeader" type="button" selector="div[data-index='search_engine_optimization']" timeout="30"/> + <element name="SectionHeader" type="button" selector="div[data-index='search_engine_optimization'] .fieldset-wrapper-title" timeout="30"/> + <element name="sectionBody" type="text" selector="div[data-index='search_engine_optimization'] .admin__fieldset-wrapper-content"/> <element name="UrlKeyInput" type="input" selector="input[name='url_key']"/> <element name="UrlKeyDefaultValueCheckbox" type="button" selector="input[name='use_default[url_key]']"/> <element name="UrlKeyRedirectCheckbox" type="button" selector="[data-index='url_key_create_redirect'] input[type='checkbox']"/> diff --git a/app/code/Magento/Catalog/Test/Mftf/Section/AdminCategorySidebarTreeSection.xml b/app/code/Magento/Catalog/Test/Mftf/Section/AdminCategorySidebarTreeSection.xml index 304c34b404ea5..c35e775152ac9 100644 --- a/app/code/Magento/Catalog/Test/Mftf/Section/AdminCategorySidebarTreeSection.xml +++ b/app/code/Magento/Catalog/Test/Mftf/Section/AdminCategorySidebarTreeSection.xml @@ -16,6 +16,7 @@ <element name="categoryTreeRoot" type="text" selector="div.x-tree-root-node>li.x-tree-node:first-of-type>div.x-tree-node-el:first-of-type" timeout="30"/> <element name="categoryInTree" type="text" selector="//a/span[contains(text(), '{{name}}')]" parameterized="true" timeout="30"/> <element name="categoryInTreeUnderRoot" type="text" selector="//li/ul/li[@class='x-tree-node']/div/a/span[contains(text(), '{{name}}')]" parameterized="true"/> + <element name="childCategoryUnderParent" type="text" selector="//li/ul/li[@class='x-tree-node']/div/a/span[contains(text(), '{{parentCategoryName}}')]/../../../ul/li[@class='x-tree-node']/div/a/span[contains(text(), '{{childCategoryName}}')]" parameterized="true"/> <element name="lastCreatedCategory" type="block" selector=".x-tree-root-ct li li:last-child" /> <element name="treeContainer" type="block" selector=".tree-holder" /> <element name="expandRootCategory" type="text" selector="img.x-tree-elbow-end-plus"/> diff --git a/app/code/Magento/Catalog/Test/Mftf/Section/AdminCreateProductAttributeSection.xml b/app/code/Magento/Catalog/Test/Mftf/Section/AdminCreateProductAttributeSection.xml deleted file mode 100644 index 0934e39dcb062..0000000000000 --- a/app/code/Magento/Catalog/Test/Mftf/Section/AdminCreateProductAttributeSection.xml +++ /dev/null @@ -1,106 +0,0 @@ -<?xml version="1.0" encoding="UTF-8"?> -<!-- - /** - * Copyright © Magento, Inc. All rights reserved. - * See COPYING.txt for license details. - */ ---> - -<sections xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" - xsi:noNamespaceSchemaLocation="urn:magento:mftf:Page/etc/SectionObject.xsd"> - <section name="AttributePropertiesSection"> - <element name="propertiesTab" type="button" selector="#product_attribute_tabs_main"/> - <element name="DefaultLabel" type="input" selector="#attribute_label"/> - <element name="InputType" type="select" selector="#frontend_input"/> - <element name="ValueRequired" type="select" selector="#is_required"/> - <element name="UpdateProductPreviewImage" type="select" selector="[name='update_product_preview_image']"/> - <element name="AdvancedProperties" type="button" selector="#advanced_fieldset-wrapper"/> - <element name="DefaultValue" type="input" selector="#default_value_text"/> - <element name="Scope" type="select" selector="#is_global"/> - <element name="Save" type="button" selector="#save" timeout="30"/> - <element name="DeleteAttribute" type="button" selector="#delete" timeout="30"/> - <element name="SaveAndEdit" type="button" selector="#save_and_edit_button" timeout="30"/> - <element name="TinyMCE4" type="button" selector="//span[text()='Default Value']/parent::label/following-sibling::div//*[contains(@class,'mce-branding')]"/> - <element name="checkIfTabOpen" selector="//div[@id='advanced_fieldset-wrapper' and not(contains(@class,'opened'))]" type="button"/> - <element name="useInLayeredNavigation" type="select" selector="#is_filterable"/> - <element name="addSwatch" type="button" selector="#add_new_swatch_text_option_button"/> - <element name="dropdownAddOptions" type="button" selector="#add_new_option_button" timeout="30"/> - <!-- Manage Options nth child--> - <element name="dropdownNthOptionIsDefault" type="checkbox" selector="tbody[data-role='options-container'] tr:nth-child({{var}}) .input-radio" parameterized="true"/> - <element name="dropdownNthOptionAdmin" type="textarea" selector="tbody[data-role='options-container'] tr:nth-child({{var}}) td:nth-child(3) input" parameterized="true"/> - <element name="dropdownNthOptionDefaultStoreView" type="textarea" selector="tbody[data-role='options-container'] tr:nth-child({{var}}) td:nth-child(4) input" parameterized="true"/> - <element name="dropdownNthOptionDelete" type="button" selector="tbody[data-role='options-container'] tr:nth-child({{var}}) button[title='Delete']" parameterized="true"/> - </section> - <section name="AttributeDeleteModalSection"> - <element name="confirm" type="button" selector=".modal-popup.confirm .action-accept"/> - <element name="cancel" type="button" selector=".modal-popup.confirm .action-dismiss"/> - </section> - <section name="AttributeManageSwatchSection"> - <element name="swatchField" type="input" selector="//th[contains(@class, 'col-swatch')]/span[contains(text(), '{{arg}}')]/ancestor::thead/following-sibling::tbody//input[@placeholder='Swatch']" parameterized="true"/> - <element name="descriptionField" type="input" selector="//th[contains(@class, 'col-swatch')]/span[contains(text(), '{{arg}}')]/ancestor::thead/following-sibling::tbody//input[@placeholder='Description']" parameterized="true"/> - </section> - <section name="AttributeOptionsSection"> - <element name="AddOption" type="button" selector="#add_new_option_button"/> - </section> - <section name="StorefrontPropertiesSection"> - <element name="PageTitle" type="text" selector="//span[text()='Storefront Properties']" /> - <element name="StoreFrontPropertiesTab" selector="#product_attribute_tabs_front" type="button"/> - <element name="EnableWYSIWYG" type="select" selector="#enabled"/> - <element name="useForPromoRuleConditions" type="select" selector="#is_used_for_promo_rules"/> - <element name="StorefrontPropertiesSectionToggle" type="button" selector="#front_fieldset-wrapper"/> - <element name="visibleOnCatalogPagesOnStorefront" type="select" selector="#is_visible_on_front"/> - </section> - <section name="WYSIWYGProductAttributeSection"> - <element name="ShowHideBtn" type="button" selector="#toggledefault_value_texteditor"/> - <element name="InsertImageBtn" type="button" selector=".scalable.action-add-image.plugin"/> - <element name="InsertImageIcon" type="button" selector=".mce-i-image"/> - <element name="InsertWidgetBtn" type="button" selector=".action-add-widget"/> - <element name="InsertWidgetIcon" type="button" selector="div[aria-label='Insert Widget']"/> - <element name="InsertVariableBtn" type="button" selector=".scalable.add-variable.plugin"/> - <element name="InsertVariableIcon" type="button" selector="div[aria-label='Insert Variable']"/> - <element name="Browse" type="button" selector=".mce-i-browse"/> - <element name="BrowseUploadImage" type="file" selector=".fileupload" /> - <element name="image" type="text" selector="//small[text()='{{var1}}']" parameterized="true"/> - <element name="imageSelected" type="text" selector="//small[text()='{{var1}}']/parent::*[@class='filecnt selected']" parameterized="true"/> - <element name="ImageSource" type="input" selector=".mce-combobox.mce-abs-layout-item.mce-last.mce-has-open" /> - <element name="ImageDescription" type="input" selector=".mce-textbox.mce-abs-layout-item.mce-last" /> - <element name="Height" type="input" selector=".mce-textbox.mce-abs-layout-item.mce-first" /> - <element name="UploadImage" type="file" selector=".fileupload" /> - <element name="OkBtn" type="button" selector="//span[text()='Ok']"/> - <element name="InsertFile" type="text" selector="#insert_files"/> - <element name="CreateFolder" type="button" selector="#new_folder" /> - <element name="DeleteSelectedBtn" type="text" selector="#delete_files"/> - <element name="CancelBtn" type="button" selector="#cancel" /> - <element name="Style" type="button" selector=".mce-txt" /> - <element name="Bold" type="button" selector=".mce-i-bold" /> - <element name="Italic" type="button" selector=".mce-i-italic" /> - <element name="Underline" type="button" selector=".mce-i-underline" /> - <element name="AlignLeft" type="button" selector=".mce-i-alignleft" /> - <element name="AlignCenter" type="button" selector=".mce-i-aligncenter" /> - <element name="AlignRight" type="button" selector=".mce-i-alignright" /> - <element name="Bullet" type="button" selector=".mce-i-bullist" /> - <element name="Numlist" type="button" selector=".mce-i-numlist" /> - <element name="InsertLink" type="button" selector=".mce-i-link" /> - <element name="InsertImage" type="button" selector=".mce-i-image" /> - <element name="InsertTable" type="button" selector=".mce-i-table" /> - <element name="SpecialCharacter" type="button" selector=".mce-i-charmap" /> - <element name="TextArea" type="input" selector="#default_value_textarea" /> - </section> - <section name="AdvancedAttributePropertiesSection"> - <element name="AdvancedAttributePropertiesSectionToggle" - type="button" selector="#advanced_fieldset-wrapper"/> - <element name="AttributeCode" type="text" selector="#attribute_code"/> - <element name="DefaultValueText" type="textarea" selector="#default_value_text"/> - <element name="DefaultValueTextArea" type="textarea" selector="#default_value_textarea"/> - <element name="DefaultValueDate" type="textarea" selector="#default_value_date"/> - <element name="defaultValueDatetime" type="date" selector="#default_value_datetime"/> - <element name="DefaultValueYesNo" type="textarea" selector="#default_value_yesno"/> - <element name="Scope" type="select" selector="#is_global"/> - <element name="UniqueValue" type="select" selector="#is_unique"/> - <element name="AddToColumnOptions" type="select" selector="#is_used_in_grid"/> - <element name="UseInFilterOptions" type="select" selector="#is_filterable_in_grid"/> - <element name="UseInProductListing" type="select" selector="#used_in_product_listing"/> - <element name="UseInSearch" type="select" selector="#is_searchable"/> - <element name="VisibleInAdvancedSearch" type="select" selector="#is_visible_in_advanced_search"/> - </section> -</sections> diff --git a/app/code/Magento/Catalog/Test/Mftf/Section/AdminCreateProductAttributeSection/AdvancedAttributePropertiesSection.xml b/app/code/Magento/Catalog/Test/Mftf/Section/AdminCreateProductAttributeSection/AdvancedAttributePropertiesSection.xml new file mode 100644 index 0000000000000..bcd05e139d17c --- /dev/null +++ b/app/code/Magento/Catalog/Test/Mftf/Section/AdminCreateProductAttributeSection/AdvancedAttributePropertiesSection.xml @@ -0,0 +1,26 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + /** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +--> +<sections xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Page/etc/SectionObject.xsd"> + <section name="AdvancedAttributePropertiesSection"> + <element name="AdvancedAttributePropertiesSectionToggle" type="button" selector="#advanced_fieldset-wrapper"/> + <element name="AttributeCode" type="text" selector="#attribute_code"/> + <element name="DefaultValueText" type="textarea" selector="#default_value_text"/> + <element name="DefaultValueTextArea" type="textarea" selector="#default_value_textarea"/> + <element name="DefaultValueDate" type="textarea" selector="#default_value_date"/> + <element name="defaultValueDatetime" type="date" selector="#default_value_datetime"/> + <element name="DefaultValueYesNo" type="textarea" selector="#default_value_yesno"/> + <element name="Scope" type="select" selector="#is_global"/> + <element name="UniqueValue" type="select" selector="#is_unique"/> + <element name="AddToColumnOptions" type="select" selector="#is_used_in_grid"/> + <element name="UseInFilterOptions" type="select" selector="#is_filterable_in_grid"/> + <element name="UseInProductListing" type="select" selector="#used_in_product_listing"/> + <element name="UseInSearch" type="select" selector="#is_searchable"/> + <element name="VisibleInAdvancedSearch" type="select" selector="#is_visible_in_advanced_search"/> + </section> +</sections> diff --git a/app/code/Magento/Catalog/Test/Mftf/Section/AdminCreateProductAttributeSection/AttributeDeleteModalSection.xml b/app/code/Magento/Catalog/Test/Mftf/Section/AdminCreateProductAttributeSection/AttributeDeleteModalSection.xml new file mode 100644 index 0000000000000..7f7be6e13516c --- /dev/null +++ b/app/code/Magento/Catalog/Test/Mftf/Section/AdminCreateProductAttributeSection/AttributeDeleteModalSection.xml @@ -0,0 +1,14 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + /** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +--> +<sections xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Page/etc/SectionObject.xsd"> + <section name="AttributeDeleteModalSection"> + <element name="confirm" type="button" selector=".modal-popup.confirm .action-accept"/> + <element name="cancel" type="button" selector=".modal-popup.confirm .action-dismiss"/> + </section> +</sections> diff --git a/app/code/Magento/Catalog/Test/Mftf/Section/AdminCreateProductAttributeSection/AttributeManageSwatchSection.xml b/app/code/Magento/Catalog/Test/Mftf/Section/AdminCreateProductAttributeSection/AttributeManageSwatchSection.xml new file mode 100644 index 0000000000000..6592f8e20cff5 --- /dev/null +++ b/app/code/Magento/Catalog/Test/Mftf/Section/AdminCreateProductAttributeSection/AttributeManageSwatchSection.xml @@ -0,0 +1,14 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + /** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +--> +<sections xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Page/etc/SectionObject.xsd"> + <section name="AttributeManageSwatchSection"> + <element name="swatchField" type="input" selector="//th[contains(@class, 'col-swatch')]/span[contains(text(), '{{arg}}')]/ancestor::thead/following-sibling::tbody//input[@placeholder='Swatch']" parameterized="true"/> + <element name="descriptionField" type="input" selector="//th[contains(@class, 'col-swatch')]/span[contains(text(), '{{arg}}')]/ancestor::thead/following-sibling::tbody//input[@placeholder='Description']" parameterized="true"/> + </section> +</sections> diff --git a/app/code/Magento/Catalog/Test/Mftf/Section/AdminCreateProductAttributeSection/AttributeOptionsSection.xml b/app/code/Magento/Catalog/Test/Mftf/Section/AdminCreateProductAttributeSection/AttributeOptionsSection.xml new file mode 100644 index 0000000000000..ad58b19de1adc --- /dev/null +++ b/app/code/Magento/Catalog/Test/Mftf/Section/AdminCreateProductAttributeSection/AttributeOptionsSection.xml @@ -0,0 +1,13 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + /** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +--> +<sections xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Page/etc/SectionObject.xsd"> + <section name="AttributeOptionsSection"> + <element name="AddOption" type="button" selector="#add_new_option_button"/> + </section> +</sections> diff --git a/app/code/Magento/Catalog/Test/Mftf/Section/AdminCreateProductAttributeSection/AttributePropertiesSection.xml b/app/code/Magento/Catalog/Test/Mftf/Section/AdminCreateProductAttributeSection/AttributePropertiesSection.xml new file mode 100644 index 0000000000000..e3a59b37ab146 --- /dev/null +++ b/app/code/Magento/Catalog/Test/Mftf/Section/AdminCreateProductAttributeSection/AttributePropertiesSection.xml @@ -0,0 +1,34 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + /** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +--> +<sections xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Page/etc/SectionObject.xsd"> + <section name="AttributePropertiesSection"> + <element name="propertiesTab" type="button" selector="#product_attribute_tabs_main"/> + <element name="DefaultLabel" type="input" selector="#attribute_label"/> + <element name="InputType" type="select" selector="#frontend_input"/> + <element name="ValueRequired" type="select" selector="#is_required"/> + <element name="UpdateProductPreviewImage" type="select" selector="[name='update_product_preview_image']"/> + <element name="AdvancedProperties" type="button" selector="#advanced_fieldset-wrapper"/> + <element name="DefaultValue" type="input" selector="#default_value_text"/> + <element name="Scope" type="select" selector="#is_global"/> + <element name="Save" type="button" selector="#save" timeout="30"/> + <element name="DeleteAttribute" type="button" selector="#delete" timeout="30"/> + <element name="SaveAndEdit" type="button" selector="#save_and_edit_button" timeout="30"/> + <element name="TinyMCE4" type="button" selector="//span[text()='Default Value']/parent::label/following-sibling::div//*[contains(@class,'mce-branding')]"/> + <element name="checkIfTabOpen" selector="//div[@id='advanced_fieldset-wrapper' and not(contains(@class,'opened'))]" type="button"/> + <element name="useInLayeredNavigation" type="select" selector="#is_filterable"/> + <element name="addSwatch" type="button" selector="#add_new_swatch_text_option_button"/> + <element name="dropdownAddOptions" type="button" selector="#add_new_option_button" timeout="30"/> + <!-- Manage Options nth child--> + <element name="dropdownNthOptionIsDefault" type="checkbox" selector="tbody[data-role='options-container'] tr:nth-child({{var}}) .input-radio" parameterized="true"/> + <element name="dropdownNthOptionAdmin" type="textarea" selector="tbody[data-role='options-container'] tr:nth-child({{var}}) td:nth-child(3) input" parameterized="true"/> + <element name="dropdownNthOptionDefaultStoreView" type="textarea" selector="tbody[data-role='options-container'] tr:nth-child({{var}}) td:nth-child(4) input" parameterized="true"/> + <element name="dropdownNthOptionDelete" type="button" selector="tbody[data-role='options-container'] tr:nth-child({{var}}) button[title='Delete']" parameterized="true"/> + <element name="attributeLabelValidationError" type="text" selector=".field-attribute_label .mage-error"/> + </section> +</sections> diff --git a/app/code/Magento/Catalog/Test/Mftf/Section/AdminCreateProductAttributeSection/StorefrontPropertiesSection.xml b/app/code/Magento/Catalog/Test/Mftf/Section/AdminCreateProductAttributeSection/StorefrontPropertiesSection.xml new file mode 100644 index 0000000000000..9e6e99ff2e59e --- /dev/null +++ b/app/code/Magento/Catalog/Test/Mftf/Section/AdminCreateProductAttributeSection/StorefrontPropertiesSection.xml @@ -0,0 +1,18 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + /** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +--> +<sections xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Page/etc/SectionObject.xsd"> + <section name="StorefrontPropertiesSection"> + <element name="PageTitle" type="text" selector="//span[text()='Storefront Properties']"/> + <element name="StoreFrontPropertiesTab" selector="#product_attribute_tabs_front" type="button"/> + <element name="EnableWYSIWYG" type="select" selector="#enabled"/> + <element name="useForPromoRuleConditions" type="select" selector="#is_used_for_promo_rules"/> + <element name="StorefrontPropertiesSectionToggle" type="button" selector="#front_fieldset-wrapper"/> + <element name="visibleOnCatalogPagesOnStorefront" type="select" selector="#is_visible_on_front"/> + </section> +</sections> diff --git a/app/code/Magento/Catalog/Test/Mftf/Section/AdminCreateProductAttributeSection/WYSIWYGProductAttributeSection.xml b/app/code/Magento/Catalog/Test/Mftf/Section/AdminCreateProductAttributeSection/WYSIWYGProductAttributeSection.xml new file mode 100644 index 0000000000000..fc3c07b9634df --- /dev/null +++ b/app/code/Magento/Catalog/Test/Mftf/Section/AdminCreateProductAttributeSection/WYSIWYGProductAttributeSection.xml @@ -0,0 +1,46 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + /** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +--> +<sections xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Page/etc/SectionObject.xsd"> + <section name="WYSIWYGProductAttributeSection"> + <element name="ShowHideBtn" type="button" selector="#toggledefault_value_texteditor"/> + <element name="InsertImageBtn" type="button" selector=".scalable.action-add-image.plugin"/> + <element name="InsertImageIcon" type="button" selector=".mce-i-image"/> + <element name="InsertWidgetBtn" type="button" selector=".action-add-widget"/> + <element name="InsertWidgetIcon" type="button" selector="div[aria-label='Insert Widget']"/> + <element name="InsertVariableBtn" type="button" selector=".scalable.add-variable.plugin"/> + <element name="InsertVariableIcon" type="button" selector="div[aria-label='Insert Variable']"/> + <element name="Browse" type="button" selector=".mce-i-browse"/> + <element name="BrowseUploadImage" type="file" selector=".fileupload"/> + <element name="image" type="text" selector="//small[text()='{{var1}}']" parameterized="true"/> + <element name="imageSelected" type="text" selector="//small[text()='{{var1}}']/parent::*[@class='filecnt selected']" parameterized="true"/> + <element name="ImageSource" type="input" selector=".mce-combobox.mce-abs-layout-item.mce-last.mce-has-open"/> + <element name="ImageDescription" type="input" selector=".mce-textbox.mce-abs-layout-item.mce-last"/> + <element name="Height" type="input" selector=".mce-textbox.mce-abs-layout-item.mce-first"/> + <element name="UploadImage" type="file" selector=".fileupload"/> + <element name="OkBtn" type="button" selector="//span[text()='Ok']"/> + <element name="InsertFile" type="text" selector="#insert_files"/> + <element name="CreateFolder" type="button" selector="#new_folder"/> + <element name="DeleteSelectedBtn" type="text" selector="#delete_files"/> + <element name="CancelBtn" type="button" selector="#cancel"/> + <element name="Style" type="button" selector=".mce-txt"/> + <element name="Bold" type="button" selector=".mce-i-bold"/> + <element name="Italic" type="button" selector=".mce-i-italic"/> + <element name="Underline" type="button" selector=".mce-i-underline"/> + <element name="AlignLeft" type="button" selector=".mce-i-alignleft"/> + <element name="AlignCenter" type="button" selector=".mce-i-aligncenter"/> + <element name="AlignRight" type="button" selector=".mce-i-alignright"/> + <element name="Bullet" type="button" selector=".mce-i-bullist"/> + <element name="Numlist" type="button" selector=".mce-i-numlist"/> + <element name="InsertLink" type="button" selector=".mce-i-link"/> + <element name="InsertImage" type="button" selector=".mce-i-image"/> + <element name="InsertTable" type="button" selector=".mce-i-table"/> + <element name="SpecialCharacter" type="button" selector=".mce-i-charmap"/> + <element name="TextArea" type="input" selector="#default_value_textarea"/> + </section> +</sections> diff --git a/app/code/Magento/Catalog/Test/Mftf/Section/AdminEditProductAttributesSection.xml b/app/code/Magento/Catalog/Test/Mftf/Section/AdminEditProductAttributesSection.xml index b243fbfd6034a..ad4ab57f8de2c 100644 --- a/app/code/Magento/Catalog/Test/Mftf/Section/AdminEditProductAttributesSection.xml +++ b/app/code/Magento/Catalog/Test/Mftf/Section/AdminEditProductAttributesSection.xml @@ -22,5 +22,6 @@ <element name="ProductDataMayBeLostConfirmButton" type="button" selector="//aside[contains(@class,'_show')]//button[.='Change Input Type']"/> <element name="defaultLabel" type="text" selector="//td[contains(text(), '{{attributeName}}')]/following-sibling::td[contains(@class, 'col-frontend_label')]" parameterized="true"/> <element name="formByStoreId" type="block" selector="//form[contains(@action,'store/{{store_id}}')]" parameterized="true"/> + <element name="tabButton" type="text" selector="#product_attribute_tabs a[title='{{tabName}}']" parameterized="true"/> </section> </sections> diff --git a/app/code/Magento/Catalog/Test/Mftf/Section/AdminProductAttributeManageLabelsSection.xml b/app/code/Magento/Catalog/Test/Mftf/Section/AdminProductAttributeManageLabelsSection.xml new file mode 100644 index 0000000000000..e1863576b8ecf --- /dev/null +++ b/app/code/Magento/Catalog/Test/Mftf/Section/AdminProductAttributeManageLabelsSection.xml @@ -0,0 +1,15 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + /** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +--> + +<sections xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Page/etc/SectionObject.xsd"> + <section name="AdminProductAttributeManageLabelsSection"> + <element name="DefaultStoreLabel" type="input" selector="#attribute-labels-table [name='frontend_label[1]']"/> + <element name="attributeStoreLabelValidationError" type="text" selector="#attribute-labels-table .mage-error"/> + </section> +</sections> diff --git a/app/code/Magento/Catalog/Test/Mftf/Section/AdminProductAttributeSetSection.xml b/app/code/Magento/Catalog/Test/Mftf/Section/AdminProductAttributeSetSection.xml deleted file mode 100644 index 8f635214ffffd..0000000000000 --- a/app/code/Magento/Catalog/Test/Mftf/Section/AdminProductAttributeSetSection.xml +++ /dev/null @@ -1,34 +0,0 @@ -<?xml version="1.0" encoding="UTF-8"?> -<!-- - /** - * Copyright © Magento, Inc. All rights reserved. - * See COPYING.txt for license details. - */ ---> - -<sections xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" - xsi:noNamespaceSchemaLocation="urn:magento:mftf:Page/etc/SectionObject.xsd"> - <section name="AdminProductAttributeSetSection"> - <element name="name" type="input" selector="#attribute_set_name"/> - <element name="basedOn" type="select" selector="#skeleton_set"/> - <element name="saveBtn" type="button" selector="button.save-attribute-set" timeout="30"/> - <element name="deleteBtn" type="button" selector="button[title='Delete']" timeout="30"/> - <element name="attribute" type="button" selector="//span[text()='{{var1}}']" parameterized="true"/> - <element name="addNewGroupBtn" type="button" selector="button.add" timeout="30"/> - <element name="newGroupName" type="input" selector="input[name='name']"/> - <element name="modalOk" type="button" selector="button.action-accept" timeout="30"/> - </section> - <section name="AttributeSetSection"> - <element name="Save" type="button" selector="button[title='Save']" /> - </section> - <section name="UnassignedAttributes"> - <element name="ProductAttributeName" type="text" selector="//span[text()='{{var1}}']" parameterized="true"/> - </section> - <section name="Group"> - <element name="FolderName" type="text" selector="//span[text()='{{var1}}']" parameterized="true"/> - </section> - <section name="ModifyAttributes"> - <!-- Parameter is the attribute name --> - <element name="nthExistingAttribute" type="select" selector="//*[text()='{{attributeName}}']/../../..//select" parameterized="true"/> - </section> -</sections> diff --git a/app/code/Magento/Catalog/Test/Mftf/Section/AdminProductAttributeSetSection/AdminProductAttributeSetSection.xml b/app/code/Magento/Catalog/Test/Mftf/Section/AdminProductAttributeSetSection/AdminProductAttributeSetSection.xml new file mode 100644 index 0000000000000..6790022d5fcff --- /dev/null +++ b/app/code/Magento/Catalog/Test/Mftf/Section/AdminProductAttributeSetSection/AdminProductAttributeSetSection.xml @@ -0,0 +1,20 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + /** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +--> +<sections xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Page/etc/SectionObject.xsd"> + <section name="AdminProductAttributeSetSection"> + <element name="name" type="input" selector="#attribute_set_name"/> + <element name="basedOn" type="select" selector="#skeleton_set"/> + <element name="saveBtn" type="button" selector="button.save-attribute-set" timeout="30"/> + <element name="deleteBtn" type="button" selector="button[title='Delete']" timeout="30"/> + <element name="attribute" type="button" selector="//span[text()='{{var1}}']" parameterized="true"/> + <element name="addNewGroupBtn" type="button" selector="button.add" timeout="30"/> + <element name="newGroupName" type="input" selector="input[name='name']"/> + <element name="modalOk" type="button" selector="button.action-accept" timeout="30"/> + </section> +</sections> diff --git a/app/code/Magento/Catalog/Test/Mftf/Section/AdminProductAttributeSetSection/AttributeSetSection.xml b/app/code/Magento/Catalog/Test/Mftf/Section/AdminProductAttributeSetSection/AttributeSetSection.xml new file mode 100644 index 0000000000000..879b043d13df3 --- /dev/null +++ b/app/code/Magento/Catalog/Test/Mftf/Section/AdminProductAttributeSetSection/AttributeSetSection.xml @@ -0,0 +1,13 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + /** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +--> +<sections xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Page/etc/SectionObject.xsd"> + <section name="AttributeSetSection"> + <element name="Save" type="button" selector="button[title='Save']"/> + </section> +</sections> diff --git a/app/code/Magento/Catalog/Test/Mftf/Section/AdminProductAttributeSetSection/GroupSection.xml b/app/code/Magento/Catalog/Test/Mftf/Section/AdminProductAttributeSetSection/GroupSection.xml new file mode 100644 index 0000000000000..73ea6e0a86f8d --- /dev/null +++ b/app/code/Magento/Catalog/Test/Mftf/Section/AdminProductAttributeSetSection/GroupSection.xml @@ -0,0 +1,13 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + /** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +--> +<sections xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Page/etc/SectionObject.xsd"> + <section name="Group"> + <element name="FolderName" type="text" selector="//span[text()='{{var1}}']" parameterized="true"/> + </section> +</sections> diff --git a/app/code/Magento/Catalog/Test/Mftf/Section/AdminProductAttributeSetSection/ModifyAttributesSection.xml b/app/code/Magento/Catalog/Test/Mftf/Section/AdminProductAttributeSetSection/ModifyAttributesSection.xml new file mode 100644 index 0000000000000..4e52263f05717 --- /dev/null +++ b/app/code/Magento/Catalog/Test/Mftf/Section/AdminProductAttributeSetSection/ModifyAttributesSection.xml @@ -0,0 +1,14 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + /** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +--> +<sections xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Page/etc/SectionObject.xsd"> + <section name="ModifyAttributes"> + <!-- Parameter is the attribute name --> + <element name="nthExistingAttribute" type="select" selector="//*[text()='{{attributeName}}']/../../..//select" parameterized="true"/> + </section> +</sections> diff --git a/app/code/Magento/Catalog/Test/Mftf/Section/AdminProductAttributeSetSection/UnassignedAttributesSection.xml b/app/code/Magento/Catalog/Test/Mftf/Section/AdminProductAttributeSetSection/UnassignedAttributesSection.xml new file mode 100644 index 0000000000000..b6a29a8b60b12 --- /dev/null +++ b/app/code/Magento/Catalog/Test/Mftf/Section/AdminProductAttributeSetSection/UnassignedAttributesSection.xml @@ -0,0 +1,13 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + /** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +--> +<sections xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Page/etc/SectionObject.xsd"> + <section name="UnassignedAttributes"> + <element name="ProductAttributeName" type="text" selector="//span[text()='{{var1}}']" parameterized="true"/> + </section> +</sections> diff --git a/app/code/Magento/Catalog/Test/Mftf/Section/AdminProductCustomizableOptionsSection.xml b/app/code/Magento/Catalog/Test/Mftf/Section/AdminProductCustomizableOptionsSection/AdminProductCustomizableOptionsSection.xml similarity index 84% rename from app/code/Magento/Catalog/Test/Mftf/Section/AdminProductCustomizableOptionsSection.xml rename to app/code/Magento/Catalog/Test/Mftf/Section/AdminProductCustomizableOptionsSection/AdminProductCustomizableOptionsSection.xml index fa2f9feffdf91..00ab3c67c7c93 100644 --- a/app/code/Magento/Catalog/Test/Mftf/Section/AdminProductCustomizableOptionsSection.xml +++ b/app/code/Magento/Catalog/Test/Mftf/Section/AdminProductCustomizableOptionsSection/AdminProductCustomizableOptionsSection.xml @@ -5,10 +5,10 @@ * See COPYING.txt for license details. */ --> - <sections xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" - xsi:noNamespaceSchemaLocation="urn:magento:mftf:Page/etc/SectionObject.xsd"> + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Page/etc/SectionObject.xsd"> <section name="AdminProductCustomizableOptionsSection"> + <element name="requiredFieldIndicator" type="text" selector=" return window.getComputedStyle(document.querySelector('#product_composite_configure_form_fields .admin__field.required .admin__field-label'), ':after').getPropertyValue('content');"/> <element name="checkIfCustomizableOptionsTabOpen" type="text" selector="//span[text()='Customizable Options']/parent::strong/parent::*[@data-state-collapsible='closed']" timeout="30"/> <element name="customizableOptions" type="text" selector="//strong[contains(@class, 'admin__collapsible-title')]/span[text()='Customizable Options']"/> <element name="useDefaultOptionTitle" type="text" selector="[data-index='options'] tr.data-row [data-index='title'] [name^='options_use_default']"/> @@ -34,7 +34,7 @@ <element name="clickSelectPriceType" type="select" selector="//span[text()='{{var1}}']/parent::div/parent::div/parent::div//tbody//tr[@data-repeat-index='{{var2}}']//span[text()='Price Type']/parent::label/parent::div/parent::div//select" parameterized="true"/> <element name="checkboxUseDefaultTitle" type="checkbox" selector="//span[text()='Option Title']/parent::label/parent::div/parent::div/div//input[@type='checkbox']"/> <element name="checkboxUseDefaultOption" type="checkbox" selector="//table[@data-index='values']//tbody//tr[@data-repeat-index='{{var1}}']//div[@class='admin__field-control']//input[@type='checkbox']" parameterized="true"/> - <element name="requiredCheckBox" type="checkbox" selector="input[name='product[options][{{index}}][is_require]']" parameterized="true" /> + <element name="requiredCheckBox" type="checkbox" selector="input[name='product[options][{{index}}][is_require]']" parameterized="true"/> <element name="fillOptionValueSku" type="input" selector="//span[text()='{{var1}}']/parent::div/parent::div/parent::div//tbody/tr[@data-repeat-index='{{var2}}']//span[text()='SKU']/parent::label/parent::div/parent::div//div[@class='admin__field-control']/input" parameterized="true"/> <element name="fillOptionCompatibleFileExtensions" type="input" selector="input[name='product[options][{{index}}][file_extension]']" parameterized="true"/> @@ -43,28 +43,19 @@ <element name="optionPriceTypeByTitle" type="select" selector="//*[@data-index='options']//*[@data-role='collapsible-title' and contains(., '{{optionTitle}}')]/ancestor::tr//*[@data-index='price_type']//select" parameterized="true"/> <element name="optionSkuByTitle" type="input" selector="//*[@data-index='options']//*[@data-role='collapsible-title' and contains(., '{{optionTitle}}')]/ancestor::tr//*[@data-index='sku']//input" parameterized="true"/> <element name="optionFileExtensionByTitle" type="input" selector="//*[@data-index='options']//*[@data-role='collapsible-title' and contains(., '{{optionTitle}}')]/ancestor::tr//*[@data-index='file_extension']//input" parameterized="true"/> - <element name="lastOptionTitle" type="input" selector="//*[@data-index='custom_options']//*[@data-index='options']/tbody/tr[last()]//*[contains(@class, '_required')]//input" /> - <element name="lastOptionTypeParent" type="block" selector="//*[@data-index='custom_options']//*[@data-index='options']/tbody/tr[last()]//*[contains(@class, 'admin__action-multiselect-text')]" /> + <element name="lastOptionTitle" type="input" selector="//*[@data-index='custom_options']//*[@data-index='options']/tbody/tr[last()]//*[contains(@class, '_required')]//input"/> + <element name="lastOptionTypeParent" type="block" selector="//*[@data-index='custom_options']//*[@data-index='options']/tbody/tr[last()]//*[contains(@class, 'admin__action-multiselect-text')]"/> <element name="lastOptionPrice" type="input" selector="//*[@data-index='custom_options']//*[@data-index='options']/tbody/tr[last()]//*[contains(@name, '[price]')]"/> <element name="lastOptionPriceType" type="select" selector="//*[@data-index='custom_options']//*[@data-index='options']/tbody/tr[last()]//*[contains(@name, '[price_type]')]"/> <!-- var 1 represents the option type that you want to select, i.e "radio buttons" --> - <element name="optionType" type="block" selector="//*[@data-index='custom_options']//label[text()='{{var1}}'][ancestor::*[contains(@class, '_active')]]" parameterized="true" /> + <element name="optionType" type="block" selector="//*[@data-index='custom_options']//label[text()='{{var1}}'][ancestor::*[contains(@class, '_active')]]" parameterized="true"/> <element name="addValue" type="button" selector="//*[@data-index='custom_options']//*[@data-index='options']/tbody/tr[last()]//*[@data-action='add_new_row']" timeout="30"/> - <element name="valueTitle" type="input" selector="//*[@data-index='custom_options']//*[@data-index='options']/tbody/tr[last()]//*[contains(@class, 'admin__control-table')]//tbody/tr[last()]//*[@data-index='title']//input" /> - <element name="valuePrice" type="input" selector="//*[@data-index='custom_options']//*[@data-index='options']/tbody/tr[last()]//*[contains(@class, 'admin__control-table')]//tbody/tr[last()]//*[@data-index='price']//input" /> + <element name="valueTitle" type="input" selector="//*[@data-index='custom_options']//*[@data-index='options']/tbody/tr[last()]//*[contains(@class, 'admin__control-table')]//tbody/tr[last()]//*[@data-index='title']//input"/> + <element name="valuePrice" type="input" selector="//*[@data-index='custom_options']//*[@data-index='options']/tbody/tr[last()]//*[contains(@class, 'admin__control-table')]//tbody/tr[last()]//*[@data-index='price']//input"/> <element name="optionPrice" type="input" selector="//*[@data-index='custom_options']//*[@data-index='options']/tbody/tr//*[@name='product[options][{{index}}][price]']" parameterized="true"/> <element name="optionPriceType" type="select" selector="//*[@data-index='custom_options']//*[@data-index='options']/tbody/tr//*[@name='product[options][{{var}}][price_type]']" parameterized="true"/> <element name="optionSku" type="input" selector="//*[@data-index='custom_options']//*[@data-index='options']/tbody/tr//*[@name='product[options][{{index}}][sku]']" parameterized="true"/> <element name="optionFileExtensions" type="input" selector="//*[@data-index='custom_options']//*[@data-index='options']/tbody/tr//*[@name='product[options][{{index}}][file_extension]']" parameterized="true"/> <element name="importOptions" type="button" selector="//button[@data-index='button_import']" timeout="30"/> </section> - <section name="AdminProductImportOptionsSection"> - <element name="selectProductTitle" type="text" selector="//aside[contains(@class, 'product_form_product_form_import_options_modal')]//h1[contains(text(), 'Select Product')]" timeout="30"/> - <element name="filterButton" type="button" selector="aside.product_form_product_form_import_options_modal button[data-action='grid-filter-expand']" timeout="30"/> - <element name="nameField" type="input" selector="aside.product_form_product_form_import_options_modal input[name='name']" timeout="30"/> - <element name="applyFiltersButton" type="button" selector="aside.product_form_product_form_import_options_modal button[data-action='grid-filter-apply']" timeout="30"/> - <element name="resetFiltersButton" type="button" selector="aside.product_form_product_form_import_options_modal button[data-action='grid-filter-reset']" timeout="30"/> - <element name="firstRowItemCheckbox" type="input" selector="aside.product_form_product_form_import_options_modal input[data-action='select-row']" timeout="30"/> - <element name="importButton" type="button" selector="//aside[contains(@class, 'product_form_product_form_import_options_modal')]//button[contains(@class, 'action-primary')]/span[contains(text(), 'Import')]" timeout="30"/> - </section> </sections> diff --git a/app/code/Magento/Catalog/Test/Mftf/Section/AdminProductCustomizableOptionsSection/AdminProductImportOptionsSection.xml b/app/code/Magento/Catalog/Test/Mftf/Section/AdminProductCustomizableOptionsSection/AdminProductImportOptionsSection.xml new file mode 100644 index 0000000000000..7d35920cebf3c --- /dev/null +++ b/app/code/Magento/Catalog/Test/Mftf/Section/AdminProductCustomizableOptionsSection/AdminProductImportOptionsSection.xml @@ -0,0 +1,19 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + /** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +--> +<sections xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Page/etc/SectionObject.xsd"> + <section name="AdminProductImportOptionsSection"> + <element name="selectProductTitle" type="text" selector="//aside[contains(@class, 'product_form_product_form_import_options_modal')]//h1[contains(text(), 'Select Product')]" timeout="30"/> + <element name="filterButton" type="button" selector="aside.product_form_product_form_import_options_modal button[data-action='grid-filter-expand']" timeout="30"/> + <element name="nameField" type="input" selector="aside.product_form_product_form_import_options_modal input[name='name']" timeout="30"/> + <element name="applyFiltersButton" type="button" selector="aside.product_form_product_form_import_options_modal button[data-action='grid-filter-apply']" timeout="30"/> + <element name="resetFiltersButton" type="button" selector="aside.product_form_product_form_import_options_modal button[data-action='grid-filter-reset']" timeout="30"/> + <element name="firstRowItemCheckbox" type="input" selector="aside.product_form_product_form_import_options_modal input[data-action='select-row']" timeout="30"/> + <element name="importButton" type="button" selector="//aside[contains(@class, 'product_form_product_form_import_options_modal')]//button[contains(@class, 'action-primary')]/span[contains(text(), 'Import')]" timeout="30"/> + </section> +</sections> diff --git a/app/code/Magento/Catalog/Test/Mftf/Section/AdminProductFormAdvancedPricingSection.xml b/app/code/Magento/Catalog/Test/Mftf/Section/AdminProductFormAdvancedPricingSection.xml index 77b89a07fb76a..91ac52a91a4c4 100644 --- a/app/code/Magento/Catalog/Test/Mftf/Section/AdminProductFormAdvancedPricingSection.xml +++ b/app/code/Magento/Catalog/Test/Mftf/Section/AdminProductFormAdvancedPricingSection.xml @@ -25,5 +25,13 @@ <element name="msrp" type="input" selector="//input[@name='product[msrp]']" timeout="30"/> <element name="msrpType" type="select" selector="//select[@name='product[msrp_display_actual_price_type]']" timeout="30"/> <element name="save" type="button" selector="#save-button" timeout="30"/> + <element name="modalTitle" type="text" selector="aside.product_form_product_form_advanced_pricing_modal h1.modal-title"/> + <!-- Last row tier price elements--> + <element name="lastTierPriceWebsite" type="select" selector="[data-index='tier_price'] table tbody tr.data-row:last-child [name*='[website_id]']"/> + <element name="lastTierPriceCustomerGroup" type="select" selector="[data-index='tier_price'] table tbody tr.data-row:last-child [name*='[cust_group]']"/> + <element name="lastTierPriceQty" type="input" selector="[data-index='tier_price'] table tbody tr.data-row:last-child [name*='[price_qty]']"/> + <element name="lastTierPriceType" type="select" selector="[data-index='tier_price'] table tbody tr.data-row:last-child [name*='[value_type]']"/> + <element name="lastTierPriceFixedAmount" type="input" selector="[data-index='tier_price'] table tbody tr.data-row:last-child [name*='[price]']"/> + <element name="lastTierPriceDiscountAmount" type="input" selector="[data-index='tier_price'] table tbody tr.data-row:last-child [name*='[percentage_value]']"/> </section> </sections> diff --git a/app/code/Magento/Catalog/Test/Mftf/Section/AdminProductFormAttributeSection.xml b/app/code/Magento/Catalog/Test/Mftf/Section/AdminProductFormAttributeSection.xml deleted file mode 100644 index b0aee1795dc3b..0000000000000 --- a/app/code/Magento/Catalog/Test/Mftf/Section/AdminProductFormAttributeSection.xml +++ /dev/null @@ -1,40 +0,0 @@ -<?xml version="1.0" encoding="UTF-8"?> -<!-- - /** - * Copyright © Magento, Inc. All rights reserved. - * See COPYING.txt for license details. - */ ---> - -<sections xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" - xsi:noNamespaceSchemaLocation="urn:magento:mftf:Page/etc/SectionObject.xsd"> - <section name="AdminProductFormAttributeSection"> - <element name="createNewAttribute" type="button" selector="//button[@data-index='add_new_attribute_button']" timeout="30"/> - </section> - <section name="AdminProductFormNewAttributeSection"> - <element name="attributeLabel" type="button" selector="//input[@name='frontend_label[0]']" timeout="30"/> - <element name="attributeType" type="select" selector="//select[@name='frontend_input']" timeout="30"/> - <element name="addValue" type="button" selector="//button[@data-action='add_new_row']" timeout="30"/> - <element name="optionViewName" type="text" selector="//table[@data-index='attribute_options_select']//span[contains(text(), '{{arg}}')]" parameterized="true" timeout="30"/> - <element name="optionValue" type="input" selector="(//input[contains(@name, 'option[value]')])[{{arg}}]" timeout="30" parameterized="true"/> - <element name="manageTitlesHeader" type="button" selector="//div[@class='fieldset-wrapper-title']//span[contains(text(), 'Manage Titles')]" timeout="30"/> - <element name="manageTitlesViewName" type="text" selector="//div[@data-index='manage-titles']//span[contains(text(), '{{arg}}')]" timeout="30" parameterized="true"/> - <element name="saveAttribute" type="button" selector="button#save" timeout="30"/> - <element name="saveInNewSet" type="button" selector="button#saveInNewSet" timeout="10"/> - </section> - <section name="AdminProductFormNewAttributeAdvancedSection"> - <element name="sectionHeader" type="button" selector="div[data-index='advanced_fieldset']"/> - <element name="defaultValue" type="textarea" selector="input[name='default_value_text']"/> - <element name="scope" type="select" selector="//div[@data-index='advanced_fieldset']//select[@name='is_global']"/> - </section> - <section name="AdminProductFormNewAttributeStorefrontSection"> - <element name="sectionHeader" type="button" selector="div[data-index='front_fieldset']"/> - <element name="useInSearch" type="checkbox" selector="div[data-index='is_searchable'] .admin__field-control label"/> - <element name="searchWeight" type="select" selector="select[name='search_weight']"/> - </section> - <section name="AdminProductFormNewAttributeNewSetSection"> - <element name="setName" type="button" selector="//div[contains(@class, 'modal-inner-wrap') and .//*[contains(., 'Enter Name for New Attribute Set')]]//input[contains(@class, 'admin__control-text')]"/> - <element name="accept" type="button" selector="//div[contains(@class, 'modal-inner-wrap') and .//*[contains(., 'Enter Name for New Attribute Set')]]//button[contains(@class, 'action-accept')]"/> - <element name="cancel" type="button" selector="//div[contains(@class, 'modal-inner-wrap') and .//*[contains(., 'Enter Name for New Attribute Set')]]//button[contains(@class, 'action-dismiss')]"/> - </section> -</sections> diff --git a/app/code/Magento/Catalog/Test/Mftf/Section/AdminProductFormAttributeSection/AdminProductFormAttributeSection.xml b/app/code/Magento/Catalog/Test/Mftf/Section/AdminProductFormAttributeSection/AdminProductFormAttributeSection.xml new file mode 100644 index 0000000000000..83d478e399b98 --- /dev/null +++ b/app/code/Magento/Catalog/Test/Mftf/Section/AdminProductFormAttributeSection/AdminProductFormAttributeSection.xml @@ -0,0 +1,13 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + /** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +--> +<sections xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Page/etc/SectionObject.xsd"> + <section name="AdminProductFormAttributeSection"> + <element name="createNewAttribute" type="button" selector="//button[@data-index='add_new_attribute_button']" timeout="30"/> + </section> +</sections> diff --git a/app/code/Magento/Catalog/Test/Mftf/Section/AdminProductFormAttributeSection/AdminProductFormNewAttributeAdvancedSection.xml b/app/code/Magento/Catalog/Test/Mftf/Section/AdminProductFormAttributeSection/AdminProductFormNewAttributeAdvancedSection.xml new file mode 100644 index 0000000000000..42cd7a3fe8cdc --- /dev/null +++ b/app/code/Magento/Catalog/Test/Mftf/Section/AdminProductFormAttributeSection/AdminProductFormNewAttributeAdvancedSection.xml @@ -0,0 +1,15 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + /** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +--> +<sections xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Page/etc/SectionObject.xsd"> + <section name="AdminProductFormNewAttributeAdvancedSection"> + <element name="sectionHeader" type="button" selector="div[data-index='advanced_fieldset']"/> + <element name="defaultValue" type="textarea" selector="input[name='default_value_text']"/> + <element name="scope" type="select" selector="//div[@data-index='advanced_fieldset']//select[@name='is_global']"/> + </section> +</sections> diff --git a/app/code/Magento/Catalog/Test/Mftf/Section/AdminProductFormAttributeSection/AdminProductFormNewAttributeNewSetSection.xml b/app/code/Magento/Catalog/Test/Mftf/Section/AdminProductFormAttributeSection/AdminProductFormNewAttributeNewSetSection.xml new file mode 100644 index 0000000000000..4c7d5acb658fb --- /dev/null +++ b/app/code/Magento/Catalog/Test/Mftf/Section/AdminProductFormAttributeSection/AdminProductFormNewAttributeNewSetSection.xml @@ -0,0 +1,15 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + /** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +--> +<sections xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Page/etc/SectionObject.xsd"> + <section name="AdminProductFormNewAttributeNewSetSection"> + <element name="setName" type="button" selector="//div[contains(@class, 'modal-inner-wrap') and .//*[contains(., 'Enter Name for New Attribute Set')]]//input[contains(@class, 'admin__control-text')]"/> + <element name="accept" type="button" selector="//div[contains(@class, 'modal-inner-wrap') and .//*[contains(., 'Enter Name for New Attribute Set')]]//button[contains(@class, 'action-accept')]"/> + <element name="cancel" type="button" selector="//div[contains(@class, 'modal-inner-wrap') and .//*[contains(., 'Enter Name for New Attribute Set')]]//button[contains(@class, 'action-dismiss')]"/> + </section> +</sections> diff --git a/app/code/Magento/Catalog/Test/Mftf/Section/AdminProductFormAttributeSection/AdminProductFormNewAttributeSection.xml b/app/code/Magento/Catalog/Test/Mftf/Section/AdminProductFormAttributeSection/AdminProductFormNewAttributeSection.xml new file mode 100644 index 0000000000000..5e2c3350e975e --- /dev/null +++ b/app/code/Magento/Catalog/Test/Mftf/Section/AdminProductFormAttributeSection/AdminProductFormNewAttributeSection.xml @@ -0,0 +1,21 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + /** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +--> +<sections xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Page/etc/SectionObject.xsd"> + <section name="AdminProductFormNewAttributeSection"> + <element name="attributeLabel" type="button" selector="//input[@name='frontend_label[0]']" timeout="30"/> + <element name="attributeType" type="select" selector="//select[@name='frontend_input']" timeout="30"/> + <element name="addValue" type="button" selector="//button[@data-action='add_new_row']" timeout="30"/> + <element name="optionViewName" type="text" selector="//table[@data-index='attribute_options_select']//span[contains(text(), '{{arg}}')]" parameterized="true" timeout="30"/> + <element name="optionValue" type="input" selector="(//input[contains(@name, 'option[value]')])[{{arg}}]" timeout="30" parameterized="true"/> + <element name="manageTitlesHeader" type="button" selector="//div[@class='fieldset-wrapper-title']//span[contains(text(), 'Manage Titles')]" timeout="30"/> + <element name="manageTitlesViewName" type="text" selector="//div[@data-index='manage-titles']//span[contains(text(), '{{arg}}')]" timeout="30" parameterized="true"/> + <element name="saveAttribute" type="button" selector="button#save" timeout="30"/> + <element name="saveInNewSet" type="button" selector="button#saveInNewSet" timeout="10"/> + </section> +</sections> diff --git a/app/code/Magento/Catalog/Test/Mftf/Section/AdminProductFormAttributeSection/AdminProductFormNewAttributeStorefrontSection.xml b/app/code/Magento/Catalog/Test/Mftf/Section/AdminProductFormAttributeSection/AdminProductFormNewAttributeStorefrontSection.xml new file mode 100644 index 0000000000000..1bc40cfc40743 --- /dev/null +++ b/app/code/Magento/Catalog/Test/Mftf/Section/AdminProductFormAttributeSection/AdminProductFormNewAttributeStorefrontSection.xml @@ -0,0 +1,15 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + /** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +--> +<sections xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Page/etc/SectionObject.xsd"> + <section name="AdminProductFormNewAttributeStorefrontSection"> + <element name="sectionHeader" type="button" selector="div[data-index='front_fieldset']"/> + <element name="useInSearch" type="checkbox" selector="div[data-index='is_searchable'] .admin__field-control label"/> + <element name="searchWeight" type="select" selector="select[name='search_weight']"/> + </section> +</sections> diff --git a/app/code/Magento/Catalog/Test/Mftf/Section/AdminProductFormSection.xml b/app/code/Magento/Catalog/Test/Mftf/Section/AdminProductFormSection.xml deleted file mode 100644 index 7649f6c344c3a..0000000000000 --- a/app/code/Magento/Catalog/Test/Mftf/Section/AdminProductFormSection.xml +++ /dev/null @@ -1,225 +0,0 @@ -<?xml version="1.0" encoding="UTF-8"?> -<!-- - /** - * Copyright © Magento, Inc. All rights reserved. - * See COPYING.txt for license details. - */ ---> -<sections xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" - xsi:noNamespaceSchemaLocation="urn:magento:mftf:Page/etc/SectionObject.xsd"> - <section name="AdminProductFormSection"> - <element name="additionalOptions" type="select" selector=".admin__control-multiselect"/> - <element name="datepickerNewAttribute" type="input" selector="[data-index='{{attrName}}'] input" timeout="30" parameterized="true"/> - <element name="attributeSet" type="select" selector="div[data-index='attribute_set_id'] .admin__field-control"/> - <element name="attributeSetFilter" type="input" selector="div[data-index='attribute_set_id'] .admin__field-control input" timeout="30"/> - <element name="attributeSetFilterResult" type="input" selector="div[data-index='attribute_set_id'] .action-menu-item._last" timeout="30"/> - <element name="attributeSetFilterResultByName" type="text" selector="//label/span[text() = '{{var}}']" timeout="30" parameterized="true"/> - <element name="productName" type="input" selector=".admin__field[data-index=name] input"/> - <element name="productNameDisabled" type="input" selector=".admin__field[data-index=name] input[disabled=true]"/> - <element name="RequiredNameIndicator" type="text" selector=" return window.getComputedStyle(document.querySelector('._required[data-index=name]>.admin__field-label span'), ':after').getPropertyValue('content');"/> - <element name="RequiredSkuIndicator" type="text" selector=" return window.getComputedStyle(document.querySelector('._required[data-index=sku]>.admin__field-label span'), ':after').getPropertyValue('content');"/> - <element name="productSku" type="input" selector=".admin__field[data-index=sku] input"/> - <element name="productSkuDisabled" type="input" selector=".admin__field[data-index=sku] input[disabled=true]"/> - <element name="enableProductAttributeLabel" type="text" selector="//span[text()='Enable Product']/parent::label"/> - <element name="enableProductAttributeLabelWrapper" type="text" selector="//span[text()='Enable Product']/parent::label/parent::div"/> - <element name="productStatus" type="checkbox" selector="input[name='product[status]']"/> - <element name="productStatusValue" type="checkbox" selector="input[name='product[status]'][value='{{value}}']" timeout="30" parameterized="true"/> - <element name="productStatusDisabled" type="checkbox" selector="input[name='product[status]'][disabled]"/> - <element name="enableProductLabel" type="checkbox" selector="input[name='product[status]']+label"/> - <element name="productStatusUseDefault" type="checkbox" selector="input[name='use_default[status]']"/> - <element name="productNameUseDefault" type="checkbox" selector="input[name='use_default[name]']"/> - <element name="productPrice" type="input" selector=".admin__field[data-index=price] input"/> - <element name="productPriceDisabled" type="input" selector=".admin__field[data-index=price] input[disabled=true]"/> - <element name="productPriceUseDefault" type="checkbox" selector=".admin__field[data-index=price] [name='use_default[price]']"/> - <element name="productTaxClass" type="select" selector="//*[@name='product[tax_class_id]']"/> - <element name="productTaxClassDisabled" type="select" selector="select[name='product[tax_class_id]'][disabled=true]"/> - <element name="productTaxClassUseDefault" type="checkbox" selector="input[name='use_default[tax_class_id]']"/> - <element name="advancedPricingLink" type="button" selector="button[data-index='advanced_pricing_button']" timeout="30"/> - <element name="currentCategory" type="text" selector=".admin__action-multiselect-crumb > span"/> - <element name="categoriesDropdown" type="multiselect" selector="div[data-index='category_ids']" timeout="30"/> - <element name="unselectCategories" type="button" selector="//span[@class='admin__action-multiselect-crumb']/span[contains(.,'{{category}}')]/../button[@data-action='remove-selected-item']" parameterized="true" timeout="30"/> - <element name="productQuantity" type="input" selector=".admin__field[data-index=qty] input"/> - <element name="advancedInventoryLink" type="button" selector="button[data-index='advanced_inventory_button'].action-additional" timeout="30"/> - <element name="productStockStatus" type="select" selector="select[name='product[quantity_and_stock_status][is_in_stock]']" timeout="30"/> - <element name="productStockStatusDisabled" type="select" selector="select[name='product[quantity_and_stock_status][is_in_stock]'][disabled=true]"/> - <element name="stockStatus" type="select" selector="[data-index='product-details'] select[name='product[quantity_and_stock_status][is_in_stock]']"/> - <element name="productWeight" type="input" selector=".admin__field[data-index=weight] input"/> - <element name="productWeightSelect" type="select" selector="select[name='product[product_has_weight]']"/> - <element name="contentTab" type="button" selector="//strong[contains(@class, 'admin__collapsible-title')]/span[text()='Content']"/> - <element name="fieldError" type="text" selector="//input[@name='product[{{fieldName}}]']/following-sibling::label[@class='admin__field-error']" parameterized="true"/> - <element name="priceFieldError" type="text" selector="//input[@name='product[price]']/parent::div/parent::div/label[@class='admin__field-error']"/> - <element name="addAttributeBtn" type="button" selector="#addAttribute"/> - <element name="createNewAttributeBtn" type="button" selector="button[data-index='add_new_attribute_button']"/> - <element name="save" type="button" selector="#save-button" timeout="30"/> - <element name="saveNewAttribute" type="button" selector="//aside[contains(@class, 'create_new_attribute_modal')]//button[@id='save']"/> - <element name="successMessage" type="text" selector="#messages"/> - <element name="attributeTab" type="button" selector="//strong[contains(@class, 'admin__collapsible-title')]/span[text()='Attributes']"/> - <element name="attributeLabel" type="input" selector="//input[@name='frontend_label[0]']"/> - <element name="frontendInput" type="select" selector="select[name = 'frontend_input']"/> - <element name="productFormTab" type="button" selector="//strong[@class='admin__collapsible-title']/span[contains(text(), '{{tabName}}')]" parameterized="true"/> - <element name="productFormTabState" type="text" selector="//strong[@class='admin__collapsible-title']/span[contains(text(), '{{tabName}}')]/parent::*/parent::*[@data-state-collapsible='{{state}}']" parameterized="true"/> - <element name="visibility" type="select" selector="//select[@name='product[visibility]']"/> - <element name="visibilityDisabled" type="select" selector="select[name='product[visibility]'][disabled=true]"/> - <element name="visibilityUseDefault" type="checkbox" selector="//input[@name='use_default[visibility]']"/> - <element name="divByDataIndex" type="input" selector="div[data-index='{{var}}']" parameterized="true"/> - <element name="setProductAsNewFrom" type="input" selector="input[name='product[news_from_date]']"/> - <element name="setProductAsNewTo" type="input" selector="input[name='product[news_to_date]']"/> - <element name="attributeLabelByText" type="text" selector="//*[@class='admin__field']//span[text()='{{attributeLabel}}']" parameterized="true"/> - <element name="attributeRequiredInput" type="input" selector="//input[contains(@name, 'product[{{attributeCode}}]')]" parameterized="true"/> - <element name="attributeFieldError" type="text" selector="//*[@class='admin__field _required _error']/..//label[contains(.,'This is a required field.')]"/> - <element name="customSelectField" type="select" selector="//select[@name='product[{{var}}]']" parameterized="true"/> - <element name="searchCategory" type="input" selector="//*[@data-index='category_ids']//input[contains(@class, 'multiselect-search')]" timeout="30"/> - <element name="selectCategory" type="input" selector="//*[@data-index='category_ids']//label[contains(., '{{categoryName}}')]" parameterized="true" timeout="30"/> - <element name="done" type="button" selector="//*[@data-index='category_ids']//button[@data-action='close-advanced-select']" timeout="30"/> - <element name="selectMultipleCategories" type="input" selector="//*[@data-index='container_category_ids']//*[contains(@class, '_selected')]"/> - <element name="countryOfManufacture" type="select" selector="select[name='product[country_of_manufacture]']"/> - <element name="newAddedAttribute" type="text" selector="//fieldset[@class='admin__fieldset']//div[contains(@data-index,'{{attributeCode}}')]" parameterized="true"/> - <element name="newCategoryButton" type="button" selector="button[data-index='create_category_button']" timeout="30"/> - <element name="footerBlock" type="block" selector="//footer"/> - </section> - <section name="ProductInWebsitesSection"> - <element name="sectionHeader" type="button" selector="div[data-index='websites']" timeout="30"/> - <element name="sectionHeaderOpened" type="button" selector="[data-index='websites']._show" timeout="30"/> - <element name="website" type="checkbox" selector="//label[contains(text(), '{{var1}}')]/parent::div//input[@type='checkbox']" parameterized="true"/> - <element name="websiteChecked" type="checkbox" selector="//label[contains(text(), '{{var1}}')]/parent::div//input[@type='checkbox'][@value='1']" parameterized="true"/> - </section> - <section name="ProductDesignSection"> - <element name="DesignTab" type="button" selector="//strong[@class='admin__collapsible-title']//span[text()='Design']"/> - <element name="LayoutDropdown" type="select" selector="select[name='product[page_layout]']"/> - <element name="productOptionsContainer" type="select" selector="select[name='product[options_container]']"/> - </section> - <section name="AdminProductFormRelatedUpSellCrossSellSection"> - <element name="relatedProductsHeader" type="button" selector=".admin__collapsible-block-wrapper[data-index='related']" timeout="30"/> - <element name="AddRelatedProductsButton" type="button" selector="button[data-index='button_related']" timeout="30"/> - <element name="addUpSellProduct" type="button" selector="button[data-index='button_upsell']" timeout="30"/> - </section> - <section name="AdminAddRelatedProductsModalSection"> - <element name="AddSelectedProductsButton" type="button" selector="//aside[contains(@class, 'related_modal')]//button[contains(@class, 'action-primary')]" timeout="30"/> - <element name="AddUpSellProductsButton" type="button" selector="//aside[contains(@class, 'upsell_modal')]//button[contains(@class, 'action-primary')]" timeout="30"/> - </section> - <section name="ProductWYSIWYGSection"> - <element name="Switcher" type="button" selector="//select[@id='dropdown-switcher']"/> - <element name="v436" type ="button" selector="//select[@id='dropdown-switcher']/option[text()='TinyMCE 4.3.6']" /> - <element name="v3" type ="button" selector="//select[@id='dropdown-switcher']/option[text()='TinyMCE 3.6(Deprecated)']" /> - <element name="TinymceDescription3" type ="button" selector="//span[text()='Description']" /> - <element name="SaveConfig" type ="button" selector="#save" /> - <element name="v4" type="button" selector="#category_form_description_v4"/> - <element name="WYSIWYGBtn" type="button" selector=".//button[@class='action-default scalable action-wysiwyg']"/> - </section> - <section name="ProductAttributeWYSIWYGSection"> - <element name="TextArea" type ="text" selector="//div[@data-index='{{var1}}']//textarea" parameterized="true"/> - <element name="showHideBtn" type="button" selector="//button[contains(@id,'{{var1}}')]" parameterized="true"/> - <element name="InsertImageBtn" type="button" selector="//div[contains(@id, '{{var1}}')]//span[text()='Insert Image...']" parameterized="true"/> - <element name="Style" type="button" selector="//div[contains(@id, '{{var1}}')]//span[text()='Paragraph']" parameterized="true"/> - <element name="Bold" type="button" selector="//div[contains(@id, '{{var1}}')]//i[@class='mce-ico mce-i-bold']" parameterized="true"/> - <element name="Italic" type="button" selector="//div[contains(@id, '{{var1}}')]//i[@class='mce-ico mce-i-bold']" parameterized="true"/> - <element name="Underline" type="button" selector="//div[contains(@id, '{{var1}}')]//i[@class='mce-ico mce-i-underline']" parameterized="true"/> - <element name="AlignLeft" type="button" selector="//div[contains(@id, '{{var1}}')]//i[@class='mce-ico mce-i-alignleft']" parameterized="true"/> - <element name="AlignCenter" type="button" selector="//div[contains(@id, '{{var1}}')]//i[@class='mce-ico mce-i-aligncenter']" parameterized="true"/> - <element name="AlignRight" type="button" selector="//div[contains(@id, '{{var1}}')]//i[@class='mce-ico mce-i-alignright']" parameterized="true"/> - <element name="Numlist" type="button" selector="//div[contains(@id, '{{var1}}')]//i[@class='mce-ico mce-i-bullist']" parameterized="true"/> - <element name="Bullet" type="button" selector="//div[contains(@id, '{{var1}}')]//i[@class='mce-ico mce-i-numlist']" parameterized="true"/> - <element name="InsertLink" type="button" selector="//div[contains(@id, '{{var1}}')]//i[@class='mce-ico mce-i-link']" parameterized="true"/> - <element name="InsertImageIcon" type="button" selector="//div[contains(@id, '{{var1}}')]//i[@class='mce-ico mce-i-image']" parameterized="true"/> - <element name="InsertTable" type="button" selector="//div[contains(@id, '{{var1}}')]//i[@class='mce-ico mce-i-table']" parameterized="true"/> - <element name="SpecialCharacter" type="button" selector="//div[contains(@id, '{{var1}}')]//i[@class='mce-ico mce-i-charmap']" parameterized="true"/> - <element name="TinyMCE4" type="text" selector="//div[contains(@id, '{{var1}}')]//*[contains(@class,'mce-branding')]" parameterized="true"/> - </section> - <section name="ProductDescriptionWYSIWYGToolbarSection"> - <element name="TinyMCE4" type ="button" selector="//div[@id='editorproduct_form_description']//*[contains(@class,'mce-branding')]" /> - <element name="showHideBtn" type="button" selector="#toggleproduct_form_description"/> - <element name="InsertImageBtn" type="button" selector="#buttonsproduct_form_description > .scalable.action-add-image.plugin" /> - <element name="Style" type="button" selector="//div[@id='editorproduct_form_description']//span[text()='Paragraph']" /> - <element name="Bold" type="button" selector="//div[@id='editorproduct_form_description']//i[@class='mce-ico mce-i-bold']" /> - <element name="Italic" type="button" selector="//div[@id='editorproduct_form_description']//i[@class='mce-ico mce-i-italic']" /> - <element name="Underline" type="button" selector="//div[@id='editorproduct_form_description']//i[@class='mce-ico mce-i-underline']" /> - <element name="AlignLeft" type="button" selector="//div[@id='editorproduct_form_description']//i[@class='mce-ico mce-i-alignleft']" /> - <element name="AlignCenter" type="button" selector="//div[@id='editorproduct_form_description']//i[@class='mce-ico mce-i-aligncenter']" /> - <element name="AlignRight" type="button" selector="//div[@id='editorproduct_form_description']//i[@class='mce-ico mce-i-alignright']" /> - <element name="Numlist" type="button" selector="//div[@id='editorproduct_form_description']//i[@class='mce-ico mce-i-bullist']" /> - <element name="Bullet" type="button" selector="//div[@id='editorproduct_form_description']//i[@class='mce-ico mce-i-numlist']" /> - <element name="InsertLink" type="button" selector="//div[@id='editorproduct_form_description']//i[@class='mce-ico mce-i-link']" /> - <element name="InsertImageIcon" type="button" selector="//div[@id='editorproduct_form_description']//i[@class='mce-ico mce-i-image']" timeout="30"/> - <element name="InsertTable" type="button" selector="//div[@id='editorproduct_form_description']//i[@class='mce-ico mce-i-table']" /> - <element name="SpecialCharacter" type="button" selector="//div[@id='editorproduct_form_description']//i[@class='mce-ico mce-i-charmap']" /> - <element name="Browse" type="button" selector=".mce-i-browse" timeout="30"/> - <element name="BrowseUploadImage" type="file" selector=".fileupload" timeout="30"/> - <element name="image" type="text" selector="//small[text()='{{var1}}']" parameterized="true"/> - <element name="imageSelected" type="text" selector="//small[text()='{{var1}}']/parent::*[@class='filecnt selected']" parameterized="true"/> - <element name="ImageSource" type="input" selector=".mce-combobox.mce-abs-layout-item.mce-last.mce-has-open" /> - <element name="ImageDescription" type="input" selector=".mce-textbox.mce-abs-layout-item.mce-last" /> - <element name="Height" type="input" selector=".mce-textbox.mce-abs-layout-item.mce-first" /> - <element name="UploadImage" type="file" selector=".fileupload" /> - <element name="OkBtn" type="button" selector="//button//span[text()='Ok']"/> - <element name="InsertFile" type="text" selector="#insert_files" timeout="30"/> - <element name="CreateFolder" type="button" selector="#new_folder" timeout="30"/> - <element name="DeleteSelectedBtn" type="text" selector="#delete_files" timeout="30"/> - <element name="CancelBtn" type="button" selector=".page-actions #cancel" /> - <element name="FolderName" type="button" selector="input[data-role='promptField']" /> - <element name="AcceptFolderName" type="button" selector=".action-primary.action-accept" timeout="30"/> - <element name="StorageRootArrow" type="button" selector="#root > .jstree-icon" /> - <element name="FolderContainer" type="button" selector="div[data-role='tree']" /> - <element name="checkIfArrowExpand" type="button" selector="//li[@id='root' and contains(@class,'jstree-closed')]" /> - <element name="WysiwygArrow" type="button" selector="#d3lzaXd5Zw-- > .jstree-icon" /> - <element name="checkIfWysiwygArrowExpand" type="button" selector="//li[@id='d3lzaXd5Zw--' and contains(@class,'jstree-closed')]" /> - <element name="confirmDelete" type="button" selector=".action-primary.action-accept" /> - </section> - <section name="ProductShortDescriptionWYSIWYGToolbarSection"> - <element name="TinyMCE4" type ="button" selector="//div[@id='editorproduct_form_short_description']//*[contains(@class,'mce-branding')]" /> - <element name="InsertImageBtn" type="button" selector="#buttonsproduct_form_short_description > .scalable.action-add-image.plugin" /> - <element name="showHideBtn" type="button" selector="#toggleproduct_form_short_description"/> - <element name="Style" type="button" selector="//div[@id='editorproduct_form_short_description']//span[text()='Paragraph']" /> - <element name="Bold" type="button" selector="//div[@id='editorproduct_form_short_description']//i[@class='mce-ico mce-i-bold']" /> - <element name="Italic" type="button" selector="//div[@id='editorproduct_form_short_description']//i[@class='mce-ico mce-i-italic']" /> - <element name="Underline" type="button" selector="//div[@id='editorproduct_form_short_description']//i[@class='mce-ico mce-i-underline']" /> - <element name="AlignLeft" type="button" selector="//div[@id='editorproduct_form_short_description']//i[@class='mce-ico mce-i-alignleft']" /> - <element name="AlignCenter" type="button" selector="//div[@id='editorproduct_form_short_description']//i[@class='mce-ico mce-i-aligncenter']" /> - <element name="AlignRight" type="button" selector="//div[@id='editorproduct_form_short_description']//i[@class='mce-ico mce-i-alignright']" /> - <element name="Numlist" type="button" selector="//div[@id='editorproduct_form_short_description']//i[@class='mce-ico mce-i-bullist']" /> - <element name="Bullet" type="button" selector="//div[@id='editorproduct_form_short_description']//i[@class='mce-ico mce-i-numlist']" /> - <element name="InsertLink" type="button" selector="//div[@id='editorproduct_form_short_description']//i[@class='mce-ico mce-i-link']" /> - <element name="InsertImageIcon" type="button" selector="//div[@id='editorproduct_form_short_description']//i[@class='mce-ico mce-i-image']" timeout="30"/> - <element name="InsertTable" type="button" selector="//div[@id='editorproduct_form_short_description']//i[@class='mce-ico mce-i-table']" /> - <element name="SpecialCharacter" type="button" selector="//div[@id='editorproduct_form_short_description']//i[@class='mce-ico mce-i-charmap']"/> - <element name="Browse" type="button" selector=".mce-i-browse"/> - <element name="BrowseUploadImage" type="file" selector=".fileupload" timeout="30" /> - <element name="image" type="text" selector="//small[text()='{{var1}}']" parameterized="true"/> - <element name="imageSelected" type="text" selector="//small[text()='{{var1}}']/parent::*[@class='filecnt selected']" parameterized="true"/> - <element name="ImageSource" type="input" selector=".mce-combobox.mce-abs-layout-item.mce-last.mce-has-open" /> - <element name="ImageDescription" type="input" selector=".mce-textbox.mce-abs-layout-item.mce-last" /> - <element name="Height" type="input" selector=".mce-textbox.mce-abs-layout-item.mce-first" /> - <element name="UploadImage" type="file" selector=".fileupload" /> - <element name="OkBtn" type="button" selector="//span[text()='Ok']" timeout="30"/> - <element name="InsertFile" type="text" selector="#insert_files"/> - <element name="CreateFolder" type="button" selector="#new_folder" /> - <element name="DeleteSelectedBtn" type="text" selector="#delete_files" timeout="30"/> - <element name="CancelBtn" type="button" selector="#cancel" /> - <element name="FolderName" type="button" selector="input[data-role='promptField']" /> - <element name="AcceptFolderName" type="button" selector=".action-primary.action-accept" /> - <element name="StorageRootArrow" type="button" selector="#root > .jstree-icon" /> - <element name="checkIfArrowExpand" type="button" selector="//li[@id='root' and contains(@class,'jstree-closed')]" /> - <element name="confirmDelete" type="button" selector=".action-primary.action-accept" /> - </section> - <section name="ProductDescriptionWysiwygSection"> - <element name="EditArea" type="text" selector="#editorproduct_form_description .mce-edit-area"/> - <element name="attributeEditArea" type="textarea" selector="#product_form_{{attributeCode}}" parameterized="true" timeout="30"/> - </section> - <section name="ProductShortDescriptionWysiwygSection"> - <element name="EditArea" type="text" selector="#editorproduct_form_short_description .mce-edit-area"/> - </section> - <section name="AdminProductFormAdvancedPricingSection"> - <element name="specialPrice" type="input" selector="input[name='product[special_price]']"/> - <element name="doneButton" type="button" selector=".product_form_product_form_advanced_pricing_modal button.action-primary" timeout="30"/> - <element name="useDefaultPrice" type="checkbox" selector="//input[@name='product[special_price]']/parent::div/following-sibling::div/input[@name='use_default[special_price]']"/> - </section> - <section name="AdminProductAttributeSection"> - <element name="attributeSectionHeader" type="button" selector="//div[@data-index='attributes']" timeout="30"/> - <element name="textAttributeByCode" type="text" selector="//input[@name='product[{{arg}}]']" parameterized="true"/> - <element name="textAttributeByName" type="text" selector="//div[@data-index='attributes']//fieldset[contains(@class, 'admin__field') and .//*[contains(.,'{{var}}')]]//input" parameterized="true"/> - <element name="dropDownAttribute" type="select" selector="//select[@name='product[{{arg}}]']" parameterized="true" timeout="30"/> - <element name="attributeSection" type="block" selector="//div[@data-index='attributes']/div[contains(@class, 'admin__collapsible-content _show')]" timeout="30"/> - <element name="customAttribute" type="text" selector="product[{{attributecode}}]" timeout="30" parameterized="true"/> - <element name="attributeGroupByName" type="button" selector="//div[@class='fieldset-wrapper-title']//span[text()='{{group}}']" parameterized="true"/> - <element name="attributeByGroupAndName" type="text" selector="//div[@class='fieldset-wrapper-title']//span[text()='{{group}}']/../../following-sibling::div//span[contains(text(),'attribute')]" parameterized="true"/> - </section> -</sections> diff --git a/app/code/Magento/Catalog/Test/Mftf/Section/AdminProductFormSection/AdminAddRelatedProductsModalSection.xml b/app/code/Magento/Catalog/Test/Mftf/Section/AdminProductFormSection/AdminAddRelatedProductsModalSection.xml new file mode 100644 index 0000000000000..90c60f4a8c2f7 --- /dev/null +++ b/app/code/Magento/Catalog/Test/Mftf/Section/AdminProductFormSection/AdminAddRelatedProductsModalSection.xml @@ -0,0 +1,14 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + /** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +--> +<sections xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Page/etc/SectionObject.xsd"> + <section name="AdminAddRelatedProductsModalSection"> + <element name="AddSelectedProductsButton" type="button" selector="//aside[contains(@class, 'related_modal')]//button[contains(@class, 'action-primary')]" timeout="30"/> + <element name="AddUpSellProductsButton" type="button" selector="//aside[contains(@class, 'upsell_modal')]//button[contains(@class, 'action-primary')]" timeout="30"/> + </section> +</sections> diff --git a/app/code/Magento/Catalog/Test/Mftf/Section/AdminProductFormSection/AdminProductAttributeSection.xml b/app/code/Magento/Catalog/Test/Mftf/Section/AdminProductFormSection/AdminProductAttributeSection.xml new file mode 100644 index 0000000000000..5349e1bb043a2 --- /dev/null +++ b/app/code/Magento/Catalog/Test/Mftf/Section/AdminProductFormSection/AdminProductAttributeSection.xml @@ -0,0 +1,20 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + /** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +--> +<sections xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Page/etc/SectionObject.xsd"> + <section name="AdminProductAttributeSection"> + <element name="attributeSectionHeader" type="button" selector="//div[@data-index='attributes']" timeout="30"/> + <element name="textAttributeByCode" type="text" selector="//input[@name='product[{{arg}}]']" parameterized="true"/> + <element name="textAttributeByName" type="text" selector="//div[@data-index='attributes']//fieldset[contains(@class, 'admin__field') and .//*[contains(.,'{{var}}')]]//input" parameterized="true"/> + <element name="dropDownAttribute" type="select" selector="//select[@name='product[{{arg}}]']" parameterized="true" timeout="30"/> + <element name="attributeSection" type="block" selector="//div[@data-index='attributes']/div[contains(@class, 'admin__collapsible-content _show')]" timeout="30"/> + <element name="customAttribute" type="text" selector="product[{{attributecode}}]" timeout="30" parameterized="true"/> + <element name="attributeGroupByName" type="button" selector="//div[@class='fieldset-wrapper-title']//span[text()='{{group}}']" parameterized="true"/> + <element name="attributeByGroupAndName" type="text" selector="//div[@class='fieldset-wrapper-title']//span[text()='{{group}}']/../../following-sibling::div//span[contains(text(),'attribute')]" parameterized="true"/> + </section> +</sections> diff --git a/app/code/Magento/Catalog/Test/Mftf/Section/AdminProductFormSection/AdminProductFormAdvancedPricingSection.xml b/app/code/Magento/Catalog/Test/Mftf/Section/AdminProductFormSection/AdminProductFormAdvancedPricingSection.xml new file mode 100644 index 0000000000000..4a9aa801a95c9 --- /dev/null +++ b/app/code/Magento/Catalog/Test/Mftf/Section/AdminProductFormSection/AdminProductFormAdvancedPricingSection.xml @@ -0,0 +1,15 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + /** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +--> +<sections xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Page/etc/SectionObject.xsd"> + <section name="AdminProductFormAdvancedPricingSection"> + <element name="specialPrice" type="input" selector="input[name='product[special_price]']"/> + <element name="doneButton" type="button" selector=".product_form_product_form_advanced_pricing_modal button.action-primary" timeout="30"/> + <element name="useDefaultPrice" type="checkbox" selector="//input[@name='product[special_price]']/parent::div/following-sibling::div/input[@name='use_default[special_price]']"/> + </section> +</sections> diff --git a/app/code/Magento/Catalog/Test/Mftf/Section/AdminProductFormSection/AdminProductFormRelatedUpSellCrossSellSection.xml b/app/code/Magento/Catalog/Test/Mftf/Section/AdminProductFormSection/AdminProductFormRelatedUpSellCrossSellSection.xml new file mode 100644 index 0000000000000..dd2e2fbe34ba0 --- /dev/null +++ b/app/code/Magento/Catalog/Test/Mftf/Section/AdminProductFormSection/AdminProductFormRelatedUpSellCrossSellSection.xml @@ -0,0 +1,15 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + /** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +--> +<sections xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Page/etc/SectionObject.xsd"> + <section name="AdminProductFormRelatedUpSellCrossSellSection"> + <element name="relatedProductsHeader" type="button" selector=".admin__collapsible-block-wrapper[data-index='related']" timeout="30"/> + <element name="AddRelatedProductsButton" type="button" selector="button[data-index='button_related']" timeout="30"/> + <element name="addUpSellProduct" type="button" selector="button[data-index='button_upsell']" timeout="30"/> + </section> +</sections> diff --git a/app/code/Magento/Catalog/Test/Mftf/Section/AdminProductFormSection/AdminProductFormSection.xml b/app/code/Magento/Catalog/Test/Mftf/Section/AdminProductFormSection/AdminProductFormSection.xml new file mode 100644 index 0000000000000..5bdd3bd5abcc6 --- /dev/null +++ b/app/code/Magento/Catalog/Test/Mftf/Section/AdminProductFormSection/AdminProductFormSection.xml @@ -0,0 +1,80 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + /** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +--> +<sections xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Page/etc/SectionObject.xsd"> + <section name="AdminProductFormSection"> + <element name="additionalOptions" type="select" selector=".admin__control-multiselect"/> + <element name="datepickerNewAttribute" type="input" selector="[data-index='{{attrName}}'] input" timeout="30" parameterized="true"/> + <element name="attributeSet" type="select" selector="div[data-index='attribute_set_id'] .admin__field-control"/> + <element name="attributeSetFilter" type="input" selector="div[data-index='attribute_set_id'] .admin__field-control input" timeout="30"/> + <element name="attributeSetFilterResult" type="input" selector="div[data-index='attribute_set_id'] .action-menu-item._last" timeout="30"/> + <element name="attributeSetFilterResultByName" type="text" selector="//label/span[text() = '{{var}}']" timeout="30" parameterized="true"/> + <element name="productName" type="input" selector=".admin__field[data-index=name] input"/> + <element name="productNameDisabled" type="input" selector=".admin__field[data-index=name] input[disabled=true]"/> + <element name="RequiredNameIndicator" type="text" selector=" return window.getComputedStyle(document.querySelector('._required[data-index=name]>.admin__field-label span'), ':after').getPropertyValue('content');"/> + <element name="RequiredSkuIndicator" type="text" selector=" return window.getComputedStyle(document.querySelector('._required[data-index=sku]>.admin__field-label span'), ':after').getPropertyValue('content');"/> + <element name="productSku" type="input" selector=".admin__field[data-index=sku] input"/> + <element name="productSkuDisabled" type="input" selector=".admin__field[data-index=sku] input[disabled=true]"/> + <element name="enableProductAttributeLabel" type="text" selector="//span[text()='Enable Product']/parent::label"/> + <element name="enableProductAttributeLabelWrapper" type="text" selector="//span[text()='Enable Product']/parent::label/parent::div"/> + <element name="productStatus" type="checkbox" selector="input[name='product[status]']"/> + <element name="productStatusValue" type="checkbox" selector="input[name='product[status]'][value='{{value}}']" timeout="30" parameterized="true"/> + <element name="productStatusDisabled" type="checkbox" selector="input[name='product[status]'][disabled]"/> + <element name="enableProductLabel" type="checkbox" selector="input[name='product[status]']+label"/> + <element name="productStatusUseDefault" type="checkbox" selector="input[name='use_default[status]']"/> + <element name="productNameUseDefault" type="checkbox" selector="input[name='use_default[name]']"/> + <element name="productPrice" type="input" selector=".admin__field[data-index=price] input"/> + <element name="productPriceDisabled" type="input" selector=".admin__field[data-index=price] input[disabled=true]"/> + <element name="productPriceUseDefault" type="checkbox" selector=".admin__field[data-index=price] [name='use_default[price]']"/> + <element name="productTaxClass" type="select" selector="//*[@name='product[tax_class_id]']"/> + <element name="productTaxClassDisabled" type="select" selector="select[name='product[tax_class_id]'][disabled=true]"/> + <element name="productTaxClassUseDefault" type="checkbox" selector="input[name='use_default[tax_class_id]']"/> + <element name="advancedPricingLink" type="button" selector="button[data-index='advanced_pricing_button']" timeout="30"/> + <element name="currentCategory" type="text" selector=".admin__action-multiselect-crumb > span"/> + <element name="categoriesDropdown" type="multiselect" selector="div[data-index='category_ids']" timeout="30"/> + <element name="unselectCategories" type="button" selector="//span[@class='admin__action-multiselect-crumb']/span[contains(.,'{{category}}')]/../button[@data-action='remove-selected-item']" parameterized="true" timeout="30"/> + <element name="productQuantity" type="input" selector=".admin__field[data-index=qty] input"/> + <element name="advancedInventoryLink" type="button" selector="button[data-index='advanced_inventory_button'].action-additional" timeout="30"/> + <element name="productStockStatus" type="select" selector="select[name='product[quantity_and_stock_status][is_in_stock]']" timeout="30"/> + <element name="productStockStatusDisabled" type="select" selector="select[name='product[quantity_and_stock_status][is_in_stock]'][disabled=true]"/> + <element name="stockStatus" type="select" selector="[data-index='product-details'] select[name='product[quantity_and_stock_status][is_in_stock]']"/> + <element name="productWeight" type="input" selector=".admin__field[data-index=weight] input"/> + <element name="productWeightSelect" type="select" selector="select[name='product[product_has_weight]']"/> + <element name="contentTab" type="button" selector="//strong[contains(@class, 'admin__collapsible-title')]/span[text()='Content']"/> + <element name="fieldError" type="text" selector="//input[@name='product[{{fieldName}}]']/following-sibling::label[@class='admin__field-error']" parameterized="true"/> + <element name="priceFieldError" type="text" selector="//input[@name='product[price]']/parent::div/parent::div/label[@class='admin__field-error']"/> + <element name="addAttributeBtn" type="button" selector="#addAttribute"/> + <element name="createNewAttributeBtn" type="button" selector="button[data-index='add_new_attribute_button']"/> + <element name="save" type="button" selector="#save-button" timeout="30"/> + <element name="saveNewAttribute" type="button" selector="//aside[contains(@class, 'create_new_attribute_modal')]//button[@id='save']"/> + <element name="successMessage" type="text" selector="#messages"/> + <element name="attributeTab" type="button" selector="//strong[contains(@class, 'admin__collapsible-title')]/span[text()='Attributes']"/> + <element name="attributeLabel" type="input" selector="//input[@name='frontend_label[0]']"/> + <element name="frontendInput" type="select" selector="select[name = 'frontend_input']"/> + <element name="productFormTab" type="button" selector="//strong[@class='admin__collapsible-title']/span[contains(text(), '{{tabName}}')]" parameterized="true"/> + <element name="productFormTabState" type="text" selector="//strong[@class='admin__collapsible-title']/span[contains(text(), '{{tabName}}')]/parent::*/parent::*[@data-state-collapsible='{{state}}']" parameterized="true"/> + <element name="visibility" type="select" selector="//select[@name='product[visibility]']"/> + <element name="visibilityDisabled" type="select" selector="select[name='product[visibility]'][disabled=true]"/> + <element name="visibilityUseDefault" type="checkbox" selector="//input[@name='use_default[visibility]']"/> + <element name="divByDataIndex" type="input" selector="div[data-index='{{var}}']" parameterized="true"/> + <element name="setProductAsNewFrom" type="input" selector="input[name='product[news_from_date]']"/> + <element name="setProductAsNewTo" type="input" selector="input[name='product[news_to_date]']"/> + <element name="attributeLabelByText" type="text" selector="//*[@class='admin__field']//span[text()='{{attributeLabel}}']" parameterized="true"/> + <element name="attributeRequiredInput" type="input" selector="//input[contains(@name, 'product[{{attributeCode}}]')]" parameterized="true"/> + <element name="attributeFieldError" type="text" selector="//*[@class='admin__field _required _error']/..//label[contains(.,'This is a required field.')]"/> + <element name="customSelectField" type="select" selector="//select[@name='product[{{var}}]']" parameterized="true"/> + <element name="searchCategory" type="input" selector="//*[@data-index='category_ids']//input[contains(@class, 'multiselect-search')]" timeout="30"/> + <element name="selectCategory" type="input" selector="//*[@data-index='category_ids']//label[contains(., '{{categoryName}}')]" parameterized="true" timeout="30"/> + <element name="done" type="button" selector="//*[@data-index='category_ids']//button[@data-action='close-advanced-select']" timeout="30"/> + <element name="selectMultipleCategories" type="input" selector="//*[@data-index='container_category_ids']//*[contains(@class, '_selected')]"/> + <element name="countryOfManufacture" type="select" selector="select[name='product[country_of_manufacture]']"/> + <element name="newAddedAttribute" type="text" selector="//fieldset[@class='admin__fieldset']//div[contains(@data-index,'{{attributeCode}}')]" parameterized="true"/> + <element name="newCategoryButton" type="button" selector="button[data-index='create_category_button']" timeout="30"/> + <element name="footerBlock" type="block" selector="//footer"/> + </section> +</sections> diff --git a/app/code/Magento/Catalog/Test/Mftf/Section/AdminProductFormSection/ProductAttributeWYSIWYGSection.xml b/app/code/Magento/Catalog/Test/Mftf/Section/AdminProductFormSection/ProductAttributeWYSIWYGSection.xml new file mode 100644 index 0000000000000..1714d8fb97c96 --- /dev/null +++ b/app/code/Magento/Catalog/Test/Mftf/Section/AdminProductFormSection/ProductAttributeWYSIWYGSection.xml @@ -0,0 +1,29 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + /** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +--> +<sections xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Page/etc/SectionObject.xsd"> + <section name="ProductAttributeWYSIWYGSection"> + <element name="TextArea" type="text" selector="//div[@data-index='{{var1}}']//textarea" parameterized="true"/> + <element name="showHideBtn" type="button" selector="//button[contains(@id,'{{var1}}')]" parameterized="true"/> + <element name="InsertImageBtn" type="button" selector="//div[contains(@id, '{{var1}}')]//span[text()='Insert Image...']" parameterized="true"/> + <element name="Style" type="button" selector="//div[contains(@id, '{{var1}}')]//span[text()='Paragraph']" parameterized="true"/> + <element name="Bold" type="button" selector="//div[contains(@id, '{{var1}}')]//i[@class='mce-ico mce-i-bold']" parameterized="true"/> + <element name="Italic" type="button" selector="//div[contains(@id, '{{var1}}')]//i[@class='mce-ico mce-i-bold']" parameterized="true"/> + <element name="Underline" type="button" selector="//div[contains(@id, '{{var1}}')]//i[@class='mce-ico mce-i-underline']" parameterized="true"/> + <element name="AlignLeft" type="button" selector="//div[contains(@id, '{{var1}}')]//i[@class='mce-ico mce-i-alignleft']" parameterized="true"/> + <element name="AlignCenter" type="button" selector="//div[contains(@id, '{{var1}}')]//i[@class='mce-ico mce-i-aligncenter']" parameterized="true"/> + <element name="AlignRight" type="button" selector="//div[contains(@id, '{{var1}}')]//i[@class='mce-ico mce-i-alignright']" parameterized="true"/> + <element name="Numlist" type="button" selector="//div[contains(@id, '{{var1}}')]//i[@class='mce-ico mce-i-bullist']" parameterized="true"/> + <element name="Bullet" type="button" selector="//div[contains(@id, '{{var1}}')]//i[@class='mce-ico mce-i-numlist']" parameterized="true"/> + <element name="InsertLink" type="button" selector="//div[contains(@id, '{{var1}}')]//i[@class='mce-ico mce-i-link']" parameterized="true"/> + <element name="InsertImageIcon" type="button" selector="//div[contains(@id, '{{var1}}')]//i[@class='mce-ico mce-i-image']" parameterized="true"/> + <element name="InsertTable" type="button" selector="//div[contains(@id, '{{var1}}')]//i[@class='mce-ico mce-i-table']" parameterized="true"/> + <element name="SpecialCharacter" type="button" selector="//div[contains(@id, '{{var1}}')]//i[@class='mce-ico mce-i-charmap']" parameterized="true"/> + <element name="TinyMCE4" type="text" selector="//div[contains(@id, '{{var1}}')]//*[contains(@class,'mce-branding')]" parameterized="true"/> + </section> +</sections> diff --git a/app/code/Magento/Catalog/Test/Mftf/Section/AdminProductFormSection/ProductDescriptionWYSIWYGToolbarSection.xml b/app/code/Magento/Catalog/Test/Mftf/Section/AdminProductFormSection/ProductDescriptionWYSIWYGToolbarSection.xml new file mode 100644 index 0000000000000..26946692ce050 --- /dev/null +++ b/app/code/Magento/Catalog/Test/Mftf/Section/AdminProductFormSection/ProductDescriptionWYSIWYGToolbarSection.xml @@ -0,0 +1,49 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + /** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +--> +<sections xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Page/etc/SectionObject.xsd"> + <section name="ProductDescriptionWYSIWYGToolbarSection"> + <element name="TinyMCE4" type="button" selector="//div[@id='editorproduct_form_description']//*[contains(@class,'mce-branding')]"/> + <element name="showHideBtn" type="button" selector="#toggleproduct_form_description"/> + <element name="InsertImageBtn" type="button" selector="#buttonsproduct_form_description > .scalable.action-add-image.plugin"/> + <element name="Style" type="button" selector="//div[@id='editorproduct_form_description']//span[text()='Paragraph']"/> + <element name="Bold" type="button" selector="//div[@id='editorproduct_form_description']//i[@class='mce-ico mce-i-bold']"/> + <element name="Italic" type="button" selector="//div[@id='editorproduct_form_description']//i[@class='mce-ico mce-i-italic']"/> + <element name="Underline" type="button" selector="//div[@id='editorproduct_form_description']//i[@class='mce-ico mce-i-underline']"/> + <element name="AlignLeft" type="button" selector="//div[@id='editorproduct_form_description']//i[@class='mce-ico mce-i-alignleft']"/> + <element name="AlignCenter" type="button" selector="//div[@id='editorproduct_form_description']//i[@class='mce-ico mce-i-aligncenter']"/> + <element name="AlignRight" type="button" selector="//div[@id='editorproduct_form_description']//i[@class='mce-ico mce-i-alignright']"/> + <element name="Numlist" type="button" selector="//div[@id='editorproduct_form_description']//i[@class='mce-ico mce-i-bullist']"/> + <element name="Bullet" type="button" selector="//div[@id='editorproduct_form_description']//i[@class='mce-ico mce-i-numlist']"/> + <element name="InsertLink" type="button" selector="//div[@id='editorproduct_form_description']//i[@class='mce-ico mce-i-link']"/> + <element name="InsertImageIcon" type="button" selector="//div[@id='editorproduct_form_description']//i[@class='mce-ico mce-i-image']" timeout="30"/> + <element name="InsertTable" type="button" selector="//div[@id='editorproduct_form_description']//i[@class='mce-ico mce-i-table']"/> + <element name="SpecialCharacter" type="button" selector="//div[@id='editorproduct_form_description']//i[@class='mce-ico mce-i-charmap']"/> + <element name="Browse" type="button" selector=".mce-i-browse" timeout="30"/> + <element name="BrowseUploadImage" type="file" selector=".fileupload" timeout="30"/> + <element name="image" type="text" selector="//small[text()='{{var1}}']" parameterized="true"/> + <element name="imageSelected" type="text" selector="//small[text()='{{var1}}']/parent::*[@class='filecnt selected']" parameterized="true"/> + <element name="ImageSource" type="input" selector=".mce-combobox.mce-abs-layout-item.mce-last.mce-has-open"/> + <element name="ImageDescription" type="input" selector=".mce-textbox.mce-abs-layout-item.mce-last"/> + <element name="Height" type="input" selector=".mce-textbox.mce-abs-layout-item.mce-first"/> + <element name="UploadImage" type="file" selector=".fileupload"/> + <element name="OkBtn" type="button" selector="//button//span[text()='Ok']"/> + <element name="InsertFile" type="text" selector="#insert_files" timeout="30"/> + <element name="CreateFolder" type="button" selector="#new_folder" timeout="30"/> + <element name="DeleteSelectedBtn" type="text" selector="#delete_files" timeout="30"/> + <element name="CancelBtn" type="button" selector=".page-actions #cancel"/> + <element name="FolderName" type="button" selector="input[data-role='promptField']"/> + <element name="AcceptFolderName" type="button" selector=".action-primary.action-accept" timeout="30"/> + <element name="StorageRootArrow" type="button" selector="#root > .jstree-icon"/> + <element name="FolderContainer" type="button" selector="div[data-role='tree']"/> + <element name="checkIfArrowExpand" type="button" selector="//li[@id='root' and contains(@class,'jstree-closed')]"/> + <element name="WysiwygArrow" type="button" selector="#d3lzaXd5Zw-- > .jstree-icon"/> + <element name="checkIfWysiwygArrowExpand" type="button" selector="//li[@id='d3lzaXd5Zw--' and contains(@class,'jstree-closed')]"/> + <element name="confirmDelete" type="button" selector=".action-primary.action-accept"/> + </section> +</sections> diff --git a/app/code/Magento/Catalog/Test/Mftf/Section/AdminProductFormSection/ProductDescriptionWysiwygSection.xml b/app/code/Magento/Catalog/Test/Mftf/Section/AdminProductFormSection/ProductDescriptionWysiwygSection.xml new file mode 100644 index 0000000000000..78fbbac45dcd9 --- /dev/null +++ b/app/code/Magento/Catalog/Test/Mftf/Section/AdminProductFormSection/ProductDescriptionWysiwygSection.xml @@ -0,0 +1,14 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + /** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +--> +<sections xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Page/etc/SectionObject.xsd"> + <section name="ProductDescriptionWysiwygSection"> + <element name="EditArea" type="text" selector="#editorproduct_form_description .mce-edit-area"/> + <element name="attributeEditArea" type="textarea" selector="#product_form_{{attributeCode}}" parameterized="true" timeout="30"/> + </section> +</sections> diff --git a/app/code/Magento/Catalog/Test/Mftf/Section/AdminProductFormSection/ProductDesignSection.xml b/app/code/Magento/Catalog/Test/Mftf/Section/AdminProductFormSection/ProductDesignSection.xml new file mode 100644 index 0000000000000..519be90c93255 --- /dev/null +++ b/app/code/Magento/Catalog/Test/Mftf/Section/AdminProductFormSection/ProductDesignSection.xml @@ -0,0 +1,15 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + /** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +--> +<sections xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Page/etc/SectionObject.xsd"> + <section name="ProductDesignSection"> + <element name="DesignTab" type="button" selector="//strong[@class='admin__collapsible-title']//span[text()='Design']"/> + <element name="LayoutDropdown" type="select" selector="select[name='product[page_layout]']"/> + <element name="productOptionsContainer" type="select" selector="select[name='product[options_container]']"/> + </section> +</sections> diff --git a/app/code/Magento/Catalog/Test/Mftf/Section/AdminProductFormSection/ProductInWebsitesSection.xml b/app/code/Magento/Catalog/Test/Mftf/Section/AdminProductFormSection/ProductInWebsitesSection.xml new file mode 100644 index 0000000000000..5ee77e773f6fd --- /dev/null +++ b/app/code/Magento/Catalog/Test/Mftf/Section/AdminProductFormSection/ProductInWebsitesSection.xml @@ -0,0 +1,16 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + /** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +--> +<sections xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Page/etc/SectionObject.xsd"> + <section name="ProductInWebsitesSection"> + <element name="sectionHeader" type="button" selector="div[data-index='websites']" timeout="30"/> + <element name="sectionHeaderOpened" type="button" selector="[data-index='websites']._show" timeout="30"/> + <element name="website" type="checkbox" selector="//label[contains(text(), '{{var1}}')]/parent::div//input[@type='checkbox']" parameterized="true"/> + <element name="websiteChecked" type="checkbox" selector="//label[contains(text(), '{{var1}}')]/parent::div//input[@type='checkbox'][@value='1']" parameterized="true"/> + </section> +</sections> diff --git a/app/code/Magento/Catalog/Test/Mftf/Section/AdminProductFormSection/ProductShortDescriptionWYSIWYGToolbarSection.xml b/app/code/Magento/Catalog/Test/Mftf/Section/AdminProductFormSection/ProductShortDescriptionWYSIWYGToolbarSection.xml new file mode 100644 index 0000000000000..90c2ef61e228e --- /dev/null +++ b/app/code/Magento/Catalog/Test/Mftf/Section/AdminProductFormSection/ProductShortDescriptionWYSIWYGToolbarSection.xml @@ -0,0 +1,46 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + /** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +--> +<sections xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Page/etc/SectionObject.xsd"> + <section name="ProductShortDescriptionWYSIWYGToolbarSection"> + <element name="TinyMCE4" type="button" selector="//div[@id='editorproduct_form_short_description']//*[contains(@class,'mce-branding')]"/> + <element name="InsertImageBtn" type="button" selector="#buttonsproduct_form_short_description > .scalable.action-add-image.plugin"/> + <element name="showHideBtn" type="button" selector="#toggleproduct_form_short_description"/> + <element name="Style" type="button" selector="//div[@id='editorproduct_form_short_description']//span[text()='Paragraph']"/> + <element name="Bold" type="button" selector="//div[@id='editorproduct_form_short_description']//i[@class='mce-ico mce-i-bold']"/> + <element name="Italic" type="button" selector="//div[@id='editorproduct_form_short_description']//i[@class='mce-ico mce-i-italic']"/> + <element name="Underline" type="button" selector="//div[@id='editorproduct_form_short_description']//i[@class='mce-ico mce-i-underline']"/> + <element name="AlignLeft" type="button" selector="//div[@id='editorproduct_form_short_description']//i[@class='mce-ico mce-i-alignleft']"/> + <element name="AlignCenter" type="button" selector="//div[@id='editorproduct_form_short_description']//i[@class='mce-ico mce-i-aligncenter']"/> + <element name="AlignRight" type="button" selector="//div[@id='editorproduct_form_short_description']//i[@class='mce-ico mce-i-alignright']"/> + <element name="Numlist" type="button" selector="//div[@id='editorproduct_form_short_description']//i[@class='mce-ico mce-i-bullist']"/> + <element name="Bullet" type="button" selector="//div[@id='editorproduct_form_short_description']//i[@class='mce-ico mce-i-numlist']"/> + <element name="InsertLink" type="button" selector="//div[@id='editorproduct_form_short_description']//i[@class='mce-ico mce-i-link']"/> + <element name="InsertImageIcon" type="button" selector="//div[@id='editorproduct_form_short_description']//i[@class='mce-ico mce-i-image']" timeout="30"/> + <element name="InsertTable" type="button" selector="//div[@id='editorproduct_form_short_description']//i[@class='mce-ico mce-i-table']"/> + <element name="SpecialCharacter" type="button" selector="//div[@id='editorproduct_form_short_description']//i[@class='mce-ico mce-i-charmap']"/> + <element name="Browse" type="button" selector=".mce-i-browse"/> + <element name="BrowseUploadImage" type="file" selector=".fileupload" timeout="30"/> + <element name="image" type="text" selector="//small[text()='{{var1}}']" parameterized="true"/> + <element name="imageSelected" type="text" selector="//small[text()='{{var1}}']/parent::*[@class='filecnt selected']" parameterized="true"/> + <element name="ImageSource" type="input" selector=".mce-combobox.mce-abs-layout-item.mce-last.mce-has-open"/> + <element name="ImageDescription" type="input" selector=".mce-textbox.mce-abs-layout-item.mce-last"/> + <element name="Height" type="input" selector=".mce-textbox.mce-abs-layout-item.mce-first"/> + <element name="UploadImage" type="file" selector=".fileupload"/> + <element name="OkBtn" type="button" selector="//span[text()='Ok']" timeout="30"/> + <element name="InsertFile" type="text" selector="#insert_files"/> + <element name="CreateFolder" type="button" selector="#new_folder"/> + <element name="DeleteSelectedBtn" type="text" selector="#delete_files" timeout="30"/> + <element name="CancelBtn" type="button" selector="#cancel"/> + <element name="FolderName" type="button" selector="input[data-role='promptField']"/> + <element name="AcceptFolderName" type="button" selector=".action-primary.action-accept"/> + <element name="StorageRootArrow" type="button" selector="#root > .jstree-icon"/> + <element name="checkIfArrowExpand" type="button" selector="//li[@id='root' and contains(@class,'jstree-closed')]"/> + <element name="confirmDelete" type="button" selector=".action-primary.action-accept"/> + </section> +</sections> diff --git a/app/code/Magento/Catalog/Test/Mftf/Section/AdminProductFormSection/ProductShortDescriptionWysiwygSection.xml b/app/code/Magento/Catalog/Test/Mftf/Section/AdminProductFormSection/ProductShortDescriptionWysiwygSection.xml new file mode 100644 index 0000000000000..66be79557b8e6 --- /dev/null +++ b/app/code/Magento/Catalog/Test/Mftf/Section/AdminProductFormSection/ProductShortDescriptionWysiwygSection.xml @@ -0,0 +1,13 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + /** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +--> +<sections xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Page/etc/SectionObject.xsd"> + <section name="ProductShortDescriptionWysiwygSection"> + <element name="EditArea" type="text" selector="#editorproduct_form_short_description .mce-edit-area"/> + </section> +</sections> diff --git a/app/code/Magento/Catalog/Test/Mftf/Section/AdminProductFormSection/ProductWYSIWYGSection.xml b/app/code/Magento/Catalog/Test/Mftf/Section/AdminProductFormSection/ProductWYSIWYGSection.xml new file mode 100644 index 0000000000000..544bdf85681c9 --- /dev/null +++ b/app/code/Magento/Catalog/Test/Mftf/Section/AdminProductFormSection/ProductWYSIWYGSection.xml @@ -0,0 +1,19 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + /** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +--> +<sections xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Page/etc/SectionObject.xsd"> + <section name="ProductWYSIWYGSection"> + <element name="Switcher" type="button" selector="//select[@id='dropdown-switcher']"/> + <element name="v436" type="button" selector="//select[@id='dropdown-switcher']/option[text()='TinyMCE 4.3.6']"/> + <element name="v3" type="button" selector="//select[@id='dropdown-switcher']/option[text()='TinyMCE 3.6(Deprecated)']"/> + <element name="TinymceDescription3" type="button" selector="//span[text()='Description']"/> + <element name="SaveConfig" type="button" selector="#save"/> + <element name="v4" type="button" selector="#category_form_description_v4"/> + <element name="WYSIWYGBtn" type="button" selector=".//button[@class='action-default scalable action-wysiwyg']"/> + </section> +</sections> diff --git a/app/code/Magento/Catalog/Test/Mftf/Section/AdminProductGridSection.xml b/app/code/Magento/Catalog/Test/Mftf/Section/AdminProductGridSection.xml index 07dd26381fe08..1aff1a5031413 100644 --- a/app/code/Magento/Catalog/Test/Mftf/Section/AdminProductGridSection.xml +++ b/app/code/Magento/Catalog/Test/Mftf/Section/AdminProductGridSection.xml @@ -21,7 +21,7 @@ <element name="firstProductRowEditButton" type="button" selector="table.data-grid tr.data-row td .action-menu-item:first-of-type"/> <element name="productThumbnail" type="text" selector="table.data-grid tr:nth-child({{row}}) td.data-grid-thumbnail-cell > img" parameterized="true"/> <element name="productThumbnailBySrc" type="text" selector="img.admin__control-thumbnail[src*='{{pattern}}']" parameterized="true"/> - <element name="productGridCell" type="text" selector="//tr[{{row}}]//td[count(//div[@data-role='grid-wrapper']//tr//th[contains(., '{{column}}')]/preceding-sibling::th) +1 ]" parameterized="true"/> + <element name="productGridCell" type="text" selector="//tr[{{row}}]//td[count(//div[@data-role='grid-wrapper']//tr//th[normalize-space(.)='{{column}}']/preceding-sibling::th) +1 ]" parameterized="true"/> <element name="productGridHeaderCell" type="text" selector="//div[@data-role='grid-wrapper']//tr//th[contains(., '{{column}}')]" parameterized="true"/> <element name="multicheckDropdown" type="button" selector="div[data-role='grid-wrapper'] th.data-grid-multicheck-cell button.action-multicheck-toggle"/> <element name="multicheckOption" type="button" selector="//div[@data-role='grid-wrapper']//th[contains(@class, data-grid-multicheck-cell)]//li//span[text() = '{{label}}']" parameterized="true"/> diff --git a/app/code/Magento/Catalog/Test/Mftf/Section/AdminProductImagesSection.xml b/app/code/Magento/Catalog/Test/Mftf/Section/AdminProductImagesSection.xml index f20e9b3a11e5e..c2de91aadbc0c 100644 --- a/app/code/Magento/Catalog/Test/Mftf/Section/AdminProductImagesSection.xml +++ b/app/code/Magento/Catalog/Test/Mftf/Section/AdminProductImagesSection.xml @@ -18,7 +18,6 @@ <element name="modalOkBtn" type="button" selector="button.action-primary.action-accept"/> <element name="uploadProgressBar" type="text" selector=".uploader .file-row"/> <element name="productImagesToggleState" type="button" selector="[data-index='gallery'] > [data-state-collapsible='{{status}}']" parameterized="true"/> - <element name="nthProductImage" type="button" selector="#media_gallery_content > div:nth-child({{var}}) img.product-image" parameterized="true"/> <element name="nthRemoveImageBtn" type="button" selector="#media_gallery_content > div:nth-child({{var}}) button.action-remove" parameterized="true"/> diff --git a/app/code/Magento/Catalog/Test/Mftf/Section/AdminProductMessagesSection.xml b/app/code/Magento/Catalog/Test/Mftf/Section/AdminProductMessagesSection.xml index 59fbeee142dfe..c51d481d7dc5e 100644 --- a/app/code/Magento/Catalog/Test/Mftf/Section/AdminProductMessagesSection.xml +++ b/app/code/Magento/Catalog/Test/Mftf/Section/AdminProductMessagesSection.xml @@ -9,7 +9,7 @@ <sections xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="urn:magento:mftf:Page/etc/SectionObject.xsd"> <section name="AdminProductMessagesSection"> - <element name="successMessage" type="text" selector=".message-success"/> + <element name="successMessage" type="text" selector=".message.message-success.success"/> <element name="errorMessage" type="text" selector=".message.message-error.error"/> </section> </sections> diff --git a/app/code/Magento/Catalog/Test/Mftf/Section/AdminProductMultiselectAttributeSection.xml b/app/code/Magento/Catalog/Test/Mftf/Section/AdminProductMultiselectAttributeSection.xml new file mode 100644 index 0000000000000..66af84b08df69 --- /dev/null +++ b/app/code/Magento/Catalog/Test/Mftf/Section/AdminProductMultiselectAttributeSection.xml @@ -0,0 +1,14 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + /** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +--> + +<sections xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Page/etc/SectionObject.xsd"> + <section name="AdminProductMultiselectAttributeSection"> + <element name="option" type="text" selector="//option[contains(@data-title,'{{value}}')]" parameterized="true"/> + </section> +</sections> diff --git a/app/code/Magento/Catalog/Test/Mftf/Section/AdminProductRelatedUpSellCrossSellSection.xml b/app/code/Magento/Catalog/Test/Mftf/Section/AdminProductRelatedUpSellCrossSellSection.xml deleted file mode 100644 index f3b0d3a895cb1..0000000000000 --- a/app/code/Magento/Catalog/Test/Mftf/Section/AdminProductRelatedUpSellCrossSellSection.xml +++ /dev/null @@ -1,29 +0,0 @@ -<?xml version="1.0" encoding="UTF-8"?> -<!-- - /** - * Copyright © Magento, Inc. All rights reserved. - * See COPYING.txt for license details. - */ ---> - -<sections xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" - xsi:noNamespaceSchemaLocation="urn:magento:mftf:Page/etc/SectionObject.xsd"> - <section name="AdminProductFormRelatedUpSellCrossSellSection"> - <element name="sectionHeader" type="block" selector=".fieldset-wrapper.admin__collapsible-block-wrapper[data-index='related']"/> - <element name="AddRelatedProductsButton" type="button" selector="button[data-index='button_related']" timeout="30"/> - <element name="AddUpSellProductsButton" type="button" selector="button[data-index='button_upsell']" timeout="30"/> - <element name="AddCrossSellProductsButton" type="button" selector="button[data-index='button_crosssell']" timeout="30"/> - <element name="relatedProductSectionText" type="text" selector=".fieldset-wrapper.admin__fieldset-section[data-index='related']"/> - <element name="upSellProductSectionText" type="text" selector=".fieldset-wrapper.admin__fieldset-section[data-index='upsell']"/> - <element name="crossSellProductSectionText" type="text" selector=".fieldset-wrapper.admin__fieldset-section[data-index='crosssell']"/> - <element name="relatedDropdown" type="block" selector="//div[@data-index='related']" timeout="30"/> - <element name="relatedDependent" type="block" selector="//div[@data-index='related']//div[contains(@class, '_show')]"/> - <element name="selectedRelatedProduct" type="block" selector="//span[@data-index='name']"/> - <element name="removeRelatedProduct" type="button" selector="//span[text()='Related Products']//..//..//..//span[text()='{{productName}}']//..//..//..//..//..//button[@class='action-delete']" parameterized="true"/> - <element name="selectedProductSku" type="text" selector="//div[@data-index='{{section}}']//span[@data-index='sku']" parameterized="true" timeout="30"/> - </section> - <section name="AdminAddUpSellProductsModalSection"> - <element name="Modal" type="button" selector=".product_form_product_form_related_upsell_modal"/> - <element name="AddSelectedProductsButton" type="button" selector="//aside[contains(@class, 'product_form_product_form_related_upsell_modal')]//button/span[contains(text(), 'Add Selected Products')]" timeout="30"/> - </section> -</sections> diff --git a/app/code/Magento/Catalog/Test/Mftf/Section/AdminProductRelatedUpSellCrossSellSection/AdminAddUpSellProductsModalSection.xml b/app/code/Magento/Catalog/Test/Mftf/Section/AdminProductRelatedUpSellCrossSellSection/AdminAddUpSellProductsModalSection.xml new file mode 100644 index 0000000000000..9d80484f6a865 --- /dev/null +++ b/app/code/Magento/Catalog/Test/Mftf/Section/AdminProductRelatedUpSellCrossSellSection/AdminAddUpSellProductsModalSection.xml @@ -0,0 +1,14 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + /** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +--> +<sections xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Page/etc/SectionObject.xsd"> + <section name="AdminAddUpSellProductsModalSection"> + <element name="Modal" type="button" selector=".product_form_product_form_related_upsell_modal"/> + <element name="AddSelectedProductsButton" type="button" selector="//aside[contains(@class, 'product_form_product_form_related_upsell_modal')]//button/span[contains(text(), 'Add Selected Products')]" timeout="30"/> + </section> +</sections> diff --git a/app/code/Magento/Catalog/Test/Mftf/Section/AdminProductRelatedUpSellCrossSellSection/AdminProductFormRelatedUpSellCrossSellSection.xml b/app/code/Magento/Catalog/Test/Mftf/Section/AdminProductRelatedUpSellCrossSellSection/AdminProductFormRelatedUpSellCrossSellSection.xml new file mode 100644 index 0000000000000..68c1121817926 --- /dev/null +++ b/app/code/Magento/Catalog/Test/Mftf/Section/AdminProductRelatedUpSellCrossSellSection/AdminProductFormRelatedUpSellCrossSellSection.xml @@ -0,0 +1,24 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + /** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +--> +<sections xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Page/etc/SectionObject.xsd"> + <section name="AdminProductFormRelatedUpSellCrossSellSection"> + <element name="sectionHeader" type="block" selector=".fieldset-wrapper.admin__collapsible-block-wrapper[data-index='related']"/> + <element name="AddRelatedProductsButton" type="button" selector="button[data-index='button_related']" timeout="30"/> + <element name="AddUpSellProductsButton" type="button" selector="button[data-index='button_upsell']" timeout="30"/> + <element name="AddCrossSellProductsButton" type="button" selector="button[data-index='button_crosssell']" timeout="30"/> + <element name="relatedProductSectionText" type="text" selector=".fieldset-wrapper.admin__fieldset-section[data-index='related']"/> + <element name="upSellProductSectionText" type="text" selector=".fieldset-wrapper.admin__fieldset-section[data-index='upsell']"/> + <element name="crossSellProductSectionText" type="text" selector=".fieldset-wrapper.admin__fieldset-section[data-index='crosssell']"/> + <element name="relatedDropdown" type="block" selector="//div[@data-index='related']" timeout="30"/> + <element name="relatedDependent" type="block" selector="//div[@data-index='related']//div[contains(@class, '_show')]"/> + <element name="selectedRelatedProduct" type="block" selector="//span[@data-index='name']"/> + <element name="removeRelatedProduct" type="button" selector="//span[text()='Related Products']//..//..//..//span[text()='{{productName}}']//..//..//..//..//..//button[@class='action-delete']" parameterized="true"/> + <element name="selectedProductSku" type="text" selector="//div[@data-index='{{section}}']//span[@data-index='sku']" parameterized="true" timeout="30"/> + </section> +</sections> diff --git a/app/code/Magento/Catalog/Test/Mftf/Section/AdminUpdateAttributesSection.xml b/app/code/Magento/Catalog/Test/Mftf/Section/AdminUpdateAttributesSection/AdminUpdateAttributesSection.xml similarity index 88% rename from app/code/Magento/Catalog/Test/Mftf/Section/AdminUpdateAttributesSection.xml rename to app/code/Magento/Catalog/Test/Mftf/Section/AdminUpdateAttributesSection/AdminUpdateAttributesSection.xml index 69d7297628d56..55d3b1bcc6cd5 100644 --- a/app/code/Magento/Catalog/Test/Mftf/Section/AdminUpdateAttributesSection.xml +++ b/app/code/Magento/Catalog/Test/Mftf/Section/AdminUpdateAttributesSection/AdminUpdateAttributesSection.xml @@ -5,9 +5,8 @@ * See COPYING.txt for license details. */ --> - <sections xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" - xsi:noNamespaceSchemaLocation="urn:magento:mftf:Page/etc/SectionObject.xsd"> + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Page/etc/SectionObject.xsd"> <section name="AdminUpdateAttributesSection"> <element name="saveButton" type="button" selector="button[title='Save']" timeout="30"/> @@ -39,8 +38,4 @@ <element name="name" type="input" selector="#name"/> <element name="description" type="input" selector="#description"/> </section> - <section name="AdminUpdateAttributesWebsiteSection"> - <element name="website" type="button" selector="#attributes_update_tabs_websites"/> - <element name="addProductToWebsite" type="checkbox" selector="#add-products-to-website-content .website-checkbox"/> - </section> </sections> diff --git a/app/code/Magento/Catalog/Test/Mftf/Section/AdminUpdateAttributesSection/AdminUpdateAttributesWebsiteSection.xml b/app/code/Magento/Catalog/Test/Mftf/Section/AdminUpdateAttributesSection/AdminUpdateAttributesWebsiteSection.xml new file mode 100644 index 0000000000000..343112bdb8dfd --- /dev/null +++ b/app/code/Magento/Catalog/Test/Mftf/Section/AdminUpdateAttributesSection/AdminUpdateAttributesWebsiteSection.xml @@ -0,0 +1,14 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + /** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +--> +<sections xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Page/etc/SectionObject.xsd"> + <section name="AdminUpdateAttributesWebsiteSection"> + <element name="website" type="button" selector="#attributes_update_tabs_websites"/> + <element name="addProductToWebsite" type="checkbox" selector="#add-products-to-website-content .website-checkbox"/> + </section> +</sections> diff --git a/app/code/Magento/Catalog/Test/Mftf/Section/StoreFrontRecentProductSection.xml b/app/code/Magento/Catalog/Test/Mftf/Section/StoreFrontRecentProductSection.xml new file mode 100644 index 0000000000000..387e252ae93d4 --- /dev/null +++ b/app/code/Magento/Catalog/Test/Mftf/Section/StoreFrontRecentProductSection.xml @@ -0,0 +1,15 @@ +<?xml version="1.0" encoding="UTF-8"?> + <!-- + /** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ + --> + +<sections xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Page/etc/SectionObject.xsd"> + + <section name="StoreFrontRecentlyViewedProductSection"> + <element name="ProductName" type="text" selector="//div[@class='products-grid']/ol/li[position()={{position}}]/div/div[@class='product-item-details']/strong/a" parameterized="true"/> + </section> +</sections> \ No newline at end of file diff --git a/app/code/Magento/Catalog/Test/Mftf/Section/StorefrontCategoryBottomToolbarSection.xml b/app/code/Magento/Catalog/Test/Mftf/Section/StorefrontCategoryBottomToolbarSection.xml index 7ce795c78f25b..09eb4ad954274 100644 --- a/app/code/Magento/Catalog/Test/Mftf/Section/StorefrontCategoryBottomToolbarSection.xml +++ b/app/code/Magento/Catalog/Test/Mftf/Section/StorefrontCategoryBottomToolbarSection.xml @@ -12,5 +12,6 @@ <element name="previousPage" type="button" selector=".//*[@class='toolbar toolbar-products'][2]//a[contains(@class, 'previous')]" timeout="30"/> <element name="pageNumber" type="text" selector="//*[@class='toolbar toolbar-products'][2]//a[contains(@class, 'page')]//span[2][contains(text() ,'{{var1}}')]" parameterized="true"/> <element name="perPage" type="select" selector="//*[@class='toolbar toolbar-products'][2]//select[@id='limiter']"/> + <element name="currentPage" type="text" selector=".products.wrapper + .toolbar-products .pages .current span:nth-of-type(2)"/> </section> </sections> diff --git a/app/code/Magento/Catalog/Test/Mftf/Section/StorefrontCategoryFilterSection.xml b/app/code/Magento/Catalog/Test/Mftf/Section/StorefrontCategoryFilterSection.xml index faee605e77319..ddec4428f90e2 100644 --- a/app/code/Magento/Catalog/Test/Mftf/Section/StorefrontCategoryFilterSection.xml +++ b/app/code/Magento/Catalog/Test/Mftf/Section/StorefrontCategoryFilterSection.xml @@ -11,6 +11,5 @@ <section name="StorefrontCategoryFilterSection"> <element name="CategoryFilter" type="button" selector="//main//div[@class='filter-options']//div[contains(text(), 'Category')]"/> <element name="CategoryByName" type="button" selector="//main//div[@class='filter-options']//li[@class='item']//a[contains(text(), '{{var1}}')]" parameterized="true"/> - <element name="CustomPriceAttribute" type="button" selector="div.filter-options-title"/> </section> </sections> diff --git a/app/code/Magento/Catalog/Test/Mftf/Section/StorefrontCategoryMainSection.xml b/app/code/Magento/Catalog/Test/Mftf/Section/StorefrontCategoryMainSection.xml index 9a84f90edcfc0..6b67f5609f7f0 100644 --- a/app/code/Magento/Catalog/Test/Mftf/Section/StorefrontCategoryMainSection.xml +++ b/app/code/Magento/Catalog/Test/Mftf/Section/StorefrontCategoryMainSection.xml @@ -17,6 +17,9 @@ <element name="ProductItemInfo" type="button" selector=".product-item-info"/> <element name="specifiedProductItemInfo" type="button" selector="//a[@class='product-item-link'][contains(text(), '{{var1}}')]" parameterized="true"/> <element name="AddToCartBtn" type="button" selector="button.action.tocart.primary"/> + <element name="addToCartButtonProductInfoHover" type="button" selector=".product-item-info:hover button.action.tocart.primary" timeout="30"/> + <element name="addToWishListIconProductInfoHover" type="button" selector=".product-item-info:hover a.action.towishlist" timeout="30"/> + <element name="addToCompareIconProductInfoHover" type="button" selector=".product-item-info:hover a.action.tocompare" timeout="30"/> <element name="addToCartProductBySku" type="button" selector="//form[@data-product-sku='{{productSku}}']//button[contains(@class, 'tocart')]" parameterized="true" /> <element name="SuccessMsg" type="button" selector="div.message-success"/> <element name="productCount" type="text" selector="#toolbar-amount"/> @@ -27,6 +30,7 @@ <element name="productLink" type="text" selector="a.product-item-link" timeout="30"/> <element name="productLinkByHref" type="text" selector="a.product-item-link[href$='{{var1}}.html']" parameterized="true"/> <element name="productPrice" type="text" selector=".price-final_price"/> + <element name="productPriceByName" type="text" selector="//a[contains(text(), '{{productName}}')]//ancestor::div[contains(@class, 'product-item-info')]//span[contains(@class, 'price')]" parameterized="true"/> <element name="categoryImage" type="text" selector=".category-image"/> <element name="emptyProductMessage" type="block" selector=".message.info.empty>div"/> <element name="lineProductName" type="text" selector=".products.list.items.product-items li:nth-of-type({{line}}) .product-item-link" timeout="30" parameterized="true"/> diff --git a/app/code/Magento/Catalog/Test/Mftf/Section/StorefrontCategoryProductSection.xml b/app/code/Magento/Catalog/Test/Mftf/Section/StorefrontCategoryProductSection.xml index 4114d64eb39af..61e6a345b9ba5 100644 --- a/app/code/Magento/Catalog/Test/Mftf/Section/StorefrontCategoryProductSection.xml +++ b/app/code/Magento/Catalog/Test/Mftf/Section/StorefrontCategoryProductSection.xml @@ -21,7 +21,7 @@ <element name="ProductTitleByName" type="button" selector="//main//li//a[contains(text(), '{{var1}}')]" parameterized="true"/> <element name="ProductPriceByName" type="text" selector="//main//li[.//a[contains(text(), '{{var1}}')]]//span[@class='price']" parameterized="true"/> - <element name="ProductCatalogRuleSpecialPriceTitleByName" type="text" selector="//div[descendant::*[contains(text(), '{{var1}}')]]//*[contains(@class, 'special-price')]" parameterized="true"/> + <element name="ProductCatalogRuleSpecialPriceTitleByName" type="text" selector="//div[contains(@class, 'product-item-info')][.//a[contains(text(), '{{productName}}')]]//span[@class='special-price']/span" parameterized="true"/> <element name="ProductCatalogRulePriceTitleByName" type="text" selector="//div[descendant::*[contains(text(), '{{var1}}')]]//*[contains(@class, 'price-label')]" parameterized="true"/> <element name="ProductImageByName" type="text" selector="//main//li[.//a[contains(text(), '{{var1}}')]]//img[@class='product-image-photo']" parameterized="true"/> <element name="ProductImageBySrc" type="text" selector=".products-grid img[src*='{{pattern}}']" parameterized="true"/> diff --git a/app/code/Magento/Catalog/Test/Mftf/Section/StorefrontCategorySidebarSection.xml b/app/code/Magento/Catalog/Test/Mftf/Section/StorefrontCategorySidebarSection.xml deleted file mode 100644 index 2ec7997f87b7e..0000000000000 --- a/app/code/Magento/Catalog/Test/Mftf/Section/StorefrontCategorySidebarSection.xml +++ /dev/null @@ -1,22 +0,0 @@ -<?xml version="1.0" encoding="UTF-8"?> -<!-- -/** - * Copyright © Magento, Inc. All rights reserved. - * See COPYING.txt for license details. - */ ---> - -<sections xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="urn:magento:mftf:Page/etc/SectionObject.xsd"> - <section name="StorefrontCategorySidebarSection"> - <element name="layeredFilterBlock" type="block" selector="#layered-filter-block"/> - <element name="filterOptionsTitle" type="text" selector="//div[@class='filter-options-title' and contains(text(), '{{var1}}')]" parameterized="true"/> - <element name="filterOptions" type="text" selector=".filter-options-content .items"/> - <element name="filterOption" type="text" selector=".filter-options-content .item"/> - <element name="optionQty" type="text" selector=".filter-options-content .item .count"/> - <element name="filterOptionByLabel" type="button" selector=" div.filter-options-item div[option-label='{{optionLabel}}']" parameterized="true"/> - <element name="removeFilter" type="button" selector="div.filter-current .remove"/> - </section> - <section name="StorefrontCategorySidebarMobileSection"> - <element name="shopByButton" type="button" selector="//div[contains(@class, 'filter-title')]/strong[contains(text(), 'Shop By')]"/> - </section> -</sections> \ No newline at end of file diff --git a/app/code/Magento/Catalog/Test/Mftf/Section/StorefrontCategorySidebarSection/StorefrontCategorySidebarMobileSection.xml b/app/code/Magento/Catalog/Test/Mftf/Section/StorefrontCategorySidebarSection/StorefrontCategorySidebarMobileSection.xml new file mode 100644 index 0000000000000..68a8f207a356f --- /dev/null +++ b/app/code/Magento/Catalog/Test/Mftf/Section/StorefrontCategorySidebarSection/StorefrontCategorySidebarMobileSection.xml @@ -0,0 +1,13 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + /** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +--> +<sections xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Page/etc/SectionObject.xsd"> + <section name="StorefrontCategorySidebarMobileSection"> + <element name="shopByButton" type="button" selector="//div[contains(@class, 'filter-title')]/strong[contains(text(), 'Shop By')]"/> + </section> +</sections> diff --git a/app/code/Magento/Catalog/Test/Mftf/Section/StorefrontCategorySidebarSection/StorefrontCategorySidebarSection.xml b/app/code/Magento/Catalog/Test/Mftf/Section/StorefrontCategorySidebarSection/StorefrontCategorySidebarSection.xml new file mode 100644 index 0000000000000..186d0cf313d96 --- /dev/null +++ b/app/code/Magento/Catalog/Test/Mftf/Section/StorefrontCategorySidebarSection/StorefrontCategorySidebarSection.xml @@ -0,0 +1,21 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + /** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +--> +<sections xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Page/etc/SectionObject.xsd"> + <section name="StorefrontCategorySidebarSection"> + <element name="layeredFilterBlock" type="block" selector="#layered-filter-block"/> + <element name="filterOptionsTitle" type="text" selector="//div[@class='filter-options-title' and contains(text(), '{{var1}}')]" parameterized="true"/> + <element name="filterOptions" type="text" selector=".filter-options-content .items"/> + <element name="filterOption" type="text" selector=".filter-options-content .item"/> + <element name="optionQty" type="text" selector=".filter-options-content .item .count"/> + <element name="filterOptionByLabel" type="button" selector=" div.filter-options-item div[data-option-label='{{optionLabel}}']" parameterized="true"/> + <element name="removeFilter" type="button" selector="div.filter-current .remove"/> + <element name="activeFilterOptions" type="text" selector=".filter-options-item.active .items"/> + <element name="activeFilterOptionItemByPosition" type="text" selector=".filter-options-item.active .items li:nth-child({{itemPosition}}) a" parameterized="true"/> + </section> +</sections> diff --git a/app/code/Magento/Catalog/Test/Mftf/Section/StorefrontProductInfoMainSection.xml b/app/code/Magento/Catalog/Test/Mftf/Section/StorefrontProductInfoMainSection.xml index fd412d3c7dee1..0b44d9bb0ea6b 100644 --- a/app/code/Magento/Catalog/Test/Mftf/Section/StorefrontProductInfoMainSection.xml +++ b/app/code/Magento/Catalog/Test/Mftf/Section/StorefrontProductInfoMainSection.xml @@ -34,6 +34,7 @@ <element name="specialPriceValue" type="text" selector="//span[@class='special-price']//span[@class='price']"/> <element name="mapPrice" type="text" selector="//div[@class='price-box price-final_price']//span[contains(@class, 'price-msrp_price')]"/> <element name="clickForPriceLink" type="text" selector="//div[@class='price-box price-final_price']//a[contains(text(), 'Click for price')]"/> + <element name="addReviewLink" type="text" selector="//div[@class='reviews-actions']//a[@class='action add']"/> <!-- The parameter is the nth custom option that you want to get --> <element name="nthCustomOption" type="block" selector="//*[@id='product-options-wrapper']/*[@class='fieldset']/*[contains(@class, 'field')][{{customOptionNum}}]" parameterized="true" /> @@ -61,6 +62,7 @@ <element name="productAttributeOptionsPrice" type="text" selector="//label[contains(.,'{{var1}}')]//span[@data-price-amount='{{var2}}']" parameterized="true"/> <element name="productAttributeOptionsDropDown" type="text" selector="//label[contains(.,'{{var1}}')]/../div[@class='control']//select//option[@price='{{var2}}']" parameterized="true"/> <element name="productAttributeOptionsRadioButtons" type="text" selector="//label[contains(.,'{{var1}}')]/../div[@class='control']//span[@data-price-amount='{{var2}}']" parameterized="true"/> + <element name="productAttributeOptionsRadioButtonByName" type="checkbox" selector="//*[@id='product-options-wrapper']//div[contains(@class,'fieldset')]//label[contains(.,'{{attributeName}}')]/../div[contains(@class,'control')]//label[contains(@class,'label') and contains(.,'{{optionName}}')]/preceding-sibling::input[@type='radio']" parameterized="true"/> <element name="productAttributeOptionsCheckbox" type="text" selector="//label[contains(.,'{{var1}}')]/../div[@class='control']//span[@data-price-amount='{{var2}}']" parameterized="true"/> <element name="productAttributeOptionsMultiselect" type="text" selector="//label[contains(.,'{{var1}}')]/../div[@class='control']//select//option[@price='{{var2}}']" parameterized="true"/> <element name="productAttributeOptionsData" type="text" selector="//span[contains(.,'{{var1}}')]/../span[@class='price-notice']//span[@data-price-amount='{{var2}}']" parameterized="true"/> @@ -103,5 +105,7 @@ <element name="customOptionHour" type="date" selector="//div[@class='field date required']//span[text()='{{option}}']/../..//div/select[@data-calendar-role='hour']" parameterized="true"/> <element name="customOptionMinute" type="date" selector="//div[@class='field date required']//span[text()='{{option}}']/../..//div/select[@data-calendar-role='minute']" parameterized="true"/> <element name="customOptionDayPart" type="date" selector="//div[@class='field date required']//span[text()='{{option}}']/../..//div/select[@data-calendar-role='day_part']" parameterized="true"/> + + <element name="addToCartEnabled" type="button" selector="#product-addtocart-button:not([disabled])"/> </section> </sections> diff --git a/app/code/Magento/Catalog/Test/Mftf/Section/StorefrontProductMediaSection.xml b/app/code/Magento/Catalog/Test/Mftf/Section/StorefrontProductMediaSection.xml index 6ed359e35ab59..447113ea65bb2 100644 --- a/app/code/Magento/Catalog/Test/Mftf/Section/StorefrontProductMediaSection.xml +++ b/app/code/Magento/Catalog/Test/Mftf/Section/StorefrontProductMediaSection.xml @@ -10,12 +10,17 @@ xsi:noNamespaceSchemaLocation="urn:magento:mftf:Page/etc/SectionObject.xsd"> <section name="StorefrontProductMediaSection"> <element name="gallerySpinner" type="block" selector="#maincontent .fotorama__spinner--show" /> - <element name="gallery" type="block" selector="[data-gallery-role='gallery']" /> + <element name="gallery" type="block" selector="[data-gallery-role='gallery']" timeout="30"/> <element name="productImage" type="text" selector="//*[@data-gallery-role='gallery' and not(contains(@class, 'fullscreen'))]//img[contains(@src, '{{filename}}') and not(contains(@class, 'full'))]" parameterized="true" /> <element name="productImageFullscreen" type="text" selector="//*[@data-gallery-role='gallery' and contains(@class, 'fullscreen')]//img[contains(@src, '{{filename}}') and contains(@class, 'full')]" parameterized="true" /> <element name="closeFullscreenImage" type="button" selector="//*[@data-gallery-role='gallery' and contains(@class, 'fullscreen')]//*[@data-gallery-role='fotorama__fullscreen-icon']" /> <element name="imageFile" type="text" selector="//*[@class='product media']//img[contains(@src, '{{filename}}')]" parameterized="true"/> <element name="productImageActive" type="text" selector=".product.media div[data-active=true] > img[src*='{{filename}}']" parameterized="true"/> <element name="productImageInFotorama" type="file" selector=".fotorama__nav__shaft img[src*='{{imageName}}']" parameterized="true"/> + <element name="fotoramaPrevButton" type="button" selector="//*[@data-gallery-role='gallery']//*[@data-gallery-role='nav-wrap']//*[@data-gallery-role='arrow' and contains(@class, 'fotorama__thumb__arr--left')]"/> + <element name="fotoramaNextButton" type="button" selector="//*[@data-gallery-role='gallery']//*[@data-gallery-role='nav-wrap']//*[@data-gallery-role='arrow' and contains(@class, 'fotorama__thumb__arr--right')]"/> + <element name="fotoramaAnyMedia" type="text" selector=".fotorama__nav__shaft img"/> + <element name="fotoramaImageThumbnail" type="block" selector="//div[contains(@class, 'fotorama__nav__shaft')]//div[contains(@class, 'fotorama__nav__frame--thumb')][{{imageNumber}}]" parameterized="true" timeout="30"/> + <element name="fotoramaImageThumbnailActive" type="block" selector="//div[contains(@class, 'fotorama__nav__shaft')]//div[contains(@class, 'fotorama__nav__frame--thumb') and contains(@class, 'fotorama__active')][{{imageNumber}}]" parameterized="true" timeout="30"/> </section> </sections> diff --git a/app/code/Magento/Catalog/Test/Mftf/Test/AddOutOfStockProductToCompareListTest.xml b/app/code/Magento/Catalog/Test/Mftf/Test/AddOutOfStockProductToCompareListTest.xml index 9a0f5ad002725..e42dd8b8ab12e 100644 --- a/app/code/Magento/Catalog/Test/Mftf/Test/AddOutOfStockProductToCompareListTest.xml +++ b/app/code/Magento/Catalog/Test/Mftf/Test/AddOutOfStockProductToCompareListTest.xml @@ -20,7 +20,7 @@ <group value="Catalog"/> </annotations> <before> - <actionGroup ref="LoginAsAdmin" stepKey="loginAsAdmin"/> + <actionGroup ref="AdminLoginActionGroup" stepKey="loginAsAdmin"/> <magentoCLI command="config:set {{CatalogInventoryOptionsShowOutOfStockDisable.path}} {{CatalogInventoryOptionsShowOutOfStockDisable.value}}" stepKey="setConfigShowOutOfStockFalse"/> <magentoCLI command="cache:flush" stepKey="flushCache"/> <createData entity="SimpleSubCategory" stepKey="category"/> @@ -33,7 +33,7 @@ <magentoCLI command="cache:flush" stepKey="flushCache"/> <deleteData createDataKey="product" stepKey="deleteProduct"/> <deleteData createDataKey="category" stepKey="deleteCategory"/> - <actionGroup ref="logout" stepKey="logout"/> + <actionGroup ref="AdminLogoutActionGroup" stepKey="logout"/> </after> <!--Open product page--> <comment userInput="Open product page" stepKey="openProdPage"/> @@ -60,8 +60,11 @@ <waitForPageLoad stepKey="waitForProdAddToCmpList"/> <!--Assert success message--> <comment userInput="Assert success message" stepKey="assertSuccessMsg"/> - <grabTextFrom selector="{{AdminProductMessagesSection.successMessage}}" stepKey="grabTextFromSuccessMessage"/> - <assertEquals expected='You added product $$product.name$$ to the comparison list.' expectedType="string" actual="($grabTextFromSuccessMessage)" stepKey="assertSuccessMessage"/> + <grabTextFrom selector="{{StorefrontMessagesSection.success}}" stepKey="grabTextFromSuccessMessage"/> + <assertEquals stepKey="assertSuccessMessage"> + <actualResult type="const">($grabTextFromSuccessMessage)</actualResult> + <expectedResult type="string">You added product $$product.name$$ to the comparison list.</expectedResult> + </assertEquals> <!--See product in the comparison list--> <comment userInput="See product in the comparison list" stepKey="seeProductInComparisonList"/> <amOnPage url="{{StorefrontProductComparePage.url}}" stepKey="navigateToComparePage"/> @@ -82,8 +85,11 @@ <waitForPageLoad stepKey="waitProdAddingToCmpList"/> <!--Assert success message--> <comment userInput="Assert success message" stepKey="assertSuccessMsg2"/> - <grabTextFrom selector="{{AdminProductMessagesSection.successMessage}}" stepKey="grabTextFromSuccessMessage2"/> - <assertEquals expected='You added product $$product.name$$ to the comparison list.' expectedType="string" actual="($grabTextFromSuccessMessage)" stepKey="assertSuccessMessage2"/> + <grabTextFrom selector="{{StorefrontMessagesSection.success}}" stepKey="grabTextFromSuccessMessage2"/> + <assertEquals stepKey="assertSuccessMessage2"> + <actualResult type="const">($grabTextFromSuccessMessage)</actualResult> + <expectedResult type="string">You added product $$product.name$$ to the comparison list.</expectedResult> + </assertEquals> <!--Check that product displays on add to compare widget--> <comment userInput="Check that product displays on add to compare widget" stepKey="checkProdNameOnWidget"/> <seeElement selector="{{StorefrontComparisonSidebarSection.ProductTitleByName($$product.name$$)}}" stepKey="seeProdNameOnCmpWidget"/> diff --git a/app/code/Magento/Catalog/Test/Mftf/Test/AddToCartCrossSellTest.xml b/app/code/Magento/Catalog/Test/Mftf/Test/AddToCartCrossSellTest.xml index 7c0161d443df6..83a05fef83607 100644 --- a/app/code/Magento/Catalog/Test/Mftf/Test/AddToCartCrossSellTest.xml +++ b/app/code/Magento/Catalog/Test/Mftf/Test/AddToCartCrossSellTest.xml @@ -7,14 +7,14 @@ --> <tests xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" - xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/testSchema.xsd"> + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/testSchema.xsd"> <test name="AddToCartCrossSellTest"> <annotations> <features value="Catalog"/> <stories value="Promote Products as Cross-Sells"/> <title value="Admin should be able to add cross-sell to products."/> <description value="Create products, add products to cross sells, and check that they appear in the Shopping Cart page."/> - <severity value="MAJOR"/> + <severity value="BLOCKER"/> <testCaseId value="MC-9143"/> <group value="Catalog"/> </annotations> @@ -30,10 +30,10 @@ <requiredEntity createDataKey="category1"/> </createData> - <actionGroup ref="LoginAsAdmin" stepKey="logInAsAdmin"/> + <actionGroup ref="AdminLoginActionGroup" stepKey="logInAsAdmin"/> </before> <after> - <actionGroup ref="logout" stepKey="logoutFromAdmin"/> + <actionGroup ref="AdminLogoutActionGroup" stepKey="logoutFromAdmin"/> <deleteData createDataKey="simpleProduct1" stepKey="deleteSimp1"/> <deleteData createDataKey="simpleProduct2" stepKey="deleteSimp2"/> @@ -42,7 +42,9 @@ </after> <!-- Go to simpleProduct1, add simpleProduct2 and simpleProduct3 as cross-sell--> - <amOnPage url="{{AdminProductEditPage.url($simpleProduct1.id$)}}" stepKey="goToProduct1"/> + <actionGroup ref="AdminProductPageOpenByIdActionGroup" stepKey="goToProduct1"> + <argument name="productId" value="$simpleProduct1.id$"/> + </actionGroup> <click stepKey="openHeader1" selector="{{AdminProductFormRelatedUpSellCrossSellSection.sectionHeader}}"/> <actionGroup ref="AddCrossSellProductBySkuActionGroup" stepKey="addProduct2ToSimp1"> @@ -55,7 +57,9 @@ <waitForPageLoad stepKey="waitForPageLoad1"/> <!-- Go to simpleProduct3, add simpleProduct1 and simpleProduct2 as cross-sell--> - <amOnPage url="{{AdminProductEditPage.url($simpleProduct3.id$)}}" stepKey="goToProduct3"/> + <actionGroup ref="AdminProductPageOpenByIdActionGroup" stepKey="goToProduct3"> + <argument name="productId" value="$simpleProduct3.id$"/> + </actionGroup> <click stepKey="openHeader2" selector="{{AdminProductFormRelatedUpSellCrossSellSection.sectionHeader}}"/> <actionGroup ref="AddCrossSellProductBySkuActionGroup" stepKey="addProduct1ToSimp3"> diff --git a/app/code/Magento/Catalog/Test/Mftf/Test/AdminAddDefaultImageSimpleProductTest.xml b/app/code/Magento/Catalog/Test/Mftf/Test/AdminAddDefaultImageSimpleProductTest.xml index ac1f967b66e41..92f24fe76502d 100644 --- a/app/code/Magento/Catalog/Test/Mftf/Test/AdminAddDefaultImageSimpleProductTest.xml +++ b/app/code/Magento/Catalog/Test/Mftf/Test/AdminAddDefaultImageSimpleProductTest.xml @@ -14,12 +14,12 @@ <stories value="Add/remove images and videos for all product types and category"/> <title value="Admin should be able to add default image for a Simple Product"/> <description value="Admin should be able to add default images for a Simple Product"/> - <severity value="MAJOR"/> + <severity value="BLOCKER"/> <testCaseId value="MC-113"/> <group value="Catalog"/> </annotations> <before> - <actionGroup ref="LoginAsAdmin" stepKey="loginAsAdmin"/> + <actionGroup ref="AdminLoginActionGroup" stepKey="loginAsAdmin"/> </before> <after> <amOnPage url="{{AdminLogoutPage.url}}" stepKey="amOnLogoutPage"/> diff --git a/app/code/Magento/Catalog/Test/Mftf/Test/AdminAddDefaultImageVirtualProductTest.xml b/app/code/Magento/Catalog/Test/Mftf/Test/AdminAddDefaultImageVirtualProductTest.xml index 367827f8c0c28..7cf388914207b 100644 --- a/app/code/Magento/Catalog/Test/Mftf/Test/AdminAddDefaultImageVirtualProductTest.xml +++ b/app/code/Magento/Catalog/Test/Mftf/Test/AdminAddDefaultImageVirtualProductTest.xml @@ -19,7 +19,7 @@ <group value="Catalog"/> </annotations> <before> - <actionGroup ref="LoginAsAdmin" stepKey="loginAsAdmin"/> + <actionGroup ref="AdminLoginActionGroup" stepKey="loginAsAdmin"/> </before> <after> <amOnPage url="{{AdminLogoutPage.url}}" stepKey="amOnLogoutPage"/> diff --git a/app/code/Magento/Catalog/Test/Mftf/Test/AdminAddDefaultVideoSimpleProductTest.xml b/app/code/Magento/Catalog/Test/Mftf/Test/AdminAddDefaultVideoSimpleProductTest.xml index 0b33ef0ac0783..61d197d34a31d 100644 --- a/app/code/Magento/Catalog/Test/Mftf/Test/AdminAddDefaultVideoSimpleProductTest.xml +++ b/app/code/Magento/Catalog/Test/Mftf/Test/AdminAddDefaultVideoSimpleProductTest.xml @@ -14,13 +14,16 @@ <stories value="Add/remove images and videos for all product types and category"/> <title value="Admin should be able to add default product video for a Simple Product"/> <description value="Admin should be able to add default product video for a Simple Product"/> - <severity value="MAJOR"/> + <severity value="BLOCKER"/> <testCaseId value="MC-111"/> <group value="Catalog"/> + <skip> + <issueId value="MC-32197"/> + </skip> </annotations> <before> <actionGroup ref="EnableAdminAccountSharingActionGroup" stepKey="enableAdminAccountSharing"/> - <actionGroup ref="LoginAsAdmin" stepKey="loginAsAdmin"/> + <actionGroup ref="AdminLoginActionGroup" stepKey="loginAsAdmin"/> </before> <after> <amOnPage url="{{AdminLogoutPage.url}}" stepKey="amOnLogoutPage"/> diff --git a/app/code/Magento/Catalog/Test/Mftf/Test/AdminAddDefaultVideoVirtualProductTest.xml b/app/code/Magento/Catalog/Test/Mftf/Test/AdminAddDefaultVideoVirtualProductTest.xml index e89cf6f4242e7..e1c81c8ae0303 100644 --- a/app/code/Magento/Catalog/Test/Mftf/Test/AdminAddDefaultVideoVirtualProductTest.xml +++ b/app/code/Magento/Catalog/Test/Mftf/Test/AdminAddDefaultVideoVirtualProductTest.xml @@ -17,6 +17,9 @@ <severity value="MAJOR"/> <testCaseId value="MC-109"/> <group value="Catalog"/> + <skip> + <issueId value="MC-32197"/> + </skip> </annotations> <!-- Replacing steps in base AdminAddDefaultVideoSimpleProductTest --> diff --git a/app/code/Magento/Catalog/Test/Mftf/Test/AdminAddImageForCategoryTest.xml b/app/code/Magento/Catalog/Test/Mftf/Test/AdminAddImageForCategoryTest.xml index ed9eb686d2c86..e4cf255a03e05 100644 --- a/app/code/Magento/Catalog/Test/Mftf/Test/AdminAddImageForCategoryTest.xml +++ b/app/code/Magento/Catalog/Test/Mftf/Test/AdminAddImageForCategoryTest.xml @@ -19,13 +19,13 @@ <group value="Catalog"/> </annotations> <before> - <actionGroup ref="LoginAsAdmin" stepKey="loginAsAdmin"/> + <actionGroup ref="AdminLoginActionGroup" stepKey="loginAsAdmin"/> </before> <after> <actionGroup ref="DeleteCategoryActionGroup" stepKey="DeleteCategory"> <argument name="categoryEntity" value="SimpleSubCategory"/> </actionGroup> - <actionGroup ref="logout" stepKey="logout"/> + <actionGroup ref="AdminLogoutActionGroup" stepKey="logout"/> </after> <!-- Go to create a new category with image --> @@ -34,7 +34,7 @@ <argument name="categoryEntity" value="SimpleSubCategory"/> </actionGroup> <actionGroup ref="AddCategoryImageActionGroup" stepKey="addCategoryImage"/> - <actionGroup ref="SaveCategoryFormActionGroup" stepKey="saveCategoryForm"/> + <actionGroup ref="AdminSaveCategoryFormActionGroup" stepKey="saveCategoryForm"/> <!-- Verify category with image in admin --> <actionGroup ref="CheckCategoryImageInAdminActionGroup" stepKey="checkCategoryImageInAdmin"/> diff --git a/app/code/Magento/Catalog/Test/Mftf/Test/AdminAddImageToWYSIWYGCatalogTest.xml b/app/code/Magento/Catalog/Test/Mftf/Test/AdminAddImageToWYSIWYGCatalogTest.xml index b3e6fcd3bfb55..1850faebd16c6 100644 --- a/app/code/Magento/Catalog/Test/Mftf/Test/AdminAddImageToWYSIWYGCatalogTest.xml +++ b/app/code/Magento/Catalog/Test/Mftf/Test/AdminAddImageToWYSIWYGCatalogTest.xml @@ -9,7 +9,7 @@ xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/testSchema.xsd"> <test name="AdminAddImageToWYSIWYGCatalogTest"> <before> - <actionGroup ref="LoginActionGroup" stepKey="login"/> + <actionGroup ref="AdminLoginActionGroup" stepKey="login"/> <actionGroup ref="EnabledWYSIWYGActionGroup" stepKey="enableWYSIWYG"/> <actionGroup ref="SwitchToVersion4ActionGroup" stepKey="switchToTinyMCE4" /> </before> @@ -55,7 +55,7 @@ <argument name="categoryEntity" value="SimpleSubCategory"/> </actionGroup> <actionGroup ref="DisabledWYSIWYGActionGroup" stepKey="disableWYSIWYG"/> - <actionGroup ref="logout" stepKey="logout"/> + <actionGroup ref="AdminLogoutActionGroup" stepKey="logout"/> </after> </test> </tests> diff --git a/app/code/Magento/Catalog/Test/Mftf/Test/AdminAddImageToWYSIWYGProductTest.xml b/app/code/Magento/Catalog/Test/Mftf/Test/AdminAddImageToWYSIWYGProductTest.xml index edf37542fd830..d86a696880bae 100644 --- a/app/code/Magento/Catalog/Test/Mftf/Test/AdminAddImageToWYSIWYGProductTest.xml +++ b/app/code/Magento/Catalog/Test/Mftf/Test/AdminAddImageToWYSIWYGProductTest.xml @@ -18,13 +18,13 @@ <testCaseId value="MAGETWO-84375"/> </annotations> <before> - <actionGroup ref="LoginActionGroup" stepKey="login"/> + <actionGroup ref="AdminLoginActionGroup" stepKey="login"/> <actionGroup ref="EnabledWYSIWYGActionGroup" stepKey="enableWYSIWYG"/> <actionGroup ref="SwitchToVersion4ActionGroup" stepKey="switchToTinyMCE4" /> </before> <after> <actionGroup ref="DisabledWYSIWYGActionGroup" stepKey="disableWYSIWYG"/> - <actionGroup ref="logout" stepKey="logout"/> + <actionGroup ref="AdminLogoutActionGroup" stepKey="logout"/> </after> <amOnPage url="{{AdminProductCreatePage.url(AddToDefaultSet.attributeSetId, 'simple')}}" stepKey="navigateToNewProduct"/> diff --git a/app/code/Magento/Catalog/Test/Mftf/Test/AdminAddInStockProductToTheCartTest.xml b/app/code/Magento/Catalog/Test/Mftf/Test/AdminAddInStockProductToTheCartTest.xml index b98ca3a375a17..52da8c70a3bc8 100644 --- a/app/code/Magento/Catalog/Test/Mftf/Test/AdminAddInStockProductToTheCartTest.xml +++ b/app/code/Magento/Catalog/Test/Mftf/Test/AdminAddInStockProductToTheCartTest.xml @@ -19,7 +19,7 @@ </annotations> <before> <!-- Login as admin --> - <actionGroup ref="LoginAsAdmin" stepKey="login"/> + <actionGroup ref="AdminLoginActionGroup" stepKey="login"/> <!--Create Category--> <createData entity="SimpleSubCategory" stepKey="createCategory"/> <!--Create Simple Product--> @@ -31,7 +31,7 @@ <!--Delete created entity --> <deleteData createDataKey="createCategory" stepKey="deleteCategory"/> <deleteData createDataKey="createSimpleProduct" stepKey="deleteSimpleProduct"/> - <actionGroup ref="logout" stepKey="logout"/> + <actionGroup ref="AdminLogoutActionGroup" stepKey="logout"/> </after> <!--Open Product Index Page and filter the product--> <amOnPage url="{{AdminProductIndexPage.url}}" stepKey="navigateToProductIndex"/> @@ -83,7 +83,7 @@ <waitForElementVisible selector="{{StorefrontMessagesSection.success}}" stepKey="waitForSuccessMessage"/> <seeElement selector="{{StorefrontProductPageSection.successMsg}}" stepKey="seeSuccessSaveMessage"/> <seeElement selector="{{StorefrontMinicartSection.quantity(1)}}" stepKey="seeAddedProductQuantityInCart"/> - <click selector="{{StorefrontMinicartSection.showCart}}" stepKey="clickOnMiniCart"/> + <actionGroup ref="StorefrontClickOnMiniCartActionGroup" stepKey="clickOnMiniCart"/> <see selector="{{StorefrontMinicartSection.miniCartItemsText}}" userInput="{{SimpleProduct.name}}" stepKey="seeProductNameInMiniCart"/> <see selector="{{StorefrontMinicartSection.miniCartItemsText}}" userInput="{{SimpleProduct.price}}" stepKey="seeProductPriceInMiniCart"/> <seeElement selector="{{StorefrontMinicartSection.goToCheckout}}" stepKey="seeCheckOutButtonInMiniCart"/> diff --git a/app/code/Magento/Catalog/Test/Mftf/Test/AdminApplyTierPriceToProductTest.xml b/app/code/Magento/Catalog/Test/Mftf/Test/AdminApplyTierPriceToProductTest/AdminApplyTierPriceToProductTest.xml similarity index 82% rename from app/code/Magento/Catalog/Test/Mftf/Test/AdminApplyTierPriceToProductTest.xml rename to app/code/Magento/Catalog/Test/Mftf/Test/AdminApplyTierPriceToProductTest/AdminApplyTierPriceToProductTest.xml index 6edb7daf50026..b2181164070dc 100644 --- a/app/code/Magento/Catalog/Test/Mftf/Test/AdminApplyTierPriceToProductTest.xml +++ b/app/code/Magento/Catalog/Test/Mftf/Test/AdminApplyTierPriceToProductTest/AdminApplyTierPriceToProductTest.xml @@ -5,7 +5,8 @@ * See COPYING.txt for license details. */ --> -<tests xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/testSchema.xsd"> +<tests xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/testSchema.xsd"> <test name="AdminApplyTierPriceToProductTest"> <annotations> <features value="Catalog"/> @@ -33,9 +34,9 @@ <amOnPage url="{{AdminProductIndexPage.url}}" stepKey="navigateToProductIndex1"/> <waitForPageLoad time="30" stepKey="waitForProductIndexPageLoad"/> <actionGroup ref="ResetProductGridToDefaultViewActionGroup" stepKey="resetGridToDefaultKeywordSearch"/> - <actionGroup ref="logout" stepKey="logoutFromAdmin"/> + <actionGroup ref="AdminLogoutActionGroup" stepKey="logoutFromAdmin"/> </after> - <actionGroup ref="LoginAsAdmin" stepKey="loginAsAdmin"/> + <actionGroup ref="AdminLoginActionGroup" stepKey="loginAsAdmin"/> <!--Case: Group Price--> <actionGroup ref="SearchForProductOnBackendActionGroup" stepKey="searchForSimpleProduct"> <argument name="product" value="$$createSimpleProduct$$"/> @@ -53,15 +54,15 @@ <click selector="{{AdminProductFormAdvancedPricingSection.doneButton}}" stepKey="clickDoneButton1"/> <actionGroup ref="SaveProductFormActionGroup" stepKey="saveProduct1"/> <actionGroup ref="LoginToStorefrontActionGroup" stepKey="customerLogin1"> - <argument name="Customer" value="$$createSimpleUSCustomer$$" /> + <argument name="Customer" value="$$createSimpleUSCustomer$$"/> </actionGroup> <amOnPage url="{{StorefrontCategoryPage.url($$createCategory.name$$)}}" stepKey="navigateToCategoryPage1"/> <waitForPageLoad time="30" stepKey="waitForPageLoad1"/> <seeElement selector="{{StorefrontCategoryProductSection.productPriceFinal('90')}}" stepKey="assertProductFinalPriceIs90_1"/> <seeElement selector="{{StorefrontCategoryProductSection.productPriceLabel('Regular Price')}}" stepKey="assertRegularPriceLabel_1"/> <seeElement selector="{{StorefrontCategoryProductSection.productPriceOld('100')}}" stepKey="assertRegularPriceAmount_1"/> - <amOnPage url="customer/account/logout/" stepKey="logoutCustomer1"/> - <waitForPageLoad time="30" stepKey="waitForPageLoad2"/> + <amOnPage url="{{StorefrontCustomerLogoutPage.url}}" stepKey="logoutCustomer1"/> + <waitForPageLoad time="30" stepKey="waitForPageLoad2"/> <amOnPage url="{{StorefrontCategoryPage.url($$createCategory.name$$)}}" stepKey="navigateToCategoryPage2"/> <waitForPageLoad time="30" stepKey="waitForPageLoad3"/> <seeElement selector="{{StorefrontCategoryProductSection.productPriceFinal('90')}}" stepKey="assertProductFinalPriceIs90_2"/> @@ -85,7 +86,7 @@ <seeElement selector="{{StorefrontCategoryProductSection.productPriceFinal('100')}}" stepKey="assertProductFinalPriceIs100_1"/> <dontSeeElement selector="{{StorefrontCategoryProductSection.productPriceLabel('Regular Price')}}" stepKey="assertRegularPriceLabel_3"/> <actionGroup ref="LoginToStorefrontActionGroup" stepKey="customerLogin2"> - <argument name="Customer" value="$$createSimpleUSCustomer$$" /> + <argument name="Customer" value="$$createSimpleUSCustomer$$"/> </actionGroup> <amOnPage url="{{StorefrontCategoryPage.url($$createCategory.name$$)}}" stepKey="navigateToCategoryPage4"/> <waitForPageLoad time="30" stepKey="waitForPageLoad5"/> @@ -116,8 +117,8 @@ <seeElement selector="{{StorefrontCategoryProductSection.productPriceFinal('100')}}" stepKey="assertProductFinalPriceIs100_2"/> <seeElement selector="{{StorefrontCategoryProductSection.productPriceLabel('As low as')}}" stepKey="assertAsLowAsPriceLabel_1"/> <seeElement selector="{{StorefrontCategoryProductSection.productPriceLinkAfterLabel('As low as', '82')}}" stepKey="assertPriceAfterAsLowAsLabel_1"/> - <amOnPage url="customer/account/logout/" stepKey="logoutCustomer2"/> - <waitForPageLoad time="30" stepKey="waitForPageLoad7"/> + <amOnPage url="{{StorefrontCustomerLogoutPage.url}}" stepKey="logoutCustomer2"/> + <waitForPageLoad time="30" stepKey="waitForPageLoad7"/> <amOnPage url="{{StorefrontCategoryPage.url($$createCategory.name$$)}}" stepKey="navigateToCategoryPage6"/> <waitForPageLoad time="30" stepKey="waitForPageLoad8"/> <seeElement selector="{{StorefrontCategoryProductSection.productPriceFinal('100')}}" stepKey="assertProductFinalPriceIs100_3"/> @@ -137,7 +138,7 @@ </actionGroup> <actionGroup ref="ClickViewAndEditCartFromMiniCartActionGroup" stepKey="goToCheckoutFromMinicart"/> <seeInField userInput="10" selector="{{CheckoutCartProductSection.ProductQuantityByName($$createSimpleProduct.name$$)}}" stepKey="seeInQtyField10"/> - <grabTextFrom selector="{{CheckoutCartProductSection.productSubtotalByName($$createSimpleProduct.name$$)}}" stepKey="grabTextFromSubtotalField1"/> + <grabTextFrom selector="{{CheckoutCartProductSection.productSubtotalByName($$createSimpleProduct.name$$)}}" stepKey="grabTextFromSubtotalField1"/> <assertEquals message="Shopping cart should contain subtotal $1,000" stepKey="assertSubtotalField1"> <expectedResult type="string">$1,000.00</expectedResult> <actualResult type="variable">grabTextFromSubtotalField1</actualResult> @@ -145,7 +146,7 @@ <fillField userInput="15" selector="{{CheckoutCartProductSection.ProductQuantityByName($$createSimpleProduct.name$$)}}" stepKey="fillProductQuantity2"/> <click selector="{{CheckoutCartProductSection.updateShoppingCartButton}}" stepKey="clickUpdateShoppingCartButton1"/> <waitForLoadingMaskToDisappear stepKey="waitForLoadingMaskToDisappear1"/> - <grabTextFrom selector="{{CheckoutCartProductSection.productSubtotalByName($$createSimpleProduct.name$$)}}" stepKey="grabTextFromSubtotalField2"/> + <grabTextFrom selector="{{CheckoutCartProductSection.productSubtotalByName($$createSimpleProduct.name$$)}}" stepKey="grabTextFromSubtotalField2"/> <assertEquals message="Shopping cart should contain subtotal $1,350" stepKey="assertSubtotalField2"> <expectedResult type="string">$1,350.00</expectedResult> <actualResult type="variable">grabTextFromSubtotalField2</actualResult> @@ -153,7 +154,7 @@ <fillField userInput="20" selector="{{CheckoutCartProductSection.ProductQuantityByName($$createSimpleProduct.name$$)}}" stepKey="fillProductQuantity3"/> <click selector="{{CheckoutCartProductSection.updateShoppingCartButton}}" stepKey="clickUpdateShoppingCartButton2"/> <waitForLoadingMaskToDisappear stepKey="waitForLoadingMaskToDisappear2"/> - <grabTextFrom selector="{{CheckoutCartProductSection.productSubtotalByName($$createSimpleProduct.name$$)}}" stepKey="grabTextFromSubtotalField3"/> + <grabTextFrom selector="{{CheckoutCartProductSection.productSubtotalByName($$createSimpleProduct.name$$)}}" stepKey="grabTextFromSubtotalField3"/> <assertEquals message="Shopping cart should contain subtotal $1,640" stepKey="assertSubtotalField3"> <expectedResult type="string">$1,640.00</expectedResult> <actualResult type="variable">grabTextFromSubtotalField3</actualResult> @@ -173,7 +174,7 @@ <amOnPage url="{{CheckoutCartPage.url}}" stepKey="goToShoppingCartPage1"/> <waitForPageLoad time="30" stepKey="waitForShoppingCartPagePageLoad1"/> <seeInField userInput="20" selector="{{CheckoutCartProductSection.ProductQuantityByName($$createSimpleProduct.name$$)}}" stepKey="seeInQtyField20"/> - <grabTextFrom selector="{{CheckoutCartProductSection.productSubtotalByName($$createSimpleProduct.name$$)}}" stepKey="grabTextFromSubtotalField4"/> + <grabTextFrom selector="{{CheckoutCartProductSection.productSubtotalByName($$createSimpleProduct.name$$)}}" stepKey="grabTextFromSubtotalField4"/> <assertEquals message="Shopping cart should contain subtotal $1,500" stepKey="assertSubtotalField4"> <expectedResult type="string">$1,500.00</expectedResult> <actualResult type="variable">grabTextFromSubtotalField4</actualResult> @@ -200,7 +201,7 @@ <amOnPage url="{{CheckoutCartPage.url}}" stepKey="goToShoppingCartPage2"/> <waitForPageLoad time="30" stepKey="waitForShoppingCartPagePageLoad2"/> <seeInField userInput="20" selector="{{CheckoutCartProductSection.ProductQuantityByName($$createSimpleProduct.name$$)}}" stepKey="seeInQtyField20_2"/> - <grabTextFrom selector="{{CheckoutCartProductSection.productSubtotalByName($$createSimpleProduct.name$$)}}" stepKey="grabTextFromSubtotalField5"/> + <grabTextFrom selector="{{CheckoutCartProductSection.productSubtotalByName($$createSimpleProduct.name$$)}}" stepKey="grabTextFromSubtotalField5"/> <assertEquals message="Shopping cart should contain subtotal $1,500" stepKey="assertSubtotalField5"> <expectedResult type="string">$1,500.00</expectedResult> <actualResult type="variable">grabTextFromSubtotalField5</actualResult> @@ -214,7 +215,7 @@ <amOnPage url="{{CheckoutCartPage.url}}" stepKey="goToShoppingCartPage3"/> <waitForPageLoad time="30" stepKey="waitForShoppingCartPagePageLoad3"/> <seeInField userInput="20" selector="{{CheckoutCartProductSection.ProductQuantityByName($$createSimpleProduct.name$$)}}" stepKey="seeInQtyField20_3"/> - <grabTextFrom selector="{{CheckoutCartProductSection.productSubtotalByName($$createSimpleProduct.name$$)}}" stepKey="grabTextFromSubtotalField6"/> + <grabTextFrom selector="{{CheckoutCartProductSection.productSubtotalByName($$createSimpleProduct.name$$)}}" stepKey="grabTextFromSubtotalField6"/> <assertEquals message="Shopping cart should contain subtotal $1,500" stepKey="assertSubtotalField6"> <expectedResult type="string">$1,500.00</expectedResult> <actualResult type="variable">grabTextFromSubtotalField6</actualResult> @@ -235,7 +236,7 @@ <scrollToTopOfPage stepKey="scrollToTopOfPage5"/> <click selector="{{AdminProductFormSection.advancedPricingLink}}" stepKey="clickOnAdvancedPricingButton5"/> <waitForElement selector="{{AdminProductFormAdvancedPricingSection.customerGroupPriceDeleteButton}}" stepKey="waitForcustomerGroupPriceDeleteButton"/> - <scrollTo selector="(//*[contains(@class, 'product_form_product_form_advanced_pricing_modal')]//tr//button[@data-action='remove_row'])[1]" x="30" y="0" stepKey="scrollToDeleteFirstRowOfCustomerGroupPrice" /> + <scrollTo selector="(//*[contains(@class, 'product_form_product_form_advanced_pricing_modal')]//tr//button[@data-action='remove_row'])[1]" x="30" y="0" stepKey="scrollToDeleteFirstRowOfCustomerGroupPrice"/> <click selector="(//tr//button[@data-action='remove_row'])[1]" userInput=".product_form_product_form_advanced_pricing_modal" stepKey="deleteFirstRowOfCustomerGroupPrice"/> <click selector="//tr//button[@data-action='remove_row']" userInput=".product_form_product_form_advanced_pricing_modal" stepKey="deleteSecondRowOfCustomerGroupPrice"/> <click selector="{{AdminProductFormAdvancedPricingSection.doneButton}}" userInput=".product_form_product_form_advanced_pricing_modal" stepKey="clickDoneButton5"/> @@ -251,7 +252,7 @@ <click selector="{{AdminProductFormActionSection.saveButton}}" stepKey="clickSaveButton"/> <amOnPage url="{{CheckoutCartPage.url}}" stepKey="goToShoppingCartPage4"/> <waitForPageLoad time="30" stepKey="waitForShoppingCartPagePageLoad4"/> - <grabTextFrom selector="{{CheckoutCartProductSection.productSubtotalByName($$createSimpleProduct.name$$)}}" stepKey="grabTextFromSubtotalField7"/> + <grabTextFrom selector="{{CheckoutCartProductSection.productSubtotalByName($$createSimpleProduct.name$$)}}" stepKey="grabTextFromSubtotalField7"/> <assertEquals message="Shopping cart should contain subtotal $4,000" stepKey="assertSubtotalField7"> <expectedResult type="string">$4,000.00</expectedResult> <actualResult type="variable">grabTextFromSubtotalField7</actualResult> @@ -269,56 +270,4 @@ <actualResult type="variable">grabTextFromMiniCartSubtotalField2</actualResult> </assertEquals> </test> - <test name="AdminApplyTierPriceToProductWithPercentageDiscountTest"> - <annotations> - <features value="Catalog"/> - <stories value="MC-5517 - System tries to save 0 in Advanced Pricing which is invalid for Discount field"/> - <title value="You should be able to apply tier price to a product with float percent discount."/> - <description value="You should be able to apply tier price to a product with float percent discount."/> - <severity value="AVERAGE"/> - <testCaseId value="MAGETWO-96881"/> - <group value="product"/> - </annotations> - <before> - <createData entity="_defaultCategory" stepKey="createCategory"/> - <createData entity="SimpleProduct" stepKey="createSimpleProduct"> - <requiredEntity createDataKey="createCategory"/> - <field key="price">100</field> - </createData> - </before> - <after> - <deleteData createDataKey="createSimpleProduct" stepKey="deleteSimpleProduct"/> - <deleteData createDataKey="createCategory" stepKey="deleteCategory"/> - <amOnPage url="{{AdminProductIndexPage.url}}" stepKey="navigateToProductIndex"/> - <waitForPageLoad time="30" stepKey="waitForProductIndexPageLoad"/> - <actionGroup ref="ResetProductGridToDefaultViewActionGroup" stepKey="resetGridToDefaultKeywordSearch"/> - <actionGroup ref="logout" stepKey="logoutFromAdmin"/> - </after> - <actionGroup ref="LoginAsAdmin" stepKey="loginAsAdmin"/> - <actionGroup ref="SearchForProductOnBackendActionGroup" stepKey="searchForSimpleProduct"> - <argument name="product" value="$$createSimpleProduct$$"/> - </actionGroup> - <actionGroup ref="OpenEditProductOnBackendActionGroup" stepKey="openEditProduct1"> - <argument name="product" value="$$createSimpleProduct$$"/> - </actionGroup> - <scrollToTopOfPage stepKey="scrollToTopOfPage"/> - <click selector="{{AdminProductFormSection.advancedPricingLink}}" stepKey="clickOnAdvancedPricingButton"/> - <waitForElement selector="{{AdminProductFormAdvancedPricingSection.customerGroupPriceAddButton}}" stepKey="waitForCustomerGroupPriceAddButton"/> - <click selector="{{AdminProductFormAdvancedPricingSection.customerGroupPriceAddButton}}" stepKey="addCustomerGroupAllGroupsQty1PriceDiscountAndpercent"/> - <fillField selector="{{AdminProductFormAdvancedPricingSection.productTierPriceQtyInput('0')}}" userInput="1" stepKey="fillProductTierPriceQtyInput"/> - <selectOption selector="{{AdminProductFormAdvancedPricingSection.productTierPriceValueTypeSelect('0')}}" userInput="Discount" stepKey="selectProductTierPriceValueType"/> - <fillField selector="{{AdminProductFormAdvancedPricingSection.productTierPricePercentageValuePriceInput('0')}}" userInput="0.1" stepKey="selectProductTierPricePriceInput"/> - <click selector="{{AdminProductFormAdvancedPricingSection.doneButton}}" stepKey="clickDoneButton"/> - <actionGroup ref="SaveProductFormActionGroup" stepKey="saveProduct1"/> - <amOnPage url="{{StorefrontProductPage.url($$createSimpleProduct.sku$$)}}" stepKey="goProductPageOnStorefront"/> - <waitForPageLoad time="30" stepKey="waitForPageLoad1"/> - <seeElement selector="{{StorefrontCategoryProductSection.productPriceFinal('99.90')}}" stepKey="assertProductFinalPriceProductPage"/> - <seeElement selector="{{StorefrontCategoryProductSection.productPriceLabel('Regular Price')}}" stepKey="assertRegularPriceProductPage"/> - <seeElement selector="{{StorefrontCategoryProductSection.productPriceOld('100')}}" stepKey="assertRegularPriceAmountProductPage"/> - <amOnPage url="{{StorefrontCategoryPage.url($$createCategory.name$$)}}" stepKey="navigateToCategoryPage"/> - <waitForPageLoad time="30" stepKey="waitForPageLoad2"/> - <seeElement selector="{{StorefrontCategoryProductSection.productPriceFinal('99.90')}}" stepKey="assertProductFinalPriceCategoryPage"/> - <seeElement selector="{{StorefrontCategoryProductSection.productPriceLabel('Regular Price')}}" stepKey="assertRegularPriceLabelCategoryPage"/> - <seeElement selector="{{StorefrontCategoryProductSection.productPriceOld('100')}}" stepKey="assertRegularPriceAmountCategoryPage"/> - </test> </tests> diff --git a/app/code/Magento/Catalog/Test/Mftf/Test/AdminApplyTierPriceToProductTest/AdminApplyTierPriceToProductWithPercentageDiscountTest.xml b/app/code/Magento/Catalog/Test/Mftf/Test/AdminApplyTierPriceToProductTest/AdminApplyTierPriceToProductWithPercentageDiscountTest.xml new file mode 100644 index 0000000000000..fd8c0ba29fdfa --- /dev/null +++ b/app/code/Magento/Catalog/Test/Mftf/Test/AdminApplyTierPriceToProductTest/AdminApplyTierPriceToProductWithPercentageDiscountTest.xml @@ -0,0 +1,62 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + /** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +--> +<tests xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/testSchema.xsd"> + <test name="AdminApplyTierPriceToProductWithPercentageDiscountTest"> + <annotations> + <features value="Catalog"/> + <stories value="MC-5517 - System tries to save 0 in Advanced Pricing which is invalid for Discount field"/> + <title value="You should be able to apply tier price to a product with float percent discount."/> + <description value="You should be able to apply tier price to a product with float percent discount."/> + <severity value="AVERAGE"/> + <testCaseId value="MAGETWO-96881"/> + <group value="product"/> + </annotations> + <before> + <createData entity="_defaultCategory" stepKey="createCategory"/> + <createData entity="SimpleProduct" stepKey="createSimpleProduct"> + <requiredEntity createDataKey="createCategory"/> + <field key="price">100</field> + </createData> + </before> + <after> + <deleteData createDataKey="createSimpleProduct" stepKey="deleteSimpleProduct"/> + <deleteData createDataKey="createCategory" stepKey="deleteCategory"/> + <amOnPage url="{{AdminProductIndexPage.url}}" stepKey="navigateToProductIndex"/> + <waitForPageLoad time="30" stepKey="waitForProductIndexPageLoad"/> + <actionGroup ref="ResetProductGridToDefaultViewActionGroup" stepKey="resetGridToDefaultKeywordSearch"/> + <actionGroup ref="AdminLogoutActionGroup" stepKey="logoutFromAdmin"/> + </after> + <actionGroup ref="AdminLoginActionGroup" stepKey="loginAsAdmin"/> + <actionGroup ref="SearchForProductOnBackendActionGroup" stepKey="searchForSimpleProduct"> + <argument name="product" value="$$createSimpleProduct$$"/> + </actionGroup> + <actionGroup ref="OpenEditProductOnBackendActionGroup" stepKey="openEditProduct1"> + <argument name="product" value="$$createSimpleProduct$$"/> + </actionGroup> + <scrollToTopOfPage stepKey="scrollToTopOfPage"/> + <click selector="{{AdminProductFormSection.advancedPricingLink}}" stepKey="clickOnAdvancedPricingButton"/> + <waitForElement selector="{{AdminProductFormAdvancedPricingSection.customerGroupPriceAddButton}}" stepKey="waitForCustomerGroupPriceAddButton"/> + <click selector="{{AdminProductFormAdvancedPricingSection.customerGroupPriceAddButton}}" stepKey="addCustomerGroupAllGroupsQty1PriceDiscountAndpercent"/> + <fillField selector="{{AdminProductFormAdvancedPricingSection.productTierPriceQtyInput('0')}}" userInput="1" stepKey="fillProductTierPriceQtyInput"/> + <selectOption selector="{{AdminProductFormAdvancedPricingSection.productTierPriceValueTypeSelect('0')}}" userInput="Discount" stepKey="selectProductTierPriceValueType"/> + <fillField selector="{{AdminProductFormAdvancedPricingSection.productTierPricePercentageValuePriceInput('0')}}" userInput="0.1" stepKey="selectProductTierPricePriceInput"/> + <click selector="{{AdminProductFormAdvancedPricingSection.doneButton}}" stepKey="clickDoneButton"/> + <actionGroup ref="SaveProductFormActionGroup" stepKey="saveProduct1"/> + <amOnPage url="{{StorefrontProductPage.url($$createSimpleProduct.sku$$)}}" stepKey="goProductPageOnStorefront"/> + <waitForPageLoad time="30" stepKey="waitForPageLoad1"/> + <seeElement selector="{{StorefrontCategoryProductSection.productPriceFinal('99.90')}}" stepKey="assertProductFinalPriceProductPage"/> + <seeElement selector="{{StorefrontCategoryProductSection.productPriceLabel('Regular Price')}}" stepKey="assertRegularPriceProductPage"/> + <seeElement selector="{{StorefrontCategoryProductSection.productPriceOld('100')}}" stepKey="assertRegularPriceAmountProductPage"/> + <amOnPage url="{{StorefrontCategoryPage.url($$createCategory.name$$)}}" stepKey="navigateToCategoryPage"/> + <waitForPageLoad time="30" stepKey="waitForPageLoad2"/> + <seeElement selector="{{StorefrontCategoryProductSection.productPriceFinal('99.90')}}" stepKey="assertProductFinalPriceCategoryPage"/> + <seeElement selector="{{StorefrontCategoryProductSection.productPriceLabel('Regular Price')}}" stepKey="assertRegularPriceLabelCategoryPage"/> + <seeElement selector="{{StorefrontCategoryProductSection.productPriceOld('100')}}" stepKey="assertRegularPriceAmountCategoryPage"/> + </test> +</tests> diff --git a/app/code/Magento/Catalog/Test/Mftf/Test/AdminAssignProductAttributeToAttributeSetTest.xml b/app/code/Magento/Catalog/Test/Mftf/Test/AdminAssignProductAttributeToAttributeSetTest.xml index 41b358bbf760e..e3fa3dada5b22 100644 --- a/app/code/Magento/Catalog/Test/Mftf/Test/AdminAssignProductAttributeToAttributeSetTest.xml +++ b/app/code/Magento/Catalog/Test/Mftf/Test/AdminAssignProductAttributeToAttributeSetTest.xml @@ -14,7 +14,7 @@ <stories value="Add/Update attribute set"/> <title value="Admin should be able to assign attributes to an attribute set"/> <description value="Admin should be able to assign attributes to an attribute set"/> - <severity value="CRITICAL"/> + <severity value="BLOCKER"/> <testCaseId value="MC-168"/> <group value="Catalog"/> </annotations> @@ -28,11 +28,14 @@ <requiredEntity createDataKey="attribute"/> </createData> - <actionGroup ref="LoginAsAdmin" stepKey="loginAsAdmin"/> + <actionGroup ref="AdminLoginActionGroup" stepKey="loginAsAdmin"/> </before> <after> <deleteData createDataKey="attribute" stepKey="deleteAttribute"/> - <actionGroup ref="logout" stepKey="logout"/> + <actionGroup ref="AdminLogoutActionGroup" stepKey="logout"/> + + <!-- Reindex invalidated indices after product attribute has been created/deleted --> + <magentoCron groups="index" stepKey="reindexInvalidatedIndices"/> </after> <!-- Go to default attribute set edit page --> diff --git a/app/code/Magento/Catalog/Test/Mftf/Test/AdminBackorderAllowedAddProductToCartTest.xml b/app/code/Magento/Catalog/Test/Mftf/Test/AdminBackorderAllowedAddProductToCartTest.xml index 9361637a0a935..2640ab04023a3 100644 --- a/app/code/Magento/Catalog/Test/Mftf/Test/AdminBackorderAllowedAddProductToCartTest.xml +++ b/app/code/Magento/Catalog/Test/Mftf/Test/AdminBackorderAllowedAddProductToCartTest.xml @@ -31,6 +31,7 @@ <!-- Set Magento back to default configuration --> <magentoCLI command="config:set {{CatalogInventoryOptionsShowOutOfStockDisable.path}} {{CatalogInventoryOptionsShowOutOfStockDisable.value}}" stepKey="setConfigShowOutOfStockFalse"/> <magentoCLI command="config:set {{CatalogInventoryItemOptionsBackordersDisable.path}} {{CatalogInventoryItemOptionsBackordersDisable.value}}" stepKey="setConfigAllowBackordersFalse"/> + <magentoCLI command="cache:clean config full_page" stepKey="cleanInvalidatedCache"/> <deleteData createDataKey="createProduct" stepKey="deleteProduct"/> </after> diff --git a/app/code/Magento/Catalog/Test/Mftf/Test/AdminCatalogCategoriesNavigateMenuTest.xml b/app/code/Magento/Catalog/Test/Mftf/Test/AdminCatalogCategoriesNavigateMenuTest.xml index a51df86d0327a..72ffcad4a9dfc 100644 --- a/app/code/Magento/Catalog/Test/Mftf/Test/AdminCatalogCategoriesNavigateMenuTest.xml +++ b/app/code/Magento/Catalog/Test/Mftf/Test/AdminCatalogCategoriesNavigateMenuTest.xml @@ -20,10 +20,10 @@ <group value="mtf_migrated"/> </annotations> <before> - <actionGroup ref="LoginAsAdmin" stepKey="LoginAsAdmin"/> + <actionGroup ref="AdminLoginActionGroup" stepKey="LoginAsAdmin"/> </before> <after> - <actionGroup ref="logout" stepKey="logout"/> + <actionGroup ref="AdminLogoutActionGroup" stepKey="logout"/> </after> <actionGroup ref="AdminNavigateMenuActionGroup" stepKey="navigateToCategoriesPage"> <argument name="menuUiId" value="{{AdminMenuCatalog.dataUiId}}"/> diff --git a/app/code/Magento/Catalog/Test/Mftf/Test/AdminCatalogProductsNavigateMenuTest.xml b/app/code/Magento/Catalog/Test/Mftf/Test/AdminCatalogProductsNavigateMenuTest.xml index 1d9400bf81e4d..acd662401399e 100644 --- a/app/code/Magento/Catalog/Test/Mftf/Test/AdminCatalogProductsNavigateMenuTest.xml +++ b/app/code/Magento/Catalog/Test/Mftf/Test/AdminCatalogProductsNavigateMenuTest.xml @@ -20,10 +20,10 @@ <group value="mtf_migrated"/> </annotations> <before> - <actionGroup ref="LoginAsAdmin" stepKey="LoginAsAdmin"/> + <actionGroup ref="AdminLoginActionGroup" stepKey="LoginAsAdmin"/> </before> <after> - <actionGroup ref="logout" stepKey="logout"/> + <actionGroup ref="AdminLogoutActionGroup" stepKey="logout"/> </after> <actionGroup ref="AdminNavigateMenuActionGroup" stepKey="navigateToCatalogProductsPage"> <argument name="menuUiId" value="{{AdminMenuCatalog.dataUiId}}"/> diff --git a/app/code/Magento/Catalog/Test/Mftf/Test/AdminChangeProductAttributeSet.xml b/app/code/Magento/Catalog/Test/Mftf/Test/AdminChangeProductAttributeSet.xml deleted file mode 100644 index 95620bf75b6d0..0000000000000 --- a/app/code/Magento/Catalog/Test/Mftf/Test/AdminChangeProductAttributeSet.xml +++ /dev/null @@ -1,66 +0,0 @@ -<?xml version="1.0" encoding="UTF-8"?> -<!-- - /** - * Copyright © Magento, Inc. All rights reserved. - * See COPYING.txt for license details. - */ ---> -<tests xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" - xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/testSchema.xsd"> - <test name="AdminChangeProductAttributeSet"> - <annotations> - <features value="Checkout"/> - <stories value="The required product attribute is not displayed when change attribute set"/> - <title value="Attributes from the selected attribute set should be shown"/> - <description value="Attributes from the selected attribute set should be shown"/> - <severity value="CRITICAL"/> - <testCaseId value="MAGETWO-98452"/> - <useCaseId value="MAGETWO-98357"/> - <group value="catalog"/> - </annotations> - <before> - <createData entity="SimpleSubCategory" stepKey="createCategory"/> - <createData entity="_defaultProduct" stepKey="createSimpleProduct"> - <requiredEntity createDataKey="createCategory"/> - </createData> - - <createData entity="productAttributeWithTwoOptions" stepKey="createProductAttribute"/> - <createData entity="productAttributeOption1" stepKey="createProductAttributeOption1"> - <requiredEntity createDataKey="createProductAttribute"/> - </createData> - <createData entity="productAttributeOption2" stepKey="createProductAttributeOption2"> - <requiredEntity createDataKey="createProductAttribute"/> - </createData> - <createData entity="CatalogAttributeSet" stepKey="createAttributeSet"/> - - <actionGroup ref="LoginAsAdmin" stepKey="login"/> - <amOnPage url="{{AdminProductAttributeSetEditPage.url}}/$$createAttributeSet.attribute_set_id$$/" stepKey="onAttributeSetEdit"/> - <actionGroup ref="AssignAttributeToGroupActionGroup" stepKey="assignAttributeToGroup"> - <argument name="group" value="Product Details"/> - <argument name="attribute" value="$$createProductAttribute.attribute_code$$"/> - </actionGroup> - <actionGroup ref="SaveAttributeSetActionGroup" stepKey="SaveAttributeSet"/> - </before> - <after> - <deleteData createDataKey="createSimpleProduct" stepKey="deleteProduct"/> - <deleteData createDataKey="createCategory" stepKey="deleteCategory"/> - <deleteData createDataKey="createProductAttribute" stepKey="deleteProductAttribute"/> - <deleteData createDataKey="createAttributeSet" stepKey="deleteAttributeSet"/> - <actionGroup ref="ClearProductsFilterActionGroup" stepKey="clearProductsFilter"/> - </after> - - <actionGroup ref="SearchForProductOnBackendActionGroup" stepKey="searchForSimpleProduct"> - <argument name="product" value="$$createSimpleProduct$$"/> - </actionGroup> - - <actionGroup ref="OpenEditProductOnBackendActionGroup" stepKey="openEditProduct1"> - <argument name="product" value="$$createSimpleProduct$$"/> - </actionGroup> - - <click selector="{{AdminProductFormSection.attributeSet}}" stepKey="startEditAttrSet"/> - <fillField selector="{{AdminProductFormSection.attributeSetFilter}}" userInput="$$createAttributeSet.attribute_set_name$$" stepKey="searchForAttrSet"/> - <click selector="{{AdminProductFormSection.attributeSetFilterResult}}" stepKey="selectAttrSet"/> - - <waitForText userInput="$$createProductAttribute.default_frontend_label$$" stepKey="seeAttributeInForm"/> - </test> -</tests> diff --git a/app/code/Magento/Catalog/Test/Mftf/Test/AdminChangeProductAttributeSetTest.xml b/app/code/Magento/Catalog/Test/Mftf/Test/AdminChangeProductAttributeSetTest.xml new file mode 100644 index 0000000000000..3b8c2cb736721 --- /dev/null +++ b/app/code/Magento/Catalog/Test/Mftf/Test/AdminChangeProductAttributeSetTest.xml @@ -0,0 +1,69 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + /** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +--> +<tests xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/testSchema.xsd"> + <test name="AdminChangeProductAttributeSetTest"> + <annotations> + <features value="Checkout"/> + <stories value="The required product attribute is not displayed when change attribute set"/> + <title value="Attributes from the selected attribute set should be shown"/> + <description value="Attributes from the selected attribute set should be shown"/> + <severity value="CRITICAL"/> + <testCaseId value="MAGETWO-98452"/> + <useCaseId value="MAGETWO-98357"/> + <group value="catalog"/> + </annotations> + <before> + <createData entity="SimpleSubCategory" stepKey="createCategory"/> + <createData entity="_defaultProduct" stepKey="createSimpleProduct"> + <requiredEntity createDataKey="createCategory"/> + </createData> + + <createData entity="productAttributeWithTwoOptions" stepKey="createProductAttribute"/> + <createData entity="productAttributeOption1" stepKey="createProductAttributeOption1"> + <requiredEntity createDataKey="createProductAttribute"/> + </createData> + <createData entity="productAttributeOption2" stepKey="createProductAttributeOption2"> + <requiredEntity createDataKey="createProductAttribute"/> + </createData> + <createData entity="CatalogAttributeSet" stepKey="createAttributeSet"/> + + <actionGroup ref="AdminLoginActionGroup" stepKey="login"/> + <amOnPage url="{{AdminProductAttributeSetEditPage.url}}/$$createAttributeSet.attribute_set_id$$/" stepKey="onAttributeSetEdit"/> + <actionGroup ref="AssignAttributeToGroupActionGroup" stepKey="assignAttributeToGroup"> + <argument name="group" value="Product Details"/> + <argument name="attribute" value="$$createProductAttribute.attribute_code$$"/> + </actionGroup> + <actionGroup ref="SaveAttributeSetActionGroup" stepKey="SaveAttributeSet"/> + </before> + <after> + <deleteData createDataKey="createSimpleProduct" stepKey="deleteProduct"/> + <deleteData createDataKey="createCategory" stepKey="deleteCategory"/> + <deleteData createDataKey="createProductAttribute" stepKey="deleteProductAttribute"/> + <deleteData createDataKey="createAttributeSet" stepKey="deleteAttributeSet"/> + <actionGroup ref="ClearProductsFilterActionGroup" stepKey="clearProductsFilter"/> + + <!-- Reindex invalidated indices after product attribute has been created/deleted --> + <magentoCron groups="index" stepKey="reindexInvalidatedIndices"/> + </after> + + <actionGroup ref="SearchForProductOnBackendActionGroup" stepKey="searchForSimpleProduct"> + <argument name="product" value="$$createSimpleProduct$$"/> + </actionGroup> + + <actionGroup ref="OpenEditProductOnBackendActionGroup" stepKey="openEditProduct1"> + <argument name="product" value="$$createSimpleProduct$$"/> + </actionGroup> + + <click selector="{{AdminProductFormSection.attributeSet}}" stepKey="startEditAttrSet"/> + <fillField selector="{{AdminProductFormSection.attributeSetFilter}}" userInput="$$createAttributeSet.attribute_set_name$$" stepKey="searchForAttrSet"/> + <click selector="{{AdminProductFormSection.attributeSetFilterResult}}" stepKey="selectAttrSet"/> + + <waitForText userInput="$$createProductAttribute.default_frontend_label$$" stepKey="seeAttributeInForm"/> + </test> +</tests> diff --git a/app/code/Magento/Catalog/Test/Mftf/Test/AdminCheckConfigurableProductPriceWithDisabledChildProductTest.xml b/app/code/Magento/Catalog/Test/Mftf/Test/AdminCheckConfigurableProductPriceWithDisabledChildProductTest.xml index b4381a674827d..39351539d14a6 100644 --- a/app/code/Magento/Catalog/Test/Mftf/Test/AdminCheckConfigurableProductPriceWithDisabledChildProductTest.xml +++ b/app/code/Magento/Catalog/Test/Mftf/Test/AdminCheckConfigurableProductPriceWithDisabledChildProductTest.xml @@ -19,7 +19,7 @@ </annotations> <before> <!-- Login as Admin --> - <actionGroup ref="LoginAsAdmin" stepKey="loginToAdminPanel"/> + <actionGroup ref="AdminLoginActionGroup" stepKey="loginToAdminPanel"/> <!-- Create Default Category --> <createData entity="_defaultCategory" stepKey="createCategory"/> @@ -117,7 +117,10 @@ <deleteData createDataKey="createConfigChildProduct2" stepKey="deleteConfigChildProduct2"/> <deleteData createDataKey="createConfigChildProduct3" stepKey="deleteConfigChildProduct3"/> <deleteData createDataKey="createConfigProductAttribute" stepKey="deleteAttribute"/> - <actionGroup ref="logout" stepKey="logout"/> + <actionGroup ref="AdminLogoutActionGroup" stepKey="logout"/> + + <!-- Reindex invalidated indices after product attribute has been created/deleted --> + <magentoCron groups="index" stepKey="reindexInvalidatedIndices"/> </after> <!-- Open Product in Store Front Page --> diff --git a/app/code/Magento/Catalog/Test/Mftf/Test/AdminCheckCustomAttributeValuesAfterProductSaveTest.xml b/app/code/Magento/Catalog/Test/Mftf/Test/AdminCheckCustomAttributeValuesAfterProductSaveTest.xml new file mode 100644 index 0000000000000..b3f7f0e6eb42a --- /dev/null +++ b/app/code/Magento/Catalog/Test/Mftf/Test/AdminCheckCustomAttributeValuesAfterProductSaveTest.xml @@ -0,0 +1,95 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + /** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +--> +<tests xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/testSchema.xsd"> + <test name="AdminCheckCustomAttributeValuesAfterProductSaveTest"> + <annotations> + <features value="Catalog"/> + <stories value="Product attributes"/> + <title value="Saving custom attribute values using UI"/> + <description value="Checks that saved custom attribute values are reflected on the product's edit page"/> + <severity value="MAJOR"/> + <testCaseId value="MC-29653"/> + <useCaseId value="MC-29023"/> + <group value="catalog"/> + </annotations> + <before> + <!-- Create multi select product attribute --> + <createData entity="productAttributeMultiselectTwoOptions" stepKey="createMultiSelectProductAttribute"/> + <!-- Add options to created product attribute --> + <createData entity="productAttributeOption1" stepKey="addFirstOptionToAttribute"> + <requiredEntity createDataKey="createMultiSelectProductAttribute"/> + </createData> + <createData entity="productAttributeOption2" stepKey="addSecondOptionToAttribute"> + <requiredEntity createDataKey="createMultiSelectProductAttribute"/> + </createData> + <createData entity="productAttributeOption3" stepKey="addThirdOptionToAttribute"> + <requiredEntity createDataKey="createMultiSelectProductAttribute"/> + </createData> + <!-- Create simple product --> + <createData entity="SimpleProduct2" stepKey="createSimpleProduct"/> + <magentoCLI command="indexer:reindex" arguments="catalogsearch_fulltext" stepKey="reindexCatalogSearch"/> + <!-- Login to Admin page --> + <actionGroup ref="AdminLoginActionGroup" stepKey="loginAsAdmin"/> + </before> + <after> + <!-- Delete created entities --> + <deleteData createDataKey="createSimpleProduct" stepKey="deleteSimpleProduct"/> + <deleteData createDataKey="createMultiSelectProductAttribute" stepKey="deleteMultiSelectProductAttribute"/> + <!-- Logout from Admin page --> + <actionGroup ref="AdminLogoutActionGroup" stepKey="logoutFromAdmin"/> + + <!-- Reindex invalidated indices after product attribute has been created/deleted --> + <magentoCron groups="index" stepKey="reindexInvalidatedIndices"/> + </after> + + <!-- Open created product for edit --> + <actionGroup ref="AdminProductPageOpenByIdActionGroup" stepKey="goToProductEditPage"> + <argument name="productId" value="$createSimpleProduct.id$"/> + </actionGroup> + <waitForPageLoad stepKey="waitForProductPageLoad"/> + + <!-- Add created attribute to the product --> + <actionGroup ref="AddProductAttributeInProductModalActionGroup" stepKey="addAttributeToProduct"> + <argument name="attributeCode" value="$createMultiSelectProductAttribute.attribute_code$"/> + </actionGroup> + <waitForPageLoad stepKey="waitForAttributeAdded"/> + + <!-- Expand 'Attributes' tab --> + <actionGroup ref="AdminExpandProductAttributesTabActionGroup" stepKey="expandAttributesTab"/> + <!-- Check created attribute presents in the 'Attributes' tab --> + <seeElement selector="{{AdminProductAttributesSection.attributeDropdownByCode($createMultiSelectProductAttribute.attribute_code$)}}" stepKey="assertAttributeIsPresentInTab"/> + <!-- Select attribute options --> + <selectOption selector="{{AdminProductAttributesSection.attributeDropdownByCode($createMultiSelectProductAttribute.attribute_code$)}}" parameterArray="[$addFirstOptionToAttribute.option[store_labels][0][label]$, $addThirdOptionToAttribute.option[store_labels][0][label]$]" stepKey="selectAttributeOptions"/> + <!-- Save product --> + <actionGroup ref="SaveProductFormActionGroup" stepKey="saveProductForm"/> + + <!-- Check attribute options are selected --> + <actionGroup ref="AdminExpandProductAttributesTabActionGroup" stepKey="expandAttributesTabAfterProductSave"/> + <seeOptionIsSelected selector="{{AdminProductAttributesSection.attributeDropdownByCode($createMultiSelectProductAttribute.attribute_code$)}}" userInput="$addFirstOptionToAttribute.option[store_labels][0][label]$" stepKey="assertFirstOptionIsSelected"/> + <seeOptionIsSelected selector="{{AdminProductAttributesSection.attributeDropdownByCode($createMultiSelectProductAttribute.attribute_code$)}}" userInput="$addThirdOptionToAttribute.option[store_labels][0][label]$" stepKey="assertThirdOptionIsSelected"/> + + <!-- Search for the product on Storefront --> + <actionGroup ref="StorefrontOpenHomePageActionGroup" stepKey="goToHomePage"/> + <actionGroup ref="StorefrontCheckQuickSearchActionGroup" stepKey="searchProductOnStorefront"> + <argument name="phrase" value="$createSimpleProduct.name$"/> + </actionGroup> + + <!-- Assert that attribute values present in layered navigation --> + <actionGroup ref="AssertStorefrontAttributeOptionPresentInLayeredNavigationActionGroup" stepKey="assertFirstAttributeOptionPresence"> + <argument name="attributeLabel" value="$createMultiSelectProductAttribute.attribute[frontend_labels][0][label]$"/> + <argument name="attributeOptionLabel" value="$addFirstOptionToAttribute.option[store_labels][0][label]$"/> + <argument name="attributeOptionPosition" value="1"/> + </actionGroup> + <actionGroup ref="AssertStorefrontAttributeOptionPresentInLayeredNavigationActionGroup" stepKey="assertThirdAttributeOptionPresence"> + <argument name="attributeLabel" value="$createMultiSelectProductAttribute.attribute[frontend_labels][0][label]$"/> + <argument name="attributeOptionLabel" value="$addThirdOptionToAttribute.option[store_labels][0][label]$"/> + <argument name="attributeOptionPosition" value="2"/> + </actionGroup> + </test> +</tests> diff --git a/app/code/Magento/Catalog/Test/Mftf/Test/AdminCheckInactiveAndNotIncludeInMenuCategoryAndSubcategoryIsNotVisibleInNavigationTest.xml b/app/code/Magento/Catalog/Test/Mftf/Test/AdminCheckInactiveAndNotIncludeInMenuCategoryAndSubcategoryIsNotVisibleInNavigationTest.xml index fd22142fcb097..f076f4fa407b4 100644 --- a/app/code/Magento/Catalog/Test/Mftf/Test/AdminCheckInactiveAndNotIncludeInMenuCategoryAndSubcategoryIsNotVisibleInNavigationTest.xml +++ b/app/code/Magento/Catalog/Test/Mftf/Test/AdminCheckInactiveAndNotIncludeInMenuCategoryAndSubcategoryIsNotVisibleInNavigationTest.xml @@ -19,7 +19,7 @@ </annotations> <before> <!--Login as admin --> - <actionGroup ref="LoginAsAdmin" stepKey="loginToAdminPanel"/> + <actionGroup ref="AdminLoginActionGroup" stepKey="loginToAdminPanel"/> <!--Create Parent Inactive and Not Include In Menu Category --> <createData entity="CatInactiveNotInMenu" stepKey="createCategory"/> </before> @@ -27,7 +27,7 @@ <after> <!--Delete created data--> <deleteData createDataKey="createCategory" stepKey="deleteCategory"/> - <actionGroup ref="logout" stepKey="logout"/> + <actionGroup ref="AdminLogoutActionGroup" stepKey="logout"/> </after> <!--Open Category Page--> <amOnPage url="{{AdminCategoryPage.url}}" stepKey="openAdminCategoryIndexPage"/> diff --git a/app/code/Magento/Catalog/Test/Mftf/Test/AdminCheckInactiveCategoryAndSubcategoryIsNotVisibleInNavigationMenuTest.xml b/app/code/Magento/Catalog/Test/Mftf/Test/AdminCheckInactiveCategoryAndSubcategoryIsNotVisibleInNavigationMenuTest.xml index b6c76d6577210..8a0b1977bf901 100644 --- a/app/code/Magento/Catalog/Test/Mftf/Test/AdminCheckInactiveCategoryAndSubcategoryIsNotVisibleInNavigationMenuTest.xml +++ b/app/code/Magento/Catalog/Test/Mftf/Test/AdminCheckInactiveCategoryAndSubcategoryIsNotVisibleInNavigationMenuTest.xml @@ -19,14 +19,14 @@ </annotations> <before> <!--Login as Admin --> - <actionGroup ref="LoginAsAdmin" stepKey="loginToAdminPanel"/> + <actionGroup ref="AdminLoginActionGroup" stepKey="loginToAdminPanel"/> <!--Create Parent Inactive Category --> <createData entity="CatNotActive" stepKey="createCategory"/> </before> <after> <!--Delete created data--> <deleteData createDataKey="createCategory" stepKey="deleteCategory"/> - <actionGroup ref="logout" stepKey="logout"/> + <actionGroup ref="AdminLogoutActionGroup" stepKey="logout"/> </after> <!--Open Category Page--> <amOnPage url="{{AdminCategoryPage.url}}" stepKey="openAdminCategoryIndexPage"/> diff --git a/app/code/Magento/Catalog/Test/Mftf/Test/AdminCheckInactiveIncludeInMenuCategoryAndSubcategoryIsNotVisibleInNavigationTest.xml b/app/code/Magento/Catalog/Test/Mftf/Test/AdminCheckInactiveIncludeInMenuCategoryAndSubcategoryIsNotVisibleInNavigationTest.xml index c9cd9acd9708c..e832f2baf24a2 100644 --- a/app/code/Magento/Catalog/Test/Mftf/Test/AdminCheckInactiveIncludeInMenuCategoryAndSubcategoryIsNotVisibleInNavigationTest.xml +++ b/app/code/Magento/Catalog/Test/Mftf/Test/AdminCheckInactiveIncludeInMenuCategoryAndSubcategoryIsNotVisibleInNavigationTest.xml @@ -19,7 +19,7 @@ </annotations> <before> <!--Login as Admin --> - <actionGroup ref="LoginAsAdmin" stepKey="loginToAdminPanel"/> + <actionGroup ref="AdminLoginActionGroup" stepKey="loginToAdminPanel"/> <!--Create inactive Include In Menu Parent Category --> <createData entity="CatNotIncludeInMenu" stepKey="createCategory"/> </before> @@ -27,7 +27,7 @@ <after> <!--Delete created data--> <deleteData createDataKey="createCategory" stepKey="deleteCategory"/> - <actionGroup ref="logout" stepKey="logout"/> + <actionGroup ref="AdminLogoutActionGroup" stepKey="logout"/> </after> <!--Open Category Page--> <amOnPage url="{{AdminCategoryPage.url}}" stepKey="openAdminCategoryIndexPage"/> diff --git a/app/code/Magento/Catalog/Test/Mftf/Test/AdminCheckMediaRolesForFirstAddedImageViaApiTest.xml b/app/code/Magento/Catalog/Test/Mftf/Test/AdminCheckMediaRolesForFirstAddedImageViaApiTest.xml new file mode 100644 index 0000000000000..b5ccd62401c9b --- /dev/null +++ b/app/code/Magento/Catalog/Test/Mftf/Test/AdminCheckMediaRolesForFirstAddedImageViaApiTest.xml @@ -0,0 +1,41 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + /** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +--> + +<tests xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/testSchema.xsd"> + <test name="AdminCheckMediaRolesForFirstAddedImageViaApiTest"> + <annotations> + <stories value="Add Simple Product with image via API"/> + <title value="Check that added image for created product has selected image roles."/> + <description value="Login as admin, create simple product, add image to created product (via API).Go to + Admin Product Edit page for created product to check that added image has selected image roles."/> + <group value="catalog"/> + </annotations> + <before> + <actionGroup ref="AdminLoginActionGroup" stepKey="loginToAdminPanel"/> + <createData entity="SimpleOutOfStockProduct" stepKey="createSimpleProduct"/> + <createData entity="ApiProductAttributeMediaGalleryEntryWithoutTypesTestImage" stepKey="createSimpleProductImage"> + <requiredEntity createDataKey="createSimpleProduct"/> + </createData> + </before> + <after> + <deleteData createDataKey="createSimpleProduct" stepKey="deleteSimpleProduct"/> + <actionGroup ref="AdminLogoutActionGroup" stepKey="logout"/> + <!-- Reindex invalidated indices after product attribute has been created/deleted --> + <magentoCron groups="index" stepKey="reindexInvalidatedIndices"/> + </after> + + <actionGroup ref="AdminProductPageOpenByIdActionGroup" stepKey="goToSimpleProduct"> + <argument name="productId" value="$$createSimpleProduct.id$$"/> + </actionGroup> + <actionGroup ref="AdminOpenProductImagesSectionActionGroup" stepKey="openProductImagesSection"/> + <actionGroup ref="AssertAdminProductImageRolesSelectedActionGroup" stepKey="checkImageRolesSelected"> + <argument name="imageFileName" value="$createSimpleProductImage.entry[content][name]$"/> + </actionGroup> + </test> +</tests> diff --git a/app/code/Magento/Catalog/Test/Mftf/Test/AdminCheckNewCategoryLevelAddedViaApiTest.xml b/app/code/Magento/Catalog/Test/Mftf/Test/AdminCheckNewCategoryLevelAddedViaApiTest.xml new file mode 100644 index 0000000000000..e707c15bccdf1 --- /dev/null +++ b/app/code/Magento/Catalog/Test/Mftf/Test/AdminCheckNewCategoryLevelAddedViaApiTest.xml @@ -0,0 +1,44 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + /** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +--> + +<tests xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/testSchema.xsd"> + <test name="AdminCheckNewCategoryLevelAddedViaApiTest"> + <annotations> + <stories value="Add parent and child categories via API"/> + <title value="Add parent and child categories via API"/> + <description value="Login as admin, create parent and child categories via API. + Check category level for child category entity based on parent level. + Check category tree: parent element has child element. "/> + <group value="catalog"/> + </annotations> + <before> + <actionGroup ref="AdminLoginActionGroup" stepKey="loginToAdminPanel"/> + <createData entity="ApiCategoryWithChildren" stepKey="createCategoryWithChildrenBlank"/> + <createData entity="ApiSubCategoryWithLevelZero" stepKey="createSubCategoryWithLevelZero"> + <requiredEntity createDataKey="createCategoryWithChildrenBlank"/> + </createData> + </before> + <after> + <deleteData createDataKey="createCategoryWithChildrenBlank" stepKey="deleteCategoryWithChildrenBlank"/> + <actionGroup ref="AdminLogoutActionGroup" stepKey="logout"/> + </after> + + <actionGroup ref="AssertAdminCategoryLevelByParentCategoryLevelActionGroup" stepKey="assertCategoryLevelByParentCategory"> + <argument name="parentCategoryLevel" value="$createCategoryWithChildrenBlank.level$"/> + <argument name="categoryLevel" value="$createSubCategoryWithLevelZero.level$"/> + </actionGroup> + + <actionGroup ref="AdminOpenCategoryPageActionGroup" stepKey="openCategoryPage"/> + <actionGroup ref="AdminExpandCategoryTreeActionGroup" stepKey="expandCategoryTree"/> + <actionGroup ref="AdminAssertParentChildCategoryTreeElementsActionGroup" stepKey="assertParentChildCategoryTreeElements"> + <argument name="parentCategoryName" value="$createCategoryWithChildrenBlank.name$"/> + <argument name="childCategoryName" value="$createSubCategoryWithLevelZero.name$"/> + </actionGroup> + </test> +</tests> diff --git a/app/code/Magento/Catalog/Test/Mftf/Test/AdminCheckOutOfStockProductIsNotVisibleInCategoryTest.xml b/app/code/Magento/Catalog/Test/Mftf/Test/AdminCheckOutOfStockProductIsNotVisibleInCategoryTest.xml index cd03d868838f3..a94610abf0918 100644 --- a/app/code/Magento/Catalog/Test/Mftf/Test/AdminCheckOutOfStockProductIsNotVisibleInCategoryTest.xml +++ b/app/code/Magento/Catalog/Test/Mftf/Test/AdminCheckOutOfStockProductIsNotVisibleInCategoryTest.xml @@ -19,7 +19,7 @@ </annotations> <before> <!-- Login as admin --> - <actionGroup ref="LoginAsAdmin" stepKey="login"/> + <actionGroup ref="AdminLoginActionGroup" stepKey="login"/> <!--Create Category--> <createData entity="SimpleSubCategory" stepKey="createCategory"/> <!--Create Simple Product--> @@ -31,7 +31,7 @@ <!-- Delete created entity --> <deleteData createDataKey="createCategory" stepKey="deleteCategory"/> <deleteData createDataKey="createSimpleProduct" stepKey="deleteSimpleProduct"/> - <actionGroup ref="logout" stepKey="logout"/> + <actionGroup ref="AdminLogoutActionGroup" stepKey="logout"/> </after> <!--Open Product Index Page and filter the product--> <amOnPage url="{{AdminProductIndexPage.url}}" stepKey="navigateToProductIndex"/> diff --git a/app/code/Magento/Catalog/Test/Mftf/Test/AdminCheckOutOfStockProductIsVisibleInCategoryTest.xml b/app/code/Magento/Catalog/Test/Mftf/Test/AdminCheckOutOfStockProductIsVisibleInCategoryTest.xml index bfea4fa7557f5..e64707a895fd4 100644 --- a/app/code/Magento/Catalog/Test/Mftf/Test/AdminCheckOutOfStockProductIsVisibleInCategoryTest.xml +++ b/app/code/Magento/Catalog/Test/Mftf/Test/AdminCheckOutOfStockProductIsVisibleInCategoryTest.xml @@ -21,7 +21,7 @@ <!--Set Display out of stock product--> <magentoCLI stepKey="setDisplayOutOfStockProduct" command="config:set cataloginventory/options/show_out_of_stock 1" /> <!-- Login as admin --> - <actionGroup ref="LoginAsAdmin" stepKey="login"/> + <actionGroup ref="AdminLoginActionGroup" stepKey="login"/> <!--Create Category--> <createData entity="SimpleSubCategory" stepKey="createCategory"/> <!--Create Simple Product--> @@ -33,7 +33,7 @@ <!-- Delete created entity --> <deleteData createDataKey="createCategory" stepKey="deleteCategory"/> <deleteData createDataKey="createSimpleProduct" stepKey="deleteSimpleProduct"/> - <actionGroup ref="logout" stepKey="logout"/> + <actionGroup ref="AdminLogoutActionGroup" stepKey="logout"/> <magentoCLI stepKey="setDisplayOutOfStockProduct" command="config:set cataloginventory/options/show_out_of_stock 0" /> </after> <!--Open Product Index Page and filter the product--> diff --git a/app/code/Magento/Catalog/Test/Mftf/Test/AdminCheckPaginationInStorefrontTest.xml b/app/code/Magento/Catalog/Test/Mftf/Test/AdminCheckPaginationInStorefrontTest.xml index 1b72458747067..278a1fdc7ad9c 100644 --- a/app/code/Magento/Catalog/Test/Mftf/Test/AdminCheckPaginationInStorefrontTest.xml +++ b/app/code/Magento/Catalog/Test/Mftf/Test/AdminCheckPaginationInStorefrontTest.xml @@ -51,7 +51,7 @@ <createData entity="PaginationProduct" stepKey="simpleProduct28"/> <createData entity="PaginationProduct" stepKey="simpleProduct29"/> <createData entity="PaginationProduct" stepKey="simpleProduct30"/> - <actionGroup ref="LoginAsAdmin" stepKey="loginToAdminPanel"/> + <actionGroup ref="AdminLoginActionGroup" stepKey="loginToAdminPanel"/> </before> <after> <magentoCLI stepKey="setFlatCatalogCategory" command="config:set catalog/frontend/flat_catalog_category 0" /> @@ -87,7 +87,7 @@ <deleteData createDataKey="simpleProduct28" stepKey="deleteSimpleProduct28"/> <deleteData createDataKey="simpleProduct29" stepKey="deleteSimpleProduct29"/> <deleteData createDataKey="simpleProduct30" stepKey="deleteSimpleProduct30"/> - <actionGroup ref="logout" stepKey="logout"/> + <actionGroup ref="AdminLogoutActionGroup" stepKey="logout"/> </after> <!--Verify default number of products displayed in the grid view--> <comment userInput="Verify default number of products displayed in the grid view" stepKey="commentVerifyDefaultValues"/> @@ -115,7 +115,7 @@ <click selector="{{CatalogProductsSection.resetFilter}}" stepKey="clickOnResetFilter"/> <waitForPageLoad stepKey="waitForPageToLoad3"/> <selectOption selector="{{AdminProductGridFilterSection.productPerPage}}" userInput="30" stepKey="selectPagePerView"/> - <wait stepKey="waitFroPageToLoad1" time="30"/> + <waitForPageLoad stepKey="waitForPageToLoadProductPerPage" time="30"/> <fillField selector="{{AdminCategoryContentSection.productTableColumnName}}" userInput="pagi" stepKey="selectProduct1"/> <click selector="{{AdminCategoryContentSection.productSearch}}" stepKey="clickSearchButton"/> <waitForPageLoad stepKey="waitFroPageToLoad2"/> diff --git a/app/code/Magento/Catalog/Test/Mftf/Test/AdminCheckProductListPriceAttributesTest.xml b/app/code/Magento/Catalog/Test/Mftf/Test/AdminCheckProductListPriceAttributesTest.xml new file mode 100644 index 0000000000000..27b8b06510ee1 --- /dev/null +++ b/app/code/Magento/Catalog/Test/Mftf/Test/AdminCheckProductListPriceAttributesTest.xml @@ -0,0 +1,64 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + /** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +--> + +<tests xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/testSchema.xsd"> + <test name="AdminCheckProductListPriceAttributesTest"> + <annotations> + <stories value="Check price attributes values on Admin Product List"/> + <title value="Check price attributes values on Admin Product List."/> + <description value="Login as admin, create simple product, add cost, special price. Go to Admin + Product List page filter grid by created product, add mentioned columns to grid, check values."/> + <group value="catalog"/> + </annotations> + <before> + <actionGroup ref="AdminLoginActionGroup" stepKey="loginToAdminPanel"/> + <createData entity="SimpleOutOfStockProductWithSpecialPriceAndCost" stepKey="createSimpleProduct"/> + + <actionGroup ref="AdminOpenProductIndexPageActionGroup" stepKey="adminOpenProductIndexPage"/> + <actionGroup ref="FilterProductGridBySkuActionGroup" stepKey="filterProductGridByCreatedSimpleProductSku"> + <argument name="product" value="$$createSimpleProduct$$"/> + </actionGroup> + </before> + <after> + <actionGroup ref="ClearFiltersAdminProductGridActionGroup" stepKey="clearFiltersAdminProductGrid"/> + <actionGroup ref="ToggleAdminProductGridColumnsDropdownActionGroup" stepKey="openToResetColumnsDropdown"/> + <actionGroup ref="ResetAdminProductGridColumnsActionGroup" stepKey="resetAdminProductGridColumns"/> + + <deleteData createDataKey="createSimpleProduct" stepKey="deleteSimpleProduct"/> + <actionGroup ref="AdminLogoutActionGroup" stepKey="logout"/> + <!-- Reindex invalidated indices after product attribute has been created/deleted --> + <magentoCron groups="index" stepKey="reindexInvalidatedIndices"/> + </after> + + <actionGroup ref="ToggleAdminProductGridColumnsDropdownActionGroup" stepKey="openColumnsDropdown"/> + <actionGroup ref="CheckAdminProductGridColumnOptionActionGroup" stepKey="checkSpecialPriceOption"> + <argument name="optionName" value="Special Price"/> + </actionGroup> + <actionGroup ref="CheckAdminProductGridColumnOptionActionGroup" stepKey="checkCostOption"> + <argument name="optionName" value="Cost"/> + </actionGroup> + <actionGroup ref="ToggleAdminProductGridColumnsDropdownActionGroup" stepKey="closeColumnsDropdown"/> + + <actionGroup ref="AssertAdminProductGridCellActionGroup" stepKey="seePrice"> + <argument name="row" value="1"/> + <argument name="column" value="Price"/> + <argument name="value" value="${{SimpleOutOfStockProduct.price}}"/> + </actionGroup> + <actionGroup ref="AssertAdminProductGridCellActionGroup" stepKey="seeCorrectSpecialPrice"> + <argument name="row" value="1"/> + <argument name="column" value="Special Price"/> + <argument name="value" value="${{ApiProductSpecialPrice.value}}"/> + </actionGroup> + <actionGroup ref="AssertAdminProductGridCellActionGroup" stepKey="seeCorrectCost"> + <argument name="row" value="1"/> + <argument name="column" value="Cost"/> + <argument name="value" value="${{ApiProductCost.value}}"/> + </actionGroup> + </test> +</tests> diff --git a/app/code/Magento/Catalog/Test/Mftf/Test/AdminCheckSubCategoryIsNotVisibleInNavigationMenuTest.xml b/app/code/Magento/Catalog/Test/Mftf/Test/AdminCheckSubCategoryIsNotVisibleInNavigationMenuTest.xml index f5872ac3efca0..ff1e65bca9acc 100644 --- a/app/code/Magento/Catalog/Test/Mftf/Test/AdminCheckSubCategoryIsNotVisibleInNavigationMenuTest.xml +++ b/app/code/Magento/Catalog/Test/Mftf/Test/AdminCheckSubCategoryIsNotVisibleInNavigationMenuTest.xml @@ -19,14 +19,14 @@ </annotations> <before> <!--Login as Admin --> - <actionGroup ref="LoginAsAdmin" stepKey="loginToAdminPanel"/> + <actionGroup ref="AdminLoginActionGroup" stepKey="loginToAdminPanel"/> <!--Create Parent Category --> <createData entity="_defaultCategory" stepKey="createCategory"/> </before> <after> <!--Delete created data--> <deleteData createDataKey="createCategory" stepKey="deleteCategory"/> - <actionGroup ref="logout" stepKey="logout"/> + <actionGroup ref="AdminLogoutActionGroup" stepKey="logout"/> </after> <!--Open Category Page--> <amOnPage url="{{AdminCategoryPage.url}}" stepKey="openAdminCategoryIndexPage"/> diff --git a/app/code/Magento/Catalog/Test/Mftf/Test/AdminCloneProductWithDuplicateUrlTest.xml b/app/code/Magento/Catalog/Test/Mftf/Test/AdminCloneProductWithDuplicateUrlTest.xml index ee9e364758899..d3140dba85752 100644 --- a/app/code/Magento/Catalog/Test/Mftf/Test/AdminCloneProductWithDuplicateUrlTest.xml +++ b/app/code/Magento/Catalog/Test/Mftf/Test/AdminCloneProductWithDuplicateUrlTest.xml @@ -20,7 +20,7 @@ <group value="catalog"/> </annotations> <before> - <actionGroup ref="LoginAsAdmin" stepKey="loginAsAdmin"/> + <actionGroup ref="AdminLoginActionGroup" stepKey="loginAsAdmin"/> <!--Create category and product--> <comment userInput="Create category and product" stepKey="commentCreateCategoryAndProduct"/> <createData entity="SimpleProduct2" stepKey="createSimpleProduct"/> @@ -32,20 +32,30 @@ <argument name="product" value="$$createSimpleProduct$$"/> </actionGroup> <actionGroup ref="ResetProductGridToDefaultViewActionGroup" stepKey="resetFiltersIfExist"/> - <actionGroup ref="logout" stepKey="logoutOfAdmin"/> + <actionGroup ref="AdminLogoutActionGroup" stepKey="logoutOfAdmin"/> </after> - <amOnPage url="{{AdminProductEditPage.url($$createSimpleProduct.id$$)}}" stepKey="goToProductEditPage"/> + <actionGroup ref="AdminProductPageOpenByIdActionGroup" stepKey="goToProductEditPage"> + <argument name="productId" value="$$createSimpleProduct.id$$"/> + </actionGroup> <waitForPageLoad stepKey="waitForSimpleProductPageLoad"/> <!--Save and duplicated the product once--> <comment userInput="Save and duplicated the product once" stepKey="commentSaveAndDuplicateProduct"/> - <actionGroup ref="AdminFormSaveAndDuplicate" stepKey="saveAndDuplicateProductFormFirstTime"/> + <actionGroup ref="AdminFormSaveAndDuplicateActionGroup" stepKey="saveAndDuplicateProductFormFirstTime"/> <conditionalClick selector="{{AdminProductSEOSection.sectionHeader}}" dependentSelector="{{AdminProductSEOSection.urlKeyInput}}" visible="false" stepKey="openSEOSection"/> <grabValueFrom selector="{{AdminProductSEOSection.urlKeyInput}}" stepKey="grabDuplicatedProductUrlKey"/> - <assertContains expected="$$createSimpleProduct.custom_attributes[url_key]$$" actual="$grabDuplicatedProductUrlKey" stepKey="assertDuplicatedProductUrlKey"/> - <assertContains expectedType="string" expected="-1" actual="$grabDuplicatedProductUrlKey" stepKey="assertDuplicatedProductUrlKey1"/> + <assertContains stepKey="assertDuplicatedProductUrlKey"> + <actualResult type="const">$grabDuplicatedProductUrlKey</actualResult> + <expectedResult type="const">$$createSimpleProduct.custom_attributes[url_key]$$</expectedResult> + </assertContains> + <assertContains stepKey="assertDuplicatedProductUrlKey1"> + <actualResult type="const">$grabDuplicatedProductUrlKey</actualResult> + <expectedResult type="string">-1</expectedResult> + </assertContains> <!--Add duplicated product to the simple product--> <comment userInput="Add duplicated product to the simple product" stepKey="commentAddProduct"/> - <amOnPage url="{{AdminProductEditPage.url($$createSimpleProduct.id$$)}}" stepKey="goToSimpleProductPage"/> + <actionGroup ref="AdminProductPageOpenByIdActionGroup" stepKey="goToSimpleProductPage"> + <argument name="productId" value="$$createSimpleProduct.id$$"/> + </actionGroup> <waitForPageLoad stepKey="waitForSimpleProductPageLoad1"/> <actionGroup ref="AddCrossSellProductBySkuActionGroup" stepKey="addCrossSellProduct"> <argument name="sku" value="$$createSimpleProduct.sku$$"/> @@ -63,14 +73,22 @@ <see selector="{{AdminProductFormRelatedUpSellCrossSellSection.selectedProductSku('crosssell')}}" userInput="$$createSimpleProduct.sku$$-1" stepKey="seeCrossSellProduct"/> <!--Save and duplicated the product second time--> <comment userInput="Save and duplicated the product second time" stepKey="commentSaveAndDuplicateProduct1"/> - <amOnPage url="{{AdminProductEditPage.url($$createSimpleProduct.id$$)}}" stepKey="goToProductEditPage1"/> + <actionGroup ref="AdminProductPageOpenByIdActionGroup" stepKey="goToProductEditPage1"> + <argument name="productId" value="$$createSimpleProduct.id$$"/> + </actionGroup> <waitForPageLoad stepKey="waitForSimpleProductPageLoad2"/> - <actionGroup ref="AdminFormSaveAndDuplicate" stepKey="saveAndDuplicateProductFormSecondTime"/> + <actionGroup ref="AdminFormSaveAndDuplicateActionGroup" stepKey="saveAndDuplicateProductFormSecondTime"/> <conditionalClick selector="{{AdminProductSEOSection.sectionHeader}}" dependentSelector="{{AdminProductSEOSection.urlKeyInput}}" visible="false" stepKey="openProductSEOSection"/> <waitForElementVisible selector="{{AdminProductSEOSection.urlKeyInput}}" stepKey="waitForUrlKeyField"/> <grabValueFrom selector="{{AdminProductSEOSection.urlKeyInput}}" stepKey="grabSecondDuplicatedProductUrlKey"/> - <assertContains expected="$$createSimpleProduct.custom_attributes[url_key]$$" actual="$grabSecondDuplicatedProductUrlKey" stepKey="assertSecondDuplicatedProductUrlKey"/> - <assertContains expectedType="string" expected="-2" actual="$grabSecondDuplicatedProductUrlKey" stepKey="assertSecondDuplicatedProductUrlKey1"/> + <assertContains stepKey="assertSecondDuplicatedProductUrlKey"> + <actualResult type="const">$grabSecondDuplicatedProductUrlKey</actualResult> + <expectedResult type="const">$$createSimpleProduct.custom_attributes[url_key]$$</expectedResult> + </assertContains> + <assertContains stepKey="assertSecondDuplicatedProductUrlKey1"> + <actualResult type="const">$grabSecondDuplicatedProductUrlKey</actualResult> + <expectedResult type="string">-2</expectedResult> + </assertContains> <conditionalClick selector="{{AdminProductFormRelatedUpSellCrossSellSection.sectionHeader}}" dependentSelector="{{AdminProductFormRelatedUpSellCrossSellSection.AddRelatedProductsButton}}" visible="false" stepKey="openProductRUSSection1"/> <waitForElementVisible selector="{{AdminProductFormRelatedUpSellCrossSellSection.selectedProductSku('related')}}" stepKey="waitForSelectedProductSku"/> <see selector="{{AdminProductFormRelatedUpSellCrossSellSection.selectedProductSku('related')}}" userInput="$$createSimpleProduct.sku$$-1" stepKey="seeRelatedProductForDuplicated"/> diff --git a/app/code/Magento/Catalog/Test/Mftf/Test/AdminConfigureProductImagePlaceholderTest.xml b/app/code/Magento/Catalog/Test/Mftf/Test/AdminConfigureProductImagePlaceholderTest.xml index b5ab36729c7fe..85378ecfb76a9 100644 --- a/app/code/Magento/Catalog/Test/Mftf/Test/AdminConfigureProductImagePlaceholderTest.xml +++ b/app/code/Magento/Catalog/Test/Mftf/Test/AdminConfigureProductImagePlaceholderTest.xml @@ -63,7 +63,7 @@ <deleteData createDataKey="productWithImages" stepKey="deleteProductWithImages"/> </after> - <actionGroup ref="LoginAsAdmin" stepKey="loginToAdminArea"/> + <actionGroup ref="AdminLoginActionGroup" stepKey="loginToAdminArea"/> <!--Admin area: configure Product Image Placeholders--> <comment userInput="Configure product image placeholders in store config" stepKey="configurePlaceholderComment"/> @@ -114,7 +114,7 @@ <seeElement selector="{{StorefrontProductMediaSection.imageFile(placeholderBaseImage.name)}}" stepKey="seeBasePlaceholderImage"/> <click selector="{{StorefrontProductPageSection.addToCartBtn}}" stepKey="addProductToCart1"/> <waitForElementVisible selector="{{StorefrontProductPageSection.successMsg}}" stepKey="waitForProductAdded1"/> - <click selector="{{StorefrontMinicartSection.showCart}}" stepKey="openMiniCart1"/> + <actionGroup ref="StorefrontClickOnMiniCartActionGroup" stepKey="openMiniCart1"/> <grabAttributeFrom selector="{{StorefrontMinicartSection.productImageByName($$productNoImages.name$$)}}" userInput="src" stepKey="getThumbnailPlaceholderImageSrc"/> <assertContains stepKey="checkThumbnailPlaceholderImage"> <actualResult type="variable">$getThumbnailPlaceholderImageSrc</actualResult> @@ -132,7 +132,7 @@ <dontSeeElement selector="{{StorefrontProductMediaSection.imageFile(placeholderBaseImage.name)}}" stepKey="dontSeeBasePlaceholderImage"/> <click selector="{{StorefrontProductPageSection.addToCartBtn}}" stepKey="addProductToCart2"/> <waitForElementVisible selector="{{StorefrontProductPageSection.successMsg}}" stepKey="waitForProductAdded2"/> - <click selector="{{StorefrontMinicartSection.showCart}}" stepKey="openMiniCart2"/> + <actionGroup ref="StorefrontClickOnMiniCartActionGroup" stepKey="openMiniCart2"/> <grabAttributeFrom selector="{{StorefrontMinicartSection.productImageByName($$productWithImages.name$$)}}" userInput="src" stepKey="getThumbnailImageSrc"/> <assertNotContains stepKey="checkThumbnailImage"> <actualResult type="variable">$getThumbnailImageSrc</actualResult> diff --git a/app/code/Magento/Catalog/Test/Mftf/Test/AdminCreateAndEditSimpleProductSettingsTest.xml b/app/code/Magento/Catalog/Test/Mftf/Test/AdminCreateAndEditSimpleProductSettingsTest.xml index 80c20a7e0b5d9..0525e7543accb 100644 --- a/app/code/Magento/Catalog/Test/Mftf/Test/AdminCreateAndEditSimpleProductSettingsTest.xml +++ b/app/code/Magento/Catalog/Test/Mftf/Test/AdminCreateAndEditSimpleProductSettingsTest.xml @@ -14,13 +14,13 @@ <stories value="Create/Edit simple product in Admin"/> <title value="Admin should be able to set/edit other product information when creating/editing a simple product"/> <description value="Admin should be able to set/edit product information when creating/editing a simple product"/> - <severity value="CRITICAL"/> + <severity value="BLOCKER"/> <testCaseId value="MC-3241"/> <group value="Catalog"/> </annotations> <before> <!-- Login as admin --> - <actionGroup ref="LoginAsAdmin" stepKey="loginAsAdmin"/> + <actionGroup ref="AdminLoginActionGroup" stepKey="loginAsAdmin"/> <!-- Create related products --> <createData entity="SimpleProduct2" stepKey="createFirstRelatedProduct"/> @@ -34,7 +34,7 @@ <deleteData createDataKey="createThirdRelatedProduct" stepKey="deleteThirdRelatedProduct"/> <!-- Log out --> - <actionGroup ref="logout" stepKey="logout"/> + <actionGroup ref="AdminLogoutActionGroup" stepKey="logout"/> </after> <!-- Create new simple product --> diff --git a/app/code/Magento/Catalog/Test/Mftf/Test/AdminCreateAndEditVirtualProductSettingsTest.xml b/app/code/Magento/Catalog/Test/Mftf/Test/AdminCreateAndEditVirtualProductSettingsTest.xml index 34451aba78f21..ce3dd8fa0873c 100644 --- a/app/code/Magento/Catalog/Test/Mftf/Test/AdminCreateAndEditVirtualProductSettingsTest.xml +++ b/app/code/Magento/Catalog/Test/Mftf/Test/AdminCreateAndEditVirtualProductSettingsTest.xml @@ -29,7 +29,7 @@ <createData entity="secondCustomWebsite" stepKey="createWebsite"/> <!-- Login as admin --> - <actionGroup ref="LoginAsAdmin" stepKey="loginAsAdmin"/> + <actionGroup ref="AdminLoginActionGroup" stepKey="loginAsAdmin"/> </before> <after> <!-- Delete created virtual product --> @@ -48,10 +48,10 @@ <deleteData createDataKey="createThirdRelatedProduct" stepKey="deleteThirdRelatedProduct"/> <!-- Log out --> - <actionGroup ref="logout" stepKey="adminLogout"/> + <actionGroup ref="AdminLogoutActionGroup" stepKey="adminLogout"/> </after> - <actionGroup ref="DisabledWYSIWYGActionGroup" stepKey="disableWYSIWYG"/> + <comment userInput="remove me" stepKey="disableWYSIWYG"/> <!-- Create new virtual product --> <actionGroup ref="GoToSpecifiedCreateProductPageActionGroup" stepKey="createVirtualProduct"> @@ -185,6 +185,6 @@ <actionGroup ref="StorefrontOpenCartFromMinicartActionGroup" stepKey="openCart"/> <dontSeeElement selector="{{StorefrontProductCartGiftOptionSection.giftOptions}}" stepKey="dontSeeGiftOptionBtn"/> - <actionGroup ref="EnabledWYSIWYGActionGroup" stepKey="enableWYSIWYG"/> + <comment userInput="remove me" stepKey="enableWYSIWYG"/> </test> </tests> diff --git a/app/code/Magento/Catalog/Test/Mftf/Test/AdminCreateAndSwitchProductType.xml b/app/code/Magento/Catalog/Test/Mftf/Test/AdminCreateAndSwitchProductType.xml deleted file mode 100644 index 12082e1daa6c3..0000000000000 --- a/app/code/Magento/Catalog/Test/Mftf/Test/AdminCreateAndSwitchProductType.xml +++ /dev/null @@ -1,83 +0,0 @@ -<?xml version="1.0" encoding="UTF-8"?> -<!-- - /** - * Copyright © Magento, Inc. All rights reserved. - * See COPYING.txt for license details. - */ ---> - -<tests xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" - xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/testSchema.xsd"> - <test name="AdminCreateSimpleProductSwitchToVirtualTest"> - <annotations> - <features value="Catalog"/> - <stories value="Product Type Switching"/> - <title value="Admin should be able to switch a new product from simple to virtual"/> - <description value="After selecting a simple product when adding Admin should be switch to virtual implicitly"/> - <severity value="CRITICAL"/> - <testCaseId value="MC-10925"/> - <group value="catalog"/> - <group value="mtf_migrated"/> - </annotations> - <before> - <createData entity="_defaultCategory" stepKey="createPreReqCategory"/> - </before> - <after> - <actionGroup ref="GoToProductCatalogPageActionGroup" stepKey="goToProductCatalogPage"/> - <actionGroup ref="DeleteProductUsingProductGridActionGroup" stepKey="deleteSimpleProduct"> - <argument name="product" value="_defaultProduct"/> - </actionGroup> - <actionGroup ref="ResetProductGridToDefaultViewActionGroup" stepKey="resetSearch"/> - <amOnPage url="admin/admin/auth/logout/" stepKey="amOnLogoutPage"/> - <deleteData createDataKey="createPreReqCategory" stepKey="deletePreReqCategory"/> - </after> - - <actionGroup ref="LoginAsAdmin" stepKey="loginAsAdmin1"/> - <!-- Open Dropdown and select simple product option --> - <comment stepKey="beforeOpenProductFillForm" userInput="Selecting Product from the Add Product Dropdown"/> - <actionGroup ref="GoToSpecifiedCreateProductPageActionGroup" stepKey="openProductFillForm"> - <argument name="productType" value="simple"/> - </actionGroup> - - <!-- Fill form for Virtual Product Type --> - <comment stepKey="beforeFillProductForm" userInput="Filling Product Form"/> - <actionGroup ref="FillMainProductFormNoWeightActionGroup" stepKey="fillProductForm"> - <argument name="product" value="_defaultProduct"/> - </actionGroup> - <actionGroup ref="SetProductUrlKeyActionGroup" stepKey="setProductUrl"> - <argument name="product" value="_defaultProduct"/> - </actionGroup> - <actionGroup ref="SaveProductFormActionGroup" stepKey="saveProductForm"/> - <!-- Check that product was added with implicit type change --> - <comment stepKey="beforeVerify" userInput="Verify Product Type Assigned Correctly"/> - <actionGroup ref="GoToProductCatalogPageActionGroup" stepKey="goToProductCatalogPage"/> - <actionGroup ref="ResetProductGridToDefaultViewActionGroup" stepKey="resetSearch"/> - <actionGroup ref="FilterProductGridByNameActionGroup" stepKey="searchForProduct"> - <argument name="product" value="_defaultProduct"/> - </actionGroup> - <see selector="{{AdminProductGridSection.productGridCell('1', 'Type')}}" userInput="Virtual Product" stepKey="seeProductTypeInGrid"/> - <actionGroup ref="AssertProductInStorefrontProductPageActionGroup" stepKey="AssertProductInStorefrontProductPage"> - <argument name="product" value="_defaultProduct"/> - </actionGroup> - </test> - <test name="AdminCreateVirtualProductSwitchToSimpleTest" extends="AdminCreateSimpleProductSwitchToVirtualTest"> - <annotations> - <features value="Catalog"/> - <stories value="Product Type Switching"/> - <title value="Admin should be able to switch a new product from virtual to simple"/> - <description value="After selecting a virtual product when adding Admin should be switch to simple implicitly"/> - <severity value="CRITICAL"/> - <testCaseId value="MC-10928"/> - <group value="catalog"/> - <group value="mtf_migrated"/> - </annotations> - <actionGroup ref="GoToSpecifiedCreateProductPageActionGroup" stepKey="openProductFillForm"> - <argument name="productType" value="virtual"/> - </actionGroup> - <!-- Fill form for Virtual Product Type --> - <actionGroup ref="FillMainProductFormActionGroup" stepKey="fillProductForm"> - <argument name="product" value="_defaultProduct"/> - </actionGroup> - <see selector="{{AdminProductGridSection.productGridCell('1', 'Type')}}" userInput="Simple Product" stepKey="seeProductTypeInGrid"/> - </test> -</tests> diff --git a/app/code/Magento/Catalog/Test/Mftf/Test/AdminCreateAndSwitchProductType/AdminCreateSimpleProductSwitchToVirtualTest.xml b/app/code/Magento/Catalog/Test/Mftf/Test/AdminCreateAndSwitchProductType/AdminCreateSimpleProductSwitchToVirtualTest.xml new file mode 100644 index 0000000000000..65e67020e4532 --- /dev/null +++ b/app/code/Magento/Catalog/Test/Mftf/Test/AdminCreateAndSwitchProductType/AdminCreateSimpleProductSwitchToVirtualTest.xml @@ -0,0 +1,62 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + /** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +--> +<tests xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/testSchema.xsd"> + <test name="AdminCreateSimpleProductSwitchToVirtualTest"> + <annotations> + <features value="Catalog"/> + <stories value="Product Type Switching"/> + <title value="Admin should be able to switch a new product from simple to virtual"/> + <description value="After selecting a simple product when adding Admin should be switch to virtual implicitly"/> + <severity value="CRITICAL"/> + <testCaseId value="MC-10925"/> + <group value="catalog"/> + <group value="mtf_migrated"/> + </annotations> + <before> + <createData entity="_defaultCategory" stepKey="createPreReqCategory"/> + </before> + <after> + <actionGroup ref="GoToProductCatalogPageActionGroup" stepKey="goToProductCatalogPage"/> + <actionGroup ref="DeleteProductUsingProductGridActionGroup" stepKey="deleteSimpleProduct"> + <argument name="product" value="_defaultProduct"/> + </actionGroup> + <actionGroup ref="ResetProductGridToDefaultViewActionGroup" stepKey="resetSearch"/> + <amOnPage url="admin/admin/auth/logout/" stepKey="amOnLogoutPage"/> + <deleteData createDataKey="createPreReqCategory" stepKey="deletePreReqCategory"/> + </after> + + <actionGroup ref="AdminLoginActionGroup" stepKey="loginAsAdmin1"/> + <!-- Open Dropdown and select simple product option --> + <comment stepKey="beforeOpenProductFillForm" userInput="Selecting Product from the Add Product Dropdown"/> + <actionGroup ref="GoToSpecifiedCreateProductPageActionGroup" stepKey="openProductFillForm"> + <argument name="productType" value="simple"/> + </actionGroup> + + <!-- Fill form for Virtual Product Type --> + <comment stepKey="beforeFillProductForm" userInput="Filling Product Form"/> + <actionGroup ref="FillMainProductFormNoWeightActionGroup" stepKey="fillProductForm"> + <argument name="product" value="_defaultProduct"/> + </actionGroup> + <actionGroup ref="SetProductUrlKeyActionGroup" stepKey="setProductUrl"> + <argument name="product" value="_defaultProduct"/> + </actionGroup> + <actionGroup ref="SaveProductFormActionGroup" stepKey="saveProductForm"/> + <!-- Check that product was added with implicit type change --> + <comment stepKey="beforeVerify" userInput="Verify Product Type Assigned Correctly"/> + <actionGroup ref="GoToProductCatalogPageActionGroup" stepKey="goToProductCatalogPage"/> + <actionGroup ref="ResetProductGridToDefaultViewActionGroup" stepKey="resetSearch"/> + <actionGroup ref="FilterProductGridByNameActionGroup" stepKey="searchForProduct"> + <argument name="product" value="_defaultProduct"/> + </actionGroup> + <see selector="{{AdminProductGridSection.productGridCell('1', 'Type')}}" userInput="Virtual Product" stepKey="seeProductTypeInGrid"/> + <actionGroup ref="AssertProductInStorefrontProductPageActionGroup" stepKey="AssertProductInStorefrontProductPage"> + <argument name="product" value="_defaultProduct"/> + </actionGroup> + </test> +</tests> diff --git a/app/code/Magento/Catalog/Test/Mftf/Test/AdminCreateAndSwitchProductType/AdminCreateVirtualProductSwitchToSimpleTest.xml b/app/code/Magento/Catalog/Test/Mftf/Test/AdminCreateAndSwitchProductType/AdminCreateVirtualProductSwitchToSimpleTest.xml new file mode 100644 index 0000000000000..f79072582035b --- /dev/null +++ b/app/code/Magento/Catalog/Test/Mftf/Test/AdminCreateAndSwitchProductType/AdminCreateVirtualProductSwitchToSimpleTest.xml @@ -0,0 +1,30 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + /** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +--> +<tests xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/testSchema.xsd"> + <test name="AdminCreateVirtualProductSwitchToSimpleTest" extends="AdminCreateSimpleProductSwitchToVirtualTest"> + <annotations> + <features value="Catalog"/> + <stories value="Product Type Switching"/> + <title value="Admin should be able to switch a new product from virtual to simple"/> + <description value="After selecting a virtual product when adding Admin should be switch to simple implicitly"/> + <severity value="CRITICAL"/> + <testCaseId value="MC-10928"/> + <group value="catalog"/> + <group value="mtf_migrated"/> + </annotations> + <actionGroup ref="GoToSpecifiedCreateProductPageActionGroup" stepKey="openProductFillForm"> + <argument name="productType" value="virtual"/> + </actionGroup> + <!-- Fill form for Virtual Product Type --> + <actionGroup ref="FillMainProductFormActionGroup" stepKey="fillProductForm"> + <argument name="product" value="_defaultProduct"/> + </actionGroup> + <see selector="{{AdminProductGridSection.productGridCell('1', 'Type')}}" userInput="Simple Product" stepKey="seeProductTypeInGrid"/> + </test> +</tests> diff --git a/app/code/Magento/Catalog/Test/Mftf/Test/AdminCreateAttributeSetEntityTest.xml b/app/code/Magento/Catalog/Test/Mftf/Test/AdminCreateAttributeSetEntityTest.xml index 51518dffaf87e..04d032511ded0 100644 --- a/app/code/Magento/Catalog/Test/Mftf/Test/AdminCreateAttributeSetEntityTest.xml +++ b/app/code/Magento/Catalog/Test/Mftf/Test/AdminCreateAttributeSetEntityTest.xml @@ -15,17 +15,20 @@ <title value="Create attribute set with new product attribute"/> <description value="Admin should be able to create attribute set with new product attribute"/> <testCaseId value="MC-10884"/> - <severity value="CRITICAL"/> + <severity value="BLOCKER"/> <group value="mtf_migrated"/> </annotations> <before> - <actionGroup ref="LoginAsAdmin" stepKey="loginAsAdmin"/> + <actionGroup ref="AdminLoginActionGroup" stepKey="loginAsAdmin"/> <createData entity="productAttributeWysiwyg" stepKey="createProductAttribute"/> <createData entity="CatalogAttributeSet" stepKey="createAttributeSet"/> </before> <after> <deleteData createDataKey="createProductAttribute" stepKey="deleteProductAttribute"/> <deleteData createDataKey="createAttributeSet" stepKey="deleteAttributeSet"/> + + <!-- Reindex invalidated indices after product attribute has been created/deleted --> + <magentoCron groups="index" stepKey="reindexInvalidatedIndices"/> </after> <amOnPage url="{{AdminProductAttributeSetGridPage.url}}" stepKey="goToAttributeSets"/> diff --git a/app/code/Magento/Catalog/Test/Mftf/Test/AdminCreateCategoryFromProductPageTest.xml b/app/code/Magento/Catalog/Test/Mftf/Test/AdminCreateCategoryFromProductPageTest.xml index ef21b53c7613b..b94c12d1d7a39 100644 --- a/app/code/Magento/Catalog/Test/Mftf/Test/AdminCreateCategoryFromProductPageTest.xml +++ b/app/code/Magento/Catalog/Test/Mftf/Test/AdminCreateCategoryFromProductPageTest.xml @@ -21,7 +21,7 @@ <before> <!-- Login as admin --> <createData entity="SimpleTwo" stepKey="simpleProduct"/> - <actionGroup ref="LoginAsAdmin" stepKey="loginAsAdmin"/> + <actionGroup ref="AdminLoginActionGroup" stepKey="loginAsAdmin"/> </before> <after> diff --git a/app/code/Magento/Catalog/Test/Mftf/Test/AdminCreateCategoryTest.xml b/app/code/Magento/Catalog/Test/Mftf/Test/AdminCreateCategoryTest.xml deleted file mode 100644 index 784b5d3fd1827..0000000000000 --- a/app/code/Magento/Catalog/Test/Mftf/Test/AdminCreateCategoryTest.xml +++ /dev/null @@ -1,103 +0,0 @@ -<?xml version="1.0" encoding="UTF-8"?> -<!-- - /** - * Copyright © Magento, Inc. All rights reserved. - * See COPYING.txt for license details. - */ ---> - -<tests xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" - xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/testSchema.xsd"> - <test name="AdminCreateCategoryTest"> - <annotations> - <features value="Catalog"/> - <stories value="Create a Category via the Admin"/> - <title value="Admin should be able to create a Category"/> - <description value="Admin should be able to create a Category"/> - <severity value="CRITICAL"/> - <testCaseId value="MAGETWO-72102"/> - <group value="category"/> - </annotations> - <after> - <actionGroup ref="logout" stepKey="adminLogout"/> - </after> - - <actionGroup ref="LoginAsAdmin" stepKey="loginAsAdmin1"/> - <amOnPage url="{{AdminCategoryPage.url}}" stepKey="navigateToCategoryPage"/> - <waitForPageLoad time="30" stepKey="waitForPageLoad1"/> - <click selector="{{AdminCategorySidebarActionSection.AddSubcategoryButton}}" stepKey="clickOnAddSubCategory"/> - <fillField selector="{{AdminCategoryBasicFieldSection.CategoryNameInput}}" userInput="{{SimpleSubCategory.name}}" stepKey="enterCategoryName"/> - <click selector="{{AdminCategorySEOSection.SectionHeader}}" stepKey="openSEO"/> - <fillField selector="{{AdminCategorySEOSection.UrlKeyInput}}" userInput="{{SimpleSubCategory.name_lwr}}" stepKey="enterURLKey"/> - <click selector="{{AdminCategoryMainActionsSection.SaveButton}}" stepKey="saveCategory"/> - <seeElement selector="{{AdminCategoryMessagesSection.SuccessMessage}}" stepKey="assertSuccess"/> - - <!-- Literal URL below, need to refactor line + StorefrontCategoryPage when support for variable URL is implemented--> - <amOnPage url="/{{SimpleSubCategory.name_lwr}}.html" stepKey="goToCategoryFrontPage"/> - <seeInTitle userInput="{{SimpleSubCategory.name}}" stepKey="assertTitle"/> - <see selector="{{StorefrontCategoryMainSection.CategoryTitle}}" userInput="{{SimpleSubCategory.name_lwr}}" stepKey="assertInfo1"/> - </test> - <test name="AdminConfigDefaultCategoryLayoutFromConfigurationSettingTest"> - <annotations> - <features value="Catalog"/> - <stories value="Default layout configuration MAGETWO-88793"/> - <title value="Admin should be able to configure the default layout for Category Page from System Configuration"/> - <description value="Admin should be able to configure the default layout for Category Page from System Configuration"/> - <severity value="CRITICAL"/> - <testCaseId value="MAGETWO-89024"/> - <group value="category"/> - </annotations> - <before> - <actionGroup ref="LoginAsAdmin" stepKey="loginAsAdmin"/> - </before> - <after> - <actionGroup ref="RestoreLayoutSetting" stepKey="sampleActionGroup"/> - <actionGroup ref="logout" stepKey="logout"/> - </after> - <amOnPage url="{{WebConfigurationPage.url}}" stepKey="navigateToWebConfigurationPage"/> - <waitForPageLoad stepKey="waitForPageLoad"/> - <conditionalClick stepKey="expandDefaultLayouts" selector="{{WebSection.DefaultLayoutsTab}}" dependentSelector="{{WebSection.CheckIfTabExpand}}" visible="true" /> - <waitForElementVisible selector="{{DefaultLayoutsSection.categoryLayout}}" stepKey="waitForDefaultCategoryLayout" /> - <seeOptionIsSelected selector="{{DefaultLayoutsSection.categoryLayout}}" userInput="No layout updates" stepKey="seeNoLayoutUpdatesSelected" /> - <selectOption selector="{{DefaultLayoutsSection.categoryLayout}}" userInput="2 columns with right bar" stepKey="select2ColumnsLayout"/> - <click selector="{{ContentManagementSection.Save}}" stepKey="clickSaveConfig" /> - <amOnPage url="{{AdminCategoryPage.url}}" stepKey="navigateToNewCatalog"/> - <waitForPageLoad stepKey="wait1"/> - <waitForLoadingMaskToDisappear stepKey="wait2" /> - <click selector="{{AdminCategorySidebarActionSection.AddSubcategoryButton}}" stepKey="clickOnAddSubCategory"/> - <click selector="{{CategoryDesignSection.DesignTab}}" stepKey="clickOnDesignTab"/> - <waitForElementVisible selector="{{CategoryDesignSection.LayoutDropdown}}" stepKey="waitForLayoutDropDown" /> - <seeOptionIsSelected selector="{{CategoryDesignSection.LayoutDropdown}}" userInput="2 columns with right bar" stepKey="see2ColumnsSelected" /> - </test> - <test name="AdminCategoryFormDisplaySettingsUIValidationTest"> - <annotations> - <features value="Catalog"/> - <stories value="Default layout configuration MAGETWO-88793"/> - <title value="Category should not be saved once layered navigation price step field is left empty"/> - <description value="Once the Config setting is unchecked Category should not be saved with layered navigation price field left empty"/> - <severity value="AVERAGE"/> - <testCaseId value="MAGETWO-95797"/> - <group value="category"/> - </annotations> - <before> - <actionGroup ref="LoginAsAdmin" stepKey="loginAsAdmin"/> - </before> - <after> - <actionGroup ref="logout" stepKey="logout"/> - </after> - <amOnPage url="{{AdminCategoryPage.url}}" stepKey="navigateToCategoryPage"/> - <waitForPageLoad time="30" stepKey="waitForPageLoad1"/> - <click selector="{{AdminCategorySidebarActionSection.AddSubcategoryButton}}" stepKey="clickOnAddSubCategory"/> - <fillField selector="{{AdminCategoryBasicFieldSection.CategoryNameInput}}" userInput="{{SimpleSubCategory.name}}" stepKey="enterCategoryName"/> - <click selector="{{CategoryDisplaySettingsSection.DisplaySettingTab}}" stepKey="clickOnDisplaySettingsTab"/> - <waitForElementVisible selector="{{CategoryDisplaySettingsSection.filterPriceRangeUseConfig}}" stepKey="wait"/> - <scrollTo selector="{{CategoryDisplaySettingsSection.layeredNavigationPriceInput}}" stepKey="scrollToLayeredNavigationField"/> - <uncheckOption selector="{{CategoryDisplaySettingsSection.filterPriceRangeUseConfig}}" stepKey="uncheckConfigSetting"/> - <click selector="{{AdminCategoryMainActionsSection.SaveButton}}" stepKey="saveCategory"/> - <see selector="{{AdminCategoryBasicFieldSection.FieldError('uid')}}" userInput="This is a required field." stepKey="seeErrorMessage"/> - <!-- Verify that the Layered navigation price step field has the required indicator --> - <comment userInput="Check if Layered navigation price field has required indicator icon" stepKey="comment" /> - <executeJS function="{{CategoryDisplaySettingsSection.RequiredFieldIndicator('filter_price_range')}}" stepKey="getRequiredFieldIndicator"/> - <assertEquals expected='"*"' expectedType="string" actualType="variable" actual="getRequiredFieldIndicator" message="pass" stepKey="assertRequiredFieldIndicator1"/> - </test> -</tests> diff --git a/app/code/Magento/Catalog/Test/Mftf/Test/AdminCreateCategoryTest/AdminCategoryFormDisplaySettingsUIValidationTest.xml b/app/code/Magento/Catalog/Test/Mftf/Test/AdminCreateCategoryTest/AdminCategoryFormDisplaySettingsUIValidationTest.xml new file mode 100644 index 0000000000000..e0fa4a3c3410c --- /dev/null +++ b/app/code/Magento/Catalog/Test/Mftf/Test/AdminCreateCategoryTest/AdminCategoryFormDisplaySettingsUIValidationTest.xml @@ -0,0 +1,44 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + /** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +--> +<tests xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/testSchema.xsd"> + <test name="AdminCategoryFormDisplaySettingsUIValidationTest"> + <annotations> + <features value="Catalog"/> + <stories value="Default layout configuration MAGETWO-88793"/> + <title value="Category should not be saved once layered navigation price step field is left empty"/> + <description value="Once the Config setting is unchecked Category should not be saved with layered navigation price field left empty"/> + <severity value="BLOCKER"/> + <testCaseId value="MAGETWO-95797"/> + <group value="category"/> + </annotations> + <before> + <actionGroup ref="AdminLoginActionGroup" stepKey="loginAsAdmin"/> + </before> + <after> + <actionGroup ref="AdminLogoutActionGroup" stepKey="logout"/> + </after> + <amOnPage url="{{AdminCategoryPage.url}}" stepKey="navigateToCategoryPage"/> + <waitForPageLoad time="30" stepKey="waitForPageLoad1"/> + <click selector="{{AdminCategorySidebarActionSection.AddSubcategoryButton}}" stepKey="clickOnAddSubCategory"/> + <fillField selector="{{AdminCategoryBasicFieldSection.CategoryNameInput}}" userInput="{{SimpleSubCategory.name}}" stepKey="enterCategoryName"/> + <click selector="{{CategoryDisplaySettingsSection.DisplaySettingTab}}" stepKey="clickOnDisplaySettingsTab"/> + <waitForElementVisible selector="{{CategoryDisplaySettingsSection.filterPriceRangeUseConfig}}" stepKey="wait"/> + <scrollTo selector="{{CategoryDisplaySettingsSection.layeredNavigationPriceInput}}" stepKey="scrollToLayeredNavigationField"/> + <uncheckOption selector="{{CategoryDisplaySettingsSection.filterPriceRangeUseConfig}}" stepKey="uncheckConfigSetting"/> + <click selector="{{AdminCategoryMainActionsSection.SaveButton}}" stepKey="saveCategory"/> + <see selector="{{AdminCategoryBasicFieldSection.FieldError('uid')}}" userInput="This is a required field." stepKey="seeErrorMessage"/> + <!-- Verify that the Layered navigation price step field has the required indicator --> + <comment userInput="Check if Layered navigation price field has required indicator icon" stepKey="comment"/> + <executeJS function="{{CategoryDisplaySettingsSection.RequiredFieldIndicator('filter_price_range')}}" stepKey="getRequiredFieldIndicator"/> + <assertEquals message="pass" stepKey="assertRequiredFieldIndicator1"> + <actualResult type="variable">getRequiredFieldIndicator</actualResult> + <expectedResult type="string">"*"</expectedResult> + </assertEquals> + </test> +</tests> diff --git a/app/code/Magento/Catalog/Test/Mftf/Test/AdminCreateCategoryTest/AdminConfigDefaultCategoryLayoutFromConfigurationSettingTest.xml b/app/code/Magento/Catalog/Test/Mftf/Test/AdminCreateCategoryTest/AdminConfigDefaultCategoryLayoutFromConfigurationSettingTest.xml new file mode 100644 index 0000000000000..e8b13afc7138e --- /dev/null +++ b/app/code/Magento/Catalog/Test/Mftf/Test/AdminCreateCategoryTest/AdminConfigDefaultCategoryLayoutFromConfigurationSettingTest.xml @@ -0,0 +1,42 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + /** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +--> +<tests xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/testSchema.xsd"> + <test name="AdminConfigDefaultCategoryLayoutFromConfigurationSettingTest"> + <annotations> + <features value="Catalog"/> + <stories value="Default layout configuration MAGETWO-88793"/> + <title value="Admin should be able to configure the default layout for Category Page from System Configuration"/> + <description value="Admin should be able to configure the default layout for Category Page from System Configuration"/> + <severity value="BLOCKER"/> + <testCaseId value="MAGETWO-89024"/> + <group value="category"/> + </annotations> + <before> + <actionGroup ref="AdminLoginActionGroup" stepKey="loginAsAdmin"/> + </before> + <after> + <actionGroup ref="RestoreLayoutSetting" stepKey="sampleActionGroup"/> + <actionGroup ref="AdminLogoutActionGroup" stepKey="logout"/> + </after> + <amOnPage url="{{WebConfigurationPage.url}}" stepKey="navigateToWebConfigurationPage"/> + <waitForPageLoad stepKey="waitForPageLoad"/> + <conditionalClick stepKey="expandDefaultLayouts" selector="{{WebSection.DefaultLayoutsTab}}" dependentSelector="{{WebSection.CheckIfTabExpand}}" visible="true"/> + <waitForElementVisible selector="{{DefaultLayoutsSection.categoryLayout}}" stepKey="waitForDefaultCategoryLayout"/> + <seeOptionIsSelected selector="{{DefaultLayoutsSection.categoryLayout}}" userInput="No layout updates" stepKey="seeNoLayoutUpdatesSelected"/> + <selectOption selector="{{DefaultLayoutsSection.categoryLayout}}" userInput="2 columns with right bar" stepKey="select2ColumnsLayout"/> + <click selector="{{ContentManagementSection.Save}}" stepKey="clickSaveConfig"/> + <amOnPage url="{{AdminCategoryPage.url}}" stepKey="navigateToNewCatalog"/> + <waitForPageLoad stepKey="wait1"/> + <waitForLoadingMaskToDisappear stepKey="wait2"/> + <click selector="{{AdminCategorySidebarActionSection.AddSubcategoryButton}}" stepKey="clickOnAddSubCategory"/> + <click selector="{{CategoryDesignSection.DesignTab}}" stepKey="clickOnDesignTab"/> + <waitForElementVisible selector="{{CategoryDesignSection.LayoutDropdown}}" stepKey="waitForLayoutDropDown"/> + <seeOptionIsSelected selector="{{CategoryDesignSection.LayoutDropdown}}" userInput="2 columns with right bar" stepKey="see2ColumnsSelected"/> + </test> +</tests> diff --git a/app/code/Magento/Catalog/Test/Mftf/Test/AdminCreateCategoryTest/AdminCreateCategoryTest.xml b/app/code/Magento/Catalog/Test/Mftf/Test/AdminCreateCategoryTest/AdminCreateCategoryTest.xml new file mode 100644 index 0000000000000..8ad76bdde7306 --- /dev/null +++ b/app/code/Magento/Catalog/Test/Mftf/Test/AdminCreateCategoryTest/AdminCreateCategoryTest.xml @@ -0,0 +1,39 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + /** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +--> +<tests xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/testSchema.xsd"> + <test name="AdminCreateCategoryTest"> + <annotations> + <features value="Catalog"/> + <stories value="Create a Category via the Admin"/> + <title value="Admin should be able to create a Category"/> + <description value="Admin should be able to create a Category"/> + <severity value="BLOCKER"/> + <testCaseId value="MAGETWO-72102"/> + <group value="category"/> + </annotations> + <after> + <actionGroup ref="AdminLogoutActionGroup" stepKey="adminLogout"/> + </after> + + <actionGroup ref="AdminLoginActionGroup" stepKey="loginAsAdmin1"/> + <amOnPage url="{{AdminCategoryPage.url}}" stepKey="navigateToCategoryPage"/> + <waitForPageLoad time="30" stepKey="waitForPageLoad1"/> + <click selector="{{AdminCategorySidebarActionSection.AddSubcategoryButton}}" stepKey="clickOnAddSubCategory"/> + <fillField selector="{{AdminCategoryBasicFieldSection.CategoryNameInput}}" userInput="{{SimpleSubCategory.name}}" stepKey="enterCategoryName"/> + <click selector="{{AdminCategorySEOSection.SectionHeader}}" stepKey="openSEO"/> + <fillField selector="{{AdminCategorySEOSection.UrlKeyInput}}" userInput="{{SimpleSubCategory.name_lwr}}" stepKey="enterURLKey"/> + <click selector="{{AdminCategoryMainActionsSection.SaveButton}}" stepKey="saveCategory"/> + <seeElement selector="{{AdminCategoryMessagesSection.SuccessMessage}}" stepKey="assertSuccess"/> + + <!-- Literal URL below, need to refactor line + StorefrontCategoryPage when support for variable URL is implemented--> + <amOnPage url="/{{SimpleSubCategory.name_lwr}}.html" stepKey="goToCategoryFrontPage"/> + <seeInTitle userInput="{{SimpleSubCategory.name}}" stepKey="assertTitle"/> + <see selector="{{StorefrontCategoryMainSection.CategoryTitle}}" userInput="{{SimpleSubCategory.name_lwr}}" stepKey="assertInfo1"/> + </test> +</tests> diff --git a/app/code/Magento/Catalog/Test/Mftf/Test/AdminCreateCategoryWithAnchorFieldTest.xml b/app/code/Magento/Catalog/Test/Mftf/Test/AdminCreateCategoryWithAnchorFieldTest.xml index 7de1d38f097c5..7deb79c66ff6d 100644 --- a/app/code/Magento/Catalog/Test/Mftf/Test/AdminCreateCategoryWithAnchorFieldTest.xml +++ b/app/code/Magento/Catalog/Test/Mftf/Test/AdminCreateCategoryWithAnchorFieldTest.xml @@ -18,13 +18,13 @@ <group value="mtf_migrated"/> </annotations> <before> - <actionGroup ref="LoginAsAdmin" stepKey="loginToAdminPanel"/> + <actionGroup ref="AdminLoginActionGroup" stepKey="loginToAdminPanel"/> <createData entity="_defaultBlock" stepKey="createDefaultCMSBlock"/> <createData entity="defaultSimpleProduct" stepKey="simpleProduct" /> </before> <after> <actionGroup ref="DeleteCategoryActionGroup" stepKey="deleteCategory"/> - <actionGroup ref="logout" stepKey="logout"/> + <actionGroup ref="AdminLogoutActionGroup" stepKey="logout"/> <deleteData createDataKey="createDefaultCMSBlock" stepKey="deleteDefaultCMSBlock"/> <deleteData stepKey="deleteSimpleProduct" createDataKey="simpleProduct"/> </after> @@ -64,6 +64,14 @@ <waitForPageLoad stepKey="waitForPageTitleToBeSaved"/> <!--Verify the Category Title--> <see selector="{{AdminCategoryContentSection.categoryPageTitle}}" userInput="{{_defaultCategory.name}}" stepKey="seePageTitle" /> + <grabFromCurrentUrl stepKey="categoryId" regex="#\/([0-9]*)?\/$#"/> + <!-- Assert Redirect path, Target Path and Redirect type in grid --> + <actionGroup ref="AdminSearchByRequestPathActionGroup" stepKey="searchByRequestPath"> + <argument name="redirectPath" value="{{_defaultCategory.name}}.html" /> + <argument name="redirectType" value="No" /> + <argument name="targetPath" value="catalog/category/view/id/{$categoryId}"/> + </actionGroup> + <!--Clear cache and reindex--> <magentoCLI command="indexer:reindex" stepKey="reindex"/> <magentoCLI command="cache:flush" stepKey="flushCache"/> diff --git a/app/code/Magento/Catalog/Test/Mftf/Test/AdminCreateCategoryWithCustomRootCategoryTest.xml b/app/code/Magento/Catalog/Test/Mftf/Test/AdminCreateCategoryWithCustomRootCategoryTest.xml index a68a585bbf31d..71167882d135d 100644 --- a/app/code/Magento/Catalog/Test/Mftf/Test/AdminCreateCategoryWithCustomRootCategoryTest.xml +++ b/app/code/Magento/Catalog/Test/Mftf/Test/AdminCreateCategoryWithCustomRootCategoryTest.xml @@ -18,7 +18,7 @@ <group value="Catalog"/> </annotations> <before> - <actionGroup ref="LoginAsAdmin" stepKey="loginToAdminPanel"/> + <actionGroup ref="AdminLoginActionGroup" stepKey="loginToAdminPanel"/> </before> <after> <amOnPage url="{{AdminSystemStorePage.url}}" stepKey="navigateToStoresIndex"/> @@ -29,7 +29,7 @@ <actionGroup ref="DeleteCategoryActionGroup" stepKey="deleteCreatedNewRootCategory"> <argument name="categoryEntity" value="NewRootCategory"/> </actionGroup> - <actionGroup ref="logout" stepKey="logout"/> + <actionGroup ref="AdminLogoutActionGroup" stepKey="logout"/> </after> <amOnPage url="{{AdminCategoryPage.url}}" stepKey="openAdminCategoryIndexPage"/> <waitForPageLoad stepKey="waitForCategoryIndexPageToBeLoaded"/> diff --git a/app/code/Magento/Catalog/Test/Mftf/Test/AdminCreateCategoryWithFiveNestingTest.xml b/app/code/Magento/Catalog/Test/Mftf/Test/AdminCreateCategoryWithFiveNestingTest.xml index 530bafaef24c2..c1df630685949 100644 --- a/app/code/Magento/Catalog/Test/Mftf/Test/AdminCreateCategoryWithFiveNestingTest.xml +++ b/app/code/Magento/Catalog/Test/Mftf/Test/AdminCreateCategoryWithFiveNestingTest.xml @@ -19,7 +19,7 @@ <group value="mtf_migrated"/> </annotations> <before> - <actionGroup ref="LoginAsAdmin" stepKey="loginToAdminPanel"/> + <actionGroup ref="AdminLoginActionGroup" stepKey="loginToAdminPanel"/> </before> <after> <amOnPage url="{{AdminCategoryPage.url}}" stepKey="goToCategoryPage"/> @@ -33,7 +33,7 @@ <see selector="You deleted the category." stepKey="seeDeleteSuccess"/> <click selector="{{AdminCategorySidebarTreeSection.expandAll}}" stepKey="expandToSeeAllCategories"/> <dontSee selector="{{AdminCategorySidebarTreeSection.categoryInTree(FirstLevelSubCat.name)}}" stepKey="dontSeeCategoryInTree"/> - <actionGroup ref="logout" stepKey="logout"/> + <actionGroup ref="AdminLogoutActionGroup" stepKey="logout"/> </after> <!--Create Category with Five Nesting --> <amOnPage url="{{AdminCategoryPage.url}}" stepKey="openAdminCategoryIndexPage"/> diff --git a/app/code/Magento/Catalog/Test/Mftf/Test/AdminCreateCategoryWithInactiveCategoryTest.xml b/app/code/Magento/Catalog/Test/Mftf/Test/AdminCreateCategoryWithInactiveCategoryTest.xml index fae1a1fa8c2e4..34714db67fbd6 100644 --- a/app/code/Magento/Catalog/Test/Mftf/Test/AdminCreateCategoryWithInactiveCategoryTest.xml +++ b/app/code/Magento/Catalog/Test/Mftf/Test/AdminCreateCategoryWithInactiveCategoryTest.xml @@ -18,11 +18,11 @@ <group value="mtf_migrated"/> </annotations> <before> - <actionGroup ref="LoginAsAdmin" stepKey="loginToAdminPanel"/> + <actionGroup ref="AdminLoginActionGroup" stepKey="loginToAdminPanel"/> </before> <after> <actionGroup ref="DeleteCategoryActionGroup" stepKey="deleteCategory"/> - <actionGroup ref="logout" stepKey="logout"/> + <actionGroup ref="AdminLogoutActionGroup" stepKey="logout"/> </after> <!-- Create In active Category --> <amOnPage url="{{AdminCategoryPage.url}}" stepKey="openAdminCategoryIndexPage"/> diff --git a/app/code/Magento/Catalog/Test/Mftf/Test/AdminCreateCategoryWithInactiveIncludeInMenuTest.xml b/app/code/Magento/Catalog/Test/Mftf/Test/AdminCreateCategoryWithInactiveIncludeInMenuTest.xml index a695aa33079bd..a11717c04ea8c 100644 --- a/app/code/Magento/Catalog/Test/Mftf/Test/AdminCreateCategoryWithInactiveIncludeInMenuTest.xml +++ b/app/code/Magento/Catalog/Test/Mftf/Test/AdminCreateCategoryWithInactiveIncludeInMenuTest.xml @@ -18,11 +18,11 @@ <group value="mtf_migrated"/> </annotations> <before> - <actionGroup ref="LoginAsAdmin" stepKey="loginToAdminPanel"/> + <actionGroup ref="AdminLoginActionGroup" stepKey="loginToAdminPanel"/> </before> <after> <actionGroup ref="DeleteCategoryActionGroup" stepKey="deleteCategory"/> - <actionGroup ref="logout" stepKey="logout"/> + <actionGroup ref="AdminLogoutActionGroup" stepKey="logout"/> </after> <!--Create Category with not included in menu Subcategory --> <amOnPage url="{{AdminCategoryPage.url}}" stepKey="openAdminCategoryIndexPage"/> diff --git a/app/code/Magento/Catalog/Test/Mftf/Test/AdminCreateCategoryWithNoAnchorFieldTest.xml b/app/code/Magento/Catalog/Test/Mftf/Test/AdminCreateCategoryWithNoAnchorFieldTest.xml new file mode 100644 index 0000000000000..ec3c7a26723c2 --- /dev/null +++ b/app/code/Magento/Catalog/Test/Mftf/Test/AdminCreateCategoryWithNoAnchorFieldTest.xml @@ -0,0 +1,93 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + /** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +--> +<tests xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/testSchema.xsd"> + <test name="AdminCreateCategoryWithNoAnchorFieldTest"> + <annotations> + <stories value="Create categories"/> + <title value="Create no anchor subcategory with all fields"/> + <description value="Login as admin and create no anchor subcategory with all fields"/> + <testCaseId value=""/> + <severity value="CRITICAL"/> + <group value="Catalog"/> + <group value="mtf_migrated"/> + </annotations> + <before> + <actionGroup ref="AdminLoginActionGroup" stepKey="loginToAdminPanel"/> + <createData entity="_defaultBlock" stepKey="createDefaultCMSBlock"/> + <createData entity="defaultSimpleProduct" stepKey="simpleProduct" /> + </before> + <after> + <actionGroup ref="DeleteCategoryActionGroup" stepKey="deleteCategory"/> + <actionGroup ref="AdminLogoutActionGroup" stepKey="logout"/> + <deleteData createDataKey="createDefaultCMSBlock" stepKey="deleteDefaultCMSBlock"/> + <deleteData stepKey="deleteSimpleProduct" createDataKey="simpleProduct"/> + </after> + + <!--Create SubCategory--> + <amOnPage url="{{AdminCategoryPage.url}}" stepKey="openAdminCategoryIndexPage"/> + <waitForPageLoad stepKey="waitForPageToLoaded"/> + <click selector="{{AdminCategorySidebarActionSection.AddSubcategoryButton}}" stepKey="clickOnAddSubCategoryButton"/> + <fillField selector="{{AdminCategoryBasicFieldSection.CategoryNameInput}}" userInput="{{_defaultCategory.name}}" stepKey="fillCategoryName"/> + <checkOption selector="{{AdminCategoryBasicFieldSection.EnableCategory}}" stepKey="enableCategory"/> + + <!--Select Content and fill the options--> + <scrollTo selector="{{AdminCategoryContentSection.sectionHeader}}" x="0" y="-80" stepKey="scrollToContent"/> + <click selector="{{AdminCategoryContentSection.sectionHeader}}" stepKey="selectContent"/> + <scrollTo selector="{{AdminCategoryContentSection.AddCMSBlock}}" x="0" y="-80" stepKey="scrollToAddCMSBlock"/> + <selectOption selector="{{AdminCategoryContentSection.AddCMSBlock}}" userInput="$$createDefaultCMSBlock.title$$" stepKey="selectCMSBlock"/> + + <!--Select Display Setting and fill the options--> + <scrollTo selector="{{CategoryDisplaySettingsSection.DisplaySettingTab}}" x="0" y="-80" stepKey="scrollToDisplaySetting"/> + <click selector="{{CategoryDisplaySettingsSection.DisplaySettingTab}}" stepKey="selectDisplaySetting"/> + <selectOption selector="{{CategoryDisplaySettingsSection.displayMode}}" userInput="PRODUCTS_AND_PAGE" stepKey="selectdisplayMode"/> + <click selector="{{CategoryDisplaySettingsSection.anchorLabel}}" stepKey="uncheckAnchor"/> + <click selector="{{CategoryDisplaySettingsSection.productListCheckBox}}" stepKey="enableTheAvailableProductList"/> + <selectOption selector="{{CategoryDisplaySettingsSection.productList}}" parameterArray="['Position', 'Product Name', 'Price']" stepKey="selectPrice"/> + <scrollTo selector="{{CategoryDisplaySettingsSection.defaultProductLisCheckBox}}" x="0" y="-80" stepKey="scrollToDefaultProductList"/> + <click selector="{{CategoryDisplaySettingsSection.defaultProductLisCheckBox}}" stepKey="enableTheDefaultProductList"/> + <selectOption selector="{{CategoryDisplaySettingsSection.defaultProductList}}" userInput="name" stepKey="selectProductName"/> + <scrollTo selector="{{CategoryDisplaySettingsSection.layeredNavigationPriceCheckBox}}" x="0" y="-80" stepKey="scrollToLayeredNavPrice"/> + <click selector="{{CategoryDisplaySettingsSection.layeredNavigationPriceCheckBox}}" stepKey="enableLayeredNavigationPrice"/> + <fillField selector="{{CategoryDisplaySettingsSection.layeredNavigationPriceInput}}" userInput="5.5" stepKey="fillThePrice"/> + + <!--Search the products and select the category products--> + <actionGroup ref="AdminAddProductToCategoryActionGroup" stepKey="addProductToCategory"> + <argument name="product" value="$$simpleProduct$$"/> + </actionGroup> + <click selector="{{AdminCategoryMainActionsSection.SaveButton}}" stepKey="clickSaveButton"/> + <waitForPageLoad stepKey="waitForCategorySaved"/> + <see selector="{{AdminCategoryMessagesSection.SuccessMessage}}" userInput="You saved the category." stepKey="assertSuccessMessage"/> + <waitForPageLoad stepKey="waitForPageTitleToBeSaved"/> + + <!-- Get Category ID --> + <actionGroup ref="OpenCategoryFromCategoryTreeActionGroup" stepKey="getCategoryId"> + <argument name="category" value="{{_defaultCategory.name}}"/> + </actionGroup> + <grabFromCurrentUrl stepKey="categoryId" regex="#\/([0-9]*)?\/$#"/> + + <!--Verify the Category Title--> + <see selector="{{AdminCategoryContentSection.categoryPageTitle}}" userInput="{{_defaultCategory.name}}" stepKey="seePageTitle" /> + + <!--Verify Url Rewrite--> + <actionGroup ref="AdminSearchByRequestPathActionGroup" stepKey="searchByRequestPath"> + <argument name="redirectPath" value="{{_defaultCategory.name_lwr}}.html"/> + <argument name="redirectType" value="No"/> + <argument name="targetPath" value="catalog/category/view/id/{$categoryId}"/> + </actionGroup> + + <!--Verify Product in store front page--> + <amOnPage url="{{StorefrontCategoryPage.url(_defaultCategory.name_lwr)}}" stepKey="amOnCategoryPage"/> + <waitForPageLoad stepKey="waitForPageToBeLoaded"/> + <see selector="{{StorefrontCategoryMainSection.CategoryTitle}}" userInput="{{_defaultCategory.name}}" stepKey="seeCategoryPageTitle"/> + <seeElement selector="{{StorefrontHeaderSection.NavigationCategoryByName(_defaultCategory.name)}}" stepKey="seeCategoryOnNavigation"/> + <waitForPageLoad stepKey="waitForProductToLoad"/> + <seeElement selector="{{StorefrontCategoryMainSection.productLinkByHref($$simpleProduct.urlKey$$)}}" stepKey="seeProductInCategory"/> + <dontSeeElement selector="{{StorefrontCategorySidebarSection.filterOptions}}" stepKey="dontSeeFilterOptionsForNonAnchorCategory"/> + </test> +</tests> diff --git a/app/code/Magento/Catalog/Test/Mftf/Test/AdminCreateCategoryWithProductsGridFilter.xml b/app/code/Magento/Catalog/Test/Mftf/Test/AdminCreateCategoryWithProductsGridFilter.xml deleted file mode 100644 index 6a49b47b75078..0000000000000 --- a/app/code/Magento/Catalog/Test/Mftf/Test/AdminCreateCategoryWithProductsGridFilter.xml +++ /dev/null @@ -1,88 +0,0 @@ -<?xml version="1.0" encoding="UTF-8"?> -<!-- - /** - * Copyright © Magento, Inc. All rights reserved. - * See COPYING.txt for license details. - */ ---> -<tests xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" - xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/testSchema.xsd"> - <test name="AdminCreateCategoryWithProductsGridFilter"> - <annotations> - <stories value="Create categories"/> - <title value="Apply category products grid filter"/> - <description value="Login as admin and create default product and product with grid filter"/> - <testCaseId value="MC-5273"/> - <severity value="CRITICAL"/> - <group value="mtf_migrated"/> - <group value="Catalog"/> - </annotations> - <before> - <actionGroup ref="LoginAsAdmin" stepKey="loginToAdminPanel"/> - </before> - <after> - <actionGroup ref="DeleteCategoryActionGroup" stepKey="deleteCategory"/> - <actionGroup ref="DeleteProductUsingProductGridActionGroup" stepKey="deleteProduct1"> - <argument name="product" value="defaultSimpleProduct"/> - </actionGroup> - <actionGroup ref="DeleteProductUsingProductGridActionGroup" stepKey="deleteProduct2"> - <argument name="product" value="SimpleProduct"/> - </actionGroup> - <actionGroup ref="NavigateToAndResetProductGridToDefaultViewActionGroup" stepKey="NavigateToAndResetProductGridToDefaultView"/> - <actionGroup ref="logout" stepKey="logout"/> - </after> - <amOnPage url="{{AdminProductIndexPage.url}}" stepKey="amOnProductList"/> - <waitForPageLoad stepKey="waitForProductList"/> - <!--Create Default Product--> - <click selector="{{AdminProductGridActionSection.addProductBtn}}" stepKey="clickAddDefaultProduct"/> - <fillField selector="{{AdminProductFormSection.productName}}" userInput="{{SimpleProduct.name}}" stepKey="fillDefaultProductName"/> - <fillField selector="{{AdminProductFormSection.productSku}}" userInput="{{SimpleProduct.sku}}" stepKey="fillDefaultProductSku"/> - <fillField selector="{{AdminProductFormSection.productPrice}}" userInput="{{SimpleProduct.price}}" stepKey="fillDefaultProductPrice"/> - <scrollTo selector="{{AdminProductFormBundleSection.seoDropdown}}" stepKey="scrollToSearchEngine"/> - <click selector="{{AdminProductFormBundleSection.seoDropdown}}" stepKey="selectSearchEngineOptimization"/> - <fillField selector="{{AdminProductFormBundleSection.urlKey}}" userInput="{{SimpleProduct.urlKey}}" stepKey="fillUrlKey"/> - <click selector="{{AdminProductFormActionSection.saveButton}}" stepKey="clickSaveDefaultProduct"/> - <waitForPageLoad stepKey="waitForPDefaultProductSaved"/> - <see selector="{{AdminCategoryMessagesSection.SuccessMessage}}" userInput="You saved the product." stepKey="successMessageYouSavedTheProductIsShown"/> - <!--Create product with grid filter Not Visible Individually--> - <amOnPage url="{{AdminProductIndexPage.url}}" stepKey="ProductList"/> - <waitForPageLoad stepKey="waitForProductPage"/> - <click selector="{{AdminProductGridActionSection.addProductBtn}}" stepKey="clickAddFilterProduct"/> - <fillField selector="{{AdminProductFormSection.productName}}" userInput="{{defaultSimpleProduct.name}}" stepKey="fillProductName"/> - <fillField selector="{{AdminProductFormSection.productSku}}" userInput="{{defaultSimpleProduct.sku}}" stepKey="fillProductSku"/> - <fillField selector="{{AdminProductFormSection.productPrice}}" userInput="{{defaultSimpleProduct.price}}" stepKey="fillProductPrice"/> - <selectOption selector="{{AdminCategoryProductsGridSection.productVisibility}}" userInput="Not Visible Individually" stepKey="selectProductVisibility"/> - <scrollTo selector="{{AdminProductFormBundleSection.seoDropdown}}" stepKey="scrollToSearchEngineOptimization"/> - <click selector="{{AdminProductFormBundleSection.seoDropdown}}" stepKey="selectSearchEngineOptimization1"/> - <fillField selector="{{AdminProductFormBundleSection.urlKey}}" userInput="{{defaultSimpleProduct.urlKey}}" stepKey="fillUrlKey1"/> - <click selector="{{AdminProductFormActionSection.saveButton}}" stepKey="clickSaveProduct"/> - <waitForPageLoad stepKey="waitForProductSaved"/> - <see selector="{{AdminCategoryMessagesSection.SuccessMessage}}" userInput="You saved the product." stepKey="messageYouSavedTheProductIsShown"/> - <amOnPage url="{{AdminCategoryPage.url}}" stepKey="openAdminCategoryIndexPage"/> - <waitForPageLoad stepKey="waitForPageToLoaded"/> - <!--Create sub category--> - <click selector="{{AdminCategorySidebarActionSection.AddSubcategoryButton}}" stepKey="clickOnAddSubCategoryButton"/> - <checkOption selector="{{AdminCategoryBasicFieldSection.EnableCategory}}" stepKey="enableCategory"/> - <fillField selector="{{AdminCategoryBasicFieldSection.CategoryNameInput}}" userInput="{{_defaultCategory.name}}" stepKey="fillCategoryName"/> - <!--Select the default product and product with grid filter--> - <scrollTo selector="{{AdminCategoryBasicFieldSection.productsInCategory}}" x="0" y="-80" stepKey="scrollToProductInCategory"/> - <click selector="{{AdminCategoryBasicFieldSection.productsInCategory}}" stepKey="clickOnProductInCategory"/> - <fillField selector="{{AdminCategoryContentSection.productTableColumnName}}" userInput="{{SimpleProduct.name}}" stepKey="selectProduct"/> - <click selector="{{AdminCategoryContentSection.productSearch}}" stepKey="clickSearchButton"/> - <click selector="{{AdminCategoryContentSection.productTableRow}}" stepKey="selectProductFromRow"/> - <fillField selector="{{AdminCategoryContentSection.productTableColumnName}}" userInput="{{defaultSimpleProduct.name}}" stepKey="selectDefaultProduct"/> - <click selector="{{AdminCategoryContentSection.productSearch}}" stepKey="clickSearchButton1"/> - <click selector="{{AdminCategoryContentSection.productTableRow}}" stepKey="selectDefaultProductFromTableRow"/> - <click selector="{{AdminCategoryMainActionsSection.SaveButton}}" stepKey="clickSaveButton"/> - <waitForPageLoad stepKey="WaitForCategorySaved"/> - <see selector="{{AdminCategoryMessagesSection.SuccessMessage}}" userInput="You saved the category." stepKey="successMessageYouSavedTheCategory"/> - <!--Verify product with grid filter is not not visible--> - <amOnPage url="{{StorefrontProductPage.url(defaultSimpleProduct.urlKey)}}" stepKey="seeOnProductPage"/> - <waitForPageLoad stepKey="waitForStoreFrontProductPageToLoad"/> - <dontSee selector="{{StorefrontProductInfoMainSection.productName}}" userInput="{{defaultSimpleProduct.name}}" stepKey="dontSeeProductInStoreFrontPage"/> - <!--Verify product in Store Front Page--> - <amOnPage url="{{StorefrontProductPage.url(SimpleProduct.urlKey)}}" stepKey="seeDefaultProductPage"/> - <waitForPageLoad stepKey="waitForStoreFrontProductPageToLoad1"/> - <see selector="{{StorefrontProductInfoMainSection.productName}}" userInput="{{SimpleProduct.name}}" stepKey="seeProductInStoreFrontPage"/> - </test> -</tests> diff --git a/app/code/Magento/Catalog/Test/Mftf/Test/AdminCreateCategoryWithProductsGridFilterTest.xml b/app/code/Magento/Catalog/Test/Mftf/Test/AdminCreateCategoryWithProductsGridFilterTest.xml new file mode 100644 index 0000000000000..99d0f88cf0e9a --- /dev/null +++ b/app/code/Magento/Catalog/Test/Mftf/Test/AdminCreateCategoryWithProductsGridFilterTest.xml @@ -0,0 +1,88 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + /** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +--> +<tests xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/testSchema.xsd"> + <test name="AdminCreateCategoryWithProductsGridFilterTest"> + <annotations> + <stories value="Create categories"/> + <title value="Apply category products grid filter"/> + <description value="Login as admin and create default product and product with grid filter"/> + <testCaseId value="MC-5273"/> + <severity value="CRITICAL"/> + <group value="mtf_migrated"/> + <group value="Catalog"/> + </annotations> + <before> + <actionGroup ref="AdminLoginActionGroup" stepKey="loginToAdminPanel"/> + </before> + <after> + <actionGroup ref="DeleteCategoryActionGroup" stepKey="deleteCategory"/> + <actionGroup ref="DeleteProductUsingProductGridActionGroup" stepKey="deleteProduct1"> + <argument name="product" value="defaultSimpleProduct"/> + </actionGroup> + <actionGroup ref="DeleteProductUsingProductGridActionGroup" stepKey="deleteProduct2"> + <argument name="product" value="SimpleProduct"/> + </actionGroup> + <actionGroup ref="NavigateToAndResetProductGridToDefaultViewActionGroup" stepKey="NavigateToAndResetProductGridToDefaultView"/> + <actionGroup ref="AdminLogoutActionGroup" stepKey="logout"/> + </after> + <amOnPage url="{{AdminProductIndexPage.url}}" stepKey="amOnProductList"/> + <waitForPageLoad stepKey="waitForProductList"/> + <!--Create Default Product--> + <click selector="{{AdminProductGridActionSection.addProductBtn}}" stepKey="clickAddDefaultProduct"/> + <fillField selector="{{AdminProductFormSection.productName}}" userInput="{{SimpleProduct.name}}" stepKey="fillDefaultProductName"/> + <fillField selector="{{AdminProductFormSection.productSku}}" userInput="{{SimpleProduct.sku}}" stepKey="fillDefaultProductSku"/> + <fillField selector="{{AdminProductFormSection.productPrice}}" userInput="{{SimpleProduct.price}}" stepKey="fillDefaultProductPrice"/> + <scrollTo selector="{{AdminProductFormBundleSection.seoDropdown}}" stepKey="scrollToSearchEngine"/> + <click selector="{{AdminProductFormBundleSection.seoDropdown}}" stepKey="selectSearchEngineOptimization"/> + <fillField selector="{{AdminProductFormBundleSection.urlKey}}" userInput="{{SimpleProduct.urlKey}}" stepKey="fillUrlKey"/> + <click selector="{{AdminProductFormActionSection.saveButton}}" stepKey="clickSaveDefaultProduct"/> + <waitForPageLoad stepKey="waitForPDefaultProductSaved"/> + <see selector="{{AdminCategoryMessagesSection.SuccessMessage}}" userInput="You saved the product." stepKey="successMessageYouSavedTheProductIsShown"/> + <!--Create product with grid filter Not Visible Individually--> + <amOnPage url="{{AdminProductIndexPage.url}}" stepKey="ProductList"/> + <waitForPageLoad stepKey="waitForProductPage"/> + <click selector="{{AdminProductGridActionSection.addProductBtn}}" stepKey="clickAddFilterProduct"/> + <fillField selector="{{AdminProductFormSection.productName}}" userInput="{{defaultSimpleProduct.name}}" stepKey="fillProductName"/> + <fillField selector="{{AdminProductFormSection.productSku}}" userInput="{{defaultSimpleProduct.sku}}" stepKey="fillProductSku"/> + <fillField selector="{{AdminProductFormSection.productPrice}}" userInput="{{defaultSimpleProduct.price}}" stepKey="fillProductPrice"/> + <selectOption selector="{{AdminCategoryProductsGridSection.productVisibility}}" userInput="Not Visible Individually" stepKey="selectProductVisibility"/> + <scrollTo selector="{{AdminProductFormBundleSection.seoDropdown}}" stepKey="scrollToSearchEngineOptimization"/> + <click selector="{{AdminProductFormBundleSection.seoDropdown}}" stepKey="selectSearchEngineOptimization1"/> + <fillField selector="{{AdminProductFormBundleSection.urlKey}}" userInput="{{defaultSimpleProduct.urlKey}}" stepKey="fillUrlKey1"/> + <click selector="{{AdminProductFormActionSection.saveButton}}" stepKey="clickSaveProduct"/> + <waitForPageLoad stepKey="waitForProductSaved"/> + <see selector="{{AdminCategoryMessagesSection.SuccessMessage}}" userInput="You saved the product." stepKey="messageYouSavedTheProductIsShown"/> + <amOnPage url="{{AdminCategoryPage.url}}" stepKey="openAdminCategoryIndexPage"/> + <waitForPageLoad stepKey="waitForPageToLoaded"/> + <!--Create sub category--> + <click selector="{{AdminCategorySidebarActionSection.AddSubcategoryButton}}" stepKey="clickOnAddSubCategoryButton"/> + <checkOption selector="{{AdminCategoryBasicFieldSection.EnableCategory}}" stepKey="enableCategory"/> + <fillField selector="{{AdminCategoryBasicFieldSection.CategoryNameInput}}" userInput="{{_defaultCategory.name}}" stepKey="fillCategoryName"/> + <!--Select the default product and product with grid filter--> + <scrollTo selector="{{AdminCategoryBasicFieldSection.productsInCategory}}" x="0" y="-80" stepKey="scrollToProductInCategory"/> + <click selector="{{AdminCategoryBasicFieldSection.productsInCategory}}" stepKey="clickOnProductInCategory"/> + <fillField selector="{{AdminCategoryContentSection.productTableColumnName}}" userInput="{{SimpleProduct.name}}" stepKey="selectProduct"/> + <click selector="{{AdminCategoryContentSection.productSearch}}" stepKey="clickSearchButton"/> + <click selector="{{AdminCategoryContentSection.productTableRow}}" stepKey="selectProductFromRow"/> + <fillField selector="{{AdminCategoryContentSection.productTableColumnName}}" userInput="{{defaultSimpleProduct.name}}" stepKey="selectDefaultProduct"/> + <click selector="{{AdminCategoryContentSection.productSearch}}" stepKey="clickSearchButton1"/> + <click selector="{{AdminCategoryContentSection.productTableRow}}" stepKey="selectDefaultProductFromTableRow"/> + <click selector="{{AdminCategoryMainActionsSection.SaveButton}}" stepKey="clickSaveButton"/> + <waitForPageLoad stepKey="WaitForCategorySaved"/> + <see selector="{{AdminCategoryMessagesSection.SuccessMessage}}" userInput="You saved the category." stepKey="successMessageYouSavedTheCategory"/> + <!--Verify product with grid filter is not not visible--> + <amOnPage url="{{StorefrontProductPage.url(defaultSimpleProduct.urlKey)}}" stepKey="seeOnProductPage"/> + <waitForPageLoad stepKey="waitForStoreFrontProductPageToLoad"/> + <dontSee selector="{{StorefrontProductInfoMainSection.productName}}" userInput="{{defaultSimpleProduct.name}}" stepKey="dontSeeProductInStoreFrontPage"/> + <!--Verify product in Store Front Page--> + <amOnPage url="{{StorefrontProductPage.url(SimpleProduct.urlKey)}}" stepKey="seeDefaultProductPage"/> + <waitForPageLoad stepKey="waitForStoreFrontProductPageToLoad1"/> + <see selector="{{StorefrontProductInfoMainSection.productName}}" userInput="{{SimpleProduct.name}}" stepKey="seeProductInStoreFrontPage"/> + </test> +</tests> diff --git a/app/code/Magento/Catalog/Test/Mftf/Test/AdminCreateCategoryWithRequiredFieldsTest.xml b/app/code/Magento/Catalog/Test/Mftf/Test/AdminCreateCategoryWithRequiredFieldsTest.xml index dac2a121d107f..871dd606c14db 100644 --- a/app/code/Magento/Catalog/Test/Mftf/Test/AdminCreateCategoryWithRequiredFieldsTest.xml +++ b/app/code/Magento/Catalog/Test/Mftf/Test/AdminCreateCategoryWithRequiredFieldsTest.xml @@ -18,11 +18,11 @@ <group value="mtf_migrated"/> </annotations> <before> - <actionGroup ref="LoginAsAdmin" stepKey="loginToAdminPanel"/> + <actionGroup ref="AdminLoginActionGroup" stepKey="loginToAdminPanel"/> </before> <after> <actionGroup ref="DeleteCategoryActionGroup" stepKey="deleteCategory"/> - <actionGroup ref="logout" stepKey="logout"/> + <actionGroup ref="AdminLogoutActionGroup" stepKey="logout"/> </after> <!-- Create subcategory with required fields --> <amOnPage url="{{AdminCategoryPage.url}}" stepKey="openAdminCategoryIndexPage"/> diff --git a/app/code/Magento/Catalog/Test/Mftf/Test/AdminCreateCustomProductAttributeWithDropdownFieldTest.xml b/app/code/Magento/Catalog/Test/Mftf/Test/AdminCreateCustomProductAttributeWithDropdownFieldTest.xml index 2d1a58764e20a..7b2c67b205ea8 100644 --- a/app/code/Magento/Catalog/Test/Mftf/Test/AdminCreateCustomProductAttributeWithDropdownFieldTest.xml +++ b/app/code/Magento/Catalog/Test/Mftf/Test/AdminCreateCustomProductAttributeWithDropdownFieldTest.xml @@ -13,7 +13,7 @@ <stories value="Create product Attribute"/> <title value="Create Custom Product Attribute Dropdown Field (Not Required) from Product Page"/> <description value="login as admin and create configurable product attribute with Dropdown field"/> - <severity value="CRITICAL"/> + <severity value="BLOCKER"/> <testCaseId value="MC-10905"/> <group value="mtf_migrated"/> <skip> @@ -23,7 +23,7 @@ <before> <!-- Login as admin --> - <actionGroup ref="LoginAsAdmin" stepKey="login"/> + <actionGroup ref="AdminLoginActionGroup" stepKey="login"/> <!--Create Category--> <createData entity="SimpleSubCategory" stepKey="createCategory"/> @@ -42,7 +42,7 @@ <argument name="ProductAttribute" value="newProductAttribute"/> </actionGroup> - <actionGroup ref="logout" stepKey="logout"/> + <actionGroup ref="AdminLogoutActionGroup" stepKey="logout"/> </after> <!-- Open Product Index Page--> diff --git a/app/code/Magento/Catalog/Test/Mftf/Test/AdminCreateDatetimeProductAttributeTest.xml b/app/code/Magento/Catalog/Test/Mftf/Test/AdminCreateDatetimeProductAttributeTest.xml index 5da824d2ccdb9..37dc7de910917 100644 --- a/app/code/Magento/Catalog/Test/Mftf/Test/AdminCreateDatetimeProductAttributeTest.xml +++ b/app/code/Magento/Catalog/Test/Mftf/Test/AdminCreateDatetimeProductAttributeTest.xml @@ -17,17 +17,17 @@ <group value="catalog"/> </annotations> <before> - <actionGroup ref="LoginAsAdmin" stepKey="login"/> + <actionGroup ref="AdminLoginActionGroup" stepKey="login"/> </before> <after> <actionGroup ref="DeleteProductAttributeActionGroup" stepKey="deleteAttribute"> <argument name="ProductAttribute" value="DatetimeProductAttribute"/> </actionGroup> <actionGroup ref="AdminGridFilterResetActionGroup" stepKey="resetGridFilter"/> - <actionGroup ref="logout" stepKey="logout"/> + <actionGroup ref="AdminLogoutActionGroup" stepKey="logout"/> </after> <!-- Generate the datetime default value --> - <generateDate date="now" format="m/j/y g:i A" stepKey="generateDefaultValue"/> + <generateDate date="now" format="n/j/y g:i A" stepKey="generateDefaultValue"/> <!-- Create new datetime product attribute --> <amOnPage url="{{AdminProductAttributeGridPage.url}}" stepKey="goToProductAttributes"/> <waitForPageLoad stepKey="waitForPageLoadAttributes"/> diff --git a/app/code/Magento/Catalog/Test/Mftf/Test/AdminCreateDropdownProductAttributeTest.xml b/app/code/Magento/Catalog/Test/Mftf/Test/AdminCreateDropdownProductAttributeTest.xml index 4118356b07e74..88b1c874caadc 100644 --- a/app/code/Magento/Catalog/Test/Mftf/Test/AdminCreateDropdownProductAttributeTest.xml +++ b/app/code/Magento/Catalog/Test/Mftf/Test/AdminCreateDropdownProductAttributeTest.xml @@ -12,19 +12,19 @@ <stories value="Create/configure Dropdown product attribute"/> <title value="Admin should be able to create dropdown product attribute"/> <description value="Admin should be able to create dropdown product attribute"/> - <severity value="CRITICAL"/> + <severity value="BLOCKER"/> <testCaseId value="MC-4982"/> <group value="Catalog"/> </annotations> <before> - <actionGroup ref="LoginAsAdmin" stepKey="login"/> + <actionGroup ref="AdminLoginActionGroup" stepKey="login"/> </before> <after> <!-- Remove attribute --> <actionGroup ref="DeleteProductAttributeActionGroup" stepKey="deleteAttribute"> <argument name="ProductAttribute" value="productDropDownAttribute"/> </actionGroup> - <actionGroup ref="logout" stepKey="logout"/> + <actionGroup ref="AdminLogoutActionGroup" stepKey="logout"/> </after> <amOnPage url="{{ProductAttributePage.url}}" stepKey="navigateToNewProductAttributePage"/> @@ -66,7 +66,9 @@ <!-- Check attribute data --> <grabValueFrom selector="{{DropdownAttributeOptionsSection.nthOptionAdminLabel('2')}}" stepKey="secondOptionAdminLabel"/> - <assertEquals actual="$secondOptionAdminLabel" expected="'Fish & Chips'" - stepKey="assertSecondOption"/> + <assertEquals stepKey="assertSecondOption"> + <actualResult type="const">$secondOptionAdminLabel</actualResult> + <expectedResult type="const">'Fish & Chips'</expectedResult> + </assertEquals> </test> </tests> diff --git a/app/code/Magento/Catalog/Test/Mftf/Test/AdminCreateDropdownProductAttributeVisibleInStorefrontAdvancedSearchFormTest.xml b/app/code/Magento/Catalog/Test/Mftf/Test/AdminCreateDropdownProductAttributeVisibleInStorefrontAdvancedSearchFormTest.xml index 557f0768c98a3..fef69edde23e8 100644 --- a/app/code/Magento/Catalog/Test/Mftf/Test/AdminCreateDropdownProductAttributeVisibleInStorefrontAdvancedSearchFormTest.xml +++ b/app/code/Magento/Catalog/Test/Mftf/Test/AdminCreateDropdownProductAttributeVisibleInStorefrontAdvancedSearchFormTest.xml @@ -15,7 +15,7 @@ <title value="AdminCreateDropdownProductAttributeVisibleInStorefrontAdvancedSearchFormTest"/> <description value="Admin should able to create product Dropdown attribute and check its visibility on frontend in Advanced Search form"/> <testCaseId value="MC-10827"/> - <severity value="CRITICAL"/> + <severity value="BLOCKER"/> <group value="mtf_migrated"/> </annotations> @@ -31,10 +31,15 @@ <!-- Create product attribute set --> <createData entity="CatalogAttributeSet" stepKey="createAttributeSet"/> - <actionGroup ref="LoginAsAdmin" stepKey="loginAsAdmin"/> + <actionGroup ref="AdminLoginActionGroup" stepKey="loginAsAdmin"/> </before> <after> - <actionGroup ref="logout" stepKey="logout"/> + <deleteData createDataKey="attribute" stepKey="deleteProductAttribute"/> + <deleteData createDataKey="createAttributeSet" stepKey="deleteAttributeSet"/> + <actionGroup ref="AdminLogoutActionGroup" stepKey="logout"/> + + <!-- Reindex invalidated indices after product attribute has been created/deleted --> + <magentoCron groups="index" stepKey="reindexInvalidatedIndices"/> </after> <!-- Filter product attribute set by attribute set name --> diff --git a/app/code/Magento/Catalog/Test/Mftf/Test/AdminCreateDuplicateCategoryTest.xml b/app/code/Magento/Catalog/Test/Mftf/Test/AdminCreateDuplicateCategoryTest.xml index cbef9566b2b78..a34712397e830 100644 --- a/app/code/Magento/Catalog/Test/Mftf/Test/AdminCreateDuplicateCategoryTest.xml +++ b/app/code/Magento/Catalog/Test/Mftf/Test/AdminCreateDuplicateCategoryTest.xml @@ -18,12 +18,12 @@ </annotations> <before> - <actionGroup ref="LoginAsAdmin" stepKey="loginToAdminPanel"/> + <actionGroup ref="AdminLoginActionGroup" stepKey="loginToAdminPanel"/> <createData entity="SimpleSubCategory" stepKey="category"/> </before> <after> <deleteData createDataKey="category" stepKey="deleteCategory"/> - <actionGroup ref="logout" stepKey="logout"/> + <actionGroup ref="AdminLogoutActionGroup" stepKey="logout"/> </after> <!-- Open Category Page and select Add category --> diff --git a/app/code/Magento/Catalog/Test/Mftf/Test/AdminCreateDuplicateProductTest.xml b/app/code/Magento/Catalog/Test/Mftf/Test/AdminCreateDuplicateProductTest.xml index 4507e1f880a86..cc76304bc5ebc 100644 --- a/app/code/Magento/Catalog/Test/Mftf/Test/AdminCreateDuplicateProductTest.xml +++ b/app/code/Magento/Catalog/Test/Mftf/Test/AdminCreateDuplicateProductTest.xml @@ -22,13 +22,14 @@ <createData entity="Two_nested_categories" stepKey="subCategory"> <requiredEntity createDataKey="category"/> </createData> - <actionGroup ref="LoginAsAdmin" stepKey="login"/> + <magentoCLI command="config:set catalog/seo/generate_category_product_rewrites 1" stepKey="enableGenerateUrlRewrite"/> + <actionGroup ref="AdminLoginActionGroup" stepKey="login"/> </before> <after> <deleteData createDataKey="subCategory" stepKey="deleteSubCategory"/> <deleteData createDataKey="category" stepKey="deleteCategory"/> - <actionGroup ref="logout" stepKey="logout"/> + <actionGroup ref="AdminLogoutActionGroup" stepKey="logout"/> </after> <!-- Go to new simple product page --> diff --git a/app/code/Magento/Catalog/Test/Mftf/Test/AdminCreateInactiveFlatCategoryAndUpdateAsInactiveTest.xml b/app/code/Magento/Catalog/Test/Mftf/Test/AdminCreateInactiveFlatCategoryAndUpdateAsInactiveTest.xml index b0e6fe87be918..c3b86c99bbd67 100644 --- a/app/code/Magento/Catalog/Test/Mftf/Test/AdminCreateInactiveFlatCategoryAndUpdateAsInactiveTest.xml +++ b/app/code/Magento/Catalog/Test/Mftf/Test/AdminCreateInactiveFlatCategoryAndUpdateAsInactiveTest.xml @@ -19,7 +19,7 @@ </annotations> <before> <!--Login as admin --> - <actionGroup ref="LoginAsAdmin" stepKey="loginToAdminPanel"/> + <actionGroup ref="AdminLoginActionGroup" stepKey="loginToAdminPanel"/> <!--Create category--> <createData entity="CatNotActive" stepKey="createCategory"/> <!-- Create First StoreView --> @@ -52,7 +52,7 @@ <actionGroup ref="AdminDeleteStoreViewActionGroup" stepKey="deleteStoreViewFr"> <argument name="customStore" value="customStoreFR"/> </actionGroup> - <actionGroup ref="logout" stepKey="logout"/> + <actionGroup ref="AdminLogoutActionGroup" stepKey="logout"/> </after> <!-- Select created category and make category inactive--> <amOnPage url="{{AdminCategoryPage.url}}" stepKey="openAdminCategoryIndexPage"/> diff --git a/app/code/Magento/Catalog/Test/Mftf/Test/AdminCreateInactiveFlatCategoryTest.xml b/app/code/Magento/Catalog/Test/Mftf/Test/AdminCreateInactiveFlatCategoryTest.xml index 7de37b9cb77ef..affdbaf7b0eb7 100644 --- a/app/code/Magento/Catalog/Test/Mftf/Test/AdminCreateInactiveFlatCategoryTest.xml +++ b/app/code/Magento/Catalog/Test/Mftf/Test/AdminCreateInactiveFlatCategoryTest.xml @@ -19,7 +19,7 @@ </annotations> <before> <!--Login as admin --> - <actionGroup ref="LoginAsAdmin" stepKey="loginToAdminPanel"/> + <actionGroup ref="AdminLoginActionGroup" stepKey="loginToAdminPanel"/> <!--Create category--> <createData entity="SimpleSubCategory" stepKey="createCategory"/> <!-- Create First StoreView --> @@ -52,7 +52,7 @@ <actionGroup ref="AdminDeleteStoreViewActionGroup" stepKey="deleteStoreViewFr"> <argument name="customStore" value="customStoreFR"/> </actionGroup> - <actionGroup ref="logout" stepKey="logout"/> + <actionGroup ref="AdminLogoutActionGroup" stepKey="logout"/> </after> <!-- Select created category and make category inactive--> <amOnPage url="{{AdminCategoryPage.url}}" stepKey="openAdminCategoryIndexPage"/> diff --git a/app/code/Magento/Catalog/Test/Mftf/Test/AdminCreateInactiveInMenuFlatCategoryTest.xml b/app/code/Magento/Catalog/Test/Mftf/Test/AdminCreateInactiveInMenuFlatCategoryTest.xml index c7aba1fe8376f..10e4d9f434be0 100644 --- a/app/code/Magento/Catalog/Test/Mftf/Test/AdminCreateInactiveInMenuFlatCategoryTest.xml +++ b/app/code/Magento/Catalog/Test/Mftf/Test/AdminCreateInactiveInMenuFlatCategoryTest.xml @@ -19,7 +19,7 @@ </annotations> <before> <!--Login as admin --> - <actionGroup ref="LoginAsAdmin" stepKey="loginToAdminPanel"/> + <actionGroup ref="AdminLoginActionGroup" stepKey="loginToAdminPanel"/> <!--Create category--> <createData entity="SimpleSubCategory" stepKey="category"/> <!-- Create First StoreView --> @@ -52,7 +52,7 @@ <argument name="customStore" value="customStoreFR"/> </actionGroup> <deleteData createDataKey="category" stepKey="deleteCategory"/> - <actionGroup ref="logout" stepKey="logout"/> + <actionGroup ref="AdminLogoutActionGroup" stepKey="logout"/> </after> <!-- Select created category and disable Include In Menu option--> <amOnPage url="{{AdminCategoryPage.url}}" stepKey="openAdminCategoryIndexPage"/> diff --git a/app/code/Magento/Catalog/Test/Mftf/Test/AdminCreateMultipleSelectProductAttributeVisibleInStorefrontAdvancedSearchFormTest.xml b/app/code/Magento/Catalog/Test/Mftf/Test/AdminCreateMultipleSelectProductAttributeVisibleInStorefrontAdvancedSearchFormTest.xml index 6bfd012bc88b2..caacfde89d1cb 100644 --- a/app/code/Magento/Catalog/Test/Mftf/Test/AdminCreateMultipleSelectProductAttributeVisibleInStorefrontAdvancedSearchFormTest.xml +++ b/app/code/Magento/Catalog/Test/Mftf/Test/AdminCreateMultipleSelectProductAttributeVisibleInStorefrontAdvancedSearchFormTest.xml @@ -15,7 +15,7 @@ <title value="Create product attribute of type Multiple Select and check its visibility on frontend in Advanced Search form"/> <description value="Admin should be able to create product attribute of type Multiple Select and check its visibility on frontend in Advanced Search form"/> <testCaseId value="MC-10828"/> - <severity value="CRITICAL"/> + <severity value="BLOCKER"/> <group value="mtf_migrated"/> </annotations> @@ -31,7 +31,7 @@ <!-- Create product attribute set --> <createData entity="CatalogAttributeSet" stepKey="createAttributeSet"/> - <actionGroup ref="LoginAsAdmin" stepKey="loginAsAdmin"/> + <actionGroup ref="AdminLoginActionGroup" stepKey="loginAsAdmin"/> </before> <after> <!-- Delete product attribute --> @@ -40,7 +40,10 @@ <!-- Delete product attribute set --> <deleteData createDataKey="createAttributeSet" stepKey="deleteAttributeSet"/> - <actionGroup ref="logout" stepKey="logout"/> + <actionGroup ref="AdminLogoutActionGroup" stepKey="logout"/> + + <!-- Reindex invalidated indices after product attribute has been created/deleted --> + <magentoCron groups="index" stepKey="reindexInvalidatedIndices"/> </after> <!-- Filter product attribute set by attribute set name --> diff --git a/app/code/Magento/Catalog/Test/Mftf/Test/AdminCreateNewAttributeFromProductPageTest.xml b/app/code/Magento/Catalog/Test/Mftf/Test/AdminCreateNewAttributeFromProductPageTest.xml index 51c4a3250d609..e99643deed11d 100644 --- a/app/code/Magento/Catalog/Test/Mftf/Test/AdminCreateNewAttributeFromProductPageTest.xml +++ b/app/code/Magento/Catalog/Test/Mftf/Test/AdminCreateNewAttributeFromProductPageTest.xml @@ -20,10 +20,10 @@ <group value="Catalog"/> </annotations> <before> - <actionGroup ref="LoginAsAdmin" stepKey="loginAsAdmin"/> + <actionGroup ref="AdminLoginActionGroup" stepKey="loginAsAdmin"/> </before> <after> - <actionGroup ref="logout" stepKey="logout"/> + <actionGroup ref="AdminLogoutActionGroup" stepKey="logout"/> </after> <actionGroup ref="GoToProductCatalogPageActionGroup" stepKey="goToProductCatalogPage"/> diff --git a/app/code/Magento/Catalog/Test/Mftf/Test/AdminCreateNewAttributeFromProductTest.xml b/app/code/Magento/Catalog/Test/Mftf/Test/AdminCreateNewAttributeFromProductTest.xml index 8fb226f5f5585..7c8ce1455fc37 100644 --- a/app/code/Magento/Catalog/Test/Mftf/Test/AdminCreateNewAttributeFromProductTest.xml +++ b/app/code/Magento/Catalog/Test/Mftf/Test/AdminCreateNewAttributeFromProductTest.xml @@ -20,7 +20,7 @@ <group value="Catalog"/> </annotations> <before> - <actionGroup ref="LoginAsAdmin" stepKey="login"/> + <actionGroup ref="AdminLoginActionGroup" stepKey="login"/> <!--Create product--> <createData entity="SimpleProduct2" stepKey="createProduct"/> @@ -42,7 +42,7 @@ <argument name="ProductAttribute" value="productDropDownAttribute"/> </actionGroup> - <actionGroup ref="logout" stepKey="logout"/> + <actionGroup ref="AdminLogoutActionGroup" stepKey="logout"/> </after> <!--Create 2 store views--> @@ -54,7 +54,9 @@ </actionGroup> <!--Go to created product page and create new attribute--> - <amOnPage url="{{AdminProductEditPage.url($$createProduct.id$$)}}" stepKey="openAdminEditPage"/> + <actionGroup ref="AdminProductPageOpenByIdActionGroup" stepKey="openAdminEditPage"> + <argument name="productId" value="$$createProduct.id$$"/> + </actionGroup> <actionGroup ref="AdminCreateAttributeWithValueWithTwoStoreViesFromProductPageActionGroup" stepKey="createAttribute"> <argument name="attributeName" value="{{productDropDownAttribute.attribute_code}}"/> <argument name="attributeType" value="Dropdown"/> diff --git a/app/code/Magento/Catalog/Test/Mftf/Test/AdminCreateNewGroupForAttributeSetTest.xml b/app/code/Magento/Catalog/Test/Mftf/Test/AdminCreateNewGroupForAttributeSetTest.xml index a105f343d3e21..573fc1f83a5a8 100644 --- a/app/code/Magento/Catalog/Test/Mftf/Test/AdminCreateNewGroupForAttributeSetTest.xml +++ b/app/code/Magento/Catalog/Test/Mftf/Test/AdminCreateNewGroupForAttributeSetTest.xml @@ -13,7 +13,7 @@ <stories value="Edit attribute set"/> <title value="Admin should be able to create new group in an Attribute Set"/> <description value="The test verifies creating a new group in an attribute set and a validation message in case of empty group name"/> - <severity value="CRITICAL"/> + <severity value="BLOCKER"/> <testCaseId value="MC-170"/> <group value="Catalog"/> </annotations> @@ -23,11 +23,15 @@ <createData entity="productAttributeWithTwoOptions" stepKey="createConfigProductAttribute"/> <!-- Login to Admin --> - <actionGroup ref="LoginAsAdmin" stepKey="LoginAsAdmin"/> + <actionGroup ref="AdminLoginActionGroup" stepKey="LoginAsAdmin"/> </before> <after> <deleteData createDataKey="createConfigProductAttribute" stepKey="deleteConfigProductAttribute"/> - <actionGroup ref="logout" stepKey="logout"/> + <deleteData createDataKey="createAttributeSet" stepKey="deleteAttributeSet"/> + <actionGroup ref="AdminLogoutActionGroup" stepKey="logout"/> + + <!-- Reindex invalidated indices after product attribute has been created/deleted --> + <magentoCron groups="index" stepKey="reindexInvalidatedIndices"/> </after> <!-- Navigate to Stores > Attributes > Attribute Set --> diff --git a/app/code/Magento/Catalog/Test/Mftf/Test/AdminCreateProductAttributeFromProductPageTest.xml b/app/code/Magento/Catalog/Test/Mftf/Test/AdminCreateProductAttributeFromProductPageTest.xml index 2e502f58041e6..7fdab11d0a050 100644 --- a/app/code/Magento/Catalog/Test/Mftf/Test/AdminCreateProductAttributeFromProductPageTest.xml +++ b/app/code/Magento/Catalog/Test/Mftf/Test/AdminCreateProductAttributeFromProductPageTest.xml @@ -13,14 +13,14 @@ <stories value="Create product Attribute"/> <title value="Create Product Attribute from Product Page"/> <description value="Login as admin and create new product attribute from product page with Text Field"/> - <severity value="CRITICAL"/> + <severity value="BLOCKER"/> <testCaseId value="MC-10899"/> <group value="mtf_migrated"/> </annotations> <before> <!-- Login as admin --> - <actionGroup ref="LoginAsAdmin" stepKey="login"/> + <actionGroup ref="AdminLoginActionGroup" stepKey="login"/> <!--Create Category--> <createData entity="SimpleSubCategory" stepKey="createCategory"/> @@ -39,7 +39,7 @@ <argument name="ProductAttribute" value="newProductAttribute"/> </actionGroup> - <actionGroup ref="logout" stepKey="logout"/> + <actionGroup ref="AdminLogoutActionGroup" stepKey="logout"/> </after> <!-- Open Product Index Page--> diff --git a/app/code/Magento/Catalog/Test/Mftf/Test/AdminCreateProductAttributeRequiredTextFieldTest.xml b/app/code/Magento/Catalog/Test/Mftf/Test/AdminCreateProductAttributeRequiredTextFieldTest.xml index 63eed37b1e84f..274a560d343d8 100644 --- a/app/code/Magento/Catalog/Test/Mftf/Test/AdminCreateProductAttributeRequiredTextFieldTest.xml +++ b/app/code/Magento/Catalog/Test/Mftf/Test/AdminCreateProductAttributeRequiredTextFieldTest.xml @@ -13,14 +13,14 @@ <stories value="Manage products"/> <title value="Create Custom Product Attribute Text Field (Required) from Product Page"/> <description value="Login as admin and create product attribute with Text Field and Required option"/> - <severity value="CRITICAL"/> + <severity value="BLOCKER"/> <testCaseId value="MC-10906"/> <group value="mtf_migrated"/> </annotations> <before> <!-- Login as admin --> - <actionGroup ref="LoginAsAdmin" stepKey="login"/> + <actionGroup ref="AdminLoginActionGroup" stepKey="login"/> <!--Create Category--> <createData entity="SimpleSubCategory" stepKey="createCategory"/> @@ -37,7 +37,7 @@ <actionGroup ref="DeleteProductAttributeActionGroup" stepKey="deleteCreatedAttribute"> <argument name="ProductAttribute" value="newProductAttribute"/> </actionGroup> - <actionGroup ref="logout" stepKey="logout"/> + <actionGroup ref="AdminLogoutActionGroup" stepKey="logout"/> </after> <!-- Open Product Index Page--> diff --git a/app/code/Magento/Catalog/Test/Mftf/Test/AdminCreateProductCustomAttributeSet.xml b/app/code/Magento/Catalog/Test/Mftf/Test/AdminCreateProductCustomAttributeSet.xml deleted file mode 100644 index 7f6feaff3ed5d..0000000000000 --- a/app/code/Magento/Catalog/Test/Mftf/Test/AdminCreateProductCustomAttributeSet.xml +++ /dev/null @@ -1,87 +0,0 @@ -<?xml version="1.0" encoding="UTF-8"?> -<!-- - /** - * Copyright © Magento, Inc. All rights reserved. - * See COPYING.txt for license details. - */ ---> - -<tests xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" - xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/testSchema.xsd"> - <test name="AdminCreateProductCustomAttributeSet"> - <annotations> - <features value="Catalog"/> - <stories value="Add/Update attribute set"/> - <title value="Admin should be able to create a simple product using a custom attribute set"/> - <description value="Admin should be able to create a simple product using a custom attribute set"/> - <severity value="AVERAGE"/> - <testCaseId value="MC-244"/> - <group value="Catalog"/> - </annotations> - - <before> - <actionGroup ref="LoginAsAdmin" stepKey="loginAsAdmin"/> - </before> - - <after> - <!-- Delete the new attribute set --> - <amOnPage url="{{AdminProductAttributeSetGridPage.url}}" stepKey="goToAttributeSets"/> - <waitForPageLoad stepKey="wait1"/> - <fillField selector="{{AdminProductAttributeSetGridSection.filter}}" userInput="{{ProductAttributeFrontendLabel.label}}" stepKey="filterByName"/> - <click selector="{{AdminProductAttributeSetGridSection.searchBtn}}" stepKey="clickSearch"/> - <click selector="{{AdminProductAttributeSetGridSection.nthRow('1')}}" stepKey="clickFirstRow"/> - <waitForPageLoad stepKey="wait2"/> - <click selector="{{AdminProductAttributeSetSection.deleteBtn}}" stepKey="clickDelete"/> - <click selector="{{AdminProductAttributeSetSection.modalOk}}" stepKey="confirmDelete"/> - <waitForPageLoad stepKey="wait3"/> - - <amOnPage url="admin/admin/auth/logout/" stepKey="amOnLogoutPage"/> - </after> - - <!-- Create a new attribute set --> - <amOnPage url="{{AdminProductAttributeSetGridPage.url}}" stepKey="goToAttributeSets"/> - <waitForPageLoad stepKey="wait1"/> - <click selector="{{AdminProductAttributeSetGridSection.addAttributeSetBtn}}" stepKey="clickAddAttributeSet"/> - <fillField selector="{{AdminProductAttributeSetSection.name}}" userInput="{{ProductAttributeFrontendLabel.label}}" stepKey="fillName"/> - <selectOption selector="{{AdminProductAttributeSetSection.basedOn}}" userInput="Default" stepKey="selectDefaultSet"/> - <click selector="{{AdminProductAttributeSetSection.saveBtn}}" stepKey="clickSave1"/> - <dragAndDrop selector1="{{AdminProductAttributeSetSection.attribute('meta_keyword')}}" selector2="{{AdminProductAttributeSetSection.attribute('manufacturer')}}" stepKey="unassign1"/> - <click selector="{{AdminProductAttributeSetSection.addNewGroupBtn}}" stepKey="clickAddNewGroup"/> - <fillField selector="{{AdminProductAttributeSetSection.newGroupName}}" userInput="TestGroupName" stepKey="fillNewGroupName"/> - <click selector="{{AdminProductAttributeSetSection.modalOk}}" stepKey="clickOkInModal"/> - <dragAndDrop selector1="{{AdminProductAttributeSetSection.attribute('manufacturer')}}" selector2="{{AdminProductAttributeSetSection.attribute('TestGroupName')}}" stepKey="assignManufacturer"/> - <click selector="{{AdminProductAttributeSetSection.saveBtn}}" stepKey="clickSave2"/> - - <!-- Go to new product page and see a default attribute --> - <amOnPage url="{{AdminProductCreatePage.url('4', 'simple')}}" stepKey="goToNewProductPage"/> - <waitForPageLoad stepKey="wait2"/> - <click selector="{{AdminProductSEOSection.sectionHeader}}" stepKey="expandSEOSection"/> - <seeElementInDOM selector="{{AdminProductFormSection.divByDataIndex('meta_keyword')}}" stepKey="seeMetaKeyword"/> - <dontSeeElementInDOM selector="{{AdminProductFormSection.divByDataIndex('testgroupname')}}" stepKey="dontSeeTestGroupName"/> - - <!-- Switch from default attribute set to new attribute set --> - <!-- A scrollToTopOfPage is needed to hide the floating header --> - <scrollToTopOfPage stepKey="scrollToTop"/> - <click selector="{{AdminProductFormSection.attributeSet}}" stepKey="startEditAttrSet"/> - <fillField selector="{{AdminProductFormSection.attributeSetFilter}}" userInput="{{ProductAttributeFrontendLabel.label}}" stepKey="searchForAttrSet"/> - <click selector="{{AdminProductFormSection.attributeSetFilterResult}}" stepKey="selectAttrSet"/> - - <!-- See new attibute set --> - <seeElementInDOM selector="{{AdminProductFormSection.divByDataIndex('testgroupname')}}" stepKey="seeTestGroupName"/> - <dontSeeElementInDOM selector="{{AdminProductFormSection.divByDataIndex('meta_keyword')}}" stepKey="dontSeeMetaKeyword"/> - - <!-- Finish filling the new product page --> - <actionGroup ref="FillMainProductFormNoWeightActionGroup" stepKey="fillSimpleProductMain"> - <argument name="product" value="_defaultProduct"/> - </actionGroup> - <actionGroup ref="SaveProductFormActionGroup" stepKey="saveSimpleProduct"/> - - <!-- Check the storefront --> - <amOnPage url="{{_defaultProduct.name}}.html" stepKey="goToProductPage"/> - <waitForPageLoad stepKey="waitForProductPageLoad"/> - <seeInTitle userInput="{{_defaultProduct.name}}" stepKey="seeProductNameInTitlte"/> - <see userInput="{{_defaultProduct.name}}" selector="{{StorefrontProductInfoMainSection.productName}}" stepKey="assertProductName"/> - <see userInput="{{_defaultProduct.sku}}" selector="{{StorefrontProductInfoMainSection.productSku}}" stepKey="assertProductSku"/> - <see userInput="${{_defaultProduct.price}}" selector="{{StorefrontProductInfoMainSection.productPrice}}" stepKey="assertProductPrice"/> - </test> -</tests> diff --git a/app/code/Magento/Catalog/Test/Mftf/Test/AdminCreateProductCustomAttributeSetTest.xml b/app/code/Magento/Catalog/Test/Mftf/Test/AdminCreateProductCustomAttributeSetTest.xml new file mode 100644 index 0000000000000..d2278f3ddae1d --- /dev/null +++ b/app/code/Magento/Catalog/Test/Mftf/Test/AdminCreateProductCustomAttributeSetTest.xml @@ -0,0 +1,87 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + /** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +--> + +<tests xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/testSchema.xsd"> + <test name="AdminCreateProductCustomAttributeSetTest"> + <annotations> + <features value="Catalog"/> + <stories value="Add/Update attribute set"/> + <title value="Admin should be able to create a simple product using a custom attribute set"/> + <description value="Admin should be able to create a simple product using a custom attribute set"/> + <severity value="AVERAGE"/> + <testCaseId value="MC-244"/> + <group value="Catalog"/> + </annotations> + + <before> + <actionGroup ref="AdminLoginActionGroup" stepKey="loginAsAdmin"/> + </before> + + <after> + <!-- Delete the new attribute set --> + <amOnPage url="{{AdminProductAttributeSetGridPage.url}}" stepKey="goToAttributeSets"/> + <waitForPageLoad stepKey="wait1"/> + <fillField selector="{{AdminProductAttributeSetGridSection.filter}}" userInput="{{ProductAttributeFrontendLabel.label}}" stepKey="filterByName"/> + <click selector="{{AdminProductAttributeSetGridSection.searchBtn}}" stepKey="clickSearch"/> + <click selector="{{AdminProductAttributeSetGridSection.nthRow('1')}}" stepKey="clickFirstRow"/> + <waitForPageLoad stepKey="wait2"/> + <click selector="{{AdminProductAttributeSetSection.deleteBtn}}" stepKey="clickDelete"/> + <click selector="{{AdminProductAttributeSetSection.modalOk}}" stepKey="confirmDelete"/> + <waitForPageLoad stepKey="wait3"/> + + <amOnPage url="admin/admin/auth/logout/" stepKey="amOnLogoutPage"/> + </after> + + <!-- Create a new attribute set --> + <amOnPage url="{{AdminProductAttributeSetGridPage.url}}" stepKey="goToAttributeSets"/> + <waitForPageLoad stepKey="wait1"/> + <click selector="{{AdminProductAttributeSetGridSection.addAttributeSetBtn}}" stepKey="clickAddAttributeSet"/> + <fillField selector="{{AdminProductAttributeSetSection.name}}" userInput="{{ProductAttributeFrontendLabel.label}}" stepKey="fillName"/> + <selectOption selector="{{AdminProductAttributeSetSection.basedOn}}" userInput="Default" stepKey="selectDefaultSet"/> + <click selector="{{AdminProductAttributeSetSection.saveBtn}}" stepKey="clickSave1"/> + <dragAndDrop selector1="{{AdminProductAttributeSetSection.attribute('meta_keyword')}}" selector2="{{AdminProductAttributeSetSection.attribute('manufacturer')}}" stepKey="unassign1"/> + <click selector="{{AdminProductAttributeSetSection.addNewGroupBtn}}" stepKey="clickAddNewGroup"/> + <fillField selector="{{AdminProductAttributeSetSection.newGroupName}}" userInput="TestGroupName" stepKey="fillNewGroupName"/> + <click selector="{{AdminProductAttributeSetSection.modalOk}}" stepKey="clickOkInModal"/> + <dragAndDrop selector1="{{AdminProductAttributeSetSection.attribute('manufacturer')}}" selector2="{{AdminProductAttributeSetSection.attribute('TestGroupName')}}" stepKey="assignManufacturer"/> + <click selector="{{AdminProductAttributeSetSection.saveBtn}}" stepKey="clickSave2"/> + + <!-- Go to new product page and see a default attribute --> + <amOnPage url="{{AdminProductCreatePage.url('4', 'simple')}}" stepKey="goToNewProductPage"/> + <waitForPageLoad stepKey="wait2"/> + <click selector="{{AdminProductSEOSection.sectionHeader}}" stepKey="expandSEOSection"/> + <seeElementInDOM selector="{{AdminProductFormSection.divByDataIndex('meta_keyword')}}" stepKey="seeMetaKeyword"/> + <dontSeeElementInDOM selector="{{AdminProductFormSection.divByDataIndex('testgroupname')}}" stepKey="dontSeeTestGroupName"/> + + <!-- Switch from default attribute set to new attribute set --> + <!-- A scrollToTopOfPage is needed to hide the floating header --> + <scrollToTopOfPage stepKey="scrollToTop"/> + <click selector="{{AdminProductFormSection.attributeSet}}" stepKey="startEditAttrSet"/> + <fillField selector="{{AdminProductFormSection.attributeSetFilter}}" userInput="{{ProductAttributeFrontendLabel.label}}" stepKey="searchForAttrSet"/> + <click selector="{{AdminProductFormSection.attributeSetFilterResult}}" stepKey="selectAttrSet"/> + + <!-- See new attibute set --> + <seeElementInDOM selector="{{AdminProductFormSection.divByDataIndex('testgroupname')}}" stepKey="seeTestGroupName"/> + <dontSeeElementInDOM selector="{{AdminProductFormSection.divByDataIndex('meta_keyword')}}" stepKey="dontSeeMetaKeyword"/> + + <!-- Finish filling the new product page --> + <actionGroup ref="FillMainProductFormNoWeightActionGroup" stepKey="fillSimpleProductMain"> + <argument name="product" value="_defaultProduct"/> + </actionGroup> + <actionGroup ref="SaveProductFormActionGroup" stepKey="saveSimpleProduct"/> + + <!-- Check the storefront --> + <amOnPage url="{{_defaultProduct.name}}.html" stepKey="goToProductPage"/> + <waitForPageLoad stepKey="waitForProductPageLoad"/> + <seeInTitle userInput="{{_defaultProduct.name}}" stepKey="seeProductNameInTitlte"/> + <see userInput="{{_defaultProduct.name}}" selector="{{StorefrontProductInfoMainSection.productName}}" stepKey="assertProductName"/> + <see userInput="{{_defaultProduct.sku}}" selector="{{StorefrontProductInfoMainSection.productSku}}" stepKey="assertProductSku"/> + <see userInput="${{_defaultProduct.price}}" selector="{{StorefrontProductInfoMainSection.productPrice}}" stepKey="assertProductPrice"/> + </test> +</tests> diff --git a/app/code/Magento/Catalog/Test/Mftf/Test/AdminCreateProductDuplicateUrlkeyTest.xml b/app/code/Magento/Catalog/Test/Mftf/Test/AdminCreateProductDuplicateUrlkeyTest.xml deleted file mode 100644 index f5e42bb84549c..0000000000000 --- a/app/code/Magento/Catalog/Test/Mftf/Test/AdminCreateProductDuplicateUrlkeyTest.xml +++ /dev/null @@ -1,85 +0,0 @@ -<?xml version="1.0" encoding="UTF-8"?> -<!-- - /** - * Copyright © Magento, Inc. All rights reserved. - * See COPYING.txt for license details. - */ ---> - -<tests xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" - xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/testSchema.xsd"> - <test name="AdminCreateProductDuplicateUrlkeyTest"> - <annotations> - <features value="Catalog"/> - <stories value="Errors"/> - <title value="Admin should see an error when trying to save a product with a duplicate URL key"/> - <description value="Admin should see an error when trying to save a product with a duplicate URL key"/> - <severity value="MAJOR"/> - <testCaseId value="MC-112"/> - <group value="product"/> - </annotations> - <before> - <createData entity="SimpleTwo" stepKey="simpleProduct"> - </createData> - </before> - <after> - <actionGroup ref="logout" stepKey="adminLogout"/> - <deleteData createDataKey="simpleProduct" stepKey="deleteProduct"/> - </after> - - <actionGroup ref="LoginAsAdmin" stepKey="loginAsAdmin"/> - <amOnPage url="{{AdminProductIndexPage.url}}" stepKey="navigateToProductIndex"/> - <click selector="{{AdminProductGridActionSection.addProductToggle}}" stepKey="clickAddProductDropdown"/> - <click selector="{{AdminProductGridActionSection.addSimpleProduct}}" stepKey="clickAddSimpleProduct"/> - <fillField userInput="$$simpleProduct.name$$new" selector="{{AdminProductFormSection.productName}}" stepKey="fillName"/> - <fillField userInput="$$simpleProduct.sku$$new" selector="{{AdminProductFormSection.productSku}}" stepKey="fillSKU"/> - <fillField userInput="$$simpleProduct.price$$" selector="{{AdminProductFormSection.productPrice}}" stepKey="fillPrice"/> - <fillField userInput="$$simpleProduct.quantity$$" selector="{{AdminProductFormSection.productQuantity}}" stepKey="fillQuantity"/> - <click selector="{{AdminProductSEOSection.sectionHeader}}" stepKey="openSeoSection"/> - <fillField userInput="$$simpleProduct.custom_attributes[url_key]$$" selector="{{AdminProductSEOSection.urlKeyInput}}" stepKey="fillUrlKey"/> - <click selector="{{AdminProductFormActionSection.saveButton}}" stepKey="saveProduct"/> - <see userInput="The value specified in the URL Key field would generate a URL that already exists" selector="{{AdminProductMessagesSection.errorMessage}}" stepKey="assertErrorMessage"/> - </test> - <test name="AdminCreateProductDuplicateProductTest"> - <annotations> - <features value="Catalog"/> - <stories value="Validation Errors"/> - <title value="No validation errors when trying to duplicate product twice"/> - <description value="No validation errors when trying to duplicate product twice"/> - <severity value="MAJOR"/> - <testCaseId value="MC-5472"/> - <group value="product"/> - </annotations> - <before> - <createData entity="_defaultCategory" stepKey="createCategory"/> - <createData entity="SimpleProduct" stepKey="createSimpleProduct"> - <requiredEntity createDataKey="createCategory"/> - </createData> - </before> - <after> - <!--Delete all products by filtering grid and using mass delete action--> - <actionGroup ref="DeleteAllDuplicateProductUsingProductGridActionGroup" stepKey="deleteAllDuplicateProducts"> - <argument name="product" value="$$createSimpleProduct$$"/> - </actionGroup> - <deleteData createDataKey="createCategory" stepKey="deletePreReqCatalog" /> - <actionGroup ref="logout" stepKey="logout"/> - </after> - <actionGroup ref="LoginAsAdmin" stepKey="loginAsAdmin"/> - <actionGroup ref="SearchForProductOnBackendActionGroup" stepKey="searchForSimpleProduct1"> - <argument name="product" value="$$createSimpleProduct$$"/> - </actionGroup> - <actionGroup ref="OpenEditProductOnBackendActionGroup" stepKey="openEditProduct1"> - <argument name="product" value="$$createSimpleProduct$$"/> - </actionGroup> - <!--Save and duplicated the product once--> - <actionGroup ref="AdminFormSaveAndDuplicate" stepKey="saveAndDuplicateProductForm1"/> - <actionGroup ref="SearchForProductOnBackendActionGroup" stepKey="searchForSimpleProduct2"> - <argument name="product" value="$$createSimpleProduct$$"/> - </actionGroup> - <actionGroup ref="OpenEditProductOnBackendActionGroup" stepKey="openEditProduct2"> - <argument name="product" value="$$createSimpleProduct$$"/> - </actionGroup> - <!--Save and duplicated the product second time--> - <actionGroup ref="AdminFormSaveAndDuplicate" stepKey="saveAndDuplicateProductForm2"/> - </test> -</tests> diff --git a/app/code/Magento/Catalog/Test/Mftf/Test/AdminCreateProductDuplicateUrlkeyTest/AdminCreateProductDuplicateProductTest.xml b/app/code/Magento/Catalog/Test/Mftf/Test/AdminCreateProductDuplicateUrlkeyTest/AdminCreateProductDuplicateProductTest.xml new file mode 100644 index 0000000000000..91ea74f1089ca --- /dev/null +++ b/app/code/Magento/Catalog/Test/Mftf/Test/AdminCreateProductDuplicateUrlkeyTest/AdminCreateProductDuplicateProductTest.xml @@ -0,0 +1,52 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + /** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +--> +<tests xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/testSchema.xsd"> + <test name="AdminCreateProductDuplicateProductTest"> + <annotations> + <features value="Catalog"/> + <stories value="Validation Errors"/> + <title value="No validation errors when trying to duplicate product twice"/> + <description value="No validation errors when trying to duplicate product twice"/> + <severity value="MAJOR"/> + <testCaseId value="MC-5472"/> + <group value="product"/> + </annotations> + <before> + <createData entity="_defaultCategory" stepKey="createCategory"/> + <createData entity="SimpleProduct" stepKey="createSimpleProduct"> + <requiredEntity createDataKey="createCategory"/> + </createData> + </before> + <after> + <!--Delete all products by filtering grid and using mass delete action--> + <actionGroup ref="DeleteAllDuplicateProductUsingProductGridActionGroup" stepKey="deleteAllDuplicateProducts"> + <argument name="product" value="$$createSimpleProduct$$"/> + </actionGroup> + <deleteData createDataKey="createCategory" stepKey="deletePreReqCatalog"/> + <actionGroup ref="AdminLogoutActionGroup" stepKey="logout"/> + </after> + <actionGroup ref="AdminLoginActionGroup" stepKey="loginAsAdmin"/> + <actionGroup ref="SearchForProductOnBackendActionGroup" stepKey="searchForSimpleProduct1"> + <argument name="product" value="$$createSimpleProduct$$"/> + </actionGroup> + <actionGroup ref="OpenEditProductOnBackendActionGroup" stepKey="openEditProduct1"> + <argument name="product" value="$$createSimpleProduct$$"/> + </actionGroup> + <!--Save and duplicated the product once--> + <actionGroup ref="AdminFormSaveAndDuplicateActionGroup" stepKey="saveAndDuplicateProductForm1"/> + <actionGroup ref="SearchForProductOnBackendActionGroup" stepKey="searchForSimpleProduct2"> + <argument name="product" value="$$createSimpleProduct$$"/> + </actionGroup> + <actionGroup ref="OpenEditProductOnBackendActionGroup" stepKey="openEditProduct2"> + <argument name="product" value="$$createSimpleProduct$$"/> + </actionGroup> + <!--Save and duplicated the product second time--> + <actionGroup ref="AdminFormSaveAndDuplicateActionGroup" stepKey="saveAndDuplicateProductForm2"/> + </test> +</tests> diff --git a/app/code/Magento/Catalog/Test/Mftf/Test/AdminCreateProductDuplicateUrlkeyTest/AdminCreateProductDuplicateUrlkeyTest.xml b/app/code/Magento/Catalog/Test/Mftf/Test/AdminCreateProductDuplicateUrlkeyTest/AdminCreateProductDuplicateUrlkeyTest.xml new file mode 100644 index 0000000000000..c461aa8bfcf18 --- /dev/null +++ b/app/code/Magento/Catalog/Test/Mftf/Test/AdminCreateProductDuplicateUrlkeyTest/AdminCreateProductDuplicateUrlkeyTest.xml @@ -0,0 +1,42 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + /** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +--> +<tests xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/testSchema.xsd"> + <test name="AdminCreateProductDuplicateUrlkeyTest"> + <annotations> + <features value="Catalog"/> + <stories value="Errors"/> + <title value="Admin should see an error when trying to save a product with a duplicate URL key"/> + <description value="Admin should see an error when trying to save a product with a duplicate URL key"/> + <severity value="MAJOR"/> + <testCaseId value="MC-112"/> + <group value="product"/> + </annotations> + <before> + <createData entity="SimpleTwo" stepKey="simpleProduct"> + </createData> + </before> + <after> + <actionGroup ref="AdminLogoutActionGroup" stepKey="adminLogout"/> + <deleteData createDataKey="simpleProduct" stepKey="deleteProduct"/> + </after> + + <actionGroup ref="AdminLoginActionGroup" stepKey="loginAsAdmin"/> + <amOnPage url="{{AdminProductIndexPage.url}}" stepKey="navigateToProductIndex"/> + <click selector="{{AdminProductGridActionSection.addProductToggle}}" stepKey="clickAddProductDropdown"/> + <click selector="{{AdminProductGridActionSection.addSimpleProduct}}" stepKey="clickAddSimpleProduct"/> + <fillField userInput="$$simpleProduct.name$$new" selector="{{AdminProductFormSection.productName}}" stepKey="fillName"/> + <fillField userInput="$$simpleProduct.sku$$new" selector="{{AdminProductFormSection.productSku}}" stepKey="fillSKU"/> + <fillField userInput="$$simpleProduct.price$$" selector="{{AdminProductFormSection.productPrice}}" stepKey="fillPrice"/> + <fillField userInput="$$simpleProduct.quantity$$" selector="{{AdminProductFormSection.productQuantity}}" stepKey="fillQuantity"/> + <click selector="{{AdminProductSEOSection.sectionHeader}}" stepKey="openSeoSection"/> + <fillField userInput="$$simpleProduct.custom_attributes[url_key]$$" selector="{{AdminProductSEOSection.urlKeyInput}}" stepKey="fillUrlKey"/> + <click selector="{{AdminProductFormActionSection.saveButton}}" stepKey="saveProduct"/> + <see userInput="The value specified in the URL Key field would generate a URL that already exists" selector="{{AdminProductMessagesSection.errorMessage}}" stepKey="assertErrorMessage"/> + </test> +</tests> diff --git a/app/code/Magento/Catalog/Test/Mftf/Test/AdminCreateRootCategoryAndSubcategoriesTest.xml b/app/code/Magento/Catalog/Test/Mftf/Test/AdminCreateRootCategoryAndSubcategoriesTest.xml index 7a99750c00e53..b959b961ef1ea 100644 --- a/app/code/Magento/Catalog/Test/Mftf/Test/AdminCreateRootCategoryAndSubcategoriesTest.xml +++ b/app/code/Magento/Catalog/Test/Mftf/Test/AdminCreateRootCategoryAndSubcategoriesTest.xml @@ -20,7 +20,7 @@ </annotations> <!--Delete all created data during the test execution and assign Default Root Category to Store--> <after> - <actionGroup ref="LoginAsAdmin" stepKey="loginAsAdmin2"/> + <actionGroup ref="AdminLoginActionGroup" stepKey="loginAsAdmin2"/> <amOnPage url="{{AdminSystemStorePage.url}}" stepKey="amOnPageAdminSystemStore"/> <waitForPageLoad stepKey="waitForPageAdminSystemStoreLoad" /> <click selector="{{AdminStoresGridSection.resetButton}}" stepKey="clickOnResetButton"/> @@ -36,9 +36,9 @@ <actionGroup ref="DeleteCategoryActionGroup" stepKey="deleteCreatedNewRootCategory"> <argument name="categoryEntity" value="NewRootCategory"/> </actionGroup> - <actionGroup ref="logout" stepKey="logout2"/> + <actionGroup ref="AdminLogoutActionGroup" stepKey="logout2"/> </after> - <actionGroup ref="LoginAsAdmin" stepKey="loginAsAdmin1"/> + <actionGroup ref="AdminLoginActionGroup" stepKey="loginAsAdmin1"/> <amOnPage url="{{AdminCategoryPage.url}}" stepKey="amOnAdminCategoryPage"/> <scrollToTopOfPage stepKey="scrollToTopOfPage1"/> <waitForPageLoad stepKey="waitForPageLoad1"/> @@ -70,10 +70,9 @@ <selectOption userInput="{{NewRootCategory.name}}" selector="{{AdminNewStoreGroupSection.storeRootCategoryDropdown}}" stepKey="selectOptionCreatedNewRootCategory"/> <click selector="{{AdminStoreGroupActionsSection.saveButton}}" stepKey="clickSaveStoreButton"/> <click selector="{{AdminConfirmationModalSection.ok}}" stepKey="clickOkOnModalDialog1"/> - <actionGroup ref="logout" stepKey="logout1"/> + <actionGroup ref="AdminLogoutActionGroup" stepKey="logout1"/> <!--Go to storefront and verify created subcategory on frontend--> - <amOnPage url="{{StorefrontHomePage.url}}" stepKey="goToStorefrontPage"/> - <waitForPageLoad stepKey="waitForPageAdminSystemStoreLoad2"/> + <actionGroup ref="StorefrontOpenHomePageActionGroup" stepKey="goToHomePage"/> <actionGroup ref="CheckCategoryOnStorefrontActionGroup" stepKey="checkCreatedSubcategory1OnFrontend"> <argument name="categoryEntity" value="SimpleSubCategory"/> </actionGroup> diff --git a/app/code/Magento/Catalog/Test/Mftf/Test/AdminCreateRootCategoryRequiredFieldsTest.xml b/app/code/Magento/Catalog/Test/Mftf/Test/AdminCreateRootCategoryRequiredFieldsTest.xml index 2b824554b9bd4..624f342741502 100644 --- a/app/code/Magento/Catalog/Test/Mftf/Test/AdminCreateRootCategoryRequiredFieldsTest.xml +++ b/app/code/Magento/Catalog/Test/Mftf/Test/AdminCreateRootCategoryRequiredFieldsTest.xml @@ -20,13 +20,13 @@ <group value="mtf_migrated"/> </annotations> <before> - <actionGroup ref = "LoginAsAdmin" stepKey="LoginToAdminPanel"/> + <actionGroup ref="AdminLoginActionGroup" stepKey="LoginToAdminPanel"/> </before> <after> <actionGroup ref="DeleteCategoryActionGroup" stepKey="deleteCategory"> <argument name="categoryEntity" value="_defaultCategory" /> </actionGroup> - <actionGroup ref="logout" stepKey="logout" /> + <actionGroup ref="AdminLogoutActionGroup" stepKey="logout" /> </after> <amOnPage url="{{AdminCategoryPage.url}}" stepKey="OpenAdminCatergoryIndexPage"/> diff --git a/app/code/Magento/Catalog/Test/Mftf/Test/AdminCreateSimpleProductNotVisibleIndividuallyTest.xml b/app/code/Magento/Catalog/Test/Mftf/Test/AdminCreateSimpleProductNotVisibleIndividuallyTest.xml new file mode 100644 index 0000000000000..12cd5454ea8e2 --- /dev/null +++ b/app/code/Magento/Catalog/Test/Mftf/Test/AdminCreateSimpleProductNotVisibleIndividuallyTest.xml @@ -0,0 +1,46 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + /** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +--> + +<tests xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/testSchema.xsd"> + <test name="AdminCreateSimpleProductNotVisibleIndividuallyTest"> + <annotations> + <features value="Catalog"/> + <stories value="Create a Simple Product via Admin"/> + <title value="Create Simple Product that Not Visible Individually"/> + <description value="Create Simple Product that Not Visible Individually"/> + <severity value="CRITICAL"/> + <testCaseId value=""/> + <group value="mtf_migrated"/> + </annotations> + <before> + <actionGroup ref="AdminLoginActionGroup" stepKey="loginToAdminPanel"/> + <createData entity="_defaultCategory" stepKey="createPreReqCategory"/> + </before> + <after> + <amOnPage url="admin/admin/auth/logout/" stepKey="amOnLogoutPage"/> + <deleteData createDataKey="createPreReqCategory" stepKey="deletePreReqCategory"/> + </after> + + <!--Create Simple Product Not Visible Individually--> + <actionGroup ref="FillAdminSimpleProductFormActionGroup" stepKey="fillProductFieldsInAdmin"> + <argument name="category" value="$$createPreReqCategory$$"/> + <argument name="simpleProduct" value="simpleProductNotVisibleIndividually"/> + </actionGroup> + + <!--Search and verify AssertUrlRewriteNotInGrid--> + <actionGroup ref="AdminSearchDeletedUrlRewriteActionGroup" stepKey="searchUrlRewriteForNotVisibleProductInGrid"> + <argument name="requestPath" value="{{simpleProductNotVisibleIndividually.urlKey}}.html"/> + </actionGroup> + + <!--Verify AssertPageByUrlRewriteIsNotFound--> + <actionGroup ref="AssertPageByUrlRewriteIsNotFoundActionGroup" stepKey="amOnPage"> + <argument name="requestPath" value="{{simpleProductNotVisibleIndividually.urlKey}}.html"/> + </actionGroup> + </test> +</tests> diff --git a/app/code/Magento/Catalog/Test/Mftf/Test/AdminCreateSimpleProductTest.xml b/app/code/Magento/Catalog/Test/Mftf/Test/AdminCreateSimpleProductTest.xml deleted file mode 100644 index 052f6b1924e89..0000000000000 --- a/app/code/Magento/Catalog/Test/Mftf/Test/AdminCreateSimpleProductTest.xml +++ /dev/null @@ -1,111 +0,0 @@ -<?xml version="1.0" encoding="UTF-8"?> -<!-- - /** - * Copyright © Magento, Inc. All rights reserved. - * See COPYING.txt for license details. - */ ---> - -<tests xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" - xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/testSchema.xsd"> - <test name="AdminCreateSimpleProductTest"> - <annotations> - <features value="Catalog"/> - <stories value="Create a Simple Product via Admin"/> - <title value="Admin should be able to create a Simple Product"/> - <description value="Admin should be able to create a Simple Product"/> - <severity value="CRITICAL"/> - <testCaseId value="MAGETWO-23414"/> - <group value="product"/> - </annotations> - <before> - <createData entity="_defaultCategory" stepKey="createPreReqCategory"/> - </before> - <after> - <deleteData createDataKey="createPreReqCategory" stepKey="deletePreReqCategory"/> - </after> - - <actionGroup ref="LoginAsAdmin" stepKey="loginAsAdmin1"/> - <actionGroup ref="FillAdminSimpleProductFormActionGroup" stepKey="fillProductFieldsInAdmin"> - <argument name="category" value="$$createPreReqCategory$$"/> - <argument name="simpleProduct" value="_defaultProduct"/> - </actionGroup> - <actionGroup ref="AssertProductInStorefrontCategoryPage" stepKey="assertProductInStorefront1"> - <argument name="category" value="$$createPreReqCategory$$"/> - <argument name="product" value="_defaultProduct"/> - </actionGroup> - <actionGroup ref="AssertProductInStorefrontProductPageActionGroup" stepKey="assertProductInStorefront2"> - <argument name="product" value="_defaultProduct"/> - </actionGroup> - </test> - - <test name="AdminConfigDefaultProductLayoutFromConfigurationSettingTest"> - <annotations> - <features value="Catalog"/> - <stories value="Default layout configuration MAGETWO-88793"/> - <title value="Admin should be able to configure a default layout for Product Page from System Configuration"/> - <description value="Admin should be able to configure a default layout for Product Page from System Configuration"/> - <severity value="CRITICAL"/> - <testCaseId value="MAGETWO-89023"/> - <group value="product"/> - </annotations> - <before> - <actionGroup ref="LoginAsAdmin" stepKey="loginAsAdmin"/> - </before> - <after> - <actionGroup ref="RestoreLayoutSetting" stepKey="sampleActionGroup"/> - <actionGroup ref="logout" stepKey="logout"/> - </after> - <amOnPage url="{{WebConfigurationPage.url}}" stepKey="navigateToWebConfigurationPage"/> - <waitForPageLoad stepKey="waitForPageLoad"/> - <conditionalClick stepKey="expandDefaultLayouts" selector="{{WebSection.DefaultLayoutsTab}}" dependentSelector="{{WebSection.CheckIfTabExpand}}" visible="true" /> - <waitForElementVisible selector="{{DefaultLayoutsSection.productLayout}}" stepKey="DefaultProductLayout" /> - <selectOption selector="{{DefaultLayoutsSection.productLayout}}" userInput="3 columns" stepKey="select3ColumnsLayout"/> - <click selector="{{ContentManagementSection.Save}}" stepKey="clickSaveConfig" /> - <amOnPage url="{{AdminProductCreatePage.url(AddToDefaultSet.attributeSetId, 'simple')}}" stepKey="navigateToNewProduct"/> - <waitForPageLoad stepKey="wait1"/> - <click selector="{{ProductDesignSection.DesignTab}}" stepKey="clickOnDesignTab"/> - <waitForElementVisible selector="{{ProductDesignSection.LayoutDropdown}}" stepKey="waitForLayoutDropDown" /> - <seeOptionIsSelected selector="{{ProductDesignSection.LayoutDropdown}}" userInput="3 columns" stepKey="see3ColumnsSelected" /> - </test> - - <test name="AdminCreateSimpleProductZeroPriceTest"> - <annotations> - <features value="Catalog"/> - <stories value="Create a Simple Product via Admin"/> - <title value="Admin should be able to create a product with zero price"/> - <description value="Admin should be able to create a product with zero price"/> - <severity value="CRITICAL"/> - <testCaseId value="MAGETWO-89910"/> - <group value="product"/> - </annotations> - <actionGroup ref="LoginAsAdmin" stepKey="loginAsAdmin"/> - <amOnPage url="{{AdminProductCreatePage.url(SimpleProduct.visibility, SimpleProduct.type_id)}}" stepKey="goToCreateProduct"/> - <waitForPageLoad stepKey="wait1"/> - <fillField selector="{{AdminProductFormSection.productName}}" userInput="{{SimpleProduct.name}}" stepKey="fillName"/> - <fillField selector="{{AdminProductFormSection.productPrice}}" userInput="0" stepKey="fillPrice"/> - <click selector="{{AdminProductFormActionSection.saveButton}}" stepKey="clickSave"/> - <amOnPage url="{{StorefrontProductPage.url(SimpleProduct.name)}}" stepKey="viewProduct"/> - <waitForPageLoad stepKey="wait2"/> - <see selector="{{StorefrontProductInfoMainSection.productPrice}}" userInput="$0.00" stepKey="seeZeroPrice"/> - </test> - - <test name="AdminCreateSimpleProductNegativePriceTest"> - <annotations> - <features value="Catalog"/> - <stories value="Create a Simple Product via Admin"/> - <title value="Admin should not be able to create a product with a negative price"/> - <description value="Admin should not be able to create a product with a negative price"/> - <severity value="CRITICAL"/> - <testCaseId value="MAGETWO-89912"/> - <group value="product"/> - </annotations> - <actionGroup ref="LoginAsAdmin" stepKey="loginAsAdmin"/> - <amOnPage url="{{AdminProductCreatePage.url(SimpleProduct.visibility, SimpleProduct.type_id)}}" stepKey="goToCreateProduct"/> - <waitForPageLoad stepKey="wait1"/> - <fillField selector="{{AdminProductFormSection.productName}}" userInput="{{SimpleProduct.name}}" stepKey="fillName"/> - <fillField selector="{{AdminProductFormSection.productPrice}}" userInput="-42" stepKey="fillPrice"/> - <click selector="{{AdminProductFormActionSection.saveButton}}" stepKey="clickSave"/> - <see selector="{{AdminProductFormSection.priceFieldError}}" userInput="Please enter a number 0 or greater in this field." stepKey="seePriceValidationError"/> - </test> -</tests> diff --git a/app/code/Magento/Catalog/Test/Mftf/Test/AdminCreateSimpleProductTest/AdminConfigDefaultProductLayoutFromConfigurationSettingTest.xml b/app/code/Magento/Catalog/Test/Mftf/Test/AdminCreateSimpleProductTest/AdminConfigDefaultProductLayoutFromConfigurationSettingTest.xml new file mode 100644 index 0000000000000..7317f2f7214f0 --- /dev/null +++ b/app/code/Magento/Catalog/Test/Mftf/Test/AdminCreateSimpleProductTest/AdminConfigDefaultProductLayoutFromConfigurationSettingTest.xml @@ -0,0 +1,39 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + /** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +--> +<tests xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/testSchema.xsd"> + <test name="AdminConfigDefaultProductLayoutFromConfigurationSettingTest"> + <annotations> + <features value="Catalog"/> + <stories value="Default layout configuration MAGETWO-88793"/> + <title value="Admin should be able to configure a default layout for Product Page from System Configuration"/> + <description value="Admin should be able to configure a default layout for Product Page from System Configuration"/> + <severity value="BLOCKER"/> + <testCaseId value="MAGETWO-89023"/> + <group value="product"/> + </annotations> + <before> + <actionGroup ref="AdminLoginActionGroup" stepKey="loginAsAdmin"/> + </before> + <after> + <actionGroup ref="RestoreLayoutSetting" stepKey="sampleActionGroup"/> + <actionGroup ref="AdminLogoutActionGroup" stepKey="logout"/> + </after> + <amOnPage url="{{WebConfigurationPage.url}}" stepKey="navigateToWebConfigurationPage"/> + <waitForPageLoad stepKey="waitForPageLoad"/> + <conditionalClick stepKey="expandDefaultLayouts" selector="{{WebSection.DefaultLayoutsTab}}" dependentSelector="{{WebSection.CheckIfTabExpand}}" visible="true"/> + <waitForElementVisible selector="{{DefaultLayoutsSection.productLayout}}" stepKey="DefaultProductLayout"/> + <selectOption selector="{{DefaultLayoutsSection.productLayout}}" userInput="3 columns" stepKey="select3ColumnsLayout"/> + <click selector="{{ContentManagementSection.Save}}" stepKey="clickSaveConfig"/> + <amOnPage url="{{AdminProductCreatePage.url(AddToDefaultSet.attributeSetId, 'simple')}}" stepKey="navigateToNewProduct"/> + <waitForPageLoad stepKey="wait1"/> + <click selector="{{ProductDesignSection.DesignTab}}" stepKey="clickOnDesignTab"/> + <waitForElementVisible selector="{{ProductDesignSection.LayoutDropdown}}" stepKey="waitForLayoutDropDown"/> + <seeOptionIsSelected selector="{{ProductDesignSection.LayoutDropdown}}" userInput="3 columns" stepKey="see3ColumnsSelected"/> + </test> +</tests> diff --git a/app/code/Magento/Catalog/Test/Mftf/Test/AdminCreateSimpleProductTest/AdminCreateSimpleProductNegativePriceTest.xml b/app/code/Magento/Catalog/Test/Mftf/Test/AdminCreateSimpleProductTest/AdminCreateSimpleProductNegativePriceTest.xml new file mode 100644 index 0000000000000..a8cc66243d73e --- /dev/null +++ b/app/code/Magento/Catalog/Test/Mftf/Test/AdminCreateSimpleProductTest/AdminCreateSimpleProductNegativePriceTest.xml @@ -0,0 +1,28 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + /** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +--> +<tests xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/testSchema.xsd"> + <test name="AdminCreateSimpleProductNegativePriceTest"> + <annotations> + <features value="Catalog"/> + <stories value="Create a Simple Product via Admin"/> + <title value="Admin should not be able to create a product with a negative price"/> + <description value="Admin should not be able to create a product with a negative price"/> + <severity value="BLOCKER"/> + <testCaseId value="MAGETWO-89912"/> + <group value="product"/> + </annotations> + <actionGroup ref="AdminLoginActionGroup" stepKey="loginAsAdmin"/> + <amOnPage url="{{AdminProductCreatePage.url(SimpleProduct.visibility, SimpleProduct.type_id)}}" stepKey="goToCreateProduct"/> + <waitForPageLoad stepKey="wait1"/> + <fillField selector="{{AdminProductFormSection.productName}}" userInput="{{SimpleProduct.name}}" stepKey="fillName"/> + <fillField selector="{{AdminProductFormSection.productPrice}}" userInput="-42" stepKey="fillPrice"/> + <click selector="{{AdminProductFormActionSection.saveButton}}" stepKey="clickSave"/> + <see selector="{{AdminProductFormSection.priceFieldError}}" userInput="Please enter a number 0 or greater in this field." stepKey="seePriceValidationError"/> + </test> +</tests> diff --git a/app/code/Magento/Catalog/Test/Mftf/Test/AdminCreateSimpleProductTest/AdminCreateSimpleProductTest.xml b/app/code/Magento/Catalog/Test/Mftf/Test/AdminCreateSimpleProductTest/AdminCreateSimpleProductTest.xml new file mode 100644 index 0000000000000..3e5ccfd8bf3b9 --- /dev/null +++ b/app/code/Magento/Catalog/Test/Mftf/Test/AdminCreateSimpleProductTest/AdminCreateSimpleProductTest.xml @@ -0,0 +1,41 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + /** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +--> +<tests xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/testSchema.xsd"> + <test name="AdminCreateSimpleProductTest"> + <annotations> + <features value="Catalog"/> + <stories value="Create a Simple Product via Admin"/> + <title value="Admin should be able to create a Simple Product"/> + <description value="Admin should be able to create a Simple Product"/> + <severity value="BLOCKER"/> + <testCaseId value="MAGETWO-23414"/> + <group value="product"/> + </annotations> + <before> + <createData entity="_defaultCategory" stepKey="createPreReqCategory"/> + </before> + <after> + <deleteData createDataKey="createPreReqCategory" stepKey="deletePreReqCategory"/> + </after> + + <actionGroup ref="AdminLoginActionGroup" stepKey="loginAsAdmin1"/> + <actionGroup ref="FillAdminSimpleProductFormActionGroup" stepKey="fillProductFieldsInAdmin"> + <argument name="category" value="$$createPreReqCategory$$"/> + <argument name="simpleProduct" value="_defaultProduct"/> + </actionGroup> + <magentoCLI stepKey="runCronIndex" command="cron:run --group=index"/> + <actionGroup ref="AssertProductInStorefrontCategoryPage" stepKey="assertProductInStorefront1"> + <argument name="category" value="$$createPreReqCategory$$"/> + <argument name="product" value="_defaultProduct"/> + </actionGroup> + <actionGroup ref="AssertProductInStorefrontProductPageActionGroup" stepKey="assertProductInStorefront2"> + <argument name="product" value="_defaultProduct"/> + </actionGroup> + </test> +</tests> diff --git a/app/code/Magento/Catalog/Test/Mftf/Test/AdminCreateSimpleProductTest/AdminCreateSimpleProductZeroPriceTest.xml b/app/code/Magento/Catalog/Test/Mftf/Test/AdminCreateSimpleProductTest/AdminCreateSimpleProductZeroPriceTest.xml new file mode 100644 index 0000000000000..f4878f2948e9d --- /dev/null +++ b/app/code/Magento/Catalog/Test/Mftf/Test/AdminCreateSimpleProductTest/AdminCreateSimpleProductZeroPriceTest.xml @@ -0,0 +1,30 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + /** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +--> +<tests xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/testSchema.xsd"> + <test name="AdminCreateSimpleProductZeroPriceTest"> + <annotations> + <features value="Catalog"/> + <stories value="Create a Simple Product via Admin"/> + <title value="Admin should be able to create a product with zero price"/> + <description value="Admin should be able to create a product with zero price"/> + <severity value="BLOCKER"/> + <testCaseId value="MAGETWO-89910"/> + <group value="product"/> + </annotations> + <actionGroup ref="AdminLoginActionGroup" stepKey="loginAsAdmin"/> + <amOnPage url="{{AdminProductCreatePage.url(SimpleProduct.visibility, SimpleProduct.type_id)}}" stepKey="goToCreateProduct"/> + <waitForPageLoad stepKey="wait1"/> + <fillField selector="{{AdminProductFormSection.productName}}" userInput="{{SimpleProduct.name}}" stepKey="fillName"/> + <fillField selector="{{AdminProductFormSection.productPrice}}" userInput="0" stepKey="fillPrice"/> + <click selector="{{AdminProductFormActionSection.saveButton}}" stepKey="clickSave"/> + <amOnPage url="{{StorefrontProductPage.url(SimpleProduct.name)}}" stepKey="viewProduct"/> + <waitForPageLoad stepKey="wait2"/> + <see selector="{{StorefrontProductInfoMainSection.productPrice}}" userInput="$0.00" stepKey="seeZeroPrice"/> + </test> +</tests> diff --git a/app/code/Magento/Catalog/Test/Mftf/Test/AdminCreateSimpleProductWithCountryOfManufactureAttributeSKUMaskTest.xml b/app/code/Magento/Catalog/Test/Mftf/Test/AdminCreateSimpleProductWithCountryOfManufactureAttributeSKUMaskTest.xml index bbaabffcc5ecd..c378ca5b2c27a 100644 --- a/app/code/Magento/Catalog/Test/Mftf/Test/AdminCreateSimpleProductWithCountryOfManufactureAttributeSKUMaskTest.xml +++ b/app/code/Magento/Catalog/Test/Mftf/Test/AdminCreateSimpleProductWithCountryOfManufactureAttributeSKUMaskTest.xml @@ -21,14 +21,14 @@ <before> <magentoCLI stepKey="setCountryOfManufacture" command="config:set catalog/fields_masks/sku" arguments="{{name}}-{{country_of_manufacture}}"/> - <actionGroup ref="LoginAsAdmin" stepKey="loginAsAdmin"/> + <actionGroup ref="AdminLoginActionGroup" stepKey="loginAsAdmin"/> </before> <after> <magentoCLI stepKey="setName" command="config:set catalog/fields_masks/sku" arguments="{{name}}"/> <actionGroup ref="DeleteProductBySkuActionGroup" stepKey="deleteCreatedProduct"> <argument name="sku" value="{{nameAndAttributeSkuMaskSimpleProduct.name}}-{{nameAndAttributeSkuMaskSimpleProduct.country_of_manufacture_label}}" /> </actionGroup> - <actionGroup ref="logout" stepKey="logout"/> + <actionGroup ref="AdminLogoutActionGroup" stepKey="logout"/> </after> <amOnPage url="{{ProductCatalogPage.url}}" stepKey="openProductCatalogPage"/> diff --git a/app/code/Magento/Catalog/Test/Mftf/Test/AdminCreateSimpleProductWithDatetimeAttributeTest.xml b/app/code/Magento/Catalog/Test/Mftf/Test/AdminCreateSimpleProductWithDatetimeAttributeTest.xml index 0f88fa9d6abf4..2141f44113057 100644 --- a/app/code/Magento/Catalog/Test/Mftf/Test/AdminCreateSimpleProductWithDatetimeAttributeTest.xml +++ b/app/code/Magento/Catalog/Test/Mftf/Test/AdminCreateSimpleProductWithDatetimeAttributeTest.xml @@ -19,15 +19,16 @@ </annotations> <before> - <actionGroup ref="LoginAsAdmin" stepKey="loginAsAdmin"/> + <actionGroup ref="AdminLoginActionGroup" stepKey="loginAsAdmin"/> </before> + <after> <deleteData createDataKey="createDatetimeAttribute" stepKey="deleteDatetimeAttribute"/> <actionGroup ref="DeleteProductBySkuActionGroup" stepKey="deleteCreatedProduct"> <argument name="sku" value="{{_defaultProduct.sku}}"/> </actionGroup> <actionGroup ref="ClearFiltersAdminDataGridActionGroup" stepKey="clearFiltersOnProductIndexPage"/> - <actionGroup ref="logout" stepKey="logout"/> + <actionGroup ref="AdminLogoutActionGroup" stepKey="logout"/> </after> <!-- Generate default value --> @@ -45,13 +46,15 @@ <actionGroup ref="AddProductAttributeInProductModalActionGroup" stepKey="addDatetimeAttribute"> <argument name="attributeCode" value="$createDatetimeAttribute.attribute_code$"/> </actionGroup> - <!-- Flush config cache to reset product attributes in attribute set --> - <magentoCLI command="cache:flush" arguments="config" stepKey="flushConfigCache"/> <!-- Save the product --> <actionGroup ref="SaveProductFormActionGroup" stepKey="saveProduct"/> + <!-- Flush config cache to reset product attributes in attribute set --> + <magentoCLI command="cache:flush" arguments="config" stepKey="flushConfigCache"/> + <reloadPage stepKey="reloadProductEditPage"/> <!-- Check default value --> - <scrollTo selector="{{AdminProductAttributesSection.sectionHeader}}" stepKey="goToAttributesSection"/> - <click selector="{{AdminProductAttributesSection.sectionHeader}}" stepKey="openAttributesSection"/> + <waitForElementVisible selector="{{AdminProductAttributesSection.sectionHeader}}" stepKey="waitAttributesSectionAppears"/> + <conditionalClick selector="{{AdminProductAttributesSection.sectionHeader}}" dependentSelector="{{AdminProductAttributesSection.attributeTextInputByCode($createDatetimeAttribute.attribute_code$)}}" visible="false" stepKey="openAttributesSection"/> + <scrollTo selector="{{AdminProductAttributesSection.sectionHeader}}" stepKey="scrollToAttributesSection"/> <waitForElementVisible selector="{{AdminProductAttributesSection.attributeTextInputByCode($createDatetimeAttribute.attribute_code$)}}" stepKey="waitForSlideOutAttributes"/> <seeInField selector="{{AdminProductAttributesSection.attributeTextInputByCode($createDatetimeAttribute.attribute_code$)}}" userInput="$generateDefaultValue" stepKey="checkDefaultValue"/> <!-- Check datetime grid filter --> diff --git a/app/code/Magento/Catalog/Test/Mftf/Test/AdminCreateSimpleProductWithUnicodeTest.xml b/app/code/Magento/Catalog/Test/Mftf/Test/AdminCreateSimpleProductWithUnicodeTest.xml index 5dcc23a725b84..8de84867241a8 100644 --- a/app/code/Magento/Catalog/Test/Mftf/Test/AdminCreateSimpleProductWithUnicodeTest.xml +++ b/app/code/Magento/Catalog/Test/Mftf/Test/AdminCreateSimpleProductWithUnicodeTest.xml @@ -22,11 +22,11 @@ <createData entity="_defaultCategory" stepKey="createPreReqCategory"/> </before> <after> - <actionGroup ref="logout" stepKey="adminLogout"/> + <actionGroup ref="AdminLogoutActionGroup" stepKey="adminLogout"/> <deleteData createDataKey="createPreReqCategory" stepKey="deletePreReqCategory"/> </after> - <actionGroup ref="LoginAsAdmin" stepKey="loginAsAdmin1"/> + <actionGroup ref="AdminLoginActionGroup" stepKey="loginAsAdmin1"/> <actionGroup ref="FillAdminSimpleProductFormActionGroup" stepKey="fillProductFieldsInAdmin"> <argument name="category" value="$$createPreReqCategory$$"/> <argument name="simpleProduct" value="ProductWithUnicode"/> diff --git a/app/code/Magento/Catalog/Test/Mftf/Test/AdminCreateTextEditorProductAttributeTest.xml b/app/code/Magento/Catalog/Test/Mftf/Test/AdminCreateTextEditorProductAttributeTest.xml index 848e765d34d70..fd6db45b1716b 100644 --- a/app/code/Magento/Catalog/Test/Mftf/Test/AdminCreateTextEditorProductAttributeTest.xml +++ b/app/code/Magento/Catalog/Test/Mftf/Test/AdminCreateTextEditorProductAttributeTest.xml @@ -26,7 +26,7 @@ <magentoCLI command="config:set {{EnableTinyMCE4.path}} {{EnableTinyMCE4.value}}" stepKey="enableTinyMCE4"/> <!-- Login as admin --> - <actionGroup ref="LoginAsAdmin" stepKey="loginAsAdmin"/> + <actionGroup ref="AdminLoginActionGroup" stepKey="loginAsAdmin"/> </before> <after> <!-- Delete attribute --> @@ -40,7 +40,7 @@ </actionGroup> <!-- Log out --> - <actionGroup ref="logout" stepKey="logout"/> + <actionGroup ref="AdminLogoutActionGroup" stepKey="logout"/> </after> <!-- Go to Stores > Product, click "Add New Attribute" --> diff --git a/app/code/Magento/Catalog/Test/Mftf/Test/AdminCreateVirtualProductFillingRequiredFieldsOnlyTest.xml b/app/code/Magento/Catalog/Test/Mftf/Test/AdminCreateVirtualProductFillingRequiredFieldsOnlyTest.xml index c3fe666c84fd4..9d3a47cd115aa 100644 --- a/app/code/Magento/Catalog/Test/Mftf/Test/AdminCreateVirtualProductFillingRequiredFieldsOnlyTest.xml +++ b/app/code/Magento/Catalog/Test/Mftf/Test/AdminCreateVirtualProductFillingRequiredFieldsOnlyTest.xml @@ -19,10 +19,10 @@ <group value="mtf_migrated"/> </annotations> <before> - <actionGroup ref="LoginAsAdmin" stepKey="loginAsAdmin"/> + <actionGroup ref="AdminLoginActionGroup" stepKey="loginAsAdmin"/> </before> <after> - <actionGroup ref="logout" stepKey="logout"/> + <actionGroup ref="AdminLogoutActionGroup" stepKey="logout"/> </after> <amOnPage url="{{ProductCatalogPage.url}}" stepKey="OpenProductCatalogPage"/> diff --git a/app/code/Magento/Catalog/Test/Mftf/Test/AdminCreateVirtualProductOutOfStockWithTierPriceTest.xml b/app/code/Magento/Catalog/Test/Mftf/Test/AdminCreateVirtualProductOutOfStockWithTierPriceTest.xml index 9db9f64396826..842f93b49c14a 100644 --- a/app/code/Magento/Catalog/Test/Mftf/Test/AdminCreateVirtualProductOutOfStockWithTierPriceTest.xml +++ b/app/code/Magento/Catalog/Test/Mftf/Test/AdminCreateVirtualProductOutOfStockWithTierPriceTest.xml @@ -20,7 +20,7 @@ </annotations> <before> <createData entity="SimpleSubCategory" stepKey="categoryEntity"/> - <actionGroup ref="LoginAsAdmin" stepKey="loginAsAdmin"/> + <actionGroup ref="AdminLoginActionGroup" stepKey="loginAsAdmin"/> </before> <after> <deleteData stepKey="deleteSimpleSubCategory" createDataKey="categoryEntity"/> @@ -28,7 +28,7 @@ <argument name="sku" value="{{virtualProductOutOfStock.sku}}"/> </actionGroup> <actionGroup ref="ClearFiltersAdminDataGridActionGroup" stepKey="clearFilter"/> - <actionGroup ref="logout" stepKey="logout"/> + <actionGroup ref="AdminLogoutActionGroup" stepKey="logout"/> </after> <amOnPage url="{{ProductCatalogPage.url}}" stepKey="OpenProductCatalogPage"/> diff --git a/app/code/Magento/Catalog/Test/Mftf/Test/AdminCreateVirtualProductWithCustomOptionsSuiteAndImportOptionsTest.xml b/app/code/Magento/Catalog/Test/Mftf/Test/AdminCreateVirtualProductWithCustomOptionsSuiteAndImportOptionsTest.xml index fe39bb6ada2bd..8bb3391b5240b 100644 --- a/app/code/Magento/Catalog/Test/Mftf/Test/AdminCreateVirtualProductWithCustomOptionsSuiteAndImportOptionsTest.xml +++ b/app/code/Magento/Catalog/Test/Mftf/Test/AdminCreateVirtualProductWithCustomOptionsSuiteAndImportOptionsTest.xml @@ -20,7 +20,7 @@ </annotations> <before> <createData entity="SimpleSubCategory" stepKey="categoryEntity"/> - <actionGroup ref="LoginAsAdmin" stepKey="loginAsAdmin"/> + <actionGroup ref="AdminLoginActionGroup" stepKey="loginAsAdmin"/> </before> <after> <deleteData stepKey="deleteSimpleSubCategory" createDataKey="categoryEntity"/> @@ -28,7 +28,7 @@ <argument name="sku" value="{{virtualProductCustomImportOptions.sku}}"/> </actionGroup> <actionGroup ref="ClearFiltersAdminDataGridActionGroup" stepKey="resetOrderFilter"/> - <actionGroup ref="logout" stepKey="logout"/> + <actionGroup ref="AdminLogoutActionGroup" stepKey="logout"/> </after> <amOnPage url="{{ProductCatalogPage.url}}" stepKey="OpenProductCatalogPage"/> diff --git a/app/code/Magento/Catalog/Test/Mftf/Test/AdminCreateVirtualProductWithTierPriceForGeneralGroupTest.xml b/app/code/Magento/Catalog/Test/Mftf/Test/AdminCreateVirtualProductWithTierPriceForGeneralGroupTest.xml index 976f714d7b3e1..5076ab2515332 100644 --- a/app/code/Magento/Catalog/Test/Mftf/Test/AdminCreateVirtualProductWithTierPriceForGeneralGroupTest.xml +++ b/app/code/Magento/Catalog/Test/Mftf/Test/AdminCreateVirtualProductWithTierPriceForGeneralGroupTest.xml @@ -10,6 +10,7 @@ xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/testSchema.xsd"> <test name="AdminCreateVirtualProductWithTierPriceForGeneralGroupTest"> <annotations> + <features value="Catalog"/> <stories value="Create virtual product"/> <title value="Create virtual product with tier price for General group"/> <description value="Test log in to Create virtual product and Create virtual product with tier price for General group"/> @@ -19,117 +20,94 @@ <group value="mtf_migrated"/> </annotations> <before> - <actionGroup ref="LoginAsAdmin" stepKey="loginAsAdmin"/> <createData entity="SimpleSubCategory" stepKey="categoryEntity"/> <createData entity="Simple_US_CA_Customer" stepKey="customer" /> + <actionGroup ref="AdminLoginActionGroup" stepKey="loginAsAdmin"/> </before> <after> + <deleteData createDataKey="categoryEntity" stepKey="deleteSimpleSubCategory"/> + <deleteData createDataKey="customer" stepKey="deleteCustomer"/> <actionGroup ref="DeleteProductUsingProductGridActionGroup" stepKey="deleteVirtualProduct"> <argument name="product" value="virtualProductGeneralGroup"/> </actionGroup> - <deleteData stepKey="deleteSimpleSubCategory" createDataKey="categoryEntity"/> - <deleteData stepKey="deleteCustomer" createDataKey="customer"/> - <actionGroup ref="logout" stepKey="logout"/> + <waitForElementVisible selector="{{AdminMessagesSection.success}}" stepKey="waitForSuccessMessageAppears"/> + <see selector="{{AdminMessagesSection.success}}" userInput="A total of 1 record(s) have been deleted." stepKey="seeSuccessMessage"/> + <actionGroup ref="AdminClearFiltersActionGroup" stepKey="clearFiltersAfter"/> + <actionGroup ref="AdminLogoutActionGroup" stepKey="logoutFromAdmin"/> </after> - <amOnPage url="{{ProductCatalogPage.url}}" stepKey="OpenProductCatalogPage"/> - <waitForPageLoad stepKey="waitForProductCatalogPage"/> - <click selector="{{AdminProductGridActionSection.addProductToggle}}" stepKey="clickAddProductToggle"/> - <waitForPageLoad stepKey="waitForProductToggleToSelectProduct"/> - <click selector="{{AdminProductGridActionSection.addVirtualProduct}}" stepKey="clickVirtualProduct"/> - - <!-- Create virtual product with tier price for general group --> - <fillField selector="{{AdminProductFormSection.productName}}" userInput="{{virtualProductGeneralGroup.name}}" stepKey="fillProductName"/> - <fillField selector="{{AdminProductFormSection.productSku}}" userInput="{{virtualProductGeneralGroup.sku}}" stepKey="fillProductSku"/> - <fillField selector="{{AdminProductFormSection.productPrice}}" userInput="{{virtualProductGeneralGroup.price}}" stepKey="fillProductPrice"/> - <click selector="{{AdminProductFormSection.advancedPricingLink}}" stepKey="clickAdvancedPricingLink"/> - <click selector="{{AdminProductFormAdvancedPricingSection.customerGroupPriceAddButton}}" stepKey="clickCustomerGroupPriceAddButton"/> - <selectOption selector="{{AdminProductFormAdvancedPricingSection.productTierPriceWebsiteSelect('0')}}" userInput="{{tierPriceOnGeneralGroup.website}}" stepKey="selectProductTierPriceWebsite"/> - <selectOption selector="{{AdminProductFormAdvancedPricingSection.productTierPriceCustGroupSelect('0')}}" userInput="{{tierPriceOnGeneralGroup.customer_group}}" stepKey="selectProductTierPriceGroup"/> - <scrollTo selector="{{AdminProductFormAdvancedPricingSection.productTierPriceQtyInput('0')}}" x="50" y="0" stepKey="scrollToProductTierPriceQuantityInputTextBox"/> - <fillField selector="{{AdminProductFormAdvancedPricingSection.productTierPriceQtyInput('0')}}" userInput="{{tierPriceOnGeneralGroup.qty}}" stepKey="fillProductTierPriceQuantityInput"/> - <fillField selector="{{AdminProductFormAdvancedPricingSection.productTierPriceFixedPriceInput('0')}}" userInput="{{tierPriceOnGeneralGroup.price}}" stepKey="fillProductTierPriceFixedPrice"/> - <click selector="{{AdminProductFormAdvancedPricingSection.doneButton}}" stepKey="clickDoneButton"/> + <!-- Create virtual product--> + <amOnPage url="{{AdminProductCreatePage.url(AddToDefaultSet.attributeSetId,'virtual')}}" stepKey="openVirtualProductCreatePage"/> + <actionGroup ref="FillMainProductFormNoWeightActionGroup" stepKey="fillNewProductData"> + <argument name="product" value="virtualProductGeneralGroup"/> + </actionGroup> + <actionGroup ref="AdminProductFormOpenAdvancedPricingDialogActionGroup" stepKey="openAdvancedPricingDialog"/> + <actionGroup ref="AdminProductFormAdvancedPricingAddTierPriceActionGroup" stepKey="addTierPrice"> + <argument name="website" value="{{tierPriceOnGeneralGroup.website}}"/> + <argument name="customerGroup" value="{{tierPriceOnGeneralGroup.customer_group}}"/> + <argument name="quantity" value="{{tierPriceOnGeneralGroup.qty}}"/> + <argument name="priceType" value="Fixed"/> + <argument name="amount" value="{{tierPriceOnGeneralGroup.price}}"/> + </actionGroup> + <actionGroup ref="AdminProductFormDoneAdvancedPricingDialogActionGroup" stepKey="doneAdvancedPricingModal"/> <selectOption selector="{{AdminProductFormSection.productTaxClass}}" userInput="{{virtualProductGeneralGroup.productTaxClass}}" stepKey="selectProductTaxClass"/> - <fillField selector="{{AdminProductFormSection.productQuantity}}" userInput="{{virtualProductGeneralGroup.quantity}}" stepKey="fillProductQuantity"/> - <click selector="{{AdminProductFormSection.productStockStatus}}" stepKey="clickProductStockStatus"/> - <click selector="{{AdminProductFormSection.categoriesDropdown}}" stepKey="clickCategoriesDropDown"/> - <fillField selector="{{AdminProductFormSection.searchCategory}}" userInput="$$categoryEntity.name$$" stepKey="fillSearchCategory" /> - <click selector="{{AdminProductFormSection.selectCategory($$categoryEntity.name$$)}}" stepKey="clickOnCategory"/> - <click selector="{{AdminProductFormSection.done}}" stepKey="clickOnDoneAdvancedCategorySelect"/> + <actionGroup ref="SetCategoryByNameActionGroup" stepKey="setNewCategory"> + <argument name="categoryName" value="$categoryEntity.name$"/> + </actionGroup> <selectOption selector="{{AdminProductFormSection.visibility}}" userInput="{{virtualProductGeneralGroup.visibility}}" stepKey="selectVisibility"/> - <click selector="{{AdminProductSEOSection.sectionHeader}}" stepKey="clickAdminProductSEOSectionHeader"/> - <fillField selector="{{AdminProductSEOSection.urlKeyInput}}" userInput="{{virtualProductGeneralGroup.urlKey}}" stepKey="fillUrlKeyInput"/> - <scrollToTopOfPage stepKey="scrollToTopOfAdminProductFormSection"/> - <click selector="{{AdminProductFormSection.save}}" stepKey="clickSaveButton"/> - <waitForPageLoad stepKey="waitForVirtualProductSaved"/> - - <!-- Verify we see success message --> - <see selector="{{AdminProductFormSection.successMessage}}" userInput="You saved the product." stepKey="seeAssertVirtualProductSuccessMessage"/> + <actionGroup ref="SetProductUrlKeyByStringActionGroup" stepKey="updateUrlKey"> + <argument name="urlKey" value="{{virtualProductGeneralGroup.urlKey}}"/> + </actionGroup> + <actionGroup ref="SaveProductFormActionGroup" stepKey="saveProductAndCheckSuccessMessage"/> - <amOnPage url="{{ProductCatalogPage.url}}" stepKey="OpenProductCatalogPage1"/> - <waitForPageLoad stepKey="waitForProductCatalogPage1"/> - <conditionalClick selector="{{AdminProductGridFilterSection.clearAll}}" dependentSelector="{{AdminProductGridFilterSection.clearAll}}" visible="true" stepKey="checkRetailCustomerTaxClass" /> - <fillField selector="{{AdminProductGridFilterSection.keywordSearch}}" userInput="{{virtualProductGeneralGroup.name}}" stepKey="fillVirtualProductName"/> - <click selector="{{AdminProductGridFilterSection.keywordSearchButton}}" stepKey="clickKeywordSearchButton"/> - <waitForPageLoad stepKey="waitForProductSearch"/> - <click selector="{{AdminProductGridFilterSection.nthRow('1')}}" stepKey="clickFirstRowToVerifyCreatedVirtualProduct"/> - <waitForPageLoad stepKey="waitUntilProductIsOpened"/> + <!-- Search created virtual product(from above steps) in the grid --> + <actionGroup ref="SearchForProductOnBackendActionGroup" stepKey="searchForVirtualProduct"> + <argument name="product" value="virtualProductGeneralGroup"/> + </actionGroup> - <!-- Verify we see created virtual product with tier price for general group(from the above step) in the product form page --> - <seeInField selector="{{AdminProductFormSection.productName}}" userInput="{{virtualProductGeneralGroup.name}}" stepKey="seeProductName"/> - <seeInField selector="{{AdminProductFormSection.productSku}}" userInput="{{virtualProductGeneralGroup.sku}}" stepKey="seeProductSku"/> - <seeInField selector="{{AdminProductFormSection.productPrice}}" userInput="{{virtualProductGeneralGroup.price}}" stepKey="seeProductPrice"/> - <click selector="{{AdminProductFormSection.advancedPricingLink}}" stepKey="clickAdvancedPricingLink1"/> - <seeOptionIsSelected selector="{{AdminProductFormAdvancedPricingSection.productTierPriceWebsiteSelect('0')}}" userInput="{{tierPriceOnGeneralGroup.website}}" stepKey="seeProductTierPriceWebsite"/> - <seeOptionIsSelected selector="{{AdminProductFormAdvancedPricingSection.productTierPriceCustGroupSelect('0')}}" userInput="{{tierPriceOnGeneralGroup.customer_group}}" stepKey="seeProductTierPriceGroup"/> - <seeInField selector="{{AdminProductFormAdvancedPricingSection.productTierPriceQtyInput('0')}}" userInput="{{tierPriceOnGeneralGroup.qty}}" stepKey="seeProductTierPriceQuantityInput"/> - <seeInField selector="{{AdminProductFormAdvancedPricingSection.productTierPriceFixedPriceInput('0')}}" userInput="{{tierPriceOnGeneralGroup.price}}" stepKey="seeProductTierPriceFixedPrice"/> - <click selector="{{AdminProductFormAdvancedPricingSection.advancedPricingCloseButton}}" stepKey="clickAdvancedPricingCloseButton"/> + <!-- Verify customer see created virtual product with tier price in the product form page --> + <actionGroup ref="AssertProductInfoOnEditPageActionGroup" stepKey="verifyProductInAdminEditForm"> + <argument name="product" value="virtualProductGeneralGroup"/> + </actionGroup> + <actionGroup ref="AdminProductFormOpenAdvancedPricingDialogActionGroup" stepKey="openAdvancedPricingDialogAgain"/> + <actionGroup ref="AssertAdminProductFormAdvancedPricingCheckTierPriceActionGroup" stepKey="checkTierPrice"> + <argument name="rowNumber" value="0"/> + <argument name="website" value="{{tierPriceOnGeneralGroup.website}}"/> + <argument name="customerGroup" value="{{tierPriceOnGeneralGroup.customer_group}}"/> + <argument name="quantity" value="{{tierPriceOnGeneralGroup.qty}}"/> + <argument name="priceType" value="Fixed"/> + <argument name="amount" value="{{tierPriceOnGeneralGroup.price}}"/> + </actionGroup> + <actionGroup ref="AdminProductFormCloseAdvancedPricingDialogActionGroup" stepKey="closeAdvancedPricingModal"/> <seeInField selector="{{AdminProductFormSection.productTaxClass}}" userInput="{{virtualProductGeneralGroup.productTaxClass}}" stepKey="seeProductTaxClass"/> - <seeInField selector="{{AdminProductFormSection.productQuantity}}" userInput="{{virtualProductGeneralGroup.quantity}}" stepKey="seeProductQuantity"/> - <seeInField selector="{{AdminProductFormSection.productStockStatus}}" userInput="{{virtualProductGeneralGroup.status}}" stepKey="seeProductStockStatus"/> <click selector="{{AdminProductFormSection.categoriesDropdown}}" stepKey="clickCategoriesDropDownToVerify"/> - <grabMultiple selector="{{AdminProductFormSection.selectMultipleCategories}}" stepKey="selectedCategories" /> + <grabMultiple selector="{{AdminProductFormSection.selectMultipleCategories}}" stepKey="selectedCategories"/> <assertEquals stepKey="assertSelectedCategories"> <actualResult type="variable">selectedCategories</actualResult> - <expectedResult type="array">[$$categoryEntity.name$$]</expectedResult> + <expectedResult type="array">[$categoryEntity.name$]</expectedResult> </assertEquals> <click selector="{{AdminProductFormSection.done}}" stepKey="clickOnDoneOnCategorySelect"/> <seeInField selector="{{AdminProductFormSection.visibility}}" userInput="{{virtualProductGeneralGroup.visibility}}" stepKey="seeVisibility"/> - <scrollTo selector="{{AdminProductSEOSection.sectionHeader}}" x="0" y="-80" stepKey="scrollToAdminProductSEOSection1"/> - <click selector="{{AdminProductSEOSection.sectionHeader}}" stepKey="clickAdminProductSEOSection1"/> + <conditionalClick selector="{{AdminProductSEOSection.sectionHeader}}" dependentSelector="{{AdminProductSEOSection.useDefaultUrl}}" visible="false" stepKey="openSearchEngineOptimizationSection"/> + <scrollTo selector="{{AdminProductSEOSection.sectionHeader}}" stepKey="scrollToAdminProductSEOSection"/> <seeInField selector="{{AdminProductSEOSection.urlKeyInput}}" userInput="{{virtualProductGeneralGroup.urlKey}}" stepKey="seeUrlKey"/> - <!--Verify customer see created virtual product on category page --> - <amOnPage url="{{StorefrontCategoryPage.url($$categoryEntity.name$$)}}" stepKey="openCategoryPage"/> - <waitForPageLoad stepKey="waitForCategoryPageToLoad"/> - <see selector="{{StorefrontCategoryMainSection.productLink}}" userInput="{{virtualProductGeneralGroup.name}}" stepKey="seeVirtualProductNameOnCategoryPage"/> + <!--Verify customer see created virtual product link on category page --> + <actionGroup ref="StorefrontNavigateCategoryPageActionGroup" stepKey="openCategoryPageOnFrontend"> + <argument name="category" value="$categoryEntity$"/> + </actionGroup> + <actionGroup ref="AssertStorefrontProductIsPresentOnCategoryPageActionGroup" stepKey="checkProductOnCategoryPage"> + <argument name="productName" value="{{virtualProductGeneralGroup.name}}"/> + </actionGroup> - <!-- Verify customer see created virtual product with tier price for general group(from above step) in storefront page with customer --> + <!--Verify customer see updated virtual product with tier price on product storefront page --> <actionGroup ref="LoginToStorefrontActionGroup" stepKey="loginAsCustomer"> - <argument name="Customer" value="$$customer$$" /> + <argument name="Customer" value="$customer$" /> </actionGroup> - <amOnPage url="{{StorefrontHomePage.url}}" stepKey="goToStorefront"/> - <waitForPageLoad stepKey="waitForStoreFrontProductPageLoad"/> - <fillField selector="{{StorefrontQuickSearchResultsSection.searchTextBox}}" userInput="{{virtualProductGeneralGroup.name}}" stepKey="fillVirtualProductNameInSearchTextBox"/> - <waitForPageLoad stepKey="waitForSearchTextBox"/> - <click selector="{{StorefrontQuickSearchResultsSection.searchTextBoxButton}}" stepKey="clickSearchTextBoxButton"/> - <waitForPageLoad stepKey="waitForSearch"/> - <see selector="{{StorefrontQuickSearchResultsSection.productLink}}" userInput="{{virtualProductGeneralGroup.name}}" stepKey="seeVirtualProductName"/> - <grabTextFrom selector="{{StorefrontQuickSearchResultsSection.asLowAsLabel}}" stepKey="tierPriceTextOnStorefrontPage"/> - - <!-- Verify customer see created virtual product with tier price --> - <assertEquals stepKey="assertTierPriceTextOnCategoryPage"> - <expectedResult type="string">As low as ${{tierPriceOnGeneralGroup.price}}</expectedResult> - <actualResult type="variable">tierPriceTextOnStorefrontPage</actualResult> - </assertEquals> - <click selector="{{StorefrontQuickSearchResultsSection.productLink}}" stepKey="openSearchedProduct"/> - <waitForPageLoad stepKey="waitForProductPageToBeLoaded"/> - <grabTextFrom selector="{{StorefrontProductInfoMainSection.tierPriceText}}" stepKey="tierPriceText"/> - <assertEquals stepKey="assertTierPriceTextOnProductPage"> - <expectedResult type="string">Buy {{tierPriceOnGeneralGroup.qty}} for ${{tierPriceOnGeneralGroup.price}} each and save 20%</expectedResult> - <actualResult type="variable">tierPriceText</actualResult> - </assertEquals> + <actionGroup ref="AssertProductInStorefrontProductPageActionGroup" stepKey="verifyProductOnFrontend"> + <argument name="product" value="virtualProductGeneralGroup"/> + </actionGroup> + <see selector="{{StorefrontProductInfoMainSection.productStockStatus}}" userInput="{{virtualProductGeneralGroup.storefrontStatus}}" stepKey="assertStockAvailableOnProductPage"/> + <see selector="{{StorefrontProductInfoMainSection.tierPriceText}}" userInput="Buy {{tierPriceOnGeneralGroup.qty}} for ${{tierPriceOnGeneralGroup.price}} each and save 20%" stepKey="assertTierPriceTextOnProductPage"/> </test> </tests> diff --git a/app/code/Magento/Catalog/Test/Mftf/Test/AdminCreateVirtualProductWithTierPriceTest.xml b/app/code/Magento/Catalog/Test/Mftf/Test/AdminCreateVirtualProductWithTierPriceTest.xml index 40f26761e7b6d..faae6a371db24 100644 --- a/app/code/Magento/Catalog/Test/Mftf/Test/AdminCreateVirtualProductWithTierPriceTest.xml +++ b/app/code/Magento/Catalog/Test/Mftf/Test/AdminCreateVirtualProductWithTierPriceTest.xml @@ -19,12 +19,12 @@ <group value="mtf_migrated"/> </annotations> <before> - <actionGroup ref="LoginAsAdmin" stepKey="loginAsAdmin"/> + <actionGroup ref="AdminLoginActionGroup" stepKey="loginAsAdmin"/> <createData entity="SimpleSubCategory" stepKey="categoryEntity"/> </before> <after> <deleteData stepKey="deleteSimpleSubCategory" createDataKey="categoryEntity"/> - <actionGroup ref="logout" stepKey="logout"/> + <actionGroup ref="AdminLogoutActionGroup" stepKey="logout"/> </after> <amOnPage url="{{ProductCatalogPage.url}}" stepKey="OpenProductCatalogPage"/> @@ -102,8 +102,7 @@ <magentoCLI command="cache:flush" stepKey="flushCache"/> <!-- Verify customer see created virtual product with tier price(from above step) on storefront page and is searchable by sku --> - <amOnPage url="{{StorefrontHomePage.url}}" stepKey="goToStorefront"/> - <waitForPageLoad stepKey="waitForStoreFrontProductPageLoad"/> + <actionGroup ref="StorefrontOpenHomePageActionGroup" stepKey="goToHomePage"/> <fillField selector="{{StorefrontQuickSearchResultsSection.searchTextBox}}" userInput="{{virtualProductBigQty.sku}}" stepKey="fillVirtualProductName"/> <waitForPageLoad stepKey="waitForSearchTextBox"/> <click selector="{{StorefrontQuickSearchResultsSection.searchTextBoxButton}}" stepKey="clickSearchTextBoxButton"/> diff --git a/app/code/Magento/Catalog/Test/Mftf/Test/AdminCreateVirtualProductWithoutManageStockTest.xml b/app/code/Magento/Catalog/Test/Mftf/Test/AdminCreateVirtualProductWithoutManageStockTest.xml index cb41b0292d33a..3b5a8d8e753da 100644 --- a/app/code/Magento/Catalog/Test/Mftf/Test/AdminCreateVirtualProductWithoutManageStockTest.xml +++ b/app/code/Magento/Catalog/Test/Mftf/Test/AdminCreateVirtualProductWithoutManageStockTest.xml @@ -19,12 +19,12 @@ <group value="mtf_migrated"/> </annotations> <before> - <actionGroup ref="LoginAsAdmin" stepKey="loginAsAdmin"/> + <actionGroup ref="AdminLoginActionGroup" stepKey="loginAsAdmin"/> <createData entity="SimpleSubCategory" stepKey="categoryEntity"/> </before> <after> <deleteData stepKey="deleteSimpleSubCategory" createDataKey="categoryEntity"/> - <actionGroup ref="logout" stepKey="logout"/> + <actionGroup ref="AdminLogoutActionGroup" stepKey="logout"/> </after> <amOnPage url="{{ProductCatalogPage.url}}" stepKey="OpenProductCatalogPage"/> diff --git a/app/code/Magento/Catalog/Test/Mftf/Test/AdminDeleteAttributeSetTest.xml b/app/code/Magento/Catalog/Test/Mftf/Test/AdminDeleteAttributeSetTest.xml index 973ff0381584a..3dfeea2c33af0 100644 --- a/app/code/Magento/Catalog/Test/Mftf/Test/AdminDeleteAttributeSetTest.xml +++ b/app/code/Magento/Catalog/Test/Mftf/Test/AdminDeleteAttributeSetTest.xml @@ -18,7 +18,7 @@ <group value="mtf_migrated"/> </annotations> <before> - <actionGroup ref="LoginAsAdmin" stepKey="loginAsAdmin"/> + <actionGroup ref="AdminLoginActionGroup" stepKey="loginAsAdmin"/> <createData entity="_defaultCategory" stepKey="createCategory"/> <createData entity="CatalogAttributeSet" stepKey="createAttributeSet"/> <createData entity="SimpleProductWithCustomAttributeSet" stepKey="SimpleProductWithCustomAttributeSet"> @@ -28,7 +28,7 @@ </before> <after> <deleteData createDataKey="createCategory" stepKey="deleteCategory"/> - <actionGroup ref="logout" stepKey="logout"/> + <actionGroup ref="AdminLogoutActionGroup" stepKey="logout"/> </after> <amOnPage url="{{AdminProductAttributeSetGridPage.url}}" stepKey="goToAttributeSetsPage"/> <fillField selector="{{AdminProductAttributeSetGridSection.filter}}" userInput="$$createAttributeSet.attribute_set_name$$" stepKey="filterByAttributeName"/> diff --git a/app/code/Magento/Catalog/Test/Mftf/Test/AdminDeleteConfigurableChildProductsTest.xml b/app/code/Magento/Catalog/Test/Mftf/Test/AdminDeleteConfigurableChildProductsTest.xml index 4a305b8dfec75..7748d4bf4db6f 100644 --- a/app/code/Magento/Catalog/Test/Mftf/Test/AdminDeleteConfigurableChildProductsTest.xml +++ b/app/code/Magento/Catalog/Test/Mftf/Test/AdminDeleteConfigurableChildProductsTest.xml @@ -21,7 +21,7 @@ <!--Set Display Out Of Stock Product --> <magentoCLI stepKey="setDisplayOutOfStockProduct" command="config:set cataloginventory/options/show_out_of_stock 0 "/> <!--Login as Admin --> - <actionGroup ref="LoginAsAdmin" stepKey="loginToAdminPanel"/> + <actionGroup ref="AdminLoginActionGroup" stepKey="loginToAdminPanel"/> <!--Create Default Category --> <createData entity="_defaultCategory" stepKey="createCategory"/> <!-- Create an attribute with two options to be used in the first child product --> @@ -81,7 +81,10 @@ <deleteData createDataKey="createCategory" stepKey="deleteCategory"/> <deleteData createDataKey="createConfigProduct" stepKey="deleteConfigProduct"/> <deleteData createDataKey="createConfigProductAttribute" stepKey="deleteAttribute"/> - <actionGroup ref="logout" stepKey="logout"/> + <actionGroup ref="AdminLogoutActionGroup" stepKey="logout"/> + + <!-- Reindex invalidated indices after product attribute has been created/deleted --> + <magentoCron groups="index" stepKey="reindexInvalidatedIndices"/> </after> <!--Open Product in Store Front Page --> <amOnPage url="$$createConfigProduct.sku$$.html" stepKey="openProductInStoreFront"/> diff --git a/app/code/Magento/Catalog/Test/Mftf/Test/AdminDeleteDropdownProductAttributeFromAttributeSetTest.xml b/app/code/Magento/Catalog/Test/Mftf/Test/AdminDeleteDropdownProductAttributeFromAttributeSetTest.xml index 3abe68a503b57..841b08e70fb4f 100644 --- a/app/code/Magento/Catalog/Test/Mftf/Test/AdminDeleteDropdownProductAttributeFromAttributeSetTest.xml +++ b/app/code/Magento/Catalog/Test/Mftf/Test/AdminDeleteDropdownProductAttributeFromAttributeSetTest.xml @@ -13,13 +13,13 @@ <stories value="Delete product attributes"/> <title value="Delete Product Attribute, Dropdown Type, from Attribute Set"/> <description value="Login as admin and delete dropdown type product attribute from attribute set"/> - <severity value="CRITICAL"/> + <severity value="BLOCKER"/> <testCaseId value="MC-10885"/> <group value="mtf_migrated"/> </annotations> <before> <!-- Login as admin --> - <actionGroup ref="LoginAsAdmin" stepKey="login"/> + <actionGroup ref="AdminLoginActionGroup" stepKey="login"/> <!-- Create Dropdown Product Attribute --> <createData entity="productDropDownAttribute" stepKey="attribute"/> <!-- Create Attribute set --> @@ -28,7 +28,10 @@ <after> <!--Delete Created Data --> <deleteData createDataKey="createAttributeSet" stepKey="deleteAttributeSet"/> - <actionGroup ref="logout" stepKey="logout"/> + <actionGroup ref="AdminLogoutActionGroup" stepKey="logout"/> + + <!-- Reindex invalidated indices after product attribute has been created/deleted --> + <magentoCron groups="index" stepKey="reindexInvalidatedIndices"/> </after> <!-- Open Product Attribute Set Page --> <amOnPage url="{{AdminProductAttributeSetGridPage.url}}" stepKey="goToAttributeSets"/> diff --git a/app/code/Magento/Catalog/Test/Mftf/Test/AdminDeleteProductAttributeTest.xml b/app/code/Magento/Catalog/Test/Mftf/Test/AdminDeleteProductAttributeTest.xml index 4060182a9bace..c0cbb44ebc681 100644 --- a/app/code/Magento/Catalog/Test/Mftf/Test/AdminDeleteProductAttributeTest.xml +++ b/app/code/Magento/Catalog/Test/Mftf/Test/AdminDeleteProductAttributeTest.xml @@ -14,15 +14,18 @@ <title value="Delete Product Attribute"/> <description value="Admin should able to delete a product attribute"/> <testCaseId value="MC-10887"/> - <severity value="CRITICAL"/> + <severity value="BLOCKER"/> <group value="mtf_migrated"/> </annotations> <before> - <actionGroup ref="LoginAsAdmin" stepKey="loginAsAdmin"/> + <actionGroup ref="AdminLoginActionGroup" stepKey="loginAsAdmin"/> <createData entity="productAttributeWysiwyg" stepKey="createProductAttribute"/> </before> <after> - <actionGroup ref="logout" stepKey="logout"/> + <actionGroup ref="AdminLogoutActionGroup" stepKey="logout"/> + + <!-- Reindex invalidated indices after product attribute has been created/deleted --> + <magentoCron groups="index" stepKey="reindexInvalidatedIndices"/> </after> <actionGroup ref="OpenProductAttributeFromSearchResultInGridActionGroup" stepKey="openProductAttributeFromSearchResultInGrid"> <argument name="productAttributeCode" value="$$createProductAttribute.attribute_code$$"/> diff --git a/app/code/Magento/Catalog/Test/Mftf/Test/AdminDeleteProductWithCustomOptionTest.xml b/app/code/Magento/Catalog/Test/Mftf/Test/AdminDeleteProductWithCustomOptionTest.xml index dec911ec84a8d..22e1d7d7c5d9e 100644 --- a/app/code/Magento/Catalog/Test/Mftf/Test/AdminDeleteProductWithCustomOptionTest.xml +++ b/app/code/Magento/Catalog/Test/Mftf/Test/AdminDeleteProductWithCustomOptionTest.xml @@ -19,7 +19,7 @@ <group value="mtf_migrated"/> </annotations> <before> - <actionGroup ref="LoginAsAdmin" stepKey="loginAsAdmin"/> + <actionGroup ref="AdminLoginActionGroup" stepKey="loginAsAdmin"/> <createData entity="_defaultCategory" stepKey="createCategory"/> <createData entity="_defaultProduct" stepKey="createSimpleProduct"> <requiredEntity createDataKey="createCategory"/> @@ -28,7 +28,7 @@ </before> <after> <deleteData createDataKey="createCategory" stepKey="deleteCategory"/> - <actionGroup ref="logout" stepKey="logout"/> + <actionGroup ref="AdminLogoutActionGroup" stepKey="logout"/> </after> <actionGroup ref="DeleteProductUsingProductGridActionGroup" stepKey="deleteSimpleProductFilteredBySkuAndName"> <argument name="product" value="$$createSimpleProduct$$"/> diff --git a/app/code/Magento/Catalog/Test/Mftf/Test/AdminDeleteProductsImageInCaseOfMultipleStoresTest.xml b/app/code/Magento/Catalog/Test/Mftf/Test/AdminDeleteProductsImageInCaseOfMultipleStoresTest.xml index 55c98bcc13d34..0b857c6d3bbaf 100644 --- a/app/code/Magento/Catalog/Test/Mftf/Test/AdminDeleteProductsImageInCaseOfMultipleStoresTest.xml +++ b/app/code/Magento/Catalog/Test/Mftf/Test/AdminDeleteProductsImageInCaseOfMultipleStoresTest.xml @@ -21,7 +21,7 @@ </annotations> <before> <!--Login as admin--> - <actionGroup ref="LoginAsAdmin" stepKey="loginAsAdmin"/> + <actionGroup ref="AdminLoginActionGroup" stepKey="loginAsAdmin"/> <!--Create new website, store and store view--> <actionGroup ref="AdminCreateWebsiteActionGroup" stepKey="createWebsite"> <argument name="newWebsiteName" value="{{NewWebSiteData.name}}"/> @@ -40,12 +40,16 @@ <createData entity="SimpleProduct2" stepKey="createProduct"/> <createData entity="SubCategory" stepKey="createSubCategory"/> <createData entity="NewRootCategory" stepKey="createRootCategory"/> - <amOnPage url="{{AdminProductEditPage.url($$createProduct.id$$)}}" stepKey="visitAdminProductPage"/> + <actionGroup ref="AdminProductPageOpenByIdActionGroup" stepKey="visitAdminProductPage"> + <argument name="productId" value="$$createProduct.id$$"/> + </actionGroup> <waitForPageLoad stepKey="waitForProductPageLoad0"/> <searchAndMultiSelectOption selector="{{AdminProductFormSection.categoriesDropdown}}" parameterArray="['Default Category', $$createRootCategory.name$$, $$createSubCategory.name$$]" stepKey="fillCategory"/> <actionGroup ref="SaveProductFormActionGroup" stepKey="saveProduct"/> <!--Add images to the product--> - <amOnPage url="{{AdminProductEditPage.url($$createProduct.id$$)}}" stepKey="visitAdminProductPage2"/> + <actionGroup ref="AdminProductPageOpenByIdActionGroup" stepKey="visitAdminProductPage2"> + <argument name="productId" value="$$createProduct.id$$"/> + </actionGroup> <waitForPageLoad stepKey="waitForProductPageLoad1"/> <actionGroup ref="AddProductImageActionGroup" stepKey="addImageToProduct"> <argument name="image" value="ProductImage"/> @@ -67,7 +71,7 @@ <deleteData createDataKey="createRootCategory" stepKey="deleteRootCategory"/> <deleteData createDataKey="createProduct" stepKey="deleteProduct"/> <createData entity="DefaultWebUrlOptionsConfig" stepKey="defaultWebUrlOptionsConfig"/> - <actionGroup ref="logout" stepKey="logout"/> + <actionGroup ref="AdminLogoutActionGroup" stepKey="logout"/> </after> <!--Grab new store view code--> <amOnPage url="{{AdminSystemStorePage.url}}" stepKey="navigateToNewWebsitePage"/> @@ -80,7 +84,9 @@ <click selector="{{AdminStoresGridSection.resetButton}}" stepKey="clickResetButton"/> <waitForPageLoad stepKey="waitForStorePageLoad"/> <!--Open product page on admin--> - <amOnPage url="{{AdminProductEditPage.url($$createProduct.id$$)}}" stepKey="openProductEditPage"/> + <actionGroup ref="AdminProductPageOpenByIdActionGroup" stepKey="openProductEditPage"> + <argument name="productId" value="$$createProduct.id$$"/> + </actionGroup> <waitForPageLoad stepKey="waitForProductPageLoad2"/> <!--Enable the newly created website and save the product--> <actionGroup ref="SelectProductInWebsitesActionGroup" stepKey="selectWebsiteInProduct2"> @@ -174,13 +180,19 @@ <amOnPage url="{{StorefrontCategoryPage.url($$createSubCategory.name$$)}}" stepKey="navigateToCategoryPage"/> <waitForPageLoad stepKey="waitForCategoryPageLoad0"/> <grabAttributeFrom userInput="src" selector="{{StorefrontCategoryMainSection.mediaDescription($$createProduct.name$$)}}" stepKey="grabAttributeFromImage"/> - <assertContains expectedType="string" expected="{{ProductImage.filename}}" actual="$grabAttributeFromImage" stepKey="assertProductImageAbsence"/> + <assertContains stepKey="assertProductImageAbsence"> + <actualResult type="const">$grabAttributeFromImage</actualResult> + <expectedResult type="string">{{ProductImage.filename}}</expectedResult> + </assertContains> <!--Open Storefront on newly created store view and assert image absence--> <amOnPage url="$grabStoreViewCode" stepKey="navigateToHomePageOfSpecificStore"/> <waitForPageLoad stepKey="waitForHomePageLoad"/> <click selector="{{StorefrontHeaderSection.NavigationCategoryByName($$createSubCategory.name$$)}}" stepKey="clickCategory"/> <waitForPageLoad stepKey="waitForCategoryPageLoad1"/> <grabAttributeFrom userInput="src" selector="{{StorefrontCategoryMainSection.mediaDescription($$createProduct.name$$)}}" stepKey="grabAttributeFromImage2"/> - <assertContains expectedType="string" expected="small_image" actual="$grabAttributeFromImage2" stepKey="assertProductImageAbsence2"/> + <assertContains stepKey="assertProductImageAbsence2"> + <actualResult type="const">$grabAttributeFromImage2</actualResult> + <expectedResult type="string">small_image</expectedResult> + </assertContains> </test> </tests> diff --git a/app/code/Magento/Catalog/Test/Mftf/Test/AdminDeleteRootCategoryAssignedToStoreTest.xml b/app/code/Magento/Catalog/Test/Mftf/Test/AdminDeleteRootCategoryAssignedToStoreTest.xml index e4b269dff96ba..0396ab743c745 100644 --- a/app/code/Magento/Catalog/Test/Mftf/Test/AdminDeleteRootCategoryAssignedToStoreTest.xml +++ b/app/code/Magento/Catalog/Test/Mftf/Test/AdminDeleteRootCategoryAssignedToStoreTest.xml @@ -18,7 +18,7 @@ <group value="mtf_migrated"/> </annotations> <before> - <actionGroup ref="LoginAsAdmin" stepKey="loginToAdminPanel"/> + <actionGroup ref="AdminLoginActionGroup" stepKey="loginToAdminPanel"/> <createData entity="NewRootCategory" stepKey="rootCategory" /> </before> <after> @@ -26,7 +26,7 @@ <argument name="storeGroupName" value="customStore.code"/> </actionGroup> <deleteData createDataKey="rootCategory" stepKey="deleteRootCategory"/> - <actionGroup ref="logout" stepKey="logout"/> + <actionGroup ref="AdminLogoutActionGroup" stepKey="logout"/> </after> <amOnPage url="{{AdminSystemStorePage.url}}" stepKey="amOnAdminSystemStorePage"/> @@ -48,4 +48,4 @@ <!--Verify Delete button is not displayed--> <dontSeeElement selector="{{AdminCategoryMainActionsSection.DeleteButton}}" stepKey="dontSeeDeleteButton"/> </test> -</tests> \ No newline at end of file +</tests> diff --git a/app/code/Magento/Catalog/Test/Mftf/Test/AdminDeleteRootCategoryTest.xml b/app/code/Magento/Catalog/Test/Mftf/Test/AdminDeleteRootCategoryTest.xml index e7ab14c77945a..0a41842d5709c 100644 --- a/app/code/Magento/Catalog/Test/Mftf/Test/AdminDeleteRootCategoryTest.xml +++ b/app/code/Magento/Catalog/Test/Mftf/Test/AdminDeleteRootCategoryTest.xml @@ -13,16 +13,16 @@ <title value="Can delete a root category not assigned to any store"/> <description value="Login as admin and delete a root category not assigned to any store"/> <testCaseId value="MC-6048"/> - <severity value="CRITICAL"/> + <severity value="BLOCKER"/> <group value="Catalog"/> <group value="mtf_migrated"/> </annotations> <before> - <actionGroup ref="LoginAsAdmin" stepKey="loginToAdminPanel"/> + <actionGroup ref="AdminLoginActionGroup" stepKey="loginToAdminPanel"/> <createData entity="NewRootCategory" stepKey="rootCategory" /> </before> <after> - <actionGroup ref="logout" stepKey="logout"/> + <actionGroup ref="AdminLogoutActionGroup" stepKey="logout"/> </after> <!--Verify Created root Category--> @@ -41,4 +41,4 @@ <click selector="{{AdminCategorySidebarTreeSection.expandAll}}" stepKey="expandToSeeAllCategories1"/> <dontSee selector="{{AdminCategoryBasicFieldSection.CategoryNameInput}}" userInput="{{NewRootCategory.name}}" stepKey="dontSeeRootCategory"/> </test> -</tests> \ No newline at end of file +</tests> diff --git a/app/code/Magento/Catalog/Test/Mftf/Test/AdminDeleteRootSubCategoryTest.xml b/app/code/Magento/Catalog/Test/Mftf/Test/AdminDeleteRootSubCategoryTest.xml index 6df571f403ac9..6ae00d213bf77 100644 --- a/app/code/Magento/Catalog/Test/Mftf/Test/AdminDeleteRootSubCategoryTest.xml +++ b/app/code/Magento/Catalog/Test/Mftf/Test/AdminDeleteRootSubCategoryTest.xml @@ -18,7 +18,7 @@ <group value="mtf_migrated"/> </annotations> <before> - <actionGroup ref="LoginAsAdmin" stepKey="loginToAdminPanel"/> + <actionGroup ref="AdminLoginActionGroup" stepKey="loginToAdminPanel"/> <createData entity="NewRootCategory" stepKey="rootCategory" /> <createData entity="SimpleRootSubCategory" stepKey="category"> <requiredEntity createDataKey="rootCategory"/> @@ -29,7 +29,7 @@ <argument name="storeGroupName" value="customStore.code"/> </actionGroup> <deleteData createDataKey="rootCategory" stepKey="deleteRootCategory"/> - <actionGroup ref="logout" stepKey="logout"/> + <actionGroup ref="AdminLogoutActionGroup" stepKey="logout"/> </after> <!--Create a Store--> @@ -83,8 +83,10 @@ <!--Verify in Category is not in Url Rewrite grid--> <amOnPage url="{{AdminUrlRewriteIndexPage.url}}" stepKey="openUrlRewriteIndexPage"/> <waitForPageLoad stepKey="waitForUrlRewritePageTopLoad"/> - <fillField selector="{{AdminUrlRewriteIndexSection.requestPathFilter}}" userInput="{{SimpleRootSubCategory.url_key}}" stepKey="fillRequestPath"/> - <click selector="{{AdminUrlRewriteIndexSection.searchButton}}" stepKey="clickOnSearchButton"/> - <see selector="{{AdminUrlRewriteIndexSection.emptyRecordMessage}}" userInput="We couldn't find any records." stepKey="seeEmptyRow"/> + <click selector="{{AdminDataGridHeaderSection.filters}}" stepKey="openUrlRewriteGridFilters"/> + <fillField selector="{{AdminDataGridHeaderSection.filterFieldInput('request_path')}}" userInput="{{SimpleRootSubCategory.url_key}}" stepKey="fillRequestPath"/> + <click selector="{{AdminDataGridHeaderSection.applyFilters}}" stepKey="clickOrderApplyFilters"/> + <waitForPageLoad stepKey="waitForPageToLoad1"/> + <see selector="{{AdminDataGridTableSection.dataGridEmpty}}" userInput="We couldn't find any records." stepKey="seeEmptyRow"/> </test> -</tests> \ No newline at end of file +</tests> diff --git a/app/code/Magento/Catalog/Test/Mftf/Test/AdminDeleteSimpleProductTest.xml b/app/code/Magento/Catalog/Test/Mftf/Test/AdminDeleteSimpleProductTest.xml index 5b8ac5157514d..390002f5d9498 100644 --- a/app/code/Magento/Catalog/Test/Mftf/Test/AdminDeleteSimpleProductTest.xml +++ b/app/code/Magento/Catalog/Test/Mftf/Test/AdminDeleteSimpleProductTest.xml @@ -14,12 +14,12 @@ <stories value="Delete products"/> <title value="Delete Simple Product"/> <description value="Admin should be able to delete a simple product"/> - <severity value="CRITICAL"/> + <severity value="BLOCKER"/> <testCaseId value="MC-11013"/> <group value="mtf_migrated"/> </annotations> <before> - <actionGroup ref="LoginAsAdmin" stepKey="loginAsAdmin"/> + <actionGroup ref="AdminLoginActionGroup" stepKey="loginAsAdmin"/> <createData entity="_defaultCategory" stepKey="createCategory"/> <createData entity="_defaultProduct" stepKey="createSimpleProduct"> <requiredEntity createDataKey="createCategory"/> @@ -27,7 +27,7 @@ </before> <after> <deleteData createDataKey="createCategory" stepKey="deleteCategory"/> - <actionGroup ref="logout" stepKey="logout"/> + <actionGroup ref="AdminLogoutActionGroup" stepKey="logout"/> </after> <actionGroup ref="DeleteProductUsingProductGridActionGroup" stepKey="deleteSimpleProductFilteredBySkuAndName"> <argument name="product" value="$$createSimpleProduct$$"/> diff --git a/app/code/Magento/Catalog/Test/Mftf/Test/AdminDeleteSystemProductAttributeTest.xml b/app/code/Magento/Catalog/Test/Mftf/Test/AdminDeleteSystemProductAttributeTest.xml index 6de1a5cd359cd..22c6bf061f274 100644 --- a/app/code/Magento/Catalog/Test/Mftf/Test/AdminDeleteSystemProductAttributeTest.xml +++ b/app/code/Magento/Catalog/Test/Mftf/Test/AdminDeleteSystemProductAttributeTest.xml @@ -14,14 +14,14 @@ <title value="Delete System Product Attribute"/> <description value="Admin should not be able to see Delete Attribute button"/> <testCaseId value="MC-10893"/> - <severity value="CRITICAL"/> + <severity value="BLOCKER"/> <group value="mtf_migrated"/> </annotations> <before> - <actionGroup ref="LoginAsAdmin" stepKey="loginAsAdmin"/> + <actionGroup ref="AdminLoginActionGroup" stepKey="loginAsAdmin"/> </before> <after> - <actionGroup ref="logout" stepKey="logout"/> + <actionGroup ref="AdminLogoutActionGroup" stepKey="logout"/> </after> <amOnPage url="{{AdminProductAttributeGridPage.url}}" stepKey="navigateToProductAttribute"/> <waitForPageLoad stepKey="waitForPageLoad"/> diff --git a/app/code/Magento/Catalog/Test/Mftf/Test/AdminDeleteTextFieldProductAttributeFromAttributeSetTest.xml b/app/code/Magento/Catalog/Test/Mftf/Test/AdminDeleteTextFieldProductAttributeFromAttributeSetTest.xml index 4f05c364fda0e..36001bd0b570a 100644 --- a/app/code/Magento/Catalog/Test/Mftf/Test/AdminDeleteTextFieldProductAttributeFromAttributeSetTest.xml +++ b/app/code/Magento/Catalog/Test/Mftf/Test/AdminDeleteTextFieldProductAttributeFromAttributeSetTest.xml @@ -13,13 +13,13 @@ <stories value="Delete product attributes"/> <title value="Delete Product Attribute, Text Field, from Attribute Set"/> <description value="Login as admin and delete Text Field type product attribute from attribute set"/> - <severity value="CRITICAL"/> + <severity value="BLOCKER"/> <testCaseId value="MC-10886"/> <group value="mtf_migrated"/> </annotations> <before> <!-- Login as admin --> - <actionGroup ref="LoginAsAdmin" stepKey="login"/> + <actionGroup ref="AdminLoginActionGroup" stepKey="login"/> <!-- Create Product Attribute and assign to Default Product Attribute Set --> <createData entity="newProductAttribute" stepKey="attribute"/> <createData entity="AddToDefaultSet" stepKey="addToDefaultAttributeSet"> @@ -31,7 +31,10 @@ <after> <!--Delete cteated Data --> <deleteData createDataKey="createSimpleProduct" stepKey="deleteSimplaeProduct"/> - <actionGroup ref="logout" stepKey="logout"/> + <actionGroup ref="AdminLogoutActionGroup" stepKey="logout"/> + + <!-- Reindex invalidated indices after product attribute has been created/deleted --> + <magentoCron groups="index" stepKey="reindexInvalidatedIndices"/> </after> <!-- Open Product Attribute Set Page --> <amOnPage url="{{AdminProductAttributeSetGridPage.url}}" stepKey="goToAttributeSets"/> diff --git a/app/code/Magento/Catalog/Test/Mftf/Test/AdminDeleteVirtualProductTest.xml b/app/code/Magento/Catalog/Test/Mftf/Test/AdminDeleteVirtualProductTest.xml index 86f253f358532..f49e1142315eb 100644 --- a/app/code/Magento/Catalog/Test/Mftf/Test/AdminDeleteVirtualProductTest.xml +++ b/app/code/Magento/Catalog/Test/Mftf/Test/AdminDeleteVirtualProductTest.xml @@ -19,7 +19,7 @@ <group value="mtf_migrated"/> </annotations> <before> - <actionGroup ref="LoginAsAdmin" stepKey="loginAsAdmin"/> + <actionGroup ref="AdminLoginActionGroup" stepKey="loginAsAdmin"/> <createData entity="_defaultCategory" stepKey="createCategory"/> <createData entity="defaultVirtualProduct" stepKey="createVirtualProduct"> <requiredEntity createDataKey="createCategory"/> @@ -27,7 +27,7 @@ </before> <after> <deleteData createDataKey="createCategory" stepKey="deleteCategory"/> - <actionGroup ref="logout" stepKey="logout"/> + <actionGroup ref="AdminLogoutActionGroup" stepKey="logout"/> </after> <actionGroup ref="DeleteProductUsingProductGridActionGroup" stepKey="deleteVirtualProductFilteredBySkuAndName"> diff --git a/app/code/Magento/Catalog/Test/Mftf/Test/AdminDisableProductOnChangingAttributeSetTest.xml b/app/code/Magento/Catalog/Test/Mftf/Test/AdminDisableProductOnChangingAttributeSetTest.xml index 0fc2c022b81e9..f2a7ffcce0eb0 100644 --- a/app/code/Magento/Catalog/Test/Mftf/Test/AdminDisableProductOnChangingAttributeSetTest.xml +++ b/app/code/Magento/Catalog/Test/Mftf/Test/AdminDisableProductOnChangingAttributeSetTest.xml @@ -29,9 +29,9 @@ <deleteData createDataKey="createSimpleProduct" stepKey="deleteProduct"/> <deleteData createDataKey="createCategory" stepKey="deleteCategory"/> <actionGroup ref="ClearProductsFilterActionGroup" stepKey="clearProductsFilter"/> - <actionGroup ref="logout" stepKey="logout"/> + <actionGroup ref="AdminLogoutActionGroup" stepKey="logout"/> </after> - <actionGroup ref="LoginAsAdmin" stepKey="login"/> + <actionGroup ref="AdminLoginActionGroup" stepKey="login"/> <amOnPage url="{{AdminProductAttributeSetEditPage.url}}/$$createAttributeSet.attribute_set_id$$/" stepKey="onAttributeSetEdit"/> <actionGroup ref="SaveAttributeSetActionGroup" stepKey="SaveAttributeSet"/> <actionGroup ref="SearchForProductOnBackendActionGroup" stepKey="searchForSimpleProduct"> diff --git a/app/code/Magento/Catalog/Test/Mftf/Test/AdminEditTextEditorProductAttributeTest.xml b/app/code/Magento/Catalog/Test/Mftf/Test/AdminEditTextEditorProductAttributeTest.xml index feea0930390b7..ebbfdc4d72f40 100644 --- a/app/code/Magento/Catalog/Test/Mftf/Test/AdminEditTextEditorProductAttributeTest.xml +++ b/app/code/Magento/Catalog/Test/Mftf/Test/AdminEditTextEditorProductAttributeTest.xml @@ -14,11 +14,11 @@ <group value="Catalog"/> <title value="Admin are able to change Input Type of Text Editor product attribute"/> <description value="Admin are able to change Input Type of Text Editor product attribute"/> - <severity value="CRITICAL"/> + <severity value="BLOCKER"/> <testCaseId value="MC-6215"/> </annotations> <before> - <actionGroup ref="LoginActionGroup" stepKey="loginGetFromGeneralFile"/> + <actionGroup ref="AdminLoginActionGroup" stepKey="loginGetFromGeneralFile"/> <actionGroup ref="EnabledWYSIWYGActionGroup" stepKey="enableWYSIWYG"/> <actionGroup ref="SwitchToVersion4ActionGroup" stepKey="switchToTinyMCE4" /> <createData stepKey="myProductAttributeCreation" entity="productAttributeWysiwyg"/> @@ -76,7 +76,7 @@ <after> <deleteData createDataKey="myProductAttributeCreation" stepKey="deletePreReqProductAttribute" /> <actionGroup ref="DisabledWYSIWYGActionGroup" stepKey="disableWYSIWYG"/> - <actionGroup ref="logout" stepKey="logout"/> + <actionGroup ref="AdminLogoutActionGroup" stepKey="logout"/> </after> </test> </tests> diff --git a/app/code/Magento/Catalog/Test/Mftf/Test/AdminFilterByNameByStoreViewOnProductGridTest.xml b/app/code/Magento/Catalog/Test/Mftf/Test/AdminFilterByNameByStoreViewOnProductGridTest.xml index 0b230b0b8e002..843221782ebd9 100644 --- a/app/code/Magento/Catalog/Test/Mftf/Test/AdminFilterByNameByStoreViewOnProductGridTest.xml +++ b/app/code/Magento/Catalog/Test/Mftf/Test/AdminFilterByNameByStoreViewOnProductGridTest.xml @@ -21,14 +21,16 @@ </annotations> <before> <createData entity="SimpleProduct2" stepKey="createSimpleProduct"/> - <actionGroup ref="LoginAsAdmin" stepKey="loginAsAdmin"/> + <actionGroup ref="AdminLoginActionGroup" stepKey="loginAsAdmin"/> </before> <after> <deleteData createDataKey="createSimpleProduct" stepKey="deleteSimpleProduct"/> <actionGroup ref="ClearProductsFilterActionGroup" stepKey="clearProductsFilter"/> - <actionGroup ref="logout" stepKey="logout"/> + <actionGroup ref="AdminLogoutActionGroup" stepKey="logout"/> </after> - <amOnPage url="{{AdminProductEditPage.url($$createSimpleProduct.id$$)}}" stepKey="goToEditPage"/> + <actionGroup ref="AdminProductPageOpenByIdActionGroup" stepKey="goToEditPage"> + <argument name="productId" value="$$createSimpleProduct.id$$"/> + </actionGroup> <actionGroup ref="AdminSwitchStoreViewActionGroup" stepKey="switchToDefaultStoreView"> <argument name="storeView" value="_defaultStore.name"/> </actionGroup> diff --git a/app/code/Magento/Catalog/Test/Mftf/Test/AdminFilteringCategoryProductsUsingScopeSelectorTest.xml b/app/code/Magento/Catalog/Test/Mftf/Test/AdminFilteringCategoryProductsUsingScopeSelectorTest.xml index 77b719c03091e..89b3b2cb6fb41 100644 --- a/app/code/Magento/Catalog/Test/Mftf/Test/AdminFilteringCategoryProductsUsingScopeSelectorTest.xml +++ b/app/code/Magento/Catalog/Test/Mftf/Test/AdminFilteringCategoryProductsUsingScopeSelectorTest.xml @@ -18,7 +18,7 @@ <group value="catalog"/> </annotations> <before> - <actionGroup ref="LoginAsAdmin" stepKey="loginAsAdmin"/> + <actionGroup ref="AdminLoginActionGroup" stepKey="loginAsAdmin"/> <!--Create website, Store and Store View--> <actionGroup ref="AdminCreateWebsiteActionGroup" stepKey="createSecondWebsite"> <argument name="newWebsiteName" value="{{secondCustomWebsite.name}}"/> @@ -97,7 +97,7 @@ <deleteData createDataKey="createProduct2" stepKey="deleteProduct2"/> <deleteData createDataKey="createProduct12" stepKey="deleteProduct3"/> <deleteData createDataKey="createCategory" stepKey="deleteCategory"/> - <actionGroup ref="logout" stepKey="logout"/> + <actionGroup ref="AdminLogoutActionGroup" stepKey="logout"/> </after> <!-- Step 1-2: Open Category page and Set scope selector to All Store Views--> <amOnPage url="{{AdminCategoryPage.url}}" stepKey="goToCategoryPage"/> @@ -106,8 +106,10 @@ <click selector="{{AdminCategoryProductsSection.sectionHeader}}" stepKey="openProductSection"/> <grabTextFrom selector="{{AdminCategorySidebarTreeSection.categoryInTree($$createCategory.name$$)}}" stepKey="grabTextFromCategory"/> - <assertRegExp expected="/\(4\)$/" expectedType="string" actual="$grabTextFromCategory" actualType="variable" - message="wrongCountProductOnAllStoreViews" stepKey="checkCountProducts"/> + <assertRegExp message="wrongCountProductOnAllStoreViews" stepKey="checkCountProducts"> + <actualResult type="variable">$grabTextFromCategory</actualResult> + <expectedResult type="string">/\(4\)$/</expectedResult> + </assertRegExp> <see selector="{{AdminCategoryProductsGridSection.productGridNameProduct($$createProduct0.name$$)}}" userInput="$$createProduct0.name$$" stepKey="seeProductName"/> <see selector="{{AdminCategoryProductsGridSection.productGridNameProduct($$createProduct1.name$$)}}" @@ -124,8 +126,10 @@ </actionGroup> <grabTextFrom selector="{{AdminCategorySidebarTreeSection.categoryInTree($$createCategory.name$$)}}" stepKey="grabTextFromCategory1"/> - <assertRegExp expected="/\(2\)$/" expectedType="string" actual="$grabTextFromCategory1" actualType="variable" - message="wrongCountProductOnWebsite1" stepKey="checkCountProducts1"/> + <assertRegExp message="wrongCountProductOnWebsite1" stepKey="checkCountProducts1"> + <actualResult type="variable">$grabTextFromCategory1</actualResult> + <expectedResult type="string">/\(2\)$/</expectedResult> + </assertRegExp> <click selector="{{AdminCategoryProductsSection.sectionHeader}}" stepKey="openProductSection1"/> <see selector="{{AdminCategoryProductsGridSection.productGridNameProduct($$createProduct1.name$$)}}" userInput="$$createProduct1.name$$" stepKey="seeProductName4"/> @@ -144,8 +148,10 @@ <click selector="{{AdminCategoryProductsSection.sectionHeader}}" stepKey="openProductSection2"/> <grabTextFrom selector="{{AdminCategorySidebarTreeSection.categoryInTree($$createCategory.name$$)}}" stepKey="grabTextFromCategory2"/> - <assertRegExp expected="/\(2\)$/" expectedType="string" actual="$grabTextFromCategory2" actualType="variable" - message="wrongCountProductOnWebsite2" stepKey="checkCountProducts2"/> + <assertRegExp message="wrongCountProductOnWebsite2" stepKey="checkCountProducts2"> + <actualResult type="variable">$grabTextFromCategory2</actualResult> + <expectedResult type="string">/\(2\)$/</expectedResult> + </assertRegExp> <see selector="{{AdminCategoryProductsGridSection.productGridNameProduct($$createProduct2.name$$)}}" userInput="$$createProduct2.name$$" stepKey="seeProductName6"/> <see selector="{{AdminCategoryProductsGridSection.productGridNameProduct($$createProduct12.name$$)}}" diff --git a/app/code/Magento/Catalog/Test/Mftf/Test/AdminGridPageNumberAfterSaveAndCloseActionTest.xml b/app/code/Magento/Catalog/Test/Mftf/Test/AdminGridPageNumberAfterSaveAndCloseActionTest.xml index fbc0354482a32..f6b2a74eca0f0 100644 --- a/app/code/Magento/Catalog/Test/Mftf/Test/AdminGridPageNumberAfterSaveAndCloseActionTest.xml +++ b/app/code/Magento/Catalog/Test/Mftf/Test/AdminGridPageNumberAfterSaveAndCloseActionTest.xml @@ -20,7 +20,7 @@ <group value="Catalog"/> </annotations> <before> - <actionGroup ref="LoginAsAdmin" stepKey="loginAsAdmin"/> + <actionGroup ref="AdminLoginActionGroup" stepKey="loginAsAdmin"/> <!--Clear product grid--> <comment userInput="Clear product grid" stepKey="commentClearProductGrid"/> <amOnPage url="{{ProductCatalogPage.url}}" stepKey="goToProductCatalog"/> @@ -47,7 +47,7 @@ <deleteData stepKey="deleteProduct1" createDataKey="product1"/> <deleteData stepKey="deleteCategory2" createDataKey="category2"/> <deleteData stepKey="deleteProduct2" createDataKey="product2"/> - <actionGroup ref="logout" stepKey="logout"/> + <actionGroup ref="AdminLogoutActionGroup" stepKey="logout"/> </after> <amOnPage url="{{ProductCatalogPage.url}}" stepKey="goToProductCatalog"/> <waitForPageLoad stepKey="waitForProductIndexPage"/> @@ -61,7 +61,7 @@ <actionGroup ref="OpenEditProductOnBackendActionGroup" stepKey="openEditProduct2"> <argument name="product" value="$$product2$$"/> </actionGroup> - <actionGroup ref="AdminFormSaveAndClose" stepKey="saveAndCloseProduct"/> + <actionGroup ref="AdminFormSaveAndCloseActionGroup" stepKey="saveAndCloseProduct"/> <waitForPageLoad stepKey="waitForPageLoad1"/> <seeInField selector="{{AdminDataGridPaginationSection.currentPage}}" userInput="2" stepKey="seeOnSecondPageOrderGrid"/> </test> diff --git a/app/code/Magento/Catalog/Test/Mftf/Test/AdminImportCustomizableOptionToProductWithSKUTest.xml b/app/code/Magento/Catalog/Test/Mftf/Test/AdminImportCustomizableOptionToProductWithSKUTest.xml index 76c0d7f7b931c..c319116bf075c 100644 --- a/app/code/Magento/Catalog/Test/Mftf/Test/AdminImportCustomizableOptionToProductWithSKUTest.xml +++ b/app/code/Magento/Catalog/Test/Mftf/Test/AdminImportCustomizableOptionToProductWithSKUTest.xml @@ -35,7 +35,7 @@ <magentoCLI command="indexer:reindex" stepKey="reindex"/> <magentoCLI command="cache:flush" stepKey="flushCache"/> - <actionGroup ref="LoginAsAdmin" stepKey="loginAsAdmin"/> + <actionGroup ref="AdminLoginActionGroup" stepKey="loginAsAdmin"/> </before> <after> <!--Delete created data--> @@ -46,10 +46,12 @@ <argument name="sku" value="$$createFirstProduct.sku$$-1"/> </actionGroup> <actionGroup ref="ClearFiltersAdminDataGridActionGroup" stepKey="clearProductGridFilter"/> - <actionGroup ref="logout" stepKey="logoutOfAdmin"/> + <actionGroup ref="AdminLogoutActionGroup" stepKey="logoutOfAdmin"/> </after> <!--Change second product sku to first product sku--> - <amOnPage url="{{AdminProductEditPage.url($$createSecondProduct.id$$)}}" stepKey="goToProductEditPage1"/> + <actionGroup ref="AdminProductPageOpenByIdActionGroup" stepKey="goToProductEditPage1"> + <argument name="productId" value="$$createSecondProduct.id$$"/> + </actionGroup> <waitForPageLoad stepKey="waitForProductEditPageLoad1"/> <fillField selector="{{AdminProductFormSection.productSku}}" userInput="$$createFirstProduct.sku$$" stepKey="fillProductSku1"/> <!--Import customizable options and check--> diff --git a/app/code/Magento/Catalog/Test/Mftf/Test/AdminMassChangeProductsStatusTest.xml b/app/code/Magento/Catalog/Test/Mftf/Test/AdminMassChangeProductsStatusTest.xml index 6896b11196cf2..0214f9141b903 100644 --- a/app/code/Magento/Catalog/Test/Mftf/Test/AdminMassChangeProductsStatusTest.xml +++ b/app/code/Magento/Catalog/Test/Mftf/Test/AdminMassChangeProductsStatusTest.xml @@ -20,7 +20,7 @@ <group value="Product Attributes"/> </annotations> <before> - <actionGroup ref="LoginAsAdmin" stepKey="loginAsAdmin"/> + <actionGroup ref="AdminLoginActionGroup" stepKey="loginAsAdmin"/> <createData entity="_defaultCategory" stepKey="createCategory"/> <createData entity="ApiSimpleProduct" stepKey="createProductOne"> <requiredEntity createDataKey="createCategory"/> diff --git a/app/code/Magento/Catalog/Test/Mftf/Test/AdminMassProductPriceUpdateTest.xml b/app/code/Magento/Catalog/Test/Mftf/Test/AdminMassProductPriceUpdateTest.xml index e98b145f01401..e8e0d449aee4e 100644 --- a/app/code/Magento/Catalog/Test/Mftf/Test/AdminMassProductPriceUpdateTest.xml +++ b/app/code/Magento/Catalog/Test/Mftf/Test/AdminMassProductPriceUpdateTest.xml @@ -19,14 +19,14 @@ <group value="mtf_migrated"/> </annotations> <before> - <actionGroup ref="LoginAsAdmin" stepKey="loginToAdminPanel"/> + <actionGroup ref="AdminLoginActionGroup" stepKey="loginToAdminPanel"/> <createData entity="defaultSimpleProduct" stepKey="simpleProduct1"/> <createData entity="defaultSimpleProduct" stepKey="simpleProduct2"/> </before> <after> <deleteData createDataKey="simpleProduct1" stepKey="deleteSimpleProduct1"/> <deleteData createDataKey="simpleProduct2" stepKey="deleteSimpleProduct2"/> - <actionGroup ref="logout" stepKey="logout"/> + <actionGroup ref="AdminLogoutActionGroup" stepKey="logout"/> </after> <!--Open Product Index Page--> diff --git a/app/code/Magento/Catalog/Test/Mftf/Test/AdminMassUpdateProductAttributesGlobalScopeTest.xml b/app/code/Magento/Catalog/Test/Mftf/Test/AdminMassUpdateProductAttributesGlobalScopeTest.xml index 71873fe5b0960..31b5961edaaaa 100644 --- a/app/code/Magento/Catalog/Test/Mftf/Test/AdminMassUpdateProductAttributesGlobalScopeTest.xml +++ b/app/code/Magento/Catalog/Test/Mftf/Test/AdminMassUpdateProductAttributesGlobalScopeTest.xml @@ -20,7 +20,7 @@ <group value="product_attributes"/> </annotations> <before> - <actionGroup ref="LoginAsAdmin" stepKey="loginAsAdmin"/> + <actionGroup ref="AdminLoginActionGroup" stepKey="loginAsAdmin"/> <actionGroup ref="DeleteAllProductsUsingProductGridActionGroup" stepKey="deleteAllProducts"/> <actionGroup ref="AdminCreateStoreViewActionGroup" stepKey="createStoreView" /> <createData entity="_defaultCategory" stepKey="createCategory"/> @@ -38,7 +38,7 @@ <actionGroup ref="AdminDeleteStoreViewActionGroup" stepKey="AdminDeleteStoreViewActionGroup"/> <click selector="{{AdminStoresGridSection.resetButton}}" stepKey="resetSearchFilter"/> <actionGroup ref="ClearProductsFilterActionGroup" stepKey="clearProductFilter"/> - <actionGroup ref="logout" stepKey="amOnLogoutPage"/> + <actionGroup ref="AdminLogoutActionGroup" stepKey="amOnLogoutPage"/> </after> <!-- Search and select products --> diff --git a/app/code/Magento/Catalog/Test/Mftf/Test/AdminMassUpdateProductAttributesMissingRequiredFieldTest.xml b/app/code/Magento/Catalog/Test/Mftf/Test/AdminMassUpdateProductAttributesMissingRequiredFieldTest.xml index a4c8f5e8cff6c..e4d69e9169613 100644 --- a/app/code/Magento/Catalog/Test/Mftf/Test/AdminMassUpdateProductAttributesMissingRequiredFieldTest.xml +++ b/app/code/Magento/Catalog/Test/Mftf/Test/AdminMassUpdateProductAttributesMissingRequiredFieldTest.xml @@ -20,7 +20,7 @@ <group value="Product Attributes"/> </annotations> <before> - <actionGroup ref="LoginAsAdmin" stepKey="loginAsAdmin"/> + <actionGroup ref="AdminLoginActionGroup" stepKey="loginAsAdmin"/> <createData entity="_defaultCategory" stepKey="createCategory"/> <createData entity="ApiSimpleProduct" stepKey="createProductOne"> <requiredEntity createDataKey="createCategory"/> diff --git a/app/code/Magento/Catalog/Test/Mftf/Test/AdminMassUpdateProductAttributesStoreViewScopeTest.xml b/app/code/Magento/Catalog/Test/Mftf/Test/AdminMassUpdateProductAttributesStoreViewScopeTest.xml deleted file mode 100644 index c9840fe455f84..0000000000000 --- a/app/code/Magento/Catalog/Test/Mftf/Test/AdminMassUpdateProductAttributesStoreViewScopeTest.xml +++ /dev/null @@ -1,161 +0,0 @@ -<?xml version="1.0" encoding="UTF-8"?> -<!-- - /** - * Copyright © Magento, Inc. All rights reserved. - * See COPYING.txt for license details. - */ ---> - -<tests xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" - xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/testSchema.xsd"> - <test name="AdminMassUpdateProductAttributesStoreViewScopeTest"> - <annotations> - <features value="Catalog"/> - <stories value="Mass update product attributes"/> - <title value="Admin should be able to mass update product attributes in store view scope"/> - <description value="Admin should be able to mass update product attributes in store view scope"/> - <severity value="AVERAGE"/> - <testCaseId value="MC-128"/> - <group value="Catalog"/> - <group value="Product Attributes"/> - <group value="SearchEngineElasticsearch"/> - </annotations> - <before> - <actionGroup ref="LoginAsAdmin" stepKey="loginAsAdmin"/> - <actionGroup ref="AdminCreateStoreViewActionGroup" stepKey="createStoreView" /> - <createData entity="ApiProductWithDescription" stepKey="createProductOne"/> - <createData entity="ApiProductWithDescription" stepKey="createProductTwo"/> - <createData entity="ApiProductNameWithNoSpaces" stepKey="createProductThree"/> - </before> - <after> - <deleteData createDataKey="createProductOne" stepKey="deleteProductOne"/> - <deleteData createDataKey="createProductTwo" stepKey="deleteProductTwo"/> - <deleteData createDataKey="createProductThree" stepKey="deleteProductThree"/> - <actionGroup ref="AdminDeleteStoreViewActionGroup" stepKey="AdminDeleteStoreViewActionGroup"/> - <amOnPage url="{{AdminLogoutPage.url}}" stepKey="amOnLogoutPage"/> - </after> - - <!-- Search and select products --> - <amOnPage url="{{AdminProductIndexPage.url}}" stepKey="navigateToProductIndex"/> - <waitForPageLoad stepKey="waitForProductIndexPageLoad"/> - <actionGroup ref="SearchProductGridByKeyword2ActionGroup" stepKey="searchByKeyword"> - <argument name="keyword" value="api-simple-product"/> - </actionGroup> - <actionGroup ref="SortProductsByIdDescendingActionGroup" stepKey="sortProductsByIdDescending"/> - <click selector="{{AdminProductGridSection.productGridCheckboxOnRow('1')}}" stepKey="clickCheckbox1"/> - <click selector="{{AdminProductGridSection.productGridCheckboxOnRow('2')}}" stepKey="clickCheckbox2"/> - <!-- Mass update attributes --> - <click selector="{{AdminProductGridSection.bulkActionDropdown}}" stepKey="clickDropdown"/> - <click selector="{{AdminProductGridSection.bulkActionOption('Update attributes')}}" stepKey="clickOption"/> - <waitForPageLoad stepKey="waitForBulkUpdatePage"/> - <seeInCurrentUrl stepKey="seeInUrl" url="catalog/product_action_attribute/edit/"/> - <!-- Switch store view --> - <actionGroup ref="AdminSwitchStoreViewActionGroup" stepKey="AdminSwitchStoreViewActionGroup"/> - <!-- Update attribute --> - <click selector="{{AdminEditProductAttributesSection.ChangeAttributeDescriptionToggle}}" stepKey="toggleToChangeDescription"/> - <fillField selector="{{AdminEditProductAttributesSection.AttributeDescription}}" userInput="Updated $$createProductOne.custom_attributes[description]$$" stepKey="fillAttributeDescriptionField"/> - <click selector="{{AdminEditProductAttributesSection.Save}}" stepKey="save"/> - <see selector="{{AdminProductMessagesSection.successMessage}}" userInput="Message is added to queue" stepKey="seeAttributeUpateSuccessMsg"/> - - - <!-- Assert on storefront default view with partial word of product name --> - <actionGroup ref="GoToStoreViewAdvancedCatalogSearchActionGroup" stepKey="GoToStoreViewAdvancedCatalogSearchActionGroupDefault"/> - <actionGroup ref="StorefrontAdvancedCatalogSearchByProductNameAndDescriptionActionGroup" stepKey="searchByNameDefault"> - <argument name="name" value="$$createProductOne.name$$"/> - <argument name="description" value="$$createProductOne.custom_attributes[description]$$"/> - </actionGroup> - <actionGroup ref="StorefrontCheckAdvancedSearchResultActionGroup" stepKey="StorefrontCheckAdvancedSearchResultDefault"/> - <see userInput="2 item" selector="{{StorefrontCatalogSearchAdvancedResultMainSection.itemFound}}" stepKey="seeInDefault"/> - - <!-- Assert on storefront custom view with partial word of product name --> - <actionGroup ref="GoToStoreViewAdvancedCatalogSearchActionGroup" stepKey="GoToStoreViewAdvancedCatalogSearchActionGroupCustom"/> - <actionGroup ref="StorefrontSwitchStoreViewActionGroup" stepKey="StorefrontSwitchStoreViewActionGroup"/> - <actionGroup ref="StorefrontAdvancedCatalogSearchByProductNameAndDescriptionActionGroup" stepKey="searchByNameCustom"> - <argument name="name" value="$$createProductOne.name$$"/> - <argument name="description" value="Updated $$createProductOne.custom_attributes[description]$$"/> - </actionGroup> - <actionGroup ref="StorefrontCheckAdvancedSearchResultActionGroup" stepKey="StorefrontCheckAdvancedSearchResultCustom"/> - <see userInput="2 item" selector="{{StorefrontCatalogSearchAdvancedResultMainSection.itemFound}}" stepKey="seeInCustom"/> - - <!-- Assert Storefront default view with exact product name --> - <actionGroup ref="GoToStoreViewAdvancedCatalogSearchActionGroup" stepKey="GoToStoreViewAdvancedCatalogSearchActionGroupDefault1"/> - <actionGroup ref="StorefrontAdvancedCatalogSearchByProductNameAndDescriptionActionGroup" stepKey="searchByNameDefault1"> - <argument name="name" value="$$createProductThree.name$$"/> - <argument name="description" value="$$createProductThree.custom_attributes[description]$$"/> - </actionGroup> - <actionGroup ref="StorefrontCheckAdvancedSearchResultActionGroup" stepKey="StorefrontCheckAdvancedSearchResultDefault1"/> - <see userInput="1 item" selector="{{StorefrontCatalogSearchAdvancedResultMainSection.itemFound}}" stepKey="seeInDefault1"/> - </test> - <test name="AdminMassUpdateProductAttributesStoreViewScopeMysqlTest"> - <annotations> - <features value="Catalog"/> - <stories value="Mass update product attributes"/> - <title value="Admin should be able to mass update product attributes in store view scope using the Mysql search engine"/> - <description value="Admin should be able to mass update product attributes in store view scope using the Mysql search engine"/> - <severity value="AVERAGE"/> - <testCaseId value="MC-20467"/> - <group value="Catalog"/> - <group value="Product Attributes"/> - <group value="SearchEngineMysql"/> - </annotations> - <before> - <actionGroup ref="LoginAsAdmin" stepKey="loginAsAdmin"/> - <actionGroup ref="AdminCreateStoreViewActionGroup" stepKey="createStoreView" /> - <createData entity="ApiProductWithDescription" stepKey="createProductOne"/> - <createData entity="ApiProductWithDescription" stepKey="createProductTwo"/> - </before> - <after> - <deleteData createDataKey="createProductOne" stepKey="deleteProductOne"/> - <deleteData createDataKey="createProductTwo" stepKey="deleteProductTwo"/> - <actionGroup ref="AdminDeleteStoreViewActionGroup" stepKey="AdminDeleteStoreViewActionGroup"/> - <amOnPage url="{{AdminLogoutPage.url}}" stepKey="amOnLogoutPage"/> - </after> - - <!-- Search and select products --> - <amOnPage url="{{AdminProductIndexPage.url}}" stepKey="navigateToProductIndex"/> - <waitForPageLoad stepKey="waitForProductIndexPageLoad"/> - <actionGroup ref="SearchProductGridByKeyword2ActionGroup" stepKey="searchByKeyword"> - <argument name="keyword" value="api-simple-product"/> - </actionGroup> - <actionGroup ref="SortProductsByIdDescendingActionGroup" stepKey="sortProductsByIdDescending"/> - <click selector="{{AdminProductGridSection.productGridCheckboxOnRow('1')}}" stepKey="clickCheckbox1"/> - <click selector="{{AdminProductGridSection.productGridCheckboxOnRow('2')}}" stepKey="clickCheckbox2"/> - <!-- Mass update attributes --> - <click selector="{{AdminProductGridSection.bulkActionDropdown}}" stepKey="clickDropdown"/> - <click selector="{{AdminProductGridSection.bulkActionOption('Update attributes')}}" stepKey="clickOption"/> - <waitForPageLoad stepKey="waitForBulkUpdatePage"/> - <seeInCurrentUrl stepKey="seeInUrl" url="catalog/product_action_attribute/edit/"/> - <!-- Switch store view --> - <actionGroup ref="AdminSwitchStoreViewActionGroup" stepKey="AdminSwitchStoreViewActionGroup"/> - <!-- Update attribute --> - <click selector="{{AdminEditProductAttributesSection.ChangeAttributeDescriptionToggle}}" stepKey="toggleToChangeDescription"/> - <fillField selector="{{AdminEditProductAttributesSection.AttributeDescription}}" userInput="Updated $$createProductOne.custom_attributes[description]$$" stepKey="fillAttributeDescriptionField"/> - <click selector="{{AdminEditProductAttributesSection.Save}}" stepKey="save"/> - <see selector="{{AdminProductMessagesSection.successMessage}}" userInput="Message is added to queue" stepKey="seeAttributeUpateSuccessMsg"/> - - <!-- Run cron twice --> - <magentoCLI command="cron:run" stepKey="runCron1"/> - <magentoCLI command="cron:run" stepKey="runCron2"/> - <reloadPage stepKey="refreshPage"/> - <waitForPageLoad stepKey="waitFormToReload1"/> - - <!-- Assert on storefront default view --> - <actionGroup ref="GoToStoreViewAdvancedCatalogSearchActionGroup" stepKey="GoToStoreViewAdvancedCatalogSearchActionGroupDefault"/> - <actionGroup ref="StorefrontAdvancedCatalogSearchByProductNameAndDescriptionActionGroup" stepKey="searchByNameDefault"> - <argument name="name" value="$$createProductOne.name$$"/> - <argument name="description" value="$$createProductOne.custom_attributes[description]$$"/> - </actionGroup> - <actionGroup ref="StorefrontCheckAdvancedSearchResultActionGroup" stepKey="StorefrontCheckAdvancedSearchResultDefault"/> - <see userInput="1 item" selector="{{StorefrontCatalogSearchAdvancedResultMainSection.itemFound}}" stepKey="seeInDefault"/> - - <!-- Assert on storefront custom view --> - <actionGroup ref="GoToStoreViewAdvancedCatalogSearchActionGroup" stepKey="GoToStoreViewAdvancedCatalogSearchActionGroupCustom"/> - <actionGroup ref="StorefrontSwitchStoreViewActionGroup" stepKey="StorefrontSwitchStoreViewActionGroup"/> - <actionGroup ref="StorefrontAdvancedCatalogSearchByProductNameAndDescriptionActionGroup" stepKey="searchByNameCustom"> - <argument name="name" value="$$createProductOne.name$$"/> - <argument name="description" value="Updated $$createProductOne.custom_attributes[description]$$"/> - </actionGroup> - <actionGroup ref="StorefrontCheckAdvancedSearchResultActionGroup" stepKey="StorefrontCheckAdvancedSearchResultCustom"/> - <see userInput="1 item" selector="{{StorefrontCatalogSearchAdvancedResultMainSection.itemFound}}" stepKey="seeInCustom"/> - </test> -</tests> diff --git a/app/code/Magento/Catalog/Test/Mftf/Test/AdminMassUpdateProductAttributesStoreViewScopeTest/AdminMassUpdateProductAttributesStoreViewScopeMysqlTest.xml b/app/code/Magento/Catalog/Test/Mftf/Test/AdminMassUpdateProductAttributesStoreViewScopeTest/AdminMassUpdateProductAttributesStoreViewScopeMysqlTest.xml new file mode 100644 index 0000000000000..7cdfd6dabed47 --- /dev/null +++ b/app/code/Magento/Catalog/Test/Mftf/Test/AdminMassUpdateProductAttributesStoreViewScopeTest/AdminMassUpdateProductAttributesStoreViewScopeMysqlTest.xml @@ -0,0 +1,82 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + /** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +--> +<tests xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/testSchema.xsd"> + <test name="AdminMassUpdateProductAttributesStoreViewScopeMysqlTest"> + <annotations> + <features value="Catalog"/> + <stories value="Mass update product attributes"/> + <title value="Admin should be able to mass update product attributes in store view scope using the Mysql search engine"/> + <description value="Admin should be able to mass update product attributes in store view scope using the Mysql search engine"/> + <severity value="AVERAGE"/> + <testCaseId value="MC-20467"/> + <group value="Catalog"/> + <group value="Product Attributes"/> + <group value="SearchEngineMysql"/> + </annotations> + <before> + <actionGroup ref="AdminLoginActionGroup" stepKey="loginAsAdmin"/> + <actionGroup ref="AdminCreateStoreViewActionGroup" stepKey="createStoreView"/> + <createData entity="ApiProductWithDescription" stepKey="createProductOne"/> + <createData entity="ApiProductWithDescription" stepKey="createProductTwo"/> + </before> + <after> + <deleteData createDataKey="createProductOne" stepKey="deleteProductOne"/> + <deleteData createDataKey="createProductTwo" stepKey="deleteProductTwo"/> + <actionGroup ref="AdminDeleteStoreViewActionGroup" stepKey="AdminDeleteStoreViewActionGroup"/> + <amOnPage url="{{AdminLogoutPage.url}}" stepKey="amOnLogoutPage"/> + </after> + + <!-- Search and select products --> + <amOnPage url="{{AdminProductIndexPage.url}}" stepKey="navigateToProductIndex"/> + <waitForPageLoad stepKey="waitForProductIndexPageLoad"/> + <actionGroup ref="SearchProductGridByKeyword2ActionGroup" stepKey="searchByKeyword"> + <argument name="keyword" value="api-simple-product"/> + </actionGroup> + <actionGroup ref="SortProductsByIdDescendingActionGroup" stepKey="sortProductsByIdDescending"/> + <click selector="{{AdminProductGridSection.productGridCheckboxOnRow('1')}}" stepKey="clickCheckbox1"/> + <click selector="{{AdminProductGridSection.productGridCheckboxOnRow('2')}}" stepKey="clickCheckbox2"/> + <!-- Mass update attributes --> + <click selector="{{AdminProductGridSection.bulkActionDropdown}}" stepKey="clickDropdown"/> + <click selector="{{AdminProductGridSection.bulkActionOption('Update attributes')}}" stepKey="clickOption"/> + <waitForPageLoad stepKey="waitForBulkUpdatePage"/> + <seeInCurrentUrl stepKey="seeInUrl" url="catalog/product_action_attribute/edit/"/> + <!-- Switch store view --> + <actionGroup ref="AdminSwitchStoreViewActionGroup" stepKey="AdminSwitchStoreViewActionGroup"/> + <!-- Update attribute --> + <click selector="{{AdminEditProductAttributesSection.ChangeAttributeDescriptionToggle}}" stepKey="toggleToChangeDescription"/> + <fillField selector="{{AdminEditProductAttributesSection.AttributeDescription}}" userInput="Updated $$createProductOne.custom_attributes[description]$$" stepKey="fillAttributeDescriptionField"/> + <click selector="{{AdminEditProductAttributesSection.Save}}" stepKey="save"/> + <see selector="{{AdminProductMessagesSection.successMessage}}" userInput="Message is added to queue" stepKey="seeAttributeUpateSuccessMsg"/> + + <!-- Run cron twice --> + <magentoCLI command="cron:run" stepKey="runCron1"/> + <magentoCLI command="cron:run" stepKey="runCron2"/> + <reloadPage stepKey="refreshPage"/> + <waitForPageLoad stepKey="waitFormToReload1"/> + + <!-- Assert on storefront default view --> + <actionGroup ref="GoToStoreViewAdvancedCatalogSearchActionGroup" stepKey="GoToStoreViewAdvancedCatalogSearchActionGroupDefault"/> + <actionGroup ref="StorefrontAdvancedCatalogSearchByProductNameAndDescriptionActionGroup" stepKey="searchByNameDefault"> + <argument name="name" value="$$createProductOne.name$$"/> + <argument name="description" value="$$createProductOne.custom_attributes[description]$$"/> + </actionGroup> + <actionGroup ref="StorefrontCheckAdvancedSearchResultActionGroup" stepKey="StorefrontCheckAdvancedSearchResultDefault"/> + <see userInput="1 item" selector="{{StorefrontCatalogSearchAdvancedResultMainSection.itemFound}}" stepKey="seeInDefault"/> + + <!-- Assert on storefront custom view --> + <actionGroup ref="GoToStoreViewAdvancedCatalogSearchActionGroup" stepKey="GoToStoreViewAdvancedCatalogSearchActionGroupCustom"/> + <actionGroup ref="StorefrontSwitchStoreViewActionGroup" stepKey="StorefrontSwitchStoreViewActionGroup"/> + <actionGroup ref="StorefrontAdvancedCatalogSearchByProductNameAndDescriptionActionGroup" stepKey="searchByNameCustom"> + <argument name="name" value="$$createProductOne.name$$"/> + <argument name="description" value="Updated $$createProductOne.custom_attributes[description]$$"/> + </actionGroup> + <actionGroup ref="StorefrontCheckAdvancedSearchResultActionGroup" stepKey="StorefrontCheckAdvancedSearchResultCustom"/> + <see userInput="1 item" selector="{{StorefrontCatalogSearchAdvancedResultMainSection.itemFound}}" stepKey="seeInCustom"/> + </test> +</tests> diff --git a/app/code/Magento/Catalog/Test/Mftf/Test/AdminMassUpdateProductAttributesStoreViewScopeTest/AdminMassUpdateProductAttributesStoreViewScopeTest.xml b/app/code/Magento/Catalog/Test/Mftf/Test/AdminMassUpdateProductAttributesStoreViewScopeTest/AdminMassUpdateProductAttributesStoreViewScopeTest.xml new file mode 100644 index 0000000000000..08b2d924e2a5e --- /dev/null +++ b/app/code/Magento/Catalog/Test/Mftf/Test/AdminMassUpdateProductAttributesStoreViewScopeTest/AdminMassUpdateProductAttributesStoreViewScopeTest.xml @@ -0,0 +1,88 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + /** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +--> +<tests xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/testSchema.xsd"> + <test name="AdminMassUpdateProductAttributesStoreViewScopeTest"> + <annotations> + <features value="Catalog"/> + <stories value="Mass update product attributes"/> + <title value="Admin should be able to mass update product attributes in store view scope"/> + <description value="Admin should be able to mass update product attributes in store view scope"/> + <severity value="AVERAGE"/> + <testCaseId value="MC-128"/> + <group value="Catalog"/> + <group value="Product Attributes"/> + <group value="SearchEngineElasticsearch"/> + </annotations> + <before> + <actionGroup ref="AdminLoginActionGroup" stepKey="loginAsAdmin"/> + <actionGroup ref="AdminCreateStoreViewActionGroup" stepKey="createStoreView"/> + <createData entity="ApiProductWithDescription" stepKey="createProductOne"/> + <createData entity="ApiProductWithDescription" stepKey="createProductTwo"/> + <createData entity="ApiProductNameWithNoSpaces" stepKey="createProductThree"/> + </before> + <after> + <deleteData createDataKey="createProductOne" stepKey="deleteProductOne"/> + <deleteData createDataKey="createProductTwo" stepKey="deleteProductTwo"/> + <deleteData createDataKey="createProductThree" stepKey="deleteProductThree"/> + <actionGroup ref="AdminDeleteStoreViewActionGroup" stepKey="AdminDeleteStoreViewActionGroup"/> + <amOnPage url="{{AdminLogoutPage.url}}" stepKey="amOnLogoutPage"/> + </after> + + <!-- Search and select products --> + <amOnPage url="{{AdminProductIndexPage.url}}" stepKey="navigateToProductIndex"/> + <waitForPageLoad stepKey="waitForProductIndexPageLoad"/> + <actionGroup ref="SearchProductGridByKeyword2ActionGroup" stepKey="searchByKeyword"> + <argument name="keyword" value="api-simple-product"/> + </actionGroup> + <actionGroup ref="SortProductsByIdDescendingActionGroup" stepKey="sortProductsByIdDescending"/> + <click selector="{{AdminProductGridSection.productGridCheckboxOnRow('1')}}" stepKey="clickCheckbox1"/> + <click selector="{{AdminProductGridSection.productGridCheckboxOnRow('2')}}" stepKey="clickCheckbox2"/> + <!-- Mass update attributes --> + <click selector="{{AdminProductGridSection.bulkActionDropdown}}" stepKey="clickDropdown"/> + <click selector="{{AdminProductGridSection.bulkActionOption('Update attributes')}}" stepKey="clickOption"/> + <waitForPageLoad stepKey="waitForBulkUpdatePage"/> + <seeInCurrentUrl stepKey="seeInUrl" url="catalog/product_action_attribute/edit/"/> + <!-- Switch store view --> + <actionGroup ref="AdminSwitchStoreViewActionGroup" stepKey="AdminSwitchStoreViewActionGroup"/> + <!-- Update attribute --> + <click selector="{{AdminEditProductAttributesSection.ChangeAttributeDescriptionToggle}}" stepKey="toggleToChangeDescription"/> + <fillField selector="{{AdminEditProductAttributesSection.AttributeDescription}}" userInput="Updated $$createProductOne.custom_attributes[description]$$" stepKey="fillAttributeDescriptionField"/> + <click selector="{{AdminEditProductAttributesSection.Save}}" stepKey="save"/> + <see selector="{{AdminProductMessagesSection.successMessage}}" userInput="Message is added to queue" stepKey="seeAttributeUpateSuccessMsg"/> + + + <!-- Assert on storefront default view with partial word of product name --> + <actionGroup ref="GoToStoreViewAdvancedCatalogSearchActionGroup" stepKey="GoToStoreViewAdvancedCatalogSearchActionGroupDefault"/> + <actionGroup ref="StorefrontAdvancedCatalogSearchByProductNameAndDescriptionActionGroup" stepKey="searchByNameDefault"> + <argument name="name" value="$$createProductOne.name$$"/> + <argument name="description" value="$$createProductOne.custom_attributes[description]$$"/> + </actionGroup> + <actionGroup ref="StorefrontCheckAdvancedSearchResultActionGroup" stepKey="StorefrontCheckAdvancedSearchResultDefault"/> + <see userInput="2 item" selector="{{StorefrontCatalogSearchAdvancedResultMainSection.itemFound}}" stepKey="seeInDefault"/> + + <!-- Assert on storefront custom view with partial word of product name --> + <actionGroup ref="GoToStoreViewAdvancedCatalogSearchActionGroup" stepKey="GoToStoreViewAdvancedCatalogSearchActionGroupCustom"/> + <actionGroup ref="StorefrontSwitchStoreViewActionGroup" stepKey="StorefrontSwitchStoreViewActionGroup"/> + <actionGroup ref="StorefrontAdvancedCatalogSearchByProductNameAndDescriptionActionGroup" stepKey="searchByNameCustom"> + <argument name="name" value="$$createProductOne.name$$"/> + <argument name="description" value="Updated $$createProductOne.custom_attributes[description]$$"/> + </actionGroup> + <actionGroup ref="StorefrontCheckAdvancedSearchResultActionGroup" stepKey="StorefrontCheckAdvancedSearchResultCustom"/> + <see userInput="2 item" selector="{{StorefrontCatalogSearchAdvancedResultMainSection.itemFound}}" stepKey="seeInCustom"/> + + <!-- Assert Storefront default view with exact product name --> + <actionGroup ref="GoToStoreViewAdvancedCatalogSearchActionGroup" stepKey="GoToStoreViewAdvancedCatalogSearchActionGroupDefault1"/> + <actionGroup ref="StorefrontAdvancedCatalogSearchByProductNameAndDescriptionActionGroup" stepKey="searchByNameDefault1"> + <argument name="name" value="$$createProductThree.name$$"/> + <argument name="description" value="$$createProductThree.custom_attributes[description]$$"/> + </actionGroup> + <actionGroup ref="StorefrontCheckAdvancedSearchResultActionGroup" stepKey="StorefrontCheckAdvancedSearchResultDefault1"/> + <see userInput="1 item" selector="{{StorefrontCatalogSearchAdvancedResultMainSection.itemFound}}" stepKey="seeInDefault1"/> + </test> +</tests> diff --git a/app/code/Magento/Catalog/Test/Mftf/Test/AdminMassUpdateProductStatusStoreViewScopeTest.xml b/app/code/Magento/Catalog/Test/Mftf/Test/AdminMassUpdateProductStatusStoreViewScopeTest.xml deleted file mode 100644 index 21c6c56adfd96..0000000000000 --- a/app/code/Magento/Catalog/Test/Mftf/Test/AdminMassUpdateProductStatusStoreViewScopeTest.xml +++ /dev/null @@ -1,302 +0,0 @@ -<?xml version="1.0" encoding="UTF-8"?> -<!-- - /** - * Copyright © Magento, Inc. All rights reserved. - * See COPYING.txt for license details. - */ ---> - -<tests xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" - xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/testSchema.xsd"> - <test name="AdminMassUpdateProductStatusStoreViewScopeTest"> - <annotations> - <features value="Catalog"/> - <stories value="Mass update product status"/> - <title value="Admin should be able to mass update product statuses in store view scope"/> - <description value="Admin should be able to mass update product statuses in store view scope"/> - <severity value="AVERAGE"/> - <testCaseId value="MAGETWO-59361"/> - <group value="Catalog"/> - <group value="Product Attributes"/> - <group value="SearchEngineElasticsearch"/> - </annotations> - <before> - <actionGroup ref="LoginActionGroup" stepKey="loginAsAdmin"/> - - <!--Create Website --> - <actionGroup ref="AdminCreateWebsiteActionGroup" stepKey="createAdditionalWebsite"> - <argument name="newWebsiteName" value="Second Website"/> - <argument name="websiteCode" value="second_website"/> - </actionGroup> - - <!--Create Store --> - <actionGroup ref="AdminCreateNewStoreGroupActionGroup" stepKey="createNewStore"> - <argument name="website" value="Second Website"/> - <argument name="storeGroupName" value="Second Store"/> - <argument name="storeGroupCode" value="second_store"/> - </actionGroup> - - <!--Create Store view --> - <amOnPage url="{{AdminSystemStorePage.url}}" stepKey="amOnAdminSystemStorePage"/> - <waitForPageLoad stepKey="waitForSystemStorePage"/> - <click selector="{{AdminStoresMainActionsSection.createStoreViewButton}}" stepKey="createStoreViewButton"/> - <waitForPageLoad stepKey="waitForProductPageLoad"/> - <waitForElementVisible selector="//legend[contains(., 'Store View Information')]" stepKey="waitForNewStorePageToOpen"/> - <selectOption userInput="Second Store" selector="{{AdminNewStoreSection.storeGrpDropdown}}" stepKey="selectStoreGroup"/> - <fillField userInput="Second Store View" selector="{{AdminNewStoreSection.storeNameTextField}}" stepKey="fillStoreViewName"/> - <fillField userInput="second_store_view" selector="{{AdminNewStoreSection.storeCodeTextField}}" stepKey="fillStoreViewCode"/> - <selectOption selector="{{AdminNewStoreSection.statusDropdown}}" userInput="1" stepKey="enableStoreViewStatus"/> - <click selector="{{AdminNewStoreViewActionsSection.saveButton}}" stepKey="clickSaveStoreView" /> - <waitForElementVisible selector="{{AdminConfirmationModalSection.ok}}" stepKey="waitForModal" /> - <see selector="{{AdminConfirmationModalSection.title}}" userInput="Warning message" stepKey="seeWarning" /> - <click selector="{{AdminConfirmationModalSection.ok}}" stepKey="dismissModal" /> - <waitForPageLoad stepKey="waitForPageLoad2" time="180" /> - <waitForElementVisible selector="{{AdminStoresGridSection.storeFilterTextField}}" time="150" stepKey="waitForPageReolad"/> - <see userInput="You saved the store view." stepKey="seeSavedMessage" /> - - <!--Create a Simple Product 1 --> - <actionGroup ref="CreateSimpleProductAndAddToWebsiteActionGroup" stepKey="createSimpleProduct1"> - <argument name="product" value="simpleProductForMassUpdate"/> - <argument name="website" value="Second Website"/> - </actionGroup> - - <!--Create a Simple Product 2 --> - <actionGroup ref="CreateSimpleProductAndAddToWebsiteActionGroup" stepKey="createSimpleProduct2"> - <argument name="product" value="simpleProductForMassUpdate2"/> - <argument name="website" value="Second Website"/> - </actionGroup> - </before> - <after> - <!--Delete website --> - <actionGroup ref="AdminDeleteWebsiteActionGroup" stepKey="deleteSecondWebsite"> - <argument name="websiteName" value="Second Website"/> - </actionGroup> - <amOnPage url="{{AdminProductIndexPage.url}}" stepKey="navigateToProductIndex"/> - - <!--Delete Products --> - <actionGroup ref="DeleteProductActionGroup" stepKey="deleteProduct1"> - <argument name="productName" value="simpleProductForMassUpdate.name"/> - </actionGroup> - <actionGroup ref="DeleteProductActionGroup" stepKey="deleteProduct2"> - <argument name="productName" value="simpleProductForMassUpdate2.name"/> - </actionGroup> - <actionGroup ref="logout" stepKey="amOnLogoutPage"/> - </after> - - <!-- Search and select products --> - <amOnPage url="{{AdminProductIndexPage.url}}" stepKey="navigateToProductIndex"/> - <waitForPageLoad stepKey="waitForProductIndexPageLoad"/> - <actionGroup ref="SearchProductGridByKeyword2ActionGroup" stepKey="searchByKeyword"> - <argument name="keyword" value="{{simpleProductForMassUpdate.keyword}}"/> - </actionGroup> - <actionGroup ref="SortProductsByIdDescendingActionGroup" stepKey="sortProductsByIdDescending"/> - - <!-- Filter to Second Store View --> - <actionGroup ref="AdminFilterStoreViewActionGroup" stepKey="filterStoreView" > - <argument name="customStore" value="'Second Store View'" /> - </actionGroup> - - <!-- Select Product 2 --> - <click selector="{{AdminProductGridSection.productGridCheckboxOnRow('2')}}" stepKey="clickCheckbox2"/> - - <!-- Mass update attributes --> - <click selector="{{AdminProductGridSection.bulkActionDropdown}}" stepKey="clickDropdown"/> - <click selector="{{AdminProductGridSection.bulkActionOption('Change status')}}" stepKey="clickOption"/> - <click selector="{{AdminProductGridSection.bulkActionOption('Disable')}}" stepKey="clickDisabled"/> - <waitForPageLoad stepKey="waitForBulkUpdatePage"/> - - <!-- Verify Product Statuses --> - <see selector="{{AdminProductGridSection.productGridContentsOnRow('1')}}" userInput="Enabled" stepKey="checkIfProduct1IsEnabled"/> - <see selector="{{AdminProductGridSection.productGridContentsOnRow('2')}}" userInput="Disabled" stepKey="checkIfProduct2IsDisabled"/> - - <!-- Filter to Default Store View --> - <actionGroup ref="AdminFilterStoreViewActionGroup" stepKey="filterDefaultStoreView"> - <argument name="customStore" value="'Default'" /> - </actionGroup> - - <!-- Verify Product Statuses --> - <see selector="{{AdminProductGridSection.productGridContentsOnRow('1')}}" userInput="Enabled" stepKey="checkIfDefaultViewProduct1IsEnabled"/> - <see selector="{{AdminProductGridSection.productGridContentsOnRow('2')}}" userInput="Enabled" stepKey="checkIfDefaultViewProduct2IsEnabled"/> - - <!-- Assert on storefront default view with first product --> - <actionGroup ref="GoToStoreViewAdvancedCatalogSearchActionGroup" stepKey="GoToStoreViewAdvancedCatalogSearchActionGroupDefault"/> - <actionGroup ref="StorefrontAdvancedCatalogSearchByProductNameAndDescriptionActionGroup" stepKey="searchByNameDefault"> - <argument name="name" value="{{simpleProductForMassUpdate.name}}"/> - <argument name="description" value=""/> - </actionGroup> - <actionGroup ref="StorefrontCheckAdvancedSearchResultActionGroup" stepKey="StorefrontCheckAdvancedSearchResultDefault"/> - <see userInput="1 item" selector="{{StorefrontCatalogSearchAdvancedResultMainSection.itemFound}}" stepKey="seeInDefault"/> - - <!-- Assert on storefront default view with second product --> - <actionGroup ref="GoToStoreViewAdvancedCatalogSearchActionGroup" stepKey="GoToStoreViewAdvancedCatalogSearchActionGroupDefaultToSearchSecondProduct"/> - <actionGroup ref="StorefrontAdvancedCatalogSearchByProductNameAndDescriptionActionGroup" stepKey="searchByNameDefaultWithSecondProduct"> - <argument name="name" value="{{simpleProductForMassUpdate2.name}}"/> - <argument name="description" value=""/> - </actionGroup> - <actionGroup ref="StorefrontCheckAdvancedSearchResultActionGroup" stepKey="StorefrontCheckAdvancedSearchResultDefaultForSecondProduct"/> - <see userInput="1 item" selector="{{StorefrontCatalogSearchAdvancedResultMainSection.itemFound}}" stepKey="seeInDefaultSecondProductResults"/> - - <!--Enable the product in Default store view--> - <amOnPage url="{{AdminProductIndexPage.url}}" stepKey="navigateToProductIndex2"/> - <waitForPageLoad stepKey="waitForProductIndexPageLoad2"/> - <click selector="{{AdminProductGridSection.productGridCheckboxOnRow('1')}}" stepKey="clickCheckboxDefaultStoreView"/> - <click selector="{{AdminProductGridSection.productGridCheckboxOnRow('2')}}" stepKey="clickCheckboxDefaultStoreView2"/> - - <!-- Mass update attributes --> - <click selector="{{AdminProductGridSection.bulkActionDropdown}}" stepKey="clickDropdownDefaultStoreView"/> - <click selector="{{AdminProductGridSection.bulkActionOption('Change status')}}" stepKey="clickOptionDefaultStoreView"/> - <click selector="{{AdminProductGridSection.bulkActionOption('Disable')}}" stepKey="clickDisabledDefaultStoreView"/> - <waitForPageLoad stepKey="waitForBulkUpdatePageDefaultStoreView"/> - <see selector="{{AdminProductGridSection.productGridContentsOnRow('1')}}" userInput="Disabled" stepKey="checkIfProduct2IsDisabledDefaultStoreView"/> - - <!-- Assert on storefront default view --> - <actionGroup ref="GoToStoreViewAdvancedCatalogSearchActionGroup" stepKey="GoToStoreViewAdvancedCatalogSearchActionGroupDefault2"/> - <actionGroup ref="StorefrontAdvancedCatalogSearchByProductNameAndDescriptionActionGroup" stepKey="searchByNameDefault2"> - <argument name="name" value="{{simpleProductForMassUpdate.name}}"/> - <argument name="description" value=""/> - </actionGroup> - <actionGroup ref="StorefrontCheckAdvancedSearchResultActionGroup" stepKey="StorefrontCheckAdvancedSearchResultDefault2"/> - <see userInput="We can't find any items matching these search criteria." selector="{{StorefrontCatalogSearchAdvancedResultMainSection.message}}" stepKey="seeInDefault2"/> - </test> - <test name="AdminMassUpdateProductStatusStoreViewScopeMysqlTest"> - <annotations> - <features value="Catalog"/> - <stories value="Mass update product status"/> - <title value="Admin should be able to mass update product statuses in store view scope using the Mysql search engine"/> - <description value="Admin should be able to mass update product statuses in store view scope using the Mysql search engine"/> - <severity value="AVERAGE"/> - <testCaseId value="MC-20471"/> - <group value="Catalog"/> - <group value="Product Attributes"/> - <group value="SearchEngineMysql"/> - </annotations> - <before> - <actionGroup ref="LoginActionGroup" stepKey="loginAsAdmin"/> - - <!--Create Website --> - <actionGroup ref="AdminCreateWebsiteActionGroup" stepKey="createAdditionalWebsite"> - <argument name="newWebsiteName" value="Second Website"/> - <argument name="websiteCode" value="second_website"/> - </actionGroup> - - <!--Create Store --> - <actionGroup ref="AdminCreateNewStoreGroupActionGroup" stepKey="createNewStore"> - <argument name="website" value="Second Website"/> - <argument name="storeGroupName" value="Second Store"/> - <argument name="storeGroupCode" value="second_store"/> - </actionGroup> - - <!--Create Store view --> - <amOnPage url="{{AdminSystemStorePage.url}}" stepKey="amOnAdminSystemStorePage"/> - <waitForPageLoad stepKey="waitForSystemStorePage"/> - <click selector="{{AdminStoresMainActionsSection.createStoreViewButton}}" stepKey="createStoreViewButton"/> - <waitForPageLoad stepKey="waitForProductPageLoad"/> - <waitForElementVisible selector="//legend[contains(., 'Store View Information')]" stepKey="waitForNewStorePageToOpen"/> - <selectOption userInput="Second Store" selector="{{AdminNewStoreSection.storeGrpDropdown}}" stepKey="selectStoreGroup"/> - <fillField userInput="Second Store View" selector="{{AdminNewStoreSection.storeNameTextField}}" stepKey="fillStoreViewName"/> - <fillField userInput="second_store_view" selector="{{AdminNewStoreSection.storeCodeTextField}}" stepKey="fillStoreViewCode"/> - <selectOption selector="{{AdminNewStoreSection.statusDropdown}}" userInput="1" stepKey="enableStoreViewStatus"/> - <click selector="{{AdminNewStoreViewActionsSection.saveButton}}" stepKey="clickSaveStoreView" /> - <waitForElementVisible selector="{{AdminConfirmationModalSection.ok}}" stepKey="waitForModal" /> - <see selector="{{AdminConfirmationModalSection.title}}" userInput="Warning message" stepKey="seeWarning" /> - <click selector="{{AdminConfirmationModalSection.ok}}" stepKey="dismissModal" /> - <waitForPageLoad stepKey="waitForPageLoad2" time="180" /> - <waitForElementVisible selector="{{AdminStoresGridSection.storeFilterTextField}}" time="150" stepKey="waitForPageReolad"/> - <see userInput="You saved the store view." stepKey="seeSavedMessage" /> - - <!--Create a Simple Product 1 --> - <actionGroup ref="CreateSimpleProductAndAddToWebsiteActionGroup" stepKey="createSimpleProduct1"> - <argument name="product" value="simpleProductForMassUpdate"/> - <argument name="website" value="Second Website"/> - </actionGroup> - - <!--Create a Simple Product 2 --> - <actionGroup ref="CreateSimpleProductAndAddToWebsiteActionGroup" stepKey="createSimpleProduct2"> - <argument name="product" value="simpleProductForMassUpdate2"/> - <argument name="website" value="Second Website"/> - </actionGroup> - </before> - <after> - <!--Delete website --> - <actionGroup ref="AdminDeleteWebsiteActionGroup" stepKey="deleteSecondWebsite"> - <argument name="websiteName" value="Second Website"/> - </actionGroup> - <amOnPage url="{{AdminProductIndexPage.url}}" stepKey="navigateToProductIndex"/> - - <!--Delete Products --> - <actionGroup ref="DeleteProductActionGroup" stepKey="deleteProduct1"> - <argument name="productName" value="simpleProductForMassUpdate.name"/> - </actionGroup> - <actionGroup ref="DeleteProductActionGroup" stepKey="deleteProduct2"> - <argument name="productName" value="simpleProductForMassUpdate2.name"/> - </actionGroup> - <actionGroup ref="logout" stepKey="amOnLogoutPage"/> - </after> - - <!-- Search and select products --> - <amOnPage url="{{AdminProductIndexPage.url}}" stepKey="navigateToProductIndex"/> - <waitForPageLoad stepKey="waitForProductIndexPageLoad"/> - <actionGroup ref="SearchProductGridByKeyword2ActionGroup" stepKey="searchByKeyword"> - <argument name="keyword" value="{{simpleProductForMassUpdate.keyword}}"/> - </actionGroup> - <actionGroup ref="SortProductsByIdDescendingActionGroup" stepKey="sortProductsByIdDescending"/> - - <!-- Filter to Second Store View --> - <actionGroup ref="AdminFilterStoreViewActionGroup" stepKey="filterStoreView" > - <argument name="customStore" value="'Second Store View'" /> - </actionGroup> - - <!-- Select Product 2 --> - <click selector="{{AdminProductGridSection.productGridCheckboxOnRow('2')}}" stepKey="clickCheckbox2"/> - - <!-- Mass update attributes --> - <click selector="{{AdminProductGridSection.bulkActionDropdown}}" stepKey="clickDropdown"/> - <click selector="{{AdminProductGridSection.bulkActionOption('Change status')}}" stepKey="clickOption"/> - <click selector="{{AdminProductGridSection.bulkActionOption('Disable')}}" stepKey="clickDisabled"/> - <waitForPageLoad stepKey="waitForBulkUpdatePage"/> - - <!-- Verify Product Statuses --> - <see selector="{{AdminProductGridSection.productGridContentsOnRow('1')}}" userInput="Enabled" stepKey="checkIfProduct1IsEnabled"/> - <see selector="{{AdminProductGridSection.productGridContentsOnRow('2')}}" userInput="Disabled" stepKey="checkIfProduct2IsDisabled"/> - - <!-- Filter to Default Store View --> - <actionGroup ref="AdminFilterStoreViewActionGroup" stepKey="filterDefaultStoreView"> - <argument name="customStore" value="'Default'" /> - </actionGroup> - - <!-- Verify Product Statuses --> - <see selector="{{AdminProductGridSection.productGridContentsOnRow('1')}}" userInput="Enabled" stepKey="checkIfDefaultViewProduct1IsEnabled"/> - <see selector="{{AdminProductGridSection.productGridContentsOnRow('2')}}" userInput="Enabled" stepKey="checkIfDefaultViewProduct2IsEnabled"/> - - <!-- Assert on storefront default view --> - <actionGroup ref="GoToStoreViewAdvancedCatalogSearchActionGroup" stepKey="GoToStoreViewAdvancedCatalogSearchActionGroupDefault"/> - <actionGroup ref="StorefrontAdvancedCatalogSearchByProductNameAndDescriptionActionGroup" stepKey="searchByNameDefault"> - <argument name="name" value="{{simpleProductForMassUpdate.keyword}}"/> - <argument name="description" value=""/> - </actionGroup> - <actionGroup ref="StorefrontCheckAdvancedSearchResultActionGroup" stepKey="StorefrontCheckAdvancedSearchResultDefault"/> - <see userInput="2 items" selector="{{StorefrontCatalogSearchAdvancedResultMainSection.itemFound}}" stepKey="seeInDefault"/> - - <!-- Enable the product in Default store view --> - <amOnPage url="{{AdminProductIndexPage.url}}" stepKey="navigateToProductIndex2"/> - <waitForPageLoad stepKey="waitForProductIndexPageLoad2"/> - <click selector="{{AdminProductGridSection.productGridCheckboxOnRow('1')}}" stepKey="clickCheckboxDefaultStoreView"/> - <click selector="{{AdminProductGridSection.productGridCheckboxOnRow('2')}}" stepKey="clickCheckboxDefaultStoreView2"/> - - <!-- Mass update attributes --> - <click selector="{{AdminProductGridSection.bulkActionDropdown}}" stepKey="clickDropdownDefaultStoreView"/> - <click selector="{{AdminProductGridSection.bulkActionOption('Change status')}}" stepKey="clickOptionDefaultStoreView"/> - <click selector="{{AdminProductGridSection.bulkActionOption('Disable')}}" stepKey="clickDisabledDefaultStoreView"/> - <waitForPageLoad stepKey="waitForBulkUpdatePageDefaultStoreView"/> - <see selector="{{AdminProductGridSection.productGridContentsOnRow('1')}}" userInput="Disabled" stepKey="checkIfProduct2IsDisabledDefaultStoreView"/> - - <!-- Assert on storefront default view --> - <actionGroup ref="GoToStoreViewAdvancedCatalogSearchActionGroup" stepKey="GoToStoreViewAdvancedCatalogSearchActionGroupDefault2"/> - <actionGroup ref="StorefrontAdvancedCatalogSearchByProductNameAndDescriptionActionGroup" stepKey="searchByNameDefault2"> - <argument name="name" value="{{simpleProductForMassUpdate.name}}"/> - <argument name="description" value=""/> - </actionGroup> - <actionGroup ref="StorefrontCheckAdvancedSearchResultActionGroup" stepKey="StorefrontCheckAdvancedSearchResultDefault2"/> - <see userInput="We can't find any items matching these search criteria." selector="{{StorefrontCatalogSearchAdvancedResultMainSection.message}}" stepKey="seeInDefault2"/> - </test> -</tests> diff --git a/app/code/Magento/Catalog/Test/Mftf/Test/AdminMassUpdateProductStatusStoreViewScopeTest/AdminMassUpdateProductStatusStoreViewScopeMysqlTest.xml b/app/code/Magento/Catalog/Test/Mftf/Test/AdminMassUpdateProductStatusStoreViewScopeTest/AdminMassUpdateProductStatusStoreViewScopeMysqlTest.xml new file mode 100644 index 0000000000000..63e22fc5a12d9 --- /dev/null +++ b/app/code/Magento/Catalog/Test/Mftf/Test/AdminMassUpdateProductStatusStoreViewScopeTest/AdminMassUpdateProductStatusStoreViewScopeMysqlTest.xml @@ -0,0 +1,151 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + /** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +--> +<tests xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/testSchema.xsd"> + <test name="AdminMassUpdateProductStatusStoreViewScopeMysqlTest"> + <annotations> + <features value="Catalog"/> + <stories value="Mass update product status"/> + <title value="Admin should be able to mass update product statuses in store view scope using the Mysql search engine"/> + <description value="Admin should be able to mass update product statuses in store view scope using the Mysql search engine"/> + <severity value="AVERAGE"/> + <testCaseId value="MC-20471"/> + <group value="Catalog"/> + <group value="Product Attributes"/> + <group value="SearchEngineMysql"/> + </annotations> + <before> + <actionGroup ref="AdminLoginActionGroup" stepKey="loginAsAdmin"/> + + <!--Create Website --> + <actionGroup ref="AdminCreateWebsiteActionGroup" stepKey="createAdditionalWebsite"> + <argument name="newWebsiteName" value="Second Website"/> + <argument name="websiteCode" value="second_website"/> + </actionGroup> + + <!--Create Store --> + <actionGroup ref="AdminCreateNewStoreGroupActionGroup" stepKey="createNewStore"> + <argument name="website" value="Second Website"/> + <argument name="storeGroupName" value="Second Store"/> + <argument name="storeGroupCode" value="second_store"/> + </actionGroup> + + <!--Create Store view --> + <amOnPage url="{{AdminSystemStorePage.url}}" stepKey="amOnAdminSystemStorePage"/> + <waitForPageLoad stepKey="waitForSystemStorePage"/> + <click selector="{{AdminStoresMainActionsSection.createStoreViewButton}}" stepKey="createStoreViewButton"/> + <waitForPageLoad stepKey="waitForProductPageLoad"/> + <waitForElementVisible selector="//legend[contains(., 'Store View Information')]" stepKey="waitForNewStorePageToOpen"/> + <selectOption userInput="Second Store" selector="{{AdminNewStoreSection.storeGrpDropdown}}" stepKey="selectStoreGroup"/> + <fillField userInput="Second Store View" selector="{{AdminNewStoreSection.storeNameTextField}}" stepKey="fillStoreViewName"/> + <fillField userInput="second_store_view" selector="{{AdminNewStoreSection.storeCodeTextField}}" stepKey="fillStoreViewCode"/> + <selectOption selector="{{AdminNewStoreSection.statusDropdown}}" userInput="1" stepKey="enableStoreViewStatus"/> + <click selector="{{AdminNewStoreViewActionsSection.saveButton}}" stepKey="clickSaveStoreView"/> + <waitForElementVisible selector="{{AdminConfirmationModalSection.ok}}" stepKey="waitForModal"/> + <see selector="{{AdminConfirmationModalSection.title}}" userInput="Warning message" stepKey="seeWarning"/> + <click selector="{{AdminConfirmationModalSection.ok}}" stepKey="dismissModal"/> + <waitForPageLoad stepKey="waitForPageLoad2" time="180"/> + <waitForElementVisible selector="{{AdminStoresGridSection.storeFilterTextField}}" time="150" stepKey="waitForPageReolad"/> + <see userInput="You saved the store view." stepKey="seeSavedMessage"/> + + <!--Create a Simple Product 1 --> + <actionGroup ref="CreateSimpleProductAndAddToWebsiteActionGroup" stepKey="createSimpleProduct1"> + <argument name="product" value="simpleProductForMassUpdate"/> + <argument name="website" value="Second Website"/> + </actionGroup> + + <!--Create a Simple Product 2 --> + <actionGroup ref="CreateSimpleProductAndAddToWebsiteActionGroup" stepKey="createSimpleProduct2"> + <argument name="product" value="simpleProductForMassUpdate2"/> + <argument name="website" value="Second Website"/> + </actionGroup> + </before> + <after> + <!--Delete website --> + <actionGroup ref="AdminDeleteWebsiteActionGroup" stepKey="deleteSecondWebsite"> + <argument name="websiteName" value="Second Website"/> + </actionGroup> + <amOnPage url="{{AdminProductIndexPage.url}}" stepKey="navigateToProductIndex"/> + + <!--Delete Products --> + <actionGroup ref="DeleteProductActionGroup" stepKey="deleteProduct1"> + <argument name="productName" value="simpleProductForMassUpdate.name"/> + </actionGroup> + <actionGroup ref="DeleteProductActionGroup" stepKey="deleteProduct2"> + <argument name="productName" value="simpleProductForMassUpdate2.name"/> + </actionGroup> + <actionGroup ref="AdminLogoutActionGroup" stepKey="amOnLogoutPage"/> + </after> + + <!-- Search and select products --> + <amOnPage url="{{AdminProductIndexPage.url}}" stepKey="navigateToProductIndex"/> + <waitForPageLoad stepKey="waitForProductIndexPageLoad"/> + <actionGroup ref="SearchProductGridByKeyword2ActionGroup" stepKey="searchByKeyword"> + <argument name="keyword" value="{{simpleProductForMassUpdate.keyword}}"/> + </actionGroup> + <actionGroup ref="SortProductsByIdDescendingActionGroup" stepKey="sortProductsByIdDescending"/> + + <!-- Filter to Second Store View --> + <actionGroup ref="AdminFilterStoreViewActionGroup" stepKey="filterStoreView"> + <argument name="customStore" value="'Second Store View'"/> + </actionGroup> + + <!-- Select Product 2 --> + <click selector="{{AdminProductGridSection.productGridCheckboxOnRow('2')}}" stepKey="clickCheckbox2"/> + + <!-- Mass update attributes --> + <click selector="{{AdminProductGridSection.bulkActionDropdown}}" stepKey="clickDropdown"/> + <click selector="{{AdminProductGridSection.bulkActionOption('Change status')}}" stepKey="clickOption"/> + <click selector="{{AdminProductGridSection.bulkActionOption('Disable')}}" stepKey="clickDisabled"/> + <waitForPageLoad stepKey="waitForBulkUpdatePage"/> + + <!-- Verify Product Statuses --> + <see selector="{{AdminProductGridSection.productGridContentsOnRow('1')}}" userInput="Enabled" stepKey="checkIfProduct1IsEnabled"/> + <see selector="{{AdminProductGridSection.productGridContentsOnRow('2')}}" userInput="Disabled" stepKey="checkIfProduct2IsDisabled"/> + + <!-- Filter to Default Store View --> + <actionGroup ref="AdminFilterStoreViewActionGroup" stepKey="filterDefaultStoreView"> + <argument name="customStore" value="'Default'"/> + </actionGroup> + + <!-- Verify Product Statuses --> + <see selector="{{AdminProductGridSection.productGridContentsOnRow('1')}}" userInput="Enabled" stepKey="checkIfDefaultViewProduct1IsEnabled"/> + <see selector="{{AdminProductGridSection.productGridContentsOnRow('2')}}" userInput="Enabled" stepKey="checkIfDefaultViewProduct2IsEnabled"/> + + <!-- Assert on storefront default view --> + <actionGroup ref="GoToStoreViewAdvancedCatalogSearchActionGroup" stepKey="GoToStoreViewAdvancedCatalogSearchActionGroupDefault"/> + <actionGroup ref="StorefrontAdvancedCatalogSearchByProductNameAndDescriptionActionGroup" stepKey="searchByNameDefault"> + <argument name="name" value="{{simpleProductForMassUpdate.keyword}}"/> + <argument name="description" value=""/> + </actionGroup> + <actionGroup ref="StorefrontCheckAdvancedSearchResultActionGroup" stepKey="StorefrontCheckAdvancedSearchResultDefault"/> + <see userInput="2 items" selector="{{StorefrontCatalogSearchAdvancedResultMainSection.itemFound}}" stepKey="seeInDefault"/> + + <!-- Enable the product in Default store view --> + <amOnPage url="{{AdminProductIndexPage.url}}" stepKey="navigateToProductIndex2"/> + <waitForPageLoad stepKey="waitForProductIndexPageLoad2"/> + <click selector="{{AdminProductGridSection.productGridCheckboxOnRow('1')}}" stepKey="clickCheckboxDefaultStoreView"/> + <click selector="{{AdminProductGridSection.productGridCheckboxOnRow('2')}}" stepKey="clickCheckboxDefaultStoreView2"/> + + <!-- Mass update attributes --> + <click selector="{{AdminProductGridSection.bulkActionDropdown}}" stepKey="clickDropdownDefaultStoreView"/> + <click selector="{{AdminProductGridSection.bulkActionOption('Change status')}}" stepKey="clickOptionDefaultStoreView"/> + <click selector="{{AdminProductGridSection.bulkActionOption('Disable')}}" stepKey="clickDisabledDefaultStoreView"/> + <waitForPageLoad stepKey="waitForBulkUpdatePageDefaultStoreView"/> + <see selector="{{AdminProductGridSection.productGridContentsOnRow('1')}}" userInput="Disabled" stepKey="checkIfProduct2IsDisabledDefaultStoreView"/> + + <!-- Assert on storefront default view --> + <actionGroup ref="GoToStoreViewAdvancedCatalogSearchActionGroup" stepKey="GoToStoreViewAdvancedCatalogSearchActionGroupDefault2"/> + <actionGroup ref="StorefrontAdvancedCatalogSearchByProductNameAndDescriptionActionGroup" stepKey="searchByNameDefault2"> + <argument name="name" value="{{simpleProductForMassUpdate.name}}"/> + <argument name="description" value=""/> + </actionGroup> + <actionGroup ref="StorefrontCheckAdvancedSearchResultActionGroup" stepKey="StorefrontCheckAdvancedSearchResultDefault2"/> + <see userInput="We can't find any items matching these search criteria." selector="{{StorefrontCatalogSearchAdvancedResultMainSection.message}}" stepKey="seeInDefault2"/> + </test> +</tests> diff --git a/app/code/Magento/Catalog/Test/Mftf/Test/AdminMassUpdateProductStatusStoreViewScopeTest/AdminMassUpdateProductStatusStoreViewScopeTest.xml b/app/code/Magento/Catalog/Test/Mftf/Test/AdminMassUpdateProductStatusStoreViewScopeTest/AdminMassUpdateProductStatusStoreViewScopeTest.xml new file mode 100644 index 0000000000000..54921d3fc2dda --- /dev/null +++ b/app/code/Magento/Catalog/Test/Mftf/Test/AdminMassUpdateProductStatusStoreViewScopeTest/AdminMassUpdateProductStatusStoreViewScopeTest.xml @@ -0,0 +1,160 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + /** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +--> +<tests xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/testSchema.xsd"> + <test name="AdminMassUpdateProductStatusStoreViewScopeTest"> + <annotations> + <features value="Catalog"/> + <stories value="Mass update product status"/> + <title value="Admin should be able to mass update product statuses in store view scope"/> + <description value="Admin should be able to mass update product statuses in store view scope"/> + <severity value="AVERAGE"/> + <testCaseId value="MAGETWO-59361"/> + <group value="Catalog"/> + <group value="Product Attributes"/> + <group value="SearchEngineElasticsearch"/> + </annotations> + <before> + <actionGroup ref="AdminLoginActionGroup" stepKey="loginAsAdmin"/> + + <!--Create Website --> + <actionGroup ref="AdminCreateWebsiteActionGroup" stepKey="createAdditionalWebsite"> + <argument name="newWebsiteName" value="Second Website"/> + <argument name="websiteCode" value="second_website"/> + </actionGroup> + + <!--Create Store --> + <actionGroup ref="AdminCreateNewStoreGroupActionGroup" stepKey="createNewStore"> + <argument name="website" value="Second Website"/> + <argument name="storeGroupName" value="Second Store"/> + <argument name="storeGroupCode" value="second_store"/> + </actionGroup> + + <!--Create Store view --> + <amOnPage url="{{AdminSystemStorePage.url}}" stepKey="amOnAdminSystemStorePage"/> + <waitForPageLoad stepKey="waitForSystemStorePage"/> + <click selector="{{AdminStoresMainActionsSection.createStoreViewButton}}" stepKey="createStoreViewButton"/> + <waitForPageLoad stepKey="waitForProductPageLoad"/> + <waitForElementVisible selector="//legend[contains(., 'Store View Information')]" stepKey="waitForNewStorePageToOpen"/> + <selectOption userInput="Second Store" selector="{{AdminNewStoreSection.storeGrpDropdown}}" stepKey="selectStoreGroup"/> + <fillField userInput="Second Store View" selector="{{AdminNewStoreSection.storeNameTextField}}" stepKey="fillStoreViewName"/> + <fillField userInput="second_store_view" selector="{{AdminNewStoreSection.storeCodeTextField}}" stepKey="fillStoreViewCode"/> + <selectOption selector="{{AdminNewStoreSection.statusDropdown}}" userInput="1" stepKey="enableStoreViewStatus"/> + <click selector="{{AdminNewStoreViewActionsSection.saveButton}}" stepKey="clickSaveStoreView"/> + <waitForElementVisible selector="{{AdminConfirmationModalSection.ok}}" stepKey="waitForModal"/> + <see selector="{{AdminConfirmationModalSection.title}}" userInput="Warning message" stepKey="seeWarning"/> + <click selector="{{AdminConfirmationModalSection.ok}}" stepKey="dismissModal"/> + <waitForPageLoad stepKey="waitForPageLoad2" time="180"/> + <waitForElementVisible selector="{{AdminStoresGridSection.storeFilterTextField}}" time="150" stepKey="waitForPageReolad"/> + <see userInput="You saved the store view." stepKey="seeSavedMessage"/> + + <!--Create a Simple Product 1 --> + <actionGroup ref="CreateSimpleProductAndAddToWebsiteActionGroup" stepKey="createSimpleProduct1"> + <argument name="product" value="simpleProductForMassUpdate"/> + <argument name="website" value="Second Website"/> + </actionGroup> + + <!--Create a Simple Product 2 --> + <actionGroup ref="CreateSimpleProductAndAddToWebsiteActionGroup" stepKey="createSimpleProduct2"> + <argument name="product" value="simpleProductForMassUpdate2"/> + <argument name="website" value="Second Website"/> + </actionGroup> + </before> + <after> + <!--Delete website --> + <actionGroup ref="AdminDeleteWebsiteActionGroup" stepKey="deleteSecondWebsite"> + <argument name="websiteName" value="Second Website"/> + </actionGroup> + <amOnPage url="{{AdminProductIndexPage.url}}" stepKey="navigateToProductIndex"/> + + <!--Delete Products --> + <actionGroup ref="DeleteProductActionGroup" stepKey="deleteProduct1"> + <argument name="productName" value="simpleProductForMassUpdate.name"/> + </actionGroup> + <actionGroup ref="DeleteProductActionGroup" stepKey="deleteProduct2"> + <argument name="productName" value="simpleProductForMassUpdate2.name"/> + </actionGroup> + <actionGroup ref="AdminLogoutActionGroup" stepKey="amOnLogoutPage"/> + </after> + + <!-- Search and select products --> + <amOnPage url="{{AdminProductIndexPage.url}}" stepKey="navigateToProductIndex"/> + <waitForPageLoad stepKey="waitForProductIndexPageLoad"/> + <actionGroup ref="SearchProductGridByKeyword2ActionGroup" stepKey="searchByKeyword"> + <argument name="keyword" value="{{simpleProductForMassUpdate.keyword}}"/> + </actionGroup> + <actionGroup ref="SortProductsByIdDescendingActionGroup" stepKey="sortProductsByIdDescending"/> + + <!-- Filter to Second Store View --> + <actionGroup ref="AdminFilterStoreViewActionGroup" stepKey="filterStoreView"> + <argument name="customStore" value="'Second Store View'"/> + </actionGroup> + + <!-- Select Product 2 --> + <click selector="{{AdminProductGridSection.productGridCheckboxOnRow('2')}}" stepKey="clickCheckbox2"/> + + <!-- Mass update attributes --> + <click selector="{{AdminProductGridSection.bulkActionDropdown}}" stepKey="clickDropdown"/> + <click selector="{{AdminProductGridSection.bulkActionOption('Change status')}}" stepKey="clickOption"/> + <click selector="{{AdminProductGridSection.bulkActionOption('Disable')}}" stepKey="clickDisabled"/> + <waitForPageLoad stepKey="waitForBulkUpdatePage"/> + + <!-- Verify Product Statuses --> + <see selector="{{AdminProductGridSection.productGridContentsOnRow('1')}}" userInput="Enabled" stepKey="checkIfProduct1IsEnabled"/> + <see selector="{{AdminProductGridSection.productGridContentsOnRow('2')}}" userInput="Disabled" stepKey="checkIfProduct2IsDisabled"/> + + <!-- Filter to Default Store View --> + <actionGroup ref="AdminFilterStoreViewActionGroup" stepKey="filterDefaultStoreView"> + <argument name="customStore" value="'Default'"/> + </actionGroup> + + <!-- Verify Product Statuses --> + <see selector="{{AdminProductGridSection.productGridContentsOnRow('1')}}" userInput="Enabled" stepKey="checkIfDefaultViewProduct1IsEnabled"/> + <see selector="{{AdminProductGridSection.productGridContentsOnRow('2')}}" userInput="Enabled" stepKey="checkIfDefaultViewProduct2IsEnabled"/> + + <!-- Assert on storefront default view with first product --> + <actionGroup ref="GoToStoreViewAdvancedCatalogSearchActionGroup" stepKey="GoToStoreViewAdvancedCatalogSearchActionGroupDefault"/> + <actionGroup ref="StorefrontAdvancedCatalogSearchByProductNameAndDescriptionActionGroup" stepKey="searchByNameDefault"> + <argument name="name" value="{{simpleProductForMassUpdate.name}}"/> + <argument name="description" value=""/> + </actionGroup> + <actionGroup ref="StorefrontCheckAdvancedSearchResultActionGroup" stepKey="StorefrontCheckAdvancedSearchResultDefault"/> + <see userInput="1 item" selector="{{StorefrontCatalogSearchAdvancedResultMainSection.itemFound}}" stepKey="seeInDefault"/> + + <!-- Assert on storefront default view with second product --> + <actionGroup ref="GoToStoreViewAdvancedCatalogSearchActionGroup" stepKey="GoToStoreViewAdvancedCatalogSearchActionGroupDefaultToSearchSecondProduct"/> + <actionGroup ref="StorefrontAdvancedCatalogSearchByProductNameAndDescriptionActionGroup" stepKey="searchByNameDefaultWithSecondProduct"> + <argument name="name" value="{{simpleProductForMassUpdate2.name}}"/> + <argument name="description" value=""/> + </actionGroup> + <actionGroup ref="StorefrontCheckAdvancedSearchResultActionGroup" stepKey="StorefrontCheckAdvancedSearchResultDefaultForSecondProduct"/> + <see userInput="1 item" selector="{{StorefrontCatalogSearchAdvancedResultMainSection.itemFound}}" stepKey="seeInDefaultSecondProductResults"/> + + <!--Enable the product in Default store view--> + <amOnPage url="{{AdminProductIndexPage.url}}" stepKey="navigateToProductIndex2"/> + <waitForPageLoad stepKey="waitForProductIndexPageLoad2"/> + <click selector="{{AdminProductGridSection.productGridCheckboxOnRow('1')}}" stepKey="clickCheckboxDefaultStoreView"/> + <click selector="{{AdminProductGridSection.productGridCheckboxOnRow('2')}}" stepKey="clickCheckboxDefaultStoreView2"/> + + <!-- Mass update attributes --> + <click selector="{{AdminProductGridSection.bulkActionDropdown}}" stepKey="clickDropdownDefaultStoreView"/> + <click selector="{{AdminProductGridSection.bulkActionOption('Change status')}}" stepKey="clickOptionDefaultStoreView"/> + <click selector="{{AdminProductGridSection.bulkActionOption('Disable')}}" stepKey="clickDisabledDefaultStoreView"/> + <waitForPageLoad stepKey="waitForBulkUpdatePageDefaultStoreView"/> + <see selector="{{AdminProductGridSection.productGridContentsOnRow('1')}}" userInput="Disabled" stepKey="checkIfProduct2IsDisabledDefaultStoreView"/> + + <!-- Assert on storefront default view --> + <actionGroup ref="GoToStoreViewAdvancedCatalogSearchActionGroup" stepKey="GoToStoreViewAdvancedCatalogSearchActionGroupDefault2"/> + <actionGroup ref="StorefrontAdvancedCatalogSearchByProductNameAndDescriptionActionGroup" stepKey="searchByNameDefault2"> + <argument name="name" value="{{simpleProductForMassUpdate.name}}"/> + <argument name="description" value=""/> + </actionGroup> + <actionGroup ref="StorefrontCheckAdvancedSearchResultActionGroup" stepKey="StorefrontCheckAdvancedSearchResultDefault2"/> + <see userInput="We can't find any items matching these search criteria." selector="{{StorefrontCatalogSearchAdvancedResultMainSection.message}}" stepKey="seeInDefault2"/> + </test> +</tests> diff --git a/app/code/Magento/Catalog/Test/Mftf/Test/AdminMoveAnchoredCategoryTest.xml b/app/code/Magento/Catalog/Test/Mftf/Test/AdminMoveAnchoredCategoryTest.xml index 551b3437cb856..61024f1cb3d35 100644 --- a/app/code/Magento/Catalog/Test/Mftf/Test/AdminMoveAnchoredCategoryTest.xml +++ b/app/code/Magento/Catalog/Test/Mftf/Test/AdminMoveAnchoredCategoryTest.xml @@ -13,8 +13,9 @@ <stories value="Edit categories"/> <title value="Admin should be able to move a category via categories tree and changes should be applied on frontend without a forced cache cleaning"/> <description value="Admin should be able to move a category via categories tree and changes should be applied on frontend without a forced cache cleaning"/> - <severity value="CRITICAL"/> - <testCaseId value="MAGETWO-76273"/> + <severity value="BLOCKER"/> + <testCaseId value="MC-10022"/> + <useCaseId value="MAGETWO-89248"/> <group value="category"/> </annotations> <before> @@ -29,94 +30,96 @@ <createData entity="_defaultProduct" stepKey="productTwo"> <requiredEntity createDataKey="simpleSubCategoryOne"/> </createData> + <magentoCron groups="index" stepKey="RunToScheduleJobs"/> + <actionGroup ref="AdminLoginActionGroup" stepKey="loginAsAdmin"/> </before> + <after> - <actionGroup ref="logout" stepKey="logoutAdminUserAfterTest"/> <deleteData createDataKey="productOne" stepKey="deleteProductOne"/> <deleteData createDataKey="productTwo" stepKey="deleteProductTwo"/> <deleteData createDataKey="simpleSubCategoryWithParent" stepKey="deleteSubcategoryWithParent"/> + <deleteData createDataKey="simpleSubCategoryOne" stepKey="deleteSubcategoryOne"/> <deleteData createDataKey="simpleSubCategoryTwo" stepKey="deleteSubcategoryTwo"/> + <actionGroup ref="AdminLogoutActionGroup" stepKey="logoutAdminUserAfterTest"/> </after> + <!--Move category one to category two--> - <actionGroup ref="LoginAsAdmin" stepKey="loginAsAdmin"/> <amOnPage url="{{AdminCategoryPage.url}}" stepKey="navigateToAdminCategoryPage"/> <waitForPageLoad stepKey="waitForAdminCategoryPageLoad1"/> <actionGroup ref="MoveCategoryActionGroup" stepKey="moveSimpleSubCategoryOneToSimpleSubCategoryTwo"> - <argument name="childCategory" value="$$simpleSubCategoryOne.name$$"/> - <argument name="parentCategory" value="$$simpleSubCategoryTwo.name$$"/> + <argument name="childCategory" value="$simpleSubCategoryOne.name$"/> + <argument name="parentCategory" value="$simpleSubCategoryTwo.name$"/> </actionGroup> <!--Verify that navigation menu categories level is correct--> - <amOnPage url="/" stepKey="amOnStorefrontPage1"/> - <waitForPageLoad stepKey="waitForPageToLoadAfterHomePageOpened1"/> - <seeElement selector="{{StorefrontNavigationSection.topCategory($$simpleSubCategoryTwo.name$$)}}" stepKey="verifyThatTopCategoryIsSubCategoryTwo"/> - <moveMouseOver selector="{{StorefrontNavigationSection.topCategory($$simpleSubCategoryTwo.name$$)}}" stepKey="mouseOverSubCategoryTwo"/> + <actionGroup ref="StorefrontOpenHomePageActionGroup" stepKey="goToHomePage1"/> + <seeElement selector="{{StorefrontNavigationSection.topCategory($simpleSubCategoryTwo.name$)}}" stepKey="verifyThatTopCategoryIsSubCategoryTwo"/> + <moveMouseOver selector="{{StorefrontNavigationSection.topCategory($simpleSubCategoryTwo.name$)}}" stepKey="mouseOverSubCategoryTwo"/> <waitForAjaxLoad stepKey="waitForAjaxOnMouseOverSubCategoryTwo"/> - <seeElement selector="{{StorefrontNavigationSection.subCategory($$simpleSubCategoryOne.name$$)}}" stepKey="verifyThatFirstLevelIsSubCategoryOne"/> - <moveMouseOver selector="{{StorefrontNavigationSection.subCategory($$simpleSubCategoryOne.name$$)}}" stepKey="mouseOverSubCategoryOne"/> + <seeElement selector="{{StorefrontNavigationSection.subCategory($simpleSubCategoryOne.name$)}}" stepKey="verifyThatFirstLevelIsSubCategoryOne"/> + <moveMouseOver selector="{{StorefrontNavigationSection.subCategory($simpleSubCategoryOne.name$)}}" stepKey="mouseOverSubCategoryOne"/> <waitForAjaxLoad stepKey="waitForAjaxOnMouseOverSubCategoryOne"/> - <seeElement selector="{{StorefrontNavigationSection.subCategory($$simpleSubCategoryWithParent.name$$)}}" stepKey="verifyThatSecondLevelIsSubCategoryWithParent1"/> + <seeElement selector="{{StorefrontNavigationSection.subCategory($simpleSubCategoryWithParent.name$)}}" stepKey="verifyThatSecondLevelIsSubCategoryWithParent1"/> <!--Open category one via navigation menu. Verify that subcategory is shown in layered navigation--> - <click selector="{{StorefrontNavigationSection.subCategory($$simpleSubCategoryOne.name$$)}}" stepKey="openSimpleSubCategoryOneByNavigationMenu1"/> + <click selector="{{StorefrontNavigationSection.subCategory($simpleSubCategoryOne.name$)}}" stepKey="openSimpleSubCategoryOneByNavigationMenu1"/> <actionGroup ref="CheckItemInLayeredNavigationActionGroup" stepKey="verifySimpleSubCategoryWithParentInLayeredNavigation1"> <argument name="itemType" value="Category"/> - <argument name="itemName" value="$$simpleSubCategoryWithParent.name$$"/> + <argument name="itemName" value="$simpleSubCategoryWithParent.name$"/> </actionGroup> <!--Open category one by direct URL. Verify simple product is visible on it. Open this product and perform assertions--> <actionGroup ref="OpenProductFromCategoryPageActionGroup" stepKey="openFirstProductFromSubCategoryOneCategoryPage1"> - <argument name="category" value="$$simpleSubCategoryOne$$"/> - <argument name="product" value="$$productOne$$"/> + <argument name="category" value="$simpleSubCategoryOne$"/> + <argument name="product" value="$productOne$"/> </actionGroup> <see selector="{{StorefrontNavigationSection.breadcrumbs}}" userInput="Home" stepKey="seeHomePageInBreadcrumbs1"/> - <see selector="{{StorefrontNavigationSection.breadcrumbs}}" userInput="$$simpleSubCategoryTwo.name$$" stepKey="seeSubCategoryTwoInBreadcrumbsOnSubCategoryOne"/> - <see selector="{{StorefrontNavigationSection.breadcrumbs}}" userInput="$$simpleSubCategoryOne.name$$" stepKey="seeSubCategoryOneInBreadcrumbsOnSubCategoryOne1"/> - <see selector="{{StorefrontNavigationSection.breadcrumbs}}" userInput="$$productOne.name$$" stepKey="seeProductInBreadcrumbsOnSubCategoryOne1"/> + <see selector="{{StorefrontNavigationSection.breadcrumbs}}" userInput="$simpleSubCategoryTwo.name$" stepKey="seeSubCategoryTwoInBreadcrumbsOnSubCategoryOne"/> + <see selector="{{StorefrontNavigationSection.breadcrumbs}}" userInput="$simpleSubCategoryOne.name$" stepKey="seeSubCategoryOneInBreadcrumbsOnSubCategoryOne1"/> + <see selector="{{StorefrontNavigationSection.breadcrumbs}}" userInput="$productOne.name$" stepKey="seeProductInBreadcrumbsOnSubCategoryOne1"/> <!--Open category two by direct URL. Verify simple product is visible on it. Open this product and perform assertions--> <actionGroup ref="OpenProductFromCategoryPageActionGroup" stepKey="openFirstProductFromSubCategoryWithParentCategoryPage"> - <argument name="category" value="$$simpleSubCategoryWithParent$$"/> - <argument name="product" value="$$productOne$$"/> + <argument name="category" value="$simpleSubCategoryWithParent$"/> + <argument name="product" value="$productOne$"/> </actionGroup> <see selector="{{StorefrontNavigationSection.breadcrumbs}}" userInput="Home" stepKey="seeHomePageInBreadcrumbsOnSubCategoryWithParent"/> - <see selector="{{StorefrontNavigationSection.breadcrumbs}}" userInput="$$simpleSubCategoryTwo.name$$" stepKey="seeSubCategoryTwoInBreadcrumbsOnSubCategoryWithParent"/> - <see selector="{{StorefrontNavigationSection.breadcrumbs}}" userInput="$$simpleSubCategoryOne.name$$" stepKey="seeSubCategoryOneInBreadcrumbsOnSubCategoryWithParent"/> - <see selector="{{StorefrontNavigationSection.breadcrumbs}}" userInput="$$simpleSubCategoryOne.name$$" stepKey="seeSubCategoryWithParentInBreadcrumbsOnSubCategoryWithParent"/> - <see selector="{{StorefrontNavigationSection.breadcrumbs}}" userInput="$$productOne.name$$" stepKey="seeProductInBreadcrumbsOnSubCategoryWithParent"/> + <see selector="{{StorefrontNavigationSection.breadcrumbs}}" userInput="$simpleSubCategoryTwo.name$" stepKey="seeSubCategoryTwoInBreadcrumbsOnSubCategoryWithParent"/> + <see selector="{{StorefrontNavigationSection.breadcrumbs}}" userInput="$simpleSubCategoryOne.name$" stepKey="seeSubCategoryOneInBreadcrumbsOnSubCategoryWithParent"/> + <see selector="{{StorefrontNavigationSection.breadcrumbs}}" userInput="$simpleSubCategoryOne.name$" stepKey="seeSubCategoryWithParentInBreadcrumbsOnSubCategoryWithParent"/> + <see selector="{{StorefrontNavigationSection.breadcrumbs}}" userInput="$productOne.name$" stepKey="seeProductInBreadcrumbsOnSubCategoryWithParent"/> <!--Move category one to the same level as category two--> <amOnPage url="{{AdminCategoryPage.url}}" stepKey="navigateToAdminCategoryPage2"/> <waitForPageLoad stepKey="waitForAdminCategoryPageLoad2"/> <actionGroup ref="MoveCategoryActionGroup" stepKey="moveSimpleSubCategoryOneToDefaultCategory"> - <argument name="childCategory" value="$$simpleSubCategoryOne.name$$"/> + <argument name="childCategory" value="$simpleSubCategoryOne.name$"/> <argument name="parentCategory" value="Default Category"/> </actionGroup> <!--Verify that navigation menu categories level is correct--> - <amOnPage url="/" stepKey="amOnStorefrontPage2"/> - <waitForPageLoad stepKey="waitForPageToLoadAfterHomePageOpened2"/> - <seeElement selector="{{StorefrontNavigationSection.topCategory($$simpleSubCategoryOne.name$$)}}" stepKey="verifyThatSubCategoryOneIsTopCategory"/> - <seeElement selector="{{StorefrontNavigationSection.topCategory($$simpleSubCategoryTwo.name$$)}}" stepKey="verifyThatSubCategoryTwoIsTopCategory"/> - <moveMouseOver selector="{{StorefrontNavigationSection.topCategory($$simpleSubCategoryOne.name$$)}}" stepKey="mouseOverTopSubCategoryOne"/> + <actionGroup ref="StorefrontOpenHomePageActionGroup" stepKey="goToHomePage2"/> + <seeElement selector="{{StorefrontNavigationSection.topCategory($simpleSubCategoryOne.name$)}}" stepKey="verifyThatSubCategoryOneIsTopCategory"/> + <seeElement selector="{{StorefrontNavigationSection.topCategory($simpleSubCategoryTwo.name$)}}" stepKey="verifyThatSubCategoryTwoIsTopCategory"/> + <moveMouseOver selector="{{StorefrontNavigationSection.topCategory($simpleSubCategoryOne.name$)}}" stepKey="mouseOverTopSubCategoryOne"/> <waitForAjaxLoad stepKey="waitForAjaxOnMouseOverTopSubCategoryOne"/> - <seeElement selector="{{StorefrontNavigationSection.subCategory($$simpleSubCategoryWithParent.name$$)}}" stepKey="verifyThatSecondLevelIsSubCategoryWithParent2"/> + <seeElement selector="{{StorefrontNavigationSection.subCategory($simpleSubCategoryWithParent.name$)}}" stepKey="verifyThatSecondLevelIsSubCategoryWithParent2"/> <!--Open category one via navigation menu. Verify that subcategory is shown in layered navigation--> - <click selector="{{StorefrontNavigationSection.topCategory($$simpleSubCategoryOne.name$$)}}" stepKey="openSimpleSubCategoryOneByNavigationMenu2"/> + <click selector="{{StorefrontNavigationSection.topCategory($simpleSubCategoryOne.name$)}}" stepKey="openSimpleSubCategoryOneByNavigationMenu2"/> <actionGroup ref="CheckItemInLayeredNavigationActionGroup" stepKey="verifySimpleSubCategoryWithParentInLayeredNavigation2"> <argument name="itemType" value="Category"/> - <argument name="itemName" value="$$simpleSubCategoryWithParent.name$$"/> + <argument name="itemName" value="$simpleSubCategoryWithParent.name$"/> </actionGroup> <!--Open category one by direct URL. Verify simple product is visible on it. Open this product and perform assertions--> <actionGroup ref="OpenProductFromCategoryPageActionGroup" stepKey="openFirstProductFromSubCategoryOneCategoryPage2"> - <argument name="category" value="$$simpleSubCategoryOne$$"/> - <argument name="product" value="$$productOne$$"/> + <argument name="category" value="$simpleSubCategoryOne$"/> + <argument name="product" value="$productOne$"/> </actionGroup> <see selector="{{StorefrontNavigationSection.breadcrumbs}}" userInput="Home" stepKey="seeHomePageInBreadcrumbs2"/> - <see selector="{{StorefrontNavigationSection.breadcrumbs}}" userInput="$$simpleSubCategoryOne.name$$" stepKey="seeSubCategoryOneInBreadcrumbsOnSubCategoryOne2"/> - <see selector="{{StorefrontNavigationSection.breadcrumbs}}" userInput="$$productOne.name$$" stepKey="seeProductInBreadcrumbsOnSubCategoryOne2"/> + <see selector="{{StorefrontNavigationSection.breadcrumbs}}" userInput="$simpleSubCategoryOne.name$" stepKey="seeSubCategoryOneInBreadcrumbsOnSubCategoryOne2"/> + <see selector="{{StorefrontNavigationSection.breadcrumbs}}" userInput="$productOne.name$" stepKey="seeProductInBreadcrumbsOnSubCategoryOne2"/> <!--Open category subcategory by direct URL. Verify simple product is visible on it. Open this product and perform assertions--> <actionGroup ref="OpenProductFromCategoryPageActionGroup" stepKey="openFirstProductFromSubCategoryOneCategoryPage3"> - <argument name="category" value="$$simpleSubCategoryWithParent$$"/> - <argument name="product" value="$$productOne$$"/> + <argument name="category" value="$simpleSubCategoryWithParent$"/> + <argument name="product" value="$productOne$"/> </actionGroup> <see selector="{{StorefrontNavigationSection.breadcrumbs}}" userInput="Home" stepKey="seeHomePageInBreadcrumbs3"/> - <see selector="{{StorefrontNavigationSection.breadcrumbs}}" userInput="$$simpleSubCategoryOne.name$$" stepKey="seeSubCategoryOneInBreadcrumbsOnSubCategoryOne3"/> - <see selector="{{StorefrontNavigationSection.breadcrumbs}}" userInput="$$simpleSubCategoryOne.name$$" stepKey="seeSubCategoryWithParentInBreadcrumbsOnSubCategoryWithParent3"/> - <see selector="{{StorefrontNavigationSection.breadcrumbs}}" userInput="$$productOne.name$$" stepKey="seeProductInBreadcrumbsOnSubCategoryOne3"/> + <see selector="{{StorefrontNavigationSection.breadcrumbs}}" userInput="$simpleSubCategoryOne.name$" stepKey="seeSubCategoryOneInBreadcrumbsOnSubCategoryOne3"/> + <see selector="{{StorefrontNavigationSection.breadcrumbs}}" userInput="$simpleSubCategoryOne.name$" stepKey="seeSubCategoryWithParentInBreadcrumbsOnSubCategoryWithParent3"/> + <see selector="{{StorefrontNavigationSection.breadcrumbs}}" userInput="$productOne.name$" stepKey="seeProductInBreadcrumbsOnSubCategoryOne3"/> </test> -</tests> \ No newline at end of file +</tests> diff --git a/app/code/Magento/Catalog/Test/Mftf/Test/AdminMoveAnchoredCategoryToDefaultCategoryTest.xml b/app/code/Magento/Catalog/Test/Mftf/Test/AdminMoveAnchoredCategoryToDefaultCategoryTest.xml index a8a8ede297b44..1ea1f60f52a2c 100644 --- a/app/code/Magento/Catalog/Test/Mftf/Test/AdminMoveAnchoredCategoryToDefaultCategoryTest.xml +++ b/app/code/Magento/Catalog/Test/Mftf/Test/AdminMoveAnchoredCategoryToDefaultCategoryTest.xml @@ -18,14 +18,14 @@ <features value="Catalog"/> </annotations> <before> - <actionGroup ref="LoginAsAdmin" stepKey="loginToAdminPanel"/> + <actionGroup ref="AdminLoginActionGroup" stepKey="loginToAdminPanel"/> <createData entity="_defaultCategory" stepKey="createDefaultCategory"/> <createData entity="defaultSimpleProduct" stepKey="simpleProduct"/> </before> <after> <deleteData createDataKey="createDefaultCategory" stepKey="deleteDefaultCategory"/> <deleteData createDataKey="simpleProduct" stepKey="deleteSimpleProduct"/> - <actionGroup ref="logout" stepKey="logout"/> + <actionGroup ref="AdminLogoutActionGroup" stepKey="logout"/> </after> <!--Open Category Page--> diff --git a/app/code/Magento/Catalog/Test/Mftf/Test/AdminMoveCategoryAndCheckUrlRewritesTest.xml b/app/code/Magento/Catalog/Test/Mftf/Test/AdminMoveCategoryAndCheckUrlRewritesTest.xml index ba6e6a43674c3..39040c7e88205 100644 --- a/app/code/Magento/Catalog/Test/Mftf/Test/AdminMoveCategoryAndCheckUrlRewritesTest.xml +++ b/app/code/Magento/Catalog/Test/Mftf/Test/AdminMoveCategoryAndCheckUrlRewritesTest.xml @@ -14,16 +14,21 @@ <description value="Login as admin, move category from one to another and check category url rewrites"/> <testCaseId value="MC-6494"/> <features value="Catalog"/> - <severity value="CRITICAL"/> + <severity value="BLOCKER"/> <group value="mtf_migrated"/> </annotations> <before> - <actionGroup ref="LoginAsAdmin" stepKey="loginToAdminPanel"/> - <createData entity="_defaultCategory" stepKey="createDefaultCategory"/> + <actionGroup ref="AdminLoginActionGroup" stepKey="loginToAdminPanel"/> + <createData entity="FirstLevelSubCat" stepKey="createDefaultCategory"> + <field key="is_active">true</field> + </createData> + <!-- Perform reindex and flush cache --> + <magentoCLI command="indexer:reindex" stepKey="reindex"/> + <magentoCLI command="cache:flush" stepKey="flushCache"/> </before> <after> <deleteData createDataKey="createDefaultCategory" stepKey="deleteCategory"/> - <actionGroup ref="logout" stepKey="logout"/> + <actionGroup ref="AdminLogoutActionGroup" stepKey="logout"/> </after> <!--Open category page--> @@ -31,7 +36,7 @@ <waitForPageLoad stepKey="waitForPageToLoaded"/> <click selector="{{AdminCategorySidebarTreeSection.expandAll}}" stepKey="clickOnExpandTree"/> <waitForPageLoad stepKey="waitForCategoryToLoad"/> - <click selector="{{AdminCategorySidebarTreeSection.categoryInTree(_defaultCategory.name)}}" stepKey="selectCategory"/> + <click selector="{{AdminCategorySidebarTreeSection.categoryInTree(FirstLevelSubCat.name)}}" stepKey="selectCategory"/> <waitForPageLoad stepKey="waitForPageToLoad"/> <!--Create second level category--> @@ -54,18 +59,19 @@ <waitForPageLoad stepKey="waitForUrlRewritePage"/> <!--Search third level category Redirect Path, Target Path and Redirect Type--> - <fillField selector="{{AdminUrlRewriteIndexSection.requestPathFilter}}" userInput="{{SimpleSubCategory.name_lwr}}" stepKey="fillRedirectPathFilter"/> - <click selector="{{AdminUrlRewriteIndexSection.searchButton}}" stepKey="clickOnSearchButton"/> + <click selector="{{AdminDataGridHeaderSection.filters}}" stepKey="openUrlRewriteGridFilters"/> + <fillField selector="{{AdminDataGridHeaderSection.filterFieldInput('request_path')}}" userInput="{{SimpleSubCategory.name_lwr}}" stepKey="fillRequestPathFilter"/> + <click selector="{{AdminDataGridHeaderSection.applyFilters}}" stepKey="clickOrderApplyFilters"/> <waitForPageLoad stepKey="waitForPageToLoad0"/> <!--Verify Category RedirectType--> - <see stepKey="verifyTheRedirectType" selector="{{AdminUrlRewriteIndexSection.redirectTypeColumn('1')}}" userInput="No" /> + <see stepKey="verifyTheRedirectType" selector="{{AdminUrlRewriteIndexSection.gridCellByColumnRowNumber('1', 'Redirect Type')}}" userInput="No" /> <!--Verify Redirect Path --> - <see selector="{{AdminUrlRewriteIndexSection.requestPathColumn('1')}}" userInput="{{_defaultCategory.name_lwr}}2/{{SubCategory.name_lwr}}/{{SimpleSubCategory.name_lwr}}.html" stepKey="verifyTheRedirectPath"/> + <see selector="{{AdminUrlRewriteIndexSection.gridCellByColumnRowNumber('1', 'Request Path')}}" userInput="{{FirstLevelSubCat.name_lwr}}2/{{SubCategory.name_lwr}}/{{SimpleSubCategory.name_lwr}}.html" stepKey="verifyTheRedirectPath"/> <!--Verify Category Target Path--> - <see stepKey="verifyTheTargetPath" selector="{{AdminUrlRewriteIndexSection.targetPathColumn('1')}}" userInput="catalog/category/view/id/{$categoryId}"/> + <see stepKey="verifyTheTargetPath" selector="{{AdminUrlRewriteIndexSection.gridCellByColumnRowNumber('1', 'Target Path')}}" userInput="catalog/category/view/id/{$categoryId}"/> <!--Open Category Page --> <amOnPage url="{{AdminCategoryPage.url}}" stepKey="openAdminCategoryIndexPage1"/> @@ -74,7 +80,7 @@ <waitForPageLoad stepKey="waitForPageToLoad2"/> <!--Move the third level category under first level category --> - <dragAndDrop selector1="{{AdminCategorySidebarTreeSection.categoryInTree(SimpleSubCategory.name)}}" selector2="{{AdminCategorySidebarTreeSection.categoryInTree(_defaultCategory.name)}}" stepKey="moveCategory"/> + <dragAndDrop selector1="{{AdminCategorySidebarTreeSection.categoryInTree(SimpleSubCategory.name)}}" selector2="{{AdminCategorySidebarTreeSection.categoryInTree($$createDefaultCategory.name$$)}}" stepKey="m0oveCategory"/> <see selector="{{AdminCategoryModalSection.message}}" userInput="This operation can take a long time" stepKey="seeWarningMessage"/> <click selector="{{AdminCategoryModalSection.ok}}" stepKey="clickOkButtonOnWarningPopup"/> <waitForPageLoad stepKey="waitForPageToLoad3"/> @@ -83,31 +89,35 @@ <!--Open Url Rewrite page --> <amOnPage url="{{AdminUrlRewriteIndexPage.url}}" stepKey="openUrlRewriteIndexPage1"/> <waitForPageLoad stepKey="waitForUrlRewritePage1"/> - <fillField selector="{{AdminUrlRewriteIndexSection.requestPathFilter}}" userInput="{{_defaultCategory.name_lwr}}2/{{SimpleSubCategory.name_lwr}}.html" stepKey="fillCategoryUrlKey1"/> - <click selector="{{AdminUrlRewriteIndexSection.searchButton}}" stepKey="clickOnSearchButton1"/> + <click selector="{{AdminDataGridHeaderSection.filters}}" stepKey="openUrlRewriteGridFilters1"/> + <fillField selector="{{AdminDataGridHeaderSection.filterFieldInput('request_path')}}" userInput="{{FirstLevelSubCat.name_lwr}}2/{{SimpleSubCategory.name_lwr}}.html" stepKey="fillCategoryUrlKey1"/> + <click selector="{{AdminDataGridHeaderSection.applyFilters}}" stepKey="clickOrderApplyFilters1"/> <waitForPageLoad stepKey="waitForPageToLoad4"/> <!--Verify new Redirect Path after move --> - <see stepKey="verifyTheRequestPathAfterMove" selector="{{AdminUrlRewriteIndexSection.requestPathColumn('1')}}" userInput="{{_defaultCategory.name_lwr}}2/{{SimpleSubCategory.name_lwr}}.html" /> + <see stepKey="verifyTheRequestPathAfterMove" selector="{{AdminUrlRewriteIndexSection.gridCellByColumnRowNumber('1', 'Request Path')}}" userInput="{{FirstLevelSubCat.name_lwr}}2/{{SimpleSubCategory.name_lwr}}.html" /> <!--Verify new Target Path after move --> - <see stepKey="verifyTheTargetPathAfterMove" selector="{{AdminUrlRewriteIndexSection.targetPathColumn('1')}}" userInput="catalog/category/view/id/{$categoryId}" /> + <see stepKey="verifyTheTargetPathAfterMove" selector="{{AdminUrlRewriteIndexSection.gridCellByColumnRowNumber('1', 'Target Path')}}" userInput="catalog/category/view/id/{$categoryId}" /> <!--Verify new RedirectType after move --> - <see stepKey="verifyTheRedirectTypeAfterMove" selector="{{AdminUrlRewriteIndexSection.redirectTypeColumn('1')}}" userInput="No" /> + <see stepKey="verifyTheRedirectTypeAfterMove" selector="{{AdminUrlRewriteIndexSection.gridCellByColumnRowNumber('1', 'Redirect Type')}}" userInput="No" /> <!--Verify before move Redirect Path displayed with associated Target Path and Redirect Type--> - <fillField selector="{{AdminUrlRewriteIndexSection.requestPathFilter}}" userInput="{{SimpleSubCategory.name_lwr}}" stepKey="fillCategoryUrlKey2"/> - <click selector="{{AdminUrlRewriteIndexSection.searchButton}}" stepKey="clickOnSearchButton2"/> + <click selector="{{AdminDataGridHeaderSection.filters}}" stepKey="openUrlRewriteGridFilters2"/> + <fillField selector="{{AdminDataGridHeaderSection.filterFieldInput('request_path')}}" userInput="{{SimpleSubCategory.name_lwr}}" stepKey="fillCategoryUrlKey2"/> + <click selector="{{AdminDataGridHeaderSection.applyFilters}}" stepKey="clickOrderApplyFilters2"/> <waitForPageLoad stepKey="waitForPageToLoad5"/> - <see stepKey="verifyTheRedirectTypeAfterMove1" selector="{{AdminUrlRewriteIndexSection.redirectTypeColumn('1')}}" userInput="Permanent (301)" /> - <see stepKey="verifyTheRequestPathAfterMove1" selector="{{AdminUrlRewriteIndexSection.requestPathColumn('1')}}" userInput="{{_defaultCategory.name_lwr}}2/{{SubCategory.name_lwr}}/{{SimpleSubCategory.name_lwr}}.html" /> - <see stepKey="verifyTheTargetPathAfterMove1" selector="{{AdminUrlRewriteIndexSection.targetPathColumn('1')}}" userInput="{{_defaultCategory.name_lwr}}2/{{SimpleSubCategory.name_lwr}}.html" /> + + + <see stepKey="verifyTheRedirectTypeAfterMove1" selector="{{AdminUrlRewriteIndexSection.gridCellByColumnRowNumber('1', 'Redirect Type')}}" userInput="Permanent (301)" /> + <see stepKey="verifyTheRequestPathAfterMove1" selector="{{AdminUrlRewriteIndexSection.gridCellByColumnRowNumber('1', 'Request Path')}}" userInput="{{FirstLevelSubCat.name_lwr}}2/{{SubCategory.name_lwr}}/{{SimpleSubCategory.name_lwr}}.html" /> + <see stepKey="verifyTheTargetPathAfterMove1" selector="{{AdminUrlRewriteIndexSection.gridCellByColumnRowNumber('1', 'Target Path')}}" userInput="{{FirstLevelSubCat.name_lwr}}2/{{SimpleSubCategory.name_lwr}}.html" /> <!--Verify before move Redirect Path directs to the category page--> - <amOnPage url="{{_defaultCategory.name_lwr}}2/{{SubCategory.name_lwr}}/{{SimpleSubCategory.name_lwr}}.html" stepKey="openCategoryStoreFrontPage"/> + <amOnPage url="{{FirstLevelSubCat.name_lwr}}2/{{SubCategory.name_lwr}}/{{SimpleSubCategory.name_lwr}}.html" stepKey="openCategoryStoreFrontPage"/> <waitForPageLoad stepKey="waitForCategoryPageToLoad"/> - <seeElement selector="{{StorefrontHeaderSection.NavigationCategoryByName(_defaultCategory.name)}}" stepKey="seeCategoryOnStoreNavigationBar"/> + <seeElement selector="{{StorefrontHeaderSection.NavigationCategoryByName(FirstLevelSubCat.name)}}" stepKey="seeCategoryOnStoreNavigationBar"/> <seeElement selector="{{StorefrontCategoryMainSection.CategoryTitle(SimpleSubCategory.name)}}" stepKey="seeCategoryInTitle"/> </test> </tests> diff --git a/app/code/Magento/Catalog/Test/Mftf/Test/AdminMoveCategoryFromParentAnchoredCategoryTest.xml b/app/code/Magento/Catalog/Test/Mftf/Test/AdminMoveCategoryFromParentAnchoredCategoryTest.xml index 271d78ab9cdb0..7fdc6e7b9cf8f 100644 --- a/app/code/Magento/Catalog/Test/Mftf/Test/AdminMoveCategoryFromParentAnchoredCategoryTest.xml +++ b/app/code/Magento/Catalog/Test/Mftf/Test/AdminMoveCategoryFromParentAnchoredCategoryTest.xml @@ -18,7 +18,7 @@ <features value="Catalog"/> </annotations> <before> - <actionGroup ref="LoginAsAdmin" stepKey="loginToAdminPanel"/> + <actionGroup ref="AdminLoginActionGroup" stepKey="loginToAdminPanel"/> <createData entity="defaultSimpleProduct" stepKey="simpleProduct"/> <createData entity="_defaultCategory" stepKey="createDefaultCategory"/> </before> @@ -28,7 +28,7 @@ <actionGroup ref="DeleteCategoryActionGroup" stepKey="deleteCategory"> <argument name="categoryEntity" value="SimpleSubCategory"/> </actionGroup> - <actionGroup ref="logout" stepKey="logout"/> + <actionGroup ref="AdminLogoutActionGroup" stepKey="logout"/> </after> <!--Open Category page--> diff --git a/app/code/Magento/Catalog/Test/Mftf/Test/AdminMoveCategoryToAnotherPositionInCategoryTreeTest.xml b/app/code/Magento/Catalog/Test/Mftf/Test/AdminMoveCategoryToAnotherPositionInCategoryTreeTest.xml index 6c403fc7714eb..2909d17b1541b 100644 --- a/app/code/Magento/Catalog/Test/Mftf/Test/AdminMoveCategoryToAnotherPositionInCategoryTreeTest.xml +++ b/app/code/Magento/Catalog/Test/Mftf/Test/AdminMoveCategoryToAnotherPositionInCategoryTreeTest.xml @@ -14,13 +14,13 @@ <title value="Move Category to Another Position in Category Tree"/> <description value="Test log in to Move Category and Move Category to Another Position in Category Tree"/> <testCaseId value="MC-13612"/> - <severity value="CRITICAL"/> + <severity value="BLOCKER"/> <group value="catalog"/> <group value="mtf_migrated"/> </annotations> <before> - <actionGroup ref="LoginAsAdmin" stepKey="loginToAdminPanel"/> + <actionGroup ref="AdminLoginActionGroup" stepKey="loginToAdminPanel"/> <createData entity="_defaultCategory" stepKey="createDefaultCategory"/> </before> <after> @@ -28,7 +28,7 @@ <actionGroup ref="DeleteCategoryActionGroup" stepKey="SecondLevelSubCat"> <argument name="categoryEntity" value="SecondLevelSubCat"/> </actionGroup> - <actionGroup ref="logout" stepKey="logout"/> + <actionGroup ref="AdminLogoutActionGroup" stepKey="logout"/> </after> <!-- Open Category Page --> @@ -96,21 +96,23 @@ <!-- Open Url Rewrite page and see the url rewrite for the moved category --> <amOnPage url="{{AdminUrlRewriteIndexPage.url}}" stepKey="openUrlRewriteIndexPage"/> <waitForPageLoad stepKey="waitForUrlRewritePageLoad"/> - <fillField selector="{{AdminUrlRewriteIndexSection.requestPathFilter}}" userInput="{{SecondLevelSubCat.name_lwr}}.html" stepKey="fillCategoryUrlKey"/> - <click selector="{{AdminUrlRewriteIndexSection.searchButton}}" stepKey="clickOnSearchButton"/> - <waitForPageLoad stepKey="waitForUrlPageToLoad"/> + <click selector="{{AdminDataGridHeaderSection.filters}}" stepKey="openUrlRewriteGridFilters"/> + <fillField selector="{{AdminDataGridHeaderSection.filterFieldInput('request_path')}}" userInput="{{SecondLevelSubCat.name_lwr}}.html" stepKey="fillCategoryUrlKey"/> + <click selector="{{AdminDataGridHeaderSection.applyFilters}}" stepKey="clickOrderApplyFilters"/> + <waitForPageLoad stepKey="waitForSearch"/> <!-- Verify new Redirect Path after move --> - <see selector="{{AdminUrlRewriteIndexSection.requestPathColumn('2')}}" userInput="{{SecondLevelSubCat.name_lwr}}.html" stepKey="verifyTheRequestPathAfterMove"/> + <see selector="{{AdminUrlRewriteIndexSection.gridCellByColumnRowNumber('2', 'Request Path')}}" userInput="{{SecondLevelSubCat.name_lwr}}.html" stepKey="verifyTheRequestPathAfterMove"/> <!-- Verify new Target Path after move --> - <see selector="{{AdminUrlRewriteIndexSection.targetPathColumn('2')}}" userInput="catalog/category/view/id/{$categoryId}" stepKey="verifyTheTargetPathAfterMove"/> + <see selector="{{AdminUrlRewriteIndexSection.gridCellByColumnRowNumber('2', 'Target Path')}}" userInput="catalog/category/view/id/{$categoryId}" stepKey="verifyTheTargetPathAfterMove"/> <!-- Verify new RedirectType after move --> - <see selector="{{AdminUrlRewriteIndexSection.redirectTypeColumn('2')}}" userInput="No" stepKey="verifyTheRedirectTypeAfterMove"/> + <see selector="{{AdminUrlRewriteIndexSection.gridCellByColumnRowNumber('2', 'Redirect Type')}}" userInput="No" stepKey="verifyTheRedirectTypeAfterMove"/> <!-- Verify before move Redirect Path displayed with associated Target Path and Redirect Type--> - <fillField selector="{{AdminUrlRewriteIndexSection.requestPathFilter}}" userInput="{{SecondLevelSubCat.name_lwr}}" stepKey="fillTheCategoryUrlKey"/> - <click selector="{{AdminUrlRewriteIndexSection.searchButton}}" stepKey="clickOnSearchButton2"/> - <waitForPageLoad stepKey="waitForSearch"/> - <see selector="{{AdminUrlRewriteIndexSection.redirectTypeColumn('1')}}" userInput="Permanent (301)" stepKey="verifyTheRedirectTypeBeforeMove"/> - <see selector="{{AdminUrlRewriteIndexSection.requestPathColumn('1')}}" userInput="{{_defaultCategory.name_lwr}}2/{{FirstLevelSubCat.name_lwr}}/{{SecondLevelSubCat.name_lwr}}.html" stepKey="verifyTheRequestPathBeforeMove"/> - <see selector="{{AdminUrlRewriteIndexSection.targetPathColumn('1')}}" userInput="{{SecondLevelSubCat.name_lwr}}.html" stepKey="verifyTheTargetPathBeforeMove"/> + <click selector="{{AdminDataGridHeaderSection.filters}}" stepKey="openUrlRewriteGridFilters1"/> + <fillField selector="{{AdminDataGridHeaderSection.filterFieldInput('request_path')}}" userInput="{{SecondLevelSubCat.name_lwr}}" stepKey="fillTheCategoryUrlKey"/> + <click selector="{{AdminDataGridHeaderSection.applyFilters}}" stepKey="clickOrderApplyFilters1"/> + <waitForPageLoad stepKey="waitForSearch1"/> + <see selector="{{AdminUrlRewriteIndexSection.gridCellByColumnRowNumber('1', 'Redirect Type')}}" userInput="Permanent (301)" stepKey="verifyTheRedirectTypeBeforeMove"/> + <see selector="{{AdminUrlRewriteIndexSection.gridCellByColumnRowNumber('1', 'Request Path')}}" userInput="{{_defaultCategory.name_lwr}}2/{{FirstLevelSubCat.name_lwr}}/{{SecondLevelSubCat.name_lwr}}.html" stepKey="verifyTheRequestPathBeforeMove"/> + <see selector="{{AdminUrlRewriteIndexSection.gridCellByColumnRowNumber('1', 'Target Path')}}" userInput="{{SecondLevelSubCat.name_lwr}}.html" stepKey="verifyTheTargetPathBeforeMove"/> </test> </tests> diff --git a/app/code/Magento/Catalog/Test/Mftf/Test/AdminMoveProductBetweenCategoriesTest.xml b/app/code/Magento/Catalog/Test/Mftf/Test/AdminMoveProductBetweenCategoriesTest.xml index 7e6e79cd08c26..fe31456aca334 100644 --- a/app/code/Magento/Catalog/Test/Mftf/Test/AdminMoveProductBetweenCategoriesTest.xml +++ b/app/code/Magento/Catalog/Test/Mftf/Test/AdminMoveProductBetweenCategoriesTest.xml @@ -19,7 +19,7 @@ </annotations> <before> - <actionGroup ref="LoginAsAdmin" stepKey="LoginAsAdmin"/> + <actionGroup ref="AdminLoginActionGroup" stepKey="LoginAsAdmin"/> <createData entity="defaultSimpleProduct" stepKey="simpleProduct"/> <createData entity="_defaultCategory" stepKey="createAnchoredCategory1"/> <createData entity="_defaultCategory" stepKey="createSecondCategory"/> @@ -53,7 +53,7 @@ <deleteData createDataKey="simpleProduct" stepKey="deleteProduct"/> <deleteData createDataKey="createSecondCategory" stepKey="deleteSecondCategory"/> <deleteData createDataKey="createAnchoredCategory1" stepKey="deleteAnchoredCategory1"/> - <actionGroup ref="logout" stepKey="logout"/> + <actionGroup ref="AdminLogoutActionGroup" stepKey="logout"/> </after> <!-- Create the anchored category <Cat1_anchored> --> <actionGroup ref="AdminAnchorCategoryActionGroup" stepKey="anchorCategory"> @@ -68,7 +68,9 @@ <seeElement selector="{{AdminCategoryMessagesSection.SuccessMessage}}" stepKey="seeSaveSuccessMessage"/> <!-- Assign <product1> to the <Sub1> --> - <amOnPage url="{{AdminProductEditPage.url($$simpleProduct.id$$)}}" stepKey="goToProduct"/> + <actionGroup ref="AdminProductPageOpenByIdActionGroup" stepKey="goToProduct"> + <argument name="productId" value="$$simpleProduct.id$$"/> + </actionGroup> <waitForPageLoad stepKey="waitForProductPageLoad"/> <click selector="{{AdminProductFormSection.categoriesDropdown}}" stepKey="activateDropDownCategory"/> <fillField userInput="{{SimpleSubCategory.name}}" selector="{{AdminProductFormSection.searchCategory}}" stepKey="fillSearch"/> @@ -129,11 +131,10 @@ <waitForPageLoad stepKey="waitForRefresh"/> <see userInput="2 cache type(s) refreshed." stepKey="seeCacheRefreshedMessage"/> - <actionGroup ref="logout" stepKey="logout"/> + <actionGroup ref="AdminLogoutActionGroup" stepKey="logout"/> <!-- Open frontend --> - <amOnPage url="{{StorefrontHomePage.url}}" stepKey="onFrontend"/> - <waitForPageLoad stepKey="waitForStorefrontPageLoad"/> + <actionGroup ref="StorefrontOpenHomePageActionGroup" stepKey="onFrontend"/> <!-- Open <Cat2> from navigation menu --> <click selector="{{StorefrontHeaderSection.NavigationCategoryByName($$createSecondCategory.name$$)}}" stepKey="openCat2"/> @@ -157,11 +158,11 @@ <!-- # Category should open successfully # <product1> should be absent on the page --> <see userInput="$$createAnchoredCategory1.name$$" selector="{{StorefrontCategoryMainSection.CategoryTitle}}" stepKey="seeCategory1Name"/> - <see userInput="We can't find products matching the selection." stepKey="seeEmptyNotice"/> + <actionGroup ref="AssertStorefrontNoProductsFoundActionGroup" stepKey="seeEmptyNotice"/> <dontSee userInput="$$simpleProduct.name$$" selector="{{StorefrontCategoryMainSection.productName}}" stepKey="dontseeProduct"/> <!-- Log in to the backend: Admin user is logged in--> - <actionGroup ref="LoginAsAdmin" stepKey="LoginAdmin"/> + <actionGroup ref="AdminLoginActionGroup" stepKey="LoginAdmin"/> <!-- Navigate to the Catalog > Products: Navigate to the Catalog>Products --> <amOnPage url="{{AdminCatalogProductPage.url}}" stepKey="amOnProductPage"/> @@ -190,8 +191,7 @@ <magentoCLI command="cron:run" stepKey="runCron2"/> <!-- Open frontend --> - <amOnPage url="{{StorefrontHomePage.url}}" stepKey="onFrontendPage"/> - <waitForPageLoad stepKey="waitForFrontPageLoad"/> + <actionGroup ref="StorefrontOpenHomePageActionGroup" stepKey="onFrontendPage"/> <!-- Open <Cat2> from navigation menu --> <click selector="{{StorefrontHeaderSection.NavigationCategoryByName($$createSecondCategory.name$$)}}" stepKey="openSecondCategory"/> diff --git a/app/code/Magento/Catalog/Test/Mftf/Test/AdminMultipleWebsitesUseDefaultValuesTest.xml b/app/code/Magento/Catalog/Test/Mftf/Test/AdminMultipleWebsitesUseDefaultValuesTest.xml index f7fd81f28199f..c1cfcf7ebe10f 100644 --- a/app/code/Magento/Catalog/Test/Mftf/Test/AdminMultipleWebsitesUseDefaultValuesTest.xml +++ b/app/code/Magento/Catalog/Test/Mftf/Test/AdminMultipleWebsitesUseDefaultValuesTest.xml @@ -14,7 +14,7 @@ <stories value="Create websites"/> <title value="Use Default Value checkboxes should be checked for new website scope"/> <description value="Use Default Value checkboxes for product attribute should be checked for new website scope"/> - <severity value="MAJOR"/> + <severity value="BLOCKER"/> <testCaseId value="MAGETWO-92454"/> <group value="Catalog"/> </annotations> @@ -22,9 +22,9 @@ <actionGroup ref="AdminDeleteWebsiteActionGroup" stepKey="deleteSecondWebsite"> <argument name="websiteName" value="Second Website"/> </actionGroup> - <actionGroup ref="logout" stepKey="adminLogout"/> + <actionGroup ref="AdminLogoutActionGroup" stepKey="adminLogout"/> </after> - <actionGroup ref="LoginActionGroup" stepKey="loginAsAdmin"/> + <actionGroup ref="AdminLoginActionGroup" stepKey="loginAsAdmin"/> <actionGroup ref="AdminCreateWebsiteActionGroup" stepKey="createAdditionalWebsite"> <argument name="newWebsiteName" value="Second Website"/> <argument name="websiteCode" value="second_website"/> @@ -81,4 +81,4 @@ <seeCheckboxIsChecked selector="{{AdminProductFormSection.productTaxClassUseDefault}}" stepKey="seeTaxClassCheckboxChecked"/> <seeCheckboxIsChecked selector="{{AdminProductFormSection.visibilityUseDefault}}" stepKey="seeVisibilityCheckboxChecked"/> </test> -</tests> \ No newline at end of file +</tests> diff --git a/app/code/Magento/Catalog/Test/Mftf/Test/AdminNavigateMultipleUpSellProductsTest.xml b/app/code/Magento/Catalog/Test/Mftf/Test/AdminNavigateMultipleUpSellProductsTest.xml index 5dd8b2e430941..659521ed9e467 100644 --- a/app/code/Magento/Catalog/Test/Mftf/Test/AdminNavigateMultipleUpSellProductsTest.xml +++ b/app/code/Magento/Catalog/Test/Mftf/Test/AdminNavigateMultipleUpSellProductsTest.xml @@ -14,7 +14,7 @@ <title value="Promote Multiple Products (Simple, Configurable) as Up-Sell Products"/> <description value="Login as admin and add simple and configurable Products as Up-Sell products"/> <testCaseId value="MC-8902"/> - <severity value="CRITICAL"/> + <severity value="BLOCKER"/> <group value="mtf_migrated"/> </annotations> @@ -74,11 +74,11 @@ </createData> <!--Login as admin--> - <actionGroup ref="LoginAsAdmin" stepKey="login"/> + <actionGroup ref="AdminLoginActionGroup" stepKey="login"/> </before> <after> <!--Logout as admin--> - <actionGroup ref="logout" stepKey="logout"/> + <actionGroup ref="AdminLogoutActionGroup" stepKey="logout"/> <!--Delete created data--> <deleteData createDataKey="createConfigProduct" stepKey="deleteConfigProduct"/> @@ -90,6 +90,9 @@ <deleteData createDataKey="createConfigChildProduct2" stepKey="deletecreateConfigChildProduct2"/> <deleteData createDataKey="createConfigChildProduct1" stepKey="deletecreateConfigChildProduct1"/> <deleteData createDataKey="createConfigProductAttribute" stepKey="deleteConfigProductAttribute"/> + + <!-- Reindex invalidated indices after product attribute has been created/deleted --> + <magentoCron groups="index" stepKey="reindexInvalidatedIndices"/> </after> <!--Open Product Index Page--> diff --git a/app/code/Magento/Catalog/Test/Mftf/Test/AdminProductAttributeLabelDontAllowHtmlTagsTest.xml b/app/code/Magento/Catalog/Test/Mftf/Test/AdminProductAttributeLabelDontAllowHtmlTagsTest.xml new file mode 100644 index 0000000000000..f3981e7b8f76a --- /dev/null +++ b/app/code/Magento/Catalog/Test/Mftf/Test/AdminProductAttributeLabelDontAllowHtmlTagsTest.xml @@ -0,0 +1,56 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + /** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +--> + +<tests xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/testSchema.xsd"> + <test name="AdminProductAttributeLabelDontAllowHtmlTagsTest"> + <annotations> + <features value="Catalog"/> + <stories value="Product Attribute label must not contain HTML tags"/> + <title value="Product Attribute label musts not contain HTML tags"/> + <description value="Test whenever HTML tags are allowed for a product attribute label"/> + <severity value="CRITICAL"/> + <group value="catalog"/> + </annotations> + <before> + <actionGroup ref="AdminLoginActionGroup" stepKey="loginAsAdmin"/> + </before> + <after> + <actionGroup ref="AdminLogoutActionGroup" stepKey="logoutAdmin"/> + </after> + + <actionGroup ref="AdminNavigateToNewProductAttributePageActionGroup" stepKey="openProductAttributePage"/> + + <actionGroup ref="AdminFillProductAttributePropertiesActionGroup" stepKey="fillAttributeDefaultLabel"> + <argument name="attributeName" value="{{productAttributeWithHtmlTagsInLabel.default_label}}"/> + <argument name="attributeType" value="{{productAttributeWithHtmlTagsInLabel.frontend_input}}" /> + </actionGroup> + + <actionGroup ref="AdminProductAttributePageSwitchTabActionGroup" stepKey="makeManageLabelsTabActive"> + <argument name="tabName" value="Manage Labels"/> + </actionGroup> + + <actionGroup ref="AdminFillProductAttributeDefaultStoreViewActionGroup" stepKey="fillAttributeDefaultStoreViewLabel"> + <argument name="value" value="{{productAttributeWithHtmlTagsInLabel.default_store_label}}"/> + </actionGroup> + + <actionGroup ref="AdminSaveProductAttributeActionGroup" stepKey="saveAttribute"/> + + <actionGroup ref="AssertSeeProductAttributeValidationErrorOnManageLabelsTabActionGroup" stepKey="validateAttributeStoreViewLabelForHtmlTags"> + <argument name="message" value="HTML tags are not allowed"/> + </actionGroup> + + <actionGroup ref="AdminProductAttributePageSwitchTabActionGroup" stepKey="makePropertiesTabActive"> + <argument name="tabName" value="Properties"/> + </actionGroup> + + <actionGroup ref="AssertSeeProductAttributeValidationErrorOnPropertiesTabActionGroup" stepKey="validateAttributeLabelForHtmlTags"> + <argument name="message" value="HTML tags are not allowed"/> + </actionGroup> + </test> +</tests> diff --git a/app/code/Magento/Catalog/Test/Mftf/Test/AdminProductCategoryIndexerInUpdateOnScheduleModeTest.xml b/app/code/Magento/Catalog/Test/Mftf/Test/AdminProductCategoryIndexerInUpdateOnScheduleModeTest.xml index 2283a0e4d6158..eebd3472cbd95 100644 --- a/app/code/Magento/Catalog/Test/Mftf/Test/AdminProductCategoryIndexerInUpdateOnScheduleModeTest.xml +++ b/app/code/Magento/Catalog/Test/Mftf/Test/AdminProductCategoryIndexerInUpdateOnScheduleModeTest.xml @@ -14,7 +14,7 @@ <title value="Product Categories Indexer in Update on Schedule mode"/> <description value="The test verifies that in Update on Schedule mode if displaying of category products on Storefront changes due to product properties change, the changes are NOT applied immediately, but applied only after cron runs (twice)."/> - <severity value="CRITICAL"/> + <severity value="BLOCKER"/> <testCaseId value="MC-11146"/> <group value="catalog"/> <group value="indexer"/> @@ -23,7 +23,7 @@ </skip> </annotations> <before> - <actionGroup ref="LoginAsAdmin" stepKey="LoginAsAdmin"/> + <actionGroup ref="AdminLoginActionGroup" stepKey="LoginAsAdmin"/> <!-- Create category A without products --> <createData entity="_defaultCategory" stepKey="createCategoryA"/> @@ -69,12 +69,14 @@ <deleteData createDataKey="createCategoryA" stepKey="deleteCategoryA"/> <deleteData createDataKey="createCategoryC" stepKey="deleteCategoryC"/> <deleteData createDataKey="createCategoryB" stepKey="deleteCategoryB"/> - <actionGroup ref="logout" stepKey="logout"/> + <actionGroup ref="AdminLogoutActionGroup" stepKey="logout"/> </after> <!-- Case: change product category from product page --> <!-- 1. Open Admin > Catalog > Products > Product A1 --> - <amOnPage url="{{AdminProductEditPage.url($$createProductA1.id$$)}}" stepKey="goToProductA1"/> + <actionGroup ref="AdminProductPageOpenByIdActionGroup" stepKey="goToProductA1"> + <argument name="productId" value="$$createProductA1.id$$"/> + </actionGroup> <waitForPageLoad stepKey="waitForProductPageLoad"/> <!-- 2. Assign category A to product A1. Save product --> @@ -89,7 +91,7 @@ <!-- The category is still empty --> <see userInput="$$createCategoryA.name$$" selector="{{StorefrontCategoryMainSection.CategoryTitle}}" stepKey="seeCategoryA1Name"/> - <see userInput="We can't find products matching the selection." stepKey="seeEmptyNotice"/> + <actionGroup ref="AssertStorefrontNoProductsFoundActionGroup" stepKey="seeEmptyNotice"/> <dontSee userInput="$$createProductA1.name$$" selector="{{StorefrontCategoryMainSection.productName}}" stepKey="dontseeProductA1"/> <!-- 4. Run cron to reindex --> @@ -104,7 +106,9 @@ <see userInput="$$createProductA1.name$$" selector="{{StorefrontCategoryMainSection.productName}}" stepKey="seeProductA1"/> <!--6. Open Admin > Catalog > Products > Product A1. Unassign category A from product A1 --> - <amOnPage url="{{AdminProductEditPage.url($$createProductA1.id$$)}}" stepKey="OnPageProductA1"/> + <actionGroup ref="AdminProductPageOpenByIdActionGroup" stepKey="OnPageProductA1"> + <argument name="productId" value="$$createProductA1.id$$"/> + </actionGroup> <waitForPageLoad stepKey="waitForProductA1PageLoad"/> <actionGroup ref="AdminUnassignCategoryOnProductAndSaveActionGroup" stepKey="unassignCategoryA"> <argument name="categoryName" value="$$createCategoryA.name$$"/> @@ -128,7 +132,7 @@ <!-- Category A is empty now --> <see userInput="$$createCategoryA.name$$" selector="{{StorefrontCategoryMainSection.CategoryTitle}}" stepKey="seeOnPageCategoryAName"/> - <see userInput="We can't find products matching the selection." stepKey="seeOnPageEmptyNotice"/> + <actionGroup ref="AssertStorefrontNoProductsFoundActionGroup" stepKey="seeOnPageEmptyNotice"/> <dontSee userInput="$$createProductA1.name$$" selector="{{StorefrontCategoryMainSection.productName}}" stepKey="dontseeProductA1OnPage"/> <!-- Case: change product status --> @@ -144,7 +148,9 @@ <see userInput="$$createProductC2.name$$" selector="{{StorefrontCategoryMainSection.productName}}" stepKey="seeNameProductC2"/> <!-- 11. Open product C1 in Admin. Make it disabled (Enable Product = No)--> - <amOnPage url="{{AdminProductEditPage.url($$createProductC1.id$$)}}" stepKey="goToProductC1"/> + <actionGroup ref="AdminProductPageOpenByIdActionGroup" stepKey="goToProductC1"> + <argument name="productId" value="$$createProductC1.id$$"/> + </actionGroup> <waitForPageLoad stepKey="waitForProductC1PageLoad"/> <click selector="{{AdminProductFormSection.enableProductLabel}}" stepKey="clickOffEnableToggleAgain"/> <!-- Saved successfully --> @@ -200,7 +206,9 @@ <!-- 17. Repeat steps 10-16, but enable products instead. --> <!-- 17.11 Open product C1 in Admin. Make it enabled --> - <amOnPage url="{{AdminProductEditPage.url($$createProductC1.id$$)}}" stepKey="goToEditProductC1"/> + <actionGroup ref="AdminProductPageOpenByIdActionGroup" stepKey="goToEditProductC1"> + <argument name="productId" value="$$createProductC1.id$$"/> + </actionGroup> <waitForPageLoad stepKey="waitForProductC1Page"/> <click selector="{{AdminProductFormSection.enableProductLabel}}" stepKey="clickOnEnableToggleAgain"/> @@ -258,7 +266,9 @@ <!-- Case: change product visibility --> <!-- 18. Repeat steps 10-17 but change product Visibility instead of product status --> <!-- 18.11 Open product C1 in Admin. Make it enabled --> - <amOnPage url="{{AdminProductEditPage.url($$createProductC1.id$$)}}" stepKey="editProductC1"/> + <actionGroup ref="AdminProductPageOpenByIdActionGroup" stepKey="editProductC1"> + <argument name="productId" value="$$createProductC1.id$$"/> + </actionGroup> <waitForPageLoad stepKey="waitProductC1Page"/> <selectOption selector="{{AdminProductFormBundleSection.visibilityDropDown}}" userInput="Search" stepKey="changeVisibility"/> diff --git a/app/code/Magento/Catalog/Test/Mftf/Test/AdminProductCustomURLKeyPreservedWhenAssignedToCategoryWithoutCustomURLKey.xml b/app/code/Magento/Catalog/Test/Mftf/Test/AdminProductCustomURLKeyPreservedWhenAssignedToCategoryWithoutCustomURLKey.xml deleted file mode 100644 index df09768139533..0000000000000 --- a/app/code/Magento/Catalog/Test/Mftf/Test/AdminProductCustomURLKeyPreservedWhenAssignedToCategoryWithoutCustomURLKey.xml +++ /dev/null @@ -1,117 +0,0 @@ -<?xml version="1.0" encoding="UTF-8"?> -<!-- - /** - * Copyright © Magento, Inc. All rights reserved. - * See COPYING.txt for license details. - */ ---> - -<tests xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" - xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/testSchema.xsd"> - <test name="AdminProductCustomURLKeyPreservedWhenAssignedToCategoryWithoutCustomURLKey"> - <annotations> - <stories value="Product"/> - <features value="Catalog"/> - <title value="Product custom URL Key is preserved when assigned to a Category (without custom URL Key) alongside with another Product without custom URL Key"/> - <description value="The test verifies that product custom URL Key is preserved when assigned to a Category (without custom URL Key) alongside with another Product without custom URL Key."/> - <severity value="MAJOR"/> - <testCaseId value="MC-6443"/> - <useCaseId value="MAGETWO-90331"/> - <group value="catalog"/> - </annotations> - <before> - <!-- Create category --> - <createData entity="SimpleSubCategory" stepKey="createCategory"/> - - <!-- Create Simple Products --> - <createData entity="SimpleProduct2" stepKey="createSimpleProductFirst"/> - <createData entity="SimpleProduct2" stepKey="createSimpleProductSecond"/> - - <!--Login as admin --> - <actionGroup ref="LoginAsAdmin" stepKey="loginToAdminPanel"/> - - <actionGroup ref="AdminCreateStoreViewActionGroup" stepKey="createStoreView"> - <argument name="customStore" value="storeViewData"/> - </actionGroup> - - <!--Run full reindex and clear caches --> - <magentoCLI command="indexer:reindex" stepKey="reindex"/> - <magentoCLI command="cache:flush" arguments="full_page" stepKey="flushCache"/> - </before> - <after> - <deleteData createDataKey="createSimpleProductFirst" stepKey="deleteFirstSimpleProduct"/> - <deleteData createDataKey="createSimpleProductSecond" stepKey="deleteSecondSimpleProduct"/> - <deleteData createDataKey="createCategory" stepKey="deleteCategory"/> - - <actionGroup ref="AdminDeleteStoreViewActionGroup" stepKey="deleteStoreView"> - <argument name="customStore" value="storeViewData"/> - </actionGroup> - - <actionGroup ref="logout" stepKey="logout"/> - </after> - - <!-- Open product --> - <amOnPage url="{{AdminProductEditPage.url($createSimpleProductSecond.id$)}}" stepKey="openProductSecondEditPage"/> - <!-- switch store view --> - <actionGroup ref="AdminSwitchStoreViewActionGroup" stepKey="switchToStoreView"> - <argument name="storeView" value="storeViewData.name"/> - </actionGroup> - - <!-- set url key --> - <conditionalClick selector="{{AdminProductSEOSection.sectionHeader}}" dependentSelector="{{AdminProductSEOSection.urlKeyInput}}" visible="false" stepKey="openSeoSection"/> - <uncheckOption selector="{{AdminProductSEOSection.useDefaultUrl}}" stepKey="uncheckUseDefaultUrlKey"/> - <fillField userInput="U2" selector="{{AdminProductSEOSection.urlKeyInput}}" stepKey="fillUrlKey"/> - <actionGroup ref="SaveProductFormActionGroup" stepKey="saveProduct"/> - - <actionGroup ref="GoToAdminCategoryPageByIdActionGroup" stepKey="openCategory"> - <argument name="id" value="$createCategory.id$"/> - </actionGroup> - - <actionGroup ref="AdminCategoryAssignProductActionGroup" stepKey="assignSimpleProductFirst"> - <argument name="productSku" value="$createSimpleProductFirst.sku$"/> - </actionGroup> - <actionGroup ref="AdminCategoryAssignProductActionGroup" stepKey="assignSimpleProductSecond"> - <argument name="productSku" value="$createSimpleProductSecond.sku$"/> - </actionGroup> - - <actionGroup ref="SaveCategoryFormActionGroup" stepKey="saveCategory"/> - - <executeJS function="return '$createCategory.name$'.toLowerCase();" stepKey="categoryNameLower" /> - <executeJS function="return '$createSimpleProductFirst.name$'.toLowerCase();" stepKey="simpleProductFirstNameLower" /> - <executeJS function="return '$createSimpleProductSecond.name$'.toLowerCase();" stepKey="simpleProductSecondNameLower" /> - - <!-- Make assertions on frontend --> - <amOnPage url="{{StorefrontHomePage.url}}" stepKey="goToStorefrontPage"/> - <click selector="{{StorefrontHeaderSection.NavigationCategoryByName($createCategory.name$)}}" stepKey="onCategoryPage"/> - <seeInCurrentUrl url="{$categoryNameLower}.html" stepKey="checkCategryUrlKey"/> - - <!-- Open first product --> - <click selector="{{StorefrontCategoryProductSection.ProductTitleByName($createSimpleProductFirst.name$)}}" stepKey="openFirstProduct"/> - <waitForPageLoad time="30" stepKey="waitForFirstProduct"/> - <seeInCurrentUrl url="{$simpleProductFirstNameLower}.html" stepKey="checkFirstSimpleProductUrlKey"/> - - <amOnPage url="{{StorefrontCategoryPage.url($createCategory.custom_attributes[url_key]$)}}" stepKey="onCategoryView"/> - <!-- Open second product --> - <click selector="{{StorefrontCategoryProductSection.ProductTitleByName($createSimpleProductSecond.name$)}}" stepKey="openSecondProduct"/> - <waitForPageLoad time="30" stepKey="waitForSecondProduct"/> - <seeInCurrentUrl url="{$simpleProductSecondNameLower}.html" stepKey="checkSecondSimpleProductUrlKey"/> - - <actionGroup ref="StorefrontSwitchStoreViewActionGroup" stepKey="switchToCustomStoreView"> - <argument name="storeView" value="storeViewData"/> - </actionGroup> - - <click selector="{{StorefrontHeaderSection.NavigationCategoryByName($createCategory.name$)}}" stepKey="openCategoryPage"/> - <seeInCurrentUrl url="{$categoryNameLower}.html" stepKey="seeCategoryUrlKey"/> - - <!-- Open product first --> - <click selector="{{StorefrontCategoryProductSection.ProductTitleByName($$createSimpleProductFirst.name$$)}}" stepKey="openFirstSimpleProduct"/> - <waitForPageLoad time="30" stepKey="waitForFirstSimpleProduct"/> - <seeInCurrentUrl url="{$simpleProductFirstNameLower}.html" stepKey="assertFirstSimpleProductUrlKey"/> - - <click selector="{{StorefrontHeaderSection.NavigationCategoryByName($createCategory.name$)}}" stepKey="openCategoryView"/> - <!-- Open product2 --> - <click selector="{{StorefrontCategoryProductSection.ProductTitleByName($createSimpleProductSecond.name$)}}" stepKey="openSecondSimpleProduct"/> - <waitForPageLoad time="30" stepKey="waitForSecondSimpleProduct"/> - <seeInCurrentUrl url="u2.html" stepKey="assertSecondSimpleProductUrlKey"/> - </test> -</tests> diff --git a/app/code/Magento/Catalog/Test/Mftf/Test/AdminProductCustomURLKeyPreservedWhenAssignedToCategoryWithoutCustomURLKeyTest.xml b/app/code/Magento/Catalog/Test/Mftf/Test/AdminProductCustomURLKeyPreservedWhenAssignedToCategoryWithoutCustomURLKeyTest.xml new file mode 100644 index 0000000000000..1c536df7c2efb --- /dev/null +++ b/app/code/Magento/Catalog/Test/Mftf/Test/AdminProductCustomURLKeyPreservedWhenAssignedToCategoryWithoutCustomURLKeyTest.xml @@ -0,0 +1,119 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + /** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +--> + +<tests xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/testSchema.xsd"> + <test name="AdminProductCustomURLKeyPreservedWhenAssignedToCategoryWithoutCustomURLKeyTest"> + <annotations> + <stories value="Product"/> + <features value="Catalog"/> + <title value="Product custom URL Key is preserved when assigned to a Category (without custom URL Key) alongside with another Product without custom URL Key"/> + <description value="The test verifies that product custom URL Key is preserved when assigned to a Category (without custom URL Key) alongside with another Product without custom URL Key."/> + <severity value="MAJOR"/> + <testCaseId value="MC-6443"/> + <useCaseId value="MAGETWO-90331"/> + <group value="catalog"/> + </annotations> + <before> + <!-- Create category --> + <createData entity="SimpleSubCategory" stepKey="createCategory"/> + + <!-- Create Simple Products --> + <createData entity="SimpleProduct2" stepKey="createSimpleProductFirst"/> + <createData entity="SimpleProduct2" stepKey="createSimpleProductSecond"/> + + <!--Login as admin --> + <actionGroup ref="AdminLoginActionGroup" stepKey="loginToAdminPanel"/> + + <actionGroup ref="AdminCreateStoreViewActionGroup" stepKey="createStoreView"> + <argument name="customStore" value="storeViewData"/> + </actionGroup> + + <!--Run full reindex and clear caches --> + <magentoCLI command="indexer:reindex" stepKey="reindex"/> + <magentoCLI command="cache:flush" arguments="full_page" stepKey="flushCache"/> + </before> + <after> + <deleteData createDataKey="createSimpleProductFirst" stepKey="deleteFirstSimpleProduct"/> + <deleteData createDataKey="createSimpleProductSecond" stepKey="deleteSecondSimpleProduct"/> + <deleteData createDataKey="createCategory" stepKey="deleteCategory"/> + + <actionGroup ref="AdminDeleteStoreViewActionGroup" stepKey="deleteStoreView"> + <argument name="customStore" value="storeViewData"/> + </actionGroup> + + <actionGroup ref="AdminLogoutActionGroup" stepKey="logout"/> + </after> + + <!-- Open product --> + <actionGroup ref="AdminProductPageOpenByIdActionGroup" stepKey="openProductSecondEditPage"> + <argument name="productId" value="$createSimpleProductSecond.id$"/> + </actionGroup> + <!-- switch store view --> + <actionGroup ref="AdminSwitchStoreViewActionGroup" stepKey="switchToStoreView"> + <argument name="storeView" value="storeViewData.name"/> + </actionGroup> + + <!-- set url key --> + <conditionalClick selector="{{AdminProductSEOSection.sectionHeader}}" dependentSelector="{{AdminProductSEOSection.urlKeyInput}}" visible="false" stepKey="openSeoSection"/> + <uncheckOption selector="{{AdminProductSEOSection.useDefaultUrl}}" stepKey="uncheckUseDefaultUrlKey"/> + <fillField userInput="U2" selector="{{AdminProductSEOSection.urlKeyInput}}" stepKey="fillUrlKey"/> + <actionGroup ref="SaveProductFormActionGroup" stepKey="saveProduct"/> + + <actionGroup ref="GoToAdminCategoryPageByIdActionGroup" stepKey="openCategory"> + <argument name="id" value="$createCategory.id$"/> + </actionGroup> + + <actionGroup ref="AdminCategoryAssignProductActionGroup" stepKey="assignSimpleProductFirst"> + <argument name="productSku" value="$createSimpleProductFirst.sku$"/> + </actionGroup> + <actionGroup ref="AdminCategoryAssignProductActionGroup" stepKey="assignSimpleProductSecond"> + <argument name="productSku" value="$createSimpleProductSecond.sku$"/> + </actionGroup> + + <actionGroup ref="AdminSaveCategoryFormActionGroup" stepKey="saveCategory"/> + + <executeJS function="return '$createCategory.name$'.toLowerCase();" stepKey="categoryNameLower" /> + <executeJS function="return '$createSimpleProductFirst.name$'.toLowerCase();" stepKey="simpleProductFirstNameLower" /> + <executeJS function="return '$createSimpleProductSecond.name$'.toLowerCase();" stepKey="simpleProductSecondNameLower" /> + + <!-- Make assertions on frontend --> + <actionGroup ref="StorefrontOpenHomePageActionGroup" stepKey="goToHomePage"/> + <click selector="{{StorefrontHeaderSection.NavigationCategoryByName($createCategory.name$)}}" stepKey="onCategoryPage"/> + <seeInCurrentUrl url="{$categoryNameLower}.html" stepKey="checkCategryUrlKey"/> + + <!-- Open first product --> + <click selector="{{StorefrontCategoryProductSection.ProductTitleByName($createSimpleProductFirst.name$)}}" stepKey="openFirstProduct"/> + <waitForPageLoad time="30" stepKey="waitForFirstProduct"/> + <seeInCurrentUrl url="{$simpleProductFirstNameLower}.html" stepKey="checkFirstSimpleProductUrlKey"/> + + <amOnPage url="{{StorefrontCategoryPage.url($createCategory.custom_attributes[url_key]$)}}" stepKey="onCategoryView"/> + <!-- Open second product --> + <click selector="{{StorefrontCategoryProductSection.ProductTitleByName($createSimpleProductSecond.name$)}}" stepKey="openSecondProduct"/> + <waitForPageLoad time="30" stepKey="waitForSecondProduct"/> + <seeInCurrentUrl url="{$simpleProductSecondNameLower}.html" stepKey="checkSecondSimpleProductUrlKey"/> + + <actionGroup ref="StorefrontSwitchStoreViewActionGroup" stepKey="switchToCustomStoreView"> + <argument name="storeView" value="storeViewData"/> + </actionGroup> + + <click selector="{{StorefrontHeaderSection.NavigationCategoryByName($createCategory.name$)}}" stepKey="openCategoryPage"/> + <seeInCurrentUrl url="{$categoryNameLower}.html" stepKey="seeCategoryUrlKey"/> + + <!-- Open product first --> + <click selector="{{StorefrontCategoryProductSection.ProductTitleByName($$createSimpleProductFirst.name$$)}}" stepKey="openFirstSimpleProduct"/> + <waitForPageLoad time="30" stepKey="waitForFirstSimpleProduct"/> + <seeInCurrentUrl url="{$simpleProductFirstNameLower}.html" stepKey="assertFirstSimpleProductUrlKey"/> + + <click selector="{{StorefrontHeaderSection.NavigationCategoryByName($createCategory.name$)}}" stepKey="openCategoryView"/> + <!-- Open product2 --> + <click selector="{{StorefrontCategoryProductSection.ProductTitleByName($createSimpleProductSecond.name$)}}" stepKey="openSecondSimpleProduct"/> + <waitForPageLoad time="30" stepKey="waitForSecondSimpleProduct"/> + <seeInCurrentUrl url="u2.html" stepKey="assertSecondSimpleProductUrlKey"/> + </test> +</tests> diff --git a/app/code/Magento/Catalog/Test/Mftf/Test/AdminProductGridFilteringByCustomAttributeTest.xml b/app/code/Magento/Catalog/Test/Mftf/Test/AdminProductGridFilteringByCustomAttributeTest.xml index 6f65865924bad..9536ee030cdf8 100644 --- a/app/code/Magento/Catalog/Test/Mftf/Test/AdminProductGridFilteringByCustomAttributeTest.xml +++ b/app/code/Magento/Catalog/Test/Mftf/Test/AdminProductGridFilteringByCustomAttributeTest.xml @@ -21,7 +21,7 @@ </annotations> <before> <!--Login as admin and delete all products --> - <actionGroup ref="LoginAsAdmin" stepKey="login"/> + <actionGroup ref="AdminLoginActionGroup" stepKey="login"/> <actionGroup ref="DeleteAllProductsUsingProductGridActionGroup" stepKey="deleteAllProducts"/> <!--Create dropdown product attribute--> <createData entity="productDropDownAttribute" stepKey="createDropdownAttribute"/> @@ -89,7 +89,10 @@ <!--Delete category--> <deleteData createDataKey="createCategory" stepKey="deleteCategory"/> <actionGroup ref="NavigateToAndResetProductGridToDefaultViewActionGroup" stepKey="NavigateToAndResetProductGridToDefaultViewAfterTest"/> - <actionGroup ref="logout" stepKey="logout"/> + <actionGroup ref="AdminLogoutActionGroup" stepKey="logout"/> + + <!-- Reindex invalidated indices after product attribute has been created/deleted --> + <magentoCron groups="index" stepKey="reindexInvalidatedIndices"/> </after> <amOnPage url="{{AdminProductIndexPage.url}}" stepKey="navigateToProductIndex"/> <waitForPageLoad stepKey="waitForProductGridPageLoad"/> diff --git a/app/code/Magento/Catalog/Test/Mftf/Test/AdminProductGridFilteringByDateAttributeTest.xml b/app/code/Magento/Catalog/Test/Mftf/Test/AdminProductGridFilteringByDateAttributeTest.xml index df2b525a56086..d47730a99308b 100644 --- a/app/code/Magento/Catalog/Test/Mftf/Test/AdminProductGridFilteringByDateAttributeTest.xml +++ b/app/code/Magento/Catalog/Test/Mftf/Test/AdminProductGridFilteringByDateAttributeTest.xml @@ -19,12 +19,12 @@ <group value="product"/> </annotations> <before> - <actionGroup ref="LoginAsAdmin" stepKey="loginAsAdmin"/> + <actionGroup ref="AdminLoginActionGroup" stepKey="loginAsAdmin"/> <createData entity="SimpleProductWithNewFromDate" stepKey="createSimpleProductWithDate"/> </before> <after> <deleteData createDataKey="createSimpleProductWithDate" stepKey="deleteProduct"/> - <actionGroup ref="logout" stepKey="logoutOfAdmin"/> + <actionGroup ref="AdminLogoutActionGroup" stepKey="logoutOfAdmin"/> </after> <amOnPage url="{{AdminProductAttributeGridPage.url}}" stepKey="navigateToProductAttribute"/> <waitForPageLoad stepKey="wait1"/> @@ -48,7 +48,7 @@ <actionGroup ref="FilterProductGridBySetNewFromDateActionGroup" stepKey="filterProductGridToCheckSetAsNewColumn"/> <click selector="{{AdminProductGridSection.firstRow}}" stepKey="clickOnFirstRowProductGrid"/> <waitForPageLoad stepKey="waitForProductEditPageToLoad"/> - <actionGroup ref="AdminFormSaveAndClose" stepKey="saveAndCloseProductForm"/> + <actionGroup ref="AdminFormSaveAndCloseActionGroup" stepKey="saveAndCloseProductForm"/> <click selector="{{AdminProductGridFilterSection.filters}}" stepKey="expandFilters"/> <seeInField selector="{{AdminProductGridFilterSection.newFromDateFilter}}" userInput="05/16/2018" stepKey="checkForNewFromDate"/> <click selector="{{AdminProductGridFilterSection.columnsDropdown}}" stepKey="openColumnsDropdown2"/> diff --git a/app/code/Magento/Catalog/Test/Mftf/Test/AdminProductImageAssignmentForMultipleStoresTest.xml b/app/code/Magento/Catalog/Test/Mftf/Test/AdminProductImageAssignmentForMultipleStoresTest.xml index 455e77666c3a2..35632f4fa6311 100644 --- a/app/code/Magento/Catalog/Test/Mftf/Test/AdminProductImageAssignmentForMultipleStoresTest.xml +++ b/app/code/Magento/Catalog/Test/Mftf/Test/AdminProductImageAssignmentForMultipleStoresTest.xml @@ -18,13 +18,10 @@ <testCaseId value="MAGETWO-58718"/> <group value="product"/> <group value="WYSIWYGDisabled"/> - <skip> - <issueId value="MC-13841"/> - </skip> </annotations> <before> <!-- Login Admin --> - <actionGroup ref="LoginAsAdmin" stepKey="loginAsAdmin"/> + <actionGroup ref="AdminLoginActionGroup" stepKey="loginAsAdmin"/> <!-- Create Store View English --> <actionGroup ref="AdminCreateStoreViewActionGroup" stepKey="createStoreViewEn"> <argument name="customStore" value="customStoreEN"/> @@ -57,7 +54,7 @@ <!-- Clear Filter Product --> <actionGroup ref="AdminClearFiltersActionGroup" stepKey="clearProductFilters"/> <!-- Logout Admin --> - <actionGroup ref="logout" stepKey="logoutOfAdmin"/> + <actionGroup ref="AdminLogoutActionGroup" stepKey="logoutOfAdmin"/> </after> <!-- Search Product and Open Edit --> <actionGroup ref="SearchForProductOnBackendActionGroup" stepKey="searchProduct"> diff --git a/app/code/Magento/Catalog/Test/Mftf/Test/AdminProductStatusAttributeDisabledByDefaultTest.xml b/app/code/Magento/Catalog/Test/Mftf/Test/AdminProductStatusAttributeDisabledByDefaultTest.xml index a882c6e7817ce..ae63158990b96 100644 --- a/app/code/Magento/Catalog/Test/Mftf/Test/AdminProductStatusAttributeDisabledByDefaultTest.xml +++ b/app/code/Magento/Catalog/Test/Mftf/Test/AdminProductStatusAttributeDisabledByDefaultTest.xml @@ -19,7 +19,7 @@ <group value="Catalog"/> </annotations> <before> - <actionGroup ref="LoginAsAdmin" stepKey="loginAsAdmin"/> + <actionGroup ref="AdminLoginActionGroup" stepKey="loginAsAdmin"/> </before> <after> @@ -35,7 +35,7 @@ <waitForPageLoad stepKey="waitForSaveAttribute1"/> <actionGroup ref="ClearCacheActionGroup" stepKey="clearCache1"/> - <actionGroup ref="logout" stepKey="logoutOfAdmin"/> + <actionGroup ref="AdminLogoutActionGroup" stepKey="logoutOfAdmin"/> </after> <amOnPage url="{{AdminProductAttributeGridPage.url}}" stepKey="navigateToProductAttribute"/> <waitForPageLoad stepKey="wait1"/> @@ -56,4 +56,4 @@ <dontSeeCheckboxIsChecked selector="{{AdminProductFormSection.productStatus}}" stepKey="dontSeeCheckboxEnableProductIsChecked"/> </test> -</tests> \ No newline at end of file +</tests> diff --git a/app/code/Magento/Catalog/Test/Mftf/Test/AdminProductTypeSwitchingOnEditingTest.xml b/app/code/Magento/Catalog/Test/Mftf/Test/AdminProductTypeSwitchingOnEditingTest.xml deleted file mode 100644 index 0b7e2a70735c3..0000000000000 --- a/app/code/Magento/Catalog/Test/Mftf/Test/AdminProductTypeSwitchingOnEditingTest.xml +++ /dev/null @@ -1,100 +0,0 @@ -<?xml version="1.0" encoding="UTF-8"?> -<!-- - /** - * Copyright © Magento, Inc. All rights reserved. - * See COPYING.txt for license details. - */ ---> - -<tests xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" - xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/testSchema.xsd"> - <test name="AdminVirtualProductTypeSwitchingToDownloadableProductTest"> - <annotations> - <features value="Catalog"/> - <stories value="Product type switching"/> - <title value="Virtual product type switching on editing to Downloadable product"/> - <description value="Virtual product type switching on editing to Downloadable product"/> - <testCaseId value="MC-17954"/> - <useCaseId value="MAGETWO-44170"/> - <severity value="MAJOR"/> - <group value="catalog"/> - </annotations> - <before> - <!-- Add downloadable domains --> - <magentoCLI stepKey="addDownloadableDomain" command="downloadable:domains:add example.com static.magento.com"/> - <actionGroup ref="LoginAsAdmin" stepKey="loginAsAdmin"/> - <!--Create product--> - <comment userInput="Create product" stepKey="commentCreateProduct"/> - <createData entity="VirtualProduct" stepKey="createProduct"/> - </before> - <after> - <!-- Remove downloadable domains --> - <magentoCLI stepKey="removeDownloadableDomain" command="downloadable:domains:remove example.com static.magento.com"/> - <!--Delete product--> - <comment userInput="Delete product" stepKey="commentDeleteProduct"/> - <deleteData createDataKey="createProduct" stepKey="deleteProduct"/> - <actionGroup ref="AdminClearFiltersActionGroup" stepKey="clearProductFilters"/> - <actionGroup ref="logout" stepKey="logout"/> - </after> - <!--Change product type to Downloadable--> - <comment userInput="Change product type to Downloadable" stepKey="commentCreateDownloadable"/> - <amOnPage url="{{AdminProductEditPage.url($$createProduct.id$$)}}" stepKey="gotToDownloadableProductPage"/> - <waitForPageLoad stepKey="waitForDownloadableProductPageLoad"/> - <actionGroup ref="AdminAddDownloadableLinkInformationActionGroup" stepKey="addDownloadableLinkInformation"/> - <checkOption selector="{{AdminProductDownloadableSection.isLinksPurchasedSeparately}}" stepKey="checkOptionPurchaseSeparately"/> - <actionGroup ref="AddDownloadableProductLinkWithMaxDownloadsActionGroup" stepKey="addDownloadableProductLink"> - <argument name="link" value="downloadableLinkWithMaxDownloads"/> - </actionGroup> - <actionGroup ref="SaveProductFormActionGroup" stepKey="saveDownloadableProductForm"/> - <!--Assert downloadable product on Admin product page grid--> - <comment userInput="Assert configurable product in Admin product page grid" stepKey="commentAssertDownloadableProductOnAdmin"/> - <amOnPage url="{{AdminCatalogProductPage.url}}" stepKey="goToCatalogProductPage"/> - <actionGroup ref="FilterProductGridBySku2ActionGroup" stepKey="filterProductGridBySku"> - <argument name="sku" value="$$createProduct.sku$$"/> - </actionGroup> - <see selector="{{AdminProductGridSection.productGridCell('1', 'Name')}}" userInput="$$createProduct.name$$" stepKey="seeDownloadableProductNameInGrid"/> - <see selector="{{AdminProductGridSection.productGridCell('1', 'Type')}}" userInput="Downloadable Product" stepKey="seeDownloadableProductTypeInGrid"/> - <actionGroup ref="AdminClearFiltersActionGroup" stepKey="clearDownloadableProductFilters"/> - <!--Assert downloadable product on storefront--> - <comment userInput="Assert downloadable product on storefront" stepKey="commentAssertDownloadableProductOnStorefront"/> - <amOnPage url="{{StorefrontProductPage.url($$createProduct.name$$)}}" stepKey="openDownloadableProductPage"/> - <waitForPageLoad stepKey="waitForStorefrontDownloadableProductPageLoad"/> - <see userInput="IN STOCK" selector="{{StorefrontProductInfoMainSection.productStockStatus}}" stepKey="assertDownloadableProductInStock"/> - <scrollTo selector="{{StorefrontDownloadableProductSection.downloadableLinkBlock}}" stepKey="scrollToLinksInStorefront"/> - <seeElement selector="{{StorefrontDownloadableProductSection.downloadableLinkLabel(downloadableLinkWithMaxDownloads.title)}}" stepKey="seeDownloadableLink" /> - </test> - <test name="AdminDownloadableProductTypeSwitchingToSimpleProductTest" extends="AdminVirtualProductTypeSwitchingToDownloadableProductTest"> - <annotations> - <features value="Catalog"/> - <stories value="Product type switching"/> - <title value="Downloadable product type switching on editing to Simple product"/> - <description value="Downloadable product type switching on editing to Simple product"/> - <testCaseId value="MC-17955"/> - <useCaseId value="MAGETWO-44170"/> - <severity value="MAJOR"/> - <group value="catalog"/> - </annotations> - <!--Change product type to Simple--> - <comment userInput="Change product type to Simple Product" stepKey="commentCreateSimple"/> - <amOnPage url="{{AdminProductEditPage.url($$createProduct.id$$)}}" stepKey="gotToProductPage"/> - <waitForPageLoad stepKey="waitForProductPageLoad"/> - <actionGroup ref="AdminAddDownloadableLinkInformationActionGroup" stepKey="addDownloadableLinkInformation"/> - <selectOption selector="{{AdminProductFormSection.productWeightSelect}}" userInput="This item has weight" stepKey="selectWeightForProduct"/> - <actionGroup ref="SaveProductFormActionGroup" stepKey="saveProductForm"/> - <!--Assert simple product on Admin product page grid--> - <comment userInput="Assert simple product in Admin product page grid" stepKey="commentAssertProductOnAdmin"/> - <amOnPage url="{{AdminCatalogProductPage.url}}" stepKey="goToCatalogSimpleProductPage"/> - <actionGroup ref="FilterProductGridBySku2ActionGroup" stepKey="filterSimpleProductGridBySku"> - <argument name="sku" value="$$createProduct.sku$$"/> - </actionGroup> - <see selector="{{AdminProductGridSection.productGridCell('1', 'Name')}}" userInput="$$createProduct.name$$" stepKey="seeSimpleProductNameInGrid"/> - <see selector="{{AdminProductGridSection.productGridCell('1', 'Type')}}" userInput="Simple Product" stepKey="seeSimpleProductTypeInGrid"/> - <actionGroup ref="AdminClearFiltersActionGroup" stepKey="clearSimpleProductFilters"/> - <!--Assert simple product on storefront--> - <comment userInput="Assert simple product on storefront" stepKey="commentAssertSimpleProductOnStorefront"/> - <amOnPage url="{{StorefrontProductPage.url($$createProduct.name$$)}}" stepKey="openSimpleProductPage"/> - <waitForPageLoad stepKey="waitForStorefrontSimpleProductPageLoad"/> - <see userInput="IN STOCK" selector="{{StorefrontProductInfoMainSection.productStockStatus}}" stepKey="assertSimpleProductInStock"/> - <dontSeeElement selector="{{StorefrontDownloadableProductSection.downloadableLinkLabel(downloadableLinkWithMaxDownloads.title)}}" stepKey="dontSeeDownloadableLink" /> - </test> -</tests> diff --git a/app/code/Magento/Catalog/Test/Mftf/Test/AdminProductTypeSwitchingOnEditingTest/AdminDownloadableProductTypeSwitchingToSimpleProductTest.xml b/app/code/Magento/Catalog/Test/Mftf/Test/AdminProductTypeSwitchingOnEditingTest/AdminDownloadableProductTypeSwitchingToSimpleProductTest.xml new file mode 100644 index 0000000000000..9d82edd0fb50c --- /dev/null +++ b/app/code/Magento/Catalog/Test/Mftf/Test/AdminProductTypeSwitchingOnEditingTest/AdminDownloadableProductTypeSwitchingToSimpleProductTest.xml @@ -0,0 +1,46 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + /** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +--> +<tests xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/testSchema.xsd"> + <test name="AdminDownloadableProductTypeSwitchingToSimpleProductTest" extends="AdminVirtualProductTypeSwitchingToDownloadableProductTest"> + <annotations> + <features value="Catalog"/> + <stories value="Product type switching"/> + <title value="Downloadable product type switching on editing to Simple product"/> + <description value="Downloadable product type switching on editing to Simple product"/> + <testCaseId value="MC-17955"/> + <useCaseId value="MAGETWO-44170"/> + <severity value="MAJOR"/> + <group value="catalog"/> + </annotations> + <!--Change product type to Simple--> + <comment userInput="Change product type to Simple Product" stepKey="commentCreateSimple"/> + <actionGroup ref="AdminProductPageOpenByIdActionGroup" stepKey="gotToProductPage"> + <argument name="productId" value="$$createProduct.id$$"/> + </actionGroup> + <waitForPageLoad stepKey="waitForProductPageLoad"/> + <actionGroup ref="AdminAddDownloadableLinkInformationActionGroup" stepKey="addDownloadableLinkInformation"/> + <selectOption selector="{{AdminProductFormSection.productWeightSelect}}" userInput="This item has weight" stepKey="selectWeightForProduct"/> + <actionGroup ref="SaveProductFormActionGroup" stepKey="saveProductForm"/> + <!--Assert simple product on Admin product page grid--> + <comment userInput="Assert simple product in Admin product page grid" stepKey="commentAssertProductOnAdmin"/> + <amOnPage url="{{AdminCatalogProductPage.url}}" stepKey="goToCatalogSimpleProductPage"/> + <actionGroup ref="FilterProductGridBySku2ActionGroup" stepKey="filterSimpleProductGridBySku"> + <argument name="sku" value="$$createProduct.sku$$"/> + </actionGroup> + <see selector="{{AdminProductGridSection.productGridCell('1', 'Name')}}" userInput="$$createProduct.name$$" stepKey="seeSimpleProductNameInGrid"/> + <see selector="{{AdminProductGridSection.productGridCell('1', 'Type')}}" userInput="Simple Product" stepKey="seeSimpleProductTypeInGrid"/> + <actionGroup ref="AdminClearFiltersActionGroup" stepKey="clearSimpleProductFilters"/> + <!--Assert simple product on storefront--> + <comment userInput="Assert simple product on storefront" stepKey="commentAssertSimpleProductOnStorefront"/> + <amOnPage url="{{StorefrontProductPage.url($$createProduct.name$$)}}" stepKey="openSimpleProductPage"/> + <waitForPageLoad stepKey="waitForStorefrontSimpleProductPageLoad"/> + <see userInput="IN STOCK" selector="{{StorefrontProductInfoMainSection.productStockStatus}}" stepKey="assertSimpleProductInStock"/> + <dontSeeElement selector="{{StorefrontDownloadableProductSection.downloadableLinkLabel(downloadableLinkWithMaxDownloads.title)}}" stepKey="dontSeeDownloadableLink"/> + </test> +</tests> diff --git a/app/code/Magento/Catalog/Test/Mftf/Test/AdminProductTypeSwitchingOnEditingTest/AdminVirtualProductTypeSwitchingToDownloadableProductTest.xml b/app/code/Magento/Catalog/Test/Mftf/Test/AdminProductTypeSwitchingOnEditingTest/AdminVirtualProductTypeSwitchingToDownloadableProductTest.xml new file mode 100644 index 0000000000000..12d654508d7d7 --- /dev/null +++ b/app/code/Magento/Catalog/Test/Mftf/Test/AdminProductTypeSwitchingOnEditingTest/AdminVirtualProductTypeSwitchingToDownloadableProductTest.xml @@ -0,0 +1,67 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + /** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +--> +<tests xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/testSchema.xsd"> + <test name="AdminVirtualProductTypeSwitchingToDownloadableProductTest"> + <annotations> + <features value="Catalog"/> + <stories value="Product type switching"/> + <title value="Virtual product type switching on editing to Downloadable product"/> + <description value="Virtual product type switching on editing to Downloadable product"/> + <testCaseId value="MC-17954"/> + <useCaseId value="MAGETWO-44170"/> + <severity value="MAJOR"/> + <group value="catalog"/> + </annotations> + <before> + <!-- Add downloadable domains --> + <magentoCLI stepKey="addDownloadableDomain" command="downloadable:domains:add example.com static.magento.com"/> + <actionGroup ref="AdminLoginActionGroup" stepKey="loginAsAdmin"/> + <!--Create product--> + <comment userInput="Create product" stepKey="commentCreateProduct"/> + <createData entity="VirtualProduct" stepKey="createProduct"/> + </before> + <after> + <!-- Remove downloadable domains --> + <magentoCLI stepKey="removeDownloadableDomain" command="downloadable:domains:remove example.com static.magento.com"/> + <!--Delete product--> + <comment userInput="Delete product" stepKey="commentDeleteProduct"/> + <deleteData createDataKey="createProduct" stepKey="deleteProduct"/> + <actionGroup ref="AdminClearFiltersActionGroup" stepKey="clearProductFilters"/> + <actionGroup ref="AdminLogoutActionGroup" stepKey="logout"/> + </after> + <!--Change product type to Downloadable--> + <comment userInput="Change product type to Downloadable" stepKey="commentCreateDownloadable"/> + <actionGroup ref="AdminProductPageOpenByIdActionGroup" stepKey="gotToDownloadableProductPage"> + <argument name="productId" value="$$createProduct.id$$"/> + </actionGroup> + <waitForPageLoad stepKey="waitForDownloadableProductPageLoad"/> + <actionGroup ref="AdminAddDownloadableLinkInformationActionGroup" stepKey="addDownloadableLinkInformation"/> + <checkOption selector="{{AdminProductDownloadableSection.isLinksPurchasedSeparately}}" stepKey="checkOptionPurchaseSeparately"/> + <actionGroup ref="AddDownloadableProductLinkWithMaxDownloadsActionGroup" stepKey="addDownloadableProductLink"> + <argument name="link" value="downloadableLinkWithMaxDownloads"/> + </actionGroup> + <actionGroup ref="SaveProductFormActionGroup" stepKey="saveDownloadableProductForm"/> + <!--Assert downloadable product on Admin product page grid--> + <comment userInput="Assert configurable product in Admin product page grid" stepKey="commentAssertDownloadableProductOnAdmin"/> + <amOnPage url="{{AdminCatalogProductPage.url}}" stepKey="goToCatalogProductPage"/> + <actionGroup ref="FilterProductGridBySku2ActionGroup" stepKey="filterProductGridBySku"> + <argument name="sku" value="$$createProduct.sku$$"/> + </actionGroup> + <see selector="{{AdminProductGridSection.productGridCell('1', 'Name')}}" userInput="$$createProduct.name$$" stepKey="seeDownloadableProductNameInGrid"/> + <see selector="{{AdminProductGridSection.productGridCell('1', 'Type')}}" userInput="Downloadable Product" stepKey="seeDownloadableProductTypeInGrid"/> + <actionGroup ref="AdminClearFiltersActionGroup" stepKey="clearDownloadableProductFilters"/> + <!--Assert downloadable product on storefront--> + <comment userInput="Assert downloadable product on storefront" stepKey="commentAssertDownloadableProductOnStorefront"/> + <amOnPage url="{{StorefrontProductPage.url($$createProduct.name$$)}}" stepKey="openDownloadableProductPage"/> + <waitForPageLoad stepKey="waitForStorefrontDownloadableProductPageLoad"/> + <see userInput="IN STOCK" selector="{{StorefrontProductInfoMainSection.productStockStatus}}" stepKey="assertDownloadableProductInStock"/> + <scrollTo selector="{{StorefrontDownloadableProductSection.downloadableLinkBlock}}" stepKey="scrollToLinksInStorefront"/> + <seeElement selector="{{StorefrontDownloadableProductSection.downloadableLinkLabel(downloadableLinkWithMaxDownloads.title)}}" stepKey="seeDownloadableLink"/> + </test> +</tests> diff --git a/app/code/Magento/Catalog/Test/Mftf/Test/AdminRemoveCustomOptionsFromProductTest.xml b/app/code/Magento/Catalog/Test/Mftf/Test/AdminRemoveCustomOptionsFromProductTest.xml index fb54b0b601d85..96ee795998459 100644 --- a/app/code/Magento/Catalog/Test/Mftf/Test/AdminRemoveCustomOptionsFromProductTest.xml +++ b/app/code/Magento/Catalog/Test/Mftf/Test/AdminRemoveCustomOptionsFromProductTest.xml @@ -28,11 +28,13 @@ <deleteData createDataKey="createProduct" stepKey="deleteProductWithOptions"/> <amOnPage url="{{AdminProductIndexPage.url}}" stepKey="navigateToProductIndex"/> <actionGroup ref="ClearFiltersAdminDataGridActionGroup" stepKey="clearProductFilter"/> - <actionGroup ref="logout" stepKey="logout"/> + <actionGroup ref="AdminLogoutActionGroup" stepKey="logout"/> </after> - <actionGroup ref="LoginAsAdmin" stepKey="loginAsAdmin"/> + <actionGroup ref="AdminLoginActionGroup" stepKey="loginAsAdmin"/> <!-- Edit Simple Product --> - <amOnPage url="{{AdminProductEditPage.url($$createProduct.id$$)}}" stepKey="goToProduct"/> + <actionGroup ref="AdminProductPageOpenByIdActionGroup" stepKey="goToProduct"> + <argument name="productId" value="$$createProduct.id$$"/> + </actionGroup> <!-- See 3 options are present --> <actionGroup ref="AdminAssertProductCustomOptionVisibleActionGroup" stepKey="assertCustomOptionsField"> <argument name="option" value="ProductOptionField"/> @@ -84,7 +86,15 @@ <argument name="option" value="ProductOptionFieldSecond"/> </actionGroup> <!-- Delete All options and See no more options present on the page --> - <actionGroup ref="AdminDeleteAllProductCustomOptionsActionGroup" stepKey="deleteAllCustomOptions"/> + <actionGroup ref="AdminDeleteProductCustomOptionActionGroup" stepKey="deleteCustomOptionField"> + <argument name="option" value="ProductOptionField"/> + </actionGroup> + <actionGroup ref="AdminDeleteProductCustomOptionActionGroup" stepKey="deleteCustomOptionFile2"> + <argument name="option" value="ProductOptionFileSecond"/> + </actionGroup> + <actionGroup ref="AdminDeleteProductCustomOptionActionGroup" stepKey="deleteCustomOptionFieldSecond"> + <argument name="option" value="ProductOptionFieldSecond"/> + </actionGroup> <!-- Product successfully saved and it has no options --> <actionGroup ref="SaveProductFormActionGroup" stepKey="saveProductWithoutCustomOptions"/> <actionGroup ref="AdminAssertProductHasNoCustomOptionsActionGroup" stepKey="assertNoCustomOptions"/> diff --git a/app/code/Magento/Catalog/Test/Mftf/Test/AdminRemoveDefaultImageSimpleProductTest.xml b/app/code/Magento/Catalog/Test/Mftf/Test/AdminRemoveDefaultImageSimpleProductTest.xml index 3b750c2cdb21c..00eaa623e2bca 100644 --- a/app/code/Magento/Catalog/Test/Mftf/Test/AdminRemoveDefaultImageSimpleProductTest.xml +++ b/app/code/Magento/Catalog/Test/Mftf/Test/AdminRemoveDefaultImageSimpleProductTest.xml @@ -19,7 +19,7 @@ <group value="Catalog"/> </annotations> <before> - <actionGroup ref="LoginAsAdmin" stepKey="loginAsAdmin"/> + <actionGroup ref="AdminLoginActionGroup" stepKey="loginAsAdmin"/> </before> <after> <amOnPage url="{{AdminLogoutPage.url}}" stepKey="amOnLogoutPage"/> diff --git a/app/code/Magento/Catalog/Test/Mftf/Test/AdminRemoveDefaultImageVirtualProductTest.xml b/app/code/Magento/Catalog/Test/Mftf/Test/AdminRemoveDefaultImageVirtualProductTest.xml index 6a68928be8c70..6cc1b256e5ec9 100644 --- a/app/code/Magento/Catalog/Test/Mftf/Test/AdminRemoveDefaultImageVirtualProductTest.xml +++ b/app/code/Magento/Catalog/Test/Mftf/Test/AdminRemoveDefaultImageVirtualProductTest.xml @@ -19,7 +19,7 @@ <group value="Catalog"/> </annotations> <before> - <actionGroup ref="LoginAsAdmin" stepKey="loginAsAdmin"/> + <actionGroup ref="AdminLoginActionGroup" stepKey="loginAsAdmin"/> </before> <after> <amOnPage url="{{AdminLogoutPage.url}}" stepKey="amOnLogoutPage"/> diff --git a/app/code/Magento/Catalog/Test/Mftf/Test/AdminRemoveDefaultVideoSimpleProductTest.xml b/app/code/Magento/Catalog/Test/Mftf/Test/AdminRemoveDefaultVideoSimpleProductTest.xml index 8c80a2bf9a851..60c32004e3ca8 100644 --- a/app/code/Magento/Catalog/Test/Mftf/Test/AdminRemoveDefaultVideoSimpleProductTest.xml +++ b/app/code/Magento/Catalog/Test/Mftf/Test/AdminRemoveDefaultVideoSimpleProductTest.xml @@ -17,10 +17,13 @@ <severity value="MAJOR"/> <testCaseId value="MC-206"/> <group value="Catalog"/> + <skip> + <issueId value="MC-32197"/> + </skip> </annotations> <before> <actionGroup ref="EnableAdminAccountSharingActionGroup" stepKey="enableAdminAccountSharing"/> - <actionGroup ref="LoginAsAdmin" stepKey="loginAsAdmin"/> + <actionGroup ref="AdminLoginActionGroup" stepKey="loginAsAdmin"/> </before> <after> <amOnPage url="{{AdminLogoutPage.url}}" stepKey="amOnLogoutPage"/> diff --git a/app/code/Magento/Catalog/Test/Mftf/Test/AdminRemoveDefaultVideoVirtualProductTest.xml b/app/code/Magento/Catalog/Test/Mftf/Test/AdminRemoveDefaultVideoVirtualProductTest.xml index 8d89e0d9b535b..0104eff068e0b 100644 --- a/app/code/Magento/Catalog/Test/Mftf/Test/AdminRemoveDefaultVideoVirtualProductTest.xml +++ b/app/code/Magento/Catalog/Test/Mftf/Test/AdminRemoveDefaultVideoVirtualProductTest.xml @@ -17,6 +17,9 @@ <severity value="MAJOR"/> <testCaseId value="MC-204"/> <group value="Catalog"/> + <skip> + <issueId value="MC-32197"/> + </skip> </annotations> <!-- Replacing steps in base AdminRemoveDefaultVideoSimpleProductTest --> diff --git a/app/code/Magento/Catalog/Test/Mftf/Test/AdminRemoveImageAffectsAllScopesTest.xml b/app/code/Magento/Catalog/Test/Mftf/Test/AdminRemoveImageAffectsAllScopesTest.xml index 47f497b381553..6cd76c4cc06b8 100644 --- a/app/code/Magento/Catalog/Test/Mftf/Test/AdminRemoveImageAffectsAllScopesTest.xml +++ b/app/code/Magento/Catalog/Test/Mftf/Test/AdminRemoveImageAffectsAllScopesTest.xml @@ -20,7 +20,7 @@ </annotations> <before> <!--Create 2 websites (with stores, store views)--> - <actionGroup ref="LoginAsAdmin" stepKey="loginAsAdmin"/> + <actionGroup ref="AdminLoginActionGroup" stepKey="loginAsAdmin"/> <createData entity="_defaultCategory" stepKey="category"/> <createData entity="_defaultProduct" stepKey="product"> <requiredEntity createDataKey="category"/> @@ -67,7 +67,7 @@ <deleteData createDataKey="product" stepKey="deleteFirstProduct"/> <magentoCLI stepKey="reindex" command="indexer:reindex"/> <magentoCLI stepKey="flushCache" command="cache:flush"/> - <actionGroup ref="logout" stepKey="logout"/> + <actionGroup ref="AdminLogoutActionGroup" stepKey="logout"/> </after> <!--Create product--> diff --git a/app/code/Magento/Catalog/Test/Mftf/Test/AdminRemoveImageFromCategoryTest.xml b/app/code/Magento/Catalog/Test/Mftf/Test/AdminRemoveImageFromCategoryTest.xml index ef276114b4de5..8033a2dffec7c 100644 --- a/app/code/Magento/Catalog/Test/Mftf/Test/AdminRemoveImageFromCategoryTest.xml +++ b/app/code/Magento/Catalog/Test/Mftf/Test/AdminRemoveImageFromCategoryTest.xml @@ -19,13 +19,13 @@ <group value="Catalog"/> </annotations> <before> - <actionGroup ref="LoginAsAdmin" stepKey="loginAsAdmin"/> + <actionGroup ref="AdminLoginActionGroup" stepKey="loginAsAdmin"/> </before> <after> <actionGroup ref="DeleteCategoryActionGroup" stepKey="DeleteCategory"> <argument name="categoryEntity" value="SimpleSubCategory"/> </actionGroup> - <actionGroup ref="logout" stepKey="logout"/> + <actionGroup ref="AdminLogoutActionGroup" stepKey="logout"/> </after> <!-- Go to create a new category with image --> @@ -34,12 +34,12 @@ <argument name="categoryEntity" value="SimpleSubCategory"/> </actionGroup> <actionGroup ref="AddCategoryImageActionGroup" stepKey="addCategoryImage"/> - <actionGroup ref="SaveCategoryFormActionGroup" stepKey="saveCategoryForm"/> + <actionGroup ref="AdminSaveCategoryFormActionGroup" stepKey="saveCategoryForm"/> <actionGroup ref="CheckCategoryImageInAdminActionGroup" stepKey="checkCategoryImageInAdmin"/> <!-- Remove image from category --> <actionGroup ref="RemoveCategoryImageActionGroup" stepKey="removeCategoryImage"/> - <actionGroup ref="SaveCategoryFormActionGroup" stepKey="saveCategoryFormAfterRemove"/> + <actionGroup ref="AdminSaveCategoryFormActionGroup" stepKey="saveCategoryFormAfterRemove"/> <actionGroup ref="CheckCategoryOnStorefrontActionGroup" stepKey="CheckCategoryOnStorefront"> <argument name="categoryEntity" value="SimpleSubCategory"/> diff --git a/app/code/Magento/Catalog/Test/Mftf/Test/AdminRequiredFieldsHaveRequiredFieldIndicatorTest.xml b/app/code/Magento/Catalog/Test/Mftf/Test/AdminRequiredFieldsHaveRequiredFieldIndicatorTest.xml index ebae27a1f7182..0281fded3a8e4 100644 --- a/app/code/Magento/Catalog/Test/Mftf/Test/AdminRequiredFieldsHaveRequiredFieldIndicatorTest.xml +++ b/app/code/Magento/Catalog/Test/Mftf/Test/AdminRequiredFieldsHaveRequiredFieldIndicatorTest.xml @@ -18,20 +18,26 @@ <group value="Catalog"/> </annotations> <after> - <actionGroup ref="logout" stepKey="adminLogout"/> + <actionGroup ref="AdminLogoutActionGroup" stepKey="adminLogout"/> </after> - <actionGroup ref="LoginAsAdmin" stepKey="loginAsAdmin1"/> + <actionGroup ref="AdminLoginActionGroup" stepKey="loginAsAdmin1"/> <amOnPage url="{{AdminCategoryPage.url}}" stepKey="navigateToCategoryPage"/> <waitForElementVisible selector="{{AdminCategorySidebarActionSection.AddSubcategoryButton}}" stepKey="waitForAddSubCategoryVisible"/> <click selector="{{AdminCategorySidebarActionSection.AddSubcategoryButton}}" stepKey="clickOnAddSubCategory"/> <!-- Verify that the Category Name field has the required field name indicator --> <executeJS function="{{AdminCategoryBasicFieldSection.RequiredFieldIndicator}}" stepKey="getRequiredFieldIndicator"/> - <assertEquals expected='"*"' expectedType="string" actualType="variable" actual="getRequiredFieldIndicator" message="pass" stepKey="assertRequiredFieldIndicator1"/> + <assertEquals message="pass" stepKey="assertRequiredFieldIndicator1"> + <actualResult type="variable">getRequiredFieldIndicator</actualResult> + <expectedResult type="string">"*"</expectedResult> + </assertEquals> <executeJS function="{{AdminCategoryBasicFieldSection.RequiredFieldIndicatorColor}}" stepKey="getRequiredFieldIndicatorColor"/> - <assertEquals expected="rgb(226, 38, 38)" expectedType="string" actualType="variable" actual="getRequiredFieldIndicatorColor" message="pass" stepKey="assertRequiredFieldIndicator2"/> + <assertEquals message="pass" stepKey="assertRequiredFieldIndicator2"> + <actualResult type="variable">getRequiredFieldIndicatorColor</actualResult> + <expectedResult type="string">rgb(226, 38, 38)</expectedResult> + </assertEquals> <amOnPage url="{{AdminProductIndexPage.url}}" stepKey="navigateToProductIndexPage"/> <click selector="{{AdminProductGridActionSection.addProductToggle}}" stepKey="addProductDropdown"/> @@ -39,16 +45,25 @@ <!-- Verify that the Product Name and Sku fields have the required field name indicator --> <executeJS function="{{AdminProductFormSection.RequiredNameIndicator}}" stepKey="productNameRequiredFieldIndicator"/> - <assertEquals expected='"*"' expectedType="string" actualType="variable" actual="productNameRequiredFieldIndicator" message="pass" stepKey="assertRequiredFieldIndicator3"/> + <assertEquals message="pass" stepKey="assertRequiredFieldIndicator3"> + <actualResult type="variable">productNameRequiredFieldIndicator</actualResult> + <expectedResult type="string">"*"</expectedResult> + </assertEquals> <executeJS function="{{AdminProductFormSection.RequiredSkuIndicator}}" stepKey="productSkuRequiredFieldIndicator"/> - <assertEquals expected='"*"' expectedType="string" actualType="variable" actual="productSkuRequiredFieldIndicator" message="pass" stepKey="assertRequiredFieldIndicator4"/> + <assertEquals message="pass" stepKey="assertRequiredFieldIndicator4"> + <actualResult type="variable">productSkuRequiredFieldIndicator</actualResult> + <expectedResult type="string">"*"</expectedResult> + </assertEquals> <!-- Verify that the CMS page have the required field name indicator next to Page Title --> <amOnPage url="{{CmsPagesPage.url}}" stepKey="amOnPagePagesGrid"/> <waitForPageLoad stepKey="waitForPageLoad1"/> <click selector="{{CmsPagesPageActionsSection.addNewPageButton}}" stepKey="clickAddNewPage"/> <executeJS function="{{CmsNewPagePageBasicFieldsSection.RequiredFieldIndicator}}" stepKey="pageTitleRequiredFieldIndicator"/> - <assertEquals expected='"*"' expectedType="string" actualType="variable" actual="pageTitleRequiredFieldIndicator" message="pass" stepKey="assertRequiredFieldIndicator5"/> + <assertEquals message="pass" stepKey="assertRequiredFieldIndicator5"> + <actualResult type="variable">pageTitleRequiredFieldIndicator</actualResult> + <expectedResult type="string">"*"</expectedResult> + </assertEquals> </test> </tests> diff --git a/app/code/Magento/Catalog/Test/Mftf/Test/AdminRestrictedUserAddCategoryFromProductPageTest.xml b/app/code/Magento/Catalog/Test/Mftf/Test/AdminRestrictedUserAddCategoryFromProductPageTest.xml index 05008b32ed4fb..e89e89205f3f5 100644 --- a/app/code/Magento/Catalog/Test/Mftf/Test/AdminRestrictedUserAddCategoryFromProductPageTest.xml +++ b/app/code/Magento/Catalog/Test/Mftf/Test/AdminRestrictedUserAddCategoryFromProductPageTest.xml @@ -20,7 +20,7 @@ <group value="catalog"/> </annotations> <before> - <actionGroup ref="LoginAsAdmin" stepKey="loginAsAdmin"/> + <actionGroup ref="AdminLoginActionGroup" stepKey="loginAsAdmin"/> <!--Create category--> <comment userInput="Create category" stepKey="commentCreateCategory"/> <createData entity="SimpleSubCategory" stepKey="createCategory"/> @@ -32,8 +32,8 @@ <argument name="sku" value="{{_defaultProduct.sku}}"/> </actionGroup> <actionGroup ref="ResetProductGridToDefaultViewActionGroup" stepKey="resetFiltersIfExist"/> - <actionGroup ref="logout" stepKey="logoutOfUser"/> - <actionGroup ref="LoginAsAdmin" stepKey="loginAsAdmin"/> + <actionGroup ref="AdminLogoutActionGroup" stepKey="logoutOfUser"/> + <actionGroup ref="AdminLoginActionGroup" stepKey="loginAsAdmin"/> <!--Delete created data--> <comment userInput="Delete created data" stepKey="commentDeleteCreatedData"/> <amOnPage url="{{AdminRolesPage.url}}" stepKey="navigateToUserRoleGrid" /> @@ -47,7 +47,7 @@ <argument name="userName" value="{{admin2.username}}"/> </actionGroup> <deleteData createDataKey="createCategory" stepKey="deleteCategory"/> - <actionGroup ref="logout" stepKey="logoutOfAdmin"/> + <actionGroup ref="AdminLogoutActionGroup" stepKey="logoutOfAdmin"/> </after> <!--Create user role--> <comment userInput="Create user role" stepKey="commentCreateUserRole"/> @@ -74,9 +74,10 @@ </actionGroup> <!--Log out of admin and login with newly created user--> <comment userInput="Log out of admin and login with newly created user" stepKey="commentLoginWithNewUser"/> - <actionGroup ref="logout" stepKey="logoutOfAdmin"/> - <actionGroup ref="LoginAsAdmin" stepKey="loginAsNewUser"> - <argument name="adminUser" value="admin2"/> + <actionGroup ref="AdminLogoutActionGroup" stepKey="logoutOfAdmin"/> + <actionGroup ref="AdminLoginActionGroup" stepKey="loginAsNewUser"> + <argument name="username" value="{{admin2.username}}"/> + <argument name="password" value="{{admin2.password}}"/> </actionGroup> <!--Go to create product page--> <comment userInput="Go to create product page" stepKey="commentGoCreateProductPage"/> diff --git a/app/code/Magento/Catalog/Test/Mftf/Test/AdminShouldBeAbleToAssociateSimpleProductToWebsitesTest.xml b/app/code/Magento/Catalog/Test/Mftf/Test/AdminShouldBeAbleToAssociateSimpleProductToWebsitesTest.xml index 9d19a1dedf7ef..00b9e77d5fe5e 100644 --- a/app/code/Magento/Catalog/Test/Mftf/Test/AdminShouldBeAbleToAssociateSimpleProductToWebsitesTest.xml +++ b/app/code/Magento/Catalog/Test/Mftf/Test/AdminShouldBeAbleToAssociateSimpleProductToWebsitesTest.xml @@ -23,7 +23,7 @@ <magentoCLI command="config:set {{StorefrontEnableAddStoreCodeToUrls.path}} {{StorefrontEnableAddStoreCodeToUrls.value}}" stepKey="setAddStoreCodeToUrlsToYes"/> <createData entity="secondCustomWebsite" stepKey="createCustomWebsite"/> <createData entity="SimpleProduct2" stepKey="createSimpleProduct"/> - <actionGroup ref="LoginAsAdmin" stepKey="loginAsAdmin"/> + <actionGroup ref="AdminLoginActionGroup" stepKey="loginAsAdmin"/> <actionGroup ref="AdminStoreGroupCreateActionGroup" stepKey="createNewStore"> <argument name="Website" value="secondCustomWebsite"/> <argument name="storeGroup" value="customStoreGroup"/> @@ -44,7 +44,7 @@ <actionGroup ref="AdminGridFilterResetActionGroup" stepKey="resetFiltersOnStoresIndexPage"/> <actionGroup ref="AdminOpenProductIndexPageActionGroup" stepKey="openProductIndexPageToResetFilters"/> <actionGroup ref="ClearFiltersAdminDataGridActionGroup" stepKey="clearFiltersOnProductIndexPage"/> - <actionGroup ref="logout" stepKey="logout"/> + <actionGroup ref="AdminLogoutActionGroup" stepKey="logout"/> </after> <!-- 1. Go to product page in admin panel to edit --> diff --git a/app/code/Magento/Catalog/Test/Mftf/Test/AdminSimpleProductEditUiTest.xml b/app/code/Magento/Catalog/Test/Mftf/Test/AdminSimpleProductEditUiTest.xml index bc5a0319bae7a..3ee8d0e3c2571 100644 --- a/app/code/Magento/Catalog/Test/Mftf/Test/AdminSimpleProductEditUiTest.xml +++ b/app/code/Magento/Catalog/Test/Mftf/Test/AdminSimpleProductEditUiTest.xml @@ -33,8 +33,10 @@ </after> <!--check admin for valid Enable Status label--> - <actionGroup ref="LoginAsAdmin" stepKey="loginAsAdmin"/> - <amOnPage url="{{AdminProductEditPage.url($$createSimpleProduct.id$$)}}" stepKey="goToEditPage"/> + <actionGroup ref="AdminLoginActionGroup" stepKey="loginAsAdmin"/> + <actionGroup ref="AdminProductPageOpenByIdActionGroup" stepKey="goToEditPage"> + <argument name="productId" value="$$createSimpleProduct.id$$"/> + </actionGroup> <waitForPageLoad stepKey="wait1"/> <seeCheckboxIsChecked selector="{{AdminProductFormSection.productStatus}}" stepKey="seeCheckboxEnableProductIsChecked"/> @@ -46,4 +48,4 @@ <click selector="{{AdminProductFormSection.enableProductAttributeLabel}}" stepKey="clickEnableProductLabel"/> <dontSeeCheckboxIsChecked selector="{{AdminProductFormSection.productStatus}}" stepKey="dontSeeCheckboxEnableProductIsCheckedAfterLabelClick"/> </test> -</tests> \ No newline at end of file +</tests> diff --git a/app/code/Magento/Catalog/Test/Mftf/Test/AdminSimpleProductImagesTest.xml b/app/code/Magento/Catalog/Test/Mftf/Test/AdminSimpleProductImagesTest.xml deleted file mode 100644 index ff8240655ca03..0000000000000 --- a/app/code/Magento/Catalog/Test/Mftf/Test/AdminSimpleProductImagesTest.xml +++ /dev/null @@ -1,306 +0,0 @@ -<?xml version="1.0" encoding="UTF-8"?> -<!-- - /** - * Copyright © Magento, Inc. All rights reserved. - * See COPYING.txt for license details. - */ ---> - -<tests xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" - xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/testSchema.xsd"> - <test name="AdminSimpleProductImagesTest"> - <annotations> - <features value="Catalog"/> - <stories value="Add/remove images and videos for all product types and category"/> - <title value="Admin should be able to add images of different types and sizes to Simple Product"/> - <description value="Admin should be able to add images of different types and sizes to Simple Product"/> - <severity value="AVERAGE"/> - <testCaseId value="MC-189"/> - <group value="Catalog"/> - </annotations> - - <before> - <createData entity="_defaultCategory" stepKey="category"/> - <createData entity="_defaultProduct" stepKey="firstProduct"> - <requiredEntity createDataKey="category"/> - </createData> - <createData entity="_defaultProduct" stepKey="secondProduct"> - <requiredEntity createDataKey="category"/> - </createData> - <actionGroup ref="LoginAsAdmin" stepKey="loginAsAdmin"/> - </before> - - <after> - <deleteData createDataKey="category" stepKey="deletePreReqCategory"/> - <deleteData createDataKey="firstProduct" stepKey="deleteFirstProduct"/> - <deleteData createDataKey="secondProduct" stepKey="deleteSecondProduct"/> - <actionGroup ref="logout" stepKey="adminLogout"/> - </after> - - <!-- Go to the first product edit page --> - <amOnPage url="{{AdminProductIndexPage.url}}" stepKey="goToProductIndex"/> - <waitForPageLoad stepKey="wait1"/> - <actionGroup ref="ResetProductGridToDefaultViewActionGroup" stepKey="resetProductGrid"/> - <actionGroup ref="FilterProductGridBySkuActionGroup" stepKey="filterProductGridBySku"> - <argument name="product" value="$$firstProduct$$"/> - </actionGroup> - <actionGroup ref="OpenProductForEditByClickingRowXColumnYInProductGridActionGroup" stepKey="openProducForEditByClickingRow1Column2InProductGrid"/> - - <!-- Set url key --> - <click selector="{{AdminProductSEOSection.sectionHeader}}" stepKey="openSeoSection"/> - <fillField selector="{{AdminProductSEOSection.urlKeyInput}}" userInput="$$firstProduct.name$$" stepKey="fillUrlKey"/> - - <click selector="{{AdminProductImagesSection.productImagesToggle}}" stepKey="expandImages"/> - - <!-- *.bmp is not allowed --> - <attachFile selector="{{AdminProductImagesSection.imageFileUpload}}" userInput="bmp.bmp" stepKey="attachBmp"/> - <waitForPageLoad stepKey="waitForUploadBmp"/> - <see selector="{{AdminProductMessagesSection.errorMessage}}" userInput="bmp.bmp was not uploaded. Disallowed file type." stepKey="seeErrorBmp"/> - <click selector="{{AdminProductImagesSection.modalOkBtn}}" stepKey="closeModalBmp"/> - - <!-- *.ico is not allowed --> - <attachFile selector="{{AdminProductImagesSection.imageFileUpload}}" userInput="ico.ico" stepKey="attachIco"/> - <waitForPageLoad stepKey="waitForUploadIco"/> - <see selector="{{AdminProductMessagesSection.errorMessage}}" userInput="ico.ico was not uploaded. Disallowed file type." stepKey="seeErrorIco"/> - <click selector="{{AdminProductImagesSection.modalOkBtn}}" stepKey="closeModalIco"/> - - <!-- *.svg is not allowed --> - <attachFile selector="{{AdminProductImagesSection.imageFileUpload}}" userInput="svg.svg" stepKey="attachSvg"/> - <waitForPageLoad stepKey="waitForUploadSvg"/> - <see selector="{{AdminProductMessagesSection.errorMessage}}" userInput="svg.svg was not uploaded. Disallowed file type." stepKey="seeErrorSvg"/> - <click selector="{{AdminProductImagesSection.modalOkBtn}}" stepKey="closeModalSvg"/> - - <!-- 0kb size is not allowed --> - <attachFile selector="{{AdminProductImagesSection.imageFileUpload}}" userInput="empty.jpg" stepKey="attachEmpty"/> - <waitForPageLoad stepKey="waitForUploadEmpty"/> - <see selector="{{AdminProductMessagesSection.errorMessage}}" userInput="empty.jpg was not uploaded." stepKey="seeErrorEmpty"/> - <click selector="{{AdminProductImagesSection.modalOkBtn}}" stepKey="closeModalEmpty"/> - - <!-- 1~ kb is allowed --> - <attachFile selector="{{AdminProductImagesSection.imageFileUpload}}" userInput="small.jpg" stepKey="attachSmall"/> - <waitForPageLoad stepKey="waitForUploadSmall"/> - <dontSeeElement selector="{{AdminProductMessagesSection.errorMessage}}" stepKey="dontSeeErrorSmall"/> - - <!-- 1~ mb is allowed --> - <attachFile selector="{{AdminProductImagesSection.imageFileUpload}}" userInput="medium.jpg" stepKey="attachMedium"/> - <waitForPageLoad stepKey="waitForUploadMedium"/> - <dontSeeElement selector="{{AdminProductMessagesSection.errorMessage}}" stepKey="dontSeeErrorMedium"/> - - <!-- 10~ mb is allowed --> - <attachFile selector="{{AdminProductImagesSection.imageFileUpload}}" userInput="large.jpg" stepKey="attachLarge"/> - <waitForPageLoad stepKey="waitForUploadLarge"/> - <dontSeeElement selector="{{AdminProductMessagesSection.errorMessage}}" stepKey="dontSeeErrorLarge"/> - - <!-- *.gif is allowed --> - <attachFile selector="{{AdminProductImagesSection.imageFileUpload}}" userInput="gif.gif" stepKey="attachGif"/> - <waitForPageLoad stepKey="waitForUploadGif"/> - <dontSeeElement selector="{{AdminProductMessagesSection.errorMessage}}" stepKey="dontSeeErrorGif"/> - - <!-- *.jpg is allowed --> - <attachFile selector="{{AdminProductImagesSection.imageFileUpload}}" userInput="jpg.jpg" stepKey="attachJpg"/> - <waitForPageLoad stepKey="waitForUploadJpg"/> - <dontSeeElement selector="{{AdminProductMessagesSection.errorMessage}}" stepKey="dontSeeErrorJpg"/> - - <!-- *.png is allowed --> - <attachFile selector="{{AdminProductImagesSection.imageFileUpload}}" userInput="png.png" stepKey="attachPng"/> - <waitForPageLoad stepKey="waitForUploadPng"/> - <dontSeeElement selector="{{AdminProductMessagesSection.errorMessage}}" stepKey="dontSeeErrorPng"/> - - <!-- Save the first product and go to the storefront --> - <click selector="{{AdminProductFormActionSection.saveButton}}" stepKey="saveProduct"/> - <amOnPage url="$$firstProduct.name$$.html" stepKey="goToStorefront"/> - <waitForPageLoad stepKey="waitForStorefront"/> - - <!-- See all of the images that we uploaded --> - <seeElementInDOM selector="{{StorefrontProductMediaSection.imageFile('small')}}" stepKey="seeSmall"/> - <seeElementInDOM selector="{{StorefrontProductMediaSection.imageFile('medium')}}" stepKey="seeMedium"/> - <seeElementInDOM selector="{{StorefrontProductMediaSection.imageFile('large')}}" stepKey="seeLarge"/> - <seeElementInDOM selector="{{StorefrontProductMediaSection.imageFile('gif')}}" stepKey="seeGif"/> - <seeElementInDOM selector="{{StorefrontProductMediaSection.imageFile('jpg')}}" stepKey="seeJpg"/> - <seeElementInDOM selector="{{StorefrontProductMediaSection.imageFile('png')}}" stepKey="seePng"/> - - <!-- Go to the category page and see a placeholder image for the second product --> - <amOnPage url="$$category.name$$.html" stepKey="goToCategoryPage"/> - <seeElement selector=".products-grid img[src*='placeholder/small_image.jpg']" stepKey="seePlaceholder"/> - - <!-- Go to the second product edit page --> - <amOnPage url="{{AdminProductIndexPage.url}}" stepKey="goToProductIndex2"/> - <waitForPageLoad stepKey="wait2"/> - <actionGroup ref="ResetProductGridToDefaultViewActionGroup" stepKey="resetProductGrid2"/> - <actionGroup ref="FilterProductGridBySkuActionGroup" stepKey="filterProductGridBySku2"> - <argument name="product" value="$$secondProduct$$"/> - </actionGroup> - <actionGroup ref="OpenProductForEditByClickingRowXColumnYInProductGridActionGroup" stepKey="openProducForEditByClickingRow1Column2InProductGrid2"/> - - <!-- Upload an image --> - <click selector="{{AdminProductImagesSection.productImagesToggle}}" stepKey="expandImages2"/> - <attachFile selector="{{AdminProductImagesSection.imageFileUpload}}" userInput="large.jpg" stepKey="attachLarge2"/> - <waitForPageLoad stepKey="waitForUploadLarge2"/> - <dontSeeElement selector="{{AdminProductMessagesSection.errorMessage}}" stepKey="dontSeeErrorLarge2"/> - - <!-- Set url key --> - <click selector="{{AdminProductSEOSection.sectionHeader}}" stepKey="openSeoSection2"/> - <fillField selector="{{AdminProductSEOSection.urlKeyInput}}" userInput="$$secondProduct.name$$" stepKey="fillUrlKey2"/> - - <!-- Save the second product --> - <click selector="{{AdminProductFormActionSection.saveButton}}" stepKey="saveProduct2"/> - <magentoCLI command="indexer:reindex" stepKey="reindex"/> - <magentoCLI command="cache:flush" stepKey="flushCache"/> - - <!-- Go to the admin grid and see the uploaded image --> - <amOnPage url="{{AdminProductIndexPage.url}}" stepKey="goToProductIndex3"/> - <waitForPageLoad stepKey="wait3"/> - <actionGroup ref="ResetProductGridToDefaultViewActionGroup" stepKey="resetProductGrid3"/> - <actionGroup ref="FilterProductGridBySkuActionGroup" stepKey="filterProductGridBySku3"> - <argument name="product" value="$$secondProduct$$"/> - </actionGroup> - <seeElement selector="img.admin__control-thumbnail[src*='/large']" stepKey="seeImgInGrid"/> - - <!-- Go to the category page and see the uploaded image --> - <amOnPage url="$$category.name$$.html" stepKey="goToCategoryPage2"/> - <seeElement selector=".products-grid img[src*='/large']" stepKey="seeUploadedImg"/> - - <!-- Go to the product page and see the uploaded image --> - <amOnPage url="$$secondProduct.name$$.html" stepKey="goToStorefront2"/> - <waitForPageLoad stepKey="waitForStorefront2"/> - <seeElementInDOM selector="{{StorefrontProductMediaSection.imageFile('large')}}" stepKey="seeLarge2"/> - </test> - - <test name="AdminSimpleProductRemoveImagesTest"> - <annotations> - <features value="Catalog"/> - <stories value="Add/remove images and videos for all product types and category"/> - <title value="Admin should be able to remove Product Images assigned as Base, Small and Thumbnail from Simple Product"/> - <description value="Admin should be able to remove Product Images assigned as Base, Small and Thumbnail from Simple Product"/> - <severity value="CRITICAL"/> - <testCaseId value="MC-191"/> - <group value="Catalog"/> - </annotations> - - <before> - <createData entity="_defaultCategory" stepKey="category"/> - <createData entity="_defaultProduct" stepKey="product"> - <requiredEntity createDataKey="category"/> - </createData> - <actionGroup ref="LoginAsAdmin" stepKey="loginAsAdmin"/> - </before> - - <after> - <deleteData createDataKey="category" stepKey="deletePreReqCategory"/> - <deleteData createDataKey="product" stepKey="deleteProduct"/> - <actionGroup ref="logout" stepKey="adminLogout"/> - </after> - - <!-- Go to the product edit page --> - <amOnPage url="{{AdminProductIndexPage.url}}" stepKey="goToProductIndex"/> - <waitForPageLoad stepKey="wait1"/> - <actionGroup ref="ResetProductGridToDefaultViewActionGroup" stepKey="resetProductGrid"/> - <actionGroup ref="FilterProductGridBySkuActionGroup" stepKey="filterProductGridBySku"> - <argument name="product" value="$$product$$"/> - </actionGroup> - <actionGroup ref="OpenProductForEditByClickingRowXColumnYInProductGridActionGroup" stepKey="openProduct"/> - - <!-- Set url key --> - <click selector="{{AdminProductSEOSection.sectionHeader}}" stepKey="openSeoSection"/> - <fillField selector="{{AdminProductSEOSection.urlKeyInput}}" userInput="$$product.name$$" stepKey="fillUrlKey"/> - - <!-- Expand images section --> - <click selector="{{AdminProductImagesSection.productImagesToggle}}" stepKey="expandImages"/> - - <!-- Upload and set Base image --> - <attachFile selector="{{AdminProductImagesSection.imageFileUpload}}" userInput="adobe-base.jpg" stepKey="attach1"/> - <waitForPageLoad stepKey="waitForUpload1"/> - <click selector="{{AdminProductImagesSection.nthProductImage('1')}}" stepKey="openImageDetails1"/> - <waitForPageLoad stepKey="waitForSlideout1"/> - <conditionalClick selector="{{AdminProductImagesSection.roleBase}}" dependentSelector="{{AdminProductImagesSection.isBaseSelected}}" visible="false" stepKey="base1"/> - <conditionalClick selector="{{AdminProductImagesSection.roleSmall}}" dependentSelector="{{AdminProductImagesSection.isSmallSelected}}" visible="true" stepKey="small1"/> - <conditionalClick selector="{{AdminProductImagesSection.roleThumbnail}}" dependentSelector="{{AdminProductImagesSection.isThumbnailSelected}}" visible="true" stepKey="thumbnail1"/> - <conditionalClick selector="{{AdminProductImagesSection.roleSwatch}}" dependentSelector="{{AdminProductImagesSection.isSwatchSelected}}" visible="true" stepKey="swatch1"/> - <pressKey selector="{{AdminProductImagesSection.altText}}" parameterArray="[\Facebook\WebDriver\WebDriverKeys::ESCAPE]" stepKey="pressEsc1"/> - <waitForPageLoad stepKey="waitForHide1"/> - - <!-- Upload and set Small image --> - <attachFile selector="{{AdminProductImagesSection.imageFileUpload}}" userInput="adobe-small.jpg" stepKey="attach2"/> - <waitForPageLoad stepKey="waitForUpload2"/> - <click selector="{{AdminProductImagesSection.nthProductImage('2')}}" stepKey="openImageDetails2"/> - <waitForPageLoad stepKey="waitForSlideout2"/> - <conditionalClick selector="{{AdminProductImagesSection.roleBase}}" dependentSelector="{{AdminProductImagesSection.isBaseSelected}}" visible="true" stepKey="base2"/> - <conditionalClick selector="{{AdminProductImagesSection.roleSmall}}" dependentSelector="{{AdminProductImagesSection.isSmallSelected}}" visible="false" stepKey="small2"/> - <conditionalClick selector="{{AdminProductImagesSection.roleThumbnail}}" dependentSelector="{{AdminProductImagesSection.isThumbnailSelected}}" visible="true" stepKey="thumbnail2"/> - <conditionalClick selector="{{AdminProductImagesSection.roleSwatch}}" dependentSelector="{{AdminProductImagesSection.isSwatchSelected}}" visible="true" stepKey="swatch2"/> - <pressKey selector="{{AdminProductImagesSection.altText}}" parameterArray="[\Facebook\WebDriver\WebDriverKeys::ESCAPE]" stepKey="pressEsc2"/> - <waitForPageLoad stepKey="waitForHide2"/> - - <!-- Upload and set Thumbnail image --> - <attachFile selector="{{AdminProductImagesSection.imageFileUpload}}" userInput="adobe-thumb.jpg" stepKey="attach3"/> - <waitForPageLoad stepKey="waitForUpload3"/> - <click selector="{{AdminProductImagesSection.nthProductImage('3')}}" stepKey="openImageDetails3"/> - <waitForPageLoad stepKey="waitForSlideout3"/> - <conditionalClick selector="{{AdminProductImagesSection.roleBase}}" dependentSelector="{{AdminProductImagesSection.isBaseSelected}}" visible="true" stepKey="base3"/> - <conditionalClick selector="{{AdminProductImagesSection.roleSmall}}" dependentSelector="{{AdminProductImagesSection.isSmallSelected}}" visible="true" stepKey="small3"/> - <conditionalClick selector="{{AdminProductImagesSection.roleThumbnail}}" dependentSelector="{{AdminProductImagesSection.isThumbnailSelected}}" visible="false" stepKey="thumbnail3"/> - <conditionalClick selector="{{AdminProductImagesSection.roleSwatch}}" dependentSelector="{{AdminProductImagesSection.isSwatchSelected}}" visible="true" stepKey="swatch3"/> - <pressKey selector="{{AdminProductImagesSection.altText}}" parameterArray="[\Facebook\WebDriver\WebDriverKeys::ESCAPE]" stepKey="pressEsc3"/> - <waitForPageLoad stepKey="waitForHide3"/> - - <!-- Save the product with all 3 images --> - <click selector="{{AdminProductFormActionSection.saveButton}}" stepKey="saveProduct"/> - - <!-- Go to the product page and see the Base image --> - <amOnPage url="$$product.name$$.html" stepKey="goToProductPage"/> - <waitForPageLoad stepKey="wait4"/> - <seeElement selector="{{StorefrontProductMediaSection.imageFile('/adobe-base')}}" stepKey="seeBase"/> - - <!-- Go to the category page and see the Small image --> - <amOnPage url="$$category.name$$.html" stepKey="goToCategoryPage"/> - <waitForPageLoad stepKey="wait3"/> - <seeElement selector="{{StorefrontCategoryProductSection.ProductImageBySrc('/adobe-small')}}" stepKey="seeThumb"/> - - <!-- Go to the admin grid and see the Thumbnail image --> - <amOnPage url="{{AdminProductIndexPage.url}}" stepKey="goToProductIndex2"/> - <waitForPageLoad stepKey="wait2"/> - <actionGroup ref="ResetProductGridToDefaultViewActionGroup" stepKey="resetProductGrid2"/> - <actionGroup ref="FilterProductGridBySkuActionGroup" stepKey="filterProductGridBySku2"> - <argument name="product" value="$$product$$"/> - </actionGroup> - <seeElement selector="{{AdminProductGridSection.productThumbnailBySrc('/adobe-thumb')}}" stepKey="seeBaseInGrid"/> - - <!-- Go to the product edit page again --> - <amOnPage url="{{AdminProductIndexPage.url}}" stepKey="goToProductIndex3"/> - <waitForPageLoad stepKey="wait5"/> - <actionGroup ref="ResetProductGridToDefaultViewActionGroup" stepKey="resetProductGrid3"/> - <actionGroup ref="FilterProductGridBySkuActionGroup" stepKey="filterProductGridBySku3"> - <argument name="product" value="$$product$$"/> - </actionGroup> - <actionGroup ref="OpenProductForEditByClickingRowXColumnYInProductGridActionGroup" stepKey="openProduct3"/> - <click selector="{{AdminProductImagesSection.productImagesToggle}}" stepKey="expandImages2"/> - - <!-- Remove all images --> - <click selector="{{AdminProductImagesSection.nthRemoveImageBtn('1')}}" stepKey="removeImage1"/> - <click selector="{{AdminProductImagesSection.nthRemoveImageBtn('2')}}" stepKey="removeImage2"/> - <click selector="{{AdminProductImagesSection.nthRemoveImageBtn('3')}}" stepKey="removeImage3"/> - <click selector="{{AdminProductFormActionSection.saveButton}}" stepKey="saveProduct2"/> - - <!-- Check admin grid for placeholder --> - <amOnPage url="{{AdminProductIndexPage.url}}" stepKey="goToProductIndex4"/> - <waitForPageLoad stepKey="wait6"/> - <actionGroup ref="ResetProductGridToDefaultViewActionGroup" stepKey="resetProductGrid4"/> - <actionGroup ref="FilterProductGridBySkuActionGroup" stepKey="filterProductGridBySku4"> - <argument name="product" value="$$product$$"/> - </actionGroup> - <dontSeeElement selector="{{AdminProductGridSection.productThumbnailBySrc('/adobe-thumb')}}" stepKey="dontSeeBaseInGrid"/> - <seeElement selector="{{AdminProductGridSection.productThumbnailBySrc('/placeholder/thumbnail')}}" stepKey="seePlaceholderThumb"/> - - <!-- Check category page for placeholder --> - <amOnPage url="$$category.name$$.html" stepKey="goToCategoryPage2"/> - <waitForPageLoad stepKey="wait7"/> - <dontSeeElement selector="{{StorefrontCategoryProductSection.ProductImageBySrc('/adobe-small')}}" stepKey="dontSeeThumb"/> - <seeElement selector="{{StorefrontCategoryProductSection.ProductImageBySrc('placeholder/small_image')}}" stepKey="seePlaceholderSmall"/> - - <!-- Check product page for placeholder --> - <amOnPage url="$$product.name$$.html" stepKey="goToProductPage2"/> - <waitForPageLoad stepKey="wait8"/> - <dontSeeElement selector="{{StorefrontProductMediaSection.imageFile('/adobe-base')}}" stepKey="dontSeeBase"/> - <seeElement selector="{{StorefrontProductMediaSection.imageFile('placeholder/image')}}" stepKey="seePlaceholderBase"/> - </test> -</tests> diff --git a/app/code/Magento/Catalog/Test/Mftf/Test/AdminSimpleProductImagesTest/AdminSimpleProductImagesTest.xml b/app/code/Magento/Catalog/Test/Mftf/Test/AdminSimpleProductImagesTest/AdminSimpleProductImagesTest.xml new file mode 100644 index 0000000000000..9819890ed3751 --- /dev/null +++ b/app/code/Magento/Catalog/Test/Mftf/Test/AdminSimpleProductImagesTest/AdminSimpleProductImagesTest.xml @@ -0,0 +1,167 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + /** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +--> +<tests xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/testSchema.xsd"> + <test name="AdminSimpleProductImagesTest"> + <annotations> + <features value="Catalog"/> + <stories value="Add/remove images and videos for all product types and category"/> + <title value="Admin should be able to add images of different types and sizes to Simple Product"/> + <description value="Admin should be able to add images of different types and sizes to Simple Product"/> + <severity value="BLOCKER"/> + <testCaseId value="MC-189"/> + <group value="Catalog"/> + </annotations> + + <before> + <createData entity="_defaultCategory" stepKey="category"/> + <createData entity="_defaultProduct" stepKey="firstProduct"> + <requiredEntity createDataKey="category"/> + </createData> + <createData entity="_defaultProduct" stepKey="secondProduct"> + <requiredEntity createDataKey="category"/> + </createData> + <actionGroup ref="AdminLoginActionGroup" stepKey="loginAsAdmin"/> + </before> + + <after> + <deleteData createDataKey="category" stepKey="deletePreReqCategory"/> + <deleteData createDataKey="firstProduct" stepKey="deleteFirstProduct"/> + <deleteData createDataKey="secondProduct" stepKey="deleteSecondProduct"/> + <actionGroup ref="AdminLogoutActionGroup" stepKey="adminLogout"/> + </after> + + <!-- Go to the first product edit page --> + <amOnPage url="{{AdminProductIndexPage.url}}" stepKey="goToProductIndex"/> + <waitForPageLoad stepKey="wait1"/> + <actionGroup ref="ResetProductGridToDefaultViewActionGroup" stepKey="resetProductGrid"/> + <actionGroup ref="FilterProductGridBySkuActionGroup" stepKey="filterProductGridBySku"> + <argument name="product" value="$$firstProduct$$"/> + </actionGroup> + <actionGroup ref="OpenProductForEditByClickingRowXColumnYInProductGridActionGroup" stepKey="openProducForEditByClickingRow1Column2InProductGrid"/> + + <!-- Set url key --> + <click selector="{{AdminProductSEOSection.sectionHeader}}" stepKey="openSeoSection"/> + <fillField selector="{{AdminProductSEOSection.urlKeyInput}}" userInput="$$firstProduct.name$$" stepKey="fillUrlKey"/> + + <click selector="{{AdminProductImagesSection.productImagesToggle}}" stepKey="expandImages"/> + + <!-- *.bmp is not allowed --> + <attachFile selector="{{AdminProductImagesSection.imageFileUpload}}" userInput="bmp.bmp" stepKey="attachBmp"/> + <waitForPageLoad stepKey="waitForUploadBmp"/> + <see selector="{{AdminProductMessagesSection.errorMessage}}" userInput="bmp.bmp was not uploaded. Disallowed file type." stepKey="seeErrorBmp"/> + <click selector="{{AdminProductImagesSection.modalOkBtn}}" stepKey="closeModalBmp"/> + + <!-- *.ico is not allowed --> + <attachFile selector="{{AdminProductImagesSection.imageFileUpload}}" userInput="ico.ico" stepKey="attachIco"/> + <waitForPageLoad stepKey="waitForUploadIco"/> + <see selector="{{AdminProductMessagesSection.errorMessage}}" userInput="ico.ico was not uploaded. Disallowed file type." stepKey="seeErrorIco"/> + <click selector="{{AdminProductImagesSection.modalOkBtn}}" stepKey="closeModalIco"/> + + <!-- *.svg is not allowed --> + <attachFile selector="{{AdminProductImagesSection.imageFileUpload}}" userInput="svg.svg" stepKey="attachSvg"/> + <waitForPageLoad stepKey="waitForUploadSvg"/> + <see selector="{{AdminProductMessagesSection.errorMessage}}" userInput="svg.svg was not uploaded. Disallowed file type." stepKey="seeErrorSvg"/> + <click selector="{{AdminProductImagesSection.modalOkBtn}}" stepKey="closeModalSvg"/> + + <!-- 0kb size is not allowed --> + <attachFile selector="{{AdminProductImagesSection.imageFileUpload}}" userInput="empty.jpg" stepKey="attachEmpty"/> + <waitForPageLoad stepKey="waitForUploadEmpty"/> + <see selector="{{AdminProductMessagesSection.errorMessage}}" userInput="empty.jpg was not uploaded." stepKey="seeErrorEmpty"/> + <click selector="{{AdminProductImagesSection.modalOkBtn}}" stepKey="closeModalEmpty"/> + + <!-- 1~ kb is allowed --> + <attachFile selector="{{AdminProductImagesSection.imageFileUpload}}" userInput="small.jpg" stepKey="attachSmall"/> + <waitForPageLoad stepKey="waitForUploadSmall"/> + <dontSeeElement selector="{{AdminProductMessagesSection.errorMessage}}" stepKey="dontSeeErrorSmall"/> + + <!-- 1~ mb is allowed --> + <attachFile selector="{{AdminProductImagesSection.imageFileUpload}}" userInput="medium.jpg" stepKey="attachMedium"/> + <waitForPageLoad stepKey="waitForUploadMedium"/> + <dontSeeElement selector="{{AdminProductMessagesSection.errorMessage}}" stepKey="dontSeeErrorMedium"/> + + <!-- 10~ mb is allowed --> + <attachFile selector="{{AdminProductImagesSection.imageFileUpload}}" userInput="large.jpg" stepKey="attachLarge"/> + <waitForPageLoad stepKey="waitForUploadLarge"/> + <dontSeeElement selector="{{AdminProductMessagesSection.errorMessage}}" stepKey="dontSeeErrorLarge"/> + + <!-- *.gif is allowed --> + <attachFile selector="{{AdminProductImagesSection.imageFileUpload}}" userInput="gif.gif" stepKey="attachGif"/> + <waitForPageLoad stepKey="waitForUploadGif"/> + <dontSeeElement selector="{{AdminProductMessagesSection.errorMessage}}" stepKey="dontSeeErrorGif"/> + + <!-- *.jpg is allowed --> + <attachFile selector="{{AdminProductImagesSection.imageFileUpload}}" userInput="jpg.jpg" stepKey="attachJpg"/> + <waitForPageLoad stepKey="waitForUploadJpg"/> + <dontSeeElement selector="{{AdminProductMessagesSection.errorMessage}}" stepKey="dontSeeErrorJpg"/> + + <!-- *.png is allowed --> + <attachFile selector="{{AdminProductImagesSection.imageFileUpload}}" userInput="png.png" stepKey="attachPng"/> + <waitForPageLoad stepKey="waitForUploadPng"/> + <dontSeeElement selector="{{AdminProductMessagesSection.errorMessage}}" stepKey="dontSeeErrorPng"/> + + <!-- Save the first product and go to the storefront --> + <click selector="{{AdminProductFormActionSection.saveButton}}" stepKey="saveProduct"/> + <amOnPage url="$$firstProduct.name$$.html" stepKey="goToStorefront"/> + <waitForPageLoad stepKey="waitForStorefront"/> + + <!-- See all of the images that we uploaded --> + <seeElementInDOM selector="{{StorefrontProductMediaSection.imageFile('small')}}" stepKey="seeSmall"/> + <seeElementInDOM selector="{{StorefrontProductMediaSection.imageFile('medium')}}" stepKey="seeMedium"/> + <seeElementInDOM selector="{{StorefrontProductMediaSection.imageFile('large')}}" stepKey="seeLarge"/> + <seeElementInDOM selector="{{StorefrontProductMediaSection.imageFile('gif')}}" stepKey="seeGif"/> + <seeElementInDOM selector="{{StorefrontProductMediaSection.imageFile('jpg')}}" stepKey="seeJpg"/> + <seeElementInDOM selector="{{StorefrontProductMediaSection.imageFile('png')}}" stepKey="seePng"/> + + <!-- Go to the category page and see a placeholder image for the second product --> + <amOnPage url="$$category.name$$.html" stepKey="goToCategoryPage"/> + <seeElement selector=".products-grid img[src*='placeholder/small_image.jpg']" stepKey="seePlaceholder"/> + + <!-- Go to the second product edit page --> + <amOnPage url="{{AdminProductIndexPage.url}}" stepKey="goToProductIndex2"/> + <waitForPageLoad stepKey="wait2"/> + <actionGroup ref="ResetProductGridToDefaultViewActionGroup" stepKey="resetProductGrid2"/> + <actionGroup ref="FilterProductGridBySkuActionGroup" stepKey="filterProductGridBySku2"> + <argument name="product" value="$$secondProduct$$"/> + </actionGroup> + <actionGroup ref="OpenProductForEditByClickingRowXColumnYInProductGridActionGroup" stepKey="openProducForEditByClickingRow1Column2InProductGrid2"/> + + <!-- Upload an image --> + <click selector="{{AdminProductImagesSection.productImagesToggle}}" stepKey="expandImages2"/> + <attachFile selector="{{AdminProductImagesSection.imageFileUpload}}" userInput="large.jpg" stepKey="attachLarge2"/> + <waitForPageLoad stepKey="waitForUploadLarge2"/> + <dontSeeElement selector="{{AdminProductMessagesSection.errorMessage}}" stepKey="dontSeeErrorLarge2"/> + + <!-- Set url key --> + <click selector="{{AdminProductSEOSection.sectionHeader}}" stepKey="openSeoSection2"/> + <fillField selector="{{AdminProductSEOSection.urlKeyInput}}" userInput="$$secondProduct.name$$" stepKey="fillUrlKey2"/> + + <!-- Save the second product --> + <click selector="{{AdminProductFormActionSection.saveButton}}" stepKey="saveProduct2"/> + <magentoCLI command="indexer:reindex" stepKey="reindex"/> + <magentoCLI command="cache:flush" stepKey="flushCache"/> + + <!-- Go to the admin grid and see the uploaded image --> + <amOnPage url="{{AdminProductIndexPage.url}}" stepKey="goToProductIndex3"/> + <waitForPageLoad stepKey="wait3"/> + <actionGroup ref="ResetProductGridToDefaultViewActionGroup" stepKey="resetProductGrid3"/> + <actionGroup ref="FilterProductGridBySkuActionGroup" stepKey="filterProductGridBySku3"> + <argument name="product" value="$$secondProduct$$"/> + </actionGroup> + <seeElement selector="img.admin__control-thumbnail[src*='/large']" stepKey="seeImgInGrid"/> + + <!-- Go to the category page and see the uploaded image --> + <amOnPage url="$$category.name$$.html" stepKey="goToCategoryPage2"/> + <seeElement selector=".products-grid img[src*='/large']" stepKey="seeUploadedImg"/> + + <!-- Go to the product page and see the uploaded image --> + <amOnPage url="$$secondProduct.name$$.html" stepKey="goToStorefront2"/> + <waitForPageLoad stepKey="waitForStorefront2"/> + <seeElementInDOM selector="{{StorefrontProductMediaSection.imageFile('large')}}" stepKey="seeLarge2"/> + </test> +</tests> diff --git a/app/code/Magento/Catalog/Test/Mftf/Test/AdminSimpleProductImagesTest/AdminSimpleProductRemoveImagesTest.xml b/app/code/Magento/Catalog/Test/Mftf/Test/AdminSimpleProductImagesTest/AdminSimpleProductRemoveImagesTest.xml new file mode 100644 index 0000000000000..ec82bdcf5bc94 --- /dev/null +++ b/app/code/Magento/Catalog/Test/Mftf/Test/AdminSimpleProductImagesTest/AdminSimpleProductRemoveImagesTest.xml @@ -0,0 +1,147 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + /** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +--> +<tests xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/testSchema.xsd"> + <test name="AdminSimpleProductRemoveImagesTest"> + <annotations> + <features value="Catalog"/> + <stories value="Add/remove images and videos for all product types and category"/> + <title value="Admin should be able to remove Product Images assigned as Base, Small and Thumbnail from Simple Product"/> + <description value="Admin should be able to remove Product Images assigned as Base, Small and Thumbnail from Simple Product"/> + <severity value="BLOCKER"/> + <testCaseId value="MC-191"/> + <group value="Catalog"/> + </annotations> + + <before> + <createData entity="_defaultCategory" stepKey="category"/> + <createData entity="_defaultProduct" stepKey="product"> + <requiredEntity createDataKey="category"/> + </createData> + <actionGroup ref="AdminLoginActionGroup" stepKey="loginAsAdmin"/> + </before> + + <after> + <deleteData createDataKey="category" stepKey="deletePreReqCategory"/> + <deleteData createDataKey="product" stepKey="deleteProduct"/> + <actionGroup ref="AdminLogoutActionGroup" stepKey="adminLogout"/> + </after> + + <!-- Go to the product edit page --> + <amOnPage url="{{AdminProductIndexPage.url}}" stepKey="goToProductIndex"/> + <waitForPageLoad stepKey="wait1"/> + <actionGroup ref="ResetProductGridToDefaultViewActionGroup" stepKey="resetProductGrid"/> + <actionGroup ref="FilterProductGridBySkuActionGroup" stepKey="filterProductGridBySku"> + <argument name="product" value="$$product$$"/> + </actionGroup> + <actionGroup ref="OpenProductForEditByClickingRowXColumnYInProductGridActionGroup" stepKey="openProduct"/> + + <!-- Set url key --> + <click selector="{{AdminProductSEOSection.sectionHeader}}" stepKey="openSeoSection"/> + <fillField selector="{{AdminProductSEOSection.urlKeyInput}}" userInput="$$product.name$$" stepKey="fillUrlKey"/> + + <!-- Expand images section --> + <click selector="{{AdminProductImagesSection.productImagesToggle}}" stepKey="expandImages"/> + + <!-- Upload and set Base image --> + <attachFile selector="{{AdminProductImagesSection.imageFileUpload}}" userInput="adobe-base.jpg" stepKey="attach1"/> + <waitForPageLoad stepKey="waitForUpload1"/> + <click selector="{{AdminProductImagesSection.nthProductImage('1')}}" stepKey="openImageDetails1"/> + <waitForPageLoad stepKey="waitForSlideout1"/> + <conditionalClick selector="{{AdminProductImagesSection.roleBase}}" dependentSelector="{{AdminProductImagesSection.isBaseSelected}}" visible="false" stepKey="base1"/> + <conditionalClick selector="{{AdminProductImagesSection.roleSmall}}" dependentSelector="{{AdminProductImagesSection.isSmallSelected}}" visible="true" stepKey="small1"/> + <conditionalClick selector="{{AdminProductImagesSection.roleThumbnail}}" dependentSelector="{{AdminProductImagesSection.isThumbnailSelected}}" visible="true" stepKey="thumbnail1"/> + <conditionalClick selector="{{AdminProductImagesSection.roleSwatch}}" dependentSelector="{{AdminProductImagesSection.isSwatchSelected}}" visible="true" stepKey="swatch1"/> + <pressKey selector="{{AdminProductImagesSection.altText}}" parameterArray="[\Facebook\WebDriver\WebDriverKeys::ESCAPE]" stepKey="pressEsc1"/> + <waitForPageLoad stepKey="waitForHide1"/> + + <!-- Upload and set Small image --> + <attachFile selector="{{AdminProductImagesSection.imageFileUpload}}" userInput="adobe-small.jpg" stepKey="attach2"/> + <waitForPageLoad stepKey="waitForUpload2"/> + <click selector="{{AdminProductImagesSection.nthProductImage('2')}}" stepKey="openImageDetails2"/> + <waitForPageLoad stepKey="waitForSlideout2"/> + <conditionalClick selector="{{AdminProductImagesSection.roleBase}}" dependentSelector="{{AdminProductImagesSection.isBaseSelected}}" visible="true" stepKey="base2"/> + <conditionalClick selector="{{AdminProductImagesSection.roleSmall}}" dependentSelector="{{AdminProductImagesSection.isSmallSelected}}" visible="false" stepKey="small2"/> + <conditionalClick selector="{{AdminProductImagesSection.roleThumbnail}}" dependentSelector="{{AdminProductImagesSection.isThumbnailSelected}}" visible="true" stepKey="thumbnail2"/> + <conditionalClick selector="{{AdminProductImagesSection.roleSwatch}}" dependentSelector="{{AdminProductImagesSection.isSwatchSelected}}" visible="true" stepKey="swatch2"/> + <pressKey selector="{{AdminProductImagesSection.altText}}" parameterArray="[\Facebook\WebDriver\WebDriverKeys::ESCAPE]" stepKey="pressEsc2"/> + <waitForPageLoad stepKey="waitForHide2"/> + + <!-- Upload and set Thumbnail image --> + <attachFile selector="{{AdminProductImagesSection.imageFileUpload}}" userInput="adobe-thumb.jpg" stepKey="attach3"/> + <waitForPageLoad stepKey="waitForUpload3"/> + <click selector="{{AdminProductImagesSection.nthProductImage('3')}}" stepKey="openImageDetails3"/> + <waitForPageLoad stepKey="waitForSlideout3"/> + <conditionalClick selector="{{AdminProductImagesSection.roleBase}}" dependentSelector="{{AdminProductImagesSection.isBaseSelected}}" visible="true" stepKey="base3"/> + <conditionalClick selector="{{AdminProductImagesSection.roleSmall}}" dependentSelector="{{AdminProductImagesSection.isSmallSelected}}" visible="true" stepKey="small3"/> + <conditionalClick selector="{{AdminProductImagesSection.roleThumbnail}}" dependentSelector="{{AdminProductImagesSection.isThumbnailSelected}}" visible="false" stepKey="thumbnail3"/> + <conditionalClick selector="{{AdminProductImagesSection.roleSwatch}}" dependentSelector="{{AdminProductImagesSection.isSwatchSelected}}" visible="true" stepKey="swatch3"/> + <pressKey selector="{{AdminProductImagesSection.altText}}" parameterArray="[\Facebook\WebDriver\WebDriverKeys::ESCAPE]" stepKey="pressEsc3"/> + <waitForPageLoad stepKey="waitForHide3"/> + + <!-- Save the product with all 3 images --> + <click selector="{{AdminProductFormActionSection.saveButton}}" stepKey="saveProduct"/> + + <!-- Go to the product page and see the Base image --> + <amOnPage url="$$product.name$$.html" stepKey="goToProductPage"/> + <waitForPageLoad stepKey="wait4"/> + <seeElement selector="{{StorefrontProductMediaSection.imageFile('/adobe-base')}}" stepKey="seeBase"/> + + <!-- Go to the category page and see the Small image --> + <amOnPage url="$$category.name$$.html" stepKey="goToCategoryPage"/> + <waitForPageLoad stepKey="wait3"/> + <seeElement selector="{{StorefrontCategoryProductSection.ProductImageBySrc('/adobe-small')}}" stepKey="seeThumb"/> + + <!-- Go to the admin grid and see the Thumbnail image --> + <amOnPage url="{{AdminProductIndexPage.url}}" stepKey="goToProductIndex2"/> + <waitForPageLoad stepKey="wait2"/> + <actionGroup ref="ResetProductGridToDefaultViewActionGroup" stepKey="resetProductGrid2"/> + <actionGroup ref="FilterProductGridBySkuActionGroup" stepKey="filterProductGridBySku2"> + <argument name="product" value="$$product$$"/> + </actionGroup> + <seeElement selector="{{AdminProductGridSection.productThumbnailBySrc('/adobe-thumb')}}" stepKey="seeBaseInGrid"/> + + <!-- Go to the product edit page again --> + <amOnPage url="{{AdminProductIndexPage.url}}" stepKey="goToProductIndex3"/> + <waitForPageLoad stepKey="wait5"/> + <actionGroup ref="ResetProductGridToDefaultViewActionGroup" stepKey="resetProductGrid3"/> + <actionGroup ref="FilterProductGridBySkuActionGroup" stepKey="filterProductGridBySku3"> + <argument name="product" value="$$product$$"/> + </actionGroup> + <actionGroup ref="OpenProductForEditByClickingRowXColumnYInProductGridActionGroup" stepKey="openProduct3"/> + <click selector="{{AdminProductImagesSection.productImagesToggle}}" stepKey="expandImages2"/> + + <!-- Remove all images --> + <click selector="{{AdminProductImagesSection.nthRemoveImageBtn('1')}}" stepKey="removeImage1"/> + <click selector="{{AdminProductImagesSection.nthRemoveImageBtn('2')}}" stepKey="removeImage2"/> + <click selector="{{AdminProductImagesSection.nthRemoveImageBtn('3')}}" stepKey="removeImage3"/> + <click selector="{{AdminProductFormActionSection.saveButton}}" stepKey="saveProduct2"/> + + <!-- Check admin grid for placeholder --> + <amOnPage url="{{AdminProductIndexPage.url}}" stepKey="goToProductIndex4"/> + <waitForPageLoad stepKey="wait6"/> + <actionGroup ref="ResetProductGridToDefaultViewActionGroup" stepKey="resetProductGrid4"/> + <actionGroup ref="FilterProductGridBySkuActionGroup" stepKey="filterProductGridBySku4"> + <argument name="product" value="$$product$$"/> + </actionGroup> + <dontSeeElement selector="{{AdminProductGridSection.productThumbnailBySrc('/adobe-thumb')}}" stepKey="dontSeeBaseInGrid"/> + <seeElement selector="{{AdminProductGridSection.productThumbnailBySrc('/placeholder/thumbnail')}}" stepKey="seePlaceholderThumb"/> + + <!-- Check category page for placeholder --> + <amOnPage url="$$category.name$$.html" stepKey="goToCategoryPage2"/> + <waitForPageLoad stepKey="wait7"/> + <dontSeeElement selector="{{StorefrontCategoryProductSection.ProductImageBySrc('/adobe-small')}}" stepKey="dontSeeThumb"/> + <seeElement selector="{{StorefrontCategoryProductSection.ProductImageBySrc('placeholder/small_image')}}" stepKey="seePlaceholderSmall"/> + + <!-- Check product page for placeholder --> + <amOnPage url="$$product.name$$.html" stepKey="goToProductPage2"/> + <waitForPageLoad stepKey="wait8"/> + <dontSeeElement selector="{{StorefrontProductMediaSection.imageFile('/adobe-base')}}" stepKey="dontSeeBase"/> + <seeElement selector="{{StorefrontProductMediaSection.imageFile('placeholder/image')}}" stepKey="seePlaceholderBase"/> + </test> +</tests> diff --git a/app/code/Magento/Catalog/Test/Mftf/Test/AdminSimpleProductSetEditContentTest.xml b/app/code/Magento/Catalog/Test/Mftf/Test/AdminSimpleProductSetEditContentTest.xml index 1e1fe4572b28d..51a91a17ff41a 100644 --- a/app/code/Magento/Catalog/Test/Mftf/Test/AdminSimpleProductSetEditContentTest.xml +++ b/app/code/Magento/Catalog/Test/Mftf/Test/AdminSimpleProductSetEditContentTest.xml @@ -21,7 +21,7 @@ </annotations> <before> <!--Admin Login--> - <actionGroup stepKey="loginToAdminPanel" ref="LoginAsAdmin"/> + <actionGroup stepKey="loginToAdminPanel" ref="AdminLoginActionGroup"/> <actionGroup ref="DisabledWYSIWYGActionGroup" stepKey="disableWYSIWYG"/> </before> <after> @@ -30,7 +30,7 @@ <argument name="product" value="SimpleProduct"/> </actionGroup> <!--Admin Logout--> - <actionGroup ref="logout" stepKey="logout"/> + <actionGroup ref="AdminLogoutActionGroup" stepKey="logout"/> </after> <!-- Create product --> diff --git a/app/code/Magento/Catalog/Test/Mftf/Test/AdminSimpleSetEditRelatedProductsTest.xml b/app/code/Magento/Catalog/Test/Mftf/Test/AdminSimpleSetEditRelatedProductsTest.xml index 0c0b8751a732e..534924e0f70c9 100644 --- a/app/code/Magento/Catalog/Test/Mftf/Test/AdminSimpleSetEditRelatedProductsTest.xml +++ b/app/code/Magento/Catalog/Test/Mftf/Test/AdminSimpleSetEditRelatedProductsTest.xml @@ -14,12 +14,12 @@ <stories value="Create/edit simple product"/> <title value="Admin should be able to set/edit Related Products information when editing a simple product"/> <description value="Admin should be able to set/edit Related Products information when editing a simple product"/> - <severity value="CRITICAL"/> + <severity value="BLOCKER"/> <testCaseId value="MC-3411"/> <group value="Catalog"/> </annotations> <before> - <actionGroup ref="LoginAsAdmin" stepKey="loginAsAdmin"/> + <actionGroup ref="AdminLoginActionGroup" stepKey="loginAsAdmin"/> <createData entity="SimpleProduct2" stepKey="simpleProduct0"/> <createData entity="SimpleProduct2" stepKey="simpleProduct1"/> <createData entity="SimpleProduct2" stepKey="simpleProduct2"/> @@ -27,6 +27,8 @@ <createData entity="SimpleProduct2" stepKey="simpleProduct4"/> <createData entity="SimpleProduct2" stepKey="simpleProduct5"/> <createData entity="SimpleProduct2" stepKey="simpleProduct6"/> + + <magentoCLI command="cron:run --group=index" stepKey="runCronIndexer"/> </before> <after> <!-- Delete simple product --> diff --git a/app/code/Magento/Catalog/Test/Mftf/Test/AdminSortingByWebsitesTest.xml b/app/code/Magento/Catalog/Test/Mftf/Test/AdminSortingByWebsitesTest.xml index b20a024a7c586..71e827a64ae2d 100644 --- a/app/code/Magento/Catalog/Test/Mftf/Test/AdminSortingByWebsitesTest.xml +++ b/app/code/Magento/Catalog/Test/Mftf/Test/AdminSortingByWebsitesTest.xml @@ -24,7 +24,7 @@ <requiredEntity createDataKey="createCategory"/> </createData> - <actionGroup ref="LoginAsAdmin" stepKey="loginAsAdmin"/> + <actionGroup ref="AdminLoginActionGroup" stepKey="loginAsAdmin"/> <!--Create new website --> <actionGroup ref="AdminCreateWebsiteActionGroup" stepKey="createAdditionalWebsite"> <argument name="newWebsiteName" value="{{customWebsite.name}}"/> @@ -45,7 +45,7 @@ <actionGroup ref="ResetWebUrlOptionsActionGroup" stepKey="resetUrlOption"/> <magentoCLI command="indexer:reindex" stepKey="reindex"/> <magentoCLI command="cache:flush" stepKey="flushCache"/> - <actionGroup ref="logout" stepKey="logout"/> + <actionGroup ref="AdminLogoutActionGroup" stepKey="logout"/> </after> <!--Assign Custom Website to Simple Product --> diff --git a/app/code/Magento/Catalog/Test/Mftf/Test/AdminStoresAttributeSetNavigateMenuTest.xml b/app/code/Magento/Catalog/Test/Mftf/Test/AdminStoresAttributeSetNavigateMenuTest.xml index ed29c281b804c..c1538cf8c5329 100644 --- a/app/code/Magento/Catalog/Test/Mftf/Test/AdminStoresAttributeSetNavigateMenuTest.xml +++ b/app/code/Magento/Catalog/Test/Mftf/Test/AdminStoresAttributeSetNavigateMenuTest.xml @@ -20,10 +20,10 @@ <group value="mtf_migrated"/> </annotations> <before> - <actionGroup ref="LoginAsAdmin" stepKey="LoginAsAdmin"/> + <actionGroup ref="AdminLoginActionGroup" stepKey="LoginAsAdmin"/> </before> <after> - <actionGroup ref="logout" stepKey="logout"/> + <actionGroup ref="AdminLogoutActionGroup" stepKey="logout"/> </after> <actionGroup ref="AdminNavigateMenuActionGroup" stepKey="navigateToAttributeSetPage"> <argument name="menuUiId" value="{{AdminMenuStores.dataUiId}}"/> diff --git a/app/code/Magento/Catalog/Test/Mftf/Test/AdminStoresProductNavigateMenuTest.xml b/app/code/Magento/Catalog/Test/Mftf/Test/AdminStoresProductNavigateMenuTest.xml index 28a33c4f20c01..e0a2c4272372a 100644 --- a/app/code/Magento/Catalog/Test/Mftf/Test/AdminStoresProductNavigateMenuTest.xml +++ b/app/code/Magento/Catalog/Test/Mftf/Test/AdminStoresProductNavigateMenuTest.xml @@ -20,10 +20,10 @@ <group value="mtf_migrated"/> </annotations> <before> - <actionGroup ref="LoginAsAdmin" stepKey="LoginAsAdmin"/> + <actionGroup ref="AdminLoginActionGroup" stepKey="LoginAsAdmin"/> </before> <after> - <actionGroup ref="logout" stepKey="logout"/> + <actionGroup ref="AdminLogoutActionGroup" stepKey="logout"/> </after> <actionGroup ref="AdminNavigateMenuActionGroup" stepKey="navigateToProductAttributePage"> <argument name="menuUiId" value="{{AdminMenuStores.dataUiId}}"/> diff --git a/app/code/Magento/Catalog/Test/Mftf/Test/AdminTierPriceNotAvailableForProductOptionsWithoutTierPriceTest.xml b/app/code/Magento/Catalog/Test/Mftf/Test/AdminTierPriceNotAvailableForProductOptionsWithoutTierPriceTest.xml index 4bcb82372e801..9838283bbfb96 100644 --- a/app/code/Magento/Catalog/Test/Mftf/Test/AdminTierPriceNotAvailableForProductOptionsWithoutTierPriceTest.xml +++ b/app/code/Magento/Catalog/Test/Mftf/Test/AdminTierPriceNotAvailableForProductOptionsWithoutTierPriceTest.xml @@ -20,7 +20,7 @@ <group value="catalog"/> </annotations> <before> - <actionGroup ref="LoginAsAdmin" stepKey="loginAsAdmin"/> + <actionGroup ref="AdminLoginActionGroup" stepKey="loginAsAdmin"/> <!--Create category--> <createData entity="SimpleSubCategory" stepKey="createCategory"/> @@ -82,7 +82,10 @@ <deleteData createDataKey="createConfigChildProduct2" stepKey="deleteConfigChildProduct2"/> <deleteData createDataKey="createConfigProductAttribute" stepKey="deleteConfigProductAttribute"/> - <actionGroup ref="logout" stepKey="logoutOfAdmin"/> + <actionGroup ref="AdminLogoutActionGroup" stepKey="logoutOfAdmin"/> + + <!-- Reindex invalidated indices after product attribute has been created/deleted --> + <magentoCron groups="index" stepKey="reindexInvalidatedIndices"/> </after> <!--Go to storefront product page an check price box css--> @@ -90,6 +93,9 @@ <waitForPageLoad stepKey="waitForStoreFrontLoad"/> <selectOption selector="{{StorefrontProductInfoMainSection.productAttributeOptionsSelectButton}}" userInput="$$getConfigAttributeOption1.value$$" stepKey="selectOption"/> <grabAttributeFrom selector="{{StorefrontProductInfoMainSection.productPrice}}" userInput="class" stepKey="grabGrabPriceClass"/> - <assertNotContains actual="$grabGrabPriceClass" expected=".price-box .price-tier_price" expectedType="string" stepKey="assertNotEquals"/> + <assertNotContains stepKey="assertNotEquals"> + <actualResult type="const">$grabGrabPriceClass</actualResult> + <expectedResult type="string">.price-box .price-tier_price</expectedResult> + </assertNotContains> </test> </tests> diff --git a/app/code/Magento/Catalog/Test/Mftf/Test/AdminUnassignProductAttributeFromAttributeSetTest.xml b/app/code/Magento/Catalog/Test/Mftf/Test/AdminUnassignProductAttributeFromAttributeSetTest.xml index 1c9cdad681d98..5fe71ce0a1e5d 100644 --- a/app/code/Magento/Catalog/Test/Mftf/Test/AdminUnassignProductAttributeFromAttributeSetTest.xml +++ b/app/code/Magento/Catalog/Test/Mftf/Test/AdminUnassignProductAttributeFromAttributeSetTest.xml @@ -14,7 +14,7 @@ <stories value="Add/Update attribute set"/> <title value="Admin should be able to unassign attributes from an attribute set"/> <description value="Admin should be able to unassign attributes from an attribute set"/> - <severity value="CRITICAL"/> + <severity value="BLOCKER"/> <testCaseId value="MC-194"/> <group value="Catalog"/> </annotations> @@ -30,12 +30,16 @@ <requiredEntity createDataKey="attribute"/> </createData> <createData entity="ApiProductWithDescription" stepKey="product"/> - <actionGroup ref="LoginAsAdmin" stepKey="loginAsAdmin"/> + <actionGroup ref="AdminLoginActionGroup" stepKey="loginAsAdmin"/> </before> <after> - <deleteData createDataKey="attribute" stepKey="deleteAttribute"/> <deleteData createDataKey="product" stepKey="deleteProduct"/> - <actionGroup ref="logout" stepKey="logout"/> + <deleteData createDataKey="attribute" stepKey="deleteAttribute"/> + <magentoCLI command="cron:run --group=index" stepKey="runCron"/> + <actionGroup ref="AdminLogoutActionGroup" stepKey="logout"/> + + <!-- Reindex invalidated indices after product attribute has been created/deleted --> + <magentoCron groups="index" stepKey="reindexInvalidatedIndices"/> </after> <!-- Assert attribute presence in storefront product additional information --> <amOnPage url="/$$product.custom_attributes[url_key]$$.html" stepKey="onProductPage1"/> diff --git a/app/code/Magento/Catalog/Test/Mftf/Test/AdminUpdateCategoryAndCheckDefaultUrlKeyOnStoreViewTest.xml b/app/code/Magento/Catalog/Test/Mftf/Test/AdminUpdateCategoryAndCheckDefaultUrlKeyOnStoreViewTest.xml index d8d462f850f8f..f3225bcd7fe3d 100644 --- a/app/code/Magento/Catalog/Test/Mftf/Test/AdminUpdateCategoryAndCheckDefaultUrlKeyOnStoreViewTest.xml +++ b/app/code/Magento/Catalog/Test/Mftf/Test/AdminUpdateCategoryAndCheckDefaultUrlKeyOnStoreViewTest.xml @@ -13,12 +13,12 @@ <title value="Update category, check default URL key on the custom store view"/> <description value="Login as admin and update category and check default URL Key on custom store view"/> <testCaseId value="MC-6063"/> - <severity value="CRITICAL"/> + <severity value="BLOCKER"/> <group value="Catalog"/> <group value="mtf_migrated"/> </annotations> <before> - <actionGroup ref="LoginAsAdmin" stepKey="loginToAdminPanel"/> + <actionGroup ref="AdminLoginActionGroup" stepKey="loginToAdminPanel"/> <createData entity="NewRootCategory" stepKey="rootCategory"/> <createData entity="SimpleRootSubCategory" stepKey="category"> <requiredEntity createDataKey="rootCategory"/> @@ -29,7 +29,7 @@ <argument name="storeGroupName" value="customStore.name"/> </actionGroup> <deleteData createDataKey="rootCategory" stepKey="deleteRootCategory"/> - <actionGroup ref="logout" stepKey="logout"/> + <actionGroup ref="AdminLogoutActionGroup" stepKey="logout"/> </after> <!--Open Store Page --> diff --git a/app/code/Magento/Catalog/Test/Mftf/Test/AdminUpdateCategoryAndMakeInactiveTest.xml b/app/code/Magento/Catalog/Test/Mftf/Test/AdminUpdateCategoryAndMakeInactiveTest.xml index 479249ca678dd..2865e9bc87dd3 100644 --- a/app/code/Magento/Catalog/Test/Mftf/Test/AdminUpdateCategoryAndMakeInactiveTest.xml +++ b/app/code/Magento/Catalog/Test/Mftf/Test/AdminUpdateCategoryAndMakeInactiveTest.xml @@ -13,17 +13,17 @@ <title value="Update category, make inactive"/> <description value="Login as admin and update category and make it Inactive"/> <testCaseId value="MC-6060"/> - <severity value="CRITICAL"/> + <severity value="BLOCKER"/> <group value="Catalog"/> <group value="mtf_migrated"/> </annotations> <before> - <actionGroup ref="LoginAsAdmin" stepKey="loginToAdminPanel"/> + <actionGroup ref="AdminLoginActionGroup" stepKey="loginToAdminPanel"/> <createData entity="_defaultCategory" stepKey="createDefaultCategory"/> </before> <after> <deleteData createDataKey="createDefaultCategory" stepKey="deleteCreatedCategory"/> - <actionGroup ref="logout" stepKey="logout"/> + <actionGroup ref="AdminLogoutActionGroup" stepKey="logout"/> </after> <!--Open category page--> diff --git a/app/code/Magento/Catalog/Test/Mftf/Test/AdminUpdateCategoryNameWithStoreViewTest.xml b/app/code/Magento/Catalog/Test/Mftf/Test/AdminUpdateCategoryNameWithStoreViewTest.xml index 2cb4a6b6dd436..3c3baf3524e87 100644 --- a/app/code/Magento/Catalog/Test/Mftf/Test/AdminUpdateCategoryNameWithStoreViewTest.xml +++ b/app/code/Magento/Catalog/Test/Mftf/Test/AdminUpdateCategoryNameWithStoreViewTest.xml @@ -18,7 +18,7 @@ <group value="mtf_migrated"/> </annotations> <before> - <actionGroup ref="LoginAsAdmin" stepKey="loginToAdminPanel"/> + <actionGroup ref="AdminLoginActionGroup" stepKey="loginToAdminPanel"/> <createData entity="NewRootCategory" stepKey="rootCategory"/> <createData entity="SimpleRootSubCategory" stepKey="category"> <requiredEntity createDataKey="rootCategory"/> @@ -29,7 +29,7 @@ <argument name="storeGroupName" value="customStore.name"/> </actionGroup> <deleteData createDataKey="rootCategory" stepKey="deleteRootCategory"/> - <actionGroup ref="logout" stepKey="logout"/> + <actionGroup ref="AdminLogoutActionGroup" stepKey="logout"/> </after> <!--Open store page --> diff --git a/app/code/Magento/Catalog/Test/Mftf/Test/AdminUpdateCategoryStoreUrlKeyTest.xml b/app/code/Magento/Catalog/Test/Mftf/Test/AdminUpdateCategoryStoreUrlKeyTest.xml index 01eba2976c3d6..8b57c2e371355 100644 --- a/app/code/Magento/Catalog/Test/Mftf/Test/AdminUpdateCategoryStoreUrlKeyTest.xml +++ b/app/code/Magento/Catalog/Test/Mftf/Test/AdminUpdateCategoryStoreUrlKeyTest.xml @@ -14,7 +14,7 @@ <stories value="Update SEO-friendly URL via the Admin"/> <title value="SEO-friendly URL should update regardless of scope or redirect change."/> <description value="SEO-friendly URL should update regardless of scope or redirect change."/> - <severity value="CRITICAL"/> + <severity value="BLOCKER"/> <testCaseId value="MAGETWO-92338"/> <group value="category"/> </annotations> @@ -22,11 +22,11 @@ <actionGroup ref="DeleteCategoryActionGroup" stepKey="deleteCategory"> <argument name="categoryEntity" value="_defaultCategory"/> </actionGroup> - <actionGroup ref="logout" stepKey="adminLogout"/> + <actionGroup ref="AdminLogoutActionGroup" stepKey="adminLogout"/> </after> <!-- Create category, change store view to default --> - <actionGroup ref="LoginAsAdmin" stepKey="loginAsAdmin"/> + <actionGroup ref="AdminLoginActionGroup" stepKey="loginAsAdmin"/> <amOnPage url="{{AdminCategoryPage.url}}" stepKey="navigateToCategoryPage"/> <waitForPageLoad stepKey="waitForPageLoad1"/> <actionGroup ref="CreateCategoryActionGroup" stepKey="createCategory"> diff --git a/app/code/Magento/Catalog/Test/Mftf/Test/AdminUpdateCategoryUrlKeyWithStoreViewTest.xml b/app/code/Magento/Catalog/Test/Mftf/Test/AdminUpdateCategoryUrlKeyWithStoreViewTest.xml index e7c4a8a093e19..117019da38ffa 100644 --- a/app/code/Magento/Catalog/Test/Mftf/Test/AdminUpdateCategoryUrlKeyWithStoreViewTest.xml +++ b/app/code/Magento/Catalog/Test/Mftf/Test/AdminUpdateCategoryUrlKeyWithStoreViewTest.xml @@ -13,12 +13,12 @@ <title value="Update category, URL key with custom store view"/> <description value="Login as admin and update category URL Key with store view"/> <testCaseId value="MC-6062"/> - <severity value="CRITICAL"/> + <severity value="BLOCKER"/> <group value="Catalog"/> <group value="mtf_migrated"/> </annotations> <before> - <actionGroup ref="LoginAsAdmin" stepKey="loginToAdminPanel"/> + <actionGroup ref="AdminLoginActionGroup" stepKey="loginToAdminPanel"/> <createData entity="NewRootCategory" stepKey="rootCategory"/> <createData entity="SimpleRootSubCategory" stepKey="category"> <requiredEntity createDataKey="rootCategory"/> @@ -29,7 +29,7 @@ <argument name="storeGroupName" value="customStore.name"/> </actionGroup> <deleteData createDataKey="rootCategory" stepKey="deleteRootCategory"/> - <actionGroup ref="logout" stepKey="logout"/> + <actionGroup ref="AdminLogoutActionGroup" stepKey="logout"/> </after> <!--Open Store Page --> diff --git a/app/code/Magento/Catalog/Test/Mftf/Test/AdminUpdateCategoryWithInactiveIncludeInMenuTest.xml b/app/code/Magento/Catalog/Test/Mftf/Test/AdminUpdateCategoryWithInactiveIncludeInMenuTest.xml index 3fea9c0eed7ca..9663632e6dc36 100644 --- a/app/code/Magento/Catalog/Test/Mftf/Test/AdminUpdateCategoryWithInactiveIncludeInMenuTest.xml +++ b/app/code/Magento/Catalog/Test/Mftf/Test/AdminUpdateCategoryWithInactiveIncludeInMenuTest.xml @@ -19,12 +19,12 @@ <group value="WYSIWYGDisabled"/> </annotations> <before> - <actionGroup ref="LoginAsAdmin" stepKey="loginToAdminPanel"/> + <actionGroup ref="AdminLoginActionGroup" stepKey="loginToAdminPanel"/> <createData entity="_defaultCategory" stepKey="createDefaultCategory"/> </before> <after> <deleteData createDataKey="createDefaultCategory" stepKey="deleteCategory"/> - <actionGroup ref="logout" stepKey="logout"/> + <actionGroup ref="AdminLogoutActionGroup" stepKey="logout"/> </after> <!--Open Category Page--> @@ -39,6 +39,7 @@ <click selector="{{AdminCategoryBasicFieldSection.includeInMenuLabel}}" stepKey="disableIncludeInMenu"/> <scrollTo selector="{{AdminCategoryContentSection.sectionHeader}}" x="0" y="-80" stepKey="scrollToContent"/> <click selector="{{AdminCategoryContentSection.sectionHeader}}" stepKey="selectContent"/> + <scrollTo selector="{{AdminCategoryContentSection.description}}" x="0" y="-80" stepKey="scrollToDescription"/> <fillField selector="{{AdminCategoryContentSection.description}}" userInput="Updated category Description Fields" stepKey="fillUpdatedDescription"/> <scrollTo selector="{{AdminCategorySEOSection.SectionHeader}}" x="0" y="-80" stepKey="scrollToSearchEngineOptimization"/> <click selector="{{AdminCategorySEOSection.SectionHeader}}" stepKey="selectSearchEngineOptimization"/> @@ -53,10 +54,11 @@ <waitForPageLoad stepKey="waitForUrlRewritePage"/> <!--Verify Updated Category UrlKey--> - <fillField selector="{{AdminUrlRewriteIndexSection.requestPathFilter}}" userInput="{{SimpleRootSubCategory.url_key}}" stepKey="fillUpdatedCategoryUrlKey"/> - <click selector="{{AdminUrlRewriteIndexSection.searchButton}}" stepKey="clickOnSearchButton"/> + <click selector="{{AdminDataGridHeaderSection.filters}}" stepKey="openUrlRewriteGridFilters"/> + <fillField selector="{{AdminDataGridHeaderSection.filterFieldInput('request_path')}}" userInput="{{SimpleRootSubCategory.url_key}}" stepKey="fillUpdatedCategoryUrlKey"/> + <click selector="{{AdminDataGridHeaderSection.applyFilters}}" stepKey="clickOrderApplyFilters"/> <waitForPageLoad stepKey="waitForPageToLoad"/> - <see stepKey="seeCategoryUrlKey" selector="{{AdminUrlRewriteIndexSection.requestPathColumn('1')}}" userInput="{{SimpleRootSubCategory.url_key}}.html" /> + <see stepKey="seeCategoryUrlKey" selector="{{AdminUrlRewriteIndexSection.gridCellByColumnRowNumber('1', 'Request Path')}}" userInput="{{SimpleRootSubCategory.url_key}}.html" /> <!--Verify Updated Category UrlKey directs to category Store Front--> <amOnPage url="{{SimpleRootSubCategory.url_key}}.html" stepKey="seeTheCategoryInStoreFrontPage"/> <waitForPageLoad time="60" stepKey="waitForStoreFrontPageLoad"/> diff --git a/app/code/Magento/Catalog/Test/Mftf/Test/AdminUpdateCategoryWithProductsTest.xml b/app/code/Magento/Catalog/Test/Mftf/Test/AdminUpdateCategoryWithProductsTest.xml index ad110ceee32d2..ccab2f9e96ce7 100644 --- a/app/code/Magento/Catalog/Test/Mftf/Test/AdminUpdateCategoryWithProductsTest.xml +++ b/app/code/Magento/Catalog/Test/Mftf/Test/AdminUpdateCategoryWithProductsTest.xml @@ -13,19 +13,19 @@ <title value="Update category, sort products by default sorting"/> <description value="Login as admin, update category and sort products"/> <testCaseId value="MC-6059"/> - <severity value="CRITICAL"/> + <severity value="BLOCKER"/> <group value="Catalog"/> <group value="mtf_migrated"/> </annotations> <before> - <actionGroup ref="LoginAsAdmin" stepKey="loginToAdminPanel"/> + <actionGroup ref="AdminLoginActionGroup" stepKey="loginToAdminPanel"/> <createData entity="defaultSimpleProduct" stepKey="simpleProduct" /> <createData entity="_defaultCategory" stepKey="createCategory"/> </before> <after> <deleteData createDataKey="createCategory" stepKey="deleteCategory"/> <deleteData createDataKey="simpleProduct" stepKey="deleteSimpleProduct"/> - <actionGroup ref="logout" stepKey="logout"/> + <actionGroup ref="AdminLogoutActionGroup" stepKey="logout"/> </after> <!--Open Category Page--> diff --git a/app/code/Magento/Catalog/Test/Mftf/Test/AdminUpdateFlatCategoryAndAddProductsTest.xml b/app/code/Magento/Catalog/Test/Mftf/Test/AdminUpdateFlatCategoryAndAddProductsTest.xml index cebf67ae2ebcf..512df79cb7c9c 100644 --- a/app/code/Magento/Catalog/Test/Mftf/Test/AdminUpdateFlatCategoryAndAddProductsTest.xml +++ b/app/code/Magento/Catalog/Test/Mftf/Test/AdminUpdateFlatCategoryAndAddProductsTest.xml @@ -18,7 +18,7 @@ <group value="mtf_migrated"/> </annotations> <before> - <actionGroup ref="LoginAsAdmin" stepKey="loginToAdminPanel"/> + <actionGroup ref="AdminLoginActionGroup" stepKey="loginToAdminPanel"/> <!-- Create Simple Product --> <createData entity="SimpleSubCategory" stepKey="category"/> <!-- Create category --> @@ -54,7 +54,7 @@ </actionGroup> <deleteData createDataKey="category" stepKey="deleteCategory"/> <deleteData createDataKey="createSimpleProduct" stepKey="deleteSimpleProduct"/> - <actionGroup ref="logout" stepKey="logout"/> + <actionGroup ref="AdminLogoutActionGroup" stepKey="logout"/> </after> <!-- Select Created Category--> <magentoCLI command="indexer:reindex" stepKey="reindexBeforeFlow"/> diff --git a/app/code/Magento/Catalog/Test/Mftf/Test/AdminUpdateFlatCategoryIncludeInNavigationTest.xml b/app/code/Magento/Catalog/Test/Mftf/Test/AdminUpdateFlatCategoryIncludeInNavigationTest.xml index d7ce22bdc0097..f444e741fea0e 100644 --- a/app/code/Magento/Catalog/Test/Mftf/Test/AdminUpdateFlatCategoryIncludeInNavigationTest.xml +++ b/app/code/Magento/Catalog/Test/Mftf/Test/AdminUpdateFlatCategoryIncludeInNavigationTest.xml @@ -18,7 +18,7 @@ <group value="mtf_migrated"/> </annotations> <before> - <actionGroup ref="LoginAsAdmin" stepKey="loginToAdminPanel"/> + <actionGroup ref="AdminLoginActionGroup" stepKey="loginToAdminPanel"/> <!--Create category--> <createData entity="CatNotIncludeInMenu" stepKey="createCategory"/> <!-- Create First StoreView --> @@ -51,7 +51,7 @@ <actionGroup ref="AdminDeleteStoreViewActionGroup" stepKey="deleteStoreViewFr"> <argument name="customStore" value="customStoreFR"/> </actionGroup> - <actionGroup ref="logout" stepKey="logout"/> + <actionGroup ref="AdminLogoutActionGroup" stepKey="logout"/> </after> <!--Verify Category is not listed in navigation menu--> <amOnPage url="/{{CatNotIncludeInMenu.name_lwr}}.html" stepKey="openCategoryPage"/> diff --git a/app/code/Magento/Catalog/Test/Mftf/Test/AdminUpdateFlatCategoryNameAndDescriptionTest.xml b/app/code/Magento/Catalog/Test/Mftf/Test/AdminUpdateFlatCategoryNameAndDescriptionTest.xml index 2b14973d6ce32..2b9cbf379c5e6 100644 --- a/app/code/Magento/Catalog/Test/Mftf/Test/AdminUpdateFlatCategoryNameAndDescriptionTest.xml +++ b/app/code/Magento/Catalog/Test/Mftf/Test/AdminUpdateFlatCategoryNameAndDescriptionTest.xml @@ -13,13 +13,13 @@ <title value="Flat Catalog - Update Category Name and Description"/> <description value="Login as admin and update flat category name and description"/> <testCaseId value="MC-11010"/> - <severity value="CRITICAL"/> + <severity value="BLOCKER"/> <group value="Catalog"/> <group value="mtf_migrated"/> <group value="WYSIWYGDisabled"/> </annotations> <before> - <actionGroup ref="LoginAsAdmin" stepKey="loginToAdminPanel"/> + <actionGroup ref="AdminLoginActionGroup" stepKey="loginToAdminPanel"/> <!--Create category--> <createData entity="_defaultCategory" stepKey="createCategory"/> <!-- Create First StoreView --> @@ -52,7 +52,7 @@ <actionGroup ref="AdminDeleteStoreViewActionGroup" stepKey="deleteStoreViewFr"> <argument name="customStore" value="customStoreFR"/> </actionGroup> - <actionGroup ref="logout" stepKey="logout"/> + <actionGroup ref="AdminLogoutActionGroup" stepKey="logout"/> </after> <!-- Select Created Category--> <amOnPage url="{{AdminCategoryPage.url}}" stepKey="openAdminCategoryIndexPage"/> diff --git a/app/code/Magento/Catalog/Test/Mftf/Test/AdminUpdateSimpleProductNameToVerifyDataOverridingOnStoreViewLevelTest.xml b/app/code/Magento/Catalog/Test/Mftf/Test/AdminUpdateSimpleProductNameToVerifyDataOverridingOnStoreViewLevelTest.xml index 60e7f03824ab7..6edffb923d540 100644 --- a/app/code/Magento/Catalog/Test/Mftf/Test/AdminUpdateSimpleProductNameToVerifyDataOverridingOnStoreViewLevelTest.xml +++ b/app/code/Magento/Catalog/Test/Mftf/Test/AdminUpdateSimpleProductNameToVerifyDataOverridingOnStoreViewLevelTest.xml @@ -19,7 +19,7 @@ <group value="mtf_migrated"/> </annotations> <before> - <actionGroup ref="LoginAsAdmin" stepKey="loginAsAdmin"/> + <actionGroup ref="AdminLoginActionGroup" stepKey="loginAsAdmin"/> <actionGroup ref="CreateStoreViewActionGroup" stepKey="createCustomStoreViewFr"> <argument name="storeView" value="customStoreFR"/> </actionGroup> @@ -38,7 +38,7 @@ <actionGroup ref="AdminDeleteStoreViewActionGroup" stepKey="deleteStoreView"> <argument name="customStore" value="customStoreFR"/> </actionGroup> - <actionGroup ref="logout" stepKey="logout"/> + <actionGroup ref="AdminLogoutActionGroup" stepKey="logout"/> </after> <!-- Search default simple product in grid --> diff --git a/app/code/Magento/Catalog/Test/Mftf/Test/AdminUpdateSimpleProductPriceToVerifyDataOverridingOnStoreViewLevelTest.xml b/app/code/Magento/Catalog/Test/Mftf/Test/AdminUpdateSimpleProductPriceToVerifyDataOverridingOnStoreViewLevelTest.xml index 7624ad0557b47..e954de90ef542 100644 --- a/app/code/Magento/Catalog/Test/Mftf/Test/AdminUpdateSimpleProductPriceToVerifyDataOverridingOnStoreViewLevelTest.xml +++ b/app/code/Magento/Catalog/Test/Mftf/Test/AdminUpdateSimpleProductPriceToVerifyDataOverridingOnStoreViewLevelTest.xml @@ -19,7 +19,7 @@ <group value="mtf_migrated"/> </annotations> <before> - <actionGroup ref="LoginAsAdmin" stepKey="loginAsAdmin"/> + <actionGroup ref="AdminLoginActionGroup" stepKey="loginAsAdmin"/> <actionGroup ref="CreateStoreViewActionGroup" stepKey="createCustomStoreViewFr"> <argument name="storeView" value="customStoreFR"/> </actionGroup> @@ -38,7 +38,7 @@ <actionGroup ref="AdminDeleteStoreViewActionGroup" stepKey="deleteStoreView"> <argument name="customStore" value="customStoreFR"/> </actionGroup> - <actionGroup ref="logout" stepKey="logout"/> + <actionGroup ref="AdminLogoutActionGroup" stepKey="logout"/> </after> <!-- Search default simple product in grid --> diff --git a/app/code/Magento/Catalog/Test/Mftf/Test/AdminUpdateSimpleProductTieredPriceTest.xml b/app/code/Magento/Catalog/Test/Mftf/Test/AdminUpdateSimpleProductTieredPriceTest.xml index a848eb3d11e61..f5b0fb8054dc1 100644 --- a/app/code/Magento/Catalog/Test/Mftf/Test/AdminUpdateSimpleProductTieredPriceTest.xml +++ b/app/code/Magento/Catalog/Test/Mftf/Test/AdminUpdateSimpleProductTieredPriceTest.xml @@ -19,7 +19,7 @@ <group value="mtf_migrated"/> </annotations> <before> - <actionGroup ref="LoginAsAdmin" stepKey="loginAsAdmin"/> + <actionGroup ref="AdminLoginActionGroup" stepKey="loginAsAdmin"/> <createData entity="SimpleSubCategory" stepKey="initialCategoryEntity"/> <createData entity="defaultSimpleProduct" stepKey="initialSimpleProduct"> <requiredEntity createDataKey="initialCategoryEntity"/> @@ -36,7 +36,7 @@ <actionGroup ref="DeleteProductBySkuActionGroup" stepKey="deleteCreatedProduct"> <argument name="sku" value="{{simpleProductTierPrice300InStock.sku}}"/> </actionGroup> - <actionGroup ref="logout" stepKey="logout"/> + <actionGroup ref="AdminLogoutActionGroup" stepKey="logout"/> </after> <!-- Search default simple product in the grid --> diff --git a/app/code/Magento/Catalog/Test/Mftf/Test/AdminUpdateSimpleProductWithRegularPriceInStockDisabledProductTest.xml b/app/code/Magento/Catalog/Test/Mftf/Test/AdminUpdateSimpleProductWithRegularPriceInStockDisabledProductTest.xml index ff3f56b566b38..d20594461173b 100644 --- a/app/code/Magento/Catalog/Test/Mftf/Test/AdminUpdateSimpleProductWithRegularPriceInStockDisabledProductTest.xml +++ b/app/code/Magento/Catalog/Test/Mftf/Test/AdminUpdateSimpleProductWithRegularPriceInStockDisabledProductTest.xml @@ -14,12 +14,12 @@ <title value="Update Simple Product with Regular Price (In Stock), Disabled Product"/> <description value="Test log in to Update Simple Product and Update Simple Product with Regular Price (In Stock), Disabled Product"/> <testCaseId value="MC-10816"/> - <severity value="CRITICAL"/> + <severity value="BLOCKER"/> <group value="catalog"/> <group value="mtf_migrated"/> </annotations> <before> - <actionGroup ref="LoginAsAdmin" stepKey="loginAsAdmin"/> + <actionGroup ref="AdminLoginActionGroup" stepKey="loginAsAdmin"/> <createData entity="SimpleSubCategory" stepKey="initialCategoryEntity"/> <createData entity="defaultSimpleProduct" stepKey="initialSimpleProduct"> <requiredEntity createDataKey="initialCategoryEntity"/> @@ -30,7 +30,7 @@ <actionGroup ref="DeleteProductBySkuActionGroup" stepKey="deleteCreatedProduct"> <argument name="sku" value="{{simpleProductDisabled.sku}}"/> </actionGroup> - <actionGroup ref="logout" stepKey="logout"/> + <actionGroup ref="AdminLogoutActionGroup" stepKey="logout"/> </after> <!-- Search default simple product in the grid page --> diff --git a/app/code/Magento/Catalog/Test/Mftf/Test/AdminUpdateSimpleProductWithRegularPriceInStockEnabledFlatTest.xml b/app/code/Magento/Catalog/Test/Mftf/Test/AdminUpdateSimpleProductWithRegularPriceInStockEnabledFlatTest.xml index 09ddc18aff4bd..5fa7acbeb8de9 100644 --- a/app/code/Magento/Catalog/Test/Mftf/Test/AdminUpdateSimpleProductWithRegularPriceInStockEnabledFlatTest.xml +++ b/app/code/Magento/Catalog/Test/Mftf/Test/AdminUpdateSimpleProductWithRegularPriceInStockEnabledFlatTest.xml @@ -19,7 +19,7 @@ <group value="mtf_migrated"/> </annotations> <before> - <actionGroup ref="LoginAsAdmin" stepKey="loginAsAdmin"/> + <actionGroup ref="AdminLoginActionGroup" stepKey="loginAsAdmin"/> <magentoCLI stepKey="setFlatCatalogProduct" command="config:set catalog/frontend/flat_catalog_product 1"/> <createData entity="SimpleSubCategory" stepKey="initialCategoryEntity"/> <createData entity="defaultSimpleProduct" stepKey="initialSimpleProduct"> @@ -33,7 +33,7 @@ <actionGroup ref="DeleteProductBySkuActionGroup" stepKey="deleteCreatedProduct"> <argument name="sku" value="{{simpleProductEnabledFlat.sku}}"/> </actionGroup> - <actionGroup ref="logout" stepKey="logout"/> + <actionGroup ref="AdminLogoutActionGroup" stepKey="logout"/> <magentoCLI stepKey="unsetFlatCatalogProduct" command="config:set catalog/frontend/flat_catalog_product 0"/> </after> diff --git a/app/code/Magento/Catalog/Test/Mftf/Test/AdminUpdateSimpleProductWithRegularPriceInStockNotVisibleIndividuallyTest.xml b/app/code/Magento/Catalog/Test/Mftf/Test/AdminUpdateSimpleProductWithRegularPriceInStockNotVisibleIndividuallyTest.xml index 94aec4cc95d1d..259eb01d9bc5d 100644 --- a/app/code/Magento/Catalog/Test/Mftf/Test/AdminUpdateSimpleProductWithRegularPriceInStockNotVisibleIndividuallyTest.xml +++ b/app/code/Magento/Catalog/Test/Mftf/Test/AdminUpdateSimpleProductWithRegularPriceInStockNotVisibleIndividuallyTest.xml @@ -19,7 +19,7 @@ <group value="mtf_migrated"/> </annotations> <before> - <actionGroup ref="LoginAsAdmin" stepKey="loginAsAdmin"/> + <actionGroup ref="AdminLoginActionGroup" stepKey="loginAsAdmin"/> <createData entity="SimpleSubCategory" stepKey="initialCategoryEntity"/> <createData entity="defaultSimpleProduct" stepKey="initialSimpleProduct"> <requiredEntity createDataKey="initialCategoryEntity"/> @@ -32,7 +32,7 @@ <actionGroup ref="DeleteProductBySkuActionGroup" stepKey="deleteCreatedProduct"> <argument name="sku" value="{{simpleProductNotVisibleIndividually.sku}}"/> </actionGroup> - <actionGroup ref="logout" stepKey="logout"/> + <actionGroup ref="AdminLogoutActionGroup" stepKey="logout"/> </after> <!-- Search default simple product in the grid page --> diff --git a/app/code/Magento/Catalog/Test/Mftf/Test/AdminUpdateSimpleProductWithRegularPriceInStockUnassignFromCategoryTest.xml b/app/code/Magento/Catalog/Test/Mftf/Test/AdminUpdateSimpleProductWithRegularPriceInStockUnassignFromCategoryTest.xml index fa3317aa815d9..45f8918100083 100644 --- a/app/code/Magento/Catalog/Test/Mftf/Test/AdminUpdateSimpleProductWithRegularPriceInStockUnassignFromCategoryTest.xml +++ b/app/code/Magento/Catalog/Test/Mftf/Test/AdminUpdateSimpleProductWithRegularPriceInStockUnassignFromCategoryTest.xml @@ -19,7 +19,7 @@ <group value="mtf_migrated"/> </annotations> <before> - <actionGroup ref="LoginAsAdmin" stepKey="loginAsAdmin"/> + <actionGroup ref="AdminLoginActionGroup" stepKey="loginAsAdmin"/> <createData entity="SimpleSubCategory" stepKey="initialCategoryEntity"/> <createData entity="_defaultProduct" stepKey="initialSimpleProduct"> <requiredEntity createDataKey="initialCategoryEntity"/> @@ -30,7 +30,7 @@ <actionGroup ref="DeleteProductBySkuActionGroup" stepKey="deleteCreatedProduct"> <argument name="sku" value="$$initialSimpleProduct.sku$$"/> </actionGroup> - <actionGroup ref="logout" stepKey="logout"/> + <actionGroup ref="AdminLogoutActionGroup" stepKey="logout"/> </after> <!--Search default simple product in the grid page --> diff --git a/app/code/Magento/Catalog/Test/Mftf/Test/AdminUpdateSimpleProductWithRegularPriceInStockVisibleInCatalogAndSearchTest.xml b/app/code/Magento/Catalog/Test/Mftf/Test/AdminUpdateSimpleProductWithRegularPriceInStockVisibleInCatalogAndSearchTest.xml index 95b74f4d38b3f..58db163bed720 100644 --- a/app/code/Magento/Catalog/Test/Mftf/Test/AdminUpdateSimpleProductWithRegularPriceInStockVisibleInCatalogAndSearchTest.xml +++ b/app/code/Magento/Catalog/Test/Mftf/Test/AdminUpdateSimpleProductWithRegularPriceInStockVisibleInCatalogAndSearchTest.xml @@ -14,12 +14,12 @@ <title value="Update Simple Product with Regular Price (In Stock) Visible in Catalog and Search"/> <description value="Test log in to Update Simple Product and Update Simple Product with Regular Price (In Stock) Visible in Catalog and Search"/> <testCaseId value="MC-10802"/> - <severity value="CRITICAL"/> + <severity value="BLOCKER"/> <group value="catalog"/> <group value="mtf_migrated"/> </annotations> <before> - <actionGroup ref="LoginAsAdmin" stepKey="loginAsAdmin"/> + <actionGroup ref="AdminLoginActionGroup" stepKey="loginAsAdmin"/> <createData entity="SimpleSubCategory" stepKey="initialCategoryEntity"/> <createData entity="defaultSimpleProduct" stepKey="initialSimpleProduct"> <requiredEntity createDataKey="initialCategoryEntity"/> @@ -32,7 +32,7 @@ <actionGroup ref="DeleteProductBySkuActionGroup" stepKey="deleteCreatedProduct"> <argument name="sku" value="{{simpleProductRegularPrice245InStock.sku}}"/> </actionGroup> - <actionGroup ref="logout" stepKey="logout"/> + <actionGroup ref="AdminLogoutActionGroup" stepKey="logout"/> </after> <!-- Search default simple product in the grid page --> diff --git a/app/code/Magento/Catalog/Test/Mftf/Test/AdminUpdateSimpleProductWithRegularPriceInStockVisibleInCatalogOnlyTest.xml b/app/code/Magento/Catalog/Test/Mftf/Test/AdminUpdateSimpleProductWithRegularPriceInStockVisibleInCatalogOnlyTest.xml index af190890ac351..5e9a48f659d6b 100644 --- a/app/code/Magento/Catalog/Test/Mftf/Test/AdminUpdateSimpleProductWithRegularPriceInStockVisibleInCatalogOnlyTest.xml +++ b/app/code/Magento/Catalog/Test/Mftf/Test/AdminUpdateSimpleProductWithRegularPriceInStockVisibleInCatalogOnlyTest.xml @@ -14,12 +14,12 @@ <title value="Update Simple Product with Regular Price (In Stock) Visible in Catalog Only"/> <description value="Test log in to Update Simple Product and Update Simple Product with Regular Price (In Stock) Visible in Catalog Only"/> <testCaseId value="MC-10804"/> - <severity value="CRITICAL"/> + <severity value="BLOCKER"/> <group value="catalog"/> <group value="mtf_migrated"/> </annotations> <before> - <actionGroup ref="LoginAsAdmin" stepKey="loginAsAdmin"/> + <actionGroup ref="AdminLoginActionGroup" stepKey="loginAsAdmin"/> <createData entity="SimpleSubCategory" stepKey="initialCategoryEntity"/> <createData entity="defaultSimpleProduct" stepKey="initialSimpleProduct"> <requiredEntity createDataKey="initialCategoryEntity"/> @@ -32,7 +32,7 @@ <actionGroup ref="DeleteProductBySkuActionGroup" stepKey="deleteCreatedProduct"> <argument name="sku" value="{{simpleProductRegularPrice32501InStock.sku}}"/> </actionGroup> - <actionGroup ref="logout" stepKey="logout"/> + <actionGroup ref="AdminLogoutActionGroup" stepKey="logout"/> </after> <!-- Search default simple product in the grid --> diff --git a/app/code/Magento/Catalog/Test/Mftf/Test/AdminUpdateSimpleProductWithRegularPriceInStockVisibleInSearchOnlyTest.xml b/app/code/Magento/Catalog/Test/Mftf/Test/AdminUpdateSimpleProductWithRegularPriceInStockVisibleInSearchOnlyTest.xml index 626c3d00a5caf..3d37b54dfa439 100644 --- a/app/code/Magento/Catalog/Test/Mftf/Test/AdminUpdateSimpleProductWithRegularPriceInStockVisibleInSearchOnlyTest.xml +++ b/app/code/Magento/Catalog/Test/Mftf/Test/AdminUpdateSimpleProductWithRegularPriceInStockVisibleInSearchOnlyTest.xml @@ -19,7 +19,7 @@ <group value="mtf_migrated"/> </annotations> <before> - <actionGroup ref="LoginAsAdmin" stepKey="loginAsAdmin"/> + <actionGroup ref="AdminLoginActionGroup" stepKey="loginAsAdmin"/> <createData entity="SimpleSubCategory" stepKey="initialCategoryEntity"/> <createData entity="defaultSimpleProduct" stepKey="initialSimpleProduct"> <requiredEntity createDataKey="initialCategoryEntity"/> @@ -32,7 +32,7 @@ <actionGroup ref="DeleteProductBySkuActionGroup" stepKey="deleteCreatedProduct"> <argument name="sku" value="{{simpleProductRegularPrice325InStock.sku}}"/> </actionGroup> - <actionGroup ref="logout" stepKey="logout"/> + <actionGroup ref="AdminLogoutActionGroup" stepKey="logout"/> </after> <!-- Search default simple product in the grid --> diff --git a/app/code/Magento/Catalog/Test/Mftf/Test/AdminUpdateSimpleProductWithRegularPriceInStockWithCustomOptionsTest.xml b/app/code/Magento/Catalog/Test/Mftf/Test/AdminUpdateSimpleProductWithRegularPriceInStockWithCustomOptionsTest.xml index 13782da076f69..855a2b1d9b0cc 100644 --- a/app/code/Magento/Catalog/Test/Mftf/Test/AdminUpdateSimpleProductWithRegularPriceInStockWithCustomOptionsTest.xml +++ b/app/code/Magento/Catalog/Test/Mftf/Test/AdminUpdateSimpleProductWithRegularPriceInStockWithCustomOptionsTest.xml @@ -14,12 +14,12 @@ <title value="Update Simple Product with Regular Price (In Stock) with Custom Options"/> <description value="Test log in to Update Simple Product and Update Simple Product with Regular Price (In Stock) with Custom Options"/> <testCaseId value="MC-10819"/> - <severity value="CRITICAL"/> + <severity value="BLOCKER"/> <group value="catalog"/> <group value="mtf_migrated"/> </annotations> <before> - <actionGroup ref="LoginAsAdmin" stepKey="loginAsAdmin"/> + <actionGroup ref="AdminLoginActionGroup" stepKey="loginAsAdmin"/> <createData entity="SimpleSubCategory" stepKey="initialCategoryEntity"/> <createData entity="defaultSimpleProduct" stepKey="initialSimpleProduct"> <requiredEntity createDataKey="initialCategoryEntity"/> @@ -32,7 +32,7 @@ <actionGroup ref="DeleteProductBySkuActionGroup" stepKey="deleteCreatedProduct"> <argument name="sku" value="{{simpleProductRegularPriceCustomOptions.sku}}"/> </actionGroup> - <actionGroup ref="logout" stepKey="logout"/> + <actionGroup ref="AdminLogoutActionGroup" stepKey="logout"/> </after> <!-- Search default simple product in the grid --> @@ -155,7 +155,7 @@ <waitForElementVisible selector="{{StorefrontMessagesSection.success}}" stepKey="waitForSuccessMessage"/> <seeElement selector="{{StorefrontProductPageSection.successMsg}}" stepKey="seeYouAddedSimpleprod4ToYourShoppingCartSuccessSaveMessage"/> <seeElement selector="{{StorefrontMinicartSection.quantity(1)}}" stepKey="seeAddedProductQuantityInCart"/> - <click selector="{{StorefrontMinicartSection.showCart}}" stepKey="clickOnMiniCart"/> + <actionGroup ref="StorefrontClickOnMiniCartActionGroup" stepKey="clickOnMiniCart"/> <see selector="{{StorefrontMinicartSection.miniCartItemsText}}" userInput="{{simpleProductRegularPriceCustomOptions.name}}" stepKey="seeProductNameInMiniCart"/> <see selector="{{StorefrontMinicartSection.miniCartItemsText}}" userInput="{{simpleProductRegularPriceCustomOptions.storefront_new_cartprice}}" stepKey="seeProductPriceInMiniCart"/> </test> diff --git a/app/code/Magento/Catalog/Test/Mftf/Test/AdminUpdateSimpleProductWithRegularPriceOutOfStockTest.xml b/app/code/Magento/Catalog/Test/Mftf/Test/AdminUpdateSimpleProductWithRegularPriceOutOfStockTest.xml index 285ceb3c4d722..af836efcf6be6 100644 --- a/app/code/Magento/Catalog/Test/Mftf/Test/AdminUpdateSimpleProductWithRegularPriceOutOfStockTest.xml +++ b/app/code/Magento/Catalog/Test/Mftf/Test/AdminUpdateSimpleProductWithRegularPriceOutOfStockTest.xml @@ -19,7 +19,7 @@ <group value="mtf_migrated"/> </annotations> <before> - <actionGroup ref="LoginAsAdmin" stepKey="loginAsAdmin"/> + <actionGroup ref="AdminLoginActionGroup" stepKey="loginAsAdmin"/> <createData entity="SimpleSubCategory" stepKey="initialCategoryEntity"/> <createData entity="defaultSimpleProduct" stepKey="initialSimpleProduct"> <requiredEntity createDataKey="initialCategoryEntity"/> @@ -32,7 +32,7 @@ <actionGroup ref="DeleteProductBySkuActionGroup" stepKey="deleteCreatedProduct"> <argument name="sku" value="{{simpleProductRegularPrice32503OutOfStock.sku}}"/> </actionGroup> - <actionGroup ref="logout" stepKey="logout"/> + <actionGroup ref="AdminLogoutActionGroup" stepKey="logout"/> </after> <!-- Search default simple product in the grid --> diff --git a/app/code/Magento/Catalog/Test/Mftf/Test/AdminUpdateTopCategoryUrlWithNoRedirectTest.xml b/app/code/Magento/Catalog/Test/Mftf/Test/AdminUpdateTopCategoryUrlWithNoRedirectTest.xml index 4dea6663e61bf..db8b808e74f76 100644 --- a/app/code/Magento/Catalog/Test/Mftf/Test/AdminUpdateTopCategoryUrlWithNoRedirectTest.xml +++ b/app/code/Magento/Catalog/Test/Mftf/Test/AdminUpdateTopCategoryUrlWithNoRedirectTest.xml @@ -13,7 +13,7 @@ <title value="Update top category url and do not create redirect"/> <description value="Login as admin and update top category url and do not create redirect"/> <testCaseId value="MC-6056"/> - <severity value="CRITICAL"/> + <severity value="BLOCKER"/> <group value="Catalog"/> <group value="mtf_migrated"/> </annotations> @@ -29,10 +29,10 @@ </createData> <!-- Login as admin --> - <actionGroup ref="LoginAsAdmin" stepKey="login"/> + <actionGroup ref="AdminLoginActionGroup" stepKey="login"/> </before> <after> - <actionGroup ref="logout" stepKey="logout"/> + <actionGroup ref="AdminLogoutActionGroup" stepKey="logout"/> <deleteData createDataKey="createThreeLevelNestedCategories" stepKey="deleteThreeNestedCategories"/> <deleteData createDataKey="createTwoLevelNestedCategories" stepKey="deleteTwoLevelNestedCategory"/> @@ -68,21 +68,27 @@ <waitForPageLoad stepKey="waitForUrlRewritePage"/> <!-- Verify third level category's Redirect Path, Target Path and Redirect Type after the URL Update --> - <click selector="{{AdminUrlRewriteIndexSection.resetButton}}" stepKey="clickOnResetButton"/> + <conditionalClick selector="{{AdminDataGridHeaderSection.clearFilters}}" + dependentSelector="{{AdminDataGridHeaderSection.clearFilters}}" + visible="true" + stepKey="clickOnResetButton"/> <waitForPageLoad stepKey="waitForPageToLoad0"/> - <fillField selector="{{AdminUrlRewriteIndexSection.requestPathFilter}}" userInput="updatedurl" stepKey="fillUpdatedUrlInRedirectPathFilter"/> - <click selector="{{AdminUrlRewriteIndexSection.searchButton}}" stepKey="clickOnSearchButton"/> + <click selector="{{AdminDataGridHeaderSection.filters}}" stepKey="openUrlRewriteGridFilters"/> + <fillField selector="{{AdminDataGridHeaderSection.filterFieldInput('request_path')}}" userInput="updatedurl" stepKey="fillUpdatedUrlInRedirectPathFilter"/> + <click selector="{{AdminDataGridHeaderSection.applyFilters}}" stepKey="clickOrderApplyFilters"/> <waitForPageLoad stepKey="waitForPageToLoad2"/> - <see stepKey="seeTheRedirectType" selector="{{AdminUrlRewriteIndexSection.redirectTypeColumn('1')}}" userInput="No" /> - <see stepKey="seeTheTargetPath" selector="{{AdminUrlRewriteIndexSection.targetPathColumn('1')}}" userInput="catalog/category/view/id/{$categoryId}"/> - <see selector="{{AdminUrlRewriteIndexSection.requestPathColumn('1')}}" userInput="$$createDefaultCategory.name$$/$$createTwoLevelNestedCategories.name$$/updatedurl.html" stepKey="seeTheRedirectPath"/> + <see stepKey="seeTheRedirectType" selector="{{AdminUrlRewriteIndexSection.gridCellByColumnRowNumber('1', 'Redirect Type')}}" userInput="No" /> + <see stepKey="seeTheTargetPath" selector="{{AdminUrlRewriteIndexSection.gridCellByColumnRowNumber('1', 'Target Path')}}" userInput="catalog/category/view/id/{$categoryId}"/> + <see selector="{{AdminUrlRewriteIndexSection.gridCellByColumnRowNumber('1', 'Request Path')}}" userInput="$$createDefaultCategory.name$$/$$createTwoLevelNestedCategories.name$$/updatedurl.html" stepKey="seeTheRedirectPath"/> <!-- Verify third level category's old URL path doesn't show redirect path--> - <click selector="{{AdminUrlRewriteIndexSection.resetButton}}" stepKey="clickOnResetButton1"/> + <click selector="{{AdminDataGridHeaderSection.clearFilters}}" stepKey="clickOnResetButton1"/> <waitForPageLoad stepKey="waitForPageToLoad3"/> - <fillField selector="{{AdminUrlRewriteIndexSection.requestPathFilter}}" userInput="{{Three_nested_categories.name_lwr}}" stepKey="fillOldUrlInRedirectPathFilter"/> - <click selector="{{AdminUrlRewriteIndexSection.searchButton}}" stepKey="clickOnSearchButton1"/> + + <click selector="{{AdminDataGridHeaderSection.filters}}" stepKey="openUrlRewriteGridFilters1"/> + <fillField selector="{{AdminDataGridHeaderSection.filterFieldInput('request_path')}}" userInput="{{Three_nested_categories.name_lwr}}" stepKey="fillOldUrlInRedirectPathFilter"/> + <click selector="{{AdminDataGridHeaderSection.applyFilters}}" stepKey="clickOrderApplyFilters1"/> <waitForPageLoad stepKey="waitForPageToLoad4"/> - <see stepKey="seeEmptyRecodsMessage" selector="{{AdminUrlRewriteIndexSection.emptyRecords}}" userInput="We couldn't find any records."/> + <see stepKey="seeEmptyRecodsMessage" selector="{{AdminDataGridTableSection.dataGridEmpty}}" userInput="We couldn't find any records."/> </test> -</tests> \ No newline at end of file +</tests> diff --git a/app/code/Magento/Catalog/Test/Mftf/Test/AdminUpdateTopCategoryUrlWithRedirectTest.xml b/app/code/Magento/Catalog/Test/Mftf/Test/AdminUpdateTopCategoryUrlWithRedirectTest.xml index ee1ed5f97edfa..c5757a6428e8d 100644 --- a/app/code/Magento/Catalog/Test/Mftf/Test/AdminUpdateTopCategoryUrlWithRedirectTest.xml +++ b/app/code/Magento/Catalog/Test/Mftf/Test/AdminUpdateTopCategoryUrlWithRedirectTest.xml @@ -13,14 +13,14 @@ <title value="Update top category url and create redirect"/> <description value="Login as admin and update top category url and create redirect"/> <testCaseId value="MC-6057"/> - <severity value="CRITICAL"/> + <severity value="BLOCKER"/> <group value="Catalog"/> <group value="mtf_migrated"/> </annotations> <before> <!-- Login as admin --> - <actionGroup ref="LoginAsAdmin" stepKey="login"/> + <actionGroup ref="AdminLoginActionGroup" stepKey="login"/> <!-- Create three level nested category --> <createData entity="_defaultCategory" stepKey="createDefaultCategory"/> <createData entity="Two_nested_categories" stepKey="createTwoLevelNestedCategories"> @@ -34,7 +34,7 @@ <deleteData createDataKey="createThreeLevelNestedCategories" stepKey="deleteThreeNestedCategories"/> <deleteData createDataKey="createTwoLevelNestedCategories" stepKey="deleteTwoLevelNestedCategory"/> <deleteData createDataKey="createDefaultCategory" stepKey="deleteDefaultCategory"/> - <actionGroup ref="logout" stepKey="logout"/> + <actionGroup ref="AdminLogoutActionGroup" stepKey="logout"/> </after> <!-- Open Category page --> @@ -66,23 +66,28 @@ <waitForPageLoad stepKey="waitForUrlRewritePage"/> <!-- Verify third level category's Redirect Path, Target Path and Redirect Type after the URL update --> - <click selector="{{AdminUrlRewriteIndexSection.resetButton}}" stepKey="clickOnResetButton"/> + <conditionalClick selector="{{AdminDataGridHeaderSection.clearFilters}}" + dependentSelector="{{AdminDataGridHeaderSection.clearFilters}}" + visible="true" + stepKey="clickOnResetButton"/> <waitForPageLoad stepKey="waitForPageToLoad2"/> - <fillField selector="{{AdminUrlRewriteIndexSection.requestPathFilter}}" userInput="updateredirecturl" stepKey="fillUpdatedURLInRedirectPathFilter"/> - <click selector="{{AdminUrlRewriteIndexSection.searchButton}}" stepKey="clickOnSearchButton"/> + <click selector="{{AdminDataGridHeaderSection.filters}}" stepKey="openUrlRewriteGridFilters"/> + <fillField selector="{{AdminDataGridHeaderSection.filterFieldInput('request_path')}}" userInput="updateredirecturl" stepKey="fillUpdatedURLInRedirectPathFilter"/> + <click selector="{{AdminDataGridHeaderSection.applyFilters}}" stepKey="clickOrderApplyFilters"/> <waitForPageLoad stepKey="waitForPageToLoad3"/> - <see selector="{{AdminUrlRewriteIndexSection.redirectTypeColumn('1')}}" userInput="No" stepKey="seeTheRedirectType"/> - <see selector="{{AdminUrlRewriteIndexSection.targetPathColumn('1')}}" userInput="catalog/category/view/id/{$categoryId}" stepKey="seeTheTargetPath"/> - <see selector="{{AdminUrlRewriteIndexSection.requestPathColumn('1')}}" userInput="$$createDefaultCategory.name$$/$$createTwoLevelNestedCategories.name$$/updateredirecturl.html" stepKey="seeTheRedirectPath"/> + <see selector="{{AdminUrlRewriteIndexSection.gridCellByColumnRowNumber('1', 'Redirect Type')}}" userInput="No" stepKey="seeTheRedirectType"/> + <see selector="{{AdminUrlRewriteIndexSection.gridCellByColumnRowNumber('1', 'Target Path')}}" userInput="catalog/category/view/id/{$categoryId}" stepKey="seeTheTargetPath"/> + <see selector="{{AdminUrlRewriteIndexSection.gridCellByColumnRowNumber('1', 'Request Path')}}" userInput="$$createDefaultCategory.name$$/$$createTwoLevelNestedCategories.name$$/updateredirecturl.html" stepKey="seeTheRedirectPath"/> <!-- Verify third level category's Redirect path, Target Path and Redirect type for old URL --> - <click selector="{{AdminUrlRewriteIndexSection.resetButton}}" stepKey="clickOnResetButton1"/> + <click selector="{{AdminDataGridHeaderSection.clearFilters}}" stepKey="clickOnResetButton1"/> <waitForPageLoad stepKey="waitForPageToLoad4"/> - <fillField selector="{{AdminUrlRewriteIndexSection.requestPathFilter}}" userInput="$$createThreeLevelNestedCategories.name$$" stepKey="fillOldUrlInRedirectPathFilter"/> - <click selector="{{AdminUrlRewriteIndexSection.searchButton}}" stepKey="clickOnSearchButton1"/> + <click selector="{{AdminDataGridHeaderSection.filters}}" stepKey="openUrlRewriteGridFilters1"/> + <fillField selector="{{AdminDataGridHeaderSection.filterFieldInput('request_path')}}" userInput="$$createThreeLevelNestedCategories.name$$" stepKey="fillOldUrlInRedirectPathFilter"/> + <click selector="{{AdminDataGridHeaderSection.applyFilters}}" stepKey="clickOrderApplyFilters1"/> <waitForPageLoad stepKey="waitForPageToLoad5"/> - <see selector="{{AdminUrlRewriteIndexSection.requestPathColumn('1')}}" userInput="$$createDefaultCategory.name$$/$$createTwoLevelNestedCategories.name$$/$$createThreeLevelNestedCategories.name$$.html" stepKey="seeTheRedirectPathForOldUrl"/> - <see selector="{{AdminUrlRewriteIndexSection.targetPathColumn('1')}}" userInput="$$createDefaultCategory.name$$/$$createTwoLevelNestedCategories.name$$/updateredirecturl.html" stepKey="seeTheTargetPathForOldUrl"/> - <see selector="{{AdminUrlRewriteIndexSection.redirectTypeColumn('1')}}" userInput="Permanent (301)" stepKey="seeTheRedirectTypeForOldUrl"/> + <see selector="{{AdminUrlRewriteIndexSection.gridCellByColumnRowNumber('1', 'Request Path')}}" userInput="$$createDefaultCategory.name$$/$$createTwoLevelNestedCategories.name$$/$$createThreeLevelNestedCategories.name$$.html" stepKey="seeTheRedirectPathForOldUrl"/> + <see selector="{{AdminUrlRewriteIndexSection.gridCellByColumnRowNumber('1', 'Target Path')}}" userInput="$$createDefaultCategory.name$$/$$createTwoLevelNestedCategories.name$$/updateredirecturl.html" stepKey="seeTheTargetPathForOldUrl"/> + <see selector="{{AdminUrlRewriteIndexSection.gridCellByColumnRowNumber('1', 'Redirect Type')}}" userInput="Permanent (301)" stepKey="seeTheRedirectTypeForOldUrl"/> </test> -</tests> \ No newline at end of file +</tests> diff --git a/app/code/Magento/Catalog/Test/Mftf/Test/AdminUpdateVirtualProductWithRegularPriceInStockVisibleInCategoryOnlyTest.xml b/app/code/Magento/Catalog/Test/Mftf/Test/AdminUpdateVirtualProductWithRegularPriceInStockVisibleInCategoryOnlyTest.xml index 0e9b9431dcfa6..595f9bcd489ec 100644 --- a/app/code/Magento/Catalog/Test/Mftf/Test/AdminUpdateVirtualProductWithRegularPriceInStockVisibleInCategoryOnlyTest.xml +++ b/app/code/Magento/Catalog/Test/Mftf/Test/AdminUpdateVirtualProductWithRegularPriceInStockVisibleInCategoryOnlyTest.xml @@ -20,7 +20,7 @@ </annotations> <before> - <actionGroup ref = "LoginAsAdmin" stepKey="loginAsAdmin"/> + <actionGroup ref="AdminLoginActionGroup" stepKey="loginAsAdmin"/> <createData entity="SimpleSubCategory" stepKey="initialCategoryEntity"/> <createData entity="defaultVirtualProduct" stepKey="initialVirtualProduct"> <requiredEntity createDataKey="initialCategoryEntity"/> @@ -33,7 +33,7 @@ </actionGroup> <deleteData stepKey="deleteSimpleSubCategory" createDataKey="initialCategoryEntity"/> <deleteData stepKey="deleteSimpleSubCategory2" createDataKey="categoryEntity"/> - <actionGroup ref="logout" stepKey="logout"/> + <actionGroup ref="AdminLogoutActionGroup" stepKey="logout"/> </after> <!-- Search default virtual product in the grid page --> diff --git a/app/code/Magento/Catalog/Test/Mftf/Test/AdminUpdateVirtualProductWithRegularPriceInStockWithCustomOptionsVisibleInSearchOnlyTest.xml b/app/code/Magento/Catalog/Test/Mftf/Test/AdminUpdateVirtualProductWithRegularPriceInStockWithCustomOptionsVisibleInSearchOnlyTest.xml index f621813f4f8c0..458d02d61426d 100644 --- a/app/code/Magento/Catalog/Test/Mftf/Test/AdminUpdateVirtualProductWithRegularPriceInStockWithCustomOptionsVisibleInSearchOnlyTest.xml +++ b/app/code/Magento/Catalog/Test/Mftf/Test/AdminUpdateVirtualProductWithRegularPriceInStockWithCustomOptionsVisibleInSearchOnlyTest.xml @@ -20,7 +20,7 @@ </annotations> <before> - <actionGroup ref = "LoginAsAdmin" stepKey="loginAsAdmin"/> + <actionGroup ref="AdminLoginActionGroup" stepKey="loginAsAdmin"/> <createData entity="SimpleSubCategory" stepKey="initialCategoryEntity"/> <createData entity="defaultVirtualProduct" stepKey="initialVirtualProduct"> <requiredEntity createDataKey="initialCategoryEntity"/> @@ -30,7 +30,7 @@ <after> <deleteData stepKey="deleteSimpleSubCategory" createDataKey="initialCategoryEntity"/> <deleteData stepKey="deleteSimpleSubCategory2" createDataKey="categoryEntity"/> - <actionGroup ref="logout" stepKey="logout"/> + <actionGroup ref="AdminLogoutActionGroup" stepKey="logout"/> </after> <!-- Search default virtual product in the grid page --> diff --git a/app/code/Magento/Catalog/Test/Mftf/Test/AdminUpdateVirtualProductWithRegularPriceOutOfStockVisibleInCategoryAndSearchTest.xml b/app/code/Magento/Catalog/Test/Mftf/Test/AdminUpdateVirtualProductWithRegularPriceOutOfStockVisibleInCategoryAndSearchTest.xml index 021ed5593c738..6d6ff0b3b1b89 100644 --- a/app/code/Magento/Catalog/Test/Mftf/Test/AdminUpdateVirtualProductWithRegularPriceOutOfStockVisibleInCategoryAndSearchTest.xml +++ b/app/code/Magento/Catalog/Test/Mftf/Test/AdminUpdateVirtualProductWithRegularPriceOutOfStockVisibleInCategoryAndSearchTest.xml @@ -20,7 +20,7 @@ </annotations> <before> - <actionGroup ref = "LoginAsAdmin" stepKey="loginAsAdmin"/> + <actionGroup ref="AdminLoginActionGroup" stepKey="loginAsAdmin"/> <createData entity="SimpleSubCategory" stepKey="initialCategoryEntity"/> <createData entity="defaultVirtualProduct" stepKey="initialVirtualProduct"> <requiredEntity createDataKey="initialCategoryEntity"/> @@ -33,7 +33,7 @@ </actionGroup> <deleteData stepKey="deleteSimpleSubCategory" createDataKey="initialCategoryEntity"/> <deleteData stepKey="deleteSimpleSubCategory2" createDataKey="categoryEntity"/> - <actionGroup ref="logout" stepKey="logout"/> + <actionGroup ref="AdminLogoutActionGroup" stepKey="logout"/> </after> <!-- Search default virtual product in the grid page --> diff --git a/app/code/Magento/Catalog/Test/Mftf/Test/AdminUpdateVirtualProductWithRegularPriceOutOfStockVisibleInCategoryOnlyTest.xml b/app/code/Magento/Catalog/Test/Mftf/Test/AdminUpdateVirtualProductWithRegularPriceOutOfStockVisibleInCategoryOnlyTest.xml index d74a6ce508b88..d5ae971d87695 100644 --- a/app/code/Magento/Catalog/Test/Mftf/Test/AdminUpdateVirtualProductWithRegularPriceOutOfStockVisibleInCategoryOnlyTest.xml +++ b/app/code/Magento/Catalog/Test/Mftf/Test/AdminUpdateVirtualProductWithRegularPriceOutOfStockVisibleInCategoryOnlyTest.xml @@ -20,7 +20,7 @@ </annotations> <before> - <actionGroup ref = "LoginAsAdmin" stepKey="loginAsAdmin"/> + <actionGroup ref="AdminLoginActionGroup" stepKey="loginAsAdmin"/> <createData entity="SimpleSubCategory" stepKey="initialCategoryEntity"/> <createData entity="defaultVirtualProduct" stepKey="initialVirtualProduct"> <requiredEntity createDataKey="initialCategoryEntity"/> @@ -33,7 +33,7 @@ </actionGroup> <deleteData stepKey="deleteSimpleSubCategory" createDataKey="initialCategoryEntity"/> <deleteData stepKey="deleteSimpleSubCategory2" createDataKey="categoryEntity"/> - <actionGroup ref="logout" stepKey="logout"/> + <actionGroup ref="AdminLogoutActionGroup" stepKey="logout"/> </after> <!-- Search default virtual product in the grid page --> diff --git a/app/code/Magento/Catalog/Test/Mftf/Test/AdminUpdateVirtualProductWithRegularPriceOutOfStockVisibleInSearchOnlyTest.xml b/app/code/Magento/Catalog/Test/Mftf/Test/AdminUpdateVirtualProductWithRegularPriceOutOfStockVisibleInSearchOnlyTest.xml index aa90d018f7710..314df67d43d00 100644 --- a/app/code/Magento/Catalog/Test/Mftf/Test/AdminUpdateVirtualProductWithRegularPriceOutOfStockVisibleInSearchOnlyTest.xml +++ b/app/code/Magento/Catalog/Test/Mftf/Test/AdminUpdateVirtualProductWithRegularPriceOutOfStockVisibleInSearchOnlyTest.xml @@ -20,7 +20,7 @@ </annotations> <before> - <actionGroup ref = "LoginAsAdmin" stepKey="loginAsAdmin"/> + <actionGroup ref="AdminLoginActionGroup" stepKey="loginAsAdmin"/> <createData entity="SimpleSubCategory" stepKey="initialCategoryEntity"/> <createData entity="defaultVirtualProduct" stepKey="initialVirtualProduct"> <requiredEntity createDataKey="initialCategoryEntity"/> @@ -31,7 +31,7 @@ <argument name="sku" value="{{updateVirtualProductRegularPrice99OutOfStock.sku}}"/> </actionGroup> <deleteData stepKey="deleteSimpleSubCategory" createDataKey="initialCategoryEntity"/> - <actionGroup ref="logout" stepKey="logout"/> + <actionGroup ref="AdminLogoutActionGroup" stepKey="logout"/> </after> <!-- Search default virtual product in the grid page --> diff --git a/app/code/Magento/Catalog/Test/Mftf/Test/AdminUpdateVirtualProductWithSpecialPriceInStockVisibleInCategoryAndSearchTest.xml b/app/code/Magento/Catalog/Test/Mftf/Test/AdminUpdateVirtualProductWithSpecialPriceInStockVisibleInCategoryAndSearchTest.xml index 94ddb3dc5e5da..d0f4fc8882e3f 100644 --- a/app/code/Magento/Catalog/Test/Mftf/Test/AdminUpdateVirtualProductWithSpecialPriceInStockVisibleInCategoryAndSearchTest.xml +++ b/app/code/Magento/Catalog/Test/Mftf/Test/AdminUpdateVirtualProductWithSpecialPriceInStockVisibleInCategoryAndSearchTest.xml @@ -20,7 +20,7 @@ </annotations> <before> - <actionGroup ref = "LoginAsAdmin" stepKey="loginAsAdmin"/> + <actionGroup ref="AdminLoginActionGroup" stepKey="loginAsAdmin"/> <createData entity="SimpleSubCategory" stepKey="initialCategoryEntity"/> <createData entity="defaultVirtualProduct" stepKey="initialVirtualProduct"> <requiredEntity createDataKey="initialCategoryEntity"/> @@ -33,7 +33,7 @@ </actionGroup> <deleteData stepKey="deleteSimpleSubCategory" createDataKey="initialCategoryEntity"/> <deleteData stepKey="deleteSimpleSubCategory2" createDataKey="categoryEntity"/> - <actionGroup ref="logout" stepKey="logout"/> + <actionGroup ref="AdminLogoutActionGroup" stepKey="logout"/> </after> <!-- Search default virtual product in the grid page --> diff --git a/app/code/Magento/Catalog/Test/Mftf/Test/AdminUpdateVirtualProductWithSpecialPriceOutOfStockVisibleInCategoryAndSearchTest.xml b/app/code/Magento/Catalog/Test/Mftf/Test/AdminUpdateVirtualProductWithSpecialPriceOutOfStockVisibleInCategoryAndSearchTest.xml index a6a629b35091e..2234d6f338b62 100644 --- a/app/code/Magento/Catalog/Test/Mftf/Test/AdminUpdateVirtualProductWithSpecialPriceOutOfStockVisibleInCategoryAndSearchTest.xml +++ b/app/code/Magento/Catalog/Test/Mftf/Test/AdminUpdateVirtualProductWithSpecialPriceOutOfStockVisibleInCategoryAndSearchTest.xml @@ -20,7 +20,7 @@ </annotations> <before> - <actionGroup ref = "LoginAsAdmin" stepKey="loginAsAdmin"/> + <actionGroup ref="AdminLoginActionGroup" stepKey="loginAsAdmin"/> <createData entity="SimpleSubCategory" stepKey="initialCategoryEntity"/> <createData entity="defaultVirtualProduct" stepKey="initialVirtualProduct"> <requiredEntity createDataKey="initialCategoryEntity"/> @@ -33,7 +33,7 @@ </actionGroup> <deleteData stepKey="deleteSimpleSubCategory" createDataKey="initialCategoryEntity"/> <deleteData stepKey="deleteSimpleSubCategory2" createDataKey="categoryEntity"/> - <actionGroup ref="logout" stepKey="logout"/> + <actionGroup ref="AdminLogoutActionGroup" stepKey="logout"/> </after> <!-- Search default virtual product in the grid page --> diff --git a/app/code/Magento/Catalog/Test/Mftf/Test/AdminUpdateVirtualProductWithTierPriceInStockVisibleInCategoryAndSearchTest.xml b/app/code/Magento/Catalog/Test/Mftf/Test/AdminUpdateVirtualProductWithTierPriceInStockVisibleInCategoryAndSearchTest.xml index 10347584b4cda..ab5d23f0f875e 100644 --- a/app/code/Magento/Catalog/Test/Mftf/Test/AdminUpdateVirtualProductWithTierPriceInStockVisibleInCategoryAndSearchTest.xml +++ b/app/code/Magento/Catalog/Test/Mftf/Test/AdminUpdateVirtualProductWithTierPriceInStockVisibleInCategoryAndSearchTest.xml @@ -8,7 +8,7 @@ <tests xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/testSchema.xsd"> - <test name="UpdateVirtualProductWithTierPriceInStockVisibleInCategoryAndSearchTest"> + <test name="AdminUpdateVirtualProductWithTierPriceInStockVisibleInCategoryAndSearchTest"> <annotations> <stories value="Update Virtual Product"/> <title value="Update Virtual Product with Tier Price (In Stock) Visible in Category and Search"/> @@ -20,141 +20,112 @@ </annotations> <before> - <actionGroup ref = "LoginAsAdmin" stepKey="loginAsAdmin"/> <createData entity="SimpleSubCategory" stepKey="initialCategoryEntity"/> <createData entity="defaultVirtualProduct" stepKey="initialVirtualProduct"> <requiredEntity createDataKey="initialCategoryEntity"/> </createData> <createData entity="SimpleSubCategory" stepKey="categoryEntity"/> + <actionGroup ref="AdminLoginActionGroup" stepKey="loginAsAdmin"/> + <magentoCron groups="index" stepKey="RunToScheduleJobs"/> </before> + <after> + <deleteData createDataKey="initialCategoryEntity" stepKey="deleteInitialCategory"/> + <deleteData createDataKey="categoryEntity" stepKey="deleteCategory" /> <actionGroup ref="DeleteProductUsingProductGridActionGroup" stepKey="deleteVirtualProduct"> <argument name="product" value="updateVirtualProductTierPriceInStock"/> </actionGroup> - <deleteData stepKey="deleteSimpleSubCategory" createDataKey="initialCategoryEntity"/> - <deleteData stepKey="deleteSimpleSubCategory2" createDataKey="categoryEntity"/> - <actionGroup ref="logout" stepKey="logout"/> + <actionGroup ref="ClearFiltersAdminDataGridActionGroup" stepKey="clearProductsGridFilters"/> + <actionGroup ref="AdminLogoutActionGroup" stepKey="logoutFromAdmin"/> </after> - <!-- Search default virtual product in the grid --> - <amOnPage url="{{ProductCatalogPage.url}}" stepKey="OpenProductCatalogPage1"/> - <waitForPageLoad stepKey="waitForProductCatalogPage1"/> - <conditionalClick selector="{{AdminProductGridFilterSection.clearAll}}" dependentSelector="{{AdminProductGridFilterSection.clearAll}}" visible="true" stepKey="clickClearAllFilter" /> - <fillField selector="{{AdminProductGridFilterSection.keywordSearch}}" userInput="$$initialVirtualProduct.name$$" stepKey="fillVirtualProductNameInKeywordSearch"/> - <click selector="{{AdminProductGridFilterSection.keywordSearchButton}}" stepKey="clickKeywordSearchButton"/> - <waitForPageLoad stepKey="waitForProductSearch"/> - <click selector="{{AdminProductGridFilterSection.nthRow('1')}}" stepKey="clickFirstRowToVerifyCreatedVirtualProduct"/> - <waitForPageLoad stepKey="waitUntilProductIsOpened"/> + <actionGroup ref="FilterAndSelectProductActionGroup" stepKey="openProductEditPageBySKU"> + <argument name="productSku" value="$initialVirtualProduct.sku$"/> + </actionGroup> <!-- Update virtual product with tier price --> - <fillField selector="{{AdminProductFormSection.productName}}" userInput="{{updateVirtualProductTierPriceInStock.name}}" stepKey="fillProductName"/> - <fillField selector="{{AdminProductFormSection.productSku}}" userInput="{{updateVirtualProductTierPriceInStock.sku}}" stepKey="fillProductSku"/> - <fillField selector="{{AdminProductFormSection.productPrice}}" userInput="{{updateVirtualProductTierPriceInStock.price}}" stepKey="fillProductPrice"/> + <actionGroup ref="FillMainProductFormNoWeightActionGroup" stepKey="fillNewProductData"> + <argument name="product" value="updateVirtualProductTierPriceInStock"/> + </actionGroup> <!-- Press enter to validate advanced pricing link --> <pressKey selector="{{AdminProductFormSection.productPrice}}" parameterArray="[\Facebook\WebDriver\WebDriverKeys::ENTER]" stepKey="pressEnterKey"/> - <click selector="{{AdminProductFormSection.advancedPricingLink}}" stepKey="clickAdvancedPricingLink"/> - <click selector="{{AdminProductFormAdvancedPricingSection.customerGroupPriceAddButton}}" stepKey="clickCustomerGroupPriceAddButton"/> - <scrollTo selector="{{AdminProductFormAdvancedPricingSection.productTierPriceQtyInput('0')}}" x="50" y="0" stepKey="scrollToProductTierPriceQuantityInputTextBox"/> - <selectOption selector="{{AdminProductFormAdvancedPricingSection.productTierPriceWebsiteSelect('0')}}" userInput="{{tierPriceOnVirtualProduct.website}}" stepKey="selectProductTierPriceWebsiteInput"/> - <selectOption selector="{{AdminProductFormAdvancedPricingSection.productTierPriceCustGroupSelect('0')}}" userInput="{{tierPriceOnVirtualProduct.customer_group}}" stepKey="selectProductTierPriceCustomerGroupInput"/> - <fillField selector="{{AdminProductFormAdvancedPricingSection.productTierPriceQtyInput('0')}}" userInput="{{tierPriceOnVirtualProduct.qty}}" stepKey="fillProductTierPriceQuantityInput"/> - <fillField selector="{{AdminProductFormAdvancedPricingSection.productTierPriceFixedPriceInput('0')}}" userInput="{{tierPriceOnVirtualProduct.price}}" stepKey="selectProductTierPriceFixedPrice"/> - <click selector="{{AdminProductFormAdvancedPricingSection.doneButton}}" stepKey="clickDoneButton"/> + <actionGroup ref="AdminProductFormOpenAdvancedPricingDialogActionGroup" stepKey="openAdvancedPricingDialog"/> + <actionGroup ref="AdminProductFormAdvancedPricingAddTierPriceActionGroup" stepKey="addTierPrice"> + <argument name="website" value="{{tierPriceOnVirtualProduct.website}}"/> + <argument name="customerGroup" value="{{tierPriceOnVirtualProduct.customer_group}}"/> + <argument name="quantity" value="{{tierPriceOnVirtualProduct.qty}}"/> + <argument name="priceType" value="Fixed"/> + <argument name="amount" value="{{tierPriceOnVirtualProduct.price}}"/> + </actionGroup> + <actionGroup ref="AdminProductFormDoneAdvancedPricingDialogActionGroup" stepKey="doneAdvancedPricingModal"/> <selectOption selector="{{AdminProductFormSection.productTaxClass}}" userInput="{{updateVirtualProductTierPriceInStock.productTaxClass}}" stepKey="selectProductStockClass"/> - <fillField selector="{{AdminProductFormSection.productQuantity}}" userInput="{{updateVirtualProductTierPriceInStock.quantity}}" stepKey="fillProductQuantity"/> - <selectOption selector="{{AdminProductFormSection.stockStatus}}" userInput="{{updateVirtualProductTierPriceInStock.status}}" stepKey="selectStockStatusInStock"/> - <click selector="{{AdminProductFormSection.categoriesDropdown}}" stepKey="clickCategoriesDropDown"/> - <fillField selector="{{AdminProductFormSection.searchCategory}}" userInput="$$initialCategoryEntity.name$$" stepKey="fillSearchForInitialCategory" /> - <waitForPageLoad stepKey="waitForCategory1"/> - <click selector="{{AdminProductFormSection.selectCategory($$initialCategoryEntity.name$$)}}" stepKey="unselectInitialCategory"/> - <fillField selector="{{AdminProductFormSection.searchCategory}}" userInput="$$categoryEntity.name$$" stepKey="fillSearchCategory" /> - <waitForPageLoad stepKey="waitForCategory2"/> - <click selector="{{AdminProductFormSection.selectCategory($$categoryEntity.name$$)}}" stepKey="clickOnCategory"/> - <click selector="{{AdminProductFormSection.done}}" stepKey="clickOnDoneAdvancedCategorySelect"/> + <actionGroup ref="RemoveCategoryFromProductActionGroup" stepKey="unselectInitialCategory"> + <argument name="categoryName" value="$initialCategoryEntity.name$"/> + </actionGroup> + <actionGroup ref="SetCategoryByNameActionGroup" stepKey="setNewCategory"> + <argument name="categoryName" value="$categoryEntity.name$"/> + </actionGroup> <selectOption selector="{{AdminProductFormSection.visibility}}" userInput="{{updateVirtualProductTierPriceInStock.visibility}}" stepKey="selectVisibility"/> - <click selector="{{AdminProductSEOSection.sectionHeader}}" stepKey="clickAdminProductSEOSection"/> - <fillField selector="{{AdminProductSEOSection.urlKeyInput}}" userInput="{{updateVirtualProductTierPriceInStock.urlKey}}" stepKey="fillUrlKey"/> - <scrollToTopOfPage stepKey="scrollToTopOfAdminProductFormSection"/> - <click selector="{{AdminProductFormSection.save}}" stepKey="clickSaveButton"/> - <waitForPageLoad stepKey="waitForVirtualProductSaved"/> - <!-- Verify we see success message --> - <see selector="{{AdminProductFormSection.successMessage}}" userInput="You saved the product." stepKey="seeAssertVirtualProductSaveSuccessMessage"/> + <actionGroup ref="SetProductUrlKeyByStringActionGroup" stepKey="updateUrlKey"> + <argument name="urlKey" value="{{updateVirtualProductTierPriceInStock.urlKey}}"/> + </actionGroup> + <actionGroup ref="SaveProductFormActionGroup" stepKey="saveProductAndCheckSuccessMessage"/> <!-- Search updated virtual product(from above step) in the grid --> - <amOnPage url="{{ProductCatalogPage.url}}" stepKey="OpenProductCatalogPageToSearchUpdatedVirtualProduct"/> - <waitForPageLoad stepKey="waitForProductCatalogPageToLoad"/> - <conditionalClick selector="{{AdminProductGridFilterSection.clearAll}}" dependentSelector="{{AdminProductGridFilterSection.clearAll}}" visible="true" stepKey="clickClearAll"/> - <click selector="{{AdminProductGridFilterSection.filters}}" stepKey="clickFiltersButton"/> - <fillField selector="{{AdminProductGridFilterSection.nameFilter}}" userInput="{{updateVirtualProductTierPriceInStock.name}}" stepKey="fillVirtualProductNameInNameFilter"/> - <fillField selector="{{AdminProductGridFilterSection.skuFilter}}" userInput="{{updateVirtualProductTierPriceInStock.sku}}" stepKey="fillVirtualProductSku"/> - <click selector="{{AdminProductGridFilterSection.applyFilters}}" stepKey="clickApplyFiltersButton"/> - <click selector="{{AdminProductGridFilterSection.nthRow('1')}}" stepKey="clickFirstRowToVerifyUpdatedVirtualProductVisibleInGrid"/> - <waitForPageLoad stepKey="waitUntilVirtualProductPageIsOpened"/> + <actionGroup ref="SearchForProductOnBackendActionGroup" stepKey="searchForSimpleProduct"> + <argument name="product" value="updateVirtualProductTierPriceInStock"/> + </actionGroup> <!-- Verify customer see updated virtual product with tier price(from the above step) in the product form page --> - <seeInField selector="{{AdminProductFormSection.productName}}" userInput="{{updateVirtualProductTierPriceInStock.name}}" stepKey="seeProductName"/> - <seeInField selector="{{AdminProductFormSection.productSku}}" userInput="{{updateVirtualProductTierPriceInStock.sku}}" stepKey="seeProductSku"/> - <seeInField selector="{{AdminProductFormSection.productPrice}}" userInput="{{updateVirtualProductTierPriceInStock.price}}" stepKey="seeProductPrice"/> - <click selector="{{AdminProductFormSection.advancedPricingLink}}" stepKey="clickAdvancedPricingLink1"/> - <seeInField selector="{{AdminProductFormAdvancedPricingSection.productTierPriceWebsiteSelect('0')}}" userInput="{{tierPriceOnVirtualProduct.website}}" stepKey="seeProductTierPriceWebsiteInput"/> - <seeInField selector="{{AdminProductFormAdvancedPricingSection.productTierPriceCustGroupSelect('0')}}" userInput="{{tierPriceOnVirtualProduct.customer_group}}" stepKey="seeProductTierPriceCustomerGroupInput"/> - <seeInField selector="{{AdminProductFormAdvancedPricingSection.productTierPriceQtyInput('0')}}" userInput="{{tierPriceOnVirtualProduct.qty}}" stepKey="seeProductTierPriceQuantityInput"/> - <seeInField selector="{{AdminProductFormAdvancedPricingSection.productTierPriceFixedPriceInput('0')}}" userInput="{{tierPriceOnVirtualProduct.price}}" stepKey="seeProductTierPriceFixedPrice"/> - <click selector="{{AdminProductFormAdvancedPricingSection.advancedPricingCloseButton}}" stepKey="clickAdvancedPricingCloseButton"/> + <actionGroup ref="AssertProductInfoOnEditPageActionGroup" stepKey="verifyProductInAdminEditForm"> + <argument name="product" value="updateVirtualProductTierPriceInStock"/> + </actionGroup> + <actionGroup ref="AdminProductFormOpenAdvancedPricingDialogActionGroup" stepKey="openAdvancedPricingDialogAgain"/> + <actionGroup ref="AssertAdminProductFormAdvancedPricingCheckTierPriceActionGroup" stepKey="checkTierPrice"> + <argument name="rowNumber" value="0"/> + <argument name="website" value="{{tierPriceOnVirtualProduct.website}}"/> + <argument name="customerGroup" value="{{tierPriceOnVirtualProduct.customer_group}}"/> + <argument name="quantity" value="{{tierPriceOnVirtualProduct.qty}}"/> + <argument name="priceType" value="Fixed"/> + <argument name="amount" value="{{tierPriceOnVirtualProduct.price}}"/> + </actionGroup> + <actionGroup ref="AdminProductFormCloseAdvancedPricingDialogActionGroup" stepKey="closeAdvancedPricingModal"/> <seeInField selector="{{AdminProductFormSection.productTaxClass}}" userInput="{{updateVirtualProductTierPriceInStock.productTaxClass}}" stepKey="seeProductTaxClass"/> - <seeInField selector="{{AdminProductFormSection.productQuantity}}" userInput="{{updateVirtualProductTierPriceInStock.quantity}}" stepKey="seeProductQuantity"/> - <seeInField selector="{{AdminProductFormSection.productStockStatus}}" userInput="{{updateVirtualProductTierPriceInStock.status}}" stepKey="seeProductStockStatus"/> <click selector="{{AdminProductFormSection.categoriesDropdown}}" stepKey="clickCategoriesDropDownToVerify"/> <grabMultiple selector="{{AdminProductFormSection.selectMultipleCategories}}" stepKey="selectedCategories" /> <assertEquals stepKey="assertSelectedCategories"> <actualResult type="variable">selectedCategories</actualResult> - <expectedResult type="array">[$$categoryEntity.name$$]</expectedResult> + <expectedResult type="array">[$categoryEntity.name$]</expectedResult> </assertEquals> <click selector="{{AdminProductFormSection.done}}" stepKey="clickOnDoneOnCategorySelect"/> <seeInField selector="{{AdminProductFormSection.visibility}}" userInput="{{updateVirtualProductTierPriceInStock.visibility}}" stepKey="seeVisibility"/> - <scrollTo selector="{{AdminProductSEOSection.sectionHeader}}" x="0" y="-80" stepKey="scrollToAdminProductSEOSection1"/> - <click selector="{{AdminProductSEOSection.sectionHeader}}" stepKey="clickAdminProductSEOSection1"/> + + <conditionalClick selector="{{AdminProductSEOSection.sectionHeader}}" dependentSelector="{{AdminProductSEOSection.useDefaultUrl}}" visible="false" stepKey="openSearchEngineOptimizationSection"/> + <scrollTo selector="{{AdminProductSEOSection.sectionHeader}}" stepKey="scrollToAdminProductSEOSection"/> <seeInField selector="{{AdminProductSEOSection.urlKeyInput}}" userInput="{{updateVirtualProductTierPriceInStock.urlKey}}" stepKey="seeUrlKey"/> <!--Verify customer see updated virtual product link on category page --> - <amOnPage url="{{StorefrontCategoryPage.url($$categoryEntity.name$$)}}" stepKey="openCategoryPage"/> - <waitForPageLoad stepKey="waitForCategoryPageLoad"/> - <see selector="{{StorefrontCategoryMainSection.productLink}}" userInput="{{updateVirtualProductTierPriceInStock.name}}" stepKey="seeVirtualProductLinkOnCategoryPage"/> + <actionGroup ref="StorefrontNavigateCategoryPageActionGroup" stepKey="openCategoryPageOnFrontend"> + <argument name="category" value="$categoryEntity$"/> + </actionGroup> + <actionGroup ref="AssertStorefrontProductIsPresentOnCategoryPageActionGroup" stepKey="checkProductOnCategoryPage"> + <argument name="productName" value="{{updateVirtualProductTierPriceInStock.name}}"/> + </actionGroup> <!--Verify customer see updated virtual product with tier price on product storefront page --> - <amOnPage url="{{StorefrontProductPage.url(updateVirtualProductTierPriceInStock.urlKey)}}" stepKey="goToProductPage"/> - <waitForPageLoad stepKey="waitForStorefrontProductPageLoad"/> - <see selector="{{StorefrontProductInfoMainSection.productName}}" userInput="{{updateVirtualProductTierPriceInStock.name}}" stepKey="seeVirtualProductNameOnStoreFrontPage"/> - <see selector="{{StorefrontProductInfoMainSection.productPrice}}" userInput="{{updateVirtualProductTierPriceInStock.price}}" stepKey="seeVirtualProductPriceOnStoreFrontPage"/> - <see selector="{{StorefrontProductInfoMainSection.productSku}}" userInput="{{updateVirtualProductTierPriceInStock.sku}}" stepKey="seeVirtualProductSku"/> - <grabTextFrom selector="{{StorefrontProductInfoMainSection.productStockStatus}}" stepKey="productStockAvailableStatus"/> - <assertEquals stepKey="assertStockAvailableOnProductPage"> - <expectedResult type="string">{{updateVirtualProductTierPriceInStock.storefrontStatus}}</expectedResult> - <actualResult type="variable">productStockAvailableStatus</actualResult> - </assertEquals> - <grabTextFrom selector="{{StorefrontProductInfoMainSection.productPrice}}" stepKey="productPriceAmount"/> - <assertEquals stepKey="assertOldPriceTextOnProductPage"> - <expectedResult type="string">${{updateVirtualProductTierPriceInStock.price}}</expectedResult> - <actualResult type="variable">productPriceAmount</actualResult> - </assertEquals> - <grabTextFrom selector="{{StorefrontProductInfoMainSection.tierPriceText}}" stepKey="tierPriceText"/> - <assertEquals stepKey="assertTierPriceTextOnProductPage"> - <expectedResult type="string">Buy {{tierPriceOnVirtualProduct.qty}} for ${{tierPriceOnVirtualProduct.price}} each and save 38%</expectedResult> - <actualResult type="variable">tierPriceText</actualResult> - </assertEquals> + <actionGroup ref="AssertProductInStorefrontProductPageActionGroup" stepKey="verifyProductOnFrontend"> + <argument name="product" value="updateVirtualProductTierPriceInStock"/> + </actionGroup> + <see selector="{{StorefrontProductInfoMainSection.productStockStatus}}" userInput="{{updateVirtualProductTierPriceInStock.storefrontStatus}}" stepKey="assertStockAvailableOnProductPage"/> + <see selector="{{StorefrontProductInfoMainSection.tierPriceText}}" userInput="Buy {{tierPriceOnVirtualProduct.qty}} for ${{tierPriceOnVirtualProduct.price}} each and save 38%" stepKey="assertTierPriceTextOnProductPage"/> <!--Verify customer see updated virtual product link on magento storefront page and is searchable by sku --> - <amOnPage url="{{StorefrontProductPage.url(updateVirtualProductTierPriceInStock.urlKey)}}" stepKey="goToMagentoStorefrontPage"/> - <waitForPageLoad stepKey="waitForStoreFrontProductPageLoad"/> - <fillField selector="{{StorefrontQuickSearchResultsSection.searchTextBox}}" userInput="{{updateVirtualProductTierPriceInStock.sku}}" stepKey="fillVirtualProductName"/> - <waitForPageLoad stepKey="waitForSearchTextBox"/> - <click selector="{{StorefrontQuickSearchResultsSection.searchTextBoxButton}}" stepKey="clickSearchTextBoxButton"/> - <waitForPageLoad stepKey="waitForSearch"/> - <see selector="{{StorefrontQuickSearchResultsSection.productLink}}" userInput="{{updateVirtualProductTierPriceInStock.name}}" stepKey="seeVirtualProductName"/> - <grabTextFrom selector="{{StorefrontQuickSearchResultsSection.asLowAsLabel}}" stepKey="tierPriceTextOnStorefrontPage"/> - <assertEquals stepKey="assertTierPriceTextOnCategoryPage"> - <expectedResult type="string">As low as ${{tierPriceOnVirtualProduct.price}}</expectedResult> - <actualResult type="variable">tierPriceTextOnStorefrontPage</actualResult> - </assertEquals> + <actionGroup ref="StorefrontCheckQuickSearchStringActionGroup" stepKey="quickSearchProductBySku"> + <argument name="phrase" value="{{updateVirtualProductTierPriceInStock.sku}}"/> + </actionGroup> + <actionGroup ref="StorefrontQuickSearchSeeProductByNameActionGroup" stepKey="checkProductInSearchResults"> + <argument name="productName" value="{{updateVirtualProductTierPriceInStock.name}}"/> + </actionGroup> + <see selector="{{StorefrontQuickSearchResultsSection.asLowAsLabel}}" userInput="As low as ${{tierPriceOnVirtualProduct.price}}" stepKey="assertTierPriceTextOnCategoryPage"/> </test> </tests> diff --git a/app/code/Magento/Catalog/Test/Mftf/Test/AdminUpdateVirtualProductWithTierPriceInStockVisibleInCategoryOnlyTest.xml b/app/code/Magento/Catalog/Test/Mftf/Test/AdminUpdateVirtualProductWithTierPriceInStockVisibleInCategoryOnlyTest.xml index 0edc487e71edf..8f0861fe33371 100644 --- a/app/code/Magento/Catalog/Test/Mftf/Test/AdminUpdateVirtualProductWithTierPriceInStockVisibleInCategoryOnlyTest.xml +++ b/app/code/Magento/Catalog/Test/Mftf/Test/AdminUpdateVirtualProductWithTierPriceInStockVisibleInCategoryOnlyTest.xml @@ -20,7 +20,7 @@ </annotations> <before> - <actionGroup ref = "LoginAsAdmin" stepKey="loginAsAdmin"/> + <actionGroup ref="AdminLoginActionGroup" stepKey="loginAsAdmin"/> <createData entity="SimpleSubCategory" stepKey="initialCategoryEntity"/> <createData entity="defaultVirtualProduct" stepKey="initialVirtualProduct"> <requiredEntity createDataKey="initialCategoryEntity"/> @@ -33,7 +33,7 @@ </actionGroup> <deleteData stepKey="deleteSimpleSubCategory" createDataKey="initialCategoryEntity"/> <deleteData stepKey="deleteSimpleSubCategory2" createDataKey="categoryEntity"/> - <actionGroup ref="logout" stepKey="logout"/> + <actionGroup ref="AdminLogoutActionGroup" stepKey="logout"/> </after> <!-- Search default virtual product in the grid page --> diff --git a/app/code/Magento/Catalog/Test/Mftf/Test/AdminUpdateVirtualProductWithTierPriceOutOfStockVisibleInCategoryAndSearchTest.xml b/app/code/Magento/Catalog/Test/Mftf/Test/AdminUpdateVirtualProductWithTierPriceOutOfStockVisibleInCategoryAndSearchTest.xml index e513d007e6a09..f7f5385381590 100644 --- a/app/code/Magento/Catalog/Test/Mftf/Test/AdminUpdateVirtualProductWithTierPriceOutOfStockVisibleInCategoryAndSearchTest.xml +++ b/app/code/Magento/Catalog/Test/Mftf/Test/AdminUpdateVirtualProductWithTierPriceOutOfStockVisibleInCategoryAndSearchTest.xml @@ -20,7 +20,7 @@ </annotations> <before> - <actionGroup ref = "LoginAsAdmin" stepKey="loginAsAdmin"/> + <actionGroup ref="AdminLoginActionGroup" stepKey="loginAsAdmin"/> <createData entity="SimpleSubCategory" stepKey="initialCategoryEntity"/> <createData entity="defaultVirtualProduct" stepKey="initialVirtualProduct"> <requiredEntity createDataKey="initialCategoryEntity"/> @@ -33,7 +33,7 @@ </actionGroup> <deleteData stepKey="deleteSimpleSubCategory" createDataKey="initialCategoryEntity"/> <deleteData stepKey="deleteSimpleSubCategory2" createDataKey="categoryEntity"/> - <actionGroup ref="logout" stepKey="logout"/> + <actionGroup ref="AdminLogoutActionGroup" stepKey="logout"/> </after> <!-- Search default virtual product in the grid page --> diff --git a/app/code/Magento/Catalog/Test/Mftf/Test/AdminVerifyProductOrderTest.xml b/app/code/Magento/Catalog/Test/Mftf/Test/AdminVerifyProductOrderTest.xml index b61be7fd95a58..9146ee4d4d579 100644 --- a/app/code/Magento/Catalog/Test/Mftf/Test/AdminVerifyProductOrderTest.xml +++ b/app/code/Magento/Catalog/Test/Mftf/Test/AdminVerifyProductOrderTest.xml @@ -8,7 +8,7 @@ <tests xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/testSchema.xsd"> - <test name="AdminVerifyProductOrder"> + <test name="AdminVerifyProductOrderTest"> <annotations> <features value="Catalog"/> <stories value="Verify Product Order"/> @@ -20,10 +20,10 @@ <group value="product"/> </annotations> <before> - <actionGroup ref="LoginAsAdmin" stepKey="loginAsAdmin1"/> + <actionGroup ref="AdminLoginActionGroup" stepKey="loginAsAdmin1"/> </before> <after> - <actionGroup ref="logout" stepKey="logout"/> + <actionGroup ref="AdminLogoutActionGroup" stepKey="logout"/> </after> <actionGroup ref="GoToProductCatalogPageActionGroup" stepKey="goToProductCatalogPage"/> <actionGroup ref="VerifyProductTypeOrder" stepKey="verifyProductTypeOrder"/> diff --git a/app/code/Magento/Catalog/Test/Mftf/Test/AdvanceCatalogSearchSimpleProductTest.xml b/app/code/Magento/Catalog/Test/Mftf/Test/AdvanceCatalogSearchSimpleProductTest.xml deleted file mode 100644 index 3125ba3decce6..0000000000000 --- a/app/code/Magento/Catalog/Test/Mftf/Test/AdvanceCatalogSearchSimpleProductTest.xml +++ /dev/null @@ -1,112 +0,0 @@ -<?xml version="1.0" encoding="UTF-8"?> -<!-- - /** - * Copyright © Magento, Inc. All rights reserved. - * See COPYING.txt for license details. - */ ---> - -<tests xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" - xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/testSchema.xsd"> - <test name="AdvanceCatalogSearchSimpleProductByNameTest"> - <annotations> - <features value="Catalog"/> - <stories value="Advanced Catalog Product Search for all product types"/> - <title value="Guest customer should be able to advance search simple product with product name"/> - <description value="Guest customer should be able to advance search simple product with product name"/> - <severity value="MAJOR"/> - <testCaseId value="MC-132"/> - <group value="Catalog"/> - </annotations> - <before> - <actionGroup ref="LoginAsAdmin" stepKey="loginAsAdmin"/> - <actionGroup ref="DeleteAllProductsUsingProductGridActionGroup" stepKey="deleteAllProducts"/> - <createData entity="ApiProductWithDescription" stepKey="product"/> - </before> - <after> - <deleteData createDataKey="product" stepKey="delete"/> - <actionGroup ref="logout" stepKey="logoutFromAdmin"/> - </after> - </test> - <test name="AdvanceCatalogSearchSimpleProductBySkuTest"> - <annotations> - <features value="Catalog"/> - <stories value="Advanced Catalog Product Search for all product types"/> - <title value="Guest customer should be able to advance search simple product with product sku"/> - <description value="Guest customer should be able to advance search simple product with product sku"/> - <severity value="MAJOR"/> - <testCaseId value="MC-133"/> - <group value="Catalog"/> - </annotations> - <before> - <createData entity="ApiProductWithDescriptionAndUnderscoredSku" stepKey="product"/> - </before> - <after> - <deleteData createDataKey="product" stepKey="delete"/> - </after> - </test> - <test name="AdvanceCatalogSearchSimpleProductByDescriptionTest"> - <annotations> - <features value="Catalog"/> - <stories value="Advanced Catalog Product Search for all product types"/> - <title value="Guest customer should be able to advance search simple product with product description"/> - <description value="Guest customer should be able to advance search simple product with product description"/> - <severity value="MAJOR"/> - <testCaseId value="MC-134"/> - <group value="Catalog"/> - </annotations> - <before> - <createData entity="ApiProductWithDescription" stepKey="product"/> - </before> - <after> - <deleteData createDataKey="product" stepKey="delete"/> - </after> - </test> - <test name="AdvanceCatalogSearchSimpleProductByShortDescriptionTest"> - <annotations> - <features value="Catalog"/> - <stories value="Advanced Catalog Product Search for all product types"/> - <title value="Guest customer should be able to advance search simple product with product short description"/> - <description value="Guest customer should be able to advance search simple product with product short description"/> - <severity value="MAJOR"/> - <testCaseId value="MC-135"/> - <group value="Catalog"/> - </annotations> - <before> - <createData entity="ApiProductWithDescription" stepKey="product"/> - </before> - <after> - <deleteData createDataKey="product" stepKey="delete"/> - </after> - </test> - <test name="AdvanceCatalogSearchSimpleProductByPriceTest"> - <annotations> - <features value="Catalog"/> - <stories value="Advanced Catalog Product Search for all product types"/> - <title value="Guest customer should be able to advance search simple product with product price"/> - <description value="Guest customer should be able to advance search simple product with product price"/> - <severity value="MAJOR"/> - <testCaseId value="MC-136"/> - <group value="Catalog"/> - </annotations> - <before> - <actionGroup ref="LoginAsAdmin" stepKey="loginAsAdmin"/> - <actionGroup ref="DeleteAllProductsUsingProductGridActionGroup" stepKey="deleteAllProducts"/> - - <createData entity="ApiProductWithDescription" stepKey="product"/> - <getData entity="GetProduct" stepKey="arg1"> - <requiredEntity createDataKey="product"/> - </getData> - <getData entity="GetProduct" stepKey="arg2"> - <requiredEntity createDataKey="product"/> - </getData> - <getData entity="GetProduct" stepKey="arg3"> - <requiredEntity createDataKey="product"/> - </getData> - </before> - <after> - <deleteData createDataKey="product" stepKey="delete"/> - <actionGroup ref="logout" stepKey="logoutFromAdmin"/> - </after> - </test> -</tests> diff --git a/app/code/Magento/Catalog/Test/Mftf/Test/AdvanceCatalogSearchSimpleProductTest/AdvanceCatalogSearchSimpleProductByDescriptionTest.xml b/app/code/Magento/Catalog/Test/Mftf/Test/AdvanceCatalogSearchSimpleProductTest/AdvanceCatalogSearchSimpleProductByDescriptionTest.xml new file mode 100644 index 0000000000000..30771fcfd947b --- /dev/null +++ b/app/code/Magento/Catalog/Test/Mftf/Test/AdvanceCatalogSearchSimpleProductTest/AdvanceCatalogSearchSimpleProductByDescriptionTest.xml @@ -0,0 +1,27 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + /** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +--> +<tests xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/testSchema.xsd"> + <test name="AdvanceCatalogSearchSimpleProductByDescriptionTest"> + <annotations> + <features value="Catalog"/> + <stories value="Advanced Catalog Product Search for all product types"/> + <title value="Guest customer should be able to advance search simple product with product description"/> + <description value="Guest customer should be able to advance search simple product with product description"/> + <severity value="MAJOR"/> + <testCaseId value="MC-134"/> + <group value="Catalog"/> + </annotations> + <before> + <createData entity="ApiProductWithDescription" stepKey="product"/> + </before> + <after> + <deleteData createDataKey="product" stepKey="delete"/> + </after> + </test> +</tests> diff --git a/app/code/Magento/Catalog/Test/Mftf/Test/AdvanceCatalogSearchSimpleProductTest/AdvanceCatalogSearchSimpleProductByNameTest.xml b/app/code/Magento/Catalog/Test/Mftf/Test/AdvanceCatalogSearchSimpleProductTest/AdvanceCatalogSearchSimpleProductByNameTest.xml new file mode 100644 index 0000000000000..5115399db9e3b --- /dev/null +++ b/app/code/Magento/Catalog/Test/Mftf/Test/AdvanceCatalogSearchSimpleProductTest/AdvanceCatalogSearchSimpleProductByNameTest.xml @@ -0,0 +1,30 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + /** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +--> +<tests xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/testSchema.xsd"> + <test name="AdvanceCatalogSearchSimpleProductByNameTest"> + <annotations> + <features value="Catalog"/> + <stories value="Advanced Catalog Product Search for all product types"/> + <title value="Guest customer should be able to advance search simple product with product name"/> + <description value="Guest customer should be able to advance search simple product with product name"/> + <severity value="MAJOR"/> + <testCaseId value="MC-132"/> + <group value="Catalog"/> + </annotations> + <before> + <actionGroup ref="AdminLoginActionGroup" stepKey="loginAsAdmin"/> + <actionGroup ref="DeleteAllProductsUsingProductGridActionGroup" stepKey="deleteAllProducts"/> + <createData entity="ApiProductWithDescription" stepKey="product"/> + </before> + <after> + <deleteData createDataKey="product" stepKey="delete"/> + <actionGroup ref="AdminLogoutActionGroup" stepKey="logoutFromAdmin"/> + </after> + </test> +</tests> diff --git a/app/code/Magento/Catalog/Test/Mftf/Test/AdvanceCatalogSearchSimpleProductTest/AdvanceCatalogSearchSimpleProductByPriceTest.xml b/app/code/Magento/Catalog/Test/Mftf/Test/AdvanceCatalogSearchSimpleProductTest/AdvanceCatalogSearchSimpleProductByPriceTest.xml new file mode 100644 index 0000000000000..cacf4f3f4c9f5 --- /dev/null +++ b/app/code/Magento/Catalog/Test/Mftf/Test/AdvanceCatalogSearchSimpleProductTest/AdvanceCatalogSearchSimpleProductByPriceTest.xml @@ -0,0 +1,40 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + /** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +--> +<tests xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/testSchema.xsd"> + <test name="AdvanceCatalogSearchSimpleProductByPriceTest"> + <annotations> + <features value="Catalog"/> + <stories value="Advanced Catalog Product Search for all product types"/> + <title value="Guest customer should be able to advance search simple product with product price"/> + <description value="Guest customer should be able to advance search simple product with product price"/> + <severity value="MAJOR"/> + <testCaseId value="MC-136"/> + <group value="Catalog"/> + </annotations> + <before> + <actionGroup ref="AdminLoginActionGroup" stepKey="loginAsAdmin"/> + <actionGroup ref="DeleteAllProductsUsingProductGridActionGroup" stepKey="deleteAllProducts"/> + + <createData entity="ApiProductWithDescription" stepKey="product"/> + <getData entity="GetProduct" stepKey="arg1"> + <requiredEntity createDataKey="product"/> + </getData> + <getData entity="GetProduct" stepKey="arg2"> + <requiredEntity createDataKey="product"/> + </getData> + <getData entity="GetProduct" stepKey="arg3"> + <requiredEntity createDataKey="product"/> + </getData> + </before> + <after> + <deleteData createDataKey="product" stepKey="delete"/> + <actionGroup ref="AdminLogoutActionGroup" stepKey="logoutFromAdmin"/> + </after> + </test> +</tests> diff --git a/app/code/Magento/Catalog/Test/Mftf/Test/AdvanceCatalogSearchSimpleProductTest/AdvanceCatalogSearchSimpleProductByShortDescriptionTest.xml b/app/code/Magento/Catalog/Test/Mftf/Test/AdvanceCatalogSearchSimpleProductTest/AdvanceCatalogSearchSimpleProductByShortDescriptionTest.xml new file mode 100644 index 0000000000000..5431a19461f76 --- /dev/null +++ b/app/code/Magento/Catalog/Test/Mftf/Test/AdvanceCatalogSearchSimpleProductTest/AdvanceCatalogSearchSimpleProductByShortDescriptionTest.xml @@ -0,0 +1,27 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + /** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +--> +<tests xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/testSchema.xsd"> + <test name="AdvanceCatalogSearchSimpleProductByShortDescriptionTest"> + <annotations> + <features value="Catalog"/> + <stories value="Advanced Catalog Product Search for all product types"/> + <title value="Guest customer should be able to advance search simple product with product short description"/> + <description value="Guest customer should be able to advance search simple product with product short description"/> + <severity value="MAJOR"/> + <testCaseId value="MC-135"/> + <group value="Catalog"/> + </annotations> + <before> + <createData entity="ApiProductWithDescription" stepKey="product"/> + </before> + <after> + <deleteData createDataKey="product" stepKey="delete"/> + </after> + </test> +</tests> diff --git a/app/code/Magento/Catalog/Test/Mftf/Test/AdvanceCatalogSearchSimpleProductTest/AdvanceCatalogSearchSimpleProductBySkuTest.xml b/app/code/Magento/Catalog/Test/Mftf/Test/AdvanceCatalogSearchSimpleProductTest/AdvanceCatalogSearchSimpleProductBySkuTest.xml new file mode 100644 index 0000000000000..dc6409043f67f --- /dev/null +++ b/app/code/Magento/Catalog/Test/Mftf/Test/AdvanceCatalogSearchSimpleProductTest/AdvanceCatalogSearchSimpleProductBySkuTest.xml @@ -0,0 +1,27 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + /** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +--> +<tests xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/testSchema.xsd"> + <test name="AdvanceCatalogSearchSimpleProductBySkuTest"> + <annotations> + <features value="Catalog"/> + <stories value="Advanced Catalog Product Search for all product types"/> + <title value="Guest customer should be able to advance search simple product with product sku"/> + <description value="Guest customer should be able to advance search simple product with product sku"/> + <severity value="MAJOR"/> + <testCaseId value="MC-133"/> + <group value="Catalog"/> + </annotations> + <before> + <createData entity="ApiProductWithDescriptionAndUnderscoredSku" stepKey="product"/> + </before> + <after> + <deleteData createDataKey="product" stepKey="delete"/> + </after> + </test> +</tests> diff --git a/app/code/Magento/Catalog/Test/Mftf/Test/AdvanceCatalogSearchVirtualProductTest.xml b/app/code/Magento/Catalog/Test/Mftf/Test/AdvanceCatalogSearchVirtualProductTest.xml deleted file mode 100644 index 07b802637b2e7..0000000000000 --- a/app/code/Magento/Catalog/Test/Mftf/Test/AdvanceCatalogSearchVirtualProductTest.xml +++ /dev/null @@ -1,81 +0,0 @@ -<?xml version="1.0" encoding="UTF-8"?> -<!-- - /** - * Copyright © Magento, Inc. All rights reserved. - * See COPYING.txt for license details. - */ ---> - -<tests xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" - xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/testSchema.xsd"> - <test name="AdvanceCatalogSearchVirtualProductByNameTest" extends="AdvanceCatalogSearchSimpleProductByNameTest"> - <annotations> - <features value="Catalog"/> - <stories value="Advanced Catalog Product Search for all product types"/> - <title value="Guest customer should be able to advance search virtual product with product name"/> - <description value="Guest customer should be able to advance search virtual product with product name"/> - <severity value="MAJOR"/> - <testCaseId value="MC-137"/> - <group value="Catalog"/> - </annotations> - <before> - <createData entity="ApiVirtualProductWithDescription" stepKey="product"/> - </before> - </test> - <test name="AdvanceCatalogSearchVirtualProductBySkuTest" extends="AdvanceCatalogSearchSimpleProductBySkuTest"> - <annotations> - <features value="Catalog"/> - <stories value="Advanced Catalog Product Search for all product types"/> - <title value="Guest customer should be able to advance search virtual product with product sku"/> - <description value="Guest customer should be able to advance search virtual product with product sku"/> - <severity value="MAJOR"/> - <testCaseId value="MC-162"/> - <group value="Catalog"/> - </annotations> - <before> - <createData entity="ApiVirtualProductWithDescriptionAndUnderscoredSku" stepKey="product"/> - </before> - </test> - <test name="AdvanceCatalogSearchVirtualProductByDescriptionTest" extends="AdvanceCatalogSearchSimpleProductByDescriptionTest"> - <annotations> - <features value="Catalog"/> - <stories value="Advanced Catalog Product Search for all product types"/> - <title value="Guest customer should be able to advance search virtual product with product description"/> - <description value="Guest customer should be able to advance search virtual product with product description"/> - <severity value="MAJOR"/> - <testCaseId value="MC-163"/> - <group value="Catalog"/> - </annotations> - <before> - <createData entity="ApiVirtualProductWithDescription" stepKey="product"/> - </before> - </test> - <test name="AdvanceCatalogSearchVirtualProductByShortDescriptionTest" extends="AdvanceCatalogSearchSimpleProductByShortDescriptionTest"> - <annotations> - <features value="Catalog"/> - <stories value="Advanced Catalog Product Search for all product types"/> - <title value="Guest customer should be able to advance search virtual product with product short description"/> - <description value="Guest customer should be able to advance search virtual product with product short description"/> - <severity value="MAJOR"/> - <testCaseId value="MC-164"/> - <group value="Catalog"/> - </annotations> - <before> - <createData entity="ApiVirtualProductWithDescription" stepKey="product"/> - </before> - </test> - <test name="AdvanceCatalogSearchVirtualProductByPriceTest" extends="AdvanceCatalogSearchSimpleProductByPriceTest"> - <annotations> - <features value="Catalog"/> - <stories value="Advanced Catalog Product Search for all product types"/> - <title value="Guest customer should be able to advance search virtual product with product price"/> - <description value="Guest customer should be able to advance search virtual product with product price"/> - <severity value="MAJOR"/> - <testCaseId value="MC-165"/> - <group value="Catalog"/> - </annotations> - <before> - <createData entity="ApiVirtualProductWithDescription" stepKey="product"/> - </before> - </test> -</tests> diff --git a/app/code/Magento/Catalog/Test/Mftf/Test/AdvanceCatalogSearchVirtualProductTest/AdvanceCatalogSearchVirtualProductByDescriptionTest.xml b/app/code/Magento/Catalog/Test/Mftf/Test/AdvanceCatalogSearchVirtualProductTest/AdvanceCatalogSearchVirtualProductByDescriptionTest.xml new file mode 100644 index 0000000000000..64da7e8599d07 --- /dev/null +++ b/app/code/Magento/Catalog/Test/Mftf/Test/AdvanceCatalogSearchVirtualProductTest/AdvanceCatalogSearchVirtualProductByDescriptionTest.xml @@ -0,0 +1,24 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + /** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +--> +<tests xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/testSchema.xsd"> + <test name="AdvanceCatalogSearchVirtualProductByDescriptionTest" extends="AdvanceCatalogSearchSimpleProductByDescriptionTest"> + <annotations> + <features value="Catalog"/> + <stories value="Advanced Catalog Product Search for all product types"/> + <title value="Guest customer should be able to advance search virtual product with product description"/> + <description value="Guest customer should be able to advance search virtual product with product description"/> + <severity value="MAJOR"/> + <testCaseId value="MC-163"/> + <group value="Catalog"/> + </annotations> + <before> + <createData entity="ApiVirtualProductWithDescription" stepKey="product"/> + </before> + </test> +</tests> diff --git a/app/code/Magento/Catalog/Test/Mftf/Test/AdvanceCatalogSearchVirtualProductTest/AdvanceCatalogSearchVirtualProductByNameTest.xml b/app/code/Magento/Catalog/Test/Mftf/Test/AdvanceCatalogSearchVirtualProductTest/AdvanceCatalogSearchVirtualProductByNameTest.xml new file mode 100644 index 0000000000000..12056962bac23 --- /dev/null +++ b/app/code/Magento/Catalog/Test/Mftf/Test/AdvanceCatalogSearchVirtualProductTest/AdvanceCatalogSearchVirtualProductByNameTest.xml @@ -0,0 +1,24 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + /** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +--> +<tests xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/testSchema.xsd"> + <test name="AdvanceCatalogSearchVirtualProductByNameTest" extends="AdvanceCatalogSearchSimpleProductByNameTest"> + <annotations> + <features value="Catalog"/> + <stories value="Advanced Catalog Product Search for all product types"/> + <title value="Guest customer should be able to advance search virtual product with product name"/> + <description value="Guest customer should be able to advance search virtual product with product name"/> + <severity value="MAJOR"/> + <testCaseId value="MC-137"/> + <group value="Catalog"/> + </annotations> + <before> + <createData entity="ApiVirtualProductWithDescription" stepKey="product"/> + </before> + </test> +</tests> diff --git a/app/code/Magento/Catalog/Test/Mftf/Test/AdvanceCatalogSearchVirtualProductTest/AdvanceCatalogSearchVirtualProductByPriceTest.xml b/app/code/Magento/Catalog/Test/Mftf/Test/AdvanceCatalogSearchVirtualProductTest/AdvanceCatalogSearchVirtualProductByPriceTest.xml new file mode 100644 index 0000000000000..68a69644d3d7b --- /dev/null +++ b/app/code/Magento/Catalog/Test/Mftf/Test/AdvanceCatalogSearchVirtualProductTest/AdvanceCatalogSearchVirtualProductByPriceTest.xml @@ -0,0 +1,24 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + /** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +--> +<tests xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/testSchema.xsd"> + <test name="AdvanceCatalogSearchVirtualProductByPriceTest" extends="AdvanceCatalogSearchSimpleProductByPriceTest"> + <annotations> + <features value="Catalog"/> + <stories value="Advanced Catalog Product Search for all product types"/> + <title value="Guest customer should be able to advance search virtual product with product price"/> + <description value="Guest customer should be able to advance search virtual product with product price"/> + <severity value="MAJOR"/> + <testCaseId value="MC-165"/> + <group value="Catalog"/> + </annotations> + <before> + <createData entity="ApiVirtualProductWithDescription" stepKey="product"/> + </before> + </test> +</tests> diff --git a/app/code/Magento/Catalog/Test/Mftf/Test/AdvanceCatalogSearchVirtualProductTest/AdvanceCatalogSearchVirtualProductByShortDescriptionTest.xml b/app/code/Magento/Catalog/Test/Mftf/Test/AdvanceCatalogSearchVirtualProductTest/AdvanceCatalogSearchVirtualProductByShortDescriptionTest.xml new file mode 100644 index 0000000000000..f6cfb58bf71df --- /dev/null +++ b/app/code/Magento/Catalog/Test/Mftf/Test/AdvanceCatalogSearchVirtualProductTest/AdvanceCatalogSearchVirtualProductByShortDescriptionTest.xml @@ -0,0 +1,24 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + /** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +--> +<tests xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/testSchema.xsd"> + <test name="AdvanceCatalogSearchVirtualProductByShortDescriptionTest" extends="AdvanceCatalogSearchSimpleProductByShortDescriptionTest"> + <annotations> + <features value="Catalog"/> + <stories value="Advanced Catalog Product Search for all product types"/> + <title value="Guest customer should be able to advance search virtual product with product short description"/> + <description value="Guest customer should be able to advance search virtual product with product short description"/> + <severity value="MAJOR"/> + <testCaseId value="MC-164"/> + <group value="Catalog"/> + </annotations> + <before> + <createData entity="ApiVirtualProductWithDescription" stepKey="product"/> + </before> + </test> +</tests> diff --git a/app/code/Magento/Catalog/Test/Mftf/Test/AdvanceCatalogSearchVirtualProductTest/AdvanceCatalogSearchVirtualProductBySkuTest.xml b/app/code/Magento/Catalog/Test/Mftf/Test/AdvanceCatalogSearchVirtualProductTest/AdvanceCatalogSearchVirtualProductBySkuTest.xml new file mode 100644 index 0000000000000..132e82d49085e --- /dev/null +++ b/app/code/Magento/Catalog/Test/Mftf/Test/AdvanceCatalogSearchVirtualProductTest/AdvanceCatalogSearchVirtualProductBySkuTest.xml @@ -0,0 +1,24 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + /** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +--> +<tests xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/testSchema.xsd"> + <test name="AdvanceCatalogSearchVirtualProductBySkuTest" extends="AdvanceCatalogSearchSimpleProductBySkuTest"> + <annotations> + <features value="Catalog"/> + <stories value="Advanced Catalog Product Search for all product types"/> + <title value="Guest customer should be able to advance search virtual product with product sku"/> + <description value="Guest customer should be able to advance search virtual product with product sku"/> + <severity value="MAJOR"/> + <testCaseId value="MC-162"/> + <group value="Catalog"/> + </annotations> + <before> + <createData entity="ApiVirtualProductWithDescriptionAndUnderscoredSku" stepKey="product"/> + </before> + </test> +</tests> diff --git a/app/code/Magento/Catalog/Test/Mftf/Test/CheckTierPricingOfProductsTest.xml b/app/code/Magento/Catalog/Test/Mftf/Test/CheckTierPricingOfProductsTest.xml index 4e0e8d03f59d5..d89ffa0055d3b 100644 --- a/app/code/Magento/Catalog/Test/Mftf/Test/CheckTierPricingOfProductsTest.xml +++ b/app/code/Magento/Catalog/Test/Mftf/Test/CheckTierPricingOfProductsTest.xml @@ -35,7 +35,8 @@ </createData> <createData entity="Simple_US_Customer" stepKey="customer"/> <!--Login as admin--> - <actionGroup ref="LoginAsAdmin" stepKey="LoginAsAdmin"/> + <actionGroup ref="AdminLoginActionGroup" stepKey="LoginAsAdmin"/> + <createData entity="CustomerAccountSharingGlobal" stepKey="setConfigCustomerAccountToGlobal"/> </before> <!--Create website, Sore adn Store View--> @@ -328,7 +329,8 @@ <actionGroup ref="AdminDeleteWebsiteActionGroup" stepKey="DeleteWebsite"> <argument name="websiteName" value="secondWebsite"/> </actionGroup> - <actionGroup ref="logout" stepKey="logout"/> + <createData entity="CustomerAccountSharingDefault" stepKey="setConfigCustomerAccountDefault"/> + <actionGroup ref="AdminLogoutActionGroup" stepKey="logout"/> <!--Do reindex and flush cache--> <magentoCLI command="indexer:reindex" stepKey="reindex"/> diff --git a/app/code/Magento/Catalog/Test/Mftf/Test/ConfigurableOptionTextInputLengthValidationHint.xml b/app/code/Magento/Catalog/Test/Mftf/Test/ConfigurableOptionTextInputLengthValidationHint.xml index a4785e25d39bb..e50c124f7cfd9 100644 --- a/app/code/Magento/Catalog/Test/Mftf/Test/ConfigurableOptionTextInputLengthValidationHint.xml +++ b/app/code/Magento/Catalog/Test/Mftf/Test/ConfigurableOptionTextInputLengthValidationHint.xml @@ -14,7 +14,7 @@ <stories value="Customizable text option input-length validation hint changes dynamically"/> <title value="You should have a dynamic length validation hint when using text option has max char limit"/> <description value="You should have a dynamic length validation hint when using text option has max char limit"/> - <severity value="MINOR"/> + <severity value="BLOCKER"/> <testCaseId value="MAGETWO-92229"/> <group value="product"/> </annotations> @@ -26,7 +26,7 @@ <deleteData createDataKey="createPreReqCategory" stepKey="deletePreReqCategory"/> </after> - <actionGroup ref="LoginAsAdmin" stepKey="loginAsAdmin1"/> + <actionGroup ref="AdminLoginActionGroup" stepKey="loginAsAdmin1"/> <actionGroup ref="AdminCreateSimpleProductWithTextOptionCharLimitActionGroup" stepKey="fillProductFieldsInAdmin"> <argument name="category" value="$$createPreReqCategory$$"/> <argument name="simpleProduct" value="_defaultProduct"/> diff --git a/app/code/Magento/Catalog/Test/Mftf/Test/CreateProductAttributeEntityTest.xml b/app/code/Magento/Catalog/Test/Mftf/Test/CreateProductAttributeEntityTest.xml deleted file mode 100644 index 1e297586ecb65..0000000000000 --- a/app/code/Magento/Catalog/Test/Mftf/Test/CreateProductAttributeEntityTest.xml +++ /dev/null @@ -1,425 +0,0 @@ -<?xml version="1.0" encoding="UTF-8"?> -<!-- - /** - * Copyright © Magento, Inc. All rights reserved. - * See COPYING.txt for license details. - */ ---> - -<tests xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" - xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/testSchema.xsd"> - <test name="CreateProductAttributeEntityTextFieldTest"> - <annotations> - <features value="Catalog"/> - <stories value="Create Product Attributes"/> - <title value="Admin should be able to create a TextField product attribute"/> - <description value="Admin should be able to create a TextField product attribute"/> - <severity value="CRITICAL"/> - <testCaseId value="MC-10894"/> - <group value="Catalog"/> - <group value="mtf_migrated"/> - </annotations> - - <before> - <actionGroup ref="LoginAsAdmin" stepKey="loginAsAdmin"/> - </before> - <after> - <actionGroup ref="NavigateToEditProductAttributeActionGroup" stepKey="goToEditPage"> - <argument name="ProductAttribute" value="{{textProductAttribute.attribute_code}}"/> - </actionGroup> - <click stepKey="clickDelete" selector="{{AttributePropertiesSection.DeleteAttribute}}"/> - <click stepKey="clickOk" selector="{{AttributeDeleteModalSection.confirm}}"/> - <waitForPageLoad stepKey="waitForDeletion"/> - <actionGroup ref="logout" stepKey="logout"/> - </after> - - <!--Navigate to Stores > Attributes > Product.--> - <amOnPage url="{{AdminProductAttributeGridPage.url}}" stepKey="goToProductAttributes"/> - - <!--Create new Product Attribute as TextField, with code and default value.--> - <actionGroup ref="CreateProductAttributeWithTextFieldActionGroup" stepKey="createAttribute"> - <argument name="attribute" value="textProductAttribute"/> - </actionGroup> - - <!--Navigate to Product Attribute.--> - <actionGroup ref="NavigateToEditProductAttributeActionGroup" stepKey="goToEditPage"> - <argument name="ProductAttribute" value="{{textProductAttribute.attribute_code}}"/> - </actionGroup> - - <!--Perform appropriate assertions against textProductAttribute entity--> - <seeInField stepKey="assertLabel" selector="{{AttributePropertiesSection.DefaultLabel}}" userInput="{{textProductAttribute.attribute_code}}"/> - <seeOptionIsSelected stepKey="assertInputType" selector="{{AttributePropertiesSection.InputType}}" userInput="{{textProductAttribute.frontend_input}}"/> - <seeOptionIsSelected stepKey="assertRequired" selector="{{AttributePropertiesSection.ValueRequired}}" userInput="{{textProductAttribute.is_required_admin}}"/> - <seeInField stepKey="assertAttrCode" selector="{{AdvancedAttributePropertiesSection.AttributeCode}}" userInput="{{textProductAttribute.attribute_code}}"/> - <seeInField stepKey="assertDefaultValue" selector="{{AdvancedAttributePropertiesSection.DefaultValueText}}" userInput="{{textProductAttribute.default_value}}"/> - - <!--Go to New Product page, add Attribute and check values--> - <amOnPage url="{{AdminProductCreatePage.url('4', 'simple')}}" stepKey="goToCreateSimpleProductPage"/> - <actionGroup ref="AddProductAttributeInProductModalActionGroup" stepKey="addAttributeToProduct"> - <argument name="attributeCode" value="{{textProductAttribute.attribute_code}}"/> - </actionGroup> - <click stepKey="openAttributes" selector="{{AdminProductAttributesSection.sectionHeader}}"/> - <waitForElementVisible selector="{{AdminProductAttributesSection.attributeTextInputByCode(textProductAttribute.attribute_code)}}" stepKey="waitforLabel"/> - <seeInField stepKey="checkDefaultValue" selector="{{AdminProductAttributesSection.attributeTextInputByCode(textProductAttribute.attribute_code)}}" userInput="{{textProductAttribute.default_value}}"/> - <see stepKey="checkLabel" selector="{{AdminProductAttributesSection.attributeLabelByCode(textProductAttribute.attribute_code)}}" userInput="{{textProductAttribute.attribute_code}}"/> - </test> - - <test name="CreateProductAttributeEntityDateTest"> - <annotations> - <features value="Catalog"/> - <stories value="Create Product Attributes"/> - <title value="Admin should be able to create a Date product attribute"/> - <description value="Admin should be able to create a Date product attribute"/> - <severity value="CRITICAL"/> - <testCaseId value="MC-10895"/> - <group value="Catalog"/> - <skip> - <issueId value="MC-13817"/> - </skip> - <group value="mtf_migrated"/> - </annotations> - - <before> - <actionGroup ref="LoginAsAdmin" stepKey="loginAsAdmin"/> - </before> - <after> - <actionGroup ref="NavigateToEditProductAttributeActionGroup" stepKey="goToEditPage"> - <argument name="ProductAttribute" value="{{dateProductAttribute.attribute_code}}"/> - </actionGroup> - <click stepKey="clickDelete" selector="{{AttributePropertiesSection.DeleteAttribute}}"/> - <click stepKey="clickOk" selector="{{AttributeDeleteModalSection.confirm}}"/> - <waitForPageLoad stepKey="waitForDeletion"/> - <actionGroup ref="logout" stepKey="logout"/> - </after> - - <!--Generate date for use as default value, needs to be MM/d/YYYY --> - <generateDate date="now" format="m/j/Y" stepKey="generateDefaultDate"/> - - <!--Navigate to Stores > Attributes > Product.--> - <amOnPage url="{{AdminProductAttributeGridPage.url}}" stepKey="goToProductAttributes"/> - - <!--Create new Product Attribute as TextField, with code and default value.--> - <actionGroup ref="CreateProductAttributeWithDateFieldActionGroup" stepKey="createAttribute"> - <argument name="attribute" value="dateProductAttribute"/> - <argument name="date" value="{$generateDefaultDate}"/> - </actionGroup> - - <!--Navigate to Product Attribute.--> - <actionGroup ref="NavigateToEditProductAttributeActionGroup" stepKey="goToEditPage"> - <argument name="ProductAttribute" value="{{dateProductAttribute.attribute_code}}"/> - </actionGroup> - - <!--Perform appropriate assertions against textProductAttribute entity--> - <seeInField stepKey="assertLabel" selector="{{AttributePropertiesSection.DefaultLabel}}" userInput="{{dateProductAttribute.attribute_code}}"/> - <seeOptionIsSelected stepKey="assertInputType" selector="{{AttributePropertiesSection.InputType}}" userInput="{{dateProductAttribute.frontend_input}}"/> - <seeOptionIsSelected stepKey="assertRequired" selector="{{AttributePropertiesSection.ValueRequired}}" userInput="{{dateProductAttribute.is_required_admin}}"/> - <seeInField stepKey="assertAttrCode" selector="{{AdvancedAttributePropertiesSection.AttributeCode}}" userInput="{{dateProductAttribute.attribute_code}}"/> - <seeInField stepKey="assertDefaultValue" selector="{{AdvancedAttributePropertiesSection.DefaultValueDate}}" userInput="{$generateDefaultDate}"/> - - <!--Go to New Product page, add Attribute and check values--> - <amOnPage url="{{AdminProductCreatePage.url('4', 'simple')}}" stepKey="goToCreateSimpleProductPage"/> - <actionGroup ref="AddProductAttributeInProductModalActionGroup" stepKey="addAttributeToProduct"> - <argument name="attributeCode" value="{{dateProductAttribute.attribute_code}}"/> - </actionGroup> - <click stepKey="openAttributes" selector="{{AdminProductAttributesSection.sectionHeader}}"/> - <waitForElementVisible selector="{{AdminProductAttributesSection.attributeTextInputByCode(dateProductAttribute.attribute_code)}}" stepKey="waitforLabel"/> - <seeInField stepKey="checkDefaultValue" selector="{{AdminProductAttributesSection.attributeTextInputByCode(dateProductAttribute.attribute_code)}}" userInput="{$generateDefaultDate}"/> - <see stepKey="checkLabel" selector="{{AdminProductAttributesSection.attributeLabelByCode(dateProductAttribute.attribute_code)}}" userInput="{{dateProductAttribute.attribute_code}}"/> - </test> - - <test name="CreateProductAttributeEntityPriceTest"> - <annotations> - <features value="Catalog"/> - <stories value="Create Product Attributes"/> - <title value="Admin should be able to create a Price product attribute"/> - <description value="Admin should be able to create a Price product attribute"/> - <severity value="CRITICAL"/> - <testCaseId value="MC-10897"/> - <group value="Catalog"/> - <group value="mtf_migrated"/> - </annotations> - - <before> - <actionGroup ref="LoginAsAdmin" stepKey="loginAsAdmin"/> - </before> - <after> - <actionGroup ref="NavigateToEditProductAttributeActionGroup" stepKey="goToEditPage"> - <argument name="ProductAttribute" value="{{priceProductAttribute.attribute_code}}"/> - </actionGroup> - <click stepKey="clickDelete" selector="{{AttributePropertiesSection.DeleteAttribute}}"/> - <click stepKey="clickOk" selector="{{AttributeDeleteModalSection.confirm}}"/> - <waitForPageLoad stepKey="waitForDeletion"/> - <actionGroup ref="logout" stepKey="logout"/> - </after> - - <!--Navigate to Stores > Attributes > Product.--> - <amOnPage url="{{AdminProductAttributeGridPage.url}}" stepKey="goToProductAttributes"/> - - <!--Create new Product Attribute with Price--> - <actionGroup ref="CreateProductAttributeActionGroup" stepKey="createAttribute"> - <argument name="attribute" value="priceProductAttribute"/> - </actionGroup> - - <!--Navigate to Product Attribute.--> - <actionGroup ref="NavigateToEditProductAttributeActionGroup" stepKey="goToEditPage"> - <argument name="ProductAttribute" value="{{priceProductAttribute.attribute_code}}"/> - </actionGroup> - - <!--Perform appropriate assertions against priceProductAttribute entity--> - <seeInField stepKey="assertLabel" selector="{{AttributePropertiesSection.DefaultLabel}}" userInput="{{priceProductAttribute.attribute_code}}"/> - <seeOptionIsSelected stepKey="assertInputType" selector="{{AttributePropertiesSection.InputType}}" userInput="{{priceProductAttribute.frontend_input}}"/> - <seeOptionIsSelected stepKey="assertRequired" selector="{{AttributePropertiesSection.ValueRequired}}" userInput="{{priceProductAttribute.is_required_admin}}"/> - <seeInField stepKey="assertAttrCode" selector="{{AdvancedAttributePropertiesSection.AttributeCode}}" userInput="{{priceProductAttribute.attribute_code}}"/> - - <!--Go to New Product page, add Attribute and check values--> - <amOnPage url="{{AdminProductCreatePage.url('4', 'simple')}}" stepKey="goToCreateSimpleProductPage"/> - <actionGroup ref="AddProductAttributeInProductModalActionGroup" stepKey="addAttributeToProduct"> - <argument name="attributeCode" value="{{priceProductAttribute.attribute_code}}"/> - </actionGroup> - <click stepKey="openAttributes" selector="{{AdminProductAttributesSection.sectionHeader}}"/> - <waitForElementVisible selector="{{AdminProductAttributesSection.attributeTextInputByCode(priceProductAttribute.attribute_code)}}" stepKey="waitforLabel"/> - <see stepKey="checkLabel" selector="{{AdminProductAttributesSection.attributeLabelByCode(priceProductAttribute.attribute_code)}}" userInput="{{priceProductAttribute.attribute_code}}"/> - </test> - - <test name="CreateProductAttributeEntityDropdownTest"> - <annotations> - <features value="Catalog"/> - <stories value="Create Product Attributes"/> - <title value="Admin should be able to create a Dropdown product attribute"/> - <description value="Admin should be able to create a Dropdown product attribute"/> - <severity value="CRITICAL"/> - <testCaseId value="MC-10896"/> - <group value="Catalog"/> - <group value="mtf_migrated"/> - </annotations> - - <before> - <actionGroup ref="LoginAsAdmin" stepKey="loginAsAdmin"/> - </before> - <after> - <actionGroup ref="NavigateToEditProductAttributeActionGroup" stepKey="goToEditPage"> - <argument name="ProductAttribute" value="{{dropdownProductAttribute.attribute_code}}"/> - </actionGroup> - <click stepKey="clickDelete" selector="{{AttributePropertiesSection.DeleteAttribute}}"/> - <click stepKey="clickOk" selector="{{AttributeDeleteModalSection.confirm}}"/> - <waitForPageLoad stepKey="waitForDeletion"/> - <actionGroup ref="logout" stepKey="logout"/> - </after> - - <!--Navigate to Stores > Attributes > Product.--> - <amOnPage url="{{AdminProductAttributeGridPage.url}}" stepKey="goToProductAttributes"/> - - <!--Create new Product Attribute as TextField, with code and default value.--> - <actionGroup ref="CreateProductAttributeActionGroup" stepKey="createAttribute"> - <argument name="attribute" value="dropdownProductAttribute"/> - </actionGroup> - - <!--Navigate to Product Attribute, add Product Options and Save - 1--> - <actionGroup ref="NavigateToEditProductAttributeActionGroup" stepKey="goToEditPage1"> - <argument name="ProductAttribute" value="{{dropdownProductAttribute.attribute_code}}"/> - </actionGroup> - <actionGroup ref="CreateAttributeDropdownNthOptionActionGroup" stepKey="createOption1"> - <argument name="adminName" value="{{dropdownProductAttribute.option1_admin}}"/> - <argument name="frontName" value="{{dropdownProductAttribute.option1_frontend}}"/> - <argument name="row" value="1"/> - </actionGroup> - <actionGroup ref="CreateAttributeDropdownNthOptionActionGroup" stepKey="createOption2"> - <argument name="adminName" value="{{dropdownProductAttribute.option2_admin}}"/> - <argument name="frontName" value="{{dropdownProductAttribute.option2_frontend}}"/> - <argument name="row" value="2"/> - </actionGroup> - <actionGroup ref="CreateAttributeDropdownNthOptionAsDefaultActionGroup" stepKey="createOption3"> - <argument name="adminName" value="{{dropdownProductAttribute.option3_admin}}"/> - <argument name="frontName" value="{{dropdownProductAttribute.option3_frontend}}"/> - <argument name="row" value="3"/> - </actionGroup> - <click stepKey="saveAttribute" selector="{{AttributePropertiesSection.Save}}"/> - - <!--Perform appropriate assertions against dropdownProductAttribute entity--> - <actionGroup ref="NavigateToEditProductAttributeActionGroup" stepKey="goToEditPageForAssertions"> - <argument name="ProductAttribute" value="{{dropdownProductAttribute.attribute_code}}"/> - </actionGroup> - <seeInField stepKey="assertLabel" selector="{{AttributePropertiesSection.DefaultLabel}}" userInput="{{dropdownProductAttribute.attribute_code}}"/> - <seeOptionIsSelected stepKey="assertInputType" selector="{{AttributePropertiesSection.InputType}}" userInput="{{dropdownProductAttribute.frontend_input_admin}}"/> - <seeOptionIsSelected stepKey="assertRequired" selector="{{AttributePropertiesSection.ValueRequired}}" userInput="{{dropdownProductAttribute.is_required_admin}}"/> - <seeInField stepKey="assertAttrCode" selector="{{AdvancedAttributePropertiesSection.AttributeCode}}" userInput="{{dropdownProductAttribute.attribute_code}}"/> - - <!--Assert options are in order and with correct attributes--> - <seeInField stepKey="seeOption1Admin" selector="{{AttributePropertiesSection.dropdownNthOptionAdmin('1')}}" userInput="{{dropdownProductAttribute.option1_admin}}"/> - <seeInField stepKey="seeOption1StoreView" selector="{{AttributePropertiesSection.dropdownNthOptionDefaultStoreView('1')}}" userInput="{{dropdownProductAttribute.option1_frontend}}"/> - <dontSeeCheckboxIsChecked stepKey="dontSeeOption1Default" selector="{{AttributePropertiesSection.dropdownNthOptionIsDefault('1')}}"/> - <seeInField stepKey="seeOption2Admin" selector="{{AttributePropertiesSection.dropdownNthOptionAdmin('2')}}" userInput="{{dropdownProductAttribute.option2_admin}}"/> - <seeInField stepKey="seeOption2StoreView" selector="{{AttributePropertiesSection.dropdownNthOptionDefaultStoreView('2')}}" userInput="{{dropdownProductAttribute.option2_frontend}}"/> - <dontSeeCheckboxIsChecked stepKey="dontSeeOption2Default" selector="{{AttributePropertiesSection.dropdownNthOptionIsDefault('2')}}"/> - <seeInField stepKey="seeOption3Admin" selector="{{AttributePropertiesSection.dropdownNthOptionAdmin('3')}}" userInput="{{dropdownProductAttribute.option3_admin}}"/> - <seeInField stepKey="seeOption3StoreView" selector="{{AttributePropertiesSection.dropdownNthOptionDefaultStoreView('3')}}" userInput="{{dropdownProductAttribute.option3_frontend}}"/> - <seeCheckboxIsChecked stepKey="seeOption3Default" selector="{{AttributePropertiesSection.dropdownNthOptionIsDefault('3')}}"/> - - <!--Go to New Product page, add Attribute and check dropdown values--> - <amOnPage url="{{AdminProductCreatePage.url('4', 'simple')}}" stepKey="goToCreateSimpleProductPage"/> - <actionGroup ref="AddProductAttributeInProductModalActionGroup" stepKey="addAttributeToProduct"> - <argument name="attributeCode" value="{{dropdownProductAttribute.attribute_code}}"/> - </actionGroup> - <click stepKey="openAttributes" selector="{{AdminProductAttributesSection.sectionHeader}}"/> - <waitForElementVisible selector="{{AdminProductAttributesSection.attributeDropdownByCode(dropdownProductAttribute.attribute_code)}}" stepKey="waitforLabel"/> - <seeOptionIsSelected selector="{{AdminProductAttributesSection.attributeDropdownByCode(dropdownProductAttribute.attribute_code)}}" userInput="{{dropdownProductAttribute.option3_admin}}" stepKey="seeDefaultIsCorrect"/> - <see stepKey="seeOption1Available" selector="{{AdminProductAttributesSection.attributeDropdownByCode(dropdownProductAttribute.attribute_code)}}" userInput="{{dropdownProductAttribute.option1_admin}}"/> - <see stepKey="seeOption2Available" selector="{{AdminProductAttributesSection.attributeDropdownByCode(dropdownProductAttribute.attribute_code)}}" userInput="{{dropdownProductAttribute.option2_admin}}"/> - <see stepKey="seeOption3Available" selector="{{AdminProductAttributesSection.attributeDropdownByCode(dropdownProductAttribute.attribute_code)}}" userInput="{{dropdownProductAttribute.option3_admin}}"/> - </test> - - <test name="CreateProductAttributeEntityDropdownWithSingleQuoteTest"> - <annotations> - <features value="Catalog"/> - <stories value="Create Product Attributes"/> - <title value="Admin should be able to create a Dropdown product attribute containing a single quote"/> - <description value="Admin should be able to create a Dropdown product attribute containing a single quote"/> - <severity value="CRITICAL"/> - <testCaseId value="MC-10898"/> - <group value="Catalog"/> - <group value="mtf_migrated"/> - </annotations> - - <before> - <actionGroup ref="LoginAsAdmin" stepKey="loginAsAdmin"/> - </before> - <after> - <actionGroup ref="NavigateToEditProductAttributeActionGroup" stepKey="goToEditPage"> - <argument name="ProductAttribute" value="{{dropdownProductAttributeWithQuote.attribute_code}}"/> - </actionGroup> - <click stepKey="clickDelete" selector="{{AttributePropertiesSection.DeleteAttribute}}"/> - <click stepKey="clickOk" selector="{{AttributeDeleteModalSection.confirm}}"/> - <waitForPageLoad stepKey="waitForDeletion"/> - <actionGroup ref="logout" stepKey="logout"/> - </after> - - <!--Navigate to Stores > Attributes > Product.--> - <amOnPage url="{{AdminProductAttributeGridPage.url}}" stepKey="goToProductAttributes"/> - - <!--Create new Product Attribute as TextField, with code and default value.--> - <actionGroup ref="CreateProductAttributeActionGroup" stepKey="createAttribute"> - <argument name="attribute" value="dropdownProductAttributeWithQuote"/> - </actionGroup> - - <!--Navigate to Product Attribute, add Product Option and Save - 1--> - <actionGroup ref="NavigateToEditProductAttributeActionGroup" stepKey="goToEditPage1"> - <argument name="ProductAttribute" value="{{dropdownProductAttributeWithQuote.attribute_code}}"/> - </actionGroup> - <actionGroup ref="CreateAttributeDropdownNthOptionAsDefaultActionGroup" stepKey="createOption1"> - <argument name="adminName" value="{{dropdownProductAttributeWithQuote.option1_admin}}"/> - <argument name="frontName" value="{{dropdownProductAttributeWithQuote.option1_frontend}}"/> - <argument name="row" value="1"/> - </actionGroup> - <click stepKey="saveAttribute" selector="{{AttributePropertiesSection.Save}}"/> - - <!--Perform appropriate assertions against dropdownProductAttribute entity--> - <actionGroup ref="NavigateToEditProductAttributeActionGroup" stepKey="goToEditPageForAssertions"> - <argument name="ProductAttribute" value="{{dropdownProductAttributeWithQuote.attribute_code}}"/> - </actionGroup> - <seeInField stepKey="assertLabel" selector="{{AttributePropertiesSection.DefaultLabel}}" userInput="{{dropdownProductAttributeWithQuote.attribute_code}}"/> - <seeOptionIsSelected stepKey="assertInputType" selector="{{AttributePropertiesSection.InputType}}" userInput="{{dropdownProductAttributeWithQuote.frontend_input_admin}}"/> - <seeOptionIsSelected stepKey="assertRequired" selector="{{AttributePropertiesSection.ValueRequired}}" userInput="{{dropdownProductAttributeWithQuote.is_required_admin}}"/> - <seeInField stepKey="assertAttrCode" selector="{{AdvancedAttributePropertiesSection.AttributeCode}}" userInput="{{dropdownProductAttributeWithQuote.attribute_code}}"/> - - <!--Assert options are in order and with correct attributes--> - <seeInField stepKey="seeOption1Admin" selector="{{AttributePropertiesSection.dropdownNthOptionAdmin('1')}}" userInput="{{dropdownProductAttributeWithQuote.option1_admin}}"/> - <seeInField stepKey="seeOption1StoreView" selector="{{AttributePropertiesSection.dropdownNthOptionDefaultStoreView('1')}}" userInput="{{dropdownProductAttributeWithQuote.option1_frontend}}"/> - <seeCheckboxIsChecked stepKey="seeOption1Default" selector="{{AttributePropertiesSection.dropdownNthOptionIsDefault('1')}}"/> - - <!--Go to New Product page, add Attribute and check dropdown values--> - <amOnPage url="{{AdminProductCreatePage.url('4', 'simple')}}" stepKey="goToCreateSimpleProductPage"/> - <actionGroup ref="AddProductAttributeInProductModalActionGroup" stepKey="addAttributeToProduct"> - <argument name="attributeCode" value="{{dropdownProductAttributeWithQuote.attribute_code}}"/> - </actionGroup> - <click stepKey="openAttributes" selector="{{AdminProductAttributesSection.sectionHeader}}"/> - <waitForElementVisible selector="{{AdminProductAttributesSection.attributeDropdownByCode(dropdownProductAttributeWithQuote.attribute_code)}}" stepKey="waitforLabel"/> - <seeOptionIsSelected selector="{{AdminProductAttributesSection.attributeDropdownByCode(dropdownProductAttributeWithQuote.attribute_code)}}" userInput="{{dropdownProductAttributeWithQuote.option1_admin}}" stepKey="seeDefaultIsCorrect"/> - <see stepKey="seeOption1Available" selector="{{AdminProductAttributesSection.attributeDropdownByCode(dropdownProductAttributeWithQuote.attribute_code)}}" userInput="{{dropdownProductAttributeWithQuote.option1_admin}}"/> - </test> - - <test name="CreateProductAttributeEntityMultiSelectTest"> - <annotations> - <features value="Catalog"/> - <stories value="Create Product Attributes"/> - <title value="Admin should be able to create a MultiSelect product attribute"/> - <description value="Admin should be able to create a MultiSelect product attribute"/> - <severity value="CRITICAL"/> - <testCaseId value="MC-10888"/> - <group value="Catalog"/> - <group value="mtf_migrated"/> - </annotations> - <before> - <actionGroup ref="LoginAsAdmin" stepKey="loginAsAdmin"/> - </before> - <after> - <actionGroup ref="NavigateToEditProductAttributeActionGroup" stepKey="goToEditPage"> - <argument name="ProductAttribute" value="{{multiselectProductAttribute.attribute_code}}"/> - </actionGroup> - <click stepKey="clickDelete" selector="{{AttributePropertiesSection.DeleteAttribute}}"/> - <click stepKey="clickOk" selector="{{AttributeDeleteModalSection.confirm}}"/> - <waitForPageLoad stepKey="waitForDeletion"/> - <actionGroup ref="logout" stepKey="logout"/> - </after> - - <!--Navigate to Stores > Attributes > Product.--> - <amOnPage url="{{AdminProductAttributeGridPage.url}}" stepKey="goToProductAttributes"/> - - <!--Create new Product Attribute as TextField, with code and default value.--> - <actionGroup ref="CreateProductAttributeActionGroup" stepKey="createAttribute"> - <argument name="attribute" value="multiselectProductAttribute"/> - </actionGroup> - - <!--Navigate to Product Attribute, add Product Options and Save - 1--> - <actionGroup ref="NavigateToEditProductAttributeActionGroup" stepKey="goToEditPage1"> - <argument name="ProductAttribute" value="{{multiselectProductAttribute.attribute_code}}"/> - </actionGroup> - <actionGroup ref="CreateAttributeDropdownNthOptionActionGroup" stepKey="createOption1"> - <argument name="adminName" value="{{multiselectProductAttribute.option1_admin}}"/> - <argument name="frontName" value="{{multiselectProductAttribute.option1_frontend}}"/> - <argument name="row" value="1"/> - </actionGroup> - <actionGroup ref="CreateAttributeDropdownNthOptionActionGroup" stepKey="createOption2"> - <argument name="adminName" value="{{multiselectProductAttribute.option2_admin}}"/> - <argument name="frontName" value="{{multiselectProductAttribute.option2_frontend}}"/> - <argument name="row" value="2"/> - </actionGroup> - <actionGroup ref="CreateAttributeDropdownNthOptionAsDefaultActionGroup" stepKey="createOption3"> - <argument name="adminName" value="{{multiselectProductAttribute.option3_admin}}"/> - <argument name="frontName" value="{{multiselectProductAttribute.option3_frontend}}"/> - <argument name="row" value="3"/> - </actionGroup> - <click stepKey="saveAttribute" selector="{{AttributePropertiesSection.Save}}"/> - - <!--Perform appropriate assertions against multiselectProductAttribute entity--> - <actionGroup ref="NavigateToEditProductAttributeActionGroup" stepKey="goToEditPageForAssertions"> - <argument name="ProductAttribute" value="{{multiselectProductAttribute.attribute_code}}"/> - </actionGroup> - <seeInField stepKey="assertLabel" selector="{{AttributePropertiesSection.DefaultLabel}}" userInput="{{multiselectProductAttribute.attribute_code}}"/> - <seeOptionIsSelected stepKey="assertInputType" selector="{{AttributePropertiesSection.InputType}}" userInput="{{multiselectProductAttribute.frontend_input_admin}}"/> - <seeOptionIsSelected stepKey="assertRequired" selector="{{AttributePropertiesSection.ValueRequired}}" userInput="{{multiselectProductAttribute.is_required_admin}}"/> - <seeInField stepKey="assertAttrCode" selector="{{AdvancedAttributePropertiesSection.AttributeCode}}" userInput="{{multiselectProductAttribute.attribute_code}}"/> - - <!--Assert options are in order and with correct attributes--> - <seeInField stepKey="seeOption1Admin" selector="{{AttributePropertiesSection.dropdownNthOptionAdmin('1')}}" userInput="{{multiselectProductAttribute.option1_admin}}"/> - <seeInField stepKey="seeOption1StoreView" selector="{{AttributePropertiesSection.dropdownNthOptionDefaultStoreView('1')}}" userInput="{{multiselectProductAttribute.option1_frontend}}"/> - <dontSeeCheckboxIsChecked stepKey="dontSeeOption1Default" selector="{{AttributePropertiesSection.dropdownNthOptionIsDefault('1')}}"/> - <seeInField stepKey="seeOption2Admin" selector="{{AttributePropertiesSection.dropdownNthOptionAdmin('2')}}" userInput="{{multiselectProductAttribute.option2_admin}}"/> - <seeInField stepKey="seeOption2StoreView" selector="{{AttributePropertiesSection.dropdownNthOptionDefaultStoreView('2')}}" userInput="{{multiselectProductAttribute.option2_frontend}}"/> - <dontSeeCheckboxIsChecked stepKey="dontSeeOption2Default" selector="{{AttributePropertiesSection.dropdownNthOptionIsDefault('2')}}"/> - <seeInField stepKey="seeOption3Admin" selector="{{AttributePropertiesSection.dropdownNthOptionAdmin('3')}}" userInput="{{multiselectProductAttribute.option3_admin}}"/> - <seeInField stepKey="seeOption3StoreView" selector="{{AttributePropertiesSection.dropdownNthOptionDefaultStoreView('3')}}" userInput="{{multiselectProductAttribute.option3_frontend}}"/> - <seeCheckboxIsChecked stepKey="seeOption3Default" selector="{{AttributePropertiesSection.dropdownNthOptionIsDefault('3')}}"/> - - <!--Go to New Product page, add Attribute and check multiselect values--> - <amOnPage url="{{AdminProductCreatePage.url('4', 'simple')}}" stepKey="goToCreateSimpleProductPage"/> - <actionGroup ref="AddProductAttributeInProductModalActionGroup" stepKey="addAttributeToProduct"> - <argument name="attributeCode" value="{{multiselectProductAttribute.attribute_code}}"/> - </actionGroup> - <click stepKey="openAttributes" selector="{{AdminProductAttributesSection.sectionHeader}}"/> - <waitForElementVisible selector="{{AdminProductAttributesSection.attributeDropdownByCode(multiselectProductAttribute.attribute_code)}}" stepKey="waitforLabel"/> - <seeOptionIsSelected selector="{{AdminProductAttributesSection.attributeDropdownByCode(multiselectProductAttribute.attribute_code)}}" userInput="{{multiselectProductAttribute.option3_admin}}" stepKey="seeDefaultIsCorrect"/> - <see stepKey="seeOption1Available" selector="{{AdminProductAttributesSection.attributeDropdownByCode(multiselectProductAttribute.attribute_code)}}" userInput="{{multiselectProductAttribute.option1_admin}}"/> - <see stepKey="seeOption2Available" selector="{{AdminProductAttributesSection.attributeDropdownByCode(multiselectProductAttribute.attribute_code)}}" userInput="{{multiselectProductAttribute.option2_admin}}"/> - <see stepKey="seeOption3Available" selector="{{AdminProductAttributesSection.attributeDropdownByCode(multiselectProductAttribute.attribute_code)}}" userInput="{{multiselectProductAttribute.option3_admin}}"/> - </test> -</tests> diff --git a/app/code/Magento/Catalog/Test/Mftf/Test/CreateProductAttributeEntityTest/CreateProductAttributeEntityDateTest.xml b/app/code/Magento/Catalog/Test/Mftf/Test/CreateProductAttributeEntityTest/CreateProductAttributeEntityDateTest.xml new file mode 100644 index 0000000000000..437532b9baebf --- /dev/null +++ b/app/code/Magento/Catalog/Test/Mftf/Test/CreateProductAttributeEntityTest/CreateProductAttributeEntityDateTest.xml @@ -0,0 +1,72 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + /** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +--> +<tests xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/testSchema.xsd"> + <test name="CreateProductAttributeEntityDateTest"> + <annotations> + <features value="Catalog"/> + <stories value="Create Product Attributes"/> + <title value="Admin should be able to create a Date product attribute"/> + <description value="Admin should be able to create a Date product attribute"/> + <severity value="BLOCKER"/> + <testCaseId value="MC-10895"/> + <group value="Catalog"/> + <skip> + <issueId value="MC-13817"/> + </skip> + <group value="mtf_migrated"/> + </annotations> + + <before> + <actionGroup ref="AdminLoginActionGroup" stepKey="loginAsAdmin"/> + </before> + <after> + <actionGroup ref="NavigateToEditProductAttributeActionGroup" stepKey="goToEditPage"> + <argument name="ProductAttribute" value="{{dateProductAttribute.attribute_code}}"/> + </actionGroup> + <click stepKey="clickDelete" selector="{{AttributePropertiesSection.DeleteAttribute}}"/> + <click stepKey="clickOk" selector="{{AttributeDeleteModalSection.confirm}}"/> + <waitForPageLoad stepKey="waitForDeletion"/> + <actionGroup ref="AdminLogoutActionGroup" stepKey="logout"/> + </after> + + <!--Generate date for use as default value, needs to be MM/d/YYYY --> + <generateDate date="now" format="m/j/Y" stepKey="generateDefaultDate"/> + + <!--Navigate to Stores > Attributes > Product.--> + <amOnPage url="{{AdminProductAttributeGridPage.url}}" stepKey="goToProductAttributes"/> + + <!--Create new Product Attribute as TextField, with code and default value.--> + <actionGroup ref="CreateProductAttributeWithDateFieldActionGroup" stepKey="createAttribute"> + <argument name="attribute" value="dateProductAttribute"/> + <argument name="date" value="{$generateDefaultDate}"/> + </actionGroup> + + <!--Navigate to Product Attribute.--> + <actionGroup ref="NavigateToEditProductAttributeActionGroup" stepKey="goToEditPage"> + <argument name="ProductAttribute" value="{{dateProductAttribute.attribute_code}}"/> + </actionGroup> + + <!--Perform appropriate assertions against textProductAttribute entity--> + <seeInField stepKey="assertLabel" selector="{{AttributePropertiesSection.DefaultLabel}}" userInput="{{dateProductAttribute.attribute_code}}"/> + <seeOptionIsSelected stepKey="assertInputType" selector="{{AttributePropertiesSection.InputType}}" userInput="{{dateProductAttribute.frontend_input}}"/> + <seeOptionIsSelected stepKey="assertRequired" selector="{{AttributePropertiesSection.ValueRequired}}" userInput="{{dateProductAttribute.is_required_admin}}"/> + <seeInField stepKey="assertAttrCode" selector="{{AdvancedAttributePropertiesSection.AttributeCode}}" userInput="{{dateProductAttribute.attribute_code}}"/> + <seeInField stepKey="assertDefaultValue" selector="{{AdvancedAttributePropertiesSection.DefaultValueDate}}" userInput="{$generateDefaultDate}"/> + + <!--Go to New Product page, add Attribute and check values--> + <amOnPage url="{{AdminProductCreatePage.url('4', 'simple')}}" stepKey="goToCreateSimpleProductPage"/> + <actionGroup ref="AddProductAttributeInProductModalActionGroup" stepKey="addAttributeToProduct"> + <argument name="attributeCode" value="{{dateProductAttribute.attribute_code}}"/> + </actionGroup> + <click stepKey="openAttributes" selector="{{AdminProductAttributesSection.sectionHeader}}"/> + <waitForElementVisible selector="{{AdminProductAttributesSection.attributeTextInputByCode(dateProductAttribute.attribute_code)}}" stepKey="waitforLabel"/> + <seeInField stepKey="checkDefaultValue" selector="{{AdminProductAttributesSection.attributeTextInputByCode(dateProductAttribute.attribute_code)}}" userInput="{$generateDefaultDate}"/> + <see stepKey="checkLabel" selector="{{AdminProductAttributesSection.attributeLabelByCode(dateProductAttribute.attribute_code)}}" userInput="{{dateProductAttribute.attribute_code}}"/> + </test> +</tests> diff --git a/app/code/Magento/Catalog/Test/Mftf/Test/CreateProductAttributeEntityTest/CreateProductAttributeEntityDropdownTest.xml b/app/code/Magento/Catalog/Test/Mftf/Test/CreateProductAttributeEntityTest/CreateProductAttributeEntityDropdownTest.xml new file mode 100644 index 0000000000000..580a5bd4939bb --- /dev/null +++ b/app/code/Magento/Catalog/Test/Mftf/Test/CreateProductAttributeEntityTest/CreateProductAttributeEntityDropdownTest.xml @@ -0,0 +1,96 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + /** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +--> +<tests xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/testSchema.xsd"> + <test name="CreateProductAttributeEntityDropdownTest"> + <annotations> + <features value="Catalog"/> + <stories value="Create Product Attributes"/> + <title value="Admin should be able to create a Dropdown product attribute"/> + <description value="Admin should be able to create a Dropdown product attribute"/> + <severity value="BLOCKER"/> + <testCaseId value="MC-10896"/> + <group value="Catalog"/> + <group value="mtf_migrated"/> + </annotations> + + <before> + <actionGroup ref="AdminLoginActionGroup" stepKey="loginAsAdmin"/> + </before> + <after> + <actionGroup ref="NavigateToEditProductAttributeActionGroup" stepKey="goToEditPage"> + <argument name="ProductAttribute" value="{{dropdownProductAttribute.attribute_code}}"/> + </actionGroup> + <click stepKey="clickDelete" selector="{{AttributePropertiesSection.DeleteAttribute}}"/> + <click stepKey="clickOk" selector="{{AttributeDeleteModalSection.confirm}}"/> + <waitForPageLoad stepKey="waitForDeletion"/> + <actionGroup ref="AdminLogoutActionGroup" stepKey="logout"/> + </after> + + <!--Navigate to Stores > Attributes > Product.--> + <amOnPage url="{{AdminProductAttributeGridPage.url}}" stepKey="goToProductAttributes"/> + + <!--Create new Product Attribute as TextField, with code and default value.--> + <actionGroup ref="CreateProductAttributeActionGroup" stepKey="createAttribute"> + <argument name="attribute" value="dropdownProductAttribute"/> + </actionGroup> + + <!--Navigate to Product Attribute, add Product Options and Save - 1--> + <actionGroup ref="NavigateToEditProductAttributeActionGroup" stepKey="goToEditPage1"> + <argument name="ProductAttribute" value="{{dropdownProductAttribute.attribute_code}}"/> + </actionGroup> + <actionGroup ref="CreateAttributeDropdownNthOptionActionGroup" stepKey="createOption1"> + <argument name="adminName" value="{{dropdownProductAttribute.option1_admin}}"/> + <argument name="frontName" value="{{dropdownProductAttribute.option1_frontend}}"/> + <argument name="row" value="1"/> + </actionGroup> + <actionGroup ref="CreateAttributeDropdownNthOptionActionGroup" stepKey="createOption2"> + <argument name="adminName" value="{{dropdownProductAttribute.option2_admin}}"/> + <argument name="frontName" value="{{dropdownProductAttribute.option2_frontend}}"/> + <argument name="row" value="2"/> + </actionGroup> + <actionGroup ref="CreateAttributeDropdownNthOptionAsDefaultActionGroup" stepKey="createOption3"> + <argument name="adminName" value="{{dropdownProductAttribute.option3_admin}}"/> + <argument name="frontName" value="{{dropdownProductAttribute.option3_frontend}}"/> + <argument name="row" value="3"/> + </actionGroup> + <click stepKey="saveAttribute" selector="{{AttributePropertiesSection.Save}}"/> + + <!--Perform appropriate assertions against dropdownProductAttribute entity--> + <actionGroup ref="NavigateToEditProductAttributeActionGroup" stepKey="goToEditPageForAssertions"> + <argument name="ProductAttribute" value="{{dropdownProductAttribute.attribute_code}}"/> + </actionGroup> + <seeInField stepKey="assertLabel" selector="{{AttributePropertiesSection.DefaultLabel}}" userInput="{{dropdownProductAttribute.attribute_code}}"/> + <seeOptionIsSelected stepKey="assertInputType" selector="{{AttributePropertiesSection.InputType}}" userInput="{{dropdownProductAttribute.frontend_input_admin}}"/> + <seeOptionIsSelected stepKey="assertRequired" selector="{{AttributePropertiesSection.ValueRequired}}" userInput="{{dropdownProductAttribute.is_required_admin}}"/> + <seeInField stepKey="assertAttrCode" selector="{{AdvancedAttributePropertiesSection.AttributeCode}}" userInput="{{dropdownProductAttribute.attribute_code}}"/> + + <!--Assert options are in order and with correct attributes--> + <seeInField stepKey="seeOption1Admin" selector="{{AttributePropertiesSection.dropdownNthOptionAdmin('1')}}" userInput="{{dropdownProductAttribute.option1_admin}}"/> + <seeInField stepKey="seeOption1StoreView" selector="{{AttributePropertiesSection.dropdownNthOptionDefaultStoreView('1')}}" userInput="{{dropdownProductAttribute.option1_frontend}}"/> + <dontSeeCheckboxIsChecked stepKey="dontSeeOption1Default" selector="{{AttributePropertiesSection.dropdownNthOptionIsDefault('1')}}"/> + <seeInField stepKey="seeOption2Admin" selector="{{AttributePropertiesSection.dropdownNthOptionAdmin('2')}}" userInput="{{dropdownProductAttribute.option2_admin}}"/> + <seeInField stepKey="seeOption2StoreView" selector="{{AttributePropertiesSection.dropdownNthOptionDefaultStoreView('2')}}" userInput="{{dropdownProductAttribute.option2_frontend}}"/> + <dontSeeCheckboxIsChecked stepKey="dontSeeOption2Default" selector="{{AttributePropertiesSection.dropdownNthOptionIsDefault('2')}}"/> + <seeInField stepKey="seeOption3Admin" selector="{{AttributePropertiesSection.dropdownNthOptionAdmin('3')}}" userInput="{{dropdownProductAttribute.option3_admin}}"/> + <seeInField stepKey="seeOption3StoreView" selector="{{AttributePropertiesSection.dropdownNthOptionDefaultStoreView('3')}}" userInput="{{dropdownProductAttribute.option3_frontend}}"/> + <seeCheckboxIsChecked stepKey="seeOption3Default" selector="{{AttributePropertiesSection.dropdownNthOptionIsDefault('3')}}"/> + + <!--Go to New Product page, add Attribute and check dropdown values--> + <amOnPage url="{{AdminProductCreatePage.url('4', 'simple')}}" stepKey="goToCreateSimpleProductPage"/> + <actionGroup ref="AddProductAttributeInProductModalActionGroup" stepKey="addAttributeToProduct"> + <argument name="attributeCode" value="{{dropdownProductAttribute.attribute_code}}"/> + </actionGroup> + <click stepKey="openAttributes" selector="{{AdminProductAttributesSection.sectionHeader}}"/> + <waitForElementVisible selector="{{AdminProductAttributesSection.attributeDropdownByCode(dropdownProductAttribute.attribute_code)}}" stepKey="waitforLabel"/> + <seeOptionIsSelected selector="{{AdminProductAttributesSection.attributeDropdownByCode(dropdownProductAttribute.attribute_code)}}" userInput="{{dropdownProductAttribute.option3_admin}}" stepKey="seeDefaultIsCorrect"/> + <see stepKey="seeOption1Available" selector="{{AdminProductAttributesSection.attributeDropdownByCode(dropdownProductAttribute.attribute_code)}}" userInput="{{dropdownProductAttribute.option1_admin}}"/> + <see stepKey="seeOption2Available" selector="{{AdminProductAttributesSection.attributeDropdownByCode(dropdownProductAttribute.attribute_code)}}" userInput="{{dropdownProductAttribute.option2_admin}}"/> + <see stepKey="seeOption3Available" selector="{{AdminProductAttributesSection.attributeDropdownByCode(dropdownProductAttribute.attribute_code)}}" userInput="{{dropdownProductAttribute.option3_admin}}"/> + </test> +</tests> diff --git a/app/code/Magento/Catalog/Test/Mftf/Test/CreateProductAttributeEntityTest/CreateProductAttributeEntityDropdownWithSingleQuoteTest.xml b/app/code/Magento/Catalog/Test/Mftf/Test/CreateProductAttributeEntityTest/CreateProductAttributeEntityDropdownWithSingleQuoteTest.xml new file mode 100644 index 0000000000000..e24bf0d7b1115 --- /dev/null +++ b/app/code/Magento/Catalog/Test/Mftf/Test/CreateProductAttributeEntityTest/CreateProductAttributeEntityDropdownWithSingleQuoteTest.xml @@ -0,0 +1,78 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + /** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +--> +<tests xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/testSchema.xsd"> + <test name="CreateProductAttributeEntityDropdownWithSingleQuoteTest"> + <annotations> + <features value="Catalog"/> + <stories value="Create Product Attributes"/> + <title value="Admin should be able to create a Dropdown product attribute containing a single quote"/> + <description value="Admin should be able to create a Dropdown product attribute containing a single quote"/> + <severity value="BLOCKER"/> + <testCaseId value="MC-10898"/> + <group value="Catalog"/> + <group value="mtf_migrated"/> + </annotations> + + <before> + <actionGroup ref="AdminLoginActionGroup" stepKey="loginAsAdmin"/> + </before> + <after> + <actionGroup ref="NavigateToEditProductAttributeActionGroup" stepKey="goToEditPage"> + <argument name="ProductAttribute" value="{{dropdownProductAttributeWithQuote.attribute_code}}"/> + </actionGroup> + <click stepKey="clickDelete" selector="{{AttributePropertiesSection.DeleteAttribute}}"/> + <click stepKey="clickOk" selector="{{AttributeDeleteModalSection.confirm}}"/> + <waitForPageLoad stepKey="waitForDeletion"/> + <actionGroup ref="AdminLogoutActionGroup" stepKey="logout"/> + </after> + + <!--Navigate to Stores > Attributes > Product.--> + <amOnPage url="{{AdminProductAttributeGridPage.url}}" stepKey="goToProductAttributes"/> + + <!--Create new Product Attribute as TextField, with code and default value.--> + <actionGroup ref="CreateProductAttributeActionGroup" stepKey="createAttribute"> + <argument name="attribute" value="dropdownProductAttributeWithQuote"/> + </actionGroup> + + <!--Navigate to Product Attribute, add Product Option and Save - 1--> + <actionGroup ref="NavigateToEditProductAttributeActionGroup" stepKey="goToEditPage1"> + <argument name="ProductAttribute" value="{{dropdownProductAttributeWithQuote.attribute_code}}"/> + </actionGroup> + <actionGroup ref="CreateAttributeDropdownNthOptionAsDefaultActionGroup" stepKey="createOption1"> + <argument name="adminName" value="{{dropdownProductAttributeWithQuote.option1_admin}}"/> + <argument name="frontName" value="{{dropdownProductAttributeWithQuote.option1_frontend}}"/> + <argument name="row" value="1"/> + </actionGroup> + <click stepKey="saveAttribute" selector="{{AttributePropertiesSection.Save}}"/> + + <!--Perform appropriate assertions against dropdownProductAttribute entity--> + <actionGroup ref="NavigateToEditProductAttributeActionGroup" stepKey="goToEditPageForAssertions"> + <argument name="ProductAttribute" value="{{dropdownProductAttributeWithQuote.attribute_code}}"/> + </actionGroup> + <seeInField stepKey="assertLabel" selector="{{AttributePropertiesSection.DefaultLabel}}" userInput="{{dropdownProductAttributeWithQuote.attribute_code}}"/> + <seeOptionIsSelected stepKey="assertInputType" selector="{{AttributePropertiesSection.InputType}}" userInput="{{dropdownProductAttributeWithQuote.frontend_input_admin}}"/> + <seeOptionIsSelected stepKey="assertRequired" selector="{{AttributePropertiesSection.ValueRequired}}" userInput="{{dropdownProductAttributeWithQuote.is_required_admin}}"/> + <seeInField stepKey="assertAttrCode" selector="{{AdvancedAttributePropertiesSection.AttributeCode}}" userInput="{{dropdownProductAttributeWithQuote.attribute_code}}"/> + + <!--Assert options are in order and with correct attributes--> + <seeInField stepKey="seeOption1Admin" selector="{{AttributePropertiesSection.dropdownNthOptionAdmin('1')}}" userInput="{{dropdownProductAttributeWithQuote.option1_admin}}"/> + <seeInField stepKey="seeOption1StoreView" selector="{{AttributePropertiesSection.dropdownNthOptionDefaultStoreView('1')}}" userInput="{{dropdownProductAttributeWithQuote.option1_frontend}}"/> + <seeCheckboxIsChecked stepKey="seeOption1Default" selector="{{AttributePropertiesSection.dropdownNthOptionIsDefault('1')}}"/> + + <!--Go to New Product page, add Attribute and check dropdown values--> + <amOnPage url="{{AdminProductCreatePage.url('4', 'simple')}}" stepKey="goToCreateSimpleProductPage"/> + <actionGroup ref="AddProductAttributeInProductModalActionGroup" stepKey="addAttributeToProduct"> + <argument name="attributeCode" value="{{dropdownProductAttributeWithQuote.attribute_code}}"/> + </actionGroup> + <click stepKey="openAttributes" selector="{{AdminProductAttributesSection.sectionHeader}}"/> + <waitForElementVisible selector="{{AdminProductAttributesSection.attributeDropdownByCode(dropdownProductAttributeWithQuote.attribute_code)}}" stepKey="waitforLabel"/> + <seeOptionIsSelected selector="{{AdminProductAttributesSection.attributeDropdownByCode(dropdownProductAttributeWithQuote.attribute_code)}}" userInput="{{dropdownProductAttributeWithQuote.option1_admin}}" stepKey="seeDefaultIsCorrect"/> + <see stepKey="seeOption1Available" selector="{{AdminProductAttributesSection.attributeDropdownByCode(dropdownProductAttributeWithQuote.attribute_code)}}" userInput="{{dropdownProductAttributeWithQuote.option1_admin}}"/> + </test> +</tests> diff --git a/app/code/Magento/Catalog/Test/Mftf/Test/CreateProductAttributeEntityTest/CreateProductAttributeEntityMultiSelectTest.xml b/app/code/Magento/Catalog/Test/Mftf/Test/CreateProductAttributeEntityTest/CreateProductAttributeEntityMultiSelectTest.xml new file mode 100644 index 0000000000000..0a84d9af3c918 --- /dev/null +++ b/app/code/Magento/Catalog/Test/Mftf/Test/CreateProductAttributeEntityTest/CreateProductAttributeEntityMultiSelectTest.xml @@ -0,0 +1,95 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + /** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +--> +<tests xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/testSchema.xsd"> + <test name="CreateProductAttributeEntityMultiSelectTest"> + <annotations> + <features value="Catalog"/> + <stories value="Create Product Attributes"/> + <title value="Admin should be able to create a MultiSelect product attribute"/> + <description value="Admin should be able to create a MultiSelect product attribute"/> + <severity value="BLOCKER"/> + <testCaseId value="MC-10888"/> + <group value="Catalog"/> + <group value="mtf_migrated"/> + </annotations> + <before> + <actionGroup ref="AdminLoginActionGroup" stepKey="loginAsAdmin"/> + </before> + <after> + <actionGroup ref="NavigateToEditProductAttributeActionGroup" stepKey="goToEditPage"> + <argument name="ProductAttribute" value="{{multiselectProductAttribute.attribute_code}}"/> + </actionGroup> + <click stepKey="clickDelete" selector="{{AttributePropertiesSection.DeleteAttribute}}"/> + <click stepKey="clickOk" selector="{{AttributeDeleteModalSection.confirm}}"/> + <waitForPageLoad stepKey="waitForDeletion"/> + <actionGroup ref="AdminLogoutActionGroup" stepKey="logout"/> + </after> + + <!--Navigate to Stores > Attributes > Product.--> + <amOnPage url="{{AdminProductAttributeGridPage.url}}" stepKey="goToProductAttributes"/> + + <!--Create new Product Attribute as TextField, with code and default value.--> + <actionGroup ref="CreateProductAttributeActionGroup" stepKey="createAttribute"> + <argument name="attribute" value="multiselectProductAttribute"/> + </actionGroup> + + <!--Navigate to Product Attribute, add Product Options and Save - 1--> + <actionGroup ref="NavigateToEditProductAttributeActionGroup" stepKey="goToEditPage1"> + <argument name="ProductAttribute" value="{{multiselectProductAttribute.attribute_code}}"/> + </actionGroup> + <actionGroup ref="CreateAttributeDropdownNthOptionActionGroup" stepKey="createOption1"> + <argument name="adminName" value="{{multiselectProductAttribute.option1_admin}}"/> + <argument name="frontName" value="{{multiselectProductAttribute.option1_frontend}}"/> + <argument name="row" value="1"/> + </actionGroup> + <actionGroup ref="CreateAttributeDropdownNthOptionActionGroup" stepKey="createOption2"> + <argument name="adminName" value="{{multiselectProductAttribute.option2_admin}}"/> + <argument name="frontName" value="{{multiselectProductAttribute.option2_frontend}}"/> + <argument name="row" value="2"/> + </actionGroup> + <actionGroup ref="CreateAttributeDropdownNthOptionAsDefaultActionGroup" stepKey="createOption3"> + <argument name="adminName" value="{{multiselectProductAttribute.option3_admin}}"/> + <argument name="frontName" value="{{multiselectProductAttribute.option3_frontend}}"/> + <argument name="row" value="3"/> + </actionGroup> + <click stepKey="saveAttribute" selector="{{AttributePropertiesSection.Save}}"/> + + <!--Perform appropriate assertions against multiselectProductAttribute entity--> + <actionGroup ref="NavigateToEditProductAttributeActionGroup" stepKey="goToEditPageForAssertions"> + <argument name="ProductAttribute" value="{{multiselectProductAttribute.attribute_code}}"/> + </actionGroup> + <seeInField stepKey="assertLabel" selector="{{AttributePropertiesSection.DefaultLabel}}" userInput="{{multiselectProductAttribute.attribute_code}}"/> + <seeOptionIsSelected stepKey="assertInputType" selector="{{AttributePropertiesSection.InputType}}" userInput="{{multiselectProductAttribute.frontend_input_admin}}"/> + <seeOptionIsSelected stepKey="assertRequired" selector="{{AttributePropertiesSection.ValueRequired}}" userInput="{{multiselectProductAttribute.is_required_admin}}"/> + <seeInField stepKey="assertAttrCode" selector="{{AdvancedAttributePropertiesSection.AttributeCode}}" userInput="{{multiselectProductAttribute.attribute_code}}"/> + + <!--Assert options are in order and with correct attributes--> + <seeInField stepKey="seeOption1Admin" selector="{{AttributePropertiesSection.dropdownNthOptionAdmin('1')}}" userInput="{{multiselectProductAttribute.option1_admin}}"/> + <seeInField stepKey="seeOption1StoreView" selector="{{AttributePropertiesSection.dropdownNthOptionDefaultStoreView('1')}}" userInput="{{multiselectProductAttribute.option1_frontend}}"/> + <dontSeeCheckboxIsChecked stepKey="dontSeeOption1Default" selector="{{AttributePropertiesSection.dropdownNthOptionIsDefault('1')}}"/> + <seeInField stepKey="seeOption2Admin" selector="{{AttributePropertiesSection.dropdownNthOptionAdmin('2')}}" userInput="{{multiselectProductAttribute.option2_admin}}"/> + <seeInField stepKey="seeOption2StoreView" selector="{{AttributePropertiesSection.dropdownNthOptionDefaultStoreView('2')}}" userInput="{{multiselectProductAttribute.option2_frontend}}"/> + <dontSeeCheckboxIsChecked stepKey="dontSeeOption2Default" selector="{{AttributePropertiesSection.dropdownNthOptionIsDefault('2')}}"/> + <seeInField stepKey="seeOption3Admin" selector="{{AttributePropertiesSection.dropdownNthOptionAdmin('3')}}" userInput="{{multiselectProductAttribute.option3_admin}}"/> + <seeInField stepKey="seeOption3StoreView" selector="{{AttributePropertiesSection.dropdownNthOptionDefaultStoreView('3')}}" userInput="{{multiselectProductAttribute.option3_frontend}}"/> + <seeCheckboxIsChecked stepKey="seeOption3Default" selector="{{AttributePropertiesSection.dropdownNthOptionIsDefault('3')}}"/> + + <!--Go to New Product page, add Attribute and check multiselect values--> + <amOnPage url="{{AdminProductCreatePage.url('4', 'simple')}}" stepKey="goToCreateSimpleProductPage"/> + <actionGroup ref="AddProductAttributeInProductModalActionGroup" stepKey="addAttributeToProduct"> + <argument name="attributeCode" value="{{multiselectProductAttribute.attribute_code}}"/> + </actionGroup> + <click stepKey="openAttributes" selector="{{AdminProductAttributesSection.sectionHeader}}"/> + <waitForElementVisible selector="{{AdminProductAttributesSection.attributeDropdownByCode(multiselectProductAttribute.attribute_code)}}" stepKey="waitforLabel"/> + <seeOptionIsSelected selector="{{AdminProductAttributesSection.attributeDropdownByCode(multiselectProductAttribute.attribute_code)}}" userInput="{{multiselectProductAttribute.option3_admin}}" stepKey="seeDefaultIsCorrect"/> + <see stepKey="seeOption1Available" selector="{{AdminProductAttributesSection.attributeDropdownByCode(multiselectProductAttribute.attribute_code)}}" userInput="{{multiselectProductAttribute.option1_admin}}"/> + <see stepKey="seeOption2Available" selector="{{AdminProductAttributesSection.attributeDropdownByCode(multiselectProductAttribute.attribute_code)}}" userInput="{{multiselectProductAttribute.option2_admin}}"/> + <see stepKey="seeOption3Available" selector="{{AdminProductAttributesSection.attributeDropdownByCode(multiselectProductAttribute.attribute_code)}}" userInput="{{multiselectProductAttribute.option3_admin}}"/> + </test> +</tests> diff --git a/app/code/Magento/Catalog/Test/Mftf/Test/CreateProductAttributeEntityTest/CreateProductAttributeEntityPriceTest.xml b/app/code/Magento/Catalog/Test/Mftf/Test/CreateProductAttributeEntityTest/CreateProductAttributeEntityPriceTest.xml new file mode 100644 index 0000000000000..97eff20b2d560 --- /dev/null +++ b/app/code/Magento/Catalog/Test/Mftf/Test/CreateProductAttributeEntityTest/CreateProductAttributeEntityPriceTest.xml @@ -0,0 +1,63 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + /** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +--> +<tests xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/testSchema.xsd"> + <test name="CreateProductAttributeEntityPriceTest"> + <annotations> + <features value="Catalog"/> + <stories value="Create Product Attributes"/> + <title value="Admin should be able to create a Price product attribute"/> + <description value="Admin should be able to create a Price product attribute"/> + <severity value="BLOCKER"/> + <testCaseId value="MC-10897"/> + <group value="Catalog"/> + <group value="mtf_migrated"/> + </annotations> + + <before> + <actionGroup ref="AdminLoginActionGroup" stepKey="loginAsAdmin"/> + </before> + <after> + <actionGroup ref="NavigateToEditProductAttributeActionGroup" stepKey="goToEditPage"> + <argument name="ProductAttribute" value="{{priceProductAttribute.attribute_code}}"/> + </actionGroup> + <click stepKey="clickDelete" selector="{{AttributePropertiesSection.DeleteAttribute}}"/> + <click stepKey="clickOk" selector="{{AttributeDeleteModalSection.confirm}}"/> + <waitForPageLoad stepKey="waitForDeletion"/> + <actionGroup ref="AdminLogoutActionGroup" stepKey="logout"/> + </after> + + <!--Navigate to Stores > Attributes > Product.--> + <amOnPage url="{{AdminProductAttributeGridPage.url}}" stepKey="goToProductAttributes"/> + + <!--Create new Product Attribute with Price--> + <actionGroup ref="CreateProductAttributeActionGroup" stepKey="createAttribute"> + <argument name="attribute" value="priceProductAttribute"/> + </actionGroup> + + <!--Navigate to Product Attribute.--> + <actionGroup ref="NavigateToEditProductAttributeActionGroup" stepKey="goToEditPage"> + <argument name="ProductAttribute" value="{{priceProductAttribute.attribute_code}}"/> + </actionGroup> + + <!--Perform appropriate assertions against priceProductAttribute entity--> + <seeInField stepKey="assertLabel" selector="{{AttributePropertiesSection.DefaultLabel}}" userInput="{{priceProductAttribute.attribute_code}}"/> + <seeOptionIsSelected stepKey="assertInputType" selector="{{AttributePropertiesSection.InputType}}" userInput="{{priceProductAttribute.frontend_input}}"/> + <seeOptionIsSelected stepKey="assertRequired" selector="{{AttributePropertiesSection.ValueRequired}}" userInput="{{priceProductAttribute.is_required_admin}}"/> + <seeInField stepKey="assertAttrCode" selector="{{AdvancedAttributePropertiesSection.AttributeCode}}" userInput="{{priceProductAttribute.attribute_code}}"/> + + <!--Go to New Product page, add Attribute and check values--> + <amOnPage url="{{AdminProductCreatePage.url('4', 'simple')}}" stepKey="goToCreateSimpleProductPage"/> + <actionGroup ref="AddProductAttributeInProductModalActionGroup" stepKey="addAttributeToProduct"> + <argument name="attributeCode" value="{{priceProductAttribute.attribute_code}}"/> + </actionGroup> + <click stepKey="openAttributes" selector="{{AdminProductAttributesSection.sectionHeader}}"/> + <waitForElementVisible selector="{{AdminProductAttributesSection.attributeTextInputByCode(priceProductAttribute.attribute_code)}}" stepKey="waitforLabel"/> + <see stepKey="checkLabel" selector="{{AdminProductAttributesSection.attributeLabelByCode(priceProductAttribute.attribute_code)}}" userInput="{{priceProductAttribute.attribute_code}}"/> + </test> +</tests> diff --git a/app/code/Magento/Catalog/Test/Mftf/Test/CreateProductAttributeEntityTest/CreateProductAttributeEntityTextFieldTest.xml b/app/code/Magento/Catalog/Test/Mftf/Test/CreateProductAttributeEntityTest/CreateProductAttributeEntityTextFieldTest.xml new file mode 100644 index 0000000000000..c0cff7b0b2bc9 --- /dev/null +++ b/app/code/Magento/Catalog/Test/Mftf/Test/CreateProductAttributeEntityTest/CreateProductAttributeEntityTextFieldTest.xml @@ -0,0 +1,65 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + /** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +--> +<tests xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/testSchema.xsd"> + <test name="CreateProductAttributeEntityTextFieldTest"> + <annotations> + <features value="Catalog"/> + <stories value="Create Product Attributes"/> + <title value="Admin should be able to create a TextField product attribute"/> + <description value="Admin should be able to create a TextField product attribute"/> + <severity value="BLOCKER"/> + <testCaseId value="MC-10894"/> + <group value="Catalog"/> + <group value="mtf_migrated"/> + </annotations> + + <before> + <actionGroup ref="AdminLoginActionGroup" stepKey="loginAsAdmin"/> + </before> + <after> + <actionGroup ref="NavigateToEditProductAttributeActionGroup" stepKey="goToEditPage"> + <argument name="ProductAttribute" value="{{textProductAttribute.attribute_code}}"/> + </actionGroup> + <click stepKey="clickDelete" selector="{{AttributePropertiesSection.DeleteAttribute}}"/> + <click stepKey="clickOk" selector="{{AttributeDeleteModalSection.confirm}}"/> + <waitForPageLoad stepKey="waitForDeletion"/> + <actionGroup ref="AdminLogoutActionGroup" stepKey="logout"/> + </after> + + <!--Navigate to Stores > Attributes > Product.--> + <amOnPage url="{{AdminProductAttributeGridPage.url}}" stepKey="goToProductAttributes"/> + + <!--Create new Product Attribute as TextField, with code and default value.--> + <actionGroup ref="CreateProductAttributeWithTextFieldActionGroup" stepKey="createAttribute"> + <argument name="attribute" value="textProductAttribute"/> + </actionGroup> + + <!--Navigate to Product Attribute.--> + <actionGroup ref="NavigateToEditProductAttributeActionGroup" stepKey="goToEditPage"> + <argument name="ProductAttribute" value="{{textProductAttribute.attribute_code}}"/> + </actionGroup> + + <!--Perform appropriate assertions against textProductAttribute entity--> + <seeInField stepKey="assertLabel" selector="{{AttributePropertiesSection.DefaultLabel}}" userInput="{{textProductAttribute.attribute_code}}"/> + <seeOptionIsSelected stepKey="assertInputType" selector="{{AttributePropertiesSection.InputType}}" userInput="{{textProductAttribute.frontend_input}}"/> + <seeOptionIsSelected stepKey="assertRequired" selector="{{AttributePropertiesSection.ValueRequired}}" userInput="{{textProductAttribute.is_required_admin}}"/> + <seeInField stepKey="assertAttrCode" selector="{{AdvancedAttributePropertiesSection.AttributeCode}}" userInput="{{textProductAttribute.attribute_code}}"/> + <seeInField stepKey="assertDefaultValue" selector="{{AdvancedAttributePropertiesSection.DefaultValueText}}" userInput="{{textProductAttribute.default_value}}"/> + + <!--Go to New Product page, add Attribute and check values--> + <amOnPage url="{{AdminProductCreatePage.url('4', 'simple')}}" stepKey="goToCreateSimpleProductPage"/> + <actionGroup ref="AddProductAttributeInProductModalActionGroup" stepKey="addAttributeToProduct"> + <argument name="attributeCode" value="{{textProductAttribute.attribute_code}}"/> + </actionGroup> + <click stepKey="openAttributes" selector="{{AdminProductAttributesSection.sectionHeader}}"/> + <waitForElementVisible selector="{{AdminProductAttributesSection.attributeTextInputByCode(textProductAttribute.attribute_code)}}" stepKey="waitforLabel"/> + <seeInField stepKey="checkDefaultValue" selector="{{AdminProductAttributesSection.attributeTextInputByCode(textProductAttribute.attribute_code)}}" userInput="{{textProductAttribute.default_value}}"/> + <see stepKey="checkLabel" selector="{{AdminProductAttributesSection.attributeLabelByCode(textProductAttribute.attribute_code)}}" userInput="{{textProductAttribute.attribute_code}}"/> + </test> +</tests> diff --git a/app/code/Magento/Catalog/Test/Mftf/Test/DeleteCategoriesTest.xml b/app/code/Magento/Catalog/Test/Mftf/Test/DeleteCategoriesTest.xml index 8609d50fecaf2..b0a3d02f31846 100644 --- a/app/code/Magento/Catalog/Test/Mftf/Test/DeleteCategoriesTest.xml +++ b/app/code/Magento/Catalog/Test/Mftf/Test/DeleteCategoriesTest.xml @@ -36,12 +36,12 @@ <createData entity="NewRootCategory" stepKey="createNewRootCategoryA"/> </before> <after> - <actionGroup ref="logout" stepKey="adminLogout"/> + <actionGroup ref="AdminLogoutActionGroup" stepKey="adminLogout"/> <deleteData createDataKey="createProduct1" stepKey="deleteProduct1"/> <deleteData createDataKey="createProduct2" stepKey="deleteProduct2"/> <deleteData createDataKey="createProduct3" stepKey="deleteProduct3"/> </after> - <actionGroup ref="LoginAsAdmin" stepKey="loginAsAdmin1"/> + <actionGroup ref="AdminLoginActionGroup" stepKey="loginAsAdmin1"/> <amOnPage url="{{AdminCategoryPage.url}}" stepKey="navigateToCategoryPage1"/> <waitForPageLoad time="30" stepKey="waitForPageCategoryLoadAfterNavigate"/> <click selector="{{AdminCategorySidebarTreeSection.categoryInTree($$createNewRootCategoryA.name$$)}}" stepKey="openNewRootCategory"/> @@ -89,8 +89,7 @@ <click selector="{{AdminCategoryModalSection.ok}}" stepKey="acceptModal1"/> <waitForElementVisible selector="{{AdminCategoryMessagesSection.SuccessMessage}}" stepKey="waitForPageReloadAfterDeleteDefaultCategory"/> <!-- Verify categories 1 and 3 their products. --> - <amOnPage url="{{StorefrontHomePage.url}}" stepKey="amOnHomePage"/> - <waitForPageLoad stepKey="homeWaitForPageLoad"/> + <actionGroup ref="StorefrontOpenHomePageActionGroup" stepKey="goToHomePage"/> <!-- @TODO: Uncomment commented below code after MQE-903 is fixed --> <!--<click selector="{{StorefrontHeaderSection.NavigationCategoryByName($$createCategoryC.name$$)}}" stepKey="browseClickCategoryC"/>--> @@ -132,8 +131,7 @@ <argument name="categoryEntity" value="$$createCategoryB$$"/> </actionGroup> <!-- Verify categories 1 and 3 are absent --> - <amOnPage url="{{StorefrontHomePage.url}}" stepKey="amOnHomePage1"/> - <waitForPageLoad stepKey="waitHomePageLoadAfterDeletingCategories"/> + <actionGroup ref="StorefrontOpenHomePageActionGroup" stepKey="goToHomePage1"/> <dontSee selector="{{StorefrontHeaderSection.NavigationCategoryByName($$createCategoryB.name$$)}}" stepKey="browseClickCategoryB"/> <dontSee selector="{{StorefrontHeaderSection.NavigationCategoryByName($$createCategoryC.name$$)}}" stepKey="browseClickCategoryC"/> <!-- Verify products 1-3 are available on storefront --> diff --git a/app/code/Magento/Catalog/Test/Mftf/Test/DeleteUsedInConfigurableProductAttributeTest.xml b/app/code/Magento/Catalog/Test/Mftf/Test/DeleteUsedInConfigurableProductAttributeTest.xml index 0daf8361ef9d1..d2b9fba0895ea 100644 --- a/app/code/Magento/Catalog/Test/Mftf/Test/DeleteUsedInConfigurableProductAttributeTest.xml +++ b/app/code/Magento/Catalog/Test/Mftf/Test/DeleteUsedInConfigurableProductAttributeTest.xml @@ -59,7 +59,7 @@ </createData> <!-- Login As Admin --> - <actionGroup ref="LoginAsAdmin" stepKey="loginAsAdmin"/> + <actionGroup ref="AdminLoginActionGroup" stepKey="loginAsAdmin"/> </before> <after> <!-- Delete the configurable product created in the before block --> @@ -72,7 +72,10 @@ <deleteData createDataKey="productAttributeHandle" stepKey="deleteProductAttribute"/> <!-- Logout --> - <actionGroup ref="logout" stepKey="logoutFromAdmin"/> + <actionGroup ref="AdminLogoutActionGroup" stepKey="logoutFromAdmin"/> + + <!-- Reindex invalidated indices after product attribute has been created/deleted --> + <magentoCron groups="index" stepKey="reindexInvalidatedIndices"/> </after> <!-- Go to Stores > Attributes > Products. Search and select the product attribute that was used to create the configurable product--> <actionGroup ref="OpenProductAttributeFromSearchResultInGridActionGroup" stepKey="openProductAttributeFromSearchResultInGrid"> diff --git a/app/code/Magento/Catalog/Test/Mftf/Test/DisplayRefreshCacheAfterChangingCategoryPageLayoutTest.xml b/app/code/Magento/Catalog/Test/Mftf/Test/DisplayRefreshCacheAfterChangingCategoryPageLayoutTest.xml index 1f71ec1e6a850..f6ede46578f33 100644 --- a/app/code/Magento/Catalog/Test/Mftf/Test/DisplayRefreshCacheAfterChangingCategoryPageLayoutTest.xml +++ b/app/code/Magento/Catalog/Test/Mftf/Test/DisplayRefreshCacheAfterChangingCategoryPageLayoutTest.xml @@ -23,14 +23,14 @@ <!-- Create category, flush cache and log in --> <comment userInput="Create category, flush cache and log in" stepKey="createCategoryAndLogIn"/> <createData entity="SimpleSubCategory" stepKey="simpleCategory"/> - <actionGroup ref="LoginAsAdmin" stepKey="logInAsAdmin"/> + <actionGroup ref="AdminLoginActionGroup" stepKey="logInAsAdmin"/> <magentoCLI command="cache:flush" stepKey="flushCache"/> </before> <after> <!-- Delete category and log out --> <comment userInput="Delete category and log out" stepKey="deleteCategoryAndLogOut"/> <deleteData createDataKey="simpleCategory" stepKey="deleteCategory"/> - <actionGroup ref="logout" stepKey="logOutFromAdmin"/> + <actionGroup ref="AdminLogoutActionGroup" stepKey="logOutFromAdmin"/> <magentoCLI command="cache:flush" stepKey="flushCache"/> </after> <!-- Navigate to category details page --> diff --git a/app/code/Magento/Catalog/Test/Mftf/Test/EndToEndB2CAdminTest.xml b/app/code/Magento/Catalog/Test/Mftf/Test/EndToEndB2CAdminTest.xml index 232321610eadc..151046f0474e0 100644 --- a/app/code/Magento/Catalog/Test/Mftf/Test/EndToEndB2CAdminTest.xml +++ b/app/code/Magento/Catalog/Test/Mftf/Test/EndToEndB2CAdminTest.xml @@ -21,11 +21,11 @@ </skip> </annotations> <after> - <actionGroup ref="logout" stepKey="logoutOfAdmin"/> + <actionGroup ref="AdminLogoutActionGroup" stepKey="logoutOfAdmin"/> </after> <!--Login to Admin Area--> - <actionGroup ref="LoginAsAdmin" stepKey="loginToAdminArea"/> + <actionGroup ref="AdminLoginActionGroup" stepKey="loginToAdminArea"/> <!--Admin creates product--> <!--Create Simple Product--> @@ -48,7 +48,10 @@ <argument name="product" value="SimpleProduct"/> </actionGroup> <grabAttributeFrom selector="{{AdminProductGridSection.productThumbnail('1')}}" userInput="src" stepKey="getSimpleProductThumbnail"/> - <assertNotRegExp expected="'/placeholder\/thumbnail\.jpg/'" actual="$getSimpleProductThumbnail" stepKey="simpleThumbnailIsNotDefault"/> + <assertNotRegExp stepKey="simpleThumbnailIsNotDefault"> + <actualResult type="const">$getSimpleProductThumbnail</actualResult> + <expectedResult type="const">'/placeholder\/thumbnail\.jpg/'</expectedResult> + </assertNotRegExp> <actionGroup ref="ViewProductInAdminGridActionGroup" stepKey="seeSimpleProductInGrid"> <argument name="product" value="SimpleProduct"/> </actionGroup> @@ -114,9 +117,18 @@ <grabTextFrom selector="{{AdminProductGridSection.productGridCell('1', 'Price')}}" stepKey="getMinimumPriceInGrid"/> <click selector="{{AdminProductGridSection.columnHeader('Price')}}" stepKey="clickPriceHeaderToSortDescForFilter"/> <grabTextFrom selector="{{AdminProductGridSection.productGridCell('1', 'Price')}}" stepKey="getMaximumPriceInGrid"/> - <assertRegExp expected="'/\$[0-9]{2}\.[0-9]{2}/'" actual="$getMinimumPriceInGrid" stepKey="assertMinimumPriceIsCorrect"/> - <assertRegExp expected="'/\$[0-9]{2}\.[0-9]{2}/'" actual="$getMaximumPriceInGrid" stepKey="assertMaximumPriceIsCorrect"/> - <assertLessThan expected="$getMaximumPriceInGrid" actual="$getMinimumPriceInGrid" stepKey="checkPriceSortCorrect"/> + <assertRegExp stepKey="assertMinimumPriceIsCorrect"> + <actualResult type="const">$getMinimumPriceInGrid</actualResult> + <expectedResult type="const">'/\$[0-9]{2}\.[0-9]{2}/'</expectedResult> + </assertRegExp> + <assertRegExp stepKey="assertMaximumPriceIsCorrect"> + <actualResult type="const">$getMaximumPriceInGrid</actualResult> + <expectedResult type="const">'/\$[0-9]{2}\.[0-9]{2}/'</expectedResult> + </assertRegExp> + <assertLessThan stepKey="checkPriceSortCorrect"> + <actualResult type="const">$getMinimumPriceInGrid</actualResult> + <expectedResult type="const">$getMaximumPriceInGrid</expectedResult> + </assertLessThan> <!--Filter by status--> <actionGroup ref="FilterProductGridByEnabledStatusActionGroup" stepKey="filterGridByEnabledProducts"/> <seeNumberOfElements selector="{{AdminProductGridSection.productGridRows}}" parameterArray="[1,20]" stepKey="seeEnabledProductsNotEmpty"/> @@ -135,23 +147,35 @@ <click selector="{{AdminProductGridSection.columnHeader('Price')}}" stepKey="clickPriceHeaderToSortAsc"/> <grabTextFrom selector="{{AdminProductGridSection.productGridCell('1', 'Price')}}" stepKey="getFirstPriceSortAsc"/> <grabTextFrom selector="{{AdminProductGridSection.productGridCell('2', 'Price')}}" stepKey="getSecondPriceSortAsc"/> - <assertLessThanOrEqual expected="$getSecondPriceSortAsc" actual="$getFirstPriceSortAsc" stepKey="checkPriceAscSortCorrect"/> + <assertLessThanOrEqual stepKey="checkPriceAscSortCorrect"> + <actualResult type="const">$getFirstPriceSortAsc</actualResult> + <expectedResult type="const">$getSecondPriceSortAsc</expectedResult> + </assertLessThanOrEqual> <!--Sort Descending--> <click selector="{{AdminProductGridSection.columnHeader('Price')}}" stepKey="clickPriceHeaderToSortDesc"/> <grabTextFrom selector="{{AdminProductGridSection.productGridCell('1', 'Price')}}" stepKey="getFirstPriceSortDesc"/> <grabTextFrom selector="{{AdminProductGridSection.productGridCell('2', 'Price')}}" stepKey="getSecondPriceSortDesc"/> - <assertGreaterThanOrEqual expected="$getSecondPriceSortDesc" actual="$getFirstPriceSortDesc" stepKey="checkPriceDescSortCorrect"/> + <assertGreaterThanOrEqual stepKey="checkPriceDescSortCorrect"> + <actualResult type="const">$getFirstPriceSortDesc</actualResult> + <expectedResult type="const">$getSecondPriceSortDesc</expectedResult> + </assertGreaterThanOrEqual> <!--By Product ID--> <actionGroup ref="ResetProductGridToDefaultViewActionGroup" stepKey="setProductGridToDefaultSortingId"/> <!--Sort Ascending--> <grabTextFrom selector="{{AdminProductGridSection.productGridCell('1', 'ID')}}" stepKey="getFirstProductIdSortAsc"/> <grabTextFrom selector="{{AdminProductGridSection.productGridCell('2', 'ID')}}" stepKey="getSecondProductIdSortAsc"/> - <assertLessThan expected="$getSecondProductIdSortAsc" actual="$getFirstProductIdSortAsc" stepKey="checkProductIdAscSortCorrect"/> + <assertLessThan stepKey="checkProductIdAscSortCorrect"> + <actualResult type="const">$getFirstProductIdSortAsc</actualResult> + <expectedResult type="const">$getSecondProductIdSortAsc</expectedResult> + </assertLessThan> <!--Sort Descending--> <click selector="{{AdminProductGridSection.columnHeader('ID')}}" stepKey="clickIdHeaderToSortDesc"/> <grabTextFrom selector="{{AdminProductGridSection.productGridCell('1', 'ID')}}" stepKey="getFirstProductIdSortDesc"/> <grabTextFrom selector="{{AdminProductGridSection.productGridCell('2', 'ID')}}" stepKey="getSecondProductIdSortDesc"/> - <assertGreaterThan expected="$getSecondProductIdSortDesc" actual="$getFirstProductIdSortDesc" stepKey="checkProductIdDescSortCorrect"/> + <assertGreaterThan stepKey="checkProductIdDescSortCorrect"> + <actualResult type="const">$getFirstProductIdSortDesc</actualResult> + <expectedResult type="const">$getSecondProductIdSortDesc</expectedResult> + </assertGreaterThan> <!--Adding column works--> <actionGroup ref="ResetProductGridToDefaultViewActionGroup" stepKey="setProductGridToDefaultColumns"/> diff --git a/app/code/Magento/Catalog/Test/Mftf/Test/EndToEndB2CGuestUserTest.xml b/app/code/Magento/Catalog/Test/Mftf/Test/EndToEndB2CGuestUserTest.xml deleted file mode 100644 index f7ebb090124d6..0000000000000 --- a/app/code/Magento/Catalog/Test/Mftf/Test/EndToEndB2CGuestUserTest.xml +++ /dev/null @@ -1,398 +0,0 @@ -<?xml version="1.0" encoding="UTF-8"?> -<!-- - /** - * Copyright © Magento, Inc. All rights reserved. - * See COPYING.txt for license details. - */ ---> - -<tests xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" - xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/testSchema.xsd"> - <test name="EndToEndB2CGuestUserTest"> - <annotations> - <features value="End to End scenarios"/> - <stories value="B2C guest user - MAGETWO-75411"/> - <group value="e2e"/> - <title value="You should be able to pass End to End B2C Guest User scenario"/> - <description value="User browses catalog, searches for product, adds product to cart, adds product to wishlist, compares products, uses coupon code and checks out."/> - <severity value="CRITICAL"/> - <testCaseId value="MAGETWO-87435"/> - <group value="SearchEngineElasticsearch"/> - </annotations> - <before> - <resetCookie userInput="PHPSESSID" stepKey="resetCookieForCart"/> - - <createData entity="ApiCategory" stepKey="createCategory"/> - - <createData entity="ApiSimpleProduct" stepKey="createSimpleProduct1"> - <requiredEntity createDataKey="createCategory"/> - </createData> - <createData entity="ApiProductAttributeMediaGalleryEntryTestImage" stepKey="createSimpleProduct1Image"> - <requiredEntity createDataKey="createSimpleProduct1"/> - </createData> - <createData entity="ApiProductAttributeMediaGalleryEntryMagentoLogo" stepKey="createSimpleProduct1Image1"> - <requiredEntity createDataKey="createSimpleProduct1"/> - </createData> - <updateData entity="ApiSimpleProductUpdateDescription" stepKey="updateSimpleProduct1" createDataKey="createSimpleProduct1"/> - - <createData entity="ApiSimpleProduct" stepKey="createSimpleProduct2"> - <requiredEntity createDataKey="createCategory"/> - </createData> - <createData entity="ApiProductAttributeMediaGalleryEntryTestImage" stepKey="createSimpleProduct2Image"> - <requiredEntity createDataKey="createSimpleProduct2"/> - </createData> - <updateData entity="ApiSimpleProductUpdateDescription" stepKey="updateSimpleProduct2" createDataKey="createSimpleProduct2"/> - </before> - <after> - <deleteData createDataKey="createCategory" stepKey="deleteCategory"/> - - <!-- @TODO: Uncomment once MQE-679 is fixed --> - <!--<deleteData createDataKey="createSimpleProduct1Image" stepKey="deleteSimpleProduct1Image"/>--> - <!-- @TODO: Uncomment once MQE-679 is fixed --> - <!--<deleteData createDataKey="createSimpleProduct1Image1" stepKey="deleteSimpleProduct1Image1"/>--> - <deleteData createDataKey="createSimpleProduct1" stepKey="deleteSimpleProduct1"/> - - <!-- @TODO: Uncomment once MQE-679 is fixed --> - <!--<deleteData createDataKey="createSimpleProduct2Image" stepKey="deleteSimpleProduct2Image"/>--> - <deleteData createDataKey="createSimpleProduct2" stepKey="deleteSimpleProduct2"/> - </after> - - <!--Re-index--> - <magentoCLI command="indexer:reindex" stepKey="reindex"/> - - <!-- Step 1: User browses catalog --> - <comment userInput="Start of browsing catalog" stepKey="startOfBrowsingCatalog" /> - <amOnPage url="{{StorefrontHomePage.url}}" stepKey="amOnHomePage"/> - <waitForPageLoad stepKey="homeWaitForPageLoad"/> - <waitForElementVisible selector="{{StorefrontPanelHeaderSection.WelcomeMessage}}" stepKey="homeWaitForWelcomeMessage"/> - <see userInput="Default welcome msg!" selector="{{StorefrontPanelHeaderSection.WelcomeMessage}}" stepKey="homeCheckWelcome"/> - - <!-- Open Category --> - <comment userInput="Open category" stepKey="commentOpenCategory" /> - <click selector="{{StorefrontHeaderSection.NavigationCategoryByName($$createCategory.name$$)}}" stepKey="browseClickCategory"/> - <actionGroup ref="StorefrontCheckCategoryActionGroup" stepKey="browseAssertCategory"> - <argument name="category" value="$$createCategory$$"/> - <argument name="productCount" value="3"/> - </actionGroup> - <!-- Check simple product 1 in category --> - <comment userInput="Check simple product 1 in category" stepKey="commentCheckSimpleProductInCategory" /> - <actionGroup ref="StorefrontCheckCategorySimpleProductActionGroup" stepKey="browseAssertCategoryProduct1"> - <argument name="product" value="$$createSimpleProduct1$$"/> - </actionGroup> - <!-- @TODO: Move Image check to action group after MQE-697 is fixed --> - <grabAttributeFrom selector="{{StorefrontCategoryProductSection.ProductImageByName($$createSimpleProduct1.name$$)}}" userInput="src" stepKey="browseGrabSimpleProduct1ImageSrc"/> - <assertNotRegExp expected="'/placeholder\/small_image\.jpg/'" actual="$browseGrabSimpleProduct1ImageSrc" stepKey="browseAssertSimpleProduct1ImageNotDefault"/> - <!-- Check simple product 2 in category --> - <comment userInput="Check simple product 2 in category" stepKey="commentCheckSimpleProduct2InCategory" /> - <actionGroup ref="StorefrontCheckCategorySimpleProductActionGroup" stepKey="browseAssertCategoryProduct2"> - <argument name="product" value="$$createSimpleProduct2$$"/> - </actionGroup> - <!-- @TODO: Move Image check to action group after MQE-697 is fixed --> - <grabAttributeFrom selector="{{StorefrontCategoryProductSection.ProductImageByName($$createSimpleProduct2.name$$)}}" userInput="src" stepKey="browseGrabSimpleProduct2ImageSrc"/> - <assertNotRegExp expected="'/placeholder\/small_image\.jpg/'" actual="$browseGrabSimpleProduct2ImageSrc" stepKey="browseAssertSimpleProduct2ImageNotDefault"/> - - <!-- View Simple Product 1 --> - <comment userInput="View simple product 1" stepKey="commentViewSimpleProduct1" after="browseAssertSimpleProduct2ImageNotDefault"/> - <click selector="{{StorefrontCategoryProductSection.ProductTitleByName($$createSimpleProduct1.name$$)}}" stepKey="browseClickCategorySimpleProduct1View" after="commentViewSimpleProduct1"/> - <waitForLoadingMaskToDisappear stepKey="waitForSimpleProduct1Viewloaded" /> - <actionGroup ref="StorefrontCheckSimpleProductActionGroup" stepKey="browseAssertProduct1Page"> - <argument name="product" value="$$createSimpleProduct1$$"/> - </actionGroup> - <!-- @TODO: Move Image check to action group after MQE-697 is fixed --> - <grabAttributeFrom selector="{{StorefrontProductInfoMainSection.productImage}}" userInput="src" stepKey="browseGrabSimpleProduct1PageImageSrc"/> - <assertNotRegExp expected="'/placeholder\/image\.jpg/'" actual="$browseGrabSimpleProduct1PageImageSrc" stepKey="browseAssertSimpleProduct1PageImageNotDefault"/> - - <!-- View Simple Product 2 --> - <comment userInput="View simple product 2" stepKey="commentViewSimpleProduct2" /> - <click selector="{{StorefrontHeaderSection.NavigationCategoryByName($$createCategory.name$$)}}" stepKey="clickCategory1"/> - <click selector="{{StorefrontCategoryProductSection.ProductTitleByName($$createSimpleProduct2.name$$)}}" stepKey="browseClickCategorySimpleProduct2View"/> - <waitForLoadingMaskToDisappear stepKey="waitForSimpleProduct2ViewLoaded" /> - <actionGroup ref="StorefrontCheckSimpleProductActionGroup" stepKey="browseAssertProduct2Page"> - <argument name="product" value="$$createSimpleProduct2$$"/> - </actionGroup> - <!-- @TODO: Move Image check to action group after MQE-697 is fixed --> - <grabAttributeFrom selector="{{StorefrontProductInfoMainSection.productImage}}" userInput="src" stepKey="browseGrabSimpleProduct2PageImageSrc"/> - <assertNotRegExp expected="'/placeholder\/image\.jpg/'" actual="$browseGrabSimpleProduct2PageImageSrc" stepKey="browseAssertSimpleProduct2PageImageNotDefault"/> - <comment userInput="End of browsing catalog" stepKey="endOfBrowsingCatalog" after="browseAssertSimpleProduct2PageImageNotDefault"/> - - <!-- Step 4: User compares products --> - <comment userInput="Start of comparing products" stepKey="startOfComparingProducts" after="endOfBrowsingCatalog"/> - <!-- Add Simple Product 1 to comparison --> - <comment userInput="Add simple product 1 to comparison" stepKey="commentAddSimpleProduct1ToComparison" /> - <click selector="{{StorefrontHeaderSection.NavigationCategoryByName($$createCategory.name$$)}}" stepKey="compareClickCategory" /> - <waitForLoadingMaskToDisappear stepKey="waitForCategoryloaded" /> - <actionGroup ref="StorefrontCheckCategoryActionGroup" stepKey="compareAssertCategory"> - <argument name="category" value="$$createCategory$$"/> - <argument name="productCount" value="3"/> - </actionGroup> - <actionGroup ref="StorefrontCheckCategorySimpleProductActionGroup" stepKey="compareAssertSimpleProduct1"> - <argument name="product" value="$$createSimpleProduct1$$"/> - </actionGroup> - <!-- @TODO: Move Image check to action group after MQE-697 is fixed --> - <grabAttributeFrom selector="{{StorefrontCategoryProductSection.ProductImageByName($$createSimpleProduct1.name$$)}}" userInput="src" stepKey="compareGrabSimpleProduct1ImageSrc"/> - <assertNotRegExp expected="'/placeholder\/small_image\.jpg/'" actual="$compareGrabSimpleProduct1ImageSrc" stepKey="compareAssertSimpleProduct1ImageNotDefault"/> - <click selector="{{StorefrontCategoryProductSection.ProductTitleByName($$createSimpleProduct1.name$$)}}" stepKey="compareClickSimpleProduct1"/> - <waitForLoadingMaskToDisappear stepKey="waitForCompareSimpleProduct1loaded" /> - <actionGroup ref="StorefrontCheckSimpleProductActionGroup" stepKey="compareAssertProduct1Page"> - <argument name="product" value="$$createSimpleProduct1$$"/> - </actionGroup> - <!-- @TODO: Move Image check to action group after MQE-697 is fixed --> - <grabAttributeFrom selector="{{StorefrontProductInfoMainSection.productImage}}" userInput="src" stepKey="compareGrabSimpleProduct1PageImageSrc"/> - <assertNotRegExp expected="'/placeholder\/image\.jpg/'" actual="$compareGrabSimpleProduct1PageImageSrc" stepKey="compareAssertSimpleProduct2PageImageNotDefault"/> - <actionGroup ref="StorefrontAddProductToCompareActionGroup" stepKey="compareAddSimpleProduct1ToCompare"> - <argument name="productVar" value="$$createSimpleProduct1$$"/> - </actionGroup> - - <!-- Add Simple Product 2 to comparison --> - <comment userInput="Add simple product 2 to comparison" stepKey="commentAddSimpleProduct2ToComparison" /> - <click selector="{{StorefrontHeaderSection.NavigationCategoryByName($$createCategory.name$$)}}" stepKey="compareClickCategory1"/> - <waitForLoadingMaskToDisappear stepKey="waitForCompareCategory1loaded" /> - <actionGroup ref="StorefrontCheckCategoryActionGroup" stepKey="compareAssertCategory1"> - <argument name="category" value="$$createCategory$$"/> - <argument name="productCount" value="3"/> - </actionGroup> - <actionGroup ref="StorefrontCheckCategorySimpleProductActionGroup" stepKey="compareAssertSimpleProduct2"> - <argument name="product" value="$$createSimpleProduct2$$"/> - </actionGroup> - <!-- @TODO: Move Image check to action group after MQE-697 is fixed --> - <grabAttributeFrom selector="{{StorefrontCategoryProductSection.ProductImageByName($$createSimpleProduct2.name$$)}}" userInput="src" stepKey="compareGrabSimpleProduct2ImageSrc"/> - <assertNotRegExp expected="'/placeholder\/small_image\.jpg/'" actual="$compareGrabSimpleProduct2ImageSrc" stepKey="compareAssertSimpleProduct2ImageNotDefault"/> - <actionGroup ref="StorefrontAddCategoryProductToCompareActionGroup" stepKey="compareAddSimpleProduct2ToCompare"> - <argument name="productVar" value="$$createSimpleProduct2$$"/> - </actionGroup> - - <!-- Check products in comparison sidebar --> - <!-- Check simple product 1 in comparison sidebar --> - <comment userInput="Check simple product 1 in comparison sidebar" stepKey="commentCheckSimpleProduct1InComparisonSidebar" after="compareAddSimpleProduct2ToCompare"/> - <actionGroup ref="StorefrontCheckCompareSidebarProductActionGroup" stepKey="compareSimpleProduct1InSidebar" after="commentCheckSimpleProduct1InComparisonSidebar"> - <argument name="productVar" value="$$createSimpleProduct1$$"/> - </actionGroup> - <!-- Check simple product 2 in comparison sidebar --> - <comment userInput="Check simple product 2 in comparison sidebar" stepKey="commentCheckSimpleProduct2InComparisonSidebar" /> - <actionGroup ref="StorefrontCheckCompareSidebarProductActionGroup" stepKey="compareSimpleProduct2InSidebar"> - <argument name="productVar" value="$$createSimpleProduct2$$"/> - </actionGroup> - - <!-- Check products on comparison page --> - <!-- Check simple product 1 on comparison page --> - <comment userInput="Check simple product 1 on comparison page" stepKey="commentCheckSimpleProduct1OnComparisonPage" after="compareSimpleProduct2InSidebar"/> - <actionGroup ref="StorefrontOpenAndCheckComparisionActionGroup" stepKey="compareOpenComparePage" after="commentCheckSimpleProduct1OnComparisonPage"/> - <actionGroup ref="StorefrontCheckCompareSimpleProductActionGroup" stepKey="compareAssertSimpleProduct1InComparison"> - <argument name="productVar" value="$$createSimpleProduct1$$"/> - </actionGroup> - <!-- @TODO: Move Image check to action group after MQE-697 is fixed --> - <grabAttributeFrom selector="{{StorefrontProductCompareMainSection.ProductImageByName($$createSimpleProduct1.name$$)}}" userInput="src" stepKey="compareGrabSimpleProduct1ImageSrcInComparison"/> - <assertNotRegExp expected="'/placeholder\/small_image\.jpg/'" actual="$compareGrabSimpleProduct1ImageSrcInComparison" stepKey="compareAssertSimpleProduct1ImageNotDefaultInComparison"/> - <!-- Check simple product2 on comparison page --> - <comment userInput="Check simple product 2 on comparison page" stepKey="commentCheckSimpleProduct2OnComparisonPage" /> - <actionGroup ref="StorefrontCheckCompareSimpleProductActionGroup" stepKey="compareAssertSimpleProduct2InComparison"> - <argument name="productVar" value="$$createSimpleProduct2$$"/> - </actionGroup> - <!-- @TODO: Move Image check to action group after MQE-697 is fixed --> - <grabAttributeFrom selector="{{StorefrontProductCompareMainSection.ProductImageByName($$createSimpleProduct2.name$$)}}" userInput="src" stepKey="compareGrabSimpleProduct2ImageSrcInComparison"/> - <assertNotRegExp expected="'/placeholder\/small_image\.jpg/'" actual="$compareGrabSimpleProduct2ImageSrcInComparison" stepKey="compareAssertSimpleProduct2ImageNotDefaultInComparison"/> - - <!-- Clear comparison sidebar --> - <comment userInput="Clear comparison sidebar" stepKey="commentClearComparisonSidebar" after="compareAssertSimpleProduct2ImageNotDefaultInComparison"/> - <click selector="{{StorefrontHeaderSection.NavigationCategoryByName($$createCategory.name$$)}}" stepKey="compareClickCategoryBeforeClear" after="commentClearComparisonSidebar"/> - - - <actionGroup ref="StorefrontCheckCategoryActionGroup" stepKey="compareAssertCategory2"> - <argument name="category" value="$$createCategory$$"/> - <argument name="productCount" value="3"/> - </actionGroup> - <actionGroup ref="StorefrontClearCompareActionGroup" stepKey="compareClearCompare"/> - <comment userInput="End of Comparing Products" stepKey="endOfComparingProducts" /> - </test> - <test name="EndToEndB2CGuestUserMysqlTest"> - <annotations> - <features value="End to End scenarios"/> - <stories value="B2C guest user - MAGETWO-75411"/> - <group value="e2e"/> - <title value="You should be able to pass End to End B2C Guest User scenario using the Mysql search engine"/> - <description value="User browses catalog, searches for product, adds product to cart, adds product to wishlist, compares products, uses coupon code and checks out using the Mysql search engine."/> - <severity value="CRITICAL"/> - <testCaseId value="MC-20476"/> - <group value="SearchEngineMysql"/> - </annotations> - <before> - <resetCookie userInput="PHPSESSID" stepKey="resetCookieForCart"/> - - <createData entity="ApiCategory" stepKey="createCategory"/> - - <createData entity="ApiSimpleProduct" stepKey="createSimpleProduct1"> - <requiredEntity createDataKey="createCategory"/> - </createData> - <createData entity="ApiProductAttributeMediaGalleryEntryTestImage" stepKey="createSimpleProduct1Image"> - <requiredEntity createDataKey="createSimpleProduct1"/> - </createData> - <createData entity="ApiProductAttributeMediaGalleryEntryMagentoLogo" stepKey="createSimpleProduct1Image1"> - <requiredEntity createDataKey="createSimpleProduct1"/> - </createData> - <updateData entity="ApiSimpleProductUpdateDescription" stepKey="updateSimpleProduct1" createDataKey="createSimpleProduct1"/> - - <createData entity="ApiSimpleProduct" stepKey="createSimpleProduct2"> - <requiredEntity createDataKey="createCategory"/> - </createData> - <createData entity="ApiProductAttributeMediaGalleryEntryTestImage" stepKey="createSimpleProduct2Image"> - <requiredEntity createDataKey="createSimpleProduct2"/> - </createData> - <updateData entity="ApiSimpleProductUpdateDescription" stepKey="updateSimpleProduct2" createDataKey="createSimpleProduct2"/> - </before> - <after> - <deleteData createDataKey="createCategory" stepKey="deleteCategory"/> - - <!-- @TODO: Uncomment once MQE-679 is fixed --> - <!--<deleteData createDataKey="createSimpleProduct1Image" stepKey="deleteSimpleProduct1Image"/>--> - <!-- @TODO: Uncomment once MQE-679 is fixed --> - <!--<deleteData createDataKey="createSimpleProduct1Image1" stepKey="deleteSimpleProduct1Image1"/>--> - <deleteData createDataKey="createSimpleProduct1" stepKey="deleteSimpleProduct1"/> - - <!-- @TODO: Uncomment once MQE-679 is fixed --> - <!--<deleteData createDataKey="createSimpleProduct2Image" stepKey="deleteSimpleProduct2Image"/>--> - <deleteData createDataKey="createSimpleProduct2" stepKey="deleteSimpleProduct2"/> - </after> - - <!-- Step 1: User browses catalog --> - <comment userInput="Start of browsing catalog" stepKey="startOfBrowsingCatalog" /> - <amOnPage url="{{StorefrontHomePage.url}}" stepKey="amOnHomePage"/> - <waitForPageLoad stepKey="homeWaitForPageLoad"/> - <waitForElementVisible selector="{{StorefrontPanelHeaderSection.WelcomeMessage}}" stepKey="homeWaitForWelcomeMessage"/> - <see userInput="Default welcome msg!" selector="{{StorefrontPanelHeaderSection.WelcomeMessage}}" stepKey="homeCheckWelcome"/> - - <!-- Open Category --> - <comment userInput="Open category" stepKey="commentOpenCategory" /> - <click selector="{{StorefrontHeaderSection.NavigationCategoryByName($$createCategory.name$$)}}" stepKey="browseClickCategory"/> - <actionGroup ref="StorefrontCheckCategoryActionGroup" stepKey="browseAssertCategory"> - <argument name="category" value="$$createCategory$$"/> - <argument name="productCount" value="3"/> - </actionGroup> - <!-- Check simple product 1 in category --> - <comment userInput="Check simple product 1 in category" stepKey="commentCheckSimpleProductInCategory" /> - <actionGroup ref="StorefrontCheckCategorySimpleProductActionGroup" stepKey="browseAssertCategoryProduct1"> - <argument name="product" value="$$createSimpleProduct1$$"/> - </actionGroup> - <!-- @TODO: Move Image check to action group after MQE-697 is fixed --> - <grabAttributeFrom selector="{{StorefrontCategoryProductSection.ProductImageByName($$createSimpleProduct1.name$$)}}" userInput="src" stepKey="browseGrabSimpleProduct1ImageSrc"/> - <assertNotRegExp expected="'/placeholder\/small_image\.jpg/'" actual="$browseGrabSimpleProduct1ImageSrc" stepKey="browseAssertSimpleProduct1ImageNotDefault"/> - <!-- Check simple product 2 in category --> - <comment userInput="Check simple product 2 in category" stepKey="commentCheckSimpleProduct2InCategory" /> - <actionGroup ref="StorefrontCheckCategorySimpleProductActionGroup" stepKey="browseAssertCategoryProduct2"> - <argument name="product" value="$$createSimpleProduct2$$"/> - </actionGroup> - <!-- @TODO: Move Image check to action group after MQE-697 is fixed --> - <grabAttributeFrom selector="{{StorefrontCategoryProductSection.ProductImageByName($$createSimpleProduct2.name$$)}}" userInput="src" stepKey="browseGrabSimpleProduct2ImageSrc"/> - <assertNotRegExp expected="'/placeholder\/small_image\.jpg/'" actual="$browseGrabSimpleProduct2ImageSrc" stepKey="browseAssertSimpleProduct2ImageNotDefault"/> - - <!-- View Simple Product 1 --> - <comment userInput="View simple product 1" stepKey="commentViewSimpleProduct1" after="browseAssertSimpleProduct2ImageNotDefault"/> - <click selector="{{StorefrontCategoryProductSection.ProductTitleByName($$createSimpleProduct1.name$$)}}" stepKey="browseClickCategorySimpleProduct1View" after="commentViewSimpleProduct1"/> - <waitForLoadingMaskToDisappear stepKey="waitForSimpleProduct1Viewloaded" /> - <actionGroup ref="StorefrontCheckSimpleProductActionGroup" stepKey="browseAssertProduct1Page"> - <argument name="product" value="$$createSimpleProduct1$$"/> - </actionGroup> - <!-- @TODO: Move Image check to action group after MQE-697 is fixed --> - <grabAttributeFrom selector="{{StorefrontProductInfoMainSection.productImage}}" userInput="src" stepKey="browseGrabSimpleProduct1PageImageSrc"/> - <assertNotRegExp expected="'/placeholder\/image\.jpg/'" actual="$browseGrabSimpleProduct1PageImageSrc" stepKey="browseAssertSimpleProduct1PageImageNotDefault"/> - - <!-- View Simple Product 2 --> - <comment userInput="View simple product 2" stepKey="commentViewSimpleProduct2" /> - <click selector="{{StorefrontHeaderSection.NavigationCategoryByName($$createCategory.name$$)}}" stepKey="clickCategory1"/> - <click selector="{{StorefrontCategoryProductSection.ProductTitleByName($$createSimpleProduct2.name$$)}}" stepKey="browseClickCategorySimpleProduct2View"/> - <waitForLoadingMaskToDisappear stepKey="waitForSimpleProduct2ViewLoaded" /> - <actionGroup ref="StorefrontCheckSimpleProductActionGroup" stepKey="browseAssertProduct2Page"> - <argument name="product" value="$$createSimpleProduct2$$"/> - </actionGroup> - <!-- @TODO: Move Image check to action group after MQE-697 is fixed --> - <grabAttributeFrom selector="{{StorefrontProductInfoMainSection.productImage}}" userInput="src" stepKey="browseGrabSimpleProduct2PageImageSrc"/> - <assertNotRegExp expected="'/placeholder\/image\.jpg/'" actual="$browseGrabSimpleProduct2PageImageSrc" stepKey="browseAssertSimpleProduct2PageImageNotDefault"/> - <comment userInput="End of browsing catalog" stepKey="endOfBrowsingCatalog" after="browseAssertSimpleProduct2PageImageNotDefault"/> - - <!-- Step 4: User compares products --> - <comment userInput="Start of comparing products" stepKey="startOfComparingProducts" after="endOfBrowsingCatalog"/> - <!-- Add Simple Product 1 to comparison --> - <comment userInput="Add simple product 1 to comparison" stepKey="commentAddSimpleProduct1ToComparison" /> - <click selector="{{StorefrontHeaderSection.NavigationCategoryByName($$createCategory.name$$)}}" stepKey="compareClickCategory" /> - <waitForLoadingMaskToDisappear stepKey="waitForCategoryloaded" /> - <actionGroup ref="StorefrontCheckCategoryActionGroup" stepKey="compareAssertCategory"> - <argument name="category" value="$$createCategory$$"/> - <argument name="productCount" value="3"/> - </actionGroup> - <actionGroup ref="StorefrontCheckCategorySimpleProductActionGroup" stepKey="compareAssertSimpleProduct1"> - <argument name="product" value="$$createSimpleProduct1$$"/> - </actionGroup> - <!-- @TODO: Move Image check to action group after MQE-697 is fixed --> - <grabAttributeFrom selector="{{StorefrontCategoryProductSection.ProductImageByName($$createSimpleProduct1.name$$)}}" userInput="src" stepKey="compareGrabSimpleProduct1ImageSrc"/> - <assertNotRegExp expected="'/placeholder\/small_image\.jpg/'" actual="$compareGrabSimpleProduct1ImageSrc" stepKey="compareAssertSimpleProduct1ImageNotDefault"/> - <click selector="{{StorefrontCategoryProductSection.ProductTitleByName($$createSimpleProduct1.name$$)}}" stepKey="compareClickSimpleProduct1"/> - <waitForLoadingMaskToDisappear stepKey="waitForCompareSimpleProduct1loaded" /> - <actionGroup ref="StorefrontCheckSimpleProductActionGroup" stepKey="compareAssertProduct1Page"> - <argument name="product" value="$$createSimpleProduct1$$"/> - </actionGroup> - <!-- @TODO: Move Image check to action group after MQE-697 is fixed --> - <grabAttributeFrom selector="{{StorefrontProductInfoMainSection.productImage}}" userInput="src" stepKey="compareGrabSimpleProduct1PageImageSrc"/> - <assertNotRegExp expected="'/placeholder\/image\.jpg/'" actual="$compareGrabSimpleProduct1PageImageSrc" stepKey="compareAssertSimpleProduct2PageImageNotDefault"/> - <actionGroup ref="StorefrontAddProductToCompareActionGroup" stepKey="compareAddSimpleProduct1ToCompare"> - <argument name="productVar" value="$$createSimpleProduct1$$"/> - </actionGroup> - - <!-- Add Simple Product 2 to comparison --> - <comment userInput="Add simple product 2 to comparison" stepKey="commentAddSimpleProduct2ToComparison" /> - <click selector="{{StorefrontHeaderSection.NavigationCategoryByName($$createCategory.name$$)}}" stepKey="compareClickCategory1"/> - <waitForLoadingMaskToDisappear stepKey="waitForCompareCategory1loaded" /> - <actionGroup ref="StorefrontCheckCategoryActionGroup" stepKey="compareAssertCategory1"> - <argument name="category" value="$$createCategory$$"/> - <argument name="productCount" value="3"/> - </actionGroup> - <actionGroup ref="StorefrontCheckCategorySimpleProductActionGroup" stepKey="compareAssertSimpleProduct2"> - <argument name="product" value="$$createSimpleProduct2$$"/> - </actionGroup> - <!-- @TODO: Move Image check to action group after MQE-697 is fixed --> - <grabAttributeFrom selector="{{StorefrontCategoryProductSection.ProductImageByName($$createSimpleProduct2.name$$)}}" userInput="src" stepKey="compareGrabSimpleProduct2ImageSrc"/> - <assertNotRegExp expected="'/placeholder\/small_image\.jpg/'" actual="$compareGrabSimpleProduct2ImageSrc" stepKey="compareAssertSimpleProduct2ImageNotDefault"/> - <actionGroup ref="StorefrontAddCategoryProductToCompareActionGroup" stepKey="compareAddSimpleProduct2ToCompare"> - <argument name="productVar" value="$$createSimpleProduct2$$"/> - </actionGroup> - - <!-- Check products in comparison sidebar --> - <!-- Check simple product 1 in comparison sidebar --> - <comment userInput="Check simple product 1 in comparison sidebar" stepKey="commentCheckSimpleProduct1InComparisonSidebar" after="compareAddSimpleProduct2ToCompare"/> - <actionGroup ref="StorefrontCheckCompareSidebarProductActionGroup" stepKey="compareSimpleProduct1InSidebar" after="commentCheckSimpleProduct1InComparisonSidebar"> - <argument name="productVar" value="$$createSimpleProduct1$$"/> - </actionGroup> - <!-- Check simple product 2 in comparison sidebar --> - <comment userInput="Check simple product 2 in comparison sidebar" stepKey="commentCheckSimpleProduct2InComparisonSidebar" /> - <actionGroup ref="StorefrontCheckCompareSidebarProductActionGroup" stepKey="compareSimpleProduct2InSidebar"> - <argument name="productVar" value="$$createSimpleProduct2$$"/> - </actionGroup> - - <!-- Check products on comparison page --> - <!-- Check simple product 1 on comparison page --> - <comment userInput="Check simple product 1 on comparison page" stepKey="commentCheckSimpleProduct1OnComparisonPage" after="compareSimpleProduct2InSidebar"/> - <actionGroup ref="StorefrontOpenAndCheckComparisionActionGroup" stepKey="compareOpenComparePage" after="commentCheckSimpleProduct1OnComparisonPage"/> - <actionGroup ref="StorefrontCheckCompareSimpleProductActionGroup" stepKey="compareAssertSimpleProduct1InComparison"> - <argument name="productVar" value="$$createSimpleProduct1$$"/> - </actionGroup> - <!-- @TODO: Move Image check to action group after MQE-697 is fixed --> - <grabAttributeFrom selector="{{StorefrontProductCompareMainSection.ProductImageByName($$createSimpleProduct1.name$$)}}" userInput="src" stepKey="compareGrabSimpleProduct1ImageSrcInComparison"/> - <assertNotRegExp expected="'/placeholder\/small_image\.jpg/'" actual="$compareGrabSimpleProduct1ImageSrcInComparison" stepKey="compareAssertSimpleProduct1ImageNotDefaultInComparison"/> - <!-- Check simple product2 on comparison page --> - <comment userInput="Check simple product 2 on comparison page" stepKey="commentCheckSimpleProduct2OnComparisonPage" /> - <actionGroup ref="StorefrontCheckCompareSimpleProductActionGroup" stepKey="compareAssertSimpleProduct2InComparison"> - <argument name="productVar" value="$$createSimpleProduct2$$"/> - </actionGroup> - <!-- @TODO: Move Image check to action group after MQE-697 is fixed --> - <grabAttributeFrom selector="{{StorefrontProductCompareMainSection.ProductImageByName($$createSimpleProduct2.name$$)}}" userInput="src" stepKey="compareGrabSimpleProduct2ImageSrcInComparison"/> - <assertNotRegExp expected="'/placeholder\/small_image\.jpg/'" actual="$compareGrabSimpleProduct2ImageSrcInComparison" stepKey="compareAssertSimpleProduct2ImageNotDefaultInComparison"/> - - <!-- Clear comparison sidebar --> - <comment userInput="Clear comparison sidebar" stepKey="commentClearComparisonSidebar" after="compareAssertSimpleProduct2ImageNotDefaultInComparison"/> - <click selector="{{StorefrontHeaderSection.NavigationCategoryByName($$createCategory.name$$)}}" stepKey="compareClickCategoryBeforeClear" after="commentClearComparisonSidebar"/> - <actionGroup ref="StorefrontCheckCategoryActionGroup" stepKey="compareAssertCategory2"> - <argument name="category" value="$$createCategory$$"/> - <argument name="productCount" value="3"/> - </actionGroup> - <actionGroup ref="StorefrontClearCompareActionGroup" stepKey="compareClearCompare"/> - <comment userInput="End of Comparing Products" stepKey="endOfComparingProducts" /> - </test> -</tests> diff --git a/app/code/Magento/Catalog/Test/Mftf/Test/EndToEndB2CGuestUserTest/EndToEndB2CGuestUserMysqlTest.xml b/app/code/Magento/Catalog/Test/Mftf/Test/EndToEndB2CGuestUserTest/EndToEndB2CGuestUserMysqlTest.xml new file mode 100644 index 0000000000000..6630557b37bd0 --- /dev/null +++ b/app/code/Magento/Catalog/Test/Mftf/Test/EndToEndB2CGuestUserTest/EndToEndB2CGuestUserMysqlTest.xml @@ -0,0 +1,227 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + /** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +--> +<tests xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/testSchema.xsd"> + <test name="EndToEndB2CGuestUserMysqlTest"> + <annotations> + <features value="End to End scenarios"/> + <stories value="B2C guest user - MAGETWO-75411"/> + <group value="e2e"/> + <title value="You should be able to pass End to End B2C Guest User scenario using the Mysql search engine"/> + <description value="User browses catalog, searches for product, adds product to cart, adds product to wishlist, compares products, uses coupon code and checks out using the Mysql search engine."/> + <severity value="CRITICAL"/> + <testCaseId value="MC-20476"/> + <group value="SearchEngineMysql"/> + </annotations> + <before> + <resetCookie userInput="PHPSESSID" stepKey="resetCookieForCart"/> + + <createData entity="ApiCategory" stepKey="createCategory"/> + + <createData entity="ApiSimpleProduct" stepKey="createSimpleProduct1"> + <requiredEntity createDataKey="createCategory"/> + </createData> + <createData entity="ApiProductAttributeMediaGalleryEntryTestImage" stepKey="createSimpleProduct1Image"> + <requiredEntity createDataKey="createSimpleProduct1"/> + </createData> + <createData entity="ApiProductAttributeMediaGalleryEntryMagentoLogo" stepKey="createSimpleProduct1Image1"> + <requiredEntity createDataKey="createSimpleProduct1"/> + </createData> + <updateData entity="ApiSimpleProductUpdateDescription" stepKey="updateSimpleProduct1" createDataKey="createSimpleProduct1"/> + + <createData entity="ApiSimpleProduct" stepKey="createSimpleProduct2"> + <requiredEntity createDataKey="createCategory"/> + </createData> + <createData entity="ApiProductAttributeMediaGalleryEntryTestImage" stepKey="createSimpleProduct2Image"> + <requiredEntity createDataKey="createSimpleProduct2"/> + </createData> + <updateData entity="ApiSimpleProductUpdateDescription" stepKey="updateSimpleProduct2" createDataKey="createSimpleProduct2"/> + </before> + <after> + <deleteData createDataKey="createCategory" stepKey="deleteCategory"/> + + <!-- @TODO: Uncomment once MQE-679 is fixed --> + <!--<deleteData createDataKey="createSimpleProduct1Image" stepKey="deleteSimpleProduct1Image"/>--> + <!-- @TODO: Uncomment once MQE-679 is fixed --> + <!--<deleteData createDataKey="createSimpleProduct1Image1" stepKey="deleteSimpleProduct1Image1"/>--> + <deleteData createDataKey="createSimpleProduct1" stepKey="deleteSimpleProduct1"/> + + <!-- @TODO: Uncomment once MQE-679 is fixed --> + <!--<deleteData createDataKey="createSimpleProduct2Image" stepKey="deleteSimpleProduct2Image"/>--> + <deleteData createDataKey="createSimpleProduct2" stepKey="deleteSimpleProduct2"/> + </after> + + <!-- Step 1: User browses catalog --> + <comment userInput="Start of browsing catalog" stepKey="startOfBrowsingCatalog"/> + <actionGroup ref="StorefrontOpenHomePageActionGroup" stepKey="goToHomePage"/> + <waitForElementVisible selector="{{StorefrontPanelHeaderSection.welcomeMessage}}" stepKey="homeWaitForWelcomeMessage"/> + <see userInput="Default welcome msg!" selector="{{StorefrontPanelHeaderSection.welcomeMessage}}" stepKey="homeCheckWelcome"/> + + <!-- Open Category --> + <comment userInput="Open category" stepKey="commentOpenCategory"/> + <click selector="{{StorefrontHeaderSection.NavigationCategoryByName($$createCategory.name$$)}}" stepKey="browseClickCategory"/> + <actionGroup ref="StorefrontCheckCategoryActionGroup" stepKey="browseAssertCategory"> + <argument name="category" value="$$createCategory$$"/> + <argument name="productCount" value="3"/> + </actionGroup> + <!-- Check simple product 1 in category --> + <comment userInput="Check simple product 1 in category" stepKey="commentCheckSimpleProductInCategory"/> + <actionGroup ref="StorefrontCheckCategorySimpleProductActionGroup" stepKey="browseAssertCategoryProduct1"> + <argument name="product" value="$$createSimpleProduct1$$"/> + </actionGroup> + <!-- @TODO: Move Image check to action group after MQE-697 is fixed --> + <grabAttributeFrom selector="{{StorefrontCategoryProductSection.ProductImageByName($$createSimpleProduct1.name$$)}}" userInput="src" stepKey="browseGrabSimpleProduct1ImageSrc"/> + <assertNotRegExp stepKey="browseAssertSimpleProduct1ImageNotDefault"> + <actualResult type="const">$browseGrabSimpleProduct1ImageSrc</actualResult> + <expectedResult type="const">'/placeholder\/small_image\.jpg/'</expectedResult> + </assertNotRegExp> + <!-- Check simple product 2 in category --> + <comment userInput="Check simple product 2 in category" stepKey="commentCheckSimpleProduct2InCategory"/> + <actionGroup ref="StorefrontCheckCategorySimpleProductActionGroup" stepKey="browseAssertCategoryProduct2"> + <argument name="product" value="$$createSimpleProduct2$$"/> + </actionGroup> + <!-- @TODO: Move Image check to action group after MQE-697 is fixed --> + <grabAttributeFrom selector="{{StorefrontCategoryProductSection.ProductImageByName($$createSimpleProduct2.name$$)}}" userInput="src" stepKey="browseGrabSimpleProduct2ImageSrc"/> + <assertNotRegExp stepKey="browseAssertSimpleProduct2ImageNotDefault"> + <actualResult type="const">$browseGrabSimpleProduct2ImageSrc</actualResult> + <expectedResult type="const">'/placeholder\/small_image\.jpg/'</expectedResult> + </assertNotRegExp> + + <!-- View Simple Product 1 --> + <comment userInput="View simple product 1" stepKey="commentViewSimpleProduct1" after="browseAssertSimpleProduct2ImageNotDefault"/> + <click selector="{{StorefrontCategoryProductSection.ProductTitleByName($$createSimpleProduct1.name$$)}}" stepKey="browseClickCategorySimpleProduct1View" after="commentViewSimpleProduct1"/> + <waitForLoadingMaskToDisappear stepKey="waitForSimpleProduct1Viewloaded"/> + <actionGroup ref="StorefrontCheckSimpleProductActionGroup" stepKey="browseAssertProduct1Page"> + <argument name="product" value="$$createSimpleProduct1$$"/> + </actionGroup> + <!-- @TODO: Move Image check to action group after MQE-697 is fixed --> + <grabAttributeFrom selector="{{StorefrontProductInfoMainSection.productImage}}" userInput="src" stepKey="browseGrabSimpleProduct1PageImageSrc"/> + <assertNotRegExp stepKey="browseAssertSimpleProduct1PageImageNotDefault"> + <actualResult type="const">$browseGrabSimpleProduct1PageImageSrc</actualResult> + <expectedResult type="const">'/placeholder\/image\.jpg/'</expectedResult> + </assertNotRegExp> + + <!-- View Simple Product 2 --> + <comment userInput="View simple product 2" stepKey="commentViewSimpleProduct2"/> + <click selector="{{StorefrontHeaderSection.NavigationCategoryByName($$createCategory.name$$)}}" stepKey="clickCategory1"/> + <click selector="{{StorefrontCategoryProductSection.ProductTitleByName($$createSimpleProduct2.name$$)}}" stepKey="browseClickCategorySimpleProduct2View"/> + <waitForLoadingMaskToDisappear stepKey="waitForSimpleProduct2ViewLoaded"/> + <actionGroup ref="StorefrontCheckSimpleProductActionGroup" stepKey="browseAssertProduct2Page"> + <argument name="product" value="$$createSimpleProduct2$$"/> + </actionGroup> + <!-- @TODO: Move Image check to action group after MQE-697 is fixed --> + <grabAttributeFrom selector="{{StorefrontProductInfoMainSection.productImage}}" userInput="src" stepKey="browseGrabSimpleProduct2PageImageSrc"/> + <assertNotRegExp stepKey="browseAssertSimpleProduct2PageImageNotDefault"> + <actualResult type="const">$browseGrabSimpleProduct2PageImageSrc</actualResult> + <expectedResult type="const">'/placeholder\/image\.jpg/'</expectedResult> + </assertNotRegExp> + <comment userInput="End of browsing catalog" stepKey="endOfBrowsingCatalog" after="browseAssertSimpleProduct2PageImageNotDefault"/> + + <!-- Step 4: User compares products --> + <comment userInput="Start of comparing products" stepKey="startOfComparingProducts" after="endOfBrowsingCatalog"/> + <!-- Add Simple Product 1 to comparison --> + <comment userInput="Add simple product 1 to comparison" stepKey="commentAddSimpleProduct1ToComparison"/> + <click selector="{{StorefrontHeaderSection.NavigationCategoryByName($$createCategory.name$$)}}" stepKey="compareClickCategory"/> + <waitForLoadingMaskToDisappear stepKey="waitForCategoryloaded"/> + <actionGroup ref="StorefrontCheckCategoryActionGroup" stepKey="compareAssertCategory"> + <argument name="category" value="$$createCategory$$"/> + <argument name="productCount" value="3"/> + </actionGroup> + <actionGroup ref="StorefrontCheckCategorySimpleProductActionGroup" stepKey="compareAssertSimpleProduct1"> + <argument name="product" value="$$createSimpleProduct1$$"/> + </actionGroup> + <!-- @TODO: Move Image check to action group after MQE-697 is fixed --> + <grabAttributeFrom selector="{{StorefrontCategoryProductSection.ProductImageByName($$createSimpleProduct1.name$$)}}" userInput="src" stepKey="compareGrabSimpleProduct1ImageSrc"/> + <assertNotRegExp stepKey="compareAssertSimpleProduct1ImageNotDefault"> + <actualResult type="const">$compareGrabSimpleProduct1ImageSrc</actualResult> + <expectedResult type="const">'/placeholder\/small_image\.jpg/'</expectedResult> + </assertNotRegExp> + <click selector="{{StorefrontCategoryProductSection.ProductTitleByName($$createSimpleProduct1.name$$)}}" stepKey="compareClickSimpleProduct1"/> + <waitForLoadingMaskToDisappear stepKey="waitForCompareSimpleProduct1loaded"/> + <actionGroup ref="StorefrontCheckSimpleProductActionGroup" stepKey="compareAssertProduct1Page"> + <argument name="product" value="$$createSimpleProduct1$$"/> + </actionGroup> + <!-- @TODO: Move Image check to action group after MQE-697 is fixed --> + <grabAttributeFrom selector="{{StorefrontProductInfoMainSection.productImage}}" userInput="src" stepKey="compareGrabSimpleProduct1PageImageSrc"/> + <assertNotRegExp stepKey="compareAssertSimpleProduct2PageImageNotDefault"> + <actualResult type="const">$compareGrabSimpleProduct1PageImageSrc</actualResult> + <expectedResult type="const">'/placeholder\/image\.jpg/'</expectedResult> + </assertNotRegExp> + <actionGroup ref="StorefrontAddProductToCompareActionGroup" stepKey="compareAddSimpleProduct1ToCompare"> + <argument name="productVar" value="$$createSimpleProduct1$$"/> + </actionGroup> + + <!-- Add Simple Product 2 to comparison --> + <comment userInput="Add simple product 2 to comparison" stepKey="commentAddSimpleProduct2ToComparison"/> + <click selector="{{StorefrontHeaderSection.NavigationCategoryByName($$createCategory.name$$)}}" stepKey="compareClickCategory1"/> + <waitForLoadingMaskToDisappear stepKey="waitForCompareCategory1loaded"/> + <actionGroup ref="StorefrontCheckCategoryActionGroup" stepKey="compareAssertCategory1"> + <argument name="category" value="$$createCategory$$"/> + <argument name="productCount" value="3"/> + </actionGroup> + <actionGroup ref="StorefrontCheckCategorySimpleProductActionGroup" stepKey="compareAssertSimpleProduct2"> + <argument name="product" value="$$createSimpleProduct2$$"/> + </actionGroup> + <!-- @TODO: Move Image check to action group after MQE-697 is fixed --> + <grabAttributeFrom selector="{{StorefrontCategoryProductSection.ProductImageByName($$createSimpleProduct2.name$$)}}" userInput="src" stepKey="compareGrabSimpleProduct2ImageSrc"/> + <assertNotRegExp stepKey="compareAssertSimpleProduct2ImageNotDefault"> + <actualResult type="const">$compareGrabSimpleProduct2ImageSrc</actualResult> + <expectedResult type="const">'/placeholder\/small_image\.jpg/'</expectedResult> + </assertNotRegExp> + <actionGroup ref="StorefrontAddCategoryProductToCompareActionGroup" stepKey="compareAddSimpleProduct2ToCompare"> + <argument name="productVar" value="$$createSimpleProduct2$$"/> + </actionGroup> + + <!-- Check products in comparison sidebar --> + <!-- Check simple product 1 in comparison sidebar --> + <comment userInput="Check simple product 1 in comparison sidebar" stepKey="commentCheckSimpleProduct1InComparisonSidebar" after="compareAddSimpleProduct2ToCompare"/> + <actionGroup ref="StorefrontCheckCompareSidebarProductActionGroup" stepKey="compareSimpleProduct1InSidebar" after="commentCheckSimpleProduct1InComparisonSidebar"> + <argument name="productVar" value="$$createSimpleProduct1$$"/> + </actionGroup> + <!-- Check simple product 2 in comparison sidebar --> + <comment userInput="Check simple product 2 in comparison sidebar" stepKey="commentCheckSimpleProduct2InComparisonSidebar"/> + <actionGroup ref="StorefrontCheckCompareSidebarProductActionGroup" stepKey="compareSimpleProduct2InSidebar"> + <argument name="productVar" value="$$createSimpleProduct2$$"/> + </actionGroup> + + <!-- Check products on comparison page --> + <!-- Check simple product 1 on comparison page --> + <comment userInput="Check simple product 1 on comparison page" stepKey="commentCheckSimpleProduct1OnComparisonPage" after="compareSimpleProduct2InSidebar"/> + <actionGroup ref="StorefrontOpenAndCheckComparisionActionGroup" stepKey="compareOpenComparePage" after="commentCheckSimpleProduct1OnComparisonPage"/> + <actionGroup ref="StorefrontCheckCompareSimpleProductActionGroup" stepKey="compareAssertSimpleProduct1InComparison"> + <argument name="productVar" value="$$createSimpleProduct1$$"/> + </actionGroup> + <!-- @TODO: Move Image check to action group after MQE-697 is fixed --> + <grabAttributeFrom selector="{{StorefrontProductCompareMainSection.ProductImageByName($$createSimpleProduct1.name$$)}}" userInput="src" stepKey="compareGrabSimpleProduct1ImageSrcInComparison"/> + <assertNotRegExp stepKey="compareAssertSimpleProduct1ImageNotDefaultInComparison"> + <actualResult type="const">$compareGrabSimpleProduct1ImageSrcInComparison</actualResult> + <expectedResult type="const">'/placeholder\/small_image\.jpg/'</expectedResult> + </assertNotRegExp> + <!-- Check simple product2 on comparison page --> + <comment userInput="Check simple product 2 on comparison page" stepKey="commentCheckSimpleProduct2OnComparisonPage"/> + <actionGroup ref="StorefrontCheckCompareSimpleProductActionGroup" stepKey="compareAssertSimpleProduct2InComparison"> + <argument name="productVar" value="$$createSimpleProduct2$$"/> + </actionGroup> + <!-- @TODO: Move Image check to action group after MQE-697 is fixed --> + <grabAttributeFrom selector="{{StorefrontProductCompareMainSection.ProductImageByName($$createSimpleProduct2.name$$)}}" userInput="src" stepKey="compareGrabSimpleProduct2ImageSrcInComparison"/> + <assertNotRegExp stepKey="compareAssertSimpleProduct2ImageNotDefaultInComparison"> + <actualResult type="const">$compareGrabSimpleProduct2ImageSrcInComparison</actualResult> + <expectedResult type="const">'/placeholder\/small_image\.jpg/'</expectedResult> + </assertNotRegExp> + + <!-- Clear comparison sidebar --> + <comment userInput="Clear comparison sidebar" stepKey="commentClearComparisonSidebar" after="compareAssertSimpleProduct2ImageNotDefaultInComparison"/> + <click selector="{{StorefrontHeaderSection.NavigationCategoryByName($$createCategory.name$$)}}" stepKey="compareClickCategoryBeforeClear" after="commentClearComparisonSidebar"/> + <actionGroup ref="StorefrontCheckCategoryActionGroup" stepKey="compareAssertCategory2"> + <argument name="category" value="$$createCategory$$"/> + <argument name="productCount" value="3"/> + </actionGroup> + <actionGroup ref="StorefrontClearCompareActionGroup" stepKey="compareClearCompare"/> + <comment userInput="End of Comparing Products" stepKey="endOfComparingProducts"/> + </test> +</tests> diff --git a/app/code/Magento/Catalog/Test/Mftf/Test/EndToEndB2CGuestUserTest/EndToEndB2CGuestUserTest.xml b/app/code/Magento/Catalog/Test/Mftf/Test/EndToEndB2CGuestUserTest/EndToEndB2CGuestUserTest.xml new file mode 100644 index 0000000000000..441c9cd5eab8b --- /dev/null +++ b/app/code/Magento/Catalog/Test/Mftf/Test/EndToEndB2CGuestUserTest/EndToEndB2CGuestUserTest.xml @@ -0,0 +1,232 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + /** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +--> +<tests xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/testSchema.xsd"> + <test name="EndToEndB2CGuestUserTest"> + <annotations> + <features value="End to End scenarios"/> + <stories value="B2C guest user - MAGETWO-75411"/> + <group value="e2e"/> + <title value="You should be able to pass End to End B2C Guest User scenario"/> + <description value="User browses catalog, searches for product, adds product to cart, adds product to wishlist, compares products, uses coupon code and checks out."/> + <severity value="CRITICAL"/> + <testCaseId value="MAGETWO-87435"/> + <group value="SearchEngineElasticsearch"/> + </annotations> + <before> + <resetCookie userInput="PHPSESSID" stepKey="resetCookieForCart"/> + + <createData entity="ApiCategory" stepKey="createCategory"/> + + <createData entity="ApiSimpleProduct" stepKey="createSimpleProduct1"> + <requiredEntity createDataKey="createCategory"/> + </createData> + <createData entity="ApiProductAttributeMediaGalleryEntryTestImage" stepKey="createSimpleProduct1Image"> + <requiredEntity createDataKey="createSimpleProduct1"/> + </createData> + <createData entity="ApiProductAttributeMediaGalleryEntryMagentoLogo" stepKey="createSimpleProduct1Image1"> + <requiredEntity createDataKey="createSimpleProduct1"/> + </createData> + <updateData entity="ApiSimpleProductUpdateDescription" stepKey="updateSimpleProduct1" createDataKey="createSimpleProduct1"/> + + <createData entity="ApiSimpleProduct" stepKey="createSimpleProduct2"> + <requiredEntity createDataKey="createCategory"/> + </createData> + <createData entity="ApiProductAttributeMediaGalleryEntryTestImage" stepKey="createSimpleProduct2Image"> + <requiredEntity createDataKey="createSimpleProduct2"/> + </createData> + <updateData entity="ApiSimpleProductUpdateDescription" stepKey="updateSimpleProduct2" createDataKey="createSimpleProduct2"/> + </before> + <after> + <deleteData createDataKey="createCategory" stepKey="deleteCategory"/> + + <!-- @TODO: Uncomment once MQE-679 is fixed --> + <!--<deleteData createDataKey="createSimpleProduct1Image" stepKey="deleteSimpleProduct1Image"/>--> + <!-- @TODO: Uncomment once MQE-679 is fixed --> + <!--<deleteData createDataKey="createSimpleProduct1Image1" stepKey="deleteSimpleProduct1Image1"/>--> + <deleteData createDataKey="createSimpleProduct1" stepKey="deleteSimpleProduct1"/> + + <!-- @TODO: Uncomment once MQE-679 is fixed --> + <!--<deleteData createDataKey="createSimpleProduct2Image" stepKey="deleteSimpleProduct2Image"/>--> + <deleteData createDataKey="createSimpleProduct2" stepKey="deleteSimpleProduct2"/> + </after> + + <!--Re-index--> + <magentoCLI command="indexer:reindex" stepKey="reindex"/> + + <!-- Step 1: User browses catalog --> + <comment userInput="Start of browsing catalog" stepKey="startOfBrowsingCatalog"/> + <actionGroup ref="StorefrontOpenHomePageActionGroup" stepKey="goToHomePage"/> + <waitForElementVisible selector="{{StorefrontPanelHeaderSection.welcomeMessage}}" stepKey="homeWaitForWelcomeMessage"/> + <see userInput="Default welcome msg!" selector="{{StorefrontPanelHeaderSection.welcomeMessage}}" stepKey="homeCheckWelcome"/> + + <!-- Open Category --> + <comment userInput="Open category" stepKey="commentOpenCategory"/> + <click selector="{{StorefrontHeaderSection.NavigationCategoryByName($$createCategory.name$$)}}" stepKey="browseClickCategory"/> + <actionGroup ref="StorefrontCheckCategoryActionGroup" stepKey="browseAssertCategory"> + <argument name="category" value="$$createCategory$$"/> + <argument name="productCount" value="3"/> + </actionGroup> + <!-- Check simple product 1 in category --> + <comment userInput="Check simple product 1 in category" stepKey="commentCheckSimpleProductInCategory"/> + <actionGroup ref="StorefrontCheckCategorySimpleProductActionGroup" stepKey="browseAssertCategoryProduct1"> + <argument name="product" value="$$createSimpleProduct1$$"/> + </actionGroup> + <!-- @TODO: Move Image check to action group after MQE-697 is fixed --> + <grabAttributeFrom selector="{{StorefrontCategoryProductSection.ProductImageByName($$createSimpleProduct1.name$$)}}" userInput="src" stepKey="browseGrabSimpleProduct1ImageSrc"/> + <assertNotRegExp stepKey="browseAssertSimpleProduct1ImageNotDefault"> + <actualResult type="const">$browseGrabSimpleProduct1ImageSrc</actualResult> + <expectedResult type="const">'/placeholder\/small_image\.jpg/'</expectedResult> + </assertNotRegExp> + <!-- Check simple product 2 in category --> + <comment userInput="Check simple product 2 in category" stepKey="commentCheckSimpleProduct2InCategory"/> + <actionGroup ref="StorefrontCheckCategorySimpleProductActionGroup" stepKey="browseAssertCategoryProduct2"> + <argument name="product" value="$$createSimpleProduct2$$"/> + </actionGroup> + <!-- @TODO: Move Image check to action group after MQE-697 is fixed --> + <grabAttributeFrom selector="{{StorefrontCategoryProductSection.ProductImageByName($$createSimpleProduct2.name$$)}}" userInput="src" stepKey="browseGrabSimpleProduct2ImageSrc"/> + <assertNotRegExp stepKey="browseAssertSimpleProduct2ImageNotDefault"> + <actualResult type="const">$browseGrabSimpleProduct2ImageSrc</actualResult> + <expectedResult type="const">'/placeholder\/small_image\.jpg/'</expectedResult> + </assertNotRegExp> + + <!-- View Simple Product 1 --> + <comment userInput="View simple product 1" stepKey="commentViewSimpleProduct1" after="browseAssertSimpleProduct2ImageNotDefault"/> + <click selector="{{StorefrontCategoryProductSection.ProductTitleByName($$createSimpleProduct1.name$$)}}" stepKey="browseClickCategorySimpleProduct1View" after="commentViewSimpleProduct1"/> + <waitForLoadingMaskToDisappear stepKey="waitForSimpleProduct1Viewloaded"/> + <actionGroup ref="StorefrontCheckSimpleProductActionGroup" stepKey="browseAssertProduct1Page"> + <argument name="product" value="$$createSimpleProduct1$$"/> + </actionGroup> + <!-- @TODO: Move Image check to action group after MQE-697 is fixed --> + <grabAttributeFrom selector="{{StorefrontProductInfoMainSection.productImage}}" userInput="src" stepKey="browseGrabSimpleProduct1PageImageSrc"/> + <assertNotRegExp stepKey="browseAssertSimpleProduct1PageImageNotDefault"> + <actualResult type="const">$browseGrabSimpleProduct1PageImageSrc</actualResult> + <expectedResult type="const">'/placeholder\/image\.jpg/'</expectedResult> + </assertNotRegExp> + + <!-- View Simple Product 2 --> + <comment userInput="View simple product 2" stepKey="commentViewSimpleProduct2"/> + <click selector="{{StorefrontHeaderSection.NavigationCategoryByName($$createCategory.name$$)}}" stepKey="clickCategory1"/> + <click selector="{{StorefrontCategoryProductSection.ProductTitleByName($$createSimpleProduct2.name$$)}}" stepKey="browseClickCategorySimpleProduct2View"/> + <waitForLoadingMaskToDisappear stepKey="waitForSimpleProduct2ViewLoaded"/> + <actionGroup ref="StorefrontCheckSimpleProductActionGroup" stepKey="browseAssertProduct2Page"> + <argument name="product" value="$$createSimpleProduct2$$"/> + </actionGroup> + <!-- @TODO: Move Image check to action group after MQE-697 is fixed --> + <grabAttributeFrom selector="{{StorefrontProductInfoMainSection.productImage}}" userInput="src" stepKey="browseGrabSimpleProduct2PageImageSrc"/> + <assertNotRegExp stepKey="browseAssertSimpleProduct2PageImageNotDefault"> + <actualResult type="const">$browseGrabSimpleProduct2PageImageSrc</actualResult> + <expectedResult type="const">'/placeholder\/image\.jpg/'</expectedResult> + </assertNotRegExp> + <comment userInput="End of browsing catalog" stepKey="endOfBrowsingCatalog" after="browseAssertSimpleProduct2PageImageNotDefault"/> + + <!-- Step 4: User compares products --> + <comment userInput="Start of comparing products" stepKey="startOfComparingProducts" after="endOfBrowsingCatalog"/> + <!-- Add Simple Product 1 to comparison --> + <comment userInput="Add simple product 1 to comparison" stepKey="commentAddSimpleProduct1ToComparison"/> + <click selector="{{StorefrontHeaderSection.NavigationCategoryByName($$createCategory.name$$)}}" stepKey="compareClickCategory"/> + <waitForLoadingMaskToDisappear stepKey="waitForCategoryloaded"/> + <actionGroup ref="StorefrontCheckCategoryActionGroup" stepKey="compareAssertCategory"> + <argument name="category" value="$$createCategory$$"/> + <argument name="productCount" value="3"/> + </actionGroup> + <actionGroup ref="StorefrontCheckCategorySimpleProductActionGroup" stepKey="compareAssertSimpleProduct1"> + <argument name="product" value="$$createSimpleProduct1$$"/> + </actionGroup> + <!-- @TODO: Move Image check to action group after MQE-697 is fixed --> + <grabAttributeFrom selector="{{StorefrontCategoryProductSection.ProductImageByName($$createSimpleProduct1.name$$)}}" userInput="src" stepKey="compareGrabSimpleProduct1ImageSrc"/> + <assertNotRegExp stepKey="compareAssertSimpleProduct1ImageNotDefault"> + <actualResult type="const">$compareGrabSimpleProduct1ImageSrc</actualResult> + <expectedResult type="const">'/placeholder\/small_image\.jpg/'</expectedResult> + </assertNotRegExp> + <click selector="{{StorefrontCategoryProductSection.ProductTitleByName($$createSimpleProduct1.name$$)}}" stepKey="compareClickSimpleProduct1"/> + <waitForLoadingMaskToDisappear stepKey="waitForCompareSimpleProduct1loaded"/> + <actionGroup ref="StorefrontCheckSimpleProductActionGroup" stepKey="compareAssertProduct1Page"> + <argument name="product" value="$$createSimpleProduct1$$"/> + </actionGroup> + <!-- @TODO: Move Image check to action group after MQE-697 is fixed --> + <grabAttributeFrom selector="{{StorefrontProductInfoMainSection.productImage}}" userInput="src" stepKey="compareGrabSimpleProduct1PageImageSrc"/> + <assertNotRegExp stepKey="compareAssertSimpleProduct2PageImageNotDefault"> + <actualResult type="const">$compareGrabSimpleProduct1PageImageSrc</actualResult> + <expectedResult type="const">'/placeholder\/image\.jpg/'</expectedResult> + </assertNotRegExp> + <actionGroup ref="StorefrontAddProductToCompareActionGroup" stepKey="compareAddSimpleProduct1ToCompare"> + <argument name="productVar" value="$$createSimpleProduct1$$"/> + </actionGroup> + + <!-- Add Simple Product 2 to comparison --> + <comment userInput="Add simple product 2 to comparison" stepKey="commentAddSimpleProduct2ToComparison"/> + <click selector="{{StorefrontHeaderSection.NavigationCategoryByName($$createCategory.name$$)}}" stepKey="compareClickCategory1"/> + <waitForLoadingMaskToDisappear stepKey="waitForCompareCategory1loaded"/> + <actionGroup ref="StorefrontCheckCategoryActionGroup" stepKey="compareAssertCategory1"> + <argument name="category" value="$$createCategory$$"/> + <argument name="productCount" value="3"/> + </actionGroup> + <actionGroup ref="StorefrontCheckCategorySimpleProductActionGroup" stepKey="compareAssertSimpleProduct2"> + <argument name="product" value="$$createSimpleProduct2$$"/> + </actionGroup> + <!-- @TODO: Move Image check to action group after MQE-697 is fixed --> + <grabAttributeFrom selector="{{StorefrontCategoryProductSection.ProductImageByName($$createSimpleProduct2.name$$)}}" userInput="src" stepKey="compareGrabSimpleProduct2ImageSrc"/> + <assertNotRegExp stepKey="compareAssertSimpleProduct2ImageNotDefault"> + <actualResult type="const">$compareGrabSimpleProduct2ImageSrc</actualResult> + <expectedResult type="const">'/placeholder\/small_image\.jpg/'</expectedResult> + </assertNotRegExp> + <actionGroup ref="StorefrontAddCategoryProductToCompareActionGroup" stepKey="compareAddSimpleProduct2ToCompare"> + <argument name="productVar" value="$$createSimpleProduct2$$"/> + </actionGroup> + + <!-- Check products in comparison sidebar --> + <!-- Check simple product 1 in comparison sidebar --> + <comment userInput="Check simple product 1 in comparison sidebar" stepKey="commentCheckSimpleProduct1InComparisonSidebar" after="compareAddSimpleProduct2ToCompare"/> + <actionGroup ref="StorefrontCheckCompareSidebarProductActionGroup" stepKey="compareSimpleProduct1InSidebar" after="commentCheckSimpleProduct1InComparisonSidebar"> + <argument name="productVar" value="$$createSimpleProduct1$$"/> + </actionGroup> + <!-- Check simple product 2 in comparison sidebar --> + <comment userInput="Check simple product 2 in comparison sidebar" stepKey="commentCheckSimpleProduct2InComparisonSidebar"/> + <actionGroup ref="StorefrontCheckCompareSidebarProductActionGroup" stepKey="compareSimpleProduct2InSidebar"> + <argument name="productVar" value="$$createSimpleProduct2$$"/> + </actionGroup> + + <!-- Check products on comparison page --> + <!-- Check simple product 1 on comparison page --> + <comment userInput="Check simple product 1 on comparison page" stepKey="commentCheckSimpleProduct1OnComparisonPage" after="compareSimpleProduct2InSidebar"/> + <actionGroup ref="StorefrontOpenAndCheckComparisionActionGroup" stepKey="compareOpenComparePage" after="commentCheckSimpleProduct1OnComparisonPage"/> + <actionGroup ref="StorefrontCheckCompareSimpleProductActionGroup" stepKey="compareAssertSimpleProduct1InComparison"> + <argument name="productVar" value="$$createSimpleProduct1$$"/> + </actionGroup> + <!-- @TODO: Move Image check to action group after MQE-697 is fixed --> + <grabAttributeFrom selector="{{StorefrontProductCompareMainSection.ProductImageByName($$createSimpleProduct1.name$$)}}" userInput="src" stepKey="compareGrabSimpleProduct1ImageSrcInComparison"/> + <assertNotRegExp stepKey="compareAssertSimpleProduct1ImageNotDefaultInComparison"> + <actualResult type="const">$compareGrabSimpleProduct1ImageSrcInComparison</actualResult> + <expectedResult type="const">'/placeholder\/small_image\.jpg/'</expectedResult> + </assertNotRegExp> + <!-- Check simple product2 on comparison page --> + <comment userInput="Check simple product 2 on comparison page" stepKey="commentCheckSimpleProduct2OnComparisonPage"/> + <actionGroup ref="StorefrontCheckCompareSimpleProductActionGroup" stepKey="compareAssertSimpleProduct2InComparison"> + <argument name="productVar" value="$$createSimpleProduct2$$"/> + </actionGroup> + <!-- @TODO: Move Image check to action group after MQE-697 is fixed --> + <grabAttributeFrom selector="{{StorefrontProductCompareMainSection.ProductImageByName($$createSimpleProduct2.name$$)}}" userInput="src" stepKey="compareGrabSimpleProduct2ImageSrcInComparison"/> + <assertNotRegExp stepKey="compareAssertSimpleProduct2ImageNotDefaultInComparison"> + <actualResult type="const">$compareGrabSimpleProduct2ImageSrcInComparison</actualResult> + <expectedResult type="const">'/placeholder\/small_image\.jpg/'</expectedResult> + </assertNotRegExp> + + <!-- Clear comparison sidebar --> + <comment userInput="Clear comparison sidebar" stepKey="commentClearComparisonSidebar" after="compareAssertSimpleProduct2ImageNotDefaultInComparison"/> + <click selector="{{StorefrontHeaderSection.NavigationCategoryByName($$createCategory.name$$)}}" stepKey="compareClickCategoryBeforeClear" after="commentClearComparisonSidebar"/> + + + <actionGroup ref="StorefrontCheckCategoryActionGroup" stepKey="compareAssertCategory2"> + <argument name="category" value="$$createCategory$$"/> + <argument name="productCount" value="3"/> + </actionGroup> + <actionGroup ref="StorefrontClearCompareActionGroup" stepKey="compareClearCompare"/> + <comment userInput="End of Comparing Products" stepKey="endOfComparingProducts"/> + </test> +</tests> diff --git a/app/code/Magento/Catalog/Test/Mftf/Test/EndToEndB2CLoggedInUserTest.xml b/app/code/Magento/Catalog/Test/Mftf/Test/EndToEndB2CLoggedInUserTest.xml index 74c6da1c47f60..fbb6893e92b1e 100644 --- a/app/code/Magento/Catalog/Test/Mftf/Test/EndToEndB2CLoggedInUserTest.xml +++ b/app/code/Magento/Catalog/Test/Mftf/Test/EndToEndB2CLoggedInUserTest.xml @@ -49,8 +49,8 @@ <comment userInput="Start of browsing catalog" stepKey="startOfBrowsingCatalog" after="endOfSigningUpUserAccount"/> <amOnPage url="{{StorefrontHomePage.url}}" stepKey="amOnHomePage" after="startOfBrowsingCatalog"/> <waitForPageLoad stepKey="homeWaitForPageLoad" after="amOnHomePage"/> - <waitForElementVisible selector="{{StorefrontPanelHeaderSection.WelcomeMessage}}" stepKey="homeWaitForWelcomeMessage" after="homeWaitForPageLoad"/> - <see userInput="Welcome, John Doe!" selector="{{StorefrontPanelHeaderSection.WelcomeMessage}}" stepKey="homeCheckWelcome" after="homeWaitForWelcomeMessage"/> + <waitForElementVisible selector="{{StorefrontPanelHeaderSection.welcomeMessage}}" stepKey="homeWaitForWelcomeMessage" after="homeWaitForPageLoad"/> + <see userInput="Welcome, John Doe!" selector="{{StorefrontPanelHeaderSection.welcomeMessage}}" stepKey="homeCheckWelcome" after="homeWaitForWelcomeMessage"/> <!-- Open Category --> <comment userInput="Open category" stepKey="commentOpenCategory" after="homeCheckWelcome"/> @@ -66,7 +66,10 @@ </actionGroup> <!-- @TODO: Move Image check to action group after MQE-697 is fixed --> <grabAttributeFrom selector="{{StorefrontCategoryProductSection.ProductImageByName($$createSimpleProduct1.name$$)}}" userInput="src" stepKey="browseGrabSimpleProduct1ImageSrc" after="browseAssertCategoryProduct1"/> - <assertNotRegExp expected="'/placeholder\/small_image\.jpg/'" actual="$browseGrabSimpleProduct1ImageSrc" stepKey="browseAssertSimpleProduct1ImageNotDefault" after="browseGrabSimpleProduct1ImageSrc"/> + <assertNotRegExp stepKey="browseAssertSimpleProduct1ImageNotDefault" after="browseGrabSimpleProduct1ImageSrc"> + <actualResult type="const">$browseGrabSimpleProduct1ImageSrc</actualResult> + <expectedResult type="const">'/placeholder\/small_image\.jpg/'</expectedResult> + </assertNotRegExp> <!-- Check simple product 2 in category --> <comment userInput="Check simple product 2 in category" stepKey="commentCheckSimpleProduct2InCategory" after="browseAssertSimpleProduct1ImageNotDefault"/> <actionGroup ref="StorefrontCheckCategorySimpleProductActionGroup" stepKey="browseAssertCategoryProduct2" after="commentCheckSimpleProduct2InCategory"> @@ -74,7 +77,10 @@ </actionGroup> <!-- @TODO: Move Image check to action group after MQE-697 is fixed --> <grabAttributeFrom selector="{{StorefrontCategoryProductSection.ProductImageByName($$createSimpleProduct2.name$$)}}" userInput="src" stepKey="browseGrabSimpleProduct2ImageSrc" after="browseAssertCategoryProduct2"/> - <assertNotRegExp expected="'/placeholder\/small_image\.jpg/'" actual="$browseGrabSimpleProduct2ImageSrc" stepKey="browseAssertSimpleProduct2ImageNotDefault" after="browseGrabSimpleProduct2ImageSrc"/> + <assertNotRegExp stepKey="browseAssertSimpleProduct2ImageNotDefault" after="browseGrabSimpleProduct2ImageSrc"> + <actualResult type="const">$browseGrabSimpleProduct2ImageSrc</actualResult> + <expectedResult type="const">'/placeholder\/small_image\.jpg/'</expectedResult> + </assertNotRegExp> <!-- View Simple Product 1 --> <comment userInput="View simple product 1" stepKey="commentViewSimpleProduct1" after="browseAssertSimpleProduct2ImageNotDefault"/> @@ -85,7 +91,10 @@ </actionGroup> <!-- @TODO: Move Image check to action group after MQE-697 is fixed --> <grabAttributeFrom selector="{{StorefrontProductInfoMainSection.productImage}}" userInput="src" stepKey="browseGrabSimpleProduct1PageImageSrc" after="browseAssertProduct1Page"/> - <assertNotRegExp expected="'/placeholder\/image\.jpg/'" actual="$browseGrabSimpleProduct1PageImageSrc" stepKey="browseAssertSimpleProduct1PageImageNotDefault" after="browseGrabSimpleProduct1PageImageSrc"/> + <assertNotRegExp stepKey="browseAssertSimpleProduct1PageImageNotDefault" after="browseGrabSimpleProduct1PageImageSrc"> + <actualResult type="const">$browseGrabSimpleProduct1PageImageSrc</actualResult> + <expectedResult type="const">'/placeholder\/image\.jpg/'</expectedResult> + </assertNotRegExp> <!-- View Simple Product 2 --> <comment userInput="View simple product 2" stepKey="commentViewSimpleProduct2" after="browseAssertSimpleProduct1PageImageNotDefault"/> @@ -97,7 +106,10 @@ </actionGroup> <!-- @TODO: Move Image check to action group after MQE-697 is fixed --> <grabAttributeFrom selector="{{StorefrontProductInfoMainSection.productImage}}" userInput="src" stepKey="browseGrabSimpleProduct2PageImageSrc" after="browseAssertProduct2Page"/> - <assertNotRegExp expected="'/placeholder\/image\.jpg/'" actual="$browseGrabSimpleProduct2PageImageSrc" stepKey="browseAssertSimpleProduct2PageImageNotDefault" after="browseGrabSimpleProduct2PageImageSrc"/> + <assertNotRegExp stepKey="browseAssertSimpleProduct2PageImageNotDefault" after="browseGrabSimpleProduct2PageImageSrc"> + <actualResult type="const">$browseGrabSimpleProduct2PageImageSrc</actualResult> + <expectedResult type="const">'/placeholder\/image\.jpg/'</expectedResult> + </assertNotRegExp> <comment userInput="End of browsing catalog" stepKey="endOfBrowsingCatalog" after="browseAssertSimpleProduct2PageImageNotDefault"/> <!-- Step 4: User compares products --> @@ -115,7 +127,10 @@ </actionGroup> <!-- @TODO: Move Image check to action group after MQE-697 is fixed --> <grabAttributeFrom selector="{{StorefrontCategoryProductSection.ProductImageByName($$createSimpleProduct1.name$$)}}" userInput="src" stepKey="compareGrabSimpleProduct1ImageSrc" after="compareAssertSimpleProduct1"/> - <assertNotRegExp expected="'/placeholder\/small_image\.jpg/'" actual="$compareGrabSimpleProduct1ImageSrc" stepKey="compareAssertSimpleProduct1ImageNotDefault" after="compareGrabSimpleProduct1ImageSrc"/> + <assertNotRegExp stepKey="compareAssertSimpleProduct1ImageNotDefault" after="compareGrabSimpleProduct1ImageSrc"> + <actualResult type="const">$compareGrabSimpleProduct1ImageSrc</actualResult> + <expectedResult type="const">'/placeholder\/small_image\.jpg/'</expectedResult> + </assertNotRegExp> <click selector="{{StorefrontCategoryProductSection.ProductTitleByName($$createSimpleProduct1.name$$)}}" stepKey="compareClickSimpleProduct1" after="compareAssertSimpleProduct1ImageNotDefault"/> <waitForLoadingMaskToDisappear stepKey="waitForCompareSimpleProduct1loaded" after="compareClickSimpleProduct1"/> <actionGroup ref="StorefrontCheckSimpleProductActionGroup" stepKey="compareAssertProduct1Page" after="waitForCompareSimpleProduct1loaded"> @@ -123,7 +138,10 @@ </actionGroup> <!-- @TODO: Move Image check to action group after MQE-697 is fixed --> <grabAttributeFrom selector="{{StorefrontProductInfoMainSection.productImage}}" userInput="src" stepKey="compareGrabSimpleProduct1PageImageSrc" after="compareAssertProduct1Page"/> - <assertNotRegExp expected="'/placeholder\/image\.jpg/'" actual="$compareGrabSimpleProduct1PageImageSrc" stepKey="compareAssertSimpleProduct2PageImageNotDefault" after="compareGrabSimpleProduct1PageImageSrc"/> + <assertNotRegExp stepKey="compareAssertSimpleProduct2PageImageNotDefault" after="compareGrabSimpleProduct1PageImageSrc"> + <actualResult type="const">$compareGrabSimpleProduct1PageImageSrc</actualResult> + <expectedResult type="const">'/placeholder\/image\.jpg/'</expectedResult> + </assertNotRegExp> <actionGroup ref="StorefrontAddProductToCompareActionGroup" stepKey="compareAddSimpleProduct1ToCompare" after="compareAssertSimpleProduct2PageImageNotDefault"> <argument name="productVar" value="$$createSimpleProduct1$$"/> </actionGroup> @@ -141,7 +159,10 @@ </actionGroup> <!-- @TODO: Move Image check to action group after MQE-697 is fixed --> <grabAttributeFrom selector="{{StorefrontCategoryProductSection.ProductImageByName($$createSimpleProduct2.name$$)}}" userInput="src" stepKey="compareGrabSimpleProduct2ImageSrc" after="compareAssertSimpleProduct2"/> - <assertNotRegExp expected="'/placeholder\/small_image\.jpg/'" actual="$compareGrabSimpleProduct2ImageSrc" stepKey="compareAssertSimpleProduct2ImageNotDefault" after="compareGrabSimpleProduct2ImageSrc"/> + <assertNotRegExp stepKey="compareAssertSimpleProduct2ImageNotDefault" after="compareGrabSimpleProduct2ImageSrc"> + <actualResult type="const">$compareGrabSimpleProduct2ImageSrc</actualResult> + <expectedResult type="const">'/placeholder\/small_image\.jpg/'</expectedResult> + </assertNotRegExp> <actionGroup ref="StorefrontAddCategoryProductToCompareActionGroup" stepKey="compareAddSimpleProduct2ToCompare" after="compareAssertSimpleProduct2ImageNotDefault"> <argument name="productVar" value="$$createSimpleProduct2$$"/> </actionGroup> @@ -166,14 +187,20 @@ </actionGroup> <!-- @TODO: Move Image check to action group after MQE-697 is fixed --> <grabAttributeFrom selector="{{StorefrontProductCompareMainSection.ProductImageByName($$createSimpleProduct1.name$$)}}" userInput="src" stepKey="compareGrabSimpleProduct1ImageSrcInComparison" after="compareAssertSimpleProduct1InComparison"/> - <assertNotRegExp expected="'/placeholder\/small_image\.jpg/'" actual="$compareGrabSimpleProduct1ImageSrcInComparison" stepKey="compareAssertSimpleProduct1ImageNotDefaultInComparison" after="compareGrabSimpleProduct1ImageSrcInComparison"/> + <assertNotRegExp stepKey="compareAssertSimpleProduct1ImageNotDefaultInComparison" after="compareGrabSimpleProduct1ImageSrcInComparison"> + <actualResult type="const">$compareGrabSimpleProduct1ImageSrcInComparison</actualResult> + <expectedResult type="const">'/placeholder\/small_image\.jpg/'</expectedResult> + </assertNotRegExp> <!-- Check simple product2 on comparison page --> <actionGroup ref="StorefrontCheckCompareSimpleProductActionGroup" stepKey="compareAssertSimpleProduct2InComparison" after="compareAssertSimpleProduct1ImageNotDefaultInComparison"> <argument name="productVar" value="$$createSimpleProduct2$$"/> </actionGroup> <!-- @TODO: Move Image check to action group after MQE-697 is fixed --> <grabAttributeFrom selector="{{StorefrontProductCompareMainSection.ProductImageByName($$createSimpleProduct2.name$$)}}" userInput="src" stepKey="compareGrabSimpleProduct2ImageSrcInComparison" after="compareAssertSimpleProduct2InComparison"/> - <assertNotRegExp expected="'/placeholder\/small_image\.jpg/'" actual="$compareGrabSimpleProduct2ImageSrcInComparison" stepKey="compareAssertSimpleProduct2ImageNotDefaultInComparison" after="compareGrabSimpleProduct2ImageSrcInComparison"/> + <assertNotRegExp stepKey="compareAssertSimpleProduct2ImageNotDefaultInComparison" after="compareGrabSimpleProduct2ImageSrcInComparison"> + <actualResult type="const">$compareGrabSimpleProduct2ImageSrcInComparison</actualResult> + <expectedResult type="const">'/placeholder\/small_image\.jpg/'</expectedResult> + </assertNotRegExp> <!-- Clear comparison sidebar --> <comment userInput="Clear comparison sidebar" stepKey="commentClearComparisonSidebar" after="compareAssertSimpleProduct2ImageNotDefaultInComparison"/> diff --git a/app/code/Magento/Catalog/Test/Mftf/Test/ProductAttributeWithoutValueInCompareListTest.xml b/app/code/Magento/Catalog/Test/Mftf/Test/ProductAttributeWithoutValueInCompareListTest.xml new file mode 100644 index 0000000000000..916bcd7405ecf --- /dev/null +++ b/app/code/Magento/Catalog/Test/Mftf/Test/ProductAttributeWithoutValueInCompareListTest.xml @@ -0,0 +1,82 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + /** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +--> + +<tests xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/testSchema.xsd"> + <test name="ProductAttributeWithoutValueInCompareListTest"> + <annotations> + <features value="Catalog"/> + <stories value="Product Comparison"/> + <title value="Product attribute without value in compare list test"/> + <description value="The product attribute that has no value should output 'N/A' on the product comparison page."/> + <severity value="MINOR"/> + <group value="Catalog"/> + </annotations> + <before> + <actionGroup ref="AdminLoginActionGroup" stepKey="loginAsAdmin"/> + <createData entity="textProductAttribute" stepKey="createProductAttribute"/> + <createData entity="CatalogAttributeSet" stepKey="createAttributeSet"/> + <amOnPage url="{{AdminProductAttributeSetEditPage.url}}/$$createAttributeSet.attribute_set_id$$/" + stepKey="onAttributeSetEdit"/> + <actionGroup ref="AssignAttributeToGroupActionGroup" stepKey="assignAttributeToGroup"> + <argument name="group" value="Product Details"/> + <argument name="attribute" value="$$createProductAttribute.attribute_code$$"/> + </actionGroup> + <actionGroup ref="SaveAttributeSetActionGroup" stepKey="SaveAttributeSet"/> + <createData entity="_defaultCategory" stepKey="createCategory"/> + <createData entity="_defaultProduct" stepKey="createProductDefault"> + <requiredEntity createDataKey="createCategory"/> + </createData> + <createData entity="SimpleProductWithCustomAttributeSet" stepKey="createProductCustom"> + <requiredEntity createDataKey="createCategory"/> + <requiredEntity createDataKey="createAttributeSet"/> + </createData> + </before> + <after> + <deleteData createDataKey="createProductDefault" stepKey="deleteProductDefault"/> + <deleteData createDataKey="createProductCustom" stepKey="deleteProductCustom"/> + <deleteData createDataKey="createCategory" stepKey="deleteCategory"/> + <deleteData createDataKey="createAttributeSet" stepKey="deleteAttributeSet"/> + <deleteData createDataKey="createProductAttribute" stepKey="deleteProductAttribute"/> + <actionGroup ref="AdminLogoutActionGroup" stepKey="logout"/> + + <!-- Reindex invalidated indices after product attribute has been created/deleted --> + <magentoCron groups="index" stepKey="reindexInvalidatedIndices"/> + </after> + <!--Open product page--> + <amOnPage url="{{StorefrontProductPage.url($$createProductDefault.name$$)}}" stepKey="goToProductDefaultPage"/> + <waitForPageLoad stepKey="waitForProductPage"/> + <!--Click on 'Add to Compare' link--> + <actionGroup ref="StorefrontAddProductToCompareActionGroup" stepKey="clickOnAddToCompare"> + <argument name="productVar" value="$$createProductDefault$$"/> + </actionGroup> + <!--See product in the comparison list--> + <actionGroup ref="SeeProductInComparisonListActionGroup" stepKey="seeProductDefaultInComparisonListActionGroup"> + <argument name="productVar" value="$$createProductDefault$$"/> + </actionGroup> + <!--Open product with custom attribute page--> + <amOnPage url="{{StorefrontProductPage.url($$createProductCustom.name$$)}}" stepKey="goToProductCustomPage"/> + <waitForPageLoad stepKey="waitForProductCustomPage"/> + <!--Click on 'Add to Compare' link--> + <actionGroup ref="StorefrontAddProductToCompareActionGroup" stepKey="clickOnAddToCompareCustom"> + <argument name="productVar" value="$$createProductCustom$$"/> + </actionGroup> + <!--See product with custom attribute in the comparison list--> + <actionGroup ref="SeeProductInComparisonListActionGroup" stepKey="seeProductCustomInComparisonListActionGroup"> + <argument name="productVar" value="$$createProductCustom$$"/> + </actionGroup> + <!--See attribute default value in the comparison list--> + <see userInput="$createProductAttribute.defaultValue$" + selector="{{StorefrontProductCompareMainSection.ProductAttributeByCodeAndProductName(ProductAttributeFrontendLabel.label, $createProductCustom.name$)}}" + stepKey="assertAttributeValueForProductCustom"/> + <!--See N/A if attribute has no value in the comparison list--> + <see userInput="N/A" + selector="{{StorefrontProductCompareMainSection.ProductAttributeByCodeAndProductName(ProductAttributeFrontendLabel.label, $createProductDefault.name$)}}" + stepKey="assertNAForProductDefault"/> + </test> +</tests> diff --git a/app/code/Magento/Catalog/Test/Mftf/Test/ProductAvailableAfterEnablingSubCategoriesTest.xml b/app/code/Magento/Catalog/Test/Mftf/Test/ProductAvailableAfterEnablingSubCategoriesTest.xml index c8a7cdee66b53..ea9a4cb08bb08 100644 --- a/app/code/Magento/Catalog/Test/Mftf/Test/ProductAvailableAfterEnablingSubCategoriesTest.xml +++ b/app/code/Magento/Catalog/Test/Mftf/Test/ProductAvailableAfterEnablingSubCategoriesTest.xml @@ -14,7 +14,7 @@ <stories value="Categories"/> <title value="Check that parent categories are showing products after enabling subcategories after fully reindex"/> <description value="Check that parent categories are showing products after enabling subcategories after fully reindex"/> - <severity value="MAJOR"/> + <severity value="BLOCKER"/> <testCaseId value="MAGETWO-97370"/> <useCaseId value="MAGETWO-96846"/> <group value="Catalog"/> @@ -29,12 +29,12 @@ <requiredEntity createDataKey="simpleSubCategory"/> </createData> - <actionGroup ref="LoginAsAdmin" stepKey="loginAsAdmin"/> + <actionGroup ref="AdminLoginActionGroup" stepKey="loginAsAdmin"/> </before> <after> <deleteData createDataKey="createSimpleProduct" stepKey="deleteSimpleProduct"/> <deleteData createDataKey="createCategory" stepKey="deleteCategory"/> - <actionGroup ref="logout" stepKey="logout"/> + <actionGroup ref="AdminLogoutActionGroup" stepKey="logout"/> </after> <amOnPage url="$$createCategory.name$$.html" stepKey="goToCategoryStorefront2"/> diff --git a/app/code/Magento/Catalog/Test/Mftf/Test/SaveProductWithCustomOptionsSecondWebsiteTest.xml b/app/code/Magento/Catalog/Test/Mftf/Test/SaveProductWithCustomOptionsSecondWebsiteTest.xml index 89e784d67eaea..b206a33ebde88 100644 --- a/app/code/Magento/Catalog/Test/Mftf/Test/SaveProductWithCustomOptionsSecondWebsiteTest.xml +++ b/app/code/Magento/Catalog/Test/Mftf/Test/SaveProductWithCustomOptionsSecondWebsiteTest.xml @@ -14,12 +14,12 @@ <stories value="Purchase a product with Custom Options of different types"/> <title value="You should be able to save a product with custom options assigned to a different website"/> <description value="Custom Options should not be split when saving the product after assigning to a different website"/> - <severity value="MAJOR"/> + <severity value="BLOCKER"/> <testCaseId value="MAGETWO-91436"/> <group value="product"/> </annotations> <before> - <actionGroup ref="LoginAsAdmin" stepKey="loginAsAdmin"/> + <actionGroup ref="AdminLoginActionGroup" stepKey="loginAsAdmin"/> <!--Create new website --> <actionGroup ref="AdminCreateWebsiteActionGroup" stepKey="createAdditionalWebsite"> <argument name="newWebsiteName" value="Second Website"/> @@ -53,7 +53,7 @@ <actionGroup ref="AdminDeleteWebsiteActionGroup" stepKey="deleteTestWebsite"> <argument name="websiteName" value="Second Website"/> </actionGroup> - <actionGroup ref="logout" stepKey="adminLogout"/> + <actionGroup ref="AdminLogoutActionGroup" stepKey="adminLogout"/> </after> <actionGroup ref="EnableWebUrlOptionsActionGroup" stepKey="addStoreCodeToUrls"/> diff --git a/app/code/Magento/Catalog/Test/Mftf/Test/SimpleProductTwoCustomOptionsTest.xml b/app/code/Magento/Catalog/Test/Mftf/Test/SimpleProductTwoCustomOptionsTest.xml index 41bacc69baca4..7b2e004495fea 100644 --- a/app/code/Magento/Catalog/Test/Mftf/Test/SimpleProductTwoCustomOptionsTest.xml +++ b/app/code/Magento/Catalog/Test/Mftf/Test/SimpleProductTwoCustomOptionsTest.xml @@ -14,13 +14,13 @@ <stories value="Create simple product with two custom options" /> <title value="Admin should be able to create simple product with two custom options"/> <description value="Admin should be able to create simple product with two custom options"/> - <severity value="AVERAGE"/> + <severity value="BLOCKER"/> <testCaseId value="MC-248"/> <group value="Catalog"/> </annotations> <before> <!-- log in as admin --> - <actionGroup ref="LoginAsAdmin" stepKey="loginAsAdmin"/> + <actionGroup ref="AdminLoginActionGroup" stepKey="loginAsAdmin"/> <!--Create product--> <amOnPage url="{{AdminProductIndexPage.url}}" stepKey="navigateToProductIndex"/> diff --git a/app/code/Magento/Catalog/Test/Mftf/Test/StoreFrontProductsDisplayUsingElasticSearchTest.xml b/app/code/Magento/Catalog/Test/Mftf/Test/StoreFrontProductsDisplayUsingElasticSearchTest.xml new file mode 100644 index 0000000000000..e109dcb0deea5 --- /dev/null +++ b/app/code/Magento/Catalog/Test/Mftf/Test/StoreFrontProductsDisplayUsingElasticSearchTest.xml @@ -0,0 +1,203 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + /** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +--> + +<tests xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/testSchema.xsd"> + <test name="StoreFrontProductsDisplayUsingElasticSearchTest"> + <annotations> + <stories value="Display All Products"/> + <title value="Display All Products on a Page"/> + <description value="Set Up Elastic Search and Display all Products on Page"/> + <testCaseId value="MC-30209"/> + <severity value="CRITICAL"/> + <group value="Catalog"/> + <group value="SearchEngineElasticsearch"/> + </annotations> + + <before> + <!--Create Category and Simple Products--> + <createData entity="SimpleSubCategory" stepKey="createCategory1"/> + <createData entity="SimpleProduct" stepKey="createSimpleProduct"> + <requiredEntity createDataKey="createCategory1"/> + </createData> + <createData entity="SimpleProduct" stepKey="createSimpleProduct2"> + <requiredEntity createDataKey="createCategory1"/> + </createData> + <createData entity="SimpleProduct" stepKey="createSimpleProduct3"> + <requiredEntity createDataKey="createCategory1"/> + </createData> + <createData entity="SimpleProduct" stepKey="createSimpleProduct4"> + <requiredEntity createDataKey="createCategory1"/> + </createData> + <createData entity="SimpleProduct" stepKey="createSimpleProduct5"> + <requiredEntity createDataKey="createCategory1"/> + </createData> + <createData entity="SimpleProduct" stepKey="createSimpleProduct6"> + <requiredEntity createDataKey="createCategory1"/> + </createData> + <createData entity="SimpleProduct" stepKey="createSimpleProduct7"> + <requiredEntity createDataKey="createCategory1"/> + </createData> + <createData entity="SimpleProduct" stepKey="createSimpleProduct8"> + <requiredEntity createDataKey="createCategory1"/> + </createData> + <createData entity="SimpleProduct" stepKey="createSimpleProduct9"> + <requiredEntity createDataKey="createCategory1"/> + </createData> + <createData entity="SimpleProduct" stepKey="createSimpleProduct10"> + <requiredEntity createDataKey="createCategory1"/> + </createData> + <createData entity="SimpleProduct" stepKey="createSimpleProduct11"> + <requiredEntity createDataKey="createCategory1"/> + </createData> + <createData entity="SimpleProduct" stepKey="createSimpleProduct12"> + <requiredEntity createDataKey="createCategory1"/> + </createData> + <createData entity="SimpleProduct" stepKey="createSimpleProduct13"> + <requiredEntity createDataKey="createCategory1"/> + </createData> + <createData entity="SimpleProduct" stepKey="createSimpleProduct14"> + <requiredEntity createDataKey="createCategory1"/> + </createData> + <createData entity="SimpleProduct" stepKey="createSimpleProduct15"> + <requiredEntity createDataKey="createCategory1"/> + </createData> + <createData entity="SimpleProduct" stepKey="createSimpleProduct16"> + <requiredEntity createDataKey="createCategory1"/> + </createData> + <createData entity="SimpleProduct" stepKey="createSimpleProduct17"> + <requiredEntity createDataKey="createCategory1"/> + </createData> + <createData entity="SimpleProduct" stepKey="createSimpleProduct18"> + <requiredEntity createDataKey="createCategory1"/> + </createData> + <createData entity="SimpleProduct" stepKey="createSimpleProduct19"> + <requiredEntity createDataKey="createCategory1"/> + </createData> + <createData entity="SimpleProduct" stepKey="createSimpleProduct20"> + <requiredEntity createDataKey="createCategory1"/> + </createData> + <createData entity="SimpleProduct" stepKey="createSimpleProduct21"> + <requiredEntity createDataKey="createCategory1"/> + </createData> + <createData entity="SimpleProduct" stepKey="createSimpleProduct22"> + <requiredEntity createDataKey="createCategory1"/> + </createData> + <createData entity="SimpleProduct" stepKey="createSimpleProduct23"> + <requiredEntity createDataKey="createCategory1"/> + </createData> + <createData entity="SimpleProduct" stepKey="createSimpleProduct24"> + <requiredEntity createDataKey="createCategory1"/> + </createData> + <createData entity="SimpleProduct" stepKey="createSimpleProduct25"> + <requiredEntity createDataKey="createCategory1"/> + </createData> + <createData entity="SimpleProduct" stepKey="createSimpleProduct26"> + <requiredEntity createDataKey="createCategory1"/> + </createData> + <createData entity="SimpleProduct" stepKey="createSimpleProduct27"> + <requiredEntity createDataKey="createCategory1"/> + </createData> + <createData entity="SimpleProduct" stepKey="createSimpleProduct28"> + <requiredEntity createDataKey="createCategory1"/> + </createData> + <createData entity="SimpleProduct" stepKey="createSimpleProduct29"> + <requiredEntity createDataKey="createCategory1"/> + </createData> + <createData entity="SimpleProduct" stepKey="createSimpleProduct30"> + <requiredEntity createDataKey="createCategory1"/> + </createData> + + <magentoCron groups="index" stepKey="reindexInvalidatedIndices"/> + <magentoCLI command="indexer:reindex" arguments="catalogsearch_fulltext" stepKey="performReindex"/> + <magentoCLI command="cache:clean" arguments="full_page" stepKey="cleanFullPageCache"/> + </before> + + <after> + <!--Delete created products, category --> + <deleteData createDataKey="createCategory1" stepKey="deleteCategory"/> + <deleteData createDataKey="createSimpleProduct" stepKey="deleteSimpleProduct1"/> + <deleteData createDataKey="createSimpleProduct2" stepKey="deleteSimpleProduct2"/> + <deleteData createDataKey="createSimpleProduct3" stepKey="deleteSimpleProduct3"/> + <deleteData createDataKey="createSimpleProduct4" stepKey="deleteSimpleProduct4"/> + <deleteData createDataKey="createSimpleProduct5" stepKey="deleteSimpleProduct5"/> + <deleteData createDataKey="createSimpleProduct6" stepKey="deleteSimpleProduct6"/> + <deleteData createDataKey="createSimpleProduct7" stepKey="deleteSimpleProduct7"/> + <deleteData createDataKey="createSimpleProduct8" stepKey="deleteSimpleProduct8"/> + <deleteData createDataKey="createSimpleProduct9" stepKey="deleteSimpleProduct9"/> + <deleteData createDataKey="createSimpleProduct10" stepKey="deleteSimpleProduct10"/> + <deleteData createDataKey="createSimpleProduct11" stepKey="deleteSimpleProduct11"/> + <deleteData createDataKey="createSimpleProduct12" stepKey="deleteSimpleProduct12"/> + <deleteData createDataKey="createSimpleProduct13" stepKey="deleteSimpleProduct13"/> + <deleteData createDataKey="createSimpleProduct14" stepKey="deleteSimpleProduct14"/> + <deleteData createDataKey="createSimpleProduct15" stepKey="deleteSimpleProduct15"/> + <deleteData createDataKey="createSimpleProduct16" stepKey="deleteSimpleProduct16"/> + <deleteData createDataKey="createSimpleProduct17" stepKey="deleteSimpleProduct17"/> + <deleteData createDataKey="createSimpleProduct18" stepKey="deleteSimpleProduct18"/> + <deleteData createDataKey="createSimpleProduct19" stepKey="deleteSimpleProduct19"/> + <deleteData createDataKey="createSimpleProduct20" stepKey="deleteSimpleProduct20"/> + <deleteData createDataKey="createSimpleProduct21" stepKey="deleteSimpleProduct21"/> + <deleteData createDataKey="createSimpleProduct22" stepKey="deleteSimpleProduct22"/> + <deleteData createDataKey="createSimpleProduct23" stepKey="deleteSimpleProduct23"/> + <deleteData createDataKey="createSimpleProduct24" stepKey="deleteSimpleProduct24"/> + <deleteData createDataKey="createSimpleProduct25" stepKey="deleteSimpleProduct25"/> + <deleteData createDataKey="createSimpleProduct26" stepKey="deleteSimpleProduct26"/> + <deleteData createDataKey="createSimpleProduct27" stepKey="deleteSimpleProduct27"/> + <deleteData createDataKey="createSimpleProduct28" stepKey="deleteSimpleProduct28"/> + <deleteData createDataKey="createSimpleProduct29" stepKey="deleteSimpleProduct29"/> + <deleteData createDataKey="createSimpleProduct30" stepKey="deleteSimpleProduct30"/> + </after> + + <!--Open Storefront on the myCategory page--> + <actionGroup ref="StorefrontNavigateCategoryPageActionGroup" stepKey="goToStorefrontCategory"> + <argument name="category" value="$createCategory1$"/> + </actionGroup> + <waitForPageLoad stepKey="waitForStorefrontCategoryPageLoad"/> + + <!--Select 12 items per page and verify number of products displayed in each page --> + <conditionalClick selector="{{StorefrontCategoryTopToolbarSection.gridMode}}" visible="true" dependentSelector="{{StorefrontCategoryTopToolbarSection.gridMode}}" stepKey="seeProductGridIsActive"/> + <scrollTo selector="{{StorefrontCategoryBottomToolbarSection.perPage}}" stepKey="scrollToBottomToolbarSection"/> + <selectOption selector="{{StorefrontCategoryBottomToolbarSection.perPage}}" userInput="12" stepKey="selectPerPageOption"/> + <!--Verify number of products displayed in First Page --> + <seeNumberOfElements selector="{{StorefrontCategoryMainSection.productLink}}" userInput="12" stepKey="seeNumberOfProductsInFirstPage"/> + <!--Verify number of products displayed in Second Page --> + <scrollTo selector="{{StorefrontCategoryBottomToolbarSection.nextPage}}" stepKey="scrollToNextButton"/> + <click selector="{{StorefrontCategoryBottomToolbarSection.nextPage}}" stepKey="clickOnNextPage"/> + <waitForPageLoad stepKey="waitForPageToLoad4"/> + <seeNumberOfElements selector="{{StorefrontCategoryMainSection.productLink}}" userInput="12" stepKey="seeNumberOfProductsInSecondPage"/> + <!--Verify number of products displayed in third Page --> + <scrollTo selector="{{StorefrontCategoryBottomToolbarSection.nextPage}}" stepKey="scrollToNextButton1"/> + <click selector="{{StorefrontCategoryBottomToolbarSection.nextPage}}" stepKey="clickOnNextPage1"/> + <waitForPageLoad stepKey="waitForPageToLoad2"/> + <seeNumberOfElements selector="{{StorefrontCategoryMainSection.productLink}}" userInput="6" stepKey="seeNumberOfProductsInThirdPage"/> + + <!--Select First Page using page number--> + <scrollTo selector="{{StorefrontCategoryBottomToolbarSection.previousPage}}" stepKey="scrollToPreviousPage4"/> + <click selector="{{StorefrontCategoryBottomToolbarSection.pageNumber('1')}}" stepKey="clickOnFirstPage"/> + <waitForPageLoad stepKey="waitForPageToLoad9"/> + <seeNumberOfElements selector="{{StorefrontCategoryMainSection.productLink}}" userInput="12" stepKey="seeNumberOfProductsFirstPage2"/> + <!--Select 24 items per page and verify number of products displayed in each page --> + <scrollTo selector="{{StorefrontCategoryBottomToolbarSection.perPage}}" stepKey="scrollToPerPage"/> + <selectOption selector="{{StorefrontCategoryBottomToolbarSection.perPage}}" userInput="24" stepKey="selectPerPageOption1"/> + <waitForPageLoad stepKey="waitForPageToLoad10"/> + <seeNumberOfElements selector="{{StorefrontCategoryMainSection.productLink}}" userInput="24" stepKey="seeNumberOfProductsInFirstPage3"/> + <scrollTo selector="{{StorefrontCategoryBottomToolbarSection.nextPage}}" stepKey="scrollToNextButton2"/> + <click selector="{{StorefrontCategoryBottomToolbarSection.nextPage}}" stepKey="clickOnNextPage2"/> + <waitForPageLoad stepKey="waitForPageToLoad11"/> + <seeNumberOfElements selector="{{StorefrontCategoryMainSection.productLink}}" userInput="6" stepKey="seeNumberOfProductsInSecondPage3"/> + <!--Select First Page using page number--> + <scrollTo selector="{{StorefrontCategoryBottomToolbarSection.pageNumber('1')}}" stepKey="scrollToPreviousPage5"/> + <click selector="{{StorefrontCategoryBottomToolbarSection.pageNumber('1')}}" stepKey="clickOnFirstPage2"/> + <waitForPageLoad stepKey="waitForPageToLoad13"/> + <!--Select 36 items per page and verify number of products displayed in each page --> + <scrollTo selector="{{StorefrontCategoryBottomToolbarSection.perPage}}" stepKey="scrollToPerPage4"/> + <selectOption selector="{{StorefrontCategoryBottomToolbarSection.perPage}}" userInput="36" stepKey="selectPerPageOption2"/> + <waitForPageLoad stepKey="waitForPageToLoad12"/> + <seeNumberOfElements selector="{{StorefrontCategoryMainSection.productLink}}" userInput="30" stepKey="seeNumberOfProductsInFirstPage4"/> + </test> +</tests> diff --git a/app/code/Magento/Catalog/Test/Mftf/Test/StoreFrontRecentlyComparedAtWebsiteLevelTest.xml b/app/code/Magento/Catalog/Test/Mftf/Test/StoreFrontRecentlyComparedAtWebsiteLevelTest.xml new file mode 100644 index 0000000000000..51150a4ecffb3 --- /dev/null +++ b/app/code/Magento/Catalog/Test/Mftf/Test/StoreFrontRecentlyComparedAtWebsiteLevelTest.xml @@ -0,0 +1,106 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + /** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +--> +<tests xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/testSchema.xsd"> + <test name="StoreFrontRecentlyComparedAtWebsiteLevelTest"> + <annotations> + <stories value="Recently Compared Product"/> + <title value="Recently Compared Product at website level"/> + <description value="Recently Compared Products widget appears on a page immediately after adding product to compare"/> + <useCaseId value="MC-32763"/> + <testCaseId value="MC-33099"/> + <severity value="MAJOR"/> + <group value="catalog"/> + <group value="widget"/> + </annotations> + <before> + <!--Create Simple Products and Category --> + <createData entity="SimpleSubCategory" stepKey="createCategory"/> + <createData entity="SimpleProduct" stepKey="createSimpleProductToCompareFirst"> + <requiredEntity createDataKey="createCategory"/> + </createData> + <createData entity="SimpleProduct" stepKey="createSimpleProductToCompareSecond"> + <requiredEntity createDataKey="createCategory"/> + </createData> + <createData entity="SimpleProduct" stepKey="createSimpleProductNotVisibleFirst"> + <requiredEntity createDataKey="createCategory"/> + </createData> + <createData entity="SimpleProduct" stepKey="createSimpleProductNotVisibleSecond"> + <requiredEntity createDataKey="createCategory"/> + </createData> + <createData entity="Simple_US_Customer" stepKey="createCustomer"/> + <!-- Login as admin --> + <actionGroup ref="AdminLoginActionGroup" stepKey="loginAsAdmin"/> + <!-- Create product widget --> + <actionGroup ref="AdminCreateRecentlyProductsWidgetActionGroup" stepKey="createRecentlyComparedProductsWidget"> + <argument name="widget" value="RecentlyComparedProductsWidget"/> + </actionGroup> + <!-- Set Stores > Configurations > Catalog > Recently Viewed/Compared Products > Show for Current = Website --> + <magentoCLI command="config:set {{RecentlyViewedProductScopeWebsite.path}} {{RecentlyViewedProductScopeWebsite.value}}" stepKey="RecentlyViewedProductScopeWebsiteGroup"/> + </before> + <after> + <!-- Customer Logout --> + <actionGroup ref="StorefrontCustomerLogoutActionGroup" stepKey="logoutFromCustomer"/> + <!-- Delete product widget --> + <actionGroup ref="AdminDeleteWidgetActionGroup" stepKey="deleteRecentlyComparedProductsWidget"> + <argument name="widget" value="RecentlyComparedProductsWidget"/> + </actionGroup> + <!-- Logout Admin --> + <actionGroup ref="AdminLogoutActionGroup" stepKey="logout"/> + <!-- Reset Stores > Configurations > Catalog > Recently Viewed/Compared Products > Show for Current = Website--> + <magentoCLI command="config:set {{RecentlyViewedProductScopeWebsite.path}} {{RecentlyViewedProductScopeWebsite.value}}" stepKey="RecentlyViewedProductScopeWebsite"/> + <!-- Delete Products and Category --> + <deleteData createDataKey="createSimpleProductToCompareFirst" stepKey="deleteSimpleProductToCompareFirst"/> + <deleteData createDataKey="createSimpleProductToCompareSecond" stepKey="deleteSimpleProductToCompareSecond"/> + <deleteData createDataKey="createSimpleProductNotVisibleFirst" stepKey="deleteSimpleProductNotVisibleFirst"/> + <deleteData createDataKey="createSimpleProductNotVisibleSecond" stepKey="deleteSimpleProductNotVisibleSecond"/> + <deleteData createDataKey="createCategory" stepKey="deleteCategory"/> + <deleteData createDataKey="createCustomer" stepKey="deleteCustomer"/> + </after> + <!--Login to storefront from customer--> + <actionGroup ref="LoginToStorefrontActionGroup" stepKey="loginCustomer"> + <argument name="Customer" value="$$createCustomer$$"/> + </actionGroup> + <see userInput="Welcome, $$createCustomer.firstname$$ $$createCustomer.lastname$$!" selector="{{StorefrontPanelHeaderSection.welcomeMessage}}" stepKey="checkWelcomeMessage"/> + <amOnPage url="{{StorefrontCategoryPage.url($$createCategory.custom_attributes[url_key]$$)}}" stepKey="openCategoryPageAfterAddedProductToCart"/> + <!--Add to compare Simple Product and Simple Product 2--> + <actionGroup ref="StorefrontAddCategoryProductToCompareActionGroup" stepKey="addSimpleProduct1ToCompare" > + <argument name="productVar" value="$$createSimpleProductToCompareFirst$$"/> + </actionGroup> + <actionGroup ref="StorefrontAddCategoryProductToCompareActionGroup" stepKey="addSimpleProduct2ToCompare" > + <argument name="productVar" value="$$createSimpleProductToCompareSecond$$"/> + </actionGroup> + <!--The Compare Products widget displays Simple Product 1 and Simple Product 2--> + <actionGroup ref="StorefrontCheckCompareSidebarProductActionGroup" stepKey="checkSimpleProduct1InCompareSidebar"> + <argument name="productVar" value="$$createSimpleProductToCompareFirst$$"/> + </actionGroup> + <actionGroup ref="StorefrontCheckCompareSidebarProductActionGroup" stepKey="checkSimpleProduct2InCompareSidebar"> + <argument name="productVar" value="$$createSimpleProductToCompareSecond$$"/> + </actionGroup> + + <!--Click Clear all in the Compare Products widget--> + <actionGroup ref="StorefrontClearCompareActionGroup" stepKey="clearCompareList"/> + <!--The Recently Compared widget displays Simple Product 1 and Simple Product 2--> + <amOnPage url="{{StorefrontCategoryPage.url($$createCategory.custom_attributes[url_key]$$)}}" stepKey="openCategoryPageToCheckProductsInRecentlyComparedSidebar"/> + <actionGroup ref="StorefrontAssertProductInRecentlyComparedWidgetActionGroup" stepKey="checkSimpleProduct1ExistInRecentlyComparedWidget"> + <argument name="product" value="$$createSimpleProductToCompareFirst$$"/> + </actionGroup> + <actionGroup ref="StorefrontAssertProductInRecentlyComparedWidgetActionGroup" stepKey="checkSimpleProduct2ExistInRecentlyComparedWidget"> + <argument name="product" value="$$createSimpleProductToCompareSecond$$"/> + </actionGroup> + <!--The Recently Compared widget not displays Simple Product 3 and Simple Product 4--> + <actionGroup ref="StorefrontAssertNotExistProductInRecentlyComparedWidgetActionGroup" stepKey="checkSimpleProduct3NotExistInRecentlyComparedWidget"> + <argument name="product" value="$$createSimpleProductNotVisibleFirst$$"/> + </actionGroup> + <actionGroup ref="StorefrontAssertNotExistProductInRecentlyComparedWidgetActionGroup" stepKey="checkSimpleProduct4NotExistInRecentlyComparedWidget"> + <argument name="product" value="$$createSimpleProductNotVisibleSecond$$"/> + </actionGroup> + <amOnPage url="customer/account/logout/" stepKey="logoutCustomer"/> + <waitForPageLoad time="30" stepKey="waitForPageLoad2"/> + </test> +</tests> diff --git a/app/code/Magento/Catalog/Test/Mftf/Test/StoreFrontRecentlyViewedAtStoreLevelTest.xml b/app/code/Magento/Catalog/Test/Mftf/Test/StoreFrontRecentlyViewedAtStoreLevelTest.xml new file mode 100644 index 0000000000000..ff7c6c58a219c --- /dev/null +++ b/app/code/Magento/Catalog/Test/Mftf/Test/StoreFrontRecentlyViewedAtStoreLevelTest.xml @@ -0,0 +1,174 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + /** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +--> +<tests xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/testSchema.xsd"> + <test name="StoreFrontRecentlyViewedAtStoreLevelTest"> + <annotations> + <stories value="Recently Viewed Product"/> + <title value="Recently Viewed Product at store level"/> + <description value="Recently Viewed Product should not be displayed on second store , if configured as, Per Store "/> + <testCaseId value="MC-32226"/> + <severity value="CRITICAL"/> + <group value="catalog"/> + <group value="WYSIWYGDisabled"/> + </annotations> + <before> + <actionGroup ref="AdminLoginActionGroup" stepKey="loginAsAdmin"/> + <!--Create Simple Product and Category --> + <createData entity="SimpleSubCategory" stepKey="createCategory"/> + <createData entity="SimpleProduct" stepKey="createSimpleProduct1"> + <requiredEntity createDataKey="createCategory"/> + </createData> + <createData entity="SimpleProduct" stepKey="createSimpleProduct2"> + <requiredEntity createDataKey="createCategory"/> + </createData> + <createData entity="SimpleProduct" stepKey="createSimpleProduct3"> + <requiredEntity createDataKey="createCategory"/> + </createData> + <createData entity="SimpleProduct" stepKey="createSimpleProduct4"> + <requiredEntity createDataKey="createCategory"/> + </createData> + <!-- Create store1 for default website --> + <actionGroup ref="AdminCreateNewStoreGroupActionGroup" stepKey="createFirstStore"> + <argument name="website" value="{{_defaultWebsite.name}}"/> + <argument name="storeGroupName" value="{{customStore.name}}"/> + <argument name="storeGroupCode" value="{{customStore.code}}"/> + </actionGroup> + <!-- Create Storeview1 for Store1--> + <actionGroup ref="AdminCreateStoreViewActionGroup" stepKey="createStoreView"> + <argument name="StoreGroup" value="customStore"/> + <argument name="customStore" value="storeViewData"/> + </actionGroup> + <!--Create storeView 2--> + <actionGroup ref="AdminCreateStoreViewActionGroup" stepKey="createStoreViewTwo"> + <argument name="StoreGroup" value="customStore"/> + <argument name="customStore" value="customStoreEN"/> + </actionGroup> + <!-- Set Stores > Configurations > Catalog > Recently Viewed/Compared Products > Show for Current = store --> + <magentoCLI command="config:set {{RecentlyViewedProductScopeStoreGroup.path}} {{RecentlyViewedProductScopeStoreGroup.value}}" stepKey="RecentlyViewedProductScopeStoreGroup"/> + </before> + <after> + <!-- Delete Product and Category --> + <deleteData createDataKey="createSimpleProduct1" stepKey="deleteSimpleProduct1"/> + <deleteData createDataKey="createSimpleProduct2" stepKey="deleteSimpleProduct2"/> + <deleteData createDataKey="createSimpleProduct3" stepKey="deleteSimpleProduct3"/> + <deleteData createDataKey="createSimpleProduct4" stepKey="deleteSimpleProduct4"/> + <deleteData createDataKey="createCategory" stepKey="deleteCategory"/> + <!-- Delete store1 for default website --> + <actionGroup ref="DeleteCustomStoreActionGroup" stepKey="deleteFirstStore"> + <argument name="storeGroupName" value="customStore.name"/> + </actionGroup> + + <!--Reset Stores > Configurations > Catalog > Recently Viewed/Compared Products > Show for Current = Website--> + <magentoCLI command="config:set {{RecentlyViewedProductScopeWebsite.path}} {{RecentlyViewedProductScopeWebsite.value}}" stepKey="RecentlyViewedProductScopeWebsite"/> + + <!-- Clear Widget--> + <actionGroup ref="AdminEditCMSPageContentActionGroup" stepKey="clearRecentlyViewedWidgetsFromCMSContent"> + <argument name="content" value="{{CmsHomePageContent.content}}"/> + <argument name="pageId" value="{{CmsHomePageContent.page_id}}"/> + </actionGroup> + <!-- Logout Admin --> + <actionGroup ref="AdminLogoutActionGroup" stepKey="logout"/> + <magentoCLI command="indexer:reindex" stepKey="reindex"/> + <magentoCLI command="cache:flush" stepKey="flushCacheAfterDeletion"/> + </after> + <!--Create widget for recently viewed products--> + <actionGroup ref="AdminEditCMSPageContentActionGroup" stepKey="clearRecentlyViewedWidgetsFromCMSContentBefore"> + <argument name="content" value="{{CmsHomePageContent.content}}"/> + <argument name="pageId" value="{{CmsHomePageContent.page_id}}"/> + </actionGroup> + + <amOnPage url="{{AdminCmsPageEditPage.url(CmsHomePageContent.page_id)}}" stepKey="navigateToEditCmsHomePage"/> + <waitForPageLoad time="50" stepKey="waitForCmsContentPageToLoad"/> + + <actionGroup ref="AdminInsertRecentlyViewedWidgetActionGroup" stepKey="insertRecentlyViewedWidget"> + <argument name="attributeSelector1" value="show_attributes"/> + <argument name="attributeSelector2" value="show_buttons"/> + <argument name="productAttributeSection1" value="1"/> + <argument name="productAttributeSection2" value="4"/> + <argument name="buttonToShowSection1" value="1"/> + <argument name="buttonToShowSection2" value="3"/> + </actionGroup> + <!-- Warm up cache --> + <magentoCLI command="cache:flush" stepKey="flushCacheAfterWidgetCreated"/> + <!-- Navigate to product 3 on store front --> + <amOnPage url="{{StorefrontProductPage.url($createSimpleProduct2.name$)}}" stepKey="goToStoreOneProductPageTwo"/> + <amOnPage url="{{StorefrontProductPage.url($createSimpleProduct3.name$)}}" stepKey="goToStoreOneProductPageThree"/> + <!-- Go to Home Page --> + <actionGroup ref="StorefrontOpenHomePageActionGroup" stepKey="amOnStoreFrontHomePage"/> + <actionGroup ref="AssertSeeProductDetailsOnStorefrontRecentlyViewedWidgetActionGroup" stepKey="assertStoreOneRecentlyViewedProduct2"> + <argument name="productName" value="$$createSimpleProduct2.name$$"/> + <argument name="productPosition" value="2"/> + </actionGroup> + <actionGroup ref="AssertSeeProductDetailsOnStorefrontRecentlyViewedWidgetActionGroup" stepKey="assertStoreOneRecentlyViewedProduct3"> + <argument name="productName" value="$$createSimpleProduct3.name$$"/> + <argument name="productPosition" value="1"/> + </actionGroup> + <!-- Switch to second store and add second product (visible on second store) to wishlist --> + <click selector="{{StorefrontFooterSection.switchStoreButton}}" stepKey="clickSwitchStoreButtonOnDefaultStore"/> + <click selector="{{StorefrontFooterSection.storeLink(customStore.name)}}" stepKey="selectCustomStore"/> + <amOnPage url="{{StorefrontProductPage.url($createSimpleProduct1.name$)}}" stepKey="goToStore2ProductPage1"/> + <amOnPage url="{{StorefrontProductPage.url($createSimpleProduct2.name$)}}" stepKey="goToStore2ProductPage2"/> + <!-- Go to Home Page --> + <actionGroup ref="StorefrontOpenHomePageActionGroup" stepKey="amOnHomePage2"/> + <actionGroup ref="AssertSeeProductDetailsOnStorefrontRecentlyViewedWidgetActionGroup" stepKey="assertNextStore1RecentlyViewedProduct1"> + <argument name="productName" value="$$createSimpleProduct1.name$$"/> + <argument name="productPosition" value="2"/> + </actionGroup> + <actionGroup ref="AssertSeeProductDetailsOnStorefrontRecentlyViewedWidgetActionGroup" stepKey="assertNextStore1RecentlyViewedProduct2"> + <argument name="productName" value="$$createSimpleProduct2.name$$"/> + <argument name="productPosition" value="1"/> + </actionGroup> + + <grabTextFrom selector="{{StoreFrontRecentlyViewedProductSection.ProductName('2')}}" stepKey="grabDontSeeHomeProduct3"/> + <assertNotContains stepKey="assertNotSeeProduct3"> + <actualResult type="const">$grabDontSeeHomeProduct3</actualResult> + <expectedResult type="const">$$createSimpleProduct3.name$$</expectedResult> + </assertNotContains> + + <!-- Switch Storeview--> + <actionGroup ref="StorefrontSwitchStoreViewActionGroup" stepKey="switchStoreViewActionGroup"> + <argument name="storeView" value="customStoreEN"/> + </actionGroup> + <actionGroup ref="AssertSeeProductDetailsOnStorefrontRecentlyViewedWidgetActionGroup" stepKey="assertNextStoreView2RecentlyViewedProduct1"> + <argument name="productName" value="$$createSimpleProduct1.name$$"/> + <argument name="productPosition" value="2"/> + </actionGroup> + <actionGroup ref="AssertSeeProductDetailsOnStorefrontRecentlyViewedWidgetActionGroup" stepKey="assertNextStoreView2RecentlyViewedProduct2"> + <argument name="productName" value="$$createSimpleProduct2.name$$"/> + <argument name="productPosition" value="1"/> + </actionGroup> + + <grabTextFrom selector="{{StoreFrontRecentlyViewedProductSection.ProductName('2')}}" stepKey="grabStoreView2DontSeeHomeProduct3"/> + <assertNotContains stepKey="assertStoreView2NotSeeProduct3"> + <actualResult type="const">$grabDontSeeHomeProduct3</actualResult> + <expectedResult type="const">$$createSimpleProduct3.name$$</expectedResult> + </assertNotContains> + + <!-- Switch to default store--> + + <click selector="{{StorefrontFooterSection.switchStoreButton}}" stepKey="clickSwitchStoreButtonOnHomeDefaultStore"/> + <click selector="{{StorefrontFooterSection.storeLink('Main Website Store')}}" stepKey="selectDefaultStoreToSwitchOn"/> + + <actionGroup ref="AssertSeeProductDetailsOnStorefrontRecentlyViewedWidgetActionGroup" stepKey="assertSwitchStore1RecentlyViewedProduct2"> + <argument name="productName" value="$$createSimpleProduct2.name$$"/> + <argument name="productPosition" value="2"/> + </actionGroup> + + <actionGroup ref="AssertSeeProductDetailsOnStorefrontRecentlyViewedWidgetActionGroup" stepKey="assertSwitchStore1RecentlyViewedProduct3"> + <argument name="productName" value="$$createSimpleProduct3.name$$"/> + <argument name="productPosition" value="1"/> + </actionGroup> + <grabTextFrom selector="{{StoreFrontRecentlyViewedProductSection.ProductName('2')}}" stepKey="grabDontSeeHomeProduct1"/> + <assertNotContains stepKey="assertNotSeeProduct1"> + <actualResult type="const">$grabDontSeeHomeProduct1</actualResult> + <expectedResult type="const">$$createSimpleProduct1.name$$</expectedResult> + </assertNotContains> + + </test> +</tests> diff --git a/app/code/Magento/Catalog/Test/Mftf/Test/StoreFrontRecentlyViewedAtStoreViewLevelTest.xml b/app/code/Magento/Catalog/Test/Mftf/Test/StoreFrontRecentlyViewedAtStoreViewLevelTest.xml new file mode 100644 index 0000000000000..284b2f6eeb16a --- /dev/null +++ b/app/code/Magento/Catalog/Test/Mftf/Test/StoreFrontRecentlyViewedAtStoreViewLevelTest.xml @@ -0,0 +1,152 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + /** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +--> +<tests xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/testSchema.xsd"> + <test name="StoreFrontRecentlyViewedAtStoreViewLevelTest"> + <annotations> + <stories value="Recently Viewed Product"/> + <title value="Recently Viewed Product at store view level"/> + <description value="Recently Viewed Product should not be displayed on second store view, if configured as, Per Store View "/> + <testCaseId value="MC-32112"/> + <severity value="CRITICAL"/> + <group value="catalog"/> + <group value="WYSIWYGDisabled"/> + </annotations> + <before> + <actionGroup ref="AdminLoginActionGroup" stepKey="loginAsAdmin"/> + <!--Create Simple Product and Category --> + <createData entity="SimpleSubCategory" stepKey="createCategory"/> + <createData entity="SimpleProduct" stepKey="createSimpleProduct1"> + <requiredEntity createDataKey="createCategory"/> + </createData> + <createData entity="SimpleProduct" stepKey="createSimpleProduct2"> + <requiredEntity createDataKey="createCategory"/> + </createData> + <createData entity="SimpleProduct" stepKey="createSimpleProduct3"> + <requiredEntity createDataKey="createCategory"/> + </createData> + <createData entity="SimpleProduct" stepKey="createSimpleProduct4"> + <requiredEntity createDataKey="createCategory"/> + </createData> + <!--Create storeView 1--> + <actionGroup ref="AdminCreateStoreViewActionGroup" stepKey="createStoreViewOne"> + <argument name="customStore" value="customStoreEN"/> + </actionGroup> + <!-- Set Stores > Configurations > Catalog > Recently Viewed/Compared Products > Show for Current = store view--> + <magentoCLI command="config:set {{RecentlyViewedProductScopeStore.path}} {{RecentlyViewedProductScopeStore.value}}" stepKey="RecentlyViewedProductScopeStore"/> + </before> + + <after> + <!-- Delete Product and Category --> + <deleteData createDataKey="createSimpleProduct1" stepKey="deleteSimpleProduct1"/> + <deleteData createDataKey="createSimpleProduct2" stepKey="deleteSimpleProduct2"/> + <deleteData createDataKey="createSimpleProduct3" stepKey="deleteSimpleProduct3"/> + <deleteData createDataKey="createSimpleProduct4" stepKey="deleteSimpleProduct4"/> + <deleteData createDataKey="createCategory" stepKey="deleteCategory"/> + + <waitForPageLoad time="30" stepKey="waitForPageLoadWebSite"/> + <magentoCLI command="config:set {{RecentlyViewedProductScopeWebsite.path}} {{RecentlyViewedProductScopeWebsite.value}}" stepKey="RecentlyViewedProductScopeWebsite"/> + <!--Delete store views--> + <actionGroup ref="AdminDeleteStoreViewActionGroup" stepKey="deleteFirstStoreView"> + <argument name="customStore" value="customStoreEN"/> + </actionGroup> + + <!-- Clear Widget--> + <actionGroup ref="AdminEditCMSPageContentActionGroup" stepKey="clearRecentlyViewedWidgetsFromCMSContent"> + <argument name="content" value="{{CmsHomePageContent.content}}"/> + <argument name="pageId" value="{{CmsHomePageContent.page_id}}"/> + </actionGroup> + + <!-- Logout Admin --> + <actionGroup ref="AdminLogoutActionGroup" stepKey="logout"/> + <magentoCLI command="indexer:reindex" stepKey="reindex"/> + <magentoCLI command="cache:flush" stepKey="flushCacheAfterDeletion"/> + + </after> + + <!--Create widget for recently viewed products--> + <actionGroup ref="AdminEditCMSPageContentActionGroup" stepKey="clearRecentlyViewedWidgetsFromCMSContentBefore"> + <argument name="content" value="{{CmsHomePageContent.content}}"/> + <argument name="pageId" value="{{CmsHomePageContent.page_id}}"/> + </actionGroup> + + <amOnPage url="{{AdminCmsPageEditPage.url(CmsHomePageContent.page_id)}}" stepKey="navigateToEditHomePagePage"/> + <waitForPageLoad time="50" stepKey="waitForContentPageToLoad"/> + + <actionGroup ref="AdminInsertRecentlyViewedWidgetActionGroup" stepKey="insertRecentlyViewedWidget"> + <argument name="attributeSelector1" value="show_attributes"/> + <argument name="attributeSelector2" value="show_buttons"/> + <argument name="productAttributeSection1" value="1"/> + <argument name="productAttributeSection2" value="4"/> + <argument name="buttonToShowSection1" value="1"/> + <argument name="buttonToShowSection2" value="3"/> + </actionGroup> + + <!-- Warm up cache --> + <magentoCLI command="cache:flush" stepKey="flushCacheAfterWidgetCreated"/> + + <!-- Navigate to product 3 on store front --> + <amOnPage url="{{StorefrontProductPage.url($createSimpleProduct2.name$)}}" stepKey="goToStore1ProductPage2"/> + <amOnPage url="{{StorefrontProductPage.url($createSimpleProduct3.name$)}}" stepKey="goToStore1ProductPage3"/> + <!-- Go to Home Page --> + <actionGroup ref="StorefrontOpenHomePageActionGroup" stepKey="amOnHomePage"/> + <actionGroup ref="AssertSeeProductDetailsOnStorefrontRecentlyViewedWidgetActionGroup" stepKey="assertStore1RecentlyViewedProduct2"> + <argument name="productName" value="$$createSimpleProduct2.name$$"/> + <argument name="productPosition" value="2"/> + </actionGroup> + <actionGroup ref="AssertSeeProductDetailsOnStorefrontRecentlyViewedWidgetActionGroup" stepKey="assertStore1RecentlyViewedProduct3"> + <argument name="productName" value="$$createSimpleProduct3.name$$"/> + <argument name="productPosition" value="1"/> + </actionGroup> + + <!-- Switch store view --> + <waitForPageLoad time="40" stepKey="waitForStorefrontPageLoad"/> + <actionGroup ref="StorefrontSwitchStoreViewActionGroup" stepKey="switchStoreViewActionGroup"> + <argument name="storeView" value="customStoreEN"/> + </actionGroup> + + <amOnPage url="{{StorefrontProductPage.url($createSimpleProduct1.name$)}}" stepKey="goToStore2ProductPage1"/> + <amOnPage url="{{StorefrontProductPage.url($createSimpleProduct2.name$)}}" stepKey="goToStore2ProductPage2"/> + + <!-- Go to Home Page --> + <actionGroup ref="StorefrontOpenHomePageActionGroup" stepKey="amOnStoreViewHomePage"/> + <actionGroup ref="AssertSeeProductDetailsOnStorefrontRecentlyViewedWidgetActionGroup" stepKey="assertNextStore1RecentlyViewedProduct1"> + <argument name="productName" value="$$createSimpleProduct1.name$$"/> + <argument name="productPosition" value="2"/> + </actionGroup> + + <actionGroup ref="AssertSeeProductDetailsOnStorefrontRecentlyViewedWidgetActionGroup" stepKey="assertNextStore1RecentlyViewedProduct2"> + <argument name="productName" value="$$createSimpleProduct2.name$$"/> + <argument name="productPosition" value="1"/> + </actionGroup> + + <grabTextFrom selector="{{StoreFrontRecentlyViewedProductSection.ProductName('2')}}" stepKey="grabDontSeeHomeProduct3"/> + <assertNotContains stepKey="assertNotSeeProduct3"> + <actualResult type="const">$grabDontSeeHomeProduct3</actualResult> + <expectedResult type="const">$$createSimpleProduct3.name$$</expectedResult> + </assertNotContains> + + <actionGroup ref="StorefrontSwitchDefaultStoreViewActionGroup" stepKey="switchToDefualtStoreView"/> + + <actionGroup ref="AssertSeeProductDetailsOnStorefrontRecentlyViewedWidgetActionGroup" stepKey="assertSwitchStore1RecentlyViewedProduct2"> + <argument name="productName" value="$$createSimpleProduct2.name$$"/> + <argument name="productPosition" value="2"/> + </actionGroup> + + <actionGroup ref="AssertSeeProductDetailsOnStorefrontRecentlyViewedWidgetActionGroup" stepKey="assertSwitchStore1RecentlyViewedProduct3"> + <argument name="productName" value="$$createSimpleProduct3.name$$"/> + <argument name="productPosition" value="1"/> + </actionGroup> + + <grabTextFrom selector="{{StoreFrontRecentlyViewedProductSection.ProductName('2')}}" stepKey="grabDontSeeHomeProduct1"/> + <assertNotContains stepKey="assertNotSeeProduct1"> + <actualResult type="const">$grabDontSeeHomeProduct1</actualResult> + <expectedResult type="const">$$createSimpleProduct1.name$$</expectedResult> + </assertNotContains> + </test> +</tests> diff --git a/app/code/Magento/Catalog/Test/Mftf/Test/StoreFrontSimpleProductWithSpecialAndTierDiscountPriceTest.xml b/app/code/Magento/Catalog/Test/Mftf/Test/StoreFrontSimpleProductWithSpecialAndTierDiscountPriceTest.xml new file mode 100644 index 0000000000000..c38ea2de558e1 --- /dev/null +++ b/app/code/Magento/Catalog/Test/Mftf/Test/StoreFrontSimpleProductWithSpecialAndTierDiscountPriceTest.xml @@ -0,0 +1,65 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + /** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +--> + +<tests xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/testSchema.xsd"> + <test name="StoreFrontSimpleProductWithSpecialAndTierDiscountPriceTest"> + <annotations> + <features value="Catalog"/> + <title value="Apply discount tier price and custom price values for simple product"/> + <description value="Apply discount tier price and custom price values for simple product"/> + <severity value="MAJOR"/> + <group value="Catalog"/> + </annotations> + <before> + <createData entity="_defaultCategory" stepKey="createCategory"/> + + <createData entity="ApiSimpleProduct" stepKey="createProduct"> + <requiredEntity createDataKey="createCategory"/> + <field key="price">100.00</field> + </createData> + + <createData entity="tierProductPriceDiscount" stepKey="addTierPrice"> + <requiredEntity createDataKey="createProduct"/> + </createData> + </before> + <after> + <deleteData createDataKey="createProduct" stepKey="deleteSimpleProduct"/> + <deleteData createDataKey="createCategory" stepKey="deleteCategory"/> + </after> + + <actionGroup ref="AdminLoginActionGroup" stepKey="LoginAsAdmin"/> + + <actionGroup ref="AdminProductPageOpenByIdActionGroup" stepKey="openAdminProductEditPage"> + <argument name="productId" value="$createProduct.id$"/> + </actionGroup> + + <actionGroup ref="AddSpecialPriceToProductActionGroup" stepKey="addSpecialPriceToProduct"> + <argument name="price" value="65.00"/> + </actionGroup> + + <actionGroup ref="SaveProductFormActionGroup" stepKey="saveProductForm"/> + + <actionGroup ref="StorefrontOpenProductPageActionGroup" stepKey="openProductPage"> + <argument name="productUrl" value="$createProduct.custom_attributes[url_key]$"/> + </actionGroup> + + <actionGroup ref="AssertStorefrontProductDetailPageNameActionGroup" stepKey="assertProductNameText"> + <argument name="productName" value="$createProduct.name$"/> + </actionGroup> + + <actionGroup ref="AssertStorefrontProductDetailPageTierPriceActionGroup" stepKey="assertProductTierPriceText"> + <argument name="tierProductPriceDiscountQuantity" value="{{tierProductPriceDiscount.quantity}}"/> + <argument name="productPriceWithAppliedTierPriceDiscount" value="64.00"/> + <argument name="productSavedPricePercent" value="2"/> + </actionGroup> + + <actionGroup ref="AssertStorefrontProductDetailPageFinalPriceActionGroup" stepKey="assertProductFinalPriceText"> + <argument name="finalProductPrice" value="65.00"/> + </actionGroup> + </test> +</tests> diff --git a/app/code/Magento/Catalog/Test/Mftf/Test/StorefrontCatalogNavigationMenuUIDesktopTest.xml b/app/code/Magento/Catalog/Test/Mftf/Test/StorefrontCatalogNavigationMenuUIDesktopTest.xml index 22ec0048497fa..2a59be6306a30 100644 --- a/app/code/Magento/Catalog/Test/Mftf/Test/StorefrontCatalogNavigationMenuUIDesktopTest.xml +++ b/app/code/Magento/Catalog/Test/Mftf/Test/StorefrontCatalogNavigationMenuUIDesktopTest.xml @@ -20,16 +20,14 @@ </annotations> <before> <!-- Login as admin --> - <actionGroup ref="LoginAsAdmin" stepKey="loginAsAdmin"/> - <actionGroup ref="DeleteDefaultCategoryChildrenActionGroup" stepKey="deleteRootCategoryChildren"/> + <actionGroup ref="AdminLoginActionGroup" stepKey="loginAsAdmin"/> </before> <after> - <actionGroup ref="DeleteDefaultCategoryChildrenActionGroup" stepKey="deleteRootCategoryChildren"/> <actionGroup ref="AdminChangeStorefrontThemeActionGroup" stepKey="changeThemeToDefault"> <argument name="theme" value="{{MagentoLumaTheme.name}}"/> </actionGroup> <!-- Admin log out --> - <actionGroup ref="logout" stepKey="logout"/> + <actionGroup ref="AdminLogoutActionGroup" stepKey="logout"/> </after> <!-- Go to Content > Themes. Change theme to Blank --> @@ -40,91 +38,51 @@ <!-- Open storefront --> <actionGroup ref="StorefrontOpenHomePageActionGroup" stepKey="openStorefrontPage"/> - <!-- Assert no category - no menu --> - <dontSeeElement selector="{{StorefrontNavigationMenuSection.navigationMenu}}" stepKey="dontSeeMenu"/> - <!-- Assert single row - no hover state --> - <createData entity="ApiCategory" stepKey="createFirstCategoryBlank"> - <field key="name">Category A</field> - </createData> + <createData entity="ApiCategoryA" stepKey="createFirstCategoryBlank"/> <reloadPage stepKey="refreshPage"/> <waitForPageLoad stepKey="waitForBlankSingleRowAppear"/> <moveMouseOver selector="{{StorefrontHeaderSection.NavigationCategoryByName($$createFirstCategoryBlank.name$$)}}" stepKey="hoverFirstCategoryBlank"/> <dontSeeElement selector="{{StorefrontNavigationMenuSection.subItemLevelHover('level0')}}" stepKey="assertNoHoverState"/> <!-- Create categories --> - <createData entity="ApiCategory" stepKey="createSecondCategoryBlank"> - <field key="name">TEST</field> - </createData> - <createData entity="ApiCategory" stepKey="createThirdCategoryBlank"> - <field key="name">_test2</field> - </createData> - <createData entity="ApiCategory" stepKey="createFourthCategoryBlank"> - <field key="name">test 3</field> - </createData> - <createData entity="ApiCategory" stepKey="createFifthCategoryBlank"> - <field key="name">Category with several products</field> - </createData> - <createData entity="ApiCategory" stepKey="createSixthCategoryBlank"> - <field key="name">test 5</field> - </createData> - <createData entity="ApiCategory" stepKey="createSeventhCategoryBlank"> - <field key="name">test 8</field> - </createData> - <createData entity="ApiCategory" stepKey="createEighthCategoryBlank"> - <field key="name">This is a very very very very very looong title</field> - </createData> - <createData entity="ApiCategory" stepKey="createNinthCategoryBlank"> - <field key="name">test 6</field> - </createData> - <createData entity="ApiCategory" stepKey="createTenthCategoryBlank"> - <field key="name">test 7</field> - </createData> - <createData entity="ApiCategory" stepKey="createEleventhCategoryBlank"> - <field key="name">test 4</field> - </createData> - <createData entity="ApiCategory" stepKey="createTwelfthCategoryBlank"> - <field key="name">Category with image</field> - </createData> - <createData entity="ApiCategory" stepKey="createThirteenthCategoryBlank"> - <field key="name">test 0</field> - </createData> - <createData entity="ApiCategory" stepKey="createCategoryWithoutChildrenBlank"> - <field key="name">Category with description & custom title</field> - </createData> - <createData entity="ApiCategory" stepKey="createCategoryWithChildrenBlank"> - <field key="name">Category with children</field> - </createData> - <createData entity="SubCategoryWithParent" stepKey="createFirstCategoryLevelOneBlank"> - <field key="name">level 1 test category very very very long name</field> + <createData entity="ApiCategoryTest" stepKey="createSecondCategoryBlank"/> + <createData entity="ApiCategoryTest2" stepKey="createThirdCategoryBlank"/> + <createData entity="ApiCategoryTest3" stepKey="createFourthCategoryBlank"/> + <createData entity="ApiCategorySeveralProducts" stepKey="createFifthCategoryBlank"/> + <createData entity="ApiCategoryTest5" stepKey="createSixthCategoryBlank"/> + <createData entity="ApiCategoryTest8" stepKey="createSeventhCategoryBlank"/> + <createData entity="ApiCategoryLongTitle" stepKey="createEighthCategoryBlank"/> + <createData entity="ApiCategoryTest6" stepKey="createNinthCategoryBlank"/> + <createData entity="ApiCategoryTest7" stepKey="createTenthCategoryBlank"/> + <createData entity="ApiCategoryTest4" stepKey="createEleventhCategoryBlank"/> + <createData entity="ApiCategoryWithImage" stepKey="createTwelfthCategoryBlank"/> + <createData entity="ApiCategoryTest0" stepKey="createThirteenthCategoryBlank"/> + <createData entity="ApiCategoryWithDescription" stepKey="createCategoryWithoutChildrenBlank"/> + <createData entity="ApiCategoryWithChildren" stepKey="createCategoryWithChildrenBlank"/> + + <createData entity="ApiSubCategoryWithParentLongName" stepKey="createFirstCategoryLevelOneBlank"> <requiredEntity createDataKey="createCategoryWithChildrenBlank"/> </createData> - <createData entity="SubCategoryWithParent" stepKey="createSecondCategoryLevelOneBlank"> - <field key="name">level 1 test category name</field> + <createData entity="ApiSubCategoryWithParentLevel1" stepKey="createSecondCategoryLevelOneBlank"> <requiredEntity createDataKey="createCategoryWithChildrenBlank"/> </createData> - <createData entity="SubCategoryWithParent" stepKey="createThirdCategoryLevelOneBlank"> - <field key="name">level 1 with children</field> + <createData entity="ApiSubCategoryWithChildrenLevel1" stepKey="createThirdCategoryLevelOneBlank"> <requiredEntity createDataKey="createCategoryWithChildrenBlank"/> </createData> - <createData entity="SubCategoryWithParent" stepKey="createCategoryLevelTwoBlank"> - <field key="name">level 2 with children</field> + <createData entity="ApiSubCategoryWithChildrenLevel2" stepKey="createCategoryLevelTwoBlank"> <requiredEntity createDataKey="createThirdCategoryLevelOneBlank"/> </createData> - <createData entity="SubCategoryWithParent" stepKey="createCategoryLevelThreeBlank"> - <field key="name">level 3 test</field> + <createData entity="ApiSubCategoryLevel3" stepKey="createCategoryLevelThreeBlank"> <requiredEntity createDataKey="createCategoryLevelTwoBlank"/> </createData> - <createData entity="SubCategoryWithParent" stepKey="createFirstCategoryLevelFourBlank"> - <field key="name">level 4</field> + <createData entity="ApiSubCategoryLevel4" stepKey="createFirstCategoryLevelFourBlank"> <requiredEntity createDataKey="createCategoryLevelThreeBlank"/> </createData> - <createData entity="SubCategoryWithParent" stepKey="createSecondCategoryLevelFourBlank"> - <field key="name">level 4 test</field> + <createData entity="ApiSubCategoryLevel4Test" stepKey="createSecondCategoryLevelFourBlank"> <requiredEntity createDataKey="createCategoryLevelThreeBlank"/> </createData> - <createData entity="SubCategoryWithParent" stepKey="createCategoryLevelFiveBlank"> - <field key="name">level 5</field> + <createData entity="ApiSubCategoryLevel5" stepKey="createCategoryLevelFiveBlank"> <requiredEntity createDataKey="createSecondCategoryLevelFourBlank"/> </createData> @@ -158,18 +116,18 @@ <argument name="color" value="{{NavigationMenuColor.gray}}"/> </actionGroup> - <!-- Submenu appears rightward --> - <seeElement selector="{{StorefrontNavigationMenuSection.submenuRightDirection('level0')}}" stepKey="assertTopLevelMenuLeftDirection"/> + <!-- Submenu appears leftward --> + <seeElement selector="{{StorefrontNavigationMenuSection.submenu('level0')}}" stepKey="assertTopLevelMenu"/> <!-- Nested level 1 & 5 --> <moveMouseOver selector="{{StorefrontHeaderSection.NavigationCategoryByName($$createCategoryLevelTwoBlank.name$$)}}" stepKey="hoverCategoryLevelTwo"/> - <seeElement selector="{{StorefrontNavigationMenuSection.submenuLeftDirection('level1')}}" stepKey="seeLevelOneMenuLeftDirection"/> + <seeElement selector="{{StorefrontNavigationMenuSection.submenu('level1')}}" stepKey="seeLevelOneMenu"/> <moveMouseOver selector="{{StorefrontHeaderSection.NavigationCategoryByName($$createCategoryLevelThreeBlank.name$$)}}" stepKey="hoverCategoryLevelThree"/> - <seeElement selector="{{StorefrontNavigationMenuSection.submenuLeftDirection('level2')}}" stepKey="seeLevelTwoMenuRightDirection"/> + <seeElement selector="{{StorefrontNavigationMenuSection.submenu('level2')}}" stepKey="seeLevelTwoMenu"/> <moveMouseOver selector="{{StorefrontHeaderSection.NavigationCategoryByName($$createSecondCategoryLevelFourBlank.name$$)}}" stepKey="hoverCategoryLevelFour"/> - <seeElement selector="{{StorefrontNavigationMenuSection.submenuRightDirection('level3')}}" stepKey="seeLevelThreeMenuRightDirection"/> + <seeElement selector="{{StorefrontNavigationMenuSection.submenu('level3')}}" stepKey="seeLevelThreeMenu"/> <actionGroup ref="StorefrontCheckElementColorActionGroup" stepKey="checkSubcategoryHighlighted"> <argument name="selector" value="{{StorefrontNavigationMenuSection.subItemLevelHover('level3')}}"/> @@ -202,9 +160,6 @@ <!-- Open storefront --> <actionGroup ref="StorefrontOpenHomePageActionGroup" stepKey="openStorefront"/> - <!-- Assert no category - no menu --> - <dontSeeElement selector="{{StorefrontNavigationMenuSection.navigationMenu}}" stepKey="dontSeeMenuOnStorefront"/> - <!-- Create categories --> <createData entity="ApiCategory" stepKey="createFirstCategoryLuma"/> <createData entity="ApiCategory" stepKey="createSecondCategoryLuma"/> @@ -278,17 +233,17 @@ </actionGroup> <!-- Submenu appears rightward --> - <seeElement selector="{{StorefrontNavigationMenuSection.submenuRightDirection('level0')}}" stepKey="seeTopLevelRightDirection"/> + <seeElement selector="{{StorefrontNavigationMenuSection.submenu('level0')}}" stepKey="seeTopLevel"/> <!-- Nested levels 1 & 5 --> <moveMouseOver selector="{{StorefrontHeaderSection.NavigationCategoryByName($$createSecondCategoryLevelTwoLuma.name$$)}}" stepKey="hoverThirdCategoryLevelTwo"/> - <seeElement selector="{{StorefrontNavigationMenuSection.submenuRightDirection('level1')}}" stepKey="seeFirstLevelRightDirection"/> + <seeElement selector="{{StorefrontNavigationMenuSection.submenu('level1')}}" stepKey="seeFirstLevelMenu"/> <moveMouseOver selector="{{StorefrontHeaderSection.NavigationCategoryByName($$createCategoryLevelThreeLuma.name$$)}}" stepKey="hoverOnCategoryLevelThree"/> - <seeElement selector="{{StorefrontNavigationMenuSection.submenuRightDirection('level2')}}" stepKey="seeSecondLevelRightDirection"/> + <seeElement selector="{{StorefrontNavigationMenuSection.submenu('level2')}}" stepKey="seeSecondLevelMenu"/> <moveMouseOver selector="{{StorefrontHeaderSection.NavigationCategoryByName($$createCategoryLevelFourLuma.name$$)}}" stepKey="hoverOnCategoryLevelFour"/> - <seeElement selector="{{StorefrontNavigationMenuSection.submenuRightDirection('level3')}}" stepKey="seeThirdLevelRightDirection"/> + <seeElement selector="{{StorefrontNavigationMenuSection.submenu('level3')}}" stepKey="seeThirdLevelMenu"/> <actionGroup ref="StorefrontCheckElementColorActionGroup" stepKey="checkSubcategoryHighlightedAfterHover"> <argument name="selector" value="{{StorefrontNavigationMenuSection.subItemLevelHover('level3')}}"/> diff --git a/app/code/Magento/Catalog/Test/Mftf/Test/StorefrontCategoryHighlightedAndProductDisplayedTest.xml b/app/code/Magento/Catalog/Test/Mftf/Test/StorefrontCategoryHighlightedAndProductDisplayedTest.xml index 3e72df9133898..a5dcc8fa28c6f 100644 --- a/app/code/Magento/Catalog/Test/Mftf/Test/StorefrontCategoryHighlightedAndProductDisplayedTest.xml +++ b/app/code/Magento/Catalog/Test/Mftf/Test/StorefrontCategoryHighlightedAndProductDisplayedTest.xml @@ -20,7 +20,7 @@ <group value="Catalog"/> </annotations> <before> - <actionGroup ref="LoginAsAdmin" stepKey="loginAsAdmin"/> + <actionGroup ref="AdminLoginActionGroup" stepKey="loginAsAdmin"/> <createData entity="SimpleSubCategory" stepKey="category1"/> <createData entity="SimpleSubCategory" stepKey="category2"/> <createData entity="SimpleSubCategory" stepKey="category3"/> @@ -47,12 +47,11 @@ <deleteData createDataKey="category2" stepKey="deleteCategory2"/> <deleteData createDataKey="category3" stepKey="deleteCategory3"/> <deleteData createDataKey="category4" stepKey="deleteCategory4"/> - <actionGroup ref="logout" stepKey="logout"/> + <actionGroup ref="AdminLogoutActionGroup" stepKey="logout"/> </after> <!--Open Storefront home page--> <comment userInput="Open Storefront home page" stepKey="openStorefrontHomePage"/> - <amOnPage url="{{StorefrontHomePage.url}}" stepKey="goToStorefrontHomePage"/> - <waitForPageLoad stepKey="waitForSimpleProductPage"/> + <actionGroup ref="StorefrontOpenHomePageActionGroup" stepKey="goToStorefrontHomePage"/> <!--Click on first category--> <comment userInput="Click on first category" stepKey="openFirstCategoryPage"/> <click selector="{{AdminCategorySidebarTreeSection.categoryInTree($$category1.name$$)}}" stepKey="clickCategory1Name"/> @@ -60,9 +59,15 @@ <!--Check if current category is highlighted and the others are not--> <comment userInput="Check if current category is highlighted and the others are not" stepKey="checkCateg1NameIsHighlighted"/> <grabAttributeFrom selector="{{AdminCategorySidebarTreeSection.categoryHighlighted($$category1.name$$)}}" userInput="class" stepKey="grabCategory1Class"/> - <assertContains expectedType="string" expected="active" actual="$grabCategory1Class" stepKey="assertCategory1IsHighlighted"/> + <assertContains stepKey="assertCategory1IsHighlighted"> + <actualResult type="const">$grabCategory1Class</actualResult> + <expectedResult type="string">active</expectedResult> + </assertContains> <executeJS function="return document.querySelectorAll('{{AdminCategorySidebarTreeSection.categoryNotHighlighted}}').length" stepKey="highlightedAmount"/> - <assertEquals expectedType="int" expected="1" actual="$highlightedAmount" stepKey="assertRestCategories1IsNotHighlighted"/> + <assertEquals stepKey="assertRestCategories1IsNotHighlighted"> + <actualResult type="const">$highlightedAmount</actualResult> + <expectedResult type="int">1</expectedResult> + </assertEquals> <!--See products in the category page--> <comment userInput="See products in the category page" stepKey="seeProductsInCategoryPage"/> <seeElement selector="{{StorefrontCategoryMainSection.specifiedProductItemInfo($product1.name$)}}" stepKey="seeProduct1InCategoryPage"/> @@ -74,9 +79,15 @@ <!--Check if current category is highlighted and the others are not--> <comment userInput="Check if current category is highlighted and the others are not" stepKey="checkCateg2NameIsHighlighted"/> <grabAttributeFrom selector="{{AdminCategorySidebarTreeSection.categoryHighlighted($$category2.name$$)}}" userInput="class" stepKey="grabCategory2Class"/> - <assertContains expectedType="string" expected="active" actual="$grabCategory2Class" stepKey="assertCategory2IsHighlighted"/> + <assertContains stepKey="assertCategory2IsHighlighted"> + <actualResult type="const">$grabCategory2Class</actualResult> + <expectedResult type="string">active</expectedResult> + </assertContains> <executeJS function="return document.querySelectorAll('{{AdminCategorySidebarTreeSection.categoryNotHighlighted}}').length" stepKey="highlightedAmount2"/> - <assertEquals expectedType="int" expected="1" actual="$highlightedAmount2" stepKey="assertRestCategories1IsNotHighlighted2"/> + <assertEquals stepKey="assertRestCategories1IsNotHighlighted2"> + <actualResult type="const">$highlightedAmount2</actualResult> + <expectedResult type="int">1</expectedResult> + </assertEquals> <!--Assert products in second category page--> <comment userInput="Assert products in second category page" stepKey="commentAssertProducts"/> <seeElement selector="{{StorefrontCategoryMainSection.specifiedProductItemInfo($product3.name$)}}" stepKey="seeProduct3InCategoryPage"/> diff --git a/app/code/Magento/Catalog/Test/Mftf/Test/StorefrontCategoryPageWithCategoryFilterTest.xml b/app/code/Magento/Catalog/Test/Mftf/Test/StorefrontCategoryPageWithCategoryFilterTest.xml new file mode 100644 index 0000000000000..8955f43e1b335 --- /dev/null +++ b/app/code/Magento/Catalog/Test/Mftf/Test/StorefrontCategoryPageWithCategoryFilterTest.xml @@ -0,0 +1,63 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + /** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +--> + +<tests xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/testSchema.xsd"> + <test name="StorefrontCategoryPageWithCategoryFilterTest"> + <annotations> + <title value="Category with Layered Navigation - verify presence of category filter"/> + <stories value="Category page: Layered Navigation with category filter"/> + <description value="Verify that the category filter is present in layered navigation on category page"/> + <features value="Catalog"/> + <severity value="MINOR"/> + <group value="Catalog"/> + </annotations> + + <before> + <!-- Create one category --> + <createData entity="_defaultCategory" stepKey="defaultCategory"> + <field key="name">TopCategory</field> + </createData> + <!-- Create second category, having as parent the 1st one --> + <createData entity="SubCategoryWithParent" stepKey="subCategory"> + <field key="name">SubCategory</field> + <field key="parent_id">$$defaultCategory.id$$</field> + <requiredEntity createDataKey="defaultCategory"/> + </createData> + + <!-- Create a product assigned to the 1st category --> + <createData entity="_defaultProduct" stepKey="createSimpleProduct1"> + <requiredEntity createDataKey="defaultCategory"/> + </createData> + + <!-- Create 2nd product assigned to the 2nd category --> + <!-- The "Category filter" item is not shown in layered navigation --> + <!-- if there are not subcategories with products to show --> + <createData entity="_defaultProduct" stepKey="createSimpleProduct2"> + <requiredEntity createDataKey="subCategory"/> + </createData> + + <!-- Set the category filter to be present on the category page layered navigation --> + <magentoCLI command="config:set {{EnableCategoryFilterOnCategoryPageConfigData.path}} {{EnableCategoryFilterOnCategoryPageConfigData.value}}" stepKey="setCategoryFilterVisibleOnStorefront"/> + + <magentoCLI command="cache:flush" stepKey="clearCache1"/> + </before> + + <after> + <deleteData createDataKey="createSimpleProduct1" stepKey="deleteProduct1"/> + <deleteData createDataKey="createSimpleProduct2" stepKey="deleteProduct2"/> + <deleteData createDataKey="subCategory" stepKey="deleteSubCategory"/> + <deleteData createDataKey="defaultCategory" stepKey="deleteCategoryMainCategory"/> + </after> + + <actionGroup ref="StorefrontNavigateCategoryPageActionGroup" stepKey="navigateToCategoryPage"> + <argument name="category" value="$$defaultCategory$$"/> + </actionGroup> + + <actionGroup ref="AssertStorefrontLayeredNavigationCategoryFilterVisibleActionGroup" stepKey="checkCategoryFilterIsPresent" /> + </test> +</tests> diff --git a/app/code/Magento/Catalog/Test/Mftf/Test/StorefrontCategoryPageWithoutCategoryFilterTest.xml b/app/code/Magento/Catalog/Test/Mftf/Test/StorefrontCategoryPageWithoutCategoryFilterTest.xml new file mode 100644 index 0000000000000..7900a712e0664 --- /dev/null +++ b/app/code/Magento/Catalog/Test/Mftf/Test/StorefrontCategoryPageWithoutCategoryFilterTest.xml @@ -0,0 +1,63 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + /** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +--> + +<tests xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/testSchema.xsd"> + <test name="StorefrontCategoryPageWithoutCategoryFilterTest"> + <annotations> + <title value="Category with Layered Navigation - verify absence of category filter"/> + <stories value="Category page: Layered Navigation without category filter"/> + <description value="Verify that the category filter is NOT present in layered navigation on category page"/> + <features value="Catalog"/> + <severity value="MINOR"/> + <group value="Catalog"/> + </annotations> + + <before> + <!-- Create one category --> + <createData entity="_defaultCategory" stepKey="defaultCategory"> + <field key="name">TopCategory</field> + </createData> + <!-- Create second category, having as parent the 1st one --> + <createData entity="SubCategoryWithParent" stepKey="subCategory"> + <field key="name">SubCategory</field> + <field key="parent_id">$$defaultCategory.id$$</field> + <requiredEntity createDataKey="defaultCategory"/> + </createData> + + <!-- Create a product assigned to the 1st category --> + <createData entity="_defaultProduct" stepKey="createSimpleProduct1"> + <requiredEntity createDataKey="defaultCategory"/> + </createData> + + <!-- Create 2nd product assigned to the 2nd category --> + <!-- The "Category filter" item is not shown in layered navigation --> + <!-- if there are not subcategories with products to show --> + <createData entity="_defaultProduct" stepKey="createSimpleProduct2"> + <requiredEntity createDataKey="subCategory"/> + </createData> + + <!-- Set the category filter to NOT be present on the category page layered navigation --> + <magentoCLI command="config:set {{DisableCategoryFilterOnCategoryPageConfigData.path}} {{DisableCategoryFilterOnCategoryPageConfigData.value}}" stepKey="hideCategoryFilterOnStorefront"/> + + <magentoCLI command="cache:flush" stepKey="clearCache"/> + </before> + + <after> + <deleteData createDataKey="createSimpleProduct1" stepKey="deleteProduct1"/> + <deleteData createDataKey="createSimpleProduct2" stepKey="deleteProduct2"/> + <deleteData createDataKey="subCategory" stepKey="deleteSubCategory"/> + <deleteData createDataKey="defaultCategory" stepKey="deleteCategoryMainCategory"/> + </after> + + <actionGroup ref="StorefrontNavigateCategoryPageActionGroup" stepKey="navigateToCategoryPage"> + <argument name="category" value="$$defaultCategory$$"/> + </actionGroup> + + <actionGroup ref="AssertStorefrontLayeredNavigationCategoryFilterNotVisibleActionGroup" stepKey="checkCategoryFilterIsNotPresent" /> + </test> +</tests> diff --git a/app/code/Magento/Catalog/Test/Mftf/Test/StorefrontCheckDefaultNumberProductsToDisplayTest.xml b/app/code/Magento/Catalog/Test/Mftf/Test/StorefrontCheckDefaultNumberProductsToDisplayTest.xml index 4eef6a2c06800..b13c3827c6727 100644 --- a/app/code/Magento/Catalog/Test/Mftf/Test/StorefrontCheckDefaultNumberProductsToDisplayTest.xml +++ b/app/code/Magento/Catalog/Test/Mftf/Test/StorefrontCheckDefaultNumberProductsToDisplayTest.xml @@ -21,7 +21,7 @@ <before> <!-- Login as Admin --> <comment userInput="Login as Admin" stepKey="commentLoginAsAdmin"/> - <actionGroup ref="LoginAsAdmin" stepKey="login"/> + <actionGroup ref="AdminLoginActionGroup" stepKey="login"/> <!--Create 37 Products and Subcategory --> <comment userInput="Create 37 Products and Subcategory" stepKey="commentCreateData"/> <createData entity="SimpleSubCategory" stepKey="createCategory"/> @@ -176,7 +176,7 @@ <deleteData createDataKey="createSimpleProductThirtyFive" stepKey="deleteProductThirtyFive"/> <deleteData createDataKey="createSimpleProductThirtySix" stepKey="deleteProductThirtySix"/> <deleteData createDataKey="createSimpleProductThirtySeven" stepKey="deleteProductThirtySeven"/> - <actionGroup ref="logout" stepKey="logout"/> + <actionGroup ref="AdminLogoutActionGroup" stepKey="logout"/> </after> <!--Verify configuration for default number of products displayed in the grid view--> <comment userInput="Verify configuration for default number of products displayed in the grid view" stepKey="commentVerifyDefaultValues"/> @@ -197,10 +197,16 @@ <!-- Check the drop-down at the bottom of page contains options --> <comment userInput="Check the drop-down at the bottom of page contains options" stepKey="commentCheckOptions"/> <scrollTo selector="{{StorefrontCategoryBottomToolbarSection.perPage}}" stepKey="scrollToBottomToolbarSection"/> - <assertElementContainsAttribute selector="{{StorefrontCategoryBottomToolbarSection.perPage}}" attribute="value" expectedValue="12" stepKey="assertPerPageFirstValue" /> + <assertElementContainsAttribute stepKey="assertPerPageFirstValue"> + <expectedResult selector="{{StorefrontCategoryBottomToolbarSection.perPage}}" attribute="value" type="string">12</expectedResult> + </assertElementContainsAttribute> <selectOption selector="{{StorefrontCategoryBottomToolbarSection.perPage}}" userInput="24" stepKey="selectPerPageSecondValue" /> - <assertElementContainsAttribute selector="{{StorefrontCategoryBottomToolbarSection.perPage}}" attribute="value" expectedValue="24" stepKey="assertPerPageSecondValue" /> + <assertElementContainsAttribute stepKey="assertPerPageSecondValue"> + <expectedResult selector="{{StorefrontCategoryBottomToolbarSection.perPage}}" attribute="value" type="string">24</expectedResult> + </assertElementContainsAttribute> <selectOption selector="{{StorefrontCategoryBottomToolbarSection.perPage}}" userInput="36" stepKey="selectPerPageThirdValue" /> - <assertElementContainsAttribute selector="{{StorefrontCategoryBottomToolbarSection.perPage}}" attribute="value" expectedValue="36" stepKey="assertPerPageThirdValue" /> + <assertElementContainsAttribute stepKey="assertPerPageThirdValue"> + <expectedResult selector="{{StorefrontCategoryBottomToolbarSection.perPage}}" attribute="value" type="string">36</expectedResult> + </assertElementContainsAttribute> </test> </tests> diff --git a/app/code/Magento/Catalog/Test/Mftf/Test/StorefrontConfigurableOptionsThumbImagesTest.xml b/app/code/Magento/Catalog/Test/Mftf/Test/StorefrontConfigurableOptionsThumbImagesTest.xml new file mode 100644 index 0000000000000..dec36d72a50d6 --- /dev/null +++ b/app/code/Magento/Catalog/Test/Mftf/Test/StorefrontConfigurableOptionsThumbImagesTest.xml @@ -0,0 +1,228 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + /** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +--> + +<tests xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/testSchema.xsd"> + <test name="StorefrontConfigurableOptionsThumbImagesTest"> + <annotations> + <stories value="Configurable Product"/> + <title value="Check thumbnail images and active image for Configurable Product"/> + <description value="Login as admin, create attribute with two options, configurable product with two + associated simple products. Add few images for products, check the fotorama thumbnail images + (visible and active) for each selected option for the configurable product"/> + <group value="catalog"/> + </annotations> + <before> + <!-- Login as Admin --> + <actionGroup ref="AdminLoginActionGroup" stepKey="loginToAdminPanel"/> + + <!-- Create Default Category --> + <createData entity="_defaultCategory" stepKey="createCategory"/> + + <!-- Create an attribute with two options to be used in the first child product --> + <createData entity="productAttributeWithTwoOptions" stepKey="createConfigProductAttribute"/> + <createData entity="productAttributeOption1" stepKey="createConfigProductAttributeOption1"> + <requiredEntity createDataKey="createConfigProductAttribute"/> + </createData> + <createData entity="productAttributeOption2" stepKey="createConfigProductAttributeOption2"> + <requiredEntity createDataKey="createConfigProductAttribute"/> + </createData> + + <!-- Add the attribute just created to default attribute set --> + <createData entity="AddToDefaultSet" stepKey="createConfigAddToAttributeSet"> + <requiredEntity createDataKey="createConfigProductAttribute"/> + </createData> + + <!-- Get the first option of the attribute created --> + <getData entity="ProductAttributeOptionGetter" index="1" stepKey="getConfigAttributeOption1"> + <requiredEntity createDataKey="createConfigProductAttribute"/> + </getData> + + <!-- Get the second option of the attribute created --> + <getData entity="ProductAttributeOptionGetter" index="2" stepKey="getConfigAttributeOption2"> + <requiredEntity createDataKey="createConfigProductAttribute"/> + </getData> + + <!-- Create Configurable product --> + <createData entity="BaseConfigurableProduct" stepKey="createConfigProduct"> + <requiredEntity createDataKey="createCategory"/> + </createData> + + <!-- Create a simple product and give it the attribute with the first option --> + <createData entity="ApiSimpleOne" stepKey="createConfigChildProduct1"> + <requiredEntity createDataKey="createConfigProductAttribute"/> + <requiredEntity createDataKey="getConfigAttributeOption1"/> + </createData> + + <!--Create a simple product and give it the attribute with the second option --> + <createData entity="ApiSimpleTwo" stepKey="createConfigChildProduct2"> + <requiredEntity createDataKey="createConfigProductAttribute"/> + <requiredEntity createDataKey="getConfigAttributeOption2"/> + </createData> + + <!-- Create the configurable product --> + <createData entity="ConfigurableProductTwoOptions" stepKey="createConfigProductOption"> + <requiredEntity createDataKey="createConfigProduct"/> + <requiredEntity createDataKey="createConfigProductAttribute"/> + <requiredEntity createDataKey="getConfigAttributeOption1"/> + <requiredEntity createDataKey="getConfigAttributeOption2"/> + </createData> + + <!-- Add the first simple product to the configurable product --> + <createData entity="ConfigurableProductAddChild" stepKey="createConfigProductAddChild1"> + <requiredEntity createDataKey="createConfigProduct"/> + <requiredEntity createDataKey="createConfigChildProduct1"/> + </createData> + + <!-- Add the second simple product to the configurable product --> + <createData entity="ConfigurableProductAddChild" stepKey="createConfigProductAddChild2"> + <requiredEntity createDataKey="createConfigProduct"/> + <requiredEntity createDataKey="createConfigChildProduct2"/> + </createData> + + <!-- ConfigProduct --> + <!-- Go to Product Page (ConfigProduct) --> + <actionGroup ref="AdminProductPageOpenByIdActionGroup" stepKey="goToConfigProduct"> + <argument name="productId" value="$$createConfigProduct.id$$"/> + </actionGroup> + + <!--Switch to 'Default Store View' scope and open product page--> + <actionGroup ref="SwitchToTheNewStoreViewActionGroup" stepKey="SwitchDefaultStoreViewForConfigProduct"> + <argument name="storeViewName" value="'Default Store View'"/> + </actionGroup> + + <!-- Add images for ConfigProduct --> + <actionGroup ref="AddProductImageActionGroup" stepKey="addConfigProductMagento3"> + <argument name="image" value="Magento3"/> + </actionGroup> + + <actionGroup ref="AddProductImageActionGroup" stepKey="addConfigProductTestImageAdobe"> + <argument name="image" value="TestImageAdobe"/> + </actionGroup> + <actionGroup ref="AdminAssignImageBaseRoleActionGroup" stepKey="assignTestImageAdobeBaseRole"> + <argument name="image" value="TestImageAdobe"/> + </actionGroup> + + <!-- Save changes fot ConfigProduct --> + <actionGroup ref="SaveProductFormActionGroup" stepKey="saveConfigProductProduct"/> + + <!-- ChildProduct1 --> + <!-- Go to Product Page (ChildProduct1) --> + <actionGroup ref="AdminProductPageOpenByIdActionGroup" stepKey="goToChildProduct1"> + <argument name="productId" value="$$createConfigChildProduct1.id$$"/> + </actionGroup> + + <!--Switch to 'Default Store View' scope and open product page--> + <actionGroup ref="SwitchToTheNewStoreViewActionGroup" stepKey="SwitchDefaultStoreViewForChildProduct1"> + <argument name="storeViewName" value="'Default Store View'"/> + </actionGroup> + + <!-- Add images for ChildProduct1 --> + <actionGroup ref="AddProductImageActionGroup" stepKey="addChildProduct1ProductImage"> + <argument name="image" value="ProductImage"/> + </actionGroup> + <actionGroup ref="AddProductImageActionGroup" stepKey="addChildProduct1Magento2"> + <argument name="image" value="Magento2"/> + </actionGroup> + <actionGroup ref="AdminAssignImageRolesIfUnassignedActionGroup" stepKey="assignMagento2Role"> + <argument name="image" value="Magento2"/> + </actionGroup> + + <!-- Save changes fot ChildProduct1 --> + <actionGroup ref="SaveProductFormActionGroup" stepKey="saveChildProduct1Product"/> + + <!-- ChildProduct2 --> + <!-- Go to Product Page (ChildProduct2) --> + <actionGroup ref="AdminProductPageOpenByIdActionGroup" stepKey="goToChildProduct2"> + <argument name="productId" value="$$createConfigChildProduct2.id$$"/> + </actionGroup> + + <!--Switch to 'Default Store View' scope and open product page--> + <actionGroup ref="SwitchToTheNewStoreViewActionGroup" stepKey="SwitchDefaultStoreViewForChildProduct2"> + <argument name="storeViewName" value="'Default Store View'"/> + </actionGroup> + + <!-- Add image for ChildProduct2 --> + <actionGroup ref="AddProductImageActionGroup" stepKey="addChildProduct2TestImageNew"> + <argument name="image" value="TestImageNew"/> + </actionGroup> + + <!-- Save changes fot ChildProduct2 --> + <actionGroup ref="SaveProductFormActionGroup" stepKey="saveChildProduct2Product"/> + </before> + <after> + <!-- Delete Created Data --> + <deleteData createDataKey="createCategory" stepKey="deleteCategory"/> + <deleteData createDataKey="createConfigProduct" stepKey="deleteConfigProduct"/> + <deleteData createDataKey="createConfigChildProduct1" stepKey="deleteConfigChildProduct1"/> + <deleteData createDataKey="createConfigChildProduct2" stepKey="deleteConfigChildProduct2"/> + <deleteData createDataKey="createConfigProductAttribute" stepKey="deleteAttribute"/> + <actionGroup ref="AdminLogoutActionGroup" stepKey="logout"/> + + <!-- Reindex invalidated indices after product attribute has been created/deleted --> + <magentoCron groups="index" stepKey="reindexInvalidatedIndices"/> + </after> + + <!-- Open ConfigProduct in Store Front Page --> + <amOnPage url="$$createConfigProduct.sku$$.html" stepKey="openProductInStoreFront"/> + <waitForPageLoad stepKey="waitForProductToLoad"/> + + <!-- Check fotorama thumbnail images (no selected options) --> + <actionGroup ref="StorefrontAssertFotoramaImageAvailabilityActionGroup" stepKey="seeMagento3ForNoOption"> + <argument name="fileName" value="{{Magento3.filename}}"/> + </actionGroup> + + <actionGroup ref="StorefrontAssertFotoramaImageAvailabilityActionGroup" stepKey="seeActiveTestImageAdobeForNoOption"> + <argument name="fileName" value="{{TestImageAdobe.filename}}"/> + </actionGroup> + + <!-- Select first option --> + <actionGroup ref="StorefrontProductPageSelectDropDownOptionValueActionGroup" stepKey="selectFirstOptionValue"> + <argument name="attributeLabel" value="$$createConfigProductAttribute.default_frontend_label$$"/> + <argument name="optionLabel" value="$$getConfigAttributeOption1.label$$"/> + </actionGroup> + + <!-- Check fotorama thumbnail images (first option selected) --> + <actionGroup ref="StorefrontAssertFotoramaImageAvailabilityActionGroup" stepKey="seeMagento3ForFirstOption"> + <argument name="fileName" value="{{Magento3.filename}}"/> + </actionGroup> + + <actionGroup ref="StorefrontAssertFotoramaImageAvailabilityActionGroup" stepKey="seeTestImageAdobeForFirstOption"> + <argument name="fileName" value="{{TestImageAdobe.filename}}"/> + </actionGroup> + + <actionGroup ref="StorefrontAssertFotoramaImageAvailabilityActionGroup" stepKey="seeProductImageForFirstOption"> + <argument name="fileName" value="{{ProductImage.filename}}"/> + </actionGroup> + + <!-- Check active fotorama thumbnail image (first option selected) --> + <actionGroup ref="StorefrontAssertActiveProductImageActionGroup" stepKey="seeActiveMagento2ForFirstOption"> + <argument name="fileName" value="{{Magento2.filename}}"/> + </actionGroup> + + <!-- Select second option --> + <actionGroup ref="StorefrontProductPageSelectDropDownOptionValueActionGroup" stepKey="selectSecondOptionValue"> + <argument name="attributeLabel" value="$$createConfigProductAttribute.default_frontend_label$$"/> + <argument name="optionLabel" value="$$getConfigAttributeOption2.label$$"/> + </actionGroup> + + <!-- Check fotorama thumbnail images (second option selected) --> + <actionGroup ref="StorefrontAssertFotoramaImageAvailabilityActionGroup" stepKey="seeMagento3ForSecondOption"> + <argument name="fileName" value="{{Magento3.filename}}"/> + </actionGroup> + + <actionGroup ref="StorefrontAssertFotoramaImageAvailabilityActionGroup" stepKey="seeTestImageAdobeForSecondOption"> + <argument name="fileName" value="{{TestImageAdobe.filename}}"/> + </actionGroup> + + <!-- Check active fotorama thumbnail image (second option selected) --> + <actionGroup ref="StorefrontAssertActiveProductImageActionGroup" stepKey="seeActiveTestImageNewForSecondOption"> + <argument name="fileName" value="{{TestImageNew.filename}}"/> + </actionGroup> + </test> +</tests> diff --git a/app/code/Magento/Catalog/Test/Mftf/Test/StorefrontEnsureThatAccordionAnchorIsVisibleOnViewportOnceClickedTest.xml b/app/code/Magento/Catalog/Test/Mftf/Test/StorefrontEnsureThatAccordionAnchorIsVisibleOnViewportOnceClickedTest.xml index 30461a30e53cb..020c3d85b9fdf 100644 --- a/app/code/Magento/Catalog/Test/Mftf/Test/StorefrontEnsureThatAccordionAnchorIsVisibleOnViewportOnceClickedTest.xml +++ b/app/code/Magento/Catalog/Test/Mftf/Test/StorefrontEnsureThatAccordionAnchorIsVisibleOnViewportOnceClickedTest.xml @@ -52,7 +52,7 @@ </createData> <!-- Login as admin --> - <actionGroup ref="LoginAsAdmin" stepKey="loginAsAdmin"/> + <actionGroup ref="AdminLoginActionGroup" stepKey="loginAsAdmin"/> </before> <after> <!-- Delete reviews --> @@ -69,6 +69,9 @@ <deleteData createDataKey="createThirdAttribute" stepKey="deleteThirdAttribute"/> <deleteData createDataKey="createFourthAttribute" stepKey="deleteFourthAttribute"/> <deleteData createDataKey="createProduct" stepKey="deleteProduct"/> + + <!-- Reindex invalidated indices after product attribute has been created/deleted --> + <magentoCron groups="index" stepKey="reindexInvalidatedIndices"/> </after> <!-- Edit the product and set those attributes values --> @@ -87,8 +90,14 @@ <actionGroup ref="SaveProductFormActionGroup" stepKey="saveSimpleProduct"/> <!-- Go to frontend and make a user account and login with it --> - <actionGroup ref="SignUpNewUserFromStorefrontActionGroup" stepKey="signUpNewUser"> - <argument name="Customer" value="CustomerEntityOne"/> + <actionGroup ref="StorefrontOpenCustomerAccountCreatePageActionGroup" stepKey="openCreateAccountPage"/> + <actionGroup ref="StorefrontFillCustomerAccountCreationFormActionGroup" stepKey="fillCreateAccountForm"> + <argument name="customer" value="CustomerEntityOne"/> + </actionGroup> + <actionGroup ref="StorefrontClickCreateAnAccountCustomerAccountCreationFormActionGroup" stepKey="submitCreateAccountForm"/> + <actionGroup ref="AssertMessageCustomerCreateAccountActionGroup" stepKey="seeSuccessMessage"> + <argument name="messageType" value="success"/> + <argument name="message" value="Thank you for registering with Main Website Store."/> </actionGroup> <!-- Go to the product view page --> diff --git a/app/code/Magento/Catalog/Test/Mftf/Test/StorefrontForthLevelCategoryTest.xml b/app/code/Magento/Catalog/Test/Mftf/Test/StorefrontForthLevelCategoryTest.xml index bb46f8010eaa8..74264149cf1cb 100644 --- a/app/code/Magento/Catalog/Test/Mftf/Test/StorefrontForthLevelCategoryTest.xml +++ b/app/code/Magento/Catalog/Test/Mftf/Test/StorefrontForthLevelCategoryTest.xml @@ -35,7 +35,7 @@ <deleteData createDataKey="category2" stepKey="deleteCategory2"/> <deleteData createDataKey="category1" stepKey="deleteCategory1"/> </after> - <amOnPage url="{{StorefrontHomePage.url}}" stepKey="amOnStorefrontPage"/> + <actionGroup ref="StorefrontOpenHomePageActionGroup" stepKey="amOnStorefrontPage"/> <moveMouseOver selector="{{StorefrontHeaderSection.NavigationCategoryByName($$category1.name$$)}}" stepKey="hoverCategoryLevelOne"/> diff --git a/app/code/Magento/Catalog/Test/Mftf/Test/StorefrontFotoramaArrowsTest.xml b/app/code/Magento/Catalog/Test/Mftf/Test/StorefrontFotoramaArrowsTest.xml new file mode 100644 index 0000000000000..df1eb0c502e18 --- /dev/null +++ b/app/code/Magento/Catalog/Test/Mftf/Test/StorefrontFotoramaArrowsTest.xml @@ -0,0 +1,61 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + /** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +--> + +<tests xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/testSchema.xsd"> + <test name="AdminAddDefaultImageSimpleProductTest"> + <annotations> + <features value="Catalog"/> + <title value="Storefront Fotorama arrows test"/> + <description value="Check arrows next to the thumbs are not visible than there is room for all pictures."/> + <severity value="BLOCKER"/> + <group value="Catalog"/> + </annotations> + <before> + <createData entity="_defaultCategory" stepKey="createCategory"/> + <createData entity="_defaultProduct" stepKey="createProduct"> + <requiredEntity createDataKey="createCategory"/> + </createData> + <actionGroup ref="AdminLoginActionGroup" stepKey="loginAsAdmin"/> + </before> + <after> + <actionGroup ref="AdminLogoutActionGroup" stepKey="logout"/> + <deleteData createDataKey="createProduct" stepKey="deleteProduct"/> + <deleteData createDataKey="createCategory" stepKey="deleteCategory"/> + </after> + + <!-- Open Product for edit --> + <actionGroup ref="SearchForProductOnBackendActionGroup" stepKey="findCreatedProductInGrid"> + <argument name="product" value="$$createProduct$$"/> + </actionGroup> + <actionGroup ref="OpenEditProductOnBackendActionGroup" stepKey="goToEditProductPage"> + <argument name="product" value="$$createProduct$$"/> + </actionGroup> + + <!-- Add images to product --> + <actionGroup ref="AddProductImageActionGroup" stepKey="addFirstImageToProduct"> + <argument name="image" value="MagentoLogo"/> + </actionGroup> + <actionGroup ref="AddProductImageActionGroup" stepKey="addSecondImageToProduct"> + <argument name="image" value="ProductImage"/> + </actionGroup> + <actionGroup ref="AddProductImageActionGroup" stepKey="addThirdImageToProduct"> + <argument name="image" value="TestImageNew"/> + </actionGroup> + <actionGroup ref="SaveProductFormActionGroup" stepKey="saveSimpleProduct"/> + + <!-- Assert product in storefront product page --> + <actionGroup ref="StorefrontOpenProductPageActionGroup" stepKey="openCreatedProductPage"> + <argument name="productUrl" value="$$createProduct.name$$"/> + </actionGroup> + + <!-- Assert Fotorama arrows aren't visible --> + <dontSeeElement selector="{{StorefrontProductMediaSection.fotoramaPrevButton}}" stepKey="dontSeePrevButton"/> + <dontSeeElement selector="{{StorefrontProductMediaSection.fotoramaNextButton}}" stepKey="dontSeeNextButton"/> + </test> +</tests> diff --git a/app/code/Magento/Catalog/Test/Mftf/Test/StorefrontProductNameWithDoubleQuote.xml b/app/code/Magento/Catalog/Test/Mftf/Test/StorefrontProductNameWithDoubleQuote.xml deleted file mode 100644 index 07c2e8a972596..0000000000000 --- a/app/code/Magento/Catalog/Test/Mftf/Test/StorefrontProductNameWithDoubleQuote.xml +++ /dev/null @@ -1,129 +0,0 @@ -<?xml version="1.0" encoding="UTF-8"?> -<!-- - /** - * Copyright © Magento, Inc. All rights reserved. - * See COPYING.txt for license details. - */ ---> - -<tests xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" - xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/testSchema.xsd"> - <test name="StorefrontProductNameWithDoubleQuote"> - <annotations> - <features value="Catalog"/> - <stories value="Create products"/> - <title value="Product with double quote in name"/> - <description value="Product with a double quote in the name should appear correctly on the storefront"/> - <severity value="CRITICAL"/> - <group value="product"/> - <testCaseId value="MAGETWO-92384"/> - </annotations> - <before> - <createData entity="_defaultCategory" stepKey="createCategory"/> - </before> - <after> - <deleteData createDataKey="createCategory" stepKey="deleteCategory"/> - </after> - - <actionGroup ref="LoginAsAdmin" stepKey="loginAsAdmin"/> - <!--Create product via admin--> - <amOnPage url="{{AdminProductIndexPage.url}}" stepKey="goToProductIndex"/> - <waitForPageLoad stepKey="waitForProductIndexPage"/> - <actionGroup ref="GoToCreateProductPageActionGroup" stepKey="goToProductCreatePage"> - <argument name="product" value="SimpleProductNameWithDoubleQuote"/> - </actionGroup> - <actionGroup ref="FillMainProductFormActionGroup" stepKey="fillProductForm"> - <argument name="product" value="SimpleProductNameWithDoubleQuote"/> - </actionGroup> - <searchAndMultiSelectOption selector="{{AdminProductFormSection.categoriesDropdown}}" parameterArray="[$$createCategory.name$$]" stepKey="selectCategory"/> - <actionGroup ref="AddProductImageActionGroup" stepKey="addImageToProduct"> - <argument name="image" value="ProductImage"/> - </actionGroup> - <actionGroup ref="SaveProductFormActionGroup" stepKey="saveProduct"/> - - <!--Run re-index task--> - <magentoCLI command="indexer:reindex" stepKey="reindex"/> - - <!--Check product in category listing--> - <amOnPage url="{{StorefrontCategoryPage.url($$createCategory.name$$)}}" stepKey="goToCategoryPage"/> - <seeElement selector="{{StorefrontCategoryProductSection.ProductImageByNameAndSrc(SimpleProductNameWithDoubleQuote.name, ProductImage.fileName)}}" stepKey="seeCorrectImageCategoryPage"/> - <see selector="{{StorefrontCategoryProductSection.ProductTitleByName(SimpleProductNameWithDoubleQuote.name)}}" userInput="{{SimpleProductNameWithDoubleQuote.name}}" stepKey="seeCorrectNameCategoryPage"/> - <see selector="{{StorefrontCategoryProductSection.ProductPriceByName(SimpleProductNameWithDoubleQuote.name)}}" userInput="${{SimpleProductNameWithDoubleQuote.price}}" stepKey="seeCorrectPriceCategoryPage"/> - <!--Open product display page--> - <click selector="{{StorefrontCategoryProductSection.ProductTitleByName(SimpleProductNameWithDoubleQuote.name)}}" stepKey="clickProductToGoProductPage"/> - <waitForPageLoad stepKey="waitForProductDisplayPageLoad"/> - <see selector="{{StorefrontProductInfoMainSection.productName}}" userInput="{{SimpleProductNameWithDoubleQuote.name}}" stepKey="seeCorrectName"/> - <see selector="{{StorefrontProductInfoMainSection.productSku}}" userInput="{{SimpleProductNameWithDoubleQuote.sku}}" stepKey="seeCorrectSku"/> - <see selector="{{StorefrontProductInfoMainSection.productPrice}}" userInput="${{SimpleProductNameWithDoubleQuote.price}}" stepKey="seeCorrectPrice"/> - <seeElement selector="{{StorefrontProductInfoMainSection.productImageSrc(ProductImage.fileName)}}" stepKey="seeCorrectImage"/> - <see selector="{{StorefrontProductInfoMainSection.stock}}" userInput="In Stock" stepKey="seeInStock"/> - <see selector="{{StorefrontNavigationSection.breadcrumbs}}" userInput="$$createCategory.name$$" stepKey="seeCorrectBreadCrumbCategory"/> - <see selector="{{StorefrontNavigationSection.breadcrumbs}}" userInput="{{SimpleProductNameWithDoubleQuote.name}}" stepKey="seeCorrectBreadCrumbProduct"/> - - <!--Remove product--> - <actionGroup ref="DeleteProductUsingProductGridActionGroup" stepKey="deleteProduct"> - <argument name="product" value="SimpleProductNameWithDoubleQuote"/> - </actionGroup> - </test> - - <test name="StorefrontProductNameWithHTMLEntities"> - <annotations> - <features value="Catalog"/> - <stories value="Create product"/> - <title value=":Proudct with html special characters in name"/> - <description value="Product with html entities in the name should appear correctly on the PDP breadcrumbs on storefront"/> - <severity value="CRITICAL"/> - <group value="product"/> - <testCaseId value="MAGETWO-93794"/> - </annotations> - <before> - <createData entity="_defaultCategory" stepKey="createCategoryOne"/> - <createData entity="productWithHTMLEntityOne" stepKey="productOne"> - <requiredEntity createDataKey="createCategoryOne"/> - </createData> - <createData entity="productWithHTMLEntityTwo" stepKey="productTwo"> - <requiredEntity createDataKey="createCategoryOne"/> - </createData> - </before> - <after> - <deleteData createDataKey="productOne" stepKey="deleteProductOne"/> - <deleteData createDataKey="productTwo" stepKey="deleteProductTwo"/> - <deleteData createDataKey="createCategoryOne" stepKey="deleteCategory"/> - </after> - - <!--Run re-index task--> - <magentoCLI command="indexer:reindex" stepKey="reindex"/> - - <!--Check product in category listing--> - <amOnPage url="{{StorefrontCategoryPage.url($$createCategoryOne.name$$)}}" stepKey="navigateToCategoryPage"/> - <waitForPageLoad stepKey="waitforCategoryPageToLoad"/> - <see selector="{{StorefrontCategoryProductSection.ProductTitleByName(productWithHTMLEntityOne.name)}}" userInput="{{productWithHTMLEntityOne.name}}" stepKey="seeCorrectNameProd1CategoryPage"/> - <see selector="{{StorefrontCategoryProductSection.ProductTitleByName(productWithHTMLEntityTwo.name)}}" userInput="{{productWithHTMLEntityTwo.name}}" stepKey="seeCorrectNameProd2CategoryPage"/> - - <!--Open product display page--> - <click selector="{{StorefrontCategoryProductSection.ProductTitleByName(productWithHTMLEntityOne.name)}}" stepKey="clickProductToGoProductPage"/> - <waitForPageLoad stepKey="waitForProductDisplayPageLoad2"/> - - <see selector="{{StorefrontProductInfoMainSection.productName}}" userInput="{{productWithHTMLEntityOne.name}}" stepKey="seeCorrectName"/> - <see selector="{{StorefrontProductInfoMainSection.productSku}}" userInput="{{productWithHTMLEntityOne.sku}}" stepKey="seeCorrectSku"/> - <see selector="{{StorefrontProductInfoMainSection.productPrice}}" userInput="${{productWithHTMLEntityOne.price}}" stepKey="seeCorrectPrice"/> - - <!--Veriy the breadcrumbs on Product Display page--> - <see selector="{{StorefrontNavigationSection.breadcrumbs}}" userInput="Home" stepKey="seeHomePageInBreadcrumbs1"/> - <see selector="{{StorefrontNavigationSection.breadcrumbs}}" userInput="$$createCategoryOne.name$$" stepKey="seeCorrectBreadCrumbCategory"/> - <see selector="{{StorefrontNavigationSection.breadcrumbs}}" userInput="$$productOne.name$$" stepKey="seeCorrectBreadCrumbProduct"/> - - <click selector="{{StorefrontNavigationSection.topCategory($$createCategoryOne.name$$)}}" stepKey="goBackToCategoryPage"/> - <waitForPageLoad stepKey="waitforCategoryPageToLoad2"/> - - <!--Open product display page--> - <click selector="{{StorefrontCategoryProductSection.ProductTitleByName(productWithHTMLEntityTwo.name)}}" stepKey="clickProductToGoSecondProductPage"/> - <waitForPageLoad stepKey="waitForProductDisplayPageLoad3"/> - - <!--Verify the breadcrumbs on Product Display page--> - <see selector="{{StorefrontNavigationSection.breadcrumbs}}" userInput="Home" stepKey="seeHomePageInBreadcrumbs2"/> - <see selector="{{StorefrontNavigationSection.breadcrumbs}}" userInput="$$createCategoryOne.name$$" stepKey="seeCorrectBreadCrumbCategory2"/> - <see selector="{{StorefrontNavigationSection.breadcrumbs}}" userInput="$$productTwo.name$$" stepKey="seeCorrectBreadCrumbProduct2"/> - - </test> -</tests> diff --git a/app/code/Magento/Catalog/Test/Mftf/Test/StorefrontProductNameWithDoubleQuoteTest/StorefrontProductNameWithDoubleQuoteTest.xml b/app/code/Magento/Catalog/Test/Mftf/Test/StorefrontProductNameWithDoubleQuoteTest/StorefrontProductNameWithDoubleQuoteTest.xml new file mode 100644 index 0000000000000..bcd5d7b851db3 --- /dev/null +++ b/app/code/Magento/Catalog/Test/Mftf/Test/StorefrontProductNameWithDoubleQuoteTest/StorefrontProductNameWithDoubleQuoteTest.xml @@ -0,0 +1,67 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + /** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +--> +<tests xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/testSchema.xsd"> + <test name="StorefrontProductNameWithDoubleQuoteTest"> + <annotations> + <features value="Catalog"/> + <stories value="Create products"/> + <title value="Product with double quote in name"/> + <description value="Product with a double quote in the name should appear correctly on the storefront"/> + <severity value="BLOCKER"/> + <group value="product"/> + <testCaseId value="MAGETWO-92384"/> + </annotations> + <before> + <createData entity="_defaultCategory" stepKey="createCategory"/> + </before> + <after> + <deleteData createDataKey="createCategory" stepKey="deleteCategory"/> + </after> + + <actionGroup ref="AdminLoginActionGroup" stepKey="loginAsAdmin"/> + <!--Create product via admin--> + <amOnPage url="{{AdminProductIndexPage.url}}" stepKey="goToProductIndex"/> + <waitForPageLoad stepKey="waitForProductIndexPage"/> + <actionGroup ref="GoToCreateProductPageActionGroup" stepKey="goToProductCreatePage"> + <argument name="product" value="SimpleProductNameWithDoubleQuote"/> + </actionGroup> + <actionGroup ref="FillMainProductFormActionGroup" stepKey="fillProductForm"> + <argument name="product" value="SimpleProductNameWithDoubleQuote"/> + </actionGroup> + <searchAndMultiSelectOption selector="{{AdminProductFormSection.categoriesDropdown}}" parameterArray="[$$createCategory.name$$]" stepKey="selectCategory"/> + <actionGroup ref="AddProductImageActionGroup" stepKey="addImageToProduct"> + <argument name="image" value="ProductImage"/> + </actionGroup> + <actionGroup ref="SaveProductFormActionGroup" stepKey="saveProduct"/> + + <!--Run re-index task--> + <magentoCLI command="indexer:reindex" stepKey="reindex"/> + + <!--Check product in category listing--> + <amOnPage url="{{StorefrontCategoryPage.url($$createCategory.name$$)}}" stepKey="goToCategoryPage"/> + <seeElement selector="{{StorefrontCategoryProductSection.ProductImageByNameAndSrc(SimpleProductNameWithDoubleQuote.name, ProductImage.fileName)}}" stepKey="seeCorrectImageCategoryPage"/> + <see selector="{{StorefrontCategoryProductSection.ProductTitleByName(SimpleProductNameWithDoubleQuote.name)}}" userInput="{{SimpleProductNameWithDoubleQuote.name}}" stepKey="seeCorrectNameCategoryPage"/> + <see selector="{{StorefrontCategoryProductSection.ProductPriceByName(SimpleProductNameWithDoubleQuote.name)}}" userInput="${{SimpleProductNameWithDoubleQuote.price}}" stepKey="seeCorrectPriceCategoryPage"/> + <!--Open product display page--> + <click selector="{{StorefrontCategoryProductSection.ProductTitleByName(SimpleProductNameWithDoubleQuote.name)}}" stepKey="clickProductToGoProductPage"/> + <waitForPageLoad stepKey="waitForProductDisplayPageLoad"/> + <see selector="{{StorefrontProductInfoMainSection.productName}}" userInput="{{SimpleProductNameWithDoubleQuote.name}}" stepKey="seeCorrectName"/> + <see selector="{{StorefrontProductInfoMainSection.productSku}}" userInput="{{SimpleProductNameWithDoubleQuote.sku}}" stepKey="seeCorrectSku"/> + <see selector="{{StorefrontProductInfoMainSection.productPrice}}" userInput="${{SimpleProductNameWithDoubleQuote.price}}" stepKey="seeCorrectPrice"/> + <seeElement selector="{{StorefrontProductInfoMainSection.productImageSrc(ProductImage.fileName)}}" stepKey="seeCorrectImage"/> + <see selector="{{StorefrontProductInfoMainSection.stock}}" userInput="In Stock" stepKey="seeInStock"/> + <see selector="{{StorefrontNavigationSection.breadcrumbs}}" userInput="$$createCategory.name$$" stepKey="seeCorrectBreadCrumbCategory"/> + <see selector="{{StorefrontNavigationSection.breadcrumbs}}" userInput="{{SimpleProductNameWithDoubleQuote.name}}" stepKey="seeCorrectBreadCrumbProduct"/> + + <!--Remove product--> + <actionGroup ref="DeleteProductUsingProductGridActionGroup" stepKey="deleteProduct"> + <argument name="product" value="SimpleProductNameWithDoubleQuote"/> + </actionGroup> + </test> +</tests> diff --git a/app/code/Magento/Catalog/Test/Mftf/Test/StorefrontProductNameWithDoubleQuoteTest/StorefrontProductNameWithHTMLEntitiesTest.xml b/app/code/Magento/Catalog/Test/Mftf/Test/StorefrontProductNameWithDoubleQuoteTest/StorefrontProductNameWithHTMLEntitiesTest.xml new file mode 100644 index 0000000000000..bd2c22c90318a --- /dev/null +++ b/app/code/Magento/Catalog/Test/Mftf/Test/StorefrontProductNameWithDoubleQuoteTest/StorefrontProductNameWithHTMLEntitiesTest.xml @@ -0,0 +1,70 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + /** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +--> +<tests xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/testSchema.xsd"> + <test name="StorefrontProductNameWithHTMLEntitiesTest"> + <annotations> + <features value="Catalog"/> + <stories value="Create product"/> + <title value=":Proudct with html special characters in name"/> + <description value="Product with html entities in the name should appear correctly on the PDP breadcrumbs on storefront"/> + <severity value="CRITICAL"/> + <group value="product"/> + <testCaseId value="MAGETWO-93794"/> + </annotations> + <before> + <createData entity="_defaultCategory" stepKey="createCategoryOne"/> + <createData entity="productWithHTMLEntityOne" stepKey="productOne"> + <requiredEntity createDataKey="createCategoryOne"/> + </createData> + <createData entity="productWithHTMLEntityTwo" stepKey="productTwo"> + <requiredEntity createDataKey="createCategoryOne"/> + </createData> + </before> + <after> + <deleteData createDataKey="productOne" stepKey="deleteProductOne"/> + <deleteData createDataKey="productTwo" stepKey="deleteProductTwo"/> + <deleteData createDataKey="createCategoryOne" stepKey="deleteCategory"/> + </after> + + <!--Run re-index task--> + <magentoCLI command="indexer:reindex" stepKey="reindex"/> + + <!--Check product in category listing--> + <amOnPage url="{{StorefrontCategoryPage.url($$createCategoryOne.name$$)}}" stepKey="navigateToCategoryPage"/> + <waitForPageLoad stepKey="waitforCategoryPageToLoad"/> + <see selector="{{StorefrontCategoryProductSection.ProductTitleByName(productWithHTMLEntityOne.name)}}" userInput="{{productWithHTMLEntityOne.name}}" stepKey="seeCorrectNameProd1CategoryPage"/> + <see selector="{{StorefrontCategoryProductSection.ProductTitleByName(productWithHTMLEntityTwo.name)}}" userInput="{{productWithHTMLEntityTwo.name}}" stepKey="seeCorrectNameProd2CategoryPage"/> + + <!--Open product display page--> + <click selector="{{StorefrontCategoryProductSection.ProductTitleByName(productWithHTMLEntityOne.name)}}" stepKey="clickProductToGoProductPage"/> + <waitForPageLoad stepKey="waitForProductDisplayPageLoad2"/> + + <see selector="{{StorefrontProductInfoMainSection.productName}}" userInput="{{productWithHTMLEntityOne.name}}" stepKey="seeCorrectName"/> + <see selector="{{StorefrontProductInfoMainSection.productSku}}" userInput="{{productWithHTMLEntityOne.sku}}" stepKey="seeCorrectSku"/> + <see selector="{{StorefrontProductInfoMainSection.productPrice}}" userInput="${{productWithHTMLEntityOne.price}}" stepKey="seeCorrectPrice"/> + + <!--Veriy the breadcrumbs on Product Display page--> + <see selector="{{StorefrontNavigationSection.breadcrumbs}}" userInput="Home" stepKey="seeHomePageInBreadcrumbs1"/> + <see selector="{{StorefrontNavigationSection.breadcrumbs}}" userInput="$$createCategoryOne.name$$" stepKey="seeCorrectBreadCrumbCategory"/> + <see selector="{{StorefrontNavigationSection.breadcrumbs}}" userInput="$$productOne.name$$" stepKey="seeCorrectBreadCrumbProduct"/> + + <click selector="{{StorefrontNavigationSection.topCategory($$createCategoryOne.name$$)}}" stepKey="goBackToCategoryPage"/> + <waitForPageLoad stepKey="waitforCategoryPageToLoad2"/> + + <!--Open product display page--> + <click selector="{{StorefrontCategoryProductSection.ProductTitleByName(productWithHTMLEntityTwo.name)}}" stepKey="clickProductToGoSecondProductPage"/> + <waitForPageLoad stepKey="waitForProductDisplayPageLoad3"/> + + <!--Verify the breadcrumbs on Product Display page--> + <see selector="{{StorefrontNavigationSection.breadcrumbs}}" userInput="Home" stepKey="seeHomePageInBreadcrumbs2"/> + <see selector="{{StorefrontNavigationSection.breadcrumbs}}" userInput="$$createCategoryOne.name$$" stepKey="seeCorrectBreadCrumbCategory2"/> + <see selector="{{StorefrontNavigationSection.breadcrumbs}}" userInput="$$productTwo.name$$" stepKey="seeCorrectBreadCrumbProduct2"/> + + </test> +</tests> diff --git a/app/code/Magento/Catalog/Test/Mftf/Test/StorefrontProductWithEmptyAttributeTest.xml b/app/code/Magento/Catalog/Test/Mftf/Test/StorefrontProductWithEmptyAttributeTest.xml index db693b7229b17..a311b63418a69 100644 --- a/app/code/Magento/Catalog/Test/Mftf/Test/StorefrontProductWithEmptyAttributeTest.xml +++ b/app/code/Magento/Catalog/Test/Mftf/Test/StorefrontProductWithEmptyAttributeTest.xml @@ -14,7 +14,7 @@ <stories value="Create products"/> <title value="Product attribute is not visible on storefront if it is empty"/> <description value="Product attribute should not be visible on storefront if it is empty"/> - <severity value="MAJOR"/> + <severity value="BLOCKER"/> <testCaseId value="MAGETWO-91893"/> <group value="product"/> </annotations> @@ -28,9 +28,12 @@ </actionGroup> <deleteData createDataKey="createPreReqCategory" stepKey="deletePreReqCategory"/> <deleteData createDataKey="createProductAttribute" stepKey="deleteProductAttribute"/> - <actionGroup ref="logout" stepKey="logoutOfAdmin"/> + <actionGroup ref="AdminLogoutActionGroup" stepKey="logoutOfAdmin"/> + + <!-- Reindex invalidated indices after product attribute has been created/deleted --> + <magentoCron groups="index" stepKey="reindexInvalidatedIndices"/> </after> - <actionGroup ref="LoginAsAdmin" stepKey="loginAsAdmin1"/> + <actionGroup ref="AdminLoginActionGroup" stepKey="loginAsAdmin1"/> <amOnPage url="{{AdminProductAttributeSetGridPage.url}}" stepKey="amOnAttributeSetPage"/> <click selector="{{AdminProductAttributeSetGridSection.AttributeSetName('Default')}}" stepKey="chooseDefaultAttributeSet"/> <waitForPageLoad stepKey="waitForAttributeSetPageLoad"/> diff --git a/app/code/Magento/Catalog/Test/Mftf/Test/StorefrontProductsCompareWithEmptyAttributeTest.xml b/app/code/Magento/Catalog/Test/Mftf/Test/StorefrontProductsCompareWithEmptyAttributeTest.xml index d7f98c4cdd307..c7817ed181ae0 100644 --- a/app/code/Magento/Catalog/Test/Mftf/Test/StorefrontProductsCompareWithEmptyAttributeTest.xml +++ b/app/code/Magento/Catalog/Test/Mftf/Test/StorefrontProductsCompareWithEmptyAttributeTest.xml @@ -32,9 +32,12 @@ <deleteData createDataKey="createProductAttribute" stepKey="deleteProductAttribute"/> <deleteData createDataKey="createSimpleProduct1" stepKey="deleteSimpleProduct1"/> <deleteData createDataKey="createSimpleProduct2" stepKey="deleteSimpleProduct2"/> - <actionGroup ref="logout" stepKey="logoutOfAdmin"/> + <actionGroup ref="AdminLogoutActionGroup" stepKey="logoutOfAdmin"/> + + <!-- Reindex invalidated indices after product attribute has been created/deleted --> + <magentoCron groups="index" stepKey="reindexInvalidatedIndices"/> </after> - <actionGroup ref="LoginAsAdmin" stepKey="loginAsAdmin1"/> + <actionGroup ref="AdminLoginActionGroup" stepKey="loginAsAdmin1"/> <amOnPage url="{{AdminProductAttributeSetGridPage.url}}" stepKey="amOnAttributeSetPage"/> <click selector="{{AdminProductAttributeSetGridSection.AttributeSetName('Default')}}" stepKey="chooseDefaultAttributeSet"/> <waitForPageLoad stepKey="waitForAttributeSetPageLoad"/> diff --git a/app/code/Magento/Catalog/Test/Mftf/Test/StorefrontPurchaseProductCustomOptionsDifferentStoreViewsTest.xml b/app/code/Magento/Catalog/Test/Mftf/Test/StorefrontPurchaseProductCustomOptionsDifferentStoreViewsTest.xml index 8637de9711da0..95af6e299662b 100644 --- a/app/code/Magento/Catalog/Test/Mftf/Test/StorefrontPurchaseProductCustomOptionsDifferentStoreViewsTest.xml +++ b/app/code/Magento/Catalog/Test/Mftf/Test/StorefrontPurchaseProductCustomOptionsDifferentStoreViewsTest.xml @@ -32,7 +32,7 @@ <field key="price">100</field> </createData> - <actionGroup ref="LoginAsAdmin" stepKey="loginAsAdmin1"/> + <actionGroup ref="AdminLoginActionGroup" stepKey="loginAsAdmin1"/> <!--Create storeView 1--> @@ -69,7 +69,7 @@ <amOnPage url="{{AdminProductIndexPage.url}}" stepKey="amOnProductGridPage"/> <actionGroup ref="ClearFiltersAdminDataGridActionGroup" stepKey="clearProductsGridFilters"/> - <actionGroup ref="logout" stepKey="logout"/> + <actionGroup ref="AdminLogoutActionGroup" stepKey="logout"/> <actionGroup ref="StorefrontCustomerLogoutActionGroup" stepKey="customerLogoutStorefront"/> </after> @@ -209,7 +209,7 @@ <!-- Switch to FR Store View Storefront --> - <amOnPage url="{{StorefrontHomePage.url}}" stepKey="amOnProduct4Page"/> + <actionGroup ref="StorefrontOpenHomePageActionGroup" stepKey="amOnProduct4Page"/> <actionGroup ref="StorefrontSwitchStoreViewActionGroup" stepKey="switchStore"> <argument name="storeView" value="customStoreFR"/> diff --git a/app/code/Magento/Catalog/Test/Mftf/Test/StorefrontPurchaseProductWithCustomOptionsTest.xml b/app/code/Magento/Catalog/Test/Mftf/Test/StorefrontPurchaseProductWithCustomOptionsTest.xml index c0653d9dbba7e..09b596f298e0f 100644 --- a/app/code/Magento/Catalog/Test/Mftf/Test/StorefrontPurchaseProductWithCustomOptionsTest.xml +++ b/app/code/Magento/Catalog/Test/Mftf/Test/StorefrontPurchaseProductWithCustomOptionsTest.xml @@ -36,7 +36,7 @@ <deleteData createDataKey="createProduct" stepKey="deleteProduct"/> <deleteData createDataKey="createCategory" stepKey="deleteCategory"/> <actionGroup ref="AdminOrdersGridClearFiltersActionGroup" stepKey="clearOrderListingFilters"/> - <actionGroup ref="logout" stepKey="logoutAdmin"/> + <actionGroup ref="AdminLogoutActionGroup" stepKey="logoutAdmin"/> <!-- Logout customer --> <actionGroup ref="StorefrontCustomerLogoutActionGroup" stepKey="customerLogoutStorefront"/> </after> @@ -128,7 +128,7 @@ <!-- Login to Admin and open Order --> - <actionGroup ref="LoginAsAdmin" stepKey="loginAsAdmin1"/> + <actionGroup ref="AdminLoginActionGroup" stepKey="loginAsAdmin1"/> <actionGroup ref="FilterOrderGridByIdActionGroup" stepKey="filterByOrderId"> <argument name="orderId" value="$grabOrderNumber"/> diff --git a/app/code/Magento/Catalog/Test/Mftf/Test/StorefrontPurchaseProductWithCustomOptionsWithLongValuesTitle.xml b/app/code/Magento/Catalog/Test/Mftf/Test/StorefrontPurchaseProductWithCustomOptionsWithLongValuesTitle.xml deleted file mode 100644 index 7a667ce9667d4..0000000000000 --- a/app/code/Magento/Catalog/Test/Mftf/Test/StorefrontPurchaseProductWithCustomOptionsWithLongValuesTitle.xml +++ /dev/null @@ -1,122 +0,0 @@ -<?xml version="1.0" encoding="UTF-8"?> -<!-- - /** - * Copyright © Magento, Inc. All rights reserved. - * See COPYING.txt for license details. - */ ---> - -<tests xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" - xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/testSchema.xsd"> - <test name="StorefrontPurchaseProductWithCustomOptionsWithLongValuesTitle"> - <annotations> - <features value="Catalog"/> - <stories value="Custom options"/> - <group value="Catalog"/> - <title value="Admin should be able to see the full title of the selected custom option value in the order"/> - <description value="Admin should be able to see the full title of the selected custom option value in the order"/> - <severity value="MAJOR"/> - <testCaseId value="MC-3043"/> - <skip> - <issueId value="MQE-1128"/> - </skip> - </annotations> - <before> - <!--Create Simple Product with Custom Options--> - <createData entity="_defaultCategory" stepKey="createCategory"/> - <createData entity="_defaultProduct" stepKey="createProduct"> - <requiredEntity createDataKey="createCategory"/> - <field key="price">17</field> - </createData> - <updateData createDataKey="createProduct" entity="productWithOptions2" stepKey="updateProductWithOptions"/> - - <createData entity="Simple_US_Customer" stepKey="createCustomer"/> - </before> - <after> - <deleteData createDataKey="createCustomer" stepKey="deleteCustomer"/> - <deleteData createDataKey="createProduct" stepKey="deleteProduct"/> - <deleteData createDataKey="createCategory" stepKey="deleteCategory"/> - <actionGroup ref="AdminOrdersGridClearFiltersActionGroup" stepKey="clearFilters"/> - <actionGroup ref="logout" stepKey="logout"/> - </after> - - <!-- Login Customer Storefront --> - - <amOnPage url="{{StorefrontCustomerSignInPage.url}}" stepKey="amOnSignInPage"/> - <fillField userInput="$$createCustomer.email$$" selector="{{StorefrontCustomerSignInFormSection.emailField}}" stepKey="fillEmail"/> - <fillField userInput="$$createCustomer.password$$" selector="{{StorefrontCustomerSignInFormSection.passwordField}}" stepKey="fillPassword"/> - <click selector="{{StorefrontCustomerSignInFormSection.signInAccountButton}}" stepKey="clickSignInAccountButton"/> - - <!-- Checking the correctness of displayed prices for user parameters --> - - <amOnPage url="{{StorefrontHomePage.url}}$$createProduct.custom_attributes[url_key]$$.html" stepKey="amOnProductPage"/> - <seeElement selector="{{StorefrontProductInfoMainSection.productAttributeOptionsDropDown(ProductOptionDropDownWithLongValuesTitle.title, ProductOptionValueDropdownLongTitle1.price)}}" stepKey="checkDropDownProductOption"/> - - <!-- Adding items to the checkout --> - - <selectOption userInput="{{ProductOptionValueDropdownLongTitle1.price}}" selector="{{StorefrontProductInfoMainSection.productOptionSelect(ProductOptionDropDownWithLongValuesTitle.title)}}" stepKey="seeProductOptionDropDown"/> - <grabTextFrom selector="{{StorefrontProductInfoMainSection.productPrice}}" stepKey="finalProductPrice"/> - - <actionGroup ref="StorefrontAddToCartCustomOptionsProductPageActionGroup" stepKey="addToCartFromStorefrontProductPage"> - <argument name="productName" value="$$createProduct.name$$"/> - </actionGroup> - - <!-- Checking the correctness of displayed custom options for user parameters on checkout --> - - <actionGroup ref="GoToCheckoutFromMinicartActionGroup" stepKey="goToCheckoutFromMinicart" /> - - <conditionalClick selector="{{CheckoutPaymentSection.cartItemsArea}}" dependentSelector="{{CheckoutPaymentSection.cartItemsArea}}" visible="true" stepKey="exposeMiniCart"/> - - <waitForLoadingMaskToDisappear stepKey="waitForLoadingMaskForCartItem"/> - <waitForElement selector="{{CheckoutPaymentSection.cartItemsAreaActive}}" time="30" stepKey="waitForCartItemsAreaActive"/> - - <see selector="{{CheckoutPaymentSection.cartItems}}" userInput="$$createProduct.name$$" stepKey="seeProductInCart"/> - - <conditionalClick selector="{{CheckoutPaymentSection.ProductOptionsByProductItemName($$createProduct.name$$)}}" dependentSelector="{{CheckoutPaymentSection.ProductOptionsActiveByProductItemName($$createProduct.name$$)}}" visible="false" stepKey="exposeProductOptions"/> - - <see selector="{{CheckoutPaymentSection.ProductOptionsActiveByProductItemName($$createProduct.name$$)}}" userInput="{{ProductOptionValueDropdownLongTitle1.title}}" stepKey="seeProductOptionValueDropdown1Input1"/> - - <!--Select shipping method--> - - <actionGroup ref="CheckoutSelectFlatRateShippingMethodActionGroup" stepKey="selectFlatRateShippingMethod"/> - <waitForElement selector="{{CheckoutShippingSection.next}}" time="30" stepKey="waitForNextButton"/> - <click selector="{{CheckoutShippingSection.next}}" stepKey="clickNext"/> - <waitForLoadingMaskToDisappear stepKey="waitForLoadingMaskAfterClickNext"/> - - <!-- Checkout select Check/Money Order payment --> - <actionGroup ref="CheckoutSelectCheckMoneyOrderPaymentActionGroup" stepKey="selectCheckMoneyPayment"/> - - <!-- Place Order --> - - <actionGroup ref="CheckoutSelectCheckMoneyOrderPaymentActionGroup" stepKey="selectPaymentMethod"/> - <waitForElementVisible selector="{{CheckoutPaymentSection.placeOrder}}" time="30" stepKey="waitForPlaceOrderButton"/> - <click selector="{{CheckoutPaymentSection.placeOrder}}" stepKey="clickPlaceOrder"/> - - <grabTextFrom selector="{{CheckoutSuccessMainSection.orderNumber22}}" stepKey="grabOrderNumber"/> - - <!-- Login to Admin and open Order --> - - <actionGroup ref="LoginAsAdmin" stepKey="loginAsAdmin1"/> - - <amOnPage url="{{AdminOrdersPage.url}}" stepKey="onOrdersPage"/> - <waitForLoadingMaskToDisappear stepKey="waitForLoadingMaskToDisappearOnOrdersPage"/> - <actionGroup ref="ClearFiltersAdminDataGridActionGroup" stepKey="clearGridFilter"/> - <fillField selector="{{AdminOrdersGridSection.search}}" userInput="{$grabOrderNumber}" stepKey="fillOrderNum"/> - <click selector="{{AdminOrdersGridSection.submitSearch}}" stepKey="submitSearchOrderNum"/> - <waitForLoadingMaskToDisappear stepKey="waitForLoadingMaskToDisappearOnSearch"/> - <click selector="{{AdminOrdersGridSection.firstRow}}" stepKey="clickOrderRow"/> - <waitForPageLoad stepKey="waitForOrderPageToLoad"/> - - <!-- Checking the correctness of displayed custom options for user parameters on Order --> - - <dontSee selector="{{AdminOrderItemsOrderedSection.productNameOptions}}" userInput="{{ProductOptionValueDropdownLongTitle1.title}}" stepKey="dontSeeAdminOrderProductOptionValueDropdown1"/> - <grabTextFrom selector="{{AdminOrderItemsOrderedSection.productNameOptions}} dd" stepKey="productOptionValueText"/> - <assertEquals stepKey="checkProductOptionValue"> - <actualResult type="variable">productOptionValueText</actualResult> - <expectedResult type="string">Optisfvdklvfnkljvnfdklpvnfdjklfdvnjkvfdkjnvfdjkfvndj111 ...</expectedResult> - </assertEquals> - <moveMouseOver selector="{{AdminOrderItemsOrderedSection.productNameOptions}} dd" stepKey="hoverProduct"/> - <waitForElementVisible selector="{{AdminOrderItemsOrderedSection.productNameOptions}} dd:nth-child(2)" stepKey="waitForCustomOptionValueFullName"/> - <see selector="{{AdminOrderItemsOrderedSection.productNameOptions}}" userInput="Optisfvdklvfnkljvnfdklpvnfdjklfdvnjkvfdkjnvfdjkfvndj11111Optisfvdklvfnkljvnfdklpvnfdjklfdvnjkvfdkjnvfdjkfvndj11111" stepKey="seeAdminOrderProductOptionValueDropdown1"/> - </test> -</tests> diff --git a/app/code/Magento/Catalog/Test/Mftf/Test/StorefrontPurchaseProductWithCustomOptionsWithLongValuesTitleTest.xml b/app/code/Magento/Catalog/Test/Mftf/Test/StorefrontPurchaseProductWithCustomOptionsWithLongValuesTitleTest.xml new file mode 100644 index 0000000000000..3c1dc2bc844cf --- /dev/null +++ b/app/code/Magento/Catalog/Test/Mftf/Test/StorefrontPurchaseProductWithCustomOptionsWithLongValuesTitleTest.xml @@ -0,0 +1,122 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + /** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +--> + +<tests xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/testSchema.xsd"> + <test name="StorefrontPurchaseProductWithCustomOptionsWithLongValuesTitleTest"> + <annotations> + <features value="Catalog"/> + <stories value="Custom options"/> + <group value="Catalog"/> + <title value="Admin should be able to see the full title of the selected custom option value in the order"/> + <description value="Admin should be able to see the full title of the selected custom option value in the order"/> + <severity value="MAJOR"/> + <testCaseId value="MC-3043"/> + <skip> + <issueId value="MQE-1128"/> + </skip> + </annotations> + <before> + <!--Create Simple Product with Custom Options--> + <createData entity="_defaultCategory" stepKey="createCategory"/> + <createData entity="_defaultProduct" stepKey="createProduct"> + <requiredEntity createDataKey="createCategory"/> + <field key="price">17</field> + </createData> + <updateData createDataKey="createProduct" entity="productWithOptions2" stepKey="updateProductWithOptions"/> + + <createData entity="Simple_US_Customer" stepKey="createCustomer"/> + </before> + <after> + <deleteData createDataKey="createCustomer" stepKey="deleteCustomer"/> + <deleteData createDataKey="createProduct" stepKey="deleteProduct"/> + <deleteData createDataKey="createCategory" stepKey="deleteCategory"/> + <actionGroup ref="AdminOrdersGridClearFiltersActionGroup" stepKey="clearFilters"/> + <actionGroup ref="AdminLogoutActionGroup" stepKey="logout"/> + </after> + + <!-- Login Customer Storefront --> + + <amOnPage url="{{StorefrontCustomerSignInPage.url}}" stepKey="amOnSignInPage"/> + <fillField userInput="$$createCustomer.email$$" selector="{{StorefrontCustomerSignInFormSection.emailField}}" stepKey="fillEmail"/> + <fillField userInput="$$createCustomer.password$$" selector="{{StorefrontCustomerSignInFormSection.passwordField}}" stepKey="fillPassword"/> + <click selector="{{StorefrontCustomerSignInFormSection.signInAccountButton}}" stepKey="clickSignInAccountButton"/> + + <!-- Checking the correctness of displayed prices for user parameters --> + + <amOnPage url="{{StorefrontHomePage.url}}$$createProduct.custom_attributes[url_key]$$.html" stepKey="amOnProductPage"/> + <seeElement selector="{{StorefrontProductInfoMainSection.productAttributeOptionsDropDown(ProductOptionDropDownWithLongValuesTitle.title, ProductOptionValueDropdownLongTitle1.price)}}" stepKey="checkDropDownProductOption"/> + + <!-- Adding items to the checkout --> + + <selectOption userInput="{{ProductOptionValueDropdownLongTitle1.price}}" selector="{{StorefrontProductInfoMainSection.productOptionSelect(ProductOptionDropDownWithLongValuesTitle.title)}}" stepKey="seeProductOptionDropDown"/> + <grabTextFrom selector="{{StorefrontProductInfoMainSection.productPrice}}" stepKey="finalProductPrice"/> + + <actionGroup ref="StorefrontAddToCartCustomOptionsProductPageActionGroup" stepKey="addToCartFromStorefrontProductPage"> + <argument name="productName" value="$$createProduct.name$$"/> + </actionGroup> + + <!-- Checking the correctness of displayed custom options for user parameters on checkout --> + + <actionGroup ref="GoToCheckoutFromMinicartActionGroup" stepKey="goToCheckoutFromMinicart" /> + + <conditionalClick selector="{{CheckoutPaymentSection.cartItemsArea}}" dependentSelector="{{CheckoutPaymentSection.cartItemsArea}}" visible="true" stepKey="exposeMiniCart"/> + + <waitForLoadingMaskToDisappear stepKey="waitForLoadingMaskForCartItem"/> + <waitForElement selector="{{CheckoutPaymentSection.cartItemsAreaActive}}" time="30" stepKey="waitForCartItemsAreaActive"/> + + <see selector="{{CheckoutPaymentSection.cartItems}}" userInput="$$createProduct.name$$" stepKey="seeProductInCart"/> + + <conditionalClick selector="{{CheckoutPaymentSection.ProductOptionsByProductItemName($$createProduct.name$$)}}" dependentSelector="{{CheckoutPaymentSection.ProductOptionsActiveByProductItemName($$createProduct.name$$)}}" visible="false" stepKey="exposeProductOptions"/> + + <see selector="{{CheckoutPaymentSection.ProductOptionsActiveByProductItemName($$createProduct.name$$)}}" userInput="{{ProductOptionValueDropdownLongTitle1.title}}" stepKey="seeProductOptionValueDropdown1Input1"/> + + <!--Select shipping method--> + + <actionGroup ref="CheckoutSelectFlatRateShippingMethodActionGroup" stepKey="selectFlatRateShippingMethod"/> + <waitForElement selector="{{CheckoutShippingSection.next}}" time="30" stepKey="waitForNextButton"/> + <click selector="{{CheckoutShippingSection.next}}" stepKey="clickNext"/> + <waitForLoadingMaskToDisappear stepKey="waitForLoadingMaskAfterClickNext"/> + + <!-- Checkout select Check/Money Order payment --> + <actionGroup ref="CheckoutSelectCheckMoneyOrderPaymentActionGroup" stepKey="selectCheckMoneyPayment"/> + + <!-- Place Order --> + + <actionGroup ref="CheckoutSelectCheckMoneyOrderPaymentActionGroup" stepKey="selectPaymentMethod"/> + <waitForElementVisible selector="{{CheckoutPaymentSection.placeOrder}}" time="30" stepKey="waitForPlaceOrderButton"/> + <click selector="{{CheckoutPaymentSection.placeOrder}}" stepKey="clickPlaceOrder"/> + + <grabTextFrom selector="{{CheckoutSuccessMainSection.orderNumber22}}" stepKey="grabOrderNumber"/> + + <!-- Login to Admin and open Order --> + + <actionGroup ref="AdminLoginActionGroup" stepKey="loginAsAdmin1"/> + + <amOnPage url="{{AdminOrdersPage.url}}" stepKey="onOrdersPage"/> + <waitForLoadingMaskToDisappear stepKey="waitForLoadingMaskToDisappearOnOrdersPage"/> + <actionGroup ref="ClearFiltersAdminDataGridActionGroup" stepKey="clearGridFilter"/> + <fillField selector="{{AdminOrdersGridSection.search}}" userInput="{$grabOrderNumber}" stepKey="fillOrderNum"/> + <click selector="{{AdminOrdersGridSection.submitSearch}}" stepKey="submitSearchOrderNum"/> + <waitForLoadingMaskToDisappear stepKey="waitForLoadingMaskToDisappearOnSearch"/> + <click selector="{{AdminOrdersGridSection.firstRow}}" stepKey="clickOrderRow"/> + <waitForPageLoad stepKey="waitForOrderPageToLoad"/> + + <!-- Checking the correctness of displayed custom options for user parameters on Order --> + + <dontSee selector="{{AdminOrderItemsOrderedSection.productNameOptions}}" userInput="{{ProductOptionValueDropdownLongTitle1.title}}" stepKey="dontSeeAdminOrderProductOptionValueDropdown1"/> + <grabTextFrom selector="{{AdminOrderItemsOrderedSection.productNameOptions}} dd" stepKey="productOptionValueText"/> + <assertEquals stepKey="checkProductOptionValue"> + <actualResult type="variable">productOptionValueText</actualResult> + <expectedResult type="string">Optisfvdklvfnkljvnfdklpvnfdjklfdvnjkvfdkjnvfdjkfvndj111 ...</expectedResult> + </assertEquals> + <moveMouseOver selector="{{AdminOrderItemsOrderedSection.productNameOptions}} dd" stepKey="hoverProduct"/> + <waitForElementVisible selector="{{AdminOrderItemsOrderedSection.productNameOptions}} dd:nth-child(2)" stepKey="waitForCustomOptionValueFullName"/> + <see selector="{{AdminOrderItemsOrderedSection.productNameOptions}}" userInput="Optisfvdklvfnkljvnfdklpvnfdjklfdvnjkvfdkjnvfdjkfvndj11111Optisfvdklvfnkljvnfdklpvnfdjklfdvnjkvfdkjnvfdjkfvndj11111" stepKey="seeAdminOrderProductOptionValueDropdown1"/> + </test> +</tests> diff --git a/app/code/Magento/Catalog/Test/Mftf/Test/StorefrontSelectedByQueryParamsConfigurableOptionsThumbImagesTest.xml b/app/code/Magento/Catalog/Test/Mftf/Test/StorefrontSelectedByQueryParamsConfigurableOptionsThumbImagesTest.xml new file mode 100644 index 0000000000000..941ae6fb84c56 --- /dev/null +++ b/app/code/Magento/Catalog/Test/Mftf/Test/StorefrontSelectedByQueryParamsConfigurableOptionsThumbImagesTest.xml @@ -0,0 +1,38 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + /** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +--> + +<tests xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/testSchema.xsd"> + <test name="StorefrontSelectedByQueryParamsConfigurableOptionsThumbImagesTest" + extends="StorefrontConfigurableOptionsThumbImagesTest"> + <annotations> + <stories value="Configurable Product"/> + <title value="Check thumbnail images and active image for Configurable Product with predefined + by query params options"/> + <description value="Login as admin, create attribute with two options, configurable product with two + associated simple products. Add few images for products, check the fotorama thumbnail images + (visible and active) for each option for the configurable product using product URL with params + to selected needed option."/> + <group value="catalog"/> + </annotations> + + <!-- Select first option using product query params URL --> + <amOnPage + url="$$createConfigProduct.sku$$.html#$$createConfigProductAttribute.attribute_id$$=$$getConfigAttributeOption1.value$$" + stepKey="selectFirstOptionValue"/> + <reloadPage stepKey="selectFirstOptionValueRefreshPage" after="selectFirstOptionValue"/> + <waitForPageLoad stepKey="waitForProductWithSelectedFirstOptionToLoad" after="selectFirstOptionValueRefreshPage"/> + + <!-- Select second option using product query params URL --> + <amOnPage + url="$$createConfigProduct.sku$$.html#$$createConfigProductAttribute.attribute_id$$=$$getConfigAttributeOption2.value$$" + stepKey="selectSecondOptionValue"/> + <reloadPage stepKey="selectSecondOptionValueRefreshPage" after="selectSecondOptionValue"/> + <waitForPageLoad stepKey="waitForProductWithSelectedSecondOptionToLoad" after="selectSecondOptionValueRefreshPage"/> + </test> +</tests> diff --git a/app/code/Magento/Catalog/Test/Mftf/Test/StorefrontSpecialPriceForDifferentTimezonesForWebsitesTest.xml b/app/code/Magento/Catalog/Test/Mftf/Test/StorefrontSpecialPriceForDifferentTimezonesForWebsitesTest.xml index 3e887a0a83aa4..0dccc409a1032 100644 --- a/app/code/Magento/Catalog/Test/Mftf/Test/StorefrontSpecialPriceForDifferentTimezonesForWebsitesTest.xml +++ b/app/code/Magento/Catalog/Test/Mftf/Test/StorefrontSpecialPriceForDifferentTimezonesForWebsitesTest.xml @@ -14,13 +14,13 @@ <stories value="Special price"/> <title value="Check that special price displayed when 'default config' scope timezone does not match 'website' scope timezone"/> <description value="Check that special price displayed when 'default config' scope timezone does not match 'website' scope timezone"/> - <severity value="MAJOR"/> + <severity value="BLOCKER"/> <testCaseId value="MAGETWO-97508"/> <useCaseId value="MAGETWO-96847"/> <group value="Catalog"/> </annotations> <before> - <actionGroup ref="LoginAsAdmin" stepKey="login"/> + <actionGroup ref="AdminLoginActionGroup" stepKey="login"/> <!--Create product--> <createData entity="SimpleProduct2" stepKey="createProduct"/> @@ -33,7 +33,7 @@ <deleteData createDataKey="createProduct" stepKey="deleteProduct"/> <deleteData createDataKey="createCustomer" stepKey="deleteCustomer"/> - <actionGroup ref="logout" stepKey="logout"/> + <actionGroup ref="AdminLogoutActionGroup" stepKey="logout"/> </after> <!--Set timezone for default config--> @@ -57,7 +57,9 @@ <click selector="{{AdminMainActionsSection.save}}" stepKey="saveConfig1"/> <!--Set special price to created product--> - <amOnPage url="{{AdminProductEditPage.url($$createProduct.id$$)}}" stepKey="openAdminEditPage"/> + <actionGroup ref="AdminProductPageOpenByIdActionGroup" stepKey="openAdminEditPage"> + <argument name="productId" value="$$createProduct.id$$"/> + </actionGroup> <actionGroup ref="AddSpecialPriceToProductActionGroup" stepKey="setSpecialPriceToCreatedProduct"> <argument name="price" value="15"/> </actionGroup> @@ -72,7 +74,10 @@ <amOnPage url="{{StorefrontProductPage.url($$createProduct.name$$)}}" stepKey="amOnSimpleProductPage"/> <waitForPageLoad stepKey="waitForPageLoad"/> <grabTextFrom selector="{{StorefrontProductInfoMainSection.specialPriceValue}}" stepKey="grabSpecialPrice"/> - <assertEquals expected='$15.00' expectedType="string" actual="$grabSpecialPrice" stepKey="assertSpecialPrice"/> + <assertEquals stepKey="assertSpecialPrice"> + <actualResult type="const">$grabSpecialPrice</actualResult> + <expectedResult type="string">$15.00</expectedResult> + </assertEquals> <!--Reset timezone--> <amOnPage url="{{GeneralConfigurationPage.url}}" stepKey="goToGeneralConfigReset"/> diff --git a/app/code/Magento/Catalog/Test/Mftf/Test/TieredPricingAndQuantityIncrementsWorkWithDecimalinventoryTest.xml b/app/code/Magento/Catalog/Test/Mftf/Test/TieredPricingAndQuantityIncrementsWorkWithDecimalinventoryTest.xml index 0413018128491..26cebae318cd9 100644 --- a/app/code/Magento/Catalog/Test/Mftf/Test/TieredPricingAndQuantityIncrementsWorkWithDecimalinventoryTest.xml +++ b/app/code/Magento/Catalog/Test/Mftf/Test/TieredPricingAndQuantityIncrementsWorkWithDecimalinventoryTest.xml @@ -29,7 +29,7 @@ <deleteData createDataKey="createPreReqSimpleProduct" stepKey="deletePreReqSimpleProduct"/> </after> <!--Step1. Login as admin. Go to Catalog > Products page. Filtering *prod1*. Open *prod1* to edit--> - <actionGroup ref="LoginAsAdmin" stepKey="LoginAsAdmin" /> + <actionGroup ref="AdminLoginActionGroup" stepKey="LoginAsAdmin" /> <actionGroup ref="SearchForProductOnBackendActionGroup" stepKey="filterGroupedProductOptions"> <argument name="product" value="SimpleProduct"/> </actionGroup> diff --git a/app/code/Magento/Catalog/Test/Mftf/Test/VerifyCategoryProductAndProductCategoryPartialReindexTest.xml b/app/code/Magento/Catalog/Test/Mftf/Test/VerifyCategoryProductAndProductCategoryPartialReindexTest.xml index e91f9742b2841..b4514c9b53736 100644 --- a/app/code/Magento/Catalog/Test/Mftf/Test/VerifyCategoryProductAndProductCategoryPartialReindexTest.xml +++ b/app/code/Magento/Catalog/Test/Mftf/Test/VerifyCategoryProductAndProductCategoryPartialReindexTest.xml @@ -14,7 +14,7 @@ <stories value="Product Categories Indexer"/> <title value="Verify Category Product and Product Category partial reindex"/> <description value="Verify that Merchant Developer can use console commands to perform partial reindex for Category Products, Product Categories, and Catalog Search"/> - <severity value="CRITICAL"/> + <severity value="BLOCKER"/> <testCaseId value="MC-11386"/> <group value="catalog"/> <group value="indexer"/> @@ -49,14 +49,15 @@ <!-- Product C in 2 categories M and N --> <createData entity="SimpleProduct2" stepKey="productC"/> - <actionGroup ref="LoginAsAdmin" stepKey="LoginAsAdmin"/> + <actionGroup ref="AdminLoginActionGroup" stepKey="LoginAsAdmin"/> <actionGroup ref="AdminAssignProductToCategoryActionGroup" stepKey="assignCategoryNAndMToProductC"> <argument name="productId" value="$$productC.id$$"/> <argument name="categoryName" value="$$categoryN.name$$, $$categoryM.name$$"/> </actionGroup> - <magentoCLI command="indexer:reindex" stepKey="reindex"/> - <magentoCLI command="cache:flush" arguments="full_page" stepKey="flushCache"/> + <wait stepKey="waitBeforeRunCronIndex" time="30"/> + <magentoCLI stepKey="runCronIndex" command="cron:run --group=index"/> + <wait stepKey="waitAfterRunCronIndex" time="60"/> </before> <after> <!-- Change "Category Products" and "Product Categories" indexers to "Update on Save" mode --> @@ -72,7 +73,7 @@ <deleteData createDataKey="categoryL" stepKey="deleteCategoryL"/> <deleteData createDataKey="categoryK" stepKey="deleteCategoryK"/> - <actionGroup ref="logout" stepKey="logout"/> + <actionGroup ref="AdminLogoutActionGroup" stepKey="logout"/> </after> <!-- Open categories K, L, M, N on Storefront --> @@ -103,7 +104,9 @@ </actionGroup> <!-- Unassign category M from Product B --> - <amOnPage url="{{AdminProductEditPage.url($$productB.id$$)}}" stepKey="amOnEditCategoryPageB"/> + <actionGroup ref="AdminProductPageOpenByIdActionGroup" stepKey="amOnEditCategoryPageB"> + <argument name="productId" value="$$productB.id$$"/> + </actionGroup> <actionGroup ref="AdminUnassignCategoryOnProductAndSaveActionGroup" stepKey="unassignCategoryM"> <argument name="categoryName" value="$$categoryM.name$$"/> </actionGroup> @@ -138,9 +141,10 @@ <amOnPage url="{{StorefrontCategoryPage.url($$categoryK.custom_attributes[url_key]$$/$$categoryN.custom_attributes[url_key]$$)}}" stepKey="amOnCategoryN"/> <see userInput="$$productC.name$$" selector="{{StorefrontCategoryMainSection.productName}}" stepKey="seeProductInCategoryN"/> - <!-- Run cron twice --> - <magentoCLI command="cron:run" stepKey="runCron"/> - <magentoCLI command="cron:run" stepKey="runCronAgain"/> + <!-- Run cron --> + <wait stepKey="waitBeforeRunMagentoCron" time="30"/> + <magentoCLI stepKey="runMagentoCron" command="cron:run --group=index"/> + <wait stepKey="waitAfterRunMagentoCron" time="60"/> <!-- Open categories K, L, M, N on Storefront in order to make sure that new assigments are applied --> <!-- Category K contains only Products A, C --> @@ -164,13 +168,17 @@ <!-- Open categories K, L, N to edit. Assign/unassign Products to/from them. Save changes --> <!-- Remove Product A assignment for category K --> - <amOnPage url="{{AdminProductEditPage.url($$productA.id$$)}}" stepKey="amOnEditProductPageA"/> + <actionGroup ref="AdminProductPageOpenByIdActionGroup" stepKey="amOnEditProductPageA"> + <argument name="productId" value="$$productA.id$$"/> + </actionGroup> <actionGroup ref="AdminUnassignCategoryOnProductAndSaveActionGroup" stepKey="unassignCategoryK"> <argument name="categoryName" value="$$categoryK.name$$"/> </actionGroup> <!-- Remove Product C assignment for category L --> - <amOnPage url="{{AdminProductEditPage.url($$productC.id$$)}}" stepKey="amOnEditProductPageC"/> + <actionGroup ref="AdminProductPageOpenByIdActionGroup" stepKey="amOnEditProductPageC"> + <argument name="productId" value="$$productC.id$$"/> + </actionGroup> <actionGroup ref="AdminUnassignCategoryOnProductAndSaveActionGroup" stepKey="unassignCategoryL"> <argument name="categoryName" value="$$categoryL.name$$"/> </actionGroup> @@ -199,9 +207,10 @@ <amOnPage url="{{StorefrontCategoryPage.url($$categoryK.custom_attributes[url_key]$$/$$categoryN.custom_attributes[url_key]$$)}}" stepKey="onStorefrontCategoryN"/> <see userInput="$$productC.name$$" selector="{{StorefrontCategoryMainSection.productName}}" stepKey="productCOnCategoryN"/> - <!-- Run cron twice --> - <magentoCLI command="cron:run" stepKey="firstCronRun"/> - <magentoCLI command="cron:run" stepKey="secondCronRun"/> + <!-- Run Cron once to reindex product changes --> + <wait stepKey="waitBeforeRunCronIndexAfterProductAssignToCategory" time="30"/> + <magentoCLI stepKey="runCronIndexAfterProductAssignToCategory" command="cron:run --group=index"/> + <wait stepKey="waitAfterRunCronIndexAfterProductAssignToCategory" time="60"/> <!-- Open categories K, L, M, N on Storefront in order to make sure that new assigments are applied --> diff --git a/app/code/Magento/Catalog/Test/Mftf/Test/VerifyChildCategoriesShouldNotIncludeInMenuTest.xml b/app/code/Magento/Catalog/Test/Mftf/Test/VerifyChildCategoriesShouldNotIncludeInMenuTest.xml index d39d54400279c..66d811ced9ac6 100644 --- a/app/code/Magento/Catalog/Test/Mftf/Test/VerifyChildCategoriesShouldNotIncludeInMenuTest.xml +++ b/app/code/Magento/Catalog/Test/Mftf/Test/VerifyChildCategoriesShouldNotIncludeInMenuTest.xml @@ -25,9 +25,9 @@ <actionGroup ref="DeleteCategoryActionGroup" stepKey="deleteCategory"> <argument name="categoryEntity" value="SimpleSubCategory"/> </actionGroup> - <actionGroup ref="logout" stepKey="logout"/> + <actionGroup ref="AdminLogoutActionGroup" stepKey="logout"/> </after> - <actionGroup ref="LoginAsAdmin" stepKey="loginAsAdmin"/> + <actionGroup ref="AdminLoginActionGroup" stepKey="loginAsAdmin"/> <amOnPage url="{{AdminCategoryPage.url}}" stepKey="navigateToCategoryPage1"/> <waitForPageLoad stepKey="waitForPageLoad1"/> <scrollToTopOfPage stepKey="scrollToTopOfPage"/> @@ -40,8 +40,7 @@ <argument name="categoryEntity" value="SubCategoryWithParent"/> </actionGroup> <!--Go to storefront and verify visibility of categories--> - <amOnPage url="{{StorefrontHomePage.url}}" stepKey="goToStorefrontPage"/> - <waitForPageLoad stepKey="waitForPageLoad2"/> + <actionGroup ref="StorefrontOpenHomePageActionGroup" stepKey="goToStorefrontPage"/> <seeElement selector="{{StorefrontHeaderSection.NavigationCategoryByName(SimpleSubCategory.name)}}" stepKey="seeSimpleSubCategoryOnStorefront1"/> <dontSeeElement selector="{{StorefrontHeaderSection.NavigationCategoryByName(SubCategoryWithParent.name)}}" stepKey="dontSeeSubCategoryWithParentOnStorefront1"/> <!--Set Include in menu to No on created category under Default Category --> @@ -53,8 +52,7 @@ <seeCheckboxIsChecked selector="{{AdminCategoryBasicFieldSection.EnableCategory}}" stepKey="seeCheckboxEnableCategoryIsChecked"/> <dontSeeCheckboxIsChecked selector="{{AdminCategoryBasicFieldSection.IncludeInMenu}}" stepKey="dontSeeCheckboxIncludeInMenuIsChecked"/> <!--Go to storefront and verify visibility of categories--> - <amOnPage url="{{StorefrontHomePage.url}}" stepKey="goToStorefrontPage2"/> - <waitForPageLoad stepKey="waitForPageLoad4"/> + <actionGroup ref="StorefrontOpenHomePageActionGroup" stepKey="goToStorefrontPage2"/> <dontSeeElement selector="{{StorefrontHeaderSection.NavigationCategoryByName(SimpleSubCategory.name)}}" stepKey="dontSeeSimpleSubCategoryOnStorefront1"/> <dontSeeElement selector="{{StorefrontHeaderSection.NavigationCategoryByName(SubCategoryWithParent.name)}}" stepKey="dontSeeSubCategoryWithParentOnStorefront2"/> <!--Set Enable category to No and Include in menu to Yes on created category under Default Category --> @@ -67,8 +65,7 @@ <dontSeeCheckboxIsChecked selector="{{AdminCategoryBasicFieldSection.EnableCategory}}" stepKey="dontSeeCheckboxEnableCategoryIsChecked"/> <seeCheckboxIsChecked selector="{{AdminCategoryBasicFieldSection.IncludeInMenu}}" stepKey="seeCheckboxIncludeInMenuIsChecked"/> <!--Go to storefront and verify visibility of categories--> - <amOnPage url="{{StorefrontHomePage.url}}" stepKey="goToStorefrontPage3"/> - <waitForPageLoad stepKey="waitForPageLoad6"/> + <actionGroup ref="StorefrontOpenHomePageActionGroup" stepKey="goToStorefrontPage3"/> <dontSeeElement selector="{{StorefrontHeaderSection.NavigationCategoryByName(SimpleSubCategory.name)}}" stepKey="dontSeeSimpleSubCategoryOnStorefront2"/> <dontSeeElement selector="{{StorefrontHeaderSection.NavigationCategoryByName(SubCategoryWithParent.name)}}" stepKey="dontSeeSubCategoryWithParentOnStorefront3"/> <!--Set Enable category to No and Include in menu to No on created category under Default Category --> @@ -80,8 +77,7 @@ <dontSeeCheckboxIsChecked selector="{{AdminCategoryBasicFieldSection.EnableCategory}}" stepKey="dontSeeCheckboxEnableCategoryIsChecked2"/> <dontSeeCheckboxIsChecked selector="{{AdminCategoryBasicFieldSection.IncludeInMenu}}" stepKey="dontSeeCheckboxIncludeInMenuIsChecked2"/> <!--Go to storefront and verify visibility of categories--> - <amOnPage url="{{StorefrontHomePage.url}}" stepKey="goToStorefrontPage4"/> - <waitForPageLoad stepKey="waitForPageLoad8"/> + <actionGroup ref="StorefrontOpenHomePageActionGroup" stepKey="goToStorefrontPage4"/> <dontSeeElement selector="{{StorefrontHeaderSection.NavigationCategoryByName(SimpleSubCategory.name)}}" stepKey="dontSeeSimpleSubCategoryOnStorefront3"/> <dontSeeElement selector="{{StorefrontHeaderSection.NavigationCategoryByName(SubCategoryWithParent.name)}}" stepKey="dontSeeSubCategoryWithParentOnStorefront4"/> </test> diff --git a/app/code/Magento/Catalog/Test/Mftf/Test/VerifyDefaultWYSIWYGToolbarOnProductTest.xml b/app/code/Magento/Catalog/Test/Mftf/Test/VerifyDefaultWYSIWYGToolbarOnProductTest.xml deleted file mode 100644 index fe0ed32f39e1e..0000000000000 --- a/app/code/Magento/Catalog/Test/Mftf/Test/VerifyDefaultWYSIWYGToolbarOnProductTest.xml +++ /dev/null @@ -1,89 +0,0 @@ -<?xml version="1.0" encoding="UTF-8"?> -<!-- - /** - * Copyright © Magento, Inc. All rights reserved. - * See COPYING.txt for license details. - */ ---> - -<tests xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" - xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/testSchema.xsd"> - <test name="VerifyDefaultWYSIWYGToolbarOnProductTest"> - <annotations> - <features value="Catalog"/> - <stories value="MAGETWO-70412-Default toolbar configuration in Magento"/> - <group value="Catalog"/> - <title value="Admin should be able to see default toolbar display on Description content area"/> - <description value="Admin should be able to see default toolbar display on Description content area"/> - <severity value="CRITICAL"/> - <testCaseId value="MAGETWO-80505"/> - </annotations> - <before> - <actionGroup ref="LoginActionGroup" stepKey="loginGetFromGeneralFile"/> - <actionGroup ref="EnabledWYSIWYGActionGroup" stepKey="enableWYSIWYG"/> - <actionGroup ref="SwitchToVersion4ActionGroup" stepKey="switchToTinyMCE4" /> - </before> - <amOnPage url="{{AdminProductCreatePage.url(AddToDefaultSet.attributeSetId, 'simple')}}" stepKey="navigateToProduct"/> - <waitForPageLoad stepKey="wait"/> - <scrollTo selector="{{AdminProductFormSection.productQuantity}}" stepKey="scrollToQty" /> - <click selector="{{AdminProductFormSection.contentTab}}" stepKey="clickContentTab" /> - <waitForElementVisible selector="{{ProductDescriptionWYSIWYGToolbarSection.TinyMCE4}}" stepKey="waitforTinyMCE4Visible1"/> - <seeElement selector="{{ProductDescriptionWYSIWYGToolbarSection.TinyMCE4}}" stepKey="TinyMCE4Description" /> - <seeElement selector="{{ProductDescriptionWYSIWYGToolbarSection.Style}}" stepKey="assertInfo2"/> - <seeElement selector="{{ProductDescriptionWYSIWYGToolbarSection.Bold}}" stepKey="assertInfo3"/> - <seeElement selector="{{ProductDescriptionWYSIWYGToolbarSection.Italic}}" stepKey="assertInfo4"/> - <seeElement selector="{{ProductDescriptionWYSIWYGToolbarSection.Underline}}" stepKey="assertInfo5"/> - <seeElement selector="{{ProductDescriptionWYSIWYGToolbarSection.AlignLeft}}" stepKey="assertInfo6"/> - <seeElement selector="{{ProductDescriptionWYSIWYGToolbarSection.AlignCenter}}" stepKey="assertInfo7"/> - <seeElement selector="{{ProductDescriptionWYSIWYGToolbarSection.AlignRight}}" stepKey="assertInfo8"/> - <seeElement selector="{{ProductDescriptionWYSIWYGToolbarSection.Numlist}}" stepKey="assertInfo9"/> - <seeElement selector="{{ProductDescriptionWYSIWYGToolbarSection.Bullet}}" stepKey="assertInfo10"/> - <seeElement selector="{{ProductDescriptionWYSIWYGToolbarSection.InsertLink}}" stepKey="assertInfo11"/> - <seeElement selector="{{ProductDescriptionWYSIWYGToolbarSection.InsertImageIcon}}" stepKey="assertInf12"/> - <seeElement selector="{{ProductDescriptionWYSIWYGToolbarSection.InsertTable}}" stepKey="assertInfo13"/> - <seeElement selector="{{ProductDescriptionWYSIWYGToolbarSection.SpecialCharacter}}" stepKey="assertInfo14"/> - <after> - <actionGroup ref="DisabledWYSIWYGActionGroup" stepKey="disableWYSIWYG"/> - <actionGroup ref="logout" stepKey="logout"/> - </after> - </test> - <test name="Verifydefaultcontrolsonproductshortdescription"> - <annotations> - <features value="Catalog"/> - <stories value="Default toolbar configuration in Magento-MAGETWO-70412"/> - <group value="WYSIWYG"/> - <title value="Admin should be able to see default toolbar display on Short Description content area"/> - <description value="Admin should be able to see default toolbar display on Short Description content area"/> - <severity value="CRITICAL"/> - <testCaseId value="MAGETWO-80505"/> - </annotations> - <before> - <actionGroup ref="LoginActionGroup" stepKey="loginGetFromGeneralFile"/> - <actionGroup ref="EnabledWYSIWYGActionGroup" stepKey="enableWYSIWYG"/> - <actionGroup ref="SwitchToVersion4ActionGroup" stepKey="switchToTinyMCE4" /> - </before> - <amOnPage url="{{AdminProductCreatePage.url(AddToDefaultSet.attributeSetId, 'simple')}}" stepKey="navigateToProduct"/> - <waitForPageLoad stepKey="wait"/> - <scrollTo selector="{{AdminProductFormSection.productQuantity}}" stepKey="scrollToQty" /> - <click selector="{{AdminProductFormSection.contentTab}}" stepKey="clickContentTab" /> - <waitForElementVisible selector="{{ProductShortDescriptionWYSIWYGToolbarSection.TinyMCE4}}" stepKey="waitforTinyMCE4Visible2"/> - <seeElement selector="{{ProductShortDescriptionWYSIWYGToolbarSection.TinyMCE4}}" stepKey="TinyMCE4ShortDescription" /> - <seeElement selector="{{ProductShortDescriptionWYSIWYGToolbarSection.Style}}" stepKey="assertInfo16"/> - <seeElement selector="{{ProductShortDescriptionWYSIWYGToolbarSection.Bold}}" stepKey="assertInfo17"/> - <seeElement selector="{{ProductShortDescriptionWYSIWYGToolbarSection.Italic}}" stepKey="assertInfo18"/> - <seeElement selector="{{ProductShortDescriptionWYSIWYGToolbarSection.Underline}}" stepKey="assertInfo19"/> - <seeElement selector="{{ProductShortDescriptionWYSIWYGToolbarSection.AlignLeft}}" stepKey="assertInfo20"/> - <seeElement selector="{{ProductShortDescriptionWYSIWYGToolbarSection.AlignCenter}}" stepKey="assertInfo21"/> - <seeElement selector="{{ProductShortDescriptionWYSIWYGToolbarSection.AlignRight}}" stepKey="assertInfo22"/> - <seeElement selector="{{ProductShortDescriptionWYSIWYGToolbarSection.Numlist}}" stepKey="assertInfo23"/> - <seeElement selector="{{ProductShortDescriptionWYSIWYGToolbarSection.Bullet}}" stepKey="assertInfo324"/> - <seeElement selector="{{ProductShortDescriptionWYSIWYGToolbarSection.InsertLink}}" stepKey="assertInfo25"/> - <seeElement selector="{{ProductShortDescriptionWYSIWYGToolbarSection.InsertImageIcon}}" stepKey="assertInfo26"/> - <seeElement selector="{{ProductShortDescriptionWYSIWYGToolbarSection.InsertTable}}" stepKey="assertInfo27"/> - <seeElement selector="{{ProductShortDescriptionWYSIWYGToolbarSection.SpecialCharacter}}" stepKey="assertInfo28"/> - <after> - <actionGroup ref="DisabledWYSIWYGActionGroup" stepKey="disableWYSIWYG"/> - <actionGroup ref="logout" stepKey="logout"/> - </after> - </test> -</tests> diff --git a/app/code/Magento/Catalog/Test/Mftf/Test/VerifyDefaultWYSIWYGToolbarOnProductTest/VerifyDefaultWYSIWYGToolbarOnProductTest.xml b/app/code/Magento/Catalog/Test/Mftf/Test/VerifyDefaultWYSIWYGToolbarOnProductTest/VerifyDefaultWYSIWYGToolbarOnProductTest.xml new file mode 100644 index 0000000000000..456abecc63ccb --- /dev/null +++ b/app/code/Magento/Catalog/Test/Mftf/Test/VerifyDefaultWYSIWYGToolbarOnProductTest/VerifyDefaultWYSIWYGToolbarOnProductTest.xml @@ -0,0 +1,49 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + /** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +--> +<tests xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/testSchema.xsd"> + <test name="VerifyDefaultWYSIWYGToolbarOnProductTest"> + <annotations> + <features value="Catalog"/> + <stories value="MAGETWO-70412-Default toolbar configuration in Magento"/> + <group value="Catalog"/> + <title value="Admin should be able to see default toolbar display on Description content area"/> + <description value="Admin should be able to see default toolbar display on Description content area"/> + <severity value="CRITICAL"/> + <testCaseId value="MAGETWO-80505"/> + </annotations> + <before> + <actionGroup ref="AdminLoginActionGroup" stepKey="loginGetFromGeneralFile"/> + <actionGroup ref="EnabledWYSIWYGActionGroup" stepKey="enableWYSIWYG"/> + <actionGroup ref="SwitchToVersion4ActionGroup" stepKey="switchToTinyMCE4"/> + </before> + <amOnPage url="{{AdminProductCreatePage.url(AddToDefaultSet.attributeSetId, 'simple')}}" stepKey="navigateToProduct"/> + <waitForPageLoad stepKey="wait"/> + <scrollTo selector="{{AdminProductFormSection.productQuantity}}" stepKey="scrollToQty"/> + <click selector="{{AdminProductFormSection.contentTab}}" stepKey="clickContentTab"/> + <waitForElementVisible selector="{{ProductDescriptionWYSIWYGToolbarSection.TinyMCE4}}" stepKey="waitforTinyMCE4Visible1"/> + <seeElement selector="{{ProductDescriptionWYSIWYGToolbarSection.TinyMCE4}}" stepKey="TinyMCE4Description"/> + <seeElement selector="{{ProductDescriptionWYSIWYGToolbarSection.Style}}" stepKey="assertInfo2"/> + <seeElement selector="{{ProductDescriptionWYSIWYGToolbarSection.Bold}}" stepKey="assertInfo3"/> + <seeElement selector="{{ProductDescriptionWYSIWYGToolbarSection.Italic}}" stepKey="assertInfo4"/> + <seeElement selector="{{ProductDescriptionWYSIWYGToolbarSection.Underline}}" stepKey="assertInfo5"/> + <seeElement selector="{{ProductDescriptionWYSIWYGToolbarSection.AlignLeft}}" stepKey="assertInfo6"/> + <seeElement selector="{{ProductDescriptionWYSIWYGToolbarSection.AlignCenter}}" stepKey="assertInfo7"/> + <seeElement selector="{{ProductDescriptionWYSIWYGToolbarSection.AlignRight}}" stepKey="assertInfo8"/> + <seeElement selector="{{ProductDescriptionWYSIWYGToolbarSection.Numlist}}" stepKey="assertInfo9"/> + <seeElement selector="{{ProductDescriptionWYSIWYGToolbarSection.Bullet}}" stepKey="assertInfo10"/> + <seeElement selector="{{ProductDescriptionWYSIWYGToolbarSection.InsertLink}}" stepKey="assertInfo11"/> + <seeElement selector="{{ProductDescriptionWYSIWYGToolbarSection.InsertImageIcon}}" stepKey="assertInf12"/> + <seeElement selector="{{ProductDescriptionWYSIWYGToolbarSection.InsertTable}}" stepKey="assertInfo13"/> + <seeElement selector="{{ProductDescriptionWYSIWYGToolbarSection.SpecialCharacter}}" stepKey="assertInfo14"/> + <after> + <actionGroup ref="DisabledWYSIWYGActionGroup" stepKey="disableWYSIWYG"/> + <actionGroup ref="AdminLogoutActionGroup" stepKey="logout"/> + </after> + </test> +</tests> diff --git a/app/code/Magento/Catalog/Test/Mftf/Test/VerifyDefaultWYSIWYGToolbarOnProductTest/VerifydefaultcontrolsonproductshortdescriptionTest.xml b/app/code/Magento/Catalog/Test/Mftf/Test/VerifyDefaultWYSIWYGToolbarOnProductTest/VerifydefaultcontrolsonproductshortdescriptionTest.xml new file mode 100644 index 0000000000000..e929cbd752f81 --- /dev/null +++ b/app/code/Magento/Catalog/Test/Mftf/Test/VerifyDefaultWYSIWYGToolbarOnProductTest/VerifydefaultcontrolsonproductshortdescriptionTest.xml @@ -0,0 +1,49 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + /** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +--> +<tests xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/testSchema.xsd"> + <test name="VerifydefaultcontrolsonproductshortdescriptionTest"> + <annotations> + <features value="Catalog"/> + <stories value="Default toolbar configuration in Magento-MAGETWO-70412"/> + <group value="WYSIWYG"/> + <title value="Admin should be able to see default toolbar display on Short Description content area"/> + <description value="Admin should be able to see default toolbar display on Short Description content area"/> + <severity value="CRITICAL"/> + <testCaseId value="MAGETWO-80505"/> + </annotations> + <before> + <actionGroup ref="AdminLoginActionGroup" stepKey="loginGetFromGeneralFile"/> + <actionGroup ref="EnabledWYSIWYGActionGroup" stepKey="enableWYSIWYG"/> + <actionGroup ref="SwitchToVersion4ActionGroup" stepKey="switchToTinyMCE4"/> + </before> + <amOnPage url="{{AdminProductCreatePage.url(AddToDefaultSet.attributeSetId, 'simple')}}" stepKey="navigateToProduct"/> + <waitForPageLoad stepKey="wait"/> + <scrollTo selector="{{AdminProductFormSection.productQuantity}}" stepKey="scrollToQty"/> + <click selector="{{AdminProductFormSection.contentTab}}" stepKey="clickContentTab"/> + <waitForElementVisible selector="{{ProductShortDescriptionWYSIWYGToolbarSection.TinyMCE4}}" stepKey="waitforTinyMCE4Visible2"/> + <seeElement selector="{{ProductShortDescriptionWYSIWYGToolbarSection.TinyMCE4}}" stepKey="TinyMCE4ShortDescription"/> + <seeElement selector="{{ProductShortDescriptionWYSIWYGToolbarSection.Style}}" stepKey="assertInfo16"/> + <seeElement selector="{{ProductShortDescriptionWYSIWYGToolbarSection.Bold}}" stepKey="assertInfo17"/> + <seeElement selector="{{ProductShortDescriptionWYSIWYGToolbarSection.Italic}}" stepKey="assertInfo18"/> + <seeElement selector="{{ProductShortDescriptionWYSIWYGToolbarSection.Underline}}" stepKey="assertInfo19"/> + <seeElement selector="{{ProductShortDescriptionWYSIWYGToolbarSection.AlignLeft}}" stepKey="assertInfo20"/> + <seeElement selector="{{ProductShortDescriptionWYSIWYGToolbarSection.AlignCenter}}" stepKey="assertInfo21"/> + <seeElement selector="{{ProductShortDescriptionWYSIWYGToolbarSection.AlignRight}}" stepKey="assertInfo22"/> + <seeElement selector="{{ProductShortDescriptionWYSIWYGToolbarSection.Numlist}}" stepKey="assertInfo23"/> + <seeElement selector="{{ProductShortDescriptionWYSIWYGToolbarSection.Bullet}}" stepKey="assertInfo324"/> + <seeElement selector="{{ProductShortDescriptionWYSIWYGToolbarSection.InsertLink}}" stepKey="assertInfo25"/> + <seeElement selector="{{ProductShortDescriptionWYSIWYGToolbarSection.InsertImageIcon}}" stepKey="assertInfo26"/> + <seeElement selector="{{ProductShortDescriptionWYSIWYGToolbarSection.InsertTable}}" stepKey="assertInfo27"/> + <seeElement selector="{{ProductShortDescriptionWYSIWYGToolbarSection.SpecialCharacter}}" stepKey="assertInfo28"/> + <after> + <actionGroup ref="DisabledWYSIWYGActionGroup" stepKey="disableWYSIWYG"/> + <actionGroup ref="AdminLogoutActionGroup" stepKey="logout"/> + </after> + </test> +</tests> diff --git a/app/code/Magento/Catalog/Test/Mftf/Test/VerifyTinyMCEv4IsNativeWYSIWYGOnCatalogTest.xml b/app/code/Magento/Catalog/Test/Mftf/Test/VerifyTinyMCEv4IsNativeWYSIWYGOnCatalogTest.xml index cc69d0828015f..b439223674b60 100644 --- a/app/code/Magento/Catalog/Test/Mftf/Test/VerifyTinyMCEv4IsNativeWYSIWYGOnCatalogTest.xml +++ b/app/code/Magento/Catalog/Test/Mftf/Test/VerifyTinyMCEv4IsNativeWYSIWYGOnCatalogTest.xml @@ -19,7 +19,7 @@ <testCaseId value="MAGETWO-82551"/> </annotations> <before> - <actionGroup ref="LoginActionGroup" stepKey="loginGetFromGeneralFile"/> + <actionGroup ref="AdminLoginActionGroup" stepKey="loginGetFromGeneralFile"/> <actionGroup ref="EnabledWYSIWYGActionGroup" stepKey="enableWYSIWYG"/> <actionGroup ref="SwitchToVersion4ActionGroup" stepKey="switchToTinyMCE4" /> </before> @@ -46,7 +46,7 @@ <see userInput="Hello World!" selector="{{StorefrontCategoryMainSection.CatalogDescription}}" stepKey="assertCatalogDescription"/> <after> <actionGroup ref="DisabledWYSIWYGActionGroup" stepKey="disableWYSIWYG"/> - <actionGroup ref="logout" stepKey="logout"/> + <actionGroup ref="AdminLogoutActionGroup" stepKey="logout"/> </after> </test> </tests> diff --git a/app/code/Magento/Catalog/Test/Mftf/Test/VerifyTinyMCEv4IsNativeWYSIWYGOnProductTest.xml b/app/code/Magento/Catalog/Test/Mftf/Test/VerifyTinyMCEv4IsNativeWYSIWYGOnProductTest.xml index 5a2728b6532c8..1e4adedfc168d 100644 --- a/app/code/Magento/Catalog/Test/Mftf/Test/VerifyTinyMCEv4IsNativeWYSIWYGOnProductTest.xml +++ b/app/code/Magento/Catalog/Test/Mftf/Test/VerifyTinyMCEv4IsNativeWYSIWYGOnProductTest.xml @@ -18,7 +18,7 @@ <testCaseId value="MAGETWO-81819"/> </annotations> <before> - <actionGroup ref="LoginActionGroup" stepKey="loginGetFromGeneralFile"/> + <actionGroup ref="AdminLoginActionGroup" stepKey="loginGetFromGeneralFile"/> <actionGroup ref="EnabledWYSIWYGActionGroup" stepKey="enableWYSIWYG"/> <actionGroup ref="SwitchToVersion4ActionGroup" stepKey="switchToTinyMCE4" /> </before> @@ -59,7 +59,7 @@ <see userInput="Hello World! Short Content" selector="{{StorefrontProductInfoMainSection.productShortDescription}}" stepKey="assertProductShortDescription"/> <after> <actionGroup ref="DisabledWYSIWYGActionGroup" stepKey="disableWYSIWYG"/> - <actionGroup ref="logout" stepKey="logout"/> + <actionGroup ref="AdminLogoutActionGroup" stepKey="logout"/> </after> </test> </tests> diff --git a/app/code/Magento/Catalog/Test/Unit/Block/Adminhtml/Product/Helper/Form/WeightTest.php b/app/code/Magento/Catalog/Test/Unit/Block/Adminhtml/Product/Helper/Form/WeightTest.php index ffcb476465dc4..2b11d62961007 100644 --- a/app/code/Magento/Catalog/Test/Unit/Block/Adminhtml/Product/Helper/Form/WeightTest.php +++ b/app/code/Magento/Catalog/Test/Unit/Block/Adminhtml/Product/Helper/Form/WeightTest.php @@ -5,62 +5,72 @@ */ namespace Magento\Catalog\Test\Unit\Block\Adminhtml\Product\Helper\Form; -class WeightTest extends \PHPUnit\Framework\TestCase +use Magento\Catalog\Block\Adminhtml\Product\Helper\Form\Weight; +use Magento\Framework\Data\Form; +use Magento\Framework\Data\Form\Element\CollectionFactory; +use Magento\Framework\Data\Form\Element\Factory; +use Magento\Framework\Data\Form\Element\Radios; +use Magento\Framework\Locale\Format; +use Magento\Framework\TestFramework\Unit\Helper\ObjectManager; +use PHPUnit\Framework\MockObject\MockObject; +use PHPUnit\Framework\TestCase; + +class WeightTest extends TestCase { /** - * @var \Magento\Catalog\Block\Adminhtml\Product\Helper\Form\Weight + * @var Weight */ protected $_model; /** - * @var \PHPUnit_Framework_MockObject_MockObject + * @var Radios|MockObject */ protected $weightSwitcher; /** - * @var \PHPUnit_Framework_MockObject_MockObject + * @var Factory|MockObject */ protected $factory; /** - * @var \PHPUnit_Framework_MockObject_MockObject + * @var CollectionFactory|MockObject */ protected $collectionFactory; /** - * @var \PHPUnit_Framework_MockObject_MockObject + * @var Format|MockObject */ protected $localeFormat; protected function setUp() { $this->weightSwitcher = $this->createPartialMock( - \Magento\Framework\Data\Form\Element\Radios::class, + Radios::class, ['setId', 'setName', 'setLabel', 'setForm'] ); - $this->weightSwitcher->expects($this->any())->method('setId')->will($this->returnSelf()); - $this->weightSwitcher->expects($this->any())->method('setName')->will($this->returnSelf()); - $this->weightSwitcher->expects($this->any())->method('setLabel')->will($this->returnSelf()); + $this->weightSwitcher->method('setId')->will($this->returnSelf()); + $this->weightSwitcher->method('setName')->will($this->returnSelf()); + $this->weightSwitcher->method('setLabel')->will($this->returnSelf()); - $this->factory = $this->createMock(\Magento\Framework\Data\Form\Element\Factory::class); + $this->factory = $this->createMock(Factory::class); $this->factory->expects( $this->once() )->method( 'create' )->with( $this->equalTo('radios') - )->will( - $this->returnValue($this->weightSwitcher) + )->willReturn( + $this->weightSwitcher ); - $this->localeFormat = $this->createMock(\Magento\Framework\Locale\Format::class); + $this->localeFormat = $this->createMock(Format::class); $this->collectionFactory = $this->createPartialMock( - \Magento\Framework\Data\Form\Element\CollectionFactory::class, + CollectionFactory::class, ['create'] ); - $this->_model = (new \Magento\Framework\TestFramework\Unit\Helper\ObjectManager($this))->getObject( - \Magento\Catalog\Block\Adminhtml\Product\Helper\Form\Weight::class, + $this->_model = (new ObjectManager($this))->getObject( + Weight::class, [ 'factoryElement' => $this->factory, 'factoryCollection' => $this->collectionFactory, @@ -71,10 +81,8 @@ protected function setUp() public function testSetForm() { - $form = $this->createMock(\Magento\Framework\Data\Form::class); - $this->weightSwitcher->expects( - $this->any() - )->method( + $form = $this->createMock(Form::class); + $this->weightSwitcher->method( 'setForm' )->with( $this->equalTo($form) @@ -87,9 +95,7 @@ public function testSetForm() public function testGetEscapedValue() { - $this->localeFormat->expects( - $this->any() - )->method( + $this->localeFormat->method( 'getPriceFormat' )->willReturn([ 'precision' => 2, @@ -97,7 +103,7 @@ public function testGetEscapedValue() 'groupSymbol' => '.', ]); - $this->_model->setValue('30000.4'); + $this->_model->setValue(30000.4); $this->_model->setEntityAttribute(true); $return = $this->_model->getEscapedValue('30000.4'); diff --git a/app/code/Magento/Catalog/Test/Unit/Block/Product/ImageFactoryTest.php b/app/code/Magento/Catalog/Test/Unit/Block/Product/ImageFactoryTest.php index 95b06e40602bf..07395f1c81dd3 100644 --- a/app/code/Magento/Catalog/Test/Unit/Block/Product/ImageFactoryTest.php +++ b/app/code/Magento/Catalog/Test/Unit/Block/Product/ImageFactoryTest.php @@ -95,6 +95,7 @@ public function createDataProvider(): array return [ $this->getTestDataWithoutAttributes(), $this->getTestDataWithAttributes(), + $this->getTestDataWithoutDimensions() ]; } @@ -144,7 +145,7 @@ private function getTestDataWithoutAttributes(): array 'height' => 100, 'label' => 'test_image_label', 'ratio' => 1, - 'custom_attributes' => '', + 'custom_attributes' => [], 'product_id' => null, 'class' => 'product-image-photo' ], @@ -202,11 +203,31 @@ private function getTestDataWithAttributes(): array 'height' => 50, 'label' => 'test_product_name', 'ratio' => 0.5, // <== - 'custom_attributes' => 'name_1="value_1" name_2="value_2"', + 'custom_attributes' => [ + 'name_1' => 'value_1', + 'name_2' => 'value_2', + ], 'product_id' => null, 'class' => 'my-class' ], ], ]; } + + /** + * @return array + */ + private function getTestDataWithoutDimensions(): array + { + $data = $this->getTestDataWithoutAttributes(); + + $data['data']['imageParamsBuilder']['image_width'] = null; + $data['data']['imageParamsBuilder']['image_height'] = null; + + $data['expected']['data']['width'] = null; + $data['expected']['data']['height'] = null; + $data['expected']['data']['ratio'] = 1.0; + + return $data; + } } diff --git a/app/code/Magento/Catalog/Test/Unit/Block/Product/ListProductTest.php b/app/code/Magento/Catalog/Test/Unit/Block/Product/ListProductTest.php index fe07f69e8046f..4653934311938 100644 --- a/app/code/Magento/Catalog/Test/Unit/Block/Product/ListProductTest.php +++ b/app/code/Magento/Catalog/Test/Unit/Block/Product/ListProductTest.php @@ -187,10 +187,6 @@ public function testGetIdentities() ->method('getProductCollection') ->will($this->returnValue($this->prodCollectionMock)); - $this->layoutMock->expects($this->once()) - ->method('getBlock') - ->will($this->returnValue($this->toolbarMock)); - $this->assertEquals( [$categoryTag, $productTag], $this->block->getIdentities() diff --git a/app/code/Magento/Catalog/Test/Unit/Block/Product/ProductList/UpsellTest.php b/app/code/Magento/Catalog/Test/Unit/Block/Product/ProductList/UpsellTest.php index 03f3c83d87dcb..f3aa3f182d198 100644 --- a/app/code/Magento/Catalog/Test/Unit/Block/Product/ProductList/UpsellTest.php +++ b/app/code/Magento/Catalog/Test/Unit/Block/Product/ProductList/UpsellTest.php @@ -5,17 +5,21 @@ */ namespace Magento\Catalog\Test\Unit\Block\Product\ProductList; +use Magento\Catalog\Block\Product\ProductList\Upsell as UpsellBlock; +use Magento\Catalog\Model\Product; + class UpsellTest extends \PHPUnit\Framework\TestCase { + const STUB_EMPTY_ARRAY = []; /** - * @var \Magento\Catalog\Block\Product\ProductList\Upsell + * @var UpsellBlock */ protected $block; protected function setUp() { $objectManager = new \Magento\Framework\TestFramework\Unit\Helper\ObjectManager($this); - $this->block = $objectManager->getObject(\Magento\Catalog\Block\Product\ProductList\Upsell::class); + $this->block = $objectManager->getObject(UpsellBlock::class); } protected function tearDown() @@ -26,10 +30,10 @@ protected function tearDown() public function testGetIdentities() { $productTag = ['compare_item_1']; - $product = $this->createMock(\Magento\Catalog\Model\Product::class); + $product = $this->createMock(Product::class); $product->expects($this->once())->method('getIdentities')->will($this->returnValue($productTag)); - $itemsCollection = new \ReflectionProperty(\Magento\Catalog\Block\Product\ProductList\Upsell::class, '_items'); + $itemsCollection = new \ReflectionProperty(UpsellBlock::class, '_items'); $itemsCollection->setAccessible(true); $itemsCollection->setValue($this->block, [$product]); @@ -38,4 +42,32 @@ public function testGetIdentities() $this->block->getIdentities() ); } + + public function testGetIdentitiesWhenItemGetIdentitiesReturnEmptyArray() + { + $product = $this->createMock(Product::class); + $product->expects($this->once())->method('getIdentities') + ->willReturn(self::STUB_EMPTY_ARRAY); + + $itemsCollection = new \ReflectionProperty(UpsellBlock::class, '_items'); + $itemsCollection->setAccessible(true); + $itemsCollection->setValue($this->block, [$product]); + + $this->assertEquals( + self::STUB_EMPTY_ARRAY, + $this->block->getIdentities() + ); + } + + public function testGetIdentitiesWhenGetItemsReturnEmptyArray() + { + $itemsCollection = new \ReflectionProperty(UpsellBlock::class, '_items'); + $itemsCollection->setAccessible(true); + $itemsCollection->setValue($this->block, self::STUB_EMPTY_ARRAY); + + $this->assertEquals( + self::STUB_EMPTY_ARRAY, + $this->block->getIdentities() + ); + } } diff --git a/app/code/Magento/Catalog/Test/Unit/Block/Ui/ProductViewCounterTest.php b/app/code/Magento/Catalog/Test/Unit/Block/Ui/ProductViewCounterTest.php index e7e8ab5ea91a7..85ab52384740d 100644 --- a/app/code/Magento/Catalog/Test/Unit/Block/Ui/ProductViewCounterTest.php +++ b/app/code/Magento/Catalog/Test/Unit/Block/Ui/ProductViewCounterTest.php @@ -6,19 +6,20 @@ namespace Magento\Catalog\Test\Unit\Block\Ui; +use Magento\Catalog\Api\Data\ProductInterface; +use Magento\Catalog\Api\Data\ProductRenderInterface; +use Magento\Catalog\Block\Ui\ProductViewCounter; +use Magento\Catalog\Model\ProductRenderFactory; use Magento\Catalog\Model\ProductRepository; use Magento\Catalog\Ui\DataProvider\Product\ProductRenderCollectorComposite; -use Magento\Catalog\Model\ProductRenderFactory; +use Magento\Framework\App\Config\ScopeConfigInterface; use Magento\Framework\EntityManager\Hydrator; +use Magento\Framework\Registry; use Magento\Framework\Serialize\SerializerInterface; use Magento\Framework\Url; use Magento\Framework\View\Element\Template\Context; -use Magento\Store\Model\StoreManager; use Magento\Store\Model\Store; -use Magento\Framework\Registry; -use Magento\Catalog\Api\Data\ProductInterface; -use Magento\Catalog\Api\Data\ProductRenderInterface; -use Magento\Catalog\Block\Ui\ProductViewCounter; +use Magento\Store\Model\StoreManager; /** * @SuppressWarnings(PHPMD.CouplingBetweenObjects) @@ -70,6 +71,11 @@ class ProductViewCounterTest extends \PHPUnit\Framework\TestCase */ private $storeManagerMock; + /** + * @var ScopeConfigInterface|\PHPUnit_Framework_MockObject_MockObject + */ + private $scopeConfigMock; + /** * @var ProductRenderFactory|\PHPUnit_Framework_MockObject_MockObject */ @@ -104,6 +110,9 @@ protected function setUp() $this->storeManagerMock = $this->getMockBuilder(StoreManager::class) ->disableOriginalConstructor() ->getMock(); + $this->scopeConfigMock = $this->getMockBuilder(ScopeConfigInterface::class) + ->disableOriginalConstructor() + ->getMock(); $this->productViewCounter = new ProductViewCounter( $this->contextMock, @@ -114,7 +123,8 @@ protected function setUp() $this->hydratorMock, $this->serializeMock, $this->urlMock, - $this->registryMock + $this->registryMock, + $this->scopeConfigMock ); } diff --git a/app/code/Magento/Catalog/Test/Unit/Controller/Category/ViewTest.php b/app/code/Magento/Catalog/Test/Unit/Controller/Category/ViewTest.php index b826f25d7c591..595f81cc4ecd3 100644 --- a/app/code/Magento/Catalog/Test/Unit/Controller/Category/ViewTest.php +++ b/app/code/Magento/Catalog/Test/Unit/Controller/Category/ViewTest.php @@ -178,7 +178,13 @@ protected function setUp() ); } - public function testApplyCustomLayoutUpdate() + /** + * Apply custom layout update is correct + * + * @dataProvider getInvocationData + * @return void + */ + public function testApplyCustomLayoutUpdate(array $expectedData): void { $categoryId = 123; $pageLayout = 'page_layout'; @@ -199,11 +205,70 @@ public function testApplyCustomLayoutUpdate() \Magento\Framework\DataObject::class, ['getPageLayout', 'getLayoutUpdates'] ); + $this->category->expects($this->at(1)) + ->method('hasChildren') + ->willReturn(true); + $this->category->expects($this->at(2)) + ->method('hasChildren') + ->willReturn($expectedData[1][0]['type'] === 'default' ? true : false); + $this->category->expects($this->once()) + ->method('getDisplayMode') + ->willReturn($expectedData[2][0]['displaymode']); + $this->expectationForPageLayoutHandles($expectedData); $settings->expects($this->atLeastOnce())->method('getPageLayout')->will($this->returnValue($pageLayout)); $settings->expects($this->once())->method('getLayoutUpdates')->willReturn(['update1', 'update2']); - $this->catalogDesign->expects($this->any())->method('getDesignSettings')->will($this->returnValue($settings)); $this->action->execute(); } + + /** + * Expected invocation for Layout Handles + * + * @param array $data + * @return void + */ + private function expectationForPageLayoutHandles($data): void + { + $index = 1; + + foreach ($data as $expectedData) { + $this->page->expects($this->at($index)) + ->method('addPageLayoutHandles') + ->with($expectedData[0], $expectedData[1], $expectedData[2]); + $index++; + } + } + + /** + * Data provider for execute method. + * + * @return array + */ + public function getInvocationData(): array + { + return [ + [ + 'layoutHandles' => [ + [['type' => 'default'], null, false], + [['type' => 'default_without_children'], null, false], + [['displaymode' => 'products'], null, false] + ] + ], + [ + 'layoutHandles' => [ + [['type' => 'default'], null, false], + [['type' => 'default_without_children'], null, false], + [['displaymode' => 'page'], null, false] + ] + ], + [ + 'layoutHandles' => [ + [['type' => 'default'], null, false], + [['type' => 'default'], null, false], + [['displaymode' => 'poducts_and_page'], null, false] + ] + ] + ]; + } } diff --git a/app/code/Magento/Catalog/Test/Unit/Controller/Product/Compare/IndexTest.php b/app/code/Magento/Catalog/Test/Unit/Controller/Product/Compare/IndexTest.php index bf6f6cff9ad80..a2e784eef9f1b 100644 --- a/app/code/Magento/Catalog/Test/Unit/Controller/Product/Compare/IndexTest.php +++ b/app/code/Magento/Catalog/Test/Unit/Controller/Product/Compare/IndexTest.php @@ -129,7 +129,6 @@ public function testExecute() ->method('getParam') ->willReturnMap( [ - ['items', null, null], ['uenc', null, $beforeUrl], ] ); @@ -141,34 +140,7 @@ public function testExecute() ->method('setBeforeCompareUrl') ->with($beforeUrl . '1') ->willReturnSelf(); - $this->listCompareMock->expects($this->never())->method('addProducts'); $this->redirectFactoryMock->expects($this->never())->method('create'); $this->index->execute(); } - - public function testExecuteWithItems() - { - $this->request->expects($this->any()) - ->method('getParam') - ->willReturnMap( - [ - ['items', null, '1,2,3'], - ['uenc', null, null], - ] - ); - $this->decoderMock->expects($this->never())->method('decode'); - $this->catalogSession->expects($this->never())->method('setBeforeCompareUrl'); - - $this->listCompareMock->expects($this->once()) - ->method('addProducts') - ->with([1, 2, 3]); - $redirect = $this->createPartialMock(\Magento\Framework\Controller\Result\Redirect::class, ['setPath']); - $redirect->expects($this->once()) - ->method('setPath') - ->with('*/*/*'); - $this->redirectFactoryMock->expects($this->once()) - ->method('create') - ->willReturn($redirect); - $this->index->execute(); - } } diff --git a/app/code/Magento/Catalog/Test/Unit/Controller/Product/Frontend/Action/SynchronizeTest.php b/app/code/Magento/Catalog/Test/Unit/Controller/Product/Frontend/Action/SynchronizeTest.php index bae370c7dd79f..4b7053765516d 100644 --- a/app/code/Magento/Catalog/Test/Unit/Controller/Product/Frontend/Action/SynchronizeTest.php +++ b/app/code/Magento/Catalog/Test/Unit/Controller/Product/Frontend/Action/SynchronizeTest.php @@ -133,8 +133,8 @@ public function testExecuteActionException() $jsonObject->expects($this->once()) ->method('setStatusHeader') ->with( - \Zend\Http\Response::STATUS_CODE_400, - \Zend\Http\AbstractMessage::VERSION_11, + \Laminas\Http\Response::STATUS_CODE_400, + \Laminas\Http\AbstractMessage::VERSION_11, 'Bad Request' ); $jsonObject->expects($this->once()) diff --git a/app/code/Magento/Catalog/Test/Unit/CustomerData/CompareProductsTest.php b/app/code/Magento/Catalog/Test/Unit/CustomerData/CompareProductsTest.php index e30ddda0b70b9..6f5d927e333ec 100644 --- a/app/code/Magento/Catalog/Test/Unit/CustomerData/CompareProductsTest.php +++ b/app/code/Magento/Catalog/Test/Unit/CustomerData/CompareProductsTest.php @@ -15,6 +15,7 @@ use Magento\Catalog\Model\Product; use Magento\Catalog\Model\Product\Url; use Magento\Catalog\Model\ResourceModel\Product\Compare\Item\Collection; +use Magento\Framework\App\Config\ScopeConfigInterface; use Magento\Framework\TestFramework\Unit\Helper\ObjectManager as ObjectManagerHelper; class CompareProductsTest extends \PHPUnit\Framework\TestCase @@ -44,6 +45,11 @@ class CompareProductsTest extends \PHPUnit\Framework\TestCase */ private $objectManagerHelper; + /** + * @var ScopeConfigInterface|\PHPUnit_Framework_MockObject_MockObject + */ + private $scopeConfigMock; + /** * @var array */ @@ -65,6 +71,9 @@ protected function setUp() $this->outputHelperMock = $this->getMockBuilder(Output::class) ->disableOriginalConstructor() ->getMock(); + $this->scopeConfigMock = $this->getMockBuilder(ScopeConfigInterface::class) + ->disableOriginalConstructor() + ->getMock(); $this->objectManagerHelper = new ObjectManagerHelper($this); @@ -73,7 +82,8 @@ protected function setUp() [ 'helper' => $this->helperMock, 'productUrl' => $this->productUrlMock, - 'outputHelper' => $this->outputHelperMock + 'outputHelper' => $this->outputHelperMock, + 'scopeConfig' => $this->scopeConfigMock ] ); } @@ -109,6 +119,7 @@ private function prepareProductsWithCorrespondingMocks(array $dataSet) : array $urlMap = []; $outputMap = []; $helperMap = []; + $productScopeMap = []; $count = count($dataSet); @@ -119,6 +130,7 @@ private function prepareProductsWithCorrespondingMocks(array $dataSet) : array $outputMap[] = [$item, $data['name'], 'name', 'productName#' . $data['id']]; $helperMap[] = [$item, 'http://remove.url/' . $data['id']]; $urlMap[] = [$item, [], 'http://product.url/' . $data['id']]; + $productScopeMap[] = [$item, 'store-' . $data['id']]; } $this->productUrlMock->expects($this->exactly($count)) @@ -193,19 +205,22 @@ public function testGetSectionData() 'id' => 1, 'product_url' => 'http://product.url/1', 'name' => 'productName#1', - 'remove_url' => 'http://remove.url/1' + 'remove_url' => 'http://remove.url/1', + 'productScope' => null ], [ 'id' => 2, 'product_url' => 'http://product.url/2', 'name' => 'productName#2', - 'remove_url' => 'http://remove.url/2' + 'remove_url' => 'http://remove.url/2', + 'productScope' => null ], [ 'id' => 3, 'product_url' => 'http://product.url/3', 'name' => 'productName#3', - 'remove_url' => 'http://remove.url/3' + 'remove_url' => 'http://remove.url/3', + 'productScope' => null ] ] ], @@ -276,7 +291,8 @@ public function testGetSectionDataSingleItem() 'id' => 12345, 'product_url' => 'http://product.url/12345', 'name' => 'productName#12345', - 'remove_url' => 'http://remove.url/12345' + 'remove_url' => 'http://remove.url/12345', + 'productScope' => null ] ] ], diff --git a/app/code/Magento/Catalog/Test/Unit/Model/Attribute/Backend/TierPrice/UpdateHandlerTest.php b/app/code/Magento/Catalog/Test/Unit/Model/Attribute/Backend/TierPrice/UpdateHandlerTest.php index fde793d5c5f89..8b398d3ba4d97 100644 --- a/app/code/Magento/Catalog/Test/Unit/Model/Attribute/Backend/TierPrice/UpdateHandlerTest.php +++ b/app/code/Magento/Catalog/Test/Unit/Model/Attribute/Backend/TierPrice/UpdateHandlerTest.php @@ -7,13 +7,13 @@ namespace Magento\Catalog\Test\Unit\Model\Attribute\Backend\TierPrice; -use Magento\Framework\TestFramework\Unit\Helper\ObjectManager; -use Magento\Catalog\Model\Product\Attribute\Backend\TierPrice\UpdateHandler; -use Magento\Store\Model\StoreManagerInterface; use Magento\Catalog\Api\ProductAttributeRepositoryInterface; +use Magento\Catalog\Model\Product\Attribute\Backend\TierPrice\UpdateHandler; +use Magento\Catalog\Model\ResourceModel\Product\Attribute\Backend\Tierprice; use Magento\Customer\Api\GroupManagementInterface; use Magento\Framework\EntityManager\MetadataPool; -use Magento\Catalog\Model\ResourceModel\Product\Attribute\Backend\Tierprice; +use Magento\Framework\TestFramework\Unit\Helper\ObjectManager; +use Magento\Store\Model\StoreManagerInterface; /** * Unit tests for \Magento\Catalog\Model\Product\Attribute\Backend\TierPrice\UpdateHandler @@ -95,20 +95,27 @@ protected function setUp() ); } - public function testExecute(): void - { - $newTierPrices = [ - ['website_id' => 0, 'price_qty' => 2, 'cust_group' => 0, 'price' => 15], - ['website_id' => 0, 'price_qty' => 3, 'cust_group' => 3200, 'price' => null, 'percentage_value' => 20] - ]; - $priceIdToDelete = 2; - $originalTierPrices = [ - ['price_id' => 1, 'website_id' => 0, 'price_qty' => 2, 'cust_group' => 0, 'price' => 10], - ['price_id' => $priceIdToDelete, 'website_id' => 0, 'price_qty' => 4, 'cust_group' => 0, 'price' => 20], - ]; - $linkField = 'entity_id'; - $productId = 10; - $originalProductId = 11; + /** + * Verify update handle. + * + * @param array $newTierPrices + * @param array $originalTierPrices + * @param int $priceIdToDelete + * @param string $linkField + * @param int $productId + * @param int $originalProductId + * @throws \Magento\Framework\Exception\InputException + * + * @dataProvider configDataProvider + */ + public function testExecute( + $newTierPrices, + $originalTierPrices, + $priceIdToDelete, + $linkField, + $productId, + $originalProductId + ): void { /** @var \PHPUnit_Framework_MockObject_MockObject $product */ $product = $this->getMockBuilder(\Magento\Catalog\Api\Data\ProductInterface::class) @@ -128,8 +135,12 @@ public function testExecute(): void ['entity_id', $originalProductId] ] ); + $product->expects($this->atLeastOnce())->method('getStoreId')->willReturn(0); - $product->expects($this->atLeastOnce())->method('setData')->with('tier_price_changed', 1); + + $product->expects($this->atLeastOnce()) + ->method('setData') + ->with('tier_price_changed', 1); $store = $this->getMockBuilder(\Magento\Store\Api\Data\StoreInterface::class) ->disableOriginalConstructor() ->setMethods(['getWebsiteId']) @@ -163,11 +174,12 @@ public function testExecute(): void $this->tierPriceResource->expects($this->exactly(2))->method('savePriceData')->willReturnSelf(); $this->tierPriceResource->expects($this->once())->method('deletePriceData') ->with($productId, null, $priceIdToDelete); - $this->assertEquals($product, $this->updateHandler->execute($product)); } /** + * Verify update handle with exception. + * * @expectedException \Magento\Framework\Exception\InputException * @expectedExceptionMessage Tier prices data should be array, but actually other type is received */ @@ -190,4 +202,88 @@ public function testExecuteWithException(): void $this->updateHandler->execute($product); } + + /** + * Returns test parameters. + * + * @return array + */ + public function configDataProvider() + { + return [ + [ + [ + [ + 'website_id' => 0, + 'price_qty' => 2, + 'cust_group' => 0, + 'price' => 15 + ], + [ + 'website_id' => 0, + 'price_qty' => 3, + 'cust_group' => 3200, + 'price' => null, + 'percentage_value' => 20 + ] + ], + [ + [ + 'price_id' => 1, + 'website_id' => 0, + 'price_qty' => 2, + 'cust_group' => 0, + 'price' => 10], + [ + 'price_id' => 2, + 'website_id' => 0, + 'price_qty' => 4, + 'cust_group' => 0, + 'price' => 20 + ], + ], + 2, + 'entity_id', + 10, + 11 + ], + [ + [ + [ + 'website_id' => 0, + 'price_qty' => 2, + 'cust_group' => 0, + 'price' => 0 + ], + [ + 'website_id' => 0, + 'price_qty' => 3, + 'cust_group' => 3200, + 'price' => null, + 'percentage_value' => 20 + ] + ], + [ + [ + 'price_id' => 1, + 'website_id' => 0, + 'price_qty' => 2, + 'cust_group' => 0, + 'price' => 10 + ], + [ + 'price_id' => 2, + 'website_id' => 0, + 'price_qty' => 4, + 'cust_group' => 0, + 'price' => 20 + ], + ], + 2, + 'entity_id', + 10, + 11 + ] + ]; + } } diff --git a/app/code/Magento/Catalog/Test/Unit/Model/Category/Attribute/Backend/ImageTest.php b/app/code/Magento/Catalog/Test/Unit/Model/Category/Attribute/Backend/ImageTest.php index a76ae5244076f..f8a89f9d9fd90 100644 --- a/app/code/Magento/Catalog/Test/Unit/Model/Category/Attribute/Backend/ImageTest.php +++ b/app/code/Magento/Catalog/Test/Unit/Model/Category/Attribute/Backend/ImageTest.php @@ -3,10 +3,14 @@ * Copyright © Magento, Inc. All rights reserved. * See COPYING.txt for license details. */ + namespace Magento\Catalog\Test\Unit\Model\Category\Attribute\Backend; use Magento\Framework\App\Filesystem\DirectoryList; +use Magento\Framework\Exception\FileSystemException; use Magento\Framework\Filesystem\Directory\WriteInterface; +use Magento\Store\Model\Store; +use Magento\Store\Model\StoreManagerInterface; /** * Test for Magento\Catalog\Model\Category\Attribute\Backend\Image class. @@ -39,6 +43,16 @@ class ImageTest extends \PHPUnit\Framework\TestCase */ private $filesystem; + /** + * @var StoreManagerInterface|\PHPUnit_Framework_MockObject_MockObject; + */ + private $storeManagerInterfaceMock; + + /** + * @var Store|\PHPUnit_Framework_MockObject_MockObject + */ + private $storeMock; + /** * @inheritdoc */ @@ -56,10 +70,6 @@ protected function setUp() ['getName'] ); - $this->attribute->expects($this->once()) - ->method('getName') - ->will($this->returnValue('test_attribute')); - $this->logger = $this->getMockForAbstractClass( \Psr\Log\LoggerInterface::class, [], @@ -75,6 +85,14 @@ protected function setUp() ['moveFileFromTmp', 'getBasePath'] ); + $this->storeManagerInterfaceMock = $this->getMockBuilder( + StoreManagerInterface::class + )->disableOriginalConstructor()->getMock(); + + $this->storeMock = $this->getMockBuilder( + Store::class + )->disableOriginalConstructor()->getMock(); + $this->filesystem = $this->getMockBuilder(\Magento\Framework\Filesystem::class)->disableOriginalConstructor() ->getMock(); } @@ -97,6 +115,10 @@ public function deletedValueDataProvider() */ public function testBeforeSaveValueDeletion($value) { + $this->attribute->expects($this->once()) + ->method('getName') + ->will($this->returnValue('test_attribute')); + $model = $this->objectManager->getObject(\Magento\Catalog\Model\Category\Attribute\Backend\Image::class); $model->setAttribute($this->attribute); @@ -132,6 +154,10 @@ public function invalidValueDataProvider() */ public function testBeforeSaveValueInvalid($value) { + $this->attribute->expects($this->once()) + ->method('getName') + ->will($this->returnValue('test_attribute')); + $model = $this->objectManager->getObject(\Magento\Catalog\Model\Category\Attribute\Backend\Image::class); $model->setAttribute($this->attribute); @@ -147,7 +173,11 @@ public function testBeforeSaveValueInvalid($value) */ public function testBeforeSaveAttributeFileName() { - $model = $this->setUpModelForAfterSave(); + $this->attribute->expects($this->once()) + ->method('getName') + ->will($this->returnValue('test_attribute')); + + $model = $this->setUpModelForTests(); $mediaDirectoryMock = $this->createMock(WriteInterface::class); $this->filesystem->expects($this->once()) ->method('getDirectoryWrite') @@ -177,7 +207,11 @@ public function testBeforeSaveAttributeFileName() */ public function testBeforeSaveAttributeFileNameOutsideOfCategoryDir() { - $model = $this->setUpModelForAfterSave(); + $this->attribute->expects($this->once()) + ->method('getName') + ->will($this->returnValue('test_attribute')); + + $model = $this->setUpModelForTests(); $model->setAttribute($this->attribute); $imagePath = '/pub/media/wysiwyg/test123.jpg'; $this->filesystem @@ -211,7 +245,19 @@ public function testBeforeSaveAttributeFileNameOutsideOfCategoryDir() */ public function testBeforeSaveTemporaryAttribute() { - $model = $this->setUpModelForAfterSave(); + $this->attribute->expects($this->once()) + ->method('getName') + ->will($this->returnValue('test_attribute')); + + $this->storeManagerInterfaceMock->expects($this->once()) + ->method('getStore') + ->willReturn($this->storeMock); + + $this->storeMock->expects($this->once()) + ->method('getBaseMediaDir') + ->willReturn('pub/media'); + + $model = $this->setUpModelForTests(); $model->setAttribute($this->attribute); $mediaDirectoryMock = $this->createMock(WriteInterface::class); @@ -220,10 +266,16 @@ public function testBeforeSaveTemporaryAttribute() ->with(DirectoryList::MEDIA) ->willReturn($mediaDirectoryMock); + $this->imageUploader->expects($this->any())->method('moveFileFromTmp')->willReturn('test123.jpg'); + $object = new \Magento\Framework\DataObject( [ 'test_attribute' => [ - ['name' => 'test123.jpg', 'tmp_name' => 'abc123', 'url' => 'http://www.example.com/test123.jpg'], + [ + 'name' => 'test123.jpg', + 'tmp_name' => 'abc123', + 'url' => 'http://www.example.com/pub/media/temp/test123.jpg' + ], ], ] ); @@ -232,7 +284,7 @@ public function testBeforeSaveTemporaryAttribute() $this->assertEquals( [ - ['name' => 'test123.jpg', 'tmp_name' => 'abc123', 'url' => 'http://www.example.com/test123.jpg'], + ['name' => '/pub/media/test123.jpg', 'tmp_name' => 'abc123', 'url' => '/pub/media/test123.jpg'], ], $object->getData('_additional_data_test_attribute') ); @@ -257,7 +309,7 @@ public function testBeforeSaveAttributeStringValue() /** * @return \Magento\Catalog\Model\Category\Attribute\Backend\Image */ - private function setUpModelForAfterSave() + private function setUpModelForTests() { $objectManagerMock = $this->createPartialMock(\Magento\Framework\App\ObjectManager::class, ['get']); @@ -268,7 +320,7 @@ private function setUpModelForAfterSave() ->will( $this->returnCallback( function ($class, $params = []) use ($imageUploaderMock) { - if ($class == \Magento\Catalog\CategoryImageUpload::class) { + if ($class == "\Magento\Catalog\CategoryImageUpload") { return $imageUploaderMock; } @@ -283,6 +335,7 @@ function ($class, $params = []) use ($imageUploaderMock) { 'objectManager' => $objectManagerMock, 'logger' => $this->logger, 'filesystem' => $this->filesystem, + 'storeManager' => $this->storeManagerInterfaceMock ] ); $this->objectManager->setBackwardCompatibleProperty($model, 'imageUploader', $this->imageUploader); @@ -307,12 +360,13 @@ public function attributeValueDataProvider() * @dataProvider attributeValueDataProvider * * @param array $value + * @throws FileSystemException */ - public function testAfterSaveWithAdditionalData($value) + public function testBeforeSaveWithAdditionalData($value) { - $model = $this->setUpModelForAfterSave(); + $model = $this->setUpModelForTests(); - $this->imageUploader->expects($this->once()) + $this->imageUploader->expects($this->never()) ->method('moveFileFromTmp') ->with($this->equalTo('test1234.jpg')); @@ -323,17 +377,18 @@ public function testAfterSaveWithAdditionalData($value) ] ); - $model->afterSave($object); + $model->beforeSave($object); } /** * @dataProvider attributeValueDataProvider * * @param array $value + * @throws FileSystemException */ - public function testAfterSaveWithoutAdditionalData($value) + public function testBeforeSaveWithoutAdditionalData($value) { - $model = $this->setUpModelForAfterSave(); + $model = $this->setUpModelForTests(); $this->imageUploader->expects($this->never()) ->method('moveFileFromTmp'); @@ -344,15 +399,38 @@ public function testAfterSaveWithoutAdditionalData($value) ] ); - $model->afterSave($object); + $model->beforeSave($object); } /** * Test afterSaveWithExceptions. */ - public function testAfterSaveWithExceptions() + public function testBeforeSaveWithExceptions() { - $model = $this->setUpModelForAfterSave(); + $model = $this->setUpModelForTests(); + + $this->storeManagerInterfaceMock->expects($this->once()) + ->method('getStore') + ->willReturn($this->storeMock); + + $this->storeMock->expects($this->once()) + ->method('getBaseMediaDir') + ->willReturn('pub/media'); + + $this->attribute->expects($this->once()) + ->method('getName') + ->will($this->returnValue('_additional_data_test_attribute')); + + $mediaDirectoryMock = $this->createMock(WriteInterface::class); + $this->filesystem->expects($this->any()) + ->method('getDirectoryWrite') + ->with(DirectoryList::MEDIA) + ->willReturn($mediaDirectoryMock); + $this->imageUploader->expects($this->any())->method('getBasePath')->willReturn('base/path'); + $mediaDirectoryMock->expects($this->any()) + ->method('getAbsolutePath') + ->with('base/path/test1234.jpg') + ->willReturn('absolute/path/base/path/test1234.jpg'); $exception = new \Exception(); @@ -370,6 +448,6 @@ public function testAfterSaveWithExceptions() ] ); - $model->afterSave($object); + $model->beforeSave($object); } } diff --git a/app/code/Magento/Catalog/Test/Unit/Model/Category/DataProviderTest.php b/app/code/Magento/Catalog/Test/Unit/Model/Category/DataProviderTest.php index 4c3450d555f1d..ce131a1953bfd 100644 --- a/app/code/Magento/Catalog/Test/Unit/Model/Category/DataProviderTest.php +++ b/app/code/Magento/Catalog/Test/Unit/Model/Category/DataProviderTest.php @@ -3,87 +3,107 @@ * Copyright © Magento, Inc. All rights reserved. * See COPYING.txt for license details. */ + namespace Magento\Catalog\Test\Unit\Model\Category; +use Magento\Catalog\Model\Category; +use Magento\Catalog\Model\Category\Attribute\Backend\Image; use Magento\Catalog\Model\Category\DataProvider; use Magento\Catalog\Model\Category\FileInfo; +use Magento\Catalog\Model\Category\Image as CategoryImage; use Magento\Catalog\Model\CategoryFactory; use Magento\Catalog\Model\ResourceModel\Category\Collection; use Magento\Catalog\Model\ResourceModel\Category\CollectionFactory; +use Magento\Catalog\Model\ResourceModel\Eav\Attribute; use Magento\Eav\Model\Config; use Magento\Eav\Model\Entity\Type; use Magento\Framework\App\RequestInterface; +use Magento\Framework\AuthorizationInterface; use Magento\Framework\Registry; +use Magento\Framework\Stdlib\ArrayUtils; +use Magento\Framework\TestFramework\Unit\Helper\ObjectManager; +use Magento\Store\Model\Store; use Magento\Store\Model\StoreManagerInterface; use Magento\Ui\DataProvider\EavValidationRules; use Magento\Ui\DataProvider\Modifier\PoolInterface; -use Magento\Framework\Stdlib\ArrayUtils; +use PHPUnit\Framework\MockObject\MockObject; +use PHPUnit\Framework\TestCase; /** * @SuppressWarnings(PHPMD.CouplingBetweenObjects) */ -class DataProviderTest extends \PHPUnit\Framework\TestCase +class DataProviderTest extends TestCase { /** - * @var EavValidationRules|\PHPUnit_Framework_MockObject_MockObject + * @var EavValidationRules|MockObject */ private $eavValidationRules; /** - * @var CollectionFactory|\PHPUnit_Framework_MockObject_MockObject + * @var CollectionFactory|MockObject */ private $categoryCollectionFactory; /** - * @var StoreManagerInterface|\PHPUnit_Framework_MockObject_MockObject + * @var StoreManagerInterface|MockObject */ private $storeManager; /** - * @var Registry|\PHPUnit_Framework_MockObject_MockObject + * @var Registry|MockObject */ private $registry; /** - * @var Config|\PHPUnit_Framework_MockObject_MockObject + * @var Config|MockObject */ private $eavConfig; /** - * @var RequestInterface|\PHPUnit_Framework_MockObject_MockObject + * @var RequestInterface|MockObject */ private $request; /** - * @var CategoryFactory|\PHPUnit_Framework_MockObject_MockObject + * @var CategoryFactory|MockObject */ private $categoryFactory; /** - * @var Collection|\PHPUnit_Framework_MockObject_MockObject + * @var Collection|MockObject */ private $collection; /** - * @var Type|\PHPUnit_Framework_MockObject_MockObject + * @var Type|MockObject */ private $eavEntityMock; /** - * @var FileInfo|\PHPUnit_Framework_MockObject_MockObject + * @var FileInfo|MockObject */ private $fileInfo; /** - * @var PoolInterface|\PHPUnit_Framework_MockObject_MockObject + * @var PoolInterface|MockObject */ private $modifierPool; /** - * @var ArrayUtils|\PHPUnit_Framework_MockObject_MockObject + * @var ArrayUtils|MockObject */ private $arrayUtils; + /** + * @var AuthorizationInterface|MockObject + */ + private $auth; + + /** + * @var CategoryImage|MockObject + */ + private $categoryImage; + /** * @inheritDoc */ @@ -96,8 +116,7 @@ protected function setUp() $this->collection = $this->getMockBuilder(Collection::class) ->disableOriginalConstructor() ->getMock(); - $this->collection->expects($this->any()) - ->method('addAttributeToSelect') + $this->collection->method('addAttributeToSelect') ->with('*') ->willReturnSelf(); @@ -105,8 +124,7 @@ protected function setUp() ->disableOriginalConstructor() ->setMethods(['create']) ->getMock(); - $this->categoryCollectionFactory->expects($this->any()) - ->method('create') + $this->categoryCollectionFactory->method('create') ->willReturn($this->collection); $this->storeManager = $this->getMockBuilder(StoreManagerInterface::class) @@ -138,9 +156,16 @@ protected function setUp() $this->modifierPool = $this->getMockBuilder(PoolInterface::class)->getMockForAbstractClass(); + $this->auth = $this->getMockBuilder(AuthorizationInterface::class)->getMockForAbstractClass(); + $this->arrayUtils = $this->getMockBuilder(ArrayUtils::class) ->setMethods(['flatten']) ->disableOriginalConstructor()->getMock(); + + $this->categoryImage = $this->createPartialMock( + CategoryImage::class, + ['getUrl'] + ); } /** @@ -152,12 +177,11 @@ private function getModel() ->method('getAttributeCollection') ->willReturn([]); - $this->eavConfig->expects($this->any()) - ->method('getEntityType') + $this->eavConfig->method('getEntityType') ->with('catalog_category') ->willReturn($this->eavEntityMock); - $objectManager = new \Magento\Framework\TestFramework\Unit\Helper\ObjectManager($this); + $objectManager = new ObjectManager($this); /** @var DataProvider $model */ $model = $objectManager->getObject( @@ -171,7 +195,9 @@ private function getModel() 'request' => $this->request, 'categoryFactory' => $this->categoryFactory, 'pool' => $this->modifierPool, - 'arrayUtils' => $this->arrayUtils + 'auth' => $this->auth, + 'arrayUtils' => $this->arrayUtils, + 'categoryImage' => $this->categoryImage, ] ); @@ -204,18 +230,17 @@ public function testGetDataNoFileExists() 'image' => $fileName, ]; - $imageBackendMock = $this->getMockBuilder(\Magento\Catalog\Model\Category\Attribute\Backend\Image::class) - ->disableOriginalConstructor() + $imageBackendMock = $this->getMockBuilder(Image::class)->disableOriginalConstructor() ->getMock(); - $attributeMock = $this->getMockBuilder(\Magento\Catalog\Model\ResourceModel\Eav\Attribute::class) + $attributeMock = $this->getMockBuilder(Attribute::class) ->disableOriginalConstructor() ->getMock(); $attributeMock->expects($this->once()) ->method('getBackend') ->willReturn($imageBackendMock); - $categoryMock = $this->getMockBuilder(\Magento\Catalog\Model\Category::class) + $categoryMock = $this->getMockBuilder(Category::class) ->disableOriginalConstructor() ->getMock(); $categoryMock->expects($this->exactly(2)) @@ -226,13 +251,11 @@ public function testGetDataNoFileExists() ['image', null, $categoryData['image']], ] ); - $categoryMock->expects($this->any()) - ->method('getExistsStoreValueFlag') + $categoryMock->method('getExistsStoreValueFlag') ->with('url_key') ->willReturn(false); - $categoryMock->expects($this->any()) - ->method('getStoreId') - ->willReturn(\Magento\Store\Model\Store::DEFAULT_STORE_ID); + $categoryMock->method('getStoreId') + ->willReturn(Store::DEFAULT_STORE_ID); $categoryMock->expects($this->once()) ->method('getId') ->willReturn($categoryId); @@ -253,7 +276,7 @@ public function testGetDataNoFileExists() $model = $this->getModel(); $result = $model->getData(); - $this->assertTrue(is_array($result)); + $this->assertInternalType('array', $result); $this->assertArrayHasKey($categoryId, $result); $this->assertArrayNotHasKey('image', $result[$categoryId]); } @@ -280,18 +303,18 @@ public function testGetData() ], ]; - $imageBackendMock = $this->getMockBuilder(\Magento\Catalog\Model\Category\Attribute\Backend\Image::class) + $imageBackendMock = $this->getMockBuilder(Image::class) ->disableOriginalConstructor() ->getMock(); - $attributeMock = $this->getMockBuilder(\Magento\Catalog\Model\ResourceModel\Eav\Attribute::class) + $attributeMock = $this->getMockBuilder(Attribute::class) ->disableOriginalConstructor() ->getMock(); $attributeMock->expects($this->once()) ->method('getBackend') ->willReturn($imageBackendMock); - $categoryMock = $this->getMockBuilder(\Magento\Catalog\Model\Category::class) + $categoryMock = $this->getMockBuilder(Category::class) ->disableOriginalConstructor() ->getMock(); $categoryMock->expects($this->exactly(2)) @@ -302,21 +325,19 @@ public function testGetData() ['image', null, $categoryData['image']], ] ); - $categoryMock->expects($this->any()) - ->method('getExistsStoreValueFlag') + $categoryMock->method('getExistsStoreValueFlag') ->with('url_key') ->willReturn(false); - $categoryMock->expects($this->any()) - ->method('getStoreId') - ->willReturn(\Magento\Store\Model\Store::DEFAULT_STORE_ID); + $categoryMock->method('getStoreId') + ->willReturn(Store::DEFAULT_STORE_ID); $categoryMock->expects($this->once()) ->method('getId') ->willReturn($categoryId); $categoryMock->expects($this->once()) ->method('getAttributes') ->willReturn(['image' => $attributeMock]); - $categoryMock->expects($this->once()) - ->method('getImageUrl') + $this->categoryImage->expects($this->once()) + ->method('getUrl') ->willReturn($categoryUrl); $this->registry->expects($this->once()) @@ -340,7 +361,7 @@ public function testGetData() $model = $this->getModel(); $result = $model->getData(); - $this->assertTrue(is_array($result)); + $this->assertInternalType('array', $result); $this->assertArrayHasKey($categoryId, $result); $this->assertArrayHasKey('image', $result[$categoryId]); @@ -351,14 +372,14 @@ public function testGetMetaWithoutParentInheritanceResolving() { $this->arrayUtils->expects($this->atLeastOnce())->method('flatten')->willReturn([1,3,3]); - $categoryMock = $this->getMockBuilder(\Magento\Catalog\Model\Category::class) + $categoryMock = $this->getMockBuilder(Category::class) ->disableOriginalConstructor() ->getMock(); $this->registry->expects($this->atLeastOnce()) ->method('registry') ->with('category') ->willReturn($categoryMock); - $attributeMock = $this->getMockBuilder(\Magento\Catalog\Model\ResourceModel\Eav\Attribute::class) + $attributeMock = $this->getMockBuilder(Attribute::class) ->disableOriginalConstructor() ->getMock(); $categoryMock->expects($this->once()) diff --git a/app/code/Magento/Catalog/Test/Unit/Model/Category/ImageTest.php b/app/code/Magento/Catalog/Test/Unit/Model/Category/ImageTest.php new file mode 100644 index 0000000000000..7cf63a56283f2 --- /dev/null +++ b/app/code/Magento/Catalog/Test/Unit/Model/Category/ImageTest.php @@ -0,0 +1,146 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +declare(strict_types=1); + +namespace Magento\Catalog\Test\Unit\Model\Category; + +use Magento\Catalog\Model\Category; +use Magento\Catalog\Model\Category\FileInfo; +use Magento\Catalog\Model\Category\Image; +use Magento\Framework\TestFramework\Unit\Helper\ObjectManager; +use Magento\Framework\UrlInterface; +use Magento\Store\Model\Store; +use Magento\Store\Model\StoreManager; +use PHPUnit\Framework\MockObject\MockObject; +use PHPUnit\Framework\TestCase; + +/** + * Test category image resolver + */ +class ImageTest extends TestCase +{ + /** + * @var Store|MockObject + */ + private $store; + /** + * @var Category + */ + private $category; + /** + * @var Image + */ + private $model; + + /** + * @inheritDoc + */ + protected function setUp() + { + $storeManager = $this->createPartialMock(StoreManager::class, ['getStore']); + $this->store = $this->createPartialMock(Store::class, ['getBaseUrl']); + $storeManager->method('getStore')->willReturn($this->store); + $objectManager = new ObjectManager($this); + $this->category = $objectManager->getObject(Category::class); + $this->model = $objectManager->getObject( + Image::class, + [ + 'storeManager' => $storeManager, + 'fileInfo' => $this->getFileInfo() + ] + ); + } + + /** + * Test that image URL resolver works correctly with different base URL format + * + * @param string $baseUrl + * @param string $imagePath + * @param string $url + * @dataProvider getUrlDataProvider + */ + public function testGetUrl(string $imagePath, string $baseUrl, string $url) + { + $this->store->method('getBaseUrl') + ->with(UrlInterface::URL_TYPE_MEDIA) + ->willReturn($baseUrl); + $this->category->setData('image_attr_code', $imagePath); + $this->assertEquals($url, $this->model->getUrl($this->category, 'image_attr_code')); + } + + /** + * @return array + */ + public function getUrlDataProvider() + { + return [ + [ + 'testimage', + 'http://www.example.com/', + 'http://www.example.com/catalog/category/testimage' + ], + [ + 'testimage', + 'http://www.example.com/pub/media/', + 'http://www.example.com/pub/media/catalog/category/testimage' + ], + [ + 'testimage', + 'http://www.example.com/base/path/pub/media/', + 'http://www.example.com/base/path/pub/media/catalog/category/testimage' + ], + [ + '/pub/media/catalog/category/testimage', + 'http://www.example.com/pub/media/', + 'http://www.example.com/pub/media/catalog/category/testimage' + ], + [ + '/pub/media/catalog/category/testimage', + 'http://www.example.com/base/path/pub/media/', + 'http://www.example.com/base/path/pub/media/catalog/category/testimage' + ], + [ + '/pub/media/posters/testimage', + 'http://www.example.com/pub/media/', + 'http://www.example.com/pub/media/posters/testimage' + ], + [ + '/pub/media/posters/testimage', + 'http://www.example.com/base/path/pub/media/', + 'http://www.example.com/base/path/pub/media/posters/testimage' + ], + [ + '', + 'http://www.example.com/', + '' + ] + ]; + } + + /** + * Get FileInfo mock + * + * @return MockObject + */ + private function getFileInfo(): MockObject + { + $mediaDir = 'pub/media'; + $fileInfo = $this->createMock(FileInfo::class); + $fileInfo->method('isBeginsWithMediaDirectoryPath') + ->willReturnCallback( + function ($path) use ($mediaDir) { + return strpos(ltrim($path, '/'), $mediaDir) === 0; + } + ); + $fileInfo->method('getRelativePathToMediaDirectory') + ->willReturnCallback( + function ($path) use ($mediaDir) { + return str_replace($mediaDir, '', $path); + } + ); + return $fileInfo; + } +} diff --git a/app/code/Magento/Catalog/Test/Unit/Model/CategoryLinkRepositoryTest.php b/app/code/Magento/Catalog/Test/Unit/Model/CategoryLinkRepositoryTest.php index b42262f1f0384..909b952078b58 100644 --- a/app/code/Magento/Catalog/Test/Unit/Model/CategoryLinkRepositoryTest.php +++ b/app/code/Magento/Catalog/Test/Unit/Model/CategoryLinkRepositoryTest.php @@ -6,40 +6,74 @@ namespace Magento\Catalog\Test\Unit\Model; +use Magento\Catalog\Api\CategoryRepositoryInterface; +use Magento\Catalog\Api\Data\CategoryProductLinkInterface; +use Magento\Catalog\Api\ProductRepositoryInterface; +use Magento\Catalog\Model\Category; +use Magento\Catalog\Model\CategoryLinkRepository; +use Magento\Catalog\Model\Product as ProductModel; +use Magento\Catalog\Model\ResourceModel\Product; +use Magento\Framework\Exception\CouldNotSaveException; +use Magento\Framework\Exception\InputException; +use PHPUnit_Framework_MockObject_MockObject as MockObject; + +/** + * Test for \Magento\Catalog\Model\CategoryLinkRepository + * @SuppressWarnings(PHPMD.TooManyFields) + * @SuppressWarnings(PHPMD.CouplingBetweenObjects) + */ class CategoryLinkRepositoryTest extends \PHPUnit\Framework\TestCase { /** - * @var \Magento\Catalog\Model\CategoryLinkRepository + * @var CategoryLinkRepository */ - protected $model; + private $model; /** - * @var \PHPUnit_Framework_MockObject_MockObject + * @var CategoryRepositoryInterface|MockObject */ - protected $categoryRepositoryMock; + private $categoryRepositoryMock; /** - * @var \PHPUnit_Framework_MockObject_MockObject + * @var ProductRepositoryInterface|MockObject */ - protected $productRepositoryMock; + private $productRepositoryMock; /** - * @var \PHPUnit_Framework_MockObject_MockObject + * @var CategoryProductLinkInterface|MockObject */ - protected $productLinkMock; + private $productLinkMock; + /** + * @var Product|MockObject + */ + private $productResourceMock; + + /** + * Initialize required data + */ protected function setUp() { - $this->categoryRepositoryMock = $this->createMock(\Magento\Catalog\Api\CategoryRepositoryInterface::class); - $this->productRepositoryMock = $this->createMock(\Magento\Catalog\Api\ProductRepositoryInterface::class); - $this->productLinkMock = $this->createMock(\Magento\Catalog\Api\Data\CategoryProductLinkInterface::class); - $this->model = new \Magento\Catalog\Model\CategoryLinkRepository( + $this->productResourceMock = $this->getMockBuilder(Product::class) + ->disableOriginalConstructor() + ->setMethods(['getProductsIdsBySkus']) + ->getMock(); + $this->categoryRepositoryMock = $this->createMock(CategoryRepositoryInterface::class); + $this->productRepositoryMock = $this->createMock(ProductRepositoryInterface::class); + $this->productLinkMock = $this->createMock(CategoryProductLinkInterface::class); + $this->model = new CategoryLinkRepository( $this->categoryRepositoryMock, - $this->productRepositoryMock + $this->productRepositoryMock, + $this->productResourceMock ); } - public function testSave() + /** + * Assign a product to the category + * + * @return void + */ + public function testSave(): void { $categoryId = 42; $productId = 55; @@ -47,10 +81,10 @@ public function testSave() $sku = 'testSku'; $productPositions = [$productId => $productPosition]; $categoryMock = $this->createPartialMock( - \Magento\Catalog\Model\Category::class, + Category::class, ['getPostedProducts', 'getProductsPosition', 'setPostedProducts', 'save'] ); - $productMock = $this->createMock(\Magento\Catalog\Model\Product::class); + $productMock = $this->createMock(ProductModel::class); $this->productLinkMock->expects($this->once())->method('getCategoryId')->willReturn($categoryId); $this->productLinkMock->expects($this->once())->method('getSku')->willReturn($sku); $this->categoryRepositoryMock->expects($this->once())->method('get')->with($categoryId) @@ -61,14 +95,16 @@ public function testSave() $this->productLinkMock->expects($this->once())->method('getPosition')->willReturn($productPosition); $categoryMock->expects($this->once())->method('setPostedProducts')->with($productPositions); $categoryMock->expects($this->once())->method('save'); + $this->assertTrue($this->model->save($this->productLinkMock)); } /** - * @expectedException \Magento\Framework\Exception\CouldNotSaveException - * @expectedExceptionMessage Could not save product "55" with position 1 to category 42 + * Assign a product to the category with `CouldNotSaveException` + * + * @return void */ - public function testSaveWithCouldNotSaveException() + public function testSaveWithCouldNotSaveException(): void { $categoryId = 42; $productId = 55; @@ -76,10 +112,10 @@ public function testSaveWithCouldNotSaveException() $sku = 'testSku'; $productPositions = [$productId => $productPosition]; $categoryMock = $this->createPartialMock( - \Magento\Catalog\Model\Category::class, + Category::class, ['getProductsPosition', 'setPostedProducts', 'save', 'getId'] ); - $productMock = $this->createMock(\Magento\Catalog\Model\Product::class); + $productMock = $this->createMock(ProductModel::class); $this->productLinkMock->expects($this->once())->method('getCategoryId')->willReturn($categoryId); $this->productLinkMock->expects($this->once())->method('getSku')->willReturn($sku); $this->categoryRepositoryMock->expects($this->once())->method('get')->with($categoryId) @@ -91,20 +127,28 @@ public function testSaveWithCouldNotSaveException() $categoryMock->expects($this->once())->method('setPostedProducts')->with($productPositions); $categoryMock->expects($this->once())->method('getId')->willReturn($categoryId); $categoryMock->expects($this->once())->method('save')->willThrowException(new \Exception()); + + $this->expectExceptionMessage('Could not save product "55" with position 1 to category 42'); + $this->expectException(CouldNotSaveException::class); $this->model->save($this->productLinkMock); } - public function testDeleteByIds() + /** + * Remove the product assignment from the category + * + * @return void + */ + public function testDeleteByIds(): void { - $categoryId = "42"; - $productSku = "testSku"; + $categoryId = 42; + $productSku = 'testSku'; $productId = 55; $productPositions = [55 => 1]; $categoryMock = $this->createPartialMock( - \Magento\Catalog\Model\Category::class, + Category::class, ['getProductsPosition', 'setPostedProducts', 'save', 'getId'] ); - $productMock = $this->createMock(\Magento\Catalog\Model\Product::class); + $productMock = $this->createMock(ProductModel::class); $this->categoryRepositoryMock->expects($this->once())->method('get')->with($categoryId) ->willReturn($categoryMock); $this->productRepositoryMock->expects($this->once())->method('get')->with($productSku) @@ -113,24 +157,26 @@ public function testDeleteByIds() $productMock->expects($this->once())->method('getId')->willReturn($productId); $categoryMock->expects($this->once())->method('setPostedProducts')->with([]); $categoryMock->expects($this->once())->method('save'); + $this->assertTrue($this->model->deleteByIds($categoryId, $productSku)); } /** - * @expectedException \Magento\Framework\Exception\CouldNotSaveException - * @expectedExceptionMessage Could not save product "55" with position 1 to category 42 + * Delete the product assignment from the category with `CouldNotSaveException` + * + * @return void */ - public function testDeleteByIdsWithCouldNotSaveException() + public function testDeleteByIdsWithCouldNotSaveException(): void { - $categoryId = "42"; - $productSku = "testSku"; + $categoryId = 42; + $productSku = 'testSku'; $productId = 55; $productPositions = [55 => 1]; $categoryMock = $this->createPartialMock( - \Magento\Catalog\Model\Category::class, + Category::class, ['getProductsPosition', 'setPostedProducts', 'save', 'getId'] ); - $productMock = $this->createMock(\Magento\Catalog\Model\Product::class); + $productMock = $this->createMock(ProductModel::class); $this->categoryRepositoryMock->expects($this->once())->method('get')->with($categoryId) ->willReturn($categoryMock); $this->productRepositoryMock->expects($this->once())->method('get')->with($productSku) @@ -140,50 +186,61 @@ public function testDeleteByIdsWithCouldNotSaveException() $categoryMock->expects($this->once())->method('setPostedProducts')->with([]); $categoryMock->expects($this->once())->method('getId')->willReturn($categoryId); $categoryMock->expects($this->once())->method('save')->willThrowException(new \Exception()); + + $this->expectExceptionMessage('Could not save product "55" with position 1 to category 42'); + $this->expectException(CouldNotSaveException::class); $this->model->deleteByIds($categoryId, $productSku); } /** - * @expectedException \Magento\Framework\Exception\InputException - * @expectedExceptionMessage The category doesn't contain the specified product. + * Delete the product assignment from the category with `InputException` + * + * @return void */ - public function testDeleteWithInputException() + public function testDeleteWithInputException(): void { - $categoryId = "42"; - $productSku = "testSku"; + $categoryId = 42; + $productSku = 'testSku'; $productId = 60; $productPositions = [55 => 1]; $this->productLinkMock->expects($this->once())->method('getCategoryId')->willReturn($categoryId); $this->productLinkMock->expects($this->once())->method('getSku')->willReturn($productSku); $categoryMock = $this->createPartialMock( - \Magento\Catalog\Model\Category::class, + Category::class, ['getProductsPosition', 'setPostedProducts', 'save', 'getId'] ); - $productMock = $this->createMock(\Magento\Catalog\Model\Product::class); + $productMock = $this->createMock(ProductModel::class); $this->categoryRepositoryMock->expects($this->once())->method('get')->with($categoryId) ->willReturn($categoryMock); $this->productRepositoryMock->expects($this->once())->method('get')->with($productSku) ->willReturn($productMock); $categoryMock->expects($this->once())->method('getProductsPosition')->willReturn($productPositions); $productMock->expects($this->once())->method('getId')->willReturn($productId); - $categoryMock->expects($this->never())->method('save'); + + $this->expectExceptionMessage('The category doesn\'t contain the specified product.'); + $this->expectException(InputException::class); $this->assertTrue($this->model->delete($this->productLinkMock)); } - public function testDelete() + /** + * Delete the product assignment from the category + * + * @return void + */ + public function testDelete(): void { - $categoryId = "42"; - $productSku = "testSku"; + $categoryId = 42; + $productSku = 'testSku'; $productId = 55; $productPositions = [55 => 1]; $this->productLinkMock->expects($this->once())->method('getCategoryId')->willReturn($categoryId); $this->productLinkMock->expects($this->once())->method('getSku')->willReturn($productSku); $categoryMock = $this->createPartialMock( - \Magento\Catalog\Model\Category::class, + Category::class, ['getProductsPosition', 'setPostedProducts', 'save', 'getId'] ); - $productMock = $this->createMock(\Magento\Catalog\Model\Product::class); + $productMock = $this->createMock(ProductModel::class); $this->categoryRepositoryMock->expects($this->once())->method('get')->with($categoryId) ->willReturn($categoryMock); $this->productRepositoryMock->expects($this->once())->method('get')->with($productSku) @@ -192,6 +249,82 @@ public function testDelete() $productMock->expects($this->once())->method('getId')->willReturn($productId); $categoryMock->expects($this->once())->method('setPostedProducts')->with([]); $categoryMock->expects($this->once())->method('save'); + $this->assertTrue($this->model->delete($this->productLinkMock)); } + + /** + * Delete by products skus + * + * @return void + */ + public function testDeleteBySkus(): void + { + $categoryId = 42; + $productSkus = ['testSku', 'testSku1', 'testSku2', 'testSku3']; + $productPositions = [55 => 1, 56 => 2, 57 => 3, 58 => 4]; + $categoryMock = $this->createPartialMock( + Category::class, + ['getProductsPosition', 'setPostedProducts', 'save', 'getId'] + ); + $this->categoryRepositoryMock->expects($this->once())->method('get')->with($categoryId) + ->willReturn($categoryMock); + $this->productResourceMock->expects($this->once())->method('getProductsIdsBySkus') + ->willReturn(['testSku' => 55, 'testSku1' => 56, 'testSku2' => 57, 'testSku3' => 58]); + $categoryMock->expects($this->once())->method('getProductsPosition')->willReturn($productPositions); + $categoryMock->expects($this->once())->method('setPostedProducts')->with([]); + $categoryMock->expects($this->once())->method('save'); + + $this->assertTrue($this->model->deleteBySkus($categoryId, $productSkus)); + } + + /** + * Delete by products skus with `InputException` + * + * @return void + */ + public function testDeleteBySkusWithInputException(): void + { + $categoryId = 42; + $productSku = 'testSku'; + $categoryMock = $this->createPartialMock( + Category::class, + ['getProductsPosition', 'setPostedProducts', 'save', 'getId'] + ); + $this->categoryRepositoryMock->expects($this->once())->method('get')->with($categoryId) + ->willReturn($categoryMock); + + $this->expectExceptionMessage('The category doesn\'t contain the specified products.'); + $this->expectException(InputException::class); + $this->model->deleteBySkus($categoryId, [$productSku]); + } + + /** + * Delete by products skus with `CouldNotSaveException` + * + * @return void + */ + public function testDeleteSkusIdsWithCouldNotSaveException(): void + { + $categoryId = 42; + $productSku = 'testSku'; + $productId = 55; + $productPositions = [55 => 1]; + $categoryMock = $this->createPartialMock( + Category::class, + ['getProductsPosition', 'setPostedProducts', 'save', 'getId'] + ); + $this->categoryRepositoryMock->expects($this->once())->method('get')->with($categoryId) + ->willReturn($categoryMock); + $this->productResourceMock->expects($this->once())->method('getProductsIdsBySkus') + ->willReturn(['testSku' => $productId]); + $categoryMock->expects($this->once())->method('getProductsPosition')->willReturn($productPositions); + $categoryMock->expects($this->once())->method('setPostedProducts')->with([]); + $categoryMock->expects($this->once())->method('getId')->willReturn($categoryId); + $categoryMock->expects($this->once())->method('save')->willThrowException(new \Exception()); + + $this->expectExceptionMessage('Could not save products "testSku" to category 42'); + $this->expectException(CouldNotSaveException::class); + $this->assertTrue($this->model->deleteBySkus($categoryId, [$productSku])); + } } diff --git a/app/code/Magento/Catalog/Test/Unit/Model/CategoryRepositoryTest.php b/app/code/Magento/Catalog/Test/Unit/Model/CategoryRepositoryTest.php index 864b91b20d017..3799e6e5fa4aa 100644 --- a/app/code/Magento/Catalog/Test/Unit/Model/CategoryRepositoryTest.php +++ b/app/code/Magento/Catalog/Test/Unit/Model/CategoryRepositoryTest.php @@ -204,7 +204,7 @@ public function testCreateNewCategory() $parentCategoryId = 15; $newCategoryId = 25; $categoryData = ['level' => '1', 'path' => '1/2', 'parent_id' => 1, 'name' => 'category']; - $dataForSave = ['store_id' => 1, 'name' => 'category', 'path' => 'path', 'parent_id' => 15]; + $dataForSave = ['store_id' => 1, 'name' => 'category', 'path' => 'path', 'parent_id' => 15, 'level' => null]; $this->extensibleDataObjectConverterMock ->expects($this->once()) ->method('toNestedArray') diff --git a/app/code/Magento/Catalog/Test/Unit/Model/Indexer/Product/Flat/Action/RowTest.php b/app/code/Magento/Catalog/Test/Unit/Model/Indexer/Product/Flat/Action/RowTest.php index 11d07872fef91..61fad897c6418 100644 --- a/app/code/Magento/Catalog/Test/Unit/Model/Indexer/Product/Flat/Action/RowTest.php +++ b/app/code/Magento/Catalog/Test/Unit/Model/Indexer/Product/Flat/Action/RowTest.php @@ -160,8 +160,24 @@ public function testExecuteWithExistingFlatTablesCreatesTables() ->willReturn('store_flat_table'); $this->connection->expects($this->any())->method('isTableExists')->with('store_flat_table') ->willReturn(true); + $this->connection->expects($this->any())->method('fetchCol') + ->willReturn(['store_id_1']); $this->flatItemEraser->expects($this->once())->method('removeDeletedProducts'); $this->flatTableBuilder->expects($this->never())->method('build')->with('store_id_1', ['product_id_1']); $this->model->execute('product_id_1'); } + + public function testExecuteWithExistingFlatTablesRemoveProductFromStore() + { + $this->productIndexerHelper->expects($this->any())->method('getFlatTableName') + ->willReturn('store_flat_table'); + $this->connection->expects($this->any())->method('isTableExists')->with('store_flat_table') + ->willReturn(true); + $this->connection->expects($this->any())->method('fetchCol') + ->willReturn([1]); + $this->flatItemEraser->expects($this->once())->method('deleteProductsFromStore'); + $this->flatItemEraser->expects($this->never())->method('removeDeletedProducts'); + $this->flatTableBuilder->expects($this->never())->method('build')->with('store_id_1', ['product_id_1']); + $this->model->execute('product_id_1'); + } } diff --git a/app/code/Magento/Catalog/Test/Unit/Model/Indexer/Product/Price/Plugin/CustomerGroupTest.php b/app/code/Magento/Catalog/Test/Unit/Model/Indexer/Product/Price/Plugin/CustomerGroupTest.php new file mode 100644 index 0000000000000..8e12ba7670638 --- /dev/null +++ b/app/code/Magento/Catalog/Test/Unit/Model/Indexer/Product/Price/Plugin/CustomerGroupTest.php @@ -0,0 +1,132 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +namespace Magento\Catalog\Test\Unit\Model\Indexer\Product\Price\Plugin; + +use Magento\Catalog\Model\Indexer\Product\Price\DimensionModeConfiguration; +use Magento\Catalog\Model\Indexer\Product\Price\Plugin\CustomerGroup; +use Magento\Catalog\Model\Indexer\Product\Price\TableMaintainer; +use Magento\Customer\Api\GroupRepositoryInterface; +use Magento\Customer\Model\Data\Group; +use Magento\Customer\Model\Indexer\CustomerGroupDimensionProvider; +use Magento\Framework\Indexer\DimensionFactory; +use Magento\Framework\TestFramework\Unit\Helper\ObjectManager; +use PHPUnit\Framework\MockObject\MockObject; + +/** + * Test for CustomerGroup plugin + */ +class CustomerGroupTest extends \PHPUnit\Framework\TestCase +{ + /** + * @var ObjectManager + */ + private $objectManager; + + /** + * @var CustomerGroup + */ + private $model; + + /** + * @var DimensionFactory|MockObject + */ + private $dimensionFactory; + + /** + * @var TableMaintainer|MockObject + */ + private $tableMaintainerMock; + + /** + * @var DimensionModeConfiguration|MockObject + */ + private $dimensionModeConfiguration; + + /** + * @var \Callable + */ + private $proceedMock; + + protected function setUp() + { + $this->objectManager = new ObjectManager($this); + + $this->dimensionFactory = $this->createPartialMock( + DimensionFactory::class, + ['create'] + ); + + $this->dimensionModeConfiguration = $this->createPartialMock( + DimensionModeConfiguration::class, + ['getDimensionConfiguration'] + ); + + $this->tableMaintainerMock = $this->createPartialMock( + TableMaintainer::class, + ['createTablesForDimensions'] + ); + + $this->model = $this->objectManager->getObject( + CustomerGroup::class, + [ + 'dimensionFactory' => $this->dimensionFactory, + 'dimensionModeConfiguration' => $this->dimensionModeConfiguration, + 'tableMaintainer' => $this->tableMaintainerMock, + ] + ); + } + + /** + * Check of call count createTablesForDimensions() method + * + * @param $customerGroupId + * @param $callTimes + * + * @dataProvider aroundSaveDataProvider + */ + public function testAroundSave($customerGroupId, $callTimes) + { + $subjectMock = $this->createMock(GroupRepositoryInterface::class); + $customerGroupMock = $this->createPartialMock( + Group::class, + ['getId'] + ); + $customerGroupMock->method('getId')->willReturn($customerGroupId); + $this->tableMaintainerMock->expects( + $this->exactly($callTimes) + )->method('createTablesForDimensions'); + $this->proceedMock = function ($customerGroupMock) { + return $customerGroupMock; + }; + $this->dimensionModeConfiguration->method('getDimensionConfiguration')->willReturn( + [CustomerGroupDimensionProvider::DIMENSION_NAME] + ); + $this->model->aroundSave($subjectMock, $this->proceedMock, $customerGroupMock); + } + + /** + * Data provider for testAroundSave + * + * @return array + */ + public function aroundSaveDataProvider() + { + return [ + 'customer_group_id = 0' => [ + 'customer_group_id' => '0', + 'create_tables_call_times' => 0 + ], + 'customer_group_id = 1' => [ + 'customer_group_id' => '1', + 'create_tables_call_times' => 0 + ], + 'customer_group_id = null' => [ + 'customer_group_id' => null, + 'create_tables_call_times' => 1 + ], + ]; + } +} diff --git a/app/code/Magento/Catalog/Test/Unit/Model/Layer/FilterListTest.php b/app/code/Magento/Catalog/Test/Unit/Model/Layer/FilterListTest.php index 731c5efd99746..8fdd41281d807 100644 --- a/app/code/Magento/Catalog/Test/Unit/Model/Layer/FilterListTest.php +++ b/app/code/Magento/Catalog/Test/Unit/Model/Layer/FilterListTest.php @@ -7,8 +7,15 @@ namespace Magento\Catalog\Test\Unit\Model\Layer; +use Magento\Catalog\Model\Config\LayerCategoryConfig; use \Magento\Catalog\Model\Layer\FilterList; +use PHPUnit\Framework\MockObject\MockObject; +/** + * Filter List Test + * + * Check whenever the given filters list matches the expected result + */ class FilterListTest extends \PHPUnit\Framework\TestCase { /** @@ -36,6 +43,14 @@ class FilterListTest extends \PHPUnit\Framework\TestCase */ protected $model; + /** + * @var LayerCategoryConfig|MockObject + */ + private $layerCategoryConfigMock; + + /** + * Set Up + */ protected function setUp() { $this->objectManagerMock = $this->createMock(\Magento\Framework\ObjectManagerInterface::class); @@ -51,8 +66,14 @@ protected function setUp() ]; $this->layerMock = $this->createMock(\Magento\Catalog\Model\Layer::class); + $this->layerCategoryConfigMock = $this->createMock(LayerCategoryConfig::class); - $this->model = new FilterList($this->objectManagerMock, $this->attributeListMock, $filters); + $this->model = new FilterList( + $this->objectManagerMock, + $this->attributeListMock, + $this->layerCategoryConfigMock, + $filters + ); } /** @@ -72,6 +93,46 @@ public function testGetFilters($method, $value, $expectedClass) ->will($this->returnValue('filter')); $this->objectManagerMock->expects($this->at(1)) + ->method('create') + ->with($expectedClass, [ + 'data' => ['attribute_model' => $this->attributeMock], + 'layer' => $this->layerMock]) + ->will($this->returnValue('filter')); + + $this->attributeMock->expects($this->once()) + ->method($method) + ->will($this->returnValue($value)); + + $this->attributeListMock->expects($this->once()) + ->method('getList') + ->will($this->returnValue([$this->attributeMock])); + + $this->layerCategoryConfigMock->expects($this->once()) + ->method('isCategoryFilterVisibleInLayerNavigation') + ->willReturn(true); + + $this->assertEquals(['filter', 'filter'], $this->model->getFilters($this->layerMock)); + } + + /** + * Test filters list result when category should not be included + * + * @param string $method + * @param string $value + * @param string $expectedClass + * @param array $expectedResult + * + * @dataProvider getFiltersWithoutCategoryDataProvider + * + * @return void + */ + public function testGetFiltersWithoutCategoryFilter( + string $method, + string $value, + string $expectedClass, + array $expectedResult + ): void { + $this->objectManagerMock->expects($this->at(0)) ->method('create') ->with( $expectedClass, @@ -90,7 +151,11 @@ public function testGetFilters($method, $value, $expectedClass) ->method('getList') ->will($this->returnValue([$this->attributeMock])); - $this->assertEquals(['filter', 'filter'], $this->model->getFilters($this->layerMock)); + $this->layerCategoryConfigMock->expects($this->once()) + ->method('isCategoryFilterVisibleInLayerNavigation') + ->willReturn(false); + + $this->assertEquals($expectedResult, $this->model->getFilters($this->layerMock)); } /** @@ -100,8 +165,8 @@ public function getFiltersDataProvider() { return [ [ - 'method' => 'getFrontendInput', - 'value' => 'price', + 'method' => 'getAttributeCode', + 'value' => FilterList::PRICE_FILTER, 'expectedClass' => 'PriceFilterClass', ], [ @@ -110,10 +175,29 @@ public function getFiltersDataProvider() 'expectedClass' => 'DecimalFilterClass', ], [ - 'method' => 'getFrontendInput', - 'value' => 'text', + 'method' => 'getAttributeCode', + 'value' => null, 'expectedClass' => 'AttributeFilterClass', ] ]; } + + /** + * Provides attribute filters without category item + * + * @return array + */ + public function getFiltersWithoutCategoryDataProvider(): array + { + return [ + 'Filters contains only price attribute' => [ + 'method' => 'getAttributeCode', + 'value' => FilterList::PRICE_FILTER, + 'expectedClass' => 'PriceFilterClass', + 'expectedResult' => [ + 'filter' + ] + ] + ]; + } } diff --git a/app/code/Magento/Catalog/Test/Unit/Model/Layout/DepersonalizePluginTest.php b/app/code/Magento/Catalog/Test/Unit/Model/Layout/DepersonalizePluginTest.php index d644443e461a2..89c588b5e32a2 100644 --- a/app/code/Magento/Catalog/Test/Unit/Model/Layout/DepersonalizePluginTest.php +++ b/app/code/Magento/Catalog/Test/Unit/Model/Layout/DepersonalizePluginTest.php @@ -3,59 +3,82 @@ * Copyright © Magento, Inc. All rights reserved. * See COPYING.txt for license details. */ +declare(strict_types=1); namespace Magento\Catalog\Test\Unit\Model\Layout; -use Magento\Framework\TestFramework\Unit\Helper\ObjectManager; +use Magento\Catalog\Model\Layout\DepersonalizePlugin; +use Magento\Catalog\Model\Session as CatalogSession; +use Magento\Framework\TestFramework\Unit\Helper\ObjectManager as ObjectManagerHelper; +use Magento\Framework\View\LayoutInterface; +use Magento\PageCache\Model\DepersonalizeChecker; +use PHPUnit\Framework\MockObject\MockObject; +use PHPUnit\Framework\TestCase; -class DepersonalizePluginTest extends \PHPUnit\Framework\TestCase +/** + * Unit tests for \Magento\Catalog\Model\Layout\DepersonalizePlugin class. + */ +class DepersonalizePluginTest extends TestCase { /** - * @var \Magento\Catalog\Model\Layout\DepersonalizePlugin + * @var DepersonalizePlugin */ - protected $plugin; + private $plugin; /** - * @var \Magento\Catalog\Model\Session|\PHPUnit_Framework_MockObject_MockObject + * @var CatalogSession|MockObject */ - protected $catalogSessionMock; + private $catalogSessionMock; /** - * @var \Magento\PageCache\Model\DepersonalizeChecker|\PHPUnit_Framework_MockObject_MockObject + * @var DepersonalizeChecker|MockObject */ - protected $depersonalizeCheckerMock; + private $depersonalizeCheckerMock; /** - * @var \Magento\Framework\View\Layout|\PHPUnit_Framework_MockObject_MockObject + * @var LayoutInterface|MockObject */ - protected $resultLayout; + private $layoutMock; + /** + * @inheritdoc + */ protected function setUp() { - $this->layoutMock = $this->createMock(\Magento\Framework\View\Layout::class); - $this->catalogSessionMock = $this->createPartialMock(\Magento\Catalog\Model\Session::class, ['clearStorage']); - $this->resultLayout = $this->createMock(\Magento\Framework\View\Layout::class); - $this->depersonalizeCheckerMock = $this->createMock(\Magento\PageCache\Model\DepersonalizeChecker::class); - - $this->plugin = (new ObjectManager($this))->getObject( - \Magento\Catalog\Model\Layout\DepersonalizePlugin::class, - ['catalogSession' => $this->catalogSessionMock, 'depersonalizeChecker' => $this->depersonalizeCheckerMock] + $this->layoutMock = $this->getMockForAbstractClass(LayoutInterface::class); + $this->catalogSessionMock = $this->createPartialMock(CatalogSession::class, ['clearStorage']); + $this->depersonalizeCheckerMock = $this->createMock(DepersonalizeChecker::class); + + $this->plugin = (new ObjectManagerHelper($this))->getObject( + DepersonalizePlugin::class, + [ + 'catalogSession' => $this->catalogSessionMock, + 'depersonalizeChecker' => $this->depersonalizeCheckerMock, + ] ); } - public function testAfterGenerateXml() + /** + * Test afterGenerateElements method when depersonalization is needed. + * + * @return void + */ + public function testAfterGenerateElements(): void { $this->catalogSessionMock->expects($this->once())->method('clearStorage'); $this->depersonalizeCheckerMock->expects($this->once())->method('checkIfDepersonalize')->willReturn(true); - $actualResult = $this->plugin->afterGenerateXml($this->layoutMock, $this->resultLayout); - $this->assertEquals($this->resultLayout, $actualResult); + $this->assertEmpty($this->plugin->afterGenerateElements($this->layoutMock)); } - public function testAfterGenerateXmlNoDepersonalize() + /** + * Test afterGenerateElements method when depersonalization is not needed. + * + * @return void + */ + public function testAfterGenerateElementsNoDepersonalize(): void { $this->catalogSessionMock->expects($this->never())->method('clearStorage'); $this->depersonalizeCheckerMock->expects($this->once())->method('checkIfDepersonalize')->willReturn(false); - $actualResult = $this->plugin->afterGenerateXml($this->layoutMock, $this->resultLayout); - $this->assertEquals($this->resultLayout, $actualResult); + $this->assertEmpty($this->plugin->afterGenerateElements($this->layoutMock)); } } diff --git a/app/code/Magento/Catalog/Test/Unit/Model/Product/CopierTest.php b/app/code/Magento/Catalog/Test/Unit/Model/Product/CopierTest.php index 809fa0225278c..91441890e83b1 100644 --- a/app/code/Magento/Catalog/Test/Unit/Model/Product/CopierTest.php +++ b/app/code/Magento/Catalog/Test/Unit/Model/Product/CopierTest.php @@ -3,101 +3,266 @@ * Copyright © Magento, Inc. All rights reserved. * See COPYING.txt for license details. */ + namespace Magento\Catalog\Test\Unit\Model\Product; +use Magento\Catalog\Api\Data\ProductExtension; use Magento\Catalog\Api\Data\ProductInterface; use Magento\Catalog\Model\Attribute\ScopeOverriddenValue; use Magento\Catalog\Model\Product; +use Magento\Catalog\Model\Product\Attribute\Source\Status; use Magento\Catalog\Model\Product\Copier; +use Magento\Catalog\Model\Product\CopyConstructorInterface; +use Magento\Catalog\Model\Product\Option\Repository; +use Magento\Catalog\Model\ProductFactory; +use Magento\Catalog\Model\ResourceModel\Product as ProductResourceModel; +use Magento\CatalogInventory\Api\Data\StockItemInterface; +use Magento\Eav\Model\Entity\AbstractEntity; +use Magento\Eav\Model\Entity\Attribute\AbstractAttribute; +use Magento\Framework\EntityManager\EntityMetadata; +use Magento\Framework\EntityManager\MetadataPool; +use PHPUnit\Framework\MockObject\MockObject; +use PHPUnit\Framework\TestCase; /** * Test for Magento\Catalog\Model\Product\Copier class. * * @SuppressWarnings(PHPMD.CouplingBetweenObjects) */ -class CopierTest extends \PHPUnit\Framework\TestCase +class CopierTest extends TestCase { /** - * @var \PHPUnit_Framework_MockObject_MockObject + * @var Copier */ - protected $optionRepositoryMock; + private $_model; /** - * @var Copier + * @var Repository|MockObject */ - protected $_model; + private $optionRepositoryMock; /** - * @var \PHPUnit_Framework_MockObject_MockObject + * @var CopyConstructorInterface|MockObject */ - protected $copyConstructorMock; + private $copyConstructorMock; /** - * @var \PHPUnit_Framework_MockObject_MockObject + * @var ProductFactory|MockObject */ - protected $productFactoryMock; + private $productFactoryMock; /** - * @var \PHPUnit_Framework_MockObject_MockObject + * @var ScopeOverriddenValue|MockObject */ - protected $productMock; + private $scopeOverriddenValueMock; /** - * @var \PHPUnit_Framework_MockObject_MockObject + * @var Product|MockObject */ - protected $metadata; + private $productMock; /** - * @var ScopeOverriddenValue|\PHPUnit_Framework_MockObject_MockObject + * @var EntityMetadata|MockObject */ - private $scopeOverriddenValue; + private $metadata; + /** + * @ingeritdoc + */ protected function setUp() { - $this->copyConstructorMock = $this->createMock(\Magento\Catalog\Model\Product\CopyConstructorInterface::class); - $this->productFactoryMock = $this->createPartialMock( - \Magento\Catalog\Model\ProductFactory::class, - ['create'] - ); - $this->optionRepositoryMock = $this->createMock( - \Magento\Catalog\Model\Product\Option\Repository::class - ); - $this->optionRepositoryMock; + $this->copyConstructorMock = $this->createMock(CopyConstructorInterface::class); + $this->productFactoryMock = $this->createPartialMock(ProductFactory::class, ['create']); + $this->scopeOverriddenValueMock = $this->createMock(ScopeOverriddenValue::class); + $this->optionRepositoryMock = $this->createMock(Repository::class); $this->productMock = $this->createMock(Product::class); - $this->productMock->expects($this->any())->method('getEntityId')->willReturn(1); - $this->scopeOverriddenValue = $this->createMock(ScopeOverriddenValue::class); - $this->metadata = $this->getMockBuilder(\Magento\Framework\EntityManager\EntityMetadata::class) + $this->metadata = $this->getMockBuilder(EntityMetadata::class) ->disableOriginalConstructor() ->getMock(); - $metadataPool = $this->getMockBuilder(\Magento\Framework\EntityManager\MetadataPool::class) + + /** @var MetadataPool|MockObject $metadataPool */ + $metadataPool = $this->getMockBuilder(MetadataPool::class) ->disableOriginalConstructor() ->getMock(); - $metadataPool->expects($this->any())->method('getMetadata')->willReturn($this->metadata); - + $metadataPool->expects($this->once()) + ->method('getMetadata') + ->willReturn($this->metadata); $this->_model = new Copier( $this->copyConstructorMock, $this->productFactoryMock, - $this->scopeOverriddenValue + $this->scopeOverriddenValueMock, + $this->optionRepositoryMock, + $metadataPool + ); + } + + /** + * Test duplicate product + * + * @return void + * @SuppressWarnings(PHPMD.ExcessiveMethodLength) + */ + public function testCopy(): void + { + $stockItem = $this->createMock(StockItemInterface::class); + $extensionAttributes = $this->getMockBuilder(ProductExtension::class) + ->setMethods(['getStockItem', 'setData']) + ->getMock(); + $extensionAttributes + ->expects($this->once()) + ->method('getStockItem') + ->willReturn($stockItem); + $extensionAttributes + ->expects($this->once()) + ->method('setData') + ->with('stock_item', null); + + $productData = [ + 'product data' => ['product data'], + ProductInterface::EXTENSION_ATTRIBUTES_KEY => $extensionAttributes, + ]; + $this->productMock->expects($this->atLeastOnce()) + ->method('getWebsiteIds'); + $this->productMock->expects($this->atLeastOnce()) + ->method('getCategoryIds'); + $this->productMock->expects($this->exactly(2)) + ->method('getData') + ->willReturnMap([ + ['', null, $productData], + ['linkField', null, '1'], + ]); + + $entityMock = $this->getMockForAbstractClass( + AbstractEntity::class, + [], + '', + false, + true, + true, + ['checkAttributeUniqueValue'] ); + $entityMock->expects($this->once()) + ->method('checkAttributeUniqueValue') + ->willReturn(true); - $this->setProperties( - $this->_model, + $attributeMock = $this->getMockForAbstractClass( + AbstractAttribute::class, + [], + '', + false, + true, + true, + ['getEntity'] + ); + $attributeMock->expects($this->once()) + ->method('getEntity') + ->willReturn($entityMock); + + $resourceMock = $this->getMockBuilder(ProductResourceModel::class) + ->disableOriginalConstructor() + ->setMethods(['getAttributeRawValue', 'duplicate', 'getAttribute']) + ->getMock(); + $resourceMock->expects($this->once()) + ->method('getAttributeRawValue') + ->willReturn('urk-key-1'); + $resourceMock->expects($this->exactly(2)) + ->method('getAttribute') + ->willReturn($attributeMock); + + $this->productMock->expects($this->exactly(2)) + ->method('getResource') + ->willReturn($resourceMock); + + $duplicateMock = $this->createPartialMock( + Product::class, [ - 'optionRepository' => $this->optionRepositoryMock, - 'metadataPool' => $metadataPool, + '__wakeup', + 'setData', + 'setOptions', + 'getData', + 'setIsDuplicate', + 'setOriginalLinkId', + 'setStatus', + 'setCreatedAt', + 'setUpdatedAt', + 'setId', + 'getEntityId', + 'save', + 'setUrlKey', + 'setStoreId', + 'getStoreIds', + 'setMetaTitle', + 'setMetaKeyword', + 'setMetaDescription', ] ); + $this->productFactoryMock->expects($this->once()) + ->method('create') + ->willReturn($duplicateMock); + + $duplicateMock->expects($this->once())->method('setOptions')->with([]); + $duplicateMock->expects($this->once())->method('setIsDuplicate')->with(true); + $duplicateMock->expects($this->once())->method('setOriginalLinkId')->with(1); + $duplicateMock->expects($this->once()) + ->method('setStatus') + ->with(Status::STATUS_DISABLED); + $duplicateMock->expects($this->atLeastOnce())->method('setStoreId'); + $duplicateMock->expects($this->once()) + ->method('setCreatedAt') + ->with(null); + $duplicateMock->expects($this->once()) + ->method('setUpdatedAt') + ->with(null); + $duplicateMock->expects($this->once()) + ->method('setId') + ->with(null); + $duplicateMock->expects($this->once()) + ->method('setMetaTitle') + ->with(null); + $duplicateMock->expects($this->once()) + ->method('setMetaKeyword') + ->with(null); + $duplicateMock->expects($this->once()) + ->method('setMetaDescription') + ->with(null); + $duplicateMock->expects($this->atLeastOnce()) + ->method('getStoreIds')->willReturn([]); + $duplicateMock->expects($this->atLeastOnce()) + ->method('setData') + ->willReturn($duplicateMock); + $this->copyConstructorMock->expects($this->once()) + ->method('build') + ->with($this->productMock, $duplicateMock); + $duplicateMock->expects($this->once()) + ->method('setUrlKey') + ->with('urk-key-2') + ->willReturn($duplicateMock); + $duplicateMock->expects($this->once()) + ->method('save'); + $this->metadata->expects($this->once()) + ->method('getLinkField') + ->willReturn('linkField'); + $duplicateMock->expects($this->never()) + ->method('getData'); + $this->optionRepositoryMock->expects($this->once()) + ->method('duplicate') + ->with($this->productMock, $duplicateMock); + + $this->assertEquals($duplicateMock, $this->_model->copy($this->productMock)); } /** + * Test duplicate product with `UrlAlreadyExistsException` while copy stores url + * + * @return void * @SuppressWarnings(PHPMD.ExcessiveMethodLength) */ - public function testCopy() + public function testUrlAlreadyExistsExceptionWhileCopyStoresUrl(): void { - $stockItem = $this->getMockBuilder(\Magento\CatalogInventory\Api\Data\StockItemInterface::class) + $stockItem = $this->getMockBuilder(StockItemInterface::class) ->getMock(); - $extensionAttributes = $this->getMockBuilder(\Magento\Catalog\Api\Data\ProductExtension::class) + $extensionAttributes = $this->getMockBuilder(ProductExtension::class) ->setMethods(['getStockItem', 'setData']) ->getMock(); $extensionAttributes @@ -115,12 +280,10 @@ public function testCopy() ]; $this->productMock->expects($this->atLeastOnce())->method('getWebsiteIds'); $this->productMock->expects($this->atLeastOnce())->method('getCategoryIds'); - $this->productMock->expects($this->any())->method('getData')->willReturnMap( - [ - ['', null, $productData], - ['linkField', null, '1'], - ] - ); + $this->productMock->expects($this->any())->method('getData')->willReturnMap([ + ['', null, $productData], + ['linkField', null, '1'], + ]); $entityMock = $this->getMockForAbstractClass( \Magento\Eav\Model\Entity\AbstractEntity::class, @@ -131,9 +294,9 @@ public function testCopy() true, ['checkAttributeUniqueValue'] ); - $entityMock->expects($this->any()) + $entityMock->expects($this->exactly(11)) ->method('checkAttributeUniqueValue') - ->willReturn(true); + ->willReturn(true, false); $attributeMock = $this->getMockForAbstractClass( \Magento\Eav\Model\Entity\Attribute\AbstractAttribute::class, @@ -197,40 +360,29 @@ public function testCopy() $duplicateMock->expects($this->once())->method('setCreatedAt')->with(null); $duplicateMock->expects($this->once())->method('setUpdatedAt')->with(null); $duplicateMock->expects($this->once())->method('setId')->with(null); - $duplicateMock->expects($this->atLeastOnce())->method('getStoreIds')->willReturn([]); + $duplicateMock->expects($this->atLeastOnce())->method('getStoreIds')->willReturn([1]); $duplicateMock->expects($this->atLeastOnce())->method('setData')->willReturn($duplicateMock); $this->copyConstructorMock->expects($this->once())->method('build')->with($this->productMock, $duplicateMock); - $duplicateMock->expects($this->once())->method('setUrlKey')->with('urk-key-2')->willReturn($duplicateMock); + $duplicateMock->expects( + $this->exactly(11) + )->method( + 'setUrlKey' + )->with( + $this->stringContains('urk-key-') + )->willReturn( + $duplicateMock + ); $duplicateMock->expects($this->once())->method('save'); - $this->metadata->expects($this->any())->method('getLinkField')->willReturn('linkField'); + $this->scopeOverriddenValueMock->expects($this->once())->method('containsValue')->willReturn(true); - $duplicateMock->expects($this->any())->method('getData')->willReturnMap( - [ - ['linkField', null, '2'], - ] - ); - $this->optionRepositoryMock->expects($this->once()) - ->method('duplicate') - ->with($this->productMock, $duplicateMock); - $resourceMock->expects($this->once())->method('duplicate')->with(1, 2); + $this->metadata->expects($this->any())->method('getLinkField')->willReturn('linkField'); - $this->assertEquals($duplicateMock, $this->_model->copy($this->productMock)); - } + $duplicateMock->expects($this->any())->method('getData')->willReturnMap([ + ['linkField', null, '2'], + ]); - /** - * @param $object - * @param array $properties - */ - private function setProperties($object, $properties = []) - { - $reflectionClass = new \ReflectionClass(get_class($object)); - foreach ($properties as $key => $value) { - if ($reflectionClass->hasProperty($key)) { - $reflectionProperty = $reflectionClass->getProperty($key); - $reflectionProperty->setAccessible(true); - $reflectionProperty->setValue($object, $value); - } - } + $this->expectException(\Magento\UrlRewrite\Model\Exception\UrlAlreadyExistsException::class); + $this->_model->copy($this->productMock); } } diff --git a/app/code/Magento/Catalog/Test/Unit/Model/Product/Gallery/GalleryManagementTest.php b/app/code/Magento/Catalog/Test/Unit/Model/Product/Gallery/GalleryManagementTest.php index 6d4e98b60ad18..30994eda87273 100644 --- a/app/code/Magento/Catalog/Test/Unit/Model/Product/Gallery/GalleryManagementTest.php +++ b/app/code/Magento/Catalog/Test/Unit/Model/Product/Gallery/GalleryManagementTest.php @@ -59,6 +59,7 @@ protected function setUp() 'getCustomAttribute', 'getMediaGalleryEntries', 'setMediaGalleryEntries', + 'getMediaAttributes', ] ); $this->mediaGalleryEntryMock = @@ -99,6 +100,9 @@ public function testCreateWithCannotSaveException() $entryContentMock = $this->getMockBuilder(\Magento\Framework\Api\Data\ImageContentInterface::class) ->disableOriginalConstructor() ->getMock(); + $attributeMock = $this->getMockBuilder(\Magento\Eav\Model\Entity\Attribute\AbstractAttribute::class) + ->disableOriginalConstructor() + ->getMock(); $this->mediaGalleryEntryMock->expects($this->any())->method('getContent')->willReturn($entryContentMock); $this->productRepositoryMock->expects($this->once()) ->method('get') @@ -108,6 +112,10 @@ public function testCreateWithCannotSaveException() $this->contentValidatorMock->expects($this->once())->method('isValid')->with($entryContentMock) ->willReturn(true); + $this->productMock->expects($this->any()) + ->method('getMediaAttributes') + ->willReturn(['small_image' => $attributeMock]); + $this->productRepositoryMock->expects($this->once())->method('save')->with($this->productMock) ->willThrowException(new \Exception()); $this->model->create($productSku, $this->mediaGalleryEntryMock); @@ -133,6 +141,8 @@ public function testCreate() $this->contentValidatorMock->expects($this->once())->method('isValid')->with($entryContentMock) ->willReturn(true); + $this->mediaGalleryEntryMock->expects($this->any())->method('getTypes')->willReturn(['small_image']); + $newEntryMock = $this->createMock(\Magento\Catalog\Api\Data\ProductAttributeMediaGalleryEntryInterface::class); $newEntryMock->expects($this->exactly(2))->method('getId')->willReturn(42); $this->productMock->expects($this->at(2))->method('getMediaGalleryEntries') diff --git a/app/code/Magento/Catalog/Test/Unit/Model/Product/Image/ParamsBuilderTest.php b/app/code/Magento/Catalog/Test/Unit/Model/Product/Image/ParamsBuilderTest.php index 22e3a88574e03..68239c8fa8aed 100644 --- a/app/code/Magento/Catalog/Test/Unit/Model/Product/Image/ParamsBuilderTest.php +++ b/app/code/Magento/Catalog/Test/Unit/Model/Product/Image/ParamsBuilderTest.php @@ -3,6 +3,7 @@ * Copyright © Magento, Inc. All rights reserved. * See COPYING.txt for license details. */ +declare(strict_types=1); namespace Magento\Catalog\Test\Unit\Model\Product\Image; @@ -11,10 +12,15 @@ use Magento\Framework\App\Area; use Magento\Framework\App\Config\ScopeConfigInterface; use Magento\Framework\Config\View; +use Magento\Framework\TestFramework\Unit\Helper\ObjectManager; use Magento\Framework\View\ConfigInterface; use Magento\Store\Model\ScopeInterface; +use PHPUnit\Framework\TestCase; -class ParamsBuilderTest extends \PHPUnit\Framework\TestCase +/** + * Test product image params builder + */ +class ParamsBuilderTest extends TestCase { /** * @var ScopeConfigInterface @@ -30,10 +36,17 @@ class ParamsBuilderTest extends \PHPUnit\Framework\TestCase * @var ParamsBuilder */ private $model; + /** + * @var array + */ + private $scopeConfigData = []; + /** + * @inheritDoc + */ protected function setUp() { - $objectManager = new \Magento\Framework\TestFramework\Unit\Helper\ObjectManager($this); + $objectManager = new ObjectManager($this); $this->scopeConfig = $this->createMock(ScopeConfigInterface::class); $this->viewConfig = $this->createMock(ConfigInterface::class); $this->model = $objectManager->getObject( @@ -43,28 +56,37 @@ protected function setUp() 'viewConfig' => $this->viewConfig, ] ); + $this->scopeConfigData = []; + $this->scopeConfig->method('getValue') + ->willReturnCallback( + function ($path, $scopeType, $scopeCode) { + return $this->scopeConfigData[$path][$scopeType][$scopeCode] ?? null; + } + ); } /** - * Test watermark location. + * Test build() with different parameters and config values + * + * @param int $scopeId + * @param array $config + * @param array $imageArguments + * @param array $expected + * @dataProvider buildDataProvider */ - public function testWatermarkLocation() + public function testBuild(int $scopeId, array $config, array $imageArguments, array $expected) { - $imageArguments = [ - 'type' => 'type', - 'height' => 'image_height', - 'width' => 'image_width', - 'angle' => 'angle', - 'background' => [1, 2, 3] + $this->scopeConfigData[Image::XML_PATH_JPEG_QUALITY][ScopeConfigInterface::SCOPE_TYPE_DEFAULT][null] = 80; + foreach ($config as $path => $value) { + $this->scopeConfigData[$path][ScopeInterface::SCOPE_STORE][$scopeId] = $value; + } + $imageArguments += [ + 'type' => 'image', + 'height' => '600', + 'width' => '400', + 'angle' => '45', + 'background' => [110, 64, 224] ]; - $scopeId = 1; - $quality = 100; - $file = 'file'; - $width = 'width'; - $height = 'height'; - $size = "{$width}x{$height}"; - $opacity = 'opacity'; - $position = 'position'; $viewMock = $this->createMock(View::class); $viewMock->expects($this->once()) @@ -77,51 +99,16 @@ public function testWatermarkLocation() ->with(['area' => Area::AREA_FRONTEND]) ->willReturn($viewMock); - $this->scopeConfig->expects($this->exactly(5))->method('getValue')->withConsecutive( - [ - Image::XML_PATH_JPEG_QUALITY - ], - [ - "design/watermark/{$imageArguments['type']}_image", - ScopeInterface::SCOPE_STORE, - $scopeId, - ], - [ - "design/watermark/{$imageArguments['type']}_size", - ScopeInterface::SCOPE_STORE], - [ - "design/watermark/{$imageArguments['type']}_imageOpacity", - ScopeInterface::SCOPE_STORE, - $scopeId - ], - [ - "design/watermark/{$imageArguments['type']}_position", - ScopeInterface::SCOPE_STORE, - $scopeId - ] - )->willReturnOnConsecutiveCalls( - $quality, - $file, - $size, - $opacity, - $position - ); - $actual = $this->model->build($imageArguments, $scopeId); - $expected = [ + $expected += [ 'image_type' => $imageArguments['type'], 'background' => $imageArguments['background'], 'angle' => $imageArguments['angle'], - 'quality' => $quality, + 'quality' => 80, 'keep_aspect_ratio' => true, 'keep_frame' => true, 'keep_transparency' => true, 'constrain_only' => true, - 'watermark_file' => $file, - 'watermark_image_opacity' => $opacity, - 'watermark_position' => $position, - 'watermark_width' => $width, - 'watermark_height' => $height, 'image_height' => $imageArguments['height'], 'image_width' => $imageArguments['width'], ]; @@ -131,4 +118,50 @@ public function testWatermarkLocation() $actual ); } + + /** + * Provides test scenarios for + * + * @return array + */ + public function buildDataProvider() + { + return [ + 'watermark config' => [ + 1, + [ + 'design/watermark/small_image_image' => 'stores/1/magento-logo.png', + 'design/watermark/small_image_size' => '60x40', + 'design/watermark/small_image_imageOpacity' => '50', + 'design/watermark/small_image_position' => 'bottom-right', + ], + [ + 'type' => 'small_image' + ], + [ + 'watermark_file' => 'stores/1/magento-logo.png', + 'watermark_image_opacity' => '50', + 'watermark_position' => 'bottom-right', + 'watermark_width' => '60', + 'watermark_height' => '40', + ] + ], + 'watermark config empty' => [ + 1, + [ + 'design/watermark/small_image_image' => 'stores/1/magento-logo.png', + ], + [ + 'type' => 'small_image' + ], + [ + 'watermark_file' => 'stores/1/magento-logo.png', + 'watermark_image_opacity' => null, + 'watermark_position' => null, + 'watermark_width' => null, + 'watermark_height' => null, + ] + ] + ]; + } } diff --git a/app/code/Magento/Catalog/Test/Unit/Model/Product/Option/ValueTest.php b/app/code/Magento/Catalog/Test/Unit/Model/Product/Option/ValueTest.php index 1ff5bef78cd79..212c8020750d2 100644 --- a/app/code/Magento/Catalog/Test/Unit/Model/Product/Option/ValueTest.php +++ b/app/code/Magento/Catalog/Test/Unit/Model/Product/Option/ValueTest.php @@ -3,15 +3,21 @@ * Copyright © Magento, Inc. All rights reserved. * See COPYING.txt for license details. */ -namespace Magento\Catalog\Test\Unit\Model\Product\Option; +declare(strict_types=1); -use \Magento\Catalog\Model\Product\Option\Value; +namespace Magento\Catalog\Test\Unit\Model\Product\Option; use Magento\Catalog\Model\Product; + use Magento\Catalog\Model\Product\Option; -use Magento\Framework\Model\ActionValidator\RemoveAction; +use Magento\Catalog\Model\Product\Option\Value; +use Magento\Catalog\Pricing\Price\CalculateCustomOptionCatalogRule; use Magento\Framework\TestFramework\Unit\Helper\ObjectManager; +use PHPUnit\Framework\MockObject\MockObject; +/** + * Test for \Magento\Catalog\Model\Product\Option\Value class. + */ class ValueTest extends \PHPUnit\Framework\TestCase { /** @@ -24,6 +30,11 @@ class ValueTest extends \PHPUnit\Framework\TestCase */ private $customOptionPriceCalculatorMock; + /** + * @var CalculateCustomOptionCatalogRule|MockObject + */ + private $CalculateCustomOptionCatalogRule; + protected function setUp() { $mockedResource = $this->getMockedResource(); @@ -33,6 +44,10 @@ protected function setUp() \Magento\Catalog\Pricing\Price\CustomOptionPriceCalculator::class ); + $this->CalculateCustomOptionCatalogRule = $this->createMock( + CalculateCustomOptionCatalogRule::class + ); + $helper = new ObjectManager($this); $this->model = $helper->getObject( \Magento\Catalog\Model\Product\Option\Value::class, @@ -40,6 +55,7 @@ protected function setUp() 'resource' => $mockedResource, 'valueCollectionFactory' => $mockedCollectionFactory, 'customOptionPriceCalculator' => $this->customOptionPriceCalculatorMock, + 'CalculateCustomOptionCatalogRule' => $this->CalculateCustomOptionCatalogRule ] ); $this->model->setOption($this->getMockedOption()); @@ -66,11 +82,11 @@ public function testGetPrice() $this->model->setPriceType(Value::TYPE_PERCENT); $this->assertEquals($price, $this->model->getPrice(false)); - $percentPice = 100; - $this->customOptionPriceCalculatorMock->expects($this->atLeastOnce()) - ->method('getOptionPriceByPriceCode') - ->willReturn($percentPice); - $this->assertEquals($percentPice, $this->model->getPrice(true)); + $percentPrice = 100; + $this->CalculateCustomOptionCatalogRule->expects($this->atLeastOnce()) + ->method('execute') + ->willReturn($percentPrice); + $this->assertEquals($percentPrice, $this->model->getPrice(true)); } public function testGetValuesCollection() diff --git a/app/code/Magento/Catalog/Test/Unit/Model/Product/ProductFrontendAction/SynchronizerTest.php b/app/code/Magento/Catalog/Test/Unit/Model/Product/ProductFrontendAction/SynchronizerTest.php index 38bed83cb9504..8b70899cd26d3 100644 --- a/app/code/Magento/Catalog/Test/Unit/Model/Product/ProductFrontendAction/SynchronizerTest.php +++ b/app/code/Magento/Catalog/Test/Unit/Model/Product/ProductFrontendAction/SynchronizerTest.php @@ -82,15 +82,15 @@ public function testFilterProductActions() { $typeId = 'recently_compared_product'; $productsData = [ - 1 => [ + 'website-1-1' => [ 'added_at' => 12, 'product_id' => 1, ], - 2 => [ + 'website-1-2' => [ 'added_at' => 13, 'product_id' => '2', ], - 3 => [ + 'website-2-3' => [ 'added_at' => 14, 'product_id' => 3, ] diff --git a/app/code/Magento/Catalog/Test/Unit/Model/Product/UrlTest.php b/app/code/Magento/Catalog/Test/Unit/Model/Product/UrlTest.php index ef7aad2cbb802..c7b6402cd3877 100644 --- a/app/code/Magento/Catalog/Test/Unit/Model/Product/UrlTest.php +++ b/app/code/Magento/Catalog/Test/Unit/Model/Product/UrlTest.php @@ -159,12 +159,11 @@ public function testGetUrl( $this->assertEquals($requestPathProduct, $this->model->getUrlInStore($product, $routeParams)); break; case 'getProductUrl': - $this->assertEquals($requestPathProduct, $this->model->getProductUrl($product, true)); + $this->assertEquals($requestPathProduct, $this->model->getProductUrl($product, null)); $this->sidResolver - ->expects($this->once()) + ->expects($this->never()) ->method('getUseSessionInUrl') ->will($this->returnValue(true)); - $this->assertEquals($requestPathProduct, $this->model->getProductUrl($product, null)); break; } } @@ -212,7 +211,7 @@ public function getUrlDataProvider() 1, 1, [], - ['_direct' => '/product/url/path', '_query' => []], + ['_direct' => '/product/url/path', '_query' => [], '_nosid' => true], null, null, ] diff --git a/app/code/Magento/Catalog/Test/Unit/Model/Product/Webapi/ProductOutputProcessorTest.php b/app/code/Magento/Catalog/Test/Unit/Model/Product/Webapi/ProductOutputProcessorTest.php new file mode 100644 index 0000000000000..fa419d23626ae --- /dev/null +++ b/app/code/Magento/Catalog/Test/Unit/Model/Product/Webapi/ProductOutputProcessorTest.php @@ -0,0 +1,350 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ + +namespace Magento\Catalog\Test\Unit\Model\Product\Webapi; + +use Magento\Catalog\Api\Data\ProductInterface; +use Magento\Catalog\Model\Product\Webapi\ProductOutputProcessor; +use Magento\Framework\Webapi\Request; +use Magento\Framework\Webapi\Rest\Request\DeserializerInterface; + +class ProductOutputProcessorTest extends \PHPUnit\Framework\TestCase +{ + /** + * @var Request + */ + private $requestMock; + + /** + * @var DeserializerInterface + */ + private $deserializerMock; + + /** + * @var ProductOutputProcessor + */ + private $productOutputProcessor; + + protected function setUp() + { + $this->requestMock = $this->createPartialMock( + Request::class, + ['getContent'] + ); + $this->deserializerMock = $this->getMockBuilder(DeserializerInterface::class) + ->getMockForAbstractClass(); + $this->productOutputProcessor = new ProductOutputProcessor($this->requestMock, $this->deserializerMock); + } + + /** + * @dataProvider getProductProcessorDataProvider + * @param $request + * @param $product + * @param $result + * @param $expectedResult + */ + public function testGetByProductType( + array $request, + ProductInterface $product, + array $result, + array $expectedResult + ) { + $this->requestMock + ->method('getContent') + ->willReturn($request); + $this->deserializerMock + ->method('deserialize') + ->willReturn($request); + $this->assertEquals($expectedResult, $this->productOutputProcessor->execute($product, $result)); + } + + /** + * Product data provider + * + * @return array + * @SuppressWarnings(PHPMD.ExcessiveMethodLength) + */ + public function getProductProcessorDataProvider() + { + return [ + 'request object contains `product_links` and `tier_prices`' => [ + 'request' => [ + [ + 'product' => [ + 'sku' => 'MH01', + 'status' => 1, + 'product_links' => [], + 'tier_prices' => [] + ] + ] + ], + 'product' => $this->setProductInformation( + [ + 'sku' => 'MH01', + 'status' => 1, + 'product_links' => [], + 'tier_prices' => [] + ] + ), + 'result' => [ + 'sku' => 'MH01', + 'status' => 1, + 'product_links' => [], + 'tier_prices' => [] + ], + 'expectedResult' => [ + 'sku' => 'MH01', + 'status' => 1, + 'product_links' => [], + 'tier_prices' => [] + ] + ], + 'request object contains `product_links`' => [ + 'request' => [ + [ + 'product' => [ + 'sku' => 'MH01', + 'status' => 1, + 'product_links' => [] + ] + ] + ], + 'product' => $this->setProductInformation( + [ + 'sku' => 'MH01', + 'status' => 1, + 'product_links' => [], + 'tier_prices' => [] + ] + ), + 'result' => [ + 'sku' => 'MH01', + 'status' => 1, + 'product_links' => [], + 'tier_prices' => [] + ], + 'expectedResult' => [ + 'sku' => 'MH01', + 'status' => 1, + 'product_links' => [] + ] + ], + 'request object SKU does not match with product object SKU' => [ + 'request' => [ + [ + 'product' => [ + 'sku' => 'MH01', + 'status' => 1 + ] + ] + ], + 'product' => $this->setProductInformation( + [ + 'sku' => 'MH03', + 'status' => 1, + 'product_links' => [], + 'tier_prices' => [] + ] + ), + 'result' => [ + 'sku' => 'MH01', + 'status' => 1, + 'product_links' => [], + 'tier_prices' => [] + ], + 'expectedResult' => [ + 'sku' => 'MH01', + 'status' => 1, + 'product_links' => [], + 'tier_prices' => [] + ] + ], + 'request object does not contain `sku`' => [ + 'request' => [ + [ + 'product' => [ + 'status' => 1, + 'product_links' => [], + 'tier_prices' => [] + ] + ] + ], + 'product' => $this->setProductInformation( + [ + 'sku' => 'MH01', + 'status' => 1, + 'product_links' => [], + 'tier_prices' => [] + ] + ), + 'result' => [ + 'sku' => 'MH01', + 'status' => 1, + 'product_links' => [], + 'tier_prices' => [] + ], + 'expectedResult' => [ + 'sku' => 'MH01', + 'status' => 1, + 'product_links' => [], + 'tier_prices' => [] + ] + ], + 'request object has empty product' => [ + 'request' => [ + [ + 'product' => [] + ] + ], + 'product' => $this->setProductInformation( + [ + 'sku' => 'MH01', + 'status' => 1, + 'product_links' => [], + 'tier_prices' => [] + ] + ), + 'result' => [ + 'sku' => 'MH01', + 'status' => 1, + 'product_links' => [], + 'tier_prices' => [] + ], + 'expectedResult' => [ + 'sku' => 'MH01', + 'status' => 1, + 'product_links' => [], + 'tier_prices' => [] + ] + ], + 'request object does not contain product' => [ + 'request' => [ + [ + 'order' => [ + 'order_id' => 1, + 'order_details' => 'test' + ] + ] + ], + 'product' => $this->setProductInformation( + [ + 'sku' => 'MH01', + 'status' => 1, + 'product_links' => [], + 'tier_prices' => [] + ] + ), + 'result' => [ + 'sku' => 'MH01', + 'status' => 1, + 'product_links' => [], + 'tier_prices' => [] + ], + 'expectedResult' => [ + 'sku' => 'MH01', + 'status' => 1, + 'product_links' => [], + 'tier_prices' => [] + ] + ], + 'request object contains `product_links` is null and `tier_prices` is null' => [ + 'request' => [ + [ + 'product' => [ + 'sku' => 'MH01', + 'status' => 1, + 'product_links' => null, + 'tier_prices' => null + ] + ] + ], + 'product' => $this->setProductInformation( + [ + 'sku' => 'MH01', + 'status' => 1, + 'product_links' => [], + 'tier_prices' => [] + ] + ), + 'result' => [ + 'sku' => 'MH01', + 'status' => 1, + 'product_links' => null, + 'tier_prices' => null + ], + 'expectedResult' => [ + 'sku' => 'MH01', + 'status' => 1, + 'product_links' => null, + 'tier_prices' => null + ] + ], + 'request object has empty array' => [ + 'request' => [], + 'product' => $this->setProductInformation( + [ + 'sku' => 'MH01', + 'status' => 1, + 'product_links' => [], + 'tier_prices' => [] + ] + ), + 'result' => [ + 'sku' => 'MH01', + 'status' => 1 + ], + 'expectedResult' => [ + 'sku' => 'MH01', + 'status' => 1 + ] + ] + ]; + } + + private function setProductInformation($productArr) + { + $productMock = $this->getMockBuilder(ProductInterface::class) + ->disableOriginalConstructor() + ->setMethods( + [ + 'setSku', + 'setStatus', + 'setProductLinks', + 'setTierPrices', + 'getSku', + 'getProductLinks', + 'getTierPrices' + ] + ) + ->getMockForAbstractClass(); + $productMock + ->method('setSku') + ->with($productArr['sku']) + ->willReturn(true); + $productMock + ->method('getSku') + ->willReturn($productArr['sku']); + $productMock + ->method('setStatus') + ->with($productArr['status']) + ->willReturn(true); + $productMock + ->method('setProductLinks') + ->with($productArr['product_links']) + ->willReturn(true); + $productMock + ->method('getProductLinks') + ->willReturn($productArr['product_links']); + $productMock + ->method('setTierPrices') + ->with($productArr['tier_prices']) + ->willReturn(true); + $productMock + ->method('getTierPrices') + ->willReturn($productArr['tier_prices']); + return $productMock; + } +} diff --git a/app/code/Magento/Catalog/Test/Unit/Model/ProductLink/ManagementTest.php b/app/code/Magento/Catalog/Test/Unit/Model/ProductLink/ManagementTest.php index ab52d87f56291..69bd7dc059022 100644 --- a/app/code/Magento/Catalog/Test/Unit/Model/ProductLink/ManagementTest.php +++ b/app/code/Magento/Catalog/Test/Unit/Model/ProductLink/ManagementTest.php @@ -7,45 +7,66 @@ namespace Magento\Catalog\Test\Unit\Model\ProductLink; use Magento\Framework\Exception\NoSuchEntityException; +use Magento\Framework\Exception\InputException; +use Magento\Framework\Exception\CouldNotSaveException; +use Magento\Catalog\Model\ProductLink\Management; +use Magento\Catalog\Model\ProductRepository; +use Magento\Catalog\Model\Product; +use Magento\Catalog\Model\Product\LinkTypeProvider; +use Magento\Catalog\Model\ProductLink\Link; +use Magento\Framework\TestFramework\Unit\Helper\ObjectManager as ObjectManagerHelper; +use PHPUnit\Framework\MockObject\MockObject; +use PHPUnit\Framework\TestCase; -class ManagementTest extends \PHPUnit\Framework\TestCase +/** + * Unit Test for Magento\Catalog\Model\ProductLink\Management + */ +class ManagementTest extends TestCase { + + const STUB_PRODUCT_SKU_1 = 'Simple Product 1'; + const STUB_PRODUCT_SKU_2 = 'Simple Product 2'; + const STUB_PRODUCT_TYPE = 'simple'; + const STUB_LINK_TYPE = 'related'; + const STUB_BAD_TYPE = 'bad type'; + /** - * @var \Magento\Catalog\Model\ProductLink\Management + * @var Management */ protected $model; /** - * @var \PHPUnit_Framework_MockObject_MockObject + * @var ProductRepository|MockObject */ - protected $productRepositoryMock; /** - * @var \PHPUnit_Framework_MockObject_MockObject + * @var Product|MockObject */ protected $productMock; /** - * @var \PHPUnit_Framework_MockObject_MockObject + * @var LinkTypeProvider|MockObject */ protected $linkTypeProviderMock; /** - * @var \Magento\Framework\ObjectManagerInterface + * @var ObjectManagerHelper */ protected $objectManager; - protected function setUp() + /** + * @inheritDoc + */ + protected function setUp(): void { - $this->productRepositoryMock = $this->createMock(\Magento\Catalog\Model\ProductRepository::class); - $this->productMock = $this->createMock(\Magento\Catalog\Model\Product::class); - - $this->linkTypeProviderMock = $this->createMock(\Magento\Catalog\Model\Product\LinkTypeProvider::class); + $this->productRepositoryMock = $this->createMock(ProductRepository::class); + $this->productMock = $this->createMock(Product::class); + $this->linkTypeProviderMock = $this->createMock(LinkTypeProvider::class); - $this->objectManager = new \Magento\Framework\TestFramework\Unit\Helper\ObjectManager($this); + $this->objectManager = new ObjectManagerHelper($this); $this->model = $this->objectManager->getObject( - \Magento\Catalog\Model\ProductLink\Management::class, + Management::class, [ 'productRepository' => $this->productRepositoryMock, 'linkTypeProvider' => $this->linkTypeProviderMock @@ -53,193 +74,320 @@ protected function setUp() ); } - public function testGetLinkedItemsByType() + /** + * Test getLinkedItemsByType() + * + * @return void + */ + public function testGetLinkedItemsByType(): void { - $productSku = 'Simple Product 1'; - $linkType = 'related'; - $this->productRepositoryMock->expects($this->once())->method('get')->with($productSku) + $productSku = self::STUB_PRODUCT_SKU_1; + $linkType = self::STUB_LINK_TYPE; + + $this->productRepositoryMock->expects($this->once()) + ->method('get') + ->with($productSku) ->willReturn($this->productMock); - $inputRelatedLink = $this->objectManager->getObject(\Magento\Catalog\Model\ProductLink\Link::class); - $inputRelatedLink->setProductSku($productSku); - $inputRelatedLink->setLinkType($linkType); - $inputRelatedLink->setData("sku", "Simple Product 2"); - $inputRelatedLink->setData("type_id", "simple"); - $inputRelatedLink->setPosition(0); - $links = [$inputRelatedLink]; + $links = $this->getInputRelatedLinkMock( + $productSku, + $linkType, + self::STUB_PRODUCT_SKU_2, + self::STUB_PRODUCT_TYPE + ); - $linkTypes = ['related' => 1, 'upsell' => 4, 'crosssell' => 5, 'associated' => 3]; - $this->linkTypeProviderMock->expects($this->once()) - ->method('getLinkTypes') - ->willReturn($linkTypes); + $this->getLinkTypesMock(); + + $this->productMock->expects($this->once()) + ->method('getProductLinks') + ->willReturn($links); - $this->productMock->expects($this->once())->method('getProductLinks')->willReturn($links); - $this->assertEquals($links, $this->model->getLinkedItemsByType($productSku, $linkType)); + $this->assertEquals( + $links, + $this->model->getLinkedItemsByType($productSku, $linkType) + ); } /** - * @expectedException \Magento\Framework\Exception\NoSuchEntityException - * @expectedExceptionMessage The "bad type" link type is unknown. Verify the type and try again. + * Test for GetLinkedItemsByType() with wrong type + * + * @return void + * @throws NoSuchEntityException */ - public function testGetLinkedItemsByTypeWithWrongType() + public function testGetLinkedItemsByTypeWithWrongType(): void { - $productSku = 'Simple Product 1'; - $linkType = 'bad type'; - $this->productRepositoryMock->expects($this->never())->method('get')->with($productSku) + $productSku = self::STUB_PRODUCT_SKU_1; + $linkType = self::STUB_BAD_TYPE; + + $this->productRepositoryMock->expects($this->never()) + ->method('get') + ->with($productSku) ->willReturn($this->productMock); - $inputRelatedLink = $this->objectManager->getObject(\Magento\Catalog\Model\ProductLink\Link::class); - $inputRelatedLink->setProductSku($productSku); - $inputRelatedLink->setLinkType($linkType); - $inputRelatedLink->setData("sku", "Simple Product 2"); - $inputRelatedLink->setData("type_id", "simple"); - $inputRelatedLink->setPosition(0); - $links = [$inputRelatedLink]; + $links = $this->getInputRelatedLinkMock( + $productSku, + $linkType, + self::STUB_PRODUCT_SKU_2, + self::STUB_PRODUCT_TYPE + ); - $linkTypes = ['related' => 1, 'upsell' => 4, 'crosssell' => 5, 'associated' => 3]; - $this->linkTypeProviderMock->expects($this->once()) - ->method('getLinkTypes') - ->willReturn($linkTypes); + $this->getLinkTypesMock(); + + $this->productMock->expects($this->never()) + ->method('getProductLinks') + ->willReturn($links); + + $this->expectException(NoSuchEntityException::class); + $this->expectExceptionMessage( + 'The "bad type" link type is unknown. Verify the type and try again.' + ); - $this->productMock->expects($this->never())->method('getProductLinks')->willReturn($links); $this->model->getLinkedItemsByType($productSku, $linkType); } - public function testSetProductLinks() + /** + * Test for setProductLinks() + * + * @return void + */ + public function testSetProductLinks(): void { - $productSku = 'Simple Product 1'; - $linkType = 'related'; - $this->productRepositoryMock->expects($this->once())->method('get')->with($productSku) + $productSku = self::STUB_PRODUCT_SKU_1; + $linkType = self::STUB_LINK_TYPE; + + $this->productRepositoryMock->expects($this->once()) + ->method('get') + ->with($productSku) ->willReturn($this->productMock); - $inputRelatedLink = $this->objectManager->getObject(\Magento\Catalog\Model\ProductLink\Link::class); - $inputRelatedLink->setProductSku($productSku); - $inputRelatedLink->setLinkType($linkType); - $inputRelatedLink->setData("sku", "Simple Product 1"); - $inputRelatedLink->setData("type_id", "related"); - $inputRelatedLink->setPosition(0); - $links = [$inputRelatedLink]; + $links = $this->getInputRelatedLinkMock( + $productSku, + $linkType, + self::STUB_PRODUCT_SKU_2, + self::STUB_PRODUCT_TYPE + ); - $linkTypes = ['related' => 1, 'upsell' => 4, 'crosssell' => 5, 'associated' => 3]; - $this->linkTypeProviderMock->expects($this->once()) - ->method('getLinkTypes') - ->willReturn($linkTypes); + $this->getLinkTypesMock(); + + $this->productMock->expects($this->once()) + ->method('getProductLinks') + ->willReturn([]); + $this->productMock->expects($this->once()) + ->method('setProductLinks') + ->with($links); - $this->productMock->expects($this->once())->method('getProductLinks')->willReturn([]); - $this->productMock->expects($this->once())->method('setProductLinks')->with($links); $this->assertTrue($this->model->setProductLinks($productSku, $links)); } /** - * @expectedException \Magento\Framework\Exception\InputException - * @expectedExceptionMessage "linkType" is required. Enter and try again. + * Test for SetProductLinks without link type in link object + * + * @return void + * @throws InputException */ - public function testSetProductLinksWithoutLinkTypeInLink() + public function testSetProductLinksWithoutLinkTypeInLink(): void { - $productSku = 'Simple Product 1'; + $productSku = self::STUB_PRODUCT_SKU_1; - $inputRelatedLink = $this->objectManager->getObject(\Magento\Catalog\Model\ProductLink\Link::class); + $inputRelatedLink = $this->objectManager->getObject(Link::class); $inputRelatedLink->setProductSku($productSku); - $inputRelatedLink->setData("sku", "Simple Product 1"); + $inputRelatedLink->setData("sku", self::STUB_PRODUCT_SKU_2); $inputRelatedLink->setPosition(0); $links = [$inputRelatedLink]; - $linkTypes = ['related' => 1, 'upsell' => 4, 'crosssell' => 5, 'associated' => 3]; - $this->linkTypeProviderMock->expects($this->once()) - ->method('getLinkTypes') - ->willReturn($linkTypes); + $this->getLinkTypesMock(); + + $this->expectException(InputException::class); + $this->expectExceptionMessage( + '"linkType" is required. Enter and try again.' + ); $this->assertTrue($this->model->setProductLinks($productSku, $links)); } /** - * @expectedException \Magento\Framework\Exception\NoSuchEntityException - * @expectedExceptionMessage The "bad type" link type wasn't found. Verify the type and try again. + * Test for SetProductLinks with empty array of items + * + * @return void + * @throws InputException + */ + public function testSetProductLinksWithEmptyArrayItems(): void + { + $productSku = self::STUB_PRODUCT_SKU_1; + + $this->productRepositoryMock->expects($this->never()) + ->method('get') + ->with($productSku) + ->willReturn($this->productMock); + + $this->linkTypeProviderMock->expects($this->never()) + ->method('getLinkTypes') + ->willReturn([]); + + $this->expectException(InputException::class); + $this->expectExceptionMessage( + 'Invalid value of "empty array" provided for the items field.' + ); + + $this->assertTrue($this->model->setProductLinks($productSku, [])); + } + + /** + * Test setProductLinks() throw exception if product link type not exist + * + * @return void + * @throws NoSuchEntityException */ public function testSetProductLinksThrowExceptionIfProductLinkTypeDoesNotExist() { - $productSku = 'Simple Product 1'; - $linkType = 'bad type'; - $this->productRepositoryMock->expects($this->never())->method('get')->with($productSku) + $productSku = self::STUB_PRODUCT_SKU_1; + $linkType = self::STUB_BAD_TYPE; + + $this->productRepositoryMock->expects($this->never()) + ->method('get') + ->with($productSku) ->willReturn($this->productMock); - $inputRelatedLink = $this->objectManager->getObject(\Magento\Catalog\Model\ProductLink\Link::class); - $inputRelatedLink->setProductSku($productSku); - $inputRelatedLink->setLinkType($linkType); - $inputRelatedLink->setData("sku", "Simple Product 2"); - $inputRelatedLink->setData("type_id", "simple"); - $inputRelatedLink->setPosition(0); - $links = [$inputRelatedLink]; + $links = $this->getInputRelatedLinkMock( + $productSku, + $linkType, + self::STUB_PRODUCT_SKU_2, + self::STUB_PRODUCT_TYPE + ); - $linkTypes = ['related' => 1, 'upsell' => 4, 'crosssell' => 5, 'associated' => 3]; - $this->linkTypeProviderMock->expects($this->once()) - ->method('getLinkTypes') - ->willReturn($linkTypes); + $this->getLinkTypesMock(); + + $this->expectException(NoSuchEntityException::class); + $this->expectExceptionMessage( + 'The "bad type" link type wasn\'t found. Verify the type and try again.' + ); $this->assertTrue($this->model->setProductLinks('', $links)); } /** - * @expectedException \Magento\Framework\Exception\NoSuchEntityException - * @expectedExceptionMessage The product that was requested doesn't exist. Verify the product and try again. + * Test for setProductLinks() with no product exception + * + * @return void + * @throws NoSuchEntityException */ public function testSetProductLinksNoProductException() { - $productSku = 'Simple Product 1'; - $linkType = 'related'; - - $inputRelatedLink = $this->objectManager->getObject(\Magento\Catalog\Model\ProductLink\Link::class); - $inputRelatedLink->setProductSku($productSku); - $inputRelatedLink->setLinkType($linkType); - $inputRelatedLink->setData("sku", "Simple Product 2"); - $inputRelatedLink->setData("type_id", "simple"); - $inputRelatedLink->setPosition(0); - $links = [$inputRelatedLink]; + $productSku = self::STUB_PRODUCT_SKU_1; + $linkType = self::STUB_LINK_TYPE; + + $links = $this->getInputRelatedLinkMock( + $productSku, + $linkType, + self::STUB_PRODUCT_SKU_2, + self::STUB_PRODUCT_TYPE + ); - $linkTypes = ['related' => 1, 'upsell' => 4, 'crosssell' => 5, 'associated' => 3]; - $this->linkTypeProviderMock->expects($this->once()) - ->method('getLinkTypes') - ->willReturn($linkTypes); + $this->getLinkTypesMock(); $this->productRepositoryMock->expects($this->once()) ->method('get') - ->will( - $this->throwException( - new \Magento\Framework\Exception\NoSuchEntityException( - __("The product that was requested doesn't exist. Verify the product and try again.") - ) + ->willThrowException( + new NoSuchEntityException( + __("The product that was requested doesn't exist. Verify the product and try again.") ) ); + + $this->expectException(NoSuchEntityException::class); + $this->expectExceptionMessage( + "The product that was requested doesn't exist. Verify the product and try again." + ); + $this->model->setProductLinks($productSku, $links); } /** - * @expectedException \Magento\Framework\Exception\CouldNotSaveException - * @expectedExceptionMessage The linked products data is invalid. Verify the data and try again. + * Test setProductLnks() with invliad data exception + * + * @return void + * @throws CouldNotSaveException */ - public function testSetProductLinksInvalidDataException() + public function testSetProductLinksInvalidDataException(): void { - $productSku = 'Simple Product 1'; - $linkType = 'related'; - $this->productRepositoryMock->expects($this->once())->method('get')->with($productSku) + $productSku = self::STUB_PRODUCT_SKU_1; + $linkType = self::STUB_LINK_TYPE; + + $this->productRepositoryMock->expects($this->once()) + ->method('get') + ->with($productSku) ->willReturn($this->productMock); - $inputRelatedLink = $this->objectManager->getObject(\Magento\Catalog\Model\ProductLink\Link::class); - $inputRelatedLink->setProductSku($productSku); - $inputRelatedLink->setLinkType($linkType); - $inputRelatedLink->setData("sku", "bad sku"); - $inputRelatedLink->setData("type_id", "bad type"); - $inputRelatedLink->setPosition(0); - $links = [$inputRelatedLink]; + $links = $this->getInputRelatedLinkMock( + $productSku, + $linkType, + self::STUB_PRODUCT_SKU_2, + self::STUB_PRODUCT_TYPE + ); + + $this->getLinkTypesMock(); + + $this->productMock->expects($this->once()) + ->method('getProductLinks') + ->willReturn([]); + + $this->productRepositoryMock->expects($this->once()) + ->method('save') + ->willThrowException( + new CouldNotSaveException( + __("The linked products data is invalid. Verify the data and try again.") + ) + ); + + $this->expectException(CouldNotSaveException::class); + $this->expectExceptionMessage( + "The linked products data is invalid. Verify the data and try again." + ); + + $this->model->setProductLinks($productSku, $links); + } + + /** + * Mock for getLinkTypesMock + * + * @return void + */ + private function getLinkTypesMock(): void + { + $linkTypes = [ + 'related' => 1, + 'upsell' => 4, + 'crosssell' => 5, + 'associated' => 3 + ]; - $linkTypes = ['related' => 1, 'upsell' => 4, 'crosssell' => 5, 'associated' => 3]; $this->linkTypeProviderMock->expects($this->once()) ->method('getLinkTypes') ->willReturn($linkTypes); + } - $this->productMock->expects($this->once())->method('getProductLinks')->willReturn([]); - - $this->productRepositoryMock->expects($this->once())->method('save')->willThrowException(new \Exception()); - $this->model->setProductLinks($productSku, $links); + /** + * get inputRelatedLinkMock + * + * @param string $productSku1 + * @param string $linkType + * @param string $productSku2 + * @param string $typeId + * @return array + */ + private function getInputRelatedLinkMock( + string $productSku1, + string $linkType, + string $productSku2, + string $typeId + ) { + + $inputRelatedLinkMock = $this->objectManager->getObject(Link::class); + $inputRelatedLinkMock->setProductSku($productSku1); + $inputRelatedLinkMock->setLinkType($linkType); + $inputRelatedLinkMock->setData("sku", $productSku2); + $inputRelatedLinkMock->setData("type_id", $typeId); + $inputRelatedLinkMock->setPosition(0); + + return [$inputRelatedLinkMock]; } } diff --git a/app/code/Magento/Catalog/Test/Unit/Model/ResourceModel/Product/ActionTest.php b/app/code/Magento/Catalog/Test/Unit/Model/ResourceModel/Product/ActionTest.php new file mode 100644 index 0000000000000..0027fd9fecc11 --- /dev/null +++ b/app/code/Magento/Catalog/Test/Unit/Model/ResourceModel/Product/ActionTest.php @@ -0,0 +1,338 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ + +namespace Magento\Catalog\Test\Unit\Model\ResourceModel\Product; + +use Magento\Catalog\Api\Data\ProductAttributeInterface; +use Magento\Catalog\Api\Data\ProductInterface; +use Magento\Catalog\Model\Factory; +use Magento\Catalog\Model\Product; +use Magento\Catalog\Model\Product\Edit\WeightResolver; +use Magento\Catalog\Model\Product\Type; +use Magento\Catalog\Model\Product\TypeTransitionManager; +use Magento\Catalog\Model\ResourceModel\Product\Action; +use Magento\Catalog\Model\ResourceModel\Product\Collection as ProductCollection; +use Magento\Catalog\Model\ResourceModel\Product\CollectionFactory as ProductCollectionFactory; +use Magento\Eav\Model\Config; +use Magento\Eav\Model\Entity\Attribute\AbstractAttribute; +use Magento\Eav\Model\Entity\Attribute\UniqueValidationInterface; +use Magento\Eav\Model\Entity\Context; +use Magento\Eav\Model\Entity\Type as EntityType; +use Magento\Framework\App\ResourceConnection; +use Magento\Framework\DB\Adapter\AdapterInterface; +use Magento\Framework\Stdlib\DateTime\DateTime; +use Magento\Framework\TestFramework\Unit\Helper\ObjectManager; +use Magento\Store\Model\StoreManagerInterface; +use PHPUnit\Framework\MockObject\MockObject; +use PHPUnit\Framework\TestCase; + +/** + * @SuppressWarnings(PHPMD.CouplingBetweenObjects) + */ +class ActionTest extends TestCase +{ + private const ENTITY_IDS = [1, 2, 5, 10]; + + /** + * @var Action + */ + private $model; + + /** + * @var Context|MockObject + */ + private $contextMock; + + /** + * @var StoreManagerInterface|MockObject + */ + private $storeManagerMock; + + /** + * @var Factory|MockObject + */ + private $factoryMock; + + /** + * @var UniqueValidationInterface|MockObject + */ + private $uniqueValidatorMock; + + /** + * @var ProductCollectionFactory|MockObject + */ + private $productCollectionFactoryMock; + + /** + * @var TypeTransitionManager|MockObject + */ + private $typeTransitionManagerMock; + + /** + * @var DateTime|MockObject + */ + private $dateTimeMock; + + /** + * @var Config|MockObject + */ + private $eavConfigMock; + + /** + * @var ResourceConnection|MockObject + */ + private $resourceMock; + + /** + * @var EntityType|MockObject + */ + private $entityTypeMock; + + /** + * @var AdapterInterface|MockObject + */ + private $connectionMock; + + /** + * @var ProductCollection|MockObject + */ + private $productCollectionMock; + + protected function setUp() + { + $this->contextMock = $this->getMockBuilder(Context::class) + ->disableOriginalConstructor() + ->getMock(); + $this->storeManagerMock = $this->getMockBuilder(StoreManagerInterface::class) + ->getMockForAbstractClass(); + $this->factoryMock = $this->getMockBuilder(Factory::class) + ->disableOriginalConstructor() + ->getMock(); + $this->uniqueValidatorMock = $this->getMockBuilder(UniqueValidationInterface::class) + ->getMockForAbstractClass(); + $this->productCollectionFactoryMock = $this->getMockBuilder(ProductCollectionFactory::class) + ->disableOriginalConstructor() + ->getMock(); + $this->typeTransitionManagerMock = $this->createPartialMock( + TypeTransitionManager::class, + ['processProduct'] + ); + $this->dateTimeMock = $this->getMockBuilder(DateTime::class) + ->disableOriginalConstructor() + ->getMock(); + + $this->eavConfigMock = $this->getMockBuilder(Config::class) + ->disableOriginalConstructor() + ->getMock(); + $this->resourceMock = $this->getMockBuilder(ResourceConnection::class) + ->disableOriginalConstructor() + ->getMock(); + $this->entityTypeMock = $this->getMockBuilder(EntityType::class) + ->disableOriginalConstructor() + ->getMock(); + + $this->contextMock->method('getEavConfig') + ->willReturn($this->eavConfigMock); + $this->contextMock->method('getResource') + ->willReturn($this->resourceMock); + $this->eavConfigMock->method('getEntityType') + ->willReturn($this->entityTypeMock); + $updatedAtAttributeMock = $this->getMockBuilder(AbstractAttribute::class) + ->disableOriginalConstructor() + ->getMockForAbstractClass(); + $this->eavConfigMock->method('getAttribute') + ->willReturn($updatedAtAttributeMock); + + $objectManager = new ObjectManager($this); + $this->model = $objectManager->getObject( + Action::class, + [ + 'context' => $this->contextMock, + 'storeManager' => $this->storeManagerMock, + 'modelFactory' => $this->factoryMock, + 'uniqueValidator' => $this->uniqueValidatorMock, + 'dateTime' => $this->dateTimeMock, + 'productCollectionFactory' => $this->productCollectionFactoryMock, + 'typeTransitionManager' => $this->typeTransitionManagerMock, + 'data' => [] + ] + ); + } + + private function prepareAdapter() + { + $this->connectionMock = $this->getMockBuilder(AdapterInterface::class) + ->getMockForAbstractClass(); + $this->resourceMock->method('getConnection') + ->willReturn($this->connectionMock); + $this->resourceMock->method('getTableName') + ->willReturn('catalog_product_entity'); + } + + private function prepareProductCollection($items) + { + $this->productCollectionMock = $this->getMockBuilder(ProductCollection::class) + ->disableOriginalConstructor() + ->getMock(); + $this->productCollectionMock->method('addIdFilter') + ->with(static::ENTITY_IDS) + ->willReturnSelf(); + $this->productCollectionMock->method('addFieldToFilter') + ->willReturnSelf(); + $this->productCollectionMock->method('addFieldToSelect') + ->willReturnSelf(); + $this->productCollectionMock->method('getItems') + ->willReturn($items); + $this->productCollectionFactoryMock->method('create') + ->willReturn($this->productCollectionMock); + } + + /** + * @param int $hasWeight + * @param string $typeId + * @param Product[] $items + * @param int[] $entityIds + * @dataProvider updateProductHasWeightAttributesDataProvider + */ + public function testUpdateProductHasWeightAttributes($hasWeight, $typeId, $items, $entityIds) + { + $this->prepareAdapter(); + $this->prepareProductCollection($items); + $attrData = [ + ProductAttributeInterface::CODE_HAS_WEIGHT => $hasWeight + ]; + $storeId = 0; + + $this->connectionMock->expects($this->once()) + ->method('update') + ->with( + 'catalog_product_entity', + ['type_id' => $typeId], + ['entity_id IN (?)' => $entityIds] + ); + + $this->model->updateAttributes(static::ENTITY_IDS, $attrData, $storeId); + } + + /** + * Update Attributes data provider + * + * @return array + */ + public function updateProductHasWeightAttributesDataProvider() + { + return [ + [ + WeightResolver::HAS_WEIGHT, + Type::TYPE_SIMPLE, + $this->getProductsVirtualToSimple(), + static::ENTITY_IDS + ], + [ + WeightResolver::HAS_NO_WEIGHT, + Type::TYPE_VIRTUAL, + $this->getProductsSimpleToVirtual(), + static::ENTITY_IDS + ], + [ + WeightResolver::HAS_NO_WEIGHT, + Type::TYPE_VIRTUAL, + $this->getProductsMixedTypes(), + array_slice(static::ENTITY_IDS, 2, 2) + ] + ]; + } + + private function getProductsSimpleToVirtual() + { + $result = []; + + foreach (static::ENTITY_IDS as $entityId) { + $productMock = $this->getMockBuilder(Product::class) + ->disableOriginalConstructor() + ->getMock(); + $productMock->method('getId') + ->willReturn($entityId); + $productMock->expects($this->at(1)) + ->method('getTypeId') + ->willReturn(Type::TYPE_SIMPLE); + $productMock->expects($this->at(2)) + ->method('getTypeId') + ->willReturn(Type::TYPE_VIRTUAL); + $productMock->expects($this->at(3)) + ->method('getTypeId') + ->willReturn(Type::TYPE_VIRTUAL); + + $result[] = $productMock; + } + + return $result; + } + + private function getProductsVirtualToSimple() + { + $result = []; + + foreach (static::ENTITY_IDS as $entityId) { + $productMock = $this->getMockBuilder(Product::class) + ->disableOriginalConstructor() + ->getMock(); + $productMock->method('getId') + ->willReturn($entityId); + $productMock->expects($this->at(1)) + ->method('getTypeId') + ->willReturn(Type::TYPE_VIRTUAL); + $productMock->expects($this->at(2)) + ->method('getTypeId') + ->willReturn(Type::TYPE_SIMPLE); + $productMock->expects($this->at(3)) + ->method('getTypeId') + ->willReturn(Type::TYPE_SIMPLE); + + $result[] = $productMock; + } + + return $result; + } + + private function getProductsMixedTypes() + { + $result = []; + + $i = 0; + foreach (static::ENTITY_IDS as $entityId) { + $productMock = $this->getMockBuilder(Product::class) + ->disableOriginalConstructor() + ->getMock(); + $productMock->method('getId') + ->willReturn($entityId); + + if ($i < 2) { + $productMock->expects($this->at(1)) + ->method('getTypeId') + ->willReturn(Type::TYPE_SIMPLE); + $productMock->expects($this->at(2)) + ->method('getTypeId') + ->willReturn(Type::TYPE_SIMPLE); + } else { + $productMock->expects($this->at(1)) + ->method('getTypeId') + ->willReturn(Type::TYPE_SIMPLE); + $productMock->expects($this->at(2)) + ->method('getTypeId') + ->willReturn(Type::TYPE_VIRTUAL); + $productMock->expects($this->at(3)) + ->method('getTypeId') + ->willReturn(Type::TYPE_VIRTUAL); + } + + $result[] = $productMock; + $i++; + } + + return $result; + } +} diff --git a/app/code/Magento/Catalog/Test/Unit/Model/ResourceModel/Product/Indexer/LinkedProductSelectBuilderByIndexPriceTest.php b/app/code/Magento/Catalog/Test/Unit/Model/ResourceModel/Product/Indexer/LinkedProductSelectBuilderByIndexPriceTest.php index 6f3d8e1a84b17..945c61f44e99c 100644 --- a/app/code/Magento/Catalog/Test/Unit/Model/ResourceModel/Product/Indexer/LinkedProductSelectBuilderByIndexPriceTest.php +++ b/app/code/Magento/Catalog/Test/Unit/Model/ResourceModel/Product/Indexer/LinkedProductSelectBuilderByIndexPriceTest.php @@ -37,6 +37,21 @@ class LinkedProductSelectBuilderByIndexPriceTest extends \PHPUnit\Framework\Test */ private $baseSelectProcessorMock; + /** + * @var \Magento\Framework\Search\Request\IndexScopeResolverInterface|\PHPUnit\Framework\MockObject\MockObject + */ + private $indexScopeResolverMock; + + /** + * @var \Magento\Framework\Indexer\Dimension|\PHPUnit\Framework\MockObject\MockObject + */ + private $dimensionMock; + + /** + * @var \Magento\Framework\Indexer\DimensionFactory|\PHPUnit\Framework\MockObject\MockObject + */ + private $dimensionFactoryMock; + /** * @var \Magento\Catalog\Model\ResourceModel\Product\Indexer\LinkedProductSelectBuilderByIndexPrice */ @@ -85,6 +100,7 @@ protected function setUp() public function testBuild() { $productId = 10; + $storeId = 1; $metadata = $this->getMockBuilder(\Magento\Framework\EntityManager\EntityMetadataInterface::class) ->disableOriginalConstructor() ->getMockForAbstractClass(); @@ -108,6 +124,6 @@ public function testBuild() $metadata->expects($this->once())->method('getLinkField')->willReturn('row_id'); $this->resourceMock->expects($this->any())->method('getTableName'); $this->baseSelectProcessorMock->expects($this->once())->method('process')->willReturnSelf(); - $this->model->build($productId); + $this->model->build($productId, $storeId); } } diff --git a/app/code/Magento/Catalog/Test/Unit/Model/ResourceModel/ProductWebsiteAssignmentHandlerTest.php b/app/code/Magento/Catalog/Test/Unit/Model/ResourceModel/ProductWebsiteAssignmentHandlerTest.php new file mode 100644 index 0000000000000..664aa311008f2 --- /dev/null +++ b/app/code/Magento/Catalog/Test/Unit/Model/ResourceModel/ProductWebsiteAssignmentHandlerTest.php @@ -0,0 +1,101 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +declare(strict_types=1); +namespace Magento\Catalog\Test\Unit\Model\ResourceModel; + +use Magento\Catalog\Model\ResourceModel\Product\Website\Link; +use Magento\Catalog\Model\ResourceModel\ProductWebsiteAssignmentHandler; +use Magento\Framework\TestFramework\Unit\Helper\ObjectManager; +use PHPUnit\Framework\TestCase; + +class ProductWebsiteAssignmentHandlerTest extends TestCase +{ + /** + * @var ProductWebsiteAssignmentHandler + */ + protected $handler; + + /** + * @var Link|\PHPUnit_Framework_MockObject_MockObject + */ + protected $productLinkMock; + + protected function setUp() + { + $objectManager = new ObjectManager($this); + + $this->productLinkMock = $this->createPartialMock( + Link::class, + ['updateProductWebsite'] + ); + $this->handler = $objectManager->getObject( + ProductWebsiteAssignmentHandler::class, + [ + 'productLink' => $this->productLinkMock + ] + ); + } + + /** + * @param $actualData + * @param $expectedResult + * @dataProvider productWebsitesDataProvider + * @throws \Exception + */ + public function testUpdateProductWebsiteReturnValidResult($actualData, $expectedResult) + { + $this->productLinkMock->expects($this->any())->method('updateProductWebsite')->willReturn($expectedResult); + $this->assertEquals( + $actualData['entityData'], + $this->handler->execute($actualData['entityType'], $actualData['entityData']) + ); + } + + /** + * @return array + */ + public function productWebsitesDataProvider(): array + { + return [ + [ + [ + 'entityType' => 'product', + 'entityData' => [ + 'entity_id' => '12345', + 'website_ids' => ['1', '2', '3'], + 'name' => 'test-1', + 'sku' => 'test-1' + ] + ], + true + ], + [ + [ + 'entityType' => 'product', + 'entityData' => [ + 'entity_id' => null, + 'website_ids' => ['1', '2', '3'], + 'name' => 'test-1', + 'sku' => 'test-1' + ] + ], + false + ], + [ + [ + 'entityType' => 'product', + 'entityData' => [ + 'entity_id' => '12345', + 'website_ids' => [null], + 'name' => 'test-1', + 'sku' => 'test-1' + ] + ], + false + ] + ]; + } +} diff --git a/app/code/Magento/Catalog/Test/Unit/Observer/SetSpecialPriceStartDateTest.php b/app/code/Magento/Catalog/Test/Unit/Observer/SetSpecialPriceStartDateTest.php new file mode 100644 index 0000000000000..c44a64f1d7433 --- /dev/null +++ b/app/code/Magento/Catalog/Test/Unit/Observer/SetSpecialPriceStartDateTest.php @@ -0,0 +1,135 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ + +declare(strict_types=1); + +namespace Magento\Catalog\Test\Unit\Observer; + +use Magento\Catalog\Model\Product; +use Magento\Catalog\Observer\SetSpecialPriceStartDate; +use Magento\Framework\Event; +use Magento\Framework\Event\Observer; +use Magento\Framework\Stdlib\DateTime\Timezone; +use Magento\Framework\TestFramework\Unit\Helper\ObjectManager; +use PHPUnit\Framework\MockObject\MockObject; +use PHPUnit\Framework\TestCase; + +/** + * Unit test for \Magento\Catalog\Observer\SetSpecialPriceStartDate + */ +class SetSpecialPriceStartDateTest extends TestCase +{ + /** + * Testable Object + * + * @var SetSpecialPriceStartDate + */ + private $observer; + + /** + * @var ObjectManager + */ + private $objectManager; + + /** + * @var Observer|MockObject + */ + private $observerMock; + + /** + * @var Event|MockObject + */ + private $eventMock; + + /** + * @var Product|MockObject + */ + private $productMock; + + /** + * @var Timezone|MockObject + */ + private $timezone; + + /** + * @var \DateTime|MockObject + */ + private $dateObject; + + /** + * @inheritdoc + */ + protected function setUp(): void + { + $this->objectManager = new ObjectManager($this); + $this->observerMock = $this->createMock(Observer::class); + $this->timezone = $this->createMock(Timezone::class); + $this->dateObject = $this->createMock(\DateTime::class); + + $this->eventMock = $this->getMockBuilder(Event::class) + ->disableOriginalConstructor() + ->setMethods(['getProduct']) + ->getMock(); + + $this->productMock = $this->getMockBuilder(Product::class) + ->disableOriginalConstructor() + ->setMethods(['getSpecialPrice', 'getSpecialFromDate', 'setData']) + ->getMock(); + + $this->observer = $this->objectManager->getObject( + SetSpecialPriceStartDate::class, + [ + 'localeDate' => $this->timezone + ] + ); + } + + /** + * Test observer execute method + */ + public function testExecuteModifySpecialFromDate(): void + { + $specialPrice = 15; + $specialFromDate = null; + $localeDateMock = ['special_from_date' => $this->returnValue($this->dateObject)]; + + $this->observerMock + ->expects($this->once()) + ->method('getEvent') + ->willReturn($this->eventMock); + + $this->eventMock + ->expects($this->once()) + ->method('getProduct') + ->willReturn($this->productMock); + + $this->dateObject->expects($this->any()) + ->method('setTime') + ->willReturnSelf(); + + $this->timezone + ->expects($this->once()) + ->method('date') + ->willReturn($this->dateObject); + + $this->productMock + ->expects($this->once()) + ->method('getSpecialPrice') + ->willReturn($specialPrice); + + $this->productMock + ->expects($this->once()) + ->method('getSpecialFromDate') + ->willReturn($specialFromDate); + + $this->productMock + ->expects($this->once()) + ->method('setData') + ->willReturn($localeDateMock); + + $this->observer->execute($this->observerMock); + } +} diff --git a/app/code/Magento/Catalog/Test/Unit/Plugin/Model/ResourceModel/ConfigTest.php b/app/code/Magento/Catalog/Test/Unit/Plugin/Model/ResourceModel/ConfigTest.php index f36c934ca9acf..142de8fd1c5df 100644 --- a/app/code/Magento/Catalog/Test/Unit/Plugin/Model/ResourceModel/ConfigTest.php +++ b/app/code/Magento/Catalog/Test/Unit/Plugin/Model/ResourceModel/ConfigTest.php @@ -3,43 +3,59 @@ * Copyright © Magento, Inc. All rights reserved. * See COPYING.txt for license details. */ +declare(strict_types=1); namespace Magento\Catalog\Test\Unit\Plugin\Model\ResourceModel; +use Magento\Catalog\Model\ResourceModel\Config as ConfigResourceModel; use Magento\Catalog\Plugin\Model\ResourceModel\Config; +use Magento\Eav\Model\Cache\Type; +use Magento\Eav\Model\Entity\Attribute; +use Magento\Framework\App\Cache\StateInterface; +use Magento\Framework\App\CacheInterface; use Magento\Framework\Serialize\SerializerInterface; use Magento\Framework\TestFramework\Unit\Helper\ObjectManager; +use PHPUnit\Framework\MockObject\MockObject; +use PHPUnit\Framework\TestCase; -class ConfigTest extends \PHPUnit\Framework\TestCase +class ConfigTest extends TestCase { - /** @var \Magento\Framework\App\CacheInterface|\PHPUnit_Framework_MockObject_MockObject */ - private $cache; + /** + * @var CacheInterface|MockObject + */ + private $cacheMock; - /** @var \Magento\Framework\App\Cache\StateInterface|\PHPUnit_Framework_MockObject_MockObject */ - private $cacheState; + /** + * @var StateInterface|MockObject + */ + private $cacheStateMock; - /** @var SerializerInterface|\PHPUnit_Framework_MockObject_MockObject */ - private $serializer; + /** + * @var SerializerInterface|MockObject + */ + private $serializerMock; - /** @var \Magento\Catalog\Model\ResourceModel\Config|\PHPUnit_Framework_MockObject_MockObject */ - private $subject; + /** + * @var ConfigResourceModel|MockObject + */ + private $configResourceModelMock; protected function setUp() { - $this->cache = $this->createMock(\Magento\Framework\App\CacheInterface::class); - $this->cacheState = $this->createMock(\Magento\Framework\App\Cache\StateInterface::class); - $this->serializer = $this->createMock(SerializerInterface::class); - $this->subject = $this->createMock(\Magento\Catalog\Model\ResourceModel\Config::class); + $this->cacheMock = $this->createMock(CacheInterface::class); + $this->cacheStateMock = $this->createMock(StateInterface::class); + $this->serializerMock = $this->createMock(SerializerInterface::class); + $this->configResourceModelMock = $this->createMock(ConfigResourceModel::class); } public function testGetAttributesUsedInListingOnCacheDisabled() { - $this->cache->expects($this->never())->method('load'); + $this->cacheMock->expects($this->never())->method('load'); $this->assertEquals( ['attributes'], $this->getConfig(false)->aroundGetAttributesUsedInListing( - $this->subject, + $this->configResourceModelMock, $this->mockPluginProceed(['attributes']) ) ); @@ -51,13 +67,13 @@ public function testGetAttributesUsedInListingFromCache() $storeId = 'store'; $attributes = ['attributes']; $serializedAttributes = '["attributes"]'; - $this->subject->expects($this->any())->method('getEntityTypeId')->willReturn($entityTypeId); - $this->subject->expects($this->any())->method('getStoreId')->willReturn($storeId); - $cacheId = \Magento\Catalog\Plugin\Model\ResourceModel\Config::PRODUCT_LISTING_ATTRIBUTES_CACHE_ID + $this->configResourceModelMock->method('getEntityTypeId')->willReturn($entityTypeId); + $this->configResourceModelMock->method('getStoreId')->willReturn($storeId); + $cacheId = Config::PRODUCT_LISTING_ATTRIBUTES_CACHE_ID . $entityTypeId . '_' . $storeId; - $this->cache->expects($this->any())->method('load')->with($cacheId)->willReturn($serializedAttributes); - $this->serializer->expects($this->once()) + $this->cacheMock->method('load')->with($cacheId)->willReturn($serializedAttributes); + $this->serializerMock->expects($this->once()) ->method('unserialize') ->with($serializedAttributes) ->willReturn($attributes); @@ -65,7 +81,7 @@ public function testGetAttributesUsedInListingFromCache() $this->assertEquals( $attributes, $this->getConfig(true)->aroundGetAttributesUsedInListing( - $this->subject, + $this->configResourceModelMock, $this->mockPluginProceed() ) ); @@ -77,31 +93,31 @@ public function testGetAttributesUsedInListingWithCacheSave() $storeId = 'store'; $attributes = ['attributes']; $serializedAttributes = '["attributes"]'; - $this->subject->expects($this->any())->method('getEntityTypeId')->willReturn($entityTypeId); - $this->subject->expects($this->any())->method('getStoreId')->willReturn($storeId); - $cacheId = \Magento\Catalog\Plugin\Model\ResourceModel\Config::PRODUCT_LISTING_ATTRIBUTES_CACHE_ID + $this->configResourceModelMock->method('getEntityTypeId')->willReturn($entityTypeId); + $this->configResourceModelMock->method('getStoreId')->willReturn($storeId); + $cacheId = Config::PRODUCT_LISTING_ATTRIBUTES_CACHE_ID . $entityTypeId . '_' . $storeId; - $this->cache->expects($this->any())->method('load')->with($cacheId)->willReturn(false); - $this->serializer->expects($this->never()) + $this->cacheMock->method('load')->with($cacheId)->willReturn(false); + $this->serializerMock->expects($this->never()) ->method('unserialize'); - $this->serializer->expects($this->once()) + $this->serializerMock->expects($this->once()) ->method('serialize') ->with($attributes) ->willReturn($serializedAttributes); - $this->cache->expects($this->any())->method('save')->with( + $this->cacheMock->method('save')->with( $serializedAttributes, $cacheId, [ - \Magento\Eav\Model\Cache\Type::CACHE_TAG, - \Magento\Eav\Model\Entity\Attribute::CACHE_TAG + Type::CACHE_TAG, + Attribute::CACHE_TAG ] ); $this->assertEquals( $attributes, $this->getConfig(true)->aroundGetAttributesUsedInListing( - $this->subject, + $this->configResourceModelMock, $this->mockPluginProceed($attributes) ) ); @@ -109,12 +125,12 @@ public function testGetAttributesUsedInListingWithCacheSave() public function testGetAttributesUsedForSortByOnCacheDisabled() { - $this->cache->expects($this->never())->method('load'); + $this->cacheMock->expects($this->never())->method('load'); $this->assertEquals( ['attributes'], $this->getConfig(false)->aroundGetAttributesUsedForSortBy( - $this->subject, + $this->configResourceModelMock, $this->mockPluginProceed(['attributes']) ) ); @@ -126,12 +142,12 @@ public function testGetAttributesUsedForSortByFromCache() $storeId = 'store'; $attributes = ['attributes']; $serializedAttributes = '["attributes"]'; - $this->subject->expects($this->any())->method('getEntityTypeId')->willReturn($entityTypeId); - $this->subject->expects($this->any())->method('getStoreId')->willReturn($storeId); - $cacheId = \Magento\Catalog\Plugin\Model\ResourceModel\Config::PRODUCT_LISTING_SORT_BY_ATTRIBUTES_CACHE_ID + $this->configResourceModelMock->method('getEntityTypeId')->willReturn($entityTypeId); + $this->configResourceModelMock->method('getStoreId')->willReturn($storeId); + $cacheId = Config::PRODUCT_LISTING_SORT_BY_ATTRIBUTES_CACHE_ID . $entityTypeId . '_' . $storeId; - $this->cache->expects($this->any())->method('load')->with($cacheId)->willReturn($serializedAttributes); - $this->serializer->expects($this->once()) + $this->cacheMock->method('load')->with($cacheId)->willReturn($serializedAttributes); + $this->serializerMock->expects($this->once()) ->method('unserialize') ->with($serializedAttributes) ->willReturn($attributes); @@ -139,7 +155,7 @@ public function testGetAttributesUsedForSortByFromCache() $this->assertEquals( $attributes, $this->getConfig(true)->aroundGetAttributesUsedForSortBy( - $this->subject, + $this->configResourceModelMock, $this->mockPluginProceed() ) ); @@ -151,30 +167,30 @@ public function testGetAttributesUsedForSortByWithCacheSave() $storeId = 'store'; $attributes = ['attributes']; $serializedAttributes = '["attributes"]'; - $this->subject->expects($this->any())->method('getEntityTypeId')->willReturn($entityTypeId); - $this->subject->expects($this->any())->method('getStoreId')->willReturn($storeId); - $cacheId = \Magento\Catalog\Plugin\Model\ResourceModel\Config::PRODUCT_LISTING_SORT_BY_ATTRIBUTES_CACHE_ID + $this->configResourceModelMock->method('getEntityTypeId')->willReturn($entityTypeId); + $this->configResourceModelMock->method('getStoreId')->willReturn($storeId); + $cacheId = Config::PRODUCT_LISTING_SORT_BY_ATTRIBUTES_CACHE_ID . $entityTypeId . '_' . $storeId; - $this->cache->expects($this->any())->method('load')->with($cacheId)->willReturn(false); - $this->serializer->expects($this->never()) + $this->cacheMock->method('load')->with($cacheId)->willReturn(false); + $this->serializerMock->expects($this->never()) ->method('unserialize'); - $this->serializer->expects($this->once()) + $this->serializerMock->expects($this->once()) ->method('serialize') ->with($attributes) ->willReturn($serializedAttributes); - $this->cache->expects($this->any())->method('save')->with( + $this->cacheMock->method('save')->with( $serializedAttributes, $cacheId, [ - \Magento\Eav\Model\Cache\Type::CACHE_TAG, - \Magento\Eav\Model\Entity\Attribute::CACHE_TAG + Type::CACHE_TAG, + Attribute::CACHE_TAG ] ); $this->assertEquals( $attributes, $this->getConfig(true)->aroundGetAttributesUsedForSortBy( - $this->subject, + $this->configResourceModelMock, $this->mockPluginProceed($attributes) ) ); @@ -182,29 +198,33 @@ public function testGetAttributesUsedForSortByWithCacheSave() /** * @param bool $cacheEnabledFlag - * @return \Magento\Catalog\Plugin\Model\ResourceModel\Config + * + * @return Config */ protected function getConfig($cacheEnabledFlag) { - $this->cacheState->expects($this->any())->method('isEnabled') - ->with(\Magento\Eav\Model\Cache\Type::TYPE_IDENTIFIER)->willReturn($cacheEnabledFlag); + $this->cacheStateMock->method('isEnabled') + ->with(Type::TYPE_IDENTIFIER) + ->willReturn($cacheEnabledFlag); + return (new ObjectManager($this))->getObject( - \Magento\Catalog\Plugin\Model\ResourceModel\Config::class, + Config::class, [ - 'cache' => $this->cache, - 'cacheState' => $this->cacheState, - 'serializer' => $this->serializer, + 'cache' => $this->cacheMock, + 'cacheState' => $this->cacheStateMock, + 'serializer' => $this->serializerMock, ] ); } /** * @param mixed $returnValue + * * @return callable */ protected function mockPluginProceed($returnValue = null) { - return function () use ($returnValue) { + return static function () use ($returnValue) { return $returnValue; }; } diff --git a/app/code/Magento/Catalog/Test/Unit/Pricing/Price/CalculateCustomOptionCatalogRuleTest.php b/app/code/Magento/Catalog/Test/Unit/Pricing/Price/CalculateCustomOptionCatalogRuleTest.php new file mode 100644 index 0000000000000..1a99ac5d451f0 --- /dev/null +++ b/app/code/Magento/Catalog/Test/Unit/Pricing/Price/CalculateCustomOptionCatalogRuleTest.php @@ -0,0 +1,266 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +declare(strict_types=1); + +namespace Magento\Catalog\Test\Unit\Pricing\Price; + +use Magento\Catalog\Model\Product; +use Magento\Catalog\Model\Product\PriceModifier\Composite as PriceModifier; +use Magento\Catalog\Pricing\Price\CalculateCustomOptionCatalogRule; +use Magento\Catalog\Pricing\Price\RegularPrice; +use Magento\Catalog\Pricing\Price\SpecialPrice; +use Magento\CatalogRule\Pricing\Price\CatalogRulePrice; +use Magento\Directory\Model\PriceCurrency; +use Magento\Framework\Pricing\PriceInfo\Base; +use Magento\Framework\TestFramework\Unit\Helper\ObjectManager; +use PHPUnit\Framework\MockObject\MockObject; +use PHPUnit\Framework\TestCase; + +/** + * Test for CalculateCustomOptionCatalogRule class. + */ +class CalculateCustomOptionCatalogRuleTest extends TestCase +{ + /** + * @var Product|MockObject + */ + private $saleableItemMock; + + /** + * @var RegularPrice|MockObject + */ + private $regularPriceMock; + + /** + * @var SpecialPrice|MockObject + */ + private $specialPriceMock; + + /** + * @var CatalogRulePrice|MockObject + */ + private $catalogRulePriceMock; + + /** + * @var PriceModifier|MockObject + */ + private $priceModifierMock; + + /** + * @var CalculateCustomOptionCatalogRule + */ + private $calculateCustomOptionCatalogRule; + + /** + * @inheritdoc + */ + protected function setUp() + { + $objectManager = new ObjectManager($this); + $this->saleableItemMock = $this->createMock(Product::class); + $this->regularPriceMock = $this->createMock(RegularPrice::class); + $this->specialPriceMock = $this->createMock(SpecialPrice::class); + $this->catalogRulePriceMock = $this->createMock(CatalogRulePrice::class); + $priceInfoMock = $this->createMock(Base::class); + $this->saleableItemMock->expects($this->any()) + ->method('getPriceInfo') + ->willReturn($priceInfoMock); + $this->regularPriceMock->expects($this->any()) + ->method('getPriceCode') + ->willReturn(RegularPrice::PRICE_CODE); + $this->specialPriceMock->expects($this->any()) + ->method('getPriceCode') + ->willReturn(SpecialPrice::PRICE_CODE); + $this->catalogRulePriceMock->expects($this->any()) + ->method('getPriceCode') + ->willReturn(CatalogRulePrice::PRICE_CODE); + $priceInfoMock->expects($this->any()) + ->method('getPrices') + ->willReturn( + [ + 'regular_price' => $this->regularPriceMock, + 'special_price' => $this->specialPriceMock, + 'catalog_rule_price' => $this->catalogRulePriceMock + ] + ); + $priceInfoMock->expects($this->any()) + ->method('getPrice') + ->willReturnMap( + [ + ['regular_price', $this->regularPriceMock], + ['special_price', $this->specialPriceMock], + ['catalog_rule_price', $this->catalogRulePriceMock], + ] + ); + $priceCurrencyMock = $this->createMock(PriceCurrency::class); + $priceCurrencyMock->expects($this->any()) + ->method('convertAndRound') + ->willReturnArgument(0); + $this->priceModifierMock = $this->createMock(PriceModifier::class); + + $this->calculateCustomOptionCatalogRule = $objectManager->getObject( + CalculateCustomOptionCatalogRule::class, + [ + 'priceCurrency' => $priceCurrencyMock, + 'priceModifier' => $this->priceModifierMock, + ] + ); + } + + /** + * Tests correct option price calculation with different catalog rules and special prices combination. + * + * @dataProvider executeDataProvider + * @param array $prices + * @param float $catalogRulePriceModifier + * @param float $optionPriceValue + * @param bool $isPercent + * @param float $expectedResult + */ + public function testExecute( + array $prices, + float $catalogRulePriceModifier, + float $optionPriceValue, + bool $isPercent, + float $expectedResult + ) { + $this->regularPriceMock->expects($this->any()) + ->method('getValue') + ->willReturn($prices['regularPriceValue']); + $this->specialPriceMock->expects($this->any()) + ->method('getValue') + ->willReturn($prices['specialPriceValue']); + $this->priceModifierMock->expects($this->any()) + ->method('modifyPrice') + ->willReturnCallback( + function ($price) use ($catalogRulePriceModifier) { + return $price * $catalogRulePriceModifier; + } + ); + + $finalPrice = $this->calculateCustomOptionCatalogRule->execute( + $this->saleableItemMock, + $optionPriceValue, + $isPercent + ); + + $this->assertSame($expectedResult, $finalPrice); + } + + /** + * Data provider for testExecute. + * + * "Active" means this price type has biggest discount, so other prices doesn't count. + * + * @return array + * @SuppressWarnings(PHPMD.ExcessiveMethodLength) + */ + public function executeDataProvider(): array + { + return [ + 'No special price, no catalog price rules, fixed option price' => [ + 'prices' => [ + 'regularPriceValue' => 1000, + 'specialPriceValue' => 1000, + ], + 'catalogRulePriceModifier' => 1.0, + 'optionPriceValue' => 100.0, + 'isPercent' => false, + 'expectedResult' => 100.0 + ], + 'No special price, no catalog price rules, percent option price' => [ + 'prices' => [ + 'regularPriceValue' => 1000, + 'specialPriceValue' => 1000, + ], + 'catalogRulePriceModifier' => 1.0, + 'optionPriceValue' => 100.0, + 'isPercent' => true, + 'expectedResult' => 1000.0 + ], + 'No special price, catalog price rule set, fixed option price' => [ + 'prices' => [ + 'regularPriceValue' => 1000, + 'specialPriceValue' => 1000, + ], + 'catalogRulePriceModifier' => 0.9, + 'optionPriceValue' => 100.0, + 'isPercent' => false, + 'expectedResult' => 90.0 + ], + 'No special price, catalog price rule set, percent option price' => [ + 'prices' => [ + 'regularPriceValue' => 1000, + 'specialPriceValue' => 1000, + ], + 'catalogRulePriceModifier' => 0.9, + 'optionPriceValue' => 100.0, + 'isPercent' => true, + 'expectedResult' => 900.0 + ], + 'Special price set, no catalog price rule, fixed option price' => [ + 'prices' => [ + 'regularPriceValue' => 1000, + 'specialPriceValue' => 900, + ], + 'catalogRulePriceModifier' => 1.0, + 'optionPriceValue' => 100.0, + 'isPercent' => false, + 'expectedResult' => 100.0 + ], + 'Special price set, no catalog price rule, percent option price' => [ + 'prices' => [ + 'regularPriceValue' => 1000, + 'specialPriceValue' => 900, + ], + 'catalogRulePriceModifier' => 1.0, + 'optionPriceValue' => 100.0, + 'isPercent' => true, + 'expectedResult' => 900.0 + ], + 'Special price set and active, catalog price rule set, fixed option price' => [ + 'prices' => [ + 'regularPriceValue' => 1000, + 'specialPriceValue' => 800, + ], + 'catalogRulePriceModifier' => 0.9, + 'optionPriceValue' => 100.0, + 'isPercent' => false, + 'expectedResult' => 100.0 + ], + 'Special price set and active, catalog price rule set, percent option price' => [ + 'prices' => [ + 'regularPriceValue' => 1000, + 'specialPriceValue' => 800, + ], + 'catalogRulePriceModifier' => 0.9, + 'optionPriceValue' => 100.0, + 'isPercent' => true, + 'expectedResult' => 800.0 + ], + 'Special price set, catalog price rule set and active, fixed option price' => [ + 'prices' => [ + 'regularPriceValue' => 1000, + 'specialPriceValue' => 950, + ], + 'catalogRulePriceModifier' => 0.9, + 'optionPriceValue' => 100.0, + 'isPercent' => false, + 'expectedResult' => 90.0 + ], + 'Special price set, catalog price rule set and active, percent option price' => [ + 'prices' => [ + 'regularPriceValue' => 1000, + 'specialPriceValue' => 950, + ], + 'catalogRulePriceModifier' => 0.9, + 'optionPriceValue' => 100.0, + 'isPercent' => true, + 'expectedResult' => 900.0 + ], + ]; + } +} diff --git a/app/code/Magento/Catalog/Test/Unit/Ui/DataProvider/Product/Form/Modifier/WebsitesTest.php b/app/code/Magento/Catalog/Test/Unit/Ui/DataProvider/Product/Form/Modifier/WebsitesTest.php index 829dc4824416d..2f8545c56e71e 100644 --- a/app/code/Magento/Catalog/Test/Unit/Ui/DataProvider/Product/Form/Modifier/WebsitesTest.php +++ b/app/code/Magento/Catalog/Test/Unit/Ui/DataProvider/Product/Form/Modifier/WebsitesTest.php @@ -6,17 +6,16 @@ namespace Magento\Catalog\Test\Unit\Ui\DataProvider\Product\Form\Modifier; use Magento\Catalog\Ui\DataProvider\Product\Form\Modifier\Websites; -use Magento\Store\Api\WebsiteRepositoryInterface; use Magento\Store\Api\GroupRepositoryInterface; use Magento\Store\Api\StoreRepositoryInterface; +use Magento\Store\Api\WebsiteRepositoryInterface; +use Magento\Store\Model\Group; +use Magento\Store\Model\Store as StoreView; use Magento\Store\Model\StoreManagerInterface; -use Magento\Framework\TestFramework\Unit\Helper\ObjectManager; use Magento\Store\Model\Website; -use Magento\Store\Model\Store as StoreView; -use Magento\Store\Model\Group; /** - * Class WebsitesTest + * Class WebsitesTest test the meta data and website data for different websites * * @SuppressWarnings(PHPMD.CouplingBetweenObjects) */ @@ -111,7 +110,7 @@ protected function setUp() ->method('getWebsiteIds') ->willReturn($this->assignedWebsites); $this->storeManagerMock = $this->getMockBuilder(\Magento\Store\Model\StoreManagerInterface::class) - ->setMethods(['isSingleStoreMode', 'getWesites']) + ->setMethods(['isSingleStoreMode', 'getWebsites']) ->getMockForAbstractClass(); $this->storeManagerMock->method('getWebsites') ->willReturn([$this->websiteMock, $this->secondWebsiteMock]); @@ -182,6 +181,14 @@ public function testModifyMeta() $this->assertTrue(isset($meta['websites']['children'][self::SECOND_WEBSITE_ID])); $this->assertTrue(isset($meta['websites']['children'][self::WEBSITE_ID])); $this->assertTrue(isset($meta['websites']['children']['copy_to_stores.' . self::WEBSITE_ID])); + $this->assertEquals( + $meta['websites']['children'][self::SECOND_WEBSITE_ID]['arguments']['data']['config']['value'], + (string) self::SECOND_WEBSITE_ID + ); + $this->assertEquals( + $meta['websites']['children'][self::WEBSITE_ID]['arguments']['data']['config']['value'], + "0" + ); } /** diff --git a/app/code/Magento/Catalog/Test/Unit/Ui/DataProvider/Product/ProductCustomOptionsDataProviderTest.php b/app/code/Magento/Catalog/Test/Unit/Ui/DataProvider/Product/ProductCustomOptionsDataProviderTest.php index 0e0cb676cdf3e..143ef4461173a 100644 --- a/app/code/Magento/Catalog/Test/Unit/Ui/DataProvider/Product/ProductCustomOptionsDataProviderTest.php +++ b/app/code/Magento/Catalog/Test/Unit/Ui/DataProvider/Product/ProductCustomOptionsDataProviderTest.php @@ -11,6 +11,9 @@ use Magento\Framework\App\RequestInterface; use Magento\Framework\Model\ResourceModel\Db\Collection\AbstractCollection; use Magento\Framework\DB\Select as DbSelect; +use Magento\Framework\EntityManager\MetadataPool; +use Magento\Framework\EntityManager\EntityMetadataInterface; +use Magento\Ui\DataProvider\Modifier\PoolInterface; class ProductCustomOptionsDataProviderTest extends \PHPUnit\Framework\TestCase { @@ -44,6 +47,21 @@ class ProductCustomOptionsDataProviderTest extends \PHPUnit\Framework\TestCase */ protected $dbSelectMock; + /** + * @var MetadataPool|\PHPUnit_Framework_MockObject_MockObject + */ + private $metadataPool; + + /** + * @var EntityMetadataInterface|\PHPUnit_Framework_MockObject_MockObject + */ + private $entityMetadata; + + /** + * @var PoolInterface|\PHPUnit_Framework_MockObject_MockObject + */ + private $modifiersPool; + protected function setUp() { $this->collectionFactoryMock = $this->getMockBuilder(CollectionFactory::class) @@ -73,12 +91,29 @@ protected function setUp() ->method('create') ->willReturn($this->collectionMock); + $this->modifiersPool = $this->getMockBuilder(PoolInterface::class) + ->getMockForAbstractClass(); + $this->entityMetadata = $this->getMockBuilder(EntityMetadataInterface::class) + ->getMockForAbstractClass(); + $this->entityMetadata->expects($this->any()) + ->method('getLinkField') + ->willReturn('entity_id'); + $this->metadataPool = $this->getMockBuilder(MetadataPool::class) + ->disableOriginalConstructor() + ->setMethods(['getMetadata']) + ->getMock(); + $this->metadataPool->expects($this->any()) + ->method('getMetadata') + ->willReturn($this->entityMetadata); + $this->objectManagerHelper = new ObjectManagerHelper($this); $this->dataProvider = $this->objectManagerHelper->getObject( ProductCustomOptionsDataProvider::class, [ 'collectionFactory' => $this->collectionFactoryMock, - 'request' => $this->requestMock + 'request' => $this->requestMock, + 'modifiersPool' => $this->modifiersPool, + 'metadataPool' => $this->metadataPool ] ); } diff --git a/app/code/Magento/Catalog/Test/Unit/ViewModel/Product/BreadcrumbsTest.php b/app/code/Magento/Catalog/Test/Unit/ViewModel/Product/BreadcrumbsTest.php index a442041660893..91bb534fff627 100644 --- a/app/code/Magento/Catalog/Test/Unit/ViewModel/Product/BreadcrumbsTest.php +++ b/app/code/Magento/Catalog/Test/Unit/ViewModel/Product/BreadcrumbsTest.php @@ -11,65 +11,72 @@ use Magento\Catalog\Model\Product; use Magento\Catalog\ViewModel\Product\Breadcrumbs; use Magento\Framework\App\Config\ScopeConfigInterface; +use Magento\Framework\Escaper; use Magento\Framework\TestFramework\Unit\Helper\ObjectManager; use Magento\Framework\Serialize\Serializer\JsonHexTag; +use Magento\Store\Model\ScopeInterface; +use PHPUnit\Framework\MockObject\MockObject; +use PHPUnit\Framework\TestCase; /** * Unit test for Magento\Catalog\ViewModel\Product\Breadcrumbs. */ -class BreadcrumbsTest extends \PHPUnit\Framework\TestCase +class BreadcrumbsTest extends TestCase { + private const XML_PATH_CATEGORY_URL_SUFFIX = 'catalog/seo/category_url_suffix'; + private const XML_PATH_PRODUCT_USE_CATEGORIES = 'catalog/seo/product_use_categories'; + /** * @var Breadcrumbs */ private $viewModel; /** - * @var CatalogHelper|\PHPUnit_Framework_MockObject_MockObject + * @var ObjectManager */ - private $catalogHelper; + private $objectManager; /** - * @var ScopeConfigInterface|\PHPUnit_Framework_MockObject_MockObject + * @var CatalogHelper|MockObject */ - private $scopeConfig; + private $catalogHelperMock; /** - * @var ObjectManager + * @var ScopeConfigInterface|MockObject */ - private $objectManager; + private $scopeConfigMock; /** - * @var JsonHexTag|\PHPUnit_Framework_MockObject_MockObject + * @var JsonHexTag|MockObject */ - private $serializer; + private $serializerMock; /** * @inheritdoc */ protected function setUp() : void { - $this->catalogHelper = $this->getMockBuilder(CatalogHelper::class) + $this->catalogHelperMock = $this->getMockBuilder(CatalogHelper::class) ->setMethods(['getProduct']) ->disableOriginalConstructor() ->getMock(); - $this->scopeConfig = $this->getMockBuilder(ScopeConfigInterface::class) + $this->scopeConfigMock = $this->getMockBuilder(ScopeConfigInterface::class) ->setMethods(['getValue', 'isSetFlag']) ->disableOriginalConstructor() ->getMockForAbstractClass(); - $escaper = $this->getObjectManager()->getObject(\Magento\Framework\Escaper::class); + $escaper = $this->getObjectManager()->getObject(Escaper::class); - $this->serializer = $this->createMock(JsonHexTag::class); + $this->serializerMock = $this->createMock(JsonHexTag::class); $this->viewModel = $this->getObjectManager()->getObject( Breadcrumbs::class, [ - 'catalogData' => $this->catalogHelper, - 'scopeConfig' => $this->scopeConfig, + 'catalogData' => $this->catalogHelperMock, + 'scopeConfig' => $this->scopeConfigMock, 'escaper' => $escaper, - 'jsonSerializer' => $this->serializer + 'jsonSerializer' => $this->serializerMock ] ); } @@ -79,9 +86,9 @@ protected function setUp() : void */ public function testGetCategoryUrlSuffix() : void { - $this->scopeConfig->expects($this->once()) + $this->scopeConfigMock->expects($this->once()) ->method('getValue') - ->with('catalog/seo/category_url_suffix', \Magento\Store\Model\ScopeInterface::SCOPE_STORE) + ->with(static::XML_PATH_CATEGORY_URL_SUFFIX, ScopeInterface::SCOPE_STORE) ->willReturn('.html'); $this->assertEquals('.html', $this->viewModel->getCategoryUrlSuffix()); @@ -92,9 +99,9 @@ public function testGetCategoryUrlSuffix() : void */ public function testIsCategoryUsedInProductUrl() : void { - $this->scopeConfig->expects($this->once()) + $this->scopeConfigMock->expects($this->once()) ->method('isSetFlag') - ->with('catalog/seo/product_use_categories', \Magento\Store\Model\ScopeInterface::SCOPE_STORE) + ->with(static::XML_PATH_PRODUCT_USE_CATEGORIES, ScopeInterface::SCOPE_STORE) ->willReturn(false); $this->assertFalse($this->viewModel->isCategoryUsedInProductUrl()); @@ -105,11 +112,12 @@ public function testIsCategoryUsedInProductUrl() : void * * @param Product|null $product * @param string $expectedName + * * @return void */ public function testGetProductName($product, string $expectedName) : void { - $this->catalogHelper->expects($this->atLeastOnce()) + $this->catalogHelperMock->expects($this->atLeastOnce()) ->method('getProduct') ->willReturn($product); @@ -132,27 +140,26 @@ public function productDataProvider() : array * * @param Product|null $product * @param string $expectedJson + * * @return void */ - public function testGetJsonConfiguration($product, string $expectedJson) : void + public function testGetJsonConfigurationHtmlEscaped($product, string $expectedJson) : void { - $this->catalogHelper->expects($this->atLeastOnce()) + $this->catalogHelperMock->expects($this->atLeastOnce()) ->method('getProduct') ->willReturn($product); - $this->scopeConfig->expects($this->any()) - ->method('isSetFlag') - ->with('catalog/seo/product_use_categories', \Magento\Store\Model\ScopeInterface::SCOPE_STORE) + $this->scopeConfigMock->method('isSetFlag') + ->with(static::XML_PATH_PRODUCT_USE_CATEGORIES, ScopeInterface::SCOPE_STORE) ->willReturn(false); - $this->scopeConfig->expects($this->any()) - ->method('getValue') - ->with('catalog/seo/category_url_suffix', \Magento\Store\Model\ScopeInterface::SCOPE_STORE) + $this->scopeConfigMock->method('getValue') + ->with(static::XML_PATH_CATEGORY_URL_SUFFIX, ScopeInterface::SCOPE_STORE) ->willReturn('."html'); - $this->serializer->expects($this->once())->method('serialize')->willReturn($expectedJson); + $this->serializerMock->expects($this->once())->method('serialize')->willReturn($expectedJson); - $this->assertEquals($expectedJson, $this->viewModel->getJsonConfiguration()); + $this->assertEquals($expectedJson, $this->viewModel->getJsonConfigurationHtmlEscaped()); } /** diff --git a/app/code/Magento/Catalog/Ui/Component/Listing/Columns/AttributeSetId.php b/app/code/Magento/Catalog/Ui/Component/Listing/Columns/AttributeSetId.php new file mode 100644 index 0000000000000..5e9f7ba065be7 --- /dev/null +++ b/app/code/Magento/Catalog/Ui/Component/Listing/Columns/AttributeSetId.php @@ -0,0 +1,39 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +declare(strict_types=1); + +namespace Magento\Catalog\Ui\Component\Listing\Columns; + +/** + * Attribute set listing column component + */ +class AttributeSetId extends \Magento\Ui\Component\Listing\Columns\Column +{ + /** + * @inheritDoc + */ + protected function applySorting() + { + $sorting = $this->getContext()->getRequestParam('sorting'); + $isSortable = $this->getData('config/sortable'); + if ($isSortable !== false + && !empty($sorting['field']) + && !empty($sorting['direction']) + && $sorting['field'] === $this->getName() + ) { + $collection = $this->getContext()->getDataProvider()->getCollection(); + $collection->joinField( + 'attribute_set', + 'eav_attribute_set', + 'attribute_set_name', + 'attribute_set_id=attribute_set_id', + null, + 'left' + ); + $collection->getSelect()->order('attribute_set_name ' . $sorting['direction']); + } + } +} diff --git a/app/code/Magento/Catalog/Ui/Component/Listing/Columns/Websites.php b/app/code/Magento/Catalog/Ui/Component/Listing/Columns/Websites.php index 494b77724e5b7..c80b2663d1f69 100644 --- a/app/code/Magento/Catalog/Ui/Component/Listing/Columns/Websites.php +++ b/app/code/Magento/Catalog/Ui/Component/Listing/Columns/Websites.php @@ -3,7 +3,6 @@ * Copyright © Magento, Inc. All rights reserved. * See COPYING.txt for license details. */ - declare(strict_types=1); namespace Magento\Catalog\Ui\Component\Listing\Columns; @@ -120,31 +119,32 @@ protected function applySorting() && !empty($sorting['direction']) && $sorting['field'] === $this->getName() ) { + /** @var \Magento\Framework\Model\ResourceModel\Db\Collection\AbstractCollection $collection */ $collection = $this->getContext()->getDataProvider()->getCollection(); - $collection - ->joinField( - 'websites_ids', - 'catalog_product_website', - 'website_id', - 'product_id=entity_id', - null, - 'left' - ) - ->joinTable( - 'store_website', - 'website_id = websites_ids', - ['name'], - null, - 'left' - ) - ->groupByAttribute('entity_id'); - $this->resourceHelper->addGroupConcatColumn( - $collection->getSelect(), - $this->websiteNames, - 'name' + + $select = $collection->getConnection()->select(); + $select->from( + ['cpw' => $collection->getTable('catalog_product_website')], + ['product_id'] + )->joinLeft( + ['sw' => $collection->getTable('store_website')], + 'cpw.website_id = sw.website_id', + [ + $this->websiteNames => new \Zend_Db_Expr( + 'GROUP_CONCAT(sw.name ORDER BY sw.website_id ASC SEPARATOR \',\')' + ) + ] + )->group( + 'cpw.product_id' ); - $collection->getSelect()->order($this->websiteNames . ' ' . $sorting['direction']); + $collection->getSelect()->joinLeft( + ['product_websites' => $select], + 'product_websites.product_id = e.entity_id', + [$this->websiteNames] + )->order( + 'product_websites.' . $this->websiteNames . ' ' . $sorting['direction'] + ); } } } diff --git a/app/code/Magento/Catalog/Ui/DataProvider/Product/Form/Modifier/Websites.php b/app/code/Magento/Catalog/Ui/DataProvider/Product/Form/Modifier/Websites.php index b9d8fc56a91d9..de204b312a3fd 100644 --- a/app/code/Magento/Catalog/Ui/DataProvider/Product/Form/Modifier/Websites.php +++ b/app/code/Magento/Catalog/Ui/DataProvider/Product/Form/Modifier/Websites.php @@ -6,12 +6,12 @@ namespace Magento\Catalog\Ui\DataProvider\Product\Form\Modifier; use Magento\Catalog\Model\Locator\LocatorInterface; -use Magento\Store\Model\StoreManagerInterface; -use Magento\Store\Api\WebsiteRepositoryInterface; use Magento\Store\Api\GroupRepositoryInterface; use Magento\Store\Api\StoreRepositoryInterface; -use Magento\Ui\Component\Form; +use Magento\Store\Api\WebsiteRepositoryInterface; +use Magento\Store\Model\StoreManagerInterface; use Magento\Ui\Component\DynamicRows; +use Magento\Ui\Component\Form; /** * Class Websites customizes websites panel @@ -211,6 +211,30 @@ protected function getFieldsForFieldset() } } + $children = $this->setDefaultWebsiteIdIfNoneAreSelected($children); + return $children; + } + + /** + * Set default website id if none are selected + * + * @param array $children + * @return array + */ + private function setDefaultWebsiteIdIfNoneAreSelected(array $children):array + { + $websitesList = $this->getWebsitesList(); + $defaultSelectedWebsite = false; + foreach ($websitesList as $website) { + if ($children[$website['id']]['arguments']['data']['config']['value']) { + $defaultSelectedWebsite = true; + break; + } + } + if (count($websitesList) === 1 && !$defaultSelectedWebsite) { + $website = reset($websitesList); + $children[$website['id']]['arguments']['data']['config']['value'] = (string)$website['id']; + } return $children; } diff --git a/app/code/Magento/Catalog/Ui/DataProvider/Product/Listing/DataProvider.php b/app/code/Magento/Catalog/Ui/DataProvider/Product/Listing/DataProvider.php index 4de0b94d06801..3289e4806df3a 100644 --- a/app/code/Magento/Catalog/Ui/DataProvider/Product/Listing/DataProvider.php +++ b/app/code/Magento/Catalog/Ui/DataProvider/Product/Listing/DataProvider.php @@ -8,12 +8,14 @@ use Magento\Framework\Api\FilterBuilder; use Magento\Framework\Api\Search\SearchCriteriaBuilder; +use Magento\Framework\App\Config\ScopeConfigInterface; use Magento\Framework\App\RequestInterface; use Magento\Framework\View\Element\UiComponent\DataProvider\Reporting; use Magento\Store\Model\StoreManager; /** * Provide information about current store and currency for product listing ui component + * @SuppressWarnings(PHPMD.CouplingBetweenObjects) */ class DataProvider extends \Magento\Framework\View\Element\UiComponent\DataProvider\DataProvider { @@ -22,6 +24,13 @@ class DataProvider extends \Magento\Framework\View\Element\UiComponent\DataProvi */ private $storeManager; + /** + * Core store config + * + * @var ScopeConfigInterface + */ + private $scopeConfig; + /** * @param string $name * @param Reporting $reporting @@ -56,6 +65,7 @@ public function __construct( $this->name = $name; $this->storeManager = $storeManager; + $this->scopeConfig = $data['config']['scopeConfig']; } /** @@ -65,9 +75,13 @@ public function getData() { $data = []; $store = $this->storeManager->getStore(); + $productsScope = $this->scopeConfig->getValue( + 'catalog/recently_products/scope', + \Magento\Store\Model\ScopeInterface::SCOPE_WEBSITE + ); $data['store'] = $store->getId(); $data['currency'] = $store->getCurrentCurrency()->getCode(); - + $data['productCurrentScope'] = $productsScope; return $data; } } diff --git a/app/code/Magento/Catalog/Ui/DataProvider/Product/Modifier/PriceAttributes.php b/app/code/Magento/Catalog/Ui/DataProvider/Product/Modifier/PriceAttributes.php new file mode 100644 index 0000000000000..7f333441dab34 --- /dev/null +++ b/app/code/Magento/Catalog/Ui/DataProvider/Product/Modifier/PriceAttributes.php @@ -0,0 +1,105 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +declare(strict_types=1); + +namespace Magento\Catalog\Ui\DataProvider\Product\Modifier; + +use Magento\Framework\Currency; +use Magento\Framework\Exception\NoSuchEntityException; +use Magento\Framework\Locale\CurrencyInterface; +use Magento\Store\Api\Data\StoreInterface; +use Magento\Store\Model\StoreManagerInterface; +use Magento\Ui\DataProvider\Modifier\ModifierInterface; + +/** + * Modify product listing price attributes + */ +class PriceAttributes implements ModifierInterface +{ + /** + * @var array + */ + private $priceAttributeList; + + /** + * @var StoreManagerInterface + */ + private $storeManager; + + /** + * @var CurrencyInterface + */ + private $localeCurrency; + + /** + * PriceAttributes constructor. + * + * @param StoreManagerInterface $storeManager + * @param CurrencyInterface $localeCurrency + * @param array $priceAttributeList + */ + public function __construct( + StoreManagerInterface $storeManager, + CurrencyInterface $localeCurrency, + array $priceAttributeList = [] + ) { + $this->storeManager = $storeManager; + $this->localeCurrency = $localeCurrency; + $this->priceAttributeList = $priceAttributeList; + } + + /** + * @inheritdoc + */ + public function modifyData(array $data): array + { + if (empty($data) || empty($this->priceAttributeList)) { + return $data; + } + + foreach ($data['items'] as &$item) { + foreach ($this->priceAttributeList as $priceAttribute) { + if (isset($item[$priceAttribute])) { + $item[$priceAttribute] = $this->getCurrency()->toCurrency(sprintf("%f", $item[$priceAttribute])); + } + } + } + + return $data; + } + + /** + * @inheritdoc + */ + public function modifyMeta(array $meta): array + { + return $meta; + } + + /** + * Retrieve store + * + * @return StoreInterface + * @throws NoSuchEntityException + */ + private function getStore(): StoreInterface + { + return $this->storeManager->getStore(); + } + + /** + * Retrieve currency + * + * @return Currency + * @throws NoSuchEntityException + */ + private function getCurrency(): Currency + { + $baseCurrencyCode = $this->getStore()->getBaseCurrencyCode(); + + return $this->localeCurrency->getCurrency($baseCurrencyCode); + } +} diff --git a/app/code/Magento/Catalog/Ui/DataProvider/Product/ProductCustomOptionsDataProvider.php b/app/code/Magento/Catalog/Ui/DataProvider/Product/ProductCustomOptionsDataProvider.php index b8e9155b8eab1..6de75e79b56da 100644 --- a/app/code/Magento/Catalog/Ui/DataProvider/Product/ProductCustomOptionsDataProvider.php +++ b/app/code/Magento/Catalog/Ui/DataProvider/Product/ProductCustomOptionsDataProvider.php @@ -12,6 +12,9 @@ use Magento\Catalog\Api\Data\ProductInterface; use Magento\Catalog\Model\Product\Option as ProductOption; use Magento\Framework\DataObject; +use Magento\Framework\EntityManager\MetadataPool; +use Magento\Framework\App\ObjectManager; +use Magento\Ui\DataProvider\Modifier\PoolInterface; /** * DataProvider for grid on Import Custom Options modal panel @@ -39,6 +42,11 @@ class ProductCustomOptionsDataProvider extends ProductDataProvider */ protected $productOptionValueModel; + /** + * @var MetadataPool + */ + private $metadataPool; + /** * @param string $name * @param string $primaryFieldName @@ -51,6 +59,8 @@ class ProductCustomOptionsDataProvider extends ProductDataProvider * @param \Magento\Ui\DataProvider\AddFilterToCollectionInterface[] $addFilterStrategies * @param array $meta * @param array $data + * @param PoolInterface|null $modifiersPool + * @param MetadataPool|null $metadataPool * @SuppressWarnings(PHPMD.ExcessiveParameterList) */ public function __construct( @@ -64,7 +74,9 @@ public function __construct( array $addFieldStrategies = [], array $addFilterStrategies = [], array $meta = [], - array $data = [] + array $data = [], + PoolInterface $modifiersPool = null, + MetadataPool $metadataPool = null ) { parent::__construct( $name, @@ -74,16 +86,19 @@ public function __construct( $addFieldStrategies, $addFilterStrategies, $meta, - $data + $data, + $modifiersPool ); $this->request = $request; $this->productOptionRepository = $productOptionRepository; $this->productOptionValueModel = $productOptionValueModel; + $this->metadataPool = $metadataPool ?: ObjectManager::getInstance() + ->get(MetadataPool::class); } /** - * {@inheritdoc} + * @inheritdoc * @since 101.0.0 */ public function getData() @@ -95,9 +110,16 @@ public function getData() $this->getCollection()->getSelect()->where('e.entity_id != ?', $currentProductId); } + try { + $entityMetadata = $this->metadataPool->getMetadata(ProductInterface::class); + $linkField = $entityMetadata->getLinkField(); + } catch (\Exception $e) { + $linkField = 'entity_id'; + } + $this->getCollection()->getSelect()->distinct()->join( ['opt' => $this->getCollection()->getTable('catalog_product_option')], - 'opt.product_id = e.entity_id', + 'opt.product_id = e.' . $linkField, null ); $this->getCollection()->load(); diff --git a/app/code/Magento/Catalog/ViewModel/Category/Image.php b/app/code/Magento/Catalog/ViewModel/Category/Image.php new file mode 100644 index 0000000000000..2982779bd2eb3 --- /dev/null +++ b/app/code/Magento/Catalog/ViewModel/Category/Image.php @@ -0,0 +1,46 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +declare(strict_types=1); + +namespace Magento\Catalog\ViewModel\Category; + +use Magento\Catalog\Model\Category; +use Magento\Catalog\Model\Category\Image as CategoryImage; +use Magento\Framework\View\Element\Block\ArgumentInterface; + +/** + * Category image view model + */ +class Image implements ArgumentInterface +{ + private const ATTRIBUTE_NAME = 'image'; + /** + * @var CategoryImage + */ + private $image; + + /** + * Initialize dependencies. + * + * @param CategoryImage $image + */ + public function __construct(CategoryImage $image) + { + $this->image = $image; + } + + /** + * Resolve category image URL + * + * @param Category $category + * @param string $attributeCode + * @return string + */ + public function getUrl(Category $category, string $attributeCode = self::ATTRIBUTE_NAME): string + { + return $this->image->getUrl($category, $attributeCode); + } +} diff --git a/app/code/Magento/Catalog/ViewModel/Category/Output.php b/app/code/Magento/Catalog/ViewModel/Category/Output.php new file mode 100644 index 0000000000000..367d59daea48e --- /dev/null +++ b/app/code/Magento/Catalog/ViewModel/Category/Output.php @@ -0,0 +1,46 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +declare(strict_types=1); + +namespace Magento\Catalog\ViewModel\Category; + +use Magento\Catalog\Helper\Output as OutputHelper; +use Magento\Catalog\Model\Category; +use Magento\Framework\View\Element\Block\ArgumentInterface; + +/** + * Category attribute output view model + */ +class Output implements ArgumentInterface +{ + /** + * @var OutputHelper + */ + private $outputHelper; + + /** + * Initialize dependencies. + * + * @param OutputHelper $outputHelper + */ + public function __construct(OutputHelper $outputHelper) + { + $this->outputHelper = $outputHelper; + } + + /** + * Prepare category attribute html output + * + * @param Category $category + * @param string $attributeHtml + * @param string $attributeName + * @return string + */ + public function categoryAttribute(Category $category, string $attributeHtml, string $attributeName): string + { + return $this->outputHelper->categoryAttribute($category, $attributeHtml, $attributeName); + } +} diff --git a/app/code/Magento/Catalog/ViewModel/Product/Breadcrumbs.php b/app/code/Magento/Catalog/ViewModel/Product/Breadcrumbs.php index 1aad46fc1e2f5..d3c8c406ee34d 100644 --- a/app/code/Magento/Catalog/ViewModel/Product/Breadcrumbs.php +++ b/app/code/Magento/Catalog/ViewModel/Product/Breadcrumbs.php @@ -3,26 +3,27 @@ * Copyright © Magento, Inc. All rights reserved. * See COPYING.txt for license details. */ +declare(strict_types=1); namespace Magento\Catalog\ViewModel\Product; use Magento\Catalog\Helper\Data; use Magento\Framework\App\Config\ScopeConfigInterface; -use Magento\Framework\App\ObjectManager; use Magento\Framework\DataObject; -use Magento\Framework\Serialize\Serializer\Json; use Magento\Framework\Serialize\Serializer\JsonHexTag; use Magento\Framework\View\Element\Block\ArgumentInterface; use Magento\Framework\Escaper; +use Magento\Store\Model\ScopeInterface; /** * Product breadcrumbs view model. */ class Breadcrumbs extends DataObject implements ArgumentInterface { + private const XML_PATH_CATEGORY_URL_SUFFIX = 'catalog/seo/category_url_suffix'; + private const XML_PATH_PRODUCT_USE_CATEGORIES = 'catalog/seo/product_use_categories'; + /** - * Catalog data. - * * @var Data */ private $catalogData; @@ -45,24 +46,21 @@ class Breadcrumbs extends DataObject implements ArgumentInterface /** * @param Data $catalogData * @param ScopeConfigInterface $scopeConfig - * @param Json|null $json - * @param Escaper|null $escaper - * @param JsonHexTag|null $jsonSerializer - * @SuppressWarnings(PHPMD.UnusedFormalParameter) + * @param Escaper $escaper + * @param JsonHexTag $jsonSerializer */ public function __construct( Data $catalogData, ScopeConfigInterface $scopeConfig, - Json $json = null, - Escaper $escaper = null, - JsonHexTag $jsonSerializer = null + Escaper $escaper, + JsonHexTag $jsonSerializer ) { parent::__construct(); $this->catalogData = $catalogData; $this->scopeConfig = $scopeConfig; - $this->escaper = $escaper ?: ObjectManager::getInstance()->get(Escaper::class); - $this->jsonSerializer = $jsonSerializer ?: ObjectManager::getInstance()->get(JsonHexTag::class); + $this->escaper = $escaper; + $this->jsonSerializer = $jsonSerializer; } /** @@ -73,8 +71,8 @@ public function __construct( public function getCategoryUrlSuffix() { return $this->scopeConfig->getValue( - 'catalog/seo/category_url_suffix', - \Magento\Store\Model\ScopeInterface::SCOPE_STORE + static::XML_PATH_CATEGORY_URL_SUFFIX, + ScopeInterface::SCOPE_STORE ); } @@ -86,8 +84,8 @@ public function getCategoryUrlSuffix() public function isCategoryUsedInProductUrl(): bool { return $this->scopeConfig->isSetFlag( - 'catalog/seo/product_use_categories', - \Magento\Store\Model\ScopeInterface::SCOPE_STORE + static::XML_PATH_PRODUCT_USE_CATEGORIES, + ScopeInterface::SCOPE_STORE ); } @@ -108,7 +106,7 @@ public function getProductName(): string * * @return string */ - public function getJsonConfigurationHtmlEscaped() : string + public function getJsonConfigurationHtmlEscaped(): string { return $this->jsonSerializer->serialize( [ @@ -120,15 +118,4 @@ public function getJsonConfigurationHtmlEscaped() : string ] ); } - - /** - * Returns breadcrumb json. - * - * @return string - * @deprecated in favor of new method with name {suffix}Html{postfix}() - */ - public function getJsonConfiguration() - { - return $this->getJsonConfigurationHtmlEscaped(); - } } diff --git a/app/code/Magento/Catalog/ViewModel/Product/Listing/PreparePostData.php b/app/code/Magento/Catalog/ViewModel/Product/Listing/PreparePostData.php new file mode 100644 index 0000000000000..c7ea49cc4dc1b --- /dev/null +++ b/app/code/Magento/Catalog/ViewModel/Product/Listing/PreparePostData.php @@ -0,0 +1,46 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +declare(strict_types=1); + +namespace Magento\Catalog\ViewModel\Product\Listing; + +use Magento\Framework\View\Element\Block\ArgumentInterface; +use Magento\Framework\App\ActionInterface; +use Magento\Framework\Url\Helper\Data as UrlHelper; + +/** + * Check is available add to compare. + */ +class PreparePostData implements ArgumentInterface +{ + /** + * @var UrlHelper + */ + private $urlHelper; + + /** + * @param UrlHelper $urlHelper + */ + public function __construct(UrlHelper $urlHelper) + { + $this->urlHelper = $urlHelper; + } + + /** + * Wrapper for the PostHelper::getPostData() + * + * @param string $url + * @param array $data + * @return array + */ + public function getPostData(string $url, array $data = []):array + { + if (!isset($data[ActionInterface::PARAM_NAME_URL_ENCODED])) { + $data[ActionInterface::PARAM_NAME_URL_ENCODED] = $this->urlHelper->getEncodedUrl(); + } + return ['action' => $url, 'data' => $data]; + } +} diff --git a/app/code/Magento/Catalog/etc/adminhtml/di.xml b/app/code/Magento/Catalog/etc/adminhtml/di.xml index 4f905cd85f3d1..7d74ab38a8560 100644 --- a/app/code/Magento/Catalog/etc/adminhtml/di.xml +++ b/app/code/Magento/Catalog/etc/adminhtml/di.xml @@ -177,6 +177,10 @@ <item name="class" xsi:type="string">Magento\Catalog\Ui\DataProvider\Product\Modifier\Attributes</item> <item name="sortOrder" xsi:type="number">10</item> </item> + <item name="priceAttributes" xsi:type="array"> + <item name="class" xsi:type="string">Magento\Catalog\Ui\DataProvider\Product\Modifier\PriceAttributes</item> + <item name="sortOrder" xsi:type="number">10</item> + </item> </argument> </arguments> </virtualType> @@ -188,6 +192,14 @@ </argument> </arguments> </type> + <type name="Magento\Catalog\Ui\DataProvider\Product\Modifier\PriceAttributes"> + <arguments> + <argument name="priceAttributeList" xsi:type="array"> + <item name="cost" xsi:type="string">cost</item> + <item name="special_price" xsi:type="string">special_price</item> + </argument> + </arguments> + </type> <type name="Magento\Catalog\Ui\DataProvider\Product\Form\Modifier\CustomOptions"> <arguments> <argument name="scopeName" xsi:type="string">product_form.product_form</argument> diff --git a/app/code/Magento/Catalog/etc/adminhtml/system.xml b/app/code/Magento/Catalog/etc/adminhtml/system.xml index c80363038ac60..8f8a5f36e516c 100644 --- a/app/code/Magento/Catalog/etc/adminhtml/system.xml +++ b/app/code/Magento/Catalog/etc/adminhtml/system.xml @@ -20,30 +20,30 @@ <resource>Magento_Catalog::config_catalog</resource> <group id="fields_masks" translate="label" type="text" sortOrder="90" showInDefault="1" showInWebsite="1" showInStore="1"> <label>Product Fields Auto-Generation</label> - <field id="sku" translate="label comment" type="text" sortOrder="10" showInDefault="1" showInWebsite="0" showInStore="0" canRestore="1"> + <field id="sku" translate="label comment" type="text" sortOrder="10" showInDefault="1" canRestore="1"> <label>Mask for SKU</label> <comment>Use {{name}} as Product Name placeholder</comment> </field> - <field id="meta_title" translate="label comment" type="text" sortOrder="20" showInDefault="1" showInWebsite="0" showInStore="0" canRestore="1"> + <field id="meta_title" translate="label comment" type="text" sortOrder="20" showInDefault="1" canRestore="1"> <label>Mask for Meta Title</label> <comment>Use {{name}} as Product Name placeholder</comment> </field> - <field id="meta_keyword" translate="label comment" type="text" sortOrder="30" showInDefault="1" showInWebsite="0" showInStore="0" canRestore="1"> + <field id="meta_keyword" translate="label comment" type="text" sortOrder="30" showInDefault="1" canRestore="1"> <label>Mask for Meta Keywords</label> <comment>Use {{name}} as Product Name or {{sku}} as Product SKU placeholders</comment> </field> - <field id="meta_description" translate="label comment" type="text" sortOrder="40" showInDefault="1" showInWebsite="0" showInStore="0" canRestore="1"> + <field id="meta_description" translate="label comment" type="text" sortOrder="40" showInDefault="1" canRestore="1"> <label>Mask for Meta Description</label> <comment>Use {{name}} and {{description}} as Product Name and Product Description placeholders</comment> </field> </group> - <group id="recently_products" translate="label" type="text" sortOrder="350" showInDefault="1" showInWebsite="1" showInStore="0"> + <group id="recently_products" translate="label" type="text" sortOrder="350" showInDefault="1" showInWebsite="1"> <label>Recently Viewed/Compared Products</label> - <field id="recently_viewed_lifetime" translate="label" type="text" sortOrder="40" showInDefault="1" showInWebsite="0" showInStore="0" canRestore="1"> + <field id="recently_viewed_lifetime" translate="label" type="text" sortOrder="40" showInDefault="1" canRestore="1"> <label>Lifetime of products in Recently Viewed Widget</label> <validate>validate-number validate-zero-or-greater</validate> </field> - <field id="recently_compared_lifetime" translate="label" type="text" sortOrder="40" showInDefault="1" showInWebsite="0" showInStore="0" canRestore="1"> + <field id="recently_compared_lifetime" translate="label" type="text" sortOrder="40" showInDefault="1" canRestore="1"> <label>Lifetime of products in Recently Compared Widget</label> <validate>validate-number validate-zero-or-greater</validate> </field> @@ -78,12 +78,12 @@ <comment>Must be in the allowed values list.</comment> <validate>validate-per-page-value</validate> </field> - <field id="flat_catalog_category" translate="label" type="select" sortOrder="100" showInDefault="1" showInWebsite="0" showInStore="0" canRestore="1"> + <field id="flat_catalog_category" translate="label" type="select" sortOrder="100" showInDefault="1" canRestore="1"> <label>Use Flat Catalog Category</label> <backend_model>Magento\Catalog\Model\Indexer\Category\Flat\System\Config\Mode</backend_model> <source_model>Magento\Config\Model\Config\Source\Yesno</source_model> </field> - <field id="flat_catalog_product" translate="label" type="select" sortOrder="100" showInDefault="1" showInWebsite="0" showInStore="0"> + <field id="flat_catalog_product" translate="label" type="select" sortOrder="100" showInDefault="1" canRestore="1"> <label>Use Flat Catalog Product</label> <backend_model>Magento\Catalog\Model\Indexer\Product\Flat\System\Config\Mode</backend_model> <source_model>Magento\Config\Model\Config\Source\Yesno</source_model> @@ -93,12 +93,12 @@ <comment>Applies to category pages.</comment> <source_model>Magento\Catalog\Model\Config\Source\ListSort</source_model> </field> - <field id="list_allow_all" translate="label comment" type="select" sortOrder="6" showInDefault="1" showInWebsite="1" showInStore="1"> + <field id="list_allow_all" translate="label comment" type="select" sortOrder="6" showInDefault="1" showInWebsite="1" showInStore="1" canRestore="1"> <label>Allow All Products per Page</label> <comment>Whether to show "All" option in the "Show X Per Page" dropdown.</comment> <source_model>Magento\Config\Model\Config\Source\Yesno</source_model> </field> - <field id="remember_pagination" translate="label comment" type="select" sortOrder="7" showInDefault="1" showInWebsite="0" showInStore="0" canRestore="1"> + <field id="remember_pagination" translate="label comment" type="select" sortOrder="7" showInDefault="1" canRestore="1"> <label>Remember Category Pagination</label> <comment>Changing may affect SEO and cache storage consumption.</comment> <source_model>Magento\Config\Model\Config\Source\Yesno</source_model> @@ -128,9 +128,9 @@ <source_model>Magento\Config\Model\Config\Source\Yesno</source_model> </field> </group> - <group id="price" translate="label" type="text" sortOrder="400" showInDefault="1" showInWebsite="0" showInStore="0"> + <group id="price" translate="label" type="text" sortOrder="400" showInDefault="1"> <label>Price</label> - <field id="scope" translate="label comment" type="select" sortOrder="1" showInDefault="1" showInWebsite="0" showInStore="0"> + <field id="scope" translate="label comment" type="select" sortOrder="1" showInDefault="1"> <label>Catalog Price Scope</label> <comment><![CDATA[This defines the base currency scope ("Currency Setup" > "Currency Options" > "Base Currency").]]></comment> <backend_model>Magento\Catalog\Model\Indexer\Product\Price\System\Config\PriceScope</backend_model> @@ -138,6 +138,12 @@ <hide_in_single_store_mode>1</hide_in_single_store_mode> </field> </group> + <group id="layered_navigation"> + <field id="display_category" translate="label" type="select" sortOrder="30" showInDefault="1" showInWebsite="1" showInStore="1" canRestore="1"> + <label>Display Category Filter</label> + <source_model>Magento\Config\Model\Config\Source\Yesno</source_model> + </field> + </group> <group id="navigation" translate="label" type="text" sortOrder="500" showInDefault="1" showInWebsite="1" showInStore="1"> <label>Category Top Navigation</label> <field id="max_depth" translate="label" type="text" sortOrder="1" showInDefault="1" canRestore="1"> @@ -169,10 +175,13 @@ </section> <section id="cms"> <group id="wysiwyg"> - <field id="use_static_urls_in_catalog" translate="label comment" type="select" sortOrder="10" showInDefault="1" showInWebsite="0" showInStore="0"> + <field id="use_static_urls_in_catalog" translate="label comment" type="select" sortOrder="10" showInDefault="1"> <label>Use Static URLs for Media Content in WYSIWYG</label> <comment>Media content will be inserted into the editor as a static URL. Media content is not updated if the system configuration base URL changes.</comment> <source_model>Magento\Config\Model\Config\Source\Yesno</source_model> + <depends> + <field id="enabled" negative="1">disabled</field> + </depends> </field> </group> </section> @@ -213,7 +222,7 @@ <resource>Magento_Config::config_system</resource> <group id="upload_configuration" translate="label" type="text" sortOrder="1000" showInDefault="1" showInWebsite="1" showInStore="1"> <label>Images Upload Configuration</label> - <field id="jpeg_quality" translate="label comment" type="text" sortOrder="100" showInDefault="1" showInWebsite="0" showInStore="0" canRestore="1"> + <field id="jpeg_quality" translate="label comment" type="text" sortOrder="100" showInDefault="1" canRestore="1"> <label>Quality</label> <validate>validate-digits validate-digits-range digits-range-1-100 required-entry</validate> <comment>Jpeg quality for resized images 1-100%.</comment> diff --git a/app/code/Magento/Catalog/etc/config.xml b/app/code/Magento/Catalog/etc/config.xml index 8506d2ae03032..aa689c7dd35b2 100644 --- a/app/code/Magento/Catalog/etc/config.xml +++ b/app/code/Magento/Catalog/etc/config.xml @@ -28,7 +28,9 @@ <grid_per_page>12</grid_per_page> <list_per_page>10</list_per_page> <flat_catalog_category>0</flat_catalog_category> + <flat_catalog_product>0</flat_catalog_product> <default_sort_by>position</default_sort_by> + <list_allow_all>0</list_allow_all> <parse_url_directives>1</parse_url_directives> <remember_pagination>0</remember_pagination> </frontend> @@ -52,6 +54,9 @@ <time_format>12h</time_format> <forbidden_extensions>php,exe</forbidden_extensions> </custom_options> + <layered_navigation> + <display_category>1</display_category> + </layered_navigation> </catalog> <indexer> <catalog_product_price> diff --git a/app/code/Magento/Catalog/etc/di.xml b/app/code/Magento/Catalog/etc/di.xml index af7b9af2d8b7e..ff67989d337bb 100644 --- a/app/code/Magento/Catalog/etc/di.xml +++ b/app/code/Magento/Catalog/etc/di.xml @@ -74,6 +74,7 @@ <preference for="Magento\Catalog\Model\Product\Configuration\Item\ItemResolverInterface" type="Magento\Catalog\Model\Product\Configuration\Item\ItemResolverComposite" /> <preference for="Magento\Catalog\Api\Data\MassActionInterface" type="\Magento\Catalog\Model\MassAction" /> <preference for="Magento\Catalog\Model\ProductLink\Data\ListCriteriaInterface" type="Magento\Catalog\Model\ProductLink\Data\ListCriteria" /> + <preference for="Magento\Catalog\Api\CategoryListDeleteBySkuInterface" type="Magento\Catalog\Model\CategoryLinkRepository"/> <type name="Magento\Customer\Model\ResourceModel\Visitor"> <plugin name="catalogLog" type="Magento\Catalog\Model\Plugin\Log" /> </type> @@ -238,6 +239,11 @@ <argument name="imageUploader" xsi:type="object">Magento\Catalog\CategoryImageUpload</argument> </arguments> </type> + <type name="Magento\Catalog\Model\Category\Attribute\Backend\Image"> + <arguments> + <argument name="imageUploader" xsi:type="object">Magento\Catalog\CategoryImageUpload</argument> + </arguments> + </type> <type name="Magento\Catalog\Model\Session"> <arguments> <argument name="storage" xsi:type="object">Magento\Catalog\Model\Session\Storage</argument> @@ -885,6 +891,11 @@ <item name="update" xsi:type="string">Magento\Catalog\Model\ResourceModel\UpdateHandler</item> </item> </item> + <item name="websites" xsi:type="array"> + <item name="Magento\Catalog\Api\Data\ProductInterface" xsi:type="array"> + <item name="create" xsi:type="string">Magento\Catalog\Model\ResourceModel\ProductWebsiteAssignmentHandler</item> + </item> + </item> </argument> </arguments> </type> diff --git a/app/code/Magento/Catalog/etc/webapi_rest/di.xml b/app/code/Magento/Catalog/etc/webapi_rest/di.xml index bfbc05b12079d..14779245f21af 100644 --- a/app/code/Magento/Catalog/etc/webapi_rest/di.xml +++ b/app/code/Magento/Catalog/etc/webapi_rest/di.xml @@ -28,4 +28,16 @@ <type name="Magento\Catalog\Api\CategoryRepositoryInterface"> <plugin name="category_authorization" type="Magento\Catalog\Plugin\CategoryAuthorization" /> </type> + <type name="Magento\Framework\Reflection\DataObjectProcessor"> + <arguments> + <argument name="processors" xsi:type="array"> + <item name="Magento\Catalog\Model\Product" xsi:type="object">Magento\Catalog\Model\Product\Webapi\ProductOutputProcessor\Proxy</item> + </argument> + </arguments> + </type> + <type name="Magento\Catalog\Model\Product\Webapi\ProductOutputProcessor"> + <arguments> + <argument name="deserializer" xsi:type="object">Magento\Framework\Webapi\Rest\Request\Deserializer\Json</argument> + </arguments> + </type> </config> diff --git a/app/code/Magento/Catalog/etc/webapi_soap/di.xml b/app/code/Magento/Catalog/etc/webapi_soap/di.xml index bfbc05b12079d..a709f23d8c12b 100644 --- a/app/code/Magento/Catalog/etc/webapi_soap/di.xml +++ b/app/code/Magento/Catalog/etc/webapi_soap/di.xml @@ -28,4 +28,16 @@ <type name="Magento\Catalog\Api\CategoryRepositoryInterface"> <plugin name="category_authorization" type="Magento\Catalog\Plugin\CategoryAuthorization" /> </type> + <type name="Magento\Framework\Reflection\DataObjectProcessor"> + <arguments> + <argument name="processors" xsi:type="array"> + <item name="Magento\Catalog\Model\Product" xsi:type="object">Magento\Catalog\Model\Product\Webapi\ProductOutputProcessor\Proxy</item> + </argument> + </arguments> + </type> + <type name="Magento\Catalog\Model\Product\Webapi\ProductOutputProcessor"> + <arguments> + <argument name="deserializer" xsi:type="object">Magento\Framework\Webapi\Rest\Request\Deserializer\Xml</argument> + </arguments> + </type> </config> diff --git a/app/code/Magento/Catalog/registration.php b/app/code/Magento/Catalog/registration.php index fb94e04aa2be0..e18d43d9a36d8 100644 --- a/app/code/Magento/Catalog/registration.php +++ b/app/code/Magento/Catalog/registration.php @@ -4,6 +4,6 @@ * See COPYING.txt for license details. */ -use \Magento\Framework\Component\ComponentRegistrar; +use Magento\Framework\Component\ComponentRegistrar; ComponentRegistrar::register(ComponentRegistrar::MODULE, 'Magento_Catalog', __DIR__); diff --git a/app/code/Magento/Catalog/view/adminhtml/templates/catalog/product/attribute/labels.phtml b/app/code/Magento/Catalog/view/adminhtml/templates/catalog/product/attribute/labels.phtml index 1d5d251f00de9..85a6b347f2feb 100644 --- a/app/code/Magento/Catalog/view/adminhtml/templates/catalog/product/attribute/labels.phtml +++ b/app/code/Magento/Catalog/view/adminhtml/templates/catalog/product/attribute/labels.phtml @@ -19,7 +19,7 @@ <table class="admin__control-table" id="attribute-labels-table"> <thead> <tr> - <?php foreach ($block->getStores() as $_store) :?> + <?php foreach ($block->getStores() as $_store): ?> <th class="col-store-view"><?= $block->escapeHtml($_store->getName()) ?></th> <?php endforeach; ?> </tr> @@ -27,13 +27,15 @@ <tbody> <tr> <?php $_labels = $block->getLabelValues() ?> - <?php foreach ($block->getStores() as $_store) :?> + <?php foreach ($block->getStores() as $_store): ?> <td class="col-store-view"> - <input class="input-text<?php if ($_store->getId() == \Magento\Store\Model\Store::DEFAULT_STORE_ID) :?> required-option<?php endif; ?>" + <?php $isRequired = $_store->getId() == \Magento\Store\Model\Store::DEFAULT_STORE_ID; ?> + <?php $isRequiredClass = $isRequired ? 'required-option' : ''; ?> + <input class="input-text validate-no-html-tags <?= /* @noEscape */ $isRequiredClass ?>" type="text" name="frontend_label[<?= $block->escapeHtmlAttr($_store->getId()) ?>]" value="<?= $block->escapeHtmlAttr($_labels[$_store->getId()]) ?>" - <?php if ($block->getReadOnly()) :?> + <?php if ($block->getReadOnly()): ?> disabled="disabled" <?php endif;?>/> </td> diff --git a/app/code/Magento/Catalog/view/adminhtml/templates/catalog/product/composite/fieldset/options/type/file.phtml b/app/code/Magento/Catalog/view/adminhtml/templates/catalog/product/composite/fieldset/options/type/file.phtml index da0b3b36d561e..89d005a178fac 100644 --- a/app/code/Magento/Catalog/view/adminhtml/templates/catalog/product/composite/fieldset/options/type/file.phtml +++ b/app/code/Magento/Catalog/view/adminhtml/templates/catalog/product/composite/fieldset/options/type/file.phtml @@ -22,7 +22,7 @@ require(['prototype'], function(){ initializeFile: function(inputBox) { this.inputFile = inputBox.select('input[name="<?= /* @noEscape */ $_fileName ?>"]')[0]; this.inputFileAction = inputBox.select('input[name="<?= /* @noEscape */ $_fieldNameAction ?>"]')[0]; - this.fileNameBox = inputBox.up('dd').select('.<?= /* @noEscape */ $_fileNamed ?>')[0]; + this.fileNameBox = inputBox.up('div').select('.<?= /* @noEscape */ $_fileNamed ?>')[0]; }, toggleFileChange: function(inputBox) { diff --git a/app/code/Magento/Catalog/view/adminhtml/templates/catalog/product/edit/action/attribute.phtml b/app/code/Magento/Catalog/view/adminhtml/templates/catalog/product/edit/action/attribute.phtml index 056cf014f769a..1eb3f9a03d3f9 100644 --- a/app/code/Magento/Catalog/view/adminhtml/templates/catalog/product/edit/action/attribute.phtml +++ b/app/code/Magento/Catalog/view/adminhtml/templates/catalog/product/edit/action/attribute.phtml @@ -17,6 +17,7 @@ <script type="text/x-magento-init"> { "#attributes-edit-form": { + "Magento_Catalog/js/product/weight-handler": {}, "Magento_Catalog/catalog/product/edit/attribute": { "validationUrl": "<?= $block->escapeJs($block->escapeUrl($block->getValidationUrl())) ?>" } diff --git a/app/code/Magento/Catalog/view/adminhtml/ui_component/category_form.xml b/app/code/Magento/Catalog/view/adminhtml/ui_component/category_form.xml index f2afef1215017..d3689a0db1306 100644 --- a/app/code/Magento/Catalog/view/adminhtml/ui_component/category_form.xml +++ b/app/code/Magento/Catalog/view/adminhtml/ui_component/category_form.xml @@ -354,7 +354,6 @@ <additionalClasses> <class name="admin__field-small">true</class> </additionalClasses> - <addBefore>$</addBefore> <label translate="true">Layered Navigation Price Step</label> </settings> </field> diff --git a/app/code/Magento/Catalog/view/adminhtml/ui_component/product_attribute_add_form.xml b/app/code/Magento/Catalog/view/adminhtml/ui_component/product_attribute_add_form.xml index 3a6621137ed5a..7f87eb0cdd391 100644 --- a/app/code/Magento/Catalog/view/adminhtml/ui_component/product_attribute_add_form.xml +++ b/app/code/Magento/Catalog/view/adminhtml/ui_component/product_attribute_add_form.xml @@ -63,6 +63,7 @@ <required>true</required> <validation> <rule name="required-entry" xsi:type="boolean">true</rule> + <rule name="validate-no-html-tags" xsi:type="boolean">true</rule> </validation> <dataType>string</dataType> <label translate="true">Attribute Label</label> diff --git a/app/code/Magento/Catalog/view/adminhtml/ui_component/product_listing.xml b/app/code/Magento/Catalog/view/adminhtml/ui_component/product_listing.xml index d2d6f098125ce..88bb578712056 100644 --- a/app/code/Magento/Catalog/view/adminhtml/ui_component/product_listing.xml +++ b/app/code/Magento/Catalog/view/adminhtml/ui_component/product_listing.xml @@ -144,7 +144,7 @@ <label translate="true">Type</label> </settings> </column> - <column name="attribute_set_id" component="Magento_Ui/js/grid/columns/select" sortOrder="50"> + <column name="attribute_set_id" class="Magento\Catalog\Ui\Component\Listing\Columns\AttributeSetId" component="Magento_Ui/js/grid/columns/select" sortOrder="50"> <settings> <options class="Magento\Catalog\Model\Product\AttributeSet\Options"/> <filter>select</filter> diff --git a/app/code/Magento/Catalog/view/adminhtml/web/catalog/category/assign-products.js b/app/code/Magento/Catalog/view/adminhtml/web/catalog/category/assign-products.js index 829c04c13106d..35b8958f2f8fd 100644 --- a/app/code/Magento/Catalog/view/adminhtml/web/catalog/category/assign-products.js +++ b/app/code/Magento/Catalog/view/adminhtml/web/catalog/category/assign-products.js @@ -51,15 +51,29 @@ define([ */ function categoryProductRowClick(grid, event) { var trElement = Event.findElement(event, 'tr'), - isInput = Event.element(event).tagName === 'INPUT', + eventElement = Event.element(event), + isInputCheckbox = eventElement.tagName === 'INPUT' && eventElement.type === 'checkbox', + isInputPosition = grid.targetElement && + grid.targetElement.tagName === 'INPUT' && + grid.targetElement.name === 'position', checked = false, checkbox = null; - if (trElement) { + if (eventElement.tagName === 'LABEL' && + trElement.querySelector('#' + eventElement.htmlFor) && + trElement.querySelector('#' + eventElement.htmlFor).type === 'checkbox' + ) { + event.stopPropagation(); + trElement.querySelector('#' + eventElement.htmlFor).trigger('click'); + + return; + } + + if (trElement && !isInputPosition) { checkbox = Element.getElementsBySelector(trElement, 'input'); if (checkbox[0]) { - checked = isInput ? checkbox[0].checked : !checkbox[0].checked; + checked = isInputCheckbox ? checkbox[0].checked : !checkbox[0].checked; gridJsObject.setCheckboxChecked(checkbox[0], checked); } } diff --git a/app/code/Magento/Catalog/view/adminhtml/web/js/product/weight-handler.js b/app/code/Magento/Catalog/view/adminhtml/web/js/product/weight-handler.js index 94300e31f74b5..1d7993fd98823 100644 --- a/app/code/Magento/Catalog/view/adminhtml/web/js/product/weight-handler.js +++ b/app/code/Magento/Catalog/view/adminhtml/web/js/product/weight-handler.js @@ -29,6 +29,14 @@ define([ return $('[data-role=weight-switcher]'); }, + /** + * Weight Change Toggle + * @returns {*|jQuery|HTMLElement} + */ + $weightChangeToggle: function () { + return $('#toggle_weight'); + }, + /** * Is locked * @returns {*} @@ -51,14 +59,39 @@ define([ this.$weight().removeClass('ignore-validate').prop('disabled', false); }, + /** + * Disabled Switcher + */ + disabledSwitcher: function () { + this.$weightSwitcher().find('input[type="radio"]').addClass('ignore-validate').prop('disabled', true); + }, + + /** + * Enabled Switcher + */ + enabledSwitcher: function () { + this.$weightSwitcher().find('input[type="radio"]').removeClass('ignore-validate').prop('disabled', false); + }, + /** * Switch Weight * @returns {*} */ switchWeight: function () { + if (this.hasWeightChangeToggle()) { + return; + } + return this.productHasWeightBySwitcher() ? this.enabled() : this.disabled(); }, + /** + * Toggle Switcher + */ + toggleSwitcher: function () { + this.isWeightChanging() ? this.enabledSwitcher() : this.disabledSwitcher(); + }, + /** * Hide weight switcher */ @@ -82,6 +115,14 @@ define([ return this.$weight.is(':visible'); }, + /** + * Has weight change toggle + * @returns {*} + */ + hasWeightChangeToggle: function () { + return this.$weightChangeToggle().is(':visible'); + }, + /** * Product has weight * @returns {Bool} @@ -90,6 +131,14 @@ define([ return $('input:checked', this.$weightSwitcher()).val() === '1'; }, + /** + * Product weight toggle is checked + * @returns {Bool} + */ + isWeightChanging: function () { + return this.$weightChangeToggle().is(':checked'); + }, + /** * Change * @param {String} data @@ -110,6 +159,10 @@ define([ if (this.hasWeightSwitcher()) { this.switchWeight(); } + + if (this.hasWeightChangeToggle()) { + this.toggleSwitcher(); + } }, /** diff --git a/app/code/Magento/Catalog/view/base/templates/product/price/tier_prices.phtml b/app/code/Magento/Catalog/view/base/templates/product/price/tier_prices.phtml index 5949b54268a62..af50446c93a95 100644 --- a/app/code/Magento/Catalog/view/base/templates/product/price/tier_prices.phtml +++ b/app/code/Magento/Catalog/view/base/templates/product/price/tier_prices.phtml @@ -17,15 +17,15 @@ $tierPrices = $tierPriceModel->getTierPriceList(); $msrpShowOnGesture = $block->getPriceType('msrp_price')->isShowPriceOnGesture(); $product = $block->getSaleableItem(); ?> -<?php if (count($tierPrices)) : ?> +<?php if (count($tierPrices)): ?> <ul class="<?= $block->escapeHtmlAttr(($block->hasListClass() ? $block->getListClass() : 'prices-tier items')) ?>"> - <?php foreach ($tierPrices as $index => $price) : ?> + <?php foreach ($tierPrices as $index => $price): ?> <li class="item"> <?php $productId = $product->getId(); $isSaleable = $product->isSaleable(); $popupId = 'msrp-popup-' . $productId . $block->getRandomString(20); - if ($msrpShowOnGesture && $price['price']->getValue() < $product->getMsrp()) : + if ($msrpShowOnGesture && $price['price']->getValue() < $product->getMsrp()): $addToCartUrl = ''; if ($isSaleable) { $addToCartUrl = $this->helper(\Magento\Checkout\Helper\Cart::class) @@ -60,7 +60,7 @@ $product = $block->getSaleableItem(); id="<?= $block->escapeHtmlAttr($popupId) ?>" data-tier-price="<?= $block->escapeHtml($block->jsonEncode($tierPriceData)) ?>"> <?= $block->escapeHtml(__('Click for price')) ?></a> - <?php else : + <?php else: $priceAmountBlock = $block->renderAmount( $price['price'], [ @@ -73,11 +73,12 @@ $product = $block->getSaleableItem(); ?> <?= /* @noEscape */ ($block->getShowDetailedPrice() !== false) ? __( - 'Buy %1 for %2 each and <strong class="benefit">save<span class="percent tier-%3"> %4</span>%</strong>', + 'Buy %1 for %2 each and '. + '<strong class="benefit">save<span class="percent tier-%3"> %4</span>%</strong>', $price['price_qty'], $priceAmountBlock, $index, - $block->formatPercent($price['percentage_value'] ?? $tierPriceModel->getSavePercent($price['price'])) + $block->formatPercent($tierPriceModel->getSavePercent($price['price'])) ) : __('Buy %1 for %2 each', $price['price_qty'], $priceAmountBlock); ?> @@ -85,7 +86,7 @@ $product = $block->getSaleableItem(); </li> <?php endforeach; ?> </ul> - <?php if ($msrpShowOnGesture) :?> + <?php if ($msrpShowOnGesture):?> <script type="text/x-magento-init"> { ".product-info-main": { diff --git a/app/code/Magento/Catalog/view/base/web/js/price-box.js b/app/code/Magento/Catalog/view/base/web/js/price-box.js index 02ae6eadef672..6ca7f24b90c36 100644 --- a/app/code/Magento/Catalog/view/base/web/js/price-box.js +++ b/app/code/Magento/Catalog/view/base/web/js/price-box.js @@ -49,6 +49,7 @@ define([ box.on('reloadPrice', this.reloadPrice.bind(this)); box.on('updatePrice', this.onUpdatePrice.bind(this)); + box.trigger('price-box-initialized'); }, /** diff --git a/app/code/Magento/Catalog/view/base/web/template/product/list/listing.html b/app/code/Magento/Catalog/view/base/web/template/product/list/listing.html index c80f591bd6590..ea0124f1c7c4d 100644 --- a/app/code/Magento/Catalog/view/base/web/template/product/list/listing.html +++ b/app/code/Magento/Catalog/view/base/web/template/product/list/listing.html @@ -25,15 +25,15 @@ <render args="$col.getBody()"/> </fastForEach> - <div if="getRegion('action-primary-area')().length || getRegion('action-secondary-area')().length" + <div if="regionHasElements('action-primary-area') || regionHasElements('action-secondary-area')" class="product-item-actions"> - <div class="actions-primary" if="getRegion('action-primary-area')().length"> + <div class="actions-primary" if="regionHasElements('action-primary-area')"> <fastForEach args="data: getRegion('action-primary-area'), as: '$col'" > <render args="$col.getBody()"/> </fastForEach> </div> - <div if="getRegion('action-secondary-area')().length" + <div if="regionHasElements('action-secondary-area')" class="actions-secondary" data-role="add-to-links"> <fastForEach args="data: getRegion('action-secondary-area'), as: '$col'" > @@ -42,7 +42,7 @@ </div> </div> - <div if="getRegion('description-area')().length" + <div if="regionHasElements('description-area')" class="product-item-description"> <fastForEach args="data: getRegion('description-area'), as: '$col'" > <render args="$col.getBody()"/> @@ -54,4 +54,4 @@ </ol> </div> </div> -</div> \ No newline at end of file +</div> diff --git a/app/code/Magento/Catalog/view/frontend/layout/catalog_category_view.xml b/app/code/Magento/Catalog/view/frontend/layout/catalog_category_view.xml index 5fee1d8447e5a..c4adcaf785012 100644 --- a/app/code/Magento/Catalog/view/frontend/layout/catalog_category_view.xml +++ b/app/code/Magento/Catalog/view/frontend/layout/catalog_category_view.xml @@ -9,7 +9,12 @@ <body> <referenceContainer name="columns.top"> <container name="category.view.container" htmlTag="div" htmlClass="category-view" after="-"> - <block class="Magento\Catalog\Block\Category\View" name="category.image" template="Magento_Catalog::category/image.phtml"/> + <block class="Magento\Catalog\Block\Category\View" name="category.image" template="Magento_Catalog::category/image.phtml"> + <arguments> + <argument name="image" xsi:type="object">Magento\Catalog\ViewModel\Category\Image</argument> + <argument name="output" xsi:type="object">Magento\Catalog\ViewModel\Category\Output</argument> + </arguments> + </block> <block class="Magento\Catalog\Block\Category\View" name="category.description" template="Magento_Catalog::category/description.phtml"/> <block class="Magento\Catalog\Block\Category\View" name="category.cms" template="Magento_Catalog::category/cms.phtml"/> </container> diff --git a/app/code/Magento/Catalog/view/frontend/layout/catalog_product_view.xml b/app/code/Magento/Catalog/view/frontend/layout/catalog_product_view.xml index 13e2d998f6cdd..6e24f84f00482 100644 --- a/app/code/Magento/Catalog/view/frontend/layout/catalog_product_view.xml +++ b/app/code/Magento/Catalog/view/frontend/layout/catalog_product_view.xml @@ -175,6 +175,7 @@ <block class="Magento\Catalog\Block\Product\ProductList\Related" name="catalog.product.related" template="Magento_Catalog::product/list/items.phtml"> <arguments> <argument name="type" xsi:type="string">related</argument> + <argument name="view_model" xsi:type="object">Magento\Catalog\ViewModel\Product\Listing\PreparePostData</argument> </arguments> <block class="Magento\Catalog\Block\Product\ProductList\Item\Container" name="related.product.addto" as="addto"> <block class="Magento\Catalog\Block\Product\ProductList\Item\AddTo\Compare" @@ -185,6 +186,7 @@ <block class="Magento\Catalog\Block\Product\ProductList\Upsell" name="product.info.upsell" template="Magento_Catalog::product/list/items.phtml"> <arguments> <argument name="type" xsi:type="string">upsell</argument> + <argument name="view_model" xsi:type="object">Magento\Catalog\ViewModel\Product\Listing\PreparePostData</argument> </arguments> <block class="Magento\Catalog\Block\Product\ProductList\Item\Container" name="upsell.product.addto" as="addto"> <block class="Magento\Catalog\Block\Product\ProductList\Item\AddTo\Compare" diff --git a/app/code/Magento/Catalog/view/frontend/layout/default.xml b/app/code/Magento/Catalog/view/frontend/layout/default.xml index 3dff3e9b3c1f8..8f414724f51db 100644 --- a/app/code/Magento/Catalog/view/frontend/layout/default.xml +++ b/app/code/Magento/Catalog/view/frontend/layout/default.xml @@ -26,6 +26,9 @@ <item name="updateRequestConfig" xsi:type="array"> <item name="url" xsi:type="serviceUrl" path="/products-render-info"/> </item> + <item name="requestConfig" xsi:type="array"> + <item name="syncUrl" xsi:type="url" path="catalog/product/frontend_action_synchronize"/> + </item> </item> </argument> </arguments> diff --git a/app/code/Magento/Catalog/view/frontend/templates/category/image.phtml b/app/code/Magento/Catalog/view/frontend/templates/category/image.phtml index 02593d3b541a1..8f72e4713d22b 100644 --- a/app/code/Magento/Catalog/view/frontend/templates/category/image.phtml +++ b/app/code/Magento/Catalog/view/frontend/templates/category/image.phtml @@ -16,10 +16,9 @@ // phpcs:disable Magento2.Security.LanguageConstruct.DirectOutput ?> <?php - $_helper = $this->helper(Magento\Catalog\Helper\Output::class); $_category = $block->getCurrentCategory(); $_imgHtml = ''; - if ($_imgUrl = $_category->getImageUrl()) { + if ($_imgUrl = $block->getImage()->getUrl($_category)) { $_imgHtml = '<div class="category-image"><img src="' . $block->escapeUrl($_imgUrl) . '" alt="' @@ -27,7 +26,7 @@ . '" title="' . $block->escapeHtmlAttr($_category->getName()) . '" class="image" /></div>'; - $_imgHtml = $_helper->categoryAttribute($_category, $_imgHtml, 'image'); + $_imgHtml = $block->getOutput()->categoryAttribute($_category, $_imgHtml, 'image'); /* @noEscape */ echo $_imgHtml; } ?> diff --git a/app/code/Magento/Catalog/view/frontend/templates/product/image.phtml b/app/code/Magento/Catalog/view/frontend/templates/product/image.phtml index 5a31f3d125c81..9f8fdfb518408 100644 --- a/app/code/Magento/Catalog/view/frontend/templates/product/image.phtml +++ b/app/code/Magento/Catalog/view/frontend/templates/product/image.phtml @@ -4,11 +4,17 @@ * See COPYING.txt for license details. */ ?> -<?php /** @var $block \Magento\Catalog\Block\Product\Image */ ?> +<?php +/** @var $block \Magento\Catalog\Block\Product\Image */ +/** @var $escaper \Magento\Framework\Escaper */ +?> -<img class="photo image <?= $block->escapeHtmlAttr($block->getClass()) ?>" - <?= $block->escapeHtml($block->getCustomAttributes()) ?> - src="<?= $block->escapeUrl($block->getImageUrl()) ?>" - width="<?= $block->escapeHtmlAttr($block->getWidth()) ?>" - height="<?= $block->escapeHtmlAttr($block->getHeight()) ?>" - alt="<?= /* @noEscape */ $block->stripTags($block->getLabel(), null, true) ?>" /> +<img class="photo image <?= $escaper->escapeHtmlAttr($block->getClass()) ?>" + <?php foreach ($block->getCustomAttributes() as $name => $value): ?> + <?= $escaper->escapeHtmlAttr($name) ?>="<?= $escaper->escapeHtmlAttr($value) ?>" + <?php endforeach; ?> + src="<?= $escaper->escapeUrl($block->getImageUrl()) ?>" + loading="lazy" + width="<?= $escaper->escapeHtmlAttr($block->getWidth()) ?>" + height="<?= $escaper->escapeHtmlAttr($block->getHeight()) ?>" + alt="<?= $escaper->escapeHtmlAttr($block->getLabel()) ?>" /> diff --git a/app/code/Magento/Catalog/view/frontend/templates/product/image_with_borders.phtml b/app/code/Magento/Catalog/view/frontend/templates/product/image_with_borders.phtml index 33f7620f1a1f5..2f352438297d4 100644 --- a/app/code/Magento/Catalog/view/frontend/templates/product/image_with_borders.phtml +++ b/app/code/Magento/Catalog/view/frontend/templates/product/image_with_borders.phtml @@ -4,16 +4,22 @@ * See COPYING.txt for license details. */ ?> -<?php /** @var $block \Magento\Catalog\Block\Product\Image */ ?> +<?php +/** @var $block \Magento\Catalog\Block\Product\Image */ +/** @var $escaper \Magento\Framework\Escaper */ +?> <span class="product-image-container" - style="width:<?= $block->escapeHtmlAttr($block->getWidth()) ?>px;"> + style="width:<?= $escaper->escapeHtmlAttr($block->getWidth()) ?>px;"> <span class="product-image-wrapper" style="padding-bottom: <?= ($block->getRatio() * 100) ?>%;"> - <img class="<?= $block->escapeHtmlAttr($block->getClass()) ?>" - <?= $block->escapeHtmlAttr($block->getCustomAttributes()) ?> - src="<?= $block->escapeUrl($block->getImageUrl()) ?>" - max-width="<?= $block->escapeHtmlAttr($block->getWidth()) ?>" - max-height="<?= $block->escapeHtmlAttr($block->getHeight()) ?>" - alt="<?= /* @noEscape */ $block->stripTags($block->getLabel(), null, true) ?>"/></span> + <img class="<?= $escaper->escapeHtmlAttr($block->getClass()) ?>" + <?php foreach ($block->getCustomAttributes() as $name => $value): ?> + <?= $escaper->escapeHtmlAttr($name) ?>="<?= $escaper->escapeHtmlAttr($value) ?>" + <?php endforeach; ?> + src="<?= $escaper->escapeUrl($block->getImageUrl()) ?>" + loading="lazy" + width="<?= $escaper->escapeHtmlAttr($block->getWidth()) ?>" + height="<?= $escaper->escapeHtmlAttr($block->getHeight()) ?>" + alt="<?= $escaper->escapeHtmlAttr($block->getLabel()) ?>"/></span> </span> diff --git a/app/code/Magento/Catalog/view/frontend/templates/product/list/items.phtml b/app/code/Magento/Catalog/view/frontend/templates/product/list/items.phtml index fc4d85044a8d6..89dcef49d2bac 100644 --- a/app/code/Magento/Catalog/view/frontend/templates/product/list/items.phtml +++ b/app/code/Magento/Catalog/view/frontend/templates/product/list/items.phtml @@ -4,6 +4,9 @@ * See COPYING.txt for license details. */ +use Magento\Catalog\ViewModel\Product\Listing\PreparePostData; +use Magento\Framework\App\ActionInterface; + // phpcs:disable Magento2.Templates.ThisInTemplate.FoundThis // phpcs:disable Generic.WhiteSpace.ScopeIndent.Incorrect // phpcs:disable Generic.Files.LineLength @@ -156,6 +159,7 @@ switch ($type = $block->getType()) { default: $exist = null; } +$_item = null; ?> <?php if ($exist):?> @@ -170,13 +174,15 @@ switch ($type = $block->getType()) { <div class="block <?= $block->escapeHtmlAttr($class) ?>"> <?php endif; ?> <div class="block-title title"> - <strong id="block-<?= $block->escapeHtmlAttr($class) ?>-heading" role="heading" aria-level="2"><?= $block->escapeHtml($title) ?></strong> + <strong id="block-<?= $block->escapeHtmlAttr($class) ?>-heading" role="heading" + aria-level="2"><?= $block->escapeHtml($title) ?></strong> </div> <div class="block-content content" aria-labelledby="block-<?= $block->escapeHtmlAttr($class) ?>-heading"> <?php if ($type == 'related' && $canItemsAddToCart):?> <div class="block-actions"> <?= $block->escapeHtml(__('Check items to add to the cart or')) ?> - <button type="button" class="action select" data-role="select-all"><span><?= $block->escapeHtml(__('select all')) ?></span></button> + <button type="button" class="action select" + data-role="select-all"><span><?= $block->escapeHtml(__('select all')) ?></span></button> </div> <?php endif; ?> <div class="products wrapper grid products-grid products-<?= $block->escapeHtmlAttr($type) ?>"> @@ -195,11 +201,15 @@ switch ($type = $block->getType()) { <?php endif; ?> <div class="product-item-info <?= /* @noEscape */ $available ?>"> <?= /* @noEscape */ '<!-- ' . $image . '-->' ?> - <a href="<?= $block->escapeUrl($block->getProductUrl($_item)) ?>" class="product photo product-item-photo"> + <a href="<?= $block->escapeUrl($block->getProductUrl($_item)) ?>" + class="product photo product-item-photo"> <?= $block->getImage($_item, $image)->toHtml() ?> </a> <div class="product details product-item-details"> - <strong class="product name product-item-name"><a class="product-item-link" title="<?= $block->escapeHtml($_item->getName()) ?>" href="<?= $block->escapeUrl($block->getProductUrl($_item)) ?>"> + <strong class="product name product-item-name"><a + class="product-item-link" + title="<?= $block->escapeHtmlAttr($_item->getName()) ?>" + href="<?= $block->escapeUrl($block->getProductUrl($_item)) ?>"> <?= $block->escapeHtml($_item->getName()) ?></a> </strong> @@ -209,52 +219,88 @@ switch ($type = $block->getType()) { <?= $block->getReviewsSummaryHtml($_item, $templateType) ?> <?php endif; ?> - <?php if ($canItemsAddToCart && !$_item->isComposite() && $_item->isSaleable() && $type == 'related'):?> + <?php if ($canItemsAddToCart && !$_item->isComposite() && $_item->isSaleable() + && $type == 'related'):?> <?php if (!$_item->getRequiredOptions()):?> - <div class="field choice related"> - <input type="checkbox" class="checkbox related" id="related-checkbox<?= $block->escapeHtmlAttr($_item->getId()) ?>" name="related_products[]" value="<?= $block->escapeHtmlAttr($_item->getId()) ?>" /> - <label class="label" for="related-checkbox<?= $block->escapeHtmlAttr($_item->getId()) ?>"><span><?= $block->escapeHtml(__('Add to Cart')) ?></span></label> + <div class="field choice related"><input + type="checkbox" + class="checkbox related" + id="related-checkbox<?= $block->escapeHtmlAttr($_item->getId()) ?>" + name="related_products[]" + value="<?= $block->escapeHtmlAttr($_item->getId()) ?>" /> + <label + class="label" + for="related-checkbox<?= $block->escapeHtmlAttr( + $_item->getId() + ) ?>"><span><?= $block->escapeHtml(__('Add to Cart')) ?></span> + </label> </div> <?php endif; ?> <?php endif; ?> <?php if ($showAddTo || $showCart):?> + <?php // phpcs:disable ?> <div class="product actions product-item-actions"> <?php if ($showCart):?> - <div class="actions-primary"> - <?php if ($_item->isSaleable()):?> - <?php if ($_item->getTypeInstance()->hasRequiredOptions($_item)):?> - <button class="action tocart primary" data-mage-init='{"redirectUrl": {"url": "<?= $block->escapeUrl($block->getAddToCartUrl($_item)) ?>"}}' type="button" title="<?= $block->escapeHtmlAttr(__('Add to Cart')) ?>"> - <span><?= $block->escapeHtml(__('Add to Cart')) ?></span> - </button> - <?php else:?> - <?php $postDataHelper = $this->helper(Magento\Framework\Data\Helper\PostHelper::class); - $postData = $postDataHelper->getPostData($block->escapeUrl($block->getAddToCartUrl($_item)), ['product' => $_item->getEntityId()]) - ?> - <button class="action tocart primary" - data-post='<?= /* @noEscape */ $postData ?>' - type="button" title="<?= $block->escapeHtmlAttr(__('Add to Cart')) ?>"> + <?php if ($_item->isSaleable()):?> + <div class="actions-primary"> + <?php if (!$_item->getTypeInstance()->isPossibleBuyFromList($_item)):?> + <button + class="action tocart primary" + data-mage-init='{"redirectUrl": {"url": "<?= $block->escapeUrl($block->getAddToCartUrl($_item)) ?>"}}' type="button" title="<?= $block->escapeHtmlAttr(__('Add to Cart')) ?>"> + <span><?= $block->escapeHtml(__('Add to Cart')) ?></span> + </button> + <?php else :?> + <?php + /** @var $viewModel PreparePostData */ + $viewModel = $block->getViewModel(); + $postArray = $viewModel->getPostData( + $block->escapeUrl($block->getAddToCartUrl($_item)), + ['product' => $_item->getEntityId()] + ); + $value = $postArray['data'][ActionInterface::PARAM_NAME_URL_ENCODED]; + ?> + <form data-role="tocart-form" + data-product-sku="<?= $block->escapeHtmlAttr($_item->getSku()) ?>" + action="<?= $block->escapeUrl($block->getAddToCartUrl($_item)) ?>" + method="post"> + <input type="hidden" name="product" + value="<?= /* @noEscape */ (int)$_item->getEntityId() ?>"> + <input type="hidden" + name="<?= /* @noEscape */ ActionInterface::PARAM_NAME_URL_ENCODED?>" + value="<?= /* @noEscape */ $value ?>"> + <?= $block->getBlockHtml('formkey') ?> + <button type="submit" + title="<?= $block->escapeHtmlAttr(__('Add to Cart')) ?>" + class="action tocart primary"> <span><?= $block->escapeHtml(__('Add to Cart')) ?></span> </button> - <?php endif; ?> + </form> + <?php endif; ?> + <?php else:?> + <?php if ($_item->getIsSalable()):?> + <div class="stock available"> + <span><?= $block->escapeHtml(__('In stock')) ?></span> + </div> <?php else:?> - <?php if ($_item->getIsSalable()):?> - <div class="stock available"><span><?= $block->escapeHtml(__('In stock')) ?></span></div> - <?php else:?> - <div class="stock unavailable"><span><?= $block->escapeHtml(__('Out of stock')) ?></span></div> - <?php endif; ?> + <div class="stock unavailable"> + <span><?= $block->escapeHtml(__('Out of stock')) ?></span> + </div> <?php endif; ?> + <?php endif; ?> </div> <?php endif; ?> <?php if ($showAddTo):?> - <div class="secondary-addto-links actions-secondary" data-role="add-to-links"> + <div class="secondary-addto-links actions-secondary" + data-role="add-to-links"> <?php if ($addToBlock = $block->getChildBlock('addto')):?> <?= $addToBlock->setProduct($_item)->getChildHtml() ?> <?php endif; ?> </div> <?php endif; ?> </div> + <?php // phpcs:enable ?> <?php endif; ?> </div> </div> @@ -264,4 +310,15 @@ switch ($type = $block->getType()) { </div> </div> </div> + <?php if (!$block->isRedirectToCartEnabled() && $_item):?> + <script type="text/x-magento-init"> + { + "[data-role=tocart-form], .form.map.checkout": { + "catalogAddToCart": { + "product_sku": "<?= $block->escapeJs($_item->getSku()) ?>" + } + } + } + </script> + <?php endif;?> <?php endif;?> diff --git a/app/code/Magento/Catalog/view/frontend/ui_component/widget_recently_compared.xml b/app/code/Magento/Catalog/view/frontend/ui_component/widget_recently_compared.xml index 7cced8bb613c3..b0a275f720670 100644 --- a/app/code/Magento/Catalog/view/frontend/ui_component/widget_recently_compared.xml +++ b/app/code/Magento/Catalog/view/frontend/ui_component/widget_recently_compared.xml @@ -32,6 +32,7 @@ <item name="namespace" xsi:type="string">recently_compared_product</item> <item name="provider" xsi:type="string">compare-products</item> </item> + <item name="scopeConfig" xsi:type="object">Magento\Framework\App\Config\ScopeConfigInterface</item> </item> </argument> </argument> diff --git a/app/code/Magento/Catalog/view/frontend/ui_component/widget_recently_viewed.xml b/app/code/Magento/Catalog/view/frontend/ui_component/widget_recently_viewed.xml index efad08eef8c12..2af3b1210b18b 100644 --- a/app/code/Magento/Catalog/view/frontend/ui_component/widget_recently_viewed.xml +++ b/app/code/Magento/Catalog/view/frontend/ui_component/widget_recently_viewed.xml @@ -31,6 +31,7 @@ <item name="identifiersConfig" xsi:type="array"> <item name="namespace" xsi:type="string">recently_viewed_product</item> </item> + <item name="scopeConfig" xsi:type="object">Magento\Framework\App\Config\ScopeConfigInterface</item> </item> </argument> </argument> diff --git a/app/code/Magento/Catalog/view/frontend/web/js/catalog-add-to-cart.js b/app/code/Magento/Catalog/view/frontend/web/js/catalog-add-to-cart.js index 382b4ef98532b..7d3e4b3280473 100644 --- a/app/code/Magento/Catalog/view/frontend/web/js/catalog-add-to-cart.js +++ b/app/code/Magento/Catalog/view/frontend/web/js/catalog-add-to-cart.js @@ -8,8 +8,9 @@ define([ 'mage/translate', 'underscore', 'Magento_Catalog/js/product/view/product-ids-resolver', + 'Magento_Catalog/js/product/view/product-info-resolver', 'jquery-ui-modules/widget' -], function ($, $t, _, idsResolver) { +], function ($, $t, _, idsResolver, productInfoResolver) { 'use strict'; $.widget('mage.catalogAddToCart', { @@ -24,7 +25,8 @@ define([ addToCartButtonDisabledClass: 'disabled', addToCartButtonTextWhileAdding: '', addToCartButtonTextAdded: '', - addToCartButtonTextDefault: '' + addToCartButtonTextDefault: '', + productInfoResolver: productInfoResolver }, /** @inheritdoc */ @@ -90,6 +92,7 @@ define([ ajaxSubmit: function (form) { var self = this, productIds = idsResolver(form), + productInfo = self.options.productInfoResolver(form), formData; $(self.options.minicartSelector).trigger('contentLoading'); @@ -119,6 +122,7 @@ define([ $(document).trigger('ajax:addToCart', { 'sku': form.data().productSku, 'productIds': productIds, + 'productInfo': productInfo, 'form': form, 'response': res }); @@ -172,6 +176,7 @@ define([ $(document).trigger('ajax:addToCart:error', { 'sku': form.data().productSku, 'productIds': productIds, + 'productInfo': productInfo, 'form': form, 'response': res }); diff --git a/app/code/Magento/Catalog/view/frontend/web/js/product/list/toolbar.js b/app/code/Magento/Catalog/view/frontend/web/js/product/list/toolbar.js index 6589f7eb0ba48..dfc0b4291cd6e 100644 --- a/app/code/Magento/Catalog/view/frontend/web/js/product/list/toolbar.js +++ b/app/code/Magento/Catalog/view/frontend/web/js/product/list/toolbar.js @@ -23,6 +23,7 @@ define([ direction: 'product_list_dir', order: 'product_list_order', limit: 'product_list_limit', + page: 'p', modeDefault: 'grid', directionDefault: 'asc', orderDefault: 'position', @@ -81,24 +82,63 @@ define([ }, /** - * @param {String} paramName - * @param {*} paramValue - * @param {*} defaultValue + * @private */ - changeUrl: function (paramName, paramValue, defaultValue) { + getUrlParams: function () { var decode = window.decodeURIComponent, urlPaths = this.options.url.split('?'), - baseUrl = urlPaths[0], urlParams = urlPaths[1] ? urlPaths[1].split('&') : [], - paramData = {}, - parameters, i, form, params, key, input, formKey; + params = {}, + parameters, i; for (i = 0; i < urlParams.length; i++) { parameters = urlParams[i].split('='); - paramData[decode(parameters[0])] = parameters[1] !== undefined ? + params[decode(parameters[0])] = parameters[1] !== undefined ? decode(parameters[1].replace(/\+/g, '%20')) : ''; } + + return params; + }, + + /** + * @returns {String} + * @private + */ + getCurrentLimit: function () { + return this.getUrlParams()[this.options.limit] || this.options.limitDefault; + }, + + /** + * @returns {String} + * @private + */ + getCurrentPage: function () { + return this.getUrlParams()[this.options.page] || 1; + }, + + /** + * @param {String} paramName + * @param {*} paramValue + * @param {*} defaultValue + */ + changeUrl: function (paramName, paramValue, defaultValue) { + var urlPaths = this.options.url.split('?'), + baseUrl = urlPaths[0], + paramData = this.getUrlParams(), + currentPage = this.getCurrentPage(), + form, params, key, input, formKey, newPage; + + if (currentPage > 1 && paramName === this.options.limit) { + newPage = Math.floor(this.getCurrentLimit() * (currentPage - 1) / paramValue) + 1; + + if (newPage > 1) { + paramData[this.options.page] = newPage; + } else { + delete paramData[this.options.page]; + } + } + paramData[paramName] = paramValue; if (this.options.post) { @@ -130,6 +170,7 @@ define([ if (paramValue == defaultValue) { //eslint-disable-line eqeqeq delete paramData[paramName]; } + paramData = $.param(paramData); location.href = baseUrl + (paramData.length ? '?' + paramData : ''); } diff --git a/app/code/Magento/Catalog/view/frontend/web/js/product/provider-compared.js b/app/code/Magento/Catalog/view/frontend/web/js/product/provider-compared.js index 93f01682b4e48..11e092e47ae22 100644 --- a/app/code/Magento/Catalog/view/frontend/web/js/product/provider-compared.js +++ b/app/code/Magento/Catalog/view/frontend/web/js/product/provider-compared.js @@ -31,13 +31,27 @@ define([ */ dataFilter: function (data) { var providerData = this.idsStorage.prepareData(customerData.get(this.identifiersConfig.provider)().items), - result = {}; + result = {}, + productCurrentScope, + scopeId; - _.each(data, function (value, key) { - if (!providerData[key]) { - result[key] = value; - } - }); + if (typeof this.data.productCurrentScope !== 'undefined') { + productCurrentScope = this.data.productCurrentScope; + scopeId = productCurrentScope === 'store' ? window.checkout.storeId : + productCurrentScope === 'group' ? window.checkout.storeGroupId : + window.checkout.websiteId; + _.each(data, function (value, key) { + if (!providerData[productCurrentScope + '-' + scopeId + '-' + key]) { + result[key] = value; + } + }); + } else { + _.each(data, function (value, key) { + if (!providerData[key]) { + result[key] = value; + } + }); + } return result; }, diff --git a/app/code/Magento/Catalog/view/frontend/web/js/product/provider.js b/app/code/Magento/Catalog/view/frontend/web/js/product/provider.js index b29ebe7d57d1c..f246a8e3a0f9f 100644 --- a/app/code/Magento/Catalog/view/frontend/web/js/product/provider.js +++ b/app/code/Magento/Catalog/view/frontend/web/js/product/provider.js @@ -138,12 +138,17 @@ define([ filterIds: function (ids) { var _ids = {}, currentTime = new Date().getTime() / 1000, - currentProductIds = productResolver($('#product_addtocart_form')); + currentProductIds = productResolver($('#product_addtocart_form')), + productCurrentScope = this.data.productCurrentScope, + scopeId = productCurrentScope === 'store' ? window.checkout.storeId : + productCurrentScope === 'group' ? window.checkout.storeGroupId : + window.checkout.websiteId; - _.each(ids, function (id) { + _.each(ids, function (id, key) { if ( - currentTime - id['added_at'] < ~~this.idsStorage.lifetime && - !_.contains(currentProductIds, id['product_id']) + currentTime - ids[key]['added_at'] < ~~this.idsStorage.lifetime && + !_.contains(currentProductIds, ids[key]['product_id']) && + (!id.hasOwnProperty('scope_id') || ids[key]['scope_id'] === scopeId) ) { _ids[id['product_id']] = id; diff --git a/app/code/Magento/Catalog/view/frontend/web/js/product/storage/ids-storage-compare.js b/app/code/Magento/Catalog/view/frontend/web/js/product/storage/ids-storage-compare.js index 2dc6105ca24d5..bd92c5d452423 100644 --- a/app/code/Magento/Catalog/view/frontend/web/js/product/storage/ids-storage-compare.js +++ b/app/code/Magento/Catalog/view/frontend/web/js/product/storage/ids-storage-compare.js @@ -67,13 +67,26 @@ define([ * @returns {Object} data */ prepareData: function (data) { - var result = {}; + var result = {}, + scopeId; _.each(data, function (item) { - result[item.id] = { - 'added_at': new Date().getTime() / 1000, - 'product_id': item.id - }; + if (typeof item.productScope !== 'undefined') { + scopeId = item.productScope === 'store' ? window.checkout.storeId : + item.productScope === 'group' ? window.checkout.storeGroupId : + window.checkout.websiteId; + + result[item.productScope + '-' + scopeId + '-' + item.id] = { + 'added_at': new Date().getTime() / 1000, + 'product_id': item.id, + 'scope_id': scopeId + }; + } else { + result[item.id] = { + 'added_at': new Date().getTime() / 1000, + 'product_id': item.id + }; + } }); return result; diff --git a/app/code/Magento/Catalog/view/frontend/web/js/product/view/product-info-resolver.js b/app/code/Magento/Catalog/view/frontend/web/js/product/view/product-info-resolver.js new file mode 100644 index 0000000000000..f58f0ba2dfa68 --- /dev/null +++ b/app/code/Magento/Catalog/view/frontend/web/js/product/view/product-info-resolver.js @@ -0,0 +1,35 @@ +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +define([ + 'underscore', + 'Magento_Catalog/js/product/view/product-info' +], function (_, productInfo) { + 'use strict'; + + /** + * Returns info about products in form. + * + * @param {jQuery} $form + * @return {Array} + */ + return function ($form) { + var product = _.findWhere($form.serializeArray(), { + name: 'product' + }); + + if (!_.isUndefined(product)) { + productInfo().push( + { + 'id': product.value + } + ); + } + + return _.uniq(productInfo(), function (item) { + return item.id; + }); + }; +}); + diff --git a/app/code/Magento/Catalog/view/frontend/web/js/product/view/product-info.js b/app/code/Magento/Catalog/view/frontend/web/js/product/view/product-info.js new file mode 100644 index 0000000000000..2198b7b8e48b0 --- /dev/null +++ b/app/code/Magento/Catalog/view/frontend/web/js/product/view/product-info.js @@ -0,0 +1,12 @@ +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ + +define([ + 'ko' +], function (ko) { + 'use strict'; + + return ko.observableArray([]); +}); diff --git a/app/code/Magento/Catalog/view/frontend/web/js/product/view/provider.js b/app/code/Magento/Catalog/view/frontend/web/js/product/view/provider.js index 9d3746973ef58..5bcf57c035929 100644 --- a/app/code/Magento/Catalog/view/frontend/web/js/product/view/provider.js +++ b/app/code/Magento/Catalog/view/frontend/web/js/product/view/provider.js @@ -85,12 +85,17 @@ define([ * @returns {Object} */ getIdentifiers: function () { - var result = {}; + var result = {}, + productCurrentScope = this.data.productCurrentScope, + scopeId = productCurrentScope === 'store' ? window.checkout.storeId : + productCurrentScope === 'group' ? window.checkout.storeGroupId : + window.checkout.websiteId; _.each(this.data.items, function (item, key) { - result[key] = { + result[productCurrentScope + '-' + scopeId + '-' + key] = { 'added_at': new Date().getTime() / 1000, - 'product_id': key + 'product_id': key, + 'scope_id': scopeId }; }, this); diff --git a/app/code/Magento/Catalog/view/frontend/web/template/product/image_with_borders.html b/app/code/Magento/Catalog/view/frontend/web/template/product/image_with_borders.html index d59237c190f71..c4e71356d46eb 100644 --- a/app/code/Magento/Catalog/view/frontend/web/template/product/image_with_borders.html +++ b/app/code/Magento/Catalog/view/frontend/web/template/product/image_with_borders.html @@ -4,8 +4,8 @@ * See COPYING.txt for license details. */ --> -<span class="product-image-container" data-bind="style: {width: width + 'px'}"> +<span class="product-image-container" data-bind="style: {width: width/2 + 'px'}"> <span class="product-image-wrapper" data-bind="style: {'padding-bottom': height/width*100 + '%'}"> - <img class="product-image-photo" data-bind="attr: {src: src, alt: alt}, style: {width: width + 'px', height: height + 'px'}" /> + <img class="product-image-photo" data-bind="attr: {src: src, alt: alt}, style: {width: 'auto', height: 'auto'}" /> </span> </span> diff --git a/app/code/Magento/CatalogGraphQl/DataProvider/Product/SearchCriteriaBuilder.php b/app/code/Magento/CatalogGraphQl/DataProvider/Product/SearchCriteriaBuilder.php index 0e92bbbab4259..1057d21283ea8 100644 --- a/app/code/Magento/CatalogGraphQl/DataProvider/Product/SearchCriteriaBuilder.php +++ b/app/code/Magento/CatalogGraphQl/DataProvider/Product/SearchCriteriaBuilder.php @@ -123,13 +123,13 @@ private function addVisibilityFilter(SearchCriteriaInterface $searchCriteria, bo { if ($isFilter && $isSearch) { // Index already contains products filtered by visibility: catalog, search, both - return ; + return; } $visibilityIds = $isSearch ? $this->visibility->getVisibleInSearchIds() : $this->visibility->getVisibleInCatalogIds(); - $this->addFilter($searchCriteria, 'visibility', $visibilityIds); + $this->addFilter($searchCriteria, 'visibility', $visibilityIds, 'in'); } /** @@ -155,13 +155,20 @@ private function preparePriceAggregation(SearchCriteriaInterface $searchCriteria * @param SearchCriteriaInterface $searchCriteria * @param string $field * @param mixed $value + * @param string|null $condition */ - private function addFilter(SearchCriteriaInterface $searchCriteria, string $field, $value): void - { + private function addFilter( + SearchCriteriaInterface $searchCriteria, + string $field, + $value, + ?string $condition = null + ): void { $filter = $this->filterBuilder ->setField($field) ->setValue($value) + ->setConditionType($condition) ->create(); + $this->filterGroupBuilder->addFilter($filter); $filterGroups = $searchCriteria->getFilterGroups(); $filterGroups[] = $this->filterGroupBuilder->create(); diff --git a/app/code/Magento/CatalogGraphQl/Model/AttributesJoiner.php b/app/code/Magento/CatalogGraphQl/Model/AttributesJoiner.php index b3a9672a47010..69592657241a0 100644 --- a/app/code/Magento/CatalogGraphQl/Model/AttributesJoiner.php +++ b/app/code/Magento/CatalogGraphQl/Model/AttributesJoiner.php @@ -45,7 +45,7 @@ public function __construct(array $fieldToAttributeMap = []) * @param AbstractCollection $collection * @return void */ - public function join(FieldNode $fieldNode, AbstractCollection $collection) : void + public function join(FieldNode $fieldNode, AbstractCollection $collection): void { foreach ($this->getQueryFields($fieldNode) as $field) { $this->addFieldToCollection($collection, $field); @@ -60,19 +60,20 @@ public function join(FieldNode $fieldNode, AbstractCollection $collection) : voi */ public function getQueryFields(FieldNode $fieldNode): array { - if (!isset($this->queryFields[$fieldNode->name->value])) { - $this->queryFields[$fieldNode->name->value] = []; + if (null === $this->getFieldNodeSelections($fieldNode)) { $query = $fieldNode->selectionSet->selections; + $selectedFields = []; /** @var FieldNode $field */ foreach ($query as $field) { if ($field->kind === 'InlineFragment') { continue; } - $this->queryFields[$fieldNode->name->value][] = $field->name->value; + $selectedFields[] = $field->name->value; } + $this->setSelectionsForFieldNode($fieldNode, $selectedFields); } - return $this->queryFields[$fieldNode->name->value]; + return $this->getFieldNodeSelections($fieldNode); } /** @@ -83,7 +84,7 @@ public function getQueryFields(FieldNode $fieldNode): array * @param AbstractCollection $collection * @param string $field */ - private function addFieldToCollection(AbstractCollection $collection, string $field) + private function addFieldToCollection(AbstractCollection $collection, string $field): void { $attribute = isset($this->fieldToAttributeMap[$field]) ? $this->fieldToAttributeMap[$field] : $field; @@ -99,4 +100,28 @@ private function addFieldToCollection(AbstractCollection $collection, string $fi } } } + + /** + * Get the fields selections for a query node + * + * @param FieldNode $fieldNode + * @return array|null + */ + private function getFieldNodeSelections(FieldNode $fieldNode): ?array + { + return $this->queryFields[$fieldNode->name->value][$fieldNode->name->loc->start] ?? null; + } + + /** + * Set the field selections for a query node + * + * Index nodes by name and position so nodes with same name don't collide + * + * @param FieldNode $fieldNode + * @param array $selectedFields + */ + private function setSelectionsForFieldNode(FieldNode $fieldNode, array $selectedFields): void + { + $this->queryFields[$fieldNode->name->value][$fieldNode->name->loc->start] = $selectedFields; + } } diff --git a/app/code/Magento/CatalogGraphQl/Model/Category/CategoryFilter.php b/app/code/Magento/CatalogGraphQl/Model/Category/CategoryFilter.php index 2c03550404ae0..09694b7cf1a0c 100644 --- a/app/code/Magento/CatalogGraphQl/Model/Category/CategoryFilter.php +++ b/app/code/Magento/CatalogGraphQl/Model/Category/CategoryFilter.php @@ -7,16 +7,20 @@ namespace Magento\CatalogGraphQl\Model\Category; +use Magento\Catalog\Api\CategoryListInterface; use Magento\Catalog\Api\Data\CategoryInterface; -use Magento\Catalog\Model\ResourceModel\Category\Collection; use Magento\Framework\App\Config\ScopeConfigInterface; use Magento\Framework\Exception\InputException; +use Magento\Framework\GraphQl\Exception\GraphQlInputException; +use Magento\Framework\GraphQl\Query\Resolver\Argument\SearchCriteria\ArgumentApplier\Filter; +use Magento\Framework\Search\Adapter\Mysql\Query\Builder\Match; +use Magento\Search\Model\Query; use Magento\Store\Api\Data\StoreInterface; use Magento\Store\Model\ScopeInterface; -use Magento\Search\Model\Query; +use Magento\Framework\GraphQl\Query\Resolver\Argument\SearchCriteria\Builder; /** - * Category filter allows to filter collection using 'id, url_key, name' from search criteria. + * Category filter allows filtering category results by attributes. */ class CategoryFilter { @@ -25,78 +29,106 @@ class CategoryFilter */ private $scopeConfig; + /** + * @var CategoryListInterface + */ + private $categoryList; + + /** + * @var Builder + */ + private $searchCriteriaBuilder; + /** * @param ScopeConfigInterface $scopeConfig + * @param CategoryListInterface $categoryList + * @param Builder $searchCriteriaBuilder */ public function __construct( - ScopeConfigInterface $scopeConfig + ScopeConfigInterface $scopeConfig, + CategoryListInterface $categoryList, + Builder $searchCriteriaBuilder ) { $this->scopeConfig = $scopeConfig; + $this->categoryList = $categoryList; + $this->searchCriteriaBuilder = $searchCriteriaBuilder; } /** - * Filter for filtering the requested categories id's based on url_key, ids, name in the result. + * Search for categories * - * @param array $args - * @param Collection $categoryCollection + * @param array $criteria * @param StoreInterface $store + * @return int[] * @throws InputException */ - public function applyFilters(array $args, Collection $categoryCollection, StoreInterface $store) + public function getResult(array $criteria, StoreInterface $store) { - $categoryCollection->addAttributeToFilter(CategoryInterface::KEY_IS_ACTIVE, ['eq' => 1]); - foreach ($args['filters'] as $field => $cond) { - foreach ($cond as $condType => $value) { - if ($field === 'ids') { - $categoryCollection->addIdFilter($value); - } else { - $this->addAttributeFilter($categoryCollection, $field, $condType, $value, $store); - } - } + $categoryIds = []; + $criteria[Filter::ARGUMENT_NAME] = $this->formatMatchFilters($criteria['filters'], $store); + $criteria[Filter::ARGUMENT_NAME][CategoryInterface::KEY_IS_ACTIVE] = ['eq' => 1]; + $searchCriteria = $this->searchCriteriaBuilder->build('categoryList', $criteria); + $pageSize = $criteria['pageSize'] ?? 20; + $currentPage = $criteria['currentPage'] ?? 1; + $searchCriteria->setPageSize($pageSize)->setCurrentPage($currentPage); + + $categories = $this->categoryList->getList($searchCriteria); + foreach ($categories->getItems() as $category) { + $categoryIds[] = (int)$category->getId(); } - } - /** - * Add filter to category collection - * - * @param Collection $categoryCollection - * @param string $field - * @param string $condType - * @param string|array $value - * @param StoreInterface $store - * @throws InputException - */ - private function addAttributeFilter($categoryCollection, $field, $condType, $value, $store) - { - if ($condType === 'match') { - $this->addMatchFilter($categoryCollection, $field, $value, $store); - return; + $totalPages = 0; + if ($categories->getTotalCount() > 0 && $searchCriteria->getPageSize() > 0) { + $totalPages = ceil($categories->getTotalCount() / $searchCriteria->getPageSize()); } - $categoryCollection->addAttributeToFilter($field, [$condType => $value]); + if ($searchCriteria->getCurrentPage() > $totalPages && $categories->getTotalCount() > 0) { + throw new GraphQlInputException( + __( + 'currentPage value %1 specified is greater than the %2 page(s) available.', + [$searchCriteria->getCurrentPage(), $totalPages] + ) + ); + } + + return [ + 'category_ids' => $categoryIds, + 'total_count' => $categories->getTotalCount(), + 'page_info' => [ + 'total_pages' => $totalPages, + 'page_size' => $searchCriteria->getPageSize(), + 'current_page' => $searchCriteria->getCurrentPage(), + ] + ]; } /** - * Add match filter to collection + * Format match filters to behave like fuzzy match * - * @param Collection $categoryCollection - * @param string $field - * @param string $value + * @param array $filters * @param StoreInterface $store + * @return array * @throws InputException */ - private function addMatchFilter($categoryCollection, $field, $value, $store) + private function formatMatchFilters(array $filters, StoreInterface $store): array { $minQueryLength = $this->scopeConfig->getValue( Query::XML_PATH_MIN_QUERY_LENGTH, ScopeInterface::SCOPE_STORE, $store ); - $searchValue = str_replace('%', '', $value); - $matchLength = strlen($searchValue); - if ($matchLength < $minQueryLength) { - throw new InputException(__('Invalid match filter')); - } - $categoryCollection->addAttributeToFilter($field, ['like' => "%{$searchValue}%"]); + foreach ($filters as $filter => $condition) { + $conditionType = current(array_keys($condition)); + if ($conditionType === 'match') { + $searchValue = trim(str_replace(Match::SPECIAL_CHARACTERS, '', $condition[$conditionType])); + $matchLength = strlen($searchValue); + if ($matchLength < $minQueryLength) { + throw new InputException(__('Invalid match filter. Minimum length is %1.', $minQueryLength)); + } + unset($filters[$filter]['match']); + $filters[$filter]['like'] = '%' . $searchValue . '%'; + } + } + return $filters; } } diff --git a/app/code/Magento/CatalogGraphQl/Model/Category/CategoryFilterAttributesForAst.php b/app/code/Magento/CatalogGraphQl/Model/Category/CategoryFilterAttributesForAst.php new file mode 100644 index 0000000000000..287f98e9f6848 --- /dev/null +++ b/app/code/Magento/CatalogGraphQl/Model/Category/CategoryFilterAttributesForAst.php @@ -0,0 +1,87 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +declare(strict_types=1); + +namespace Magento\CatalogGraphQl\Model\Category; + +use Magento\Framework\GraphQl\ConfigInterface; +use Magento\Framework\GraphQl\Query\Resolver\Argument\FieldEntityAttributesInterface; + +/** + * Retrieve filterable attributes for Category queries + */ +class CategoryFilterAttributesForAst implements FieldEntityAttributesInterface +{ + /** + * Map schema fields to entity attributes + * + * @var array + */ + private $fieldMapping = [ + 'ids' => 'entity_id' + ]; + + /** + * @var array + */ + private $additionalFields = [ + 'is_active' + ]; + + /** + * @var ConfigInterface + */ + private $config; + + /** + * @param ConfigInterface $config + * @param array $additionalFields + * @param array $attributeFieldMapping + */ + public function __construct( + ConfigInterface $config, + array $additionalFields = [], + array $attributeFieldMapping = [] + ) { + $this->config = $config; + $this->additionalFields = array_merge($this->additionalFields, $additionalFields); + $this->fieldMapping = array_merge($this->fieldMapping, $attributeFieldMapping); + } + + /** + * @inheritdoc + * + * Gather attributes for Category filtering + * Example format ['attributeNameInGraphQl' => ['type' => 'String'. 'fieldName' => 'attributeNameInSearchCriteria']] + * + * @return array + */ + public function getEntityAttributes() : array + { + $categoryFilterType = $this->config->getConfigElement('CategoryFilterInput'); + + if (!$categoryFilterType) { + throw new \LogicException(__("CategoryFilterInput type not defined in schema.")); + } + + $fields = []; + foreach ($categoryFilterType->getFields() as $field) { + $fields[$field->getName()] = [ + 'type' => 'String', + 'fieldName' => $this->fieldMapping[$field->getName()] ?? $field->getName(), + ]; + } + + foreach ($this->additionalFields as $additionalField) { + $fields[$additionalField] = [ + 'type' => 'String', + 'fieldName' => $additionalField, + ]; + } + + return $fields; + } +} diff --git a/app/code/Magento/CatalogGraphQl/Model/Config/FilterAttributeReader.php b/app/code/Magento/CatalogGraphQl/Model/Config/FilterAttributeReader.php index 4f3a88cc788df..6976086e74890 100644 --- a/app/code/Magento/CatalogGraphQl/Model/Config/FilterAttributeReader.php +++ b/app/code/Magento/CatalogGraphQl/Model/Config/FilterAttributeReader.php @@ -10,16 +10,15 @@ use Magento\Framework\Config\ReaderInterface; use Magento\Framework\GraphQl\Schema\Type\Entity\MapperInterface; use Magento\Catalog\Model\ResourceModel\Product\Attribute\CollectionFactory; -use Magento\Catalog\Model\ResourceModel\Product\Attribute\Collection; use Magento\Catalog\Model\ResourceModel\Eav\Attribute; /** * Adds custom/eav attributes to product filter type in the GraphQL config. * * Product Attribute should satisfy the following criteria: - * - Attribute is searchable - * - "Visible in Advanced Search" is set to "Yes" - * - Attribute of type "Select" must have options + * - (Attribute is searchable AND "Visible in Advanced Search" is set to "Yes") + * - OR attribute is "Used in Layered Navigation" + * - AND Attribute of type "Select" must have options */ class FilterAttributeReader implements ReaderInterface { @@ -77,7 +76,7 @@ public function read($scope = null) : array $typeNames = $this->mapper->getMappedTypes(self::ENTITY_TYPE); $config = []; - foreach ($this->getAttributeCollection() as $attribute) { + foreach ($this->getFilterAttributes() as $attribute) { $attributeCode = $attribute->getAttributeCode(); foreach ($typeNames as $typeName) { @@ -120,15 +119,25 @@ private function getFilterType(Attribute $attribute): string } /** - * Create attribute collection + * Get attributes to use in product filter input * - * @return Collection|\Magento\Catalog\Model\ResourceModel\Eav\Attribute[] + * @return array */ - private function getAttributeCollection() + private function getFilterAttributes(): array { - return $this->collectionFactory->create() + $filterableAttributes = $this->collectionFactory + ->create() + ->addHasOptionsFilter() + ->addIsFilterableFilter() + ->getItems(); + + $searchableAttributes = $this->collectionFactory + ->create() ->addHasOptionsFilter() ->addIsSearchableFilter() - ->addDisplayInAdvancedSearchFilter(); + ->addDisplayInAdvancedSearchFilter() + ->getItems(); + + return $filterableAttributes + $searchableAttributes; } } diff --git a/app/code/Magento/CatalogGraphQl/Model/Product/Option/DateType.php b/app/code/Magento/CatalogGraphQl/Model/Product/Option/DateType.php index cd582ffda9244..b3eac723c05e5 100644 --- a/app/code/Magento/CatalogGraphQl/Model/Product/Option/DateType.php +++ b/app/code/Magento/CatalogGraphQl/Model/Product/Option/DateType.php @@ -45,6 +45,9 @@ private function formatValues($values) { if (isset($values[$this->getOption()->getId()])) { $value = $values[$this->getOption()->getId()]; + if (isset($value['date']) || isset($value['day'], $value['month'], $value['year'])) { + return $values; + } $dateTime = \DateTime::createFromFormat(DateTime::DATETIME_PHP_FORMAT, $value); if ($dateTime === false) { diff --git a/app/code/Magento/CatalogGraphQl/Model/Resolver/CategoriesQuery.php b/app/code/Magento/CatalogGraphQl/Model/Resolver/CategoriesQuery.php new file mode 100644 index 0000000000000..eb6708dc48f01 --- /dev/null +++ b/app/code/Magento/CatalogGraphQl/Model/Resolver/CategoriesQuery.php @@ -0,0 +1,103 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +declare(strict_types=1); + +namespace Magento\CatalogGraphQl\Model\Resolver; + +use Magento\CatalogGraphQl\Model\Resolver\Products\DataProvider\ExtractDataFromCategoryTree; +use Magento\Framework\Exception\InputException; +use Magento\Framework\GraphQl\Config\Element\Field; +use Magento\Framework\GraphQl\Exception\GraphQlInputException; +use Magento\Framework\GraphQl\Query\ResolverInterface; +use Magento\Framework\GraphQl\Schema\Type\ResolveInfo; +use Magento\CatalogGraphQl\Model\Resolver\Products\DataProvider\CategoryTree; +use Magento\CatalogGraphQl\Model\Category\CategoryFilter; + +/** + * Categories resolver, used for GraphQL category data request processing. + */ +class CategoriesQuery implements ResolverInterface +{ + /** + * @var CategoryTree + */ + private $categoryTree; + + /** + * @var CategoryFilter + */ + private $categoryFilter; + + /** + * @var ExtractDataFromCategoryTree + */ + private $extractDataFromCategoryTree; + + /** + * @param CategoryTree $categoryTree + * @param ExtractDataFromCategoryTree $extractDataFromCategoryTree + * @param CategoryFilter $categoryFilter + */ + public function __construct( + CategoryTree $categoryTree, + ExtractDataFromCategoryTree $extractDataFromCategoryTree, + CategoryFilter $categoryFilter + ) { + $this->categoryTree = $categoryTree; + $this->extractDataFromCategoryTree = $extractDataFromCategoryTree; + $this->categoryFilter = $categoryFilter; + } + + /** + * @inheritdoc + */ + public function resolve(Field $field, $context, ResolveInfo $info, array $value = null, array $args = null) + { + $store = $context->getExtensionAttributes()->getStore(); + + if (isset($args['currentPage']) && $args['currentPage'] < 1) { + throw new GraphQlInputException(__('currentPage value must be greater than 0.')); + } + if (isset($args['pageSize']) && $args['pageSize'] < 1) { + throw new GraphQlInputException(__('pageSize value must be greater than 0.')); + } + if (!isset($args['filters'])) { + //When no filters are specified, get the root category + $args['filters']['ids'] = ['eq' => $store->getRootCategoryId()]; + } + + try { + $filterResult = $this->categoryFilter->getResult($args, $store); + } catch (InputException $e) { + throw new GraphQlInputException(__($e->getMessage())); + } + + $rootCategoryIds = $filterResult['category_ids']; + $filterResult['items'] = $this->fetchCategories($rootCategoryIds, $info); + return $filterResult; + } + + /** + * Fetch category tree data + * + * @param array $categoryIds + * @param ResolveInfo $info + * @return array + */ + private function fetchCategories(array $categoryIds, ResolveInfo $info) + { + $fetchedCategories = []; + foreach ($categoryIds as $categoryId) { + $categoryTree = $this->categoryTree->getTree($info, $categoryId); + if (empty($categoryTree)) { + continue; + } + $fetchedCategories[] = current($this->extractDataFromCategoryTree->execute($categoryTree)); + } + + return $fetchedCategories; + } +} diff --git a/app/code/Magento/CatalogGraphQl/Model/Resolver/Category/CategoriesIdentity.php b/app/code/Magento/CatalogGraphQl/Model/Resolver/Category/CategoriesIdentity.php index dd18c463b98de..0a679eb064226 100644 --- a/app/code/Magento/CatalogGraphQl/Model/Resolver/Category/CategoriesIdentity.php +++ b/app/code/Magento/CatalogGraphQl/Model/Resolver/Category/CategoriesIdentity.php @@ -7,6 +7,7 @@ namespace Magento\CatalogGraphQl\Model\Resolver\Category; +use Magento\Catalog\Model\Category; use Magento\Framework\GraphQl\Query\Resolver\IdentityInterface; /** @@ -15,7 +16,7 @@ class CategoriesIdentity implements IdentityInterface { /** @var string */ - private $cacheTag = \Magento\Catalog\Model\Category::CACHE_TAG; + private $cacheTag = Category::CACHE_TAG; /** * Get category IDs from resolved data @@ -26,8 +27,9 @@ class CategoriesIdentity implements IdentityInterface public function getIdentities(array $resolvedData): array { $ids = []; - if (!empty($resolvedData)) { - foreach ($resolvedData as $category) { + $resolvedCategories = $resolvedData['items'] ?? $resolvedData; + if (!empty($resolvedCategories)) { + foreach ($resolvedCategories as $category) { $ids[] = sprintf('%s_%s', $this->cacheTag, $category['id']); } if (!empty($ids)) { diff --git a/app/code/Magento/CatalogGraphQl/Model/Resolver/Category/Image.php b/app/code/Magento/CatalogGraphQl/Model/Resolver/Category/Image.php index a06a8252d5a5e..5de7fdc10ff4a 100644 --- a/app/code/Magento/CatalogGraphQl/Model/Resolver/Category/Image.php +++ b/app/code/Magento/CatalogGraphQl/Model/Resolver/Category/Image.php @@ -13,6 +13,8 @@ use Magento\Framework\Exception\LocalizedException; use Magento\Store\Api\Data\StoreInterface; use Magento\Framework\Filesystem\DirectoryList; +use Magento\Catalog\Model\Category\FileInfo; +use Magento\Framework\GraphQl\Exception\GraphQlInputException; /** * Resolve category image to a fully qualified URL @@ -22,12 +24,19 @@ class Image implements ResolverInterface /** @var DirectoryList */ private $directoryList; + /** @var FileInfo */ + private $fileInfo; + /** * @param DirectoryList $directoryList + * @param FileInfo $fileInfo */ - public function __construct(DirectoryList $directoryList) - { + public function __construct( + DirectoryList $directoryList, + FileInfo $fileInfo + ) { $this->directoryList = $directoryList; + $this->fileInfo = $fileInfo; } /** @@ -45,21 +54,40 @@ public function resolve( } /** @var \Magento\Catalog\Model\Category $category */ $category = $value['model']; - $imagePath = $category->getImage(); + $imagePath = $category->getData('image'); if (empty($imagePath)) { return null; } /** @var StoreInterface $store */ $store = $context->getExtensionAttributes()->getStore(); - $baseUrl = $store->getBaseUrl('media'); + $baseUrl = $store->getBaseUrl(); - $mediaPath = $this->directoryList->getUrlPath('media'); - $pos = strpos($imagePath, $mediaPath); - if ($pos !== false) { - $imagePath = substr($imagePath, $pos + strlen($mediaPath), strlen($baseUrl)); + $filenameWithMedia = $this->fileInfo->isBeginsWithMediaDirectoryPath($imagePath) + ? $imagePath : $this->formatFileNameWithMediaCategoryFolder($imagePath); + + if (!$this->fileInfo->isExist($filenameWithMedia)) { + throw new GraphQlInputException(__('Category image not found.')); } - $imageUrl = rtrim($baseUrl, '/') . '/' . ltrim($imagePath, '/'); - return $imageUrl; + // return full url + return rtrim($baseUrl, '/') . $filenameWithMedia; + } + + /** + * Format category media folder to filename + * + * @param string $fileName + * @return string + */ + private function formatFileNameWithMediaCategoryFolder(string $fileName): string + { + // phpcs:ignore Magento2.Functions.DiscouragedFunction + $baseFileName = basename($fileName); + return '/' + . $this->directoryList->getUrlPath('media') + . '/' + . ltrim(FileInfo::ENTITY_MEDIA_PATH, '/') + . '/' + . $baseFileName; } } diff --git a/app/code/Magento/CatalogGraphQl/Model/Resolver/Category/Products.php b/app/code/Magento/CatalogGraphQl/Model/Resolver/Category/Products.php index abc5ae7e1da7f..85b86f313de4d 100644 --- a/app/code/Magento/CatalogGraphQl/Model/Resolver/Category/Products.php +++ b/app/code/Magento/CatalogGraphQl/Model/Resolver/Category/Products.php @@ -7,16 +7,12 @@ namespace Magento\CatalogGraphQl\Model\Resolver\Category; -use Magento\Catalog\Api\ProductRepositoryInterface; use Magento\CatalogGraphQl\DataProvider\Product\SearchCriteriaBuilder; -use Magento\CatalogGraphQl\Model\Resolver\Products\Query\Search; -use Magento\Framework\App\ObjectManager; use Magento\Framework\GraphQl\Config\Element\Field; use Magento\Framework\GraphQl\Query\ResolverInterface; use Magento\Framework\GraphQl\Schema\Type\ResolveInfo; -use Magento\Framework\GraphQl\Query\Resolver\Argument\SearchCriteria\Builder; -use Magento\CatalogGraphQl\Model\Resolver\Products\Query\Filter; use Magento\Framework\GraphQl\Exception\GraphQlInputException; +use Magento\CatalogGraphQl\Model\Resolver\Products\Query\ProductQueryInterface; /** * Category products resolver, used by GraphQL endpoints to retrieve products assigned to a category @@ -24,24 +20,7 @@ class Products implements ResolverInterface { /** - * @var \Magento\Catalog\Api\ProductRepositoryInterface - */ - private $productRepository; - - /** - * @var Builder - * @deprecated - */ - private $searchCriteriaBuilder; - - /** - * @var Filter - * @deprecated - */ - private $filterQuery; - - /** - * @var Search + * @var ProductQueryInterface */ private $searchQuery; @@ -51,25 +30,15 @@ class Products implements ResolverInterface private $searchApiCriteriaBuilder; /** - * @param ProductRepositoryInterface $productRepository - * @param Builder $searchCriteriaBuilder - * @param Filter $filterQuery - * @param Search $searchQuery + * @param ProductQueryInterface $searchQuery * @param SearchCriteriaBuilder $searchApiCriteriaBuilder */ public function __construct( - ProductRepositoryInterface $productRepository, - Builder $searchCriteriaBuilder, - Filter $filterQuery, - Search $searchQuery = null, - SearchCriteriaBuilder $searchApiCriteriaBuilder = null + ProductQueryInterface $searchQuery, + SearchCriteriaBuilder $searchApiCriteriaBuilder ) { - $this->productRepository = $productRepository; - $this->searchCriteriaBuilder = $searchCriteriaBuilder; - $this->filterQuery = $filterQuery; - $this->searchQuery = $searchQuery ?? ObjectManager::getInstance()->get(Search::class); - $this->searchApiCriteriaBuilder = $searchApiCriteriaBuilder ?? - ObjectManager::getInstance()->get(SearchCriteriaBuilder::class); + $this->searchQuery = $searchQuery; + $this->searchApiCriteriaBuilder = $searchApiCriteriaBuilder; } /** @@ -94,18 +63,17 @@ public function resolve( 'eq' => $value['id'] ] ]; - $searchCriteria = $this->searchApiCriteriaBuilder->build($args, false); - $searchResult = $this->searchQuery->getResult($searchCriteria, $info); + $searchResult = $this->searchQuery->getResult($args, $info); //possible division by 0 - if ($searchCriteria->getPageSize()) { - $maxPages = ceil($searchResult->getTotalCount() / $searchCriteria->getPageSize()); + if ($searchResult->getPageSize()) { + $maxPages = ceil($searchResult->getTotalCount() / $searchResult->getPageSize()); } else { $maxPages = 0; } - $currentPage = $searchCriteria->getCurrentPage(); - if ($searchCriteria->getCurrentPage() > $maxPages && $searchResult->getTotalCount() > 0) { + $currentPage = $searchResult->getCurrentPage(); + if ($searchResult->getCurrentPage() > $maxPages && $searchResult->getTotalCount() > 0) { $currentPage = new GraphQlInputException( __( 'currentPage value %1 specified is greater than the number of pages available.', @@ -118,7 +86,7 @@ public function resolve( 'total_count' => $searchResult->getTotalCount(), 'items' => $searchResult->getProductsSearchResult(), 'page_info' => [ - 'page_size' => $searchCriteria->getPageSize(), + 'page_size' => $searchResult->getPageSize(), 'current_page' => $currentPage, 'total_pages' => $maxPages ] diff --git a/app/code/Magento/CatalogGraphQl/Model/Resolver/CategoryList.php b/app/code/Magento/CatalogGraphQl/Model/Resolver/CategoryList.php index 6b8949d612829..f32c5a1f38425 100644 --- a/app/code/Magento/CatalogGraphQl/Model/Resolver/CategoryList.php +++ b/app/code/Magento/CatalogGraphQl/Model/Resolver/CategoryList.php @@ -10,12 +10,11 @@ use Magento\CatalogGraphQl\Model\Resolver\Products\DataProvider\ExtractDataFromCategoryTree; use Magento\Framework\Exception\InputException; use Magento\Framework\GraphQl\Config\Element\Field; -use Magento\Framework\GraphQl\Exception\GraphQlNoSuchEntityException; +use Magento\Framework\GraphQl\Exception\GraphQlInputException; use Magento\Framework\GraphQl\Query\ResolverInterface; use Magento\Framework\GraphQl\Schema\Type\ResolveInfo; use Magento\CatalogGraphQl\Model\Resolver\Products\DataProvider\CategoryTree; use Magento\CatalogGraphQl\Model\Category\CategoryFilter; -use Magento\Catalog\Model\ResourceModel\Category\CollectionFactory; /** * Category List resolver, used for GraphQL category data request processing. @@ -27,11 +26,6 @@ class CategoryList implements ResolverInterface */ private $categoryTree; - /** - * @var CollectionFactory - */ - private $collectionFactory; - /** * @var CategoryFilter */ @@ -46,18 +40,15 @@ class CategoryList implements ResolverInterface * @param CategoryTree $categoryTree * @param ExtractDataFromCategoryTree $extractDataFromCategoryTree * @param CategoryFilter $categoryFilter - * @param CollectionFactory $collectionFactory */ public function __construct( CategoryTree $categoryTree, ExtractDataFromCategoryTree $extractDataFromCategoryTree, - CategoryFilter $categoryFilter, - CollectionFactory $collectionFactory + CategoryFilter $categoryFilter ) { $this->categoryTree = $categoryTree; $this->extractDataFromCategoryTree = $extractDataFromCategoryTree; $this->categoryFilter = $categoryFilter; - $this->collectionFactory = $collectionFactory; } /** @@ -70,24 +61,17 @@ public function resolve(Field $field, $context, ResolveInfo $info, array $value } $store = $context->getExtensionAttributes()->getStore(); - $rootCategoryIds = []; if (!isset($args['filters'])) { - $rootCategoryIds[] = (int)$store->getRootCategoryId(); - } else { - $categoryCollection = $this->collectionFactory->create(); - try { - $this->categoryFilter->applyFilters($args, $categoryCollection, $store); - } catch (InputException $e) { - return []; - } - - foreach ($categoryCollection as $category) { - $rootCategoryIds[] = (int)$category->getId(); - } + $args['filters']['ids'] = ['eq' => $store->getRootCategoryId()]; + } + try { + $filterResults = $this->categoryFilter->getResult($args, $store); + $rootCategoryIds = $filterResults['category_ids']; + } catch (InputException $e) { + throw new GraphQlInputException(__($e->getMessage())); } - $result = $this->fetchCategories($rootCategoryIds, $info); - return $result; + return $this->fetchCategories($rootCategoryIds, $info); } /** @@ -96,7 +80,6 @@ public function resolve(Field $field, $context, ResolveInfo $info, array $value * @param array $categoryIds * @param ResolveInfo $info * @return array - * @throws GraphQlNoSuchEntityException */ private function fetchCategories(array $categoryIds, ResolveInfo $info) { diff --git a/app/code/Magento/CatalogGraphQl/Model/Resolver/Product/EntityIdToId.php b/app/code/Magento/CatalogGraphQl/Model/Resolver/Product/EntityIdToId.php index ada3caad5f9f8..701ee70204486 100644 --- a/app/code/Magento/CatalogGraphQl/Model/Resolver/Product/EntityIdToId.php +++ b/app/code/Magento/CatalogGraphQl/Model/Resolver/Product/EntityIdToId.php @@ -53,7 +53,7 @@ public function resolve( $product = $value['model']; $productId = $product->getData( - $this->metadataPool->getMetadata(ProductInterface::class)->getLinkField() + $this->metadataPool->getMetadata(ProductInterface::class)->getIdentifierField() ); return $productId; diff --git a/app/code/Magento/CatalogGraphQl/Model/Resolver/Product/PriceRange.php b/app/code/Magento/CatalogGraphQl/Model/Resolver/Product/PriceRange.php index 9396b1f02b975..dbb52f2010930 100644 --- a/app/code/Magento/CatalogGraphQl/Model/Resolver/Product/PriceRange.php +++ b/app/code/Magento/CatalogGraphQl/Model/Resolver/Product/PriceRange.php @@ -86,7 +86,7 @@ private function getMinimumProductPrice(SaleableInterface $product, StoreInterfa $priceProvider = $this->priceProviderPool->getProviderByProductType($product->getTypeId()); $regularPrice = $priceProvider->getMinimalRegularPrice($product)->getValue(); $finalPrice = $priceProvider->getMinimalFinalPrice($product)->getValue(); - $minPriceArray = $this->formatPrice($regularPrice, $finalPrice, $store); + $minPriceArray = $this->formatPrice((float) $regularPrice, (float) $finalPrice, $store); $minPriceArray['model'] = $product; return $minPriceArray; } @@ -103,7 +103,7 @@ private function getMaximumProductPrice(SaleableInterface $product, StoreInterfa $priceProvider = $this->priceProviderPool->getProviderByProductType($product->getTypeId()); $regularPrice = $priceProvider->getMaximalRegularPrice($product)->getValue(); $finalPrice = $priceProvider->getMaximalFinalPrice($product)->getValue(); - $maxPriceArray = $this->formatPrice($regularPrice, $finalPrice, $store); + $maxPriceArray = $this->formatPrice((float) $regularPrice, (float) $finalPrice, $store); $maxPriceArray['model'] = $product; return $maxPriceArray; } diff --git a/app/code/Magento/CatalogGraphQl/Model/Resolver/Products.php b/app/code/Magento/CatalogGraphQl/Model/Resolver/Products.php index 691f93e4148bc..e3d9ba2a9b3c6 100644 --- a/app/code/Magento/CatalogGraphQl/Model/Resolver/Products.php +++ b/app/code/Magento/CatalogGraphQl/Model/Resolver/Products.php @@ -7,6 +7,7 @@ namespace Magento\CatalogGraphQl\Model\Resolver; +use Magento\CatalogGraphQl\Model\Resolver\Products\Query\ProductQueryInterface; use Magento\Framework\GraphQl\Schema\Type\ResolveInfo; use Magento\CatalogGraphQl\Model\Resolver\Products\Query\Filter; use Magento\CatalogGraphQl\Model\Resolver\Products\Query\Search; @@ -24,51 +25,24 @@ class Products implements ResolverInterface { /** - * @var Builder - * @deprecated - */ - private $searchCriteriaBuilder; - - /** - * @var Search + * @var ProductQueryInterface */ private $searchQuery; - /** - * @var Filter - * @deprecated - */ - private $filterQuery; - - /** - * @var SearchFilter - * @deprecated - */ - private $searchFilter; - /** * @var SearchCriteriaBuilder */ private $searchApiCriteriaBuilder; /** - * @param Builder $searchCriteriaBuilder - * @param Search $searchQuery - * @param Filter $filterQuery - * @param SearchFilter $searchFilter + * @param ProductQueryInterface $searchQuery * @param SearchCriteriaBuilder|null $searchApiCriteriaBuilder */ public function __construct( - Builder $searchCriteriaBuilder, - Search $searchQuery, - Filter $filterQuery, - SearchFilter $searchFilter, + ProductQueryInterface $searchQuery, SearchCriteriaBuilder $searchApiCriteriaBuilder = null ) { - $this->searchCriteriaBuilder = $searchCriteriaBuilder; $this->searchQuery = $searchQuery; - $this->filterQuery = $filterQuery; - $this->searchFilter = $searchFilter; $this->searchApiCriteriaBuilder = $searchApiCriteriaBuilder ?? \Magento\Framework\App\ObjectManager::getInstance()->get(SearchCriteriaBuilder::class); } @@ -95,11 +69,7 @@ public function resolve( ); } - //get product children fields queried - $productFields = (array)$info->getFieldSelection(1); - $includeAggregations = isset($productFields['filters']) || isset($productFields['aggregations']); - $searchCriteria = $this->searchApiCriteriaBuilder->build($args, $includeAggregations); - $searchResult = $this->searchQuery->getResult($searchCriteria, $info, $args); + $searchResult = $this->searchQuery->getResult($args, $info); if ($searchResult->getCurrentPage() > $searchResult->getTotalPages() && $searchResult->getTotalCount() > 0) { throw new GraphQlInputException( diff --git a/app/code/Magento/CatalogGraphQl/Model/Resolver/Products/DataProvider/Image/Placeholder.php b/app/code/Magento/CatalogGraphQl/Model/Resolver/Products/DataProvider/Image/Placeholder.php index f5cf2a9ef82ff..e7ce63784addf 100644 --- a/app/code/Magento/CatalogGraphQl/Model/Resolver/Products/DataProvider/Image/Placeholder.php +++ b/app/code/Magento/CatalogGraphQl/Model/Resolver/Products/DataProvider/Image/Placeholder.php @@ -8,7 +8,9 @@ namespace Magento\CatalogGraphQl\Model\Resolver\Products\DataProvider\Image; use Magento\Catalog\Model\View\Asset\PlaceholderFactory; +use Magento\Framework\App\Area; use Magento\Framework\View\Asset\Repository as AssetRepository; +use Magento\Framework\View\DesignInterface; /** * Image Placeholder provider @@ -25,16 +27,24 @@ class Placeholder */ private $assetRepository; + /** + * @var DesignInterface + */ + private $themeDesign; + /** * @param PlaceholderFactory $placeholderFactory * @param AssetRepository $assetRepository + * @param DesignInterface $themeDesign */ public function __construct( PlaceholderFactory $placeholderFactory, - AssetRepository $assetRepository + AssetRepository $assetRepository, + DesignInterface $themeDesign ) { $this->placeholderFactory = $placeholderFactory; $this->assetRepository = $assetRepository; + $this->themeDesign = $themeDesign; } /** @@ -52,8 +62,14 @@ public function getPlaceholder(string $imageType): string return $imageAsset->getUrl(); } - return $this->assetRepository->getUrl( - "Magento_Catalog::images/product/placeholder/{$imageType}.jpg" + $params = [ + 'area' => Area::AREA_FRONTEND, + 'themeId' => $this->themeDesign->getConfigurationDesignTheme(Area::AREA_FRONTEND), + ]; + + return $this->assetRepository->getUrlWithParams( + "Magento_Catalog::images/product/placeholder/{$imageType}.jpg", + $params ); } } diff --git a/app/code/Magento/CatalogGraphQl/Model/Resolver/Products/DataProvider/Image/Placeholder/Theme.php b/app/code/Magento/CatalogGraphQl/Model/Resolver/Products/DataProvider/Image/Placeholder/Theme.php deleted file mode 100644 index dc48c5ef69346..0000000000000 --- a/app/code/Magento/CatalogGraphQl/Model/Resolver/Products/DataProvider/Image/Placeholder/Theme.php +++ /dev/null @@ -1,71 +0,0 @@ -<?php -/** - * Copyright © Magento, Inc. All rights reserved. - * See COPYING.txt for license details. - */ -declare(strict_types=1); - -namespace Magento\CatalogGraphQl\Model\Resolver\Products\DataProvider\Image\Placeholder; - -use Magento\Framework\App\Config\ScopeConfigInterface; -use Magento\Framework\View\Design\Theme\ThemeProviderInterface; -use Magento\Store\Model\StoreManagerInterface; - -/** - * Theme provider - */ -class Theme -{ - /** - * @var ScopeConfigInterface - */ - private $scopeConfig; - - /** - * @var StoreManagerInterface - */ - private $storeManager; - - /** - * @var ThemeProviderInterface - */ - private $themeProvider; - - /** - * @param ScopeConfigInterface $scopeConfig - * @param StoreManagerInterface $storeManager - * @param ThemeProviderInterface $themeProvider - */ - public function __construct( - ScopeConfigInterface $scopeConfig, - StoreManagerInterface $storeManager, - ThemeProviderInterface $themeProvider - ) { - $this->scopeConfig = $scopeConfig; - $this->storeManager = $storeManager; - $this->themeProvider = $themeProvider; - } - - /** - * Get theme model - * - * @return array - * @throws \Magento\Framework\Exception\NoSuchEntityException - */ - public function getThemeData(): array - { - $themeId = $this->scopeConfig->getValue( - \Magento\Framework\View\DesignInterface::XML_PATH_THEME_ID, - \Magento\Store\Model\ScopeInterface::SCOPE_STORE, - $this->storeManager->getStore()->getId() - ); - - /** @var $theme \Magento\Framework\View\Design\ThemeInterface */ - $theme = $this->themeProvider->getThemeById($themeId); - - $data = $theme->getData(); - $data['themeModel'] = $theme; - - return $data; - } -} diff --git a/app/code/Magento/CatalogGraphQl/Model/Resolver/Products/DataProvider/ProductSearch.php b/app/code/Magento/CatalogGraphQl/Model/Resolver/Products/DataProvider/ProductSearch.php index ff845f4796763..4c83afb89cc46 100644 --- a/app/code/Magento/CatalogGraphQl/Model/Resolver/Products/DataProvider/ProductSearch.php +++ b/app/code/Magento/CatalogGraphQl/Model/Resolver/Products/DataProvider/ProductSearch.php @@ -7,16 +7,17 @@ namespace Magento\CatalogGraphQl\Model\Resolver\Products\DataProvider; +use Magento\Catalog\Api\Data\ProductSearchResultsInterfaceFactory; +use Magento\Catalog\Model\ResourceModel\Product\Collection; +use Magento\Catalog\Model\ResourceModel\Product\CollectionFactory; use Magento\CatalogGraphQl\Model\Resolver\Products\DataProvider\Product\CollectionPostProcessor; +use Magento\CatalogGraphQl\Model\Resolver\Products\DataProvider\Product\CollectionProcessorInterface; +use Magento\CatalogGraphQl\Model\Resolver\Products\DataProvider\ProductSearch\ProductCollectionSearchCriteriaBuilder; use Magento\CatalogSearch\Model\ResourceModel\Fulltext\Collection\SearchResultApplierFactory; use Magento\CatalogSearch\Model\ResourceModel\Fulltext\Collection\SearchResultApplierInterface; use Magento\Framework\Api\Search\SearchResultInterface; use Magento\Framework\Api\SearchCriteriaInterface; -use Magento\Catalog\Model\ResourceModel\Product\CollectionFactory; -use Magento\Catalog\Model\ResourceModel\Product\Collection; -use Magento\Catalog\Api\Data\ProductSearchResultsInterfaceFactory; use Magento\Framework\Api\SearchResultsInterface; -use Magento\CatalogGraphQl\Model\Resolver\Products\DataProvider\Product\CollectionProcessorInterface; /** * Product field data provider for product search, used for GraphQL resolver processing. @@ -48,25 +49,33 @@ class ProductSearch */ private $searchResultApplierFactory; + /** + * @var ProductCollectionSearchCriteriaBuilder + */ + private $searchCriteriaBuilder; + /** * @param CollectionFactory $collectionFactory * @param ProductSearchResultsInterfaceFactory $searchResultsFactory * @param CollectionProcessorInterface $collectionPreProcessor * @param CollectionPostProcessor $collectionPostProcessor * @param SearchResultApplierFactory $searchResultsApplierFactory + * @param ProductCollectionSearchCriteriaBuilder $searchCriteriaBuilder */ public function __construct( CollectionFactory $collectionFactory, ProductSearchResultsInterfaceFactory $searchResultsFactory, CollectionProcessorInterface $collectionPreProcessor, CollectionPostProcessor $collectionPostProcessor, - SearchResultApplierFactory $searchResultsApplierFactory + SearchResultApplierFactory $searchResultsApplierFactory, + ProductCollectionSearchCriteriaBuilder $searchCriteriaBuilder ) { $this->collectionFactory = $collectionFactory; $this->searchResultsFactory = $searchResultsFactory; $this->collectionPreProcessor = $collectionPreProcessor; $this->collectionPostProcessor = $collectionPostProcessor; $this->searchResultApplierFactory = $searchResultsApplierFactory; + $this->searchCriteriaBuilder = $searchCriteriaBuilder; } /** @@ -85,15 +94,21 @@ public function getList( /** @var Collection $collection */ $collection = $this->collectionFactory->create(); - //Join search results - $this->getSearchResultsApplier($searchResult, $collection, $this->getSortOrderArray($searchCriteria))->apply(); + //Create a copy of search criteria without filters to preserve the results from search + $searchCriteriaForCollection = $this->searchCriteriaBuilder->build($searchCriteria); + //Apply CatalogSearch results from search and join table + $this->getSearchResultsApplier( + $searchResult, + $collection, + $this->getSortOrderArray($searchCriteriaForCollection) + )->apply(); - $this->collectionPreProcessor->process($collection, $searchCriteria, $attributes); + $this->collectionPreProcessor->process($collection, $searchCriteriaForCollection, $attributes); $collection->load(); $this->collectionPostProcessor->process($collection, $attributes); $searchResults = $this->searchResultsFactory->create(); - $searchResults->setSearchCriteria($searchCriteria); + $searchResults->setSearchCriteria($searchCriteriaForCollection); $searchResults->setItems($collection->getItems()); $searchResults->setTotalCount($searchResult->getTotalCount()); return $searchResults; diff --git a/app/code/Magento/CatalogGraphQl/Model/Resolver/Products/DataProvider/ProductSearch/ProductCollectionSearchCriteriaBuilder.php b/app/code/Magento/CatalogGraphQl/Model/Resolver/Products/DataProvider/ProductSearch/ProductCollectionSearchCriteriaBuilder.php new file mode 100644 index 0000000000000..4a124d69bd20f --- /dev/null +++ b/app/code/Magento/CatalogGraphQl/Model/Resolver/Products/DataProvider/ProductSearch/ProductCollectionSearchCriteriaBuilder.php @@ -0,0 +1,77 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +declare(strict_types=1); + +namespace Magento\CatalogGraphQl\Model\Resolver\Products\DataProvider\ProductSearch; + +use Magento\Catalog\Model\CategoryProductLink; +use Magento\Framework\Api\FilterBuilder; +use Magento\Framework\Api\Search\FilterGroupBuilder; +use Magento\Framework\Api\Search\SearchCriteriaInterfaceFactory; +use Magento\Framework\Api\SearchCriteriaInterface; + +/** + * Builds a search criteria intended for the product collection based on search criteria used on SearchAPI + */ +class ProductCollectionSearchCriteriaBuilder +{ + /** @var SearchCriteriaInterfaceFactory */ + private $searchCriteriaFactory; + + /** @var FilterBuilder */ + private $filterBuilder; + + /** @var FilterGroupBuilder */ + private $filterGroupBuilder; + + /** + * @param SearchCriteriaInterfaceFactory $searchCriteriaFactory + * @param FilterBuilder $filterBuilder + * @param FilterGroupBuilder $filterGroupBuilder + */ + public function __construct( + SearchCriteriaInterfaceFactory $searchCriteriaFactory, + FilterBuilder $filterBuilder, + FilterGroupBuilder $filterGroupBuilder + ) { + $this->searchCriteriaFactory = $searchCriteriaFactory; + $this->filterBuilder = $filterBuilder; + $this->filterGroupBuilder = $filterGroupBuilder; + } + + /** + * Build searchCriteria from search for product collection + * + * @param SearchCriteriaInterface $searchCriteria + * @return SearchCriteriaInterface + */ + public function build(SearchCriteriaInterface $searchCriteria): SearchCriteriaInterface + { + //Create a copy of search criteria without filters to preserve the results from search + $searchCriteriaForCollection = $this->searchCriteriaFactory->create() + ->setSortOrders($searchCriteria->getSortOrders()) + ->setPageSize($searchCriteria->getPageSize()) + ->setCurrentPage($searchCriteria->getCurrentPage()); + + //Add category id to enable sorting by position + foreach ($searchCriteria->getFilterGroups() as $filterGroup) { + foreach ($filterGroup->getFilters() as $filter) { + if ($filter->getField() == CategoryProductLink::KEY_CATEGORY_ID) { + $categoryFilter = $this->filterBuilder + ->setField($filter->getField()) + ->setValue($filter->getValue()) + ->setConditionType($filter->getConditionType()) + ->create(); + + $this->filterGroupBuilder->addFilter($categoryFilter); + $categoryGroup = $this->filterGroupBuilder->create(); + $searchCriteriaForCollection->setFilterGroups([$categoryGroup]); + } + } + } + return $searchCriteriaForCollection; + } +} diff --git a/app/code/Magento/CatalogGraphQl/Model/Resolver/Products/FilterArgument/ProductEntityAttributesForAst.php b/app/code/Magento/CatalogGraphQl/Model/Resolver/Products/FilterArgument/ProductEntityAttributesForAst.php index 973b8fbcd6b0f..c2ce239ea74bf 100644 --- a/app/code/Magento/CatalogGraphQl/Model/Resolver/Products/FilterArgument/ProductEntityAttributesForAst.php +++ b/app/code/Magento/CatalogGraphQl/Model/Resolver/Products/FilterArgument/ProductEntityAttributesForAst.php @@ -17,6 +17,10 @@ */ class ProductEntityAttributesForAst implements FieldEntityAttributesInterface { + private const PRODUCT_BASE_TYPE = 'SimpleProduct'; + + private const PRODUCT_FILTER_INPUT = 'ProductAttributeFilterInput'; + /** * @var ConfigInterface */ @@ -51,9 +55,9 @@ public function __construct( */ public function getEntityAttributes() : array { - $productTypeSchema = $this->config->getConfigElement('SimpleProduct'); + $productTypeSchema = $this->config->getConfigElement(self::PRODUCT_BASE_TYPE); if (!$productTypeSchema instanceof Type) { - throw new \LogicException(__("SimpleProduct type not defined in schema.")); + throw new \LogicException(__("%1 type not defined in schema.", self::PRODUCT_BASE_TYPE)); } $fields = []; @@ -69,6 +73,9 @@ public function getEntityAttributes() : array } } + $productAttributeFilterFields = $this->getProductAttributeFilterFields(); + $fields = array_merge($fields, $productAttributeFilterFields); + foreach ($this->additionalAttributes as $attributeName) { $fields[$attributeName] = [ 'type' => 'String', @@ -78,4 +85,24 @@ public function getEntityAttributes() : array return $fields; } + + /** + * Get fields from ProductAttributeFilterInput + * + * @return array + */ + private function getProductAttributeFilterFields() + { + $filterFields = []; + + $productAttributeFilterSchema = $this->config->getConfigElement(self::PRODUCT_FILTER_INPUT); + $productAttributeFilterFields = $productAttributeFilterSchema->getFields(); + foreach ($productAttributeFilterFields as $filterField) { + $filterFields[$filterField->getName()] = [ + 'type' => 'String', + 'fieldName' => $filterField->getName(), + ]; + } + return $filterFields; + } } diff --git a/app/code/Magento/CatalogGraphQl/Model/Resolver/Products/Query/FieldSelection.php b/app/code/Magento/CatalogGraphQl/Model/Resolver/Products/Query/FieldSelection.php index ffa0a3e6848e1..64ab128b22ab4 100644 --- a/app/code/Magento/CatalogGraphQl/Model/Resolver/Products/Query/FieldSelection.php +++ b/app/code/Magento/CatalogGraphQl/Model/Resolver/Products/Query/FieldSelection.php @@ -7,7 +7,6 @@ namespace Magento\CatalogGraphQl\Model\Resolver\Products\Query; -use GraphQL\Language\AST\SelectionNode; use Magento\Framework\GraphQl\Query\FieldTranslator; use Magento\Framework\GraphQl\Schema\Type\ResolveInfo; @@ -37,57 +36,18 @@ public function __construct(FieldTranslator $fieldTranslator) */ public function getProductsFieldSelection(ResolveInfo $resolveInfo): array { - return $this->getProductFields($resolveInfo); - } + $productFields = $resolveInfo->getFieldSelection(1); + $sectionNames = ['items', 'product']; - /** - * Return field names for all requested product fields. - * - * @param ResolveInfo $info - * @return string[] - */ - private function getProductFields(ResolveInfo $info): array - { $fieldNames = []; - foreach ($info->fieldNodes as $node) { - if ($node->name->value !== 'products' && $node->name->value !== 'variants') { - continue; - } - foreach ($node->selectionSet->selections as $selection) { - if ($selection->name->value !== 'items' && $selection->name->value !== 'product') { - continue; - } - $fieldNames[] = $this->collectProductFieldNames($selection, $fieldNames); - } - } - if (!empty($fieldNames)) { - $fieldNames = array_merge(...$fieldNames); - } - return $fieldNames; - } - - /** - * Collect field names for each node in selection - * - * @param SelectionNode $selection - * @param array $fieldNames - * @return array - */ - private function collectProductFieldNames(SelectionNode $selection, array $fieldNames = []): array - { - foreach ($selection->selectionSet->selections as $itemSelection) { - if ($itemSelection->kind === 'InlineFragment') { - foreach ($itemSelection->selectionSet->selections as $inlineSelection) { - if ($inlineSelection->kind === 'InlineFragment') { - continue; - } - $fieldNames[] = $this->fieldTranslator->translate($inlineSelection->name->value); + foreach ($sectionNames as $sectionName) { + if (isset($productFields[$sectionName])) { + foreach (array_keys($productFields[$sectionName]) as $fieldName) { + $fieldNames[] = $this->fieldTranslator->translate($fieldName); } - continue; } - $fieldNames[] = $this->fieldTranslator->translate($itemSelection->name->value); } - return $fieldNames; + return array_unique($fieldNames); } } diff --git a/app/code/Magento/CatalogGraphQl/Model/Resolver/Products/Query/Filter.php b/app/code/Magento/CatalogGraphQl/Model/Resolver/Products/Query/Filter.php index cc25af44fdfbe..670eee9c4583e 100644 --- a/app/code/Magento/CatalogGraphQl/Model/Resolver/Products/Query/Filter.php +++ b/app/code/Magento/CatalogGraphQl/Model/Resolver/Products/Query/Filter.php @@ -7,16 +7,23 @@ namespace Magento\CatalogGraphQl\Model\Resolver\Products\Query; -use Magento\Framework\GraphQl\Schema\Type\ResolveInfo; +use Magento\Catalog\Model\Layer\Resolver as LayerResolver; +use Magento\Catalog\Model\Product; use Magento\Framework\Api\SearchCriteriaInterface; -use Magento\CatalogGraphQl\Model\Resolver\Products\DataProvider\Product; +use Magento\Framework\Exception\InputException; +use Magento\Framework\GraphQl\Query\Resolver\Argument\SearchCriteria\Builder as SearchCriteriaBuilder; +use Magento\Framework\GraphQl\Schema\Type\ResolveInfo; +use Magento\CatalogGraphQl\Model\Resolver\Products\DataProvider\Product as ProductProvider; use Magento\CatalogGraphQl\Model\Resolver\Products\SearchResult; use Magento\CatalogGraphQl\Model\Resolver\Products\SearchResultFactory; +use Magento\Search\Model\Query; +use Magento\Framework\App\Config\ScopeConfigInterface; +use Magento\Store\Model\ScopeInterface; /** * Retrieve filtered product data based off given search criteria in a format that GraphQL can interpret. */ -class Filter +class Filter implements ProductQueryInterface { /** * @var SearchResultFactory @@ -24,12 +31,12 @@ class Filter private $searchResultFactory; /** - * @var \Magento\CatalogGraphQl\Model\Resolver\Products\DataProvider\Product + * @var ProductProvider */ private $productDataProvider; /** - * @var \Magento\Catalog\Model\Layer\Resolver + * @var LayerResolver */ private $layerResolver; @@ -38,50 +45,154 @@ class Filter */ private $fieldSelection; + /** + * @var SearchCriteriaBuilder + */ + private $searchCriteriaBuilder; + + /** + * @var ScopeConfigInterface + */ + private $scopeConfig; + /** * @param SearchResultFactory $searchResultFactory - * @param Product $productDataProvider - * @param \Magento\Catalog\Model\Layer\Resolver $layerResolver + * @param ProductProvider $productDataProvider + * @param LayerResolver $layerResolver * @param FieldSelection $fieldSelection + * @param SearchCriteriaBuilder $searchCriteriaBuilder + * @param ScopeConfigInterface $scopeConfig */ public function __construct( SearchResultFactory $searchResultFactory, - Product $productDataProvider, - \Magento\Catalog\Model\Layer\Resolver $layerResolver, - FieldSelection $fieldSelection + ProductProvider $productDataProvider, + LayerResolver $layerResolver, + FieldSelection $fieldSelection, + SearchCriteriaBuilder $searchCriteriaBuilder, + ScopeConfigInterface $scopeConfig ) { $this->searchResultFactory = $searchResultFactory; $this->productDataProvider = $productDataProvider; $this->layerResolver = $layerResolver; $this->fieldSelection = $fieldSelection; + $this->searchCriteriaBuilder = $searchCriteriaBuilder; + $this->scopeConfig = $scopeConfig; } /** * Filter catalog product data based off given search criteria * - * @param SearchCriteriaInterface $searchCriteria + * @param array $args * @param ResolveInfo $info - * @param bool $isSearch * @return SearchResult */ public function getResult( - SearchCriteriaInterface $searchCriteria, - ResolveInfo $info, - bool $isSearch = false + array $args, + ResolveInfo $info ): SearchResult { $fields = $this->fieldSelection->getProductsFieldSelection($info); - $products = $this->productDataProvider->getList($searchCriteria, $fields, $isSearch); + try { + $searchCriteria = $this->buildSearchCriteria($args, $info); + $searchResults = $this->productDataProvider->getList($searchCriteria, $fields); + } catch (InputException $e) { + return $this->createEmptyResult($args); + } + $productArray = []; - /** @var \Magento\Catalog\Model\Product $product */ - foreach ($products->getItems() as $product) { + /** @var Product $product */ + foreach ($searchResults->getItems() as $product) { $productArray[$product->getId()] = $product->getData(); $productArray[$product->getId()]['model'] = $product; } + //possible division by 0 + if ($searchCriteria->getPageSize()) { + $maxPages = (int)ceil($searchResults->getTotalCount() / $searchCriteria->getPageSize()); + } else { + $maxPages = 0; + } + + return $this->searchResultFactory->create( + [ + 'totalCount' => $searchResults->getTotalCount(), + 'productsSearchResult' => $productArray, + 'pageSize' => $searchCriteria->getPageSize(), + 'currentPage' => $searchCriteria->getCurrentPage(), + 'totalPages' => $maxPages, + ] + ); + } + + /** + * Build search criteria from query input args + * + * @param array $args + * @param ResolveInfo $info + * @return SearchCriteriaInterface + */ + private function buildSearchCriteria(array $args, ResolveInfo $info): SearchCriteriaInterface + { + if (!empty($args['filter'])) { + $args['filter'] = $this->formatFilters($args['filter']); + } + + $criteria = $this->searchCriteriaBuilder->build($info->fieldName, $args); + $criteria->setCurrentPage($args['currentPage']); + $criteria->setPageSize($args['pageSize']); + + return $criteria; + } + + /** + * Reformat filters + * + * @param array $filters + * @return array + * @throws InputException + */ + private function formatFilters(array $filters): array + { + $formattedFilters = []; + $minimumQueryLength = $this->scopeConfig->getValue( + Query::XML_PATH_MIN_QUERY_LENGTH, + ScopeInterface::SCOPE_STORE + ); + + foreach ($filters as $field => $filter) { + foreach ($filter as $condition => $value) { + if ($condition === 'match') { + // reformat 'match' filter so MySQL filtering behaves like SearchAPI filtering + $condition = 'like'; + $value = str_replace('%', '', trim($value)); + if (strlen($value) < $minimumQueryLength) { + throw new InputException(__('Invalid match filter')); + } + $value = '%' . preg_replace('/ +/', '%', $value) . '%'; + } + $formattedFilters[$field] = [$condition => $value]; + } + } + + return $formattedFilters; + } + + /** + * Return and empty SearchResult object + * + * Used for handling exceptions gracefully + * + * @param array $args + * @return SearchResult + */ + private function createEmptyResult(array $args): SearchResult + { return $this->searchResultFactory->create( [ - 'totalCount' => $products->getTotalCount(), - 'productsSearchResult' => $productArray + 'totalCount' => 0, + 'productsSearchResult' => [], + 'pageSize' => $args['pageSize'], + 'currentPage' => $args['currentPage'], + 'totalPages' => 0, ] ); } diff --git a/app/code/Magento/CatalogGraphQl/Model/Resolver/Products/Query/ProductQueryInterface.php b/app/code/Magento/CatalogGraphQl/Model/Resolver/Products/Query/ProductQueryInterface.php new file mode 100644 index 0000000000000..580af5d87be26 --- /dev/null +++ b/app/code/Magento/CatalogGraphQl/Model/Resolver/Products/Query/ProductQueryInterface.php @@ -0,0 +1,25 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ + +namespace Magento\CatalogGraphQl\Model\Resolver\Products\Query; + +use Magento\CatalogGraphQl\Model\Resolver\Products\SearchResult; +use Magento\Framework\GraphQl\Schema\Type\ResolveInfo; + +/** + * Search for products by criteria + */ +interface ProductQueryInterface +{ + /** + * Get product search result + * + * @param array $args + * @param ResolveInfo $info + * @return SearchResult + */ + public function getResult(array $args, ResolveInfo $info): SearchResult; +} diff --git a/app/code/Magento/CatalogGraphQl/Model/Resolver/Products/Query/Search.php b/app/code/Magento/CatalogGraphQl/Model/Resolver/Products/Query/Search.php index ef83cc6132ecc..fbb0e42f2afeb 100644 --- a/app/code/Magento/CatalogGraphQl/Model/Resolver/Products/Query/Search.php +++ b/app/code/Magento/CatalogGraphQl/Model/Resolver/Products/Query/Search.php @@ -7,18 +7,19 @@ namespace Magento\CatalogGraphQl\Model\Resolver\Products\Query; +use Magento\CatalogGraphQl\DataProvider\Product\SearchCriteriaBuilder; use Magento\CatalogGraphQl\Model\Resolver\Products\DataProvider\ProductSearch; -use Magento\Framework\GraphQl\Schema\Type\ResolveInfo; -use Magento\Framework\Api\Search\SearchCriteriaInterface; use Magento\CatalogGraphQl\Model\Resolver\Products\SearchResult; use Magento\CatalogGraphQl\Model\Resolver\Products\SearchResultFactory; +use Magento\Framework\Api\Search\SearchCriteriaInterface; +use Magento\Framework\GraphQl\Schema\Type\ResolveInfo; use Magento\Search\Api\SearchInterface; -use Magento\Framework\Api\Search\SearchCriteriaInterfaceFactory; +use Magento\Search\Model\Search\PageSizeProvider; /** * Full text search for catalog using given search criteria. */ -class Search +class Search implements ProductQueryInterface { /** * @var SearchInterface @@ -31,15 +32,10 @@ class Search private $searchResultFactory; /** - * @var \Magento\Search\Model\Search\PageSizeProvider + * @var PageSizeProvider */ private $pageSizeProvider; - /** - * @var SearchCriteriaInterfaceFactory - */ - private $searchCriteriaFactory; - /** * @var FieldSelection */ @@ -50,68 +46,64 @@ class Search */ private $productsProvider; + /** + * @var SearchCriteriaBuilder + */ + private $searchCriteriaBuilder; + /** * @param SearchInterface $search * @param SearchResultFactory $searchResultFactory - * @param \Magento\Search\Model\Search\PageSizeProvider $pageSize - * @param SearchCriteriaInterfaceFactory $searchCriteriaFactory + * @param PageSizeProvider $pageSize * @param FieldSelection $fieldSelection * @param ProductSearch $productsProvider + * @param SearchCriteriaBuilder $searchCriteriaBuilder */ public function __construct( SearchInterface $search, SearchResultFactory $searchResultFactory, - \Magento\Search\Model\Search\PageSizeProvider $pageSize, - SearchCriteriaInterfaceFactory $searchCriteriaFactory, + PageSizeProvider $pageSize, FieldSelection $fieldSelection, - ProductSearch $productsProvider + ProductSearch $productsProvider, + SearchCriteriaBuilder $searchCriteriaBuilder ) { $this->search = $search; $this->searchResultFactory = $searchResultFactory; $this->pageSizeProvider = $pageSize; - $this->searchCriteriaFactory = $searchCriteriaFactory; $this->fieldSelection = $fieldSelection; $this->productsProvider = $productsProvider; + $this->searchCriteriaBuilder = $searchCriteriaBuilder; } /** - * Return results of full text catalog search of given term, and will return filtered results if filter is specified + * Return product search results using Search API * - * @param SearchCriteriaInterface $searchCriteria + * @param array $args * @param ResolveInfo $info * @return SearchResult * @throws \Exception */ public function getResult( - SearchCriteriaInterface $searchCriteria, + array $args, ResolveInfo $info ): SearchResult { $queryFields = $this->fieldSelection->getProductsFieldSelection($info); + $searchCriteria = $this->buildSearchCriteria($args, $info); $realPageSize = $searchCriteria->getPageSize(); $realCurrentPage = $searchCriteria->getCurrentPage(); - // Current page must be set to 0 and page size to max for search to grab all ID's as temporary workaround + //Because of limitations of sort and pagination on search API we will query all IDS $pageSize = $this->pageSizeProvider->getMaxPageSize(); $searchCriteria->setPageSize($pageSize); $searchCriteria->setCurrentPage(0); $itemsResults = $this->search->search($searchCriteria); - //Create copy of search criteria without conditions (conditions will be applied by joining search result) - $searchCriteriaCopy = $this->searchCriteriaFactory->create() - ->setSortOrders($searchCriteria->getSortOrders()) - ->setPageSize($realPageSize) - ->setCurrentPage($realCurrentPage); - - $searchResults = $this->productsProvider->getList($searchCriteriaCopy, $itemsResults, $queryFields); - - //possible division by 0 - if ($realPageSize) { - $maxPages = (int)ceil($searchResults->getTotalCount() / $realPageSize); - } else { - $maxPages = 0; - } + //Address limitations of sort and pagination on search API apply original pagination from GQL query $searchCriteria->setPageSize($realPageSize); $searchCriteria->setCurrentPage($realCurrentPage); + $searchResults = $this->productsProvider->getList($searchCriteria, $itemsResults, $queryFields); + + $totalPages = $realPageSize ? ((int)ceil($searchResults->getTotalCount() / $realPageSize)) : 0; $productArray = []; /** @var \Magento\Catalog\Model\Product $product */ @@ -127,8 +119,24 @@ public function getResult( 'searchAggregation' => $itemsResults->getAggregations(), 'pageSize' => $realPageSize, 'currentPage' => $realCurrentPage, - 'totalPages' => $maxPages, + 'totalPages' => $totalPages, ] ); } + + /** + * Build search criteria from query input args + * + * @param array $args + * @param ResolveInfo $info + * @return SearchCriteriaInterface + */ + private function buildSearchCriteria(array $args, ResolveInfo $info): SearchCriteriaInterface + { + $productFields = (array)$info->getFieldSelection(1); + $includeAggregations = isset($productFields['filters']) || isset($productFields['aggregations']); + $searchCriteria = $this->searchCriteriaBuilder->build($args, $includeAggregations); + + return $searchCriteria; + } } diff --git a/app/code/Magento/CatalogGraphQl/Model/Resolver/Products/SearchCriteria/CollectionProcessor/FilterProcessor/CategoryFilter.php b/app/code/Magento/CatalogGraphQl/Model/Resolver/Products/SearchCriteria/CollectionProcessor/FilterProcessor/CategoryFilter.php index e3b3588166163..f709f8cd6eb72 100644 --- a/app/code/Magento/CatalogGraphQl/Model/Resolver/Products/SearchCriteria/CollectionProcessor/FilterProcessor/CategoryFilter.php +++ b/app/code/Magento/CatalogGraphQl/Model/Resolver/Products/SearchCriteria/CollectionProcessor/FilterProcessor/CategoryFilter.php @@ -9,11 +9,9 @@ use Magento\Catalog\Model\CategoryFactory; use Magento\Catalog\Model\ResourceModel\Category as CategoryResourceModel; -use Magento\Catalog\Model\ResourceModel\Product\Collection; use Magento\Framework\Api\Filter; use Magento\Framework\Api\SearchCriteria\CollectionProcessor\FilterProcessor\CustomFilterInterface; use Magento\Framework\Data\Collection\AbstractDb; -use Magento\Framework\Exception\LocalizedException; /** * Category filter allows to filter products collection using 'category_id' filter from search criteria. @@ -50,22 +48,24 @@ public function __construct( * @param Filter $filter * @param AbstractDb $collection * @return bool Whether the filter is applied - * @throws LocalizedException */ public function apply(Filter $filter, AbstractDb $collection) { - $conditionType = $filter->getConditionType(); - - if ($conditionType !== 'eq') { - throw new LocalizedException(__("'category_id' only supports 'eq' condition type.")); + $categoryIds = $filter->getValue(); + if (!is_array($categoryIds)) { + $categoryIds = [$categoryIds]; } - $categoryId = $filter->getValue(); - /** @var Collection $collection */ - $category = $this->categoryFactory->create(); - $this->categoryResourceModel->load($category, $categoryId); - $collection->addCategoryFilter($category); + $categoryProducts = []; + foreach ($categoryIds as $categoryId) { + $category = $this->categoryFactory->create(); + $this->categoryResourceModel->load($category, $categoryId); + $categoryProducts[$categoryId] = $category->getProductCollection()->getAllIds(); + $collection->addCategoryFilter($category); + } + $categoryProductIds = array_unique(array_merge(...$categoryProducts)); + $collection->addIdFilter($categoryProductIds); return true; } } diff --git a/app/code/Magento/CatalogGraphQl/etc/di.xml b/app/code/Magento/CatalogGraphQl/etc/di.xml index 1fe62fc442ecf..5fec7bfd4fda7 100644 --- a/app/code/Magento/CatalogGraphQl/etc/di.xml +++ b/app/code/Magento/CatalogGraphQl/etc/di.xml @@ -36,6 +36,7 @@ <arguments> <argument name="attributesInstances" xsi:type="array"> <item name="products" xsi:type="object">Magento\CatalogGraphQl\Model\Resolver\Products\FilterArgument\ProductEntityAttributesForAst</item> + <item name="categoryList" xsi:type="object">Magento\CatalogGraphQl\Model\Category\CategoryFilterAttributesForAst</item> </argument> </arguments> </type> @@ -71,4 +72,6 @@ </type> <preference type="\Magento\CatalogGraphQl\Model\Resolver\Product\Price\Provider" for="\Magento\CatalogGraphQl\Model\Resolver\Product\Price\ProviderInterface"/> + + <preference type="Magento\CatalogGraphQl\Model\Resolver\Products\Query\Search" for="Magento\CatalogGraphQl\Model\Resolver\Products\Query\ProductQueryInterface"/> </config> diff --git a/app/code/Magento/CatalogGraphQl/etc/graphql/di.xml b/app/code/Magento/CatalogGraphQl/etc/graphql/di.xml index 066a7b38d8967..ed548efc896f8 100644 --- a/app/code/Magento/CatalogGraphQl/etc/graphql/di.xml +++ b/app/code/Magento/CatalogGraphQl/etc/graphql/di.xml @@ -176,4 +176,10 @@ </argument> </arguments> </type> + + <type name="Magento\Catalog\Helper\Data"> + <arguments> + <argument name="templateFilterModel" xsi:type="string">Magento\Widget\Model\Template\FilterEmulate</argument> + </arguments> + </type> </config> diff --git a/app/code/Magento/CatalogGraphQl/etc/schema.graphqls b/app/code/Magento/CatalogGraphQl/etc/schema.graphqls index f70a32a1b549e..f77b301d61e28 100644 --- a/app/code/Magento/CatalogGraphQl/etc/schema.graphqls +++ b/app/code/Magento/CatalogGraphQl/etc/schema.graphqls @@ -15,8 +15,13 @@ type Query { ): CategoryTree @resolver(class: "Magento\\CatalogGraphQl\\Model\\Resolver\\CategoryTree") @doc(description: "The category query searches for categories that match the criteria specified in the search and filter attributes.") @deprecated(reason: "Use 'categoryList' query instead of 'category' query") @cache(cacheIdentity: "Magento\\CatalogGraphQl\\Model\\Resolver\\Category\\CategoryTreeIdentity") categoryList( - filters: CategoryFilterInput @doc(description: "Identifies which Category filter inputs to search for and return.") + filters: CategoryFilterInput @doc(description: "Identifies which Category filter inputs to search for and return.") ): [CategoryTree] @doc(description: "Returns an array of categories based on the specified filters.") @resolver(class: "Magento\\CatalogGraphQl\\Model\\Resolver\\CategoryList") @cache(cacheIdentity: "Magento\\CatalogGraphQl\\Model\\Resolver\\Category\\CategoriesIdentity") + categories ( + filters: CategoryFilterInput @doc(description: "Identifies which Category filter inputs to search for and return.") + pageSize: Int = 20 @doc(description: "Specifies the maximum number of results to return at once. This attribute is optional.") + currentPage: Int = 1 @doc(description: "Specifies which page of results to return. The default value is 1.") + ): CategoryResult @resolver(class: "Magento\\CatalogGraphQl\\Model\\Resolver\\CategoriesQuery") @cache(cacheIdentity: "Magento\\CatalogGraphQl\\Model\\Resolver\\Category\\CategoriesIdentity") } type Price @doc(description: "Price is deprecated, replaced by ProductPrice. The Price object defines the price of a product as well as any tax-related adjustments.") { @@ -100,8 +105,8 @@ interface ProductInterface @typeResolver(class: "Magento\\CatalogGraphQl\\Model\ created_at: String @doc(description: "Timestamp indicating when the product was created.") updated_at: String @doc(description: "Timestamp indicating when the product was updated.") country_of_manufacture: String @doc(description: "The product's country of origin.") - type_id: String @doc(description: "One of simple, virtual, bundle, downloadable, grouped, or configurable.") - websites: [Website] @doc(description: "An array of websites in which the product is available.") @resolver(class: "Magento\\CatalogGraphQl\\Model\\Resolver\\Product\\Websites") + type_id: String @doc(description: "One of simple, virtual, bundle, downloadable, grouped, or configurable.") @deprecated(reason: "Use __typename instead.") + websites: [Website] @doc(description: "An array of websites in which the product is available.") @deprecated(reason: "The field should not be used on the storefront.") @resolver(class: "Magento\\CatalogGraphQl\\Model\\Resolver\\Product\\Websites") product_links: [ProductLinksInterface] @doc(description: "An array of ProductLinks objects.") @resolver(class: "Magento\\CatalogGraphQl\\Model\\Resolver\\Product\\BatchProductLinks") media_gallery_entries: [MediaGalleryEntry] @deprecated(reason: "Use product's `media_gallery` instead") @doc(description: "An array of MediaGalleryEntry objects.") @resolver(class: "Magento\\CatalogGraphQl\\Model\\Resolver\\Product\\MediaGalleryEntries") price: ProductPrices @deprecated(reason: "Use price_range for product price information.") @doc(description: "A ProductPrices object, indicating the price of an item.") @resolver(class: "Magento\\CatalogGraphQl\\Model\\Resolver\\Product\\Price") @@ -133,6 +138,12 @@ type CategoryTree implements CategoryInterface @doc(description: "Category Tree children: [CategoryTree] @doc(description: "Child categories tree.") @resolver(class: "Magento\\CatalogGraphQl\\Model\\Resolver\\CategoryTree") } +type CategoryResult @doc(description: "A collection of CategoryTree objects and pagination information.") { + items: [CategoryTree] @doc(description: "A list of categories that match the filter criteria.") + page_info: SearchResultPageInfo @doc(description: "An object that includes the page_info and currentPage values specified in the query.") + total_count: Int @doc(description: "The total number of categories that match the criteria.") +} + type CustomizableDateOption implements CustomizableOptionInterface @doc(description: "CustomizableDateOption contains information about a date picker that is defined as part of a customizable option.") { value: CustomizableDateValue @doc(description: "An object that defines a date field in a customizable option.") product_sku: String @doc(description: "The Stock Keeping Unit of the base product.") @@ -199,11 +210,17 @@ type CustomizableFileValue @doc(description: "CustomizableFileValue defines the interface MediaGalleryInterface @doc(description: "Contains basic information about a product image or video.") @typeResolver(class: "Magento\\CatalogGraphQl\\Model\\MediaGalleryTypeResolver") { url: String @doc(description: "The URL of the product image or video.") @resolver(class: "Magento\\CatalogGraphQl\\Model\\Resolver\\Product\\MediaGallery\\Url") label: String @doc(description: "The label of the product image or video.") + position: Int @doc(description: "The media item's position after it has been sorted.") + disabled: Boolean @doc(description: "Whether the image is hidden from view.") } type ProductImage implements MediaGalleryInterface @doc(description: "Product image information. Contains the image URL and label.") { } +type ProductVideo implements MediaGalleryInterface @doc(description: "Contains information about a product video.") { + video_content: ProductMediaGalleryEntriesVideoContent @doc(description: "Contains a ProductMediaGalleryEntriesVideoContent object.") +} + interface CustomizableOptionInterface @typeResolver(class: "Magento\\CatalogGraphQl\\Model\\CustomizableOptionTypeResolver") @doc(description: "The CustomizableOptionInterface contains basic information about a customizable option. It can be implemented by several types of configurable options.") { title: String @doc(description: "The display name for this option.") required: Boolean @doc(description: "Indicates whether the option is required.") @@ -301,8 +318,9 @@ input ProductAttributeFilterInput @doc(description: "ProductAttributeFilterInput input CategoryFilterInput @doc(description: "CategoryFilterInput defines the filters to be used in the search. A filter contains at least one attribute, a comparison operator, and the value that is being searched for.") { ids: FilterEqualTypeInput @doc(description: "Filter by category ID that uniquely identifies the category.") - url_key: FilterEqualTypeInput @doc(description: "Filter by the part of the URL that identifies the category") + url_key: FilterEqualTypeInput @doc(description: "Filter by the part of the URL that identifies the category.") name: FilterMatchTypeInput @doc(description: "Filter by the display name of the category.") + url_path: FilterEqualTypeInput @doc(description: "Filter by the URL path for the category.") } input ProductFilterInput @doc(description: "ProductFilterInput is deprecated, use @ProductAttributeFilterInput instead. ProductFilterInput defines the filters to be used in the search. A filter contains at least one attribute, a comparison operator, and the value that is being searched for.") { @@ -403,7 +421,7 @@ type MediaGalleryEntry @doc(description: "MediaGalleryEntry defines characterist media_type: String @doc(description: "image or video.") label: String @doc(description: "The alt text displayed on the UI when the user points to the image.") position: Int @doc(description: "The media item's position after it has been sorted.") - disabled: Boolean @doc(description: "Whether the image is hidden from vie.") + disabled: Boolean @doc(description: "Whether the image is hidden from view.") types: [String] @doc(description: "Array of image types. It can have the following values: image, small_image, thumbnail.") file: String @doc(description: "The path of the image on the server.") content: ProductMediaGalleryEntriesContent @doc(description: "Contains a ProductMediaGalleryEntriesContent object.") @@ -466,7 +484,3 @@ type StoreConfig @doc(description: "The type contains information about a store catalog_default_sort_by : String @doc(description: "Default Sort By.") root_category_id: Int @doc(description: "The ID of the root category") @resolver(class: "Magento\\CatalogGraphQl\\Model\\Resolver\\RootCategoryId") } - -type ProductVideo @doc(description: "Contains information about a product video.") implements MediaGalleryInterface { - video_content: ProductMediaGalleryEntriesVideoContent @doc(description: "Contains a ProductMediaGalleryEntriesVideoContent object.") -} diff --git a/app/code/Magento/CatalogImportExport/Model/Export/Product.php b/app/code/Magento/CatalogImportExport/Model/Export/Product.php index 5baa4b4274be5..530bf6b1a0057 100644 --- a/app/code/Magento/CatalogImportExport/Model/Export/Product.php +++ b/app/code/Magento/CatalogImportExport/Model/Export/Product.php @@ -5,12 +5,13 @@ */ namespace Magento\CatalogImportExport\Model\Export; +use Magento\Catalog\Model\Product as ProductEntity; use Magento\Catalog\Model\ResourceModel\Product\Option\Collection; +use Magento\CatalogImportExport\Model\Import\Product as ImportProduct; use Magento\CatalogImportExport\Model\Import\Product\CategoryProcessor; +use Magento\Framework\App\ObjectManager; use Magento\ImportExport\Model\Import; -use \Magento\Store\Model\Store; -use \Magento\CatalogImportExport\Model\Import\Product as ImportProduct; -use Magento\Catalog\Model\Product as ProductEntity; +use Magento\Store\Model\Store; /** * Export entity product model @@ -21,6 +22,8 @@ * @SuppressWarnings(PHPMD.ExcessiveClassComplexity) * @SuppressWarnings(PHPMD.CouplingBetweenObjects) * @SuppressWarnings(PHPMD.ExcessiveParameterList) + * @SuppressWarnings(PHPMD.ExcessiveClassLength) + * @SuppressWarnings(PHPMD.TooManyMethods) * @since 100.0.2 */ class Product extends \Magento\ImportExport\Model\Export\Entity\AbstractEntity @@ -348,6 +351,10 @@ class Product extends \Magento\ImportExport\Model\Export\Entity\AbstractEntity * @var string */ private $productEntityLinkField; + /** + * @var ProductFilterInterface + */ + private $filter; /** * Product constructor. @@ -369,6 +376,7 @@ class Product extends \Magento\ImportExport\Model\Export\Entity\AbstractEntity * @param ProductEntity\LinkTypeProvider $linkTypeProvider * @param RowCustomizerInterface $rowCustomizer * @param array $dateAttrCodes + * @param ProductFilterInterface $filter * @throws \Magento\Framework\Exception\LocalizedException */ public function __construct( @@ -388,7 +396,8 @@ public function __construct( \Magento\CatalogImportExport\Model\Export\Product\Type\Factory $_typeFactory, \Magento\Catalog\Model\Product\LinkTypeProvider $linkTypeProvider, \Magento\CatalogImportExport\Model\Export\RowCustomizerInterface $rowCustomizer, - array $dateAttrCodes = [] + array $dateAttrCodes = [], + ?ProductFilterInterface $filter = null ) { $this->_entityCollectionFactory = $collectionFactory; $this->_exportConfig = $exportConfig; @@ -404,6 +413,7 @@ public function __construct( $this->_linkTypeProvider = $linkTypeProvider; $this->rowCustomizer = $rowCustomizer; $this->dateAttrCodes = array_merge($this->dateAttrCodes, $dateAttrCodes); + $this->filter = $filter ?? ObjectManager::getInstance()->get(ProductFilterInterface::class); parent::__construct($localeDate, $config, $resource, $storeManager); @@ -819,9 +829,11 @@ protected function getItemsPerPage() case 'g': $memoryLimit *= 1024; // fall-through intentional + // no break case 'm': $memoryLimit *= 1024; // fall-through intentional + // no break case 'k': $memoryLimit *= 1024; break; @@ -913,12 +925,7 @@ protected function _prepareEntityCollection(\Magento\Eav\Model\Entity\Collection $exportFilter = !empty($this->_parameters[\Magento\ImportExport\Model\Export::FILTER_ELEMENT_GROUP]) ? $this->_parameters[\Magento\ImportExport\Model\Export::FILTER_ELEMENT_GROUP] : []; - if (isset($exportFilter['category_ids']) - && trim($exportFilter['category_ids']) - && $collection instanceof \Magento\Catalog\Model\ResourceModel\Product\Collection - ) { - $collection->addCategoriesFilter(['in' => explode(',', $exportFilter['category_ids'])]); - } + $collection = $this->filter->filter($collection, $exportFilter); return parent::_prepareEntityCollection($collection); } @@ -979,7 +986,6 @@ protected function loadCollection(): array $collection = $this->_getEntityCollection(); foreach (array_keys($this->_storeIdToCode) as $storeId) { $collection->setOrder('entity_id', 'asc'); - $this->_prepareEntityCollection($collection); $collection->setStoreId($storeId); $collection->load(); foreach ($collection as $itemId => $item) { diff --git a/app/code/Magento/CatalogImportExport/Model/Export/Product/CategoryFilter.php b/app/code/Magento/CatalogImportExport/Model/Export/Product/CategoryFilter.php new file mode 100644 index 0000000000000..2907135695b20 --- /dev/null +++ b/app/code/Magento/CatalogImportExport/Model/Export/Product/CategoryFilter.php @@ -0,0 +1,31 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +declare(strict_types=1); + +namespace Magento\CatalogImportExport\Model\Export\Product; + +use Magento\Catalog\Model\ResourceModel\Product\Collection; +use Magento\CatalogImportExport\Model\Export\ProductFilterInterface; + +/** + * Category filter for products export + */ +class CategoryFilter implements ProductFilterInterface +{ + private const NAME = 'category_ids'; + + /** + * @inheritDoc + */ + public function filter(Collection $collection, array $filters): Collection + { + $value = trim($filters[self::NAME] ?? ''); + if ($value) { + $collection->addCategoriesFilter(['in' => explode(',', $value)]); + } + return $collection; + } +} diff --git a/app/code/Magento/CatalogImportExport/Model/Export/Product/Stock.php b/app/code/Magento/CatalogImportExport/Model/Export/Product/Stock.php new file mode 100644 index 0000000000000..3648487df02ec --- /dev/null +++ b/app/code/Magento/CatalogImportExport/Model/Export/Product/Stock.php @@ -0,0 +1,111 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +declare(strict_types=1); + +namespace Magento\CatalogImportExport\Model\Export\Product; + +use Magento\Catalog\Model\ResourceModel\Product\Collection; +use Magento\CatalogInventory\Model\Configuration; +use Magento\CatalogInventory\Model\ResourceModel\Stock\Item as StockItemResourceModel; +use Magento\Framework\App\Config\ScopeConfigInterface; +use Magento\Store\Model\ScopeInterface; + +/** + * Stock status collection filter + */ +class Stock +{ + /** + * @var ScopeConfigInterface + */ + private $scopeConfig; + /** + * @var StockItemResourceModel + */ + private $stockItemResourceModel; + + /** + * @param ScopeConfigInterface $scopeConfig + * @param StockItemResourceModel $stockItemResourceModel + */ + public function __construct( + ScopeConfigInterface $scopeConfig, + StockItemResourceModel $stockItemResourceModel + ) { + $this->scopeConfig = $scopeConfig; + $this->stockItemResourceModel = $stockItemResourceModel; + } + + /** + * Filter provided collection to return only "in stock" products + * + * @param Collection $collection + * @return Collection + */ + public function addInStockFilterToCollection(Collection $collection): Collection + { + $manageStock = $this->scopeConfig->getValue( + Configuration::XML_PATH_MANAGE_STOCK, + ScopeInterface::SCOPE_STORE + ); + $cond = [ + '{{table}}.use_config_manage_stock = 0 AND {{table}}.manage_stock=1 AND {{table}}.is_in_stock=1', + '{{table}}.use_config_manage_stock = 0 AND {{table}}.manage_stock=0' + ]; + + if ($manageStock) { + $cond[] = '{{table}}.use_config_manage_stock = 1 AND {{table}}.is_in_stock=1'; + } else { + $cond[] = '{{table}}.use_config_manage_stock = 1'; + } + return $this->addFilterToCollection($collection, '(' . join(') OR (', $cond) . ')'); + } + + /** + * Filter provided collection to return only "out of stock" products + * + * @param Collection $collection + * @return Collection + */ + public function addOutOfStockFilterToCollection(Collection $collection): Collection + { + $manageStock = $this->scopeConfig->getValue( + Configuration::XML_PATH_MANAGE_STOCK, + ScopeInterface::SCOPE_STORE + ); + $cond = [ + '{{table}}.use_config_manage_stock = 0 AND {{table}}.manage_stock=1 AND {{table}}.is_in_stock=0', + ]; + + if ($manageStock) { + $cond[] = '{{table}}.use_config_manage_stock = 1 AND {{table}}.is_in_stock=0'; + } + return $this->addFilterToCollection($collection, '(' . join(') OR (', $cond) . ')'); + } + + /** + * Add stock status filter to the collection + * + * @param Collection $collection + * @param string $condition + * @return Collection + */ + private function addFilterToCollection(Collection $collection, string $condition): Collection + { + $condition = str_replace( + '{{table}}', + 'inventory_stock_item_filter', + '({{table}}.product_id=e.entity_id) AND (' . $condition . ')' + ); + $collection->getSelect() + ->joinInner( + ['inventory_stock_item_filter' => $this->stockItemResourceModel->getMainTable()], + $condition, + [] + ); + return $collection; + } +} diff --git a/app/code/Magento/CatalogImportExport/Model/Export/Product/StockStatusFilter.php b/app/code/Magento/CatalogImportExport/Model/Export/Product/StockStatusFilter.php new file mode 100644 index 0000000000000..9a57e58669729 --- /dev/null +++ b/app/code/Magento/CatalogImportExport/Model/Export/Product/StockStatusFilter.php @@ -0,0 +1,58 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +declare(strict_types=1); + +namespace Magento\CatalogImportExport\Model\Export\Product; + +use Magento\Catalog\Model\ResourceModel\Product\Collection; +use Magento\CatalogImportExport\Model\Export\ProductFilterInterface; +use Magento\Framework\App\Config\ScopeConfigInterface; + +/** + * Stock status filter for products export + */ +class StockStatusFilter implements ProductFilterInterface +{ + private const NAME = 'quantity_and_stock_status'; + private const IN_STOCK = '1'; + private const OUT_OF_STOCK = '0'; + /** + * @var Stock + */ + private $stockHelper; + /** + * @var ScopeConfigInterface + */ + private $scopeConfig; + + /** + * @param Stock $stockHelper + * @param ScopeConfigInterface $scopeConfig + */ + public function __construct( + Stock $stockHelper, + ScopeConfigInterface $scopeConfig + ) { + $this->stockHelper = $stockHelper; + $this->scopeConfig = $scopeConfig; + } + /** + * @inheritDoc + */ + public function filter(Collection $collection, array $filters): Collection + { + $value = $filters[self::NAME] ?? ''; + switch ($value) { + case self::IN_STOCK: + $this->stockHelper->addInStockFilterToCollection($collection); + break; + case self::OUT_OF_STOCK: + $this->stockHelper->addOutOfStockFilterToCollection($collection); + break; + } + return $collection; + } +} diff --git a/app/code/Magento/CatalogImportExport/Model/Export/ProductFilterInterface.php b/app/code/Magento/CatalogImportExport/Model/Export/ProductFilterInterface.php new file mode 100644 index 0000000000000..30985f3dc8cd7 --- /dev/null +++ b/app/code/Magento/CatalogImportExport/Model/Export/ProductFilterInterface.php @@ -0,0 +1,22 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +declare(strict_types=1); + +namespace Magento\CatalogImportExport\Model\Export; + +use Magento\Catalog\Model\ResourceModel\Product\Collection; + +interface ProductFilterInterface +{ + /** + * Filter provided product collection + * + * @param Collection $collection + * @param array $filters + * @return Collection + */ + public function filter(Collection $collection, array $filters): Collection; +} diff --git a/app/code/Magento/CatalogImportExport/Model/Export/ProductFilters.php b/app/code/Magento/CatalogImportExport/Model/Export/ProductFilters.php new file mode 100644 index 0000000000000..49e1be8496d30 --- /dev/null +++ b/app/code/Magento/CatalogImportExport/Model/Export/ProductFilters.php @@ -0,0 +1,39 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +declare(strict_types=1); + +namespace Magento\CatalogImportExport\Model\Export; + +use Magento\Catalog\Model\ResourceModel\Product\Collection; + +/** + * Product filters pool for export + */ +class ProductFilters implements ProductFilterInterface +{ + /** + * @var ProductFilterInterface[] + */ + private $filters; + /** + * @param ProductFilterInterface[] $filters + */ + public function __construct(array $filters = []) + { + $this->filters = $filters; + } + + /** + * @inheritDoc + */ + public function filter(Collection $collection, array $filters): Collection + { + foreach ($this->filters as $filter) { + $collection = $filter->filter($collection, $filters); + } + return $collection; + } +} diff --git a/app/code/Magento/CatalogImportExport/Model/Import/Product.php b/app/code/Magento/CatalogImportExport/Model/Import/Product.php index 7ebc397cbe650..c5fcac99767bd 100644 --- a/app/code/Magento/CatalogImportExport/Model/Import/Product.php +++ b/app/code/Magento/CatalogImportExport/Model/Import/Product.php @@ -11,8 +11,11 @@ use Magento\Catalog\Model\Product\Visibility; use Magento\Catalog\Model\ResourceModel\Product\Link; use Magento\CatalogImportExport\Model\Import\Product\ImageTypeProcessor; +use Magento\CatalogImportExport\Model\Import\Product\LinkProcessor; use Magento\CatalogImportExport\Model\Import\Product\MediaGalleryProcessor; use Magento\CatalogImportExport\Model\Import\Product\RowValidatorInterface as ValidatorInterface; +use Magento\CatalogImportExport\Model\Import\Product\StatusProcessor; +use Magento\CatalogImportExport\Model\Import\Product\StockProcessor; use Magento\CatalogImportExport\Model\StockItemImporterInterface; use Magento\CatalogInventory\Api\Data\StockItemInterface; use Magento\Framework\App\Filesystem\DirectoryList; @@ -222,6 +225,8 @@ class Product extends \Magento\ImportExport\Model\Import\Entity\AbstractEntity /** * Links attribute name-to-link type ID. * + * @deprecated use DI for LinkProcessor class if you want to add additional types + * * @var array */ protected $_linkNameToId = [ @@ -305,6 +310,7 @@ class Product extends \Magento\ImportExport\Model\Import\Entity\AbstractEntity // Can't add new translated strings in patch release 'invalidLayoutUpdate' => 'Invalid format.', 'insufficientPermissions' => 'Invalid format.', + ValidatorInterface::ERROR_SKU_MARGINAL_WHITESPACES => 'SKU contains marginal whitespaces' ]; //@codingStandardsIgnoreEnd @@ -746,6 +752,20 @@ class Product extends \Magento\ImportExport\Model\Import\Entity\AbstractEntity */ private $productRepository; + /** + * @var StatusProcessor + */ + private $statusProcessor; + /** + * @var StockProcessor + */ + private $stockProcessor; + + /** + * @var LinkProcessor + */ + private $linkProcessor; + /** * @param \Magento\Framework\Json\Helper\Data $jsonHelper * @param \Magento\ImportExport\Helper\Data $importExportData @@ -791,6 +811,9 @@ class Product extends \Magento\ImportExport\Model\Import\Entity\AbstractEntity * @param StockItemImporterInterface|null $stockItemImporter * @param DateTimeFactory $dateTimeFactory * @param ProductRepositoryInterface|null $productRepository + * @param StatusProcessor|null $statusProcessor + * @param StockProcessor|null $stockProcessor + * @param LinkProcessor|null $linkProcessor * @throws LocalizedException * @throws \Magento\Framework\Exception\FileSystemException * @SuppressWarnings(PHPMD.ExcessiveParameterList) @@ -840,7 +863,10 @@ public function __construct( MediaGalleryProcessor $mediaProcessor = null, StockItemImporterInterface $stockItemImporter = null, DateTimeFactory $dateTimeFactory = null, - ProductRepositoryInterface $productRepository = null + ProductRepositoryInterface $productRepository = null, + StatusProcessor $statusProcessor = null, + StockProcessor $stockProcessor = null, + LinkProcessor $linkProcessor = null ) { $this->_eventManager = $eventManager; $this->stockRegistry = $stockRegistry; @@ -876,6 +902,14 @@ public function __construct( $this->mediaProcessor = $mediaProcessor ?: ObjectManager::getInstance()->get(MediaGalleryProcessor::class); $this->stockItemImporter = $stockItemImporter ?: ObjectManager::getInstance() ->get(StockItemImporterInterface::class); + $this->statusProcessor = $statusProcessor ?: ObjectManager::getInstance() + ->get(StatusProcessor::class); + $this->stockProcessor = $stockProcessor ?: ObjectManager::getInstance() + ->get(StockProcessor::class); + $this->linkProcessor = $linkProcessor ?? ObjectManager::getInstance() + ->get(LinkProcessor::class); + $this->linkProcessor->addNameToIds($this->_linkNameToId); + parent::__construct( $jsonHelper, $importExportData, @@ -1113,6 +1147,7 @@ protected function _replaceProducts() * Save products data. * * @return $this + * @throws LocalizedException */ protected function _saveProductsData() { @@ -1120,7 +1155,7 @@ protected function _saveProductsData() foreach ($this->_productTypeModels as $productTypeModel) { $productTypeModel->saveData(); } - $this->_saveLinks(); + $this->linkProcessor->saveLinks($this, $this->_dataSourceModel, $this->getProductEntityLinkField()); $this->_saveStockItem(); if ($this->_replaceFlag) { $this->getOptionEntity()->clearProductsSkuToId(); @@ -1254,30 +1289,13 @@ protected function _prepareRowForDb(array $rowData) * * Must be called after ALL products saving done. * + * @deprecated use linkProcessor Directly + * * @return $this */ protected function _saveLinks() { - /** @var Link $resource */ - $resource = $this->_linkFactory->create(); - $mainTable = $resource->getMainTable(); - $positionAttrId = []; - $nextLinkId = $this->_resourceHelper->getNextAutoincrement($mainTable); - - // pre-load 'position' attributes ID for each link type once - foreach ($this->_linkNameToId as $linkId) { - $select = $this->_connection->select()->from( - $resource->getTable('catalog_product_link_attribute'), - ['id' => 'product_link_attribute_id'] - )->where( - 'link_type_id = :link_id AND product_link_attribute_code = :position' - ); - $bind = [':link_id' => $linkId, ':position' => 'position']; - $positionAttrId[$linkId] = $this->_connection->fetchOne($select, $bind); - } - while ($bunch = $this->_dataSourceModel->getNextBunch()) { - $this->processLinkBunches($bunch, $resource, $nextLinkId, $positionAttrId); - } + $this->linkProcessor->saveLinks($this, $this->_dataSourceModel, $this->getProductEntityLinkField()); return $this; } @@ -1290,12 +1308,18 @@ protected function _saveLinks() protected function _saveProductAttributes(array $attributesData) { $linkField = $this->getProductEntityLinkField(); + $statusAttributeId = (int) $this->retrieveAttributeByCode('status')->getId(); foreach ($attributesData as $tableName => $skuData) { + $linkIdBySkuForStatusChanged = []; $tableData = []; foreach ($skuData as $sku => $attributes) { $linkId = $this->_oldSku[strtolower($sku)][$linkField]; foreach ($attributes as $attributeId => $storeValues) { foreach ($storeValues as $storeId => $storeValue) { + if ($attributeId === $statusAttributeId) { + $this->statusProcessor->setStatus($sku, $storeId, $storeValue); + $linkIdBySkuForStatusChanged[strtolower($sku)] = $linkId; + } $tableData[] = [ $linkField => $linkId, 'attribute_id' => $attributeId, @@ -1305,6 +1329,9 @@ protected function _saveProductAttributes(array $attributesData) } } } + if ($linkIdBySkuForStatusChanged) { + $this->statusProcessor->loadOldStatus($linkIdBySkuForStatusChanged); + } $this->_connection->insertOnDuplicate($tableName, $tableData, ['value']); } @@ -1505,14 +1532,19 @@ public function getImagesFromRow(array $rowData) return [$images, $labels]; } + // phpcs:disable Generic.Metrics.NestingLevel + /** * Gather and save information about product entities. * + * FIXME: Reduce nesting level + * * @return $this * @SuppressWarnings(PHPMD.CyclomaticComplexity) * @SuppressWarnings(PHPMD.NPathComplexity) * @SuppressWarnings(PHPMD.ExcessiveMethodLength) * @SuppressWarnings(PHPMD.UnusedLocalVariable) + * @SuppressWarnings(PHPMD.UnusedLocalVariable) * @throws LocalizedException * phpcs:disable Generic.Metrics.NestingLevel.TooHigh */ @@ -1694,6 +1726,7 @@ protected function _saveProducts() } $rowData[self::COL_MEDIA_IMAGE] = []; + list($rowImages, $rowData) = $this->clearNoSelectionImages($rowImages, $rowData); /* * Note: to avoid problems with undefined sorting, the value of media gallery items positions @@ -1900,6 +1933,29 @@ protected function _saveProducts() } //phpcs:enable Generic.Metrics.NestingLevel + // phpcs:enable + + /** + * Clears entries from Image Set and Row Data marked as no_selection + * + * @param array $rowImages + * @param array $rowData + * @return array + */ + private function clearNoSelectionImages($rowImages, $rowData) + { + foreach ($rowImages as $column => $columnImages) { + foreach ($columnImages as $key => $image) { + if ($image == 'no_selection') { + unset($rowImages[$column][$key]); + unset($rowData[$column]); + } + } + } + + return [$rowImages, $rowData]; + } + /** * Prepare array with image states (visible or hidden from product page) * @@ -2188,6 +2244,7 @@ protected function _saveStockItem() while ($bunch = $this->_dataSourceModel->getNextBunch()) { $stockData = []; $productIdsToReindex = []; + $stockChangedProductIds = []; // Format bunch to stock data rows foreach ($bunch as $rowNum => $rowData) { if (!$this->isRowAllowedToImport($rowData, $rowNum)) { @@ -2197,8 +2254,16 @@ protected function _saveStockItem() $row = []; $sku = $rowData[self::COL_SKU]; if ($this->skuProcessor->getNewSku($sku) !== null) { + $stockItem = $this->getRowExistingStockItem($rowData); + $existingStockItemData = $stockItem->getData(); $row = $this->formatStockDataForRow($rowData); $productIdsToReindex[] = $row['product_id']; + $storeId = $this->getRowStoreId($rowData); + if (!empty(array_diff_assoc($row, $existingStockItemData)) + || $this->statusProcessor->isStatusChanged($sku, $storeId) + ) { + $stockChangedProductIds[] = $row['product_id']; + } } if (!isset($stockData[$sku])) { @@ -2211,11 +2276,24 @@ protected function _saveStockItem() $this->stockItemImporter->import($stockData); } + $this->reindexStockStatus($stockChangedProductIds); $this->reindexProducts($productIdsToReindex); } return $this; } + /** + * Reindex stock status for provided product IDs + * + * @param array $productIds + */ + private function reindexStockStatus(array $productIds): void + { + if ($productIds) { + $this->stockProcessor->reindexList($productIds); + } + } + /** * Initiate product reindex by product ids * @@ -2471,7 +2549,7 @@ public function validateRow(array $rowData, $rowNum) $this->addRowError( ValidatorInterface::ERROR_DUPLICATE_URL_KEY, $rowNum, - $rowData[self::COL_NAME], + $urlKey, $message, $errorLevel ) @@ -3064,199 +3142,28 @@ private function getValidationErrorLevel($sku): string } /** - * Processes link bunches + * Get row store ID * - * @param array $bunch - * @param Link $resource - * @param int $nextLinkId - * @param array $positionAttrId - * @return void - * @throws LocalizedException - * @SuppressWarnings(PHPMD.CyclomaticComplexity) - */ - private function processLinkBunches( - array $bunch, - Link $resource, - int $nextLinkId, - array $positionAttrId - ): void { - $productIds = []; - $linkRows = []; - $positionRows = []; - $linksToDelete = []; - - $bunch = array_filter($bunch, [$this, 'isRowAllowedToImport'], ARRAY_FILTER_USE_BOTH); - foreach ($bunch as $rowData) { - $sku = $rowData[self::COL_SKU]; - $productId = $this->skuProcessor->getNewSku($sku)[$this->getProductEntityLinkField()]; - $productIds[] = $productId; - $productLinkKeys = $this->fetchProductLinks($resource, $productId); - $linkNameToId = array_filter( - $this->_linkNameToId, - function ($linkName) use ($rowData) { - return isset($rowData[$linkName . 'sku']); - }, - ARRAY_FILTER_USE_KEY - ); - foreach ($linkNameToId as $linkName => $linkId) { - $linkSkus = explode($this->getMultipleValueSeparator(), $rowData[$linkName . 'sku']); - //process empty value - if (!empty($linkSkus[0]) && $linkSkus[0] === $this->getEmptyAttributeValueConstant()) { - $linksToDelete[$linkId][] = $productId; - continue; - } - - $linkPositions = !empty($rowData[$linkName . 'position']) - ? explode($this->getMultipleValueSeparator(), $rowData[$linkName . 'position']) - : []; - $linkSkus = array_filter( - $linkSkus, - function ($linkedSku) use ($sku) { - $linkedSku = trim($linkedSku); - return ($this->skuProcessor->getNewSku($linkedSku) !== null || $this->isSkuExist($linkedSku)) - && strcasecmp($linkedSku, $sku) !== 0; - } - ); - - foreach ($linkSkus as $linkedKey => $linkedSku) { - $linkedId = $this->getProductLinkedId($linkedSku); - if ($linkedId == null) { - // Import file links to a SKU which is skipped for some reason, which leads to a "NULL" - // link causing fatal errors. - $formatStr = 'WARNING: Orphaned link skipped: From SKU %s (ID %d) to SKU %s, Link type id: %d'; - $exception = new \Exception(sprintf($formatStr, $sku, $productId, $linkedSku, $linkId)); - $this->_logger->critical($exception); - continue; - } - $linkKey = $this->composeLinkKey($productId, $linkedId, $linkId); - $productLinkKeys[$linkKey] = $productLinkKeys[$linkKey] ?? $nextLinkId; - - $linkRows[$linkKey] = $linkRows[$linkKey] ?? [ - 'link_id' => $productLinkKeys[$linkKey], - 'product_id' => $productId, - 'linked_product_id' => $linkedId, - 'link_type_id' => $linkId, - ]; - - if (!empty($linkPositions[$linkedKey])) { - $positionRows[] = [ - 'link_id' => $productLinkKeys[$linkKey], - 'product_link_attribute_id' => $positionAttrId[$linkId], - 'value' => $linkPositions[$linkedKey], - ]; - } - $nextLinkId++; - } - } - } - $this->deleteProductsLinks($resource, $linksToDelete); - $this->saveLinksData($resource, $productIds, $linkRows, $positionRows); - } - - /** - * Delete links - * - * @param Link $resource - * @param array $linksToDelete - * @return void - * @throws LocalizedException - */ - private function deleteProductsLinks(Link $resource, array $linksToDelete) - { - if (!empty($linksToDelete) && Import::BEHAVIOR_APPEND === $this->getBehavior()) { - foreach ($linksToDelete as $linkTypeId => $productIds) { - if (!empty($productIds)) { - $whereLinkId = $this->_connection->quoteInto('link_type_id', $linkTypeId); - $whereProductId = $this->_connection->quoteInto('product_id IN (?)', array_unique($productIds)); - $this->_connection->delete( - $resource->getMainTable(), - $whereLinkId . ' AND ' . $whereProductId - ); - } - } - } - } - - /** - * Fetches Product Links - * - * @param Link $resource - * @param int $productId - * @return array - */ - private function fetchProductLinks(Link $resource, int $productId) : array - { - $productLinkKeys = []; - $select = $this->_connection->select()->from( - $resource->getTable('catalog_product_link'), - ['id' => 'link_id', 'linked_id' => 'linked_product_id', 'link_type_id' => 'link_type_id'] - )->where( - 'product_id = :product_id' - ); - $bind = [':product_id' => $productId]; - foreach ($this->_connection->fetchAll($select, $bind) as $linkData) { - $linkKey = $this->composeLinkKey($productId, $linkData['linked_id'], $linkData['link_type_id']); - $productLinkKeys[$linkKey] = $linkData['id']; - } - - return $productLinkKeys; - } - - /** - * Gets the Id of the Sku - * - * @param string $linkedSku - * @return int|null - */ - private function getProductLinkedId(string $linkedSku) : ?int - { - $linkedSku = trim($linkedSku); - $newSku = $this->skuProcessor->getNewSku($linkedSku); - $linkedId = !empty($newSku) ? $newSku['entity_id'] : $this->getExistingSku($linkedSku)['entity_id']; - return $linkedId; - } - - /** - * Saves information about product links - * - * @param Link $resource - * @param array $productIds - * @param array $linkRows - * @param array $positionRows - * @throws LocalizedException + * @param array $rowData + * @return int */ - private function saveLinksData(Link $resource, array $productIds, array $linkRows, array $positionRows) + private function getRowStoreId(array $rowData): int { - $mainTable = $resource->getMainTable(); - if (Import::BEHAVIOR_APPEND != $this->getBehavior() && $productIds) { - $this->_connection->delete( - $mainTable, - $this->_connection->quoteInto('product_id IN (?)', array_unique($productIds)) - ); - } - if ($linkRows) { - $this->_connection->insertOnDuplicate($mainTable, $linkRows, ['link_id']); - } - if ($positionRows) { - // process linked product positions - $this->_connection->insertOnDuplicate( - $resource->getAttributeTypeTable('int'), - $positionRows, - ['value'] - ); - } + return !empty($rowData[self::COL_STORE]) + ? (int) $this->getStoreIdByCode($rowData[self::COL_STORE]) + : Store::DEFAULT_STORE_ID; } /** - * Composes the link key + * Get row stock item model * - * @param int $productId - * @param int $linkedId - * @param int $linkTypeId - * @return string + * @param array $rowData + * @return StockItemInterface */ - private function composeLinkKey(int $productId, int $linkedId, int $linkTypeId) : string + private function getRowExistingStockItem(array $rowData): StockItemInterface { - return "{$productId}-{$linkedId}-{$linkTypeId}"; + $productId = $this->skuProcessor->getNewSku($rowData[self::COL_SKU])['entity_id']; + $websiteId = $this->stockConfiguration->getDefaultScopeId(); + return $this->stockRegistry->getStockItem($productId, $websiteId); } } diff --git a/app/code/Magento/CatalogImportExport/Model/Import/Product/LinkProcessor.php b/app/code/Magento/CatalogImportExport/Model/Import/Product/LinkProcessor.php new file mode 100644 index 0000000000000..a45338c391a58 --- /dev/null +++ b/app/code/Magento/CatalogImportExport/Model/Import/Product/LinkProcessor.php @@ -0,0 +1,401 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +namespace Magento\CatalogImportExport\Model\Import\Product; + +use Magento\Catalog\Model\ResourceModel\Product\Link; +use Magento\Catalog\Model\ResourceModel\Product\LinkFactory; +use Magento\CatalogImportExport\Model\Import\Product; +use Magento\Framework\Exception\LocalizedException; +use Magento\ImportExport\Model\Import; +use Magento\ImportExport\Model\ResourceModel\Helper; +use Magento\ImportExport\Model\ResourceModel\Import\Data; +use Psr\Log\LoggerInterface; + +/** + * Processor for links between products + * + * Remark: Via DI it is possible to supply additional link types. + */ +class LinkProcessor +{ + /** + * @var array + */ + private $linkNameToId; + + /** + * @var LinkFactory + */ + private $linkFactory; + + /** + * @var Helper + */ + private $resourceHelper; + + /** + * @var SkuProcessor + */ + private $skuProcessor; + + /** + * @var LoggerInterface + */ + private $logger; + + /** + * LinkProcessor constructor. + * + * @param LinkFactory $linkFactory + * @param Helper $resourceHelper + * @param SkuProcessor $skuProcessor + * @param LoggerInterface $logger + * @param array $linkNameToId + */ + public function __construct( + LinkFactory $linkFactory, + Helper $resourceHelper, + SkuProcessor $skuProcessor, + LoggerInterface $logger, + array $linkNameToId + ) { + $this->linkFactory = $linkFactory; + $this->resourceHelper = $resourceHelper; + $this->skuProcessor = $skuProcessor; + $this->logger = $logger; + + $this->linkNameToId = $linkNameToId; + } + + /** + * Gather and save information about product links. + * + * Must be called after ALL products saving done. + * + * @param Product $importEntity + * @param Data $dataSourceModel + * @param string $linkField + * @return void + * @throws LocalizedException + */ + public function saveLinks( + Product $importEntity, + Data $dataSourceModel, + string $linkField + ): void { + $resource = $this->linkFactory->create(); + $mainTable = $resource->getMainTable(); + $positionAttrId = []; + $nextLinkId = $this->resourceHelper->getNextAutoincrement($mainTable); + + // pre-load 'position' attributes ID for each link type once + foreach ($this->linkNameToId as $linkId) { + $select = $importEntity->getConnection()->select()->from( + $resource->getTable('catalog_product_link_attribute'), + ['id' => 'product_link_attribute_id'] + )->where( + 'link_type_id = :link_id AND product_link_attribute_code = :position' + ); + $bind = [':link_id' => $linkId, ':position' => 'position']; + $positionAttrId[$linkId] = $importEntity->getConnection()->fetchOne($select, $bind); + } + while ($bunch = $dataSourceModel->getNextBunch()) { + $this->processLinkBunches($importEntity, $linkField, $bunch, $resource, $nextLinkId, $positionAttrId); + } + } + + /** + * Add link types (exists for backwards compatibility) + * + * @deprecated Use DI to inject to the constructor + * @param array $nameToIds + */ + public function addNameToIds(array $nameToIds): void + { + $this->linkNameToId = array_merge($nameToIds, $this->linkNameToId); + } + + /** + * Processes link bunches + * + * @param Product $importEntity + * @param string $linkField + * @param array $bunch + * @param Link $resource + * @param int $nextLinkId + * @param array $positionAttrId + * + * @return void + * @throws LocalizedException + */ + private function processLinkBunches( + Product $importEntity, + string $linkField, + array $bunch, + Link $resource, + int $nextLinkId, + array $positionAttrId + ): void { + $productIds = []; + $linkRows = []; + $positionRows = []; + $linksToDelete = []; + + $bunch = array_filter($bunch, [$importEntity, 'isRowAllowedToImport'], ARRAY_FILTER_USE_BOTH); + foreach ($bunch as $rowData) { + $sku = $rowData[Product::COL_SKU]; + $productId = $this->skuProcessor->getNewSku($sku)[$linkField]; + $productIds[] = $productId; + $productLinkKeys = $this->fetchProductLinks($importEntity, $resource, $productId); + $linkNameToId = $this->filterProvidedLinkTypes($rowData); + + foreach ($linkNameToId as $linkName => $linkId) { + $linkSkus = explode($importEntity->getMultipleValueSeparator(), $rowData[$linkName . 'sku']); + + //process empty value + if (!empty($linkSkus[0]) && $linkSkus[0] === $importEntity->getEmptyAttributeValueConstant()) { + $linksToDelete[$linkId][] = $productId; + continue; + } + + $linkPositions = ! empty($rowData[$linkName . 'position']) + ? explode($importEntity->getMultipleValueSeparator(), $rowData[$linkName . 'position']) + : []; + + $linkSkus = $this->filterValidLinks($importEntity, $sku, $linkSkus); + + foreach ($linkSkus as $linkedKey => $linkedSku) { + $linkedId = $this->getProductLinkedId($importEntity, $linkedSku); + if ($linkedId == null) { + // Import file links to a SKU which is skipped for some reason, which leads to a "NULL" + // link causing fatal errors. + $formatStr = 'WARNING: Orphaned link skipped: From SKU %s (ID %d) to SKU %s, Link type id: %d'; + $exception = new \Exception(sprintf($formatStr, $sku, $productId, $linkedSku, $linkId)); + $this->logger->critical($exception); + continue; + } + $linkKey = $this->composeLinkKey($productId, $linkedId, $linkId); + $productLinkKeys[$linkKey] = $productLinkKeys[$linkKey] ?? $nextLinkId; + + $linkRows[$linkKey] = $linkRows[$linkKey] ?? [ + 'link_id' => $productLinkKeys[$linkKey], + 'product_id' => $productId, + 'linked_product_id' => $linkedId, + 'link_type_id' => $linkId, + ]; + + if (! empty($linkPositions[$linkedKey])) { + $positionRows[] = [ + 'link_id' => $productLinkKeys[$linkKey], + 'product_link_attribute_id' => $positionAttrId[$linkId], + 'value' => $linkPositions[$linkedKey], + ]; + } + $nextLinkId++; + } + } + } + + $this->deleteProductsLinks($importEntity, $resource, $linksToDelete); + $this->saveLinksData($importEntity, $resource, $productIds, $linkRows, $positionRows); + } + + /** + * Delete links + * + * @param Product $importEntity + * @param Link $resource + * @param array $linksToDelete + * @return void + * @throws LocalizedException + */ + private function deleteProductsLinks( + Product $importEntity, + Link $resource, + array $linksToDelete + ) { + if (!empty($linksToDelete) && Import::BEHAVIOR_APPEND === $importEntity->getBehavior()) { + foreach ($linksToDelete as $linkTypeId => $productIds) { + if (!empty($productIds)) { + $whereLinkId = $importEntity->getConnection()->quoteInto('link_type_id', $linkTypeId); + $whereProductId = $importEntity->getConnection()->quoteInto( + 'product_id IN (?)', + array_unique($productIds) + ); + $importEntity->getConnection()->delete( + $resource->getMainTable(), + $whereLinkId . ' AND ' . $whereProductId + ); + } + } + } + } + + /** + * Check if product exists for specified SKU + * + * @param Product $importEntity + * @param string $sku + * @return bool + */ + private function isSkuExist(Product $importEntity, string $sku): bool + { + $sku = strtolower($sku); + return isset($importEntity->getOldSku()[$sku]); + } + + /** + * Get existing SKU record + * + * @param Product $importEntity + * @param string $sku + * @return mixed + */ + private function getExistingSku(Product $importEntity, string $sku) + { + $sku = strtolower($sku); + return $importEntity->getOldSku()[$sku]; + } + + /** + * Fetches Product Links + * + * @param Product $importEntity + * @param Link $resource + * @param int $productId + * + * @return array + */ + private function fetchProductLinks(Product $importEntity, Link $resource, int $productId): array + { + $productLinkKeys = []; + $select = $importEntity->getConnection()->select()->from( + $resource->getTable('catalog_product_link'), + ['id' => 'link_id', 'linked_id' => 'linked_product_id', 'link_type_id' => 'link_type_id'] + )->where( + 'product_id = :product_id' + ); + $bind = [':product_id' => $productId]; + foreach ($importEntity->getConnection()->fetchAll($select, $bind) as $linkData) { + $linkKey = $this->composeLinkKey($productId, $linkData['linked_id'], $linkData['link_type_id']); + $productLinkKeys[$linkKey] = $linkData['id']; + } + + return $productLinkKeys; + } + + /** + * Gets the Id of the Sku + * + * @param Product $importEntity + * @param string $linkedSku + * @return int|null + */ + private function getProductLinkedId(Product $importEntity, string $linkedSku): ?int + { + $linkedSku = trim($linkedSku); + $newSku = $this->skuProcessor->getNewSku($linkedSku); + + $linkedId = ! empty($newSku) ? + $newSku['entity_id'] : + $this->getExistingSku($importEntity, $linkedSku)['entity_id']; + + return $linkedId; + } + + /** + * Saves information about product links + * + * @param Product $importEntity + * @param Link $resource + * @param array $productIds + * @param array $linkRows + * @param array $positionRows + * + * @throws LocalizedException + */ + private function saveLinksData( + Product $importEntity, + Link $resource, + array $productIds, + array $linkRows, + array $positionRows + ) { + $mainTable = $resource->getMainTable(); + if (Import::BEHAVIOR_APPEND != $importEntity->getBehavior() && $productIds) { + $importEntity->getConnection()->delete( + $mainTable, + $importEntity->getConnection()->quoteInto('product_id IN (?)', array_unique($productIds)) + ); + } + if ($linkRows) { + $importEntity->getConnection()->insertOnDuplicate($mainTable, $linkRows, ['link_id']); + } + if ($positionRows) { + // process linked product positions + $importEntity->getConnection()->insertOnDuplicate( + $resource->getAttributeTypeTable('int'), + $positionRows, + ['value'] + ); + } + } + + /** + * Composes the link key + * + * @param int $productId + * @param int $linkedId + * @param int $linkTypeId + * + * @return string + */ + private function composeLinkKey(int $productId, int $linkedId, int $linkTypeId): string + { + return "{$productId}-{$linkedId}-{$linkTypeId}"; + } + + /** + * Filter out link types which are not provided in the rowData + * + * @param array $rowData + * @return array + */ + private function filterProvidedLinkTypes(array $rowData) + { + return array_filter( + $this->linkNameToId, + function ($linkName) use ($rowData) { + return isset($rowData[$linkName . 'sku']); + }, + ARRAY_FILTER_USE_KEY + ); + } + + /** + * Filter out invalid links + * + * @param Product $importEntity + * @param string $sku + * @param array $linkSkus + * @return array + */ + private function filterValidLinks(Product $importEntity, string $sku, array $linkSkus) + { + return array_filter( + $linkSkus, + function ($linkedSku) use ($sku, $importEntity) { + $linkedSku = trim($linkedSku); + + return ( + $this->skuProcessor->getNewSku($linkedSku) !== null + || $this->isSkuExist($importEntity, $linkedSku) + ) + && strcasecmp($linkedSku, $sku) !== 0; + } + ); + } +} diff --git a/app/code/Magento/CatalogImportExport/Model/Import/Product/MediaGalleryProcessor.php b/app/code/Magento/CatalogImportExport/Model/Import/Product/MediaGalleryProcessor.php index bd8523a4e396e..a94a87a44b32a 100644 --- a/app/code/Magento/CatalogImportExport/Model/Import/Product/MediaGalleryProcessor.php +++ b/app/code/Magento/CatalogImportExport/Model/Import/Product/MediaGalleryProcessor.php @@ -108,40 +108,161 @@ public function __construct( public function saveMediaGallery(array $mediaGalleryData) { $this->initMediaGalleryResources(); - $mediaGalleryDataGlobal = array_replace_recursive(...$mediaGalleryData); - $imageNames = []; - $multiInsertData = []; - $valueToProductId = []; - foreach ($mediaGalleryDataGlobal as $productSku => $mediaGalleryRows) { - $productId = $this->skuProcessor->getNewSku($productSku)[$this->getProductEntityLinkField()]; - $insertedGalleryImgs = []; - foreach ($mediaGalleryRows as $insertValue) { - if (!in_array($insertValue['value'], $insertedGalleryImgs)) { - $valueArr = [ - 'attribute_id' => $insertValue['attribute_id'], - 'value' => $insertValue['value'], + $mediaGalleryValues = []; + $mediaGalleryValueData = []; + $productMediaGalleryValueData = []; + $mediaGalleryValueToEntityData = []; + $mediaGalleryValueToStoreData = []; + $productLinkIdField = $this->getProductEntityLinkField(); + foreach ($mediaGalleryData as $storeId => $storeMediaGalleryData) { + foreach ($storeMediaGalleryData as $sku => $productMediaGalleryData) { + $productId = $this->skuProcessor->getNewSku($sku)[$productLinkIdField]; + $productMediaGalleryValueData[$productId] = $productMediaGalleryValueData[$productId] ?? []; + foreach ($productMediaGalleryData as $data) { + if (!in_array($data['value'], $productMediaGalleryValueData[$productId])) { + $productMediaGalleryValueData[$productId][] = $data['value']; + $mediaGalleryValueData[] = [ + 'attribute_id' => $data['attribute_id'], + 'value' => $data['value'], + ]; + $mediaGalleryValueToEntityData[] = [ + 'value' => $data['value'], + $productLinkIdField => $productId, + ]; + } + $mediaGalleryValues[] = $data['value']; + $mediaGalleryValueToStoreData[] = [ + 'value' => $data['value'], + 'store_id' => $storeId, + $productLinkIdField => $productId, + 'label' => $data['label'], + 'position' => $data['position'], + 'disabled' => $data['disabled'], ]; - $valueToProductId[$insertValue['value']][] = $productId; - $imageNames[] = $insertValue['value']; - $multiInsertData[] = $valueArr; - $insertedGalleryImgs[] = $insertValue['value']; } } } - $oldMediaValues = $this->connection->fetchAssoc( - $this->connection->select()->from($this->mediaGalleryTableName, ['value_id', 'value']) - ->where('value IN (?)', $imageNames) - ); - $this->connection->insertOnDuplicate($this->mediaGalleryTableName, $multiInsertData); - $newMediaSelect = $this->connection->select()->from($this->mediaGalleryTableName, ['value_id', 'value']) - ->where('value IN (?)', $imageNames); - if (array_keys($oldMediaValues)) { - $newMediaSelect->where('value_id NOT IN (?)', array_keys($oldMediaValues)); + try { + $mediaValueIdValueMap = []; + $oldMediaValues = $this->connection->fetchCol( + $this->connection->select() + ->from($this->mediaGalleryTableName, ['value_id']) + ->where('value IN (?)', $mediaGalleryValues) + ); + $this->connection->insertOnDuplicate( + $this->mediaGalleryTableName, + $mediaGalleryValueData + ); + $newMediaSelect = $this->connection->select() + ->from($this->mediaGalleryTableName, ['value_id', 'value']) + ->where('value IN (?)', $mediaGalleryValues); + if ($oldMediaValues) { + $newMediaSelect->where('value_id NOT IN (?)', $oldMediaValues); + } + $mediaValueIdValueMap = $this->connection->fetchPairs($newMediaSelect); + $productIdMediaValueIdMap = $this->getProductIdMediaValueIdMap( + $productMediaGalleryValueData, + $mediaValueIdValueMap + ); + $mediaGalleryValueToEntityData = $this->prepareMediaGalleryValueToEntityData( + $mediaGalleryValueToEntityData, + $productIdMediaValueIdMap + ); + $this->connection->insertOnDuplicate( + $this->mediaGalleryEntityToValueTableName, + $mediaGalleryValueToEntityData, + ['value_id'] + ); + $mediaGalleryValueToStoreData = $this->prepareMediaGalleryValueData( + $mediaGalleryValueToStoreData, + $productIdMediaValueIdMap + ); + $this->connection->insertOnDuplicate( + $this->mediaGalleryValueTableName, + $mediaGalleryValueToStoreData, + ['value_id', 'store_id', $productLinkIdField, 'label', 'position', 'disabled'] + ); + } catch (\Throwable $exception) { + if ($mediaValueIdValueMap) { + $this->connection->delete( + $this->mediaGalleryTableName, + $this->connection->quoteInto('value_id IN (?)', array_keys($mediaValueIdValueMap)) + ); + } + throw $exception; } - $newMediaValues = $this->connection->fetchAssoc($newMediaSelect); - foreach ($mediaGalleryData as $storeId => $storeMediaGalleryData) { - $this->processMediaPerStore((int)$storeId, $storeMediaGalleryData, $newMediaValues, $valueToProductId); + } + + /** + * Get media values IDs per products IDs + * + * @param array $productMediaGalleryValueData + * @param array $mediaValueIdValueMap + * @return array + */ + private function getProductIdMediaValueIdMap( + array $productMediaGalleryValueData, + array $mediaValueIdValueMap + ): array { + $productIdMediaValueIdMap = []; + foreach ($productMediaGalleryValueData as $productId => $productMediaGalleryValues) { + foreach ($productMediaGalleryValues as $productMediaGalleryValue) { + foreach ($mediaValueIdValueMap as $valueId => $value) { + if ($productMediaGalleryValue === $value) { + $productIdMediaValueIdMap[$productId][$value] = $valueId; + unset($mediaValueIdValueMap[$valueId]); + break; + } + } + } + } + return $productIdMediaValueIdMap; + } + + /** + * Prepare media entity gallery value to entity data for insert + * + * @param array $mediaGalleryValueToEntityData + * @param array $productIdMediaValueIdMap + * @return array + */ + private function prepareMediaGalleryValueToEntityData( + array $mediaGalleryValueToEntityData, + array $productIdMediaValueIdMap + ): array { + $productLinkIdField = $this->getProductEntityLinkField(); + foreach ($mediaGalleryValueToEntityData as $index => $data) { + $productId = $data[$productLinkIdField]; + $value = $data['value']; + $mediaGalleryValueToEntityData[$index]['value_id'] = $productIdMediaValueIdMap[$productId][$value]; + unset($mediaGalleryValueToEntityData[$index]['value']); + } + return $mediaGalleryValueToEntityData; + } + + /** + * Prepare media entity gallery value data for insert + * + * @param array $mediaGalleryValueData + * @param array $productIdMediaValueIdMap + * @return array + */ + private function prepareMediaGalleryValueData( + array $mediaGalleryValueData, + array $productIdMediaValueIdMap + ): array { + $productLinkIdField = $this->getProductEntityLinkField(); + $lastPositions = $this->getLastMediaPositionPerProduct(array_keys($productIdMediaValueIdMap)); + foreach ($mediaGalleryValueData as $index => $data) { + $productId = $data[$productLinkIdField]; + $value = $data['value']; + $position = $data['position']; + $storeId = $data['store_id']; + $mediaGalleryValueData[$index]['value_id'] = $productIdMediaValueIdMap[$productId][$value]; + $mediaGalleryValueData[$index]['position'] = $position + ($lastPositions[$storeId][$productId] ?? 0); + unset($mediaGalleryValueData[$index]['value']); } + return $mediaGalleryValueData; } /** @@ -289,13 +410,12 @@ private function initMediaGalleryResources() } /** - * Get the last media position for each product from the given list + * Get the last media position for each product per store from the given list * - * @param int $storeId * @param array $productIds * @return array */ - private function getLastMediaPositionPerProduct(int $storeId, array $productIds): array + private function getLastMediaPositionPerProduct(array $productIds): array { $result = []; if ($productIds) { @@ -305,95 +425,25 @@ private function getLastMediaPositionPerProduct(int $storeId, array $productIds) $positions = $this->connection->fetchAll( $this->connection ->select() - ->from($this->mediaGalleryValueTableName, [$productKeyName, 'position']) + ->from($this->mediaGalleryValueTableName, [$productKeyName, 'store_id', 'position']) ->where("$productKeyName IN (?)", $productIds) - ->where('value_id is not null') - ->where('store_id = ?', $storeId) ); - // Make sure the result contains all product ids even if the product has no media files - $result = array_fill_keys($productIds, 0); // Find the largest position for each product foreach ($positions as $record) { $productId = $record[$productKeyName]; - $result[$productId] = $result[$productId] < $record['position'] + $storeId = $record['store_id']; + if (!isset($result[$storeId][$productId])) { + $result[$storeId][$productId] = 0; + } + $result[$storeId][$productId] = $result[$storeId][$productId] < $record['position'] ? $record['position'] - : $result[$productId]; + : $result[$storeId][$productId]; } } return $result; } - /** - * Save media gallery data per store. - * - * @param int $storeId - * @param array $mediaGalleryData - * @param array $newMediaValues - * @param array $valueToProductId - * @return void - */ - private function processMediaPerStore( - int $storeId, - array $mediaGalleryData, - array $newMediaValues, - array $valueToProductId - ) { - $multiInsertData = []; - $dataForSkinnyTable = []; - $lastMediaPositionPerProduct = $this->getLastMediaPositionPerProduct( - $storeId, - array_unique(array_merge(...array_values($valueToProductId))) - ); - - foreach ($mediaGalleryData as $mediaGalleryRows) { - foreach ($mediaGalleryRows as $insertValue) { - foreach ($newMediaValues as $valueId => $values) { - if ($values['value'] == $insertValue['value']) { - $insertValue['value_id'] = $valueId; - $insertValue[$this->getProductEntityLinkField()] - = array_shift($valueToProductId[$values['value']]); - unset($newMediaValues[$valueId]); - break; - } - } - if (isset($insertValue['value_id'])) { - $productId = $insertValue[$this->getProductEntityLinkField()]; - $valueArr = [ - 'value_id' => $insertValue['value_id'], - 'store_id' => $storeId, - $this->getProductEntityLinkField() => $productId, - 'label' => $insertValue['label'], - 'position' => $lastMediaPositionPerProduct[$productId] + $insertValue['position'], - 'disabled' => $insertValue['disabled'], - ]; - $multiInsertData[] = $valueArr; - $dataForSkinnyTable[] = [ - 'value_id' => $insertValue['value_id'], - $this->getProductEntityLinkField() => $insertValue[$this->getProductEntityLinkField()], - ]; - } - } - } - try { - $this->connection->insertOnDuplicate( - $this->mediaGalleryValueTableName, - $multiInsertData, - ['value_id', 'store_id', $this->getProductEntityLinkField(), 'label', 'position', 'disabled'] - ); - $this->connection->insertOnDuplicate( - $this->mediaGalleryEntityToValueTableName, - $dataForSkinnyTable, - ['value_id'] - ); - } catch (\Exception $e) { - $this->connection->delete( - $this->mediaGalleryTableName, - $this->connection->quoteInto('value_id IN (?)', $newMediaValues) - ); - } - } - /** * Get product entity link field. * diff --git a/app/code/Magento/CatalogImportExport/Model/Import/Product/Option.php b/app/code/Magento/CatalogImportExport/Model/Import/Product/Option.php index 4c716421b7ae6..e12fc726f1056 100644 --- a/app/code/Magento/CatalogImportExport/Model/Import/Product/Option.php +++ b/app/code/Magento/CatalogImportExport/Model/Import/Product/Option.php @@ -6,14 +6,14 @@ namespace Magento\CatalogImportExport\Model\Import\Product; -use Magento\CatalogImportExport\Model\Import\Product; -use Magento\Framework\App\ResourceConnection; -use Magento\ImportExport\Model\Import\ErrorProcessing\ProcessingErrorAggregatorInterface; use Magento\Catalog\Api\Data\ProductInterface; use Magento\Catalog\Model\ResourceModel\Product\Option\Value\Collection as ProductOptionValueCollection; use Magento\Catalog\Model\ResourceModel\Product\Option\Value\CollectionFactory as ProductOptionValueCollectionFactory; -use Magento\Store\Model\Store; +use Magento\CatalogImportExport\Model\Import\Product; +use Magento\Framework\App\ResourceConnection; use Magento\ImportExport\Model\Import; +use Magento\ImportExport\Model\Import\ErrorProcessing\ProcessingErrorAggregatorInterface; +use Magento\Store\Model\Store; /** * Entity class which provide possibility to import product custom options @@ -110,6 +110,13 @@ class Option extends \Magento\ImportExport\Model\Import\Entity\AbstractEntity 'file' => ['sku', 'file_extension', 'image_size_x', 'image_size_y'], ]; + /** + * Invalid rows list + * + * @var array + */ + private $_invalidRows; + /** * Keep product id value for every row which will be imported * @@ -433,7 +440,7 @@ protected function _initMessageTemplates() self::ERROR_INVALID_TYPE, __( 'Value for \'type\' sub attribute in \'custom_options\' attribute contains incorrect value, acceptable values are: %1', - '\''.implode('\', \'', array_keys($this->_specificTypes)).'\'' + '\'' . implode('\', \'', array_keys($this->_specificTypes)) . '\'' ) ); $this->_productEntity->addMessageTemplate(self::ERROR_EMPTY_TITLE, __('Please enter a value for title.')); @@ -1251,7 +1258,9 @@ protected function _importData() $childCount = []; $optionsToRemove = []; foreach ($bunch as $rowNumber => $rowData) { - if (isset($optionId, $valueId) && empty($rowData[PRODUCT::COL_STORE_VIEW_CODE])) { + if (isset($optionId, $valueId) && + (empty($rowData[PRODUCT::COL_STORE_VIEW_CODE]) || empty($rowData['custom_options'])) + ) { $nextOptionId = $optionId; $nextValueId = $valueId; } @@ -1548,8 +1557,8 @@ protected function _collectOptionTitle(array $rowData, $prevOptionId, array &$ti if (!empty($rowData[self::COLUMN_TITLE])) { if (!isset($titles[$prevOptionId][$defaultStoreId])) { if (isset($this->lastOptionTitle[$prevOptionId])) { - $titles[$prevOptionId] = $this->lastOptionTitle[$prevOptionId]; - unset($this->lastOptionTitle); + $titles[$prevOptionId] = $this->lastOptionTitle[$prevOptionId]; + unset($this->lastOptionTitle); } else { $titles[$prevOptionId][$defaultStoreId] = $rowData[self::COLUMN_TITLE]; } diff --git a/app/code/Magento/CatalogImportExport/Model/Import/Product/RowValidatorInterface.php b/app/code/Magento/CatalogImportExport/Model/Import/Product/RowValidatorInterface.php index f41596ad185a6..f13b603003898 100644 --- a/app/code/Magento/CatalogImportExport/Model/Import/Product/RowValidatorInterface.php +++ b/app/code/Magento/CatalogImportExport/Model/Import/Product/RowValidatorInterface.php @@ -87,6 +87,8 @@ interface RowValidatorInterface extends \Magento\Framework\Validator\ValidatorIn const ERROR_DUPLICATE_MULTISELECT_VALUES = 'duplicatedMultiselectValues'; + const ERROR_SKU_MARGINAL_WHITESPACES = 'skuMarginalWhitespaces'; + /** * Value that means all entities (e.g. websites, groups etc.) */ diff --git a/app/code/Magento/CatalogImportExport/Model/Import/Product/StatusProcessor.php b/app/code/Magento/CatalogImportExport/Model/Import/Product/StatusProcessor.php new file mode 100644 index 0000000000000..1c6d679848216 --- /dev/null +++ b/app/code/Magento/CatalogImportExport/Model/Import/Product/StatusProcessor.php @@ -0,0 +1,153 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +declare(strict_types=1); + +namespace Magento\CatalogImportExport\Model\Import\Product; + +use Magento\Catalog\Api\Data\ProductInterface; +use Magento\CatalogImportExport\Model\Import\Proxy\Product\ResourceModelFactory; +use Magento\Eav\Model\Entity\Attribute\AbstractAttribute; +use Magento\Framework\App\ResourceConnection; +use Magento\Framework\EntityManager\MetadataPool; + +/** + * Imported product status state manager + */ +class StatusProcessor +{ + private const ATTRIBUTE_CODE = 'status'; + /** + * @var array + */ + private $oldData; + /** + * @var array + */ + private $newData; + /** + * @var ResourceModelFactory + */ + private $resourceFactory; + /** + * @var ResourceConnection + */ + private $resourceConnection; + /** + * @var MetadataPool + */ + private $metadataPool; + /** + * @var string + */ + private $productEntityLinkField; + /** + * @var AbstractAttribute + */ + private $attribute; + + /** + * Initializes dependencies. + * + * @param MetadataPool $metadataPool + * @param ResourceModelFactory $resourceFactory + * @param ResourceConnection $resourceConnection + */ + public function __construct( + MetadataPool $metadataPool, + ResourceModelFactory $resourceFactory, + ResourceConnection $resourceConnection + ) { + $this->oldData = []; + $this->newData = []; + $this->resourceFactory = $resourceFactory; + $this->resourceConnection = $resourceConnection; + $this->metadataPool = $metadataPool; + } + + /** + * Check if status has changed for given (sku, storeId) + * + * @param string $sku + * @param int $storeId + * @return bool + */ + public function isStatusChanged(string $sku, int $storeId): bool + { + $sku = strtolower($sku); + if (!isset($this->newData[$sku][$storeId])) { + $changed = false; + } elseif (!isset($this->oldData[$sku][$storeId])) { + $changed = true; + } else { + $oldStatus = (int) $this->oldData[$sku][$storeId]; + $newStatus = (int) $this->newData[$sku][$storeId]; + $changed = $oldStatus !== $newStatus; + } + return $changed; + } + + /** + * Load old status data + * + * @param array $linkIdBySku + */ + public function loadOldStatus(array $linkIdBySku): void + { + $connection = $this->resourceConnection->getConnection(); + $linkId = $this->getProductEntityLinkField(); + $select = $connection->select() + ->from($this->getAttribute()->getBackend()->getTable()) + ->columns([$linkId, 'store_id', 'value']) + ->where(sprintf('%s IN (?)', $linkId), array_values($linkIdBySku)); + $skuByLinkId = array_flip($linkIdBySku); + + foreach ($connection->fetchAll($select) as $item) { + if (isset($skuByLinkId[$item[$linkId]])) { + $this->oldData[$skuByLinkId[$item[$linkId]]][$item['store_id']] = $item['value']; + } + } + } + + /** + * Set SKU status for given storeId + * + * @param string $sku + * @param string $storeId + * @param int $value + */ + public function setStatus(string $sku, string $storeId, int $value): void + { + $sku = strtolower($sku); + $this->newData[$sku][$storeId] = $value; + } + + /** + * Get product entity link field. + * + * @return string + */ + private function getProductEntityLinkField() + { + if (!$this->productEntityLinkField) { + $this->productEntityLinkField = $this->metadataPool->getMetadata(ProductInterface::class)->getLinkField(); + } + + return $this->productEntityLinkField; + } + + /** + * Get Attribute model + * + * @return AbstractAttribute + */ + private function getAttribute(): AbstractAttribute + { + if ($this->attribute === null) { + $this->attribute = $this->resourceFactory->create()->getAttribute(self::ATTRIBUTE_CODE); + } + return $this->attribute; + } +} diff --git a/app/code/Magento/CatalogImportExport/Model/Import/Product/StockProcessor.php b/app/code/Magento/CatalogImportExport/Model/Import/Product/StockProcessor.php new file mode 100644 index 0000000000000..76508d1ebec89 --- /dev/null +++ b/app/code/Magento/CatalogImportExport/Model/Import/Product/StockProcessor.php @@ -0,0 +1,57 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +declare(strict_types=1); + +namespace Magento\CatalogImportExport\Model\Import\Product; + +use Magento\Framework\Indexer\IndexerRegistry; + +/** + * Imported product stock manager + */ +class StockProcessor +{ + /** + * @var IndexerRegistry + */ + private $indexerRegistry; + /** + * @var array + */ + private $indexers; + + /** + * Initializes dependencies. + * + * @param IndexerRegistry $indexerRegistry + * @param array $indexers + */ + public function __construct( + IndexerRegistry $indexerRegistry, + array $indexers = [] + ) { + $this->indexerRegistry = $indexerRegistry; + $this->indexers = array_filter($indexers); + } + + /** + * Reindex products by ids + * + * @param array $ids + * @return void + */ + public function reindexList(array $ids = []): void + { + if ($ids) { + foreach ($this->indexers as $indexerName) { + $indexer = $this->indexerRegistry->get($indexerName); + if (!$indexer->isScheduled()) { + $indexer->reindexList($ids); + } + } + } + } +} diff --git a/app/code/Magento/CatalogImportExport/Model/Import/Product/Validator.php b/app/code/Magento/CatalogImportExport/Model/Import/Product/Validator.php index 4b7416f6ad9a6..b2eca68db4d1c 100644 --- a/app/code/Magento/CatalogImportExport/Model/Import/Product/Validator.php +++ b/app/code/Magento/CatalogImportExport/Model/Import/Product/Validator.php @@ -10,7 +10,7 @@ use Magento\Catalog\Model\Product\Attribute\Backend\Sku; /** - * Class Validator + * Product import model validator * * @api * @since 100.0.2 @@ -72,8 +72,12 @@ protected function textValidation($attrCode, $type) $val = $this->string->cleanString($this->_rowData[$attrCode]); if ($type == 'text') { $valid = $this->string->strlen($val) < Product::DB_MAX_TEXT_LENGTH; - } else if ($attrCode == Product::COL_SKU) { + } elseif ($attrCode == Product::COL_SKU) { $valid = $this->string->strlen($val) <= SKU::SKU_MAX_LENGTH; + if ($this->string->strlen($val) !== $this->string->strlen(trim($val))) { + $this->_addMessages([RowValidatorInterface::ERROR_SKU_MARGINAL_WHITESPACES]); + return false; + } } else { $valid = $this->string->strlen($val) < Product::DB_MAX_VARCHAR_LENGTH; } @@ -359,5 +363,7 @@ public function init($context) foreach ($this->validators as $validator) { $validator->init($context); } + + return $this; } } diff --git a/app/code/Magento/CatalogImportExport/Test/Mftf/ActionGroup/DeleteAllExportedFilesActionGroup.xml b/app/code/Magento/CatalogImportExport/Test/Mftf/ActionGroup/DeleteAllExportedFilesActionGroup.xml deleted file mode 100644 index aa8fad2a1d575..0000000000000 --- a/app/code/Magento/CatalogImportExport/Test/Mftf/ActionGroup/DeleteAllExportedFilesActionGroup.xml +++ /dev/null @@ -1,32 +0,0 @@ -<?xml version="1.0" encoding="UTF-8"?> -<!-- - /** - * Copyright © Magento, Inc. All rights reserved. - * See COPYING.txt for license details. - */ ---> - -<actionGroups xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" - xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/actionGroupSchema.xsd"> - <actionGroup name="DeleteAllExportedFilesActionGroup"> - <amOnPage url="{{AdminExportIndexPage.url}}" stepKey="goToExportIndexPage"/> - <executeInSelenium - function=" - function ($webdriver) use ($I) { - $buttons = $webdriver->findElements(\Facebook\WebDriver\WebDriverBy::xpath('//tr[@data-repeat-index=\'0\']//button')); - while(!empty($buttons)) { - $buttons[0]->click(); - $I->waitForElementVisible('//tr[@data-repeat-index=\'0\']//a[text()=\'Delete\']', 10); - $deleteButton = $webdriver->findElement(\Facebook\WebDriver\WebDriverBy::xpath('//tr[@data-repeat-index=\'0\']//a[text()=\'Delete\']')); - $deleteButton->click(); - $I->waitForElementVisible('.modal-popup.confirm button.action-accept', 10); - $I->click('.modal-popup.confirm button.action-accept'); - $I->waitForPageLoad(60); - $buttons = $webdriver->findElements(\Facebook\WebDriver\WebDriverBy::xpath('//tr[@data-repeat-index=\'0\']//button')); - } - }" - stepKey="deleteAllExportedFilesOneByOne"/> - <waitForElementVisible selector="{{AdminDataGridTableSection.dataGridEmpty}}" stepKey="waitDataGridEmptyMessageAppears"/> - <see selector="{{AdminDataGridTableSection.dataGridEmpty}}" userInput="We couldn't find any records." stepKey="assertDataGridEmptyMessage"/> - </actionGroup> -</actionGroups> diff --git a/app/code/Magento/CatalogImportExport/Test/Mftf/ActionGroup/DeleteExportedFileActionGroup.xml b/app/code/Magento/CatalogImportExport/Test/Mftf/ActionGroup/DeleteExportedFileActionGroup.xml index 78d7293b7437b..49173f79736c2 100644 --- a/app/code/Magento/CatalogImportExport/Test/Mftf/ActionGroup/DeleteExportedFileActionGroup.xml +++ b/app/code/Magento/CatalogImportExport/Test/Mftf/ActionGroup/DeleteExportedFileActionGroup.xml @@ -10,19 +10,19 @@ xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/actionGroupSchema.xsd"> <actionGroup name="DeleteExportedFileActionGroup"> <annotations> - <description>Deletes the provided Grid Index on the Exports grid page.</description> + <description>Deletes the exported file on the Exports grid page.</description> </annotations> <arguments> - <argument name="rowIndex" type="string"/> + <argument name="fileName" type="string"/> </arguments> <amOnPage url="{{AdminExportIndexPage.url}}" stepKey="goToExportIndexPage"/> <waitForPageLoad time="30" stepKey="waitFormReload"/> - <click stepKey="clickSelectBtn" selector="{{AdminExportAttributeSection.selectByIndex(rowIndex)}}"/> - <click stepKey="clickOnDelete" selector="{{AdminExportAttributeSection.delete(rowIndex)}}" after="clickSelectBtn"/> + <click stepKey="clickSelectBtn" selector="{{AdminExportAttributeSection.selectByFileName(fileName)}}"/> + <click stepKey="clickOnDelete" selector="{{AdminExportAttributeSection.deleteByFileName(fileName)}}" after="clickSelectBtn"/> <waitForElementVisible selector="{{AdminProductGridConfirmActionSection.title}}" stepKey="waitForConfirmModal"/> <click selector="{{AdminProductGridConfirmActionSection.ok}}" stepKey="confirmDelete"/> - <waitForElementVisible selector="{{AdminDataGridTableSection.dataGridEmpty}}" stepKey="waitDataGridEmptyMessageAppears"/> - <see selector="{{AdminDataGridTableSection.dataGridEmpty}}" userInput="We couldn't find any records." stepKey="assertDataGridEmptyMessage"/> + <waitForPageLoad time="30" stepKey="waitFormReload2"/> + <dontSeeElement selector="{{AdminExportAttributeSection.fileName(fileName)}}" stepKey="assertDontSeeFile"/> </actionGroup> </actionGroups> diff --git a/app/code/Magento/CatalogImportExport/Test/Mftf/ActionGroup/DownloadFileActionGroup.xml b/app/code/Magento/CatalogImportExport/Test/Mftf/ActionGroup/DownloadFileActionGroup.xml new file mode 100644 index 0000000000000..7866a2ca61658 --- /dev/null +++ b/app/code/Magento/CatalogImportExport/Test/Mftf/ActionGroup/DownloadFileActionGroup.xml @@ -0,0 +1,24 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + /** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +--> + +<actionGroups xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/actionGroupSchema.xsd"> + <actionGroup name="DownloadFileActionGroup"> + <annotations> + <description>Downloads the provided fileName on the Exports grid page.</description> + </annotations> + <arguments> + <argument name="fileName" type="string"/> + </arguments> + + <reloadPage stepKey="refreshPage"/> + <waitForPageLoad stepKey="waitFormReload"/> + <click stepKey="clickSelectBtn" selector="{{AdminExportAttributeSection.selectByFileName(fileName)}}"/> + <click stepKey="clickOnDownload" selector="{{AdminExportAttributeSection.downloadByFileName(fileName)}}" after="clickSelectBtn"/> + </actionGroup> +</actionGroups> diff --git a/app/code/Magento/CatalogImportExport/Test/Mftf/ActionGroup/DownloadFileByRowIndexActionGroup.xml b/app/code/Magento/CatalogImportExport/Test/Mftf/ActionGroup/DownloadFileByRowIndexActionGroup.xml deleted file mode 100644 index ec164ff172625..0000000000000 --- a/app/code/Magento/CatalogImportExport/Test/Mftf/ActionGroup/DownloadFileByRowIndexActionGroup.xml +++ /dev/null @@ -1,24 +0,0 @@ -<?xml version="1.0" encoding="UTF-8"?> -<!-- - /** - * Copyright © Magento, Inc. All rights reserved. - * See COPYING.txt for license details. - */ ---> - -<actionGroups xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" - xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/actionGroupSchema.xsd"> - <actionGroup name="DownloadFileByRowIndexActionGroup"> - <annotations> - <description>Downloads the provided Grid Index on the Exports grid page.</description> - </annotations> - <arguments> - <argument name="rowIndex" type="string"/> - </arguments> - - <reloadPage stepKey="refreshPage"/> - <waitForPageLoad stepKey="waitFormReload"/> - <click stepKey="clickSelectBtn" selector="{{AdminExportAttributeSection.selectByIndex(rowIndex)}}"/> - <click stepKey="clickOnDownload" selector="{{AdminExportAttributeSection.download(rowIndex)}}" after="clickSelectBtn"/> - </actionGroup> -</actionGroups> diff --git a/app/code/Magento/CatalogImportExport/Test/Mftf/Test/AdminExportBundleProductTest.xml b/app/code/Magento/CatalogImportExport/Test/Mftf/Test/AdminExportBundleProductTest.xml index 369805a94dd84..2b4609f1219df 100644 --- a/app/code/Magento/CatalogImportExport/Test/Mftf/Test/AdminExportBundleProductTest.xml +++ b/app/code/Magento/CatalogImportExport/Test/Mftf/Test/AdminExportBundleProductTest.xml @@ -85,8 +85,7 @@ <magentoCLI command="cron:run" stepKey="runCron2"/> <!-- Login as admin --> - <actionGroup ref="LoginAsAdmin" stepKey="loginAsAdmin"/> - <actionGroup ref="DeleteAllExportedFilesActionGroup" stepKey="clearExportedFilesList"/> + <actionGroup ref="AdminLoginActionGroup" stepKey="loginAsAdmin"/> </before> <after> <!-- Delete products creations --> @@ -101,12 +100,11 @@ <deleteData createDataKey="secondSimpleProductForFixedWithAttribute" stepKey="deleteSecondSimpleProductForFixedWithAttribute"/> <deleteData createDataKey="createProductAttribute" stepKey="deleteProductAttribute"/> - <!-- Delete exported file --> - <actionGroup ref="DeleteExportedFileActionGroup" stepKey="deleteExportedFile"> - <argument name="rowIndex" value="0"/> - </actionGroup> <!-- Log out --> - <actionGroup ref="logout" stepKey="logout"/> + <actionGroup ref="AdminLogoutActionGroup" stepKey="logout"/> + + <!-- Reindex invalidated indices after product attribute has been created/deleted --> + <magentoCron groups="index" stepKey="reindexInvalidatedIndices"/> </after> <!-- Go to export page --> @@ -119,9 +117,16 @@ <magentoCLI command="cron:run" stepKey="runCron3"/> <magentoCLI command="cron:run" stepKey="runCron4"/> + <grabTextFrom selector="{{AdminExportAttributeSection.exportFileNameByPosition('0')}}" stepKey="grabNameFile"/> + <!-- Download product --> - <actionGroup ref="DownloadFileByRowIndexActionGroup" stepKey="downloadCreatedProducts"> - <argument name="rowIndex" value="0"/> + <actionGroup ref="DownloadFileActionGroup" stepKey="downloadCreatedProducts"> + <argument name="fileName" value="{$grabNameFile}"/> + </actionGroup> + + <!-- Delete exported file --> + <actionGroup ref="DeleteExportedFileActionGroup" stepKey="deleteExportedFile"> + <argument name="fileName" value="{$grabNameFile}"/> </actionGroup> </test> </tests> diff --git a/app/code/Magento/CatalogImportExport/Test/Mftf/Test/AdminExportGroupedProductWithSpecialPriceTest.xml b/app/code/Magento/CatalogImportExport/Test/Mftf/Test/AdminExportGroupedProductWithSpecialPriceTest.xml index d9b93196db060..3449a39679666 100644 --- a/app/code/Magento/CatalogImportExport/Test/Mftf/Test/AdminExportGroupedProductWithSpecialPriceTest.xml +++ b/app/code/Magento/CatalogImportExport/Test/Mftf/Test/AdminExportGroupedProductWithSpecialPriceTest.xml @@ -53,8 +53,7 @@ <magentoCLI command="cron:run" stepKey="runCron2"/> <!-- Login as admin --> - <actionGroup ref="LoginAsAdmin" stepKey="loginAsAdmin"/> - <actionGroup ref="DeleteAllExportedFilesActionGroup" stepKey="clearExportedFilesList"/> + <actionGroup ref="AdminLoginActionGroup" stepKey="loginAsAdmin"/> </before> <after> <!-- Deleted created products --> @@ -65,12 +64,8 @@ <!-- Delete category --> <deleteData createDataKey="createCategory" stepKey="deleteCategory"/> - <!-- Delete exported file --> - <actionGroup ref="DeleteExportedFileActionGroup" stepKey="deleteExportedFile"> - <argument name="rowIndex" value="0"/> - </actionGroup> <!-- Log out --> - <actionGroup ref="logout" stepKey="logout"/> + <actionGroup ref="AdminLogoutActionGroup" stepKey="logout"/> </after> <!-- Go to export page --> @@ -84,9 +79,16 @@ <magentoCLI command="cron:run" stepKey="runCron3"/> <magentoCLI command="cron:run" stepKey="runCron4"/> + <grabTextFrom selector="{{AdminExportAttributeSection.exportFileNameByPosition('0')}}" stepKey="grabNameFile"/> + <!-- Download product --> - <actionGroup ref="DownloadFileByRowIndexActionGroup" stepKey="downloadCreatedProducts"> - <argument name="rowIndex" value="0"/> + <actionGroup ref="DownloadFileActionGroup" stepKey="downloadCreatedProducts"> + <argument name="fileName" value="{$grabNameFile}"/> + </actionGroup> + + <!-- Delete exported file --> + <actionGroup ref="DeleteExportedFileActionGroup" stepKey="deleteExportedFile"> + <argument name="fileName" value="{$grabNameFile}"/> </actionGroup> </test> </tests> diff --git a/app/code/Magento/CatalogImportExport/Test/Mftf/Test/AdminExportImportConfigurableProductWithImagesTest.xml b/app/code/Magento/CatalogImportExport/Test/Mftf/Test/AdminExportImportConfigurableProductWithImagesTest.xml index 642bbfc0453c5..64b1680d2e9a1 100644 --- a/app/code/Magento/CatalogImportExport/Test/Mftf/Test/AdminExportImportConfigurableProductWithImagesTest.xml +++ b/app/code/Magento/CatalogImportExport/Test/Mftf/Test/AdminExportImportConfigurableProductWithImagesTest.xml @@ -127,8 +127,7 @@ </createData> <!-- Login as admin --> - <actionGroup ref="LoginAsAdmin" stepKey="loginAsAdmin"/> - <actionGroup ref="DeleteAllExportedFilesActionGroup" stepKey="clearExportedFilesList"/> + <actionGroup ref="AdminLoginActionGroup" stepKey="loginAsAdmin"/> </before> <after> <!-- Remove downloadable domains --> @@ -140,19 +139,21 @@ <deleteData createDataKey="createDownloadableProduct" stepKey="deleteDownloadableProduct"/> <deleteData createDataKey="createGroupedProduct" stepKey="deleteGroupedProduct"/> <deleteData createDataKey="createExportImportConfigurableProduct" stepKey="deleteConfigProduct"/> + <deleteData createDataKey="createExportImportConfigurableProductAttribute" stepKey="deleteConfigProductAttribute"/> + <deleteData createDataKey="createExportImportCategory" stepKey="deleteExportImportCategory"/> <deleteData createDataKey="createConfigFirstChildProduct" stepKey="deleteConfigFirstChildProduct"/> <deleteData createDataKey="createConfigSecondChildProduct" stepKey="deleteConfigSecondChildProduct"/> - <deleteData createDataKey="createExportImportConfigurableProductAttribute" stepKey="deleteConfigProductAttribute"/> <deleteData createDataKey="createConfigurableProduct" stepKey="deleteConfigurableProduct"/> <deleteData createDataKey="createConfigChildProduct" stepKey="deleteConfigChildProduct"/> <deleteData createDataKey="createConfigProductAttr" stepKey="deleteConfigProductAttr"/> <deleteData createDataKey="createCategory" stepKey="deleteCategory"/> - <deleteData createDataKey="createExportImportCategory" stepKey="deleteExportImportCategory"/> - <amOnPage url="{{AdminProductIndexPage.url}}" stepKey="navigateToProductIndex"/> <actionGroup ref="ResetProductGridToDefaultViewActionGroup" stepKey="resetProductGridColumnsInitial"/> <!-- Admin logout--> - <actionGroup ref="logout" stepKey="adminLogout"/> + <actionGroup ref="AdminLogoutActionGroup" stepKey="adminLogout"/> + + <!-- Reindex invalidated indices after product attribute has been created/deleted --> + <magentoCron groups="index" stepKey="reindexInvalidatedIndices"/> </after> <!-- Go to System > Export --> @@ -168,9 +169,11 @@ <magentoCLI command="cron:run" stepKey="runCronFirstTime"/> <magentoCLI command="cron:run" stepKey="runCronSecondTime"/> + <grabTextFrom selector="{{AdminExportAttributeSection.exportFileNameByPosition('0')}}" stepKey="grabNameFile"/> + <!-- Save exported file: file successfully downloaded --> - <actionGroup ref="DownloadFileByRowIndexActionGroup" stepKey="downloadCreatedProducts"> - <argument name="rowIndex" value="0"/> + <actionGroup ref="DownloadFileActionGroup" stepKey="downloadCreatedProducts"> + <argument name="fileName" value="{$grabNameFile}"/> </actionGroup> <!-- Go to Catalog > Products. Find ConfProd and delete it --> @@ -218,5 +221,10 @@ <argument name="image" value="MagentoLogo"/> </actionGroup> <closeTab stepKey="closeConfigChildProductPage"/> + + <!-- Delete exported file --> + <actionGroup ref="DeleteExportedFileActionGroup" stepKey="deleteExportedFile"> + <argument name="fileName" value="{$grabNameFile}"/> + </actionGroup> </test> </tests> diff --git a/app/code/Magento/CatalogImportExport/Test/Mftf/Test/AdminExportSimpleAndConfigurableProductsWithCustomOptionsTest.xml b/app/code/Magento/CatalogImportExport/Test/Mftf/Test/AdminExportSimpleAndConfigurableProductsWithCustomOptionsTest.xml index 397c1ee57e7f5..ac730a6c6a7cc 100644 --- a/app/code/Magento/CatalogImportExport/Test/Mftf/Test/AdminExportSimpleAndConfigurableProductsWithCustomOptionsTest.xml +++ b/app/code/Magento/CatalogImportExport/Test/Mftf/Test/AdminExportSimpleAndConfigurableProductsWithCustomOptionsTest.xml @@ -78,8 +78,7 @@ <magentoCLI command="cron:run" stepKey="runCron2"/> <!-- Login as admin --> - <actionGroup ref="LoginAsAdmin" stepKey="loginAsAdmin"/> - <actionGroup ref="DeleteAllExportedFilesActionGroup" stepKey="clearExportedFilesList"/> + <actionGroup ref="AdminLoginActionGroup" stepKey="loginAsAdmin"/> </before> <after> <!-- Delete configurable product creation --> @@ -89,12 +88,11 @@ <deleteData createDataKey="createConfigProductAttribute" stepKey="deleteConfigProductAttribute"/> <deleteData createDataKey="createCategory" stepKey="deleteCategory"/> - <!-- Delete exported file --> - <actionGroup ref="DeleteExportedFileActionGroup" stepKey="deleteExportedFile"> - <argument name="rowIndex" value="0"/> - </actionGroup> <!-- Log out --> - <actionGroup ref="logout" stepKey="logout"/> + <actionGroup ref="AdminLogoutActionGroup" stepKey="logout"/> + + <!-- Reindex invalidated indices after product attribute has been created/deleted --> + <magentoCron groups="index" stepKey="reindexInvalidatedIndices"/> </after> <!-- Go to export page --> @@ -111,9 +109,16 @@ <magentoCLI command="cron:run" stepKey="runCron3"/> <magentoCLI command="cron:run" stepKey="runCron4"/> + <grabTextFrom selector="{{AdminExportAttributeSection.exportFileNameByPosition('0')}}" stepKey="grabNameFile"/> + <!-- Download product --> - <actionGroup ref="DownloadFileByRowIndexActionGroup" stepKey="downloadCreatedProducts"> - <argument name="rowIndex" value="0"/> + <actionGroup ref="DownloadFileActionGroup" stepKey="downloadCreatedProducts"> + <argument name="fileName" value="{$grabNameFile}"/> + </actionGroup> + + <!-- Delete exported file --> + <actionGroup ref="DeleteExportedFileActionGroup" stepKey="deleteExportedFile"> + <argument name="fileName" value="{$grabNameFile}"/> </actionGroup> </test> </tests> diff --git a/app/code/Magento/CatalogImportExport/Test/Mftf/Test/AdminExportSimpleProductAndConfigurableProductsWithAssignedImagesTest.xml b/app/code/Magento/CatalogImportExport/Test/Mftf/Test/AdminExportSimpleProductAndConfigurableProductsWithAssignedImagesTest.xml index e00346654ecf4..a0a3efdedadc8 100644 --- a/app/code/Magento/CatalogImportExport/Test/Mftf/Test/AdminExportSimpleProductAndConfigurableProductsWithAssignedImagesTest.xml +++ b/app/code/Magento/CatalogImportExport/Test/Mftf/Test/AdminExportSimpleProductAndConfigurableProductsWithAssignedImagesTest.xml @@ -94,8 +94,7 @@ <magentoCLI command="cron:run" stepKey="runCron2"/> <!-- Login as admin --> - <actionGroup ref="LoginAsAdmin" stepKey="loginAsAdmin"/> - <actionGroup ref="DeleteAllExportedFilesActionGroup" stepKey="clearExportedFilesList"/> + <actionGroup ref="AdminLoginActionGroup" stepKey="loginAsAdmin"/> </before> <after> <!-- Delete configurable product creation --> @@ -105,12 +104,11 @@ <deleteData createDataKey="createConfigProductAttribute" stepKey="deleteConfigProductAttribute"/> <deleteData createDataKey="createCategory" stepKey="deleteCategory"/> - <!-- Delete exported file --> - <actionGroup ref="DeleteExportedFileActionGroup" stepKey="deleteExportedFile"> - <argument name="rowIndex" value="0"/> - </actionGroup> <!-- Log out --> - <actionGroup ref="logout" stepKey="logout"/> + <actionGroup ref="AdminLogoutActionGroup" stepKey="logout"/> + + <!-- Reindex invalidated indices after product attribute has been created/deleted --> + <magentoCron groups="index" stepKey="reindexInvalidatedIndices"/> </after> <!-- Go to export page --> @@ -126,9 +124,16 @@ <magentoCLI command="cron:run" stepKey="runCron3"/> <magentoCLI command="cron:run" stepKey="runCron4"/> + <grabTextFrom selector="{{AdminExportAttributeSection.exportFileNameByPosition('0')}}" stepKey="grabNameFile"/> + <!-- Download product --> - <actionGroup ref="DownloadFileByRowIndexActionGroup" stepKey="downloadCreatedProducts"> - <argument name="rowIndex" value="0"/> + <actionGroup ref="DownloadFileActionGroup" stepKey="downloadCreatedProducts"> + <argument name="fileName" value="{$grabNameFile}"/> + </actionGroup> + + <!-- Delete exported file --> + <actionGroup ref="DeleteExportedFileActionGroup" stepKey="deleteExportedFile"> + <argument name="fileName" value="{$grabNameFile}"/> </actionGroup> </test> </tests> diff --git a/app/code/Magento/CatalogImportExport/Test/Mftf/Test/AdminExportSimpleProductAssignedToMainWebsiteAndConfigurableProductAssignedToCustomWebsiteTest.xml b/app/code/Magento/CatalogImportExport/Test/Mftf/Test/AdminExportSimpleProductAssignedToMainWebsiteAndConfigurableProductAssignedToCustomWebsiteTest.xml index 04be8f3ae823e..f9669dfbdac27 100644 --- a/app/code/Magento/CatalogImportExport/Test/Mftf/Test/AdminExportSimpleProductAssignedToMainWebsiteAndConfigurableProductAssignedToCustomWebsiteTest.xml +++ b/app/code/Magento/CatalogImportExport/Test/Mftf/Test/AdminExportSimpleProductAssignedToMainWebsiteAndConfigurableProductAssignedToCustomWebsiteTest.xml @@ -76,10 +76,10 @@ <magentoCLI command="cron:run" stepKey="runCron2"/> <!-- Login as admin --> - <actionGroup ref="LoginAsAdmin" stepKey="loginAsAdmin"/> - <actionGroup ref="DeleteAllExportedFilesActionGroup" stepKey="clearExportedFilesList"/> + <actionGroup ref="AdminLoginActionGroup" stepKey="loginAsAdmin"/> </before> <after> + <!-- Delete simple product --> <deleteData createDataKey="createSimpleProduct" stepKey="deleteSimpleProduct"/> @@ -90,12 +90,11 @@ <deleteData createDataKey="createConfigProductAttribute" stepKey="deleteConfigProductAttribute"/> <deleteData createDataKey="createCategory" stepKey="deleteCategory"/> - <!-- Delete exported file --> - <actionGroup ref="DeleteExportedFileActionGroup" stepKey="deleteExportedFile"> - <argument name="rowIndex" value="0"/> - </actionGroup> <!-- Log out --> - <actionGroup ref="logout" stepKey="logout"/> + <actionGroup ref="AdminLogoutActionGroup" stepKey="logout"/> + + <!-- Reindex invalidated indices after product attribute has been created/deleted --> + <magentoCron groups="index" stepKey="reindexInvalidatedIndices"/> </after> <!-- Go to export page --> @@ -109,9 +108,16 @@ <magentoCLI command="cron:run" stepKey="runCron3"/> <magentoCLI command="cron:run" stepKey="runCron4"/> + <grabTextFrom selector="{{AdminExportAttributeSection.exportFileNameByPosition('0')}}" stepKey="grabNameFile"/> + <!-- Download product --> - <actionGroup ref="DownloadFileByRowIndexActionGroup" stepKey="downloadCreatedProducts"> - <argument name="rowIndex" value="0"/> + <actionGroup ref="DownloadFileActionGroup" stepKey="downloadCreatedProducts"> + <argument name="fileName" value="{$grabNameFile}"/> + </actionGroup> + + <!-- Delete exported file --> + <actionGroup ref="DeleteExportedFileActionGroup" stepKey="deleteExportedFile"> + <argument name="fileName" value="{$grabNameFile}"/> </actionGroup> </test> </tests> diff --git a/app/code/Magento/CatalogImportExport/Test/Mftf/Test/AdminExportSimpleProductWithCustomAttributeTest.xml b/app/code/Magento/CatalogImportExport/Test/Mftf/Test/AdminExportSimpleProductWithCustomAttributeTest.xml index 8553fb8a2cf7e..2e8e62667b314 100644 --- a/app/code/Magento/CatalogImportExport/Test/Mftf/Test/AdminExportSimpleProductWithCustomAttributeTest.xml +++ b/app/code/Magento/CatalogImportExport/Test/Mftf/Test/AdminExportSimpleProductWithCustomAttributeTest.xml @@ -33,8 +33,7 @@ <magentoCLI command="cron:run" stepKey="runCron2"/> <!-- Login as admin --> - <actionGroup ref="LoginAsAdmin" stepKey="loginAsAdmin"/> - <actionGroup ref="DeleteAllExportedFilesActionGroup" stepKey="clearExportedFilesList"/> + <actionGroup ref="AdminLoginActionGroup" stepKey="loginAsAdmin"/> </before> <after> <!-- Delete product creations --> @@ -42,12 +41,8 @@ <deleteData createDataKey="createAttributeSet" stepKey="deleteAttributeSet"/> <deleteData createDataKey="createCategory" stepKey="deleteCategory"/> - <!-- Delete exported file --> - <actionGroup ref="DeleteExportedFileActionGroup" stepKey="deleteExportedFile"> - <argument name="rowIndex" value="0"/> - </actionGroup> <!-- Log out --> - <actionGroup ref="logout" stepKey="logout"/> + <actionGroup ref="AdminLogoutActionGroup" stepKey="logout"/> </after> <!-- Go to export page --> @@ -61,9 +56,17 @@ <magentoCLI command="cron:run" stepKey="runCron3"/> <magentoCLI command="cron:run" stepKey="runCron4"/> + <grabTextFrom selector="{{AdminExportAttributeSection.exportFileNameByPosition('0')}}" stepKey="grabNameFile"/> + <!-- Download product --> - <actionGroup ref="DownloadFileByRowIndexActionGroup" stepKey="downloadCreatedProducts"> - <argument name="rowIndex" value="0"/> + <actionGroup ref="DownloadFileActionGroup" stepKey="downloadCreatedProducts"> + <argument name="fileName" value="{$grabNameFile}"/> + </actionGroup> + + <!-- Delete exported file --> + <actionGroup ref="DeleteExportedFileActionGroup" stepKey="deleteExportedFile"> + <argument name="fileName" value="{$grabNameFile}"/> </actionGroup> + </test> </tests> diff --git a/app/code/Magento/CatalogImportExport/Test/Unit/Model/Import/Product/LinkProcessorTest.php b/app/code/Magento/CatalogImportExport/Test/Unit/Model/Import/Product/LinkProcessorTest.php new file mode 100644 index 0000000000000..284ef0b783ec8 --- /dev/null +++ b/app/code/Magento/CatalogImportExport/Test/Unit/Model/Import/Product/LinkProcessorTest.php @@ -0,0 +1,132 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ + +namespace Magento\CatalogImportExport\Test\Unit\Model\Import\Product; + +use Magento\Framework\TestFramework\Unit\Helper\ObjectManager as ObjectManagerHelper; + +/** + * @SuppressWarnings(PHPMD.CouplingBetweenObjects) + */ +class LinkProcessorTest extends \PHPUnit\Framework\TestCase +{ + /** + * @var \Magento\Framework\TestFramework\Unit\Helper\ObjectManager + */ + protected $objectManager; + + /** @var ObjectManagerHelper */ + protected $objectManagerHelper; + + /** + * @var \Magento\CatalogImportExport\Model\Import\Product\LinkProcessor|\PHPUnit_Framework_MockObject_MockObject + */ + protected $linkProcessor; + + /** + * @var \Magento\CatalogImportExport\Model\Import\Product\Type\AbstractType + */ + protected $product; + + /** + * @var \Magento\ImportExport\Model\ResourceModel\Helper + */ + protected $resourceHelper; + + /** + * @var \Magento\Catalog\Model\ResourceModel\Product\Link + */ + protected $resource; + + /** + * @var \Magento\Catalog\Model\ResourceModel\Product\LinkFactory + */ + protected $linkFactory; + + /** + * @var \Magento\CatalogImportExport\Model\Import\Product\SkuProcessor::class + */ + protected $skuProcessor; + + /** + * @var \Psr\Log\LoggerInterface + */ + protected $logger; + + protected function setUp() + { + $this->objectManager = new \Magento\Framework\TestFramework\Unit\Helper\ObjectManager($this); + $this->objectManagerHelper = new ObjectManagerHelper($this); + + $this->resourceHelper = $this->createMock(\Magento\ImportExport\Model\ResourceModel\Helper::class); + + $this->resource = $this->createMock(\Magento\Catalog\Model\ResourceModel\Product\Link::class); + $this->resource->method('getMainTable')->willReturn('main_link_table'); + + $this->linkFactory = $this->createPartialMock( + \Magento\Catalog\Model\ResourceModel\Product\LinkFactory::class, + ['create'] + ); + $this->linkFactory->method('create')->willReturn($this->resource); + + $this->skuProcessor = $this->createMock( + \Magento\CatalogImportExport\Model\Import\Product\SkuProcessor::class + ); + $this->logger = $this->createMock(\Psr\Log\LoggerInterface::class); + } + + /** + * @dataProvider diConfigDataProvider + * @param $expectedCallCount + * @param $linkToNameId + * @throws \Magento\Framework\Exception\LocalizedException + */ + public function testSaveLinks($expectedCallCount, $linkToNameId) + { + $this->linkProcessor = + new \Magento\CatalogImportExport\Model\Import\Product\LinkProcessor( + $this->linkFactory, + $this->resourceHelper, + $this->skuProcessor, + $this->logger, + $linkToNameId + ); + + $importEntity = $this->createMock(\Magento\CatalogImportExport\Model\Import\Product::class); + $connection = $this->createMock(\Magento\Framework\DB\Adapter\AdapterInterface::class); + $importEntity->method('getConnection')->willReturn($connection); + $select = $this->createMock(\Magento\Framework\DB\Select::class); + + // expect one call per linkToNameId + $connection->expects($this->exactly($expectedCallCount))->method('select')->willReturn($select); + + $select->method('from')->willReturn($select); + + $dataSourceModel = $this->createMock(\Magento\ImportExport\Model\ResourceModel\Import\Data::class); + + $this->linkProcessor->saveLinks($importEntity, $dataSourceModel, '_related_'); + } + + /** + * @return array + */ + public function diConfigDataProvider() + { + return [ + [3, [ + '_related_' => \Magento\Catalog\Model\Product\Link::LINK_TYPE_RELATED, + '_crosssell_' => \Magento\Catalog\Model\Product\Link::LINK_TYPE_CROSSSELL, + '_upsell_' => \Magento\Catalog\Model\Product\Link::LINK_TYPE_UPSELL, + ]], + [4, [ + '_related_' => \Magento\Catalog\Model\Product\Link::LINK_TYPE_RELATED, + '_crosssell_' => \Magento\Catalog\Model\Product\Link::LINK_TYPE_CROSSSELL, + '_upsell_' => \Magento\Catalog\Model\Product\Link::LINK_TYPE_UPSELL, + '_custom_link_' => 9, + ]], + ]; + } +} diff --git a/app/code/Magento/CatalogImportExport/Test/Unit/Model/Import/ProductTest.php b/app/code/Magento/CatalogImportExport/Test/Unit/Model/Import/ProductTest.php index 40041fe90db96..ff724ddc746aa 100644 --- a/app/code/Magento/CatalogImportExport/Test/Unit/Model/Import/ProductTest.php +++ b/app/code/Magento/CatalogImportExport/Test/Unit/Model/Import/ProductTest.php @@ -13,8 +13,8 @@ use PHPUnit\Framework\MockObject\MockObject; /** - * Class ProductTest - * @package Magento\CatalogImportExport\Test\Unit\Model\Import + * Test import entity product model + * * @SuppressWarnings(PHPMD.TooManyFields) * @SuppressWarnings(PHPMD.ExcessiveClassComplexity) * @SuppressWarnings(PHPMD.CouplingBetweenObjects) @@ -547,6 +547,17 @@ public function testSaveProductAttributes() $this->_connection->expects($this->once()) ->method('insertOnDuplicate') ->with($testTable, $tableData, ['value']); + $attribute = $this->getMockBuilder(\Magento\Eav\Model\Entity\Attribute\AbstractAttribute::class) + ->disableOriginalConstructor() + ->setMethods(['getId']) + ->getMockForAbstractClass(); + $attribute->expects($this->once())->method('getId')->willReturn(1); + $resource = $this->getMockBuilder(\Magento\CatalogImportExport\Model\Import\Proxy\Product\ResourceModel::class) + ->disableOriginalConstructor() + ->setMethods(['getAttribute']) + ->getMock(); + $resource->expects($this->once())->method('getAttribute')->willReturn($attribute); + $this->_resourceFactory->expects($this->once())->method('create')->willReturn($resource); $this->setPropertyValue($this->importProduct, '_oldSku', [$testSku => ['entity_id' => self::ENTITY_ID]]); $object = $this->invokeMethod($this->importProduct, '_saveProductAttributes', [$attributesData]); $this->assertEquals($this->importProduct, $object); diff --git a/app/code/Magento/CatalogImportExport/etc/di.xml b/app/code/Magento/CatalogImportExport/etc/di.xml index 4e2fe390e0b17..040e9dcda132a 100644 --- a/app/code/Magento/CatalogImportExport/etc/di.xml +++ b/app/code/Magento/CatalogImportExport/etc/di.xml @@ -8,6 +8,7 @@ <config xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="urn:magento:framework:ObjectManager/etc/config.xsd"> <preference for="Magento\CatalogImportExport\Model\Export\RowCustomizerInterface" type="Magento\CatalogImportExport\Model\Export\RowCustomizer\Composite" /> <preference for="Magento\CatalogImportExport\Model\StockItemImporterInterface" type="Magento\CatalogImportExport\Model\StockItemImporter" /> + <preference for="Magento\CatalogImportExport\Model\Export\ProductFilterInterface" type="Magento\CatalogImportExport\Model\Export\ProductFilters" /> <type name="Magento\ImportExport\Model\Import"> <plugin name="catalogProductFlatIndexerImport" type="Magento\CatalogImportExport\Model\Indexer\Product\Flat\Plugin\Import" /> <plugin name="invalidatePriceIndexerOnImport" type="Magento\CatalogImportExport\Model\Indexer\Product\Price\Plugin\Import" /> @@ -16,6 +17,13 @@ <plugin name="invalidateProductCategoryIndexerOnImport" type="Magento\CatalogImportExport\Model\Indexer\Product\Category\Plugin\Import" /> <plugin name="invalidateCategoryProductIndexerOnImport" type="Magento\CatalogImportExport\Model\Indexer\Category\Product\Plugin\Import" /> </type> + <type name="Magento\CatalogImportExport\Model\Import\Product\StockProcessor"> + <arguments> + <argument name="indexers" xsi:type="array"> + <item name="cataloginventory_stock" xsi:type="const">Magento\CatalogInventory\Model\Indexer\Stock\Processor::INDEXER_ID</item> + </argument> + </arguments> + </type> <type name="Magento\CatalogImportExport\Model\Import\Product\Validator"> <arguments> <argument name="validators" xsi:type="array"> @@ -35,4 +43,21 @@ <argument name="validationState" xsi:type="object">Magento\Framework\Config\ValidationState\Required</argument> </arguments> </type> + <type name="Magento\CatalogImportExport\Model\Export\ProductFilters"> + <arguments> + <argument name="filters" xsi:type="array"> + <item name="category_ids" xsi:type="object">Magento\CatalogImportExport\Model\Export\Product\CategoryFilter</item> + <item name="quantity_and_stock_status" xsi:type="object">Magento\CatalogImportExport\Model\Export\Product\StockStatusFilter</item> + </argument> + </arguments> + </type> + <type name="Magento\CatalogImportExport\Model\Import\Product\LinkProcessor"> + <arguments> + <argument name="linkNameToId" xsi:type="array"> + <item name="_related_" xsi:type="const">Magento\Catalog\Model\Product\Link::LINK_TYPE_RELATED</item> + <item name="_crosssell_" xsi:type="const">Magento\Catalog\Model\Product\Link::LINK_TYPE_CROSSSELL</item> + <item name="_upsell_" xsi:type="const">Magento\Catalog\Model\Product\Link::LINK_TYPE_UPSELL</item> + </argument> + </arguments> + </type> </config> diff --git a/app/code/Magento/CatalogImportExport/registration.php b/app/code/Magento/CatalogImportExport/registration.php index 144a7cdaeeea0..bd6683f4af4d1 100644 --- a/app/code/Magento/CatalogImportExport/registration.php +++ b/app/code/Magento/CatalogImportExport/registration.php @@ -4,6 +4,6 @@ * See COPYING.txt for license details. */ -use \Magento\Framework\Component\ComponentRegistrar; +use Magento\Framework\Component\ComponentRegistrar; ComponentRegistrar::register(ComponentRegistrar::MODULE, 'Magento_CatalogImportExport', __DIR__); diff --git a/app/code/Magento/CatalogInventory/Model/Quote/Item/QuantityValidator.php b/app/code/Magento/CatalogInventory/Model/Quote/Item/QuantityValidator.php index 502d9532e8a05..e67568b80898e 100644 --- a/app/code/Magento/CatalogInventory/Model/Quote/Item/QuantityValidator.php +++ b/app/code/Magento/CatalogInventory/Model/Quote/Item/QuantityValidator.php @@ -222,9 +222,11 @@ private function checkOptionsQtyIncrements(Item $quoteItem, array $options): voi { $removeErrors = true; foreach ($options as $option) { + $optionValue = $option->getValue(); + $optionQty = $quoteItem->getData('qty') * $optionValue; $result = $this->stockState->checkQtyIncrements( $option->getProduct()->getId(), - $quoteItem->getData('qty'), + $optionQty, $option->getProduct()->getStore()->getWebsiteId() ); if ($result->getHasError()) { diff --git a/app/code/Magento/CatalogInventory/Model/Quote/Item/QuantityValidator/Initializer/StockItem.php b/app/code/Magento/CatalogInventory/Model/Quote/Item/QuantityValidator/Initializer/StockItem.php index 7a46780f2d783..f104552b4e0fc 100644 --- a/app/code/Magento/CatalogInventory/Model/Quote/Item/QuantityValidator/Initializer/StockItem.php +++ b/app/code/Magento/CatalogInventory/Model/Quote/Item/QuantityValidator/Initializer/StockItem.php @@ -118,6 +118,10 @@ public function initialize( $product->getStore()->getWebsiteId() ); + if ($result->getHasError() === true && in_array($result->getErrorCode(), ['qty_available', 'out_stock'])) { + $quoteItem->setHasError(true); + } + /* We need to ensure that any possible plugin will not erase the data */ $backOrdersQty = $this->stockStateProvider->checkQuoteItemQty($stockItem, $rowQty, $qtyForCheck, $qty) ->getItemBackorders(); diff --git a/app/code/Magento/CatalogInventory/Model/Quote/Item/QuantityValidator/QuoteItemQtyList.php b/app/code/Magento/CatalogInventory/Model/Quote/Item/QuantityValidator/QuoteItemQtyList.php index c0b60c9935d71..600bf9897a036 100644 --- a/app/code/Magento/CatalogInventory/Model/Quote/Item/QuantityValidator/QuoteItemQtyList.php +++ b/app/code/Magento/CatalogInventory/Model/Quote/Item/QuantityValidator/QuoteItemQtyList.php @@ -5,6 +5,9 @@ */ namespace Magento\CatalogInventory\Model\Quote\Item\QuantityValidator; +/** + * Class QuoteItemQtyList collects qty of quote items + */ class QuoteItemQtyList { /** @@ -17,6 +20,7 @@ class QuoteItemQtyList /** * Get product qty includes information from all quote items + * * Need be used only in singleton mode * * @param int $productId @@ -29,9 +33,7 @@ class QuoteItemQtyList public function getQty($productId, $quoteItemId, $quoteId, $itemQty) { $qty = $itemQty; - if (isset( - $this->_checkedQuoteItems[$quoteId][$productId]['qty'] - ) && !in_array( + if (isset($this->_checkedQuoteItems[$quoteId][$productId]['qty']) && !in_array( $quoteItemId, $this->_checkedQuoteItems[$quoteId][$productId]['items'] ) @@ -39,8 +41,10 @@ public function getQty($productId, $quoteItemId, $quoteId, $itemQty) $qty += $this->_checkedQuoteItems[$quoteId][$productId]['qty']; } - $this->_checkedQuoteItems[$quoteId][$productId]['qty'] = $qty; - $this->_checkedQuoteItems[$quoteId][$productId]['items'][] = $quoteItemId; + if ($quoteItemId !== null) { + $this->_checkedQuoteItems[$quoteId][$productId]['qty'] = $qty; + $this->_checkedQuoteItems[$quoteId][$productId]['items'][] = $quoteItemId; + } return $qty; } diff --git a/app/code/Magento/CatalogInventory/Model/Source/Stock.php b/app/code/Magento/CatalogInventory/Model/Source/Stock.php index 7d44ab782de61..9661fc83ce275 100644 --- a/app/code/Magento/CatalogInventory/Model/Source/Stock.php +++ b/app/code/Magento/CatalogInventory/Model/Source/Stock.php @@ -42,7 +42,7 @@ public function getAllOptions() public function addValueSortToCollection($collection, $dir = \Magento\Framework\Data\Collection::SORT_ORDER_DESC) { $collection->getSelect()->joinLeft( - ['stock_item_table' => 'cataloginventory_stock_item'], + ['stock_item_table' => $collection->getTable('cataloginventory_stock_item')], "e.entity_id=stock_item_table.product_id", [] ); diff --git a/app/code/Magento/CatalogInventory/Model/Stock/StockItemChecker.php b/app/code/Magento/CatalogInventory/Model/Stock/StockItemChecker.php new file mode 100644 index 0000000000000..e3a9c48b27155 --- /dev/null +++ b/app/code/Magento/CatalogInventory/Model/Stock/StockItemChecker.php @@ -0,0 +1,70 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +declare(strict_types=1); + +namespace Magento\CatalogInventory\Model\Stock; + +use Magento\CatalogInventory\Api\StockItemRepositoryInterface; +use Magento\Framework\Stdlib\ArrayUtils; +use Magento\CatalogInventory\Model\Stock\Item as StockItem; + +/** + * Verifies Stock item model changes. + */ +class StockItemChecker +{ + /** + * @var StockItemRepositoryInterface + */ + private $stockItemRepository; + + /** + * @var ArrayUtils + */ + private $arrayUtils; + + /** + * @var string[] + */ + private $skippedAttributes; + + /** + * @param StockItemRepositoryInterface $stockItemRepository + * @param ArrayUtils $arrayUtils + * @param string[] $skippedAttributes + */ + public function __construct( + StockItemRepositoryInterface $stockItemRepository, + ArrayUtils $arrayUtils, + array $skippedAttributes = [] + ) { + $this->stockItemRepository = $stockItemRepository; + $this->arrayUtils = $arrayUtils; + $this->skippedAttributes = $skippedAttributes; + } + + /** + * Check if stock item is modified. + * + * @param StockItem $model + * @return bool + */ + public function isModified($model): bool + { + if (!$model->getId()) { + return true; + } + $stockItem = $this->stockItemRepository->get($model->getId()); + $stockItemData = $stockItem->getData(); + $modelData = $model->getData(); + foreach ($this->skippedAttributes as $attribute) { + unset($stockItemData[$attribute], $modelData[$attribute]); + } + $diff = $this->arrayUtils->recursiveDiff($stockItemData, $modelData); + + return !empty($diff); + } +} diff --git a/app/code/Magento/CatalogInventory/Model/StockIndex.php b/app/code/Magento/CatalogInventory/Model/StockIndex.php index 41db057cf149c..ad0cff43c6ac9 100644 --- a/app/code/Magento/CatalogInventory/Model/StockIndex.php +++ b/app/code/Magento/CatalogInventory/Model/StockIndex.php @@ -6,16 +6,20 @@ namespace Magento\CatalogInventory\Model; +use Magento\Catalog\Model\Product; use Magento\Catalog\Model\Product\Type as ProductType; use Magento\Catalog\Model\Product\Website as ProductWebsite; -use Magento\Catalog\Model\ProductFactory; use Magento\CatalogInventory\Api\StockIndexInterface; +use Magento\CatalogInventory\Model\ResourceModel\Stock\Status as StockStatusResourceModel; use Magento\CatalogInventory\Model\Spi\StockRegistryProviderInterface; use Magento\Catalog\Api\ProductRepositoryInterface; use Magento\Catalog\Model\Product\Attribute\Source\Status; +use Magento\Framework\App\ObjectManager; +use Magento\Store\Model\Website; /** - * Class StockIndex + * Index responsible for Stock + * * @SuppressWarnings(PHPMD.CouplingBetweenObjects) */ class StockIndex implements StockIndexInterface @@ -31,7 +35,7 @@ class StockIndex implements StockIndexInterface protected $productRepository; /** - * @var \Magento\CatalogInventory\Model\ResourceModel\Stock\Status + * @var StockStatusResourceModel */ protected $stockStatusResource; @@ -47,6 +51,11 @@ class StockIndex implements StockIndexInterface */ protected $websites; + /** + * @var ProductWebsite + */ + private $productWebsite; + /** * Product Type Instances cache * @@ -88,7 +97,7 @@ public function rebuild($productId = null, $scopeId = null) } else { $lastProductId = 0; while (true) { - /** @var \Magento\CatalogInventory\Model\ResourceModel\Stock\Status $resource */ + /** @var StockStatusResourceModel $resource */ $resource = $this->getStockStatusResource(); $productCollection = $resource->getProductCollection($lastProductId); if (!$productCollection) { @@ -115,7 +124,7 @@ public function updateProductStockStatus($productId, $websiteId) { $item = $this->stockRegistryProvider->getStockItem($productId, $websiteId); - $status = \Magento\CatalogInventory\Model\Stock\Status::STATUS_IN_STOCK; + $status = Stock\Status::STATUS_IN_STOCK; $qty = 0; if ($item->getItemId()) { $status = $item->getIsInStock(); @@ -132,16 +141,16 @@ public function updateProductStockStatus($productId, $websiteId) * @param int $websiteId * @param int $qty * @param int $status - * @return $this + * @return void * @SuppressWarnings(PHPMD.CyclomaticComplexity) */ protected function processChildren( $productId, $websiteId, $qty = 0, - $status = \Magento\CatalogInventory\Model\Stock\Status::STATUS_IN_STOCK + $status = Stock\Status::STATUS_IN_STOCK ) { - if ($status == \Magento\CatalogInventory\Model\Stock\Status::STATUS_OUT_OF_STOCK) { + if ($status == Stock\Status::STATUS_OUT_OF_STOCK) { $this->getStockStatusResource()->saveProductStatus($productId, $status, $qty, $websiteId); return; } @@ -150,20 +159,22 @@ protected function processChildren( $websitesWithStores = $this->getWebsitesWithDefaultStores($websiteId); foreach (array_keys($websitesWithStores) as $websiteId) { - /* @var $website \Magento\Store\Model\Website */ + /* @var $website Website */ $statuses[$websiteId] = $status; } - /** @var \Magento\Catalog\Model\Product $product */ + /** @var Product $product */ $product = $this->productRepository->getById($productId); $typeInstance = $product->getTypeInstance(); $requiredChildrenIds = $typeInstance->getChildrenIds($productId, true); if ($requiredChildrenIds) { - $childrenIds = []; + $childrenIds = [[]]; foreach ($requiredChildrenIds as $groupedChildrenIds) { - $childrenIds = array_merge($childrenIds, $groupedChildrenIds); + $childrenIds[] = $groupedChildrenIds; } + $childrenIds = array_merge(...$childrenIds); + $childrenWebsites = $this->productWebsite->getWebsites($childrenIds); foreach ($websitesWithStores as $websiteId => $storeId) { $childrenStatus = $this->getStockStatusResource()->getProductStatus($childrenIds, $storeId); @@ -177,7 +188,7 @@ protected function processChildren( && in_array($websiteId, $childrenWebsites[$childId]) && $childrenStatus[$childId] == Status::STATUS_ENABLED && isset($childrenStock[$childId]) - && $childrenStock[$childId] == \Magento\CatalogInventory\Model\Stock\Status::STATUS_IN_STOCK + && $childrenStock[$childId] == Stock\Status::STATUS_IN_STOCK ) { $optionStatus = true; } @@ -196,12 +207,12 @@ protected function processChildren( * Retrieve website models * * @param int|null $websiteId - * @return array + * @return Website[] */ protected function getWebsitesWithDefaultStores($websiteId = null) { if ($this->websites === null) { - /** @var \Magento\CatalogInventory\Model\ResourceModel\Stock\Status $resource */ + /** @var StockStatusResourceModel $resource */ $resource = $this->getStockStatusResource(); $this->websites = $resource->getWebsiteStores(); } @@ -217,23 +228,25 @@ protected function getWebsitesWithDefaultStores($websiteId = null) * * @param int $productId * @param int $websiteId - * @return $this + * @return void */ protected function processParents($productId, $websiteId) { - $parentIds = []; + $parentIds = [[]]; foreach ($this->getProductTypeInstances() as $typeInstance) { - /* @var $typeInstance AbstractType */ - $parentIds = array_merge($parentIds, $typeInstance->getParentIdsByChild($productId)); + /* @var ProductType\AbstractType $typeInstance */ + $parentIds[] = $typeInstance->getParentIdsByChild($productId); } - if (!$parentIds) { - return $this; + $parentIds = array_merge(...$parentIds); + + if (empty($parentIds)) { + return; } foreach ($parentIds as $parentId) { $item = $this->stockRegistryProvider->getStockItem($parentId, $websiteId); - $status = \Magento\CatalogInventory\Model\Stock\Status::STATUS_IN_STOCK; + $status = Stock\Status::STATUS_IN_STOCK; $qty = 0; if ($item->getItemId()) { $status = $item->getIsInStock(); @@ -244,10 +257,9 @@ protected function processParents($productId, $websiteId) } /** - * Retrieve Product Type Instances - * as key - type code, value - instance model + * Retrieve Product Type Instances as key - type code, value - instance model * - * @return array + * @return ProductType\AbstractType[] */ protected function getProductTypeInstances() { @@ -262,14 +274,14 @@ protected function getProductTypeInstances() } /** - * @return \Magento\CatalogInventory\Model\ResourceModel\Stock\Status + * Returns ResourceModel for Stock Status + * + * @return StockStatusResourceModel */ protected function getStockStatusResource() { if (empty($this->stockStatusResource)) { - $this->stockStatusResource = \Magento\Framework\App\ObjectManager::getInstance()->get( - \Magento\CatalogInventory\Model\ResourceModel\Stock\Status::class - ); + $this->stockStatusResource = ObjectManager::getInstance()->get(StockStatusResourceModel::class); } return $this->stockStatusResource; } diff --git a/app/code/Magento/CatalogInventory/Model/StockStateProvider.php b/app/code/Magento/CatalogInventory/Model/StockStateProvider.php index 6851b05aa56a6..b57518b681aa2 100644 --- a/app/code/Magento/CatalogInventory/Model/StockStateProvider.php +++ b/app/code/Magento/CatalogInventory/Model/StockStateProvider.php @@ -14,7 +14,7 @@ use Magento\Framework\Math\Division as MathDivision; /** - * Interface StockStateProvider + * Provider stocks state */ class StockStateProvider implements StockStateProviderInterface { @@ -156,6 +156,7 @@ public function checkQuoteItemQty(StockItemInterface $stockItem, $qty, $summaryQ if (!$stockItem->getIsInStock()) { $result->setHasError(true) + ->setErrorCode('out_stock') ->setMessage(__('This product is out of stock.')) ->setQuoteMessage(__('Some of the products are out of stock.')) ->setQuoteMessageIndex('stock'); @@ -165,7 +166,11 @@ public function checkQuoteItemQty(StockItemInterface $stockItem, $qty, $summaryQ if (!$this->checkQty($stockItem, $summaryQty) || !$this->checkQty($stockItem, $qty)) { $message = __('The requested qty is not available'); - $result->setHasError(true)->setMessage($message)->setQuoteMessage($message)->setQuoteMessageIndex('qty'); + $result->setHasError(true) + ->setErrorCode('qty_available') + ->setMessage($message) + ->setQuoteMessage($message) + ->setQuoteMessageIndex('qty'); return $result; } else { if ($stockItem->getQty() - $summaryQty < 0) { diff --git a/app/code/Magento/CatalogInventory/Test/Mftf/ActionGroup/AdminCatalogInventoryChangeManageStockActionGroup.xml b/app/code/Magento/CatalogInventory/Test/Mftf/ActionGroup/AdminCatalogInventoryChangeManageStockActionGroup.xml new file mode 100644 index 0000000000000..2c38f14f53379 --- /dev/null +++ b/app/code/Magento/CatalogInventory/Test/Mftf/ActionGroup/AdminCatalogInventoryChangeManageStockActionGroup.xml @@ -0,0 +1,24 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + /** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +--> + +<actionGroups xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/actionGroupSchema.xsd"> + <actionGroup name="AdminCatalogInventoryChangeManageStockActionGroup"> + <annotations> + <description>Opens advanced inventory modal if it has not opened yet. Sets Manage stock value.</description> + </annotations> + <arguments> + <argument name="manageStock" type="string" defaultValue="Yes"/> + </arguments> + <conditionalClick selector="{{AdminProductFormSection.advancedInventoryLink}}" dependentSelector="{{AdminProductFormAdvancedInventorySection.advancedInventoryModal}}" visible="false" stepKey="openAdvancedInventoryWindow"/> + <waitForElementVisible selector="{{AdminProductFormAdvancedInventorySection.useConfigSettings}}" stepKey="waitForAdvancedInventoryModalWindowOpen"/> + <uncheckOption selector="{{AdminProductFormAdvancedInventorySection.useConfigSettings}}" stepKey="uncheckManageStockConfigSetting"/> + <selectOption selector="{{AdminProductFormAdvancedInventorySection.manageStock}}" userInput="{{manageStock}}" stepKey="changeManageStock"/> + <click selector="{{AdminProductFormAdvancedInventorySection.doneButton}}" stepKey="clickDoneButton"/> + </actionGroup> +</actionGroups> diff --git a/app/code/Magento/CatalogInventory/Test/Mftf/Metadata/cataloginventory_item_options-meta.xml b/app/code/Magento/CatalogInventory/Test/Mftf/Metadata/CataloginventoryItemOptionsMeta.xml similarity index 100% rename from app/code/Magento/CatalogInventory/Test/Mftf/Metadata/cataloginventory_item_options-meta.xml rename to app/code/Magento/CatalogInventory/Test/Mftf/Metadata/CataloginventoryItemOptionsMeta.xml diff --git a/app/code/Magento/CatalogInventory/Test/Mftf/Test/AdminCreateProductWithZeroMaximumQtyAllowedInShoppingCartTest.xml b/app/code/Magento/CatalogInventory/Test/Mftf/Test/AdminCreateProductWithZeroMaximumQtyAllowedInShoppingCartTest.xml index 74b24209682ff..9db6f250dc114 100644 --- a/app/code/Magento/CatalogInventory/Test/Mftf/Test/AdminCreateProductWithZeroMaximumQtyAllowedInShoppingCartTest.xml +++ b/app/code/Magento/CatalogInventory/Test/Mftf/Test/AdminCreateProductWithZeroMaximumQtyAllowedInShoppingCartTest.xml @@ -23,12 +23,12 @@ <before> <createData entity="DefaultValueForMaxSaleQty" stepKey="setDefaultValueForMaxSaleQty"/> <createData entity="SimpleProduct2" stepKey="createdProduct"/> - <actionGroup ref="LoginAsAdmin" stepKey="login"/> + <actionGroup ref="AdminLoginActionGroup" stepKey="login"/> </before> <after> <createData entity="DefaultValueForMaxSaleQty" stepKey="setDefaultValueForMaxSaleQty"/> <deleteData createDataKey="createdProduct" stepKey="deleteProduct"/> - <actionGroup ref="logout" stepKey="logout"/> + <actionGroup ref="AdminLogoutActionGroup" stepKey="logout"/> </after> <!-- Go to Inventory configuration page --> @@ -54,7 +54,9 @@ <actionGroup ref="AdminSaveConfigActionGroup" stepKey="saveConfigWithCorrectNumber"/> <!-- Go to product page --> - <amOnPage url="{{AdminProductEditPage.url($$createdProduct.id$$)}}" stepKey="openAdminProductEditPage"/> + <actionGroup ref="AdminProductPageOpenByIdActionGroup" stepKey="openAdminProductEditPage"> + <argument name="productId" value="$$createdProduct.id$$"/> + </actionGroup> <!-- Validate zero value --> <actionGroup ref="AdminProductMaxQtyAllowedInShoppingCartValidationActionGroup" stepKey="productValidateZeroValue"> <argument name="qty" value="0"/> diff --git a/app/code/Magento/CatalogInventory/Test/Mftf/Test/AssociatedProductToConfigurableOutOfStockTest.xml b/app/code/Magento/CatalogInventory/Test/Mftf/Test/AssociatedProductToConfigurableOutOfStockTest.xml index 8458fcf3b94e0..b291fb46ae71c 100644 --- a/app/code/Magento/CatalogInventory/Test/Mftf/Test/AssociatedProductToConfigurableOutOfStockTest.xml +++ b/app/code/Magento/CatalogInventory/Test/Mftf/Test/AssociatedProductToConfigurableOutOfStockTest.xml @@ -80,13 +80,16 @@ </before> <after> - <deleteData createDataKey="createConfigProduct" stepKey="deleteConfigProduct"/> - <deleteData createDataKey="simplecategory" stepKey="deleteSimpleCategory"/> - <deleteData createDataKey="createSimpleUsCustomer" stepKey="deleteCustomer"/> - <deleteData createDataKey="createConfigChildProduct1" stepKey="deleteConfigChildProduct1"/> - <deleteData createDataKey="createConfigChildProduct2" stepKey="deleteConfigChildProduct2"/> - <deleteData createDataKey="createConfigProductAttribute" stepKey="deleteConfigProductAttribute"/> - </after> + <deleteData createDataKey="createConfigProduct" stepKey="deleteConfigProduct"/> + <deleteData createDataKey="simplecategory" stepKey="deleteSimpleCategory"/> + <deleteData createDataKey="createSimpleUsCustomer" stepKey="deleteCustomer"/> + <deleteData createDataKey="createConfigChildProduct1" stepKey="deleteConfigChildProduct1"/> + <deleteData createDataKey="createConfigChildProduct2" stepKey="deleteConfigChildProduct2"/> + <deleteData createDataKey="createConfigProductAttribute" stepKey="deleteConfigProductAttribute"/> + + <!-- Reindex invalidated indices after product attribute has been created/deleted --> + <magentoCron groups="index" stepKey="reindexInvalidatedIndices"/> + </after> <!-- Login as a customer --> <actionGroup ref="LoginToStorefrontActionGroup" stepKey="signUpNewUser"> @@ -110,7 +113,7 @@ <waitForPageLoad stepKey="waitForOrderSuccessPage1"/> <grabTextFrom selector="{{CheckoutSuccessMainSection.orderNumber22}}" stepKey="grabOrderNumber"/> <actionGroup ref="StorefrontSignOutActionGroup" stepKey="StorefrontSignOutActionGroup"/> - <actionGroup ref="LoginAsAdmin" stepKey="loginAsAdmin1"/> + <actionGroup ref="AdminLoginActionGroup" stepKey="loginAsAdmin1"/> <amOnPage url="{{AdminOrdersPage.url}}" stepKey="onOrdersPage"/> <waitForLoadingMaskToDisappear stepKey="waitForLoadingMask3"/> @@ -132,7 +135,7 @@ <waitForLoadingMaskToDisappear stepKey="waitForShipLoadingMask"/> <click selector="{{AdminShipmentMainActionsSection.submitShipment}}" stepKey="submitShipment"/> <waitForPageLoad stepKey="waitShipmentCreated"/> - <actionGroup ref="logout" stepKey="logout"/> + <actionGroup ref="AdminLogoutActionGroup" stepKey="logout"/> <magentoCLI stepKey="runCron" command="cron:run --group='index'"/> <!-- Wait till cron job runs for schedule updates --> diff --git a/app/code/Magento/CatalogInventory/Test/Unit/Model/Quote/Item/QuantityValidator/QuoteItemQtyListTest.php b/app/code/Magento/CatalogInventory/Test/Unit/Model/Quote/Item/QuantityValidator/QuoteItemQtyListTest.php new file mode 100644 index 0000000000000..59a391581fb06 --- /dev/null +++ b/app/code/Magento/CatalogInventory/Test/Unit/Model/Quote/Item/QuantityValidator/QuoteItemQtyListTest.php @@ -0,0 +1,70 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ + +namespace Magento\CatalogInventory\Test\Unit\Model\Quote\Item\QuantityValidator; + +use Magento\Framework\TestFramework\Unit\Helper\ObjectManager; +use PHPUnit\Framework\TestCase; +use Magento\CatalogInventory\Model\Quote\Item\QuantityValidator\QuoteItemQtyList; + +/** + * Class QuoteItemQtyListTest + * + * Test for QuoteItemQtyList class + */ +class QuoteItemQtyListTest extends TestCase +{ + /** + * @var QuoteItemQtyList + */ + private $quoteItemQtyList; + + /** + * @var int + */ + private $itemQtyTestValue; + + /** + * Sets up the fixture, for example, open a network connection. + * This method is called before a test is executed. + */ + protected function setUp() + { + $objectManagerHelper = new ObjectManager($this); + $this->quoteItemQtyList = $objectManagerHelper->getObject(QuoteItemQtyList::class); + } + + /** + * This tests the scenario when item has not quote_item_id and after save gets a value. + * + * @return void + */ + public function testSingleQuoteItemQty() + { + $this->itemQtyTestValue = 1; + $qty = $this->quoteItemQtyList->getQty(125, null, 11232, 1); + $this->assertEquals($this->itemQtyTestValue, $qty); + + $qty = $this->quoteItemQtyList->getQty(125, 1, 11232, 1); + $this->assertEquals($this->itemQtyTestValue, $qty); + } + + /** + * This tests the scenario when item has been added twice to the cart. + * + * @return void + */ + public function testMultipleQuoteItemQty() + { + $this->itemQtyTestValue = 1; + $qty = $this->quoteItemQtyList->getQty(127, 1, 112, 1); + $this->assertEquals($this->itemQtyTestValue, $qty); + + $this->itemQtyTestValue = 2; + $qty = $this->quoteItemQtyList->getQty(127, 2, 112, 1); + $this->assertEquals($this->itemQtyTestValue, $qty); + } +} diff --git a/app/code/Magento/CatalogInventory/Test/Unit/Model/Source/StockTest.php b/app/code/Magento/CatalogInventory/Test/Unit/Model/Source/StockTest.php index 11f41fcaf6d01..1c81e17358e71 100644 --- a/app/code/Magento/CatalogInventory/Test/Unit/Model/Source/StockTest.php +++ b/app/code/Magento/CatalogInventory/Test/Unit/Model/Source/StockTest.php @@ -25,6 +25,7 @@ public function testAddValueSortToCollection() $selectMock = $this->createMock(\Magento\Framework\DB\Select::class); $collectionMock = $this->createMock(\Magento\Eav\Model\Entity\Collection\AbstractCollection::class); $collectionMock->expects($this->atLeastOnce())->method('getSelect')->willReturn($selectMock); + $collectionMock->expects($this->atLeastOnce())->method('getTable')->willReturn('cataloginventory_stock_item'); $selectMock->expects($this->once()) ->method('joinLeft') diff --git a/app/code/Magento/CatalogInventory/Test/Unit/Model/Stock/StockItemCheckerTest.php b/app/code/Magento/CatalogInventory/Test/Unit/Model/Stock/StockItemCheckerTest.php new file mode 100644 index 0000000000000..0f2568a2e213d --- /dev/null +++ b/app/code/Magento/CatalogInventory/Test/Unit/Model/Stock/StockItemCheckerTest.php @@ -0,0 +1,138 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +declare(strict_types=1); + +namespace Magento\CatalogInventory\Test\Unit\Model\Stock; + +use Magento\CatalogInventory\Api\Data\StockItemInterface; +use Magento\CatalogInventory\Model\Stock\StockItemChecker; +use Magento\CatalogInventory\Model\Stock\Item as StockItem; +use Magento\CatalogInventory\Model\Stock\StockItemRepository; +use Magento\Framework\Stdlib\ArrayUtils; +use Magento\Framework\TestFramework\Unit\Helper\ObjectManager; +use PHPUnit\Framework\MockObject\MockObject; +use PHPUnit\Framework\TestCase; + +/** + * Unit test for StockItemChecker. + * @see StockItemChecker + */ +class StockItemCheckerTest extends TestCase +{ + /** + * @var StockItemChecker + */ + private $model; + + /** + * @var StockItemRepository|MockObject + */ + private $stockItemRepository; + + /** + * @var StockItem|MockObject + */ + private $stockItemModel; + + /** + * @var ArrayUtils + */ + private $arrayUtils; + + /** + * @inheritdoc + */ + protected function setUp() + { + $objectManager = new ObjectManager($this); + + $this->stockItemRepository = $this->createPartialMock(StockItemRepository::class, ['get']); + $this->arrayUtils = $objectManager->getObject(ArrayUtils::class); + $this->stockItemModel = $this->createPartialMock(StockItem::class, ['getId', 'getData']); + + $this->model = $objectManager->getObject( + StockItemChecker::class, + [ + 'stockItemRepository' => $this->stockItemRepository, + 'arrayUtils' => $this->arrayUtils, + 'skippedAttributes' => [StockItemInterface::LOW_STOCK_DATE], + ] + ); + } + + /** + * Test for isModified method when model is new. + * + * @return void + */ + public function testIsModifiedWhenModelIsNew(): void + { + $this->stockItemModel->expects($this->once())->method('getId')->willReturn(null); + $this->stockItemRepository->expects($this->never())->method('get'); + + $this->assertTrue($this->model->isModified($this->stockItemModel)); + } + + /** + * Test for isModified method when found difference between data. + * + * @param array $itemFromRepository + * @param array $model + * @param bool $expectedResult + * @return void + * @dataProvider stockItemModelDataProvider + */ + public function testIsModified( + array $itemFromRepository, + array $model, + bool $expectedResult + ): void { + $this->stockItemModel->expects($this->exactly(2))->method('getId')->willReturn($model['id']); + $this->stockItemRepository->expects($this->once())->method('get')->willReturn($this->stockItemModel); + $this->stockItemModel->expects($this->exactly(2)) + ->method('getData') + ->willReturnOnConsecutiveCalls($itemFromRepository, $model); + + $this->assertEquals($expectedResult, $this->model->isModified($this->stockItemModel)); + } + + /** + * Data provider for testIsModified. + * + * @return array + */ + public function stockItemModelDataProvider(): array + { + return [ + 'Model is modified' => [ + 'stockItemFromRepository' => [ + 'id' => 1, + 'low_stock_date' => '01.01.2020', + 'qty' => 100, + ], + 'model' => [ + 'id' => 1, + 'low_stock_date' => '01.01.2021', + 'qty' => 99, + ], + 'expectedResult' => true, + ], + 'Model is not modified' => [ + 'stockItemFromRepository' => [ + 'id' => 1, + 'low_stock_date' => '01.01.2020', + 'qty' => 100, + ], + 'model' => [ + 'id' => 1, + 'low_stock_date' => '01.01.2021', + 'qty' => 100, + ], + 'expectedResult' => false, + ], + ]; + } +} diff --git a/app/code/Magento/CatalogInventory/Test/Unit/Observer/InvalidatePriceIndexUponConfigChangeObserverTest.php b/app/code/Magento/CatalogInventory/Test/Unit/Observer/InvalidatePriceIndexUponConfigChangeObserverTest.php new file mode 100644 index 0000000000000..1dd7df8952473 --- /dev/null +++ b/app/code/Magento/CatalogInventory/Test/Unit/Observer/InvalidatePriceIndexUponConfigChangeObserverTest.php @@ -0,0 +1,118 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +declare(strict_types=1); + +namespace Magento\CatalogInventory\Test\Unit\Observer; + +use Magento\Catalog\Model\Indexer\Product\Price\Processor; +use Magento\CatalogInventory\Model\Configuration; +use Magento\CatalogInventory\Observer\InvalidatePriceIndexUponConfigChangeObserver; +use Magento\Framework\Event; +use Magento\Framework\Event\Observer; +use Magento\Framework\Indexer\IndexerInterface; +use Magento\Framework\TestFramework\Unit\Helper\ObjectManager; +use PHPUnit\Framework\MockObject\MockObject; +use PHPUnit\Framework\TestCase; + +/** + * Class InvalidatePriceIndexUponConfigChangeObserverTest + * + * Testing invalidating product price index onn config changing + */ +class InvalidatePriceIndexUponConfigChangeObserverTest extends TestCase +{ + /** + * @var InvalidatePriceIndexUponConfigChangeObserver + */ + private $observer; + + /** + * @var Processor|MockObject + */ + private $priceIndexProcessorMock; + + /** + * @var Observer|MockObject + */ + private $observerMock; + + /** + * @var Event|MockObject + */ + private $eventMock; + + /** + * @var IndexerInterface|MockObject + */ + private $indexerMock; + + /** + * Set Up + */ + public function setUp() + { + $objectManager = new ObjectManager($this); + $this->priceIndexProcessorMock = $this->createMock(Processor::class); + $this->indexerMock = $this->getMockBuilder(IndexerInterface::class) + ->getMockForAbstractClass(); + $this->observerMock = $this->createMock(Observer::class); + $this->eventMock = $this->getMockBuilder(Event::class) + ->disableOriginalConstructor() + ->setMethods(['getChangedPaths']) + ->getMock(); + + $this->observer = $objectManager->getObject( + InvalidatePriceIndexUponConfigChangeObserver::class, + [ + 'priceIndexProcessor' => $this->priceIndexProcessorMock + ] + ); + } + + /** + * Testing invalidating product price index on catalog inventory config changes + */ + public function testInvalidatingPriceOnChangingOutOfStockConfig() + { + $changedPaths = [Configuration::XML_PATH_SHOW_OUT_OF_STOCK]; + + $this->eventMock->expects($this->once()) + ->method('getChangedPaths') + ->willReturn($changedPaths); + $this->observerMock->expects($this->once()) + ->method('getEvent') + ->willReturn($this->eventMock); + $this->indexerMock->expects($this->once()) + ->method('invalidate'); + $this->priceIndexProcessorMock->expects($this->once()) + ->method('getIndexer') + ->willReturn($this->indexerMock); + + $this->observer->execute($this->observerMock); + } + + /** + * Testing invalidating product price index on changing any other config + */ + public function testInvalidatingPriceOnChangingAnyOtherConfig() + { + $changedPaths = [Configuration::XML_PATH_ITEM_AUTO_RETURN]; + + $this->eventMock->expects($this->once()) + ->method('getChangedPaths') + ->willReturn($changedPaths); + $this->observerMock->expects($this->once()) + ->method('getEvent') + ->willReturn($this->eventMock); + $this->indexerMock->expects($this->never()) + ->method('invalidate'); + $this->priceIndexProcessorMock->expects($this->never()) + ->method('getIndexer') + ->willReturn($this->indexerMock); + + $this->observer->execute($this->observerMock); + } +} diff --git a/app/code/Magento/CatalogInventory/Ui/DataProvider/Product/Form/Modifier/AdvancedInventory.php b/app/code/Magento/CatalogInventory/Ui/DataProvider/Product/Form/Modifier/AdvancedInventory.php index aafde14a28584..38a33b75f552a 100644 --- a/app/code/Magento/CatalogInventory/Ui/DataProvider/Product/Form/Modifier/AdvancedInventory.php +++ b/app/code/Magento/CatalogInventory/Ui/DataProvider/Product/Form/Modifier/AdvancedInventory.php @@ -242,6 +242,7 @@ private function prepareMeta() 'handleChanges' => '${$.provider}:data.product.stock_data.is_qty_decimal', ], 'sortOrder' => 10, + 'disabled' => $this->locator->getProduct()->isLockedAttribute($fieldCode), ]; $advancedInventoryButton['arguments']['data']['config'] = [ 'displayAsLink' => true, diff --git a/app/code/Magento/CatalogInventory/Ui/DataProvider/Product/Form/Modifier/AdvancedInventoryModal.php b/app/code/Magento/CatalogInventory/Ui/DataProvider/Product/Form/Modifier/AdvancedInventoryModal.php new file mode 100644 index 0000000000000..76194b0710441 --- /dev/null +++ b/app/code/Magento/CatalogInventory/Ui/DataProvider/Product/Form/Modifier/AdvancedInventoryModal.php @@ -0,0 +1,70 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +declare(strict_types=1); + +namespace Magento\CatalogInventory\Ui\DataProvider\Product\Form\Modifier; + +use Magento\Catalog\Model\Locator\LocatorInterface; +use Magento\Catalog\Ui\DataProvider\Product\Form\Modifier\AbstractModifier; + +/** + * Data provider for advanced inventory modal form. + */ +class AdvancedInventoryModal extends AbstractModifier +{ + /** + * @var LocatorInterface + */ + private $locator; + + /** + * @var array + */ + private $meta; + + /** + * @param LocatorInterface $locator + */ + public function __construct(LocatorInterface $locator) + { + $this->locator = $locator; + } + + /** + * @inheritdoc + */ + public function modifyMeta(array $meta) + { + $this->meta = $meta; + $this->prepareMeta(); + + return $this->meta; + } + + /** + * @inheritdoc + */ + public function modifyData(array $data) + { + return $data; + } + + /** + * Modify Advanced Inventory Modal meta. + * + * @return array + */ + private function prepareMeta(): array + { + $product = $this->locator->getProduct(); + $readOnly = (bool)$product->getInventoryReadonly(); + + $this->meta['advanced_inventory_modal']['children']['stock_data']['arguments'] + ['data']['config']['disabled'] = $readOnly; + + return $this->meta; + } +} diff --git a/app/code/Magento/CatalogInventory/etc/adminhtml/di.xml b/app/code/Magento/CatalogInventory/etc/adminhtml/di.xml index 28035de29bc2e..065d9c1878849 100644 --- a/app/code/Magento/CatalogInventory/etc/adminhtml/di.xml +++ b/app/code/Magento/CatalogInventory/etc/adminhtml/di.xml @@ -14,11 +14,6 @@ </argument> </arguments> </type> - <type name="Magento\CatalogInventory\Model\Spi\StockStateProviderInterface"> - <arguments> - <argument name="qtyCheckApplicable" xsi:type="boolean">false</argument> - </arguments> - </type> <type name="Magento\Catalog\Ui\DataProvider\Product\ProductDataProvider"> <arguments> <argument name="addFieldStrategies" xsi:type="array"> @@ -37,10 +32,21 @@ <item name="class" xsi:type="string">Magento\CatalogInventory\Ui\DataProvider\Product\Form\Modifier\AdvancedInventory</item> <item name="sortOrder" xsi:type="number">20</item> </item> + <item name="advancedInventoryModal" xsi:type="array"> + <item name="class" xsi:type="string">Magento\CatalogInventory\Ui\DataProvider\Product\Form\Modifier\AdvancedInventoryModal</item> + <item name="sortOrder" xsi:type="number">900</item> + </item> </argument> </arguments> </virtualType> <type name="Magento\Catalog\Model\ProductLink\Search"> <plugin name="processOutOfStockProducts" type="Magento\CatalogInventory\Model\Plugin\ProductSearch"/> </type> + <type name="Magento\CatalogInventory\Model\Stock\StockItemChecker"> + <arguments> + <argument name="skippedAttributes" xsi:type="array"> + <item name="low_stock_date" xsi:type="const">Magento\CatalogInventory\Api\Data\StockItemInterface::LOW_STOCK_DATE</item> + </argument> + </arguments> + </type> </config> diff --git a/app/code/Magento/CatalogInventory/etc/adminhtml/system.xml b/app/code/Magento/CatalogInventory/etc/adminhtml/system.xml index 546f838b9b428..a5a8476b20a02 100644 --- a/app/code/Magento/CatalogInventory/etc/adminhtml/system.xml +++ b/app/code/Magento/CatalogInventory/etc/adminhtml/system.xml @@ -13,21 +13,21 @@ <resource>Magento_CatalogInventory::cataloginventory</resource> <group id="options" translate="label" type="text" sortOrder="1" showInDefault="1" showInWebsite="1" showInStore="1"> <label>Stock Options</label> - <field id="can_subtract" translate="label" type="select" sortOrder="2" showInDefault="1" showInWebsite="0" showInStore="0" canRestore="1"> + <field id="can_subtract" translate="label" type="select" sortOrder="2" showInDefault="1" canRestore="1"> <label>Decrease Stock When Order is Placed</label> <source_model>Magento\Config\Model\Config\Source\Yesno</source_model> </field> - <field id="can_back_in_stock" translate="label" type="select" sortOrder="2" showInDefault="1" showInWebsite="0" showInStore="0" canRestore="1"> + <field id="can_back_in_stock" translate="label" type="select" sortOrder="2" showInDefault="1" canRestore="1"> <label>Set Items' Status to be In Stock When Order is Cancelled</label> <source_model>Magento\Config\Model\Config\Source\Yesno</source_model> </field> - <field id="show_out_of_stock" translate="label comment" type="select" sortOrder="3" showInDefault="1" showInWebsite="0" showInStore="0" canRestore="1"> + <field id="show_out_of_stock" translate="label comment" type="select" sortOrder="3" showInDefault="1" canRestore="1"> <label>Display Out of Stock Products</label> <comment>Products will still be shown by direct product URLs.</comment> <source_model>Magento\Config\Model\Config\Source\Yesno</source_model> <backend_model>Magento\CatalogInventory\Model\Config\Backend\ShowOutOfStock</backend_model> </field> - <field id="stock_threshold_qty" translate="label" type="text" sortOrder="4" showInDefault="1" showInWebsite="1" showInStore="0" canRestore="1"> + <field id="stock_threshold_qty" translate="label" type="text" sortOrder="4" showInDefault="1" showInWebsite="1" canRestore="1"> <label>Only X left Threshold</label> <validate>validate-number</validate> </field> @@ -41,45 +41,45 @@ <![CDATA[Please note that these settings apply to individual items in the cart, not to the entire cart.]]> </comment> <label>Product Stock Options</label> - <field id="manage_stock" translate="label comment" type="select" sortOrder="1" showInDefault="1" showInWebsite="0" showInStore="0" canRestore="1"> + <field id="manage_stock" translate="label comment" type="select" sortOrder="1" showInDefault="1" canRestore="1"> <label>Manage Stock</label> <source_model>Magento\Config\Model\Config\Source\Yesno</source_model> <backend_model>Magento\CatalogInventory\Model\Config\Backend\Managestock</backend_model> <comment>Changing can take some time due to processing whole catalog.</comment> </field> - <field id="backorders" translate="label comment" type="select" sortOrder="3" showInDefault="1" showInWebsite="0" showInStore="0" canRestore="1"> + <field id="backorders" translate="label comment" type="select" sortOrder="3" showInDefault="1" canRestore="1"> <label>Backorders</label> <source_model>Magento\CatalogInventory\Model\Source\Backorders</source_model> <backend_model>Magento\CatalogInventory\Model\Config\Backend\Backorders</backend_model> <comment>Changing can take some time due to processing whole catalog.</comment> </field> - <field id="max_sale_qty" translate="label" type="text" sortOrder="4" showInDefault="1" showInWebsite="0" showInStore="0" canRestore="1"> + <field id="max_sale_qty" translate="label" type="text" sortOrder="4" showInDefault="1" canRestore="1"> <label>Maximum Qty Allowed in Shopping Cart</label> <validate>validate-number validate-greater-than-zero</validate> </field> - <field id="min_qty" translate="label" type="text" sortOrder="5" showInDefault="1" showInWebsite="0" showInStore="0" canRestore="1"> + <field id="min_qty" translate="label" type="text" sortOrder="5" showInDefault="1" canRestore="1"> <label>Out-of-Stock Threshold</label> <validate>validate-number</validate> <backend_model>Magento\CatalogInventory\Model\System\Config\Backend\Minqty</backend_model> </field> - <field id="min_sale_qty" translate="label" sortOrder="6" showInDefault="1" showInWebsite="0" showInStore="0" canRestore="1"> + <field id="min_sale_qty" translate="label" sortOrder="6" showInDefault="1" canRestore="1"> <label>Minimum Qty Allowed in Shopping Cart</label> <frontend_model>Magento\CatalogInventory\Block\Adminhtml\Form\Field\Minsaleqty</frontend_model> <backend_model>Magento\CatalogInventory\Model\System\Config\Backend\Minsaleqty</backend_model> </field> - <field id="notify_stock_qty" translate="label" type="text" sortOrder="7" showInDefault="1" showInWebsite="0" showInStore="0" canRestore="1"> + <field id="notify_stock_qty" translate="label" type="text" sortOrder="7" showInDefault="1" canRestore="1"> <label>Notify for Quantity Below</label> <validate>validate-number</validate> </field> - <field id="auto_return" translate="label" type="select" sortOrder="10" showInDefault="1" showInWebsite="0" showInStore="0"> + <field id="auto_return" translate="label" type="select" sortOrder="10" showInDefault="1" canRestore="1"> <label>Automatically Return Credit Memo Item to Stock</label> <source_model>Magento\Config\Model\Config\Source\Yesno</source_model> </field> - <field id="enable_qty_increments" translate="label" type="select" sortOrder="8" showInDefault="1" showInWebsite="0" showInStore="0" canRestore="1"> + <field id="enable_qty_increments" translate="label" type="select" sortOrder="8" showInDefault="1" canRestore="1"> <label>Enable Qty Increments</label> <source_model>Magento\Config\Model\Config\Source\Yesno</source_model> </field> - <field id="qty_increments" translate="label" type="text" sortOrder="9" showInDefault="1" showInWebsite="0" showInStore="0" canRestore="1"> + <field id="qty_increments" translate="label" type="text" sortOrder="9" showInDefault="1" canRestore="1"> <label>Qty Increments</label> <validate>validate-number validate-greater-than-zero</validate> <backend_model>Magento\CatalogInventory\Model\System\Config\Backend\Qtyincrements</backend_model> diff --git a/app/code/Magento/CatalogInventory/etc/config.xml b/app/code/Magento/CatalogInventory/etc/config.xml index 976b3f4cad510..21641b3a4cfcd 100644 --- a/app/code/Magento/CatalogInventory/etc/config.xml +++ b/app/code/Magento/CatalogInventory/etc/config.xml @@ -22,6 +22,7 @@ <min_sale_qty>1</min_sale_qty> <min_qty>0</min_qty> <notify_stock_qty>1</notify_stock_qty> + <auto_return>0</auto_return> <enable_qty_increments>0</enable_qty_increments> <qty_increments>1</qty_increments> </item_options> diff --git a/app/code/Magento/CatalogInventory/etc/mview.xml b/app/code/Magento/CatalogInventory/etc/mview.xml index 72dda16e8b5bb..338f1fe0610a1 100644 --- a/app/code/Magento/CatalogInventory/etc/mview.xml +++ b/app/code/Magento/CatalogInventory/etc/mview.xml @@ -9,6 +9,8 @@ <view id="cataloginventory_stock" class="Magento\CatalogInventory\Model\Indexer\Stock" group="indexer"> <subscriptions> <table name="cataloginventory_stock_item" entity_column="product_id" /> + <!--Track product status to trigger stock indexer--> + <table name="catalog_product_entity_int" entity_column="entity_id" /> </subscriptions> </view> <view id="catalog_product_price" class="Magento\Catalog\Model\Indexer\Product\Price" group="indexer"> diff --git a/app/code/Magento/CatalogInventory/registration.php b/app/code/Magento/CatalogInventory/registration.php index da5cb708ea17f..d39cc2074d890 100644 --- a/app/code/Magento/CatalogInventory/registration.php +++ b/app/code/Magento/CatalogInventory/registration.php @@ -4,6 +4,6 @@ * See COPYING.txt for license details. */ -use \Magento\Framework\Component\ComponentRegistrar; +use Magento\Framework\Component\ComponentRegistrar; ComponentRegistrar::register(ComponentRegistrar::MODULE, 'Magento_CatalogInventory', __DIR__); diff --git a/app/code/Magento/CatalogInventory/view/adminhtml/ui_component/product_form.xml b/app/code/Magento/CatalogInventory/view/adminhtml/ui_component/product_form.xml index c77c77a5183d0..27ce26cabc627 100644 --- a/app/code/Magento/CatalogInventory/view/adminhtml/ui_component/product_form.xml +++ b/app/code/Magento/CatalogInventory/view/adminhtml/ui_component/product_form.xml @@ -49,6 +49,9 @@ <scopeLabel>[GLOBAL]</scopeLabel> <label translate="true">Manage Stock</label> <dataScope>manage_stock</dataScope> + <imports> + <link name="disabled">${$.parentName}.use_config_${$.index}:disableParent</link> + </imports> </settings> <formElements> <select> @@ -71,9 +74,9 @@ <links> <link name="linkedValue">${$.provider}:data.product.stock_data.manage_stock</link> </links> - <exports> - <link name="checked">${$.parentName}.manage_stock:disabled</link> - </exports> + <imports> + <link name="disabled">ns = ${ $.ns }, index = stock_data:disabled</link> + </imports> </settings> <formElements> <checkbox class="Magento\CatalogInventory\Ui\Component\Product\Form\Element\UseConfigSettings"> @@ -129,6 +132,9 @@ </validation> <label translate="true">Out-of-Stock Threshold</label> <dataScope>min_qty</dataScope> + <imports> + <link name="disabled">${$.parentName}.use_config_${$.index}:disableParent</link> + </imports> </settings> </field> <field name="use_config_min_qty" component="Magento_CatalogInventory/js/components/use-config-settings" formElement="checkbox"> @@ -144,9 +150,9 @@ <links> <link name="linkedValue">${$.provider}:data.product.stock_data.min_qty</link> </links> - <exports> - <link name="checked">${$.parentName}.min_qty:disabled</link> - </exports> + <imports> + <link name="disabled">ns = ${ $.ns }, index = stock_data:disabled</link> + </imports> </settings> <formElements> <checkbox class="Magento\CatalogInventory\Ui\Component\Product\Form\Element\UseConfigSettings"> @@ -187,6 +193,7 @@ <dataScope>min_sale_qty</dataScope> <imports> <link name="handleChanges">${$.provider}:data.product.stock_data.is_qty_decimal</link> + <link name="disabled">${$.parentName}.use_config_${$.index}:disableParent</link> </imports> </settings> </field> @@ -207,6 +214,9 @@ <class name="admin__field-no-label">true</class> </additionalClasses> <dataScope>use_config_min_sale_qty</dataScope> + <imports> + <link name="disabled">ns = ${ $.ns }, index = stock_data:disabled</link> + </imports> </settings> <formElements> <checkbox class="Magento\CatalogInventory\Ui\Component\Product\Form\Element\UseConfigSettings"> @@ -287,6 +297,9 @@ </validation> <label translate="true">Maximum Qty Allowed in Shopping Cart</label> <dataScope>max_sale_qty</dataScope> + <imports> + <link name="disabled">${$.parentName}.use_config_${$.index}:disableParent</link> + </imports> </settings> </field> <field name="use_config_max_sale_qty" component="Magento_CatalogInventory/js/components/use-config-settings" formElement="checkbox"> @@ -302,9 +315,9 @@ <links> <link name="linkedValue">${$.provider}:data.product.stock_data.max_sale_qty</link> </links> - <exports> - <link name="checked">${$.parentName}.max_sale_qty:disabled</link> - </exports> + <imports> + <link name="disabled">ns = ${ $.ns }, index = stock_data:disabled</link> + </imports> </settings> <formElements> <checkbox class="Magento\CatalogInventory\Ui\Component\Product\Form\Element\UseConfigSettings"> @@ -388,6 +401,9 @@ <scopeLabel>[GLOBAL]</scopeLabel> <label translate="true">Backorders</label> <dataScope>backorders</dataScope> + <imports> + <link name="disabled">${$.parentName}.use_config_${$.index}:disableParent</link> + </imports> </settings> <formElements> <select> @@ -410,9 +426,9 @@ <links> <link name="linkedValue">${$.provider}:data.product.stock_data.backorders</link> </links> - <exports> - <link name="checked">${$.parentName}.backorders:disabled</link> - </exports> + <imports> + <link name="disabled">ns = ${ $.ns }, index = stock_data:disabled</link> + </imports> </settings> <formElements> <checkbox class="Magento\CatalogInventory\Ui\Component\Product\Form\Element\UseConfigSettings"> @@ -450,6 +466,9 @@ </validation> <label translate="true">Notify for Quantity Below</label> <dataScope>notify_stock_qty</dataScope> + <imports> + <link name="disabled">${$.parentName}.use_config_${$.index}:disableParent</link> + </imports> </settings> </field> <field name="use_config_notify_stock_qty" component="Magento_CatalogInventory/js/components/use-config-settings" formElement="checkbox"> @@ -465,9 +484,9 @@ <links> <link name="linkedValue">${$.provider}:data.product.stock_data.notify_stock_qty</link> </links> - <exports> - <link name="checked">${$.parentName}.notify_stock_qty:disabled</link> - </exports> + <imports> + <link name="disabled">ns = ${ $.ns }, index = stock_data:disabled</link> + </imports> </settings> <formElements> <checkbox class="Magento\CatalogInventory\Ui\Component\Product\Form\Element\UseConfigSettings"> @@ -500,6 +519,9 @@ <scopeLabel>[GLOBAL]</scopeLabel> <label translate="true">Enable Qty Increments</label> <dataScope>enable_qty_increments</dataScope> + <imports> + <link name="disabled">${$.parentName}.use_config_${$.index}:disableParent</link> + </imports> </settings> <formElements> <select> @@ -522,9 +544,9 @@ <links> <link name="linkedValue">${$.provider}:data.product.stock_data.enable_qty_increments</link> </links> - <exports> - <link name="checked">${$.parentName}.enable_qty_increments:disabled</link> - </exports> + <imports> + <link name="disabled">ns = ${ $.ns }, index = stock_data:disabled</link> + </imports> </settings> <formElements> <checkbox class="Magento\CatalogInventory\Ui\Component\Product\Form\Element\UseConfigSettings"> @@ -565,6 +587,7 @@ <dataScope>qty_increments</dataScope> <imports> <link name="handleChanges">${$.provider}:data.product.stock_data.is_qty_decimal</link> + <link name="disabled">${$.parentName}.use_config_${$.index}:disableParent</link> </imports> </settings> </field> @@ -581,9 +604,9 @@ <links> <link name="linkedValue">${$.provider}:data.product.stock_data.qty_increments</link> </links> - <exports> - <link name="checked">${$.parentName}.qty_increments:disabled</link> - </exports> + <imports> + <link name="disabled">ns = ${ $.ns }, index = stock_data:disabled</link> + </imports> </settings> <formElements> <checkbox class="Magento\CatalogInventory\Ui\Component\Product\Form\Element\UseConfigSettings"> @@ -618,6 +641,9 @@ <scopeLabel>[GLOBAL]</scopeLabel> <label translate="true">Stock Status</label> <dataScope>is_in_stock</dataScope> + <imports> + <link name="disabled">ns = ${ $.ns }, index = stock_data:disabled</link> + </imports> </settings> <formElements> <select> diff --git a/app/code/Magento/CatalogInventory/view/adminhtml/web/js/components/use-config-min-sale-qty.js b/app/code/Magento/CatalogInventory/view/adminhtml/web/js/components/use-config-min-sale-qty.js index 4ac1d5ed2d294..c7ca38f05f707 100644 --- a/app/code/Magento/CatalogInventory/view/adminhtml/web/js/components/use-config-min-sale-qty.js +++ b/app/code/Magento/CatalogInventory/view/adminhtml/web/js/components/use-config-min-sale-qty.js @@ -37,7 +37,7 @@ define([ /** * @inheritdoc */ - 'onCheckedChanged': function (newChecked) { + onCheckedChanged: function (newChecked) { var valueFromConfig = this.valueFromConfig(); if (newChecked && (_.isArray(valueFromConfig) && valueFromConfig.length === 0 || valueFromConfig === 1)) { @@ -49,7 +49,7 @@ define([ this.changeVisibleDisabled(this.inputField, true, true, null); this.changeVisibleDisabled(this.dynamicRowsField, false, true, null); } else { - this.changeVisibleDisabled(this.inputField, true, false, null); + this.changeVisibleDisabled(this.inputField, true, this.disabled() || false, null); this.changeVisibleDisabled(this.dynamicRowsField, false, true, null); } diff --git a/app/code/Magento/CatalogInventory/view/adminhtml/web/js/components/use-config-settings.js b/app/code/Magento/CatalogInventory/view/adminhtml/web/js/components/use-config-settings.js index f91797448fa55..e121743b0f69f 100644 --- a/app/code/Magento/CatalogInventory/view/adminhtml/web/js/components/use-config-settings.js +++ b/app/code/Magento/CatalogInventory/view/adminhtml/web/js/components/use-config-settings.js @@ -11,7 +11,15 @@ define([ return checkbox.extend({ defaults: { valueFromConfig: '', - linkedValue: '' + linkedValue: '', + disableParent: false, + listens: { + disabled: 'processState', + checked: 'processState onCheckedChanged' + }, + imports: { + readOnly: 'ns = ${ $.ns }, index = stock_data:disabled' + } }, /** @@ -20,13 +28,24 @@ define([ initObservable: function () { return this ._super() - .observe(['valueFromConfig', 'linkedValue']); + .observe(['valueFromConfig', 'linkedValue', 'disableParent']); + }, + + /** + * Handle checked and disabled changes to calculate disableParent value + */ + processState: function () { + this.disableParent(this.checked() || this.readOnly); + + if (this.readOnly) { + this.disable(); + } }, /** * @inheritdoc */ - 'onCheckedChanged': function (newChecked) { + onCheckedChanged: function (newChecked) { if (newChecked) { this.linkedValue(this.valueFromConfig()); } diff --git a/app/code/Magento/CatalogRule/Model/ResourceModel/Product/LinkedProductSelectBuilderByCatalogRulePrice.php b/app/code/Magento/CatalogRule/Model/ResourceModel/Product/LinkedProductSelectBuilderByCatalogRulePrice.php index 48c463fc18b80..aaafc4ecd4c5d 100644 --- a/app/code/Magento/CatalogRule/Model/ResourceModel/Product/LinkedProductSelectBuilderByCatalogRulePrice.php +++ b/app/code/Magento/CatalogRule/Model/ResourceModel/Product/LinkedProductSelectBuilderByCatalogRulePrice.php @@ -86,9 +86,9 @@ public function __construct( /** * @inheritdoc */ - public function build($productId) + public function build(int $productId, int $storeId) : array { - $timestamp = $this->localeDate->scopeTimeStamp($this->storeManager->getStore()); + $timestamp = $this->localeDate->scopeTimeStamp($this->storeManager->getStore($storeId)); $currentDate = $this->dateTime->formatDate($timestamp, false); $linkField = $this->metadataPool->getMetadata(ProductInterface::class)->getLinkField(); $productTable = $this->resource->getTableName('catalog_product_entity'); @@ -108,7 +108,7 @@ public function build($productId) sprintf('t.product_id = %s.%s', BaseSelectProcessorInterface::PRODUCT_TABLE_ALIAS, $linkField), [] )->where('parent.entity_id = ?', $productId) - ->where('t.website_id = ?', $this->storeManager->getStore()->getWebsiteId()) + ->where('t.website_id = ?', $this->storeManager->getStore($storeId)->getWebsiteId()) ->where('t.customer_group_id = ?', $this->customerSession->getCustomerGroupId()) ->where('t.rule_date = ?', $currentDate) ->order('t.rule_price ' . Select::SQL_ASC) diff --git a/app/code/Magento/CatalogRule/Model/Rule.php b/app/code/Magento/CatalogRule/Model/Rule.php index f2e8e54d34665..cd24201963f25 100644 --- a/app/code/Magento/CatalogRule/Model/Rule.php +++ b/app/code/Magento/CatalogRule/Model/Rule.php @@ -3,6 +3,8 @@ * Copyright © Magento, Inc. All rights reserved. * See COPYING.txt for license details. */ +declare(strict_types=1); + namespace Magento\CatalogRule\Model; use Magento\Catalog\Model\Product; @@ -13,6 +15,7 @@ use Magento\CatalogRule\Helper\Data; use Magento\CatalogRule\Model\Data\Condition\Converter; use Magento\CatalogRule\Model\Indexer\Rule\RuleProductProcessor; +use Magento\CatalogRule\Model\ResourceModel\Product\ConditionsToCollectionApplier; use Magento\CatalogRule\Model\ResourceModel\Rule as RuleResourceModel; use Magento\CatalogRule\Model\Rule\Action\CollectionFactory as RuleCollectionFactory; use Magento\CatalogRule\Model\Rule\Condition\CombineFactory; @@ -33,7 +36,6 @@ use Magento\Framework\Stdlib\DateTime; use Magento\Framework\Stdlib\DateTime\TimezoneInterface; use Magento\Store\Model\StoreManagerInterface; -use Magento\CatalogRule\Model\ResourceModel\Product\ConditionsToCollectionApplier; /** * Catalog Rule data model @@ -499,7 +501,8 @@ public function calcProductPriceRule(Product $product, $price) } else { $customerGroupId = $this->_customerSession->getCustomerGroupId(); } - $dateTs = $this->_localeDate->scopeTimeStamp($storeId); + $currentDateTime = new \DateTime(); + $dateTs = $currentDateTime->getTimestamp(); $cacheKey = date('Y-m-d', $dateTs) . "|{$websiteId}|{$customerGroupId}|{$productId}|{$price}"; if (!array_key_exists($cacheKey, self::$_priceRulesData)) { @@ -895,4 +898,12 @@ public function getIdentities() { return ['price']; } + + /** + * Clear price rules cache. + */ + public function clearPriceRulesData(): void + { + self::$_priceRulesData = []; + } } diff --git a/app/code/Magento/CatalogRule/Observer/ProcessAdminFinalPriceObserver.php b/app/code/Magento/CatalogRule/Observer/ProcessAdminFinalPriceObserver.php index 89ed519cfb8c8..0add936467471 100644 --- a/app/code/Magento/CatalogRule/Observer/ProcessAdminFinalPriceObserver.php +++ b/app/code/Magento/CatalogRule/Observer/ProcessAdminFinalPriceObserver.php @@ -7,9 +7,10 @@ namespace Magento\CatalogRule\Observer; -use Magento\Framework\Stdlib\DateTime\TimezoneInterface; -use Magento\Framework\Registry; use Magento\Framework\Event\ObserverInterface; +use Magento\Framework\Registry; +use Magento\Framework\Stdlib\DateTime\TimezoneInterface; +use Magento\Store\Model\StoreManagerInterface; /** * Observer for applying catalog rules on product for admin area @@ -23,6 +24,11 @@ class ProcessAdminFinalPriceObserver implements ObserverInterface */ protected $coreRegistry; + /** + * @var StoreManagerInterface + */ + protected $storeManager; + /** * @var \Magento\Framework\Stdlib\DateTime\TimezoneInterface */ @@ -41,17 +47,20 @@ class ProcessAdminFinalPriceObserver implements ObserverInterface /** * @param RulePricesStorage $rulePricesStorage * @param Registry $coreRegistry + * @param StoreManagerInterface $storeManager * @param \Magento\CatalogRule\Model\ResourceModel\RuleFactory $resourceRuleFactory * @param TimezoneInterface $localeDate */ public function __construct( RulePricesStorage $rulePricesStorage, Registry $coreRegistry, + StoreManagerInterface $storeManager, \Magento\CatalogRule\Model\ResourceModel\RuleFactory $resourceRuleFactory, TimezoneInterface $localeDate ) { $this->rulePricesStorage = $rulePricesStorage; $this->coreRegistry = $coreRegistry; + $this->storeManager = $storeManager; $this->resourceRuleFactory = $resourceRuleFactory; $this->localeDate = $localeDate; } @@ -61,6 +70,7 @@ public function __construct( * * @param \Magento\Framework\Event\Observer $observer * @return $this + * @throws \Magento\Framework\Exception\NoSuchEntityException */ public function execute(\Magento\Framework\Event\Observer $observer) { @@ -74,13 +84,17 @@ public function execute(\Magento\Framework\Event\Observer $observer) $wId = $ruleData->getWebsiteId(); $gId = $ruleData->getCustomerGroupId(); $pId = $product->getId(); - $key = "{$date->format('Y-m-d H:i:s')}|{$wId}|{$gId}|{$pId}"; } elseif ($product->getWebsiteId() !== null && $product->getCustomerGroupId() !== null) { $wId = $product->getWebsiteId(); $gId = $product->getCustomerGroupId(); $pId = $product->getId(); $key = "{$date->format('Y-m-d H:i:s')}|{$wId}|{$gId}|{$pId}"; + } elseif ($product->getWebsiteId() === null && $product->getCustomerGroupId() !== null) { + $wId = $this->storeManager->getStore($storeId)->getWebsiteId(); + $gId = $product->getCustomerGroupId(); + $pId = $product->getId(); + $key = "{$date->format('Y-m-d H:i:s')}|{$wId}|{$gId}|{$pId}"; } if ($key) { diff --git a/app/code/Magento/CatalogRule/Test/Mftf/ActionGroup/AdminCatalogPriceRuleAddSkuConditionActionGroup.xml b/app/code/Magento/CatalogRule/Test/Mftf/ActionGroup/AdminCatalogPriceRuleAddSkuConditionActionGroup.xml new file mode 100644 index 0000000000000..2c4e3ddb648e0 --- /dev/null +++ b/app/code/Magento/CatalogRule/Test/Mftf/ActionGroup/AdminCatalogPriceRuleAddSkuConditionActionGroup.xml @@ -0,0 +1,28 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + /** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +--> +<actionGroups xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/actionGroupSchema.xsd"> + <actionGroup name="AdminCatalogPriceRuleAddSkuConditionActionGroup"> + <annotations> + <description>Create new product SKU based condition in Catalog Price Rule form.</description> + </annotations> + <arguments> + <argument name="productSku" type="string" defaultValue="{{ApiSimpleTwo.sku}}"/> + </arguments> + + <conditionalClick selector="{{AdminNewCatalogPriceRule.conditionsTabTitle}}" dependentSelector="{{AdminNewCatalogPriceRule.conditionsTabBody}}" visible="false" stepKey="openConditionsSectionIfNeeded"/> + <scrollTo selector="{{AdminNewCatalogPriceRule.conditionsTabTitle}}" stepKey="scrollToConditionsFieldset"/> + <waitForElementVisible selector="{{AdminNewCatalogPriceRuleConditions.newCondition}}" stepKey="waitForNewConditionButton"/> + <click selector="{{AdminNewCatalogPriceRuleConditions.newCondition}}" stepKey="clickAddNewConditionButton"/> + <selectOption selector="{{AdminNewCatalogPriceRuleConditions.conditionSelect('1')}}" userInput="Magento\CatalogRule\Model\Rule\Condition\Product|sku" stepKey="selectConditionTypeSku"/> + <waitForPageLoad stepKey="waitConditionFormRefresh"/> + <click selector="{{AdminNewCatalogPriceRuleConditions.targetEllipsis('1')}}" stepKey="clickEllipsis"/> + <fillField selector="{{AdminNewCatalogPriceRuleConditions.targetInput('1', '1')}}" userInput="{{productSku}}" stepKey="fillProductSku"/> + <click selector="{{AdminNewCatalogPriceRuleConditions.applyButton('1', '1')}}" stepKey="clickApply"/> + </actionGroup> +</actionGroups> diff --git a/app/code/Magento/CatalogRule/Test/Mftf/ActionGroup/AdminCatalogPriceRuleFillActionsActionGroup.xml b/app/code/Magento/CatalogRule/Test/Mftf/ActionGroup/AdminCatalogPriceRuleFillActionsActionGroup.xml new file mode 100644 index 0000000000000..da8571769ef31 --- /dev/null +++ b/app/code/Magento/CatalogRule/Test/Mftf/ActionGroup/AdminCatalogPriceRuleFillActionsActionGroup.xml @@ -0,0 +1,27 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + /** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +--> +<actionGroups xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/actionGroupSchema.xsd"> + <actionGroup name="AdminCatalogPriceRuleFillActionsActionGroup"> + <annotations> + <description>Fill Catalog Price Rule actions fields: Apply, Discount Amount, Discard subsequent rules.</description> + </annotations> + <arguments> + <argument name="apply" type="string" defaultValue="{{_defaultCatalogRule.simple_action}}"/> + <argument name="discountAmount" type="string" defaultValue="{{_defaultCatalogRule.discount_amount}}"/> + <argument name="discardSubsequentRules" type="string" defaultValue="Yes"/> + </arguments> + + <conditionalClick selector="{{AdminNewCatalogPriceRule.actionsTabTitle}}" dependentSelector="{{AdminNewCatalogPriceRule.actionsTabBody}}" visible="false" stepKey="openActionSectionIfNeeded"/> + <scrollTo selector="{{AdminNewCatalogPriceRule.actionsTabTitle}}" stepKey="scrollToActionsFieldset"/> + <waitForElementVisible selector="{{AdminNewCatalogPriceRuleActions.apply}}" stepKey="waitActionsFieldsetFullyOpened"/> + <selectOption selector="{{AdminNewCatalogPriceRuleActions.apply}}" userInput="{{apply}}" stepKey="fillDiscountType"/> + <fillField selector="{{AdminNewCatalogPriceRuleActions.discountAmount}}" userInput="{{discountAmount}}" stepKey="fillDiscountAmount"/> + <selectOption selector="{{AdminNewCatalogPriceRuleActions.disregardRules}}" userInput="{{discardSubsequentRules}}" stepKey="fillDiscardSubsequentRules"/> + </actionGroup> +</actionGroups> diff --git a/app/code/Magento/CatalogRule/Test/Mftf/ActionGroup/AdminCatalogPriceRuleFillMainInfoActionGroup.xml b/app/code/Magento/CatalogRule/Test/Mftf/ActionGroup/AdminCatalogPriceRuleFillMainInfoActionGroup.xml new file mode 100644 index 0000000000000..e609550d19461 --- /dev/null +++ b/app/code/Magento/CatalogRule/Test/Mftf/ActionGroup/AdminCatalogPriceRuleFillMainInfoActionGroup.xml @@ -0,0 +1,34 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + /** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +--> +<actionGroups xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/actionGroupSchema.xsd"> + <actionGroup name="AdminCatalogPriceRuleFillMainInfoActionGroup"> + <annotations> + <description>Fill Catalog Price Rule main info fields: Name, Description, Active (1/0), Priority.</description> + </annotations> + <arguments> + <argument name="name" type="string" defaultValue="{{_defaultCatalogRule.name}}"/> + <argument name="description" type="string" defaultValue="{{_defaultCatalogRule.description}}"/> + <argument name="active" type="string" defaultValue="1"/> + <argument name="websites" type="string" defaultValue="'Main Website'"/> + <argument name="groups" type="string" defaultValue="'NOT LOGGED IN','General','Wholesale','Retailer'"/> + <argument name="fromDate" type="string" defaultValue=""/> + <argument name="toDate" type="string" defaultValue=""/> + <argument name="priority" type="string" defaultValue=""/> + </arguments> + + <fillField selector="{{AdminNewCatalogPriceRule.ruleName}}" userInput="{{name}}" stepKey="fillName"/> + <fillField selector="{{AdminNewCatalogPriceRule.description}}" userInput="{{description}}" stepKey="fillDescription"/> + <conditionalClick selector="{{AdminNewCatalogPriceRule.isActive}}" dependentSelector="{{AdminNewCatalogPriceRule.activeByStatus(active)}}" visible="false" stepKey="fillActive"/> + <selectOption selector="{{AdminNewCatalogPriceRule.websites}}" parameterArray="[{{websites}}]" stepKey="selectSpecifiedWebsites"/> + <selectOption selector="{{AdminNewCatalogPriceRule.customerGroups}}" parameterArray="[{{groups}}]" stepKey="selectSpecifiedCustomerGroups"/> + <fillField selector="{{AdminNewCatalogPriceRule.fromDate}}" userInput="{{fromDate}}" stepKey="fillFromDate"/> + <fillField selector="{{AdminNewCatalogPriceRule.toDate}}" userInput="{{toDate}}" stepKey="fillToDate"/> + <fillField selector="{{AdminNewCatalogPriceRule.priority}}" userInput="{{priority}}" stepKey="fillPriority"/> + </actionGroup> +</actionGroups> diff --git a/app/code/Magento/CatalogRule/Test/Mftf/ActionGroup/AdminCatalogPriceRuleSaveAndApplyActionGroup.xml b/app/code/Magento/CatalogRule/Test/Mftf/ActionGroup/AdminCatalogPriceRuleSaveAndApplyActionGroup.xml new file mode 100644 index 0000000000000..12a3cad59a606 --- /dev/null +++ b/app/code/Magento/CatalogRule/Test/Mftf/ActionGroup/AdminCatalogPriceRuleSaveAndApplyActionGroup.xml @@ -0,0 +1,22 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + /** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +--> +<actionGroups xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/actionGroupSchema.xsd"> + <actionGroup name="AdminCatalogPriceRuleSaveAndApplyActionGroup"> + <annotations> + <description>Clicks Save and Apply on a Admin Catalog Price Rule creation/edit page. Validates that the Success Message is present. Validates that applied rules success message is present.</description> + </annotations> + + <scrollToTopOfPage stepKey="scrollToTop"/> + <waitForElementVisible selector="{{AdminNewCatalogPriceRule.saveAndApply}}" stepKey="waitForSaveAndApplyButton"/> + <click selector="{{AdminNewCatalogPriceRule.saveAndApply}}" stepKey="saveAndApplyRule"/> + <waitForElementVisible selector="{{AdminMessagesSection.success}}" stepKey="waitForSuccessMessageAppears"/> + <see selector="{{AdminMessagesSection.success}}" userInput="You saved the rule." stepKey="checkSuccessSaveMessage"/> + <see selector="{{AdminMessagesSection.success}}" userInput="Updated rules applied." stepKey="checkSuccessAppliedMessage"/> + </actionGroup> +</actionGroups> diff --git a/app/code/Magento/CatalogRule/Test/Mftf/ActionGroup/AdminCatalogPriceRuleSelectCustomerGroupsActionGroup.xml b/app/code/Magento/CatalogRule/Test/Mftf/ActionGroup/AdminCatalogPriceRuleSelectCustomerGroupsActionGroup.xml new file mode 100644 index 0000000000000..8c37325aff722 --- /dev/null +++ b/app/code/Magento/CatalogRule/Test/Mftf/ActionGroup/AdminCatalogPriceRuleSelectCustomerGroupsActionGroup.xml @@ -0,0 +1,20 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + /** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +--> +<actionGroups xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/actionGroupSchema.xsd"> + <actionGroup name="AdminCatalogPriceRuleSelectCustomerGroupsActionGroup"> + <annotations> + <description>Fill Catalog Price Rule customer groups multiselect on new/edit page.</description> + </annotations> + <arguments> + <argument name="groups" type="string" defaultValue="'NOT LOGGED IN','General','Wholesale','Retailer'"/> + </arguments> + + <selectOption selector="{{AdminNewCatalogPriceRule.customerGroups}}" parameterArray="[{{groups}}]" stepKey="selectSpecifiedCustomerGroups"/> + </actionGroup> +</actionGroups> diff --git a/app/code/Magento/CatalogRule/Test/Mftf/ActionGroup/AdminCreateCatalogPriceRuleWithConditionActionGroup.xml b/app/code/Magento/CatalogRule/Test/Mftf/ActionGroup/AdminCreateCatalogPriceRuleWithConditionActionGroup.xml index eebc1175f1894..9e21ea186f67b 100644 --- a/app/code/Magento/CatalogRule/Test/Mftf/ActionGroup/AdminCreateCatalogPriceRuleWithConditionActionGroup.xml +++ b/app/code/Magento/CatalogRule/Test/Mftf/ActionGroup/AdminCreateCatalogPriceRuleWithConditionActionGroup.xml @@ -23,6 +23,9 @@ <comment userInput="Assert that attribute contains today date without time" stepKey="assertDate" after="waitForAttributeLoad"/> <generateDate date="now" format="Y-m-d" stepKey="today" after="assertDate"/> <grabTextFrom selector="{{PriceRuleConditionsSection.firstProductAttributeSelected}}" stepKey="grabTextFromSelectedAttribute" after="today"/> - <assertEquals expected="$today" actual="$grabTextFromSelectedAttribute" stepKey="assertTodayDate" after="grabTextFromSelectedAttribute"/> + <assertEquals stepKey="assertTodayDate" after="grabTextFromSelectedAttribute"> + <actualResult type="const">$grabTextFromSelectedAttribute</actualResult> + <expectedResult type="const">$today</expectedResult> + </assertEquals> </actionGroup> </actionGroups> diff --git a/app/code/Magento/CatalogRule/Test/Mftf/ActionGroup/AdminSaveAndApplyRulesActionGroup.xml b/app/code/Magento/CatalogRule/Test/Mftf/ActionGroup/AdminSaveAndApplyRulesActionGroup.xml index 82e7a6979e34b..9ad27d22caba6 100644 --- a/app/code/Magento/CatalogRule/Test/Mftf/ActionGroup/AdminSaveAndApplyRulesActionGroup.xml +++ b/app/code/Magento/CatalogRule/Test/Mftf/ActionGroup/AdminSaveAndApplyRulesActionGroup.xml @@ -10,9 +10,9 @@ xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/actionGroupSchema.xsd"> <actionGroup name="AdminSaveAndApplyRulesActionGroup"> <annotations> - <description>Clicks Save on a Admin Catalog Price Rule creation/edit page. Validates that the Success Message is present. Clicks Apply Rules. Validates that the Success Message is present.</description> + <description>DEPRECATED. Please use AdminCatalogPriceRuleSaveAndApplyActionGroup instead. Clicks Save on a Admin Catalog Price Rule creation/edit page. Validates that the Success Message is present. Clicks Apply Rules. Validates that the Success Message is present.</description> </annotations> - + <waitForPageLoad stepKey="waitForPageToLoad"/> <scrollToTopOfPage stepKey="scrollToTop"/> <click selector="{{AdminNewCatalogPriceRule.save}}" stepKey="saveTheCatalogRule"/> diff --git a/app/code/Magento/CatalogRule/Test/Mftf/ActionGroup/CatalogPriceRuleActionGroup.xml b/app/code/Magento/CatalogRule/Test/Mftf/ActionGroup/CatalogPriceRuleActionGroup.xml index e0d02a280bf6c..85961a28fac53 100644 --- a/app/code/Magento/CatalogRule/Test/Mftf/ActionGroup/CatalogPriceRuleActionGroup.xml +++ b/app/code/Magento/CatalogRule/Test/Mftf/ActionGroup/CatalogPriceRuleActionGroup.xml @@ -11,7 +11,8 @@ <!-- action group to create a new catalog price rule giving a catalogRule entity --> <actionGroup name="NewCatalogPriceRuleByUIActionGroup"> <annotations> - <description>Goes to the Catalog Price Rule grid. Clicks on Add. Fills in the provided Catalog Rule details.</description> + <description>Consider using set of AG: AdminOpenNewCatalogPriceRuleFormPageActionGroup, AdminCatalogPriceRuleFillMainInfoActionGroup, AdminCatalogPriceRuleFillActionsActionGroup, AdminCatalogPriceRuleSaveAndApplyActionGroup + Goes to the Catalog Price Rule grid. Clicks on Add. Fills in the provided Catalog Rule details.</description> </annotations> <arguments> <argument name="catalogRule" defaultValue="_defaultCatalogRule"/> diff --git a/app/code/Magento/CatalogRule/Test/Mftf/ActionGroup/CatalogSelectCustomerGroupActionGroup.xml b/app/code/Magento/CatalogRule/Test/Mftf/ActionGroup/CatalogSelectCustomerGroupActionGroup.xml index cd2f7a207a3e2..b6fc92e1a1df6 100644 --- a/app/code/Magento/CatalogRule/Test/Mftf/ActionGroup/CatalogSelectCustomerGroupActionGroup.xml +++ b/app/code/Magento/CatalogRule/Test/Mftf/ActionGroup/CatalogSelectCustomerGroupActionGroup.xml @@ -10,7 +10,7 @@ xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/actionGroupSchema.xsd"> <actionGroup name="CatalogSelectCustomerGroupActionGroup"> <annotations> - <description>Selects the provided Customer Group Name on the Admin Catalog Price Rule creation/edit page.</description> + <description>DEPRECATED. Please use AdminCatalogPriceRuleSelectCustomerGroupsActionGroup instead. Selects the provided Customer Group Name on the Admin Catalog Price Rule creation/edit page.</description> </annotations> <arguments> <argument name="customerGroupName" defaultValue="NOT LOGGED IN" type="string"/> diff --git a/app/code/Magento/CatalogRule/Test/Mftf/ActionGroup/CreateCatalogPriceRuleConditionWithAttributeAndOptionActionGroup.xml b/app/code/Magento/CatalogRule/Test/Mftf/ActionGroup/CreateCatalogPriceRuleConditionWithAttributeAndOptionActionGroup.xml index bdc09c56353df..732aee0ad63d7 100644 --- a/app/code/Magento/CatalogRule/Test/Mftf/ActionGroup/CreateCatalogPriceRuleConditionWithAttributeAndOptionActionGroup.xml +++ b/app/code/Magento/CatalogRule/Test/Mftf/ActionGroup/CreateCatalogPriceRuleConditionWithAttributeAndOptionActionGroup.xml @@ -18,8 +18,8 @@ <argument name="indexA" type="string"/> <argument name="indexB" type="string"/> </arguments> - - <click selector="{{AdminNewCatalogPriceRule.conditionsTab}}" stepKey="openConditionsTab"/> + + <conditionalClick selector="{{AdminNewCatalogPriceRule.conditionsTabTitle}}" dependentSelector="{{AdminNewCatalogPriceRule.conditionsTabBody}}" visible="false" stepKey="openConditionsTab"/> <waitForPageLoad stepKey="waitForConditionTabOpened"/> <click selector="{{AdminNewCatalogPriceRuleConditions.newCondition}}" stepKey="addNewCondition"/> <selectOption selector="{{AdminNewCatalogPriceRuleConditions.conditionSelect(indexA)}}" userInput="{{attributeName}}" stepKey="selectTypeCondition"/> diff --git a/app/code/Magento/CatalogRule/Test/Mftf/ActionGroup/NewCatalogPriceRuleByUIWithConditionIsSKUActionGroup.xml b/app/code/Magento/CatalogRule/Test/Mftf/ActionGroup/NewCatalogPriceRuleByUIWithConditionIsSKUActionGroup.xml index 9d25ffa948ad1..bd8d1d21bf694 100644 --- a/app/code/Magento/CatalogRule/Test/Mftf/ActionGroup/NewCatalogPriceRuleByUIWithConditionIsSKUActionGroup.xml +++ b/app/code/Magento/CatalogRule/Test/Mftf/ActionGroup/NewCatalogPriceRuleByUIWithConditionIsSKUActionGroup.xml @@ -10,7 +10,8 @@ xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/actionGroupSchema.xsd"> <actionGroup name="NewCatalogPriceRuleByUIWithConditionIsSKUActionGroup" extends="NewCatalogPriceRuleByUIActionGroup"> <annotations> - <description>EXTENDS: newCatalogPriceRuleByUI. Add a Catalog Price Rule Condition based on the provided SKU.</description> + <description>Consider using set of AG: AdminOpenNewCatalogPriceRuleFormPageActionGroup, AdminCatalogPriceRuleFillMainInfoActionGroup, AdminCatalogPriceRuleFillActionsActionGroup, AdminCatalogPriceRuleAddSkuConditionActionGroup, AdminCatalogPriceRuleSaveAndApplyActionGroup + EXTENDS: newCatalogPriceRuleByUI. Add a Catalog Price Rule Condition based on the provided SKU.</description> </annotations> <arguments> <argument name="productSku"/> diff --git a/app/code/Magento/CatalogRule/Test/Mftf/ActionGroup/OpenCatalogPriceRuleActionGroup.xml b/app/code/Magento/CatalogRule/Test/Mftf/ActionGroup/OpenCatalogPriceRuleActionGroup.xml index c810d9579df92..68ce445d991b3 100644 --- a/app/code/Magento/CatalogRule/Test/Mftf/ActionGroup/OpenCatalogPriceRuleActionGroup.xml +++ b/app/code/Magento/CatalogRule/Test/Mftf/ActionGroup/OpenCatalogPriceRuleActionGroup.xml @@ -13,6 +13,7 @@ <argument name="ruleName" type="string" defaultValue="CustomCatalogRule.name"/> </arguments> <amOnPage url="{{AdminCatalogPriceRuleGridPage.url}}" stepKey="goToAdminCatalogPriceRuleGridPage"/> + <waitForPageLoad stepKey="waitForAdminCatalogPriceRuleGridPageLoad"/> <conditionalClick selector="{{AdminDataGridHeaderSection.clearFilters}}" dependentSelector="{{AdminDataGridHeaderSection.clearFilters}}" visible="true" stepKey="clearExistingFilters"/> <fillField selector="{{AdminCatalogPriceRuleGridSection.filterByRuleName}}" userInput="{{ruleName}}" stepKey="filterByRuleName"/> <click selector="{{AdminDataGridHeaderSection.applyFilters}}" stepKey="clickSearch"/> diff --git a/app/code/Magento/CatalogRule/Test/Mftf/Metadata/catalog-rule-meta.xml b/app/code/Magento/CatalogRule/Test/Mftf/Metadata/CatalogRuleMeta.xml similarity index 100% rename from app/code/Magento/CatalogRule/Test/Mftf/Metadata/catalog-rule-meta.xml rename to app/code/Magento/CatalogRule/Test/Mftf/Metadata/CatalogRuleMeta.xml diff --git a/app/code/Magento/CatalogRule/Test/Mftf/Section/AdminNewCatalogPriceRuleSection.xml b/app/code/Magento/CatalogRule/Test/Mftf/Section/AdminNewCatalogPriceRuleSection.xml deleted file mode 100644 index 7d375da6dfb65..0000000000000 --- a/app/code/Magento/CatalogRule/Test/Mftf/Section/AdminNewCatalogPriceRuleSection.xml +++ /dev/null @@ -1,76 +0,0 @@ -<?xml version="1.0" encoding="UTF-8"?> -<!-- - /** - * Copyright © Magento, Inc. All rights reserved. - * See COPYING.txt for license details. - */ ---> - -<sections xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" - xsi:noNamespaceSchemaLocation="urn:magento:mftf:Page/etc/SectionObject.xsd"> - <section name="AdminNewCatalogPriceRule"> - <element name="saveAndApply" type="button" selector="#save_and_apply" timeout="30"/> - <element name="save" type="button" selector="#save" timeout="30"/> - <element name="saveAndContinue" type="button" selector="#save_and_continue" timeout="30"/> - <element name="delete" type="button" selector="#delete" timeout="30"/> - <element name="okButton" type="button" selector="//button[@class='action-primary action-accept']"/> - <element name="successMessage" type="text" selector="#messages"/> - - <element name="ruleName" type="input" selector="[name='name']"/> - <element name="ruleNameNew" type="input" selector="[name='staging[name]']"/> - <element name="description" type="textarea" selector="[name='description']"/> - <element name="status" type="select" selector="[name='is_active']"/> - <element name="isActive" type="select" selector="input[name='is_active']+label"/> - - <element name="websites" type="select" selector="[name='website_ids']"/> - <element name="active" type="checkbox" selector="//div[contains(@class, 'admin__actions-switch')]/input[@name='is_active']/../label"/> - <element name="activeIsEnabled" type="checkbox" selector="(//div[contains(@class, 'admin__actions-switch')])[1]/input[@value='1']"/> - <element name="activePosition" type="checkbox" selector="fieldset[class='admin__fieldset'] div[class*='_required']:nth-of-type(4)"/> - <element name="websitesOptions" type="select" selector="[name='website_ids'] option"/> - <element name="customerGroups" type="select" selector="[name='customer_group_ids']"/> - <element name="customerGroupsOptions" type="select" selector="[name='customer_group_ids'] option"/> - - <element name="fromDateButton" type="button" selector="[name='from_date'] + button" timeout="15"/> - <element name="startDateButton" type="button" selector="[name='staging[start_time]'] + button" timeout="15"/> - <element name="toDateButton" type="button" selector="[name='to_date'] + button" timeout="15"/> - <element name="todayDate" type="button" selector="#ui-datepicker-div [data-handler='today']"/> - <element name="priority" type="input" selector="[name='sort_order']"/> - <element name="conditionsTab" type="block" selector="[data-index='block_promo_catalog_edit_tab_conditions']"/> - <element name="actionsTab" type="block" selector="[data-index='actions']"/> - - <element name="fieldError" type="text" selector="//input[@name='{{fieldName}}']/following-sibling::label[@class='admin__field-error']" parameterized="true"/> - </section> - - <section name="AdminNewCatalogPriceRuleActions"> - <element name="apply" type="select" selector="[name='simple_action']"/> - <element name="discountAmount" type="input" selector="[name='discount_amount']"/> - <element name="disregardRules" type="select" selector="[name='stop_rules_processing']"/> - </section> - - <section name="AdminNewCatalogPriceRuleConditions"> - <element name="newCondition" type="button" selector=".rule-param.rule-param-new-child" timeout="30"/> - <element name="conditionsDropdown" type="select" selector="select[data-form-part='catalog_rule_form'][data-ui-id='newchild-0-select-rule-conditions-1-new-child']"/> - <element name="conditionSelect" type="select" selector="select#conditions__{{var}}__new_child" parameterized="true" timeout="30"/> - <element name="targetEllipsis" type="button" selector="//li[{{var}}]//a[@class='label'][text() = '...']" parameterized="true"/> - <element name="targetEllipsisValue" type="button" selector="//ul[@id='conditions__{{var}}__children']//a[contains(text(), '{{var1}}')]" parameterized="true" timeout="30"/> - <element name="ellipsisValue" type="button" selector="//ul[@id='conditions__{{var}}__children']//a[contains(text(), '...')]" parameterized="true" timeout="30"/> - <element name="targetEllipsisSelect" type="select" selector="select#conditions__{{var1}}--{{var2}}__value" parameterized="true" timeout="30"/> - <element name="targetSelect" type="select" selector="//ul[@id='conditions__{{var}}__children']//select" parameterized="true" timeout="30"/> - <element name="targetInput" type="input" selector="input#conditions__{{var1}}--{{var2}}__value" parameterized="true"/> - <element name="applyButton" type="button" selector="#conditions__{{var1}}__children li:nth-of-type({{var2}}) a.rule-param-apply" parameterized="true"/> - <element name="condition" type="text" selector="//span[@class='rule-param']/a[text()='{{condition}}']" parameterized="true"/> - <element name="activeOperatorSelect" type="select" selector=".rule-param-edit select[name*='[operator]']"/> - <element name="activeValueInput" type="input" selector=".rule-param-edit [name*='[value]']"/> - <element name="activeConditionApplyButton" type="button" selector=".rule-param-edit .rule-param-apply" timeout="30"/> - </section> - - <section name="AdminCatalogPriceRuleGrid"> - <element name="applyRules" type="button" selector="#apply_rules" timeout="30"/> - <element name="updateMessage" type="text" selector="//div[@class='message message-notice notice']//div[contains(.,'We found updated rules that are not applied. Please click')]"/> - <element name="ruleFilter" type="input" selector="//td[@data-column='name']/input[@name='name']"/> - <element name="resetFilter" type="button" selector="//button[@title='Reset Filter']" timeout="30"/> - <element name="search" type="button" selector="//div[@id='promo_catalog_grid']//button[@title='Search']" timeout="30"/> - <element name="selectRowByRuleName" type="text" selector="//tr[@data-role='row']//td[contains(.,'{{ruleName}}')]" parameterized="true"/> - <element name="firstRow" type="text" selector="//tr[@data-role='row']"/> - </section> -</sections> diff --git a/app/code/Magento/CatalogRule/Test/Mftf/Section/AdminNewCatalogPriceRuleSection/AdminCatalogPriceRuleGridSection.xml b/app/code/Magento/CatalogRule/Test/Mftf/Section/AdminNewCatalogPriceRuleSection/AdminCatalogPriceRuleGridSection.xml new file mode 100644 index 0000000000000..aeab4eb369b02 --- /dev/null +++ b/app/code/Magento/CatalogRule/Test/Mftf/Section/AdminNewCatalogPriceRuleSection/AdminCatalogPriceRuleGridSection.xml @@ -0,0 +1,19 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + /** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +--> +<sections xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Page/etc/SectionObject.xsd"> + <section name="AdminCatalogPriceRuleGrid"> + <element name="applyRules" type="button" selector="#apply_rules" timeout="30"/> + <element name="updateMessage" type="text" selector="//div[@class='message message-notice notice']//div[contains(.,'We found updated rules that are not applied. Please click')]"/> + <element name="ruleFilter" type="input" selector="//td[@data-column='name']/input[@name='name']"/> + <element name="resetFilter" type="button" selector="//button[@title='Reset Filter']" timeout="30"/> + <element name="search" type="button" selector="//div[@id='promo_catalog_grid']//button[@title='Search']" timeout="30"/> + <element name="selectRowByRuleName" type="text" selector="//tr[@data-role='row']//td[contains(.,'{{ruleName}}')]" parameterized="true"/> + <element name="firstRow" type="text" selector="//tr[@data-role='row']"/> + </section> +</sections> diff --git a/app/code/Magento/CatalogRule/Test/Mftf/Section/AdminNewCatalogPriceRuleSection/AdminNewCatalogPriceRuleActionsSection.xml b/app/code/Magento/CatalogRule/Test/Mftf/Section/AdminNewCatalogPriceRuleSection/AdminNewCatalogPriceRuleActionsSection.xml new file mode 100644 index 0000000000000..7c52080ac2704 --- /dev/null +++ b/app/code/Magento/CatalogRule/Test/Mftf/Section/AdminNewCatalogPriceRuleSection/AdminNewCatalogPriceRuleActionsSection.xml @@ -0,0 +1,15 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + /** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +--> +<sections xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Page/etc/SectionObject.xsd"> + <section name="AdminNewCatalogPriceRuleActions"> + <element name="apply" type="select" selector="[name='simple_action']"/> + <element name="discountAmount" type="input" selector="[name='discount_amount']"/> + <element name="disregardRules" type="select" selector="[name='stop_rules_processing']"/> + </section> +</sections> diff --git a/app/code/Magento/CatalogRule/Test/Mftf/Section/AdminNewCatalogPriceRuleSection/AdminNewCatalogPriceRuleConditionsSection.xml b/app/code/Magento/CatalogRule/Test/Mftf/Section/AdminNewCatalogPriceRuleSection/AdminNewCatalogPriceRuleConditionsSection.xml new file mode 100644 index 0000000000000..db064a0eedc85 --- /dev/null +++ b/app/code/Magento/CatalogRule/Test/Mftf/Section/AdminNewCatalogPriceRuleSection/AdminNewCatalogPriceRuleConditionsSection.xml @@ -0,0 +1,26 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + /** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +--> +<sections xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Page/etc/SectionObject.xsd"> + <section name="AdminNewCatalogPriceRuleConditions"> + <element name="newCondition" type="button" selector=".rule-param.rule-param-new-child" timeout="30"/> + <element name="conditionsDropdown" type="select" selector="select[data-form-part='catalog_rule_form'][data-ui-id='newchild-0-select-rule-conditions-1-new-child']"/> + <element name="conditionSelect" type="select" selector="select#conditions__{{var}}__new_child" parameterized="true" timeout="30"/> + <element name="targetEllipsis" type="button" selector="//li[{{var}}]//a[@class='label'][text() = '...']" parameterized="true"/> + <element name="targetEllipsisValue" type="button" selector="//ul[@id='conditions__{{var}}__children']//a[contains(text(), '{{var1}}')]" parameterized="true" timeout="30"/> + <element name="ellipsisValue" type="button" selector="//ul[@id='conditions__{{var}}__children']//a[contains(text(), '...')]" parameterized="true" timeout="30"/> + <element name="targetEllipsisSelect" type="select" selector="select#conditions__{{var1}}--{{var2}}__value" parameterized="true" timeout="30"/> + <element name="targetSelect" type="select" selector="//ul[@id='conditions__{{var}}__children']//select" parameterized="true" timeout="30"/> + <element name="targetInput" type="input" selector="input#conditions__{{var1}}--{{var2}}__value" parameterized="true"/> + <element name="applyButton" type="button" selector="#conditions__{{var1}}__children li:nth-of-type({{var2}}) a.rule-param-apply" parameterized="true" timeout="30"/> + <element name="condition" type="text" selector="//span[@class='rule-param']/a[text()='{{condition}}']" parameterized="true"/> + <element name="activeOperatorSelect" type="select" selector=".rule-param-edit select[name*='[operator]']"/> + <element name="activeValueInput" type="input" selector=".rule-param-edit [name*='[value]']"/> + <element name="activeConditionApplyButton" type="button" selector=".rule-param-edit .rule-param-apply" timeout="30"/> + </section> +</sections> diff --git a/app/code/Magento/CatalogRule/Test/Mftf/Section/AdminNewCatalogPriceRuleSection/AdminNewCatalogPriceRuleSection.xml b/app/code/Magento/CatalogRule/Test/Mftf/Section/AdminNewCatalogPriceRuleSection/AdminNewCatalogPriceRuleSection.xml new file mode 100644 index 0000000000000..35d99ae90a1ec --- /dev/null +++ b/app/code/Magento/CatalogRule/Test/Mftf/Section/AdminNewCatalogPriceRuleSection/AdminNewCatalogPriceRuleSection.xml @@ -0,0 +1,48 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + /** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +--> +<sections xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Page/etc/SectionObject.xsd"> + <section name="AdminNewCatalogPriceRule"> + <element name="saveAndApply" type="button" selector="#save_and_apply" timeout="30"/> + <element name="save" type="button" selector="#save" timeout="30"/> + <element name="saveAndContinue" type="button" selector="#save_and_continue" timeout="30"/> + <element name="delete" type="button" selector="#delete" timeout="30"/> + <element name="okButton" type="button" selector="//button[@class='action-primary action-accept']"/> + <element name="successMessage" type="text" selector="#messages"/> + + <element name="ruleName" type="input" selector="[name='name']"/> + <element name="ruleNameNew" type="input" selector="[name='staging[name]']"/> + <element name="description" type="textarea" selector="[name='description']"/> + <element name="status" type="select" selector="[name='is_active']"/> + <element name="isActive" type="text" selector="input[name='is_active']+label"/> + <element name="activeByStatus" type="text" selector="div.admin__actions-switch input[name='is_active'][value='{{value}}']+label" parameterized="true"/> + + <element name="websites" type="select" selector="[name='website_ids']"/> + <element name="active" type="checkbox" selector="//div[contains(@class, 'admin__actions-switch')]/input[@name='is_active']/../label"/> + <element name="activeIsEnabled" type="checkbox" selector="(//div[contains(@class, 'admin__actions-switch')])[1]/input[@value='1']"/> + <element name="activePosition" type="checkbox" selector="fieldset[class='admin__fieldset'] div[class*='_required']:nth-of-type(4)"/> + <element name="websitesOptions" type="select" selector="[name='website_ids'] option"/> + <element name="customerGroups" type="select" selector="[name='customer_group_ids']"/> + <element name="customerGroupsOptions" type="select" selector="[name='customer_group_ids'] option"/> + + <element name="fromDateButton" type="button" selector="[name='from_date'] + button" timeout="15"/> + <element name="startDateButton" type="button" selector="[name='staging[start_time]'] + button" timeout="15"/> + <element name="toDateButton" type="button" selector="[name='to_date'] + button" timeout="15"/> + <element name="todayDate" type="button" selector="#ui-datepicker-div [data-handler='today']"/> + <element name="fromDate" type="input" selector="[name='from_date']"/> + <element name="toDate" type="input" selector="[name='to_date']"/> + <element name="priority" type="input" selector="[name='sort_order']"/> + <element name="conditionsTab" type="block" selector="[data-index='block_promo_catalog_edit_tab_conditions']"/> + <element name="conditionsTabTitle" type="block" selector="[data-index='block_promo_catalog_edit_tab_conditions'] .fieldset-wrapper-title"/> + <element name="conditionsTabBody" type="block" selector="[data-index='block_promo_catalog_edit_tab_conditions'] .admin__fieldset-wrapper-content"/> + <element name="actionsTab" type="block" selector="[data-index='actions']"/> + <element name="actionsTabTitle" type="block" selector="[data-index='actions'] .fieldset-wrapper-title"/> + <element name="actionsTabBody" type="block" selector="[data-index='actions'] .admin__fieldset-wrapper-content"/> + <element name="fieldError" type="text" selector="//input[@name='{{fieldName}}']/following-sibling::label[@class='admin__field-error']" parameterized="true"/> + </section> +</sections> diff --git a/app/code/Magento/CatalogRule/Test/Mftf/Test/AdminApplyCatalogRuleByCategoryTest.xml b/app/code/Magento/CatalogRule/Test/Mftf/Test/AdminApplyCatalogRuleByCategoryTest.xml index d7d7da58c27fc..4d97338333be8 100644 --- a/app/code/Magento/CatalogRule/Test/Mftf/Test/AdminApplyCatalogRuleByCategoryTest.xml +++ b/app/code/Magento/CatalogRule/Test/Mftf/Test/AdminApplyCatalogRuleByCategoryTest.xml @@ -13,7 +13,7 @@ <stories value="Apply catalog price rule"/> <title value="Admin should be able to apply the catalog rule by category"/> <description value="Admin should be able to apply the catalog rule by category"/> - <severity value="MAJOR"/> + <severity value="BLOCKER"/> <testCaseId value="MC-74"/> <group value="CatalogRule"/> </annotations> @@ -27,7 +27,7 @@ <requiredEntity createDataKey="createCategoryTwo"/> </createData> - <actionGroup ref="LoginAsAdmin" stepKey="loginAsAdmin"/> + <actionGroup ref="AdminLoginActionGroup" stepKey="loginAsAdmin"/> </before> <after> <deleteData createDataKey="createCategoryOne" stepKey="deleteCategoryOne"/> diff --git a/app/code/Magento/CatalogRule/Test/Mftf/Test/AdminApplyCatalogRuleForConfigurableProductWithSpecialPricesTest.xml b/app/code/Magento/CatalogRule/Test/Mftf/Test/AdminApplyCatalogRuleForConfigurableProductWithSpecialPricesTest.xml index d99ebac94fbec..9cc0090fef756 100644 --- a/app/code/Magento/CatalogRule/Test/Mftf/Test/AdminApplyCatalogRuleForConfigurableProductWithSpecialPricesTest.xml +++ b/app/code/Magento/CatalogRule/Test/Mftf/Test/AdminApplyCatalogRuleForConfigurableProductWithSpecialPricesTest.xml @@ -19,7 +19,7 @@ </annotations> <before> <!-- Login as Admin --> - <actionGroup ref="LoginAsAdmin" stepKey="loginAsAdmin"/> + <actionGroup ref="AdminLoginActionGroup" stepKey="loginAsAdmin"/> <!-- Create category --> <createData entity="ApiCategory" stepKey="createCategory"/> @@ -84,7 +84,7 @@ </actionGroup> <!-- Admin logout --> - <actionGroup ref="logout" stepKey="adminLogout"/> + <actionGroup ref="AdminLogoutActionGroup" stepKey="adminLogout"/> <!-- Delete all created data --> <deleteData createDataKey="createConfigProduct" stepKey="deleteConfigProduct"/> @@ -92,10 +92,13 @@ <deleteData createDataKey="createSecondConfigChildProduct" stepKey="deleteConfigChildProduct2"/> <deleteData createDataKey="createConfigProductAttribute" stepKey="deleteConfigProductAttribute"/> <deleteData createDataKey="createCategory" stepKey="deleteCategory"/> + + <!-- Reindex invalidated indices after product attribute has been created/deleted --> + <magentoCron groups="index" stepKey="reindexInvalidatedIndices"/> </after> <!-- Add special prices for products --> - <actionGroup ref="GoToProductPageViaIDActionGroup" stepKey="goToFirstChildProduct"> + <actionGroup ref="AdminProductPageOpenByIdActionGroup" stepKey="goToFirstChildProduct"> <argument name="productId" value="$$createFirstConfigChildProduct.id$$"/> </actionGroup> <actionGroup ref="AddSpecialPriceToProductActionGroup" stepKey="addSpecialPriceForFirstProduct"> @@ -103,7 +106,7 @@ </actionGroup> <actionGroup ref="SaveProductFormActionGroup" stepKey="saveFirstProduct"/> - <actionGroup ref="GoToProductPageViaIDActionGroup" stepKey="goToSecondChildProduct"> + <actionGroup ref="AdminProductPageOpenByIdActionGroup" stepKey="goToSecondChildProduct"> <argument name="productId" value="$$createSecondConfigChildProduct.id$$"/> </actionGroup> <actionGroup ref="AddSpecialPriceToProductActionGroup" stepKey="addSpecialPriceForSecondProduct"> diff --git a/app/code/Magento/CatalogRule/Test/Mftf/Test/AdminCreateCatalogPriceRuleTest.xml b/app/code/Magento/CatalogRule/Test/Mftf/Test/AdminCreateCatalogPriceRuleTest.xml deleted file mode 100644 index 10689b415b291..0000000000000 --- a/app/code/Magento/CatalogRule/Test/Mftf/Test/AdminCreateCatalogPriceRuleTest.xml +++ /dev/null @@ -1,222 +0,0 @@ -<?xml version="1.0" encoding="UTF-8"?> -<!-- - /** - * Copyright © Magento, Inc. All rights reserved. - * See COPYING.txt for license details. - */ ---> - -<tests xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" - xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/testSchema.xsd"> - <test name="AdminCreateCatalogPriceRuleByPercentTest"> - <annotations> - <features value="CatalogRule"/> - <stories value="Create catalog price rule"/> - <title value="Admin should be able to create a catalog price rule applied as a percentage of original (for simple product)"/> - <description value="Admin should be able to create a catalog price rule applied as a percentage of original (for simple product)"/> - <severity value="MAJOR"/> - <testCaseId value="MC-65"/> - <group value="CatalogRule"/> - </annotations> - <before> - <!-- Create the simple product and category that it will be in --> - <createData entity="ApiCategory" stepKey="createCategory"/> - <createData entity="ApiSimpleProduct" stepKey="createProduct"> - <requiredEntity createDataKey="createCategory"/> - </createData> - - <!-- Perform reindex and flush cache --> - <magentoCLI command="indexer:reindex" stepKey="reindex"/> - <magentoCLI command="cache:flush" stepKey="flushCache"/> - - <!-- log in and create the price rule --> - <actionGroup ref="LoginAsAdmin" stepKey="loginAsAdmin"/> - <actionGroup stepKey="createNewPriceRule" ref="NewCatalogPriceRuleByUIActionGroup"/> - <actionGroup stepKey="selectNotLoggedInCustomerGroup" ref="SelectNotLoggedInCustomerGroupActionGroup"/> - <click stepKey="saveAndApply" selector="{{AdminNewCatalogPriceRule.saveAndApply}}"/> - <see stepKey="assertSuccess" selector="{{AdminCategoryMessagesSection.SuccessMessage}}" userInput="You saved the rule."/> - </before> - <after> - <!-- delete the simple product and catalog price rule and logout --> - <amOnPage stepKey="goToPriceRulePage" url="admin/catalog_rule/promo_catalog/"/> - <actionGroup stepKey="deletePriceRule" ref="deleteEntitySecondaryGrid"> - <argument name="name" value="{{_defaultCatalogRule.name}}"/> - <argument name="searchInput" value="{{AdminSecondaryGridSection.catalogRuleIdentifierSearch}}"/> - </actionGroup> - <amOnPage url="{{AdminLogoutPage.url}}" stepKey="amOnLogoutPage"/> - <deleteData createDataKey="createProduct" stepKey="deleteSimpleProduct"/> - <deleteData createDataKey="createCategory" stepKey="deleteCategory"/> - </after> - - <!-- Go to category page and make sure that all of the prices are correct --> - <amOnPage url="$$createCategory.name$$.html" stepKey="goToCategoryPage"/> - <waitForPageLoad stepKey="waitForCategory"/> - <see stepKey="seeOldPrice" selector="{{StorefrontCategoryProductSection.ProductOldPriceByNumber('1')}}" userInput="$$createProduct.price$$"/> - <see stepKey="seeNewPrice" selector="{{StorefrontCategoryProductSection.ProductSpecialPriceByNumber('1')}}" userInput="$110.70"/> - - <!-- Go to the simple product page and check that the prices are correct --> - <amOnPage stepKey="goToProductPage" url="$$createProduct.sku$$.html"/> - <waitForPageLoad stepKey="waitForProductPage"/> - <see stepKey="seeOldPriceTag" selector="{{StorefrontProductInfoMainSection.oldPriceTag}}" userInput="Regular Price"/> - <see stepKey="seeOldPrice2" selector="{{StorefrontProductInfoMainSection.oldPriceAmount}}" userInput="$$createProduct.price$$"/> - <see stepKey="seeNewPrice2" selector="{{StorefrontProductInfoMainSection.updatedPrice}}" userInput="$110.70"/> - - <!-- Add the product to cart and check that the price is correct there --> - <click stepKey="addToCart" selector="{{StorefrontProductActionSection.addToCart}}"/> - <waitForPageLoad stepKey="waitForAddedToCart"/> - <amOnPage url="{{CheckoutCartPage.url}}" stepKey="goToCheckout"/> - <waitForPageLoad stepKey="waitForCart"/> - <see stepKey="seeNewPriceInCart" selector="{{CheckoutCartSummarySection.subtotal}}" userInput="$110.70"/> - </test> - - <test name="AdminCreateCatalogPriceRuleByFixedTest" extends="AdminCreateCatalogPriceRuleByPercentTest"> - <annotations> - <features value="CatalogRule"/> - <stories value="Create catalog price rule"/> - <title value="Admin should be able to create a catalog price rule applied as a fixed amount (for simple product)"/> - <description value="Admin should be able to create a catalog price rule applied as a fixed amount (for simple product)"/> - <severity value="MAJOR"/> - <testCaseId value="MC-93"/> - <group value="CatalogRule"/> - </annotations> - <before> - <actionGroup stepKey="createNewPriceRule" ref="NewCatalogPriceRuleByUIActionGroup"> - <argument name="catalogRule" value="CatalogRuleByFixed"/> - </actionGroup> - </before> - <after> - <actionGroup stepKey="deletePriceRule" ref="deleteEntitySecondaryGrid"> - <argument name="name" value="{{CatalogRuleByFixed.name}}"/> - <argument name="searchInput" value="{{AdminSecondaryGridSection.catalogRuleIdentifierSearch}}"/> - </actionGroup> - </after> - </test> - - <test name="AdminCreateCatalogPriceRuleToPercentTest" extends="AdminCreateCatalogPriceRuleByPercentTest"> - <annotations> - <features value="CatalogRule"/> - <stories value="Create catalog price rule"/> - <title value="Admin should be able to create a catalog price rule adjust final price to this percentage (for simple product)"/> - <description value="Admin should be able to create a catalog price rule adjust final price to this percentage (for simple product)"/> - <severity value="MAJOR"/> - <testCaseId value="MC-69"/> - <group value="CatalogRule"/> - </annotations> - <before> - <actionGroup stepKey="createNewPriceRule" ref="NewCatalogPriceRuleByUIActionGroup"> - <argument name="catalogRule" value="CatalogRuleToPercent"/> - </actionGroup> - </before> - <after> - <actionGroup stepKey="deletePriceRule" ref="deleteEntitySecondaryGrid"> - <argument name="name" value="{{CatalogRuleToPercent.name}}"/> - <argument name="searchInput" value="{{AdminSecondaryGridSection.catalogRuleIdentifierSearch}}"/> - </actionGroup> - </after> - </test> - - <test name="AdminCreateCatalogPriceRuleToFixedTest" extends="AdminCreateCatalogPriceRuleByPercentTest"> - <annotations> - <features value="CatalogRule"/> - <stories value="Create catalog price rule"/> - <title value="Admin should be able to create a catalog price rule adjust final price to discount value (for simple product)"/> - <description value="Admin should be able to create a catalog price rule adjust final price to discount value (for simple product)"/> - <severity value="MAJOR"/> - <testCaseId value="MC-60"/> - <group value="CatalogRule"/> - </annotations> - <before> - <actionGroup stepKey="createNewPriceRule" ref="NewCatalogPriceRuleByUIActionGroup"> - <argument name="catalogRule" value="CatalogRuleToFixed"/> - </actionGroup> - </before> - <after> - <actionGroup stepKey="deletePriceRule" ref="deleteEntitySecondaryGrid"> - <argument name="name" value="{{CatalogRuleToFixed.name}}"/> - <argument name="searchInput" value="{{AdminSecondaryGridSection.catalogRuleIdentifierSearch}}"/> - </actionGroup> - </after> - </test> - - <test name="AdminCreateCatalogPriceRuleForCustomerGroupTest"> - <annotations> - <features value="CatalogRule"/> - <stories value="Apply catalog price rule"/> - <title value="Admin should be able to apply the catalog rule by customer group"/> - <description value="Admin should be able to apply the catalog rule by customer group"/> - <severity value="MAJOR"/> - <testCaseId value="MC-71"/> - <group value="CatalogRule"/> - </annotations> - <before> - <!-- Create a simple product and a category--> - <createData entity="ApiCategory" stepKey="createCategory"/> - <createData entity="ApiSimpleProduct" stepKey="createProduct"> - <requiredEntity createDataKey="createCategory"/> - </createData> - - <actionGroup ref="LoginAsAdmin" stepKey="loginAsAdmin"/> - </before> - <after> - <!-- Delete the simple product and category --> - <deleteData createDataKey="createProduct" stepKey="deleteProduct"/> - <deleteData createDataKey="createCategory" stepKey="deleteCategory"/> - <!-- Delete the catalog rule --> - <amOnPage url="{{CatalogRulePage.url}}" stepKey="goToRulePage"/> - <waitForPageLoad stepKey="waitForRulePage"/> - <actionGroup ref="deleteEntitySecondaryGrid" stepKey="deletePriceRule"> - <argument name="name" value="{{_defaultCatalogRule.name}}"/> - <argument name="searchInput" value="{{AdminSecondaryGridSection.catalogRuleIdentifierSearch}}"/> - </actionGroup> - <amOnPage url="{{AdminLogoutPage.url}}" stepKey="amOnLogoutPage"/> - </after> - - <!-- Create a catalog rule for the NOT LOGGED IN customer group --> - <actionGroup ref="NewCatalogPriceRuleByUIActionGroup" stepKey="createNewPriceRule"/> - <actionGroup ref="SelectNotLoggedInCustomerGroupActionGroup" stepKey="selectNotLoggedInCustomerGroup"/> - <click selector="{{AdminNewCatalogPriceRule.saveAndApply}}" stepKey="saveAndApply"/> - <see selector="{{AdminCategoryMessagesSection.SuccessMessage}}" userInput="You saved the rule." stepKey="assertSuccess"/> - - <!-- Perform reindex and flush cache --> - <magentoCLI command="indexer:reindex" stepKey="reindex"/> - <magentoCLI command="cache:flush" stepKey="flushCache"/> - - <!-- As a NOT LOGGED IN user, go to the storefront category page and should see the discount --> - <amOnPage url="$$createCategory.name$$.html" stepKey="goToCategory1"/> - <see selector="{{StorefrontCategoryProductSection.ProductInfoByNumber('1')}}" userInput="$$createProduct.name$$" stepKey="seeProduct1"/> - <see selector="{{StorefrontCategoryProductSection.ProductInfoByNumber('1')}}" userInput="$110.70" stepKey="seeDiscountedPrice1"/> - - <!-- Create a user account --> - <actionGroup ref="SignUpNewUserFromStorefrontActionGroup" stepKey="createAnAccount"> - <argument name="Customer" value="CustomerEntityOne"/> - </actionGroup> - - <!-- As a logged in user, go to the storefront category page and should NOT see discount --> - <amOnPage url="$$createCategory.name$$.html" stepKey="goToCategory2"/> - <see selector="{{StorefrontCategoryProductSection.ProductInfoByNumber('1')}}" userInput="$$createProduct.name$$" stepKey="seeProduct2"/> - <see selector="{{StorefrontCategoryProductSection.ProductInfoByNumber('1')}}" userInput="$123.00" stepKey="seeDiscountedPrice2"/> - </test> - - <test name="AdminCreateCatalogPriceRuleWithInvalidDataTest"> - <annotations> - <features value="CatalogRule"/> - <stories value="Create Catalog Price Rule"/> - <title value="Admin can not create catalog price rule with the invalid data"/> - <description value="Admin can not create catalog price rule with the invalid data"/> - <severity value="MAJOR"/> - <group value="CatalogRule"/> - </annotations> - <before> - <actionGroup ref="LoginAsAdmin" stepKey="loginAsAdmin"/> - </before> - <after> - <amOnPage url="{{AdminLogoutPage.url}}" stepKey="amOnLogoutPage"/> - </after> - - <actionGroup ref="NewCatalogPriceRuleWithInvalidDataActionGroup" stepKey="createNewPriceRule"> - <argument name="catalogRule" value="catalogRuleWithInvalid"/> - </actionGroup> - - <see selector="{{AdminNewCatalogPriceRule.fieldError('sort_order')}}" userInput="Please enter a valid number in this field." stepKey="seeSortOrderError"/> - </test> -</tests> diff --git a/app/code/Magento/CatalogRule/Test/Mftf/Test/AdminCreateCatalogPriceRuleTest/AdminCreateCatalogPriceRuleByFixedTest.xml b/app/code/Magento/CatalogRule/Test/Mftf/Test/AdminCreateCatalogPriceRuleTest/AdminCreateCatalogPriceRuleByFixedTest.xml new file mode 100644 index 0000000000000..3ba0f1d59f07d --- /dev/null +++ b/app/code/Magento/CatalogRule/Test/Mftf/Test/AdminCreateCatalogPriceRuleTest/AdminCreateCatalogPriceRuleByFixedTest.xml @@ -0,0 +1,32 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + /** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +--> +<tests xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/testSchema.xsd"> + <test name="AdminCreateCatalogPriceRuleByFixedTest" extends="AdminCreateCatalogPriceRuleByPercentTest"> + <annotations> + <features value="CatalogRule"/> + <stories value="Create catalog price rule"/> + <title value="Admin should be able to create a catalog price rule applied as a fixed amount (for simple product)"/> + <description value="Admin should be able to create a catalog price rule applied as a fixed amount (for simple product)"/> + <severity value="MAJOR"/> + <testCaseId value="MC-93"/> + <group value="CatalogRule"/> + </annotations> + <before> + <actionGroup stepKey="createNewPriceRule" ref="NewCatalogPriceRuleByUIActionGroup"> + <argument name="catalogRule" value="CatalogRuleByFixed"/> + </actionGroup> + </before> + <after> + <actionGroup stepKey="deletePriceRule" ref="deleteEntitySecondaryGrid"> + <argument name="name" value="{{CatalogRuleByFixed.name}}"/> + <argument name="searchInput" value="{{AdminSecondaryGridSection.catalogRuleIdentifierSearch}}"/> + </actionGroup> + </after> + </test> +</tests> diff --git a/app/code/Magento/CatalogRule/Test/Mftf/Test/AdminCreateCatalogPriceRuleTest/AdminCreateCatalogPriceRuleByPercentTest.xml b/app/code/Magento/CatalogRule/Test/Mftf/Test/AdminCreateCatalogPriceRuleTest/AdminCreateCatalogPriceRuleByPercentTest.xml new file mode 100644 index 0000000000000..8c3d1dad1b2a3 --- /dev/null +++ b/app/code/Magento/CatalogRule/Test/Mftf/Test/AdminCreateCatalogPriceRuleTest/AdminCreateCatalogPriceRuleByPercentTest.xml @@ -0,0 +1,70 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + /** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +--> +<tests xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/testSchema.xsd"> + <test name="AdminCreateCatalogPriceRuleByPercentTest"> + <annotations> + <features value="CatalogRule"/> + <stories value="Create catalog price rule"/> + <title value="Admin should be able to create a catalog price rule applied as a percentage of original (for simple product)"/> + <description value="Admin should be able to create a catalog price rule applied as a percentage of original (for simple product)"/> + <severity value="MAJOR"/> + <testCaseId value="MC-65"/> + <group value="CatalogRule"/> + </annotations> + <before> + <!-- Create the simple product and category that it will be in --> + <createData entity="ApiCategory" stepKey="createCategory"/> + <createData entity="ApiSimpleProduct" stepKey="createProduct"> + <requiredEntity createDataKey="createCategory"/> + </createData> + + <!-- Perform reindex and flush cache --> + <magentoCLI command="indexer:reindex" stepKey="reindex"/> + <magentoCLI command="cache:flush" stepKey="flushCache"/> + + <!-- log in and create the price rule --> + <actionGroup ref="AdminLoginActionGroup" stepKey="loginAsAdmin"/> + <actionGroup stepKey="createNewPriceRule" ref="NewCatalogPriceRuleByUIActionGroup"/> + <actionGroup stepKey="selectNotLoggedInCustomerGroup" ref="SelectNotLoggedInCustomerGroupActionGroup"/> + <click stepKey="saveAndApply" selector="{{AdminNewCatalogPriceRule.saveAndApply}}"/> + <see stepKey="assertSuccess" selector="{{AdminCategoryMessagesSection.SuccessMessage}}" userInput="You saved the rule."/> + </before> + <after> + <!-- delete the simple product and catalog price rule and logout --> + <amOnPage stepKey="goToPriceRulePage" url="admin/catalog_rule/promo_catalog/"/> + <actionGroup stepKey="deletePriceRule" ref="deleteEntitySecondaryGrid"> + <argument name="name" value="{{_defaultCatalogRule.name}}"/> + <argument name="searchInput" value="{{AdminSecondaryGridSection.catalogRuleIdentifierSearch}}"/> + </actionGroup> + <amOnPage url="{{AdminLogoutPage.url}}" stepKey="amOnLogoutPage"/> + <deleteData createDataKey="createProduct" stepKey="deleteSimpleProduct"/> + <deleteData createDataKey="createCategory" stepKey="deleteCategory"/> + </after> + + <!-- Go to category page and make sure that all of the prices are correct --> + <amOnPage url="$$createCategory.name$$.html" stepKey="goToCategoryPage"/> + <waitForPageLoad stepKey="waitForCategory"/> + <see stepKey="seeOldPrice" selector="{{StorefrontCategoryProductSection.ProductOldPriceByNumber('1')}}" userInput="$$createProduct.price$$"/> + <see stepKey="seeNewPrice" selector="{{StorefrontCategoryProductSection.ProductSpecialPriceByNumber('1')}}" userInput="$110.70"/> + + <!-- Go to the simple product page and check that the prices are correct --> + <amOnPage stepKey="goToProductPage" url="$$createProduct.sku$$.html"/> + <waitForPageLoad stepKey="waitForProductPage"/> + <see stepKey="seeOldPriceTag" selector="{{StorefrontProductInfoMainSection.oldPriceTag}}" userInput="Regular Price"/> + <see stepKey="seeOldPrice2" selector="{{StorefrontProductInfoMainSection.oldPriceAmount}}" userInput="$$createProduct.price$$"/> + <see stepKey="seeNewPrice2" selector="{{StorefrontProductInfoMainSection.updatedPrice}}" userInput="$110.70"/> + + <!-- Add the product to cart and check that the price is correct there --> + <click stepKey="addToCart" selector="{{StorefrontProductActionSection.addToCart}}"/> + <waitForPageLoad stepKey="waitForAddedToCart"/> + <amOnPage url="{{CheckoutCartPage.url}}" stepKey="goToCheckout"/> + <waitForPageLoad stepKey="waitForCart"/> + <see stepKey="seeNewPriceInCart" selector="{{CheckoutCartSummarySection.subtotal}}" userInput="$110.70"/> + </test> +</tests> diff --git a/app/code/Magento/CatalogRule/Test/Mftf/Test/AdminCreateCatalogPriceRuleTest/AdminCreateCatalogPriceRuleForCustomerGroupTest.xml b/app/code/Magento/CatalogRule/Test/Mftf/Test/AdminCreateCatalogPriceRuleTest/AdminCreateCatalogPriceRuleForCustomerGroupTest.xml new file mode 100644 index 0000000000000..b9318f72bee9e --- /dev/null +++ b/app/code/Magento/CatalogRule/Test/Mftf/Test/AdminCreateCatalogPriceRuleTest/AdminCreateCatalogPriceRuleForCustomerGroupTest.xml @@ -0,0 +1,74 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + /** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +--> +<tests xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/testSchema.xsd"> + <test name="AdminCreateCatalogPriceRuleForCustomerGroupTest"> + <annotations> + <features value="CatalogRule"/> + <stories value="Apply catalog price rule"/> + <title value="Admin should be able to apply the catalog rule by customer group"/> + <description value="Admin should be able to apply the catalog rule by customer group"/> + <severity value="MAJOR"/> + <testCaseId value="MC-71"/> + <group value="CatalogRule"/> + </annotations> + <before> + <!-- Create a simple product and a category--> + <createData entity="ApiCategory" stepKey="createCategory"/> + <createData entity="ApiSimpleProduct" stepKey="createProduct"> + <requiredEntity createDataKey="createCategory"/> + </createData> + + <actionGroup ref="AdminLoginActionGroup" stepKey="loginAsAdmin"/> + </before> + <after> + <!-- Delete the simple product and category --> + <deleteData createDataKey="createProduct" stepKey="deleteProduct"/> + <deleteData createDataKey="createCategory" stepKey="deleteCategory"/> + <!-- Delete the catalog rule --> + <amOnPage url="{{CatalogRulePage.url}}" stepKey="goToRulePage"/> + <waitForPageLoad stepKey="waitForRulePage"/> + <actionGroup ref="deleteEntitySecondaryGrid" stepKey="deletePriceRule"> + <argument name="name" value="{{_defaultCatalogRule.name}}"/> + <argument name="searchInput" value="{{AdminSecondaryGridSection.catalogRuleIdentifierSearch}}"/> + </actionGroup> + <amOnPage url="{{AdminLogoutPage.url}}" stepKey="amOnLogoutPage"/> + </after> + + <!-- Create a catalog rule for the NOT LOGGED IN customer group --> + <actionGroup ref="NewCatalogPriceRuleByUIActionGroup" stepKey="createNewPriceRule"/> + <actionGroup ref="SelectNotLoggedInCustomerGroupActionGroup" stepKey="selectNotLoggedInCustomerGroup"/> + <click selector="{{AdminNewCatalogPriceRule.saveAndApply}}" stepKey="saveAndApply"/> + <see selector="{{AdminCategoryMessagesSection.SuccessMessage}}" userInput="You saved the rule." stepKey="assertSuccess"/> + + <!-- Perform reindex and flush cache --> + <magentoCLI command="indexer:reindex" stepKey="reindex"/> + <magentoCLI command="cache:flush" stepKey="flushCache"/> + + <!-- As a NOT LOGGED IN user, go to the storefront category page and should see the discount --> + <amOnPage url="$$createCategory.name$$.html" stepKey="goToCategory1"/> + <see selector="{{StorefrontCategoryProductSection.ProductInfoByNumber('1')}}" userInput="$$createProduct.name$$" stepKey="seeProduct1"/> + <see selector="{{StorefrontCategoryProductSection.ProductInfoByNumber('1')}}" userInput="$110.70" stepKey="seeDiscountedPrice1"/> + + <!-- Create a user account --> + <actionGroup ref="StorefrontOpenCustomerAccountCreatePageActionGroup" stepKey="openCreateAccountPage"/> + <actionGroup ref="StorefrontFillCustomerAccountCreationFormActionGroup" stepKey="fillCreateAccountForm"> + <argument name="customer" value="CustomerEntityOne"/> + </actionGroup> + <actionGroup ref="StorefrontClickCreateAnAccountCustomerAccountCreationFormActionGroup" stepKey="submitCreateAccountForm"/> + <actionGroup ref="AssertMessageCustomerCreateAccountActionGroup" stepKey="seeSuccessMessage"> + <argument name="messageType" value="success"/> + <argument name="message" value="Thank you for registering with Main Website Store."/> + </actionGroup> + + <!-- As a logged in user, go to the storefront category page and should NOT see discount --> + <amOnPage url="$$createCategory.name$$.html" stepKey="goToCategory2"/> + <see selector="{{StorefrontCategoryProductSection.ProductInfoByNumber('1')}}" userInput="$$createProduct.name$$" stepKey="seeProduct2"/> + <see selector="{{StorefrontCategoryProductSection.ProductInfoByNumber('1')}}" userInput="$123.00" stepKey="seeDiscountedPrice2"/> + </test> +</tests> diff --git a/app/code/Magento/CatalogRule/Test/Mftf/Test/AdminCreateCatalogPriceRuleTest/AdminCreateCatalogPriceRuleToFixedTest.xml b/app/code/Magento/CatalogRule/Test/Mftf/Test/AdminCreateCatalogPriceRuleTest/AdminCreateCatalogPriceRuleToFixedTest.xml new file mode 100644 index 0000000000000..538e472812d85 --- /dev/null +++ b/app/code/Magento/CatalogRule/Test/Mftf/Test/AdminCreateCatalogPriceRuleTest/AdminCreateCatalogPriceRuleToFixedTest.xml @@ -0,0 +1,32 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + /** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +--> +<tests xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/testSchema.xsd"> + <test name="AdminCreateCatalogPriceRuleToFixedTest" extends="AdminCreateCatalogPriceRuleByPercentTest"> + <annotations> + <features value="CatalogRule"/> + <stories value="Create catalog price rule"/> + <title value="Admin should be able to create a catalog price rule adjust final price to discount value (for simple product)"/> + <description value="Admin should be able to create a catalog price rule adjust final price to discount value (for simple product)"/> + <severity value="MAJOR"/> + <testCaseId value="MC-60"/> + <group value="CatalogRule"/> + </annotations> + <before> + <actionGroup stepKey="createNewPriceRule" ref="NewCatalogPriceRuleByUIActionGroup"> + <argument name="catalogRule" value="CatalogRuleToFixed"/> + </actionGroup> + </before> + <after> + <actionGroup stepKey="deletePriceRule" ref="deleteEntitySecondaryGrid"> + <argument name="name" value="{{CatalogRuleToFixed.name}}"/> + <argument name="searchInput" value="{{AdminSecondaryGridSection.catalogRuleIdentifierSearch}}"/> + </actionGroup> + </after> + </test> +</tests> diff --git a/app/code/Magento/CatalogRule/Test/Mftf/Test/AdminCreateCatalogPriceRuleTest/AdminCreateCatalogPriceRuleToPercentTest.xml b/app/code/Magento/CatalogRule/Test/Mftf/Test/AdminCreateCatalogPriceRuleTest/AdminCreateCatalogPriceRuleToPercentTest.xml new file mode 100644 index 0000000000000..23945412693f2 --- /dev/null +++ b/app/code/Magento/CatalogRule/Test/Mftf/Test/AdminCreateCatalogPriceRuleTest/AdminCreateCatalogPriceRuleToPercentTest.xml @@ -0,0 +1,32 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + /** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +--> +<tests xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/testSchema.xsd"> + <test name="AdminCreateCatalogPriceRuleToPercentTest" extends="AdminCreateCatalogPriceRuleByPercentTest"> + <annotations> + <features value="CatalogRule"/> + <stories value="Create catalog price rule"/> + <title value="Admin should be able to create a catalog price rule adjust final price to this percentage (for simple product)"/> + <description value="Admin should be able to create a catalog price rule adjust final price to this percentage (for simple product)"/> + <severity value="MAJOR"/> + <testCaseId value="MC-69"/> + <group value="CatalogRule"/> + </annotations> + <before> + <actionGroup stepKey="createNewPriceRule" ref="NewCatalogPriceRuleByUIActionGroup"> + <argument name="catalogRule" value="CatalogRuleToPercent"/> + </actionGroup> + </before> + <after> + <actionGroup stepKey="deletePriceRule" ref="deleteEntitySecondaryGrid"> + <argument name="name" value="{{CatalogRuleToPercent.name}}"/> + <argument name="searchInput" value="{{AdminSecondaryGridSection.catalogRuleIdentifierSearch}}"/> + </actionGroup> + </after> + </test> +</tests> diff --git a/app/code/Magento/CatalogRule/Test/Mftf/Test/AdminCreateCatalogPriceRuleTest/AdminCreateCatalogPriceRuleWithInvalidDataTest.xml b/app/code/Magento/CatalogRule/Test/Mftf/Test/AdminCreateCatalogPriceRuleTest/AdminCreateCatalogPriceRuleWithInvalidDataTest.xml new file mode 100644 index 0000000000000..90a0835508b06 --- /dev/null +++ b/app/code/Magento/CatalogRule/Test/Mftf/Test/AdminCreateCatalogPriceRuleTest/AdminCreateCatalogPriceRuleWithInvalidDataTest.xml @@ -0,0 +1,32 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + /** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +--> +<tests xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/testSchema.xsd"> + <test name="AdminCreateCatalogPriceRuleWithInvalidDataTest"> + <annotations> + <features value="CatalogRule"/> + <stories value="Create Catalog Price Rule"/> + <title value="Admin can not create catalog price rule with the invalid data"/> + <description value="Admin can not create catalog price rule with the invalid data"/> + <severity value="MAJOR"/> + <group value="CatalogRule"/> + </annotations> + <before> + <actionGroup ref="AdminLoginActionGroup" stepKey="loginAsAdmin"/> + </before> + <after> + <amOnPage url="{{AdminLogoutPage.url}}" stepKey="amOnLogoutPage"/> + </after> + + <actionGroup ref="NewCatalogPriceRuleWithInvalidDataActionGroup" stepKey="createNewPriceRule"> + <argument name="catalogRule" value="catalogRuleWithInvalid"/> + </actionGroup> + + <see selector="{{AdminNewCatalogPriceRule.fieldError('sort_order')}}" userInput="Please enter a valid number in this field." stepKey="seeSortOrderError"/> + </test> +</tests> diff --git a/app/code/Magento/CatalogRule/Test/Mftf/Test/AdminCreateInactiveCatalogPriceRuleTest.xml b/app/code/Magento/CatalogRule/Test/Mftf/Test/AdminCreateInactiveCatalogPriceRuleTest.xml index 83dff1ecdcab5..1d4b21cb04a60 100644 --- a/app/code/Magento/CatalogRule/Test/Mftf/Test/AdminCreateInactiveCatalogPriceRuleTest.xml +++ b/app/code/Magento/CatalogRule/Test/Mftf/Test/AdminCreateInactiveCatalogPriceRuleTest.xml @@ -18,7 +18,7 @@ </annotations> <before> - <actionGroup ref="LoginAsAdmin" stepKey="loginToAdminPanel"/> + <actionGroup ref="AdminLoginActionGroup" stepKey="loginToAdminPanel"/> </before> <after> <actionGroup ref="AdminSearchCatalogRuleInGridActionGroup" stepKey="searchCreatedCatalogRule"> @@ -28,7 +28,7 @@ <argument name="catalogRuleName" value="{{InactiveCatalogRule.name}}"/> </actionGroup> <actionGroup ref="AdminDeleteCatalogRuleActionGroup" stepKey="deleteTheCatalogRule"/> - <actionGroup ref="logout" stepKey="logout"/> + <actionGroup ref="AdminLogoutActionGroup" stepKey="logout"/> </after> <!-- Create Inactive Catalog Price Rule --> diff --git a/app/code/Magento/CatalogRule/Test/Mftf/Test/AdminDeleteCatalogPriceRuleEntityTest.xml b/app/code/Magento/CatalogRule/Test/Mftf/Test/AdminDeleteCatalogPriceRuleEntityTest.xml deleted file mode 100644 index 02d998870fb65..0000000000000 --- a/app/code/Magento/CatalogRule/Test/Mftf/Test/AdminDeleteCatalogPriceRuleEntityTest.xml +++ /dev/null @@ -1,222 +0,0 @@ -<?xml version="1.0" encoding="UTF-8"?> -<!-- - /** - * Copyright © Magento, Inc. All rights reserved. - * See COPYING.txt for license details. - */ ---> - -<tests xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" - xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/testSchema.xsd"> - <test name="AdminDeleteCatalogPriceRuleEntityFromSimpleProductTest"> - <annotations> - <stories value="Delete Catalog Price Rule"/> - <title value="Delete Catalog Price Rule for Simple Product"/> - <description value="Assert that Catalog Price Rule is not applied for simple product."/> - <testCaseId value="MC-14073"/> - <severity value="CRITICAL"/> - <group value="CatalogRule"/> - <group value="mtf_migrated"/> - </annotations> - - <before> - <createData entity="Simple_US_Customer" stepKey="createCustomer1"/> - <createData entity="_defaultCategory" stepKey="createCategory1"/> - <createData entity="SimpleProduct" stepKey="createProduct1"> - <requiredEntity createDataKey="createCategory1"/> - </createData> - - <actionGroup ref="LoginAsAdmin" stepKey="loginAsAdmin1"/> - - <amOnPage url="{{AdminNewCatalogPriceRulePage.url}}" stepKey="openNewCatalogPriceRulePage"/> - <waitForPageLoad stepKey="waitForPageToLoad1"/> - - <actionGroup ref="CreateCatalogPriceRuleViaTheUiActionGroup" stepKey="createCatalogPriceRuleViaTheUi1"> - <argument name="catalogRule" value="DeleteActiveCatalogPriceRuleWithConditions"/> - <argument name="customerGroup" value="General"/> - <argument name="disregardRules" value="Yes"/> - </actionGroup> - - <click selector="{{AdminNewCatalogPriceRule.save}}" stepKey="saveTheCatalogRule"/> - <waitForPageLoad stepKey="waitForPageToLoad3"/> - <see selector="{{AdminNewCatalogPriceRule.successMessage}}" userInput="You saved the rule." stepKey="seeSuccessMessage"/> - </before> - <after> - <actionGroup ref="logout" stepKey="logoutOfAdmin1"/> - - <deleteData createDataKey="createCustomer1" stepKey="deleteCustomer1"/> - <deleteData createDataKey="createProduct1" stepKey="deleteSimpleProduct1"/> - <deleteData createDataKey="createCategory1" stepKey="deleteCategoryFirst1"/> - </after> - - <!-- Delete the simple product and catalog price rule --> - <amOnPage url="{{CatalogRulePage.url}}" stepKey="goToPriceRulePage1"/> - <waitForPageLoad stepKey="waitForPriceRulePage"/> - <actionGroup ref="deleteEntitySecondaryGrid" stepKey="deletePriceRule1"> - <argument name="name" value="{{DeleteActiveCatalogPriceRuleWithConditions.name}}"/> - <argument name="searchInput" value="{{AdminSecondaryGridSection.catalogRuleIdentifierSearch}}"/> - </actionGroup> - <waitForPageLoad time="30" stepKey="waitForPageLoad1"/> - - <!-- Assert that the Success message is present after the delete --> - <see selector="{{AdminMessagesSection.success}}" userInput="You deleted the rule." stepKey="seeDeletedRuleMessage1"/> - - <!-- Reindex --> - <magentoCLI command="cache:flush" stepKey="flushCache1"/> - <magentoCLI command="indexer:reindex" stepKey="reindex1"/> - - <!-- Assert that the rule isn't present on the Category page --> - <amOnPage url="$$createCategory1.name$$.html" stepKey="goToStorefrontCategoryPage1"/> - <waitForPageLoad stepKey="waitForPageLoad3"/> - <dontSee selector="{{StorefrontCategoryProductSection.ProductCatalogRulePriceTitleByName($$createProduct1.name$$)}}" userInput="Regular Price" stepKey="dontSeeRegularPriceText1"/> - <dontSeeElement selector="{{StorefrontCategoryProductSection.ProductCatalogRuleSpecialPriceTitleByName($$createProduct1.name$$)}}" stepKey="dontSeeSpecialPrice1"/> - - <!-- Assert that the rule isn't present on the Product page --> - <amOnPage url="$$createProduct1.name$$.html" stepKey="goToStorefrontProductPage1"/> - <waitForPageLoad stepKey="waitForPageLoad4"/> - <dontSee selector="{{StorefrontProductInfoMainSection.oldPriceTag}}" userInput="Regular Price" stepKey="dontSeeRegularPRiceText2"/> - <see selector="{{StorefrontProductInfoMainSection.productPrice}}" userInput="$$createProduct1.price$$" stepKey="seeTrueProductPrice1"/> - - <!-- Assert that the rule isn't present in the Shopping Cart --> - <actionGroup ref="AddToCartFromStorefrontProductPageActionGroup" stepKey="addProductToShoppingCart1"> - <argument name="productName" value="$$createProduct1.name$$"/> - </actionGroup> - <click selector="{{StorefrontMinicartSection.showCart}}" stepKey="openMiniShoppingCart1"/> - <see selector="{{StorefrontMinicartSection.productPriceByName($$createProduct1.name$$)}}" userInput="$$createProduct1.price$$" stepKey="seeCorrectProductPrice1"/> - - <!-- Assert that the rule isn't present on the Checkout page --> - <click selector="{{StorefrontMiniCartSection.goToCheckout}}" stepKey="goToCheckout1"/> - <conditionalClick selector="{{CheckoutCartSummarySection.expandShoppingCartSummary}}" dependentSelector="{{CheckoutCartSummarySection.expandShoppingCartSummary}}" visible="true" stepKey="expandShoppingCartSummary1"/> - <see selector="{{CheckoutCartProductSection.ProductRegularPriceByName($$createProduct1.name$$)}}" userInput="$$createProduct1.price$$" stepKey="seeCorrectProductPriceOnCheckout1"/> - </test> - - <test name="AdminDeleteCatalogPriceRuleEntityFromConfigurableProductTest"> - <annotations> - <stories value="Delete Catalog Price Rule"/> - <title value="Delete Catalog Price Rule for Configurable Product"/> - <description value="Assert that Catalog Price Rule is not applied for configurable product"/> - <testCaseId value="MC-14074"/> - <severity value="CRITICAL"/> - <group value="CatalogRule"/> - <group value="mtf_migrated"/> - </annotations> - - <before> - <createData entity="Simple_US_Customer" stepKey="createCustomer1"/> - <createData entity="SimpleSubCategory" stepKey="createCategory1"/> - - <!-- Create the configurable product based on the data in the /data folder --> - <createData entity="ApiConfigurableProduct" stepKey="createConfigProduct1"> - <requiredEntity createDataKey="createCategory1"/> - </createData> - - <!-- Make the configurable product have two options, that are children of the default attribute set --> - <createData entity="productAttributeWithTwoOptions" stepKey="createConfigProductAttribute1"/> - <createData entity="productAttributeOption1" stepKey="createConfigProductAttributeOption1"> - <requiredEntity createDataKey="createConfigProductAttribute1"/> - </createData> - <createData entity="productAttributeOption2" stepKey="createConfigProductAttributeOption2"> - <requiredEntity createDataKey="createConfigProductAttribute1"/> - </createData> - <createData entity="AddToDefaultSet" stepKey="createConfigAddToAttributeSet"> - <requiredEntity createDataKey="createConfigProductAttribute1"/> - </createData> - <getData entity="ProductAttributeOptionGetter" index="1" stepKey="getConfigAttributeOption1"> - <requiredEntity createDataKey="createConfigProductAttribute1"/> - </getData> - <getData entity="ProductAttributeOptionGetter" index="2" stepKey="getConfigAttributeOption2"> - <requiredEntity createDataKey="createConfigProductAttribute1"/> - </getData> - - <!-- Create the 2 children that will be a part of the configurable product --> - <createData entity="ApiSimpleOne" stepKey="createConfigChildProduct1"> - <requiredEntity createDataKey="createConfigProductAttribute1"/> - <requiredEntity createDataKey="getConfigAttributeOption1"/> - </createData> - <createData entity="ApiSimpleTwo" stepKey="createConfigChildProduct2"> - <requiredEntity createDataKey="createConfigProductAttribute1"/> - <requiredEntity createDataKey="getConfigAttributeOption2"/> - </createData> - - <!-- Assign the two products to the configurable product --> - <createData entity="ConfigurableProductTwoOptions" stepKey="createConfigProductOption1"> - <requiredEntity createDataKey="createConfigProduct1"/> - <requiredEntity createDataKey="createConfigProductAttribute1"/> - <requiredEntity createDataKey="getConfigAttributeOption1"/> - <requiredEntity createDataKey="getConfigAttributeOption2"/> - </createData> - <createData entity="ConfigurableProductAddChild" stepKey="createConfigProductAddChild1"> - <requiredEntity createDataKey="createConfigProduct1"/> - <requiredEntity createDataKey="createConfigChildProduct1"/> - </createData> - <createData entity="ConfigurableProductAddChild" stepKey="createConfigProductAddChild2"> - <requiredEntity createDataKey="createConfigProduct1"/> - <requiredEntity createDataKey="createConfigChildProduct2"/> - </createData> - - <!-- Perform reindex and flush cache --> - <magentoCLI command="indexer:reindex" stepKey="reindex"/> - <magentoCLI command="cache:flush" stepKey="flushCache"/> - - <actionGroup ref="LoginAsAdmin" stepKey="loginAsAdmin1"/> - - <amOnPage url="{{AdminNewCatalogPriceRulePage.url}}" stepKey="openNewCatalogPriceRulePage"/> - <waitForPageLoad stepKey="waitForPageToLoad1"/> - - <actionGroup ref="CreateCatalogPriceRuleViaTheUiActionGroup" stepKey="createCatalogPriceRuleViaTheUi1"> - <argument name="catalogRule" value="DeleteActiveCatalogPriceRuleWithConditions"/> - <argument name="customerGroup" value="General"/> - <argument name="disregardRules" value="Yes"/> - </actionGroup> - - <click selector="{{AdminNewCatalogPriceRule.save}}" stepKey="saveTheCatalogRule"/> - <waitForPageLoad stepKey="waitForPageToLoad3"/> - <see selector="{{AdminNewCatalogPriceRule.successMessage}}" userInput="You saved the rule." stepKey="seeSuccessMessage"/> - </before> - <after> - <actionGroup ref="logout" stepKey="logoutOfAdmin1"/> - - <deleteData createDataKey="createCustomer1" stepKey="deleteCustomer"/> - <deleteData createDataKey="createCategory1" stepKey="deleteCategory1"/> - <deleteData createDataKey="createConfigProduct1" stepKey="deleteConfigProduct1"/> - <deleteData createDataKey="createConfigChildProduct1" stepKey="deleteConfigChildProduct1"/> - <deleteData createDataKey="createConfigChildProduct2" stepKey="deleteConfigChildProduct2"/> - <deleteData createDataKey="createConfigProductAttribute1" stepKey="deleteConfigProductAttribute1"/> - </after> - - <!-- Delete the simple product and catalog price rule --> - <amOnPage url="{{CatalogRulePage.url}}" stepKey="goToPriceRulePage1"/> - <waitForPageLoad stepKey="waitForPriceRulePage"/> - <actionGroup ref="deleteEntitySecondaryGrid" stepKey="deletePriceRule1"> - <argument name="name" value="{{DeleteActiveCatalogPriceRuleWithConditions.name}}"/> - <argument name="searchInput" value="{{AdminSecondaryGridSection.catalogRuleIdentifierSearch}}"/> - </actionGroup> - <waitForPageLoad time="30" stepKey="waitForPageLoad1"/> - <see selector="{{AdminMessagesSection.success}}" userInput="You deleted the rule." stepKey="seeDeletedRuleMessage1"/> - - <!-- Reindex --> - <magentoCLI command="cache:flush" stepKey="flushCache1"/> - <magentoCLI command="indexer:reindex" stepKey="reindex1"/> - - <!-- Assert that the rule isn't present on the Category page --> - <amOnPage url="$$createCategory1.name$$.html" stepKey="goToStorefrontCategoryPage1"/> - <waitForPageLoad stepKey="waitForPageLoad2"/> - <see selector="{{StorefrontCategoryProductSection.ProductPriceByName($$createConfigProduct1.name$$)}}" userInput="$$createConfigChildProduct1.price$$" stepKey="seeRegularPriceText1"/> - - <!-- Assert that the rule isn't present on the Product page --> - <amOnPage url="{{StorefrontProductPage.url($$createConfigProduct1.custom_attributes[url_key]$$)}}" stepKey="goToStorefrontProductPage1"/> - <waitForPageLoad stepKey="waitForPageLoad3"/> - <dontSee selector="{{StorefrontProductInfoMainSection.oldPriceTag}}" userInput="Regular Price" stepKey="dontSeeRegularPriceText2"/> - <see selector="{{StorefrontProductInfoMainSection.productPrice}}" userInput="$$createConfigChildProduct1.price$$" stepKey="seeTrueProductPrice1"/> - - <!-- Assert that the rule isn't present in the Shopping Cart --> - <selectOption selector="{{StorefrontProductInfoMainSection.productAttributeOptionsSelectButton}}" userInput="option1" stepKey="selectOption1"/> - <click selector="{{StorefrontProductActionSection.addToCart}}" stepKey="addToCart1"/> - <waitForPageLoad time="30" stepKey="waitForPageLoad4"/> - <waitForElementVisible selector="{{StorefrontMessagesSection.success}}" stepKey="waitForSuccessMessage"/> - <see selector="{{StorefrontMessagesSection.success}}" userInput="You added $$createConfigProduct1.name$ to your shopping cart." stepKey="seeAddToCartSuccessMessage"/> - <click selector="{{StorefrontMinicartSection.showCart}}" stepKey="openMiniShoppingCart1"/> - <waitForPageLoad time="30" stepKey="waitForPageLoad5"/> - <see selector="{{StorefrontMinicartSection.productPriceByName($$createConfigProduct1.name$$)}}" userInput="$$createConfigProduct1.price$$" stepKey="seeCorrectProductPrice1"/> - </test> -</tests> diff --git a/app/code/Magento/CatalogRule/Test/Mftf/Test/AdminDeleteCatalogPriceRuleEntityTest/AdminDeleteCatalogPriceRuleEntityFromConfigurableProductTest.xml b/app/code/Magento/CatalogRule/Test/Mftf/Test/AdminDeleteCatalogPriceRuleEntityTest/AdminDeleteCatalogPriceRuleEntityFromConfigurableProductTest.xml new file mode 100644 index 0000000000000..6b34fd1e67e9b --- /dev/null +++ b/app/code/Magento/CatalogRule/Test/Mftf/Test/AdminDeleteCatalogPriceRuleEntityTest/AdminDeleteCatalogPriceRuleEntityFromConfigurableProductTest.xml @@ -0,0 +1,141 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + /** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +--> +<tests xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/testSchema.xsd"> + <test name="AdminDeleteCatalogPriceRuleEntityFromConfigurableProductTest"> + <annotations> + <stories value="Delete Catalog Price Rule"/> + <title value="Delete Catalog Price Rule for Configurable Product"/> + <description value="Assert that Catalog Price Rule is not applied for configurable product"/> + <testCaseId value="MC-14074"/> + <severity value="CRITICAL"/> + <group value="CatalogRule"/> + <group value="mtf_migrated"/> + </annotations> + + <before> + <createData entity="Simple_US_Customer" stepKey="createCustomer1"/> + <createData entity="SimpleSubCategory" stepKey="createCategory1"/> + + <!-- Create the configurable product based on the data in the /data folder --> + <createData entity="ApiConfigurableProduct" stepKey="createConfigProduct1"> + <requiredEntity createDataKey="createCategory1"/> + </createData> + + <!-- Make the configurable product have two options, that are children of the default attribute set --> + <createData entity="productAttributeWithTwoOptions" stepKey="createConfigProductAttribute1"/> + <createData entity="productAttributeOption1" stepKey="createConfigProductAttributeOption1"> + <requiredEntity createDataKey="createConfigProductAttribute1"/> + </createData> + <createData entity="productAttributeOption2" stepKey="createConfigProductAttributeOption2"> + <requiredEntity createDataKey="createConfigProductAttribute1"/> + </createData> + <createData entity="AddToDefaultSet" stepKey="createConfigAddToAttributeSet"> + <requiredEntity createDataKey="createConfigProductAttribute1"/> + </createData> + <getData entity="ProductAttributeOptionGetter" index="1" stepKey="getConfigAttributeOption1"> + <requiredEntity createDataKey="createConfigProductAttribute1"/> + </getData> + <getData entity="ProductAttributeOptionGetter" index="2" stepKey="getConfigAttributeOption2"> + <requiredEntity createDataKey="createConfigProductAttribute1"/> + </getData> + + <!-- Create the 2 children that will be a part of the configurable product --> + <createData entity="ApiSimpleOne" stepKey="createConfigChildProduct1"> + <requiredEntity createDataKey="createConfigProductAttribute1"/> + <requiredEntity createDataKey="getConfigAttributeOption1"/> + </createData> + <createData entity="ApiSimpleTwo" stepKey="createConfigChildProduct2"> + <requiredEntity createDataKey="createConfigProductAttribute1"/> + <requiredEntity createDataKey="getConfigAttributeOption2"/> + </createData> + + <!-- Assign the two products to the configurable product --> + <createData entity="ConfigurableProductTwoOptions" stepKey="createConfigProductOption1"> + <requiredEntity createDataKey="createConfigProduct1"/> + <requiredEntity createDataKey="createConfigProductAttribute1"/> + <requiredEntity createDataKey="getConfigAttributeOption1"/> + <requiredEntity createDataKey="getConfigAttributeOption2"/> + </createData> + <createData entity="ConfigurableProductAddChild" stepKey="createConfigProductAddChild1"> + <requiredEntity createDataKey="createConfigProduct1"/> + <requiredEntity createDataKey="createConfigChildProduct1"/> + </createData> + <createData entity="ConfigurableProductAddChild" stepKey="createConfigProductAddChild2"> + <requiredEntity createDataKey="createConfigProduct1"/> + <requiredEntity createDataKey="createConfigChildProduct2"/> + </createData> + + <!-- Perform reindex and flush cache --> + <magentoCLI command="indexer:reindex" stepKey="reindex"/> + <magentoCLI command="cache:flush" stepKey="flushCache"/> + + <actionGroup ref="AdminLoginActionGroup" stepKey="loginAsAdmin1"/> + + <amOnPage url="{{AdminNewCatalogPriceRulePage.url}}" stepKey="openNewCatalogPriceRulePage"/> + <waitForPageLoad stepKey="waitForPageToLoad1"/> + + <actionGroup ref="CreateCatalogPriceRuleViaTheUiActionGroup" stepKey="createCatalogPriceRuleViaTheUi1"> + <argument name="catalogRule" value="DeleteActiveCatalogPriceRuleWithConditions"/> + <argument name="customerGroup" value="General"/> + <argument name="disregardRules" value="Yes"/> + </actionGroup> + + <click selector="{{AdminNewCatalogPriceRule.save}}" stepKey="saveTheCatalogRule"/> + <waitForPageLoad stepKey="waitForPageToLoad3"/> + <see selector="{{AdminNewCatalogPriceRule.successMessage}}" userInput="You saved the rule." stepKey="seeSuccessMessage"/> + </before> + <after> + <actionGroup ref="AdminLogoutActionGroup" stepKey="logoutOfAdmin1"/> + + <deleteData createDataKey="createCustomer1" stepKey="deleteCustomer"/> + <deleteData createDataKey="createCategory1" stepKey="deleteCategory1"/> + <deleteData createDataKey="createConfigProduct1" stepKey="deleteConfigProduct1"/> + <deleteData createDataKey="createConfigChildProduct1" stepKey="deleteConfigChildProduct1"/> + <deleteData createDataKey="createConfigChildProduct2" stepKey="deleteConfigChildProduct2"/> + <deleteData createDataKey="createConfigProductAttribute1" stepKey="deleteConfigProductAttribute1"/> + + <!-- Reindex invalidated indices after product attribute has been created/deleted --> + <magentoCron groups="index" stepKey="reindexInvalidatedIndices"/> + </after> + + <!-- Delete the simple product and catalog price rule --> + <amOnPage url="{{CatalogRulePage.url}}" stepKey="goToPriceRulePage1"/> + <waitForPageLoad stepKey="waitForPriceRulePage"/> + <actionGroup ref="deleteEntitySecondaryGrid" stepKey="deletePriceRule1"> + <argument name="name" value="{{DeleteActiveCatalogPriceRuleWithConditions.name}}"/> + <argument name="searchInput" value="{{AdminSecondaryGridSection.catalogRuleIdentifierSearch}}"/> + </actionGroup> + <waitForPageLoad time="30" stepKey="waitForPageLoad1"/> + <see selector="{{AdminMessagesSection.success}}" userInput="You deleted the rule." stepKey="seeDeletedRuleMessage1"/> + + <!-- Reindex --> + <magentoCLI command="cache:flush" stepKey="flushCache1"/> + <magentoCLI command="indexer:reindex" stepKey="reindex1"/> + + <!-- Assert that the rule isn't present on the Category page --> + <amOnPage url="$$createCategory1.name$$.html" stepKey="goToStorefrontCategoryPage1"/> + <waitForPageLoad stepKey="waitForPageLoad2"/> + <see selector="{{StorefrontCategoryProductSection.ProductPriceByName($$createConfigProduct1.name$$)}}" userInput="$$createConfigChildProduct1.price$$" stepKey="seeRegularPriceText1"/> + + <!-- Assert that the rule isn't present on the Product page --> + <amOnPage url="{{StorefrontProductPage.url($$createConfigProduct1.custom_attributes[url_key]$$)}}" stepKey="goToStorefrontProductPage1"/> + <waitForPageLoad stepKey="waitForPageLoad3"/> + <dontSee selector="{{StorefrontProductInfoMainSection.oldPriceTag}}" userInput="Regular Price" stepKey="dontSeeRegularPriceText2"/> + <see selector="{{StorefrontProductInfoMainSection.productPrice}}" userInput="$$createConfigChildProduct1.price$$" stepKey="seeTrueProductPrice1"/> + + <!-- Assert that the rule isn't present in the Shopping Cart --> + <selectOption selector="{{StorefrontProductInfoMainSection.productAttributeOptionsSelectButton}}" userInput="option1" stepKey="selectOption1"/> + <click selector="{{StorefrontProductActionSection.addToCart}}" stepKey="addToCart1"/> + <waitForPageLoad time="30" stepKey="waitForPageLoad4"/> + <waitForElementVisible selector="{{StorefrontMessagesSection.success}}" stepKey="waitForSuccessMessage"/> + <see selector="{{StorefrontMessagesSection.success}}" userInput="You added $$createConfigProduct1.name$ to your shopping cart." stepKey="seeAddToCartSuccessMessage"/> + <actionGroup ref="StorefrontClickOnMiniCartActionGroup" stepKey="openMiniShoppingCart1"/> + <see selector="{{StorefrontMinicartSection.productPriceByName($$createConfigProduct1.name$$)}}" userInput="$$createConfigProduct1.price$$" stepKey="seeCorrectProductPrice1"/> + </test> +</tests> diff --git a/app/code/Magento/CatalogRule/Test/Mftf/Test/AdminDeleteCatalogPriceRuleEntityTest/AdminDeleteCatalogPriceRuleEntityFromSimpleProductTest.xml b/app/code/Magento/CatalogRule/Test/Mftf/Test/AdminDeleteCatalogPriceRuleEntityTest/AdminDeleteCatalogPriceRuleEntityFromSimpleProductTest.xml new file mode 100644 index 0000000000000..49a9d5e8ae51b --- /dev/null +++ b/app/code/Magento/CatalogRule/Test/Mftf/Test/AdminDeleteCatalogPriceRuleEntityTest/AdminDeleteCatalogPriceRuleEntityFromSimpleProductTest.xml @@ -0,0 +1,91 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + /** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +--> +<tests xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/testSchema.xsd"> + <test name="AdminDeleteCatalogPriceRuleEntityFromSimpleProductTest"> + <annotations> + <stories value="Delete Catalog Price Rule"/> + <title value="Delete Catalog Price Rule for Simple Product"/> + <description value="Assert that Catalog Price Rule is not applied for simple product."/> + <testCaseId value="MC-14073"/> + <severity value="CRITICAL"/> + <group value="CatalogRule"/> + <group value="mtf_migrated"/> + </annotations> + + <before> + <createData entity="Simple_US_Customer" stepKey="createCustomer1"/> + <createData entity="_defaultCategory" stepKey="createCategory1"/> + <createData entity="SimpleProduct" stepKey="createProduct1"> + <requiredEntity createDataKey="createCategory1"/> + </createData> + + <actionGroup ref="AdminLoginActionGroup" stepKey="loginAsAdmin1"/> + + <amOnPage url="{{AdminNewCatalogPriceRulePage.url}}" stepKey="openNewCatalogPriceRulePage"/> + <waitForPageLoad stepKey="waitForPageToLoad1"/> + + <actionGroup ref="CreateCatalogPriceRuleViaTheUiActionGroup" stepKey="createCatalogPriceRuleViaTheUi1"> + <argument name="catalogRule" value="DeleteActiveCatalogPriceRuleWithConditions"/> + <argument name="customerGroup" value="General"/> + <argument name="disregardRules" value="Yes"/> + </actionGroup> + + <click selector="{{AdminNewCatalogPriceRule.save}}" stepKey="saveTheCatalogRule"/> + <waitForPageLoad stepKey="waitForPageToLoad3"/> + <see selector="{{AdminNewCatalogPriceRule.successMessage}}" userInput="You saved the rule." stepKey="seeSuccessMessage"/> + </before> + <after> + <actionGroup ref="AdminLogoutActionGroup" stepKey="logoutOfAdmin1"/> + + <deleteData createDataKey="createCustomer1" stepKey="deleteCustomer1"/> + <deleteData createDataKey="createProduct1" stepKey="deleteSimpleProduct1"/> + <deleteData createDataKey="createCategory1" stepKey="deleteCategoryFirst1"/> + </after> + + <!-- Delete the simple product and catalog price rule --> + <amOnPage url="{{CatalogRulePage.url}}" stepKey="goToPriceRulePage1"/> + <waitForPageLoad stepKey="waitForPriceRulePage"/> + <actionGroup ref="deleteEntitySecondaryGrid" stepKey="deletePriceRule1"> + <argument name="name" value="{{DeleteActiveCatalogPriceRuleWithConditions.name}}"/> + <argument name="searchInput" value="{{AdminSecondaryGridSection.catalogRuleIdentifierSearch}}"/> + </actionGroup> + <waitForPageLoad time="30" stepKey="waitForPageLoad1"/> + + <!-- Assert that the Success message is present after the delete --> + <see selector="{{AdminMessagesSection.success}}" userInput="You deleted the rule." stepKey="seeDeletedRuleMessage1"/> + + <!-- Reindex --> + <magentoCLI command="cache:flush" stepKey="flushCache1"/> + <magentoCLI command="indexer:reindex" stepKey="reindex1"/> + + <!-- Assert that the rule isn't present on the Category page --> + <amOnPage url="$$createCategory1.name$$.html" stepKey="goToStorefrontCategoryPage1"/> + <waitForPageLoad stepKey="waitForPageLoad3"/> + <dontSee selector="{{StorefrontCategoryProductSection.ProductCatalogRulePriceTitleByName($$createProduct1.name$$)}}" userInput="Regular Price" stepKey="dontSeeRegularPriceText1"/> + <dontSeeElement selector="{{StorefrontCategoryProductSection.ProductCatalogRuleSpecialPriceTitleByName($$createProduct1.name$$)}}" stepKey="dontSeeSpecialPrice1"/> + + <!-- Assert that the rule isn't present on the Product page --> + <amOnPage url="$$createProduct1.name$$.html" stepKey="goToStorefrontProductPage1"/> + <waitForPageLoad stepKey="waitForPageLoad4"/> + <dontSee selector="{{StorefrontProductInfoMainSection.oldPriceTag}}" userInput="Regular Price" stepKey="dontSeeRegularPRiceText2"/> + <see selector="{{StorefrontProductInfoMainSection.productPrice}}" userInput="$$createProduct1.price$$" stepKey="seeTrueProductPrice1"/> + + <!-- Assert that the rule isn't present in the Shopping Cart --> + <actionGroup ref="AddToCartFromStorefrontProductPageActionGroup" stepKey="addProductToShoppingCart1"> + <argument name="productName" value="$$createProduct1.name$$"/> + </actionGroup> + <actionGroup ref="StorefrontClickOnMiniCartActionGroup" stepKey="openMiniShoppingCart1"/> + <see selector="{{StorefrontMinicartSection.productPriceByName($$createProduct1.name$$)}}" userInput="$$createProduct1.price$$" stepKey="seeCorrectProductPrice1"/> + + <!-- Assert that the rule isn't present on the Checkout page --> + <click selector="{{StorefrontMiniCartSection.goToCheckout}}" stepKey="goToCheckout1"/> + <conditionalClick selector="{{CheckoutCartSummarySection.expandShoppingCartSummary}}" dependentSelector="{{CheckoutCartSummarySection.expandShoppingCartSummary}}" visible="true" stepKey="expandShoppingCartSummary1"/> + <see selector="{{CheckoutCartProductSection.ProductRegularPriceByName($$createProduct1.name$$)}}" userInput="$$createProduct1.price$$" stepKey="seeCorrectProductPriceOnCheckout1"/> + </test> +</tests> diff --git a/app/code/Magento/CatalogRule/Test/Mftf/Test/AdminDeleteCatalogPriceRuleTest.xml b/app/code/Magento/CatalogRule/Test/Mftf/Test/AdminDeleteCatalogPriceRuleTest.xml index a6eb6eb1e5178..c6b3569f3b597 100644 --- a/app/code/Magento/CatalogRule/Test/Mftf/Test/AdminDeleteCatalogPriceRuleTest.xml +++ b/app/code/Magento/CatalogRule/Test/Mftf/Test/AdminDeleteCatalogPriceRuleTest.xml @@ -26,7 +26,7 @@ <requiredEntity createDataKey="createCategory"/> </createData> <!-- Login to Admin page --> - <actionGroup ref="LoginAsAdmin" stepKey="loginAsAdmin"/> + <actionGroup ref="AdminLoginActionGroup" stepKey="loginAsAdmin"/> <!-- Create a configurable product --> <actionGroup ref="CreateConfigurableProductActionGroup" stepKey="createConfigurableProduct"> <argument name="product" value="_defaultProduct"/> @@ -43,7 +43,7 @@ <amOnPage url="{{AdminCatalogPriceRuleGridPage.url}}" stepKey="goToCatalogRuleGridPage"/> <waitForPageLoad stepKey="waitForCatalogRuleGridPageLoaded"/> <actionGroup ref="ClearFiltersAdminDataGridActionGroup" stepKey="clearCatalogRuleGridFilters"/> - <actionGroup ref="logout" stepKey="amOnLogoutPage"/> + <actionGroup ref="AdminLogoutActionGroup" stepKey="amOnLogoutPage"/> </after> <!-- Create a catalog price rule --> diff --git a/app/code/Magento/CatalogRule/Test/Mftf/Test/AdminEnableAttributeIsUndefinedCatalogPriceRuleTest.xml b/app/code/Magento/CatalogRule/Test/Mftf/Test/AdminEnableAttributeIsUndefinedCatalogPriceRuleTest.xml index da855b08cf390..973d73da2ab6a 100644 --- a/app/code/Magento/CatalogRule/Test/Mftf/Test/AdminEnableAttributeIsUndefinedCatalogPriceRuleTest.xml +++ b/app/code/Magento/CatalogRule/Test/Mftf/Test/AdminEnableAttributeIsUndefinedCatalogPriceRuleTest.xml @@ -20,7 +20,7 @@ <group value="CatalogRule"/> </annotations> <before> - <actionGroup ref="LoginAsAdmin" stepKey="login"/> + <actionGroup ref="AdminLoginActionGroup" stepKey="login"/> <createData entity="ApiCategory" stepKey="createFirstCategory"/> <createData entity="ApiSimpleProduct" stepKey="createFirstProduct"> @@ -62,7 +62,7 @@ <deleteData createDataKey="createSecondCategory" stepKey="deleteSecondCategory"/> <deleteData createDataKey="createSecondProductAttribute" stepKey="deleteSecondProductAttribute"/> - <actionGroup ref="logout" stepKey="logout"/> + <actionGroup ref="AdminLogoutActionGroup" stepKey="logout"/> <magentoCLI command="indexer:reindex" stepKey="reindex"/> <magentoCLI command="cache:flush" stepKey="flushCache"/> </after> @@ -89,13 +89,19 @@ <amOnPage url="{{StorefrontProductPage.url($$createFirstProduct.custom_attributes[url_key]$$)}}" stepKey="navigateToFirstProductPage"/> <waitForPageLoad stepKey="waitForFirstProductPageLoad"/> <grabTextFrom selector="{{StorefrontProductInfoMainSection.updatedPrice}}" stepKey="grabFirstProductUpdatedPrice"/> - <assertEquals expected='$110.70' expectedType="string" actual="($grabFirstProductUpdatedPrice)" stepKey="assertFirstProductUpdatedPrice"/> + <assertEquals stepKey="assertFirstProductUpdatedPrice"> + <actualResult type="const">($grabFirstProductUpdatedPrice)</actualResult> + <expectedResult type="string">$110.70</expectedResult> + </assertEquals> <!--Check Catalog Price Rule for second product--> <amOnPage url="{{StorefrontProductPage.url($$createSecondProduct.custom_attributes[url_key]$$)}}" stepKey="navigateToSecondProductPage"/> <waitForPageLoad stepKey="waitForSecondProductPageLoad"/> <grabTextFrom selector="{{StorefrontProductInfoMainSection.updatedPrice}}" stepKey="grabSecondProductUpdatedPrice"/> - <assertEquals expected='$110.70' expectedType="string" actual="($grabFirstProductUpdatedPrice)" stepKey="assertSecondProductUpdatedPrice"/> + <assertEquals stepKey="assertSecondProductUpdatedPrice"> + <actualResult type="const">($grabFirstProductUpdatedPrice)</actualResult> + <expectedResult type="string">$110.70</expectedResult> + </assertEquals> <!--Delete previous attribute and Catalog Price Rule--> <deleteData createDataKey="createProductAttribute" stepKey="deleteProductAttribute"/> @@ -132,12 +138,18 @@ <amOnPage url="{{StorefrontProductPage.url($$createThirdProduct.custom_attributes[url_key]$$)}}" stepKey="navigateToThirdProductPage"/> <waitForPageLoad stepKey="waitForThirdProductPageLoad"/> <grabTextFrom selector="{{StorefrontProductInfoMainSection.updatedPrice}}" stepKey="grabThirdProductUpdatedPrice"/> - <assertEquals expected='$110.70' expectedType="string" actual="($grabThirdProductUpdatedPrice)" stepKey="assertThirdProductUpdatedPrice"/> + <assertEquals stepKey="assertThirdProductUpdatedPrice"> + <actualResult type="const">($grabThirdProductUpdatedPrice)</actualResult> + <expectedResult type="string">$110.70</expectedResult> + </assertEquals> <!--Check Catalog Price Rule for forth product--> <amOnPage url="{{StorefrontProductPage.url($$createForthProduct.custom_attributes[url_key]$$)}}" stepKey="navigateToForthProductPage"/> <waitForPageLoad stepKey="waitForForthProductPageLoad"/> <grabTextFrom selector="{{StorefrontProductInfoMainSection.updatedPrice}}" stepKey="grabForthProductUpdatedPrice"/> - <assertEquals expected='$110.70' expectedType="string" actual="($grabForthProductUpdatedPrice)" stepKey="assertForthProductUpdatedPrice"/> + <assertEquals stepKey="assertForthProductUpdatedPrice"> + <actualResult type="const">($grabForthProductUpdatedPrice)</actualResult> + <expectedResult type="string">$110.70</expectedResult> + </assertEquals> </test> </tests> diff --git a/app/code/Magento/CatalogRule/Test/Mftf/Test/AdminMarketingCatalogPriceRuleNavigateMenuTest.xml b/app/code/Magento/CatalogRule/Test/Mftf/Test/AdminMarketingCatalogPriceRuleNavigateMenuTest.xml index 0fe35419aaf3e..60b16276f6c17 100644 --- a/app/code/Magento/CatalogRule/Test/Mftf/Test/AdminMarketingCatalogPriceRuleNavigateMenuTest.xml +++ b/app/code/Magento/CatalogRule/Test/Mftf/Test/AdminMarketingCatalogPriceRuleNavigateMenuTest.xml @@ -20,10 +20,10 @@ <group value="mtf_migrated"/> </annotations> <before> - <actionGroup ref="LoginAsAdmin" stepKey="LoginAsAdmin"/> + <actionGroup ref="AdminLoginActionGroup" stepKey="LoginAsAdmin"/> </before> <after> - <actionGroup ref="logout" stepKey="logout"/> + <actionGroup ref="AdminLogoutActionGroup" stepKey="logout"/> </after> <actionGroup ref="AdminNavigateMenuActionGroup" stepKey="navigateToCatalogPriceRulePage"> <argument name="menuUiId" value="{{AdminMenuMarketing.dataUiId}}"/> diff --git a/app/code/Magento/CatalogRule/Test/Mftf/Test/ApplyCatalogPriceRuleByProductAttributeTest.xml b/app/code/Magento/CatalogRule/Test/Mftf/Test/ApplyCatalogPriceRuleByProductAttributeTest.xml index 1615985370182..0747d42dc8737 100644 --- a/app/code/Magento/CatalogRule/Test/Mftf/Test/ApplyCatalogPriceRuleByProductAttributeTest.xml +++ b/app/code/Magento/CatalogRule/Test/Mftf/Test/ApplyCatalogPriceRuleByProductAttributeTest.xml @@ -21,7 +21,7 @@ </skip> </annotations> <before> - <actionGroup ref="LoginAsAdmin" stepKey="login"/> + <actionGroup ref="AdminLoginActionGroup" stepKey="login"/> <createData entity="ApiCategory" stepKey="createFirstCategory"/> <createData entity="ApiSimpleProduct" stepKey="createFirstProduct"> @@ -104,7 +104,10 @@ </actionGroup> <click stepKey="resetFilters" selector="{{AdminSecondaryGridSection.resetFilters}}"/> - <actionGroup ref="logout" stepKey="logout"/> + <actionGroup ref="AdminLogoutActionGroup" stepKey="logout"/> + + <!-- Reindex invalidated indices after product attribute has been created/deleted --> + <magentoCron groups="index" stepKey="reindexInvalidatedIndices"/> </after> <!-- Add values to your attribute ( ex: red , green) --> diff --git a/app/code/Magento/CatalogRule/Test/Mftf/Test/ApplyCatalogRuleForSimpleAndConfigurableProductTest.xml b/app/code/Magento/CatalogRule/Test/Mftf/Test/ApplyCatalogRuleForSimpleAndConfigurableProductTest.xml index 2f12df8b75fc6..101883f5ea9f2 100644 --- a/app/code/Magento/CatalogRule/Test/Mftf/Test/ApplyCatalogRuleForSimpleAndConfigurableProductTest.xml +++ b/app/code/Magento/CatalogRule/Test/Mftf/Test/ApplyCatalogRuleForSimpleAndConfigurableProductTest.xml @@ -17,13 +17,10 @@ <testCaseId value="MC-14770"/> <group value="CatalogRule"/> <group value="mtf_migrated"/> - <skip> - <issueId value="MC-17140"/> - </skip> </annotations> <before> <!-- Login as Admin --> - <actionGroup ref="LoginAsAdmin" stepKey="loginAsAdmin"/> + <actionGroup ref="AdminLoginActionGroup" stepKey="loginAsAdmin"/> <!-- Create Simple Product --> <createData entity="ApiCategory" stepKey="createCategory"/> @@ -90,7 +87,7 @@ </actionGroup> <!-- Logout --> - <actionGroup ref="logout" stepKey="logout"/> + <actionGroup ref="AdminLogoutActionGroup" stepKey="logout"/> <!-- Delete products and category --> <deleteData createDataKey="createSimpleProduct" stepKey="deleteSimpleProduct"/> @@ -99,6 +96,9 @@ <deleteData createDataKey="createConfigChildProduct2" stepKey="deleteConfigChildProduct2"/> <deleteData createDataKey="createConfigProductAttribute" stepKey="deleteConfigProductAttribute"/> <deleteData createDataKey="createCategory" stepKey="deleteApiCategory"/> + + <!-- Reindex invalidated indices after product attribute has been created/deleted --> + <magentoCron groups="index" stepKey="reindexInvalidatedIndices"/> </after> <!-- Begin creating a new catalog price rule --> <actionGroup ref="NewCatalogPriceRuleByUIWithConditionIsCategoryActionGroup" stepKey="newCatalogPriceRuleByUIWithConditionIsCategory"> @@ -117,6 +117,11 @@ <!-- Navigate to category on store front --> <amOnPage url="{{StorefrontProductPage.url($createCategory.name$)}}" stepKey="goToCategoryPage"/> + <!-- Sort products By Price --> + <actionGroup ref="StorefrontCategoryPageSortProductActionGroup" stepKey="sortProductByPrice"/> + <!-- Set Ascending Direction --> + <actionGroup ref="StorefrontCategoryPageSortAscendingActionGroup" stepKey="setAscendingDirection"/> + <!-- Check simple product name on store front category page --> <actionGroup ref="AssertProductDetailsOnStorefrontActionGroup" stepKey="storefrontProduct1Name"> <argument name="productInfo" value="$createSimpleProduct.name$"/> diff --git a/app/code/Magento/CatalogRule/Test/Mftf/Test/ApplyCatalogRuleForSimpleProductAndFixedMethodTest.xml b/app/code/Magento/CatalogRule/Test/Mftf/Test/ApplyCatalogRuleForSimpleProductAndFixedMethodTest.xml index 956a2e07fc6b4..552a6dc48eb7f 100644 --- a/app/code/Magento/CatalogRule/Test/Mftf/Test/ApplyCatalogRuleForSimpleProductAndFixedMethodTest.xml +++ b/app/code/Magento/CatalogRule/Test/Mftf/Test/ApplyCatalogRuleForSimpleProductAndFixedMethodTest.xml @@ -17,13 +17,10 @@ <testCaseId value="MC-14771"/> <group value="CatalogRule"/> <group value="mtf_migrated"/> - <skip> - <issueId value="MC-17140"/> - </skip> </annotations> <before> <!-- Login as Admin --> - <actionGroup ref="LoginAsAdmin" stepKey="loginAsAdmin"/> + <actionGroup ref="AdminLoginActionGroup" stepKey="loginAsAdmin"/> <!-- Create category --> <createData entity="_defaultCategory" stepKey="createCategory"/> @@ -50,7 +47,7 @@ </actionGroup> <!-- Logout --> - <actionGroup ref="logout" stepKey="logout"/> + <actionGroup ref="AdminLogoutActionGroup" stepKey="logout"/> </after> <!-- 1. Begin creating a new catalog price rule --> <actionGroup ref="NewCatalogPriceRuleByUIWithConditionIsCategoryActionGroup" stepKey="newCatalogPriceRuleByUIWithConditionIsCategory"> diff --git a/app/code/Magento/CatalogRule/Test/Mftf/Test/ApplyCatalogRuleForSimpleProductForNewCustomerGroupTest.xml b/app/code/Magento/CatalogRule/Test/Mftf/Test/ApplyCatalogRuleForSimpleProductForNewCustomerGroupTest.xml index e5e3cf9ead5e5..6e8cb01f64ea2 100644 --- a/app/code/Magento/CatalogRule/Test/Mftf/Test/ApplyCatalogRuleForSimpleProductForNewCustomerGroupTest.xml +++ b/app/code/Magento/CatalogRule/Test/Mftf/Test/ApplyCatalogRuleForSimpleProductForNewCustomerGroupTest.xml @@ -17,13 +17,10 @@ <testCaseId value="MC-14772"/> <group value="CatalogRule"/> <group value="mtf_migrated"/> - <skip> - <issueId value="MC-17140"/> - </skip> </annotations> <before> <!-- Login as Admin --> - <actionGroup ref="LoginAsAdmin" stepKey="loginAsAdmin"/> + <actionGroup ref="AdminLoginActionGroup" stepKey="loginAsAdmin"/> <!-- Create new customer group --> <createData entity="CustomCustomerGroup" stepKey="customerGroup" /> @@ -61,7 +58,7 @@ </actionGroup> <!-- Logout --> - <actionGroup ref="logout" stepKey="logout"/> + <actionGroup ref="AdminLogoutActionGroup" stepKey="logout"/> </after> <!-- 1. Begin creating a new catalog price rule --> <actionGroup ref="NewCatalogPriceRuleByUIWithConditionIsCategoryActionGroup" stepKey="newCatalogPriceRuleByUIWithConditionIsCategory"> diff --git a/app/code/Magento/CatalogRule/Test/Mftf/Test/ApplyCatalogRuleForSimpleProductWithCustomOptionsTest.xml b/app/code/Magento/CatalogRule/Test/Mftf/Test/ApplyCatalogRuleForSimpleProductWithCustomOptionsTest.xml index 166d202ec2410..aa20a1d72d063 100644 --- a/app/code/Magento/CatalogRule/Test/Mftf/Test/ApplyCatalogRuleForSimpleProductWithCustomOptionsTest.xml +++ b/app/code/Magento/CatalogRule/Test/Mftf/Test/ApplyCatalogRuleForSimpleProductWithCustomOptionsTest.xml @@ -20,7 +20,7 @@ </annotations> <before> <!-- Login as Admin --> - <actionGroup ref="LoginAsAdmin" stepKey="loginAsAdmin"/> + <actionGroup ref="AdminLoginActionGroup" stepKey="loginAsAdmin"/> <!-- Create category --> <createData entity="_defaultCategory" stepKey="createCategory"/> @@ -63,7 +63,7 @@ </actionGroup> <!-- Logout --> - <actionGroup ref="logout" stepKey="logout"/> + <actionGroup ref="AdminLogoutActionGroup" stepKey="logout"/> </after> <!-- 1. Begin creating a new catalog price rule --> <actionGroup ref="NewCatalogPriceRuleByUIWithConditionIsCategoryActionGroup" stepKey="newCatalogPriceRuleByUIWithConditionIsCategory"> diff --git a/app/code/Magento/CatalogRule/Test/Mftf/Test/CatalogPriceRuleAndCustomerGroupMembershipArePersistedUnderLongTermCookieTest.xml b/app/code/Magento/CatalogRule/Test/Mftf/Test/CatalogPriceRuleAndCustomerGroupMembershipArePersistedUnderLongTermCookieTest.xml index 86d3dccba7595..6ac9f713e2844 100644 --- a/app/code/Magento/CatalogRule/Test/Mftf/Test/CatalogPriceRuleAndCustomerGroupMembershipArePersistedUnderLongTermCookieTest.xml +++ b/app/code/Magento/CatalogRule/Test/Mftf/Test/CatalogPriceRuleAndCustomerGroupMembershipArePersistedUnderLongTermCookieTest.xml @@ -15,71 +15,87 @@ <title value="Verify that Catalog Price Rule and Customer Group Membership are persisted under long-term cookie"/> <description value="Verify that Catalog Price Rule and Customer Group Membership are persisted under long-term cookie"/> <severity value="CRITICAL"/> - <testCaseId value="MAGETWO-69455"/> + <testCaseId value="MC-27571"/> <group value="persistent"/> </annotations> <before> - <createData entity="PersistentConfigEnabled" stepKey="enablePersistent"/> - <createData entity="PersistentLogoutClearDisable" stepKey="persistentLogoutClearDisable"/> + + <createData entity="PersistentConfigSettings" stepKey="enablePersistent"/> <createData entity="_defaultCategory" stepKey="createCategory"/> - <createData entity="_defaultProduct" stepKey="createProduct"> + <createData entity="productWithHTMLEntityOne" stepKey="createProduct"> <requiredEntity createDataKey="createCategory"/> - <field key="price">50</field> - </createData> - <createData entity="Simple_US_Customer" stepKey="createCustomer"> - <field key="group_id">1</field> </createData> + <createData entity="Simple_US_Customer" stepKey="createCustomer"/> + + <actionGroup ref="AdminLoginActionGroup" stepKey="loginToAdminPanel"/> - <actionGroup ref="LoginAsAdmin" stepKey="login"/> <!--Create Catalog Rule--> - <actionGroup ref="NewCatalogPriceRuleByUIWithConditionIsCategoryActionGroup" stepKey="createCatalogPriceRule"> - <argument name="catalogRule" value="_defaultCatalogRule"/> - <argument name="categoryId" value="$$createCategory.id$$"/> + <actionGroup ref="AdminOpenNewCatalogPriceRuleFormPageActionGroup" stepKey="startCreatingFirstPriceRule"/> + <actionGroup ref="AdminCatalogPriceRuleFillMainInfoActionGroup" stepKey="fillMainInfoForFirstPriceRule"> + <argument name="groups" value="'General'"/> </actionGroup> - <actionGroup ref="SelectGeneralCustomerGroupActionGroup" stepKey="selectCustomerGroup"/> - <click selector="{{AdminNewCatalogPriceRule.saveAndApply}}" stepKey="clickSaveAndApplyRules"/> - <see selector="{{AdminCategoryMessagesSection.SuccessMessage}}" userInput="You saved the rule." stepKey="assertSuccess"/> - <magentoCLI command="indexer:reindex" stepKey="reindex"/> - <magentoCLI command="cache:flush" stepKey="flushCache"/> + <actionGroup ref="AdminFillCatalogRuleConditionActionGroup" stepKey="createCatalogPriceRule"> + <argument name="conditionValue" value="$createCategory.id$"/> + </actionGroup> + <actionGroup ref="AdminCatalogPriceRuleFillActionsActionGroup" stepKey="fillActionsForThirdPriceRule"/> + <actionGroup ref="AdminCatalogPriceRuleSaveAndApplyActionGroup" stepKey="clickSaveAndApplyRule"/> + + <!-- Perform reindex --> + <magentoCLI command="indexer:reindex" arguments="catalogrule_rule" stepKey="reindex"/> </before> <after> - <!-- Delete the rule --> - <amOnPage url="{{CatalogRulePage.url}}" stepKey="goToCatalogPriceRulePage"/> - <actionGroup ref="deleteEntitySecondaryGrid" stepKey="deletePriceRule"> - <argument name="name" value="{{_defaultCatalogRule.name}}"/> - <argument name="searchInput" value="{{AdminSecondaryGridSection.catalogRuleIdentifierSearch}}"/> - </actionGroup> - <actionGroup ref="logout" stepKey="logout"/> <createData entity="PersistentConfigDefault" stepKey="setDefaultPersistentState"/> <createData entity="PersistentLogoutClearEnabled" stepKey="persistentLogoutClearEnabled"/> <deleteData createDataKey="createCategory" stepKey="deleteCategory"/> <deleteData createDataKey="createProduct" stepKey="deleteProduct"/> <deleteData createDataKey="createCustomer" stepKey="deleteCustomer"/> + <!-- Delete the rule --> + <actionGroup ref="RemoveCatalogPriceRuleActionGroup" stepKey="deleteCatalogPriceRule"> + <argument name="ruleName" value="{{_defaultCatalogRule.name}}"/> + </actionGroup> + <actionGroup ref="AdminLogoutActionGroup" stepKey="logout"/> </after> <!--Go to category and check price--> - <amOnPage url="{{StorefrontCategoryPage.url($$createCategory.name$$)}}" stepKey="onStorefrontCategoryPage"/> - <see selector="{{StorefrontCategoryProductSection.ProductPriceByNumber('1')}}" userInput="$$createProduct.price$$" stepKey="checkPriceSimpleProduct"/> + <actionGroup ref="AssertStorefrontProductPriceInCategoryPageActionGroup" stepKey="assertProductPriceInCategoryPage"> + <argument name="categoryUrl" value="$createCategory.custom_attributes[url_key]$"/> + <argument name="productName" value="$createProduct.name$"/> + </actionGroup> <!--Login to storefront from customer and check price--> <actionGroup ref="LoginToStorefrontActionGroup" stepKey="logInFromCustomer"> - <argument name="Customer" value="$$createCustomer$$"/> + <argument name="Customer" value="$createCustomer$"/> + </actionGroup> + <actionGroup ref="AssertCustomerWelcomeMessageActionGroup" stepKey="seeWelcomeMessageForJohnDoeCustomer"> + <argument name="customerFullName" value="{{Simple_Customer_Without_Address.fullname}}"/> </actionGroup> - <amOnPage url="{{StorefrontCategoryPage.url($$createCategory.name$$)}}" stepKey="onStorefrontCategoryPage2"/> - <see userInput="Welcome, $$createCustomer.firstname$$ $$createCustomer.lastname$$!" selector="{{StorefrontPanelHeaderSection.WelcomeMessage}}" stepKey="homeCheckWelcome"/> - <see selector="{{StorefrontCategoryProductSection.ProductSpecialPriceByNumber('1')}}" userInput="45.00" stepKey="checkPriceSimpleProduct2"/> - <!--Click *Sign Out* and check the price of the Simple Product--> + <!--Go to category and check special price--> + <actionGroup ref="AssertStorefrontProductSpecialPriceInCategoryPageActionGroup" stepKey="assertProductSpecialPriceInCategoryPage"> + <argument name="categoryUrl" value="$createCategory.custom_attributes[url_key]$"/> + <argument name="productName" value="$createProduct.name$"/> + </actionGroup> + + + <!--Click *Sign Out*--> <actionGroup ref="StorefrontSignOutActionGroup" stepKey="storefrontSignOut"/> - <amOnPage url="{{StorefrontCategoryPage.url($$createCategory.name$$)}}" stepKey="onStorefrontCategoryPage3"/> - <see userInput="Welcome, $$createCustomer.firstname$$ $$createCustomer.lastname$$!" selector="{{StorefrontPanelHeaderSection.WelcomeMessage}}" stepKey="homeCheckWelcome2"/> - <seeElement selector="{{StorefrontPanelHeaderSection.notYouLink}}" stepKey="checkLinkNotYoy"/> - <see selector="{{StorefrontCategoryProductSection.ProductSpecialPriceByNumber('1')}}" userInput="45.00" stepKey="checkPriceSimpleProduct3"/> + <actionGroup ref="StorefrontAssertPersistentCustomerWelcomeMessageActionGroup" stepKey="seeWelcomeForJohnDoeCustomer"> + <argument name="customerFullName" value="{{Simple_Customer_Without_Address.fullname}}"/> + </actionGroup> + + <!--Go to category and check special price--> + <actionGroup ref="AssertStorefrontProductSpecialPriceInCategoryPageActionGroup" stepKey="assertProductSpecialPriceInCategoryPageAfterLogout"> + <argument name="categoryUrl" value="$createCategory.custom_attributes[url_key]$"/> + <argument name="productName" value="$createProduct.name$"/> + </actionGroup> <!--Click the *Not you?* link and check the price for Simple Product--> - <click selector="{{StorefrontPanelHeaderSection.notYouLink}}" stepKey="clickNext"/> - <amOnPage url="{{StorefrontCategoryPage.url($$createCategory.name$$)}}" stepKey="onStorefrontCategoryPage4"/> - <see userInput="Default welcome msg!" selector="{{StorefrontPanelHeaderSection.WelcomeMessage}}" stepKey="homeCheckWelcome3"/> - <see selector="{{StorefrontCategoryProductSection.ProductPriceByNumber('1')}}" userInput="$$createProduct.price$$" stepKey="checkPriceSimpleProduct4"/> + <click selector="{{StorefrontPanelHeaderSection.notYouLink}}" stepKey="clickNotYouLink"/> + <actionGroup ref="AssertStorefrontDefaultWelcomeMessageActionGroup" stepKey="seeWelcomeMessageForJohnDoeCustomerAfterLogout"/> + <actionGroup ref="AssertStorefrontProductPriceInCategoryPageActionGroup" stepKey="assertProductPriceInCategoryPageAfterLogout"> + <argument name="categoryUrl" value="$createCategory.custom_attributes[url_key]$"/> + <argument name="productName" value="$createProduct.name$"/> + </actionGroup> + <dontSeeElement selector="{{StorefrontCategoryProductSection.ProductCatalogRuleSpecialPriceTitleByName($createProduct.name$)}}" stepKey="dontSeeSpecialPrice"/> </test> </tests> diff --git a/app/code/Magento/CatalogRule/Test/Mftf/Test/StorefrontInactiveCatalogRuleTest.xml b/app/code/Magento/CatalogRule/Test/Mftf/Test/StorefrontInactiveCatalogRuleTest.xml index a4fc6757ae6cb..46ba6e30100b1 100644 --- a/app/code/Magento/CatalogRule/Test/Mftf/Test/StorefrontInactiveCatalogRuleTest.xml +++ b/app/code/Magento/CatalogRule/Test/Mftf/Test/StorefrontInactiveCatalogRuleTest.xml @@ -16,52 +16,52 @@ <description value="Customer should not see the catalog price rule promotion if status is inactive"/> <severity value="CRITICAL"/> <testCaseId value="MC-79"/> - <group value="CatalogRule"/> + <group value="catalogRule"/> </annotations> + <before> - <actionGroup ref="LoginAsAdmin" stepKey="login"/> <createData entity="ApiCategory" stepKey="createCategory"/> <createData entity="ApiSimpleProduct" stepKey="createProduct"> <requiredEntity createDataKey="createCategory"/> </createData> - <actionGroup stepKey="createNewPriceRule" ref="NewCatalogPriceRuleByUIActionGroup"/> - <actionGroup stepKey="selectLoggedInCustomers" ref="SelectNotLoggedInCustomerGroupActionGroup"/> - <scrollToTopOfPage stepKey="scrollToTop"/> - <click stepKey="setInactive" selector="{{AdminCategoryBasicFieldSection.enableCategoryLabel}}"/> - <click selector="{{AdminNewCatalogPriceRule.saveAndApply}}" stepKey="saveAndApply"/> - <see selector="{{AdminCategoryMessagesSection.SuccessMessage}}" userInput="You saved the rule." stepKey="seeSuccess"/> + <actionGroup ref="AdminLoginActionGroup" stepKey="loginToAdminPanel"/> - <!-- Perform reindex and flush cache --> - <magentoCLI command="indexer:reindex" stepKey="reindex"/> - <magentoCLI command="cache:flush" stepKey="flushCache"/> + <actionGroup ref="AdminOpenNewCatalogPriceRuleFormPageActionGroup" stepKey="startCreatingFirstPriceRule"/> + <actionGroup ref="AdminCatalogPriceRuleFillMainInfoActionGroup" stepKey="fillMainInfoForFirstPriceRule"> + <argument name="active" value="0"/> + <argument name="groups" value="'NOT LOGGED IN'"/> + </actionGroup> + <actionGroup ref="AdminCatalogPriceRuleFillActionsActionGroup" stepKey="fillActionsForThirdPriceRule"/> + <actionGroup ref="AdminCatalogPriceRuleSaveAndApplyActionGroup" stepKey="saveAndApplyFirstPriceRule"/> + <!-- Perform reindex --> + <magentoCLI command="indexer:reindex" arguments="catalogrule_rule" stepKey="reindex"/> </before> + <after> <deleteData createDataKey="createProduct" stepKey="deleteSimpleProduct"/> <deleteData createDataKey="createCategory" stepKey="deleteCategory"/> - <amOnPage url="admin/catalog_rule/promo_catalog/" stepKey="goToPriceRulePage"/> - <actionGroup ref="deleteEntitySecondaryGrid" stepKey="deletePriceRule"> - <argument name="name" value="{{_defaultCatalogRule.name}}"/> - <argument name="searchInput" value="{{AdminSecondaryGridSection.catalogRuleIdentifierSearch}}"/> + <actionGroup ref="RemoveCatalogPriceRuleActionGroup" stepKey="deleteCatalogPriceRule"> + <argument name="ruleName" value="{{_defaultCatalogRule.name}}"/> </actionGroup> - <amOnPage url="{{AdminLogoutPage.url}}" stepKey="logout"/> + <actionGroup ref="AdminLogoutActionGroup" stepKey="logoutFromAdmin"/> </after> <!-- Verify price is not discounted on category page --> - <amOnPage url="$$createCategory.name$$.html" stepKey="goToCategory"/> - <waitForPageLoad stepKey="waitForCategory"/> - <see selector="{{StorefrontCategoryProductSection.ProductPriceByNumber('1')}}" userInput="$$createProduct.price$$" stepKey="seePrice1"/> + <amOnPage url="{{StorefrontCategoryPage.url($createCategory.custom_attributes[url_key]$)}}" stepKey="openCategoryPageOnFrontend"/> + <waitForPageLoad stepKey="waitForCategoryPageLoaded"/> + <see selector="{{StorefrontCategoryProductSection.ProductPriceByNumber('1')}}" userInput="$$createProduct.price$$" stepKey="seeProductPriceOnCategoryPage"/> <!-- Verify price is not discounted on the product page --> - <amOnPage url="$$createProduct.sku$$.html" stepKey="goToProduct"/> - <waitForPageLoad stepKey="waitForProduct"/> - <see selector="{{StorefrontProductInfoMainSection.productPrice}}" userInput="$$createProduct.price$$" stepKey="seePrice2"/> + <amOnPage url="{{StorefrontProductPage.url($createProduct.custom_attributes[url_key]$)}}" stepKey="openProductPageOnFrontend"/> + <waitForPageLoad stepKey="waitForProductPageLoaded"/> + <see selector="{{StorefrontProductInfoMainSection.productPrice}}" userInput="$createProduct.price$" stepKey="seePriceOnProductPage"/> <!-- Verify price is not discounted in the cart --> - <click selector="{{StorefrontProductActionSection.addToCart}}" stepKey="addToCart"/> - <waitForPageLoad stepKey="waitForCart"/> - <waitForElementVisible selector="{{StorefrontMessagesSection.success}}" stepKey="waitForSuccessMessage"/> - <amOnPage url="{{CheckoutCartPage.url}}" stepKey="goToCheckout"/> - <waitForPageLoad stepKey="waitForCheckout"/> - <see selector="{{CheckoutCartSummarySection.subtotal}}" userInput="$$createProduct.price$$" stepKey="seePrice3"/> + <actionGroup ref="AddToCartFromStorefrontProductPageActionGroup" stepKey="addToCartFromStorefrontProductPage"> + <argument name="productName" value="$createProduct.name$"/> + </actionGroup> + <actionGroup ref="StorefrontOpenCartPageActionGroup" stepKey="openCartPage" /> + <waitForElementVisible selector="{{CheckoutCartSummarySection.subtotal}}" stepKey="waitForSubtotalAppears"/> + <see selector="{{CheckoutCartSummarySection.subtotal}}" userInput="$createProduct.price$" stepKey="seeProductPriceOnCartPage"/> </test> </tests> diff --git a/app/code/Magento/CatalogRule/Test/Unit/Observer/ProcessAdminFinalPriceObserverTest.php b/app/code/Magento/CatalogRule/Test/Unit/Observer/ProcessAdminFinalPriceObserverTest.php new file mode 100644 index 0000000000000..558fef8660606 --- /dev/null +++ b/app/code/Magento/CatalogRule/Test/Unit/Observer/ProcessAdminFinalPriceObserverTest.php @@ -0,0 +1,191 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +namespace Magento\CatalogRule\Test\Unit\Observer; + +use Magento\Catalog\Model\Product; +use Magento\CatalogRule\Model\ResourceModel\RuleFactory; +use Magento\CatalogRule\Observer\ProcessAdminFinalPriceObserver; +use Magento\CatalogRule\Observer\RulePricesStorage; +use Magento\Framework\Event; +use Magento\Framework\Event\Observer; +use Magento\Framework\Stdlib\DateTime\TimezoneInterface; +use Magento\Framework\TestFramework\Unit\Helper\ObjectManager; +use Magento\Store\Model\Store; +use Magento\Store\Model\StoreManagerInterface; +use Magento\Ui\Component\Form\Element\DataType\Date; +use PHPUnit\Framework\TestCase; + +/** + * Class ProcessAdminFinalPriceObserverTest + * + * Test class for Observer for applying catalog rules on product for admin area + * @SuppressWarnings(PHPMD.CouplingBetweenObjects) + */ +class ProcessAdminFinalPriceObserverTest extends TestCase +{ + /** + * @var ProcessAdminFinalPriceObserver + */ + private $observer; + + /** + * Store Manager mock + * + * @var StoreManagerInterface + */ + private $storeManagerMock; + + /** + * Locale Date mock + * + * @var TimezoneInterface + */ + private $localeDateMock; + + /** + * Resource Rule Factory mock + * + * @var RuleFactory + */ + private $resourceRuleFactoryMock; + + /** + * Rule Prices Storage mock + * + * @var RulePricesStorage + */ + private $rulePricesStorageMock; + + /** + * @var Event|\PHPUnit_Framework_MockObject_MockObject + */ + private $eventMock; + + /** + * @var Observer|\PHPUnit\Framework\MockObject\MockObject + */ + private $observerMock; + + protected function setUp() + { + $this->observerMock = $this + ->getMockBuilder(Observer::class) + ->disableOriginalConstructor() + ->getMock(); + $this->eventMock = $this + ->getMockBuilder(Event::class) + ->setMethods(['getProduct']) + ->disableOriginalConstructor() + ->getMock(); + $this->rulePricesStorageMock = $this->getMockBuilder(RulePricesStorage::class) + ->setMethods(['getWebsiteId', 'getRulePrice', 'getCustomerGroupId', 'setRulePrice']) + ->disableOriginalConstructor() + ->getMock(); + $this->storeManagerMock = $this->getMockBuilder(StoreManagerInterface::class) + ->setMethods(['getStore']) + ->disableOriginalConstructor() + ->getMockForAbstractClass(); + $this->resourceRuleFactoryMock = $this->getMockBuilder(RuleFactory::class) + ->setMethods(['create']) + ->disableOriginalConstructor() + ->getMock(); + $this->localeDateMock = $this->getMockBuilder(TimezoneInterface::class) + ->setMethods(['scopeDate']) + ->disableOriginalConstructor() + ->getMockForAbstractClass(); + $objectManagerHelper = new ObjectManager($this); + $this->observer = $objectManagerHelper->getObject( + ProcessAdminFinalPriceObserver::class, + [ + 'rulePricesStorage' => $this->rulePricesStorageMock, + 'storeManager' => $this->storeManagerMock, + 'resourceRuleFactory' => $this->resourceRuleFactoryMock, + 'localeDate' => $this->localeDateMock + ] + ); + } + + public function testExecute() + { + $finalPrice = 20.00; + $rulePrice = 10.00; + $storeId = 2; + $wId = 1; + $gId = 4; + $pId = 20; + $localeDateFormat = 'Y-m-d H:i:s'; + $date = '2019-12-02 08:00:00'; + $storeMock = $this->createMock(Store::class); + $this->observerMock + ->expects($this->atLeastOnce()) + ->method('getEvent') + ->willReturn($this->eventMock); + + $productMock = $this->getMockBuilder(Product::class) + ->setMethods( + [ + 'getStoreId', + 'getWebsiteId', + 'getId', + 'getData', + 'getCustomerGroupId', + 'setFinalPrice' + ] + ) + ->disableOriginalConstructor() + ->getMock(); + $dateMock = $this->getMockBuilder(Date::class) + ->setMethods(['format']) + ->disableOriginalConstructor() + ->getMock(); + + $this->localeDateMock->expects($this->once()) + ->method('scopeDate') + ->with($storeId) + ->willReturn($dateMock); + $dateMock->expects($this->once()) + ->method('format') + ->with($localeDateFormat) + ->willReturn($date); + $storeMock->expects($this->once()) + ->method('getWebsiteId') + ->willReturn($wId); + $this->storeManagerMock->expects($this->once()) + ->method('getStore') + ->with($storeId) + ->willReturn($storeMock); + $productMock->expects($this->once()) + ->method('getStoreId') + ->willReturn($storeId); + $productMock->expects($this->any()) + ->method('getCustomerGroupId') + ->willReturn($gId); + $productMock->expects($this->once()) + ->method('getId') + ->willReturn($pId); + $productMock->expects($this->once()) + ->method('getData') + ->with('final_price') + ->willReturn($finalPrice); + $this->rulePricesStorageMock->expects($this->any()) + ->method('getCustomerGroupId') + ->willReturn($gId); + $this->resourceRuleFactoryMock->expects($this->once()) + ->method('create') + ->willReturn($this->rulePricesStorageMock); + $this->rulePricesStorageMock->expects($this->any()) + ->method('getRulePrice') + ->willReturn($rulePrice); + $this->rulePricesStorageMock->expects($this->once()) + ->method('setRulePrice') + ->willReturnSelf(); + $this->eventMock + ->expects($this->atLeastOnce()) + ->method('getProduct') + ->willReturn($productMock); + $this->assertEquals($this->observer, $this->observer->execute($this->observerMock)); + } +} diff --git a/app/code/Magento/CatalogRule/registration.php b/app/code/Magento/CatalogRule/registration.php index ec3cbee2e1582..fb370fedf3752 100644 --- a/app/code/Magento/CatalogRule/registration.php +++ b/app/code/Magento/CatalogRule/registration.php @@ -4,6 +4,6 @@ * See COPYING.txt for license details. */ -use \Magento\Framework\Component\ComponentRegistrar; +use Magento\Framework\Component\ComponentRegistrar; ComponentRegistrar::register(ComponentRegistrar::MODULE, 'Magento_CatalogRule', __DIR__); diff --git a/app/code/Magento/CatalogRuleConfigurable/Test/Mftf/Test/AdminApplyCatalogRuleForConfigurableProductWithAssignedSimpleProducts2Test.xml b/app/code/Magento/CatalogRuleConfigurable/Test/Mftf/Test/AdminApplyCatalogRuleForConfigurableProductWithAssignedSimpleProducts2Test.xml new file mode 100644 index 0000000000000..48f53da8e2a2e --- /dev/null +++ b/app/code/Magento/CatalogRuleConfigurable/Test/Mftf/Test/AdminApplyCatalogRuleForConfigurableProductWithAssignedSimpleProducts2Test.xml @@ -0,0 +1,293 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + /** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +--> + +<tests xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/testSchema.xsd"> + <test name="AdminApplyCatalogRuleForConfigurableProductWithAssignedSimpleProducts2Test"> + <annotations> + <features value="CatalogRuleConfigurable"/> + <stories value="Apply catalog price rule"/> + <title value="Apply catalog rule for configurable product with assigned simple products"/> + <description value="Admin should be able to apply catalog rule for configurable product with assigned simple products"/> + <severity value="CRITICAL"/> + <testCaseId value="MC-27708"/> + <group value="catalog"/> + <group value="configurable_product"/> + <group value="catalog_rule_configurable"/> + <group value="mtf_migrated"/> + </annotations> + <before> + <!-- Create category for first configurable product --> + <createData entity="SimpleSubCategory" stepKey="firstSimpleCategory"/> + + <!-- Create first configurable product with two options --> + <createData entity="ApiConfigurableProduct" stepKey="createFirstConfigProduct"> + <requiredEntity createDataKey="firstSimpleCategory"/> + </createData> + + <createData entity="productAttributeWithTwoOptions" stepKey="createFirstConfigProductAttribute"/> + <createData entity="productAttributeOption1" stepKey="createFirstConfigProductAttributeFirstOption"> + <requiredEntity createDataKey="createFirstConfigProductAttribute"/> + </createData> + <createData entity="productAttributeOption2" stepKey="createFirstConfigProductAttributeSecondOption"> + <requiredEntity createDataKey="createFirstConfigProductAttribute"/> + </createData> + + <createData entity="AddToDefaultSet" stepKey="addFirstProductToAttributeSet"> + <requiredEntity createDataKey="createFirstConfigProductAttribute"/> + </createData> + + <getData entity="ProductAttributeOptionGetter" index="1" stepKey="getFirstConfigAttributeFirstOption"> + <requiredEntity createDataKey="createFirstConfigProductAttribute"/> + </getData> + <getData entity="ProductAttributeOptionGetter" index="2" stepKey="getFirstConfigAttributeSecondOption"> + <requiredEntity createDataKey="createFirstConfigProductAttribute"/> + </getData> + + <!-- Create two child products for first configurable product --> + <createData entity="ApiSimpleOne" stepKey="createFirstConfigFirstChildProduct"> + <requiredEntity createDataKey="createFirstConfigProductAttribute"/> + <requiredEntity createDataKey="getFirstConfigAttributeFirstOption"/> + </createData> + + <createData entity="ApiSimpleOne" stepKey="createFirstConfigSecondChildProduct"> + <requiredEntity createDataKey="createFirstConfigProductAttribute"/> + <requiredEntity createDataKey="getFirstConfigAttributeSecondOption"/> + </createData> + + <createData entity="ConfigurableProductTwoOptions" stepKey="createFirstConfigProductOption"> + <requiredEntity createDataKey="createFirstConfigProduct"/> + <requiredEntity createDataKey="createFirstConfigProductAttribute"/> + <requiredEntity createDataKey="getFirstConfigAttributeFirstOption"/> + <requiredEntity createDataKey="getFirstConfigAttributeSecondOption"/> + </createData> + + <createData entity="ConfigurableProductAddChild" stepKey="createFirstConfigProductAddFirstChild"> + <requiredEntity createDataKey="createFirstConfigProduct"/> + <requiredEntity createDataKey="createFirstConfigFirstChildProduct"/> + </createData> + <createData entity="ConfigurableProductAddChild" stepKey="createFirstConfigProductAddSecondChild"> + <requiredEntity createDataKey="createFirstConfigProduct"/> + <requiredEntity createDataKey="createFirstConfigSecondChildProduct"/> + </createData> + + <!-- Add customizable options to first product --> + <updateData createDataKey="createFirstConfigProduct" entity="productWithOptionRadiobutton" stepKey="updateFirstProductWithOption"/> + + <!-- Create category for second configurable product --> + <createData entity="SimpleSubCategory" stepKey="secondSimpleCategory"/> + + <!-- Create second configurable product with two options --> + <createData entity="ApiConfigurableProduct" stepKey="createSecondConfigProduct"> + <requiredEntity createDataKey="secondSimpleCategory"/> + </createData> + + <createData entity="productAttributeWithTwoOptions" stepKey="createSecondConfigProductAttribute"/> + <createData entity="productAttributeOption1" stepKey="createSecondConfigProductAttributeFirstOption"> + <requiredEntity createDataKey="createSecondConfigProductAttribute"/> + </createData> + <createData entity="productAttributeOption2" stepKey="createSecondConfigProductAttributeSecondOption"> + <requiredEntity createDataKey="createSecondConfigProductAttribute"/> + </createData> + + <createData entity="AddToDefaultSet" stepKey="addSecondProductToAttributeSet"> + <requiredEntity createDataKey="createSecondConfigProductAttribute"/> + </createData> + + <getData entity="ProductAttributeOptionGetter" index="1" stepKey="getSecondConfigAttributeFirstOption"> + <requiredEntity createDataKey="createSecondConfigProductAttribute"/> + </getData> + <getData entity="ProductAttributeOptionGetter" index="2" stepKey="getSecondConfigAttributeSecondOption"> + <requiredEntity createDataKey="createSecondConfigProductAttribute"/> + </getData> + + <!-- Create two child products for second configurable product --> + <createData entity="ApiSimpleOne" stepKey="createSecondConfigFirstChildProduct"> + <requiredEntity createDataKey="createSecondConfigProductAttribute"/> + <requiredEntity createDataKey="getSecondConfigAttributeFirstOption"/> + </createData> + + <createData entity="ApiSimpleOne" stepKey="createSecondConfigSecondChildProduct"> + <requiredEntity createDataKey="createSecondConfigProductAttribute"/> + <requiredEntity createDataKey="getSecondConfigAttributeSecondOption"/> + </createData> + + <createData entity="ConfigurableProductTwoOptions" stepKey="createSecondConfigProductOption"> + <requiredEntity createDataKey="createSecondConfigProduct"/> + <requiredEntity createDataKey="createSecondConfigProductAttribute"/> + <requiredEntity createDataKey="getSecondConfigAttributeFirstOption"/> + <requiredEntity createDataKey="getSecondConfigAttributeSecondOption"/> + </createData> + + <createData entity="ConfigurableProductAddChild" stepKey="createSecondConfigProductAddFirstChild"> + <requiredEntity createDataKey="createSecondConfigProduct"/> + <requiredEntity createDataKey="createSecondConfigFirstChildProduct"/> + </createData> + <createData entity="ConfigurableProductAddChild" stepKey="createSecondConfigProductAddSecondChild"> + <requiredEntity createDataKey="createSecondConfigProduct"/> + <requiredEntity createDataKey="createSecondConfigSecondChildProduct"/> + </createData> + + <!-- Add customizable options to second product --> + <updateData createDataKey="createSecondConfigProduct" entity="productWithOptionRadiobutton" stepKey="updateSecondProductWithOption"/> + + <!--Create customer group --> + <createData entity="CustomCustomerGroup" stepKey="customerGroup"/> + + <!-- Create Customer --> + <createData entity="SimpleUsCustomerWithNewCustomerGroup" stepKey="createCustomer"> + <requiredEntity createDataKey="customerGroup" /> + </createData> + + <!-- Login as Admin --> + <actionGroup ref="AdminLoginActionGroup" stepKey="loginToAdminPanel"/> + </before> + <after> + <!-- Delete created data --> + <deleteData createDataKey="createFirstConfigProduct" stepKey="deleteFirstConfigProduct"/> + <deleteData createDataKey="createFirstConfigFirstChildProduct" stepKey="deleteFirstConfigFirstChildProduct"/> + <deleteData createDataKey="createFirstConfigSecondChildProduct" stepKey="deleteFirstConfigSecondChildProduct"/> + <deleteData createDataKey="createFirstConfigProductAttribute" stepKey="deleteFirstConfigProductAttribute"/> + <deleteData createDataKey="firstSimpleCategory" stepKey="deleteFirstSimpleCategory"/> + + <deleteData createDataKey="createSecondConfigProduct" stepKey="deleteSecondConfigProduct"/> + <deleteData createDataKey="createSecondConfigFirstChildProduct" stepKey="deleteSecondConfigFirstChildProduct"/> + <deleteData createDataKey="createSecondConfigSecondChildProduct" stepKey="deleteSecondConfigSecondChildProduct"/> + <deleteData createDataKey="createSecondConfigProductAttribute" stepKey="deleteSecondConfigProductAttribute"/> + <deleteData createDataKey="secondSimpleCategory" stepKey="deleteSimpleCategory"/> + + <!-- Customer log out --> + <!-- Must logout before delete customer otherwise magento fails during logout --> + <actionGroup ref="StorefrontCustomerLogoutActionGroup" stepKey="logoutFromStorefront"/> + + <deleteData createDataKey="createCustomer" stepKey="deleteCustomer"/> + <deleteData createDataKey="customerGroup" stepKey="deleteCustomerGroup"/> + + <!-- Delete created price rules --> + <actionGroup ref="RemoveCatalogPriceRuleActionGroup" stepKey="deleteCatalogPriceRule"> + <argument name="ruleName" value="{{_defaultCatalogRule.name}}"/> + </actionGroup> + <!-- Admin log out --> + <actionGroup ref="AdminLogoutActionGroup" stepKey="logoutFromAdmin"/> + + <!-- Reindex invalidated indices after product attribute has been created/deleted --> + <magentoCron groups="index" stepKey="reindexInvalidatedIndices"/> + </after> + + <!-- Create catalog price rule --> + <executeJS function="return '$$customerGroup.code$$'" stepKey="customerGroupName"/> + <actionGroup ref="AdminOpenNewCatalogPriceRuleFormPageActionGroup" stepKey="startCreatingCatalogPriceRule"/> + <actionGroup ref="AdminCatalogPriceRuleFillMainInfoActionGroup" stepKey="fillMainInfoForCatalogPriceRule"> + <argument name="groups" value=""{$customerGroupName}""/> + </actionGroup> + <actionGroup ref="AdminCatalogPriceRuleFillActionsActionGroup" stepKey="fillActionsForCatalogPriceRule"> + <argument name="apply" value="{{CatalogRuleToFixed.simple_action}}"/> + <argument name="discountAmount" value="{{CatalogRuleToFixed.discount_amount}}"/> + </actionGroup> + <actionGroup ref="AdminCatalogPriceRuleSaveAndApplyActionGroup" stepKey="saveAndApplyCatalogPriceRule"/> + + <actionGroup ref="AdminReindexAndFlushCache" stepKey="reindexAndFlushCache"/> + + <!-- Login to storefront from customer --> + <actionGroup ref="LoginToStorefrontActionGroup" stepKey="loginCustomerOnStorefront"> + <argument name="Customer" value="$$createCustomer$$"/> + </actionGroup> + + <!-- Assert first product in category --> + <amOnPage url="{{StorefrontCategoryPage.url($$firstSimpleCategory.custom_attributes[url_key]$$)}}" stepKey="goToFirstCategoryPageStorefront"/> + <waitForPageLoad stepKey="waitForFirstCategoryPageLoad"/> + <actionGroup ref="StorefrontCheckCategoryConfigurableProductWithUpdatedPriceActionGroup" stepKey="checkFirstProductPriceInCategory"> + <argument name="productName" value="$$createFirstConfigProduct.name$$"/> + <argument name="expectedPrice" value="{{CatalogRuleToFixed.discount_amount}}"/> + </actionGroup> + + <!-- Assert second product in category --> + <amOnPage url="{{StorefrontCategoryPage.url($$secondSimpleCategory.custom_attributes[url_key]$$)}}" stepKey="goToSecondCategoryPageStorefront"/> + <waitForPageLoad stepKey="waitForSecondCategoryPageLoad"/> + <actionGroup ref="StorefrontCheckCategoryConfigurableProductWithUpdatedPriceActionGroup" stepKey="checkSecondProductPriceInCategory"> + <argument name="productName" value="$$createSecondConfigProduct.name$$"/> + <argument name="expectedPrice" value="{{CatalogRuleToFixed.discount_amount}}"/> + </actionGroup> + + <!-- Assert first product in storefront product page --> + <amOnPage url="{{StorefrontProductPage.url($$createFirstConfigProduct.custom_attributes[url_key]$$)}}" stepKey="amOnFirstProductPage"/> + <waitForPageLoad stepKey="waitForFirstProductPageLoad"/> + <actionGroup ref="StorefrontAssertUpdatedProductPriceInStorefrontProductPageActionGroup" stepKey="checkFirstProductPriceInStorefrontProductPage"> + <argument name="productName" value="$$createFirstConfigProduct.name$$"/> + <argument name="expectedPrice" value="{{CatalogRuleToFixed.discount_amount}}"/> + </actionGroup> + + <!-- Add first product with selected options to the cart --> + <actionGroup ref="StorefrontProductPageSelectDropDownOptionValueActionGroup" stepKey="firstConfigProductSelectFirstOptionValue"> + <argument name="attributeLabel" value="$$createFirstConfigProductAttribute.default_frontend_label$$"/> + <argument name="optionLabel" value="$$createFirstConfigProductAttributeFirstOption.option[store_labels][1][label]$$"/> + </actionGroup> + <actionGroup ref="StorefrontProductPageSelectRadioButtonOptionValueActionGroup" stepKey="firstConfigProductSelectSecondOptionValue"> + <argument name="attributeLabel" value="{{ProductOptionRadiobuttonWithTwoFixedOptions.title}}"/> + <argument name="optionLabel" value="{{ProductOptionValueRadioButtons1.title}}"/> + </actionGroup> + <actionGroup ref="AddToCartFromStorefrontProductPageActionGroup" stepKey="addFirstConfigProductToCart"> + <argument name="productName" value="$$createFirstConfigProduct.name$$"/> + </actionGroup> + + <!-- Add first product with another selected options to the cart --> + <actionGroup ref="StorefrontProductPageSelectDropDownOptionValueActionGroup" stepKey="firstConfigProductSelectFirstOptionAnotherValue"> + <argument name="attributeLabel" value="$$createFirstConfigProductAttribute.default_frontend_label$$"/> + <argument name="optionLabel" value="$$createFirstConfigProductAttributeSecondOption.option[store_labels][1][label]$$"/> + </actionGroup> + <actionGroup ref="StorefrontProductPageSelectRadioButtonOptionValueActionGroup" stepKey="firstConfigProductSelectSecondOptionAnotherValue"> + <argument name="attributeLabel" value="{{ProductOptionRadiobuttonWithTwoFixedOptions.title}}"/> + <argument name="optionLabel" value="{{ProductOptionValueRadioButtons3.title}}"/> + </actionGroup> + <actionGroup ref="AddToCartFromStorefrontProductPageActionGroup" stepKey="addFirstConfigProductWithOtherOptionsToCart"> + <argument name="productName" value="$$createFirstConfigProduct.name$$"/> + </actionGroup> + + <!-- Assert second product in storefront product page --> + <amOnPage url="{{StorefrontProductPage.url($$createSecondConfigProduct.custom_attributes[url_key]$$)}}" stepKey="amOnSecondProductPage"/> + <waitForPageLoad stepKey="waitForSecondProductPageLoad"/> + <actionGroup ref="StorefrontAssertUpdatedProductPriceInStorefrontProductPageActionGroup" stepKey="checkSecondProductPriceInStorefrontProductPage"> + <argument name="productName" value="$$createSecondConfigProduct.name$$"/> + <argument name="expectedPrice" value="{{CatalogRuleToFixed.discount_amount}}"/> + </actionGroup> + + <!-- Add second product with selected options to the cart --> + <actionGroup ref="StorefrontProductPageSelectDropDownOptionValueActionGroup" stepKey="secondConfigProductSelectFirstOptionValue"> + <argument name="attributeLabel" value="$$createSecondConfigProductAttribute.default_frontend_label$$"/> + <argument name="optionLabel" value="$$createSecondConfigProductAttributeFirstOption.option[store_labels][1][label]$$"/> + </actionGroup> + <actionGroup ref="StorefrontProductPageSelectRadioButtonOptionValueActionGroup" stepKey="secondConfigProductSelectSecondOptionValue"> + <argument name="attributeLabel" value="{{ProductOptionRadiobuttonWithTwoFixedOptions.title}}"/> + <argument name="optionLabel" value="{{ProductOptionValueRadioButtons1.title}}"/> + </actionGroup> + <actionGroup ref="AddToCartFromStorefrontProductPageActionGroup" stepKey="addSecondConfigProductToCart"> + <argument name="productName" value="$$createSecondConfigProduct.name$$"/> + </actionGroup> + + <!-- Add second product with another selected options to the cart --> + <actionGroup ref="StorefrontProductPageSelectDropDownOptionValueActionGroup" stepKey="secondConfigProductSelectFirstOptionAnotherValue"> + <argument name="attributeLabel" value="$$createSecondConfigProductAttribute.default_frontend_label$$"/> + <argument name="optionLabel" value="$$createSecondConfigProductAttributeSecondOption.option[store_labels][1][label]$$"/> + </actionGroup> + <actionGroup ref="StorefrontProductPageSelectRadioButtonOptionValueActionGroup" stepKey="secondConfigProductSelectSecondOptionAnotherValue"> + <argument name="attributeLabel" value="{{ProductOptionRadiobuttonWithTwoFixedOptions.title}}"/> + <argument name="optionLabel" value="{{ProductOptionValueRadioButtons3.title}}"/> + </actionGroup> + <actionGroup ref="AddToCartFromStorefrontProductPageActionGroup" stepKey="addSecondConfigProductWithOtherOptionsToCart"> + <argument name="productName" value="$$createSecondConfigProduct.name$$"/> + </actionGroup> + + <!--Assert products prices in the cart --> + <actionGroup ref="StorefrontOpenCartFromMinicartActionGroup" stepKey="amOnShoppingCartPage"/> + <waitForPageLoad stepKey="waitForShoppingCartPageLoad"/> + <waitForElementVisible selector="{{CheckoutCartProductSection.ProductPriceByOption($$createFirstConfigProductAttributeFirstOption.option[store_labels][1][label]$$)}}" stepKey="waitForCartFullyLoaded"/> + <see userInput="$210.69" selector="{{CheckoutCartProductSection.ProductPriceByOption($$createFirstConfigProductAttributeFirstOption.option[store_labels][1][label]$$)}}" stepKey="assertFirstProductPriceForFirstProductOption"/> + <see userInput="$120.70" selector="{{CheckoutCartProductSection.ProductPriceByOption($$createFirstConfigProductAttributeSecondOption.option[store_labels][1][label]$$)}}" stepKey="assertFirstProductPriceForSecondProductOption"/> + <see userInput="$210.69" selector="{{CheckoutCartProductSection.ProductPriceByOption($$createSecondConfigProductAttributeFirstOption.option[store_labels][1][label]$$)}}" stepKey="assertSecondProductPriceForFirstProductOption"/> + <see userInput="$120.70" selector="{{CheckoutCartProductSection.ProductPriceByOption($$createSecondConfigProductAttributeSecondOption.option[store_labels][1][label]$$)}}" stepKey="assertSecondProductPriceForSecondProductOption"/> + </test> +</tests> diff --git a/app/code/Magento/CatalogRuleConfigurable/Test/Mftf/Test/AdminApplyCatalogRuleForConfigurableProductWithAssignedSimpleProductsTest.xml b/app/code/Magento/CatalogRuleConfigurable/Test/Mftf/Test/AdminApplyCatalogRuleForConfigurableProductWithAssignedSimpleProductsTest.xml deleted file mode 100644 index 71ab764453b20..0000000000000 --- a/app/code/Magento/CatalogRuleConfigurable/Test/Mftf/Test/AdminApplyCatalogRuleForConfigurableProductWithAssignedSimpleProductsTest.xml +++ /dev/null @@ -1,268 +0,0 @@ -<?xml version="1.0" encoding="UTF-8"?> -<!-- - /** - * Copyright © Magento, Inc. All rights reserved. - * See COPYING.txt for license details. - */ ---> - -<tests xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/testSchema.xsd"> - <test name="AdminApplyCatalogRuleForConfigurableProductWithAssignedSimpleProductsTest"> - <annotations> - <features value="CatalogRuleConfigurable"/> - <stories value="Apply catalog price rule"/> - <title value="Apply catalog rule for configurable product with assigned simple products"/> - <description value="Admin should be able to apply catalog rule for configurable product with assigned simple products"/> - <severity value="CRITICAL"/> - <testCaseId value="MC-14063"/> - <group value="catalogRuleConfigurable"/> - <group value="mtf_migrated"/> - <skip> - <issueId value="MC-17140"/> - </skip> - </annotations> - <before> - <!-- Create category for first configurable product --> - <createData entity="SimpleSubCategory" stepKey="firstSimpleCategory"/> - - <!-- Create first configurable product with two options --> - <createData entity="ApiConfigurableProduct" stepKey="createFirstConfigProduct"> - <requiredEntity createDataKey="firstSimpleCategory"/> - </createData> - - <createData entity="productAttributeWithTwoOptions" stepKey="createFirstConfigProductAttribute"/> - <createData entity="productAttributeOption1" stepKey="createFirstConfigProductAttributeFirstOption"> - <requiredEntity createDataKey="createFirstConfigProductAttribute"/> - </createData> - <createData entity="productAttributeOption2" stepKey="createFirstConfigProductAttributeSecondOption"> - <requiredEntity createDataKey="createFirstConfigProductAttribute"/> - </createData> - - <createData entity="AddToDefaultSet" stepKey="addFirstProductToAttributeSet"> - <requiredEntity createDataKey="createFirstConfigProductAttribute"/> - </createData> - - <getData entity="ProductAttributeOptionGetter" index="1" stepKey="getFirstConfigAttributeFirstOption"> - <requiredEntity createDataKey="createFirstConfigProductAttribute"/> - </getData> - <getData entity="ProductAttributeOptionGetter" index="2" stepKey="getFirstConfigAttributeSecondOption"> - <requiredEntity createDataKey="createFirstConfigProductAttribute"/> - </getData> - - <!-- Create two child products for first configurable product --> - <createData entity="ApiSimpleOne" stepKey="createFirstConfigFirstChildProduct"> - <requiredEntity createDataKey="createFirstConfigProductAttribute"/> - <requiredEntity createDataKey="getFirstConfigAttributeFirstOption"/> - </createData> - - <createData entity="ApiSimpleOne" stepKey="createFirstConfigSecondChildProduct"> - <requiredEntity createDataKey="createFirstConfigProductAttribute"/> - <requiredEntity createDataKey="getFirstConfigAttributeSecondOption"/> - </createData> - - <createData entity="ConfigurableProductTwoOptions" stepKey="createFirstConfigProductOption"> - <requiredEntity createDataKey="createFirstConfigProduct"/> - <requiredEntity createDataKey="createFirstConfigProductAttribute"/> - <requiredEntity createDataKey="getFirstConfigAttributeFirstOption"/> - <requiredEntity createDataKey="getFirstConfigAttributeSecondOption"/> - </createData> - - <createData entity="ConfigurableProductAddChild" stepKey="createFirstConfigProductAddFirstChild"> - <requiredEntity createDataKey="createFirstConfigProduct"/> - <requiredEntity createDataKey="createFirstConfigFirstChildProduct"/> - </createData> - <createData entity="ConfigurableProductAddChild" stepKey="createFirstConfigProductAddSecondChild"> - <requiredEntity createDataKey="createFirstConfigProduct"/> - <requiredEntity createDataKey="createFirstConfigSecondChildProduct"/> - </createData> - - <!-- Add customizable options to first product --> - <updateData createDataKey="createFirstConfigProduct" entity="productWithOptionRadiobutton" stepKey="updateFirstProductWithOption"/> - - <!-- Create category for second configurable product --> - <createData entity="SimpleSubCategory" stepKey="secondSimpleCategory"/> - - <!-- Create second configurable product with two options --> - <createData entity="ApiConfigurableProduct" stepKey="createSecondConfigProduct"> - <requiredEntity createDataKey="secondSimpleCategory"/> - </createData> - - <createData entity="productAttributeWithTwoOptions" stepKey="createSecondConfigProductAttribute"/> - <createData entity="productAttributeOption1" stepKey="createSecondConfigProductAttributeFirstOption"> - <requiredEntity createDataKey="createSecondConfigProductAttribute"/> - </createData> - <createData entity="productAttributeOption2" stepKey="createSecondConfigProductAttributeSecondOption"> - <requiredEntity createDataKey="createSecondConfigProductAttribute"/> - </createData> - - <createData entity="AddToDefaultSet" stepKey="addSecondProductToAttributeSet"> - <requiredEntity createDataKey="createSecondConfigProductAttribute"/> - </createData> - - <getData entity="ProductAttributeOptionGetter" index="1" stepKey="getSecondConfigAttributeFirstOption"> - <requiredEntity createDataKey="createSecondConfigProductAttribute"/> - </getData> - <getData entity="ProductAttributeOptionGetter" index="2" stepKey="getSecondConfigAttributeSecondOption"> - <requiredEntity createDataKey="createSecondConfigProductAttribute"/> - </getData> - - <!-- Create two child products for second configurable product --> - <createData entity="ApiSimpleOne" stepKey="createSecondConfigFirstChildProduct"> - <requiredEntity createDataKey="createSecondConfigProductAttribute"/> - <requiredEntity createDataKey="getSecondConfigAttributeFirstOption"/> - </createData> - - <createData entity="ApiSimpleOne" stepKey="createSecondConfigSecondChildProduct"> - <requiredEntity createDataKey="createSecondConfigProductAttribute"/> - <requiredEntity createDataKey="getSecondConfigAttributeSecondOption"/> - </createData> - - <createData entity="ConfigurableProductTwoOptions" stepKey="createSecondConfigProductOption"> - <requiredEntity createDataKey="createSecondConfigProduct"/> - <requiredEntity createDataKey="createSecondConfigProductAttribute"/> - <requiredEntity createDataKey="getSecondConfigAttributeFirstOption"/> - <requiredEntity createDataKey="getSecondConfigAttributeSecondOption"/> - </createData> - - <createData entity="ConfigurableProductAddChild" stepKey="createSecondConfigProductAddFirstChild"> - <requiredEntity createDataKey="createSecondConfigProduct"/> - <requiredEntity createDataKey="createSecondConfigFirstChildProduct"/> - </createData> - <createData entity="ConfigurableProductAddChild" stepKey="createSecondConfigProductAddSecondChild"> - <requiredEntity createDataKey="createSecondConfigProduct"/> - <requiredEntity createDataKey="createSecondConfigSecondChildProduct"/> - </createData> - - <!-- Add customizable options to second product --> - <updateData createDataKey="createSecondConfigProduct" entity="productWithOptionRadiobutton" stepKey="updateSecondProductWithOption"/> - - <!--Create customer group --> - <createData entity="CustomCustomerGroup" stepKey="customerGroup"/> - - <!-- Create Customer --> - <createData entity="SimpleUsCustomerWithNewCustomerGroup" stepKey="createCustomer"> - <requiredEntity createDataKey="customerGroup" /> - </createData> - - <!-- Login as Admin --> - <actionGroup ref="LoginAsAdmin" stepKey="loginToAdminPanel"/> - </before> - <after> - <!-- Customer log out --> - <actionGroup ref="StorefrontCustomerLogoutActionGroup" stepKey="customerLogout"/> - - <!-- Delete created data --> - <deleteData createDataKey="createFirstConfigProduct" stepKey="deleteFirstConfigProduct"/> - <deleteData createDataKey="createFirstConfigFirstChildProduct" stepKey="deleteFirstConfigFirstChildProduct"/> - <deleteData createDataKey="createFirstConfigSecondChildProduct" stepKey="deleteFirstConfigSecondChildProduct"/> - <deleteData createDataKey="createFirstConfigProductAttribute" stepKey="deleteFirstConfigProductAttribute"/> - <deleteData createDataKey="firstSimpleCategory" stepKey="deleteFirstSimpleCategory"/> - - <deleteData createDataKey="createSecondConfigProduct" stepKey="deleteSecondConfigProduct"/> - <deleteData createDataKey="createSecondConfigFirstChildProduct" stepKey="deleteSecondConfigFirstChildProduct"/> - <deleteData createDataKey="createSecondConfigSecondChildProduct" stepKey="deleteSecondConfigSecondChildProduct"/> - <deleteData createDataKey="createSecondConfigProductAttribute" stepKey="deleteSecondConfigProductAttribute"/> - <deleteData createDataKey="secondSimpleCategory" stepKey="deleteSimpleCategory"/> - - <deleteData createDataKey="createCustomer" stepKey="deleteCustomer"/> - <deleteData createDataKey="customerGroup" stepKey="deleteCustomerGroup"/> - - <!-- Delete created price rules --> - <amOnPage url="{{CatalogRulePage.url}}" stepKey="goToPriceRulePage"/> - <actionGroup ref="deleteEntitySecondaryGrid" stepKey="deletePriceRuleForFirstOption"> - <argument name="name" value="{{CatalogRuleToFixed.name}}"/> - <argument name="searchInput" value="{{AdminSecondaryGridSection.catalogRuleIdentifierSearch}}"/> - </actionGroup> - - <!-- Admin log out --> - <actionGroup ref="logout" stepKey="logout"/> - </after> - <!-- Create price rule --> - <actionGroup ref="NewCatalogPriceRuleByUIActionGroup" stepKey="createPriceRule"> - <argument name="catalogRule" value="CatalogRuleToFixed"/> - </actionGroup> - <actionGroup ref="CatalogSelectCustomerGroupActionGroup" stepKey="addCustomerGroup"> - <argument name="customerGroupName" value="$$customerGroup.code$$"/> - </actionGroup> - - <!-- Save price rule --> - <click selector="{{AdminNewCatalogPriceRule.saveAndApply}}" stepKey="clickSaveAndApplyRule"/> - <see selector="{{AdminCategoryMessagesSection.SuccessMessage}}" userInput="You saved the rule." stepKey="assertSuccessMessage"/> - - <!-- Run full reindex and clear caches --> - <magentoCLI command="indexer:reindex" stepKey="reindex"/> - <magentoCLI command="cache:flush" stepKey="flushCache"/> - - <!-- Login to storefront from customer --> - <actionGroup ref="LoginToStorefrontActionGroup" stepKey="logInFromCustomer"> - <argument name="Customer" value="$$createCustomer$$"/> - </actionGroup> - - <!-- Assert first product in category --> - <amOnPage url="$$firstSimpleCategory.name$$.html" stepKey="goToFirstCategoryPageStorefront"/> - <waitForPageLoad stepKey="waitForFirstCategoryPageLoad"/> - <actionGroup ref="StorefrontCheckCategoryConfigurableProductWithUpdatedPriceActionGroup" stepKey="checkFirstProductPriceInCategory"> - <argument name="productName" value="$$createFirstConfigProduct.name$$"/> - <argument name="expectedPrice" value="{{CatalogRuleToFixed.discount_amount}}"/> - </actionGroup> - - <!-- Assert second product in category --> - <amOnPage url="$$secondSimpleCategory.name$$.html" stepKey="goToSecondCategoryPageStorefront"/> - <waitForPageLoad stepKey="waitForSecondCategoryPageLoad"/> - <actionGroup ref="StorefrontCheckCategoryConfigurableProductWithUpdatedPriceActionGroup" stepKey="checkSecondProductPriceInCategory"> - <argument name="productName" value="$$createSecondConfigProduct.name$$"/> - <argument name="expectedPrice" value="{{CatalogRuleToFixed.discount_amount}}"/> - </actionGroup> - - <!-- Assert first product in storefront product page --> - <amOnPage url="$$createFirstConfigProduct.custom_attributes[url_key]$$.html" stepKey="amOnFirstProductPage"/> - <waitForPageLoad stepKey="waitForFirstProductPageLoad"/> - <actionGroup ref="StorefrontAssertUpdatedProductPriceInStorefrontProductPageActionGroup" stepKey="checkFirstProductPriceInStorefrontProductPage"> - <argument name="productName" value="$$createFirstConfigProduct.name$$"/> - <argument name="expectedPrice" value="{{CatalogRuleToFixed.discount_amount}}"/> - </actionGroup> - - <!-- Add first product with selected options to the cart --> - <actionGroup ref="StorefrontAddProductWithSelectedConfigurableAndCustomOptionsToCartActionGroup" stepKey="addFirstProductWithSelectedOptionToCart1"> - <argument name="product" value="$$createFirstConfigProduct$$"/> - <argument name="option" value="$$createFirstConfigProductAttributeFirstOption.option[store_labels][1][label]$$"/> - <argument name="customizableOption" value="{{ProductOptionValueRadioButtons1.title}}"/> - </actionGroup> - <grabTextFrom selector="{{StorefrontProductInfoMainSection.updatedPrice}}" stepKey="grabForthProductUpdatedPrice1"/> - - <actionGroup ref="StorefrontAddProductWithSelectedConfigurableAndCustomOptionsToCartActionGroup" stepKey="addFirstProductWithSelectedOptionToCart2"> - <argument name="product" value="$$createFirstConfigProduct$$"/> - <argument name="option" value="$$createFirstConfigProductAttributeSecondOption.option[store_labels][1][label]$$"/> - <argument name="customizableOption" value="{{ProductOptionValueRadioButtons3.title}}"/> - </actionGroup> - - <!-- Assert second product in storefront product page --> - <amOnPage url="$$createSecondConfigProduct.custom_attributes[url_key]$$.html" stepKey="amOnSecondProductPage"/> - <waitForPageLoad stepKey="waitForSecondProductPageLoad"/> - <actionGroup ref="StorefrontAssertUpdatedProductPriceInStorefrontProductPageActionGroup" stepKey="checkSecondProductPriceInStorefrontProductPage"> - <argument name="productName" value="$$createSecondConfigProduct.name$$"/> - <argument name="expectedPrice" value="{{CatalogRuleToFixed.discount_amount}}"/> - </actionGroup> - - <!-- Add second product with selected options to the cart --> - <actionGroup ref="StorefrontAddProductWithSelectedConfigurableAndCustomOptionsToCartActionGroup" stepKey="addSecondProductWithSelectedOptionToCart1"> - <argument name="product" value="$$createSecondConfigProduct$$"/> - <argument name="option" value="$$createSecondConfigProductAttributeFirstOption.option[store_labels][1][label]$$"/> - <argument name="customizableOption" value="{{ProductOptionValueRadioButtons1.title}}"/> - </actionGroup> - - <actionGroup ref="StorefrontAddProductWithSelectedConfigurableAndCustomOptionsToCartActionGroup" stepKey="addSecondProductWithSelectedOptionToCart2"> - <argument name="product" value="$$createSecondConfigProduct$$"/> - <argument name="option" value="$$createSecondConfigProductAttributeSecondOption.option[store_labels][1][label]$$"/> - <argument name="customizableOption" value="{{ProductOptionValueRadioButtons3.title}}"/> - </actionGroup> - - <!--Assert products prices in the cart --> - <amOnPage url="/checkout/cart/" stepKey="amOnShoppingCartPage"/> - <waitForPageLoad stepKey="waitForShoppingCartPageLoad"/> - <see userInput="$210.69" selector="{{CheckoutCartProductSection.ProductPriceByOption($$createFirstConfigProductAttributeFirstOption.option[store_labels][1][label]$$)}}" stepKey="assertFirstProductPriceForFirstProductOption"/> - <see userInput="$120.70" selector="{{CheckoutCartProductSection.ProductPriceByOption($$createFirstConfigProductAttributeSecondOption.option[store_labels][1][label]$$)}}" stepKey="assertFirstProductPriceForSecondProductOption"/> - <see userInput="$210.69" selector="{{CheckoutCartProductSection.ProductPriceByOption($$createSecondConfigProductAttributeFirstOption.option[store_labels][1][label]$$)}}" stepKey="assertSecondProductPriceForFirstProductOption"/> - <see userInput="$120.70" selector="{{CheckoutCartProductSection.ProductPriceByOption($$createSecondConfigProductAttributeSecondOption.option[store_labels][1][label]$$)}}" stepKey="assertSecondProductPriceForSecondProductOption"/> - </test> -</tests> diff --git a/app/code/Magento/CatalogRuleConfigurable/Test/Mftf/Test/AdminApplyCatalogRuleForConfigurableProductWithOptions2Test.xml b/app/code/Magento/CatalogRuleConfigurable/Test/Mftf/Test/AdminApplyCatalogRuleForConfigurableProductWithOptions2Test.xml new file mode 100644 index 0000000000000..350f896606c19 --- /dev/null +++ b/app/code/Magento/CatalogRuleConfigurable/Test/Mftf/Test/AdminApplyCatalogRuleForConfigurableProductWithOptions2Test.xml @@ -0,0 +1,237 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + /** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +--> + +<tests xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/testSchema.xsd"> + <test name="AdminApplyCatalogRuleForConfigurableProductWithOptions2Test"> + <annotations> + <features value="CatalogRuleConfigurable"/> + <stories value="Apply catalog price rule"/> + <title value="Apply catalog price rule for configurable product with options"/> + <description value="Admin should be able to apply the catalog rule for configurable product with options"/> + <severity value="CRITICAL"/> + <testCaseId value="MC-27707"/> + <group value="catalog"/> + <group value="configurable_product"/> + <group value="catalog_rule_configurable"/> + <group value="mtf_migrated"/> + </annotations> + <before> + <!-- Create category --> + <createData entity="SimpleSubCategory" stepKey="simpleCategory"/> + + <!-- Create configurable product with three options --> + <createData entity="ApiConfigurableProduct" stepKey="createConfigProduct"> + <requiredEntity createDataKey="simpleCategory"/> + </createData> + + <createData entity="productAttributeWithTwoOptions" stepKey="createConfigProductAttribute"/> + <createData entity="productAttributeOption1" stepKey="createConfigProductAttributeFirstOption"> + <requiredEntity createDataKey="createConfigProductAttribute"/> + </createData> + <createData entity="productAttributeOption2" stepKey="createConfigProductAttributeSecondOption"> + <requiredEntity createDataKey="createConfigProductAttribute"/> + </createData> + <createData entity="productAttributeOption3" stepKey="createConfigProductAttributeThirdOption"> + <requiredEntity createDataKey="createConfigProductAttribute"/> + </createData> + + <createData entity="AddToDefaultSet" stepKey="createConfigAddToAttributeSet"> + <requiredEntity createDataKey="createConfigProductAttribute"/> + </createData> + + <getData entity="ProductAttributeOptionGetter" index="1" stepKey="getConfigAttributeFirstOption"> + <requiredEntity createDataKey="createConfigProductAttribute"/> + </getData> + <getData entity="ProductAttributeOptionGetter" index="2" stepKey="getConfigAttributeSecondOption"> + <requiredEntity createDataKey="createConfigProductAttribute"/> + </getData> + <getData entity="ProductAttributeOptionGetter" index="3" stepKey="getConfigAttributeThirdOption"> + <requiredEntity createDataKey="createConfigProductAttribute"/> + </getData> + + <!-- Create three child products --> + <createData entity="ApiSimpleOne" stepKey="createConfigFirstChildProduct"> + <requiredEntity createDataKey="createConfigProductAttribute"/> + <requiredEntity createDataKey="getConfigAttributeFirstOption"/> + </createData> + + <createData entity="ApiSimpleOne" stepKey="createConfigSecondChildProduct"> + <requiredEntity createDataKey="createConfigProductAttribute"/> + <requiredEntity createDataKey="getConfigAttributeSecondOption"/> + </createData> + + <createData entity="ApiSimpleOne" stepKey="createConfigThirdChildProduct"> + <requiredEntity createDataKey="createConfigProductAttribute"/> + <requiredEntity createDataKey="getConfigAttributeThirdOption"/> + </createData> + + <createData entity="ConfigurableProductTwoOptions" stepKey="createConfigProductOption"> + <requiredEntity createDataKey="createConfigProduct"/> + <requiredEntity createDataKey="createConfigProductAttribute"/> + <requiredEntity createDataKey="getConfigAttributeFirstOption"/> + <requiredEntity createDataKey="getConfigAttributeSecondOption"/> + <requiredEntity createDataKey="getConfigAttributeThirdOption"/> + </createData> + + <createData entity="ConfigurableProductAddChild" stepKey="createConfigProductAddFirstChild"> + <requiredEntity createDataKey="createConfigProduct"/> + <requiredEntity createDataKey="createConfigFirstChildProduct"/> + </createData> + <createData entity="ConfigurableProductAddChild" stepKey="createConfigProductAddSecondChild"> + <requiredEntity createDataKey="createConfigProduct"/> + <requiredEntity createDataKey="createConfigSecondChildProduct"/> + </createData> + <createData entity="ConfigurableProductAddChild" stepKey="createConfigProductAddThirdChild"> + <requiredEntity createDataKey="createConfigProduct"/> + <requiredEntity createDataKey="createConfigThirdChildProduct"/> + </createData> + + <!-- Login as Admin --> + <actionGroup ref="AdminLoginActionGroup" stepKey="loginToAdminPanel"/> + </before> + <after> + <!-- Delete created data --> + <deleteData createDataKey="createConfigProduct" stepKey="deleteConfigProduct"/> + <deleteData createDataKey="createConfigFirstChildProduct" stepKey="deleteFirstSimpleProduct"/> + <deleteData createDataKey="createConfigSecondChildProduct" stepKey="deleteSecondSimpleProduct"/> + <deleteData createDataKey="createConfigThirdChildProduct" stepKey="deleteThirdSimpleProduct"/> + <deleteData createDataKey="createConfigProductAttribute" stepKey="deleteConfigProductAttribute"/> + <deleteData createDataKey="simpleCategory" stepKey="deleteCategory"/> + + <!-- Delete created price rules --> + <actionGroup ref="RemoveCatalogPriceRuleActionGroup" stepKey="deleteFirstCatalogPriceRule"> + <argument name="ruleName" value="{{CatalogRuleToFixed.name}}"/> + </actionGroup> + <actionGroup ref="RemoveCatalogPriceRuleActionGroup" stepKey="deleteSecondCatalogPriceRule"> + <argument name="ruleName" value="{{CatalogRuleWithoutDiscount.name}}"/> + </actionGroup> + <actionGroup ref="RemoveCatalogPriceRuleActionGroup" stepKey="deleteThirdCatalogPriceRule"> + <argument name="ruleName" value="{{_defaultCatalogRule.name}}"/> + </actionGroup> + <actionGroup ref="AdminLogoutActionGroup" stepKey="logoutFromAdmin"/> + + <!-- Reindex invalidated indices after product attribute has been created/deleted --> + <magentoCron groups="index" stepKey="reindexInvalidatedIndices"/> + </after> + + <!-- Create price rule for first configurable product option --> + <actionGroup ref="AdminOpenNewCatalogPriceRuleFormPageActionGroup" stepKey="startCreatingFirstPriceRule"/> + <actionGroup ref="AdminCatalogPriceRuleFillMainInfoActionGroup" stepKey="fillMainInfoForFirstPriceRule"> + <argument name="name" value="{{CatalogRuleToFixed.name}}"/> + <argument name="description" value="{{CatalogRuleToFixed.description}}"/> + <argument name="groups" value="'NOT LOGGED IN'"/> + </actionGroup> + <actionGroup ref="CreateCatalogPriceRuleConditionWithAttributeAndOptionActionGroup" stepKey="fillConditionsForFirstPriceRule"> + <argument name="attributeName" value="$$createConfigProductAttribute.attribute[frontend_labels][0][label]$$"/> + <argument name="targetSelectValue" value="$$createConfigProductAttributeFirstOption.option[store_labels][1][label]$$"/> + <argument name="indexA" value="1"/> + <argument name="indexB" value="1"/> + </actionGroup> + <actionGroup ref="AdminCatalogPriceRuleFillActionsActionGroup" stepKey="fillActionsForFirstPriceRule"> + <argument name="apply" value="{{CatalogRuleToFixed.simple_action}}"/> + <argument name="discountAmount" value="{{CatalogRuleToFixed.discount_amount}}"/> + </actionGroup> + <actionGroup ref="AdminCatalogPriceRuleSaveAndApplyActionGroup" stepKey="saveAndApplyFirstPriceRule"/> + + <!-- Create price rule for second configurable product option --> + <actionGroup ref="AdminOpenNewCatalogPriceRuleFormPageActionGroup" stepKey="startCreatingThirdPriceRule"/> + <actionGroup ref="AdminCatalogPriceRuleFillMainInfoActionGroup" stepKey="fillMainInfoForThirdPriceRule"> + <argument name="name" value="{{_defaultCatalogRule.name}}"/> + <argument name="description" value="{{_defaultCatalogRule.description}}"/> + <argument name="groups" value="'NOT LOGGED IN'"/> + </actionGroup> + <actionGroup ref="CreateCatalogPriceRuleConditionWithAttributeAndOptionActionGroup" stepKey="fillConditionsForThirdPriceRule"> + <argument name="attributeName" value="$$createConfigProductAttribute.attribute[frontend_labels][0][label]$$"/> + <argument name="targetSelectValue" value="$$createConfigProductAttributeSecondOption.option[store_labels][1][label]$$"/> + <argument name="indexA" value="1"/> + <argument name="indexB" value="1"/> + </actionGroup> + <actionGroup ref="AdminCatalogPriceRuleFillActionsActionGroup" stepKey="fillActionsForThirdPriceRule"/> + <actionGroup ref="AdminCatalogPriceRuleSaveAndApplyActionGroup" stepKey="saveAndApplyThirdPriceRule"/> + + <!-- Create price rule for third configurable product option --> + <actionGroup ref="AdminOpenNewCatalogPriceRuleFormPageActionGroup" stepKey="startCreatingSecondPriceRule"/> + <actionGroup ref="AdminCatalogPriceRuleFillMainInfoActionGroup" stepKey="fillMainInfoForSecondPriceRule"> + <argument name="name" value="{{CatalogRuleWithoutDiscount.name}}"/> + <argument name="description" value="{{CatalogRuleWithoutDiscount.description}}"/> + <argument name="groups" value="'NOT LOGGED IN'"/> + </actionGroup> + <actionGroup ref="CreateCatalogPriceRuleConditionWithAttributeAndOptionActionGroup" stepKey="fillConditionsForSecondPriceRule"> + <argument name="attributeName" value="$$createConfigProductAttribute.attribute[frontend_labels][0][label]$$"/> + <argument name="targetSelectValue" value="$$createConfigProductAttributeThirdOption.option[store_labels][1][label]$$"/> + <argument name="indexA" value="1"/> + <argument name="indexB" value="1"/> + </actionGroup> + <actionGroup ref="AdminCatalogPriceRuleFillActionsActionGroup" stepKey="fillActionsForSecondPriceRule"> + <argument name="apply" value="{{CatalogRuleWithoutDiscount.simple_action}}"/> + <argument name="discountAmount" value="{{CatalogRuleWithoutDiscount.discount_amount}}"/> + </actionGroup> + <actionGroup ref="AdminCatalogPriceRuleSaveAndApplyActionGroup" stepKey="saveAndApplySecondPriceRule"/> + + <actionGroup ref="AdminReindexAndFlushCache" stepKey="reindexAndFlushCache"/> + + <!-- Assert product in storefront product page --> + <amOnPage url="{{StorefrontProductPage.url($$createConfigProduct.custom_attributes[url_key]$$)}}" stepKey="amOnProductPage"/> + <waitForPageLoad stepKey="waitForProductPageLoad"/> + <actionGroup ref="StorefrontAssertUpdatedProductPriceInStorefrontProductPageActionGroup" stepKey="assertUpdatedProductPriceInStorefrontProductPage"> + <argument name="productName" value="$$createConfigProduct.name$$"/> + <argument name="expectedPrice" value="As low as ${{CatalogRuleToFixed.discount_amount}}"/> + </actionGroup> + + <executeJS function="return '$' + ({{CatalogRuleToFixed.discount_amount}}).toFixed(2);" stepKey="firstOptionPrice"/> + <executeJS function="return '$' + ({{ApiConfigurableProduct.price}} * (100 - {{_defaultCatalogRule.discount_amount}})/100).toFixed(2);" stepKey="secondOptionPrice"/> + + <!-- Assert product options price in storefront product page --> + <actionGroup ref="StorefrontAssertCatalogPriceRuleAppliedToProductOptionActionGroup" stepKey="assertCatalogPriceRuleAppliedToFirstProductOption"> + <argument name="option" value="$$createConfigProductAttributeFirstOption.option[store_labels][1][label]$$"/> + <argument name="expectedPrice" value="{$firstOptionPrice} Regular Price ${{ApiConfigurableProduct.price}}"/> + </actionGroup> + + <actionGroup ref="StorefrontAssertCatalogPriceRuleAppliedToProductOptionActionGroup" stepKey="assertCatalogPriceRuleAppliedToSecondProductOption"> + <argument name="option" value="$$createConfigProductAttributeSecondOption.option[store_labels][1][label]$$"/> + <argument name="expectedPrice" value="{$secondOptionPrice} Regular Price ${{ApiConfigurableProduct.price}}"/> + </actionGroup> + + <actionGroup ref="StorefrontAssertCatalogPriceRuleAppliedToProductOptionActionGroup" stepKey="assertCatalogPriceRuleAppliedToThirdProductOption"> + <argument name="option" value="$$createConfigProductAttributeThirdOption.option[store_labels][1][label]$$"/> + <argument name="expectedPrice" value="{{ApiConfigurableProduct.price}}"/> + </actionGroup> + + <!-- Add product with selected option to the cart --> + <actionGroup ref="StorefrontProductPageSelectDropDownOptionValueActionGroup" stepKey="selectFirstOptionValue"> + <argument name="attributeLabel" value="$$createConfigProductAttribute.default_frontend_label$$"/> + <argument name="optionLabel" value="$$createConfigProductAttributeFirstOption.option[store_labels][1][label]$$"/> + </actionGroup> + <actionGroup ref="AddToCartFromStorefrontProductPageActionGroup" stepKey="addFirstOptionToCart"> + <argument name="productName" value="$$createConfigProduct.name$$"/> + </actionGroup> + + <actionGroup ref="StorefrontProductPageSelectDropDownOptionValueActionGroup" stepKey="selectSecondOptionValue"> + <argument name="attributeLabel" value="$$createConfigProductAttribute.default_frontend_label$$"/> + <argument name="optionLabel" value="$$createConfigProductAttributeSecondOption.option[store_labels][1][label]$$"/> + </actionGroup> + <actionGroup ref="AddToCartFromStorefrontProductPageActionGroup" stepKey="addSecondOptionToCart"> + <argument name="productName" value="$$createConfigProduct.name$$"/> + </actionGroup> + + <actionGroup ref="StorefrontProductPageSelectDropDownOptionValueActionGroup" stepKey="selectThirdOptionValue"> + <argument name="attributeLabel" value="$$createConfigProductAttribute.default_frontend_label$$"/> + <argument name="optionLabel" value="$$createConfigProductAttributeThirdOption.option[store_labels][1][label]$$"/> + </actionGroup> + <actionGroup ref="AddToCartFromStorefrontProductPageActionGroup" stepKey="addThirdOptionToCart"> + <argument name="productName" value="$$createConfigProduct.name$$"/> + </actionGroup> + + <!--Assert product price in the cart --> + <actionGroup ref="StorefrontOpenCartFromMinicartActionGroup" stepKey="openCartPage"/> + <waitForElementVisible selector="{{CheckoutCartProductSection.ProductPriceByOption($$createConfigProductAttributeFirstOption.option[store_labels][1][label]$$)}}" stepKey="waitForPriceAppears"/> + <see userInput="{$firstOptionPrice}" selector="{{CheckoutCartProductSection.ProductPriceByOption($$createConfigProductAttributeFirstOption.option[store_labels][1][label]$$)}}" stepKey="assertProductPriceForFirstProductOption"/> + <see userInput="{$secondOptionPrice}" selector="{{CheckoutCartProductSection.ProductPriceByOption($$createConfigProductAttributeSecondOption.option[store_labels][1][label]$$)}}" stepKey="assertProductPriceForSecondProductOption"/> + <see userInput="{{ApiConfigurableProduct.price}}" selector="{{CheckoutCartProductSection.ProductPriceByOption($$createConfigProductAttributeThirdOption.option[store_labels][1][label]$$)}}" stepKey="assertProductPriceForThirdProductOption"/> + </test> +</tests> diff --git a/app/code/Magento/CatalogRuleConfigurable/Test/Mftf/Test/AdminApplyCatalogRuleForConfigurableProductWithOptionsTest.xml b/app/code/Magento/CatalogRuleConfigurable/Test/Mftf/Test/AdminApplyCatalogRuleForConfigurableProductWithOptionsTest.xml deleted file mode 100644 index bfe7aadeca5ec..0000000000000 --- a/app/code/Magento/CatalogRuleConfigurable/Test/Mftf/Test/AdminApplyCatalogRuleForConfigurableProductWithOptionsTest.xml +++ /dev/null @@ -1,221 +0,0 @@ -<?xml version="1.0" encoding="UTF-8"?> -<!-- - /** - * Copyright © Magento, Inc. All rights reserved. - * See COPYING.txt for license details. - */ ---> - -<tests xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/testSchema.xsd"> - <test name="AdminApplyCatalogRuleForConfigurableProductWithOptionsTest"> - <annotations> - <features value="CatalogRuleConfigurable"/> - <stories value="Apply catalog price rule"/> - <title value="Apply catalog price rule for configurable product with options"/> - <description value="Admin should be able to apply the catalog rule for configurable product with options"/> - <severity value="CRITICAL"/> - <testCaseId value="MC-14062"/> - <group value="catalogRuleConfigurable"/> - <group value="mtf_migrated"/> - <skip> - <issueId value="MC-17140"/> - </skip> - </annotations> - <before> - <!-- Create category --> - <createData entity="SimpleSubCategory" stepKey="simpleCategory"/> - - <!-- Create configurable product with three options --> - <createData entity="ApiConfigurableProduct" stepKey="createConfigProduct"> - <requiredEntity createDataKey="simpleCategory"/> - </createData> - - <createData entity="productAttributeWithTwoOptions" stepKey="createConfigProductAttribute"/> - <createData entity="productAttributeOption1" stepKey="createConfigProductAttributeFirstOption"> - <requiredEntity createDataKey="createConfigProductAttribute"/> - </createData> - <createData entity="productAttributeOption2" stepKey="createConfigProductAttributeSecondOption"> - <requiredEntity createDataKey="createConfigProductAttribute"/> - </createData> - <createData entity="productAttributeOption3" stepKey="createConfigProductAttributeThirdOption"> - <requiredEntity createDataKey="createConfigProductAttribute"/> - </createData> - - <createData entity="AddToDefaultSet" stepKey="createConfigAddToAttributeSet"> - <requiredEntity createDataKey="createConfigProductAttribute"/> - </createData> - - <getData entity="ProductAttributeOptionGetter" index="1" stepKey="getConfigAttributeFirstOption"> - <requiredEntity createDataKey="createConfigProductAttribute"/> - </getData> - <getData entity="ProductAttributeOptionGetter" index="2" stepKey="getConfigAttributeSecondOption"> - <requiredEntity createDataKey="createConfigProductAttribute"/> - </getData> - <getData entity="ProductAttributeOptionGetter" index="3" stepKey="getConfigAttributeThirdOption"> - <requiredEntity createDataKey="createConfigProductAttribute"/> - </getData> - - <!-- Create three child products --> - <createData entity="ApiSimpleOne" stepKey="createConfigFirstChildProduct"> - <requiredEntity createDataKey="createConfigProductAttribute"/> - <requiredEntity createDataKey="getConfigAttributeFirstOption"/> - </createData> - - <createData entity="ApiSimpleOne" stepKey="createConfigSecondChildProduct"> - <requiredEntity createDataKey="createConfigProductAttribute"/> - <requiredEntity createDataKey="getConfigAttributeSecondOption"/> - </createData> - - <createData entity="ApiSimpleOne" stepKey="createConfigThirdChildProduct"> - <requiredEntity createDataKey="createConfigProductAttribute"/> - <requiredEntity createDataKey="getConfigAttributeThirdOption"/> - </createData> - - <createData entity="ConfigurableProductTwoOptions" stepKey="createConfigProductOption"> - <requiredEntity createDataKey="createConfigProduct"/> - <requiredEntity createDataKey="createConfigProductAttribute"/> - <requiredEntity createDataKey="getConfigAttributeFirstOption"/> - <requiredEntity createDataKey="getConfigAttributeSecondOption"/> - <requiredEntity createDataKey="getConfigAttributeThirdOption"/> - </createData> - - <createData entity="ConfigurableProductAddChild" stepKey="createConfigProductAddFirstChild"> - <requiredEntity createDataKey="createConfigProduct"/> - <requiredEntity createDataKey="createConfigFirstChildProduct"/> - </createData> - <createData entity="ConfigurableProductAddChild" stepKey="createConfigProductAddSecondChild"> - <requiredEntity createDataKey="createConfigProduct"/> - <requiredEntity createDataKey="createConfigSecondChildProduct"/> - </createData> - <createData entity="ConfigurableProductAddChild" stepKey="createConfigProductAddThirdChild"> - <requiredEntity createDataKey="createConfigProduct"/> - <requiredEntity createDataKey="createConfigThirdChildProduct"/> - </createData> - - <!-- Login as Admin --> - <actionGroup ref="LoginAsAdmin" stepKey="loginToAdminPanel"/> - </before> - <after> - <!-- Delete created data --> - <deleteData createDataKey="createConfigProduct" stepKey="deleteConfigProduct"/> - <deleteData createDataKey="createConfigFirstChildProduct" stepKey="deleteFirstSimpleProduct"/> - <deleteData createDataKey="createConfigSecondChildProduct" stepKey="deleteSecondSimpleProduct"/> - <deleteData createDataKey="createConfigThirdChildProduct" stepKey="deleteThirdSimpleProduct"/> - <deleteData createDataKey="createConfigProductAttribute" stepKey="deleteConfigProductAttribute"/> - <deleteData createDataKey="simpleCategory" stepKey="deleteCategory"/> - - <!-- Delete created price rules --> - <amOnPage url="{{CatalogRulePage.url}}" stepKey="goToPriceRulePage"/> - <actionGroup ref="deleteEntitySecondaryGrid" stepKey="deletePriceRuleForFirstOption"> - <argument name="name" value="{{CatalogRuleToFixed.name}}"/> - <argument name="searchInput" value="{{AdminSecondaryGridSection.catalogRuleIdentifierSearch}}"/> - </actionGroup> - - <actionGroup ref="deleteEntitySecondaryGrid" stepKey="deletePriceRuleForSecondOption"> - <argument name="name" value="{{_defaultCatalogRule.name}}"/> - <argument name="searchInput" value="{{AdminSecondaryGridSection.catalogRuleIdentifierSearch}}"/> - </actionGroup> - - <actionGroup ref="deleteEntitySecondaryGrid" stepKey="deletePriceRuleForThirdOption"> - <argument name="name" value="{{CatalogRuleWithoutDiscount.name}}"/> - <argument name="searchInput" value="{{AdminSecondaryGridSection.catalogRuleIdentifierSearch}}"/> - </actionGroup> - - <!-- Log out --> - <actionGroup ref="logout" stepKey="logout"/> - </after> - - <!-- Create price rule for first configurable product option --> - <actionGroup ref="NewCatalogPriceRuleByUIActionGroup" stepKey="createFirstPriceRule"> - <argument name="catalogRule" value="CatalogRuleToFixed"/> - </actionGroup> - <actionGroup ref="SelectNotLoggedInCustomerGroupActionGroup" stepKey="selectNotLoggedInCustomerGroupForFirstPriceRule"/> - <actionGroup ref="CreateCatalogPriceRuleConditionWithAttributeAndOptionActionGroup" stepKey="createFirstCatalogPriceRuleCondition"> - <argument name="attributeName" value="$$createConfigProductAttribute.attribute[frontend_labels][0][label]$$"/> - <argument name="targetSelectValue" value="$$createConfigProductAttributeFirstOption.option[store_labels][1][label]$$"/> - <argument name="indexA" value="1"/> - <argument name="indexB" value="1"/> - </actionGroup> - <click selector="{{AdminNewCatalogPriceRule.saveAndApply}}" stepKey="saveAndApplyFirstPriceRule"/> - <see selector="{{AdminCategoryMessagesSection.SuccessMessage}}" userInput="You saved the rule." stepKey="assertSuccessMessageForFirstPriceRule"/> - - <!-- Create price rule for second configurable product option --> - <actionGroup ref="NewCatalogPriceRuleByUIActionGroup" stepKey="createSecondPriceRule"> - <argument name="catalogRule" value="_defaultCatalogRule"/> - </actionGroup> - <actionGroup ref="SelectNotLoggedInCustomerGroupActionGroup" stepKey="selectNotLoggedInCustomerGroupForSecondPriceRule"/> - <actionGroup ref="CreateCatalogPriceRuleConditionWithAttributeAndOptionActionGroup" stepKey="createSecondCatalogPriceRuleCondition"> - <argument name="attributeName" value="$$createConfigProductAttribute.attribute[frontend_labels][0][label]$$"/> - <argument name="targetSelectValue" value="$$createConfigProductAttributeSecondOption.option[store_labels][1][label]$$"/> - <argument name="indexA" value="1"/> - <argument name="indexB" value="1"/> - </actionGroup> - <click selector="{{AdminNewCatalogPriceRule.saveAndApply}}" stepKey="saveAndApplySecondPriceRule"/> - <see selector="{{AdminCategoryMessagesSection.SuccessMessage}}" userInput="You saved the rule." stepKey="assertSuccessMessageForSecondPriceRule"/> - - <!-- Create price rule for third configurable product option --> - <actionGroup ref="NewCatalogPriceRuleByUIActionGroup" stepKey="createThirdPriceRule"> - <argument name="catalogRule" value="CatalogRuleWithoutDiscount"/> - </actionGroup> - <actionGroup ref="SelectNotLoggedInCustomerGroupActionGroup" stepKey="selectNotLoggedInCustomerGroupForThirdPriceRule"/> - <actionGroup ref="CreateCatalogPriceRuleConditionWithAttributeAndOptionActionGroup" stepKey="createThirdCatalogPriceRuleCondition"> - <argument name="attributeName" value="$$createConfigProductAttribute.attribute[frontend_labels][0][label]$$"/> - <argument name="targetSelectValue" value="$$createConfigProductAttributeThirdOption.option[store_labels][1][label]$$"/> - <argument name="indexA" value="1"/> - <argument name="indexB" value="1"/> - </actionGroup> - <click selector="{{AdminNewCatalogPriceRule.saveAndApply}}" stepKey="saveAndApplyThirdPriceRule"/> - <see selector="{{AdminCategoryMessagesSection.SuccessMessage}}" userInput="You saved the rule." stepKey="assertSuccessMessageForThirdPriceRule"/> - - <!-- Run full reindex and clear caches --> - <magentoCLI command="indexer:reindex" stepKey="reindex"/> - <magentoCLI command="cache:flush" stepKey="flushCache"/> - - <!-- Assert product in storefront product page --> - <amOnPage url="$$createConfigProduct.custom_attributes[url_key]$$.html" stepKey="amOnProductPage"/> - <waitForPageLoad stepKey="waitForProductPageLoad"/> - <actionGroup ref="StorefrontAssertUpdatedProductPriceInStorefrontProductPageActionGroup" stepKey="assertUpdatedProductPriceInStorefrontProductPage"> - <argument name="productName" value="$$createConfigProduct.name$$"/> - <argument name="expectedPrice" value="{{CatalogRuleToFixed.discount_amount}}"/> - </actionGroup> - - <!-- Assert product options price in storefront product page --> - <actionGroup ref="StorefrontAssertCatalogPriceRuleAppliedToProductOptionActionGroup" stepKey="assertCatalogPriceRuleAppliedToFirstProductOption"> - <argument name="option" value="$$createConfigProductAttributeFirstOption.option[store_labels][1][label]$$"/> - <argument name="expectedPrice" value="{{CatalogRuleToFixed.discount_amount}}"/> - </actionGroup> - - <actionGroup ref="StorefrontAssertCatalogPriceRuleAppliedToProductOptionActionGroup" stepKey="assertCatalogPriceRuleAppliedToSecondProductOption"> - <argument name="option" value="$$createConfigProductAttributeSecondOption.option[store_labels][1][label]$$"/> - <argument name="expectedPrice" value="$110.70"/> - </actionGroup> - - <actionGroup ref="StorefrontAssertCatalogPriceRuleAppliedToProductOptionActionGroup" stepKey="assertCatalogPriceRuleAppliedToThirdProductOption"> - <argument name="option" value="$$createConfigProductAttributeThirdOption.option[store_labels][1][label]$$"/> - <argument name="expectedPrice" value="{{ApiConfigurableProduct.price}}"/> - </actionGroup> - - <!-- Add product with selected option to the cart --> - <actionGroup ref="StorefrontAddProductWithSelectedConfigurableOptionToCartActionGroup" stepKey="addProductWithSelectedFirstOptionToCart"> - <argument name="product" value="$$createConfigProduct$$"/> - <argument name="option" value="$$createConfigProductAttributeFirstOption.option[store_labels][1][label]$$"/> - </actionGroup> - - <actionGroup ref="StorefrontAddProductWithSelectedConfigurableOptionToCartActionGroup" stepKey="addProductWithSelectedSecondOptionToCart"> - <argument name="product" value="$$createConfigProduct$$"/> - <argument name="option" value="$$createConfigProductAttributeSecondOption.option[store_labels][1][label]$$"/> - </actionGroup> - - <actionGroup ref="StorefrontAddProductWithSelectedConfigurableOptionToCartActionGroup" stepKey="addProductWithSelectedThirdOptionToCart"> - <argument name="product" value="$$createConfigProduct$$"/> - <argument name="option" value="$$createConfigProductAttributeThirdOption.option[store_labels][1][label]$$"/> - </actionGroup> - - <!--Assert product price in the cart --> - <amOnPage url="/checkout/cart/" stepKey="amOnShoppingCartPage"/> - <waitForPageLoad stepKey="waitForShoppingCartPageLoad"/> - <see userInput="{{CatalogRuleToFixed.discount_amount}}" selector="{{CheckoutCartProductSection.ProductPriceByOption($$createConfigProductAttributeFirstOption.option[store_labels][1][label]$$)}}" stepKey="assertProductPriceForFirstProductOption"/> - <see userInput="$110.70" selector="{{CheckoutCartProductSection.ProductPriceByOption($$createConfigProductAttributeSecondOption.option[store_labels][1][label]$$)}}" stepKey="assertProductPriceForSecondProductOption"/> - <see userInput="{{ApiConfigurableProduct.price}}" selector="{{CheckoutCartProductSection.ProductPriceByOption($$createConfigProductAttributeThirdOption.option[store_labels][1][label]$$)}}" stepKey="assertProductPriceForThirdProductOption"/> - </test> -</tests> diff --git a/app/code/Magento/CatalogRuleConfigurable/registration.php b/app/code/Magento/CatalogRuleConfigurable/registration.php index 0cc791e23a9a4..5f60111f246f1 100644 --- a/app/code/Magento/CatalogRuleConfigurable/registration.php +++ b/app/code/Magento/CatalogRuleConfigurable/registration.php @@ -4,6 +4,6 @@ * See COPYING.txt for license details. */ -use \Magento\Framework\Component\ComponentRegistrar; +use Magento\Framework\Component\ComponentRegistrar; ComponentRegistrar::register(ComponentRegistrar::MODULE, 'Magento_CatalogRuleConfigurable', __DIR__); diff --git a/app/code/Magento/CatalogRuleGraphQl/README.md b/app/code/Magento/CatalogRuleGraphQl/README.md new file mode 100644 index 0000000000000..6f9761fedecbb --- /dev/null +++ b/app/code/Magento/CatalogRuleGraphQl/README.md @@ -0,0 +1,3 @@ +# CatalogRuleGraphQl + +The *Magento_CatalogRuleGraphQl* module applies catalog rules to products for GraphQL requests. \ No newline at end of file diff --git a/app/code/Magento/CatalogRuleGraphQl/composer.json b/app/code/Magento/CatalogRuleGraphQl/composer.json new file mode 100644 index 0000000000000..f2aae6c79d16b --- /dev/null +++ b/app/code/Magento/CatalogRuleGraphQl/composer.json @@ -0,0 +1,24 @@ +{ + "name": "magento/module-catalog-rule-graph-ql", + "description": "N/A", + "type": "magento2-module", + "require": { + "php": "~7.1.3||~7.2.0||~7.3.0", + "magento/framework": "*" + }, + "suggest": { + "magento/module-catalog-rule": "*" + }, + "license": [ + "OSL-3.0", + "AFL-3.0" + ], + "autoload": { + "files": [ + "registration.php" + ], + "psr-4": { + "Magento\\CatalogRuleGraphQl\\": "" + } + } +} diff --git a/app/code/Magento/CatalogRuleGraphQl/etc/graphql/events.xml b/app/code/Magento/CatalogRuleGraphQl/etc/graphql/events.xml new file mode 100644 index 0000000000000..0d0a1fef5354f --- /dev/null +++ b/app/code/Magento/CatalogRuleGraphQl/etc/graphql/events.xml @@ -0,0 +1,15 @@ +<?xml version="1.0"?> +<!-- +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +--> +<config xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="urn:magento:framework:Event/etc/events.xsd"> + <event name="catalog_product_get_final_price"> + <observer name="catalogrule" instance="Magento\CatalogRule\Observer\ProcessFrontFinalPriceObserver" /> + </event> + <event name="prepare_catalog_product_collection_prices"> + <observer name="catalogrule" instance="Magento\CatalogRule\Observer\PrepareCatalogProductCollectionPricesObserver" /> + </event> +</config> diff --git a/app/code/Magento/CatalogRuleGraphQl/etc/module.xml b/app/code/Magento/CatalogRuleGraphQl/etc/module.xml new file mode 100644 index 0000000000000..affc22f35a738 --- /dev/null +++ b/app/code/Magento/CatalogRuleGraphQl/etc/module.xml @@ -0,0 +1,10 @@ +<?xml version="1.0"?> +<!-- +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +--> +<config xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="urn:magento:framework:Module/etc/module.xsd"> + <module name="Magento_CatalogRuleGraphQl" /> +</config> diff --git a/app/code/Magento/CatalogRuleGraphQl/registration.php b/app/code/Magento/CatalogRuleGraphQl/registration.php new file mode 100644 index 0000000000000..df4dcd1cb47b6 --- /dev/null +++ b/app/code/Magento/CatalogRuleGraphQl/registration.php @@ -0,0 +1,9 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ + +use Magento\Framework\Component\ComponentRegistrar; + +ComponentRegistrar::register(ComponentRegistrar::MODULE, 'Magento_CatalogRuleGraphQl', __DIR__); diff --git a/app/code/Magento/CatalogSearch/Model/Indexer/Fulltext/Plugin/Attribute.php b/app/code/Magento/CatalogSearch/Model/Indexer/Fulltext/Plugin/Attribute.php index 86dccf8cfe559..7b5d43ece922d 100644 --- a/app/code/Magento/CatalogSearch/Model/Indexer/Fulltext/Plugin/Attribute.php +++ b/app/code/Magento/CatalogSearch/Model/Indexer/Fulltext/Plugin/Attribute.php @@ -6,6 +6,11 @@ namespace Magento\CatalogSearch\Model\Indexer\Fulltext\Plugin; use Magento\CatalogSearch\Model\Indexer\Fulltext; +use Magento\Framework\Model\AbstractModel; +use Magento\Catalog\Model\ResourceModel\Attribute as AttributeResourceModel; +use Magento\Framework\Search\Request\Config; +use Magento\Framework\Indexer\IndexerRegistry; +use Magento\Catalog\Api\Data\EavAttributeInterface; /** * Catalog search indexer plugin for catalog attribute. @@ -13,7 +18,7 @@ class Attribute extends AbstractPlugin { /** - * @var \Magento\Framework\Search\Request\Config + * @var Config */ private $config; @@ -33,12 +38,12 @@ class Attribute extends AbstractPlugin private $saveIsNew; /** - * @param \Magento\Framework\Indexer\IndexerRegistry $indexerRegistry - * @param \Magento\Framework\Search\Request\Config $config + * @param IndexerRegistry $indexerRegistry + * @param Config $config */ public function __construct( - \Magento\Framework\Indexer\IndexerRegistry $indexerRegistry, - \Magento\Framework\Search\Request\Config $config + IndexerRegistry $indexerRegistry, + Config $config ) { parent::__construct($indexerRegistry); $this->config = $config; @@ -47,36 +52,32 @@ public function __construct( /** * Check if indexer invalidation is needed on attribute save (searchable flag change) * - * @param \Magento\Catalog\Model\ResourceModel\Attribute $subject - * @param \Magento\Framework\Model\AbstractModel $attribute + * @param AttributeResourceModel $subject + * @param AbstractModel $attribute * * @return void * @SuppressWarnings(PHPMD.UnusedFormalParameter) */ public function beforeSave( - \Magento\Catalog\Model\ResourceModel\Attribute $subject, - \Magento\Framework\Model\AbstractModel $attribute + AttributeResourceModel $subject, + AbstractModel $attribute ) { $this->saveIsNew = $attribute->isObjectNew(); - $this->saveNeedInvalidation = ( - $attribute->dataHasChangedFor('is_searchable') - || $attribute->dataHasChangedFor('is_filterable') - || $attribute->dataHasChangedFor('is_visible_in_advanced_search') - ); + $this->saveNeedInvalidation = $this->shouldInvalidateSearchIndex($attribute); } /** * Invalidate indexer on attribute save (searchable flag change) * - * @param \Magento\Catalog\Model\ResourceModel\Attribute $subject - * @param \Magento\Catalog\Model\ResourceModel\Attribute $result + * @param AttributeResourceModel $subject + * @param AttributeResourceModel $result * - * @return \Magento\Catalog\Model\ResourceModel\Attribute + * @return AttributeResourceModel * @SuppressWarnings(PHPMD.UnusedFormalParameter) */ public function afterSave( - \Magento\Catalog\Model\ResourceModel\Attribute $subject, - \Magento\Catalog\Model\ResourceModel\Attribute $result + AttributeResourceModel $subject, + AttributeResourceModel $result ) { if ($this->saveNeedInvalidation) { $this->indexerRegistry->get(Fulltext::INDEXER_ID)->invalidate(); @@ -91,15 +92,15 @@ public function afterSave( /** * Check if indexer invalidation is needed on searchable attribute delete * - * @param \Magento\Catalog\Model\ResourceModel\Attribute $subject - * @param \Magento\Framework\Model\AbstractModel $attribute + * @param AttributeResourceModel $subject + * @param AbstractModel $attribute * * @return void * @SuppressWarnings(PHPMD.UnusedFormalParameter) */ public function beforeDelete( - \Magento\Catalog\Model\ResourceModel\Attribute $subject, - \Magento\Framework\Model\AbstractModel $attribute + AttributeResourceModel $subject, + AbstractModel $attribute ) { $this->deleteNeedInvalidation = !$attribute->isObjectNew() && $attribute->getIsSearchable(); } @@ -107,19 +108,45 @@ public function beforeDelete( /** * Invalidate indexer on searchable attribute delete * - * @param \Magento\Catalog\Model\ResourceModel\Attribute $subject - * @param \Magento\Catalog\Model\ResourceModel\Attribute $result + * @param AttributeResourceModel $subject + * @param AttributeResourceModel $result * - * @return \Magento\Catalog\Model\ResourceModel\Attribute + * @return AttributeResourceModel * @SuppressWarnings(PHPMD.UnusedFormalParameter) */ public function afterDelete( - \Magento\Catalog\Model\ResourceModel\Attribute $subject, - \Magento\Catalog\Model\ResourceModel\Attribute $result + AttributeResourceModel $subject, + AttributeResourceModel $result ) { if ($this->deleteNeedInvalidation) { $this->indexerRegistry->get(Fulltext::INDEXER_ID)->invalidate(); } return $result; } + + /** + * Check if catalogsearch_fulltext index should be invalidated. + * + * @param AbstractModel $attribute + * @return bool + */ + private function shouldInvalidateSearchIndex( + AbstractModel $attribute + ):bool { + $shouldInvalidate = false; + $fields = [ + EavAttributeInterface::IS_SEARCHABLE, + EavAttributeInterface::IS_FILTERABLE, + EavAttributeInterface::IS_VISIBLE_IN_ADVANCED_SEARCH, + ]; + foreach ($fields as $field) { + if ($this->saveIsNew && $attribute->getData($field) + || !$this->saveIsNew && $attribute->dataHasChangedFor($field)) { + $shouldInvalidate = true; + break; + } + } + + return $shouldInvalidate; + } } diff --git a/app/code/Magento/CatalogSearch/Model/Layer/Filter/Attribute.php b/app/code/Magento/CatalogSearch/Model/Layer/Filter/Attribute.php index d8947ac4224a8..b1aecc6885bf0 100644 --- a/app/code/Magento/CatalogSearch/Model/Layer/Filter/Attribute.php +++ b/app/code/Magento/CatalogSearch/Model/Layer/Filter/Attribute.php @@ -67,7 +67,8 @@ public function apply(\Magento\Framework\App\RequestInterface $request) $labels = []; foreach ((array) $attributeValue as $value) { - $labels[] = (array) $this->getOptionText($value); + $label = $this->getOptionText($value); + $labels[] = is_array($label) ? $label : [$label]; } $label = implode(',', array_unique(array_merge(...$labels))); $this->getLayer() diff --git a/app/code/Magento/CatalogSearch/Model/Layer/Filter/Decimal.php b/app/code/Magento/CatalogSearch/Model/Layer/Filter/Decimal.php index 3b0c4dfb6df2f..e2cb9174b5e97 100644 --- a/app/code/Magento/CatalogSearch/Model/Layer/Filter/Decimal.php +++ b/app/code/Magento/CatalogSearch/Model/Layer/Filter/Decimal.php @@ -14,9 +14,6 @@ */ class Decimal extends AbstractFilter { - /** Decimal delta for filter */ - private const DECIMAL_DELTA = 0.001; - /** * @var \Magento\Framework\Pricing\PriceCurrencyInterface */ @@ -75,17 +72,11 @@ public function apply(\Magento\Framework\App\RequestInterface $request) list($from, $to) = explode('-', $filter); - // When the range is 10-20 we only need to get products that are in the 10-19.99 range. - $toValue = $to; - if (!empty($toValue) && $from !== $toValue) { - $toValue -= self::DECIMAL_DELTA; - } - $this->getLayer() ->getProductCollection() ->addFieldToFilter( $this->getAttributeModel()->getAttributeCode(), - ['from' => $from, 'to' => $toValue] + ['from' => $from, 'to' => $to] ); $this->getLayer()->getState()->addFilter( @@ -122,7 +113,7 @@ protected function _getItemsData() $from = ''; } if ($to == '*') { - $to = ''; + $to = null; } $label = $this->renderRangeLabel(empty($from) ? 0 : $from, $to); $value = $from . '-' . $to; @@ -149,7 +140,7 @@ protected function _getItemsData() protected function renderRangeLabel($fromPrice, $toPrice) { $formattedFromPrice = $this->priceCurrency->format($fromPrice); - if ($toPrice === '') { + if ($toPrice === null) { return __('%1 and above', $formattedFromPrice); } else { if ($fromPrice != $toPrice) { diff --git a/app/code/Magento/CatalogSearch/Model/Layer/Filter/Price.php b/app/code/Magento/CatalogSearch/Model/Layer/Filter/Price.php index 66d9281ed38e2..332bb991bf29f 100644 --- a/app/code/Magento/CatalogSearch/Model/Layer/Filter/Price.php +++ b/app/code/Magento/CatalogSearch/Model/Layer/Filter/Price.php @@ -141,7 +141,7 @@ public function apply(\Magento\Framework\App\RequestInterface $request) list($from, $to) = $filter; $this->getLayer()->getProductCollection()->addFieldToFilter( - $this->getAttributeModel()->getAttributeCode(), + 'price', ['from' => $from, 'to' => empty($to) || $from == $to ? $to : $to - self::PRICE_DELTA] ); diff --git a/app/code/Magento/CatalogSearch/Model/ResourceModel/Fulltext/Collection.php b/app/code/Magento/CatalogSearch/Model/ResourceModel/Fulltext/Collection.php index 3506437ea038d..30a7c723940e2 100644 --- a/app/code/Magento/CatalogSearch/Model/ResourceModel/Fulltext/Collection.php +++ b/app/code/Magento/CatalogSearch/Model/ResourceModel/Fulltext/Collection.php @@ -385,6 +385,8 @@ public function addFieldToFilter($field, $condition = null) public function clear() { $this->searchResult = null; + $this->setFlag('has_category_filter', false); + return parent::clear(); } @@ -394,6 +396,8 @@ public function clear() protected function _reset() { $this->searchResult = null; + $this->setFlag('has_category_filter', false); + return parent::_reset(); } @@ -423,7 +427,11 @@ public function _loadEntities($printQuery = false, $logQuery = false) throw $e; } + $position = 0; foreach ($rows as $value) { + if ($this->getFlag('has_category_filter')) { + $value['cat_index_position'] = $position++; + } $object = $this->getNewEmptyItem()->setData($value); $this->addItem($object); if (isset($this->_itemsById[$object->getId()])) { @@ -432,6 +440,9 @@ public function _loadEntities($printQuery = false, $logQuery = false) $this->_itemsById[$object->getId()] = [$object]; } } + if ($this->getFlag('has_category_filter')) { + $this->setFlag('has_category_filter', false); + } return $this; } @@ -669,6 +680,7 @@ public function addCategoryFilter(\Magento\Catalog\Model\Category $category) if ($this->defaultFilterStrategyApplyChecker->isApplicable()) { parent::addCategoryFilter($category); } else { + $this->setFlag('has_category_filter', true); $this->_productLimitationPrice(); } diff --git a/app/code/Magento/CatalogSearch/Model/Search/FilterMapper/CustomAttributeStockStatusFilter.php b/app/code/Magento/CatalogSearch/Model/Search/FilterMapper/CustomAttributeStockStatusFilter.php new file mode 100644 index 0000000000000..28aa3df2d56b4 --- /dev/null +++ b/app/code/Magento/CatalogSearch/Model/Search/FilterMapper/CustomAttributeStockStatusFilter.php @@ -0,0 +1,100 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +declare(strict_types=1); + +namespace Magento\CatalogSearch\Model\Search\FilterMapper; + +use Magento\Catalog\Model\Product; +use Magento\CatalogSearch\Model\Adapter\Mysql\Filter\AliasResolver; +use Magento\Eav\Model\Config as EavConfig; +use Magento\Framework\DB\Select; +use Magento\Framework\Search\Request\FilterInterface; + +/** + * Add stock status filter for each requested filter + */ +class CustomAttributeStockStatusFilter +{ + /** + * Suffix to append to filter name in order to generate stock status table alias for JOIN clause + */ + private const STOCK_STATUS_TABLE_ALIAS_SUFFIX = '_stock_index'; + /** + * Attribute types to apply + */ + private const TARGET_ATTRIBUTE_TYPES = [ + 'select', + 'multiselect' + ]; + /** + * @var EavConfig + */ + private $eavConfig; + /** + * @var AliasResolver + */ + private $aliasResolver; + /** + * @var StockStatusQueryBuilder|null + */ + private $stockStatusQueryBuilder; + + /** + * @param EavConfig $eavConfig + * @param AliasResolver $aliasResolver + * @param StockStatusQueryBuilder $stockStatusQueryBuilder + */ + public function __construct( + EavConfig $eavConfig, + AliasResolver $aliasResolver, + StockStatusQueryBuilder $stockStatusQueryBuilder + ) { + $this->eavConfig = $eavConfig; + $this->aliasResolver = $aliasResolver; + $this->stockStatusQueryBuilder = $stockStatusQueryBuilder; + } + + /** + * Apply stock status filter to provided filter + * + * @param Select $select + * @param mixed $values + * @param FilterInterface[] $filters + * @return Select + */ + public function apply(Select $select, $values = null, FilterInterface ...$filters): Select + { + $select = clone $select; + foreach ($filters as $filter) { + if ($this->isApplicable($filter)) { + $mainTableAlias = $this->aliasResolver->getAlias($filter); + $stockTableAlias = $mainTableAlias . self::STOCK_STATUS_TABLE_ALIAS_SUFFIX; + $select = $this->stockStatusQueryBuilder->apply( + $select, + $mainTableAlias, + $stockTableAlias, + 'source_id', + $values + ); + } + } + return $select; + } + + /** + * Check if stock status filter is applicable to provided filter + * + * @param FilterInterface $filter + * @return bool + */ + private function isApplicable(FilterInterface $filter): bool + { + $attribute = $this->eavConfig->getAttribute(Product::ENTITY, $filter->getField()); + return $attribute + && $filter->getType() === FilterInterface::TYPE_TERM + && in_array($attribute->getFrontendInput(), self::TARGET_ATTRIBUTE_TYPES, true); + } +} diff --git a/app/code/Magento/CatalogSearch/Model/Search/FilterMapper/FilterMapper.php b/app/code/Magento/CatalogSearch/Model/Search/FilterMapper/FilterMapper.php index 7136fad5b19a9..1c4a803c1dd00 100644 --- a/app/code/Magento/CatalogSearch/Model/Search/FilterMapper/FilterMapper.php +++ b/app/code/Magento/CatalogSearch/Model/Search/FilterMapper/FilterMapper.php @@ -9,13 +9,14 @@ use Magento\CatalogSearch\Model\Search\SelectContainer\SelectContainer; use Magento\CatalogSearch\Model\Adapter\Mysql\Filter\AliasResolver; use Magento\CatalogInventory\Model\Stock; +use Magento\Framework\App\ObjectManager; /** - * Class FilterMapper * This class applies filters to Select based on SelectContainer configuration * - * @deprecated + * @deprecated MySQL search engine is not recommended. * @see \Magento\ElasticSearch + * @SuppressWarnings(PHPMD.CouplingBetweenObjects) */ class FilterMapper { @@ -43,6 +44,10 @@ class FilterMapper * @var StockStatusFilter */ private $stockStatusFilter; + /** + * @var CustomAttributeStockStatusFilter + */ + private $customAttributeStockStatusFilter; /** * @param AliasResolver $aliasResolver @@ -50,24 +55,27 @@ class FilterMapper * @param FilterStrategyInterface $filterStrategy * @param VisibilityFilter $visibilityFilter * @param StockStatusFilter $stockStatusFilter + * @param CustomAttributeStockStatusFilter|null $customAttributeStockStatusFilter */ public function __construct( AliasResolver $aliasResolver, CustomAttributeFilter $customAttributeFilter, FilterStrategyInterface $filterStrategy, VisibilityFilter $visibilityFilter, - StockStatusFilter $stockStatusFilter + StockStatusFilter $stockStatusFilter, + ?CustomAttributeStockStatusFilter $customAttributeStockStatusFilter = null ) { $this->aliasResolver = $aliasResolver; $this->customAttributeFilter = $customAttributeFilter; $this->filterStrategy = $filterStrategy; $this->visibilityFilter = $visibilityFilter; $this->stockStatusFilter = $stockStatusFilter; + $this->customAttributeStockStatusFilter = $customAttributeStockStatusFilter + ?? ObjectManager::getInstance()->get(CustomAttributeStockStatusFilter::class); } /** - * Applies filters to Select query in SelectContainer - * based on SelectContainer configuration + * Applies filters to Select query in SelectContainer based on SelectContainer configuration * * @param SelectContainer $selectContainer * @return SelectContainer @@ -79,22 +87,22 @@ public function applyFilters(SelectContainer $selectContainer) { $select = $selectContainer->getSelect(); - if ($selectContainer->hasCustomAttributesFilters()) { - $select = $this->customAttributeFilter->apply($select, ...$selectContainer->getCustomAttributesFilters()); - } - - $filterType = StockStatusFilter::FILTER_JUST_ENTITY; - if ($selectContainer->hasCustomAttributesFilters()) { - $filterType = StockStatusFilter::FILTER_ENTITY_AND_SUB_PRODUCTS; - } - $select = $this->stockStatusFilter->apply( $select, Stock::STOCK_IN_STOCK, - $filterType, + StockStatusFilter::FILTER_JUST_ENTITY, $selectContainer->isShowOutOfStockEnabled() ); + if ($selectContainer->hasCustomAttributesFilters()) { + $select = $this->customAttributeFilter->apply($select, ...$selectContainer->getCustomAttributesFilters()); + $select = $this->customAttributeStockStatusFilter->apply( + $select, + $selectContainer->isShowOutOfStockEnabled() ? null : Stock::STOCK_IN_STOCK, + ...$selectContainer->getCustomAttributesFilters() + ); + } + $appliedFilters = []; if ($selectContainer->hasVisibilityFilter()) { diff --git a/app/code/Magento/CatalogSearch/Model/Search/FilterMapper/StockStatusFilter.php b/app/code/Magento/CatalogSearch/Model/Search/FilterMapper/StockStatusFilter.php index 0e3ba0d4e669f..420c69a7325f6 100644 --- a/app/code/Magento/CatalogSearch/Model/Search/FilterMapper/StockStatusFilter.php +++ b/app/code/Magento/CatalogSearch/Model/Search/FilterMapper/StockStatusFilter.php @@ -6,6 +6,7 @@ namespace Magento\CatalogSearch\Model\Search\FilterMapper; +use Magento\Framework\App\ObjectManager; use Magento\Framework\App\ResourceConnection; use Magento\Framework\DB\Select; use Magento\Framework\Search\Adapter\Mysql\ConditionManager; @@ -13,10 +14,9 @@ use Magento\CatalogInventory\Api\StockRegistryInterface; /** - * Class StockStatusFilter * Adds filter by stock status to base select * - * @deprecated + * @deprecated MySQL search engine is not recommended. * @see \Magento\ElasticSearch */ class StockStatusFilter @@ -56,23 +56,31 @@ class StockStatusFilter * @var StockRegistryInterface */ private $stockRegistry; + /** + * @var StockStatusQueryBuilder + */ + private $stockStatusQueryBuilder; /** * @param ResourceConnection $resourceConnection * @param ConditionManager $conditionManager * @param StockConfigurationInterface $stockConfiguration * @param StockRegistryInterface $stockRegistry + * @param StockStatusQueryBuilder|null $stockStatusQueryBuilder */ public function __construct( ResourceConnection $resourceConnection, ConditionManager $conditionManager, StockConfigurationInterface $stockConfiguration, - StockRegistryInterface $stockRegistry + StockRegistryInterface $stockRegistry, + ?StockStatusQueryBuilder $stockStatusQueryBuilder = null ) { $this->resourceConnection = $resourceConnection; $this->conditionManager = $conditionManager; $this->stockConfiguration = $stockConfiguration; $this->stockRegistry = $stockRegistry; + $this->stockStatusQueryBuilder = $stockStatusQueryBuilder + ?? ObjectManager::getInstance()->get(StockStatusQueryBuilder::class); } /** @@ -94,99 +102,27 @@ public function apply(Select $select, $stockValues, $type, $showOutOfStockFlag) $select = clone $select; $mainTableAlias = $this->extractTableAliasFromSelect($select); - $this->addMainStockStatusJoin($select, $stockValues, $mainTableAlias, $showOutOfStockFlag); + $select = $this->stockStatusQueryBuilder->apply( + $select, + $mainTableAlias, + 'stock_index', + 'entity_id', + $showOutOfStockFlag ? null : $stockValues + ); if ($type === self::FILTER_ENTITY_AND_SUB_PRODUCTS) { - $this->addSubProductsStockStatusJoin($select, $stockValues, $mainTableAlias, $showOutOfStockFlag); + $select = $this->stockStatusQueryBuilder->apply( + $select, + $mainTableAlias, + 'sub_products_stock_index', + 'source_id', + $showOutOfStockFlag ? null : $stockValues + ); } return $select; } - /** - * Adds filter join for products by stock status - * In case when $showOutOfStockFlag is true - joins are still required to filter only enabled products - * - * @param Select $select - * @param array|int $stockValues - * @param string $mainTableAlias - * @param bool $showOutOfStockFlag - * @return void - */ - private function addMainStockStatusJoin(Select $select, $stockValues, $mainTableAlias, $showOutOfStockFlag) - { - $catalogInventoryTable = $this->resourceConnection->getTableName('cataloginventory_stock_status'); - $select->joinInner( - ['stock_index' => $catalogInventoryTable], - $this->conditionManager->combineQueries( - [ - sprintf('stock_index.product_id = %s.entity_id', $mainTableAlias), - $this->conditionManager->generateCondition( - 'stock_index.website_id', - '=', - $this->stockConfiguration->getDefaultScopeId() - ), - $showOutOfStockFlag - ? '' - : $this->conditionManager->generateCondition( - 'stock_index.stock_status', - is_array($stockValues) ? 'in' : '=', - $stockValues - ), - $this->conditionManager->generateCondition( - 'stock_index.stock_id', - '=', - (int) $this->stockRegistry->getStock()->getStockId() - ), - ], - Select::SQL_AND - ), - [] - ); - } - - /** - * Adds filter join for sub products by stock status - * In case when $showOutOfStockFlag is true - joins are still required to filter only enabled products - * - * @param Select $select - * @param array|int $stockValues - * @param string $mainTableAlias - * @param bool $showOutOfStockFlag - * @return void - */ - private function addSubProductsStockStatusJoin(Select $select, $stockValues, $mainTableAlias, $showOutOfStockFlag) - { - $catalogInventoryTable = $this->resourceConnection->getTableName('cataloginventory_stock_status'); - $select->joinInner( - ['sub_products_stock_index' => $catalogInventoryTable], - $this->conditionManager->combineQueries( - [ - sprintf('sub_products_stock_index.product_id = %s.source_id', $mainTableAlias), - $this->conditionManager->generateCondition( - 'sub_products_stock_index.website_id', - '=', - $this->stockConfiguration->getDefaultScopeId() - ), - $showOutOfStockFlag - ? '' - : $this->conditionManager->generateCondition( - 'sub_products_stock_index.stock_status', - is_array($stockValues) ? 'in' : '=', - $stockValues - ), - $this->conditionManager->generateCondition( - 'sub_products_stock_index.stock_id', - '=', - (int) $this->stockRegistry->getStock()->getStockId() - ), - ], - Select::SQL_AND - ), - [] - ); - } - /** * Extracts alias for table that is used in FROM clause in Select * diff --git a/app/code/Magento/CatalogSearch/Model/Search/FilterMapper/StockStatusQueryBuilder.php b/app/code/Magento/CatalogSearch/Model/Search/FilterMapper/StockStatusQueryBuilder.php new file mode 100644 index 0000000000000..2d0d408875661 --- /dev/null +++ b/app/code/Magento/CatalogSearch/Model/Search/FilterMapper/StockStatusQueryBuilder.php @@ -0,0 +1,106 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +declare(strict_types=1); + +namespace Magento\CatalogSearch\Model\Search\FilterMapper; + +use Magento\CatalogInventory\Model\ResourceModel\Stock\Status as StockStatusResourceModel; +use Magento\Framework\DB\Select; +use Magento\Framework\Search\Adapter\Mysql\ConditionManager; +use Magento\CatalogInventory\Api\StockConfigurationInterface; +use Magento\CatalogInventory\Api\StockRegistryInterface; + +/** + * Add stock status filter to Select + */ +class StockStatusQueryBuilder +{ + /** + * @var StockStatusResourceModel + */ + private $stockStatusResourceModel; + + /** + * @var ConditionManager + */ + private $conditionManager; + + /** + * @var StockConfigurationInterface + */ + private $stockConfiguration; + + /** + * @var StockRegistryInterface + */ + private $stockRegistry; + + /** + * @param StockStatusResourceModel $stockStatusResourceModel + * @param ConditionManager $conditionManager + * @param StockConfigurationInterface $stockConfiguration + * @param StockRegistryInterface $stockRegistry + */ + public function __construct( + StockStatusResourceModel $stockStatusResourceModel, + ConditionManager $conditionManager, + StockConfigurationInterface $stockConfiguration, + StockRegistryInterface $stockRegistry + ) { + $this->stockStatusResourceModel = $stockStatusResourceModel; + $this->conditionManager = $conditionManager; + $this->stockConfiguration = $stockConfiguration; + $this->stockRegistry = $stockRegistry; + } + + /** + * Add stock filter to Select + * + * @param Select $select + * @param string $mainTableAlias + * @param string $stockTableAlias + * @param string $joinField + * @param mixed $values + * @return Select + */ + public function apply( + Select $select, + string $mainTableAlias, + string $stockTableAlias, + string $joinField, + $values = null + ): Select { + $select->joinInner( + [$stockTableAlias => $this->stockStatusResourceModel->getMainTable()], + $this->conditionManager->combineQueries( + [ + sprintf('%s.product_id = %s.%s', $stockTableAlias, $mainTableAlias, $joinField), + $this->conditionManager->generateCondition( + sprintf('%s.website_id', $stockTableAlias), + '=', + $this->stockConfiguration->getDefaultScopeId() + ), + $values === null + ? '' + : $this->conditionManager->generateCondition( + sprintf('%s.stock_status', $stockTableAlias), + is_array($values) ? 'in' : '=', + $values + ), + $this->conditionManager->generateCondition( + sprintf('%s.stock_id', $stockTableAlias), + '=', + (int) $this->stockRegistry->getStock()->getStockId() + ), + ], + Select::SQL_AND + ), + [] + ); + + return $select; + } +} diff --git a/app/code/Magento/CatalogSearch/Model/Search/FiltersExtractor.php b/app/code/Magento/CatalogSearch/Model/Search/FiltersExtractor.php index 55c8582979912..63d7ee0e6e4c0 100644 --- a/app/code/Magento/CatalogSearch/Model/Search/FiltersExtractor.php +++ b/app/code/Magento/CatalogSearch/Model/Search/FiltersExtractor.php @@ -10,11 +10,10 @@ use Magento\Framework\Search\Request\Filter\BoolExpression; /** - * Class FiltersExtractor * Extracts filters from QueryInterface * - * @deprecated - * @see \Magento\ElasticSearch + * @deprecated Use Magento\Elasticsearch implementation of QueryInterface + * @see \Magento\Elasticsearch */ class FiltersExtractor { @@ -26,19 +25,19 @@ class FiltersExtractor */ public function extractFiltersFromQuery(QueryInterface $query) { - $filters = []; + $filters = [[]]; switch ($query->getType()) { case QueryInterface::TYPE_BOOL: /** @var \Magento\Framework\Search\Request\Query\BoolExpression $query */ foreach ($query->getMust() as $subQuery) { - $filters = array_merge($filters, $this->extractFiltersFromQuery($subQuery)); + $filters[] = $this->extractFiltersFromQuery($subQuery); } foreach ($query->getShould() as $subQuery) { - $filters = array_merge($filters, $this->extractFiltersFromQuery($subQuery)); + $filters[] = $this->extractFiltersFromQuery($subQuery); } foreach ($query->getMustNot() as $subQuery) { - $filters = array_merge($filters, $this->extractFiltersFromQuery($subQuery)); + $filters[] = $this->extractFiltersFromQuery($subQuery); } break; @@ -46,9 +45,9 @@ public function extractFiltersFromQuery(QueryInterface $query) /** @var Filter $query */ $filter = $query->getReference(); if (FilterInterface::TYPE_BOOL === $filter->getType()) { - $filters = array_merge($filters, $this->getFiltersFromBoolFilter($filter)); + $filters[] = $this->getFiltersFromBoolFilter($filter); } else { - $filters[] = $filter; + $filters[] = [$filter]; } break; @@ -56,39 +55,41 @@ public function extractFiltersFromQuery(QueryInterface $query) break; } - return $filters; + return array_merge(...$filters); } /** + * Returns list of filters from Bool filter + * * @param BoolExpression $boolExpression * @return FilterInterface[] */ private function getFiltersFromBoolFilter(BoolExpression $boolExpression) { - $filters = []; + $filters = [[]]; /** @var BoolExpression $filter */ foreach ($boolExpression->getMust() as $filter) { if ($filter->getType() === FilterInterface::TYPE_BOOL) { - $filters = array_merge($filters, $this->getFiltersFromBoolFilter($filter)); + $filters[] = $this->getFiltersFromBoolFilter($filter); } else { - $filters[] = $filter; + $filters[] = [$filter]; } } foreach ($boolExpression->getShould() as $filter) { if ($filter->getType() === FilterInterface::TYPE_BOOL) { - $filters = array_merge($filters, $this->getFiltersFromBoolFilter($filter)); + $filters[] = $this->getFiltersFromBoolFilter($filter); } else { - $filters[] = $filter; + $filters[] = [$filter]; } } foreach ($boolExpression->getMustNot() as $filter) { if ($filter->getType() === FilterInterface::TYPE_BOOL) { - $filters = array_merge($filters, $this->getFiltersFromBoolFilter($filter)); + $filters[] = $this->getFiltersFromBoolFilter($filter); } else { - $filters[] = $filter; + $filters[] = [$filter]; } } - return $filters; + return array_merge(...$filters); } } diff --git a/app/code/Magento/CatalogSearch/Model/Search/RequestGenerator.php b/app/code/Magento/CatalogSearch/Model/Search/RequestGenerator.php index 5ac252677ff79..caa49d0f0c4a4 100644 --- a/app/code/Magento/CatalogSearch/Model/Search/RequestGenerator.php +++ b/app/code/Magento/CatalogSearch/Model/Search/RequestGenerator.php @@ -100,10 +100,7 @@ private function generateRequest($attributeType, $container, $useFulltext) ], ]; $bucketName = $attribute->getAttributeCode() . self::BUCKET_SUFFIX; - $generatorType = $attribute->getFrontendInput() === 'price' - ? $attribute->getFrontendInput() - : $attribute->getBackendType(); - $generator = $this->generatorResolver->getGeneratorForType($generatorType); + $generator = $this->generatorResolver->getGeneratorForType($attribute->getBackendType()); $request['filters'][$filterName] = $generator->getFilterData($attribute, $filterName); $request['aggregations'][$bucketName] = $generator->getAggregationData($attribute, $bucketName); } diff --git a/app/code/Magento/CatalogSearch/Model/Search/RequestGenerator/Price.php b/app/code/Magento/CatalogSearch/Model/Search/RequestGenerator/Price.php deleted file mode 100644 index 949806d14f45a..0000000000000 --- a/app/code/Magento/CatalogSearch/Model/Search/RequestGenerator/Price.php +++ /dev/null @@ -1,46 +0,0 @@ -<?php -/** - * Copyright © Magento, Inc. All rights reserved. - * See COPYING.txt for license details. - */ -declare(strict_types=1); - -namespace Magento\CatalogSearch\Model\Search\RequestGenerator; - -use Magento\Catalog\Model\ResourceModel\Eav\Attribute; -use Magento\Framework\Search\Request\BucketInterface; -use Magento\Framework\Search\Request\FilterInterface; - -/** - * Catalog search range request generator. - */ -class Price implements GeneratorInterface -{ - /** - * @inheritdoc - */ - public function getFilterData(Attribute $attribute, $filterName): array - { - return [ - 'type' => FilterInterface::TYPE_RANGE, - 'name' => $filterName, - 'field' => $attribute->getAttributeCode(), - 'from' => '$' . $attribute->getAttributeCode() . '.from$', - 'to' => '$' . $attribute->getAttributeCode() . '.to$', - ]; - } - - /** - * @inheritdoc - */ - public function getAggregationData(Attribute $attribute, $bucketName): array - { - return [ - 'type' => BucketInterface::TYPE_DYNAMIC, - 'name' => $bucketName, - 'field' => $attribute->getAttributeCode(), - 'method' => '$price_dynamic_algorithm$', - 'metric' => [['type' => 'count']], - ]; - } -} diff --git a/app/code/Magento/CatalogSearch/Test/Mftf/ActionGroup/StorefrontFillFormAdvancedSearchWithCustomDropDownAttributeActionGroup.xml b/app/code/Magento/CatalogSearch/Test/Mftf/ActionGroup/StorefrontFillFormAdvancedSearchWithCustomDropDownAttributeActionGroup.xml new file mode 100644 index 0000000000000..1618465769a66 --- /dev/null +++ b/app/code/Magento/CatalogSearch/Test/Mftf/ActionGroup/StorefrontFillFormAdvancedSearchWithCustomDropDownAttributeActionGroup.xml @@ -0,0 +1,21 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + /** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +--> + +<actionGroups xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/actionGroupSchema.xsd"> + <actionGroup name="StorefrontFillFormAdvancedSearchWithCustomDropDownAttributeActionGroup" extends="StorefrontFillFormAdvancedSearchActionGroup"> + <annotations> + <description>Fills in the advanced search form and select the attribute.</description> + </annotations> + <arguments> + <argument name="attributeCode" type="string" defaultValue="{{productDropDownAttribute.attribute_code}}"/> + <argument name="optionName" type="string" defaultValue="{{productAttributeOption1.label}}"/> + </arguments> + <selectOption userInput="{{optionName}}" selector="{{StorefrontCatalogSearchAdvancedFormSection.AttributeByCode(attributeCode)}}" before="clickSubmit" stepKey="selectOption"/> + </actionGroup> +</actionGroups> diff --git a/app/code/Magento/CatalogSearch/Test/Mftf/ActionGroup/StorefrontQuickSearchWithPaginationActionGroup.xml b/app/code/Magento/CatalogSearch/Test/Mftf/ActionGroup/StorefrontQuickSearchWithPaginationActionGroup.xml new file mode 100644 index 0000000000000..95ebfa40adb26 --- /dev/null +++ b/app/code/Magento/CatalogSearch/Test/Mftf/ActionGroup/StorefrontQuickSearchWithPaginationActionGroup.xml @@ -0,0 +1,22 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + /** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +--> + +<actionGroups xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/actionGroupSchema.xsd"> + <actionGroup name="StorefrontQuickSearchWithPaginationActionGroup"> + <annotations> + <description>Navigate to catalog search page with prepared GET params to get search results with particular page number.</description> + </annotations> + <arguments> + <argument name="phrase" type="string" defaultValue="{{_defaultProduct.name}}"/> + <argument name="pageNumber" type="string" defaultValue="1"/> + </arguments> + <amOnPage url="{{StorefrontCatalogSearchPage.url}}?q={{phrase}}&p={{pageNumber}}" stepKey="navigateToCatalogSearchPageWithPreparedRequest"/> + <waitForPageLoad stepKey="waitForCatalogSearchPageLoad"/> + </actionGroup> +</actionGroups> diff --git a/app/code/Magento/CatalogSearch/Test/Mftf/Metadata/catalog_search-meta.xml b/app/code/Magento/CatalogSearch/Test/Mftf/Metadata/CatalogSearchMeta.xml similarity index 100% rename from app/code/Magento/CatalogSearch/Test/Mftf/Metadata/catalog_search-meta.xml rename to app/code/Magento/CatalogSearch/Test/Mftf/Metadata/CatalogSearchMeta.xml diff --git a/app/code/Magento/CatalogSearch/Test/Mftf/Test/AdminCreateSearchTermEntityTest.xml b/app/code/Magento/CatalogSearch/Test/Mftf/Test/AdminCreateSearchTermEntityTest.xml index 86a1d4321cba0..6361c076ce177 100644 --- a/app/code/Magento/CatalogSearch/Test/Mftf/Test/AdminCreateSearchTermEntityTest.xml +++ b/app/code/Magento/CatalogSearch/Test/Mftf/Test/AdminCreateSearchTermEntityTest.xml @@ -20,7 +20,7 @@ </annotations> <before> <!-- Login as admin --> - <actionGroup ref="LoginAsAdmin" stepKey="loginAsAdmin"/> + <actionGroup ref="AdminLoginActionGroup" stepKey="loginAsAdmin"/> <!-- Create simple product --> <createData entity="SimpleProduct2" stepKey="createSimpleProduct"/> @@ -35,7 +35,7 @@ <deleteData createDataKey="createSimpleProduct" stepKey="deleteSimpleProduct"/> <!-- Log out --> - <actionGroup ref="logout" stepKey="logout"/> + <actionGroup ref="AdminLogoutActionGroup" stepKey="logout"/> </after> <!-- Go to the search terms page and create new search term --> @@ -47,8 +47,7 @@ </actionGroup> <!-- Go to storefront --> - <amOnPage url="{{StorefrontHomePage.url}}" stepKey="amOnStorefrontPage"/> - <waitForPageLoad stepKey="waitForPageLoad"/> + <actionGroup ref="StorefrontOpenHomePageActionGroup" stepKey="amOnStorefrontPage"/> <!-- Assert created search term on storefront --> <actionGroup ref="AssertSearchTermOnFrontendActionGroup" stepKey="assertCreatedSearchTermOnFrontend"> diff --git a/app/code/Magento/CatalogSearch/Test/Mftf/Test/AdminDeleteSearchTermTest.xml b/app/code/Magento/CatalogSearch/Test/Mftf/Test/AdminDeleteSearchTermTest.xml index b449d92d8e003..c376456a64ac4 100644 --- a/app/code/Magento/CatalogSearch/Test/Mftf/Test/AdminDeleteSearchTermTest.xml +++ b/app/code/Magento/CatalogSearch/Test/Mftf/Test/AdminDeleteSearchTermTest.xml @@ -20,7 +20,7 @@ </annotations> <before> - <actionGroup ref = "LoginAsAdmin" stepKey="loginAsAdmin"/> + <actionGroup ref="AdminLoginActionGroup" stepKey="loginAsAdmin"/> <createData entity="SimpleSubCategory" stepKey="initialCategoryEntity"/> <createData entity="SimpleProduct" stepKey="simpleProduct"> <requiredEntity createDataKey="initialCategoryEntity"/> @@ -29,7 +29,7 @@ <after> <deleteData stepKey="deleteSimpleSubCategory" createDataKey="initialCategoryEntity"/> <deleteData stepKey="deleteSimpleProduct" createDataKey="simpleProduct"/> - <actionGroup ref="logout" stepKey="logout"/> + <actionGroup ref="AdminLogoutActionGroup" stepKey="logout"/> </after> <!--Add new search term--> diff --git a/app/code/Magento/CatalogSearch/Test/Mftf/Test/AdminMarketingSearchTermsNavigateMenuTest.xml b/app/code/Magento/CatalogSearch/Test/Mftf/Test/AdminMarketingSearchTermsNavigateMenuTest.xml index bc255020d98b3..a0abefebcb6b4 100644 --- a/app/code/Magento/CatalogSearch/Test/Mftf/Test/AdminMarketingSearchTermsNavigateMenuTest.xml +++ b/app/code/Magento/CatalogSearch/Test/Mftf/Test/AdminMarketingSearchTermsNavigateMenuTest.xml @@ -20,10 +20,10 @@ <group value="mtf_migrated"/> </annotations> <before> - <actionGroup ref="LoginAsAdmin" stepKey="LoginAsAdmin"/> + <actionGroup ref="AdminLoginActionGroup" stepKey="LoginAsAdmin"/> </before> <after> - <actionGroup ref="logout" stepKey="logout"/> + <actionGroup ref="AdminLogoutActionGroup" stepKey="logout"/> </after> <actionGroup ref="AdminNavigateMenuActionGroup" stepKey="navigateToMarketingSearchTermsPage"> <argument name="menuUiId" value="{{AdminMenuMarketing.dataUiId}}"/> diff --git a/app/code/Magento/CatalogSearch/Test/Mftf/Test/AdminReportsSearchTermsNavigateMenuTest.xml b/app/code/Magento/CatalogSearch/Test/Mftf/Test/AdminReportsSearchTermsNavigateMenuTest.xml index 85cf0e3ba90ed..42c6d13784a98 100644 --- a/app/code/Magento/CatalogSearch/Test/Mftf/Test/AdminReportsSearchTermsNavigateMenuTest.xml +++ b/app/code/Magento/CatalogSearch/Test/Mftf/Test/AdminReportsSearchTermsNavigateMenuTest.xml @@ -20,10 +20,10 @@ <group value="mtf_migrated"/> </annotations> <before> - <actionGroup ref="LoginAsAdmin" stepKey="LoginAsAdmin"/> + <actionGroup ref="AdminLoginActionGroup" stepKey="LoginAsAdmin"/> </before> <after> - <actionGroup ref="logout" stepKey="logout"/> + <actionGroup ref="AdminLogoutActionGroup" stepKey="logout"/> </after> <actionGroup ref="AdminNavigateMenuActionGroup" stepKey="navigateToReportSearchTermsPage"> <argument name="menuUiId" value="{{AdminMenuReports.dataUiId}}"/> diff --git a/app/code/Magento/CatalogSearch/Test/Mftf/Test/AdvanceCatalogSearchSimpleProductTest.xml b/app/code/Magento/CatalogSearch/Test/Mftf/Test/AdvanceCatalogSearchSimpleProductTest.xml deleted file mode 100644 index 0e92d9fb0c7ad..0000000000000 --- a/app/code/Magento/CatalogSearch/Test/Mftf/Test/AdvanceCatalogSearchSimpleProductTest.xml +++ /dev/null @@ -1,102 +0,0 @@ -<?xml version="1.0" encoding="UTF-8"?> -<!-- - /** - * Copyright © Magento, Inc. All rights reserved. - * See COPYING.txt for license details. - */ ---> - -<tests xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" - xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/testSchema.xsd"> - <test name="AdvanceCatalogSearchSimpleProductByNameTest"> - <annotations> - <features value="CatalogSearch"/> - <group value="CatalogSearch"/> - </annotations> - - <!-- Perform reindex and flush cache --> - <magentoCLI command="indexer:reindex" stepKey="reindex"/> - <magentoCLI command="cache:flush" stepKey="flushCache"/> - - <actionGroup ref="GoToStoreViewAdvancedCatalogSearchActionGroup" stepKey="GoToStoreViewAdvancedCatalogSearchActionGroup"/> - <actionGroup ref="StorefrontAdvancedCatalogSearchByProductNameActionGroup" stepKey="search"> - <argument name="name" value="$$product.name$$"/> - </actionGroup> - <actionGroup ref="StorefrontCheckAdvancedSearchResultActionGroup" stepKey="StorefrontCheckAdvancedSearchResult"/> - <see userInput="1 item" selector="{{StorefrontCatalogSearchAdvancedResultMainSection.itemFound}}" stepKey="see"/> - <see userInput="$$product.name$$" selector="{{StorefrontCatalogSearchAdvancedResultMainSection.productName}}" stepKey="seeProductName"/> - </test> - <test name="AdvanceCatalogSearchSimpleProductBySkuTest"> - <annotations> - <features value="CatalogSearch"/> - <group value="CatalogSearch"/> - </annotations> - - <!-- Perform reindex and flush cache --> - <magentoCLI command="indexer:reindex" stepKey="reindex"/> - <magentoCLI command="cache:flush" stepKey="flushCache"/> - - <actionGroup ref="GoToStoreViewAdvancedCatalogSearchActionGroup" stepKey="GoToStoreViewAdvancedCatalogSearchActionGroup"/> - <actionGroup ref="StorefrontAdvancedCatalogSearchByProductSkuActionGroup" stepKey="search"> - <argument name="sku" value="$$product.sku$$"/> - </actionGroup> - <actionGroup ref="StorefrontCheckAdvancedSearchResultActionGroup" stepKey="StorefrontCheckAdvancedSearchResult"/> - <see userInput="1 item" selector="{{StorefrontCatalogSearchAdvancedResultMainSection.itemFound}}" stepKey="see"/> - <see userInput="$$product.name$$" selector="{{StorefrontCatalogSearchAdvancedResultMainSection.productName}}" stepKey="seeProductName"/> - </test> - <test name="AdvanceCatalogSearchSimpleProductByDescriptionTest"> - <annotations> - <features value="CatalogSearch"/> - <group value="CatalogSearch"/> - </annotations> - <!-- Perform reindex and flush cache --> - <magentoCLI command="indexer:reindex" stepKey="reindex"/> - <magentoCLI command="cache:flush" stepKey="flushCache"/> - - <actionGroup ref="GoToStoreViewAdvancedCatalogSearchActionGroup" stepKey="GoToStoreViewAdvancedCatalogSearchActionGroup"/> - <actionGroup ref="StorefrontAdvancedCatalogSearchByDescriptionActionGroup" stepKey="search"> - <argument name="description" value="$$product.custom_attributes[description]$$"/> - </actionGroup> - <actionGroup ref="StorefrontCheckAdvancedSearchResultActionGroup" stepKey="StorefrontCheckAdvancedSearchResult"/> - <see userInput="1 item" selector="{{StorefrontCatalogSearchAdvancedResultMainSection.itemFound}}" stepKey="see"/> - <see userInput="$$product.name$$" selector="{{StorefrontCatalogSearchAdvancedResultMainSection.productName}}" stepKey="seeProductName"/> - </test> - <test name="AdvanceCatalogSearchSimpleProductByShortDescriptionTest"> - <annotations> - <features value="CatalogSearch"/> - <group value="CatalogSearch"/> - </annotations> - - <!-- Perform reindex and flush cache --> - <magentoCLI command="indexer:reindex" stepKey="reindex"/> - <magentoCLI command="cache:flush" stepKey="flushCache"/> - - <actionGroup ref="GoToStoreViewAdvancedCatalogSearchActionGroup" stepKey="GoToStoreViewAdvancedCatalogSearchActionGroup"/> - <actionGroup ref="StorefrontAdvancedCatalogSearchByShortDescriptionActionGroup" stepKey="search"> - <argument name="shortDescription" value="$$product.custom_attributes[short_description]$$"/> - </actionGroup> - <actionGroup ref="StorefrontCheckAdvancedSearchResultActionGroup" stepKey="StorefrontCheckAdvancedSearchResult"/> - <see userInput="1 item" selector="{{StorefrontCatalogSearchAdvancedResultMainSection.itemFound}}" stepKey="see"/> - <see userInput="$$product.name$$" selector="{{StorefrontCatalogSearchAdvancedResultMainSection.productName}}" stepKey="seeProductName"/> - </test> - <test name="AdvanceCatalogSearchSimpleProductByPriceTest"> - <annotations> - <features value="CatalogSearch"/> - <group value="CatalogSearch"/> - </annotations> - - <!-- Perform reindex and flush cache --> - <magentoCLI command="indexer:reindex" stepKey="reindex"/> - <magentoCLI command="cache:flush" stepKey="flushCache"/> - - <actionGroup ref="GoToStoreViewAdvancedCatalogSearchActionGroup" stepKey="GoToStoreViewAdvancedCatalogSearchActionGroup"/> - <actionGroup ref="StorefrontAdvancedCatalogSearchByProductNameAndPriceActionGroup" stepKey="search"> - <argument name="name" value="$$arg1.name$$"/> - <argument name="priceFrom" value="$$arg2.price$$"/> - <argument name="priceTo" value="$$arg3.price$$"/> - </actionGroup> - <actionGroup ref="StorefrontCheckAdvancedSearchResultActionGroup" stepKey="StorefrontCheckAdvancedSearchResult"/> - <see userInput="1 item" selector="{{StorefrontCatalogSearchAdvancedResultMainSection.itemFound}}" stepKey="see"/> - <see userInput="$$arg1.name$$" selector="{{StorefrontCatalogSearchAdvancedResultMainSection.productName}}" stepKey="seeProductName"/> - </test> -</tests> diff --git a/app/code/Magento/CatalogSearch/Test/Mftf/Test/AdvanceCatalogSearchSimpleProductTest/AdvanceCatalogSearchSimpleProductByDescriptionTest.xml b/app/code/Magento/CatalogSearch/Test/Mftf/Test/AdvanceCatalogSearchSimpleProductTest/AdvanceCatalogSearchSimpleProductByDescriptionTest.xml new file mode 100644 index 0000000000000..d23663d43dcd0 --- /dev/null +++ b/app/code/Magento/CatalogSearch/Test/Mftf/Test/AdvanceCatalogSearchSimpleProductTest/AdvanceCatalogSearchSimpleProductByDescriptionTest.xml @@ -0,0 +1,27 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + /** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +--> +<tests xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/testSchema.xsd"> + <test name="AdvanceCatalogSearchSimpleProductByDescriptionTest"> + <annotations> + <features value="CatalogSearch"/> + <group value="CatalogSearch"/> + </annotations> + <!-- Perform reindex and flush cache --> + <magentoCLI command="indexer:reindex" stepKey="reindex"/> + <magentoCLI command="cache:flush" stepKey="flushCache"/> + + <actionGroup ref="GoToStoreViewAdvancedCatalogSearchActionGroup" stepKey="GoToStoreViewAdvancedCatalogSearchActionGroup"/> + <actionGroup ref="StorefrontAdvancedCatalogSearchByDescriptionActionGroup" stepKey="search"> + <argument name="description" value="$$product.custom_attributes[description]$$"/> + </actionGroup> + <actionGroup ref="StorefrontCheckAdvancedSearchResultActionGroup" stepKey="StorefrontCheckAdvancedSearchResult"/> + <see userInput="1 item" selector="{{StorefrontCatalogSearchAdvancedResultMainSection.itemFound}}" stepKey="see"/> + <see userInput="$$product.name$$" selector="{{StorefrontCatalogSearchAdvancedResultMainSection.productName}}" stepKey="seeProductName"/> + </test> +</tests> diff --git a/app/code/Magento/CatalogSearch/Test/Mftf/Test/AdvanceCatalogSearchSimpleProductTest/AdvanceCatalogSearchSimpleProductByNameTest.xml b/app/code/Magento/CatalogSearch/Test/Mftf/Test/AdvanceCatalogSearchSimpleProductTest/AdvanceCatalogSearchSimpleProductByNameTest.xml new file mode 100644 index 0000000000000..0b3fb2fa42532 --- /dev/null +++ b/app/code/Magento/CatalogSearch/Test/Mftf/Test/AdvanceCatalogSearchSimpleProductTest/AdvanceCatalogSearchSimpleProductByNameTest.xml @@ -0,0 +1,28 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + /** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +--> +<tests xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/testSchema.xsd"> + <test name="AdvanceCatalogSearchSimpleProductByNameTest"> + <annotations> + <features value="CatalogSearch"/> + <group value="CatalogSearch"/> + </annotations> + + <!-- Perform reindex and flush cache --> + <magentoCLI command="indexer:reindex" stepKey="reindex"/> + <magentoCLI command="cache:flush" stepKey="flushCache"/> + + <actionGroup ref="GoToStoreViewAdvancedCatalogSearchActionGroup" stepKey="GoToStoreViewAdvancedCatalogSearchActionGroup"/> + <actionGroup ref="StorefrontAdvancedCatalogSearchByProductNameActionGroup" stepKey="search"> + <argument name="name" value="$$product.name$$"/> + </actionGroup> + <actionGroup ref="StorefrontCheckAdvancedSearchResultActionGroup" stepKey="StorefrontCheckAdvancedSearchResult"/> + <see userInput="1 item" selector="{{StorefrontCatalogSearchAdvancedResultMainSection.itemFound}}" stepKey="see"/> + <see userInput="$$product.name$$" selector="{{StorefrontCatalogSearchAdvancedResultMainSection.productName}}" stepKey="seeProductName"/> + </test> +</tests> diff --git a/app/code/Magento/CatalogSearch/Test/Mftf/Test/AdvanceCatalogSearchSimpleProductTest/AdvanceCatalogSearchSimpleProductByPriceTest.xml b/app/code/Magento/CatalogSearch/Test/Mftf/Test/AdvanceCatalogSearchSimpleProductTest/AdvanceCatalogSearchSimpleProductByPriceTest.xml new file mode 100644 index 0000000000000..517e200f8ce11 --- /dev/null +++ b/app/code/Magento/CatalogSearch/Test/Mftf/Test/AdvanceCatalogSearchSimpleProductTest/AdvanceCatalogSearchSimpleProductByPriceTest.xml @@ -0,0 +1,30 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + /** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +--> +<tests xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/testSchema.xsd"> + <test name="AdvanceCatalogSearchSimpleProductByPriceTest"> + <annotations> + <features value="CatalogSearch"/> + <group value="CatalogSearch"/> + </annotations> + + <!-- Perform reindex and flush cache --> + <magentoCLI command="indexer:reindex" stepKey="reindex"/> + <magentoCLI command="cache:flush" stepKey="flushCache"/> + + <actionGroup ref="GoToStoreViewAdvancedCatalogSearchActionGroup" stepKey="GoToStoreViewAdvancedCatalogSearchActionGroup"/> + <actionGroup ref="StorefrontAdvancedCatalogSearchByProductNameAndPriceActionGroup" stepKey="search"> + <argument name="name" value="$$arg1.name$$"/> + <argument name="priceFrom" value="$$arg2.price$$"/> + <argument name="priceTo" value="$$arg3.price$$"/> + </actionGroup> + <actionGroup ref="StorefrontCheckAdvancedSearchResultActionGroup" stepKey="StorefrontCheckAdvancedSearchResult"/> + <see userInput="1 item" selector="{{StorefrontCatalogSearchAdvancedResultMainSection.itemFound}}" stepKey="see"/> + <see userInput="$$arg1.name$$" selector="{{StorefrontCatalogSearchAdvancedResultMainSection.productName}}" stepKey="seeProductName"/> + </test> +</tests> diff --git a/app/code/Magento/CatalogSearch/Test/Mftf/Test/AdvanceCatalogSearchSimpleProductTest/AdvanceCatalogSearchSimpleProductByShortDescriptionTest.xml b/app/code/Magento/CatalogSearch/Test/Mftf/Test/AdvanceCatalogSearchSimpleProductTest/AdvanceCatalogSearchSimpleProductByShortDescriptionTest.xml new file mode 100644 index 0000000000000..0bd08d31e8ffa --- /dev/null +++ b/app/code/Magento/CatalogSearch/Test/Mftf/Test/AdvanceCatalogSearchSimpleProductTest/AdvanceCatalogSearchSimpleProductByShortDescriptionTest.xml @@ -0,0 +1,28 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + /** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +--> +<tests xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/testSchema.xsd"> + <test name="AdvanceCatalogSearchSimpleProductByShortDescriptionTest"> + <annotations> + <features value="CatalogSearch"/> + <group value="CatalogSearch"/> + </annotations> + + <!-- Perform reindex and flush cache --> + <magentoCLI command="indexer:reindex" stepKey="reindex"/> + <magentoCLI command="cache:flush" stepKey="flushCache"/> + + <actionGroup ref="GoToStoreViewAdvancedCatalogSearchActionGroup" stepKey="GoToStoreViewAdvancedCatalogSearchActionGroup"/> + <actionGroup ref="StorefrontAdvancedCatalogSearchByShortDescriptionActionGroup" stepKey="search"> + <argument name="shortDescription" value="$$product.custom_attributes[short_description]$$"/> + </actionGroup> + <actionGroup ref="StorefrontCheckAdvancedSearchResultActionGroup" stepKey="StorefrontCheckAdvancedSearchResult"/> + <see userInput="1 item" selector="{{StorefrontCatalogSearchAdvancedResultMainSection.itemFound}}" stepKey="see"/> + <see userInput="$$product.name$$" selector="{{StorefrontCatalogSearchAdvancedResultMainSection.productName}}" stepKey="seeProductName"/> + </test> +</tests> diff --git a/app/code/Magento/CatalogSearch/Test/Mftf/Test/AdvanceCatalogSearchSimpleProductTest/AdvanceCatalogSearchSimpleProductBySkuTest.xml b/app/code/Magento/CatalogSearch/Test/Mftf/Test/AdvanceCatalogSearchSimpleProductTest/AdvanceCatalogSearchSimpleProductBySkuTest.xml new file mode 100644 index 0000000000000..d273f9828dc95 --- /dev/null +++ b/app/code/Magento/CatalogSearch/Test/Mftf/Test/AdvanceCatalogSearchSimpleProductTest/AdvanceCatalogSearchSimpleProductBySkuTest.xml @@ -0,0 +1,28 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + /** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +--> +<tests xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/testSchema.xsd"> + <test name="AdvanceCatalogSearchSimpleProductBySkuTest"> + <annotations> + <features value="CatalogSearch"/> + <group value="CatalogSearch"/> + </annotations> + + <!-- Perform reindex and flush cache --> + <magentoCLI command="indexer:reindex" stepKey="reindex"/> + <magentoCLI command="cache:flush" stepKey="flushCache"/> + + <actionGroup ref="GoToStoreViewAdvancedCatalogSearchActionGroup" stepKey="GoToStoreViewAdvancedCatalogSearchActionGroup"/> + <actionGroup ref="StorefrontAdvancedCatalogSearchByProductSkuActionGroup" stepKey="search"> + <argument name="sku" value="$$product.sku$$"/> + </actionGroup> + <actionGroup ref="StorefrontCheckAdvancedSearchResultActionGroup" stepKey="StorefrontCheckAdvancedSearchResult"/> + <see userInput="1 item" selector="{{StorefrontCatalogSearchAdvancedResultMainSection.itemFound}}" stepKey="see"/> + <see userInput="$$product.name$$" selector="{{StorefrontCatalogSearchAdvancedResultMainSection.productName}}" stepKey="seeProductName"/> + </test> +</tests> diff --git a/app/code/Magento/CatalogSearch/Test/Mftf/Test/EndToEndB2CGuestUserTest.xml b/app/code/Magento/CatalogSearch/Test/Mftf/Test/EndToEndB2CGuestUserTest.xml deleted file mode 100644 index 57940e39e9281..0000000000000 --- a/app/code/Magento/CatalogSearch/Test/Mftf/Test/EndToEndB2CGuestUserTest.xml +++ /dev/null @@ -1,143 +0,0 @@ -<?xml version="1.0" encoding="UTF-8"?> -<!-- - /** - * Copyright © Magento, Inc. All rights reserved. - * See COPYING.txt for license details. - */ ---> - -<tests xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" - xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/testSchema.xsd"> - <test name="EndToEndB2CGuestUserTest"> - <!-- Step 2: User searches for product --> - <comment userInput="Start of searching products" stepKey="startOfSearchingProducts" after="endOfBrowsingCatalog"/> - <!-- Advanced Search with Product Data --> - <comment userInput="Advanced search" stepKey="commentAdvancedSearch" after="startOfSearchingProducts"/> - <actionGroup ref="StorefrontOpenAdvancedSearchActionGroup" stepKey="searchOpenAdvancedSearchForm" after="commentAdvancedSearch"/> - <!-- @TODO: Change to scalar value after MQE-498 is implemented --> - <fillField userInput="$$createSimpleProduct1.name$$" selector="{{StorefrontCatalogSearchAdvancedFormSection.ProductName}}" stepKey="searchAdvancedFillProductName" after="searchOpenAdvancedSearchForm"/> - <fillField userInput="$$createSimpleProduct1.sku$$" selector="{{StorefrontCatalogSearchAdvancedFormSection.SKU}}" stepKey="searchAdvancedFillSKU" after="searchAdvancedFillProductName"/> - <fillField userInput="$$createSimpleProduct1.price$$" selector="{{StorefrontCatalogSearchAdvancedFormSection.PriceFrom}}" stepKey="searchAdvancedFillPriceFrom" after="searchAdvancedFillSKU"/> - <fillField userInput="$$createSimpleProduct1.price$$" selector="{{StorefrontCatalogSearchAdvancedFormSection.PriceTo}}" stepKey="searchAdvancedFillPriceTo" after="searchAdvancedFillPriceFrom"/> - <click selector="{{StorefrontCatalogSearchAdvancedFormSection.SubmitButton}}" stepKey="searchClickAdvancedSearchSubmitButton" after="searchAdvancedFillPriceTo"/> - <waitForLoadingMaskToDisappear stepKey="waitForSearchProductsloaded" after="searchClickAdvancedSearchSubmitButton"/> - <actionGroup ref="StorefrontCheckAdvancedSearchResultActionGroup" stepKey="searchCheckAdvancedSearchResult" after="waitForSearchProductsloaded"/> - <see userInput="4" selector="{{StorefrontCatalogSearchAdvancedResultMainSection.productCount}} span" stepKey="searchAdvancedAssertProductCount" after="searchCheckAdvancedSearchResult"/> - <actionGroup ref="StorefrontCheckCategorySimpleProductActionGroup" stepKey="searchAssertSimpleProduct1" after="searchAdvancedAssertProductCount"> - <argument name="product" value="$$createSimpleProduct1$$"/> - </actionGroup> - <!-- @TODO: Move Image check to action group after MQE-697 is fixed --> - <grabAttributeFrom selector="{{StorefrontCategoryProductSection.ProductImageByName($$createSimpleProduct1.name$$)}}" userInput="src" stepKey="searchAdvancedGrabSimpleProduct1ImageSrc" after="searchAssertSimpleProduct1"/> - <assertNotRegExp expected="'/placeholder\/small_image\.jpg/'" actual="$searchAdvancedGrabSimpleProduct1ImageSrc" stepKey="searchAdvancedAssertSimpleProduct1ImageNotDefault" after="searchAdvancedGrabSimpleProduct1ImageSrc"/> - <click selector="{{StorefrontCategoryProductSection.ProductTitleByName($$createSimpleProduct1.name$$)}}" stepKey="searchClickSimpleProduct1View" after="searchAdvancedAssertSimpleProduct1ImageNotDefault"/> - <waitForLoadingMaskToDisappear stepKey="waitForSearchSimpleProduct1Viewloaded" after="searchClickSimpleProduct1View"/> - <actionGroup ref="StorefrontCheckSimpleProductActionGroup" stepKey="searchAssertSimpleProduct1Page" after="waitForSearchSimpleProduct1Viewloaded"> - <argument name="product" value="$$createSimpleProduct1$$"/> - </actionGroup> - <!-- @TODO: Move Image check to action group after MQE-697 is fixed --> - <grabAttributeFrom selector="{{StorefrontProductInfoMainSection.productImage}}" userInput="src" stepKey="searchAdvancedGrabSimpleProduct1PageImageSrc" after="searchAssertSimpleProduct1Page"/> - <assertNotRegExp expected="'/placeholder\/image\.jpg/'" actual="$searchAdvancedGrabSimpleProduct1PageImageSrc" stepKey="searchAdvancedAssertSimpleProduct1PageImageNotDefault" after="searchAdvancedGrabSimpleProduct1PageImageSrc"/> - - <!-- Quick Search with common part of product names --> - <comment userInput="Quick search" stepKey="commentQuickSearch" after="searchAdvancedAssertSimpleProduct1PageImageNotDefault"/> - <actionGroup ref="StorefrontCheckQuickSearchActionGroup" stepKey="searchQuickSearchCommonPart" after="commentQuickSearch"> - <!-- @TODO: Change to scalar value after MQE-498 is implemented --> - <argument name="phrase" value="CONST.apiSimpleProduct"/> - </actionGroup> - <actionGroup ref="StorefrontSelectSearchFilterCategoryActionGroup" stepKey="searchSelectFilterCategoryCommonPart" after="searchQuickSearchCommonPart"> - <argument name="category" value="$$createCategory$$"/> - </actionGroup> - <see userInput="3" selector="{{StorefrontCategoryMainSection.productCount}} span" stepKey="searchAssertFilterCategoryProductCountCommonPart" after="searchSelectFilterCategoryCommonPart"/> - - <!-- Search simple product 1 --> - <comment userInput="Search simple product 1" stepKey="commentSearchSimpleProduct1" after="searchAssertFilterCategoryProductCountCommonPart"/> - <actionGroup ref="StorefrontCheckCategorySimpleProductActionGroup" stepKey="searchAssertFilterCategorySimpleProduct1" after="commentSearchSimpleProduct1"> - <argument name="product" value="$$createSimpleProduct1$$"/> - </actionGroup> - <!-- @TODO: Move Image check to action group after MQE-697 is fixed --> - <grabAttributeFrom selector="{{StorefrontCategoryProductSection.ProductImageByName($$createSimpleProduct1.name$$)}}" userInput="src" stepKey="searchGrabSimpleProduct1ImageSrc" after="searchAssertFilterCategorySimpleProduct1"/> - <assertNotRegExp expected="'/placeholder\/small_image\.jpg/'" actual="$searchGrabSimpleProduct1ImageSrc" stepKey="searchAssertSimpleProduct1ImageNotDefault" after="searchGrabSimpleProduct1ImageSrc"/> - <!-- Search simple product2 --> - <actionGroup ref="StorefrontCheckCategorySimpleProductActionGroup" stepKey="searchAssertFilterCategorySimpleProduct2" after="searchAssertSimpleProduct1ImageNotDefault"> - <argument name="product" value="$$createSimpleProduct2$$"/> - </actionGroup> - <!-- @TODO: Move Image check to action group after MQE-697 is fixed --> - <grabAttributeFrom selector="{{StorefrontCategoryProductSection.ProductImageByName($$createSimpleProduct2.name$$)}}" userInput="src" stepKey="searchGrabSimpleProduct2ImageSrc" after="searchAssertFilterCategorySimpleProduct2"/> - <assertNotRegExp expected="'/placeholder\/small_image\.jpg/'" actual="$searchGrabSimpleProduct2ImageSrc" stepKey="searchAssertSimpleProduct2ImageNotDefault" after="searchGrabSimpleProduct2ImageSrc"/> - - <!-- Quick Search with non-existent product name --> - <comment userInput="Quick Search with non-existent product name" stepKey="commentQuickSearchWithNonExistentProductName" after="searchAssertSimpleProduct2ImageNotDefault" /> - <actionGroup ref="StorefrontCheckQuickSearchActionGroup" stepKey="searchFillQuickSearchNonExistent" after="commentQuickSearchWithNonExistentProductName"> - <!-- @TODO: Change to scalar value after MQE-498 is implemented --> - <argument name="phrase" value="CONST.nonexistentProductName"/> - </actionGroup> - <see userInput="Your search returned no results." selector="{{StorefrontCatalogSearchMainSection.message}}" stepKey="searchAssertQuickSearchMessageNonExistent" after="searchFillQuickSearchNonExistent"/> - <comment userInput="End of searching products" stepKey="endOfSearchingProducts" after="searchAssertQuickSearchMessageNonExistent" /> - </test> - <test name="EndToEndB2CGuestUserMysqlTest"> - <!-- Step 2: User searches for product --> - <comment userInput="Start of searching products" stepKey="startOfSearchingProducts" after="endOfBrowsingCatalog"/> - <!-- Advanced Search with Product 1 Data --> - <comment userInput="Advanced search" stepKey="commentAdvancedSearch" after="startOfSearchingProducts"/> - <actionGroup ref="StorefrontOpenAdvancedSearchActionGroup" stepKey="searchOpenAdvancedSearchForm" after="commentAdvancedSearch"/> - <!-- @TODO: Change to scalar value after MQE-498 is implemented --> - <fillField userInput="$$createSimpleProduct1.name$$" selector="{{StorefrontCatalogSearchAdvancedFormSection.ProductName}}" stepKey="searchAdvancedFillProductName" after="searchOpenAdvancedSearchForm"/> - <fillField userInput="$$createSimpleProduct1.sku$$" selector="{{StorefrontCatalogSearchAdvancedFormSection.SKU}}" stepKey="searchAdvancedFillSKU" after="searchAdvancedFillProductName"/> - <fillField userInput="$$createSimpleProduct1.price$$" selector="{{StorefrontCatalogSearchAdvancedFormSection.PriceFrom}}" stepKey="searchAdvancedFillPriceFrom" after="searchAdvancedFillSKU"/> - <fillField userInput="$$createSimpleProduct1.price$$" selector="{{StorefrontCatalogSearchAdvancedFormSection.PriceTo}}" stepKey="searchAdvancedFillPriceTo" after="searchAdvancedFillPriceFrom"/> - <click selector="{{StorefrontCatalogSearchAdvancedFormSection.SubmitButton}}" stepKey="searchClickAdvancedSearchSubmitButton" after="searchAdvancedFillPriceTo"/> - <waitForLoadingMaskToDisappear stepKey="waitForSearchProductsloaded" after="searchClickAdvancedSearchSubmitButton"/> - <actionGroup ref="StorefrontCheckAdvancedSearchResultActionGroup" stepKey="searchCheckAdvancedSearchResult" after="waitForSearchProductsloaded"/> - <see userInput="1" selector="{{StorefrontCatalogSearchAdvancedResultMainSection.productCount}} span" stepKey="searchAdvancedAssertProductCount" after="searchCheckAdvancedSearchResult"/> - <actionGroup ref="StorefrontCheckCategorySimpleProductActionGroup" stepKey="searchAssertSimpleProduct1" after="searchAdvancedAssertProductCount"> - <argument name="product" value="$$createSimpleProduct1$$"/> - </actionGroup> - <!-- @TODO: Move Image check to action group after MQE-697 is fixed --> - <grabAttributeFrom selector="{{StorefrontCategoryProductSection.ProductImageByName($$createSimpleProduct1.name$$)}}" userInput="src" stepKey="searchAdvancedGrabSimpleProduct1ImageSrc" after="searchAssertSimpleProduct1"/> - <assertNotRegExp expected="'/placeholder\/small_image\.jpg/'" actual="$searchAdvancedGrabSimpleProduct1ImageSrc" stepKey="searchAdvancedAssertSimpleProduct1ImageNotDefault" after="searchAdvancedGrabSimpleProduct1ImageSrc"/> - <click selector="{{StorefrontCategoryProductSection.ProductTitleByName($$createSimpleProduct1.name$$)}}" stepKey="searchClickSimpleProduct1View" after="searchAdvancedAssertSimpleProduct1ImageNotDefault"/> - <waitForLoadingMaskToDisappear stepKey="waitForSearchSimpleProduct1Viewloaded" after="searchClickSimpleProduct1View"/> - <actionGroup ref="StorefrontCheckSimpleProductActionGroup" stepKey="searchAssertSimpleProduct1Page" after="waitForSearchSimpleProduct1Viewloaded"> - <argument name="product" value="$$createSimpleProduct1$$"/> - </actionGroup> - <!-- @TODO: Move Image check to action group after MQE-697 is fixed --> - <grabAttributeFrom selector="{{StorefrontProductInfoMainSection.productImage}}" userInput="src" stepKey="searchAdvancedGrabSimpleProduct1PageImageSrc" after="searchAssertSimpleProduct1Page"/> - <assertNotRegExp expected="'/placeholder\/image\.jpg/'" actual="$searchAdvancedGrabSimpleProduct1PageImageSrc" stepKey="searchAdvancedAssertSimpleProduct1PageImageNotDefault" after="searchAdvancedGrabSimpleProduct1PageImageSrc"/> - - <!-- Quick Search with common part of product names --> - <comment userInput="Quick search" stepKey="commentQuickSearch" after="searchAdvancedAssertSimpleProduct1PageImageNotDefault"/> - <actionGroup ref="StorefrontCheckQuickSearchActionGroup" stepKey="searchQuickSearchCommonPart" after="commentQuickSearch"> - <!-- @TODO: Change to scalar value after MQE-498 is implemented --> - <argument name="phrase" value="CONST.apiSimpleProduct"/> - </actionGroup> - <actionGroup ref="StorefrontSelectSearchFilterCategoryActionGroup" stepKey="searchSelectFilterCategoryCommonPart" after="searchQuickSearchCommonPart"> - <argument name="category" value="$$createCategory$$"/> - </actionGroup> - <see userInput="3" selector="{{StorefrontCategoryMainSection.productCount}} span" stepKey="searchAssertFilterCategoryProductCountCommonPart" after="searchSelectFilterCategoryCommonPart"/> - - <!-- Search simple product 1 --> - <comment userInput="Search simple product 1" stepKey="commentSearchSimpleProduct1" after="searchAssertFilterCategoryProductCountCommonPart"/> - <actionGroup ref="StorefrontCheckCategorySimpleProductActionGroup" stepKey="searchAssertFilterCategorySimpleProduct1" after="commentSearchSimpleProduct1"> - <argument name="product" value="$$createSimpleProduct1$$"/> - </actionGroup> - <!-- @TODO: Move Image check to action group after MQE-697 is fixed --> - <grabAttributeFrom selector="{{StorefrontCategoryProductSection.ProductImageByName($$createSimpleProduct1.name$$)}}" userInput="src" stepKey="searchGrabSimpleProduct1ImageSrc" after="searchAssertFilterCategorySimpleProduct1"/> - <assertNotRegExp expected="'/placeholder\/small_image\.jpg/'" actual="$searchGrabSimpleProduct1ImageSrc" stepKey="searchAssertSimpleProduct1ImageNotDefault" after="searchGrabSimpleProduct1ImageSrc"/> - <!-- Search simple product2 --> - <actionGroup ref="StorefrontCheckCategorySimpleProductActionGroup" stepKey="searchAssertFilterCategorySimpleProduct2" after="searchAssertSimpleProduct1ImageNotDefault"> - <argument name="product" value="$$createSimpleProduct2$$"/> - </actionGroup> - <!-- @TODO: Move Image check to action group after MQE-697 is fixed --> - <grabAttributeFrom selector="{{StorefrontCategoryProductSection.ProductImageByName($$createSimpleProduct2.name$$)}}" userInput="src" stepKey="searchGrabSimpleProduct2ImageSrc" after="searchAssertFilterCategorySimpleProduct2"/> - <assertNotRegExp expected="'/placeholder\/small_image\.jpg/'" actual="$searchGrabSimpleProduct2ImageSrc" stepKey="searchAssertSimpleProduct2ImageNotDefault" after="searchGrabSimpleProduct2ImageSrc"/> - - <!-- Quick Search with non-existent product name --> - <comment userInput="Quick Search with non-existent product name" stepKey="commentQuickSearchWithNonExistentProductName" after="searchAssertSimpleProduct2ImageNotDefault" /> - <actionGroup ref="StorefrontCheckQuickSearchActionGroup" stepKey="searchFillQuickSearchNonExistent" after="commentQuickSearchWithNonExistentProductName"> - <!-- @TODO: Change to scalar value after MQE-498 is implemented --> - <argument name="phrase" value="CONST.nonexistentProductName"/> - </actionGroup> - <see userInput="Your search returned no results." selector="{{StorefrontCatalogSearchMainSection.message}}" stepKey="searchAssertQuickSearchMessageNonExistent" after="searchFillQuickSearchNonExistent"/> - <comment userInput="End of searching products" stepKey="endOfSearchingProducts" after="searchAssertQuickSearchMessageNonExistent" /> - </test> -</tests> diff --git a/app/code/Magento/CatalogSearch/Test/Mftf/Test/EndToEndB2CGuestUserTest/EndToEndB2CGuestUserMysqlTest.xml b/app/code/Magento/CatalogSearch/Test/Mftf/Test/EndToEndB2CGuestUserTest/EndToEndB2CGuestUserMysqlTest.xml new file mode 100644 index 0000000000000..9d5aebb43c616 --- /dev/null +++ b/app/code/Magento/CatalogSearch/Test/Mftf/Test/EndToEndB2CGuestUserTest/EndToEndB2CGuestUserMysqlTest.xml @@ -0,0 +1,88 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + /** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +--> +<tests xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/testSchema.xsd"> + <test name="EndToEndB2CGuestUserMysqlTest"> + <!-- Step 2: User searches for product --> + <comment userInput="Start of searching products" stepKey="startOfSearchingProducts" after="endOfBrowsingCatalog"/> + <!-- Advanced Search with Product 1 Data --> + <comment userInput="Advanced search" stepKey="commentAdvancedSearch" after="startOfSearchingProducts"/> + <actionGroup ref="StorefrontOpenAdvancedSearchActionGroup" stepKey="searchOpenAdvancedSearchForm" after="commentAdvancedSearch"/> + <!-- @TODO: Change to scalar value after MQE-498 is implemented --> + <fillField userInput="$$createSimpleProduct1.name$$" selector="{{StorefrontCatalogSearchAdvancedFormSection.ProductName}}" stepKey="searchAdvancedFillProductName" after="searchOpenAdvancedSearchForm"/> + <fillField userInput="$$createSimpleProduct1.sku$$" selector="{{StorefrontCatalogSearchAdvancedFormSection.SKU}}" stepKey="searchAdvancedFillSKU" after="searchAdvancedFillProductName"/> + <fillField userInput="$$createSimpleProduct1.price$$" selector="{{StorefrontCatalogSearchAdvancedFormSection.PriceFrom}}" stepKey="searchAdvancedFillPriceFrom" after="searchAdvancedFillSKU"/> + <fillField userInput="$$createSimpleProduct1.price$$" selector="{{StorefrontCatalogSearchAdvancedFormSection.PriceTo}}" stepKey="searchAdvancedFillPriceTo" after="searchAdvancedFillPriceFrom"/> + <click selector="{{StorefrontCatalogSearchAdvancedFormSection.SubmitButton}}" stepKey="searchClickAdvancedSearchSubmitButton" after="searchAdvancedFillPriceTo"/> + <waitForLoadingMaskToDisappear stepKey="waitForSearchProductsloaded" after="searchClickAdvancedSearchSubmitButton"/> + <actionGroup ref="StorefrontCheckAdvancedSearchResultActionGroup" stepKey="searchCheckAdvancedSearchResult" after="waitForSearchProductsloaded"/> + <see userInput="1" selector="{{StorefrontCatalogSearchAdvancedResultMainSection.productCount}} span" stepKey="searchAdvancedAssertProductCount" after="searchCheckAdvancedSearchResult"/> + <actionGroup ref="StorefrontCheckCategorySimpleProductActionGroup" stepKey="searchAssertSimpleProduct1" after="searchAdvancedAssertProductCount"> + <argument name="product" value="$$createSimpleProduct1$$"/> + </actionGroup> + <!-- @TODO: Move Image check to action group after MQE-697 is fixed --> + <grabAttributeFrom selector="{{StorefrontCategoryProductSection.ProductImageByName($$createSimpleProduct1.name$$)}}" userInput="src" stepKey="searchAdvancedGrabSimpleProduct1ImageSrc" after="searchAssertSimpleProduct1"/> + <assertNotRegExp stepKey="searchAdvancedAssertSimpleProduct1ImageNotDefault" after="searchAdvancedGrabSimpleProduct1ImageSrc"> + <actualResult type="const">$searchAdvancedGrabSimpleProduct1ImageSrc</actualResult> + <expectedResult type="const">'/placeholder\/small_image\.jpg/'</expectedResult> + </assertNotRegExp> + <click selector="{{StorefrontCategoryProductSection.ProductTitleByName($$createSimpleProduct1.name$$)}}" stepKey="searchClickSimpleProduct1View" after="searchAdvancedAssertSimpleProduct1ImageNotDefault"/> + <waitForLoadingMaskToDisappear stepKey="waitForSearchSimpleProduct1Viewloaded" after="searchClickSimpleProduct1View"/> + <actionGroup ref="StorefrontCheckSimpleProductActionGroup" stepKey="searchAssertSimpleProduct1Page" after="waitForSearchSimpleProduct1Viewloaded"> + <argument name="product" value="$$createSimpleProduct1$$"/> + </actionGroup> + <!-- @TODO: Move Image check to action group after MQE-697 is fixed --> + <grabAttributeFrom selector="{{StorefrontProductInfoMainSection.productImage}}" userInput="src" stepKey="searchAdvancedGrabSimpleProduct1PageImageSrc" after="searchAssertSimpleProduct1Page"/> + <assertNotRegExp stepKey="searchAdvancedAssertSimpleProduct1PageImageNotDefault" after="searchAdvancedGrabSimpleProduct1PageImageSrc"> + <actualResult type="const">$searchAdvancedGrabSimpleProduct1PageImageSrc</actualResult> + <expectedResult type="const">'/placeholder\/image\.jpg/'</expectedResult> + </assertNotRegExp> + + <!-- Quick Search with common part of product names --> + <comment userInput="Quick search" stepKey="commentQuickSearch" after="searchAdvancedAssertSimpleProduct1PageImageNotDefault"/> + <actionGroup ref="StorefrontCheckQuickSearchActionGroup" stepKey="searchQuickSearchCommonPart" after="commentQuickSearch"> + <!-- @TODO: Change to scalar value after MQE-498 is implemented --> + <argument name="phrase" value="CONST.apiSimpleProduct"/> + </actionGroup> + <actionGroup ref="StorefrontSelectSearchFilterCategoryActionGroup" stepKey="searchSelectFilterCategoryCommonPart" after="searchQuickSearchCommonPart"> + <argument name="category" value="$$createCategory$$"/> + </actionGroup> + <see userInput="3" selector="{{StorefrontCategoryMainSection.productCount}} span" stepKey="searchAssertFilterCategoryProductCountCommonPart" after="searchSelectFilterCategoryCommonPart"/> + + <!-- Search simple product 1 --> + <comment userInput="Search simple product 1" stepKey="commentSearchSimpleProduct1" after="searchAssertFilterCategoryProductCountCommonPart"/> + <actionGroup ref="StorefrontCheckCategorySimpleProductActionGroup" stepKey="searchAssertFilterCategorySimpleProduct1" after="commentSearchSimpleProduct1"> + <argument name="product" value="$$createSimpleProduct1$$"/> + </actionGroup> + <!-- @TODO: Move Image check to action group after MQE-697 is fixed --> + <grabAttributeFrom selector="{{StorefrontCategoryProductSection.ProductImageByName($$createSimpleProduct1.name$$)}}" userInput="src" stepKey="searchGrabSimpleProduct1ImageSrc" after="searchAssertFilterCategorySimpleProduct1"/> + <assertNotRegExp stepKey="searchAssertSimpleProduct1ImageNotDefault" after="searchGrabSimpleProduct1ImageSrc"> + <actualResult type="const">$searchGrabSimpleProduct1ImageSrc</actualResult> + <expectedResult type="const">'/placeholder\/small_image\.jpg/'</expectedResult> + </assertNotRegExp> + <!-- Search simple product2 --> + <actionGroup ref="StorefrontCheckCategorySimpleProductActionGroup" stepKey="searchAssertFilterCategorySimpleProduct2" after="searchAssertSimpleProduct1ImageNotDefault"> + <argument name="product" value="$$createSimpleProduct2$$"/> + </actionGroup> + <!-- @TODO: Move Image check to action group after MQE-697 is fixed --> + <grabAttributeFrom selector="{{StorefrontCategoryProductSection.ProductImageByName($$createSimpleProduct2.name$$)}}" userInput="src" stepKey="searchGrabSimpleProduct2ImageSrc" after="searchAssertFilterCategorySimpleProduct2"/> + <assertNotRegExp stepKey="searchAssertSimpleProduct2ImageNotDefault" after="searchGrabSimpleProduct2ImageSrc"> + <actualResult type="const">$searchGrabSimpleProduct2ImageSrc</actualResult> + <expectedResult type="const">'/placeholder\/small_image\.jpg/'</expectedResult> + </assertNotRegExp> + + <!-- Quick Search with non-existent product name --> + <comment userInput="Quick Search with non-existent product name" stepKey="commentQuickSearchWithNonExistentProductName" after="searchAssertSimpleProduct2ImageNotDefault"/> + <actionGroup ref="StorefrontCheckQuickSearchActionGroup" stepKey="searchFillQuickSearchNonExistent" after="commentQuickSearchWithNonExistentProductName"> + <!-- @TODO: Change to scalar value after MQE-498 is implemented --> + <argument name="phrase" value="CONST.nonexistentProductName"/> + </actionGroup> + <see userInput="Your search returned no results." selector="{{StorefrontCatalogSearchMainSection.message}}" stepKey="searchAssertQuickSearchMessageNonExistent" after="searchFillQuickSearchNonExistent"/> + <comment userInput="End of searching products" stepKey="endOfSearchingProducts" after="searchAssertQuickSearchMessageNonExistent"/> + </test> +</tests> diff --git a/app/code/Magento/CatalogSearch/Test/Mftf/Test/EndToEndB2CGuestUserTest/EndToEndB2CGuestUserTest.xml b/app/code/Magento/CatalogSearch/Test/Mftf/Test/EndToEndB2CGuestUserTest/EndToEndB2CGuestUserTest.xml new file mode 100644 index 0000000000000..a0d856df1b93c --- /dev/null +++ b/app/code/Magento/CatalogSearch/Test/Mftf/Test/EndToEndB2CGuestUserTest/EndToEndB2CGuestUserTest.xml @@ -0,0 +1,88 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + /** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +--> +<tests xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/testSchema.xsd"> + <test name="EndToEndB2CGuestUserTest"> + <!-- Step 2: User searches for product --> + <comment userInput="Start of searching products" stepKey="startOfSearchingProducts" after="endOfBrowsingCatalog"/> + <!-- Advanced Search with Product Data --> + <comment userInput="Advanced search" stepKey="commentAdvancedSearch" after="startOfSearchingProducts"/> + <actionGroup ref="StorefrontOpenAdvancedSearchActionGroup" stepKey="searchOpenAdvancedSearchForm" after="commentAdvancedSearch"/> + <!-- @TODO: Change to scalar value after MQE-498 is implemented --> + <fillField userInput="$$createSimpleProduct1.name$$" selector="{{StorefrontCatalogSearchAdvancedFormSection.ProductName}}" stepKey="searchAdvancedFillProductName" after="searchOpenAdvancedSearchForm"/> + <fillField userInput="$$createSimpleProduct1.sku$$" selector="{{StorefrontCatalogSearchAdvancedFormSection.SKU}}" stepKey="searchAdvancedFillSKU" after="searchAdvancedFillProductName"/> + <fillField userInput="$$createSimpleProduct1.price$$" selector="{{StorefrontCatalogSearchAdvancedFormSection.PriceFrom}}" stepKey="searchAdvancedFillPriceFrom" after="searchAdvancedFillSKU"/> + <fillField userInput="$$createSimpleProduct1.price$$" selector="{{StorefrontCatalogSearchAdvancedFormSection.PriceTo}}" stepKey="searchAdvancedFillPriceTo" after="searchAdvancedFillPriceFrom"/> + <click selector="{{StorefrontCatalogSearchAdvancedFormSection.SubmitButton}}" stepKey="searchClickAdvancedSearchSubmitButton" after="searchAdvancedFillPriceTo"/> + <waitForLoadingMaskToDisappear stepKey="waitForSearchProductsloaded" after="searchClickAdvancedSearchSubmitButton"/> + <actionGroup ref="StorefrontCheckAdvancedSearchResultActionGroup" stepKey="searchCheckAdvancedSearchResult" after="waitForSearchProductsloaded"/> + <see userInput="4" selector="{{StorefrontCatalogSearchAdvancedResultMainSection.productCount}} span" stepKey="searchAdvancedAssertProductCount" after="searchCheckAdvancedSearchResult"/> + <actionGroup ref="StorefrontCheckCategorySimpleProductActionGroup" stepKey="searchAssertSimpleProduct1" after="searchAdvancedAssertProductCount"> + <argument name="product" value="$$createSimpleProduct1$$"/> + </actionGroup> + <!-- @TODO: Move Image check to action group after MQE-697 is fixed --> + <grabAttributeFrom selector="{{StorefrontCategoryProductSection.ProductImageByName($$createSimpleProduct1.name$$)}}" userInput="src" stepKey="searchAdvancedGrabSimpleProduct1ImageSrc" after="searchAssertSimpleProduct1"/> + <assertNotRegExp stepKey="searchAdvancedAssertSimpleProduct1ImageNotDefault" after="searchAdvancedGrabSimpleProduct1ImageSrc"> + <actualResult type="const">$searchAdvancedGrabSimpleProduct1ImageSrc</actualResult> + <expectedResult type="const">'/placeholder\/small_image\.jpg/'</expectedResult> + </assertNotRegExp> + <click selector="{{StorefrontCategoryProductSection.ProductTitleByName($$createSimpleProduct1.name$$)}}" stepKey="searchClickSimpleProduct1View" after="searchAdvancedAssertSimpleProduct1ImageNotDefault"/> + <waitForLoadingMaskToDisappear stepKey="waitForSearchSimpleProduct1Viewloaded" after="searchClickSimpleProduct1View"/> + <actionGroup ref="StorefrontCheckSimpleProductActionGroup" stepKey="searchAssertSimpleProduct1Page" after="waitForSearchSimpleProduct1Viewloaded"> + <argument name="product" value="$$createSimpleProduct1$$"/> + </actionGroup> + <!-- @TODO: Move Image check to action group after MQE-697 is fixed --> + <grabAttributeFrom selector="{{StorefrontProductInfoMainSection.productImage}}" userInput="src" stepKey="searchAdvancedGrabSimpleProduct1PageImageSrc" after="searchAssertSimpleProduct1Page"/> + <assertNotRegExp stepKey="searchAdvancedAssertSimpleProduct1PageImageNotDefault" after="searchAdvancedGrabSimpleProduct1PageImageSrc"> + <actualResult type="const">$searchAdvancedGrabSimpleProduct1PageImageSrc</actualResult> + <expectedResult type="const">'/placeholder\/image\.jpg/'</expectedResult> + </assertNotRegExp> + + <!-- Quick Search with common part of product names --> + <comment userInput="Quick search" stepKey="commentQuickSearch" after="searchAdvancedAssertSimpleProduct1PageImageNotDefault"/> + <actionGroup ref="StorefrontCheckQuickSearchActionGroup" stepKey="searchQuickSearchCommonPart" after="commentQuickSearch"> + <!-- @TODO: Change to scalar value after MQE-498 is implemented --> + <argument name="phrase" value="CONST.apiSimpleProduct"/> + </actionGroup> + <actionGroup ref="StorefrontSelectSearchFilterCategoryActionGroup" stepKey="searchSelectFilterCategoryCommonPart" after="searchQuickSearchCommonPart"> + <argument name="category" value="$$createCategory$$"/> + </actionGroup> + <see userInput="3" selector="{{StorefrontCategoryMainSection.productCount}} span" stepKey="searchAssertFilterCategoryProductCountCommonPart" after="searchSelectFilterCategoryCommonPart"/> + + <!-- Search simple product 1 --> + <comment userInput="Search simple product 1" stepKey="commentSearchSimpleProduct1" after="searchAssertFilterCategoryProductCountCommonPart"/> + <actionGroup ref="StorefrontCheckCategorySimpleProductActionGroup" stepKey="searchAssertFilterCategorySimpleProduct1" after="commentSearchSimpleProduct1"> + <argument name="product" value="$$createSimpleProduct1$$"/> + </actionGroup> + <!-- @TODO: Move Image check to action group after MQE-697 is fixed --> + <grabAttributeFrom selector="{{StorefrontCategoryProductSection.ProductImageByName($$createSimpleProduct1.name$$)}}" userInput="src" stepKey="searchGrabSimpleProduct1ImageSrc" after="searchAssertFilterCategorySimpleProduct1"/> + <assertNotRegExp stepKey="searchAssertSimpleProduct1ImageNotDefault" after="searchGrabSimpleProduct1ImageSrc"> + <actualResult type="const">$searchGrabSimpleProduct1ImageSrc</actualResult> + <expectedResult type="const">'/placeholder\/small_image\.jpg/'</expectedResult> + </assertNotRegExp> + <!-- Search simple product2 --> + <actionGroup ref="StorefrontCheckCategorySimpleProductActionGroup" stepKey="searchAssertFilterCategorySimpleProduct2" after="searchAssertSimpleProduct1ImageNotDefault"> + <argument name="product" value="$$createSimpleProduct2$$"/> + </actionGroup> + <!-- @TODO: Move Image check to action group after MQE-697 is fixed --> + <grabAttributeFrom selector="{{StorefrontCategoryProductSection.ProductImageByName($$createSimpleProduct2.name$$)}}" userInput="src" stepKey="searchGrabSimpleProduct2ImageSrc" after="searchAssertFilterCategorySimpleProduct2"/> + <assertNotRegExp stepKey="searchAssertSimpleProduct2ImageNotDefault" after="searchGrabSimpleProduct2ImageSrc"> + <actualResult type="const">$searchGrabSimpleProduct2ImageSrc</actualResult> + <expectedResult type="const">'/placeholder\/small_image\.jpg/'</expectedResult> + </assertNotRegExp> + + <!-- Quick Search with non-existent product name --> + <comment userInput="Quick Search with non-existent product name" stepKey="commentQuickSearchWithNonExistentProductName" after="searchAssertSimpleProduct2ImageNotDefault"/> + <actionGroup ref="StorefrontCheckQuickSearchActionGroup" stepKey="searchFillQuickSearchNonExistent" after="commentQuickSearchWithNonExistentProductName"> + <!-- @TODO: Change to scalar value after MQE-498 is implemented --> + <argument name="phrase" value="CONST.nonexistentProductName"/> + </actionGroup> + <see userInput="Your search returned no results." selector="{{StorefrontCatalogSearchMainSection.message}}" stepKey="searchAssertQuickSearchMessageNonExistent" after="searchFillQuickSearchNonExistent"/> + <comment userInput="End of searching products" stepKey="endOfSearchingProducts" after="searchAssertQuickSearchMessageNonExistent"/> + </test> +</tests> diff --git a/app/code/Magento/CatalogSearch/Test/Mftf/Test/EndToEndB2CLoggedInUserTest.xml b/app/code/Magento/CatalogSearch/Test/Mftf/Test/EndToEndB2CLoggedInUserTest.xml index cd3dec912a3c1..51d9973117c6f 100644 --- a/app/code/Magento/CatalogSearch/Test/Mftf/Test/EndToEndB2CLoggedInUserTest.xml +++ b/app/code/Magento/CatalogSearch/Test/Mftf/Test/EndToEndB2CLoggedInUserTest.xml @@ -28,7 +28,10 @@ </actionGroup> <!-- @TODO: Move Image check to action group after MQE-697 is fixed --> <grabAttributeFrom selector="{{StorefrontCategoryProductSection.ProductImageByName($$createSimpleProduct1.name$$)}}" userInput="src" stepKey="searchAdvancedGrabSimpleProduct1ImageSrc" after="searchAssertSimpleProduct1"/> - <assertNotRegExp expected="'/placeholder\/small_image\.jpg/'" actual="$searchAdvancedGrabSimpleProduct1ImageSrc" stepKey="searchAdvancedAssertSimpleProduct1ImageNotDefault" after="searchAdvancedGrabSimpleProduct1ImageSrc"/> + <assertNotRegExp stepKey="searchAdvancedAssertSimpleProduct1ImageNotDefault" after="searchAdvancedGrabSimpleProduct1ImageSrc"> + <actualResult type="const">$searchAdvancedGrabSimpleProduct1ImageSrc</actualResult> + <expectedResult type="const">'/placeholder\/small_image\.jpg/'</expectedResult> + </assertNotRegExp> <click selector="{{StorefrontCategoryProductSection.ProductTitleByName($$createSimpleProduct1.name$$)}}" stepKey="searchClickSimpleProduct1View" after="searchAdvancedAssertSimpleProduct1ImageNotDefault"/> <waitForLoadingMaskToDisappear stepKey="waitForSearchSimpleProduct1Viewloaded" after="searchClickSimpleProduct1View"/> <actionGroup ref="StorefrontCheckSimpleProductActionGroup" stepKey="searchAssertSimpleProduct1Page" after="waitForSearchSimpleProduct1Viewloaded"> @@ -36,7 +39,10 @@ </actionGroup> <!-- @TODO: Move Image check to action group after MQE-697 is fixed --> <grabAttributeFrom selector="{{StorefrontProductInfoMainSection.productImage}}" userInput="src" stepKey="searchAdvancedGrabSimpleProduct1PageImageSrc" after="searchAssertSimpleProduct1Page"/> - <assertNotRegExp expected="'/placeholder\/image\.jpg/'" actual="$searchAdvancedGrabSimpleProduct1PageImageSrc" stepKey="searchAdvancedAssertSimpleProduct1PageImageNotDefault" after="searchAdvancedGrabSimpleProduct1PageImageSrc"/> + <assertNotRegExp stepKey="searchAdvancedAssertSimpleProduct1PageImageNotDefault" after="searchAdvancedGrabSimpleProduct1PageImageSrc"> + <actualResult type="const">$searchAdvancedGrabSimpleProduct1PageImageSrc</actualResult> + <expectedResult type="const">'/placeholder\/image\.jpg/'</expectedResult> + </assertNotRegExp> <!-- Quick Search with common part of product names --> <comment userInput="Quick search" stepKey="commentQuickSearch" after="searchAdvancedAssertSimpleProduct1PageImageNotDefault"/> @@ -56,14 +62,20 @@ </actionGroup> <!-- @TODO: Move Image check to action group after MQE-697 is fixed --> <grabAttributeFrom selector="{{StorefrontCategoryProductSection.ProductImageByName($$createSimpleProduct1.name$$)}}" userInput="src" stepKey="searchGrabSimpleProduct1ImageSrc" after="searchAssertFilterCategorySimpleProduct1"/> - <assertNotRegExp expected="'/placeholder\/small_image\.jpg/'" actual="$searchGrabSimpleProduct1ImageSrc" stepKey="searchAssertSimpleProduct1ImageNotDefault" after="searchGrabSimpleProduct1ImageSrc"/> + <assertNotRegExp stepKey="searchAssertSimpleProduct1ImageNotDefault" after="searchGrabSimpleProduct1ImageSrc"> + <actualResult type="const">$searchGrabSimpleProduct1ImageSrc</actualResult> + <expectedResult type="const">'/placeholder\/small_image\.jpg/'</expectedResult> + </assertNotRegExp> <!-- Search simple product2 --> <actionGroup ref="StorefrontCheckCategorySimpleProductActionGroup" stepKey="searchAssertFilterCategorySimpleProduct2" after="searchAssertSimpleProduct1ImageNotDefault"> <argument name="product" value="$$createSimpleProduct2$$"/> </actionGroup> <!-- @TODO: Move Image check to action group after MQE-697 is fixed --> <grabAttributeFrom selector="{{StorefrontCategoryProductSection.ProductImageByName($$createSimpleProduct2.name$$)}}" userInput="src" stepKey="searchGrabSimpleProduct2ImageSrc" after="searchAssertFilterCategorySimpleProduct2"/> - <assertNotRegExp expected="'/placeholder\/small_image\.jpg/'" actual="$searchGrabSimpleProduct2ImageSrc" stepKey="searchAssertSimpleProduct2ImageNotDefault" after="searchGrabSimpleProduct2ImageSrc"/> + <assertNotRegExp stepKey="searchAssertSimpleProduct2ImageNotDefault" after="searchGrabSimpleProduct2ImageSrc"> + <actualResult type="const">$searchGrabSimpleProduct2ImageSrc</actualResult> + <expectedResult type="const">'/placeholder\/small_image\.jpg/'</expectedResult> + </assertNotRegExp> <!-- Quick Search with non-existent product name --> <comment userInput="Quick Search with non-existent product name" stepKey="commentQuickSearchWithNonExistentProductName" after="searchAssertSimpleProduct2ImageNotDefault" /> diff --git a/app/code/Magento/CatalogSearch/Test/Mftf/Test/LayerNavigationOfCatalogSearchTest.xml b/app/code/Magento/CatalogSearch/Test/Mftf/Test/LayerNavigationOfCatalogSearchTest.xml deleted file mode 100644 index fa9407495e6e7..0000000000000 --- a/app/code/Magento/CatalogSearch/Test/Mftf/Test/LayerNavigationOfCatalogSearchTest.xml +++ /dev/null @@ -1,92 +0,0 @@ -<?xml version="1.0" encoding="UTF-8"?> -<!-- - /** - * Copyright © Magento, Inc. All rights reserved. - * See COPYING.txt for license details. - */ ---> - -<tests xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" - xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/testSchema.xsd"> - <test name="LayerNavigationOfCatalogSearchTest"> - <annotations> - <stories value="Search terms"/> - <title value="Layer Navigation of Catalog Search Should Equalize Price Range As Default Configuration"/> - <description value="Make sure filter of custom attribute with type of price displays on storefront Catalog page and price range should respect the configuration in Admin site"/> - <testCaseId value="MC-16979"/> - <useCaseId value="MC-16650"/> - <severity value="MAJOR"/> - <group value="CatalogSearch"/> - </annotations> - <before> - <magentoCLI command="config:set catalog/layered_navigation/price_range_calculation auto" stepKey="setAutoPriceRange"/> - <createData stepKey="createPriceAttribute" entity="productAttributeTypeOfPrice"/> - <createData stepKey="assignPriceAttributeGroup" entity="AddToDefaultSet"> - <requiredEntity createDataKey="createPriceAttribute"/> - </createData> - <createData entity="SimpleSubCategory" stepKey="subCategory"/> - <createData entity="SimpleProduct" stepKey="simpleProduct1"> - <requiredEntity createDataKey="subCategory"/> - </createData> - <createData entity="SimpleProduct" stepKey="simpleProduct2"> - <requiredEntity createDataKey="subCategory"/> - </createData> - <actionGroup ref = "LoginAsAdmin" stepKey="loginAsAdmin"/> - </before> - <after> - <deleteData stepKey="deleteSimpleSubCategory" createDataKey="subCategory"/> - <deleteData stepKey="deleteSimpleProduct1" createDataKey="simpleProduct1"/> - <deleteData stepKey="deleteSimpleProduct2" createDataKey="simpleProduct2"/> - <deleteData createDataKey="createPriceAttribute" stepKey="deleteAttribute"/> - <actionGroup ref="logout" stepKey="logout"/> - </after> - <!--Update value for price attribute of Product 1--> - <comment userInput="Update value for price attribute of Product 1" stepKey="comment1"/> - <actionGroup ref="NavigateToCreatedProductEditPageActionGroup" stepKey="navigateToCreatedProductEditPage1"> - <argument name="product" value="$$simpleProduct1$$"/> - </actionGroup> - <grabTextFrom selector="{{AdminProductFormSection.attributeLabelByText($$createPriceAttribute.attribute[frontend_labels][0][label]$$)}}" stepKey="grabAttributeLabel"/> - <fillField selector="{{AdminProductAttributeSection.customAttribute($$createPriceAttribute.attribute_code$$)}}" userInput="30" stepKey="fillCustomPrice1"/> - <click selector="{{AdminProductFormSection.save}}" stepKey="clickSaveButton1"/> - <waitForPageLoad stepKey="waitForSimpleProductSaved1"/> - <!--Update value for price attribute of Product 2--> - <comment userInput="Update value for price attribute of Product 1" stepKey="comment2"/> - <actionGroup ref="NavigateToCreatedProductEditPageActionGroup" stepKey="navigateToCreatedProductEditPage2"> - <argument name="product" value="$$simpleProduct2$$"/> - </actionGroup> - <fillField selector="{{AdminProductAttributeSection.customAttribute($$createPriceAttribute.attribute_code$$)}}" userInput="70" stepKey="fillCustomPrice2"/> - <click selector="{{AdminProductFormSection.save}}" stepKey="clickSaveButton2"/> - <waitForPageLoad stepKey="waitForSimpleProductSaved2"/> - - <!--Run re-index task--> - <magentoCLI command="indexer:reindex" stepKey="reindex"/> - - <!--Navigate to category on Storefront--> - <comment userInput="Navigate to category on Storefront" stepKey="comment3"/> - <amOnPage url="{{StorefrontCategoryPage.url($$subCategory.name$$)}}" stepKey="goToCategoryStorefront"/> - <waitForPageLoad stepKey="waitForPageLoad"/> - <see userInput="{$grabAttributeLabel}" selector="{{StorefrontCategoryFilterSection.CustomPriceAttribute}}" stepKey="seePriceLayerNavigationOnStorefront"/> - </test> -</tests> - - - - - - - - - - - - - - - - - - - - - - diff --git a/app/code/Magento/CatalogSearch/Test/Mftf/Test/MinimalQueryLengthForCatalogSearchTest.xml b/app/code/Magento/CatalogSearch/Test/Mftf/Test/MinimalQueryLengthForCatalogSearchTest.xml index 89269a1ad0d9e..437357ba861f2 100644 --- a/app/code/Magento/CatalogSearch/Test/Mftf/Test/MinimalQueryLengthForCatalogSearchTest.xml +++ b/app/code/Magento/CatalogSearch/Test/Mftf/Test/MinimalQueryLengthForCatalogSearchTest.xml @@ -25,13 +25,13 @@ <createData entity="ApiSimpleProduct" stepKey="createProduct"> <requiredEntity createDataKey="createCategory"/> </createData> - <actionGroup ref="LoginAsAdmin" stepKey="loginAsAdmin"/> + <actionGroup ref="AdminLoginActionGroup" stepKey="loginAsAdmin"/> </before> <after> <deleteData createDataKey="createCategory" stepKey="deleteCategory"/> <deleteData createDataKey="createProduct" stepKey="deleteProduct"/> <createData entity="SetMinQueryLengthToDefault" stepKey="setMinimumQueryLengthToDefault"/> - <actionGroup ref="logout" stepKey="logout"/> + <actionGroup ref="AdminLogoutActionGroup" stepKey="logout"/> </after> <actionGroup ref="SetMinimalQueryLengthActionGroup" stepKey="setMinQueryLength"/> <comment userInput="Go to Storefront and search for product" stepKey="searchProdUsingMinQueryLength"/> diff --git a/app/code/Magento/CatalogSearch/Test/Mftf/Test/SearchEntityResultsTest.xml b/app/code/Magento/CatalogSearch/Test/Mftf/Test/SearchEntityResultsTest.xml deleted file mode 100644 index cc6f202272e3b..0000000000000 --- a/app/code/Magento/CatalogSearch/Test/Mftf/Test/SearchEntityResultsTest.xml +++ /dev/null @@ -1,703 +0,0 @@ -<?xml version="1.0" encoding="UTF-8"?> -<!-- - /** - * Copyright © Magento, Inc. All rights reserved. - * See COPYING.txt for license details. - */ ---> - -<tests xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" - xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/testSchema.xsd"> - <test name="QuickSearchProductBySku"> - <annotations> - <stories value="Search Product on Storefront"/> - <title value="User should be able to use Quick Search to find products"/> - <description value="Use Quick Search to find a product"/> - <severity value="MAJOR"/> - <testCaseId value="MC-14783"/> - <group value="CatalogSearch"/> - <group value="mtf_migrated"/> - </annotations> - <before> - <createData entity="_defaultCategory" stepKey="createCategory"/> - <createData entity="_defaultProduct" stepKey="createSimpleProduct"> - <requiredEntity createDataKey="createCategory"/> - </createData> - - <!-- Perform reindex and flush cache --> - <magentoCLI command="indexer:reindex" stepKey="reindex"/> - <magentoCLI command="cache:flush" stepKey="flushCache"/> - </before> - <after> - <deleteData stepKey="deleteProduct" createDataKey="createSimpleProduct"/> - <deleteData stepKey="deleteCategory" createDataKey="createCategory"/> - </after> - <amOnPage url="{{StorefrontHomePage.url}}" stepKey="goToFrontPage"/> - <actionGroup ref="StorefrontCheckQuickSearchStringActionGroup" stepKey="searchStorefront"> - <argument name="phrase" value="$createSimpleProduct.sku$"/> - </actionGroup> - <actionGroup ref="StorefrontOpenProductFromQuickSearchActionGroup" stepKey="openAndCheckProduct"> - <argument name="productName" value="$createSimpleProduct.name$"/> - <argument name="productUrlKey" value="$createSimpleProduct.custom_attributes[url_key]$"/> - </actionGroup> - </test> - <test name="QuickSearchProductByName" extends="QuickSearchProductBySku"> - <annotations> - <stories value="Search Product on Storefront"/> - <title value="User should be able to use Quick Search to find products via Name"/> - <description value="Use Quick Search to find a product"/> - <severity value="MAJOR"/> - <testCaseId value="MC-14791"/> - <group value="CatalogSearch"/> - <group value="mtf_migrated"/> - </annotations> - <!-- Overwrite search to use name --> - <actionGroup ref="StorefrontCheckQuickSearchStringActionGroup" stepKey="searchStorefront"> - <argument name="phrase" value="$createSimpleProduct.name$"/> - </actionGroup> - </test> - <test name="QuickSearchProductByNameWithSpecialChars" extends="QuickSearchProductBySku"> - <annotations> - <stories value="Search Product on Storefront"/> - <title value="Quick Search can find products with names that contain special characters"/> - <description value="Use Quick Search to find a product by name"/> - <severity value="MAJOR"/> - <testCaseId value="MC-14792"/> - <group value="CatalogSearch"/> - <group value="mtf_migrated"/> - </annotations> - <before> - <createData entity="productWithSpecialCharacters" stepKey="createSimpleProduct"> - <requiredEntity createDataKey="createCategory"/> - </createData> - </before> - <!-- Overwrite search to use name --> - <actionGroup ref="StorefrontCheckQuickSearchStringActionGroup" stepKey="searchStorefront"> - <argument name="phrase" value="$createSimpleProduct.name$"/> - </actionGroup> - </test> - <test name="QuickSearchEmptyResults"> - <annotations> - <features value="CatalogSearch"/> - <stories value="Search Product on Storefront"/> - <title value="User should not get search results on query that doesn't return anything"/> - <description value="Use invalid query to return no products"/> - <severity value="MAJOR"/> - <testCaseId value="MC-14793"/> - <group value="CatalogSearch"/> - <group value="mtf_migrated"/> - </annotations> - <before> - <createData entity="_defaultCategory" stepKey="createCategory"/> - <createData entity="_defaultProduct" stepKey="createSimpleProduct"> - <requiredEntity createDataKey="createCategory"/> - </createData> - - <!-- Perform reindex and flush cache --> - <magentoCLI command="indexer:reindex" stepKey="reindex"/> - <magentoCLI command="cache:flush" stepKey="flushCache"/> - </before> - <after> - <deleteData createDataKey="createSimpleProduct" stepKey="deleteProduct"/> - <deleteData createDataKey="createCategory" stepKey="deleteCategory"/> - </after> - - <amOnPage url="{{StorefrontHomePage.url}}" stepKey="goToFrontPage"/> - <actionGroup ref="StorefrontCheckQuickSearchStringActionGroup" stepKey="searchStorefront"> - <argument name="phrase" value="ThisShouldn'tReturnAnything"/> - </actionGroup> - <actionGroup ref="StorefrontCheckSearchIsEmptyActionGroup" stepKey="checkEmpty"/> - </test> - - <test name="QuickSearchWithTwoCharsEmptyResults" extends="QuickSearchEmptyResults"> - <annotations> - <features value="CatalogSearch"/> - <stories value="Search Product on Storefront"/> - <title value="User should not get search results on query that only contains two characters"/> - <description value="Use of 2 character query to return no products"/> - <severity value="MAJOR"/> - <testCaseId value="MC-14794"/> - <group value="CatalogSearch"/> - <group value="mtf_migrated"/> - </annotations> - - <before> - <magentoCLI command="config:set {{MinimalQueryLengthFourConfigData.path}} {{MinimalQueryLengthFourConfigData.value}}" after="createSimpleProduct" stepKey="setMinimalQueryLengthToFour"/> - </before> - - <after> - <magentoCLI command="config:set {{MinimalQueryLengthDefaultConfigData.path}} {{MinimalQueryLengthDefaultConfigData.value}}" after="deleteCategory" stepKey="setMinimalQueryLengthToFour"/> - </after> - - <executeJS function="var s = '$createSimpleProduct.name$'; var ret=s.substring(0,{{MinimalQueryLengthFourConfigData.value}} - 1); return ret;" before="searchStorefront" stepKey="getFirstLessThenConfigLetters"/> - - <actionGroup ref="StorefrontCheckQuickSearchStringActionGroup" after="checkEmpty" stepKey="searchStorefrontConfigLetters"> - <argument name="phrase" value="$createSimpleProduct.name$"/> - </actionGroup> - <actionGroup ref="StorefrontQuickSearchTooShortStringActionGroup" after="searchStorefrontConfigLetters" stepKey="checkCannotSearchWithTooShortString"> - <argument name="phrase" value="$getFirstLessThenConfigLetters"/> - <argument name="minQueryLength" value="{{MinimalQueryLengthFourConfigData.value}}"/> - </actionGroup> - <actionGroup ref="StorefrontQuickSearchRelatedSearchTermsAppearsActionGroup" after="checkCannotSearchWithTooShortString" stepKey="checkRelatedSearchTerm"> - <argument name="term" value="$createSimpleProduct.name$"/> - </actionGroup> - </test> - - <test name="QuickSearchProductByNameWithThreeLetters" extends="QuickSearchProductBySku"> - <annotations> - <stories value="Search Product on Storefront"/> - <title value="User should be able to use Quick Search to find products by their first three letters"/> - <description value="Use Quick Search to find a product using only first three letters"/> - <severity value="MAJOR"/> - <testCaseId value="MC-15034"/> - <group value="CatalogSearch"/> - <group value="SearchEngineMysql"/> - <group value="mtf_migrated"/> - </annotations> - <executeJS function="var s = '$createSimpleProduct.name$'; var ret=s.substring(0,3); return ret;" stepKey="getFirstThreeLetters" before="searchStorefront"/> - <actionGroup ref="StorefrontCheckQuickSearchStringActionGroup" stepKey="searchStorefront"> - <argument name="phrase" value="{$getFirstThreeLetters}"/> - </actionGroup> - </test> - <test name="QuickSearchProductBy128CharQuery" extends="QuickSearchProductBySku"> - <annotations> - <stories value="Search Product on Storefront"/> - <title value="User should be able to use Quick Search product with long names, using first 128 letters"/> - <description value="Use Quick Search to find a product with name of 130 length with query of only 128"/> - <severity value="MAJOR"/> - <testCaseId value="MC-14795"/> - <group value="CatalogSearch"/> - <group value="mtf_migrated"/> - </annotations> - <before> - <createData entity="productWith130CharName" stepKey="createSimpleProduct"> - <requiredEntity createDataKey="createCategory"/> - </createData> - </before> - <executeJS function="var s = '$createSimpleProduct.name$'; var ret=s.substring(0,128); return ret;" stepKey="get128Letters" before="searchStorefront"/> - <actionGroup ref="StorefrontCheckQuickSearchStringActionGroup" stepKey="searchStorefront"> - <argument name="phrase" value="{$get128Letters}"/> - </actionGroup> - </test> - - <test name="QuickSearchTwoProductsWithSameWeight"> - <annotations> - <stories value="Search Product on Storefront"/> - <title value="Quick Search should sort products with the same weight appropriately"/> - <description value="Use Quick Search to find a two products with the same weight"/> - <severity value="MAJOR"/> - <testCaseId value="MC-14796"/> - <group value="CatalogSearch"/> - <group value="SearchEngineMysql"/> - <group value="mtf_migrated"/> - </annotations> - <before> - <createData entity="_defaultCategory" stepKey="createCategory"/> - <createData entity="productAlphabeticalA" stepKey="product1"> - <requiredEntity createDataKey="createCategory"/> - </createData> - <createData entity="productAlphabeticalB" stepKey="product2"> - <requiredEntity createDataKey="createCategory"/> - </createData> - <actionGroup ref="LoginAsAdmin" stepKey="loginAsAdmin1"/> - - - <!-- Create and Assign Attribute to product1--> - <actionGroup ref="GoToProductPageViaIDActionGroup" stepKey="goToProduct1"> - <argument name="productId" value="$product1.id$"/> - </actionGroup> - <actionGroup ref="AdminCreateAttributeWithSearchWeightActionGroup" stepKey="createProduct1Attribute"> - <argument name="attributeType" value="Text Field"/> - <argument name="attributeName" value="$product1.name$"/> - <argument name="attributeSetName" value="$product1.name$"/> - <argument name="weight" value="1"/> - <argument name="defaultValue" value="{{_defaultProduct.name}}"/> - </actionGroup> - <actionGroup ref="AdminProductPageSelectAttributeSetActionGroup" stepKey="selectAttributeSet1"> - <argument name="attributeSetName" value="$product1.name$"/> - </actionGroup> - <!--fill in default--> - <actionGroup ref="SaveProductFormActionGroup" stepKey="saveProduct1a"/> - <actionGroup ref="AdminProductPageFillTextAttributeValueByNameActionGroup" stepKey="fillDefault1"> - <argument name="attributeName" value="$product1.name$"/> - <argument name="value" value="{{_defaultProduct.name}}"/> - </actionGroup> - <actionGroup ref="SaveProductFormActionGroup" stepKey="saveProduct1b"/> - <!-- Create and Assign Attribute to product2--> - <actionGroup ref="GoToProductPageViaIDActionGroup" stepKey="goToProduct2"> - <argument name="productId" value="$product2.id$"/> - </actionGroup> - <actionGroup ref="AdminCreateAttributeWithSearchWeightActionGroup" stepKey="createProduct2Attribute"> - <argument name="attributeType" value="Text Field"/> - <argument name="attributeName" value="$product2.name$"/> - <argument name="attributeSetName" value="$product2.name$"/> - <argument name="weight" value="1"/> - <argument name="defaultValue" value="{{_defaultProduct.name}}"/> - </actionGroup> - <actionGroup ref="AdminProductPageSelectAttributeSetActionGroup" stepKey="selectAttributeSet2"> - <argument name="attributeSetName" value="$product2.name$"/> - </actionGroup> - <actionGroup ref="SaveProductFormActionGroup" stepKey="saveProduct2a"/> - <!--fill in default--> - <actionGroup ref="AdminProductPageFillTextAttributeValueByNameActionGroup" stepKey="fillDefault2"> - <argument name="attributeName" value="$product2.name$"/> - <argument name="value" value="{{_defaultProduct.name}}"/> - </actionGroup> - <actionGroup ref="SaveProductFormActionGroup" stepKey="saveProduct2b"/> - </before> - <after> - <deleteData stepKey="deleteProduct1" createDataKey="product1"/> - <deleteData stepKey="deleteProduct2" createDataKey="product2"/> - <deleteData stepKey="deleteCategory" createDataKey="createCategory"/> - </after> - <amOnPage url="{{StorefrontHomePage.url}}" stepKey="goToFrontPage"/> - <actionGroup ref="StorefrontCheckQuickSearchStringActionGroup" stepKey="searchStorefront"> - <argument name="phrase" value="{{_defaultProduct.name}}"/> - </actionGroup> - <actionGroup ref="StorefrontQuickSearchCheckProductNameInGridActionGroup" stepKey="assertProduct1Position"> - <argument name="productName" value="$product1.name$"/> - <argument name="index" value="2"/> - </actionGroup> - <actionGroup ref="StorefrontQuickSearchCheckProductNameInGridActionGroup" stepKey="assertProduct2Position"> - <argument name="productName" value="$product2.name$"/> - <argument name="index" value="1"/> - </actionGroup> - </test> - <test name="QuickSearchTwoProductsWithDifferentWeight" extends="QuickSearchTwoProductsWithSameWeight"> - <annotations> - <stories value="Search Product on Storefront"/> - <title value="Quick Search should sort products with the different weight appropriately"/> - <description value="Use Quick Search to find a two products with the different weight"/> - <severity value="MAJOR"/> - <testCaseId value="MC-14797"/> - <group value="CatalogSearch"/> - <group value="SearchEngineMysql"/> - <group value="mtf_migrated"/> - </annotations> - <before> - <actionGroup ref="AdminCreateAttributeWithSearchWeightActionGroup" stepKey="createProduct1Attribute"> - <argument name="attributeType" value="Text Field"/> - <argument name="attributeName" value="$product1.name$"/> - <argument name="attributeSetName" value="$product1.name$"/> - <argument name="weight" value="5"/> - <argument name="defaultValue" value="{{_defaultProduct.name}}"/> - </actionGroup> - </before> - <actionGroup ref="StorefrontQuickSearchCheckProductNameInGridActionGroup" stepKey="assertProduct1Position"> - <argument name="productName" value="$product1.name$"/> - <argument name="index" value="1"/> - </actionGroup> - <actionGroup ref="StorefrontQuickSearchCheckProductNameInGridActionGroup" stepKey="assertProduct2Position"> - <argument name="productName" value="$product2.name$"/> - <argument name="index" value="2"/> - </actionGroup> - </test> - - <test name="QuickSearchAndAddToCart"> - <annotations> - <stories value="Search Product on Storefront"/> - <title value="User should be able to use Quick Search to find a simple product and add it to cart"/> - <description value="Use Quick Search to find simple Product and Add to Cart"/> - <severity value="MAJOR"/> - <testCaseId value="MC-14784"/> - <group value="CatalogSearch"/> - <group value="mtf_migrated"/> - </annotations> - <before> - <createData entity="_defaultCategory" stepKey="createCategory"/> - <createData entity="_defaultProduct" stepKey="createSimpleProduct"> - <requiredEntity createDataKey="createCategory"/> - </createData> - - <!-- Perform reindex and flush cache --> - <magentoCLI command="indexer:reindex" stepKey="reindex"/> - <magentoCLI command="cache:flush" stepKey="flushCache"/> - </before> - <after> - <deleteData stepKey="deleteProduct" createDataKey="createSimpleProduct"/> - <deleteData stepKey="deleteCategory" createDataKey="createCategory"/> - </after> - <amOnPage url="{{StorefrontHomePage.url}}" stepKey="goToFrontPage"/> - <actionGroup ref="StorefrontCheckQuickSearchStringActionGroup" stepKey="searchStorefront"> - <argument name="phrase" value="$createSimpleProduct.name$"/> - </actionGroup> - <actionGroup ref="StorefrontAddToCartFromQuickSearchActionGroup" stepKey="addProductToCart"> - <argument name="productName" value="$createSimpleProduct.name$"/> - </actionGroup> - </test> - <test name="QuickSearchAndAddToCartVirtual"> - <annotations> - <stories value="Search Product on Storefront"/> - <title value="User should be able to use Quick Search to find a virtual product and add it to cart"/> - <description value="Use Quick Search to find virtual Product and Add to Cart"/> - <severity value="MAJOR"/> - <testCaseId value="MC-14785"/> - <group value="CatalogSearch"/> - <group value="mtf_migrated"/> - </annotations> - <before> - <createData entity="_defaultCategory" stepKey="createCategory"/> - <createData entity="VirtualProduct" stepKey="createVirtualProduct"> - <requiredEntity createDataKey="createCategory"/> - </createData> - - <!-- Perform reindex and flush cache --> - <magentoCLI command="indexer:reindex" stepKey="reindex"/> - <magentoCLI command="cache:flush" stepKey="flushCache"/> - </before> - <after> - <deleteData stepKey="deleteProduct" createDataKey="createVirtualProduct"/> - <deleteData stepKey="deleteCategory" createDataKey="createCategory"/> - </after> - <amOnPage url="{{StorefrontHomePage.url}}" stepKey="goToFrontPage"/> - <actionGroup ref="StorefrontCheckQuickSearchStringActionGroup" stepKey="searchStorefront"> - <argument name="phrase" value="$createVirtualProduct.name$"/> - </actionGroup> - <actionGroup ref="StorefrontAddToCartFromQuickSearchActionGroup" stepKey="addProductToCart"> - <argument name="productName" value="$createVirtualProduct.name$"/> - </actionGroup> - </test> - <test name="QuickSearchAndAddToCartConfigurable"> - <annotations> - <stories value="Search Product on Storefront"/> - <title value="User should be able to use Quick Search to find a configurable product and add it to cart"/> - <description value="Use Quick Search to find configurable Product and Add to Cart"/> - <severity value="MAJOR"/> - <testCaseId value="MC-14786"/> - <group value="CatalogSearch"/> - <group value="mtf_migrated"/> - </annotations> - <before> - <createData entity="_defaultCategory" stepKey="createCategory"/> - <actionGroup ref="LoginAsAdmin" stepKey="loginAsAdmin1"/> - <actionGroup ref="CreateConfigurableProductActionGroup" stepKey="createProduct"> - <argument name="product" value="_defaultProduct"/> - <argument name="category" value="$$createCategory$$"/> - </actionGroup> - - <!-- Perform reindex and flush cache --> - <magentoCLI command="indexer:reindex" stepKey="reindex"/> - <magentoCLI command="cache:flush" stepKey="flushCache"/> - </before> - <after> - <deleteData stepKey="deleteCategory" createDataKey="createCategory"/> - <actionGroup ref="DeleteProductBySkuActionGroup" stepKey="deleteProduct"> - <argument name="sku" value="{{_defaultProduct.sku}}"/> - </actionGroup> - <actionGroup ref="ClearFiltersAdminDataGridActionGroup" stepKey="clearProductsGridFilters"/> - <actionGroup ref="logout" stepKey="logoutFromAdmin"/> - </after> - <amOnPage url="{{StorefrontHomePage.url}}" stepKey="goToFrontPage"/> - <actionGroup ref="StorefrontCheckQuickSearchStringActionGroup" stepKey="searchStorefront"> - <argument name="phrase" value="{{_defaultProduct.name}}"/> - </actionGroup> - <actionGroup ref="StorefrontOpenProductFromQuickSearchActionGroup" stepKey="openAndCheckProduct"> - <argument name="productName" value="{{_defaultProduct.name}}"/> - <argument name="productUrlKey" value="{{_defaultProduct.urlKey}}"/> - </actionGroup> - <actionGroup ref="SelectSingleAttributeAndAddToCartActionGroup" stepKey="addProductToCart"> - <argument name="productName" value="{{_defaultProduct.name}}"/> - <argument name="attributeCode" value="{{colorProductAttribute.default_label}}"/> - <argument name="optionName" value="{{colorProductAttribute1.name}}"/> - </actionGroup> - </test> - <test name="QuickSearchAndAddToCartDownloadable"> - <annotations> - <stories value="Search Product on Storefront"/> - <title value="User should be able to use Quick Search to find a downloadable product and add it to cart"/> - <description value="Use Quick Search to find downloadable Product and Add to Cart"/> - <severity value="MAJOR"/> - <testCaseId value="MC-14787"/> - <group value="CatalogSearch"/> - <group value="mtf_migrated"/> - </annotations> - <before> - <magentoCLI stepKey="addDownloadableDomain" command="downloadable:domains:add example.com static.magento.com"/> - <createData entity="_defaultCategory" stepKey="createCategory"/> - <createData entity="DownloadableProductWithOneLink" stepKey="createProduct"> - <requiredEntity createDataKey="createCategory"/> - </createData> - <createData entity="downloadableLink1" stepKey="addDownloadableLink1"> - <requiredEntity createDataKey="createProduct"/> - </createData> - - <!-- Perform reindex and flush cache --> - <magentoCLI command="indexer:reindex" stepKey="reindex"/> - <magentoCLI command="cache:flush" stepKey="flushCache"/> - </before> - <after> - <magentoCLI stepKey="removeDownloadableDomain" command="downloadable:domains:remove example.com static.magento.com"/> - <deleteData stepKey="deleteProduct" createDataKey="createProduct"/> - <deleteData stepKey="deleteCategory" createDataKey="createCategory"/> - </after> - <amOnPage url="{{StorefrontHomePage.url}}" stepKey="goToFrontPage"/> - <actionGroup ref="StorefrontCheckQuickSearchStringActionGroup" stepKey="searchStorefront"> - <argument name="phrase" value="$createProduct.name$"/> - </actionGroup> - <actionGroup ref="StorefrontAddToCartFromQuickSearchActionGroup" stepKey="addProductToCart"> - <argument name="productName" value="$createProduct.name$"/> - </actionGroup> - </test> - <test name="QuickSearchAndAddToCartGrouped"> - <annotations> - <stories value="Search Product on Storefront"/> - <title value="User should be able to use Quick Search to find a grouped product and add it to cart"/> - <description value="Use Quick Search to find grouped Product and Add to Cart"/> - <severity value="MAJOR"/> - <testCaseId value="MC-14788"/> - <group value="CatalogSearch"/> - <group value="mtf_migrated"/> - </annotations> - <before> - <actionGroup ref="LoginAsAdmin" stepKey="loginAsAdmin"/> - <actionGroup ref="DeleteAllProductsUsingProductGridActionGroup" stepKey="deleteAllProducts"/> - <createData entity="ApiProductWithDescription" stepKey="simple1"/> - <createData entity="ApiGroupedProduct" stepKey="createProduct"/> - <createData entity="OneSimpleProductLink" stepKey="addProductOne"> - <requiredEntity createDataKey="createProduct"/> - <requiredEntity createDataKey="simple1"/> - </createData> - - <!-- Perform reindex and flush cache --> - <magentoCLI command="indexer:reindex" stepKey="reindex"/> - <magentoCLI command="cache:flush" stepKey="flushCache"/> - </before> - <after> - <deleteData stepKey="deleteGroupedProduct" createDataKey="createProduct"/> - <deleteData stepKey="deleteSimpleProduct" createDataKey="simple1"/> - <actionGroup ref="logout" stepKey="logoutFromAdmin"/> - </after> - <amOnPage url="{{StorefrontHomePage.url}}" stepKey="goToFrontPage"/> - <actionGroup ref="StorefrontCheckQuickSearchStringActionGroup" stepKey="searchStorefront"> - <argument name="phrase" value=""$createProduct.name$""/> - </actionGroup> - <actionGroup ref="StorefrontAddToCartFromQuickSearchActionGroup" stepKey="addProductToCart"> - <argument name="productName" value="$createProduct.name$"/> - </actionGroup> - </test> - <test name="QuickSearchAndAddToCartBundleDynamic"> - <annotations> - <stories value="Search Product on Storefront"/> - <title value="User should be able to use Quick Search to find a Bundle Dynamic product and add it to cart"/> - <description value="Use Quick Search to find Bundle Dynamic Product and Add to Cart"/> - <severity value="MAJOR"/> - <testCaseId value="MC-14789"/> - <group value="CatalogSearch"/> - <group value="mtf_migrated"/> - </annotations> - <before> - <createData entity="_defaultCategory" stepKey="createCategory"/> - <createData entity="_defaultProduct" stepKey="createProduct"> - <requiredEntity createDataKey="createCategory"/> - </createData> - - <!--Create dynamic product--> - <createData entity="ApiBundleProductPriceViewRange" stepKey="createBundleProduct"> - <requiredEntity createDataKey="createCategory"/> - </createData> - <createData entity="DropDownBundleOption" stepKey="bundleOption"> - <requiredEntity createDataKey="createBundleProduct"/> - </createData> - <createData entity="ApiBundleLink" stepKey="createBundleLink1"> - <requiredEntity createDataKey="createBundleProduct"/> - <requiredEntity createDataKey="bundleOption"/> - <requiredEntity createDataKey="createProduct"/> - <field key="qty">10</field> - </createData> - <!--Finish bundle creation--> - <actionGroup ref="LoginAsAdmin" stepKey="loginAsAdmin"/> - <amOnPage url="{{AdminProductEditPage.url($$createBundleProduct.id$$)}}" stepKey="goToProductEditPage"/> - <actionGroup ref="SaveProductFormActionGroup" stepKey="saveProduct"/> - - <!-- Perform reindex and flush cache --> - <magentoCLI command="indexer:reindex" stepKey="reindex"/> - <magentoCLI command="cache:flush" stepKey="flushCache"/> - </before> - <after> - <deleteData stepKey="deleteBundleProduct" createDataKey="createBundleProduct"/> - <deleteData stepKey="deleteProduct" createDataKey="createProduct"/> - <deleteData stepKey="deleteCategory" createDataKey="createCategory"/> - <actionGroup ref="logout" stepKey="logoutFromAdmin"/> - </after> - <amOnPage url="{{StorefrontHomePage.url}}" stepKey="goToFrontPage"/> - <actionGroup ref="StorefrontCheckQuickSearchStringActionGroup" stepKey="searchStorefront"> - <argument name="phrase" value="$createBundleProduct.name$"/> - </actionGroup> - <actionGroup ref="StorefrontOpenProductFromQuickSearchActionGroup" stepKey="openAndCheckProduct"> - <argument name="productName" value="$createBundleProduct.name$"/> - <argument name="productUrlKey" value="$createBundleProduct.custom_attributes[url_key]$"/> - </actionGroup> - <actionGroup ref="StorefrontAddBundleProductFromProductToCartActionGroup" stepKey="addProductToCart"> - <argument name="productName" value="$createBundleProduct.name$"/> - </actionGroup> - </test> - <test name="QuickSearchAndAddToCartBundleFixed"> - <annotations> - <stories value="Search Product on Storefront"/> - <title value="User should be able to use Quick Search to find a Bundle Fixed product and add it to cart"/> - <description value="Use Quick Search to find Bundle Fixed Product and Add to Cart"/> - <severity value="MAJOR"/> - <testCaseId value="MC-14790"/> - <group value="CatalogSearch"/> - <group value="mtf_migrated"/> - </annotations> - <before> - <createData entity="_defaultCategory" stepKey="createCategory"/> - <createData entity="_defaultProduct" stepKey="createProduct"> - <requiredEntity createDataKey="createCategory"/> - </createData> - - <!--Create fixed product--> - <!--Create 2 simple products--> - <createData entity="SimpleProduct2" stepKey="simpleProduct1"/> - <createData entity="SimpleProduct2" stepKey="simpleProduct2"/> - <!-- Create the bundle product based --> - <createData entity="ApiFixedBundleProduct" stepKey="createBundleProduct"/> - <createData entity="MultipleSelectOption" stepKey="createBundleOption1_1"> - <requiredEntity createDataKey="createBundleProduct"/> - <field key="required">false</field> - </createData> - <createData entity="CheckboxOption" stepKey="createBundleOption1_2"> - <requiredEntity createDataKey="createBundleProduct"/> - </createData> - <createData entity="ApiBundleLink" stepKey="linkOptionToProduct"> - <requiredEntity createDataKey="createBundleProduct"/> - <requiredEntity createDataKey="createBundleOption1_1"/> - <requiredEntity createDataKey="simpleProduct1"/> - </createData> - <createData entity="ApiBundleLink" stepKey="linkOptionToProduct2"> - <requiredEntity createDataKey="createBundleProduct"/> - <requiredEntity createDataKey="createBundleOption1_1"/> - <requiredEntity createDataKey="simpleProduct2"/> - </createData> - - <!--Finish bundle creation--> - <actionGroup ref="LoginAsAdmin" stepKey="loginAsAdmin"/> - <amOnPage url="{{AdminProductEditPage.url($$createBundleProduct.id$$)}}" stepKey="goToProductEditPage"/> - <actionGroup ref="SaveProductFormActionGroup" stepKey="saveProduct"/> - - <!-- Perform reindex and flush cache --> - <magentoCLI command="indexer:reindex" stepKey="reindex"/> - <magentoCLI command="cache:flush" stepKey="flushCache"/> - </before> - <after> - <deleteData stepKey="deleteBundleProduct" createDataKey="createBundleProduct"/> - <deleteData stepKey="deleteProduct" createDataKey="createProduct"/> - <deleteData createDataKey="simpleProduct1" stepKey="deleteSimpleProduct1"/> - <deleteData createDataKey="simpleProduct2" stepKey="deleteSimpleProduct2"/> - <deleteData stepKey="deleteCategory" createDataKey="createCategory"/> - <actionGroup ref="logout" stepKey="logoutFromAdmin"/> - </after> - <comment userInput="$simpleProduct1.name$" stepKey="asdf"/> - <amOnPage url="{{StorefrontHomePage.url}}" stepKey="goToFrontPage"/> - <actionGroup ref="StorefrontCheckQuickSearchStringActionGroup" stepKey="searchStorefront"> - <argument name="phrase" value="$createBundleProduct.name$"/> - </actionGroup> - <actionGroup ref="StorefrontOpenProductFromQuickSearchActionGroup" stepKey="openAndCheckProduct"> - <argument name="productName" value="$createBundleProduct.name$"/> - <argument name="productUrlKey" value="$createBundleProduct.custom_attributes[url_key]$"/> - </actionGroup> - <actionGroup ref="StorefrontAddBundleProductFromProductToCartWithMultiOptionActionGroup" stepKey="addProductToCart"> - <argument name="productName" value="$createBundleProduct.name$"/> - <argument name="optionName" value="$createBundleOption1_1.name$"/> - <argument name="value" value="$simpleProduct1.name$"/> - </actionGroup> - </test> - - <test name="QuickSearchConfigurableChildren"> - <annotations> - <stories value="Search Product on Storefront"/> - <title value="User should be able to use Quick Search to a configurable product's child products"/> - <description value="Use Quick Search to find a configurable product with enabled/disable children"/> - <severity value="MAJOR"/> - <testCaseId value="MC-14798"/> - <group value="CatalogSearch"/> - <group value="mtf_migrated"/> - <skip> - <issueId value="MC-15101"/> - </skip> - </annotations> - <before> - <!-- Create the category --> - <createData entity="ApiCategory" stepKey="createCategory"/> - - <!-- Create blank AttributeSet--> - <createData entity="CatalogAttributeSet" stepKey="attributeSet"/> - - <!-- Create an attribute with two options to be used in the first child product --> - <createData entity="hiddenDropdownAttributeWithOptions" stepKey="createConfigProductAttribute"/> - <createData entity="productAttributeOption1" stepKey="createConfigProductAttributeOption1"> - <requiredEntity createDataKey="createConfigProductAttribute"/> - </createData> - - <!-- Assign attribute to set --> - <actionGroup ref="LoginAsAdmin" stepKey="loginAsAdmin"/> - <actionGroup ref="goToAttributeGridPage" stepKey="goToPage"/> - <actionGroup ref="GoToAttributeSetByNameActionGroup" stepKey="goToSet"> - <argument name="name" value="$attributeSet.attribute_set_name$"/> - </actionGroup> - <actionGroup ref="AssignAttributeToGroupActionGroup" stepKey="assignToAttributeSetAndGroup"> - <argument name="group" value="Product Details"/> - <argument name="attribute" value="$createConfigProductAttribute.attribute_code$"/> - </actionGroup> - <actionGroup ref="SaveAttributeSetActionGroup" stepKey="savePage"/> - - <!-- Get the first option of the attribute we created --> - <getData entity="ProductAttributeOptionGetter" index="1" stepKey="getConfigAttributeOption1"> - <requiredEntity createDataKey="createConfigProductAttribute"/> - </getData> - - <!-- Create a simple product,give it the attributeSet and attribute with the first option --> - <createData entity="ApiSimpleOneHidden" stepKey="createConfigChildProduct1"> - <field key="attribute_set_id">$attributeSet.attribute_set_id$</field> - <requiredEntity createDataKey="createConfigProductAttribute"/> - <requiredEntity createDataKey="getConfigAttributeOption1"/> - </createData> - <updateData entity="ApiSimpleProductUpdateDescription" stepKey="updateSimpleProduct1" createDataKey="createConfigChildProduct1"/> - - <!-- Create the configurable product, give it the attributeSet and add it to the category --> - <createData entity="ApiConfigurableProduct" stepKey="createConfigProduct"> - <field key="attribute_set_id">$attributeSet.attribute_set_id$</field> - <requiredEntity createDataKey="createCategory"/> - </createData> - <!-- Create the configurable product --> - <createData entity="ConfigurableProductOneOption" stepKey="createConfigProductOption"> - <requiredEntity createDataKey="createConfigProduct"/> - <requiredEntity createDataKey="createConfigProductAttribute"/> - <requiredEntity createDataKey="getConfigAttributeOption1"/> - </createData> - <!-- Add the first simple product to the configurable product --> - <createData entity="ConfigurableProductAddChild" stepKey="createConfigProductAddChild1"> - <requiredEntity createDataKey="createConfigProduct"/> - <requiredEntity createDataKey="createConfigChildProduct1"/> - </createData> - - <!-- Perform reindex and flush cache --> - <magentoCLI command="indexer:reindex" stepKey="reindex"/> - <magentoCLI command="cache:flush" stepKey="flushCache"/> - </before> - <amOnPage url="{{StorefrontHomePage.url}}" stepKey="goToFrontPage"/> - <actionGroup ref="StorefrontCheckQuickSearchStringActionGroup" stepKey="searchStorefront"> - <argument name="phrase" value="$createConfigProduct.name$"/> - </actionGroup> - <actionGroup ref="StorefrontQuickSearchCheckProductNameInGridActionGroup" stepKey="seeProductInGrid"> - <argument name="productName" value="$createConfigProduct.name$"/> - <argument name="index" value="1"/> - </actionGroup> - - <!-- Disable Child Product --> - <actionGroup ref="GoToProductPageViaIDActionGroup" stepKey="goToChildProduct"> - <argument name="productId" value="$createConfigChildProduct1.id$"/> - </actionGroup> - <actionGroup ref="ToggleProductEnabledActionGroup" stepKey="disableProduct"/> - <actionGroup ref="SaveProductFormActionGroup" stepKey="saveProduct"/> - - <amOnPage url="{{StorefrontHomePage.url}}" stepKey="goToFrontPageAgain"/> - <actionGroup ref="StorefrontCheckQuickSearchStringActionGroup" stepKey="searchStorefrontAgain"> - <argument name="phrase" value="$createConfigProduct.name$"/> - </actionGroup> - <actionGroup ref="StorefrontQuickSearchCheckProductNameNotInGridActionGroup" stepKey="dontSeeProductAnymore"> - <argument name="productName" value="$createConfigProduct.name$"/> - </actionGroup> - </test> -</tests> diff --git a/app/code/Magento/CatalogSearch/Test/Mftf/Test/SearchEntityResultsTest/QuickSearchAndAddToCartBundleDynamicTest.xml b/app/code/Magento/CatalogSearch/Test/Mftf/Test/SearchEntityResultsTest/QuickSearchAndAddToCartBundleDynamicTest.xml new file mode 100644 index 0000000000000..49fce41fddf05 --- /dev/null +++ b/app/code/Magento/CatalogSearch/Test/Mftf/Test/SearchEntityResultsTest/QuickSearchAndAddToCartBundleDynamicTest.xml @@ -0,0 +1,68 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + /** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +--> +<tests xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/testSchema.xsd"> + <test name="QuickSearchAndAddToCartBundleDynamicTest"> + <annotations> + <stories value="Search Product on Storefront"/> + <title value="User should be able to use Quick Search to find a Bundle Dynamic product and add it to cart"/> + <description value="Use Quick Search to find Bundle Dynamic Product and Add to Cart"/> + <severity value="MAJOR"/> + <testCaseId value="MC-14789"/> + <group value="CatalogSearch"/> + <group value="mtf_migrated"/> + </annotations> + <before> + <createData entity="_defaultCategory" stepKey="createCategory"/> + <createData entity="_defaultProduct" stepKey="createProduct"> + <requiredEntity createDataKey="createCategory"/> + </createData> + + <!--Create dynamic product--> + <createData entity="ApiBundleProductPriceViewRange" stepKey="createBundleProduct"> + <requiredEntity createDataKey="createCategory"/> + </createData> + <createData entity="DropDownBundleOption" stepKey="bundleOption"> + <requiredEntity createDataKey="createBundleProduct"/> + </createData> + <createData entity="ApiBundleLink" stepKey="createBundleLink1"> + <requiredEntity createDataKey="createBundleProduct"/> + <requiredEntity createDataKey="bundleOption"/> + <requiredEntity createDataKey="createProduct"/> + <field key="qty">10</field> + </createData> + <!--Finish bundle creation--> + <actionGroup ref="AdminLoginActionGroup" stepKey="loginAsAdmin"/> + <actionGroup ref="AdminProductPageOpenByIdActionGroup" stepKey="goToProductEditPage"> + <argument name="productId" value="$$createBundleProduct.id$$"/> + </actionGroup> + <actionGroup ref="SaveProductFormActionGroup" stepKey="saveProduct"/> + + <!-- Perform reindex and flush cache --> + <magentoCLI command="indexer:reindex" stepKey="reindex"/> + <magentoCLI command="cache:flush" stepKey="flushCache"/> + </before> + <after> + <deleteData stepKey="deleteBundleProduct" createDataKey="createBundleProduct"/> + <deleteData stepKey="deleteProduct" createDataKey="createProduct"/> + <deleteData stepKey="deleteCategory" createDataKey="createCategory"/> + <actionGroup ref="AdminLogoutActionGroup" stepKey="logoutFromAdmin"/> + </after> + <amOnPage url="{{StorefrontHomePage.url}}" stepKey="goToFrontPage"/> + <actionGroup ref="StorefrontCheckQuickSearchStringActionGroup" stepKey="searchStorefront"> + <argument name="phrase" value="$createBundleProduct.name$"/> + </actionGroup> + <actionGroup ref="StorefrontOpenProductFromQuickSearchActionGroup" stepKey="openAndCheckProduct"> + <argument name="productName" value="$createBundleProduct.name$"/> + <argument name="productUrlKey" value="$createBundleProduct.custom_attributes[url_key]$"/> + </actionGroup> + <actionGroup ref="StorefrontAddBundleProductFromProductToCartActionGroup" stepKey="addProductToCart"> + <argument name="productName" value="$createBundleProduct.name$"/> + </actionGroup> + </test> +</tests> diff --git a/app/code/Magento/CatalogSearch/Test/Mftf/Test/SearchEntityResultsTest/QuickSearchAndAddToCartBundleFixedTest.xml b/app/code/Magento/CatalogSearch/Test/Mftf/Test/SearchEntityResultsTest/QuickSearchAndAddToCartBundleFixedTest.xml new file mode 100644 index 0000000000000..4b0a5c84ac360 --- /dev/null +++ b/app/code/Magento/CatalogSearch/Test/Mftf/Test/SearchEntityResultsTest/QuickSearchAndAddToCartBundleFixedTest.xml @@ -0,0 +1,84 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + /** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +--> +<tests xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/testSchema.xsd"> + <test name="QuickSearchAndAddToCartBundleFixedTest"> + <annotations> + <stories value="Search Product on Storefront"/> + <title value="User should be able to use Quick Search to find a Bundle Fixed product and add it to cart"/> + <description value="Use Quick Search to find Bundle Fixed Product and Add to Cart"/> + <severity value="MAJOR"/> + <testCaseId value="MC-14790"/> + <group value="CatalogSearch"/> + <group value="mtf_migrated"/> + </annotations> + <before> + <createData entity="_defaultCategory" stepKey="createCategory"/> + <createData entity="_defaultProduct" stepKey="createProduct"> + <requiredEntity createDataKey="createCategory"/> + </createData> + + <!--Create fixed product--> + <!--Create 2 simple products--> + <createData entity="SimpleProduct2" stepKey="simpleProduct1"/> + <createData entity="SimpleProduct2" stepKey="simpleProduct2"/> + <!-- Create the bundle product based --> + <createData entity="ApiFixedBundleProduct" stepKey="createBundleProduct"/> + <createData entity="MultipleSelectOption" stepKey="createBundleOption1_1"> + <requiredEntity createDataKey="createBundleProduct"/> + <field key="required">false</field> + </createData> + <createData entity="CheckboxOption" stepKey="createBundleOption1_2"> + <requiredEntity createDataKey="createBundleProduct"/> + </createData> + <createData entity="ApiBundleLink" stepKey="linkOptionToProduct"> + <requiredEntity createDataKey="createBundleProduct"/> + <requiredEntity createDataKey="createBundleOption1_1"/> + <requiredEntity createDataKey="simpleProduct1"/> + </createData> + <createData entity="ApiBundleLink" stepKey="linkOptionToProduct2"> + <requiredEntity createDataKey="createBundleProduct"/> + <requiredEntity createDataKey="createBundleOption1_1"/> + <requiredEntity createDataKey="simpleProduct2"/> + </createData> + + <!--Finish bundle creation--> + <actionGroup ref="AdminLoginActionGroup" stepKey="loginAsAdmin"/> + <actionGroup ref="AdminProductPageOpenByIdActionGroup" stepKey="goToProductEditPage"> + <argument name="productId" value="$$createBundleProduct.id$$"/> + </actionGroup> + <actionGroup ref="SaveProductFormActionGroup" stepKey="saveProduct"/> + + <!-- Perform reindex and flush cache --> + <magentoCLI command="indexer:reindex" stepKey="reindex"/> + <magentoCLI command="cache:flush" stepKey="flushCache"/> + </before> + <after> + <deleteData stepKey="deleteBundleProduct" createDataKey="createBundleProduct"/> + <deleteData stepKey="deleteProduct" createDataKey="createProduct"/> + <deleteData createDataKey="simpleProduct1" stepKey="deleteSimpleProduct1"/> + <deleteData createDataKey="simpleProduct2" stepKey="deleteSimpleProduct2"/> + <deleteData stepKey="deleteCategory" createDataKey="createCategory"/> + <actionGroup ref="AdminLogoutActionGroup" stepKey="logoutFromAdmin"/> + </after> + <comment userInput="$simpleProduct1.name$" stepKey="asdf"/> + <amOnPage url="{{StorefrontHomePage.url}}" stepKey="goToFrontPage"/> + <actionGroup ref="StorefrontCheckQuickSearchStringActionGroup" stepKey="searchStorefront"> + <argument name="phrase" value="$createBundleProduct.name$"/> + </actionGroup> + <actionGroup ref="StorefrontOpenProductFromQuickSearchActionGroup" stepKey="openAndCheckProduct"> + <argument name="productName" value="$createBundleProduct.name$"/> + <argument name="productUrlKey" value="$createBundleProduct.custom_attributes[url_key]$"/> + </actionGroup> + <actionGroup ref="StorefrontAddBundleProductFromProductToCartWithMultiOptionActionGroup" stepKey="addProductToCart"> + <argument name="productName" value="$createBundleProduct.name$"/> + <argument name="optionName" value="$createBundleOption1_1.name$"/> + <argument name="value" value="$simpleProduct1.name$"/> + </actionGroup> + </test> +</tests> diff --git a/app/code/Magento/CatalogSearch/Test/Mftf/Test/SearchEntityResultsTest/QuickSearchAndAddToCartConfigurableTest.xml b/app/code/Magento/CatalogSearch/Test/Mftf/Test/SearchEntityResultsTest/QuickSearchAndAddToCartConfigurableTest.xml new file mode 100644 index 0000000000000..35db90363b1ae --- /dev/null +++ b/app/code/Magento/CatalogSearch/Test/Mftf/Test/SearchEntityResultsTest/QuickSearchAndAddToCartConfigurableTest.xml @@ -0,0 +1,54 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + /** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +--> +<tests xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/testSchema.xsd"> + <test name="QuickSearchAndAddToCartConfigurableTest"> + <annotations> + <stories value="Search Product on Storefront"/> + <title value="User should be able to use Quick Search to find a configurable product and add it to cart"/> + <description value="Use Quick Search to find configurable Product and Add to Cart"/> + <severity value="MAJOR"/> + <testCaseId value="MC-14786"/> + <group value="CatalogSearch"/> + <group value="mtf_migrated"/> + </annotations> + <before> + <createData entity="_defaultCategory" stepKey="createCategory"/> + <actionGroup ref="AdminLoginActionGroup" stepKey="loginAsAdmin1"/> + <actionGroup ref="CreateConfigurableProductActionGroup" stepKey="createProduct"> + <argument name="product" value="_defaultProduct"/> + <argument name="category" value="$$createCategory$$"/> + </actionGroup> + + <!-- Perform reindex and flush cache --> + <magentoCLI command="indexer:reindex" stepKey="reindex"/> + <magentoCLI command="cache:flush" stepKey="flushCache"/> + </before> + <after> + <deleteData stepKey="deleteCategory" createDataKey="createCategory"/> + <actionGroup ref="DeleteProductBySkuActionGroup" stepKey="deleteProduct"> + <argument name="sku" value="{{_defaultProduct.sku}}"/> + </actionGroup> + <actionGroup ref="ClearFiltersAdminDataGridActionGroup" stepKey="clearProductsGridFilters"/> + <actionGroup ref="AdminLogoutActionGroup" stepKey="logoutFromAdmin"/> + </after> + <amOnPage url="{{StorefrontHomePage.url}}" stepKey="goToFrontPage"/> + <actionGroup ref="StorefrontCheckQuickSearchStringActionGroup" stepKey="searchStorefront"> + <argument name="phrase" value="{{_defaultProduct.name}}"/> + </actionGroup> + <actionGroup ref="StorefrontOpenProductFromQuickSearchActionGroup" stepKey="openAndCheckProduct"> + <argument name="productName" value="{{_defaultProduct.name}}"/> + <argument name="productUrlKey" value="{{_defaultProduct.urlKey}}"/> + </actionGroup> + <actionGroup ref="SelectSingleAttributeAndAddToCartActionGroup" stepKey="addProductToCart"> + <argument name="productName" value="{{_defaultProduct.name}}"/> + <argument name="attributeCode" value="{{colorProductAttribute.default_label}}"/> + <argument name="optionName" value="{{colorProductAttribute1.name}}"/> + </actionGroup> + </test> +</tests> diff --git a/app/code/Magento/CatalogSearch/Test/Mftf/Test/SearchEntityResultsTest/QuickSearchAndAddToCartDownloadableTest.xml b/app/code/Magento/CatalogSearch/Test/Mftf/Test/SearchEntityResultsTest/QuickSearchAndAddToCartDownloadableTest.xml new file mode 100644 index 0000000000000..79a2fc8646c04 --- /dev/null +++ b/app/code/Magento/CatalogSearch/Test/Mftf/Test/SearchEntityResultsTest/QuickSearchAndAddToCartDownloadableTest.xml @@ -0,0 +1,47 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + /** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +--> +<tests xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/testSchema.xsd"> + <test name="QuickSearchAndAddToCartDownloadableTest"> + <annotations> + <stories value="Search Product on Storefront"/> + <title value="User should be able to use Quick Search to find a downloadable product and add it to cart"/> + <description value="Use Quick Search to find downloadable Product and Add to Cart"/> + <severity value="MAJOR"/> + <testCaseId value="MC-14787"/> + <group value="CatalogSearch"/> + <group value="mtf_migrated"/> + </annotations> + <before> + <magentoCLI stepKey="addDownloadableDomain" command="downloadable:domains:add example.com static.magento.com"/> + <createData entity="_defaultCategory" stepKey="createCategory"/> + <createData entity="DownloadableProductWithOneLink" stepKey="createProduct"> + <requiredEntity createDataKey="createCategory"/> + </createData> + <createData entity="downloadableLink1" stepKey="addDownloadableLink1"> + <requiredEntity createDataKey="createProduct"/> + </createData> + + <!-- Perform reindex and flush cache --> + <magentoCLI command="indexer:reindex" stepKey="reindex"/> + <magentoCLI command="cache:flush" stepKey="flushCache"/> + </before> + <after> + <magentoCLI stepKey="removeDownloadableDomain" command="downloadable:domains:remove example.com static.magento.com"/> + <deleteData stepKey="deleteProduct" createDataKey="createProduct"/> + <deleteData stepKey="deleteCategory" createDataKey="createCategory"/> + </after> + <amOnPage url="{{StorefrontHomePage.url}}" stepKey="goToFrontPage"/> + <actionGroup ref="StorefrontCheckQuickSearchStringActionGroup" stepKey="searchStorefront"> + <argument name="phrase" value="$createProduct.name$"/> + </actionGroup> + <actionGroup ref="StorefrontAddToCartFromQuickSearchActionGroup" stepKey="addProductToCart"> + <argument name="productName" value="$createProduct.name$"/> + </actionGroup> + </test> +</tests> diff --git a/app/code/Magento/CatalogSearch/Test/Mftf/Test/SearchEntityResultsTest/QuickSearchAndAddToCartGroupedTest.xml b/app/code/Magento/CatalogSearch/Test/Mftf/Test/SearchEntityResultsTest/QuickSearchAndAddToCartGroupedTest.xml new file mode 100644 index 0000000000000..cf30e4d06e8e7 --- /dev/null +++ b/app/code/Magento/CatalogSearch/Test/Mftf/Test/SearchEntityResultsTest/QuickSearchAndAddToCartGroupedTest.xml @@ -0,0 +1,47 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + /** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +--> +<tests xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/testSchema.xsd"> + <test name="QuickSearchAndAddToCartGroupedTest"> + <annotations> + <stories value="Search Product on Storefront"/> + <title value="User should be able to use Quick Search to find a grouped product and add it to cart"/> + <description value="Use Quick Search to find grouped Product and Add to Cart"/> + <severity value="MAJOR"/> + <testCaseId value="MC-14788"/> + <group value="CatalogSearch"/> + <group value="mtf_migrated"/> + </annotations> + <before> + <actionGroup ref="AdminLoginActionGroup" stepKey="loginAsAdmin"/> + <actionGroup ref="DeleteAllProductsUsingProductGridActionGroup" stepKey="deleteAllProducts"/> + <createData entity="ApiProductWithDescription" stepKey="simple1"/> + <createData entity="ApiGroupedProduct" stepKey="createProduct"/> + <createData entity="OneSimpleProductLink" stepKey="addProductOne"> + <requiredEntity createDataKey="createProduct"/> + <requiredEntity createDataKey="simple1"/> + </createData> + + <!-- Perform reindex and flush cache --> + <magentoCLI command="indexer:reindex" stepKey="reindex"/> + <magentoCLI command="cache:flush" stepKey="flushCache"/> + </before> + <after> + <deleteData stepKey="deleteGroupedProduct" createDataKey="createProduct"/> + <deleteData stepKey="deleteSimpleProduct" createDataKey="simple1"/> + <actionGroup ref="AdminLogoutActionGroup" stepKey="logoutFromAdmin"/> + </after> + <amOnPage url="{{StorefrontHomePage.url}}" stepKey="goToFrontPage"/> + <actionGroup ref="StorefrontCheckQuickSearchStringActionGroup" stepKey="searchStorefront"> + <argument name="phrase" value=""$createProduct.name$""/> + </actionGroup> + <actionGroup ref="StorefrontAddToCartFromQuickSearchActionGroup" stepKey="addProductToCart"> + <argument name="productName" value="$createProduct.name$"/> + </actionGroup> + </test> +</tests> diff --git a/app/code/Magento/CatalogSearch/Test/Mftf/Test/SearchEntityResultsTest/QuickSearchAndAddToCartTest.xml b/app/code/Magento/CatalogSearch/Test/Mftf/Test/SearchEntityResultsTest/QuickSearchAndAddToCartTest.xml new file mode 100644 index 0000000000000..ba6fa813367c3 --- /dev/null +++ b/app/code/Magento/CatalogSearch/Test/Mftf/Test/SearchEntityResultsTest/QuickSearchAndAddToCartTest.xml @@ -0,0 +1,42 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + /** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +--> +<tests xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/testSchema.xsd"> + <test name="QuickSearchAndAddToCartTest"> + <annotations> + <stories value="Search Product on Storefront"/> + <title value="User should be able to use Quick Search to find a simple product and add it to cart"/> + <description value="Use Quick Search to find simple Product and Add to Cart"/> + <severity value="MAJOR"/> + <testCaseId value="MC-14784"/> + <group value="CatalogSearch"/> + <group value="mtf_migrated"/> + </annotations> + <before> + <createData entity="_defaultCategory" stepKey="createCategory"/> + <createData entity="_defaultProduct" stepKey="createSimpleProduct"> + <requiredEntity createDataKey="createCategory"/> + </createData> + + <!-- Perform reindex and flush cache --> + <magentoCLI command="indexer:reindex" stepKey="reindex"/> + <magentoCLI command="cache:flush" stepKey="flushCache"/> + </before> + <after> + <deleteData stepKey="deleteProduct" createDataKey="createSimpleProduct"/> + <deleteData stepKey="deleteCategory" createDataKey="createCategory"/> + </after> + <amOnPage url="{{StorefrontHomePage.url}}" stepKey="goToFrontPage"/> + <actionGroup ref="StorefrontCheckQuickSearchStringActionGroup" stepKey="searchStorefront"> + <argument name="phrase" value="$createSimpleProduct.name$"/> + </actionGroup> + <actionGroup ref="StorefrontAddToCartFromQuickSearchActionGroup" stepKey="addProductToCart"> + <argument name="productName" value="$createSimpleProduct.name$"/> + </actionGroup> + </test> +</tests> diff --git a/app/code/Magento/CatalogSearch/Test/Mftf/Test/SearchEntityResultsTest/QuickSearchAndAddToCartVirtualTest.xml b/app/code/Magento/CatalogSearch/Test/Mftf/Test/SearchEntityResultsTest/QuickSearchAndAddToCartVirtualTest.xml new file mode 100644 index 0000000000000..b71388f5f409b --- /dev/null +++ b/app/code/Magento/CatalogSearch/Test/Mftf/Test/SearchEntityResultsTest/QuickSearchAndAddToCartVirtualTest.xml @@ -0,0 +1,42 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + /** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +--> +<tests xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/testSchema.xsd"> + <test name="QuickSearchAndAddToCartVirtualTest"> + <annotations> + <stories value="Search Product on Storefront"/> + <title value="User should be able to use Quick Search to find a virtual product and add it to cart"/> + <description value="Use Quick Search to find virtual Product and Add to Cart"/> + <severity value="MAJOR"/> + <testCaseId value="MC-14785"/> + <group value="CatalogSearch"/> + <group value="mtf_migrated"/> + </annotations> + <before> + <createData entity="_defaultCategory" stepKey="createCategory"/> + <createData entity="VirtualProduct" stepKey="createVirtualProduct"> + <requiredEntity createDataKey="createCategory"/> + </createData> + + <!-- Perform reindex and flush cache --> + <magentoCLI command="indexer:reindex" stepKey="reindex"/> + <magentoCLI command="cache:flush" stepKey="flushCache"/> + </before> + <after> + <deleteData stepKey="deleteProduct" createDataKey="createVirtualProduct"/> + <deleteData stepKey="deleteCategory" createDataKey="createCategory"/> + </after> + <amOnPage url="{{StorefrontHomePage.url}}" stepKey="goToFrontPage"/> + <actionGroup ref="StorefrontCheckQuickSearchStringActionGroup" stepKey="searchStorefront"> + <argument name="phrase" value="$createVirtualProduct.name$"/> + </actionGroup> + <actionGroup ref="StorefrontAddToCartFromQuickSearchActionGroup" stepKey="addProductToCart"> + <argument name="productName" value="$createVirtualProduct.name$"/> + </actionGroup> + </test> +</tests> diff --git a/app/code/Magento/CatalogSearch/Test/Mftf/Test/SearchEntityResultsTest/QuickSearchEmptyResultsTest.xml b/app/code/Magento/CatalogSearch/Test/Mftf/Test/SearchEntityResultsTest/QuickSearchEmptyResultsTest.xml new file mode 100644 index 0000000000000..566b4d204751d --- /dev/null +++ b/app/code/Magento/CatalogSearch/Test/Mftf/Test/SearchEntityResultsTest/QuickSearchEmptyResultsTest.xml @@ -0,0 +1,42 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + /** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +--> +<tests xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/testSchema.xsd"> + <test name="QuickSearchEmptyResultsTest"> + <annotations> + <features value="CatalogSearch"/> + <stories value="Search Product on Storefront"/> + <title value="User should not get search results on query that doesn't return anything"/> + <description value="Use invalid query to return no products"/> + <severity value="MAJOR"/> + <testCaseId value="MC-14793"/> + <group value="CatalogSearch"/> + <group value="mtf_migrated"/> + </annotations> + <before> + <createData entity="_defaultCategory" stepKey="createCategory"/> + <createData entity="_defaultProduct" stepKey="createSimpleProduct"> + <requiredEntity createDataKey="createCategory"/> + </createData> + + <!-- Perform reindex and flush cache --> + <magentoCLI command="indexer:reindex" stepKey="reindex"/> + <magentoCLI command="cache:flush" stepKey="flushCache"/> + </before> + <after> + <deleteData createDataKey="createSimpleProduct" stepKey="deleteProduct"/> + <deleteData createDataKey="createCategory" stepKey="deleteCategory"/> + </after> + + <amOnPage url="{{StorefrontHomePage.url}}" stepKey="goToFrontPage"/> + <actionGroup ref="StorefrontCheckQuickSearchStringActionGroup" stepKey="searchStorefront"> + <argument name="phrase" value="ThisShouldn'tReturnAnything"/> + </actionGroup> + <actionGroup ref="StorefrontCheckSearchIsEmptyActionGroup" stepKey="checkEmpty"/> + </test> +</tests> diff --git a/app/code/Magento/CatalogSearch/Test/Mftf/Test/SearchEntityResultsTest/QuickSearchProductBy128CharQueryTest.xml b/app/code/Magento/CatalogSearch/Test/Mftf/Test/SearchEntityResultsTest/QuickSearchProductBy128CharQueryTest.xml new file mode 100644 index 0000000000000..b2b6bbb473091 --- /dev/null +++ b/app/code/Magento/CatalogSearch/Test/Mftf/Test/SearchEntityResultsTest/QuickSearchProductBy128CharQueryTest.xml @@ -0,0 +1,30 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + /** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +--> +<tests xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/testSchema.xsd"> + <test name="QuickSearchProductBy128CharQueryTest" extends="QuickSearchProductBySkuTest"> + <annotations> + <stories value="Search Product on Storefront"/> + <title value="User should be able to use Quick Search product with long names, using first 128 letters"/> + <description value="Use Quick Search to find a product with name of 130 length with query of only 128"/> + <severity value="MAJOR"/> + <testCaseId value="MC-14795"/> + <group value="CatalogSearch"/> + <group value="mtf_migrated"/> + </annotations> + <before> + <createData entity="productWith130CharName" stepKey="createSimpleProduct"> + <requiredEntity createDataKey="createCategory"/> + </createData> + </before> + <executeJS function="var s = '$createSimpleProduct.name$'; var ret=s.substring(0,128); return ret;" stepKey="get128Letters" before="searchStorefront"/> + <actionGroup ref="StorefrontCheckQuickSearchStringActionGroup" stepKey="searchStorefront"> + <argument name="phrase" value="{$get128Letters}"/> + </actionGroup> + </test> +</tests> diff --git a/app/code/Magento/CatalogSearch/Test/Mftf/Test/SearchEntityResultsTest/QuickSearchProductByNameTest.xml b/app/code/Magento/CatalogSearch/Test/Mftf/Test/SearchEntityResultsTest/QuickSearchProductByNameTest.xml new file mode 100644 index 0000000000000..62f4e3da1059c --- /dev/null +++ b/app/code/Magento/CatalogSearch/Test/Mftf/Test/SearchEntityResultsTest/QuickSearchProductByNameTest.xml @@ -0,0 +1,25 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + /** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +--> +<tests xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/testSchema.xsd"> + <test name="QuickSearchProductByNameTest" extends="QuickSearchProductBySkuTest"> + <annotations> + <stories value="Search Product on Storefront"/> + <title value="User should be able to use Quick Search to find products via Name"/> + <description value="Use Quick Search to find a product"/> + <severity value="MAJOR"/> + <testCaseId value="MC-14791"/> + <group value="CatalogSearch"/> + <group value="mtf_migrated"/> + </annotations> + <!-- Overwrite search to use name --> + <actionGroup ref="StorefrontCheckQuickSearchStringActionGroup" stepKey="searchStorefront"> + <argument name="phrase" value="$createSimpleProduct.name$"/> + </actionGroup> + </test> +</tests> diff --git a/app/code/Magento/CatalogSearch/Test/Mftf/Test/SearchEntityResultsTest/QuickSearchProductByNameWithSpecialCharsTest.xml b/app/code/Magento/CatalogSearch/Test/Mftf/Test/SearchEntityResultsTest/QuickSearchProductByNameWithSpecialCharsTest.xml new file mode 100644 index 0000000000000..7f21972ce801b --- /dev/null +++ b/app/code/Magento/CatalogSearch/Test/Mftf/Test/SearchEntityResultsTest/QuickSearchProductByNameWithSpecialCharsTest.xml @@ -0,0 +1,30 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + /** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +--> +<tests xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/testSchema.xsd"> + <test name="QuickSearchProductByNameWithSpecialCharsTest" extends="QuickSearchProductBySkuTest"> + <annotations> + <stories value="Search Product on Storefront"/> + <title value="Quick Search can find products with names that contain special characters"/> + <description value="Use Quick Search to find a product by name"/> + <severity value="MAJOR"/> + <testCaseId value="MC-14792"/> + <group value="CatalogSearch"/> + <group value="mtf_migrated"/> + </annotations> + <before> + <createData entity="productWithSpecialCharacters" stepKey="createSimpleProduct"> + <requiredEntity createDataKey="createCategory"/> + </createData> + </before> + <!-- Overwrite search to use name --> + <actionGroup ref="StorefrontCheckQuickSearchStringActionGroup" stepKey="searchStorefront"> + <argument name="phrase" value="$createSimpleProduct.name$"/> + </actionGroup> + </test> +</tests> diff --git a/app/code/Magento/CatalogSearch/Test/Mftf/Test/SearchEntityResultsTest/QuickSearchProductByNameWithThreeLettersTest.xml b/app/code/Magento/CatalogSearch/Test/Mftf/Test/SearchEntityResultsTest/QuickSearchProductByNameWithThreeLettersTest.xml new file mode 100644 index 0000000000000..e5e7719dd63b3 --- /dev/null +++ b/app/code/Magento/CatalogSearch/Test/Mftf/Test/SearchEntityResultsTest/QuickSearchProductByNameWithThreeLettersTest.xml @@ -0,0 +1,26 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + /** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +--> +<tests xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/testSchema.xsd"> + <test name="QuickSearchProductByNameWithThreeLettersTest" extends="QuickSearchProductBySkuTest"> + <annotations> + <stories value="Search Product on Storefront"/> + <title value="User should be able to use Quick Search to find products by their first three letters"/> + <description value="Use Quick Search to find a product using only first three letters"/> + <severity value="MAJOR"/> + <testCaseId value="MC-15034"/> + <group value="CatalogSearch"/> + <group value="SearchEngineMysql"/> + <group value="mtf_migrated"/> + </annotations> + <executeJS function="var s = '$createSimpleProduct.name$'; var ret=s.substring(0,3); return ret;" stepKey="getFirstThreeLetters" before="searchStorefront"/> + <actionGroup ref="StorefrontCheckQuickSearchStringActionGroup" stepKey="searchStorefront"> + <argument name="phrase" value="{$getFirstThreeLetters}"/> + </actionGroup> + </test> +</tests> diff --git a/app/code/Magento/CatalogSearch/Test/Mftf/Test/SearchEntityResultsTest/QuickSearchProductBySkuTest.xml b/app/code/Magento/CatalogSearch/Test/Mftf/Test/SearchEntityResultsTest/QuickSearchProductBySkuTest.xml new file mode 100644 index 0000000000000..814e27182799f --- /dev/null +++ b/app/code/Magento/CatalogSearch/Test/Mftf/Test/SearchEntityResultsTest/QuickSearchProductBySkuTest.xml @@ -0,0 +1,43 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + /** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +--> +<tests xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/testSchema.xsd"> + <test name="QuickSearchProductBySkuTest"> + <annotations> + <stories value="Search Product on Storefront"/> + <title value="User should be able to use Quick Search to find products"/> + <description value="Use Quick Search to find a product"/> + <severity value="MAJOR"/> + <testCaseId value="MC-14783"/> + <group value="CatalogSearch"/> + <group value="mtf_migrated"/> + </annotations> + <before> + <createData entity="_defaultCategory" stepKey="createCategory"/> + <createData entity="_defaultProduct" stepKey="createSimpleProduct"> + <requiredEntity createDataKey="createCategory"/> + </createData> + + <!-- Perform reindex and flush cache --> + <magentoCLI command="indexer:reindex" stepKey="reindex"/> + <magentoCLI command="cache:flush" stepKey="flushCache"/> + </before> + <after> + <deleteData stepKey="deleteProduct" createDataKey="createSimpleProduct"/> + <deleteData stepKey="deleteCategory" createDataKey="createCategory"/> + </after> + <amOnPage url="{{StorefrontHomePage.url}}" stepKey="goToFrontPage"/> + <actionGroup ref="StorefrontCheckQuickSearchStringActionGroup" stepKey="searchStorefront"> + <argument name="phrase" value="$createSimpleProduct.sku$"/> + </actionGroup> + <actionGroup ref="StorefrontOpenProductFromQuickSearchActionGroup" stepKey="openAndCheckProduct"> + <argument name="productName" value="$createSimpleProduct.name$"/> + <argument name="productUrlKey" value="$createSimpleProduct.custom_attributes[url_key]$"/> + </actionGroup> + </test> +</tests> diff --git a/app/code/Magento/CatalogSearch/Test/Mftf/Test/SearchEntityResultsTest/QuickSearchTwoProductsWithDifferentWeightTest.xml b/app/code/Magento/CatalogSearch/Test/Mftf/Test/SearchEntityResultsTest/QuickSearchTwoProductsWithDifferentWeightTest.xml new file mode 100644 index 0000000000000..ee126ce2ad874 --- /dev/null +++ b/app/code/Magento/CatalogSearch/Test/Mftf/Test/SearchEntityResultsTest/QuickSearchTwoProductsWithDifferentWeightTest.xml @@ -0,0 +1,39 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + /** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +--> +<tests xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/testSchema.xsd"> + <test name="QuickSearchTwoProductsWithDifferentWeightTest" extends="QuickSearchTwoProductsWithSameWeightTest"> + <annotations> + <stories value="Search Product on Storefront"/> + <title value="Quick Search should sort products with the different weight appropriately"/> + <description value="Use Quick Search to find a two products with the different weight"/> + <severity value="MAJOR"/> + <testCaseId value="MC-14797"/> + <group value="CatalogSearch"/> + <group value="SearchEngineMysql"/> + <group value="mtf_migrated"/> + </annotations> + <before> + <actionGroup ref="AdminCreateAttributeWithSearchWeightActionGroup" stepKey="createProduct1Attribute"> + <argument name="attributeType" value="Text Field"/> + <argument name="attributeName" value="$product1.name$"/> + <argument name="attributeSetName" value="$product1.name$"/> + <argument name="weight" value="5"/> + <argument name="defaultValue" value="{{_defaultProduct.name}}"/> + </actionGroup> + </before> + <actionGroup ref="StorefrontQuickSearchCheckProductNameInGridActionGroup" stepKey="assertProduct1Position"> + <argument name="productName" value="$product1.name$"/> + <argument name="index" value="1"/> + </actionGroup> + <actionGroup ref="StorefrontQuickSearchCheckProductNameInGridActionGroup" stepKey="assertProduct2Position"> + <argument name="productName" value="$product2.name$"/> + <argument name="index" value="2"/> + </actionGroup> + </test> +</tests> diff --git a/app/code/Magento/CatalogSearch/Test/Mftf/Test/SearchEntityResultsTest/QuickSearchTwoProductsWithSameWeightTest.xml b/app/code/Magento/CatalogSearch/Test/Mftf/Test/SearchEntityResultsTest/QuickSearchTwoProductsWithSameWeightTest.xml new file mode 100644 index 0000000000000..00a7ffa3fc142 --- /dev/null +++ b/app/code/Magento/CatalogSearch/Test/Mftf/Test/SearchEntityResultsTest/QuickSearchTwoProductsWithSameWeightTest.xml @@ -0,0 +1,93 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + /** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +--> +<tests xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/testSchema.xsd"> + <test name="QuickSearchTwoProductsWithSameWeightTest"> + <annotations> + <stories value="Search Product on Storefront"/> + <title value="Quick Search should sort products with the same weight appropriately"/> + <description value="Use Quick Search to find a two products with the same weight"/> + <severity value="MAJOR"/> + <testCaseId value="MC-14796"/> + <group value="CatalogSearch"/> + <group value="SearchEngineMysql"/> + <group value="mtf_migrated"/> + </annotations> + <before> + <createData entity="_defaultCategory" stepKey="createCategory"/> + <createData entity="productAlphabeticalA" stepKey="product1"> + <requiredEntity createDataKey="createCategory"/> + </createData> + <createData entity="productAlphabeticalB" stepKey="product2"> + <requiredEntity createDataKey="createCategory"/> + </createData> + <actionGroup ref="AdminLoginActionGroup" stepKey="loginAsAdmin1"/> + + + <!-- Create and Assign Attribute to product1--> + <actionGroup ref="AdminProductPageOpenByIdActionGroup" stepKey="goToProduct1"> + <argument name="productId" value="$product1.id$"/> + </actionGroup> + <actionGroup ref="AdminCreateAttributeWithSearchWeightActionGroup" stepKey="createProduct1Attribute"> + <argument name="attributeType" value="Text Field"/> + <argument name="attributeName" value="$product1.name$"/> + <argument name="attributeSetName" value="$product1.name$"/> + <argument name="weight" value="1"/> + <argument name="defaultValue" value="{{_defaultProduct.name}}"/> + </actionGroup> + <actionGroup ref="AdminProductPageSelectAttributeSetActionGroup" stepKey="selectAttributeSet1"> + <argument name="attributeSetName" value="$product1.name$"/> + </actionGroup> + <!--fill in default--> + <actionGroup ref="SaveProductFormActionGroup" stepKey="saveProduct1a"/> + <actionGroup ref="AdminProductPageFillTextAttributeValueByNameActionGroup" stepKey="fillDefault1"> + <argument name="attributeName" value="$product1.name$"/> + <argument name="value" value="{{_defaultProduct.name}}"/> + </actionGroup> + <actionGroup ref="SaveProductFormActionGroup" stepKey="saveProduct1b"/> + <!-- Create and Assign Attribute to product2--> + <actionGroup ref="AdminProductPageOpenByIdActionGroup" stepKey="goToProduct2"> + <argument name="productId" value="$product2.id$"/> + </actionGroup> + <actionGroup ref="AdminCreateAttributeWithSearchWeightActionGroup" stepKey="createProduct2Attribute"> + <argument name="attributeType" value="Text Field"/> + <argument name="attributeName" value="$product2.name$"/> + <argument name="attributeSetName" value="$product2.name$"/> + <argument name="weight" value="1"/> + <argument name="defaultValue" value="{{_defaultProduct.name}}"/> + </actionGroup> + <actionGroup ref="AdminProductPageSelectAttributeSetActionGroup" stepKey="selectAttributeSet2"> + <argument name="attributeSetName" value="$product2.name$"/> + </actionGroup> + <actionGroup ref="SaveProductFormActionGroup" stepKey="saveProduct2a"/> + <!--fill in default--> + <actionGroup ref="AdminProductPageFillTextAttributeValueByNameActionGroup" stepKey="fillDefault2"> + <argument name="attributeName" value="$product2.name$"/> + <argument name="value" value="{{_defaultProduct.name}}"/> + </actionGroup> + <actionGroup ref="SaveProductFormActionGroup" stepKey="saveProduct2b"/> + </before> + <after> + <deleteData stepKey="deleteProduct1" createDataKey="product1"/> + <deleteData stepKey="deleteProduct2" createDataKey="product2"/> + <deleteData stepKey="deleteCategory" createDataKey="createCategory"/> + </after> + <amOnPage url="{{StorefrontHomePage.url}}" stepKey="goToFrontPage"/> + <actionGroup ref="StorefrontCheckQuickSearchStringActionGroup" stepKey="searchStorefront"> + <argument name="phrase" value="{{_defaultProduct.name}}"/> + </actionGroup> + <actionGroup ref="StorefrontQuickSearchCheckProductNameInGridActionGroup" stepKey="assertProduct1Position"> + <argument name="productName" value="$product1.name$"/> + <argument name="index" value="2"/> + </actionGroup> + <actionGroup ref="StorefrontQuickSearchCheckProductNameInGridActionGroup" stepKey="assertProduct2Position"> + <argument name="productName" value="$product2.name$"/> + <argument name="index" value="1"/> + </actionGroup> + </test> +</tests> diff --git a/app/code/Magento/CatalogSearch/Test/Mftf/Test/SearchEntityResultsTest/QuickSearchWithTwoCharsEmptyResultsTest.xml b/app/code/Magento/CatalogSearch/Test/Mftf/Test/SearchEntityResultsTest/QuickSearchWithTwoCharsEmptyResultsTest.xml new file mode 100644 index 0000000000000..5f549cc1af5b6 --- /dev/null +++ b/app/code/Magento/CatalogSearch/Test/Mftf/Test/SearchEntityResultsTest/QuickSearchWithTwoCharsEmptyResultsTest.xml @@ -0,0 +1,43 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + /** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +--> +<tests xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/testSchema.xsd"> + <test name="QuickSearchWithTwoCharsEmptyResultsTest" extends="QuickSearchEmptyResultsTest"> + <annotations> + <features value="CatalogSearch"/> + <stories value="Search Product on Storefront"/> + <title value="User should not get search results on query that only contains two characters"/> + <description value="Use of 2 character query to return no products"/> + <severity value="MAJOR"/> + <testCaseId value="MC-14794"/> + <group value="CatalogSearch"/> + <group value="mtf_migrated"/> + </annotations> + + <before> + <magentoCLI command="config:set {{MinimalQueryLengthFourConfigData.path}} {{MinimalQueryLengthFourConfigData.value}}" after="createSimpleProduct" stepKey="setMinimalQueryLengthToFour"/> + </before> + + <after> + <magentoCLI command="config:set {{MinimalQueryLengthDefaultConfigData.path}} {{MinimalQueryLengthDefaultConfigData.value}}" after="deleteCategory" stepKey="setMinimalQueryLengthToFour"/> + </after> + + <executeJS function="var s = '$createSimpleProduct.name$'; var ret=s.substring(0,{{MinimalQueryLengthFourConfigData.value}} - 1); return ret;" before="searchStorefront" stepKey="getFirstLessThenConfigLetters"/> + + <actionGroup ref="StorefrontCheckQuickSearchStringActionGroup" after="checkEmpty" stepKey="searchStorefrontConfigLetters"> + <argument name="phrase" value="$createSimpleProduct.name$"/> + </actionGroup> + <actionGroup ref="StorefrontQuickSearchTooShortStringActionGroup" after="searchStorefrontConfigLetters" stepKey="checkCannotSearchWithTooShortString"> + <argument name="phrase" value="$getFirstLessThenConfigLetters"/> + <argument name="minQueryLength" value="{{MinimalQueryLengthFourConfigData.value}}"/> + </actionGroup> + <actionGroup ref="StorefrontQuickSearchRelatedSearchTermsAppearsActionGroup" after="checkCannotSearchWithTooShortString" stepKey="checkRelatedSearchTerm"> + <argument name="term" value="$createSimpleProduct.name$"/> + </actionGroup> + </test> +</tests> diff --git a/app/code/Magento/CatalogSearch/Test/Mftf/Test/StorefrontAdvancedSearchEntitySimpleProductTest.xml b/app/code/Magento/CatalogSearch/Test/Mftf/Test/StorefrontAdvancedSearchEntitySimpleProductTest.xml index 61f274cd13061..968435747bdbb 100644 --- a/app/code/Magento/CatalogSearch/Test/Mftf/Test/StorefrontAdvancedSearchEntitySimpleProductTest.xml +++ b/app/code/Magento/CatalogSearch/Test/Mftf/Test/StorefrontAdvancedSearchEntitySimpleProductTest.xml @@ -19,7 +19,7 @@ <group value="mtf_migrated"/> </annotations> <before> - <actionGroup ref="LoginAsAdmin" stepKey="loginAsAdmin"/> + <actionGroup ref="AdminLoginActionGroup" stepKey="loginAsAdmin"/> <!-- Delete all products left by prev tests because it sensitive for search--> <actionGroup ref="DeleteAllProductsUsingProductGridActionGroup" stepKey="deleteAllProducts"/> <!-- Create Data --> @@ -28,7 +28,7 @@ <after> <!-- Delete data --> <deleteData createDataKey="createProduct" stepKey="deleteProduct"/> - <actionGroup ref="logout" stepKey="logoutAdmin"/> + <actionGroup ref="AdminLogoutActionGroup" stepKey="logoutAdmin"/> </after> <!-- Perform reindex and flush cache --> diff --git a/app/code/Magento/CatalogSearch/Test/Mftf/Test/StorefrontQuickSearchConfigurableChildrenTest.xml b/app/code/Magento/CatalogSearch/Test/Mftf/Test/StorefrontQuickSearchConfigurableChildrenTest.xml new file mode 100644 index 0000000000000..6f510fa315d7d --- /dev/null +++ b/app/code/Magento/CatalogSearch/Test/Mftf/Test/StorefrontQuickSearchConfigurableChildrenTest.xml @@ -0,0 +1,109 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + /** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +--> + +<tests xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/testSchema.xsd"> + <test name="StorefrontQuickSearchConfigurableChildrenTest"> + <annotations> + <stories value="Search Product on Storefront"/> + <title value="User should be able to use Quick Search to a configurable product's child products"/> + <description value="Use Quick Search to find a configurable product with enabled/disable children"/> + <severity value="MAJOR"/> + <testCaseId value="MC-28374"/> + <useCaseId value="MAGETWO-69181"/> + <group value="catalogSearch"/> + <group value="mtf_migrated"/> + </annotations> + <before> + <!-- Create the category --> + <createData entity="ApiCategory" stepKey="createCategory"/> + <!-- Create blank AttributeSet--> + <createData entity="CatalogAttributeSet" stepKey="createAttributeSet"/> + <!-- Create an attribute with two options to be used in the first child product --> + <createData entity="hiddenDropdownAttributeWithOptions" stepKey="createProductAttribute"/> + <createData entity="productAttributeOption1" stepKey="createProductAttributeOption"> + <requiredEntity createDataKey="createProductAttribute"/> + </createData> + + <!-- Assign attribute to set --> + <actionGroup ref="AdminLoginActionGroup" stepKey="loginAsAdmin"/> + <actionGroup ref="GoToAttributeGridPageActionGroup" stepKey="goToAttributeSetPage"/> + <actionGroup ref="GoToAttributeSetByNameActionGroup" stepKey="openAttributeSetByName"> + <argument name="name" value="$createAttributeSet.attribute_set_name$"/> + </actionGroup> + <actionGroup ref="AssignAttributeToGroupActionGroup" stepKey="assignAttributeToGroup"> + <argument name="group" value="Product Details"/> + <argument name="attribute" value="$createProductAttribute.attribute_code$"/> + </actionGroup> + <actionGroup ref="SaveAttributeSetActionGroup" stepKey="savePage"/> + + <!-- Get the first option of the attribute we created --> + <getData entity="ProductAttributeOptionGetter" index="1" stepKey="getAttributeOption"> + <requiredEntity createDataKey="createProductAttribute"/> + </getData> + + <!-- Create a simple product --> + <createData entity="ApiSimpleOneHidden" storeCode="all" stepKey="createSimpleProduct"> + <field key="attribute_set_id">$createAttributeSet.attribute_set_id$</field> + <requiredEntity createDataKey="createProductAttribute"/> + <requiredEntity createDataKey="getAttributeOption"/> + </createData> + <updateData entity="ApiSimpleProductUpdateDescription" createDataKey="createSimpleProduct" stepKey="updateSimpleProduct"/> + + <!-- Create the configurable product --> + <createData entity="ApiConfigurableProduct" stepKey="createConfigurableProduct"> + <field key="attribute_set_id">$createAttributeSet.attribute_set_id$</field> + <requiredEntity createDataKey="createCategory"/> + </createData> + <!-- Create the configurable product option --> + <createData entity="ConfigurableProductOneOption" stepKey="createConfigProductOption"> + <requiredEntity createDataKey="createConfigurableProduct"/> + <requiredEntity createDataKey="createProductAttribute"/> + <requiredEntity createDataKey="getAttributeOption"/> + </createData> + <!-- Add the first simple product to the configurable product --> + <createData entity="ConfigurableProductAddChild" stepKey="createConfigProductAddChild"> + <requiredEntity createDataKey="createConfigurableProduct"/> + <requiredEntity createDataKey="createSimpleProduct"/> + </createData> + + <!-- Perform reindex --> + <magentoCLI command="indexer:reindex" stepKey="reindex"/> + </before> + <after> + <deleteData createDataKey="createConfigurableProduct" stepKey="deleteConfigurableProduct"/> + <deleteData createDataKey="createSimpleProduct" stepKey="deleteSimpleProduct"/> + <deleteData createDataKey="createProductAttribute" stepKey="deleteProductAttribute"/> + <deleteData createDataKey="createAttributeSet" stepKey="deleteAttributeSet"/> + <deleteData createDataKey="createCategory" stepKey="deleteCategory"/> + <actionGroup ref="AdminLogoutActionGroup" stepKey="logoutFromAdmin"/> + </after> + <amOnPage url="{{StorefrontHomePage.url}}" stepKey="goToHomePage"/> + <actionGroup ref="StorefrontCheckQuickSearchStringActionGroup" stepKey="searchStorefront"> + <argument name="phrase" value="$createConfigurableProduct.name$"/> + </actionGroup> + <actionGroup ref="StorefrontQuickSearchSeeProductByNameActionGroup" stepKey="seeProductInGrid"> + <argument name="productName" value="$createConfigurableProduct.name$"/> + </actionGroup> + + <!-- Disable Child Product --> + <actionGroup ref="AdminProductPageOpenByIdActionGroup" stepKey="openSimpleProduct"> + <argument name="productId" value="$createSimpleProduct.id$"/> + </actionGroup> + <actionGroup ref="ToggleProductEnabledActionGroup" stepKey="disableProduct"/> + <actionGroup ref="SaveProductFormActionGroup" stepKey="saveProduct"/> + + <amOnPage url="{{StorefrontHomePage.url}}" stepKey="goToHomePageAgain"/> + <actionGroup ref="StorefrontCheckQuickSearchStringActionGroup" stepKey="searchStorefrontAgain"> + <argument name="phrase" value="$createConfigurableProduct.name$"/> + </actionGroup> + <actionGroup ref="StorefrontQuickSearchCheckProductNameNotInGridActionGroup" stepKey="dontSeeProductAnymore"> + <argument name="productName" value="$createConfigurableProduct.name$"/> + </actionGroup> + </test> +</tests> diff --git a/app/code/Magento/CatalogSearch/Test/Mftf/Test/StorefrontUpdateSearchTermEntityTest.xml b/app/code/Magento/CatalogSearch/Test/Mftf/Test/StorefrontUpdateSearchTermEntityTest.xml index 6c475ddc60a95..8a0d91ae05b34 100644 --- a/app/code/Magento/CatalogSearch/Test/Mftf/Test/StorefrontUpdateSearchTermEntityTest.xml +++ b/app/code/Magento/CatalogSearch/Test/Mftf/Test/StorefrontUpdateSearchTermEntityTest.xml @@ -25,21 +25,29 @@ <requiredEntity createDataKey="createCategory1"/> </createData> - <amOnPage url="{{StorefrontHomePage.url}}" stepKey="amOnStorefrontPage1"/> - <waitForPageLoad stepKey="waitForPageLoad1"/> + <!-- Perform reindex and flush cache --> + <magentoCLI command="indexer:reindex" stepKey="reindex"/> + <magentoCLI command="cache:flush" stepKey="flushCache"/> + <actionGroup ref="StorefrontOpenHomePageActionGroup" stepKey="amOnStorefrontPage1"/> </before> <after> - <actionGroup ref="logout" stepKey="logoutOfAdmin1"/> - <deleteData createDataKey="createProduct1" stepKey="deleteSimpleProduct1"/> <deleteData createDataKey="createCategory1" stepKey="deleteCategory1"/> + + <!-- Delete all search terms --> + <amOnPage url="{{AdminCatalogSearchTermIndexPage.url}}" stepKey="openAdminCatalogSearchTermIndexPage"/> + <waitForPageLoad stepKey="waitForAdminCatalogSearchTermIndexPageLoad"/> + <comment userInput="Delete all search terms" stepKey="deleteAllSearchTermsComment"/> + <actionGroup ref="AdminDeleteAllSearchTermsActionGroup" stepKey="deleteAllSearchTerms"/> + + <actionGroup ref="AdminLogoutActionGroup" stepKey="logoutOfAdmin1"/> </after> <actionGroup ref="StorefrontCheckQuickSearchStringActionGroup" stepKey="quickSearchByProductName1"> <argument name="phrase" value="$$createProduct1.name$$"/> </actionGroup> - <actionGroup ref="LoginAsAdmin" stepKey="loginAsAdmin1"/> + <actionGroup ref="AdminLoginActionGroup" stepKey="loginAsAdmin1"/> <amOnPage url="{{AdminCatalogSearchTermIndexPage.url}}" stepKey="openAdminCatalogSearchTermIndexPage1"/> <waitForPageLoad stepKey="waitForPageLoad1"/> @@ -62,8 +70,7 @@ <argument name="searchQuery" value="{{UpdatedSearchTermData1.query_text}}"/> </actionGroup> - <amOnPage url="{{StorefrontHomePage.url}}" stepKey="amOnStorefrontPage2"/> - <waitForPageLoad stepKey="waitForPageLoad4"/> + <actionGroup ref="StorefrontOpenHomePageActionGroup" stepKey="amOnStorefrontPage2"/> <actionGroup ref="StorefrontCheckQuickSearchStringActionGroup" stepKey="quickSearchByProductName2"> <argument name="phrase" value="{{UpdatedSearchTermData1.query_text}}"/> diff --git a/app/code/Magento/CatalogSearch/Test/Unit/Model/Indexer/Fulltext/Plugin/AttributeTest.php b/app/code/Magento/CatalogSearch/Test/Unit/Model/Indexer/Fulltext/Plugin/AttributeTest.php index 70ae4a75924d4..fc2b353afaf03 100644 --- a/app/code/Magento/CatalogSearch/Test/Unit/Model/Indexer/Fulltext/Plugin/AttributeTest.php +++ b/app/code/Magento/CatalogSearch/Test/Unit/Model/Indexer/Fulltext/Plugin/AttributeTest.php @@ -64,7 +64,7 @@ protected function setUp() ); $this->attributeMock = $this->createPartialMock( \Magento\Catalog\Model\ResourceModel\Eav\Attribute::class, - ['dataHasChangedFor', 'isObjectNew', 'getIsSearchable'] + ['dataHasChangedFor', 'isObjectNew', 'getIsSearchable', 'getData'] ); $this->config = $this->getMockBuilder(\Magento\Framework\Search\Request\Config::class) ->disableOriginalConstructor() @@ -85,7 +85,7 @@ public function testBeforeSave() ->method('isObjectNew') ->willReturn(true); $this->attributeMock->expects($this->once()) - ->method('dataHasChangedFor') + ->method('getData') ->with('is_searchable') ->willReturn(true); $this->assertEquals( @@ -102,22 +102,34 @@ public function testAfterSaveNoInvalidation() ); } - public function testAfterSaveWithInvalidation() + /** + * Test afterSave with invalidation. + * + * @param bool $saveNeedInvalidation + * @param bool $saveIsNew + * @dataProvider afterSaveDataProvider + */ + public function testAfterSaveWithInvalidation(bool $saveNeedInvalidation, bool $saveIsNew) { $model = $this->objectManager->getObject( Attribute::class, [ 'indexerRegistry' => $this->indexerRegistryMock, 'config' => $this->config, - 'saveNeedInvalidation' => true, - 'saveIsNew' => true + 'saveNeedInvalidation' => $saveNeedInvalidation, + 'saveIsNew' => $saveIsNew, ] ); - $this->indexerMock->expects($this->once())->method('invalidate'); - $this->prepareIndexer(); - $this->config->expects($this->once()) - ->method('reset'); + if ($saveNeedInvalidation) { + $this->indexerMock->expects($this->once())->method('invalidate'); + $this->prepareIndexer(); + } + + if ($saveIsNew || $saveNeedInvalidation) { + $this->config->expects($this->once()) + ->method('reset'); + } $this->assertEquals( $this->subjectMock, @@ -125,6 +137,19 @@ public function testAfterSaveWithInvalidation() ); } + /** + * @return array + */ + public function afterSaveDataProvider(): array + { + return [ + 'save_new_with_invalidation' => ['saveNeedInvalidation' => true, 'isNew' => true], + 'save_new_without_invalidation' => ['saveNeedInvalidation' => false, 'isNew' => true], + 'update_existing_with_inalidation' => ['saveNeedInvalidation' => true, 'isNew' => false], + 'update_existing_without_inalidation' => ['saveNeedInvalidation' => false, 'isNew' => false], + ]; + } + public function testBeforeDelete() { $this->attributeMock->expects($this->once()) diff --git a/app/code/Magento/CatalogSearch/Test/Unit/Model/Layer/Filter/PriceTest.php b/app/code/Magento/CatalogSearch/Test/Unit/Model/Layer/Filter/PriceTest.php index f783f75a170e3..fc78c45af07a6 100644 --- a/app/code/Magento/CatalogSearch/Test/Unit/Model/Layer/Filter/PriceTest.php +++ b/app/code/Magento/CatalogSearch/Test/Unit/Model/Layer/Filter/PriceTest.php @@ -209,12 +209,6 @@ public function testApply() $priceId = '15-50'; $requestVar = 'test_request_var'; - $this->target->setAttributeModel($this->attribute); - $attributeCode = 'price'; - $this->attribute->expects($this->any()) - ->method('getAttributeCode') - ->will($this->returnValue($attributeCode)); - $this->target->setRequestVar($requestVar); $this->request->expects($this->exactly(1)) ->method('getParam') diff --git a/app/code/Magento/CatalogSearch/Test/Unit/Model/Search/RequestGenerator/PriceTest.php b/app/code/Magento/CatalogSearch/Test/Unit/Model/Search/RequestGenerator/PriceTest.php deleted file mode 100644 index 3635430197591..0000000000000 --- a/app/code/Magento/CatalogSearch/Test/Unit/Model/Search/RequestGenerator/PriceTest.php +++ /dev/null @@ -1,82 +0,0 @@ -<?php -/** - * Copyright © Magento, Inc. All rights reserved. - * See COPYING.txt for license details. - */ -declare(strict_types=1); - -namespace Magento\CatalogSearch\Test\Unit\Model\Search\RequestGenerator; - -use Magento\Catalog\Model\ResourceModel\Eav\Attribute; -use Magento\CatalogSearch\Model\Search\RequestGenerator\Price; -use Magento\Framework\Search\Request\BucketInterface; -use Magento\Framework\Search\Request\FilterInterface; -use Magento\Framework\App\Config\ScopeConfigInterface; - -/** - * Test catalog search range request generator. - */ -class PriceTest extends \PHPUnit\Framework\TestCase -{ - /** @var Price */ - private $price; - - /** @var Attribute|\PHPUnit_Framework_MockObject_MockObject */ - private $attribute; - - /** @var \Magento\Framework\App\Config\ScopeConfigInterface|\PHPUnit_Framework_MockObject_MockObject */ - private $scopeConfigMock; - - protected function setUp() - { - $this->attribute = $this->getMockBuilder(Attribute::class) - ->disableOriginalConstructor() - ->setMethods(['getAttributeCode']) - ->getMockForAbstractClass(); - $this->scopeConfigMock = $this->getMockBuilder(ScopeConfigInterface::class) - ->setMethods(['getValue']) - ->getMockForAbstractClass(); - $objectManager = new \Magento\Framework\TestFramework\Unit\Helper\ObjectManager($this); - $this->price = $objectManager->getObject( - Price::class, - ['scopeConfig' => $this->scopeConfigMock] - ); - } - - public function testGetFilterData() - { - $filterName = 'test_filter_name'; - $attributeCode = 'test_attribute_code'; - $expected = [ - 'type' => FilterInterface::TYPE_RANGE, - 'name' => $filterName, - 'field' => $attributeCode, - 'from' => '$' . $attributeCode . '.from$', - 'to' => '$' . $attributeCode . '.to$', - ]; - $this->attribute->expects($this->atLeastOnce()) - ->method('getAttributeCode') - ->willReturn($attributeCode); - $actual = $this->price->getFilterData($this->attribute, $filterName); - $this->assertEquals($expected, $actual); - } - - public function testGetAggregationData() - { - $bucketName = 'test_bucket_name'; - $attributeCode = 'test_attribute_code'; - $method = 'price_dynamic_algorithm'; - $expected = [ - 'type' => BucketInterface::TYPE_DYNAMIC, - 'name' => $bucketName, - 'field' => $attributeCode, - 'method' => '$'. $method . '$', - 'metric' => [['type' => 'count']], - ]; - $this->attribute->expects($this->atLeastOnce()) - ->method('getAttributeCode') - ->willReturn($attributeCode); - $actual = $this->price->getAggregationData($this->attribute, $bucketName); - $this->assertEquals($expected, $actual); - } -} diff --git a/app/code/Magento/CatalogSearch/etc/adminhtml/system.xml b/app/code/Magento/CatalogSearch/etc/adminhtml/system.xml index c358062b88a41..e875a48aa29dc 100644 --- a/app/code/Magento/CatalogSearch/etc/adminhtml/system.xml +++ b/app/code/Magento/CatalogSearch/etc/adminhtml/system.xml @@ -38,7 +38,7 @@ <label>Autocomplete Limit</label> <validate>validate-digits</validate> </field> - <field id="enable_eav_indexer" translate="label" type="select" sortOrder="18" showInDefault="1" showInWebsite="0" showInStore="0"> + <field id="enable_eav_indexer" translate="label" type="select" sortOrder="18" showInDefault="1" canRestore="1"> <label>Enable EAV Indexer</label> <comment>Enable/Disable Product EAV indexer to improve indexation speed. Make sure that indexer is not used by 3rd party extensions.</comment> <depends> diff --git a/app/code/Magento/CatalogSearch/etc/di.xml b/app/code/Magento/CatalogSearch/etc/di.xml index 4e5b38878ee52..43ebded020ec1 100644 --- a/app/code/Magento/CatalogSearch/etc/di.xml +++ b/app/code/Magento/CatalogSearch/etc/di.xml @@ -284,7 +284,6 @@ <argument name="defaultGenerator" xsi:type="object">\Magento\CatalogSearch\Model\Search\RequestGenerator\General</argument> <argument name="generators" xsi:type="array"> <item name="decimal" xsi:type="object">Magento\CatalogSearch\Model\Search\RequestGenerator\Decimal</item> - <item name="price" xsi:type="object">Magento\CatalogSearch\Model\Search\RequestGenerator\Price</item> </argument> </arguments> </type> diff --git a/app/code/Magento/CatalogSearch/registration.php b/app/code/Magento/CatalogSearch/registration.php index 959e26c84a722..31f792d2ea90c 100644 --- a/app/code/Magento/CatalogSearch/registration.php +++ b/app/code/Magento/CatalogSearch/registration.php @@ -4,6 +4,6 @@ * See COPYING.txt for license details. */ -use \Magento\Framework\Component\ComponentRegistrar; +use Magento\Framework\Component\ComponentRegistrar; ComponentRegistrar::register(ComponentRegistrar::MODULE, 'Magento_CatalogSearch', __DIR__); diff --git a/app/code/Magento/CatalogUrlRewrite/Model/Category/Plugin/Store/Group.php b/app/code/Magento/CatalogUrlRewrite/Model/Category/Plugin/Store/Group.php index 1dbfad6cf5219..308b82e38c43a 100644 --- a/app/code/Magento/CatalogUrlRewrite/Model/Category/Plugin/Store/Group.php +++ b/app/code/Magento/CatalogUrlRewrite/Model/Category/Plugin/Store/Group.php @@ -5,6 +5,7 @@ */ namespace Magento\CatalogUrlRewrite\Model\Category\Plugin\Store; +use Magento\Store\Model\ResourceModel\Group as StoreGroup; use Magento\UrlRewrite\Model\UrlPersistInterface; use Magento\UrlRewrite\Service\V1\Data\UrlRewrite; use Magento\Catalog\Model\CategoryFactory; @@ -16,37 +17,39 @@ use Magento\Framework\Model\AbstractModel; /** + * Generate Product and Category URLs + * * @SuppressWarnings(PHPMD.CouplingBetweenObjects) */ class Group { /** - * @var \Magento\UrlRewrite\Model\UrlPersistInterface + * @var UrlPersistInterface */ protected $urlPersist; /** - * @var \Magento\Catalog\Model\CategoryFactory + * @var CategoryFactory */ protected $categoryFactory; /** - * @var \Magento\Catalog\Model\ProductFactory + * @var ProductFactory */ protected $productFactory; /** - * @var \Magento\CatalogUrlRewrite\Model\CategoryUrlRewriteGenerator + * @var CategoryUrlRewriteGenerator */ protected $categoryUrlRewriteGenerator; /** - * @var \Magento\CatalogUrlRewrite\Model\ProductUrlRewriteGenerator + * @var ProductUrlRewriteGenerator */ protected $productUrlRewriteGenerator; /** - * @var \Magento\Store\Model\StoreManagerInterface + * @var StoreManagerInterface */ protected $storeManager; @@ -77,15 +80,15 @@ public function __construct( /** * Perform updating url for categories and products assigned to the group * - * @param \Magento\Store\Model\ResourceModel\Group $subject - * @param \Magento\Store\Model\ResourceModel\Group $result + * @param StoreGroup $subject + * @param StoreGroup $result * @param AbstractModel $group - * @return \Magento\Store\Model\ResourceModel\Group + * @return StoreGroup * @SuppressWarnings(PHPMD.UnusedFormalParameter) */ public function afterSave( - \Magento\Store\Model\ResourceModel\Group $subject, - \Magento\Store\Model\ResourceModel\Group $result, + StoreGroup $subject, + StoreGroup $result, AbstractModel $group ) { if (!$group->isObjectNew() @@ -118,7 +121,7 @@ public function afterSave( */ protected function generateProductUrls($websiteId, $originWebsiteId) { - $urls = []; + $urls = [[]]; $websiteIds = $websiteId != $originWebsiteId ? [$websiteId, $originWebsiteId] : [$websiteId]; @@ -130,33 +133,30 @@ protected function generateProductUrls($websiteId, $originWebsiteId) foreach ($collection as $product) { /** @var \Magento\Catalog\Model\Product $product */ $product->setStoreId(Store::DEFAULT_STORE_ID); - $urls = array_merge( - $urls, - $this->productUrlRewriteGenerator->generate($product) - ); + $urls[] = $this->productUrlRewriteGenerator->generate($product); } - return $urls; + return array_merge(...$urls); } /** + * Generate url rewrites for categories assigned to store + * * @param int $rootCategoryId * @param array $storeIds * @return array */ protected function generateCategoryUrls($rootCategoryId, $storeIds) { - $urls = []; + $urls = [[]]; $categories = $this->categoryFactory->create()->getCategories($rootCategoryId, 1, false, true); foreach ($categories as $category) { /** @var \Magento\Catalog\Model\Category $category */ $category->setStoreId(Store::DEFAULT_STORE_ID); $category->setStoreIds($storeIds); - $urls = array_merge( - $urls, - $this->categoryUrlRewriteGenerator->generate($category) - ); + $urls[] = $this->categoryUrlRewriteGenerator->generate($category); } - return $urls; + + return array_merge(...$urls); } } diff --git a/app/code/Magento/CatalogUrlRewrite/Model/Product/AnchorUrlRewriteGenerator.php b/app/code/Magento/CatalogUrlRewrite/Model/Product/AnchorUrlRewriteGenerator.php index 5d08ea33ff8a1..a6589c6062846 100644 --- a/app/code/Magento/CatalogUrlRewrite/Model/Product/AnchorUrlRewriteGenerator.php +++ b/app/code/Magento/CatalogUrlRewrite/Model/Product/AnchorUrlRewriteGenerator.php @@ -67,7 +67,7 @@ public function generate($storeId, Product $product, ObjectRegistry $productCate $anchorCategoryIds = $category->getAnchorsAbove(); if ($anchorCategoryIds) { foreach ($anchorCategoryIds as $anchorCategoryId) { - $anchorCategory = $this->categoryRepository->get($anchorCategoryId); + $anchorCategory = $this->categoryRepository->get($anchorCategoryId, $storeId); if ((int)$anchorCategory->getParentId() === Category::TREE_ROOT_ID) { continue; } diff --git a/app/code/Magento/CatalogUrlRewrite/Model/ProductUrlPathGenerator.php b/app/code/Magento/CatalogUrlRewrite/Model/ProductUrlPathGenerator.php index ac3a5092bb3bf..da2dd8a505869 100644 --- a/app/code/Magento/CatalogUrlRewrite/Model/ProductUrlPathGenerator.php +++ b/app/code/Magento/CatalogUrlRewrite/Model/ProductUrlPathGenerator.php @@ -3,20 +3,19 @@ * Copyright © Magento, Inc. All rights reserved. * See COPYING.txt for license details. */ +declare(strict_types=1); namespace Magento\CatalogUrlRewrite\Model; -use Magento\Store\Model\Store; use Magento\Catalog\Api\ProductRepositoryInterface; use Magento\Catalog\Model\Category; use Magento\Catalog\Model\Product; -use Magento\CatalogUrlRewrite\Model\CategoryUrlPathGenerator; use Magento\Framework\App\Config\ScopeConfigInterface; use Magento\Store\Model\ScopeInterface; use Magento\Store\Model\StoreManagerInterface; /** - * Class ProductUrlPathGenerator + * Model product url path generator */ class ProductUrlPathGenerator { @@ -150,7 +149,7 @@ protected function prepareProductUrlKey(Product $product) $urlKey = (string)$product->getUrlKey(); $urlKey = trim(strtolower($urlKey)); - return $urlKey ?: $product->formatUrlKey($product->getName()); + return $product->formatUrlKey($urlKey ?: $product->getName()); } /** diff --git a/app/code/Magento/CatalogUrlRewrite/Observer/CategoryUrlPathAutogeneratorObserver.php b/app/code/Magento/CatalogUrlRewrite/Observer/CategoryUrlPathAutogeneratorObserver.php index 0d0b0fb995706..401f2c9629f76 100644 --- a/app/code/Magento/CatalogUrlRewrite/Observer/CategoryUrlPathAutogeneratorObserver.php +++ b/app/code/Magento/CatalogUrlRewrite/Observer/CategoryUrlPathAutogeneratorObserver.php @@ -7,12 +7,14 @@ namespace Magento\CatalogUrlRewrite\Observer; +use Magento\Catalog\Api\CategoryRepositoryInterface; use Magento\Catalog\Model\Category; +use Magento\CatalogUrlRewrite\Model\Category\ChildrenCategoriesProvider; use Magento\CatalogUrlRewrite\Model\CategoryUrlPathGenerator; use Magento\CatalogUrlRewrite\Service\V1\StoreViewService; -use Magento\Catalog\Api\CategoryRepositoryInterface; -use Magento\CatalogUrlRewrite\Model\Category\ChildrenCategoriesProvider; use Magento\Framework\Event\ObserverInterface; +use Magento\Framework\Exception\LocalizedException; +use Magento\Framework\Exception\NoSuchEntityException; use Magento\Store\Model\Store; /** @@ -83,7 +85,7 @@ public function __construct( * * @param \Magento\Framework\Event\Observer $observer * @return void - * @throws \Magento\Framework\Exception\LocalizedException + * @throws LocalizedException */ public function execute(\Magento\Framework\Event\Observer $observer) { @@ -106,18 +108,61 @@ public function execute(\Magento\Framework\Event\Observer $observer) * Update Url Key * * @param Category $category - * @param string $urlKey - * @throws \Magento\Framework\Exception\LocalizedException - * @throws \Magento\Framework\Exception\NoSuchEntityException + * @param string|null $urlKey + * @return void + * @throws LocalizedException + * @throws NoSuchEntityException + */ + private function updateUrlKey(Category $category, ?string $urlKey): void + { + $this->validateUrlKey($category, $urlKey); + $category->setUrlKey($urlKey) + ->setUrlPath($this->categoryUrlPathGenerator->getUrlPath($category)); + if (!$category->isObjectNew()) { + $category->getResource()->saveAttribute($category, 'url_path'); + if ($category->dataHasChangedFor('url_path')) { + $this->updateUrlPathForChildren($category); + } + } + } + + /** + * Validate URL key value + * + * @param Category $category + * @param string|null $urlKey + * @return void + * @throws LocalizedException */ - private function updateUrlKey($category, $urlKey) + private function validateUrlKey(Category $category, ?string $urlKey): void { + if (empty($urlKey) && !empty($category->getName()) && !empty($category->getUrlKey())) { + throw new LocalizedException( + __( + 'Invalid URL key. The "%1" URL key can not be used to generate Latin URL key. ' . + 'Please use Latin letters and numbers to avoid generating URL key issues.', + $category->getUrlKey() + ) + ); + } + + if (empty($urlKey) && !empty($category->getName())) { + throw new LocalizedException( + __( + 'Invalid URL key. The "%1" category name can not be used to generate Latin URL key. ' . + 'Please add URL key or change category name using Latin letters and numbers to avoid generating ' . + 'URL key issues.', + $category->getName() + ) + ); + } + if (empty($urlKey)) { - throw new \Magento\Framework\Exception\LocalizedException(__('Invalid URL key')); + throw new LocalizedException(__('Invalid URL key')); } if (in_array($urlKey, $this->getInvalidValues())) { - throw new \Magento\Framework\Exception\LocalizedException( + throw new LocalizedException( __( 'URL key "%1" matches a reserved endpoint name (%2). Use another URL key.', $urlKey, @@ -125,15 +170,6 @@ private function updateUrlKey($category, $urlKey) ) ); } - - $category->setUrlKey($urlKey) - ->setUrlPath($this->categoryUrlPathGenerator->getUrlPath($category)); - if (!$category->isObjectNew()) { - $category->getResource()->saveAttribute($category, 'url_path'); - if ($category->dataHasChangedFor('url_path')) { - $this->updateUrlPathForChildren($category); - } - } } /** @@ -199,7 +235,7 @@ protected function isGlobalScope($storeId) * @param Category $category * @param Category|null $parentCategory * @return void - * @throws \Magento\Framework\Exception\NoSuchEntityException + * @throws NoSuchEntityException */ protected function updateUrlPathForCategory(Category $category, Category $parentCategory = null) { diff --git a/app/code/Magento/CatalogUrlRewrite/Setup/Patch/Data/UpdateUrlKeyForProducts.php b/app/code/Magento/CatalogUrlRewrite/Setup/Patch/Data/UpdateUrlKeyForProducts.php new file mode 100644 index 0000000000000..5e7039912999b --- /dev/null +++ b/app/code/Magento/CatalogUrlRewrite/Setup/Patch/Data/UpdateUrlKeyForProducts.php @@ -0,0 +1,102 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +declare(strict_types=1); + +namespace Magento\CatalogUrlRewrite\Setup\Patch\Data; + +use Magento\Catalog\Model\Product\Url; +use Magento\Eav\Setup\EavSetup; +use Magento\Eav\Setup\EavSetupFactory; +use Magento\Framework\Setup\ModuleDataSetupInterface; +use Magento\Framework\Setup\Patch\DataPatchInterface; +use Magento\Framework\Setup\Patch\PatchVersionInterface; + +/** + * Update url_key all products. + */ +class UpdateUrlKeyForProducts implements DataPatchInterface, PatchVersionInterface +{ + /** + * @var ModuleDataSetupInterface + */ + private $moduleDataSetup; + + /** + * @var EavSetup + */ + private $eavSetup; + + /** + * @var Url + */ + private $urlProduct; + + /** + * @param ModuleDataSetupInterface $moduleDataSetup + * @param EavSetupFactory $eavSetupFactory + * @param Url $urlProduct + */ + public function __construct( + ModuleDataSetupInterface $moduleDataSetup, + EavSetupFactory $eavSetupFactory, + Url $urlProduct + ) { + $this->moduleDataSetup = $moduleDataSetup; + $this->eavSetup = $eavSetupFactory->create(['setup' => $moduleDataSetup]); + $this->urlProduct = $urlProduct; + } + + /** + * @inheritdoc + */ + public function apply() + { + $productTypeId = $this->eavSetup->getEntityTypeId(\Magento\Catalog\Model\Product::ENTITY); + $table = $this->moduleDataSetup->getTable('catalog_product_entity_varchar'); + $select = $this->moduleDataSetup->getConnection()->select()->from( + $table, + ['value_id', 'value'] + )->where( + 'attribute_id = ?', + $this->eavSetup->getAttributeId($productTypeId, 'url_key') + ); + + $result = $this->moduleDataSetup->getConnection()->fetchAll($select); + foreach ($result as $key => $item) { + $result[$key]['value'] = $this->urlProduct->formatUrlKey($item['value']); + } + + foreach (array_chunk($result, 500, true) as $pathResult) { + $this->moduleDataSetup->getConnection()->insertOnDuplicate($table, $pathResult, ['value']); + } + + return $this; + } + + /** + * @inheritDoc + */ + public static function getVersion() + { + return "2.4.0"; + } + + /** + * @inheritdoc + */ + public static function getDependencies() + { + return []; + } + + /** + * @inheritdoc + */ + public function getAliases() + { + return []; + } +} diff --git a/app/code/Magento/CatalogUrlRewrite/Test/Mftf/ActionGroup/AssertStorefrontProductRewriteUrlSubCategoryActionGroup.xml b/app/code/Magento/CatalogUrlRewrite/Test/Mftf/ActionGroup/AssertStorefrontProductRewriteUrlSubCategoryActionGroup.xml new file mode 100644 index 0000000000000..4675d3b2669a4 --- /dev/null +++ b/app/code/Magento/CatalogUrlRewrite/Test/Mftf/ActionGroup/AssertStorefrontProductRewriteUrlSubCategoryActionGroup.xml @@ -0,0 +1,21 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + /** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +--> +<actionGroups xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/actionGroupSchema.xsd"> + <actionGroup name="AssertStorefrontProductRewriteUrlSubCategoryActionGroup"> + <annotations> + <description>Validates that the provided Product Title is present on the Rewrite URL with a subcategory page.</description> + </annotations> + <arguments> + <argument name="category" type="string" defaultValue="simplecategory"/> + <argument name="product" defaultValue="SimpleProduct" /> + </arguments> + + <amOnPage url="{{category}}/{{product.urlKey}}2.html" stepKey="goToProductPage"/> + <see selector="{{StorefrontProductInfoMainSection.productName}}" userInput="{{product.name}}" stepKey="seeProductNameInStoreFront"/> + </actionGroup> +</actionGroups> diff --git a/app/code/Magento/CatalogUrlRewrite/Test/Mftf/Data/GenerateCategoryProductUrlRewriteConfigData.xml b/app/code/Magento/CatalogUrlRewrite/Test/Mftf/Data/GenerateCategoryProductUrlRewriteConfigData.xml new file mode 100644 index 0000000000000..9ce6d397a551b --- /dev/null +++ b/app/code/Magento/CatalogUrlRewrite/Test/Mftf/Data/GenerateCategoryProductUrlRewriteConfigData.xml @@ -0,0 +1,30 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + /** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +--> + +<entities xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:noNamespaceSchemaLocation="urn:magento:mftf:DataGenerator/etc/dataProfileSchema.xsd"> + <entity name="EnableGenerateCategoryProductUrlRewritesConfigData"> + <!-- Default value --> + <data key="path">catalog/seo/generate_category_product_rewrites</data> + <data key="label">Yes</data> + <data key="value">1</data> + </entity> + <entity name="DisableGenerateCategoryProductUrlRewritesConfigData"> + <data key="path">catalog/seo/generate_category_product_rewrites</data> + <data key="label">No</data> + <data key="value">0</data> + </entity> + <entity name="EnableCategoriesPathProductUrls"> + <data key="path">catalog/seo/product_use_categories</data> + <data key="value">1</data> + </entity> + <entity name="DisableCategoriesPathProductUrls"> + <data key="path">catalog/seo/product_use_categories</data> + <data key="value">0</data> + </entity> +</entities> diff --git a/app/code/Magento/CatalogUrlRewrite/Test/Mftf/Test/AdminCategoryWithRestrictedUrlKeyNotCreatedTest.xml b/app/code/Magento/CatalogUrlRewrite/Test/Mftf/Test/AdminCategoryWithRestrictedUrlKeyNotCreatedTest.xml index c3c7e3c3281f4..dec157484dfef 100644 --- a/app/code/Magento/CatalogUrlRewrite/Test/Mftf/Test/AdminCategoryWithRestrictedUrlKeyNotCreatedTest.xml +++ b/app/code/Magento/CatalogUrlRewrite/Test/Mftf/Test/AdminCategoryWithRestrictedUrlKeyNotCreatedTest.xml @@ -19,7 +19,7 @@ <group value="CatalogUrlRewrite"/> </annotations> <before> - <actionGroup ref="LoginAsAdmin" stepKey="loginAsAdmin"/> + <actionGroup ref="AdminLoginActionGroup" stepKey="loginAsAdmin"/> </before> <after> <!--Delete created categories--> @@ -36,7 +36,7 @@ <actionGroup ref="AdminDeleteCategoryByNameActionGroup" stepKey="deleteGraphQlCategory"> <argument name="categoryName" value="graphql"/> </actionGroup> - <actionGroup ref="logout" stepKey="logout"/> + <actionGroup ref="AdminLogoutActionGroup" stepKey="logout"/> </after> <!--Check category creation with restricted url key 'admin'--> <comment userInput="Check category creation with restricted url key 'admin'" stepKey="commentCheckAdminCategoryCreation"/> diff --git a/app/code/Magento/CatalogUrlRewrite/Test/Mftf/Test/AdminRewriteProductWithTwoStoreTest.xml b/app/code/Magento/CatalogUrlRewrite/Test/Mftf/Test/AdminRewriteProductWithTwoStoreTest.xml new file mode 100644 index 0000000000000..e8a1ee850cd74 --- /dev/null +++ b/app/code/Magento/CatalogUrlRewrite/Test/Mftf/Test/AdminRewriteProductWithTwoStoreTest.xml @@ -0,0 +1,86 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + /** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +--> +<tests xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/testSchema.xsd"> + <test name="AdminRewriteProductWithTwoStoreTest"> + <annotations> + <title value="Rewriting URL of product"/> + <description value="Rewriting URL of product. Verify the full URL address"/> + <group value="CatalogUrlRewrite"/> + </annotations> + + <before> + <magentoCLI command="config:set {{EnableCategoriesPathProductUrls.path}} {{EnableCategoriesPathProductUrls.value}}" stepKey="enableUseCategoriesPath"/> + <magentoCLI command="cache:flush" stepKey="flushCache"/> + + <actionGroup ref="AdminLoginActionGroup" stepKey="loginAsAdmin"/> + <actionGroup ref="AdminCreateStoreViewActionGroup" stepKey="createStoreView" /> + <createData entity="_defaultCategoryDifferentUrlStore" stepKey="defaultCategory"/> + <createData entity="SimpleSubCategoryDifferentUrlStore" stepKey="subCategory"> + <requiredEntity createDataKey="defaultCategory"/> + </createData> + <createData entity="SimpleProduct" stepKey="simpleProduct"> + <requiredEntity createDataKey="subCategory"/> + </createData> + </before> + + <after> + <actionGroup ref="AdminDeleteStoreViewActionGroup" stepKey="deleteStoreView"/> + <actionGroup ref="AdminLogoutActionGroup" stepKey="logout"/> + <deleteData createDataKey="simpleProduct" stepKey="deleteSimpleProduct"/> + <deleteData createDataKey="defaultCategory" stepKey="deleteNewRootCategory"/> + <magentoCLI command="config:set {{DisableCategoriesPathProductUrls.path}} {{DisableCategoriesPathProductUrls.value}}" stepKey="disableUseCategoriesPath"/> + <magentoCLI command="cache:flush" stepKey="flushCache"/> + </after> + + <actionGroup ref="NavigateToCreatedCategoryActionGroup" stepKey="navigateToCreatedDefaultCategory"> + <argument name="Category" value="$$defaultCategory$$"/> + </actionGroup> + <actionGroup ref="AdminSwitchStoreViewActionGroup" stepKey="AdminSwitchDefaultStoreViewForDefaultCategory"> + <argument name="storeView" value="_defaultStore.name"/> + </actionGroup> + <actionGroup ref="AdminChangeSeoUrlKeyForSubCategoryWithoutRedirectActionGroup" stepKey="changeSeoUrlKeyForDefaultCategoryDefaultStore"> + <argument name="value" value="{{_defaultCategoryDifferentUrlStore.url_key_default_store}}"/> + </actionGroup> + <actionGroup ref="AdminSwitchStoreViewActionGroup" stepKey="AdminSwitchCustomStoreViewForDefaultCategory"> + <argument name="storeView" value="customStore.name"/> + </actionGroup> + <actionGroup ref="AdminChangeSeoUrlKeyForSubCategoryWithoutRedirectActionGroup" stepKey="changeSeoUrlKeyForDefaultCategoryCustomStore"> + <argument name="value" value="{{_defaultCategoryDifferentUrlStore.url_key_custom_store}}"/> + </actionGroup> + + <actionGroup ref="NavigateToCreatedCategoryActionGroup" stepKey="navigateToCreatedSubCategory"> + <argument name="Category" value="$$subCategory$$"/> + </actionGroup> + <actionGroup ref="AdminSwitchStoreViewActionGroup" stepKey="AdminSwitchDefaultStoreViewForSubCategory"> + <argument name="storeView" value="_defaultStore.name"/> + </actionGroup> + <actionGroup ref="AdminChangeSeoUrlKeyForSubCategoryWithoutRedirectActionGroup" stepKey="changeSeoUrlKeyForSubCategoryDefaultStore"> + <argument name="value" value="{{SimpleSubCategoryDifferentUrlStore.url_key_default_store}}"/> + </actionGroup> + <actionGroup ref="AdminSwitchStoreViewActionGroup" stepKey="AdminSwitchCustomStoreViewForSubCategory"> + <argument name="storeView" value="customStore.name"/> + </actionGroup> + + <actionGroup ref="AdminChangeSeoUrlKeyForSubCategoryWithoutRedirectActionGroup" stepKey="changeSeoUrlKeyForSubCategoryCustomStore"> + <argument name="value" value="{{SimpleSubCategoryDifferentUrlStore.url_key_custom_store}}"/> + </actionGroup> + <actionGroup ref="AssertStorefrontProductRewriteUrlSubCategoryActionGroup" stepKey="validatesRewriteUrlDefaultStore"> + <argument name="category" value="{{_defaultCategoryDifferentUrlStore.url_key_default_store}}"/> + <argument name="product" value="SimpleProduct" /> + </actionGroup> + + <actionGroup ref="StorefrontSwitchStoreViewActionGroup" stepKey="switchStore"> + <argument name="storeView" value="customStore" /> + </actionGroup> + <actionGroup ref="AssertStorefrontProductRewriteUrlSubCategoryActionGroup" stepKey="validatesRewriteUrlCustomStore"> + <argument name="category" value="{{_defaultCategoryDifferentUrlStore.url_key_custom_store}}"/> + <argument name="product" value="SimpleProduct" /> + </actionGroup> + + </test> +</tests> diff --git a/app/code/Magento/CatalogUrlRewrite/Test/Mftf/Test/AdminUrlForProductRewrittenCorrectlyTest.xml b/app/code/Magento/CatalogUrlRewrite/Test/Mftf/Test/AdminUrlForProductRewrittenCorrectlyTest.xml index cc5f09faca57b..75ae9d821c356 100644 --- a/app/code/Magento/CatalogUrlRewrite/Test/Mftf/Test/AdminUrlForProductRewrittenCorrectlyTest.xml +++ b/app/code/Magento/CatalogUrlRewrite/Test/Mftf/Test/AdminUrlForProductRewrittenCorrectlyTest.xml @@ -20,7 +20,7 @@ </annotations> <before> - <actionGroup ref="LoginAsAdmin" stepKey="loginAsAdmin"/> + <actionGroup ref="AdminLoginActionGroup" stepKey="loginAsAdmin"/> <!--Create product--> <createData entity="_defaultCategory" stepKey="category"/> @@ -33,11 +33,13 @@ <deleteData createDataKey="createProduct" stepKey="deleteSimpleProduct"/> <deleteData createDataKey="category" stepKey="deleteCategory"/> - <actionGroup ref="logout" stepKey="logout"/> + <actionGroup ref="AdminLogoutActionGroup" stepKey="logout"/> </after> <!--Open Created product--> - <amOnPage url="{{AdminProductEditPage.url($$createProduct.id$$)}}" stepKey="amOnEditPage"/> + <actionGroup ref="AdminProductPageOpenByIdActionGroup" stepKey="amOnEditPage"> + <argument name="productId" value="$$createProduct.id$$"/> + </actionGroup> <waitForPageLoad stepKey="waitForEditPage"/> <!--Switch to Default Store view--> diff --git a/app/code/Magento/CatalogUrlRewrite/Test/Mftf/Test/RewriteStoreLevelUrlKeyOfChildCategoryTest.xml b/app/code/Magento/CatalogUrlRewrite/Test/Mftf/Test/RewriteStoreLevelUrlKeyOfChildCategoryTest.xml index c3a358bbbd292..779d439fda301 100644 --- a/app/code/Magento/CatalogUrlRewrite/Test/Mftf/Test/RewriteStoreLevelUrlKeyOfChildCategoryTest.xml +++ b/app/code/Magento/CatalogUrlRewrite/Test/Mftf/Test/RewriteStoreLevelUrlKeyOfChildCategoryTest.xml @@ -12,13 +12,13 @@ <stories value="MAGETWO-91649: #13513: Magento ignore store-level url_key of child category in URL rewrite process for global scope"/> <description value="Rewriting Store-level URL key of child category"/> <features value="CatalogUrlRewrite"/> - <severity value="MAJOR"/> + <severity value="BLOCKER"/> <testCaseId value="MAGETWO-94934"/> <group value="CatalogUrlRewrite"/> </annotations> <before> - <actionGroup ref="LoginAsAdmin" stepKey="loginAsAdmin"/> + <actionGroup ref="AdminLoginActionGroup" stepKey="loginAsAdmin"/> <actionGroup ref="AdminCreateStoreViewActionGroup" stepKey="createStoreView" /> <createData entity="_defaultCategory" stepKey="defaultCategory"/> @@ -45,8 +45,7 @@ <argument name="value" value="gear-global"/> </actionGroup> - <amOnPage url="{{StorefrontHomePage.url}}" stepKey="goToStorefrontPage"/> - <waitForPageLoad stepKey="waitForPageLoad"/> + <actionGroup ref="StorefrontOpenHomePageActionGroup" stepKey="goToStorefrontPage"/> <actionGroup ref="StorefrontSwitchStoreViewActionGroup" stepKey="storefrontSwitchStoreView"/> @@ -58,7 +57,7 @@ <after> <actionGroup ref="AdminDeleteStoreViewActionGroup" stepKey="deleteStoreView"/> - <actionGroup ref="logout" stepKey="logout"/> + <actionGroup ref="AdminLogoutActionGroup" stepKey="logout"/> <deleteData createDataKey="subCategory" stepKey="deleteSubCategory"/> <deleteData createDataKey="defaultCategory" stepKey="deleteNewRootCategory"/> diff --git a/app/code/Magento/CatalogUrlRewrite/Test/Mftf/Test/StorefrontCategoryAccessibleWhenSuffixIsNullTest.xml b/app/code/Magento/CatalogUrlRewrite/Test/Mftf/Test/StorefrontCategoryAccessibleWhenSuffixIsNullTest.xml new file mode 100644 index 0000000000000..99037a5c89af1 --- /dev/null +++ b/app/code/Magento/CatalogUrlRewrite/Test/Mftf/Test/StorefrontCategoryAccessibleWhenSuffixIsNullTest.xml @@ -0,0 +1,40 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + /** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +--> +<tests xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/testSchema.xsd"> + <test name="StorefrontCategoryAccessibleWhenSuffixIsNullTest"> + <annotations> + <stories value="Url rewrites"/> + <title value="Storefront category is accessible when url suffix is set to null test"/> + <description value="Check no crash occurs on Category page when catalog/seo/category_url_suffix is set to null"/> + <features value="CatalogUrlRewrite"/> + <stories value="Url rewrites"/> + <severity value="MAJOR"/> + <group value="CatalogUrlRewrite"/> + </annotations> + <before> + <magentoCLI command="config:set catalog/seo/category_url_suffix ''" stepKey="setCategoryUrlSuffix"/> + <magentoCLI command="config:set catalog/seo/generate_category_product_rewrites 0" + stepKey="setCategoryProductRewrites"/> + <magentoCLI command="cache:flush" stepKey="flushCacheBefore"/> + <createData entity="_defaultCategory" stepKey="createCategory"/> + </before> + <after> + <deleteData createDataKey="createCategory" stepKey="deleteCategory"/> + <magentoCLI command="config:set catalog/seo/category_url_suffix '.html'" + stepKey="restoreCategoryUrlSuffix"/> + <magentoCLI command="config:set catalog/seo/generate_category_product_rewrites 1" + stepKey="restoreCategoryProductRewrites"/> + <magentoCLI command="cache:flush" stepKey="flushCacheAfter"/> + </after> + + <amOnPage url="/$$createCategory.name$$" stepKey="onCategoryPage"/> + <waitForPageLoad stepKey="waitForPageLoad"/> + <seeInTitle userInput="$$createCategory.name$$" stepKey="assertCategoryNameInTitle"/> + </test> +</tests> diff --git a/app/code/Magento/CatalogUrlRewrite/Test/Unit/Model/Product/AnchorUrlRewriteGeneratorTest.php b/app/code/Magento/CatalogUrlRewrite/Test/Unit/Model/Product/AnchorUrlRewriteGeneratorTest.php index 662e156b8f100..d8fec2de0e46e 100644 --- a/app/code/Magento/CatalogUrlRewrite/Test/Unit/Model/Product/AnchorUrlRewriteGeneratorTest.php +++ b/app/code/Magento/CatalogUrlRewrite/Test/Unit/Model/Product/AnchorUrlRewriteGeneratorTest.php @@ -3,53 +3,67 @@ * Copyright © Magento, Inc. All rights reserved. * See COPYING.txt for license details. */ +declare(strict_types=1); + namespace Magento\CatalogUrlRewrite\Test\Unit\Model\Product; +use Magento\Catalog\Api\CategoryRepositoryInterface; +use Magento\Catalog\Model\Product; +use Magento\CatalogUrlRewrite\Model\ObjectRegistry; +use Magento\CatalogUrlRewrite\Model\Product\AnchorUrlRewriteGenerator; +use Magento\CatalogUrlRewrite\Model\ProductUrlPathGenerator; use Magento\CatalogUrlRewrite\Model\ProductUrlRewriteGenerator; use Magento\Framework\TestFramework\Unit\Helper\ObjectManager; +use Magento\UrlRewrite\Service\V1\Data\UrlRewrite; +use Magento\UrlRewrite\Service\V1\Data\UrlRewriteFactory; +use PHPUnit\Framework\TestCase; +use PHPUnit_Framework_MockObject_MockObject as MockObject; -class AnchorUrlRewriteGeneratorTest extends \PHPUnit\Framework\TestCase +class AnchorUrlRewriteGeneratorTest extends TestCase { - /** @var \Magento\CatalogUrlRewrite\Model\Product\AnchorUrlRewriteGenerator */ + /** @var AnchorUrlRewriteGenerator */ protected $anchorUrlRewriteGenerator; - /** @var \Magento\CatalogUrlRewrite\Model\ProductUrlPathGenerator|\PHPUnit_Framework_MockObject_MockObject */ + /** @var ProductUrlPathGenerator|MockObject */ protected $productUrlPathGenerator; - /** @var \Magento\Catalog\Model\Product|\PHPUnit_Framework_MockObject_MockObject */ + /** @var Product|MockObject */ protected $product; - /** @var \Magento\Catalog\Api\CategoryRepositoryInterface|\PHPUnit_Framework_MockObject_MockObject */ + /** @var CategoryRepositoryInterface|MockObject */ private $categoryRepositoryInterface; - /** @var \Magento\CatalogUrlRewrite\Model\ObjectRegistry|\PHPUnit_Framework_MockObject_MockObject */ + /** @var ObjectRegistry|MockObject */ protected $categoryRegistry; - /** @var \Magento\UrlRewrite\Service\V1\Data\UrlRewriteFactory|\PHPUnit_Framework_MockObject_MockObject */ + /** @var UrlRewriteFactory|MockObject */ protected $urlRewriteFactory; - /** @var \Magento\UrlRewrite\Service\V1\Data\UrlRewrite|\PHPUnit_Framework_MockObject_MockObject */ + /** @var UrlRewrite|MockObject */ protected $urlRewrite; + /** + * @inheritDoc + */ protected function setUp() { - $this->urlRewriteFactory = $this->getMockBuilder(\Magento\UrlRewrite\Service\V1\Data\UrlRewriteFactory::class) + $this->urlRewriteFactory = $this->getMockBuilder(UrlRewriteFactory::class) ->setMethods(['create']) ->disableOriginalConstructor()->getMock(); - $this->urlRewrite = $this->getMockBuilder(\Magento\UrlRewrite\Service\V1\Data\UrlRewrite::class) + $this->urlRewrite = $this->getMockBuilder(UrlRewrite::class) ->disableOriginalConstructor()->getMock(); - $this->product = $this->getMockBuilder(\Magento\Catalog\Model\Product::class) + $this->product = $this->getMockBuilder(Product::class) ->disableOriginalConstructor()->getMock(); $this->categoryRepositoryInterface = $this->getMockBuilder( - \Magento\Catalog\Api\CategoryRepositoryInterface::class + CategoryRepositoryInterface::class )->disableOriginalConstructor()->getMock(); - $this->categoryRegistry = $this->getMockBuilder(\Magento\CatalogUrlRewrite\Model\ObjectRegistry::class) + $this->categoryRegistry = $this->getMockBuilder(ObjectRegistry::class) ->disableOriginalConstructor()->getMock(); $this->productUrlPathGenerator = $this->getMockBuilder( - \Magento\CatalogUrlRewrite\Model\ProductUrlPathGenerator::class + ProductUrlPathGenerator::class )->disableOriginalConstructor()->getMock(); $this->anchorUrlRewriteGenerator = (new ObjectManager($this))->getObject( - \Magento\CatalogUrlRewrite\Model\Product\AnchorUrlRewriteGenerator::class, + AnchorUrlRewriteGenerator::class, [ 'productUrlPathGenerator' => $this->productUrlPathGenerator, 'urlRewriteFactory' => $this->urlRewriteFactory, @@ -58,7 +72,12 @@ protected function setUp() ); } - public function testGenerateEmpty() + /** + * Verify generate if category registry list is empty. + * + * @return void + */ + public function testGenerateEmpty(): void { $this->categoryRegistry->expects($this->any())->method('getList')->will($this->returnValue([])); @@ -68,7 +87,12 @@ public function testGenerateEmpty() ); } - public function testGenerateCategories() + /** + * Verify generate product rewrites for anchor categories. + * + * @return void + */ + public function testGenerateCategories(): void { $urlPathWithCategory = 'category1/category2/category3/simple-product.html'; $storeId = 10; @@ -100,9 +124,9 @@ public function testGenerateCategories() ->expects($this->any()) ->method('get') ->withConsecutive( - [ 'category_id' => $categoryIds[0]], - [ 'category_id' => $categoryIds[1]], - [ 'category_id' => $categoryIds[2]] + [$categoryIds[0], $storeId], + [$categoryIds[1], $storeId], + [$categoryIds[2], $storeId] ) ->will($this->returnValue($category)); $this->categoryRegistry->expects($this->any())->method('getList') diff --git a/app/code/Magento/CatalogUrlRewrite/Test/Unit/Model/ProductUrlPathGeneratorTest.php b/app/code/Magento/CatalogUrlRewrite/Test/Unit/Model/ProductUrlPathGeneratorTest.php index 5076577447af3..95ef16c5ace4c 100644 --- a/app/code/Magento/CatalogUrlRewrite/Test/Unit/Model/ProductUrlPathGeneratorTest.php +++ b/app/code/Magento/CatalogUrlRewrite/Test/Unit/Model/ProductUrlPathGeneratorTest.php @@ -7,34 +7,42 @@ namespace Magento\CatalogUrlRewrite\Test\Unit\Model; +use Magento\Catalog\Api\ProductRepositoryInterface; +use Magento\Catalog\Model\Category; +use Magento\Catalog\Model\Product; +use Magento\CatalogUrlRewrite\Model\CategoryUrlPathGenerator; use Magento\CatalogUrlRewrite\Model\ProductUrlPathGenerator; +use Magento\Framework\App\Config\ScopeConfigInterface; use Magento\Framework\TestFramework\Unit\Helper\ObjectManager; use Magento\Store\Model\ScopeInterface; +use Magento\Store\Model\StoreManagerInterface; +use PHPUnit\Framework\TestCase; +use PHPUnit_Framework_MockObject_MockObject as MockObject; /** - * Class ProductUrlPathGeneratorTest + * Verify ProductUrlPathGenerator class */ -class ProductUrlPathGeneratorTest extends \PHPUnit\Framework\TestCase +class ProductUrlPathGeneratorTest extends TestCase { - /** @var \Magento\CatalogUrlRewrite\Model\ProductUrlPathGenerator */ + /** @var ProductUrlPathGenerator */ protected $productUrlPathGenerator; - /** @var \Magento\Store\Model\StoreManagerInterface|\PHPUnit_Framework_MockObject_MockObject */ + /** @var StoreManagerInterface|MockObject */ protected $storeManager; - /** @var \Magento\Framework\App\Config\ScopeConfigInterface|\PHPUnit_Framework_MockObject_MockObject */ + /** @var ScopeConfigInterface|MockObject */ protected $scopeConfig; - /** @var \Magento\CatalogUrlRewrite\Model\CategoryUrlPathGenerator|\PHPUnit_Framework_MockObject_MockObject */ + /** @var CategoryUrlPathGenerator|MockObject */ protected $categoryUrlPathGenerator; - /** @var \Magento\Catalog\Model\Product|\PHPUnit_Framework_MockObject_MockObject */ + /** @var Product|MockObject */ protected $product; - /** @var \Magento\Catalog\Api\ProductRepositoryInterface|\PHPUnit_Framework_MockObject_MockObject */ + /** @var ProductRepositoryInterface|MockObject */ protected $productRepository; - /** @var \Magento\Catalog\Model\Category|\PHPUnit_Framework_MockObject_MockObject */ + /** @var Category|MockObject */ protected $category; /** @@ -42,7 +50,7 @@ class ProductUrlPathGeneratorTest extends \PHPUnit\Framework\TestCase */ protected function setUp(): void { - $this->category = $this->createMock(\Magento\Catalog\Model\Category::class); + $this->category = $this->createMock(Category::class); $productMethods = [ '__wakeup', 'getData', @@ -54,17 +62,17 @@ protected function setUp(): void 'setStoreId', ]; - $this->product = $this->createPartialMock(\Magento\Catalog\Model\Product::class, $productMethods); - $this->storeManager = $this->createMock(\Magento\Store\Model\StoreManagerInterface::class); - $this->scopeConfig = $this->createMock(\Magento\Framework\App\Config\ScopeConfigInterface::class); + $this->product = $this->createPartialMock(Product::class, $productMethods); + $this->storeManager = $this->createMock(StoreManagerInterface::class); + $this->scopeConfig = $this->createMock(ScopeConfigInterface::class); $this->categoryUrlPathGenerator = $this->createMock( - \Magento\CatalogUrlRewrite\Model\CategoryUrlPathGenerator::class + CategoryUrlPathGenerator::class ); - $this->productRepository = $this->createMock(\Magento\Catalog\Api\ProductRepositoryInterface::class); + $this->productRepository = $this->createMock(ProductRepositoryInterface::class); $this->productRepository->expects($this->any())->method('getById')->willReturn($this->product); $this->productUrlPathGenerator = (new ObjectManager($this))->getObject( - \Magento\CatalogUrlRewrite\Model\ProductUrlPathGenerator::class, + ProductUrlPathGenerator::class, [ 'storeManager' => $this->storeManager, 'scopeConfig' => $this->scopeConfig, @@ -75,13 +83,15 @@ protected function setUp(): void } /** + * Data provider for testGetUrlPath. + * * @return array */ public function getUrlPathDataProvider(): array { return [ - 'path based on url key uppercase' => ['Url-Key', null, 0, 'url-key'], - 'path based on url key' => ['url-key', null, 0, 'url-key'], + 'path based on url key uppercase' => ['Url-Key', null, 1, 'url-key'], + 'path based on url key' => ['url-key', null, 1, 'url-key'], 'path based on product name 1' => ['', 'product-name', 1, 'product-name'], 'path based on product name 2' => [null, 'product-name', 1, 'product-name'], 'path based on product name 3' => [false, 'product-name', 1, 'product-name'] @@ -89,6 +99,8 @@ public function getUrlPathDataProvider(): array } /** + * Verify get url path. + * * @dataProvider getUrlPathDataProvider * @param string|null|bool $urlKey * @param string|null|bool $productName @@ -109,6 +121,8 @@ public function testGetUrlPath($urlKey, $productName, $formatterCalled, $result) } /** + * Verify get url key. + * * @param string|bool $productUrlKey * @param string|bool $expectedUrlKey * @return void @@ -122,6 +136,8 @@ public function testGetUrlKey($productUrlKey, $expectedUrlKey): void } /** + * Data provider for testGetUrlKey. + * * @return array */ public function getUrlKeyDataProvider(): array @@ -133,6 +149,8 @@ public function getUrlKeyDataProvider(): array } /** + * Verify get url path with default utl key. + * * @param string|null|bool $storedUrlKey * @param string|null|bool $productName * @param string $expectedUrlKey @@ -150,6 +168,8 @@ public function testGetUrlPathDefaultUrlKey($storedUrlKey, $productName, $expect } /** + * Data provider for testGetUrlPathDefaultUrlKey. + * * @return array */ public function getUrlPathDefaultUrlKeyDataProvider(): array @@ -161,6 +181,8 @@ public function getUrlPathDefaultUrlKeyDataProvider(): array } /** + * Verify get url path with category. + * * @return void */ public function testGetUrlPathWithCategory(): void @@ -177,6 +199,8 @@ public function testGetUrlPathWithCategory(): void } /** + * Verify get url path with suffix. + * * @return void */ public function testGetUrlPathWithSuffix(): void @@ -198,6 +222,8 @@ public function testGetUrlPathWithSuffix(): void } /** + * Verify get url path with suffix and category and store. + * * @return void */ public function testGetUrlPathWithSuffixAndCategoryAndStore(): void @@ -219,6 +245,8 @@ public function testGetUrlPathWithSuffixAndCategoryAndStore(): void } /** + * Verify get canonical url path. + * * @return void */ public function testGetCanonicalUrlPath(): void @@ -232,6 +260,8 @@ public function testGetCanonicalUrlPath(): void } /** + * Verify get canonical path with category. + * * @return void */ public function testGetCanonicalUrlPathWithCategory(): void diff --git a/app/code/Magento/CatalogUrlRewrite/Test/Unit/Observer/ProductProcessUrlRewriteRemovingObserverTest.php b/app/code/Magento/CatalogUrlRewrite/Test/Unit/Observer/ProductProcessUrlRewriteRemovingObserverTest.php new file mode 100644 index 0000000000000..7184f1b3cd275 --- /dev/null +++ b/app/code/Magento/CatalogUrlRewrite/Test/Unit/Observer/ProductProcessUrlRewriteRemovingObserverTest.php @@ -0,0 +1,148 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ + +declare(strict_types=1); + +namespace Magento\CatalogUrlRewrite\Test\Unit\Observer; + +use Magento\Catalog\Model\Product; +use Magento\CatalogUrlRewrite\Model\ProductUrlRewriteGenerator; +use Magento\CatalogUrlRewrite\Observer\ProductProcessUrlRewriteRemovingObserver; +use Magento\Framework\Event; +use Magento\Framework\Event\Observer; +use Magento\Framework\TestFramework\Unit\Helper\ObjectManager; +use Magento\UrlRewrite\Model\UrlPersistInterface; +use Magento\UrlRewrite\Service\V1\Data\UrlRewrite; +use PHPUnit\Framework\MockObject\MockObject; +use PHPUnit\Framework\TestCase; + +/** + * Unit test for Magento\CatalogUrlRewrite\Observer\ProductProcessUrlRewriteRemovingObserver + */ +class ProductProcessUrlRewriteRemovingObserverTest extends TestCase +{ + /* + * Stub product ID + */ + private const STUB_PRODUCT_ID = 333; + + /** + * Testable Object + * + * @var ProductProcessUrlRewriteRemovingObserver + */ + private $observer; + + /** + * @var ObjectManager + */ + private $objectManager; + + /** + * @var Observer|MockObject + */ + private $observerMock; + + /** + * @var Event|MockObject + */ + private $eventMock; + + /** + * @var Product|MockObject + */ + private $productMock; + + /** + * @var UrlPersistInterface|MockObject + */ + private $urlPersistMock; + + /** + * @inheritdoc + */ + protected function setUp(): void + { + $this->objectManager = new ObjectManager($this); + $this->observerMock = $this->createMock(Observer::class); + + $this->eventMock = $this->getMockBuilder(Event::class) + ->disableOriginalConstructor() + ->setMethods(['getProduct']) + ->getMock(); + + $this->productMock = $this->getMockBuilder(Product::class) + ->disableOriginalConstructor() + ->setMethods(['getId']) + ->getMock(); + + $this->urlPersistMock = $this->getMockBuilder(UrlPersistInterface::class) + ->disableOriginalConstructor() + ->getMock(); + + $this->observer = $this->objectManager->getObject( + ProductProcessUrlRewriteRemovingObserver::class, + [ + 'urlPersist' => $this->urlPersistMock + ] + ); + } + + /** + * Test for execute(), covers test case for removing product URLs from storage + */ + public function testRemoveProductUrlsFromStorage(): void + { + $this->observerMock + ->expects($this->once()) + ->method('getEvent') + ->willReturn($this->eventMock); + + $this->eventMock + ->expects($this->once()) + ->method('getProduct') + ->willReturn($this->productMock); + + $this->productMock + ->expects($this->exactly(2)) + ->method('getId') + ->willReturn(self::STUB_PRODUCT_ID); + + $this->urlPersistMock->expects($this->once()) + ->method('deleteByData') + ->with([ + UrlRewrite::ENTITY_ID => self::STUB_PRODUCT_ID, + UrlRewrite::ENTITY_TYPE => ProductUrlRewriteGenerator::ENTITY_TYPE, + ]); + + $this->observer->execute($this->observerMock); + } + + /** + * Test for execute(), covers test case for removing product URLs when the product doesn't have an ID + */ + public function testRemoveProductUrlsWithEmptyProductId() + { + $this->observerMock + ->expects($this->once()) + ->method('getEvent') + ->willReturn($this->eventMock); + + $this->eventMock + ->expects($this->once()) + ->method('getProduct') + ->willReturn($this->productMock); + + $this->productMock + ->expects($this->once()) + ->method('getId') + ->willReturn(null); + + $this->urlPersistMock->expects($this->never())->method('deleteByData'); + + $this->observer->execute($this->observerMock); + } +} diff --git a/app/code/Magento/CatalogUrlRewrite/etc/adminhtml/system.xml b/app/code/Magento/CatalogUrlRewrite/etc/adminhtml/system.xml index 1e1f4e86fa3dc..75d395473f969 100644 --- a/app/code/Magento/CatalogUrlRewrite/etc/adminhtml/system.xml +++ b/app/code/Magento/CatalogUrlRewrite/etc/adminhtml/system.xml @@ -28,13 +28,11 @@ <label>Create Permanent Redirect for URLs if URL Key Changed</label> <source_model>Magento\Config\Model\Config\Source\Yesno</source_model> </field> - <field id="generate_category_product_rewrites" translate="label" type="select" sortOrder="6" showInDefault="1" showInWebsite="0" showInStore="0" canRestore="1"> + <field id="generate_category_product_rewrites" translate="label comment" type="select" sortOrder="6" showInDefault="1" canRestore="1"> <label>Generate "category/product" URL Rewrites</label> <backend_model>Magento\CatalogUrlRewrite\Model\TableCleaner</backend_model> <source_model>Magento\Config\Model\Config\Source\Yesno</source_model> - <comment> - <![CDATA[<strong style="color:red">Warning!</strong> Turning this option off will result in permanent removal of category/product URL rewrites without an ability to restore them.]]> - </comment> + <comment><![CDATA[<strong style="color:red">Warning!</strong> Turning this option off will result in permanent removal of category/product URL rewrites without an ability to restore them.]]></comment> <frontend_class>generate_category_product_rewrites</frontend_class> </field> </group> diff --git a/app/code/Magento/CatalogUrlRewrite/i18n/en_US.csv b/app/code/Magento/CatalogUrlRewrite/i18n/en_US.csv index 0f21e8ddf9fc9..7f1e1cd086408 100644 --- a/app/code/Magento/CatalogUrlRewrite/i18n/en_US.csv +++ b/app/code/Magento/CatalogUrlRewrite/i18n/en_US.csv @@ -5,5 +5,8 @@ "Product URL Suffix","Product URL Suffix" "Use Categories Path for Product URLs","Use Categories Path for Product URLs" "Create Permanent Redirect for URLs if URL Key Changed","Create Permanent Redirect for URLs if URL Key Changed" -"Generate "category/product" URL Rewrites","Generate "category/product" URL Rewrites" +"Generate ""category/product"" URL Rewrites","Generate ""category/product"" URL Rewrites" "URL key ""%1"" matches a reserved endpoint name (%2). Use another URL key.","URL key ""%1"" matches a reserved endpoint name (%2). Use another URL key." +"Invalid URL key. The ""%1"" URL key can not be used to generate Latin URL key. Please use Latin letters and numbers to avoid generating URL key issues.","Invalid URL key. The ""%1"" URL key can not be used to generate Latin URL key. Please use Latin letters and numbers to avoid generating URL key issues." +"Invalid URL key. The ""%1"" category name can not be used to generate Latin URL key. Please add URL key or change category name using Latin letters and numbers to avoid generating URL key issues.","Invalid URL key. The ""%1"" category name can not be used to generate Latin URL key. Please add URL key or change category name using Latin letters and numbers to avoid generating URL key issues." +"<strong style=""color:red"">Warning!</strong> Turning this option off will result in permanent removal of category/product URL rewrites without an ability to restore them.","<strong style=""color:red"">Warning!</strong> Turning this option off will result in permanent removal of category/product URL rewrites without an ability to restore them." diff --git a/app/code/Magento/CatalogUrlRewrite/registration.php b/app/code/Magento/CatalogUrlRewrite/registration.php index 50a5544c738d0..f00aaf5bb5518 100644 --- a/app/code/Magento/CatalogUrlRewrite/registration.php +++ b/app/code/Magento/CatalogUrlRewrite/registration.php @@ -4,6 +4,6 @@ * See COPYING.txt for license details. */ -use \Magento\Framework\Component\ComponentRegistrar; +use Magento\Framework\Component\ComponentRegistrar; ComponentRegistrar::register(ComponentRegistrar::MODULE, 'Magento_CatalogUrlRewrite', __DIR__); diff --git a/app/code/Magento/CatalogWidget/Block/Product/ProductsList.php b/app/code/Magento/CatalogWidget/Block/Product/ProductsList.php index 9e47830debfc4..9934cc9ad106a 100644 --- a/app/code/Magento/CatalogWidget/Block/Product/ProductsList.php +++ b/app/code/Magento/CatalogWidget/Block/Product/ProductsList.php @@ -6,15 +6,31 @@ namespace Magento\CatalogWidget\Block\Product; +use Magento\Catalog\Api\CategoryRepositoryInterface; +use Magento\Catalog\Block\Product\AbstractProduct; +use Magento\Catalog\Block\Product\Context; +use Magento\Catalog\Block\Product\Widget\Html\Pager; use Magento\Catalog\Model\Product; -use Magento\Framework\App\ObjectManager; +use Magento\Catalog\Model\Product\Visibility; +use Magento\Catalog\Model\ResourceModel\Product\Collection; +use Magento\Catalog\Model\ResourceModel\Product\CollectionFactory; +use Magento\Catalog\Pricing\Price\FinalPrice; +use Magento\CatalogWidget\Model\Rule; use Magento\Framework\App\ActionInterface; +use Magento\Framework\App\Http\Context as HttpContext; +use Magento\Framework\App\ObjectManager; use Magento\Framework\DataObject\IdentityInterface; +use Magento\Framework\Exception\LocalizedException; +use Magento\Framework\Exception\NoSuchEntityException; use Magento\Framework\Pricing\PriceCurrencyInterface; use Magento\Framework\Serialize\Serializer\Json; +use Magento\Framework\Url\EncoderInterface; use Magento\Framework\View\LayoutFactory; +use Magento\Framework\View\LayoutInterface; +use Magento\Rule\Model\Condition\Combine; +use Magento\Rule\Model\Condition\Sql\Builder as SqlBuilder; use Magento\Widget\Block\BlockInterface; -use Magento\Framework\Url\EncoderInterface; +use Magento\Widget\Helper\Conditions; /** * Catalog Products List widget block @@ -22,7 +38,7 @@ * @SuppressWarnings(PHPMD.CouplingBetweenObjects) * @SuppressWarnings(PHPMD.ExcessiveParameterList) */ -class ProductsList extends \Magento\Catalog\Block\Product\AbstractProduct implements BlockInterface, IdentityInterface +class ProductsList extends AbstractProduct implements BlockInterface, IdentityInterface { /** * Default value for products count that will be shown @@ -32,7 +48,7 @@ class ProductsList extends \Magento\Catalog\Block\Product\AbstractProduct implem /** * Name of request parameter for page number value * - * @deprecated + * @deprecated @see $this->getData('page_var_name') */ const PAGE_VAR_NAME = 'np'; @@ -49,41 +65,41 @@ class ProductsList extends \Magento\Catalog\Block\Product\AbstractProduct implem /** * Instance of pager block * - * @var \Magento\Catalog\Block\Product\Widget\Html\Pager + * @var Pager */ protected $pager; /** - * @var \Magento\Framework\App\Http\Context + * @var HttpContext */ protected $httpContext; /** * Catalog product visibility * - * @var \Magento\Catalog\Model\Product\Visibility + * @var Visibility */ protected $catalogProductVisibility; /** * Product collection factory * - * @var \Magento\Catalog\Model\ResourceModel\Product\CollectionFactory + * @var CollectionFactory */ protected $productCollectionFactory; /** - * @var \Magento\Rule\Model\Condition\Sql\Builder + * @var SqlBuilder */ protected $sqlBuilder; /** - * @var \Magento\CatalogWidget\Model\Rule + * @var Rule */ protected $rule; /** - * @var \Magento\Widget\Helper\Conditions + * @var Conditions */ protected $conditionsHelper; @@ -105,42 +121,49 @@ class ProductsList extends \Magento\Catalog\Block\Product\AbstractProduct implem private $layoutFactory; /** - * @var \Magento\Framework\Url\EncoderInterface|null + * @var EncoderInterface|null */ private $urlEncoder; /** - * @var \Magento\Framework\View\Element\RendererList + * @var RendererList */ private $rendererListBlock; /** - * @param \Magento\Catalog\Block\Product\Context $context - * @param \Magento\Catalog\Model\ResourceModel\Product\CollectionFactory $productCollectionFactory - * @param \Magento\Catalog\Model\Product\Visibility $catalogProductVisibility - * @param \Magento\Framework\App\Http\Context $httpContext - * @param \Magento\Rule\Model\Condition\Sql\Builder $sqlBuilder - * @param \Magento\CatalogWidget\Model\Rule $rule - * @param \Magento\Widget\Helper\Conditions $conditionsHelper + * @var CategoryRepositoryInterface + */ + private $categoryRepository; + + /** + * @param Context $context + * @param CollectionFactory $productCollectionFactory + * @param Visibility $catalogProductVisibility + * @param HttpContext $httpContext + * @param SqlBuilder $sqlBuilder + * @param Rule $rule + * @param Conditions $conditionsHelper * @param array $data * @param Json|null $json * @param LayoutFactory|null $layoutFactory - * @param \Magento\Framework\Url\EncoderInterface|null $urlEncoder + * @param EncoderInterface|null $urlEncoder + * @param CategoryRepositoryInterface|null $categoryRepository * * @SuppressWarnings(PHPMD.ExcessiveParameterList) */ public function __construct( - \Magento\Catalog\Block\Product\Context $context, - \Magento\Catalog\Model\ResourceModel\Product\CollectionFactory $productCollectionFactory, - \Magento\Catalog\Model\Product\Visibility $catalogProductVisibility, - \Magento\Framework\App\Http\Context $httpContext, - \Magento\Rule\Model\Condition\Sql\Builder $sqlBuilder, - \Magento\CatalogWidget\Model\Rule $rule, - \Magento\Widget\Helper\Conditions $conditionsHelper, + Context $context, + CollectionFactory $productCollectionFactory, + Visibility $catalogProductVisibility, + HttpContext $httpContext, + SqlBuilder $sqlBuilder, + Rule $rule, + Conditions $conditionsHelper, array $data = [], Json $json = null, LayoutFactory $layoutFactory = null, - EncoderInterface $urlEncoder = null + EncoderInterface $urlEncoder = null, + CategoryRepositoryInterface $categoryRepository = null ) { $this->productCollectionFactory = $productCollectionFactory; $this->catalogProductVisibility = $catalogProductVisibility; @@ -151,6 +174,8 @@ public function __construct( $this->json = $json ?: ObjectManager::getInstance()->get(Json::class); $this->layoutFactory = $layoutFactory ?: ObjectManager::getInstance()->get(LayoutFactory::class); $this->urlEncoder = $urlEncoder ?: ObjectManager::getInstance()->get(EncoderInterface::class); + $this->categoryRepository = $categoryRepository ?? ObjectManager::getInstance() + ->get(CategoryRepositoryInterface::class); parent::__construct( $context, $data @@ -173,8 +198,10 @@ protected function _construct() $this->addData([ 'cache_lifetime' => 86400, - 'cache_tags' => [\Magento\Catalog\Model\Product::CACHE_TAG, - ], ]); + 'cache_tags' => [ + Product::CACHE_TAG, + ], + ]); } /** @@ -182,6 +209,7 @@ protected function _construct() * * @return array * @SuppressWarnings(PHPMD.RequestAwareBlockMethod) + * @throws NoSuchEntityException */ public function getCacheKeyInfo() { @@ -195,7 +223,7 @@ public function getCacheKeyInfo() $this->_storeManager->getStore()->getId(), $this->_design->getDesignTheme()->getId(), $this->httpContext->getValue(\Magento\Customer\Model\Context::CONTEXT_GROUP), - (int) $this->getRequest()->getParam($this->getData('page_var_name'), 1), + (int)$this->getRequest()->getParam($this->getData('page_var_name'), 1), $this->getProductsPerPage(), $this->getProductsCount(), $conditions, @@ -210,7 +238,7 @@ public function getCacheKeyInfo() * @SuppressWarnings(PHPMD.NPathComplexity) */ public function getProductPriceHtml( - \Magento\Catalog\Model\Product $product, + Product $product, $priceType = null, $renderZone = \Magento\Framework\Pricing\Render::ZONE_ITEM_LIST, array $arguments = [] @@ -239,7 +267,7 @@ public function getProductPriceHtml( } $price = $priceRender->render( - \Magento\Catalog\Pricing\Price\FinalPrice::PRICE_CODE, + FinalPrice::PRICE_CODE, $product, $arguments ); @@ -253,7 +281,7 @@ public function getProductPriceHtml( protected function getDetailsRendererList() { if (empty($this->rendererListBlock)) { - /** @var $layout \Magento\Framework\View\LayoutInterface */ + /** @var $layout LayoutInterface */ $layout = $this->layoutFactory->create(['cacheable' => false]); $layout->getUpdate()->addHandle('catalog_widget_product_list')->load(); $layout->generateXml(); @@ -294,12 +322,13 @@ protected function _beforeToHtml() /** * Prepare and return product collection * - * @return \Magento\Catalog\Model\ResourceModel\Product\Collection + * @return Collection * @SuppressWarnings(PHPMD.RequestAwareBlockMethod) + * @throws LocalizedException */ public function createCollection() { - /** @var $collection \Magento\Catalog\Model\ResourceModel\Product\Collection */ + /** @var $collection Collection */ $collection = $this->productCollectionFactory->create(); if ($this->getData('store_id') !== null) { @@ -327,10 +356,38 @@ public function createCollection() return $collection; } + /** + * Update conditions if the category is an anchor category + * + * @param array $condition + * @return array + */ + private function updateAnchorCategoryConditions(array $condition): array + { + if (array_key_exists('value', $condition)) { + $categoryId = $condition['value']; + + try { + $category = $this->categoryRepository->get($categoryId, $this->_storeManager->getStore()->getId()); + } catch (NoSuchEntityException $e) { + return $condition; + } + + $children = $category->getIsAnchor() ? $category->getChildren(true) : []; + if ($children) { + $children = explode(',', $children); + $condition['operator'] = "()"; + $condition['value'] = array_merge([$categoryId], $children); + } + } + + return $condition; + } + /** * Get conditions * - * @return \Magento\Rule\Model\Condition\Combine + * @return Combine */ protected function getConditions() { @@ -343,10 +400,14 @@ protected function getConditions() } foreach ($conditions as $key => $condition) { - if (!empty($condition['attribute']) - && in_array($condition['attribute'], ['special_from_date', 'special_to_date']) - ) { - $conditions[$key]['value'] = date('Y-m-d H:i:s', strtotime($condition['value'])); + if (!empty($condition['attribute'])) { + if (in_array($condition['attribute'], ['special_from_date', 'special_to_date'])) { + $conditions[$key]['value'] = date('Y-m-d H:i:s', strtotime($condition['value'])); + } + + if ($condition['attribute'] == 'category_ids') { + $conditions[$key] = $this->updateAnchorCategoryConditions($condition); + } } } @@ -412,13 +473,14 @@ protected function getPageSize() * Render pagination HTML * * @return string + * @throws LocalizedException */ public function getPagerHtml() { if ($this->showPager() && $this->getProductCollection()->getSize() > $this->getProductsPerPage()) { if (!$this->pager) { $this->pager = $this->getLayout()->createBlock( - \Magento\Catalog\Block\Product\Widget\Html\Pager::class, + Pager::class, $this->getWidgetPagerBlockName() ); @@ -444,16 +506,17 @@ public function getPagerHtml() */ public function getIdentities() { - $identities = []; + $identities = [[]]; if ($this->getProductCollection()) { foreach ($this->getProductCollection() as $product) { if ($product instanceof IdentityInterface) { - $identities = array_merge($identities, $product->getIdentities()); + $identities[] = $product->getIdentities(); } } } + $identities = array_merge(...$identities); - return $identities ?: [\Magento\Catalog\Model\Product::CACHE_TAG]; + return $identities ?: [Product::CACHE_TAG]; } /** @@ -475,7 +538,7 @@ public function getTitle() private function getPriceCurrency() { if ($this->priceCurrency === null) { - $this->priceCurrency = \Magento\Framework\App\ObjectManager::getInstance() + $this->priceCurrency = ObjectManager::getInstance() ->get(PriceCurrencyInterface::class); } return $this->priceCurrency; diff --git a/app/code/Magento/CatalogWidget/Model/Rule/Condition/Product.php b/app/code/Magento/CatalogWidget/Model/Rule/Condition/Product.php index a712ae91cbfa9..f9340a495de65 100644 --- a/app/code/Magento/CatalogWidget/Model/Rule/Condition/Product.php +++ b/app/code/Magento/CatalogWidget/Model/Rule/Condition/Product.php @@ -11,10 +11,12 @@ use Magento\Catalog\Api\Data\ProductInterface; use Magento\Catalog\Model\ProductCategoryList; +use Magento\Catalog\Model\ResourceModel\Product\Collection; use Magento\Store\Model\Store; /** - * Class Product + * Rule product condition data model + * * @SuppressWarnings(PHPMD.CouplingBetweenObjects) */ class Product extends \Magento\Rule\Model\Condition\Product\AbstractProduct @@ -121,46 +123,34 @@ protected function _addSpecialAttributes(array &$attributes) /** * Add condition to collection * - * @param \Magento\Catalog\Model\ResourceModel\Product\Collection $collection + * @param Collection $collection * @return $this */ public function addToCollection($collection) { $attribute = $this->getAttributeObject(); + $attributeCode = $attribute->getAttributeCode(); + if ($attributeCode !== 'price' || !$collection->getLimitationFilters()->isUsingPriceIndex()) { + if ($collection->isEnabledFlat()) { + if ($attribute->isEnabledInFlat()) { + $alias = array_keys($collection->getSelect()->getPart('from'))[0]; + $this->joinedAttributes[$attributeCode] = $alias . '.' . $attributeCode; + } else { + $alias = 'at_' . $attributeCode; + if (!in_array($alias, array_keys($collection->getSelect()->getPart('from')))) { + $collection->joinAttribute($attributeCode, "catalog_product/$attributeCode", 'entity_id'); + } - if ($collection->isEnabledFlat()) { - if ($attribute->isEnabledInFlat()) { - $alias = array_keys($collection->getSelect()->getPart('from'))[0]; - $this->joinedAttributes[$attribute->getAttributeCode()] = $alias . '.' . $attribute->getAttributeCode(); - } else { - $alias = 'at_' . $attribute->getAttributeCode(); - if (!in_array($alias, array_keys($collection->getSelect()->getPart('from')))) { - $collection->joinAttribute( - $attribute->getAttributeCode(), - 'catalog_product/'.$attribute->getAttributeCode(), - 'entity_id' - ); + $this->joinedAttributes[$attributeCode] = $alias . '.value'; } - - $this->joinedAttributes[$attribute->getAttributeCode()] = $alias . '.value'; + } elseif ($attributeCode !== 'category_ids' && !$attribute->isStatic()) { + $this->addAttributeToCollection($attribute, $collection); + $attributes = $this->getRule()->getCollectedAttributes(); + $attributes[$attributeCode] = true; + $this->getRule()->setCollectedAttributes($attributes); } - return $this; } - if ('category_ids' == $attribute->getAttributeCode() || $attribute->isStatic()) { - return $this; - } - - if ($attribute->getBackend() && $attribute->isScopeGlobal()) { - $this->addGlobalAttribute($attribute, $collection); - } else { - $this->addNotGlobalAttribute($attribute, $collection); - } - - $attributes = $this->getRule()->getCollectedAttributes(); - $attributes[$attribute->getAttributeCode()] = true; - $this->getRule()->setCollectedAttributes($attributes); - return $this; } @@ -168,12 +158,12 @@ public function addToCollection($collection) * Adds Attributes that belong to Global Scope * * @param \Magento\Catalog\Model\ResourceModel\Eav\Attribute $attribute - * @param \Magento\Catalog\Model\ResourceModel\Product\Collection $collection + * @param Collection $collection * @return $this */ protected function addGlobalAttribute( \Magento\Catalog\Model\ResourceModel\Eav\Attribute $attribute, - \Magento\Catalog\Model\ResourceModel\Product\Collection $collection + Collection $collection ) { switch ($attribute->getBackendType()) { case 'decimal': @@ -206,12 +196,12 @@ protected function addGlobalAttribute( * Adds Attributes that don't belong to Global Scope * * @param \Magento\Catalog\Model\ResourceModel\Eav\Attribute $attribute - * @param \Magento\Catalog\Model\ResourceModel\Product\Collection $collection + * @param Collection $collection * @return $this */ protected function addNotGlobalAttribute( \Magento\Catalog\Model\ResourceModel\Eav\Attribute $attribute, - \Magento\Catalog\Model\ResourceModel\Product\Collection $collection + Collection $collection ) { $storeId = $this->storeManager->getStore()->getId(); $values = $collection->getAllAttributeValues($attribute); @@ -250,10 +240,12 @@ protected function addNotGlobalAttribute( public function getMappedSqlField() { $result = ''; - if (in_array($this->getAttribute(), ['category_ids', 'sku'])) { + if (in_array($this->getAttribute(), ['category_ids', 'sku', 'attribute_set_id'])) { $result = parent::getMappedSqlField(); } elseif (isset($this->joinedAttributes[$this->getAttribute()])) { $result = $this->joinedAttributes[$this->getAttribute()]; + } elseif ($this->getAttribute() === 'price') { + $result = 'price_index.min_price'; } elseif ($this->getAttributeObject()->isStatic()) { $result = $this->getAttributeObject()->getAttributeCode(); } elseif ($this->getValueParsed()) { @@ -266,11 +258,27 @@ public function getMappedSqlField() /** * @inheritdoc * - * @param \Magento\Catalog\Model\ResourceModel\Product\Collection $productCollection + * @param Collection $productCollection * @return $this */ public function collectValidatedAttributes($productCollection) { return $this->addToCollection($productCollection); } + + /** + * Add attribute to collection based on scope + * + * @param \Magento\Catalog\Model\ResourceModel\Eav\Attribute $attribute + * @param Collection $collection + * @return void + */ + private function addAttributeToCollection($attribute, $collection): void + { + if ($attribute->getBackend() && $attribute->isScopeGlobal()) { + $this->addGlobalAttribute($attribute, $collection); + } else { + $this->addNotGlobalAttribute($attribute, $collection); + } + } } diff --git a/app/code/Magento/CatalogWidget/Test/Mftf/ActionGroup/AdminFillCatalogProductsListWidgetCategoryActionGroup.xml b/app/code/Magento/CatalogWidget/Test/Mftf/ActionGroup/AdminFillCatalogProductsListWidgetCategoryActionGroup.xml new file mode 100644 index 0000000000000..ecc5780da0e02 --- /dev/null +++ b/app/code/Magento/CatalogWidget/Test/Mftf/ActionGroup/AdminFillCatalogProductsListWidgetCategoryActionGroup.xml @@ -0,0 +1,33 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + /** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +--> + +<actionGroups xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/actionGroupSchema.xsd"> + <actionGroup name="AdminFillCatalogProductsListWidgetCategoryActionGroup"> + <annotations> + <description>Fill catalog products list widget category.</description> + </annotations> + + <arguments> + <argument name="categoryName" type="string" defaultValue="{{_defaultCategory.name}}"/> + </arguments> + + <waitForElementVisible selector="{{WidgetSection.AddParam}}" stepKey="waitForAddParamElement"/> + <click selector="{{WidgetSection.AddParam}}" stepKey="clickOnAddParamElement"/> + <waitForElementVisible selector="{{WidgetSection.ConditionsDropdown}}" stepKey="waitForConditionsDropdownVisible"/> + <selectOption selector="{{WidgetSection.ConditionsDropdown}}" userInput="Category" stepKey="selectCategoryAsCondition"/> + <waitForElementVisible selector="{{WidgetSection.RuleParam}}" stepKey="waitForRuleParamElementVisible"/> + <click selector="{{WidgetSection.RuleParam}}" stepKey="clickToAddRuleParam"/> + <click selector="{{WidgetSection.Chooser}}" stepKey="clickToSelectFromList"/> + <waitForPageLoad stepKey="waitForPageLoadAfterSelectingRuleParam"/> + <waitForElementVisible selector="{{WidgetSection.PreCreateCategory(categoryName)}}" stepKey="waitForCategoryElementVisible"/> + <click selector="{{WidgetSection.PreCreateCategory(categoryName)}}" stepKey="selectCategoryFromArguments"/> + <click selector="{{AdminNewWidgetSection.applyParameter}}" stepKey="clickApplyButton"/> + <waitForElementNotVisible selector="{{InsertWidgetSection.categoryTreeWrapper}}" stepKey="waitForCategoryTreeIsNotVisible"/> + </actionGroup> +</actionGroups> diff --git a/app/code/Magento/CatalogWidget/Test/Mftf/Section/CatalogWidgetSection.xml b/app/code/Magento/CatalogWidget/Test/Mftf/Section/CatalogWidgetSection.xml deleted file mode 100644 index 855d325c9850c..0000000000000 --- a/app/code/Magento/CatalogWidget/Test/Mftf/Section/CatalogWidgetSection.xml +++ /dev/null @@ -1,26 +0,0 @@ -<?xml version="1.0" encoding="utf-8"?> -<!-- - /** - * Copyright © Magento, Inc. All rights reserved. - * See COPYING.txt for license details. - */ ---> -<sections xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="urn:magento:mftf:Page/etc/SectionObject.xsd"> - <section name="CatalogWidgetSection"> - <element name="insertWidgetButton" type="button" selector=".scalable.action-add-widget.plugin"/> - <element name="storeViewOption" type="button" selector="//*[@name='store_id']"/> - </section> - - <section name="InsertWidgetSection"> - <element name="widgetTypeDropDown" type="select" selector="#select_widget_type"/> - <element name="conditionsAddButton" type="button" selector=".rule-param.rule-param-new-child"/> - <element name="conditionsSelectBox" type="button" selector="#conditions__1__new_child"/> - <element name="addCondition" type="button" selector="//*[@id='conditions__1--{{arg1}}__value']/../preceding-sibling::a" parameterized="true"/> - <element name="conditionField" type="button" selector="#conditions__1--{{arg2}}__value" parameterized="true"/> - <element name="save" type="button" selector="#save-button"/> - <element name="conditionIs" type="button" selector="//*[@id='conditions__1--1__attribute']/following-sibling::span[1]"/> - <element name="conditionOperator" type="button" selector="#conditions__1--{{arg3}}__operator" parameterized="true"/> - <element name="checkElementStorefrontByPrice" type="button" selector="//*[@class='product-items widget-product-grid']//*[contains(text(),'${{arg4}}.00')]" parameterized="true"/> - <element name="checkElementStorefrontByName" type="button" selector="//*[@class='product-items widget-product-grid']//*[@class='product-item'][{{productPosition}}]//a[contains(text(), '{{productName}}')]" parameterized="true"/> - </section> -</sections> diff --git a/app/code/Magento/CatalogWidget/Test/Mftf/Section/CatalogWidgetSection/CatalogWidgetSection.xml b/app/code/Magento/CatalogWidget/Test/Mftf/Section/CatalogWidgetSection/CatalogWidgetSection.xml new file mode 100644 index 0000000000000..b33fe7b48d2b1 --- /dev/null +++ b/app/code/Magento/CatalogWidget/Test/Mftf/Section/CatalogWidgetSection/CatalogWidgetSection.xml @@ -0,0 +1,14 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + /** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +--> +<sections xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Page/etc/SectionObject.xsd"> + <section name="CatalogWidgetSection"> + <element name="insertWidgetButton" type="button" selector=".scalable.action-add-widget.plugin"/> + <element name="storeViewOption" type="button" selector="//*[@name='store_id']"/> + </section> +</sections> diff --git a/app/code/Magento/CatalogWidget/Test/Mftf/Section/CatalogWidgetSection/InsertWidgetSection.xml b/app/code/Magento/CatalogWidget/Test/Mftf/Section/CatalogWidgetSection/InsertWidgetSection.xml new file mode 100644 index 0000000000000..9b40971611d6f --- /dev/null +++ b/app/code/Magento/CatalogWidget/Test/Mftf/Section/CatalogWidgetSection/InsertWidgetSection.xml @@ -0,0 +1,23 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + /** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +--> +<sections xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Page/etc/SectionObject.xsd"> + <section name="InsertWidgetSection"> + <element name="widgetTypeDropDown" type="select" selector="#select_widget_type"/> + <element name="conditionsAddButton" type="button" selector=".rule-param.rule-param-new-child"/> + <element name="conditionsSelectBox" type="button" selector="#conditions__1__new_child"/> + <element name="addCondition" type="button" selector="//*[@id='conditions__1--{{arg1}}__value']/../preceding-sibling::a" parameterized="true"/> + <element name="conditionField" type="button" selector="#conditions__1--{{arg2}}__value" parameterized="true"/> + <element name="save" type="button" selector="#save-button"/> + <element name="conditionIs" type="button" selector="//*[@id='conditions__1--1__attribute']/following-sibling::span[1]"/> + <element name="conditionOperator" type="button" selector="#conditions__1--{{arg3}}__operator" parameterized="true"/> + <element name="checkElementStorefrontByPrice" type="button" selector="//*[@class='product-items widget-product-grid']//*[contains(text(),'${{arg4}}.00')]" parameterized="true"/> + <element name="checkElementStorefrontByName" type="button" selector="//*[@class='product-items widget-product-grid']//*[@class='product-item'][{{productPosition}}]//a[contains(text(), '{{productName}}')]" parameterized="true"/> + <element name="categoryTreeWrapper" type="text" selector=".rule-chooser .tree.x-tree"/> + </section> +</sections> diff --git a/app/code/Magento/CatalogWidget/Test/Mftf/Test/CatalogProductListWidgetOperatorsTest.xml b/app/code/Magento/CatalogWidget/Test/Mftf/Test/CatalogProductListWidgetOperatorsTest.xml index 13ffedb1f707b..288134a889db0 100644 --- a/app/code/Magento/CatalogWidget/Test/Mftf/Test/CatalogProductListWidgetOperatorsTest.xml +++ b/app/code/Magento/CatalogWidget/Test/Mftf/Test/CatalogProductListWidgetOperatorsTest.xml @@ -37,7 +37,7 @@ <createData entity="_defaultBlock" stepKey="createPreReqBlock"/> <!--User log in on back-end as admin--> - <actionGroup ref="LoginAsAdmin" stepKey="loginAsAdmin"/> + <actionGroup ref="AdminLoginActionGroup" stepKey="loginAsAdmin"/> <actionGroup ref="EnabledWYSIWYGActionGroup" stepKey="enableWYSIWYG"/> </before> @@ -151,7 +151,7 @@ <deleteData createDataKey="createFirstProduct" stepKey="deleteFirstProduct"/> <deleteData createDataKey="createSecondProduct" stepKey="deleteSecondProduct"/> <deleteData createDataKey="createThirdProduct" stepKey="deleteThirdProduct"/> - <actionGroup ref="logout" stepKey="logout"/> + <actionGroup ref="AdminLogoutActionGroup" stepKey="logout"/> </after> </test> </tests> diff --git a/app/code/Magento/CatalogWidget/Test/Mftf/Test/CatalogProductListWidgetOrderTest.xml b/app/code/Magento/CatalogWidget/Test/Mftf/Test/CatalogProductListWidgetOrderTest.xml index f0af04478bc28..fd87d58e47125 100644 --- a/app/code/Magento/CatalogWidget/Test/Mftf/Test/CatalogProductListWidgetOrderTest.xml +++ b/app/code/Magento/CatalogWidget/Test/Mftf/Test/CatalogProductListWidgetOrderTest.xml @@ -37,7 +37,7 @@ <field key="price">30</field> </createData> <createData entity="_defaultCmsPage" stepKey="createPreReqPage"/> - <actionGroup ref="LoginAsAdmin" stepKey="loginAsAdmin"/> + <actionGroup ref="AdminLoginActionGroup" stepKey="loginAsAdmin"/> <actionGroup ref="EnabledWYSIWYGActionGroup" stepKey="enableWYSIWYG"/> </before> <!--Open created cms page--> @@ -82,7 +82,7 @@ <deleteData createDataKey="createFirstProduct" stepKey="deleteFirstProduct"/> <deleteData createDataKey="createSecondProduct" stepKey="deleteSecondProduct"/> <deleteData createDataKey="createThirdProduct" stepKey="deleteThirdProduct"/> - <actionGroup ref="logout" stepKey="logout"/> + <actionGroup ref="AdminLogoutActionGroup" stepKey="logout"/> </after> </test> </tests> diff --git a/app/code/Magento/CatalogWidget/Test/Mftf/Test/StorefrontProductGridUIUpdatesOnDesktopTest.xml b/app/code/Magento/CatalogWidget/Test/Mftf/Test/StorefrontProductGridUIUpdatesOnDesktopTest.xml new file mode 100644 index 0000000000000..5590aa1cdcefa --- /dev/null +++ b/app/code/Magento/CatalogWidget/Test/Mftf/Test/StorefrontProductGridUIUpdatesOnDesktopTest.xml @@ -0,0 +1,93 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +--> + +<tests xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/testSchema.xsd"> + <test name="StorefrontProductGridUIUpdatesOnDesktopTest"> + <annotations> + <features value="Catalog"/> + <stories value="New products list widget"/> + <title value="Storefront product grid UI updates on Desktop"/> + <description value="Storefront product grid UI updates on Desktop"/> + <severity value="MAJOR"/> + <testCaseId value="MC-27569"/> + <group value="catalog"/> + <group value="WYSIWYGDisabled"/> + </annotations> + <before> + <!-- 1. Create multiple products and assign to a category. --> + <createData entity="ApiCategory" stepKey="createCategory"/> + <createData entity="ApiSimpleProduct" stepKey="createFirstSimpleProduct"> + <requiredEntity createDataKey="createCategory"/> + </createData> + <createData entity="ApiSimpleProduct" stepKey="createSecondSimpleProduct"> + <requiredEntity createDataKey="createCategory"/> + </createData> + <createData entity="ApiSimpleProduct" stepKey="createThirdSimpleProduct"> + <requiredEntity createDataKey="createCategory"/> + </createData> + <createData entity="ApiSimpleProduct" stepKey="createFourthSimpleProduct"> + <requiredEntity createDataKey="createCategory"/> + </createData> + <!-- 2. Create new CMS page and add "Catalog Product List" widget type via content >insert widget option --> + <createData entity="_emptyCmsPage" stepKey="createCmsPage"/> + <actionGroup ref="AdminLoginActionGroup" stepKey="loginToAdminPanel"/> + <actionGroup ref="NavigateToCreatedCMSPageActionGroup" stepKey="navigateToCreatedCmsPage"> + <argument name="CMSPage" value="$createCmsPage$"/> + </actionGroup> + <actionGroup ref="AdminInsertWidgetToCmsPageContentActionGroup" stepKey="insertWidgetToCmsPageContentActionGroup"> + <argument name="widgetType" value="Catalog Products List"/> + </actionGroup> + <actionGroup ref="AdminFillCatalogProductsListWidgetCategoryActionGroup" stepKey="fillCatalogProductsListWidgetOptions"> + <argument name="categoryName" value="$createCategory.name$"/> + </actionGroup> + <actionGroup ref="AdminClickInsertWidgetActionGroup" stepKey="clickInsertWidgetButton"/> + <actionGroup ref="SaveCmsPageActionGroup" stepKey="clickSaveButton"/> + </before> + <after> + <deleteData createDataKey="createFirstSimpleProduct" stepKey="deleteFirstSimpleProduct"/> + <deleteData createDataKey="createSecondSimpleProduct" stepKey="deleteSecondSimpleProduct"/> + <deleteData createDataKey="createThirdSimpleProduct" stepKey="deleteThirdSimpleProduct"/> + <deleteData createDataKey="createFourthSimpleProduct" stepKey="deleteFourthSimpleProduct"/> + <deleteData createDataKey="createCategory" stepKey="deleteCategory"/> + <deleteData createDataKey="createCmsPage" stepKey="deleteCmsPage"/> + <actionGroup ref="AdminLogoutActionGroup" stepKey="logoutFromAdmin"/> + </after> + <actionGroup ref="StorefrontGoToCMSPageActionGroup" stepKey="navigateAndOpenCreatedCmsPage"> + <argument name="identifier" value="$createCmsPage.identifier$"/> + </actionGroup> + <actionGroup ref="AssertStorefrontProductControlsAreNotVisibleWithoutHoverActionGroup" stepKey="assertProductControlsAreNotVisibleWithoutHoverOnCmsPage"/> + <seeElement selector="{{StorefrontCategoryMainSection.productLinkByHref($createFirstSimpleProduct.custom_attributes[url_key]$)}}" stepKey="assertProductName"/> + <seeElement selector="{{StorefrontCategoryMainSection.productPriceByName($createFirstSimpleProduct.name$)}}" stepKey="assertProductPrice"/> + <moveMouseOver selector="{{StorefrontCategoryProductSection.ProductInfoByName($createFirstSimpleProduct.name$)}}" stepKey="hoverProduct"/> + <actionGroup ref="AssertStorefrontProductControlsAreVisibleOnHoverActionGroup" stepKey="assertProductControlsAreVisibleOnHoverOnCmsPage"/> + <actionGroup ref="AssertStorefrontAddToWishListIconIsClickableForGuestUserActionGroup" stepKey="assertAddToWishListIconIsClickable"/> + <actionGroup ref="StorefrontGoToCMSPageActionGroup" stepKey="navigateAndOpenCreatedCmsPageToVerifyAddToCompareIconIsClickable"> + <argument name="identifier" value="$createCmsPage.identifier$"/> + </actionGroup> + <actionGroup ref="StorefrontAddCategoryProductToCompareActionGroup" stepKey="assertAddToCompareIconIsClickable"> + <argument name="productVar" value="$createFirstSimpleProduct$"/> + </actionGroup> + <actionGroup ref="StorefrontNavigateCategoryPageActionGroup" stepKey="navigateCategoryCreatedInPreconditions"> + <argument name="category" value="$createCategory$"/> + </actionGroup> + <actionGroup ref="AssertStorefrontProductControlsAreNotVisibleWithoutHoverActionGroup" stepKey="assertProductControlsAreNotVisibleWithoutHoverOnCategoryPage"/> + <seeElement selector="{{StorefrontCategoryMainSection.productLinkByHref($createFirstSimpleProduct.custom_attributes[url_key]$)}}" stepKey="assertProductNameOnCategoryPage"/> + <seeElement selector="{{StorefrontCategoryMainSection.productPriceByName($createFirstSimpleProduct.name$)}}" stepKey="assertProductPriceOnCategoryPage"/> + <moveMouseOver selector="{{StorefrontCategoryProductSection.ProductInfoByName($createFirstSimpleProduct.name$)}}" stepKey="hoverProductOnCategoryPage"/> + <actionGroup ref="AssertStorefrontProductControlsAreVisibleOnHoverActionGroup" stepKey="assertProductControlsAreVisibleOnHoverOnCategoryPage"/> + <actionGroup ref="AssertStorefrontAddToWishListIconIsClickableForGuestUserActionGroup" stepKey="assertAddToWishListIconIsClickableOnCategoryPage"/> + <actionGroup ref="StorefrontNavigateCategoryPageActionGroup" stepKey="navigateCategoryCreatedInPreconditionsToVerifyAddToCompareIconIsClickable"> + <argument name="category" value="$createCategory$"/> + </actionGroup> + <actionGroup ref="StorefrontAddCategoryProductToCompareActionGroup" stepKey="assertAddToCompareIconIsClickableOnCategoryPage"> + <argument name="productVar" value="$createFirstSimpleProduct$"/> + </actionGroup> + <actionGroup ref="StorefrontClearCompareActionGroup" stepKey="clearAllCompareProducts"/> + </test> +</tests> diff --git a/app/code/Magento/CatalogWidget/Test/Unit/Model/Rule/Condition/ProductTest.php b/app/code/Magento/CatalogWidget/Test/Unit/Model/Rule/Condition/ProductTest.php index 219cae6829299..7dceb41d263ec 100644 --- a/app/code/Magento/CatalogWidget/Test/Unit/Model/Rule/Condition/ProductTest.php +++ b/app/code/Magento/CatalogWidget/Test/Unit/Model/Rule/Condition/ProductTest.php @@ -5,53 +5,65 @@ */ namespace Magento\CatalogWidget\Test\Unit\Model\Rule\Condition; +use Magento\Catalog\Model\ProductCategoryList; +use Magento\Catalog\Model\ResourceModel\Eav\Attribute; +use Magento\Catalog\Model\ResourceModel\Product; +use Magento\Catalog\Model\ResourceModel\Product\Collection; +use Magento\CatalogWidget\Model\Rule\Condition\Product as ProductWidget; +use Magento\Eav\Model\Config; +use Magento\Eav\Model\Entity\AbstractEntity; +use Magento\Framework\DB\Adapter\AdapterInterface; +use Magento\Framework\DB\Select; use Magento\Framework\TestFramework\Unit\Helper\ObjectManager; +use Magento\SalesRule\Model\Rule; +use Magento\Store\Api\Data\StoreInterface; +use Magento\Store\Model\StoreManagerInterface; +use PHPUnit\Framework\MockObject\MockObject; +use PHPUnit\Framework\TestCase; /** * @SuppressWarnings(PHPMD.CouplingBetweenObjects) */ -class ProductTest extends \PHPUnit\Framework\TestCase +class ProductTest extends TestCase { /** - * @var \Magento\CatalogWidget\Model\Rule\Condition\Product + * @var ProductWidget */ private $model; /** - * @var \Magento\Catalog\Model\ResourceModel\Product|\PHPUnit_Framework_MockObject_MockObject + * @var MockObject */ - private $productResource; + private $attributeMock; /** - * @var \PHPUnit_Framework_MockObject_MockObject + * @var Product|MockObject */ - private $attributeMock; + private $productResource; /** * @inheritdoc - * - * @return void */ protected function setUp() { $objectManagerHelper = new ObjectManager($this); - $eavConfig = $this->createMock(\Magento\Eav\Model\Config::class); - $this->attributeMock = $this->createMock(\Magento\Catalog\Model\ResourceModel\Eav\Attribute::class); + $eavConfig = $this->createMock(Config::class); + $this->attributeMock = $this->createMock(Attribute::class); $eavConfig->expects($this->any())->method('getAttribute')->willReturn($this->attributeMock); - $ruleMock = $this->createMock(\Magento\SalesRule\Model\Rule::class); - $storeManager = $this->createMock(\Magento\Store\Model\StoreManagerInterface::class); - $storeMock = $this->createMock(\Magento\Store\Api\Data\StoreInterface::class); + $ruleMock = $this->createMock(Rule::class); + $storeManager = $this->createMock(StoreManagerInterface::class); + $storeMock = $this->createMock(StoreInterface::class); $storeManager->expects($this->any())->method('getStore')->willReturn($storeMock); - $this->productResource = $this->createMock(\Magento\Catalog\Model\ResourceModel\Product::class); + $this->productResource = $this->createMock(Product::class); $this->productResource->expects($this->once())->method('loadAllAttributes')->willReturnSelf(); $this->productResource->expects($this->once())->method('getAttributesByCode')->willReturn([]); - $productCategoryList = $this->getMockBuilder(\Magento\Catalog\Model\ProductCategoryList::class) + $productCategoryList = $this->getMockBuilder(ProductCategoryList::class) ->disableOriginalConstructor() ->getMock(); $this->model = $objectManagerHelper->getObject( - \Magento\CatalogWidget\Model\Rule\Condition\Product::class, + ProductWidget::class, [ 'config' => $eavConfig, 'storeManager' => $storeManager, @@ -72,8 +84,8 @@ protected function setUp() */ public function testAddToCollection() { - $collectionMock = $this->createMock(\Magento\Catalog\Model\ResourceModel\Product\Collection::class); - $selectMock = $this->createMock(\Magento\Framework\DB\Select::class); + $collectionMock = $this->createMock(Collection::class); + $selectMock = $this->createMock(Select::class); $collectionMock->expects($this->once())->method('getSelect')->willReturn($selectMock); $selectMock->expects($this->any())->method('join')->willReturnSelf(); $this->attributeMock->expects($this->any())->method('getAttributeCode')->willReturn('code'); @@ -83,10 +95,10 @@ public function testAddToCollection() $this->attributeMock->expects($this->once())->method('isScopeGlobal')->willReturn(true); $this->attributeMock->expects($this->once())->method('getBackendType')->willReturn('multiselect'); - $entityMock = $this->createMock(\Magento\Eav\Model\Entity\AbstractEntity::class); + $entityMock = $this->createMock(AbstractEntity::class); $entityMock->expects($this->once())->method('getLinkField')->willReturn('entitiy_id'); $this->attributeMock->expects($this->once())->method('getEntity')->willReturn($entityMock); - $connection = $this->createMock(\Magento\Framework\DB\Adapter\AdapterInterface::class); + $connection = $this->createMock(AdapterInterface::class); $this->productResource->expects($this->atLeastOnce())->method('getConnection')->willReturn($connection); @@ -102,5 +114,7 @@ public function testGetMappedSqlFieldSku() { $this->model->setAttribute('sku'); $this->assertEquals('e.sku', $this->model->getMappedSqlField()); + $this->model->setAttribute('attribute_set_id'); + $this->assertEquals('e.attribute_set_id', $this->model->getMappedSqlField()); } } diff --git a/app/code/Magento/CatalogWidget/registration.php b/app/code/Magento/CatalogWidget/registration.php index f9e6a76047ad9..09cd043c70c76 100644 --- a/app/code/Magento/CatalogWidget/registration.php +++ b/app/code/Magento/CatalogWidget/registration.php @@ -4,6 +4,6 @@ * See COPYING.txt for license details. */ -use \Magento\Framework\Component\ComponentRegistrar; +use Magento\Framework\Component\ComponentRegistrar; ComponentRegistrar::register(ComponentRegistrar::MODULE, 'Magento_CatalogWidget', __DIR__); diff --git a/app/code/Magento/CatalogWidget/view/frontend/layout/catalog_widget_product_list.xml b/app/code/Magento/CatalogWidget/view/frontend/layout/catalog_widget_product_list.xml index db44d8b62dc1a..4fe7af7f34683 100644 --- a/app/code/Magento/CatalogWidget/view/frontend/layout/catalog_widget_product_list.xml +++ b/app/code/Magento/CatalogWidget/view/frontend/layout/catalog_widget_product_list.xml @@ -1,17 +1,14 @@ +<?xml version="1.0"?> <!-- - ~ Copyright © Magento, Inc. All rights reserved. - ~ See COPYING.txt for license details. - --> - -<!-- - ~ Copyright © Magento, Inc. All rights reserved. - ~ See COPYING.txt for license details. - --> - +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +--> <page xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="urn:magento:framework:View/Layout/etc/page_configuration.xsd"> <body> <block class="Magento\Framework\View\Element\RendererList" name="category.product.type.widget.details.renderers"> <block class="Magento\Framework\View\Element\Template" name="category.product.type.details.renderers.default" as="default"/> </block> </body> -</page> \ No newline at end of file +</page> diff --git a/app/code/Magento/Checkout/Block/Cart/Shipping.php b/app/code/Magento/Checkout/Block/Cart/Shipping.php index c52b7fe18814f..712ee84afd232 100644 --- a/app/code/Magento/Checkout/Block/Cart/Shipping.php +++ b/app/code/Magento/Checkout/Block/Cart/Shipping.php @@ -3,54 +3,73 @@ * Copyright © Magento, Inc. All rights reserved. * See COPYING.txt for license details. */ + namespace Magento\Checkout\Block\Cart; +use Magento\Checkout\Model\CompositeConfigProvider; +use Magento\Checkout\Block\Checkout\LayoutProcessorInterface; +use Magento\Framework\Serialize\Serializer\Json; +use Magento\Framework\Serialize\Serializer\JsonHexTag; +use Magento\Framework\View\Element\Template\Context; +use Magento\Customer\Model\Session as CustomerSession; +use Magento\Checkout\Model\Session as CheckoutSession; +use Magento\Framework\App\ObjectManager; + /** + * Cart Shipping Block + * * @api * @SuppressWarnings(PHPMD.CouplingBetweenObjects) */ class Shipping extends \Magento\Checkout\Block\Cart\AbstractCart { /** - * @var \Magento\Checkout\Model\CompositeConfigProvider + * @var CompositeConfigProvider */ protected $configProvider; /** - * @var array|\Magento\Checkout\Block\Checkout\LayoutProcessorInterface[] + * @var array|LayoutProcessorInterface[] */ protected $layoutProcessors; /** - * @var \Magento\Framework\Serialize\Serializer\Json + * @var Json */ private $serializer; /** - * @param \Magento\Framework\View\Element\Template\Context $context - * @param \Magento\Customer\Model\Session $customerSession - * @param \Magento\Checkout\Model\Session $checkoutSession - * @param \Magento\Checkout\Model\CompositeConfigProvider $configProvider + * @var JsonHexTag + */ + private $jsonHexTagSerializer; + + /** + * @param Context $context + * @param CustomerSession $customerSession + * @param CheckoutSession $checkoutSession + * @param CompositeConfigProvider $configProvider * @param array $layoutProcessors * @param array $data - * @param \Magento\Framework\Serialize\Serializer\Json|null $serializer + * @param Json|null $serializer + * @param JsonHexTag|null $jsonHexTagSerializer * @throws \RuntimeException */ public function __construct( - \Magento\Framework\View\Element\Template\Context $context, - \Magento\Customer\Model\Session $customerSession, - \Magento\Checkout\Model\Session $checkoutSession, - \Magento\Checkout\Model\CompositeConfigProvider $configProvider, + Context $context, + CustomerSession $customerSession, + CheckoutSession $checkoutSession, + CompositeConfigProvider $configProvider, array $layoutProcessors = [], array $data = [], - \Magento\Framework\Serialize\Serializer\Json $serializer = null + Json $serializer = null, + JsonHexTag $jsonHexTagSerializer = null ) { $this->configProvider = $configProvider; $this->layoutProcessors = $layoutProcessors; parent::__construct($context, $customerSession, $checkoutSession, $data); $this->_isScopePrivate = true; - $this->serializer = $serializer ?: \Magento\Framework\App\ObjectManager::getInstance() - ->get(\Magento\Framework\Serialize\Serializer\Json::class); + $this->serializer = $serializer ?: ObjectManager::getInstance()->get(Json::class); + $this->jsonHexTagSerializer = $jsonHexTagSerializer ?: ObjectManager::getInstance()->get(JsonHexTag::class); } /** @@ -75,7 +94,7 @@ public function getJsLayout() $this->jsLayout = $processor->process($this->jsLayout); } - return json_encode($this->jsLayout, JSON_HEX_TAG); + return $this->jsonHexTagSerializer->serialize($this->jsLayout); } /** @@ -90,11 +109,13 @@ public function getBaseUrl() } /** + * Get Serialized Checkout Config + * * @return bool|string * @since 100.2.0 */ public function getSerializedCheckoutConfig() { - return json_encode($this->getCheckoutConfig(), JSON_HEX_TAG); + return $this->jsonHexTagSerializer->serialize($this->getCheckoutConfig()); } } diff --git a/app/code/Magento/Checkout/Block/Cart/Sidebar.php b/app/code/Magento/Checkout/Block/Cart/Sidebar.php index c5e309df3cad6..147782e501ae4 100644 --- a/app/code/Magento/Checkout/Block/Cart/Sidebar.php +++ b/app/code/Magento/Checkout/Block/Cart/Sidebar.php @@ -83,7 +83,8 @@ public function getConfig() 'minicartMaxItemsVisible' => $this->getMiniCartMaxItemsCount(), 'websiteId' => $this->_storeManager->getStore()->getWebsiteId(), 'maxItemsToDisplay' => $this->getMaxItemsToDisplay(), - 'storeId' => $this->_storeManager->getStore()->getId() + 'storeId' => $this->_storeManager->getStore()->getId(), + 'storeGroupId' => $this->_storeManager->getStore()->getStoreGroupId() ]; } diff --git a/app/code/Magento/Checkout/Block/Checkout/AttributeMerger.php b/app/code/Magento/Checkout/Block/Checkout/AttributeMerger.php index 5dedf2c7e7eba..1dd1131cde1f1 100644 --- a/app/code/Magento/Checkout/Block/Checkout/AttributeMerger.php +++ b/app/code/Magento/Checkout/Block/Checkout/AttributeMerger.php @@ -289,6 +289,7 @@ protected function getMultilineFieldConfig($attributeCode, array $attributeConfi 'dataScope' => $lineIndex, 'provider' => $providerName, 'validation' => $isFirstLine + //phpcs:ignore Magento2.Performance.ForeachArrayMerge ? array_merge( ['required-entry' => (bool)$attributeConfig['required']], $attributeConfig['validation'] @@ -381,14 +382,14 @@ protected function getCustomer(): ?CustomerInterface /** * Retrieve field options from attribute configuration * - * @param string $attributeCode + * @param mixed $attributeCode * @param array $attributeConfig * @return array * @SuppressWarnings(PHPMD.UnusedFormalParameter) */ protected function getFieldOptions($attributeCode, array $attributeConfig) { - return isset($attributeConfig['options']) ? $attributeConfig['options'] : []; + return $attributeConfig['options'] ?? []; } /** diff --git a/app/code/Magento/Checkout/Block/Checkout/LayoutProcessor.php b/app/code/Magento/Checkout/Block/Checkout/LayoutProcessor.php index 557f143352446..00cc06ea0ff47 100644 --- a/app/code/Magento/Checkout/Block/Checkout/LayoutProcessor.php +++ b/app/code/Magento/Checkout/Block/Checkout/LayoutProcessor.php @@ -3,24 +3,30 @@ * Copyright © Magento, Inc. All rights reserved. * See COPYING.txt for license details. */ +declare(strict_types=1); + namespace Magento\Checkout\Block\Checkout; use Magento\Checkout\Helper\Data; -use Magento\Framework\App\ObjectManager; +use Magento\Customer\Model\AttributeMetadataDataProvider; +use Magento\Customer\Model\Options; +use Magento\Eav\Api\Data\AttributeInterface; +use Magento\Shipping\Model\Config; use Magento\Store\Model\StoreManagerInterface; +use Magento\Ui\Component\Form\AttributeMapper; /** - * Class LayoutProcessor + * Checkout Layout Processor */ -class LayoutProcessor implements \Magento\Checkout\Block\Checkout\LayoutProcessorInterface +class LayoutProcessor implements LayoutProcessorInterface { /** - * @var \Magento\Customer\Model\AttributeMetadataDataProvider + * @var AttributeMetadataDataProvider */ private $attributeMetadataDataProvider; /** - * @var \Magento\Ui\Component\Form\AttributeMapper + * @var AttributeMapper */ protected $attributeMapper; @@ -30,7 +36,7 @@ class LayoutProcessor implements \Magento\Checkout\Block\Checkout\LayoutProcesso protected $merger; /** - * @var \Magento\Customer\Model\Options + * @var Options */ private $options; @@ -45,39 +51,35 @@ class LayoutProcessor implements \Magento\Checkout\Block\Checkout\LayoutProcesso private $storeManager; /** - * @var \Magento\Shipping\Model\Config + * @var Config */ private $shippingConfig; /** - * @param \Magento\Customer\Model\AttributeMetadataDataProvider $attributeMetadataDataProvider - * @param \Magento\Ui\Component\Form\AttributeMapper $attributeMapper + * @param AttributeMetadataDataProvider $attributeMetadataDataProvider + * @param AttributeMapper $attributeMapper * @param AttributeMerger $merger - * @param \Magento\Customer\Model\Options|null $options - * @param Data|null $checkoutDataHelper - * @param \Magento\Shipping\Model\Config|null $shippingConfig - * @param StoreManagerInterface|null $storeManager + * @param Options $options + * @param Data $checkoutDataHelper + * @param Config $shippingConfig + * @param StoreManagerInterface $storeManager */ public function __construct( - \Magento\Customer\Model\AttributeMetadataDataProvider $attributeMetadataDataProvider, - \Magento\Ui\Component\Form\AttributeMapper $attributeMapper, + AttributeMetadataDataProvider $attributeMetadataDataProvider, + AttributeMapper $attributeMapper, AttributeMerger $merger, - \Magento\Customer\Model\Options $options = null, - Data $checkoutDataHelper = null, - \Magento\Shipping\Model\Config $shippingConfig = null, - StoreManagerInterface $storeManager = null + Options $options, + Data $checkoutDataHelper, + Config $shippingConfig, + StoreManagerInterface $storeManager ) { $this->attributeMetadataDataProvider = $attributeMetadataDataProvider; $this->attributeMapper = $attributeMapper; $this->merger = $merger; - $this->options = $options ?: \Magento\Framework\App\ObjectManager::getInstance() - ->get(\Magento\Customer\Model\Options::class); - $this->checkoutDataHelper = $checkoutDataHelper ?: \Magento\Framework\App\ObjectManager::getInstance() - ->get(Data::class); - $this->shippingConfig = $shippingConfig ?: \Magento\Framework\App\ObjectManager::getInstance() - ->get(\Magento\Shipping\Model\Config::class); - $this->storeManager = $storeManager ?: \Magento\Framework\App\ObjectManager::getInstance() - ->get(StoreManagerInterface::class); + $this->options = $options; + $this->checkoutDataHelper = $checkoutDataHelper; + $this->shippingConfig = $shippingConfig; + $this->storeManager = $storeManager; } /** @@ -87,7 +89,7 @@ public function __construct( */ private function getAddressAttributes() { - /** @var \Magento\Eav\Api\Data\AttributeInterface[] $attributes */ + /** @var AttributeInterface[] $attributes */ $attributes = $this->attributeMetadataDataProvider->loadAttributesCollection( 'customer_address', 'customer_register_address' @@ -157,8 +159,10 @@ public function process($jsLayout) $elements = $this->getAddressAttributes(); $elements = $this->convertElementsToSelect($elements, $attributesToConvert); // The following code is a workaround for custom address attributes - if (isset($jsLayout['components']['checkout']['children']['steps']['children']['billing-step']['children'] - ['payment']['children'])) { + if (isset( + $jsLayout['components']['checkout']['children']['steps']['children']['billing-step']['children']['payment'] + ['children'] + )) { $jsLayout['components']['checkout']['children']['steps']['children']['billing-step']['children'] ['payment']['children'] = $this->processPaymentChildrenComponents( $jsLayout['components']['checkout']['children']['steps']['children']['billing-step']['children'] @@ -166,8 +170,11 @@ public function process($jsLayout) $elements ); } - if (isset($jsLayout['components']['checkout']['children']['steps']['children']['shipping-step']['children'] - ['step-config']['children']['shipping-rates-validation']['children'])) { + + if (isset( + $jsLayout['components']['checkout']['children']['steps']['children']['shipping-step']['children'] + ['step-config']['children']['shipping-rates-validation']['children'] + )) { $jsLayout['components']['checkout']['children']['steps']['children']['shipping-step']['children'] ['step-config']['children']['shipping-rates-validation']['children'] = $this->processShippingChildrenComponents( @@ -176,8 +183,10 @@ public function process($jsLayout) ); } - if (isset($jsLayout['components']['checkout']['children']['steps']['children']['shipping-step'] - ['children']['shippingAddress']['children']['shipping-address-fieldset']['children'])) { + if (isset( + $jsLayout['components']['checkout']['children']['steps']['children']['shipping-step']['children'] + ['shippingAddress']['children']['shipping-address-fieldset']['children'] + )) { $fields = $jsLayout['components']['checkout']['children']['steps']['children']['shipping-step'] ['children']['shippingAddress']['children']['shipping-address-fieldset']['children']; $jsLayout['components']['checkout']['children']['steps']['children']['shipping-step'] @@ -188,6 +197,7 @@ public function process($jsLayout) $fields ); } + return $jsLayout; } @@ -304,9 +314,6 @@ private function getBillingAddressComponent($paymentCode, $elements) 'checkoutProvider', 'billingAddress' . $paymentCode, [ - 'country_id' => [ - 'sortOrder' => 115, - ], 'region' => [ 'visible' => false, ], diff --git a/app/code/Magento/Checkout/Controller/Sidebar/RemoveItem.php b/app/code/Magento/Checkout/Controller/Sidebar/RemoveItem.php index f589e702de950..3a17f4f718019 100644 --- a/app/code/Magento/Checkout/Controller/Sidebar/RemoveItem.php +++ b/app/code/Magento/Checkout/Controller/Sidebar/RemoveItem.php @@ -3,104 +3,102 @@ * Copyright © Magento, Inc. All rights reserved. * See COPYING.txt for license details. */ +declare(strict_types=1); + namespace Magento\Checkout\Controller\Sidebar; -use Magento\Framework\App\Action\HttpPostActionInterface as HttpPostActionInterface; +use Exception; +use Magento\Checkout\Model\Sidebar; +use Magento\Framework\App\Action\HttpPostActionInterface; +use Magento\Framework\App\RequestInterface; +use Magento\Framework\Controller\Result\JsonFactory as ResultJsonFactory; +use Magento\Framework\Controller\Result\RedirectFactory as ResultRedirectFactory; +use Magento\Framework\Data\Form\FormKey\Validator; +use Magento\Framework\Exception\LocalizedException; +use Psr\Log\LoggerInterface; -class RemoveItem extends \Magento\Framework\App\Action\Action implements HttpPostActionInterface +class RemoveItem implements HttpPostActionInterface { /** - * @var \Magento\Checkout\Model\Sidebar + * @var RequestInterface */ - protected $sidebar; + private $request; /** - * @var \Psr\Log\LoggerInterface + * @var ResultJsonFactory */ - protected $logger; + private $resultJsonFactory; /** - * @var \Magento\Framework\Json\Helper\Data + * @var ResultRedirectFactory */ - protected $jsonHelper; + private $resultRedirectFactory; /** - * @var \Magento\Framework\View\Result\PageFactory + * @var Sidebar */ - protected $resultPageFactory; + protected $sidebar; /** - * @var \Magento\Framework\Data\Form\FormKey\Validator + * @var Validator */ private $formKeyValidator; /** - * @param \Magento\Framework\App\Action\Context $context - * @param \Magento\Checkout\Model\Sidebar $sidebar - * @param \Psr\Log\LoggerInterface $logger - * @param \Magento\Framework\Json\Helper\Data $jsonHelper - * @param \Magento\Framework\View\Result\PageFactory $resultPageFactory + * @var LoggerInterface + */ + protected $logger; + + /** + * @param RequestInterface $request + * @param ResultJsonFactory $resultJsonFactory + * @param ResultRedirectFactory $resultRedirectFactory + * @param Sidebar $sidebar + * @param Validator $formKeyValidator + * @param LoggerInterface $logger */ public function __construct( - \Magento\Framework\App\Action\Context $context, - \Magento\Checkout\Model\Sidebar $sidebar, - \Psr\Log\LoggerInterface $logger, - \Magento\Framework\Json\Helper\Data $jsonHelper, - \Magento\Framework\View\Result\PageFactory $resultPageFactory + RequestInterface $request, + ResultJsonFactory $resultJsonFactory, + ResultRedirectFactory $resultRedirectFactory, + Sidebar $sidebar, + Validator $formKeyValidator, + LoggerInterface $logger ) { + $this->request = $request; + $this->resultJsonFactory = $resultJsonFactory; + $this->resultRedirectFactory = $resultRedirectFactory; $this->sidebar = $sidebar; + $this->formKeyValidator = $formKeyValidator; $this->logger = $logger; - $this->jsonHelper = $jsonHelper; - $this->resultPageFactory = $resultPageFactory; - parent::__construct($context); } /** - * @return $this + * @inheritDoc */ public function execute() { - if (!$this->getFormKeyValidator()->validate($this->getRequest())) { - return $this->resultRedirectFactory->create()->setPath('*/cart/'); + if (!$this->formKeyValidator->validate($this->request)) { + return $this->resultRedirectFactory->create() + ->setPath('*/cart/'); } - $itemId = (int)$this->getRequest()->getParam('item_id'); + + $itemId = (int)$this->request->getParam('item_id'); + $error = ''; + try { $this->sidebar->checkQuoteItem($itemId); $this->sidebar->removeQuoteItem($itemId); - return $this->jsonResponse(); - } catch (\Magento\Framework\Exception\LocalizedException $e) { - return $this->jsonResponse($e->getMessage()); - } catch (\Exception $e) { + } catch (LocalizedException $e) { + $error = $e->getMessage(); + } catch (Exception $e) { $this->logger->critical($e); - return $this->jsonResponse($e->getMessage()); + $error = $e->getMessage(); } - } - - /** - * Compile JSON response - * - * @param string $error - * @return \Magento\Framework\App\Response\Http - */ - protected function jsonResponse($error = '') - { - $response = $this->sidebar->getResponseData($error); - return $this->getResponse()->representJson( - $this->jsonHelper->jsonEncode($response) - ); - } + $resultJson = $this->resultJsonFactory->create(); + $resultJson->setData($this->sidebar->getResponseData($error)); - /** - * @return \Magento\Framework\Data\Form\FormKey\Validator - * @deprecated 100.0.9 - */ - private function getFormKeyValidator() - { - if (!$this->formKeyValidator) { - $this->formKeyValidator = \Magento\Framework\App\ObjectManager::getInstance() - ->get(\Magento\Framework\Data\Form\FormKey\Validator::class); - } - return $this->formKeyValidator; + return $resultJson; } } diff --git a/app/code/Magento/Checkout/Model/Cart/RequestQuantityProcessor.php b/app/code/Magento/Checkout/Model/Cart/RequestQuantityProcessor.php index 971b35c8f3e3d..27566ba6805af 100644 --- a/app/code/Magento/Checkout/Model/Cart/RequestQuantityProcessor.php +++ b/app/code/Magento/Checkout/Model/Cart/RequestQuantityProcessor.php @@ -9,6 +9,9 @@ use Magento\Framework\Locale\ResolverInterface; +/** + * Cart request quantity processor + */ class RequestQuantityProcessor { /** @@ -34,7 +37,7 @@ public function __construct( */ public function process(array $cartData): array { - $filter = new \Zend\I18n\Filter\NumberParse($this->localeResolver->getLocale()); + $filter = new \Laminas\I18n\Filter\NumberParse($this->localeResolver->getLocale()); foreach ($cartData as $index => $data) { if (isset($data['qty'])) { diff --git a/app/code/Magento/Checkout/Model/GuestPaymentInformationManagement.php b/app/code/Magento/Checkout/Model/GuestPaymentInformationManagement.php index cae78389d4120..1d15a5dd7f176 100644 --- a/app/code/Magento/Checkout/Model/GuestPaymentInformationManagement.php +++ b/app/code/Magento/Checkout/Model/GuestPaymentInformationManagement.php @@ -56,11 +56,6 @@ class GuestPaymentInformationManagement implements \Magento\Checkout\Api\GuestPa */ private $logger; - /** - * @var ResourceConnection - */ - private $connectionPool; - /** * @param \Magento\Quote\Api\GuestBillingAddressManagementInterface $billingAddressManagement * @param \Magento\Quote\Api\GuestPaymentMethodManagementInterface $paymentMethodManagement @@ -68,7 +63,6 @@ class GuestPaymentInformationManagement implements \Magento\Checkout\Api\GuestPa * @param \Magento\Checkout\Api\PaymentInformationManagementInterface $paymentInformationManagement * @param \Magento\Quote\Model\QuoteIdMaskFactory $quoteIdMaskFactory * @param CartRepositoryInterface $cartRepository - * @param ResourceConnection $connectionPool * @codeCoverageIgnore */ public function __construct( @@ -77,8 +71,7 @@ public function __construct( \Magento\Quote\Api\GuestCartManagementInterface $cartManagement, \Magento\Checkout\Api\PaymentInformationManagementInterface $paymentInformationManagement, \Magento\Quote\Model\QuoteIdMaskFactory $quoteIdMaskFactory, - CartRepositoryInterface $cartRepository, - ResourceConnection $connectionPool = null + CartRepositoryInterface $cartRepository ) { $this->billingAddressManagement = $billingAddressManagement; $this->paymentMethodManagement = $paymentMethodManagement; @@ -86,7 +79,6 @@ public function __construct( $this->paymentInformationManagement = $paymentInformationManagement; $this->quoteIdMaskFactory = $quoteIdMaskFactory; $this->cartRepository = $cartRepository; - $this->connectionPool = $connectionPool ?: ObjectManager::getInstance()->get(ResourceConnection::class); } /** @@ -98,33 +90,23 @@ public function savePaymentInformationAndPlaceOrder( \Magento\Quote\Api\Data\PaymentInterface $paymentMethod, \Magento\Quote\Api\Data\AddressInterface $billingAddress = null ) { - $salesConnection = $this->connectionPool->getConnection('sales'); - $checkoutConnection = $this->connectionPool->getConnection('checkout'); - $salesConnection->beginTransaction(); - $checkoutConnection->beginTransaction(); - + $this->savePaymentInformation($cartId, $email, $paymentMethod, $billingAddress); try { - $this->savePaymentInformation($cartId, $email, $paymentMethod, $billingAddress); - try { - $orderId = $this->cartManagement->placeOrder($cartId); - } catch (\Magento\Framework\Exception\LocalizedException $e) { - throw new CouldNotSaveException( - __($e->getMessage()), - $e - ); - } catch (\Exception $e) { - $this->getLogger()->critical($e); - throw new CouldNotSaveException( - __('An error occurred on the server. Please try to place the order again.'), - $e - ); - } - $salesConnection->commit(); - $checkoutConnection->commit(); + $orderId = $this->cartManagement->placeOrder($cartId); + } catch (\Magento\Framework\Exception\LocalizedException $e) { + $this->getLogger()->critical( + 'Placing an order with quote_id ' . $cartId . ' is failed: ' . $e->getMessage() + ); + throw new CouldNotSaveException( + __($e->getMessage()), + $e + ); } catch (\Exception $e) { - $salesConnection->rollBack(); - $checkoutConnection->rollBack(); - throw $e; + $this->getLogger()->critical($e); + throw new CouldNotSaveException( + __('An error occurred on the server. Please try to place the order again.'), + $e + ); } return $orderId; diff --git a/app/code/Magento/Checkout/Model/Layout/DepersonalizePlugin.php b/app/code/Magento/Checkout/Model/Layout/DepersonalizePlugin.php index de36bf658c59d..2f39cb6907661 100644 --- a/app/code/Magento/Checkout/Model/Layout/DepersonalizePlugin.php +++ b/app/code/Magento/Checkout/Model/Layout/DepersonalizePlugin.php @@ -3,50 +3,54 @@ * Copyright © Magento, Inc. All rights reserved. * See COPYING.txt for license details. */ +declare(strict_types=1); + namespace Magento\Checkout\Model\Layout; +use Magento\Checkout\Model\Session as CheckoutSession; +use Magento\Framework\View\LayoutInterface; use Magento\PageCache\Model\DepersonalizeChecker; /** - * Class DepersonalizePlugin + * Depersonalize customer data. + * + * @SuppressWarnings(PHPMD.CookieAndSessionMisuse) */ class DepersonalizePlugin { /** * @var DepersonalizeChecker */ - protected $depersonalizeChecker; + private $depersonalizeChecker; /** - * @var \Magento\Checkout\Model\Session + * @var CheckoutSession */ - protected $checkoutSession; + private $checkoutSession; /** * @param DepersonalizeChecker $depersonalizeChecker - * @param \Magento\Checkout\Model\Session $checkoutSession + * @param CheckoutSession $checkoutSession * @codeCoverageIgnore */ public function __construct( DepersonalizeChecker $depersonalizeChecker, - \Magento\Checkout\Model\Session $checkoutSession + CheckoutSession $checkoutSession ) { - $this->checkoutSession = $checkoutSession; $this->depersonalizeChecker = $depersonalizeChecker; + $this->checkoutSession = $checkoutSession; } /** - * After generate Xml + * Change sensitive customer data if the depersonalization is needed. * - * @param \Magento\Framework\View\LayoutInterface $subject - * @param \Magento\Framework\View\LayoutInterface $result - * @return \Magento\Framework\View\LayoutInterface + * @param LayoutInterface $subject + * @return void */ - public function afterGenerateXml(\Magento\Framework\View\LayoutInterface $subject, $result) + public function afterGenerateElements(LayoutInterface $subject) { if ($this->depersonalizeChecker->checkIfDepersonalize($subject)) { $this->checkoutSession->clearStorage(); } - return $result; } } diff --git a/app/code/Magento/Checkout/Model/PaymentInformationManagement.php b/app/code/Magento/Checkout/Model/PaymentInformationManagement.php index 2f1a36318ebc8..1f7931d7d3e6a 100644 --- a/app/code/Magento/Checkout/Model/PaymentInformationManagement.php +++ b/app/code/Magento/Checkout/Model/PaymentInformationManagement.php @@ -9,7 +9,7 @@ use Magento\Framework\Exception\CouldNotSaveException; /** - * Payment information management + * Payment information management service. * * @SuppressWarnings(PHPMD.CouplingBetweenObjects) */ @@ -85,6 +85,9 @@ public function savePaymentInformationAndPlaceOrder( try { $orderId = $this->cartManagement->placeOrder($cartId); } catch (\Magento\Framework\Exception\LocalizedException $e) { + $this->getLogger()->critical( + 'Placing an order with quote_id ' . $cartId . ' is failed: ' . $e->getMessage() + ); throw new CouldNotSaveException( __($e->getMessage()), $e diff --git a/app/code/Magento/Checkout/Model/ShippingInformationManagement.php b/app/code/Magento/Checkout/Model/ShippingInformationManagement.php index 369ae8e6f725e..cbbbd9a9b4d01 100644 --- a/app/code/Magento/Checkout/Model/ShippingInformationManagement.php +++ b/app/code/Magento/Checkout/Model/ShippingInformationManagement.php @@ -6,28 +6,38 @@ namespace Magento\Checkout\Model; +use Magento\Checkout\Api\Data\PaymentDetailsInterface; +use Magento\Checkout\Api\Data\ShippingInformationInterface; +use Magento\Checkout\Api\ShippingInformationManagementInterface; +use Magento\Customer\Api\AddressRepositoryInterface; +use Magento\Framework\App\Config\ScopeConfigInterface; +use Magento\Framework\App\ObjectManager; use Magento\Framework\Exception\InputException; -use Magento\Framework\Exception\StateException; +use Magento\Framework\Exception\LocalizedException; use Magento\Framework\Exception\NoSuchEntityException; +use Magento\Framework\Exception\StateException; +use Magento\Quote\Api\CartRepositoryInterface; +use Magento\Quote\Api\CartTotalRepositoryInterface; use Magento\Quote\Api\Data\AddressInterface; +use Magento\Quote\Api\Data\CartExtensionFactory; use Magento\Quote\Api\Data\CartInterface; -use Psr\Log\LoggerInterface as Logger; +use Magento\Quote\Api\PaymentMethodManagementInterface; +use Magento\Quote\Model\Quote; +use Magento\Quote\Model\Quote\TotalsCollector; use Magento\Quote\Model\QuoteAddressValidator; -use Magento\Quote\Api\Data\CartExtensionFactory; use Magento\Quote\Model\ShippingAssignmentFactory; use Magento\Quote\Model\ShippingFactory; -use Magento\Framework\App\ObjectManager; +use Psr\Log\LoggerInterface as Logger; /** - * Class ShippingInformationManagement + * Class checkout shipping information management * - * @package Magento\Checkout\Model * @SuppressWarnings(PHPMD.CouplingBetweenObjects) */ -class ShippingInformationManagement implements \Magento\Checkout\Api\ShippingInformationManagementInterface +class ShippingInformationManagement implements ShippingInformationManagementInterface { /** - * @var \Magento\Quote\Api\PaymentMethodManagementInterface + * @var PaymentMethodManagementInterface */ protected $paymentMethodManagement; @@ -37,12 +47,12 @@ class ShippingInformationManagement implements \Magento\Checkout\Api\ShippingInf protected $paymentDetailsFactory; /** - * @var \Magento\Quote\Api\CartTotalRepositoryInterface + * @var CartTotalRepositoryInterface */ protected $cartTotalsRepository; /** - * @var \Magento\Quote\Api\CartRepositoryInterface + * @var CartRepositoryInterface */ protected $quoteRepository; @@ -57,65 +67,63 @@ class ShippingInformationManagement implements \Magento\Checkout\Api\ShippingInf protected $addressValidator; /** - * @var \Magento\Customer\Api\AddressRepositoryInterface + * @var AddressRepositoryInterface * @deprecated 100.2.0 */ protected $addressRepository; /** - * @var \Magento\Framework\App\Config\ScopeConfigInterface + * @var ScopeConfigInterface * @deprecated 100.2.0 */ protected $scopeConfig; /** - * @var \Magento\Quote\Model\Quote\TotalsCollector + * @var TotalsCollector * @deprecated 100.2.0 */ protected $totalsCollector; /** - * @var \Magento\Quote\Api\Data\CartExtensionFactory + * @var CartExtensionFactory */ private $cartExtensionFactory; /** - * @var \Magento\Quote\Model\ShippingAssignmentFactory + * @var ShippingAssignmentFactory */ protected $shippingAssignmentFactory; /** - * @var \Magento\Quote\Model\ShippingFactory + * @var ShippingFactory */ private $shippingFactory; /** - * Constructor - * - * @param \Magento\Quote\Api\PaymentMethodManagementInterface $paymentMethodManagement - * @param \Magento\Checkout\Model\PaymentDetailsFactory $paymentDetailsFactory - * @param \Magento\Quote\Api\CartTotalRepositoryInterface $cartTotalsRepository - * @param \Magento\Quote\Api\CartRepositoryInterface $quoteRepository - * @param \Magento\Quote\Model\QuoteAddressValidator $addressValidator + * @param PaymentMethodManagementInterface $paymentMethodManagement + * @param PaymentDetailsFactory $paymentDetailsFactory + * @param CartTotalRepositoryInterface $cartTotalsRepository + * @param CartRepositoryInterface $quoteRepository + * @param QuoteAddressValidator $addressValidator * @param Logger $logger - * @param \Magento\Customer\Api\AddressRepositoryInterface $addressRepository - * @param \Magento\Framework\App\Config\ScopeConfigInterface $scopeConfig - * @param \Magento\Quote\Model\Quote\TotalsCollector $totalsCollector + * @param AddressRepositoryInterface $addressRepository + * @param ScopeConfigInterface $scopeConfig + * @param TotalsCollector $totalsCollector * @param CartExtensionFactory|null $cartExtensionFactory * @param ShippingAssignmentFactory|null $shippingAssignmentFactory * @param ShippingFactory|null $shippingFactory * @SuppressWarnings(PHPMD.ExcessiveParameterList) */ public function __construct( - \Magento\Quote\Api\PaymentMethodManagementInterface $paymentMethodManagement, - \Magento\Checkout\Model\PaymentDetailsFactory $paymentDetailsFactory, - \Magento\Quote\Api\CartTotalRepositoryInterface $cartTotalsRepository, - \Magento\Quote\Api\CartRepositoryInterface $quoteRepository, + PaymentMethodManagementInterface $paymentMethodManagement, + PaymentDetailsFactory $paymentDetailsFactory, + CartTotalRepositoryInterface $cartTotalsRepository, + CartRepositoryInterface $quoteRepository, QuoteAddressValidator $addressValidator, Logger $logger, - \Magento\Customer\Api\AddressRepositoryInterface $addressRepository, - \Magento\Framework\App\Config\ScopeConfigInterface $scopeConfig, - \Magento\Quote\Model\Quote\TotalsCollector $totalsCollector, + AddressRepositoryInterface $addressRepository, + ScopeConfigInterface $scopeConfig, + TotalsCollector $totalsCollector, CartExtensionFactory $cartExtensionFactory = null, ShippingAssignmentFactory $shippingAssignmentFactory = null, ShippingFactory $shippingFactory = null @@ -141,24 +149,23 @@ public function __construct( * Save address information. * * @param int $cartId - * @param \Magento\Checkout\Api\Data\ShippingInformationInterface $addressInformation - * @return \Magento\Checkout\Api\Data\PaymentDetailsInterface + * @param ShippingInformationInterface $addressInformation + * @return PaymentDetailsInterface * @throws InputException * @throws NoSuchEntityException * @throws StateException */ public function saveAddressInformation( $cartId, - \Magento\Checkout\Api\Data\ShippingInformationInterface $addressInformation - ) { - /** @var \Magento\Quote\Model\Quote $quote */ + ShippingInformationInterface $addressInformation + ): PaymentDetailsInterface { + /** @var Quote $quote */ $quote = $this->quoteRepository->getActive($cartId); $this->validateQuote($quote); $address = $addressInformation->getShippingAddress(); - if (!$address || !$address->getCountryId()) { - throw new StateException(__('The shipping address is missing. Set the address and try again.')); - } + $this->validateAddress($address); + if (!$address->getCustomerAddressId()) { $address->setCustomerAddressId(null); } @@ -182,6 +189,14 @@ public function saveAddressInformation( $quote->setIsMultiShipping(false); $this->quoteRepository->save($quote); + } catch (LocalizedException $e) { + $this->logger->critical($e); + throw new InputException( + __( + 'The shipping information was unable to be saved. Error: "%message"', + ['message' => $e->getMessage()] + ) + ); } catch (\Exception $e) { $this->logger->critical($e); throw new InputException( @@ -199,26 +214,39 @@ public function saveAddressInformation( ); } - /** @var \Magento\Checkout\Api\Data\PaymentDetailsInterface $paymentDetails */ + /** @var PaymentDetailsInterface $paymentDetails */ $paymentDetails = $this->paymentDetailsFactory->create(); $paymentDetails->setPaymentMethods($this->paymentMethodManagement->getList($cartId)); $paymentDetails->setTotals($this->cartTotalsRepository->get($cartId)); return $paymentDetails; } + /** + * Validate shipping address + * + * @param AddressInterface|null $address + * @return void + * @throws StateException + */ + private function validateAddress(?AddressInterface $address): void + { + if (!$address || !$address->getCountryId()) { + throw new StateException(__('The shipping address is missing. Set the address and try again.')); + } + } + /** * Validate quote * - * @param \Magento\Quote\Model\Quote $quote + * @param Quote $quote * @throws InputException - * @throws NoSuchEntityException * @return void */ - protected function validateQuote(\Magento\Quote\Model\Quote $quote) + protected function validateQuote(Quote $quote): void { - if (0 == $quote->getItemsCount()) { + if (!$quote->getItemsCount()) { throw new InputException( - __("The shipping method can't be set for an empty cart. Add an item to cart and try again.") + __('The shipping method can\'t be set for an empty cart. Add an item to cart and try again.') ); } } @@ -231,7 +259,7 @@ protected function validateQuote(\Magento\Quote\Model\Quote $quote) * @param string $method * @return CartInterface */ - private function prepareShippingAssignment(CartInterface $quote, AddressInterface $address, $method) + private function prepareShippingAssignment(CartInterface $quote, AddressInterface $address, $method): CartInterface { $cartExtension = $quote->getExtensionAttributes(); if ($cartExtension === null) { diff --git a/app/code/Magento/Checkout/Test/Mftf/ActionGroup/AssertStorefrontCheckoutCartEstimateShippingAndTaxAddressActionGroup.xml b/app/code/Magento/Checkout/Test/Mftf/ActionGroup/AssertStorefrontCheckoutCartEstimateShippingAndTaxAddressActionGroup.xml new file mode 100644 index 0000000000000..32e529fb7ca0a --- /dev/null +++ b/app/code/Magento/Checkout/Test/Mftf/ActionGroup/AssertStorefrontCheckoutCartEstimateShippingAndTaxAddressActionGroup.xml @@ -0,0 +1,31 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + /** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +--> + +<actionGroups xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/actionGroupSchema.xsd"> + <actionGroup name="AssertStorefrontCheckoutCartEstimateShippingAndTaxAddressActionGroup"> + <annotations> + <description>Check address data in Estimate Shipping And Tax section of shopping cart on storefront</description> + </annotations> + <arguments> + <argument name="country" type="string" defaultValue="{{US_Address_TX.country}}"/> + <argument name="state" type="string" defaultValue="{{US_Address_TX.state}}"/> + <argument name="postcode" type="string" defaultValue="{{US_Address_TX.postcode}}"/> + </arguments> + + <waitForElementVisible selector="{{CheckoutCartSummarySection.subtotal}}" stepKey="waitForSubtotalVisible"/> + <conditionalClick selector="{{CheckoutCartSummarySection.estimateShippingAndTax}}" dependentSelector="{{CheckoutCartSummarySection.country}}" visible="false" stepKey="expandEstimateShippingAndTaxIfNeeded" /> + <seeOptionIsSelected selector="{{CheckoutCartSummarySection.country}}" userInput="{{country}}" stepKey="checkCountry"/> + <seeOptionIsSelected selector="{{CheckoutCartSummarySection.stateProvince}}" userInput="{{state}}" stepKey="checkState" /> + <grabValueFrom selector="{{CheckoutCartSummarySection.postcode}}" stepKey="grabPostCodeText"/> + <assertEquals message="Address postcode is invalid" stepKey="checkPostcode"> + <expectedResult type="string">{{postcode}}</expectedResult> + <actualResult type="variable">grabPostCodeText</actualResult> + </assertEquals> + </actionGroup> +</actionGroups> diff --git a/app/code/Magento/Checkout/Test/Mftf/ActionGroup/AssertStorefrontCheckoutCartItemsActionGroup.xml b/app/code/Magento/Checkout/Test/Mftf/ActionGroup/AssertStorefrontCheckoutCartItemsActionGroup.xml index fa4db86a5860d..47e44e86affb8 100644 --- a/app/code/Magento/Checkout/Test/Mftf/ActionGroup/AssertStorefrontCheckoutCartItemsActionGroup.xml +++ b/app/code/Magento/Checkout/Test/Mftf/ActionGroup/AssertStorefrontCheckoutCartItemsActionGroup.xml @@ -19,10 +19,10 @@ <argument name="subtotal" type="string"/> <argument name="qty" type="string"/> </arguments> - + <see selector="{{CheckoutCartProductSection.productName}}" userInput="{{productName}}" stepKey="seeProductNameInCheckoutSummary"/> - <see selector="{{CheckoutCartProductSection.checkoutCartProductPrice}}" userInput="{{productPrice}}" stepKey="seeProductPriceInCart"/> - <see selector="{{CheckoutCartProductSection.checkoutCartSubtotal}}" userInput="{{subtotal}}" stepKey="seeSubtotalPrice"/> - <seeInField selector="{{CheckoutCartProductSection.qtyByContains(productSku)}}" userInput="{{qty}}" stepKey="seeProductQuantity"/> + <see selector="{{CheckoutCartProductSection.ProductPriceByName(productName)}}" userInput="{{productPrice}}" stepKey="seeProductPriceInCart"/> + <see selector="{{CheckoutCartProductSection.productSubtotalByName(productName)}}" userInput="{{subtotal}}" stepKey="seeSubtotalPrice"/> + <seeInField selector="{{CheckoutCartProductSection.ProductQuantityByName(productName)}}" userInput="{{qty}}" stepKey="seeProductQuantity"/> </actionGroup> </actionGroups> diff --git a/app/code/Magento/Checkout/Test/Mftf/ActionGroup/AssertStorefrontOrderIsNotPlacedActionGroup.xml b/app/code/Magento/Checkout/Test/Mftf/ActionGroup/AssertStorefrontOrderIsNotPlacedActionGroup.xml new file mode 100644 index 0000000000000..18fc6fac18085 --- /dev/null +++ b/app/code/Magento/Checkout/Test/Mftf/ActionGroup/AssertStorefrontOrderIsNotPlacedActionGroup.xml @@ -0,0 +1,17 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + /** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +--> + +<actionGroups xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/actionGroupSchema.xsd"> + <actionGroup name="AssertStorefrontOrderIsNotPlacedActionGroup" extends="AssertStorefrontOrderCannotBePlacedActionGroup"> + <annotations> + <description>Validates order cannot be placed and checks error message.</description> + </annotations> + <remove keyForRemoval="assertErrorMessage"/> + <seeElementInDOM selector="{{CheckoutHeaderSection.errorMessageContainsText(error)}}" stepKey="assertErrorMessageInDOM"/> + </actionGroup> +</actionGroups> diff --git a/app/code/Magento/Checkout/Test/Mftf/ActionGroup/IdentityOfDefaultBillingAndShippingAddressActionGroup.xml b/app/code/Magento/Checkout/Test/Mftf/ActionGroup/IdentityOfDefaultBillingAndShippingAddressActionGroup.xml index c91314f1c1bc2..88b517c1cbe25 100644 --- a/app/code/Magento/Checkout/Test/Mftf/ActionGroup/IdentityOfDefaultBillingAndShippingAddressActionGroup.xml +++ b/app/code/Magento/Checkout/Test/Mftf/ActionGroup/IdentityOfDefaultBillingAndShippingAddressActionGroup.xml @@ -20,6 +20,9 @@ <!--Make sure that shipping and billing addresses are different--> <see userInput="Shipping Address" stepKey="seeShippingAddress"/> <see userInput="Billing Address" stepKey="seeBillingAddress"/> - <assertEquals stepKey="assert" actual="$billingAddr" expected="$shippingAddr"/> + <assertEquals stepKey="assert"> + <actualResult type="const">$billingAddr</actualResult> + <expectedResult type="const">$shippingAddr</expectedResult> + </assertEquals> </actionGroup> </actionGroups> diff --git a/app/code/Magento/Checkout/Test/Mftf/ActionGroup/PlaceOrderWithLoggedUserActionGroup.xml b/app/code/Magento/Checkout/Test/Mftf/ActionGroup/PlaceOrderWithLoggedUserActionGroup.xml index 2bd1ee0f7d809..95d78777ed922 100644 --- a/app/code/Magento/Checkout/Test/Mftf/ActionGroup/PlaceOrderWithLoggedUserActionGroup.xml +++ b/app/code/Magento/Checkout/Test/Mftf/ActionGroup/PlaceOrderWithLoggedUserActionGroup.xml @@ -17,6 +17,8 @@ <argument name="shippingMethod" defaultValue="" type="string"/> </arguments> + <waitForLoadingMaskToDisappear stepKey="waitForCartTotalsLoaded"/> + <waitForElementVisible selector="{{CheckoutCartSummarySection.orderTotal}}" stepKey="waitForCartGrandTotalVisible"/> <waitForElementVisible selector="{{CheckoutCartSummarySection.proceedToCheckout}}" stepKey="waitProceedToCheckout"/> <click selector="{{CheckoutCartSummarySection.proceedToCheckout}}" stepKey="clickProceedToCheckout"/> <click selector="{{CheckoutShippingMethodsSection.checkShippingMethodByName('shippingMethod')}}" stepKey="selectShippingMethod"/> diff --git a/app/code/Magento/Checkout/Test/Mftf/ActionGroup/StorefrontAddSimpleProductToCartActionGroup.xml b/app/code/Magento/Checkout/Test/Mftf/ActionGroup/StorefrontAddSimpleProductToCartActionGroup.xml index a17a6b4353783..d6add9a5d5c00 100644 --- a/app/code/Magento/Checkout/Test/Mftf/ActionGroup/StorefrontAddSimpleProductToCartActionGroup.xml +++ b/app/code/Magento/Checkout/Test/Mftf/ActionGroup/StorefrontAddSimpleProductToCartActionGroup.xml @@ -11,12 +11,12 @@ <!-- Add Product to Cart from the category page and check message --> <actionGroup name="StorefrontAddSimpleProductToCartActionGroup"> <annotations> - <description>Adds the provided Product to the Storefront Shopping Cart from a Storefront Category page. Validates that the provided Success Message is present and correct.</description> + <description>Only works on Storefront Category Page - Add Simple Product to Cart</description> </annotations> <arguments> <argument name="product" type="entity"/> </arguments> - + <moveMouseOver selector="{{StorefrontCategoryProductSection.ProductInfoByName(product.name)}}" stepKey="moveMouseOverProduct"/> <click selector="{{StorefrontCategoryProductSection.ProductAddToCartByName(product.name)}}" stepKey="clickAddToCart"/> <waitForElementVisible selector="{{StorefrontCategoryMainSection.SuccessMsg}}" stepKey="waitForSuccessMessage"/> diff --git a/app/code/Magento/Checkout/Test/Mftf/ActionGroup/StorefrontAssertProductAddToCartErrorMessageActionGroup.xml b/app/code/Magento/Checkout/Test/Mftf/ActionGroup/StorefrontAssertProductAddToCartErrorMessageActionGroup.xml new file mode 100644 index 0000000000000..2147f837d0abc --- /dev/null +++ b/app/code/Magento/Checkout/Test/Mftf/ActionGroup/StorefrontAssertProductAddToCartErrorMessageActionGroup.xml @@ -0,0 +1,17 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. +--> + +<actionGroups xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/actionGroupSchema.xsd"> + <actionGroup name="StorefrontAssertProductAddToCartErrorMessageActionGroup"> + <arguments> + <argument name="message" type="string" defaultValue=""/> + </arguments> + <waitForElementVisible selector="{{StorefrontMessagesSection.error}}" time="10" stepKey="waitForProductAddedMessage"/> + <see selector="{{StorefrontMessagesSection.error}}" userInput="{{message}}" stepKey="seeAddToCartErrorMessage"/> + </actionGroup> +</actionGroups> diff --git a/app/code/Magento/Checkout/Test/Mftf/ActionGroup/StorefrontAssertProductAddToCartSuccessMessageActionGroup.xml b/app/code/Magento/Checkout/Test/Mftf/ActionGroup/StorefrontAssertProductAddToCartSuccessMessageActionGroup.xml new file mode 100644 index 0000000000000..60c9461c53f7b --- /dev/null +++ b/app/code/Magento/Checkout/Test/Mftf/ActionGroup/StorefrontAssertProductAddToCartSuccessMessageActionGroup.xml @@ -0,0 +1,17 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. +--> + +<actionGroups xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/actionGroupSchema.xsd"> + <actionGroup name="StorefrontAssertProductAddToCartSuccessMessageActionGroup"> + <arguments> + <argument name="message" type="string" defaultValue=""/> + </arguments> + <waitForElementVisible selector="{{StorefrontMessagesSection.success}}" time="30" stepKey="waitForProductAddedMessage"/> + <see selector="{{StorefrontMessagesSection.success}}" userInput="message" stepKey="seeAddToCartSuccessMessage"/> + </actionGroup> +</actionGroups> diff --git a/app/code/Magento/Checkout/Test/Mftf/ActionGroup/StorefrontCheckoutCartFillEstimateShippingAndTaxActionGroup.xml b/app/code/Magento/Checkout/Test/Mftf/ActionGroup/StorefrontCheckoutCartFillEstimateShippingAndTaxActionGroup.xml new file mode 100644 index 0000000000000..543b6bc8cb599 --- /dev/null +++ b/app/code/Magento/Checkout/Test/Mftf/ActionGroup/StorefrontCheckoutCartFillEstimateShippingAndTaxActionGroup.xml @@ -0,0 +1,28 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + /** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +--> + +<actionGroups xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/actionGroupSchema.xsd"> + <actionGroup name="StorefrontCheckoutCartFillEstimateShippingAndTaxActionGroup"> + <annotations> + <description>Fill address data in Estimate Shipping And Tax section of shopping cart on storefront</description> + </annotations> + <arguments> + <argument name="country" type="string" defaultValue="{{US_Address_TX.country}}"/> + <argument name="state" type="string" defaultValue="{{US_Address_TX.state}}"/> + <argument name="postcode" type="string" defaultValue="{{US_Address_TX.postcode}}"/> + </arguments> + + <conditionalClick selector="{{CheckoutCartSummarySection.estimateShippingAndTax}}" dependentSelector="{{CheckoutCartSummarySection.estimateShippingAndTaxSummary}}" visible="false" stepKey="openEestimateShippingAndTaxSection"/> + <selectOption selector="{{CheckoutCartSummarySection.country}}" userInput="{{country}}" stepKey="selectCountry"/> + <selectOption selector="{{CheckoutCartSummarySection.stateProvince}}" userInput="{{state}}" stepKey="selectState"/> + <waitForElementVisible selector="{{CheckoutCartSummarySection.postcode}}" stepKey="waitForPostCodeVisible"/> + <fillField selector="{{CheckoutCartSummarySection.postcode}}" userInput="{{postcode}}" stepKey="selectPostCode"/> + <waitForLoadingMaskToDisappear stepKey="waitForLoadingMaskToDiappear"/> + </actionGroup> +</actionGroups> diff --git a/app/code/Magento/Checkout/Test/Mftf/ActionGroup/StorefrontCheckoutCheckProductActionGroup.xml b/app/code/Magento/Checkout/Test/Mftf/ActionGroup/StorefrontCheckoutCheckProductActionGroup.xml index 520db653a5c59..b7fda2b8b0345 100644 --- a/app/code/Magento/Checkout/Test/Mftf/ActionGroup/StorefrontCheckoutCheckProductActionGroup.xml +++ b/app/code/Magento/Checkout/Test/Mftf/ActionGroup/StorefrontCheckoutCheckProductActionGroup.xml @@ -19,6 +19,9 @@ <see selector="{{CheckoutCartProductSection.productName}}" userInput="{{product.name}}" stepKey="seeProductName"/> <grabValueFrom selector="{{CheckoutCartProductSection.ProductQuantityByName(product.name)}}" stepKey="grabProductQty"/> - <assertEquals expected="{{cartItem.qty}}" actual="$grabProductQty" stepKey="assertQtyShoppingCart"/> + <assertEquals stepKey="assertQtyShoppingCart"> + <actualResult type="const">$grabProductQty</actualResult> + <expectedResult type="const">{{cartItem.qty}}</expectedResult> + </assertEquals> </actionGroup> </actionGroups> diff --git a/app/code/Magento/Checkout/Test/Mftf/ActionGroup/StorefrontCheckoutClickNextOnShippingStepActionGroup.xml b/app/code/Magento/Checkout/Test/Mftf/ActionGroup/StorefrontCheckoutClickNextOnShippingStepActionGroup.xml new file mode 100644 index 0000000000000..1e86becdc0888 --- /dev/null +++ b/app/code/Magento/Checkout/Test/Mftf/ActionGroup/StorefrontCheckoutClickNextOnShippingStepActionGroup.xml @@ -0,0 +1,18 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + /** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +--> + +<actionGroups xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/actionGroupSchema.xsd"> + <actionGroup name="StorefrontCheckoutClickNextOnShippingStepActionGroup" extends="StorefrontCheckoutForwardFromShippingStepActionGroup"> + <annotations> + <description>Scrolls and clicks next on Checkout Shipping step</description> + </annotations> + <scrollTo selector="{{CheckoutShippingSection.next}}" before="clickNext" stepKey="scrollToNextButton"/> + <waitForLoadingMaskToDisappear stepKey="waitForLoadingMaskToDisappear"/> + </actionGroup> +</actionGroups> diff --git a/app/code/Magento/Checkout/Test/Mftf/ActionGroup/StorefrontOpenCustomerAddressesOnPaymentStepInCheckoutActionGroup.xml b/app/code/Magento/Checkout/Test/Mftf/ActionGroup/StorefrontOpenCustomerAddressesOnPaymentStepInCheckoutActionGroup.xml new file mode 100644 index 0000000000000..2ca395cbf6d25 --- /dev/null +++ b/app/code/Magento/Checkout/Test/Mftf/ActionGroup/StorefrontOpenCustomerAddressesOnPaymentStepInCheckoutActionGroup.xml @@ -0,0 +1,20 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + /** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +--> + +<actionGroups xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/actionGroupSchema.xsd"> + <actionGroup name="StorefrontOpenCustomerAddressesOnPaymentStepInCheckoutActionGroup"> + <annotations> + <description>Click 'Change Address' link on the Storefront Checkout page on the 'Payment' step.</description> + </annotations> + + <click selector="{{CheckoutBillingAddressSection.changeAddressButton}}" stepKey="clickChangeAddresslink"/> + <waitForElementVisible selector="{{CheckoutShippingAddressSearchSection.popupSelectShippingAddress}}" stepKey="seePopup"/> + <seeElement selector="{{CheckoutShippingAddressSearchSection.selectShippingAddressGrid}}" stepKey="seeAddressGrid"/> + </actionGroup> +</actionGroups> diff --git a/app/code/Magento/Checkout/Test/Mftf/ActionGroup/StorefrontOpenMiniCartActionGroup/ClickViewAndEditCartFromMiniCartActionGroup.xml b/app/code/Magento/Checkout/Test/Mftf/ActionGroup/StorefrontOpenMiniCartActionGroup/ClickViewAndEditCartFromMiniCartActionGroup.xml new file mode 100644 index 0000000000000..87330318f67dd --- /dev/null +++ b/app/code/Magento/Checkout/Test/Mftf/ActionGroup/StorefrontOpenMiniCartActionGroup/ClickViewAndEditCartFromMiniCartActionGroup.xml @@ -0,0 +1,22 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + /** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +--> +<actionGroups xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/actionGroupSchema.xsd"> + <actionGroup name="clickViewAndEditCartFromMiniCartActionGroup"> + <annotations> + <description>Clicks on the Storefront Mini Shopping Cart icon. Clicks on the 'View and Edit Cart' link. Validates that the URL is present and correct. PLEASE NOTE: The URL is Hardcoded.</description> + </annotations> + + <scrollTo selector="{{StorefrontMinicartSection.showCart}}" stepKey="scrollToMiniCart"/> + <conditionalClick selector="{{StorefrontMinicartSection.showCart}}" dependentSelector="{{StorefrontMinicartSection.miniCartOpened}}" visible="false" stepKey="openMiniCart"/> + <waitForElementVisible selector="{{StorefrontMinicartSection.viewAndEditCart}}" stepKey="waitForViewAndEditCartVisible"/> + <click selector="{{StorefrontMinicartSection.viewAndEditCart}}" stepKey="viewAndEditCart"/> + <waitForPageLoad stepKey="waitForPageToLoad"/> + <seeInCurrentUrl url="checkout/cart" stepKey="seeInCurrentUrl"/> + </actionGroup> +</actionGroups> diff --git a/app/code/Magento/Checkout/Test/Mftf/ActionGroup/StorefrontOpenMiniCartActionGroup.xml b/app/code/Magento/Checkout/Test/Mftf/ActionGroup/StorefrontOpenMiniCartActionGroup/StorefrontOpenMiniCartActionGroup.xml similarity index 88% rename from app/code/Magento/Checkout/Test/Mftf/ActionGroup/StorefrontOpenMiniCartActionGroup.xml rename to app/code/Magento/Checkout/Test/Mftf/ActionGroup/StorefrontOpenMiniCartActionGroup/StorefrontOpenMiniCartActionGroup.xml index 4203aa35a3bdb..2918f547da293 100644 --- a/app/code/Magento/Checkout/Test/Mftf/ActionGroup/StorefrontOpenMiniCartActionGroup.xml +++ b/app/code/Magento/Checkout/Test/Mftf/ActionGroup/StorefrontOpenMiniCartActionGroup/StorefrontOpenMiniCartActionGroup.xml @@ -5,9 +5,8 @@ * See COPYING.txt for license details. */ --> - <actionGroups xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" - xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/actionGroupSchema.xsd"> + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/actionGroupSchema.xsd"> <actionGroup name="StorefrontOpenMiniCartActionGroup"> <annotations> <description>Clicks on the Mini Shopping Cart icon in the Storefront Header.</description> diff --git a/app/code/Magento/Checkout/Test/Mftf/ActionGroup/StorefrontOpenOrderFromSuccessPageActionGroup.xml b/app/code/Magento/Checkout/Test/Mftf/ActionGroup/StorefrontOpenOrderFromSuccessPageActionGroup.xml new file mode 100644 index 0000000000000..80c9ff0a45135 --- /dev/null +++ b/app/code/Magento/Checkout/Test/Mftf/ActionGroup/StorefrontOpenOrderFromSuccessPageActionGroup.xml @@ -0,0 +1,23 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + /** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +--> + +<actionGroups xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/actionGroupSchema.xsd"> + <actionGroup name="StorefrontOpenOrderFromSuccessPageActionGroup"> + <annotations> + <description>Click order number link from checkout success page and check order number on order view page.</description> + </annotations> + <arguments> + <argument name="orderNumber" defaultValue="000000001" type="string"/> + </arguments> + + <click selector="{{CheckoutSuccessMainSection.orderLinkByOrderNumber(orderNumber)}}" stepKey="clickOrderNumberLink"/> + <waitForPageLoad stepKey="waitForOrderPageIsLoaded"/> + <see userInput="Order # {{orderNumber}}" selector="{{StorefrontCustomerOrderViewSection.orderTitle}}" stepKey="assertOrderNumberIsCorrect"/> + </actionGroup> +</actionGroups> diff --git a/app/code/Magento/Checkout/Test/Mftf/ActionGroup/StorefrontProductPageAddSimpleProductToCartActionGroup.xml b/app/code/Magento/Checkout/Test/Mftf/ActionGroup/StorefrontProductPageAddSimpleProductToCartActionGroup.xml new file mode 100644 index 0000000000000..202f8989dce0f --- /dev/null +++ b/app/code/Magento/Checkout/Test/Mftf/ActionGroup/StorefrontProductPageAddSimpleProductToCartActionGroup.xml @@ -0,0 +1,15 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. +--> + +<actionGroups xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/actionGroupSchema.xsd"> + <actionGroup name="StorefrontProductPageAddSimpleProductToCartActionGroup"> + <click selector="{{StorefrontProductPageSection.addToCartBtn}}" stepKey="addToCart"/> + <waitForElementVisible selector="{{StorefrontProductActionSection.addToCartButtonTitleIsAddToCart}}" stepKey="waitForElementVisibleAddToCartButtonTitleIsAddToCart"/> + <waitForPageLoad stepKey="waitForPageLoad"/> + </actionGroup> +</actionGroups> diff --git a/app/code/Magento/Checkout/Test/Mftf/ActionGroup/StorefrontSearchAddressInSelectBillingAddressPopupOnPaymentStepOnCheckoutActionGroup.xml b/app/code/Magento/Checkout/Test/Mftf/ActionGroup/StorefrontSearchAddressInSelectBillingAddressPopupOnPaymentStepOnCheckoutActionGroup.xml new file mode 100644 index 0000000000000..845e0b7733fca --- /dev/null +++ b/app/code/Magento/Checkout/Test/Mftf/ActionGroup/StorefrontSearchAddressInSelectBillingAddressPopupOnPaymentStepOnCheckoutActionGroup.xml @@ -0,0 +1,24 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + /** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +--> + +<actionGroups xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/actionGroupSchema.xsd"> + <actionGroup name="StorefrontSearchAddressInSelectBillingAddressPopupOnPaymentStepOnCheckoutActionGroup"> + <annotations> + <description>Search customer address in 'Select Billing Address' popup on the Storefront Checkout on the 'Payment' step.</description> + </annotations> + <arguments> + <argument name="addressSearchTerm" type="string" defaultValue="10001"/> + </arguments> + + <fillField selector="{{CheckoutBillingAddressSearchSection.searchAddressField}}" userInput="{{addressSearchTerm}}" stepKey="fillAddressInSearchField"/> + <click selector="{{CheckoutBillingAddressSearchSection.searchAddressButton}}" stepKey="clickSearchAddressButton"/> + <see selector="{{CheckoutBillingAddressSearchSection.addressesFound}}" userInput="1 addresses" stepKey="assertOneAddressesWasFound"/> + <see selector="{{CheckoutBillingAddressSearchSection.firstBillingAddressItem}}" userInput="{{addressSearchTerm}}" stepKey="verifyAddresses"/> + </actionGroup> +</actionGroups> diff --git a/app/code/Magento/Checkout/Test/Mftf/ActionGroup/StorefrontSelectOptionDropDownActionGroup.xml b/app/code/Magento/Checkout/Test/Mftf/ActionGroup/StorefrontSelectOptionDropDownActionGroup.xml deleted file mode 100644 index 0e36e0ecbe71f..0000000000000 --- a/app/code/Magento/Checkout/Test/Mftf/ActionGroup/StorefrontSelectOptionDropDownActionGroup.xml +++ /dev/null @@ -1,22 +0,0 @@ -<?xml version="1.0" encoding="UTF-8"?> -<!-- - /** - * Copyright © Magento, Inc. All rights reserved. - * See COPYING.txt for license details. - */ ---> - -<actionGroups xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" - xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/actionGroupSchema.xsd"> - <actionGroup name="StorefrontSelectOptionDropDownActionGroup"> - <annotations> - <description>Selects the provided Product Option Value under the provided Product Option Title on a Storefront Product page.</description> - </annotations> - <arguments> - <argument name="optionTitle" defaultValue="ProductOptionDropDown"/> - <argument name="option" defaultValue="ProductOptionValueDropdown2.title"/> - </arguments> - - <selectOption selector="{{StorefrontProductInfoMainSection.productOptionSelect(optionTitle.title)}}" userInput="{{option}}" stepKey="fillOptionDropDown"/> - </actionGroup> -</actionGroups> diff --git a/app/code/Magento/Checkout/Test/Mftf/ActionGroup/StorefrontSelectOptionRadioButtonActionGroup.xml b/app/code/Magento/Checkout/Test/Mftf/ActionGroup/StorefrontSelectOptionRadioButtonActionGroup.xml index c0de6f8f8466f..ba75a03d6ff04 100644 --- a/app/code/Magento/Checkout/Test/Mftf/ActionGroup/StorefrontSelectOptionRadioButtonActionGroup.xml +++ b/app/code/Magento/Checkout/Test/Mftf/ActionGroup/StorefrontSelectOptionRadioButtonActionGroup.xml @@ -10,7 +10,7 @@ xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/actionGroupSchema.xsd"> <actionGroup name="StorefrontSelectOptionRadioButtonActionGroup"> <annotations> - <description>Checks the provided Product Option radio button for the provided Product Option Price on a Storefront Product page.</description> + <description>DEPRECATED. Please use StorefrontProductPageSelectRadioButtonOptionValueActionGroup instead. Checks the provided Product Option radio button for the provided Product Option Price on a Storefront Product page.</description> </annotations> <arguments> <argument name="optionTitle" defaultValue="ProductOptionRadiobutton"/> diff --git a/app/code/Magento/Checkout/Test/Mftf/Page/CheckoutPage.xml b/app/code/Magento/Checkout/Test/Mftf/Page/CheckoutPage.xml index d6173dfa17916..a021ce6c93e63 100644 --- a/app/code/Magento/Checkout/Test/Mftf/Page/CheckoutPage.xml +++ b/app/code/Magento/Checkout/Test/Mftf/Page/CheckoutPage.xml @@ -9,6 +9,7 @@ <pages xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="urn:magento:mftf:Page/etc/PageObject.xsd"> <page name="CheckoutPage" url="/checkout" area="storefront" module="Magento_Checkout"> + <section name="StorefrontCheckoutPageMessagesSection"/> <section name="CheckoutShippingSection"/> <section name="CheckoutShippingMethodsSection"/> <section name="CheckoutOrderSummarySection"/> diff --git a/app/code/Magento/Checkout/Test/Mftf/Section/CheckoutHeaderSection.xml b/app/code/Magento/Checkout/Test/Mftf/Section/CheckoutHeaderSection.xml index babbe51746df8..615db79b2ba6d 100644 --- a/app/code/Magento/Checkout/Test/Mftf/Section/CheckoutHeaderSection.xml +++ b/app/code/Magento/Checkout/Test/Mftf/Section/CheckoutHeaderSection.xml @@ -9,5 +9,6 @@ <section name="CheckoutHeaderSection"> <element name="shippingMethodStep" type="text" selector=".opc-progress-bar-item:nth-of-type(1)"/> <element name="reviewAndPaymentsStep" type="text" selector=".opc-progress-bar-item:nth-of-type(2)"/> + <element name="errorMessageContainsText" type="text" selector="//div[contains(@class, 'message message-error error')]//div[contains(text(), '{{text}}')]" parameterized="true"/> </section> </sections> diff --git a/app/code/Magento/Checkout/Test/Mftf/Section/CheckoutSuccessMainSection.xml b/app/code/Magento/Checkout/Test/Mftf/Section/CheckoutSuccessMainSection.xml index c486e13ecf58b..d15b89e58a550 100644 --- a/app/code/Magento/Checkout/Test/Mftf/Section/CheckoutSuccessMainSection.xml +++ b/app/code/Magento/Checkout/Test/Mftf/Section/CheckoutSuccessMainSection.xml @@ -20,5 +20,6 @@ <element name="createAnAccount" type="button" selector="[data-bind*="i18n: 'Create an Account'"]" timeout="30"/> <element name="printLink" type="button" selector=".print" timeout="30"/> <element name="orderNumberWithoutLink" type="text" selector="//div[contains(@class, 'checkout-success')]//p/span"/> + <element name="orderLinkByOrderNumber" type="text" selector="//div[contains(@class,'success')]//a[contains(.,'{{orderNumber}}')]" parameterized="true" timeout="30"/> </section> </sections> diff --git a/app/code/Magento/Checkout/Test/Mftf/Section/StorefrontCheckoutPageMessagesSection.xml b/app/code/Magento/Checkout/Test/Mftf/Section/StorefrontCheckoutPageMessagesSection.xml new file mode 100644 index 0000000000000..4cedf837918c5 --- /dev/null +++ b/app/code/Magento/Checkout/Test/Mftf/Section/StorefrontCheckoutPageMessagesSection.xml @@ -0,0 +1,14 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + /** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +--> + +<sections xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Page/etc/SectionObject.xsd"> + <section name="StorefrontCheckoutPageMessagesSection"> + <element name="successMessage" type="text" selector="//div[@id='checkout']//div[@data-role='checkout-messages']//div[contains(@class,'message-success')]//div[contains(.,'{{message}}')]" parameterized="true"/> + </section> +</sections> diff --git a/app/code/Magento/Checkout/Test/Mftf/Test/AddressStateFieldShouldNotAcceptJustIntegerValuesTest.xml b/app/code/Magento/Checkout/Test/Mftf/Test/AddressStateFieldShouldNotAcceptJustIntegerValuesTest.xml index f3e31d23f715b..dd454d7aca10b 100644 --- a/app/code/Magento/Checkout/Test/Mftf/Test/AddressStateFieldShouldNotAcceptJustIntegerValuesTest.xml +++ b/app/code/Magento/Checkout/Test/Mftf/Test/AddressStateFieldShouldNotAcceptJustIntegerValuesTest.xml @@ -23,6 +23,8 @@ <createData entity="ApiSimpleProduct" stepKey="createProduct"> <requiredEntity createDataKey="createCategory"/> </createData> + <magentoCLI command="indexer:reindex" stepKey="reindex"/> + <magentoCLI command="cache:flush" stepKey="flushCache"/> </before> <after> <deleteData createDataKey="createCategory" stepKey="deleteCategory"/> diff --git a/app/code/Magento/Checkout/Test/Mftf/Test/AdminCheckConfigsChangesIsNotAffectedStartedCheckoutProcessTest.xml b/app/code/Magento/Checkout/Test/Mftf/Test/AdminCheckConfigsChangesIsNotAffectedStartedCheckoutProcessTest.xml index 31a9d011a91c7..ab0453e1faa18 100644 --- a/app/code/Magento/Checkout/Test/Mftf/Test/AdminCheckConfigsChangesIsNotAffectedStartedCheckoutProcessTest.xml +++ b/app/code/Magento/Checkout/Test/Mftf/Test/AdminCheckConfigsChangesIsNotAffectedStartedCheckoutProcessTest.xml @@ -37,7 +37,7 @@ <deleteData createDataKey="createProduct" stepKey="deleteProduct"/> <!-- Log out --> - <actionGroup ref="logout" stepKey="logout"/> + <actionGroup ref="AdminLogoutActionGroup" stepKey="logout"/> </after> <!-- Add product to cart --> @@ -75,7 +75,7 @@ <!-- Open new browser's window and login as Admin --> <openNewTab stepKey="openNewTab"/> - <actionGroup ref="LoginAsAdmin" stepKey="loginAsAdmin"/> + <actionGroup ref="AdminLoginActionGroup" stepKey="loginAsAdmin"/> <!-- Go to Store > Configuration > Sales > Shipping Methods --> <actionGroup ref="AdminOpenShippingMethodsConfigPageActionGroup" stepKey="openShippingMethodConfigPage"/> diff --git a/app/code/Magento/Checkout/Test/Mftf/Test/CheckCheckoutSuccessPageTest.xml b/app/code/Magento/Checkout/Test/Mftf/Test/CheckCheckoutSuccessPageTest.xml deleted file mode 100644 index 971af9688e754..0000000000000 --- a/app/code/Magento/Checkout/Test/Mftf/Test/CheckCheckoutSuccessPageTest.xml +++ /dev/null @@ -1,219 +0,0 @@ -<?xml version="1.0" encoding="UTF-8"?> -<!-- - /** - * Copyright © Magento, Inc. All rights reserved. - * See COPYING.txt for license details. - */ ---> - -<tests xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" - xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/testSchema.xsd"> - <test name="CheckCheckoutSuccessPageAsRegisterCustomer"> - <annotations> - <features value="Checkout"/> - <stories value="Success page elements are presented for placed order as Customer"/> - <title value="Customer Checkout"/> - <description value="To be sure that other elements of Success page are shown for placed order as registered Customer."/> - <severity value="CRITICAL"/> - <testCaseId value="MC-16488"/> - <group value="checkout"/> - </annotations> - - <before> - <createData entity="SimpleTwo" stepKey="createSimpleProduct"/> - <createData entity="Simple_US_Customer" stepKey="createSimpleUsCustomer"> - <field key="group_id">1</field> - </createData> - </before> - - <after> - <!--Logout from customer account--> - <amOnPage url="customer/account/logout/" stepKey="logoutCustomerOne"/> - <waitForPageLoad stepKey="waitLogoutCustomerOne"/> - <actionGroup ref="logout" stepKey="adminLogout"/> - <deleteData createDataKey="createSimpleProduct" stepKey="deleteProduct"/> - <deleteData createDataKey="createSimpleUsCustomer" stepKey="deleteCustomer"/> - </after> - - <!--Log in to Storefront as Customer--> - <actionGroup ref="LoginToStorefrontActionGroup" stepKey="signUpNewUser"> - <argument name="Customer" value="$$createSimpleUsCustomer$$"/> - </actionGroup> - - <!--Go to product page--> - <amOnPage url="$$createSimpleProduct.custom_attributes[url_key]$$.html" stepKey="navigateToSimpleProductPage"/> - <waitForPageLoad stepKey="waitForCatalogPageLoad"/> - - <!--Add Product to Shopping Cart--> - <actionGroup ref="AddToCartFromStorefrontProductPageActionGroup" stepKey="addToCartFromStorefrontProductPage"> - <argument name="productName" value="$$createSimpleProduct.name$$"/> - </actionGroup> - - <!--Go to Checkout--> - <actionGroup ref="GoToCheckoutFromMinicartActionGroup" stepKey="goToCheckoutFromMinicart"/> - <click selector="{{CheckoutShippingMethodsSection.firstShippingMethod}}" stepKey="selectFirstShippingMethod"/> - <waitForLoadingMaskToDisappear stepKey="waitForLoadingMask2"/> - <waitForElement selector="{{CheckoutShippingMethodsSection.next}}" time="30" stepKey="waitForNextButton"/> - <click selector="{{CheckoutShippingMethodsSection.next}}" stepKey="clickNext"/> - <!-- Checkout select Check/Money Order payment --> - <actionGroup ref="CheckoutSelectCheckMoneyOrderPaymentActionGroup" stepKey="selectCheckMoneyPayment"/> - <!--Click Place Order button--> - <click selector="{{CheckoutPaymentSection.placeOrder}}" stepKey="clickPlaceOrder"/> - <see selector="{{CheckoutSuccessMainSection.successTitle}}" userInput="Thank you for your purchase!" stepKey="seeSuccessTitle"/> - <see selector="{{CheckoutSuccessMainSection.orderNumberText}}" userInput="Your order number is: " stepKey="seeOrderNumber"/> - <see selector="{{CheckoutSuccessMainSection.success}}" userInput="We'll email you an order confirmation with details and tracking info." stepKey="seeSuccessNotify"/> - - <click selector="{{CheckoutSuccessMainSection.orderLink}}" stepKey="clickOrderLink"/> - <seeInCurrentUrl url="{{StorefrontCustomerOrderPage.url}}" stepKey="seeMyOrderPage"/> - - <!--Go to product page--> - <amOnPage url="$$createSimpleProduct.custom_attributes[url_key]$$.html" stepKey="navigateToSimpleProductPage2"/> - <waitForPageLoad stepKey="waitForCatalogPageLoad2"/> - - <!--Add Product to Shopping Cart--> - <actionGroup ref="AddToCartFromStorefrontProductPageActionGroup" stepKey="addToCartFromStorefrontProductPage2"> - <argument name="productName" value="$$createSimpleProduct.name$$"/> - </actionGroup> - - <!--Go to Checkout--> - <actionGroup ref="GoToCheckoutFromMinicartActionGroup" stepKey="goToCheckoutFromMinicart2"/> - <click selector="{{CheckoutShippingMethodsSection.firstShippingMethod}}" stepKey="selectFirstShippingMethod2"/> - <waitForLoadingMaskToDisappear stepKey="waitForLoadingMask3"/> - <waitForElement selector="{{CheckoutShippingMethodsSection.next}}" time="30" stepKey="waitForNextButton2"/> - <click selector="{{CheckoutShippingMethodsSection.next}}" stepKey="clickNext2"/> - <!-- Checkout select Check/Money Order payment --> - <actionGroup ref="CheckoutSelectCheckMoneyOrderPaymentActionGroup" stepKey="selectCheckMoneyPayment2"/> - - <!--Click Place Order button--> - <click selector="{{CheckoutPaymentSection.placeOrder}}" stepKey="clickPlaceOrder2"/> - <see selector="{{CheckoutSuccessMainSection.successTitle}}" userInput="Thank you for your purchase!" stepKey="waitForLoadSuccessPage2"/> - <click selector="{{CheckoutSuccessMainSection.continueShoppingButton}}" stepKey="clickContinueShoppingButton"/> - <see userInput="Home Page" selector="{{StorefrontCMSPageSection.mainTitle}}" stepKey="seeHomePageTitle"/> - <seeCurrentUrlEquals url="{{_ENV.MAGENTO_BASE_URL}}" stepKey="seeHomePageUrl"/> - - <!--Go to product page--> - <amOnPage url="$$createSimpleProduct.custom_attributes[url_key]$$.html" stepKey="navigateToSimpleProductPage3"/> - <waitForPageLoad stepKey="waitForCatalogPageLoad3"/> - - <!--Add Product to Shopping Cart--> - <actionGroup ref="AddToCartFromStorefrontProductPageActionGroup" stepKey="addToCartFromStorefrontProductPage3"> - <argument name="productName" value="$$createSimpleProduct.name$$"/> - </actionGroup> - - <!--Go to Checkout--> - <actionGroup ref="GoToCheckoutFromMinicartActionGroup" stepKey="goToCheckoutFromMinicart3"/> - <click selector="{{CheckoutShippingMethodsSection.firstShippingMethod}}" stepKey="selectFirstShippingMethod3"/> - <waitForLoadingMaskToDisappear stepKey="waitForLoadingMask4"/> - <waitForElement selector="{{CheckoutShippingMethodsSection.next}}" time="30" stepKey="waitForNextButton3"/> - <click selector="{{CheckoutShippingMethodsSection.next}}" stepKey="clickNext3"/> - - <!-- Checkout select Check/Money Order payment --> - <actionGroup ref="CheckoutSelectCheckMoneyOrderPaymentActionGroup" stepKey="selectCheckMoneyPayment3"/> - - <!--Click Place Order button--> - <click selector="{{CheckoutPaymentSection.placeOrder}}" stepKey="clickPlaceOrder3"/> - <see selector="{{CheckoutSuccessMainSection.successTitle}}" userInput="Thank you for your purchase!" stepKey="waitForLoadSuccessPage3"/> - - <!--Check "Print Receipt" button is presented (desktop only)--> - <seeElement selector="{{CheckoutSuccessMainSection.printLink}}" stepKey="seeVisiblePrint"/> - <resizeWindow width="600" height="800" stepKey="resizeWindow"/> - <waitForElementNotVisible selector="{{CheckoutSuccessMainSection.printLink}}" stepKey="waitInvisiblePrint"/> - <dontSeeElement selector="{{CheckoutSuccessMainSection.printLink}}" stepKey="seeInvisiblePrint"/> - <resizeWindow width="1360" height="1020" stepKey="maximizeWindowKey1"/> - <waitForElementVisible selector="{{CheckoutSuccessMainSection.printLink}}" stepKey="waitVisiblePrint"/> - <seeElement selector="{{CheckoutSuccessMainSection.printLink}}" stepKey="seeVisiblePrint2" /> - - <!--See print page--> - <click selector="{{CheckoutSuccessMainSection.printLink}}" stepKey="clickPrintLink"/> - <switchToWindow stepKey="switchToWindow"/> - <switchToNextTab stepKey="switchToTab"/> - <seeInCurrentUrl url="sales/order/print/order_id" stepKey="seePrintPage"/> - <seeElement selector="{{StorefrontCustomerOrderViewSection.orderTitle}}" stepKey="seeOrderTitleOnPrint"/> - <switchToWindow stepKey="switchToWindow2"/> - </test> - <test name="CheckCheckoutSuccessPageAsGuest"> - <annotations> - <features value="Checkout"/> - <stories value="Success page elements are presented for placed order as Guest"/> - <title value="Guest Checkout - elements of success page are presented for placed order as guest"/> - <description value="To be sure that other elements of Success page are presented for placed order as Guest"/> - <severity value="CRITICAL"/> - <testCaseId value="MC-16490"/> - <group value="checkout"/> - </annotations> - - <before> - <createData entity="SimpleTwo" stepKey="createSimpleProduct"/> - </before> - - <after> - <actionGroup ref="logout" stepKey="adminLogout"/> - <deleteData createDataKey="createSimpleProduct" stepKey="deleteProduct"/> - </after> - - <!--Go to product page--> - <amOnPage url="$$createSimpleProduct.custom_attributes[url_key]$$.html" stepKey="navigateToSimpleProductPage"/> - <waitForPageLoad stepKey="waitForCatalogPageLoad"/> - - <!--Add Product to Shopping Cart--> - <actionGroup ref="AddToCartFromStorefrontProductPageActionGroup" stepKey="addToCartFromStorefrontProductPage"> - <argument name="productName" value="$$createSimpleProduct.name$$"/> - </actionGroup> - - <!--Go to Checkout--> - <actionGroup ref="GoToCheckoutFromMinicartActionGroup" stepKey="goToCheckoutFromMinicart"/> - - <actionGroup ref="GuestCheckoutFillingShippingSectionActionGroup" stepKey="guestCheckoutFillingShippingSection"> - <argument name="customerVar" value="CustomerEntityOne" /> - <argument name="customerAddressVar" value="CustomerAddressSimple" /> - </actionGroup> - - <!-- Checkout select Check/Money Order payment --> - <actionGroup ref="CheckoutSelectCheckMoneyOrderPaymentActionGroup" stepKey="selectCheckMoneyPayment"/> - - <!--Click Place Order button--> - <click selector="{{CheckoutPaymentSection.placeOrder}}" stepKey="clickPlaceOrder"/> - <see selector="{{CheckoutSuccessMainSection.successTitle}}" userInput="Thank you for your purchase!" stepKey="waitForLoadSuccessPage"/> - - <!--See success messages--> - <see selector="{{CheckoutSuccessMainSection.successTitle}}" userInput="Thank you for your purchase!" stepKey="seeSuccessTitle"/> - <see selector="{{CheckoutSuccessMainSection.orderNumberText}}" userInput="Your order # is: " stepKey="seeOrderNumber"/> - - <!--Check register section--> - <see selector="{{CheckoutSuccessMainSection.success}}" userInput="We'll email you an order confirmation with details and tracking info." stepKey="seeSuccessNotify"/> - <see selector="{{CheckoutSuccessRegisterSection.registerMessage}}" userInput="You can track your order status by creating an account." stepKey="seeRegisterMessage"/> - <see selector="{{CheckoutSuccessRegisterSection.customerEmail}}" userInput="Email Address: {{CustomerEntityOne.email}}" stepKey="seeCustomerEmail"/> - <seeElement selector="{{CheckoutSuccessRegisterSection.createAccountButton}}" stepKey="seeVisibleCreateAccountButton"/> - <click selector="{{CheckoutSuccessRegisterSection.createAccountButton}}" stepKey="clickCreateAccountButton"/> - <seeInCurrentUrl url="{{StorefrontCustomerCreatePage.url}}" stepKey="seeCreateAccountPage"/> - <see userInput="Create New Customer Account" selector="{{StorefrontCMSPageSection.mainTitle}}" stepKey="seeCreateAccountPageTitle"/> - - <!--Go to product page--> - <amOnPage url="$$createSimpleProduct.custom_attributes[url_key]$$.html" stepKey="navigateToSimpleProductPage2"/> - <waitForPageLoad stepKey="waitForCatalogPageLoad2"/> - - <!--Add Product to Shopping Cart--> - <actionGroup ref="AddToCartFromStorefrontProductPageActionGroup" stepKey="addToCartFromStorefrontProductPage2"> - <argument name="productName" value="$$createSimpleProduct.name$$"/> - </actionGroup> - - <!--Go to Checkout--> - <actionGroup ref="GoToCheckoutFromMinicartActionGroup" stepKey="goToCheckoutFromMinicart2"/> - <actionGroup ref="GuestCheckoutFillingShippingSectionActionGroup" stepKey="guestCheckoutFillingShippingSection2"> - <argument name="customerVar" value="CustomerEntityOne" /> - <argument name="customerAddressVar" value="CustomerAddressSimple" /> - </actionGroup> - - <!-- Checkout select Check/Money Order payment --> - <actionGroup ref="CheckoutSelectCheckMoneyOrderPaymentActionGroup" stepKey="selectCheckMoneyPayment2"/> - - <!--Click Place Order button--> - <click selector="{{CheckoutPaymentSection.placeOrder}}" stepKey="clickPlaceOrder2"/> - <see selector="{{CheckoutSuccessMainSection.successTitle}}" userInput="Thank you for your purchase!" stepKey="waitForLoadSuccessPage2"/> - - <!--Continue shopping--> - <click selector="{{CheckoutSuccessMainSection.continueShoppingButton}}" stepKey="clickContinueShoppingButton"/> - <seeCurrentUrlEquals url="{{_ENV.MAGENTO_BASE_URL}}" stepKey="seeHomePageUrl"/> - <see userInput="Home Page" selector="{{StorefrontCMSPageSection.mainTitle}}" stepKey="seeHomePageTitle"/> - </test> -</tests> diff --git a/app/code/Magento/Checkout/Test/Mftf/Test/CheckCheckoutSuccessPageTest/CheckCheckoutSuccessPageAsGuestTest.xml b/app/code/Magento/Checkout/Test/Mftf/Test/CheckCheckoutSuccessPageTest/CheckCheckoutSuccessPageAsGuestTest.xml new file mode 100644 index 0000000000000..febeaa05be43e --- /dev/null +++ b/app/code/Magento/Checkout/Test/Mftf/Test/CheckCheckoutSuccessPageTest/CheckCheckoutSuccessPageAsGuestTest.xml @@ -0,0 +1,95 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + /** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +--> +<tests xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/testSchema.xsd"> + <test name="CheckCheckoutSuccessPageAsGuestTest"> + <annotations> + <features value="Checkout"/> + <stories value="Success page elements are presented for placed order as Guest"/> + <title value="Guest Checkout - elements of success page are presented for placed order as guest"/> + <description value="To be sure that other elements of Success page are presented for placed order as Guest"/> + <severity value="CRITICAL"/> + <testCaseId value="MC-16490"/> + <group value="checkout"/> + </annotations> + + <before> + <createData entity="SimpleTwo" stepKey="createSimpleProduct"/> + </before> + + <after> + <actionGroup ref="AdminLogoutActionGroup" stepKey="adminLogout"/> + <deleteData createDataKey="createSimpleProduct" stepKey="deleteProduct"/> + </after> + + <!--Go to product page--> + <amOnPage url="$$createSimpleProduct.custom_attributes[url_key]$$.html" stepKey="navigateToSimpleProductPage"/> + <waitForPageLoad stepKey="waitForCatalogPageLoad"/> + + <!--Add Product to Shopping Cart--> + <actionGroup ref="AddToCartFromStorefrontProductPageActionGroup" stepKey="addToCartFromStorefrontProductPage"> + <argument name="productName" value="$$createSimpleProduct.name$$"/> + </actionGroup> + + <!--Go to Checkout--> + <actionGroup ref="GoToCheckoutFromMinicartActionGroup" stepKey="goToCheckoutFromMinicart"/> + + <actionGroup ref="GuestCheckoutFillingShippingSectionActionGroup" stepKey="guestCheckoutFillingShippingSection"> + <argument name="customerVar" value="CustomerEntityOne"/> + <argument name="customerAddressVar" value="CustomerAddressSimple"/> + </actionGroup> + + <!-- Checkout select Check/Money Order payment --> + <actionGroup ref="CheckoutSelectCheckMoneyOrderPaymentActionGroup" stepKey="selectCheckMoneyPayment"/> + + <!--Click Place Order button--> + <click selector="{{CheckoutPaymentSection.placeOrder}}" stepKey="clickPlaceOrder"/> + <see selector="{{CheckoutSuccessMainSection.successTitle}}" userInput="Thank you for your purchase!" stepKey="waitForLoadSuccessPage"/> + + <!--See success messages--> + <see selector="{{CheckoutSuccessMainSection.successTitle}}" userInput="Thank you for your purchase!" stepKey="seeSuccessTitle"/> + <see selector="{{CheckoutSuccessMainSection.orderNumberText}}" userInput="Your order # is: " stepKey="seeOrderNumber"/> + + <!--Check register section--> + <see selector="{{CheckoutSuccessMainSection.success}}" userInput="We'll email you an order confirmation with details and tracking info." stepKey="seeSuccessNotify"/> + <see selector="{{CheckoutSuccessRegisterSection.registerMessage}}" userInput="You can track your order status by creating an account." stepKey="seeRegisterMessage"/> + <see selector="{{CheckoutSuccessRegisterSection.customerEmail}}" userInput="Email Address: {{CustomerEntityOne.email}}" stepKey="seeCustomerEmail"/> + <seeElement selector="{{CheckoutSuccessRegisterSection.createAccountButton}}" stepKey="seeVisibleCreateAccountButton"/> + <click selector="{{CheckoutSuccessRegisterSection.createAccountButton}}" stepKey="clickCreateAccountButton"/> + <seeInCurrentUrl url="{{StorefrontCustomerCreatePage.url}}" stepKey="seeCreateAccountPage"/> + <see userInput="Create New Customer Account" selector="{{StorefrontCMSPageSection.mainTitle}}" stepKey="seeCreateAccountPageTitle"/> + + <!--Go to product page--> + <amOnPage url="$$createSimpleProduct.custom_attributes[url_key]$$.html" stepKey="navigateToSimpleProductPage2"/> + <waitForPageLoad stepKey="waitForCatalogPageLoad2"/> + + <!--Add Product to Shopping Cart--> + <actionGroup ref="AddToCartFromStorefrontProductPageActionGroup" stepKey="addToCartFromStorefrontProductPage2"> + <argument name="productName" value="$$createSimpleProduct.name$$"/> + </actionGroup> + + <!--Go to Checkout--> + <actionGroup ref="GoToCheckoutFromMinicartActionGroup" stepKey="goToCheckoutFromMinicart2"/> + <actionGroup ref="GuestCheckoutFillingShippingSectionActionGroup" stepKey="guestCheckoutFillingShippingSection2"> + <argument name="customerVar" value="CustomerEntityOne"/> + <argument name="customerAddressVar" value="CustomerAddressSimple"/> + </actionGroup> + + <!-- Checkout select Check/Money Order payment --> + <actionGroup ref="CheckoutSelectCheckMoneyOrderPaymentActionGroup" stepKey="selectCheckMoneyPayment2"/> + + <!--Click Place Order button--> + <click selector="{{CheckoutPaymentSection.placeOrder}}" stepKey="clickPlaceOrder2"/> + <see selector="{{CheckoutSuccessMainSection.successTitle}}" userInput="Thank you for your purchase!" stepKey="waitForLoadSuccessPage2"/> + + <!--Continue shopping--> + <click selector="{{CheckoutSuccessMainSection.continueShoppingButton}}" stepKey="clickContinueShoppingButton"/> + <seeCurrentUrlEquals url="{{_ENV.MAGENTO_BASE_URL}}" stepKey="seeHomePageUrl"/> + <see userInput="Home Page" selector="{{StorefrontCMSPageSection.mainTitle}}" stepKey="seeHomePageTitle"/> + </test> +</tests> diff --git a/app/code/Magento/Checkout/Test/Mftf/Test/CheckCheckoutSuccessPageTest/CheckCheckoutSuccessPageAsRegisterCustomerTest.xml b/app/code/Magento/Checkout/Test/Mftf/Test/CheckCheckoutSuccessPageTest/CheckCheckoutSuccessPageAsRegisterCustomerTest.xml new file mode 100644 index 0000000000000..a8bdde867a445 --- /dev/null +++ b/app/code/Magento/Checkout/Test/Mftf/Test/CheckCheckoutSuccessPageTest/CheckCheckoutSuccessPageAsRegisterCustomerTest.xml @@ -0,0 +1,133 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + /** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +--> +<tests xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/testSchema.xsd"> + <test name="CheckCheckoutSuccessPageAsRegisterCustomerTest"> + <annotations> + <features value="Checkout"/> + <stories value="Success page elements are presented for placed order as Customer"/> + <title value="Customer Checkout"/> + <description value="To be sure that other elements of Success page are shown for placed order as registered Customer."/> + <severity value="CRITICAL"/> + <testCaseId value="MC-16488"/> + <group value="checkout"/> + </annotations> + + <before> + <createData entity="SimpleTwo" stepKey="createSimpleProduct"/> + <createData entity="Simple_US_Customer" stepKey="createSimpleUsCustomer"> + <field key="group_id">1</field> + </createData> + </before> + + <after> + <!--Logout from customer account--> + <amOnPage url="{{StorefrontCustomerLogoutPage.url}}" stepKey="logoutCustomerOne"/> + <waitForPageLoad stepKey="waitLogoutCustomerOne"/> + <actionGroup ref="AdminLogoutActionGroup" stepKey="adminLogout"/> + <deleteData createDataKey="createSimpleProduct" stepKey="deleteProduct"/> + <deleteData createDataKey="createSimpleUsCustomer" stepKey="deleteCustomer"/> + </after> + + <!--Log in to Storefront as Customer--> + <actionGroup ref="LoginToStorefrontActionGroup" stepKey="signUpNewUser"> + <argument name="Customer" value="$$createSimpleUsCustomer$$"/> + </actionGroup> + + <!--Go to product page--> + <amOnPage url="$$createSimpleProduct.custom_attributes[url_key]$$.html" stepKey="navigateToSimpleProductPage"/> + <waitForPageLoad stepKey="waitForCatalogPageLoad"/> + + <!--Add Product to Shopping Cart--> + <actionGroup ref="AddToCartFromStorefrontProductPageActionGroup" stepKey="addToCartFromStorefrontProductPage"> + <argument name="productName" value="$$createSimpleProduct.name$$"/> + </actionGroup> + + <!--Go to Checkout--> + <actionGroup ref="GoToCheckoutFromMinicartActionGroup" stepKey="goToCheckoutFromMinicart"/> + <click selector="{{CheckoutShippingMethodsSection.firstShippingMethod}}" stepKey="selectFirstShippingMethod"/> + <waitForLoadingMaskToDisappear stepKey="waitForLoadingMask2"/> + <waitForElement selector="{{CheckoutShippingMethodsSection.next}}" time="30" stepKey="waitForNextButton"/> + <click selector="{{CheckoutShippingMethodsSection.next}}" stepKey="clickNext"/> + <!-- Checkout select Check/Money Order payment --> + <actionGroup ref="CheckoutSelectCheckMoneyOrderPaymentActionGroup" stepKey="selectCheckMoneyPayment"/> + <!--Click Place Order button--> + <click selector="{{CheckoutPaymentSection.placeOrder}}" stepKey="clickPlaceOrder"/> + <see selector="{{CheckoutSuccessMainSection.successTitle}}" userInput="Thank you for your purchase!" stepKey="seeSuccessTitle"/> + <see selector="{{CheckoutSuccessMainSection.orderNumberText}}" userInput="Your order number is: " stepKey="seeOrderNumber"/> + <see selector="{{CheckoutSuccessMainSection.success}}" userInput="We'll email you an order confirmation with details and tracking info." stepKey="seeSuccessNotify"/> + + <click selector="{{CheckoutSuccessMainSection.orderLink}}" stepKey="clickOrderLink"/> + <seeInCurrentUrl url="{{StorefrontCustomerOrderPage.url}}" stepKey="seeMyOrderPage"/> + + <!--Go to product page--> + <amOnPage url="$$createSimpleProduct.custom_attributes[url_key]$$.html" stepKey="navigateToSimpleProductPage2"/> + <waitForPageLoad stepKey="waitForCatalogPageLoad2"/> + + <!--Add Product to Shopping Cart--> + <actionGroup ref="AddToCartFromStorefrontProductPageActionGroup" stepKey="addToCartFromStorefrontProductPage2"> + <argument name="productName" value="$$createSimpleProduct.name$$"/> + </actionGroup> + + <!--Go to Checkout--> + <actionGroup ref="GoToCheckoutFromMinicartActionGroup" stepKey="goToCheckoutFromMinicart2"/> + <click selector="{{CheckoutShippingMethodsSection.firstShippingMethod}}" stepKey="selectFirstShippingMethod2"/> + <waitForLoadingMaskToDisappear stepKey="waitForLoadingMask3"/> + <waitForElement selector="{{CheckoutShippingMethodsSection.next}}" time="30" stepKey="waitForNextButton2"/> + <click selector="{{CheckoutShippingMethodsSection.next}}" stepKey="clickNext2"/> + <!-- Checkout select Check/Money Order payment --> + <actionGroup ref="CheckoutSelectCheckMoneyOrderPaymentActionGroup" stepKey="selectCheckMoneyPayment2"/> + + <!--Click Place Order button--> + <click selector="{{CheckoutPaymentSection.placeOrder}}" stepKey="clickPlaceOrder2"/> + <see selector="{{CheckoutSuccessMainSection.successTitle}}" userInput="Thank you for your purchase!" stepKey="waitForLoadSuccessPage2"/> + <click selector="{{CheckoutSuccessMainSection.continueShoppingButton}}" stepKey="clickContinueShoppingButton"/> + <see userInput="Home Page" selector="{{StorefrontCMSPageSection.mainTitle}}" stepKey="seeHomePageTitle"/> + <seeCurrentUrlEquals url="{{_ENV.MAGENTO_BASE_URL}}" stepKey="seeHomePageUrl"/> + + <!--Go to product page--> + <amOnPage url="$$createSimpleProduct.custom_attributes[url_key]$$.html" stepKey="navigateToSimpleProductPage3"/> + <waitForPageLoad stepKey="waitForCatalogPageLoad3"/> + + <!--Add Product to Shopping Cart--> + <actionGroup ref="AddToCartFromStorefrontProductPageActionGroup" stepKey="addToCartFromStorefrontProductPage3"> + <argument name="productName" value="$$createSimpleProduct.name$$"/> + </actionGroup> + + <!--Go to Checkout--> + <actionGroup ref="GoToCheckoutFromMinicartActionGroup" stepKey="goToCheckoutFromMinicart3"/> + <click selector="{{CheckoutShippingMethodsSection.firstShippingMethod}}" stepKey="selectFirstShippingMethod3"/> + <waitForLoadingMaskToDisappear stepKey="waitForLoadingMask4"/> + <waitForElement selector="{{CheckoutShippingMethodsSection.next}}" time="30" stepKey="waitForNextButton3"/> + <click selector="{{CheckoutShippingMethodsSection.next}}" stepKey="clickNext3"/> + + <!-- Checkout select Check/Money Order payment --> + <actionGroup ref="CheckoutSelectCheckMoneyOrderPaymentActionGroup" stepKey="selectCheckMoneyPayment3"/> + + <!--Click Place Order button--> + <click selector="{{CheckoutPaymentSection.placeOrder}}" stepKey="clickPlaceOrder3"/> + <see selector="{{CheckoutSuccessMainSection.successTitle}}" userInput="Thank you for your purchase!" stepKey="waitForLoadSuccessPage3"/> + + <!--Check "Print Receipt" button is presented (desktop only)--> + <seeElement selector="{{CheckoutSuccessMainSection.printLink}}" stepKey="seeVisiblePrint"/> + <resizeWindow width="600" height="800" stepKey="resizeWindow"/> + <waitForElementNotVisible selector="{{CheckoutSuccessMainSection.printLink}}" stepKey="waitInvisiblePrint"/> + <dontSeeElement selector="{{CheckoutSuccessMainSection.printLink}}" stepKey="seeInvisiblePrint"/> + <resizeWindow width="1360" height="1020" stepKey="maximizeWindowKey1"/> + <waitForElementVisible selector="{{CheckoutSuccessMainSection.printLink}}" stepKey="waitVisiblePrint"/> + <seeElement selector="{{CheckoutSuccessMainSection.printLink}}" stepKey="seeVisiblePrint2"/> + + <!--See print page--> + <click selector="{{CheckoutSuccessMainSection.printLink}}" stepKey="clickPrintLink"/> + <switchToWindow stepKey="switchToWindow"/> + <switchToNextTab stepKey="switchToTab"/> + <seeInCurrentUrl url="sales/order/print/order_id" stepKey="seePrintPage"/> + <seeElement selector="{{StorefrontCustomerOrderViewSection.orderTitle}}" stepKey="seeOrderTitleOnPrint"/> + <switchToWindow stepKey="switchToWindow2"/> + </test> +</tests> diff --git a/app/code/Magento/Checkout/Test/Mftf/Test/CheckoutSpecificDestinationsTest.xml b/app/code/Magento/Checkout/Test/Mftf/Test/CheckoutSpecificDestinationsTest.xml index 1bfb1804f41a7..58a44f5b89f90 100644 --- a/app/code/Magento/Checkout/Test/Mftf/Test/CheckoutSpecificDestinationsTest.xml +++ b/app/code/Magento/Checkout/Test/Mftf/Test/CheckoutSpecificDestinationsTest.xml @@ -24,7 +24,7 @@ <requiredEntity createDataKey="defaultCategory"/> </createData> - <actionGroup ref="LoginAsAdmin" stepKey="loginAsAdmin"/> + <actionGroup ref="AdminLoginActionGroup" stepKey="loginAsAdmin"/> </before> <!--Go to configuration general page--> @@ -77,7 +77,7 @@ </actionGroup> <after> - <actionGroup ref="logout" stepKey="logout"/> + <actionGroup ref="AdminLogoutActionGroup" stepKey="logout"/> <deleteData createDataKey="simpleProduct" stepKey="deleteProduct"/> <deleteData createDataKey="defaultCategory" stepKey="deleteCategory"/> diff --git a/app/code/Magento/Checkout/Test/Mftf/Test/ConfiguringInstantPurchaseFunctionalityTest.xml b/app/code/Magento/Checkout/Test/Mftf/Test/ConfiguringInstantPurchaseFunctionalityTest.xml index aea0657cc2e3b..016616a27a05a 100644 --- a/app/code/Magento/Checkout/Test/Mftf/Test/ConfiguringInstantPurchaseFunctionalityTest.xml +++ b/app/code/Magento/Checkout/Test/Mftf/Test/ConfiguringInstantPurchaseFunctionalityTest.xml @@ -33,7 +33,7 @@ <createData entity="SimpleProduct2" stepKey="createProduct"/> <!-- Login as admin --> - <actionGroup ref="LoginAsAdmin" stepKey="loginAsAdmin"/> + <actionGroup ref="AdminLoginActionGroup" stepKey="loginAsAdmin"/> </before> <after> <!-- Set configs to default --> @@ -47,7 +47,7 @@ <deleteData createDataKey="createCustomer" stepKey="deleteCustomer"/> <!-- Admin logout --> - <actionGroup ref="logout" stepKey="adminLogout"/> + <actionGroup ref="AdminLogoutActionGroup" stepKey="adminLogout"/> </after> <!-- Create store views --> diff --git a/app/code/Magento/Checkout/Test/Mftf/Test/DeleteConfigurableProductFromShoppingCartTest.xml b/app/code/Magento/Checkout/Test/Mftf/Test/DeleteConfigurableProductFromShoppingCartTest.xml index 891f647e3d5ef..62fa3063de08a 100644 --- a/app/code/Magento/Checkout/Test/Mftf/Test/DeleteConfigurableProductFromShoppingCartTest.xml +++ b/app/code/Magento/Checkout/Test/Mftf/Test/DeleteConfigurableProductFromShoppingCartTest.xml @@ -60,6 +60,9 @@ <!-- Delete category --> <deleteData createDataKey="createCategory" stepKey="deleteCategory"/> + + <!-- Reindex invalidated indices after product attribute has been created/deleted --> + <magentoCron groups="index" stepKey="reindexInvalidatedIndices"/> </after> <!-- Add configurable product to the cart --> diff --git a/app/code/Magento/Checkout/Test/Mftf/Test/EndToEndB2CGuestUserTest.xml b/app/code/Magento/Checkout/Test/Mftf/Test/EndToEndB2CGuestUserTest.xml deleted file mode 100644 index 7002479279a78..0000000000000 --- a/app/code/Magento/Checkout/Test/Mftf/Test/EndToEndB2CGuestUserTest.xml +++ /dev/null @@ -1,387 +0,0 @@ -<?xml version="1.0" encoding="UTF-8"?> -<!-- - /** - * Copyright © Magento, Inc. All rights reserved. - * See COPYING.txt for license details. - */ ---> - -<tests xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" - xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/testSchema.xsd"> - <test name="EndToEndB2CGuestUserTest"> - <!-- Step 3: User adds products to cart --> - <comment userInput="Start of adding products to cart" stepKey="startOfAddingProductsToCart" after="endOfBrowsingCatalog"/> - <!-- Add Simple Product 1 to cart --> - <comment userInput="Add Simple Product 1 to cart" stepKey="commentAddSimpleProduct1ToCart" after="startOfAddingProductsToCart" /> - <click selector="{{StorefrontHeaderSection.NavigationCategoryByName($$createCategory.name$$)}}" stepKey="cartClickCategory" after="commentAddSimpleProduct1ToCart"/> - <waitForLoadingMaskToDisappear stepKey="waitForCartCategoryloaded" after="cartClickCategory"/> - <actionGroup ref="StorefrontCheckCategoryActionGroup" stepKey="cartAssertCategory" after="waitForCartCategoryloaded"> - <argument name="category" value="$$createCategory$$"/> - <argument name="productCount" value="3"/> - </actionGroup> - <actionGroup ref="StorefrontCheckCategorySimpleProductActionGroup" stepKey="cartAssertSimpleProduct1" after="cartAssertCategory"> - <argument name="product" value="$$createSimpleProduct1$$"/> - </actionGroup> - <!-- @TODO: Move Image check to action group after MQE-697 is fixed --> - <grabAttributeFrom selector="{{StorefrontCategoryProductSection.ProductImageByName($$createSimpleProduct1.name$$)}}" userInput="src" stepKey="cartGrabSimpleProduct1ImageSrc" after="cartAssertSimpleProduct1"/> - <assertNotRegExp expected="'/placeholder\/small_image\.jpg/'" actual="$cartGrabSimpleProduct1ImageSrc" stepKey="cartAssertSimpleProduct1ImageNotDefault" after="cartGrabSimpleProduct1ImageSrc"/> - <click selector="{{StorefrontCategoryProductSection.ProductTitleByName($$createSimpleProduct1.name$$)}}" stepKey="cartClickSimpleProduct1" after="cartAssertSimpleProduct1ImageNotDefault"/> - <waitForLoadingMaskToDisappear stepKey="waitForCartSimpleProduct1loaded" after="cartClickSimpleProduct1"/> - <actionGroup ref="StorefrontCheckSimpleProductActionGroup" stepKey="cartAssertProduct1Page" after="waitForCartSimpleProduct1loaded"> - <argument name="product" value="$$createSimpleProduct1$$"/> - </actionGroup> - <!-- @TODO: Move Image check to action group after MQE-697 is fixed --> - <grabAttributeFrom selector="{{StorefrontProductInfoMainSection.productImage}}" userInput="src" stepKey="cartGrabSimpleProduct1PageImageSrc" after="cartAssertProduct1Page"/> - <assertNotRegExp expected="'/placeholder\/image\.jpg/'" actual="$cartGrabSimpleProduct1PageImageSrc" stepKey="cartAssertSimpleProduct1PageImageNotDefault" after="cartGrabSimpleProduct1PageImageSrc"/> - <actionGroup ref="StorefrontAddProductToCartActionGroup" stepKey="cartAddProduct1ToCart" after="cartAssertSimpleProduct1PageImageNotDefault"> - <argument name="product" value="$$createSimpleProduct1$$"/> - <argument name="productCount" value="1"/> - </actionGroup> - - <!-- Add Simple Product 2 to cart --> - <comment userInput="Add Simple Product 2 to cart" stepKey="commentAddSimpleProduct2ToCart" after="cartAddProduct1ToCart" /> - <click selector="{{StorefrontHeaderSection.NavigationCategoryByName($$createCategory.name$$)}}" stepKey="cartClickCategory1" after="commentAddSimpleProduct2ToCart"/> - <waitForLoadingMaskToDisappear stepKey="waitForCartCategory1loaded" after="cartClickCategory1"/> - <actionGroup ref="StorefrontCheckCategoryActionGroup" stepKey="cartAssertCategory1ForSimpleProduct2" after="waitForCartCategory1loaded"> - <argument name="category" value="$$createCategory$$"/> - <argument name="productCount" value="3"/> - </actionGroup> - <actionGroup ref="StorefrontCheckCategorySimpleProductActionGroup" stepKey="cartAssertSimpleProduct2" after="cartAssertCategory1ForSimpleProduct2"> - <argument name="product" value="$$createSimpleProduct2$$"/> - </actionGroup> - <!-- @TODO: Move Image check to action group after MQE-697 is fixed --> - <grabAttributeFrom selector="{{StorefrontCategoryProductSection.ProductImageByName($$createSimpleProduct2.name$$)}}" userInput="src" stepKey="cartGrabSimpleProduct2ImageSrc" after="cartAssertSimpleProduct2"/> - <assertNotRegExp expected="'/placeholder\/small_image\.jpg/'" actual="$cartGrabSimpleProduct2ImageSrc" stepKey="cartAssertSimpleProduct2ImageNotDefault" after="cartGrabSimpleProduct2ImageSrc"/> - <actionGroup ref="StorefrontAddCategoryProductToCartActionGroup" stepKey="cartAddProduct2ToCart" after="cartAssertSimpleProduct2ImageNotDefault"> - <argument name="product" value="$$createSimpleProduct2$$"/> - <!-- @TODO: Change to scalar value after MQE-498 is implemented --> - <argument name="productCount" value="CONST.two"/> - </actionGroup> - - <!-- Check products in minicart --> - <!-- Check simple product 1 in minicart --> - <comment userInput="Check simple product 1 in minicart" stepKey="commentCheckSimpleProduct1InMinicart" after="cartAddProduct2ToCart"/> - <actionGroup ref="StorefrontOpenMinicartAndCheckSimpleProductActionGroup" stepKey="cartOpenMinicartAndCheckSimpleProduct1" after="commentCheckSimpleProduct1InMinicart"> - <argument name="product" value="$$createSimpleProduct1$$"/> - </actionGroup> - <!-- @TODO: Move Image check to action group after MQE-697 is fixed --> - <grabAttributeFrom selector="{{StorefrontMinicartSection.productImageByName($$createSimpleProduct1.name$$)}}" userInput="src" stepKey="cartMinicartGrabSimpleProduct1ImageSrc" after="cartOpenMinicartAndCheckSimpleProduct1"/> - <assertNotRegExp expected="'/placeholder\/thumbnail\.jpg/'" actual="$cartMinicartGrabSimpleProduct1ImageSrc" stepKey="cartMinicartAssertSimpleProduct1ImageNotDefault" after="cartMinicartGrabSimpleProduct1ImageSrc"/> - <click selector="{{StorefrontMinicartSection.productLinkByName($$createSimpleProduct1.name$$)}}" stepKey="cartMinicartClickSimpleProduct1" after="cartMinicartAssertSimpleProduct1ImageNotDefault"/> - <waitForLoadingMaskToDisappear stepKey="waitForMinicartSimpleProduct1loaded" after="cartMinicartClickSimpleProduct1"/> - <actionGroup ref="StorefrontCheckSimpleProductActionGroup" stepKey="cartAssertMinicartProduct1Page" after="waitForMinicartSimpleProduct1loaded"> - <argument name="product" value="$$createSimpleProduct1$$"/> - </actionGroup> - <!-- @TODO: Move Image check to action group after MQE-697 is fixed --> - <grabAttributeFrom selector="{{StorefrontProductInfoMainSection.productImage}}" userInput="src" stepKey="cartMinicartGrabSimpleProduct1PageImageSrc" after="cartAssertMinicartProduct1Page"/> - <assertNotRegExp expected="'/placeholder\/image\.jpg/'" actual="$cartMinicartGrabSimpleProduct1PageImageSrc" stepKey="cartMinicartAssertSimpleProduct1PageImageNotDefault" after="cartMinicartGrabSimpleProduct1PageImageSrc"/> - <actionGroup ref="StorefrontOpenMinicartAndCheckSimpleProductActionGroup" stepKey="cartOpenMinicartAndCheckSimpleProduct2" after="cartMinicartAssertSimpleProduct1PageImageNotDefault"> - <argument name="product" value="$$createSimpleProduct2$$"/> - </actionGroup> - <!-- Check simple product2 in minicart --> - <comment userInput="Check simple product 2 in minicart" stepKey="commentCheckSimpleProduct2InMinicart" after="cartOpenMinicartAndCheckSimpleProduct2"/> - <!-- @TODO: Move Image check to action group after MQE-697 is fixed --> - <grabAttributeFrom selector="{{StorefrontMinicartSection.productImageByName($$createSimpleProduct2.name$$)}}" userInput="src" stepKey="cartMinicartGrabSimpleProduct2ImageSrc" after="commentCheckSimpleProduct2InMinicart"/> - <assertNotRegExp expected="'/placeholder\/thumbnail\.jpg/'" actual="$cartMinicartGrabSimpleProduct2ImageSrc" stepKey="cartMinicartAssertSimpleProduct2ImageNotDefault" after="cartMinicartGrabSimpleProduct2ImageSrc"/> - <click selector="{{StorefrontMinicartSection.productLinkByName($$createSimpleProduct2.name$$)}}" stepKey="cartMinicartClickSimpleProduct2" after="cartMinicartAssertSimpleProduct2ImageNotDefault"/> - <waitForLoadingMaskToDisappear stepKey="waitForMinicartSimpleProduct2loaded" after="cartMinicartClickSimpleProduct2"/> - <actionGroup ref="StorefrontCheckSimpleProductActionGroup" stepKey="cartAssertMinicartProduct2Page" after="waitForMinicartSimpleProduct2loaded"> - <argument name="product" value="$$createSimpleProduct2$$"/> - </actionGroup> - <!-- @TODO: Move Image check to action group after MQE-697 is fixed --> - <grabAttributeFrom selector="{{StorefrontProductInfoMainSection.productImage}}" userInput="src" stepKey="cartMinicartGrabSimpleProduct2PageImageSrc" after="cartAssertMinicartProduct2Page"/> - <assertNotRegExp expected="'/placeholder\/image\.jpg/'" actual="$cartMinicartGrabSimpleProduct2PageImageSrc" stepKey="cartMinicartAssertSimpleProduct2PageImageNotDefault" after="cartMinicartGrabSimpleProduct2PageImageSrc"/> - - <!-- Check products in cart --> - <comment userInput="Check cart information" stepKey="commentCheckCartInformation" after="cartMinicartAssertSimpleProduct2PageImageNotDefault" /> - <actionGroup ref="StorefrontOpenCartFromMinicartActionGroup" stepKey="cartOpenCart" after="commentCheckCartInformation"/> - <actionGroup ref="StorefrontCheckCartActionGroup" stepKey="cartAssertCart" after="cartOpenCart"> - <argument name="subtotal" value="480.00"/> - <argument name="shipping" value="15.00"/> - <argument name="shippingMethod" value="Flat Rate - Fixed"/> - <argument name="total" value="495.00"/> - </actionGroup> - - <!-- Check simple product 1 in cart --> - <comment userInput="Check simple product 1 in cart" stepKey="commentCheckSimpleProduct1InCart" after="cartAssertCart"/> - <actionGroup ref="StorefrontCheckCartSimpleProductActionGroup" stepKey="cartAssertCartSimpleProduct1" after="commentCheckSimpleProduct1InCart"> - <argument name="product" value="$$createSimpleProduct1$$"/> - <!-- @TODO: Change to scalar value after MQE-498 is implemented --> - <argument name="productQuantity" value="CONST.one"/> - </actionGroup> - <!-- @TODO: Move Image check to action group after MQE-697 is fixed --> - <grabAttributeFrom selector="{{CheckoutCartProductSection.ProductImageByName($$createSimpleProduct1.name$$)}}" userInput="src" stepKey="cartCartGrabSimpleProduct1ImageSrc" after="cartAssertCartSimpleProduct1"/> - <assertNotRegExp expected="'/placeholder\/small_image\.jpg/'" actual="$cartCartGrabSimpleProduct1ImageSrc" stepKey="cartCartAssertSimpleProduct1ImageNotDefault" after="cartCartGrabSimpleProduct1ImageSrc"/> - <click selector="{{CheckoutCartProductSection.ProductLinkByName($$createSimpleProduct1.name$$)}}" stepKey="cartClickCartSimpleProduct1" after="cartCartAssertSimpleProduct1ImageNotDefault"/> - <waitForLoadingMaskToDisappear stepKey="waitForCartSimpleProduct1loadedAgain" after="cartClickCartSimpleProduct1"/> - <actionGroup ref="StorefrontCheckSimpleProductActionGroup" stepKey="cartAssertCartProduct1Page" after="waitForCartSimpleProduct1loadedAgain"> - <argument name="product" value="$$createSimpleProduct1$$"/> - </actionGroup> - <!-- @TODO: Move Image check to action group after MQE-697 is fixed --> - <grabAttributeFrom selector="{{StorefrontProductInfoMainSection.productImage}}" userInput="src" stepKey="cartCartGrabSimpleProduct2PageImageSrc1" after="cartAssertCartProduct1Page"/> - <assertNotRegExp expected="'/placeholder\/image\.jpg/'" actual="$cartCartGrabSimpleProduct2PageImageSrc1" stepKey="cartCartAssertSimpleProduct2PageImageNotDefault1" after="cartCartGrabSimpleProduct2PageImageSrc1"/> - - <!-- Check simple product 2 in cart --> - <comment userInput="Check simple product 2 in cart" stepKey="commentCheckSimpleProduct2InCart" after="cartCartAssertSimpleProduct2PageImageNotDefault1"/> - <actionGroup ref="StorefrontOpenCartFromMinicartActionGroup" stepKey="cartOpenCart1" after="commentCheckSimpleProduct2InCart"/> - <actionGroup ref="StorefrontCheckCartSimpleProductActionGroup" stepKey="cartAssertCartSimpleProduct2" after="cartOpenCart1"> - <argument name="product" value="$$createSimpleProduct2$$"/> - <!-- @TODO: Change to scalar value after MQE-498 is implemented --> - <argument name="productQuantity" value="CONST.one"/> - </actionGroup> - <!-- @TODO: Move Image check to action group after MQE-697 is fixed --> - <grabAttributeFrom selector="{{CheckoutCartProductSection.ProductImageByName($$createSimpleProduct2.name$$)}}" userInput="src" stepKey="cartCartGrabSimpleProduct2ImageSrc" after="cartAssertCartSimpleProduct2"/> - <assertNotRegExp expected="'/placeholder\/small_image\.jpg/'" actual="$cartCartGrabSimpleProduct2ImageSrc" stepKey="cartCartAssertSimpleProduct2ImageNotDefault" after="cartCartGrabSimpleProduct2ImageSrc"/> - <click selector="{{CheckoutCartProductSection.ProductLinkByName($$createSimpleProduct2.name$$)}}" stepKey="cartClickCartSimpleProduct2" after="cartCartAssertSimpleProduct2ImageNotDefault"/> - <waitForLoadingMaskToDisappear stepKey="waitForCartSimpleProduct2loaded" after="cartClickCartSimpleProduct2"/> - <actionGroup ref="StorefrontCheckSimpleProductActionGroup" stepKey="cartAssertCartProduct2Page" after="waitForCartSimpleProduct2loaded"> - <argument name="product" value="$$createSimpleProduct2$$"/> - </actionGroup> - <!-- @TODO: Move Image check to action group after MQE-697 is fixed --> - <grabAttributeFrom selector="{{StorefrontProductInfoMainSection.productImage}}" userInput="src" stepKey="cartCartGrabSimpleProduct2PageImageSrc2" after="cartAssertCartProduct2Page"/> - <assertNotRegExp expected="'/placeholder\/image\.jpg/'" actual="$cartCartGrabSimpleProduct2PageImageSrc2" stepKey="cartCartAssertSimpleProduct2PageImageNotDefault2" after="cartCartGrabSimpleProduct2PageImageSrc2"/> - <comment userInput="End of adding products to cart" stepKey="endOfAddingProductsToCart" after="cartCartAssertSimpleProduct2PageImageNotDefault2" /> - - <!-- Step 6: Check out --> - <comment userInput="Start of checking out" stepKey="startOfCheckingOut" after="endOfUsingCouponCode" /> - <actionGroup ref="GoToCheckoutFromMinicartActionGroup" stepKey="guestGoToCheckoutFromMinicart" after="startOfCheckingOut"/> - <actionGroup ref="GuestCheckoutFillingShippingSectionActionGroup" stepKey="guestCheckoutFillingShippingSection" after="guestGoToCheckoutFromMinicart"> - <argument name="customerVar" value="CustomerEntityOne" /> - <argument name="customerAddressVar" value="CustomerAddressSimple" /> - </actionGroup> - - <!-- Check order summary in checkout --> - <comment userInput="Check order summary in checkout" stepKey="commentCheckOrderSummaryInCheckout" after="guestCheckoutFillingShippingSection" /> - <actionGroup ref="CheckOrderSummaryInCheckoutActionGroup" stepKey="guestCheckoutCheckOrderSummary" after="commentCheckOrderSummaryInCheckout"> - <argument name="subtotal" value="480.00"/> - <argument name="shippingTotal" value="15.00"/> - <argument name="shippingMethod" value="Flat Rate - Fixed"/> - <argument name="total" value="495.00"/> - </actionGroup> - - <!-- Check ship to information in checkout --> - <comment userInput="Check ship to information in checkout" stepKey="commentCheckShipToInformationInCheckout" after="guestCheckoutCheckOrderSummary" /> - <actionGroup ref="CheckShipToInformationInCheckoutActionGroup" stepKey="guestCheckoutCheckShipToInformation" after="commentCheckShipToInformationInCheckout"> - <argument name="customerVar" value="CustomerEntityOne" /> - <argument name="customerAddressVar" value="CustomerAddressSimple" /> - </actionGroup> - - <!-- Check shipping method in checkout --> - <comment userInput="Check shipping method in checkout" stepKey="commentCheckShippingMethodInCheckout" after="guestCheckoutCheckShipToInformation" /> - <actionGroup ref="CheckShippingMethodInCheckoutActionGroup" stepKey="guestCheckoutCheckShippingMethod" after="commentCheckShippingMethodInCheckout"> - <argument name="shippingMethod" value="E2EB2CQuote.shippingMethod" /> - </actionGroup> - - <!-- Verify Simple Product 1 is in checkout cart items --> - <comment userInput="Verify Simple Product 1 is in checkout cart items" stepKey="commentVerifySimpleProduct1IsInCheckoutCartItems" after="guestCheckoutCheckShippingMethod" /> - <actionGroup ref="CheckProductInCheckoutCartItemsActionGroup" stepKey="guestCheckoutCheckSimpleProduct1InCartItems" after="commentVerifySimpleProduct1IsInCheckoutCartItems"> - <argument name="productVar" value="$$createSimpleProduct1$$"/> - </actionGroup> - - <!-- Verify Simple Product 2 is in checkout cart items --> - <comment userInput="Verify Simple Product 2 is in checkout cart items" stepKey="commentVerifySimpleProduct2IsInCheckoutCartItems" after="guestCheckoutCheckSimpleProduct1InCartItems" /> - <actionGroup ref="CheckProductInCheckoutCartItemsActionGroup" stepKey="guestCheckoutCheckSimpleProduct2InCartItems" after="commentVerifySimpleProduct2IsInCheckoutCartItems"> - <argument name="productVar" value="$$createSimpleProduct2$$"/> - </actionGroup> - - <comment userInput="Place order with check money order payment" stepKey="commentPlaceOrderWithCheckMoneyOrderPayment" after="guestCheckoutCheckSimpleProduct2InCartItems" /> - <actionGroup ref="CheckoutSelectCheckMoneyOrderPaymentActionGroup" stepKey="guestSelectCheckMoneyOrderPayment" after="commentPlaceOrderWithCheckMoneyOrderPayment"/> - <actionGroup ref="CheckBillingAddressInCheckoutActionGroup" stepKey="guestSeeBillingAddress" after="guestSelectCheckMoneyOrderPayment"> - <argument name="customerVar" value="CustomerEntityOne" /> - <argument name="customerAddressVar" value="CustomerAddressSimple" /> - </actionGroup> - <actionGroup ref="CheckoutPlaceOrderActionGroup" stepKey="guestPlaceorder" after="guestSeeBillingAddress"> - <argument name="orderNumberMessage" value="CONST.successGuestCheckoutOrderNumberMessage" /> - <argument name="emailYouMessage" value="CONST.successCheckoutEmailYouMessage" /> - </actionGroup> - <comment userInput="End of checking out" stepKey="endOfCheckingOut" after="guestPlaceorder" /> - </test> - <test name="EndToEndB2CGuestUserMysqlTest"> - <!-- Step 3: User adds products to cart --> - <comment userInput="Start of adding products to cart" stepKey="startOfAddingProductsToCart" after="endOfBrowsingCatalog"/> - <!-- Add Simple Product 1 to cart --> - <comment userInput="Add Simple Product 1 to cart" stepKey="commentAddSimpleProduct1ToCart" after="startOfAddingProductsToCart" /> - <click selector="{{StorefrontHeaderSection.NavigationCategoryByName($$createCategory.name$$)}}" stepKey="cartClickCategory" after="commentAddSimpleProduct1ToCart"/> - <waitForLoadingMaskToDisappear stepKey="waitForCartCategoryloaded" after="cartClickCategory"/> - <actionGroup ref="StorefrontCheckCategoryActionGroup" stepKey="cartAssertCategory" after="waitForCartCategoryloaded"> - <argument name="category" value="$$createCategory$$"/> - <argument name="productCount" value="3"/> - </actionGroup> - <actionGroup ref="StorefrontCheckCategorySimpleProductActionGroup" stepKey="cartAssertSimpleProduct1" after="cartAssertCategory"> - <argument name="product" value="$$createSimpleProduct1$$"/> - </actionGroup> - <!-- @TODO: Move Image check to action group after MQE-697 is fixed --> - <grabAttributeFrom selector="{{StorefrontCategoryProductSection.ProductImageByName($$createSimpleProduct1.name$$)}}" userInput="src" stepKey="cartGrabSimpleProduct1ImageSrc" after="cartAssertSimpleProduct1"/> - <assertNotRegExp expected="'/placeholder\/small_image\.jpg/'" actual="$cartGrabSimpleProduct1ImageSrc" stepKey="cartAssertSimpleProduct1ImageNotDefault" after="cartGrabSimpleProduct1ImageSrc"/> - <click selector="{{StorefrontCategoryProductSection.ProductTitleByName($$createSimpleProduct1.name$$)}}" stepKey="cartClickSimpleProduct1" after="cartAssertSimpleProduct1ImageNotDefault"/> - <waitForLoadingMaskToDisappear stepKey="waitForCartSimpleProduct1loaded" after="cartClickSimpleProduct1"/> - <actionGroup ref="StorefrontCheckSimpleProductActionGroup" stepKey="cartAssertProduct1Page" after="waitForCartSimpleProduct1loaded"> - <argument name="product" value="$$createSimpleProduct1$$"/> - </actionGroup> - <!-- @TODO: Move Image check to action group after MQE-697 is fixed --> - <grabAttributeFrom selector="{{StorefrontProductInfoMainSection.productImage}}" userInput="src" stepKey="cartGrabSimpleProduct1PageImageSrc" after="cartAssertProduct1Page"/> - <assertNotRegExp expected="'/placeholder\/image\.jpg/'" actual="$cartGrabSimpleProduct1PageImageSrc" stepKey="cartAssertSimpleProduct1PageImageNotDefault" after="cartGrabSimpleProduct1PageImageSrc"/> - <actionGroup ref="StorefrontAddProductToCartActionGroup" stepKey="cartAddProduct1ToCart" after="cartAssertSimpleProduct1PageImageNotDefault"> - <argument name="product" value="$$createSimpleProduct1$$"/> - <argument name="productCount" value="1"/> - </actionGroup> - - <!-- Add Simple Product 2 to cart --> - <comment userInput="Add Simple Product 2 to cart" stepKey="commentAddSimpleProduct2ToCart" after="cartAddProduct1ToCart" /> - <click selector="{{StorefrontHeaderSection.NavigationCategoryByName($$createCategory.name$$)}}" stepKey="cartClickCategory1" after="commentAddSimpleProduct2ToCart"/> - <waitForLoadingMaskToDisappear stepKey="waitForCartCategory1loaded" after="cartClickCategory1"/> - <actionGroup ref="StorefrontCheckCategoryActionGroup" stepKey="cartAssertCategory1ForSimpleProduct2" after="waitForCartCategory1loaded"> - <argument name="category" value="$$createCategory$$"/> - <argument name="productCount" value="3"/> - </actionGroup> - <actionGroup ref="StorefrontCheckCategorySimpleProductActionGroup" stepKey="cartAssertSimpleProduct2" after="cartAssertCategory1ForSimpleProduct2"> - <argument name="product" value="$$createSimpleProduct2$$"/> - </actionGroup> - <!-- @TODO: Move Image check to action group after MQE-697 is fixed --> - <grabAttributeFrom selector="{{StorefrontCategoryProductSection.ProductImageByName($$createSimpleProduct2.name$$)}}" userInput="src" stepKey="cartGrabSimpleProduct2ImageSrc" after="cartAssertSimpleProduct2"/> - <assertNotRegExp expected="'/placeholder\/small_image\.jpg/'" actual="$cartGrabSimpleProduct2ImageSrc" stepKey="cartAssertSimpleProduct2ImageNotDefault" after="cartGrabSimpleProduct2ImageSrc"/> - <actionGroup ref="StorefrontAddCategoryProductToCartActionGroup" stepKey="cartAddProduct2ToCart" after="cartAssertSimpleProduct2ImageNotDefault"> - <argument name="product" value="$$createSimpleProduct2$$"/> - <!-- @TODO: Change to scalar value after MQE-498 is implemented --> - <argument name="productCount" value="CONST.two"/> - </actionGroup> - - <!-- Check products in minicart --> - <!-- Check simple product 1 in minicart --> - <comment userInput="Check simple product 1 in minicart" stepKey="commentCheckSimpleProduct1InMinicart" after="cartAddProduct2ToCart"/> - <actionGroup ref="StorefrontOpenMinicartAndCheckSimpleProductActionGroup" stepKey="cartOpenMinicartAndCheckSimpleProduct1" after="commentCheckSimpleProduct1InMinicart"> - <argument name="product" value="$$createSimpleProduct1$$"/> - </actionGroup> - <!-- @TODO: Move Image check to action group after MQE-697 is fixed --> - <grabAttributeFrom selector="{{StorefrontMinicartSection.productImageByName($$createSimpleProduct1.name$$)}}" userInput="src" stepKey="cartMinicartGrabSimpleProduct1ImageSrc" after="cartOpenMinicartAndCheckSimpleProduct1"/> - <assertNotRegExp expected="'/placeholder\/thumbnail\.jpg/'" actual="$cartMinicartGrabSimpleProduct1ImageSrc" stepKey="cartMinicartAssertSimpleProduct1ImageNotDefault" after="cartMinicartGrabSimpleProduct1ImageSrc"/> - <click selector="{{StorefrontMinicartSection.productLinkByName($$createSimpleProduct1.name$$)}}" stepKey="cartMinicartClickSimpleProduct1" after="cartMinicartAssertSimpleProduct1ImageNotDefault"/> - <waitForLoadingMaskToDisappear stepKey="waitForMinicartSimpleProduct1loaded" after="cartMinicartClickSimpleProduct1"/> - <actionGroup ref="StorefrontCheckSimpleProductActionGroup" stepKey="cartAssertMinicartProduct1Page" after="waitForMinicartSimpleProduct1loaded"> - <argument name="product" value="$$createSimpleProduct1$$"/> - </actionGroup> - <!-- @TODO: Move Image check to action group after MQE-697 is fixed --> - <grabAttributeFrom selector="{{StorefrontProductInfoMainSection.productImage}}" userInput="src" stepKey="cartMinicartGrabSimpleProduct1PageImageSrc" after="cartAssertMinicartProduct1Page"/> - <assertNotRegExp expected="'/placeholder\/image\.jpg/'" actual="$cartMinicartGrabSimpleProduct1PageImageSrc" stepKey="cartMinicartAssertSimpleProduct1PageImageNotDefault" after="cartMinicartGrabSimpleProduct1PageImageSrc"/> - <actionGroup ref="StorefrontOpenMinicartAndCheckSimpleProductActionGroup" stepKey="cartOpenMinicartAndCheckSimpleProduct2" after="cartMinicartAssertSimpleProduct1PageImageNotDefault"> - <argument name="product" value="$$createSimpleProduct2$$"/> - </actionGroup> - <!-- Check simple product2 in minicart --> - <comment userInput="Check simple product 2 in minicart" stepKey="commentCheckSimpleProduct2InMinicart" after="cartOpenMinicartAndCheckSimpleProduct2"/> - <!-- @TODO: Move Image check to action group after MQE-697 is fixed --> - <grabAttributeFrom selector="{{StorefrontMinicartSection.productImageByName($$createSimpleProduct2.name$$)}}" userInput="src" stepKey="cartMinicartGrabSimpleProduct2ImageSrc" after="commentCheckSimpleProduct2InMinicart"/> - <assertNotRegExp expected="'/placeholder\/thumbnail\.jpg/'" actual="$cartMinicartGrabSimpleProduct2ImageSrc" stepKey="cartMinicartAssertSimpleProduct2ImageNotDefault" after="cartMinicartGrabSimpleProduct2ImageSrc"/> - <click selector="{{StorefrontMinicartSection.productLinkByName($$createSimpleProduct2.name$$)}}" stepKey="cartMinicartClickSimpleProduct2" after="cartMinicartAssertSimpleProduct2ImageNotDefault"/> - <waitForLoadingMaskToDisappear stepKey="waitForMinicartSimpleProduct2loaded" after="cartMinicartClickSimpleProduct2"/> - <actionGroup ref="StorefrontCheckSimpleProductActionGroup" stepKey="cartAssertMinicartProduct2Page" after="waitForMinicartSimpleProduct2loaded"> - <argument name="product" value="$$createSimpleProduct2$$"/> - </actionGroup> - <!-- @TODO: Move Image check to action group after MQE-697 is fixed --> - <grabAttributeFrom selector="{{StorefrontProductInfoMainSection.productImage}}" userInput="src" stepKey="cartMinicartGrabSimpleProduct2PageImageSrc" after="cartAssertMinicartProduct2Page"/> - <assertNotRegExp expected="'/placeholder\/image\.jpg/'" actual="$cartMinicartGrabSimpleProduct2PageImageSrc" stepKey="cartMinicartAssertSimpleProduct2PageImageNotDefault" after="cartMinicartGrabSimpleProduct2PageImageSrc"/> - - <!-- Check products in cart --> - <comment userInput="Check cart information" stepKey="commentCheckCartInformation" after="cartMinicartAssertSimpleProduct2PageImageNotDefault" /> - <actionGroup ref="StorefrontOpenCartFromMinicartActionGroup" stepKey="cartOpenCart" after="commentCheckCartInformation"/> - <actionGroup ref="StorefrontCheckCartActionGroup" stepKey="cartAssertCart" after="cartOpenCart"> - <argument name="subtotal" value="480.00"/> - <argument name="shipping" value="15.00"/> - <argument name="shippingMethod" value="Flat Rate - Fixed"/> - <argument name="total" value="495.00"/> - </actionGroup> - - <!-- Check simple product 1 in cart --> - <comment userInput="Check simple product 1 in cart" stepKey="commentCheckSimpleProduct1InCart" after="cartAssertCart"/> - <actionGroup ref="StorefrontCheckCartSimpleProductActionGroup" stepKey="cartAssertCartSimpleProduct1" after="commentCheckSimpleProduct1InCart"> - <argument name="product" value="$$createSimpleProduct1$$"/> - <!-- @TODO: Change to scalar value after MQE-498 is implemented --> - <argument name="productQuantity" value="CONST.one"/> - </actionGroup> - <!-- @TODO: Move Image check to action group after MQE-697 is fixed --> - <grabAttributeFrom selector="{{CheckoutCartProductSection.ProductImageByName($$createSimpleProduct1.name$$)}}" userInput="src" stepKey="cartCartGrabSimpleProduct1ImageSrc" after="cartAssertCartSimpleProduct1"/> - <assertNotRegExp expected="'/placeholder\/small_image\.jpg/'" actual="$cartCartGrabSimpleProduct1ImageSrc" stepKey="cartCartAssertSimpleProduct1ImageNotDefault" after="cartCartGrabSimpleProduct1ImageSrc"/> - <click selector="{{CheckoutCartProductSection.ProductLinkByName($$createSimpleProduct1.name$$)}}" stepKey="cartClickCartSimpleProduct1" after="cartCartAssertSimpleProduct1ImageNotDefault"/> - <waitForLoadingMaskToDisappear stepKey="waitForCartSimpleProduct1loadedAgain" after="cartClickCartSimpleProduct1"/> - <actionGroup ref="StorefrontCheckSimpleProductActionGroup" stepKey="cartAssertCartProduct1Page" after="waitForCartSimpleProduct1loadedAgain"> - <argument name="product" value="$$createSimpleProduct1$$"/> - </actionGroup> - <!-- @TODO: Move Image check to action group after MQE-697 is fixed --> - <grabAttributeFrom selector="{{StorefrontProductInfoMainSection.productImage}}" userInput="src" stepKey="cartCartGrabSimpleProduct2PageImageSrc1" after="cartAssertCartProduct1Page"/> - <assertNotRegExp expected="'/placeholder\/image\.jpg/'" actual="$cartCartGrabSimpleProduct2PageImageSrc1" stepKey="cartCartAssertSimpleProduct2PageImageNotDefault1" after="cartCartGrabSimpleProduct2PageImageSrc1"/> - - <!-- Check simple product 2 in cart --> - <comment userInput="Check simple product 2 in cart" stepKey="commentCheckSimpleProduct2InCart" after="cartCartAssertSimpleProduct2PageImageNotDefault1"/> - <actionGroup ref="StorefrontOpenCartFromMinicartActionGroup" stepKey="cartOpenCart1" after="commentCheckSimpleProduct2InCart"/> - <actionGroup ref="StorefrontCheckCartSimpleProductActionGroup" stepKey="cartAssertCartSimpleProduct2" after="cartOpenCart1"> - <argument name="product" value="$$createSimpleProduct2$$"/> - <!-- @TODO: Change to scalar value after MQE-498 is implemented --> - <argument name="productQuantity" value="CONST.one"/> - </actionGroup> - <!-- @TODO: Move Image check to action group after MQE-697 is fixed --> - <grabAttributeFrom selector="{{CheckoutCartProductSection.ProductImageByName($$createSimpleProduct2.name$$)}}" userInput="src" stepKey="cartCartGrabSimpleProduct2ImageSrc" after="cartAssertCartSimpleProduct2"/> - <assertNotRegExp expected="'/placeholder\/small_image\.jpg/'" actual="$cartCartGrabSimpleProduct2ImageSrc" stepKey="cartCartAssertSimpleProduct2ImageNotDefault" after="cartCartGrabSimpleProduct2ImageSrc"/> - <click selector="{{CheckoutCartProductSection.ProductLinkByName($$createSimpleProduct2.name$$)}}" stepKey="cartClickCartSimpleProduct2" after="cartCartAssertSimpleProduct2ImageNotDefault"/> - <waitForLoadingMaskToDisappear stepKey="waitForCartSimpleProduct2loaded" after="cartClickCartSimpleProduct2"/> - <actionGroup ref="StorefrontCheckSimpleProductActionGroup" stepKey="cartAssertCartProduct2Page" after="waitForCartSimpleProduct2loaded"> - <argument name="product" value="$$createSimpleProduct2$$"/> - </actionGroup> - <!-- @TODO: Move Image check to action group after MQE-697 is fixed --> - <grabAttributeFrom selector="{{StorefrontProductInfoMainSection.productImage}}" userInput="src" stepKey="cartCartGrabSimpleProduct2PageImageSrc2" after="cartAssertCartProduct2Page"/> - <assertNotRegExp expected="'/placeholder\/image\.jpg/'" actual="$cartCartGrabSimpleProduct2PageImageSrc2" stepKey="cartCartAssertSimpleProduct2PageImageNotDefault2" after="cartCartGrabSimpleProduct2PageImageSrc2"/> - <comment userInput="End of adding products to cart" stepKey="endOfAddingProductsToCart" after="cartCartAssertSimpleProduct2PageImageNotDefault2" /> - - <!-- Step 6: Check out --> - <comment userInput="Start of checking out" stepKey="startOfCheckingOut" after="endOfUsingCouponCode" /> - <actionGroup ref="GoToCheckoutFromMinicartActionGroup" stepKey="guestGoToCheckoutFromMinicart" after="startOfCheckingOut"/> - <actionGroup ref="GuestCheckoutFillingShippingSectionActionGroup" stepKey="guestCheckoutFillingShippingSection" after="guestGoToCheckoutFromMinicart"> - <argument name="customerVar" value="CustomerEntityOne" /> - <argument name="customerAddressVar" value="CustomerAddressSimple" /> - </actionGroup> - - <!-- Check order summary in checkout --> - <comment userInput="Check order summary in checkout" stepKey="commentCheckOrderSummaryInCheckout" after="guestCheckoutFillingShippingSection" /> - <actionGroup ref="CheckOrderSummaryInCheckoutActionGroup" stepKey="guestCheckoutCheckOrderSummary" after="commentCheckOrderSummaryInCheckout"> - <argument name="subtotal" value="480.00"/> - <argument name="shippingTotal" value="15.00"/> - <argument name="shippingMethod" value="Flat Rate - Fixed"/> - <argument name="total" value="495.00"/> - </actionGroup> - - <!-- Check ship to information in checkout --> - <comment userInput="Check ship to information in checkout" stepKey="commentCheckShipToInformationInCheckout" after="guestCheckoutCheckOrderSummary" /> - <actionGroup ref="CheckShipToInformationInCheckoutActionGroup" stepKey="guestCheckoutCheckShipToInformation" after="commentCheckShipToInformationInCheckout"> - <argument name="customerVar" value="CustomerEntityOne" /> - <argument name="customerAddressVar" value="CustomerAddressSimple" /> - </actionGroup> - - <!-- Check shipping method in checkout --> - <comment userInput="Check shipping method in checkout" stepKey="commentCheckShippingMethodInCheckout" after="guestCheckoutCheckShipToInformation" /> - <actionGroup ref="CheckShippingMethodInCheckoutActionGroup" stepKey="guestCheckoutCheckShippingMethod" after="commentCheckShippingMethodInCheckout"> - <argument name="shippingMethod" value="E2EB2CQuote.shippingMethod" /> - </actionGroup> - - <!-- Verify Simple Product 1 is in checkout cart items --> - <comment userInput="Verify Simple Product 1 is in checkout cart items" stepKey="commentVerifySimpleProduct1IsInCheckoutCartItems" after="guestCheckoutCheckShippingMethod" /> - <actionGroup ref="CheckProductInCheckoutCartItemsActionGroup" stepKey="guestCheckoutCheckSimpleProduct1InCartItems" after="commentVerifySimpleProduct1IsInCheckoutCartItems"> - <argument name="productVar" value="$$createSimpleProduct1$$"/> - </actionGroup> - - <!-- Verify Simple Product 2 is in checkout cart items --> - <comment userInput="Verify Simple Product 2 is in checkout cart items" stepKey="commentVerifySimpleProduct2IsInCheckoutCartItems" after="guestCheckoutCheckSimpleProduct1InCartItems" /> - <actionGroup ref="CheckProductInCheckoutCartItemsActionGroup" stepKey="guestCheckoutCheckSimpleProduct2InCartItems" after="commentVerifySimpleProduct2IsInCheckoutCartItems"> - <argument name="productVar" value="$$createSimpleProduct2$$"/> - </actionGroup> - - <comment userInput="Place order with check money order payment" stepKey="commentPlaceOrderWithCheckMoneyOrderPayment" after="guestCheckoutCheckSimpleProduct2InCartItems" /> - <actionGroup ref="CheckoutSelectCheckMoneyOrderPaymentActionGroup" stepKey="guestSelectCheckMoneyOrderPayment" after="commentPlaceOrderWithCheckMoneyOrderPayment"/> - <actionGroup ref="CheckBillingAddressInCheckoutActionGroup" stepKey="guestSeeBillingAddress" after="guestSelectCheckMoneyOrderPayment"> - <argument name="customerVar" value="CustomerEntityOne" /> - <argument name="customerAddressVar" value="CustomerAddressSimple" /> - </actionGroup> - <actionGroup ref="CheckoutPlaceOrderActionGroup" stepKey="guestPlaceorder" after="guestSeeBillingAddress"> - <argument name="orderNumberMessage" value="CONST.successGuestCheckoutOrderNumberMessage" /> - <argument name="emailYouMessage" value="CONST.successCheckoutEmailYouMessage" /> - </actionGroup> - <comment userInput="End of checking out" stepKey="endOfCheckingOut" after="guestPlaceorder" /> - </test> -</tests> diff --git a/app/code/Magento/Checkout/Test/Mftf/Test/EndToEndB2CGuestUserTest/EndToEndB2CGuestUserMysqlTest.xml b/app/code/Magento/Checkout/Test/Mftf/Test/EndToEndB2CGuestUserTest/EndToEndB2CGuestUserMysqlTest.xml new file mode 100644 index 0000000000000..c9d04b9843d95 --- /dev/null +++ b/app/code/Magento/Checkout/Test/Mftf/Test/EndToEndB2CGuestUserTest/EndToEndB2CGuestUserMysqlTest.xml @@ -0,0 +1,231 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + /** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +--> +<tests xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/testSchema.xsd"> + <test name="EndToEndB2CGuestUserMysqlTest"> + <!-- Step 3: User adds products to cart --> + <comment userInput="Start of adding products to cart" stepKey="startOfAddingProductsToCart" after="endOfBrowsingCatalog"/> + <!-- Add Simple Product 1 to cart --> + <comment userInput="Add Simple Product 1 to cart" stepKey="commentAddSimpleProduct1ToCart" after="startOfAddingProductsToCart"/> + <click selector="{{StorefrontHeaderSection.NavigationCategoryByName($$createCategory.name$$)}}" stepKey="cartClickCategory" after="commentAddSimpleProduct1ToCart"/> + <waitForLoadingMaskToDisappear stepKey="waitForCartCategoryloaded" after="cartClickCategory"/> + <actionGroup ref="StorefrontCheckCategoryActionGroup" stepKey="cartAssertCategory" after="waitForCartCategoryloaded"> + <argument name="category" value="$$createCategory$$"/> + <argument name="productCount" value="3"/> + </actionGroup> + <actionGroup ref="StorefrontCheckCategorySimpleProductActionGroup" stepKey="cartAssertSimpleProduct1" after="cartAssertCategory"> + <argument name="product" value="$$createSimpleProduct1$$"/> + </actionGroup> + <!-- @TODO: Move Image check to action group after MQE-697 is fixed --> + <grabAttributeFrom selector="{{StorefrontCategoryProductSection.ProductImageByName($$createSimpleProduct1.name$$)}}" userInput="src" stepKey="cartGrabSimpleProduct1ImageSrc" after="cartAssertSimpleProduct1"/> + <assertNotRegExp stepKey="cartAssertSimpleProduct1ImageNotDefault" after="cartGrabSimpleProduct1ImageSrc"> + <actualResult type="const">$cartGrabSimpleProduct1ImageSrc</actualResult> + <expectedResult type="const">'/placeholder\/small_image\.jpg/'</expectedResult> + </assertNotRegExp> + <click selector="{{StorefrontCategoryProductSection.ProductTitleByName($$createSimpleProduct1.name$$)}}" stepKey="cartClickSimpleProduct1" after="cartAssertSimpleProduct1ImageNotDefault"/> + <waitForLoadingMaskToDisappear stepKey="waitForCartSimpleProduct1loaded" after="cartClickSimpleProduct1"/> + <actionGroup ref="StorefrontCheckSimpleProductActionGroup" stepKey="cartAssertProduct1Page" after="waitForCartSimpleProduct1loaded"> + <argument name="product" value="$$createSimpleProduct1$$"/> + </actionGroup> + <!-- @TODO: Move Image check to action group after MQE-697 is fixed --> + <grabAttributeFrom selector="{{StorefrontProductInfoMainSection.productImage}}" userInput="src" stepKey="cartGrabSimpleProduct1PageImageSrc" after="cartAssertProduct1Page"/> + <assertNotRegExp stepKey="cartAssertSimpleProduct1PageImageNotDefault" after="cartGrabSimpleProduct1PageImageSrc"> + <actualResult type="const">$cartGrabSimpleProduct1PageImageSrc</actualResult> + <expectedResult type="const">'/placeholder\/image\.jpg/'</expectedResult> + </assertNotRegExp> + <actionGroup ref="StorefrontAddProductToCartActionGroup" stepKey="cartAddProduct1ToCart" after="cartAssertSimpleProduct1PageImageNotDefault"> + <argument name="product" value="$$createSimpleProduct1$$"/> + <argument name="productCount" value="1"/> + </actionGroup> + + <!-- Add Simple Product 2 to cart --> + <comment userInput="Add Simple Product 2 to cart" stepKey="commentAddSimpleProduct2ToCart" after="cartAddProduct1ToCart"/> + <click selector="{{StorefrontHeaderSection.NavigationCategoryByName($$createCategory.name$$)}}" stepKey="cartClickCategory1" after="commentAddSimpleProduct2ToCart"/> + <waitForLoadingMaskToDisappear stepKey="waitForCartCategory1loaded" after="cartClickCategory1"/> + <actionGroup ref="StorefrontCheckCategoryActionGroup" stepKey="cartAssertCategory1ForSimpleProduct2" after="waitForCartCategory1loaded"> + <argument name="category" value="$$createCategory$$"/> + <argument name="productCount" value="3"/> + </actionGroup> + <actionGroup ref="StorefrontCheckCategorySimpleProductActionGroup" stepKey="cartAssertSimpleProduct2" after="cartAssertCategory1ForSimpleProduct2"> + <argument name="product" value="$$createSimpleProduct2$$"/> + </actionGroup> + <!-- @TODO: Move Image check to action group after MQE-697 is fixed --> + <grabAttributeFrom selector="{{StorefrontCategoryProductSection.ProductImageByName($$createSimpleProduct2.name$$)}}" userInput="src" stepKey="cartGrabSimpleProduct2ImageSrc" after="cartAssertSimpleProduct2"/> + <assertNotRegExp stepKey="cartAssertSimpleProduct2ImageNotDefault" after="cartGrabSimpleProduct2ImageSrc"> + <actualResult type="const">$cartGrabSimpleProduct2ImageSrc</actualResult> + <expectedResult type="const">'/placeholder\/small_image\.jpg/'</expectedResult> + </assertNotRegExp> + <actionGroup ref="StorefrontAddCategoryProductToCartActionGroup" stepKey="cartAddProduct2ToCart" after="cartAssertSimpleProduct2ImageNotDefault"> + <argument name="product" value="$$createSimpleProduct2$$"/> + <!-- @TODO: Change to scalar value after MQE-498 is implemented --> + <argument name="productCount" value="CONST.two"/> + </actionGroup> + + <!-- Check products in minicart --> + <!-- Check simple product 1 in minicart --> + <comment userInput="Check simple product 1 in minicart" stepKey="commentCheckSimpleProduct1InMinicart" after="cartAddProduct2ToCart"/> + <actionGroup ref="StorefrontOpenMinicartAndCheckSimpleProductActionGroup" stepKey="cartOpenMinicartAndCheckSimpleProduct1" after="commentCheckSimpleProduct1InMinicart"> + <argument name="product" value="$$createSimpleProduct1$$"/> + </actionGroup> + <!-- @TODO: Move Image check to action group after MQE-697 is fixed --> + <grabAttributeFrom selector="{{StorefrontMinicartSection.productImageByName($$createSimpleProduct1.name$$)}}" userInput="src" stepKey="cartMinicartGrabSimpleProduct1ImageSrc" after="cartOpenMinicartAndCheckSimpleProduct1"/> + <assertNotRegExp stepKey="cartMinicartAssertSimpleProduct1ImageNotDefault" after="cartMinicartGrabSimpleProduct1ImageSrc"> + <actualResult type="const">$cartMinicartGrabSimpleProduct1ImageSrc</actualResult> + <expectedResult type="const">'/placeholder\/thumbnail\.jpg/'</expectedResult> + </assertNotRegExp> + <click selector="{{StorefrontMinicartSection.productLinkByName($$createSimpleProduct1.name$$)}}" stepKey="cartMinicartClickSimpleProduct1" after="cartMinicartAssertSimpleProduct1ImageNotDefault"/> + <waitForLoadingMaskToDisappear stepKey="waitForMinicartSimpleProduct1loaded" after="cartMinicartClickSimpleProduct1"/> + <actionGroup ref="StorefrontCheckSimpleProductActionGroup" stepKey="cartAssertMinicartProduct1Page" after="waitForMinicartSimpleProduct1loaded"> + <argument name="product" value="$$createSimpleProduct1$$"/> + </actionGroup> + <!-- @TODO: Move Image check to action group after MQE-697 is fixed --> + <grabAttributeFrom selector="{{StorefrontProductInfoMainSection.productImage}}" userInput="src" stepKey="cartMinicartGrabSimpleProduct1PageImageSrc" after="cartAssertMinicartProduct1Page"/> + <assertNotRegExp stepKey="cartMinicartAssertSimpleProduct1PageImageNotDefault" after="cartMinicartGrabSimpleProduct1PageImageSrc"> + <actualResult type="const">$cartMinicartGrabSimpleProduct1PageImageSrc</actualResult> + <expectedResult type="const">'/placeholder\/image\.jpg/'</expectedResult> + </assertNotRegExp> + <actionGroup ref="StorefrontOpenMinicartAndCheckSimpleProductActionGroup" stepKey="cartOpenMinicartAndCheckSimpleProduct2" after="cartMinicartAssertSimpleProduct1PageImageNotDefault"> + <argument name="product" value="$$createSimpleProduct2$$"/> + </actionGroup> + <!-- Check simple product2 in minicart --> + <comment userInput="Check simple product 2 in minicart" stepKey="commentCheckSimpleProduct2InMinicart" after="cartOpenMinicartAndCheckSimpleProduct2"/> + <!-- @TODO: Move Image check to action group after MQE-697 is fixed --> + <grabAttributeFrom selector="{{StorefrontMinicartSection.productImageByName($$createSimpleProduct2.name$$)}}" userInput="src" stepKey="cartMinicartGrabSimpleProduct2ImageSrc" after="commentCheckSimpleProduct2InMinicart"/> + <assertNotRegExp stepKey="cartMinicartAssertSimpleProduct2ImageNotDefault" after="cartMinicartGrabSimpleProduct2ImageSrc"> + <actualResult type="const">$cartMinicartGrabSimpleProduct2ImageSrc</actualResult> + <expectedResult type="const">'/placeholder\/thumbnail\.jpg/'</expectedResult> + </assertNotRegExp> + <click selector="{{StorefrontMinicartSection.productLinkByName($$createSimpleProduct2.name$$)}}" stepKey="cartMinicartClickSimpleProduct2" after="cartMinicartAssertSimpleProduct2ImageNotDefault"/> + <waitForLoadingMaskToDisappear stepKey="waitForMinicartSimpleProduct2loaded" after="cartMinicartClickSimpleProduct2"/> + <actionGroup ref="StorefrontCheckSimpleProductActionGroup" stepKey="cartAssertMinicartProduct2Page" after="waitForMinicartSimpleProduct2loaded"> + <argument name="product" value="$$createSimpleProduct2$$"/> + </actionGroup> + <!-- @TODO: Move Image check to action group after MQE-697 is fixed --> + <grabAttributeFrom selector="{{StorefrontProductInfoMainSection.productImage}}" userInput="src" stepKey="cartMinicartGrabSimpleProduct2PageImageSrc" after="cartAssertMinicartProduct2Page"/> + <assertNotRegExp stepKey="cartMinicartAssertSimpleProduct2PageImageNotDefault" after="cartMinicartGrabSimpleProduct2PageImageSrc"> + <actualResult type="const">$cartMinicartGrabSimpleProduct2PageImageSrc</actualResult> + <expectedResult type="const">'/placeholder\/image\.jpg/'</expectedResult> + </assertNotRegExp> + + <!-- Check products in cart --> + <comment userInput="Check cart information" stepKey="commentCheckCartInformation" after="cartMinicartAssertSimpleProduct2PageImageNotDefault"/> + <actionGroup ref="StorefrontOpenCartFromMinicartActionGroup" stepKey="cartOpenCart" after="commentCheckCartInformation"/> + <actionGroup ref="StorefrontCheckCartActionGroup" stepKey="cartAssertCart" after="cartOpenCart"> + <argument name="subtotal" value="480.00"/> + <argument name="shipping" value="15.00"/> + <argument name="shippingMethod" value="Flat Rate - Fixed"/> + <argument name="total" value="495.00"/> + </actionGroup> + + <!-- Check simple product 1 in cart --> + <comment userInput="Check simple product 1 in cart" stepKey="commentCheckSimpleProduct1InCart" after="cartAssertCart"/> + <actionGroup ref="StorefrontCheckCartSimpleProductActionGroup" stepKey="cartAssertCartSimpleProduct1" after="commentCheckSimpleProduct1InCart"> + <argument name="product" value="$$createSimpleProduct1$$"/> + <!-- @TODO: Change to scalar value after MQE-498 is implemented --> + <argument name="productQuantity" value="CONST.one"/> + </actionGroup> + <!-- @TODO: Move Image check to action group after MQE-697 is fixed --> + <grabAttributeFrom selector="{{CheckoutCartProductSection.ProductImageByName($$createSimpleProduct1.name$$)}}" userInput="src" stepKey="cartCartGrabSimpleProduct1ImageSrc" after="cartAssertCartSimpleProduct1"/> + <assertNotRegExp stepKey="cartCartAssertSimpleProduct1ImageNotDefault" after="cartCartGrabSimpleProduct1ImageSrc"> + <actualResult type="const">$cartCartGrabSimpleProduct1ImageSrc</actualResult> + <expectedResult type="const">'/placeholder\/small_image\.jpg/'</expectedResult> + </assertNotRegExp> + <click selector="{{CheckoutCartProductSection.ProductLinkByName($$createSimpleProduct1.name$$)}}" stepKey="cartClickCartSimpleProduct1" after="cartCartAssertSimpleProduct1ImageNotDefault"/> + <waitForLoadingMaskToDisappear stepKey="waitForCartSimpleProduct1loadedAgain" after="cartClickCartSimpleProduct1"/> + <actionGroup ref="StorefrontCheckSimpleProductActionGroup" stepKey="cartAssertCartProduct1Page" after="waitForCartSimpleProduct1loadedAgain"> + <argument name="product" value="$$createSimpleProduct1$$"/> + </actionGroup> + <!-- @TODO: Move Image check to action group after MQE-697 is fixed --> + <grabAttributeFrom selector="{{StorefrontProductInfoMainSection.productImage}}" userInput="src" stepKey="cartCartGrabSimpleProduct2PageImageSrc1" after="cartAssertCartProduct1Page"/> + <assertNotRegExp stepKey="cartCartAssertSimpleProduct2PageImageNotDefault1" after="cartCartGrabSimpleProduct2PageImageSrc1"> + <actualResult type="const">$cartCartGrabSimpleProduct2PageImageSrc1</actualResult> + <expectedResult type="const">'/placeholder\/image\.jpg/'</expectedResult> + </assertNotRegExp> + + <!-- Check simple product 2 in cart --> + <comment userInput="Check simple product 2 in cart" stepKey="commentCheckSimpleProduct2InCart" after="cartCartAssertSimpleProduct2PageImageNotDefault1"/> + <actionGroup ref="StorefrontOpenCartFromMinicartActionGroup" stepKey="cartOpenCart1" after="commentCheckSimpleProduct2InCart"/> + <actionGroup ref="StorefrontCheckCartSimpleProductActionGroup" stepKey="cartAssertCartSimpleProduct2" after="cartOpenCart1"> + <argument name="product" value="$$createSimpleProduct2$$"/> + <!-- @TODO: Change to scalar value after MQE-498 is implemented --> + <argument name="productQuantity" value="CONST.one"/> + </actionGroup> + <!-- @TODO: Move Image check to action group after MQE-697 is fixed --> + <grabAttributeFrom selector="{{CheckoutCartProductSection.ProductImageByName($$createSimpleProduct2.name$$)}}" userInput="src" stepKey="cartCartGrabSimpleProduct2ImageSrc" after="cartAssertCartSimpleProduct2"/> + <assertNotRegExp stepKey="cartCartAssertSimpleProduct2ImageNotDefault" after="cartCartGrabSimpleProduct2ImageSrc"> + <actualResult type="const">$cartCartGrabSimpleProduct2ImageSrc</actualResult> + <expectedResult type="const">'/placeholder\/small_image\.jpg/'</expectedResult> + </assertNotRegExp> + <click selector="{{CheckoutCartProductSection.ProductLinkByName($$createSimpleProduct2.name$$)}}" stepKey="cartClickCartSimpleProduct2" after="cartCartAssertSimpleProduct2ImageNotDefault"/> + <waitForLoadingMaskToDisappear stepKey="waitForCartSimpleProduct2loaded" after="cartClickCartSimpleProduct2"/> + <actionGroup ref="StorefrontCheckSimpleProductActionGroup" stepKey="cartAssertCartProduct2Page" after="waitForCartSimpleProduct2loaded"> + <argument name="product" value="$$createSimpleProduct2$$"/> + </actionGroup> + <!-- @TODO: Move Image check to action group after MQE-697 is fixed --> + <grabAttributeFrom selector="{{StorefrontProductInfoMainSection.productImage}}" userInput="src" stepKey="cartCartGrabSimpleProduct2PageImageSrc2" after="cartAssertCartProduct2Page"/> + <assertNotRegExp stepKey="cartCartAssertSimpleProduct2PageImageNotDefault2" after="cartCartGrabSimpleProduct2PageImageSrc2"> + <actualResult type="const">$cartCartGrabSimpleProduct2PageImageSrc2</actualResult> + <expectedResult type="const">'/placeholder\/image\.jpg/'</expectedResult> + </assertNotRegExp> + <comment userInput="End of adding products to cart" stepKey="endOfAddingProductsToCart" after="cartCartAssertSimpleProduct2PageImageNotDefault2"/> + + <!-- Step 6: Check out --> + <comment userInput="Start of checking out" stepKey="startOfCheckingOut" after="endOfUsingCouponCode"/> + <actionGroup ref="GoToCheckoutFromMinicartActionGroup" stepKey="guestGoToCheckoutFromMinicart" after="startOfCheckingOut"/> + <actionGroup ref="GuestCheckoutFillingShippingSectionActionGroup" stepKey="guestCheckoutFillingShippingSection" after="guestGoToCheckoutFromMinicart"> + <argument name="customerVar" value="CustomerEntityOne"/> + <argument name="customerAddressVar" value="CustomerAddressSimple"/> + </actionGroup> + + <!-- Check order summary in checkout --> + <comment userInput="Check order summary in checkout" stepKey="commentCheckOrderSummaryInCheckout" after="guestCheckoutFillingShippingSection"/> + <actionGroup ref="CheckOrderSummaryInCheckoutActionGroup" stepKey="guestCheckoutCheckOrderSummary" after="commentCheckOrderSummaryInCheckout"> + <argument name="subtotal" value="480.00"/> + <argument name="shippingTotal" value="15.00"/> + <argument name="shippingMethod" value="Flat Rate - Fixed"/> + <argument name="total" value="495.00"/> + </actionGroup> + + <!-- Check ship to information in checkout --> + <comment userInput="Check ship to information in checkout" stepKey="commentCheckShipToInformationInCheckout" after="guestCheckoutCheckOrderSummary"/> + <actionGroup ref="CheckShipToInformationInCheckoutActionGroup" stepKey="guestCheckoutCheckShipToInformation" after="commentCheckShipToInformationInCheckout"> + <argument name="customerVar" value="CustomerEntityOne"/> + <argument name="customerAddressVar" value="CustomerAddressSimple"/> + </actionGroup> + + <!-- Check shipping method in checkout --> + <comment userInput="Check shipping method in checkout" stepKey="commentCheckShippingMethodInCheckout" after="guestCheckoutCheckShipToInformation"/> + <actionGroup ref="CheckShippingMethodInCheckoutActionGroup" stepKey="guestCheckoutCheckShippingMethod" after="commentCheckShippingMethodInCheckout"> + <argument name="shippingMethod" value="E2EB2CQuote.shippingMethod"/> + </actionGroup> + + <!-- Verify Simple Product 1 is in checkout cart items --> + <comment userInput="Verify Simple Product 1 is in checkout cart items" stepKey="commentVerifySimpleProduct1IsInCheckoutCartItems" after="guestCheckoutCheckShippingMethod"/> + <actionGroup ref="CheckProductInCheckoutCartItemsActionGroup" stepKey="guestCheckoutCheckSimpleProduct1InCartItems" after="commentVerifySimpleProduct1IsInCheckoutCartItems"> + <argument name="productVar" value="$$createSimpleProduct1$$"/> + </actionGroup> + + <!-- Verify Simple Product 2 is in checkout cart items --> + <comment userInput="Verify Simple Product 2 is in checkout cart items" stepKey="commentVerifySimpleProduct2IsInCheckoutCartItems" after="guestCheckoutCheckSimpleProduct1InCartItems"/> + <actionGroup ref="CheckProductInCheckoutCartItemsActionGroup" stepKey="guestCheckoutCheckSimpleProduct2InCartItems" after="commentVerifySimpleProduct2IsInCheckoutCartItems"> + <argument name="productVar" value="$$createSimpleProduct2$$"/> + </actionGroup> + + <comment userInput="Place order with check money order payment" stepKey="commentPlaceOrderWithCheckMoneyOrderPayment" after="guestCheckoutCheckSimpleProduct2InCartItems"/> + <actionGroup ref="CheckoutSelectCheckMoneyOrderPaymentActionGroup" stepKey="guestSelectCheckMoneyOrderPayment" after="commentPlaceOrderWithCheckMoneyOrderPayment"/> + <actionGroup ref="CheckBillingAddressInCheckoutActionGroup" stepKey="guestSeeBillingAddress" after="guestSelectCheckMoneyOrderPayment"> + <argument name="customerVar" value="CustomerEntityOne"/> + <argument name="customerAddressVar" value="CustomerAddressSimple"/> + </actionGroup> + <actionGroup ref="CheckoutPlaceOrderActionGroup" stepKey="guestPlaceorder" after="guestSeeBillingAddress"> + <argument name="orderNumberMessage" value="CONST.successGuestCheckoutOrderNumberMessage"/> + <argument name="emailYouMessage" value="CONST.successCheckoutEmailYouMessage"/> + </actionGroup> + <comment userInput="End of checking out" stepKey="endOfCheckingOut" after="guestPlaceorder"/> + </test> +</tests> diff --git a/app/code/Magento/Checkout/Test/Mftf/Test/EndToEndB2CGuestUserTest/EndToEndB2CGuestUserTest.xml b/app/code/Magento/Checkout/Test/Mftf/Test/EndToEndB2CGuestUserTest/EndToEndB2CGuestUserTest.xml new file mode 100644 index 0000000000000..8a0da7ab82a82 --- /dev/null +++ b/app/code/Magento/Checkout/Test/Mftf/Test/EndToEndB2CGuestUserTest/EndToEndB2CGuestUserTest.xml @@ -0,0 +1,231 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + /** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +--> +<tests xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/testSchema.xsd"> + <test name="EndToEndB2CGuestUserTest"> + <!-- Step 3: User adds products to cart --> + <comment userInput="Start of adding products to cart" stepKey="startOfAddingProductsToCart" after="endOfBrowsingCatalog"/> + <!-- Add Simple Product 1 to cart --> + <comment userInput="Add Simple Product 1 to cart" stepKey="commentAddSimpleProduct1ToCart" after="startOfAddingProductsToCart"/> + <click selector="{{StorefrontHeaderSection.NavigationCategoryByName($$createCategory.name$$)}}" stepKey="cartClickCategory" after="commentAddSimpleProduct1ToCart"/> + <waitForLoadingMaskToDisappear stepKey="waitForCartCategoryloaded" after="cartClickCategory"/> + <actionGroup ref="StorefrontCheckCategoryActionGroup" stepKey="cartAssertCategory" after="waitForCartCategoryloaded"> + <argument name="category" value="$$createCategory$$"/> + <argument name="productCount" value="3"/> + </actionGroup> + <actionGroup ref="StorefrontCheckCategorySimpleProductActionGroup" stepKey="cartAssertSimpleProduct1" after="cartAssertCategory"> + <argument name="product" value="$$createSimpleProduct1$$"/> + </actionGroup> + <!-- @TODO: Move Image check to action group after MQE-697 is fixed --> + <grabAttributeFrom selector="{{StorefrontCategoryProductSection.ProductImageByName($$createSimpleProduct1.name$$)}}" userInput="src" stepKey="cartGrabSimpleProduct1ImageSrc" after="cartAssertSimpleProduct1"/> + <assertNotRegExp stepKey="cartAssertSimpleProduct1ImageNotDefault" after="cartGrabSimpleProduct1ImageSrc"> + <actualResult type="const">$cartGrabSimpleProduct1ImageSrc</actualResult> + <expectedResult type="const">'/placeholder\/small_image\.jpg/'</expectedResult> + </assertNotRegExp> + <click selector="{{StorefrontCategoryProductSection.ProductTitleByName($$createSimpleProduct1.name$$)}}" stepKey="cartClickSimpleProduct1" after="cartAssertSimpleProduct1ImageNotDefault"/> + <waitForLoadingMaskToDisappear stepKey="waitForCartSimpleProduct1loaded" after="cartClickSimpleProduct1"/> + <actionGroup ref="StorefrontCheckSimpleProductActionGroup" stepKey="cartAssertProduct1Page" after="waitForCartSimpleProduct1loaded"> + <argument name="product" value="$$createSimpleProduct1$$"/> + </actionGroup> + <!-- @TODO: Move Image check to action group after MQE-697 is fixed --> + <grabAttributeFrom selector="{{StorefrontProductInfoMainSection.productImage}}" userInput="src" stepKey="cartGrabSimpleProduct1PageImageSrc" after="cartAssertProduct1Page"/> + <assertNotRegExp stepKey="cartAssertSimpleProduct1PageImageNotDefault" after="cartGrabSimpleProduct1PageImageSrc"> + <actualResult type="const">$cartGrabSimpleProduct1PageImageSrc</actualResult> + <expectedResult type="const">'/placeholder\/image\.jpg/'</expectedResult> + </assertNotRegExp> + <actionGroup ref="StorefrontAddProductToCartActionGroup" stepKey="cartAddProduct1ToCart" after="cartAssertSimpleProduct1PageImageNotDefault"> + <argument name="product" value="$$createSimpleProduct1$$"/> + <argument name="productCount" value="1"/> + </actionGroup> + + <!-- Add Simple Product 2 to cart --> + <comment userInput="Add Simple Product 2 to cart" stepKey="commentAddSimpleProduct2ToCart" after="cartAddProduct1ToCart"/> + <click selector="{{StorefrontHeaderSection.NavigationCategoryByName($$createCategory.name$$)}}" stepKey="cartClickCategory1" after="commentAddSimpleProduct2ToCart"/> + <waitForLoadingMaskToDisappear stepKey="waitForCartCategory1loaded" after="cartClickCategory1"/> + <actionGroup ref="StorefrontCheckCategoryActionGroup" stepKey="cartAssertCategory1ForSimpleProduct2" after="waitForCartCategory1loaded"> + <argument name="category" value="$$createCategory$$"/> + <argument name="productCount" value="3"/> + </actionGroup> + <actionGroup ref="StorefrontCheckCategorySimpleProductActionGroup" stepKey="cartAssertSimpleProduct2" after="cartAssertCategory1ForSimpleProduct2"> + <argument name="product" value="$$createSimpleProduct2$$"/> + </actionGroup> + <!-- @TODO: Move Image check to action group after MQE-697 is fixed --> + <grabAttributeFrom selector="{{StorefrontCategoryProductSection.ProductImageByName($$createSimpleProduct2.name$$)}}" userInput="src" stepKey="cartGrabSimpleProduct2ImageSrc" after="cartAssertSimpleProduct2"/> + <assertNotRegExp stepKey="cartAssertSimpleProduct2ImageNotDefault" after="cartGrabSimpleProduct2ImageSrc"> + <actualResult type="const">$cartGrabSimpleProduct2ImageSrc</actualResult> + <expectedResult type="const">'/placeholder\/small_image\.jpg/'</expectedResult> + </assertNotRegExp> + <actionGroup ref="StorefrontAddCategoryProductToCartActionGroup" stepKey="cartAddProduct2ToCart" after="cartAssertSimpleProduct2ImageNotDefault"> + <argument name="product" value="$$createSimpleProduct2$$"/> + <!-- @TODO: Change to scalar value after MQE-498 is implemented --> + <argument name="productCount" value="CONST.two"/> + </actionGroup> + + <!-- Check products in minicart --> + <!-- Check simple product 1 in minicart --> + <comment userInput="Check simple product 1 in minicart" stepKey="commentCheckSimpleProduct1InMinicart" after="cartAddProduct2ToCart"/> + <actionGroup ref="StorefrontOpenMinicartAndCheckSimpleProductActionGroup" stepKey="cartOpenMinicartAndCheckSimpleProduct1" after="commentCheckSimpleProduct1InMinicart"> + <argument name="product" value="$$createSimpleProduct1$$"/> + </actionGroup> + <!-- @TODO: Move Image check to action group after MQE-697 is fixed --> + <grabAttributeFrom selector="{{StorefrontMinicartSection.productImageByName($$createSimpleProduct1.name$$)}}" userInput="src" stepKey="cartMinicartGrabSimpleProduct1ImageSrc" after="cartOpenMinicartAndCheckSimpleProduct1"/> + <assertNotRegExp stepKey="cartMinicartAssertSimpleProduct1ImageNotDefault" after="cartMinicartGrabSimpleProduct1ImageSrc"> + <actualResult type="const">$cartMinicartGrabSimpleProduct1ImageSrc</actualResult> + <expectedResult type="const">'/placeholder\/thumbnail\.jpg/'</expectedResult> + </assertNotRegExp> + <click selector="{{StorefrontMinicartSection.productLinkByName($$createSimpleProduct1.name$$)}}" stepKey="cartMinicartClickSimpleProduct1" after="cartMinicartAssertSimpleProduct1ImageNotDefault"/> + <waitForLoadingMaskToDisappear stepKey="waitForMinicartSimpleProduct1loaded" after="cartMinicartClickSimpleProduct1"/> + <actionGroup ref="StorefrontCheckSimpleProductActionGroup" stepKey="cartAssertMinicartProduct1Page" after="waitForMinicartSimpleProduct1loaded"> + <argument name="product" value="$$createSimpleProduct1$$"/> + </actionGroup> + <!-- @TODO: Move Image check to action group after MQE-697 is fixed --> + <grabAttributeFrom selector="{{StorefrontProductInfoMainSection.productImage}}" userInput="src" stepKey="cartMinicartGrabSimpleProduct1PageImageSrc" after="cartAssertMinicartProduct1Page"/> + <assertNotRegExp stepKey="cartMinicartAssertSimpleProduct1PageImageNotDefault" after="cartMinicartGrabSimpleProduct1PageImageSrc"> + <actualResult type="const">$cartMinicartGrabSimpleProduct1PageImageSrc</actualResult> + <expectedResult type="const">'/placeholder\/image\.jpg/'</expectedResult> + </assertNotRegExp> + <actionGroup ref="StorefrontOpenMinicartAndCheckSimpleProductActionGroup" stepKey="cartOpenMinicartAndCheckSimpleProduct2" after="cartMinicartAssertSimpleProduct1PageImageNotDefault"> + <argument name="product" value="$$createSimpleProduct2$$"/> + </actionGroup> + <!-- Check simple product2 in minicart --> + <comment userInput="Check simple product 2 in minicart" stepKey="commentCheckSimpleProduct2InMinicart" after="cartOpenMinicartAndCheckSimpleProduct2"/> + <!-- @TODO: Move Image check to action group after MQE-697 is fixed --> + <grabAttributeFrom selector="{{StorefrontMinicartSection.productImageByName($$createSimpleProduct2.name$$)}}" userInput="src" stepKey="cartMinicartGrabSimpleProduct2ImageSrc" after="commentCheckSimpleProduct2InMinicart"/> + <assertNotRegExp stepKey="cartMinicartAssertSimpleProduct2ImageNotDefault" after="cartMinicartGrabSimpleProduct2ImageSrc"> + <actualResult type="const">$cartMinicartGrabSimpleProduct2ImageSrc</actualResult> + <expectedResult type="const">'/placeholder\/thumbnail\.jpg/'</expectedResult> + </assertNotRegExp> + <click selector="{{StorefrontMinicartSection.productLinkByName($$createSimpleProduct2.name$$)}}" stepKey="cartMinicartClickSimpleProduct2" after="cartMinicartAssertSimpleProduct2ImageNotDefault"/> + <waitForLoadingMaskToDisappear stepKey="waitForMinicartSimpleProduct2loaded" after="cartMinicartClickSimpleProduct2"/> + <actionGroup ref="StorefrontCheckSimpleProductActionGroup" stepKey="cartAssertMinicartProduct2Page" after="waitForMinicartSimpleProduct2loaded"> + <argument name="product" value="$$createSimpleProduct2$$"/> + </actionGroup> + <!-- @TODO: Move Image check to action group after MQE-697 is fixed --> + <grabAttributeFrom selector="{{StorefrontProductInfoMainSection.productImage}}" userInput="src" stepKey="cartMinicartGrabSimpleProduct2PageImageSrc" after="cartAssertMinicartProduct2Page"/> + <assertNotRegExp stepKey="cartMinicartAssertSimpleProduct2PageImageNotDefault" after="cartMinicartGrabSimpleProduct2PageImageSrc"> + <actualResult type="const">$cartMinicartGrabSimpleProduct2PageImageSrc</actualResult> + <expectedResult type="const">'/placeholder\/image\.jpg/'</expectedResult> + </assertNotRegExp> + + <!-- Check products in cart --> + <comment userInput="Check cart information" stepKey="commentCheckCartInformation" after="cartMinicartAssertSimpleProduct2PageImageNotDefault"/> + <actionGroup ref="StorefrontOpenCartFromMinicartActionGroup" stepKey="cartOpenCart" after="commentCheckCartInformation"/> + <actionGroup ref="StorefrontCheckCartActionGroup" stepKey="cartAssertCart" after="cartOpenCart"> + <argument name="subtotal" value="480.00"/> + <argument name="shipping" value="15.00"/> + <argument name="shippingMethod" value="Flat Rate - Fixed"/> + <argument name="total" value="495.00"/> + </actionGroup> + + <!-- Check simple product 1 in cart --> + <comment userInput="Check simple product 1 in cart" stepKey="commentCheckSimpleProduct1InCart" after="cartAssertCart"/> + <actionGroup ref="StorefrontCheckCartSimpleProductActionGroup" stepKey="cartAssertCartSimpleProduct1" after="commentCheckSimpleProduct1InCart"> + <argument name="product" value="$$createSimpleProduct1$$"/> + <!-- @TODO: Change to scalar value after MQE-498 is implemented --> + <argument name="productQuantity" value="CONST.one"/> + </actionGroup> + <!-- @TODO: Move Image check to action group after MQE-697 is fixed --> + <grabAttributeFrom selector="{{CheckoutCartProductSection.ProductImageByName($$createSimpleProduct1.name$$)}}" userInput="src" stepKey="cartCartGrabSimpleProduct1ImageSrc" after="cartAssertCartSimpleProduct1"/> + <assertNotRegExp stepKey="cartCartAssertSimpleProduct1ImageNotDefault" after="cartCartGrabSimpleProduct1ImageSrc"> + <actualResult type="const">$cartCartGrabSimpleProduct1ImageSrc</actualResult> + <expectedResult type="const">'/placeholder\/small_image\.jpg/'</expectedResult> + </assertNotRegExp> + <click selector="{{CheckoutCartProductSection.ProductLinkByName($$createSimpleProduct1.name$$)}}" stepKey="cartClickCartSimpleProduct1" after="cartCartAssertSimpleProduct1ImageNotDefault"/> + <waitForLoadingMaskToDisappear stepKey="waitForCartSimpleProduct1loadedAgain" after="cartClickCartSimpleProduct1"/> + <actionGroup ref="StorefrontCheckSimpleProductActionGroup" stepKey="cartAssertCartProduct1Page" after="waitForCartSimpleProduct1loadedAgain"> + <argument name="product" value="$$createSimpleProduct1$$"/> + </actionGroup> + <!-- @TODO: Move Image check to action group after MQE-697 is fixed --> + <grabAttributeFrom selector="{{StorefrontProductInfoMainSection.productImage}}" userInput="src" stepKey="cartCartGrabSimpleProduct2PageImageSrc1" after="cartAssertCartProduct1Page"/> + <assertNotRegExp stepKey="cartCartAssertSimpleProduct2PageImageNotDefault1" after="cartCartGrabSimpleProduct2PageImageSrc1"> + <actualResult type="const">$cartCartGrabSimpleProduct2PageImageSrc1</actualResult> + <expectedResult type="const">'/placeholder\/image\.jpg/'</expectedResult> + </assertNotRegExp> + + <!-- Check simple product 2 in cart --> + <comment userInput="Check simple product 2 in cart" stepKey="commentCheckSimpleProduct2InCart" after="cartCartAssertSimpleProduct2PageImageNotDefault1"/> + <actionGroup ref="StorefrontOpenCartFromMinicartActionGroup" stepKey="cartOpenCart1" after="commentCheckSimpleProduct2InCart"/> + <actionGroup ref="StorefrontCheckCartSimpleProductActionGroup" stepKey="cartAssertCartSimpleProduct2" after="cartOpenCart1"> + <argument name="product" value="$$createSimpleProduct2$$"/> + <!-- @TODO: Change to scalar value after MQE-498 is implemented --> + <argument name="productQuantity" value="CONST.one"/> + </actionGroup> + <!-- @TODO: Move Image check to action group after MQE-697 is fixed --> + <grabAttributeFrom selector="{{CheckoutCartProductSection.ProductImageByName($$createSimpleProduct2.name$$)}}" userInput="src" stepKey="cartCartGrabSimpleProduct2ImageSrc" after="cartAssertCartSimpleProduct2"/> + <assertNotRegExp stepKey="cartCartAssertSimpleProduct2ImageNotDefault" after="cartCartGrabSimpleProduct2ImageSrc"> + <actualResult type="const">$cartCartGrabSimpleProduct2ImageSrc</actualResult> + <expectedResult type="const">'/placeholder\/small_image\.jpg/'</expectedResult> + </assertNotRegExp> + <click selector="{{CheckoutCartProductSection.ProductLinkByName($$createSimpleProduct2.name$$)}}" stepKey="cartClickCartSimpleProduct2" after="cartCartAssertSimpleProduct2ImageNotDefault"/> + <waitForLoadingMaskToDisappear stepKey="waitForCartSimpleProduct2loaded" after="cartClickCartSimpleProduct2"/> + <actionGroup ref="StorefrontCheckSimpleProductActionGroup" stepKey="cartAssertCartProduct2Page" after="waitForCartSimpleProduct2loaded"> + <argument name="product" value="$$createSimpleProduct2$$"/> + </actionGroup> + <!-- @TODO: Move Image check to action group after MQE-697 is fixed --> + <grabAttributeFrom selector="{{StorefrontProductInfoMainSection.productImage}}" userInput="src" stepKey="cartCartGrabSimpleProduct2PageImageSrc2" after="cartAssertCartProduct2Page"/> + <assertNotRegExp stepKey="cartCartAssertSimpleProduct2PageImageNotDefault2" after="cartCartGrabSimpleProduct2PageImageSrc2"> + <actualResult type="const">$cartCartGrabSimpleProduct2PageImageSrc2</actualResult> + <expectedResult type="const">'/placeholder\/image\.jpg/'</expectedResult> + </assertNotRegExp> + <comment userInput="End of adding products to cart" stepKey="endOfAddingProductsToCart" after="cartCartAssertSimpleProduct2PageImageNotDefault2"/> + + <!-- Step 6: Check out --> + <comment userInput="Start of checking out" stepKey="startOfCheckingOut" after="endOfUsingCouponCode"/> + <actionGroup ref="GoToCheckoutFromMinicartActionGroup" stepKey="guestGoToCheckoutFromMinicart" after="startOfCheckingOut"/> + <actionGroup ref="GuestCheckoutFillingShippingSectionActionGroup" stepKey="guestCheckoutFillingShippingSection" after="guestGoToCheckoutFromMinicart"> + <argument name="customerVar" value="CustomerEntityOne"/> + <argument name="customerAddressVar" value="CustomerAddressSimple"/> + </actionGroup> + + <!-- Check order summary in checkout --> + <comment userInput="Check order summary in checkout" stepKey="commentCheckOrderSummaryInCheckout" after="guestCheckoutFillingShippingSection"/> + <actionGroup ref="CheckOrderSummaryInCheckoutActionGroup" stepKey="guestCheckoutCheckOrderSummary" after="commentCheckOrderSummaryInCheckout"> + <argument name="subtotal" value="480.00"/> + <argument name="shippingTotal" value="15.00"/> + <argument name="shippingMethod" value="Flat Rate - Fixed"/> + <argument name="total" value="495.00"/> + </actionGroup> + + <!-- Check ship to information in checkout --> + <comment userInput="Check ship to information in checkout" stepKey="commentCheckShipToInformationInCheckout" after="guestCheckoutCheckOrderSummary"/> + <actionGroup ref="CheckShipToInformationInCheckoutActionGroup" stepKey="guestCheckoutCheckShipToInformation" after="commentCheckShipToInformationInCheckout"> + <argument name="customerVar" value="CustomerEntityOne"/> + <argument name="customerAddressVar" value="CustomerAddressSimple"/> + </actionGroup> + + <!-- Check shipping method in checkout --> + <comment userInput="Check shipping method in checkout" stepKey="commentCheckShippingMethodInCheckout" after="guestCheckoutCheckShipToInformation"/> + <actionGroup ref="CheckShippingMethodInCheckoutActionGroup" stepKey="guestCheckoutCheckShippingMethod" after="commentCheckShippingMethodInCheckout"> + <argument name="shippingMethod" value="E2EB2CQuote.shippingMethod"/> + </actionGroup> + + <!-- Verify Simple Product 1 is in checkout cart items --> + <comment userInput="Verify Simple Product 1 is in checkout cart items" stepKey="commentVerifySimpleProduct1IsInCheckoutCartItems" after="guestCheckoutCheckShippingMethod"/> + <actionGroup ref="CheckProductInCheckoutCartItemsActionGroup" stepKey="guestCheckoutCheckSimpleProduct1InCartItems" after="commentVerifySimpleProduct1IsInCheckoutCartItems"> + <argument name="productVar" value="$$createSimpleProduct1$$"/> + </actionGroup> + + <!-- Verify Simple Product 2 is in checkout cart items --> + <comment userInput="Verify Simple Product 2 is in checkout cart items" stepKey="commentVerifySimpleProduct2IsInCheckoutCartItems" after="guestCheckoutCheckSimpleProduct1InCartItems"/> + <actionGroup ref="CheckProductInCheckoutCartItemsActionGroup" stepKey="guestCheckoutCheckSimpleProduct2InCartItems" after="commentVerifySimpleProduct2IsInCheckoutCartItems"> + <argument name="productVar" value="$$createSimpleProduct2$$"/> + </actionGroup> + + <comment userInput="Place order with check money order payment" stepKey="commentPlaceOrderWithCheckMoneyOrderPayment" after="guestCheckoutCheckSimpleProduct2InCartItems"/> + <actionGroup ref="CheckoutSelectCheckMoneyOrderPaymentActionGroup" stepKey="guestSelectCheckMoneyOrderPayment" after="commentPlaceOrderWithCheckMoneyOrderPayment"/> + <actionGroup ref="CheckBillingAddressInCheckoutActionGroup" stepKey="guestSeeBillingAddress" after="guestSelectCheckMoneyOrderPayment"> + <argument name="customerVar" value="CustomerEntityOne"/> + <argument name="customerAddressVar" value="CustomerAddressSimple"/> + </actionGroup> + <actionGroup ref="CheckoutPlaceOrderActionGroup" stepKey="guestPlaceorder" after="guestSeeBillingAddress"> + <argument name="orderNumberMessage" value="CONST.successGuestCheckoutOrderNumberMessage"/> + <argument name="emailYouMessage" value="CONST.successCheckoutEmailYouMessage"/> + </actionGroup> + <comment userInput="End of checking out" stepKey="endOfCheckingOut" after="guestPlaceorder"/> + </test> +</tests> diff --git a/app/code/Magento/Checkout/Test/Mftf/Test/EndToEndB2CLoggedInUserTest.xml b/app/code/Magento/Checkout/Test/Mftf/Test/EndToEndB2CLoggedInUserTest.xml index 6df859c9972c3..f47b536106400 100644 --- a/app/code/Magento/Checkout/Test/Mftf/Test/EndToEndB2CLoggedInUserTest.xml +++ b/app/code/Magento/Checkout/Test/Mftf/Test/EndToEndB2CLoggedInUserTest.xml @@ -24,7 +24,10 @@ </actionGroup> <!-- @TODO: Move Image check to action group after MQE-697 is fixed --> <grabAttributeFrom selector="{{StorefrontCategoryProductSection.ProductImageByName($$createSimpleProduct1.name$$)}}" userInput="src" stepKey="cartGrabSimpleProduct1ImageSrc" after="cartAssertSimpleProduct1"/> - <assertNotRegExp expected="'/placeholder\/small_image\.jpg/'" actual="$cartGrabSimpleProduct1ImageSrc" stepKey="cartAssertSimpleProduct1ImageNotDefault" after="cartGrabSimpleProduct1ImageSrc"/> + <assertNotRegExp stepKey="cartAssertSimpleProduct1ImageNotDefault" after="cartGrabSimpleProduct1ImageSrc"> + <actualResult type="const">$cartGrabSimpleProduct1ImageSrc</actualResult> + <expectedResult type="const">'/placeholder\/small_image\.jpg/'</expectedResult> + </assertNotRegExp> <click selector="{{StorefrontCategoryProductSection.ProductTitleByName($$createSimpleProduct1.name$$)}}" stepKey="cartClickSimpleProduct1" after="cartAssertSimpleProduct1ImageNotDefault"/> <waitForLoadingMaskToDisappear stepKey="waitForCartSimpleProduct1loaded" after="cartClickSimpleProduct1"/> <actionGroup ref="StorefrontCheckSimpleProductActionGroup" stepKey="cartAssertProduct1Page" after="waitForCartSimpleProduct1loaded"> @@ -32,7 +35,10 @@ </actionGroup> <!-- @TODO: Move Image check to action group after MQE-697 is fixed --> <grabAttributeFrom selector="{{StorefrontProductInfoMainSection.productImage}}" userInput="src" stepKey="cartGrabSimpleProduct1PageImageSrc" after="cartAssertProduct1Page"/> - <assertNotRegExp expected="'/placeholder\/image\.jpg/'" actual="$cartGrabSimpleProduct1PageImageSrc" stepKey="cartAssertSimpleProduct1PageImageNotDefault" after="cartGrabSimpleProduct1PageImageSrc"/> + <assertNotRegExp stepKey="cartAssertSimpleProduct1PageImageNotDefault" after="cartGrabSimpleProduct1PageImageSrc"> + <actualResult type="const">$cartGrabSimpleProduct1PageImageSrc</actualResult> + <expectedResult type="const">'/placeholder\/image\.jpg/'</expectedResult> + </assertNotRegExp> <actionGroup ref="StorefrontAddProductToCartActionGroup" stepKey="cartAddProduct1ToCart" after="cartAssertSimpleProduct1PageImageNotDefault"> <argument name="product" value="$$createSimpleProduct1$$"/> <argument name="productCount" value="1"/> @@ -51,7 +57,10 @@ </actionGroup> <!-- @TODO: Move Image check to action group after MQE-697 is fixed --> <grabAttributeFrom selector="{{StorefrontCategoryProductSection.ProductImageByName($$createSimpleProduct2.name$$)}}" userInput="src" stepKey="cartGrabSimpleProduct2ImageSrc" after="cartAssertSimpleProduct2"/> - <assertNotRegExp expected="'/placeholder\/small_image\.jpg/'" actual="$cartGrabSimpleProduct2ImageSrc" stepKey="cartAssertSimpleProduct2ImageNotDefault" after="cartGrabSimpleProduct2ImageSrc"/> + <assertNotRegExp stepKey="cartAssertSimpleProduct2ImageNotDefault" after="cartGrabSimpleProduct2ImageSrc"> + <actualResult type="const">$cartGrabSimpleProduct2ImageSrc</actualResult> + <expectedResult type="const">'/placeholder\/small_image\.jpg/'</expectedResult> + </assertNotRegExp> <actionGroup ref="StorefrontAddCategoryProductToCartActionGroup" stepKey="cartAddProduct2ToCart" after="cartAssertSimpleProduct2ImageNotDefault"> <argument name="product" value="$$createSimpleProduct2$$"/> <!-- @TODO: Change to scalar value after MQE-498 is implemented --> @@ -66,7 +75,10 @@ </actionGroup> <!-- @TODO: Move Image check to action group after MQE-697 is fixed --> <grabAttributeFrom selector="{{StorefrontMinicartSection.productImageByName($$createSimpleProduct1.name$$)}}" userInput="src" stepKey="cartMinicartGrabSimpleProduct1ImageSrc" after="cartOpenMinicartAndCheckSimpleProduct1"/> - <assertNotRegExp expected="'/placeholder\/thumbnail\.jpg/'" actual="$cartMinicartGrabSimpleProduct1ImageSrc" stepKey="cartMinicartAssertSimpleProduct1ImageNotDefault" after="cartMinicartGrabSimpleProduct1ImageSrc"/> + <assertNotRegExp stepKey="cartMinicartAssertSimpleProduct1ImageNotDefault" after="cartMinicartGrabSimpleProduct1ImageSrc"> + <actualResult type="const">$cartMinicartGrabSimpleProduct1ImageSrc</actualResult> + <expectedResult type="const">'/placeholder\/thumbnail\.jpg/'</expectedResult> + </assertNotRegExp> <click selector="{{StorefrontMinicartSection.productLinkByName($$createSimpleProduct1.name$$)}}" stepKey="cartMinicartClickSimpleProduct1" after="cartMinicartAssertSimpleProduct1ImageNotDefault"/> <waitForLoadingMaskToDisappear stepKey="waitForMinicartSimpleProduct1loaded" after="cartMinicartClickSimpleProduct1"/> <actionGroup ref="StorefrontCheckSimpleProductActionGroup" stepKey="cartAssertMinicartProduct1Page" after="waitForMinicartSimpleProduct1loaded"> @@ -74,7 +86,10 @@ </actionGroup> <!-- @TODO: Move Image check to action group after MQE-697 is fixed --> <grabAttributeFrom selector="{{StorefrontProductInfoMainSection.productImage}}" userInput="src" stepKey="cartMinicartGrabSimpleProduct1PageImageSrc" after="cartAssertMinicartProduct1Page"/> - <assertNotRegExp expected="'/placeholder\/image\.jpg/'" actual="$cartMinicartGrabSimpleProduct1PageImageSrc" stepKey="cartMinicartAssertSimpleProduct1PageImageNotDefault" after="cartMinicartGrabSimpleProduct1PageImageSrc"/> + <assertNotRegExp stepKey="cartMinicartAssertSimpleProduct1PageImageNotDefault" after="cartMinicartGrabSimpleProduct1PageImageSrc"> + <actualResult type="const">$cartMinicartGrabSimpleProduct1PageImageSrc</actualResult> + <expectedResult type="const">'/placeholder\/image\.jpg/'</expectedResult> + </assertNotRegExp> <actionGroup ref="StorefrontOpenMinicartAndCheckSimpleProductActionGroup" stepKey="cartOpenMinicartAndCheckSimpleProduct2" after="cartMinicartAssertSimpleProduct1PageImageNotDefault"> <argument name="product" value="$$createSimpleProduct2$$"/> </actionGroup> @@ -82,7 +97,10 @@ <comment userInput="Check simple product 2 in minicart" stepKey="commentCheckSimpleProduct2InMinicart" after="cartOpenMinicartAndCheckSimpleProduct2"/> <!-- @TODO: Move Image check to action group after MQE-697 is fixed --> <grabAttributeFrom selector="{{StorefrontMinicartSection.productImageByName($$createSimpleProduct2.name$$)}}" userInput="src" stepKey="cartMinicartGrabSimpleProduct2ImageSrc" after="commentCheckSimpleProduct2InMinicart"/> - <assertNotRegExp expected="'/placeholder\/thumbnail\.jpg/'" actual="$cartMinicartGrabSimpleProduct2ImageSrc" stepKey="cartMinicartAssertSimpleProduct2ImageNotDefault" after="cartMinicartGrabSimpleProduct2ImageSrc"/> + <assertNotRegExp stepKey="cartMinicartAssertSimpleProduct2ImageNotDefault" after="cartMinicartGrabSimpleProduct2ImageSrc"> + <actualResult type="const">$cartMinicartGrabSimpleProduct2ImageSrc</actualResult> + <expectedResult type="const">'/placeholder\/thumbnail\.jpg/'</expectedResult> + </assertNotRegExp> <click selector="{{StorefrontMinicartSection.productLinkByName($$createSimpleProduct2.name$$)}}" stepKey="cartMinicartClickSimpleProduct2" after="cartMinicartAssertSimpleProduct2ImageNotDefault"/> <waitForLoadingMaskToDisappear stepKey="waitForMinicartSimpleProduct2loaded" after="cartMinicartClickSimpleProduct2"/> <actionGroup ref="StorefrontCheckSimpleProductActionGroup" stepKey="cartAssertMinicartProduct2Page" after="waitForMinicartSimpleProduct2loaded"> @@ -90,7 +108,10 @@ </actionGroup> <!-- @TODO: Move Image check to action group after MQE-697 is fixed --> <grabAttributeFrom selector="{{StorefrontProductInfoMainSection.productImage}}" userInput="src" stepKey="cartMinicartGrabSimpleProduct2PageImageSrc" after="cartAssertMinicartProduct2Page"/> - <assertNotRegExp expected="'/placeholder\/image\.jpg/'" actual="$cartMinicartGrabSimpleProduct2PageImageSrc" stepKey="cartMinicartAssertSimpleProduct2PageImageNotDefault" after="cartMinicartGrabSimpleProduct2PageImageSrc"/> + <assertNotRegExp stepKey="cartMinicartAssertSimpleProduct2PageImageNotDefault" after="cartMinicartGrabSimpleProduct2PageImageSrc"> + <actualResult type="const">$cartMinicartGrabSimpleProduct2PageImageSrc</actualResult> + <expectedResult type="const">'/placeholder\/image\.jpg/'</expectedResult> + </assertNotRegExp> <!-- Check products in cart --> <comment userInput="Check cart information" stepKey="commentCheckCartInformation" after="cartMinicartAssertSimpleProduct2PageImageNotDefault" /> @@ -111,7 +132,10 @@ </actionGroup> <!-- @TODO: Move Image check to action group after MQE-697 is fixed --> <grabAttributeFrom selector="{{CheckoutCartProductSection.ProductImageByName($$createSimpleProduct1.name$$)}}" userInput="src" stepKey="cartCartGrabSimpleProduct1ImageSrc" after="cartAssertCartSimpleProduct1"/> - <assertNotRegExp expected="'/placeholder\/small_image\.jpg/'" actual="$cartCartGrabSimpleProduct1ImageSrc" stepKey="cartCartAssertSimpleProduct1ImageNotDefault" after="cartCartGrabSimpleProduct1ImageSrc"/> + <assertNotRegExp stepKey="cartCartAssertSimpleProduct1ImageNotDefault" after="cartCartGrabSimpleProduct1ImageSrc"> + <actualResult type="const">$cartCartGrabSimpleProduct1ImageSrc</actualResult> + <expectedResult type="const">'/placeholder\/small_image\.jpg/'</expectedResult> + </assertNotRegExp> <click selector="{{CheckoutCartProductSection.ProductLinkByName($$createSimpleProduct1.name$$)}}" stepKey="cartClickCartSimpleProduct1" after="cartCartAssertSimpleProduct1ImageNotDefault"/> <waitForLoadingMaskToDisappear stepKey="waitForCartSimpleProduct1loadedAgain" after="cartClickCartSimpleProduct1"/> <actionGroup ref="StorefrontCheckSimpleProductActionGroup" stepKey="cartAssertCartProduct1Page" after="waitForCartSimpleProduct1loadedAgain"> @@ -119,7 +143,10 @@ </actionGroup> <!-- @TODO: Move Image check to action group after MQE-697 is fixed --> <grabAttributeFrom selector="{{StorefrontProductInfoMainSection.productImage}}" userInput="src" stepKey="cartCartGrabSimpleProduct2PageImageSrc1" after="cartAssertCartProduct1Page"/> - <assertNotRegExp expected="'/placeholder\/image\.jpg/'" actual="$cartCartGrabSimpleProduct2PageImageSrc1" stepKey="cartCartAssertSimpleProduct2PageImageNotDefault1" after="cartCartGrabSimpleProduct2PageImageSrc1"/> + <assertNotRegExp stepKey="cartCartAssertSimpleProduct2PageImageNotDefault1" after="cartCartGrabSimpleProduct2PageImageSrc1"> + <actualResult type="const">$cartCartGrabSimpleProduct2PageImageSrc1</actualResult> + <expectedResult type="const">'/placeholder\/image\.jpg/'</expectedResult> + </assertNotRegExp> <!-- Check simple product 2 in cart --> <comment userInput="Check simple product 2 in cart" stepKey="commentCheckSimpleProduct2InCart" after="cartCartAssertSimpleProduct2PageImageNotDefault1"/> @@ -131,7 +158,10 @@ </actionGroup> <!-- @TODO: Move Image check to action group after MQE-697 is fixed --> <grabAttributeFrom selector="{{CheckoutCartProductSection.ProductImageByName($$createSimpleProduct2.name$$)}}" userInput="src" stepKey="cartCartGrabSimpleProduct2ImageSrc" after="cartAssertCartSimpleProduct2"/> - <assertNotRegExp expected="'/placeholder\/small_image\.jpg/'" actual="$cartCartGrabSimpleProduct2ImageSrc" stepKey="cartCartAssertSimpleProduct2ImageNotDefault" after="cartCartGrabSimpleProduct2ImageSrc"/> + <assertNotRegExp stepKey="cartCartAssertSimpleProduct2ImageNotDefault" after="cartCartGrabSimpleProduct2ImageSrc"> + <actualResult type="const">$cartCartGrabSimpleProduct2ImageSrc</actualResult> + <expectedResult type="const">'/placeholder\/small_image\.jpg/'</expectedResult> + </assertNotRegExp> <click selector="{{CheckoutCartProductSection.ProductLinkByName($$createSimpleProduct2.name$$)}}" stepKey="cartClickCartSimpleProduct2" after="cartCartAssertSimpleProduct2ImageNotDefault"/> <waitForLoadingMaskToDisappear stepKey="waitForCartSimpleProduct2loaded" after="cartClickCartSimpleProduct2"/> <actionGroup ref="StorefrontCheckSimpleProductActionGroup" stepKey="cartAssertCartProduct2Page" after="waitForCartSimpleProduct2loaded"> @@ -139,7 +169,10 @@ </actionGroup> <!-- @TODO: Move Image check to action group after MQE-697 is fixed --> <grabAttributeFrom selector="{{StorefrontProductInfoMainSection.productImage}}" userInput="src" stepKey="cartCartGrabSimpleProduct2PageImageSrc2" after="cartAssertCartProduct2Page"/> - <assertNotRegExp expected="'/placeholder\/image\.jpg/'" actual="$cartCartGrabSimpleProduct2PageImageSrc2" stepKey="cartCartAssertSimpleProduct2PageImageNotDefault2" after="cartCartGrabSimpleProduct2PageImageSrc2"/> + <assertNotRegExp stepKey="cartCartAssertSimpleProduct2PageImageNotDefault2" after="cartCartGrabSimpleProduct2PageImageSrc2"> + <actualResult type="const">$cartCartGrabSimpleProduct2PageImageSrc2</actualResult> + <expectedResult type="const">'/placeholder\/image\.jpg/'</expectedResult> + </assertNotRegExp> <comment userInput="End of adding products to cart" stepKey="endOfAddingProductsToCart" after="cartCartAssertSimpleProduct2PageImageNotDefault2" /> <!-- Step 7: Check out --> diff --git a/app/code/Magento/Checkout/Test/Mftf/Test/IdentityOfDefaultBillingAndShippingAddressTest.xml b/app/code/Magento/Checkout/Test/Mftf/Test/IdentityOfDefaultBillingAndShippingAddressTest.xml index e2d1a1b9139c8..c3713206ed1d2 100644 --- a/app/code/Magento/Checkout/Test/Mftf/Test/IdentityOfDefaultBillingAndShippingAddressTest.xml +++ b/app/code/Magento/Checkout/Test/Mftf/Test/IdentityOfDefaultBillingAndShippingAddressTest.xml @@ -31,8 +31,14 @@ <amOnPage url="" stepKey="DoToStorefront"/> <!-- Fill out form for a new user with address --> - <actionGroup ref="SignUpNewUserFromStorefrontActionGroup" stepKey="signUpNewUser"> - <argument name="Customer" value="Simple_US_Customer_NY"/> + <actionGroup ref="StorefrontOpenCustomerAccountCreatePageActionGroup" stepKey="openCreateAccountPage"/> + <actionGroup ref="StorefrontFillCustomerAccountCreationFormActionGroup" stepKey="fillCreateAccountForm"> + <argument name="customer" value="Simple_US_Customer_NY"/> + </actionGroup> + <actionGroup ref="StorefrontClickCreateAnAccountCustomerAccountCreationFormActionGroup" stepKey="submitCreateAccountForm"/> + <actionGroup ref="AssertMessageCustomerCreateAccountActionGroup" stepKey="seeSuccessMessage"> + <argument name="messageType" value="success"/> + <argument name="message" value="Thank you for registering with Main Website Store."/> </actionGroup> <!-- Add simple product to cart --> diff --git a/app/code/Magento/Checkout/Test/Mftf/Test/NoErrorCartCheckoutForProductsDeletedFromMiniCartTest.xml b/app/code/Magento/Checkout/Test/Mftf/Test/NoErrorCartCheckoutForProductsDeletedFromMiniCartTest.xml index fd6656b1d1b28..ceaf72fff83bb 100644 --- a/app/code/Magento/Checkout/Test/Mftf/Test/NoErrorCartCheckoutForProductsDeletedFromMiniCartTest.xml +++ b/app/code/Magento/Checkout/Test/Mftf/Test/NoErrorCartCheckoutForProductsDeletedFromMiniCartTest.xml @@ -41,17 +41,16 @@ <see selector="{{StorefrontCategoryMainSection.SuccessMsg}}" userInput="You added $$createSimpleProduct.name$$ to your shopping cart." stepKey="seeAddedToCartMessage"/> <see selector="{{StorefrontMinicartSection.quantity}}" userInput="1" stepKey="seeCartQuantity"/> <!-- open the minicart --> - <click selector="{{StorefrontMinicartSection.showCart}}" stepKey="clickShowMinicart1"/> + <actionGroup ref="StorefrontClickOnMiniCartActionGroup" stepKey="clickShowMinicart1"/> <click selector="{{StorefrontMinicartSection.viewAndEditCart}}" stepKey="editProductFromMiniCart"/> - <click selector="{{StorefrontMinicartSection.showCart}}" stepKey="clickShowMinicart2"/> + <actionGroup ref="StorefrontClickOnMiniCartActionGroup" stepKey="clickShowMinicart2"/> <click selector="{{StorefrontMinicartSection.deleteMiniCartItem}}" stepKey="deleteMiniCartItem"/> <waitForElementVisible selector="{{StoreFrontRemoveItemModalSection.message}}" stepKey="waitFortheConfirmationModal"/> <see selector="{{StoreFrontRemoveItemModalSection.message}}" userInput="Are you sure you would like to remove this item from the shopping cart?" stepKey="seeDeleteConfirmationMessage"/> <click selector="{{StoreFrontRemoveItemModalSection.ok}}" stepKey="confirmDelete"/> <waitForPageLoad stepKey="waitForDeleteToFinish"/> <dontSeeElement selector="{{CheckoutCartProductSection.RemoveItem}}" stepKey="dontSeeDeleteProductFromCheckoutCart"/> - <click selector="{{StorefrontMinicartSection.showCart}}" stepKey="clickCart"/> - <waitForPageLoad stepKey="WaitForPageLoad3"/> + <actionGroup ref="StorefrontClickOnMiniCartActionGroup" stepKey="clickCart"/> <see userInput="You have no items in your shopping cart." stepKey="seeNoItemsInShoppingCart"/> </test> </tests> diff --git a/app/code/Magento/Checkout/Test/Mftf/Test/OnePageCheckoutAsCustomerUsingDefaultAddressTest.xml b/app/code/Magento/Checkout/Test/Mftf/Test/OnePageCheckoutAsCustomerUsingDefaultAddressTest.xml index f2501fdf4c0c6..3215503b3205a 100644 --- a/app/code/Magento/Checkout/Test/Mftf/Test/OnePageCheckoutAsCustomerUsingDefaultAddressTest.xml +++ b/app/code/Magento/Checkout/Test/Mftf/Test/OnePageCheckoutAsCustomerUsingDefaultAddressTest.xml @@ -30,7 +30,7 @@ </before> <after> <!-- Admin log out --> - <actionGroup ref="logout" stepKey="logout"/> + <actionGroup ref="AdminLogoutActionGroup" stepKey="logout"/> <!-- Customer log out --> <actionGroup ref="StorefrontCustomerLogoutActionGroup" stepKey="customerLogout"/> @@ -82,7 +82,7 @@ <grabTextFrom selector="{{CheckoutSuccessMainSection.orderNumber22}}" stepKey="grabOrderNumber"/> <!-- Login as admin --> - <actionGroup ref="LoginAsAdmin" stepKey="loginAsAdmin"/> + <actionGroup ref="AdminLoginActionGroup" stepKey="loginAsAdmin"/> <!-- Open created order in backend --> <amOnPage url="{{AdminOrdersPage.url}}" stepKey="goToOrders"/> diff --git a/app/code/Magento/Checkout/Test/Mftf/Test/OnePageCheckoutAsCustomerUsingNewAddressTest.xml b/app/code/Magento/Checkout/Test/Mftf/Test/OnePageCheckoutAsCustomerUsingNewAddressTest.xml index b072989bc968c..76a998fec8adc 100644 --- a/app/code/Magento/Checkout/Test/Mftf/Test/OnePageCheckoutAsCustomerUsingNewAddressTest.xml +++ b/app/code/Magento/Checkout/Test/Mftf/Test/OnePageCheckoutAsCustomerUsingNewAddressTest.xml @@ -14,7 +14,7 @@ <stories value="OnePageCheckout within Offline Payment Methods"/> <title value="OnePageCheckout as customer using new address test"/> <description value="Checkout as customer using new address"/> - <severity value="CRITICAL"/> + <severity value="BLOCKER"/> <testCaseId value="MC-14740"/> <group value="checkout"/> <group value="mtf_migrated"/> @@ -30,7 +30,7 @@ </before> <after> <!-- Admin log out --> - <actionGroup ref="logout" stepKey="logout"/> + <actionGroup ref="AdminLogoutActionGroup" stepKey="logout"/> <!-- Customer log out --> <actionGroup ref="StorefrontCustomerLogoutActionGroup" stepKey="customerLogout"/> @@ -95,7 +95,7 @@ <grabTextFrom selector="{{CheckoutSuccessMainSection.orderNumber22}}" stepKey="grabOrderNumber"/> <!-- Login as admin --> - <actionGroup ref="LoginAsAdmin" stepKey="loginAsAdmin"/> + <actionGroup ref="AdminLoginActionGroup" stepKey="loginAsAdmin"/> <!-- Open created order in backend --> <amOnPage url="{{AdminOrdersPage.url}}" stepKey="goToOrders"/> diff --git a/app/code/Magento/Checkout/Test/Mftf/Test/OnePageCheckoutAsCustomerUsingNonDefaultAddressTest.xml b/app/code/Magento/Checkout/Test/Mftf/Test/OnePageCheckoutAsCustomerUsingNonDefaultAddressTest.xml index 9b0da574d6307..340ff4159900a 100644 --- a/app/code/Magento/Checkout/Test/Mftf/Test/OnePageCheckoutAsCustomerUsingNonDefaultAddressTest.xml +++ b/app/code/Magento/Checkout/Test/Mftf/Test/OnePageCheckoutAsCustomerUsingNonDefaultAddressTest.xml @@ -14,7 +14,7 @@ <stories value="OnePageCheckout within Offline Payment Methods"/> <title value="OnePageCheckout as customer using non default address test"/> <description value="Checkout as customer using non default address"/> - <severity value="CRITICAL"/> + <severity value="BLOCKER"/> <testCaseId value="MC-14739"/> <group value="checkout"/> <group value="mtf_migrated"/> @@ -30,7 +30,7 @@ </before> <after> <!-- Admin log out --> - <actionGroup ref="logout" stepKey="logout"/> + <actionGroup ref="AdminLogoutActionGroup" stepKey="logout"/> <!-- Customer Log out --> <actionGroup ref="StorefrontCustomerLogoutActionGroup" stepKey="customerLogout"/> @@ -83,7 +83,7 @@ <seeElement selector="{{CheckoutSuccessMainSection.success}}" stepKey="orderIsSuccessfullyPlaced"/> <grabTextFrom selector="{{CheckoutSuccessMainSection.orderNumber22}}" stepKey="grabOrderNumber"/> - <actionGroup ref="LoginAsAdmin" stepKey="loginAsAdmin"/> + <actionGroup ref="AdminLoginActionGroup" stepKey="loginAsAdmin"/> <!-- Open created order in backend --> <amOnPage url="{{AdminOrdersPage.url}}" stepKey="goToOrders"/> diff --git a/app/code/Magento/Checkout/Test/Mftf/Test/OnePageCheckoutUsingSignInLinkTest.xml b/app/code/Magento/Checkout/Test/Mftf/Test/OnePageCheckoutUsingSignInLinkTest.xml index 80cf9c14b578e..1c03808ac71cf 100644 --- a/app/code/Magento/Checkout/Test/Mftf/Test/OnePageCheckoutUsingSignInLinkTest.xml +++ b/app/code/Magento/Checkout/Test/Mftf/Test/OnePageCheckoutUsingSignInLinkTest.xml @@ -30,7 +30,7 @@ </before> <after> <!-- Admin log out --> - <actionGroup ref="logout" stepKey="logout"/> + <actionGroup ref="AdminLogoutActionGroup" stepKey="logout"/> <!-- Customer Log out --> <actionGroup ref="StorefrontCustomerLogoutActionGroup" stepKey="customerLogout"/> @@ -76,7 +76,7 @@ <grabTextFrom selector="{{CheckoutSuccessMainSection.orderNumber22}}" stepKey="grabOrderNumber"/> <!-- Login as admin --> - <actionGroup ref="LoginAsAdmin" stepKey="loginAsAdmin"/> + <actionGroup ref="AdminLoginActionGroup" stepKey="loginAsAdmin"/> <!-- Open created order in backend --> <amOnPage url="{{AdminOrdersPage.url}}" stepKey="goToOrders"/> @@ -88,6 +88,9 @@ <!-- Assert that shipping and billing address are the same --> <grabTextFrom selector="{{AdminShipmentAddressInformationSection.shippingAddress}}" stepKey="shippingAddress"/> <grabTextFrom selector="{{AdminShipmentAddressInformationSection.billingAddress}}" stepKey="billingAddress"/> - <assertEquals stepKey="assertAddress" actual="$billingAddress" expected="$shippingAddress"/> + <assertEquals stepKey="assertAddress"> + <actualResult type="const">$billingAddress</actualResult> + <expectedResult type="const">$shippingAddress</expectedResult> + </assertEquals> </test> </tests> diff --git a/app/code/Magento/Checkout/Test/Mftf/Test/OnePageCheckoutWithAllProductTypesTest.xml b/app/code/Magento/Checkout/Test/Mftf/Test/OnePageCheckoutWithAllProductTypesTest.xml index 797172de5de45..e678bb0d2a87b 100644 --- a/app/code/Magento/Checkout/Test/Mftf/Test/OnePageCheckoutWithAllProductTypesTest.xml +++ b/app/code/Magento/Checkout/Test/Mftf/Test/OnePageCheckoutWithAllProductTypesTest.xml @@ -14,7 +14,7 @@ <stories value="OnePageCheckout within Offline Payment Methods"/> <title value="OnePageCheckout with all product types test"/> <description value="Checkout with all product types"/> - <severity value="CRITICAL"/> + <severity value="BLOCKER"/> <testCaseId value="MC-14742"/> <group value="checkout"/> <group value="mtf_migrated"/> @@ -111,6 +111,9 @@ <!-- Logout customer --> <actionGroup ref="StorefrontCustomerLogoutActionGroup" stepKey="customerLogoutStorefront"/> + + <!-- Reindex invalidated indices after product attribute has been created/deleted --> + <magentoCron groups="index" stepKey="reindexInvalidatedIndices"/> </after> <!-- Add Simple Product to cart --> @@ -192,7 +195,7 @@ <grabTextFrom selector="{{CheckoutSuccessMainSection.orderNumber22}}" stepKey="grabOrderNumber"/> <!-- Login as admin --> - <actionGroup ref="LoginAsAdmin" stepKey="loginAsAdmin"/> + <actionGroup ref="AdminLoginActionGroup" stepKey="loginAsAdmin"/> <!-- Open created order --> <amOnPage url="{{AdminOrdersPage.url}}" stepKey="goToOrders"/> @@ -204,7 +207,10 @@ <!-- Assert that addresses on order page the same --> <grabTextFrom selector="{{AdminShipmentAddressInformationSection.shippingAddress}}" stepKey="shippingAddressOrderPage"/> <grabTextFrom selector="{{AdminShipmentAddressInformationSection.billingAddress}}" stepKey="billingAddressOrderPage"/> - <assertEquals actual="$billingAddressOrderPage" expected="$shippingAddressOrderPage" stepKey="assertAddressOrderPage"/> + <assertEquals stepKey="assertAddressOrderPage"> + <actualResult type="const">$billingAddressOrderPage</actualResult> + <expectedResult type="const">$shippingAddressOrderPage</expectedResult> + </assertEquals> <!-- Assert order total --> <amOnPage url="{{StorefrontCustomerDashboardPage.url}}" stepKey="navigateToCustomerDashboardPage"/> @@ -219,7 +225,13 @@ <!-- Asserts that addresses in address book equal to addresses in order --> <grabTextFrom selector="{{CheckoutOrderSummarySection.shippingAddress}}" stepKey="shippingAddress"/> <grabTextFrom selector="{{CheckoutOrderSummarySection.billingAddress}}" stepKey="billingAddress"/> - <assertEquals actual="$shippingAddress" expected="$shippingAddressOrderPage" stepKey="assertShippingAddress"/> - <assertEquals actual="$billingAddress" expected="$billingAddressOrderPage" stepKey="assertBillingAddress"/> + <assertEquals stepKey="assertShippingAddress"> + <actualResult type="const">$shippingAddress</actualResult> + <expectedResult type="const">$shippingAddressOrderPage</expectedResult> + </assertEquals> + <assertEquals stepKey="assertBillingAddress"> + <actualResult type="const">$billingAddress</actualResult> + <expectedResult type="const">$billingAddressOrderPage</expectedResult> + </assertEquals> </test> </tests> diff --git a/app/code/Magento/Checkout/Test/Mftf/Test/ShoppingCartAndMiniShoppingCartPerCustomerTest.xml b/app/code/Magento/Checkout/Test/Mftf/Test/ShoppingCartAndMiniShoppingCartPerCustomerTest.xml index 3a9bec5cdfcf3..984f8933efa76 100644 --- a/app/code/Magento/Checkout/Test/Mftf/Test/ShoppingCartAndMiniShoppingCartPerCustomerTest.xml +++ b/app/code/Magento/Checkout/Test/Mftf/Test/ShoppingCartAndMiniShoppingCartPerCustomerTest.xml @@ -75,9 +75,9 @@ <waitForPageLoad stepKey="waitForSecondProductPageLoad"/> <!-- Fill the custom options values --> - <actionGroup ref="StorefrontSelectOptionDropDownActionGroup" stepKey="selectFirstOption"> - <argument name="optionTitle" value="ProductOptionValueDropdown"/> - <argument name="option" value="ProductOptionValueWithSkuDropdown1.title"/> + <actionGroup ref="StorefrontProductPageSelectDropDownOptionValueActionGroup" stepKey="selectFirstOption"> + <argument name="attributeLabel" value="{{ProductOptionValueDropdown.title}}"/> + <argument name="optionLabel" value="{{ProductOptionValueWithSkuDropdown1.title}}"/> </actionGroup> <fillField selector="{{StorefrontProductInfoMainSection.productOptionFieldInput(ProductOptionField.title)}}" userInput="OptionField" stepKey="fillProductOptionInputField"/> @@ -135,8 +135,7 @@ <see selector="{{StorefrontProductPageSection.orderTotal}}" userInput="{{quoteQty2Subtotal266.currency}}{{quoteQty2Subtotal266.total}}" stepKey="seeFirstCustomerOrderTotal"/> <!-- Assert products in mini cart for first customer --> - <amOnPage url="{{StorefrontHomePage.url}}" stepKey="goToStoreFrontHomePage"/> - <waitForPageLoad stepKey="waitForHomePageLoad"/> + <actionGroup ref="StorefrontOpenHomePageActionGroup" stepKey="goToStoreFrontHomePage"/> <actionGroup ref="AssertOneProductNameInMiniCartActionGroup" stepKey="assertFirstProductInMiniCart"> <argument name="productName" value="$$createSimpleProduct.name$$"/> </actionGroup> @@ -168,10 +167,8 @@ <see selector="{{StorefrontProductPageSection.orderTotal}}" userInput="{{quoteQty2Price123.currency}}{{quoteQty2Price123.total}}" stepKey="seeSecondCustomerOrderTotal"/> <!-- Assert product in mini cart --> - <amOnPage url="{{StorefrontHomePage.url}}" stepKey="goToHomePage"/> - <waitForPageLoad stepKey="waitForHomePageToLoad"/> - <click selector="{{StorefrontMinicartSection.showCart}}" stepKey="clickOnMiniCart"/> - <waitForPageLoad stepKey="waitForPageToLoad"/> + <actionGroup ref="StorefrontOpenHomePageActionGroup" stepKey="goToHomePage"/> + <actionGroup ref="StorefrontClickOnMiniCartActionGroup" stepKey="clickOnMiniCart"/> <actionGroup ref="AssertStorefrontMiniCartItemsActionGroup" stepKey="assertProductInMiniCart"> <argument name="productName" value="$$createSimpleProduct.name$$"/> <argument name="productPrice" value="$$createSimpleProduct.price$$"/> diff --git a/app/code/Magento/Checkout/Test/Mftf/Test/StoreFrontAddProductWithAllTypesOfCustomOptionToTheShoppingCartWithoutAnySelectedOptionTest.xml b/app/code/Magento/Checkout/Test/Mftf/Test/StoreFrontAddProductWithAllTypesOfCustomOptionToTheShoppingCartWithoutAnySelectedOptionTest.xml index 450bfff27125a..9e5542838745a 100644 --- a/app/code/Magento/Checkout/Test/Mftf/Test/StoreFrontAddProductWithAllTypesOfCustomOptionToTheShoppingCartWithoutAnySelectedOptionTest.xml +++ b/app/code/Magento/Checkout/Test/Mftf/Test/StoreFrontAddProductWithAllTypesOfCustomOptionToTheShoppingCartWithoutAnySelectedOptionTest.xml @@ -13,7 +13,7 @@ <title value="Add simple product with all types of custom options to cart without selecting any options"/> <description value="Add simple product with all types of custom options to cart without selecting any options"/> <testCaseId value="MC-14725"/> - <severity value="CRITICAL"/> + <severity value="BLOCKER"/> <group value="mtf_migrated"/> </annotations> diff --git a/app/code/Magento/Checkout/Test/Mftf/Test/StoreFrontCheckCustomerInfoCreatedByGuestTest.xml b/app/code/Magento/Checkout/Test/Mftf/Test/StoreFrontCheckCustomerInfoCreatedByGuestTest.xml index c08a930ba6224..bd81a1cfab604 100644 --- a/app/code/Magento/Checkout/Test/Mftf/Test/StoreFrontCheckCustomerInfoCreatedByGuestTest.xml +++ b/app/code/Magento/Checkout/Test/Mftf/Test/StoreFrontCheckCustomerInfoCreatedByGuestTest.xml @@ -31,7 +31,7 @@ <after> <deleteData createDataKey="product" stepKey="deleteProduct" /> <deleteData createDataKey="category" stepKey="deleteCategory" /> - <actionGroup ref="logout" stepKey="logoutFromAdmin"/> + <actionGroup ref="AdminLogoutActionGroup" stepKey="logoutFromAdmin"/> </after> <amOnPage url="$$product.name$$.html" stepKey="navigateToProductPage"/> @@ -54,7 +54,7 @@ <fillField selector="{{StorefrontCustomerCreateFormSection.confirmPasswordField}}" userInput="{{CustomerEntityOne.password}}" stepKey="TypeConfirmationPassword"/> <click selector="{{StorefrontCustomerCreateFormSection.createAccountButton}}" stepKey="clickOnCreateAccount"/> <see userInput="Thank you for registering" stepKey="verifyAccountCreated"/> - <actionGroup ref="LoginAsAdmin" stepKey="loginToAdmin"/> + <actionGroup ref="AdminLoginActionGroup" stepKey="loginToAdmin"/> <amOnPage url="{{AdminOrderPage.url({$grabOrderNumber})}}" stepKey="navigateToOrderPage"/> <waitForPageLoad stepKey="waitForCreatedOrderPage"/> <see stepKey="seeCustomerName" userInput="{{CustomerEntityOne.firstname}}" selector="{{AdminShipmentOrderInformationSection.customerName}}"/> diff --git a/app/code/Magento/Checkout/Test/Mftf/Test/StoreFrontFreeShippingRecalculationAfterCouponCodeAddedTest.xml b/app/code/Magento/Checkout/Test/Mftf/Test/StoreFrontFreeShippingRecalculationAfterCouponCodeAddedTest.xml index 8a2990c5869c9..57ed8e442af7c 100644 --- a/app/code/Magento/Checkout/Test/Mftf/Test/StoreFrontFreeShippingRecalculationAfterCouponCodeAddedTest.xml +++ b/app/code/Magento/Checkout/Test/Mftf/Test/StoreFrontFreeShippingRecalculationAfterCouponCodeAddedTest.xml @@ -13,14 +13,14 @@ <stories value="Checkout Free Shipping Recalculation after Coupon Code Added"/> <description value="User should be able to do checkout free shipping recalculation after adding coupon code"/> <features value="Checkout"/> - <severity value="MAJOR"/> + <severity value="BLOCKER"/> <testCaseId value="MAGETWO-96537"/> <useCaseId value="MAGETWO-96431"/> <group value="Checkout"/> </annotations> <before> - <actionGroup ref="LoginAsAdmin" stepKey="loginAsAdmin"/> + <actionGroup ref="AdminLoginActionGroup" stepKey="loginAsAdmin"/> <createData entity="Simple_US_Customer" stepKey="createSimpleUsCustomer"> <field key="group_id">1</field> </createData> @@ -54,7 +54,7 @@ <actionGroup ref="DeleteCartPriceRuleByName" stepKey="deleteCartPriceRule"> <argument name="ruleName" value="{{CatPriceRule.name}}"/> </actionGroup> - <actionGroup ref="logout" stepKey="logout"/> + <actionGroup ref="AdminLogoutActionGroup" stepKey="logout"/> </after> <actionGroup ref="ApplyCartRuleOnStorefrontActionGroup" stepKey="applyCartRule"> diff --git a/app/code/Magento/Checkout/Test/Mftf/Test/StoreFrontUpdateShoppingCartWhileUpdateMinicartTest.xml b/app/code/Magento/Checkout/Test/Mftf/Test/StoreFrontUpdateShoppingCartWhileUpdateMinicartTest.xml index f17716af8fd56..1f9948f80f391 100644 --- a/app/code/Magento/Checkout/Test/Mftf/Test/StoreFrontUpdateShoppingCartWhileUpdateMinicartTest.xml +++ b/app/code/Magento/Checkout/Test/Mftf/Test/StoreFrontUpdateShoppingCartWhileUpdateMinicartTest.xml @@ -39,10 +39,13 @@ <!--Go to Shopping cart and check Qty--> <actionGroup ref="ClickViewAndEditCartFromMiniCartActionGroup" stepKey="goToShoppingCart"/> <grabValueFrom selector="{{CheckoutCartProductSection.ProductQuantityByName($$createProduct.name$$)}}" stepKey="grabQtyShoppingCart"/> - <assertEquals expected="1" actual="$grabQtyShoppingCart" stepKey="assertQtyShoppingCart"/> + <assertEquals stepKey="assertQtyShoppingCart"> + <actualResult type="const">$grabQtyShoppingCart</actualResult> + <expectedResult type="const">1</expectedResult> + </assertEquals> <!--Open minicart and change Qty--> - <click selector="{{StorefrontMinicartSection.showCart}}" stepKey="openMiniCart"/> + <actionGroup ref="StorefrontClickOnMiniCartActionGroup" stepKey="openMiniCart"/> <waitForElementVisible selector="{{StorefrontMinicartSection.quantity}}" stepKey="waitForElementQty"/> <pressKey selector="{{StorefrontMinicartSection.itemQuantity($$createProduct.name$$)}}" parameterArray="[\Facebook\WebDriver\WebDriverKeys::BACKSPACE]" stepKey="deleteFiled"/> <fillField selector="{{StorefrontMinicartSection.itemQuantity($$createProduct.name$$)}}" userInput="5" stepKey="changeQty"/> @@ -51,6 +54,9 @@ <!--Check Qty in shopping cart after updating--> <grabValueFrom selector="{{CheckoutCartProductSection.ProductQuantityByName($$createProduct.name$$)}}" stepKey="grabQtyShoppingCart1"/> - <assertEquals expected="5" actual="$grabQtyShoppingCart1" stepKey="assertQtyShoppingCart1"/> + <assertEquals stepKey="assertQtyShoppingCart1"> + <actualResult type="const">$grabQtyShoppingCart1</actualResult> + <expectedResult type="const">5</expectedResult> + </assertEquals> </test> </tests> diff --git a/app/code/Magento/Checkout/Test/Mftf/Test/StorefrontAddBundleDynamicProductToShoppingCartTest.xml b/app/code/Magento/Checkout/Test/Mftf/Test/StorefrontAddBundleDynamicProductToShoppingCartTest.xml index 5e5278a256194..e82f3c0588835 100644 --- a/app/code/Magento/Checkout/Test/Mftf/Test/StorefrontAddBundleDynamicProductToShoppingCartTest.xml +++ b/app/code/Magento/Checkout/Test/Mftf/Test/StorefrontAddBundleDynamicProductToShoppingCartTest.xml @@ -111,8 +111,7 @@ </actionGroup> <!-- Assert Product in Mini Cart --> - <click selector="{{StorefrontMinicartSection.showCart}}" stepKey="clickOnMiniCart"/> - <waitForPageLoad stepKey="waitForPageToLoad1"/> + <actionGroup ref="StorefrontClickOnMiniCartActionGroup" stepKey="clickOnMiniCart"/> <actionGroup ref="AssertStorefrontMiniCartItemsActionGroup" stepKey="assertSimpleProduct3MiniCart"> <argument name="productName" value="$$createBundleProduct.name$$"/> <argument name="productPrice" value="$50.00"/> diff --git a/app/code/Magento/Checkout/Test/Mftf/Test/StorefrontAddConfigurableProductToShoppingCartTest.xml b/app/code/Magento/Checkout/Test/Mftf/Test/StorefrontAddConfigurableProductToShoppingCartTest.xml index e716ba294f578..21e785de6cab3 100644 --- a/app/code/Magento/Checkout/Test/Mftf/Test/StorefrontAddConfigurableProductToShoppingCartTest.xml +++ b/app/code/Magento/Checkout/Test/Mftf/Test/StorefrontAddConfigurableProductToShoppingCartTest.xml @@ -120,6 +120,9 @@ <deleteData createDataKey="createConfigProduct" stepKey="deleteProduct"/> <deleteData createDataKey="createCategory" stepKey="deleteCategory"/> <deleteData createDataKey="createConfigProductAttribute" stepKey="deleteProductAttribute"/> + + <!-- Reindex invalidated indices after product attribute has been created/deleted --> + <magentoCron groups="index" stepKey="reindexInvalidatedIndices"/> </after> <!-- Add Configurable Product to the cart --> @@ -154,8 +157,7 @@ </actionGroup> <!-- Assert product details in Mini Cart --> - <click selector="{{StorefrontMinicartSection.showCart}}" stepKey="clickOnMiniCart"/> - <waitForPageLoad stepKey="waitForPageToLoad"/> + <actionGroup ref="StorefrontClickOnMiniCartActionGroup" stepKey="clickOnMiniCart"/> <actionGroup ref="AssertStorefrontMiniCartItemsActionGroup" stepKey="assertMiniCart"> <argument name="productName" value="$$createConfigProduct.name$$"/> <argument name="productPrice" value="$$createConfigChildProduct2.price$$"/> diff --git a/app/code/Magento/Checkout/Test/Mftf/Test/StorefrontAddDownloadableProductToShoppingCartTest.xml b/app/code/Magento/Checkout/Test/Mftf/Test/StorefrontAddDownloadableProductToShoppingCartTest.xml index 3c1421f2616aa..bbc0a29000a77 100644 --- a/app/code/Magento/Checkout/Test/Mftf/Test/StorefrontAddDownloadableProductToShoppingCartTest.xml +++ b/app/code/Magento/Checkout/Test/Mftf/Test/StorefrontAddDownloadableProductToShoppingCartTest.xml @@ -34,7 +34,7 @@ <after> <magentoCLI stepKey="removeDownloadableDomain" command="downloadable:domains:remove example.com static.magento.com"/> <deleteData createDataKey="createDownloadableProduct" stepKey="deleteProduct"/> - <actionGroup ref="logout" stepKey="logout"/> + <actionGroup ref="AdminLogoutActionGroup" stepKey="logout"/> </after> <!-- Open Downloadable Product page --> @@ -77,8 +77,7 @@ </actionGroup> <!-- Assert product details in Mini Cart --> - <click selector="{{StorefrontMinicartSection.showCart}}" stepKey="clickOnMiniCart"/> - <waitForPageLoad stepKey="waitForPageToLoad1"/> + <actionGroup ref="StorefrontClickOnMiniCartActionGroup" stepKey="clickOnMiniCart"/> <actionGroup ref="AssertStorefrontMiniCartItemsActionGroup" stepKey="assertMiniCart"> <argument name="productName" value="$$createDownloadableProduct.name$$"/> <argument name="productPrice" value="$123.00"/> diff --git a/app/code/Magento/Checkout/Test/Mftf/Test/StorefrontAddGroupedProductToShoppingCartTest.xml b/app/code/Magento/Checkout/Test/Mftf/Test/StorefrontAddGroupedProductToShoppingCartTest.xml index 0fc4cee5e0582..3e2f32a4ab055 100644 --- a/app/code/Magento/Checkout/Test/Mftf/Test/StorefrontAddGroupedProductToShoppingCartTest.xml +++ b/app/code/Magento/Checkout/Test/Mftf/Test/StorefrontAddGroupedProductToShoppingCartTest.xml @@ -101,8 +101,7 @@ </actionGroup> <!-- Assert product1 details in Mini Cart --> - <click selector="{{StorefrontMinicartSection.showCart}}" stepKey="clickOnMiniCart"/> - <waitForPageLoad stepKey="waitForPageToLoad1"/> + <actionGroup ref="StorefrontClickOnMiniCartActionGroup" stepKey="clickOnMiniCart"/> <actionGroup ref="AssertStorefrontMiniCartItemsActionGroup" stepKey="assertSimpleProduct3MiniCart"> <argument name="productName" value="$$simple3.name$$"/> <argument name="productPrice" value="$300.00"/> diff --git a/app/code/Magento/Checkout/Test/Mftf/Test/StorefrontAddProductWithAllTypesOfCustomOptionToTheShoppingCartTest.xml b/app/code/Magento/Checkout/Test/Mftf/Test/StorefrontAddProductWithAllTypesOfCustomOptionToTheShoppingCartTest.xml index 319183d4641e6..e8a72b6e88109 100644 --- a/app/code/Magento/Checkout/Test/Mftf/Test/StorefrontAddProductWithAllTypesOfCustomOptionToTheShoppingCartTest.xml +++ b/app/code/Magento/Checkout/Test/Mftf/Test/StorefrontAddProductWithAllTypesOfCustomOptionToTheShoppingCartTest.xml @@ -13,7 +13,7 @@ <title value="Create a simple products with all types of oprtions and add it to the cart"/> <description value="Create a simple products with all types of oprtions and add it to the cart"/> <testCaseId value="MC-14726"/> - <severity value="CRITICAL"/> + <severity value="BLOCKER"/> <group value="mtf_migrated"/> </annotations> @@ -48,7 +48,10 @@ <actionGroup ref="StorefrontAttachOptionFileActionGroup" stepKey="selectAndAttachFile"/> <!--Select Option From DropDown option --> - <actionGroup ref="StorefrontSelectOptionDropDownActionGroup" stepKey="selectDropDownOption"/> + <actionGroup ref="StorefrontProductPageSelectDropDownOptionValueActionGroup" stepKey="selectFirstOption"> + <argument name="attributeLabel" value="{{ProductOptionDropDown.title}}"/> + <argument name="optionLabel" value="{{ProductOptionValueDropdown2.title}}"/> + </actionGroup> <scrollTo selector="{{StorefrontProductInfoMainSection.customOptionLabel(ProductOptionMultiSelect.title)}}" stepKey="scrollToMultiSelect"/> <!-- Select CheckBox From CheckBox option --> diff --git a/app/code/Magento/Checkout/Test/Mftf/Test/StorefrontAddTwoBundleMultiSelectOptionsToTheShoppingCartTest.xml b/app/code/Magento/Checkout/Test/Mftf/Test/StorefrontAddTwoBundleMultiSelectOptionsToTheShoppingCartTest.xml index 14788250a9bca..265f9a7cbbc98 100644 --- a/app/code/Magento/Checkout/Test/Mftf/Test/StorefrontAddTwoBundleMultiSelectOptionsToTheShoppingCartTest.xml +++ b/app/code/Magento/Checkout/Test/Mftf/Test/StorefrontAddTwoBundleMultiSelectOptionsToTheShoppingCartTest.xml @@ -112,8 +112,7 @@ </actionGroup> <!-- Assert Product in Mini Cart --> - <click selector="{{StorefrontMinicartSection.showCart}}" stepKey="clickOnMiniCart"/> - <waitForPageLoad stepKey="waitForPageToLoad1"/> + <actionGroup ref="StorefrontClickOnMiniCartActionGroup" stepKey="clickOnMiniCart"/> <actionGroup ref="AssertStorefrontMiniCartItemsActionGroup" stepKey="assertSimpleProduct3MiniCart"> <argument name="productName" value="$$createBundleProduct.name$$"/> <argument name="productPrice" value="$60.00"/> diff --git a/app/code/Magento/Checkout/Test/Mftf/Test/StorefrontApplyPromoCodeDuringCheckoutTest.xml b/app/code/Magento/Checkout/Test/Mftf/Test/StorefrontApplyPromoCodeDuringCheckoutTest.xml index 7edc37d510266..e4eb53a1f1925 100644 --- a/app/code/Magento/Checkout/Test/Mftf/Test/StorefrontApplyPromoCodeDuringCheckoutTest.xml +++ b/app/code/Magento/Checkout/Test/Mftf/Test/StorefrontApplyPromoCodeDuringCheckoutTest.xml @@ -38,7 +38,7 @@ <deleteData createDataKey="createCartPriceRule" stepKey="deleteCartPriceRule"/> <!-- Admin log out --> - <actionGroup ref="logout" stepKey="logout"/> + <actionGroup ref="AdminLogoutActionGroup" stepKey="logout"/> </after> <!-- Go to Storefront as Guest and add simple product to cart --> @@ -54,7 +54,7 @@ <argument name="shippingMethod" value="Flat Rate"/> </actionGroup> - <!-- Click Apply Discount Code: section is expanded. Input promo code, apply and see success message --> + <!-- Click Apply Discount Code: section is expanded. Input promo code, apply and see success message --> <actionGroup ref="StorefrontApplyDiscountCodeActionGroup" stepKey="applyCoupon"> <argument name="discountCode" value="$$createCouponForCartPriceRule.code$$"/> </actionGroup> @@ -77,7 +77,7 @@ <grabTextFrom selector="{{CheckoutSuccessMainSection.orderNumber}}" stepKey="grabOrderNumber"/> <!-- Login as admin --> - <actionGroup ref="LoginAsAdmin" stepKey="loginAsAdmin"/> + <actionGroup ref="AdminLoginActionGroup" stepKey="loginAsAdmin"/> <!-- Verify total on order page --> <actionGroup ref="FilterOrderGridByIdActionGroup" stepKey="filterOrderById"> diff --git a/app/code/Magento/Checkout/Test/Mftf/Test/StorefrontCheckCartAndCheckoutItemsCountTest.xml b/app/code/Magento/Checkout/Test/Mftf/Test/StorefrontCheckCartAndCheckoutItemsCountTest.xml deleted file mode 100644 index c3f173961f0c5..0000000000000 --- a/app/code/Magento/Checkout/Test/Mftf/Test/StorefrontCheckCartAndCheckoutItemsCountTest.xml +++ /dev/null @@ -1,85 +0,0 @@ -<?xml version="1.0" encoding="UTF-8"?> -<!-- - /** - * Copyright © Magento, Inc. All rights reserved. - * See COPYING.txt for license details. - */ ---> -<tests xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" - xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/testSchema.xsd"> - <test name="StorefrontCartItemsCountDisplayItemsQuantities"> - <annotations> - <stories value="Checkout order summary has wrong item count"/> - <title value="Checkout order summary has wrong item count - display items quantities"/> - <description value="Items count in shopping cart and on checkout page should be consistent with settings 'checkout/cart_link/use_qty'"/> - <testCaseId value="MC-18281"/> - <severity value="CRITICAL"/> - <group value="checkout"/> - </annotations> - - <before> - <!--Set Display Cart Summary to display items quantities--> - <magentoCLI command="config:set {{DisplayItemsQuantities.path}} {{DisplayItemsQuantities.value}}" stepKey="setDisplayCartSummary"/> - <!--Create simple product--> - <createData entity="SimpleProduct2" stepKey="simpleProduct1"/> - <!--Create simple product--> - <createData entity="SimpleProduct2" stepKey="simpleProduct2"/> - </before> - <after> - <deleteData createDataKey="simpleProduct1" stepKey="deleteProduct1"/> - <deleteData createDataKey="simpleProduct2" stepKey="deleteProduct2"/> - <magentoCLI command="config:set {{DisplayItemsQuantities.path}} {{DisplayItemsQuantities.value}}" stepKey="resetDisplayCartSummary"/> - </after> - - <!-- Add simpleProduct1 to cart --> - <amOnPage url="{{StorefrontProductPage.url($$simpleProduct1.custom_attributes[url_key]$)}}" stepKey="amOnProduct1Page"/> - <actionGroup ref="AddProductWithQtyToCartFromStorefrontProductPageActionGroup" stepKey="addProduct1ToCart"> - <argument name="productName" value="$$simpleProduct1.name$$"/> - <argument name="productQty" value="2"/> - </actionGroup> - <!-- Add simpleProduct2 to cart --> - <amOnPage url="{{StorefrontProductPage.url($$simpleProduct2.custom_attributes[url_key]$)}}" stepKey="amOnProduct2Page"/> - <actionGroup ref="AddProductWithQtyToCartFromStorefrontProductPageActionGroup" stepKey="addProduct2ToCart"> - <argument name="productName" value="$$simpleProduct2.name$$"/> - <argument name="productQty" value="1"/> - </actionGroup> - - <!-- Open Mini Cart --> - <actionGroup ref="StorefrontOpenMiniCartActionGroup" stepKey="openMiniCart"/> - - <!-- Assert Products Count in Mini Cart --> - <actionGroup ref="StorefrontAssertMiniCartItemCountActionGroup" stepKey="assertProductCountAndTextInMiniCart"> - <argument name="productCount" value="3"/> - <argument name="productCountText" value="3 Items in Cart"/> - </actionGroup> - <!-- Assert Products Count on checkout page --> - <actionGroup ref="StorefrontCheckoutAndAssertOrderSummaryDisplayActionGroup" stepKey="assertProductCountOnCheckoutPage"> - <argument name="itemsText" value="3 Items in Cart"/> - </actionGroup> - </test> - <test name="StorefrontCartItemsCountDisplayUniqueItems" extends="StorefrontCartItemsCountDisplayItemsQuantities"> - <annotations> - <stories value="Checkout order summary has wrong item count"/> - <title value="Checkout order summary has wrong item count - display unique items"/> - <description value="Items count in shopping cart and on checkout page should be consistent with settings 'checkout/cart_link/use_qty'"/> - <testCaseId value="MC-18281"/> - <severity value="CRITICAL"/> - <group value="checkout"/> - </annotations> - - <!-- Assert Products Count in Mini Cart --> - <actionGroup ref="StorefrontAssertMiniCartItemCountActionGroup" stepKey="assertProductCountAndTextInMiniCart"> - <argument name="productCount" value="2"/> - <argument name="productCountText" value="2 Items in Cart"/> - </actionGroup> - <!-- Assert Products Count on checkout page --> - <actionGroup ref="StorefrontCheckoutAndAssertOrderSummaryDisplayActionGroup" stepKey="assertProductCountOnCheckoutPage"> - <argument name="itemsText" value="2 Items in Cart"/> - </actionGroup> - - <before> - <!--Set Display Cart Summary to display items quantities--> - <magentoCLI command="config:set {{DisplayUniqueItems.path}} {{DisplayUniqueItems.value}}" stepKey="setDisplayCartSummary"/> - </before> - </test> -</tests> diff --git a/app/code/Magento/Checkout/Test/Mftf/Test/StorefrontCheckCartAndCheckoutItemsCountTest/StorefrontCartItemsCountDisplayItemsQuantitiesTest.xml b/app/code/Magento/Checkout/Test/Mftf/Test/StorefrontCheckCartAndCheckoutItemsCountTest/StorefrontCartItemsCountDisplayItemsQuantitiesTest.xml new file mode 100644 index 0000000000000..7ce7e12a2b704 --- /dev/null +++ b/app/code/Magento/Checkout/Test/Mftf/Test/StorefrontCheckCartAndCheckoutItemsCountTest/StorefrontCartItemsCountDisplayItemsQuantitiesTest.xml @@ -0,0 +1,60 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + /** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +--> +<tests xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/testSchema.xsd"> + <test name="StorefrontCartItemsCountDisplayItemsQuantitiesTest"> + <annotations> + <stories value="Checkout order summary has wrong item count"/> + <title value="Checkout order summary has wrong item count - display items quantities"/> + <description value="Items count in shopping cart and on checkout page should be consistent with settings 'checkout/cart_link/use_qty'"/> + <testCaseId value="MC-18281"/> + <severity value="CRITICAL"/> + <group value="checkout"/> + </annotations> + + <before> + <!--Set Display Cart Summary to display items quantities--> + <magentoCLI command="config:set {{DisplayItemsQuantities.path}} {{DisplayItemsQuantities.value}}" stepKey="setDisplayCartSummary"/> + <!--Create simple product--> + <createData entity="SimpleProduct2" stepKey="simpleProduct1"/> + <!--Create simple product--> + <createData entity="SimpleProduct2" stepKey="simpleProduct2"/> + </before> + <after> + <deleteData createDataKey="simpleProduct1" stepKey="deleteProduct1"/> + <deleteData createDataKey="simpleProduct2" stepKey="deleteProduct2"/> + <magentoCLI command="config:set {{DisplayItemsQuantities.path}} {{DisplayItemsQuantities.value}}" stepKey="resetDisplayCartSummary"/> + </after> + + <!-- Add simpleProduct1 to cart --> + <amOnPage url="{{StorefrontProductPage.url($$simpleProduct1.custom_attributes[url_key]$)}}" stepKey="amOnProduct1Page"/> + <actionGroup ref="AddProductWithQtyToCartFromStorefrontProductPageActionGroup" stepKey="addProduct1ToCart"> + <argument name="productName" value="$$simpleProduct1.name$$"/> + <argument name="productQty" value="2"/> + </actionGroup> + <!-- Add simpleProduct2 to cart --> + <amOnPage url="{{StorefrontProductPage.url($$simpleProduct2.custom_attributes[url_key]$)}}" stepKey="amOnProduct2Page"/> + <actionGroup ref="AddProductWithQtyToCartFromStorefrontProductPageActionGroup" stepKey="addProduct2ToCart"> + <argument name="productName" value="$$simpleProduct2.name$$"/> + <argument name="productQty" value="1"/> + </actionGroup> + + <!-- Open Mini Cart --> + <actionGroup ref="StorefrontOpenMiniCartActionGroup" stepKey="openMiniCart"/> + + <!-- Assert Products Count in Mini Cart --> + <actionGroup ref="StorefrontAssertMiniCartItemCountActionGroup" stepKey="assertProductCountAndTextInMiniCart"> + <argument name="productCount" value="3"/> + <argument name="productCountText" value="3 Items in Cart"/> + </actionGroup> + <!-- Assert Products Count on checkout page --> + <actionGroup ref="StorefrontCheckoutAndAssertOrderSummaryDisplayActionGroup" stepKey="assertProductCountOnCheckoutPage"> + <argument name="itemsText" value="3 Items in Cart"/> + </actionGroup> + </test> +</tests> diff --git a/app/code/Magento/Checkout/Test/Mftf/Test/StorefrontCheckCartAndCheckoutItemsCountTest/StorefrontCartItemsCountDisplayUniqueItemsTest.xml b/app/code/Magento/Checkout/Test/Mftf/Test/StorefrontCheckCartAndCheckoutItemsCountTest/StorefrontCartItemsCountDisplayUniqueItemsTest.xml new file mode 100644 index 0000000000000..9fa7f64d38aa1 --- /dev/null +++ b/app/code/Magento/Checkout/Test/Mftf/Test/StorefrontCheckCartAndCheckoutItemsCountTest/StorefrontCartItemsCountDisplayUniqueItemsTest.xml @@ -0,0 +1,35 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + /** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +--> +<tests xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/testSchema.xsd"> + <test name="StorefrontCartItemsCountDisplayUniqueItemsTest" extends="StorefrontCartItemsCountDisplayItemsQuantitiesTest"> + <annotations> + <stories value="Checkout order summary has wrong item count"/> + <title value="Checkout order summary has wrong item count - display unique items"/> + <description value="Items count in shopping cart and on checkout page should be consistent with settings 'checkout/cart_link/use_qty'"/> + <testCaseId value="MC-18281"/> + <severity value="CRITICAL"/> + <group value="checkout"/> + </annotations> + + <!-- Assert Products Count in Mini Cart --> + <actionGroup ref="StorefrontAssertMiniCartItemCountActionGroup" stepKey="assertProductCountAndTextInMiniCart"> + <argument name="productCount" value="2"/> + <argument name="productCountText" value="2 Items in Cart"/> + </actionGroup> + <!-- Assert Products Count on checkout page --> + <actionGroup ref="StorefrontCheckoutAndAssertOrderSummaryDisplayActionGroup" stepKey="assertProductCountOnCheckoutPage"> + <argument name="itemsText" value="2 Items in Cart"/> + </actionGroup> + + <before> + <!--Set Display Cart Summary to display items quantities--> + <magentoCLI command="config:set {{DisplayUniqueItems.path}} {{DisplayUniqueItems.value}}" stepKey="setDisplayCartSummary"/> + </before> + </test> +</tests> diff --git a/app/code/Magento/Checkout/Test/Mftf/Test/StorefrontCheckoutDisabledBundleProductTest.xml b/app/code/Magento/Checkout/Test/Mftf/Test/StorefrontCheckoutDisabledBundleProductTest.xml index 7405a3100728f..444ebf653b94f 100644 --- a/app/code/Magento/Checkout/Test/Mftf/Test/StorefrontCheckoutDisabledBundleProductTest.xml +++ b/app/code/Magento/Checkout/Test/Mftf/Test/StorefrontCheckoutDisabledBundleProductTest.xml @@ -61,7 +61,7 @@ </actionGroup> <!-- Login to admin panel --> <openNewTab stepKey="openNewTab"/> - <actionGroup ref="LoginAsAdmin" stepKey="loginAsAdmin"/> + <actionGroup ref="AdminLoginActionGroup" stepKey="loginAsAdmin"/> <!-- Find the first simple product that we just created using the product grid and go to its page--> <amOnPage url="{{AdminProductIndexPage.url}}" stepKey="visitAdminProductPage"/> <!-- Disabled bundle product from grid --> diff --git a/app/code/Magento/Checkout/Test/Mftf/Test/StorefrontCheckoutWithDifferentShippingAndBillingAddressAndProductWithTierPricesTest.xml b/app/code/Magento/Checkout/Test/Mftf/Test/StorefrontCheckoutWithDifferentShippingAndBillingAddressAndProductWithTierPricesTest.xml index 07d29aa0aac4a..ce7c5c3b29353 100644 --- a/app/code/Magento/Checkout/Test/Mftf/Test/StorefrontCheckoutWithDifferentShippingAndBillingAddressAndProductWithTierPricesTest.xml +++ b/app/code/Magento/Checkout/Test/Mftf/Test/StorefrontCheckoutWithDifferentShippingAndBillingAddressAndProductWithTierPricesTest.xml @@ -19,7 +19,7 @@ <before> <magentoCLI command="config:set {{EnablePaymentBankTransferConfigData.path}} {{EnablePaymentBankTransferConfigData.value}}" stepKey="enableBankTransferPayment"/> - <actionGroup ref="LoginAsAdmin" stepKey="loginToAdminPanel"/> + <actionGroup ref="AdminLoginActionGroup" stepKey="loginToAdminPanel"/> <createData entity="SimpleProduct2" stepKey="simpleProduct"> <field key="price">50.00</field> </createData> @@ -45,7 +45,7 @@ <actionGroup ref="AdminDeleteCustomerActionGroup" stepKey="deleteCustomer"> <argument name="customerEmail" value="UKCustomer.email"/> </actionGroup> - <actionGroup ref="logout" stepKey="logout"/> + <actionGroup ref="AdminLogoutActionGroup" stepKey="logout"/> </after> <!--Open Product page in StoreFront and assert product and price range --> @@ -77,6 +77,7 @@ <checkOption selector="{{CheckoutPaymentSection.bankTransfer}}" stepKey="selectBankTransfer"/> <waitForElementVisible selector="{{CheckoutPaymentSection.billingAddressNotSameBankTransferCheckbox}}" stepKey="waitForElementToBeVisible"/> <uncheckOption selector="{{CheckoutPaymentSection.billingAddressNotSameBankTransferCheckbox}}" stepKey="uncheckSameBillingAndShippingAddress"/> + <waitForElementVisible selector="{{CheckoutShippingSection.editActiveAddressButton}}" stepKey="waitForEditButtonToBeVisible"/> <conditionalClick selector="{{CheckoutShippingSection.editActiveAddressButton}}" dependentSelector="{{CheckoutShippingSection.editActiveAddressButton}}" visible="true" stepKey="clickEditButton"/> <waitForLoadingMaskToDisappear stepKey="waitForLoadingMask"/> diff --git a/app/code/Magento/Checkout/Test/Mftf/Test/StorefrontCheckoutWithDifferentShippingAndBillingAddressAndRegisterCustomerAfterCheckoutTest.xml b/app/code/Magento/Checkout/Test/Mftf/Test/StorefrontCheckoutWithDifferentShippingAndBillingAddressAndRegisterCustomerAfterCheckoutTest.xml index 2f19dcd2dc32f..16c7a0609a6ff 100644 --- a/app/code/Magento/Checkout/Test/Mftf/Test/StorefrontCheckoutWithDifferentShippingAndBillingAddressAndRegisterCustomerAfterCheckoutTest.xml +++ b/app/code/Magento/Checkout/Test/Mftf/Test/StorefrontCheckoutWithDifferentShippingAndBillingAddressAndRegisterCustomerAfterCheckoutTest.xml @@ -18,7 +18,7 @@ </annotations> <before> - <actionGroup ref="LoginAsAdmin" stepKey="loginToAdminPanel"/> + <actionGroup ref="AdminLoginActionGroup" stepKey="loginToAdminPanel"/> <createData entity="SimpleProduct2" stepKey="simpleProduct"> <field key="price">50.00</field> </createData> @@ -31,7 +31,7 @@ <actionGroup ref="AdminDeleteCustomerActionGroup" stepKey="deleteCustomer"> <argument name="customerEmail" value="UKCustomer.email"/> </actionGroup> - <actionGroup ref="logout" stepKey="logout"/> + <actionGroup ref="AdminLogoutActionGroup" stepKey="logout"/> </after> <!--Open Product page in StoreFront and assert product and price range --> diff --git a/app/code/Magento/Checkout/Test/Mftf/Test/StorefrontCheckoutWithSpecialPriceProductsTest.xml b/app/code/Magento/Checkout/Test/Mftf/Test/StorefrontCheckoutWithSpecialPriceProductsTest.xml index a9db81620d329..787d8f58891d0 100644 --- a/app/code/Magento/Checkout/Test/Mftf/Test/StorefrontCheckoutWithSpecialPriceProductsTest.xml +++ b/app/code/Magento/Checkout/Test/Mftf/Test/StorefrontCheckoutWithSpecialPriceProductsTest.xml @@ -18,7 +18,7 @@ </annotations> <before> - <actionGroup ref="LoginAsAdmin" stepKey="loginToAdminPanel"/> + <actionGroup ref="AdminLoginActionGroup" stepKey="loginToAdminPanel"/> <createData entity="Simple_US_Customer" stepKey="createCustomer"/> <createData entity="defaultSimpleProduct" stepKey="simpleProduct"> <field key="price">10.00</field> @@ -101,7 +101,10 @@ <deleteData createDataKey="createConfigProduct" stepKey="deleteConfigProduct"/> <deleteData createDataKey="createConfigProductAttribute" stepKey="deleteProductAttribute"/> <deleteData createDataKey="createCustomer" stepKey="deleteCustomer"/> - <actionGroup ref="logout" stepKey="logout"/> + <actionGroup ref="AdminLogoutActionGroup" stepKey="logout"/> + + <!-- Reindex invalidated indices after product attribute has been created/deleted --> + <magentoCron groups="index" stepKey="reindexInvalidatedIndices"/> </after> <!--Open Product page in StoreFront and assert product and price range --> diff --git a/app/code/Magento/Checkout/Test/Mftf/Test/StorefrontCustomerCheckoutDisabledProductAndCouponTest.xml b/app/code/Magento/Checkout/Test/Mftf/Test/StorefrontCustomerCheckoutDisabledProductAndCouponTest.xml index f8e1f32e93f52..b962d80a4d88b 100644 --- a/app/code/Magento/Checkout/Test/Mftf/Test/StorefrontCustomerCheckoutDisabledProductAndCouponTest.xml +++ b/app/code/Magento/Checkout/Test/Mftf/Test/StorefrontCustomerCheckoutDisabledProductAndCouponTest.xml @@ -67,7 +67,7 @@ <!-- Login to admin panel --> <openNewTab stepKey="openNewTab"/> - <actionGroup ref="LoginAsAdmin" stepKey="loginAsAdmin"/> + <actionGroup ref="AdminLoginActionGroup" stepKey="loginAsAdmin"/> <!-- Find the first simple product that we just created using the product grid and go to its page--> <amOnPage url="{{AdminProductIndexPage.url}}" stepKey="visitAdminProductPage"/> diff --git a/app/code/Magento/Checkout/Test/Mftf/Test/StorefrontCustomerCheckoutOnLoginWhenGuestCheckoutIsDisabledTest.xml b/app/code/Magento/Checkout/Test/Mftf/Test/StorefrontCustomerCheckoutOnLoginWhenGuestCheckoutIsDisabledTest.xml index 1c3d3d4914fb4..5fbfdb5a07678 100644 --- a/app/code/Magento/Checkout/Test/Mftf/Test/StorefrontCustomerCheckoutOnLoginWhenGuestCheckoutIsDisabledTest.xml +++ b/app/code/Magento/Checkout/Test/Mftf/Test/StorefrontCustomerCheckoutOnLoginWhenGuestCheckoutIsDisabledTest.xml @@ -18,7 +18,7 @@ </annotations> <before> - <actionGroup ref="LoginAsAdmin" stepKey="loginToAdminPanel"/> + <actionGroup ref="AdminLoginActionGroup" stepKey="loginToAdminPanel"/> <magentoCLI command="config:set {{DisableGuestCheckoutConfigData.path}} {{DisableGuestCheckoutConfigData.value}}" stepKey="disableGuestCheckout"/> <magentoCLI command="config:set {{DisableCustomerRedirectToDashboardConfigData.path}} {{DisableCustomerRedirectToDashboardConfigData.value}}" stepKey="disableCustomerRedirect"/> <createData entity="SimpleProduct2" stepKey="simpleProduct"> @@ -35,7 +35,7 @@ <actionGroup ref="StorefrontSignOutActionGroup" stepKey="customerLogout"/> <deleteData createDataKey="simpleProduct" stepKey="deleteProduct"/> <deleteData createDataKey="createCustomer" stepKey="deleteCustomer"/> - <actionGroup ref="logout" stepKey="logout"/> + <actionGroup ref="AdminLogoutActionGroup" stepKey="logout"/> </after> <!--Open Product page in StoreFront and assert product and price range --> diff --git a/app/code/Magento/Checkout/Test/Mftf/Test/StorefrontCustomerCheckoutTest.xml b/app/code/Magento/Checkout/Test/Mftf/Test/StorefrontCustomerCheckoutTest.xml deleted file mode 100644 index 917a4ac58464f..0000000000000 --- a/app/code/Magento/Checkout/Test/Mftf/Test/StorefrontCustomerCheckoutTest.xml +++ /dev/null @@ -1,281 +0,0 @@ -<?xml version="1.0" encoding="UTF-8"?> -<!-- - /** - * Copyright © Magento, Inc. All rights reserved. - * See COPYING.txt for license details. - */ ---> - -<tests xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" - xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/testSchema.xsd"> - <test name="StorefrontCustomerCheckoutTest"> - <annotations> - <features value="Checkout"/> - <stories value="Checkout via the Admin"/> - <title value="Customer Checkout via the Admin"/> - <description value="Should be able to place an order as a customer."/> - <severity value="CRITICAL"/> - <testCaseId value="MC-5922"/> - <group value="checkout"/> - </annotations> - <before> - <createData entity="SimpleSubCategory" stepKey="simplecategory"/> - <createData entity="SimpleProduct" stepKey="simpleproduct1"> - <requiredEntity createDataKey="simplecategory"/> - </createData> - <createData entity="Simple_US_Customer" stepKey="simpleuscustomer"/> - </before> - <after> - <!--Clear filters--> - <amOnPage url="{{AdminCustomerPage.url}}" stepKey="navigateToCustomers"/> - <conditionalClick selector="{{AdminDataGridHeaderSection.clearFilters}}" dependentSelector="{{AdminDataGridHeaderSection.clearFilters}}" visible="true" stepKey="clearExistingCustomerFilters"/> - - <actionGroup ref="logout" stepKey="logout"/> - <deleteData createDataKey="simpleproduct1" stepKey="deleteProduct1"/> - <deleteData createDataKey="simplecategory" stepKey="deleteCategory"/> - </after> - - <actionGroup ref="LoginToStorefrontActionGroup" stepKey="customerLogin"> - <argument name="Customer" value="$$simpleuscustomer$$" /> - </actionGroup> - - <amOnPage url="{{StorefrontCategoryPage.url($$simplecategory.name$$)}}" stepKey="onCategoryPage"/> - <waitForPageLoad stepKey="waitForCatalogPageLoad"/> - <moveMouseOver selector="{{StorefrontCategoryMainSection.ProductItemInfo}}" stepKey="hoverProduct"/> - <click selector="{{StorefrontCategoryMainSection.AddToCartBtn}}" stepKey="addToCart"/> - <waitForElementVisible selector="{{StorefrontCategoryMainSection.SuccessMsg}}" time="30" stepKey="waitForProductAdded"/> - <see selector="{{StorefrontCategoryMainSection.SuccessMsg}}" userInput="You added $$simpleproduct1.name$$ to your shopping cart." stepKey="seeAddedToCartMessage"/> - <see selector="{{StorefrontMinicartSection.quantity}}" userInput="1" stepKey="seeCartQuantity"/> - <actionGroup ref="GoToCheckoutFromMinicartActionGroup" stepKey="goToCheckoutFromMinicart" /> - - <click stepKey="s35" selector="{{CheckoutShippingMethodsSection.firstShippingMethod}}"/> - <waitForElement stepKey="s36" selector="{{CheckoutShippingMethodsSection.next}}" time="30"/> - <click stepKey="s37" selector="{{CheckoutShippingMethodsSection.next}}" /> - <!-- Checkout select Check/Money Order payment --> - <actionGroup ref="CheckoutSelectCheckMoneyOrderPaymentActionGroup" stepKey="selectCheckMoneyPayment"/> - <waitForPageLoad stepKey="s39"/> - <waitForElement stepKey="s41" selector="{{CheckoutPaymentSection.placeOrder}}" time="30" /> - <see stepKey="s47" selector="{{CheckoutPaymentSection.billingAddress}}" userInput="{{US_Address_TX.street[0]}}" /> - <click stepKey="s49" selector="{{CheckoutPaymentSection.placeOrder}}" /> - <waitForPageLoad stepKey="s51"/> - <grabTextFrom stepKey="s53" selector="{{CheckoutSuccessMainSection.orderNumber22}}"/> - <see stepKey="s55" selector="{{CheckoutSuccessMainSection.success}}" userInput="Your order number is:" /> - - <actionGroup ref="LoginAsAdmin" stepKey="loginAsAdmin1"/> - - <amOnPage stepKey="s67" url="{{AdminOrdersPage.url}}"/> - <waitForPageLoad stepKey="s75"/> - <actionGroup ref="AdminOrdersGridClearFiltersActionGroup" stepKey="clearFiltersIfPresent"/> - <fillField stepKey="s77" selector="{{AdminOrdersGridSection.search}}" userInput="{$s53}" /> - <waitForPageLoad stepKey="s78"/> - - <click stepKey="s81" selector="{{AdminOrdersGridSection.submitSearch22}}" /> - <waitForPageLoad stepKey="s831"/> - <click stepKey="s84" selector="{{AdminOrdersGridSection.firstRow}}" /> - <waitForPageLoad stepKey="waitForOrderToLoad"/> - <see stepKey="s85" selector="{{AdminOrderDetailsInformationSection.orderStatus}}" userInput="Pending" /> - <see stepKey="s87" selector="{{AdminOrderDetailsInformationSection.accountInformation}}" userInput="Customer" /> - <see stepKey="s89" selector="{{AdminOrderDetailsInformationSection.accountInformation}}" userInput="$$simpleuscustomer.email$$" /> - <see stepKey="s91" selector="{{AdminOrderDetailsInformationSection.billingAddress}}" userInput="{{US_Address_TX.street[0]}}" /> - <see stepKey="s93" selector="{{AdminOrderDetailsInformationSection.shippingAddress}}" userInput="{{US_Address_TX.street[0]}}" /> - <see stepKey="s95" selector="{{AdminOrderDetailsInformationSection.itemsOrdered}}" userInput="$$simpleproduct1.name$$" /> - - <amOnPage stepKey="s96" url="{{AdminCustomerPage.url}}"/> - <waitForPageLoad stepKey="s97"/> - <waitForElementVisible selector="{{AdminCustomerFiltersSection.filtersButton}}" time="30" stepKey="waitFiltersButton"/> - <click stepKey="s98" selector="{{AdminCustomerFiltersSection.filtersButton}}"/> - <fillField stepKey="s99" selector="{{AdminCustomerFiltersSection.emailInput}}" userInput="$$simpleuscustomer.email$$"/> - <click stepKey="s100" selector="{{AdminCustomerFiltersSection.apply}}"/> - <click stepKey="s101" selector="{{AdminCustomerGridSection.firstRowEditLink}}"/> - <click stepKey="s102" selector="{{AdminEditCustomerInformationSection.orders}}"/> - <see stepKey="s103" selector="{{AdminEditCustomerOrdersSection.orderGrid}}" userInput="$$simpleuscustomer.firstname$$ $$simpleuscustomer.lastname$$" /> - </test> - <test name="StorefrontCustomerCheckoutTestWithMultipleAddressesAndTaxRates"> - <annotations> - <features value="Checkout"/> - <stories value="Customer checkout"/> - <title value="Customer Checkout with multiple addresses and tax rates"/> - <description value="Should be able to place an order as a customer with multiple addresses and tax rates."/> - <testCaseId value="MAGETWO-93109"/> - <severity value="AVERAGE"/> - <group value="checkout"/> - </annotations> - <before> - <createData entity="SimpleSubCategory" stepKey="simplecategory"/> - <createData entity="SimpleProduct" stepKey="simpleproduct1"> - <requiredEntity createDataKey="simplecategory"/> - </createData> - <createData entity="Simple_US_Customer_Multiple_Addresses" stepKey="multiple_address_customer"/> - - <actionGroup ref="LoginAsAdmin" stepKey="loginAsAdmin"/> - - <!-- Go to tax rule page --> - <amOnPage url="{{AdminTaxRuleGridPage.url}}" stepKey="goToTaxRulePage"/> - <waitForPageLoad stepKey="waitForTaxRatePage"/> - <click stepKey="addNewTaxRate" selector="{{AdminGridMainControls.add}}"/> - <fillField stepKey="fillRuleName" selector="{{AdminTaxRulesSection.ruleName}}" userInput="SampleRule"/> - - <!-- Add NY and CA tax rules --> - <actionGroup ref="AddNewTaxRateNoZipActionGroup" stepKey="addNYTaxRate"> - <argument name="taxCode" value="SimpleTaxNY"/> - </actionGroup> - - <actionGroup ref="AddNewTaxRateNoZipActionGroup" stepKey="addCATaxRate"> - <argument name="taxCode" value="SimpleTaxCA"/> - </actionGroup> - - <click stepKey="clickSave" selector="{{AdminStoresMainActionsSection.saveButton}}"/> - - <!--TODO: REMOVE AFTER FIX MC-21717 --> - <magentoCLI command="indexer:reindex" stepKey="reindex"/> - <magentoCLI command="cache:flush full_page" stepKey="flushCache"/> - </before> - <after> - <!-- Go to the tax rule page and delete the row we created--> - <amOnPage url="{{AdminTaxRuleGridPage.url}}" stepKey="goToTaxRulesPage"/> - <waitForPageLoad stepKey="waitForRulesPage"/> - <actionGroup ref="deleteEntitySecondaryGrid" stepKey="deleteRule"> - <argument name="name" value="SampleRule"/> - <argument name="searchInput" value="{{AdminSecondaryGridSection.taxIdentifierSearch}}"/> - </actionGroup> - - <!-- Go to the tax rate page --> - <amOnPage url="{{AdminTaxRateGridPage.url}}" stepKey="goToTaxRatesPage"/> - <waitForPageLoad stepKey="waitForRatesPage"/> - <!-- Delete the two tax rates that were created --> - <actionGroup ref="deleteEntitySecondaryGrid" stepKey="deleteNYRate"> - <argument name="name" value="{{SimpleTaxNY.state}}-{{SimpleTaxNY.rate}}"/> - <argument name="searchInput" value="{{AdminSecondaryGridSection.taxIdentifierSearch}}"/> - </actionGroup> - - <actionGroup ref="deleteEntitySecondaryGrid" stepKey="deleteCARate"> - <argument name="name" value="{{SimpleTaxCA.state}}-{{SimpleTaxCA.rate}}"/> - <argument name="searchInput" value="{{AdminSecondaryGridSection.taxIdentifierSearch}}"/> - </actionGroup> - <actionGroup ref="logout" stepKey="adminLogout"/> - <deleteData createDataKey="simpleproduct1" stepKey="deleteProduct1"/> - <deleteData createDataKey="simplecategory" stepKey="deleteCategory"/> - <deleteData createDataKey="multiple_address_customer" stepKey="deleteCustomer"/> - </after> - - <actionGroup ref="LoginToStorefrontActionGroup" stepKey="customerLogin"> - <argument name="Customer" value="$$multiple_address_customer$$" /> - </actionGroup> - - <amOnPage url="{{StorefrontCategoryPage.url($$simplecategory.name$$)}}" stepKey="onCategoryPage1"/> - <waitForPageLoad stepKey="waitForCatalogPageLoad1"/> - <moveMouseOver selector="{{StorefrontCategoryMainSection.ProductItemInfo}}" stepKey="hoverProduct1"/> - <click selector="{{StorefrontCategoryMainSection.AddToCartBtn}}" stepKey="addToCart1"/> - <waitForElementVisible selector="{{StorefrontCategoryMainSection.SuccessMsg}}" time="30" stepKey="waitForProductAdded1"/> - <see selector="{{StorefrontCategoryMainSection.SuccessMsg}}" userInput="You added $$simpleproduct1.name$$ to your shopping cart." stepKey="seeAddedToCartMessage1"/> - <see selector="{{StorefrontMinicartSection.quantity}}" userInput="1" stepKey="seeCartQuantity1"/> - <actionGroup ref="GoToCheckoutFromMinicartActionGroup" stepKey="goToCheckoutFromMinicart1" /> - - <click stepKey="selectFirstShippingMethod1" selector="{{CheckoutShippingMethodsSection.firstShippingMethod}}"/> - <waitForElement stepKey="waitForShippingMethodSelect1" selector="{{CheckoutShippingMethodsSection.next}}" time="30"/> - <click stepKey="clickNextOnShippingMethodLoad1" selector="{{CheckoutShippingMethodsSection.next}}" /> - <!-- Checkout select Check/Money Order payment --> - <actionGroup ref="CheckoutSelectCheckMoneyOrderPaymentActionGroup" stepKey="selectCheckMoneyPayment"/> - <waitForElement stepKey="waitForPlaceOrderButton1" selector="{{CheckoutPaymentSection.placeOrder}}" time="30" /> - <see stepKey="seeBillingAddressIsCorrect1" selector="{{CheckoutPaymentSection.billingAddress}}" userInput="{{US_Address_NY.street[0]}}" /> - <click stepKey="clickPlaceOrderButton1" selector="{{CheckoutPaymentSection.placeOrder}}" /> - <waitForPageLoad stepKey="waitForOrderSuccessPage1"/> - <see stepKey="seeSuccessMessage1" selector="{{CheckoutSuccessMainSection.success}}" userInput="Your order number is:" /> - - <amOnPage url="{{StorefrontCategoryPage.url($$simplecategory.name$$)}}" stepKey="onCategoryPage2"/> - <waitForPageLoad stepKey="waitForCatalogPageLoad2"/> - <moveMouseOver selector="{{StorefrontCategoryMainSection.ProductItemInfo}}" stepKey="hoverProduct2"/> - <click selector="{{StorefrontCategoryMainSection.AddToCartBtn}}" stepKey="addToCart2"/> - <waitForElementVisible selector="{{StorefrontCategoryMainSection.SuccessMsg}}" time="30" stepKey="waitForProductAdded2"/> - <see selector="{{StorefrontCategoryMainSection.SuccessMsg}}" userInput="You added $$simpleproduct1.name$$ to your shopping cart." stepKey="seeAddedToCartMessage2"/> - <see selector="{{StorefrontMinicartSection.quantity}}" userInput="1" stepKey="seeCartQuantity2"/> - <actionGroup ref="GoToCheckoutFromMinicartActionGroup" stepKey="goToCheckoutFromMinicart2" /> - - <click selector="{{CheckoutShippingMethodsSection.shipHereButton}}" stepKey="changeShippingAddress"/> - <waitForElementNotVisible selector="{{CheckoutShippingMethodsSection.shippingMethodLoader}}" time="30" stepKey="waitForShippingMethodLoaderNotVisible"/> - <waitForElementVisible selector="{{CheckoutShippingMethodsSection.firstShippingMethod}}" time="30" stepKey="waitForShippingMethodRadioToBeVisible"/> - <waitForPageLoad stepKey="waitForPageLoad23"/> - <click selector="{{CheckoutShippingMethodsSection.firstShippingMethod}}" stepKey="selectFirstShippingMethod2"/> - <waitForElement selector="{{CheckoutShippingMethodsSection.next}}" time="30" stepKey="waitForShippingMethodSelect2"/> - <click selector="{{CheckoutShippingMethodsSection.next}}" stepKey="clickNextOnShippingMethodLoad2"/> - <!-- Checkout select Check/Money Order payment --> - <actionGroup ref="CheckoutSelectCheckMoneyOrderPaymentActionGroup" stepKey="selectCheckMoneyPayment2"/> - <waitForElement selector="{{CheckoutPaymentSection.placeOrder}}" time="30" stepKey="waitForPlaceOrderButton2"/> - <see selector="{{CheckoutPaymentSection.billingAddress}}" userInput="{{US_Address_NY.street[0]}}" stepKey="seeBillingAddressIsCorrect2" /> - <click selector="{{CheckoutPaymentSection.placeOrder}}" stepKey="clickPlaceOrderButton2"/> - <waitForPageLoad stepKey="waitForOrderSuccessPage2"/> - <see selector="{{CheckoutSuccessMainSection.success}}" userInput="Your order number is:" stepKey="seeSuccessMessage2"/> - </test> - <test name="StorefrontCustomerCheckoutTestWithRestrictedCountriesForPayment"> - <annotations> - <features value="Checkout"/> - <stories value="Checkout flow if payment solutions are not available"/> - <title value="Checkout via Customer Checkout with restricted countries for payment"/> - <description value="Should be able to place an order as a Customer with restricted countries for payment."/> - <severity value="MAJOR"/> - <testCaseId value="MAGETWO-42653"/> - <group value="checkout"/> - </annotations> - <before> - <createData entity="_defaultCategory" stepKey="createCategory"/> - <createData entity="ApiSimpleProduct" stepKey="createProduct"> - <requiredEntity createDataKey="createCategory"/> - </createData> - <magentoCLI command="config:set checkout/options/display_billing_address_on 1" stepKey="setShowBillingAddressOnPaymentPage" /> - <magentoCLI command="config:set payment/checkmo/allowspecific 1" stepKey="allowSpecificValue" /> - <magentoCLI command="config:set payment/checkmo/specificcountry GB" stepKey="specificCountryValue" /> - <createData entity="Simple_US_Customer" stepKey="simpleuscustomer"/> - - <!-- Perform reindex and flush cache --> - <magentoCLI command="indexer:reindex" stepKey="reindex"/> - <magentoCLI command="cache:flush" stepKey="flushCache"/> - </before> - <after> - <deleteData createDataKey="createCategory" stepKey="deleteCategory"/> - <deleteData createDataKey="createProduct" stepKey="deleteProduct"/> - <magentoCLI command="config:set payment/checkmo/allowspecific 0" stepKey="allowSpecificValue" /> - <magentoCLI command="config:set payment/checkmo/specificcountry ''" stepKey="specificCountryValue" /> - <magentoCLI command="config:set checkout/options/display_billing_address_on 0" stepKey="setDisplayBillingAddressOnPaymentMethod" /> - <actionGroup ref="StorefrontCustomerLogoutActionGroup" stepKey="logoutCustomer"/> - </after> - <!-- Login as Customer --> - <actionGroup ref="LoginToStorefrontActionGroup" stepKey="customerLogin"> - <argument name="Customer" value="$$simpleuscustomer$$" /> - </actionGroup> - - <!-- Add product to cart --> - <amOnPage url="{{StorefrontCategoryPage.url($$createCategory.name$$)}}" stepKey="onCategoryPage"/> - <waitForPageLoad stepKey="waitForPageLoad1"/> - <moveMouseOver selector="{{StorefrontCategoryMainSection.ProductItemInfo}}" stepKey="hoverProduct"/> - <click selector="{{StorefrontCategoryMainSection.AddToCartBtn}}" stepKey="addToCart"/> - <waitForElementVisible selector="{{StorefrontCategoryMainSection.SuccessMsg}}" time="30" stepKey="waitForProductAdded"/> - <see selector="{{StorefrontCategoryMainSection.SuccessMsg}}" userInput="You added $$createProduct.name$$ to your shopping cart." stepKey="seeAddedToCartMessage"/> - <see selector="{{StorefrontMinicartSection.quantity}}" userInput="1" stepKey="seeCartQuantity"/> - - <!-- Go to checkout page --> - <actionGroup ref="GoToCheckoutFromMinicartActionGroup" stepKey="customerGoToCheckoutFromMinicart" /> - - <!-- Select address --> - <click stepKey="selectAddress" selector="{{CheckoutShippingMethodsSection.firstShippingMethod}}"/> - <waitForElement stepKey="waitNextButton" selector="{{CheckoutShippingMethodsSection.next}}" time="30"/> - <click stepKey="clickNextButton" selector="{{CheckoutShippingMethodsSection.next}}" /> - <waitForPageLoad stepKey="waitBillingForm"/> - <seeInCurrentUrl url="{{CheckoutPage.url}}/#payment" stepKey="assertCheckoutPaymentUrl"/> - <dontSee selector="{{CheckoutPaymentSection.paymentMethodByName('Check / Money order')}}" stepKey="paymentMethodDoesNotAvailable"/> - - <!-- Fill UK Address and verify that payment available and checkout successful --> - <uncheckOption selector="{{StorefrontCheckoutPaymentMethodSection.billingAddressSameAsShippingShared}}" stepKey="uncheckBillingAddressSameAsShippingCheckCheckBox"/> - <selectOption selector="{{CheckoutPaymentSection.billingAddressSelectShared}}" userInput="New Address" stepKey="clickOnNewAddress"/> - <waitForPageLoad stepKey="waitNewAddressBillingForm"/> - <actionGroup ref="LoggedInCheckoutFillNewBillingAddressActionGroup" stepKey="changeAddress"> - <argument name="Address" value="updateCustomerUKAddress"/> - <argument name="classPrefix" value="[aria-hidden=false]"/> - </actionGroup> - <click selector="{{CheckoutPaymentSection.addressAction('Update')}}" stepKey="clickUpdateBillingAddressButton" /> - <actionGroup ref="CheckoutSelectCheckMoneyOrderPaymentActionGroup" stepKey="customerSelectCheckMoneyOrderPayment" /> - <actionGroup ref="CheckoutPlaceOrderActionGroup" stepKey="customerPlaceorder"> - <argument name="orderNumberMessage" value="CONST.successCheckoutOrderNumberMessage" /> - <argument name="emailYouMessage" value="CONST.successCheckoutEmailYouMessage" /> - </actionGroup> - </test> -</tests> diff --git a/app/code/Magento/Checkout/Test/Mftf/Test/StorefrontCustomerCheckoutTest/StorefrontCustomerCheckoutTest.xml b/app/code/Magento/Checkout/Test/Mftf/Test/StorefrontCustomerCheckoutTest/StorefrontCustomerCheckoutTest.xml new file mode 100644 index 0000000000000..b5f573aba7561 --- /dev/null +++ b/app/code/Magento/Checkout/Test/Mftf/Test/StorefrontCustomerCheckoutTest/StorefrontCustomerCheckoutTest.xml @@ -0,0 +1,84 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + /** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +--> +<tests xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/testSchema.xsd"> + <test name="StorefrontCustomerCheckoutTest"> + <annotations> + <features value="Customer Checkout"/> + <stories value="Checkout via Storefront"/> + <title value="Customer Checkout via Storefront"/> + <description value="Should be able to place an order as a customer."/> + <severity value="BLOCKER"/> + <testCaseId value="MC-30274"/> + <group value="checkout"/> + </annotations> + <before> + <createData entity="SimpleSubCategory" stepKey="createCategory"/> + <createData entity="SimpleProduct" stepKey="createSimpleProduct"> + <requiredEntity createDataKey="createCategory"/> + </createData> + <createData entity="Simple_US_Customer" stepKey="createCustomer"/> + <magentoCLI command="indexer:reindex" stepKey="reindex"/> + </before> + <after> + <deleteData createDataKey="createSimpleProduct" stepKey="deleteSimpleProduct"/> + <deleteData createDataKey="createCategory" stepKey="deleteSimpleCategory"/> + <deleteData createDataKey="createCustomer" stepKey="deleteUsCustomer"/> + <actionGroup ref="AdminClearCustomersFiltersActionGroup" stepKey="resetCustomerFilters"/> + <actionGroup ref="AdminLogoutActionGroup" stepKey="logoutFromAdmin"/> + <actionGroup ref="StorefrontCustomerLogoutActionGroup" stepKey="logoutCustomer"/> + </after> + + <actionGroup ref="LoginToStorefrontActionGroup" stepKey="storefrontCustomerLogin"> + <argument name="Customer" value="$$createCustomer$$"/> + </actionGroup> + + <actionGroup ref="StorefrontNavigateCategoryPageActionGroup" stepKey="navigateToCategoryPage"> + <argument name="category" value="$$createCategory$$"/> + </actionGroup> + + <waitForPageLoad stepKey="waitForCatalogPageLoad"/> + + <actionGroup ref="StorefrontAddCategoryProductToCartActionGroup" stepKey="addProductToCart"> + <argument name="product" value="$$createSimpleProduct$$"/> + <argument name="productCount" value="CONST.one"/> + </actionGroup> + + <actionGroup ref="GoToCheckoutFromMinicartActionGroup" stepKey="goToCheckoutFromMinicart"/> + <actionGroup ref="CheckoutSelectFlatRateShippingMethodActionGroup" stepKey="selectFlatRate"/> + <actionGroup ref="StorefrontCheckoutForwardFromShippingStepActionGroup" stepKey="goToReview"/> + <actionGroup ref="CheckoutSelectCheckMoneyOrderPaymentActionGroup" stepKey="selectCheckMoneyOrder"/> + <actionGroup ref="CheckoutPlaceOrderActionGroup" stepKey="clickOnPlaceOrder"> + <argument name="orderNumberMessage" value="CONST.successCheckoutOrderNumberMessage"/> + <argument name="emailYouMessage" value="CONST.successCheckoutEmailYouMessage"/> + </actionGroup> + + <grabTextFrom selector="{{CheckoutSuccessMainSection.orderNumber22}}" stepKey="orderNumber"/> + + <actionGroup ref="AdminLoginActionGroup" stepKey="loginAsAdmin"/> + + <actionGroup ref="OpenOrderByIdActionGroup" stepKey="addFilterToGridAndOpenOrder"> + <argument name="orderId" value="{$orderNumber}"/> + </actionGroup> + + <see selector="{{AdminOrderDetailsInformationSection.orderStatus}}" userInput="Pending" stepKey="verifyOrderStatus"/> + <see selector="{{AdminOrderDetailsInformationSection.accountInformation}}" userInput="Customer" stepKey="verifyAccountInformation"/> + <see selector="{{AdminOrderDetailsInformationSection.accountInformation}}" userInput="$$createCustomer.email$$" stepKey="verifyCustomerEmail"/> + <see selector="{{AdminOrderDetailsInformationSection.billingAddress}}" userInput="{{US_Address_TX.street[0]}}" stepKey="verifyBillingAddress"/> + <see selector="{{AdminOrderDetailsInformationSection.shippingAddress}}" userInput="{{US_Address_TX.street[0]}}" stepKey="verifyShippingAddress"/> + <see selector="{{AdminOrderDetailsInformationSection.itemsOrdered}}" userInput="$$createSimpleProduct.name$$" stepKey="verifyProductName"/> + + <actionGroup ref="OpenEditCustomerFromAdminActionGroup" stepKey="openCustomerEditPage"> + <argument name="customer" value="$$createCustomer$$"/> + </actionGroup> + + <click selector="{{AdminEditCustomerInformationSection.orders}}" stepKey="navigateToOrdersTab"/> + <waitForElementVisible selector="{{AdminEditCustomerOrdersSection.orderGrid}}" stepKey="waitForOrdersGridVisible"/> + <see selector="{{AdminEditCustomerOrdersSection.orderGrid}}" userInput="$$createCustomer.firstname$$ $$createCustomer.lastname$$" stepKey="verifyOrder"/> + </test> +</tests> diff --git a/app/code/Magento/Checkout/Test/Mftf/Test/StorefrontCustomerCheckoutTest/StorefrontCustomerCheckoutTestWithMultipleAddressesAndTaxRatesTest.xml b/app/code/Magento/Checkout/Test/Mftf/Test/StorefrontCustomerCheckoutTest/StorefrontCustomerCheckoutTestWithMultipleAddressesAndTaxRatesTest.xml new file mode 100644 index 0000000000000..4c3c1561a2445 --- /dev/null +++ b/app/code/Magento/Checkout/Test/Mftf/Test/StorefrontCustomerCheckoutTest/StorefrontCustomerCheckoutTestWithMultipleAddressesAndTaxRatesTest.xml @@ -0,0 +1,126 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + /** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +--> +<tests xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/testSchema.xsd"> + <test name="StorefrontCustomerCheckoutTestWithMultipleAddressesAndTaxRatesTest"> + <annotations> + <features value="Checkout"/> + <stories value="Customer checkout"/> + <title value="Customer Checkout with multiple addresses and tax rates"/> + <description value="Should be able to place an order as a customer with multiple addresses and tax rates."/> + <testCaseId value="MAGETWO-93109"/> + <severity value="AVERAGE"/> + <group value="checkout"/> + </annotations> + <before> + <createData entity="SimpleSubCategory" stepKey="simplecategory"/> + <createData entity="SimpleProduct" stepKey="simpleproduct1"> + <requiredEntity createDataKey="simplecategory"/> + </createData> + <createData entity="Simple_US_Customer_Multiple_Addresses" stepKey="multiple_address_customer"/> + + <actionGroup ref="AdminLoginActionGroup" stepKey="loginAsAdmin"/> + + <!-- Go to tax rule page --> + <amOnPage url="{{AdminTaxRuleGridPage.url}}" stepKey="goToTaxRulePage"/> + <waitForPageLoad stepKey="waitForTaxRatePage"/> + <click stepKey="addNewTaxRate" selector="{{AdminGridMainControls.add}}"/> + <fillField stepKey="fillRuleName" selector="{{AdminTaxRulesSection.ruleName}}" userInput="SampleRule"/> + + <!-- Add NY and CA tax rules --> + <actionGroup ref="AddNewTaxRateNoZipActionGroup" stepKey="addNYTaxRate"> + <argument name="taxCode" value="SimpleTaxNY"/> + </actionGroup> + + <actionGroup ref="AddNewTaxRateNoZipActionGroup" stepKey="addCATaxRate"> + <argument name="taxCode" value="SimpleTaxCA"/> + </actionGroup> + + <click stepKey="clickSave" selector="{{AdminStoresMainActionsSection.saveButton}}"/> + + <!--TODO: REMOVE AFTER FIX MC-21717 --> + <magentoCLI command="indexer:reindex" stepKey="reindex"/> + <magentoCLI command="cache:flush full_page" stepKey="flushCache"/> + </before> + <after> + <!-- Go to the tax rule page and delete the row we created--> + <amOnPage url="{{AdminTaxRuleGridPage.url}}" stepKey="goToTaxRulesPage"/> + <waitForPageLoad stepKey="waitForRulesPage"/> + <actionGroup ref="deleteEntitySecondaryGrid" stepKey="deleteRule"> + <argument name="name" value="SampleRule"/> + <argument name="searchInput" value="{{AdminSecondaryGridSection.taxIdentifierSearch}}"/> + </actionGroup> + + <!-- Go to the tax rate page --> + <amOnPage url="{{AdminTaxRateGridPage.url}}" stepKey="goToTaxRatesPage"/> + <waitForPageLoad stepKey="waitForRatesPage"/> + <!-- Delete the two tax rates that were created --> + <actionGroup ref="deleteEntitySecondaryGrid" stepKey="deleteNYRate"> + <argument name="name" value="{{SimpleTaxNY.state}}-{{SimpleTaxNY.rate}}"/> + <argument name="searchInput" value="{{AdminSecondaryGridSection.taxIdentifierSearch}}"/> + </actionGroup> + + <actionGroup ref="deleteEntitySecondaryGrid" stepKey="deleteCARate"> + <argument name="name" value="{{SimpleTaxCA.state}}-{{SimpleTaxCA.rate}}"/> + <argument name="searchInput" value="{{AdminSecondaryGridSection.taxIdentifierSearch}}"/> + </actionGroup> + <actionGroup ref="AdminLogoutActionGroup" stepKey="adminLogout"/> + <deleteData createDataKey="simpleproduct1" stepKey="deleteProduct1"/> + <deleteData createDataKey="simplecategory" stepKey="deleteCategory"/> + <deleteData createDataKey="multiple_address_customer" stepKey="deleteCustomer"/> + </after> + + <actionGroup ref="LoginToStorefrontActionGroup" stepKey="customerLogin"> + <argument name="Customer" value="$$multiple_address_customer$$"/> + </actionGroup> + + <amOnPage url="{{StorefrontCategoryPage.url($$simplecategory.name$$)}}" stepKey="onCategoryPage1"/> + <waitForPageLoad stepKey="waitForCatalogPageLoad1"/> + <moveMouseOver selector="{{StorefrontCategoryMainSection.ProductItemInfo}}" stepKey="hoverProduct1"/> + <click selector="{{StorefrontCategoryMainSection.AddToCartBtn}}" stepKey="addToCart1"/> + <waitForElementVisible selector="{{StorefrontCategoryMainSection.SuccessMsg}}" time="30" stepKey="waitForProductAdded1"/> + <see selector="{{StorefrontCategoryMainSection.SuccessMsg}}" userInput="You added $$simpleproduct1.name$$ to your shopping cart." stepKey="seeAddedToCartMessage1"/> + <see selector="{{StorefrontMinicartSection.quantity}}" userInput="1" stepKey="seeCartQuantity1"/> + <actionGroup ref="GoToCheckoutFromMinicartActionGroup" stepKey="goToCheckoutFromMinicart1"/> + + <click stepKey="selectFirstShippingMethod1" selector="{{CheckoutShippingMethodsSection.firstShippingMethod}}"/> + <waitForElement stepKey="waitForShippingMethodSelect1" selector="{{CheckoutShippingMethodsSection.next}}" time="30"/> + <click stepKey="clickNextOnShippingMethodLoad1" selector="{{CheckoutShippingMethodsSection.next}}"/> + <!-- Checkout select Check/Money Order payment --> + <actionGroup ref="CheckoutSelectCheckMoneyOrderPaymentActionGroup" stepKey="selectCheckMoneyPayment"/> + <waitForElement stepKey="waitForPlaceOrderButton1" selector="{{CheckoutPaymentSection.placeOrder}}" time="30"/> + <see stepKey="seeBillingAddressIsCorrect1" selector="{{CheckoutPaymentSection.billingAddress}}" userInput="{{US_Address_NY.street[0]}}"/> + <click stepKey="clickPlaceOrderButton1" selector="{{CheckoutPaymentSection.placeOrder}}"/> + <waitForPageLoad stepKey="waitForOrderSuccessPage1"/> + <see stepKey="seeSuccessMessage1" selector="{{CheckoutSuccessMainSection.success}}" userInput="Your order number is:"/> + + <amOnPage url="{{StorefrontCategoryPage.url($$simplecategory.name$$)}}" stepKey="onCategoryPage2"/> + <waitForPageLoad stepKey="waitForCatalogPageLoad2"/> + <moveMouseOver selector="{{StorefrontCategoryMainSection.ProductItemInfo}}" stepKey="hoverProduct2"/> + <click selector="{{StorefrontCategoryMainSection.AddToCartBtn}}" stepKey="addToCart2"/> + <waitForElementVisible selector="{{StorefrontCategoryMainSection.SuccessMsg}}" time="30" stepKey="waitForProductAdded2"/> + <see selector="{{StorefrontCategoryMainSection.SuccessMsg}}" userInput="You added $$simpleproduct1.name$$ to your shopping cart." stepKey="seeAddedToCartMessage2"/> + <see selector="{{StorefrontMinicartSection.quantity}}" userInput="1" stepKey="seeCartQuantity2"/> + <actionGroup ref="GoToCheckoutFromMinicartActionGroup" stepKey="goToCheckoutFromMinicart2"/> + + <click selector="{{CheckoutShippingMethodsSection.shipHereButton}}" stepKey="changeShippingAddress"/> + <waitForElementNotVisible selector="{{CheckoutShippingMethodsSection.shippingMethodLoader}}" time="30" stepKey="waitForShippingMethodLoaderNotVisible"/> + <waitForElementVisible selector="{{CheckoutShippingMethodsSection.firstShippingMethod}}" time="30" stepKey="waitForShippingMethodRadioToBeVisible"/> + <waitForPageLoad stepKey="waitForPageLoad23"/> + <click selector="{{CheckoutShippingMethodsSection.firstShippingMethod}}" stepKey="selectFirstShippingMethod2"/> + <waitForElement selector="{{CheckoutShippingMethodsSection.next}}" time="30" stepKey="waitForShippingMethodSelect2"/> + <click selector="{{CheckoutShippingMethodsSection.next}}" stepKey="clickNextOnShippingMethodLoad2"/> + <!-- Checkout select Check/Money Order payment --> + <actionGroup ref="CheckoutSelectCheckMoneyOrderPaymentActionGroup" stepKey="selectCheckMoneyPayment2"/> + <waitForElement selector="{{CheckoutPaymentSection.placeOrder}}" time="30" stepKey="waitForPlaceOrderButton2"/> + <see selector="{{CheckoutPaymentSection.billingAddress}}" userInput="{{US_Address_NY.street[0]}}" stepKey="seeBillingAddressIsCorrect2"/> + <click selector="{{CheckoutPaymentSection.placeOrder}}" stepKey="clickPlaceOrderButton2"/> + <waitForPageLoad stepKey="waitForOrderSuccessPage2"/> + <see selector="{{CheckoutSuccessMainSection.success}}" userInput="Your order number is:" stepKey="seeSuccessMessage2"/> + </test> +</tests> diff --git a/app/code/Magento/Checkout/Test/Mftf/Test/StorefrontCustomerCheckoutTest/StorefrontCustomerCheckoutTestWithRestrictedCountriesForPaymentTest.xml b/app/code/Magento/Checkout/Test/Mftf/Test/StorefrontCustomerCheckoutTest/StorefrontCustomerCheckoutTestWithRestrictedCountriesForPaymentTest.xml new file mode 100644 index 0000000000000..d042a15e3c958 --- /dev/null +++ b/app/code/Magento/Checkout/Test/Mftf/Test/StorefrontCustomerCheckoutTest/StorefrontCustomerCheckoutTestWithRestrictedCountriesForPaymentTest.xml @@ -0,0 +1,82 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + /** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +--> +<tests xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/testSchema.xsd"> + <test name="StorefrontCustomerCheckoutTestWithRestrictedCountriesForPaymentTest"> + <annotations> + <features value="Checkout"/> + <stories value="Checkout flow if payment solutions are not available"/> + <title value="Checkout via Customer Checkout with restricted countries for payment"/> + <description value="Should be able to place an order as a Customer with restricted countries for payment."/> + <severity value="MAJOR"/> + <testCaseId value="MAGETWO-42653"/> + <group value="checkout"/> + </annotations> + <before> + <createData entity="_defaultCategory" stepKey="createCategory"/> + <createData entity="ApiSimpleProduct" stepKey="createProduct"> + <requiredEntity createDataKey="createCategory"/> + </createData> + <magentoCLI command="config:set checkout/options/display_billing_address_on 1" stepKey="setShowBillingAddressOnPaymentPage"/> + <magentoCLI command="config:set payment/checkmo/allowspecific 1" stepKey="allowSpecificValue"/> + <magentoCLI command="config:set payment/checkmo/specificcountry GB" stepKey="specificCountryValue"/> + <createData entity="Simple_US_Customer" stepKey="simpleuscustomer"/> + + <!-- Perform reindex and flush cache --> + <magentoCLI command="indexer:reindex" stepKey="reindex"/> + <magentoCLI command="cache:flush" stepKey="flushCache"/> + </before> + <after> + <deleteData createDataKey="createCategory" stepKey="deleteCategory"/> + <deleteData createDataKey="createProduct" stepKey="deleteProduct"/> + <magentoCLI command="config:set payment/checkmo/allowspecific 0" stepKey="allowSpecificValue"/> + <magentoCLI command="config:set payment/checkmo/specificcountry ''" stepKey="specificCountryValue"/> + <magentoCLI command="config:set checkout/options/display_billing_address_on 0" stepKey="setDisplayBillingAddressOnPaymentMethod"/> + <actionGroup ref="StorefrontCustomerLogoutActionGroup" stepKey="logoutCustomer"/> + </after> + <!-- Login as Customer --> + <actionGroup ref="LoginToStorefrontActionGroup" stepKey="customerLogin"> + <argument name="Customer" value="$$simpleuscustomer$$"/> + </actionGroup> + + <!-- Add product to cart --> + <amOnPage url="{{StorefrontCategoryPage.url($$createCategory.name$$)}}" stepKey="onCategoryPage"/> + <waitForPageLoad stepKey="waitForPageLoad1"/> + <moveMouseOver selector="{{StorefrontCategoryMainSection.ProductItemInfo}}" stepKey="hoverProduct"/> + <click selector="{{StorefrontCategoryMainSection.AddToCartBtn}}" stepKey="addToCart"/> + <waitForElementVisible selector="{{StorefrontCategoryMainSection.SuccessMsg}}" time="30" stepKey="waitForProductAdded"/> + <see selector="{{StorefrontCategoryMainSection.SuccessMsg}}" userInput="You added $$createProduct.name$$ to your shopping cart." stepKey="seeAddedToCartMessage"/> + <see selector="{{StorefrontMinicartSection.quantity}}" userInput="1" stepKey="seeCartQuantity"/> + + <!-- Go to checkout page --> + <actionGroup ref="GoToCheckoutFromMinicartActionGroup" stepKey="customerGoToCheckoutFromMinicart"/> + + <!-- Select address --> + <click stepKey="selectAddress" selector="{{CheckoutShippingMethodsSection.firstShippingMethod}}"/> + <waitForElement stepKey="waitNextButton" selector="{{CheckoutShippingMethodsSection.next}}" time="30"/> + <click stepKey="clickNextButton" selector="{{CheckoutShippingMethodsSection.next}}"/> + <waitForPageLoad stepKey="waitBillingForm"/> + <seeInCurrentUrl url="{{CheckoutPage.url}}/#payment" stepKey="assertCheckoutPaymentUrl"/> + <dontSee selector="{{CheckoutPaymentSection.paymentMethodByName('Check / Money order')}}" stepKey="paymentMethodDoesNotAvailable"/> + + <!-- Fill UK Address and verify that payment available and checkout successful --> + <uncheckOption selector="{{StorefrontCheckoutPaymentMethodSection.billingAddressSameAsShippingShared}}" stepKey="uncheckBillingAddressSameAsShippingCheckCheckBox"/> + <selectOption selector="{{CheckoutPaymentSection.billingAddressSelectShared}}" userInput="New Address" stepKey="clickOnNewAddress"/> + <waitForPageLoad stepKey="waitNewAddressBillingForm"/> + <actionGroup ref="LoggedInCheckoutFillNewBillingAddressActionGroup" stepKey="changeAddress"> + <argument name="Address" value="updateCustomerUKAddress"/> + <argument name="classPrefix" value="[aria-hidden=false]"/> + </actionGroup> + <click selector="{{CheckoutPaymentSection.addressAction('Update')}}" stepKey="clickUpdateBillingAddressButton"/> + <actionGroup ref="CheckoutSelectCheckMoneyOrderPaymentActionGroup" stepKey="customerSelectCheckMoneyOrderPayment"/> + <actionGroup ref="CheckoutPlaceOrderActionGroup" stepKey="customerPlaceorder"> + <argument name="orderNumberMessage" value="CONST.successCheckoutOrderNumberMessage"/> + <argument name="emailYouMessage" value="CONST.successCheckoutEmailYouMessage"/> + </actionGroup> + </test> +</tests> diff --git a/app/code/Magento/Checkout/Test/Mftf/Test/StorefrontCustomerCheckoutWithNewCustomerRegistrationAndDisableGuestCheckoutTest.xml b/app/code/Magento/Checkout/Test/Mftf/Test/StorefrontCustomerCheckoutWithNewCustomerRegistrationAndDisableGuestCheckoutTest.xml index e0fff27a583e7..0f82302260995 100644 --- a/app/code/Magento/Checkout/Test/Mftf/Test/StorefrontCustomerCheckoutWithNewCustomerRegistrationAndDisableGuestCheckoutTest.xml +++ b/app/code/Magento/Checkout/Test/Mftf/Test/StorefrontCustomerCheckoutWithNewCustomerRegistrationAndDisableGuestCheckoutTest.xml @@ -12,13 +12,13 @@ <stories value="Checkout"/> <title value="Verify customer checkout by following new customer registration when guest checkout is disabled"/> <description value="Customer is redirected to checkout on login, follow the new Customer registration when guest checkout is disabled"/> - <severity value="CRITICAL"/> + <severity value="BLOCKER"/> <testCaseId value="MC-14704"/> <group value="mtf_migrated"/> </annotations> <before> - <actionGroup ref="LoginAsAdmin" stepKey="loginToAdminPanel"/> + <actionGroup ref="AdminLoginActionGroup" stepKey="loginToAdminPanel"/> <magentoCLI command="config:set {{DisableGuestCheckoutConfigData.path}} {{DisableGuestCheckoutConfigData.value}}" stepKey="disableGuestCheckout"/> <magentoCLI command="config:set {{DisableCustomerRedirectToDashboardConfigData.path}} {{DisableCustomerRedirectToDashboardConfigData.value}}" stepKey="disableCustomerRedirect"/> <createData entity="SimpleProduct2" stepKey="simpleProduct"> @@ -35,7 +35,7 @@ <actionGroup ref="StorefrontSignOutActionGroup" stepKey="customerLogout"/> <deleteData createDataKey="simpleProduct" stepKey="deleteProduct"/> <deleteData createDataKey="createCustomer" stepKey="deleteCustomer"/> - <actionGroup ref="logout" stepKey="logout"/> + <actionGroup ref="AdminLogoutActionGroup" stepKey="logout"/> </after> <!--Open Product page in StoreFront and assert product and price range --> diff --git a/app/code/Magento/Checkout/Test/Mftf/Test/StorefrontCustomerCheckoutWithoutRegionTest.xml b/app/code/Magento/Checkout/Test/Mftf/Test/StorefrontCustomerCheckoutWithoutRegionTest.xml index a6b9372679a11..0c762519e9083 100644 --- a/app/code/Magento/Checkout/Test/Mftf/Test/StorefrontCustomerCheckoutWithoutRegionTest.xml +++ b/app/code/Magento/Checkout/Test/Mftf/Test/StorefrontCustomerCheckoutWithoutRegionTest.xml @@ -24,7 +24,7 @@ <requiredEntity createDataKey="createCategory"/> </createData> <createData entity="Simple_GB_Customer" stepKey="createCustomer"/> - <actionGroup stepKey="loginToAdminPanel" ref="LoginAsAdmin"/> + <actionGroup stepKey="loginToAdminPanel" ref="AdminLoginActionGroup"/> <actionGroup ref="SelectCountriesWithRequiredRegionActionGroup" stepKey="setCustomCountryWithRequiredRegion"> <argument name="countries" value="CustomCountryWithRequiredRegion"/> </actionGroup> @@ -33,7 +33,7 @@ <actionGroup ref="SelectCountriesWithRequiredRegionActionGroup" stepKey="setDefaultCountriesWithRequiredRegion"> <argument name="countries" value="DefaultCountriesWithRequiredRegions"/> </actionGroup> - <actionGroup ref="logout" stepKey="logout"/> + <actionGroup ref="AdminLogoutActionGroup" stepKey="logout"/> <deleteData createDataKey="createProduct" stepKey="deleteProduct"/> <deleteData createDataKey="createCategory" stepKey="deleteCategory"/> <deleteData createDataKey="createCustomer" stepKey="deleteCustomer"/> diff --git a/app/code/Magento/Checkout/Test/Mftf/Test/StorefrontCustomerLoginDuringCheckoutTest.xml b/app/code/Magento/Checkout/Test/Mftf/Test/StorefrontCustomerLoginDuringCheckoutTest.xml index afefbff5ea59a..27597a72fdb7f 100644 --- a/app/code/Magento/Checkout/Test/Mftf/Test/StorefrontCustomerLoginDuringCheckoutTest.xml +++ b/app/code/Magento/Checkout/Test/Mftf/Test/StorefrontCustomerLoginDuringCheckoutTest.xml @@ -23,7 +23,7 @@ <createData entity="SimpleProduct2" stepKey="createProduct"/> <!-- Login as admin --> - <actionGroup ref="LoginAsAdmin" stepKey="loginAsAdmin"/> + <actionGroup ref="AdminLoginActionGroup" stepKey="loginAsAdmin"/> </before> <after> <!-- Delete simple product --> @@ -38,10 +38,18 @@ </actionGroup> <!-- Logout admin --> - <actionGroup ref="logout" stepKey="logout"/> + <actionGroup ref="AdminLogoutActionGroup" stepKey="logout"/> </after> <!-- Go to Storefront as Guest and create new account --> - <actionGroup ref="SignUpNewUserFromStorefrontActionGroup" stepKey="createNewCustomerAccount"/> + <actionGroup ref="StorefrontOpenCustomerAccountCreatePageActionGroup" stepKey="openCreateAccountPage"/> + <actionGroup ref="StorefrontFillCustomerAccountCreationFormActionGroup" stepKey="fillCreateAccountForm"> + <argument name="customer" value="CustomerEntityOne"/> + </actionGroup> + <actionGroup ref="StorefrontClickCreateAnAccountCustomerAccountCreationFormActionGroup" stepKey="submitCreateAccountForm"/> + <actionGroup ref="AssertMessageCustomerCreateAccountActionGroup" stepKey="seeSuccessMessage"> + <argument name="messageType" value="success"/> + <argument name="message" value="Thank you for registering with Main Website Store."/> + </actionGroup> <!-- Sign Out --> <actionGroup ref="StorefrontCustomerLogoutActionGroup" stepKey="customerLogout"/> diff --git a/app/code/Magento/Checkout/Test/Mftf/Test/StorefrontDeleteBundleProductFromMiniShoppingCartTest.xml b/app/code/Magento/Checkout/Test/Mftf/Test/StorefrontDeleteBundleProductFromMiniShoppingCartTest.xml index 8956ed7687f06..d116d0049c9df 100644 --- a/app/code/Magento/Checkout/Test/Mftf/Test/StorefrontDeleteBundleProductFromMiniShoppingCartTest.xml +++ b/app/code/Magento/Checkout/Test/Mftf/Test/StorefrontDeleteBundleProductFromMiniShoppingCartTest.xml @@ -19,7 +19,7 @@ </annotations> <before> - <actionGroup ref="LoginAsAdmin" stepKey="LoginAsAdmin"/> + <actionGroup ref="AdminLoginActionGroup" stepKey="LoginAsAdmin"/> <magentoCLI command="config:set {{EnableFlatRateConfigData.path}} {{EnableFlatRateConfigData.value}}" stepKey="enableFlatRate"/> <magentoCLI command="config:set {{EnableFlatRateDefaultPriceConfigData.path}} {{EnableFlatRateDefaultPriceConfigData.value}}" stepKey="enableFlatRateDefaultPrice"/> <createData entity="SimpleSubCategory" stepKey="createSubCategory"/> @@ -46,7 +46,7 @@ <deleteData createDataKey="simpleProduct1" stepKey="deleteProduct1"/> <deleteData createDataKey="createBundleProduct" stepKey="deleteBundleProduct"/> <deleteData createDataKey="createSubCategory" stepKey="deleteCategory"/> - <actionGroup ref="logout" stepKey="logout"/> + <actionGroup ref="AdminLogoutActionGroup" stepKey="logout"/> </after> <!--Open Product page in StoreFront --> @@ -65,8 +65,7 @@ <waitForPageLoad stepKey="waitForMiniCartPanelToAppear"/> <!-- Assert Product in Mini Cart --> - <click selector="{{StorefrontMinicartSection.showCart}}" stepKey="clickOnMiniCart"/> - <waitForPageLoad stepKey="waitForPageToLoad1"/> + <actionGroup ref="StorefrontClickOnMiniCartActionGroup" stepKey="clickOnMiniCart"/> <actionGroup ref="AssertStorefrontMiniCartItemsActionGroup" stepKey="assertSimpleProduct3MiniCart"> <argument name="productName" value="$$createBundleProduct.name$$"/> <argument name="productPrice" value="$10.00"/> diff --git a/app/code/Magento/Checkout/Test/Mftf/Test/StorefrontDeleteConfigurableProductFromMiniShoppingCartTest.xml b/app/code/Magento/Checkout/Test/Mftf/Test/StorefrontDeleteConfigurableProductFromMiniShoppingCartTest.xml index 34264e5982651..eb8b047b57288 100644 --- a/app/code/Magento/Checkout/Test/Mftf/Test/StorefrontDeleteConfigurableProductFromMiniShoppingCartTest.xml +++ b/app/code/Magento/Checkout/Test/Mftf/Test/StorefrontDeleteConfigurableProductFromMiniShoppingCartTest.xml @@ -19,7 +19,7 @@ </annotations> <before> - <actionGroup ref="LoginAsAdmin" stepKey="LoginAsAdmin"/> + <actionGroup ref="AdminLoginActionGroup" stepKey="LoginAsAdmin"/> <!-- Create Default Category --> <createData entity="_defaultCategory" stepKey="createCategory"/> @@ -71,7 +71,10 @@ <deleteData createDataKey="createConfigProduct" stepKey="deleteProduct"/> <deleteData createDataKey="createCategory" stepKey="deleteCategory"/> <deleteData createDataKey="createConfigProductAttribute" stepKey="deleteProductAttribute"/> - <actionGroup ref="logout" stepKey="logout"/> + <actionGroup ref="AdminLogoutActionGroup" stepKey="logout"/> + + <!-- Reindex invalidated indices after product attribute has been created/deleted --> + <magentoCron groups="index" stepKey="reindexInvalidatedIndices"/> </after> <!-- Add Configurable Product to the cart --> diff --git a/app/code/Magento/Checkout/Test/Mftf/Test/StorefrontDeleteDownloadableProductFromMiniShoppingCartTest.xml b/app/code/Magento/Checkout/Test/Mftf/Test/StorefrontDeleteDownloadableProductFromMiniShoppingCartTest.xml index dd9259833cbc4..8a52fa7740b95 100644 --- a/app/code/Magento/Checkout/Test/Mftf/Test/StorefrontDeleteDownloadableProductFromMiniShoppingCartTest.xml +++ b/app/code/Magento/Checkout/Test/Mftf/Test/StorefrontDeleteDownloadableProductFromMiniShoppingCartTest.xml @@ -20,7 +20,7 @@ <before> <magentoCLI stepKey="addDownloadableDomain" command="downloadable:domains:add example.com static.magento.com"/> - <actionGroup ref="LoginAsAdmin" stepKey="LoginAsAdmin"/> + <actionGroup ref="AdminLoginActionGroup" stepKey="LoginAsAdmin"/> <magentoCLI command="config:set {{EnableFlatRateConfigData.path}} {{EnableFlatRateConfigData.value}}" stepKey="enableFlatRate"/> <magentoCLI command="config:set {{EnableFlatRateDefaultPriceConfigData.path}} {{EnableFlatRateDefaultPriceConfigData.value}}" stepKey="enableFlatRateDefaultPrice"/> <createData entity="ApiDownloadableProduct" stepKey="createDownloadableProduct"/> @@ -33,7 +33,7 @@ <after> <magentoCLI stepKey="removeDownloadableDomain" command="downloadable:domains:remove example.com static.magento.com"/> <deleteData createDataKey="createDownloadableProduct" stepKey="deleteProduct"/> - <actionGroup ref="logout" stepKey="logout"/> + <actionGroup ref="AdminLogoutActionGroup" stepKey="logout"/> </after> <!-- Open Downloadable Product page --> @@ -49,8 +49,7 @@ <actionGroup ref="ClickViewAndEditCartFromMiniCartActionGroup" stepKey="selectViewAndEditCart"/> <!-- Assert product details in Mini Cart --> - <click selector="{{StorefrontMinicartSection.showCart}}" stepKey="clickOnMiniCart"/> - <waitForPageLoad stepKey="waitForPageToLoad1"/> + <actionGroup ref="StorefrontClickOnMiniCartActionGroup" stepKey="clickOnMiniCart"/> <actionGroup ref="AssertStorefrontMiniCartItemsActionGroup" stepKey="assertMiniCart"> <argument name="productName" value="$$createDownloadableProduct.name$$"/> <argument name="productPrice" value="$123.00"/> diff --git a/app/code/Magento/Checkout/Test/Mftf/Test/StorefrontDeleteProductsWithCartItemsDisplayDefaultLimitationFromMiniShoppingCartTest.xml b/app/code/Magento/Checkout/Test/Mftf/Test/StorefrontDeleteProductsWithCartItemsDisplayDefaultLimitationFromMiniShoppingCartTest.xml index 07215988c945d..b460bc2bb3446 100644 --- a/app/code/Magento/Checkout/Test/Mftf/Test/StorefrontDeleteProductsWithCartItemsDisplayDefaultLimitationFromMiniShoppingCartTest.xml +++ b/app/code/Magento/Checkout/Test/Mftf/Test/StorefrontDeleteProductsWithCartItemsDisplayDefaultLimitationFromMiniShoppingCartTest.xml @@ -19,7 +19,7 @@ </annotations> <before> - <actionGroup ref="LoginAsAdmin" stepKey="LoginAsAdmin"/> + <actionGroup ref="AdminLoginActionGroup" stepKey="LoginAsAdmin"/> <!--Create 10 simple products--> <createData entity="SimpleProduct2" stepKey="simpleProduct1"> <field key="price">10.00</field> @@ -63,7 +63,7 @@ <deleteData createDataKey="simpleProduct8" stepKey="deleteProduct8"/> <deleteData createDataKey="simpleProduct9" stepKey="deleteProduct9"/> <deleteData createDataKey="simpleProduct10" stepKey="deleteProduct10"/> - <actionGroup ref="logout" stepKey="logout"/> + <actionGroup ref="AdminLogoutActionGroup" stepKey="logout"/> </after> <!--Open Product1 page in StoreFront--> diff --git a/app/code/Magento/Checkout/Test/Mftf/Test/StorefrontDeleteSimpleAndVirtualProductFromMiniShoppingCartTest.xml b/app/code/Magento/Checkout/Test/Mftf/Test/StorefrontDeleteSimpleAndVirtualProductFromMiniShoppingCartTest.xml index b1aa1de71293f..21966875519dc 100644 --- a/app/code/Magento/Checkout/Test/Mftf/Test/StorefrontDeleteSimpleAndVirtualProductFromMiniShoppingCartTest.xml +++ b/app/code/Magento/Checkout/Test/Mftf/Test/StorefrontDeleteSimpleAndVirtualProductFromMiniShoppingCartTest.xml @@ -19,7 +19,7 @@ </annotations> <before> - <actionGroup ref="LoginAsAdmin" stepKey="LoginAsAdmin"/> + <actionGroup ref="AdminLoginActionGroup" stepKey="LoginAsAdmin"/> <!--Create simple product--> <createData entity="SimpleProduct2" stepKey="simpleProduct"> <field key="price">10.00</field> @@ -32,7 +32,7 @@ <after> <deleteData createDataKey="simpleProduct" stepKey="deleteSimpleProduct"/> <deleteData createDataKey="virtualProduct" stepKey="deleteVirtualproduct"/> - <actionGroup ref="logout" stepKey="logout"/> + <actionGroup ref="AdminLogoutActionGroup" stepKey="logout"/> </after> <!-- Add Simple Product to the cart --> @@ -47,8 +47,7 @@ </actionGroup> <!-- Assert Simple and Virtual products in mini cart --> - <click selector="{{StorefrontMinicartSection.showCart}}" stepKey="clickOnMiniCart"/> - <waitForPageLoad stepKey="waitForPageToLoad1"/> + <actionGroup ref="StorefrontClickOnMiniCartActionGroup" stepKey="clickOnMiniCart"/> <actionGroup ref="AssertStorefrontMiniCartItemsActionGroup" stepKey="assertSimpleProductInMiniCart"> <argument name="productName" value="$$simpleProduct.name$$"/> <argument name="productPrice" value="$10.00"/> diff --git a/app/code/Magento/Checkout/Test/Mftf/Test/StorefrontDeleteSimpleProductFromMiniShoppingCartTest.xml b/app/code/Magento/Checkout/Test/Mftf/Test/StorefrontDeleteSimpleProductFromMiniShoppingCartTest.xml index 0108b6310f59d..effd376ab4bfb 100644 --- a/app/code/Magento/Checkout/Test/Mftf/Test/StorefrontDeleteSimpleProductFromMiniShoppingCartTest.xml +++ b/app/code/Magento/Checkout/Test/Mftf/Test/StorefrontDeleteSimpleProductFromMiniShoppingCartTest.xml @@ -19,7 +19,7 @@ </annotations> <before> - <actionGroup ref="LoginAsAdmin" stepKey="LoginAsAdmin"/> + <actionGroup ref="AdminLoginActionGroup" stepKey="LoginAsAdmin"/> <!--Create simple product--> <createData entity="SimpleProduct2" stepKey="simpleProduct"> <field key="price">10.00</field> @@ -27,7 +27,7 @@ </before> <after> <deleteData createDataKey="simpleProduct" stepKey="deleteProduct"/> - <actionGroup ref="logout" stepKey="logout"/> + <actionGroup ref="AdminLogoutActionGroup" stepKey="logout"/> </after> <!-- Add Simple Product to the cart --> @@ -36,8 +36,7 @@ </actionGroup> <!-- Assert Product in Mini Cart --> - <click selector="{{StorefrontMinicartSection.showCart}}" stepKey="clickOnMiniCart"/> - <waitForPageLoad stepKey="waitForPageToLoad1"/> + <actionGroup ref="StorefrontClickOnMiniCartActionGroup" stepKey="clickOnMiniCart"/> <actionGroup ref="AssertStorefrontMiniCartItemsActionGroup" stepKey="assertSimpleProduct3MiniCart"> <argument name="productName" value="$$simpleProduct.name$$"/> <argument name="productPrice" value="$10.00"/> diff --git a/app/code/Magento/Checkout/Test/Mftf/Test/StorefrontGuestCheckoutTest.xml b/app/code/Magento/Checkout/Test/Mftf/Test/StorefrontGuestCheckoutTest.xml deleted file mode 100644 index a2007da317c00..0000000000000 --- a/app/code/Magento/Checkout/Test/Mftf/Test/StorefrontGuestCheckoutTest.xml +++ /dev/null @@ -1,155 +0,0 @@ -<?xml version="1.0" encoding="UTF-8"?> -<!-- - /** - * Copyright © Magento, Inc. All rights reserved. - * See COPYING.txt for license details. - */ ---> - -<tests xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" - xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/testSchema.xsd"> - <test name="StorefrontGuestCheckoutTest"> - <annotations> - <features value="Checkout"/> - <stories value="Checkout via Guest Checkout"/> - <title value="Guest Checkout - guest should be able to place an order"/> - <description value="Should be able to place an order as a Guest"/> - <severity value="CRITICAL"/> - <testCaseId value="MC-12825"/> - <group value="checkout"/> - </annotations> - <before> - <createData entity="_defaultCategory" stepKey="createCategory"/> - <createData entity="ApiSimpleProduct" stepKey="createProduct"> - <requiredEntity createDataKey="createCategory"/> - </createData> - - <!-- Perform reindex and flush cache --> - <magentoCLI command="indexer:reindex" stepKey="reindex"/> - <magentoCLI command="cache:flush" stepKey="flushCache"/> - </before> - <after> - <actionGroup ref="logout" stepKey="adminLogout"/> - <deleteData createDataKey="createCategory" stepKey="deleteCategory"/> - <deleteData createDataKey="createProduct" stepKey="deleteProduct"/> - </after> - - <amOnPage url="{{StorefrontCategoryPage.url($$createCategory.name$$)}}" stepKey="onCategoryPage"/> - <waitForPageLoad stepKey="waitForPageLoad1"/> - <moveMouseOver selector="{{StorefrontCategoryMainSection.ProductItemInfo}}" stepKey="hoverProduct"/> - <click selector="{{StorefrontCategoryMainSection.AddToCartBtn}}" stepKey="addToCart"/> - <waitForElementVisible selector="{{StorefrontCategoryMainSection.SuccessMsg}}" time="30" stepKey="waitForProductAdded"/> - <see selector="{{StorefrontCategoryMainSection.SuccessMsg}}" userInput="You added $$createProduct.name$$ to your shopping cart." stepKey="seeAddedToCartMessage"/> - <see selector="{{StorefrontMinicartSection.quantity}}" userInput="1" stepKey="seeCartQuantity"/> - <actionGroup ref="GoToCheckoutFromMinicartActionGroup" stepKey="guestGoToCheckoutFromMinicart" /> - <actionGroup ref="GuestCheckoutFillingShippingSectionActionGroup" stepKey="guestCheckoutFillingShippingSection"> - <argument name="customerVar" value="CustomerEntityOne" /> - <argument name="customerAddressVar" value="CustomerAddressSimple" /> - </actionGroup> - <actionGroup ref="CheckoutSelectCheckMoneyOrderPaymentActionGroup" stepKey="guestSelectCheckMoneyOrderPayment" /> - <actionGroup ref="CheckBillingAddressInCheckoutActionGroup" stepKey="guestSeeAddress"> - <argument name="customerVar" value="CustomerEntityOne" /> - <argument name="customerAddressVar" value="CustomerAddressSimple" /> - </actionGroup> - <actionGroup ref="CheckoutPlaceOrderActionGroup" stepKey="guestPlaceorder"> - <argument name="orderNumberMessage" value="CONST.successGuestCheckoutOrderNumberMessage" /> - <argument name="emailYouMessage" value="CONST.successCheckoutEmailYouMessage" /> - </actionGroup> - <grabTextFrom selector="{{CheckoutSuccessMainSection.orderNumber}}" stepKey="grabOrderNumber"/> - - <actionGroup ref="LoginAsAdmin" stepKey="loginAsAdmin1"/> - - <amOnPage url="{{AdminOrdersPage.url}}" stepKey="onOrdersPage"/> - <waitForLoadingMaskToDisappear stepKey="waitForLoadingMaskToDisappearOnOrdersPage"/> - <actionGroup ref="ClearFiltersAdminDataGridActionGroup" stepKey="clearGridFilter"/> - <fillField selector="{{AdminOrdersGridSection.search}}" userInput="{$grabOrderNumber}" stepKey="fillOrderNum"/> - <click selector="{{AdminOrdersGridSection.submitSearch}}" stepKey="submitSearchOrderNum"/> - <waitForLoadingMaskToDisappear stepKey="waitForLoadingMaskToDisappearOnSearch"/> - <click selector="{{AdminOrdersGridSection.firstRow}}" stepKey="clickOrderRow"/> - <waitForPageLoad stepKey="waitForOrderPageToLoad"/> - <see selector="{{AdminOrderDetailsInformationSection.orderStatus}}" userInput="Pending" stepKey="seeAdminOrderStatus"/> - <see selector="{{AdminOrderDetailsInformationSection.accountInformation}}" userInput="{{CustomerEntityOne.fullname}}" stepKey="seeAdminOrderGuest"/> - <see selector="{{AdminOrderDetailsInformationSection.accountInformation}}" userInput="{{CustomerEntityOne.email}}" stepKey="seeAdminOrderEmail"/> - <see selector="{{AdminOrderDetailsInformationSection.billingAddress}}" userInput="{{CustomerAddressSimple.street[0]}}" stepKey="seeAdminOrderBillingAddress"/> - <see selector="{{AdminOrderDetailsInformationSection.shippingAddress}}" userInput="{{CustomerAddressSimple.street[0]}}" stepKey="seeAdminOrderShippingAddress"/> - <see selector="{{AdminOrderDetailsInformationSection.itemsOrdered}}" userInput="$$createProduct.name$$" stepKey="seeAdminOrderProduct"/> - </test> - <test name="StorefrontGuestCheckoutWithSidebarDisabledTest" extends="StorefrontGuestCheckoutTest"> - <annotations> - <features value="Checkout"/> - <stories value="Checkout via Guest Checkout"/> - <title value="Guest Checkout when Cart sidebar disabled"/> - <description value="Should be able to place an order as a Guest when Cart sidebar is disabled"/> - <severity value="CRITICAL"/> - <testCaseId value="MC-16587"/> - <group value="checkout"/> - </annotations> - <before> - <magentoCLI stepKey="disableSidebar" command="config:set checkout/sidebar/display 0" /> - </before> - <after> - <magentoCLI stepKey="enableSidebar" command="config:set checkout/sidebar/display 1" /> - </after> - <remove keyForRemoval="guestGoToCheckoutFromMinicart" /> - <actionGroup ref="GoToCheckoutFromCartActionGroup" stepKey="guestGoToCheckoutFromCart" after="seeCartQuantity" /> - </test> - <test name="StorefrontGuestCheckoutTestWithRestrictedCountriesForPayment"> - <annotations> - <features value="Checkout"/> - <stories value="Checkout flow if payment solutions are not available"/> - <title value="Checkout via Guest Checkout with restricted countries for payment"/> - <description value="Should be able to place an order as a Guest with restricted countries for payment."/> - <severity value="CRITICAL"/> - <testCaseId value="MAGETWO-42653"/> - <group value="checkout"/> - </annotations> - <before> - <createData entity="_defaultCategory" stepKey="createCategory"/> - <createData entity="ApiSimpleProduct" stepKey="createProduct"> - <requiredEntity createDataKey="createCategory"/> - </createData> - <magentoCLI stepKey="allowSpecificValue" command="config:set payment/checkmo/allowspecific 1" /> - <magentoCLI stepKey="specificCountryValue" command="config:set payment/checkmo/specificcountry GB" /> - <magentoCLI command="indexer:reindex" stepKey="reindex"/> - <magentoCLI command="cache:flush" stepKey="flushCache"/> - </before> - <after> - <actionGroup ref="logout" stepKey="adminLogout"/> - <deleteData createDataKey="createCategory" stepKey="deleteCategory"/> - <deleteData createDataKey="createProduct" stepKey="deleteProduct"/> - <magentoCLI stepKey="allowSpecificValue" command="config:set payment/checkmo/allowspecific 0" /> - <magentoCLI stepKey="specificCountryValue" command="config:set payment/checkmo/specificcountry ''" /> - </after> - - <!-- Add product to cart --> - <amOnPage url="{{StorefrontCategoryPage.url($$createCategory.name$$)}}" stepKey="onCategoryPage"/> - <waitForPageLoad stepKey="waitForPageLoad1"/> - <moveMouseOver selector="{{StorefrontCategoryMainSection.ProductItemInfo}}" stepKey="hoverProduct"/> - <click selector="{{StorefrontCategoryMainSection.AddToCartBtn}}" stepKey="addToCart"/> - <waitForElementVisible selector="{{StorefrontCategoryMainSection.SuccessMsg}}" time="30" stepKey="waitForProductAdded"/> - <see selector="{{StorefrontCategoryMainSection.SuccessMsg}}" userInput="You added $$createProduct.name$$ to your shopping cart." stepKey="seeAddedToCartMessage"/> - <see selector="{{StorefrontMinicartSection.quantity}}" userInput="1" stepKey="seeCartQuantity"/> - - <!-- Go to checkout page --> - <actionGroup ref="GoToCheckoutFromMinicartActionGroup" stepKey="guestGoToCheckoutFromMinicart" /> - - <!-- Fill US Address and verify that no payment available --> - <actionGroup ref="GuestCheckoutWithSpecificCountryOptionForPaymentMethodActionGroup" stepKey="guestCheckoutFillingShippingSection"> - <argument name="customerVar" value="CustomerEntityOne" /> - <argument name="customerAddressVar" value="CustomerAddressSimple" /> - <argument name="paymentMethod" value="Check / Money order"/> - </actionGroup> - - <!-- Fill UK Address and verify that payment available and checkout successful --> - <click selector="{{CheckoutHeaderSection.shippingMethodStep}}" stepKey="goToShipping" /> - <actionGroup ref="GuestCheckoutFillingShippingSectionWithoutRegionActionGroup" stepKey="guestCheckoutFillingShippingSectionUK"> - <argument name="customerVar" value="CustomerEntityOne" /> - <argument name="customerAddressVar" value="UK_Not_Default_Address" /> - </actionGroup> - <actionGroup ref="CheckoutSelectCheckMoneyOrderPaymentActionGroup" stepKey="guestSelectCheckMoneyOrderPayment" /> - <actionGroup ref="CheckoutPlaceOrderActionGroup" stepKey="guestPlaceorder"> - <argument name="orderNumberMessage" value="CONST.successGuestCheckoutOrderNumberMessage" /> - <argument name="emailYouMessage" value="CONST.successCheckoutEmailYouMessage" /> - </actionGroup> - </test> -</tests> diff --git a/app/code/Magento/Checkout/Test/Mftf/Test/StorefrontGuestCheckoutTest/StorefrontGuestCheckoutTest.xml b/app/code/Magento/Checkout/Test/Mftf/Test/StorefrontGuestCheckoutTest/StorefrontGuestCheckoutTest.xml new file mode 100644 index 0000000000000..5c06f9ed55067 --- /dev/null +++ b/app/code/Magento/Checkout/Test/Mftf/Test/StorefrontGuestCheckoutTest/StorefrontGuestCheckoutTest.xml @@ -0,0 +1,76 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + /** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +--> +<tests xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/testSchema.xsd"> + <test name="StorefrontGuestCheckoutTest"> + <annotations> + <features value="Checkout"/> + <stories value="Checkout via Guest Checkout"/> + <title value="Guest Checkout - guest should be able to place an order"/> + <description value="Should be able to place an order as a Guest"/> + <severity value="BLOCKER"/> + <testCaseId value="MC-12825"/> + <group value="checkout"/> + </annotations> + <before> + <createData entity="_defaultCategory" stepKey="createCategory"/> + <createData entity="ApiSimpleProduct" stepKey="createProduct"> + <requiredEntity createDataKey="createCategory"/> + </createData> + + <!-- Perform reindex and flush cache --> + <magentoCLI command="indexer:reindex" stepKey="reindex"/> + <magentoCLI command="cache:flush" stepKey="flushCache"/> + </before> + <after> + <actionGroup ref="AdminLogoutActionGroup" stepKey="adminLogout"/> + <deleteData createDataKey="createCategory" stepKey="deleteCategory"/> + <deleteData createDataKey="createProduct" stepKey="deleteProduct"/> + </after> + + <amOnPage url="{{StorefrontCategoryPage.url($$createCategory.name$$)}}" stepKey="onCategoryPage"/> + <waitForPageLoad stepKey="waitForPageLoad1"/> + <moveMouseOver selector="{{StorefrontCategoryMainSection.ProductItemInfo}}" stepKey="hoverProduct"/> + <click selector="{{StorefrontCategoryMainSection.AddToCartBtn}}" stepKey="addToCart"/> + <waitForElementVisible selector="{{StorefrontCategoryMainSection.SuccessMsg}}" time="30" stepKey="waitForProductAdded"/> + <see selector="{{StorefrontCategoryMainSection.SuccessMsg}}" userInput="You added $$createProduct.name$$ to your shopping cart." stepKey="seeAddedToCartMessage"/> + <see selector="{{StorefrontMinicartSection.quantity}}" userInput="1" stepKey="seeCartQuantity"/> + <actionGroup ref="GoToCheckoutFromMinicartActionGroup" stepKey="guestGoToCheckoutFromMinicart"/> + <actionGroup ref="GuestCheckoutFillingShippingSectionActionGroup" stepKey="guestCheckoutFillingShippingSection"> + <argument name="customerVar" value="CustomerEntityOne"/> + <argument name="customerAddressVar" value="CustomerAddressSimple"/> + </actionGroup> + <actionGroup ref="CheckoutSelectCheckMoneyOrderPaymentActionGroup" stepKey="guestSelectCheckMoneyOrderPayment"/> + <actionGroup ref="CheckBillingAddressInCheckoutActionGroup" stepKey="guestSeeAddress"> + <argument name="customerVar" value="CustomerEntityOne"/> + <argument name="customerAddressVar" value="CustomerAddressSimple"/> + </actionGroup> + <actionGroup ref="CheckoutPlaceOrderActionGroup" stepKey="guestPlaceorder"> + <argument name="orderNumberMessage" value="CONST.successGuestCheckoutOrderNumberMessage"/> + <argument name="emailYouMessage" value="CONST.successCheckoutEmailYouMessage"/> + </actionGroup> + <grabTextFrom selector="{{CheckoutSuccessMainSection.orderNumber}}" stepKey="grabOrderNumber"/> + + <actionGroup ref="AdminLoginActionGroup" stepKey="loginAsAdmin1"/> + + <amOnPage url="{{AdminOrdersPage.url}}" stepKey="onOrdersPage"/> + <waitForLoadingMaskToDisappear stepKey="waitForLoadingMaskToDisappearOnOrdersPage"/> + <actionGroup ref="ClearFiltersAdminDataGridActionGroup" stepKey="clearGridFilter"/> + <fillField selector="{{AdminOrdersGridSection.search}}" userInput="{$grabOrderNumber}" stepKey="fillOrderNum"/> + <click selector="{{AdminOrdersGridSection.submitSearch}}" stepKey="submitSearchOrderNum"/> + <waitForLoadingMaskToDisappear stepKey="waitForLoadingMaskToDisappearOnSearch"/> + <click selector="{{AdminOrdersGridSection.firstRow}}" stepKey="clickOrderRow"/> + <waitForPageLoad stepKey="waitForOrderPageToLoad"/> + <see selector="{{AdminOrderDetailsInformationSection.orderStatus}}" userInput="Pending" stepKey="seeAdminOrderStatus"/> + <see selector="{{AdminOrderDetailsInformationSection.accountInformation}}" userInput="{{CustomerEntityOne.fullname}}" stepKey="seeAdminOrderGuest"/> + <see selector="{{AdminOrderDetailsInformationSection.accountInformation}}" userInput="{{CustomerEntityOne.email}}" stepKey="seeAdminOrderEmail"/> + <see selector="{{AdminOrderDetailsInformationSection.billingAddress}}" userInput="{{CustomerAddressSimple.street[0]}}" stepKey="seeAdminOrderBillingAddress"/> + <see selector="{{AdminOrderDetailsInformationSection.shippingAddress}}" userInput="{{CustomerAddressSimple.street[0]}}" stepKey="seeAdminOrderShippingAddress"/> + <see selector="{{AdminOrderDetailsInformationSection.itemsOrdered}}" userInput="$$createProduct.name$$" stepKey="seeAdminOrderProduct"/> + </test> +</tests> diff --git a/app/code/Magento/Checkout/Test/Mftf/Test/StorefrontGuestCheckoutTest/StorefrontGuestCheckoutTestWithRestrictedCountriesForPaymentTest.xml b/app/code/Magento/Checkout/Test/Mftf/Test/StorefrontGuestCheckoutTest/StorefrontGuestCheckoutTestWithRestrictedCountriesForPaymentTest.xml new file mode 100644 index 0000000000000..0520accdd4b84 --- /dev/null +++ b/app/code/Magento/Checkout/Test/Mftf/Test/StorefrontGuestCheckoutTest/StorefrontGuestCheckoutTestWithRestrictedCountriesForPaymentTest.xml @@ -0,0 +1,69 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + /** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +--> +<tests xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/testSchema.xsd"> + <test name="StorefrontGuestCheckoutTestWithRestrictedCountriesForPaymentTest"> + <annotations> + <features value="Checkout"/> + <stories value="Checkout flow if payment solutions are not available"/> + <title value="Checkout via Guest Checkout with restricted countries for payment"/> + <description value="Should be able to place an order as a Guest with restricted countries for payment."/> + <severity value="CRITICAL"/> + <testCaseId value="MAGETWO-42653"/> + <group value="checkout"/> + </annotations> + <before> + <createData entity="_defaultCategory" stepKey="createCategory"/> + <createData entity="ApiSimpleProduct" stepKey="createProduct"> + <requiredEntity createDataKey="createCategory"/> + </createData> + <magentoCLI stepKey="allowSpecificValue" command="config:set payment/checkmo/allowspecific 1"/> + <magentoCLI stepKey="specificCountryValue" command="config:set payment/checkmo/specificcountry GB"/> + <magentoCLI command="indexer:reindex" stepKey="reindex"/> + <magentoCLI command="cache:flush" stepKey="flushCache"/> + </before> + <after> + <actionGroup ref="AdminLogoutActionGroup" stepKey="adminLogout"/> + <deleteData createDataKey="createCategory" stepKey="deleteCategory"/> + <deleteData createDataKey="createProduct" stepKey="deleteProduct"/> + <magentoCLI stepKey="allowSpecificValue" command="config:set payment/checkmo/allowspecific 0"/> + <magentoCLI stepKey="specificCountryValue" command="config:set payment/checkmo/specificcountry ''"/> + </after> + + <!-- Add product to cart --> + <amOnPage url="{{StorefrontCategoryPage.url($$createCategory.name$$)}}" stepKey="onCategoryPage"/> + <waitForPageLoad stepKey="waitForPageLoad1"/> + <moveMouseOver selector="{{StorefrontCategoryMainSection.ProductItemInfo}}" stepKey="hoverProduct"/> + <click selector="{{StorefrontCategoryMainSection.AddToCartBtn}}" stepKey="addToCart"/> + <waitForElementVisible selector="{{StorefrontCategoryMainSection.SuccessMsg}}" time="30" stepKey="waitForProductAdded"/> + <see selector="{{StorefrontCategoryMainSection.SuccessMsg}}" userInput="You added $$createProduct.name$$ to your shopping cart." stepKey="seeAddedToCartMessage"/> + <see selector="{{StorefrontMinicartSection.quantity}}" userInput="1" stepKey="seeCartQuantity"/> + + <!-- Go to checkout page --> + <actionGroup ref="GoToCheckoutFromMinicartActionGroup" stepKey="guestGoToCheckoutFromMinicart"/> + + <!-- Fill US Address and verify that no payment available --> + <actionGroup ref="GuestCheckoutWithSpecificCountryOptionForPaymentMethodActionGroup" stepKey="guestCheckoutFillingShippingSection"> + <argument name="customerVar" value="CustomerEntityOne"/> + <argument name="customerAddressVar" value="CustomerAddressSimple"/> + <argument name="paymentMethod" value="Check / Money order"/> + </actionGroup> + + <!-- Fill UK Address and verify that payment available and checkout successful --> + <click selector="{{CheckoutHeaderSection.shippingMethodStep}}" stepKey="goToShipping"/> + <actionGroup ref="GuestCheckoutFillingShippingSectionWithoutRegionActionGroup" stepKey="guestCheckoutFillingShippingSectionUK"> + <argument name="customerVar" value="CustomerEntityOne"/> + <argument name="customerAddressVar" value="UK_Not_Default_Address"/> + </actionGroup> + <actionGroup ref="CheckoutSelectCheckMoneyOrderPaymentActionGroup" stepKey="guestSelectCheckMoneyOrderPayment"/> + <actionGroup ref="CheckoutPlaceOrderActionGroup" stepKey="guestPlaceorder"> + <argument name="orderNumberMessage" value="CONST.successGuestCheckoutOrderNumberMessage"/> + <argument name="emailYouMessage" value="CONST.successCheckoutEmailYouMessage"/> + </actionGroup> + </test> +</tests> diff --git a/app/code/Magento/Checkout/Test/Mftf/Test/StorefrontGuestCheckoutTest/StorefrontGuestCheckoutWithSidebarDisabledTest.xml b/app/code/Magento/Checkout/Test/Mftf/Test/StorefrontGuestCheckoutTest/StorefrontGuestCheckoutWithSidebarDisabledTest.xml new file mode 100644 index 0000000000000..74ad9985e72f2 --- /dev/null +++ b/app/code/Magento/Checkout/Test/Mftf/Test/StorefrontGuestCheckoutTest/StorefrontGuestCheckoutWithSidebarDisabledTest.xml @@ -0,0 +1,29 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + /** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +--> +<tests xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/testSchema.xsd"> + <test name="StorefrontGuestCheckoutWithSidebarDisabledTest" extends="StorefrontGuestCheckoutTest"> + <annotations> + <features value="Checkout"/> + <stories value="Checkout via Guest Checkout"/> + <title value="Guest Checkout when Cart sidebar disabled"/> + <description value="Should be able to place an order as a Guest when Cart sidebar is disabled"/> + <severity value="CRITICAL"/> + <testCaseId value="MC-16587"/> + <group value="checkout"/> + </annotations> + <before> + <magentoCLI stepKey="disableSidebar" command="config:set checkout/sidebar/display 0"/> + </before> + <after> + <magentoCLI stepKey="enableSidebar" command="config:set checkout/sidebar/display 1"/> + </after> + <remove keyForRemoval="guestGoToCheckoutFromMinicart"/> + <actionGroup ref="GoToCheckoutFromCartActionGroup" stepKey="guestGoToCheckoutFromCart" after="seeCartQuantity"/> + </test> +</tests> diff --git a/app/code/Magento/Checkout/Test/Mftf/Test/StorefrontGuestCheckoutUsingFreeShippingAndTaxesTest.xml b/app/code/Magento/Checkout/Test/Mftf/Test/StorefrontGuestCheckoutUsingFreeShippingAndTaxesTest.xml index b800ef758d65e..2787627b935c1 100644 --- a/app/code/Magento/Checkout/Test/Mftf/Test/StorefrontGuestCheckoutUsingFreeShippingAndTaxesTest.xml +++ b/app/code/Magento/Checkout/Test/Mftf/Test/StorefrontGuestCheckoutUsingFreeShippingAndTaxesTest.xml @@ -21,7 +21,7 @@ </annotations> <before> - <actionGroup ref="LoginAsAdmin" stepKey="loginToAdminPanel"/> + <actionGroup ref="AdminLoginActionGroup" stepKey="loginToAdminPanel"/> <createData entity="FlatRateShippingMethodConfig" stepKey="enableFlatRate"/> <createData entity="FreeShippingMethodsSettingConfig" stepKey="freeShippingMethodsSettingConfig"/> <createData entity="MinimumOrderAmount100" stepKey="minimumOrderAmount100"/> @@ -108,7 +108,10 @@ <deleteData createDataKey="createTaxRule" stepKey="deleteTaxRule1"/> <createData entity="DefaultShippingMethodsConfig" stepKey="defaultShippingMethodsConfig"/> <createData entity="DefaultMinimumOrderAmount" stepKey="defaultMinimumOrderAmount"/> - <actionGroup ref="logout" stepKey="logout"/> + <actionGroup ref="AdminLogoutActionGroup" stepKey="logout"/> + + <!-- Reindex invalidated indices after product attribute has been created/deleted --> + <magentoCron groups="index" stepKey="reindexInvalidatedIndices"/> </after> <!-- Create a Tax Rule --> diff --git a/app/code/Magento/Checkout/Test/Mftf/Test/StorefrontGuestCheckoutWithCouponAndZeroSubtotalTest.xml b/app/code/Magento/Checkout/Test/Mftf/Test/StorefrontGuestCheckoutWithCouponAndZeroSubtotalTest.xml index 97004ea91f5fd..b63766e0cd374 100644 --- a/app/code/Magento/Checkout/Test/Mftf/Test/StorefrontGuestCheckoutWithCouponAndZeroSubtotalTest.xml +++ b/app/code/Magento/Checkout/Test/Mftf/Test/StorefrontGuestCheckoutWithCouponAndZeroSubtotalTest.xml @@ -18,7 +18,7 @@ </annotations> <before> - <actionGroup ref="LoginAsAdmin" stepKey="loginToAdminPanel"/> + <actionGroup ref="AdminLoginActionGroup" stepKey="loginToAdminPanel"/> <createData entity="VirtualProduct" stepKey="virtualProduct"> <field key="price">50.00</field> </createData> @@ -27,7 +27,7 @@ <after> <deleteData createDataKey="virtualProduct" stepKey="deleteVirtualProduct"/> <deleteData createDataKey="createSalesRule" stepKey="deleteSalesRule"/> - <actionGroup ref="logout" stepKey="logout"/> + <actionGroup ref="AdminLogoutActionGroup" stepKey="logout"/> </after> <!--Open Product page in StoreFront and assert product and price range --> diff --git a/app/code/Magento/Checkout/Test/Mftf/Test/StorefrontNotApplicableShippingMethodInReviewAndPaymentStepTest.xml b/app/code/Magento/Checkout/Test/Mftf/Test/StorefrontNotApplicableShippingMethodInReviewAndPaymentStepTest.xml index 1a427bbe77166..d83550a82a87c 100644 --- a/app/code/Magento/Checkout/Test/Mftf/Test/StorefrontNotApplicableShippingMethodInReviewAndPaymentStepTest.xml +++ b/app/code/Magento/Checkout/Test/Mftf/Test/StorefrontNotApplicableShippingMethodInReviewAndPaymentStepTest.xml @@ -16,10 +16,7 @@ <severity value="MAJOR"/> <testCaseId value="MC-22625"/> <useCaseId value="MC-21926"/> - <group value="Checkout"/> - <skip> - <issueId value="MC-29597"/> - </skip> + <group value="checkout"/> </annotations> <before> @@ -34,21 +31,19 @@ <magentoCLI command="config:set {{AdminFedexEnableShowMethodConfigData.path}} {{AdminFedexEnableShowMethodConfigData.value}}" stepKey="enableShowMethod"/> <!--Set StoreInformation configs data--> - <magentoCLI command="config:set {{AdminGeneralSetStoreNameConfigData.path}} '{{AdminGeneralSetStoreNameConfigData.value}}'" stepKey="setStoreInformationName"/> - <magentoCLI command="config:set {{AdminGeneralSetStorePhoneConfigData.path}} {{DE_Address_Berlin_Not_Default_Address.telephone}}" stepKey="setStoreInformationPhone"/> - <magentoCLI command="config:set {{AdminGeneralSetCountryConfigData.path}} {{DE_Address_Berlin_Not_Default_Address.country_id}}" stepKey="setStoreInformationCountry"/> - <magentoCLI command="config:set {{AdminGeneralSetCityConfigData.path}} {{DE_Address_Berlin_Not_Default_Address.city}}" stepKey="setStoreInformationCity"/> - <magentoCLI command="config:set {{AdminGeneralSetPostcodeConfigData.path}} {{DE_Address_Berlin_Not_Default_Address.postcode}}" stepKey="setStoreInformationPostcode"/> - <magentoCLI command="config:set {{AdminGeneralSetStreetAddressConfigData.path}} '{{DE_Address_Berlin_Not_Default_Address.street[0]}}'" stepKey="setStoreInformationStreetAddress"/> - <magentoCLI command="config:set {{AdminGeneralSetStreetAddress2ConfigData.path}} '{{US_Address_California.street[0]}}'" stepKey="setStoreInformationStreetAddress2"/> - <magentoCLI command="config:set {{AdminGeneralSetVatNumberConfigData.path}} {{AdminGeneralSetVatNumberConfigData.value}}" stepKey="setStoreInformationVatNumber"/> + <actionGroup ref="AdminSetStoreInformationConfigurationActionGroup" stepKey="setStoreInformationConfigData"> + <argument name="telephone" value="{{DE_Address_Berlin_Not_Default_Address.telephone}}"/> + <argument name="country" value="{{DE_Address_Berlin_Not_Default_Address.country_id}}"/> + <argument name="state" value="{{DE_Address_Berlin_Not_Default_Address.state}}"/> + <argument name="city" value="{{DE_Address_Berlin_Not_Default_Address.city}}"/> + <argument name="postcode" value="{{DE_Address_Berlin_Not_Default_Address.postcode}}"/> + <argument name="street" value="{{DE_Address_Berlin_Not_Default_Address.street[0]}}"/> + </actionGroup> <!--Set Shipping settings origin data--> - <magentoCLI command="config:set {{AdminShippingSettingsOriginCountryConfigData.path}} {{DE_Address_Berlin_Not_Default_Address.country_id}}" stepKey="setOriginCountry"/> - <magentoCLI command="config:set {{AdminShippingSettingsOriginCityConfigData.path}} {{DE_Address_Berlin_Not_Default_Address.city}}" stepKey="setOriginCity"/> - <magentoCLI command="config:set {{AdminShippingSettingsOriginZipCodeConfigData.path}} {{DE_Address_Berlin_Not_Default_Address.postcode}}" stepKey="setOriginZipCode"/> - <magentoCLI command="config:set {{AdminShippingSettingsOriginStreetAddressConfigData.path}} '{{DE_Address_Berlin_Not_Default_Address.street[0]}}'" stepKey="setOriginStreetAddress"/> - <magentoCLI command="config:set {{AdminShippingSettingsOriginStreetAddress2ConfigData.path}} '{{US_Address_California.street[0]}}'" stepKey="setOriginStreetAddress2"/> + <actionGroup ref="AdminSetShippingOriginConfigurationActionGroup" stepKey="setShippingOriginConfigurationData"> + <argument name="address" value="DE_Address_Berlin_Not_Default_Address"/> + </actionGroup> <!-- Create Simple Product --> <createData entity="defaultSimpleProduct" stepKey="createSimpleProduct"> @@ -62,9 +57,6 @@ </createData> <!-- Create Customer with filled Shipping & Billing Address --> <createData entity="CustomerEntityOne" stepKey="createCustomer"/> - - <magentoCLI command="indexer:reindex" stepKey="reindex"/> - <magentoCLI command="cache:flush" stepKey="flushCache"/> </before> <after> @@ -79,19 +71,19 @@ <magentoCLI command="config:set {{AdminFedexDisableSandboxModeConfigData.path}} {{AdminFedexDisableSandboxModeConfigData.value}}" stepKey="disableSandbox"/> <magentoCLI command="config:set {{AdminFedexDisableDebugConfigData.path}} {{AdminFedexDisableDebugConfigData.value}}" stepKey="disableDebug"/> <magentoCLI command="config:set {{AdminFedexDisableShowMethodConfigData.path}} {{AdminFedexDisableShowMethodConfigData.value}}" stepKey="disableShowMethod"/> - <magentoCLI command="config:set {{AdminGeneralSetStoreNameConfigData.path}} ''" stepKey="setStoreInformationName"/> - <magentoCLI command="config:set {{AdminGeneralSetStorePhoneConfigData.path}} ''" stepKey="setStoreInformationPhone"/> - <magentoCLI command="config:set {{AdminGeneralSetCityConfigData.path}} ''" stepKey="setStoreInformationCity"/> - <magentoCLI command="config:set {{AdminGeneralSetPostcodeConfigData.path}} ''" stepKey="setStoreInformationPostcode"/> - <magentoCLI command="config:set {{AdminGeneralSetStreetAddressConfigData.path}} ''" stepKey="setStoreInformationStreetAddress"/> - <magentoCLI command="config:set {{AdminGeneralSetStreetAddress2ConfigData.path}} ''" stepKey="setStoreInformationStreetAddress2"/> - <magentoCLI command="config:set {{AdminGeneralSetVatNumberConfigData.path}} ''" stepKey="setStoreInformationVatNumber"/> - <magentoCLI command="config:set {{AdminShippingSettingsOriginCityConfigData.path}} ''" stepKey="setOriginCity"/> - <magentoCLI command="config:set {{AdminShippingSettingsOriginZipCodeConfigData.path}} ''" stepKey="setOriginZipCode"/> - <magentoCLI command="config:set {{AdminShippingSettingsOriginStreetAddressConfigData.path}} ''" stepKey="setOriginStreetAddress"/> - <magentoCLI command="config:set {{AdminShippingSettingsOriginStreetAddress2ConfigData.path}} ''" stepKey="setOriginStreetAddress2"/> - <magentoCLI command="indexer:reindex" stepKey="reindex"/> - <magentoCLI command="cache:flush" stepKey="flushCache"/> + <actionGroup ref="AdminResetShippingOriginConfigurationActionGroup" stepKey="resetShippingOriginConfig"/> + <actionGroup ref="AdminSetStoreInformationConfigurationActionGroup" stepKey="resetStoreInformationConfig"> + <argument name="storeName" value=""/> + <argument name="storeHoursOfOperation" value=""/> + <argument name="vatNumber" value=""/> + <argument name="telephone" value=""/> + <argument name="country" value=""/> + <argument name="state" value=""/> + <argument name="city" value=""/> + <argument name="postcode" value=""/> + <argument name="street" value=""/> + </actionGroup> + <actionGroup ref="AdminLogoutActionGroup" stepKey="logoutFromAdmin"/> </after> <!-- Guest Customer Test Scenario --> @@ -115,7 +107,7 @@ </actionGroup> <!-- Go to Order review --> - <actionGroup ref="StorefrontCheckoutForwardFromShippingStepActionGroup" stepKey="goToCheckoutReview"/> + <actionGroup ref="StorefrontCheckoutClickNextOnShippingStepActionGroup" stepKey="goToCheckoutReview"/> <!-- Checkout select Check/Money Order payment --> <actionGroup ref="CheckoutSelectCheckMoneyOrderPaymentActionGroup" stepKey="selectCheckMoneyPayment"/> @@ -132,7 +124,7 @@ <actionGroup ref="AssertStorefrontNotCalculatedValueInShippingTotalInOrderSummaryActionGroup" stepKey="assertNotYetCalculated"/> <!-- Assert order cannot be placed and error message will shown. --> - <actionGroup ref="AssertStorefrontOrderCannotBePlacedActionGroup" stepKey="assertOrderCannotBePlaced"> + <actionGroup ref="AssertStorefrontOrderIsNotPlacedActionGroup" stepKey="assertOrderCannotBePlaced"> <argument name="error" value="The shipping method is missing. Select the shipping method and try again."/> </actionGroup> @@ -145,7 +137,7 @@ </actionGroup> <!-- Go to Order review --> - <actionGroup ref="StorefrontCheckoutForwardFromShippingStepActionGroup" stepKey="goToCheckoutReview2"/> + <actionGroup ref="StorefrontCheckoutClickNextOnShippingStepActionGroup" stepKey="goToCheckoutOrderReview"/> <!-- Place order assert succeed --> <actionGroup ref="ClickPlaceOrderActionGroup" stepKey="checkoutPlaceOrder"/> @@ -157,53 +149,52 @@ </actionGroup> <!-- Add Simple Product to Cart --> - <actionGroup ref="StorefrontAddSimpleProductToShoppingCartActionGroup" stepKey="addProductToCart2"> + <actionGroup ref="StorefrontAddSimpleProductToShoppingCartActionGroup" stepKey="addSimpleProductToCart"> <argument name="product" value="$$createSimpleProduct$$"/> </actionGroup> <!-- Go to Checkout --> - <actionGroup ref="GoToCheckoutFromMinicartActionGroup" stepKey="goToCheckout2"/> - + <actionGroup ref="GoToCheckoutFromMinicartActionGroup" stepKey="proceedToCheckout"/> <!-- Select Free Shipping --> - <actionGroup ref="StorefrontSetShippingMethodActionGroup" stepKey="setShippingMethodFreeShipping2"> + <actionGroup ref="StorefrontSetShippingMethodActionGroup" stepKey="selectFreeShippingMethod"> <argument name="shippingMethodName" value="Free Shipping"/> </actionGroup> <!-- Go to Order review --> - <actionGroup ref="StorefrontCheckoutForwardFromShippingStepActionGroup" stepKey="goToCheckoutReview3"/> + <actionGroup ref="StorefrontCheckoutClickNextOnShippingStepActionGroup" stepKey="goToCheckoutPaymentPage"/> <!-- Checkout select Check/Money Order payment --> - <actionGroup ref="CheckoutSelectCheckMoneyOrderPaymentActionGroup" stepKey="selectCheckMoneyPayment2"/> + <actionGroup ref="CheckoutSelectCheckMoneyOrderPaymentActionGroup" stepKey="selectCheckMoneyPaymentMethod"/> <!-- Select payment solution --> - <checkOption selector="{{CheckoutPaymentSection.billingAddressNotSameCheckbox}}" stepKey="selectPaymentSolution2" /> - <waitForElement selector="{{CheckoutPaymentSection.placeOrder}}" time="30" stepKey="waitForPlaceOrderButton2"/> + <checkOption selector="{{CheckoutPaymentSection.billingAddressNotSameCheckbox}}" stepKey="checkBillingAddressNotSameCheckbox"/> + <waitForElementVisible selector="{{CheckoutPaymentSection.placeOrder}}" time="30" stepKey="waitForPlaceOrderButtonVisible"/> <!-- Apply Discount Coupon to the Order --> - <actionGroup ref="StorefrontApplyDiscountCodeActionGroup" stepKey="applyDiscountCoupon2"> + <actionGroup ref="StorefrontApplyDiscountCodeActionGroup" stepKey="applyDiscountCouponCode"> <argument name="discountCode" value="$createCartPriceRuleCoupon.code$"/> </actionGroup> <!-- Assert Shipping total is not yet calculated --> - <actionGroup ref="AssertStorefrontNotCalculatedValueInShippingTotalInOrderSummaryActionGroup" stepKey="assertNotYetCalculated2"/> + <actionGroup ref="AssertStorefrontNotCalculatedValueInShippingTotalInOrderSummaryActionGroup" stepKey="assertShippingTotalNotYetCalculated"/> <!-- Assert order cannot be placed and error message will shown. --> - <actionGroup ref="AssertStorefrontOrderCannotBePlacedActionGroup" stepKey="assertOrderCannotBePlaced2"> + <actionGroup ref="AssertStorefrontOrderIsNotPlacedActionGroup" stepKey="assertOrderIsNotPlaced"> <argument name="error" value="The shipping method is missing. Select the shipping method and try again."/> </actionGroup> <!-- Go to checkout page --> - <actionGroup ref="OpenStoreFrontCheckoutShippingPageActionGroup" stepKey="openCheckoutShippingPage2"/> + <actionGroup ref="OpenStoreFrontCheckoutShippingPageActionGroup" stepKey="goToCheckoutShippingPage"/> <!-- Chose flat rate --> - <actionGroup ref="StorefrontSetShippingMethodActionGroup" stepKey="setShippingMethodFlatRate2"> + <actionGroup ref="StorefrontSetShippingMethodActionGroup" stepKey="selectFlatRateShippingMethod"> <argument name="shippingMethodName" value="Flat Rate"/> </actionGroup> <!-- Go to Order review --> - <actionGroup ref="StorefrontCheckoutForwardFromShippingStepActionGroup" stepKey="goToCheckoutReview4"/> + <actionGroup ref="StorefrontCheckoutClickNextOnShippingStepActionGroup" stepKey="goToCheckoutPaymentStep"/> <!-- Place order assert succeed --> - <actionGroup ref="ClickPlaceOrderActionGroup" stepKey="checkoutPlaceOrder2"/> + <actionGroup ref="ClickPlaceOrderActionGroup" stepKey="placeOrder"/> </test> </tests> diff --git a/app/code/Magento/Checkout/Test/Mftf/Test/StorefrontOnePageCheckoutDataWhenChangeQtyTest.xml b/app/code/Magento/Checkout/Test/Mftf/Test/StorefrontOnePageCheckoutDataWhenChangeQtyTest.xml index ae7b8d2446380..9b536882dc64b 100644 --- a/app/code/Magento/Checkout/Test/Mftf/Test/StorefrontOnePageCheckoutDataWhenChangeQtyTest.xml +++ b/app/code/Magento/Checkout/Test/Mftf/Test/StorefrontOnePageCheckoutDataWhenChangeQtyTest.xml @@ -21,6 +21,8 @@ <before> <!--Create a product--> <createData entity="SimpleProduct2" stepKey="createProduct"/> + + <magentoCLI command="cron:run --group=index" stepKey="runCronIndexer"/> </before> <after> <!--Delete created data--> @@ -70,7 +72,10 @@ <click selector="{{CheckoutCartProductSection.updateShoppingCartButton}}" stepKey="clickUpdateShoppingCart"/> <waitForAjaxLoad stepKey="waitForAjaxLoad"/> <grabValueFrom selector="{{CheckoutCartProductSection.qty($$createProduct.name$$)}}" stepKey="grabQty"/> - <assertEquals expected="2" actual="$grabQty" stepKey="assertQty"/> + <assertEquals stepKey="assertQty"> + <actualResult type="const">$grabQty</actualResult> + <expectedResult type="const">2</expectedResult> + </assertEquals> <click selector="{{CheckoutCartSummarySection.proceedToCheckout}}" stepKey="clickProceedToCheckout"/> <!--Check that form is filled with customer data--> @@ -83,13 +88,37 @@ <grabValueFrom selector="{{CheckoutShippingSection.postcode}}" stepKey="grabPostcode1"/> <grabValueFrom selector="{{CheckoutShippingSection.telephone}}" stepKey="grabTelephone1"/> - <assertEquals expected="$grabEmail" actual="$grabEmail1" stepKey="assertEmail"/> - <assertEquals expected="$grabFirstName" actual="$grabFirstName1" stepKey="assertFirstName"/> - <assertEquals expected="$grabLastName" actual="$grabLastName1" stepKey="assertLastName"/> - <assertEquals expected="$grabStreet" actual="$grabStreet1" stepKey="assertStreet"/> - <assertEquals expected="$grabCity" actual="$grabCity1" stepKey="assertCity"/> - <assertEquals expected="$grabRegion" actual="$grabRegion1" stepKey="assertRegion"/> - <assertEquals expected="$grabPostcode" actual="$grabPostcode1" stepKey="assertPostcode"/> - <assertEquals expected="$grabTelephone" actual="$grabTelephone1" stepKey="assertTelephone"/> + <assertEquals stepKey="assertEmail"> + <actualResult type="const">$grabEmail1</actualResult> + <expectedResult type="const">$grabEmail</expectedResult> + </assertEquals> + <assertEquals stepKey="assertFirstName"> + <actualResult type="const">$grabFirstName1</actualResult> + <expectedResult type="const">$grabFirstName</expectedResult> + </assertEquals> + <assertEquals stepKey="assertLastName"> + <actualResult type="const">$grabLastName1</actualResult> + <expectedResult type="const">$grabLastName</expectedResult> + </assertEquals> + <assertEquals stepKey="assertStreet"> + <actualResult type="const">$grabStreet1</actualResult> + <expectedResult type="const">$grabStreet</expectedResult> + </assertEquals> + <assertEquals stepKey="assertCity"> + <actualResult type="const">$grabCity1</actualResult> + <expectedResult type="const">$grabCity</expectedResult> + </assertEquals> + <assertEquals stepKey="assertRegion"> + <actualResult type="const">$grabRegion1</actualResult> + <expectedResult type="const">$grabRegion</expectedResult> + </assertEquals> + <assertEquals stepKey="assertPostcode"> + <actualResult type="const">$grabPostcode1</actualResult> + <expectedResult type="const">$grabPostcode</expectedResult> + </assertEquals> + <assertEquals stepKey="assertTelephone"> + <actualResult type="const">$grabTelephone1</actualResult> + <expectedResult type="const">$grabTelephone</expectedResult> + </assertEquals> </test> </tests> diff --git a/app/code/Magento/Checkout/Test/Mftf/Test/StorefrontPersistentDataForGuestCustomerWithPhysicalQuoteTest.xml b/app/code/Magento/Checkout/Test/Mftf/Test/StorefrontPersistentDataForGuestCustomerWithPhysicalQuoteTest.xml index c106ec9c552ff..391cfd254101a 100644 --- a/app/code/Magento/Checkout/Test/Mftf/Test/StorefrontPersistentDataForGuestCustomerWithPhysicalQuoteTest.xml +++ b/app/code/Magento/Checkout/Test/Mftf/Test/StorefrontPersistentDataForGuestCustomerWithPhysicalQuoteTest.xml @@ -14,7 +14,7 @@ <stories value="Checkout via Guest Checkout"/> <title value="Persistent Data for Guest Customer with physical quote"/> <description value="One can use Persistent Data for Guest Customer with physical quote"/> - <severity value="MAJOR"/> + <severity value="BLOCKER"/> <testCaseId value="MC-13479"/> <group value="checkout"/> </annotations> diff --git a/app/code/Magento/Checkout/Test/Mftf/Test/StorefrontProductNameMinicartOnCheckoutPageDifferentStoreViewsTest.xml b/app/code/Magento/Checkout/Test/Mftf/Test/StorefrontProductNameMinicartOnCheckoutPageDifferentStoreViewsTest.xml index b678cb835f503..f666226233b6d 100644 --- a/app/code/Magento/Checkout/Test/Mftf/Test/StorefrontProductNameMinicartOnCheckoutPageDifferentStoreViewsTest.xml +++ b/app/code/Magento/Checkout/Test/Mftf/Test/StorefrontProductNameMinicartOnCheckoutPageDifferentStoreViewsTest.xml @@ -20,7 +20,7 @@ <group value="checkout"/> </annotations> <before> - <actionGroup ref="LoginAsAdmin" stepKey="loginAsAdmin"/> + <actionGroup ref="AdminLoginActionGroup" stepKey="loginAsAdmin"/> <!--Create a product--> <createData entity="SimpleProduct2" stepKey="createProduct"/> @@ -32,7 +32,7 @@ <argument name="customStore" value="customStore"/> </actionGroup> - <actionGroup ref="logout" stepKey="logout"/> + <actionGroup ref="AdminLogoutActionGroup" stepKey="logout"/> </after> <!-- Create store view --> @@ -41,7 +41,9 @@ </actionGroup> <!--Go to created product page--> - <amOnPage url="{{AdminProductEditPage.url($$createProduct.id$$)}}" stepKey="goToEditPage"/> + <actionGroup ref="AdminProductPageOpenByIdActionGroup" stepKey="goToEditPage"> + <argument name="productId" value="$$createProduct.id$$"/> + </actionGroup> <waitForPageLoad stepKey="waitForProductPage"/> <!--Switch to second store view and change the product name--> @@ -67,24 +69,42 @@ <waitForPageLoad stepKey="waitForStoreView"/> <!--Check product name in Minicart--> - <click selector="{{StorefrontMinicartSection.showCart}}" stepKey="clickCart"/> + <actionGroup ref="StorefrontClickOnMiniCartActionGroup" stepKey="clickCart"/> <grabTextFrom selector="{{StorefrontMinicartSection.productName}}" stepKey="grabProductNameMinicart"/> - <assertContains expected="$$createProduct.name$$" actual="$grabProductNameMinicart" stepKey="assertProductNameMinicart"/> - <assertContains expectedType="string" expected="-new" actual="$grabProductNameMinicart" stepKey="assertProductNameMinicart1"/> + <assertContains stepKey="assertProductNameMinicart"> + <actualResult type="const">$grabProductNameMinicart</actualResult> + <expectedResult type="const">$$createProduct.name$$</expectedResult> + </assertContains> + <assertContains stepKey="assertProductNameMinicart1"> + <actualResult type="const">$grabProductNameMinicart</actualResult> + <expectedResult type="string">-new</expectedResult> + </assertContains> <!--Check product name in Shopping Cart page--> <click selector="{{StorefrontMinicartSection.viewAndEditCart}}" stepKey="clickViewAndEdit"/> <waitForPageLoad stepKey="waitForShoppingCartPageLoad"/> <grabTextFrom selector="{{CheckoutCartProductSection.productName}}" stepKey="grabProductNameCart"/> - <assertContains expected="$$createProduct.name$$" actual="$grabProductNameCart" stepKey="assertProductNameCart"/> - <assertContains expectedType="string" expected="-new" actual="$grabProductNameCart" stepKey="assertProductNameCart1"/> + <assertContains stepKey="assertProductNameCart"> + <actualResult type="const">$grabProductNameCart</actualResult> + <expectedResult type="const">$$createProduct.name$$</expectedResult> + </assertContains> + <assertContains stepKey="assertProductNameCart1"> + <actualResult type="const">$grabProductNameCart</actualResult> + <expectedResult type="string">-new</expectedResult> + </assertContains> <!--Proceed to checkout and check product name in Order Summary area--> <click selector="{{CheckoutCartSummarySection.proceedToCheckout}}" stepKey="proceedToCheckout"/> <waitForPageLoad stepKey="waitForShippingPageLoad"/> <click selector="{{CheckoutShippingGuestInfoSection.itemInCart}}" stepKey="clickItemInCart"/> <grabTextFrom selector="{{CheckoutShippingGuestInfoSection.productName}}" stepKey="grabProductNameShipping"/> - <assertContains expected="$$createProduct.name$$" actual="$grabProductNameShipping" stepKey="assertProductNameShipping"/> - <assertContains expectedType="string" expected="-new" actual="$grabProductNameShipping" stepKey="assertProductNameShipping1"/> + <assertContains stepKey="assertProductNameShipping"> + <actualResult type="const">$grabProductNameShipping</actualResult> + <expectedResult type="const">$$createProduct.name$$</expectedResult> + </assertContains> + <assertContains stepKey="assertProductNameShipping1"> + <actualResult type="const">$grabProductNameShipping</actualResult> + <expectedResult type="string">-new</expectedResult> + </assertContains> </test> </tests> diff --git a/app/code/Magento/Checkout/Test/Mftf/Test/StorefrontProductQuantityChangesInBackendAfterCustomerCheckoutTest.xml b/app/code/Magento/Checkout/Test/Mftf/Test/StorefrontProductQuantityChangesInBackendAfterCustomerCheckoutTest.xml index e87aa31576595..146db30aa7c86 100644 --- a/app/code/Magento/Checkout/Test/Mftf/Test/StorefrontProductQuantityChangesInBackendAfterCustomerCheckoutTest.xml +++ b/app/code/Magento/Checkout/Test/Mftf/Test/StorefrontProductQuantityChangesInBackendAfterCustomerCheckoutTest.xml @@ -18,7 +18,7 @@ </annotations> <before> - <actionGroup ref="LoginAsAdmin" stepKey="loginToAdminPanel"/> + <actionGroup ref="AdminLoginActionGroup" stepKey="loginToAdminPanel"/> <magentoCLI command="config:set {{EnablePaymentBankTransferConfigData.path}} {{EnablePaymentBankTransferConfigData.value}}" stepKey="enableBankTransferPayment"/> <magentoCLI command="config:set {{EnableCatalogInventoryConfigData.path}} {{EnableCatalogInventoryConfigData.value}}" stepKey="enableCatalogInventoryStock"/> <createData entity="defaultSimpleProduct" stepKey="simpleProduct"> @@ -28,7 +28,7 @@ <after> <magentoCLI command="config:set {{DisablePaymentBankTransferConfigData.path}} {{DisablePaymentBankTransferConfigData.value}}" stepKey="enableGuestCheckout"/> <deleteData createDataKey="simpleProduct" stepKey="deleteProduct"/> - <actionGroup ref="logout" stepKey="logout"/> + <actionGroup ref="AdminLogoutActionGroup" stepKey="logout"/> </after> <!--Open Product page in StoreFront and assert product and price range --> diff --git a/app/code/Magento/Checkout/Test/Mftf/Test/StorefrontRefreshPageDuringGuestCheckoutTest.xml b/app/code/Magento/Checkout/Test/Mftf/Test/StorefrontRefreshPageDuringGuestCheckoutTest.xml index bb74726330b68..43b2262265841 100644 --- a/app/code/Magento/Checkout/Test/Mftf/Test/StorefrontRefreshPageDuringGuestCheckoutTest.xml +++ b/app/code/Magento/Checkout/Test/Mftf/Test/StorefrontRefreshPageDuringGuestCheckoutTest.xml @@ -20,7 +20,7 @@ </annotations> <before> <!-- Login as admin --> - <actionGroup ref="LoginAsAdmin" stepKey="loginAsAdmin"/> + <actionGroup ref="AdminLoginActionGroup" stepKey="loginAsAdmin"/> <!-- Create simple product --> <createData entity="SimpleProduct2" stepKey="createProduct"/> @@ -30,7 +30,7 @@ <deleteData createDataKey="createProduct" stepKey="deleteProduct"/> <!-- Logout admin --> - <actionGroup ref="logout" stepKey="logoutAsAdmin"/> + <actionGroup ref="AdminLogoutActionGroup" stepKey="logoutAsAdmin"/> </after> <!-- Add simple product to cart as Guest --> <amOnPage url="{{StorefrontProductPage.url($$createProduct.custom_attributes[url_key]$$)}}" stepKey="goToProductPage"/> diff --git a/app/code/Magento/Checkout/Test/Mftf/Test/StorefrontUKCustomerCheckoutWithCouponTest.xml b/app/code/Magento/Checkout/Test/Mftf/Test/StorefrontUKCustomerCheckoutWithCouponTest.xml index b53954709b2de..849b586594955 100644 --- a/app/code/Magento/Checkout/Test/Mftf/Test/StorefrontUKCustomerCheckoutWithCouponTest.xml +++ b/app/code/Magento/Checkout/Test/Mftf/Test/StorefrontUKCustomerCheckoutWithCouponTest.xml @@ -44,7 +44,7 @@ <deleteData createDataKey="createCustomer" stepKey="deleteCustomer"/> <deleteData createDataKey="createSalesRule" stepKey="deleteSalesRule"/> <actionGroup ref="AdminOrdersGridClearFiltersActionGroup" stepKey="clearOrderFilters"/> - <actionGroup ref="logout" stepKey="logout"/> + <actionGroup ref="AdminLogoutActionGroup" stepKey="logout"/> </after> <!-- Open Downloadable Product page --> @@ -109,7 +109,7 @@ <grabTextFrom selector="{{CheckoutSuccessMainSection.orderLink}}" stepKey="orderId"/> <!-- Login to Admin Page --> - <actionGroup ref="LoginAsAdmin" stepKey="loginToAdminPanel"/> + <actionGroup ref="AdminLoginActionGroup" stepKey="loginToAdminPanel"/> <!-- Open Order Index Page --> <amOnPage url="{{AdminOrdersPage.url}}" stepKey="goToOrders"/> diff --git a/app/code/Magento/Checkout/Test/Mftf/Test/StorefrontUKGuestCheckoutWithConditionProductQuantityEqualsToOrderedQuantityTest.xml b/app/code/Magento/Checkout/Test/Mftf/Test/StorefrontUKGuestCheckoutWithConditionProductQuantityEqualsToOrderedQuantityTest.xml index ba096d4a59615..ec99bb0e46722 100644 --- a/app/code/Magento/Checkout/Test/Mftf/Test/StorefrontUKGuestCheckoutWithConditionProductQuantityEqualsToOrderedQuantityTest.xml +++ b/app/code/Magento/Checkout/Test/Mftf/Test/StorefrontUKGuestCheckoutWithConditionProductQuantityEqualsToOrderedQuantityTest.xml @@ -18,14 +18,14 @@ </annotations> <before> - <actionGroup ref="LoginAsAdmin" stepKey="loginToAdminPanel"/> + <actionGroup ref="AdminLoginActionGroup" stepKey="loginToAdminPanel"/> <createData entity="defaultSimpleProduct" stepKey="simpleProduct"> <field key="price">10.00</field> </createData> </before> <after> <deleteData createDataKey="simpleProduct" stepKey="deleteProduct"/> - <actionGroup ref="logout" stepKey="logout"/> + <actionGroup ref="AdminLogoutActionGroup" stepKey="logout"/> </after> <!--Open Product page in StoreFront and assert product and price range --> diff --git a/app/code/Magento/Checkout/Test/Mftf/Test/StorefrontUSCustomerCheckoutWithCouponAndBankTransferPaymentMethodTest.xml b/app/code/Magento/Checkout/Test/Mftf/Test/StorefrontUSCustomerCheckoutWithCouponAndBankTransferPaymentMethodTest.xml index e67fd938c0a74..642814ded6e3e 100644 --- a/app/code/Magento/Checkout/Test/Mftf/Test/StorefrontUSCustomerCheckoutWithCouponAndBankTransferPaymentMethodTest.xml +++ b/app/code/Magento/Checkout/Test/Mftf/Test/StorefrontUSCustomerCheckoutWithCouponAndBankTransferPaymentMethodTest.xml @@ -18,7 +18,7 @@ </annotations> <before> - <actionGroup ref="LoginAsAdmin" stepKey="loginToAdminPanel"/> + <actionGroup ref="AdminLoginActionGroup" stepKey="loginToAdminPanel"/> <magentoCLI command="config:set {{EnablePaymentBankTransferConfigData.path}} {{EnablePaymentBankTransferConfigData.value}}" stepKey="enableBankTransferPayment"/> <createData entity="SimpleProduct2" stepKey="simpleProduct"> <field key="price">50.00</field> @@ -32,7 +32,7 @@ <magentoCLI command="config:set {{DisablePaymentBankTransferConfigData.path}} {{DisablePaymentBankTransferConfigData.value}}" stepKey="disableBankTransferPayment"/> <deleteData createDataKey="simpleProduct" stepKey="deleteProduct"/> <deleteData createDataKey="createSalesRule" stepKey="deleteSalesRule"/> - <actionGroup ref="logout" stepKey="logout"/> + <actionGroup ref="AdminLogoutActionGroup" stepKey="logout"/> </after> <!-- Open Product page in StoreFront and assert product and price range --> diff --git a/app/code/Magento/Checkout/Test/Mftf/Test/StorefrontUpdatePriceInShoppingCartAfterProductSaveTest.xml b/app/code/Magento/Checkout/Test/Mftf/Test/StorefrontUpdatePriceInShoppingCartAfterProductSaveTest.xml index 4bff22950174f..12e2820821c87 100644 --- a/app/code/Magento/Checkout/Test/Mftf/Test/StorefrontUpdatePriceInShoppingCartAfterProductSaveTest.xml +++ b/app/code/Magento/Checkout/Test/Mftf/Test/StorefrontUpdatePriceInShoppingCartAfterProductSaveTest.xml @@ -14,7 +14,7 @@ <stories value="Checkout via the Storefront"/> <title value="Update price in shopping cart after product save"/> <description value="Price in shopping cart should be updated after product save with changed price"/> - <severity value="CRITICAL"/> + <severity value="BLOCKER"/> <testCaseId value="MAGETWO-58179"/> <group value="checkout"/> </annotations> @@ -22,7 +22,7 @@ <createData entity="SimpleProduct2" stepKey="createSimpleProduct"> <field key="price">100</field> </createData> - <actionGroup ref="LoginAsAdmin" stepKey="loginAsAdmin"/> + <actionGroup ref="AdminLoginActionGroup" stepKey="loginAsAdmin"/> <actionGroup ref="SetCustomerDataLifetimeActionGroup" stepKey="setCustomerDataLifetime"> <argument name="minutes" value="1"/> </actionGroup> @@ -31,7 +31,7 @@ <deleteData createDataKey="createSimpleProduct" stepKey="deleteProduct"/> <actionGroup ref="SetCustomerDataLifetimeActionGroup" stepKey="setDefaultCustomerDataLifetime"/> <magentoCLI command="indexer:reindex customer_grid" stepKey="reindexCustomerGrid"/> - <actionGroup ref="logout" stepKey="logout"/> + <actionGroup ref="AdminLogoutActionGroup" stepKey="logout"/> </after> <!--Go to product page--> <amOnPage url="{{StorefrontProductPage.url($$createSimpleProduct.custom_attributes[url_key]$$)}}" stepKey="navigateToSimpleProductPage"/> @@ -52,7 +52,9 @@ <!--Edit product price via admin panel--> <openNewTab stepKey="openNewTab"/> - <amOnPage url="{{AdminProductEditPage.url($$createSimpleProduct.id$$)}}" stepKey="goToProductEditPage"/> + <actionGroup ref="AdminProductPageOpenByIdActionGroup" stepKey="goToProductEditPage"> + <argument name="productId" value="$$createSimpleProduct.id$$"/> + </actionGroup> <fillField userInput="120" selector="{{AdminProductFormSection.productPrice}}" stepKey="setNewPrice"/> <actionGroup ref="SaveProductFormActionGroup" stepKey="saveProduct"/> <closeTab stepKey="closeTab"/> diff --git a/app/code/Magento/Checkout/Test/Mftf/Test/StorefrontUpdateShoppingCartSimpleProductQtyTest.xml b/app/code/Magento/Checkout/Test/Mftf/Test/StorefrontUpdateShoppingCartSimpleProductQtyTest.xml index d0d75317531b7..d166bfdac0ba4 100644 --- a/app/code/Magento/Checkout/Test/Mftf/Test/StorefrontUpdateShoppingCartSimpleProductQtyTest.xml +++ b/app/code/Magento/Checkout/Test/Mftf/Test/StorefrontUpdateShoppingCartSimpleProductQtyTest.xml @@ -15,7 +15,7 @@ <title value="Check updating shopping cart while updating items qty"/> <description value="Check updating shopping cart while updating items qty"/> <testCaseId value="MC-14731" /> - <severity value="AVERAGE"/> + <severity value="BLOCKER"/> <group value="shoppingCart"/> <group value="mtf_migrated"/> </annotations> diff --git a/app/code/Magento/Checkout/Test/Mftf/Test/StorefrontUpdateShoppingCartSimpleWithCustomOptionsProductQtyTest.xml b/app/code/Magento/Checkout/Test/Mftf/Test/StorefrontUpdateShoppingCartSimpleWithCustomOptionsProductQtyTest.xml index 0b52b08980ded..91a601dc6ef52 100644 --- a/app/code/Magento/Checkout/Test/Mftf/Test/StorefrontUpdateShoppingCartSimpleWithCustomOptionsProductQtyTest.xml +++ b/app/code/Magento/Checkout/Test/Mftf/Test/StorefrontUpdateShoppingCartSimpleWithCustomOptionsProductQtyTest.xml @@ -15,7 +15,7 @@ <title value="Check updating shopping cart while updating qty of items with custom options"/> <description value="Check updating shopping cart while updating qty of items with custom options"/> <testCaseId value="MC-14732" /> - <severity value="AVERAGE"/> + <severity value="BLOCKER"/> <group value="shoppingCart"/> <group value="mtf_migrated"/> </annotations> diff --git a/app/code/Magento/Checkout/Test/Mftf/Test/StorefrontVerifySecureURLRedirectCheckoutTest.xml b/app/code/Magento/Checkout/Test/Mftf/Test/StorefrontVerifySecureURLRedirectCheckoutTest.xml index cbf0072d44aed..778967c187f65 100644 --- a/app/code/Magento/Checkout/Test/Mftf/Test/StorefrontVerifySecureURLRedirectCheckoutTest.xml +++ b/app/code/Magento/Checkout/Test/Mftf/Test/StorefrontVerifySecureURLRedirectCheckoutTest.xml @@ -8,7 +8,7 @@ <tests xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/testSchema.xsd"> - <test name="StorefrontVerifySecureURLRedirectCheckout"> + <test name="StorefrontVerifySecureURLRedirectCheckoutTest"> <annotations> <features value="Checkout"/> <stories value="Storefront Secure URLs"/> diff --git a/app/code/Magento/Checkout/Test/Mftf/Test/ZeroSubtotalOrdersWithProcessingStatusTest.xml b/app/code/Magento/Checkout/Test/Mftf/Test/ZeroSubtotalOrdersWithProcessingStatusTest.xml index 0f0b98912de30..43d20e0d676ea 100644 --- a/app/code/Magento/Checkout/Test/Mftf/Test/ZeroSubtotalOrdersWithProcessingStatusTest.xml +++ b/app/code/Magento/Checkout/Test/Mftf/Test/ZeroSubtotalOrdersWithProcessingStatusTest.xml @@ -26,7 +26,7 @@ <createData entity="PaymentMethodsSettingConfig" stepKey="paymentMethodsSettingConfig"/> <createData entity="FreeShippingMethodsSettingConfig" stepKey="freeShippingMethodsSettingConfig"/> <!--Go to Admin page--> - <actionGroup ref="LoginAsAdmin" stepKey="loginAsAdmin"/> + <actionGroup ref="AdminLoginActionGroup" stepKey="loginAsAdmin"/> </before> <after> @@ -36,7 +36,7 @@ <createData entity="DefaultShippingMethodsConfig" stepKey="defaultShippingMethodsConfig"/> <createData entity="DisableFreeShippingConfig" stepKey="disableFreeShippingConfig"/> <createData entity="DisablePaymentMethodsSettingConfig" stepKey="disablePaymentMethodsSettingConfig"/> - <actionGroup ref="logout" stepKey="logout"/> + <actionGroup ref="AdminLogoutActionGroup" stepKey="logout"/> <deleteData createDataKey="simpleproduct" stepKey="deleteProduct"/> <deleteData createDataKey="simplecategory" stepKey="deleteCategory"/> </after> @@ -68,7 +68,7 @@ </actionGroup> <!--Proceed to shipment--> - <click selector="{{StorefrontMinicartSection.showCart}}" stepKey="clickToOpenCard"/> + <actionGroup ref="StorefrontClickOnMiniCartActionGroup" stepKey="clickToOpenCard"/> <click selector="{{StorefrontMinicartSection.goToCheckout}}" stepKey="clickToProceedToCheckout"/> <waitForPageLoad stepKey="waitForTheFormIsOpened"/> diff --git a/app/code/Magento/Checkout/Test/Unit/Block/Cart/ShippingTest.php b/app/code/Magento/Checkout/Test/Unit/Block/Cart/ShippingTest.php index 302188224b97a..5ab4615c52828 100644 --- a/app/code/Magento/Checkout/Test/Unit/Block/Cart/ShippingTest.php +++ b/app/code/Magento/Checkout/Test/Unit/Block/Cart/ShippingTest.php @@ -3,44 +3,79 @@ * Copyright © Magento, Inc. All rights reserved. * See COPYING.txt for license details. */ + +declare(strict_types=1); + namespace Magento\Checkout\Test\Unit\Block\Cart; -class ShippingTest extends \PHPUnit\Framework\TestCase +use Magento\Checkout\Block\Cart\Shipping; +use Magento\Checkout\Model\CompositeConfigProvider; +use Magento\Checkout\Block\Checkout\LayoutProcessorInterface; +use Magento\Framework\Serialize\Serializer\Json; +use Magento\Framework\Serialize\Serializer\JsonHexTag; +use Magento\Framework\View\Element\Template\Context; +use Magento\Customer\Model\Session as CustomerSession; +use Magento\Checkout\Model\Session as CheckoutSession; +use Magento\Store\Model\StoreManagerInterface; +use Magento\Store\Model\Store; +use Magento\Framework\TestFramework\Unit\Helper\ObjectManager; +use PHPUnit\Framework\MockObject\MockObject; +use PHPUnit\Framework\TestCase; + +/** + * Unit Test for Magento\Checkout\Block\Cart\Shipping + */ +class ShippingTest extends TestCase { + /** - * @var \Magento\Checkout\Block\Cart\Shipping + * Stub Preinitialized Componets */ - protected $model; + private const STUB_PREINITIALIZED_COMPONENTS = [ + 'components' => [ + 'firstComponent' => ['param' => 'value'] + ] + ]; /** - * @var \PHPUnit_Framework_MockObject_MockObject + * Stub Base URL */ - protected $context; + private const STUB_BASE_URL = 'baseurl'; /** - * @var \PHPUnit_Framework_MockObject_MockObject + * @var Shipping */ - protected $customerSession; + protected $block; /** - * @var \PHPUnit_Framework_MockObject_MockObject + * @var Context|MockObject */ - protected $checkoutSession; + protected $contextMock; /** - * @var \PHPUnit_Framework_MockObject_MockObject + * @var CustomerSession|MockObject */ - protected $configProvider; + protected $customerSessionMock; /** - * @var \PHPUnit_Framework_MockObject_MockObject + * @var CheckoutSession|MockObject */ - protected $layoutProcessor; + protected $checkoutSessionMock; /** - * @var \PHPUnit_Framework_MockObject_MockObject + * @var CompositeConfigProvider|MockObject */ - protected $storeManager; + protected $configProviderMock; + + /** + * @var LayoutProcessorInterface|MockObject + */ + protected $layoutProcessorMock; + + /** + * @var StoreManagerInterface|MockObject + */ + protected $storeManagerInterfaceMock; /** * @var array @@ -48,77 +83,147 @@ class ShippingTest extends \PHPUnit\Framework\TestCase protected $layout; /** - * @var \PHPUnit_Framework_MockObject_MockObject + * @var Json|MockObject */ - private $serializer; + private $serializerMock; - protected function setUp() + /** + * @var JsonHexTag|MockObject + */ + private $jsonHexTagSerializerMock; + + /** + * @inheritDoc + */ + protected function setUp(): void { - $this->context = $this->createMock(\Magento\Framework\View\Element\Template\Context::class); - $this->customerSession = $this->createMock(\Magento\Customer\Model\Session::class); - $this->checkoutSession = $this->createMock(\Magento\Checkout\Model\Session::class); - $this->configProvider = $this->createMock(\Magento\Checkout\Model\CompositeConfigProvider::class); - $this->layoutProcessor = $this->createMock(\Magento\Checkout\Block\Checkout\LayoutProcessorInterface::class); - $this->layout = [ - 'components' => [ - 'firstComponent' => ['param' => 'value'], - 'secondComponent' => ['param' => 'value'], + $this->contextMock = $this->createMock(Context::class); + $this->customerSessionMock = $this->createMock(CustomerSession::class); + $this->checkoutSessionMock = $this->createMock(CheckoutSession::class); + $this->configProviderMock = $this->createMock(CompositeConfigProvider::class); + $this->layoutProcessorMock = $this->createMock(LayoutProcessorInterface::class); + $this->serializerMock = $this->createMock(JsonHexTag::class); + $this->jsonHexTagSerializerMock = $this->createMock(JsonHexTag::class); + $this->storeManagerInterfaceMock = $this->createMock(StoreManagerInterface::class); + $this->layout = self::STUB_PREINITIALIZED_COMPONENTS; + + $objectManager = new ObjectManager($this); + $this->block = $objectManager->getObject( + Shipping::class, + [ + 'configProvider' => $this->configProviderMock, + 'layoutProcessors' => [$this->layoutProcessorMock], + 'jsLayout' => $this->layout, + 'serializer' => $this->serializerMock, + 'jsonHexTagSerializer' => $this->jsonHexTagSerializerMock, + 'storeManager' => $this->storeManagerInterfaceMock ] - ]; - - $this->storeManager = $this->createMock(\Magento\Store\Model\StoreManagerInterface::class); - $this->context->expects($this->once())->method('getStoreManager')->willReturn($this->storeManager); - $this->serializer = $this->createMock(\Magento\Framework\Serialize\Serializer\Json::class); - - $this->model = new \Magento\Checkout\Block\Cart\Shipping( - $this->context, - $this->customerSession, - $this->checkoutSession, - $this->configProvider, - [$this->layoutProcessor], - ['jsLayout' => $this->layout], - $this->serializer ); } - public function testGetCheckoutConfig() + /** + * Test for getCheckoutConfig + * + * @return void + */ + public function testGetCheckoutConfig(): void { $config = ['param' => 'value']; - $this->configProvider->expects($this->once())->method('getConfig')->willReturn($config); - $this->assertEquals($config, $this->model->getCheckoutConfig()); + $this->configProviderMock->expects($this->once()) + ->method('getConfig') + ->willReturn($config); + + $this->assertEquals($config, $this->block->getCheckoutConfig()); } - public function testGetJsLayout() + /** + * Test for getJsLayout() + * + * @return void + * @dataProvider getJsLayoutDataProvider + */ + public function testGetJsLayout(array $layoutProcessed, string $jsonLayoutProcessed): void { - $layoutProcessed = $this->layout; - $layoutProcessed['components']['thirdComponent'] = ['param' => 'value']; - $jsonLayoutProcessed = json_encode($layoutProcessed); - - $this->layoutProcessor->expects($this->once()) + $this->layoutProcessorMock->expects($this->once()) ->method('process') ->with($this->layout) ->willReturn($layoutProcessed); - $this->assertEquals( - $jsonLayoutProcessed, - $this->model->getJsLayout() - ); + $this->jsonHexTagSerializerMock->expects($this->once()) + ->method('serialize') + ->willReturn($jsonLayoutProcessed); + + $this->assertEquals($jsonLayoutProcessed, $this->block->getJsLayout()); } - public function testGetBaseUrl() + /** + * Data for getJsLayout() + * + * @return array + */ + public function getJsLayoutDataProvider(): array { - $baseUrl = 'baseUrl'; - $storeMock = $this->createPartialMock(\Magento\Store\Model\Store::class, ['getBaseUrl']); - $storeMock->expects($this->once())->method('getBaseUrl')->willReturn($baseUrl); - $this->storeManager->expects($this->once())->method('getStore')->willReturn($storeMock); - $this->assertEquals($baseUrl, $this->model->getBaseUrl()); + $layoutProcessed = $this->layout; + $layoutProcessed['components']['secondComponent'] = ['param' => 'value']; + return [ + [ + $layoutProcessed, + '{"components":{"firstComponent":{"param":"value"},"secondComponent":{"param":"value"}}}' + ] + ]; } - public function testGetSerializedCheckoutConfig() + /** + * Test for getBaseUrl() + * + * @return void + */ + public function testGetBaseUrl(): void { - $checkoutConfig = ['checkout', 'config']; - $this->configProvider->expects($this->once())->method('getConfig')->willReturn($checkoutConfig); + $baseUrl = self::STUB_BASE_URL; + $storeMock = $this->createPartialMock(Store::class, ['getBaseUrl']); + $storeMock->expects($this->once()) + ->method('getBaseUrl') + ->willReturn($baseUrl); + + $this->storeManagerInterfaceMock->expects($this->once()) + ->method('getStore') + ->willReturn($storeMock); + + $this->assertEquals($baseUrl, $this->block->getBaseUrl()); + } - $this->assertEquals(json_encode($checkoutConfig), $this->model->getSerializedCheckoutConfig()); + /** + * Test for getSerializedCheckoutConfig() + * + * @return void + * @dataProvider jsonEncodeDataProvider + */ + public function testGetSerializedCheckoutConfig(array $checkoutConfig, string $expectedJson): void + { + $this->configProviderMock->expects($this->once()) + ->method('getConfig') + ->willReturn($checkoutConfig); + + $this->jsonHexTagSerializerMock->expects($this->once()) + ->method('serialize') + ->willReturn($expectedJson); + + $this->assertEquals($expectedJson, $this->block->getSerializedCheckoutConfig()); + } + + /** + * Data for getSerializedCheckoutConfig() + * + * @return array + */ + public function jsonEncodeDataProvider(): array + { + return [ + [ + ['checkout', 'config'], + '["checkout","config"]' + ] + ]; } } diff --git a/app/code/Magento/Checkout/Test/Unit/Block/Cart/SidebarTest.php b/app/code/Magento/Checkout/Test/Unit/Block/Cart/SidebarTest.php index f69ced3b094c7..fdf63b4ebe1ed 100644 --- a/app/code/Magento/Checkout/Test/Unit/Block/Cart/SidebarTest.php +++ b/app/code/Magento/Checkout/Test/Unit/Block/Cart/SidebarTest.php @@ -145,7 +145,8 @@ public function testGetConfig() 'minicartMaxItemsVisible' => 3, 'websiteId' => 100, 'maxItemsToDisplay' => 8, - 'storeId' => null + 'storeId' => null, + 'storeGroupId' => null ]; $valueMap = [ diff --git a/app/code/Magento/Checkout/Test/Unit/Block/Checkout/LayoutProcessorTest.php b/app/code/Magento/Checkout/Test/Unit/Block/Checkout/LayoutProcessorTest.php index 31ca2a2033012..9fff4b622e596 100644 --- a/app/code/Magento/Checkout/Test/Unit/Block/Checkout/LayoutProcessorTest.php +++ b/app/code/Magento/Checkout/Test/Unit/Block/Checkout/LayoutProcessorTest.php @@ -10,14 +10,16 @@ use Magento\Checkout\Helper\Data; use Magento\Customer\Model\AttributeMetadataDataProvider; use Magento\Customer\Model\Options; - +use Magento\Shipping\Model\Config; +use Magento\Store\Model\StoreManagerInterface; use Magento\Ui\Component\Form\AttributeMapper; -use PHPUnit_Framework_MockObject_MockObject as MockObject; +use PHPUnit\Framework\MockObject\MockObject; +use PHPUnit\Framework\TestCase; /** * LayoutProcessorTest covers a list of variations for checkout layout processor */ -class LayoutProcessorTest extends \PHPUnit\Framework\TestCase +class LayoutProcessorTest extends TestCase { /** * @var AttributeMetadataDataProvider|MockObject @@ -45,7 +47,7 @@ class LayoutProcessorTest extends \PHPUnit\Framework\TestCase private $layoutProcessor; /** - * @var MockObject + * @var StoreManagerInterface|MockObject */ private $storeManager; @@ -75,11 +77,11 @@ protected function setUp() ->disableOriginalConstructor() ->getMock(); - $shippingConfig = $this->getMockBuilder(\Magento\Shipping\Model\Config::class) + $shippingConfig = $this->getMockBuilder(Config::class) ->disableOriginalConstructor() ->getMock(); - $this->storeManager = $this->createMock(\Magento\Store\Model\StoreManagerInterface::class); + $this->storeManager = $this->createMock(StoreManagerInterface::class); $this->layoutProcessor = new LayoutProcessor( $this->attributeDataProvider, @@ -109,10 +111,12 @@ public function testProcess() $this->attributeMerger->expects(static::exactly(2)) ->method('merge') - ->willReturnMap([ - ['payment1_1' => $this->getBillingComponent('payment1_1')], - ['payment2_1' => $this->getBillingComponent('payment2_1')], - ]); + ->willReturnMap( + [ + ['payment1_1' => $this->getBillingComponent('payment1_1')], + ['payment2_1' => $this->getBillingComponent('payment2_1')], + ] + ); $actual = $this->layoutProcessor->process($jsLayout); @@ -236,9 +240,6 @@ private function getLayoutData() private function getBillingComponent($paymentCode) { return [ - 'country_id' => [ - 'sortOrder' => 115, - ], 'region' => [ 'visible' => false, ], diff --git a/app/code/Magento/Checkout/Test/Unit/Controller/Sidebar/RemoveItemTest.php b/app/code/Magento/Checkout/Test/Unit/Controller/Sidebar/RemoveItemTest.php index 7653a51b2f9b7..c7dacbb1fe307 100644 --- a/app/code/Magento/Checkout/Test/Unit/Controller/Sidebar/RemoveItemTest.php +++ b/app/code/Magento/Checkout/Test/Unit/Controller/Sidebar/RemoveItemTest.php @@ -3,88 +3,105 @@ * Copyright © Magento, Inc. All rights reserved. * See COPYING.txt for license details. */ +declare(strict_types=1); + namespace Magento\Checkout\Test\Unit\Controller\Sidebar; +use Magento\Checkout\Controller\Sidebar\RemoveItem; +use Magento\Checkout\Model\Sidebar; +use Magento\Framework\App\RequestInterface; +use Magento\Framework\Controller\Result\Json as ResultJson; +use Magento\Framework\Controller\Result\JsonFactory as ResultJsonFactory; +use Magento\Framework\Controller\Result\Redirect as ResultRedirect; +use Magento\Framework\Controller\Result\RedirectFactory as ResultRedirectFactory; +use Magento\Framework\Data\Form\FormKey\Validator; use Magento\Framework\Exception\LocalizedException; -use Magento\Framework\TestFramework\Unit\Helper\ObjectManager as ObjectManagerHelper; +use Magento\Framework\TestFramework\Unit\Helper\ObjectManager; +use PHPUnit\Framework\MockObject\MockObject; +use PHPUnit\Framework\TestCase; +use Psr\Log\LoggerInterface; /** * @SuppressWarnings(PHPMD.CouplingBetweenObjects) */ -class RemoveItemTest extends \PHPUnit\Framework\TestCase +class RemoveItemTest extends TestCase { - /** @var \Magento\Checkout\Controller\Sidebar\RemoveItem */ - protected $removeItem; - - /** @var ObjectManagerHelper */ - protected $objectManagerHelper; - - /** @var \Magento\Checkout\Model\Sidebar|\PHPUnit_Framework_MockObject_MockObject */ - protected $sidebarMock; + /** + * @var RemoveItem + */ + private $action; - /** @var \Psr\Log\LoggerInterface|\PHPUnit_Framework_MockObject_MockObject */ - protected $loggerMock; + /** + * @var RequestInterface|MockObject + */ + private $requestMock; - /** @var \Magento\Framework\Json\Helper\Data|\PHPUnit_Framework_MockObject_MockObject */ - protected $jsonHelperMock; + /** + * @var ResultJsonFactory|MockObject + */ + private $resultJsonFactoryMock; - /** @var \Magento\Framework\App\RequestInterface|\PHPUnit_Framework_MockObject_MockObject */ - protected $requestMock; + /** + * @var ResultRedirectFactory|MockObject + */ + private $resultRedirectFactoryMock; - /** @var \Magento\Framework\App\ResponseInterface|\PHPUnit_Framework_MockObject_MockObject */ - protected $responseMock; + /** + * @var Sidebar|MockObject + */ + private $sidebarMock; - /** @var \Magento\Framework\View\Result\PageFactory|\PHPUnit_Framework_MockObject_MockObject */ - protected $resultPageFactoryMock; + /** + * @var Validator|MockObject + */ + private $formKeyValidatorMock; /** - * @var \Magento\Framework\Controller\Result\RedirectFactory|\PHPUnit_Framework_MockObject_MockObject + * @var LoggerInterface|MockObject */ - private $resultRedirectFactory; + private $loggerMock; protected function setUp() { - $this->sidebarMock = $this->createMock(\Magento\Checkout\Model\Sidebar::class); - $this->loggerMock = $this->createMock(\Psr\Log\LoggerInterface::class); - $this->jsonHelperMock = $this->createMock(\Magento\Framework\Json\Helper\Data::class); - $this->requestMock = $this->createMock(\Magento\Framework\App\RequestInterface::class); - $this->responseMock = $this->getMockForAbstractClass( - \Magento\Framework\App\ResponseInterface::class, - [], - '', - false, - true, - true, - ['representJson'] + $this->requestMock = $this->createMock(RequestInterface::class); + $this->resultJsonFactoryMock = $this->createPartialMock( + ResultJsonFactory::class, + ['create'] ); - $this->resultPageFactoryMock = $this->createMock(\Magento\Framework\View\Result\PageFactory::class); - $this->resultRedirectFactory = $this->createPartialMock( - \Magento\Framework\Controller\Result\RedirectFactory::class, + $this->resultRedirectFactoryMock = $this->createPartialMock( + ResultRedirectFactory::class, ['create'] ); + $this->sidebarMock = $this->createMock(Sidebar::class); + $this->formKeyValidatorMock = $this->createMock(Validator::class); + $this->loggerMock = $this->createMock(LoggerInterface::class); - $this->objectManagerHelper = new ObjectManagerHelper($this); - $this->removeItem = $this->objectManagerHelper->getObject( - \Magento\Checkout\Controller\Sidebar\RemoveItem::class, + $objectManager = new ObjectManager($this); + $this->action = $objectManager->getObject( + RemoveItem::class, [ - 'sidebar' => $this->sidebarMock, - 'logger' => $this->loggerMock, - 'jsonHelper' => $this->jsonHelperMock, 'request' => $this->requestMock, - 'response' => $this->responseMock, - 'resultPageFactory' => $this->resultPageFactoryMock, - 'resultRedirectFactory' => $this->resultRedirectFactory - + 'resultJsonFactory' => $this->resultJsonFactoryMock, + 'resultRedirectFactory' => $this->resultRedirectFactoryMock, + 'sidebar' => $this->sidebarMock, + 'formKeyValidator' => $this->formKeyValidatorMock, + 'logger' => $this->loggerMock ] ); - $formKeyValidatorMock = $this->createMock(\Magento\Framework\Data\Form\FormKey\Validator::class); - $this->setPropertyValue($this->removeItem, 'formKeyValidator', $formKeyValidatorMock); } public function testExecute() { - $this->getPropertyValue($this->removeItem, 'formKeyValidator') - ->expects($this->once()) + $responseData = [ + 'cleanup' => true, + 'data' => [ + 'summary_qty' => 0, + 'summary_text' => __(' items'), + 'subtotal' => 0, + ], + ]; + + $this->formKeyValidatorMock->expects($this->once()) ->method('validate') ->with($this->requestMock) ->willReturn(true); @@ -101,46 +118,33 @@ public function testExecute() ->method('removeQuoteItem') ->with(1) ->willReturnSelf(); + $this->sidebarMock->expects($this->once()) ->method('getResponseData') ->with('') - ->willReturn( - [ - 'cleanup' => true, - 'data' => [ - 'summary_qty' => 0, - 'summary_text' => __(' items'), - 'subtotal' => 0, - ], - ] - ); - - $this->jsonHelperMock->expects($this->once()) - ->method('jsonEncode') - ->with( - [ - 'cleanup' => true, - 'data' => [ - 'summary_qty' => 0, - 'summary_text' => __(' items'), - 'subtotal' => 0, - ], - ] - ) - ->willReturn('json encoded'); - - $this->responseMock->expects($this->once()) - ->method('representJson') - ->with('json encoded') - ->willReturn('json represented'); - - $this->assertEquals('json represented', $this->removeItem->execute()); + ->willReturn($responseData); + + $resultJson = $this->createMock(ResultJson::class); + $resultJson->expects($this->once()) + ->method('setData') + ->with($responseData) + ->willReturnSelf(); + $this->resultJsonFactoryMock->expects($this->once()) + ->method('create') + ->willReturn($resultJson); + + $this->assertEquals($resultJson, $this->action->execute()); } public function testExecuteWithLocalizedException() { - $this->getPropertyValue($this->removeItem, 'formKeyValidator') - ->expects($this->once()) + $errorMessage = 'Error message!'; + $responseData = [ + 'success' => false, + 'error_message' => $errorMessage + ]; + + $this->formKeyValidatorMock->expects($this->once()) ->method('validate') ->with($this->requestMock) ->willReturn(true); @@ -152,40 +156,34 @@ public function testExecuteWithLocalizedException() $this->sidebarMock->expects($this->once()) ->method('checkQuoteItem') ->with(1) - ->willThrowException(new LocalizedException(__('Error message!'))); + ->willThrowException(new LocalizedException(__($errorMessage))); $this->sidebarMock->expects($this->once()) ->method('getResponseData') - ->with('Error message!') - ->willReturn( - [ - 'success' => false, - 'error_message' => 'Error message!', - ] - ); - - $this->jsonHelperMock->expects($this->once()) - ->method('jsonEncode') - ->with( - [ - 'success' => false, - 'error_message' => 'Error message!', - ] - ) - ->willReturn('json encoded'); - - $this->responseMock->expects($this->once()) - ->method('representJson') - ->with('json encoded') - ->willReturn('json represented'); - - $this->assertEquals('json represented', $this->removeItem->execute()); + ->with($errorMessage) + ->willReturn($responseData); + + $resultJson = $this->createMock(ResultJson::class); + $resultJson->expects($this->once()) + ->method('setData') + ->with($responseData) + ->willReturnSelf(); + $this->resultJsonFactoryMock->expects($this->once()) + ->method('create') + ->willReturn($resultJson); + + $this->assertEquals($resultJson, $this->action->execute()); } public function testExecuteWithException() { - $this->getPropertyValue($this->removeItem, 'formKeyValidator') - ->expects($this->once()) + $errorMessage = 'Error message!'; + $responseData = [ + 'success' => false, + 'error_message' => $errorMessage + ]; + + $this->formKeyValidatorMock->expects($this->once()) ->method('validate') ->with($this->requestMock) ->willReturn(true); @@ -194,7 +192,7 @@ public function testExecuteWithException() ->with('item_id', null) ->willReturn('1'); - $exception = new \Exception('Error message!'); + $exception = new \Exception($errorMessage); $this->sidebarMock->expects($this->once()) ->method('checkQuoteItem') @@ -208,77 +206,36 @@ public function testExecuteWithException() $this->sidebarMock->expects($this->once()) ->method('getResponseData') - ->with('Error message!') - ->willReturn( - [ - 'success' => false, - 'error_message' => 'Error message!', - ] - ); - - $this->jsonHelperMock->expects($this->once()) - ->method('jsonEncode') - ->with( - [ - 'success' => false, - 'error_message' => 'Error message!', - ] - ) - ->willReturn('json encoded'); - - $this->responseMock->expects($this->once()) - ->method('representJson') - ->with('json encoded') - ->willReturn('json represented'); - - $this->assertEquals('json represented', $this->removeItem->execute()); + ->with($errorMessage) + ->willReturn($responseData); + + $resultJson = $this->createMock(ResultJson::class); + $resultJson->expects($this->once()) + ->method('setData') + ->with($responseData) + ->willReturnSelf(); + $this->resultJsonFactoryMock->expects($this->once()) + ->method('create') + ->willReturn($resultJson); + + $this->assertEquals($resultJson, $this->action->execute()); } public function testExecuteWhenFormKeyValidationFailed() { - $resultRedirect = $this->createMock(\Magento\Framework\Controller\Result\Redirect::class); - $resultRedirect->expects($this->once())->method('setPath')->with('*/cart/')->willReturnSelf(); - $this->resultRedirectFactory->expects($this->once())->method('create')->willReturn($resultRedirect); - $this->getPropertyValue($this->removeItem, 'formKeyValidator') - ->expects($this->once()) + $resultRedirect = $this->createMock(ResultRedirect::class); + $resultRedirect->expects($this->once()) + ->method('setPath') + ->with('*/cart/') + ->willReturnSelf(); + $this->resultRedirectFactoryMock->expects($this->once()) + ->method('create') + ->willReturn($resultRedirect); + $this->formKeyValidatorMock->expects($this->once()) ->method('validate') ->with($this->requestMock) ->willReturn(false); - $this->assertEquals($resultRedirect, $this->removeItem->execute()); - } - - /** - * Get any object property value. - * - * @param $object - * @param $property - * @return mixed - * @deprecated - */ - protected function getPropertyValue($object, $property) - { - $reflection = new \ReflectionClass(get_class($object)); - $reflectionProperty = $reflection->getProperty($property); - $reflectionProperty->setAccessible(true); - - return $reflectionProperty->getValue($object); - } - - /** - * Set object property value. - * - * @param $object - * @param $property - * @param $value - * @deprecated - */ - protected function setPropertyValue(&$object, $property, $value) - { - $reflection = new \ReflectionClass(get_class($object)); - $reflectionProperty = $reflection->getProperty($property); - $reflectionProperty->setAccessible(true); - $reflectionProperty->setValue($object, $value); - return $object; + $this->assertEquals($resultRedirect, $this->action->execute()); } } diff --git a/app/code/Magento/Checkout/Test/Unit/Model/GuestPaymentInformationManagementTest.php b/app/code/Magento/Checkout/Test/Unit/Model/GuestPaymentInformationManagementTest.php index 1de0ebce10f51..e3843991a181f 100644 --- a/app/code/Magento/Checkout/Test/Unit/Model/GuestPaymentInformationManagementTest.php +++ b/app/code/Magento/Checkout/Test/Unit/Model/GuestPaymentInformationManagementTest.php @@ -7,8 +7,6 @@ namespace Magento\Checkout\Test\Unit\Model; -use Magento\Framework\App\ResourceConnection; -use Magento\Framework\DB\Adapter\AdapterInterface; use Magento\Quote\Model\Quote; use Magento\Quote\Model\Quote\Address; use Magento\Quote\Model\QuoteIdMask; @@ -53,11 +51,6 @@ class GuestPaymentInformationManagementTest extends \PHPUnit\Framework\TestCase */ private $loggerMock; - /** - * @var ResourceConnection|\PHPUnit_Framework_MockObject_MockObject - */ - private $resourceConnectionMock; - protected function setUp() { $objectManager = new \Magento\Framework\TestFramework\Unit\Helper\ObjectManager($this); @@ -75,10 +68,6 @@ protected function setUp() ['create'] ); $this->loggerMock = $this->createMock(\Psr\Log\LoggerInterface::class); - $this->resourceConnectionMock = $this->getMockBuilder(ResourceConnection::class) - ->disableOriginalConstructor() - ->getMock(); - $this->model = $objectManager->getObject( \Magento\Checkout\Model\GuestPaymentInformationManagement::class, [ @@ -86,8 +75,7 @@ protected function setUp() 'paymentMethodManagement' => $this->paymentMethodManagementMock, 'cartManagement' => $this->cartManagementMock, 'cartRepository' => $this->cartRepositoryMock, - 'quoteIdMaskFactory' => $this->quoteIdMaskFactoryMock, - 'connectionPool' => $this->resourceConnectionMock, + 'quoteIdMaskFactory' => $this->quoteIdMaskFactoryMock ] ); $objectManager->setBackwardCompatibleProperty($this->model, 'logger', $this->loggerMock); @@ -104,26 +92,6 @@ public function testSavePaymentInformationAndPlaceOrder() $billingAddressMock->expects($this->once())->method('setEmail')->with($email)->willReturnSelf(); - $adapterMockForSales = $this->getMockBuilder(AdapterInterface::class) - ->disableOriginalConstructor() - ->getMockForAbstractClass(); - $adapterMockForCheckout = $this->getMockBuilder(AdapterInterface::class) - ->disableOriginalConstructor() - ->getMockForAbstractClass(); - - $this->resourceConnectionMock->expects($this->at(0)) - ->method('getConnection') - ->with('sales') - ->willReturn($adapterMockForSales); - $adapterMockForSales->expects($this->once())->method('beginTransaction'); - $adapterMockForSales->expects($this->once())->method('commit'); - - $this->resourceConnectionMock->expects($this->at(1)) - ->method('getConnection') - ->with('checkout') - ->willReturn($adapterMockForCheckout); - $adapterMockForCheckout->expects($this->once())->method('beginTransaction'); - $adapterMockForCheckout->expects($this->once())->method('commit'); $this->paymentMethodManagementMock->expects($this->once())->method('set')->with($cartId, $paymentMock); $this->cartManagementMock->expects($this->once())->method('placeOrder')->with($cartId)->willReturn($orderId); @@ -146,27 +114,6 @@ public function testSavePaymentInformationAndPlaceOrderException() $this->getMockForAssignBillingAddress($cartId, $billingAddressMock); $billingAddressMock->expects($this->once())->method('setEmail')->with($email)->willReturnSelf(); - $adapterMockForSales = $this->getMockBuilder(AdapterInterface::class) - ->disableOriginalConstructor() - ->getMockForAbstractClass(); - $adapterMockForCheckout = $this->getMockBuilder(AdapterInterface::class) - ->disableOriginalConstructor() - ->getMockForAbstractClass(); - - $this->resourceConnectionMock->expects($this->at(0)) - ->method('getConnection') - ->with('sales') - ->willReturn($adapterMockForSales); - $adapterMockForSales->expects($this->once())->method('beginTransaction'); - $adapterMockForSales->expects($this->once())->method('rollback'); - - $this->resourceConnectionMock->expects($this->at(1)) - ->method('getConnection') - ->with('checkout') - ->willReturn($adapterMockForCheckout); - $adapterMockForCheckout->expects($this->once())->method('beginTransaction'); - $adapterMockForCheckout->expects($this->once())->method('rollback'); - $this->paymentMethodManagementMock->expects($this->once())->method('set')->with($cartId, $paymentMock); $exception = new \Magento\Framework\Exception\CouldNotSaveException(__('DB exception')); $this->cartManagementMock->expects($this->once())->method('placeOrder')->willThrowException($exception); @@ -236,31 +183,9 @@ public function testSavePaymentInformationAndPlaceOrderWithLocalizedException() $billingAddressMock->expects($this->once())->method('setEmail')->with($email)->willReturnSelf(); - $adapterMockForSales = $this->getMockBuilder(AdapterInterface::class) - ->disableOriginalConstructor() - ->getMockForAbstractClass(); - $adapterMockForCheckout = $this->getMockBuilder(AdapterInterface::class) - ->disableOriginalConstructor() - ->getMockForAbstractClass(); - - $this->resourceConnectionMock->expects($this->at(0)) - ->method('getConnection') - ->with('sales') - ->willReturn($adapterMockForSales); - $adapterMockForSales->expects($this->once())->method('beginTransaction'); - $adapterMockForSales->expects($this->once())->method('rollback'); - - $this->resourceConnectionMock->expects($this->at(1)) - ->method('getConnection') - ->with('checkout') - ->willReturn($adapterMockForCheckout); - $adapterMockForCheckout->expects($this->once())->method('beginTransaction'); - $adapterMockForCheckout->expects($this->once())->method('rollback'); - $this->paymentMethodManagementMock->expects($this->once())->method('set')->with($cartId, $paymentMock); $phrase = new \Magento\Framework\Phrase(__('DB exception')); $exception = new \Magento\Framework\Exception\LocalizedException($phrase); - $this->loggerMock->expects($this->never())->method('critical'); $this->cartManagementMock->expects($this->once())->method('placeOrder')->willThrowException($exception); $this->model->savePaymentInformationAndPlaceOrder($cartId, $email, $paymentMock, $billingAddressMock); diff --git a/app/code/Magento/Checkout/Test/Unit/Model/Layout/DepersonalizePluginTest.php b/app/code/Magento/Checkout/Test/Unit/Model/Layout/DepersonalizePluginTest.php index 350f9954208fa..3d69bc88bb73d 100644 --- a/app/code/Magento/Checkout/Test/Unit/Model/Layout/DepersonalizePluginTest.php +++ b/app/code/Magento/Checkout/Test/Unit/Model/Layout/DepersonalizePluginTest.php @@ -3,84 +3,90 @@ * Copyright © Magento, Inc. All rights reserved. * See COPYING.txt for license details. */ +declare(strict_types=1); namespace Magento\Checkout\Test\Unit\Model\Layout; +use Magento\Checkout\Model\Layout\DepersonalizePlugin; +use Magento\Checkout\Model\Session as CheckoutSession; +use Magento\Framework\TestFramework\Unit\Helper\ObjectManager as ObjectManagerHelper; +use Magento\Framework\View\LayoutInterface; +use Magento\PageCache\Model\DepersonalizeChecker; +use PHPUnit\Framework\MockObject\MockObject; +use PHPUnit\Framework\TestCase; + /** - * Class DepersonalizePluginTest + * Unit tests for \Magento\Checkout\Model\Layout\DepersonalizePlugin class. */ -class DepersonalizePluginTest extends \PHPUnit\Framework\TestCase +class DepersonalizePluginTest extends TestCase { /** - * @var \Magento\Customer\Model\Layout\DepersonalizePluginTest + * @var DepersonalizePlugin */ - protected $plugin; + private $plugin; /** - * @var \Magento\Framework\View\LayoutInterface|\PHPUnit_Framework_MockObject_MockObject + * @var LayoutInterface|MockObject */ - protected $layoutMock; + private $layoutMock; /** - * @var \Magento\Checkout\Model\Session|\PHPUnit_Framework_MockObject_MockObject + * @var CheckoutSession|MockObject */ - protected $checkoutSessionMock; + private $checkoutSessionMock; /** - * @var \Magento\PageCache\Model\DepersonalizeChecker|\PHPUnit_Framework_MockObject_MockObject + * @var DepersonalizeChecker|MockObject */ - protected $depersonalizeCheckerMock; + private $depersonalizeCheckerMock; /** - * SetUp + * @inheritdoc */ protected function setUp() { - $this->layoutMock = $this->createMock(\Magento\Framework\View\Layout::class); - $this->checkoutSessionMock = $this->createPartialMock( - \Magento\Framework\Session\Generic::class, - ['clearStorage', 'setData', 'getData'] - ); - $this->checkoutSessionMock = $this->createPartialMock(\Magento\Checkout\Model\Session::class, ['clearStorage']); - $this->requestMock = $this->createMock(\Magento\Framework\App\Request\Http::class); - $this->moduleManagerMock = $this->createMock(\Magento\Framework\Module\Manager::class); - $this->cacheConfigMock = $this->createMock(\Magento\PageCache\Model\Config::class); - $this->depersonalizeCheckerMock = $this->createMock(\Magento\PageCache\Model\DepersonalizeChecker::class); + $this->layoutMock = $this->getMockForAbstractClass(LayoutInterface::class); + $this->checkoutSessionMock = $this->createPartialMock(CheckoutSession::class, ['clearStorage']); + $this->depersonalizeCheckerMock = $this->createMock(DepersonalizeChecker::class); - $this->plugin = new \Magento\Checkout\Model\Layout\DepersonalizePlugin( - $this->depersonalizeCheckerMock, - $this->checkoutSessionMock + $this->plugin = (new ObjectManagerHelper($this))->getObject( + DepersonalizePlugin::class, + [ + 'depersonalizeChecker' => $this->depersonalizeCheckerMock, + 'checkoutSession' => $this->checkoutSessionMock, + ] ); } /** - * Test method afterGenerateXml + * Test afterGenerateElements method when depersonalization is needed. + * + * @return void */ - public function testAfterGenerateXml() + public function testAfterGenerateElements(): void { - $expectedResult = $this->createMock(\Magento\Framework\View\Layout::class); - $this->depersonalizeCheckerMock->expects($this->once())->method('checkIfDepersonalize')->willReturn(true); $this->checkoutSessionMock ->expects($this->once()) ->method('clearStorage') - ->will($this->returnValue($expectedResult)); + ->willReturnSelf(); - $actualResult = $this->plugin->afterGenerateXml($this->layoutMock, $expectedResult); - $this->assertEquals($expectedResult, $actualResult); + $this->assertEmpty($this->plugin->afterGenerateElements($this->layoutMock)); } - public function testAfterGenerateXmlNoDepersonalize() + /** + * Test afterGenerateElements method when depersonalization is not needed. + * + * @return void + */ + public function testAfterGenerateElementsNoDepersonalize(): void { - $expectedResult = $this->createMock(\Magento\Framework\View\Layout::class); - $this->depersonalizeCheckerMock->expects($this->once())->method('checkIfDepersonalize')->willReturn(false); $this->checkoutSessionMock ->expects($this->never()) ->method('clearStorage') - ->will($this->returnValue($expectedResult)); + ->willReturnSelf(); - $actualResult = $this->plugin->afterGenerateXml($this->layoutMock, $expectedResult); - $this->assertEquals($expectedResult, $actualResult); + $this->assertEmpty($this->plugin->afterGenerateElements($this->layoutMock)); } } diff --git a/app/code/Magento/Checkout/Test/Unit/Model/PaymentInformationManagementTest.php b/app/code/Magento/Checkout/Test/Unit/Model/PaymentInformationManagementTest.php index df5c255398ebd..ece395e3131f9 100644 --- a/app/code/Magento/Checkout/Test/Unit/Model/PaymentInformationManagementTest.php +++ b/app/code/Magento/Checkout/Test/Unit/Model/PaymentInformationManagementTest.php @@ -157,7 +157,6 @@ public function testSavePaymentInformationAndPlaceOrderWithLocolizedException() $this->paymentMethodManagementMock->expects($this->once())->method('set')->with($cartId, $paymentMock); $phrase = new \Magento\Framework\Phrase(__('DB exception')); $exception = new \Magento\Framework\Exception\LocalizedException($phrase); - $this->loggerMock->expects($this->never())->method('critical'); $this->cartManagementMock->expects($this->once())->method('placeOrder')->willThrowException($exception); $this->model->savePaymentInformationAndPlaceOrder($cartId, $paymentMock, $billingAddressMock); diff --git a/app/code/Magento/Checkout/Test/Unit/Model/ShippingInformationManagementTest.php b/app/code/Magento/Checkout/Test/Unit/Model/ShippingInformationManagementTest.php index 93375bb884535..79c4b37fb1813 100644 --- a/app/code/Magento/Checkout/Test/Unit/Model/ShippingInformationManagementTest.php +++ b/app/code/Magento/Checkout/Test/Unit/Model/ShippingInformationManagementTest.php @@ -6,101 +6,136 @@ namespace Magento\Checkout\Test\Unit\Model; +use Magento\Checkout\Api\Data\PaymentDetailsInterface; +use Magento\Checkout\Api\Data\ShippingInformationInterface; +use Magento\Checkout\Model\PaymentDetailsFactory; +use Magento\Checkout\Model\ShippingInformationManagement; +use Magento\Framework\Exception\InputException; +use Magento\Framework\Exception\LocalizedException; +use Magento\Framework\Exception\NoSuchEntityException; +use Magento\Framework\Exception\StateException; +use Magento\Framework\TestFramework\Unit\Helper\ObjectManager; +use Magento\Quote\Api\CartRepositoryInterface; +use Magento\Quote\Api\CartTotalRepositoryInterface; +use Magento\Quote\Api\Data\AddressInterface; +use Magento\Quote\Api\Data\CartExtension; +use Magento\Quote\Api\Data\CartExtensionFactory; +use Magento\Quote\Api\Data\PaymentMethodInterface; +use Magento\Quote\Api\Data\TotalsInterface; +use Magento\Quote\Api\PaymentMethodManagementInterface; +use Magento\Quote\Model\Quote; +use Magento\Quote\Model\Quote\Address; +use Magento\Quote\Model\QuoteAddressValidator; +use Magento\Quote\Model\Shipping; +use Magento\Quote\Model\ShippingAssignment; +use Magento\Quote\Model\ShippingAssignmentFactory; +use Magento\Quote\Model\ShippingFactory; +use PHPUnit\Framework\MockObject\MockObject as MockObject; +use PHPUnit\Framework\TestCase; + /** + * Test for \Magento\Checkout\Model\ShippingInformationManagement. + * * @SuppressWarnings(PHPMD.CouplingBetweenObjects) * @SuppressWarnings(PHPMD.TooManyFields) */ -class ShippingInformationManagementTest extends \PHPUnit\Framework\TestCase +class ShippingInformationManagementTest extends TestCase { + private const STUB_CART_ID = 100; + + private const STUB_ITEMS_COUNT = 99; + + private const STUB_CARRIER_CODE = 'carrier_code'; + + private const STUB_SHIPPING_METHOD = 'shipping_method'; + + private const STUB_ERROR_MESSAGE = 'error message'; + /** - * @var \Magento\Framework\TestFramework\Unit\Helper\ObjectManager + * @var ShippingInformationManagement */ - private $objectManager; + private $model; /** - * @var \PHPUnit_Framework_MockObject_MockObject + * @var ObjectManager */ - protected $paymentMethodManagementMock; + private $objectManager; /** - * @var \PHPUnit_Framework_MockObject_MockObject + * @var PaymentMethodManagementInterface|MockObject */ - protected $paymentDetailsFactoryMock; + private $paymentMethodManagementMock; /** - * @var \PHPUnit_Framework_MockObject_MockObject + * @var PaymentDetailsFactory|MockObject */ - protected $cartTotalsRepositoryMock; + private $paymentDetailsFactoryMock; /** - * @var \PHPUnit_Framework_MockObject_MockObject + * @var CartTotalRepositoryInterface|MockObject */ - protected $quoteRepositoryMock; + private $cartTotalsRepositoryMock; /** - * @var \PHPUnit_Framework_MockObject_MockObject + * @var CartRepositoryInterface|MockObject */ - protected $shippingAddressMock; + private $quoteRepositoryMock; /** - * @var \PHPUnit_Framework_MockObject_MockObject + * @var Address|MockObject */ - protected $quoteMock; + private $shippingAddressMock; /** - * @var \Magento\Checkout\Model\ShippingInformationManagement + * @var Quote|MockObject */ - protected $model; + private $quoteMock; /** - * @var \PHPUnit_Framework_MockObject_MockObject + * @var ShippingAssignmentFactory|MockObject */ private $shippingAssignmentFactoryMock; /** - * @var \PHPUnit_Framework_MockObject_MockObject + * @var CartExtensionFactory|MockObject */ private $cartExtensionFactoryMock; /** - * @var \PHPUnit_Framework_MockObject_MockObject + * @var ShippingFactory|MockObject */ private $shippingFactoryMock; /** - * @var \PHPUnit_Framework_MockObject_MockObject + * @var CartExtension|MockObject */ private $cartExtensionMock; /** - * @var \PHPUnit_Framework_MockObject_MockObject + * @var ShippingAssignment|MockObject */ private $shippingAssignmentMock; /** - * @var \PHPUnit_Framework_MockObject_MockObject + * @var QuoteAddressValidator|MockObject */ - private $shippingMock; + private $addressValidatorMock; /** - * @var \PHPUnit_Framework_MockObject_MockObject + * @inheritdoc */ - private $addressValidatorMock; - protected function setUp() { - $this->objectManager = new \Magento\Framework\TestFramework\Unit\Helper\ObjectManager($this); - $this->paymentMethodManagementMock = $this->createMock( - \Magento\Quote\Api\PaymentMethodManagementInterface::class - ); + $this->objectManager = new ObjectManager($this); + $this->paymentMethodManagementMock = $this->createMock(PaymentMethodManagementInterface::class); $this->paymentDetailsFactoryMock = $this->createPartialMock( - \Magento\Checkout\Model\PaymentDetailsFactory::class, + PaymentDetailsFactory::class, ['create'] ); - $this->cartTotalsRepositoryMock = $this->createMock(\Magento\Quote\Api\CartTotalRepositoryInterface::class); - $this->quoteRepositoryMock = $this->createMock(\Magento\Quote\Api\CartRepositoryInterface::class); + $this->cartTotalsRepositoryMock = $this->createMock(CartTotalRepositoryInterface::class); + $this->quoteRepositoryMock = $this->createMock(CartRepositoryInterface::class); $this->shippingAddressMock = $this->createPartialMock( - \Magento\Quote\Model\Quote\Address::class, + Address::class, [ 'getSaveInAddressBook', 'getSameAsBilling', @@ -120,7 +155,7 @@ protected function setUp() ); $this->quoteMock = $this->createPartialMock( - \Magento\Quote\Model\Quote::class, + Quote::class, [ 'isVirtual', 'getItemsCount', @@ -134,24 +169,22 @@ protected function setUp() 'getExtensionAttributes', 'setExtensionAttributes', 'setBillingAddress' - ], - [], - '', - false + ] ); - $this->shippingAssignmentFactoryMock = - $this->createPartialMock(\Magento\Quote\Model\ShippingAssignmentFactory::class, ['create']); - $this->cartExtensionFactoryMock = - $this->createPartialMock(\Magento\Quote\Api\Data\CartExtensionFactory::class, ['create']); - $this->shippingFactoryMock = - $this->createPartialMock(\Magento\Quote\Model\ShippingFactory::class, ['create']); - $this->addressValidatorMock = $this->createMock( - \Magento\Quote\Model\QuoteAddressValidator::class + $this->shippingAssignmentFactoryMock = $this->createPartialMock( + ShippingAssignmentFactory::class, + ['create'] ); + $this->cartExtensionFactoryMock = $this->createPartialMock( + CartExtensionFactory::class, + ['create'] + ); + $this->shippingFactoryMock = $this->createPartialMock(ShippingFactory::class, ['create']); + $this->addressValidatorMock = $this->createMock(QuoteAddressValidator::class); $this->model = $this->objectManager->getObject( - \Magento\Checkout\Model\ShippingInformationManagement::class, + ShippingInformationManagement::class, [ 'paymentMethodManagement' => $this->paymentMethodManagementMock, 'paymentDetailsFactory' => $this->paymentDetailsFactoryMock, @@ -166,59 +199,80 @@ protected function setUp() } /** - * @expectedException \Magento\Framework\Exception\InputException - * @expectedExceptionMessage The shipping method can't be set for an empty cart. Add an item to cart and try again. + * Save address with `InputException` + * + * @return void */ - public function testSaveAddressInformationIfCartIsEmpty() + public function testSaveAddressInformationIfCartIsEmpty(): void { - $cartId = 100; - $addressInformationMock = $this->createMock(\Magento\Checkout\Api\Data\ShippingInformationInterface::class); + $cartId = self::STUB_CART_ID; + /** @var ShippingInformationInterface|MockObject $addressInformationMock */ + $addressInformationMock = $this->createMock(ShippingInformationInterface::class); - $this->quoteMock->expects($this->once())->method('getItemsCount')->willReturn(0); + $this->quoteMock->expects($this->once()) + ->method('getItemsCount') + ->willReturn(0); $this->quoteRepositoryMock->expects($this->once()) ->method('getActive') ->with($cartId) ->willReturn($this->quoteMock); + $this->expectException(InputException::class); + $this->expectExceptionMessage( + 'The shipping method can\'t be set for an empty cart. Add an item to cart and try again.' + ); $this->model->saveAddressInformation($cartId, $addressInformationMock); } /** + * Sets shipping assignments + * * @param string $shippingMethod + * @return void */ - private function setShippingAssignmentsMocks($shippingMethod) + private function setShippingAssignmentsMocks($shippingMethod): void { - $this->quoteMock->expects($this->once())->method('getExtensionAttributes')->willReturn(null); - $this->shippingAddressMock->expects($this->once())->method('setLimitCarrier'); + $this->quoteMock->expects($this->once()) + ->method('getExtensionAttributes') + ->willReturn(null); + $this->shippingAddressMock->expects($this->once()) + ->method('setLimitCarrier'); $this->cartExtensionMock = $this->createPartialMock( - \Magento\Quote\Api\Data\CartExtension::class, + CartExtension::class, ['getShippingAssignments', 'setShippingAssignments'] ); $this->cartExtensionFactoryMock->expects($this->once()) ->method('create') ->willReturn($this->cartExtensionMock); - $this->cartExtensionMock->expects($this->once())->method('getShippingAssignments')->willReturn(null); + $this->cartExtensionMock->expects($this->once()) + ->method('getShippingAssignments') + ->willReturn(null); - $this->shippingAssignmentMock = $this->createMock( - \Magento\Quote\Model\ShippingAssignment::class - ); + $this->shippingAssignmentMock = $this->createMock(ShippingAssignment::class); $this->shippingAssignmentFactoryMock->expects($this->once()) ->method('create') ->willReturn($this->shippingAssignmentMock); - $this->shippingAssignmentMock->expects($this->once())->method('getShipping')->willReturn(null); + $this->shippingAssignmentMock->expects($this->once()) + ->method('getShipping') + ->willReturn(null); - $this->shippingMock = $this->createMock(\Magento\Quote\Model\Shipping::class); - $this->shippingFactoryMock->expects($this->once())->method('create')->willReturn($this->shippingMock); + $shippingMock = $this->createMock(Shipping::class); + $this->shippingFactoryMock->expects($this->once()) + ->method('create') + ->willReturn($shippingMock); - $this->shippingMock->expects($this->once()) + $shippingMock->expects($this->once()) ->method('setAddress') ->with($this->shippingAddressMock) ->willReturnSelf(); - $this->shippingMock->expects($this->once())->method('setMethod')->with($shippingMethod)->willReturnSelf(); + $shippingMock->expects($this->once()) + ->method('setMethod') + ->with($shippingMethod) + ->willReturnSelf(); $this->shippingAssignmentMock->expects($this->once()) ->method('setShipping') - ->with($this->shippingMock) + ->with($shippingMock) ->willReturnSelf(); $this->cartExtensionMock->expects($this->once()) @@ -233,38 +287,116 @@ private function setShippingAssignmentsMocks($shippingMethod) } /** - * @expectedException \Magento\Framework\Exception\StateException - * @expectedExceptionMessage The shipping address is missing. Set the address and try again. + * Save address with `StateException` + * + * @return void */ - public function testSaveAddressInformationIfShippingAddressNotSet() + public function testSaveAddressInformationIfShippingAddressNotSet(): void { - $cartId = 100; - $addressInformationMock = $this->createMock(\Magento\Checkout\Api\Data\ShippingInformationInterface::class); + $cartId = self::STUB_CART_ID; + /** @var ShippingInformationInterface|MockObject $addressInformationMock */ + $addressInformationMock = $this->createMock(ShippingInformationInterface::class); $addressInformationMock->expects($this->once()) ->method('getShippingAddress') ->willReturn($this->shippingAddressMock); - $this->shippingAddressMock->expects($this->once())->method('getCountryId')->willReturn(null); + $this->shippingAddressMock->expects($this->once()) + ->method('getCountryId') + ->willReturn(null); + + $this->quoteRepositoryMock->expects($this->once()) + ->method('getActive') + ->with($cartId) + ->willReturn($this->quoteMock); + $this->quoteMock->expects($this->once()) + ->method('getItemsCount') + ->willReturn(self::STUB_ITEMS_COUNT); + + $this->expectException(StateException::class); + $this->expectExceptionMessage('The shipping address is missing. Set the address and try again.'); + $this->model->saveAddressInformation($cartId, $addressInformationMock); + } + + /** + * Save address with `LocalizedException` + * + * @return void + */ + public function testSaveAddressInformationWithLocalizedException(): void + { + $cartId = self::STUB_CART_ID; + $carrierCode = self::STUB_CARRIER_CODE; + $shippingMethod = self::STUB_SHIPPING_METHOD; + $errorMessage = self::STUB_ERROR_MESSAGE; + $exception = new LocalizedException(__($errorMessage)); + /** @var ShippingInformationInterface|MockObject $addressInformationMock */ + $addressInformationMock = $this->createMock(ShippingInformationInterface::class); + + $this->addressValidatorMock->expects($this->exactly(2)) + ->method('validateForCart'); $this->quoteRepositoryMock->expects($this->once()) ->method('getActive') ->with($cartId) ->willReturn($this->quoteMock); - $this->quoteMock->expects($this->once())->method('getItemsCount')->willReturn(100); + $addressInformationMock->expects($this->once()) + ->method('getShippingAddress') + ->willReturn($this->shippingAddressMock); + $addressInformationMock->expects($this->once()) + ->method('getShippingCarrierCode') + ->willReturn($carrierCode); + $addressInformationMock->expects($this->once()) + ->method('getShippingMethodCode') + ->willReturn($shippingMethod); + + $billingAddress = $this->createMock(AddressInterface::class); + $addressInformationMock->expects($this->once()) + ->method('getBillingAddress') + ->willReturn($billingAddress); + + $this->shippingAddressMock->expects($this->once()) + ->method('getCountryId') + ->willReturn('USA'); + + $this->setShippingAssignmentsMocks($carrierCode . '_' . $shippingMethod); + + $this->quoteMock->expects($this->once()) + ->method('getItemsCount') + ->willReturn(self::STUB_ITEMS_COUNT); + $this->quoteMock->expects($this->once()) + ->method('setIsMultiShipping') + ->with(false) + ->willReturnSelf(); + $this->quoteMock->expects($this->once()) + ->method('setBillingAddress') + ->with($billingAddress) + ->willReturnSelf(); + + $this->quoteRepositoryMock->expects($this->once()) + ->method('save') + ->with($this->quoteMock) + ->willThrowException($exception); + + $this->expectException(LocalizedException::class); + $this->expectExceptionMessage( + 'The shipping information was unable to be saved. Error: "' . $errorMessage . '"' + ); $this->model->saveAddressInformation($cartId, $addressInformationMock); } /** - * @expectedException \Magento\Framework\Exception\InputException - * @expectedExceptionMessage The shipping information was unable to be saved. Verify the input data and try again. + * Save address with `InputException` + * + * @return void */ - public function testSaveAddressInformationIfCanNotSaveQuote() + public function testSaveAddressInformationIfCanNotSaveQuote(): void { - $cartId = 100; - $carrierCode = 'carrier_code'; - $shippingMethod = 'shipping_method'; - $addressInformationMock = $this->createMock(\Magento\Checkout\Api\Data\ShippingInformationInterface::class); + $cartId = self::STUB_CART_ID; + $carrierCode = self::STUB_CARRIER_CODE; + $shippingMethod = self::STUB_SHIPPING_METHOD; + /** @var ShippingInformationInterface|MockObject $addressInformationMock */ + $addressInformationMock = $this->createMock(ShippingInformationInterface::class); $this->addressValidatorMock->expects($this->exactly(2)) ->method('validateForCart'); @@ -277,38 +409,59 @@ public function testSaveAddressInformationIfCanNotSaveQuote() $addressInformationMock->expects($this->once()) ->method('getShippingAddress') ->willReturn($this->shippingAddressMock); - $addressInformationMock->expects($this->once())->method('getShippingCarrierCode')->willReturn($carrierCode); - $addressInformationMock->expects($this->once())->method('getShippingMethodCode')->willReturn($shippingMethod); + $addressInformationMock->expects($this->once()) + ->method('getShippingCarrierCode') + ->willReturn($carrierCode); + $addressInformationMock->expects($this->once()) + ->method('getShippingMethodCode') + ->willReturn($shippingMethod); - $billingAddress = $this->createMock(\Magento\Quote\Api\Data\AddressInterface::class); - $addressInformationMock->expects($this->once())->method('getBillingAddress')->willReturn($billingAddress); + $billingAddress = $this->createMock(AddressInterface::class); + $addressInformationMock->expects($this->once()) + ->method('getBillingAddress') + ->willReturn($billingAddress); - $this->shippingAddressMock->expects($this->once())->method('getCountryId')->willReturn('USA'); + $this->shippingAddressMock->expects($this->once()) + ->method('getCountryId') + ->willReturn('USA'); $this->setShippingAssignmentsMocks($carrierCode . '_' . $shippingMethod); - $this->quoteMock->expects($this->once())->method('getItemsCount')->willReturn(100); - $this->quoteMock->expects($this->once())->method('setIsMultiShipping')->with(false)->willReturnSelf(); - $this->quoteMock->expects($this->once())->method('setBillingAddress')->with($billingAddress)->willReturnSelf(); + $this->quoteMock->expects($this->once()) + ->method('getItemsCount') + ->willReturn(self::STUB_ITEMS_COUNT); + $this->quoteMock->expects($this->once()) + ->method('setIsMultiShipping') + ->with(false)->willReturnSelf(); + $this->quoteMock->expects($this->once()) + ->method('setBillingAddress') + ->with($billingAddress) + ->willReturnSelf(); $this->quoteRepositoryMock->expects($this->once()) ->method('save') ->with($this->quoteMock) ->willThrowException(new \Exception()); + $this->expectException(InputException::class); + $this->expectExceptionMessage( + 'The shipping information was unable to be saved. Verify the input data and try again.' + ); $this->model->saveAddressInformation($cartId, $addressInformationMock); } /** - * @expectedException \Magento\Framework\Exception\NoSuchEntityException - * @expectedExceptionMessage Carrier with such method not found: carrier_code, shipping_method + * Save address with `NoSuchEntityException` + * + * @return void */ - public function testSaveAddressInformationIfCarrierCodeIsInvalid() + public function testSaveAddressInformationIfCarrierCodeIsInvalid(): void { - $cartId = 100; - $carrierCode = 'carrier_code'; - $shippingMethod = 'shipping_method'; - $addressInformationMock = $this->createMock(\Magento\Checkout\Api\Data\ShippingInformationInterface::class); + $cartId = self::STUB_CART_ID; + $carrierCode = self::STUB_CARRIER_CODE; + $shippingMethod = self::STUB_SHIPPING_METHOD; + /** @var ShippingInformationInterface|MockObject $addressInformationMock */ + $addressInformationMock = $this->createMock(ShippingInformationInterface::class); $this->addressValidatorMock->expects($this->exactly(2)) ->method('validateForCart'); @@ -320,39 +473,70 @@ public function testSaveAddressInformationIfCarrierCodeIsInvalid() $addressInformationMock->expects($this->once()) ->method('getShippingAddress') ->willReturn($this->shippingAddressMock); - $addressInformationMock->expects($this->once())->method('getShippingCarrierCode')->willReturn($carrierCode); - $addressInformationMock->expects($this->once())->method('getShippingMethodCode')->willReturn($shippingMethod); + $addressInformationMock->expects($this->once()) + ->method('getShippingCarrierCode') + ->willReturn($carrierCode); + $addressInformationMock->expects($this->once()) + ->method('getShippingMethodCode') + ->willReturn($shippingMethod); - $billingAddress = $this->createMock(\Magento\Quote\Api\Data\AddressInterface::class); - $addressInformationMock->expects($this->once())->method('getBillingAddress')->willReturn($billingAddress); - $this->shippingAddressMock->expects($this->once())->method('getCountryId')->willReturn('USA'); + $billingAddress = $this->createMock(AddressInterface::class); + $addressInformationMock->expects($this->once()) + ->method('getBillingAddress') + ->willReturn($billingAddress); + $this->shippingAddressMock->expects($this->once()) + ->method('getCountryId') + ->willReturn('USA'); $this->setShippingAssignmentsMocks($carrierCode . '_' . $shippingMethod); - $this->quoteMock->expects($this->once())->method('getItemsCount')->willReturn(100); - $this->quoteMock->expects($this->once())->method('setIsMultiShipping')->with(false)->willReturnSelf(); - $this->quoteMock->expects($this->once())->method('setBillingAddress')->with($billingAddress)->willReturnSelf(); - $this->quoteMock->expects($this->once())->method('getShippingAddress')->willReturn($this->shippingAddressMock); + $this->quoteMock->expects($this->once()) + ->method('getItemsCount') + ->willReturn(self::STUB_ITEMS_COUNT); + $this->quoteMock->expects($this->once()) + ->method('setIsMultiShipping') + ->with(false) + ->willReturnSelf(); + $this->quoteMock->expects($this->once()) + ->method('setBillingAddress') + ->with($billingAddress) + ->willReturnSelf(); + $this->quoteMock->expects($this->once()) + ->method('getShippingAddress') + ->willReturn($this->shippingAddressMock); $this->quoteRepositoryMock->expects($this->once()) ->method('save') ->with($this->quoteMock); - $this->shippingAddressMock->expects($this->once())->method('getShippingMethod')->willReturn($shippingMethod); + $this->shippingAddressMock->expects($this->once()) + ->method('getShippingMethod') + ->willReturn($shippingMethod); $this->shippingAddressMock->expects($this->once()) ->method('getShippingRateByCode') ->with($shippingMethod) ->willReturn(false); + $this->expectException(NoSuchEntityException::class); + $this->expectExceptionMessage( + 'Carrier with such method not found: ' . self::STUB_CARRIER_CODE . ', ' . self::STUB_SHIPPING_METHOD + ); + $this->model->saveAddressInformation($cartId, $addressInformationMock); } - public function testSaveAddressInformation() + /** + * Save address info test + * + * @return void + */ + public function testSaveAddressInformation(): void { - $cartId = 100; - $carrierCode = 'carrier_code'; - $shippingMethod = 'shipping_method'; - $addressInformationMock = $this->createMock(\Magento\Checkout\Api\Data\ShippingInformationInterface::class); + $cartId = self::STUB_CART_ID; + $carrierCode = self::STUB_CARRIER_CODE; + $shippingMethod = self::STUB_SHIPPING_METHOD; + /** @var ShippingInformationInterface|MockObject $addressInformationMock */ + $addressInformationMock = $this->createMock(ShippingInformationInterface::class); $this->addressValidatorMock->expects($this->exactly(2)) ->method('validateForCart'); @@ -364,40 +548,62 @@ public function testSaveAddressInformation() $addressInformationMock->expects($this->once()) ->method('getShippingAddress') ->willReturn($this->shippingAddressMock); - $addressInformationMock->expects($this->once())->method('getShippingCarrierCode')->willReturn($carrierCode); - $addressInformationMock->expects($this->once())->method('getShippingMethodCode')->willReturn($shippingMethod); + $addressInformationMock->expects($this->once()) + ->method('getShippingCarrierCode') + ->willReturn($carrierCode); + $addressInformationMock->expects($this->once()) + ->method('getShippingMethodCode') + ->willReturn($shippingMethod); - $billingAddress = $this->createMock(\Magento\Quote\Api\Data\AddressInterface::class); - $addressInformationMock->expects($this->once())->method('getBillingAddress')->willReturn($billingAddress); - $this->shippingAddressMock->expects($this->once())->method('getCountryId')->willReturn('USA'); + $billingAddress = $this->createMock(AddressInterface::class); + $addressInformationMock->expects($this->once()) + ->method('getBillingAddress') + ->willReturn($billingAddress); + $this->shippingAddressMock->expects($this->once()) + ->method('getCountryId') + ->willReturn('USA'); $this->setShippingAssignmentsMocks($carrierCode . '_' . $shippingMethod); - $this->quoteMock->expects($this->once())->method('getItemsCount')->willReturn(100); - $this->quoteMock->expects($this->once())->method('setIsMultiShipping')->with(false)->willReturnSelf(); - $this->quoteMock->expects($this->once())->method('setBillingAddress')->with($billingAddress)->willReturnSelf(); - $this->quoteMock->expects($this->once())->method('getShippingAddress')->willReturn($this->shippingAddressMock); + $this->quoteMock->expects($this->once()) + ->method('getItemsCount') + ->willReturn(self::STUB_ITEMS_COUNT); + $this->quoteMock->expects($this->once()) + ->method('setIsMultiShipping') + ->with(false) + ->willReturnSelf(); + $this->quoteMock->expects($this->once()) + ->method('setBillingAddress') + ->with($billingAddress) + ->willReturnSelf(); + $this->quoteMock->expects($this->once()) + ->method('getShippingAddress') + ->willReturn($this->shippingAddressMock); $this->quoteRepositoryMock->expects($this->once()) ->method('save') ->with($this->quoteMock); - $this->shippingAddressMock->expects($this->once())->method('getShippingMethod')->willReturn($shippingMethod); + $this->shippingAddressMock->expects($this->once()) + ->method('getShippingMethod') + ->willReturn($shippingMethod); $this->shippingAddressMock->expects($this->once()) ->method('getShippingRateByCode') ->with($shippingMethod) ->willReturn('rates'); - $paymentDetailsMock = $this->createMock(\Magento\Checkout\Api\Data\PaymentDetailsInterface::class); - $this->paymentDetailsFactoryMock->expects($this->once())->method('create')->willReturn($paymentDetailsMock); + $paymentDetailsMock = $this->createMock(PaymentDetailsInterface::class); + $this->paymentDetailsFactoryMock->expects($this->once()) + ->method('create') + ->willReturn($paymentDetailsMock); - $paymentMethodMock = $this->createMock(\Magento\Quote\Api\Data\PaymentMethodInterface::class); + $paymentMethodMock = $this->createMock(PaymentMethodInterface::class); $this->paymentMethodManagementMock->expects($this->once()) ->method('getList') ->with($cartId) ->willReturn([$paymentMethodMock]); - $cartTotalsMock = $this->createMock(\Magento\Quote\Api\Data\TotalsInterface::class); + $cartTotalsMock = $this->createMock(TotalsInterface::class); $this->cartTotalsRepositoryMock->expects($this->once()) ->method('get') ->with($cartId) @@ -407,7 +613,9 @@ public function testSaveAddressInformation() ->method('setPaymentMethods') ->with([$paymentMethodMock]) ->willReturnSelf(); - $paymentDetailsMock->expects($this->once())->method('setTotals')->with()->willReturnSelf($cartTotalsMock); + $paymentDetailsMock->expects($this->once()) + ->method('setTotals') + ->willReturn($cartTotalsMock); $this->assertEquals( $paymentDetailsMock, diff --git a/app/code/Magento/Checkout/Test/Unit/Plugin/ResetQuoteAddressesTest.php b/app/code/Magento/Checkout/Test/Unit/Plugin/ResetQuoteAddressesTest.php new file mode 100644 index 0000000000000..d5d3e3e8b0469 --- /dev/null +++ b/app/code/Magento/Checkout/Test/Unit/Plugin/ResetQuoteAddressesTest.php @@ -0,0 +1,240 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +declare(strict_types=1); + +namespace Magento\Checkout\Test\Unit\Plugin; + +use Magento\Checkout\Plugin\Model\Quote\ResetQuoteAddresses; +use Magento\Quote\Api\Data\CartExtensionInterface; +use Magento\Quote\Model\Quote; +use Magento\Quote\Model\Quote\Address; +use PHPUnit\Framework\MockObject\MockObject; +use PHPUnit\Framework\TestCase; + +/** + * Class ResetQuoteAddressesTest + * + * Test of clearing quote addresses after all items were removed. + */ +class ResetQuoteAddressesTest extends TestCase +{ + /** + * @var int + */ + private const STUB_ADDRESS_ID = 1; + + /** + * @var int + */ + private const STUB_ITEM_ID = 1; + + /** + * @var int + */ + private const STUB_SHIPPING_ASSIGNMENTS = 1; + + /** + * @var array + */ + private const STUB_QUOTE_ITEMS = [1, 2]; + + /** + * @var ResetQuoteAddresses + */ + private $plugin; + + /** + * @var Quote|MockObject + */ + private $quoteMock; + + /** + * @var CartExtensionInterface|MockObject + */ + private $extensionAttributesMock; + + /** + * Set Up + */ + protected function setUp() + { + $this->quoteMock = $this->createPartialMock(Quote::class, [ + 'getAllAddresses', + 'getAllVisibleItems', + 'removeAddress', + 'getExtensionAttributes', + 'isVirtual', + ]); + $this->extensionAttributesMock = $this->getMockBuilder(CartExtensionInterface::class) + ->setMethods( + [ + 'getShippingAssignments', + 'setShippingAssignments' + ] + ) + ->getMockForAbstractClass(); + + $this->plugin = new ResetQuoteAddresses(); + } + + /** + * Test removing the addresses from a non empty quote + */ + public function testRemovingTheAddressesFromNonEmptyQuote() + { + $this->quoteMock->expects($this->any()) + ->method('getAllVisibleItems') + ->will($this->returnValue(static::STUB_QUOTE_ITEMS)); + $this->quoteMock->expects($this->never()) + ->method('getAllAddresses') + ->willReturnSelf(); + + $this->plugin->afterRemoveItem($this->quoteMock, $this->quoteMock, 1); + } + + /** + * Test clearing the addresses from an empty quote with addresses + * + * @dataProvider quoteAddressesDataProvider + * + * @param bool $isVirtualQuote + * @param array $extensionAttributes + */ + public function testClearingAddressesSuccessfullyFromEmptyQuoteWithAddress( + bool $isVirtualQuote, + array $extensionAttributes + ) { + $this->quoteMock->expects($this->any()) + ->method('getAllVisibleItems') + ->will($this->returnValue([])); + + $address = $this->createPartialMock(Address::class, ['getId']); + + $address->expects($this->any()) + ->method('getId') + ->willReturn(static::STUB_ADDRESS_ID); + + $addresses = [$address]; + + $this->quoteMock->expects($this->any()) + ->method('getAllAddresses') + ->will($this->returnValue($addresses)); + + $this->quoteMock->expects($this->exactly(count($addresses))) + ->method('removeAddress') + ->willReturnSelf(); + + $this->quoteMock->expects($this->once()) + ->method('getExtensionAttributes') + ->willReturn($this->extensionAttributesMock); + + $this->quoteMock->expects($this->once()) + ->method('isVirtual') + ->willReturn($isVirtualQuote); + + if (!$isVirtualQuote && $extensionAttributes) { + $this->extensionAttributesMock->expects($this->any()) + ->method('getShippingAssignments') + ->willReturn([static::STUB_SHIPPING_ASSIGNMENTS]); + + $this->extensionAttributesMock->expects($this->once()) + ->method('setShippingAssignments') + ->willReturnSelf(); + } + + $this->plugin->afterRemoveItem($this->quoteMock, $this->quoteMock, static::STUB_ITEM_ID); + } + + /** + * Test clearing the addresses from an empty quote + * + * @dataProvider quoteNoAddressesDataProvider + * + * @param bool $isVirtualQuote + * @param array $extensionAttributes + */ + public function testClearingTheAddressesFromEmptyQuote( + bool $isVirtualQuote, + array $extensionAttributes + ) { + $quoteVisibleItems = []; + $addresses = []; + + $this->quoteMock->expects($this->any()) + ->method('getAllVisibleItems') + ->will($this->returnValue($quoteVisibleItems)); + + $this->quoteMock->expects($this->any()) + ->method('getAllAddresses') + ->willReturn($addresses); + + $this->quoteMock->expects($this->once()) + ->method('getExtensionAttributes') + ->willReturn($this->extensionAttributesMock); + + $this->quoteMock->expects($this->once()) + ->method('isVirtual') + ->willReturn($isVirtualQuote); + + if (!$isVirtualQuote && $extensionAttributes) { + $this->extensionAttributesMock->expects($this->any()) + ->method('getShippingAssignments') + ->willReturn($extensionAttributes); + + $this->extensionAttributesMock->expects($this->once()) + ->method('setShippingAssignments') + ->willReturnSelf(); + } + + $this->plugin->afterRemoveItem($this->quoteMock, $this->quoteMock, static::STUB_ITEM_ID); + } + + /** + * Quote without address data provider + * + * @return array + */ + public function quoteNoAddressesDataProvider(): array + { + return [ + 'Test case with virtual quote' => [ + true, + [] + ], + 'Test case with a non virtual quote without extension attributes' => [ + false, + [] + ], + 'Test case with a non virtual quote with shipping assignments' => [ + false, + [1] + ] + ]; + } + + /** + * Quote with address information data provider + * + * @return array + */ + public function quoteAddressesDataProvider(): array + { + return [ + 'Test case with a virtual quote and no shipping assignments' => [ + true, + [] + ], + 'Test case with a virtual quote and with shipping assignments' => [ + true, + [1] + ], + 'Test case with none virtual quote and with shipping assignments' => [ + false, + [1] + ] + ]; + } +} diff --git a/app/code/Magento/Checkout/etc/adminhtml/system.xml b/app/code/Magento/Checkout/etc/adminhtml/system.xml index 399474a36bfc7..7454c2b6524f3 100644 --- a/app/code/Magento/Checkout/etc/adminhtml/system.xml +++ b/app/code/Magento/Checkout/etc/adminhtml/system.xml @@ -21,7 +21,7 @@ <label>Allow Guest Checkout</label> <source_model>Magento\Config\Model\Config\Source\Yesno</source_model> </field> - <field id="display_billing_address_on" translate="label" type="select" sortOrder="20" showInDefault="1" showInWebsite="1" showInStore="1"> + <field id="display_billing_address_on" translate="label" type="select" sortOrder="20" showInDefault="1" showInWebsite="1" showInStore="1" canRestore="1"> <label>Display Billing Address On</label> <source_model>\Magento\Checkout\Model\Adminhtml\BillingAddressDisplayOptions</source_model> </field> @@ -32,7 +32,7 @@ </group> <group id="cart" translate="label" sortOrder="2" showInDefault="1" showInWebsite="1" showInStore="1"> <label>Shopping Cart</label> - <field id="delete_quote_after" translate="label" type="text" sortOrder="1" showInDefault="1" showInWebsite="1" showInStore="0" canRestore="1"> + <field id="delete_quote_after" translate="label" type="text" sortOrder="1" showInDefault="1" showInWebsite="1" canRestore="1"> <label>Quote Lifetime (days)</label> <validate>validate-zero-or-greater validate-digits</validate> </field> @@ -49,9 +49,9 @@ <source_model>Magento\Config\Model\Config\Source\Yesno</source_model> </field> </group> - <group id="cart_link" translate="label" sortOrder="3" showInDefault="1" showInWebsite="1" showInStore="0"> + <group id="cart_link" translate="label" sortOrder="3" showInDefault="1" showInWebsite="1"> <label>My Cart Link</label> - <field id="use_qty" translate="label" type="select" sortOrder="1" showInDefault="1" showInWebsite="1" showInStore="0" canRestore="1"> + <field id="use_qty" translate="label" type="select" sortOrder="1" showInDefault="1" showInWebsite="1" canRestore="1"> <label>Display Cart Summary</label> <source_model>Magento\Checkout\Model\Config\Source\Cart\Summary</source_model> </field> diff --git a/app/code/Magento/Checkout/etc/config.xml b/app/code/Magento/Checkout/etc/config.xml index f8c2e7ebcb503..c8408f6d902fa 100644 --- a/app/code/Magento/Checkout/etc/config.xml +++ b/app/code/Magento/Checkout/etc/config.xml @@ -11,6 +11,7 @@ <options> <onepage_checkout_enabled>1</onepage_checkout_enabled> <guest_checkout>1</guest_checkout> + <display_billing_address_on>0</display_billing_address_on> <max_items_display_count>10</max_items_display_count> </options> <cart> diff --git a/app/code/Magento/Checkout/registration.php b/app/code/Magento/Checkout/registration.php index 741146232ee25..ca98e2eef761f 100644 --- a/app/code/Magento/Checkout/registration.php +++ b/app/code/Magento/Checkout/registration.php @@ -4,6 +4,6 @@ * See COPYING.txt for license details. */ -use \Magento\Framework\Component\ComponentRegistrar; +use Magento\Framework\Component\ComponentRegistrar; ComponentRegistrar::register(ComponentRegistrar::MODULE, 'Magento_Checkout', __DIR__); diff --git a/app/code/Magento/Checkout/view/frontend/layout/checkout_cart_index.xml b/app/code/Magento/Checkout/view/frontend/layout/checkout_cart_index.xml index 69d2523d88dfb..4b5d1033408e4 100644 --- a/app/code/Magento/Checkout/view/frontend/layout/checkout_cart_index.xml +++ b/app/code/Magento/Checkout/view/frontend/layout/checkout_cart_index.xml @@ -189,6 +189,7 @@ <block class="Magento\Checkout\Block\Cart\Crosssell" name="checkout.cart.crosssell" template="Magento_Catalog::product/list/items.phtml" after="-" ifconfig="checkout/cart/crosssell_enabled"> <arguments> <argument name="type" xsi:type="string">crosssell</argument> + <argument name="view_model" xsi:type="object">Magento\Catalog\ViewModel\Product\Listing\PreparePostData</argument> </arguments> <block class="Magento\Catalog\Block\Product\ProductList\Item\Container" name="crosssell.product.addto" as="addto"> <block class="Magento\Catalog\Block\Product\ProductList\Item\AddTo\Compare" diff --git a/app/code/Magento/Checkout/view/frontend/layout/checkout_index_index.xml b/app/code/Magento/Checkout/view/frontend/layout/checkout_index_index.xml index fed0c951eff9f..c33b784fcd20c 100644 --- a/app/code/Magento/Checkout/view/frontend/layout/checkout_index_index.xml +++ b/app/code/Magento/Checkout/view/frontend/layout/checkout_index_index.xml @@ -222,9 +222,6 @@ <item name="min_text_length" xsi:type="number">0</item> </item> </item> - <item name="country_id" xsi:type="array"> - <item name="sortOrder" xsi:type="string">115</item> - </item> <item name="telephone" xsi:type="array"> <item name="config" xsi:type="array"> <item name="tooltip" xsi:type="array"> diff --git a/app/code/Magento/Checkout/view/frontend/requirejs-config.js b/app/code/Magento/Checkout/view/frontend/requirejs-config.js index 1f552b779539c..602a1227644c0 100644 --- a/app/code/Magento/Checkout/view/frontend/requirejs-config.js +++ b/app/code/Magento/Checkout/view/frontend/requirejs-config.js @@ -12,7 +12,8 @@ var config = { sidebar: 'Magento_Checkout/js/sidebar', checkoutLoader: 'Magento_Checkout/js/checkout-loader', checkoutData: 'Magento_Checkout/js/checkout-data', - proceedToCheckout: 'Magento_Checkout/js/proceed-to-checkout' + proceedToCheckout: 'Magento_Checkout/js/proceed-to-checkout', + catalogAddToCart: 'Magento_Catalog/js/catalog-add-to-cart' } } }; diff --git a/app/code/Magento/Checkout/view/frontend/web/js/action/select-billing-address.js b/app/code/Magento/Checkout/view/frontend/web/js/action/select-billing-address.js index 086859fa8021d..1f14ab444e0c1 100644 --- a/app/code/Magento/Checkout/view/frontend/web/js/action/select-billing-address.js +++ b/app/code/Magento/Checkout/view/frontend/web/js/action/select-billing-address.js @@ -18,7 +18,7 @@ define([ if (quote.shippingAddress() && billingAddress.getCacheKey() == //eslint-disable-line eqeqeq quote.shippingAddress().getCacheKey() ) { - address = $.extend({}, billingAddress); + address = $.extend(true, {}, billingAddress); address.saveInAddressBook = null; } else { address = billingAddress; diff --git a/app/code/Magento/Checkout/view/frontend/web/js/action/select-payment-method.js b/app/code/Magento/Checkout/view/frontend/web/js/action/select-payment-method.js index 34f1700749794..5adbd9356a8d8 100644 --- a/app/code/Magento/Checkout/view/frontend/web/js/action/select-payment-method.js +++ b/app/code/Magento/Checkout/view/frontend/web/js/action/select-payment-method.js @@ -7,7 +7,7 @@ * @api */ define([ - '../model/quote' + 'Magento_Checkout/js/model/quote' ], function (quote) { 'use strict'; diff --git a/app/code/Magento/Checkout/view/frontend/web/js/action/update-shopping-cart.js b/app/code/Magento/Checkout/view/frontend/web/js/action/update-shopping-cart.js index a8e70b65019ce..f9def2fd58b23 100644 --- a/app/code/Magento/Checkout/view/frontend/web/js/action/update-shopping-cart.js +++ b/app/code/Magento/Checkout/view/frontend/web/js/action/update-shopping-cart.js @@ -122,6 +122,9 @@ define([ submitForm: function () { this.element .off('submit', this.onSubmit) + .on('submit', function () { + $(document.body).trigger('processStart'); + }) .submit(); } }); diff --git a/app/code/Magento/Checkout/view/frontend/web/js/sidebar.js b/app/code/Magento/Checkout/view/frontend/web/js/sidebar.js index 6fc5ef9d2a574..a7ccb217fa102 100644 --- a/app/code/Magento/Checkout/view/frontend/web/js/sidebar.js +++ b/app/code/Magento/Checkout/view/frontend/web/js/sidebar.js @@ -260,7 +260,12 @@ define([ if (!_.isUndefined(productData)) { $(document).trigger('ajax:removeFromCart', { - productIds: [productData['product_id']] + productIds: [productData['product_id']], + productInfo: [ + { + 'id': productData['product_id'] + } + ] }); if (window.location.href.indexOf(this.shoppingCartUrl) === 0) { diff --git a/app/code/Magento/Checkout/view/frontend/web/js/view/billing-address.js b/app/code/Magento/Checkout/view/frontend/web/js/view/billing-address.js index e728a5c0fcdd5..f850386890470 100644 --- a/app/code/Magento/Checkout/view/frontend/web/js/view/billing-address.js +++ b/app/code/Magento/Checkout/view/frontend/web/js/view/billing-address.js @@ -255,9 +255,11 @@ function ( return attribute.label; } - resultAttribute = _.findWhere(this.source.get('customAttributes')[attribute['attribute_code']], { - value: attribute.value - }); + if (typeof this.source.get('customAttributes') !== 'undefined') { + resultAttribute = _.findWhere(this.source.get('customAttributes')[attribute['attribute_code']], { + value: attribute.value + }); + } return resultAttribute && resultAttribute.label || attribute.value; } diff --git a/app/code/Magento/Checkout/view/frontend/web/js/view/minicart.js b/app/code/Magento/Checkout/view/frontend/web/js/view/minicart.js index 4adc1cd88c0ae..c77e72e38107a 100644 --- a/app/code/Magento/Checkout/view/frontend/web/js/view/minicart.js +++ b/app/code/Magento/Checkout/view/frontend/web/js/view/minicart.js @@ -162,10 +162,11 @@ define([ /** * Get cart param by name. + * * @param {String} name * @returns {*} */ - getCartParam: function (name) { + getCartParamUnsanitizedHtml: function (name) { if (!_.isUndefined(name)) { if (!this.cart.hasOwnProperty(name)) { this.cart[name] = ko.observable(); @@ -175,12 +176,21 @@ define([ return this.cart[name](); }, + /** + * @deprecated please use getCartParamUnsanitizedHtml. + * @param {String} name + * @returns {*} + */ + getCartParam: function (name) { + return this.getCartParamUnsanitizedHtml(name); + }, + /** * Returns array of cart items, limited by 'maxItemsToDisplay' setting * @returns [] */ getCartItems: function () { - var items = this.getCartParam('items') || []; + var items = this.getCartParamUnsanitizedHtml('items') || []; items = items.slice(parseInt(-this.maxItemsToDisplay, 10)); @@ -192,7 +202,7 @@ define([ * @returns {Number} */ getCartLineItemsCount: function () { - var items = this.getCartParam('items') || []; + var items = this.getCartParamUnsanitizedHtml('items') || []; return parseInt(items.length, 10); } diff --git a/app/code/Magento/Checkout/view/frontend/web/js/view/payment/list.js b/app/code/Magento/Checkout/view/frontend/web/js/view/payment/list.js index 15ae5d68534b8..17ccf3863a875 100644 --- a/app/code/Magento/Checkout/view/frontend/web/js/view/payment/list.js +++ b/app/code/Magento/Checkout/view/frontend/web/js/view/payment/list.js @@ -203,7 +203,7 @@ define([ */ isPaymentMethodsAvailable: function () { return _.some(this.paymentGroupsList(), function (group) { - return this.getRegion(group.displayArea)().length; + return this.regionHasElements(group.displayArea); }, this); }, diff --git a/app/code/Magento/Checkout/view/frontend/web/js/view/shipping-address/address-renderer/default.js b/app/code/Magento/Checkout/view/frontend/web/js/view/shipping-address/address-renderer/default.js index 939a2af1a25aa..1f8cc90fe1622 100644 --- a/app/code/Magento/Checkout/view/frontend/web/js/view/shipping-address/address-renderer/default.js +++ b/app/code/Magento/Checkout/view/frontend/web/js/view/shipping-address/address-renderer/default.js @@ -65,9 +65,11 @@ define([ return attribute.label; } - resultAttribute = _.findWhere(this.source.get('customAttributes')[attribute['attribute_code']], { - value: attribute.value - }); + if (typeof this.source.get('customAttributes') !== 'undefined') { + resultAttribute = _.findWhere(this.source.get('customAttributes')[attribute['attribute_code']], { + value: attribute.value + }); + } return resultAttribute && resultAttribute.label || attribute.value; }, diff --git a/app/code/Magento/Checkout/view/frontend/web/js/view/shipping-information/address-renderer/default.js b/app/code/Magento/Checkout/view/frontend/web/js/view/shipping-information/address-renderer/default.js index 009178cbb19b9..6ec9fde554dc2 100644 --- a/app/code/Magento/Checkout/view/frontend/web/js/view/shipping-information/address-renderer/default.js +++ b/app/code/Magento/Checkout/view/frontend/web/js/view/shipping-information/address-renderer/default.js @@ -42,9 +42,11 @@ define([ return attribute.label; } - resultAttribute = _.findWhere(this.source.get('customAttributes')[attribute['attribute_code']], { - value: attribute.value - }); + if (typeof this.source.get('customAttributes') !== 'undefined') { + resultAttribute = _.findWhere(this.source.get('customAttributes')[attribute['attribute_code']], { + value: attribute.value + }); + } return resultAttribute && resultAttribute.label || attribute.value; } diff --git a/app/code/Magento/Checkout/view/frontend/web/template/cart/shipping-rates.html b/app/code/Magento/Checkout/view/frontend/web/template/cart/shipping-rates.html index 5d1889519a302..9f0d436056924 100644 --- a/app/code/Magento/Checkout/view/frontend/web/template/cart/shipping-rates.html +++ b/app/code/Magento/Checkout/view/frontend/web/template/cart/shipping-rates.html @@ -24,7 +24,8 @@ checked: $parents[1].selectedShippingMethod, attr: { value: carrier_code + '_' + method_code, - id: 's_method_' + carrier_code + '_' + method_code + id: 's_method_' + carrier_code + '_' + method_code, + disabled: false } "/> <label class="label" data-bind="attr: {for: 's_method_' + carrier_code + '_' + method_code}"> diff --git a/app/code/Magento/Checkout/view/frontend/web/template/form/element/email.html b/app/code/Magento/Checkout/view/frontend/web/template/form/element/email.html index 8d6142e07fcf0..8e71d5845cec8 100644 --- a/app/code/Magento/Checkout/view/frontend/web/template/form/element/email.html +++ b/app/code/Magento/Checkout/view/frontend/web/template/form/element/email.html @@ -14,9 +14,7 @@ method="post"> <fieldset id="customer-email-fieldset" class="fieldset" data-bind="blockLoader: isLoading"> <div class="field required"> - <label class="label" for="customer-email"> - <span data-bind="i18n: 'Email Address'"></span> - </label> + <label class="label" for="customer-email"><span data-bind="i18n: 'Email Address'"></span></label> <div class="control _with-tooltip"> <input class="input-text" type="email" @@ -35,9 +33,7 @@ <!--Hidden fields --> <fieldset class="fieldset hidden-fields" data-bind="fadeVisible: isPasswordVisible"> <div class="field"> - <label class="label" for="customer-password"> - <span data-bind="i18n: 'Password'"></span> - </label> + <label class="label" for="customer-password"><span data-bind="i18n: 'Password'"></span></label> <div class="control"> <input class="input-text" data-bind=" diff --git a/app/code/Magento/Checkout/view/frontend/web/template/minicart/content.html b/app/code/Magento/Checkout/view/frontend/web/template/minicart/content.html index 0719a7d01ec70..c5fd6d545702b 100644 --- a/app/code/Magento/Checkout/view/frontend/web/template/minicart/content.html +++ b/app/code/Magento/Checkout/view/frontend/web/template/minicart/content.html @@ -56,7 +56,7 @@ " translate="'Proceed to Checkout'" /> - <div data-bind="html: getCartParam('extra_actions')"></div> + <div data-bind="html: getCartParamUnsanitizedHtml('extra_actions')"></div> </div> </div> </if> @@ -97,7 +97,7 @@ </div> </div> - <div id="minicart-widgets" class="minicart-widgets" if="getRegion('promotion').length"> + <div id="minicart-widgets" class="minicart-widgets" if="regionHasElements('promotion')"> <each args="getRegion('promotion')" render=""/> </div> </div> diff --git a/app/code/Magento/Checkout/view/frontend/web/template/payment-methods/list.html b/app/code/Magento/Checkout/view/frontend/web/template/payment-methods/list.html index b7be5efee383e..77b801ec0e826 100644 --- a/app/code/Magento/Checkout/view/frontend/web/template/payment-methods/list.html +++ b/app/code/Magento/Checkout/view/frontend/web/template/payment-methods/list.html @@ -8,7 +8,7 @@ class="items payment-methods"> <div repeat="foreach: paymentGroupsList, item: '$group'" class="payment-group"> - <div if="getRegion($group().displayArea)().length" + <div if="regionHasElements($group().displayArea)" translate="getGroupTitle($group)" class="step-title" data-role="title"> diff --git a/app/code/Magento/CheckoutAgreements/Model/AgreementsValidator.php b/app/code/Magento/CheckoutAgreements/Model/AgreementsValidator.php index 8d25ba88c0f82..2643e69ba1efd 100644 --- a/app/code/Magento/CheckoutAgreements/Model/AgreementsValidator.php +++ b/app/code/Magento/CheckoutAgreements/Model/AgreementsValidator.php @@ -5,13 +5,15 @@ */ namespace Magento\CheckoutAgreements\Model; +use Magento\Checkout\Api\AgreementsValidatorInterface; + /** - * Class AgreementsValidator + * Validator for Checkout Agreements */ -class AgreementsValidator implements \Magento\Checkout\Api\AgreementsValidatorInterface +class AgreementsValidator implements AgreementsValidatorInterface { /** - * @var \Magento\CheckoutAgreements\Model\AgreementsProviderInterface[] + * @var AgreementsProviderInterface[] */ protected $agreementsProviders; @@ -33,11 +35,13 @@ public function __construct($list = null) public function isValid($agreementIds = []) { $agreementIds = $agreementIds === null ? [] : $agreementIds; - $requiredAgreements = []; + $requiredAgreements = [[]]; foreach ($this->agreementsProviders as $agreementsProvider) { - $requiredAgreements = array_merge($requiredAgreements, $agreementsProvider->getRequiredAgreementIds()); + $requiredAgreements[] = $agreementsProvider->getRequiredAgreementIds(); } - $agreementsDiff = array_diff($requiredAgreements, $agreementIds); + + $agreementsDiff = array_diff(array_merge(...$requiredAgreements), $agreementIds); + return empty($agreementsDiff); } } diff --git a/app/code/Magento/CheckoutAgreements/Model/Checkout/Plugin/GuestValidation.php b/app/code/Magento/CheckoutAgreements/Model/Checkout/Plugin/GuestValidation.php index fbceca0906702..95330c9d01381 100644 --- a/app/code/Magento/CheckoutAgreements/Model/Checkout/Plugin/GuestValidation.php +++ b/app/code/Magento/CheckoutAgreements/Model/Checkout/Plugin/GuestValidation.php @@ -11,7 +11,7 @@ use Magento\CheckoutAgreements\Model\Api\SearchCriteria\ActiveStoreAgreementsFilter; /** - * Class GuestValidation + * Guest checkout agreements validation. * * Plugin that checks if checkout agreement enabled and validates all agreements. * Current plugin is duplicate from Magento\CheckoutAgreements\Model\Checkout\Plugin\Validation due to different @@ -58,6 +58,8 @@ public function __construct( } /** + * Validates agreements before save payment information and order placing. + * * @param \Magento\Checkout\Api\GuestPaymentInformationManagementInterface $subject * @param string $cartId * @param string $email @@ -80,28 +82,8 @@ public function beforeSavePaymentInformationAndPlaceOrder( } /** - * @param \Magento\Checkout\Api\GuestPaymentInformationManagementInterface $subject - * @param string $cartId - * @param string $email - * @param \Magento\Quote\Api\Data\PaymentInterface $paymentMethod - * @param \Magento\Quote\Api\Data\AddressInterface|null $billingAddress - * @throws \Magento\Framework\Exception\CouldNotSaveException - * @return void - * @SuppressWarnings(PHPMD.UnusedFormalParameter) - */ - public function beforeSavePaymentInformation( - \Magento\Checkout\Api\GuestPaymentInformationManagementInterface $subject, - $cartId, - $email, - \Magento\Quote\Api\Data\PaymentInterface $paymentMethod, - \Magento\Quote\Api\Data\AddressInterface $billingAddress = null - ) { - if ($this->isAgreementEnabled()) { - $this->validateAgreements($paymentMethod); - } - } - - /** + * Validates agreements. + * * @param \Magento\Quote\Api\Data\PaymentInterface $paymentMethod * @throws \Magento\Framework\Exception\CouldNotSaveException * @return void @@ -123,7 +105,8 @@ private function validateAgreements(\Magento\Quote\Api\Data\PaymentInterface $pa } /** - * Verify if agreement validation needed + * Verify if agreement validation needed. + * * @return bool */ private function isAgreementEnabled() diff --git a/app/code/Magento/CheckoutAgreements/Model/Checkout/Plugin/Validation.php b/app/code/Magento/CheckoutAgreements/Model/Checkout/Plugin/Validation.php index 67e2a6c9ec334..5338a16004a00 100644 --- a/app/code/Magento/CheckoutAgreements/Model/Checkout/Plugin/Validation.php +++ b/app/code/Magento/CheckoutAgreements/Model/Checkout/Plugin/Validation.php @@ -7,23 +7,24 @@ namespace Magento\CheckoutAgreements\Model\Checkout\Plugin; use Magento\CheckoutAgreements\Model\AgreementsProvider; -use Magento\Store\Model\ScopeInterface; use Magento\CheckoutAgreements\Model\Api\SearchCriteria\ActiveStoreAgreementsFilter; +use Magento\Quote\Api\CartRepositoryInterface; +use Magento\Store\Model\ScopeInterface; /** - * Class Validation + * Class Validation validates the agreement based on the payment method */ class Validation { /** * @var \Magento\Framework\App\Config\ScopeConfigInterface */ - protected $scopeConfiguration; + private $scopeConfiguration; /** * @var \Magento\Checkout\Api\AgreementsValidatorInterface */ - protected $agreementsValidator; + private $agreementsValidator; /** * @var \Magento\CheckoutAgreements\Api\CheckoutAgreementsListInterface @@ -35,25 +36,37 @@ class Validation */ private $activeStoreAgreementsFilter; + /** + * Quote repository. + * + * @var \Magento\Quote\Api\CartRepositoryInterface + */ + private $quoteRepository; + /** * @param \Magento\Checkout\Api\AgreementsValidatorInterface $agreementsValidator * @param \Magento\Framework\App\Config\ScopeConfigInterface $scopeConfiguration * @param \Magento\CheckoutAgreements\Api\CheckoutAgreementsListInterface $checkoutAgreementsList * @param ActiveStoreAgreementsFilter $activeStoreAgreementsFilter + * @param CartRepositoryInterface $quoteRepository */ public function __construct( \Magento\Checkout\Api\AgreementsValidatorInterface $agreementsValidator, \Magento\Framework\App\Config\ScopeConfigInterface $scopeConfiguration, \Magento\CheckoutAgreements\Api\CheckoutAgreementsListInterface $checkoutAgreementsList, - \Magento\CheckoutAgreements\Model\Api\SearchCriteria\ActiveStoreAgreementsFilter $activeStoreAgreementsFilter + \Magento\CheckoutAgreements\Model\Api\SearchCriteria\ActiveStoreAgreementsFilter $activeStoreAgreementsFilter, + CartRepositoryInterface $quoteRepository ) { $this->agreementsValidator = $agreementsValidator; $this->scopeConfiguration = $scopeConfiguration; $this->checkoutAgreementsList = $checkoutAgreementsList; $this->activeStoreAgreementsFilter = $activeStoreAgreementsFilter; + $this->quoteRepository = $quoteRepository; } /** + * Validates agreements before save payment information and order placing. + * * @param \Magento\Checkout\Api\PaymentInformationManagementInterface $subject * @param int $cartId * @param \Magento\Quote\Api\Data\PaymentInterface $paymentMethod @@ -74,13 +87,16 @@ public function beforeSavePaymentInformationAndPlaceOrder( } /** + * Check validation before saving the payment information + * * @param \Magento\Checkout\Api\PaymentInformationManagementInterface $subject * @param int $cartId * @param \Magento\Quote\Api\Data\PaymentInterface $paymentMethod * @param \Magento\Quote\Api\Data\AddressInterface|null $billingAddress - * @throws \Magento\Framework\Exception\CouldNotSaveException * @return void * @SuppressWarnings(PHPMD.UnusedFormalParameter) + * @throws \Magento\Framework\Exception\NoSuchEntityException + * @throws \Magento\Framework\Exception\CouldNotSaveException */ public function beforeSavePaymentInformation( \Magento\Checkout\Api\PaymentInformationManagementInterface $subject, @@ -88,12 +104,15 @@ public function beforeSavePaymentInformation( \Magento\Quote\Api\Data\PaymentInterface $paymentMethod, \Magento\Quote\Api\Data\AddressInterface $billingAddress = null ) { - if ($this->isAgreementEnabled()) { + $quote = $this->quoteRepository->getActive($cartId); + if ($this->isAgreementEnabled() && !$quote->getIsMultiShipping()) { $this->validateAgreements($paymentMethod); } } /** + * Validate agreements base on the payment method + * * @param \Magento\Quote\Api\Data\PaymentInterface $paymentMethod * @throws \Magento\Framework\Exception\CouldNotSaveException * @return void @@ -115,10 +134,11 @@ protected function validateAgreements(\Magento\Quote\Api\Data\PaymentInterface $ } /** - * Verify if agreement validation needed + * Verify if agreement validation needed. + * * @return bool */ - protected function isAgreementEnabled() + private function isAgreementEnabled() { $isAgreementsEnabled = $this->scopeConfiguration->isSetFlag( AgreementsProvider::PATH_ENABLED, diff --git a/app/code/Magento/CheckoutAgreements/Test/Mftf/ActionGroup/AdminAssertTermAbsentInGridActionGroup.xml b/app/code/Magento/CheckoutAgreements/Test/Mftf/ActionGroup/AdminAssertTermAbsentInGridActionGroup.xml new file mode 100644 index 0000000000000..9e95f27c47287 --- /dev/null +++ b/app/code/Magento/CheckoutAgreements/Test/Mftf/ActionGroup/AdminAssertTermAbsentInGridActionGroup.xml @@ -0,0 +1,21 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + /** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +--> + +<actionGroups xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/actionGroupSchema.xsd"> + <actionGroup name="AdminAssertTermAbsentInGridActionGroup"> + <arguments> + <argument name="termName" type="string"/> + </arguments> + <amOnPage url="{{AdminTermsPage.url}}" stepKey="onTermGridPage"/> + <waitForPageLoad stepKey="waitForPageLoad"/> + <fillField selector="{{AdminTermGridSection.filterByTermName}}" userInput="{{termName}}" stepKey="fillTermNameFilter"/> + <click selector="{{AdminTermGridSection.searchButton}}" stepKey="clickSearchButton"/> + <dontSee selector="{{AdminTermGridSection.firstRowConditionName}}" userInput="{{termName}}" stepKey="assertTermAbsentInGrid"/> + </actionGroup> +</actionGroups> diff --git a/app/code/Magento/CheckoutAgreements/Test/Mftf/ActionGroup/AdminTermsConditionsDeleteTermByNameActionGroup.xml b/app/code/Magento/CheckoutAgreements/Test/Mftf/ActionGroup/AdminTermsConditionsDeleteTermByNameActionGroup.xml new file mode 100644 index 0000000000000..9489fece37008 --- /dev/null +++ b/app/code/Magento/CheckoutAgreements/Test/Mftf/ActionGroup/AdminTermsConditionsDeleteTermByNameActionGroup.xml @@ -0,0 +1,17 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + /** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +--> + +<actionGroups xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/actionGroupSchema.xsd"> + <actionGroup name="AdminTermsConditionsDeleteTermByNameActionGroup"> + <click selector="{{AdminEditTermFormSection.delete}}" stepKey="clickDeleteButton"/> + <waitForElementVisible selector="{{AdminEditTermFormSection.acceptPopupButton}}" stepKey="waitForElement"/> + <click selector="{{AdminEditTermFormSection.acceptPopupButton}}" stepKey="clickDeleteOkButton"/> + <waitForText selector="{{AdminTermFormMessagesSection.successMessage}}" userInput="You deleted the condition." stepKey="seeSuccessMessage"/> + </actionGroup> +</actionGroups> diff --git a/app/code/Magento/CheckoutAgreements/Test/Mftf/ActionGroup/AdminTermsConditionsEditTermByNameActionGroup.xml b/app/code/Magento/CheckoutAgreements/Test/Mftf/ActionGroup/AdminTermsConditionsEditTermByNameActionGroup.xml new file mode 100644 index 0000000000000..8f2e65415ac22 --- /dev/null +++ b/app/code/Magento/CheckoutAgreements/Test/Mftf/ActionGroup/AdminTermsConditionsEditTermByNameActionGroup.xml @@ -0,0 +1,19 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + /** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +--> + +<actionGroups xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/actionGroupSchema.xsd"> + <actionGroup name="AdminTermsConditionsEditTermByNameActionGroup" extends="AdminTermsConditionsFilterGridByNameActionGroup"> + <annotations> + <description>Filters Terms and Conditions grid and opens the first result Edit page</description> + </annotations> + + <click selector="{{AdminTermGridSection.firstRowConditionId}}" stepKey="clickFirstRow"/> + <waitForPageLoad stepKey="waitForEditTermPageLoad"/> + </actionGroup> +</actionGroups> diff --git a/app/code/Magento/CheckoutAgreements/Test/Mftf/ActionGroup/AdminTermsConditionsFillTermEditFormActionGroup.xml b/app/code/Magento/CheckoutAgreements/Test/Mftf/ActionGroup/AdminTermsConditionsFillTermEditFormActionGroup.xml new file mode 100644 index 0000000000000..f32f1b11926a3 --- /dev/null +++ b/app/code/Magento/CheckoutAgreements/Test/Mftf/ActionGroup/AdminTermsConditionsFillTermEditFormActionGroup.xml @@ -0,0 +1,24 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + /** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +--> + +<actionGroups xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/actionGroupSchema.xsd"> + <actionGroup name="AdminTermsConditionsFillTermEditFormActionGroup"> + <arguments> + <argument name="term"/> + </arguments> + + <fillField selector="{{AdminNewTermFormSection.conditionName}}" userInput="{{term.name}}" stepKey="fillFieldConditionName"/> + <selectOption selector="{{AdminNewTermFormSection.isActive}}" userInput="{{term.isActive}}" stepKey="selectOptionIsActive"/> + <selectOption selector="{{AdminNewTermFormSection.isHtml}}" userInput="{{term.isHtml}}" stepKey="selectOptionIsHtml"/> + <selectOption selector="{{AdminNewTermFormSection.mode}}" userInput="{{term.mode}}" stepKey="selectOptionMode"/> + <selectOption selector="{{AdminNewTermFormSection.storeView}}" userInput="{{term.storeView}}" stepKey="selectOptionStoreView" /> + <fillField selector="{{AdminNewTermFormSection.checkboxText}}" userInput="{{term.checkboxText}}" stepKey="fillFieldCheckboxText"/> + <fillField selector="{{AdminNewTermFormSection.content}}" userInput="{{term.content}}" stepKey="fillFieldContent"/> + </actionGroup> +</actionGroups> diff --git a/app/code/Magento/CheckoutAgreements/Test/Mftf/ActionGroup/AdminTermsConditionsFilterGridByNameActionGroup.xml b/app/code/Magento/CheckoutAgreements/Test/Mftf/ActionGroup/AdminTermsConditionsFilterGridByNameActionGroup.xml new file mode 100644 index 0000000000000..2290d8152473c --- /dev/null +++ b/app/code/Magento/CheckoutAgreements/Test/Mftf/ActionGroup/AdminTermsConditionsFilterGridByNameActionGroup.xml @@ -0,0 +1,22 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + /** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +--> + +<actionGroups xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/actionGroupSchema.xsd"> + <actionGroup name="AdminTermsConditionsFilterGridByNameActionGroup"> + <annotations> + <description>Filters Terms and Conditions grid for name</description> + </annotations> + <arguments> + <argument name="termName" type="string"/> + </arguments> + + <fillField selector="{{AdminTermGridSection.filterByTermName}}" userInput="{{termName}}" stepKey="fillTermNameFilter"/> + <click selector="{{AdminTermGridSection.searchButton}}" stepKey="clickSearchButton"/> + </actionGroup> +</actionGroups> diff --git a/app/code/Magento/CheckoutAgreements/Test/Mftf/ActionGroup/AdminTermsConditionsOpenGridActionGroup.xml b/app/code/Magento/CheckoutAgreements/Test/Mftf/ActionGroup/AdminTermsConditionsOpenGridActionGroup.xml new file mode 100644 index 0000000000000..98a0a04e501fc --- /dev/null +++ b/app/code/Magento/CheckoutAgreements/Test/Mftf/ActionGroup/AdminTermsConditionsOpenGridActionGroup.xml @@ -0,0 +1,15 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + /** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +--> + +<actionGroups xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/actionGroupSchema.xsd"> + <actionGroup name="AdminTermsConditionsOpenGridActionGroup"> + <amOnPage url="{{AdminTermsPage.url}}" stepKey="onTermGridPage"/> + <waitForPageLoad stepKey="waitForPageLoad"/> + </actionGroup> +</actionGroups> diff --git a/app/code/Magento/CheckoutAgreements/Test/Mftf/ActionGroup/AdminTermsConditionsOpenNewTermPageActionGroup.xml b/app/code/Magento/CheckoutAgreements/Test/Mftf/ActionGroup/AdminTermsConditionsOpenNewTermPageActionGroup.xml new file mode 100644 index 0000000000000..a6aa97dd269d4 --- /dev/null +++ b/app/code/Magento/CheckoutAgreements/Test/Mftf/ActionGroup/AdminTermsConditionsOpenNewTermPageActionGroup.xml @@ -0,0 +1,15 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + /** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +--> + +<actionGroups xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/actionGroupSchema.xsd"> + <actionGroup name="AdminTermsConditionsOpenNewTermPageActionGroup"> + <amOnPage url="{{AdminNewTermPage.url}}" stepKey="amOnNewTermPage"/> + <waitForPageLoad stepKey="waitForAdminNewTermPageLoad"/> + </actionGroup> +</actionGroups> diff --git a/app/code/Magento/CheckoutAgreements/Test/Mftf/ActionGroup/AdminTermsConditionsSaveTermActionGroup.xml b/app/code/Magento/CheckoutAgreements/Test/Mftf/ActionGroup/AdminTermsConditionsSaveTermActionGroup.xml new file mode 100644 index 0000000000000..457c4495c28e3 --- /dev/null +++ b/app/code/Magento/CheckoutAgreements/Test/Mftf/ActionGroup/AdminTermsConditionsSaveTermActionGroup.xml @@ -0,0 +1,15 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + /** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +--> + +<actionGroups xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/actionGroupSchema.xsd"> + <actionGroup name="AdminTermsConditionsSaveTermActionGroup"> + <click selector="{{AdminNewTermFormSection.save}}" stepKey="saveTerm"/> + <see selector="{{AdminTermFormMessagesSection.successMessage}}" userInput="You saved the condition." stepKey="seeSuccessMessage"/> + </actionGroup> +</actionGroups> diff --git a/app/code/Magento/CheckoutAgreements/Test/Mftf/ActionGroup/AdminTermsConditionsUpdateTermActionGroup.xml b/app/code/Magento/CheckoutAgreements/Test/Mftf/ActionGroup/AdminTermsConditionsUpdateTermActionGroup.xml new file mode 100644 index 0000000000000..b7f92093356fa --- /dev/null +++ b/app/code/Magento/CheckoutAgreements/Test/Mftf/ActionGroup/AdminTermsConditionsUpdateTermActionGroup.xml @@ -0,0 +1,31 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + /** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +--> + +<actionGroups xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/actionGroupSchema.xsd"> + <actionGroup name="UpdateTermActionGroup"> + <arguments> + <argument name="term" /> + <argument name="updateTermData" /> + </arguments> + <amOnPage url="{{AdminTermsPage.url}}" stepKey="onTermGridPage"/> + <waitForPageLoad stepKey="waitForAdminTermsGridLoad"/> + <fillField selector="{{AdminTermGridSection.filterByTermName}}" userInput="{{term.name}}" stepKey="fillTermNameFilter"/> + <click selector="{{AdminTermGridSection.searchButton}}" stepKey="clickSearchButton"/> + + <fillField selector="{{AdminNewTermFormSection.conditionName}}" userInput="{{updateTermData.name}}" stepKey="fillFieldConditionName"/> + <selectOption selector="{{AdminNewTermFormSection.isActive}}" userInput="{{updateTermData.isActive}}" stepKey="selectOptionIsActive"/> + <selectOption selector="{{AdminNewTermFormSection.isHtml}}" userInput="{{updateTermData.isHtml}}" stepKey="selectOptionIsHtml"/> + <selectOption selector="{{AdminNewTermFormSection.mode}}" userInput="{{updateTermData.mode}}" stepKey="selectOptionMode"/> + <selectOption selector="{{AdminNewTermFormSection.storeView}}" userInput="{{updateTermData.storeView}}" stepKey="selectOptionStoreView" /> + <fillField selector="{{AdminNewTermFormSection.checkboxText}}" userInput="{{updateTermData.checkboxText}}" stepKey="fillFieldCheckboxText"/> + <fillField selector="{{AdminNewTermFormSection.content}}" userInput="{{updateTermData.content}}" stepKey="fillFieldContent"/> + <click selector="{{AdminNewTermFormSection.save}}" stepKey="saveTerm"/> + <see selector="{{AdminTermFormMessagesSection.successMessage}}" userInput="You saved the condition." stepKey="seeSuccessMessage"/> + </actionGroup> +</actionGroups> diff --git a/app/code/Magento/CheckoutAgreements/Test/Mftf/ActionGroup/AssertAdminTermsConditionsInGridActionGroup.xml b/app/code/Magento/CheckoutAgreements/Test/Mftf/ActionGroup/AssertAdminTermsConditionsInGridActionGroup.xml new file mode 100644 index 0000000000000..37f2761bf8e9a --- /dev/null +++ b/app/code/Magento/CheckoutAgreements/Test/Mftf/ActionGroup/AssertAdminTermsConditionsInGridActionGroup.xml @@ -0,0 +1,14 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + /** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +--> + +<actionGroups xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/actionGroupSchema.xsd"> + <actionGroup name="AssertAdminTermsConditionsInGridActionGroup" extends="AdminTermsConditionsFilterGridByNameActionGroup"> + <see selector="{{AdminTermGridSection.firstRowConditionName}}" userInput="{{termName}}" stepKey="assertTermInGrid" after="clickSearchButton"/> + </actionGroup> +</actionGroups> diff --git a/app/code/Magento/CheckoutAgreements/Test/Mftf/ActionGroup/AssertStorefrontTermAbsentInCheckoutActionGroup.xml b/app/code/Magento/CheckoutAgreements/Test/Mftf/ActionGroup/AssertStorefrontTermAbsentInCheckoutActionGroup.xml new file mode 100644 index 0000000000000..bf0c4f4b5a2c5 --- /dev/null +++ b/app/code/Magento/CheckoutAgreements/Test/Mftf/ActionGroup/AssertStorefrontTermAbsentInCheckoutActionGroup.xml @@ -0,0 +1,31 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + /** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +--> + +<actionGroups xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/actionGroupSchema.xsd"> + <actionGroup name="AssertStorefrontTermAbsentInCheckoutActionGroup"> + <arguments> + <argument name="termCheckboxText" type="string"/> + </arguments> + <!--Check if agreement is absent on checkout--> + <dontSee selector="{{StorefrontCheckoutAgreementsSection.checkoutAgreementButton}}" userInput="{{termCheckboxText}}" stepKey="seeTermInCheckout"/> + + <!--Checkout select Check/Money Order payment--> + <waitForLoadingMaskToDisappear stepKey="waitForPaymentPageRendering"/> + <waitForPageLoad stepKey="waitForPaymentRendering"/> + <conditionalClick selector="{{StorefrontCheckoutPaymentMethodSection.checkPaymentMethodByName('Check / Money order')}}" dependentSelector="{{StorefrontCheckoutPaymentMethodSection.checkPaymentMethodByName('Check / Money order')}}" visible="true" stepKey="selectCheckmoPaymentMethod"/> + <waitForLoadingMaskToDisappear stepKey="waitForLoadingMaskAfterPaymentMethodSelection"/> + + <!--Click Place Order button--> + <click selector="{{CheckoutPaymentSection.placeOrder}}" stepKey="clickPlaceOrder"/> + + <!--See success messages--> + <see selector="{{CheckoutSuccessMainSection.successTitle}}" userInput="Thank you for your purchase!" stepKey="seeSuccessTitle"/> + <see selector="{{CheckoutSuccessMainSection.orderNumberText}}" userInput="Your order # is: " stepKey="seeOrderNumber"/> + </actionGroup> +</actionGroups> diff --git a/app/code/Magento/CheckoutAgreements/Test/Mftf/ActionGroup/AssertStorefrontTermInCheckoutActionGroup.xml b/app/code/Magento/CheckoutAgreements/Test/Mftf/ActionGroup/AssertStorefrontTermInCheckoutActionGroup.xml new file mode 100644 index 0000000000000..bef0591a6e311 --- /dev/null +++ b/app/code/Magento/CheckoutAgreements/Test/Mftf/ActionGroup/AssertStorefrontTermInCheckoutActionGroup.xml @@ -0,0 +1,32 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + /** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +--> + +<actionGroups xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/actionGroupSchema.xsd"> + <actionGroup name="AssertStorefrontTermInCheckoutActionGroup"> + <arguments> + <argument name="termCheckboxText" type="string"/> + </arguments> + <!--Check if agreement is present on checkout and select it--> + <see selector="{{StorefrontCheckoutAgreementsSection.checkoutAgreementButton}}" userInput="{{termCheckboxText}}" stepKey="seeTermInCheckout"/> + <selectOption selector="{{StorefrontCheckoutAgreementsSection.checkoutAgreementCheckbox}}" userInput="{{termCheckboxText}}" stepKey="checkAgreement"/> + + <!--Checkout select Check/Money Order payment--> + <waitForLoadingMaskToDisappear stepKey="waitForPaymentPageRendering"/> + <waitForPageLoad stepKey="waitForPaymentRendering"/> + <conditionalClick selector="{{StorefrontCheckoutPaymentMethodSection.checkPaymentMethodByName('Check / Money order')}}" dependentSelector="{{StorefrontCheckoutPaymentMethodSection.checkPaymentMethodByName('Check / Money order')}}" visible="true" stepKey="selectCheckmoPaymentMethod"/> + <waitForLoadingMaskToDisappear stepKey="waitForLoadingMaskAfterPaymentMethodSelection"/> + + <!--Click Place Order button--> + <click selector="{{CheckoutPaymentSection.placeOrder}}" stepKey="clickPlaceOrder"/> + + <!--See success messages--> + <see selector="{{CheckoutSuccessMainSection.successTitle}}" userInput="Thank you for your purchase!" stepKey="seeSuccessTitle"/> + <see selector="{{CheckoutSuccessMainSection.orderNumberText}}" userInput="Your order # is: " stepKey="seeOrderNumber"/> + </actionGroup> +</actionGroups> diff --git a/app/code/Magento/CheckoutAgreements/Test/Mftf/ActionGroup/AssertStorefrontTermRequireMessageInMultishippingCheckoutActionGroup.xml b/app/code/Magento/CheckoutAgreements/Test/Mftf/ActionGroup/AssertStorefrontTermRequireMessageInMultishippingCheckoutActionGroup.xml new file mode 100644 index 0000000000000..c8f49adc30067 --- /dev/null +++ b/app/code/Magento/CheckoutAgreements/Test/Mftf/ActionGroup/AssertStorefrontTermRequireMessageInMultishippingCheckoutActionGroup.xml @@ -0,0 +1,41 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + /** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +--> + +<actionGroups xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/actionGroupSchema.xsd"> + <actionGroup name="AssertStorefrontTermRequireMessageInMultishippingCheckoutActionGroup"> + <arguments> + <argument name="termCheckboxText" type="string"/> + </arguments> + + <!--Go to Checkout Cart and proceed with multiple addresses--> + <amOnPage url="{{CheckoutCartPage.url}}" stepKey="goToCheckoutCart"/> + <waitForPageLoad stepKey="waitForCheckoutPageLoad"/> + <click selector="{{MultishippingSection.checkoutWithMultipleAddresses}}" stepKey="proceedMultishipping"/> + + <!--Procees do overview page--> + <click selector="{{StorefrontMultishippingCheckoutAddressesToolbarSection.goToShippingInformation}}" stepKey="clickGoToShippingInformation"/> + <waitForPageLoad stepKey="waitForCheckoutAddressToolbarPageLoad"/> + <click selector="{{StorefrontMultishippingCheckoutShippingToolbarSection.continueToBilling}}" stepKey="clickContinueToBilling"/> + <waitForPageLoad stepKey="waitForCheckoutShippingToolbarPageLoad"/> + <click selector="{{StorefrontMultishippingCheckoutBillingToolbarSection.goToReviewOrder}}" stepKey="clickGoToReviewOrder"/> + <waitForPageLoad stepKey="waitForCheckoutBillingToolbarPageLoad"/> + + <!--Check if agreement is present on checkout and select it--> + <scrollTo selector="{{StorefrontMultishippingCheckoutOverviewReviewSection.placeOrder}}" stepKey="scrollToButtonPlaceOrder"/> + <see selector="{{StorefrontCheckoutAgreementsSection.checkoutAgreementButton}}" userInput="{{termCheckboxText}}" stepKey="seeTermInCheckout"/> + <click selector="{{StorefrontMultishippingCheckoutOverviewReviewSection.placeOrder}}" stepKey="tryToPlaceOrder1"/> + <see selector="{{StorefrontCheckoutAgreementsSection.checkoutAgreementErrorMessage}}" userInput="This is a required field." stepKey="seeErrorMessage"/> + <selectOption selector="{{StorefrontCheckoutAgreementsSection.checkoutAgreementCheckbox}}" userInput="{{termCheckboxText}}" stepKey="checkAgreement"/> + <click selector="{{StorefrontMultishippingCheckoutOverviewReviewSection.placeOrder}}" stepKey="tryToPlaceOrder2"/> + <dontSee selector="{{StorefrontCheckoutAgreementsSection.checkoutAgreementErrorMessage}}" userInput="This is a required field." stepKey="dontSeeErrorMessage"/> + + <!--See success message--> + <see selector="{{CheckoutSuccessMainSection.successTitle}}" userInput="Thank you for your purchase!" stepKey="seeSuccessTitle"/> + </actionGroup> +</actionGroups> diff --git a/app/code/Magento/CheckoutAgreements/Test/Mftf/ActionGroup/StorefrontProcessCheckoutToPaymentActionGroup.xml b/app/code/Magento/CheckoutAgreements/Test/Mftf/ActionGroup/StorefrontProcessCheckoutToPaymentActionGroup.xml new file mode 100644 index 0000000000000..c40f24836c815 --- /dev/null +++ b/app/code/Magento/CheckoutAgreements/Test/Mftf/ActionGroup/StorefrontProcessCheckoutToPaymentActionGroup.xml @@ -0,0 +1,35 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + /** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +--> + +<actionGroups xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/actionGroupSchema.xsd"> + <actionGroup name="StorefrontProcessCheckoutToPaymentActionGroup"> + <!--Go to Checkout--> + <waitForElementNotVisible selector="{{StorefrontMinicartSection.emptyCart}}" stepKey="waitUpdateQuantity"/> + <wait time="5" stepKey="waitMinicartRendering"/> + <click selector="{{StorefrontMinicartSection.showCart}}" stepKey="clickCart"/> + <click selector="{{StorefrontMinicartSection.goToCheckout}}" stepKey="goToCheckout"/> + + <!--Process steps--> + <fillField selector="{{CheckoutShippingSection.email}}" userInput="{{CustomerEntityOne.email}}" stepKey="enterEmail"/> + <fillField selector="{{CheckoutShippingSection.firstName}}" userInput="{{CustomerEntityOne.firstname}}" stepKey="enterFirstName"/> + <fillField selector="{{CheckoutShippingSection.lastName}}" userInput="{{CustomerEntityOne.lastname}}" stepKey="enterLastName"/> + <fillField selector="{{CheckoutShippingSection.street}}" userInput="{{CustomerAddressSimple.street[0]}}" stepKey="enterStreet"/> + <fillField selector="{{CheckoutShippingSection.city}}" userInput="{{CustomerAddressSimple.city}}" stepKey="enterCity"/> + <selectOption selector="{{CheckoutShippingSection.region}}" userInput="{{CustomerAddressSimple.state}}" stepKey="selectRegion"/> + <fillField selector="{{CheckoutShippingSection.postcode}}" userInput="{{CustomerAddressSimple.postcode}}" stepKey="enterPostcode"/> + <fillField selector="{{CheckoutShippingSection.telephone}}" userInput="{{CustomerAddressSimple.telephone}}" stepKey="enterTelephone"/> + <waitForLoadingMaskToDisappear stepKey="waitForShippingMethods"/> + <click selector="{{CheckoutShippingMethodsSection.checkShippingMethodByName('')}}" stepKey="selectShippingMethod"/> + <waitForElement selector="{{CheckoutShippingSection.next}}" time="30" stepKey="waitForTheNextButton"/> + <waitForElementNotVisible selector=".loading-mask" time="300" stepKey="waitForProcessShippingMethod"/> + <click selector="{{CheckoutShippingSection.next}}" stepKey="clickNext"/> + <waitForElement selector="{{CheckoutPaymentSection.paymentSectionTitle}}" time="30" stepKey="waitForPaymentSectionLoaded"/> + <seeInCurrentUrl url="{{CheckoutPage.url}}/#payment" stepKey="assertCheckoutPaymentUrl"/> + </actionGroup> +</actionGroups> diff --git a/app/code/Magento/CheckoutAgreements/Test/Mftf/Data/TermData.xml b/app/code/Magento/CheckoutAgreements/Test/Mftf/Data/TermData.xml new file mode 100644 index 0000000000000..0172ffc771384 --- /dev/null +++ b/app/code/Magento/CheckoutAgreements/Test/Mftf/Data/TermData.xml @@ -0,0 +1,47 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + /** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +--> + +<entities xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:noNamespaceSchemaLocation="urn:magento:mftf:DataGenerator/etc/dataProfileSchema.xsd"> + <entity name="activeTextTerm" type="term"> + <data key="name" unique="suffix">name</data> + <data key="isActive">Enabled</data> + <data key="isHtml">Text</data> + <data key="mode">Manually</data> + <data key="storeView">Default Store View</data> + <data key="checkboxText" unique="suffix">test_checkbox</data> + <data key="content" unique="suffix">TestMessage</data> + </entity> + <entity name="activeHtmlTerm" type="term"> + <data key="name" unique="suffix">name</data> + <data key="isActive">Enabled</data> + <data key="isHtml">HTML</data> + <data key="mode">Manually</data> + <data key="storeView">Default Store View</data> + <data key="checkboxText" unique="suffix">test_checkbox</data> + <data key="content"><html></data> + </entity> + <entity name="disabledTextTerm" type="term"> + <data key="name" unique="suffix">name</data> + <data key="isActive">Disabled</data> + <data key="isHtml">Text</data> + <data key="mode">Manually</data> + <data key="storeView">Default Store View</data> + <data key="checkboxText" unique="suffix">test_checkbox</data> + <data key="content" unique="suffix">TestMessage</data> + </entity> + <entity name="disabledHtmlTerm" type="term"> + <data key="name" unique="suffix">name</data> + <data key="isActive">Disabled</data> + <data key="isHtml">HTML</data> + <data key="mode">Manually</data> + <data key="storeView">Default Store View</data> + <data key="checkboxText" unique="suffix">test_checkbox</data> + <data key="content"><html></data> + </entity> +</entities> diff --git a/app/code/Magento/CheckoutAgreements/Test/Mftf/Page/AdminEditTermPage.xml b/app/code/Magento/CheckoutAgreements/Test/Mftf/Page/AdminEditTermPage.xml new file mode 100644 index 0000000000000..3a13f7d7d5e60 --- /dev/null +++ b/app/code/Magento/CheckoutAgreements/Test/Mftf/Page/AdminEditTermPage.xml @@ -0,0 +1,14 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + /** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +--> + +<pages xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Page/etc/PageObject.xsd"> + <page name="AdminEditTermPage" url="checkout/agreement/edit/id/{{termId}}/" area="admin" module="Magento_CheckoutAgreements" parameterized="true"> + <section name="AdminEditTermFormSection"/> + </page> +</pages> diff --git a/app/code/Magento/CheckoutAgreements/Test/Mftf/Page/AdminNewTermPage.xml b/app/code/Magento/CheckoutAgreements/Test/Mftf/Page/AdminNewTermPage.xml new file mode 100644 index 0000000000000..51ec18a1d17e5 --- /dev/null +++ b/app/code/Magento/CheckoutAgreements/Test/Mftf/Page/AdminNewTermPage.xml @@ -0,0 +1,15 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + /** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +--> + +<pages xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Page/etc/PageObject.xsd"> + <page name="AdminNewTermPage" url="checkout/agreement/new" area="admin" module="Magento_CheckoutAgreements"> + <section name="AdminNewTermFormSection"/> + <section name="AdminTermFormMessagesSection"/> + </page> +</pages> diff --git a/app/code/Magento/CheckoutAgreements/Test/Mftf/Page/AdminTermsPage.xml b/app/code/Magento/CheckoutAgreements/Test/Mftf/Page/AdminTermsPage.xml new file mode 100644 index 0000000000000..4fb1a9704d9d0 --- /dev/null +++ b/app/code/Magento/CheckoutAgreements/Test/Mftf/Page/AdminTermsPage.xml @@ -0,0 +1,12 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- + /** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +--> +<pages xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="urn:magento:mftf:Page/etc/PageObject.xsd"> + <page name="AdminTermsPage" url="checkout/agreement/" area="admin" module="Magento_CheckoutAgreements"> + <section name="AdminTermGridSection"/> + </page> +</pages> diff --git a/app/code/Magento/CheckoutAgreements/Test/Mftf/Page/CheckoutPage.xml b/app/code/Magento/CheckoutAgreements/Test/Mftf/Page/CheckoutPage.xml new file mode 100644 index 0000000000000..fdb75ab4bc4c2 --- /dev/null +++ b/app/code/Magento/CheckoutAgreements/Test/Mftf/Page/CheckoutPage.xml @@ -0,0 +1,14 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + /** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +--> + +<pages xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Page/etc/PageObject.xsd"> + <page name="CheckoutPage" url="/checkout" area="storefront" module="Magento_CheckoutAgreements"> + <section name="StorefrontCheckoutAgreementsSection"/> + </page> +</pages> diff --git a/app/code/Magento/CheckoutAgreements/Test/Mftf/Section/AdminEditTermFormSection.xml b/app/code/Magento/CheckoutAgreements/Test/Mftf/Section/AdminEditTermFormSection.xml new file mode 100644 index 0000000000000..734a75b49c03a --- /dev/null +++ b/app/code/Magento/CheckoutAgreements/Test/Mftf/Section/AdminEditTermFormSection.xml @@ -0,0 +1,15 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + /** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +--> + +<sections xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Page/etc/SectionObject.xsd"> + <section name="AdminEditTermFormSection"> + <element name="delete" type="button" selector=".page-main-actions #delete"/> + <element name="acceptPopupButton" type="button" selector="button.action-primary.action-accept"/> + </section> +</sections> diff --git a/app/code/Magento/CheckoutAgreements/Test/Mftf/Section/AdminNewTermFormSection.xml b/app/code/Magento/CheckoutAgreements/Test/Mftf/Section/AdminNewTermFormSection.xml new file mode 100644 index 0000000000000..aeff29ef116fc --- /dev/null +++ b/app/code/Magento/CheckoutAgreements/Test/Mftf/Section/AdminNewTermFormSection.xml @@ -0,0 +1,22 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + /** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +--> + +<sections xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Page/etc/SectionObject.xsd"> + <section name="AdminNewTermFormSection"> + <element name="save" type="button" selector=".page-main-actions #save"/> + <element name="conditionName" type="input" selector="#name"/> + <element name="isActive" type="select" selector="#is_active"/> + <element name="isHtml" type="select" selector="#is_html"/> + <element name="mode" type="select" selector="#mode"/> + <element name="storeView" type="multiselect" selector="#stores"/> + <element name="checkboxText" type="input" selector="#checkbox_text"/> + <element name="content" type="textarea" selector="#content"/> + <element name="contentHeight" type="input" selector="#content_height"/> + </section> +</sections> diff --git a/app/code/Magento/CheckoutAgreements/Test/Mftf/Section/AdminTermFormMessagesSection.xml b/app/code/Magento/CheckoutAgreements/Test/Mftf/Section/AdminTermFormMessagesSection.xml new file mode 100644 index 0000000000000..a3a91855b0640 --- /dev/null +++ b/app/code/Magento/CheckoutAgreements/Test/Mftf/Section/AdminTermFormMessagesSection.xml @@ -0,0 +1,15 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + /** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +--> + +<sections xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Page/etc/SectionObject.xsd"> + <section name="AdminTermFormMessagesSection"> + <element name="successMessage" type="text" selector=".message-success"/> + <element name="errorMessage" type="text" selector=".message.message-error.error"/> + </section> +</sections> diff --git a/app/code/Magento/CheckoutAgreements/Test/Mftf/Section/AdminTermGridSection.xml b/app/code/Magento/CheckoutAgreements/Test/Mftf/Section/AdminTermGridSection.xml new file mode 100644 index 0000000000000..326f9dcce4320 --- /dev/null +++ b/app/code/Magento/CheckoutAgreements/Test/Mftf/Section/AdminTermGridSection.xml @@ -0,0 +1,18 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- + /** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +--> + +<sections xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="urn:magento:mftf:Page/etc/SectionObject.xsd"> + <section name="AdminTermGridSection"> + <element name="searchButton" type="button" selector="//div[contains(@class,'admin__data-grid-header')]//div[contains(@class,'admin__filter-actions')]/button[1]"/> + <element name="resetButton" type="button" selector="//div[contains(@class,'admin__data-grid-header')]//div[contains(@class,'admin__filter-actions')]/button[2]"/> + <element name="filterByTermName" type="input" selector="#agreementGrid_filter_name"/> + <element name="firstRowConditionName" type="text" selector=".data-grid>tbody>tr>td.col-name"/> + <element name="firstRowConditionId" type="text" selector=".data-grid>tbody>tr>td.col-id.col-agreement_id"/> + <element name="successMessage" type="text" selector=".message-success"/> + </section> +</sections> diff --git a/app/code/Magento/CheckoutAgreements/Test/Mftf/Section/StorefrontCheckoutAgreementsSection.xml b/app/code/Magento/CheckoutAgreements/Test/Mftf/Section/StorefrontCheckoutAgreementsSection.xml new file mode 100644 index 0000000000000..cb3e98949c622 --- /dev/null +++ b/app/code/Magento/CheckoutAgreements/Test/Mftf/Section/StorefrontCheckoutAgreementsSection.xml @@ -0,0 +1,16 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + /** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +--> + +<sections xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Page/etc/SectionObject.xsd"> + <section name="StorefrontCheckoutAgreementsSection"> + <element name="checkoutAgreementCheckbox" type="checkbox" selector="div.checkout-agreement.field.choice.required > input"/> + <element name="checkoutAgreementButton" type="button" selector="div.checkout-agreements-block > div > div > div > label > button > span"/> + <element name="checkoutAgreementErrorMessage" type="button" selector="div.checkout-agreement.field.choice.required > div.mage-error"/> + </section> +</sections> diff --git a/app/code/Magento/CheckoutAgreements/Test/Mftf/Test/AdminCreateActiveHtmlTermEntityTest.xml b/app/code/Magento/CheckoutAgreements/Test/Mftf/Test/AdminCreateActiveHtmlTermEntityTest.xml new file mode 100644 index 0000000000000..c597d3d660dc8 --- /dev/null +++ b/app/code/Magento/CheckoutAgreements/Test/Mftf/Test/AdminCreateActiveHtmlTermEntityTest.xml @@ -0,0 +1,64 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + /** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +--> + +<tests xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/testSchema.xsd"> + <test name="AdminCreateActiveHtmlTermEntityTest"> + <annotations> + <features value="CheckoutAgreements"/> + <stories value="Checkout agreements"/> + <title value="Create active HTML checkout agreement"/> + <description value="Admin should be able to create active HTML checkout agreement"/> + <severity value="CRITICAL"/> + <testCaseId value="MC-14668"/> + <group value="checkoutAgreements"/> + <group value="mtf_migrated"/> + </annotations> + <before> + <magentoCLI command="config:set checkout/options/enable_agreements 1" stepKey="setEnableTermsOnCheckout"/> + + <createData entity="SimpleTwo" stepKey="createProduct"/> + <magentoCron stepKey="runCronIndex" groups="index"/> + + <actionGroup ref="AdminLoginActionGroup" stepKey="adminLogin"/> + </before> + <after> + <magentoCLI command="config:set checkout/options/enable_agreements 0" stepKey="setDisableTermsOnCheckout"/> + <deleteData createDataKey="createProduct" stepKey="deletedProduct"/> + + <actionGroup ref="AdminTermsConditionsOpenGridActionGroup" stepKey="openTermsGridToDelete"/> + <actionGroup ref="AdminTermsConditionsEditTermByNameActionGroup" stepKey="openTermToDelete"> + <argument name="termName" value="{{activeHtmlTerm.name}}"/> + </actionGroup> + <actionGroup ref="AdminTermsConditionsDeleteTermByNameActionGroup" stepKey="deleteOpenedTerm"/> + + <actionGroup ref="AdminLogoutActionGroup" stepKey="adminLogout"/> + </after> + + <actionGroup ref="AdminTermsConditionsOpenNewTermPageActionGroup" stepKey="openNewTerm"/> + <actionGroup ref="AdminTermsConditionsFillTermEditFormActionGroup" stepKey="fillNewTerm"> + <argument name="term" value="activeHtmlTerm"/> + </actionGroup> + <actionGroup ref="AdminTermsConditionsSaveTermActionGroup" stepKey="saveNewTerm"/> + + <actionGroup ref="AdminTermsConditionsOpenGridActionGroup" stepKey="openTermsGrid"/> + <actionGroup ref="AssertAdminTermsConditionsInGridActionGroup" stepKey="assertTermInGrid"> + <argument name="termName" value="{{activeHtmlTerm.name}}"/> + </actionGroup> + + <openNewTab stepKey="openStorefrontTab"/> + <actionGroup ref="AddSimpleProductToCartActionGroup" stepKey="addProductToTheCart"> + <argument name="product" value="$$createProduct$$"/> + </actionGroup> + <actionGroup ref="StorefrontProcessCheckoutToPaymentActionGroup" stepKey="processCheckoutToThePaymentMethodsPage"/> + <actionGroup ref="AssertStorefrontTermInCheckoutActionGroup" stepKey="assertTermInCheckout"> + <argument name="termCheckboxText" value="{{activeHtmlTerm.checkboxText}}"/> + </actionGroup> + <closeTab stepKey="closeStorefrontTab"/> + </test> +</tests> diff --git a/app/code/Magento/CheckoutAgreements/Test/Mftf/Test/AdminCreateActiveTextTermEntityTest.xml b/app/code/Magento/CheckoutAgreements/Test/Mftf/Test/AdminCreateActiveTextTermEntityTest.xml new file mode 100644 index 0000000000000..a90c3536ec744 --- /dev/null +++ b/app/code/Magento/CheckoutAgreements/Test/Mftf/Test/AdminCreateActiveTextTermEntityTest.xml @@ -0,0 +1,38 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + /** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +--> + +<tests xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/testSchema.xsd"> + <test name="AdminCreateActiveTextTermEntityTest" extends="AdminCreateActiveHtmlTermEntityTest"> + <annotations> + <features value="CheckoutAgreements"/> + <stories value="Checkout agreements"/> + <title value="Create active text checkout agreement"/> + <description value="Admin should be able to create active text checkout agreement"/> + <severity value="CRITICAL"/> + <testCaseId value="MC-14667"/> + <group value="checkoutAgreements"/> + <group value="mtf_migrated"/> + </annotations> + <after> + <actionGroup ref="AdminTermsConditionsEditTermByNameActionGroup" stepKey="openTermToDelete"> + <argument name="termName" value="{{activeTextTerm.name}}"/> + </actionGroup> + </after> + + <actionGroup ref="AdminTermsConditionsFillTermEditFormActionGroup" stepKey="fillNewTerm"> + <argument name="term" value="activeTextTerm"/> + </actionGroup> + <actionGroup ref="AssertAdminTermsConditionsInGridActionGroup" stepKey="assertTermInGrid"> + <argument name="termName" value="{{activeTextTerm.name}}"/> + </actionGroup> + <actionGroup ref="AssertStorefrontTermInCheckoutActionGroup" stepKey="assertTermInCheckout"> + <argument name="termCheckboxText" value="{{activeTextTerm.checkboxText}}"/> + </actionGroup> + </test> +</tests> diff --git a/app/code/Magento/CheckoutAgreements/Test/Mftf/Test/AdminCreateDisabledTextTermEntityTest.xml b/app/code/Magento/CheckoutAgreements/Test/Mftf/Test/AdminCreateDisabledTextTermEntityTest.xml new file mode 100644 index 0000000000000..3c0c171fdfe9e --- /dev/null +++ b/app/code/Magento/CheckoutAgreements/Test/Mftf/Test/AdminCreateDisabledTextTermEntityTest.xml @@ -0,0 +1,64 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + /** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +--> + +<tests xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/testSchema.xsd"> + <test name="AdminCreateDisabledTextTermEntityTest"> + <annotations> + <features value="CheckoutAgreements"/> + <stories value="Checkout agreements"/> + <title value="Create disabled text checkout agreement"/> + <description value="Admin should be able to create disabled text checkout agreement"/> + <severity value="CRITICAL"/> + <testCaseId value="MC-14669"/> + <group value="checkoutAgreements"/> + <group value="mtf_migrated"/> + </annotations> + <before> + <magentoCLI command="config:set checkout/options/enable_agreements 1" stepKey="setEnableTermsOnCheckout"/> + + <createData entity="SimpleTwo" stepKey="createProduct"/> + <magentoCron stepKey="runCronIndex" groups="index"/> + + <actionGroup ref="AdminLoginActionGroup" stepKey="adminLogin"/> + </before> + <after> + <magentoCLI command="config:set checkout/options/enable_agreements 0" stepKey="setDisableTermsOnCheckout"/> + <deleteData createDataKey="createProduct" stepKey="deletedProduct"/> + + <actionGroup ref="AdminTermsConditionsOpenGridActionGroup" stepKey="openTermsGridToDelete"/> + <actionGroup ref="AdminTermsConditionsEditTermByNameActionGroup" stepKey="openTermToDelete"> + <argument name="termName" value="{{disabledTextTerm.name}}"/> + </actionGroup> + <actionGroup ref="AdminTermsConditionsDeleteTermByNameActionGroup" stepKey="deleteOpenedTerm"/> + + <actionGroup ref="AdminLogoutActionGroup" stepKey="adminLogout"/> + </after> + + <actionGroup ref="AdminTermsConditionsOpenNewTermPageActionGroup" stepKey="openNewTerm"/> + <actionGroup ref="AdminTermsConditionsFillTermEditFormActionGroup" stepKey="fillNewTerm"> + <argument name="term" value="disabledTextTerm"/> + </actionGroup> + <actionGroup ref="AdminTermsConditionsSaveTermActionGroup" stepKey="saveNewTerm"/> + + <actionGroup ref="AdminTermsConditionsOpenGridActionGroup" stepKey="openTermsGrid"/> + <actionGroup ref="AssertAdminTermsConditionsInGridActionGroup" stepKey="assertTermInGrid"> + <argument name="termName" value="{{disabledTextTerm.name}}"/> + </actionGroup> + + <openNewTab stepKey="openStorefrontTab"/> + <actionGroup ref="AddSimpleProductToCartActionGroup" stepKey="addProductToTheCart"> + <argument name="product" value="$$createProduct$$"/> + </actionGroup> + <actionGroup ref="StorefrontProcessCheckoutToPaymentActionGroup" stepKey="processCheckoutToThePaymentMethodsPage"/> + <actionGroup ref="AssertStorefrontTermAbsentInCheckoutActionGroup" stepKey="assertTermAbsentInCheckout"> + <argument name="termCheckboxText" value="{{disabledTextTerm.checkboxText}}"/> + </actionGroup> + <closeTab stepKey="closeStorefrontTab"/> + </test> +</tests> diff --git a/app/code/Magento/CheckoutAgreements/Test/Mftf/Test/AdminCreateEnabledTextTermOnMultishippingEntityTest.xml b/app/code/Magento/CheckoutAgreements/Test/Mftf/Test/AdminCreateEnabledTextTermOnMultishippingEntityTest.xml new file mode 100644 index 0000000000000..c60ef95c8edce --- /dev/null +++ b/app/code/Magento/CheckoutAgreements/Test/Mftf/Test/AdminCreateEnabledTextTermOnMultishippingEntityTest.xml @@ -0,0 +1,67 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + /** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +--> + +<tests xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/testSchema.xsd"> + <test name="AdminCreateEnabledTextTermOnMultishippingEntityTest"> + <annotations> + <features value="CheckoutAgreements"/> + <stories value="Checkout agreements"/> + <title value="Create enabled text checkout multishipping agreement"/> + <description value="Admin should be able to create enabled text checkout multishipping agreement"/> + <severity value="CRITICAL"/> + <testCaseId value="MC-14670"/> + <group value="checkoutAgreements"/> + <group value="mtf_migrated"/> + </annotations> + <before> + <magentoCLI command="config:set checkout/options/enable_agreements 1" stepKey="setEnableTermsOnCheckout"/> + + <createData entity="Simple_US_Customer_Multiple_Addresses" stepKey="createdCustomer"/> + <createData entity="SimpleTwo" stepKey="createdProduct1"/> + <createData entity="SimpleTwo" stepKey="createdProduct2"/> + <magentoCron stepKey="runCronIndex" groups="index"/> + + <actionGroup ref="AdminLoginActionGroup" stepKey="adminLogin"/> + </before> + <after> + <magentoCLI command="config:set checkout/options/enable_agreements 0" stepKey="setDisableTermsOnCheckout"/> + + <deleteData createDataKey="createdCustomer" stepKey="deletedCustomer"/> + <deleteData createDataKey="createdProduct1" stepKey="deletedProduct1"/> + <deleteData createDataKey="createdProduct2" stepKey="deletedProduct2"/> + + <actionGroup ref="AdminTermsConditionsOpenGridActionGroup" stepKey="openTermsGridToDelete"/> + <actionGroup ref="AdminTermsConditionsEditTermByNameActionGroup" stepKey="openTermToDelete"> + <argument name="termName" value="{{activeTextTerm.name}}"/> + </actionGroup> + <actionGroup ref="AdminTermsConditionsDeleteTermByNameActionGroup" stepKey="deleteOpenedTerm"/> + + <actionGroup ref="AdminLogoutActionGroup" stepKey="logout"/> + </after> + + <actionGroup ref="AdminTermsConditionsOpenNewTermPageActionGroup" stepKey="openNewTerm"/> + <actionGroup ref="AdminTermsConditionsFillTermEditFormActionGroup" stepKey="fillNewTerm"> + <argument name="term" value="activeTextTerm"/> + </actionGroup> + <actionGroup ref="AdminTermsConditionsSaveTermActionGroup" stepKey="saveNewTerm"/> + + <actionGroup ref="LoginToStorefrontActionGroup" stepKey="customerLogin"> + <argument name="Customer" value="$$createdCustomer$$" /> + </actionGroup> + <actionGroup ref="AddSimpleProductToCartActionGroup" stepKey="addProduct1ToTheCart"> + <argument name="product" value="$$createdProduct1$$"/> + </actionGroup> + <actionGroup ref="AddSimpleProductToCartActionGroup" stepKey="addProduct2ToTheCart"> + <argument name="product" value="$$createdProduct2$$"/> + </actionGroup> + <actionGroup ref="AssertStorefrontTermRequireMessageInMultishippingCheckoutActionGroup" stepKey="assertTermInCheckout"> + <argument name="termCheckboxText" value="{{activeTextTerm.checkboxText}}"/> + </actionGroup> + </test> +</tests> diff --git a/app/code/Magento/CheckoutAgreements/Test/Mftf/Test/AdminDeleteActiveTextTermEntityTest.xml b/app/code/Magento/CheckoutAgreements/Test/Mftf/Test/AdminDeleteActiveTextTermEntityTest.xml new file mode 100644 index 0000000000000..1e17dcc675573 --- /dev/null +++ b/app/code/Magento/CheckoutAgreements/Test/Mftf/Test/AdminDeleteActiveTextTermEntityTest.xml @@ -0,0 +1,55 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + /** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +--> + +<tests xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/testSchema.xsd"> + <test name="AdminDeleteActiveTextTermEntityTest"> + <annotations> + <features value="CheckoutAgreements"/> + <stories value="Checkout agreements"/> + <title value="Delete active text checkout agreement"/> + <description value="Admin should be able to delete active text checkout agreement"/> + <severity value="CRITICAL"/> + <testCaseId value="MC-14663"/> + <group value="checkoutAgreements"/> + <group value="mtf_migrated"/> + </annotations> + <before> + <magentoCLI command="config:set checkout/options/enable_agreements 1" stepKey="setEnableTermsOnCheckout"/> + <actionGroup ref="AdminLoginActionGroup" stepKey="LoginAsAdmin" /> + <createData entity="SimpleTwo" stepKey="createdProduct"/> + <actionGroup ref="AdminTermsConditionsOpenNewTermPageActionGroup" stepKey="openNewTerm"/> + <actionGroup ref="AdminTermsConditionsFillTermEditFormActionGroup" stepKey="createTerm"> + <argument name="term" value="activeTextTerm"/> + </actionGroup> + <actionGroup ref="AdminTermsConditionsSaveTermActionGroup" stepKey="saveNewTerm"/> + </before> + <after> + <magentoCLI command="config:set checkout/options/enable_agreements 0" stepKey="setDisableTermsOnCheckout"/> + <deleteData createDataKey="createdProduct" stepKey="deletedProduct"/> + <actionGroup ref="AdminLogoutActionGroup" stepKey="logout"/> + </after> + + <actionGroup ref="AdminTermsConditionsOpenGridActionGroup" stepKey="openTermsGridToDelete"/> + <actionGroup ref="AdminTermsConditionsEditTermByNameActionGroup" stepKey="openTermToDelete"> + <argument name="termName" value="{{activeTextTerm.name}}"/> + </actionGroup> + <actionGroup ref="AdminTermsConditionsDeleteTermByNameActionGroup" stepKey="deleteOpenedTerm"/> + <actionGroup ref="AdminAssertTermAbsentInGridActionGroup" stepKey="assertTermAbsentInGrid"> + <argument name="termName" value="{{activeTextTerm.name}}"/> + </actionGroup> + + <actionGroup ref="AddSimpleProductToCartActionGroup" stepKey="openProductPage"> + <argument name="product" value="$$createdProduct$$"/> + </actionGroup> + <actionGroup ref="StorefrontProcessCheckoutToPaymentActionGroup" stepKey="processCheckoutToThePaymentMethodsPage"/> + <actionGroup ref="AssertStorefrontTermAbsentInCheckoutActionGroup" stepKey="assertTermAbsentInCheckout"> + <argument name="termCheckboxText" value="{{activeTextTerm.checkboxText}}"/> + </actionGroup> + </test> +</tests> diff --git a/app/code/Magento/CheckoutAgreements/Test/Mftf/Test/AdminStoresTermsAndConditionsNavigateMenuTest.xml b/app/code/Magento/CheckoutAgreements/Test/Mftf/Test/AdminStoresTermsAndConditionsNavigateMenuTest.xml index d2d4cb9138bd5..3e680ce83f00e 100644 --- a/app/code/Magento/CheckoutAgreements/Test/Mftf/Test/AdminStoresTermsAndConditionsNavigateMenuTest.xml +++ b/app/code/Magento/CheckoutAgreements/Test/Mftf/Test/AdminStoresTermsAndConditionsNavigateMenuTest.xml @@ -20,10 +20,10 @@ <group value="mtf_migrated"/> </annotations> <before> - <actionGroup ref="LoginAsAdmin" stepKey="LoginAsAdmin"/> + <actionGroup ref="AdminLoginActionGroup" stepKey="LoginAsAdmin"/> </before> <after> - <actionGroup ref="logout" stepKey="logout"/> + <actionGroup ref="AdminLogoutActionGroup" stepKey="logout"/> </after> <actionGroup ref="AdminNavigateMenuActionGroup" stepKey="navigateToStoresTermsAndConditionsPage"> <argument name="menuUiId" value="{{AdminMenuStores.dataUiId}}"/> diff --git a/app/code/Magento/CheckoutAgreements/Test/Mftf/Test/AdminUpdateDisabledHtmlTermEntityTest.xml b/app/code/Magento/CheckoutAgreements/Test/Mftf/Test/AdminUpdateDisabledHtmlTermEntityTest.xml new file mode 100644 index 0000000000000..f9d60796d0424 --- /dev/null +++ b/app/code/Magento/CheckoutAgreements/Test/Mftf/Test/AdminUpdateDisabledHtmlTermEntityTest.xml @@ -0,0 +1,73 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + /** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +--> + +<tests xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/testSchema.xsd"> + <test name="AdminUpdateDisabledHtmlTermEntityTest"> + <annotations> + <features value="CheckoutAgreements"/> + <stories value="Checkout agreements"/> + <title value="Update disabled HTML checkout agreement"/> + <description value="Admin should be able to update disabled HTML checkout agreement"/> + <severity value="CRITICAL"/> + <testCaseId value="MC-14664"/> + <group value="checkoutAgreements"/> + <group value="mtf_migrated"/> + </annotations> + <before> + <magentoCLI command="config:set checkout/options/enable_agreements 1" stepKey="setEnableTermsOnCheckout"/> + + <createData entity="SimpleTwo" stepKey="createProduct"/> + <magentoCron stepKey="runCronIndex" groups="index"/> + + <actionGroup ref="AdminLoginActionGroup" stepKey="adminLogin"/> + </before> + <after> + <magentoCLI command="config:set checkout/options/enable_agreements 0" stepKey="setDisableTermsOnCheckout"/> + <deleteData createDataKey="createProduct" stepKey="deletedProduct"/> + + <actionGroup ref="AdminTermsConditionsOpenGridActionGroup" stepKey="openTermsGridToDelete"/> + <actionGroup ref="AdminTermsConditionsEditTermByNameActionGroup" stepKey="openTermToDelete"> + <argument name="termName" value="{{activeTextTerm.name}}"/> + </actionGroup> + <actionGroup ref="AdminTermsConditionsDeleteTermByNameActionGroup" stepKey="deleteOpenedTerm"/> + + <actionGroup ref="AdminLogoutActionGroup" stepKey="adminLogout"/> + </after> + + <actionGroup ref="AdminTermsConditionsOpenNewTermPageActionGroup" stepKey="openNewTerm"/> + <actionGroup ref="AdminTermsConditionsFillTermEditFormActionGroup" stepKey="fillNewTerm"> + <argument name="term" value="disabledHtmlTerm"/> + </actionGroup> + <actionGroup ref="AdminTermsConditionsSaveTermActionGroup" stepKey="saveNewTerm"/> + + <actionGroup ref="AdminTermsConditionsOpenGridActionGroup" stepKey="openTermsGrid"/> + <actionGroup ref="AdminTermsConditionsEditTermByNameActionGroup" stepKey="openUpdateTerm"> + <argument name="termName" value="{{disabledHtmlTerm.name}}"/> + </actionGroup> + <actionGroup ref="AdminTermsConditionsFillTermEditFormActionGroup" stepKey="fillUpdateTerm"> + <argument name="term" value="activeTextTerm"/> + </actionGroup> + <actionGroup ref="AdminTermsConditionsSaveTermActionGroup" stepKey="saveUpdateTerm"/> + + <actionGroup ref="AdminTermsConditionsOpenGridActionGroup" stepKey="openNewTermsGrid"/> + <actionGroup ref="AssertAdminTermsConditionsInGridActionGroup" stepKey="assertTermInGrid"> + <argument name="termName" value="{{activeTextTerm.name}}"/> + </actionGroup> + + <openNewTab stepKey="openStorefrontTab"/> + <actionGroup ref="AddSimpleProductToCartActionGroup" stepKey="addProductToTheCart"> + <argument name="product" value="$$createProduct$$"/> + </actionGroup> + <actionGroup ref="StorefrontProcessCheckoutToPaymentActionGroup" stepKey="processCheckoutToThePaymentMethodsPage"/> + <actionGroup ref="AssertStorefrontTermInCheckoutActionGroup" stepKey="assertTermInCheckout"> + <argument name="termCheckboxText" value="{{activeTextTerm.checkboxText}}"/> + </actionGroup> + <closeTab stepKey="closeStorefrontTab"/> + </test> +</tests> diff --git a/app/code/Magento/CheckoutAgreements/Test/Mftf/Test/AdminUpdateDisabledTextTermEntityTest.xml b/app/code/Magento/CheckoutAgreements/Test/Mftf/Test/AdminUpdateDisabledTextTermEntityTest.xml new file mode 100644 index 0000000000000..198a9fe3fc7b4 --- /dev/null +++ b/app/code/Magento/CheckoutAgreements/Test/Mftf/Test/AdminUpdateDisabledTextTermEntityTest.xml @@ -0,0 +1,45 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + /** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +--> + +<tests xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/testSchema.xsd"> + <test name="AdminUpdateDisabledTextTermEntityTest" extends="AdminUpdateDisabledHtmlTermEntityTest"> + <annotations> + <features value="CheckoutAgreements"/> + <stories value="Checkout agreements"/> + <title value="Update disabled Text checkout agreement"/> + <description value="Admin should be able to update disabled Text checkout agreement"/> + <severity value="CRITICAL"/> + <testCaseId value="MC-14665"/> + <group value="checkoutAgreements"/> + <group value="mtf_migrated"/> + </annotations> + + <after> + <actionGroup ref="AdminTermsConditionsEditTermByNameActionGroup" stepKey="openTermToDelete"> + <argument name="termName" value="{{activeHtmlTerm.name}}"/> + </actionGroup> + </after> + + <actionGroup ref="AdminTermsConditionsFillTermEditFormActionGroup" stepKey="fillNewTerm"> + <argument name="term" value="disabledTextTerm"/> + </actionGroup> + <actionGroup ref="AdminTermsConditionsEditTermByNameActionGroup" stepKey="openUpdateTerm"> + <argument name="termName" value="{{disabledTextTerm.name}}"/> + </actionGroup> + <actionGroup ref="AdminTermsConditionsFillTermEditFormActionGroup" stepKey="fillUpdateTerm"> + <argument name="term" value="activeHtmlTerm"/> + </actionGroup> + <actionGroup ref="AssertAdminTermsConditionsInGridActionGroup" stepKey="assertTermInGrid"> + <argument name="termName" value="{{activeHtmlTerm.name}}"/> + </actionGroup> + <actionGroup ref="AssertStorefrontTermInCheckoutActionGroup" stepKey="assertTermInCheckout"> + <argument name="termCheckboxText" value="{{activeHtmlTerm.checkboxText}}"/> + </actionGroup> + </test> +</tests> diff --git a/app/code/Magento/CheckoutAgreements/Test/Mftf/Test/AdminUpdateEnabledTextTermEntityTest.xml b/app/code/Magento/CheckoutAgreements/Test/Mftf/Test/AdminUpdateEnabledTextTermEntityTest.xml new file mode 100644 index 0000000000000..f82840bc07c7d --- /dev/null +++ b/app/code/Magento/CheckoutAgreements/Test/Mftf/Test/AdminUpdateEnabledTextTermEntityTest.xml @@ -0,0 +1,44 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + /** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +--> + +<tests xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/testSchema.xsd"> + <test name="AdminUpdateEnabledTextTermEntityTest" extends="AdminUpdateDisabledHtmlTermEntityTest"> + <annotations> + <features value="CheckoutAgreements"/> + <stories value="Checkout agreements"/> + <title value="Update enabled Text checkout agreement"/> + <description value="Admin should be able to update enabled Text checkout agreement"/> + <severity value="CRITICAL"/> + <testCaseId value="MC-14666"/> + <group value="checkoutAgreements"/> + <group value="mtf_migrated"/> + </annotations> + <after> + <actionGroup ref="AdminTermsConditionsEditTermByNameActionGroup" stepKey="openTermToDelete"> + <argument name="termName" value="{{disabledHtmlTerm.name}}"/> + </actionGroup> + </after> + + <actionGroup ref="AdminTermsConditionsFillTermEditFormActionGroup" stepKey="fillNewTerm"> + <argument name="term" value="activeTextTerm"/> + </actionGroup> + <actionGroup ref="AdminTermsConditionsEditTermByNameActionGroup" stepKey="openUpdateTerm"> + <argument name="termName" value="{{activeTextTerm.name}}"/> + </actionGroup> + <actionGroup ref="AdminTermsConditionsFillTermEditFormActionGroup" stepKey="fillUpdateTerm"> + <argument name="term" value="disabledHtmlTerm"/> + </actionGroup> + <actionGroup ref="AssertAdminTermsConditionsInGridActionGroup" stepKey="assertTermInGrid"> + <argument name="termName" value="{{disabledHtmlTerm.name}}"/> + </actionGroup> + <actionGroup ref="AssertStorefrontTermAbsentInCheckoutActionGroup" stepKey="assertTermInCheckout"> + <argument name="termCheckboxText" value="{{disabledHtmlTerm.checkboxText}}"/> + </actionGroup> + </test> +</tests> diff --git a/app/code/Magento/CheckoutAgreements/Test/Unit/Model/Checkout/Plugin/GuestValidationTest.php b/app/code/Magento/CheckoutAgreements/Test/Unit/Model/Checkout/Plugin/GuestValidationTest.php index 3d7b910c7abc5..b685d3edff275 100644 --- a/app/code/Magento/CheckoutAgreements/Test/Unit/Model/Checkout/Plugin/GuestValidationTest.php +++ b/app/code/Magento/CheckoutAgreements/Test/Unit/Model/Checkout/Plugin/GuestValidationTest.php @@ -10,7 +10,6 @@ use Magento\Store\Model\ScopeInterface; /** - * Class GuestValidationTest * @SuppressWarnings(PHPMD.CouplingBetweenObjects) */ class GuestValidationTest extends \PHPUnit\Framework\TestCase @@ -109,7 +108,7 @@ public function testBeforeSavePaymentInformationAndPlaceOrder() $this->paymentMock->expects(static::atLeastOnce()) ->method('getExtensionAttributes') ->willReturn($this->extensionAttributesMock); - $this->model->beforeSavePaymentInformation( + $this->model->beforeSavePaymentInformationAndPlaceOrder( $this->subjectMock, $cartId, $email, @@ -144,7 +143,7 @@ public function testBeforeSavePaymentInformationAndPlaceOrderIfAgreementsNotVali $this->paymentMock->expects(static::atLeastOnce()) ->method('getExtensionAttributes') ->willReturn($this->extensionAttributesMock); - $this->model->beforeSavePaymentInformation( + $this->model->beforeSavePaymentInformationAndPlaceOrder( $this->subjectMock, $cartId, $email, @@ -156,36 +155,4 @@ public function testBeforeSavePaymentInformationAndPlaceOrderIfAgreementsNotVali "The order wasn't placed. First, agree to the terms and conditions, then try placing your order again." ); } - - public function testBeforeSavePaymentInformation() - { - $cartId = 100; - $email = 'email@example.com'; - $agreements = [1, 2, 3]; - $this->scopeConfigMock - ->expects($this->once()) - ->method('isSetFlag') - ->with(AgreementsProvider::PATH_ENABLED, ScopeInterface::SCOPE_STORE) - ->willReturn(true); - $searchCriteriaMock = $this->createMock(\Magento\Framework\Api\SearchCriteria::class); - $this->agreementsFilterMock->expects($this->once()) - ->method('buildSearchCriteria') - ->willReturn($searchCriteriaMock); - $this->checkoutAgreementsListMock->expects($this->once()) - ->method('getList') - ->with($searchCriteriaMock) - ->willReturn([1]); - $this->extensionAttributesMock->expects($this->once())->method('getAgreementIds')->willReturn($agreements); - $this->agreementsValidatorMock->expects($this->once())->method('isValid')->with($agreements)->willReturn(true); - $this->paymentMock->expects(static::atLeastOnce()) - ->method('getExtensionAttributes') - ->willReturn($this->extensionAttributesMock); - $this->model->beforeSavePaymentInformation( - $this->subjectMock, - $cartId, - $email, - $this->paymentMock, - $this->addressMock - ); - } } diff --git a/app/code/Magento/CheckoutAgreements/Test/Unit/Model/Checkout/Plugin/ValidationTest.php b/app/code/Magento/CheckoutAgreements/Test/Unit/Model/Checkout/Plugin/ValidationTest.php index 7f11fad202401..64c91edb4e27d 100644 --- a/app/code/Magento/CheckoutAgreements/Test/Unit/Model/Checkout/Plugin/ValidationTest.php +++ b/app/code/Magento/CheckoutAgreements/Test/Unit/Model/Checkout/Plugin/ValidationTest.php @@ -10,7 +10,7 @@ use Magento\Store\Model\ScopeInterface; /** - * Class ValidationTest + * Class ValidationTest validates the agreement based on the payment method * @SuppressWarnings(PHPMD.CouplingBetweenObjects) */ class ValidationTest extends \PHPUnit\Framework\TestCase @@ -60,12 +60,24 @@ class ValidationTest extends \PHPUnit\Framework\TestCase */ private $agreementsFilterMock; + /** + * @var \PHPUnit_Framework_MockObject_MockObject + */ + private $quoteMock; + + /** + * @var \PHPUnit_Framework_MockObject_MockObject + */ + private $quoteRepositoryMock; + protected function setUp() { $this->agreementsValidatorMock = $this->createMock(\Magento\Checkout\Api\AgreementsValidatorInterface::class); $this->subjectMock = $this->createMock(\Magento\Checkout\Api\PaymentInformationManagementInterface::class); $this->paymentMock = $this->createMock(\Magento\Quote\Api\Data\PaymentInterface::class); $this->addressMock = $this->createMock(\Magento\Quote\Api\Data\AddressInterface::class); + $this->quoteMock = $this->createPartialMock(\Magento\Quote\Model\Quote::class, ['getIsMultiShipping']); + $this->quoteRepositoryMock = $this->createMock(\Magento\Quote\Api\CartRepositoryInterface::class); $this->extensionAttributesMock = $this->createPartialMock( \Magento\Quote\Api\Data\PaymentExtension::class, ['getAgreementIds'] @@ -82,7 +94,8 @@ protected function setUp() $this->agreementsValidatorMock, $this->scopeConfigMock, $this->checkoutAgreementsListMock, - $this->agreementsFilterMock + $this->agreementsFilterMock, + $this->quoteRepositoryMock ); } @@ -96,6 +109,15 @@ public function testBeforeSavePaymentInformationAndPlaceOrder() ->with(AgreementsProvider::PATH_ENABLED, ScopeInterface::SCOPE_STORE) ->willReturn(true); $searchCriteriaMock = $this->createMock(\Magento\Framework\Api\SearchCriteria::class); + $this->quoteMock + ->expects($this->once()) + ->method('getIsMultiShipping') + ->willReturn(false); + $this->quoteRepositoryMock + ->expects($this->once()) + ->method('getActive') + ->with($cartId) + ->willReturn($this->quoteMock); $this->agreementsFilterMock->expects($this->once()) ->method('buildSearchCriteria') ->willReturn($searchCriteriaMock); @@ -108,7 +130,12 @@ public function testBeforeSavePaymentInformationAndPlaceOrder() $this->paymentMock->expects(static::atLeastOnce()) ->method('getExtensionAttributes') ->willReturn($this->extensionAttributesMock); - $this->model->beforeSavePaymentInformation($this->subjectMock, $cartId, $this->paymentMock, $this->addressMock); + $this->model->beforeSavePaymentInformation( + $this->subjectMock, + $cartId, + $this->paymentMock, + $this->addressMock + ); } /** @@ -124,6 +151,15 @@ public function testBeforeSavePaymentInformationAndPlaceOrderIfAgreementsNotVali ->with(AgreementsProvider::PATH_ENABLED, ScopeInterface::SCOPE_STORE) ->willReturn(true); $searchCriteriaMock = $this->createMock(\Magento\Framework\Api\SearchCriteria::class); + $this->quoteMock + ->expects($this->once()) + ->method('getIsMultiShipping') + ->willReturn(false); + $this->quoteRepositoryMock + ->expects($this->once()) + ->method('getActive') + ->with($cartId) + ->willReturn($this->quoteMock); $this->agreementsFilterMock->expects($this->once()) ->method('buildSearchCriteria') ->willReturn($searchCriteriaMock); @@ -136,7 +172,12 @@ public function testBeforeSavePaymentInformationAndPlaceOrderIfAgreementsNotVali $this->paymentMock->expects(static::atLeastOnce()) ->method('getExtensionAttributes') ->willReturn($this->extensionAttributesMock); - $this->model->beforeSavePaymentInformation($this->subjectMock, $cartId, $this->paymentMock, $this->addressMock); + $this->model->beforeSavePaymentInformation( + $this->subjectMock, + $cartId, + $this->paymentMock, + $this->addressMock + ); $this->expectExceptionMessage( "The order wasn't placed. First, agree to the terms and conditions, then try placing your order again." @@ -152,6 +193,15 @@ public function testBeforeSavePaymentInformation() ->method('isSetFlag') ->with(AgreementsProvider::PATH_ENABLED, ScopeInterface::SCOPE_STORE) ->willReturn(true); + $this->quoteMock + ->expects($this->once()) + ->method('getIsMultiShipping') + ->willReturn(false); + $this->quoteRepositoryMock + ->expects($this->once()) + ->method('getActive') + ->with($cartId) + ->willReturn($this->quoteMock); $searchCriteriaMock = $this->createMock(\Magento\Framework\Api\SearchCriteria::class); $this->agreementsFilterMock->expects($this->once()) ->method('buildSearchCriteria') diff --git a/app/code/Magento/CheckoutAgreements/etc/adminhtml/system.xml b/app/code/Magento/CheckoutAgreements/etc/adminhtml/system.xml index 64ffb2b5dc21a..496f3fec70e5e 100644 --- a/app/code/Magento/CheckoutAgreements/etc/adminhtml/system.xml +++ b/app/code/Magento/CheckoutAgreements/etc/adminhtml/system.xml @@ -9,7 +9,7 @@ <system> <section id="checkout"> <group id="options"> - <field id="enable_agreements" translate="label" type="select" sortOrder="20" showInDefault="1" showInWebsite="1" showInStore="1"> + <field id="enable_agreements" translate="label" type="select" sortOrder="20" showInDefault="1" showInWebsite="1" showInStore="1" canRestore="1"> <label>Enable Terms and Conditions</label> <source_model>Magento\Config\Model\Config\Source\Yesno</source_model> </field> diff --git a/app/code/Magento/CheckoutAgreements/etc/config.xml b/app/code/Magento/CheckoutAgreements/etc/config.xml new file mode 100644 index 0000000000000..e2932af0c25cb --- /dev/null +++ b/app/code/Magento/CheckoutAgreements/etc/config.xml @@ -0,0 +1,16 @@ +<?xml version="1.0"?> +<!-- +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +--> +<config xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="urn:magento:module:Magento_Store:etc/config.xsd"> + <default> + <checkout> + <options> + <enable_agreements>0</enable_agreements> + </options> + </checkout> + </default> +</config> diff --git a/app/code/Magento/CheckoutAgreements/registration.php b/app/code/Magento/CheckoutAgreements/registration.php index b4d4ce25d2826..15562341a94ae 100644 --- a/app/code/Magento/CheckoutAgreements/registration.php +++ b/app/code/Magento/CheckoutAgreements/registration.php @@ -4,6 +4,6 @@ * See COPYING.txt for license details. */ -use \Magento\Framework\Component\ComponentRegistrar; +use Magento\Framework\Component\ComponentRegistrar; ComponentRegistrar::register(ComponentRegistrar::MODULE, 'Magento_CheckoutAgreements', __DIR__); diff --git a/app/code/Magento/Cms/Api/BlockRepositoryInterface.php b/app/code/Magento/Cms/Api/BlockRepositoryInterface.php index b713ca91ea852..4b180b5153295 100644 --- a/app/code/Magento/Cms/Api/BlockRepositoryInterface.php +++ b/app/code/Magento/Cms/Api/BlockRepositoryInterface.php @@ -24,7 +24,7 @@ public function save(Data\BlockInterface $block); /** * Retrieve block. * - * @param int $blockId + * @param string $blockId * @return \Magento\Cms\Api\Data\BlockInterface * @throws \Magento\Framework\Exception\LocalizedException */ @@ -51,7 +51,7 @@ public function delete(Data\BlockInterface $block); /** * Delete block by ID. * - * @param int $blockId + * @param string $blockId * @return bool true on success * @throws \Magento\Framework\Exception\NoSuchEntityException * @throws \Magento\Framework\Exception\LocalizedException diff --git a/app/code/Magento/Cms/Block/Adminhtml/Wysiwyg/Images/Content/Uploader.php b/app/code/Magento/Cms/Block/Adminhtml/Wysiwyg/Images/Content/Uploader.php index cf0fc34b217e4..07b66fc53fa84 100644 --- a/app/code/Magento/Cms/Block/Adminhtml/Wysiwyg/Images/Content/Uploader.php +++ b/app/code/Magento/Cms/Block/Adminhtml/Wysiwyg/Images/Content/Uploader.php @@ -35,6 +35,8 @@ public function __construct( } /** + * Constructor + * * @return void */ protected function _construct() @@ -49,7 +51,7 @@ protected function _construct() $files[] = '*.' . $ext; } $this->getConfig()->setUrl( - $this->_urlBuilder->addSessionParam()->getUrl('cms/*/upload', ['type' => $type]) + $this->_urlBuilder->getUrl('cms/*/upload', ['type' => $type]) )->setFileField( 'image' )->setFilters( diff --git a/app/code/Magento/Cms/Controller/Adminhtml/Wysiwyg/Directive.php b/app/code/Magento/Cms/Controller/Adminhtml/Wysiwyg/Directive.php index 97d0b35a2354f..a3370b2666264 100644 --- a/app/code/Magento/Cms/Controller/Adminhtml/Wysiwyg/Directive.php +++ b/app/code/Magento/Cms/Controller/Adminhtml/Wysiwyg/Directive.php @@ -21,6 +21,7 @@ use Magento\Framework\Controller\Result\RawFactory; use Magento\Backend\App\Action\Context; use Magento\Framework\App\ObjectManager; +use Magento\Framework\Filesystem\Driver\File; /** * Process template text for wysiwyg editor. @@ -67,6 +68,11 @@ class Directive extends Action implements HttpGetActionInterface */ private $filter; + /** + * @var File + */ + private $file; + /** * Constructor * @@ -77,6 +83,7 @@ class Directive extends Action implements HttpGetActionInterface * @param LoggerInterface|null $logger * @param Config|null $config * @param Filter|null $filter + * @param File|null $file */ public function __construct( Context $context, @@ -85,7 +92,8 @@ public function __construct( AdapterFactory $adapterFactory = null, LoggerInterface $logger = null, Config $config = null, - Filter $filter = null + Filter $filter = null, + File $file = null ) { parent::__construct($context); $this->urlDecoder = $urlDecoder; @@ -94,6 +102,7 @@ public function __construct( $this->logger = $logger ?: ObjectManager::getInstance()->get(LoggerInterface::class); $this->config = $config ?: ObjectManager::getInstance()->get(Config::class); $this->filter = $filter ?: ObjectManager::getInstance()->get(Filter::class); + $this->file = $file ?: ObjectManager::getInstance()->get(File::class); } /** @@ -127,6 +136,15 @@ public function execute() $this->logger->warning($e); } } + $mimeType = $image->getMimeType(); + unset($image); + // To avoid issues with PNG images with alpha blending we return raw file + // after validation as an image source instead of generating the new PNG image + // with image adapter + $content = $this->file->fileGetContents($imagePath); + $resultRaw->setHeader('Content-Type', $mimeType); + $resultRaw->setContents($content); + return $resultRaw; } } diff --git a/app/code/Magento/Cms/Controller/Adminhtml/Wysiwyg/Images/DeleteFiles.php b/app/code/Magento/Cms/Controller/Adminhtml/Wysiwyg/Images/DeleteFiles.php index 6f57efad41e75..fa873930aaade 100644 --- a/app/code/Magento/Cms/Controller/Adminhtml/Wysiwyg/Images/DeleteFiles.php +++ b/app/code/Magento/Cms/Controller/Adminhtml/Wysiwyg/Images/DeleteFiles.php @@ -5,8 +5,8 @@ */ namespace Magento\Cms\Controller\Adminhtml\Wysiwyg\Images; -use Magento\Framework\App\Filesystem\DirectoryList; use Magento\Framework\App\Action\HttpPostActionInterface; +use Magento\Framework\App\Filesystem\DirectoryList; /** * Delete image files. @@ -60,10 +60,15 @@ public function __construct( */ public function execute() { + $resultJson = $this->resultJsonFactory->create(); + + if (!$this->getRequest()->isPost()) { + $result = ['error' => true, 'message' => __('Wrong request.')]; + /** @var \Magento\Framework\Controller\Result\Json $resultJson */ + return $resultJson->setData($result); + } + try { - if (!$this->getRequest()->isPost()) { - throw new \Exception('Wrong request.'); - } $files = $this->getRequest()->getParam('files'); /** @var $helper \Magento\Cms\Helper\Wysiwyg\Images */ @@ -79,17 +84,16 @@ public function execute() /** @var \Magento\Framework\Filesystem $filesystem */ $filesystem = $this->_objectManager->get(\Magento\Framework\Filesystem::class); $dir = $filesystem->getDirectoryRead(DirectoryList::MEDIA); - $filePath = $path . '/' . \Magento\Framework\File\Uploader::getCorrectFileName($file); + $filePath = $path . '/' . $file; if ($dir->isFile($dir->getRelativePath($filePath)) && !preg_match('#.htaccess#', $file)) { $this->getStorage()->deleteFile($filePath); } } - + return $this->resultRawFactory->create(); + // phpcs:ignore Magento2.Exceptions.ThrowCatch } catch (\Exception $e) { $result = ['error' => true, 'message' => $e->getMessage()]; - /** @var \Magento\Framework\Controller\Result\Json $resultJson */ - $resultJson = $this->resultJsonFactory->create(); return $resultJson->setData($result); } diff --git a/app/code/Magento/Cms/Controller/Page/View.php b/app/code/Magento/Cms/Controller/Page/View.php index 9d5785450ec71..da35e7a9ea9e4 100644 --- a/app/code/Magento/Cms/Controller/Page/View.php +++ b/app/code/Magento/Cms/Controller/Page/View.php @@ -1,50 +1,78 @@ <?php /** - * * Copyright © Magento, Inc. All rights reserved. * See COPYING.txt for license details. */ +declare(strict_types=1); + namespace Magento\Cms\Controller\Page; -use Magento\Framework\App\Action\HttpPostActionInterface; +use Magento\Cms\Helper\Page as PageHelper; use Magento\Framework\App\Action\HttpGetActionInterface; -use Magento\Framework\App\Action\Action; +use Magento\Framework\App\Action\HttpPostActionInterface; +use Magento\Framework\App\RequestInterface; +use Magento\Framework\Controller\Result\ForwardFactory; +use Magento\Framework\Controller\ResultInterface; /** * Custom page for storefront. Needs to be accessible by POST because of the store switching. */ -class View extends Action implements HttpGetActionInterface, HttpPostActionInterface +class View implements HttpGetActionInterface, HttpPostActionInterface { /** - * @var \Magento\Framework\Controller\Result\ForwardFactory + * @var ForwardFactory */ protected $resultForwardFactory; /** - * @param \Magento\Framework\App\Action\Context $context - * @param \Magento\Framework\Controller\Result\ForwardFactory $resultForwardFactory + * @var RequestInterface + */ + private $request; + + /** + * @var PageHelper + */ + private $pageHelper; + + /** + * @param RequestInterface $request + * @param PageHelper $pageHelper + * @param ForwardFactory $resultForwardFactory */ public function __construct( - \Magento\Framework\App\Action\Context $context, - \Magento\Framework\Controller\Result\ForwardFactory $resultForwardFactory + RequestInterface $request, + PageHelper $pageHelper, + ForwardFactory $resultForwardFactory ) { + $this->request = $request; + $this->pageHelper = $pageHelper; $this->resultForwardFactory = $resultForwardFactory; - parent::__construct($context); } /** * View CMS page action * - * @return \Magento\Framework\Controller\ResultInterface + * @return ResultInterface */ public function execute() { - $pageId = $this->getRequest()->getParam('page_id', $this->getRequest()->getParam('id', false)); - $resultPage = $this->_objectManager->get(\Magento\Cms\Helper\Page::class)->prepareResultPage($this, $pageId); + $resultPage = $this->pageHelper->prepareResultPage($this, $this->getPageId()); if (!$resultPage) { $resultForward = $this->resultForwardFactory->create(); return $resultForward->forward('noroute'); } return $resultPage; } + + /** + * Returns Page ID if provided or null + * + * @return int|null + */ + private function getPageId(): ?int + { + $id = $this->request->getParam('page_id') ?? $this->request->getParam('id'); + + return $id ? (int)$id : null; + } } diff --git a/app/code/Magento/Cms/Helper/Page.php b/app/code/Magento/Cms/Helper/Page.php index 39b292bf07239..d899a5cea985a 100644 --- a/app/code/Magento/Cms/Helper/Page.php +++ b/app/code/Magento/Cms/Helper/Page.php @@ -8,17 +8,20 @@ use Magento\Cms\Model\Page\CustomLayoutManagerInterface; use Magento\Cms\Model\Page\CustomLayoutRepositoryInterface; use Magento\Cms\Model\Page\IdentityMap; -use Magento\Framework\App\Action\Action; +use Magento\Framework\App\ActionInterface; +use Magento\Framework\App\Helper\AbstractHelper; use Magento\Framework\App\ObjectManager; use Magento\Framework\Exception\NoSuchEntityException; +use Magento\Framework\View\Result\Page as ResultPage; /** * CMS Page Helper + * * @SuppressWarnings(PHPMD.CouplingBetweenObjects) * @SuppressWarnings(PHPMD.CyclomaticComplexity) * @SuppressWarnings(PHPMD.NPathComplexity) */ -class Page extends \Magento\Framework\App\Helper\AbstractHelper +class Page extends AbstractHelper { /** * CMS no-route config path @@ -146,14 +149,14 @@ public function __construct( /** * Return result CMS page * - * @param Action $action + * @param ActionInterface $action * @param int $pageId - * @return \Magento\Framework\View\Result\Page|bool + * @return ResultPage|bool */ - public function prepareResultPage(Action $action, $pageId = null) + public function prepareResultPage(ActionInterface $action, $pageId = null) { if ($pageId !== null && $pageId !== $this->_page->getId()) { - $delimiterPosition = strrpos($pageId, '|'); + $delimiterPosition = strrpos((string)$pageId, '|'); if ($delimiterPosition) { $pageId = substr($pageId, 0, $delimiterPosition); } @@ -180,7 +183,7 @@ public function prepareResultPage(Action $action, $pageId = null) $this->_design->setDesignTheme($this->_page->getCustomTheme()); } } - /** @var \Magento\Framework\View\Result\Page $resultPage */ + /** @var ResultPage $resultPage */ $resultPage = $this->resultPageFactory->create(); $this->setLayoutType($inRange, $resultPage); $resultPage->addHandle('cms_page_view'); @@ -247,8 +250,8 @@ public function getPageUrl($pageId = null) * Set layout type * * @param bool $inRange - * @param \Magento\Framework\View\Result\Page $resultPage - * @return \Magento\Framework\View\Result\Page + * @param ResultPage $resultPage + * @return ResultPage */ protected function setLayoutType($inRange, $resultPage) { diff --git a/app/code/Magento/Cms/Helper/Wysiwyg/Images.php b/app/code/Magento/Cms/Helper/Wysiwyg/Images.php index cd3473c6bab87..e42bb0143f6bf 100644 --- a/app/code/Magento/Cms/Helper/Wysiwyg/Images.php +++ b/app/code/Magento/Cms/Helper/Wysiwyg/Images.php @@ -224,8 +224,7 @@ public function getImageHtmlDeclaration($filename, $renderAsTag = false) } /** - * Return path of the current selected directory or root directory for startup - * Try to create target directory if it doesn't exist + * Return path of the root directory for startup. Also try to create target directory if it doesn't exist * * @return string * @throws \Magento\Framework\Exception\LocalizedException @@ -241,18 +240,40 @@ public function getCurrentPath() $currentPath = $path; } } + + $currentTreePath = $this->_getRequest()->getParam('current_tree_path'); + if ($currentTreePath) { + $currentTreePath = $this->convertIdToPath($currentTreePath); + $this->createSubDirIfNotExist($currentTreePath); + } + + $this->_currentPath = $currentPath; + } + + return $this->_currentPath; + } + + /** + * Create subdirectory if doesn't exist + * + * @param string $absPath Path of subdirectory to create + * @throws \Magento\Framework\Exception\LocalizedException + */ + private function createSubDirIfNotExist(string $absPath) + { + $relPath = $this->_directory->getRelativePath($absPath); + if (!$this->_directory->isExist($relPath)) { try { - $currentDir = $this->_directory->getRelativePath($currentPath); - if (!$this->_directory->isExist($currentDir)) { - $this->_directory->create($currentDir); - } + $this->_directory->create($relPath); } catch (\Magento\Framework\Exception\FileSystemException $e) { - $message = __('The directory %1 is not writable by server.', $currentPath); + $message = __( + 'Can\'t create %1 as subdirectory of %2, you might have some permission issue.', + $relPath, + $this->_directory->getAbsolutePath() + ); throw new \Magento\Framework\Exception\LocalizedException($message); } - $this->_currentPath = $currentPath; } - return $this->_currentPath; } /** @@ -294,6 +315,8 @@ public function idEncode($string) public function idDecode($string) { $string = strtr($string, ':_-', '+/='); + + // phpcs:ignore Magento2.Functions.DiscouragedFunction return base64_decode($string); } @@ -315,7 +338,7 @@ public function getShortFilename($filename, $maxLength = 20) /** * Set user-traversable image directory subpath relative to media directory and relative to nested storage root * - * @var string $subpath + * @param string $subpath * @return void */ public function setImageDirectorySubpath($subpath) diff --git a/app/code/Magento/Cms/Model/Page/CustomLayout/CustomLayoutManager.php b/app/code/Magento/Cms/Model/Page/CustomLayout/CustomLayoutManager.php index 988bd5b4ac136..a172278015544 100644 --- a/app/code/Magento/Cms/Model/Page/CustomLayout/CustomLayoutManager.php +++ b/app/code/Magento/Cms/Model/Page/CustomLayout/CustomLayoutManager.php @@ -147,7 +147,8 @@ public function applyUpdate(PageLayout $layout, CustomLayoutSelectedInterface $l } $layout->addPageLayoutHandles( - ['selectable' => $this->sanitizeIdentifier($page) .'_' .$layoutSelected->getLayoutFileId()] + ['selectable' => $this->sanitizeIdentifier($page) .'_' .$layoutSelected->getLayoutFileId()], + 'cms_page_view' ); } } diff --git a/app/code/Magento/Cms/Model/PageRepository.php b/app/code/Magento/Cms/Model/PageRepository.php index 72f07771f59d4..2de44b6691274 100644 --- a/app/code/Magento/Cms/Model/PageRepository.php +++ b/app/code/Magento/Cms/Model/PageRepository.php @@ -9,19 +9,21 @@ use Magento\Cms\Api\Data; use Magento\Cms\Api\PageRepositoryInterface; use Magento\Cms\Model\Page\IdentityMap; +use Magento\Cms\Model\ResourceModel\Page as ResourcePage; +use Magento\Cms\Model\ResourceModel\Page\CollectionFactory as PageCollectionFactory; use Magento\Framework\Api\DataObjectHelper; use Magento\Framework\Api\SearchCriteria\CollectionProcessorInterface; use Magento\Framework\App\ObjectManager; +use Magento\Framework\EntityManager\HydratorInterface; use Magento\Framework\Exception\CouldNotDeleteException; use Magento\Framework\Exception\CouldNotSaveException; use Magento\Framework\Exception\NoSuchEntityException; use Magento\Framework\Reflection\DataObjectProcessor; -use Magento\Cms\Model\ResourceModel\Page as ResourcePage; -use Magento\Cms\Model\ResourceModel\Page\CollectionFactory as PageCollectionFactory; use Magento\Store\Model\StoreManagerInterface; /** - * Class PageRepository + * @inheritdoc + * * @SuppressWarnings(PHPMD.CouplingBetweenObjects) */ class PageRepository implements PageRepositoryInterface @@ -76,6 +78,11 @@ class PageRepository implements PageRepositoryInterface */ private $identityMap; + /** + * @var HydratorInterface + */ + private $hydrator; + /** * @param ResourcePage $resource * @param PageFactory $pageFactory @@ -87,6 +94,7 @@ class PageRepository implements PageRepositoryInterface * @param StoreManagerInterface $storeManager * @param CollectionProcessorInterface $collectionProcessor * @param IdentityMap|null $identityMap + * @param HydratorInterface|null $hydrator * @SuppressWarnings(PHPMD.ExcessiveParameterList) */ public function __construct( @@ -99,7 +107,8 @@ public function __construct( DataObjectProcessor $dataObjectProcessor, StoreManagerInterface $storeManager, CollectionProcessorInterface $collectionProcessor = null, - ?IdentityMap $identityMap = null + ?IdentityMap $identityMap = null, + ?HydratorInterface $hydrator = null ) { $this->resource = $resource; $this->pageFactory = $pageFactory; @@ -111,6 +120,7 @@ public function __construct( $this->storeManager = $storeManager; $this->collectionProcessor = $collectionProcessor ?: $this->getCollectionProcessor(); $this->identityMap = $identityMap ?? ObjectManager::getInstance()->get(IdentityMap::class); + $this->hydrator = $hydrator ?: ObjectManager::getInstance()->get(HydratorInterface::class); } /** @@ -150,8 +160,13 @@ public function save(\Magento\Cms\Api\Data\PageInterface $page) $storeId = $this->storeManager->getStore()->getId(); $page->setStoreId($storeId); } + $pageId = $page->getId(); + try { $this->validateLayoutUpdate($page); + if ($pageId) { + $page = $this->hydrator->hydrate($this->getById($pageId), $this->hydrator->extract($page)); + } $this->resource->save($page); $this->identityMap->add($page); } catch (\Exception $exception) { @@ -248,6 +263,7 @@ private function getCollectionProcessor() { if (!$this->collectionProcessor) { $this->collectionProcessor = \Magento\Framework\App\ObjectManager::getInstance()->get( + // phpstan:ignore "Class Magento\Cms\Model\Api\SearchCriteria\PageCollectionProcessor not found." \Magento\Cms\Model\Api\SearchCriteria\PageCollectionProcessor::class ); } diff --git a/app/code/Magento/Cms/Model/ResourceModel/Block.php b/app/code/Magento/Cms/Model/ResourceModel/Block.php index 30e817713755c..1324b9bd127e9 100644 --- a/app/code/Magento/Cms/Model/ResourceModel/Block.php +++ b/app/code/Magento/Cms/Model/ResourceModel/Block.php @@ -185,13 +185,9 @@ public function getIsUniqueBlockToStores(AbstractModel $object) $entityMetadata = $this->metadataPool->getMetadata(BlockInterface::class); $linkField = $entityMetadata->getLinkField(); - $stores = (array)$object->getData('store_id'); - $isDefaultStore = $this->_storeManager->isSingleStoreMode() - || array_search(Store::DEFAULT_STORE_ID, $stores) !== false; - - if (!$isDefaultStore) { - $stores[] = Store::DEFAULT_STORE_ID; - } + $stores = $this->_storeManager->isSingleStoreMode() + ? [Store::DEFAULT_STORE_ID] + : (array)$object->getData('store_id'); $select = $this->getConnection()->select() ->from(['cb' => $this->getMainTable()]) @@ -200,11 +196,8 @@ public function getIsUniqueBlockToStores(AbstractModel $object) 'cb.' . $linkField . ' = cbs.' . $linkField, [] ) - ->where('cb.identifier = ? ', $object->getData('identifier')); - - if (!$isDefaultStore) { - $select->where('cbs.store_id IN (?)', $stores); - } + ->where('cb.identifier = ? ', $object->getData('identifier')) + ->where('cbs.store_id IN (?)', $stores); if ($object->getId()) { $select->where('cb.' . $entityMetadata->getIdentifierField() . ' <> ?', $object->getId()); diff --git a/app/code/Magento/Cms/Model/Wysiwyg/Images/Storage.php b/app/code/Magento/Cms/Model/Wysiwyg/Images/Storage.php index 21f3b232e783c..f0a232bdccccc 100644 --- a/app/code/Magento/Cms/Model/Wysiwyg/Images/Storage.php +++ b/app/code/Magento/Cms/Model/Wysiwyg/Images/Storage.php @@ -570,7 +570,11 @@ public function getThumbnailPath($filePath, $checkFile = false) $mediaRootDir = $this->_cmsWysiwygImages->getStorageRoot(); if (strpos($filePath, (string) $mediaRootDir) === 0) { - $thumbPath = $this->getThumbnailRoot() . substr($filePath, strlen($mediaRootDir)); + $relativeFilePath = substr($filePath, strlen($mediaRootDir)); + // phpcs:ignore Magento2.Functions.DiscouragedFunction + $thumbPath = $relativeFilePath === basename($filePath) + ? $this->getThumbnailRoot() . DIRECTORY_SEPARATOR . $relativeFilePath + : $this->getThumbnailRoot() . $relativeFilePath; if (!$checkFile || $this->_directory->isExist($this->_directory->getRelativePath($thumbPath))) { return $thumbPath; @@ -589,21 +593,12 @@ public function getThumbnailPath($filePath, $checkFile = false) */ public function getThumbnailUrl($filePath, $checkFile = false) { - $mediaRootDir = $this->_cmsWysiwygImages->getStorageRoot(); - - if (strpos($filePath, (string) $mediaRootDir) === 0) { - $thumbSuffix = self::THUMBS_DIRECTORY_NAME . substr($filePath, strlen($mediaRootDir)); - if (!$checkFile || $this->_directory->isExist( - $this->_directory->getRelativePath($mediaRootDir . '/' . $thumbSuffix) - ) - ) { - $thumbSuffix = substr( - $mediaRootDir, - strlen($this->_directory->getAbsolutePath()) - ) . '/' . $thumbSuffix; - $randomIndex = '?rand=' . time(); - return str_replace('\\', '/', $this->_cmsWysiwygImages->getBaseUrl() . $thumbSuffix) . $randomIndex; - } + $thumbPath = $this->getThumbnailPath($filePath, $checkFile); + if ($thumbPath) { + $thumbRelativePath = ltrim($this->_directory->getRelativePath($thumbPath), '/\\'); + $baseUrl = rtrim($this->_cmsWysiwygImages->getBaseUrl(), '/'); + $randomIndex = '?rand=' . time(); + return str_replace('\\', '/', $baseUrl . '/' . $thumbRelativePath) . $randomIndex; } return false; @@ -666,11 +661,13 @@ public function resizeOnTheFly($filename) */ public function getThumbsPath($filePath = false) { - $mediaRootDir = $this->_cmsWysiwygImages->getStorageRoot(); $thumbnailDir = $this->getThumbnailRoot(); - if ($filePath && strpos($filePath, (string) $mediaRootDir) === 0) { - $thumbnailDir .= $this->file->getParentDirectory(substr($filePath, strlen($mediaRootDir))); + if ($filePath) { + $thumbPath = $this->getThumbnailPath($filePath, false); + if ($thumbPath) { + $thumbnailDir = $this->file->getParentDirectory($thumbPath); + } } return $thumbnailDir; diff --git a/app/code/Magento/Cms/Test/Mftf/ActionGroup/AddStoreViewToCmsPageActionGroup.xml b/app/code/Magento/Cms/Test/Mftf/ActionGroup/AddStoreViewToCmsPageActionGroup.xml index 505f3bebbf70c..fabf6480b174b 100644 --- a/app/code/Magento/Cms/Test/Mftf/ActionGroup/AddStoreViewToCmsPageActionGroup.xml +++ b/app/code/Magento/Cms/Test/Mftf/ActionGroup/AddStoreViewToCmsPageActionGroup.xml @@ -10,7 +10,7 @@ xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/actionGroupSchema.xsd"> <actionGroup name="AddStoreViewToCmsPageActionGroup" extends="NavigateToCreatedCMSPageActionGroup"> <annotations> - <description>EXTENDS: navigateToCreatedCMSPage. Adds the provided Store View to a Page.</description> + <description>EXTENDS: NavigateToCreatedCMSPageActionGroup. Adds the provided Store View to a Page.</description> </annotations> <arguments> <argument name="storeViewName" type="string"/> diff --git a/app/code/Magento/Cms/Test/Mftf/ActionGroup/AdminClickInsertWidgetActionGroup.xml b/app/code/Magento/Cms/Test/Mftf/ActionGroup/AdminClickInsertWidgetActionGroup.xml new file mode 100644 index 0000000000000..c221dd62cd7f6 --- /dev/null +++ b/app/code/Magento/Cms/Test/Mftf/ActionGroup/AdminClickInsertWidgetActionGroup.xml @@ -0,0 +1,19 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + /** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +--> + +<actionGroups xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/actionGroupSchema.xsd"> + <actionGroup name="AdminClickInsertWidgetActionGroup"> + <annotations> + <description>Click "Insert Widget" button in the opened widget popup</description> + </annotations> + + <click selector="{{WidgetSection.InsertWidget}}" stepKey="clickInsertWidgetButton"/> + <waitForElementNotVisible selector="{{WidgetSection.InsertWidgetTitle}}" stepKey="waitForWidgetPopupDisappear"/> + </actionGroup> +</actionGroups> diff --git a/app/code/Magento/Cms/Test/Mftf/ActionGroup/AdminCmsPageUpdateTitleActionGroup.xml b/app/code/Magento/Cms/Test/Mftf/ActionGroup/AdminCmsPageUpdateTitleActionGroup.xml new file mode 100644 index 0000000000000..0ae610a59e480 --- /dev/null +++ b/app/code/Magento/Cms/Test/Mftf/ActionGroup/AdminCmsPageUpdateTitleActionGroup.xml @@ -0,0 +1,17 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + /** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +--> +<actionGroups xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/actionGroupSchema.xsd"> + <actionGroup name="AdminCmsPageSetTitleActionGroup"> + <arguments> + <argument name="newTitle" type="string"/> + </arguments> + + <fillField stepKey="fillNewTitle" selector="{{CmsNewPagePageBasicFieldsSection.pageTitle}}" userInput="{{newTitle}}"/> + </actionGroup> +</actionGroups> diff --git a/app/code/Magento/Cms/Test/Mftf/ActionGroup/AdminDisableCMSPageActionGroup.xml b/app/code/Magento/Cms/Test/Mftf/ActionGroup/AdminDisableCMSPageActionGroup.xml new file mode 100644 index 0000000000000..6617c8eea8155 --- /dev/null +++ b/app/code/Magento/Cms/Test/Mftf/ActionGroup/AdminDisableCMSPageActionGroup.xml @@ -0,0 +1,14 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + /** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +--> +<actionGroups xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/actionGroupSchema.xsd"> + <actionGroup name="AdminDisableCMSPageActionGroup"> + <seeElement selector="{{CmsNewPagePageBasicFieldsSection.isActive('1')}}" stepKey="seePageIsEnabled" /> + <click selector="{{CmsNewPagePageBasicFieldsSection.isActiveLabel}}" stepKey="setPageDisabled"/> + </actionGroup> +</actionGroups> \ No newline at end of file diff --git a/app/code/Magento/Cms/Test/Mftf/ActionGroup/AdminEditCMSPageContentActionGroup.xml b/app/code/Magento/Cms/Test/Mftf/ActionGroup/AdminEditCMSPageContentActionGroup.xml new file mode 100644 index 0000000000000..b745e9705ed30 --- /dev/null +++ b/app/code/Magento/Cms/Test/Mftf/ActionGroup/AdminEditCMSPageContentActionGroup.xml @@ -0,0 +1,24 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + /** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +--> + +<actionGroups xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/actionGroupSchema.xsd"> + <actionGroup name="AdminEditCMSPageContentActionGroup"> + <arguments> + <argument name="content" type="string" /> + <argument name="pageId" type="string" /> + </arguments> + <amOnPage url="{{AdminCmsPageEditPage.url(pageId)}}" stepKey="navigateToEditCMSPage"/> + <waitForPageLoad stepKey="waitForCmsPageEditPage"/> + <conditionalClick selector="{{CmsNewPagePageActionsSection.contentSectionName}}" dependentSelector="{{CatalogWidgetSection.insertWidgetButton}}" visible="false" stepKey="clickShowHideEditorIfVisible"/> + <waitForElementVisible selector="{{CmsNewPagePageContentSection.content}}" stepKey="waitForContentField"/> + <fillField selector="{{CmsNewPagePageContentSection.content}}" userInput="{{content}}" stepKey="resetCMSPageToDefaultContent"/> + <click selector="{{CmsNewPagePageActionsSection.saveAndContinueEdit}}" stepKey="clickSave"/> + <waitForPageLoad stepKey="waitForSettingsApply"/> + </actionGroup> +</actionGroups> diff --git a/app/code/Magento/Cms/Test/Mftf/ActionGroup/AdminFillCMSPageContentFieldActionGroup.xml b/app/code/Magento/Cms/Test/Mftf/ActionGroup/AdminFillCMSPageContentFieldActionGroup.xml new file mode 100644 index 0000000000000..f62ee4a8a4ccd --- /dev/null +++ b/app/code/Magento/Cms/Test/Mftf/ActionGroup/AdminFillCMSPageContentFieldActionGroup.xml @@ -0,0 +1,16 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + /** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +--> +<actionGroups xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/actionGroupSchema.xsd"> + <actionGroup name="AdminFillCMSPageContentFieldActionGroup"> + <arguments> + <argument name="content" type="string"/> + </arguments> + <fillField selector="{{CmsNewPagePageContentSection.content}}" userInput="{{content}}" stepKey="fillFieldContent"/> + </actionGroup> +</actionGroups> diff --git a/app/code/Magento/Cms/Test/Mftf/ActionGroup/AdminFillCmsBlockFormActionGroup.xml b/app/code/Magento/Cms/Test/Mftf/ActionGroup/AdminFillCmsBlockFormActionGroup.xml new file mode 100644 index 0000000000000..685385382027b --- /dev/null +++ b/app/code/Magento/Cms/Test/Mftf/ActionGroup/AdminFillCmsBlockFormActionGroup.xml @@ -0,0 +1,25 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + /** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +--> + +<actionGroups xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/actionGroupSchema.xsd"> + <actionGroup name="AdminFillCmsBlockFormActionGroup"> + <annotations> + <description>Fills in the Block Title, Identifier with marginal space, Store View and Content. PLEASE NOTE: The values are passed through arguments in test.</description> + </annotations> + <arguments> + <argument name="cmsBlockDataTitle" type="string" /> + <argument name="cmsBlockDataIdentifier" type="string" /> + <argument name="cmsBlockDataContent" type="string" /> + </arguments> + <fillField selector="{{BlockNewPageBasicFieldsSection.blockTitle}}" userInput="{{cmsBlockDataTitle}}" stepKey="fillFieldTitle1"/> + <fillField selector="{{BlockNewPageBasicFieldsSection.identifier}}" userInput="{{cmsBlockDataIdentifier}}" stepKey="fillFieldIdentifier"/> + <selectOption selector="{{BlockNewPageBasicFieldsSection.storeView}}" userInput="All Store View" stepKey="selectAllStoreView"/> + <fillField selector="{{BlockContentSection.TextArea}}" userInput="{{cmsBlockDataContent}}" stepKey="fillContentField"/> + </actionGroup> +</actionGroups> diff --git a/app/code/Magento/Cms/Test/Mftf/ActionGroup/AdminInsertRecentlyViewedWidgetActionGroup.xml b/app/code/Magento/Cms/Test/Mftf/ActionGroup/AdminInsertRecentlyViewedWidgetActionGroup.xml new file mode 100644 index 0000000000000..e8c66c68348fc --- /dev/null +++ b/app/code/Magento/Cms/Test/Mftf/ActionGroup/AdminInsertRecentlyViewedWidgetActionGroup.xml @@ -0,0 +1,40 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + /** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +--> +<actionGroups xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/actionGroupSchema.xsd"> + <actionGroup name="AdminInsertRecentlyViewedWidgetActionGroup"> + <arguments> + <argument name="attributeSelector1" type="string" defaultValue="show_attributes"/> + <argument name="attributeSelector2" type="string" defaultValue="show_buttons"/> + <argument name="productAttributeSection1" type="string" defaultValue="1"/> + <argument name="productAttributeSection2" type="string" defaultValue="4" /> + <argument name="buttonToShowSection1" type="string" defaultValue="1"/> + <argument name="buttonToShowSection2" type="string" defaultValue="3" /> + </arguments> + <click selector="{{CmsNewPagePageActionsSection.contentSectionName}}" stepKey="expandContent"/> + <waitForPageLoad time="50" stepKey="waitForPageLoadContentSection"/> + <conditionalClick selector="{{CmsNewPagePageActionsSection.showHideEditor}}" dependentSelector="{{CmsNewPagePageActionsSection.showHideEditor}}" visible="true" stepKey="clickNextShowHideEditorIfVisible"/> + <waitForElementVisible selector="{{CatalogWidgetSection.insertWidgetButton}}" stepKey="waitForInsertWidgetElement"/> + <click selector="{{CatalogWidgetSection.insertWidgetButton}}" stepKey="clickInsertWidget"/> + <waitForElementVisible selector="{{InsertWidgetSection.widgetTypeDropDown}}" time="30" stepKey="waitForWidgetTypeDropDownVisible"/> + <!--Select "Widget Type"--> + <selectOption selector="{{WidgetSection.WidgetType}}" userInput="Recently Viewed Products" stepKey="selectRecentlyViewedProducts"/> + <waitForPageLoad time="30" stepKey="waitForPageLoadWidgetType"/> + <!--Select all product attributes--> + <dragAndDrop selector1="{{AdminRecentlyViewedWidgetSection.attributeSelector(attributeSelector1,productAttributeSection1)}}" selector2="{{AdminRecentlyViewedWidgetSection.attributeSelector(attributeSelector1,productAttributeSection2)}}" stepKey="selectProductSpecifiedOptions"/> + <!--Select all buttons to show--> + <dragAndDrop selector1="{{AdminRecentlyViewedWidgetSection.attributeSelector(attributeSelector2,buttonToShowSection2)}}" selector2="{{AdminRecentlyViewedWidgetSection.attributeSelector(attributeSelector2,buttonToShowSection2)}}" stepKey="selectButtonSpecifiedOptions"/> + <click selector="{{WidgetSection.InsertWidget}}" stepKey="clickInsertWidgetToSave"/> + <waitForPageLoad time="30" stepKey="waitForWidgetInsertPageLoad"/> + <!-- Check that widget is inserted --> + <waitForElementVisible selector="#cms_page_form_content" stepKey="checkCMSContent" time="30"/> + <click selector="{{CmsNewPagePageActionsSection.saveAndContinueEdit}}" stepKey="clickNextSave"/> + <waitForPageLoad stepKey="waitForPageActionSave" time="30"/> + <waitForElementVisible selector="*[data-ui-id='messages-message-success']" time="60" stepKey="waitForSaveSuccess"/> + </actionGroup> +</actionGroups> \ No newline at end of file diff --git a/app/code/Magento/Cms/Test/Mftf/ActionGroup/AdminInsertWidgetToCmsPageContentActionGroup.xml b/app/code/Magento/Cms/Test/Mftf/ActionGroup/AdminInsertWidgetToCmsPageContentActionGroup.xml new file mode 100644 index 0000000000000..734d8d98722b2 --- /dev/null +++ b/app/code/Magento/Cms/Test/Mftf/ActionGroup/AdminInsertWidgetToCmsPageContentActionGroup.xml @@ -0,0 +1,25 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + /** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +--> + +<actionGroups xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/actionGroupSchema.xsd"> + <actionGroup name="AdminInsertWidgetToCmsPageContentActionGroup"> + <annotations> + <description>Insert widget to CMS Page content field. You are on CMS edit page, content tab is expanded.</description> + </annotations> + + <arguments> + <argument name="widgetType" type="string" defaultValue="CMS Page Link"/> + </arguments> + <waitForElementVisible selector="{{CmsNewPagePageActionsSection.insertWidget}}" stepKey="waitForInsertWidgetElementVisible"/> + <click selector="{{CmsNewPagePageActionsSection.insertWidget}}" stepKey="clickOnInsertWidgetButton"/> + <waitForPageLoad stepKey="waitForPageLoadAfterClickOnInsertWidgetButton"/> + <waitForElementVisible selector="{{WidgetSection.InsertWidgetTitle}}" stepKey="waitForInsertWidgetTitle"/> + <selectOption selector="{{WidgetSection.WidgetType}}" userInput="{{widgetType}}" stepKey="selectWidgetType"/> + </actionGroup> +</actionGroups> diff --git a/app/code/Magento/Cms/Test/Mftf/ActionGroup/AdminNavigateToPageGridActionGroup.xml b/app/code/Magento/Cms/Test/Mftf/ActionGroup/AdminNavigateToPageGridActionGroup.xml new file mode 100644 index 0000000000000..7dc68e7a5a891 --- /dev/null +++ b/app/code/Magento/Cms/Test/Mftf/ActionGroup/AdminNavigateToPageGridActionGroup.xml @@ -0,0 +1,19 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + /** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +--> + +<actionGroups xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/actionGroupSchema.xsd"> + <actionGroup name="AdminNavigateToPageGridActionGroup"> + <annotations> + <description>Navigates to CMS page grid.</description> + </annotations> + + <amOnPage url="{{CmsPagesPage.url}}" stepKey="amOnPagePagesGrid"/> + <waitForPageLoad stepKey="waitForPageLoad1"/> + </actionGroup> +</actionGroups> diff --git a/app/code/Magento/Cms/Test/Mftf/ActionGroup/AdminOpenCreateNewCMSPageActionGroup.xml b/app/code/Magento/Cms/Test/Mftf/ActionGroup/AdminOpenCreateNewCMSPageActionGroup.xml new file mode 100644 index 0000000000000..79ce1bc9d8e47 --- /dev/null +++ b/app/code/Magento/Cms/Test/Mftf/ActionGroup/AdminOpenCreateNewCMSPageActionGroup.xml @@ -0,0 +1,14 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + /** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +--> +<actionGroups xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/actionGroupSchema.xsd"> + <actionGroup name="AdminOpenCreateNewCMSPageActionGroup"> + <amOnPage url="{{CmsNewPagePage.url}}" stepKey="navigateToCreateNewPage"/> + <waitForPageLoad stepKey="waitForNewPagePageLoad"/> + </actionGroup> +</actionGroups> diff --git a/app/code/Magento/Cms/Test/Mftf/ActionGroup/AdminSaveAndDuplicateCMSPageWithSplitButtonActionGroup.xml b/app/code/Magento/Cms/Test/Mftf/ActionGroup/AdminSaveAndDuplicateCMSPageWithSplitButtonActionGroup.xml new file mode 100644 index 0000000000000..43c0fc9ff3a45 --- /dev/null +++ b/app/code/Magento/Cms/Test/Mftf/ActionGroup/AdminSaveAndDuplicateCMSPageWithSplitButtonActionGroup.xml @@ -0,0 +1,23 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + /** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +--> + +<actionGroups xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/actionGroupSchema.xsd"> + <actionGroup name="AdminSaveAndDuplicateCMSPageWithSplitButtonActionGroup"> + <annotations> + <description>Clicks on the Save and Duplicate button.</description> + </annotations> + + <waitForElementVisible selector="{{CmsNewPagePageActionsSection.expandSplitButton}}" stepKey="waitForExpandSplitButtonToBeVisible"/> + <click selector="{{CmsNewPagePageActionsSection.expandSplitButton}}" stepKey="expandSplitBtn2" /> + <click selector="{{CmsNewPagePageActionsSection.saveAndDuplicate}}" stepKey="clickSaveAndDuplicate" /> + <waitForPageLoad stepKey="waitForPageLoadAfterClickingSaveAndDuplicate"/> + <see userInput="You saved the page." stepKey="seeSavedPageMsgOnForm"/> + <see userInput="You duplicated the page." stepKey="seeDuplicatedPageMsg"/> + </actionGroup> +</actionGroups> diff --git a/app/code/Magento/Cms/Test/Mftf/ActionGroup/AdminSelectCMSPageFromGridActionGroup.xml b/app/code/Magento/Cms/Test/Mftf/ActionGroup/AdminSelectCMSPageFromGridActionGroup.xml new file mode 100644 index 0000000000000..bd721b4d7d40c --- /dev/null +++ b/app/code/Magento/Cms/Test/Mftf/ActionGroup/AdminSelectCMSPageFromGridActionGroup.xml @@ -0,0 +1,16 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + /** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +--> +<actionGroups xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/actionGroupSchema.xsd"> + <actionGroup name="AdminSelectCMSPageFromGridActionGroup"> + <arguments> + <argument name="identifier" defaultValue=""/> + </arguments> + <click selector="{{CmsPagesPageActionsSection.select(identifier)}}" stepKey="clickSelectCMSPage" /> + </actionGroup> +</actionGroups> diff --git a/app/code/Magento/Cms/Test/Mftf/ActionGroup/AdminSelectCMSPageStoreViewActionGroup.xml b/app/code/Magento/Cms/Test/Mftf/ActionGroup/AdminSelectCMSPageStoreViewActionGroup.xml new file mode 100644 index 0000000000000..3e4a67e595c04 --- /dev/null +++ b/app/code/Magento/Cms/Test/Mftf/ActionGroup/AdminSelectCMSPageStoreViewActionGroup.xml @@ -0,0 +1,18 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + /** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +--> +<actionGroups xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/actionGroupSchema.xsd"> + <actionGroup name="AdminSelectCMSPageStoreViewActionGroup"> + <arguments> + <argument name="storeViewName" type="string"/> + </arguments> + <click selector="{{CmsNewPagePiwSection.header}}" stepKey="clickPageInWebsites"/> + <waitForElementVisible selector="{{CmsNewPagePiwSection.selectStoreView(storeViewName)}}" stepKey="waitForStoreGridReload"/> + <clickWithLeftButton selector="{{CmsNewPagePiwSection.selectStoreView(storeViewName)}}" stepKey="clickStoreView"/> + </actionGroup> +</actionGroups> \ No newline at end of file diff --git a/app/code/Magento/Cms/Test/Mftf/ActionGroup/AdminSetCMSBlockDisabledActionGroup.xml b/app/code/Magento/Cms/Test/Mftf/ActionGroup/AdminSetCMSBlockDisabledActionGroup.xml new file mode 100644 index 0000000000000..5c02822bedd4a --- /dev/null +++ b/app/code/Magento/Cms/Test/Mftf/ActionGroup/AdminSetCMSBlockDisabledActionGroup.xml @@ -0,0 +1,14 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + /** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +--> +<actionGroups xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/actionGroupSchema.xsd"> + <actionGroup name="AdminSetCMSBlockDisabledActionGroup"> + <seeElement selector="{{BlockNewPageBasicFieldsSection.isActive('1')}}" stepKey="seeBlockIsEnabled" /> + <click selector="{{BlockNewPageBasicFieldsSection.isActiveLabel}}" stepKey="setBlockDisabled"/> + </actionGroup> +</actionGroups> diff --git a/app/code/Magento/Cms/Test/Mftf/ActionGroup/AdminSetCMSBlockEnabledActionGroup.xml b/app/code/Magento/Cms/Test/Mftf/ActionGroup/AdminSetCMSBlockEnabledActionGroup.xml new file mode 100644 index 0000000000000..4ba17decb82ab --- /dev/null +++ b/app/code/Magento/Cms/Test/Mftf/ActionGroup/AdminSetCMSBlockEnabledActionGroup.xml @@ -0,0 +1,14 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + /** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +--> +<actionGroups xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/actionGroupSchema.xsd"> + <actionGroup name="AdminSetCMSBlockEnabledActionGroup"> + <seeElement selector="{{BlockNewPageBasicFieldsSection.isActive('0')}}" stepKey="seeBlockIsDisabled" /> + <click selector="{{BlockNewPageBasicFieldsSection.isActiveLabel}}" stepKey="setBlockEnabled"/> + </actionGroup> +</actionGroups> diff --git a/app/code/Magento/Cms/Test/Mftf/ActionGroup/AssertAdminCmsPageSaveSplitButtonActionGroup.xml b/app/code/Magento/Cms/Test/Mftf/ActionGroup/AssertAdminCmsPageSaveSplitButtonActionGroup.xml new file mode 100644 index 0000000000000..6ea87aeae5998 --- /dev/null +++ b/app/code/Magento/Cms/Test/Mftf/ActionGroup/AssertAdminCmsPageSaveSplitButtonActionGroup.xml @@ -0,0 +1,22 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + /** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +--> + +<actionGroups xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/actionGroupSchema.xsd"> + <actionGroup name="AssertAdminCmsPageSaveSplitButtonActionGroup"> + <annotations> + <description>Verify Save and Duplicate and Save and Close button.</description> + </annotations> + + <amOnPage url="{{CmsNewPagePage.url}}" stepKey="amOnPageCreationForm"/> + <waitForPageLoad stepKey="waitForPageLoad1"/> + <click selector="{{CmsNewPagePageActionsSection.expandSplitButton}}" stepKey="expandSplitBtn1" /> + <see selector="{{CmsNewPagePageActionsSection.saveAndDuplicate}}" userInput="Save & Duplicate" stepKey="seeSaveAndDuplicate"/> + <see selector="{{CmsNewPagePageActionsSection.saveAndClose}}" userInput="Save & Close" stepKey="seeSaveAndClose"/> + </actionGroup> +</actionGroups> diff --git a/app/code/Magento/Cms/Test/Mftf/ActionGroup/AssertCMSPageStoreIdActionGroup.xml b/app/code/Magento/Cms/Test/Mftf/ActionGroup/AssertCMSPageStoreIdActionGroup.xml new file mode 100644 index 0000000000000..4d05d68e5e9b2 --- /dev/null +++ b/app/code/Magento/Cms/Test/Mftf/ActionGroup/AssertCMSPageStoreIdActionGroup.xml @@ -0,0 +1,22 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + /** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +--> +<actionGroups xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/actionGroupSchema.xsd"> + <actionGroup name="AssertCMSPageStoreIdActionGroup"> + <arguments> + <argument name="storeId" type="string"/> + </arguments> + <click selector="{{CmsNewPagePiwSection.header}}" stepKey="clickPageInWebsites"/> + <waitForElementVisible selector="{{CmsNewPagePiwSection.storeIdDropdown}}" stepKey="waitForStoresSectionReload"/> + <grabValueFrom selector="{{CmsNewPagePiwSection.storeIdDropdown}}" stepKey="grabValueFromStoreView"/> + <assertEquals stepKey="assertTitle" message="pass"> + <expectedResult type="string">{{storeId}}</expectedResult> + <actualResult type="variable">grabValueFromStoreView</actualResult> + </assertEquals> + </actionGroup> +</actionGroups> \ No newline at end of file diff --git a/app/code/Magento/Cms/Test/Mftf/ActionGroup/AssertStorefrontNoTextOnCategoryPageActionGroup.xml b/app/code/Magento/Cms/Test/Mftf/ActionGroup/AssertStorefrontNoTextOnCategoryPageActionGroup.xml new file mode 100644 index 0000000000000..8ca93004c4e1d --- /dev/null +++ b/app/code/Magento/Cms/Test/Mftf/ActionGroup/AssertStorefrontNoTextOnCategoryPageActionGroup.xml @@ -0,0 +1,21 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + /** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +--> +<actionGroups xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/actionGroupSchema.xsd"> + <actionGroup name="AssertStorefrontNoTextOnCategoryPageActionGroup"> + <arguments> + <argument name="category" defaultValue="_defaultCategory"/> + <argument name="text" type="string"/> + </arguments> + <amOnPage url="{{StorefrontCategoryPage.url(category.name)}}" stepKey="navigateToCategoryPage"/> + <waitForPageLoad stepKey="waitForPageLoad1"/> + <see selector="{{StorefrontCategoryMainSection.CategoryTitle}}" userInput="{{category.name_lwr}}" stepKey="assertCategoryOnStorefront"/> + <seeInTitle userInput="{{category.name}}" stepKey="seeCategoryNameInTitle"/> + <dontSee selector="{{StorefrontCMSPageSection.mainContent}}" userInput="{{text}}" stepKey="seeAssertTextInMainContent"/> + </actionGroup> +</actionGroups> diff --git a/app/code/Magento/Cms/Test/Mftf/ActionGroup/AssertStorefrontTextOnCategoryPageActionGroup.xml b/app/code/Magento/Cms/Test/Mftf/ActionGroup/AssertStorefrontTextOnCategoryPageActionGroup.xml new file mode 100644 index 0000000000000..aa761854a10fa --- /dev/null +++ b/app/code/Magento/Cms/Test/Mftf/ActionGroup/AssertStorefrontTextOnCategoryPageActionGroup.xml @@ -0,0 +1,21 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + /** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +--> +<actionGroups xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/actionGroupSchema.xsd"> + <actionGroup name="AssertStorefrontTextOnCategoryPageActionGroup"> + <arguments> + <argument name="category" defaultValue="_defaultCategory"/> + <argument name="text" type="string"/> + </arguments> + <amOnPage url="{{StorefrontCategoryPage.url(category.name)}}" stepKey="navigateToCategoryPage"/> + <waitForPageLoad stepKey="waitForPageLoad1"/> + <see selector="{{StorefrontCategoryMainSection.CategoryTitle}}" userInput="{{category.name_lwr}}" stepKey="assertCategoryOnStorefront"/> + <seeInTitle userInput="{{category.name}}" stepKey="seeCategoryNameInTitle"/> + <see selector="{{StorefrontCMSPageSection.mainContent}}" userInput="{{text}}" stepKey="seeAssertTextInMainContent"/> + </actionGroup> +</actionGroups> diff --git a/app/code/Magento/Cms/Test/Mftf/ActionGroup/ClearWidgetsForCMSHomePageContentWYSIWYGDisabledActionGroup.xml b/app/code/Magento/Cms/Test/Mftf/ActionGroup/ClearWidgetsForCMSHomePageContentWYSIWYGDisabledActionGroup.xml index adaeb8c90ff0b..31aedde95d95b 100644 --- a/app/code/Magento/Cms/Test/Mftf/ActionGroup/ClearWidgetsForCMSHomePageContentWYSIWYGDisabledActionGroup.xml +++ b/app/code/Magento/Cms/Test/Mftf/ActionGroup/ClearWidgetsForCMSHomePageContentWYSIWYGDisabledActionGroup.xml @@ -9,7 +9,7 @@ <actionGroups xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/actionGroupSchema.xsd"> <actionGroup name="ClearWidgetsForCMSHomePageContentWYSIWYGDisabledActionGroup"> - <amOnPage url="{{CmsPageEditPage.url('2')}}" stepKey="navigateToEditHomePagePage"/> + <amOnPage url="{{AdminCmsPageEditPage.url('2')}}" stepKey="navigateToEditHomePagePage"/> <waitForPageLoad stepKey="waitForCmsPageEditPage"/> <conditionalClick selector="{{CmsNewPagePageActionsSection.contentSectionName}}" dependentSelector="{{CatalogWidgetSection.insertWidgetButton}}" visible="false" stepKey="clickShowHideEditorIfVisible"/> <waitForElementVisible selector="{{CmsNewPagePageContentSection.content}}" stepKey="waitForContentField"/> diff --git a/app/code/Magento/Cms/Test/Mftf/ActionGroup/ClearWidgetsFromCMSContentActionGroup.xml b/app/code/Magento/Cms/Test/Mftf/ActionGroup/ClearWidgetsFromCMSContentActionGroup.xml index 98de51574aa4b..a50f674208d6a 100644 --- a/app/code/Magento/Cms/Test/Mftf/ActionGroup/ClearWidgetsFromCMSContentActionGroup.xml +++ b/app/code/Magento/Cms/Test/Mftf/ActionGroup/ClearWidgetsFromCMSContentActionGroup.xml @@ -13,7 +13,7 @@ <description>Goes to the Admin CMS Page Edit page for the Page ID number 2. Clears the Widget and replaces it with Text. PLEASE NOTE: The Page ID/Text are Hardcoded.</description> </annotations> - <amOnPage url="{{CmsPageEditPage.url('2')}}" stepKey="navigateToEditHomePagePage"/> + <amOnPage url="{{AdminCmsPageEditPage.url('2')}}" stepKey="navigateToEditHomePagePage"/> <waitForPageLoad stepKey="waitEditHomePagePageToLoad"/> <click selector="{{CmsNewPagePageContentSection.header}}" stepKey="clickContentTab"/> <waitForElementNotVisible selector="{{CmsWYSIWYGSection.CheckIfTabExpand}}" stepKey="waitForTabExpand"/> diff --git a/app/code/Magento/Cms/Test/Mftf/ActionGroup/ClickInsertEditImageTinyMCEButtonActionGroup.xml b/app/code/Magento/Cms/Test/Mftf/ActionGroup/ClickInsertEditImageTinyMCEButtonActionGroup.xml new file mode 100644 index 0000000000000..1685898743596 --- /dev/null +++ b/app/code/Magento/Cms/Test/Mftf/ActionGroup/ClickInsertEditImageTinyMCEButtonActionGroup.xml @@ -0,0 +1,19 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + /** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +--> + +<actionGroups xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/actionGroupSchema.xsd"> + <actionGroup name="ClickInsertEditImageTinyMCEButtonActionGroup"> + <annotations> + <description>Clicks on the 'Insert/edit image' TinyMCE button.</description> + </annotations> + + <click selector="{{TinyMCESection.InsertImageIcon}}" stepKey="clickInsertImageBtn" /> + <waitForPageLoad stepKey="waitForPageLoad"/> + </actionGroup> +</actionGroups> diff --git a/app/code/Magento/Cms/Test/Mftf/ActionGroup/CreateImageFolderByEnterKeyActionGroup.xml b/app/code/Magento/Cms/Test/Mftf/ActionGroup/CreateImageFolderByEnterKeyActionGroup.xml new file mode 100644 index 0000000000000..5d8138b9c9cc7 --- /dev/null +++ b/app/code/Magento/Cms/Test/Mftf/ActionGroup/CreateImageFolderByEnterKeyActionGroup.xml @@ -0,0 +1,18 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + /** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +--> + +<actionGroups xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/actionGroupSchema.xsd"> + <actionGroup name="CreateImageFolderByEnterKeyActionGroup" extends="CreateImageFolderActionGroup"> + <annotations> + <description>Creates a folder (by enter key) in the Media Gallery based on the provided Folder.</description> + </annotations> + + <pressKey selector="{{MediaGallerySection.FolderName}}" parameterArray="[\Facebook\WebDriver\WebDriverKeys::ENTER]" stepKey="acceptFolderName"/> + </actionGroup> +</actionGroups> diff --git a/app/code/Magento/Cms/Test/Mftf/ActionGroup/CreateNewPageWithBasicValuesActionGroup.xml b/app/code/Magento/Cms/Test/Mftf/ActionGroup/CreateNewPageWithBasicValuesActionGroup.xml new file mode 100644 index 0000000000000..1f9ddf7e70424 --- /dev/null +++ b/app/code/Magento/Cms/Test/Mftf/ActionGroup/CreateNewPageWithBasicValuesActionGroup.xml @@ -0,0 +1,24 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + /** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +--> + +<actionGroups xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/actionGroupSchema.xsd"> + <actionGroup name="CreateNewPageWithBasicValues"> + <annotations> + <description>Click Add new page button and fills basic information in cms page. Values are hardcoded from _defaultCmsPage.</description> + </annotations> + + <click selector="{{CmsPagesPageActionsSection.addNewPageButton}}" stepKey="clickAddNewPage"/> + <fillField selector="{{CmsNewPagePageBasicFieldsSection.pageTitle}}" userInput="{{_defaultCmsPage.title}}" stepKey="fillFieldTitle"/> + <click selector="{{CmsNewPagePageContentSection.header}}" stepKey="clickExpandContent"/> + <fillField selector="{{CmsNewPagePageContentSection.contentHeading}}" userInput="{{_defaultCmsPage.content_heading}}" stepKey="fillFieldContentHeading"/> + <fillField selector="{{CmsNewPagePageContentSection.content}}" userInput="{{_defaultCmsPage.content}}" stepKey="fillFieldContent"/> + <click selector="{{CmsNewPagePageSeoSection.header}}" stepKey="clickExpandSearchEngineOptimisation"/> + <fillField selector="{{CmsNewPagePageSeoSection.urlKey}}" userInput="{{_defaultCmsPage.identifier}}" stepKey="fillFieldUrlKey"/> + </actionGroup> +</actionGroups> diff --git a/app/code/Magento/Cms/Test/Mftf/ActionGroup/DeleteFolderActionGroup.xml b/app/code/Magento/Cms/Test/Mftf/ActionGroup/DeleteFolderActionGroup.xml new file mode 100644 index 0000000000000..4c33b3d8ce35d --- /dev/null +++ b/app/code/Magento/Cms/Test/Mftf/ActionGroup/DeleteFolderActionGroup.xml @@ -0,0 +1,28 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + /** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +--> + +<actionGroups xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/actionGroupSchema.xsd"> + <actionGroup name="DeleteFolderActionGroup"> + <annotations> + <description>Deletes the provided folder by name from the Media Gallery.</description> + </annotations> + <arguments> + <argument name="ImageFolder" defaultValue="ImageFolder"/> + </arguments> + + <click userInput="{{ImageFolder.name}}" stepKey="clickOnCreatedFolder"/> + <waitForLoadingMaskToDisappear stepKey="waitForLoading"/> + <see selector="{{MediaGallerySection.DeleteFolder}}" userInput="Delete Folder" stepKey="seeDeleteFolderBtn"/> + <click selector="{{MediaGallerySection.DeleteFolder}}" stepKey="clickDeleteFolderBtn"/> + <waitForText userInput="OK" stepKey="waitForConfirm"/> + <click selector="{{MediaGallerySection.confirmDelete}}" stepKey="confirmDelete"/> + <waitForPageLoad stepKey="waitForPopUpHide"/> + <dontSeeElement selector="{{ImageFolder.name}}" stepKey="dontSeeFolderName"/> + </actionGroup> +</actionGroups> diff --git a/app/code/Magento/Cms/Test/Mftf/ActionGroup/FillOutCMSPageContentActionGroup.xml b/app/code/Magento/Cms/Test/Mftf/ActionGroup/FillOutCMSPageContentActionGroup.xml index 5a1ab2c05d24d..0ee195ae13a3c 100644 --- a/app/code/Magento/Cms/Test/Mftf/ActionGroup/FillOutCMSPageContentActionGroup.xml +++ b/app/code/Magento/Cms/Test/Mftf/ActionGroup/FillOutCMSPageContentActionGroup.xml @@ -15,6 +15,8 @@ <fillField selector="{{CmsNewPagePageBasicFieldsSection.pageTitle}}" userInput="{{_duplicatedCMSPage.title}}" stepKey="fillFieldTitle"/> <click selector="{{CmsNewPagePageContentSection.header}}" stepKey="clickExpandContentTabForPage"/> + <fillField selector="{{CmsNewPagePageContentSection.contentHeading}}" userInput="{{_duplicatedCMSPage.content_heading}}" stepKey="fillFieldContentHeading"/> + <scrollTo selector="{{CmsNewPagePageContentSection.content}}" stepKey="scrollToPageContent"/> <fillField selector="{{CmsNewPagePageContentSection.content}}" userInput="{{_duplicatedCMSPage.content}}" stepKey="fillFieldContent"/> <click selector="{{CmsNewPagePageSeoSection.header}}" stepKey="clickExpandSearchEngineOptimisation"/> <fillField selector="{{CmsNewPagePageSeoSection.urlKey}}" userInput="{{_duplicatedCMSPage.identifier}}" stepKey="fillFieldUrlKey"/> diff --git a/app/code/Magento/Cms/Test/Mftf/ActionGroup/PressEscImageFolderActionGroup.xml b/app/code/Magento/Cms/Test/Mftf/ActionGroup/PressEscImageFolderActionGroup.xml new file mode 100644 index 0000000000000..85f83193e64e3 --- /dev/null +++ b/app/code/Magento/Cms/Test/Mftf/ActionGroup/PressEscImageFolderActionGroup.xml @@ -0,0 +1,27 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + /** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +--> + +<actionGroups xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/actionGroupSchema.xsd"> + <actionGroup name="PressEscImageFolderActionGroup"> + <annotations> + <description>Opens the 'create folder' modal, fills 'folder name' input with provided folder name, + presses escape key to cancel folder creation (close modal).</description> + </annotations> + <arguments> + <argument name="ImageFolder" defaultValue="ImageFolder"/> + </arguments> + + <click selector="{{MediaGallerySection.CreateFolder}}" stepKey="createFolder"/> + <waitForElementVisible selector="{{MediaGallerySection.FolderName}}" stepKey="waitForPopUp"/> + <fillField selector="{{MediaGallerySection.FolderName}}" userInput="{{ImageFolder.name}}" stepKey="fillFolderName"/> + <pressKey selector="{{MediaGallerySection.FolderName}}" parameterArray="[\Facebook\WebDriver\WebDriverKeys::ESCAPE]" stepKey="cancelFolderName"/> + <waitForPageLoad stepKey="waitForPopUpHide"/> + <dontSeeElement selector="{{ImageFolder.name}}" stepKey="dontSeeFolderName"/> + </actionGroup> +</actionGroups> diff --git a/app/code/Magento/Cms/Test/Mftf/ActionGroup/SaveAndCloseCMSBlockWithSplitButtonActionGroup.xml b/app/code/Magento/Cms/Test/Mftf/ActionGroup/SaveAndCloseCMSBlockWithSplitButtonActionGroup.xml index 95ce3c499b6b8..44e29f7e2fe55 100644 --- a/app/code/Magento/Cms/Test/Mftf/ActionGroup/SaveAndCloseCMSBlockWithSplitButtonActionGroup.xml +++ b/app/code/Magento/Cms/Test/Mftf/ActionGroup/SaveAndCloseCMSBlockWithSplitButtonActionGroup.xml @@ -17,6 +17,7 @@ <click selector="{{BlockNewPagePageActionsSection.expandSplitButton}}" stepKey="expandSplitButton"/> <click selector="{{BlockNewPagePageActionsSection.saveAndClose}}" stepKey="clickSaveBlock"/> <waitForPageLoad stepKey="waitForPageLoadAfterClickingSave"/> - <see userInput="You saved the block." stepKey="assertSaveBlockSuccessMessage"/> + <waitForElementVisible selector="{{AdminMessagesSection.success}}" stepKey="waitForSuccessMessageAppear"/> + <see userInput="You saved the block." selector="{{AdminMessagesSection.success}}" stepKey="assertSaveBlockSuccessMessage"/> </actionGroup> </actionGroups> diff --git a/app/code/Magento/Cms/Test/Mftf/ActionGroup/SaveAndDuplicateCMSBlockWithSplitButtonActionGroup.xml b/app/code/Magento/Cms/Test/Mftf/ActionGroup/SaveAndDuplicateCMSBlockWithSplitButtonActionGroup.xml new file mode 100644 index 0000000000000..625046d1a978b --- /dev/null +++ b/app/code/Magento/Cms/Test/Mftf/ActionGroup/SaveAndDuplicateCMSBlockWithSplitButtonActionGroup.xml @@ -0,0 +1,23 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + /** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +--> + +<actionGroups xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/actionGroupSchema.xsd"> + <actionGroup name="SaveAndDuplicateCMSBlockWithSplitButtonActionGroup"> + <annotations> + <description>Clicks on the Save and Duplicate button.</description> + </annotations> + + <waitForElementVisible selector="{{BlockNewPagePageActionsSection.expandSplitButton}}" stepKey="waitForExpandSplitButtonToBeVisible"/> + <click selector="{{BlockNewPagePageActionsSection.expandSplitButton}}" stepKey="expandSplitButton"/> + <click selector="{{BlockNewPagePageActionsSection.saveAndDuplicate}}" stepKey="clickSaveAndDuplicate" /> + <waitForPageLoad stepKey="waitForPageLoadAfterClickingSave"/> + <see userInput="You saved the block." stepKey="assertSaveBlockSuccessMessage"/> + <see userInput="You duplicated the block." stepKey="seeDuplicatedBlockMsg"/> + </actionGroup> +</actionGroups> diff --git a/app/code/Magento/Cms/Test/Mftf/ActionGroup/SearchBlockOnGridPageActionGroup.xml b/app/code/Magento/Cms/Test/Mftf/ActionGroup/SearchBlockOnGridPageActionGroup.xml deleted file mode 100644 index 9f00f208b11e8..0000000000000 --- a/app/code/Magento/Cms/Test/Mftf/ActionGroup/SearchBlockOnGridPageActionGroup.xml +++ /dev/null @@ -1,52 +0,0 @@ -<?xml version="1.0" encoding="UTF-8"?> -<!-- - /** - * Copyright © Magento, Inc. All rights reserved. - * See COPYING.txt for license details. - */ ---> - -<actionGroups xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" - xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/actionGroupSchema.xsd"> - <actionGroup name="searchBlockOnGridPage"> - <annotations> - <description>Searches the Admin CMS Blocks grid based on the provided Block.</description> - </annotations> - <arguments> - <argument name="Block" defaultValue=""/> - </arguments> - - <fillField selector="//input[@name='chooser_identifier']" userInput="{{Block.identifier}}" stepKey="fillEntityIdentifier"/> - <click selector="//div[@class='modal-inner-wrap']//button[@title='Search']" stepKey="clickSearchBtn"/> - <waitForLoadingMaskToDisappear stepKey="waitForSecondIdSortDescendingToFinish2"/> - <waitForElementVisible selector="{{WidgetSection.BlockPage(Block.identifier)}}" stepKey="waitForBlockTitle"/> - </actionGroup> - - <actionGroup name ="deleteBlock"> - <annotations> - <description>Goes to the Admin CMS Blocks page. Filters the grid based on the provided Block. Deletes the Block via the grid.</description> - </annotations> - <arguments> - <argument name="Block" defaultValue=""/> - </arguments> - - <amOnPage url="{{CmsBlocksPage.url}}" stepKey="navigateToCMSBlocksGrid"/> - <waitForPageLoad stepKey="waitForPageLoad1"/> - <conditionalClick selector="{{BlockPageActionsSection.clearAll}}" dependentSelector="{{BlockPageActionsSection.activeFilters}}" stepKey="clickToResetFilter" visible="true"/> - <waitForPageLoad stepKey="waitForPageLoad2"/> - <click selector="{{BlockPageActionsSection.FilterBtn}}" stepKey="clickFilterBtn"/> - <fillField selector="{{BlockPageActionsSection.URLKey}}" userInput="{{Block.identifier}}" stepKey="fillBlockIdentifierInput"/> - <click selector="{{BlockPageActionsSection.ApplyFiltersBtn}}" stepKey="applyFilter"/> - <waitForLoadingMaskToDisappear stepKey="waitForGridToLoadResults"/> - <waitForElementVisible selector="{{BlockPageActionsSection.select(Block.identifier)}}" stepKey="waitForCMSPageGrid"/> - <click selector="{{BlockPageActionsSection.select(Block.identifier)}}" stepKey="clickSelect"/> - <waitForElementVisible selector="{{BlockPageActionsSection.edit(Block.identifier)}}" stepKey="waitForEditLink"/> - <click selector="{{BlockPageActionsSection.edit(Block.identifier)}}" stepKey="clickEdit"/> - <waitForLoadingMaskToDisappear stepKey="waitForPageToLoad"/> - <click selector="{{CmsBlockBlockActionSection.deleteBlock}}" stepKey="deleteBlock"/> - <waitForElementVisible selector="{{CmsBlockBlockActionSection.deleteConfirm}}" stepKey="waitForOkButtonToBeVisible"/> - <click selector="{{CmsBlockBlockActionSection.deleteConfirm}}" stepKey="clickOkButton"/> - <waitForPageLoad stepKey="waitForPageLoad3"/> - <see userInput="You deleted the block." stepKey="seeSuccessMessage"/> - </actionGroup> -</actionGroups> diff --git a/app/code/Magento/Cms/Test/Mftf/ActionGroup/SearchBlockOnGridPageActionGroup/DeleteBlockActionGroup.xml b/app/code/Magento/Cms/Test/Mftf/ActionGroup/SearchBlockOnGridPageActionGroup/DeleteBlockActionGroup.xml new file mode 100644 index 0000000000000..d0050356d382b --- /dev/null +++ b/app/code/Magento/Cms/Test/Mftf/ActionGroup/SearchBlockOnGridPageActionGroup/DeleteBlockActionGroup.xml @@ -0,0 +1,37 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + /** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +--> +<actionGroups xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/actionGroupSchema.xsd"> + <actionGroup name="deleteBlock"> + <annotations> + <description>Goes to the Admin CMS Blocks page. Filters the grid based on the provided Block. Deletes the Block via the grid.</description> + </annotations> + <arguments> + <argument name="Block" defaultValue=""/> + </arguments> + + <amOnPage url="{{CmsBlocksPage.url}}" stepKey="navigateToCMSBlocksGrid"/> + <waitForPageLoad stepKey="waitForPageLoad1"/> + <conditionalClick selector="{{BlockPageActionsSection.clearAll}}" dependentSelector="{{BlockPageActionsSection.activeFilters}}" stepKey="clickToResetFilter" visible="true"/> + <waitForPageLoad stepKey="waitForPageLoad2"/> + <click selector="{{BlockPageActionsSection.FilterBtn}}" stepKey="clickFilterBtn"/> + <fillField selector="{{BlockPageActionsSection.URLKey}}" userInput="{{Block.identifier}}" stepKey="fillBlockIdentifierInput"/> + <click selector="{{BlockPageActionsSection.ApplyFiltersBtn}}" stepKey="applyFilter"/> + <waitForLoadingMaskToDisappear stepKey="waitForGridToLoadResults"/> + <waitForElementVisible selector="{{BlockPageActionsSection.select(Block.identifier)}}" stepKey="waitForCMSPageGrid"/> + <click selector="{{BlockPageActionsSection.select(Block.identifier)}}" stepKey="clickSelect"/> + <waitForElementVisible selector="{{BlockPageActionsSection.edit(Block.identifier)}}" stepKey="waitForEditLink"/> + <click selector="{{BlockPageActionsSection.edit(Block.identifier)}}" stepKey="clickEdit"/> + <waitForLoadingMaskToDisappear stepKey="waitForPageToLoad"/> + <click selector="{{CmsBlockBlockActionSection.deleteBlock}}" stepKey="deleteBlock"/> + <waitForElementVisible selector="{{CmsBlockBlockActionSection.deleteConfirm}}" stepKey="waitForOkButtonToBeVisible"/> + <click selector="{{CmsBlockBlockActionSection.deleteConfirm}}" stepKey="clickOkButton"/> + <waitForPageLoad stepKey="waitForPageLoad3"/> + <see userInput="You deleted the block." stepKey="seeSuccessMessage"/> + </actionGroup> +</actionGroups> diff --git a/app/code/Magento/Cms/Test/Mftf/ActionGroup/SearchBlockOnGridPageActionGroup/SearchBlockOnGridPageActionGroup.xml b/app/code/Magento/Cms/Test/Mftf/ActionGroup/SearchBlockOnGridPageActionGroup/SearchBlockOnGridPageActionGroup.xml new file mode 100644 index 0000000000000..609a100fa380a --- /dev/null +++ b/app/code/Magento/Cms/Test/Mftf/ActionGroup/SearchBlockOnGridPageActionGroup/SearchBlockOnGridPageActionGroup.xml @@ -0,0 +1,23 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + /** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +--> +<actionGroups xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/actionGroupSchema.xsd"> + <actionGroup name="searchBlockOnGridPage"> + <annotations> + <description>Searches the Admin CMS Blocks grid based on the provided Block.</description> + </annotations> + <arguments> + <argument name="Block" defaultValue=""/> + </arguments> + + <fillField selector="//input[@name='chooser_identifier']" userInput="{{Block.identifier}}" stepKey="fillEntityIdentifier"/> + <click selector="//div[@class='modal-inner-wrap']//button[@title='Search']" stepKey="clickSearchBtn"/> + <waitForLoadingMaskToDisappear stepKey="waitForSecondIdSortDescendingToFinish2"/> + <waitForElementVisible selector="{{WidgetSection.BlockPage(Block.identifier)}}" stepKey="waitForBlockTitle"/> + </actionGroup> +</actionGroups> diff --git a/app/code/Magento/Cms/Test/Mftf/ActionGroup/StorefrontGoToCMSPageActionGroup.xml b/app/code/Magento/Cms/Test/Mftf/ActionGroup/StorefrontGoToCMSPageActionGroup.xml index 90f7a0036e1e6..ba0f728004ad3 100644 --- a/app/code/Magento/Cms/Test/Mftf/ActionGroup/StorefrontGoToCMSPageActionGroup.xml +++ b/app/code/Magento/Cms/Test/Mftf/ActionGroup/StorefrontGoToCMSPageActionGroup.xml @@ -16,7 +16,7 @@ <argument name="identifier" type="string"/> </arguments> - <amOnPage url="{{StorefrontHomePage.url}}{{identifier}}" stepKey="amOnCmsPageOnStorefront"/> + <amOnPage url="{{StorefrontHomePage.url}}/{{identifier}}" stepKey="amOnCmsPageOnStorefront"/> <waitForPageLoad stepKey="waitForPageLoadOnStorefront"/> </actionGroup> </actionGroups> diff --git a/app/code/Magento/Cms/Test/Mftf/ActionGroup/VerifyCmsBlockSaveSplitButtonActionGroup.xml b/app/code/Magento/Cms/Test/Mftf/ActionGroup/VerifyCmsBlockSaveSplitButtonActionGroup.xml new file mode 100644 index 0000000000000..bc30098cb43c7 --- /dev/null +++ b/app/code/Magento/Cms/Test/Mftf/ActionGroup/VerifyCmsBlockSaveSplitButtonActionGroup.xml @@ -0,0 +1,24 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + /** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +--> + +<actionGroups xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/actionGroupSchema.xsd"> + <actionGroup name="VerifyCmsBlockSaveSplitButtonActionGroup"> + <annotations> + <description>verify Save and Close and Save and Duplicate button.</description> + </annotations> + + <amOnPage url="{{CmsNewBlock.url}}" stepKey="amOnBlocksCreationForm"/> + <waitForPageLoad stepKey="waitForPageLoad1"/> + <!--Verify Save&Duplicate button and Save&Close button--> + <click selector="{{BlockNewPagePageActionsSection.expandSplitButton}}" stepKey="expandSplitBtn1" /> + <waitForElementVisible selector="{{BlockNewPagePageActionsSection.saveAndDuplicate}}" stepKey="waitForButtonMenuOpened"/> + <see selector="{{BlockNewPagePageActionsSection.saveAndDuplicate}}" userInput="Save & Duplicate" stepKey="seeSaveAndDuplicate"/> + <see selector="{{BlockNewPagePageActionsSection.saveAndClose}}" userInput="Save & Close" stepKey="seeSaveAndClose"/> + </actionGroup> +</actionGroups> diff --git a/app/code/Magento/Cms/Test/Mftf/ActionGroup/VerifyCreatedCmsPageActionGroup.xml b/app/code/Magento/Cms/Test/Mftf/ActionGroup/VerifyCreatedCmsPageActionGroup.xml new file mode 100644 index 0000000000000..5cbd3bf7072a0 --- /dev/null +++ b/app/code/Magento/Cms/Test/Mftf/ActionGroup/VerifyCreatedCmsPageActionGroup.xml @@ -0,0 +1,21 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + /** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +--> + +<actionGroups xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/actionGroupSchema.xsd"> + <actionGroup name="VerifyCreatedCmsPage"> + <annotations> + <description>Verify created cms page in store front. Values are hardcoded from _defaultCmsPage.</description> + </annotations> + + <amOnPage url="{{_defaultCmsPage.identifier}}" stepKey="amOnPageTestPage"/> + <waitForPageLoad stepKey="waitForPageLoad2"/> + <see userInput="{{_defaultCmsPage.content_heading}}" stepKey="seeContentHeading"/> + <see userInput="{{_defaultCmsPage.content}}" stepKey="seeContent"/> + </actionGroup> +</actionGroups> diff --git a/app/code/Magento/Cms/Test/Mftf/Data/CmsHomepageData.xml b/app/code/Magento/Cms/Test/Mftf/Data/CmsHomepageData.xml new file mode 100644 index 0000000000000..ce2ce747716a2 --- /dev/null +++ b/app/code/Magento/Cms/Test/Mftf/Data/CmsHomepageData.xml @@ -0,0 +1,16 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + /** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +--> + +<entities xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:noNamespaceSchemaLocation="urn:magento:mftf:DataGenerator/etc/dataProfileSchema.xsd"> + <entity name="CmsHomePageContent"> + <data key="content">CMS homepage content goes here</data> + <data key="page_id">2</data> + </entity> + +</entities> \ No newline at end of file diff --git a/app/code/Magento/Cms/Test/Mftf/Metadata/block-meta.xml b/app/code/Magento/Cms/Test/Mftf/Metadata/BlockMeta.xml similarity index 100% rename from app/code/Magento/Cms/Test/Mftf/Metadata/block-meta.xml rename to app/code/Magento/Cms/Test/Mftf/Metadata/BlockMeta.xml diff --git a/app/code/Magento/Cms/Test/Mftf/Metadata/cms-meta.xml b/app/code/Magento/Cms/Test/Mftf/Metadata/CmsMeta.xml similarity index 100% rename from app/code/Magento/Cms/Test/Mftf/Metadata/cms-meta.xml rename to app/code/Magento/Cms/Test/Mftf/Metadata/CmsMeta.xml diff --git a/app/code/Magento/Cms/Test/Mftf/Page/AdminCmsPageEditPage.xml b/app/code/Magento/Cms/Test/Mftf/Page/AdminCmsPageEditPage.xml index 978b6d6a6d261..3c5461ff3a031 100644 --- a/app/code/Magento/Cms/Test/Mftf/Page/AdminCmsPageEditPage.xml +++ b/app/code/Magento/Cms/Test/Mftf/Page/AdminCmsPageEditPage.xml @@ -7,7 +7,7 @@ --> <pages xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="urn:magento:mftf:Page/etc/PageObject.xsd"> - <page name="AdminCmsPageEditPage" area="admin" url="/cms/page/edit/page_id/{{id}}" parameterized="true" module="Magento_Cms"> + <page name="AdminCmsPageEditPage" area="admin" url="/cms/page/edit/page_id/{{pageId}}" parameterized="true" module="Magento_Cms"> <section name="CmsNewPagePageActionsSection"/> <section name="CmsNewPagePageBasicFieldsSection"/> <section name="CmsNewPagePageContentSection"/> diff --git a/app/code/Magento/Cms/Test/Mftf/Page/CmsPageEditPage.xml b/app/code/Magento/Cms/Test/Mftf/Page/CmsPageEditPage.xml deleted file mode 100644 index 73db6b61343b1..0000000000000 --- a/app/code/Magento/Cms/Test/Mftf/Page/CmsPageEditPage.xml +++ /dev/null @@ -1,16 +0,0 @@ -<?xml version="1.0" encoding="utf-8"?> -<!-- -/** - * Copyright © Magento, Inc. All rights reserved. - * See COPYING.txt for license details. - */ - --> -<pages xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" - xsi:noNamespaceSchemaLocation="urn:magento:mftf:Page/etc/PageObject.xsd"> - <page name="CmsPageEditPage" area="admin" url="admin/cms_page/edit/page_id/{{var}}" parameterized="true" module="Magento_Cms"> - <section name="CmsNewPagePageActionsSection"/> - <section name="CmsNewPagePageBasicFieldsSection"/> - <section name="CmsNewPagePageContentSection"/> - <section name="CmsNewPagePageSeoSection"/> - </page> -</pages> diff --git a/app/code/Magento/Cms/Test/Mftf/Section/AdminRecentlyViewedWidgetSection.xml b/app/code/Magento/Cms/Test/Mftf/Section/AdminRecentlyViewedWidgetSection.xml new file mode 100644 index 0000000000000..37d1491945491 --- /dev/null +++ b/app/code/Magento/Cms/Test/Mftf/Section/AdminRecentlyViewedWidgetSection.xml @@ -0,0 +1,14 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + /** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +--> + +<sections xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Page/etc/SectionObject.xsd"> + <section name="AdminRecentlyViewedWidgetSection"> + <element name="attributeSelector" type="multiselect" selector="select[name='parameters[{{attributeName}}][]'] option:nth-of-type({{attributePosition}})" parameterized="true"/> + </section> +</sections> diff --git a/app/code/Magento/Cms/Test/Mftf/Section/CmsNewBlockBlockActionsSection.xml b/app/code/Magento/Cms/Test/Mftf/Section/CmsNewBlockBlockActionsSection.xml deleted file mode 100644 index 445279a8b1403..0000000000000 --- a/app/code/Magento/Cms/Test/Mftf/Section/CmsNewBlockBlockActionsSection.xml +++ /dev/null @@ -1,34 +0,0 @@ -<?xml version="1.0" encoding="UTF-8"?> -<!-- - /** - * Copyright © Magento, Inc. All rights reserved. - * See COPYING.txt for license details. - */ ---> - -<sections xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" - xsi:noNamespaceSchemaLocation="urn:magento:mftf:Page/etc/SectionObject.xsd"> - <section name="CmsNewBlockBlockActionsSection"> - <element name="savePage" type="button" selector="#save-button" timeout="30"/> - </section> - <section name="BlockNewPagePageActionsSection"> - <element name="saveBlock" type="button" selector="#save-button" timeout="10"/> - <element name="saveAndContinueEdit" type="button" selector="#save-button" timeout="10"/> - <element name="saveAndDuplicate" type="button" selector="#save_and_duplicate" timeout="10"/> - <element name="saveAndClose" type="button" selector="#save_and_close" timeout="10"/> - <element name="expandSplitButton" type="button" selector="//button[@data-ui-id='save-button-dropdown']" timeout="10"/> - <element name="back" type="button" selector="#back"/> - </section> - <section name="BlockWYSIWYGSection"> - <element name="ShowHideBtn" type="button" selector="#togglecms_block_form_content"/> - </section> - <section name="BlockContentSection"> - <element name="TextArea" type="input" selector="#cms_block_form_content"/> - <element name="image" type="file" selector="#tinymce img"/> - <element name="contentIframe" type="iframe" selector="cms_block_form_content_ifr"/> - </section> - <section name="CmsBlockBlockActionSection"> - <element name="deleteBlock" type="button" selector="#delete" timeout="30"/> - <element name="deleteConfirm" type="button" selector=".action-primary.action-accept" timeout="60"/> - </section> -</sections> diff --git a/app/code/Magento/Cms/Test/Mftf/Section/CmsNewBlockBlockActionsSection/BlockContentSection.xml b/app/code/Magento/Cms/Test/Mftf/Section/CmsNewBlockBlockActionsSection/BlockContentSection.xml new file mode 100644 index 0000000000000..1d5e8541dd497 --- /dev/null +++ b/app/code/Magento/Cms/Test/Mftf/Section/CmsNewBlockBlockActionsSection/BlockContentSection.xml @@ -0,0 +1,15 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + /** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +--> +<sections xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Page/etc/SectionObject.xsd"> + <section name="BlockContentSection"> + <element name="TextArea" type="input" selector="#cms_block_form_content"/> + <element name="image" type="file" selector="#tinymce img"/> + <element name="contentIframe" type="iframe" selector="cms_block_form_content_ifr"/> + </section> +</sections> diff --git a/app/code/Magento/Cms/Test/Mftf/Section/CmsNewBlockBlockActionsSection/BlockNewPagePageActionsSection.xml b/app/code/Magento/Cms/Test/Mftf/Section/CmsNewBlockBlockActionsSection/BlockNewPagePageActionsSection.xml new file mode 100644 index 0000000000000..e9c96ba8a8b2e --- /dev/null +++ b/app/code/Magento/Cms/Test/Mftf/Section/CmsNewBlockBlockActionsSection/BlockNewPagePageActionsSection.xml @@ -0,0 +1,18 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + /** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +--> +<sections xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Page/etc/SectionObject.xsd"> + <section name="BlockNewPagePageActionsSection"> + <element name="saveBlock" type="button" selector="#save-button" timeout="10"/> + <element name="saveAndContinueEdit" type="button" selector="#save-button" timeout="10"/> + <element name="saveAndDuplicate" type="button" selector="#save_and_duplicate" timeout="10"/> + <element name="saveAndClose" type="button" selector="#save_and_close" timeout="10"/> + <element name="expandSplitButton" type="button" selector="//button[@data-ui-id='save-button-dropdown']" timeout="10"/> + <element name="back" type="button" selector="#back"/> + </section> +</sections> diff --git a/app/code/Magento/Cms/Test/Mftf/Section/CmsNewBlockBlockActionsSection/BlockWYSIWYGSection.xml b/app/code/Magento/Cms/Test/Mftf/Section/CmsNewBlockBlockActionsSection/BlockWYSIWYGSection.xml new file mode 100644 index 0000000000000..bb213969c9dab --- /dev/null +++ b/app/code/Magento/Cms/Test/Mftf/Section/CmsNewBlockBlockActionsSection/BlockWYSIWYGSection.xml @@ -0,0 +1,13 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + /** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +--> +<sections xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Page/etc/SectionObject.xsd"> + <section name="BlockWYSIWYGSection"> + <element name="ShowHideBtn" type="button" selector="#togglecms_block_form_content"/> + </section> +</sections> diff --git a/app/code/Magento/Cms/Test/Mftf/Section/CmsNewBlockBlockActionsSection/CmsBlockBlockActionSection.xml b/app/code/Magento/Cms/Test/Mftf/Section/CmsNewBlockBlockActionsSection/CmsBlockBlockActionSection.xml new file mode 100644 index 0000000000000..24a235bee2f78 --- /dev/null +++ b/app/code/Magento/Cms/Test/Mftf/Section/CmsNewBlockBlockActionsSection/CmsBlockBlockActionSection.xml @@ -0,0 +1,14 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + /** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +--> +<sections xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Page/etc/SectionObject.xsd"> + <section name="CmsBlockBlockActionSection"> + <element name="deleteBlock" type="button" selector="#delete" timeout="30"/> + <element name="deleteConfirm" type="button" selector=".action-primary.action-accept" timeout="60"/> + </section> +</sections> diff --git a/app/code/Magento/Cms/Test/Mftf/Section/CmsNewBlockBlockActionsSection/CmsNewBlockBlockActionsSection.xml b/app/code/Magento/Cms/Test/Mftf/Section/CmsNewBlockBlockActionsSection/CmsNewBlockBlockActionsSection.xml new file mode 100644 index 0000000000000..a3377488c04e0 --- /dev/null +++ b/app/code/Magento/Cms/Test/Mftf/Section/CmsNewBlockBlockActionsSection/CmsNewBlockBlockActionsSection.xml @@ -0,0 +1,13 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + /** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +--> +<sections xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Page/etc/SectionObject.xsd"> + <section name="CmsNewBlockBlockActionsSection"> + <element name="savePage" type="button" selector="#save-button" timeout="30"/> + </section> +</sections> diff --git a/app/code/Magento/Cms/Test/Mftf/Section/CmsNewBlockBlockBasicFieldsSection.xml b/app/code/Magento/Cms/Test/Mftf/Section/CmsNewBlockBlockBasicFieldsSection.xml deleted file mode 100644 index 79fc3bac0fb25..0000000000000 --- a/app/code/Magento/Cms/Test/Mftf/Section/CmsNewBlockBlockBasicFieldsSection.xml +++ /dev/null @@ -1,23 +0,0 @@ -<?xml version="1.0" encoding="UTF-8"?> -<!-- - /** - * Copyright © Magento, Inc. All rights reserved. - * See COPYING.txt for license details. - */ ---> - -<sections xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" - xsi:noNamespaceSchemaLocation="urn:magento:mftf:Page/etc/SectionObject.xsd"> - <section name="CmsNewBlockBlockBasicFieldsSection"> - <element name="title" type="input" selector="input[name=title]"/> - <element name="identifier" type="input" selector="input[name=identifier]"/> - <element name="content_textarea" type="input" selector="#cms_block_form_content"/> - </section> - <section name="BlockNewPageBasicFieldsSection"> - <element name="isActive" type="button" selector="//input[@name='is_active' and @value='{{var1}}']" parameterized="true"/> - <element name="blockTitle" type="input" selector="input[name=title]"/> - <element name="identifier" type="input" selector="input[name=identifier]"/> - <element name="storeView" type="multiselect" selector="select[name=store_id]"/> - <element name="duplicatedIdentifier" type="input" selector="//input[contains(@data-value,'{{var1}}')]" parameterized="true"/> - </section> -</sections> diff --git a/app/code/Magento/Cms/Test/Mftf/Section/CmsNewBlockBlockBasicFieldsSection/BlockNewPageBasicFieldsSection.xml b/app/code/Magento/Cms/Test/Mftf/Section/CmsNewBlockBlockBasicFieldsSection/BlockNewPageBasicFieldsSection.xml new file mode 100644 index 0000000000000..9b6b00602b753 --- /dev/null +++ b/app/code/Magento/Cms/Test/Mftf/Section/CmsNewBlockBlockBasicFieldsSection/BlockNewPageBasicFieldsSection.xml @@ -0,0 +1,18 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + /** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +--> +<sections xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Page/etc/SectionObject.xsd"> + <section name="BlockNewPageBasicFieldsSection"> + <element name="isActive" type="button" selector="//input[@name='is_active' and @value='{{var1}}']" parameterized="true"/> + <element name="blockTitle" type="input" selector="input[name=title]"/> + <element name="identifier" type="input" selector="input[name=identifier]"/> + <element name="storeView" type="multiselect" selector="select[name=store_id]"/> + <element name="duplicatedIdentifier" type="input" selector="//input[contains(@data-value,'{{var1}}')]" parameterized="true"/> + <element name="isActiveLabel" type="button" selector="div[data-index=is_active] .admin__actions-switch-label"/> + </section> +</sections> diff --git a/app/code/Magento/Cms/Test/Mftf/Section/CmsNewBlockBlockBasicFieldsSection/CmsNewBlockBlockBasicFieldsSection.xml b/app/code/Magento/Cms/Test/Mftf/Section/CmsNewBlockBlockBasicFieldsSection/CmsNewBlockBlockBasicFieldsSection.xml new file mode 100644 index 0000000000000..06fd4c17c274e --- /dev/null +++ b/app/code/Magento/Cms/Test/Mftf/Section/CmsNewBlockBlockBasicFieldsSection/CmsNewBlockBlockBasicFieldsSection.xml @@ -0,0 +1,15 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + /** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +--> +<sections xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Page/etc/SectionObject.xsd"> + <section name="CmsNewBlockBlockBasicFieldsSection"> + <element name="title" type="input" selector="input[name=title]"/> + <element name="identifier" type="input" selector="input[name=identifier]"/> + <element name="content_textarea" type="input" selector="#cms_block_form_content"/> + </section> +</sections> diff --git a/app/code/Magento/Cms/Test/Mftf/Section/CmsNewPagePageBasicFieldsSection.xml b/app/code/Magento/Cms/Test/Mftf/Section/CmsNewPagePageBasicFieldsSection.xml index 7288e5d455d52..92170c447fa11 100644 --- a/app/code/Magento/Cms/Test/Mftf/Section/CmsNewPagePageBasicFieldsSection.xml +++ b/app/code/Magento/Cms/Test/Mftf/Section/CmsNewPagePageBasicFieldsSection.xml @@ -12,6 +12,7 @@ <element name="pageTitle" type="input" selector="input[name=title]"/> <element name="RequiredFieldIndicator" type="text" selector=" return window.getComputedStyle(document.querySelector('._required[data-index=title]>.admin__field-label span'), ':after').getPropertyValue('content');"/> <element name="isActive" type="button" selector="//input[@name='is_active' and @value='{{var1}}']" parameterized="true"/> + <element name="isActiveLabel" type="button" selector="div[data-index=is_active] .admin__actions-switch-label"/> <element name="duplicatedURLKey" type="input" selector="//input[contains(@data-value,'{{var1}}')]" parameterized="true"/> </section> </sections> diff --git a/app/code/Magento/Cms/Test/Mftf/Section/CmsNewPagePageContentSection.xml b/app/code/Magento/Cms/Test/Mftf/Section/CmsNewPagePageContentSection.xml deleted file mode 100644 index 05a125b9cc6a8..0000000000000 --- a/app/code/Magento/Cms/Test/Mftf/Section/CmsNewPagePageContentSection.xml +++ /dev/null @@ -1,29 +0,0 @@ -<?xml version="1.0" encoding="UTF-8"?> -<!-- - /** - * Copyright © Magento, Inc. All rights reserved. - * See COPYING.txt for license details. - */ ---> - -<sections xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" - xsi:noNamespaceSchemaLocation="urn:magento:mftf:Page/etc/SectionObject.xsd"> - <section name="CmsNewPagePageContentSection"> - <element name="header" type="button" selector="div[data-index=content]"/> - <element name="contentHeading" type="input" selector="input[name=content_heading]"/> - <element name="content" type="input" selector="#cms_page_form_content"/> - </section> - <section name="CmsWYSIWYGSection"> - <element name="CheckIfTabExpand" type="button" selector="//div[@data-state-collapsible='closed']//span[text()='Content']"/> - <element name="ShowHideBtn" type="button" selector="#togglecms_page_form_content"/> - <element name="InsertWidgetBtn" type="button" selector=".action-add-widget"/> - <element name="InsertVariableBtn" type="button" selector=".scalable.add-variable.plugin"/> - <element name="InsertImageBtn" type="button" selector=".scalable.action-add-image.plugin"/> - <element name="imageSource" type="text" selector="//img[contains(@src,'{{var1}}')]" parameterized="true"/> - <element name="ImageAlt" type="text" selector="//img[contains(@alt,'{{var1}}')]" parameterized="true"/> - </section> - <section name="CmsDesignSection"> - <element name="DesignTab" type="button" selector="//strong[@class='admin__collapsible-title']//span[text()='Design']"/> - <element name="LayoutDropdown" type="select" selector="select[name='page_layout']"/> - </section> -</sections> diff --git a/app/code/Magento/Cms/Test/Mftf/Section/CmsNewPagePageContentSection/CmsDesignSection.xml b/app/code/Magento/Cms/Test/Mftf/Section/CmsNewPagePageContentSection/CmsDesignSection.xml new file mode 100644 index 0000000000000..cf0f6797ce423 --- /dev/null +++ b/app/code/Magento/Cms/Test/Mftf/Section/CmsNewPagePageContentSection/CmsDesignSection.xml @@ -0,0 +1,14 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + /** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +--> +<sections xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Page/etc/SectionObject.xsd"> + <section name="CmsDesignSection"> + <element name="DesignTab" type="button" selector="//strong[@class='admin__collapsible-title']//span[text()='Design']"/> + <element name="LayoutDropdown" type="select" selector="select[name='page_layout']"/> + </section> +</sections> diff --git a/app/code/Magento/Cms/Test/Mftf/Section/CmsNewPagePageContentSection/CmsNewPagePageContentSection.xml b/app/code/Magento/Cms/Test/Mftf/Section/CmsNewPagePageContentSection/CmsNewPagePageContentSection.xml new file mode 100644 index 0000000000000..6b0569adb878e --- /dev/null +++ b/app/code/Magento/Cms/Test/Mftf/Section/CmsNewPagePageContentSection/CmsNewPagePageContentSection.xml @@ -0,0 +1,15 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + /** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +--> +<sections xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Page/etc/SectionObject.xsd"> + <section name="CmsNewPagePageContentSection"> + <element name="header" type="button" selector="div[data-index=content]"/> + <element name="contentHeading" type="input" selector="input[name=content_heading]"/> + <element name="content" type="input" selector="#cms_page_form_content"/> + </section> +</sections> diff --git a/app/code/Magento/Cms/Test/Mftf/Section/CmsNewPagePageContentSection/CmsWYSIWYGSection.xml b/app/code/Magento/Cms/Test/Mftf/Section/CmsNewPagePageContentSection/CmsWYSIWYGSection.xml new file mode 100644 index 0000000000000..d88838563ec0f --- /dev/null +++ b/app/code/Magento/Cms/Test/Mftf/Section/CmsNewPagePageContentSection/CmsWYSIWYGSection.xml @@ -0,0 +1,19 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + /** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +--> +<sections xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Page/etc/SectionObject.xsd"> + <section name="CmsWYSIWYGSection"> + <element name="CheckIfTabExpand" type="button" selector="//div[@data-state-collapsible='closed']//span[text()='Content']"/> + <element name="ShowHideBtn" type="button" selector="#togglecms_page_form_content"/> + <element name="InsertWidgetBtn" type="button" selector=".action-add-widget"/> + <element name="InsertVariableBtn" type="button" selector=".scalable.add-variable.plugin"/> + <element name="InsertImageBtn" type="button" selector=".scalable.action-add-image.plugin"/> + <element name="imageSource" type="text" selector="//img[contains(@src,'{{var1}}')]" parameterized="true"/> + <element name="ImageAlt" type="text" selector="//img[contains(@alt,'{{var1}}')]" parameterized="true"/> + </section> +</sections> diff --git a/app/code/Magento/Cms/Test/Mftf/Section/CmsNewPagePiwSection.xml b/app/code/Magento/Cms/Test/Mftf/Section/CmsNewPagePiwSection.xml index bd487e3b2c03c..7bb6e606d7943 100644 --- a/app/code/Magento/Cms/Test/Mftf/Section/CmsNewPagePiwSection.xml +++ b/app/code/Magento/Cms/Test/Mftf/Section/CmsNewPagePiwSection.xml @@ -11,5 +11,6 @@ <section name="CmsNewPagePiwSection"> <element name="header" type="button" selector="div[data-index=websites]" timeout="30"/> <element name="selectStoreView" type="select" selector="//option[contains(text(),'{{var1}}')]" parameterized="true"/> + <element name="storeIdDropdown" type="select" selector="//div[@data-bind="scope: 'cms_page_form.cms_page_form'"]//select[@name='store_id']"/> </section> -</sections> \ No newline at end of file +</sections> diff --git a/app/code/Magento/Cms/Test/Mftf/Section/TinyMCESection.xml b/app/code/Magento/Cms/Test/Mftf/Section/TinyMCESection.xml deleted file mode 100644 index b85c7554b58ae..0000000000000 --- a/app/code/Magento/Cms/Test/Mftf/Section/TinyMCESection.xml +++ /dev/null @@ -1,122 +0,0 @@ -<?xml version="1.0" encoding="UTF-8"?> -<!-- - /** - * Copyright © Magento, Inc. All rights reserved. - * See COPYING.txt for license details. - */ ---> - -<sections xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" - xsi:noNamespaceSchemaLocation="urn:magento:mftf:Page/etc/SectionObject.xsd"> - <section name="TinyMCESection"> - <element name="checkIfContentTabOpen" type="button" selector="//span[text()='Content']/parent::strong/parent::*[@data-state-collapsible='closed']"/> - <element name="CheckIfTabExpand" type="button" selector="//div[@data-state-collapsible='closed']//span[text()='Content']"/> - <element name="TinyMCE4" type="text" selector=".mce-branding" /> - <element name="InsertWidgetBtn" type="button" selector=".action-add-widget"/> - <element name="InsertWidgetIcon" type="button" selector="div[aria-label='Insert Widget']"/> - <element name="InsertVariableBtn" type="button" selector=".scalable.add-variable.plugin"/> - <element name="InsertVariableIcon" type="button" selector="div[aria-label='Insert Variable']"/> - <element name="InsertImageBtn" type="button" selector=".scalable.action-add-image.plugin"/> - <element name="InsertImageIcon" type="button" selector=".mce-i-image"/> - <element name="Style" type="button" selector=".mce-txt" /> - <element name="Bold" type="button" selector=".mce-i-bold" /> - <element name="Italic" type="button" selector=".mce-i-italic" /> - <element name="Underline" type="button" selector=".mce-i-underline" /> - <element name="AlignLeft" type="button" selector=".mce-i-alignleft" /> - <element name="AlignCenter" type="button" selector=".mce-i-aligncenter" /> - <element name="AlignRight" type="button" selector=".mce-i-alignright" /> - <element name="Bullet" type="button" selector=".mce-i-bullist" /> - <element name="Numlist" type="button" selector=".mce-i-numlist" /> - <element name="InsertLink" type="button" selector=".mce-i-link" /> - <element name="InsertImage" type="button" selector=".mce-i-image" /> - <element name="InsertTable" type="button" selector=".mce-i-table" /> - <element name="SpecialCharacter" type="button" selector=".mce-i-charmap" /> - <element name="WidgetButton" type="button" selector="span[class*='magento-widget mceNonEditable']"/> - <element name="EditorContent" type="input" selector="#tinymce"/> - </section> - <section name="MediaGallerySection"> - <element name="Browse" type="button" selector=".mce-i-browse"/> - <element name="browseForImage" type="button" selector="//*[@id='srcbrowser']"/> - <element name="BrowseUploadImage" type="file" selector=".fileupload" /> - <element name="image" type="text" selector="//small[text()='{{var1}}']" parameterized="true"/> - <element name="imageOrImageCopy" type="text" selector="//div[contains(@class,'media-gallery-modal')]//img[contains(@alt, '{{arg1}}.{{arg2}}')]|//img[contains(@alt,'{{arg1}}_') and contains(@alt,'.{{arg2}}')]" parameterized="true"/> - <element name="lastImageOrImageCopy" type="text" selector="(//div[contains(@class,'media-gallery-modal')]//img[contains(@alt, '{{arg1}}.{{arg2}}')]|//img[contains(@alt,'{{arg1}}_') and contains(@alt,'.{{arg2}}')])[last()]" parameterized="true"/> - <element name="imageSelected" type="text" selector="//small[text()='{{var1}}']/parent::*[@class='filecnt selected']" parameterized="true"/> - <element name="ImageSource" type="input" selector=".mce-combobox.mce-abs-layout-item.mce-last.mce-has-open" /> - <element name="ImageDescription" type="input" selector=".mce-textbox.mce-abs-layout-item.mce-last" /> - <element name="ImageDescriptionTinyMCE3" type="input" selector="#alt" /> - <element name="Height" type="input" selector=".mce-textbox.mce-abs-layout-item.mce-first" /> - <element name="UploadImage" type="file" selector=".fileupload" /> - <element name="OkBtn" type="button" selector="//span[text()='Ok']"/> - <element name="insertBtn" type="button" selector="#insert"/> - <element name="InsertFile" type="text" selector="#insert_files"/> - <element name="CreateFolder" type="button" selector="#new_folder" /> - <element name="DeleteSelectedBtn" type="text" selector="#delete_files"/> - <element name="CancelBtn" type="button" selector="#cancel" /> - <element name="FolderName" type="button" selector="input[data-role='promptField']" /> - <element name="AcceptFolderName" type="button" selector=".action-primary.action-accept" /> - <element name="StorageRootArrow" type="button" selector="#root > .jstree-icon" /> - <element name="checkIfArrowExpand" type="button" selector="//li[@id='root' and contains(@class,'jstree-closed')]" /> - <element name="WysiwygArrow" type="button" selector="#d3lzaXd5Zw-- > .jstree-icon" /> - <element name="checkIfWysiwygArrowExpand" type="button" selector="//li[@id='d3lzaXd5Zw--' and contains(@class,'jstree-closed')]" /> - <element name="confirmDelete" type="button" selector=".action-primary.action-accept" /> - <element name="imageBlockByName" type="block" selector="//div[@data-row='file'][contains(., '{{imageName}}')]" parameterized="true"/> - </section> - <section name="VariableSection"> - <element name="InsertWidget" type="button" selector="#insert_variable"/> - <element name="InsertVariableBtnEnabled" type="button" selector="//button[@id='insert_variable' and not(contains(@class,'disabled'))]"/> - <element name="InsertVariableBtnDisabled" type="button" selector="//button[@id='insert_variable' and contains(@class,'disabled')]"/> - <element name="CancelBtnEnabled" type="button" selector="//button[@class='action-scalable cancel' and not(contains(@class,'disabled'))]"/> - <element name="Close" type="button" selector="#close"/> - <element name="SearchTxtbox" type="input" selector="input[placeholder='Search by keyword']"/> - <element name="ColName" type="text" selector="//table[@class='data-grid data-grid-draggable']/thead/tr/th/span[text()='{{var1}}']" parameterized="true"/> - <element name="Radio" type="input" selector="//input[@type='radio' and contains(@value, '{{var1}}')]" parameterized="true"/> - <element name="VariableRadio" type="input" selector="//div[text()='{{var1}}']/parent::td//preceding-sibling::td/input[@type='radio']" parameterized="true"/> - <element name="VariableInAscSort" type="input" selector="#variable"/> - <element name="VariableInDescSort" type="input" selector="#variable"/> - <element name="Type" type="input" selector="#value"/> - <element name="Code" type="input" selector="#code"/> - <element name="searchResult" type="text" selector="//table/tbody/tr//td/div[text()='{{var1}}']" parameterized="true" /> - <element name="VariableTitle" type="text" selector="//h1[contains(text(), 'Insert Variable')]"/> - </section> - <section name="WidgetSection"> - <element name="InsertWidgetTitle" type="text" selector="//h1[contains(text(),'Insert Widget')]"/> - <element name="DisplayType" type="select" selector="select[name='parameters[display_type]']"/> - <element name="SelectCategoryTitle" type="text" selector="//h1[contains(text(),'Select Category')]"/> - <element name="SelectProductTitle" type="text" selector="//h1[contains(text(),'Select Product')]"/> - <element name="SelectPageTitle" type="text" selector="//h1[contains(text(),'Select Page')]"/> - <element name="SelectBlockTitle" type="text" selector="//h1[contains(text(),'Select Block')]"/> - <element name="InsertWidget" type="button" selector="#insert_button" timeout="30"/> - <element name="InsertWidgetBtnDisabled" type="button" selector="//button[@id='insert_button' and contains(@class,'disabled')]"/> - <element name="InsertWidgetBtnEnabled" type="button" selector="//button[@id='insert_button' and not(contains(@class,'disabled'))]"/> - <element name="CancelBtnEnabled" type="button" selector="//button[@id='reset' and not(contains(@class,'disabled'))]"/> - <element name="Close" type="button" selector="#close"/> - <element name="WidgetType" type="button" selector="#select_widget_type"/> - <element name="WidgetTemplate" type="button" selector="select[name='parameters[template]']"/> - <element name="BtnChooser" type="button" selector=".btn-chooser"/> - <element name="CMSPage" type="text" selector="//td[contains(text(),'Home page')]"/> - <element name="BlockPage" type="text" selector="//td[contains(text(),'{{var1}}')]" parameterized="true"/> - <element name="PreCreateCategory" type="text" selector=" //span[contains(text(),'{{var1}}')]" parameterized="true"/> - <element name="PreCreateProduct" type="text" selector="//td[contains(text(),'{{var1}}')]" parameterized="true"/> - <element name="NoOfProductToDisplay" type="input" selector="input[data-ui-id='wysiwyg-widget-options-fieldset-element-text-parameters-products-count']"/> - <element name="AddParam" type="button" selector=".rule-param-add"/> - <element name="ConditionsDropdown" type="select" selector="#conditions__1__new_child"/> - <element name="RuleParam" type="button" selector="//a[text()='...']"/> - <element name="RuleParam1" type="button" selector="(//span[@class='rule-param']//a)[{{var}}]" parameterized="true"/> - <element name="RuleParamSelect" type="select" selector="//ul[contains(@class,'rule-param-children')]/li[{{arg1}}]//*[contains(@class,'rule-param')][{{arg2}}]//select" parameterized="true"/> - <element name="RuleParamInput" type="input" selector="//ul[contains(@class,'rule-param-children')]/li[{{arg1}}]//*[contains(@class,'rule-param')][{{arg2}}]//input" parameterized="true"/> - <element name="RuleParamLabel" type="input" selector="//ul[contains(@class,'rule-param-children')]/li[{{arg1}}]//*[contains(@class,'rule-param')][{{arg2}}]//a" parameterized="true"/> - <element name="Chooser" type="button" selector="//img[@title='Open Chooser']"/> - <element name="PageSize" type="input" selector="input[name='parameters[page_size]']"/> - <element name="ProductAttribute" type="multiselect" selector="select[name='parameters[show_attributes][]']" /> - <element name="ButtonToShow" type="multiselect" selector="select[name='parameters[show_buttons][]']"/> - <!--Compare on Storefront--> - <element name="ProductName" type="text" selector=".product.name.product-item-name" /> - <element name="CompareBtn" type="button" selector=".action.tocompare"/> - <element name="ClearCompare" type="button" selector="#compare-clear-all"/> - <element name="AcceptClear" type="button" selector=".action-primary.action-accept" /> - <element name="ChooserName" type="input" selector="input[name='chooser_name']" /> - <element name="SelectPageButton" type="button" selector="//button[@title='Select Page...']"/> - <element name="SelectPageFilterInput" type="input" selector="input.admin__control-text[name='{{filterName}}']" parameterized="true"/> - </section> -</sections> diff --git a/app/code/Magento/Cms/Test/Mftf/Section/TinyMCESection/MediaGallerySection.xml b/app/code/Magento/Cms/Test/Mftf/Section/TinyMCESection/MediaGallerySection.xml new file mode 100644 index 0000000000000..112335e726270 --- /dev/null +++ b/app/code/Magento/Cms/Test/Mftf/Section/TinyMCESection/MediaGallerySection.xml @@ -0,0 +1,39 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + /** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +--> +<sections xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Page/etc/SectionObject.xsd"> + <section name="MediaGallerySection"> + <element name="Browse" type="button" selector=".mce-i-browse"/> + <element name="browseForImage" type="button" selector="//*[@id='srcbrowser']"/> + <element name="BrowseUploadImage" type="file" selector=".fileupload"/> + <element name="image" type="text" selector="//small[text()='{{var1}}']" parameterized="true"/> + <element name="imageOrImageCopy" type="text" selector="//div[contains(@class,'media-gallery-modal')]//img[contains(@alt, '{{arg1}}.{{arg2}}')]|//img[contains(@alt,'{{arg1}}_') and contains(@alt,'.{{arg2}}')]" parameterized="true"/> + <element name="lastImageOrImageCopy" type="text" selector="(//div[contains(@class,'media-gallery-modal')]//img[contains(@alt, '{{arg1}}.{{arg2}}')]|//img[contains(@alt,'{{arg1}}_') and contains(@alt,'.{{arg2}}')])[last()]" parameterized="true"/> + <element name="imageSelected" type="text" selector="//small[text()='{{var1}}']/parent::*[@class='filecnt selected']" parameterized="true"/> + <element name="ImageSource" type="input" selector=".mce-combobox.mce-abs-layout-item.mce-last.mce-has-open"/> + <element name="ImageDescription" type="input" selector=".mce-textbox.mce-abs-layout-item.mce-last"/> + <element name="ImageDescriptionTinyMCE3" type="input" selector="#alt"/> + <element name="Height" type="input" selector=".mce-textbox.mce-abs-layout-item.mce-first"/> + <element name="UploadImage" type="file" selector=".fileupload"/> + <element name="OkBtn" type="button" selector="//span[text()='Ok']"/> + <element name="insertBtn" type="button" selector="#insert"/> + <element name="InsertFile" type="text" selector="#insert_files"/> + <element name="CreateFolder" type="button" selector="#new_folder"/> + <element name="DeleteFolder" type="button" selector="#delete_folder"/> + <element name="DeleteSelectedBtn" type="text" selector="#delete_files"/> + <element name="CancelBtn" type="button" selector="#cancel"/> + <element name="FolderName" type="button" selector="input[data-role='promptField']"/> + <element name="AcceptFolderName" type="button" selector=".action-primary.action-accept"/> + <element name="StorageRootArrow" type="button" selector="#root > .jstree-icon"/> + <element name="checkIfArrowExpand" type="button" selector="//li[@id='root' and contains(@class,'jstree-closed')]"/> + <element name="WysiwygArrow" type="button" selector="#d3lzaXd5Zw-- > .jstree-icon"/> + <element name="checkIfWysiwygArrowExpand" type="button" selector="//li[@id='d3lzaXd5Zw--' and contains(@class,'jstree-closed')]"/> + <element name="confirmDelete" type="button" selector=".action-primary.action-accept"/> + <element name="imageBlockByName" type="block" selector="//div[@data-row='file'][contains(., '{{imageName}}')]" parameterized="true"/> + </section> +</sections> diff --git a/app/code/Magento/Cms/Test/Mftf/Section/TinyMCESection/TinyMCESection.xml b/app/code/Magento/Cms/Test/Mftf/Section/TinyMCESection/TinyMCESection.xml new file mode 100644 index 0000000000000..e3e6ae9cffc02 --- /dev/null +++ b/app/code/Magento/Cms/Test/Mftf/Section/TinyMCESection/TinyMCESection.xml @@ -0,0 +1,36 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + /** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +--> +<sections xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Page/etc/SectionObject.xsd"> + <section name="TinyMCESection"> + <element name="checkIfContentTabOpen" type="button" selector="//span[text()='Content']/parent::strong/parent::*[@data-state-collapsible='closed']"/> + <element name="CheckIfTabExpand" type="button" selector="//div[@data-state-collapsible='closed']//span[text()='Content']"/> + <element name="TinyMCE4" type="text" selector=".mce-branding"/> + <element name="InsertWidgetBtn" type="button" selector=".action-add-widget"/> + <element name="InsertWidgetIcon" type="button" selector="div[aria-label='Insert Widget']"/> + <element name="InsertVariableBtn" type="button" selector=".scalable.add-variable.plugin"/> + <element name="InsertVariableIcon" type="button" selector="div[aria-label='Insert Variable']"/> + <element name="InsertImageBtn" type="button" selector=".scalable.action-add-image.plugin"/> + <element name="InsertImageIcon" type="button" selector=".mce-i-image"/> + <element name="Style" type="button" selector=".mce-txt"/> + <element name="Bold" type="button" selector=".mce-i-bold"/> + <element name="Italic" type="button" selector=".mce-i-italic"/> + <element name="Underline" type="button" selector=".mce-i-underline"/> + <element name="AlignLeft" type="button" selector=".mce-i-alignleft"/> + <element name="AlignCenter" type="button" selector=".mce-i-aligncenter"/> + <element name="AlignRight" type="button" selector=".mce-i-alignright"/> + <element name="Bullet" type="button" selector=".mce-i-bullist"/> + <element name="Numlist" type="button" selector=".mce-i-numlist"/> + <element name="InsertLink" type="button" selector=".mce-i-link"/> + <element name="InsertImage" type="button" selector=".mce-i-image"/> + <element name="InsertTable" type="button" selector=".mce-i-table"/> + <element name="SpecialCharacter" type="button" selector=".mce-i-charmap"/> + <element name="WidgetButton" type="button" selector="span[class*='magento-widget mceNonEditable']"/> + <element name="EditorContent" type="input" selector="#tinymce"/> + </section> +</sections> diff --git a/app/code/Magento/Cms/Test/Mftf/Section/TinyMCESection/VariableSection.xml b/app/code/Magento/Cms/Test/Mftf/Section/TinyMCESection/VariableSection.xml new file mode 100644 index 0000000000000..b63a355bc797e --- /dev/null +++ b/app/code/Magento/Cms/Test/Mftf/Section/TinyMCESection/VariableSection.xml @@ -0,0 +1,27 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + /** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +--> +<sections xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Page/etc/SectionObject.xsd"> + <section name="VariableSection"> + <element name="InsertWidget" type="button" selector="#insert_variable"/> + <element name="InsertVariableBtnEnabled" type="button" selector="//button[@id='insert_variable' and not(contains(@class,'disabled'))]"/> + <element name="InsertVariableBtnDisabled" type="button" selector="//button[@id='insert_variable' and contains(@class,'disabled')]"/> + <element name="CancelBtnEnabled" type="button" selector="//button[@class='action-scalable cancel' and not(contains(@class,'disabled'))]"/> + <element name="Close" type="button" selector="#close"/> + <element name="SearchTxtbox" type="input" selector="input[placeholder='Search by keyword']"/> + <element name="ColName" type="text" selector="//table[@class='data-grid data-grid-draggable']/thead/tr/th/span[text()='{{var1}}']" parameterized="true"/> + <element name="Radio" type="input" selector="//input[@type='radio' and contains(@value, '{{var1}}')]" parameterized="true"/> + <element name="VariableRadio" type="input" selector="//div[text()='{{var1}}']/parent::td//preceding-sibling::td/input[@type='radio']" parameterized="true"/> + <element name="VariableInAscSort" type="input" selector="#variable"/> + <element name="VariableInDescSort" type="input" selector="#variable"/> + <element name="Type" type="input" selector="#value"/> + <element name="Code" type="input" selector="#code"/> + <element name="searchResult" type="text" selector="//table/tbody/tr//td/div[text()='{{var1}}']" parameterized="true"/> + <element name="VariableTitle" type="text" selector="//h1[contains(text(), 'Insert Variable')]"/> + </section> +</sections> diff --git a/app/code/Magento/Cms/Test/Mftf/Section/TinyMCESection/WidgetSection.xml b/app/code/Magento/Cms/Test/Mftf/Section/TinyMCESection/WidgetSection.xml new file mode 100644 index 0000000000000..1869a6544c3d3 --- /dev/null +++ b/app/code/Magento/Cms/Test/Mftf/Section/TinyMCESection/WidgetSection.xml @@ -0,0 +1,50 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + /** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +--> +<sections xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Page/etc/SectionObject.xsd"> + <section name="WidgetSection"> + <element name="InsertWidgetTitle" type="text" selector="//h1[contains(text(),'Insert Widget')]"/> + <element name="DisplayType" type="select" selector="select[name='parameters[display_type]']"/> + <element name="SelectCategoryTitle" type="text" selector="//h1[contains(text(),'Select Category')]"/> + <element name="SelectProductTitle" type="text" selector="//h1[contains(text(),'Select Product')]"/> + <element name="SelectPageTitle" type="text" selector="//h1[contains(text(),'Select Page')]"/> + <element name="SelectBlockTitle" type="text" selector="//h1[contains(text(),'Select Block')]"/> + <element name="InsertWidget" type="button" selector="#insert_button" timeout="30"/> + <element name="InsertWidgetBtnDisabled" type="button" selector="//button[@id='insert_button' and contains(@class,'disabled')]"/> + <element name="InsertWidgetBtnEnabled" type="button" selector="//button[@id='insert_button' and not(contains(@class,'disabled'))]"/> + <element name="CancelBtnEnabled" type="button" selector="//button[@id='reset' and not(contains(@class,'disabled'))]"/> + <element name="Close" type="button" selector="#close"/> + <element name="WidgetType" type="button" selector="#select_widget_type"/> + <element name="WidgetTemplate" type="button" selector="select[name='parameters[template]']"/> + <element name="BtnChooser" type="button" selector=".btn-chooser"/> + <element name="CMSPage" type="text" selector="//td[contains(text(),'Home page')]"/> + <element name="BlockPage" type="text" selector="//td[contains(text(),'{{var1}}')]" parameterized="true"/> + <element name="PreCreateCategory" type="text" selector=" //span[contains(text(),'{{var1}}')]" parameterized="true"/> + <element name="PreCreateProduct" type="text" selector="//td[contains(text(),'{{var1}}')]" parameterized="true"/> + <element name="NoOfProductToDisplay" type="input" selector="input[data-ui-id='wysiwyg-widget-options-fieldset-element-text-parameters-products-count']"/> + <element name="AddParam" type="button" selector=".rule-param-add"/> + <element name="ConditionsDropdown" type="select" selector="#conditions__1__new_child"/> + <element name="RuleParam" type="button" selector="//a[text()='...']"/> + <element name="RuleParam1" type="button" selector="(//span[@class='rule-param']//a)[{{var}}]" parameterized="true"/> + <element name="RuleParamSelect" type="select" selector="//ul[contains(@class,'rule-param-children')]/li[{{arg1}}]//*[contains(@class,'rule-param')][{{arg2}}]//select" parameterized="true"/> + <element name="RuleParamInput" type="input" selector="//ul[contains(@class,'rule-param-children')]/li[{{arg1}}]//*[contains(@class,'rule-param')][{{arg2}}]//input" parameterized="true"/> + <element name="RuleParamLabel" type="input" selector="//ul[contains(@class,'rule-param-children')]/li[{{arg1}}]//*[contains(@class,'rule-param')][{{arg2}}]//a" parameterized="true"/> + <element name="Chooser" type="button" selector="//img[@title='Open Chooser']"/> + <element name="PageSize" type="input" selector="input[name='parameters[page_size]']"/> + <element name="ProductAttribute" type="multiselect" selector="select[name='parameters[show_attributes][]']"/> + <element name="ButtonToShow" type="multiselect" selector="select[name='parameters[show_buttons][]']"/> + <!--Compare on Storefront--> + <element name="ProductName" type="text" selector=".product.name.product-item-name"/> + <element name="CompareBtn" type="button" selector=".action.tocompare"/> + <element name="ClearCompare" type="button" selector="#compare-clear-all"/> + <element name="AcceptClear" type="button" selector=".action-primary.action-accept"/> + <element name="ChooserName" type="input" selector="input[name='chooser_name']"/> + <element name="SelectPageButton" type="button" selector="//button[@title='Select Page...']"/> + <element name="SelectPageFilterInput" type="input" selector="input.admin__control-text[name='{{filterName}}']" parameterized="true"/> + </section> +</sections> diff --git a/app/code/Magento/Cms/Test/Mftf/Test/AdminAddImageToCMSPageTinyMCE3Test.xml b/app/code/Magento/Cms/Test/Mftf/Test/AdminAddImageToCMSPageTinyMCE3Test.xml index 69f9d24699db0..7c2aedceb9b7e 100644 --- a/app/code/Magento/Cms/Test/Mftf/Test/AdminAddImageToCMSPageTinyMCE3Test.xml +++ b/app/code/Magento/Cms/Test/Mftf/Test/AdminAddImageToCMSPageTinyMCE3Test.xml @@ -15,11 +15,11 @@ <group value="Cms"/> <title value="Verify that admin is able to upload image to a CMS Page with TinyMCE3 enabled"/> <description value="Verify that admin is able to upload image to CMS Page with TinyMCE3 enabled"/> - <severity value="MAJOR"/> + <severity value="BLOCKER"/> <testCaseId value="MAGETWO-95725"/> </annotations> <before> - <actionGroup ref="LoginAsAdmin" stepKey="loginAsAdmin"/> + <actionGroup ref="AdminLoginActionGroup" stepKey="loginAsAdmin"/> <magentoCLI command="config:set cms/wysiwyg/enabled enabled" stepKey="enableWYSIWYG"/> <!-- Choose TinyMCE3 as the default WYSIWYG editor--> <magentoCLI command="config:set cms/wysiwyg/editor Magento_Tinymce3/tinymce3Adapter" stepKey="enableTinyMCE3"/> @@ -28,7 +28,7 @@ <!-- Switch WYSIWYG editor to TinyMCE4--> <comment userInput="Reset editor as TinyMCE4" stepKey="chooseTinyMCE4AsEditor"/> <magentoCLI command="config:set cms/wysiwyg/editor mage/adminhtml/wysiwyg/tiny_mce/tinymce4Adapter" stepKey="enableTinyMCE4"/> - <actionGroup ref="logout" stepKey="logout"/> + <actionGroup ref="AdminLogoutActionGroup" stepKey="logout"/> </after> <amOnPage url="{{CmsNewPagePage.url}}" stepKey="navigateToPage2"/> <waitForPageLoad stepKey="wait5"/> diff --git a/app/code/Magento/Cms/Test/Mftf/Test/AdminAddImageToWYSIWYGBlockTest.xml b/app/code/Magento/Cms/Test/Mftf/Test/AdminAddImageToWYSIWYGBlockTest.xml index 64b775c6cbef9..162c9a60fd6b1 100644 --- a/app/code/Magento/Cms/Test/Mftf/Test/AdminAddImageToWYSIWYGBlockTest.xml +++ b/app/code/Magento/Cms/Test/Mftf/Test/AdminAddImageToWYSIWYGBlockTest.xml @@ -14,13 +14,13 @@ <group value="Cms"/> <title value="Admin should be able to add image to WYSIWYG content of Block"/> <description value="Admin should be able to add image to WYSIWYG content of Block"/> - <severity value="CRITICAL"/> + <severity value="BLOCKER"/> <testCaseId value="MAGETWO-84376"/> </annotations> <before> <createData entity="_defaultCmsPage" stepKey="createCMSPage" /> <createData entity="_defaultBlock" stepKey="createPreReqBlock" /> - <actionGroup ref="LoginActionGroup" stepKey="login"/> + <actionGroup ref="AdminLoginActionGroup" stepKey="login"/> <actionGroup ref="EnabledWYSIWYGActionGroup" stepKey="enableWYSIWYG"/> <actionGroup ref="SwitchToVersion4ActionGroup" stepKey="switchToTinyMCE4" /> </before> @@ -63,7 +63,7 @@ <deleteData createDataKey="createPreReqBlock" stepKey="deletePreReqBlock" /> <deleteData createDataKey="createCMSPage" stepKey="deletePreReqCMSPage" /> <actionGroup ref="DisabledWYSIWYGActionGroup" stepKey="disableWYSIWYG"/> - <actionGroup ref="logout" stepKey="logout"/> + <actionGroup ref="AdminLogoutActionGroup" stepKey="logout"/> </after> </test> </tests> diff --git a/app/code/Magento/Cms/Test/Mftf/Test/AdminAddImageToWYSIWYGCMSTest.xml b/app/code/Magento/Cms/Test/Mftf/Test/AdminAddImageToWYSIWYGCMSTest.xml index dd45c6768e67f..0476ecf99ad36 100644 --- a/app/code/Magento/Cms/Test/Mftf/Test/AdminAddImageToWYSIWYGCMSTest.xml +++ b/app/code/Magento/Cms/Test/Mftf/Test/AdminAddImageToWYSIWYGCMSTest.xml @@ -14,12 +14,12 @@ <group value="Cms"/> <title value="Admin should be able to add image to WYSIWYG content of CMS Page"/> <description value="Admin should be able to add image to WYSIWYG content of CMS Page"/> - <severity value="CRITICAL"/> + <severity value="BLOCKER"/> <testCaseId value="MAGETWO-85825"/> </annotations> <before> <createData entity="_defaultCmsPage" stepKey="createCMSPage" /> - <actionGroup ref="LoginActionGroup" stepKey="login"/> + <actionGroup ref="AdminLoginActionGroup" stepKey="login"/> <actionGroup ref="EnabledWYSIWYGActionGroup" stepKey="enableWYSIWYG"/> <actionGroup ref="SwitchToVersion4ActionGroup" stepKey="switchToTinyMCE4" /> </before> @@ -55,9 +55,10 @@ <seeElement selector="{{StorefrontCMSPageSection.mediaDescription}}" stepKey="assertMediaDescription"/> <seeElementInDOM selector="{{StorefrontCMSPageSection.imageSource(ImageUpload3.fileName)}}" stepKey="assertMediaSource"/> <after> + <actionGroup ref="DisabledWYSIWYGActionGroup" stepKey="disableWYSIWYGFirst"/> <deleteData createDataKey="createCMSPage" stepKey="deletePreReqCMSPage" /> <actionGroup ref="DisabledWYSIWYGActionGroup" stepKey="disableWYSIWYG"/> - <actionGroup ref="logout" stepKey="logout"/> + <actionGroup ref="AdminLogoutActionGroup" stepKey="logout"/> </after> </test> </tests> diff --git a/app/code/Magento/Cms/Test/Mftf/Test/AdminAddVariableToWYSIWYGBlockTest.xml b/app/code/Magento/Cms/Test/Mftf/Test/AdminAddVariableToWYSIWYGBlockTest.xml index cc7f59c7a9478..32bd75d373115 100644 --- a/app/code/Magento/Cms/Test/Mftf/Test/AdminAddVariableToWYSIWYGBlockTest.xml +++ b/app/code/Magento/Cms/Test/Mftf/Test/AdminAddVariableToWYSIWYGBlockTest.xml @@ -20,7 +20,7 @@ <before> <createData entity="_defaultCmsPage" stepKey="createCMSPage" /> <createData entity="_defaultBlock" stepKey="createPreReqBlock" /> - <actionGroup ref="LoginActionGroup" stepKey="loginGetFromGeneralFile"/> + <actionGroup ref="AdminLoginActionGroup" stepKey="loginGetFromGeneralFile"/> <actionGroup ref="EnabledWYSIWYGActionGroup" stepKey="enableWYSIWYG"/> <actionGroup ref="SwitchToVersion4ActionGroup" stepKey="switchToTinyMCE4" /> </before> @@ -143,7 +143,7 @@ <deleteData createDataKey="createPreReqBlock" stepKey="deletePreReqBlock" /> <deleteData createDataKey="createCMSPage" stepKey="deletePreReqCMSPage" /> <actionGroup ref="DisabledWYSIWYGActionGroup" stepKey="disableWYSIWYG"/> - <actionGroup ref="logout" stepKey="logout"/> + <actionGroup ref="AdminLogoutActionGroup" stepKey="logout"/> </after> </test> </tests> diff --git a/app/code/Magento/Cms/Test/Mftf/Test/AdminAddVariableToWYSIWYGCMSTest.xml b/app/code/Magento/Cms/Test/Mftf/Test/AdminAddVariableToWYSIWYGCMSTest.xml index 92164f34f5a83..55c01f3818a19 100644 --- a/app/code/Magento/Cms/Test/Mftf/Test/AdminAddVariableToWYSIWYGCMSTest.xml +++ b/app/code/Magento/Cms/Test/Mftf/Test/AdminAddVariableToWYSIWYGCMSTest.xml @@ -17,7 +17,7 @@ <testCaseId value="MAGETWO-83504"/> </annotations> <before> - <actionGroup ref="LoginActionGroup" stepKey="loginGetFromGeneralFile"/> + <actionGroup ref="AdminLoginActionGroup" stepKey="loginGetFromGeneralFile"/> <actionGroup ref="EnabledWYSIWYGActionGroup" stepKey="enableWYSIWYG"/> <actionGroup ref="SwitchToVersion4ActionGroup" stepKey="switchToTinyMCE4" /> </before> @@ -95,7 +95,7 @@ <dontSee userInput="{{customVariable.html}}" stepKey="dontSeeCustomVariableName" /> <after> <actionGroup ref="DisabledWYSIWYGActionGroup" stepKey="disableWYSIWYG"/> - <actionGroup ref="logout" stepKey="logout"/> + <actionGroup ref="AdminLogoutActionGroup" stepKey="logout"/> </after> </test> </tests> diff --git a/app/code/Magento/Cms/Test/Mftf/Test/AdminAddWidgetToWYSIWYGBlockTest.xml b/app/code/Magento/Cms/Test/Mftf/Test/AdminAddWidgetToWYSIWYGBlockTest.xml index 24440a6164e84..887fe88533f74 100644 --- a/app/code/Magento/Cms/Test/Mftf/Test/AdminAddWidgetToWYSIWYGBlockTest.xml +++ b/app/code/Magento/Cms/Test/Mftf/Test/AdminAddWidgetToWYSIWYGBlockTest.xml @@ -14,13 +14,13 @@ <group value="Cms"/> <title value="Admin should be able to add widget to WYSIWYG content of Block"/> <description value="Admin should be able to add widget to WYSIWYG content Block"/> - <severity value="CRITICAL"/> + <severity value="BLOCKER"/> <testCaseId value="MAGETWO-84654"/> </annotations> <before> <createData entity="_defaultCmsPage" stepKey="createCMSPage" /> <createData entity="_defaultBlock" stepKey="createPreReqBlock" /> - <actionGroup ref="LoginActionGroup" stepKey="login"/> + <actionGroup ref="AdminLoginActionGroup" stepKey="login"/> <actionGroup ref="EnabledWYSIWYGActionGroup" stepKey="enableWYSIWYG"/> <actionGroup ref="SwitchToVersion4ActionGroup" stepKey="switchToTinyMCE4" /> </before> @@ -81,7 +81,7 @@ <deleteData createDataKey="createCMSPage" stepKey="deletePreReqCMSPage" /> <deleteData createDataKey="createPreReqBlock" stepKey="deletePreReqBlock" /> <actionGroup ref="DisabledWYSIWYGActionGroup" stepKey="disableWYSIWYG"/> - <actionGroup ref="logout" stepKey="logout"/> + <actionGroup ref="AdminLogoutActionGroup" stepKey="logout"/> </after> </test> </tests> diff --git a/app/code/Magento/Cms/Test/Mftf/Test/AdminAddWidgetToWYSIWYGWithCMSPageLinkTypeTest.xml b/app/code/Magento/Cms/Test/Mftf/Test/AdminAddWidgetToWYSIWYGWithCMSPageLinkTypeTest.xml index 53ed63c89bfb6..450003db465a8 100644 --- a/app/code/Magento/Cms/Test/Mftf/Test/AdminAddWidgetToWYSIWYGWithCMSPageLinkTypeTest.xml +++ b/app/code/Magento/Cms/Test/Mftf/Test/AdminAddWidgetToWYSIWYGWithCMSPageLinkTypeTest.xml @@ -15,11 +15,11 @@ <group value="Cms"/> <title value="Admin should be able to create a CMS page with widget type: CMS page link"/> <description value="Admin should be able to create a CMS page with widget type: CMS page link"/> - <severity value="CRITICAL"/> + <severity value="BLOCKER"/> <testCaseId value="MAGETWO-83781"/> </annotations> <before> - <actionGroup ref="LoginActionGroup" stepKey="login"/> + <actionGroup ref="AdminLoginActionGroup" stepKey="login"/> <actionGroup ref="EnabledWYSIWYGActionGroup" stepKey="enableWYSIWYG"/> <actionGroup ref="SwitchToVersion4ActionGroup" stepKey="switchToTinyMCE4" /> </before> @@ -67,7 +67,7 @@ <see userInput="Home page" stepKey="seeHomepageWidget" /> <after> <actionGroup ref="DisabledWYSIWYGActionGroup" stepKey="disableWYSIWYG"/> - <actionGroup ref="logout" stepKey="logout"/> + <actionGroup ref="AdminLogoutActionGroup" stepKey="logout"/> </after> </test> </tests> diff --git a/app/code/Magento/Cms/Test/Mftf/Test/AdminAddWidgetToWYSIWYGWithCMSStaticBlockTypeTest.xml b/app/code/Magento/Cms/Test/Mftf/Test/AdminAddWidgetToWYSIWYGWithCMSStaticBlockTypeTest.xml index c199601231a13..633dd4dbc3388 100644 --- a/app/code/Magento/Cms/Test/Mftf/Test/AdminAddWidgetToWYSIWYGWithCMSStaticBlockTypeTest.xml +++ b/app/code/Magento/Cms/Test/Mftf/Test/AdminAddWidgetToWYSIWYGWithCMSStaticBlockTypeTest.xml @@ -15,12 +15,12 @@ <group value="Cms"/> <title value="Admin should be able to create a CMS page with widget type: CMS Static Block"/> <description value="Admin should be able to create a CMS page with widget type: CMS Static Block"/> - <severity value="CRITICAL"/> + <severity value="BLOCKER"/> <testCaseId value="MAGETWO-83787"/> </annotations> <before> <createData entity="_defaultBlock" stepKey="createPreReqBlock" /> - <actionGroup ref="LoginActionGroup" stepKey="login"/> + <actionGroup ref="AdminLoginActionGroup" stepKey="login"/> <actionGroup ref="EnabledWYSIWYGActionGroup" stepKey="enableWYSIWYG"/> <actionGroup ref="SwitchToVersion4ActionGroup" stepKey="switchToTinyMCE4" /> </before> @@ -69,7 +69,7 @@ <after> <deleteData createDataKey="createPreReqBlock" stepKey="deletePreReqBlock" /> <actionGroup ref="DisabledWYSIWYGActionGroup" stepKey="disableWYSIWYG"/> - <actionGroup ref="logout" stepKey="logout"/> + <actionGroup ref="AdminLogoutActionGroup" stepKey="logout"/> </after> </test> </tests> diff --git a/app/code/Magento/Cms/Test/Mftf/Test/AdminAddWidgetToWYSIWYGWithCatalogCategoryLinkTypeTest.xml b/app/code/Magento/Cms/Test/Mftf/Test/AdminAddWidgetToWYSIWYGWithCatalogCategoryLinkTypeTest.xml index 0237d3704f7c1..14bdc89cec311 100644 --- a/app/code/Magento/Cms/Test/Mftf/Test/AdminAddWidgetToWYSIWYGWithCatalogCategoryLinkTypeTest.xml +++ b/app/code/Magento/Cms/Test/Mftf/Test/AdminAddWidgetToWYSIWYGWithCatalogCategoryLinkTypeTest.xml @@ -14,12 +14,12 @@ <group value="Cms"/> <title value="Admin should be able to create a CMS page with widget type: Catalog category link"/> <description value="Admin should be able to create a CMS page with widget type: Catalog category link"/> - <severity value="CRITICAL"/> + <severity value="BLOCKER"/> <testCaseId value="MAGETWO-83611"/> </annotations> <before> <createData entity="_defaultCategory" stepKey="createPreReqCategory"/> - <actionGroup ref="LoginActionGroup" stepKey="login"/> + <actionGroup ref="AdminLoginActionGroup" stepKey="login"/> <actionGroup ref="EnabledWYSIWYGActionGroup" stepKey="enableWYSIWYG"/> <actionGroup ref="SwitchToVersion4ActionGroup" stepKey="switchToTinyMCE4" /> <actionGroup ref="ConfigAdminAccountSharingActionGroup" stepKey="allowAdminShareAccount"/> @@ -70,14 +70,15 @@ <see userInput="Hello CMS Page!" stepKey="seeContent2"/> <!--see widget on Storefront--> <grabAttributeFrom selector=".widget a" userInput="href" stepKey="dataHref" /> - <assertRegExp expected="|$$createPreReqCategory.name$$.html|i" - expectedType="string" actual="$dataHref" actualType="variable" - stepKey="seeProductLinkInCategory"/> + <assertRegExp stepKey="seeProductLinkInCategory"> + <actualResult type="variable">$dataHref</actualResult> + <expectedResult type="string">|$$createPreReqCategory.name$$.html|i</expectedResult> + </assertRegExp> <after> <deleteData createDataKey="createPreReqCategory" stepKey="deletePreReqCatalog" /> <actionGroup ref="DisabledWYSIWYGActionGroup" stepKey="disableWYSIWYG"/> <magentoCLI command="config:set catalog/seo/generate_category_product_rewrites 1" stepKey="enableGenerateUrlRewrite"/> - <actionGroup ref="logout" stepKey="logout"/> + <actionGroup ref="AdminLogoutActionGroup" stepKey="logout"/> </after> </test> </tests> diff --git a/app/code/Magento/Cms/Test/Mftf/Test/AdminAddWidgetToWYSIWYGWithCatalogProductLinkTypeTest.xml b/app/code/Magento/Cms/Test/Mftf/Test/AdminAddWidgetToWYSIWYGWithCatalogProductLinkTypeTest.xml index 4e7ecd48a6af4..2b788bc6ca0fd 100644 --- a/app/code/Magento/Cms/Test/Mftf/Test/AdminAddWidgetToWYSIWYGWithCatalogProductLinkTypeTest.xml +++ b/app/code/Magento/Cms/Test/Mftf/Test/AdminAddWidgetToWYSIWYGWithCatalogProductLinkTypeTest.xml @@ -15,7 +15,7 @@ <group value="Cms"/> <title value="Admin should be able to create a CMS page with widget type: Catalog product link"/> <description value="Admin should be able to create a CMS page with widget type: Catalog product link"/> - <severity value="CRITICAL"/> + <severity value="BLOCKER"/> <testCaseId value="MAGETWO-83788"/> </annotations> <before> @@ -23,7 +23,7 @@ <createData entity="_defaultProduct" stepKey="createPreReqProduct"> <requiredEntity createDataKey="createPreReqCategory"/> </createData> - <actionGroup ref="LoginActionGroup" stepKey="login"/> + <actionGroup ref="AdminLoginActionGroup" stepKey="login"/> <actionGroup ref="EnabledWYSIWYGActionGroup" stepKey="enableWYSIWYG"/> <actionGroup ref="SwitchToVersion4ActionGroup" stepKey="switchToTinyMCE4" /> </before> @@ -76,15 +76,16 @@ <waitForPageLoad stepKey="wait8" /> <!--see widget on Storefront--> <grabAttributeFrom selector=".widget a" userInput="href" stepKey="dataHref" /> - <assertRegExp expected="|$$createPreReqCategory.name$$/$$createPreReqProduct.name$$.html|i" - expectedType="string" actual="$dataHref" actualType="variable" - stepKey="seeProductLinkInCategory"/> + <assertRegExp stepKey="seeProductLinkInCategory"> + <actualResult type="variable">$dataHref</actualResult> + <expectedResult type="string">|$$createPreReqCategory.name$$/$$createPreReqProduct.name$$.html|i</expectedResult> + </assertRegExp> <after> <deleteData createDataKey="createPreReqCategory" stepKey="deletePreReqCatalog" /> <deleteData createDataKey="createPreReqProduct" stepKey="deletePreReqProduct" /> <actionGroup ref="DisabledWYSIWYGActionGroup" stepKey="disableWYSIWYG"/> <magentoCLI command="config:set catalog/seo/generate_category_product_rewrites 1" stepKey="enableGenerateUrlRewrite"/> - <actionGroup ref="logout" stepKey="logout"/> + <actionGroup ref="AdminLogoutActionGroup" stepKey="logout"/> </after> </test> </tests> diff --git a/app/code/Magento/Cms/Test/Mftf/Test/AdminAddWidgetToWYSIWYGWithCatalogProductListTypeTest.xml b/app/code/Magento/Cms/Test/Mftf/Test/AdminAddWidgetToWYSIWYGWithCatalogProductListTypeTest.xml index 2cd41eb9d9f7b..2124206466c2d 100644 --- a/app/code/Magento/Cms/Test/Mftf/Test/AdminAddWidgetToWYSIWYGWithCatalogProductListTypeTest.xml +++ b/app/code/Magento/Cms/Test/Mftf/Test/AdminAddWidgetToWYSIWYGWithCatalogProductListTypeTest.xml @@ -14,7 +14,7 @@ <group value="Cms"/> <title value="Admin should be able to create a CMS page with widget type: Catalog product list"/> <description value="Admin should be able to create a CMS page with widget type: Catalog product list"/> - <severity value="CRITICAL"/> + <severity value="BLOCKER"/> <testCaseId value="MAGETWO-67091"/> </annotations> <before> @@ -25,7 +25,7 @@ <createData entity="_defaultProduct" stepKey="createPreReqProduct2"> <requiredEntity createDataKey="createPreReqCategory"/> </createData> - <actionGroup ref="LoginActionGroup" stepKey="login"/> + <actionGroup ref="AdminLoginActionGroup" stepKey="login"/> <actionGroup ref="EnabledWYSIWYGActionGroup" stepKey="enableWYSIWYG"/> <actionGroup ref="SwitchToVersion4ActionGroup" stepKey="switchToTinyMCE4" /> </before> @@ -100,7 +100,7 @@ <deleteData createDataKey="createPreReqProduct1" stepKey="deletePreReqProduct1" /> <deleteData createDataKey="createPreReqProduct2" stepKey="deletePreReqProduct2" /> <actionGroup ref="DisabledWYSIWYGActionGroup" stepKey="disableWYSIWYG"/> - <actionGroup ref="logout" stepKey="logout"/> + <actionGroup ref="AdminLogoutActionGroup" stepKey="logout"/> </after> </test> </tests> diff --git a/app/code/Magento/Cms/Test/Mftf/Test/AdminAddWidgetToWYSIWYGWithRecentlyComparedProductsTypeTest.xml b/app/code/Magento/Cms/Test/Mftf/Test/AdminAddWidgetToWYSIWYGWithRecentlyComparedProductsTypeTest.xml index 803a3b692dbe2..85ae0380d4b43 100644 --- a/app/code/Magento/Cms/Test/Mftf/Test/AdminAddWidgetToWYSIWYGWithRecentlyComparedProductsTypeTest.xml +++ b/app/code/Magento/Cms/Test/Mftf/Test/AdminAddWidgetToWYSIWYGWithRecentlyComparedProductsTypeTest.xml @@ -15,7 +15,7 @@ <group value="Cms"/> <title value="Admin should be able to create a CMS page with widget type: Recently Compared Products"/> <description value="Admin should be able to create a CMS page with widget type: Recently Compared Products"/> - <severity value="CRITICAL"/> + <severity value="BLOCKER"/> <testCaseId value="MAGETWO-83792"/> </annotations> <!--Main test--> @@ -24,7 +24,7 @@ <createData entity="_defaultProduct" stepKey="createPreReqProduct"> <requiredEntity createDataKey="createPreReqCategory"/> </createData> - <actionGroup ref="LoginActionGroup" stepKey="login"/> + <actionGroup ref="AdminLoginActionGroup" stepKey="login"/> <actionGroup ref="EnabledWYSIWYGActionGroup" stepKey="enableWYSIWYG"/> <actionGroup ref="SwitchToVersion4ActionGroup" stepKey="switchToTinyMCE4" /> </before> @@ -83,7 +83,7 @@ <deleteData createDataKey="createPreReqCategory" stepKey="deletePreReqCatalog" /> <deleteData createDataKey="createPreReqProduct" stepKey="deletePreReqProduct" /> <actionGroup ref="DisabledWYSIWYGActionGroup" stepKey="disableWYSIWYG"/> - <actionGroup ref="logout" stepKey="logout"/> + <actionGroup ref="AdminLogoutActionGroup" stepKey="logout"/> </after> </test> </tests> diff --git a/app/code/Magento/Cms/Test/Mftf/Test/AdminAddWidgetToWYSIWYGWithRecentlyViewedProductsTypeTest.xml b/app/code/Magento/Cms/Test/Mftf/Test/AdminAddWidgetToWYSIWYGWithRecentlyViewedProductsTypeTest.xml index 2c3b060d3bc15..14182a4c33549 100644 --- a/app/code/Magento/Cms/Test/Mftf/Test/AdminAddWidgetToWYSIWYGWithRecentlyViewedProductsTypeTest.xml +++ b/app/code/Magento/Cms/Test/Mftf/Test/AdminAddWidgetToWYSIWYGWithRecentlyViewedProductsTypeTest.xml @@ -15,7 +15,7 @@ <group value="Cms"/> <title value="Admin should be able to create a CMS page with widget type: Recently Viewed Products"/> <description value="Admin should be able to create a CMS page with widget type: Recently Viewed Products"/> - <severity value="CRITICAL"/> + <severity value="BLOCKER"/> <testCaseId value="MAGETWO-83789"/> </annotations> <before> @@ -23,7 +23,7 @@ <createData entity="_defaultProduct" stepKey="createPreReqProduct"> <requiredEntity createDataKey="createPreReqCategory"/> </createData> - <actionGroup ref="LoginActionGroup" stepKey="login"/> + <actionGroup ref="AdminLoginActionGroup" stepKey="login"/> <actionGroup ref="EnabledWYSIWYGActionGroup" stepKey="enableWYSIWYG"/> <actionGroup ref="SwitchToVersion4ActionGroup" stepKey="switchToTinyMCE4" /> </before> @@ -73,7 +73,7 @@ <deleteData createDataKey="createPreReqCategory" stepKey="deletePreReqCatalog" /> <deleteData createDataKey="createPreReqProduct" stepKey="deletePreReqProduct" /> <actionGroup ref="DisabledWYSIWYGActionGroup" stepKey="disableWYSIWYG"/> - <actionGroup ref="logout" stepKey="logout"/> + <actionGroup ref="AdminLogoutActionGroup" stepKey="logout"/> </after> </test> </tests> diff --git a/app/code/Magento/Cms/Test/Mftf/Test/AdminCMSPageCreateDisabledPageTest.xml b/app/code/Magento/Cms/Test/Mftf/Test/AdminCMSPageCreateDisabledPageTest.xml new file mode 100644 index 0000000000000..446ef1b9f3b93 --- /dev/null +++ b/app/code/Magento/Cms/Test/Mftf/Test/AdminCMSPageCreateDisabledPageTest.xml @@ -0,0 +1,47 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + /** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +--> +<tests xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/testSchema.xsd"> + <test name="AdminCMSPageCreateDisabledPageTest"> + <annotations> + <features value="Cms"/> + <stories value="Create a CMS Page via the Admin"/> + <title value="Create disabled CMS Page via the Admin"/> + <description value="Admin should be able to create a CMS Page"/> + <severity value="CRITICAL"/> + <testCaseId value="MC-14677"/> + <group value="backend"/> + <group value="Cms"/> + <group value="mtf_migrated"/> + <group value="WYSIWYGDisabled"/> + </annotations> + <before> + <actionGroup ref="AdminLoginActionGroup" stepKey="loginAsAdmin"/> + </before> + <after> + <actionGroup ref="AdminLogoutActionGroup" stepKey="logout"/> + </after> + <!--Go to New CMS Page page--> + <actionGroup ref="AdminOpenCreateNewCMSPageActionGroup" stepKey="navigateToCreateNewPage"/> + <!--Set page disabled--> + <actionGroup ref="AdminDisableCMSPageActionGroup" stepKey="setCMSPageDisabled"/> + <!--Fill the CMS page form--> + <actionGroup ref="FillOutCMSPageContent" stepKey="fillBasicPageDataForDisabledPage"/> + <!--Verify successfully saved--> + <actionGroup ref="SaveCmsPageActionGroup" stepKey="saveDisabledPage"/> + <!--Check that page is disabled on frontend--> + <actionGroup ref="StorefrontGoToCMSPageActionGroup" stepKey="goToCMSPageOnStorefront"> + <argument name="identifier" value="{{_duplicatedCMSPage.identifier}}"/> + </actionGroup> + <actionGroup ref="AssertCMSPageNotFoundOnStorefrontActionGroup" stepKey="seeNotFoundError"/> + <!--Delete page--> + <actionGroup ref="DeletePageByUrlKeyActionGroup" stepKey="deleteDisabledPage"> + <argument name="UrlKey" value="{{_duplicatedCMSPage.identifier}}"/> + </actionGroup> + </test> +</tests> diff --git a/app/code/Magento/Cms/Test/Mftf/Test/AdminCMSPageCreatePageForDefaultStoreTest.xml b/app/code/Magento/Cms/Test/Mftf/Test/AdminCMSPageCreatePageForDefaultStoreTest.xml new file mode 100644 index 0000000000000..e3fe278c4449e --- /dev/null +++ b/app/code/Magento/Cms/Test/Mftf/Test/AdminCMSPageCreatePageForDefaultStoreTest.xml @@ -0,0 +1,53 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + /** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +--> +<tests xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/testSchema.xsd"> + <test name="AdminCMSPageCreatePageForDefaultStoreTest"> + <annotations> + <features value="Cms"/> + <stories value="Create a CMS Page via the Admin"/> + <title value="Create CMS Page via the Admin for default store"/> + <description value="Admin should be able to create a CMS Page"/> + <severity value="CRITICAL"/> + <testCaseId value="MC-14676"/> + <group value="backend"/> + <group value="Cms"/> + <group value="mtf_migrated"/> + <group value="WYSIWYGDisabled"/> + </annotations> + <before> + <actionGroup ref="AdminLoginActionGroup" stepKey="loginAsAdmin"/> + </before> + <after> + <actionGroup ref="AdminLogoutActionGroup" stepKey="logout"/> + </after> + <!--Go to New CMS Page page--> + <actionGroup ref="AdminOpenCreateNewCMSPageActionGroup" stepKey="navigateToCreateNewPage"/> + <!--Fill the CMS page form--> + <actionGroup ref="FillOutCMSPageContent" stepKey="fillBasicPageDataForPageWithDefaultStore"/> + <actionGroup ref="AdminSelectCMSPageStoreViewActionGroup" stepKey="selectCMSPageStoreView"> + <argument name="storeViewName" value="Default Store View"/> + </actionGroup> + <!--Verify successfully saved--> + <actionGroup ref="SaveCmsPageActionGroup" stepKey="savePageWithDefaultStore"/> + <!--Navigate to page in Admin--> + <actionGroup ref="NavigateToCreatedCMSPageActionGroup" stepKey="navigateToCMSPageWithDefaultStoreInAdmin"> + <argument name="CMSPage" value="_duplicatedCMSPage"/> + </actionGroup> + <!--Verify Page Data in Admin--> + <actionGroup ref="AssertCMSPageContentActionGroup" stepKey="verifyPageWithDefaultStoreDataInAdmin"/> + <!--Verify Store ID--> + <actionGroup ref="AssertCMSPageStoreIdActionGroup" stepKey="verifyStoreId"> + <argument name="storeId" value="1"/> + </actionGroup> + <!--Delete page--> + <actionGroup ref="DeletePageByUrlKeyActionGroup" stepKey="deletePageWithDefaultStore"> + <argument name="UrlKey" value="{{_duplicatedCMSPage.identifier}}"/> + </actionGroup> + </test> +</tests> diff --git a/app/code/Magento/Cms/Test/Mftf/Test/AdminCMSPageCreatePageInSingleStoreModeTest.xml b/app/code/Magento/Cms/Test/Mftf/Test/AdminCMSPageCreatePageInSingleStoreModeTest.xml new file mode 100644 index 0000000000000..4bb56dddec963 --- /dev/null +++ b/app/code/Magento/Cms/Test/Mftf/Test/AdminCMSPageCreatePageInSingleStoreModeTest.xml @@ -0,0 +1,57 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + /** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +--> +<tests xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/testSchema.xsd"> + <test name="AdminCMSPageCreatePageInSingleStoreModeTest"> + <annotations> + <features value="Cms"/> + <stories value="Create a CMS Page via the Admin"/> + <title value="Create CMS Page via the Admin in single store mode"/> + <description value="Admin should be able to create a CMS Page"/> + <severity value="CRITICAL"/> + <testCaseId value="MC-14679"/> + <group value="backend"/> + <group value="Cms"/> + <group value="mtf_migrated"/> + <group value="WYSIWYGDisabled"/> + </annotations> + <before> + <magentoCLI command="config:set {{StorefrontSingleStoreModeEnabledConfigData.path}} {{StorefrontSingleStoreModeEnabledConfigData.value}}" stepKey="enableSingleStoreMode" /> + <actionGroup ref="AdminLoginActionGroup" stepKey="loginAsAdmin"/> + </before> + <after> + <magentoCLI command="config:set {{StorefrontSingleStoreModeDisabledConfigData.path}} {{StorefrontSingleStoreModeDisabledConfigData.value}}" stepKey="disableSingleStoreMode" /> + <actionGroup ref="AdminLogoutActionGroup" stepKey="logout"/> + </after> + <!--Go to New CMS Page page--> + <actionGroup ref="AdminOpenCreateNewCMSPageActionGroup" stepKey="navigateToCreateNewPage"/> + <!--Fill the CMS page form--> + <actionGroup ref="FillOutCMSPageContent" stepKey="fillBasicPageDataForPageWithDefaultStore"/> + <!--Verify successfully saved--> + <actionGroup ref="SaveCmsPageActionGroup" stepKey="savePageInSingleStoreMode"/> + <!--verify page on frontend--> + <actionGroup ref="StorefrontGoToCMSPageActionGroup" stepKey="navigateToPageOnStoreFront"> + <argument name="identifier" value="{{_duplicatedCMSPage.identifier}}"/> + </actionGroup> + <actionGroup ref="AssertStoreFrontCMSPageActionGroup" stepKey="verifyPageDataOnFrontend"> + <argument name="cmsTitle" value="{{_duplicatedCMSPage.title}}"/> + <argument name="cmsContent" value="{{_duplicatedCMSPage.content}}"/> + <argument name="cmsContentHeading" value="{{_duplicatedCMSPage.content_heading}}"/> + </actionGroup> + <!--Navigate to page in Admin--> + <actionGroup ref="NavigateToCreatedCMSPageActionGroup" stepKey="navigateToCMSPageInAdminInSingleStoreMode"> + <argument name="CMSPage" value="_duplicatedCMSPage"/> + </actionGroup> + <!--Verify Page Data in Admin--> + <actionGroup ref="AssertCMSPageContentActionGroup" stepKey="verifyPageDataInAdminInSingleStoreMode"/> + <!--Delete page--> + <actionGroup ref="DeletePageByUrlKeyActionGroup" stepKey="deletePageInSingleStoreMode"> + <argument name="UrlKey" value="{{_duplicatedCMSPage.identifier}}"/> + </actionGroup> + </test> +</tests> diff --git a/app/code/Magento/Cms/Test/Mftf/Test/AdminCMSPageCreatePageTest.xml b/app/code/Magento/Cms/Test/Mftf/Test/AdminCMSPageCreatePageTest.xml new file mode 100644 index 0000000000000..596503cd8faab --- /dev/null +++ b/app/code/Magento/Cms/Test/Mftf/Test/AdminCMSPageCreatePageTest.xml @@ -0,0 +1,54 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + /** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +--> +<tests xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/testSchema.xsd"> + <test name="AdminCMSPageCreatePageTest"> + <annotations> + <features value="Cms"/> + <stories value="Create a CMS Page via the Admin"/> + <title value="Create CMS Page via the Admin"/> + <description value="Admin should be able to create a CMS Page"/> + <severity value="CRITICAL"/> + <testCaseId value="MC-14675"/> + <group value="backend"/> + <group value="Cms"/> + <group value="mtf_migrated"/> + <group value="WYSIWYGDisabled"/> + </annotations> + <before> + <actionGroup ref="AdminLoginActionGroup" stepKey="loginAsAdmin"/> + </before> + <after> + <actionGroup ref="AdminLogoutActionGroup" stepKey="logout"/> + </after> + <!--Go to New CMS Page page--> + <actionGroup ref="AdminOpenCreateNewCMSPageActionGroup" stepKey="navigateToCreateNewPage"/> + <actionGroup ref="FillOutCMSPageContent" stepKey="fillBasicPageData"/> + <!--verify successfully saved--> + <actionGroup ref="SaveCmsPageActionGroup" stepKey="saveNewPage"/> + <!--verify page on frontend--> + <actionGroup ref="StorefrontGoToCMSPageActionGroup" stepKey="navigateToPageOnStoreFront"> + <argument name="identifier" value="{{_duplicatedCMSPage.identifier}}"/> + </actionGroup> + <actionGroup ref="AssertStoreFrontCMSPageActionGroup" stepKey="verifyPageDataOnFrontend"> + <argument name="cmsTitle" value="{{_duplicatedCMSPage.title}}"/> + <argument name="cmsContent" value="{{_duplicatedCMSPage.content}}"/> + <argument name="cmsContentHeading" value="{{_duplicatedCMSPage.content_heading}}"/> + </actionGroup> + <!--verify page in grid--> + <actionGroup ref="AdminOpenCMSPagesGridActionGroup" stepKey="openCMSPagesGridActionGroup"/> + <actionGroup ref="ClearFiltersAdminDataGridActionGroup" stepKey="clearGridFilters"/> + <actionGroup ref="SortByIdDescendingActionGroup" stepKey="sortGridByIdDescending"/> + <actionGroup ref="AdminSelectCMSPageFromGridActionGroup" stepKey="verifyPageInGrid"> + <argument name="identifier" value="_duplicatedCMSPage.identifier"/> + </actionGroup> + <actionGroup ref="DeletePageByUrlKeyActionGroup" stepKey="deletePage"> + <argument name="UrlKey" value="{{_duplicatedCMSPage.identifier}}"/> + </actionGroup> + </test> +</tests> diff --git a/app/code/Magento/Cms/Test/Mftf/Test/AdminCMSPageCreatePageWithBlockTest.xml b/app/code/Magento/Cms/Test/Mftf/Test/AdminCMSPageCreatePageWithBlockTest.xml new file mode 100644 index 0000000000000..aac56e16a53c4 --- /dev/null +++ b/app/code/Magento/Cms/Test/Mftf/Test/AdminCMSPageCreatePageWithBlockTest.xml @@ -0,0 +1,54 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + /** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +--> +<tests xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/testSchema.xsd"> + <test name="AdminCMSPageCreatePageWithBlockTest"> + <annotations> + <features value="Cms"/> + <stories value="Create a CMS Page via the Admin"/> + <title value="Create CMS Page that contains block content via the Admin"/> + <description value="Admin should be able to create a CMS Page"/> + <severity value="CRITICAL"/> + <testCaseId value="MC-14678"/> + <group value="backend"/> + <group value="Cms"/> + <group value="mtf_migrated"/> + <group value="WYSIWYGDisabled"/> + </annotations> + <before> + <actionGroup ref="AdminLoginActionGroup" stepKey="loginAsAdmin"/> + </before> + <after> + <actionGroup ref="AdminLogoutActionGroup" stepKey="logout"/> + </after> + <!--Go to New CMS Page page--> + <actionGroup ref="AdminOpenCreateNewCMSPageActionGroup" stepKey="navigateToCreateNewPage"/> + <actionGroup ref="FillOutCMSPageContent" stepKey="fillBasicPageData"/> + <actionGroup ref="AdminFillCMSPageContentFieldActionGroup" stepKey="fillContentField"> + <argument name="content" value="{{block class='Magento\Framework\View\Element\Text' text='bla bla bla' cache_key='BACKEND_ACL_RESOURCES' cache_lifetime=999}}"/> + </actionGroup> + <actionGroup ref="AdminSelectCMSPageStoreViewActionGroup" stepKey="selectCMSPageStoreViewForPageWithBlock"> + <argument name="storeViewName" value="Default Store View"/> + </actionGroup> + <!--Verify successfully saved--> + <actionGroup ref="SaveCmsPageActionGroup" stepKey="savePageWithBlock"/> + <!--verify page on frontend--> + <actionGroup ref="StorefrontGoToCMSPageActionGroup" stepKey="navigateToPageOnStoreFront"> + <argument name="identifier" value="{{_duplicatedCMSPage.identifier}}"/> + </actionGroup> + <actionGroup ref="AssertStoreFrontCMSPageActionGroup" stepKey="verifyPageWithBlockDataOnFrontend"> + <argument name="cmsTitle" value="{{_duplicatedCMSPage.title}}"/> + <argument name="cmsContent" value="bla bla bla"/> + <argument name="cmsContentHeading" value="{{_duplicatedCMSPage.content_heading}}"/> + </actionGroup> + <!--Delete page with block--> + <actionGroup ref="DeletePageByUrlKeyActionGroup" stepKey="deletePageWithBlock"> + <argument name="UrlKey" value="{{_duplicatedCMSPage.identifier}}"/> + </actionGroup> + </test> +</tests> diff --git a/app/code/Magento/Cms/Test/Mftf/Test/AdminCheckCreateFolderEscapeAndEnterHandlesForWYSIWYGBlockTest.xml b/app/code/Magento/Cms/Test/Mftf/Test/AdminCheckCreateFolderEscapeAndEnterHandlesForWYSIWYGBlockTest.xml new file mode 100644 index 0000000000000..aa29f33f90664 --- /dev/null +++ b/app/code/Magento/Cms/Test/Mftf/Test/AdminCheckCreateFolderEscapeAndEnterHandlesForWYSIWYGBlockTest.xml @@ -0,0 +1,53 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + /** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +--> +<tests xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/testSchema.xsd"> + <test name="AdminCheckCreateFolderEscapeAndEnterHandlesForWYSIWYGBlockTest"> + <annotations> + <features value="Cms"/> + <stories value="WYSIWYG toolbar configuration with Magento Media Gallery"/> + <group value="Cms"/> + <title value="Admin should be able to cancel and close 'create folder' modal window using ESC key and + to add image to new folder (using enter key) for WYSIWYG content of Block"/> + <description value="Admin should be able to cancel and close 'create folder' modal window using ESC key and + to add image to new folder (using enter key) for WYSIWYG content of Block"/> + </annotations> + + <before> + <createData entity="_defaultBlock" stepKey="createPreReqBlock" /> + <actionGroup ref="AdminLoginActionGroup" stepKey="login"/> + <actionGroup ref="EnabledWYSIWYGActionGroup" stepKey="enableWYSIWYG"/> + <actionGroup ref="SwitchToVersion4ActionGroup" stepKey="switchToTinyMCE4" /> + </before> + + <after> + <deleteData createDataKey="createPreReqBlock" stepKey="deletePreReqBlock" /> + <actionGroup ref="DisabledWYSIWYGActionGroup" stepKey="disableWYSIWYG"/> + <actionGroup ref="AdminLogoutActionGroup" stepKey="logout"/> + </after> + + <actionGroup ref="NavigateToCreatedCMSBlockPageActionGroup" stepKey="navigateToCreatedCMSBlockPage"> + <argument name="CMSBlockPage" value="$$createPreReqBlock$$"/> + </actionGroup> + <actionGroup ref="ClickInsertEditImageTinyMCEButtonActionGroup" stepKey="clickInsertImageIcon"/> + <actionGroup ref="ClickBrowseBtnOnUploadPopupActionGroup" stepKey="clickBrowserBtn"/> + <actionGroup ref="VerifyMediaGalleryStorageActionsActionGroup" stepKey="VerifyMediaGalleryStorageBtn"/> + + <actionGroup ref="CreateImageFolderByEnterKeyActionGroup" stepKey="CreateImageFolderByEnterKeyPress"> + <argument name="ImageFolder" value="ImageFolder"/> + </actionGroup> + + <actionGroup ref="DeleteFolderActionGroup" stepKey="DeleteCreatedFolder"> + <argument name="ImageFolder" value="ImageFolder"/> + </actionGroup> + + <actionGroup ref="PressEscImageFolderActionGroup" stepKey="CancelImageFolderCreation"> + <argument name="ImageFolder" value="ImageFolder"/> + </actionGroup> + </test> +</tests> diff --git a/app/code/Magento/Cms/Test/Mftf/Test/AdminCmsPageMassActionTest.xml b/app/code/Magento/Cms/Test/Mftf/Test/AdminCmsPageMassActionTest.xml index 7cc0719dcbeb2..9a02104d8d6ef 100644 --- a/app/code/Magento/Cms/Test/Mftf/Test/AdminCmsPageMassActionTest.xml +++ b/app/code/Magento/Cms/Test/Mftf/Test/AdminCmsPageMassActionTest.xml @@ -10,6 +10,7 @@ <test name="AdminCmsPageMassActionTest"> <annotations> <features value="CmsPage"/> + <stories value="Create CMS Page"/> <title value="Create two CMS Pages and perform mass disable action"/> <description value="Admin should be able to perform mass actions to CMS pages"/> <stories value="Admin Grid Mass Action" /> @@ -20,14 +21,14 @@ <group value="mtf_migrated"/> </annotations> <before> - <actionGroup ref="LoginAsAdmin" stepKey="loginAsAdmin"/> + <actionGroup ref="AdminLoginActionGroup" stepKey="loginAsAdmin"/> <createData entity="_defaultCmsPage" stepKey="firstCMSPage" /> <createData entity="_duplicatedCMSPage" stepKey="secondCMSPage" /> </before> <after> <deleteData createDataKey="firstCMSPage" stepKey="deleteFirstCMSPage" /> <deleteData createDataKey="secondCMSPage" stepKey="deleteSecondCMSPage" /> - <actionGroup ref="logout" stepKey="logout"/> + <actionGroup ref="AdminLogoutActionGroup" stepKey="logout"/> </after> <!--Go to Grid page--> diff --git a/app/code/Magento/Cms/Test/Mftf/Test/AdminCmsPageUpdateAndDisableTest.xml b/app/code/Magento/Cms/Test/Mftf/Test/AdminCmsPageUpdateAndDisableTest.xml new file mode 100644 index 0000000000000..24d61311e8951 --- /dev/null +++ b/app/code/Magento/Cms/Test/Mftf/Test/AdminCmsPageUpdateAndDisableTest.xml @@ -0,0 +1,46 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + /** + *CreateNewPage Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +--> + +<tests xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/testSchema.xsd"> + <test name="AdminCmsPageUpdateAndDisableTest"> + <annotations> + <features value="Cms"/> + <testCaseId value="MC-14673" /> + <title value="Update CMS Page via the Admin, disable"/> + <description value="Admin should be able to update a CMS Page"/> + <group value="backend"/> + <group value="cMSContent"/> + <group value="mtf_migrated"/> + <group value="WYSIWYGDisabled"/> + </annotations> + <before> + <actionGroup ref="AdminLoginActionGroup" stepKey="loginAsAdmin"/> + <createData entity="_defaultCmsPage" stepKey="existingCMSPage" /> + </before> + <after> + <deleteData createDataKey="existingCMSPage" stepKey="deleteCMSPage" /> + <actionGroup ref="AdminLogoutActionGroup" stepKey="logout"/> + </after> + <!--Navigate to Page in Admin--> + <actionGroup ref="NavigateToCreatedCMSPageActionGroup" stepKey="navigateToCreatedCMSPage"> + <argument name="CMSPage" value="$$existingCMSPage$$"/> + </actionGroup> + <!--Deactivate page--> + <actionGroup ref="AdminDisableCMSPageActionGroup" stepKey="setPageDisabled"/> + <!--Fill data using _duplicatedCMSPage--> + <actionGroup ref="FillOutCMSPageContent" stepKey="fillNewData"/> + <!--Save page--> + <actionGroup ref="SaveCmsPageActionGroup" stepKey="saveDisabledPage"/> + <!--Check that page is not found on storefront--> + <actionGroup ref="StorefrontGoToCMSPageActionGroup" stepKey="goToCMSPageOnStorefront"> + <argument name="identifier" value="{{_duplicatedCMSPage.identifier}}"/> + </actionGroup> + <actionGroup ref="AssertCMSPageNotFoundOnStorefrontActionGroup" stepKey="seeNotFoundError"/> + </test> +</tests> diff --git a/app/code/Magento/Cms/Test/Mftf/Test/AdminCmsPageUpdateTest.xml b/app/code/Magento/Cms/Test/Mftf/Test/AdminCmsPageUpdateTest.xml new file mode 100644 index 0000000000000..6e05828726e7d --- /dev/null +++ b/app/code/Magento/Cms/Test/Mftf/Test/AdminCmsPageUpdateTest.xml @@ -0,0 +1,52 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + /** + *CreateNewPage Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +--> + +<tests xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/testSchema.xsd"> + <test name="AdminCmsPageUpdateTest"> + <annotations> + <features value="Cms"/> + <title value="Update CMS Page via the Admin"/> + <description value="Admin should be able to update a CMS Page"/> + <testCaseId value="MC-14674"/> + <group value="backend"/> + <group value="cMSContent"/> + <group value="mtf_migrated"/> + <group value="WYSIWYGDisabled"/> + </annotations> + <before> + <actionGroup ref="AdminLoginActionGroup" stepKey="loginAsAdmin"/> + <createData entity="_defaultCmsPage" stepKey="existingCMSPage" /> + </before> + <after> + <deleteData createDataKey="existingCMSPage" stepKey="deleteCMSPage" /> + <actionGroup ref="AdminLogoutActionGroup" stepKey="logout"/> + </after> + <!--Navigate to Page in Admin--> + <actionGroup ref="NavigateToCreatedCMSPageActionGroup" stepKey="navigateToCreatedCMSPage"> + <argument name="CMSPage" value="$$existingCMSPage$$"/> + </actionGroup> + <!--Fill data using _duplicatedCMSPage--> + <actionGroup ref="FillOutCMSPageContent" stepKey="fillNewData"/> + <actionGroup ref="SaveCmsPageActionGroup" stepKey="saveActivatedPage"/> + <!--Verify data in admin--> + <actionGroup ref="NavigateToCreatedCMSPageActionGroup" stepKey="navigateToUpdatedCMSPage"> + <argument name="CMSPage" value="_duplicatedCMSPage"/> + </actionGroup> + <actionGroup ref="AssertCMSPageContentActionGroup" stepKey="verifyPageDataInAdmin"/> + <!--Verify data on frontend--> + <actionGroup ref="StorefrontGoToCMSPageActionGroup" stepKey="navigateToPageOnStorefront"> + <argument name="identifier" value="{{_duplicatedCMSPage.identifier}}"/> + </actionGroup> + <actionGroup ref="AssertStoreFrontCMSPageActionGroup" stepKey="verifyPageDataOnFrontend"> + <argument name="cmsTitle" value="{{_duplicatedCMSPage.title}}"/> + <argument name="cmsContent" value="{{_duplicatedCMSPage.content}}"/> + <argument name="cmsContentHeading" value="{{_duplicatedCMSPage.content_heading}}"/> + </actionGroup> + </test> +</tests> diff --git a/app/code/Magento/Cms/Test/Mftf/Test/AdminConfigDefaultCMSPageLayoutFromConfigurationSettingTest.xml b/app/code/Magento/Cms/Test/Mftf/Test/AdminConfigDefaultCMSPageLayoutFromConfigurationSettingTest.xml new file mode 100644 index 0000000000000..0eac31c891e64 --- /dev/null +++ b/app/code/Magento/Cms/Test/Mftf/Test/AdminConfigDefaultCMSPageLayoutFromConfigurationSettingTest.xml @@ -0,0 +1,44 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + /** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +--> + +<tests xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/testSchema.xsd"> + <test name="AdminConfigDefaultCMSPageLayoutFromConfigurationSettingTest"> + <annotations> + <features value="Cms"/> + <stories value="Default layout configuration MAGETWO-88793"/> + <title value="Admin should be able to configure the default layout for CMS Page from System Configuration"/> + <description value="Admin should be able to configure the default layout for CMS Page from System Configuration"/> + <severity value="CRITICAL"/> + <testCaseId value="MAGETWO-89025"/> + <group value="Cms"/> + </annotations> + <before> + <actionGroup ref="AdminLoginActionGroup" stepKey="loginAsAdmin"/> + </before> + <after> + <actionGroup ref="RestoreLayoutSetting" stepKey="sampleActionGroup"/> + <actionGroup ref="AdminLogoutActionGroup" stepKey="logout"/> + </after> + <amOnPage url="{{WebConfigurationPage.url}}" stepKey="navigateToWebConfigurationPage"/> + <waitForPageLoad stepKey="waitForPageLoad"/> + <conditionalClick stepKey="expandDefaultLayouts" selector="{{WebSection.DefaultLayoutsTab}}" dependentSelector="{{WebSection.CheckIfTabExpand}}" visible="true" /> + <waitForElementVisible selector="{{DefaultLayoutsSection.pageLayout}}" stepKey="DefaultProductLayout" /> + <seeOptionIsSelected selector="{{DefaultLayoutsSection.pageLayout}}" userInput="1 column" stepKey="seeOneColumnSelected" /> + <seeOptionIsSelected selector="{{DefaultLayoutsSection.productLayout}}" userInput="No layout updates" stepKey="seeNoLayoutUpdatesSelected1" /> + <seeOptionIsSelected selector="{{DefaultLayoutsSection.categoryLayout}}" userInput="No layout updates" stepKey="seeNoLayoutUpdatesSelected2" /> + <selectOption selector="{{DefaultLayoutsSection.pageLayout}}" userInput="2 columns with right bar" stepKey="selectColumnsWithRightBar"/> + <click selector="{{ContentManagementSection.Save}}" stepKey="clickSaveConfig" /> + <amOnPage url="{{CmsNewPagePage.url}}" stepKey="amOnPagePagesGrid"/> + <waitForPageLoad stepKey="waitForPageLoad1"/> + <waitForLoadingMaskToDisappear stepKey="wait2" /> + <click selector="{{CmsDesignSection.DesignTab}}" stepKey="clickOnDesignTab"/> + <waitForElementVisible selector="{{CmsDesignSection.LayoutDropdown}}" stepKey="waitForLayoutDropDown" /> + <seeOptionIsSelected selector="{{CmsDesignSection.LayoutDropdown}}" userInput="2 columns with right bar" stepKey="seeColumnsWithRightBar" /> + </test> +</tests> diff --git a/app/code/Magento/Cms/Test/Mftf/Test/AdminContentBlocksNavigateMenuTest.xml b/app/code/Magento/Cms/Test/Mftf/Test/AdminContentBlocksNavigateMenuTest.xml index 19f501d6aa209..7d3946ea86c92 100644 --- a/app/code/Magento/Cms/Test/Mftf/Test/AdminContentBlocksNavigateMenuTest.xml +++ b/app/code/Magento/Cms/Test/Mftf/Test/AdminContentBlocksNavigateMenuTest.xml @@ -20,10 +20,10 @@ <group value="mtf_migrated"/> </annotations> <before> - <actionGroup ref="LoginAsAdmin" stepKey="LoginAsAdmin"/> + <actionGroup ref="AdminLoginActionGroup" stepKey="LoginAsAdmin"/> </before> <after> - <actionGroup ref="logout" stepKey="logout"/> + <actionGroup ref="AdminLogoutActionGroup" stepKey="logout"/> </after> <actionGroup ref="AdminNavigateMenuActionGroup" stepKey="navigateToContentBlocksPage"> <argument name="menuUiId" value="{{AdminMenuContent.dataUiId}}"/> diff --git a/app/code/Magento/Cms/Test/Mftf/Test/AdminContentPagesNavigateMenuTest.xml b/app/code/Magento/Cms/Test/Mftf/Test/AdminContentPagesNavigateMenuTest.xml index 323a1de7b9a4e..bd24e4b082edc 100644 --- a/app/code/Magento/Cms/Test/Mftf/Test/AdminContentPagesNavigateMenuTest.xml +++ b/app/code/Magento/Cms/Test/Mftf/Test/AdminContentPagesNavigateMenuTest.xml @@ -20,10 +20,10 @@ <group value="mtf_migrated"/> </annotations> <before> - <actionGroup ref="LoginAsAdmin" stepKey="LoginAsAdmin"/> + <actionGroup ref="AdminLoginActionGroup" stepKey="LoginAsAdmin"/> </before> <after> - <actionGroup ref="logout" stepKey="logout"/> + <actionGroup ref="AdminLogoutActionGroup" stepKey="logout"/> </after> <actionGroup ref="AdminNavigateMenuActionGroup" stepKey="navigateToContentPagesPage"> <argument name="menuUiId" value="{{AdminMenuContent.dataUiId}}"/> diff --git a/app/code/Magento/Cms/Test/Mftf/Test/AdminCreateCmsBlockTest.xml b/app/code/Magento/Cms/Test/Mftf/Test/AdminCreateCmsBlockTest.xml index ef4a7575c35d3..eb13bb71b5f6b 100644 --- a/app/code/Magento/Cms/Test/Mftf/Test/AdminCreateCmsBlockTest.xml +++ b/app/code/Magento/Cms/Test/Mftf/Test/AdminCreateCmsBlockTest.xml @@ -17,36 +17,29 @@ <severity value="CRITICAL"/> <testCaseId value="MAGETWO-89185"/> <group value="Cms"/> + <group value="WYSIWYGDisabled"/> </annotations> <before> - <actionGroup ref="LoginActionGroup" stepKey="loginGetFromGeneralFile"/> - <actionGroup ref="DisabledWYSIWYGActionGroup" stepKey="disableWYSIWYG"/> + <actionGroup ref="AdminLoginActionGroup" stepKey="loginGetFromGeneralFile"/> </before> <after> <actionGroup ref="deleteBlock" stepKey="deleteCreatedBlock"> <argument name="Block" value="_defaultBlock"/> </actionGroup> - <actionGroup ref="logout" stepKey="logout"/> + <actionGroup ref="AdminLogoutActionGroup" stepKey="logout"/> </after> - <amOnPage url="{{CmsNewBlock.url}}" stepKey="amOnBlocksCreationForm"/> - <waitForPageLoad stepKey="waitForPageLoad1"/> - <!--Verify Save&Duplicate button and Save&Close button--> - <click selector="{{BlockNewPagePageActionsSection.expandSplitButton}}" stepKey="expandSplitBtn1" /> - <see selector="{{BlockNewPagePageActionsSection.saveAndDuplicate}}" userInput="Save & Duplicate" stepKey="seeSaveAndDuplicate"/> - <see selector="{{BlockNewPagePageActionsSection.saveAndClose}}" userInput="Save & Close" stepKey="seeSaveAndClose"/> + <!-- Navigate to create cms block page and verify save split button --> + <actionGroup ref="VerifyCmsBlockSaveSplitButtonActionGroup" stepKey="verifyCmsBlockSaveButton" /> <!--Create new CMS Block page--> <actionGroup ref="FillOutBlockContent" stepKey="FillOutBlockContent"/> - <click selector="{{BlockNewPagePageActionsSection.expandSplitButton}}" stepKey="expandSplitBtn2" /> - <click selector="{{BlockNewPagePageActionsSection.saveAndDuplicate}}" stepKey="clickSaveAndDuplicate" /> - <waitForPageLoad stepKey="waitForPageLoad3"/> - <see userInput="You saved the block." stepKey="seeSavedBlockMsgOnForm"/> - <see userInput="You duplicated the block." stepKey="seeDuplicatedBlockMsg"/> + <!-- Click save and duplicate action --> + <actionGroup ref="SaveAndDuplicateCMSBlockWithSplitButtonActionGroup" stepKey="clickSaveAndDuplicateButton" /> + <!--Verify duplicated CMS Block Page--> <seeElement selector="{{BlockNewPageBasicFieldsSection.isActive('0')}}" stepKey="seeBlockNotEnable" /> <actionGroup ref="AssertBlockContent" stepKey="assertContent"/> - <click selector="{{BlockNewPagePageActionsSection.expandSplitButton}}" stepKey="expandSplitBtn3" /> - <click selector="{{BlockNewPagePageActionsSection.saveAndClose}}" stepKey="clickSaveAndClose"/> - <see userInput="You saved the block." stepKey="seeSavedBlockMsgOnGrid"/> + <!-- Click save and close button --> + <actionGroup ref="SaveAndCloseCMSBlockWithSplitButtonActionGroup" stepKey="saveAndCloseAction" /> <seeElement selector="div[data-role='grid-wrapper']" stepKey="seeGridPage" /> </test> </tests> diff --git a/app/code/Magento/Cms/Test/Mftf/Test/AdminCreateCmsBlockWithMarginalSpaceTest.xml b/app/code/Magento/Cms/Test/Mftf/Test/AdminCreateCmsBlockWithMarginalSpaceTest.xml new file mode 100644 index 0000000000000..f2c84dea13f97 --- /dev/null +++ b/app/code/Magento/Cms/Test/Mftf/Test/AdminCreateCmsBlockWithMarginalSpaceTest.xml @@ -0,0 +1,42 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + /** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +--> + +<tests xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/testSchema.xsd"> + <test name="AdminCreateCmsBlockWithMarginalSpaceTest"> + <annotations> + <features value="Cms"/> + <stories value="CMS Block Identifier with marginal space"/> + <title value="Admin can not able create a CMS block with marginal space in identifier field"/> + <description value="Admin can not able create a CMS block with marginal space in identifier field"/> + <severity value="CRITICAL"/> + <group value="Cms"/> + <group value="WYSIWYGDisabled"/> + </annotations> + <before> + <actionGroup ref="AdminLoginActionGroup" stepKey="loginGetFromGeneralFile"/> + </before> + + <amOnPage url="{{CmsNewBlock.url}}" stepKey="amOnBlocksCreationForm"/> + <waitForPageLoad stepKey="waitForPageLoad1"/> + <!--Verify Save&Duplicate button and Save&Close button--> + <click selector="{{BlockNewPagePageActionsSection.expandSplitButton}}" stepKey="expandSplitBtn1" /> + <see selector="{{BlockNewPagePageActionsSection.saveAndDuplicate}}" userInput="Save & Duplicate" stepKey="seeSaveAndDuplicate"/> + <see selector="{{BlockNewPagePageActionsSection.saveAndClose}}" userInput="Save & Close" stepKey="seeSaveAndClose"/> + <!--Create new CMS Block page with marginal space in identifier field--> + <actionGroup ref="AdminFillCmsBlockFormActionGroup" stepKey="FillOutBlockContent"> + <argument name="cmsBlockDataTitle" value="Default Block" /> + <argument name="cmsBlockDataIdentifier" value=" block " /> + <argument name="cmsBlockDataContent" value="Here is a block test. Yeah!" /> + </actionGroup> + <click selector="{{BlockNewPagePageActionsSection.expandSplitButton}}" stepKey="expandSplitBtn2" /> + <click selector="{{BlockNewPagePageActionsSection.saveAndClose}}" stepKey="clicksaveAndClose" /> + <waitForPageLoad stepKey="waitForPageLoad3"/> + <see userInput="No marginal white space please" stepKey="seeNoMarginalSpaceMsgOnIdentifierField"/> + </test> +</tests> diff --git a/app/code/Magento/Cms/Test/Mftf/Test/AdminCreateCmsPageTest.xml b/app/code/Magento/Cms/Test/Mftf/Test/AdminCreateCmsPageTest.xml index db8b5606cd398..b2fca0ff99115 100644 --- a/app/code/Magento/Cms/Test/Mftf/Test/AdminCreateCmsPageTest.xml +++ b/app/code/Magento/Cms/Test/Mftf/Test/AdminCreateCmsPageTest.xml @@ -17,103 +17,20 @@ <severity value="CRITICAL"/> <testCaseId value="MAGETWO-25580"/> <group value="Cms"/> + <group value="WYSIWYGDisabled"/> </annotations> <before> - <actionGroup ref="LoginActionGroup" stepKey="loginGetFromGeneralFile"/> - <actionGroup ref="DisabledWYSIWYGActionGroup" stepKey="disableWYSIWYG"/> + <actionGroup ref="AdminLoginActionGroup" stepKey="loginGetFromGeneralFile"/> </before> <after> - <actionGroup ref="logout" stepKey="adminLogout"/> + <actionGroup ref="AdminLogoutActionGroup" stepKey="adminLogout"/> </after> - <amOnPage url="{{CmsPagesPage.url}}" stepKey="amOnPagePagesGrid"/> - <waitForPageLoad stepKey="waitForPageLoad1"/> - <click selector="{{CmsPagesPageActionsSection.addNewPageButton}}" stepKey="clickAddNewPage"/> - <fillField selector="{{CmsNewPagePageBasicFieldsSection.pageTitle}}" userInput="{{_defaultCmsPage.title}}" stepKey="fillFieldTitle"/> - <click selector="{{CmsNewPagePageContentSection.header}}" stepKey="clickExpandContent"/> - <fillField selector="{{CmsNewPagePageContentSection.contentHeading}}" userInput="{{_defaultCmsPage.content_heading}}" stepKey="fillFieldContentHeading"/> - <!-- As of 2017/11/15, this test is failing here (Jenkins only, works locally). See MQE-282. --> - <fillField selector="{{CmsNewPagePageContentSection.content}}" userInput="{{_defaultCmsPage.content}}" stepKey="fillFieldContent"/> - <click selector="{{CmsNewPagePageSeoSection.header}}" stepKey="clickExpandSearchEngineOptimisation"/> - <fillField selector="{{CmsNewPagePageSeoSection.urlKey}}" userInput="{{_defaultCmsPage.identifier}}" stepKey="fillFieldUrlKey"/> - <waitForElementVisible selector="{{CmsNewPagePageActionsSection.saveAndContinueEdit}}" stepKey="waitForSaveButtonVisible"/> - <click selector="{{CmsNewPagePageActionsSection.expandSplitButton}}" stepKey="expandButtonMenu"/> - <waitForElementVisible selector="{{CmsNewPagePageActionsSection.splitButtonMenu}}" stepKey="waitForSplitButtonMenuVisible"/> - <click selector="{{CmsNewPagePageActionsSection.savePage}}" stepKey="clickSavePage"/> - <see userInput="You saved the page." stepKey="seeSuccessMessage"/> - <amOnPage url="{{_defaultCmsPage.identifier}}" stepKey="amOnPageTestPage"/> - <waitForPageLoad stepKey="waitForPageLoad2"/> - <see userInput="{{_defaultCmsPage.content_heading}}" stepKey="seeContentHeading"/> - <see userInput="{{_defaultCmsPage.content}}" stepKey="seeContent"/> - </test> - <test name="AdminConfigDefaultCMSPageLayoutFromConfigurationSettingTest"> - <annotations> - <features value="Cms"/> - <stories value="Default layout configuration MAGETWO-88793"/> - <title value="Admin should be able to configure the default layout for CMS Page from System Configuration"/> - <description value="Admin should be able to configure the default layout for CMS Page from System Configuration"/> - <severity value="CRITICAL"/> - <testCaseId value="MAGETWO-89025"/> - <group value="Cms"/> - </annotations> - <before> - <actionGroup ref="LoginAsAdmin" stepKey="loginAsAdmin"/> - </before> - <after> - <actionGroup ref="RestoreLayoutSetting" stepKey="sampleActionGroup"/> - <actionGroup ref="logout" stepKey="logout"/> - </after> - <amOnPage url="{{WebConfigurationPage.url}}" stepKey="navigateToWebConfigurationPage"/> - <waitForPageLoad stepKey="waitForPageLoad"/> - <conditionalClick stepKey="expandDefaultLayouts" selector="{{WebSection.DefaultLayoutsTab}}" dependentSelector="{{WebSection.CheckIfTabExpand}}" visible="true" /> - <waitForElementVisible selector="{{DefaultLayoutsSection.pageLayout}}" stepKey="DefaultProductLayout" /> - <seeOptionIsSelected selector="{{DefaultLayoutsSection.pageLayout}}" userInput="1 column" stepKey="seeOneColumnSelected" /> - <seeOptionIsSelected selector="{{DefaultLayoutsSection.productLayout}}" userInput="No layout updates" stepKey="seeNoLayoutUpdatesSelected1" /> - <seeOptionIsSelected selector="{{DefaultLayoutsSection.categoryLayout}}" userInput="No layout updates" stepKey="seeNoLayoutUpdatesSelected2" /> - <selectOption selector="{{DefaultLayoutsSection.pageLayout}}" userInput="2 columns with right bar" stepKey="selectColumnsWithRightBar"/> - <click selector="{{ContentManagementSection.Save}}" stepKey="clickSaveConfig" /> - <amOnPage url="{{CmsNewPagePage.url}}" stepKey="amOnPagePagesGrid"/> - <waitForPageLoad stepKey="waitForPageLoad1"/> - <waitForLoadingMaskToDisappear stepKey="wait2" /> - <click selector="{{CmsDesignSection.DesignTab}}" stepKey="clickOnDesignTab"/> - <waitForElementVisible selector="{{CmsDesignSection.LayoutDropdown}}" stepKey="waitForLayoutDropDown" /> - <seeOptionIsSelected selector="{{CmsDesignSection.LayoutDropdown}}" userInput="2 columns with right bar" stepKey="seeColumnsWithRightBar" /> - </test> - <test name="AdminCreateDuplicatedCmsPageTest"> - <annotations> - <features value="Cms"/> - <stories value="CMS Page Duplication and Reset Removal MAGETWO-87096"/> - <title value="Admin should be able to duplicate a CMS Page"/> - <description value="Admin should be able to duplicate a CMS Page"/> - <severity value="CRITICAL"/> - <testCaseId value="MAGETWO-89184"/> - <group value="Cms"/> - </annotations> - <before> - <actionGroup ref="LoginActionGroup" stepKey="loginGetFromGeneralFile"/> - <actionGroup ref="DisabledWYSIWYGActionGroup" stepKey="disableWYSIWYG"/> - </before> - <after> - <actionGroup ref="logout" stepKey="logout"/> - </after> - <amOnPage url="{{CmsNewPagePage.url}}" stepKey="amOnPageCreationForm"/> - <waitForPageLoad stepKey="waitForPageLoad1"/> - <!--Verify Save&Duplicate button and Save&Close button--> - <click selector="{{CmsNewPagePageActionsSection.expandSplitButton}}" stepKey="expandSplitBtn1" /> - <see selector="{{CmsNewPagePageActionsSection.saveAndDuplicate}}" userInput="Save & Duplicate" stepKey="seeSaveAndDuplicate"/> - <see selector="{{CmsNewPagePageActionsSection.saveAndClose}}" userInput="Save & Close" stepKey="seeSaveAndClose"/> - <!--Create new CMS Page page--> - <actionGroup ref="FillOutCMSPageContent" stepKey="FillOutBlockContent"/> - <click selector="{{CmsNewPagePageActionsSection.expandSplitButton}}" stepKey="expandSplitBtn2" /> - <click selector="{{CmsNewPagePageActionsSection.saveAndDuplicate}}" stepKey="clickSaveAndDuplicate" /> - <waitForPageLoad stepKey="waitForPageLoad3"/> - <see userInput="You saved the page." stepKey="seeSavedPageMsgOnForm"/> - <see userInput="You duplicated the page." stepKey="seeDuplicatedPageMsg"/> - <!--Verify duplicated CMS Page--> - <seeElement selector="{{BlockNewPageBasicFieldsSection.isActive('0')}}" stepKey="seeBlockNotEnable" /> - <actionGroup ref="AssertCMSPageContentActionGroup" stepKey="assertContent"/> - <click selector="{{CmsNewPagePageActionsSection.expandSplitButton}}" stepKey="expandSplitBtn3" /> - <click selector="{{CmsNewPagePageActionsSection.saveAndClose}}" stepKey="clickSaveAndClose"/> - <see userInput="You saved the page." stepKey="seeSavedCMSPageMsgOnGrid"/> - <seeElement selector="div[data-role='grid-wrapper']" stepKey="seeGridPage" /> + <actionGroup ref="AdminNavigateToPageGridActionGroup" stepKey="navigateToCmsPageGrid" /> + <actionGroup ref="CreateNewPageWithBasicValues" stepKey="createNewPageWithBasicValues" /> + <actionGroup ref="SaveCmsPageActionGroup" stepKey="clickSaveCmsPageButton" /> + <actionGroup ref="VerifyCreatedCmsPage" stepKey="verifyCmsPage" /> + <actionGroup ref="DeletePageByUrlKeyActionGroup" stepKey="deletePage"> + <argument name="UrlKey" value="{{_defaultCmsPage.identifier}}"/> + </actionGroup> </test> </tests> diff --git a/app/code/Magento/Cms/Test/Mftf/Test/AdminCreateDisabledCmsBlockEntityAndAssignToCategoryTest.xml b/app/code/Magento/Cms/Test/Mftf/Test/AdminCreateDisabledCmsBlockEntityAndAssignToCategoryTest.xml new file mode 100644 index 0000000000000..51fbbf8020545 --- /dev/null +++ b/app/code/Magento/Cms/Test/Mftf/Test/AdminCreateDisabledCmsBlockEntityAndAssignToCategoryTest.xml @@ -0,0 +1,55 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + /** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +--> + +<tests xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/testSchema.xsd"> + <test name="AdminCreateDisabledCmsBlockEntityAndAssignToCategoryTest"> + <annotations> + <features value="Cms"/> + <testCaseId value="MC-14657" /> + <stories value="Create a CMS block via the Admin, disable, add to category, verify on frontend"/> + <title value="Create disabled CMS block entity and assign to category"/> + <severity value="MAJOR"/> + <group value="cMSContent"/> + <group value="mtf_migrated"/> + </annotations> + <before> + <createData entity="_defaultCategory" stepKey="newDefaultCategory"/> + <createData entity="_defaultBlock" stepKey="newDefaultBlock"/> + <actionGroup ref="AdminLoginActionGroup" stepKey="loginAsAdmin"/> + </before> + <after> + <deleteData createDataKey="newDefaultCategory" stepKey="deleteCategory"/> + <deleteData createDataKey="newDefaultBlock" stepKey="deleteBlock"/> + <actionGroup ref="AdminLogoutActionGroup" stepKey="logout"/> + </after> + + <actionGroup ref="AdminOpenCmsBlockActionGroup" stepKey="openCmsBlock"> + <argument name="block_id" value="$$newDefaultBlock.id$$"/> + </actionGroup> + <actionGroup ref="AdminSetCMSBlockDisabledActionGroup" stepKey="disableBlock"/> + <actionGroup ref="SaveCMSBlockActionGroup" stepKey="saveCMSBlock"/> + + <actionGroup ref="AdminOpenCategoriesPageActionGroup" stepKey="openCategoriesPage"/> + <actionGroup ref="AdminCategoriesExpandAllActionGroup" stepKey="expandAll"/> + <actionGroup ref="AdminCategoriesOpenCategoryActionGroup" stepKey="openCategory"> + <argument name="category" value="$$newDefaultCategory$$"/> + </actionGroup> + <actionGroup ref="AdminCategoriesOpenContentSectionActionGroup" stepKey="openContentSection"/> + <actionGroup ref="AdminCategoriesSetStaticBlockActionGroup" stepKey="setStaticBlock"/> + <actionGroup ref="AdminCategoriesSetDisplayModeActionGroup" stepKey="setDisplay"> + <argument name="value" value="Static block only"/> + </actionGroup> + <actionGroup ref="AdminSaveCategoryActionGroup" stepKey="saveCategory"/> + + <actionGroup ref="AssertStorefrontNoTextOnCategoryPageActionGroup" stepKey="assertBlockOnCategoryFrontPage"> + <argument name="category" value="$$newDefaultCategory$$"/> + <argument name="text" value="{{_defaultBlock.content}}"/> + </actionGroup> + </test> +</tests> diff --git a/app/code/Magento/Cms/Test/Mftf/Test/AdminCreateDuplicatedCmsPageTest.xml b/app/code/Magento/Cms/Test/Mftf/Test/AdminCreateDuplicatedCmsPageTest.xml new file mode 100644 index 0000000000000..1e2e47de8c17f --- /dev/null +++ b/app/code/Magento/Cms/Test/Mftf/Test/AdminCreateDuplicatedCmsPageTest.xml @@ -0,0 +1,43 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + /** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +--> + +<tests xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/testSchema.xsd"> + <test name="AdminCreateDuplicatedCmsPageTest"> + <annotations> + <features value="Cms"/> + <stories value="CMS Page Duplication and Reset Removal MAGETWO-87096"/> + <title value="Admin should be able to duplicate a CMS Page"/> + <description value="Admin should be able to duplicate a CMS Page"/> + <severity value="CRITICAL"/> + <testCaseId value="MAGETWO-89184"/> + <group value="Cms"/> + <group value="WYSIWYGDisabled"/> + </annotations> + <before> + <actionGroup ref="AdminLoginActionGroup" stepKey="loginGetFromGeneralFile"/> + </before> + <after> + <actionGroup ref="AdminLogoutActionGroup" stepKey="logout"/> + </after> + <!-- Navigate to create a CMS page and Verify Save&Duplicate - Save&Close button --> + <actionGroup ref="AssertAdminCmsPageSaveSplitButtonActionGroup" stepKey="verifyCmsPageSaveButton" /> + <!-- Filled out Content --> + <actionGroup ref="FillOutCMSPageContent" stepKey="FillOutPageContent"/> + <!-- Click save and duplicate action --> + <actionGroup ref="AdminSaveAndDuplicateCMSPageWithSplitButtonActionGroup" stepKey="clickSaveAndDuplicateButton"/> + <!--Verify duplicated CMS Page--> + <seeElement selector="{{BlockNewPageBasicFieldsSection.isActive('0')}}" stepKey="seeBlockNotEnable" /> + <actionGroup ref="AssertCMSPageContentActionGroup" stepKey="assertContent"/> + <!-- Click Save Button --> + <actionGroup ref="SaveCmsPageActionGroup" stepKey="clickSaveCmsPageButton"/> + <actionGroup ref="DeletePageByUrlKeyActionGroup" stepKey="deleteCMSPage"> + <argument name="UrlKey" value="{{_duplicatedCMSPage.identifier}}"/> + </actionGroup> + </test> +</tests> diff --git a/app/code/Magento/Cms/Test/Mftf/Test/AdminCreateEnabledCmsBlockEntityAndAssignToCategoryTest.xml b/app/code/Magento/Cms/Test/Mftf/Test/AdminCreateEnabledCmsBlockEntityAndAssignToCategoryTest.xml new file mode 100644 index 0000000000000..5ccd8fa713cc0 --- /dev/null +++ b/app/code/Magento/Cms/Test/Mftf/Test/AdminCreateEnabledCmsBlockEntityAndAssignToCategoryTest.xml @@ -0,0 +1,49 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + /** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +--> + +<tests xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/testSchema.xsd"> + <test name="AdminCreateEnabledCmsBlockEntityAndAssignToCategoryTest"> + <annotations> + <features value="Cms"/> + <stories value="Create a CMS block via the Admin, add to category, verify on frontend"/> + <title value="Create CMS block entity and assign to category"/> + <testCaseId value="MC-14658"/> + <severity value="MAJOR"/> + <group value="cMSContent"/> + <group value="mtf_migrated"/> + </annotations> + <before> + <createData entity="_defaultCategory" stepKey="newDefaultCategory"/> + <createData entity="_defaultBlock" stepKey="newDefaultBlock"/> + <actionGroup ref="AdminLoginActionGroup" stepKey="loginAsAdmin"/> + </before> + <after> + <deleteData createDataKey="newDefaultCategory" stepKey="deleteCategory"/> + <deleteData createDataKey="newDefaultBlock" stepKey="deleteBlock"/> + <actionGroup ref="AdminLogoutActionGroup" stepKey="logout"/> + </after> + + <actionGroup ref="AdminOpenCategoriesPageActionGroup" stepKey="openCategoriesPage"/> + <actionGroup ref="AdminCategoriesExpandAllActionGroup" stepKey="expandAll"/> + <actionGroup ref="AdminCategoriesOpenCategoryActionGroup" stepKey="openCategory"> + <argument name="category" value="$$newDefaultCategory$$"/> + </actionGroup> + <actionGroup ref="AdminCategoriesOpenContentSectionActionGroup" stepKey="openContentSection"/> + <actionGroup ref="AdminCategoriesSetStaticBlockActionGroup" stepKey="setStaticBlock"/> + <actionGroup ref="AdminCategoriesSetDisplayModeActionGroup" stepKey="setDisplay"> + <argument name="value" value="Static block only"/> + </actionGroup> + <actionGroup ref="AdminSaveCategoryActionGroup" stepKey="saveCategory"/> + + <actionGroup ref="AssertStorefrontTextOnCategoryPageActionGroup" stepKey="assertBlockOnCategoryFrontPage"> + <argument name="category" value="$$newDefaultCategory$$"/> + <argument name="text" value="{{_defaultBlock.content}}"/> + </actionGroup> + </test> +</tests> diff --git a/app/code/Magento/Cms/Test/Mftf/Test/AdminMediaGalleryPopupUploadImagesWithoutErrorTest.xml b/app/code/Magento/Cms/Test/Mftf/Test/AdminMediaGalleryPopupUploadImagesWithoutErrorTest.xml index ff389a6ce2965..58803533efaf2 100644 --- a/app/code/Magento/Cms/Test/Mftf/Test/AdminMediaGalleryPopupUploadImagesWithoutErrorTest.xml +++ b/app/code/Magento/Cms/Test/Mftf/Test/AdminMediaGalleryPopupUploadImagesWithoutErrorTest.xml @@ -26,14 +26,14 @@ <!--Create block--> <comment userInput="Create block" stepKey="commentCreateBlock"/> <createData entity="Sales25offBlock" stepKey="createBlock"/> - <actionGroup ref="LoginActionGroup" stepKey="login"/> + <actionGroup ref="AdminLoginActionGroup" stepKey="login"/> </before> <after> <!--Disable WYSIWYG options--> <comment userInput="Disable WYSIWYG options" stepKey="commentDisableWYSIWYG"/> <magentoCLI command="config:set cms/wysiwyg/enabled disabled" stepKey="disableWYSIWYG"/> <deleteData createDataKey="createBlock" stepKey="deleteBlock" /> - <actionGroup ref="logout" stepKey="logout"/> + <actionGroup ref="AdminLogoutActionGroup" stepKey="logout"/> </after> <!--Open created block page and add image--> <comment userInput="Open create block page and add image" stepKey="commentOpenBlockPage"/> diff --git a/app/code/Magento/Cms/Test/Mftf/Test/AdminSaveAndCloseCmsBlockTest.xml b/app/code/Magento/Cms/Test/Mftf/Test/AdminSaveAndCloseCmsBlockTest.xml new file mode 100644 index 0000000000000..b1073c1a0f41d --- /dev/null +++ b/app/code/Magento/Cms/Test/Mftf/Test/AdminSaveAndCloseCmsBlockTest.xml @@ -0,0 +1,40 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + /** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +--> + +<tests xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/testSchema.xsd"> + <test name="AdminSaveAndCloseCmsBlockTest"> + <annotations> + <features value="Cms"/> + <stories value="CMS Block Creation and Reset Removal"/> + <title value="Admin should be able to create a CMS block"/> + <description value="Admin should be able to create a CMS block using save and close"/> + <severity value="CRITICAL"/> + <group value="Cms"/> + <group value="WYSIWYGDisabled"/> + </annotations> + + <before> + <actionGroup ref="AdminLoginActionGroup" stepKey="loginToAdminPanel"/> + </before> + + <after> + <actionGroup ref="deleteBlock" stepKey="deleteCreatedBlock"> + <argument name="Block" value="_defaultBlock"/> + </actionGroup> + <actionGroup ref="ClearFiltersAdminDataGridActionGroup" stepKey="resetGridFilters"/> + <actionGroup ref="AdminLogoutActionGroup" stepKey="logoutFromAdmin"/> + </after> + + <!-- Navigate to create cms block page and verify save split button --> + <actionGroup ref="VerifyCmsBlockSaveSplitButtonActionGroup" stepKey="assertCmsBlockSaveSplitButton"/> + <!--Create new CMS Block page--> + <actionGroup ref="FillOutBlockContent" stepKey="fillOutBlockContent"/> + <actionGroup ref="SaveAndCloseCMSBlockWithSplitButtonActionGroup" stepKey="saveCmsBlockContent" /> + </test> +</tests> diff --git a/app/code/Magento/Cms/Test/Mftf/Test/CheckOrderOfProdsInWidgetOnCMSPageTest.xml b/app/code/Magento/Cms/Test/Mftf/Test/CheckOrderOfProdsInWidgetOnCMSPageTest.xml index afcf5e3764ef7..c69cd620b1d72 100644 --- a/app/code/Magento/Cms/Test/Mftf/Test/CheckOrderOfProdsInWidgetOnCMSPageTest.xml +++ b/app/code/Magento/Cms/Test/Mftf/Test/CheckOrderOfProdsInWidgetOnCMSPageTest.xml @@ -20,7 +20,7 @@ <group value="Catalog"/> </annotations> <before> - <actionGroup ref="LoginAsAdmin" stepKey="loginAsAdmin"/> + <actionGroup ref="AdminLoginActionGroup" stepKey="loginAsAdmin"/> <actionGroup ref="EnabledWYSIWYGActionGroup" stepKey="enableWYSIWYG"/> <actionGroup ref="SwitchToVersion4ActionGroup" stepKey="enableTinyMCE4"/> <waitForPageLoad stepKey="waitConfigToSave"/> @@ -39,7 +39,7 @@ <deleteData createDataKey="product1" stepKey="deleteProduct1"/> <deleteData createDataKey="product2" stepKey="deleteProduct2"/> <deleteData createDataKey="createCMSPage" stepKey="deletePreReqCMSPage"/> - <actionGroup ref="logout" stepKey="logoutOfAdmin"/> + <actionGroup ref="AdminLogoutActionGroup" stepKey="logoutOfAdmin"/> </after> <actionGroup ref="NavigateToCreatedCMSPageActionGroup" stepKey="navigateToCreatedCMSPage1"> <argument name="CMSPage" value="$$createCMSPage$$"/> diff --git a/app/code/Magento/Cms/Test/Mftf/Test/CheckStaticBlocksTest.xml b/app/code/Magento/Cms/Test/Mftf/Test/CheckStaticBlocksTest.xml index e6ab1c130606b..fe3e69880fc5c 100644 --- a/app/code/Magento/Cms/Test/Mftf/Test/CheckStaticBlocksTest.xml +++ b/app/code/Magento/Cms/Test/Mftf/Test/CheckStaticBlocksTest.xml @@ -14,13 +14,13 @@ <stories value="MAGETWO-91559 - Static blocks with same ID appear in place of correct block"/> <title value="Check static blocks: ID should be unique per Store View"/> <description value="Check static blocks: ID should be unique per Store View"/> - <severity value="CRITICAL"/> + <severity value="BLOCKER"/> <testCaseId value="MAGETWO-94229"/> <group value="Cms"/> </annotations> <before> <magentoCLI command="config:set cms/wysiwyg/enabled disabled" stepKey="disableWYSIWYG"/> - <actionGroup ref="LoginAsAdmin" stepKey="LoginAsAdmin"/> + <actionGroup ref="AdminLoginActionGroup" stepKey="LoginAsAdmin"/> <actionGroup ref="AdminCreateWebsiteActionGroup" stepKey="AdminCreateWebsite"> <argument name="newWebsiteName" value="secondWebsite"/> <argument name="websiteCode" value="second_website"/> @@ -56,12 +56,23 @@ <seeInCurrentUrl url="cms/block/new" stepKey="VerifyNewBlockPageIsOpened1"/> <!--Add new BLock with the same data--> <actionGroup ref="FillOutBlockContent" stepKey="FillOutBlockContent1"/> - <selectOption selector="{{BlockNewPageBasicFieldsSection.storeView}}" userInput="Default Store View" stepKey="selectDefaultStoreView" /> - <selectOption selector="{{BlockNewPageBasicFieldsSection.storeView}}" userInput="{{customStore.name}}" stepKey="selectSecondStoreView1" /> <click selector="{{BlockNewPagePageActionsSection.saveBlock}}" stepKey="ClickToSaveBlock1"/> <waitForPageLoad stepKey="waitForPageLoad6"/> <!--Verify that corresponding message is displayed--> <see userInput="A block identifier with the same properties already exists in the selected store." stepKey="VerifyBlockIsSaved1"/> + <!--Click to go back and add new block--> + <click selector="{{BlockNewPagePageActionsSection.back}}" stepKey="ClickToGoBack1"/> + <waitForPageLoad stepKey="waitForPageLoad7"/> + <click selector="{{BlockPageActionsSection.addNewBlock}}" stepKey="ClickToAddNewBlock2"/> + <waitForPageLoad stepKey="waitForPageLoad8"/> + <seeInCurrentUrl url="cms/block/new" stepKey="VerifyNewBlockPageIsOpened2"/> + <!--Add new BLock with the same data for another store view--> + <actionGroup ref="FillOutBlockContent" stepKey="FillOutBlockContent2"/> + <selectOption selector="{{BlockNewPageBasicFieldsSection.storeView}}" userInput="Default Store View" stepKey="selectDefaultStoreView" /> + <selectOption selector="{{BlockNewPageBasicFieldsSection.storeView}}" userInput="{{customStore.name}}" stepKey="selectSecondStoreView1" /> + <click selector="{{BlockNewPagePageActionsSection.saveBlock}}" stepKey="ClickToSaveBlock2"/> + <waitForPageLoad stepKey="waitForPageLoad9"/> + <see userInput="You saved the block." stepKey="VerifyBlockIsSaved2"/> <after> <actionGroup ref="AdminDeleteWebsiteActionGroup" stepKey="DeleteWebsite"> diff --git a/app/code/Magento/Cms/Test/Mftf/Test/StoreFrontMobileViewValidation.xml b/app/code/Magento/Cms/Test/Mftf/Test/StoreFrontMobileViewValidation.xml deleted file mode 100644 index 6165def067ef4..0000000000000 --- a/app/code/Magento/Cms/Test/Mftf/Test/StoreFrontMobileViewValidation.xml +++ /dev/null @@ -1,47 +0,0 @@ -<?xml version="1.0" encoding="UTF-8"?> -<!-- - /** - * Copyright © Magento, Inc. All rights reserved. - * See COPYING.txt for license details. - */ ---> - -<tests xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" - xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/testSchema.xsd"> - <test name="StoreFrontMobileViewValidation"> - <annotations> - <features value="Cms"/> - <stories value="Mobile view page footer should stick to the bottom of page on Store front"/> - <title value="Mobile view page footer should stick to the bottom of page on Store front"/> - <description value="Mobile view page footer should stick to the bottom of page on Store front"/> - <severity value="MAJOR"/> - <testCaseId value="MAGETWO-94333"/> - <useCaseId value="MAGETWO-93978"/> - <group value="Cms"/> - </annotations> - <before> - <createData entity="_longContentCmsPage" stepKey="createPreReqCMSPage"/> - </before> - <after> - <deleteData createDataKey="createPreReqCMSPage" stepKey="deletePreReqCMSPage"/> - <resizeWindow width="1280" height="1024" stepKey="resizeWindowToDesktop"/> - </after> - <resizeWindow width="375" height="812" stepKey="resizeWindowToMobile"/> - <amOnPage url="$$createPreReqCMSPage.identifier$$" stepKey="amOnPageTestPage"/> - <waitForPageLoad stepKey="waitForPageLoad6" /> - <!--Verifying that Footer is not in visible area by default as the CMS page has lots of content which will occupy entire visible area--> - <executeJS function="return document.querySelector('{{StorefrontCMSPageSection.footerTop}}').getBoundingClientRect().top" stepKey="topOfFooter"/> - <assertGreaterThan stepKey="assertDefaultLoad"> - <actualResult type="variable">topOfFooter</actualResult> - <expectedResult type="string">812</expectedResult> - </assertGreaterThan> - <!--Verifying that even after scroll footer section is below the main content section--> - <scrollTo selector="{{StorefrontCMSPageSection.footerTop}}" stepKey="scrollToFooterSection"/> - <executeJS function="return document.querySelector('{{StorefrontCMSPageSection.footerTop}}').getBoundingClientRect().top" stepKey="topOfTheFooterAfterScroll"/> - <executeJS function="return document.querySelector('{{StorefrontCMSPageSection.mainContent}}').getBoundingClientRect().bottom" stepKey="bottomOfMainContent"/> - <assertGreaterThan stepKey="assertAfterScroll"> - <actualResult type="variable">topOfTheFooterAfterScroll</actualResult> - <expectedResult type="variable">bottomOfMainContent</expectedResult> - </assertGreaterThan> - </test> -</tests> diff --git a/app/code/Magento/Cms/Test/Mftf/Test/StoreFrontMobileViewValidationTest.xml b/app/code/Magento/Cms/Test/Mftf/Test/StoreFrontMobileViewValidationTest.xml new file mode 100644 index 0000000000000..38005682287e8 --- /dev/null +++ b/app/code/Magento/Cms/Test/Mftf/Test/StoreFrontMobileViewValidationTest.xml @@ -0,0 +1,47 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + /** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +--> + +<tests xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/testSchema.xsd"> + <test name="StoreFrontMobileViewValidationTest"> + <annotations> + <features value="Cms"/> + <stories value="Mobile view page footer should stick to the bottom of page on Store front"/> + <title value="Mobile view page footer should stick to the bottom of page on Store front"/> + <description value="Mobile view page footer should stick to the bottom of page on Store front"/> + <severity value="MAJOR"/> + <testCaseId value="MAGETWO-94333"/> + <useCaseId value="MAGETWO-93978"/> + <group value="Cms"/> + </annotations> + <before> + <createData entity="_longContentCmsPage" stepKey="createPreReqCMSPage"/> + </before> + <after> + <deleteData createDataKey="createPreReqCMSPage" stepKey="deletePreReqCMSPage"/> + <resizeWindow width="1280" height="1024" stepKey="resizeWindowToDesktop"/> + </after> + <resizeWindow width="375" height="812" stepKey="resizeWindowToMobile"/> + <amOnPage url="$$createPreReqCMSPage.identifier$$" stepKey="amOnPageTestPage"/> + <waitForPageLoad stepKey="waitForPageLoad6" /> + <!--Verifying that Footer is not in visible area by default as the CMS page has lots of content which will occupy entire visible area--> + <executeJS function="return document.querySelector('{{StorefrontCMSPageSection.footerTop}}').getBoundingClientRect().top" stepKey="topOfFooter"/> + <assertGreaterThan stepKey="assertDefaultLoad"> + <actualResult type="variable">topOfFooter</actualResult> + <expectedResult type="string">812</expectedResult> + </assertGreaterThan> + <!--Verifying that even after scroll footer section is below the main content section--> + <scrollTo selector="{{StorefrontCMSPageSection.footerTop}}" stepKey="scrollToFooterSection"/> + <executeJS function="return document.querySelector('{{StorefrontCMSPageSection.footerTop}}').getBoundingClientRect().top" stepKey="topOfTheFooterAfterScroll"/> + <executeJS function="return document.querySelector('{{StorefrontCMSPageSection.mainContent}}').getBoundingClientRect().bottom" stepKey="bottomOfMainContent"/> + <assertGreaterThan stepKey="assertAfterScroll"> + <actualResult type="variable">topOfTheFooterAfterScroll</actualResult> + <expectedResult type="variable">bottomOfMainContent</expectedResult> + </assertGreaterThan> + </test> +</tests> diff --git a/app/code/Magento/Cms/Test/Mftf/Test/StoreViewLanguageCorrectSwitchTest.xml b/app/code/Magento/Cms/Test/Mftf/Test/StoreViewLanguageCorrectSwitchTest.xml index 6569c07f4580a..2fd31fffa838d 100644 --- a/app/code/Magento/Cms/Test/Mftf/Test/StoreViewLanguageCorrectSwitchTest.xml +++ b/app/code/Magento/Cms/Test/Mftf/Test/StoreViewLanguageCorrectSwitchTest.xml @@ -20,7 +20,7 @@ <group value="Cms"/> </annotations> <before> - <actionGroup ref="LoginAsAdmin" stepKey="loginAsAdmin"/> + <actionGroup ref="AdminLoginActionGroup" stepKey="loginAsAdmin"/> <!-- Create Cms Pages --> <createData entity="_newDefaultCmsPage" stepKey="createFirstCmsPage"/> @@ -32,7 +32,7 @@ <actionGroup ref="AdminDeleteStoreViewActionGroup" stepKey="deleteStoreView"> <argument name="customStore" value="NewStoreViewData"/> </actionGroup> - <actionGroup ref="logout" stepKey="logout"/> + <actionGroup ref="AdminLogoutActionGroup" stepKey="logout"/> </after> <!-- Create StoreView --> diff --git a/app/code/Magento/Cms/Test/Mftf/Test/VerifyTinyMCEv4IsNativeWYSIWYGOnBlockTest.xml b/app/code/Magento/Cms/Test/Mftf/Test/VerifyTinyMCEv4IsNativeWYSIWYGOnBlockTest.xml index 944cd02d87b86..58f3b9d5bd3b1 100644 --- a/app/code/Magento/Cms/Test/Mftf/Test/VerifyTinyMCEv4IsNativeWYSIWYGOnBlockTest.xml +++ b/app/code/Magento/Cms/Test/Mftf/Test/VerifyTinyMCEv4IsNativeWYSIWYGOnBlockTest.xml @@ -20,7 +20,7 @@ </annotations> <before> <createData entity="_defaultCmsPage" stepKey="createPreReqCMSPage" /> - <actionGroup ref="LoginActionGroup" stepKey="loginGetFromGeneralFile"/> + <actionGroup ref="AdminLoginActionGroup" stepKey="loginGetFromGeneralFile"/> <actionGroup ref="EnabledWYSIWYGActionGroup" stepKey="enableWYSIWYG"/> <actionGroup ref="SwitchToVersion4ActionGroup" stepKey="switchToTinyMCE4" /> </before> @@ -91,7 +91,7 @@ <waitForPageLoad stepKey="waitForGridReload"/> <deleteData createDataKey="createPreReqCMSPage" stepKey="deletePreReqCMSPage" /> <actionGroup ref="DisabledWYSIWYGActionGroup" stepKey="disableWYSIWYG"/> - <actionGroup ref="logout" stepKey="logout"/> + <actionGroup ref="AdminLogoutActionGroup" stepKey="logout"/> </after> </test> </tests> diff --git a/app/code/Magento/Cms/Test/Mftf/Test/VerifyTinyMCEv4IsNativeWYSIWYGOnCMSPageTest.xml b/app/code/Magento/Cms/Test/Mftf/Test/VerifyTinyMCEv4IsNativeWYSIWYGOnCMSPageTest.xml index 24377e92fe083..43615ac906b00 100644 --- a/app/code/Magento/Cms/Test/Mftf/Test/VerifyTinyMCEv4IsNativeWYSIWYGOnCMSPageTest.xml +++ b/app/code/Magento/Cms/Test/Mftf/Test/VerifyTinyMCEv4IsNativeWYSIWYGOnCMSPageTest.xml @@ -19,7 +19,7 @@ <testCaseId value="MAGETWO-84182 "/> </annotations> <before> - <actionGroup ref="LoginActionGroup" stepKey="loginGetFromGeneralFile"/> + <actionGroup ref="AdminLoginActionGroup" stepKey="loginGetFromGeneralFile"/> <actionGroup ref="EnabledWYSIWYGActionGroup" stepKey="enableWYSIWYG"/> <actionGroup ref="SwitchToVersion4ActionGroup" stepKey="switchToTinyMCE4" /> </before> @@ -51,7 +51,7 @@ <see userInput="Hello World!" stepKey="seeContent" /> <after> <actionGroup ref="DisabledWYSIWYGActionGroup" stepKey="disableWYSIWYG"/> - <actionGroup ref="logout" stepKey="logout"/> + <actionGroup ref="AdminLogoutActionGroup" stepKey="logout"/> </after> </test> </tests> diff --git a/app/code/Magento/Cms/Test/Unit/Controller/Adminhtml/Wysiwyg/DirectiveTest.php b/app/code/Magento/Cms/Test/Unit/Controller/Adminhtml/Wysiwyg/DirectiveTest.php index 5fea276225622..be9f6b8d8ccd5 100644 --- a/app/code/Magento/Cms/Test/Unit/Controller/Adminhtml/Wysiwyg/DirectiveTest.php +++ b/app/code/Magento/Cms/Test/Unit/Controller/Adminhtml/Wysiwyg/DirectiveTest.php @@ -5,100 +5,123 @@ */ namespace Magento\Cms\Test\Unit\Controller\Adminhtml\Wysiwyg; +use Magento\Backend\App\Action\Context; +use Magento\Cms\Controller\Adminhtml\Wysiwyg\Directive; +use Magento\Cms\Model\Template\Filter; +use Magento\Cms\Model\Wysiwyg\Config; +use Magento\Framework\App\RequestInterface; +use Magento\Framework\App\ResponseInterface; +use Magento\Framework\Controller\Result\Raw; +use Magento\Framework\Controller\Result\RawFactory; +use Magento\Framework\Filesystem\Driver\File; +use Magento\Framework\Image\Adapter\AdapterInterface; +use Magento\Framework\Image\AdapterFactory; +use Magento\Framework\ObjectManagerInterface; +use Magento\Framework\TestFramework\Unit\Helper\ObjectManager; +use Magento\Framework\Url\DecoderInterface; +use PHPUnit\Framework\TestCase; +use PHPUnit_Framework_MockObject_MockObject; +use Psr\Log\LoggerInterface; + /** * @covers \Magento\Cms\Controller\Adminhtml\Wysiwyg\Directive * @SuppressWarnings(PHPMD.CouplingBetweenObjects) */ -class DirectiveTest extends \PHPUnit\Framework\TestCase +class DirectiveTest extends TestCase { const IMAGE_PATH = 'pub/media/wysiwyg/image.jpg'; /** - * @var \Magento\Cms\Controller\Adminhtml\Wysiwyg\Directive + * @var Directive */ protected $wysiwygDirective; /** - * @var \Magento\Backend\App\Action\Context|\PHPUnit_Framework_MockObject_MockObject + * @var Context|PHPUnit_Framework_MockObject_MockObject */ protected $actionContextMock; /** - * @var \Magento\Framework\App\RequestInterface|\PHPUnit_Framework_MockObject_MockObject + * @var RequestInterface|PHPUnit_Framework_MockObject_MockObject */ protected $requestMock; /** - * @var \Magento\Framework\Url\DecoderInterface|\PHPUnit_Framework_MockObject_MockObject + * @var DecoderInterface|PHPUnit_Framework_MockObject_MockObject */ protected $urlDecoderMock; /** - * @var \Magento\Framework\ObjectManagerInterface|\PHPUnit_Framework_MockObject_MockObject + * @var ObjectManagerInterface|PHPUnit_Framework_MockObject_MockObject */ protected $objectManagerMock; /** - * @var \Magento\Cms\Model\Template\Filter|\PHPUnit_Framework_MockObject_MockObject + * @var Filter|PHPUnit_Framework_MockObject_MockObject */ protected $templateFilterMock; /** - * @var \Magento\Framework\Image\AdapterFactory|\PHPUnit_Framework_MockObject_MockObject + * @var AdapterFactory|PHPUnit_Framework_MockObject_MockObject */ protected $imageAdapterFactoryMock; /** - * @var \Magento\Framework\Image\Adapter\AdapterInterface|\PHPUnit_Framework_MockObject_MockObject + * @var AdapterInterface|PHPUnit_Framework_MockObject_MockObject */ protected $imageAdapterMock; /** - * @var \Magento\Framework\App\ResponseInterface|\PHPUnit_Framework_MockObject_MockObject + * @var ResponseInterface|PHPUnit_Framework_MockObject_MockObject */ protected $responseMock; /** - * @var \Magento\Cms\Model\Wysiwyg\Config|\PHPUnit_Framework_MockObject_MockObject + * @var File|PHPUnit_Framework_MockObject_MockObject + */ + protected $fileMock; + + /** + * @var Config|PHPUnit_Framework_MockObject_MockObject */ protected $wysiwygConfigMock; /** - * @var \Psr\Log\LoggerInterface|\PHPUnit_Framework_MockObject_MockObject + * @var LoggerInterface|PHPUnit_Framework_MockObject_MockObject */ protected $loggerMock; /** - * @var \Magento\Framework\Controller\Result\RawFactory|\PHPUnit_Framework_MockObject_MockObject + * @var RawFactory|PHPUnit_Framework_MockObject_MockObject */ protected $rawFactoryMock; /** - * @var \Magento\Framework\Controller\Result\Raw|\PHPUnit_Framework_MockObject_MockObject + * @var Raw|PHPUnit_Framework_MockObject_MockObject */ protected $rawMock; protected function setUp() { - $this->actionContextMock = $this->getMockBuilder(\Magento\Backend\App\Action\Context::class) + $this->actionContextMock = $this->getMockBuilder(Context::class) ->disableOriginalConstructor() ->getMock(); - $this->requestMock = $this->getMockBuilder(\Magento\Framework\App\RequestInterface::class) + $this->requestMock = $this->getMockBuilder(RequestInterface::class) ->disableOriginalConstructor() ->getMock(); - $this->urlDecoderMock = $this->getMockBuilder(\Magento\Framework\Url\DecoderInterface::class) + $this->urlDecoderMock = $this->getMockBuilder(DecoderInterface::class) ->disableOriginalConstructor() ->getMock(); - $this->objectManagerMock = $this->getMockBuilder(\Magento\Framework\ObjectManagerInterface::class) + $this->objectManagerMock = $this->getMockBuilder(ObjectManagerInterface::class) ->disableOriginalConstructor() ->getMock(); - $this->templateFilterMock = $this->getMockBuilder(\Magento\Cms\Model\Template\Filter::class) + $this->templateFilterMock = $this->getMockBuilder(Filter::class) ->disableOriginalConstructor() ->getMock(); - $this->imageAdapterFactoryMock = $this->getMockBuilder(\Magento\Framework\Image\AdapterFactory::class) + $this->imageAdapterFactoryMock = $this->getMockBuilder(AdapterFactory::class) ->disableOriginalConstructor() ->getMock(); - $this->imageAdapterMock = $this->getMockBuilder(\Magento\Framework\Image\Adapter\AdapterInterface::class) + $this->imageAdapterMock = $this->getMockBuilder(AdapterInterface::class) ->disableOriginalConstructor() ->setMethods( [ @@ -117,21 +140,25 @@ protected function setUp() ] ) ->getMock(); - $this->responseMock = $this->getMockBuilder(\Magento\Framework\App\ResponseInterface::class) + $this->responseMock = $this->getMockBuilder(ResponseInterface::class) ->disableOriginalConstructor() ->setMethods(['setHeader', 'setBody', 'sendResponse']) ->getMock(); - $this->wysiwygConfigMock = $this->getMockBuilder(\Magento\Cms\Model\Wysiwyg\Config::class) + $this->fileMock = $this->getMockBuilder(File::class) ->disableOriginalConstructor() + ->setMethods(['fileGetContents']) ->getMock(); - $this->loggerMock = $this->getMockBuilder(\Psr\Log\LoggerInterface::class) + $this->wysiwygConfigMock = $this->getMockBuilder(Config::class) ->disableOriginalConstructor() ->getMock(); - $this->rawFactoryMock = $this->getMockBuilder(\Magento\Framework\Controller\Result\RawFactory::class) + $this->loggerMock = $this->getMockBuilder(LoggerInterface::class) + ->disableOriginalConstructor() + ->getMock(); + $this->rawFactoryMock = $this->getMockBuilder(RawFactory::class) ->setMethods(['create']) ->disableOriginalConstructor() ->getMock(); - $this->rawMock = $this->getMockBuilder(\Magento\Framework\Controller\Result\Raw::class) + $this->rawMock = $this->getMockBuilder(Raw::class) ->disableOriginalConstructor() ->getMock(); @@ -145,9 +172,9 @@ protected function setUp() ->method('getObjectManager') ->willReturn($this->objectManagerMock); - $objectManager = new \Magento\Framework\TestFramework\Unit\Helper\ObjectManager($this); + $objectManager = new ObjectManager($this); $this->wysiwygDirective = $objectManager->getObject( - \Magento\Cms\Controller\Adminhtml\Wysiwyg\Directive::class, + Directive::class, [ 'context' => $this->actionContextMock, 'urlDecoder' => $this->urlDecoderMock, @@ -155,7 +182,8 @@ protected function setUp() 'adapterFactory' => $this->imageAdapterFactoryMock, 'logger' => $this->loggerMock, 'config' => $this->wysiwygConfigMock, - 'filter' => $this->templateFilterMock + 'filter' => $this->templateFilterMock, + 'file' => $this->fileMock, ] ); } @@ -172,23 +200,29 @@ public function testExecute() $this->imageAdapterMock->expects($this->once()) ->method('open') ->with(self::IMAGE_PATH); - $this->imageAdapterMock->expects($this->once()) + $this->imageAdapterMock->expects($this->atLeastOnce()) ->method('getMimeType') ->willReturn($mimeType); - $this->rawMock->expects($this->once()) + $this->rawMock->expects($this->atLeastOnce()) ->method('setHeader') ->with('Content-Type', $mimeType) ->willReturnSelf(); - $this->rawMock->expects($this->once()) + $this->rawMock->expects($this->atLeastOnce()) ->method('setContents') ->with($imageBody) ->willReturnSelf(); $this->imageAdapterMock->expects($this->once()) ->method('getImage') ->willReturn($imageBody); + $this->fileMock->expects($this->once()) + ->method('fileGetContents') + ->willReturn($imageBody); $this->rawFactoryMock->expects($this->any()) ->method('create') ->willReturn($this->rawMock); + $this->imageAdapterFactoryMock->expects($this->once()) + ->method('create') + ->willReturn($this->imageAdapterMock); $this->assertSame( $this->rawMock, @@ -217,20 +251,23 @@ public function testExecuteException() $this->imageAdapterMock->expects($this->at(1)) ->method('open') ->with($placeholderPath); - $this->imageAdapterMock->expects($this->once()) + $this->imageAdapterMock->expects($this->atLeastOnce()) ->method('getMimeType') ->willReturn($mimeType); - $this->rawMock->expects($this->once()) + $this->rawMock->expects($this->atLeastOnce()) ->method('setHeader') ->with('Content-Type', $mimeType) ->willReturnSelf(); - $this->rawMock->expects($this->once()) + $this->rawMock->expects($this->atLeastOnce()) ->method('setContents') ->with($imageBody) ->willReturnSelf(); - $this->imageAdapterMock->expects($this->once()) + $this->imageAdapterMock->expects($this->any()) ->method('getImage') ->willReturn($imageBody); + $this->fileMock->expects($this->once()) + ->method('fileGetContents') + ->willReturn($imageBody); $this->loggerMock->expects($this->once()) ->method('warning') ->with($exception); @@ -238,6 +275,10 @@ public function testExecuteException() ->method('create') ->willReturn($this->rawMock); + $this->imageAdapterFactoryMock->expects($this->exactly(1)) + ->method('create') + ->willReturn($this->imageAdapterMock); + $this->assertSame( $this->rawMock, $this->wysiwygDirective->execute() @@ -297,6 +338,20 @@ public function testExecuteWithDeletedImage() ->method('warning') ->with($exception); + $this->rawMock->expects($this->once()) + ->method('setHeader') + ->with('Content-Type', null) + ->willReturnSelf(); + + $this->rawMock->expects($this->once()) + ->method('setContents') + ->with(null) + ->willReturnSelf(); + + $this->rawFactoryMock->expects($this->any()) + ->method('create') + ->willReturn($this->rawMock); + $this->wysiwygDirective->execute(); } } diff --git a/app/code/Magento/Cms/Test/Unit/Controller/Page/ViewTest.php b/app/code/Magento/Cms/Test/Unit/Controller/Page/ViewTest.php index f15e6ff3e3bf2..e4c2beaa75aa7 100644 --- a/app/code/Magento/Cms/Test/Unit/Controller/Page/ViewTest.php +++ b/app/code/Magento/Cms/Test/Unit/Controller/Page/ViewTest.php @@ -3,73 +3,80 @@ * Copyright © Magento, Inc. All rights reserved. * See COPYING.txt for license details. */ +declare(strict_types=1); + namespace Magento\Cms\Test\Unit\Controller\Page; -class ViewTest extends \PHPUnit\Framework\TestCase +use Magento\Cms\Controller\Page\View; +use Magento\Cms\Helper\Page as PageHelper; +use Magento\Framework\App\RequestInterface; +use Magento\Framework\Controller\Result\Forward; +use Magento\Framework\Controller\Result\ForwardFactory; +use Magento\Framework\TestFramework\Unit\Helper\ObjectManager as ObjectManagerHelper; +use Magento\Framework\View\Result\Page; +use PHPUnit\Framework\MockObject\MockObject; +use PHPUnit\Framework\TestCase; + +class ViewTest extends TestCase { + private const STUB_PAGE_ID = 2; + /** - * @var \Magento\Cms\Controller\Page\View + * @var View */ protected $controller; /** - * @var \PHPUnit_Framework_MockObject_MockObject + * @var MockObject */ - protected $cmsHelperMock; + protected $pageHelperMock; /** - * @var \PHPUnit_Framework_MockObject_MockObject + * @var MockObject|RequestInterface */ protected $requestMock; /** - * @var \Magento\Framework\Controller\Result\ForwardFactory|\PHPUnit_Framework_MockObject_MockObject + * @var MockObject|ForwardFactory */ protected $forwardFactoryMock; /** - * @var \Magento\Framework\Controller\Result\Forward|\PHPUnit_Framework_MockObject_MockObject + * @var MockObject|Forward */ protected $forwardMock; /** - * @var \Magento\Framework\View\Result\Page|\PHPUnit_Framework_MockObject_MockObject + * @var MockObject|Page */ protected $resultPageMock; - /** - * @var string - */ - protected $pageId = '2'; - protected function setUp() { - $helper = new \Magento\Framework\TestFramework\Unit\Helper\ObjectManager($this); - $objectManagerMock = $this->createMock(\Magento\Framework\ObjectManagerInterface::class); - $responseMock = $this->createMock(\Magento\Framework\App\Response\Http::class); - $this->resultPageMock = $this->getMockBuilder(\Magento\Framework\View\Result\Page::class) + $objectManager = new ObjectManagerHelper($this); + + $this->resultPageMock = $this->getMockBuilder(Page::class) ->disableOriginalConstructor() ->getMock(); - $this->forwardFactoryMock = $this->getMockBuilder(\Magento\Framework\Controller\Result\ForwardFactory::class) + $this->forwardFactoryMock = $this->getMockBuilder(ForwardFactory::class) ->setMethods(['create']) ->disableOriginalConstructor() ->getMock(); - $this->forwardMock = $this->getMockBuilder(\Magento\Framework\Controller\Result\Forward::class) + $this->forwardMock = $this->getMockBuilder(Forward::class) ->disableOriginalConstructor() ->getMock(); $this->forwardFactoryMock->expects($this->any()) ->method('create') ->willReturn($this->forwardMock); - $this->requestMock = $this->createMock(\Magento\Framework\App\Request\Http::class); - $this->cmsHelperMock = $this->createMock(\Magento\Cms\Helper\Page::class); - $objectManagerMock->expects($this->once())->method('get')->willReturn($this->cmsHelperMock); - $this->controller = $helper->getObject( - \Magento\Cms\Controller\Page\View::class, + $this->requestMock = $this->createMock(RequestInterface::class); + $this->pageHelperMock = $this->createMock(PageHelper::class); + + $this->controller = $objectManager->getObject( + View::class, [ - 'response' => $responseMock, - 'objectManager' => $objectManagerMock, 'request' => $this->requestMock, + 'pageHelper' => $this->pageHelperMock, 'resultForwardFactory' => $this->forwardFactoryMock ] ); @@ -81,13 +88,13 @@ public function testExecuteResultPage() ->method('getParam') ->willReturnMap( [ - ['page_id', $this->pageId, $this->pageId], - ['id', false, $this->pageId] + ['page_id', null, self::STUB_PAGE_ID], + ['id', null, self::STUB_PAGE_ID] ] ); - $this->cmsHelperMock->expects($this->once()) + $this->pageHelperMock->expects($this->once()) ->method('prepareResultPage') - ->with($this->controller, $this->pageId) + ->with($this->controller, self::STUB_PAGE_ID) ->willReturn($this->resultPageMock); $this->assertSame($this->resultPageMock, $this->controller->execute()); } @@ -98,8 +105,8 @@ public function testExecuteResultForward() ->method('getParam') ->willReturnMap( [ - ['page_id', $this->pageId, $this->pageId], - ['id', false, $this->pageId] + ['page_id', null, self::STUB_PAGE_ID], + ['id', null, self::STUB_PAGE_ID] ] ); $this->forwardMock->expects($this->once()) diff --git a/app/code/Magento/Cms/Test/Unit/Helper/Wysiwyg/ImagesTest.php b/app/code/Magento/Cms/Test/Unit/Helper/Wysiwyg/ImagesTest.php index d13b4f47a85e7..4acef951d8f4a 100644 --- a/app/code/Magento/Cms/Test/Unit/Helper/Wysiwyg/ImagesTest.php +++ b/app/code/Magento/Cms/Test/Unit/Helper/Wysiwyg/ImagesTest.php @@ -339,13 +339,14 @@ public function providerIsUsingStaticUrlsAllowed() * @param bool $isExist * @dataProvider providerGetCurrentPath */ - public function testGetCurrentPath($pathId, $expectedPath, $isExist) + public function testGetCurrentPath($pathId, $subDir, $expectedPath, $isExist) { $this->requestMock->expects($this->any()) ->method('getParam') ->willReturnMap( [ ['node', null, $pathId], + ['current_tree_path', null, $subDir], ] ); @@ -367,21 +368,33 @@ public function testGetCurrentPath($pathId, $expectedPath, $isExist) ['PATH', '.'], ] ); - $this->directoryWriteMock->expects($this->once()) - ->method('isExist') - ->willReturn($isExist); - $this->directoryWriteMock->expects($this->any()) - ->method('create') - ->with($this->directoryWriteMock->getRelativePath($expectedPath)); + + if ($subDir) { + $this->directoryWriteMock->expects($this->once()) + ->method('isExist') + ->willReturn($isExist); + $this->directoryWriteMock->expects($this->any()) + ->method('create') + ->with($this->directoryWriteMock->getRelativePath($expectedPath)); + } $this->assertEquals($expectedPath, $this->imagesHelper->getCurrentPath()); } public function testGetCurrentPathThrowException() { + $this->requestMock->expects($this->any()) + ->method('getParam') + ->willReturn('PATH'); + $this->expectException(\Magento\Framework\Exception\LocalizedException::class); - $this->expectExceptionMessage('The directory PATH is not writable by server.'); + $this->expectExceptionMessage( + 'Can\'t create SUBDIR as subdirectory of PATH, you might have some permission issue.' + ); + $this->directoryWriteMock->expects($this->any()) + ->method('getRelativePath') + ->willReturn('SUBDIR'); $this->directoryWriteMock->expects($this->once()) ->method('isExist') ->willReturn(false); @@ -402,12 +415,12 @@ public function testGetCurrentPathThrowException() public function providerGetCurrentPath() { return [ - ['L3Rlc3RfcGF0aA--', 'PATH/test_path', true], - ['L215LmpwZw--', 'PATH', true], - [null, 'PATH', true], - ['L3Rlc3RfcGF0aA--', 'PATH/test_path', false], - ['L215LmpwZw--', 'PATH', false], - [null, 'PATH', false], + ['L3Rlc3RfcGF0aA--', 'L3Rlc3RfcGF0aA--', 'PATH/test_path', true], + ['L215LmpwZw--', '', 'PATH', true], + [null, '', 'PATH', true], + ['L3Rlc3RfcGF0aA--', 'L3Rlc3RfcGF0aA--', 'PATH/test_path', false], + ['L215LmpwZw--', '', 'PATH', false], + [null, '', 'PATH', false], ]; } diff --git a/app/code/Magento/Cms/Test/Unit/Model/Wysiwyg/Images/StorageTest.php b/app/code/Magento/Cms/Test/Unit/Model/Wysiwyg/Images/StorageTest.php index 9a9c63195051c..f87eee62e1095 100644 --- a/app/code/Magento/Cms/Test/Unit/Model/Wysiwyg/Images/StorageTest.php +++ b/app/code/Magento/Cms/Test/Unit/Model/Wysiwyg/Images/StorageTest.php @@ -131,6 +131,7 @@ class StorageTest extends \PHPUnit\Framework\TestCase */ protected function setUp() { + $this->objectManagerHelper = new \Magento\Framework\TestFramework\Unit\Helper\ObjectManager($this); $this->filesystemMock = $this->createMock(\Magento\Framework\Filesystem::class); $this->driverMock = $this->getMockBuilder(\Magento\Framework\Filesystem\DriverInterface::class) ->setMethods(['getRealPathSafety']) @@ -159,10 +160,7 @@ protected function setUp() $this->returnValue($this->directoryMock) ); - $this->fileMock = $this->createPartialMock( - \Magento\Framework\Filesystem\Driver\File::class, - ['getParentDirectory'] - ); + $this->fileMock = $this->objectManagerHelper->getObject(\Magento\Framework\Filesystem\Driver\File::class); $this->ioFileMock = $this->createPartialMock(\Magento\Framework\Filesystem\Io\File::class, ['getPathInfo']); $this->ioFileMock->expects( $this->any() @@ -233,8 +231,6 @@ function ($path) { 'image_allowed' => $this->allowedImageExtensions, ]; - $this->objectManagerHelper = new \Magento\Framework\TestFramework\Unit\Helper\ObjectManager($this); - $this->imagesStorage = $this->objectManagerHelper->getObject( \Magento\Cms\Model\Wysiwyg\Images\Storage::class, [ @@ -525,8 +521,6 @@ public function testUploadFile() ] ); - $this->fileMock->expects($this->any())->method('getParentDirectory')->willReturn($path); - $image = $this->getMockBuilder(\Magento\Catalog\Model\Product\Image::class) ->disableOriginalConstructor() ->setMethods(['open', 'keepAspectRatio', 'resize', 'save']) diff --git a/app/code/Magento/Cms/Test/Unit/Ui/Component/Listing/Column/PageActionsTest.php b/app/code/Magento/Cms/Test/Unit/Ui/Component/Listing/Column/PageActionsTest.php index 53d8ee5220768..781d6d31246ca 100644 --- a/app/code/Magento/Cms/Test/Unit/Ui/Component/Listing/Column/PageActionsTest.php +++ b/app/code/Magento/Cms/Test/Unit/Ui/Component/Listing/Column/PageActionsTest.php @@ -3,91 +3,112 @@ * Copyright © Magento, Inc. All rights reserved. * See COPYING.txt for license details. */ +declare(strict_types=1); + namespace Magento\Cms\Test\Unit\Ui\Component\Listing\Column; use Magento\Cms\Ui\Component\Listing\Column\PageActions; +use Magento\Cms\ViewModel\Page\Grid\UrlBuilder; use Magento\Framework\Escaper; +use Magento\Framework\TestFramework\Unit\Helper\ObjectManager; +use Magento\Framework\UrlInterface; +use Magento\Framework\View\Element\UiComponent\ContextInterface; +use Magento\Framework\View\Element\UiComponent\Processor; +use PHPUnit\Framework\TestCase; +use PHPUnit_Framework_MockObject_MockObject as MockObject; /** * Test for Magento\Cms\Ui\Component\Listing\Column\PageActions class. */ -class PageActionsTest extends \PHPUnit\Framework\TestCase +class PageActionsTest extends TestCase { - public function testPrepareItemsByPageId() + + /** + * @var UrlInterface|MockObject + */ + private $urlBuilderMock; + + /** + * @var UrlBuilder|MockObject + */ + private $scopeUrlBuilderMock; + + /** + * @var ContextInterface|MockObject + */ + private $contextMock; + + /** + * @var Processor|MockObject + */ + private $processorMock; + + /** + * @var Escaper|MockObject + */ + private $escaperMock; + + /** + * @var PageActions + */ + private $model; + + /** + * @inheritDoc + */ + public function setUp() { - $pageId = 1; - // Create Mocks and SUT - $objectManager = new \Magento\Framework\TestFramework\Unit\Helper\ObjectManager($this); - /** @var \PHPUnit_Framework_MockObject_MockObject $urlBuilderMock */ - $urlBuilderMock = $this->getMockBuilder(\Magento\Framework\UrlInterface::class) - ->disableOriginalConstructor() - ->getMock(); - $contextMock = $this->getMockBuilder(\Magento\Framework\View\Element\UiComponent\ContextInterface::class) + $this->urlBuilderMock = $this->createMock(UrlInterface::class); + $this->scopeUrlBuilderMock = $this->createMock(UrlBuilder::class); + $this->processorMock = $this->createMock(Processor::class); + $this->contextMock = $this->getMockBuilder(ContextInterface::class) ->getMockForAbstractClass(); - $processor = $this->getMockBuilder(\Magento\Framework\View\Element\UiComponent\Processor::class) + $this->escaperMock = $this->getMockBuilder(Escaper::class) ->disableOriginalConstructor() + ->setMethods(['escapeHtml']) ->getMock(); - $contextMock->expects($this->never())->method('getProcessor')->willReturn($processor); - /** @var \Magento\Cms\Ui\Component\Listing\Column\PageActions $model */ - $model = $objectManager->getObject( - \Magento\Cms\Ui\Component\Listing\Column\PageActions::class, + $objectManager = new ObjectManager($this); + + $this->model = $objectManager->getObject( + PageActions::class, [ - 'urlBuilder' => $urlBuilderMock, - 'context' => $contextMock, + 'urlBuilder' => $this->urlBuilderMock, + 'context' => $this->contextMock, + 'scopeUrlBuilder' => $this->scopeUrlBuilderMock ] ); - $escaper = $this->getMockBuilder(Escaper::class) - ->disableOriginalConstructor() - ->setMethods(['escapeHtml']) - ->getMock(); - $objectManager->setBackwardCompatibleProperty($model, 'escaper', $escaper); - - // Define test input and expectations - $title = 'page title'; - $items = [ - 'data' => [ - 'items' => [ - [ - 'page_id' => $pageId, - 'title' => $title - ] - ] - ] - ]; - $name = 'item_name'; - $expectedItems = [ - [ - 'page_id' => $pageId, - 'title' => $title, - $name => [ - 'edit' => [ - 'href' => 'test/url/edit', - 'label' => __('Edit'), - '__disableTmpl' => true, - ], - 'delete' => [ - 'href' => 'test/url/delete', - 'label' => __('Delete'), - 'confirm' => [ - 'title' => __('Delete %1', $title), - 'message' => __('Are you sure you want to delete a %1 record?', $title), - '__disableTmpl' => true, - ], - 'post' => true, - '__disableTmpl' => true, - ], - ], - ], - ]; + $objectManager->setBackwardCompatibleProperty($this->model, 'escaper', $this->escaperMock); + } - $escaper->expects(static::once()) + /** + * Verify Prepare Items by page Id. + * + * @dataProvider configDataProvider + * @param int $pageId + * @param string $title + * @param string $name + * @param array $items + * @param array $expectedItems + * @return void + */ + public function testPrepareItemsByPageId( + int $pageId, + string $title, + string $name, + array $items, + array $expectedItems + ):void { + $this->contextMock->expects($this->never()) + ->method('getProcessor') + ->willReturn($this->processorMock); + $this->escaperMock->expects(static::once()) ->method('escapeHtml') ->with($title) ->willReturn($title); // Configure mocks and object data - $urlBuilderMock->expects($this->any()) + $this->urlBuilderMock->expects($this->any()) ->method('getUrl') ->willReturnMap( [ @@ -107,9 +128,77 @@ public function testPrepareItemsByPageId() ], ] ); - $model->setName($name); - $items = $model->prepareDataSource($items); + + $this->scopeUrlBuilderMock->expects($this->any()) + ->method('getUrl') + ->willReturn('test/url/view'); + + $this->model->setName($name); + $items = $this->model->prepareDataSource($items); // Run test $this->assertEquals($expectedItems, $items['data']['items']); } + + /** + * Data provider for testPrepareItemsByPageId + * + * @return array + */ + public function configDataProvider():array + { + $pageId = 1; + $title = 'page title'; + $identifier = 'page_identifier'; + $name = 'item_name'; + + return [ + [ + 'pageId' => $pageId, + 'title' => $title, + 'name' => $name, + 'items' => [ + 'data' => [ + 'items' => [ + [ + 'page_id' => $pageId, + 'title' => $title, + 'identifier' => $identifier + ] + ] + ] + ], + 'expectedItems' => [ + [ + 'page_id' => $pageId, + 'title' => $title, + 'identifier' => $identifier, + $name => [ + 'edit' => [ + 'href' => 'test/url/edit', + 'label' => __('Edit'), + '__disableTmpl' => true, + ], + 'delete' => [ + 'href' => 'test/url/delete', + 'label' => __('Delete'), + 'confirm' => [ + 'title' => __('Delete %1', $title), + 'message' => __('Are you sure you want to delete a %1 record?', $title), + '__disableTmpl' => true, + ], + 'post' => true, + '__disableTmpl' => true, + ], + 'preview' => [ + 'href' => 'test/url/view', + 'label' => __('View'), + '__disableTmpl' => true, + 'target' => '_blank' + ] + ], + ], + ] + ] + ]; + } } diff --git a/app/code/Magento/Cms/Ui/Component/Listing/Column/PageActions.php b/app/code/Magento/Cms/Ui/Component/Listing/Column/PageActions.php index fa3756abfded4..0c6000bdbab84 100644 --- a/app/code/Magento/Cms/Ui/Component/Listing/Column/PageActions.php +++ b/app/code/Magento/Cms/Ui/Component/Listing/Column/PageActions.php @@ -14,7 +14,7 @@ use Magento\Ui\Component\Listing\Columns\Column; /** - * Class PageActions + * Class prepare Page Actions */ class PageActions extends Column { @@ -111,6 +111,7 @@ public function prepareDataSource(array $dataSource) ), 'label' => __('View'), '__disableTmpl' => true, + 'target' => '_blank' ]; } } diff --git a/app/code/Magento/Cms/etc/adminhtml/system.xml b/app/code/Magento/Cms/etc/adminhtml/system.xml index 20d543440565b..785d0e3710f4f 100644 --- a/app/code/Magento/Cms/etc/adminhtml/system.xml +++ b/app/code/Magento/Cms/etc/adminhtml/system.xml @@ -58,9 +58,12 @@ <label>Enable WYSIWYG Editor</label> <source_model>Magento\Cms\Model\Config\Source\Wysiwyg\Enabled</source_model> </field> - <field id="editor" translate="label" type="select" sortOrder="2" showInDefault="1" showInWebsite="1" showInStore="0" canRestore="1"> + <field id="editor" translate="label" type="select" sortOrder="2" showInDefault="1" showInWebsite="1" canRestore="1"> <label>WYSIWYG Editor</label> <source_model>Magento\Cms\Model\Config\Source\Wysiwyg\Editor</source_model> + <depends> + <field id="enabled" negative="1">disabled</field> + </depends> </field> </group> </section> diff --git a/app/code/Magento/Cms/etc/di.xml b/app/code/Magento/Cms/etc/di.xml index 35fb7d82651f0..7fc8268eea5e0 100644 --- a/app/code/Magento/Cms/etc/di.xml +++ b/app/code/Magento/Cms/etc/di.xml @@ -116,14 +116,6 @@ <argument name="resourceModel" xsi:type="string">Magento\Cms\Model\ResourceModel\Block</argument> </arguments> </type> - <virtualType name="CmsGirdFilterPool" type="Magento\Framework\View\Element\UiComponent\DataProvider\FilterPool"> - <arguments> - <argument name="appliers" xsi:type="array"> - <item name="regular" xsi:type="object">Magento\Framework\View\Element\UiComponent\DataProvider\RegularFilter</item> - <item name="fulltext" xsi:type="object">Magento\Framework\View\Element\UiComponent\DataProvider\FulltextFilter</item> - </argument> - </arguments> - </virtualType> <type name="Magento\Framework\Model\Entity\RepositoryFactory"> <arguments> <argument name="entities" xsi:type="array"> @@ -201,6 +193,7 @@ <type name="Magento\Cms\Model\PageRepository"> <arguments> <argument name="collectionProcessor" xsi:type="object">Magento\Cms\Model\Api\SearchCriteria\PageCollectionProcessor</argument> + <argument name="hydrator" xsi:type="object">Magento\Framework\EntityManager\AbstractModelHydrator</argument> </arguments> </type> <virtualType name="Magento\Cms\Model\Api\SearchCriteria\CollectionProcessor\BlockFilterProcessor" type="Magento\Framework\Api\SearchCriteria\CollectionProcessor\FilterProcessor"> diff --git a/app/code/Magento/Cms/registration.php b/app/code/Magento/Cms/registration.php index d9feda25d6118..aa55b4932fdba 100644 --- a/app/code/Magento/Cms/registration.php +++ b/app/code/Magento/Cms/registration.php @@ -4,6 +4,6 @@ * See COPYING.txt for license details. */ -use \Magento\Framework\Component\ComponentRegistrar; +use Magento\Framework\Component\ComponentRegistrar; ComponentRegistrar::register(ComponentRegistrar::MODULE, 'Magento_Cms', __DIR__); diff --git a/app/code/Magento/Cms/view/adminhtml/ui_component/cms_block_listing.xml b/app/code/Magento/Cms/view/adminhtml/ui_component/cms_block_listing.xml index 793fc7d26cb4a..af54df24b64f5 100644 --- a/app/code/Magento/Cms/view/adminhtml/ui_component/cms_block_listing.xml +++ b/app/code/Magento/Cms/view/adminhtml/ui_component/cms_block_listing.xml @@ -146,6 +146,7 @@ <editor> <validation> <rule name="required-entry" xsi:type="boolean">true</rule> + <rule name="no-marginal-whitespace" xsi:type="boolean">true</rule> </validation> <editorType>text</editorType> </editor> diff --git a/app/code/Magento/CmsUrlRewrite/registration.php b/app/code/Magento/CmsUrlRewrite/registration.php index aad46cb41827c..6acd495235110 100644 --- a/app/code/Magento/CmsUrlRewrite/registration.php +++ b/app/code/Magento/CmsUrlRewrite/registration.php @@ -4,6 +4,6 @@ * See COPYING.txt for license details. */ -use \Magento\Framework\Component\ComponentRegistrar; +use Magento\Framework\Component\ComponentRegistrar; ComponentRegistrar::register(ComponentRegistrar::MODULE, 'Magento_CmsUrlRewrite', __DIR__); diff --git a/app/code/Magento/Config/Block/System/Config/Form/Field.php b/app/code/Magento/Config/Block/System/Config/Form/Field.php index e4b582a1f0504..ac4a85b7d3bc6 100644 --- a/app/code/Magento/Config/Block/System/Config/Form/Field.php +++ b/app/code/Magento/Config/Block/System/Config/Form/Field.php @@ -43,6 +43,10 @@ public function render(\Magento\Framework\Data\Form\Element\AbstractElement $ele $element->setDisabled(true); } + if ($element->getIsDisableInheritance()) { + $element->setReadonly(true); + } + $html = '<td class="label"><label for="' . $element->getHtmlId() . '"><span' . $this->_renderScopeLabel($element) . '>' . @@ -94,7 +98,7 @@ protected function _renderInheritCheckbox(\Magento\Framework\Data\Form\Element\A $htmlId = $element->getHtmlId(); $namePrefix = preg_replace('#\[value\](\[\])?$#', '', $element->getName()); $checkedHtml = $element->getInherit() == 1 ? 'checked="checked"' : ''; - $disabled = $element->getIsDisableInheritance() == true ? ' disabled="disabled"' : ''; + $disabled = $element->getIsDisableInheritance() == true ? ' disabled="disabled" readonly="1"' : ''; $html = '<td class="use-default">'; $html .= '<input id="' . diff --git a/app/code/Magento/Config/Model/Config/Importer/SaveProcessor.php b/app/code/Magento/Config/Model/Config/Importer/SaveProcessor.php index 2ea9df52c0a03..225729f69a74e 100644 --- a/app/code/Magento/Config/Model/Config/Importer/SaveProcessor.php +++ b/app/code/Magento/Config/Model/Config/Importer/SaveProcessor.php @@ -91,6 +91,7 @@ public function process(array $data) * @param string $scope The configuration scope (default, website, or store) * @param string $scopeCode The scope code * @return void + * @throws \Magento\Framework\Exception\RuntimeException */ private function invokeSave(array $scopeData, $scope, $scopeCode = null) { @@ -98,11 +99,13 @@ private function invokeSave(array $scopeData, $scope, $scopeCode = null) foreach ($scopeData as $path) { $value = $this->scopeConfig->getValue($path, $scope, $scopeCode); - $backendModel = $this->valueFactory->create($path, $value, $scope, $scopeCode); + if ($value !== null) { + $backendModel = $this->valueFactory->create($path, $value, $scope, $scopeCode); - if ($backendModel instanceof Value) { - $backendModel->beforeSave(); - $backendModel->afterSave(); + if ($backendModel instanceof Value) { + $backendModel->beforeSave(); + $backendModel->afterSave(); + } } } } diff --git a/app/code/Magento/Config/Model/Config/Structure.php b/app/code/Magento/Config/Model/Config/Structure.php index a380dc82a7c5e..a16920f0dc527 100644 --- a/app/code/Magento/Config/Model/Config/Structure.php +++ b/app/code/Magento/Config/Model/Config/Structure.php @@ -292,6 +292,7 @@ public function getFieldPathsByAttribute($attributeName, $attributeValue) foreach ($section['children'] as $group) { if (isset($group['children'])) { $path = $section['id'] . '/' . $group['id']; + // phpcs:ignore Magento2.Performance.ForeachArrayMerge.ForeachArrayMerge $result = array_merge( $result, $this->_getGroupFieldPathsByAttribute( @@ -398,7 +399,7 @@ private function getFieldsRecursively(array $elements = []) $this->getFieldsRecursively($element['children']) ); } else { - if ($element['_elementType'] === 'field' && isset($element['label'])) { + if ($element['_elementType'] === 'field') { $structurePath = (isset($element['path']) ? $element['path'] . '/' : '') . $element['id']; $configPath = isset($element['config_path']) ? $element['config_path'] : $structurePath; diff --git a/app/code/Magento/Config/Model/Config/Structure/AbstractElement.php b/app/code/Magento/Config/Model/Config/Structure/AbstractElement.php index 23a3dea1a7029..c4a0cb5e886d9 100644 --- a/app/code/Magento/Config/Model/Config/Structure/AbstractElement.php +++ b/app/code/Magento/Config/Model/Config/Structure/AbstractElement.php @@ -103,7 +103,7 @@ public function getData() */ public function getId() { - return isset($this->_data['id']) ? $this->_data['id'] : ''; + return $this->_data['id'] ?? ''; } /** @@ -133,7 +133,7 @@ public function getComment() */ public function getFrontendModel() { - return isset($this->_data['frontend_model']) ? $this->_data['frontend_model'] : ''; + return $this->_data['frontend_model'] ?? ''; } /** @@ -194,7 +194,7 @@ protected function _hasVisibilityValue($key) */ public function getClass() { - return isset($this->_data['class']) ? $this->_data['class'] : ''; + return $this->_data['class'] ?? ''; } /** diff --git a/app/code/Magento/Config/Model/Config/Structure/Element/Field.php b/app/code/Magento/Config/Model/Config/Structure/Element/Field.php index 834b2a9e17e37..2a4397b3300c0 100644 --- a/app/code/Magento/Config/Model/Config/Structure/Element/Field.php +++ b/app/code/Magento/Config/Model/Config/Structure/Element/Field.php @@ -150,7 +150,7 @@ public function getTooltip() */ public function getType() { - return isset($this->_data['type']) ? $this->_data['type'] : 'text'; + return $this->_data['type'] ?? 'text'; } /** @@ -204,7 +204,7 @@ public function getRequiredFields($fieldPrefix = '') */ public function getFrontendClass() { - return isset($this->_data['frontend_class']) ? $this->_data['frontend_class'] : ''; + return $this->_data['frontend_class'] ?? ''; } /** @@ -256,7 +256,7 @@ public function getGroupPath() */ public function getConfigPath() { - return isset($this->_data['config_path']) ? $this->_data['config_path'] : null; + return $this->_data['config_path'] ?? null; } /** @@ -334,7 +334,7 @@ public function hasValidation() */ public function getValidation() { - return isset($this->_data['validate']) ? $this->_data['validate'] : null; + return $this->_data['validate'] ?? null; } /** diff --git a/app/code/Magento/Config/Model/Config/Structure/Element/Section.php b/app/code/Magento/Config/Model/Config/Structure/Element/Section.php index 80c029dfea2d0..15f590dfd2ffa 100644 --- a/app/code/Magento/Config/Model/Config/Structure/Element/Section.php +++ b/app/code/Magento/Config/Model/Config/Structure/Element/Section.php @@ -6,7 +6,7 @@ namespace Magento\Config\Model\Config\Structure\Element; /** - * Section + * Element section * * @api * @since 100.0.2 @@ -43,7 +43,7 @@ public function __construct( */ public function getHeaderCss() { - return isset($this->_data['header_css']) ? $this->_data['header_css'] : ''; + return $this->_data['header_css'] ?? ''; } /** diff --git a/app/code/Magento/Config/Test/Mftf/ActionGroup/AdminCheckUseSystemValueActionGroup.xml b/app/code/Magento/Config/Test/Mftf/ActionGroup/AdminCheckUseSystemValueActionGroup.xml new file mode 100644 index 0000000000000..10c36ced4cee3 --- /dev/null +++ b/app/code/Magento/Config/Test/Mftf/ActionGroup/AdminCheckUseSystemValueActionGroup.xml @@ -0,0 +1,18 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + /** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +--> + +<actionGroups xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/actionGroupSchema.xsd"> + <actionGroup name="AdminCheckUseSystemValueActionGroup"> + <arguments> + <argument name="rowId" type="string"/> + </arguments> + + <checkOption selector="{{AdminConfigSection.useSystemValue(rowId)}}" stepKey="checkUseSystemValue"/> + </actionGroup> +</actionGroups> diff --git a/app/code/Magento/Config/Test/Mftf/ActionGroup/AdminToggleEnabledActionGroup.xml b/app/code/Magento/Config/Test/Mftf/ActionGroup/AdminToggleEnabledActionGroup.xml new file mode 100644 index 0000000000000..1fe36cf9ae390 --- /dev/null +++ b/app/code/Magento/Config/Test/Mftf/ActionGroup/AdminToggleEnabledActionGroup.xml @@ -0,0 +1,18 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + /** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +--> + +<actionGroups xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/actionGroupSchema.xsd"> + <actionGroup name="AdminToggleEnabledActionGroup"> + <arguments> + <argument name="element" type="string"/> + <argument name="state" type="string" defaultValue="Yes"/> + </arguments> + <selectOption selector="{{element}}" userInput="{{state}}" stepKey="switchActiveState"/> + </actionGroup> +</actionGroups> diff --git a/app/code/Magento/Config/Test/Mftf/ActionGroup/AdminUncheckUseSystemValueActionGroup.xml b/app/code/Magento/Config/Test/Mftf/ActionGroup/AdminUncheckUseSystemValueActionGroup.xml new file mode 100644 index 0000000000000..25ecd6fe09a27 --- /dev/null +++ b/app/code/Magento/Config/Test/Mftf/ActionGroup/AdminUncheckUseSystemValueActionGroup.xml @@ -0,0 +1,18 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + /** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +--> + +<actionGroups xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/actionGroupSchema.xsd"> + <actionGroup name="AdminUncheckUseSystemValueActionGroup"> + <arguments> + <argument name="rowId" type="string"/> + </arguments> + + <uncheckOption selector="{{AdminConfigSection.useSystemValue(rowId)}}" stepKey="uncheckUseSystemValue"/> + </actionGroup> +</actionGroups> diff --git a/app/code/Magento/Config/Test/Mftf/ActionGroup/AssertAdminValidationErrorActionGroup.xml b/app/code/Magento/Config/Test/Mftf/ActionGroup/AssertAdminValidationErrorActionGroup.xml new file mode 100644 index 0000000000000..768eefd26a4f9 --- /dev/null +++ b/app/code/Magento/Config/Test/Mftf/ActionGroup/AssertAdminValidationErrorActionGroup.xml @@ -0,0 +1,20 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + /** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +--> + +<actionGroups xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/actionGroupSchema.xsd"> + <actionGroup name="AssertAdminValidationErrorActionGroup"> + <arguments> + <argument name="inputId" type="string"/> + <argument name="errorMessage" type="string" defaultValue="This is a required field."/> + </arguments> + + <see selector="{{AdminConfigSection.errorElement(inputId)}}" userInput="{{errorMessage}}" + stepKey="seeElementValidationError"/> + </actionGroup> +</actionGroups> diff --git a/app/code/Magento/Config/Test/Mftf/ActionGroup/ConfigAdminAccountSharingActionGroup.xml b/app/code/Magento/Config/Test/Mftf/ActionGroup/ConfigAdminAccountSharingActionGroup.xml index 4e11dd6e971de..976f64a457074 100644 --- a/app/code/Magento/Config/Test/Mftf/ActionGroup/ConfigAdminAccountSharingActionGroup.xml +++ b/app/code/Magento/Config/Test/Mftf/ActionGroup/ConfigAdminAccountSharingActionGroup.xml @@ -13,7 +13,7 @@ <description>Goes to the 'Configuration' page for 'Admin'. Enables 'Admin Account Sharing'. Clicks on the Save button.</description> </annotations> - <amOnPage url="{{_ENV.MAGENTO_BACKEND_NAME}}/admin/system_config/edit/section/admin/" stepKey="navigateToConfigurationPage"/> + <amOnPage url="{{AdminConfigAdvancedAdmin.url}}" stepKey="navigateToConfigurationPage"/> <waitForPageLoad stepKey="wait1"/> <conditionalClick stepKey="expandSecurityTab" selector="{{AdminSection.SecurityTab}}" dependentSelector="{{AdminSection.CheckIfTabExpand}}" visible="true"/> <waitForElementVisible selector="{{AdminSection.AdminAccountSharing}}" stepKey="waitForAdminAccountSharingDrpDown"/> diff --git a/app/code/Magento/Config/Test/Mftf/Metadata/allow_guest_checkout-meta.xml b/app/code/Magento/Config/Test/Mftf/Metadata/AllowGuestCheckoutMeta.xml similarity index 100% rename from app/code/Magento/Config/Test/Mftf/Metadata/allow_guest_checkout-meta.xml rename to app/code/Magento/Config/Test/Mftf/Metadata/AllowGuestCheckoutMeta.xml diff --git a/app/code/Magento/Config/Test/Mftf/Metadata/locale_options_config-meta.xml b/app/code/Magento/Config/Test/Mftf/Metadata/LocaleOptionsConfigMeta.xml similarity index 100% rename from app/code/Magento/Config/Test/Mftf/Metadata/locale_options_config-meta.xml rename to app/code/Magento/Config/Test/Mftf/Metadata/LocaleOptionsConfigMeta.xml diff --git a/app/code/Magento/Config/Test/Mftf/Metadata/system_config-countries-meta.xml b/app/code/Magento/Config/Test/Mftf/Metadata/SystemConfigCountriesMeta.xml similarity index 100% rename from app/code/Magento/Config/Test/Mftf/Metadata/system_config-countries-meta.xml rename to app/code/Magento/Config/Test/Mftf/Metadata/SystemConfigCountriesMeta.xml diff --git a/app/code/Magento/Config/Test/Mftf/Metadata/system_config-meta.xml b/app/code/Magento/Config/Test/Mftf/Metadata/SystemConfigMeta.xml similarity index 100% rename from app/code/Magento/Config/Test/Mftf/Metadata/system_config-meta.xml rename to app/code/Magento/Config/Test/Mftf/Metadata/SystemConfigMeta.xml diff --git a/app/code/Magento/Config/Test/Mftf/Metadata/web_url_options_config-meta.xml b/app/code/Magento/Config/Test/Mftf/Metadata/WebUrlOptionsConfigMeta.xml similarity index 100% rename from app/code/Magento/Config/Test/Mftf/Metadata/web_url_options_config-meta.xml rename to app/code/Magento/Config/Test/Mftf/Metadata/WebUrlOptionsConfigMeta.xml diff --git a/app/code/Magento/Config/Test/Mftf/Page/AdminConfigPage.xml b/app/code/Magento/Config/Test/Mftf/Page/AdminConfigPage.xml deleted file mode 100644 index a1ee5348d094c..0000000000000 --- a/app/code/Magento/Config/Test/Mftf/Page/AdminConfigPage.xml +++ /dev/null @@ -1,30 +0,0 @@ -<?xml version="1.0" encoding="utf-8"?> -<!-- - /** - * Copyright © Magento, Inc. All rights reserved. - * See COPYING.txt for license details. - */ ---> -<pages xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="urn:magento:mftf:Page/etc/PageObject.xsd"> - <page name="AdminConfigPage" url="admin/system_config/" area="admin" module="Magento_Config"> - <section name="AdminConfigSection"/> - </page> - <page name="AdminContentManagementPage" url="admin/system_config/edit/section/cms/" area="admin" module="Magento_Config"> - <section name="ContentManagementSection"/> - </page> - <page name="CatalogConfigPage" url="admin/system_config/edit/section/catalog/" area="admin" module="Magento_Config"> - <section name="ContentManagementSection"/> - </page> - <page name="AdminSalesTaxClassPage" url="admin/system_config/edit/section/tax/" area="admin" module="Magento_Config"> - <section name="SalesTaxClassSection"/> - </page> - <page name="AdminConfigGeneralPage" url="admin/system_config/edit/section/general/" area="admin" module="Magento_Config"> - <section name="GeneralSection"/> - </page> - <page name="AdminConfigDeveloperPage" url="admin/system_config/edit/section/dev/" area="admin" module="Magento_Config"> - <section name="AdminConfigSection" /> - </page> - <page name="AdminConfigAdvancedAdmin" url="admin/system_config/edit/section/admin/" area="admin" module="Magento_Config"> - <section name="AdvanceAdminSection"/> - </page> -</pages> diff --git a/app/code/Magento/Config/Test/Mftf/Page/AdminConfigPage/AdminConfigAdvancedAdminPage.xml b/app/code/Magento/Config/Test/Mftf/Page/AdminConfigPage/AdminConfigAdvancedAdminPage.xml new file mode 100644 index 0000000000000..5d1b92898753b --- /dev/null +++ b/app/code/Magento/Config/Test/Mftf/Page/AdminConfigPage/AdminConfigAdvancedAdminPage.xml @@ -0,0 +1,13 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + /** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +--> +<pages xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Page/etc/PageObject.xsd"> + <page name="AdminConfigAdvancedAdmin" url="admin/system_config/edit/section/admin/" area="admin" module="Magento_Config"> + <section name="AdvanceAdminSection"/> + </page> +</pages> diff --git a/app/code/Magento/Config/Test/Mftf/Page/AdminConfigPage/AdminConfigDeveloperPage.xml b/app/code/Magento/Config/Test/Mftf/Page/AdminConfigPage/AdminConfigDeveloperPage.xml new file mode 100644 index 0000000000000..36370eba6becc --- /dev/null +++ b/app/code/Magento/Config/Test/Mftf/Page/AdminConfigPage/AdminConfigDeveloperPage.xml @@ -0,0 +1,13 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + /** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +--> +<pages xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Page/etc/PageObject.xsd"> + <page name="AdminConfigDeveloperPage" url="admin/system_config/edit/section/dev/" area="admin" module="Magento_Config"> + <section name="AdminConfigSection"/> + </page> +</pages> diff --git a/app/code/Magento/Config/Test/Mftf/Page/AdminConfigPage/AdminConfigGeneralPage.xml b/app/code/Magento/Config/Test/Mftf/Page/AdminConfigPage/AdminConfigGeneralPage.xml new file mode 100644 index 0000000000000..08b7519826b13 --- /dev/null +++ b/app/code/Magento/Config/Test/Mftf/Page/AdminConfigPage/AdminConfigGeneralPage.xml @@ -0,0 +1,13 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + /** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +--> +<pages xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Page/etc/PageObject.xsd"> + <page name="AdminConfigGeneralPage" url="admin/system_config/edit/section/general/" area="admin" module="Magento_Config"> + <section name="GeneralSection"/> + </page> +</pages> diff --git a/app/code/Magento/Config/Test/Mftf/Page/AdminConfigPage/AdminConfigPage.xml b/app/code/Magento/Config/Test/Mftf/Page/AdminConfigPage/AdminConfigPage.xml new file mode 100644 index 0000000000000..1c110681329df --- /dev/null +++ b/app/code/Magento/Config/Test/Mftf/Page/AdminConfigPage/AdminConfigPage.xml @@ -0,0 +1,13 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + /** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +--> +<pages xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Page/etc/PageObject.xsd"> + <page name="AdminConfigPage" url="admin/system_config/" area="admin" module="Magento_Config"> + <section name="AdminConfigSection"/> + </page> +</pages> diff --git a/app/code/Magento/Config/Test/Mftf/Page/AdminConfigPage/AdminContentManagementPage.xml b/app/code/Magento/Config/Test/Mftf/Page/AdminConfigPage/AdminContentManagementPage.xml new file mode 100644 index 0000000000000..8e367ecc3ae04 --- /dev/null +++ b/app/code/Magento/Config/Test/Mftf/Page/AdminConfigPage/AdminContentManagementPage.xml @@ -0,0 +1,13 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + /** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +--> +<pages xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Page/etc/PageObject.xsd"> + <page name="AdminContentManagementPage" url="admin/system_config/edit/section/cms/" area="admin" module="Magento_Config"> + <section name="ContentManagementSection"/> + </page> +</pages> diff --git a/app/code/Magento/Config/Test/Mftf/Page/AdminConfigPage/AdminSalesTaxClassPage.xml b/app/code/Magento/Config/Test/Mftf/Page/AdminConfigPage/AdminSalesTaxClassPage.xml new file mode 100644 index 0000000000000..74fcfbc500231 --- /dev/null +++ b/app/code/Magento/Config/Test/Mftf/Page/AdminConfigPage/AdminSalesTaxClassPage.xml @@ -0,0 +1,13 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + /** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +--> +<pages xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Page/etc/PageObject.xsd"> + <page name="AdminSalesTaxClassPage" url="admin/system_config/edit/section/tax/" area="admin" module="Magento_Config"> + <section name="SalesTaxClassSection"/> + </page> +</pages> diff --git a/app/code/Magento/Config/Test/Mftf/Page/AdminConfigPage/CatalogConfigPage.xml b/app/code/Magento/Config/Test/Mftf/Page/AdminConfigPage/CatalogConfigPage.xml new file mode 100644 index 0000000000000..96cb199b51355 --- /dev/null +++ b/app/code/Magento/Config/Test/Mftf/Page/AdminConfigPage/CatalogConfigPage.xml @@ -0,0 +1,13 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + /** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +--> +<pages xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Page/etc/PageObject.xsd"> + <page name="CatalogConfigPage" url="admin/system_config/edit/section/catalog/" area="admin" module="Magento_Config"> + <section name="ContentManagementSection"/> + </page> +</pages> diff --git a/app/code/Magento/Config/Test/Mftf/Section/AdminConfigSection.xml b/app/code/Magento/Config/Test/Mftf/Section/AdminConfigSection.xml index a4fb3c7e32975..de01de3c7f4a8 100644 --- a/app/code/Magento/Config/Test/Mftf/Section/AdminConfigSection.xml +++ b/app/code/Magento/Config/Test/Mftf/Section/AdminConfigSection.xml @@ -22,5 +22,6 @@ <element name="useSystemValue" type="checkbox" selector="#{{configRowId}} > .use-default > input" parameterized="true"/> <element name="collapsibleSectionByTitle" type="text" selector="//form[@id='config-edit-form']//div[@class='section-config'][contains(.,'{{sectionTitle}}')]" parameterized="true" /> <element name="expandedSectionByTitle" type="text" selector="//form[@id='config-edit-form']//div[@class='section-config active'][contains(.,'{{sectionTitle}}')]" parameterized="true" /> + <element name="errorElement" type="text" selector="#{{inputId}}-error" parameterized="true" /> </section> </sections> diff --git a/app/code/Magento/Config/Test/Mftf/Section/AdminPersistentShoppingCartSection.xml b/app/code/Magento/Config/Test/Mftf/Section/AdminPersistentShoppingCartSection.xml new file mode 100644 index 0000000000000..7dc0d16e39556 --- /dev/null +++ b/app/code/Magento/Config/Test/Mftf/Section/AdminPersistentShoppingCartSection.xml @@ -0,0 +1,20 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + /** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +--> + +<sections xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Page/etc/SectionObject.xsd"> + <section name="AdminPersistentShoppingCartSection"> + <element name="DefaultLayoutsTab" type="button" selector=".entry-edit-head-link"/> + <element name="CheckIfTabExpand" type="button" selector=".entry-edit-head-link:not(.open)"/> + <element name="persistenceLifeTime" type="input" selector="#persistent_options_lifetime"/> + <element name="rememberMeEnable" type="input" selector="#persistent_options_remember_enabled"/> + <element name="rememberMeDefault" type="input" selector="#persistent_options_remember_default"/> + <element name="clearPersistenceOnLogout" type="input" selector="#persistent_options_logout_clear"/> + <element name="persistShoppingCart" type="input" selector="#persistent_options_shopping_cart"/> + </section> +</sections> diff --git a/app/code/Magento/Config/Test/Mftf/Section/CatalogSection.xml b/app/code/Magento/Config/Test/Mftf/Section/CatalogSection/CatalogSection.xml similarity index 76% rename from app/code/Magento/Config/Test/Mftf/Section/CatalogSection.xml rename to app/code/Magento/Config/Test/Mftf/Section/CatalogSection/CatalogSection.xml index e024d9e5b2e47..851157c5d03c0 100644 --- a/app/code/Magento/Config/Test/Mftf/Section/CatalogSection.xml +++ b/app/code/Magento/Config/Test/Mftf/Section/CatalogSection/CatalogSection.xml @@ -5,9 +5,8 @@ * See COPYING.txt for license details. */ --> - <sections xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" - xsi:noNamespaceSchemaLocation="urn:magento:mftf:Page/etc/SectionObject.xsd"> + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Page/etc/SectionObject.xsd"> <section name="CatalogSection"> <element name="storefront" type="select" selector="#catalog_frontend-head"/> <element name="CheckIfTabExpand" type="button" selector="#catalog_frontend-head:not(.open)"/> @@ -25,10 +24,4 @@ <element name="GenerateUrlRewrites" type="select" selector="#catalog_seo_generate_category_product_rewrites"/> <element name="successMessage" type="text" selector="#messages"/> </section> - <section name="GenerateUrlRewritesConfirm"> - <element name="title" type="text" selector=".modal-popup.confirm h1.modal-title"/> - <element name="message" type="text" selector=".modal-popup.confirm div.modal-content"/> - <element name="cancel" type="button" selector=".modal-popup.confirm button.action-dismiss"/> - <element name="ok" type="button" selector=".modal-popup.confirm button.action-accept" timeout="60"/> - </section> </sections> diff --git a/app/code/Magento/Config/Test/Mftf/Section/CatalogSection/GenerateUrlRewritesConfirmSection.xml b/app/code/Magento/Config/Test/Mftf/Section/CatalogSection/GenerateUrlRewritesConfirmSection.xml new file mode 100644 index 0000000000000..504a0f0f2f561 --- /dev/null +++ b/app/code/Magento/Config/Test/Mftf/Section/CatalogSection/GenerateUrlRewritesConfirmSection.xml @@ -0,0 +1,16 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + /** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +--> +<sections xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Page/etc/SectionObject.xsd"> + <section name="GenerateUrlRewritesConfirm"> + <element name="title" type="text" selector=".modal-popup.confirm h1.modal-title"/> + <element name="message" type="text" selector=".modal-popup.confirm div.modal-content"/> + <element name="cancel" type="button" selector=".modal-popup.confirm button.action-dismiss"/> + <element name="ok" type="button" selector=".modal-popup.confirm button.action-accept" timeout="60"/> + </section> +</sections> diff --git a/app/code/Magento/Config/Test/Mftf/Section/GeneralSection.xml b/app/code/Magento/Config/Test/Mftf/Section/GeneralSection.xml deleted file mode 100644 index 9bce5065317f8..0000000000000 --- a/app/code/Magento/Config/Test/Mftf/Section/GeneralSection.xml +++ /dev/null @@ -1,47 +0,0 @@ -<?xml version="1.0" encoding="UTF-8"?> -<!-- - /** - * Copyright © Magento, Inc. All rights reserved. - * See COPYING.txt for license details. - */ ---> - -<sections xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" - xsi:noNamespaceSchemaLocation="urn:magento:mftf:Page/etc/SectionObject.xsd"> - <section name="ContentManagementSection"> - <element name="WYSIWYGOptions" type="button" selector="#cms_wysiwyg-head" timeout="60"/> - <element name="CheckIfTabExpand" type="button" selector="#cms_wysiwyg-head:not(.open)"/> - <element name="EnableSystemValue" type="button" selector="#cms_wysiwyg_enabled_inherit"/> - <element name="EnableWYSIWYG" type="button" selector="#cms_wysiwyg_enabled"/> - <element name="SwitcherSystemValue" type="button" selector="#cms_wysiwyg_editor_inherit" timeout="60"/> - <element name="Switcher" type="button" selector="#cms_wysiwyg_editor" /> - <element name="StaticURL" type="button" selector="#cms_wysiwyg_use_static_urls_in_catalog" /> - <element name="Save" type="button" selector="#save" timeout="30"/> - <element name="StoreConfigurationPageSuccessMessage" type="text" selector="#messages [data-ui-id='messages-message-success']"/> - </section> - <section name="WebSection"> - <element name="DefaultLayoutsTab" type="button" selector="#web_default_layouts-head"/> - <element name="CheckIfTabExpand" type="button" selector="#web_default_layouts-head:not(.open)"/> - <element name="UrlOptionsTab" type="button" selector="#web_url-head"/> - <element name="CheckIfUrlOptionsTabExpand" type="button" selector="#web_url-head:not(.open)"/> - </section> - <section name="DefaultLayoutsSection"> - <element name="productLayout" type="select" selector="#web_default_layouts_default_product_layout"/> - <element name="categoryLayout" type="select" selector="#web_default_layouts_default_category_layout"/> - <element name="pageLayout" type="select" selector="#web_default_layouts_default_cms_layout"/> - </section> - <section name="UrlOptionsSection"> - <element name="addStoreCodeToUrl" type="select" selector="#web_url_use_store"/> - <element name="systemValueForStoreCode" type="checkbox" selector="#web_url_use_store_inherit"/> - </section> - <section name="CountryOptionsSection"> - <element name="countryOptions" type="button" selector="#general_country-head"/> - <element name="countryOptionsOpen" type="button" selector="#general_country-head.open"/> - <element name="topDestinations" type="select" selector="#general_country_destinations"/> - </section> - <section name="StateOptionsSection"> - <element name="stateOptions" type="button" selector="#general_region-head"/> - <element name="countriesWithRequiredRegions" type="select" selector="#general_region_state_required"/> - <element name="allowToChooseState" type="select" selector="general_region_display_all"/> - </section> -</sections> diff --git a/app/code/Magento/Config/Test/Mftf/Section/GeneralSection/ContentManagementSection.xml b/app/code/Magento/Config/Test/Mftf/Section/GeneralSection/ContentManagementSection.xml new file mode 100644 index 0000000000000..80b2c7e0c4645 --- /dev/null +++ b/app/code/Magento/Config/Test/Mftf/Section/GeneralSection/ContentManagementSection.xml @@ -0,0 +1,21 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + /** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +--> +<sections xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Page/etc/SectionObject.xsd"> + <section name="ContentManagementSection"> + <element name="WYSIWYGOptions" type="button" selector="#cms_wysiwyg-head" timeout="60"/> + <element name="CheckIfTabExpand" type="button" selector="#cms_wysiwyg-head:not(.open)"/> + <element name="EnableSystemValue" type="button" selector="#cms_wysiwyg_enabled_inherit"/> + <element name="EnableWYSIWYG" type="button" selector="#cms_wysiwyg_enabled"/> + <element name="SwitcherSystemValue" type="button" selector="#cms_wysiwyg_editor_inherit" timeout="60"/> + <element name="Switcher" type="button" selector="#cms_wysiwyg_editor"/> + <element name="StaticURL" type="button" selector="#cms_wysiwyg_use_static_urls_in_catalog"/> + <element name="Save" type="button" selector="#save" timeout="30"/> + <element name="StoreConfigurationPageSuccessMessage" type="text" selector="#messages [data-ui-id='messages-message-success']"/> + </section> +</sections> diff --git a/app/code/Magento/Config/Test/Mftf/Section/GeneralSection/CountryOptionsSection.xml b/app/code/Magento/Config/Test/Mftf/Section/GeneralSection/CountryOptionsSection.xml new file mode 100644 index 0000000000000..77d5af706247e --- /dev/null +++ b/app/code/Magento/Config/Test/Mftf/Section/GeneralSection/CountryOptionsSection.xml @@ -0,0 +1,15 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + /** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +--> +<sections xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Page/etc/SectionObject.xsd"> + <section name="CountryOptionsSection"> + <element name="countryOptions" type="button" selector="#general_country-head"/> + <element name="countryOptionsOpen" type="button" selector="#general_country-head.open"/> + <element name="topDestinations" type="select" selector="#general_country_destinations"/> + </section> +</sections> diff --git a/app/code/Magento/Config/Test/Mftf/Section/GeneralSection/DefaultLayoutsSection.xml b/app/code/Magento/Config/Test/Mftf/Section/GeneralSection/DefaultLayoutsSection.xml new file mode 100644 index 0000000000000..25ec63e1354f1 --- /dev/null +++ b/app/code/Magento/Config/Test/Mftf/Section/GeneralSection/DefaultLayoutsSection.xml @@ -0,0 +1,15 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + /** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +--> +<sections xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Page/etc/SectionObject.xsd"> + <section name="DefaultLayoutsSection"> + <element name="productLayout" type="select" selector="#web_default_layouts_default_product_layout"/> + <element name="categoryLayout" type="select" selector="#web_default_layouts_default_category_layout"/> + <element name="pageLayout" type="select" selector="#web_default_layouts_default_cms_layout"/> + </section> +</sections> diff --git a/app/code/Magento/Config/Test/Mftf/Section/GeneralSection/StateOptionsSection.xml b/app/code/Magento/Config/Test/Mftf/Section/GeneralSection/StateOptionsSection.xml new file mode 100644 index 0000000000000..99a76a446aaa4 --- /dev/null +++ b/app/code/Magento/Config/Test/Mftf/Section/GeneralSection/StateOptionsSection.xml @@ -0,0 +1,15 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + /** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +--> +<sections xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Page/etc/SectionObject.xsd"> + <section name="StateOptionsSection"> + <element name="stateOptions" type="button" selector="#general_region-head"/> + <element name="countriesWithRequiredRegions" type="select" selector="#general_region_state_required"/> + <element name="allowToChooseState" type="select" selector="general_region_display_all"/> + </section> +</sections> diff --git a/app/code/Magento/Config/Test/Mftf/Section/GeneralSection/UrlOptionsSection.xml b/app/code/Magento/Config/Test/Mftf/Section/GeneralSection/UrlOptionsSection.xml new file mode 100644 index 0000000000000..9aebec8123b57 --- /dev/null +++ b/app/code/Magento/Config/Test/Mftf/Section/GeneralSection/UrlOptionsSection.xml @@ -0,0 +1,14 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + /** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +--> +<sections xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Page/etc/SectionObject.xsd"> + <section name="UrlOptionsSection"> + <element name="addStoreCodeToUrl" type="select" selector="#web_url_use_store"/> + <element name="systemValueForStoreCode" type="checkbox" selector="#web_url_use_store_inherit"/> + </section> +</sections> diff --git a/app/code/Magento/Config/Test/Mftf/Section/GeneralSection/WebSection.xml b/app/code/Magento/Config/Test/Mftf/Section/GeneralSection/WebSection.xml new file mode 100644 index 0000000000000..56ea1f844d002 --- /dev/null +++ b/app/code/Magento/Config/Test/Mftf/Section/GeneralSection/WebSection.xml @@ -0,0 +1,16 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + /** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +--> +<sections xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Page/etc/SectionObject.xsd"> + <section name="WebSection"> + <element name="DefaultLayoutsTab" type="button" selector="#web_default_layouts-head"/> + <element name="CheckIfTabExpand" type="button" selector="#web_default_layouts-head:not(.open)"/> + <element name="UrlOptionsTab" type="button" selector="#web_url-head"/> + <element name="CheckIfUrlOptionsTabExpand" type="button" selector="#web_url-head:not(.open)"/> + </section> +</sections> diff --git a/app/code/Magento/Config/Test/Mftf/Test/CheckingCountryDropDownWithOneAllowedCountryTest.xml b/app/code/Magento/Config/Test/Mftf/Test/CheckingCountryDropDownWithOneAllowedCountryTest.xml index 1d54f2ef52c02..5327979154389 100644 --- a/app/code/Magento/Config/Test/Mftf/Test/CheckingCountryDropDownWithOneAllowedCountryTest.xml +++ b/app/code/Magento/Config/Test/Mftf/Test/CheckingCountryDropDownWithOneAllowedCountryTest.xml @@ -24,19 +24,25 @@ <after> <magentoCLI stepKey="flushCache" command="cache:flush"/> <createData entity="DisableAdminAccountAllowCountry" stepKey="setDefaultValueForAllowCountries"/> - <actionGroup ref="LoginAsAdmin" stepKey="loginAsAdmin"/> + <actionGroup ref="AdminLoginActionGroup" stepKey="loginAsAdmin"/> <actionGroup ref="AdminDeleteCustomerActionGroup" stepKey="deleteCustomer"> <argument name="customerEmail" value="CustomerEntityOne.email"/> </actionGroup> <actionGroup ref="AdminClearCustomersFiltersActionGroup" stepKey="clearFilters"/> <waitForPageLoad stepKey="WaitForPageToLoad"/> - <actionGroup ref="logout" stepKey="logout"/> + <actionGroup ref="AdminLogoutActionGroup" stepKey="logout"/> </after> <!--Flush Magento Cache--> <magentoCLI stepKey="flushCache" command="cache:flush"/> <!--Create a customer account from Storefront--> - <actionGroup ref="SignUpNewUserFromStorefrontActionGroup" stepKey="createAnAccount"> - <argument name="Customer" value="CustomerEntityOne"/> + <actionGroup ref="StorefrontOpenCustomerAccountCreatePageActionGroup" stepKey="openCreateAccountPage"/> + <actionGroup ref="StorefrontFillCustomerAccountCreationFormActionGroup" stepKey="fillCreateAccountForm"> + <argument name="customer" value="CustomerEntityOne"/> + </actionGroup> + <actionGroup ref="StorefrontClickCreateAnAccountCustomerAccountCreationFormActionGroup" stepKey="submitCreateAccountForm"/> + <actionGroup ref="AssertMessageCustomerCreateAccountActionGroup" stepKey="seeSuccessMessage"> + <argument name="messageType" value="success"/> + <argument name="message" value="Thank you for registering with Main Website Store."/> </actionGroup> <click selector="{{CheckoutPaymentSection.addressBook}}" stepKey="goToAddressBook"/> <click selector="{{StorefrontCustomerAddressSection.country}}" stepKey="clickToExpandCountryDropDown"/> diff --git a/app/code/Magento/Config/Test/Mftf/Test/ConfigurationTest.xml b/app/code/Magento/Config/Test/Mftf/Test/ConfigurationTest.xml index 66aacf706b039..d4fcbb06ea2d3 100644 --- a/app/code/Magento/Config/Test/Mftf/Test/ConfigurationTest.xml +++ b/app/code/Magento/Config/Test/Mftf/Test/ConfigurationTest.xml @@ -8,7 +8,7 @@ <tests xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/testSchema.xsd"> - <test name="VerifyAllowDynamicMediaURLsSettingIsRemoved"> + <test name="VerifyAllowDynamicMediaURLsSettingIsRemovedTest"> <annotations> <features value="Backend"/> <stories value="Dynamic Media URL"/> @@ -20,10 +20,10 @@ <group value="configuration"/> </annotations> <before> - <actionGroup ref="LoginAsAdmin" stepKey="loginAsAdmin"/> + <actionGroup ref="AdminLoginActionGroup" stepKey="loginAsAdmin"/> </before> <after> - <actionGroup ref="logout" stepKey="logout"/> + <actionGroup ref="AdminLogoutActionGroup" stepKey="logout"/> </after> <amOnPage url="{{CatalogConfigPage.url}}" stepKey="navigateToConfigurationPage" /> <waitForPageLoad stepKey="waitForPageLoad"/> diff --git a/app/code/Magento/Config/Test/Unit/Block/System/Config/Form/FieldTest.php b/app/code/Magento/Config/Test/Unit/Block/System/Config/Form/FieldTest.php index 6be1fe04b68dd..d942af9352e6c 100644 --- a/app/code/Magento/Config/Test/Unit/Block/System/Config/Form/FieldTest.php +++ b/app/code/Magento/Config/Test/Unit/Block/System/Config/Form/FieldTest.php @@ -7,8 +7,6 @@ /** * Test how class render field html element in Stores Configuration - * - * @package Magento\Config\Test\Unit\Block\System\Config\Form */ class FieldTest extends \PHPUnit\Framework\TestCase { @@ -72,6 +70,7 @@ protected function setUp() 'getCanUseDefaultValue', 'setDisabled', 'getTooltip', + 'setReadonly' ] ); @@ -179,7 +178,8 @@ public function testRenderInheritCheckbox() $this->_elementMock->expects($this->any())->method('getCanUseWebsiteValue')->will($this->returnValue(true)); $this->_elementMock->expects($this->any())->method('getCanUseDefaultValue')->will($this->returnValue(true)); $this->_elementMock->expects($this->once())->method('setDisabled')->with(true); - $this->_elementMock->expects($this->once())->method('getIsDisableInheritance')->willReturn(true); + $this->_elementMock->method('getIsDisableInheritance')->willReturn(true); + $this->_elementMock->method('setReadonly')->with(true); $expected = '<td class="use-default">'; $expected .= '<input id="' . @@ -187,7 +187,7 @@ public function testRenderInheritCheckbox() '_inherit" name="' . $this->_testData['name'] . '[inherit]" type="checkbox" value="1"' . - ' class="checkbox config-inherit" checked="checked"' . ' disabled="disabled"' . + ' class="checkbox config-inherit" checked="checked"' . ' disabled="disabled"' . ' readonly="1"' . ' onclick="toggleValueElements(this, Element.previous(this.parentNode))" /> '; $expected .= '<label for="' . $this->_testData['htmlId'] . '_inherit" class="inherit">Use Website</label>'; diff --git a/app/code/Magento/Config/Test/Unit/Model/Config/Importer/SaveProcessorTest.php b/app/code/Magento/Config/Test/Unit/Model/Config/Importer/SaveProcessorTest.php index aec3a6f64fec0..39a0e14f3e91c 100644 --- a/app/code/Magento/Config/Test/Unit/Model/Config/Importer/SaveProcessorTest.php +++ b/app/code/Magento/Config/Test/Unit/Model/Config/Importer/SaveProcessorTest.php @@ -136,4 +136,37 @@ public function testProcess() $this->assertSame(null, $this->model->process($data)); } + + public function testProcessWithNullValues() + { + $data = [ + 'default' => [ + 'advanced' => ['modules_disable_output' => ['Test_Module' => '1']] + ], + 'websites' => ['test_website' => ['general' => ['locale' => ['timezone' => 'America/Rio_Branco']]]], + ]; + $this->arrayUtilsMock->expects($this->exactly(2)) + ->method('flatten') + ->willReturnMap([ + [ + [ + 'advanced' => ['modules_disable_output' => ['Test_Module' => '1']] + ], + '', + '/', + ['advanced/modules_disable_output/Test_Module' => '1'] + ], + [ + ['general' => ['locale' => ['timezone' => 'America/Rio_Branco']]], + '', + '/', + ['general/locale/timezone' => 'America/Rio_Branco'] + ] + ]); + $this->scopeConfigMock->expects($this->exactly(2)) + ->method('getValue') + ->willReturn(null); + + $this->assertSame(null, $this->model->process($data)); + } } diff --git a/app/code/Magento/Config/Test/Unit/Model/Config/StructureTest.php b/app/code/Magento/Config/Test/Unit/Model/Config/StructureTest.php index a17faf8f35883..57e57fcd2503f 100644 --- a/app/code/Magento/Config/Test/Unit/Model/Config/StructureTest.php +++ b/app/code/Magento/Config/Test/Unit/Model/Config/StructureTest.php @@ -50,6 +50,9 @@ class StructureTest extends \PHPUnit\Framework\TestCase */ protected $_structureData; + /** + * @inheritdoc + */ protected function setUp() { $this->_flyweightFactory = $this->getMockBuilder(FlyweightFactory::class) @@ -82,7 +85,12 @@ protected function setUp() ); } - public function testGetTabsBuildsSectionTree() + /** + * Verify tabs build section tree + * + * @return void + */ + public function testGetTabsBuildsSectionTree(): void { $expected = ['tab1' => ['children' => ['section1' => ['tab' => 'tab1']]]]; @@ -108,7 +116,12 @@ public function testGetTabsBuildsSectionTree() $this->assertEquals($this->_tabIteratorMock, $model->getTabs()); } - public function testGetSectionList() + /** + * Verify get section list method + * + * @return void + */ + public function testGetSectionList(): void { $expected = [ 'section1_child_id_1' => true, @@ -152,6 +165,8 @@ public function testGetSectionList() } /** + * Verify Get Element return empty element if element is requested + * * @param string $path * @param string $expectedType * @param string $expectedId @@ -174,6 +189,8 @@ public function testGetElementReturnsEmptyElementIfNotExistingElementIsRequested } /** + * Verify get Element return empty by path element if not exist + * * @param string $path * @param string $expectedType * @param string $expectedId @@ -196,6 +213,8 @@ public function testGetElementReturnsEmptyByConfigPathElementIfNotExistingElemen } /** + * Verify Element return e,pty element if not exists + * * @param string $expectedType * @param string $expectedId * @param string $expectedPath @@ -234,14 +253,24 @@ public function emptyElementDataProvider() ]; } - public function testGetElementReturnsProperElementByPath() + /** + * Verify get element returns proper element by path + * + * @return void + */ + public function testGetElementReturnsProperElementByPath(): void { $elementMock = $this->getElementPathReturnsProperElementByPath(); $this->assertEquals($elementMock, $this->_model->getElement('section_1/group_level_1/field_3')); } - public function testGetElementByConfigPathReturnsProperElementByPath() + /** + * Verify get element by config path return proper path + * + * @return void + */ + public function testGetElementByConfigPathReturnsProperElementByPath(): void { $elementMock = $this->getElementPathReturnsProperElementByPath(); @@ -249,6 +278,8 @@ public function testGetElementByConfigPathReturnsProperElementByPath() } /** + * Build mock element + * * @return Mock */ private function getElementPathReturnsProperElementByPath() @@ -271,7 +302,12 @@ private function getElementPathReturnsProperElementByPath() return $elementMock; } - public function testGetElementByPathPartsIfSectionDataIsEmpty() + /** + * Verefy get element by path part + * + * @return void + */ + public function testGetElementByPathPartsIfSectionDataIsEmpty(): void { $fieldData = [ 'id' => 'field_3', @@ -342,7 +378,12 @@ public function testGetFirstSectionReturnsFirstAllowedSection() $this->assertEquals('currentSection', $this->_model->getFirstSection()->getData()); } - public function testGetElementReturnsProperElementByPathCachesObject() + /** + * Verify get element return element by path caches object + * + * @return void + */ + public function testGetElementReturnsProperElementByPathCachesObject(): void { $elementMock = $this->getElementReturnsProperElementByPathCachesObject(); @@ -350,7 +391,12 @@ public function testGetElementReturnsProperElementByPathCachesObject() $this->assertEquals($elementMock, $this->_model->getElement('section_1/group_level_1/field_3')); } - public function testGetElementByConfigPathReturnsProperElementByPathCachesObject() + /** + * Verify Get Element by id returns proper element + * + * @return void + */ + public function testGetElementByConfigPathReturnsProperElementByPathCachesObject(): void { $elementMock = $this->getElementReturnsProperElementByPathCachesObject(); @@ -393,6 +439,8 @@ public function testGetFieldPathsByAttribute($attributeName, $attributeValue, $p } /** + * DataProvider + * * @return array */ public function getFieldPathsByAttributeDataProvider() @@ -411,33 +459,53 @@ public function getFieldPathsByAttributeDataProvider() ]; } - public function testGetFieldPaths() + /** + * Verify get Fields paths method + * + * @dataProvider getFieldPaths + * @param array $expected + * @return void + */ + public function testGetFieldPaths(array $expected): void { - $expected = [ - 'section/group/field2' => [ - 'field_2' - ], - 'field_3' => [ - 'field_3', - 'field_3' - ], - 'field_3_1' => [ - 'field_3_1' - ], - 'field_3_1_1' => [ - 'field_3_1_1' - ], - 'section/group/field4' => [ - 'field_4', - ], - 'field_5' => [ - 'field_5', - ], - ]; - $this->assertSame( $expected, $this->_model->getFieldPaths() ); } + + /** + * dataprovider for Field Paths + * + * @return array + */ + public function getFieldPaths(): array + { + return [ + [ + [ + 'section/group/field2' => [ + 'field_2' + ], + 'field_3' => [ + 'field_3', + 'field_3' + ], + 'field_3_1' => [ + 'field_3_1' + ], + 'field_3_1_1' => [ + 'field_3_1_1' + ], + 'section/group/field4' => [ + 'field_4', + ], + 'field_5' => [ + 'field_5', + 'field_5' + ] + ] + ] + ]; + } } diff --git a/app/code/Magento/Config/Test/Unit/Model/_files/converted_config.php b/app/code/Magento/Config/Test/Unit/Model/_files/converted_config.php index ef2447338dc07..ed6cc868f8d08 100644 --- a/app/code/Magento/Config/Test/Unit/Model/_files/converted_config.php +++ b/app/code/Magento/Config/Test/Unit/Model/_files/converted_config.php @@ -158,14 +158,14 @@ ], '_elementType' => 'field', ], - 'field_5' => [ + 'field_5' => [ 'id' => 'field_5', 'translate' => 'label', 'showInWebsite' => '1', 'type' => 'text', 'label' => '', '_elementType' => 'field', - ], + ], ], '_elementType' => 'group', ], @@ -190,6 +190,29 @@ ], '_elementType' => 'section', ], + 'section_3' => [ + 'id' => 'section_3', + 'type' => 'text', + '_elementType' => 'section', + 'children' => [ + 'group_5' => [ + 'id' => 'group_5', + 'type' => 'text', + 'showInDefault' => 1, + 'showInWebsite' => 1, + 'showInStore' => 1, + '_elementType' => 'group', + 'children' => [ + 'field_5' => [ + 'id' => 'field_5', + 'showInWebsite' => '1', + 'type' => 'text', + '_elementType' => 'field', + ] + ] + ] + ] + ] ], ], ], diff --git a/app/code/Magento/Config/Test/Unit/Model/_files/system_2.xml b/app/code/Magento/Config/Test/Unit/Model/_files/system_2.xml index c4001f47ced0b..715d7cf4e8a61 100644 --- a/app/code/Magento/Config/Test/Unit/Model/_files/system_2.xml +++ b/app/code/Magento/Config/Test/Unit/Model/_files/system_2.xml @@ -86,5 +86,11 @@ </depends> </group> </section> + <section id="section_3" type="text"> + <group id="group_5" type="text" showInDefault="1" showInWebsite="1" showInStore="1"> + <field id="field_5" showInWebsite="1" type="text"> + </field> + </group> + </section> </system> </config> diff --git a/app/code/Magento/Config/registration.php b/app/code/Magento/Config/registration.php index 8a57e9d5c2217..a6dd84b3a23ef 100644 --- a/app/code/Magento/Config/registration.php +++ b/app/code/Magento/Config/registration.php @@ -4,6 +4,6 @@ * See COPYING.txt for license details. */ -use \Magento\Framework\Component\ComponentRegistrar; +use Magento\Framework\Component\ComponentRegistrar; ComponentRegistrar::register(ComponentRegistrar::MODULE, 'Magento_Config', __DIR__); diff --git a/app/code/Magento/ConfigurableImportExport/Model/Import/Product/Type/Configurable.php b/app/code/Magento/ConfigurableImportExport/Model/Import/Product/Type/Configurable.php index 2767e725cc9b0..88007d0b1a880 100644 --- a/app/code/Magento/ConfigurableImportExport/Model/Import/Product/Type/Configurable.php +++ b/app/code/Magento/ConfigurableImportExport/Model/Import/Product/Type/Configurable.php @@ -573,7 +573,7 @@ protected function _parseVariations($rowData) $fieldAndValuePairs = []; foreach ($fieldAndValuePairsText as $nameAndValue) { - $nameAndValue = explode(ImportProduct::PAIR_NAME_VALUE_SEPARATOR, $nameAndValue); + $nameAndValue = explode(ImportProduct::PAIR_NAME_VALUE_SEPARATOR, $nameAndValue, 2); if (!empty($nameAndValue)) { $value = isset($nameAndValue[1]) ? trim($nameAndValue[1]) : ''; // Ignoring field names' case. diff --git a/app/code/Magento/ConfigurableImportExport/Test/Unit/Model/Import/Product/Type/ConfigurableTest.php b/app/code/Magento/ConfigurableImportExport/Test/Unit/Model/Import/Product/Type/ConfigurableTest.php index 8d75fd902e911..85b8dc5ec1d04 100644 --- a/app/code/Magento/ConfigurableImportExport/Test/Unit/Model/Import/Product/Type/ConfigurableTest.php +++ b/app/code/Magento/ConfigurableImportExport/Test/Unit/Model/Import/Product/Type/ConfigurableTest.php @@ -9,8 +9,8 @@ use Magento\ConfigurableImportExport; /** - * Class ConfigurableTest - * @package Magento\ConfigurableImportExport\Test\Unit\Model\Import\Product\Type + * Configurable import export tests + * * @SuppressWarnings(PHPMD.CouplingBetweenObjects) */ class ConfigurableTest extends \Magento\ImportExport\Test\Unit\Model\Import\AbstractImportTestCase @@ -78,6 +78,8 @@ class ConfigurableTest extends \Magento\ImportExport\Test\Unit\Model\Import\Abst protected $productEntityLinkField = 'entity_id'; /** + * @inheritdoc + * * @SuppressWarnings(PHPMD.ExcessiveMethodLength) */ protected function setUp() @@ -270,10 +272,12 @@ protected function setUp() } /** + * Bunches data provider + * * @return array * @SuppressWarnings(PHPMD.ExcessiveMethodLength) */ - protected function _getBunch() + protected function _getBunch(): array { return [[ 'sku' => 'configurableskuI22', @@ -393,9 +397,11 @@ protected function _getBunch() } /** + * Super attributes data provider + * * @return array */ - protected function _getSuperAttributes() + protected function _getSuperAttributes(): array { return [ 'testattr2' => [ @@ -404,7 +410,6 @@ protected function _getSuperAttributes() 'attribute_code' => 'testattr2', 'is_global' => '1', 'is_visible' => '1', - 'is_static' => '0', 'is_required' => '0', 'is_unique' => '0', 'frontend_label' => 'testattr2', @@ -426,7 +431,6 @@ protected function _getSuperAttributes() 'attribute_code' => 'testattr3', 'is_global' => '1', 'is_visible' => '1', - 'is_static' => '0', 'is_required' => '0', 'is_unique' => '0', 'frontend_label' => 'testattr3', @@ -445,6 +449,8 @@ protected function _getSuperAttributes() } /** + * Verify save mtethod + * * @SuppressWarnings(PHPMD.ExcessiveMethodLength) */ public function testSaveData() @@ -527,6 +533,8 @@ public function testSaveData() } /** + * Callback for is row allowed to import + * * @param $rowData * @param $rowNum * @return bool @@ -540,75 +548,31 @@ public function isRowAllowedToImport($rowData, $rowNum) return true; } - public function testIsRowValid() + /** + * Verify is row valid method + * + * @dataProvider getProductDataIsValidRow + * @param array $productData + * @return void + */ + public function testIsRowValid(array $productData): void { $bunch = $this->_getBunch(); - $badProduct = [ - 'sku' => 'configurableskuI22BadPrice', - 'store_view_code' => null, - 'attribute_set_code' => 'Default', - 'product_type' => 'configurable', - 'name' => 'Configurable Product 21 BadPrice', - 'product_websites' => 'website_1', - 'configurable_variation_labels' => 'testattr2=Select Color, testattr3=Select Size', - 'configurable_variations' => 'sku=testconf2-attr2val1-testattr3v1,' - . 'testattr2=attr2val1_DOESNT_EXIST,' - . 'testattr3=testattr3v1,' - . 'display=1|sku=testconf2-attr2val1-testattr3v2,' - . 'testattr2=attr2val1,' - . 'testattr3=testattr3v2,' - . 'display=0', - '_store' => null, - '_attribute_set' => 'Default', - '_type' => 'configurable', - '_product_websites' => 'website_1', - ]; // Checking that variations' field names are case-insensitive with this // product. $caseInsensitiveSKU = 'configurableskuI22CaseInsensitive'; - $caseInsensitiveProduct = [ - 'sku' => $caseInsensitiveSKU, - 'store_view_code' => null, - 'attribute_set_code' => 'Default', - 'product_type' => 'configurable', - 'name' => 'Configurable Product 21', - 'product_websites' => 'website_1', - 'configurable_variation_labels' => 'testattr2=Select Color, testattr3=Select Size', - 'configurable_variations' => 'SKU=testconf2-attr2val1-testattr3v1,' - . 'testattr2=attr2val1,' - . 'testattr3=testattr3v1,' - . 'display=1|sku=testconf2-attr2val1-testattr3v2,' - . 'testattr2=attr2val1,' - . 'testattr3=testattr3v2,' - . 'display=0', - '_store' => null, - '_attribute_set' => 'Default', - '_type' => 'configurable', - '_product_websites' => 'website_1', - ]; - $bunch[] = $badProduct; - $bunch[] = $caseInsensitiveProduct; + $productData['caseInsencitiveProduct']['sku'] = $caseInsensitiveSKU; + $bunch[] = $productData['bad_product']; + $bunch[] = $productData['caseInsencitiveProduct']; // Set _attributes to avoid error in Magento\CatalogImportExport\Model\Import\Product\Type\AbstractType. $this->setPropertyValue($this->configurable, '_attributes', [ - $badProduct[\Magento\CatalogImportExport\Model\Import\Product::COL_ATTR_SET] => [], + $productData['bad_product'][\Magento\CatalogImportExport\Model\Import\Product::COL_ATTR_SET] => [], ]); // Avoiding errors about attributes not being super - $this->setPropertyValue( - $this->configurable, - '_superAttributes', - [ - 'testattr2' => ['options' => ['attr2val1' => 1]], - 'testattr3' => [ - 'options' => [ - 'testattr3v2' => 1, - 'testattr3v1' => 1, - ], - ], - ] - ); + $this->setPropertyValue($this->configurable, '_superAttributes', $productData['super_attributes']); foreach ($bunch as $rowData) { - $result = $this->configurable->isRowValid($rowData, 0, !isset($this->_oldSku[$rowData['sku']])); + $result = $this->configurable->isRowValid($rowData, 0, false); $this->assertNotNull($result); if ($rowData['sku'] === $caseInsensitiveSKU) { $this->assertTrue($result); @@ -616,7 +580,78 @@ public function testIsRowValid() } } - public function testRowValidationForNumericalSkus() + /** + * + * Data provider for isValidRows test. + * + * @return array + */ + public function getProductDataIsValidRow(): array + { + return [ + [ + [ + 'bad_product' => [ + 'sku' => 'configurableskuI22BadPrice', + 'store_view_code' => null, + 'attribute_set_code' => 'Default', + 'product_type' => 'configurable', + 'name' => 'Configurable Product 21 BadPrice', + 'product_websites' => 'website_1', + 'configurable_variation_labels' => 'testattr2=Select Color, testattr3=Select Size', + 'configurable_variations' => 'sku=testconf2-attr2val1-testattr3v1,' + . 'testattr2=attr2val1_DOESNT_EXIST,' + . 'testattr3=testattr3v1,' + . 'display=1|sku=testconf2-attr2val1-testattr3v2,' + . 'testattr2=attr2val1,' + . 'testattr3=testattr3v2,' + . 'display=0', + '_store' => null, + '_attribute_set' => 'Default', + '_type' => 'configurable', + '_product_websites' => 'website_1', + ], + 'caseInsencitiveProduct' => [ + 'sku' => '', + 'store_view_code' => null, + 'attribute_set_code' => 'Default', + 'product_type' => 'configurable', + 'name' => 'Configurable Product 21', + 'product_websites' => 'website_1', + 'configurable_variation_labels' => 'testattr2=Select Color, testattr3=Select Size', + 'configurable_variations' => 'SKU=testconf2-attr2val1-testattr3v1,' + . 'testattr2=attr2val1,' + . 'testattr3=testattr3v1=sx=sl,' + . 'display=1|sku=testconf2-attr2val1-testattr3v2,' + . 'testattr2=attr2val1,' + . 'testattr3=testattr3v2,' + . 'display=0', + '_store' => null, + '_attribute_set' => 'Default', + '_type' => 'configurable', + '_product_websites' => 'website_1', + ], + 'super_attributes' => + [ + 'testattr2' => ['options' => ['attr2val1' => 1]], + 'testattr3' => [ + 'options' => [ + 'testattr3v2' => 1, + 'testattr3v1=sx=sl' => 1, + ], + ], + ] + ] + ] + ]; + } + + /** + * Verify row validation with numeric skus + * + * @return void + */ + public function testRowValidationForNumericalSkus(): void { // Set _attributes to avoid error in Magento\CatalogImportExport\Model\Import\Product\Type\AbstractType. $this->setPropertyValue($this->configurable, '_attributes', [ @@ -649,9 +684,11 @@ public function testRowValidationForNumericalSkus() } /** + * Row validation Data Provider + * * @return array */ - public function rowValidationDataProvider() + public function rowValidationDataProvider(): array { return [ 'duplicateProduct' => [ diff --git a/app/code/Magento/ConfigurableImportExport/registration.php b/app/code/Magento/ConfigurableImportExport/registration.php index 103565fb30a93..305ff345893c0 100644 --- a/app/code/Magento/ConfigurableImportExport/registration.php +++ b/app/code/Magento/ConfigurableImportExport/registration.php @@ -4,6 +4,6 @@ * See COPYING.txt for license details. */ -use \Magento\Framework\Component\ComponentRegistrar; +use Magento\Framework\Component\ComponentRegistrar; ComponentRegistrar::register(ComponentRegistrar::MODULE, 'Magento_ConfigurableImportExport', __DIR__); diff --git a/app/code/Magento/ConfigurableProduct/Model/Plugin/ProductRepositorySave.php b/app/code/Magento/ConfigurableProduct/Model/Plugin/ProductRepositorySave.php index 54b6e42ff7678..8bc7f05b49e30 100644 --- a/app/code/Magento/ConfigurableProduct/Model/Plugin/ProductRepositorySave.php +++ b/app/code/Magento/ConfigurableProduct/Model/Plugin/ProductRepositorySave.php @@ -5,15 +5,17 @@ */ namespace Magento\ConfigurableProduct\Model\Plugin; -use Magento\Catalog\Model\ProductFactory; use Magento\Catalog\Api\Data\ProductInterface; use Magento\Framework\Exception\InputException; use Magento\Catalog\Api\ProductRepositoryInterface; -use Magento\Framework\Exception\CouldNotSaveException; use Magento\ConfigurableProduct\Api\Data\OptionInterface; use Magento\Catalog\Api\ProductAttributeRepositoryInterface; use Magento\ConfigurableProduct\Model\Product\Type\Configurable; +use Magento\Framework\Exception\NoSuchEntityException; +/** + * Plugin to validate product links of configurable product and reset configurable attributes + */ class ProductRepositorySave { /** @@ -22,46 +24,45 @@ class ProductRepositorySave private $productAttributeRepository; /** - * @var ProductFactory + * @var ProductRepositoryInterface */ - private $productFactory; + private $productRepository; /** * @param ProductAttributeRepositoryInterface $productAttributeRepository - * @param ProductFactory $productFactory + * @param ProductRepositoryInterface $productRepository */ public function __construct( ProductAttributeRepositoryInterface $productAttributeRepository, - ProductFactory $productFactory + ProductRepositoryInterface $productRepository ) { $this->productAttributeRepository = $productAttributeRepository; - $this->productFactory = $productFactory; + $this->productRepository = $productRepository; } /** - * Validate product links and reset configurable attributes to configurable product + * Validate product links of configurable product * * @param ProductRepositoryInterface $subject - * @param ProductInterface $result * @param ProductInterface $product * @param bool $saveOptions - * @return ProductInterface - * @throws CouldNotSaveException + * @return array * @throws InputException + * @throws NoSuchEntityException * * @SuppressWarnings(PHPMD.UnusedFormalParameter) */ - public function afterSave( + public function beforeSave( ProductRepositoryInterface $subject, - ProductInterface $result, ProductInterface $product, $saveOptions = false ) { + $result[] = $product; if ($product->getTypeId() !== Configurable::TYPE_CODE) { return $result; } - $extensionAttributes = $result->getExtensionAttributes(); + $extensionAttributes = $product->getExtensionAttributes(); if ($extensionAttributes === null) { return $result; } @@ -81,23 +82,49 @@ public function afterSave( $attributeCodes[] = $attributeCode; } $this->validateProductLinks($attributeCodes, $configurableLinks); + + return $result; + } + + /** + * Reset configurable attributes to configurable product + * + * @param ProductRepositoryInterface $subject + * @param ProductInterface $result + * @param ProductInterface $product + * @param bool $saveOptions + * @return ProductInterface + * + * @SuppressWarnings(PHPMD.UnusedFormalParameter) + */ + public function afterSave( + ProductRepositoryInterface $subject, + ProductInterface $result, + ProductInterface $product, + $saveOptions = false + ) { + if ($product->getTypeId() !== Configurable::TYPE_CODE) { + return $result; + } $result->getTypeInstance()->resetConfigurableAttributes($product); return $result; } /** + * Validate product links + * * @param array $attributeCodes * @param array $linkIds - * @return $this + * @return void * @throws InputException + * @throws NoSuchEntityException */ private function validateProductLinks(array $attributeCodes, array $linkIds) { $valueMap = []; - foreach ($linkIds as $productId) { - $variation = $this->productFactory->create()->load($productId); + $variation = $this->productRepository->getById($productId); $valueKey = ''; foreach ($attributeCodes as $attributeCode) { if (!$variation->getData($attributeCode)) { diff --git a/app/code/Magento/ConfigurableProduct/Model/Product/Type/Configurable.php b/app/code/Magento/ConfigurableProduct/Model/Product/Type/Configurable.php index 5b50cc0ebd5e0..1b3ecfb1d222a 100644 --- a/app/code/Magento/ConfigurableProduct/Model/Product/Type/Configurable.php +++ b/app/code/Magento/ConfigurableProduct/Model/Product/Type/Configurable.php @@ -102,6 +102,13 @@ class Configurable extends \Magento\Catalog\Model\Product\Type\AbstractType */ protected $_canConfigure = true; + /** + * Local cache + * + * @var array + */ + protected $isSaleableBySku = []; + /** * @var \Magento\Framework\App\Config\ScopeConfigInterface */ @@ -585,7 +592,7 @@ protected function getGalleryReadHandler() * @param \Magento\Catalog\Model\Product $product * @return \Magento\ConfigurableProduct\Model\ResourceModel\Product\Type\Configurable\Product\Collection */ - public function getUsedProductCollection($product) + protected function getLinkedProductCollection($product) { $collection = $this->_productCollectionFactory->create()->setFlag( 'product_children', @@ -600,6 +607,17 @@ public function getUsedProductCollection($product) return $collection; } + /** + * Retrieve related products collection. Extension point for listing + * + * @param \Magento\Catalog\Model\Product $product + * @return \Magento\ConfigurableProduct\Model\ResourceModel\Product\Type\Configurable\Product\Collection + */ + public function getUsedProductCollection($product) + { + return $this->getLinkedProductCollection($product); + } + /** * Before save process * @@ -744,15 +762,27 @@ private function saveRelatedProducts(ProductInterface $product) */ public function isSalable($product) { + $storeId = $this->getStoreFilter($product); + if ($storeId instanceof \Magento\Store\Model\Store) { + $storeId = $storeId->getId(); + } + + $sku = $product->getSku(); + if (isset($this->isSaleableBySku[$storeId][$sku])) { + return $this->isSaleableBySku[$storeId][$sku]; + } + $salable = parent::isSalable($product); if ($salable !== false) { - $collection = $this->getUsedProductCollection($product); - $collection->addStoreFilter($this->getStoreFilter($product)); + $collection = $this->getLinkedProductCollection($product); + $collection->addStoreFilter($storeId); $collection = $this->salableProcessor->process($collection); $salable = 0 !== $collection->getSize(); } + $this->isSaleableBySku[$storeId][$sku] = $salable; + return $salable; } @@ -1240,14 +1270,8 @@ private function getCatalogConfig() */ public function isPossibleBuyFromList($product) { - $isAllCustomOptionsDisplayed = true; - foreach ($this->getConfigurableAttributes($product) as $attribute) { - $eavAttribute = $attribute->getProductAttribute(); - - $isAllCustomOptionsDisplayed = ($isAllCustomOptionsDisplayed && $eavAttribute->getUsedInProductListing()); - } - - return $isAllCustomOptionsDisplayed; + //such cases already handled by add to cart action + return true; } /** diff --git a/app/code/Magento/ConfigurableProduct/Model/ResourceModel/Product/LinkedProductSelectBuilder.php b/app/code/Magento/ConfigurableProduct/Model/ResourceModel/Product/LinkedProductSelectBuilder.php index 3f1c22548c8d8..fc5e149c0726e 100644 --- a/app/code/Magento/ConfigurableProduct/Model/ResourceModel/Product/LinkedProductSelectBuilder.php +++ b/app/code/Magento/ConfigurableProduct/Model/ResourceModel/Product/LinkedProductSelectBuilder.php @@ -39,11 +39,11 @@ public function __construct( } /** - * {@inheritdoc} + * @inheritdoc */ - public function build($productId) + public function build(int $productId, int $storeId) : array { - $selects = $this->linkedProductSelectBuilder->build($productId); + $selects = $this->linkedProductSelectBuilder->build($productId, $storeId); foreach ($selects as $select) { $this->baseSelectProcessor->process($select); diff --git a/app/code/Magento/ConfigurableProduct/Pricing/Price/LowestPriceOptionsProvider.php b/app/code/Magento/ConfigurableProduct/Pricing/Price/LowestPriceOptionsProvider.php index d3ce508b31e0d..29717a057cbdb 100644 --- a/app/code/Magento/ConfigurableProduct/Pricing/Price/LowestPriceOptionsProvider.php +++ b/app/code/Magento/ConfigurableProduct/Pricing/Price/LowestPriceOptionsProvider.php @@ -62,14 +62,16 @@ public function __construct( } /** - * {@inheritdoc} + * @inheritdoc */ public function getProducts(ProductInterface $product) { - $key = $this->storeManager->getStore()->getId() . '-' . $product->getId(); + $productId = $product->getId(); + $storeId = $product->getStoreId() ?: $this->storeManager->getStore()->getId(); + $key = $storeId . '-' . $productId; if (!isset($this->linkedProductMap[$key])) { $productIds = $this->resource->getConnection()->fetchCol( - '(' . implode(') UNION (', $this->linkedProductSelectBuilder->build($product->getId())) . ')' + '(' . implode(') UNION (', $this->linkedProductSelectBuilder->build($productId, $storeId)) . ')' ); $this->linkedProductMap[$key] = $this->collectionFactory->create() diff --git a/app/code/Magento/ConfigurableProduct/Pricing/Render/TierPriceBox.php b/app/code/Magento/ConfigurableProduct/Pricing/Render/TierPriceBox.php index 447ba16d72710..4be124f6d9e5b 100644 --- a/app/code/Magento/ConfigurableProduct/Pricing/Render/TierPriceBox.php +++ b/app/code/Magento/ConfigurableProduct/Pricing/Render/TierPriceBox.php @@ -9,8 +9,6 @@ /** * Responsible for displaying tier price box on configurable product page. - * - * @package Magento\ConfigurableProduct\Pricing\Render */ class TierPriceBox extends FinalPriceBox { @@ -23,6 +21,7 @@ public function toHtml() if (!$this->isMsrpPriceApplicable() && $this->isTierPriceApplicable()) { return parent::toHtml(); } + return ''; } /** diff --git a/app/code/Magento/ConfigurableProduct/Test/Mftf/ActionGroup/CreateApiConfigurableProductWithProductAttributeAdvanceSearchActionGroup.xml b/app/code/Magento/ConfigurableProduct/Test/Mftf/ActionGroup/CreateApiConfigurableProductWithProductAttributeAdvanceSearchActionGroup.xml new file mode 100644 index 0000000000000..77fcc137a0db5 --- /dev/null +++ b/app/code/Magento/ConfigurableProduct/Test/Mftf/ActionGroup/CreateApiConfigurableProductWithProductAttributeAdvanceSearchActionGroup.xml @@ -0,0 +1,21 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + /** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +--> + +<actionGroups xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/actionGroupSchema.xsd"> + <actionGroup name="CreateApiConfigurableProductWithDescriptionActionGroup" extends="AdminCreateApiConfigurableProductActionGroup"> + <annotations> + <description>Creates a Configurable Product with Description and 2 Product Options via API.</description> + </annotations> + + <!-- Replacement action. Create the configurable product via API. --> + <createData entity="ApiConfigurableProductWithDescription" stepKey="createConfigProduct"> + <field key="name">{{productName}}</field> + </createData> + </actionGroup> +</actionGroups> diff --git a/app/code/Magento/ConfigurableProduct/Test/Mftf/Data/ConfigurableProductData.xml b/app/code/Magento/ConfigurableProduct/Test/Mftf/Data/ConfigurableProductData.xml index 3edb7c0f17ab9..87b181616bd7a 100644 --- a/app/code/Magento/ConfigurableProduct/Test/Mftf/Data/ConfigurableProductData.xml +++ b/app/code/Magento/ConfigurableProduct/Test/Mftf/Data/ConfigurableProductData.xml @@ -23,12 +23,12 @@ <requiredEntity type="custom_attribute_array">CustomAttributeCategoryIds</requiredEntity> </entity> <entity name="ApiConfigurableProduct" type="product"> + <data key="name" unique="suffix">API Configurable Product</data> <data key="sku" unique="suffix">api-configurable-product</data> + <data key="urlKey" unique="suffix">api-configurable-product</data> <data key="type_id">configurable</data> <data key="attribute_set_id">4</data> <data key="visibility">4</data> - <data key="name" unique="suffix">API Configurable Product</data> - <data key="urlKey" unique="suffix">api-configurable-product</data> <data key="price">123.00</data> <data key="weight">2</data> <data key="status">1</data> diff --git a/app/code/Magento/ConfigurableProduct/Test/Mftf/Metadata/configurable_product_add_child-meta.xml b/app/code/Magento/ConfigurableProduct/Test/Mftf/Metadata/ConfigurableProductAddChildMeta.xml similarity index 100% rename from app/code/Magento/ConfigurableProduct/Test/Mftf/Metadata/configurable_product_add_child-meta.xml rename to app/code/Magento/ConfigurableProduct/Test/Mftf/Metadata/ConfigurableProductAddChildMeta.xml diff --git a/app/code/Magento/ConfigurableProduct/Test/Mftf/Metadata/configurable_product_options-meta.xml b/app/code/Magento/ConfigurableProduct/Test/Mftf/Metadata/ConfigurableProductOptionsMeta.xml similarity index 100% rename from app/code/Magento/ConfigurableProduct/Test/Mftf/Metadata/configurable_product_options-meta.xml rename to app/code/Magento/ConfigurableProduct/Test/Mftf/Metadata/ConfigurableProductOptionsMeta.xml diff --git a/app/code/Magento/ConfigurableProduct/Test/Mftf/Metadata/extension_attribute_configurable_product_options-meta.xml b/app/code/Magento/ConfigurableProduct/Test/Mftf/Metadata/ExtensionAttributeConfigurableProductOptionsMeta.xml similarity index 100% rename from app/code/Magento/ConfigurableProduct/Test/Mftf/Metadata/extension_attribute_configurable_product_options-meta.xml rename to app/code/Magento/ConfigurableProduct/Test/Mftf/Metadata/ExtensionAttributeConfigurableProductOptionsMeta.xml diff --git a/app/code/Magento/ConfigurableProduct/Test/Mftf/Metadata/valueIndex-meta.xml b/app/code/Magento/ConfigurableProduct/Test/Mftf/Metadata/ValueIndexMeta.xml similarity index 100% rename from app/code/Magento/ConfigurableProduct/Test/Mftf/Metadata/valueIndex-meta.xml rename to app/code/Magento/ConfigurableProduct/Test/Mftf/Metadata/ValueIndexMeta.xml diff --git a/app/code/Magento/ConfigurableProduct/Test/Mftf/Section/AdminProductFormConfigurationsSection/AdminConfigurableProductFormSection.xml b/app/code/Magento/ConfigurableProduct/Test/Mftf/Section/AdminProductFormConfigurationsSection/AdminConfigurableProductFormSection.xml new file mode 100644 index 0000000000000..769ba76dedb76 --- /dev/null +++ b/app/code/Magento/ConfigurableProduct/Test/Mftf/Section/AdminProductFormConfigurationsSection/AdminConfigurableProductFormSection.xml @@ -0,0 +1,17 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + /** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +--> +<sections xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Page/etc/SectionObject.xsd"> + <section name="AdminConfigurableProductFormSection"> + <element name="productWeight" type="input" selector=".admin__control-text[name='product[weight]']"/> + <element name="productQuantity" type="input" selector=".admin__control-text[name='product[quantity_and_stock_status][qty]']"/> + <element name="currentVariationsQuantityCells" type="button" selector="td[data-index='quantity_container']"/> + <element name="rowByCode" type="textarea" selector="//span[contains(text(), '{{var1}}-{{var2}}')]//ancestor-or-self::tr" parameterized="true"/> + <element name="currentAttribute" type="text" selector="//fieldset[@class='admin__fieldset']/div[contains(@class, 'admin__field _disabled')]//span"/> + </section> +</sections> diff --git a/app/code/Magento/ConfigurableProduct/Test/Mftf/Section/AdminProductFormConfigurationsSection/AdminConfigurableProductSelectAttributesSlideOutSection.xml b/app/code/Magento/ConfigurableProduct/Test/Mftf/Section/AdminProductFormConfigurationsSection/AdminConfigurableProductSelectAttributesSlideOutSection.xml new file mode 100644 index 0000000000000..5d4e30561ff97 --- /dev/null +++ b/app/code/Magento/ConfigurableProduct/Test/Mftf/Section/AdminProductFormConfigurationsSection/AdminConfigurableProductSelectAttributesSlideOutSection.xml @@ -0,0 +1,13 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + /** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +--> +<sections xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Page/etc/SectionObject.xsd"> + <section name="AdminConfigurableProductSelectAttributesSlideOut"> + <element name="grid" type="button" selector=".admin__data-grid-wrap tbody"/> + </section> +</sections> diff --git a/app/code/Magento/ConfigurableProduct/Test/Mftf/Section/AdminProductFormConfigurationsSection.xml b/app/code/Magento/ConfigurableProduct/Test/Mftf/Section/AdminProductFormConfigurationsSection/AdminProductFormConfigurationsSection.xml similarity index 77% rename from app/code/Magento/ConfigurableProduct/Test/Mftf/Section/AdminProductFormConfigurationsSection.xml rename to app/code/Magento/ConfigurableProduct/Test/Mftf/Section/AdminProductFormConfigurationsSection/AdminProductFormConfigurationsSection.xml index 336f95aa55576..320b3d575f3c6 100644 --- a/app/code/Magento/ConfigurableProduct/Test/Mftf/Section/AdminProductFormConfigurationsSection.xml +++ b/app/code/Magento/ConfigurableProduct/Test/Mftf/Section/AdminProductFormConfigurationsSection/AdminProductFormConfigurationsSection.xml @@ -5,9 +5,8 @@ * See COPYING.txt for license details. */ --> - <sections xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" - xsi:noNamespaceSchemaLocation="urn:magento:mftf:Page/etc/SectionObject.xsd"> + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Page/etc/SectionObject.xsd"> <section name="AdminProductFormConfigurationsSection"> <element name="sectionHeader" type="text" selector=".admin__collapsible-block-wrapper[data-index='configurable']"/> <element name="createdConfigurationsBlock" type="text" selector="div.admin__field.admin__field-wide"/> @@ -34,26 +33,13 @@ <element name="confProductWeightCell" type="input" selector="//*[.='Attributes']/ancestor::tr//span[contains(text(), '{{var}}')]/ancestor::tr/td[@data-index='price_weight']//input" parameterized="true"/> <element name="confProductOptionStatusCell" type="text" selector="//*[.='Attributes']/ancestor::tr//span[contains(text(), '{{productName}}')]/ancestor::tr/td[@data-index='status']" parameterized="true"/> <element name="confProductSkuMessage" type="text" selector="//*[@name='configurable-matrix[{{arg}}][sku]']/following-sibling::label" parameterized="true"/> - <element name="variationsSkuInputByRow" selector="[data-index='configurable-matrix'] table > tbody > tr:nth-of-type({{row}}) input[name*='sku']" type="input" parameterized="true"/> - <element name="variationsSkuInputErrorByRow" selector="[data-index='configurable-matrix'] table > tbody > tr:nth-of-type({{row}}) .admin__field-error" type="text" parameterized="true"/> + <element name="variationsSkuInputByRow" selector="[data-index='configurable-matrix'] table > tbody > tr:nth-of-type({{row}}) input[name*='sku']" type="input" parameterized="true"/> + <element name="variationsSkuInputErrorByRow" selector="[data-index='configurable-matrix'] table > tbody > tr:nth-of-type({{row}}) .admin__field-error" type="text" parameterized="true"/> <element name="variationLabel" type="text" selector="//div[@data-index='configurable-matrix']/label"/> <element name="stepsWizardTitle" type="text" selector="div.content:not([style='display: none;']) .steps-wizard-title"/> <element name="attributeEntityByName" type="text" selector="//div[@class='attribute-entity']//div[normalize-space(.)='{{attributeLabel}}']" parameterized="true"/> - <element name="fileUploaderInput" type="file" selector="//input[@type='file' and @class='file-uploader-input']" /> + <element name="fileUploaderInput" type="file" selector="//input[@type='file' and @class='file-uploader-input']"/> <element name="variationImageSource" type="text" selector="[data-index='configurable-matrix'] [data-index='thumbnail_image_container'] img[src*='{{imageName}}']" parameterized="true"/> <element name="variationProductLinkByName" type="text" selector="//div[@data-index='configurable-matrix']//*[@data-index='name_container']//a[contains(text(), '{{productName}}')]" parameterized="true"/> </section> - <section name="AdminConfigurableProductFormSection"> - <element name="productWeight" type="input" selector=".admin__control-text[name='product[weight]']"/> - <element name="productQuantity" type="input" selector=".admin__control-text[name='product[quantity_and_stock_status][qty]']"/> - <element name="currentVariationsQuantityCells" type="button" selector="td[data-index='quantity_container']"/> - <element name="rowByCode" type="textarea" selector="//span[contains(text(), '{{var1}}-{{var2}}')]//ancestor-or-self::tr" parameterized="true"/> - <element name="currentAttribute" type="text" selector="//fieldset[@class='admin__fieldset']/div[contains(@class, 'admin__field _disabled')]//span"/> - </section> - <section name="AdminConfigurableProductSelectAttributesSlideOut"> - <element name="grid" type="button" selector=".admin__data-grid-wrap tbody"/> - </section> - <section name="StorefrontConfigurableProductPage"> - <element name="productAttributeDropDown" type="select" selector="select[id*='attribute']"/> - </section> </sections> diff --git a/app/code/Magento/ConfigurableProduct/Test/Mftf/Section/AdminProductFormConfigurationsSection/StorefrontConfigurableProductPageSection.xml b/app/code/Magento/ConfigurableProduct/Test/Mftf/Section/AdminProductFormConfigurationsSection/StorefrontConfigurableProductPageSection.xml new file mode 100644 index 0000000000000..2bf61604df605 --- /dev/null +++ b/app/code/Magento/ConfigurableProduct/Test/Mftf/Section/AdminProductFormConfigurationsSection/StorefrontConfigurableProductPageSection.xml @@ -0,0 +1,13 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + /** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +--> +<sections xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Page/etc/SectionObject.xsd"> + <section name="StorefrontConfigurableProductPage"> + <element name="productAttributeDropDown" type="select" selector="select[id*='attribute']"/> + </section> +</sections> diff --git a/app/code/Magento/ConfigurableProduct/Test/Mftf/Section/ConfigurableProductAttributeNameDesignSection.xml b/app/code/Magento/ConfigurableProduct/Test/Mftf/Section/ConfigurableProductAttributeNameDesignSection.xml deleted file mode 100644 index ea5638f6816c9..0000000000000 --- a/app/code/Magento/ConfigurableProduct/Test/Mftf/Section/ConfigurableProductAttributeNameDesignSection.xml +++ /dev/null @@ -1,66 +0,0 @@ -<?xml version="1.0" encoding="UTF-8"?> -<!-- - /** - * Copyright © Magento, Inc. All rights reserved. - * See COPYING.txt for license details. - */ ---> - -<sections xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" - xsi:noNamespaceSchemaLocation="urn:magento:mftf:Page/etc/SectionObject.xsd"> - <section name="CatalogProductsSection"> - <element name="catalogItem" type="button" selector="//*[@id='menu-magento-catalog-catalog']/a/span"/> - <element name="productItem" type="button" selector="//*[@data-ui-id='menu-magento-catalog-catalog-products']/a"/> - <element name="storesItem" type="button" selector="//*[@id='menu-magento-backend-stores']/a/span"/> - <element name="searchDefaultLabelField" type="input" selector="//*[@id='attributeGrid_filter_frontend_label']"/> - <element name="searchButton" type="button" selector="//div[@class='admin__filter-actions']//*[contains(text(), 'Search')]"/> - <element name="storesProductItem" type="button" selector="//*[@data-ui-id='menu-magento-catalog-catalog-attributes-attributes']/a"/> - <element name="createdAttributeItem" type="button" selector="//td[contains(@class, 'col-label') and normalize-space()='design']"/> - <element name="deleteAttributeItem" type="button" selector="//*[@id='delete']"/> - <element name="okButton" type="button" selector="//footer[@class='modal-footer']//*[contains(text(),'OK')]"/> - <element name="messageSuccessSavedProduct" type="button" selector="//div[@data-ui-id='messages-message-success']"/> - <element name="resetFilter" type="button" selector="//span[contains(text(), 'Reset Filter')]"/> - </section> - - <section name="ConfigurableProductSection"> - <element name="addProductItem" type="button" selector="//*[@id='add_new_product']/button[2]"/> - <element name="configProductItem" type="button" selector="//*[@id='add_new_product']//*[contains(text(),'Configurable Product')]"/> - <element name="nextButton" type="button" selector="//div[@class='nav-bar-outer-actions']//*[contains(text(),'Next')]"/> - <element name="generateConfigure" type="button" selector="//div[@class='nav-bar-outer-actions']//*[contains(text(),'Generate Products')]"/> - <element name="selectCreatedAttribute" type="button" selector="//*[@class='admin__data-grid-wrap']//td[normalize-space()='design']/preceding-sibling::td"/> - <element name="closeFrame" type="button" selector="//*[@class='modal-header']//*[contains(text(),'Create Product Configurations')]/following-sibling::button"/> - </section> - - <section name="NewProduct"> - <element name="productName" type="input" selector="//input[@name='product[name]']"/> - <element name="price" type="input" selector="//input[@name='product[price]']"/> - <element name="weight" type="input" selector="//input[@name='product[weight]']"/> - <element name="createConfigurationButton" type="button" selector="//*[contains(text(),'Create Configurations')]"/> - <element name="createNewAttributeButton" type="button" selector="//*[contains(text(),'Create New Attribute')]"/> - <element name="newAttributeIFrame" type="iframe" selector="create_new_attribute_container"/> - <element name="defaultLabel" type="input" selector="//*[@id='attribute_label']"/> - <element name="addOptionButton" type="button" selector="//*[@id='add_new_option_button']"/> - <element name="adminFieldRed" type="input" selector="//input[@name='option[value][option_0][0]']"/> - <element name="defaultStoreViewFieldRed" type="input" selector="//input[@name='option[value][option_0][1]']"/> - <element name="adminFieldBlue" type="input" selector="//input[@name='option[value][option_1][0]']"/> - <element name="defaultStoreViewFieldBlue" type="input" selector="//input[@name='option[value][option_1][1]']"/> - <element name="adminFieldYellow" type="input" selector="//input[@name='option[value][option_2][0]']"/> - <element name="defaultStoreViewFieldYellow" type="input" selector="//input[@name='option[value][option_2][1]']"/> - <element name="adminFieldGreen" type="input" selector="//input[@name='option[value][option_3][0]']"/> - <element name="defaultStoreViewFieldGreen" type="input" selector="//input[@name='option[value][option_3][1]']"/> - <element name="adminFieldBlack" type="input" selector="//input[@name='option[value][option_4][0]']"/> - <element name="defaultStoreViewFieldBlack" type="input" selector="//input[@name='option[value][option_4][1]']"/> - <element name="saveAttributeButton" type="button" selector="//*[@id='save']"/> - <element name="advancedAttributeProperties" type="button" selector="//*[@id='advanced_fieldset-wrapper']//*[contains(text(),'Advanced Attribute Properties')]"/> - <element name="attributeCodeField" type="input" selector="//*[@id='attribute_code']"/> - </section> - - <section name="CreateProductConfigurations"> - <element name="checkboxRed" type="input" selector="//fieldset[@class='admin__fieldset admin__fieldset-options']//*[contains(text(),'red')]/preceding-sibling::input"/> - <element name="checkboxBlue" type="input" selector="//fieldset[@class='admin__fieldset admin__fieldset-options']//*[contains(text(),'blue')]/preceding-sibling::input"/> - <element name="checkboxYellow" type="input" selector="//fieldset[@class='admin__fieldset admin__fieldset-options']//*[contains(text(),'yellow')]/preceding-sibling::input"/> - <element name="checkboxGreen" type="input" selector="//fieldset[@class='admin__fieldset admin__fieldset-options']//*[contains(text(),'green')]/preceding-sibling::input"/> - <element name="checkboxBlack" type="input" selector="//fieldset[@class='admin__fieldset admin__fieldset-options']//*[contains(text(),'black')]/preceding-sibling::input"/> - <element name="errorMessage" type="input" selector="//div[@data-ui-id='messages-message-error']"/> - </section> -</sections> diff --git a/app/code/Magento/ConfigurableProduct/Test/Mftf/Section/ConfigurableProductAttributeNameDesignSection/CatalogProductsSection.xml b/app/code/Magento/ConfigurableProduct/Test/Mftf/Section/ConfigurableProductAttributeNameDesignSection/CatalogProductsSection.xml new file mode 100644 index 0000000000000..fb71d6cbda2a8 --- /dev/null +++ b/app/code/Magento/ConfigurableProduct/Test/Mftf/Section/ConfigurableProductAttributeNameDesignSection/CatalogProductsSection.xml @@ -0,0 +1,23 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + /** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +--> +<sections xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Page/etc/SectionObject.xsd"> + <section name="CatalogProductsSection"> + <element name="catalogItem" type="button" selector="//*[@id='menu-magento-catalog-catalog']/a/span"/> + <element name="productItem" type="button" selector="//*[@data-ui-id='menu-magento-catalog-catalog-products']/a"/> + <element name="storesItem" type="button" selector="//*[@id='menu-magento-backend-stores']/a/span"/> + <element name="searchDefaultLabelField" type="input" selector="//*[@id='attributeGrid_filter_frontend_label']"/> + <element name="searchButton" type="button" selector="//div[@class='admin__filter-actions']//*[contains(text(), 'Search')]"/> + <element name="storesProductItem" type="button" selector="//*[@data-ui-id='menu-magento-catalog-catalog-attributes-attributes']/a"/> + <element name="createdAttributeItem" type="button" selector="//td[contains(@class, 'col-label') and normalize-space()='design']"/> + <element name="deleteAttributeItem" type="button" selector="//*[@id='delete']"/> + <element name="okButton" type="button" selector="//footer[@class='modal-footer']//*[contains(text(),'OK')]"/> + <element name="messageSuccessSavedProduct" type="button" selector="//div[@data-ui-id='messages-message-success']"/> + <element name="resetFilter" type="button" selector="//span[contains(text(), 'Reset Filter')]"/> + </section> +</sections> diff --git a/app/code/Magento/ConfigurableProduct/Test/Mftf/Section/ConfigurableProductAttributeNameDesignSection/ConfigurableProductSection.xml b/app/code/Magento/ConfigurableProduct/Test/Mftf/Section/ConfigurableProductAttributeNameDesignSection/ConfigurableProductSection.xml new file mode 100644 index 0000000000000..8099f30941f7d --- /dev/null +++ b/app/code/Magento/ConfigurableProduct/Test/Mftf/Section/ConfigurableProductAttributeNameDesignSection/ConfigurableProductSection.xml @@ -0,0 +1,18 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + /** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +--> +<sections xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Page/etc/SectionObject.xsd"> + <section name="ConfigurableProductSection"> + <element name="addProductItem" type="button" selector="//*[@id='add_new_product']/button[2]"/> + <element name="configProductItem" type="button" selector="//*[@id='add_new_product']//*[contains(text(),'Configurable Product')]"/> + <element name="nextButton" type="button" selector="//div[@class='nav-bar-outer-actions']//*[contains(text(),'Next')]"/> + <element name="generateConfigure" type="button" selector="//div[@class='nav-bar-outer-actions']//*[contains(text(),'Generate Products')]"/> + <element name="selectCreatedAttribute" type="button" selector="//*[@class='admin__data-grid-wrap']//td[normalize-space()='design']/preceding-sibling::td"/> + <element name="closeFrame" type="button" selector="//*[@class='modal-header']//*[contains(text(),'Create Product Configurations')]/following-sibling::button"/> + </section> +</sections> diff --git a/app/code/Magento/ConfigurableProduct/Test/Mftf/Section/ConfigurableProductAttributeNameDesignSection/CreateProductConfigurationsSection.xml b/app/code/Magento/ConfigurableProduct/Test/Mftf/Section/ConfigurableProductAttributeNameDesignSection/CreateProductConfigurationsSection.xml new file mode 100644 index 0000000000000..02ef25dc71f6a --- /dev/null +++ b/app/code/Magento/ConfigurableProduct/Test/Mftf/Section/ConfigurableProductAttributeNameDesignSection/CreateProductConfigurationsSection.xml @@ -0,0 +1,18 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + /** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +--> +<sections xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Page/etc/SectionObject.xsd"> + <section name="CreateProductConfigurations"> + <element name="checkboxRed" type="input" selector="//fieldset[@class='admin__fieldset admin__fieldset-options']//*[contains(text(),'red')]/preceding-sibling::input"/> + <element name="checkboxBlue" type="input" selector="//fieldset[@class='admin__fieldset admin__fieldset-options']//*[contains(text(),'blue')]/preceding-sibling::input"/> + <element name="checkboxYellow" type="input" selector="//fieldset[@class='admin__fieldset admin__fieldset-options']//*[contains(text(),'yellow')]/preceding-sibling::input"/> + <element name="checkboxGreen" type="input" selector="//fieldset[@class='admin__fieldset admin__fieldset-options']//*[contains(text(),'green')]/preceding-sibling::input"/> + <element name="checkboxBlack" type="input" selector="//fieldset[@class='admin__fieldset admin__fieldset-options']//*[contains(text(),'black')]/preceding-sibling::input"/> + <element name="errorMessage" type="input" selector="//div[@data-ui-id='messages-message-error']"/> + </section> +</sections> diff --git a/app/code/Magento/ConfigurableProduct/Test/Mftf/Section/ConfigurableProductAttributeNameDesignSection/NewProductSection.xml b/app/code/Magento/ConfigurableProduct/Test/Mftf/Section/ConfigurableProductAttributeNameDesignSection/NewProductSection.xml new file mode 100644 index 0000000000000..cab08666f8701 --- /dev/null +++ b/app/code/Magento/ConfigurableProduct/Test/Mftf/Section/ConfigurableProductAttributeNameDesignSection/NewProductSection.xml @@ -0,0 +1,33 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + /** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +--> +<sections xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Page/etc/SectionObject.xsd"> + <section name="NewProduct"> + <element name="productName" type="input" selector="//input[@name='product[name]']"/> + <element name="price" type="input" selector="//input[@name='product[price]']"/> + <element name="weight" type="input" selector="//input[@name='product[weight]']"/> + <element name="createConfigurationButton" type="button" selector="//*[contains(text(),'Create Configurations')]"/> + <element name="createNewAttributeButton" type="button" selector="//*[contains(text(),'Create New Attribute')]"/> + <element name="newAttributeIFrame" type="iframe" selector="create_new_attribute_container"/> + <element name="defaultLabel" type="input" selector="//*[@id='attribute_label']"/> + <element name="addOptionButton" type="button" selector="//*[@id='add_new_option_button']"/> + <element name="adminFieldRed" type="input" selector="//input[@name='option[value][option_0][0]']"/> + <element name="defaultStoreViewFieldRed" type="input" selector="//input[@name='option[value][option_0][1]']"/> + <element name="adminFieldBlue" type="input" selector="//input[@name='option[value][option_1][0]']"/> + <element name="defaultStoreViewFieldBlue" type="input" selector="//input[@name='option[value][option_1][1]']"/> + <element name="adminFieldYellow" type="input" selector="//input[@name='option[value][option_2][0]']"/> + <element name="defaultStoreViewFieldYellow" type="input" selector="//input[@name='option[value][option_2][1]']"/> + <element name="adminFieldGreen" type="input" selector="//input[@name='option[value][option_3][0]']"/> + <element name="defaultStoreViewFieldGreen" type="input" selector="//input[@name='option[value][option_3][1]']"/> + <element name="adminFieldBlack" type="input" selector="//input[@name='option[value][option_4][0]']"/> + <element name="defaultStoreViewFieldBlack" type="input" selector="//input[@name='option[value][option_4][1]']"/> + <element name="saveAttributeButton" type="button" selector="//*[@id='save']"/> + <element name="advancedAttributeProperties" type="button" selector="//*[@id='advanced_fieldset-wrapper']//*[contains(text(),'Advanced Attribute Properties')]"/> + <element name="attributeCodeField" type="input" selector="//*[@id='attribute_code']"/> + </section> +</sections> diff --git a/app/code/Magento/ConfigurableProduct/Test/Mftf/Section/StorefrontProductInfoMainSection.xml b/app/code/Magento/ConfigurableProduct/Test/Mftf/Section/StorefrontProductInfoMainSection.xml index 0853d22eda7b9..cf0e99f7c45c0 100644 --- a/app/code/Magento/ConfigurableProduct/Test/Mftf/Section/StorefrontProductInfoMainSection.xml +++ b/app/code/Magento/ConfigurableProduct/Test/Mftf/Section/StorefrontProductInfoMainSection.xml @@ -10,6 +10,7 @@ xsi:noNamespaceSchemaLocation="urn:magento:mftf:Page/etc/SectionObject.xsd"> <section name="StorefrontProductInfoMainSection"> <element name="optionByAttributeId" type="input" selector="#attribute{{var1}}" parameterized="true"/> + <element name="selectableProductOptions" type="select" selector="#attribute{{var1}} option:not([disabled])" parameterized="true"/> <element name="productAttributeTitle1" type="text" selector="#product-options-wrapper div[tabindex='0'] label"/> <element name="productPrice" type="text" selector="div.price-box.price-final_price"/> <element name="productAttributeOptions1" type="select" selector="#product-options-wrapper div[tabindex='0'] option"/> diff --git a/app/code/Magento/ConfigurableProduct/Test/Mftf/Test/AdminAddDefaultImageConfigurableTest.xml b/app/code/Magento/ConfigurableProduct/Test/Mftf/Test/AdminAddDefaultImageConfigurableTest.xml index 2f9347157ef33..0d83cc6610194 100644 --- a/app/code/Magento/ConfigurableProduct/Test/Mftf/Test/AdminAddDefaultImageConfigurableTest.xml +++ b/app/code/Magento/ConfigurableProduct/Test/Mftf/Test/AdminAddDefaultImageConfigurableTest.xml @@ -20,7 +20,7 @@ </annotations> <before> - <actionGroup ref="LoginAsAdmin" stepKey="loginAsAdmin"/> + <actionGroup ref="AdminLoginActionGroup" stepKey="loginAsAdmin"/> <createData entity="SimpleSubCategory" stepKey="categoryHandle"/> <createData entity="SimpleProduct" stepKey="simple1Handle"> @@ -89,6 +89,9 @@ <deleteData createDataKey="baseConfigProductHandle" stepKey="deleteConfig"/> <deleteData createDataKey="categoryHandle" stepKey="deleteCategory"/> <deleteData createDataKey="productAttributeHandle" stepKey="deleteProductAttribute"/> + + <!-- Reindex invalidated indices after product attribute has been created/deleted --> + <magentoCron groups="index" stepKey="reindexInvalidatedIndices"/> </after> <amOnPage url="{{AdminProductIndexPage.url}}" stepKey="productIndexPage"/> diff --git a/app/code/Magento/ConfigurableProduct/Test/Mftf/Test/AdminAddingNewOptionsWithImagesAndPricesToConfigurableProductTest.xml b/app/code/Magento/ConfigurableProduct/Test/Mftf/Test/AdminAddingNewOptionsWithImagesAndPricesToConfigurableProductTest.xml index 86c4d0398b2cc..72ebd7962f420 100644 --- a/app/code/Magento/ConfigurableProduct/Test/Mftf/Test/AdminAddingNewOptionsWithImagesAndPricesToConfigurableProductTest.xml +++ b/app/code/Magento/ConfigurableProduct/Test/Mftf/Test/AdminAddingNewOptionsWithImagesAndPricesToConfigurableProductTest.xml @@ -21,7 +21,7 @@ <before> <actionGroup ref="AdminCreateApiConfigurableProductActionGroup" stepKey="createConfigurableProduct"/> - <actionGroup ref="LoginAsAdmin" stepKey="loginAsAdmin"/> + <actionGroup ref="AdminLoginActionGroup" stepKey="loginAsAdmin"/> </before> <after> @@ -32,13 +32,15 @@ <amOnPage url="{{AdminProductIndexPage.url}}" stepKey="navigateToProductIndex"/> <actionGroup ref="AdminClearFiltersActionGroup" stepKey="clearProductFilters"/> <actionGroup ref="DeleteProductActionGroup" stepKey="deleteProduct1"> - <argument name="productName" value="$$createConfigProductAttributeCreateConfigurableProduct.name$$"/> + <argument name="productName" value="$$createConfigProductCreateConfigurableProduct.name$$"/> </actionGroup> - <actionGroup ref="logout" stepKey="logout"/> + <actionGroup ref="AdminLogoutActionGroup" stepKey="logout"/> </after> <!--Open edit product page--> - <amOnPage url="{{AdminProductEditPage.url($$createConfigProductCreateConfigurableProduct.id$$)}}" stepKey="goToProductEditPage"/> + <actionGroup ref="AdminProductPageOpenByIdActionGroup" stepKey="goToProductEditPage"> + <argument name="productId" value="$$createConfigProductCreateConfigurableProduct.id$$"/> + </actionGroup> <!--Open edit configuration wizard--> <click selector="{{AdminProductFormConfigurationsSection.createConfigurations}}" stepKey="clickEditConfigurations"/> diff --git a/app/code/Magento/ConfigurableProduct/Test/Mftf/Test/AdminAssertNoticeThatExistingSkuAutomaticallyChangedWhenSavingProductWithSameSkuTest.xml b/app/code/Magento/ConfigurableProduct/Test/Mftf/Test/AdminAssertNoticeThatExistingSkuAutomaticallyChangedWhenSavingProductWithSameSkuTest.xml index 1613452dfc47c..6e26d73f3a36f 100644 --- a/app/code/Magento/ConfigurableProduct/Test/Mftf/Test/AdminAssertNoticeThatExistingSkuAutomaticallyChangedWhenSavingProductWithSameSkuTest.xml +++ b/app/code/Magento/ConfigurableProduct/Test/Mftf/Test/AdminAssertNoticeThatExistingSkuAutomaticallyChangedWhenSavingProductWithSameSkuTest.xml @@ -18,7 +18,7 @@ <group value="mtf_migrated"/> </annotations> <before> - <actionGroup ref="LoginAsAdmin" stepKey="loginAsAdmin"/> + <actionGroup ref="AdminLoginActionGroup" stepKey="loginAsAdmin"/> </before> <after> <!-- Delete configurable product --> @@ -27,12 +27,12 @@ </actionGroup> <!-- Delete product attribute --> - <actionGroup ref="DeleteProductAttributeByLabelActionGroup" stepKey="deleteProductAttribute"> - <argument name="ProductAttribute" value="colorProductAttribute"/> + <actionGroup ref="AdminDeleteProductAttributeByLabelActionGroup" stepKey="deleteProductAttribute"> + <argument name="productAttributeLabel" value="{{colorProductAttribute.default_label}}"/> </actionGroup> <!-- Log out --> - <actionGroup ref="logout" stepKey="logout"/> + <actionGroup ref="AdminLogoutActionGroup" stepKey="logout"/> </after> <!-- Create configurable product --> diff --git a/app/code/Magento/ConfigurableProduct/Test/Mftf/Test/AdminCheckConfigurableProductAttributeValueUniquenessTest.xml b/app/code/Magento/ConfigurableProduct/Test/Mftf/Test/AdminCheckConfigurableProductAttributeValueUniquenessTest.xml index 98cb03916bdab..8962efbb8dd26 100644 --- a/app/code/Magento/ConfigurableProduct/Test/Mftf/Test/AdminCheckConfigurableProductAttributeValueUniquenessTest.xml +++ b/app/code/Magento/ConfigurableProduct/Test/Mftf/Test/AdminCheckConfigurableProductAttributeValueUniquenessTest.xml @@ -20,7 +20,7 @@ <group value="ConfigurableProduct"/> </annotations> <before> - <actionGroup ref="LoginAsAdmin" stepKey="loginAsAdmin"/> + <actionGroup ref="AdminLoginActionGroup" stepKey="loginAsAdmin"/> <createData entity="dropdownProductAttribute" stepKey="createProductAttribute"/> </before> <after> @@ -33,7 +33,10 @@ <actionGroup ref="ResetProductGridToDefaultViewActionGroup" stepKey="resetProductGridColumnsInitial"/> <deleteData createDataKey="createCategory" stepKey="deleteCategory"/> <deleteData createDataKey="createProductAttribute" stepKey="deleteAttribute"/> - <actionGroup ref="logout" stepKey="logOut"/> + <actionGroup ref="AdminLogoutActionGroup" stepKey="logOut"/> + + <!-- Reindex invalidated indices after product attribute has been created/deleted --> + <magentoCron groups="index" stepKey="reindexInvalidatedIndices"/> </after> <!--Create configurable product--> <comment userInput="Create configurable product" stepKey="createConfProd"/> diff --git a/app/code/Magento/ConfigurableProduct/Test/Mftf/Test/AdminCheckResultsOfColorAndOtherFiltersTest.xml b/app/code/Magento/ConfigurableProduct/Test/Mftf/Test/AdminCheckResultsOfColorAndOtherFiltersTest.xml index fabd4a2c253b6..7d75f5d53c1f4 100644 --- a/app/code/Magento/ConfigurableProduct/Test/Mftf/Test/AdminCheckResultsOfColorAndOtherFiltersTest.xml +++ b/app/code/Magento/ConfigurableProduct/Test/Mftf/Test/AdminCheckResultsOfColorAndOtherFiltersTest.xml @@ -69,7 +69,7 @@ <requiredEntity createDataKey="createConfigProductAttribute2"/> </createData> <!-- Login as Admin --> - <actionGroup ref="LoginAsAdmin" stepKey="loginAsAdmin"/> + <actionGroup ref="AdminLoginActionGroup" stepKey="loginAsAdmin"/> <!-- Add created attributes with options to Attribute Set --> <actionGroup ref="AdminAddUnassignedAttributeToGroupActionGroup" stepKey="createDefaultAttributeSet"> <argument name="label" value="mySet"/> @@ -109,7 +109,10 @@ <actionGroup ref="ClearFiltersAdminDataGridActionGroup" stepKey="clearProductAttributesFilter"/> <actionGroup ref="AdminClearFiltersActionGroup" stepKey="clearProductsGridFilter"/> <!-- Log out --> - <actionGroup ref="logout" stepKey="logout"/> + <actionGroup ref="AdminLogoutActionGroup" stepKey="logout"/> + + <!-- Reindex invalidated indices after product attribute has been created/deleted --> + <magentoCron groups="index" stepKey="reindexInvalidatedIndices"/> </after> <!-- Create three configurable products with options --> <amOnPage url="{{AdminProductIndexPage.url}}" stepKey="amOnProductGridPage"/> diff --git a/app/code/Magento/ConfigurableProduct/Test/Mftf/Test/AdminCheckValidatorConfigurableProductTest.xml b/app/code/Magento/ConfigurableProduct/Test/Mftf/Test/AdminCheckValidatorConfigurableProductTest.xml index 5bdccf15b19d3..dc8c09864d0ab 100644 --- a/app/code/Magento/ConfigurableProduct/Test/Mftf/Test/AdminCheckValidatorConfigurableProductTest.xml +++ b/app/code/Magento/ConfigurableProduct/Test/Mftf/Test/AdminCheckValidatorConfigurableProductTest.xml @@ -21,7 +21,7 @@ <before> <!--Login as admin--> - <actionGroup ref="LoginAsAdmin" stepKey="loginAsAdmin"/> + <actionGroup ref="AdminLoginActionGroup" stepKey="loginAsAdmin"/> <!--Create Category--> <createData entity="ApiCategory" stepKey="createCategory"/> <!--Create Configurable product--> @@ -43,7 +43,10 @@ <actionGroup ref="DeleteProductAttributeActionGroup" stepKey="deleteAttribute"> <argument name="ProductAttribute" value="productDropDownAttribute"/> </actionGroup> - <actionGroup ref="logout" stepKey="logout"/> + <actionGroup ref="AdminLogoutActionGroup" stepKey="logout"/> + + <!-- Reindex invalidated indices after product attribute has been created/deleted --> + <magentoCron groups="index" stepKey="reindexInvalidatedIndices"/> </after> <!-- Find the product that we just created using the product grid --> diff --git a/app/code/Magento/ConfigurableProduct/Test/Mftf/Test/AdminConfigurableProductCreateTest.xml b/app/code/Magento/ConfigurableProduct/Test/Mftf/Test/AdminConfigurableProductCreateTest.xml deleted file mode 100644 index 2a1c38ee135eb..0000000000000 --- a/app/code/Magento/ConfigurableProduct/Test/Mftf/Test/AdminConfigurableProductCreateTest.xml +++ /dev/null @@ -1,140 +0,0 @@ -<?xml version="1.0" encoding="UTF-8"?> -<!-- - /** - * Copyright © Magento, Inc. All rights reserved. - * See COPYING.txt for license details. - */ ---> - -<tests xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" - xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/testSchema.xsd"> - <test name="AdminConfigurableProductCreateTest"> - <annotations> - <features value="ConfigurableProduct"/> - <stories value="Create, Read, Update, Delete"/> - <title value="admin should be able to create a configurable product with attributes"/> - <description value="admin should be able to create a configurable product with attributes"/> - <severity value="CRITICAL"/> - <testCaseId value="MC-84"/> - <group value="ConfigurableProduct"/> - </annotations> - - <before> - <createData entity="ApiCategory" stepKey="createCategory"/> - <actionGroup ref="LoginAsAdmin" stepKey="loginAsAdmin1"/> - </before> - - <after> - <deleteData createDataKey="createCategory" stepKey="deleteCategory"/> - <actionGroup ref="logout" stepKey="adminLogout"/> - </after> - - <!-- Create a configurable product via the UI --> - <actionGroup ref="CreateConfigurableProductActionGroup" stepKey="createProduct"> - <argument name="product" value="_defaultProduct"/> - <argument name="category" value="$$createCategory$$"/> - </actionGroup> - - <!-- assert color configurations on the admin create product page --> - <dontSee selector="{{AdminProductFormConfigurationsSection.variationLabel}}" stepKey="seeLabelNotVisible"/> - <seeNumberOfElements selector="{{AdminProductFormConfigurationsSection.currentVariationsRows}}" userInput="3" stepKey="seeNumberOfRows"/> - <see selector="{{AdminProductFormConfigurationsSection.currentVariationsNameCells}}" userInput="{{colorProductAttribute1.name}}" stepKey="seeAttributeName1InField"/> - <see selector="{{AdminProductFormConfigurationsSection.currentVariationsNameCells}}" userInput="{{colorProductAttribute2.name}}" stepKey="seeAttributeName2InField"/> - <see selector="{{AdminProductFormConfigurationsSection.currentVariationsNameCells}}" userInput="{{colorProductAttribute3.name}}" stepKey="seeAttributeName3InField"/> - <see selector="{{AdminProductFormConfigurationsSection.currentVariationsSkuCells}}" userInput="{{colorProductAttribute1.name}}" stepKey="seeAttributeSku1InField"/> - <see selector="{{AdminProductFormConfigurationsSection.currentVariationsSkuCells}}" userInput="{{colorProductAttribute2.name}}" stepKey="seeAttributeSku2InField"/> - <see selector="{{AdminProductFormConfigurationsSection.currentVariationsSkuCells}}" userInput="{{colorProductAttribute3.name}}" stepKey="seeAttributeSku3InField"/> - <see selector="{{AdminProductFormConfigurationsSection.currentVariationsPriceCells}}" userInput="{{colorProductAttribute1.price}}" stepKey="seeUniquePrice1InField"/> - <see selector="{{AdminProductFormConfigurationsSection.currentVariationsPriceCells}}" userInput="{{colorProductAttribute2.price}}" stepKey="seeUniquePrice2InField"/> - <see selector="{{AdminProductFormConfigurationsSection.currentVariationsPriceCells}}" userInput="{{colorProductAttribute3.price}}" stepKey="seeUniquePrice3InField"/> - <see selector="{{AdminProductFormConfigurationsSection.currentVariationsQuantityCells}}" userInput="{{colorProductAttribute.attribute_quantity}}" stepKey="seeQuantityInField"/> - - <!-- assert storefront category list page --> - <amOnPage url="/" stepKey="amOnStorefront"/> - <waitForPageLoad stepKey="waitForPageLoad3"/> - <click userInput="$$createCategory.name$$" stepKey="clickOnCategoryName"/> - <waitForPageLoad stepKey="waitForPageLoad4"/> - <see userInput="{{_defaultProduct.name}}" stepKey="assertProductPresent"/> - <see userInput="{{colorProductAttribute1.price}}" stepKey="assertProductPricePresent"/> - - <!-- assert storefront product details page --> - <click userInput="{{_defaultProduct.name}}" stepKey="clickOnProductName"/> - <waitForPageLoad stepKey="waitForPageLoad5"/> - <seeInTitle userInput="{{_defaultProduct.name}}" stepKey="assertProductNameTitle"/> - <see userInput="{{_defaultProduct.name}}" selector="{{StorefrontProductInfoMainSection.productName}}" stepKey="assertProductName"/> - <see userInput="{{colorProductAttribute1.price}}" selector="{{StorefrontProductInfoMainSection.productPrice}}" stepKey="assertProductPrice"/> - <see userInput="{{_defaultProduct.sku}}" selector="{{StorefrontProductInfoMainSection.productSku}}" stepKey="assertProductSku"/> - <see selector="{{StorefrontProductInfoMainSection.productAttributeTitle1}}" userInput="{{colorProductAttribute.default_label}}" stepKey="seeColorAttributeName1"/> - <see selector="{{StorefrontProductInfoMainSection.productAttributeOptions1}}" userInput="{{colorProductAttribute1.name}}" stepKey="seeInDropDown1"/> - <see selector="{{StorefrontProductInfoMainSection.productAttributeOptions1}}" userInput="{{colorProductAttribute2.name}}" stepKey="seeInDropDown2"/> - <see selector="{{StorefrontProductInfoMainSection.productAttributeOptions1}}" userInput="{{colorProductAttribute3.name}}" stepKey="seeInDropDown3"/> - </test> - - <test name="AdminCreateConfigurableProductAfterGettingIncorrectSKUMessageTest"> - <annotations> - <features value="ConfigurableProduct"/> - <stories value="Create, Read, Update, Delete"/> - <title value="admin should be able to create a configurable product after incorrect sku"/> - <description value="admin should be able to create a configurable product after incorrect sku"/> - <severity value="MAJOR"/> - <testCaseId value="MAGETWO-96365"/> - <useCaseId value="MAGETWO-94556"/> - <group value="ConfigurableProduct"/> - </annotations> - - <before> - <createData entity="ApiCategory" stepKey="createCategory"/> - <createData entity="ApiConfigurableProduct" stepKey="createConfigProduct"> - <requiredEntity createDataKey="createCategory"/> - </createData> - <actionGroup ref="LoginAsAdmin" stepKey="loginAsAdmin"/> - </before> - <after> - <deleteData createDataKey="createConfigProduct" stepKey="deleteConfigProduct"/> - <deleteData createDataKey="createCategory" stepKey="deleteCategory"/> - <actionGroup ref="logout" stepKey="logout"/> - </after> - - <amOnPage url="{{AdminProductEditPage.url($$createConfigProduct.id$$)}}" stepKey="goToEditPage"/> - <waitForPageLoad stepKey="waitForProductPage"/> - <conditionalClick selector="{{AdminProductFormConfigurationsSection.sectionHeader}}" dependentSelector="{{AdminProductFormConfigurationsSection.createConfigurations}}" visible="false" stepKey="openConfigurationSection"/> - <click selector="{{AdminProductFormConfigurationsSection.createConfigurations}}" stepKey="openConfigurationPane"/> - <click selector="{{AdminCreateProductConfigurationsPanel.filters}}" stepKey="clickFilters"/> - <fillField selector="{{AdminCreateProductConfigurationsPanel.attributeCode}}" userInput="color" stepKey="fillFilterAttributeCodeField"/> - <click selector="{{AdminCreateProductConfigurationsPanel.applyFilters}}" stepKey="clickApplyFiltersButton"/> - <click selector="{{AdminCreateProductConfigurationsPanel.firstCheckbox}}" stepKey="clickOnFirstCheckbox"/> - <click selector="{{AdminCreateProductConfigurationsPanel.next}}" stepKey="clickOnNextButton1"/> - <waitForElementVisible selector="{{AdminCreateProductConfigurationsPanel.createNewValue}}" stepKey="waitCreateNewValueAppears"/> - <click selector="{{AdminCreateProductConfigurationsPanel.createNewValue}}" stepKey="clickOnCreateNewValue1"/> - <fillField userInput="{{colorProductAttribute2.name}}" selector="{{AdminCreateProductConfigurationsPanel.attributeName}}" stepKey="fillFieldForNewAttribute1"/> - <click selector="{{AdminCreateProductConfigurationsPanel.saveAttribute}}" stepKey="clickOnSaveNewAttribute1"/> - <click selector="{{AdminCreateProductConfigurationsPanel.next}}" stepKey="clickOnNextButton2"/> - <click selector="{{AdminCreateProductConfigurationsPanel.next}}" stepKey="clickOnNextButton3"/> - <click selector="{{ConfigurableProductSection.generateConfigure}}" stepKey="generateConfigure"/> - <waitForPageLoad stepKey="waitForGenerateConfigure"/> - <grabValueFrom selector="{{AdminProductFormConfigurationsSection.firstSKUInConfigurableProductsGrid}}" stepKey="grabTextFromContent"/> - <fillField stepKey="fillMoreThan64Symbols" selector="{{AdminProductFormConfigurationsSection.firstSKUInConfigurableProductsGrid}}" userInput="01234567890123456789012345678901234567890123456789012345678901234"/> - <click selector="{{AdminProductFormActionSection.saveButton}}" stepKey="saveProduct1"/> - <conditionalClick selector="{{AdminChooseAffectedAttributeSetPopup.closePopUp}}" dependentSelector="{{AdminChooseAffectedAttributeSetPopup.closePopUp}}" visible="true" stepKey="clickOnCloseInPopup"/> - <see stepKey="seeErrorMessage" userInput="Please enter less or equal than 64 symbols."/> - <fillField stepKey="fillCorrectSKU" selector="{{AdminProductFormConfigurationsSection.firstSKUInConfigurableProductsGrid}}" userInput="$grabTextFromContent"/> - <click selector="{{AdminProductFormActionSection.saveButton}}" stepKey="saveProduct2"/> - <conditionalClick selector="{{AdminChooseAffectedAttributeSetPopup.confirm}}" dependentSelector="{{AdminChooseAffectedAttributeSetPopup.confirm}}" visible="true" stepKey="clickOnConfirmInPopup"/> - <see userInput="You saved the product." stepKey="seeSaveConfirmation"/> - <amOnPage url="{{AdminProductAttributeGridPage.url}}" stepKey="goToProductAttributes"/> - <waitForPageLoad stepKey="waitForProductAttributes"/> - <click selector="{{AdminProductAttributeGridSection.ResetFilter}}" stepKey="resetFiltersOnGrid1"/> - <fillField selector="{{AdminProductAttributeGridSection.FilterByAttributeCode}}" userInput="color" stepKey="fillFilter"/> - <click selector="{{AdminProductAttributeGridSection.Search}}" stepKey="clickSearch"/> - <click selector="{{AdminProductAttributeGridSection.AttributeCode('color')}}" stepKey="clickRowToEdit"/> - <click selector="{{DropdownAttributeOptionsSection.deleteButton(1)}}" stepKey="deleteOption"/> - <click selector="{{AttributePropertiesSection.Save}}" stepKey="saveAttribute"/> - <click selector="{{AdminProductAttributeGridSection.ResetFilter}}" stepKey="resetFiltersOnGrid2"/> - <actionGroup stepKey="deleteProduct1" ref="DeleteProductBySkuActionGroup"> - <argument name="sku" value="$grabTextFromContent"/> - </actionGroup> - <amOnPage url="{{AdminProductIndexPage.url}}" stepKey="visitAdminProductPage"/> - <waitForPageLoad time="60" stepKey="waitForPageLoadInitial"/> - <conditionalClick selector="{{AdminProductGridFilterSection.clearFilters}}" dependentSelector="{{AdminProductGridFilterSection.clearFilters}}" visible="true" stepKey="clickClearFiltersInitial"/> - </test> -</tests> diff --git a/app/code/Magento/ConfigurableProduct/Test/Mftf/Test/AdminConfigurableProductCreateTest/AdminConfigurableProductCreateTest.xml b/app/code/Magento/ConfigurableProduct/Test/Mftf/Test/AdminConfigurableProductCreateTest/AdminConfigurableProductCreateTest.xml new file mode 100644 index 0000000000000..59da88874f5b2 --- /dev/null +++ b/app/code/Magento/ConfigurableProduct/Test/Mftf/Test/AdminConfigurableProductCreateTest/AdminConfigurableProductCreateTest.xml @@ -0,0 +1,71 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + /** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +--> +<tests xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/testSchema.xsd"> + <test name="AdminConfigurableProductCreateTest"> + <annotations> + <features value="ConfigurableProduct"/> + <stories value="Create, Read, Update, Delete"/> + <title value="admin should be able to create a configurable product with attributes"/> + <description value="admin should be able to create a configurable product with attributes"/> + <severity value="BLOCKER"/> + <testCaseId value="MC-84"/> + <group value="ConfigurableProduct"/> + </annotations> + + <before> + <createData entity="ApiCategory" stepKey="createCategory"/> + <actionGroup ref="AdminLoginActionGroup" stepKey="loginAsAdmin1"/> + </before> + + <after> + <deleteData createDataKey="createCategory" stepKey="deleteCategory"/> + <actionGroup ref="AdminLogoutActionGroup" stepKey="adminLogout"/> + </after> + + <!-- Create a configurable product via the UI --> + <actionGroup ref="CreateConfigurableProductActionGroup" stepKey="createProduct"> + <argument name="product" value="_defaultProduct"/> + <argument name="category" value="$$createCategory$$"/> + </actionGroup> + + <!-- assert color configurations on the admin create product page --> + <dontSee selector="{{AdminProductFormConfigurationsSection.variationLabel}}" stepKey="seeLabelNotVisible"/> + <seeNumberOfElements selector="{{AdminProductFormConfigurationsSection.currentVariationsRows}}" userInput="3" stepKey="seeNumberOfRows"/> + <see selector="{{AdminProductFormConfigurationsSection.currentVariationsNameCells}}" userInput="{{colorProductAttribute1.name}}" stepKey="seeAttributeName1InField"/> + <see selector="{{AdminProductFormConfigurationsSection.currentVariationsNameCells}}" userInput="{{colorProductAttribute2.name}}" stepKey="seeAttributeName2InField"/> + <see selector="{{AdminProductFormConfigurationsSection.currentVariationsNameCells}}" userInput="{{colorProductAttribute3.name}}" stepKey="seeAttributeName3InField"/> + <see selector="{{AdminProductFormConfigurationsSection.currentVariationsSkuCells}}" userInput="{{colorProductAttribute1.name}}" stepKey="seeAttributeSku1InField"/> + <see selector="{{AdminProductFormConfigurationsSection.currentVariationsSkuCells}}" userInput="{{colorProductAttribute2.name}}" stepKey="seeAttributeSku2InField"/> + <see selector="{{AdminProductFormConfigurationsSection.currentVariationsSkuCells}}" userInput="{{colorProductAttribute3.name}}" stepKey="seeAttributeSku3InField"/> + <see selector="{{AdminProductFormConfigurationsSection.currentVariationsPriceCells}}" userInput="{{colorProductAttribute1.price}}" stepKey="seeUniquePrice1InField"/> + <see selector="{{AdminProductFormConfigurationsSection.currentVariationsPriceCells}}" userInput="{{colorProductAttribute2.price}}" stepKey="seeUniquePrice2InField"/> + <see selector="{{AdminProductFormConfigurationsSection.currentVariationsPriceCells}}" userInput="{{colorProductAttribute3.price}}" stepKey="seeUniquePrice3InField"/> + <see selector="{{AdminProductFormConfigurationsSection.currentVariationsQuantityCells}}" userInput="{{colorProductAttribute.attribute_quantity}}" stepKey="seeQuantityInField"/> + + <!-- assert storefront category list page --> + <amOnPage url="/" stepKey="amOnStorefront"/> + <waitForPageLoad stepKey="waitForPageLoad3"/> + <click userInput="$$createCategory.name$$" stepKey="clickOnCategoryName"/> + <waitForPageLoad stepKey="waitForPageLoad4"/> + <see userInput="{{_defaultProduct.name}}" stepKey="assertProductPresent"/> + <see userInput="{{colorProductAttribute1.price}}" stepKey="assertProductPricePresent"/> + + <!-- assert storefront product details page --> + <click userInput="{{_defaultProduct.name}}" stepKey="clickOnProductName"/> + <waitForPageLoad stepKey="waitForPageLoad5"/> + <seeInTitle userInput="{{_defaultProduct.name}}" stepKey="assertProductNameTitle"/> + <see userInput="{{_defaultProduct.name}}" selector="{{StorefrontProductInfoMainSection.productName}}" stepKey="assertProductName"/> + <see userInput="{{colorProductAttribute1.price}}" selector="{{StorefrontProductInfoMainSection.productPrice}}" stepKey="assertProductPrice"/> + <see userInput="{{_defaultProduct.sku}}" selector="{{StorefrontProductInfoMainSection.productSku}}" stepKey="assertProductSku"/> + <see selector="{{StorefrontProductInfoMainSection.productAttributeTitle1}}" userInput="{{colorProductAttribute.default_label}}" stepKey="seeColorAttributeName1"/> + <see selector="{{StorefrontProductInfoMainSection.productAttributeOptions1}}" userInput="{{colorProductAttribute1.name}}" stepKey="seeInDropDown1"/> + <see selector="{{StorefrontProductInfoMainSection.productAttributeOptions1}}" userInput="{{colorProductAttribute2.name}}" stepKey="seeInDropDown2"/> + <see selector="{{StorefrontProductInfoMainSection.productAttributeOptions1}}" userInput="{{colorProductAttribute3.name}}" stepKey="seeInDropDown3"/> + </test> +</tests> diff --git a/app/code/Magento/ConfigurableProduct/Test/Mftf/Test/AdminConfigurableProductCreateTest/AdminCreateConfigurableProductAfterGettingIncorrectSKUMessageTest.xml b/app/code/Magento/ConfigurableProduct/Test/Mftf/Test/AdminConfigurableProductCreateTest/AdminCreateConfigurableProductAfterGettingIncorrectSKUMessageTest.xml new file mode 100644 index 0000000000000..274a75aedbc5f --- /dev/null +++ b/app/code/Magento/ConfigurableProduct/Test/Mftf/Test/AdminConfigurableProductCreateTest/AdminCreateConfigurableProductAfterGettingIncorrectSKUMessageTest.xml @@ -0,0 +1,79 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + /** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +--> +<tests xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/testSchema.xsd"> + <test name="AdminCreateConfigurableProductAfterGettingIncorrectSKUMessageTest"> + <annotations> + <features value="ConfigurableProduct"/> + <stories value="Create, Read, Update, Delete"/> + <title value="admin should be able to create a configurable product after incorrect sku"/> + <description value="admin should be able to create a configurable product after incorrect sku"/> + <severity value="BLOCKER"/> + <testCaseId value="MAGETWO-96365"/> + <useCaseId value="MAGETWO-94556"/> + <group value="ConfigurableProduct"/> + </annotations> + + <before> + <createData entity="ApiCategory" stepKey="createCategory"/> + <createData entity="ApiConfigurableProduct" stepKey="createConfigProduct"> + <requiredEntity createDataKey="createCategory"/> + </createData> + <actionGroup ref="AdminLoginActionGroup" stepKey="loginAsAdmin"/> + </before> + <after> + <deleteData createDataKey="createConfigProduct" stepKey="deleteConfigProduct"/> + <deleteData createDataKey="createCategory" stepKey="deleteCategory"/> + <actionGroup ref="AdminLogoutActionGroup" stepKey="logout"/> + </after> + + <actionGroup ref="AdminProductPageOpenByIdActionGroup" stepKey="goToEditPage"> + <argument name="productId" value="$$createConfigProduct.id$$"/> + </actionGroup> + <waitForPageLoad stepKey="waitForProductPage"/> + <conditionalClick selector="{{AdminProductFormConfigurationsSection.sectionHeader}}" dependentSelector="{{AdminProductFormConfigurationsSection.createConfigurations}}" visible="false" stepKey="openConfigurationSection"/> + <click selector="{{AdminProductFormConfigurationsSection.createConfigurations}}" stepKey="openConfigurationPane"/> + <click selector="{{AdminCreateProductConfigurationsPanel.filters}}" stepKey="clickFilters"/> + <fillField selector="{{AdminCreateProductConfigurationsPanel.attributeCode}}" userInput="color" stepKey="fillFilterAttributeCodeField"/> + <click selector="{{AdminCreateProductConfigurationsPanel.applyFilters}}" stepKey="clickApplyFiltersButton"/> + <click selector="{{AdminCreateProductConfigurationsPanel.firstCheckbox}}" stepKey="clickOnFirstCheckbox"/> + <click selector="{{AdminCreateProductConfigurationsPanel.next}}" stepKey="clickOnNextButton1"/> + <waitForElementVisible selector="{{AdminCreateProductConfigurationsPanel.createNewValue}}" stepKey="waitCreateNewValueAppears"/> + <click selector="{{AdminCreateProductConfigurationsPanel.createNewValue}}" stepKey="clickOnCreateNewValue1"/> + <fillField userInput="{{colorProductAttribute2.name}}" selector="{{AdminCreateProductConfigurationsPanel.attributeName}}" stepKey="fillFieldForNewAttribute1"/> + <click selector="{{AdminCreateProductConfigurationsPanel.saveAttribute}}" stepKey="clickOnSaveNewAttribute1"/> + <click selector="{{AdminCreateProductConfigurationsPanel.next}}" stepKey="clickOnNextButton2"/> + <click selector="{{AdminCreateProductConfigurationsPanel.next}}" stepKey="clickOnNextButton3"/> + <click selector="{{ConfigurableProductSection.generateConfigure}}" stepKey="generateConfigure"/> + <waitForPageLoad stepKey="waitForGenerateConfigure"/> + <grabValueFrom selector="{{AdminProductFormConfigurationsSection.firstSKUInConfigurableProductsGrid}}" stepKey="grabTextFromContent"/> + <fillField stepKey="fillMoreThan64Symbols" selector="{{AdminProductFormConfigurationsSection.firstSKUInConfigurableProductsGrid}}" userInput="01234567890123456789012345678901234567890123456789012345678901234"/> + <click selector="{{AdminProductFormActionSection.saveButton}}" stepKey="saveProduct1"/> + <conditionalClick selector="{{AdminChooseAffectedAttributeSetPopup.closePopUp}}" dependentSelector="{{AdminChooseAffectedAttributeSetPopup.closePopUp}}" visible="true" stepKey="clickOnCloseInPopup"/> + <see stepKey="seeErrorMessage" userInput="Please enter less or equal than 64 symbols."/> + <fillField stepKey="fillCorrectSKU" selector="{{AdminProductFormConfigurationsSection.firstSKUInConfigurableProductsGrid}}" userInput="$grabTextFromContent"/> + <click selector="{{AdminProductFormActionSection.saveButton}}" stepKey="saveProduct2"/> + <conditionalClick selector="{{AdminChooseAffectedAttributeSetPopup.confirm}}" dependentSelector="{{AdminChooseAffectedAttributeSetPopup.confirm}}" visible="true" stepKey="clickOnConfirmInPopup"/> + <see userInput="You saved the product." stepKey="seeSaveConfirmation"/> + <amOnPage url="{{AdminProductAttributeGridPage.url}}" stepKey="goToProductAttributes"/> + <waitForPageLoad stepKey="waitForProductAttributes"/> + <click selector="{{AdminProductAttributeGridSection.ResetFilter}}" stepKey="resetFiltersOnGrid1"/> + <fillField selector="{{AdminProductAttributeGridSection.FilterByAttributeCode}}" userInput="color" stepKey="fillFilter"/> + <click selector="{{AdminProductAttributeGridSection.Search}}" stepKey="clickSearch"/> + <click selector="{{AdminProductAttributeGridSection.AttributeCode('color')}}" stepKey="clickRowToEdit"/> + <click selector="{{DropdownAttributeOptionsSection.deleteButton(1)}}" stepKey="deleteOption"/> + <click selector="{{AttributePropertiesSection.Save}}" stepKey="saveAttribute"/> + <click selector="{{AdminProductAttributeGridSection.ResetFilter}}" stepKey="resetFiltersOnGrid2"/> + <actionGroup stepKey="deleteProduct1" ref="DeleteProductBySkuActionGroup"> + <argument name="sku" value="$grabTextFromContent"/> + </actionGroup> + <amOnPage url="{{AdminProductIndexPage.url}}" stepKey="visitAdminProductPage"/> + <waitForPageLoad time="60" stepKey="waitForPageLoadInitial"/> + <conditionalClick selector="{{AdminProductGridFilterSection.clearFilters}}" dependentSelector="{{AdminProductGridFilterSection.clearFilters}}" visible="true" stepKey="clickClearFiltersInitial"/> + </test> +</tests> diff --git a/app/code/Magento/ConfigurableProduct/Test/Mftf/Test/AdminConfigurableProductDeleteTest.xml b/app/code/Magento/ConfigurableProduct/Test/Mftf/Test/AdminConfigurableProductDeleteTest.xml deleted file mode 100644 index 83e428b454c46..0000000000000 --- a/app/code/Magento/ConfigurableProduct/Test/Mftf/Test/AdminConfigurableProductDeleteTest.xml +++ /dev/null @@ -1,264 +0,0 @@ -<?xml version="1.0" encoding="UTF-8"?> -<!-- - /** - * Copyright © Magento, Inc. All rights reserved. - * See COPYING.txt for license details. - */ ---> - -<tests xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" - xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/testSchema.xsd"> - <test name="AdminConfigurableProductDeleteTest"> - <annotations> - <features value="ConfigurableProduct"/> - <stories value="Create, Read, Update, Delete"/> - <title value="admin should be able to delete a configurable product"/> - <description value="admin should be able to delete a configurable product"/> - <testCaseId value="MC-87"/> - <group value="ConfigurableProduct"/> - <severity value="AVERAGE"/> - </annotations> - - <before> - <!-- TODO: This should be converted to an actionGroup once MQE-993 is fixed. --> - <createData entity="ApiCategory" stepKey="createCategory"/> - <createData entity="ApiConfigurableProduct" stepKey="createConfigProduct"> - <requiredEntity createDataKey="createCategory"/> - </createData> - <createData entity="productAttributeWithTwoOptions" stepKey="createConfigProductAttribute"/> - <createData entity="productAttributeOption1" stepKey="createConfigProductAttributeOption1"> - <requiredEntity createDataKey="createConfigProductAttribute"/> - </createData> - <createData entity="productAttributeOption2" stepKey="createConfigProductAttributeOption2"> - <requiredEntity createDataKey="createConfigProductAttribute"/> - </createData> - <createData entity="AddToDefaultSet" stepKey="createConfigAddToAttributeSet"> - <requiredEntity createDataKey="createConfigProductAttribute"/> - </createData> - <getData entity="ProductAttributeOptionGetter" index="1" stepKey="getConfigAttributeOption1"> - <requiredEntity createDataKey="createConfigProductAttribute"/> - </getData> - <getData entity="ProductAttributeOptionGetter" index="2" stepKey="getConfigAttributeOption2"> - <requiredEntity createDataKey="createConfigProductAttribute"/> - </getData> - <createData entity="ApiSimpleOne" stepKey="createConfigChildProduct1"> - <requiredEntity createDataKey="createConfigProductAttribute"/> - <requiredEntity createDataKey="getConfigAttributeOption1"/> - </createData> - <createData entity="ApiSimpleTwo" stepKey="createConfigChildProduct2"> - <requiredEntity createDataKey="createConfigProductAttribute"/> - <requiredEntity createDataKey="getConfigAttributeOption2"/> - </createData> - <createData entity="ConfigurableProductTwoOptions" stepKey="createConfigProductOption"> - <requiredEntity createDataKey="createConfigProduct"/> - <requiredEntity createDataKey="createConfigProductAttribute"/> - <requiredEntity createDataKey="getConfigAttributeOption1"/> - <requiredEntity createDataKey="getConfigAttributeOption2"/> - </createData> - <createData entity="ConfigurableProductAddChild" stepKey="createConfigProductAddChild1"> - <requiredEntity createDataKey="createConfigProduct"/> - <requiredEntity createDataKey="createConfigChildProduct1"/> - </createData> - <createData entity="ConfigurableProductAddChild" stepKey="createConfigProductAddChild2"> - <requiredEntity createDataKey="createConfigProduct"/> - <requiredEntity createDataKey="createConfigChildProduct2"/> - </createData> - </before> - - <after> - <actionGroup ref="logout" stepKey="adminLogout"/> - <deleteData createDataKey="createConfigChildProduct1" stepKey="deleteConfigChildProduct1"/> - <deleteData createDataKey="createConfigChildProduct2" stepKey="deleteConfigChildProduct2"/> - <deleteData createDataKey="createConfigProductAttribute" stepKey="deleteConfigProductAttribute"/> - </after> - - <!-- assert product visible in storefront --> - <amOnPage url="$$createConfigProduct.sku$$.html" stepKey="gotoStorefront1"/> - <waitForPageLoad stepKey="wait1"/> - <see selector="{{StorefrontProductInfoMainSection.productName}}" userInput="$$createConfigProduct.name$$" stepKey="seeProduct"/> - - <!-- go to admin and delete --> - <actionGroup ref="LoginAsAdmin" stepKey="login"/> - <amOnPage url="{{AdminProductIndexPage.url}}" stepKey="goToProductList"/> - <waitForPageLoad stepKey="wait2"/> - <conditionalClick selector="{{AdminProductGridFilterSection.clearAll}}" dependentSelector="{{AdminProductGridFilterSection.clearAll}}" stepKey="clearAll" visible="true"/> - <actionGroup ref="SearchProductGridByKeywordActionGroup" stepKey="searchForProduct"> - <argument name="keyword" value="ApiConfigurableProduct.name"/> - </actionGroup> - <click selector="label.data-grid-checkbox-cell-inner" stepKey="clickCheckbox"/> - <click selector="{{AdminProductGridSection.bulkActionDropdown}}" stepKey="clickActionDropdown"/> - <click selector="{{AdminProductGridSection.bulkActionOption('Delete')}}" stepKey="clickDeleteAction"/> - <waitForElementVisible selector="{{AdminProductGridConfirmActionSection.title}}" stepKey="waitForConfirmModal"/> - <click selector="{{AdminProductGridConfirmActionSection.ok}}" stepKey="confirmProductDelete"/> - <see selector="{{AdminProductMessagesSection.successMessage}}" userInput="A total of 1 record(s) have been deleted." stepKey="seeSuccessMsg"/> - - <!-- after delete, assert product page is 404 --> - <amOnPage url="$$createConfigProduct.sku$$.html" stepKey="gotoStorefront2"/> - <waitForPageLoad stepKey="wait3"/> - <see selector="{{StorefrontProductInfoMainSection.productName}}" userInput="Whoops, our bad..." stepKey="seeWhoops"/> - <dontSee selector="{{StorefrontProductInfoMainSection.productName}}" userInput="$$createConfigProduct.name$$" stepKey="dontSeeProduct"/> - </test> - - <test name="AdminConfigurableProductBulkDeleteTest"> - <annotations> - <features value="ConfigurableProduct"/> - <stories value="Create, Read, Update, Delete"/> - <title value="admin should be able to mass delete configurable products"/> - <description value="admin should be able to mass delete configurable products"/> - <testCaseId value="MC-99"/> - <group value="ConfigurableProduct"/> - <severity value="AVERAGE"/> - </annotations> - - <before> - <!-- TODO: Parts of this should be converted to an actionGroup once MQE-993 is fixed. --> - <!-- Create shared category and attribute --> - <createData entity="ApiCategory" stepKey="createCategory"/> - <createData entity="productAttributeWithTwoOptions" stepKey="createConfigProductAttribute"/> - <createData entity="productAttributeOption1" stepKey="createConfigProductAttributeOption1"> - <requiredEntity createDataKey="createConfigProductAttribute"/> - </createData> - <createData entity="productAttributeOption2" stepKey="createConfigProductAttributeOption2"> - <requiredEntity createDataKey="createConfigProductAttribute"/> - </createData> - <createData entity="AddToDefaultSet" stepKey="createConfigAddToAttributeSet"> - <requiredEntity createDataKey="createConfigProductAttribute"/> - </createData> - <getData entity="ProductAttributeOptionGetter" index="1" stepKey="getConfigAttributeOption1"> - <requiredEntity createDataKey="createConfigProductAttribute"/> - </getData> - <getData entity="ProductAttributeOptionGetter" index="2" stepKey="getConfigAttributeOption2"> - <requiredEntity createDataKey="createConfigProductAttribute"/> - </getData> - - <!-- Create first of three configurable products --> - <createData entity="ApiConfigurableProduct" stepKey="createProduct1"> - <requiredEntity createDataKey="createCategory"/> - </createData> - <createData entity="ApiSimpleOne" stepKey="createConfigChildProduct1"> - <requiredEntity createDataKey="createConfigProductAttribute"/> - <requiredEntity createDataKey="getConfigAttributeOption1"/> - </createData> - <createData entity="ApiSimpleTwo" stepKey="createConfigChildProduct2"> - <requiredEntity createDataKey="createConfigProductAttribute"/> - <requiredEntity createDataKey="getConfigAttributeOption2"/> - </createData> - <createData entity="ConfigurableProductTwoOptions" stepKey="createConfigProductOption1"> - <requiredEntity createDataKey="createProduct1"/> - <requiredEntity createDataKey="createConfigProductAttribute"/> - <requiredEntity createDataKey="getConfigAttributeOption1"/> - <requiredEntity createDataKey="getConfigAttributeOption2"/> - </createData> - <createData entity="ConfigurableProductAddChild" stepKey="createConfigProductAddChild1"> - <requiredEntity createDataKey="createProduct1"/> - <requiredEntity createDataKey="createConfigChildProduct1"/> - </createData> - <createData entity="ConfigurableProductAddChild" stepKey="createConfigProductAddChild2"> - <requiredEntity createDataKey="createProduct1"/> - <requiredEntity createDataKey="createConfigChildProduct2"/> - </createData> - - <!-- Create second configurable product --> - <createData entity="ApiConfigurableProduct" stepKey="createProduct2"> - <requiredEntity createDataKey="createCategory"/> - </createData> - <createData entity="ApiSimpleOne" stepKey="createConfigChildProduct3"> - <requiredEntity createDataKey="createConfigProductAttribute"/> - <requiredEntity createDataKey="getConfigAttributeOption1"/> - </createData> - <createData entity="ApiSimpleTwo" stepKey="createConfigChildProduct4"> - <requiredEntity createDataKey="createConfigProductAttribute"/> - <requiredEntity createDataKey="getConfigAttributeOption2"/> - </createData> - <createData entity="ConfigurableProductTwoOptions" stepKey="createConfigProductOption2"> - <requiredEntity createDataKey="createProduct2"/> - <requiredEntity createDataKey="createConfigProductAttribute"/> - <requiredEntity createDataKey="getConfigAttributeOption1"/> - <requiredEntity createDataKey="getConfigAttributeOption2"/> - </createData> - <createData entity="ConfigurableProductAddChild" stepKey="createConfigProductAddChild3"> - <requiredEntity createDataKey="createProduct2"/> - <requiredEntity createDataKey="createConfigChildProduct3"/> - </createData> - <createData entity="ConfigurableProductAddChild" stepKey="createConfigProductAddChild4"> - <requiredEntity createDataKey="createProduct2"/> - <requiredEntity createDataKey="createConfigChildProduct4"/> - </createData> - - <!-- Create third configurable product --> - <createData entity="ApiConfigurableProduct" stepKey="createProduct3"> - <requiredEntity createDataKey="createCategory"/> - </createData> - <createData entity="ApiSimpleOne" stepKey="createConfigChildProduct5"> - <requiredEntity createDataKey="createConfigProductAttribute"/> - <requiredEntity createDataKey="getConfigAttributeOption1"/> - </createData> - <createData entity="ApiSimpleTwo" stepKey="createConfigChildProduct6"> - <requiredEntity createDataKey="createConfigProductAttribute"/> - <requiredEntity createDataKey="getConfigAttributeOption2"/> - </createData> - <createData entity="ConfigurableProductTwoOptions" stepKey="createConfigProductOption3"> - <requiredEntity createDataKey="createProduct3"/> - <requiredEntity createDataKey="createConfigProductAttribute"/> - <requiredEntity createDataKey="getConfigAttributeOption1"/> - <requiredEntity createDataKey="getConfigAttributeOption2"/> - </createData> - <createData entity="ConfigurableProductAddChild" stepKey="createConfigProductAddChild5"> - <requiredEntity createDataKey="createProduct3"/> - <requiredEntity createDataKey="createConfigChildProduct5"/> - </createData> - <createData entity="ConfigurableProductAddChild" stepKey="createConfigProductAddChild6"> - <requiredEntity createDataKey="createProduct3"/> - <requiredEntity createDataKey="createConfigChildProduct6"/> - </createData> - - <actionGroup ref="LoginAsAdmin" stepKey="login"/> - </before> - - <after> - <actionGroup ref="logout" stepKey="adminLogout"/> - <deleteData createDataKey="createConfigChildProduct1" stepKey="deleteConfigChildProduct1"/> - <deleteData createDataKey="createConfigChildProduct2" stepKey="deleteConfigChildProduct2"/> - <deleteData createDataKey="createConfigChildProduct3" stepKey="deleteConfigChildProduct3"/> - <deleteData createDataKey="createConfigChildProduct4" stepKey="deleteConfigChildProduct4"/> - <deleteData createDataKey="createConfigChildProduct5" stepKey="deleteConfigChildProduct5"/> - <deleteData createDataKey="createConfigChildProduct6" stepKey="deleteConfigChildProduct6"/> - <deleteData createDataKey="createConfigProductAttribute" stepKey="deleteConfigProductAttribute"/> - <deleteData createDataKey="createCategory" stepKey="deleteCategory"/> - </after> - - <!-- Search for prefix of the 3 products we created via api --> - <amOnPage url="{{AdminProductIndexPage.url}}" stepKey="goToProductList"/> - <waitForPageLoad stepKey="wait1"/> - <conditionalClick selector="{{AdminProductGridFilterSection.clearAll}}" dependentSelector="{{AdminProductGridFilterSection.clearAll}}" stepKey="clearAll" visible="true"/> - <actionGroup ref="SearchProductGridByKeywordActionGroup" stepKey="searchForProduct"> - <argument name="keyword" value="ApiConfigurableProduct.name"/> - </actionGroup> - - <!-- Select all, then delete --> - <click selector="{{AdminProductGridSection.multicheckDropdown}}" stepKey="openMulticheckDropdown"/> - <click selector="{{AdminProductGridSection.multicheckOption('Select All')}}" stepKey="selectAllProductInFilteredGrid"/> - <click selector="{{AdminProductGridSection.bulkActionDropdown}}" stepKey="clickActionDropdown"/> - <click selector="{{AdminProductGridSection.bulkActionOption('Delete')}}" stepKey="clickDeleteAction"/> - <waitForElementVisible selector="{{AdminProductGridConfirmActionSection.title}}" stepKey="waitForConfirmModal"/> - <click selector="{{AdminProductGridConfirmActionSection.ok}}" stepKey="confirmProductDelete"/> - - <!-- Should not see the records in the admin panel --> - <see selector="{{AdminProductMessagesSection.successMessage}}" userInput="A total of 3 record(s) have been deleted." stepKey="seeSuccessMsg"/> - <seeNumberOfElements selector="{{AdminProductGridSection.productGridRows}}" userInput="0" stepKey="seeNoResults"/> - - <!-- after delete, assert product pages are 404 --> - <amOnPage url="$$createProduct1.sku$$.html" stepKey="gotoStorefront1"/> - <waitForPageLoad stepKey="waitForProduct1"/> - <see selector="{{StorefrontProductInfoMainSection.productName}}" userInput="Whoops, our bad..." stepKey="seeWhoops1"/> - <dontSee selector="{{StorefrontProductInfoMainSection.productName}}" userInput="$$createProduct1.name$$" stepKey="dontSeeProduct1"/> - <amOnPage url="$$createProduct1.sku$$.html" stepKey="gotoStorefront2"/> - <waitForPageLoad stepKey="waitForProduct2"/> - <see selector="{{StorefrontProductInfoMainSection.productName}}" userInput="Whoops, our bad..." stepKey="seeWhoops2"/> - <dontSee selector="{{StorefrontProductInfoMainSection.productName}}" userInput="$$createProduct1.name$$" stepKey="dontSeeProduct2"/> - <amOnPage url="$$createProduct1.sku$$.html" stepKey="gotoStorefront3"/> - <waitForPageLoad stepKey="waitForProduct3"/> - <see selector="{{StorefrontProductInfoMainSection.productName}}" userInput="Whoops, our bad..." stepKey="seeWhoops3"/> - <dontSee selector="{{StorefrontProductInfoMainSection.productName}}" userInput="$$createProduct1.name$$" stepKey="dontSeeProduct3"/> - </test> -</tests> diff --git a/app/code/Magento/ConfigurableProduct/Test/Mftf/Test/AdminConfigurableProductDeleteTest/AdminConfigurableProductBulkDeleteTest.xml b/app/code/Magento/ConfigurableProduct/Test/Mftf/Test/AdminConfigurableProductDeleteTest/AdminConfigurableProductBulkDeleteTest.xml new file mode 100644 index 0000000000000..a7615d5565828 --- /dev/null +++ b/app/code/Magento/ConfigurableProduct/Test/Mftf/Test/AdminConfigurableProductDeleteTest/AdminConfigurableProductBulkDeleteTest.xml @@ -0,0 +1,175 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + /** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +--> +<tests xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/testSchema.xsd"> + <test name="AdminConfigurableProductBulkDeleteTest"> + <annotations> + <features value="ConfigurableProduct"/> + <stories value="Create, Read, Update, Delete"/> + <title value="admin should be able to mass delete configurable products"/> + <description value="admin should be able to mass delete configurable products"/> + <testCaseId value="MC-99"/> + <group value="ConfigurableProduct"/> + <severity value="BLOCKER"/> + </annotations> + + <before> + <!-- TODO: Parts of this should be converted to an actionGroup once MQE-993 is fixed. --> + <!-- Create shared category and attribute --> + <createData entity="ApiCategory" stepKey="createCategory"/> + <createData entity="productAttributeWithTwoOptions" stepKey="createConfigProductAttribute"/> + <createData entity="productAttributeOption1" stepKey="createConfigProductAttributeOption1"> + <requiredEntity createDataKey="createConfigProductAttribute"/> + </createData> + <createData entity="productAttributeOption2" stepKey="createConfigProductAttributeOption2"> + <requiredEntity createDataKey="createConfigProductAttribute"/> + </createData> + <createData entity="AddToDefaultSet" stepKey="createConfigAddToAttributeSet"> + <requiredEntity createDataKey="createConfigProductAttribute"/> + </createData> + <getData entity="ProductAttributeOptionGetter" index="1" stepKey="getConfigAttributeOption1"> + <requiredEntity createDataKey="createConfigProductAttribute"/> + </getData> + <getData entity="ProductAttributeOptionGetter" index="2" stepKey="getConfigAttributeOption2"> + <requiredEntity createDataKey="createConfigProductAttribute"/> + </getData> + + <!-- Create first of three configurable products --> + <createData entity="ApiConfigurableProduct" stepKey="createProduct1"> + <requiredEntity createDataKey="createCategory"/> + </createData> + <createData entity="ApiSimpleOne" stepKey="createConfigChildProduct1"> + <requiredEntity createDataKey="createConfigProductAttribute"/> + <requiredEntity createDataKey="getConfigAttributeOption1"/> + </createData> + <createData entity="ApiSimpleTwo" stepKey="createConfigChildProduct2"> + <requiredEntity createDataKey="createConfigProductAttribute"/> + <requiredEntity createDataKey="getConfigAttributeOption2"/> + </createData> + <createData entity="ConfigurableProductTwoOptions" stepKey="createConfigProductOption1"> + <requiredEntity createDataKey="createProduct1"/> + <requiredEntity createDataKey="createConfigProductAttribute"/> + <requiredEntity createDataKey="getConfigAttributeOption1"/> + <requiredEntity createDataKey="getConfigAttributeOption2"/> + </createData> + <createData entity="ConfigurableProductAddChild" stepKey="createConfigProductAddChild1"> + <requiredEntity createDataKey="createProduct1"/> + <requiredEntity createDataKey="createConfigChildProduct1"/> + </createData> + <createData entity="ConfigurableProductAddChild" stepKey="createConfigProductAddChild2"> + <requiredEntity createDataKey="createProduct1"/> + <requiredEntity createDataKey="createConfigChildProduct2"/> + </createData> + + <!-- Create second configurable product --> + <createData entity="ApiConfigurableProduct" stepKey="createProduct2"> + <requiredEntity createDataKey="createCategory"/> + </createData> + <createData entity="ApiSimpleOne" stepKey="createConfigChildProduct3"> + <requiredEntity createDataKey="createConfigProductAttribute"/> + <requiredEntity createDataKey="getConfigAttributeOption1"/> + </createData> + <createData entity="ApiSimpleTwo" stepKey="createConfigChildProduct4"> + <requiredEntity createDataKey="createConfigProductAttribute"/> + <requiredEntity createDataKey="getConfigAttributeOption2"/> + </createData> + <createData entity="ConfigurableProductTwoOptions" stepKey="createConfigProductOption2"> + <requiredEntity createDataKey="createProduct2"/> + <requiredEntity createDataKey="createConfigProductAttribute"/> + <requiredEntity createDataKey="getConfigAttributeOption1"/> + <requiredEntity createDataKey="getConfigAttributeOption2"/> + </createData> + <createData entity="ConfigurableProductAddChild" stepKey="createConfigProductAddChild3"> + <requiredEntity createDataKey="createProduct2"/> + <requiredEntity createDataKey="createConfigChildProduct3"/> + </createData> + <createData entity="ConfigurableProductAddChild" stepKey="createConfigProductAddChild4"> + <requiredEntity createDataKey="createProduct2"/> + <requiredEntity createDataKey="createConfigChildProduct4"/> + </createData> + + <!-- Create third configurable product --> + <createData entity="ApiConfigurableProduct" stepKey="createProduct3"> + <requiredEntity createDataKey="createCategory"/> + </createData> + <createData entity="ApiSimpleOne" stepKey="createConfigChildProduct5"> + <requiredEntity createDataKey="createConfigProductAttribute"/> + <requiredEntity createDataKey="getConfigAttributeOption1"/> + </createData> + <createData entity="ApiSimpleTwo" stepKey="createConfigChildProduct6"> + <requiredEntity createDataKey="createConfigProductAttribute"/> + <requiredEntity createDataKey="getConfigAttributeOption2"/> + </createData> + <createData entity="ConfigurableProductTwoOptions" stepKey="createConfigProductOption3"> + <requiredEntity createDataKey="createProduct3"/> + <requiredEntity createDataKey="createConfigProductAttribute"/> + <requiredEntity createDataKey="getConfigAttributeOption1"/> + <requiredEntity createDataKey="getConfigAttributeOption2"/> + </createData> + <createData entity="ConfigurableProductAddChild" stepKey="createConfigProductAddChild5"> + <requiredEntity createDataKey="createProduct3"/> + <requiredEntity createDataKey="createConfigChildProduct5"/> + </createData> + <createData entity="ConfigurableProductAddChild" stepKey="createConfigProductAddChild6"> + <requiredEntity createDataKey="createProduct3"/> + <requiredEntity createDataKey="createConfigChildProduct6"/> + </createData> + + <actionGroup ref="AdminLoginActionGroup" stepKey="login"/> + </before> + + <after> + <actionGroup ref="AdminLogoutActionGroup" stepKey="adminLogout"/> + <deleteData createDataKey="createConfigChildProduct1" stepKey="deleteConfigChildProduct1"/> + <deleteData createDataKey="createConfigChildProduct2" stepKey="deleteConfigChildProduct2"/> + <deleteData createDataKey="createConfigChildProduct3" stepKey="deleteConfigChildProduct3"/> + <deleteData createDataKey="createConfigChildProduct4" stepKey="deleteConfigChildProduct4"/> + <deleteData createDataKey="createConfigChildProduct5" stepKey="deleteConfigChildProduct5"/> + <deleteData createDataKey="createConfigChildProduct6" stepKey="deleteConfigChildProduct6"/> + <deleteData createDataKey="createConfigProductAttribute" stepKey="deleteConfigProductAttribute"/> + <deleteData createDataKey="createCategory" stepKey="deleteCategory"/> + + <!-- Reindex invalidated indices after product attribute has been created/deleted --> + <magentoCron groups="index" stepKey="reindexInvalidatedIndices"/> + </after> + + <!-- Search for prefix of the 3 products we created via api --> + <amOnPage url="{{AdminProductIndexPage.url}}" stepKey="goToProductList"/> + <waitForPageLoad stepKey="wait1"/> + <conditionalClick selector="{{AdminProductGridFilterSection.clearAll}}" dependentSelector="{{AdminProductGridFilterSection.clearAll}}" stepKey="clearAll" visible="true"/> + <actionGroup ref="SearchProductGridByKeywordActionGroup" stepKey="searchForProduct"> + <argument name="keyword" value="ApiConfigurableProduct.name"/> + </actionGroup> + + <!-- Select all, then delete --> + <click selector="{{AdminProductGridSection.multicheckDropdown}}" stepKey="openMulticheckDropdown"/> + <click selector="{{AdminProductGridSection.multicheckOption('Select All')}}" stepKey="selectAllProductInFilteredGrid"/> + <click selector="{{AdminProductGridSection.bulkActionDropdown}}" stepKey="clickActionDropdown"/> + <click selector="{{AdminProductGridSection.bulkActionOption('Delete')}}" stepKey="clickDeleteAction"/> + <waitForElementVisible selector="{{AdminProductGridConfirmActionSection.title}}" stepKey="waitForConfirmModal"/> + <click selector="{{AdminProductGridConfirmActionSection.ok}}" stepKey="confirmProductDelete"/> + + <!-- Should not see the records in the admin panel --> + <see selector="{{AdminProductMessagesSection.successMessage}}" userInput="A total of 3 record(s) have been deleted." stepKey="seeSuccessMsg"/> + <seeNumberOfElements selector="{{AdminProductGridSection.productGridRows}}" userInput="0" stepKey="seeNoResults"/> + + <!-- after delete, assert product pages are 404 --> + <amOnPage url="$$createProduct1.sku$$.html" stepKey="gotoStorefront1"/> + <waitForPageLoad stepKey="waitForProduct1"/> + <see selector="{{StorefrontProductInfoMainSection.productName}}" userInput="Whoops, our bad..." stepKey="seeWhoops1"/> + <dontSee selector="{{StorefrontProductInfoMainSection.productName}}" userInput="$$createProduct1.name$$" stepKey="dontSeeProduct1"/> + <amOnPage url="$$createProduct1.sku$$.html" stepKey="gotoStorefront2"/> + <waitForPageLoad stepKey="waitForProduct2"/> + <see selector="{{StorefrontProductInfoMainSection.productName}}" userInput="Whoops, our bad..." stepKey="seeWhoops2"/> + <dontSee selector="{{StorefrontProductInfoMainSection.productName}}" userInput="$$createProduct1.name$$" stepKey="dontSeeProduct2"/> + <amOnPage url="$$createProduct1.sku$$.html" stepKey="gotoStorefront3"/> + <waitForPageLoad stepKey="waitForProduct3"/> + <see selector="{{StorefrontProductInfoMainSection.productName}}" userInput="Whoops, our bad..." stepKey="seeWhoops3"/> + <dontSee selector="{{StorefrontProductInfoMainSection.productName}}" userInput="$$createProduct1.name$$" stepKey="dontSeeProduct3"/> + </test> +</tests> diff --git a/app/code/Magento/ConfigurableProduct/Test/Mftf/Test/AdminConfigurableProductDeleteTest/AdminConfigurableProductDeleteTest.xml b/app/code/Magento/ConfigurableProduct/Test/Mftf/Test/AdminConfigurableProductDeleteTest/AdminConfigurableProductDeleteTest.xml new file mode 100644 index 0000000000000..807ea69bb3958 --- /dev/null +++ b/app/code/Magento/ConfigurableProduct/Test/Mftf/Test/AdminConfigurableProductDeleteTest/AdminConfigurableProductDeleteTest.xml @@ -0,0 +1,103 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + /** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +--> +<tests xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/testSchema.xsd"> + <test name="AdminConfigurableProductDeleteTest"> + <annotations> + <features value="ConfigurableProduct"/> + <stories value="Create, Read, Update, Delete"/> + <title value="admin should be able to delete a configurable product"/> + <description value="admin should be able to delete a configurable product"/> + <testCaseId value="MC-87"/> + <group value="ConfigurableProduct"/> + <severity value="BLOCKER"/> + </annotations> + + <before> + <!-- TODO: This should be converted to an actionGroup once MQE-993 is fixed. --> + <createData entity="ApiCategory" stepKey="createCategory"/> + <createData entity="ApiConfigurableProduct" stepKey="createConfigProduct"> + <requiredEntity createDataKey="createCategory"/> + </createData> + <createData entity="productAttributeWithTwoOptions" stepKey="createConfigProductAttribute"/> + <createData entity="productAttributeOption1" stepKey="createConfigProductAttributeOption1"> + <requiredEntity createDataKey="createConfigProductAttribute"/> + </createData> + <createData entity="productAttributeOption2" stepKey="createConfigProductAttributeOption2"> + <requiredEntity createDataKey="createConfigProductAttribute"/> + </createData> + <createData entity="AddToDefaultSet" stepKey="createConfigAddToAttributeSet"> + <requiredEntity createDataKey="createConfigProductAttribute"/> + </createData> + <getData entity="ProductAttributeOptionGetter" index="1" stepKey="getConfigAttributeOption1"> + <requiredEntity createDataKey="createConfigProductAttribute"/> + </getData> + <getData entity="ProductAttributeOptionGetter" index="2" stepKey="getConfigAttributeOption2"> + <requiredEntity createDataKey="createConfigProductAttribute"/> + </getData> + <createData entity="ApiSimpleOne" stepKey="createConfigChildProduct1"> + <requiredEntity createDataKey="createConfigProductAttribute"/> + <requiredEntity createDataKey="getConfigAttributeOption1"/> + </createData> + <createData entity="ApiSimpleTwo" stepKey="createConfigChildProduct2"> + <requiredEntity createDataKey="createConfigProductAttribute"/> + <requiredEntity createDataKey="getConfigAttributeOption2"/> + </createData> + <createData entity="ConfigurableProductTwoOptions" stepKey="createConfigProductOption"> + <requiredEntity createDataKey="createConfigProduct"/> + <requiredEntity createDataKey="createConfigProductAttribute"/> + <requiredEntity createDataKey="getConfigAttributeOption1"/> + <requiredEntity createDataKey="getConfigAttributeOption2"/> + </createData> + <createData entity="ConfigurableProductAddChild" stepKey="createConfigProductAddChild1"> + <requiredEntity createDataKey="createConfigProduct"/> + <requiredEntity createDataKey="createConfigChildProduct1"/> + </createData> + <createData entity="ConfigurableProductAddChild" stepKey="createConfigProductAddChild2"> + <requiredEntity createDataKey="createConfigProduct"/> + <requiredEntity createDataKey="createConfigChildProduct2"/> + </createData> + </before> + + <after> + <actionGroup ref="AdminLogoutActionGroup" stepKey="adminLogout"/> + <deleteData createDataKey="createConfigChildProduct1" stepKey="deleteConfigChildProduct1"/> + <deleteData createDataKey="createConfigChildProduct2" stepKey="deleteConfigChildProduct2"/> + <deleteData createDataKey="createConfigProductAttribute" stepKey="deleteConfigProductAttribute"/> + + <!-- Reindex invalidated indices after product attribute has been created/deleted --> + <magentoCron groups="index" stepKey="reindexInvalidatedIndices"/> + </after> + + <!-- assert product visible in storefront --> + <amOnPage url="$$createConfigProduct.sku$$.html" stepKey="gotoStorefront1"/> + <waitForPageLoad stepKey="wait1"/> + <see selector="{{StorefrontProductInfoMainSection.productName}}" userInput="$$createConfigProduct.name$$" stepKey="seeProduct"/> + + <!-- go to admin and delete --> + <actionGroup ref="AdminLoginActionGroup" stepKey="login"/> + <amOnPage url="{{AdminProductIndexPage.url}}" stepKey="goToProductList"/> + <waitForPageLoad stepKey="wait2"/> + <conditionalClick selector="{{AdminProductGridFilterSection.clearAll}}" dependentSelector="{{AdminProductGridFilterSection.clearAll}}" stepKey="clearAll" visible="true"/> + <actionGroup ref="SearchProductGridByKeywordActionGroup" stepKey="searchForProduct"> + <argument name="keyword" value="ApiConfigurableProduct.name"/> + </actionGroup> + <click selector="label.data-grid-checkbox-cell-inner" stepKey="clickCheckbox"/> + <click selector="{{AdminProductGridSection.bulkActionDropdown}}" stepKey="clickActionDropdown"/> + <click selector="{{AdminProductGridSection.bulkActionOption('Delete')}}" stepKey="clickDeleteAction"/> + <waitForElementVisible selector="{{AdminProductGridConfirmActionSection.title}}" stepKey="waitForConfirmModal"/> + <click selector="{{AdminProductGridConfirmActionSection.ok}}" stepKey="confirmProductDelete"/> + <see selector="{{AdminProductMessagesSection.successMessage}}" userInput="A total of 1 record(s) have been deleted." stepKey="seeSuccessMsg"/> + + <!-- after delete, assert product page is 404 --> + <amOnPage url="$$createConfigProduct.sku$$.html" stepKey="gotoStorefront2"/> + <waitForPageLoad stepKey="wait3"/> + <see selector="{{StorefrontProductInfoMainSection.productName}}" userInput="Whoops, our bad..." stepKey="seeWhoops"/> + <dontSee selector="{{StorefrontProductInfoMainSection.productName}}" userInput="$$createConfigProduct.name$$" stepKey="dontSeeProduct"/> + </test> +</tests> diff --git a/app/code/Magento/ConfigurableProduct/Test/Mftf/Test/AdminConfigurableProductLongSkuTest.xml b/app/code/Magento/ConfigurableProduct/Test/Mftf/Test/AdminConfigurableProductLongSkuTest.xml index baea299581052..10cdcea2855d6 100644 --- a/app/code/Magento/ConfigurableProduct/Test/Mftf/Test/AdminConfigurableProductLongSkuTest.xml +++ b/app/code/Magento/ConfigurableProduct/Test/Mftf/Test/AdminConfigurableProductLongSkuTest.xml @@ -39,7 +39,7 @@ </getData> <!--Create Category--> <createData entity="ApiCategory" stepKey="createCategory"/> - <actionGroup ref="LoginAsAdmin" stepKey="loginAsAdmin1"/> + <actionGroup ref="AdminLoginActionGroup" stepKey="loginAsAdmin1"/> </before> <after> @@ -52,7 +52,10 @@ <deleteData createDataKey="createConfigProductAttribute" stepKey="deleteConfigProductAttribute"/> <!--Clean up category--> <deleteData createDataKey="createCategory" stepKey="deleteCategory"/> - <actionGroup ref="logout" stepKey="adminLogout"/> + <actionGroup ref="AdminLogoutActionGroup" stepKey="adminLogout"/> + + <!-- Reindex invalidated indices after product attribute has been created/deleted --> + <magentoCron groups="index" stepKey="reindexInvalidatedIndices"/> </after> <!--Create a configurable product with long name and sku--> diff --git a/app/code/Magento/ConfigurableProduct/Test/Mftf/Test/AdminConfigurableProductOutOfStockTest.xml b/app/code/Magento/ConfigurableProduct/Test/Mftf/Test/AdminConfigurableProductOutOfStockTest.xml deleted file mode 100644 index 6bba4aa6b43ce..0000000000000 --- a/app/code/Magento/ConfigurableProduct/Test/Mftf/Test/AdminConfigurableProductOutOfStockTest.xml +++ /dev/null @@ -1,346 +0,0 @@ -<?xml version="1.0" encoding="UTF-8"?> -<!-- - /** - * Copyright © Magento, Inc. All rights reserved. - * See COPYING.txt for license details. - */ ---> - -<tests xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" - xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/testSchema.xsd"> - <test name="AdminConfigurableProductChildrenOutOfStockTest"> - <annotations> - <features value="ConfigurableProduct"/> - <stories value="Product visibility when in stock/out of stock"/> - <title value="Configurable Product goes 'Out of Stock' if all associated Simple Products are 'Out of Stock'"/> - <description value="Configurable Product goes 'Out of Stock' if all associated Simple Products are 'Out of Stock'"/> - <severity value="CRITICAL"/> - <testCaseId value="MC-181"/> - <group value="ConfigurableProduct"/> - </annotations> - <before> - <!-- TODO: This should be converted to an actionGroup once MQE-993 is fixed. --> - <!-- Create the category to put the product in --> - <createData entity="ApiCategory" stepKey="createCategory"/> - - <!-- Create the configurable product based on the data in the /data folder --> - <createData entity="ApiConfigurableProduct" stepKey="createConfigProduct"> - <requiredEntity createDataKey="createCategory"/> - </createData> - - <!-- Make the configurable product have two options, that are children of the default attribute set --> - <createData entity="productAttributeWithTwoOptions" stepKey="createConfigProductAttribute"/> - <createData entity="productAttributeOption1" stepKey="createConfigProductAttributeOption1"> - <requiredEntity createDataKey="createConfigProductAttribute"/> - </createData> - <createData entity="productAttributeOption2" stepKey="createConfigProductAttributeOption2"> - <requiredEntity createDataKey="createConfigProductAttribute"/> - </createData> - <createData entity="AddToDefaultSet" stepKey="createConfigAddToAttributeSet"> - <requiredEntity createDataKey="createConfigProductAttribute"/> - </createData> - <getData entity="ProductAttributeOptionGetter" index="1" stepKey="getConfigAttributeOption1"> - <requiredEntity createDataKey="createConfigProductAttribute"/> - </getData> - <getData entity="ProductAttributeOptionGetter" index="2" stepKey="getConfigAttributeOption2"> - <requiredEntity createDataKey="createConfigProductAttribute"/> - </getData> - - <!-- Create the 2 children that will be a part of the configurable product --> - <createData entity="ApiSimpleOne" stepKey="createConfigChildProduct1"> - <requiredEntity createDataKey="createConfigProductAttribute"/> - <requiredEntity createDataKey="getConfigAttributeOption1"/> - </createData> - <createData entity="ApiSimpleTwo" stepKey="createConfigChildProduct2"> - <requiredEntity createDataKey="createConfigProductAttribute"/> - <requiredEntity createDataKey="getConfigAttributeOption2"/> - </createData> - - <!-- Assign the two products to the configurable product --> - <createData entity="ConfigurableProductTwoOptions" stepKey="createConfigProductOption"> - <requiredEntity createDataKey="createConfigProduct"/> - <requiredEntity createDataKey="createConfigProductAttribute"/> - <requiredEntity createDataKey="getConfigAttributeOption1"/> - <requiredEntity createDataKey="getConfigAttributeOption2"/> - </createData> - <createData entity="ConfigurableProductAddChild" stepKey="createConfigProductAddChild1"> - <requiredEntity createDataKey="createConfigProduct"/> - <requiredEntity createDataKey="createConfigChildProduct1"/> - </createData> - <createData entity="ConfigurableProductAddChild" stepKey="createConfigProductAddChild2"> - <requiredEntity createDataKey="createConfigProduct"/> - <requiredEntity createDataKey="createConfigChildProduct2"/> - </createData> - - <!-- log in --> - <actionGroup ref="LoginAsAdmin" stepKey="login"/> - </before> - - <after> - <actionGroup ref="logout" stepKey="adminLogout"/> - <deleteData createDataKey="createCategory" stepKey="deleteCategory"/> - <deleteData createDataKey="createConfigProduct" stepKey="deleteConfigProduct"/> - <deleteData createDataKey="createConfigChildProduct1" stepKey="deleteConfigChildProduct1"/> - <deleteData createDataKey="createConfigChildProduct2" stepKey="deleteConfigChildProduct2"/> - <deleteData createDataKey="createConfigProductAttribute" stepKey="deleteConfigProductAttribute"/> - </after> - - <!-- Check to make sure that the configurable product shows up as in stock --> - <amOnPage url="/{{ApiConfigurableProduct.urlKey}}2.html" stepKey="goToConfigProductPage"/> - <waitForPageLoad stepKey="waitForStoreFrontLoad"/> - <see stepKey="checkForOutOfStock" selector="{{StorefrontProductInfoMainSection.stockIndication}}" userInput="IN STOCK" /> - - <!-- Find the first simple product that we just created using the product grid and go to its page--> - <amOnPage url="{{AdminProductIndexPage.url}}" stepKey="visitAdminProductPage"/> - <waitForPageLoad stepKey="waitForAdminProductGridLoad"/> - <conditionalClick selector="{{AdminProductGridFilterSection.clearFilters}}" dependentSelector="{{AdminProductGridFilterSection.clearFilters}}" visible="true" stepKey="clickClearFiltersInitial"/> - <actionGroup ref="FilterProductGridBySkuActionGroup" stepKey="findCreatedProduct"> - <argument name="product" value="ApiSimpleOne"/> - </actionGroup> - <waitForPageLoad stepKey="waitForFiltersToBeApplied"/> - <click selector="{{AdminProductGridSection.firstRow}}" stepKey="clickOnProductPage"/> - <waitForPageLoad stepKey="waitForProductPageLoad"/> - - <!-- Edit the quantity of the simple first product as 0 --> - <fillField selector="{{AdminProductFormSection.productQuantity}}" userInput="0" stepKey="fillProductQuantity"/> - <click selector="{{AdminProductFormActionSection.saveButton}}" stepKey="clickSaveProduct"/> - - <!-- Check to make sure that the configurable product shows up as in stock --> - <amOnPage url="/{{ApiConfigurableProduct.urlKey}}2.html" stepKey="goToConfigProductPage2"/> - <waitForPageLoad stepKey="waitForStoreFrontLoad2"/> - <see stepKey="checkForOutOfStock2" selector="{{StorefrontProductInfoMainSection.stockIndication}}" userInput="IN STOCK"/> - - <!-- Find the second simple product that we just created using the product grid and go to its page--> - <amOnPage url="{{AdminProductIndexPage.url}}" stepKey="visitAdminProductPage2"/> - <waitForPageLoad stepKey="waitForAdminProductGridLoad2"/> - <conditionalClick selector="{{AdminProductGridFilterSection.clearFilters}}" dependentSelector="{{AdminProductGridFilterSection.clearFilters}}" visible="true" stepKey="clickClearFiltersInitial2"/> - <actionGroup ref="FilterProductGridBySkuActionGroup" stepKey="findCreatedProduct2"> - <argument name="product" value="ApiSimpleTwo"/> - </actionGroup> - <waitForPageLoad stepKey="waitForFiltersToBeApplied2"/> - <click selector="{{AdminProductGridSection.firstRow}}" stepKey="clickOnProductPage2"/> - <waitForPageLoad stepKey="waitForProductPageLoad2"/> - - <!-- Edit the quantity of the second simple product as 0 --> - <fillField selector="{{AdminProductFormSection.productQuantity}}" userInput="0" stepKey="fillProductQuantity2"/> - <click selector="{{AdminProductFormActionSection.saveButton}}" stepKey="clickSaveProduct2"/> - - <!-- Check to make sure that the configurable product shows up as out of stock --> - <amOnPage url="/{{ApiConfigurableProduct.urlKey}}2.html" stepKey="goToConfigProductPage3"/> - <waitForPageLoad stepKey="waitForStoreFrontLoad3"/> - <see stepKey="checkForOutOfStock3" selector="{{StorefrontProductInfoMainSection.stockIndication}}" userInput="OUT OF STOCK"/> - </test> - - <test name="AdminConfigurableProductOutOfStockTestDeleteChildren"> - <annotations> - <features value="ConfigurableProduct"/> - <stories value="Product visibility when in stock/out of stock"/> - <title value="Configurable Product goes 'Out of Stock' if all associated Simple Products are deleted"/> - <description value="Configurable Product goes 'Out of Stock' if all associated Simple Products are deleted"/> - <severity value="CRITICAL"/> - <testCaseId value="MC-3042"/> - <group value="ConfigurableProduct"/> - </annotations> - <before> - <!-- TODO: This should be converted to an actionGroup once MQE-993 is fixed. --> - <!-- Create the category to put the product in --> - <createData entity="ApiCategory" stepKey="createCategory"/> - - <!-- Create the configurable product based on the data in the /data folder --> - <createData entity="ApiConfigurableProduct" stepKey="createConfigProduct"> - <requiredEntity createDataKey="createCategory"/> - </createData> - - <!-- Make the configurable product have two options, that are children of the default attribute set --> - <createData entity="productAttributeWithTwoOptions" stepKey="createConfigProductAttribute"/> - <createData entity="productAttributeOption1" stepKey="createConfigProductAttributeOption1"> - <requiredEntity createDataKey="createConfigProductAttribute"/> - </createData> - <createData entity="productAttributeOption2" stepKey="createConfigProductAttributeOption2"> - <requiredEntity createDataKey="createConfigProductAttribute"/> - </createData> - <createData entity="AddToDefaultSet" stepKey="createConfigAddToAttributeSet"> - <requiredEntity createDataKey="createConfigProductAttribute"/> - </createData> - <getData entity="ProductAttributeOptionGetter" index="1" stepKey="getConfigAttributeOption1"> - <requiredEntity createDataKey="createConfigProductAttribute"/> - </getData> - <getData entity="ProductAttributeOptionGetter" index="2" stepKey="getConfigAttributeOption2"> - <requiredEntity createDataKey="createConfigProductAttribute"/> - </getData> - - <!-- Create the 2 children that will be a part of the configurable product --> - <createData entity="ApiSimpleOne" stepKey="createConfigChildProduct1"> - <requiredEntity createDataKey="createConfigProductAttribute"/> - <requiredEntity createDataKey="getConfigAttributeOption1"/> - </createData> - <createData entity="ApiSimpleTwo" stepKey="createConfigChildProduct2"> - <requiredEntity createDataKey="createConfigProductAttribute"/> - <requiredEntity createDataKey="getConfigAttributeOption2"/> - </createData> - - <!-- Assign the two products to the configurable product --> - <createData entity="ConfigurableProductTwoOptions" stepKey="createConfigProductOption"> - <requiredEntity createDataKey="createConfigProduct"/> - <requiredEntity createDataKey="createConfigProductAttribute"/> - <requiredEntity createDataKey="getConfigAttributeOption1"/> - <requiredEntity createDataKey="getConfigAttributeOption2"/> - </createData> - <createData entity="ConfigurableProductAddChild" stepKey="createConfigProductAddChild1"> - <requiredEntity createDataKey="createConfigProduct"/> - <requiredEntity createDataKey="createConfigChildProduct1"/> - </createData> - <createData entity="ConfigurableProductAddChild" stepKey="createConfigProductAddChild2"> - <requiredEntity createDataKey="createConfigProduct"/> - <requiredEntity createDataKey="createConfigChildProduct2"/> - </createData> - - <!-- log in --> - <actionGroup ref="LoginAsAdmin" stepKey="login"/> - </before> - - <after> - <actionGroup ref="logout" stepKey="adminLogout"/> - <deleteData createDataKey="createCategory" stepKey="deleteCategory"/> - <deleteData createDataKey="createConfigProduct" stepKey="deleteConfigProduct"/> - <deleteData createDataKey="createConfigProductAttribute" stepKey="deleteConfigProductAttribute"/> - </after> - - <!-- Check to make sure that the configurable product shows up as in stock --> - <amOnPage url="/{{ApiConfigurableProduct.urlKey}}2.html" stepKey="goToConfigProductPage"/> - <waitForPageLoad stepKey="waitForStoreFrontLoad"/> - <see stepKey="checkForOutOfStock" selector="{{StorefrontProductInfoMainSection.stockIndication}}" userInput="IN STOCK" /> - - <!-- Delete the first simple product --> - <actionGroup stepKey="deleteProduct1" ref="DeleteProductBySkuActionGroup"> - <argument name="sku" value="{{ApiSimpleOne.sku}}"/> - </actionGroup> - - <!-- Check to make sure that the configurable product shows up as in stock --> - <amOnPage url="/{{ApiConfigurableProduct.urlKey}}2.html" stepKey="goToConfigProductPage2"/> - <waitForPageLoad stepKey="waitForStoreFrontLoad2"/> - <see stepKey="checkForOutOfStock2" selector="{{StorefrontProductInfoMainSection.stockIndication}}" userInput="IN STOCK"/> - - <!-- Delete the second simple product --> - <actionGroup stepKey="deleteProduct2" ref="DeleteProductBySkuActionGroup"> - <argument name="sku" value="{{ApiSimpleTwo.sku}}"/> - </actionGroup> - - <!-- Check to make sure that the configurable product shows up as out of stock --> - <amOnPage url="/{{ApiConfigurableProduct.urlKey}}2.html" stepKey="goToConfigProductPage3"/> - <waitForPageLoad stepKey="waitForStoreFrontLoad3"/> - <see stepKey="checkForOutOfStock3" selector="{{StorefrontProductInfoMainSection.stockIndication}}" userInput="OUT OF STOCK"/> - </test> - - <test name="AdminConfigurableProductOutOfStockAndDeleteCombinationTest"> - <annotations> - <features value="ConfigurableProduct"/> - <stories value="Product visibility when in stock/out of stock"/> - <title value="Configurable Product goes 'Out of Stock' if all associated Simple Products are a combination of 'Out of Stock' and deleted"/> - <description value="Configurable Product goes 'Out of Stock' if all associated Simple Products are a combination of 'Out of Stock' and deleted"/> - <severity value="CRITICAL"/> - <testCaseId value="MC-3046"/> - <group value="ConfigurableProduct"/> - </annotations> - <before> - <!-- TODO: This should be converted to an actionGroup once MQE-993 is fixed. --> - <!-- Create the category to put the product in --> - <createData entity="ApiCategory" stepKey="createCategory"/> - - <!-- Create the configurable product based on the data in the /data folder --> - <createData entity="ApiConfigurableProduct" stepKey="createConfigProduct"> - <requiredEntity createDataKey="createCategory"/> - </createData> - - <!-- Make the configurable product have two options, that are children of the default attribute set --> - <createData entity="productAttributeWithTwoOptions" stepKey="createConfigProductAttribute"/> - <createData entity="productAttributeOption1" stepKey="createConfigProductAttributeOption1"> - <requiredEntity createDataKey="createConfigProductAttribute"/> - </createData> - <createData entity="productAttributeOption2" stepKey="createConfigProductAttributeOption2"> - <requiredEntity createDataKey="createConfigProductAttribute"/> - </createData> - <createData entity="AddToDefaultSet" stepKey="createConfigAddToAttributeSet"> - <requiredEntity createDataKey="createConfigProductAttribute"/> - </createData> - <getData entity="ProductAttributeOptionGetter" index="1" stepKey="getConfigAttributeOption1"> - <requiredEntity createDataKey="createConfigProductAttribute"/> - </getData> - <getData entity="ProductAttributeOptionGetter" index="2" stepKey="getConfigAttributeOption2"> - <requiredEntity createDataKey="createConfigProductAttribute"/> - </getData> - - <!-- Create the 2 children that will be a part of the configurable product --> - <createData entity="ApiSimpleOne" stepKey="createConfigChildProduct1"> - <requiredEntity createDataKey="createConfigProductAttribute"/> - <requiredEntity createDataKey="getConfigAttributeOption1"/> - </createData> - <createData entity="ApiSimpleTwo" stepKey="createConfigChildProduct2"> - <requiredEntity createDataKey="createConfigProductAttribute"/> - <requiredEntity createDataKey="getConfigAttributeOption2"/> - </createData> - - <!-- Assign the two products to the configurable product --> - <createData entity="ConfigurableProductTwoOptions" stepKey="createConfigProductOption"> - <requiredEntity createDataKey="createConfigProduct"/> - <requiredEntity createDataKey="createConfigProductAttribute"/> - <requiredEntity createDataKey="getConfigAttributeOption1"/> - <requiredEntity createDataKey="getConfigAttributeOption2"/> - </createData> - <createData entity="ConfigurableProductAddChild" stepKey="createConfigProductAddChild1"> - <requiredEntity createDataKey="createConfigProduct"/> - <requiredEntity createDataKey="createConfigChildProduct1"/> - </createData> - <createData entity="ConfigurableProductAddChild" stepKey="createConfigProductAddChild2"> - <requiredEntity createDataKey="createConfigProduct"/> - <requiredEntity createDataKey="createConfigChildProduct2"/> - </createData> - - <!-- log in --> - <actionGroup ref="LoginAsAdmin" stepKey="login"/> - </before> - - <after> - <actionGroup ref="logout" stepKey="adminLogout"/> - <deleteData createDataKey="createCategory" stepKey="deleteCategory"/> - <deleteData createDataKey="createConfigProduct" stepKey="deleteConfigProduct"/> - <deleteData createDataKey="createConfigChildProduct2" stepKey="deleteConfigChildProduct2"/> - <deleteData createDataKey="createConfigProductAttribute" stepKey="deleteConfigProductAttribute"/> - </after> - - <!-- Check to make sure that the configurable product shows up as in stock --> - <amOnPage url="/{{ApiConfigurableProduct.urlKey}}2.html" stepKey="goToConfigProductPage"/> - <waitForPageLoad stepKey="waitForStoreFrontLoad"/> - <see stepKey="checkForOutOfStock" selector="{{StorefrontProductInfoMainSection.stockIndication}}" userInput="IN STOCK" /> - - <!-- Delete the first simple product --> - <actionGroup stepKey="deleteProduct1" ref="DeleteProductBySkuActionGroup"> - <argument name="sku" value="{{ApiSimpleOne.sku}}"/> - </actionGroup> - - <!-- Check to make sure that the configurable product shows up as in stock --> - <amOnPage url="/{{ApiConfigurableProduct.urlKey}}2.html" stepKey="goToConfigProductPage2"/> - <waitForPageLoad stepKey="waitForStoreFrontLoad2"/> - <see stepKey="checkForOutOfStock2" selector="{{StorefrontProductInfoMainSection.stockIndication}}" userInput="IN STOCK"/> - - <!-- Find the second simple product that we just created using the product grid and go to its page--> - <amOnPage url="{{AdminProductIndexPage.url}}" stepKey="visitAdminProductPage2"/> - <waitForPageLoad stepKey="waitForAdminProductGridLoad2"/> - <conditionalClick selector="{{AdminProductGridFilterSection.clearFilters}}" dependentSelector="{{AdminProductGridFilterSection.clearFilters}}" visible="true" stepKey="clickClearFiltersInitial2"/> - <actionGroup ref="FilterProductGridBySkuActionGroup" stepKey="findCreatedProduct2"> - <argument name="product" value="ApiSimpleTwo"/> - </actionGroup> - <waitForPageLoad stepKey="waitForFiltersToBeApplied2"/> - <click selector="{{AdminProductGridSection.firstRow}}" stepKey="clickOnProductPage2"/> - <waitForPageLoad stepKey="waitForProductPageLoad2"/> - - <!-- Edit the quantity of the second simple product as 0 --> - <fillField selector="{{AdminProductFormSection.productQuantity}}" userInput="0" stepKey="fillProductQuantity2"/> - <click selector="{{AdminProductFormActionSection.saveButton}}" stepKey="clickSaveProduct2"/> - - <!-- Check to make sure that the configurable product shows up as out of stock --> - <amOnPage url="/{{ApiConfigurableProduct.urlKey}}2.html" stepKey="goToConfigProductPage3"/> - <waitForPageLoad stepKey="waitForStoreFrontLoad3"/> - <see stepKey="checkForOutOfStock3" selector="{{StorefrontProductInfoMainSection.stockIndication}}" userInput="OUT OF STOCK"/> - </test> -</tests> diff --git a/app/code/Magento/ConfigurableProduct/Test/Mftf/Test/AdminConfigurableProductOutOfStockTest/AdminConfigurableProductChildrenOutOfStockTest.xml b/app/code/Magento/ConfigurableProduct/Test/Mftf/Test/AdminConfigurableProductOutOfStockTest/AdminConfigurableProductChildrenOutOfStockTest.xml new file mode 100644 index 0000000000000..8d2f80ef262fd --- /dev/null +++ b/app/code/Magento/ConfigurableProduct/Test/Mftf/Test/AdminConfigurableProductOutOfStockTest/AdminConfigurableProductChildrenOutOfStockTest.xml @@ -0,0 +1,135 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + /** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +--> +<tests xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/testSchema.xsd"> + <test name="AdminConfigurableProductChildrenOutOfStockTest"> + <annotations> + <features value="ConfigurableProduct"/> + <stories value="Product visibility when in stock/out of stock"/> + <title value="Configurable Product goes 'Out of Stock' if all associated Simple Products are 'Out of Stock'"/> + <description value="Configurable Product goes 'Out of Stock' if all associated Simple Products are 'Out of Stock'"/> + <severity value="CRITICAL"/> + <testCaseId value="MC-181"/> + <group value="ConfigurableProduct"/> + </annotations> + <before> + <!-- TODO: This should be converted to an actionGroup once MQE-993 is fixed. --> + <!-- Create the category to put the product in --> + <createData entity="ApiCategory" stepKey="createCategory"/> + + <!-- Create the configurable product based on the data in the /data folder --> + <createData entity="ApiConfigurableProduct" stepKey="createConfigProduct"> + <requiredEntity createDataKey="createCategory"/> + </createData> + + <!-- Make the configurable product have two options, that are children of the default attribute set --> + <createData entity="productAttributeWithTwoOptions" stepKey="createConfigProductAttribute"/> + <createData entity="productAttributeOption1" stepKey="createConfigProductAttributeOption1"> + <requiredEntity createDataKey="createConfigProductAttribute"/> + </createData> + <createData entity="productAttributeOption2" stepKey="createConfigProductAttributeOption2"> + <requiredEntity createDataKey="createConfigProductAttribute"/> + </createData> + <createData entity="AddToDefaultSet" stepKey="createConfigAddToAttributeSet"> + <requiredEntity createDataKey="createConfigProductAttribute"/> + </createData> + <getData entity="ProductAttributeOptionGetter" index="1" stepKey="getConfigAttributeOption1"> + <requiredEntity createDataKey="createConfigProductAttribute"/> + </getData> + <getData entity="ProductAttributeOptionGetter" index="2" stepKey="getConfigAttributeOption2"> + <requiredEntity createDataKey="createConfigProductAttribute"/> + </getData> + + <!-- Create the 2 children that will be a part of the configurable product --> + <createData entity="ApiSimpleOne" stepKey="createConfigChildProduct1"> + <requiredEntity createDataKey="createConfigProductAttribute"/> + <requiredEntity createDataKey="getConfigAttributeOption1"/> + </createData> + <createData entity="ApiSimpleTwo" stepKey="createConfigChildProduct2"> + <requiredEntity createDataKey="createConfigProductAttribute"/> + <requiredEntity createDataKey="getConfigAttributeOption2"/> + </createData> + + <!-- Assign the two products to the configurable product --> + <createData entity="ConfigurableProductTwoOptions" stepKey="createConfigProductOption"> + <requiredEntity createDataKey="createConfigProduct"/> + <requiredEntity createDataKey="createConfigProductAttribute"/> + <requiredEntity createDataKey="getConfigAttributeOption1"/> + <requiredEntity createDataKey="getConfigAttributeOption2"/> + </createData> + <createData entity="ConfigurableProductAddChild" stepKey="createConfigProductAddChild1"> + <requiredEntity createDataKey="createConfigProduct"/> + <requiredEntity createDataKey="createConfigChildProduct1"/> + </createData> + <createData entity="ConfigurableProductAddChild" stepKey="createConfigProductAddChild2"> + <requiredEntity createDataKey="createConfigProduct"/> + <requiredEntity createDataKey="createConfigChildProduct2"/> + </createData> + + <!-- log in --> + <actionGroup ref="AdminLoginActionGroup" stepKey="login"/> + </before> + + <after> + <actionGroup ref="AdminLogoutActionGroup" stepKey="adminLogout"/> + <deleteData createDataKey="createCategory" stepKey="deleteCategory"/> + <deleteData createDataKey="createConfigProduct" stepKey="deleteConfigProduct"/> + <deleteData createDataKey="createConfigChildProduct1" stepKey="deleteConfigChildProduct1"/> + <deleteData createDataKey="createConfigChildProduct2" stepKey="deleteConfigChildProduct2"/> + <deleteData createDataKey="createConfigProductAttribute" stepKey="deleteConfigProductAttribute"/> + + <!-- Reindex invalidated indices after product attribute has been created/deleted --> + <magentoCron groups="index" stepKey="reindexInvalidatedIndices"/> + </after> + + <!-- Check to make sure that the configurable product shows up as in stock --> + <amOnPage url="/{{ApiConfigurableProduct.urlKey}}2.html" stepKey="goToConfigProductPage"/> + <waitForPageLoad stepKey="waitForStoreFrontLoad"/> + <see stepKey="checkForOutOfStock" selector="{{StorefrontProductInfoMainSection.stockIndication}}" userInput="IN STOCK"/> + + <!-- Find the first simple product that we just created using the product grid and go to its page--> + <amOnPage url="{{AdminProductIndexPage.url}}" stepKey="visitAdminProductPage"/> + <waitForPageLoad stepKey="waitForAdminProductGridLoad"/> + <conditionalClick selector="{{AdminProductGridFilterSection.clearFilters}}" dependentSelector="{{AdminProductGridFilterSection.clearFilters}}" visible="true" stepKey="clickClearFiltersInitial"/> + <actionGroup ref="FilterProductGridBySkuActionGroup" stepKey="findCreatedProduct"> + <argument name="product" value="ApiSimpleOne"/> + </actionGroup> + <waitForPageLoad stepKey="waitForFiltersToBeApplied"/> + <click selector="{{AdminProductGridSection.firstRow}}" stepKey="clickOnProductPage"/> + <waitForPageLoad stepKey="waitForProductPageLoad"/> + + <!-- Edit the quantity of the simple first product as 0 --> + <fillField selector="{{AdminProductFormSection.productQuantity}}" userInput="0" stepKey="fillProductQuantity"/> + <click selector="{{AdminProductFormActionSection.saveButton}}" stepKey="clickSaveProduct"/> + + <!-- Check to make sure that the configurable product shows up as in stock --> + <amOnPage url="/{{ApiConfigurableProduct.urlKey}}2.html" stepKey="goToConfigProductPage2"/> + <waitForPageLoad stepKey="waitForStoreFrontLoad2"/> + <see stepKey="checkForOutOfStock2" selector="{{StorefrontProductInfoMainSection.stockIndication}}" userInput="IN STOCK"/> + + <!-- Find the second simple product that we just created using the product grid and go to its page--> + <amOnPage url="{{AdminProductIndexPage.url}}" stepKey="visitAdminProductPage2"/> + <waitForPageLoad stepKey="waitForAdminProductGridLoad2"/> + <conditionalClick selector="{{AdminProductGridFilterSection.clearFilters}}" dependentSelector="{{AdminProductGridFilterSection.clearFilters}}" visible="true" stepKey="clickClearFiltersInitial2"/> + <actionGroup ref="FilterProductGridBySkuActionGroup" stepKey="findCreatedProduct2"> + <argument name="product" value="ApiSimpleTwo"/> + </actionGroup> + <waitForPageLoad stepKey="waitForFiltersToBeApplied2"/> + <click selector="{{AdminProductGridSection.firstRow}}" stepKey="clickOnProductPage2"/> + <waitForPageLoad stepKey="waitForProductPageLoad2"/> + + <!-- Edit the quantity of the second simple product as 0 --> + <fillField selector="{{AdminProductFormSection.productQuantity}}" userInput="0" stepKey="fillProductQuantity2"/> + <click selector="{{AdminProductFormActionSection.saveButton}}" stepKey="clickSaveProduct2"/> + + <!-- Check to make sure that the configurable product shows up as out of stock --> + <amOnPage url="/{{ApiConfigurableProduct.urlKey}}2.html" stepKey="goToConfigProductPage3"/> + <waitForPageLoad stepKey="waitForStoreFrontLoad3"/> + <see stepKey="checkForOutOfStock3" selector="{{StorefrontProductInfoMainSection.stockIndication}}" userInput="OUT OF STOCK"/> + </test> +</tests> diff --git a/app/code/Magento/ConfigurableProduct/Test/Mftf/Test/AdminConfigurableProductOutOfStockTest/AdminConfigurableProductOutOfStockAndDeleteCombinationTest.xml b/app/code/Magento/ConfigurableProduct/Test/Mftf/Test/AdminConfigurableProductOutOfStockTest/AdminConfigurableProductOutOfStockAndDeleteCombinationTest.xml new file mode 100644 index 0000000000000..3121725c23fe9 --- /dev/null +++ b/app/code/Magento/ConfigurableProduct/Test/Mftf/Test/AdminConfigurableProductOutOfStockTest/AdminConfigurableProductOutOfStockAndDeleteCombinationTest.xml @@ -0,0 +1,124 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + /** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +--> +<tests xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/testSchema.xsd"> + <test name="AdminConfigurableProductOutOfStockAndDeleteCombinationTest"> + <annotations> + <features value="ConfigurableProduct"/> + <stories value="Product visibility when in stock/out of stock"/> + <title value="Configurable Product goes 'Out of Stock' if all associated Simple Products are a combination of 'Out of Stock' and deleted"/> + <description value="Configurable Product goes 'Out of Stock' if all associated Simple Products are a combination of 'Out of Stock' and deleted"/> + <severity value="CRITICAL"/> + <testCaseId value="MC-3046"/> + <group value="ConfigurableProduct"/> + </annotations> + <before> + <!-- TODO: This should be converted to an actionGroup once MQE-993 is fixed. --> + <!-- Create the category to put the product in --> + <createData entity="ApiCategory" stepKey="createCategory"/> + + <!-- Create the configurable product based on the data in the /data folder --> + <createData entity="ApiConfigurableProduct" stepKey="createConfigProduct"> + <requiredEntity createDataKey="createCategory"/> + </createData> + + <!-- Make the configurable product have two options, that are children of the default attribute set --> + <createData entity="productAttributeWithTwoOptions" stepKey="createConfigProductAttribute"/> + <createData entity="productAttributeOption1" stepKey="createConfigProductAttributeOption1"> + <requiredEntity createDataKey="createConfigProductAttribute"/> + </createData> + <createData entity="productAttributeOption2" stepKey="createConfigProductAttributeOption2"> + <requiredEntity createDataKey="createConfigProductAttribute"/> + </createData> + <createData entity="AddToDefaultSet" stepKey="createConfigAddToAttributeSet"> + <requiredEntity createDataKey="createConfigProductAttribute"/> + </createData> + <getData entity="ProductAttributeOptionGetter" index="1" stepKey="getConfigAttributeOption1"> + <requiredEntity createDataKey="createConfigProductAttribute"/> + </getData> + <getData entity="ProductAttributeOptionGetter" index="2" stepKey="getConfigAttributeOption2"> + <requiredEntity createDataKey="createConfigProductAttribute"/> + </getData> + + <!-- Create the 2 children that will be a part of the configurable product --> + <createData entity="ApiSimpleOne" stepKey="createConfigChildProduct1"> + <requiredEntity createDataKey="createConfigProductAttribute"/> + <requiredEntity createDataKey="getConfigAttributeOption1"/> + </createData> + <createData entity="ApiSimpleTwo" stepKey="createConfigChildProduct2"> + <requiredEntity createDataKey="createConfigProductAttribute"/> + <requiredEntity createDataKey="getConfigAttributeOption2"/> + </createData> + + <!-- Assign the two products to the configurable product --> + <createData entity="ConfigurableProductTwoOptions" stepKey="createConfigProductOption"> + <requiredEntity createDataKey="createConfigProduct"/> + <requiredEntity createDataKey="createConfigProductAttribute"/> + <requiredEntity createDataKey="getConfigAttributeOption1"/> + <requiredEntity createDataKey="getConfigAttributeOption2"/> + </createData> + <createData entity="ConfigurableProductAddChild" stepKey="createConfigProductAddChild1"> + <requiredEntity createDataKey="createConfigProduct"/> + <requiredEntity createDataKey="createConfigChildProduct1"/> + </createData> + <createData entity="ConfigurableProductAddChild" stepKey="createConfigProductAddChild2"> + <requiredEntity createDataKey="createConfigProduct"/> + <requiredEntity createDataKey="createConfigChildProduct2"/> + </createData> + + <!-- log in --> + <actionGroup ref="AdminLoginActionGroup" stepKey="login"/> + </before> + + <after> + <actionGroup ref="AdminLogoutActionGroup" stepKey="adminLogout"/> + <deleteData createDataKey="createCategory" stepKey="deleteCategory"/> + <deleteData createDataKey="createConfigProduct" stepKey="deleteConfigProduct"/> + <deleteData createDataKey="createConfigChildProduct2" stepKey="deleteConfigChildProduct2"/> + <deleteData createDataKey="createConfigProductAttribute" stepKey="deleteConfigProductAttribute"/> + + <!-- Reindex invalidated indices after product attribute has been created/deleted --> + <magentoCron groups="index" stepKey="reindexInvalidatedIndices"/> + </after> + + <!-- Check to make sure that the configurable product shows up as in stock --> + <amOnPage url="/{{ApiConfigurableProduct.urlKey}}2.html" stepKey="goToConfigProductPage"/> + <waitForPageLoad stepKey="waitForStoreFrontLoad"/> + <see stepKey="checkForOutOfStock" selector="{{StorefrontProductInfoMainSection.stockIndication}}" userInput="IN STOCK"/> + + <!-- Delete the first simple product --> + <actionGroup stepKey="deleteProduct1" ref="DeleteProductBySkuActionGroup"> + <argument name="sku" value="{{ApiSimpleOne.sku}}"/> + </actionGroup> + + <!-- Check to make sure that the configurable product shows up as in stock --> + <amOnPage url="/{{ApiConfigurableProduct.urlKey}}2.html" stepKey="goToConfigProductPage2"/> + <waitForPageLoad stepKey="waitForStoreFrontLoad2"/> + <see stepKey="checkForOutOfStock2" selector="{{StorefrontProductInfoMainSection.stockIndication}}" userInput="IN STOCK"/> + + <!-- Find the second simple product that we just created using the product grid and go to its page--> + <amOnPage url="{{AdminProductIndexPage.url}}" stepKey="visitAdminProductPage2"/> + <waitForPageLoad stepKey="waitForAdminProductGridLoad2"/> + <conditionalClick selector="{{AdminProductGridFilterSection.clearFilters}}" dependentSelector="{{AdminProductGridFilterSection.clearFilters}}" visible="true" stepKey="clickClearFiltersInitial2"/> + <actionGroup ref="FilterProductGridBySkuActionGroup" stepKey="findCreatedProduct2"> + <argument name="product" value="ApiSimpleTwo"/> + </actionGroup> + <waitForPageLoad stepKey="waitForFiltersToBeApplied2"/> + <click selector="{{AdminProductGridSection.firstRow}}" stepKey="clickOnProductPage2"/> + <waitForPageLoad stepKey="waitForProductPageLoad2"/> + + <!-- Edit the quantity of the second simple product as 0 --> + <fillField selector="{{AdminProductFormSection.productQuantity}}" userInput="0" stepKey="fillProductQuantity2"/> + <click selector="{{AdminProductFormActionSection.saveButton}}" stepKey="clickSaveProduct2"/> + + <!-- Check to make sure that the configurable product shows up as out of stock --> + <amOnPage url="/{{ApiConfigurableProduct.urlKey}}2.html" stepKey="goToConfigProductPage3"/> + <waitForPageLoad stepKey="waitForStoreFrontLoad3"/> + <see stepKey="checkForOutOfStock3" selector="{{StorefrontProductInfoMainSection.stockIndication}}" userInput="OUT OF STOCK"/> + </test> +</tests> diff --git a/app/code/Magento/ConfigurableProduct/Test/Mftf/Test/AdminConfigurableProductOutOfStockTest/AdminConfigurableProductOutOfStockTestDeleteChildrenTest.xml b/app/code/Magento/ConfigurableProduct/Test/Mftf/Test/AdminConfigurableProductOutOfStockTest/AdminConfigurableProductOutOfStockTestDeleteChildrenTest.xml new file mode 100644 index 0000000000000..2e8bf8ff66933 --- /dev/null +++ b/app/code/Magento/ConfigurableProduct/Test/Mftf/Test/AdminConfigurableProductOutOfStockTest/AdminConfigurableProductOutOfStockTestDeleteChildrenTest.xml @@ -0,0 +1,113 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + /** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +--> +<tests xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/testSchema.xsd"> + <test name="AdminConfigurableProductOutOfStockTestDeleteChildrenTest"> + <annotations> + <features value="ConfigurableProduct"/> + <stories value="Product visibility when in stock/out of stock"/> + <title value="Configurable Product goes 'Out of Stock' if all associated Simple Products are deleted"/> + <description value="Configurable Product goes 'Out of Stock' if all associated Simple Products are deleted"/> + <severity value="CRITICAL"/> + <testCaseId value="MC-3042"/> + <group value="ConfigurableProduct"/> + </annotations> + <before> + <!-- TODO: This should be converted to an actionGroup once MQE-993 is fixed. --> + <!-- Create the category to put the product in --> + <createData entity="ApiCategory" stepKey="createCategory"/> + + <!-- Create the configurable product based on the data in the /data folder --> + <createData entity="ApiConfigurableProduct" stepKey="createConfigProduct"> + <requiredEntity createDataKey="createCategory"/> + </createData> + + <!-- Make the configurable product have two options, that are children of the default attribute set --> + <createData entity="productAttributeWithTwoOptions" stepKey="createConfigProductAttribute"/> + <createData entity="productAttributeOption1" stepKey="createConfigProductAttributeOption1"> + <requiredEntity createDataKey="createConfigProductAttribute"/> + </createData> + <createData entity="productAttributeOption2" stepKey="createConfigProductAttributeOption2"> + <requiredEntity createDataKey="createConfigProductAttribute"/> + </createData> + <createData entity="AddToDefaultSet" stepKey="createConfigAddToAttributeSet"> + <requiredEntity createDataKey="createConfigProductAttribute"/> + </createData> + <getData entity="ProductAttributeOptionGetter" index="1" stepKey="getConfigAttributeOption1"> + <requiredEntity createDataKey="createConfigProductAttribute"/> + </getData> + <getData entity="ProductAttributeOptionGetter" index="2" stepKey="getConfigAttributeOption2"> + <requiredEntity createDataKey="createConfigProductAttribute"/> + </getData> + + <!-- Create the 2 children that will be a part of the configurable product --> + <createData entity="ApiSimpleOne" stepKey="createConfigChildProduct1"> + <requiredEntity createDataKey="createConfigProductAttribute"/> + <requiredEntity createDataKey="getConfigAttributeOption1"/> + </createData> + <createData entity="ApiSimpleTwo" stepKey="createConfigChildProduct2"> + <requiredEntity createDataKey="createConfigProductAttribute"/> + <requiredEntity createDataKey="getConfigAttributeOption2"/> + </createData> + + <!-- Assign the two products to the configurable product --> + <createData entity="ConfigurableProductTwoOptions" stepKey="createConfigProductOption"> + <requiredEntity createDataKey="createConfigProduct"/> + <requiredEntity createDataKey="createConfigProductAttribute"/> + <requiredEntity createDataKey="getConfigAttributeOption1"/> + <requiredEntity createDataKey="getConfigAttributeOption2"/> + </createData> + <createData entity="ConfigurableProductAddChild" stepKey="createConfigProductAddChild1"> + <requiredEntity createDataKey="createConfigProduct"/> + <requiredEntity createDataKey="createConfigChildProduct1"/> + </createData> + <createData entity="ConfigurableProductAddChild" stepKey="createConfigProductAddChild2"> + <requiredEntity createDataKey="createConfigProduct"/> + <requiredEntity createDataKey="createConfigChildProduct2"/> + </createData> + + <!-- log in --> + <actionGroup ref="AdminLoginActionGroup" stepKey="login"/> + </before> + + <after> + <actionGroup ref="AdminLogoutActionGroup" stepKey="adminLogout"/> + <deleteData createDataKey="createCategory" stepKey="deleteCategory"/> + <deleteData createDataKey="createConfigProduct" stepKey="deleteConfigProduct"/> + <deleteData createDataKey="createConfigProductAttribute" stepKey="deleteConfigProductAttribute"/> + + <!-- Reindex invalidated indices after product attribute has been created/deleted --> + <magentoCron groups="index" stepKey="reindexInvalidatedIndices"/> + </after> + + <!-- Check to make sure that the configurable product shows up as in stock --> + <amOnPage url="/{{ApiConfigurableProduct.urlKey}}2.html" stepKey="goToConfigProductPage"/> + <waitForPageLoad stepKey="waitForStoreFrontLoad"/> + <see stepKey="checkForOutOfStock" selector="{{StorefrontProductInfoMainSection.stockIndication}}" userInput="IN STOCK"/> + + <!-- Delete the first simple product --> + <actionGroup stepKey="deleteProduct1" ref="DeleteProductBySkuActionGroup"> + <argument name="sku" value="{{ApiSimpleOne.sku}}"/> + </actionGroup> + + <!-- Check to make sure that the configurable product shows up as in stock --> + <amOnPage url="/{{ApiConfigurableProduct.urlKey}}2.html" stepKey="goToConfigProductPage2"/> + <waitForPageLoad stepKey="waitForStoreFrontLoad2"/> + <see stepKey="checkForOutOfStock2" selector="{{StorefrontProductInfoMainSection.stockIndication}}" userInput="IN STOCK"/> + + <!-- Delete the second simple product --> + <actionGroup stepKey="deleteProduct2" ref="DeleteProductBySkuActionGroup"> + <argument name="sku" value="{{ApiSimpleTwo.sku}}"/> + </actionGroup> + + <!-- Check to make sure that the configurable product shows up as out of stock --> + <amOnPage url="/{{ApiConfigurableProduct.urlKey}}2.html" stepKey="goToConfigProductPage3"/> + <waitForPageLoad stepKey="waitForStoreFrontLoad3"/> + <see stepKey="checkForOutOfStock3" selector="{{StorefrontProductInfoMainSection.stockIndication}}" userInput="OUT OF STOCK"/> + </test> +</tests> diff --git a/app/code/Magento/ConfigurableProduct/Test/Mftf/Test/AdminConfigurableProductSearchTest.xml b/app/code/Magento/ConfigurableProduct/Test/Mftf/Test/AdminConfigurableProductSearchTest.xml deleted file mode 100644 index 889ca5b24b242..0000000000000 --- a/app/code/Magento/ConfigurableProduct/Test/Mftf/Test/AdminConfigurableProductSearchTest.xml +++ /dev/null @@ -1,166 +0,0 @@ -<?xml version="1.0" encoding="UTF-8"?> -<!-- - /** - * Copyright © Magento, Inc. All rights reserved. - * See COPYING.txt for license details. - */ ---> - -<tests xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" - xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/testSchema.xsd"> - <test name="AdminConfigurableProductSearchTest"> - <annotations> - <features value="ConfigurableProduct"/> - <stories value="Search"/> - <title value="admin should be able to search for a configurable product"/> - <description value="admin should be able to search for a configurable product"/> - <testCaseId value="MC-100"/> - <group value="ConfigurableProduct"/> - <severity value="AVERAGE"/> - </annotations> - - <before> - <!-- TODO: This should be converted to an actionGroup once MQE-993 is fixed. --> - <createData entity="ApiCategory" stepKey="createCategory"/> - <createData entity="ApiConfigurableProduct" stepKey="createConfigProduct"> - <requiredEntity createDataKey="createCategory"/> - </createData> - <createData entity="productAttributeWithTwoOptions" stepKey="createConfigProductAttribute"/> - <createData entity="productAttributeOption1" stepKey="createConfigProductAttributeOption1"> - <requiredEntity createDataKey="createConfigProductAttribute"/> - </createData> - <createData entity="productAttributeOption2" stepKey="createConfigProductAttributeOption2"> - <requiredEntity createDataKey="createConfigProductAttribute"/> - </createData> - <createData entity="AddToDefaultSet" stepKey="createConfigAddToAttributeSet"> - <requiredEntity createDataKey="createConfigProductAttribute"/> - </createData> - <getData entity="ProductAttributeOptionGetter" index="1" stepKey="getConfigAttributeOption1"> - <requiredEntity createDataKey="createConfigProductAttribute"/> - </getData> - <getData entity="ProductAttributeOptionGetter" index="2" stepKey="getConfigAttributeOption2"> - <requiredEntity createDataKey="createConfigProductAttribute"/> - </getData> - <createData entity="ApiSimpleOne" stepKey="createConfigChildProduct1"> - <requiredEntity createDataKey="createConfigProductAttribute"/> - <requiredEntity createDataKey="getConfigAttributeOption1"/> - </createData> - <createData entity="ApiSimpleTwo" stepKey="createConfigChildProduct2"> - <requiredEntity createDataKey="createConfigProductAttribute"/> - <requiredEntity createDataKey="getConfigAttributeOption2"/> - </createData> - <createData entity="ConfigurableProductTwoOptions" stepKey="createConfigProductOption"> - <requiredEntity createDataKey="createConfigProduct"/> - <requiredEntity createDataKey="createConfigProductAttribute"/> - <requiredEntity createDataKey="getConfigAttributeOption1"/> - <requiredEntity createDataKey="getConfigAttributeOption2"/> - </createData> - <createData entity="ConfigurableProductAddChild" stepKey="createConfigProductAddChild1"> - <requiredEntity createDataKey="createConfigProduct"/> - <requiredEntity createDataKey="createConfigChildProduct1"/> - </createData> - <createData entity="ConfigurableProductAddChild" stepKey="createConfigProductAddChild2"> - <requiredEntity createDataKey="createConfigProduct"/> - <requiredEntity createDataKey="createConfigChildProduct2"/> - </createData> - - <actionGroup ref="LoginAsAdmin" stepKey="login"/> - </before> - - <after> - <actionGroup ref="logout" stepKey="adminLogout"/> - <deleteData createDataKey="createConfigProduct" stepKey="deleteConfigProduct"/> - <deleteData createDataKey="createConfigChildProduct1" stepKey="deleteConfigChildProduct1"/> - <deleteData createDataKey="createConfigChildProduct2" stepKey="deleteConfigChildProduct2"/> - <deleteData createDataKey="createConfigProductAttribute" stepKey="deleteConfigProductAttribute"/> - </after> - - <amOnPage url="{{AdminProductIndexPage.url}}" stepKey="goToProductList"/> - <waitForPageLoad stepKey="wait1"/> - <conditionalClick selector="{{AdminProductGridFilterSection.clearAll}}" dependentSelector="{{AdminProductGridFilterSection.clearAll}}" stepKey="clearAll" visible="true"/> - <actionGroup ref="SearchProductGridByKeywordActionGroup" stepKey="searchForProduct"> - <argument name="keyword" value="ApiConfigurableProduct.name"/> - </actionGroup> - <waitForPageLoad stepKey="wait2"/> - <seeNumberOfElements selector="{{AdminProductGridSection.productGridRows}}" userInput="1" stepKey="seeOneResult"/> - <see selector="{{AdminProductGridSection.productGridCell('1', 'Name')}}" userInput="{{ApiConfigurableProduct.name}}" stepKey="seeInGrid"/> - <see selector="{{AdminProductGridFilterSection.enabledFilters}}" userInput="{{ApiConfigurableProduct.name}}" stepKey="seeInActiveFilters"/> - </test> - - <test name="AdminConfigurableProductFilterByTypeTest"> - <annotations> - <features value="ConfigurableProduct"/> - <stories value="Search"/> - <title value="admin should be able to filter by type configurable product"/> - <description value="admin should be able to filter by type configurable product"/> - <testCaseId value="MC-66"/> - <group value="ConfigurableProduct"/> - <severity value="AVERAGE"/> - </annotations> - - <before> - <!-- TODO: This should be converted to an actionGroup once MQE-993 is fixed. --> - <createData entity="ApiCategory" stepKey="createCategory"/> - <createData entity="ApiConfigurableProduct" stepKey="createConfigProduct"> - <requiredEntity createDataKey="createCategory"/> - </createData> - <createData entity="productAttributeWithTwoOptions" stepKey="createConfigProductAttribute"/> - <createData entity="productAttributeOption1" stepKey="createConfigProductAttributeOption1"> - <requiredEntity createDataKey="createConfigProductAttribute"/> - </createData> - <createData entity="productAttributeOption2" stepKey="createConfigProductAttributeOption2"> - <requiredEntity createDataKey="createConfigProductAttribute"/> - </createData> - <createData entity="AddToDefaultSet" stepKey="createConfigAddToAttributeSet"> - <requiredEntity createDataKey="createConfigProductAttribute"/> - </createData> - <getData entity="ProductAttributeOptionGetter" index="1" stepKey="getConfigAttributeOption1"> - <requiredEntity createDataKey="createConfigProductAttribute"/> - </getData> - <getData entity="ProductAttributeOptionGetter" index="2" stepKey="getConfigAttributeOption2"> - <requiredEntity createDataKey="createConfigProductAttribute"/> - </getData> - <createData entity="ApiSimpleOne" stepKey="createConfigChildProduct1"> - <requiredEntity createDataKey="createConfigProductAttribute"/> - <requiredEntity createDataKey="getConfigAttributeOption1"/> - </createData> - <createData entity="ApiSimpleTwo" stepKey="createConfigChildProduct2"> - <requiredEntity createDataKey="createConfigProductAttribute"/> - <requiredEntity createDataKey="getConfigAttributeOption2"/> - </createData> - <createData entity="ConfigurableProductTwoOptions" stepKey="createConfigProductOption"> - <requiredEntity createDataKey="createConfigProduct"/> - <requiredEntity createDataKey="createConfigProductAttribute"/> - <requiredEntity createDataKey="getConfigAttributeOption1"/> - <requiredEntity createDataKey="getConfigAttributeOption2"/> - </createData> - <createData entity="ConfigurableProductAddChild" stepKey="createConfigProductAddChild1"> - <requiredEntity createDataKey="createConfigProduct"/> - <requiredEntity createDataKey="createConfigChildProduct1"/> - </createData> - <createData entity="ConfigurableProductAddChild" stepKey="createConfigProductAddChild2"> - <requiredEntity createDataKey="createConfigProduct"/> - <requiredEntity createDataKey="createConfigChildProduct2"/> - </createData> - - <actionGroup ref="LoginAsAdmin" stepKey="login"/> - </before> - - <after> - <actionGroup ref="logout" stepKey="adminLogout"/> - <deleteData createDataKey="createConfigProduct" stepKey="deleteConfigProduct"/> - <deleteData createDataKey="createConfigChildProduct1" stepKey="deleteConfigChildProduct1"/> - <deleteData createDataKey="createConfigChildProduct2" stepKey="deleteConfigChildProduct2"/> - <deleteData createDataKey="createConfigProductAttribute" stepKey="deleteConfigProductAttribute"/> - </after> - - <amOnPage url="{{AdminProductIndexPage.url}}" stepKey="goToProductList"/> - <waitForPageLoad stepKey="wait1"/> - <conditionalClick selector="{{AdminProductGridFilterSection.clearAll}}" dependentSelector="{{AdminProductGridFilterSection.clearAll}}" stepKey="clearAll" visible="true"/> - <click selector="{{AdminProductGridFilterSection.filters}}" stepKey="clickShowFilters"/> - <selectOption selector="{{AdminProductGridFilterSection.typeFilter}}" userInput="{{ApiConfigurableProduct.type_id}}" stepKey="selectConfigurableType"/> - <click selector="{{AdminProductGridFilterSection.applyFilters}}" stepKey="clickApplyFilters"/> - <see selector="{{AdminProductGridFilterSection.enabledFilters}}" userInput="Type: Configurable Product" stepKey="seeFilter"/> - <see selector="{{AdminProductGridSection.table}}" userInput="$$createConfigProduct.name$$" stepKey="seeProduct"/> - </test> -</tests> diff --git a/app/code/Magento/ConfigurableProduct/Test/Mftf/Test/AdminConfigurableProductSearchTest/AdminConfigurableProductFilterByTypeTest.xml b/app/code/Magento/ConfigurableProduct/Test/Mftf/Test/AdminConfigurableProductSearchTest/AdminConfigurableProductFilterByTypeTest.xml new file mode 100644 index 0000000000000..a35ef058dfd80 --- /dev/null +++ b/app/code/Magento/ConfigurableProduct/Test/Mftf/Test/AdminConfigurableProductSearchTest/AdminConfigurableProductFilterByTypeTest.xml @@ -0,0 +1,89 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + /** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +--> +<tests xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/testSchema.xsd"> + <test name="AdminConfigurableProductFilterByTypeTest"> + <annotations> + <features value="ConfigurableProduct"/> + <stories value="Search"/> + <title value="admin should be able to filter by type configurable product"/> + <description value="admin should be able to filter by type configurable product"/> + <testCaseId value="MC-66"/> + <group value="ConfigurableProduct"/> + <severity value="AVERAGE"/> + </annotations> + + <before> + <!-- TODO: This should be converted to an actionGroup once MQE-993 is fixed. --> + <createData entity="ApiCategory" stepKey="createCategory"/> + <createData entity="ApiConfigurableProduct" stepKey="createConfigProduct"> + <requiredEntity createDataKey="createCategory"/> + </createData> + <createData entity="productAttributeWithTwoOptions" stepKey="createConfigProductAttribute"/> + <createData entity="productAttributeOption1" stepKey="createConfigProductAttributeOption1"> + <requiredEntity createDataKey="createConfigProductAttribute"/> + </createData> + <createData entity="productAttributeOption2" stepKey="createConfigProductAttributeOption2"> + <requiredEntity createDataKey="createConfigProductAttribute"/> + </createData> + <createData entity="AddToDefaultSet" stepKey="createConfigAddToAttributeSet"> + <requiredEntity createDataKey="createConfigProductAttribute"/> + </createData> + <getData entity="ProductAttributeOptionGetter" index="1" stepKey="getConfigAttributeOption1"> + <requiredEntity createDataKey="createConfigProductAttribute"/> + </getData> + <getData entity="ProductAttributeOptionGetter" index="2" stepKey="getConfigAttributeOption2"> + <requiredEntity createDataKey="createConfigProductAttribute"/> + </getData> + <createData entity="ApiSimpleOne" stepKey="createConfigChildProduct1"> + <requiredEntity createDataKey="createConfigProductAttribute"/> + <requiredEntity createDataKey="getConfigAttributeOption1"/> + </createData> + <createData entity="ApiSimpleTwo" stepKey="createConfigChildProduct2"> + <requiredEntity createDataKey="createConfigProductAttribute"/> + <requiredEntity createDataKey="getConfigAttributeOption2"/> + </createData> + <createData entity="ConfigurableProductTwoOptions" stepKey="createConfigProductOption"> + <requiredEntity createDataKey="createConfigProduct"/> + <requiredEntity createDataKey="createConfigProductAttribute"/> + <requiredEntity createDataKey="getConfigAttributeOption1"/> + <requiredEntity createDataKey="getConfigAttributeOption2"/> + </createData> + <createData entity="ConfigurableProductAddChild" stepKey="createConfigProductAddChild1"> + <requiredEntity createDataKey="createConfigProduct"/> + <requiredEntity createDataKey="createConfigChildProduct1"/> + </createData> + <createData entity="ConfigurableProductAddChild" stepKey="createConfigProductAddChild2"> + <requiredEntity createDataKey="createConfigProduct"/> + <requiredEntity createDataKey="createConfigChildProduct2"/> + </createData> + + <actionGroup ref="AdminLoginActionGroup" stepKey="login"/> + </before> + + <after> + <actionGroup ref="AdminLogoutActionGroup" stepKey="adminLogout"/> + <deleteData createDataKey="createConfigProduct" stepKey="deleteConfigProduct"/> + <deleteData createDataKey="createConfigChildProduct1" stepKey="deleteConfigChildProduct1"/> + <deleteData createDataKey="createConfigChildProduct2" stepKey="deleteConfigChildProduct2"/> + <deleteData createDataKey="createConfigProductAttribute" stepKey="deleteConfigProductAttribute"/> + + <!-- Reindex invalidated indices after product attribute has been created/deleted --> + <magentoCron groups="index" stepKey="reindexInvalidatedIndices"/> + </after> + + <amOnPage url="{{AdminProductIndexPage.url}}" stepKey="goToProductList"/> + <waitForPageLoad stepKey="wait1"/> + <conditionalClick selector="{{AdminProductGridFilterSection.clearAll}}" dependentSelector="{{AdminProductGridFilterSection.clearAll}}" stepKey="clearAll" visible="true"/> + <click selector="{{AdminProductGridFilterSection.filters}}" stepKey="clickShowFilters"/> + <selectOption selector="{{AdminProductGridFilterSection.typeFilter}}" userInput="{{ApiConfigurableProduct.type_id}}" stepKey="selectConfigurableType"/> + <click selector="{{AdminProductGridFilterSection.applyFilters}}" stepKey="clickApplyFilters"/> + <see selector="{{AdminProductGridFilterSection.enabledFilters}}" userInput="Type: Configurable Product" stepKey="seeFilter"/> + <see selector="{{AdminProductGridSection.table}}" userInput="$$createConfigProduct.name$$" stepKey="seeProduct"/> + </test> +</tests> diff --git a/app/code/Magento/ConfigurableProduct/Test/Mftf/Test/AdminConfigurableProductSearchTest/AdminConfigurableProductSearchTest.xml b/app/code/Magento/ConfigurableProduct/Test/Mftf/Test/AdminConfigurableProductSearchTest/AdminConfigurableProductSearchTest.xml new file mode 100644 index 0000000000000..6d9015b5d1cbf --- /dev/null +++ b/app/code/Magento/ConfigurableProduct/Test/Mftf/Test/AdminConfigurableProductSearchTest/AdminConfigurableProductSearchTest.xml @@ -0,0 +1,91 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + /** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +--> +<tests xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/testSchema.xsd"> + <test name="AdminConfigurableProductSearchTest"> + <annotations> + <features value="ConfigurableProduct"/> + <stories value="Search"/> + <title value="admin should be able to search for a configurable product"/> + <description value="admin should be able to search for a configurable product"/> + <testCaseId value="MC-100"/> + <group value="ConfigurableProduct"/> + <severity value="AVERAGE"/> + </annotations> + + <before> + <!-- TODO: This should be converted to an actionGroup once MQE-993 is fixed. --> + <createData entity="ApiCategory" stepKey="createCategory"/> + <createData entity="ApiConfigurableProduct" stepKey="createConfigProduct"> + <requiredEntity createDataKey="createCategory"/> + </createData> + <createData entity="productAttributeWithTwoOptions" stepKey="createConfigProductAttribute"/> + <createData entity="productAttributeOption1" stepKey="createConfigProductAttributeOption1"> + <requiredEntity createDataKey="createConfigProductAttribute"/> + </createData> + <createData entity="productAttributeOption2" stepKey="createConfigProductAttributeOption2"> + <requiredEntity createDataKey="createConfigProductAttribute"/> + </createData> + <createData entity="AddToDefaultSet" stepKey="createConfigAddToAttributeSet"> + <requiredEntity createDataKey="createConfigProductAttribute"/> + </createData> + <getData entity="ProductAttributeOptionGetter" index="1" stepKey="getConfigAttributeOption1"> + <requiredEntity createDataKey="createConfigProductAttribute"/> + </getData> + <getData entity="ProductAttributeOptionGetter" index="2" stepKey="getConfigAttributeOption2"> + <requiredEntity createDataKey="createConfigProductAttribute"/> + </getData> + <createData entity="ApiSimpleOne" stepKey="createConfigChildProduct1"> + <requiredEntity createDataKey="createConfigProductAttribute"/> + <requiredEntity createDataKey="getConfigAttributeOption1"/> + </createData> + <createData entity="ApiSimpleTwo" stepKey="createConfigChildProduct2"> + <requiredEntity createDataKey="createConfigProductAttribute"/> + <requiredEntity createDataKey="getConfigAttributeOption2"/> + </createData> + <createData entity="ConfigurableProductTwoOptions" stepKey="createConfigProductOption"> + <requiredEntity createDataKey="createConfigProduct"/> + <requiredEntity createDataKey="createConfigProductAttribute"/> + <requiredEntity createDataKey="getConfigAttributeOption1"/> + <requiredEntity createDataKey="getConfigAttributeOption2"/> + </createData> + <createData entity="ConfigurableProductAddChild" stepKey="createConfigProductAddChild1"> + <requiredEntity createDataKey="createConfigProduct"/> + <requiredEntity createDataKey="createConfigChildProduct1"/> + </createData> + <createData entity="ConfigurableProductAddChild" stepKey="createConfigProductAddChild2"> + <requiredEntity createDataKey="createConfigProduct"/> + <requiredEntity createDataKey="createConfigChildProduct2"/> + </createData> + + <actionGroup ref="AdminLoginActionGroup" stepKey="login"/> + </before> + + <after> + <actionGroup ref="AdminLogoutActionGroup" stepKey="adminLogout"/> + <deleteData createDataKey="createConfigProduct" stepKey="deleteConfigProduct"/> + <deleteData createDataKey="createConfigChildProduct1" stepKey="deleteConfigChildProduct1"/> + <deleteData createDataKey="createConfigChildProduct2" stepKey="deleteConfigChildProduct2"/> + <deleteData createDataKey="createConfigProductAttribute" stepKey="deleteConfigProductAttribute"/> + + <!-- Reindex invalidated indices after product attribute has been created/deleted --> + <magentoCron groups="index" stepKey="reindexInvalidatedIndices"/> + </after> + + <amOnPage url="{{AdminProductIndexPage.url}}" stepKey="goToProductList"/> + <waitForPageLoad stepKey="wait1"/> + <conditionalClick selector="{{AdminProductGridFilterSection.clearAll}}" dependentSelector="{{AdminProductGridFilterSection.clearAll}}" stepKey="clearAll" visible="true"/> + <actionGroup ref="SearchProductGridByKeywordActionGroup" stepKey="searchForProduct"> + <argument name="keyword" value="ApiConfigurableProduct.name"/> + </actionGroup> + <waitForPageLoad stepKey="wait2"/> + <seeNumberOfElements selector="{{AdminProductGridSection.productGridRows}}" userInput="1" stepKey="seeOneResult"/> + <see selector="{{AdminProductGridSection.productGridCell('1', 'Name')}}" userInput="{{ApiConfigurableProduct.name}}" stepKey="seeInGrid"/> + <see selector="{{AdminProductGridFilterSection.enabledFilters}}" userInput="{{ApiConfigurableProduct.name}}" stepKey="seeInActiveFilters"/> + </test> +</tests> diff --git a/app/code/Magento/ConfigurableProduct/Test/Mftf/Test/AdminConfigurableProductUpdateAttributeTest.xml b/app/code/Magento/ConfigurableProduct/Test/Mftf/Test/AdminConfigurableProductUpdateAttributeTest.xml deleted file mode 100644 index c437b39a405cd..0000000000000 --- a/app/code/Magento/ConfigurableProduct/Test/Mftf/Test/AdminConfigurableProductUpdateAttributeTest.xml +++ /dev/null @@ -1,272 +0,0 @@ -<?xml version="1.0" encoding="UTF-8"?> -<!-- - /** - * Copyright © Magento, Inc. All rights reserved. - * See COPYING.txt for license details. - */ ---> - -<tests xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" - xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/testSchema.xsd"> - <test name="AdminConfigurableProductUpdateAttributeTest"> - <annotations> - <features value="ConfigurableProduct"/> - <stories value="Edit a configurable product in admin"/> - <title value="Admin should be able to update existing attributes of a configurable product"/> - <description value="Admin should be able to update existing attributes of a configurable product"/> - <severity value="AVERAGE"/> - <testCaseId value="MC-179"/> - <group value="ConfigurableProduct"/> - </annotations> - - <before> - <!-- Create the attribute we will be modifying --> - <createData entity="productAttributeWithTwoOptions" stepKey="createModifiableProductAttribute"/> - - <!-- Create the two attributes the product will have --> - <createData entity="productAttributeOption1" stepKey="createModifiableProductAttributeOption1"> - <requiredEntity createDataKey="createModifiableProductAttribute"/> - </createData> - <createData entity="productAttributeOption2" stepKey="createModifiableProductAttributeOption2"> - <requiredEntity createDataKey="createModifiableProductAttribute"/> - </createData> - - <!-- Add the product to the default set --> - <createData entity="AddToDefaultSet" stepKey="createModifiableAddToAttributeSet"> - <requiredEntity createDataKey="createModifiableProductAttribute"/> - </createData> - - <!-- TODO: This should be converted to an actionGroup once MQE-993 is fixed. --> - <!-- Create the category the product will be a part of --> - <createData entity="ApiCategory" stepKey="createCategory"/> - <createData entity="ApiConfigurableProduct" stepKey="createConfigProduct"> - <requiredEntity createDataKey="createCategory"/> - </createData> - <createData entity="productAttributeWithTwoOptions" stepKey="createConfigProductAttribute"/> - - <!-- Create the two attributes the product will have --> - <createData entity="productAttributeOption1" stepKey="createConfigProductAttributeOption1"> - <requiredEntity createDataKey="createConfigProductAttribute"/> - </createData> - <createData entity="productAttributeOption2" stepKey="createConfigProductAttributeOption2"> - <requiredEntity createDataKey="createConfigProductAttribute"/> - </createData> - - <!-- Add the product to the default set --> - <createData entity="AddToDefaultSet" stepKey="createConfigAddToAttributeSet"> - <requiredEntity createDataKey="createConfigProductAttribute"/> - </createData> - - <!-- Get the two attributes --> - <getData entity="ProductAttributeOptionGetter" index="1" stepKey="getConfigAttributeOption1"> - <requiredEntity createDataKey="createConfigProductAttribute"/> - </getData> - <getData entity="ProductAttributeOptionGetter" index="2" stepKey="getConfigAttributeOption2"> - <requiredEntity createDataKey="createConfigProductAttribute"/> - </getData> - - <!-- Create the two children product --> - <createData entity="ApiSimpleOne" stepKey="createConfigChildProduct1"> - <requiredEntity createDataKey="createConfigProductAttribute"/> - <requiredEntity createDataKey="getConfigAttributeOption1"/> - </createData> - <createData entity="ApiSimpleTwo" stepKey="createConfigChildProduct2"> - <requiredEntity createDataKey="createConfigProductAttribute"/> - <requiredEntity createDataKey="getConfigAttributeOption2"/> - </createData> - - <!-- Create the two configurable product with both children --> - <createData entity="ConfigurableProductTwoOptions" stepKey="createConfigProductOption"> - <requiredEntity createDataKey="createConfigProduct"/> - <requiredEntity createDataKey="createConfigProductAttribute"/> - <requiredEntity createDataKey="getConfigAttributeOption1"/> - <requiredEntity createDataKey="getConfigAttributeOption2"/> - </createData> - <createData entity="ConfigurableProductAddChild" stepKey="createConfigProductAddChild1"> - <requiredEntity createDataKey="createConfigProduct"/> - <requiredEntity createDataKey="createConfigChildProduct1"/> - </createData> - <createData entity="ConfigurableProductAddChild" stepKey="createConfigProductAddChild2"> - <requiredEntity createDataKey="createConfigProduct"/> - <requiredEntity createDataKey="createConfigChildProduct2"/> - </createData> - - <!-- login --> - <actionGroup ref="LoginAsAdmin" stepKey="login"/> - </before> - - <after> - <actionGroup ref="logout" stepKey="adminLogout"/> - - <!-- Delete everything that was created in the before block --> - <deleteData createDataKey="createCategory" stepKey="deleteCatagory" /> - <deleteData createDataKey="createConfigProduct" stepKey="deleteConfigProduct"/> - <deleteData createDataKey="createConfigChildProduct1" stepKey="deleteConfigChildProduct1"/> - <deleteData createDataKey="createConfigChildProduct2" stepKey="deleteConfigChildProduct2"/> - <deleteData createDataKey="createModifiableProductAttribute" stepKey="deleteModifiableProductAttribute"/> - <deleteData createDataKey="createConfigProductAttribute" stepKey="deleteConfigProductAttribute"/> - </after> - - <!-- Get the current option of the attribute before it was changed --> - <amOnPage url="/{{ApiConfigurableProduct.urlKey}}2.html" stepKey="goToConfigProductPage"/> - <waitForPageLoad stepKey="waitForStoreFrontProductPageLoad"/> - - <grabTextFrom stepKey="getBeforeOption" selector="{{StorefrontProductInfoMainSection.nthAttributeOnPage('1')}}"/> - - <!-- Find the product that we just created using the product grid --> - <amOnPage url="{{AdminProductIndexPage.url}}" stepKey="visitAdminProductPage"/> - <waitForPageLoad stepKey="waitForAdminProductPageLoad"/> - <conditionalClick selector="{{AdminProductGridFilterSection.clearFilters}}" dependentSelector="{{AdminProductGridFilterSection.clearFilters}}" visible="true" stepKey="clickClearFiltersInitial"/> - <actionGroup ref="FilterProductGridBySkuActionGroup" stepKey="findCreatedProduct"> - <argument name="product" value="ApiConfigurableProduct"/> - </actionGroup> - <waitForPageLoad stepKey="waitForProductFilterLoad"/> - - <click selector="{{AdminProductGridSection.firstRow}}" stepKey="clickOnProductPage"/> - <waitForPageLoad stepKey="waitForProductPageLoad"/> - - <!-- change the option on the first attribute --> - <selectOption stepKey="clickFirstAttribute" selector="{{ModifyAttributes.nthExistingAttribute($$createModifiableProductAttribute.default_frontend_label$$)}}" userInput="option1"/> - - <!-- Save the product --> - <click stepKey="saveProductAttribute" selector="{{AdminProductFormActionSection.saveButton}}"/> - <see stepKey="assertSuccess" selector="{{AdminProductMessagesSection.successMessage}}" userInput="You saved the product."/> - - <!-- Go back to the configurable product page and check to see if it has changed --> - <amOnPage url="/{{ApiConfigurableProduct.urlKey}}2.html" stepKey="goToConfigProductPage2"/> - <waitForPageLoad stepKey="waitForStoreFrontProductPageLoad2"/> - <grabTextFrom stepKey="getCurrentOption" selector="{{StorefrontProductInfoMainSection.nthAttributeOnPage('1')}}"/> - <assertNotEquals expected="{$getBeforeOption}" expectedType="string" actual="{$getCurrentOption}" actualType="string" stepKey="assertNotEquals"/> - - </test> - - <test name="AdminConfigurableProductUpdateChildAttributeTest"> - <annotations> - <features value="ConfigurableProduct"/> - <stories value="Edit a configurable product in admin"/> - <title value="Admin should be able to update existing attributes of child products of a configurable product"/> - <description value="Admin should be able to update existing attributes of child products of a configurable product"/> - <severity value="AVERAGE"/> - <testCaseId value="MC-288"/> - <group value="ConfigurableProduct"/> - </annotations> - - <before> - - <!-- TODO: This should be converted to an actionGroup once MQE-993 is fixed. --> - <!-- Create the category the product will be a part of --> - <createData entity="ApiCategory" stepKey="createCategory"/> - <createData entity="ApiConfigurableProduct" stepKey="createConfigProduct"> - <requiredEntity createDataKey="createCategory"/> - </createData> - <createData entity="productAttributeWithTwoOptions" stepKey="createConfigProductAttribute"/> - - <!-- Create the two attributes the product will have --> - <createData entity="productAttributeOption1" stepKey="createConfigProductAttributeOption1"> - <requiredEntity createDataKey="createConfigProductAttribute"/> - </createData> - <createData entity="productAttributeOption2" stepKey="createConfigProductAttributeOption2"> - <requiredEntity createDataKey="createConfigProductAttribute"/> - </createData> - - <!-- Add the product to the default set --> - <createData entity="AddToDefaultSet" stepKey="createConfigAddToAttributeSet"> - <requiredEntity createDataKey="createConfigProductAttribute"/> - </createData> - - <!-- Get the two attributes --> - <getData entity="ProductAttributeOptionGetter" index="1" stepKey="getConfigAttributeOption1"> - <requiredEntity createDataKey="createConfigProductAttribute"/> - </getData> - <getData entity="ProductAttributeOptionGetter" index="2" stepKey="getConfigAttributeOption2"> - <requiredEntity createDataKey="createConfigProductAttribute"/> - </getData> - - <!-- Create the two children product --> - <createData entity="ApiSimpleOne" stepKey="createConfigChildProduct1"> - <requiredEntity createDataKey="createConfigProductAttribute"/> - <requiredEntity createDataKey="getConfigAttributeOption1"/> - </createData> - <createData entity="ApiSimpleTwo" stepKey="createConfigChildProduct2"> - <requiredEntity createDataKey="createConfigProductAttribute"/> - <requiredEntity createDataKey="getConfigAttributeOption2"/> - </createData> - - <!-- Create the two configurable product with both children --> - <createData entity="ConfigurableProductTwoOptions" stepKey="createConfigProductOption"> - <requiredEntity createDataKey="createConfigProduct"/> - <requiredEntity createDataKey="createConfigProductAttribute"/> - <requiredEntity createDataKey="getConfigAttributeOption1"/> - <requiredEntity createDataKey="getConfigAttributeOption2"/> - </createData> - <createData entity="ConfigurableProductAddChild" stepKey="createConfigProductAddChild1"> - <requiredEntity createDataKey="createConfigProduct"/> - <requiredEntity createDataKey="createConfigChildProduct1"/> - </createData> - <createData entity="ConfigurableProductAddChild" stepKey="createConfigProductAddChild2"> - <requiredEntity createDataKey="createConfigProduct"/> - <requiredEntity createDataKey="createConfigChildProduct2"/> - </createData> - - <!-- login --> - <actionGroup ref="LoginAsAdmin" stepKey="login"/> - </before> - - <after> - <actionGroup ref="logout" stepKey="adminLogout"/> - - <!-- Delete everything that was created in the before block --> - <deleteData createDataKey="createCategory" stepKey="deleteCatagory" /> - <deleteData createDataKey="createConfigProduct" stepKey="deleteConfigProduct"/> - <deleteData createDataKey="createConfigChildProduct1" stepKey="deleteConfigChildProduct1"/> - <deleteData createDataKey="createConfigChildProduct2" stepKey="deleteConfigChildProduct2"/> - <deleteData createDataKey="createConfigProductAttribute" stepKey="deleteConfigProductAttribute"/> - </after> - - <!-- Find the product that we just created using the product grid --> - <amOnPage url="{{AdminProductIndexPage.url}}" stepKey="visitAdminProductPage"/> - <waitForPageLoad stepKey="waitForAdminProductPageLoad"/> - <conditionalClick selector="{{AdminProductGridFilterSection.clearFilters}}" dependentSelector="{{AdminProductGridFilterSection.clearFilters}}" visible="true" stepKey="clickClearFiltersInitial"/> - <actionGroup ref="FilterProductGridBySkuActionGroup" stepKey="findCreatedProduct"> - <argument name="product" value="$$createConfigProduct$$"/> - </actionGroup> - <waitForPageLoad stepKey="waitForProductFilterLoad"/> - <actionGroup ref="OpenEditProductOnBackendActionGroup" stepKey="openProduct"> - <argument name="product" value="$$createConfigProduct$$" /> - </actionGroup> - - <!-- Open the wizard for editing configurations and fill out a new attribute --> - <click stepKey="clickEditConfig" selector="{{AdminProductFormConfigurationsSection.createConfigurations}}"/> - <waitForPageLoad stepKey="waitForEditConfig"/> - <click stepKey="clickNextWizard" selector="{{AdminCreateProductConfigurationsPanel.next}}"/> - <waitForElementVisible selector="{{AdminCreateProductConfigurationsPanel.createNewValue}}" stepKey="waitCreateNewValueAppears"/> - <click stepKey="createNewValue" selector="{{AdminCreateProductConfigurationsPanel.createNewValue}}"/> - <fillField stepKey="fillNewAttribute" selector="{{AdminCreateProductConfigurationsPanel.attributeName}}" userInput="simple"/> - <click stepKey="confirmNewAttribute" selector="{{AdminCreateProductConfigurationsPanel.saveAttribute}}"/> - <click stepKey="clickNextWizard2" selector="{{AdminCreateProductConfigurationsPanel.next}}"/> - - <!-- Give the product a price and quantity --> - <click stepKey="click" selector="{{AdminCreateProductConfigurationsPanel.applySingleQuantityToEachSkus}}"/> - <fillField stepKey="fillProductQuantity" selector="{{AdminCreateProductConfigurationsPanel.quantity}}" userInput="{{_defaultProduct.quantity}}"/> - <click stepKey="clickNextWizard3" selector="{{AdminCreateProductConfigurationsPanel.next}}"/> - <click stepKey="clickGenerateProducts" selector="{{AdminCreateProductConfigurationsPanel.next}}"/> - - <!-- Save the product --> - <waitForPageLoad stepKey="waitForGeneration"/> - <click stepKey="saveProductAttribute" selector="{{AdminProductFormActionSection.saveButton}}"/> - <see stepKey="assertSuccess" selector="{{AdminProductMessagesSection.successMessage}}" userInput="You saved the product."/> - - <!-- Check to make sure the created product has appeared on the configurable product storefront --> - <amOnPage url="/{{ApiConfigurableProduct.urlKey}}2.html" stepKey="goToConfigProductPage"/> - <waitForPageLoad stepKey="waitForStorefront"/> - <selectOption stepKey="clickFirstAttribute" selector="{{ModifyAttributes.nthExistingAttribute($$createConfigProductAttribute.default_frontend_label$$)}}" userInput="simple"/> - <waitForPageLoad stepKey="waitForPageExecution"/> - <see stepKey="checkPrice" selector="{{StorefrontProductInfoMainSection.productPrice}}" userInput="0.00"/> - <waitForPageLoad stepKey="waitForStoreFrontProductPageLoad"/> - - <!-- Find the simple product that we just created using the product grid and delete it --> - <actionGroup ref="DeleteProductBySkuActionGroup" stepKey="findCreatedProduct2"> - <argument name="sku" value="{{ApiConfigurableProduct.sku}}2-simple"/> - </actionGroup> - </test> -</tests> diff --git a/app/code/Magento/ConfigurableProduct/Test/Mftf/Test/AdminConfigurableProductUpdateAttributeTest/AdminConfigurableProductUpdateAttributeTest.xml b/app/code/Magento/ConfigurableProduct/Test/Mftf/Test/AdminConfigurableProductUpdateAttributeTest/AdminConfigurableProductUpdateAttributeTest.xml new file mode 100644 index 0000000000000..4b6baf8c58493 --- /dev/null +++ b/app/code/Magento/ConfigurableProduct/Test/Mftf/Test/AdminConfigurableProductUpdateAttributeTest/AdminConfigurableProductUpdateAttributeTest.xml @@ -0,0 +1,147 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + /** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +--> +<tests xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/testSchema.xsd"> + <test name="AdminConfigurableProductUpdateAttributeTest"> + <annotations> + <features value="ConfigurableProduct"/> + <stories value="Edit a configurable product in admin"/> + <title value="Admin should be able to update existing attributes of a configurable product"/> + <description value="Admin should be able to update existing attributes of a configurable product"/> + <severity value="AVERAGE"/> + <testCaseId value="MC-179"/> + <group value="ConfigurableProduct"/> + </annotations> + + <before> + <!-- Create the attribute we will be modifying --> + <createData entity="productAttributeWithTwoOptions" stepKey="createModifiableProductAttribute"/> + + <!-- Create the two attributes the product will have --> + <createData entity="productAttributeOption1" stepKey="createModifiableProductAttributeOption1"> + <requiredEntity createDataKey="createModifiableProductAttribute"/> + </createData> + <createData entity="productAttributeOption2" stepKey="createModifiableProductAttributeOption2"> + <requiredEntity createDataKey="createModifiableProductAttribute"/> + </createData> + + <!-- Add the product to the default set --> + <createData entity="AddToDefaultSet" stepKey="createModifiableAddToAttributeSet"> + <requiredEntity createDataKey="createModifiableProductAttribute"/> + </createData> + + <!-- TODO: This should be converted to an actionGroup once MQE-993 is fixed. --> + <!-- Create the category the product will be a part of --> + <createData entity="ApiCategory" stepKey="createCategory"/> + <createData entity="ApiConfigurableProduct" stepKey="createConfigProduct"> + <requiredEntity createDataKey="createCategory"/> + </createData> + <createData entity="productAttributeWithTwoOptions" stepKey="createConfigProductAttribute"/> + + <!-- Create the two attributes the product will have --> + <createData entity="productAttributeOption1" stepKey="createConfigProductAttributeOption1"> + <requiredEntity createDataKey="createConfigProductAttribute"/> + </createData> + <createData entity="productAttributeOption2" stepKey="createConfigProductAttributeOption2"> + <requiredEntity createDataKey="createConfigProductAttribute"/> + </createData> + + <!-- Add the product to the default set --> + <createData entity="AddToDefaultSet" stepKey="createConfigAddToAttributeSet"> + <requiredEntity createDataKey="createConfigProductAttribute"/> + </createData> + + <!-- Get the two attributes --> + <getData entity="ProductAttributeOptionGetter" index="1" stepKey="getConfigAttributeOption1"> + <requiredEntity createDataKey="createConfigProductAttribute"/> + </getData> + <getData entity="ProductAttributeOptionGetter" index="2" stepKey="getConfigAttributeOption2"> + <requiredEntity createDataKey="createConfigProductAttribute"/> + </getData> + + <!-- Create the two children product --> + <createData entity="ApiSimpleOne" stepKey="createConfigChildProduct1"> + <requiredEntity createDataKey="createConfigProductAttribute"/> + <requiredEntity createDataKey="getConfigAttributeOption1"/> + </createData> + <createData entity="ApiSimpleTwo" stepKey="createConfigChildProduct2"> + <requiredEntity createDataKey="createConfigProductAttribute"/> + <requiredEntity createDataKey="getConfigAttributeOption2"/> + </createData> + + <!-- Create the two configurable product with both children --> + <createData entity="ConfigurableProductTwoOptions" stepKey="createConfigProductOption"> + <requiredEntity createDataKey="createConfigProduct"/> + <requiredEntity createDataKey="createConfigProductAttribute"/> + <requiredEntity createDataKey="getConfigAttributeOption1"/> + <requiredEntity createDataKey="getConfigAttributeOption2"/> + </createData> + <createData entity="ConfigurableProductAddChild" stepKey="createConfigProductAddChild1"> + <requiredEntity createDataKey="createConfigProduct"/> + <requiredEntity createDataKey="createConfigChildProduct1"/> + </createData> + <createData entity="ConfigurableProductAddChild" stepKey="createConfigProductAddChild2"> + <requiredEntity createDataKey="createConfigProduct"/> + <requiredEntity createDataKey="createConfigChildProduct2"/> + </createData> + + <!-- login --> + <actionGroup ref="AdminLoginActionGroup" stepKey="login"/> + </before> + + <after> + <actionGroup ref="AdminLogoutActionGroup" stepKey="adminLogout"/> + + <!-- Delete everything that was created in the before block --> + <deleteData createDataKey="createCategory" stepKey="deleteCatagory"/> + <deleteData createDataKey="createConfigProduct" stepKey="deleteConfigProduct"/> + <deleteData createDataKey="createConfigChildProduct1" stepKey="deleteConfigChildProduct1"/> + <deleteData createDataKey="createConfigChildProduct2" stepKey="deleteConfigChildProduct2"/> + <deleteData createDataKey="createModifiableProductAttribute" stepKey="deleteModifiableProductAttribute"/> + <deleteData createDataKey="createConfigProductAttribute" stepKey="deleteConfigProductAttribute"/> + + <!-- Reindex invalidated indices after product attribute has been created/deleted --> + <magentoCron groups="index" stepKey="reindexInvalidatedIndices"/> + </after> + + <!-- Get the current option of the attribute before it was changed --> + <amOnPage url="/{{ApiConfigurableProduct.urlKey}}2.html" stepKey="goToConfigProductPage"/> + <waitForPageLoad stepKey="waitForStoreFrontProductPageLoad"/> + + <grabTextFrom stepKey="getBeforeOption" selector="{{StorefrontProductInfoMainSection.nthAttributeOnPage('1')}}"/> + + <!-- Find the product that we just created using the product grid --> + <amOnPage url="{{AdminProductIndexPage.url}}" stepKey="visitAdminProductPage"/> + <waitForPageLoad stepKey="waitForAdminProductPageLoad"/> + <conditionalClick selector="{{AdminProductGridFilterSection.clearFilters}}" dependentSelector="{{AdminProductGridFilterSection.clearFilters}}" visible="true" stepKey="clickClearFiltersInitial"/> + <actionGroup ref="FilterProductGridBySkuActionGroup" stepKey="findCreatedProduct"> + <argument name="product" value="ApiConfigurableProduct"/> + </actionGroup> + <waitForPageLoad stepKey="waitForProductFilterLoad"/> + + <click selector="{{AdminProductGridSection.firstRow}}" stepKey="clickOnProductPage"/> + <waitForPageLoad stepKey="waitForProductPageLoad"/> + + <!-- change the option on the first attribute --> + <selectOption stepKey="clickFirstAttribute" selector="{{ModifyAttributes.nthExistingAttribute($$createModifiableProductAttribute.default_frontend_label$$)}}" userInput="option1"/> + + <!-- Save the product --> + <click stepKey="saveProductAttribute" selector="{{AdminProductFormActionSection.saveButton}}"/> + <see stepKey="assertSuccess" selector="{{AdminProductMessagesSection.successMessage}}" userInput="You saved the product."/> + + <!-- Go back to the configurable product page and check to see if it has changed --> + <amOnPage url="/{{ApiConfigurableProduct.urlKey}}2.html" stepKey="goToConfigProductPage2"/> + <waitForPageLoad stepKey="waitForStoreFrontProductPageLoad2"/> + <grabTextFrom stepKey="getCurrentOption" selector="{{StorefrontProductInfoMainSection.nthAttributeOnPage('1')}}"/> + <assertNotEquals stepKey="assertNotEquals"> + <actualResult type="string">{$getCurrentOption}</actualResult> + <expectedResult type="string">{$getBeforeOption}</expectedResult> + </assertNotEquals> + + </test> +</tests> diff --git a/app/code/Magento/ConfigurableProduct/Test/Mftf/Test/AdminConfigurableProductUpdateAttributeTest/AdminConfigurableProductUpdateChildAttributeTest.xml b/app/code/Magento/ConfigurableProduct/Test/Mftf/Test/AdminConfigurableProductUpdateAttributeTest/AdminConfigurableProductUpdateChildAttributeTest.xml new file mode 100644 index 0000000000000..56f53519e69af --- /dev/null +++ b/app/code/Magento/ConfigurableProduct/Test/Mftf/Test/AdminConfigurableProductUpdateAttributeTest/AdminConfigurableProductUpdateChildAttributeTest.xml @@ -0,0 +1,142 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + /** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +--> +<tests xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/testSchema.xsd"> + <test name="AdminConfigurableProductUpdateChildAttributeTest"> + <annotations> + <features value="ConfigurableProduct"/> + <stories value="Edit a configurable product in admin"/> + <title value="Admin should be able to update existing attributes of child products of a configurable product"/> + <description value="Admin should be able to update existing attributes of child products of a configurable product"/> + <severity value="AVERAGE"/> + <testCaseId value="MC-288"/> + <group value="ConfigurableProduct"/> + </annotations> + + <before> + + <!-- TODO: This should be converted to an actionGroup once MQE-993 is fixed. --> + <!-- Create the category the product will be a part of --> + <createData entity="ApiCategory" stepKey="createCategory"/> + <createData entity="ApiConfigurableProduct" stepKey="createConfigProduct"> + <requiredEntity createDataKey="createCategory"/> + </createData> + <createData entity="productAttributeWithTwoOptions" stepKey="createConfigProductAttribute"/> + + <!-- Create the two attributes the product will have --> + <createData entity="productAttributeOption1" stepKey="createConfigProductAttributeOption1"> + <requiredEntity createDataKey="createConfigProductAttribute"/> + </createData> + <createData entity="productAttributeOption2" stepKey="createConfigProductAttributeOption2"> + <requiredEntity createDataKey="createConfigProductAttribute"/> + </createData> + + <!-- Add the product to the default set --> + <createData entity="AddToDefaultSet" stepKey="createConfigAddToAttributeSet"> + <requiredEntity createDataKey="createConfigProductAttribute"/> + </createData> + + <!-- Get the two attributes --> + <getData entity="ProductAttributeOptionGetter" index="1" stepKey="getConfigAttributeOption1"> + <requiredEntity createDataKey="createConfigProductAttribute"/> + </getData> + <getData entity="ProductAttributeOptionGetter" index="2" stepKey="getConfigAttributeOption2"> + <requiredEntity createDataKey="createConfigProductAttribute"/> + </getData> + + <!-- Create the two children product --> + <createData entity="ApiSimpleOne" stepKey="createConfigChildProduct1"> + <requiredEntity createDataKey="createConfigProductAttribute"/> + <requiredEntity createDataKey="getConfigAttributeOption1"/> + </createData> + <createData entity="ApiSimpleTwo" stepKey="createConfigChildProduct2"> + <requiredEntity createDataKey="createConfigProductAttribute"/> + <requiredEntity createDataKey="getConfigAttributeOption2"/> + </createData> + + <!-- Create the two configurable product with both children --> + <createData entity="ConfigurableProductTwoOptions" stepKey="createConfigProductOption"> + <requiredEntity createDataKey="createConfigProduct"/> + <requiredEntity createDataKey="createConfigProductAttribute"/> + <requiredEntity createDataKey="getConfigAttributeOption1"/> + <requiredEntity createDataKey="getConfigAttributeOption2"/> + </createData> + <createData entity="ConfigurableProductAddChild" stepKey="createConfigProductAddChild1"> + <requiredEntity createDataKey="createConfigProduct"/> + <requiredEntity createDataKey="createConfigChildProduct1"/> + </createData> + <createData entity="ConfigurableProductAddChild" stepKey="createConfigProductAddChild2"> + <requiredEntity createDataKey="createConfigProduct"/> + <requiredEntity createDataKey="createConfigChildProduct2"/> + </createData> + + <!-- login --> + <actionGroup ref="AdminLoginActionGroup" stepKey="login"/> + </before> + + <after> + <actionGroup ref="AdminLogoutActionGroup" stepKey="adminLogout"/> + + <!-- Delete everything that was created in the before block --> + <deleteData createDataKey="createCategory" stepKey="deleteCatagory"/> + <deleteData createDataKey="createConfigProduct" stepKey="deleteConfigProduct"/> + <deleteData createDataKey="createConfigChildProduct1" stepKey="deleteConfigChildProduct1"/> + <deleteData createDataKey="createConfigChildProduct2" stepKey="deleteConfigChildProduct2"/> + <deleteData createDataKey="createConfigProductAttribute" stepKey="deleteConfigProductAttribute"/> + + <!-- Reindex invalidated indices after product attribute has been created/deleted --> + <magentoCron groups="index" stepKey="reindexInvalidatedIndices"/> + </after> + + <!-- Find the product that we just created using the product grid --> + <amOnPage url="{{AdminProductIndexPage.url}}" stepKey="visitAdminProductPage"/> + <waitForPageLoad stepKey="waitForAdminProductPageLoad"/> + <conditionalClick selector="{{AdminProductGridFilterSection.clearFilters}}" dependentSelector="{{AdminProductGridFilterSection.clearFilters}}" visible="true" stepKey="clickClearFiltersInitial"/> + <actionGroup ref="FilterProductGridBySkuActionGroup" stepKey="findCreatedProduct"> + <argument name="product" value="$$createConfigProduct$$"/> + </actionGroup> + <waitForPageLoad stepKey="waitForProductFilterLoad"/> + <actionGroup ref="OpenEditProductOnBackendActionGroup" stepKey="openProduct"> + <argument name="product" value="$$createConfigProduct$$"/> + </actionGroup> + + <!-- Open the wizard for editing configurations and fill out a new attribute --> + <click stepKey="clickEditConfig" selector="{{AdminProductFormConfigurationsSection.createConfigurations}}"/> + <waitForPageLoad stepKey="waitForEditConfig"/> + <click stepKey="clickNextWizard" selector="{{AdminCreateProductConfigurationsPanel.next}}"/> + <waitForElementVisible selector="{{AdminCreateProductConfigurationsPanel.createNewValue}}" stepKey="waitCreateNewValueAppears"/> + <click stepKey="createNewValue" selector="{{AdminCreateProductConfigurationsPanel.createNewValue}}"/> + <fillField stepKey="fillNewAttribute" selector="{{AdminCreateProductConfigurationsPanel.attributeName}}" userInput="simple"/> + <click stepKey="confirmNewAttribute" selector="{{AdminCreateProductConfigurationsPanel.saveAttribute}}"/> + <click stepKey="clickNextWizard2" selector="{{AdminCreateProductConfigurationsPanel.next}}"/> + + <!-- Give the product a price and quantity --> + <click stepKey="click" selector="{{AdminCreateProductConfigurationsPanel.applySingleQuantityToEachSkus}}"/> + <fillField stepKey="fillProductQuantity" selector="{{AdminCreateProductConfigurationsPanel.quantity}}" userInput="{{_defaultProduct.quantity}}"/> + <click stepKey="clickNextWizard3" selector="{{AdminCreateProductConfigurationsPanel.next}}"/> + <click stepKey="clickGenerateProducts" selector="{{AdminCreateProductConfigurationsPanel.next}}"/> + + <!-- Save the product --> + <waitForPageLoad stepKey="waitForGeneration"/> + <click stepKey="saveProductAttribute" selector="{{AdminProductFormActionSection.saveButton}}"/> + <see stepKey="assertSuccess" selector="{{AdminProductMessagesSection.successMessage}}" userInput="You saved the product."/> + + <!-- Check to make sure the created product has appeared on the configurable product storefront --> + <amOnPage url="/{{ApiConfigurableProduct.urlKey}}2.html" stepKey="goToConfigProductPage"/> + <waitForPageLoad stepKey="waitForStorefront"/> + <selectOption stepKey="clickFirstAttribute" selector="{{ModifyAttributes.nthExistingAttribute($$createConfigProductAttribute.default_frontend_label$$)}}" userInput="simple"/> + <waitForPageLoad stepKey="waitForPageExecution"/> + <see stepKey="checkPrice" selector="{{StorefrontProductInfoMainSection.productPrice}}" userInput="0.00"/> + <waitForPageLoad stepKey="waitForStoreFrontProductPageLoad"/> + + <!-- Find the simple product that we just created using the product grid and delete it --> + <actionGroup ref="DeleteProductBySkuActionGroup" stepKey="findCreatedProduct2"> + <argument name="sku" value="{{ApiConfigurableProduct.sku}}2-simple"/> + </actionGroup> + </test> +</tests> diff --git a/app/code/Magento/ConfigurableProduct/Test/Mftf/Test/AdminConfigurableProductUpdateTest.xml b/app/code/Magento/ConfigurableProduct/Test/Mftf/Test/AdminConfigurableProductUpdateTest.xml deleted file mode 100644 index da0c5a65f944d..0000000000000 --- a/app/code/Magento/ConfigurableProduct/Test/Mftf/Test/AdminConfigurableProductUpdateTest.xml +++ /dev/null @@ -1,368 +0,0 @@ -<?xml version="1.0" encoding="UTF-8"?> -<!-- - /** - * Copyright © Magento, Inc. All rights reserved. - * See COPYING.txt for license details. - */ ---> - -<tests xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" - xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/testSchema.xsd"> - <test name="AdminConfigurableProductBulkUpdateTest"> - <annotations> - <features value="ConfigurableProduct"/> - <stories value="Create, Read, Update, Delete"/> - <title value="admin should be able to bulk update attributes of configurable products"/> - <description value="admin should be able to bulk update attributes of configurable products"/> - <testCaseId value="MC-88"/> - <group value="ConfigurableProduct"/> - <severity value="AVERAGE"/> - </annotations> - - <before> - <createData entity="ApiCategory" stepKey="createCategory"/> - <createData entity="ApiConfigurableProduct" stepKey="createProduct1"> - <requiredEntity createDataKey="createCategory"/> - </createData> - <createData entity="ApiConfigurableProduct" stepKey="createProduct2"> - <requiredEntity createDataKey="createCategory"/> - </createData> - <createData entity="ApiConfigurableProduct" stepKey="createProduct3"> - <requiredEntity createDataKey="createCategory"/> - </createData> - <actionGroup ref="LoginAsAdmin" stepKey="login"/> - </before> - - <after> - <deleteData createDataKey="createCategory" stepKey="deleteCategory"/> - <deleteData createDataKey="createProduct1" stepKey="deleteFirstProduct"/> - <deleteData createDataKey="createProduct2" stepKey="deleteSecondProduct"/> - <deleteData createDataKey="createProduct3" stepKey="deleteThirdProduct"/> - <amOnPage url="{{AdminProductIndexPage.url}}" stepKey="goToProductList"/> - <waitForPageLoad stepKey="waitForPageLoad"/> - <actionGroup ref="ClearFiltersAdminDataGridActionGroup" stepKey="clearProductsGridFilters"/> - <actionGroup ref="logout" stepKey="logout"/> - </after> - - <!-- Search for prefix of the 3 products we created via api --> - <amOnPage url="{{AdminProductIndexPage.url}}" stepKey="goToProductList"/> - <waitForPageLoad stepKey="wait1"/> - <conditionalClick selector="{{AdminProductGridFilterSection.clearAll}}" dependentSelector="{{AdminProductGridFilterSection.clearAll}}" visible="true" stepKey="clearAll"/> - <actionGroup ref="SearchProductGridByKeywordActionGroup" stepKey="searchForProduct"> - <argument name="keyword" value="ApiConfigurableProduct.name"/> - </actionGroup> - - <!-- Select all, then start the bulk update attributes flow --> - <click selector="{{AdminProductGridSection.multicheckDropdown}}" stepKey="openMulticheckDropdown"/> - <click selector="{{AdminProductGridSection.multicheckOption('Select All')}}" stepKey="selectAllProductInFilteredGrid"/> - <click selector="{{AdminProductGridSection.bulkActionDropdown}}" stepKey="clickActionDropdown"/> - <click selector="{{AdminProductGridSection.bulkActionOption('Update attributes')}}" stepKey="clickBulkUpdate"/> - <waitForPageLoad stepKey="wait2"/> - - <!-- Update the description --> - <click selector="{{AdminUpdateAttributesSection.toggleDescription}}" stepKey="clickToggleDescription"/> - <fillField selector="{{AdminUpdateAttributesSection.description}}" userInput="MFTF automation!" stepKey="fillDescription"/> - <click selector="{{AdminEditProductAttributesSection.Save}}" stepKey="clickSave"/> - <waitForElementVisible selector="{{AdminProductMessagesSection.successMessage}}" time="60" stepKey="waitForSuccessMessage"/> - <see selector="{{AdminProductMessagesSection.successMessage}}" userInput="Message is added to queue" stepKey="seeAttributeUpdateSuccessMsg"/> - - <!-- Run cron twice --> - <magentoCLI command="cron:run" arguments="--group=consumers" stepKey="runCron1"/> - <magentoCLI command="cron:run" arguments="--group=consumers" stepKey="runCron2"/> - <magentoCLI command="indexer:reindex" stepKey="reindex"/> - - <!-- Check storefront for description --> - <amOnPage url="{{StorefrontProductPage.url($$createProduct1.custom_attributes[url_key]$$)}}" stepKey="goToFirstProductPageOnStorefront"/> - <waitForPageLoad stepKey="waitForFirstProductPageLoad"/> - <see selector="{{StorefrontProductInfoMainSection.productDescription}}" userInput="MFTF automation!" stepKey="seeFirstDescription"/> - <amOnPage url="{{StorefrontProductPage.url($$createProduct2.custom_attributes[url_key]$$)}}" stepKey="goToSecondProductPageOnStorefront"/> - <waitForPageLoad stepKey="waitForSecondProductPageLoad"/> - <see selector="{{StorefrontProductInfoMainSection.productDescription}}" userInput="MFTF automation!" stepKey="seeSecondDescription"/> - <amOnPage url="{{StorefrontProductPage.url($$createProduct3.custom_attributes[url_key]$$)}}" stepKey="goToThirdProductPageOnStorefront"/> - <waitForPageLoad stepKey="waitForThirdProductPageLoad"/> - <see selector="{{StorefrontProductInfoMainSection.productDescription}}" userInput="MFTF automation!" stepKey="seeThirdDescription"/> - </test> - - <test name="AdminConfigurableProductRemoveAnOptionTest"> - <annotations> - <features value="ConfigurableProduct"/> - <stories value="Create, Read, Update, Delete"/> - <title value="Admin should be able to remove a product configuration"/> - <description value="Admin should be able to remove a product configuration"/> - <testCaseId value="MC-63"/> - <group value="ConfigurableProduct"/> - <severity value="AVERAGE"/> - </annotations> - - <before> - <!-- This was copied and modified from the EndToEndB2CGuestUserTest --> - <createData entity="ApiCategory" stepKey="createCategory"/> - <createData entity="ApiConfigurableProduct" stepKey="createConfigProduct"> - <requiredEntity createDataKey="createCategory"/> - </createData> - <createData entity="productAttributeWithTwoOptions" stepKey="createConfigProductAttribute"/> - <createData entity="productAttributeOption1" stepKey="createConfigProductAttributeOption1"> - <requiredEntity createDataKey="createConfigProductAttribute"/> - </createData> - <createData entity="productAttributeOption2" stepKey="createConfigProductAttributeOption2"> - <requiredEntity createDataKey="createConfigProductAttribute"/> - </createData> - <createData entity="AddToDefaultSet" stepKey="createConfigAddToAttributeSet"> - <requiredEntity createDataKey="createConfigProductAttribute"/> - </createData> - <getData entity="ProductAttributeOptionGetter" index="1" stepKey="getConfigAttributeOption1"> - <requiredEntity createDataKey="createConfigProductAttribute"/> - </getData> - <getData entity="ProductAttributeOptionGetter" index="2" stepKey="getConfigAttributeOption2"> - <requiredEntity createDataKey="createConfigProductAttribute"/> - </getData> - <createData entity="ApiSimpleOne" stepKey="createConfigChildProduct1"> - <requiredEntity createDataKey="createConfigProductAttribute"/> - <requiredEntity createDataKey="getConfigAttributeOption1"/> - </createData> - <createData entity="ApiSimpleTwo" stepKey="createConfigChildProduct2"> - <requiredEntity createDataKey="createConfigProductAttribute"/> - <requiredEntity createDataKey="getConfigAttributeOption2"/> - </createData> - <createData entity="ConfigurableProductTwoOptions" stepKey="createConfigProductOption"> - <requiredEntity createDataKey="createConfigProduct"/> - <requiredEntity createDataKey="createConfigProductAttribute"/> - <requiredEntity createDataKey="getConfigAttributeOption1"/> - <requiredEntity createDataKey="getConfigAttributeOption2"/> - </createData> - <createData entity="ConfigurableProductAddChild" stepKey="createConfigProductAddChild1"> - <requiredEntity createDataKey="createConfigProduct"/> - <requiredEntity createDataKey="createConfigChildProduct1"/> - </createData> - <createData entity="ConfigurableProductAddChild" stepKey="createConfigProductAddChild2"> - <requiredEntity createDataKey="createConfigProduct"/> - <requiredEntity createDataKey="createConfigChildProduct2"/> - </createData> - </before> - - <after> - <deleteData createDataKey="createConfigChildProduct1" stepKey="deleteConfigChildProduct1"/> - <deleteData createDataKey="createConfigChildProduct2" stepKey="deleteConfigChildProduct2"/> - <deleteData createDataKey="createConfigProduct" stepKey="deleteConfigProduct"/> - <deleteData createDataKey="createConfigProductAttribute" stepKey="deleteConfigProductAttribute"/> - </after> - - <!--check storefront for both options--> - <amOnPage url="$$createConfigProduct.sku$$.html" stepKey="amOnStorefront1"/> - <waitForPageLoad stepKey="wait1"/> - <see selector="{{StorefrontProductInfoMainSection.productAttributeOptions1}}" userInput="option1" stepKey="seeOption1Storefront"/> - <see selector="{{StorefrontProductInfoMainSection.productAttributeOptions1}}" userInput="option2" stepKey="seeOption2Storefront"/> - - <!--check admin for both options--> - <actionGroup ref="LoginAsAdmin" stepKey="loginAsAdmin"/> - <amOnPage url="{{AdminProductEditPage.url($$createConfigProduct.id$$)}}" stepKey="goToEditPage"/> - <waitForPageLoad stepKey="wait2"/> - <see selector="{{AdminProductFormConfigurationsSection.currentVariationsNameCells}}" userInput="$$createConfigChildProduct1.name$$" stepKey="seeOption1Admin"/> - <see selector="{{AdminProductFormConfigurationsSection.currentVariationsNameCells}}" userInput="$$createConfigChildProduct2.name$$" stepKey="seeOption2Admin"/> - - <!--remove an option--> - <click selector="{{AdminProductFormConfigurationsSection.actionsBtn('1')}}" stepKey="clickToExpandActions"/> - <click selector="{{AdminProductFormConfigurationsSection.removeProductBtn}}" stepKey="clickRemove"/> - <click selector="{{AdminProductFormActionSection.saveButton}}" stepKey="clickSave"/> - <waitForPageLoad stepKey="wait3"/> - - <!--check admin for one option--> - <dontSee selector="{{AdminProductFormConfigurationsSection.currentVariationsNameCells}}" userInput="$$createConfigChildProduct1.name$$" stepKey="dontSeeOption1Admin"/> - - <!--check storefront for one option--> - <amOnPage url="$$createConfigProduct.sku$$.html" stepKey="amOnStorefront2"/> - <waitForPageLoad stepKey="wait4"/> - <dontSee selector="{{StorefrontProductInfoMainSection.productAttributeOptions1}}" userInput="option1" stepKey="dontSeeOption1InStorefront"/> - <see selector="{{StorefrontProductInfoMainSection.productAttributeOptions1}}" userInput="option2" stepKey="seeOption2Again"/> - </test> - - <test name="AdminConfigurableProductDisableAnOptionTest"> - <annotations> - <features value="ConfigurableProduct"/> - <stories value="Create, Read, Update, Delete"/> - <title value="Admin should be able to disable a product configuration"/> - <description value="Admin should be able to disable a product configuration"/> - <testCaseId value="MC-119"/> - <group value="ConfigurableProduct"/> - <severity value="AVERAGE"/> - </annotations> - - <before> - <!-- This was copied and modified from the EndToEndB2CGuestUserTest --> - <createData entity="ApiCategory" stepKey="createCategory"/> - <createData entity="ApiConfigurableProduct" stepKey="createConfigProduct"> - <requiredEntity createDataKey="createCategory"/> - </createData> - <createData entity="productAttributeWithTwoOptions" stepKey="createConfigProductAttribute"/> - <createData entity="productAttributeOption1" stepKey="createConfigProductAttributeOption1"> - <requiredEntity createDataKey="createConfigProductAttribute"/> - </createData> - <createData entity="productAttributeOption2" stepKey="createConfigProductAttributeOption2"> - <requiredEntity createDataKey="createConfigProductAttribute"/> - </createData> - <createData entity="AddToDefaultSet" stepKey="createConfigAddToAttributeSet"> - <requiredEntity createDataKey="createConfigProductAttribute"/> - </createData> - <getData entity="ProductAttributeOptionGetter" index="1" stepKey="getConfigAttributeOption1"> - <requiredEntity createDataKey="createConfigProductAttribute"/> - </getData> - <getData entity="ProductAttributeOptionGetter" index="2" stepKey="getConfigAttributeOption2"> - <requiredEntity createDataKey="createConfigProductAttribute"/> - </getData> - <createData entity="ApiSimpleOne" stepKey="createConfigChildProduct1"> - <requiredEntity createDataKey="createConfigProductAttribute"/> - <requiredEntity createDataKey="getConfigAttributeOption1"/> - </createData> - <createData entity="ApiSimpleTwo" stepKey="createConfigChildProduct2"> - <requiredEntity createDataKey="createConfigProductAttribute"/> - <requiredEntity createDataKey="getConfigAttributeOption2"/> - </createData> - <createData entity="ConfigurableProductTwoOptions" stepKey="createConfigProductOption"> - <requiredEntity createDataKey="createConfigProduct"/> - <requiredEntity createDataKey="createConfigProductAttribute"/> - <requiredEntity createDataKey="getConfigAttributeOption1"/> - <requiredEntity createDataKey="getConfigAttributeOption2"/> - </createData> - <createData entity="ConfigurableProductAddChild" stepKey="createConfigProductAddChild1"> - <requiredEntity createDataKey="createConfigProduct"/> - <requiredEntity createDataKey="createConfigChildProduct1"/> - </createData> - <createData entity="ConfigurableProductAddChild" stepKey="createConfigProductAddChild2"> - <requiredEntity createDataKey="createConfigProduct"/> - <requiredEntity createDataKey="createConfigChildProduct2"/> - </createData> - </before> - - <after> - <deleteData createDataKey="createConfigChildProduct1" stepKey="deleteConfigChildProduct1"/> - <deleteData createDataKey="createConfigChildProduct2" stepKey="deleteConfigChildProduct2"/> - <deleteData createDataKey="createConfigProduct" stepKey="deleteConfigProduct"/> - <deleteData createDataKey="createConfigProductAttribute" stepKey="deleteConfigProductAttribute"/> - </after> - - <!--check storefront for both options--> - <amOnPage url="$$createConfigProduct.sku$$.html" stepKey="amOnStorefront1"/> - <waitForPageLoad stepKey="wait1"/> - <see selector="{{StorefrontProductInfoMainSection.productAttributeOptions1}}" userInput="option1" stepKey="seeOption1Storefront"/> - <see selector="{{StorefrontProductInfoMainSection.productAttributeOptions1}}" userInput="option2" stepKey="seeOption2Storefront"/> - - <!--go to admin and disable an option--> - <actionGroup ref="LoginAsAdmin" stepKey="loginAsAdmin"/> - <amOnPage url="{{AdminProductEditPage.url($$createConfigProduct.id$$)}}" stepKey="goToEditPage"/> - <waitForPageLoad stepKey="wait2"/> - <click selector="{{AdminProductFormConfigurationsSection.actionsBtn('1')}}" stepKey="clickToExpandActions"/> - <click selector="{{AdminProductFormConfigurationsSection.disableProductBtn}}" stepKey="clickDisable"/> - <click selector="{{AdminProductFormActionSection.saveButton}}" stepKey="clickSave"/> - <waitForPageLoad stepKey="wait3"/> - - <!--check storefront for one option--> - <amOnPage url="$$createConfigProduct.sku$$.html" stepKey="amOnStorefront2"/> - <waitForPageLoad stepKey="wait4"/> - <dontSee selector="{{StorefrontProductInfoMainSection.productAttributeOptions1}}" userInput="option1" stepKey="dontSeeOption1InStorefront"/> - <see selector="{{StorefrontProductInfoMainSection.productAttributeOptions1}}" userInput="option2" stepKey="seeOption2Again"/> - </test> - - <test name="AdminConfigurableProductRemoveConfigurationTest"> - <annotations> - <features value="ConfigurableProduct"/> - <stories value="Edit a configurable product in admin"/> - <title value="Admin should be able to remove a configuration from a Configurable Product"/> - <description value="Admin should be able to remove a configuration from a Configurable Product"/> - <severity value="AVERAGE"/> - <testCaseId value="MC-86"/> - <group value="ConfigurableProduct"/> - </annotations> - - <before> - <createData entity="ApiCategory" stepKey="createCategory"/> - <actionGroup ref="LoginAsAdmin" stepKey="loginAsAdmin"/> - </before> - - <after> - <deleteData createDataKey="createCategory" stepKey="deleteCategory"/> - <actionGroup ref="logout" stepKey="adminLogout"/> - </after> - - <!-- Create a configurable product via the UI --> - <actionGroup ref="CreateConfigurableProductActionGroup" stepKey="createProduct"> - <argument name="product" value="_defaultProduct"/> - <argument name="category" value="$$createCategory$$"/> - </actionGroup> - - <!-- - After saving, we are still on the product edit page. There is no need to reload or go to this page - again, because a round trip to the server has already happened. - --> - - <!-- Remove a configuration option from the configurable product --> - <click selector="{{AdminProductFormConfigurationsSection.createConfigurations}}" stepKey="clickEditConfigurations"/> - <click selector="{{AdminCreateProductConfigurationsPanel.next}}" stepKey="clickOnNextButton1"/> - <click selector="{{AdminCreateProductConfigurationsPanel.attributeCheckboxByIndex('1')}}" stepKey="deselectOption"/> - <click selector="{{AdminCreateProductConfigurationsPanel.next}}" stepKey="clickOnNextButton2"/> - <click selector="{{AdminCreateProductConfigurationsPanel.next}}" stepKey="clickOnNextButton3"/> - <click selector="{{AdminCreateProductConfigurationsPanel.next}}" stepKey="clickOnNextButton4"/> - <click selector="{{AdminProductFormActionSection.saveButton}}" stepKey="saveProduct"/> - - <!-- Verify that the removed option is not present in the storefront --> - <amOnPage url="{{StorefrontProductPage.url(_defaultProduct.urlKey)}}" stepKey="amOnStorefrontPage"/> - <waitForPageLoad stepKey="waitForStorefrontPage"/> - <dontSee selector="{{StorefrontProductInfoMainSection.productAttributeOptions1}}" userInput="{{colorProductAttribute1.name}}" stepKey="seeInDropDown1"/> - <see selector="{{StorefrontProductInfoMainSection.productAttributeOptions1}}" userInput="{{colorProductAttribute2.name}}" stepKey="seeInDropDown2"/> - <see selector="{{StorefrontProductInfoMainSection.productAttributeOptions1}}" userInput="{{colorProductAttribute3.name}}" stepKey="seeInDropDown3"/> - </test> - - <test name="AdminConfigurableProductAddConfigurationTest"> - <annotations> - <features value="ConfigurableProduct"/> - <stories value="Edit a configurable product in admin"/> - <title value="Admin should be able to edit configuration to add a value to an existing attribute"/> - <description value="Admin should be able to edit configuration to add a value to an existing attribute"/> - <severity value="AVERAGE"/> - <testCaseId value="MC-95"/> - <group value="ConfigurableProduct"/> - </annotations> - - <before> - <createData entity="ApiCategory" stepKey="createCategory"/> - <actionGroup ref="LoginAsAdmin" stepKey="loginAsAdmin"/> - </before> - - <after> - <deleteData createDataKey="createCategory" stepKey="deleteCategory"/> - <actionGroup ref="logout" stepKey="adminLogout"/> - </after> - - <!-- Create a configurable product via the UI --> - <actionGroup ref="CreateConfigurableProductActionGroup" stepKey="createProduct"> - <argument name="product" value="_defaultProduct"/> - <argument name="category" value="$$createCategory$$"/> - </actionGroup> - - <!-- - After saving, we are still on the product edit page. There is no need to reload or go to this page - again, because a round trip to the server has already happened. - --> - - <!-- Add a configuration option to the configurable product --> - <click selector="{{AdminProductFormConfigurationsSection.createConfigurations}}" stepKey="clickEditConfigurations"/> - <click selector="{{AdminCreateProductConfigurationsPanel.next}}" stepKey="clickOnNextButton1"/> - <waitForElementVisible selector="{{AdminCreateProductConfigurationsPanel.createNewValue}}" stepKey="waitCreateNewValueAppears"/> - <click selector="{{AdminCreateProductConfigurationsPanel.createNewValue}}" stepKey="clickOnCreateNewValue1"/> - <fillField userInput="{{colorProductAttribute4.name}}" selector="{{AdminCreateProductConfigurationsPanel.attributeName}}" stepKey="fillFieldForNewAttribute1"/> - <click selector="{{AdminCreateProductConfigurationsPanel.saveAttribute}}" stepKey="clickOnSaveNewAttribute1"/> - <click selector="{{AdminCreateProductConfigurationsPanel.next}}" stepKey="clickOnNextButton2"/> - <click selector="{{AdminCreateProductConfigurationsPanel.applySingleQuantityToEachSkus}}" stepKey="clickOnApplySingleQuantityToEachSku"/> - <fillField selector="{{AdminCreateProductConfigurationsPanel.quantity}}" userInput="42" stepKey="enterAttributeQuantity"/> - <click selector="{{AdminCreateProductConfigurationsPanel.next}}" stepKey="clickOnNextButton3"/> - <click selector="{{AdminCreateProductConfigurationsPanel.next}}" stepKey="clickOnNextButton4"/> - <click selector="{{AdminProductFormActionSection.saveButton}}" stepKey="saveProduct"/> - - <!-- Verify that the added option is present in the storefront --> - <amOnPage url="{{StorefrontProductPage.url(_defaultProduct.urlKey)}}" stepKey="amOnStorefrontPage"/> - <waitForPageLoad stepKey="waitForStorefrontPage"/> - <see selector="{{StorefrontProductInfoMainSection.productAttributeOptions1}}" userInput="{{colorProductAttribute1.name}}" stepKey="seeInDropDown1"/> - <see selector="{{StorefrontProductInfoMainSection.productAttributeOptions1}}" userInput="{{colorProductAttribute2.name}}" stepKey="seeInDropDown2"/> - <see selector="{{StorefrontProductInfoMainSection.productAttributeOptions1}}" userInput="{{colorProductAttribute3.name}}" stepKey="seeInDropDown3"/> - <see selector="{{StorefrontProductInfoMainSection.productAttributeOptions1}}" userInput="{{colorProductAttribute4.name}}" stepKey="seeInDropDown4"/> - </test> -</tests> diff --git a/app/code/Magento/ConfigurableProduct/Test/Mftf/Test/AdminConfigurableProductUpdateTest/AdminConfigurableProductAddConfigurationTest.xml b/app/code/Magento/ConfigurableProduct/Test/Mftf/Test/AdminConfigurableProductUpdateTest/AdminConfigurableProductAddConfigurationTest.xml new file mode 100644 index 0000000000000..589f20d0d544c --- /dev/null +++ b/app/code/Magento/ConfigurableProduct/Test/Mftf/Test/AdminConfigurableProductUpdateTest/AdminConfigurableProductAddConfigurationTest.xml @@ -0,0 +1,64 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + /** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +--> +<tests xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/testSchema.xsd"> + <test name="AdminConfigurableProductAddConfigurationTest"> + <annotations> + <features value="ConfigurableProduct"/> + <stories value="Edit a configurable product in admin"/> + <title value="Admin should be able to edit configuration to add a value to an existing attribute"/> + <description value="Admin should be able to edit configuration to add a value to an existing attribute"/> + <severity value="BLOCKER"/> + <testCaseId value="MC-95"/> + <group value="ConfigurableProduct"/> + </annotations> + + <before> + <createData entity="ApiCategory" stepKey="createCategory"/> + <actionGroup ref="AdminLoginActionGroup" stepKey="loginAsAdmin"/> + </before> + + <after> + <deleteData createDataKey="createCategory" stepKey="deleteCategory"/> + <actionGroup ref="AdminLogoutActionGroup" stepKey="adminLogout"/> + </after> + + <!-- Create a configurable product via the UI --> + <actionGroup ref="CreateConfigurableProductActionGroup" stepKey="createProduct"> + <argument name="product" value="_defaultProduct"/> + <argument name="category" value="$$createCategory$$"/> + </actionGroup> + + <!-- + After saving, we are still on the product edit page. There is no need to reload or go to this page + again, because a round trip to the server has already happened. + --> + + <!-- Add a configuration option to the configurable product --> + <click selector="{{AdminProductFormConfigurationsSection.createConfigurations}}" stepKey="clickEditConfigurations"/> + <click selector="{{AdminCreateProductConfigurationsPanel.next}}" stepKey="clickOnNextButton1"/> + <waitForElementVisible selector="{{AdminCreateProductConfigurationsPanel.createNewValue}}" stepKey="waitCreateNewValueAppears"/> + <click selector="{{AdminCreateProductConfigurationsPanel.createNewValue}}" stepKey="clickOnCreateNewValue1"/> + <fillField userInput="{{colorProductAttribute4.name}}" selector="{{AdminCreateProductConfigurationsPanel.attributeName}}" stepKey="fillFieldForNewAttribute1"/> + <click selector="{{AdminCreateProductConfigurationsPanel.saveAttribute}}" stepKey="clickOnSaveNewAttribute1"/> + <click selector="{{AdminCreateProductConfigurationsPanel.next}}" stepKey="clickOnNextButton2"/> + <click selector="{{AdminCreateProductConfigurationsPanel.applySingleQuantityToEachSkus}}" stepKey="clickOnApplySingleQuantityToEachSku"/> + <fillField selector="{{AdminCreateProductConfigurationsPanel.quantity}}" userInput="42" stepKey="enterAttributeQuantity"/> + <click selector="{{AdminCreateProductConfigurationsPanel.next}}" stepKey="clickOnNextButton3"/> + <click selector="{{AdminCreateProductConfigurationsPanel.next}}" stepKey="clickOnNextButton4"/> + <click selector="{{AdminProductFormActionSection.saveButton}}" stepKey="saveProduct"/> + + <!-- Verify that the added option is present in the storefront --> + <amOnPage url="{{StorefrontProductPage.url(_defaultProduct.urlKey)}}" stepKey="amOnStorefrontPage"/> + <waitForPageLoad stepKey="waitForStorefrontPage"/> + <see selector="{{StorefrontProductInfoMainSection.productAttributeOptions1}}" userInput="{{colorProductAttribute1.name}}" stepKey="seeInDropDown1"/> + <see selector="{{StorefrontProductInfoMainSection.productAttributeOptions1}}" userInput="{{colorProductAttribute2.name}}" stepKey="seeInDropDown2"/> + <see selector="{{StorefrontProductInfoMainSection.productAttributeOptions1}}" userInput="{{colorProductAttribute3.name}}" stepKey="seeInDropDown3"/> + <see selector="{{StorefrontProductInfoMainSection.productAttributeOptions1}}" userInput="{{colorProductAttribute4.name}}" stepKey="seeInDropDown4"/> + </test> +</tests> diff --git a/app/code/Magento/ConfigurableProduct/Test/Mftf/Test/AdminConfigurableProductUpdateTest/AdminConfigurableProductBulkUpdateTest.xml b/app/code/Magento/ConfigurableProduct/Test/Mftf/Test/AdminConfigurableProductUpdateTest/AdminConfigurableProductBulkUpdateTest.xml new file mode 100644 index 0000000000000..bd409d0e4bfde --- /dev/null +++ b/app/code/Magento/ConfigurableProduct/Test/Mftf/Test/AdminConfigurableProductUpdateTest/AdminConfigurableProductBulkUpdateTest.xml @@ -0,0 +1,84 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + /** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +--> +<tests xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/testSchema.xsd"> + <test name="AdminConfigurableProductBulkUpdateTest"> + <annotations> + <features value="ConfigurableProduct"/> + <stories value="Create, Read, Update, Delete"/> + <title value="admin should be able to bulk update attributes of configurable products"/> + <description value="admin should be able to bulk update attributes of configurable products"/> + <testCaseId value="MC-88"/> + <group value="ConfigurableProduct"/> + <severity value="BLOCKER"/> + </annotations> + + <before> + <createData entity="ApiCategory" stepKey="createCategory"/> + <createData entity="ApiConfigurableProduct" stepKey="createProduct1"> + <requiredEntity createDataKey="createCategory"/> + </createData> + <createData entity="ApiConfigurableProduct" stepKey="createProduct2"> + <requiredEntity createDataKey="createCategory"/> + </createData> + <createData entity="ApiConfigurableProduct" stepKey="createProduct3"> + <requiredEntity createDataKey="createCategory"/> + </createData> + <actionGroup ref="AdminLoginActionGroup" stepKey="login"/> + </before> + + <after> + <deleteData createDataKey="createCategory" stepKey="deleteCategory"/> + <deleteData createDataKey="createProduct1" stepKey="deleteFirstProduct"/> + <deleteData createDataKey="createProduct2" stepKey="deleteSecondProduct"/> + <deleteData createDataKey="createProduct3" stepKey="deleteThirdProduct"/> + <amOnPage url="{{AdminProductIndexPage.url}}" stepKey="goToProductList"/> + <waitForPageLoad stepKey="waitForPageLoad"/> + <actionGroup ref="ClearFiltersAdminDataGridActionGroup" stepKey="clearProductsGridFilters"/> + <actionGroup ref="AdminLogoutActionGroup" stepKey="logout"/> + </after> + + <!-- Search for prefix of the 3 products we created via api --> + <amOnPage url="{{AdminProductIndexPage.url}}" stepKey="goToProductList"/> + <waitForPageLoad stepKey="wait1"/> + <conditionalClick selector="{{AdminProductGridFilterSection.clearAll}}" dependentSelector="{{AdminProductGridFilterSection.clearAll}}" visible="true" stepKey="clearAll"/> + <actionGroup ref="SearchProductGridByKeywordActionGroup" stepKey="searchForProduct"> + <argument name="keyword" value="ApiConfigurableProduct.name"/> + </actionGroup> + + <!-- Select all, then start the bulk update attributes flow --> + <click selector="{{AdminProductGridSection.multicheckDropdown}}" stepKey="openMulticheckDropdown"/> + <click selector="{{AdminProductGridSection.multicheckOption('Select All')}}" stepKey="selectAllProductInFilteredGrid"/> + <click selector="{{AdminProductGridSection.bulkActionDropdown}}" stepKey="clickActionDropdown"/> + <click selector="{{AdminProductGridSection.bulkActionOption('Update attributes')}}" stepKey="clickBulkUpdate"/> + <waitForPageLoad stepKey="wait2"/> + + <!-- Update the description --> + <click selector="{{AdminUpdateAttributesSection.toggleDescription}}" stepKey="clickToggleDescription"/> + <fillField selector="{{AdminUpdateAttributesSection.description}}" userInput="MFTF automation!" stepKey="fillDescription"/> + <click selector="{{AdminEditProductAttributesSection.Save}}" stepKey="clickSave"/> + <waitForElementVisible selector="{{AdminProductMessagesSection.successMessage}}" time="60" stepKey="waitForSuccessMessage"/> + <see selector="{{AdminProductMessagesSection.successMessage}}" userInput="Message is added to queue" stepKey="seeAttributeUpdateSuccessMsg"/> + + <!-- Run cron twice --> + <magentoCLI command="cron:run" arguments="--group=consumers" stepKey="runCron1"/> + <magentoCLI command="cron:run" arguments="--group=consumers" stepKey="runCron2"/> + <magentoCLI command="indexer:reindex" stepKey="reindex"/> + + <!-- Check storefront for description --> + <amOnPage url="{{StorefrontProductPage.url($$createProduct1.custom_attributes[url_key]$$)}}" stepKey="goToFirstProductPageOnStorefront"/> + <waitForPageLoad stepKey="waitForFirstProductPageLoad"/> + <see selector="{{StorefrontProductInfoMainSection.productDescription}}" userInput="MFTF automation!" stepKey="seeFirstDescription"/> + <amOnPage url="{{StorefrontProductPage.url($$createProduct2.custom_attributes[url_key]$$)}}" stepKey="goToSecondProductPageOnStorefront"/> + <waitForPageLoad stepKey="waitForSecondProductPageLoad"/> + <see selector="{{StorefrontProductInfoMainSection.productDescription}}" userInput="MFTF automation!" stepKey="seeSecondDescription"/> + <amOnPage url="{{StorefrontProductPage.url($$createProduct3.custom_attributes[url_key]$$)}}" stepKey="goToThirdProductPageOnStorefront"/> + <waitForPageLoad stepKey="waitForThirdProductPageLoad"/> + <see selector="{{StorefrontProductInfoMainSection.productDescription}}" userInput="MFTF automation!" stepKey="seeThirdDescription"/> + </test> +</tests> diff --git a/app/code/Magento/ConfigurableProduct/Test/Mftf/Test/AdminConfigurableProductUpdateTest/AdminConfigurableProductDisableAnOptionTest.xml b/app/code/Magento/ConfigurableProduct/Test/Mftf/Test/AdminConfigurableProductUpdateTest/AdminConfigurableProductDisableAnOptionTest.xml new file mode 100644 index 0000000000000..1eb3df993dd1c --- /dev/null +++ b/app/code/Magento/ConfigurableProduct/Test/Mftf/Test/AdminConfigurableProductUpdateTest/AdminConfigurableProductDisableAnOptionTest.xml @@ -0,0 +1,100 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + /** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +--> +<tests xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/testSchema.xsd"> + <test name="AdminConfigurableProductDisableAnOptionTest"> + <annotations> + <features value="ConfigurableProduct"/> + <stories value="Create, Read, Update, Delete"/> + <title value="Admin should be able to disable a product configuration"/> + <description value="Admin should be able to disable a product configuration"/> + <testCaseId value="MC-119"/> + <group value="ConfigurableProduct"/> + <severity value="BLOCKER"/> + </annotations> + + <before> + <!-- This was copied and modified from the EndToEndB2CGuestUserTest --> + <createData entity="ApiCategory" stepKey="createCategory"/> + <createData entity="ApiConfigurableProduct" stepKey="createConfigProduct"> + <requiredEntity createDataKey="createCategory"/> + </createData> + <createData entity="productAttributeWithTwoOptions" stepKey="createConfigProductAttribute"/> + <createData entity="productAttributeOption1" stepKey="createConfigProductAttributeOption1"> + <requiredEntity createDataKey="createConfigProductAttribute"/> + </createData> + <createData entity="productAttributeOption2" stepKey="createConfigProductAttributeOption2"> + <requiredEntity createDataKey="createConfigProductAttribute"/> + </createData> + <createData entity="AddToDefaultSet" stepKey="createConfigAddToAttributeSet"> + <requiredEntity createDataKey="createConfigProductAttribute"/> + </createData> + <getData entity="ProductAttributeOptionGetter" index="1" stepKey="getConfigAttributeOption1"> + <requiredEntity createDataKey="createConfigProductAttribute"/> + </getData> + <getData entity="ProductAttributeOptionGetter" index="2" stepKey="getConfigAttributeOption2"> + <requiredEntity createDataKey="createConfigProductAttribute"/> + </getData> + <createData entity="ApiSimpleOne" stepKey="createConfigChildProduct1"> + <requiredEntity createDataKey="createConfigProductAttribute"/> + <requiredEntity createDataKey="getConfigAttributeOption1"/> + </createData> + <createData entity="ApiSimpleTwo" stepKey="createConfigChildProduct2"> + <requiredEntity createDataKey="createConfigProductAttribute"/> + <requiredEntity createDataKey="getConfigAttributeOption2"/> + </createData> + <createData entity="ConfigurableProductTwoOptions" stepKey="createConfigProductOption"> + <requiredEntity createDataKey="createConfigProduct"/> + <requiredEntity createDataKey="createConfigProductAttribute"/> + <requiredEntity createDataKey="getConfigAttributeOption1"/> + <requiredEntity createDataKey="getConfigAttributeOption2"/> + </createData> + <createData entity="ConfigurableProductAddChild" stepKey="createConfigProductAddChild1"> + <requiredEntity createDataKey="createConfigProduct"/> + <requiredEntity createDataKey="createConfigChildProduct1"/> + </createData> + <createData entity="ConfigurableProductAddChild" stepKey="createConfigProductAddChild2"> + <requiredEntity createDataKey="createConfigProduct"/> + <requiredEntity createDataKey="createConfigChildProduct2"/> + </createData> + </before> + + <after> + <deleteData createDataKey="createConfigChildProduct1" stepKey="deleteConfigChildProduct1"/> + <deleteData createDataKey="createConfigChildProduct2" stepKey="deleteConfigChildProduct2"/> + <deleteData createDataKey="createConfigProduct" stepKey="deleteConfigProduct"/> + <deleteData createDataKey="createConfigProductAttribute" stepKey="deleteConfigProductAttribute"/> + + <!-- Reindex invalidated indices after product attribute has been created/deleted --> + <magentoCron groups="index" stepKey="reindexInvalidatedIndices"/> + </after> + + <!--check storefront for both options--> + <amOnPage url="$$createConfigProduct.sku$$.html" stepKey="amOnStorefront1"/> + <waitForPageLoad stepKey="wait1"/> + <see selector="{{StorefrontProductInfoMainSection.productAttributeOptions1}}" userInput="option1" stepKey="seeOption1Storefront"/> + <see selector="{{StorefrontProductInfoMainSection.productAttributeOptions1}}" userInput="option2" stepKey="seeOption2Storefront"/> + + <!--go to admin and disable an option--> + <actionGroup ref="AdminLoginActionGroup" stepKey="loginAsAdmin"/> + <actionGroup ref="AdminProductPageOpenByIdActionGroup" stepKey="goToEditPage"> + <argument name="productId" value="$$createConfigProduct.id$$"/> + </actionGroup> + <waitForPageLoad stepKey="wait2"/> + <click selector="{{AdminProductFormConfigurationsSection.actionsBtn('1')}}" stepKey="clickToExpandActions"/> + <click selector="{{AdminProductFormConfigurationsSection.disableProductBtn}}" stepKey="clickDisable"/> + <click selector="{{AdminProductFormActionSection.saveButton}}" stepKey="clickSave"/> + <waitForPageLoad stepKey="wait3"/> + + <!--check storefront for one option--> + <amOnPage url="$$createConfigProduct.sku$$.html" stepKey="amOnStorefront2"/> + <waitForPageLoad stepKey="wait4"/> + <dontSee selector="{{StorefrontProductInfoMainSection.productAttributeOptions1}}" userInput="option1" stepKey="dontSeeOption1InStorefront"/> + <see selector="{{StorefrontProductInfoMainSection.productAttributeOptions1}}" userInput="option2" stepKey="seeOption2Again"/> + </test> +</tests> diff --git a/app/code/Magento/ConfigurableProduct/Test/Mftf/Test/AdminConfigurableProductUpdateTest/AdminConfigurableProductRemoveAnOptionTest.xml b/app/code/Magento/ConfigurableProduct/Test/Mftf/Test/AdminConfigurableProductUpdateTest/AdminConfigurableProductRemoveAnOptionTest.xml new file mode 100644 index 0000000000000..00b17fda944f1 --- /dev/null +++ b/app/code/Magento/ConfigurableProduct/Test/Mftf/Test/AdminConfigurableProductUpdateTest/AdminConfigurableProductRemoveAnOptionTest.xml @@ -0,0 +1,107 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + /** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +--> +<tests xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/testSchema.xsd"> + <test name="AdminConfigurableProductRemoveAnOptionTest"> + <annotations> + <features value="ConfigurableProduct"/> + <stories value="Create, Read, Update, Delete"/> + <title value="Admin should be able to remove a product configuration"/> + <description value="Admin should be able to remove a product configuration"/> + <testCaseId value="MC-63"/> + <group value="ConfigurableProduct"/> + <severity value="BLOCKER"/> + </annotations> + + <before> + <!-- This was copied and modified from the EndToEndB2CGuestUserTest --> + <createData entity="ApiCategory" stepKey="createCategory"/> + <createData entity="ApiConfigurableProduct" stepKey="createConfigProduct"> + <requiredEntity createDataKey="createCategory"/> + </createData> + <createData entity="productAttributeWithTwoOptions" stepKey="createConfigProductAttribute"/> + <createData entity="productAttributeOption1" stepKey="createConfigProductAttributeOption1"> + <requiredEntity createDataKey="createConfigProductAttribute"/> + </createData> + <createData entity="productAttributeOption2" stepKey="createConfigProductAttributeOption2"> + <requiredEntity createDataKey="createConfigProductAttribute"/> + </createData> + <createData entity="AddToDefaultSet" stepKey="createConfigAddToAttributeSet"> + <requiredEntity createDataKey="createConfigProductAttribute"/> + </createData> + <getData entity="ProductAttributeOptionGetter" index="1" stepKey="getConfigAttributeOption1"> + <requiredEntity createDataKey="createConfigProductAttribute"/> + </getData> + <getData entity="ProductAttributeOptionGetter" index="2" stepKey="getConfigAttributeOption2"> + <requiredEntity createDataKey="createConfigProductAttribute"/> + </getData> + <createData entity="ApiSimpleOne" stepKey="createConfigChildProduct1"> + <requiredEntity createDataKey="createConfigProductAttribute"/> + <requiredEntity createDataKey="getConfigAttributeOption1"/> + </createData> + <createData entity="ApiSimpleTwo" stepKey="createConfigChildProduct2"> + <requiredEntity createDataKey="createConfigProductAttribute"/> + <requiredEntity createDataKey="getConfigAttributeOption2"/> + </createData> + <createData entity="ConfigurableProductTwoOptions" stepKey="createConfigProductOption"> + <requiredEntity createDataKey="createConfigProduct"/> + <requiredEntity createDataKey="createConfigProductAttribute"/> + <requiredEntity createDataKey="getConfigAttributeOption1"/> + <requiredEntity createDataKey="getConfigAttributeOption2"/> + </createData> + <createData entity="ConfigurableProductAddChild" stepKey="createConfigProductAddChild1"> + <requiredEntity createDataKey="createConfigProduct"/> + <requiredEntity createDataKey="createConfigChildProduct1"/> + </createData> + <createData entity="ConfigurableProductAddChild" stepKey="createConfigProductAddChild2"> + <requiredEntity createDataKey="createConfigProduct"/> + <requiredEntity createDataKey="createConfigChildProduct2"/> + </createData> + </before> + + <after> + <deleteData createDataKey="createConfigChildProduct1" stepKey="deleteConfigChildProduct1"/> + <deleteData createDataKey="createConfigChildProduct2" stepKey="deleteConfigChildProduct2"/> + <deleteData createDataKey="createConfigProduct" stepKey="deleteConfigProduct"/> + <deleteData createDataKey="createConfigProductAttribute" stepKey="deleteConfigProductAttribute"/> + + <!-- Reindex invalidated indices after product attribute has been created/deleted --> + <magentoCron groups="index" stepKey="reindexInvalidatedIndices"/> + </after> + + <!--check storefront for both options--> + <amOnPage url="$$createConfigProduct.sku$$.html" stepKey="amOnStorefront1"/> + <waitForPageLoad stepKey="wait1"/> + <see selector="{{StorefrontProductInfoMainSection.productAttributeOptions1}}" userInput="option1" stepKey="seeOption1Storefront"/> + <see selector="{{StorefrontProductInfoMainSection.productAttributeOptions1}}" userInput="option2" stepKey="seeOption2Storefront"/> + + <!--check admin for both options--> + <actionGroup ref="AdminLoginActionGroup" stepKey="loginAsAdmin"/> + <actionGroup ref="AdminProductPageOpenByIdActionGroup" stepKey="goToEditPage"> + <argument name="productId" value="$$createConfigProduct.id$$"/> + </actionGroup> + <waitForPageLoad stepKey="wait2"/> + <see selector="{{AdminProductFormConfigurationsSection.currentVariationsNameCells}}" userInput="$$createConfigChildProduct1.name$$" stepKey="seeOption1Admin"/> + <see selector="{{AdminProductFormConfigurationsSection.currentVariationsNameCells}}" userInput="$$createConfigChildProduct2.name$$" stepKey="seeOption2Admin"/> + + <!--remove an option--> + <click selector="{{AdminProductFormConfigurationsSection.actionsBtn('1')}}" stepKey="clickToExpandActions"/> + <click selector="{{AdminProductFormConfigurationsSection.removeProductBtn}}" stepKey="clickRemove"/> + <click selector="{{AdminProductFormActionSection.saveButton}}" stepKey="clickSave"/> + <waitForPageLoad stepKey="wait3"/> + + <!--check admin for one option--> + <dontSee selector="{{AdminProductFormConfigurationsSection.currentVariationsNameCells}}" userInput="$$createConfigChildProduct1.name$$" stepKey="dontSeeOption1Admin"/> + + <!--check storefront for one option--> + <amOnPage url="$$createConfigProduct.sku$$.html" stepKey="amOnStorefront2"/> + <waitForPageLoad stepKey="wait4"/> + <dontSee selector="{{StorefrontProductInfoMainSection.productAttributeOptions1}}" userInput="option1" stepKey="dontSeeOption1InStorefront"/> + <see selector="{{StorefrontProductInfoMainSection.productAttributeOptions1}}" userInput="option2" stepKey="seeOption2Again"/> + </test> +</tests> diff --git a/app/code/Magento/ConfigurableProduct/Test/Mftf/Test/AdminConfigurableProductUpdateTest/AdminConfigurableProductRemoveConfigurationTest.xml b/app/code/Magento/ConfigurableProduct/Test/Mftf/Test/AdminConfigurableProductUpdateTest/AdminConfigurableProductRemoveConfigurationTest.xml new file mode 100644 index 0000000000000..bef7d26c5007f --- /dev/null +++ b/app/code/Magento/ConfigurableProduct/Test/Mftf/Test/AdminConfigurableProductUpdateTest/AdminConfigurableProductRemoveConfigurationTest.xml @@ -0,0 +1,58 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + /** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +--> +<tests xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/testSchema.xsd"> + <test name="AdminConfigurableProductRemoveConfigurationTest"> + <annotations> + <features value="ConfigurableProduct"/> + <stories value="Edit a configurable product in admin"/> + <title value="Admin should be able to remove a configuration from a Configurable Product"/> + <description value="Admin should be able to remove a configuration from a Configurable Product"/> + <severity value="BLOCKER"/> + <testCaseId value="MC-86"/> + <group value="ConfigurableProduct"/> + </annotations> + + <before> + <createData entity="ApiCategory" stepKey="createCategory"/> + <actionGroup ref="AdminLoginActionGroup" stepKey="loginAsAdmin"/> + </before> + + <after> + <deleteData createDataKey="createCategory" stepKey="deleteCategory"/> + <actionGroup ref="AdminLogoutActionGroup" stepKey="adminLogout"/> + </after> + + <!-- Create a configurable product via the UI --> + <actionGroup ref="CreateConfigurableProductActionGroup" stepKey="createProduct"> + <argument name="product" value="_defaultProduct"/> + <argument name="category" value="$$createCategory$$"/> + </actionGroup> + + <!-- + After saving, we are still on the product edit page. There is no need to reload or go to this page + again, because a round trip to the server has already happened. + --> + + <!-- Remove a configuration option from the configurable product --> + <click selector="{{AdminProductFormConfigurationsSection.createConfigurations}}" stepKey="clickEditConfigurations"/> + <click selector="{{AdminCreateProductConfigurationsPanel.next}}" stepKey="clickOnNextButton1"/> + <click selector="{{AdminCreateProductConfigurationsPanel.attributeCheckboxByIndex('1')}}" stepKey="deselectOption"/> + <click selector="{{AdminCreateProductConfigurationsPanel.next}}" stepKey="clickOnNextButton2"/> + <click selector="{{AdminCreateProductConfigurationsPanel.next}}" stepKey="clickOnNextButton3"/> + <click selector="{{AdminCreateProductConfigurationsPanel.next}}" stepKey="clickOnNextButton4"/> + <click selector="{{AdminProductFormActionSection.saveButton}}" stepKey="saveProduct"/> + + <!-- Verify that the removed option is not present in the storefront --> + <amOnPage url="{{StorefrontProductPage.url(_defaultProduct.urlKey)}}" stepKey="amOnStorefrontPage"/> + <waitForPageLoad stepKey="waitForStorefrontPage"/> + <dontSee selector="{{StorefrontProductInfoMainSection.productAttributeOptions1}}" userInput="{{colorProductAttribute1.name}}" stepKey="seeInDropDown1"/> + <see selector="{{StorefrontProductInfoMainSection.productAttributeOptions1}}" userInput="{{colorProductAttribute2.name}}" stepKey="seeInDropDown2"/> + <see selector="{{StorefrontProductInfoMainSection.productAttributeOptions1}}" userInput="{{colorProductAttribute3.name}}" stepKey="seeInDropDown3"/> + </test> +</tests> diff --git a/app/code/Magento/ConfigurableProduct/Test/Mftf/Test/AdminConfigurableSetEditRelatedProductsTest.xml b/app/code/Magento/ConfigurableProduct/Test/Mftf/Test/AdminConfigurableSetEditRelatedProductsTest.xml index bba8232139d69..924707a16600f 100644 --- a/app/code/Magento/ConfigurableProduct/Test/Mftf/Test/AdminConfigurableSetEditRelatedProductsTest.xml +++ b/app/code/Magento/ConfigurableProduct/Test/Mftf/Test/AdminConfigurableSetEditRelatedProductsTest.xml @@ -21,6 +21,8 @@ </annotations> <before> <createData entity="ApiCategory" stepKey="createCategory"/> + + <magentoCLI command="cron:run --group=index" stepKey="runCronIndexer"/> </before> <after> <!-- Delete configurable product --> diff --git a/app/code/Magento/ConfigurableProduct/Test/Mftf/Test/AdminCreateAndEditConfigurableProductSettingsTest.xml b/app/code/Magento/ConfigurableProduct/Test/Mftf/Test/AdminCreateAndEditConfigurableProductSettingsTest.xml index 6632cbcee30f2..796a4628393bb 100644 --- a/app/code/Magento/ConfigurableProduct/Test/Mftf/Test/AdminCreateAndEditConfigurableProductSettingsTest.xml +++ b/app/code/Magento/ConfigurableProduct/Test/Mftf/Test/AdminCreateAndEditConfigurableProductSettingsTest.xml @@ -20,11 +20,11 @@ </annotations> <before> <!-- Login as admin --> - <actionGroup ref="LoginAsAdmin" stepKey="loginAsAdmin"/> + <actionGroup ref="AdminLoginActionGroup" stepKey="loginAsAdmin"/> </before> <after> <!-- Log out --> - <actionGroup ref="logout" stepKey="adminLogout"/> + <actionGroup ref="AdminLogoutActionGroup" stepKey="adminLogout"/> </after> <!-- Create a configurable product --> @@ -91,7 +91,7 @@ <!-- Verify Url Key after changing --> <actionGroup ref="StorefrontOpenProductPageActionGroup" stepKey="openProductPage"> - <argument name="productUrl" value="{{ApiConfigurableProduct.name}}"/> + <argument name="productUrl" value="{{ApiConfigurableProduct.urlKey}}"/> </actionGroup> <!-- Assert product design settings "Layout empty" --> diff --git a/app/code/Magento/ConfigurableProduct/Test/Mftf/Test/AdminCreateAndSwitchProductType.xml b/app/code/Magento/ConfigurableProduct/Test/Mftf/Test/AdminCreateAndSwitchProductType.xml deleted file mode 100644 index a5ad45048ce96..0000000000000 --- a/app/code/Magento/ConfigurableProduct/Test/Mftf/Test/AdminCreateAndSwitchProductType.xml +++ /dev/null @@ -1,192 +0,0 @@ -<?xml version="1.0" encoding="UTF-8"?> -<!-- - /** - * Copyright © Magento, Inc. All rights reserved. - * See COPYING.txt for license details. - */ ---> - -<tests xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" - xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/testSchema.xsd"> - <test name="AdminCreateConfigurableProductSwitchToSimpleTest" extends="AdminCreateSimpleProductSwitchToVirtualTest"> - <annotations> - <features value="Catalog"/> - <stories value="Product Type Switching"/> - <title value="Admin should be able to switch a new product from configurable to simple"/> - <description value="After selecting a configurable product when adding Admin should be switch to simple implicitly"/> - <severity value="CRITICAL"/> - <testCaseId value="MC-10926"/> - <group value="catalog"/> - <group value="mtf_migrated"/> - </annotations> - <actionGroup ref="GoToSpecifiedCreateProductPageActionGroup" stepKey="openProductFillForm"> - <argument name="productType" value="configurable"/> - </actionGroup> - <actionGroup ref="FillMainProductFormActionGroup" stepKey="fillProductForm"> - <argument name="product" value="_defaultProduct"/> - </actionGroup> - <see selector="{{AdminProductGridSection.productGridCell('1', 'Type')}}" userInput="Simple Product" stepKey="seeProductTypeInGrid"/> - </test> - <test name="AdminCreateConfigurableProductSwitchToVirtualTest" extends="AdminCreateSimpleProductSwitchToVirtualTest"> - <annotations> - <features value="Catalog"/> - <stories value="Product Type Switching"/> - <title value="Admin should be able to switch a new product from configurable to virtual"/> - <description value="After selecting a configurable product when adding Admin should be switch to virtual implicitly"/> - <severity value="CRITICAL"/> - <testCaseId value="MC-10927"/> - <group value="catalog"/> - <group value="mtf_migrated"/> - </annotations> - <actionGroup ref="GoToSpecifiedCreateProductPageActionGroup" stepKey="openProductFillForm"> - <argument name="productType" value="configurable"/> - </actionGroup> - <see selector="{{AdminProductGridSection.productGridCell('1', 'Type')}}" userInput="Virtual Product" stepKey="seeProductTypeInGrid"/> - </test> - <test name="AdminCreateVirtualProductSwitchToConfigurableTest" extends="AdminCreateSimpleProductSwitchToVirtualTest"> - <annotations> - <features value="Catalog"/> - <stories value="Product Type Switching"/> - <title value="Admin should be able to switch a new product from virtual to configurable"/> - <description value="After selecting a virtual product when adding Admin should be switch to configurable implicitly"/> - <severity value="CRITICAL"/> - <testCaseId value="MC-10930"/> - <group value="catalog"/> - <group value="mtf_migrated"/> - </annotations> - <before> - <createData entity="productAttributeWithTwoOptions" stepKey="createConfigProductAttribute"/> - <createData entity="productAttributeOption1" stepKey="createConfigProductAttributeOption1"> - <requiredEntity createDataKey="createConfigProductAttribute"/> - </createData> - </before> - <after> - <deleteData stepKey="deleteAttribute" createDataKey="createConfigProductAttribute"/> - </after> - <actionGroup ref="GoToSpecifiedCreateProductPageActionGroup" stepKey="openProductFillForm"> - <argument name="productType" value="virtual"/> - </actionGroup> - <actionGroup ref="FillMainProductFormActionGroup" stepKey="fillProductForm"> - <argument name="product" value="_defaultProduct"/> - </actionGroup> - <comment before="createConfiguration" stepKey="beforeCreateConfiguration" userInput="Adding Configuration to Product"/> - <actionGroup ref="GenerateConfigurationsByAttributeCodeActionGroup" stepKey="createConfiguration" after="fillProductForm"> - <argument name="attributeCode" value="$$createConfigProductAttribute.attribute_code$$"/> - </actionGroup> - <actionGroup ref="SaveConfiguredProductActionGroup" stepKey="saveProductForm"/> - <see selector="{{AdminProductGridSection.productGridCell('2', 'Type')}}" userInput="Configurable Product" stepKey="seeProductTypeInGrid"/> - <actionGroup ref="VerifyOptionInProductStorefrontActionGroup" stepKey="verifyConfigurableOption" after="AssertProductInStorefrontProductPage"> - <argument name="attributeCode" value="$createConfigProductAttribute.default_frontend_label$"/> - <argument name="optionName" value="$createConfigProductAttributeOption1.option[store_labels][1][label]$"/> - </actionGroup> - </test> - <test name="AdminCreateSimpleProductSwitchToConfigurableTest" extends="AdminCreateSimpleProductSwitchToVirtualTest"> - <annotations> - <features value="Catalog"/> - <stories value="Product Type Switching"/> - <title value="Admin should be able to switch a new product from simple to configurable"/> - <description value="After selecting a simple product when adding Admin should be switch to configurable implicitly"/> - <severity value="CRITICAL"/> - <useCaseId value="MAGETWO-44165"/> - <testCaseId value="MAGETWO-29398"/> - <group value="catalog"/> - </annotations> - <before> - <createData entity="productAttributeWithTwoOptions" stepKey="createConfigProductAttribute"/> - <createData entity="productAttributeOption1" stepKey="createConfigProductAttributeOption1"> - <requiredEntity createDataKey="createConfigProductAttribute"/> - </createData> - </before> - <after> - <deleteData stepKey="deleteAttribute" createDataKey="createConfigProductAttribute"/> - </after> - <actionGroup ref="GoToSpecifiedCreateProductPageActionGroup" stepKey="openProductFillForm"> - <argument name="productType" value="simple"/> - </actionGroup> - <!-- Create configurable product from simple product page--> - <comment userInput="Create configurable product" stepKey="commentCreateProduct"/> - <actionGroup ref="FillMainProductFormActionGroup" stepKey="fillProductForm"> - <argument name="product" value="_defaultProduct"/> - </actionGroup> - <comment before="createConfiguration" stepKey="beforeCreateConfiguration" userInput="Adding Configuration to Product"/> - <actionGroup ref="GenerateConfigurationsByAttributeCodeActionGroup" stepKey="createConfiguration" after="fillProductForm"> - <argument name="attributeCode" value="$$createConfigProductAttribute.attribute_code$$"/> - </actionGroup> - <actionGroup ref="SaveConfiguredProductActionGroup" stepKey="saveProductForm"/> - <see selector="{{AdminProductGridSection.productGridCell('2', 'Type')}}" userInput="Configurable Product" stepKey="seeProductTypeInGrid"/> - <!-- Verify product on store front --> - <comment userInput="Verify product on store front" stepKey="commentVerifyProductGrid"/> - <actionGroup ref="VerifyOptionInProductStorefrontActionGroup" stepKey="verifyConfigurableOption" after="AssertProductInStorefrontProductPage"> - <argument name="attributeCode" value="$createConfigProductAttribute.default_frontend_label$"/> - <argument name="optionName" value="$createConfigProductAttributeOption1.option[store_labels][1][label]$"/> - </actionGroup> - </test> - <test name="AdminCreateDownloadableProductSwitchToConfigurableTest"> - <annotations> - <features value="Catalog"/> - <stories value="Product Type Switching"/> - <title value="Admin should be able to switch a new product from downloadable to configurable"/> - <description value="After selecting a downloadable product when adding Admin should be switch to configurable implicitly"/> - <severity value="CRITICAL"/> - <useCaseId value="MAGETWO-44165"/> - <testCaseId value="MAGETWO-29398"/> - <group value="catalog"/> - <group value="mtf_migrated"/> - </annotations> - <before> - <createData entity="_defaultCategory" stepKey="createPreReqCategory"/> - <createData entity="productAttributeWithTwoOptions" stepKey="createConfigProductAttribute"/> - <createData entity="productAttributeOption1" stepKey="createConfigProductAttributeOption1"> - <requiredEntity createDataKey="createConfigProductAttribute"/> - </createData> - </before> - <after> - <actionGroup ref="GoToProductCatalogPageActionGroup" stepKey="goToProductCatalogPage"/> - <actionGroup ref="DeleteProductUsingProductGridActionGroup" stepKey="deleteConfigurableProduct"> - <argument name="product" value="_defaultProduct"/> - </actionGroup> - <actionGroup ref="ResetProductGridToDefaultViewActionGroup" stepKey="resetSearch"/> - <deleteData createDataKey="createPreReqCategory" stepKey="deletePreReqCategory"/> - <deleteData stepKey="deleteAttribute" createDataKey="createConfigProductAttribute"/> - <actionGroup ref="logout" stepKey="logout"/> - </after> - <!-- Create configurable product from downloadable product page--> - <comment userInput="Create configurable product" stepKey="commentCreateProduct"/> - <actionGroup ref="LoginAsAdmin" stepKey="loginAsAdmin1"/> - <!-- Open Dropdown and select downloadable product option --> - <comment stepKey="beforeOpenProductFillForm" userInput="Selecting Product from the Add Product Dropdown"/> - <actionGroup ref="GoToSpecifiedCreateProductPageActionGroup" stepKey="openProductFillForm"> - <argument name="productType" value="downloadable"/> - </actionGroup> - <scrollTo selector="{{AdminProductDownloadableSection.sectionHeader}}" stepKey="scrollToDownloadableInfo" /> - <uncheckOption selector="{{AdminProductDownloadableSection.isDownloadableProduct}}" stepKey="checkIsDownloadable"/> - <!-- Fill form for Downloadable Product Type --> - <comment stepKey="beforeFillProductForm" userInput="Filling Product Form"/> - <actionGroup ref="FillMainProductFormActionGroup" stepKey="fillProductForm"> - <argument name="product" value="_defaultProduct"/> - </actionGroup> - <actionGroup ref="SetProductUrlKeyActionGroup" stepKey="setProductUrl"> - <argument name="product" value="_defaultProduct"/> - </actionGroup> - <comment before="createConfiguration" stepKey="beforeCreateConfiguration" userInput="Adding Configuration to Product"/> - <actionGroup ref="GenerateConfigurationsByAttributeCodeActionGroup" stepKey="createConfiguration"> - <argument name="attributeCode" value="$$createConfigProductAttribute.attribute_code$$"/> - </actionGroup> - <actionGroup ref="SaveConfiguredProductActionGroup" stepKey="saveProductForm"/> - <!-- Check that product was added with implicit type change --> - <comment stepKey="beforeVerify" userInput="Verify Product Type Assigned Correctly"/> - <actionGroup ref="GoToProductCatalogPageActionGroup" stepKey="goToProductCatalogPage"/> - <actionGroup ref="ResetProductGridToDefaultViewActionGroup" stepKey="resetSearch"/> - <actionGroup ref="FilterProductGridByNameActionGroup" stepKey="searchForProduct"> - <argument name="product" value="_defaultProduct"/> - </actionGroup> - <see selector="{{AdminProductGridSection.productGridCell('2', 'Type')}}" userInput="Configurable Product" stepKey="seeProductTypeInGrid"/> - <actionGroup ref="AssertProductInStorefrontProductPageActionGroup" stepKey="assertProductInStorefrontProductPage"> - <argument name="product" value="_defaultProduct"/> - </actionGroup> - <actionGroup ref="VerifyOptionInProductStorefrontActionGroup" stepKey="verifyConfigurableOption"> - <argument name="attributeCode" value="$createConfigProductAttribute.default_frontend_label$"/> - <argument name="optionName" value="$createConfigProductAttributeOption1.option[store_labels][1][label]$"/> - </actionGroup> - </test> -</tests> diff --git a/app/code/Magento/ConfigurableProduct/Test/Mftf/Test/AdminCreateAndSwitchProductType/AdminCreateConfigurableProductSwitchToSimpleTest.xml b/app/code/Magento/ConfigurableProduct/Test/Mftf/Test/AdminCreateAndSwitchProductType/AdminCreateConfigurableProductSwitchToSimpleTest.xml new file mode 100644 index 0000000000000..98bd5a0fed4ed --- /dev/null +++ b/app/code/Magento/ConfigurableProduct/Test/Mftf/Test/AdminCreateAndSwitchProductType/AdminCreateConfigurableProductSwitchToSimpleTest.xml @@ -0,0 +1,29 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + /** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +--> +<tests xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/testSchema.xsd"> + <test name="AdminCreateConfigurableProductSwitchToSimpleTest" extends="AdminCreateSimpleProductSwitchToVirtualTest"> + <annotations> + <features value="Catalog"/> + <stories value="Product Type Switching"/> + <title value="Admin should be able to switch a new product from configurable to simple"/> + <description value="After selecting a configurable product when adding Admin should be switch to simple implicitly"/> + <severity value="CRITICAL"/> + <testCaseId value="MC-10926"/> + <group value="catalog"/> + <group value="mtf_migrated"/> + </annotations> + <actionGroup ref="GoToSpecifiedCreateProductPageActionGroup" stepKey="openProductFillForm"> + <argument name="productType" value="configurable"/> + </actionGroup> + <actionGroup ref="FillMainProductFormActionGroup" stepKey="fillProductForm"> + <argument name="product" value="_defaultProduct"/> + </actionGroup> + <see selector="{{AdminProductGridSection.productGridCell('1', 'Type')}}" userInput="Simple Product" stepKey="seeProductTypeInGrid"/> + </test> +</tests> diff --git a/app/code/Magento/ConfigurableProduct/Test/Mftf/Test/AdminCreateAndSwitchProductType/AdminCreateConfigurableProductSwitchToVirtualTest.xml b/app/code/Magento/ConfigurableProduct/Test/Mftf/Test/AdminCreateAndSwitchProductType/AdminCreateConfigurableProductSwitchToVirtualTest.xml new file mode 100644 index 0000000000000..756cdfd5d5d6f --- /dev/null +++ b/app/code/Magento/ConfigurableProduct/Test/Mftf/Test/AdminCreateAndSwitchProductType/AdminCreateConfigurableProductSwitchToVirtualTest.xml @@ -0,0 +1,26 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + /** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +--> +<tests xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/testSchema.xsd"> + <test name="AdminCreateConfigurableProductSwitchToVirtualTest" extends="AdminCreateSimpleProductSwitchToVirtualTest"> + <annotations> + <features value="Catalog"/> + <stories value="Product Type Switching"/> + <title value="Admin should be able to switch a new product from configurable to virtual"/> + <description value="After selecting a configurable product when adding Admin should be switch to virtual implicitly"/> + <severity value="CRITICAL"/> + <testCaseId value="MC-10927"/> + <group value="catalog"/> + <group value="mtf_migrated"/> + </annotations> + <actionGroup ref="GoToSpecifiedCreateProductPageActionGroup" stepKey="openProductFillForm"> + <argument name="productType" value="configurable"/> + </actionGroup> + <see selector="{{AdminProductGridSection.productGridCell('1', 'Type')}}" userInput="Virtual Product" stepKey="seeProductTypeInGrid"/> + </test> +</tests> diff --git a/app/code/Magento/ConfigurableProduct/Test/Mftf/Test/AdminCreateAndSwitchProductType/AdminCreateDownloadableProductSwitchToConfigurableTest.xml b/app/code/Magento/ConfigurableProduct/Test/Mftf/Test/AdminCreateAndSwitchProductType/AdminCreateDownloadableProductSwitchToConfigurableTest.xml new file mode 100644 index 0000000000000..db5c824341c57 --- /dev/null +++ b/app/code/Magento/ConfigurableProduct/Test/Mftf/Test/AdminCreateAndSwitchProductType/AdminCreateDownloadableProductSwitchToConfigurableTest.xml @@ -0,0 +1,79 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + /** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +--> +<tests xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/testSchema.xsd"> + <test name="AdminCreateDownloadableProductSwitchToConfigurableTest"> + <annotations> + <features value="Catalog"/> + <stories value="Product Type Switching"/> + <title value="Admin should be able to switch a new product from downloadable to configurable"/> + <description value="After selecting a downloadable product when adding Admin should be switch to configurable implicitly"/> + <severity value="CRITICAL"/> + <useCaseId value="MAGETWO-44165"/> + <testCaseId value="MAGETWO-29398"/> + <group value="catalog"/> + <group value="mtf_migrated"/> + </annotations> + <before> + <createData entity="_defaultCategory" stepKey="createPreReqCategory"/> + <createData entity="productAttributeWithTwoOptions" stepKey="createConfigProductAttribute"/> + <createData entity="productAttributeOption1" stepKey="createConfigProductAttributeOption1"> + <requiredEntity createDataKey="createConfigProductAttribute"/> + </createData> + </before> + <after> + <actionGroup ref="GoToProductCatalogPageActionGroup" stepKey="goToProductCatalogPage"/> + <actionGroup ref="DeleteProductUsingProductGridActionGroup" stepKey="deleteConfigurableProduct"> + <argument name="product" value="_defaultProduct"/> + </actionGroup> + <actionGroup ref="ResetProductGridToDefaultViewActionGroup" stepKey="resetSearch"/> + <deleteData createDataKey="createPreReqCategory" stepKey="deletePreReqCategory"/> + <deleteData stepKey="deleteAttribute" createDataKey="createConfigProductAttribute"/> + <magentoCLI command="cron:run --group=index" stepKey="runCron"/> + <actionGroup ref="AdminLogoutActionGroup" stepKey="logout"/> + </after> + <!-- Create configurable product from downloadable product page--> + <comment userInput="Create configurable product" stepKey="commentCreateProduct"/> + <actionGroup ref="AdminLoginActionGroup" stepKey="loginAsAdmin1"/> + <!-- Open Dropdown and select downloadable product option --> + <comment stepKey="beforeOpenProductFillForm" userInput="Selecting Product from the Add Product Dropdown"/> + <actionGroup ref="GoToSpecifiedCreateProductPageActionGroup" stepKey="openProductFillForm"> + <argument name="productType" value="downloadable"/> + </actionGroup> + <scrollTo selector="{{AdminProductDownloadableSection.sectionHeader}}" stepKey="scrollToDownloadableInfo"/> + <uncheckOption selector="{{AdminProductDownloadableSection.isDownloadableProduct}}" stepKey="checkIsDownloadable"/> + <!-- Fill form for Downloadable Product Type --> + <comment stepKey="beforeFillProductForm" userInput="Filling Product Form"/> + <actionGroup ref="FillMainProductFormActionGroup" stepKey="fillProductForm"> + <argument name="product" value="_defaultProduct"/> + </actionGroup> + <actionGroup ref="SetProductUrlKeyActionGroup" stepKey="setProductUrl"> + <argument name="product" value="_defaultProduct"/> + </actionGroup> + <comment before="createConfiguration" stepKey="beforeCreateConfiguration" userInput="Adding Configuration to Product"/> + <actionGroup ref="GenerateConfigurationsByAttributeCodeActionGroup" stepKey="createConfiguration"> + <argument name="attributeCode" value="$$createConfigProductAttribute.attribute_code$$"/> + </actionGroup> + <actionGroup ref="SaveConfiguredProductActionGroup" stepKey="saveProductForm"/> + <!-- Check that product was added with implicit type change --> + <comment stepKey="beforeVerify" userInput="Verify Product Type Assigned Correctly"/> + <actionGroup ref="GoToProductCatalogPageActionGroup" stepKey="goToProductCatalogPage"/> + <actionGroup ref="ResetProductGridToDefaultViewActionGroup" stepKey="resetSearch"/> + <actionGroup ref="FilterProductGridByNameActionGroup" stepKey="searchForProduct"> + <argument name="product" value="_defaultProduct"/> + </actionGroup> + <see selector="{{AdminProductGridSection.productGridCell('2', 'Type')}}" userInput="Configurable Product" stepKey="seeProductTypeInGrid"/> + <actionGroup ref="AssertProductInStorefrontProductPageActionGroup" stepKey="assertProductInStorefrontProductPage"> + <argument name="product" value="_defaultProduct"/> + </actionGroup> + <actionGroup ref="VerifyOptionInProductStorefrontActionGroup" stepKey="verifyConfigurableOption"> + <argument name="attributeCode" value="$createConfigProductAttribute.default_frontend_label$"/> + <argument name="optionName" value="$createConfigProductAttributeOption1.option[store_labels][1][label]$"/> + </actionGroup> + </test> +</tests> diff --git a/app/code/Magento/ConfigurableProduct/Test/Mftf/Test/AdminCreateAndSwitchProductType/AdminCreateSimpleProductSwitchToConfigurableTest.xml b/app/code/Magento/ConfigurableProduct/Test/Mftf/Test/AdminCreateAndSwitchProductType/AdminCreateSimpleProductSwitchToConfigurableTest.xml new file mode 100644 index 0000000000000..cbfa1cc2b8bd6 --- /dev/null +++ b/app/code/Magento/ConfigurableProduct/Test/Mftf/Test/AdminCreateAndSwitchProductType/AdminCreateSimpleProductSwitchToConfigurableTest.xml @@ -0,0 +1,51 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + /** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +--> +<tests xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/testSchema.xsd"> + <test name="AdminCreateSimpleProductSwitchToConfigurableTest" extends="AdminCreateSimpleProductSwitchToVirtualTest"> + <annotations> + <features value="Catalog"/> + <stories value="Product Type Switching"/> + <title value="Admin should be able to switch a new product from simple to configurable"/> + <description value="After selecting a simple product when adding Admin should be switch to configurable implicitly"/> + <severity value="CRITICAL"/> + <useCaseId value="MAGETWO-44165"/> + <testCaseId value="MAGETWO-29398"/> + <group value="catalog"/> + </annotations> + <before> + <createData entity="productAttributeWithTwoOptions" stepKey="createConfigProductAttribute"/> + <createData entity="productAttributeOption1" stepKey="createConfigProductAttributeOption1"> + <requiredEntity createDataKey="createConfigProductAttribute"/> + </createData> + </before> + <after> + <deleteData stepKey="deleteAttribute" createDataKey="createConfigProductAttribute"/> + </after> + <actionGroup ref="GoToSpecifiedCreateProductPageActionGroup" stepKey="openProductFillForm"> + <argument name="productType" value="simple"/> + </actionGroup> + <!-- Create configurable product from simple product page--> + <comment userInput="Create configurable product" stepKey="commentCreateProduct"/> + <actionGroup ref="FillMainProductFormActionGroup" stepKey="fillProductForm"> + <argument name="product" value="_defaultProduct"/> + </actionGroup> + <comment before="createConfiguration" stepKey="beforeCreateConfiguration" userInput="Adding Configuration to Product"/> + <actionGroup ref="GenerateConfigurationsByAttributeCodeActionGroup" stepKey="createConfiguration" after="fillProductForm"> + <argument name="attributeCode" value="$$createConfigProductAttribute.attribute_code$$"/> + </actionGroup> + <actionGroup ref="SaveConfiguredProductActionGroup" stepKey="saveProductForm"/> + <see selector="{{AdminProductGridSection.productGridCell('2', 'Type')}}" userInput="Configurable Product" stepKey="seeProductTypeInGrid"/> + <!-- Verify product on store front --> + <comment userInput="Verify product on store front" stepKey="commentVerifyProductGrid"/> + <actionGroup ref="VerifyOptionInProductStorefrontActionGroup" stepKey="verifyConfigurableOption" after="AssertProductInStorefrontProductPage"> + <argument name="attributeCode" value="$createConfigProductAttribute.default_frontend_label$"/> + <argument name="optionName" value="$createConfigProductAttributeOption1.option[store_labels][1][label]$"/> + </actionGroup> + </test> +</tests> diff --git a/app/code/Magento/ConfigurableProduct/Test/Mftf/Test/AdminCreateAndSwitchProductType/AdminCreateVirtualProductSwitchToConfigurableTest.xml b/app/code/Magento/ConfigurableProduct/Test/Mftf/Test/AdminCreateAndSwitchProductType/AdminCreateVirtualProductSwitchToConfigurableTest.xml new file mode 100644 index 0000000000000..cfeb95afc4924 --- /dev/null +++ b/app/code/Magento/ConfigurableProduct/Test/Mftf/Test/AdminCreateAndSwitchProductType/AdminCreateVirtualProductSwitchToConfigurableTest.xml @@ -0,0 +1,47 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + /** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +--> +<tests xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/testSchema.xsd"> + <test name="AdminCreateVirtualProductSwitchToConfigurableTest" extends="AdminCreateSimpleProductSwitchToVirtualTest"> + <annotations> + <features value="Catalog"/> + <stories value="Product Type Switching"/> + <title value="Admin should be able to switch a new product from virtual to configurable"/> + <description value="After selecting a virtual product when adding Admin should be switch to configurable implicitly"/> + <severity value="CRITICAL"/> + <testCaseId value="MC-10930"/> + <group value="catalog"/> + <group value="mtf_migrated"/> + </annotations> + <before> + <createData entity="productAttributeWithTwoOptions" stepKey="createConfigProductAttribute"/> + <createData entity="productAttributeOption1" stepKey="createConfigProductAttributeOption1"> + <requiredEntity createDataKey="createConfigProductAttribute"/> + </createData> + </before> + <after> + <deleteData stepKey="deleteAttribute" createDataKey="createConfigProductAttribute"/> + </after> + <actionGroup ref="GoToSpecifiedCreateProductPageActionGroup" stepKey="openProductFillForm"> + <argument name="productType" value="virtual"/> + </actionGroup> + <actionGroup ref="FillMainProductFormActionGroup" stepKey="fillProductForm"> + <argument name="product" value="_defaultProduct"/> + </actionGroup> + <comment before="createConfiguration" stepKey="beforeCreateConfiguration" userInput="Adding Configuration to Product"/> + <actionGroup ref="GenerateConfigurationsByAttributeCodeActionGroup" stepKey="createConfiguration" after="fillProductForm"> + <argument name="attributeCode" value="$$createConfigProductAttribute.attribute_code$$"/> + </actionGroup> + <actionGroup ref="SaveConfiguredProductActionGroup" stepKey="saveProductForm"/> + <see selector="{{AdminProductGridSection.productGridCell('2', 'Type')}}" userInput="Configurable Product" stepKey="seeProductTypeInGrid"/> + <actionGroup ref="VerifyOptionInProductStorefrontActionGroup" stepKey="verifyConfigurableOption" after="AssertProductInStorefrontProductPage"> + <argument name="attributeCode" value="$createConfigProductAttribute.default_frontend_label$"/> + <argument name="optionName" value="$createConfigProductAttributeOption1.option[store_labels][1][label]$"/> + </actionGroup> + </test> +</tests> diff --git a/app/code/Magento/ConfigurableProduct/Test/Mftf/Test/AdminCreateConfigurableProductBasedOnParentSkuTest.xml b/app/code/Magento/ConfigurableProduct/Test/Mftf/Test/AdminCreateConfigurableProductBasedOnParentSkuTest.xml index 578e908664ecb..e5456429373e1 100644 --- a/app/code/Magento/ConfigurableProduct/Test/Mftf/Test/AdminCreateConfigurableProductBasedOnParentSkuTest.xml +++ b/app/code/Magento/ConfigurableProduct/Test/Mftf/Test/AdminCreateConfigurableProductBasedOnParentSkuTest.xml @@ -18,7 +18,7 @@ <group value="mtf_migrated"/> </annotations> <before> - <actionGroup ref="LoginAsAdmin" stepKey="loginAsAdmin"/> + <actionGroup ref="AdminLoginActionGroup" stepKey="loginAsAdmin"/> </before> <after> <!-- Delete configurable product with children products --> @@ -27,12 +27,12 @@ </actionGroup> <!-- Delete product attribute --> - <actionGroup ref="DeleteProductAttributeByLabelActionGroup" stepKey="deleteProductAttribute"> - <argument name="ProductAttribute" value="colorProductAttribute"/> + <actionGroup ref="AdminDeleteProductAttributeByLabelActionGroup" stepKey="deleteProductAttribute"> + <argument name="productAttributeLabel" value="{{colorProductAttribute.default_label}}"/> </actionGroup> <!-- Log out --> - <actionGroup ref="logout" stepKey="logout"/> + <actionGroup ref="AdminLogoutActionGroup" stepKey="logout"/> </after> <!-- Create configurable product --> diff --git a/app/code/Magento/ConfigurableProduct/Test/Mftf/Test/AdminCreateConfigurableProductWithCreatingCategoryAndAttributeTest.xml b/app/code/Magento/ConfigurableProduct/Test/Mftf/Test/AdminCreateConfigurableProductWithCreatingCategoryAndAttributeTest.xml index 363daa0ec8bb2..32117fdfe4366 100644 --- a/app/code/Magento/ConfigurableProduct/Test/Mftf/Test/AdminCreateConfigurableProductWithCreatingCategoryAndAttributeTest.xml +++ b/app/code/Magento/ConfigurableProduct/Test/Mftf/Test/AdminCreateConfigurableProductWithCreatingCategoryAndAttributeTest.xml @@ -19,7 +19,7 @@ </annotations> <before> <createData entity="SimpleSubCategory" stepKey="createCategory"/> - <actionGroup ref="LoginAsAdmin" stepKey="loginAsAdmin"/> + <actionGroup ref="AdminLoginActionGroup" stepKey="loginAsAdmin"/> </before> <after> <!-- Delete configurable product --> @@ -36,8 +36,8 @@ </actionGroup> <!-- Delete product attribute --> - <actionGroup ref="DeleteProductAttributeByLabelActionGroup" stepKey="deleteProductAttribute"> - <argument name="ProductAttribute" value="colorProductAttribute"/> + <actionGroup ref="AdminDeleteProductAttributeByLabelActionGroup" stepKey="deleteProductAttribute"> + <argument name="productAttributeLabel" value="{{colorProductAttribute.default_label}}"/> </actionGroup> <!-- Delete attribute set --> @@ -49,7 +49,7 @@ <deleteData createDataKey="createCategory" stepKey="deleteCategory"/> <!-- Log out --> - <actionGroup ref="logout" stepKey="logout"/> + <actionGroup ref="AdminLogoutActionGroup" stepKey="logout"/> </after> <!-- Create configurable product --> diff --git a/app/code/Magento/ConfigurableProduct/Test/Mftf/Test/AdminCreateConfigurableProductWithDisabledChildrenProductsTest.xml b/app/code/Magento/ConfigurableProduct/Test/Mftf/Test/AdminCreateConfigurableProductWithDisabledChildrenProductsTest.xml index f64ba4a63cec9..3bf5666d5a997 100644 --- a/app/code/Magento/ConfigurableProduct/Test/Mftf/Test/AdminCreateConfigurableProductWithDisabledChildrenProductsTest.xml +++ b/app/code/Magento/ConfigurableProduct/Test/Mftf/Test/AdminCreateConfigurableProductWithDisabledChildrenProductsTest.xml @@ -36,7 +36,7 @@ <requiredEntity createDataKey="getConfigAttributeOption"/> </createData> <createData entity="SimpleSubCategory" stepKey="createCategory"/> - <actionGroup ref="LoginAsAdmin" stepKey="loginAsAdmin"/> + <actionGroup ref="AdminLoginActionGroup" stepKey="loginAsAdmin"/> </before> <after> <!-- Don't display out of stock product --> @@ -53,7 +53,10 @@ <deleteData createDataKey="createCategory" stepKey="deleteCategory"/> <!-- Log out --> - <actionGroup ref="logout" stepKey="logout"/> + <actionGroup ref="AdminLogoutActionGroup" stepKey="logout"/> + + <!-- Reindex invalidated indices after product attribute has been created/deleted --> + <magentoCron groups="index" stepKey="reindexInvalidatedIndices"/> </after> <!-- Create configurable product --> diff --git a/app/code/Magento/ConfigurableProduct/Test/Mftf/Test/AdminCreateConfigurableProductWithImagesTest.xml b/app/code/Magento/ConfigurableProduct/Test/Mftf/Test/AdminCreateConfigurableProductWithImagesTest.xml index b448131e75f15..fa8866fa7d91c 100644 --- a/app/code/Magento/ConfigurableProduct/Test/Mftf/Test/AdminCreateConfigurableProductWithImagesTest.xml +++ b/app/code/Magento/ConfigurableProduct/Test/Mftf/Test/AdminCreateConfigurableProductWithImagesTest.xml @@ -42,7 +42,7 @@ </createData> <createData entity="SimpleSubCategory" stepKey="createCategory"/> - <actionGroup ref="LoginAsAdmin" stepKey="loginAsAdmin"/> + <actionGroup ref="AdminLoginActionGroup" stepKey="loginAsAdmin"/> </before> <after> <!-- Delete configurable product --> @@ -56,7 +56,10 @@ <deleteData createDataKey="createCategory" stepKey="deleteCategory"/> <!-- Log out --> - <actionGroup ref="logout" stepKey="logout"/> + <actionGroup ref="AdminLogoutActionGroup" stepKey="logout"/> + + <!-- Reindex invalidated indices after product attribute has been created/deleted --> + <magentoCron groups="index" stepKey="reindexInvalidatedIndices"/> </after> <!--Create configurable product --> diff --git a/app/code/Magento/ConfigurableProduct/Test/Mftf/Test/AdminCreateConfigurableProductWithThreeProductDisplayOutOfStockProductsTest.xml b/app/code/Magento/ConfigurableProduct/Test/Mftf/Test/AdminCreateConfigurableProductWithThreeProductDisplayOutOfStockProductsTest.xml index 1c6908818b4da..e76d14f3a6aae 100644 --- a/app/code/Magento/ConfigurableProduct/Test/Mftf/Test/AdminCreateConfigurableProductWithThreeProductDisplayOutOfStockProductsTest.xml +++ b/app/code/Magento/ConfigurableProduct/Test/Mftf/Test/AdminCreateConfigurableProductWithThreeProductDisplayOutOfStockProductsTest.xml @@ -57,7 +57,7 @@ </createData> <createData entity="SimpleSubCategory" stepKey="createCategory"/> - <actionGroup ref="LoginAsAdmin" stepKey="loginAsAdmin"/> + <actionGroup ref="AdminLoginActionGroup" stepKey="loginAsAdmin"/> </before> <after> <!-- Don't display out of stock product --> @@ -76,7 +76,10 @@ <deleteData createDataKey="createCategory" stepKey="deleteCategory"/> <!-- Log out --> - <actionGroup ref="logout" stepKey="logout"/> + <actionGroup ref="AdminLogoutActionGroup" stepKey="logout"/> + + <!-- Reindex invalidated indices after product attribute has been created/deleted --> + <magentoCron groups="index" stepKey="reindexInvalidatedIndices"/> </after> <!--Create configurable product --> diff --git a/app/code/Magento/ConfigurableProduct/Test/Mftf/Test/AdminCreateConfigurableProductWithThreeProductDontDisplayOutOfStockProductsTest.xml b/app/code/Magento/ConfigurableProduct/Test/Mftf/Test/AdminCreateConfigurableProductWithThreeProductDontDisplayOutOfStockProductsTest.xml index 9b4cea72882eb..9516216d4a62e 100644 --- a/app/code/Magento/ConfigurableProduct/Test/Mftf/Test/AdminCreateConfigurableProductWithThreeProductDontDisplayOutOfStockProductsTest.xml +++ b/app/code/Magento/ConfigurableProduct/Test/Mftf/Test/AdminCreateConfigurableProductWithThreeProductDontDisplayOutOfStockProductsTest.xml @@ -57,7 +57,7 @@ </createData> <createData entity="SimpleSubCategory" stepKey="createCategory"/> - <actionGroup ref="LoginAsAdmin" stepKey="loginAsAdmin"/> + <actionGroup ref="AdminLoginActionGroup" stepKey="loginAsAdmin"/> </before> <after> <!-- Delete configurable product --> @@ -72,8 +72,13 @@ <deleteData createDataKey="createConfigProductAttribute" stepKey="deleteConfigProductAttribute"/> <deleteData createDataKey="createCategory" stepKey="deleteCategory"/> + <magentoCLI command="cron:run --group=index" stepKey="runCron"/> + <!-- Log out --> - <actionGroup ref="logout" stepKey="logout"/> + <actionGroup ref="AdminLogoutActionGroup" stepKey="logout"/> + + <!-- Reindex invalidated indices after product attribute has been created/deleted --> + <magentoCron groups="index" stepKey="reindexInvalidatedIndices"/> </after> <!--Create configurable product --> diff --git a/app/code/Magento/ConfigurableProduct/Test/Mftf/Test/AdminCreateConfigurableProductWithTierPriceForOneItemTest.xml b/app/code/Magento/ConfigurableProduct/Test/Mftf/Test/AdminCreateConfigurableProductWithTierPriceForOneItemTest.xml index e67788d5e7bb0..660eb82a9eacb 100644 --- a/app/code/Magento/ConfigurableProduct/Test/Mftf/Test/AdminCreateConfigurableProductWithTierPriceForOneItemTest.xml +++ b/app/code/Magento/ConfigurableProduct/Test/Mftf/Test/AdminCreateConfigurableProductWithTierPriceForOneItemTest.xml @@ -50,7 +50,7 @@ <createData entity="tierProductPrice" stepKey="addTierPrice"> <requiredEntity createDataKey="createFirstSimpleProduct" /> </createData> - <actionGroup ref="LoginAsAdmin" stepKey="loginAsAdmin"/> + <actionGroup ref="AdminLoginActionGroup" stepKey="loginAsAdmin"/> </before> <after> <!-- Delete configurable product --> @@ -64,7 +64,10 @@ <deleteData createDataKey="createConfigProductAttribute" stepKey="deleteConfigProductAttribute"/> <!-- Log out --> - <actionGroup ref="logout" stepKey="logout"/> + <actionGroup ref="AdminLogoutActionGroup" stepKey="logout"/> + + <!-- Reindex invalidated indices after product attribute has been created/deleted --> + <magentoCron groups="index" stepKey="reindexInvalidatedIndices"/> </after> <!--Create configurable product --> diff --git a/app/code/Magento/ConfigurableProduct/Test/Mftf/Test/AdminCreateConfigurableProductWithTwoOptionsAssignedToCategoryTest.xml b/app/code/Magento/ConfigurableProduct/Test/Mftf/Test/AdminCreateConfigurableProductWithTwoOptionsAssignedToCategoryTest.xml index 2f4fc97cb48ff..bceabb27e3ae7 100644 --- a/app/code/Magento/ConfigurableProduct/Test/Mftf/Test/AdminCreateConfigurableProductWithTwoOptionsAssignedToCategoryTest.xml +++ b/app/code/Magento/ConfigurableProduct/Test/Mftf/Test/AdminCreateConfigurableProductWithTwoOptionsAssignedToCategoryTest.xml @@ -22,7 +22,7 @@ <createData entity="SimpleSubCategory" stepKey="createCategory"/> <!-- Login as admin --> - <actionGroup ref="LoginAsAdmin" stepKey="loginAsAdmin"/> + <actionGroup ref="AdminLoginActionGroup" stepKey="loginAsAdmin"/> </before> <after> <!-- Delete configurable product --> @@ -39,8 +39,8 @@ </actionGroup> <!-- Delete product attribute --> - <actionGroup ref="DeleteProductAttributeByLabelActionGroup" stepKey="deleteProductAttribute"> - <argument name="ProductAttribute" value="colorProductAttribute"/> + <actionGroup ref="AdminDeleteProductAttributeByLabelActionGroup" stepKey="deleteProductAttribute"> + <argument name="productAttributeLabel" value="{{colorProductAttribute.default_label}}"/> </actionGroup> <!-- Delete attribute set --> @@ -52,7 +52,7 @@ <deleteData createDataKey="createCategory" stepKey="deleteCategory"/> <!-- Log out --> - <actionGroup ref="logout" stepKey="logout"/> + <actionGroup ref="AdminLogoutActionGroup" stepKey="logout"/> </after> <!-- Create configurable product --> @@ -131,7 +131,7 @@ <waitForElementVisible selector="{{StorefrontCategoryMainSection.SuccessMsg}}" stepKey="waitForSuccessMessage"/> <!-- Assert configurable product in cart --> - <amOnPage url="/checkout/cart/" stepKey="amOnShoppingCartPage"/> + <amOnPage url="{{CheckoutCartPage.url}}" stepKey="amOnShoppingCartPage"/> <waitForPageLoad stepKey="waitForShoppingCartPageLoad"/> <actionGroup ref="StorefrontCheckCartConfigurableProductActionGroup" stepKey="storefrontCheckCartConfigurableProductActionGroup"> <argument name="product" value="ApiConfigurableProduct"/> @@ -140,8 +140,7 @@ </actionGroup> <!-- Assert child products are not displayed separately: two next step --> - <amOnPage url="{{StorefrontHomePage.url}}" stepKey="goToStoreFront"/> - <waitForPageLoad stepKey="waitForStoreFrontPageLoad"/> + <actionGroup ref="StorefrontOpenHomePageActionGroup" stepKey="goToStoreFront"/> <!-- Quick search the storefront for the first attribute option --> <submitForm selector="{{StorefrontQuickSearchSection.searchMiniForm}}" parameterArray="['q' => {{colorConfigurableProductAttribute1.sku}}]" stepKey="searchStorefrontFirstChildProduct"/> diff --git a/app/code/Magento/ConfigurableProduct/Test/Mftf/Test/AdminCreateConfigurableProductWithTwoOptionsWithoutAssignedToCategoryTest.xml b/app/code/Magento/ConfigurableProduct/Test/Mftf/Test/AdminCreateConfigurableProductWithTwoOptionsWithoutAssignedToCategoryTest.xml index 0d8f8bfb65c16..9bb5b5073215b 100644 --- a/app/code/Magento/ConfigurableProduct/Test/Mftf/Test/AdminCreateConfigurableProductWithTwoOptionsWithoutAssignedToCategoryTest.xml +++ b/app/code/Magento/ConfigurableProduct/Test/Mftf/Test/AdminCreateConfigurableProductWithTwoOptionsWithoutAssignedToCategoryTest.xml @@ -18,7 +18,7 @@ <group value="mtf_migrated"/> </annotations> <before> - <actionGroup ref="LoginAsAdmin" stepKey="loginAsAdmin"/> + <actionGroup ref="AdminLoginActionGroup" stepKey="loginAsAdmin"/> </before> <after> <!-- Delete configurable product --> @@ -35,8 +35,8 @@ </actionGroup> <!-- Delete product attribute --> - <actionGroup ref="DeleteProductAttributeByLabelActionGroup" stepKey="deleteProductAttribute"> - <argument name="ProductAttribute" value="colorProductAttribute"/> + <actionGroup ref="AdminDeleteProductAttributeByLabelActionGroup" stepKey="deleteProductAttribute"> + <argument name="productAttributeLabel" value="{{colorProductAttribute.default_label}}"/> </actionGroup> <!-- Delete attribute set --> @@ -45,7 +45,7 @@ </actionGroup> <!-- Log out --> - <actionGroup ref="logout" stepKey="logout"/> + <actionGroup ref="AdminLogoutActionGroup" stepKey="logout"/> </after> <!-- Create configurable product --> @@ -113,7 +113,7 @@ <waitForElementVisible selector="{{StorefrontCategoryMainSection.SuccessMsg}}" stepKey="waitForSuccessMessage"/> <!-- Assert configurable product in cart --> - <amOnPage url="/checkout/cart/" stepKey="amOnShoppingCartPage"/> + <amOnPage url="{{CheckoutCartPage.url}}" stepKey="amOnShoppingCartPage"/> <waitForPageLoad stepKey="waitForShoppingCartPageLoad"/> <actionGroup ref="StorefrontCheckCartConfigurableProductActionGroup" stepKey="storefrontCheckCartConfigurableProductActionGroup"> <argument name="product" value="ApiConfigurableProduct"/> @@ -122,8 +122,7 @@ </actionGroup> <!-- Assert child products are not displayed separately: two next step --> - <amOnPage url="{{StorefrontHomePage.url}}" stepKey="goToStoreFront"/> - <waitForPageLoad stepKey="waitForStoreFrontPageLoad"/> + <actionGroup ref="StorefrontOpenHomePageActionGroup" stepKey="goToStoreFront"/> <!-- Quick search the storefront for the first attribute option --> <submitForm selector="{{StorefrontQuickSearchSection.searchMiniForm}}" parameterArray="['q' => {{colorConfigurableProductAttribute1.sku}}]" stepKey="searchStorefrontFirstChildProduct"/> diff --git a/app/code/Magento/ConfigurableProduct/Test/Mftf/Test/AdminDeleteConfigurableProductTest.xml b/app/code/Magento/ConfigurableProduct/Test/Mftf/Test/AdminDeleteConfigurableProductTest.xml index c2edbaa4e6e87..e625a1cf6f2be 100644 --- a/app/code/Magento/ConfigurableProduct/Test/Mftf/Test/AdminDeleteConfigurableProductTest.xml +++ b/app/code/Magento/ConfigurableProduct/Test/Mftf/Test/AdminDeleteConfigurableProductTest.xml @@ -18,7 +18,7 @@ <group value="mtf_migrated"/> </annotations> <before> - <actionGroup ref="LoginAsAdmin" stepKey="loginAsAdmin"/> + <actionGroup ref="AdminLoginActionGroup" stepKey="loginAsAdmin"/> <createData entity="_defaultCategory" stepKey="createCategory"/> <createData entity="BaseConfigurableProduct" stepKey="createConfigurableProduct"> <requiredEntity createDataKey="createCategory"/> @@ -26,7 +26,7 @@ </before> <after> <deleteData createDataKey="createCategory" stepKey="deleteCategory"/> - <actionGroup ref="logout" stepKey="logout"/> + <actionGroup ref="AdminLogoutActionGroup" stepKey="logout"/> </after> <actionGroup ref="DeleteProductUsingProductGridActionGroup" stepKey="deleteConfigurableProductFilteredBySkuAndName"> <argument name="product" value="$$createConfigurableProduct$$"/> diff --git a/app/code/Magento/ConfigurableProduct/Test/Mftf/Test/AdminProductTypeSwitchingOnEditingTest.xml b/app/code/Magento/ConfigurableProduct/Test/Mftf/Test/AdminProductTypeSwitchingOnEditingTest.xml deleted file mode 100644 index f1535d62861ac..0000000000000 --- a/app/code/Magento/ConfigurableProduct/Test/Mftf/Test/AdminProductTypeSwitchingOnEditingTest.xml +++ /dev/null @@ -1,181 +0,0 @@ -<?xml version="1.0" encoding="UTF-8"?> -<!-- - /** - * Copyright © Magento, Inc. All rights reserved. - * See COPYING.txt for license details. - */ ---> - -<tests xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" - xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/testSchema.xsd"> - <test name="AdminSimpleProductTypeSwitchingToConfigurableProductTest"> - <annotations> - <features value="ConfigurableProduct"/> - <stories value="Product type switching"/> - <title value="Simple product type switching on editing to configurable product"/> - <description value="Simple product type switching on editing to configurable product"/> - <testCaseId value="MAGETWO-29633"/> - <useCaseId value="MAGETWO-44170"/> - <severity value="MAJOR"/> - <group value="catalog"/> - </annotations> - <before> - <actionGroup ref="LoginAsAdmin" stepKey="loginAsAdmin"/> - <!--Create product--> - <comment userInput="Create product" stepKey="commentCreateProduct"/> - <createData entity="SimpleProduct2" stepKey="createProduct"/> - <!--Create attribute with options--> - <comment userInput="Create attribute with options" stepKey="commentCreateAttributeWithOptions"/> - <createData entity="productAttributeWithTwoOptions" stepKey="createConfigProductAttribute"/> - <createData entity="productAttributeOption1" stepKey="createConfigProductAttributeOptionOne"> - <requiredEntity createDataKey="createConfigProductAttribute"/> - </createData> - <createData entity="productAttributeOption2" stepKey="createConfigProductAttributeOptionTwo"> - <requiredEntity createDataKey="createConfigProductAttribute"/> - </createData> - </before> - <after> - <!--Delete product--> - <comment userInput="Delete product" stepKey="commentDeleteProduct"/> - <deleteData createDataKey="createProduct" stepKey="deleteProduct"/> - <deleteData createDataKey="createConfigProductAttribute" stepKey="deleteAttribute"/> - <actionGroup ref="DeleteAllDuplicateProductUsingProductGridActionGroup" stepKey="deleteAllDuplicateProducts"> - <argument name="product" value="$$createProduct$$"/> - </actionGroup> - <actionGroup ref="AdminClearFiltersActionGroup" stepKey="clearProductFilters"/> - <actionGroup ref="logout" stepKey="logout"/> - </after> - <!--Add configurations to product--> - <comment userInput="Add configurations to product" stepKey="commentAddConfigs"/> - <amOnPage url="{{AdminProductEditPage.url($$createProduct.id$$)}}" stepKey="gotToSimpleProductPage"/> - <waitForPageLoad stepKey="waitForSimpleProductPageLoad"/> - <actionGroup ref="GenerateConfigurationsByAttributeCodeActionGroup" stepKey="setupConfigurations"> - <argument name="attributeCode" value="$$createConfigProductAttribute.attribute_code$$"/> - </actionGroup> - <actionGroup ref="SaveConfiguredProductActionGroup" stepKey="saveConfigProductForm"/> - <!--Assert configurable product on Admin product page grid--> - <comment userInput="Assert configurable product in Admin product page grid" stepKey="commentAssertConfigProductOnAdmin"/> - <amOnPage url="{{AdminCatalogProductPage.url}}" stepKey="goToCatalogProductPage"/> - <actionGroup ref="FilterProductGridBySku2ActionGroup" stepKey="filterProductGridBySku"> - <argument name="sku" value="$$createProduct.sku$$"/> - </actionGroup> - <see selector="{{AdminProductGridSection.productGridCell('1', 'Name')}}" userInput="$$createProduct.name$$" stepKey="seeProductNameInGrid"/> - <see selector="{{AdminProductGridSection.productGridCell('1', 'Type')}}" userInput="Configurable Product" stepKey="seeProductTypeInGrid"/> - <see selector="{{AdminProductGridSection.productGridCell('2', 'Name')}}" userInput="$$createProduct.name$$-option1" stepKey="seeProductNameInGrid1"/> - <see selector="{{AdminProductGridSection.productGridCell('3', 'Name')}}" userInput="$$createProduct.name$$-option2" stepKey="seeProductNameInGrid2"/> - <actionGroup ref="AdminClearFiltersActionGroup" stepKey="clearProductFilters"/> - <!--Assert configurable product on storefront--> - <comment userInput="Assert configurable product on storefront" stepKey="commentAssertConfigProductOnStorefront"/> - <amOnPage url="{{StorefrontProductPage.url($$createProduct.name$$)}}" stepKey="openProductPage"/> - <waitForPageLoad stepKey="waitForStorefrontProductPageLoad"/> - <see userInput="IN STOCK" selector="{{StorefrontProductInfoMainSection.productStockStatus}}" stepKey="assertInStock"/> - <click selector="{{StorefrontProductInfoMainSection.productAttributeOptionsSelectButton}}" stepKey="clickAttributeDropDown"/> - <see userInput="option1" stepKey="verifyOption1Exists"/> - <see userInput="option2" stepKey="verifyOption2Exists"/> - </test> - <test name="AdminConfigurableProductTypeSwitchingToVirtualProductTest" extends="AdminSimpleProductTypeSwitchingToConfigurableProductTest"> - <annotations> - <features value="ConfigurableProduct"/> - <stories value="Product type switching"/> - <title value="Configurable product type switching on editing to virtual product"/> - <description value="Configurable product type switching on editing to virtual product"/> - <testCaseId value="MC-17952"/> - <useCaseId value="MAGETWO-44170"/> - <severity value="MAJOR"/> - <group value="catalog"/> - </annotations> - <!--Delete product configurations--> - <comment userInput="Delete product configuration" stepKey="commentDeleteConfigs"/> - <amOnPage url="{{AdminProductEditPage.url($$createProduct.id$$)}}" stepKey="gotToConfigProductPage"/> - <waitForPageLoad stepKey="waitForConfigurableProductPageLoad"/> - <conditionalClick selector="{{ AdminProductFormConfigurationsSection.sectionHeader}}" dependentSelector="{{AdminProductFormConfigurationsSection.createConfigurations}}" visible="false" stepKey="openConfigurationSection"/> - <click selector="{{AdminProductFormConfigurationsSection.actionsBtn('1')}}" stepKey="clickToExpandOption1Actions"/> - <click selector="{{AdminProductFormConfigurationsSection.removeProductBtn}}" stepKey="clickRemoveOption1"/> - <click selector="{{AdminProductFormConfigurationsSection.actionsBtn('1')}}" stepKey="clickToExpandOption2Actions"/> - <click selector="{{AdminProductFormConfigurationsSection.removeProductBtn}}" stepKey="clickRemoveOption2"/> - <fillField selector="{{AdminProductFormSection.productPrice}}" userInput="{{SimpleProduct2.price}}" stepKey="fillProductPrice"/> - <fillField selector="{{AdminProductFormSection.productQuantity}}" userInput="{{SimpleProduct2.quantity}}" stepKey="fillProductQty"/> - <clearField selector="{{AdminProductFormSection.productWeight}}" stepKey="clearWeightField"/> - <selectOption selector="{{AdminProductFormSection.productWeightSelect}}" userInput="This item has no weight" stepKey="selectNoWeight"/> - <actionGroup ref="SaveProductFormActionGroup" stepKey="saveVirtualProductForm"/> - <!--Assert virtual product on Admin product page grid--> - <comment userInput="Assert virtual product on Admin product page grid" stepKey="commentAssertVirtualProductOnAdmin"/> - <amOnPage url="{{AdminCatalogProductPage.url}}" stepKey="goToCatalogProductPageForVirtual"/> - <actionGroup ref="FilterProductGridBySku2ActionGroup" stepKey="filterProductGridBySkuForVirtual"> - <argument name="sku" value="$$createProduct.sku$$"/> - </actionGroup> - <see selector="{{AdminProductGridSection.productGridCell('1', 'Name')}}" userInput="$$createProduct.name$$" stepKey="seeVirtualProductNameInGrid"/> - <see selector="{{AdminProductGridSection.productGridCell('1', 'Type')}}" userInput="Virtual Product" stepKey="seeVirtualProductTypeInGrid"/> - <!--Assert virtual product on storefront--> - <comment userInput="Assert virtual product on storefront" stepKey="commentAssertVirtualProductOnStorefront"/> - <amOnPage url="{{StorefrontProductPage.url($$createProduct.name$$)}}" stepKey="openVirtualProductPage"/> - <waitForPageLoad stepKey="waitForStorefrontVirtualProductPageLoad"/> - <see userInput="IN STOCK" selector="{{StorefrontProductInfoMainSection.productStockStatus}}" stepKey="assertVirtualProductInStock"/> - </test> - <test name="AdminVirtualProductTypeSwitchingToConfigurableProductTest"> - <annotations> - <features value="ConfigurableProduct"/> - <stories value="Product type switching"/> - <title value="Virtual product type switching on editing to configurable product"/> - <description value="Virtual product type switching on editing to configurable product"/> - <testCaseId value="MC-17953"/> - <useCaseId value="MAGETWO-44170"/> - <severity value="MAJOR"/> - <group value="catalog"/> - </annotations> - <before> - <actionGroup ref="LoginAsAdmin" stepKey="loginAsAdmin"/> - <!--Create product--> - <comment userInput="Create product" stepKey="commentCreateProduct"/> - <createData entity="VirtualProduct" stepKey="createProduct"/> - <!--Create attribute with options--> - <comment userInput="Create attribute with options" stepKey="commentCreateAttributeWithOptions"/> - <createData entity="productAttributeWithTwoOptions" stepKey="createConfigProductAttribute"/> - <createData entity="productAttributeOption1" stepKey="createConfigProductAttributeOptionOne"> - <requiredEntity createDataKey="createConfigProductAttribute"/> - </createData> - <createData entity="productAttributeOption2" stepKey="createConfigProductAttributeOptionTwo"> - <requiredEntity createDataKey="createConfigProductAttribute"/> - </createData> - </before> - <after> - <!--Delete product--> - <comment userInput="Delete product" stepKey="commentDeleteProduct"/> - <deleteData createDataKey="createProduct" stepKey="deleteProduct"/> - <deleteData createDataKey="createConfigProductAttribute" stepKey="deleteAttribute"/> - <actionGroup ref="DeleteAllDuplicateProductUsingProductGridActionGroup" stepKey="deleteAllDuplicateProducts"> - <argument name="product" value="$$createProduct$$"/> - </actionGroup> - <actionGroup ref="AdminClearFiltersActionGroup" stepKey="clearProductFilters"/> - <actionGroup ref="logout" stepKey="logout"/> - </after> - <!--Add configurations to product--> - <comment userInput="Add configurations to product" stepKey="commentAddConfigurations"/> - <amOnPage url="{{AdminProductEditPage.url($$createProduct.id$$)}}" stepKey="gotToConfigProductPage"/> - <waitForPageLoad stepKey="waitForConfigurableProductPageLoad"/> - <selectOption selector="{{AdminProductFormSection.productWeightSelect}}" userInput="This item has weight" stepKey="selectWeightForConfigurableProduct"/> - <actionGroup ref="GenerateConfigurationsByAttributeCodeActionGroup" stepKey="setupConfigurationsForProduct"> - <argument name="attributeCode" value="$$createConfigProductAttribute.attribute_code$$"/> - </actionGroup> - <actionGroup ref="SaveConfiguredProductActionGroup" stepKey="saveNewConfigurableProductForm"/> - <!--Assert configurable product on Admin product page grid--> - <comment userInput="Assert configurable product in Admin product page grid" stepKey="commentAssertConfigurableProductOnAdmin"/> - <amOnPage url="{{AdminCatalogProductPage.url}}" stepKey="goToCatalogProductPageForConfigurable"/> - <actionGroup ref="FilterProductGridBySku2ActionGroup" stepKey="filterProductGridBySkuForConfigurable"> - <argument name="sku" value="$$createProduct.sku$$"/> - </actionGroup> - <see selector="{{AdminProductGridSection.productGridCell('1', 'Name')}}" userInput="$$createProduct.name$$" stepKey="seeConfigurableProductNameInGrid"/> - <see selector="{{AdminProductGridSection.productGridCell('1', 'Type')}}" userInput="Configurable Product" stepKey="seeConfigurableProductTypeInGrid"/> - <see selector="{{AdminProductGridSection.productGridCell('2', 'Name')}}" userInput="$$createProduct.name$$-option1" stepKey="seeConfigurableProductNameInGrid1"/> - <see selector="{{AdminProductGridSection.productGridCell('3', 'Name')}}" userInput="$$createProduct.name$$-option2" stepKey="seeConfigurableProductNameInGrid2"/> - <actionGroup ref="AdminClearFiltersActionGroup" stepKey="clearConfigurableProductFilters"/> - <!--Assert configurable product on storefront--> - <comment userInput="Assert configurable product on storefront" stepKey="commentAssertConfigurableProductOnStorefront"/> - <amOnPage url="{{StorefrontProductPage.url($$createProduct.name$$)}}" stepKey="openConfigurableProductPage"/> - <waitForPageLoad stepKey="waitForStorefrontConfigurableProductPageLoad"/> - <see userInput="IN STOCK" selector="{{StorefrontProductInfoMainSection.productStockStatus}}" stepKey="assertConfigurableProductInStock"/> - <click selector="{{StorefrontProductInfoMainSection.productAttributeOptionsSelectButton}}" stepKey="clickConfigurableAttributeDropDown"/> - <see userInput="option1" stepKey="verifyConfigurableProductOption1Exists"/> - <see userInput="option2" stepKey="verifyConfigurableProductOption2Exists"/> - </test> -</tests> diff --git a/app/code/Magento/ConfigurableProduct/Test/Mftf/Test/AdminProductTypeSwitchingOnEditingTest/AdminConfigurableProductTypeSwitchingToVirtualProductTest.xml b/app/code/Magento/ConfigurableProduct/Test/Mftf/Test/AdminProductTypeSwitchingOnEditingTest/AdminConfigurableProductTypeSwitchingToVirtualProductTest.xml new file mode 100644 index 0000000000000..36450313f9f3a --- /dev/null +++ b/app/code/Magento/ConfigurableProduct/Test/Mftf/Test/AdminProductTypeSwitchingOnEditingTest/AdminConfigurableProductTypeSwitchingToVirtualProductTest.xml @@ -0,0 +1,51 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + /** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +--> +<tests xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/testSchema.xsd"> + <test name="AdminConfigurableProductTypeSwitchingToVirtualProductTest" extends="AdminSimpleProductTypeSwitchingToConfigurableProductTest"> + <annotations> + <features value="ConfigurableProduct"/> + <stories value="Product type switching"/> + <title value="Configurable product type switching on editing to virtual product"/> + <description value="Configurable product type switching on editing to virtual product"/> + <testCaseId value="MC-17952"/> + <useCaseId value="MAGETWO-44170"/> + <severity value="MAJOR"/> + <group value="catalog"/> + </annotations> + <!--Delete product configurations--> + <comment userInput="Delete product configuration" stepKey="commentDeleteConfigs"/> + <actionGroup ref="AdminProductPageOpenByIdActionGroup" stepKey="gotToConfigProductPage"> + <argument name="productId" value="$$createProduct.id$$"/> + </actionGroup> + <waitForPageLoad stepKey="waitForConfigurableProductPageLoad"/> + <conditionalClick selector="{{ AdminProductFormConfigurationsSection.sectionHeader}}" dependentSelector="{{AdminProductFormConfigurationsSection.createConfigurations}}" visible="false" stepKey="openConfigurationSection"/> + <click selector="{{AdminProductFormConfigurationsSection.actionsBtn('1')}}" stepKey="clickToExpandOption1Actions"/> + <click selector="{{AdminProductFormConfigurationsSection.removeProductBtn}}" stepKey="clickRemoveOption1"/> + <click selector="{{AdminProductFormConfigurationsSection.actionsBtn('1')}}" stepKey="clickToExpandOption2Actions"/> + <click selector="{{AdminProductFormConfigurationsSection.removeProductBtn}}" stepKey="clickRemoveOption2"/> + <fillField selector="{{AdminProductFormSection.productPrice}}" userInput="{{SimpleProduct2.price}}" stepKey="fillProductPrice"/> + <fillField selector="{{AdminProductFormSection.productQuantity}}" userInput="{{SimpleProduct2.quantity}}" stepKey="fillProductQty"/> + <clearField selector="{{AdminProductFormSection.productWeight}}" stepKey="clearWeightField"/> + <selectOption selector="{{AdminProductFormSection.productWeightSelect}}" userInput="This item has no weight" stepKey="selectNoWeight"/> + <actionGroup ref="SaveProductFormActionGroup" stepKey="saveVirtualProductForm"/> + <!--Assert virtual product on Admin product page grid--> + <comment userInput="Assert virtual product on Admin product page grid" stepKey="commentAssertVirtualProductOnAdmin"/> + <amOnPage url="{{AdminCatalogProductPage.url}}" stepKey="goToCatalogProductPageForVirtual"/> + <actionGroup ref="FilterProductGridBySku2ActionGroup" stepKey="filterProductGridBySkuForVirtual"> + <argument name="sku" value="$$createProduct.sku$$"/> + </actionGroup> + <see selector="{{AdminProductGridSection.productGridCell('1', 'Name')}}" userInput="$$createProduct.name$$" stepKey="seeVirtualProductNameInGrid"/> + <see selector="{{AdminProductGridSection.productGridCell('1', 'Type')}}" userInput="Virtual Product" stepKey="seeVirtualProductTypeInGrid"/> + <!--Assert virtual product on storefront--> + <comment userInput="Assert virtual product on storefront" stepKey="commentAssertVirtualProductOnStorefront"/> + <amOnPage url="{{StorefrontProductPage.url($$createProduct.name$$)}}" stepKey="openVirtualProductPage"/> + <waitForPageLoad stepKey="waitForStorefrontVirtualProductPageLoad"/> + <see userInput="IN STOCK" selector="{{StorefrontProductInfoMainSection.productStockStatus}}" stepKey="assertVirtualProductInStock"/> + </test> +</tests> diff --git a/app/code/Magento/ConfigurableProduct/Test/Mftf/Test/AdminProductTypeSwitchingOnEditingTest/AdminSimpleProductTypeSwitchingToConfigurableProductTest.xml b/app/code/Magento/ConfigurableProduct/Test/Mftf/Test/AdminProductTypeSwitchingOnEditingTest/AdminSimpleProductTypeSwitchingToConfigurableProductTest.xml new file mode 100644 index 0000000000000..c5fdbfb36490a --- /dev/null +++ b/app/code/Magento/ConfigurableProduct/Test/Mftf/Test/AdminProductTypeSwitchingOnEditingTest/AdminSimpleProductTypeSwitchingToConfigurableProductTest.xml @@ -0,0 +1,80 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + /** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +--> +<tests xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/testSchema.xsd"> + <test name="AdminSimpleProductTypeSwitchingToConfigurableProductTest"> + <annotations> + <features value="ConfigurableProduct"/> + <stories value="Product type switching"/> + <title value="Simple product type switching on editing to configurable product"/> + <description value="Simple product type switching on editing to configurable product"/> + <testCaseId value="MAGETWO-29633"/> + <useCaseId value="MAGETWO-44170"/> + <severity value="MAJOR"/> + <group value="catalog"/> + </annotations> + <before> + <actionGroup ref="AdminLoginActionGroup" stepKey="loginAsAdmin"/> + <!--Create product--> + <comment userInput="Create product" stepKey="commentCreateProduct"/> + <createData entity="SimpleProduct2" stepKey="createProduct"/> + <!--Create attribute with options--> + <comment userInput="Create attribute with options" stepKey="commentCreateAttributeWithOptions"/> + <createData entity="productAttributeWithTwoOptions" stepKey="createConfigProductAttribute"/> + <createData entity="productAttributeOption1" stepKey="createConfigProductAttributeOptionOne"> + <requiredEntity createDataKey="createConfigProductAttribute"/> + </createData> + <createData entity="productAttributeOption2" stepKey="createConfigProductAttributeOptionTwo"> + <requiredEntity createDataKey="createConfigProductAttribute"/> + </createData> + </before> + <after> + <!--Delete product--> + <comment userInput="Delete product" stepKey="commentDeleteProduct"/> + <deleteData createDataKey="createProduct" stepKey="deleteProduct"/> + <deleteData createDataKey="createConfigProductAttribute" stepKey="deleteAttribute"/> + <actionGroup ref="DeleteAllDuplicateProductUsingProductGridActionGroup" stepKey="deleteAllDuplicateProducts"> + <argument name="product" value="$$createProduct$$"/> + </actionGroup> + <actionGroup ref="AdminClearFiltersActionGroup" stepKey="clearProductFilters"/> + <actionGroup ref="AdminLogoutActionGroup" stepKey="logout"/> + + <!-- Reindex invalidated indices after product attribute has been created/deleted --> + <magentoCron groups="index" stepKey="reindexInvalidatedIndices"/> + </after> + <!--Add configurations to product--> + <comment userInput="Add configurations to product" stepKey="commentAddConfigs"/> + <actionGroup ref="AdminProductPageOpenByIdActionGroup" stepKey="gotToSimpleProductPage"> + <argument name="productId" value="$$createProduct.id$$"/> + </actionGroup> + <waitForPageLoad stepKey="waitForSimpleProductPageLoad"/> + <actionGroup ref="GenerateConfigurationsByAttributeCodeActionGroup" stepKey="setupConfigurations"> + <argument name="attributeCode" value="$$createConfigProductAttribute.attribute_code$$"/> + </actionGroup> + <actionGroup ref="SaveConfiguredProductActionGroup" stepKey="saveConfigProductForm"/> + <!--Assert configurable product on Admin product page grid--> + <comment userInput="Assert configurable product in Admin product page grid" stepKey="commentAssertConfigProductOnAdmin"/> + <amOnPage url="{{AdminCatalogProductPage.url}}" stepKey="goToCatalogProductPage"/> + <actionGroup ref="FilterProductGridBySku2ActionGroup" stepKey="filterProductGridBySku"> + <argument name="sku" value="$$createProduct.sku$$"/> + </actionGroup> + <see selector="{{AdminProductGridSection.productGridCell('1', 'Name')}}" userInput="$$createProduct.name$$" stepKey="seeProductNameInGrid"/> + <see selector="{{AdminProductGridSection.productGridCell('1', 'Type')}}" userInput="Configurable Product" stepKey="seeProductTypeInGrid"/> + <see selector="{{AdminProductGridSection.productGridCell('2', 'Name')}}" userInput="$$createProduct.name$$-option1" stepKey="seeProductNameInGrid1"/> + <see selector="{{AdminProductGridSection.productGridCell('3', 'Name')}}" userInput="$$createProduct.name$$-option2" stepKey="seeProductNameInGrid2"/> + <actionGroup ref="AdminClearFiltersActionGroup" stepKey="clearProductFilters"/> + <!--Assert configurable product on storefront--> + <comment userInput="Assert configurable product on storefront" stepKey="commentAssertConfigProductOnStorefront"/> + <amOnPage url="{{StorefrontProductPage.url($$createProduct.name$$)}}" stepKey="openProductPage"/> + <waitForPageLoad stepKey="waitForStorefrontProductPageLoad"/> + <see userInput="IN STOCK" selector="{{StorefrontProductInfoMainSection.productStockStatus}}" stepKey="assertInStock"/> + <click selector="{{StorefrontProductInfoMainSection.productAttributeOptionsSelectButton}}" stepKey="clickAttributeDropDown"/> + <see userInput="option1" stepKey="verifyOption1Exists"/> + <see userInput="option2" stepKey="verifyOption2Exists"/> + </test> +</tests> diff --git a/app/code/Magento/ConfigurableProduct/Test/Mftf/Test/AdminProductTypeSwitchingOnEditingTest/AdminVirtualProductTypeSwitchingToConfigurableProductTest.xml b/app/code/Magento/ConfigurableProduct/Test/Mftf/Test/AdminProductTypeSwitchingOnEditingTest/AdminVirtualProductTypeSwitchingToConfigurableProductTest.xml new file mode 100644 index 0000000000000..90a396b970c3a --- /dev/null +++ b/app/code/Magento/ConfigurableProduct/Test/Mftf/Test/AdminProductTypeSwitchingOnEditingTest/AdminVirtualProductTypeSwitchingToConfigurableProductTest.xml @@ -0,0 +1,81 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + /** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +--> +<tests xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/testSchema.xsd"> + <test name="AdminVirtualProductTypeSwitchingToConfigurableProductTest"> + <annotations> + <features value="ConfigurableProduct"/> + <stories value="Product type switching"/> + <title value="Virtual product type switching on editing to configurable product"/> + <description value="Virtual product type switching on editing to configurable product"/> + <testCaseId value="MC-17953"/> + <useCaseId value="MAGETWO-44170"/> + <severity value="MAJOR"/> + <group value="catalog"/> + </annotations> + <before> + <actionGroup ref="AdminLoginActionGroup" stepKey="loginAsAdmin"/> + <!--Create product--> + <comment userInput="Create product" stepKey="commentCreateProduct"/> + <createData entity="VirtualProduct" stepKey="createProduct"/> + <!--Create attribute with options--> + <comment userInput="Create attribute with options" stepKey="commentCreateAttributeWithOptions"/> + <createData entity="productAttributeWithTwoOptions" stepKey="createConfigProductAttribute"/> + <createData entity="productAttributeOption1" stepKey="createConfigProductAttributeOptionOne"> + <requiredEntity createDataKey="createConfigProductAttribute"/> + </createData> + <createData entity="productAttributeOption2" stepKey="createConfigProductAttributeOptionTwo"> + <requiredEntity createDataKey="createConfigProductAttribute"/> + </createData> + </before> + <after> + <!--Delete product--> + <comment userInput="Delete product" stepKey="commentDeleteProduct"/> + <deleteData createDataKey="createProduct" stepKey="deleteProduct"/> + <deleteData createDataKey="createConfigProductAttribute" stepKey="deleteAttribute"/> + <actionGroup ref="DeleteAllDuplicateProductUsingProductGridActionGroup" stepKey="deleteAllDuplicateProducts"> + <argument name="product" value="$$createProduct$$"/> + </actionGroup> + <actionGroup ref="AdminClearFiltersActionGroup" stepKey="clearProductFilters"/> + <actionGroup ref="AdminLogoutActionGroup" stepKey="logout"/> + + <!-- Reindex invalidated indices after product attribute has been created/deleted --> + <magentoCron groups="index" stepKey="reindexInvalidatedIndices"/> + </after> + <!--Add configurations to product--> + <comment userInput="Add configurations to product" stepKey="commentAddConfigurations"/> + <actionGroup ref="AdminProductPageOpenByIdActionGroup" stepKey="gotToConfigProductPage"> + <argument name="productId" value="$$createProduct.id$$"/> + </actionGroup> + <waitForPageLoad stepKey="waitForConfigurableProductPageLoad"/> + <selectOption selector="{{AdminProductFormSection.productWeightSelect}}" userInput="This item has weight" stepKey="selectWeightForConfigurableProduct"/> + <actionGroup ref="GenerateConfigurationsByAttributeCodeActionGroup" stepKey="setupConfigurationsForProduct"> + <argument name="attributeCode" value="$$createConfigProductAttribute.attribute_code$$"/> + </actionGroup> + <actionGroup ref="SaveConfiguredProductActionGroup" stepKey="saveNewConfigurableProductForm"/> + <!--Assert configurable product on Admin product page grid--> + <comment userInput="Assert configurable product in Admin product page grid" stepKey="commentAssertConfigurableProductOnAdmin"/> + <amOnPage url="{{AdminCatalogProductPage.url}}" stepKey="goToCatalogProductPageForConfigurable"/> + <actionGroup ref="FilterProductGridBySku2ActionGroup" stepKey="filterProductGridBySkuForConfigurable"> + <argument name="sku" value="$$createProduct.sku$$"/> + </actionGroup> + <see selector="{{AdminProductGridSection.productGridCell('1', 'Name')}}" userInput="$$createProduct.name$$" stepKey="seeConfigurableProductNameInGrid"/> + <see selector="{{AdminProductGridSection.productGridCell('1', 'Type')}}" userInput="Configurable Product" stepKey="seeConfigurableProductTypeInGrid"/> + <see selector="{{AdminProductGridSection.productGridCell('2', 'Name')}}" userInput="$$createProduct.name$$-option1" stepKey="seeConfigurableProductNameInGrid1"/> + <see selector="{{AdminProductGridSection.productGridCell('3', 'Name')}}" userInput="$$createProduct.name$$-option2" stepKey="seeConfigurableProductNameInGrid2"/> + <actionGroup ref="AdminClearFiltersActionGroup" stepKey="clearConfigurableProductFilters"/> + <!--Assert configurable product on storefront--> + <comment userInput="Assert configurable product on storefront" stepKey="commentAssertConfigurableProductOnStorefront"/> + <amOnPage url="{{StorefrontProductPage.url($$createProduct.name$$)}}" stepKey="openConfigurableProductPage"/> + <waitForPageLoad stepKey="waitForStorefrontConfigurableProductPageLoad"/> + <see userInput="IN STOCK" selector="{{StorefrontProductInfoMainSection.productStockStatus}}" stepKey="assertConfigurableProductInStock"/> + <click selector="{{StorefrontProductInfoMainSection.productAttributeOptionsSelectButton}}" stepKey="clickConfigurableAttributeDropDown"/> + <see userInput="option1" stepKey="verifyConfigurableProductOption1Exists"/> + <see userInput="option2" stepKey="verifyConfigurableProductOption2Exists"/> + </test> +</tests> diff --git a/app/code/Magento/ConfigurableProduct/Test/Mftf/Test/AdminRelatedProductsTest.xml b/app/code/Magento/ConfigurableProduct/Test/Mftf/Test/AdminRelatedProductsTest.xml index 710430cf123dc..b6b3d21c8a626 100644 --- a/app/code/Magento/ConfigurableProduct/Test/Mftf/Test/AdminRelatedProductsTest.xml +++ b/app/code/Magento/ConfigurableProduct/Test/Mftf/Test/AdminRelatedProductsTest.xml @@ -78,7 +78,7 @@ <requiredEntity createDataKey="baseConfigProductHandle"/> <requiredEntity createDataKey="childProductHandle2"/> </createData> - <actionGroup ref="LoginAsAdmin" stepKey="loginAsAdmin1"/> + <actionGroup ref="AdminLoginActionGroup" stepKey="loginAsAdmin1"/> </before> <after> <amOnPage url="{{AdminLogoutPage.url}}" stepKey="amOnLogoutPage"/> @@ -89,6 +89,9 @@ <deleteData createDataKey="baseConfigProductHandle" stepKey="deleteConfig"/> <deleteData createDataKey="categoryHandle" stepKey="deleteCategory"/> <deleteData createDataKey="productAttributeHandle" stepKey="deleteProductAttribute"/> + + <!-- Reindex invalidated indices after product attribute has been created/deleted --> + <magentoCron groups="index" stepKey="reindexInvalidatedIndices"/> </after> <comment userInput="Filter and edit simple product 1" stepKey="filterAndEditComment1"/> diff --git a/app/code/Magento/ConfigurableProduct/Test/Mftf/Test/AdminRemoveDefaultImageConfigurableTest.xml b/app/code/Magento/ConfigurableProduct/Test/Mftf/Test/AdminRemoveDefaultImageConfigurableTest.xml index 0cc73f117aaad..86d4070a9a2c8 100644 --- a/app/code/Magento/ConfigurableProduct/Test/Mftf/Test/AdminRemoveDefaultImageConfigurableTest.xml +++ b/app/code/Magento/ConfigurableProduct/Test/Mftf/Test/AdminRemoveDefaultImageConfigurableTest.xml @@ -20,7 +20,7 @@ </annotations> <before> - <actionGroup ref="LoginAsAdmin" stepKey="loginAsAdmin"/> + <actionGroup ref="AdminLoginActionGroup" stepKey="loginAsAdmin"/> <createData entity="SimpleSubCategory" stepKey="categoryHandle"/> <createData entity="SimpleProduct" stepKey="simple1Handle"> @@ -89,6 +89,8 @@ <deleteData createDataKey="baseConfigProductHandle" stepKey="deleteConfig"/> <deleteData createDataKey="categoryHandle" stepKey="deleteCategory"/> <deleteData createDataKey="productAttributeHandle" stepKey="deleteProductAttribute"/> + <!-- Reindex invalidated indices after product attribute has been created/deleted --> + <magentoCron groups="index" stepKey="reindexInvalidatedIndices"/> </after> <amOnPage url="{{AdminProductIndexPage.url}}" stepKey="productIndexPage"/> diff --git a/app/code/Magento/ConfigurableProduct/Test/Mftf/Test/AdvanceCatalogSearchConfigurableTest.xml b/app/code/Magento/ConfigurableProduct/Test/Mftf/Test/AdvanceCatalogSearchConfigurableTest.xml deleted file mode 100644 index 0370280309272..0000000000000 --- a/app/code/Magento/ConfigurableProduct/Test/Mftf/Test/AdvanceCatalogSearchConfigurableTest.xml +++ /dev/null @@ -1,315 +0,0 @@ -<?xml version="1.0" encoding="UTF-8"?> -<!-- - /** - * Copyright © Magento, Inc. All rights reserved. - * See COPYING.txt for license details. - */ ---> - -<tests xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" - xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/testSchema.xsd"> - <test name="AdvanceCatalogSearchConfigurableByNameTest" extends="AdvanceCatalogSearchSimpleProductByNameTest"> - <annotations> - <features value="ConfigurableProduct"/> - <stories value="Advanced Catalog Product Search for all product types"/> - <title value="Guest customer should be able to advance search configurable product with product name"/> - <description value="Guest customer should be able to advance search configurable product with product name"/> - <severity value="MAJOR"/> - <testCaseId value="MC-138"/> - <group value="ConfigurableProduct"/> - </annotations> - <before> - <createData entity="SimpleSubCategory" stepKey="categoryHandle" before="simple1Handle"/> - - <createData entity="SimpleProduct" stepKey="simple1Handle" before="simple2Handle"> - <requiredEntity createDataKey="categoryHandle"/> - </createData> - - <createData entity="SimpleProduct" stepKey="simple2Handle" before="product"> - <requiredEntity createDataKey="categoryHandle"/> - </createData> - - <!-- TODO: Move configurable product creation to an actionGroup when MQE-697 is fixed --> - <createData entity="ApiConfigurableProductWithDescription" stepKey="product"/> - - <createData entity="productDropDownAttribute" stepKey="productAttributeHandle"/> - - <createData entity="productAttributeOption1" stepKey="productAttributeOption1Handle"> - <requiredEntity createDataKey="productAttributeHandle"/> - </createData> - <createData entity="productAttributeOption2" stepKey="productAttributeOption2Handle"> - <requiredEntity createDataKey="productAttributeHandle"/> - </createData> - - <createData entity="AddToDefaultSet" stepKey="addToAttributeSetHandle"> - <requiredEntity createDataKey="productAttributeHandle"/> - </createData> - - <getData entity="ProductAttributeOptionGetter" index="1" stepKey="getAttributeOption1Handle"> - <requiredEntity createDataKey="productAttributeHandle"/> - </getData> - <getData entity="ProductAttributeOptionGetter" index="2" stepKey="getAttributeOption2Handle"> - <requiredEntity createDataKey="productAttributeHandle"/> - </getData> - - <createData entity="SimpleOne" stepKey="childProductHandle1"> - <requiredEntity createDataKey="productAttributeHandle"/> - <requiredEntity createDataKey="getAttributeOption1Handle"/> - </createData> - <createData entity="SimpleOne" stepKey="childProductHandle2"> - <requiredEntity createDataKey="productAttributeHandle"/> - <requiredEntity createDataKey="getAttributeOption2Handle"/> - </createData> - - <createData entity="ConfigurableProductTwoOptions" stepKey="configProductOptionHandle"> - <requiredEntity createDataKey="product"/> - <requiredEntity createDataKey="productAttributeHandle"/> - <requiredEntity createDataKey="getAttributeOption1Handle"/> - <requiredEntity createDataKey="getAttributeOption2Handle"/> - </createData> - - <createData entity="ConfigurableProductAddChild" stepKey="configProductHandle1"> - <requiredEntity createDataKey="product"/> - <requiredEntity createDataKey="childProductHandle1"/> - </createData> - <createData entity="ConfigurableProductAddChild" stepKey="configProductHandle2"> - <requiredEntity createDataKey="product"/> - <requiredEntity createDataKey="childProductHandle2"/> - </createData> - </before> - <after> - <deleteData createDataKey="simple1Handle" stepKey="deleteSimple1" before="deleteSimple2"/> - <deleteData createDataKey="simple2Handle" stepKey="deleteSimple2" before="delete"/> - <deleteData createDataKey="childProductHandle1" stepKey="deleteChildProduct1" before="delete"/> - <deleteData createDataKey="childProductHandle2" stepKey="deleteChildProduct2" before="delete"/> - </after> - </test> - <test name="AdvanceCatalogSearchConfigurableBySkuTest" extends="AdvanceCatalogSearchSimpleProductBySkuTest"> - <annotations> - <features value="ConfigurableProduct"/> - <stories value="Advanced Catalog Product Search for all product types"/> - <title value="Guest customer should be able to advance search configurable product with product sku"/> - <description value="Guest customer should be able to advance search configurable product with product sku"/> - <severity value="MAJOR"/> - <testCaseId value="MC-144"/> - <group value="ConfigurableProduct"/> - </annotations> - <before> - <createData entity="SimpleSubCategory" stepKey="categoryHandle" before="simple1Handle"/> - - <createData entity="SimpleProduct" stepKey="simple1Handle" before="simple2Handle"> - <requiredEntity createDataKey="categoryHandle"/> - </createData> - - <createData entity="SimpleProduct" stepKey="simple2Handle" before="product"> - <requiredEntity createDataKey="categoryHandle"/> - </createData> - - <!-- TODO: Move configurable product creation to an actionGroup when MQE-697 is fixed --> - <createData entity="ApiConfigurableProductWithDescriptionUnderscoredSku" stepKey="product"/> - - <createData entity="productDropDownAttribute" stepKey="productAttributeHandle"/> - - <createData entity="productAttributeOption1" stepKey="productAttributeOption1Handle"> - <requiredEntity createDataKey="productAttributeHandle"/> - </createData> - <createData entity="productAttributeOption2" stepKey="productAttributeOption2Handle"> - <requiredEntity createDataKey="productAttributeHandle"/> - </createData> - - <createData entity="AddToDefaultSet" stepKey="addToAttributeSetHandle"> - <requiredEntity createDataKey="productAttributeHandle"/> - </createData> - - <getData entity="ProductAttributeOptionGetter" index="1" stepKey="getAttributeOption1Handle"> - <requiredEntity createDataKey="productAttributeHandle"/> - </getData> - <getData entity="ProductAttributeOptionGetter" index="2" stepKey="getAttributeOption2Handle"> - <requiredEntity createDataKey="productAttributeHandle"/> - </getData> - - <createData entity="SimpleOne" stepKey="childProductHandle1"> - <requiredEntity createDataKey="productAttributeHandle"/> - <requiredEntity createDataKey="getAttributeOption1Handle"/> - </createData> - <createData entity="SimpleOne" stepKey="childProductHandle2"> - <requiredEntity createDataKey="productAttributeHandle"/> - <requiredEntity createDataKey="getAttributeOption2Handle"/> - </createData> - - <createData entity="ConfigurableProductTwoOptions" stepKey="configProductOptionHandle"> - <requiredEntity createDataKey="product"/> - <requiredEntity createDataKey="productAttributeHandle"/> - <requiredEntity createDataKey="getAttributeOption1Handle"/> - <requiredEntity createDataKey="getAttributeOption2Handle"/> - </createData> - - <createData entity="ConfigurableProductAddChild" stepKey="configProductHandle1"> - <requiredEntity createDataKey="product"/> - <requiredEntity createDataKey="childProductHandle1"/> - </createData> - <createData entity="ConfigurableProductAddChild" stepKey="configProductHandle2"> - <requiredEntity createDataKey="product"/> - <requiredEntity createDataKey="childProductHandle2"/> - </createData> - </before> - <after> - <deleteData createDataKey="simple1Handle" stepKey="deleteSimple1" before="deleteSimple2"/> - <deleteData createDataKey="simple2Handle" stepKey="deleteSimple2" before="delete"/> - <deleteData createDataKey="childProductHandle1" stepKey="deleteChildProduct1" before="delete"/> - <deleteData createDataKey="childProductHandle2" stepKey="deleteChildProduct2" before="delete"/> - </after> - </test> - <test name="AdvanceCatalogSearchConfigurableByDescriptionTest" extends="AdvanceCatalogSearchSimpleProductByDescriptionTest"> - <annotations> - <features value="ConfigurableProduct"/> - <stories value="Advanced Catalog Product Search for all product types"/> - <title value="Guest customer should be able to advance search configurable product with product description"/> - <description value="Guest customer should be able to advance search configurable product with product description"/> - <severity value="MAJOR"/> - <testCaseId value="MC-237"/> - <group value="ConfigurableProduct"/> - </annotations> - <before> - <createData entity="SimpleSubCategory" stepKey="categoryHandle" before="simple1Handle"/> - - <createData entity="SimpleProduct" stepKey="simple1Handle" before="simple2Handle"> - <requiredEntity createDataKey="categoryHandle"/> - </createData> - - <createData entity="SimpleProduct" stepKey="simple2Handle" before="product"> - <requiredEntity createDataKey="categoryHandle"/> - </createData> - - <!-- TODO: Move configurable product creation to an actionGroup when MQE-697 is fixed --> - <createData entity="ApiConfigurableProductWithDescription" stepKey="product"/> - - <createData entity="productDropDownAttribute" stepKey="productAttributeHandle"/> - - <createData entity="productAttributeOption1" stepKey="productAttributeOption1Handle"> - <requiredEntity createDataKey="productAttributeHandle"/> - </createData> - <createData entity="productAttributeOption2" stepKey="productAttributeOption2Handle"> - <requiredEntity createDataKey="productAttributeHandle"/> - </createData> - - <createData entity="AddToDefaultSet" stepKey="addToAttributeSetHandle"> - <requiredEntity createDataKey="productAttributeHandle"/> - </createData> - - <getData entity="ProductAttributeOptionGetter" index="1" stepKey="getAttributeOption1Handle"> - <requiredEntity createDataKey="productAttributeHandle"/> - </getData> - <getData entity="ProductAttributeOptionGetter" index="2" stepKey="getAttributeOption2Handle"> - <requiredEntity createDataKey="productAttributeHandle"/> - </getData> - - <createData entity="SimpleOne" stepKey="childProductHandle1"> - <requiredEntity createDataKey="productAttributeHandle"/> - <requiredEntity createDataKey="getAttributeOption1Handle"/> - </createData> - <createData entity="SimpleOne" stepKey="childProductHandle2"> - <requiredEntity createDataKey="productAttributeHandle"/> - <requiredEntity createDataKey="getAttributeOption2Handle"/> - </createData> - - <createData entity="ConfigurableProductTwoOptions" stepKey="configProductOptionHandle"> - <requiredEntity createDataKey="product"/> - <requiredEntity createDataKey="productAttributeHandle"/> - <requiredEntity createDataKey="getAttributeOption1Handle"/> - <requiredEntity createDataKey="getAttributeOption2Handle"/> - </createData> - - <createData entity="ConfigurableProductAddChild" stepKey="configProductHandle1"> - <requiredEntity createDataKey="product"/> - <requiredEntity createDataKey="childProductHandle1"/> - </createData> - <createData entity="ConfigurableProductAddChild" stepKey="configProductHandle2"> - <requiredEntity createDataKey="product"/> - <requiredEntity createDataKey="childProductHandle2"/> - </createData> - </before> - <after> - <deleteData createDataKey="simple1Handle" stepKey="deleteSimple1" before="deleteSimple2"/> - <deleteData createDataKey="simple2Handle" stepKey="deleteSimple2" before="delete"/> - <deleteData createDataKey="childProductHandle1" stepKey="deleteChildProduct1" before="delete"/> - <deleteData createDataKey="childProductHandle2" stepKey="deleteChildProduct2" before="delete"/> - </after> - </test> - <test name="AdvanceCatalogSearchConfigurableByShortDescriptionTest" extends="AdvanceCatalogSearchSimpleProductByShortDescriptionTest"> - <annotations> - <features value="ConfigurableProduct"/> - <stories value="Advanced Catalog Product Search for all product types"/> - <title value="Guest customer should be able to advance search configurable product with product short description"/> - <description value="Guest customer should be able to advance search configurable product with product short description"/> - <severity value="MAJOR"/> - <testCaseId value="MC-240"/> - <group value="ConfigurableProduct"/> - </annotations> - <before> - <createData entity="SimpleSubCategory" stepKey="categoryHandle" before="simple1Handle"/> - - <createData entity="SimpleProduct" stepKey="simple1Handle" before="simple2Handle"> - <requiredEntity createDataKey="categoryHandle"/> - </createData> - - <createData entity="SimpleProduct" stepKey="simple2Handle" before="product"> - <requiredEntity createDataKey="categoryHandle"/> - </createData> - - <!-- TODO: Move configurable product creation to an actionGroup when MQE-697 is fixed --> - <createData entity="ApiConfigurableProductWithDescription" stepKey="product"/> - - <createData entity="productDropDownAttribute" stepKey="productAttributeHandle"/> - - <createData entity="productAttributeOption1" stepKey="productAttributeOption1Handle"> - <requiredEntity createDataKey="productAttributeHandle"/> - </createData> - <createData entity="productAttributeOption2" stepKey="productAttributeOption2Handle"> - <requiredEntity createDataKey="productAttributeHandle"/> - </createData> - - <createData entity="AddToDefaultSet" stepKey="addToAttributeSetHandle"> - <requiredEntity createDataKey="productAttributeHandle"/> - </createData> - - <getData entity="ProductAttributeOptionGetter" index="1" stepKey="getAttributeOption1Handle"> - <requiredEntity createDataKey="productAttributeHandle"/> - </getData> - <getData entity="ProductAttributeOptionGetter" index="2" stepKey="getAttributeOption2Handle"> - <requiredEntity createDataKey="productAttributeHandle"/> - </getData> - - <createData entity="SimpleOne" stepKey="childProductHandle1"> - <requiredEntity createDataKey="productAttributeHandle"/> - <requiredEntity createDataKey="getAttributeOption1Handle"/> - </createData> - <createData entity="SimpleOne" stepKey="childProductHandle2"> - <requiredEntity createDataKey="productAttributeHandle"/> - <requiredEntity createDataKey="getAttributeOption2Handle"/> - </createData> - - <createData entity="ConfigurableProductTwoOptions" stepKey="configProductOptionHandle"> - <requiredEntity createDataKey="product"/> - <requiredEntity createDataKey="productAttributeHandle"/> - <requiredEntity createDataKey="getAttributeOption1Handle"/> - <requiredEntity createDataKey="getAttributeOption2Handle"/> - </createData> - - <createData entity="ConfigurableProductAddChild" stepKey="configProductHandle1"> - <requiredEntity createDataKey="product"/> - <requiredEntity createDataKey="childProductHandle1"/> - </createData> - <createData entity="ConfigurableProductAddChild" stepKey="configProductHandle2"> - <requiredEntity createDataKey="product"/> - <requiredEntity createDataKey="childProductHandle2"/> - </createData> - </before> - <after> - <deleteData createDataKey="simple1Handle" stepKey="deleteSimple1" before="deleteSimple2"/> - <deleteData createDataKey="simple2Handle" stepKey="deleteSimple2" before="delete"/> - <deleteData createDataKey="childProductHandle1" stepKey="deleteChildProduct1" before="delete"/> - <deleteData createDataKey="childProductHandle2" stepKey="deleteChildProduct2" before="delete"/> - </after> - </test> -</tests> diff --git a/app/code/Magento/ConfigurableProduct/Test/Mftf/Test/AdvanceCatalogSearchConfigurableTest/AdvanceCatalogSearchConfigurableByDescriptionTest.xml b/app/code/Magento/ConfigurableProduct/Test/Mftf/Test/AdvanceCatalogSearchConfigurableTest/AdvanceCatalogSearchConfigurableByDescriptionTest.xml new file mode 100644 index 0000000000000..04f4fc65c805a --- /dev/null +++ b/app/code/Magento/ConfigurableProduct/Test/Mftf/Test/AdvanceCatalogSearchConfigurableTest/AdvanceCatalogSearchConfigurableByDescriptionTest.xml @@ -0,0 +1,90 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + /** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +--> +<tests xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/testSchema.xsd"> + <test name="AdvanceCatalogSearchConfigurableByDescriptionTest" extends="AdvanceCatalogSearchSimpleProductByDescriptionTest"> + <annotations> + <features value="ConfigurableProduct"/> + <stories value="Advanced Catalog Product Search for all product types"/> + <title value="Guest customer should be able to advance search configurable product with product description"/> + <description value="Guest customer should be able to advance search configurable product with product description"/> + <severity value="MAJOR"/> + <testCaseId value="MC-237"/> + <group value="ConfigurableProduct"/> + </annotations> + <before> + <createData entity="SimpleSubCategory" stepKey="categoryHandle" before="simple1Handle"/> + + <createData entity="SimpleProduct" stepKey="simple1Handle" before="simple2Handle"> + <requiredEntity createDataKey="categoryHandle"/> + </createData> + + <createData entity="SimpleProduct" stepKey="simple2Handle" before="product"> + <requiredEntity createDataKey="categoryHandle"/> + </createData> + + <!-- TODO: Move configurable product creation to an actionGroup when MQE-697 is fixed --> + <createData entity="ApiConfigurableProductWithDescription" stepKey="product"/> + + <createData entity="productDropDownAttribute" stepKey="productAttributeHandle"/> + + <createData entity="productAttributeOption1" stepKey="productAttributeOption1Handle"> + <requiredEntity createDataKey="productAttributeHandle"/> + </createData> + <createData entity="productAttributeOption2" stepKey="productAttributeOption2Handle"> + <requiredEntity createDataKey="productAttributeHandle"/> + </createData> + + <createData entity="AddToDefaultSet" stepKey="addToAttributeSetHandle"> + <requiredEntity createDataKey="productAttributeHandle"/> + </createData> + + <getData entity="ProductAttributeOptionGetter" index="1" stepKey="getAttributeOption1Handle"> + <requiredEntity createDataKey="productAttributeHandle"/> + </getData> + <getData entity="ProductAttributeOptionGetter" index="2" stepKey="getAttributeOption2Handle"> + <requiredEntity createDataKey="productAttributeHandle"/> + </getData> + + <createData entity="SimpleOne" stepKey="childProductHandle1"> + <requiredEntity createDataKey="productAttributeHandle"/> + <requiredEntity createDataKey="getAttributeOption1Handle"/> + </createData> + <createData entity="SimpleOne" stepKey="childProductHandle2"> + <requiredEntity createDataKey="productAttributeHandle"/> + <requiredEntity createDataKey="getAttributeOption2Handle"/> + </createData> + + <createData entity="ConfigurableProductTwoOptions" stepKey="configProductOptionHandle"> + <requiredEntity createDataKey="product"/> + <requiredEntity createDataKey="productAttributeHandle"/> + <requiredEntity createDataKey="getAttributeOption1Handle"/> + <requiredEntity createDataKey="getAttributeOption2Handle"/> + </createData> + + <createData entity="ConfigurableProductAddChild" stepKey="configProductHandle1"> + <requiredEntity createDataKey="product"/> + <requiredEntity createDataKey="childProductHandle1"/> + </createData> + <createData entity="ConfigurableProductAddChild" stepKey="configProductHandle2"> + <requiredEntity createDataKey="product"/> + <requiredEntity createDataKey="childProductHandle2"/> + </createData> + </before> + <after> + <deleteData createDataKey="simple1Handle" stepKey="deleteSimple1" before="deleteSimple2"/> + <deleteData createDataKey="simple2Handle" stepKey="deleteSimple2" before="delete"/> + <deleteData createDataKey="childProductHandle1" stepKey="deleteChildProduct1" before="delete"/> + <deleteData createDataKey="childProductHandle2" stepKey="deleteChildProduct2" before="delete"/> + <deleteData createDataKey="productAttributeHandle" stepKey="deleteProductDropDownAttribute"/> + + <!-- Reindex invalidated indices after product attribute has been created/deleted --> + <magentoCron groups="index" stepKey="reindexInvalidatedIndices"/> + </after> + </test> +</tests> diff --git a/app/code/Magento/ConfigurableProduct/Test/Mftf/Test/AdvanceCatalogSearchConfigurableTest/AdvanceCatalogSearchConfigurableByNameTest.xml b/app/code/Magento/ConfigurableProduct/Test/Mftf/Test/AdvanceCatalogSearchConfigurableTest/AdvanceCatalogSearchConfigurableByNameTest.xml new file mode 100644 index 0000000000000..739b8e83d770f --- /dev/null +++ b/app/code/Magento/ConfigurableProduct/Test/Mftf/Test/AdvanceCatalogSearchConfigurableTest/AdvanceCatalogSearchConfigurableByNameTest.xml @@ -0,0 +1,90 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + /** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +--> +<tests xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/testSchema.xsd"> + <test name="AdvanceCatalogSearchConfigurableByNameTest" extends="AdvanceCatalogSearchSimpleProductByNameTest"> + <annotations> + <features value="ConfigurableProduct"/> + <stories value="Advanced Catalog Product Search for all product types"/> + <title value="Guest customer should be able to advance search configurable product with product name"/> + <description value="Guest customer should be able to advance search configurable product with product name"/> + <severity value="MAJOR"/> + <testCaseId value="MC-138"/> + <group value="ConfigurableProduct"/> + </annotations> + <before> + <createData entity="SimpleSubCategory" stepKey="categoryHandle" before="simple1Handle"/> + + <createData entity="SimpleProduct" stepKey="simple1Handle" before="simple2Handle"> + <requiredEntity createDataKey="categoryHandle"/> + </createData> + + <createData entity="SimpleProduct" stepKey="simple2Handle" before="product"> + <requiredEntity createDataKey="categoryHandle"/> + </createData> + + <!-- TODO: Move configurable product creation to an actionGroup when MQE-697 is fixed --> + <createData entity="ApiConfigurableProductWithDescription" stepKey="product"/> + + <createData entity="productDropDownAttribute" stepKey="productAttributeHandle"/> + + <createData entity="productAttributeOption1" stepKey="productAttributeOption1Handle"> + <requiredEntity createDataKey="productAttributeHandle"/> + </createData> + <createData entity="productAttributeOption2" stepKey="productAttributeOption2Handle"> + <requiredEntity createDataKey="productAttributeHandle"/> + </createData> + + <createData entity="AddToDefaultSet" stepKey="addToAttributeSetHandle"> + <requiredEntity createDataKey="productAttributeHandle"/> + </createData> + + <getData entity="ProductAttributeOptionGetter" index="1" stepKey="getAttributeOption1Handle"> + <requiredEntity createDataKey="productAttributeHandle"/> + </getData> + <getData entity="ProductAttributeOptionGetter" index="2" stepKey="getAttributeOption2Handle"> + <requiredEntity createDataKey="productAttributeHandle"/> + </getData> + + <createData entity="SimpleOne" stepKey="childProductHandle1"> + <requiredEntity createDataKey="productAttributeHandle"/> + <requiredEntity createDataKey="getAttributeOption1Handle"/> + </createData> + <createData entity="SimpleOne" stepKey="childProductHandle2"> + <requiredEntity createDataKey="productAttributeHandle"/> + <requiredEntity createDataKey="getAttributeOption2Handle"/> + </createData> + + <createData entity="ConfigurableProductTwoOptions" stepKey="configProductOptionHandle"> + <requiredEntity createDataKey="product"/> + <requiredEntity createDataKey="productAttributeHandle"/> + <requiredEntity createDataKey="getAttributeOption1Handle"/> + <requiredEntity createDataKey="getAttributeOption2Handle"/> + </createData> + + <createData entity="ConfigurableProductAddChild" stepKey="configProductHandle1"> + <requiredEntity createDataKey="product"/> + <requiredEntity createDataKey="childProductHandle1"/> + </createData> + <createData entity="ConfigurableProductAddChild" stepKey="configProductHandle2"> + <requiredEntity createDataKey="product"/> + <requiredEntity createDataKey="childProductHandle2"/> + </createData> + </before> + <after> + <deleteData createDataKey="simple1Handle" stepKey="deleteSimple1" before="deleteSimple2"/> + <deleteData createDataKey="simple2Handle" stepKey="deleteSimple2" before="delete"/> + <deleteData createDataKey="childProductHandle1" stepKey="deleteChildProduct1" before="delete"/> + <deleteData createDataKey="childProductHandle2" stepKey="deleteChildProduct2" before="delete"/> + <deleteData createDataKey="productAttributeHandle" stepKey="deleteProductDropDownAttribute"/> + + <!-- Reindex invalidated indices after product attribute has been created/deleted --> + <magentoCron groups="index" stepKey="reindexInvalidatedIndices"/> + </after> + </test> +</tests> diff --git a/app/code/Magento/ConfigurableProduct/Test/Mftf/Test/AdvanceCatalogSearchConfigurableTest/AdvanceCatalogSearchConfigurableByShortDescriptionTest.xml b/app/code/Magento/ConfigurableProduct/Test/Mftf/Test/AdvanceCatalogSearchConfigurableTest/AdvanceCatalogSearchConfigurableByShortDescriptionTest.xml new file mode 100644 index 0000000000000..c4595158e55d3 --- /dev/null +++ b/app/code/Magento/ConfigurableProduct/Test/Mftf/Test/AdvanceCatalogSearchConfigurableTest/AdvanceCatalogSearchConfigurableByShortDescriptionTest.xml @@ -0,0 +1,90 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + /** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +--> +<tests xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/testSchema.xsd"> + <test name="AdvanceCatalogSearchConfigurableByShortDescriptionTest" extends="AdvanceCatalogSearchSimpleProductByShortDescriptionTest"> + <annotations> + <features value="ConfigurableProduct"/> + <stories value="Advanced Catalog Product Search for all product types"/> + <title value="Guest customer should be able to advance search configurable product with product short description"/> + <description value="Guest customer should be able to advance search configurable product with product short description"/> + <severity value="MAJOR"/> + <testCaseId value="MC-240"/> + <group value="ConfigurableProduct"/> + </annotations> + <before> + <createData entity="SimpleSubCategory" stepKey="categoryHandle" before="simple1Handle"/> + + <createData entity="SimpleProduct" stepKey="simple1Handle" before="simple2Handle"> + <requiredEntity createDataKey="categoryHandle"/> + </createData> + + <createData entity="SimpleProduct" stepKey="simple2Handle" before="product"> + <requiredEntity createDataKey="categoryHandle"/> + </createData> + + <!-- TODO: Move configurable product creation to an actionGroup when MQE-697 is fixed --> + <createData entity="ApiConfigurableProductWithDescription" stepKey="product"/> + + <createData entity="productDropDownAttribute" stepKey="productAttributeHandle"/> + + <createData entity="productAttributeOption1" stepKey="productAttributeOption1Handle"> + <requiredEntity createDataKey="productAttributeHandle"/> + </createData> + <createData entity="productAttributeOption2" stepKey="productAttributeOption2Handle"> + <requiredEntity createDataKey="productAttributeHandle"/> + </createData> + + <createData entity="AddToDefaultSet" stepKey="addToAttributeSetHandle"> + <requiredEntity createDataKey="productAttributeHandle"/> + </createData> + + <getData entity="ProductAttributeOptionGetter" index="1" stepKey="getAttributeOption1Handle"> + <requiredEntity createDataKey="productAttributeHandle"/> + </getData> + <getData entity="ProductAttributeOptionGetter" index="2" stepKey="getAttributeOption2Handle"> + <requiredEntity createDataKey="productAttributeHandle"/> + </getData> + + <createData entity="SimpleOne" stepKey="childProductHandle1"> + <requiredEntity createDataKey="productAttributeHandle"/> + <requiredEntity createDataKey="getAttributeOption1Handle"/> + </createData> + <createData entity="SimpleOne" stepKey="childProductHandle2"> + <requiredEntity createDataKey="productAttributeHandle"/> + <requiredEntity createDataKey="getAttributeOption2Handle"/> + </createData> + + <createData entity="ConfigurableProductTwoOptions" stepKey="configProductOptionHandle"> + <requiredEntity createDataKey="product"/> + <requiredEntity createDataKey="productAttributeHandle"/> + <requiredEntity createDataKey="getAttributeOption1Handle"/> + <requiredEntity createDataKey="getAttributeOption2Handle"/> + </createData> + + <createData entity="ConfigurableProductAddChild" stepKey="configProductHandle1"> + <requiredEntity createDataKey="product"/> + <requiredEntity createDataKey="childProductHandle1"/> + </createData> + <createData entity="ConfigurableProductAddChild" stepKey="configProductHandle2"> + <requiredEntity createDataKey="product"/> + <requiredEntity createDataKey="childProductHandle2"/> + </createData> + </before> + <after> + <deleteData createDataKey="simple1Handle" stepKey="deleteSimple1" before="deleteSimple2"/> + <deleteData createDataKey="simple2Handle" stepKey="deleteSimple2" before="delete"/> + <deleteData createDataKey="childProductHandle1" stepKey="deleteChildProduct1" before="delete"/> + <deleteData createDataKey="childProductHandle2" stepKey="deleteChildProduct2" before="delete"/> + <deleteData createDataKey="productAttributeHandle" stepKey="deleteProductDropDownAttribute"/> + + <!-- Reindex invalidated indices after product attribute has been created/deleted --> + <magentoCron groups="index" stepKey="reindexInvalidatedIndices"/> + </after> + </test> +</tests> diff --git a/app/code/Magento/ConfigurableProduct/Test/Mftf/Test/AdvanceCatalogSearchConfigurableTest/AdvanceCatalogSearchConfigurableBySkuTest.xml b/app/code/Magento/ConfigurableProduct/Test/Mftf/Test/AdvanceCatalogSearchConfigurableTest/AdvanceCatalogSearchConfigurableBySkuTest.xml new file mode 100644 index 0000000000000..34c0fcd634373 --- /dev/null +++ b/app/code/Magento/ConfigurableProduct/Test/Mftf/Test/AdvanceCatalogSearchConfigurableTest/AdvanceCatalogSearchConfigurableBySkuTest.xml @@ -0,0 +1,90 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + /** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +--> +<tests xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/testSchema.xsd"> + <test name="AdvanceCatalogSearchConfigurableBySkuTest" extends="AdvanceCatalogSearchSimpleProductBySkuTest"> + <annotations> + <features value="ConfigurableProduct"/> + <stories value="Advanced Catalog Product Search for all product types"/> + <title value="Guest customer should be able to advance search configurable product with product sku"/> + <description value="Guest customer should be able to advance search configurable product with product sku"/> + <severity value="MAJOR"/> + <testCaseId value="MC-144"/> + <group value="ConfigurableProduct"/> + </annotations> + <before> + <createData entity="SimpleSubCategory" stepKey="categoryHandle" before="simple1Handle"/> + + <createData entity="SimpleProduct" stepKey="simple1Handle" before="simple2Handle"> + <requiredEntity createDataKey="categoryHandle"/> + </createData> + + <createData entity="SimpleProduct" stepKey="simple2Handle" before="product"> + <requiredEntity createDataKey="categoryHandle"/> + </createData> + + <!-- TODO: Move configurable product creation to an actionGroup when MQE-697 is fixed --> + <createData entity="ApiConfigurableProductWithDescriptionUnderscoredSku" stepKey="product"/> + + <createData entity="productDropDownAttribute" stepKey="productAttributeHandle"/> + + <createData entity="productAttributeOption1" stepKey="productAttributeOption1Handle"> + <requiredEntity createDataKey="productAttributeHandle"/> + </createData> + <createData entity="productAttributeOption2" stepKey="productAttributeOption2Handle"> + <requiredEntity createDataKey="productAttributeHandle"/> + </createData> + + <createData entity="AddToDefaultSet" stepKey="addToAttributeSetHandle"> + <requiredEntity createDataKey="productAttributeHandle"/> + </createData> + + <getData entity="ProductAttributeOptionGetter" index="1" stepKey="getAttributeOption1Handle"> + <requiredEntity createDataKey="productAttributeHandle"/> + </getData> + <getData entity="ProductAttributeOptionGetter" index="2" stepKey="getAttributeOption2Handle"> + <requiredEntity createDataKey="productAttributeHandle"/> + </getData> + + <createData entity="SimpleOne" stepKey="childProductHandle1"> + <requiredEntity createDataKey="productAttributeHandle"/> + <requiredEntity createDataKey="getAttributeOption1Handle"/> + </createData> + <createData entity="SimpleOne" stepKey="childProductHandle2"> + <requiredEntity createDataKey="productAttributeHandle"/> + <requiredEntity createDataKey="getAttributeOption2Handle"/> + </createData> + + <createData entity="ConfigurableProductTwoOptions" stepKey="configProductOptionHandle"> + <requiredEntity createDataKey="product"/> + <requiredEntity createDataKey="productAttributeHandle"/> + <requiredEntity createDataKey="getAttributeOption1Handle"/> + <requiredEntity createDataKey="getAttributeOption2Handle"/> + </createData> + + <createData entity="ConfigurableProductAddChild" stepKey="configProductHandle1"> + <requiredEntity createDataKey="product"/> + <requiredEntity createDataKey="childProductHandle1"/> + </createData> + <createData entity="ConfigurableProductAddChild" stepKey="configProductHandle2"> + <requiredEntity createDataKey="product"/> + <requiredEntity createDataKey="childProductHandle2"/> + </createData> + </before> + <after> + <deleteData createDataKey="simple1Handle" stepKey="deleteSimple1" before="deleteSimple2"/> + <deleteData createDataKey="simple2Handle" stepKey="deleteSimple2" before="delete"/> + <deleteData createDataKey="childProductHandle1" stepKey="deleteChildProduct1" before="delete"/> + <deleteData createDataKey="childProductHandle2" stepKey="deleteChildProduct2" before="delete"/> + <deleteData createDataKey="productAttributeHandle" stepKey="deleteProductDropDownAttribute"/> + + <!-- Reindex invalidated indices after product attribute has been created/deleted --> + <magentoCron groups="index" stepKey="reindexInvalidatedIndices"/> + </after> + </test> +</tests> diff --git a/app/code/Magento/ConfigurableProduct/Test/Mftf/Test/ConfigurableProductAttributeNameDesignTest.xml b/app/code/Magento/ConfigurableProduct/Test/Mftf/Test/ConfigurableProductAttributeNameDesignTest.xml index bb16d04dfc94a..e064e635de1cd 100644 --- a/app/code/Magento/ConfigurableProduct/Test/Mftf/Test/ConfigurableProductAttributeNameDesignTest.xml +++ b/app/code/Magento/ConfigurableProduct/Test/Mftf/Test/ConfigurableProductAttributeNameDesignTest.xml @@ -21,7 +21,7 @@ <before> <!-- Log in to Dashboard page --> - <actionGroup ref="LoginAsAdmin" stepKey="loginAsAdmin1"/> + <actionGroup ref="AdminLoginActionGroup" stepKey="loginAsAdmin1"/> </before> diff --git a/app/code/Magento/ConfigurableProduct/Test/Mftf/Test/ConfigurableProductPriceAdditionalStoreViewTest.xml b/app/code/Magento/ConfigurableProduct/Test/Mftf/Test/ConfigurableProductPriceAdditionalStoreViewTest.xml index 0b078bc7d2a98..a34dfd06ce844 100644 --- a/app/code/Magento/ConfigurableProduct/Test/Mftf/Test/ConfigurableProductPriceAdditionalStoreViewTest.xml +++ b/app/code/Magento/ConfigurableProduct/Test/Mftf/Test/ConfigurableProductPriceAdditionalStoreViewTest.xml @@ -64,7 +64,7 @@ <requiredEntity createDataKey="createConfigProduct"/> <requiredEntity createDataKey="createConfigChildProduct2"/> </createData> - <actionGroup ref="LoginAsAdmin" stepKey="loginAsAdmin1"/> + <actionGroup ref="AdminLoginActionGroup" stepKey="loginAsAdmin1"/> </before> <after> @@ -77,7 +77,9 @@ <actionGroup ref="AdminDeleteWebsiteActionGroup" stepKey="deleteSecondWebsite"> <argument name="websiteName" value="Second Website"/> </actionGroup> - <actionGroup ref="logout" stepKey="adminLogout"/> + <actionGroup ref="AdminLogoutActionGroup" stepKey="adminLogout"/> + <!-- Reindex invalidated indices after product attribute has been created/deleted --> + <magentoCron groups="index" stepKey="reindexInvalidatedIndices"/> </after> <actionGroup ref="EnableWebUrlOptionsActionGroup" stepKey="addStoreCodeToUrls"/> @@ -106,7 +108,9 @@ <see userInput="You saved the store view." stepKey="seeSaveMessage" /> <!--go to admin and open product edit page to disable product all store view --> - <amOnPage url="{{AdminProductEditPage.url($$createConfigProduct.id$$)}}" stepKey="goToProductEditPage"/> + <actionGroup ref="AdminProductPageOpenByIdActionGroup" stepKey="goToProductEditPage"> + <argument name="productId" value="$$createConfigProduct.id$$"/> + </actionGroup> <waitForPageLoad stepKey="waitEditPage"/> <click selector="{{AdminProductFormSection.enableProductLabel}}" stepKey="disableProductForAllStoreView"/> <click selector="{{AdminProductFormActionSection.saveButton}}" stepKey="clickSaveButton2"/> @@ -140,10 +144,12 @@ <waitForElementVisible selector="{{AdminProductFormSection.enableProductLabel}}" stepKey="waitForProductEnableSlider"/> <click selector="{{AdminProductFormSection.enableProductLabel}}" stepKey="enableProductForSecondStoreView"/> <seeCheckboxIsChecked selector="{{AdminProductFormSection.productStatus}}" stepKey="seeThatProductIsEnabled"/> - <actionGroup ref="AdminFormSaveAndClose" stepKey="enabledConfigProductSecondStore"/> + <actionGroup ref="AdminFormSaveAndCloseActionGroup" stepKey="enabledConfigProductSecondStore"/> <!--go to admin and open product edit page to enable child product for second store view --> - <amOnPage url="{{AdminProductEditPage.url($$createConfigProduct.id$$)}}" stepKey="goToProductEditPage2"/> + <actionGroup ref="AdminProductPageOpenByIdActionGroup" stepKey="goToProductEditPage2"> + <argument name="productId" value="$$createConfigProduct.id$$"/> + </actionGroup> <waitForPageLoad stepKey="waitEditPage2"/> <click selector="{{AdminProductFormActionSection.changeStoreButton}}" stepKey="clickStoreviewSwitcher1"/> <click selector="{{AdminProductFormActionSection.selectStoreView('Second Store View')}}" stepKey="chooseStoreView1"/> @@ -154,7 +160,7 @@ <click selector="{{AdminProductFormConfigurationsSection.enableProductBtn}}" stepKey="clickEnableChildProduct1"/> <click selector="{{AdminProductFormConfigurationsSection.actionsBtn('2')}}" stepKey="clickToExpandActionsForSecondVariation2"/> <click selector="{{AdminProductFormConfigurationsSection.enableProductBtn}}" stepKey="clickEnableChildProduct2"/> - <actionGroup ref="AdminFormSaveAndClose" stepKey="saveAll"/> + <actionGroup ref="AdminFormSaveAndCloseActionGroup" stepKey="saveAll"/> <!-- assert second store view storefront category list page --> <amOnPage url="/second_store_view/" stepKey="amOnsecondStoreFront1"/> @@ -164,14 +170,18 @@ <see userInput="$$createConfigProduct.name$$" stepKey="assertProductPresent1"/> <!--go to admin and open child product1 and assign it to the second website --> - <amOnPage url="{{AdminProductEditPage.url($$createConfigChildProduct1.id$$)}}" stepKey="goToProduct1EditPage1"/> + <actionGroup ref="AdminProductPageOpenByIdActionGroup" stepKey="goToProduct1EditPage1"> + <argument name="productId" value="$$createConfigChildProduct1.id$$"/> + </actionGroup> <waitForPageLoad stepKey="waitChild1EditPageToLoad"/> <click selector="{{ProductInWebsitesSection.sectionHeader}}" stepKey="openProduct1InWebsitesSection"/> <click selector="{{ProductInWebsitesSection.website('Second Website')}}" stepKey="selectSecondWebsite1"/> - <actionGroup ref="AdminFormSaveAndClose" stepKey="saveUpdatedChild1Again"/> + <actionGroup ref="AdminFormSaveAndCloseActionGroup" stepKey="saveUpdatedChild1Again"/> <!--go to admin again and open child product1 and enable for second store view--> - <amOnPage url="{{AdminProductEditPage.url($$createConfigChildProduct1.id$$)}}" stepKey="goToProduct1EditPage"/> + <actionGroup ref="AdminProductPageOpenByIdActionGroup" stepKey="goToProduct1EditPage"> + <argument name="productId" value="$$createConfigChildProduct1.id$$"/> + </actionGroup> <waitForPageLoad stepKey="waitChild1EditPageToLoad1"/> <click selector="{{AdminProductFormActionSection.changeStoreButton}}" stepKey="clickStoreviewSwitcherP1"/> <click selector="{{AdminProductFormActionSection.selectStoreView('Second Store View')}}" stepKey="chooseStoreView2P1"/> @@ -180,17 +190,21 @@ <see userInput="Second Store View" selector="{{AdminMainActionsSection.storeSwitcher}}" stepKey="seeNewStoreViewNameP1"/> <waitForElementVisible selector="{{AdminProductFormSection.enableProductLabel}}" stepKey="waitForProductEnableSliderP1"/> <seeCheckboxIsChecked selector="{{AdminProductFormSection.productStatus}}" stepKey="seeThatProduct1IsEnabled"/> - <actionGroup ref="AdminFormSaveAndClose" stepKey="save2UpdatedChild1"/> + <actionGroup ref="AdminFormSaveAndCloseActionGroup" stepKey="save2UpdatedChild1"/> <!--go to admin and open child product2 edit page and assign it to the second website --> - <amOnPage url="{{AdminProductEditPage.url($$createConfigChildProduct2.id$$)}}" stepKey="goToProduct2EditPage"/> + <actionGroup ref="AdminProductPageOpenByIdActionGroup" stepKey="goToProduct2EditPage"> + <argument name="productId" value="$$createConfigChildProduct2.id$$"/> + </actionGroup> <waitForPageLoad stepKey="waitChild2EditPageToLoad"/> <click selector="{{ProductInWebsitesSection.sectionHeader}}" stepKey="openProduct2InWebsitesSection"/> <click selector="{{ProductInWebsitesSection.website('Second Website')}}" stepKey="selectSecondWebsite2"/> - <actionGroup ref="AdminFormSaveAndClose" stepKey="saveUpdatedChild2"/> + <actionGroup ref="AdminFormSaveAndCloseActionGroup" stepKey="saveUpdatedChild2"/> <!--go to admin again and open child product2 and enable for second store view--> - <amOnPage url="{{AdminProductEditPage.url($$createConfigChildProduct2.id$$)}}" stepKey="goToProduct2EditPage2"/> + <actionGroup ref="AdminProductPageOpenByIdActionGroup" stepKey="goToProduct2EditPage2"> + <argument name="productId" value="$$createConfigChildProduct2.id$$"/> + </actionGroup> <waitForPageLoad stepKey="waitChild2EditPageToLoad1"/> <click selector="{{AdminProductFormActionSection.changeStoreButton}}" stepKey="clickStoreviewSwitcherP2"/> <click selector="{{AdminProductFormActionSection.selectStoreView('Second Store View')}}" stepKey="chooseStoreView2P2"/> @@ -199,7 +213,7 @@ <see userInput="Second Store View" selector="{{AdminMainActionsSection.storeSwitcher}}" stepKey="seeNewStoreViewNameP2"/> <waitForElementVisible selector="{{AdminProductFormSection.enableProductLabel}}" stepKey="waitForProductEnableSliderP2"/> <seeCheckboxIsChecked selector="{{AdminProductFormSection.productStatus}}" stepKey="seeThatProduct2IsEnabled"/> - <actionGroup ref="AdminFormSaveAndClose" stepKey="save2UpdatedChild2"/> + <actionGroup ref="AdminFormSaveAndCloseActionGroup" stepKey="save2UpdatedChild2"/> <!-- assert storefront category list page --> <amOnPage url="/second_store_view/" stepKey="amOnsecondStoreFront"/> diff --git a/app/code/Magento/ConfigurableProduct/Test/Mftf/Test/EndToEndB2CGuestUserTest.xml b/app/code/Magento/ConfigurableProduct/Test/Mftf/Test/EndToEndB2CGuestUserTest.xml deleted file mode 100644 index 59bb7f53f0aa8..0000000000000 --- a/app/code/Magento/ConfigurableProduct/Test/Mftf/Test/EndToEndB2CGuestUserTest.xml +++ /dev/null @@ -1,429 +0,0 @@ -<?xml version="1.0" encoding="UTF-8"?> -<!-- - /** - * Copyright © Magento, Inc. All rights reserved. - * See COPYING.txt for license details. - */ ---> - -<tests xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" - xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/testSchema.xsd"> - <test name="EndToEndB2CGuestUserTest"> - <before> - <createData entity="ApiConfigurableProduct" stepKey="createConfigProduct"> - <requiredEntity createDataKey="createCategory"/> - </createData> - <createData entity="productAttributeWithTwoOptions" stepKey="createConfigProductAttribute"/> - <createData entity="productAttributeOption1" stepKey="createConfigProductAttributeOption1"> - <requiredEntity createDataKey="createConfigProductAttribute"/> - </createData> - <createData entity="productAttributeOption2" stepKey="createConfigProductAttributeOption2"> - <requiredEntity createDataKey="createConfigProductAttribute"/> - </createData> - <createData entity="AddToDefaultSet" stepKey="createConfigAddToAttributeSet"> - <requiredEntity createDataKey="createConfigProductAttribute"/> - </createData> - <getData entity="ProductAttributeOptionGetter" index="1" stepKey="getConfigAttributeOption1"> - <requiredEntity createDataKey="createConfigProductAttribute"/> - </getData> - <getData entity="ProductAttributeOptionGetter" index="2" stepKey="getConfigAttributeOption2"> - <requiredEntity createDataKey="createConfigProductAttribute"/> - </getData> - <createData entity="ApiSimpleOne" stepKey="createConfigChildProduct1"> - <requiredEntity createDataKey="createConfigProductAttribute"/> - <requiredEntity createDataKey="getConfigAttributeOption1"/> - </createData> - <createData entity="ApiProductAttributeMediaGalleryEntryTestImage" stepKey="createConfigChildProduct1Image"> - <requiredEntity createDataKey="createConfigChildProduct1"/> - </createData> - <createData entity="ApiSimpleTwo" stepKey="createConfigChildProduct2"> - <requiredEntity createDataKey="createConfigProductAttribute"/> - <requiredEntity createDataKey="getConfigAttributeOption2"/> - </createData> - <createData entity="ApiProductAttributeMediaGalleryEntryMagentoLogo" stepKey="createConfigChildProduct2Image"> - <requiredEntity createDataKey="createConfigChildProduct2"/> - </createData> - <createData entity="ConfigurableProductTwoOptions" stepKey="createConfigProductOption"> - <requiredEntity createDataKey="createConfigProduct"/> - <requiredEntity createDataKey="createConfigProductAttribute"/> - <requiredEntity createDataKey="getConfigAttributeOption1"/> - <requiredEntity createDataKey="getConfigAttributeOption2"/> - </createData> - <createData entity="ConfigurableProductAddChild" stepKey="createConfigProductAddChild1"> - <requiredEntity createDataKey="createConfigProduct"/> - <requiredEntity createDataKey="createConfigChildProduct1"/> - </createData> - <createData entity="ConfigurableProductAddChild" stepKey="createConfigProductAddChild2"> - <requiredEntity createDataKey="createConfigProduct"/> - <requiredEntity createDataKey="createConfigChildProduct2"/> - </createData> - <createData entity="ApiProductAttributeMediaGalleryEntryTestImage" stepKey="createConfigProductImage"> - <requiredEntity createDataKey="createConfigProduct"/> - </createData> - <updateData entity="ApiSimpleProductUpdateDescription" stepKey="updateConfigProduct" createDataKey="createConfigProduct"/> - </before> - <after> - <!-- @TODO: Uncomment once MQE-679 is fixed --> - <!--<deleteData createDataKey="createConfigChildProduct1Image" stepKey="deleteConfigChildProduct1Image"/>--> - <deleteData createDataKey="createConfigChildProduct1" stepKey="deleteConfigChildProduct1"/> - <!-- @TODO: Uncomment once MQE-679 is fixed --> - <!--<deleteData createDataKey="createConfigChildProduct2Image" stepKey="deleteConfigChildProduct2Image"/>--> - <deleteData createDataKey="createConfigChildProduct2" stepKey="deleteConfigChildProduct2"/> - <!-- @TODO: Uncomment once MQE-679 is fixed --> - <!--<deleteData createDataKey="createConfigProductImage" stepKey="deleteConfigProductImage"/>--> - <deleteData createDataKey="createConfigProduct" stepKey="deleteConfigProduct"/> - <deleteData createDataKey="createConfigProductAttribute" stepKey="deleteConfigProductAttribute"/> - </after> - - <!-- Verify Configurable Product in checkout cart items --> - <comment userInput="Verify Configurable Product in checkout cart items" stepKey="commentVerifyConfigurableProductInCheckoutCartItems" after="guestCheckoutCheckSimpleProduct2InCartItems" /> - <actionGroup ref="CheckConfigurableProductInCheckoutCartItemsActionGroup" stepKey="guestCheckoutCheckConfigurableProductInCartItems" after="commentVerifyConfigurableProductInCheckoutCartItems"> - <argument name="productVar" value="$$createConfigProduct$$"/> - <argument name="optionLabel" value="$$createConfigProductAttribute.attribute[frontend_labels][0][label]$$" /> - <argument name="optionValue" value="$$createConfigProductAttributeOption2.option[store_labels][1][label]$$" /> - </actionGroup> - - <!-- Check configurable product in category --> - <comment userInput="Verify Configurable Product in category" stepKey="commentVerifyConfigurableProductInCategory" after="browseAssertSimpleProduct2ImageNotDefault" /> - <actionGroup ref="StorefrontCheckCategoryConfigurableProductActionGroup" stepKey="browseAssertCategoryConfigProduct" after="commentVerifyConfigurableProductInCategory"> - <argument name="product" value="$$createConfigProduct$$"/> - <argument name="optionProduct" value="$$createConfigChildProduct1$$"/> - </actionGroup> - <!-- @TODO: Move Image check to action group after MQE-697 is fixed --> - <grabAttributeFrom selector="{{StorefrontCategoryProductSection.ProductImageByName($$createConfigProduct.name$$)}}" userInput="src" stepKey="browseGrabConfigProductImageSrc" after="browseAssertCategoryConfigProduct"/> - <assertNotRegExp expected="'/placeholder\/small_image\.jpg/'" actual="$browseGrabConfigProductImageSrc" stepKey="browseAssertConfigProductImageNotDefault" after="browseGrabConfigProductImageSrc"/> - - <!-- View Configurable Product --> - <comment userInput="View Configurable Product" stepKey="commentViewConfigurableProduct" after="browseAssertSimpleProduct2PageImageNotDefault" /> - <click selector="{{StorefrontHeaderSection.NavigationCategoryByName($$createCategory.name$$)}}" stepKey="clickCategory2" after="commentViewConfigurableProduct"/> - <click selector="{{StorefrontCategoryProductSection.ProductTitleByName($$createConfigProduct.name$$)}}" stepKey="browseClickCategoryConfigProductView" after="clickCategory2"/> - <waitForLoadingMaskToDisappear stepKey="waitForConfigurableProductViewloaded" after="browseClickCategoryConfigProductView"/> - <actionGroup ref="StorefrontCheckConfigurableProductActionGroup" stepKey="browseAssertConfigProductPage" after="waitForConfigurableProductViewloaded"> - <argument name="product" value="$$createConfigProduct$$"/> - <argument name="optionProduct" value="$$createConfigChildProduct1$$"/> - </actionGroup> - <!-- @TODO: Move Image check to action group after MQE-697 is fixed --> - <grabAttributeFrom selector="{{StorefrontProductInfoMainSection.productImage}}" userInput="src" stepKey="browseGrabConfigProductPageImageSrc" after="browseAssertConfigProductPage"/> - <assertNotRegExp expected="'/placeholder\/image\.jpg/'" actual="$browseGrabConfigProductPageImageSrc" stepKey="browseAssertConfigProductPageImageNotDefault" after="browseGrabConfigProductPageImageSrc"/> - - <!-- Add Configurable Product to cart --> - <comment userInput="Add Configurable Product to cart" stepKey="commentAddConfigurableProductToCart" after="cartAddProduct2ToCart" /> - <click selector="{{StorefrontHeaderSection.NavigationCategoryByName($$createCategory.name$$)}}" stepKey="cartClickCategory2" after="commentAddConfigurableProductToCart"/> - <waitForLoadingMaskToDisappear stepKey="waitForCartCategory2loaded" after="cartClickCategory2"/> - <actionGroup ref="StorefrontCheckCategoryActionGroup" stepKey="cartAssertCategory1ForConfigurableProduct" after="waitForCartCategory2loaded"> - <argument name="category" value="$$createCategory$$"/> - <argument name="productCount" value="3"/> - </actionGroup> - <actionGroup ref="StorefrontCheckCategoryConfigurableProductActionGroup" stepKey="cartAssertConfigProduct" after="cartAssertCategory1ForConfigurableProduct"> - <argument name="product" value="$$createConfigProduct$$"/> - <argument name="optionProduct" value="$$createConfigChildProduct1$$"/> - </actionGroup> - <!-- @TODO: Move Image check to action group after MQE-697 is fixed --> - <grabAttributeFrom selector="{{StorefrontCategoryProductSection.ProductImageByName($$createConfigProduct.name$$)}}" userInput="src" stepKey="cartGrabConfigProductImageSrc" after="cartAssertConfigProduct"/> - <assertNotRegExp expected="'/placeholder\/small_image\.jpg/'" actual="$cartGrabConfigProductImageSrc" stepKey="cartAssertConfigProductImageNotDefault" after="cartGrabConfigProductImageSrc"/> - <click selector="{{StorefrontCategoryProductSection.ProductAddToCartByName($$createConfigProduct.name$$)}}" stepKey="cartClickCategoryConfigProductAddToCart" after="cartAssertConfigProductImageNotDefault"/> - <waitForElement selector="{{StorefrontMessagesSection.message('You need to choose options for your item.')}}" time="30" stepKey="cartWaitForConfigProductPageLoad" after="cartClickCategoryConfigProductAddToCart"/> - <actionGroup ref="StorefrontCheckConfigurableProductActionGroup" stepKey="cartAssertConfigProductPage" after="cartWaitForConfigProductPageLoad"> - <argument name="product" value="$$createConfigProduct$$"/> - <argument name="optionProduct" value="$$createConfigChildProduct1$$"/> - </actionGroup> - <!-- @TODO: Move Image check to action group after MQE-697 is fixed --> - <grabAttributeFrom selector="{{StorefrontProductInfoMainSection.productImage}}" userInput="src" stepKey="cartGrabConfigProductPageImageSrc1" after="cartAssertConfigProductPage"/> - <assertNotRegExp expected="'/placeholder\/image\.jpg/'" actual="$cartGrabConfigProductPageImageSrc1" stepKey="cartAssertConfigProductPageImageNotDefault1" after="cartGrabConfigProductPageImageSrc1"/> - <selectOption userInput="$$createConfigProductAttributeOption2.option[store_labels][1][label]$$" selector="{{StorefrontProductInfoMainSection.optionByAttributeId($$createConfigProductAttribute.attribute_id$$)}}" stepKey="cartConfigProductFillOption" after="cartAssertConfigProductPageImageNotDefault1"/> - <waitForLoadingMaskToDisappear stepKey="waitForConfigurableProductOptionloaded" after="cartConfigProductFillOption"/> - <actionGroup ref="StorefrontCheckConfigurableProductActionGroup" stepKey="cartAssertConfigProductWithOptionPage" after="waitForConfigurableProductOptionloaded"> - <argument name="product" value="$$createConfigProduct$$"/> - <argument name="optionProduct" value="$$createConfigChildProduct2$$"/> - </actionGroup> - <!-- @TODO: Move Image check to action group after MQE-697 is fixed --> - <grabAttributeFrom selector="{{StorefrontProductInfoMainSection.productImage}}" userInput="src" stepKey="cartGrabConfigProductPageImageSrc2" after="cartAssertConfigProductWithOptionPage"/> - <assertNotRegExp expected="'/placeholder\/image\.jpg/'" actual="$cartGrabConfigProductPageImageSrc2" stepKey="cartAssertConfigProductPageImageNotDefault2" after="cartGrabConfigProductPageImageSrc2"/> - <actionGroup ref="StorefrontAddProductToCartActionGroup" stepKey="cartAddConfigProductToCart" after="cartAssertConfigProductPageImageNotDefault2"> - <argument name="product" value="$$createConfigProduct$$"/> - <argument name="productCount" value="3"/> - </actionGroup> - - <!-- Check configurable product in minicart --> - <comment userInput="Check configurable product in minicart" stepKey="commentCheckConfigurableProductInMinicart" after="cartMinicartAssertSimpleProduct2PageImageNotDefault" /> - <actionGroup ref="StorefrontOpenMinicartAndCheckConfigurableProductActionGroup" stepKey="cartOpenMinicartAndCheckConfigProduct" after="commentCheckConfigurableProductInMinicart"> - <argument name="product" value="$$createConfigProduct$$"/> - <argument name="optionProduct" value="$$createConfigChildProduct2$$"/> - </actionGroup> - <!-- @TODO: Move Image check to action group after MQE-697 is fixed --> - <grabAttributeFrom selector="{{StorefrontMinicartSection.productImageByName($$createConfigProduct.name$$)}}" userInput="src" stepKey="cartMinicartGrabConfigProductImageSrc" after="cartOpenMinicartAndCheckConfigProduct"/> - <assertNotRegExp expected="'/placeholder\/thumbnail\.jpg/'" actual="$cartMinicartGrabConfigProductImageSrc" stepKey="cartMinicartAssertConfigProductImageNotDefault" after="cartMinicartGrabConfigProductImageSrc"/> - <click selector="{{StorefrontMinicartSection.productOptionsDetailsByName($$createConfigProduct.name$$)}}" stepKey="cartMinicartClickConfigProductDetails" after="cartMinicartAssertConfigProductImageNotDefault"/> - <see userInput="$$createConfigProductAttributeOption2.option[store_labels][1][label]$$" selector="{{StorefrontMinicartSection.productOptionByNameAndAttribute($$createConfigProduct.name$$, $$createConfigProductAttribute.attribute[frontend_labels][0][label]$$)}}" stepKey="cartMinicartCheckConfigProductOption" after="cartMinicartClickConfigProductDetails"/> - <click selector="{{StorefrontMinicartSection.productLinkByName($$createConfigProduct.name$$)}}" stepKey="cartMinicartClickConfigProduct" after="cartMinicartCheckConfigProductOption"/> - <waitForLoadingMaskToDisappear stepKey="waitForMinicartConfigProductloaded" after="cartMinicartClickConfigProduct"/> - <actionGroup ref="StorefrontCheckConfigurableProductActionGroup" stepKey="cartAssertMinicartConfigProductPage" after="waitForMinicartConfigProductloaded"> - <argument name="product" value="$$createConfigProduct$$"/> - <argument name="optionProduct" value="$$createConfigChildProduct1$$"/> - </actionGroup> - <!-- @TODO: Move Image check to action group after MQE-697 is fixed --> - <grabAttributeFrom selector="{{StorefrontProductInfoMainSection.productImage}}" userInput="src" stepKey="cartMinicartGrabConfigProductPageImageSrc" after="cartAssertMinicartConfigProductPage"/> - <assertNotRegExp expected="'/placeholder\/image\.jpg/'" actual="$cartMinicartGrabConfigProductPageImageSrc" stepKey="cartMinicartAssertConfigProductPageImageNotDefault" after="cartMinicartGrabConfigProductPageImageSrc"/> - - <!-- Check configurable product in cart --> - <comment userInput="Check configurable product in cart" stepKey="commentCheckConfigurableProductInCart" after="cartCartAssertSimpleProduct2PageImageNotDefault2" /> - <actionGroup ref="StorefrontOpenCartFromMinicartActionGroup" stepKey="cartOpenCart2" after="commentCheckConfigurableProductInCart"/> - <actionGroup ref="StorefrontCheckCartConfigurableProductActionGroup" stepKey="cartAssertCartConfigProduct" after="cartOpenCart2"> - <argument name="product" value="$$createConfigProduct$$"/> - <argument name="optionProduct" value="$$createConfigChildProduct2$$"/> - <!-- @TODO: Change to scalar value after MQE-498 is implemented --> - <argument name="productQuantity" value="CONST.one"/> - </actionGroup> - <!-- @TODO: Move Image check to action group after MQE-697 is fixed --> - <grabAttributeFrom selector="{{CheckoutCartProductSection.ProductImageByName($$createConfigProduct.name$$)}}" userInput="src" stepKey="cartCartGrabConfigProduct2ImageSrc" after="cartAssertCartConfigProduct"/> - <assertNotRegExp expected="'/placeholder\/small_image\.jpg/'" actual="$cartCartGrabConfigProduct2ImageSrc" stepKey="cartCartAssertConfigProduct2ImageNotDefault" after="cartCartGrabConfigProduct2ImageSrc"/> - <see userInput="$$createConfigProductAttributeOption2.option[store_labels][1][label]$$" selector="{{CheckoutCartProductSection.ProductOptionByNameAndAttribute($$createConfigProduct.name$$, $$createConfigProductAttribute.attribute[frontend_labels][0][label]$$)}}" stepKey="cartCheckConfigProductOption" after="cartCartAssertConfigProduct2ImageNotDefault"/> - <click selector="{{CheckoutCartProductSection.ProductLinkByName($$createConfigProduct.name$$)}}" stepKey="cartClickCartConfigProduct" after="cartCheckConfigProductOption"/> - <waitForLoadingMaskToDisappear stepKey="waitForCartConfigProductloaded" after="cartClickCartConfigProduct"/> - <actionGroup ref="StorefrontCheckConfigurableProductActionGroup" stepKey="cartAssertCartConfigProductPage" after="waitForCartConfigProductloaded"> - <argument name="product" value="$$createConfigProduct$$"/> - <argument name="optionProduct" value="$$createConfigChildProduct1$$"/> - </actionGroup> - <!-- @TODO: Move Image check to action group after MQE-697 is fixed --> - <grabAttributeFrom selector="{{StorefrontProductInfoMainSection.productImage}}" userInput="src" stepKey="cartCartGrabConfigProductPageImageSrc" after="cartAssertCartConfigProductPage"/> - <assertNotRegExp expected="'/placeholder\/image\.jpg/'" actual="$cartCartGrabConfigProductPageImageSrc" stepKey="cartCartAssertConfigProductPageImageNotDefault" after="cartCartGrabConfigProductPageImageSrc"/> - - <!-- Add Configurable Product to comparison --> - <comment userInput="Add Configurable Product to comparison" stepKey="commentAddConfigurableProductToComparison" after="compareAddSimpleProduct2ToCompare" /> - <actionGroup ref="StorefrontCheckCategoryConfigurableProductActionGroup" stepKey="compareAssertConfigProduct" after="commentAddConfigurableProductToComparison"> - <argument name="product" value="$$createConfigProduct$$"/> - <argument name="optionProduct" value="$$createConfigChildProduct1$$"/> - </actionGroup> - <!-- @TODO: Move Image check to action group after MQE-697 is fixed --> - <grabAttributeFrom selector="{{StorefrontCategoryProductSection.ProductImageByName($$createConfigProduct.name$$)}}" userInput="src" stepKey="compareGrabConfigProductImageSrc" after="compareAssertConfigProduct"/> - <assertNotRegExp expected="'/placeholder\/small_image\.jpg/'" actual="$compareGrabConfigProductImageSrc" stepKey="compareAssertConfigProductImageNotDefault" after="compareGrabConfigProductImageSrc"/> - <actionGroup ref="StorefrontAddCategoryProductToCompareActionGroup" stepKey="compareAddConfigProductToCompare" after="compareAssertConfigProductImageNotDefault"> - <argument name="productVar" value="$$createConfigProduct$$"/> - </actionGroup> - - <!-- Check configurable product in comparison sidebar --> - <comment userInput="Add Configurable Product in comparison sidebar" stepKey="commentAddConfigurableProductInComparisonSidebar" after="compareSimpleProduct2InSidebar" /> - <actionGroup ref="StorefrontCheckCompareSidebarProductActionGroup" stepKey="compareConfigProductInSidebar" after="commentAddConfigurableProductInComparisonSidebar"> - <argument name="productVar" value="$$createConfigProduct$$"/> - </actionGroup> - - <!-- Check configurable product on comparison page --> - <comment userInput="Add Configurable Product on comparison page" stepKey="commentAddConfigurableProductOnComparisonPage" after="compareAssertSimpleProduct2ImageNotDefaultInComparison" /> - <actionGroup ref="StorefrontCheckCompareConfigurableProductActionGroup" stepKey="compareAssertConfigProductInComparison" after="commentAddConfigurableProductOnComparisonPage"> - <argument name="product" value="$$createConfigProduct$$"/> - <argument name="optionProduct" value="$$createConfigChildProduct1$$"/> - </actionGroup> - <!-- @TODO: Move Image check to action group after MQE-697 is fixed --> - <grabAttributeFrom selector="{{StorefrontProductCompareMainSection.ProductImageByName($$createConfigProduct.name$$)}}" userInput="src" stepKey="compareGrabConfigProductImageSrcInComparison" after="compareAssertConfigProductInComparison"/> - <assertNotRegExp expected="'/placeholder\/small_image\.jpg/'" actual="$compareGrabConfigProductImageSrcInComparison" stepKey="compareAssertConfigProductImageNotDefaultInComparison" after="compareGrabConfigProductImageSrcInComparison"/> - </test> - <test name="EndToEndB2CGuestUserMysqlTest"> - <before> - <createData entity="ApiConfigurableProduct" stepKey="createConfigProduct"> - <requiredEntity createDataKey="createCategory"/> - </createData> - <createData entity="productAttributeWithTwoOptions" stepKey="createConfigProductAttribute"/> - <createData entity="productAttributeOption1" stepKey="createConfigProductAttributeOption1"> - <requiredEntity createDataKey="createConfigProductAttribute"/> - </createData> - <createData entity="productAttributeOption2" stepKey="createConfigProductAttributeOption2"> - <requiredEntity createDataKey="createConfigProductAttribute"/> - </createData> - <createData entity="AddToDefaultSet" stepKey="createConfigAddToAttributeSet"> - <requiredEntity createDataKey="createConfigProductAttribute"/> - </createData> - <getData entity="ProductAttributeOptionGetter" index="1" stepKey="getConfigAttributeOption1"> - <requiredEntity createDataKey="createConfigProductAttribute"/> - </getData> - <getData entity="ProductAttributeOptionGetter" index="2" stepKey="getConfigAttributeOption2"> - <requiredEntity createDataKey="createConfigProductAttribute"/> - </getData> - <createData entity="ApiSimpleOne" stepKey="createConfigChildProduct1"> - <requiredEntity createDataKey="createConfigProductAttribute"/> - <requiredEntity createDataKey="getConfigAttributeOption1"/> - </createData> - <createData entity="ApiProductAttributeMediaGalleryEntryTestImage" stepKey="createConfigChildProduct1Image"> - <requiredEntity createDataKey="createConfigChildProduct1"/> - </createData> - <createData entity="ApiSimpleTwo" stepKey="createConfigChildProduct2"> - <requiredEntity createDataKey="createConfigProductAttribute"/> - <requiredEntity createDataKey="getConfigAttributeOption2"/> - </createData> - <createData entity="ApiProductAttributeMediaGalleryEntryMagentoLogo" stepKey="createConfigChildProduct2Image"> - <requiredEntity createDataKey="createConfigChildProduct2"/> - </createData> - <createData entity="ConfigurableProductTwoOptions" stepKey="createConfigProductOption"> - <requiredEntity createDataKey="createConfigProduct"/> - <requiredEntity createDataKey="createConfigProductAttribute"/> - <requiredEntity createDataKey="getConfigAttributeOption1"/> - <requiredEntity createDataKey="getConfigAttributeOption2"/> - </createData> - <createData entity="ConfigurableProductAddChild" stepKey="createConfigProductAddChild1"> - <requiredEntity createDataKey="createConfigProduct"/> - <requiredEntity createDataKey="createConfigChildProduct1"/> - </createData> - <createData entity="ConfigurableProductAddChild" stepKey="createConfigProductAddChild2"> - <requiredEntity createDataKey="createConfigProduct"/> - <requiredEntity createDataKey="createConfigChildProduct2"/> - </createData> - <createData entity="ApiProductAttributeMediaGalleryEntryTestImage" stepKey="createConfigProductImage"> - <requiredEntity createDataKey="createConfigProduct"/> - </createData> - <updateData entity="ApiSimpleProductUpdateDescription" stepKey="updateConfigProduct" createDataKey="createConfigProduct"/> - </before> - <after> - <!-- @TODO: Uncomment once MQE-679 is fixed --> - <!--<deleteData createDataKey="createConfigChildProduct1Image" stepKey="deleteConfigChildProduct1Image"/>--> - <deleteData createDataKey="createConfigChildProduct1" stepKey="deleteConfigChildProduct1"/> - <!-- @TODO: Uncomment once MQE-679 is fixed --> - <!--<deleteData createDataKey="createConfigChildProduct2Image" stepKey="deleteConfigChildProduct2Image"/>--> - <deleteData createDataKey="createConfigChildProduct2" stepKey="deleteConfigChildProduct2"/> - <!-- @TODO: Uncomment once MQE-679 is fixed --> - <!--<deleteData createDataKey="createConfigProductImage" stepKey="deleteConfigProductImage"/>--> - <deleteData createDataKey="createConfigProduct" stepKey="deleteConfigProduct"/> - <deleteData createDataKey="createConfigProductAttribute" stepKey="deleteConfigProductAttribute"/> - </after> - - <!-- Verify Configurable Product in checkout cart items --> - <comment userInput="Verify Configurable Product in checkout cart items" stepKey="commentVerifyConfigurableProductInCheckoutCartItems" after="guestCheckoutCheckSimpleProduct2InCartItems" /> - <actionGroup ref="CheckConfigurableProductInCheckoutCartItemsActionGroup" stepKey="guestCheckoutCheckConfigurableProductInCartItems" after="commentVerifyConfigurableProductInCheckoutCartItems"> - <argument name="productVar" value="$$createConfigProduct$$"/> - <argument name="optionLabel" value="$$createConfigProductAttribute.attribute[frontend_labels][0][label]$$" /> - <argument name="optionValue" value="$$createConfigProductAttributeOption2.option[store_labels][1][label]$$" /> - </actionGroup> - - <!-- Check configurable product in category --> - <comment userInput="Verify Configurable Product in category" stepKey="commentVerifyConfigurableProductInCategory" after="browseAssertSimpleProduct2ImageNotDefault" /> - <actionGroup ref="StorefrontCheckCategoryConfigurableProductActionGroup" stepKey="browseAssertCategoryConfigProduct" after="commentVerifyConfigurableProductInCategory"> - <argument name="product" value="$$createConfigProduct$$"/> - <argument name="optionProduct" value="$$createConfigChildProduct1$$"/> - </actionGroup> - <!-- @TODO: Move Image check to action group after MQE-697 is fixed --> - <grabAttributeFrom selector="{{StorefrontCategoryProductSection.ProductImageByName($$createConfigProduct.name$$)}}" userInput="src" stepKey="browseGrabConfigProductImageSrc" after="browseAssertCategoryConfigProduct"/> - <assertNotRegExp expected="'/placeholder\/small_image\.jpg/'" actual="$browseGrabConfigProductImageSrc" stepKey="browseAssertConfigProductImageNotDefault" after="browseGrabConfigProductImageSrc"/> - - <!-- View Configurable Product --> - <comment userInput="View Configurable Product" stepKey="commentViewConfigurableProduct" after="browseAssertSimpleProduct2PageImageNotDefault" /> - <click selector="{{StorefrontHeaderSection.NavigationCategoryByName($$createCategory.name$$)}}" stepKey="clickCategory2" after="commentViewConfigurableProduct"/> - <click selector="{{StorefrontCategoryProductSection.ProductTitleByName($$createConfigProduct.name$$)}}" stepKey="browseClickCategoryConfigProductView" after="clickCategory2"/> - <waitForLoadingMaskToDisappear stepKey="waitForConfigurableProductViewloaded" after="browseClickCategoryConfigProductView"/> - <actionGroup ref="StorefrontCheckConfigurableProductActionGroup" stepKey="browseAssertConfigProductPage" after="waitForConfigurableProductViewloaded"> - <argument name="product" value="$$createConfigProduct$$"/> - <argument name="optionProduct" value="$$createConfigChildProduct1$$"/> - </actionGroup> - <!-- @TODO: Move Image check to action group after MQE-697 is fixed --> - <grabAttributeFrom selector="{{StorefrontProductInfoMainSection.productImage}}" userInput="src" stepKey="browseGrabConfigProductPageImageSrc" after="browseAssertConfigProductPage"/> - <assertNotRegExp expected="'/placeholder\/image\.jpg/'" actual="$browseGrabConfigProductPageImageSrc" stepKey="browseAssertConfigProductPageImageNotDefault" after="browseGrabConfigProductPageImageSrc"/> - - <!-- Add Configurable Product to cart --> - <comment userInput="Add Configurable Product to cart" stepKey="commentAddConfigurableProductToCart" after="cartAddProduct2ToCart" /> - <click selector="{{StorefrontHeaderSection.NavigationCategoryByName($$createCategory.name$$)}}" stepKey="cartClickCategory2" after="commentAddConfigurableProductToCart"/> - <waitForLoadingMaskToDisappear stepKey="waitForCartCategory2loaded" after="cartClickCategory2"/> - <actionGroup ref="StorefrontCheckCategoryActionGroup" stepKey="cartAssertCategory1ForConfigurableProduct" after="waitForCartCategory2loaded"> - <argument name="category" value="$$createCategory$$"/> - <argument name="productCount" value="3"/> - </actionGroup> - <actionGroup ref="StorefrontCheckCategoryConfigurableProductActionGroup" stepKey="cartAssertConfigProduct" after="cartAssertCategory1ForConfigurableProduct"> - <argument name="product" value="$$createConfigProduct$$"/> - <argument name="optionProduct" value="$$createConfigChildProduct1$$"/> - </actionGroup> - <!-- @TODO: Move Image check to action group after MQE-697 is fixed --> - <grabAttributeFrom selector="{{StorefrontCategoryProductSection.ProductImageByName($$createConfigProduct.name$$)}}" userInput="src" stepKey="cartGrabConfigProductImageSrc" after="cartAssertConfigProduct"/> - <assertNotRegExp expected="'/placeholder\/small_image\.jpg/'" actual="$cartGrabConfigProductImageSrc" stepKey="cartAssertConfigProductImageNotDefault" after="cartGrabConfigProductImageSrc"/> - <click selector="{{StorefrontCategoryProductSection.ProductAddToCartByName($$createConfigProduct.name$$)}}" stepKey="cartClickCategoryConfigProductAddToCart" after="cartAssertConfigProductImageNotDefault"/> - <waitForElement selector="{{StorefrontMessagesSection.message('You need to choose options for your item.')}}" time="30" stepKey="cartWaitForConfigProductPageLoad" after="cartClickCategoryConfigProductAddToCart"/> - <actionGroup ref="StorefrontCheckConfigurableProductActionGroup" stepKey="cartAssertConfigProductPage" after="cartWaitForConfigProductPageLoad"> - <argument name="product" value="$$createConfigProduct$$"/> - <argument name="optionProduct" value="$$createConfigChildProduct1$$"/> - </actionGroup> - <!-- @TODO: Move Image check to action group after MQE-697 is fixed --> - <grabAttributeFrom selector="{{StorefrontProductInfoMainSection.productImage}}" userInput="src" stepKey="cartGrabConfigProductPageImageSrc1" after="cartAssertConfigProductPage"/> - <assertNotRegExp expected="'/placeholder\/image\.jpg/'" actual="$cartGrabConfigProductPageImageSrc1" stepKey="cartAssertConfigProductPageImageNotDefault1" after="cartGrabConfigProductPageImageSrc1"/> - <selectOption userInput="$$createConfigProductAttributeOption2.option[store_labels][1][label]$$" selector="{{StorefrontProductInfoMainSection.optionByAttributeId($$createConfigProductAttribute.attribute_id$$)}}" stepKey="cartConfigProductFillOption" after="cartAssertConfigProductPageImageNotDefault1"/> - <waitForLoadingMaskToDisappear stepKey="waitForConfigurableProductOptionloaded" after="cartConfigProductFillOption"/> - <actionGroup ref="StorefrontCheckConfigurableProductActionGroup" stepKey="cartAssertConfigProductWithOptionPage" after="waitForConfigurableProductOptionloaded"> - <argument name="product" value="$$createConfigProduct$$"/> - <argument name="optionProduct" value="$$createConfigChildProduct2$$"/> - </actionGroup> - <!-- @TODO: Move Image check to action group after MQE-697 is fixed --> - <grabAttributeFrom selector="{{StorefrontProductInfoMainSection.productImage}}" userInput="src" stepKey="cartGrabConfigProductPageImageSrc2" after="cartAssertConfigProductWithOptionPage"/> - <assertNotRegExp expected="'/placeholder\/image\.jpg/'" actual="$cartGrabConfigProductPageImageSrc2" stepKey="cartAssertConfigProductPageImageNotDefault2" after="cartGrabConfigProductPageImageSrc2"/> - <actionGroup ref="StorefrontAddProductToCartActionGroup" stepKey="cartAddConfigProductToCart" after="cartAssertConfigProductPageImageNotDefault2"> - <argument name="product" value="$$createConfigProduct$$"/> - <argument name="productCount" value="3"/> - </actionGroup> - - <!-- Check configurable product in minicart --> - <comment userInput="Check configurable product in minicart" stepKey="commentCheckConfigurableProductInMinicart" after="cartMinicartAssertSimpleProduct2PageImageNotDefault" /> - <actionGroup ref="StorefrontOpenMinicartAndCheckConfigurableProductActionGroup" stepKey="cartOpenMinicartAndCheckConfigProduct" after="commentCheckConfigurableProductInMinicart"> - <argument name="product" value="$$createConfigProduct$$"/> - <argument name="optionProduct" value="$$createConfigChildProduct2$$"/> - </actionGroup> - <!-- @TODO: Move Image check to action group after MQE-697 is fixed --> - <grabAttributeFrom selector="{{StorefrontMinicartSection.productImageByName($$createConfigProduct.name$$)}}" userInput="src" stepKey="cartMinicartGrabConfigProductImageSrc" after="cartOpenMinicartAndCheckConfigProduct"/> - <assertNotRegExp expected="'/placeholder\/thumbnail\.jpg/'" actual="$cartMinicartGrabConfigProductImageSrc" stepKey="cartMinicartAssertConfigProductImageNotDefault" after="cartMinicartGrabConfigProductImageSrc"/> - <click selector="{{StorefrontMinicartSection.productOptionsDetailsByName($$createConfigProduct.name$$)}}" stepKey="cartMinicartClickConfigProductDetails" after="cartMinicartAssertConfigProductImageNotDefault"/> - <see userInput="$$createConfigProductAttributeOption2.option[store_labels][1][label]$$" selector="{{StorefrontMinicartSection.productOptionByNameAndAttribute($$createConfigProduct.name$$, $$createConfigProductAttribute.attribute[frontend_labels][0][label]$$)}}" stepKey="cartMinicartCheckConfigProductOption" after="cartMinicartClickConfigProductDetails"/> - <click selector="{{StorefrontMinicartSection.productLinkByName($$createConfigProduct.name$$)}}" stepKey="cartMinicartClickConfigProduct" after="cartMinicartCheckConfigProductOption"/> - <waitForLoadingMaskToDisappear stepKey="waitForMinicartConfigProductloaded" after="cartMinicartClickConfigProduct"/> - <actionGroup ref="StorefrontCheckConfigurableProductActionGroup" stepKey="cartAssertMinicartConfigProductPage" after="waitForMinicartConfigProductloaded"> - <argument name="product" value="$$createConfigProduct$$"/> - <argument name="optionProduct" value="$$createConfigChildProduct1$$"/> - </actionGroup> - <!-- @TODO: Move Image check to action group after MQE-697 is fixed --> - <grabAttributeFrom selector="{{StorefrontProductInfoMainSection.productImage}}" userInput="src" stepKey="cartMinicartGrabConfigProductPageImageSrc" after="cartAssertMinicartConfigProductPage"/> - <assertNotRegExp expected="'/placeholder\/image\.jpg/'" actual="$cartMinicartGrabConfigProductPageImageSrc" stepKey="cartMinicartAssertConfigProductPageImageNotDefault" after="cartMinicartGrabConfigProductPageImageSrc"/> - - <!-- Check configurable product in cart --> - <comment userInput="Check configurable product in cart" stepKey="commentCheckConfigurableProductInCart" after="cartCartAssertSimpleProduct2PageImageNotDefault2" /> - <actionGroup ref="StorefrontOpenCartFromMinicartActionGroup" stepKey="cartOpenCart2" after="commentCheckConfigurableProductInCart"/> - <actionGroup ref="StorefrontCheckCartConfigurableProductActionGroup" stepKey="cartAssertCartConfigProduct" after="cartOpenCart2"> - <argument name="product" value="$$createConfigProduct$$"/> - <argument name="optionProduct" value="$$createConfigChildProduct2$$"/> - <!-- @TODO: Change to scalar value after MQE-498 is implemented --> - <argument name="productQuantity" value="CONST.one"/> - </actionGroup> - <!-- @TODO: Move Image check to action group after MQE-697 is fixed --> - <grabAttributeFrom selector="{{CheckoutCartProductSection.ProductImageByName($$createConfigProduct.name$$)}}" userInput="src" stepKey="cartCartGrabConfigProduct2ImageSrc" after="cartAssertCartConfigProduct"/> - <assertNotRegExp expected="'/placeholder\/small_image\.jpg/'" actual="$cartCartGrabConfigProduct2ImageSrc" stepKey="cartCartAssertConfigProduct2ImageNotDefault" after="cartCartGrabConfigProduct2ImageSrc"/> - <see userInput="$$createConfigProductAttributeOption2.option[store_labels][1][label]$$" selector="{{CheckoutCartProductSection.ProductOptionByNameAndAttribute($$createConfigProduct.name$$, $$createConfigProductAttribute.attribute[frontend_labels][0][label]$$)}}" stepKey="cartCheckConfigProductOption" after="cartCartAssertConfigProduct2ImageNotDefault"/> - <click selector="{{CheckoutCartProductSection.ProductLinkByName($$createConfigProduct.name$$)}}" stepKey="cartClickCartConfigProduct" after="cartCheckConfigProductOption"/> - <waitForLoadingMaskToDisappear stepKey="waitForCartConfigProductloaded" after="cartClickCartConfigProduct"/> - <actionGroup ref="StorefrontCheckConfigurableProductActionGroup" stepKey="cartAssertCartConfigProductPage" after="waitForCartConfigProductloaded"> - <argument name="product" value="$$createConfigProduct$$"/> - <argument name="optionProduct" value="$$createConfigChildProduct1$$"/> - </actionGroup> - <!-- @TODO: Move Image check to action group after MQE-697 is fixed --> - <grabAttributeFrom selector="{{StorefrontProductInfoMainSection.productImage}}" userInput="src" stepKey="cartCartGrabConfigProductPageImageSrc" after="cartAssertCartConfigProductPage"/> - <assertNotRegExp expected="'/placeholder\/image\.jpg/'" actual="$cartCartGrabConfigProductPageImageSrc" stepKey="cartCartAssertConfigProductPageImageNotDefault" after="cartCartGrabConfigProductPageImageSrc"/> - - <!-- Add Configurable Product to comparison --> - <comment userInput="Add Configurable Product to comparison" stepKey="commentAddConfigurableProductToComparison" after="compareAddSimpleProduct2ToCompare" /> - <actionGroup ref="StorefrontCheckCategoryConfigurableProductActionGroup" stepKey="compareAssertConfigProduct" after="commentAddConfigurableProductToComparison"> - <argument name="product" value="$$createConfigProduct$$"/> - <argument name="optionProduct" value="$$createConfigChildProduct1$$"/> - </actionGroup> - <!-- @TODO: Move Image check to action group after MQE-697 is fixed --> - <grabAttributeFrom selector="{{StorefrontCategoryProductSection.ProductImageByName($$createConfigProduct.name$$)}}" userInput="src" stepKey="compareGrabConfigProductImageSrc" after="compareAssertConfigProduct"/> - <assertNotRegExp expected="'/placeholder\/small_image\.jpg/'" actual="$compareGrabConfigProductImageSrc" stepKey="compareAssertConfigProductImageNotDefault" after="compareGrabConfigProductImageSrc"/> - <actionGroup ref="StorefrontAddCategoryProductToCompareActionGroup" stepKey="compareAddConfigProductToCompare" after="compareAssertConfigProductImageNotDefault"> - <argument name="productVar" value="$$createConfigProduct$$"/> - </actionGroup> - - <!-- Check configurable product in comparison sidebar --> - <comment userInput="Add Configurable Product in comparison sidebar" stepKey="commentAddConfigurableProductInComparisonSidebar" after="compareSimpleProduct2InSidebar" /> - <actionGroup ref="StorefrontCheckCompareSidebarProductActionGroup" stepKey="compareConfigProductInSidebar" after="commentAddConfigurableProductInComparisonSidebar"> - <argument name="productVar" value="$$createConfigProduct$$"/> - </actionGroup> - - <!-- Check configurable product on comparison page --> - <comment userInput="Add Configurable Product on comparison page" stepKey="commentAddConfigurableProductOnComparisonPage" after="compareAssertSimpleProduct2ImageNotDefaultInComparison" /> - <actionGroup ref="StorefrontCheckCompareConfigurableProductActionGroup" stepKey="compareAssertConfigProductInComparison" after="commentAddConfigurableProductOnComparisonPage"> - <argument name="product" value="$$createConfigProduct$$"/> - <argument name="optionProduct" value="$$createConfigChildProduct1$$"/> - </actionGroup> - <!-- @TODO: Move Image check to action group after MQE-697 is fixed --> - <grabAttributeFrom selector="{{StorefrontProductCompareMainSection.ProductImageByName($$createConfigProduct.name$$)}}" userInput="src" stepKey="compareGrabConfigProductImageSrcInComparison" after="compareAssertConfigProductInComparison"/> - <assertNotRegExp expected="'/placeholder\/small_image\.jpg/'" actual="$compareGrabConfigProductImageSrcInComparison" stepKey="compareAssertConfigProductImageNotDefaultInComparison" after="compareGrabConfigProductImageSrcInComparison"/> - </test> -</tests> diff --git a/app/code/Magento/ConfigurableProduct/Test/Mftf/Test/EndToEndB2CGuestUserTest/EndToEndB2CGuestUserMysqlTest.xml b/app/code/Magento/ConfigurableProduct/Test/Mftf/Test/EndToEndB2CGuestUserTest/EndToEndB2CGuestUserMysqlTest.xml new file mode 100644 index 0000000000000..aaf3da2dab21f --- /dev/null +++ b/app/code/Magento/ConfigurableProduct/Test/Mftf/Test/EndToEndB2CGuestUserTest/EndToEndB2CGuestUserMysqlTest.xml @@ -0,0 +1,255 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + /** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +--> +<tests xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/testSchema.xsd"> + <test name="EndToEndB2CGuestUserMysqlTest"> + <before> + <createData entity="ApiConfigurableProduct" stepKey="createConfigProduct"> + <requiredEntity createDataKey="createCategory"/> + </createData> + <createData entity="productAttributeWithTwoOptions" stepKey="createConfigProductAttribute"/> + <createData entity="productAttributeOption1" stepKey="createConfigProductAttributeOption1"> + <requiredEntity createDataKey="createConfigProductAttribute"/> + </createData> + <createData entity="productAttributeOption2" stepKey="createConfigProductAttributeOption2"> + <requiredEntity createDataKey="createConfigProductAttribute"/> + </createData> + <createData entity="AddToDefaultSet" stepKey="createConfigAddToAttributeSet"> + <requiredEntity createDataKey="createConfigProductAttribute"/> + </createData> + <getData entity="ProductAttributeOptionGetter" index="1" stepKey="getConfigAttributeOption1"> + <requiredEntity createDataKey="createConfigProductAttribute"/> + </getData> + <getData entity="ProductAttributeOptionGetter" index="2" stepKey="getConfigAttributeOption2"> + <requiredEntity createDataKey="createConfigProductAttribute"/> + </getData> + <createData entity="ApiSimpleOne" stepKey="createConfigChildProduct1"> + <requiredEntity createDataKey="createConfigProductAttribute"/> + <requiredEntity createDataKey="getConfigAttributeOption1"/> + </createData> + <createData entity="ApiProductAttributeMediaGalleryEntryTestImage" stepKey="createConfigChildProduct1Image"> + <requiredEntity createDataKey="createConfigChildProduct1"/> + </createData> + <createData entity="ApiSimpleTwo" stepKey="createConfigChildProduct2"> + <requiredEntity createDataKey="createConfigProductAttribute"/> + <requiredEntity createDataKey="getConfigAttributeOption2"/> + </createData> + <createData entity="ApiProductAttributeMediaGalleryEntryMagentoLogo" stepKey="createConfigChildProduct2Image"> + <requiredEntity createDataKey="createConfigChildProduct2"/> + </createData> + <createData entity="ConfigurableProductTwoOptions" stepKey="createConfigProductOption"> + <requiredEntity createDataKey="createConfigProduct"/> + <requiredEntity createDataKey="createConfigProductAttribute"/> + <requiredEntity createDataKey="getConfigAttributeOption1"/> + <requiredEntity createDataKey="getConfigAttributeOption2"/> + </createData> + <createData entity="ConfigurableProductAddChild" stepKey="createConfigProductAddChild1"> + <requiredEntity createDataKey="createConfigProduct"/> + <requiredEntity createDataKey="createConfigChildProduct1"/> + </createData> + <createData entity="ConfigurableProductAddChild" stepKey="createConfigProductAddChild2"> + <requiredEntity createDataKey="createConfigProduct"/> + <requiredEntity createDataKey="createConfigChildProduct2"/> + </createData> + <createData entity="ApiProductAttributeMediaGalleryEntryTestImage" stepKey="createConfigProductImage"> + <requiredEntity createDataKey="createConfigProduct"/> + </createData> + <updateData entity="ApiSimpleProductUpdateDescription" stepKey="updateConfigProduct" createDataKey="createConfigProduct"/> + </before> + <after> + <!-- @TODO: Uncomment once MQE-679 is fixed --> + <!--<deleteData createDataKey="createConfigChildProduct1Image" stepKey="deleteConfigChildProduct1Image"/>--> + <deleteData createDataKey="createConfigChildProduct1" stepKey="deleteConfigChildProduct1"/> + <!-- @TODO: Uncomment once MQE-679 is fixed --> + <!--<deleteData createDataKey="createConfigChildProduct2Image" stepKey="deleteConfigChildProduct2Image"/>--> + <deleteData createDataKey="createConfigChildProduct2" stepKey="deleteConfigChildProduct2"/> + <!-- @TODO: Uncomment once MQE-679 is fixed --> + <!--<deleteData createDataKey="createConfigProductImage" stepKey="deleteConfigProductImage"/>--> + <deleteData createDataKey="createConfigProduct" stepKey="deleteConfigProduct"/> + <deleteData createDataKey="createConfigProductAttribute" stepKey="deleteConfigProductAttribute"/> + + <!-- Reindex invalidated indices after product attribute has been created/deleted --> + <magentoCron groups="index" stepKey="reindexInvalidatedIndices"/> + </after> + + <!-- Verify Configurable Product in checkout cart items --> + <comment userInput="Verify Configurable Product in checkout cart items" stepKey="commentVerifyConfigurableProductInCheckoutCartItems" after="guestCheckoutCheckSimpleProduct2InCartItems"/> + <actionGroup ref="CheckConfigurableProductInCheckoutCartItemsActionGroup" stepKey="guestCheckoutCheckConfigurableProductInCartItems" after="commentVerifyConfigurableProductInCheckoutCartItems"> + <argument name="productVar" value="$$createConfigProduct$$"/> + <argument name="optionLabel" value="$$createConfigProductAttribute.attribute[frontend_labels][0][label]$$"/> + <argument name="optionValue" value="$$createConfigProductAttributeOption2.option[store_labels][1][label]$$"/> + </actionGroup> + + <!-- Check configurable product in category --> + <comment userInput="Verify Configurable Product in category" stepKey="commentVerifyConfigurableProductInCategory" after="browseAssertSimpleProduct2ImageNotDefault"/> + <actionGroup ref="StorefrontCheckCategoryConfigurableProductActionGroup" stepKey="browseAssertCategoryConfigProduct" after="commentVerifyConfigurableProductInCategory"> + <argument name="product" value="$$createConfigProduct$$"/> + <argument name="optionProduct" value="$$createConfigChildProduct1$$"/> + </actionGroup> + <!-- @TODO: Move Image check to action group after MQE-697 is fixed --> + <grabAttributeFrom selector="{{StorefrontCategoryProductSection.ProductImageByName($$createConfigProduct.name$$)}}" userInput="src" stepKey="browseGrabConfigProductImageSrc" after="browseAssertCategoryConfigProduct"/> + <assertNotRegExp stepKey="browseAssertConfigProductImageNotDefault" after="browseGrabConfigProductImageSrc"> + <actualResult type="const">$browseGrabConfigProductImageSrc</actualResult> + <expectedResult type="const">'/placeholder\/small_image\.jpg/'</expectedResult> + </assertNotRegExp> + + <!-- View Configurable Product --> + <comment userInput="View Configurable Product" stepKey="commentViewConfigurableProduct" after="browseAssertSimpleProduct2PageImageNotDefault"/> + <click selector="{{StorefrontHeaderSection.NavigationCategoryByName($$createCategory.name$$)}}" stepKey="clickCategory2" after="commentViewConfigurableProduct"/> + <click selector="{{StorefrontCategoryProductSection.ProductTitleByName($$createConfigProduct.name$$)}}" stepKey="browseClickCategoryConfigProductView" after="clickCategory2"/> + <waitForLoadingMaskToDisappear stepKey="waitForConfigurableProductViewloaded" after="browseClickCategoryConfigProductView"/> + <actionGroup ref="StorefrontCheckConfigurableProductActionGroup" stepKey="browseAssertConfigProductPage" after="waitForConfigurableProductViewloaded"> + <argument name="product" value="$$createConfigProduct$$"/> + <argument name="optionProduct" value="$$createConfigChildProduct1$$"/> + </actionGroup> + <!-- @TODO: Move Image check to action group after MQE-697 is fixed --> + <grabAttributeFrom selector="{{StorefrontProductInfoMainSection.productImage}}" userInput="src" stepKey="browseGrabConfigProductPageImageSrc" after="browseAssertConfigProductPage"/> + <assertNotRegExp stepKey="browseAssertConfigProductPageImageNotDefault" after="browseGrabConfigProductPageImageSrc"> + <actualResult type="const">$browseGrabConfigProductPageImageSrc</actualResult> + <expectedResult type="const">'/placeholder\/image\.jpg/'</expectedResult> + </assertNotRegExp> + + <!-- Add Configurable Product to cart --> + <comment userInput="Add Configurable Product to cart" stepKey="commentAddConfigurableProductToCart" after="cartAddProduct2ToCart"/> + <click selector="{{StorefrontHeaderSection.NavigationCategoryByName($$createCategory.name$$)}}" stepKey="cartClickCategory2" after="commentAddConfigurableProductToCart"/> + <waitForLoadingMaskToDisappear stepKey="waitForCartCategory2loaded" after="cartClickCategory2"/> + <actionGroup ref="StorefrontCheckCategoryActionGroup" stepKey="cartAssertCategory1ForConfigurableProduct" after="waitForCartCategory2loaded"> + <argument name="category" value="$$createCategory$$"/> + <argument name="productCount" value="3"/> + </actionGroup> + <actionGroup ref="StorefrontCheckCategoryConfigurableProductActionGroup" stepKey="cartAssertConfigProduct" after="cartAssertCategory1ForConfigurableProduct"> + <argument name="product" value="$$createConfigProduct$$"/> + <argument name="optionProduct" value="$$createConfigChildProduct1$$"/> + </actionGroup> + <!-- @TODO: Move Image check to action group after MQE-697 is fixed --> + <grabAttributeFrom selector="{{StorefrontCategoryProductSection.ProductImageByName($$createConfigProduct.name$$)}}" userInput="src" stepKey="cartGrabConfigProductImageSrc" after="cartAssertConfigProduct"/> + <assertNotRegExp stepKey="cartAssertConfigProductImageNotDefault" after="cartGrabConfigProductImageSrc"> + <actualResult type="const">$cartGrabConfigProductImageSrc</actualResult> + <expectedResult type="const">'/placeholder\/small_image\.jpg/'</expectedResult> + </assertNotRegExp> + <click selector="{{StorefrontCategoryProductSection.ProductAddToCartByName($$createConfigProduct.name$$)}}" stepKey="cartClickCategoryConfigProductAddToCart" after="cartAssertConfigProductImageNotDefault"/> + <waitForElement selector="{{StorefrontMessagesSection.message('You need to choose options for your item.')}}" time="30" stepKey="cartWaitForConfigProductPageLoad" after="cartClickCategoryConfigProductAddToCart"/> + <actionGroup ref="StorefrontCheckConfigurableProductActionGroup" stepKey="cartAssertConfigProductPage" after="cartWaitForConfigProductPageLoad"> + <argument name="product" value="$$createConfigProduct$$"/> + <argument name="optionProduct" value="$$createConfigChildProduct1$$"/> + </actionGroup> + <!-- @TODO: Move Image check to action group after MQE-697 is fixed --> + <grabAttributeFrom selector="{{StorefrontProductInfoMainSection.productImage}}" userInput="src" stepKey="cartGrabConfigProductPageImageSrc1" after="cartAssertConfigProductPage"/> + <assertNotRegExp stepKey="cartAssertConfigProductPageImageNotDefault1" after="cartGrabConfigProductPageImageSrc1"> + <actualResult type="const">$cartGrabConfigProductPageImageSrc1</actualResult> + <expectedResult type="const">'/placeholder\/image\.jpg/'</expectedResult> + </assertNotRegExp> + <selectOption userInput="$$createConfigProductAttributeOption2.option[store_labels][1][label]$$" selector="{{StorefrontProductInfoMainSection.optionByAttributeId($$createConfigProductAttribute.attribute_id$$)}}" stepKey="cartConfigProductFillOption" after="cartAssertConfigProductPageImageNotDefault1"/> + <waitForLoadingMaskToDisappear stepKey="waitForConfigurableProductOptionloaded" after="cartConfigProductFillOption"/> + <actionGroup ref="StorefrontCheckConfigurableProductActionGroup" stepKey="cartAssertConfigProductWithOptionPage" after="waitForConfigurableProductOptionloaded"> + <argument name="product" value="$$createConfigProduct$$"/> + <argument name="optionProduct" value="$$createConfigChildProduct2$$"/> + </actionGroup> + <!-- @TODO: Move Image check to action group after MQE-697 is fixed --> + <grabAttributeFrom selector="{{StorefrontProductInfoMainSection.productImage}}" userInput="src" stepKey="cartGrabConfigProductPageImageSrc2" after="cartAssertConfigProductWithOptionPage"/> + <assertNotRegExp stepKey="cartAssertConfigProductPageImageNotDefault2" after="cartGrabConfigProductPageImageSrc2"> + <actualResult type="const">$cartGrabConfigProductPageImageSrc2</actualResult> + <expectedResult type="const">'/placeholder\/image\.jpg/'</expectedResult> + </assertNotRegExp> + <actionGroup ref="StorefrontAddProductToCartActionGroup" stepKey="cartAddConfigProductToCart" after="cartAssertConfigProductPageImageNotDefault2"> + <argument name="product" value="$$createConfigProduct$$"/> + <argument name="productCount" value="3"/> + </actionGroup> + + <!-- Check configurable product in minicart --> + <comment userInput="Check configurable product in minicart" stepKey="commentCheckConfigurableProductInMinicart" after="cartMinicartAssertSimpleProduct2PageImageNotDefault"/> + <actionGroup ref="StorefrontOpenMinicartAndCheckConfigurableProductActionGroup" stepKey="cartOpenMinicartAndCheckConfigProduct" after="commentCheckConfigurableProductInMinicart"> + <argument name="product" value="$$createConfigProduct$$"/> + <argument name="optionProduct" value="$$createConfigChildProduct2$$"/> + </actionGroup> + <!-- @TODO: Move Image check to action group after MQE-697 is fixed --> + <grabAttributeFrom selector="{{StorefrontMinicartSection.productImageByName($$createConfigProduct.name$$)}}" userInput="src" stepKey="cartMinicartGrabConfigProductImageSrc" after="cartOpenMinicartAndCheckConfigProduct"/> + <assertNotRegExp stepKey="cartMinicartAssertConfigProductImageNotDefault" after="cartMinicartGrabConfigProductImageSrc"> + <actualResult type="const">$cartMinicartGrabConfigProductImageSrc</actualResult> + <expectedResult type="const">'/placeholder\/thumbnail\.jpg/'</expectedResult> + </assertNotRegExp> + <click selector="{{StorefrontMinicartSection.productOptionsDetailsByName($$createConfigProduct.name$$)}}" stepKey="cartMinicartClickConfigProductDetails" after="cartMinicartAssertConfigProductImageNotDefault"/> + <see userInput="$$createConfigProductAttributeOption2.option[store_labels][1][label]$$" selector="{{StorefrontMinicartSection.productOptionByNameAndAttribute($$createConfigProduct.name$$, $$createConfigProductAttribute.attribute[frontend_labels][0][label]$$)}}" stepKey="cartMinicartCheckConfigProductOption" after="cartMinicartClickConfigProductDetails"/> + <click selector="{{StorefrontMinicartSection.productLinkByName($$createConfigProduct.name$$)}}" stepKey="cartMinicartClickConfigProduct" after="cartMinicartCheckConfigProductOption"/> + <waitForLoadingMaskToDisappear stepKey="waitForMinicartConfigProductloaded" after="cartMinicartClickConfigProduct"/> + <actionGroup ref="StorefrontCheckConfigurableProductActionGroup" stepKey="cartAssertMinicartConfigProductPage" after="waitForMinicartConfigProductloaded"> + <argument name="product" value="$$createConfigProduct$$"/> + <argument name="optionProduct" value="$$createConfigChildProduct1$$"/> + </actionGroup> + <!-- @TODO: Move Image check to action group after MQE-697 is fixed --> + <grabAttributeFrom selector="{{StorefrontProductInfoMainSection.productImage}}" userInput="src" stepKey="cartMinicartGrabConfigProductPageImageSrc" after="cartAssertMinicartConfigProductPage"/> + <assertNotRegExp stepKey="cartMinicartAssertConfigProductPageImageNotDefault" after="cartMinicartGrabConfigProductPageImageSrc"> + <actualResult type="const">$cartMinicartGrabConfigProductPageImageSrc</actualResult> + <expectedResult type="const">'/placeholder\/image\.jpg/'</expectedResult> + </assertNotRegExp> + + <!-- Check configurable product in cart --> + <comment userInput="Check configurable product in cart" stepKey="commentCheckConfigurableProductInCart" after="cartCartAssertSimpleProduct2PageImageNotDefault2"/> + <actionGroup ref="StorefrontOpenCartFromMinicartActionGroup" stepKey="cartOpenCart2" after="commentCheckConfigurableProductInCart"/> + <actionGroup ref="StorefrontCheckCartConfigurableProductActionGroup" stepKey="cartAssertCartConfigProduct" after="cartOpenCart2"> + <argument name="product" value="$$createConfigProduct$$"/> + <argument name="optionProduct" value="$$createConfigChildProduct2$$"/> + <!-- @TODO: Change to scalar value after MQE-498 is implemented --> + <argument name="productQuantity" value="CONST.one"/> + </actionGroup> + <!-- @TODO: Move Image check to action group after MQE-697 is fixed --> + <grabAttributeFrom selector="{{CheckoutCartProductSection.ProductImageByName($$createConfigProduct.name$$)}}" userInput="src" stepKey="cartCartGrabConfigProduct2ImageSrc" after="cartAssertCartConfigProduct"/> + <assertNotRegExp stepKey="cartCartAssertConfigProduct2ImageNotDefault" after="cartCartGrabConfigProduct2ImageSrc"> + <actualResult type="const">$cartCartGrabConfigProduct2ImageSrc</actualResult> + <expectedResult type="const">'/placeholder\/small_image\.jpg/'</expectedResult> + </assertNotRegExp> + <see userInput="$$createConfigProductAttributeOption2.option[store_labels][1][label]$$" selector="{{CheckoutCartProductSection.ProductOptionByNameAndAttribute($$createConfigProduct.name$$, $$createConfigProductAttribute.attribute[frontend_labels][0][label]$$)}}" stepKey="cartCheckConfigProductOption" after="cartCartAssertConfigProduct2ImageNotDefault"/> + <click selector="{{CheckoutCartProductSection.ProductLinkByName($$createConfigProduct.name$$)}}" stepKey="cartClickCartConfigProduct" after="cartCheckConfigProductOption"/> + <waitForLoadingMaskToDisappear stepKey="waitForCartConfigProductloaded" after="cartClickCartConfigProduct"/> + <actionGroup ref="StorefrontCheckConfigurableProductActionGroup" stepKey="cartAssertCartConfigProductPage" after="waitForCartConfigProductloaded"> + <argument name="product" value="$$createConfigProduct$$"/> + <argument name="optionProduct" value="$$createConfigChildProduct1$$"/> + </actionGroup> + <!-- @TODO: Move Image check to action group after MQE-697 is fixed --> + <grabAttributeFrom selector="{{StorefrontProductInfoMainSection.productImage}}" userInput="src" stepKey="cartCartGrabConfigProductPageImageSrc" after="cartAssertCartConfigProductPage"/> + <assertNotRegExp stepKey="cartCartAssertConfigProductPageImageNotDefault" after="cartCartGrabConfigProductPageImageSrc"> + <actualResult type="const">$cartCartGrabConfigProductPageImageSrc</actualResult> + <expectedResult type="const">'/placeholder\/image\.jpg/'</expectedResult> + </assertNotRegExp> + + <!-- Add Configurable Product to comparison --> + <comment userInput="Add Configurable Product to comparison" stepKey="commentAddConfigurableProductToComparison" after="compareAddSimpleProduct2ToCompare"/> + <actionGroup ref="StorefrontCheckCategoryConfigurableProductActionGroup" stepKey="compareAssertConfigProduct" after="commentAddConfigurableProductToComparison"> + <argument name="product" value="$$createConfigProduct$$"/> + <argument name="optionProduct" value="$$createConfigChildProduct1$$"/> + </actionGroup> + <!-- @TODO: Move Image check to action group after MQE-697 is fixed --> + <grabAttributeFrom selector="{{StorefrontCategoryProductSection.ProductImageByName($$createConfigProduct.name$$)}}" userInput="src" stepKey="compareGrabConfigProductImageSrc" after="compareAssertConfigProduct"/> + <assertNotRegExp stepKey="compareAssertConfigProductImageNotDefault" after="compareGrabConfigProductImageSrc"> + <actualResult type="const">$compareGrabConfigProductImageSrc</actualResult> + <expectedResult type="const">'/placeholder\/small_image\.jpg/'</expectedResult> + </assertNotRegExp> + <actionGroup ref="StorefrontAddCategoryProductToCompareActionGroup" stepKey="compareAddConfigProductToCompare" after="compareAssertConfigProductImageNotDefault"> + <argument name="productVar" value="$$createConfigProduct$$"/> + </actionGroup> + + <!-- Check configurable product in comparison sidebar --> + <comment userInput="Add Configurable Product in comparison sidebar" stepKey="commentAddConfigurableProductInComparisonSidebar" after="compareSimpleProduct2InSidebar"/> + <actionGroup ref="StorefrontCheckCompareSidebarProductActionGroup" stepKey="compareConfigProductInSidebar" after="commentAddConfigurableProductInComparisonSidebar"> + <argument name="productVar" value="$$createConfigProduct$$"/> + </actionGroup> + + <!-- Check configurable product on comparison page --> + <comment userInput="Add Configurable Product on comparison page" stepKey="commentAddConfigurableProductOnComparisonPage" after="compareAssertSimpleProduct2ImageNotDefaultInComparison"/> + <actionGroup ref="StorefrontCheckCompareConfigurableProductActionGroup" stepKey="compareAssertConfigProductInComparison" after="commentAddConfigurableProductOnComparisonPage"> + <argument name="product" value="$$createConfigProduct$$"/> + <argument name="optionProduct" value="$$createConfigChildProduct1$$"/> + </actionGroup> + <!-- @TODO: Move Image check to action group after MQE-697 is fixed --> + <grabAttributeFrom selector="{{StorefrontProductCompareMainSection.ProductImageByName($$createConfigProduct.name$$)}}" userInput="src" stepKey="compareGrabConfigProductImageSrcInComparison" after="compareAssertConfigProductInComparison"/> + <assertNotRegExp stepKey="compareAssertConfigProductImageNotDefaultInComparison" after="compareGrabConfigProductImageSrcInComparison"> + <actualResult type="const">$compareGrabConfigProductImageSrcInComparison</actualResult> + <expectedResult type="const">'/placeholder\/small_image\.jpg/'</expectedResult> + </assertNotRegExp> + </test> +</tests> diff --git a/app/code/Magento/ConfigurableProduct/Test/Mftf/Test/EndToEndB2CGuestUserTest/EndToEndB2CGuestUserTest.xml b/app/code/Magento/ConfigurableProduct/Test/Mftf/Test/EndToEndB2CGuestUserTest/EndToEndB2CGuestUserTest.xml new file mode 100644 index 0000000000000..02f054e405bb7 --- /dev/null +++ b/app/code/Magento/ConfigurableProduct/Test/Mftf/Test/EndToEndB2CGuestUserTest/EndToEndB2CGuestUserTest.xml @@ -0,0 +1,255 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + /** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +--> +<tests xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/testSchema.xsd"> + <test name="EndToEndB2CGuestUserTest"> + <before> + <createData entity="ApiConfigurableProduct" stepKey="createConfigProduct"> + <requiredEntity createDataKey="createCategory"/> + </createData> + <createData entity="productAttributeWithTwoOptions" stepKey="createConfigProductAttribute"/> + <createData entity="productAttributeOption1" stepKey="createConfigProductAttributeOption1"> + <requiredEntity createDataKey="createConfigProductAttribute"/> + </createData> + <createData entity="productAttributeOption2" stepKey="createConfigProductAttributeOption2"> + <requiredEntity createDataKey="createConfigProductAttribute"/> + </createData> + <createData entity="AddToDefaultSet" stepKey="createConfigAddToAttributeSet"> + <requiredEntity createDataKey="createConfigProductAttribute"/> + </createData> + <getData entity="ProductAttributeOptionGetter" index="1" stepKey="getConfigAttributeOption1"> + <requiredEntity createDataKey="createConfigProductAttribute"/> + </getData> + <getData entity="ProductAttributeOptionGetter" index="2" stepKey="getConfigAttributeOption2"> + <requiredEntity createDataKey="createConfigProductAttribute"/> + </getData> + <createData entity="ApiSimpleOne" stepKey="createConfigChildProduct1"> + <requiredEntity createDataKey="createConfigProductAttribute"/> + <requiredEntity createDataKey="getConfigAttributeOption1"/> + </createData> + <createData entity="ApiProductAttributeMediaGalleryEntryTestImage" stepKey="createConfigChildProduct1Image"> + <requiredEntity createDataKey="createConfigChildProduct1"/> + </createData> + <createData entity="ApiSimpleTwo" stepKey="createConfigChildProduct2"> + <requiredEntity createDataKey="createConfigProductAttribute"/> + <requiredEntity createDataKey="getConfigAttributeOption2"/> + </createData> + <createData entity="ApiProductAttributeMediaGalleryEntryMagentoLogo" stepKey="createConfigChildProduct2Image"> + <requiredEntity createDataKey="createConfigChildProduct2"/> + </createData> + <createData entity="ConfigurableProductTwoOptions" stepKey="createConfigProductOption"> + <requiredEntity createDataKey="createConfigProduct"/> + <requiredEntity createDataKey="createConfigProductAttribute"/> + <requiredEntity createDataKey="getConfigAttributeOption1"/> + <requiredEntity createDataKey="getConfigAttributeOption2"/> + </createData> + <createData entity="ConfigurableProductAddChild" stepKey="createConfigProductAddChild1"> + <requiredEntity createDataKey="createConfigProduct"/> + <requiredEntity createDataKey="createConfigChildProduct1"/> + </createData> + <createData entity="ConfigurableProductAddChild" stepKey="createConfigProductAddChild2"> + <requiredEntity createDataKey="createConfigProduct"/> + <requiredEntity createDataKey="createConfigChildProduct2"/> + </createData> + <createData entity="ApiProductAttributeMediaGalleryEntryTestImage" stepKey="createConfigProductImage"> + <requiredEntity createDataKey="createConfigProduct"/> + </createData> + <updateData entity="ApiSimpleProductUpdateDescription" stepKey="updateConfigProduct" createDataKey="createConfigProduct"/> + </before> + <after> + <!-- @TODO: Uncomment once MQE-679 is fixed --> + <!--<deleteData createDataKey="createConfigChildProduct1Image" stepKey="deleteConfigChildProduct1Image"/>--> + <deleteData createDataKey="createConfigChildProduct1" stepKey="deleteConfigChildProduct1"/> + <!-- @TODO: Uncomment once MQE-679 is fixed --> + <!--<deleteData createDataKey="createConfigChildProduct2Image" stepKey="deleteConfigChildProduct2Image"/>--> + <deleteData createDataKey="createConfigChildProduct2" stepKey="deleteConfigChildProduct2"/> + <!-- @TODO: Uncomment once MQE-679 is fixed --> + <!--<deleteData createDataKey="createConfigProductImage" stepKey="deleteConfigProductImage"/>--> + <deleteData createDataKey="createConfigProduct" stepKey="deleteConfigProduct"/> + <deleteData createDataKey="createConfigProductAttribute" stepKey="deleteConfigProductAttribute"/> + + <!-- Reindex invalidated indices after product attribute has been created/deleted --> + <magentoCron groups="index" stepKey="reindexInvalidatedIndices"/> + </after> + + <!-- Verify Configurable Product in checkout cart items --> + <comment userInput="Verify Configurable Product in checkout cart items" stepKey="commentVerifyConfigurableProductInCheckoutCartItems" after="guestCheckoutCheckSimpleProduct2InCartItems"/> + <actionGroup ref="CheckConfigurableProductInCheckoutCartItemsActionGroup" stepKey="guestCheckoutCheckConfigurableProductInCartItems" after="commentVerifyConfigurableProductInCheckoutCartItems"> + <argument name="productVar" value="$$createConfigProduct$$"/> + <argument name="optionLabel" value="$$createConfigProductAttribute.attribute[frontend_labels][0][label]$$"/> + <argument name="optionValue" value="$$createConfigProductAttributeOption2.option[store_labels][1][label]$$"/> + </actionGroup> + + <!-- Check configurable product in category --> + <comment userInput="Verify Configurable Product in category" stepKey="commentVerifyConfigurableProductInCategory" after="browseAssertSimpleProduct2ImageNotDefault"/> + <actionGroup ref="StorefrontCheckCategoryConfigurableProductActionGroup" stepKey="browseAssertCategoryConfigProduct" after="commentVerifyConfigurableProductInCategory"> + <argument name="product" value="$$createConfigProduct$$"/> + <argument name="optionProduct" value="$$createConfigChildProduct1$$"/> + </actionGroup> + <!-- @TODO: Move Image check to action group after MQE-697 is fixed --> + <grabAttributeFrom selector="{{StorefrontCategoryProductSection.ProductImageByName($$createConfigProduct.name$$)}}" userInput="src" stepKey="browseGrabConfigProductImageSrc" after="browseAssertCategoryConfigProduct"/> + <assertNotRegExp stepKey="browseAssertConfigProductImageNotDefault" after="browseGrabConfigProductImageSrc"> + <actualResult type="const">$browseGrabConfigProductImageSrc</actualResult> + <expectedResult type="const">'/placeholder\/small_image\.jpg/'</expectedResult> + </assertNotRegExp> + + <!-- View Configurable Product --> + <comment userInput="View Configurable Product" stepKey="commentViewConfigurableProduct" after="browseAssertSimpleProduct2PageImageNotDefault"/> + <click selector="{{StorefrontHeaderSection.NavigationCategoryByName($$createCategory.name$$)}}" stepKey="clickCategory2" after="commentViewConfigurableProduct"/> + <click selector="{{StorefrontCategoryProductSection.ProductTitleByName($$createConfigProduct.name$$)}}" stepKey="browseClickCategoryConfigProductView" after="clickCategory2"/> + <waitForLoadingMaskToDisappear stepKey="waitForConfigurableProductViewloaded" after="browseClickCategoryConfigProductView"/> + <actionGroup ref="StorefrontCheckConfigurableProductActionGroup" stepKey="browseAssertConfigProductPage" after="waitForConfigurableProductViewloaded"> + <argument name="product" value="$$createConfigProduct$$"/> + <argument name="optionProduct" value="$$createConfigChildProduct1$$"/> + </actionGroup> + <!-- @TODO: Move Image check to action group after MQE-697 is fixed --> + <grabAttributeFrom selector="{{StorefrontProductInfoMainSection.productImage}}" userInput="src" stepKey="browseGrabConfigProductPageImageSrc" after="browseAssertConfigProductPage"/> + <assertNotRegExp stepKey="browseAssertConfigProductPageImageNotDefault" after="browseGrabConfigProductPageImageSrc"> + <actualResult type="const">$browseGrabConfigProductPageImageSrc</actualResult> + <expectedResult type="const">'/placeholder\/image\.jpg/'</expectedResult> + </assertNotRegExp> + + <!-- Add Configurable Product to cart --> + <comment userInput="Add Configurable Product to cart" stepKey="commentAddConfigurableProductToCart" after="cartAddProduct2ToCart"/> + <click selector="{{StorefrontHeaderSection.NavigationCategoryByName($$createCategory.name$$)}}" stepKey="cartClickCategory2" after="commentAddConfigurableProductToCart"/> + <waitForLoadingMaskToDisappear stepKey="waitForCartCategory2loaded" after="cartClickCategory2"/> + <actionGroup ref="StorefrontCheckCategoryActionGroup" stepKey="cartAssertCategory1ForConfigurableProduct" after="waitForCartCategory2loaded"> + <argument name="category" value="$$createCategory$$"/> + <argument name="productCount" value="3"/> + </actionGroup> + <actionGroup ref="StorefrontCheckCategoryConfigurableProductActionGroup" stepKey="cartAssertConfigProduct" after="cartAssertCategory1ForConfigurableProduct"> + <argument name="product" value="$$createConfigProduct$$"/> + <argument name="optionProduct" value="$$createConfigChildProduct1$$"/> + </actionGroup> + <!-- @TODO: Move Image check to action group after MQE-697 is fixed --> + <grabAttributeFrom selector="{{StorefrontCategoryProductSection.ProductImageByName($$createConfigProduct.name$$)}}" userInput="src" stepKey="cartGrabConfigProductImageSrc" after="cartAssertConfigProduct"/> + <assertNotRegExp stepKey="cartAssertConfigProductImageNotDefault" after="cartGrabConfigProductImageSrc"> + <actualResult type="const">$cartGrabConfigProductImageSrc</actualResult> + <expectedResult type="const">'/placeholder\/small_image\.jpg/'</expectedResult> + </assertNotRegExp> + <click selector="{{StorefrontCategoryProductSection.ProductAddToCartByName($$createConfigProduct.name$$)}}" stepKey="cartClickCategoryConfigProductAddToCart" after="cartAssertConfigProductImageNotDefault"/> + <waitForElement selector="{{StorefrontMessagesSection.message('You need to choose options for your item.')}}" time="30" stepKey="cartWaitForConfigProductPageLoad" after="cartClickCategoryConfigProductAddToCart"/> + <actionGroup ref="StorefrontCheckConfigurableProductActionGroup" stepKey="cartAssertConfigProductPage" after="cartWaitForConfigProductPageLoad"> + <argument name="product" value="$$createConfigProduct$$"/> + <argument name="optionProduct" value="$$createConfigChildProduct1$$"/> + </actionGroup> + <!-- @TODO: Move Image check to action group after MQE-697 is fixed --> + <grabAttributeFrom selector="{{StorefrontProductInfoMainSection.productImage}}" userInput="src" stepKey="cartGrabConfigProductPageImageSrc1" after="cartAssertConfigProductPage"/> + <assertNotRegExp stepKey="cartAssertConfigProductPageImageNotDefault1" after="cartGrabConfigProductPageImageSrc1"> + <actualResult type="const">$cartGrabConfigProductPageImageSrc1</actualResult> + <expectedResult type="const">'/placeholder\/image\.jpg/'</expectedResult> + </assertNotRegExp> + <selectOption userInput="$$createConfigProductAttributeOption2.option[store_labels][1][label]$$" selector="{{StorefrontProductInfoMainSection.optionByAttributeId($$createConfigProductAttribute.attribute_id$$)}}" stepKey="cartConfigProductFillOption" after="cartAssertConfigProductPageImageNotDefault1"/> + <waitForLoadingMaskToDisappear stepKey="waitForConfigurableProductOptionloaded" after="cartConfigProductFillOption"/> + <actionGroup ref="StorefrontCheckConfigurableProductActionGroup" stepKey="cartAssertConfigProductWithOptionPage" after="waitForConfigurableProductOptionloaded"> + <argument name="product" value="$$createConfigProduct$$"/> + <argument name="optionProduct" value="$$createConfigChildProduct2$$"/> + </actionGroup> + <!-- @TODO: Move Image check to action group after MQE-697 is fixed --> + <grabAttributeFrom selector="{{StorefrontProductInfoMainSection.productImage}}" userInput="src" stepKey="cartGrabConfigProductPageImageSrc2" after="cartAssertConfigProductWithOptionPage"/> + <assertNotRegExp stepKey="cartAssertConfigProductPageImageNotDefault2" after="cartGrabConfigProductPageImageSrc2"> + <actualResult type="const">$cartGrabConfigProductPageImageSrc2</actualResult> + <expectedResult type="const">'/placeholder\/image\.jpg/'</expectedResult> + </assertNotRegExp> + <actionGroup ref="StorefrontAddProductToCartActionGroup" stepKey="cartAddConfigProductToCart" after="cartAssertConfigProductPageImageNotDefault2"> + <argument name="product" value="$$createConfigProduct$$"/> + <argument name="productCount" value="3"/> + </actionGroup> + + <!-- Check configurable product in minicart --> + <comment userInput="Check configurable product in minicart" stepKey="commentCheckConfigurableProductInMinicart" after="cartMinicartAssertSimpleProduct2PageImageNotDefault"/> + <actionGroup ref="StorefrontOpenMinicartAndCheckConfigurableProductActionGroup" stepKey="cartOpenMinicartAndCheckConfigProduct" after="commentCheckConfigurableProductInMinicart"> + <argument name="product" value="$$createConfigProduct$$"/> + <argument name="optionProduct" value="$$createConfigChildProduct2$$"/> + </actionGroup> + <!-- @TODO: Move Image check to action group after MQE-697 is fixed --> + <grabAttributeFrom selector="{{StorefrontMinicartSection.productImageByName($$createConfigProduct.name$$)}}" userInput="src" stepKey="cartMinicartGrabConfigProductImageSrc" after="cartOpenMinicartAndCheckConfigProduct"/> + <assertNotRegExp stepKey="cartMinicartAssertConfigProductImageNotDefault" after="cartMinicartGrabConfigProductImageSrc"> + <actualResult type="const">$cartMinicartGrabConfigProductImageSrc</actualResult> + <expectedResult type="const">'/placeholder\/thumbnail\.jpg/'</expectedResult> + </assertNotRegExp> + <click selector="{{StorefrontMinicartSection.productOptionsDetailsByName($$createConfigProduct.name$$)}}" stepKey="cartMinicartClickConfigProductDetails" after="cartMinicartAssertConfigProductImageNotDefault"/> + <see userInput="$$createConfigProductAttributeOption2.option[store_labels][1][label]$$" selector="{{StorefrontMinicartSection.productOptionByNameAndAttribute($$createConfigProduct.name$$, $$createConfigProductAttribute.attribute[frontend_labels][0][label]$$)}}" stepKey="cartMinicartCheckConfigProductOption" after="cartMinicartClickConfigProductDetails"/> + <click selector="{{StorefrontMinicartSection.productLinkByName($$createConfigProduct.name$$)}}" stepKey="cartMinicartClickConfigProduct" after="cartMinicartCheckConfigProductOption"/> + <waitForLoadingMaskToDisappear stepKey="waitForMinicartConfigProductloaded" after="cartMinicartClickConfigProduct"/> + <actionGroup ref="StorefrontCheckConfigurableProductActionGroup" stepKey="cartAssertMinicartConfigProductPage" after="waitForMinicartConfigProductloaded"> + <argument name="product" value="$$createConfigProduct$$"/> + <argument name="optionProduct" value="$$createConfigChildProduct1$$"/> + </actionGroup> + <!-- @TODO: Move Image check to action group after MQE-697 is fixed --> + <grabAttributeFrom selector="{{StorefrontProductInfoMainSection.productImage}}" userInput="src" stepKey="cartMinicartGrabConfigProductPageImageSrc" after="cartAssertMinicartConfigProductPage"/> + <assertNotRegExp stepKey="cartMinicartAssertConfigProductPageImageNotDefault" after="cartMinicartGrabConfigProductPageImageSrc"> + <actualResult type="const">$cartMinicartGrabConfigProductPageImageSrc</actualResult> + <expectedResult type="const">'/placeholder\/image\.jpg/'</expectedResult> + </assertNotRegExp> + + <!-- Check configurable product in cart --> + <comment userInput="Check configurable product in cart" stepKey="commentCheckConfigurableProductInCart" after="cartCartAssertSimpleProduct2PageImageNotDefault2"/> + <actionGroup ref="StorefrontOpenCartFromMinicartActionGroup" stepKey="cartOpenCart2" after="commentCheckConfigurableProductInCart"/> + <actionGroup ref="StorefrontCheckCartConfigurableProductActionGroup" stepKey="cartAssertCartConfigProduct" after="cartOpenCart2"> + <argument name="product" value="$$createConfigProduct$$"/> + <argument name="optionProduct" value="$$createConfigChildProduct2$$"/> + <!-- @TODO: Change to scalar value after MQE-498 is implemented --> + <argument name="productQuantity" value="CONST.one"/> + </actionGroup> + <!-- @TODO: Move Image check to action group after MQE-697 is fixed --> + <grabAttributeFrom selector="{{CheckoutCartProductSection.ProductImageByName($$createConfigProduct.name$$)}}" userInput="src" stepKey="cartCartGrabConfigProduct2ImageSrc" after="cartAssertCartConfigProduct"/> + <assertNotRegExp stepKey="cartCartAssertConfigProduct2ImageNotDefault" after="cartCartGrabConfigProduct2ImageSrc"> + <actualResult type="const">$cartCartGrabConfigProduct2ImageSrc</actualResult> + <expectedResult type="const">'/placeholder\/small_image\.jpg/'</expectedResult> + </assertNotRegExp> + <see userInput="$$createConfigProductAttributeOption2.option[store_labels][1][label]$$" selector="{{CheckoutCartProductSection.ProductOptionByNameAndAttribute($$createConfigProduct.name$$, $$createConfigProductAttribute.attribute[frontend_labels][0][label]$$)}}" stepKey="cartCheckConfigProductOption" after="cartCartAssertConfigProduct2ImageNotDefault"/> + <click selector="{{CheckoutCartProductSection.ProductLinkByName($$createConfigProduct.name$$)}}" stepKey="cartClickCartConfigProduct" after="cartCheckConfigProductOption"/> + <waitForLoadingMaskToDisappear stepKey="waitForCartConfigProductloaded" after="cartClickCartConfigProduct"/> + <actionGroup ref="StorefrontCheckConfigurableProductActionGroup" stepKey="cartAssertCartConfigProductPage" after="waitForCartConfigProductloaded"> + <argument name="product" value="$$createConfigProduct$$"/> + <argument name="optionProduct" value="$$createConfigChildProduct1$$"/> + </actionGroup> + <!-- @TODO: Move Image check to action group after MQE-697 is fixed --> + <grabAttributeFrom selector="{{StorefrontProductInfoMainSection.productImage}}" userInput="src" stepKey="cartCartGrabConfigProductPageImageSrc" after="cartAssertCartConfigProductPage"/> + <assertNotRegExp stepKey="cartCartAssertConfigProductPageImageNotDefault" after="cartCartGrabConfigProductPageImageSrc"> + <actualResult type="const">$cartCartGrabConfigProductPageImageSrc</actualResult> + <expectedResult type="const">'/placeholder\/image\.jpg/'</expectedResult> + </assertNotRegExp> + + <!-- Add Configurable Product to comparison --> + <comment userInput="Add Configurable Product to comparison" stepKey="commentAddConfigurableProductToComparison" after="compareAddSimpleProduct2ToCompare"/> + <actionGroup ref="StorefrontCheckCategoryConfigurableProductActionGroup" stepKey="compareAssertConfigProduct" after="commentAddConfigurableProductToComparison"> + <argument name="product" value="$$createConfigProduct$$"/> + <argument name="optionProduct" value="$$createConfigChildProduct1$$"/> + </actionGroup> + <!-- @TODO: Move Image check to action group after MQE-697 is fixed --> + <grabAttributeFrom selector="{{StorefrontCategoryProductSection.ProductImageByName($$createConfigProduct.name$$)}}" userInput="src" stepKey="compareGrabConfigProductImageSrc" after="compareAssertConfigProduct"/> + <assertNotRegExp stepKey="compareAssertConfigProductImageNotDefault" after="compareGrabConfigProductImageSrc"> + <actualResult type="const">$compareGrabConfigProductImageSrc</actualResult> + <expectedResult type="const">'/placeholder\/small_image\.jpg/'</expectedResult> + </assertNotRegExp> + <actionGroup ref="StorefrontAddCategoryProductToCompareActionGroup" stepKey="compareAddConfigProductToCompare" after="compareAssertConfigProductImageNotDefault"> + <argument name="productVar" value="$$createConfigProduct$$"/> + </actionGroup> + + <!-- Check configurable product in comparison sidebar --> + <comment userInput="Add Configurable Product in comparison sidebar" stepKey="commentAddConfigurableProductInComparisonSidebar" after="compareSimpleProduct2InSidebar"/> + <actionGroup ref="StorefrontCheckCompareSidebarProductActionGroup" stepKey="compareConfigProductInSidebar" after="commentAddConfigurableProductInComparisonSidebar"> + <argument name="productVar" value="$$createConfigProduct$$"/> + </actionGroup> + + <!-- Check configurable product on comparison page --> + <comment userInput="Add Configurable Product on comparison page" stepKey="commentAddConfigurableProductOnComparisonPage" after="compareAssertSimpleProduct2ImageNotDefaultInComparison"/> + <actionGroup ref="StorefrontCheckCompareConfigurableProductActionGroup" stepKey="compareAssertConfigProductInComparison" after="commentAddConfigurableProductOnComparisonPage"> + <argument name="product" value="$$createConfigProduct$$"/> + <argument name="optionProduct" value="$$createConfigChildProduct1$$"/> + </actionGroup> + <!-- @TODO: Move Image check to action group after MQE-697 is fixed --> + <grabAttributeFrom selector="{{StorefrontProductCompareMainSection.ProductImageByName($$createConfigProduct.name$$)}}" userInput="src" stepKey="compareGrabConfigProductImageSrcInComparison" after="compareAssertConfigProductInComparison"/> + <assertNotRegExp stepKey="compareAssertConfigProductImageNotDefaultInComparison" after="compareGrabConfigProductImageSrcInComparison"> + <actualResult type="const">$compareGrabConfigProductImageSrcInComparison</actualResult> + <expectedResult type="const">'/placeholder\/small_image\.jpg/'</expectedResult> + </assertNotRegExp> + </test> +</tests> diff --git a/app/code/Magento/ConfigurableProduct/Test/Mftf/Test/EndToEndB2CLoggedInUserTest.xml b/app/code/Magento/ConfigurableProduct/Test/Mftf/Test/EndToEndB2CLoggedInUserTest.xml index dd0673563838e..e4a9755adcef4 100644 --- a/app/code/Magento/ConfigurableProduct/Test/Mftf/Test/EndToEndB2CLoggedInUserTest.xml +++ b/app/code/Magento/ConfigurableProduct/Test/Mftf/Test/EndToEndB2CLoggedInUserTest.xml @@ -61,6 +61,8 @@ <requiredEntity createDataKey="createConfigProduct"/> </createData> <updateData entity="ApiSimpleProductUpdateDescription" stepKey="updateConfigProduct" createDataKey="createConfigProduct"/> + <!-- Reindex invalidated indices after product attribute has been created/deleted --> + <magentoCron groups="index" stepKey="reindexInvalidatedIndices"/> </before> <after> <!-- @TODO: Uncomment once MQE-679 is fixed --> @@ -73,6 +75,9 @@ <!--<deleteData createDataKey="createConfigProductImage" stepKey="deleteConfigProductImage"/>--> <deleteData createDataKey="createConfigProduct" stepKey="deleteConfigProduct"/> <deleteData createDataKey="createConfigProductAttribute" stepKey="deleteConfigProductAttribute"/> + + <!-- Reindex invalidated indices after product attribute has been created/deleted --> + <magentoCron groups="index" stepKey="reindexInvalidatedIndices"/> </after> <!-- Verify Configurable Product in checkout cart items --> @@ -91,7 +96,10 @@ </actionGroup> <!-- @TODO: Move Image check to action group after MQE-697 is fixed --> <grabAttributeFrom selector="{{StorefrontCategoryProductSection.ProductImageByName($$createConfigProduct.name$$)}}" userInput="src" stepKey="browseGrabConfigProductImageSrc" after="browseAssertCategoryConfigProduct"/> - <assertNotRegExp expected="'/placeholder\/small_image\.jpg/'" actual="$browseGrabConfigProductImageSrc" stepKey="browseAssertConfigProductImageNotDefault" after="browseGrabConfigProductImageSrc"/> + <assertNotRegExp stepKey="browseAssertConfigProductImageNotDefault" after="browseGrabConfigProductImageSrc"> + <actualResult type="const">$browseGrabConfigProductImageSrc</actualResult> + <expectedResult type="const">'/placeholder\/small_image\.jpg/'</expectedResult> + </assertNotRegExp> <!-- View Configurable Product --> <comment userInput="View Configurable Product" stepKey="commentViewConfigurableProduct" after="browseAssertSimpleProduct2PageImageNotDefault" /> @@ -104,7 +112,10 @@ </actionGroup> <!-- @TODO: Move Image check to action group after MQE-697 is fixed --> <grabAttributeFrom selector="{{StorefrontProductInfoMainSection.productImage}}" userInput="src" stepKey="browseGrabConfigProductPageImageSrc" after="browseAssertConfigProductPage"/> - <assertNotRegExp expected="'/placeholder\/image\.jpg/'" actual="$browseGrabConfigProductPageImageSrc" stepKey="browseAssertConfigProductPageImageNotDefault" after="browseGrabConfigProductPageImageSrc"/> + <assertNotRegExp stepKey="browseAssertConfigProductPageImageNotDefault" after="browseGrabConfigProductPageImageSrc"> + <actualResult type="const">$browseGrabConfigProductPageImageSrc</actualResult> + <expectedResult type="const">'/placeholder\/image\.jpg/'</expectedResult> + </assertNotRegExp> <!-- Add Configurable Product to cart --> <comment userInput="Add Configurable Product to cart" stepKey="commentAddConfigurableProductToCart" after="cartAddProduct2ToCart" /> @@ -120,7 +131,10 @@ </actionGroup> <!-- @TODO: Move Image check to action group after MQE-697 is fixed --> <grabAttributeFrom selector="{{StorefrontCategoryProductSection.ProductImageByName($$createConfigProduct.name$$)}}" userInput="src" stepKey="cartGrabConfigProductImageSrc" after="cartAssertConfigProduct"/> - <assertNotRegExp expected="'/placeholder\/small_image\.jpg/'" actual="$cartGrabConfigProductImageSrc" stepKey="cartAssertConfigProductImageNotDefault" after="cartGrabConfigProductImageSrc"/> + <assertNotRegExp stepKey="cartAssertConfigProductImageNotDefault" after="cartGrabConfigProductImageSrc"> + <actualResult type="const">$cartGrabConfigProductImageSrc</actualResult> + <expectedResult type="const">'/placeholder\/small_image\.jpg/'</expectedResult> + </assertNotRegExp> <click selector="{{StorefrontCategoryProductSection.ProductAddToCartByName($$createConfigProduct.name$$)}}" stepKey="cartClickCategoryConfigProductAddToCart" after="cartAssertConfigProductImageNotDefault"/> <waitForElement selector="{{StorefrontMessagesSection.message('You need to choose options for your item.')}}" time="30" stepKey="cartWaitForConfigProductPageLoad" after="cartClickCategoryConfigProductAddToCart"/> <actionGroup ref="StorefrontCheckConfigurableProductActionGroup" stepKey="cartAssertConfigProductPage" after="cartWaitForConfigProductPageLoad"> @@ -129,7 +143,10 @@ </actionGroup> <!-- @TODO: Move Image check to action group after MQE-697 is fixed --> <grabAttributeFrom selector="{{StorefrontProductInfoMainSection.productImage}}" userInput="src" stepKey="cartGrabConfigProductPageImageSrc1" after="cartAssertConfigProductPage"/> - <assertNotRegExp expected="'/placeholder\/image\.jpg/'" actual="$cartGrabConfigProductPageImageSrc1" stepKey="cartAssertConfigProductPageImageNotDefault1" after="cartGrabConfigProductPageImageSrc1"/> + <assertNotRegExp stepKey="cartAssertConfigProductPageImageNotDefault1" after="cartGrabConfigProductPageImageSrc1"> + <actualResult type="const">$cartGrabConfigProductPageImageSrc1</actualResult> + <expectedResult type="const">'/placeholder\/image\.jpg/'</expectedResult> + </assertNotRegExp> <selectOption userInput="$$createConfigProductAttributeOption2.option[store_labels][1][label]$$" selector="{{StorefrontProductInfoMainSection.optionByAttributeId($$createConfigProductAttribute.attribute_id$$)}}" stepKey="cartConfigProductFillOption" after="cartAssertConfigProductPageImageNotDefault1"/> <waitForLoadingMaskToDisappear stepKey="waitForConfigurableProductOptionloaded" after="cartConfigProductFillOption"/> <actionGroup ref="StorefrontCheckConfigurableProductActionGroup" stepKey="cartAssertConfigProductWithOptionPage" after="waitForConfigurableProductOptionloaded"> @@ -138,7 +155,10 @@ </actionGroup> <!-- @TODO: Move Image check to action group after MQE-697 is fixed --> <grabAttributeFrom selector="{{StorefrontProductInfoMainSection.productImage}}" userInput="src" stepKey="cartGrabConfigProductPageImageSrc2" after="cartAssertConfigProductWithOptionPage"/> - <assertNotRegExp expected="'/placeholder\/image\.jpg/'" actual="$cartGrabConfigProductPageImageSrc2" stepKey="cartAssertConfigProductPageImageNotDefault2" after="cartGrabConfigProductPageImageSrc2"/> + <assertNotRegExp stepKey="cartAssertConfigProductPageImageNotDefault2" after="cartGrabConfigProductPageImageSrc2"> + <actualResult type="const">$cartGrabConfigProductPageImageSrc2</actualResult> + <expectedResult type="const">'/placeholder\/image\.jpg/'</expectedResult> + </assertNotRegExp> <actionGroup ref="StorefrontAddProductToCartActionGroup" stepKey="cartAddConfigProductToCart" after="cartAssertConfigProductPageImageNotDefault2"> <argument name="product" value="$$createConfigProduct$$"/> <argument name="productCount" value="3"/> @@ -152,7 +172,10 @@ </actionGroup> <!-- @TODO: Move Image check to action group after MQE-697 is fixed --> <grabAttributeFrom selector="{{StorefrontMinicartSection.productImageByName($$createConfigProduct.name$$)}}" userInput="src" stepKey="cartMinicartGrabConfigProductImageSrc" after="cartOpenMinicartAndCheckConfigProduct"/> - <assertNotRegExp expected="'/placeholder\/thumbnail\.jpg/'" actual="$cartMinicartGrabConfigProductImageSrc" stepKey="cartMinicartAssertConfigProductImageNotDefault" after="cartMinicartGrabConfigProductImageSrc"/> + <assertNotRegExp stepKey="cartMinicartAssertConfigProductImageNotDefault" after="cartMinicartGrabConfigProductImageSrc"> + <actualResult type="const">$cartMinicartGrabConfigProductImageSrc</actualResult> + <expectedResult type="const">'/placeholder\/thumbnail\.jpg/'</expectedResult> + </assertNotRegExp> <click selector="{{StorefrontMinicartSection.productOptionsDetailsByName($$createConfigProduct.name$$)}}" stepKey="cartMinicartClickConfigProductDetails" after="cartMinicartAssertConfigProductImageNotDefault"/> <see userInput="$$createConfigProductAttributeOption2.option[store_labels][1][label]$$" selector="{{StorefrontMinicartSection.productOptionByNameAndAttribute($$createConfigProduct.name$$, $$createConfigProductAttribute.attribute[frontend_labels][0][label]$$)}}" stepKey="cartMinicartCheckConfigProductOption" after="cartMinicartClickConfigProductDetails"/> <click selector="{{StorefrontMinicartSection.productLinkByName($$createConfigProduct.name$$)}}" stepKey="cartMinicartClickConfigProduct" after="cartMinicartCheckConfigProductOption"/> @@ -163,7 +186,10 @@ </actionGroup> <!-- @TODO: Move Image check to action group after MQE-697 is fixed --> <grabAttributeFrom selector="{{StorefrontProductInfoMainSection.productImage}}" userInput="src" stepKey="cartMinicartGrabConfigProductPageImageSrc" after="cartAssertMinicartConfigProductPage"/> - <assertNotRegExp expected="'/placeholder\/image\.jpg/'" actual="$cartMinicartGrabConfigProductPageImageSrc" stepKey="cartMinicartAssertConfigProductPageImageNotDefault" after="cartMinicartGrabConfigProductPageImageSrc"/> + <assertNotRegExp stepKey="cartMinicartAssertConfigProductPageImageNotDefault" after="cartMinicartGrabConfigProductPageImageSrc"> + <actualResult type="const">$cartMinicartGrabConfigProductPageImageSrc</actualResult> + <expectedResult type="const">'/placeholder\/image\.jpg/'</expectedResult> + </assertNotRegExp> <!-- Check configurable product in cart --> <comment userInput="Check configurable product in cart" stepKey="commentCheckConfigurableProductInCart" after="cartCartAssertSimpleProduct2PageImageNotDefault2" /> @@ -176,7 +202,10 @@ </actionGroup> <!-- @TODO: Move Image check to action group after MQE-697 is fixed --> <grabAttributeFrom selector="{{CheckoutCartProductSection.ProductImageByName($$createConfigProduct.name$$)}}" userInput="src" stepKey="cartCartGrabConfigProduct2ImageSrc" after="cartAssertCartConfigProduct"/> - <assertNotRegExp expected="'/placeholder\/small_image\.jpg/'" actual="$cartCartGrabConfigProduct2ImageSrc" stepKey="cartCartAssertConfigProduct2ImageNotDefault" after="cartCartGrabConfigProduct2ImageSrc"/> + <assertNotRegExp stepKey="cartCartAssertConfigProduct2ImageNotDefault" after="cartCartGrabConfigProduct2ImageSrc"> + <actualResult type="const">$cartCartGrabConfigProduct2ImageSrc</actualResult> + <expectedResult type="const">'/placeholder\/small_image\.jpg/'</expectedResult> + </assertNotRegExp> <see userInput="$$createConfigProductAttributeOption2.option[store_labels][1][label]$$" selector="{{CheckoutCartProductSection.ProductOptionByNameAndAttribute($$createConfigProduct.name$$, $$createConfigProductAttribute.attribute[frontend_labels][0][label]$$)}}" stepKey="cartCheckConfigProductOption" after="cartCartAssertConfigProduct2ImageNotDefault"/> <click selector="{{CheckoutCartProductSection.ProductLinkByName($$createConfigProduct.name$$)}}" stepKey="cartClickCartConfigProduct" after="cartCheckConfigProductOption"/> <waitForLoadingMaskToDisappear stepKey="waitForCartConfigProductloaded" after="cartClickCartConfigProduct"/> @@ -186,7 +215,10 @@ </actionGroup> <!-- @TODO: Move Image check to action group after MQE-697 is fixed --> <grabAttributeFrom selector="{{StorefrontProductInfoMainSection.productImage}}" userInput="src" stepKey="cartCartGrabConfigProductPageImageSrc" after="cartAssertCartConfigProductPage"/> - <assertNotRegExp expected="'/placeholder\/image\.jpg/'" actual="$cartCartGrabConfigProductPageImageSrc" stepKey="cartCartAssertConfigProductPageImageNotDefault" after="cartCartGrabConfigProductPageImageSrc"/> + <assertNotRegExp stepKey="cartCartAssertConfigProductPageImageNotDefault" after="cartCartGrabConfigProductPageImageSrc"> + <actualResult type="const">$cartCartGrabConfigProductPageImageSrc</actualResult> + <expectedResult type="const">'/placeholder\/image\.jpg/'</expectedResult> + </assertNotRegExp> <!-- Add Configurable Product to comparison --> <comment userInput="Add Configurable Product to comparison" stepKey="commentAddConfigurableProductToComparison" after="compareAddSimpleProduct2ToCompare" /> @@ -196,7 +228,10 @@ </actionGroup> <!-- @TODO: Move Image check to action group after MQE-697 is fixed --> <grabAttributeFrom selector="{{StorefrontCategoryProductSection.ProductImageByName($$createConfigProduct.name$$)}}" userInput="src" stepKey="compareGrabConfigProductImageSrc" after="compareAssertConfigProduct"/> - <assertNotRegExp expected="'/placeholder\/small_image\.jpg/'" actual="$compareGrabConfigProductImageSrc" stepKey="compareAssertConfigProductImageNotDefault" after="compareGrabConfigProductImageSrc"/> + <assertNotRegExp stepKey="compareAssertConfigProductImageNotDefault" after="compareGrabConfigProductImageSrc"> + <actualResult type="const">$compareGrabConfigProductImageSrc</actualResult> + <expectedResult type="const">'/placeholder\/small_image\.jpg/'</expectedResult> + </assertNotRegExp> <actionGroup ref="StorefrontAddCategoryProductToCompareActionGroup" stepKey="compareAddConfigProductToCompare" after="compareAssertConfigProductImageNotDefault"> <argument name="productVar" value="$$createConfigProduct$$"/> </actionGroup> @@ -215,6 +250,9 @@ </actionGroup> <!-- @TODO: Move Image check to action group after MQE-697 is fixed --> <grabAttributeFrom selector="{{StorefrontProductCompareMainSection.ProductImageByName($$createConfigProduct.name$$)}}" userInput="src" stepKey="compareGrabConfigProductImageSrcInComparison" after="compareAssertConfigProductInComparison"/> - <assertNotRegExp expected="'/placeholder\/small_image\.jpg/'" actual="$compareGrabConfigProductImageSrcInComparison" stepKey="compareAssertConfigProductImageNotDefaultInComparison" after="compareGrabConfigProductImageSrcInComparison"/> + <assertNotRegExp stepKey="compareAssertConfigProductImageNotDefaultInComparison" after="compareGrabConfigProductImageSrcInComparison"> + <actualResult type="const">$compareGrabConfigProductImageSrcInComparison</actualResult> + <expectedResult type="const">'/placeholder\/small_image\.jpg/'</expectedResult> + </assertNotRegExp> </test> </tests> diff --git a/app/code/Magento/ConfigurableProduct/Test/Mftf/Test/NewProductsListWidgetConfigurableProductTest.xml b/app/code/Magento/ConfigurableProduct/Test/Mftf/Test/NewProductsListWidgetConfigurableProductTest.xml index eadc7dadaf708..372aa03e4e152 100644 --- a/app/code/Magento/ConfigurableProduct/Test/Mftf/Test/NewProductsListWidgetConfigurableProductTest.xml +++ b/app/code/Magento/ConfigurableProduct/Test/Mftf/Test/NewProductsListWidgetConfigurableProductTest.xml @@ -70,12 +70,17 @@ <deleteData createDataKey="createConfigChildProduct2" stepKey="deleteConfigChildProduct2"/> <deleteData createDataKey="createConfigProduct" stepKey="deleteConfigProduct"/> <deleteData createDataKey="createConfigProductAttribute" stepKey="deleteConfigProductAttribute"/> + + <!-- Reindex invalidated indices after product attribute has been created/deleted --> + <magentoCron groups="index" stepKey="reindexInvalidatedIndices"/> </after> <!-- A Cms page containing the New Products Widget gets created here via extends --> <!-- Modify the Configurable Product that we created in the before block --> - <amOnPage url="{{AdminProductEditPage.url($$createConfigProduct.id$$)}}" stepKey="amOnEditPage"/> + <actionGroup ref="AdminProductPageOpenByIdActionGroup" stepKey="amOnEditPage"> + <argument name="productId" value="$$createConfigProduct.id$$"/> + </actionGroup> <waitForPageLoad stepKey="waitForEditPage"/> <fillField selector="{{AdminProductFormSection.setProductAsNewFrom}}" userInput="01/1/2000" stepKey="fillProductNewFrom"/> <fillField selector="{{AdminProductFormSection.setProductAsNewTo}}" userInput="01/1/2099" stepKey="fillProductNewTo"/> @@ -88,4 +93,4 @@ <waitForPageLoad stepKey="waitForCmsPage"/> <see selector="{{StorefrontCategoryMainSection.ProductItemInfo}}" userInput="$$createConfigProduct.name$$" stepKey="seeProductName"/> </test> -</tests> \ No newline at end of file +</tests> diff --git a/app/code/Magento/ConfigurableProduct/Test/Mftf/Test/NoErrorForMiniCartItemEditTest.xml b/app/code/Magento/ConfigurableProduct/Test/Mftf/Test/NoErrorForMiniCartItemEditTest.xml index 42bad3e4bb8bf..10d8aeb875742 100644 --- a/app/code/Magento/ConfigurableProduct/Test/Mftf/Test/NoErrorForMiniCartItemEditTest.xml +++ b/app/code/Magento/ConfigurableProduct/Test/Mftf/Test/NoErrorForMiniCartItemEditTest.xml @@ -11,13 +11,14 @@ <test name="NoErrorForMiniCartItemEditTest"> <annotations> <features value="ConfigurableProduct"/> + <stories value="Storefront Minicart Update"/> <title value="No error for minicart item edit test"/> <description value="Already selected configurable option should be selected when configurable product is edited from minicart"/> <severity value="MAJOR"/> <group value="ConfigurableProduct"/> </annotations> <before> - <actionGroup ref="LoginAsAdmin" stepKey="loginAsAdmin1"/> + <actionGroup ref="AdminLoginActionGroup" stepKey="loginAsAdmin1"/> <createData entity="ApiCategory" stepKey="createCategory"/> <!-- Create Configurable product --> <actionGroup ref="CreateConfigurableProductActionGroup" stepKey="createProduct"> @@ -34,7 +35,7 @@ <conditionalClick selector="{{AdminProductGridFilterSection.clearFilters}}" dependentSelector="{{AdminProductGridFilterSection.clearFilters}}" visible="true" stepKey="clickClearFilters"/> - <actionGroup ref="logout" stepKey="logout"/> + <actionGroup ref="AdminLogoutActionGroup" stepKey="logout"/> </after> <!-- Go To Created Product Page --> @@ -59,7 +60,7 @@ <waitForPageLoad stepKey="waitForMiniCart"/> <!-- Edit Item in Cart --> - <click selector="{{StorefrontMinicartSection.showCart}}" stepKey="openMiniCart"/> + <actionGroup ref="StorefrontClickOnMiniCartActionGroup" stepKey="openMiniCart"/> <click selector="{{StorefrontMinicartSection.editMiniCartItem}}" stepKey="clickEditCartItem"/> <!-- Check if Product Configuration is still selected --> diff --git a/app/code/Magento/ConfigurableProduct/Test/Mftf/Test/NoOptionAvailableToConfigureDisabledProductTest.xml b/app/code/Magento/ConfigurableProduct/Test/Mftf/Test/NoOptionAvailableToConfigureDisabledProductTest.xml index 5224cc6a9cced..54e23470ae786 100644 --- a/app/code/Magento/ConfigurableProduct/Test/Mftf/Test/NoOptionAvailableToConfigureDisabledProductTest.xml +++ b/app/code/Magento/ConfigurableProduct/Test/Mftf/Test/NoOptionAvailableToConfigureDisabledProductTest.xml @@ -20,7 +20,7 @@ <group value="ConfigurableProduct"/> </annotations> <before> - <actionGroup ref="LoginAsAdmin" stepKey="login"/> + <actionGroup ref="AdminLoginActionGroup" stepKey="login"/> <!--Create category--> <comment userInput="Create category" stepKey="commentCreateCategory"/> <createData entity="SimpleSubCategory" stepKey="createCategory"/> @@ -102,17 +102,24 @@ <deleteData createDataKey="createConfigChildProduct3" stepKey="deleteConfigChildProduct3"/> <deleteData createDataKey="createConfigProductAttribute" stepKey="deleteConfigProductAttribute"/> <deleteData createDataKey="createCustomer" stepKey="deleteCreatedCustomer"/> - <actionGroup ref="logout" stepKey="logout"/> + <actionGroup ref="AdminLogoutActionGroup" stepKey="logout"/> + + <!-- Reindex invalidated indices after product attribute has been created/deleted --> + <magentoCron groups="index" stepKey="reindexInvalidatedIndices"/> </after> <!-- Disable child product --> <comment userInput="Disable child product" stepKey="disableChildProduct"/> - <amOnPage url="{{AdminProductEditPage.url($$createConfigChildProduct1.id$$)}}" stepKey="goToEditPage"/> + <actionGroup ref="AdminProductPageOpenByIdActionGroup" stepKey="goToEditPage"> + <argument name="productId" value="$$createConfigChildProduct1.id$$"/> + </actionGroup> <waitForPageLoad stepKey="waitForChildProductPageLoad"/> <click selector="{{AdminProductFormSection.enableProductLabel}}" stepKey="disableProduct"/> <actionGroup ref="SaveProductFormActionGroup" stepKey="saveProductForm"/> <!-- Set the second product out of stock --> <comment userInput="Set the second product out of stock" stepKey="outOfStockChildProduct"/> - <amOnPage url="{{AdminProductEditPage.url($$createConfigChildProduct2.id$$)}}" stepKey="goToSecondProductEditPage"/> + <actionGroup ref="AdminProductPageOpenByIdActionGroup" stepKey="goToSecondProductEditPage"> + <argument name="productId" value="$$createConfigChildProduct2.id$$"/> + </actionGroup> <waitForPageLoad stepKey="waitForSecondChildProductPageLoad"/> <selectOption selector="{{AdminProductFormSection.productStockStatus}}" userInput="Out of Stock" stepKey="outOfStockStatus"/> <actionGroup ref="SaveProductFormActionGroup" stepKey="saveSecondProductForm"/> @@ -154,7 +161,12 @@ <click selector="{{AdminInvoicePaymentShippingSection.shippingMethod}}" stepKey="chooseShippingMethod"/> <waitForPageLoad stepKey="waitForShippingMethodLoad"/> <click selector="{{AdminOrderFormActionSection.SubmitOrder}}" stepKey="clickSubmitOrder"/> - <waitForPageLoad stepKey="waitForSuccess"/> - <see selector="{{AdminOrderDetailsMessagesSection.successMessage}}" userInput="You created the order." stepKey="seeSuccessMessage"/> + <waitForPageLoad stepKey="waitForError"/> + <!-- Check that error remains --> + <actionGroup ref="AssertAdminItemOrderedErrorActionGroup" stepKey="assertProductErrorRemains"> + <argument name="productName" value="$createConfigChildProduct2.name$"/> + <argument name="messageType" value="error"/> + <argument name="message" value="This product is out of stock."/> + </actionGroup> </test> </tests> diff --git a/app/code/Magento/ConfigurableProduct/Test/Mftf/Test/ProductsQtyReturnAfterOrderCancelTest.xml b/app/code/Magento/ConfigurableProduct/Test/Mftf/Test/ProductsQtyReturnAfterOrderCancelTest.xml index 6045ca5567b45..355c4fa049f01 100644 --- a/app/code/Magento/ConfigurableProduct/Test/Mftf/Test/ProductsQtyReturnAfterOrderCancelTest.xml +++ b/app/code/Magento/ConfigurableProduct/Test/Mftf/Test/ProductsQtyReturnAfterOrderCancelTest.xml @@ -8,13 +8,13 @@ <tests xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/testSchema.xsd"> - <test name="ProductsQtyReturnAfterOrderCancel"> + <test name="ProductsQtyReturnAfterOrderCancelTest"> <annotations> <features value="ConfigurableProduct"/> <stories value="Cancel order"/> - <title value="Product qunatity return after order cancel"/> - <description value="Check Product qunatity return after order cancel"/> + <title value="Product quantity return after order cancel"/> + <description value="Check Product quantity return after order cancel"/> <severity value="CRITICAL"/> <testCaseId value="MAGETWO-97228"/> <useCaseId value="MAGETWO-82221"/> @@ -26,13 +26,13 @@ <createData entity="ApiConfigurableProduct" stepKey="createConfigProduct"> <requiredEntity createDataKey="createCategory"/> </createData> - <actionGroup ref="LoginAsAdmin" stepKey="login"/> + <actionGroup ref="AdminLoginActionGroup" stepKey="login"/> </before> <after> <deleteData createDataKey="createCategory" stepKey="deleteCategory"/> <deleteData createDataKey="createConfigProduct" stepKey="deleteConfigProduct"/> - <actionGroup ref="logout" stepKey="amOnLogoutPage"/> + <actionGroup ref="AdminLogoutActionGroup" stepKey="amOnLogoutPage"/> </after> <amOnPage url="{{AdminCatalogProductPage.url}}" stepKey="GoToCatalogProductPage1"/> @@ -75,7 +75,7 @@ <click selector="{{AdminOrderDetailsMainActionsSection.invoice}}" stepKey="clickInvoiceButton"/> <waitForPageLoad stepKey="waitForNewInvoicePageLoad"/> <fillField selector="{{AdminInvoiceItemsSection.qtyToInvoiceColumn}}" userInput="1" stepKey="ChangeQtyToInvoice"/> - <click selector="{{AdminInvoiceItemsSection.updateQty}}" stepKey="updateQunatity"/> + <click selector="{{AdminInvoiceItemsSection.updateQty}}" stepKey="updateQuantity"/> <waitForPageLoad stepKey="waitPageToBeLoaded"/> <click selector="{{AdminInvoiceMainActionsSection.submitInvoice}}" stepKey="clickSubmitInvoice"/> <click selector="{{AdminOrderDetailsMainActionsSection.ship}}" stepKey="clickShipAction"/> diff --git a/app/code/Magento/ConfigurableProduct/Test/Mftf/Test/StorefrontAdvanceCatalogSearchConfigurableBySkuWithHyphenTest.xml b/app/code/Magento/ConfigurableProduct/Test/Mftf/Test/StorefrontAdvanceCatalogSearchConfigurableBySkuWithHyphenTest.xml index a8e982475253f..d5b50f107161a 100644 --- a/app/code/Magento/ConfigurableProduct/Test/Mftf/Test/StorefrontAdvanceCatalogSearchConfigurableBySkuWithHyphenTest.xml +++ b/app/code/Magento/ConfigurableProduct/Test/Mftf/Test/StorefrontAdvanceCatalogSearchConfigurableBySkuWithHyphenTest.xml @@ -83,6 +83,10 @@ <deleteData createDataKey="simple2Handle" stepKey="deleteSimple2" before="delete"/> <deleteData createDataKey="childProductHandle1" stepKey="deleteChildProduct1" before="delete"/> <deleteData createDataKey="childProductHandle2" stepKey="deleteChildProduct2" before="delete"/> + <deleteData createDataKey="productAttributeHandle" stepKey="deleteProductDropDownAttribute"/> + + <!-- Reindex invalidated indices after product attribute has been created/deleted --> + <magentoCron groups="index" stepKey="reindexInvalidatedIndices"/> </after> </test> </tests> diff --git a/app/code/Magento/ConfigurableProduct/Test/Mftf/Test/StorefrontConfigurableProductCategoryViewChildOnlyTest.xml b/app/code/Magento/ConfigurableProduct/Test/Mftf/Test/StorefrontConfigurableProductCategoryViewChildOnlyTest.xml deleted file mode 100644 index 9d7807c543def..0000000000000 --- a/app/code/Magento/ConfigurableProduct/Test/Mftf/Test/StorefrontConfigurableProductCategoryViewChildOnlyTest.xml +++ /dev/null @@ -1,129 +0,0 @@ -<?xml version="1.0" encoding="UTF-8"?> -<!-- - /** - * Copyright © Magento, Inc. All rights reserved. - * See COPYING.txt for license details. - */ ---> - -<tests xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" - xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/testSchema.xsd"> - <test name="StorefrontConfigurableProductCategoryViewChildOnlyTest"> - <annotations> - <features value="ConfigurableProduct"/> - <stories value="View configurable product child in storefront"/> - <title value="It should be possible to only view the child product of a configurable product"/> - <description value="Create configurable product, add to category such that only child variation is visible in category"/> - <severity value="CRITICAL"/> - <testCaseId value="MC-5832"/> - <group value="ConfigurableProduct"/> - <skip> - <issueId value="MC-17140"/> - </skip> - </annotations> - <before> - <!-- Create the category --> - <createData entity="ApiCategory" stepKey="createCategory"/> - <createData entity="ApiCategory" stepKey="secondCategory"/> - - <!-- Create an attribute with two options to be used in the first child product --> - <createData entity="productAttributeWithTwoOptions" stepKey="createConfigProductAttribute"/> - <createData entity="productAttributeOption1" stepKey="createConfigProductAttributeOption1"> - <requiredEntity createDataKey="createConfigProductAttribute"/> - </createData> - <createData entity="productAttributeOption2" stepKey="createConfigProductAttributeOption2"> - <requiredEntity createDataKey="createConfigProductAttribute"/> - </createData> - - <!-- Add the attribute we just created to default attribute set --> - <createData entity="AddToDefaultSet" stepKey="createConfigAddToAttributeSet"> - <requiredEntity createDataKey="createConfigProductAttribute"/> - </createData> - - <!-- Get the first option of the attribute we created --> - <getData entity="ProductAttributeOptionGetter" index="1" stepKey="getConfigAttributeOption1"> - <requiredEntity createDataKey="createConfigProductAttribute"/> - </getData> - - <!-- Get the second option of the attribute we created --> - <getData entity="ProductAttributeOptionGetter" index="2" stepKey="getConfigAttributeOption2"> - <requiredEntity createDataKey="createConfigProductAttribute"/> - </getData> - - <!-- Create the configurable product and add it to the category --> - <createData entity="ApiConfigurableProduct" stepKey="createConfigProduct"> - <requiredEntity createDataKey="createCategory"/> - </createData> - - <!-- Create a simple product and give it the attribute with the first option --> - <createData entity="ApiSimpleOne" stepKey="createConfigChildProduct1"> - <requiredEntity createDataKey="createConfigProductAttribute"/> - <requiredEntity createDataKey="getConfigAttributeOption1"/> - </createData> - - <!-- Create a simple product and give it the attribute with the second option --> - <createData entity="ApiSimpleTwo" stepKey="createConfigChildProduct2"> - <requiredEntity createDataKey="createConfigProductAttribute"/> - <requiredEntity createDataKey="getConfigAttributeOption2"/> - </createData> - - <createData entity="ConfigurableProductTwoOptions" stepKey="createConfigProductOption"> - <requiredEntity createDataKey="createConfigProduct"/> - <requiredEntity createDataKey="createConfigProductAttribute"/> - <requiredEntity createDataKey="getConfigAttributeOption1"/> - <requiredEntity createDataKey="getConfigAttributeOption2"/> - </createData> - - <!-- Add the first simple product to the configurable product --> - <createData entity="ConfigurableProductAddChild" stepKey="createConfigProductAddChild1"> - <requiredEntity createDataKey="createConfigProduct"/> - <requiredEntity createDataKey="createConfigChildProduct1"/> - </createData> - - <!-- Add the second simple product to the configurable product --> - <createData entity="ConfigurableProductAddChild" stepKey="createConfigProductAddChild2"> - <requiredEntity createDataKey="createConfigProduct"/> - <requiredEntity createDataKey="createConfigChildProduct2"/> - </createData> - </before> - - <after> - <actionGroup ref="logout" stepKey="adminLogout"/> - <deleteData createDataKey="createConfigProduct" stepKey="deleteConfigProduct"/> - <deleteData createDataKey="createConfigChildProduct1" stepKey="deleteConfigChildProduct1"/> - <deleteData createDataKey="createConfigChildProduct2" stepKey="deleteConfigChildProduct2"/> - <deleteData createDataKey="createConfigProductAttribute" stepKey="deleteConfigProductAttribute"/> - <deleteData createDataKey="createCategory" stepKey="deleteApiCategory"/> - <deleteData createDataKey="secondCategory" stepKey="deleteSecondCategory"/> - </after> - - <actionGroup ref="LoginAsAdmin" stepKey="login"/> - - <!-- Go to the product page for the first product --> - <amOnPage stepKey="goToProductGrid" url="{{ProductCatalogPage.url}}"/> - <waitForPageLoad stepKey="waitForProductGridLoad"/> - <actionGroup stepKey="searchForSimpleProduct" ref="FilterProductGridBySku2ActionGroup"> - <argument name="sku" value="$$createConfigChildProduct1.sku$$"/> - </actionGroup> - <actionGroup stepKey="openProductEditPage" ref="OpenProductForEditByClickingRowXColumnYInProductGridActionGroup"/> - <!-- Edit the visibility the first simple product --> - <selectOption selector="{{AdminProductFormSection.visibility}}" userInput="Catalog, Search" stepKey="selectVisibilityCatalogSearch"/> - <!--Add to category--> - <searchAndMultiSelectOption selector="{{AdminProductFormSection.categoriesDropdown}}" parameterArray="[$$secondCategory.name$$]" stepKey="addProductToCategory"/> - <click selector="{{AdminProductFormActionSection.saveButton}}" stepKey="saveProduct"/> - <seeElement selector="{{AdminProductMessagesSection.successMessage}}" stepKey="assertSaveMessageSuccess"/> - - <!-- Go to storefront to view child product --> - <amOnPage stepKey="goToStoreFront" url="{{StorefrontHomePage.url}}"/> - <waitForPageLoad stepKey="waitForStorefront"/> - <click selector="{{StorefrontHeaderSection.NavigationCategoryByName($$secondCategory.name$$)}}" stepKey="goToCategoryStorefront"/> - <waitForPageLoad stepKey="waitForStorefrontCategory"/> - <seeElement selector="{{StorefrontCategoryProductSection.ProductTitleByName($$createConfigChildProduct1.name$$)}}" stepKey="seeChildProductInCategory"/> - <dontSeeElement selector="{{StorefrontCategoryProductSection.ProductTitleByName($$createConfigChildProduct2.name$$)}}" stepKey="dontSeeOtherChildProduct"/> - <dontSeeElement selector="{{StorefrontCategoryProductSection.ProductTitleByName($$createConfigProduct.name$$)}}" stepKey="dontSeeParentProduct"/> - <click selector="{{StorefrontCategoryProductSection.ProductTitleByName($$createConfigChildProduct1.name$$)}}" stepKey="clickProductName"/> - <waitForPageLoad stepKey="waitForProductPageLoad"/> - <seeInCurrentUrl url="$$createConfigChildProduct1.custom_attributes[url_key]$$" stepKey="seeProductPageIsAccessible"/> - <seeElement selector="{{StorefrontProductInfoMainSection.productName($$createConfigChildProduct1.name$$)}}" stepKey="seeProductNameOnProductPage"/> - </test> -</tests> diff --git a/app/code/Magento/ConfigurableProduct/Test/Mftf/Test/StorefrontConfigurableProductChildSearchTest.xml b/app/code/Magento/ConfigurableProduct/Test/Mftf/Test/StorefrontConfigurableProductChildSearchTest.xml index 294ab9fd0664d..10ea3a7e400c4 100644 --- a/app/code/Magento/ConfigurableProduct/Test/Mftf/Test/StorefrontConfigurableProductChildSearchTest.xml +++ b/app/code/Magento/ConfigurableProduct/Test/Mftf/Test/StorefrontConfigurableProductChildSearchTest.xml @@ -114,7 +114,7 @@ <requiredEntity createDataKey="createConfigProductAttributeSelect"/> </createData> - <actionGroup ref="LoginAsAdmin" stepKey="login"/> + <actionGroup ref="AdminLoginActionGroup" stepKey="login"/> <!-- Go to the product page for the first product --> <amOnPage stepKey="goToProductGrid" url="{{ProductCatalogPage.url}}"/> @@ -132,7 +132,7 @@ </before> <after> - <actionGroup ref="logout" stepKey="adminLogout"/> + <actionGroup ref="AdminLogoutActionGroup" stepKey="adminLogout"/> <deleteData createDataKey="createConfigProduct" stepKey="deleteConfigProduct"/> <deleteData createDataKey="createConfigChildProduct1" stepKey="deleteConfigChildProduct1"/> <deleteData createDataKey="createConfigChildProduct2" stepKey="deleteConfigChildProduct2"/> @@ -140,6 +140,9 @@ <deleteData createDataKey="createConfigProductAttributeMultiSelect" stepKey="deleteConfigProductAttributeMultiSelect"/> <deleteData createDataKey="createConfigProductAttributeSelect" stepKey="deleteConfigProductAttributeSelect"/> <deleteData createDataKey="createCategory" stepKey="deleteApiCategory"/> + + <!-- Reindex invalidated indices after product attribute has been created/deleted --> + <magentoCron groups="index" stepKey="reindexInvalidatedIndices"/> </after> <!-- Quick search the storefront for the first attribute option --> diff --git a/app/code/Magento/ConfigurableProduct/Test/Mftf/Test/StorefrontConfigurableProductDetailsTest.xml b/app/code/Magento/ConfigurableProduct/Test/Mftf/Test/StorefrontConfigurableProductDetailsTest.xml deleted file mode 100644 index 42b9dfc92760a..0000000000000 --- a/app/code/Magento/ConfigurableProduct/Test/Mftf/Test/StorefrontConfigurableProductDetailsTest.xml +++ /dev/null @@ -1,285 +0,0 @@ -<?xml version="1.0" encoding="UTF-8"?> -<!-- - /** - * Copyright © Magento, Inc. All rights reserved. - * See COPYING.txt for license details. - */ ---> - -<tests xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" - xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/testSchema.xsd"> - <test name="StorefrontConfigurableProductBasicInfoTest"> - <annotations> - <features value="ConfigurableProduct"/> - <stories value="View configurable product details in storefront"/> - <title value="Guest customer should see basic Configurable Product details"/> - <description value="Guest customer should see basic Configurable Product details"/> - <severity value="MAJOR"/> - <testCaseId value="MC-77"/> - <group value="ConfigurableProduct"/> - </annotations> - - <before> - <createData entity="ApiCategory" stepKey="createCategory"/> - <actionGroup ref="LoginAsAdmin" stepKey="loginAsAdmin"/> - - <!-- Create a configurable product via the UI --> - <actionGroup ref="CreateConfigurableProductActionGroup" stepKey="createProduct"> - <argument name="product" value="_defaultProduct"/> - <argument name="category" value="$$createCategory$$"/> - </actionGroup> - </before> - <after> - <deleteData createDataKey="createCategory" stepKey="deleteCategory"/> - <actionGroup ref="DeleteProductBySkuActionGroup" stepKey="deleteProduct"> - <argument name="sku" value="{{_defaultProduct.sku}}"/> - </actionGroup> - <actionGroup ref="ClearFiltersAdminDataGridActionGroup" stepKey="clearProductsGridFilters"/> - <actionGroup ref="logout" stepKey="adminLogout"/> - </after> - - <!-- Verify configurable product details in storefront product view --> - <amOnPage url="{{_defaultProduct.urlKey}}.html" stepKey="amOnConfigurableProductPage"/> - <waitForPageLoad stepKey="wait"/> - <see userInput="{{_defaultProduct.name}}" selector="{{StorefrontProductInfoMainSection.productName}}" stepKey="seeProductName"/> - <see userInput="{{_defaultProduct.sku}}" selector="{{StorefrontProductInfoMainSection.productSku}}" stepKey="seeProductSku"/> - <see userInput="As low as" selector="{{StorefrontProductInfoMainSection.productPriceLabel}}" stepKey="seeProductPriceLabel"/> - <see userInput="IN STOCK" selector="{{StorefrontProductInfoMainSection.productStockStatus}}" stepKey="seeProductStockStatus"/> - <see userInput="1.00" selector="{{StorefrontProductInfoMainSection.productPrice}}" stepKey="seeProductPrice"/> - <see userInput="{{colorProductAttribute.default_label}}" selector="{{StorefrontProductInfoMainSection.productAttributeTitle1}}" stepKey="seeProductAttributeTitle"/> - <see selector="{{StorefrontProductInfoMainSection.productAttributeTitle1}}" userInput="{{colorProductAttribute.default_label}}" stepKey="seeColorAttributeName1"/> - <see userInput="{{colorProductAttribute1.name}}" selector="{{StorefrontProductInfoMainSection.productAttributeOptions1}}" stepKey="seeInDropDown1"/> - <see userInput="{{colorProductAttribute2.name}}" selector="{{StorefrontProductInfoMainSection.productAttributeOptions1}}" stepKey="seeInDropDown2"/> - <see userInput="{{colorProductAttribute3.name}}" selector="{{StorefrontProductInfoMainSection.productAttributeOptions1}}" stepKey="seeInDropDown3"/> - </test> - - <test name="StorefrontConfigurableProductOptionsTest"> - <annotations> - <features value="ConfigurableProduct"/> - <stories value="View configurable product details in storefront"/> - <title value="Guest customer should be able to see product configuration options"/> - <description value="Guest customer should be able to see product configuration options"/> - <severity value="MAJOR"/> - <testCaseId value="MC-92"/> - <group value="ConfigurableProduct"/> - </annotations> - - <before> - <createData entity="ApiCategory" stepKey="createCategory"/> - <actionGroup ref="LoginAsAdmin" stepKey="loginAsAdmin"/> - - <!-- Create a configurable product via the UI --> - <actionGroup ref="CreateConfigurableProductActionGroup" stepKey="createProduct"> - <argument name="product" value="_defaultProduct"/> - <argument name="category" value="$$createCategory$$"/> - </actionGroup> - </before> - <after> - <deleteData createDataKey="createCategory" stepKey="deleteCategory"/> - <actionGroup ref="DeleteProductBySkuActionGroup" stepKey="deleteProduct"> - <argument name="sku" value="{{_defaultProduct.sku}}"/> - </actionGroup> - <actionGroup ref="ClearFiltersAdminDataGridActionGroup" stepKey="clearProductsGridFilters"/> - <actionGroup ref="logout" stepKey="adminLogout"/> - </after> - - <!-- Verify configurable product options in storefront product view --> - <amOnPage url="{{_defaultProduct.urlKey}}.html" stepKey="amOnConfigurableProductPage"/> - <waitForPageLoad stepKey="wait"/> - <see userInput="{{_defaultProduct.name}}" selector="{{StorefrontProductInfoMainSection.productName}}" stepKey="seeProductName"/> - <selectOption userInput="{{colorProductAttribute1.name}}" selector="{{StorefrontProductInfoMainSection.productAttributeOptionsSelectButton}}" stepKey="selectOption1"/> - <dontSee userInput="As low as" selector="{{StorefrontProductInfoMainSection.productPriceLabel}}" stepKey="dontSeeProductPriceLabel1"/> - <see userInput="1.00" selector="{{StorefrontProductInfoMainSection.productPrice}}" stepKey="seeProductPrice1"/> - <selectOption userInput="{{colorProductAttribute2.name}}" selector="{{StorefrontProductInfoMainSection.productAttributeOptionsSelectButton}}" stepKey="selectOption2"/> - <dontSee userInput="As low as" selector="{{StorefrontProductInfoMainSection.productPriceLabel}}" stepKey="dontSeeProductPriceLabel2"/> - <see userInput="2.00" selector="{{StorefrontProductInfoMainSection.productPrice}}" stepKey="seeProductPrice2"/> - <selectOption userInput="{{colorProductAttribute3.name}}" selector="{{StorefrontProductInfoMainSection.productAttributeOptionsSelectButton}}" stepKey="selectOption3"/> - <dontSee userInput="As low as" selector="{{StorefrontProductInfoMainSection.productPriceLabel}}" stepKey="dontSeeProductPriceLabel3"/> - <see userInput="3.00" selector="{{StorefrontProductInfoMainSection.productPrice}}" stepKey="seeProductPrice3"/> - </test> - - <test name="StorefrontConfigurableProductCanAddToCartTest"> - <annotations> - <features value="ConfigurableProduct"/> - <stories value="View configurable product details in storefront"/> - <title value="Guest customer should be able to successfully add the product to the cart"/> - <description value="Guest customer should be able to successfully add the product to the cart"/> - <severity value="MAJOR"/> - <testCaseId value="MC-97"/> - <group value="ConfigurableProduct"/> - </annotations> - - <before> - <createData entity="ApiCategory" stepKey="createCategory"/> - <actionGroup ref="LoginAsAdmin" stepKey="loginAsAdmin"/> - - <!-- Create a configurable product via the UI --> - <actionGroup ref="CreateConfigurableProductActionGroup" stepKey="createProduct"> - <argument name="product" value="_defaultProduct"/> - <argument name="category" value="$$createCategory$$"/> - </actionGroup> - </before> - <after> - <deleteData createDataKey="createCategory" stepKey="deleteCategory"/> - <actionGroup ref="DeleteProductBySkuActionGroup" stepKey="deleteProduct"> - <argument name="sku" value="{{_defaultProduct.sku}}"/> - </actionGroup> - <actionGroup ref="ClearFiltersAdminDataGridActionGroup" stepKey="clearProductsGridFilters"/> - <actionGroup ref="logout" stepKey="adminLogout"/> - </after> - - <!-- Verify adding configurable product to cart after an option is selected in storefront product view --> - <amOnPage url="{{_defaultProduct.urlKey}}.html" stepKey="amOnConfigurableProductPage"/> - <waitForPageLoad stepKey="wait"/> - <see userInput="{{_defaultProduct.name}}" selector="{{StorefrontProductInfoMainSection.productName}}" stepKey="seeProductName"/> - <selectOption userInput="{{colorProductAttribute1.name}}" selector="{{StorefrontProductInfoMainSection.productAttributeOptionsSelectButton}}" stepKey="selectOption1"/> - <waitForElementVisible selector="{{StorefrontProductInfoMainSection.AddToCart}}" stepKey="waitForAddToCartVisible"/> - <actionGroup ref="StorefrontAddProductToCartActionGroup" stepKey="addProductToCart"> - <argument name="product" value="_defaultProduct"/> - <argument name="productCount" value="1"/> - </actionGroup> - </test> - - <test name="StorefrontConfigurableProductCantAddToCartTest"> - <annotations> - <features value="ConfigurableProduct"/> - <stories value="View configurable product details in storefront"/> - <title value="Guest customer should not be able to add the product to the cart if options are not selected"/> - <description value="Guest customer should not be able to add the product to the cart if options are not selected"/> - <severity value="MAJOR"/> - <testCaseId value="MC-81"/> - <group value="ConfigurableProduct"/> - </annotations> - - <before> - <createData entity="ApiCategory" stepKey="createCategory"/> - <actionGroup ref="LoginAsAdmin" stepKey="loginAsAdmin"/> - - <!-- Create a configurable product via the UI --> - <actionGroup ref="CreateConfigurableProductActionGroup" stepKey="createProduct"> - <argument name="product" value="_defaultProduct"/> - <argument name="category" value="$$createCategory$$"/> - </actionGroup> - </before> - <after> - <deleteData createDataKey="createCategory" stepKey="deleteCategory"/> - <actionGroup ref="DeleteProductBySkuActionGroup" stepKey="deleteProduct"> - <argument name="sku" value="{{_defaultProduct.sku}}"/> - </actionGroup> - <actionGroup ref="ClearFiltersAdminDataGridActionGroup" stepKey="clearProductsGridFilters"/> - <actionGroup ref="logout" stepKey="adminLogout"/> - </after> - - <!-- Verify not able to add configurable product to cart when no option is selected in storefront product view --> - <amOnPage url="{{_defaultProduct.urlKey}}.html" stepKey="amOnConfigurableProductPage"/> - <waitForPageLoad stepKey="wait"/> - <see userInput="{{_defaultProduct.name}}" selector="{{StorefrontProductInfoMainSection.productName}}" stepKey="seeProductName"/> - <waitForElementVisible selector="{{StorefrontProductInfoMainSection.AddToCart}}" stepKey="waitForAddToCartVisible"/> - <click selector="{{StorefrontProductInfoMainSection.AddToCart}}" stepKey="clickAddToCart" /> - <see userInput="This is a required field" selector="{{StorefrontProductInfoMainSection.productAttributeOptionsError}}" stepKey="seeError"/> - </test> - - <test name="StorefrontConfigurableProductVariationsTest"> - <annotations> - <features value="ConfigurableProduct"/> - <stories value="Configurable Product"/> - <title value="Customer should get the right options list"/> - <description value="Customer should get the right options list for each variation of configurable product"/> - <severity value="CRITICAL"/> - <testCaseId value="MC-23027"/> - <useCaseId value="MC-22732"/> - <group value="configurable_product"/> - </annotations> - - <before> - <createData entity="ApiCategory" stepKey="createCategory"/> - <!-- Add first attribute with options --> - <createData entity="productAttributeWithTwoOptions" stepKey="createFirstAttribute"/> - <createData entity="productAttributeOption1" stepKey="createFirstAttributeFirstOption"> - <requiredEntity createDataKey="createFirstAttribute"/> - </createData> - <createData entity="productAttributeOption2" stepKey="createFirstAttributeSecondOption"> - <requiredEntity createDataKey="createFirstAttribute"/> - </createData> - <!-- Add second attribute with options --> - <createData entity="productAttributeWithTwoOptions" stepKey="createSecondAttribute"/> - <createData entity="productAttributeOption1" stepKey="createSecondAttributeFirstOption"> - <requiredEntity createDataKey="createSecondAttribute"/> - </createData> - <createData entity="productAttributeOption2" stepKey="createSecondAttributeSecondOption"> - <requiredEntity createDataKey="createSecondAttribute"/> - </createData> - <actionGroup ref="LoginAsAdmin" stepKey="loginAsAdmin"/> - </before> - <after> - <deleteData createDataKey="createCategory" stepKey="deleteCategory"/> - <actionGroup ref="DeleteProductBySkuActionGroup" stepKey="deleteProduct"> - <argument name="sku" value="{{BaseConfigurableProduct.sku}}"/> - </actionGroup> - <deleteData createDataKey="createFirstAttribute" stepKey="deleteFirstAttribute"/> - <deleteData createDataKey="createSecondAttribute" stepKey="deleteSecondAttribute"/> - <actionGroup ref="ClearFiltersAdminDataGridActionGroup" stepKey="clearProductsGridFilters"/> - <actionGroup ref="logout" stepKey="adminLogout"/> - </after> - - <actionGroup ref="AdminOpenProductIndexPageActionGroup" stepKey="openProductIndexPage"/> - <actionGroup ref="GoToCreateProductPageActionGroup" stepKey="navigateToCreateProductPage"> - <argument name="product" value="BaseConfigurableProduct"/> - </actionGroup> - <actionGroup ref="FillMainProductFormActionGroup" stepKey="fillProductForm"> - <argument name="product" value="BaseConfigurableProduct"/> - </actionGroup> - <actionGroup ref="SetCategoryByNameActionGroup" stepKey="addCategoryToProduct"> - <argument name="categoryName" value="$createCategory.name$"/> - </actionGroup> - <actionGroup ref="SetProductUrlKeyByStringActionGroup" stepKey="fillUrlKey"> - <argument name="urlKey" value="{{BaseConfigurableProduct.urlKey}}"/> - </actionGroup> - <click selector="{{AdminProductFormConfigurationsSection.createConfigurations}}" stepKey="clickOnCreateConfigurations"/> - <actionGroup ref="AdminSelectAttributeInConfigurableAttributesGrid" stepKey="checkFirstAttribute"> - <argument name="attributeCode" value="$createFirstAttribute.attribute_code$"/> - </actionGroup> - <actionGroup ref="AdminSelectAttributeInConfigurableAttributesGrid" stepKey="checkSecondAttribute"> - <argument name="attributeCode" value="$createSecondAttribute.attribute_code$"/> - </actionGroup> - <click selector="{{AdminCreateProductConfigurationsPanel.next}}" stepKey="clickOnNextButton"/> - <waitForPageLoad stepKey="waitForStepLoad"/> - <click selector="{{AdminCreateProductConfigurationsPanel.selectAllByAttribute($createFirstAttribute.default_frontend_label$)}}" stepKey="clickFirstAttributeSelectAll"/> - <click selector="{{AdminCreateProductConfigurationsPanel.selectAllByAttribute($createSecondAttribute.default_frontend_label$)}}" stepKey="clickSecondAttributeSelectAll"/> - <click selector="{{AdminCreateProductConfigurationsPanel.next}}" stepKey="clickSecondNextStep"/> - <waitForElement selector="{{AdminCreateProductConfigurationsPanel.next}}" stepKey="waitThirdNextButton"/> - <click selector="{{AdminCreateProductConfigurationsPanel.next}}" stepKey="clickThirdStep"/> - <waitForElement selector="{{AdminCreateProductConfigurationsPanel.next}}" stepKey="waitGenerateConfigurationsButton"/> - <click selector="{{AdminCreateProductConfigurationsPanel.next}}" stepKey="clickToGenerateConfigurations"/> - - <waitForElementVisible selector="{{AdminCreateProductConfigurationsPanel.variationsGrid}}" stepKey="waitForVariationsGrid"/> - <actionGroup ref="AdminChangeConfigurableProductVariationQty" stepKey="setFirstVariationQuantity"> - <argument name="rowIndex" value="0"/> - <argument name="quantity" value="0"/> - </actionGroup> - <actionGroup ref="AdminChangeConfigurableProductVariationQty" stepKey="setSecondVariationQuantity"> - <argument name="rowIndex" value="1"/> - <argument name="quantity" value="1"/> - </actionGroup> - <actionGroup ref="AdminChangeConfigurableProductVariationQty" stepKey="setThirdVariationQuantity"> - <argument name="rowIndex" value="2"/> - <argument name="quantity" value="1"/> - </actionGroup> - <actionGroup ref="AdminChangeConfigurableProductVariationQty" stepKey="setFourthVariationQuantity"> - <argument name="rowIndex" value="3"/> - <argument name="quantity" value="1"/> - </actionGroup> - <actionGroup ref="SaveConfigurableProductActionGroup" stepKey="saveConfigurableProduct"> - <argument name="product" value="BaseConfigurableProduct"/> - </actionGroup> - <scrollTo selector="{{AdminProductSEOSection.sectionHeader}}" x="0" y="-80" stepKey="scrollToAdminProductSEOSection"/> - <click selector="{{AdminProductSEOSection.sectionHeader}}" stepKey="clickAdminProductSEOSectionHeader"/> - <grabValueFrom selector="{{AdminProductSEOSection.urlKeyInput}}" stepKey="grabUrlKey"/> - <amOnPage url="{$grabUrlKey}.html" stepKey="amOnConfigurableProductPage"/> - <waitForPageLoad stepKey="waitForProductPageLoad"/> - <waitForElementVisible selector="{{StorefrontProductInfoMainSection.productOptionSelect($createFirstAttribute.default_frontend_label$)}}" stepKey="waitForFirstSelect"/> - <selectOption userInput="$createFirstAttributeFirstOption.option[store_labels][0][label]$" selector="{{StorefrontProductInfoMainSection.productOptionSelect($createFirstAttribute.default_frontend_label$)}}" stepKey="selectFirstAttributeFirstOption"/> - <waitForElementVisible selector="{{StorefrontProductInfoMainSection.productOptionSelect($createSecondAttribute.default_frontend_label$)}}" stepKey="waitForSecondSelect"/> - <selectOption userInput="$createSecondAttributeSecondOption.option[store_labels][0][label]$" selector="{{StorefrontProductInfoMainSection.productOptionSelect($createSecondAttribute.default_frontend_label$)}}" stepKey="selectSecondAttributeSecondOption"/> - </test> -</tests> diff --git a/app/code/Magento/ConfigurableProduct/Test/Mftf/Test/StorefrontConfigurableProductDetailsTest/StorefrontConfigurableProductBasicInfoTest.xml b/app/code/Magento/ConfigurableProduct/Test/Mftf/Test/StorefrontConfigurableProductDetailsTest/StorefrontConfigurableProductBasicInfoTest.xml new file mode 100644 index 0000000000000..317563a468d74 --- /dev/null +++ b/app/code/Magento/ConfigurableProduct/Test/Mftf/Test/StorefrontConfigurableProductDetailsTest/StorefrontConfigurableProductBasicInfoTest.xml @@ -0,0 +1,54 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + /** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +--> +<tests xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/testSchema.xsd"> + <test name="StorefrontConfigurableProductBasicInfoTest"> + <annotations> + <features value="ConfigurableProduct"/> + <stories value="View configurable product details in storefront"/> + <title value="Guest customer should see basic Configurable Product details"/> + <description value="Guest customer should see basic Configurable Product details"/> + <severity value="MAJOR"/> + <testCaseId value="MC-77"/> + <group value="ConfigurableProduct"/> + </annotations> + + <before> + <createData entity="ApiCategory" stepKey="createCategory"/> + <actionGroup ref="AdminLoginActionGroup" stepKey="loginAsAdmin"/> + + <!-- Create a configurable product via the UI --> + <actionGroup ref="CreateConfigurableProductActionGroup" stepKey="createProduct"> + <argument name="product" value="_defaultProduct"/> + <argument name="category" value="$$createCategory$$"/> + </actionGroup> + </before> + <after> + <deleteData createDataKey="createCategory" stepKey="deleteCategory"/> + <actionGroup ref="DeleteProductBySkuActionGroup" stepKey="deleteProduct"> + <argument name="sku" value="{{_defaultProduct.sku}}"/> + </actionGroup> + <actionGroup ref="ClearFiltersAdminDataGridActionGroup" stepKey="clearProductsGridFilters"/> + <actionGroup ref="AdminLogoutActionGroup" stepKey="adminLogout"/> + </after> + + <!-- Verify configurable product details in storefront product view --> + <amOnPage url="{{_defaultProduct.urlKey}}.html" stepKey="amOnConfigurableProductPage"/> + <waitForPageLoad stepKey="wait"/> + <see userInput="{{_defaultProduct.name}}" selector="{{StorefrontProductInfoMainSection.productName}}" stepKey="seeProductName"/> + <see userInput="{{_defaultProduct.sku}}" selector="{{StorefrontProductInfoMainSection.productSku}}" stepKey="seeProductSku"/> + <see userInput="As low as" selector="{{StorefrontProductInfoMainSection.productPriceLabel}}" stepKey="seeProductPriceLabel"/> + <see userInput="IN STOCK" selector="{{StorefrontProductInfoMainSection.productStockStatus}}" stepKey="seeProductStockStatus"/> + <see userInput="1.00" selector="{{StorefrontProductInfoMainSection.productPrice}}" stepKey="seeProductPrice"/> + <see userInput="{{colorProductAttribute.default_label}}" selector="{{StorefrontProductInfoMainSection.productAttributeTitle1}}" stepKey="seeProductAttributeTitle"/> + <see selector="{{StorefrontProductInfoMainSection.productAttributeTitle1}}" userInput="{{colorProductAttribute.default_label}}" stepKey="seeColorAttributeName1"/> + <see userInput="{{colorProductAttribute1.name}}" selector="{{StorefrontProductInfoMainSection.productAttributeOptions1}}" stepKey="seeInDropDown1"/> + <see userInput="{{colorProductAttribute2.name}}" selector="{{StorefrontProductInfoMainSection.productAttributeOptions1}}" stepKey="seeInDropDown2"/> + <see userInput="{{colorProductAttribute3.name}}" selector="{{StorefrontProductInfoMainSection.productAttributeOptions1}}" stepKey="seeInDropDown3"/> + </test> +</tests> diff --git a/app/code/Magento/ConfigurableProduct/Test/Mftf/Test/StorefrontConfigurableProductDetailsTest/StorefrontConfigurableProductCanAddToCartTest.xml b/app/code/Magento/ConfigurableProduct/Test/Mftf/Test/StorefrontConfigurableProductDetailsTest/StorefrontConfigurableProductCanAddToCartTest.xml new file mode 100644 index 0000000000000..9fc6dce10c21a --- /dev/null +++ b/app/code/Magento/ConfigurableProduct/Test/Mftf/Test/StorefrontConfigurableProductDetailsTest/StorefrontConfigurableProductCanAddToCartTest.xml @@ -0,0 +1,51 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + /** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +--> +<tests xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/testSchema.xsd"> + <test name="StorefrontConfigurableProductCanAddToCartTest"> + <annotations> + <features value="ConfigurableProduct"/> + <stories value="View configurable product details in storefront"/> + <title value="Guest customer should be able to successfully add the product to the cart"/> + <description value="Guest customer should be able to successfully add the product to the cart"/> + <severity value="MAJOR"/> + <testCaseId value="MC-97"/> + <group value="ConfigurableProduct"/> + </annotations> + + <before> + <createData entity="ApiCategory" stepKey="createCategory"/> + <actionGroup ref="AdminLoginActionGroup" stepKey="loginAsAdmin"/> + + <!-- Create a configurable product via the UI --> + <actionGroup ref="CreateConfigurableProductActionGroup" stepKey="createProduct"> + <argument name="product" value="_defaultProduct"/> + <argument name="category" value="$$createCategory$$"/> + </actionGroup> + </before> + <after> + <deleteData createDataKey="createCategory" stepKey="deleteCategory"/> + <actionGroup ref="DeleteProductBySkuActionGroup" stepKey="deleteProduct"> + <argument name="sku" value="{{_defaultProduct.sku}}"/> + </actionGroup> + <actionGroup ref="ClearFiltersAdminDataGridActionGroup" stepKey="clearProductsGridFilters"/> + <actionGroup ref="AdminLogoutActionGroup" stepKey="adminLogout"/> + </after> + + <!-- Verify adding configurable product to cart after an option is selected in storefront product view --> + <amOnPage url="{{_defaultProduct.urlKey}}.html" stepKey="amOnConfigurableProductPage"/> + <waitForPageLoad stepKey="wait"/> + <see userInput="{{_defaultProduct.name}}" selector="{{StorefrontProductInfoMainSection.productName}}" stepKey="seeProductName"/> + <selectOption userInput="{{colorProductAttribute1.name}}" selector="{{StorefrontProductInfoMainSection.productAttributeOptionsSelectButton}}" stepKey="selectOption1"/> + <waitForElementVisible selector="{{StorefrontProductInfoMainSection.AddToCart}}" stepKey="waitForAddToCartVisible"/> + <actionGroup ref="StorefrontAddProductToCartActionGroup" stepKey="addProductToCart"> + <argument name="product" value="_defaultProduct"/> + <argument name="productCount" value="1"/> + </actionGroup> + </test> +</tests> diff --git a/app/code/Magento/ConfigurableProduct/Test/Mftf/Test/StorefrontConfigurableProductDetailsTest/StorefrontConfigurableProductCantAddToCartTest.xml b/app/code/Magento/ConfigurableProduct/Test/Mftf/Test/StorefrontConfigurableProductDetailsTest/StorefrontConfigurableProductCantAddToCartTest.xml new file mode 100644 index 0000000000000..01859f995b00b --- /dev/null +++ b/app/code/Magento/ConfigurableProduct/Test/Mftf/Test/StorefrontConfigurableProductDetailsTest/StorefrontConfigurableProductCantAddToCartTest.xml @@ -0,0 +1,48 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + /** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +--> +<tests xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/testSchema.xsd"> + <test name="StorefrontConfigurableProductCantAddToCartTest"> + <annotations> + <features value="ConfigurableProduct"/> + <stories value="View configurable product details in storefront"/> + <title value="Guest customer should not be able to add the product to the cart if options are not selected"/> + <description value="Guest customer should not be able to add the product to the cart if options are not selected"/> + <severity value="MAJOR"/> + <testCaseId value="MC-81"/> + <group value="ConfigurableProduct"/> + </annotations> + + <before> + <createData entity="ApiCategory" stepKey="createCategory"/> + <actionGroup ref="AdminLoginActionGroup" stepKey="loginAsAdmin"/> + + <!-- Create a configurable product via the UI --> + <actionGroup ref="CreateConfigurableProductActionGroup" stepKey="createProduct"> + <argument name="product" value="_defaultProduct"/> + <argument name="category" value="$$createCategory$$"/> + </actionGroup> + </before> + <after> + <deleteData createDataKey="createCategory" stepKey="deleteCategory"/> + <actionGroup ref="DeleteProductBySkuActionGroup" stepKey="deleteProduct"> + <argument name="sku" value="{{_defaultProduct.sku}}"/> + </actionGroup> + <actionGroup ref="ClearFiltersAdminDataGridActionGroup" stepKey="clearProductsGridFilters"/> + <actionGroup ref="AdminLogoutActionGroup" stepKey="adminLogout"/> + </after> + + <!-- Verify not able to add configurable product to cart when no option is selected in storefront product view --> + <amOnPage url="{{_defaultProduct.urlKey}}.html" stepKey="amOnConfigurableProductPage"/> + <waitForPageLoad stepKey="wait"/> + <see userInput="{{_defaultProduct.name}}" selector="{{StorefrontProductInfoMainSection.productName}}" stepKey="seeProductName"/> + <waitForElementVisible selector="{{StorefrontProductInfoMainSection.AddToCart}}" stepKey="waitForAddToCartVisible"/> + <click selector="{{StorefrontProductInfoMainSection.AddToCart}}" stepKey="clickAddToCart"/> + <see userInput="This is a required field" selector="{{StorefrontProductInfoMainSection.productAttributeOptionsError}}" stepKey="seeError"/> + </test> +</tests> diff --git a/app/code/Magento/ConfigurableProduct/Test/Mftf/Test/StorefrontConfigurableProductDetailsTest/StorefrontConfigurableProductOptionsTest.xml b/app/code/Magento/ConfigurableProduct/Test/Mftf/Test/StorefrontConfigurableProductDetailsTest/StorefrontConfigurableProductOptionsTest.xml new file mode 100644 index 0000000000000..1aef8c33785ca --- /dev/null +++ b/app/code/Magento/ConfigurableProduct/Test/Mftf/Test/StorefrontConfigurableProductDetailsTest/StorefrontConfigurableProductOptionsTest.xml @@ -0,0 +1,54 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + /** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +--> +<tests xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/testSchema.xsd"> + <test name="StorefrontConfigurableProductOptionsTest"> + <annotations> + <features value="ConfigurableProduct"/> + <stories value="View configurable product details in storefront"/> + <title value="Guest customer should be able to see product configuration options"/> + <description value="Guest customer should be able to see product configuration options"/> + <severity value="MAJOR"/> + <testCaseId value="MC-92"/> + <group value="ConfigurableProduct"/> + </annotations> + + <before> + <createData entity="ApiCategory" stepKey="createCategory"/> + <actionGroup ref="AdminLoginActionGroup" stepKey="loginAsAdmin"/> + + <!-- Create a configurable product via the UI --> + <actionGroup ref="CreateConfigurableProductActionGroup" stepKey="createProduct"> + <argument name="product" value="_defaultProduct"/> + <argument name="category" value="$$createCategory$$"/> + </actionGroup> + </before> + <after> + <deleteData createDataKey="createCategory" stepKey="deleteCategory"/> + <actionGroup ref="DeleteProductBySkuActionGroup" stepKey="deleteProduct"> + <argument name="sku" value="{{_defaultProduct.sku}}"/> + </actionGroup> + <actionGroup ref="ClearFiltersAdminDataGridActionGroup" stepKey="clearProductsGridFilters"/> + <actionGroup ref="AdminLogoutActionGroup" stepKey="adminLogout"/> + </after> + + <!-- Verify configurable product options in storefront product view --> + <amOnPage url="{{_defaultProduct.urlKey}}.html" stepKey="amOnConfigurableProductPage"/> + <waitForPageLoad stepKey="wait"/> + <see userInput="{{_defaultProduct.name}}" selector="{{StorefrontProductInfoMainSection.productName}}" stepKey="seeProductName"/> + <selectOption userInput="{{colorProductAttribute1.name}}" selector="{{StorefrontProductInfoMainSection.productAttributeOptionsSelectButton}}" stepKey="selectOption1"/> + <dontSee userInput="As low as" selector="{{StorefrontProductInfoMainSection.productPriceLabel}}" stepKey="dontSeeProductPriceLabel1"/> + <see userInput="1.00" selector="{{StorefrontProductInfoMainSection.productPrice}}" stepKey="seeProductPrice1"/> + <selectOption userInput="{{colorProductAttribute2.name}}" selector="{{StorefrontProductInfoMainSection.productAttributeOptionsSelectButton}}" stepKey="selectOption2"/> + <dontSee userInput="As low as" selector="{{StorefrontProductInfoMainSection.productPriceLabel}}" stepKey="dontSeeProductPriceLabel2"/> + <see userInput="2.00" selector="{{StorefrontProductInfoMainSection.productPrice}}" stepKey="seeProductPrice2"/> + <selectOption userInput="{{colorProductAttribute3.name}}" selector="{{StorefrontProductInfoMainSection.productAttributeOptionsSelectButton}}" stepKey="selectOption3"/> + <dontSee userInput="As low as" selector="{{StorefrontProductInfoMainSection.productPriceLabel}}" stepKey="dontSeeProductPriceLabel3"/> + <see userInput="3.00" selector="{{StorefrontProductInfoMainSection.productPrice}}" stepKey="seeProductPrice3"/> + </test> +</tests> diff --git a/app/code/Magento/ConfigurableProduct/Test/Mftf/Test/StorefrontConfigurableProductDetailsTest/StorefrontConfigurableProductVariationsTest.xml b/app/code/Magento/ConfigurableProduct/Test/Mftf/Test/StorefrontConfigurableProductDetailsTest/StorefrontConfigurableProductVariationsTest.xml new file mode 100644 index 0000000000000..e51250c76bfe7 --- /dev/null +++ b/app/code/Magento/ConfigurableProduct/Test/Mftf/Test/StorefrontConfigurableProductDetailsTest/StorefrontConfigurableProductVariationsTest.xml @@ -0,0 +1,116 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + /** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +--> +<tests xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/testSchema.xsd"> + <test name="StorefrontConfigurableProductVariationsTest"> + <annotations> + <features value="ConfigurableProduct"/> + <stories value="Configurable Product"/> + <title value="Customer should get the right options list"/> + <description value="Customer should get the right options list for each variation of configurable product"/> + <severity value="CRITICAL"/> + <testCaseId value="MC-23027"/> + <useCaseId value="MC-22732"/> + <group value="configurable_product"/> + </annotations> + + <before> + <createData entity="ApiCategory" stepKey="createCategory"/> + <!-- Add first attribute with options --> + <createData entity="productAttributeWithTwoOptions" stepKey="createFirstAttribute"/> + <createData entity="productAttributeOption1" stepKey="createFirstAttributeFirstOption"> + <requiredEntity createDataKey="createFirstAttribute"/> + </createData> + <createData entity="productAttributeOption2" stepKey="createFirstAttributeSecondOption"> + <requiredEntity createDataKey="createFirstAttribute"/> + </createData> + <!-- Add second attribute with options --> + <createData entity="productAttributeWithTwoOptions" stepKey="createSecondAttribute"/> + <createData entity="productAttributeOption1" stepKey="createSecondAttributeFirstOption"> + <requiredEntity createDataKey="createSecondAttribute"/> + </createData> + <createData entity="productAttributeOption2" stepKey="createSecondAttributeSecondOption"> + <requiredEntity createDataKey="createSecondAttribute"/> + </createData> + <actionGroup ref="AdminLoginActionGroup" stepKey="loginAsAdmin"/> + </before> + <after> + <deleteData createDataKey="createCategory" stepKey="deleteCategory"/> + <actionGroup ref="DeleteProductBySkuActionGroup" stepKey="deleteProduct"> + <argument name="sku" value="{{BaseConfigurableProduct.sku}}"/> + </actionGroup> + <deleteData createDataKey="createFirstAttribute" stepKey="deleteFirstAttribute"/> + <deleteData createDataKey="createSecondAttribute" stepKey="deleteSecondAttribute"/> + <actionGroup ref="ClearFiltersAdminDataGridActionGroup" stepKey="clearProductsGridFilters"/> + <actionGroup ref="AdminLogoutActionGroup" stepKey="adminLogout"/> + + <!-- Reindex invalidated indices after product attribute has been created/deleted --> + <magentoCron groups="index" stepKey="reindexInvalidatedIndices"/> + </after> + + <actionGroup ref="AdminOpenProductIndexPageActionGroup" stepKey="openProductIndexPage"/> + <actionGroup ref="GoToCreateProductPageActionGroup" stepKey="navigateToCreateProductPage"> + <argument name="product" value="BaseConfigurableProduct"/> + </actionGroup> + <actionGroup ref="FillMainProductFormActionGroup" stepKey="fillProductForm"> + <argument name="product" value="BaseConfigurableProduct"/> + </actionGroup> + <actionGroup ref="SetCategoryByNameActionGroup" stepKey="addCategoryToProduct"> + <argument name="categoryName" value="$createCategory.name$"/> + </actionGroup> + <actionGroup ref="SetProductUrlKeyByStringActionGroup" stepKey="fillUrlKey"> + <argument name="urlKey" value="{{BaseConfigurableProduct.urlKey}}"/> + </actionGroup> + <click selector="{{AdminProductFormConfigurationsSection.createConfigurations}}" stepKey="clickOnCreateConfigurations"/> + <actionGroup ref="AdminSelectAttributeInConfigurableAttributesGrid" stepKey="checkFirstAttribute"> + <argument name="attributeCode" value="$createFirstAttribute.attribute_code$"/> + </actionGroup> + <actionGroup ref="AdminSelectAttributeInConfigurableAttributesGrid" stepKey="checkSecondAttribute"> + <argument name="attributeCode" value="$createSecondAttribute.attribute_code$"/> + </actionGroup> + <click selector="{{AdminCreateProductConfigurationsPanel.next}}" stepKey="clickOnNextButton"/> + <waitForPageLoad stepKey="waitForStepLoad"/> + <click selector="{{AdminCreateProductConfigurationsPanel.selectAllByAttribute($createFirstAttribute.default_frontend_label$)}}" stepKey="clickFirstAttributeSelectAll"/> + <click selector="{{AdminCreateProductConfigurationsPanel.selectAllByAttribute($createSecondAttribute.default_frontend_label$)}}" stepKey="clickSecondAttributeSelectAll"/> + <click selector="{{AdminCreateProductConfigurationsPanel.next}}" stepKey="clickSecondNextStep"/> + <waitForElement selector="{{AdminCreateProductConfigurationsPanel.next}}" stepKey="waitThirdNextButton"/> + <click selector="{{AdminCreateProductConfigurationsPanel.next}}" stepKey="clickThirdStep"/> + <waitForElement selector="{{AdminCreateProductConfigurationsPanel.next}}" stepKey="waitGenerateConfigurationsButton"/> + <click selector="{{AdminCreateProductConfigurationsPanel.next}}" stepKey="clickToGenerateConfigurations"/> + + <waitForElementVisible selector="{{AdminCreateProductConfigurationsPanel.variationsGrid}}" stepKey="waitForVariationsGrid"/> + <actionGroup ref="AdminChangeConfigurableProductVariationQty" stepKey="setFirstVariationQuantity"> + <argument name="rowIndex" value="0"/> + <argument name="quantity" value="0"/> + </actionGroup> + <actionGroup ref="AdminChangeConfigurableProductVariationQty" stepKey="setSecondVariationQuantity"> + <argument name="rowIndex" value="1"/> + <argument name="quantity" value="1"/> + </actionGroup> + <actionGroup ref="AdminChangeConfigurableProductVariationQty" stepKey="setThirdVariationQuantity"> + <argument name="rowIndex" value="2"/> + <argument name="quantity" value="1"/> + </actionGroup> + <actionGroup ref="AdminChangeConfigurableProductVariationQty" stepKey="setFourthVariationQuantity"> + <argument name="rowIndex" value="3"/> + <argument name="quantity" value="1"/> + </actionGroup> + <actionGroup ref="SaveConfigurableProductActionGroup" stepKey="saveConfigurableProduct"> + <argument name="product" value="BaseConfigurableProduct"/> + </actionGroup> + <scrollTo selector="{{AdminProductSEOSection.sectionHeader}}" x="0" y="-80" stepKey="scrollToAdminProductSEOSection"/> + <click selector="{{AdminProductSEOSection.sectionHeader}}" stepKey="clickAdminProductSEOSectionHeader"/> + <grabValueFrom selector="{{AdminProductSEOSection.urlKeyInput}}" stepKey="grabUrlKey"/> + <amOnPage url="{$grabUrlKey}.html" stepKey="amOnConfigurableProductPage"/> + <waitForPageLoad stepKey="waitForProductPageLoad"/> + <waitForElementVisible selector="{{StorefrontProductInfoMainSection.productOptionSelect($createFirstAttribute.default_frontend_label$)}}" stepKey="waitForFirstSelect"/> + <selectOption userInput="$createFirstAttributeFirstOption.option[store_labels][0][label]$" selector="{{StorefrontProductInfoMainSection.productOptionSelect($createFirstAttribute.default_frontend_label$)}}" stepKey="selectFirstAttributeFirstOption"/> + <waitForElementVisible selector="{{StorefrontProductInfoMainSection.productOptionSelect($createSecondAttribute.default_frontend_label$)}}" stepKey="waitForSecondSelect"/> + <selectOption userInput="$createSecondAttributeSecondOption.option[store_labels][0][label]$" selector="{{StorefrontProductInfoMainSection.productOptionSelect($createSecondAttribute.default_frontend_label$)}}" stepKey="selectSecondAttributeSecondOption"/> + </test> +</tests> diff --git a/app/code/Magento/ConfigurableProduct/Test/Mftf/Test/StorefrontConfigurableProductViewTest.xml b/app/code/Magento/ConfigurableProduct/Test/Mftf/Test/StorefrontConfigurableProductViewTest.xml deleted file mode 100644 index bfdadd54b872c..0000000000000 --- a/app/code/Magento/ConfigurableProduct/Test/Mftf/Test/StorefrontConfigurableProductViewTest.xml +++ /dev/null @@ -1,130 +0,0 @@ -<?xml version="1.0" encoding="UTF-8"?> -<!-- - /** - * Copyright © Magento, Inc. All rights reserved. - * See COPYING.txt for license details. - */ ---> - -<tests xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" - xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/testSchema.xsd"> - <test name="StorefrontConfigurableProductGridViewTest"> - <annotations> - <features value="ConfigurableProduct"/> - <stories value="View configurable product in a category in storefront"/> - <title value="customer should see the configurable product in the category grid"/> - <description value="customer should see the configurable product in the category grid"/> - <severity value="CRITICAL"/> - <testCaseId value="MC-94"/> - <group value="ConfigurableProduct"/> - </annotations> - - <before> - <createData entity="ApiCategory" stepKey="createCategory"/> - <actionGroup ref="LoginAsAdmin" stepKey="loginAsAdmin1"/> - <!-- Create a configurable product via the UI --> - <actionGroup ref="CreateConfigurableProductActionGroup" stepKey="createProduct"> - <argument name="product" value="_defaultProduct"/> - <argument name="category" value="$$createCategory$$"/> - </actionGroup> - <!-- TODO: REMOVE AFTER FIX MC-21717 --> - <magentoCLI command="indexer:reindex" stepKey="reindex"/> - <magentoCLI command="cache:flush eav" stepKey="flushCache"/> - </before> - - <after> - <deleteData createDataKey="createCategory" stepKey="deleteCategory"/> - <actionGroup stepKey="deleteProduct" ref="DeleteProductBySkuActionGroup"> - <argument name="sku" value="{{_defaultProduct.sku}}"/> - </actionGroup> - <actionGroup ref="logout" stepKey="adminLogout"/> - </after> - - <!-- Verify the storefront category grid view --> - <amOnPage url="{{StorefrontCategoryPage.url($$createCategory.name$$)}}" stepKey="amOnCategoryPage1"/> - <waitForPageLoad stepKey="wait1"/> - <seeElement selector="{{StorefrontCategoryMainSection.productImage}}" stepKey="seePhoto"/> - <see selector="{{StorefrontCategoryMainSection.productLink}}" userInput="{{_defaultProduct.name}}" stepKey="seeName"/> - <seeElement selector="{{StorefrontCategoryMainSection.productLinkByHref(_defaultProduct.urlKey)}}" stepKey="seeNameLink"/> - <see selector="{{StorefrontCategoryMainSection.productPrice}}" userInput="{{colorProductAttribute1.price}}" stepKey="seePrice"/> - </test> - - <test name="StorefrontConfigurableProductListViewTest"> - <annotations> - <features value="ConfigurableProduct"/> - <stories value="View configurable product in a category in storefront"/> - <title value="customer should see the configurable product in the category list"/> - <description value="customer should see the configurable product in the category list"/> - <severity value="CRITICAL"/> - <testCaseId value="MC-61"/> - <group value="ConfigurableProduct"/> - </annotations> - - <before> - <createData entity="ApiCategory" stepKey="createCategory"/> - <actionGroup ref="LoginAsAdmin" stepKey="loginAsAdmin1"/> - - <!-- Create a configurable product via the UI --> - <actionGroup ref="CreateConfigurableProductActionGroup" stepKey="createProduct"> - <argument name="product" value="_defaultProduct"/> - <argument name="category" value="$$createCategory$$"/> - </actionGroup> - </before> - - <after> - <deleteData createDataKey="createCategory" stepKey="deleteCategory"/> - <actionGroup ref="logout" stepKey="adminLogout"/> - </after> - - <!-- Verify storefront category list view --> - <amOnPage url="{{StorefrontCategoryPage.url($$createCategory.name$$)}}" stepKey="amOnCategoryPage1"/> - <waitForPageLoad stepKey="wait1"/> - <click selector="{{StorefrontCategoryMainSection.modeListButton}}" stepKey="clickListView"/> - <waitForPageLoad stepKey="wait2"/> - <seeElement selector="{{StorefrontCategoryMainSection.productImage}}" stepKey="seePhoto"/> - <see selector="{{StorefrontCategoryMainSection.productLink}}" userInput="{{_defaultProduct.name}}" stepKey="seeName"/> - <seeElement selector="{{StorefrontCategoryMainSection.productLinkByHref(_defaultProduct.urlKey)}}" stepKey="seeNameLink"/> - <see selector="{{StorefrontCategoryMainSection.productPrice}}" userInput="{{colorProductAttribute1.price}}" stepKey="seePrice"/> - </test> - - <test name="StorefrontConfigurableProductAddToCartTest"> - <annotations> - <features value="ConfigurableProduct"/> - <stories value="View configurable product in a category in storefront"/> - <title value="customer should be taken to the product details page when clicking add to cart"/> - <description value="customer should be taken to the product details page when clicking add to cart"/> - <severity value="CRITICAL"/> - <testCaseId value="MC-89"/> - <group value="ConfigurableProduct"/> - </annotations> - - <before> - <createData entity="ApiCategory" stepKey="createCategory"/> - <actionGroup ref="LoginAsAdmin" stepKey="loginAsAdmin1"/> - - <!-- Create a configurable product via the UI --> - <actionGroup ref="CreateConfigurableProductActionGroup" stepKey="createProduct"> - <argument name="product" value="_defaultProduct"/> - <argument name="category" value="$$createCategory$$"/> - </actionGroup> - </before> - - <after> - <deleteData createDataKey="createCategory" stepKey="deleteCategory"/> - <actionGroup ref="logout" stepKey="adminLogout"/> - </after> - - <!-- Should be taken to product details page when adding to cart because an option needs to be selected --> - <amOnPage url="{{StorefrontCategoryPage.url($$createCategory.name$$)}}" stepKey="amOnCategoryPage1"/> - <waitForPageLoad stepKey="wait1"/> - <click selector="{{StorefrontCategoryMainSection.modeListButton}}" stepKey="clickListView"/> - <waitForPageLoad stepKey="wait2"/> - <click selector="{{StorefrontCategoryMainSection.AddToCartBtn}}" stepKey="clickAddToCart"/> - <waitForPageLoad stepKey="wait3"/> - <grabFromCurrentUrl stepKey="grabUrl"/> - <assertContains stepKey="assertUrl"> - <expectedResult type="string">{{_defaultProduct.urlKey}}</expectedResult> - <actualResult type="string">{$grabUrl}</actualResult> - </assertContains> - </test> -</tests> diff --git a/app/code/Magento/ConfigurableProduct/Test/Mftf/Test/StorefrontConfigurableProductViewTest/StorefrontConfigurableProductAddToCartTest.xml b/app/code/Magento/ConfigurableProduct/Test/Mftf/Test/StorefrontConfigurableProductViewTest/StorefrontConfigurableProductAddToCartTest.xml new file mode 100644 index 0000000000000..1fee355ad5e44 --- /dev/null +++ b/app/code/Magento/ConfigurableProduct/Test/Mftf/Test/StorefrontConfigurableProductViewTest/StorefrontConfigurableProductAddToCartTest.xml @@ -0,0 +1,50 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + /** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +--> +<tests xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/testSchema.xsd"> + <test name="StorefrontConfigurableProductAddToCartTest"> + <annotations> + <features value="ConfigurableProduct"/> + <stories value="View configurable product in a category in storefront"/> + <title value="customer should be taken to the product details page when clicking add to cart"/> + <description value="customer should be taken to the product details page when clicking add to cart"/> + <severity value="CRITICAL"/> + <testCaseId value="MC-89"/> + <group value="ConfigurableProduct"/> + </annotations> + + <before> + <createData entity="ApiCategory" stepKey="createCategory"/> + <actionGroup ref="AdminLoginActionGroup" stepKey="loginAsAdmin1"/> + + <!-- Create a configurable product via the UI --> + <actionGroup ref="CreateConfigurableProductActionGroup" stepKey="createProduct"> + <argument name="product" value="_defaultProduct"/> + <argument name="category" value="$$createCategory$$"/> + </actionGroup> + </before> + + <after> + <deleteData createDataKey="createCategory" stepKey="deleteCategory"/> + <actionGroup ref="AdminLogoutActionGroup" stepKey="adminLogout"/> + </after> + + <!-- Should be taken to product details page when adding to cart because an option needs to be selected --> + <amOnPage url="{{StorefrontCategoryPage.url($$createCategory.name$$)}}" stepKey="amOnCategoryPage1"/> + <waitForPageLoad stepKey="wait1"/> + <click selector="{{StorefrontCategoryMainSection.modeListButton}}" stepKey="clickListView"/> + <waitForPageLoad stepKey="wait2"/> + <click selector="{{StorefrontCategoryMainSection.AddToCartBtn}}" stepKey="clickAddToCart"/> + <waitForPageLoad stepKey="wait3"/> + <grabFromCurrentUrl stepKey="grabUrl"/> + <assertContains stepKey="assertUrl"> + <expectedResult type="string">{{_defaultProduct.urlKey}}</expectedResult> + <actualResult type="string">{$grabUrl}</actualResult> + </assertContains> + </test> +</tests> diff --git a/app/code/Magento/ConfigurableProduct/Test/Mftf/Test/StorefrontConfigurableProductViewTest/StorefrontConfigurableProductGridViewTest.xml b/app/code/Magento/ConfigurableProduct/Test/Mftf/Test/StorefrontConfigurableProductViewTest/StorefrontConfigurableProductGridViewTest.xml new file mode 100644 index 0000000000000..ca0426f1b97d5 --- /dev/null +++ b/app/code/Magento/ConfigurableProduct/Test/Mftf/Test/StorefrontConfigurableProductViewTest/StorefrontConfigurableProductGridViewTest.xml @@ -0,0 +1,50 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + /** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +--> +<tests xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/testSchema.xsd"> + <test name="StorefrontConfigurableProductGridViewTest"> + <annotations> + <features value="ConfigurableProduct"/> + <stories value="View configurable product in a category in storefront"/> + <title value="customer should see the configurable product in the category grid"/> + <description value="customer should see the configurable product in the category grid"/> + <severity value="CRITICAL"/> + <testCaseId value="MC-94"/> + <group value="ConfigurableProduct"/> + </annotations> + + <before> + <createData entity="ApiCategory" stepKey="createCategory"/> + <actionGroup ref="AdminLoginActionGroup" stepKey="loginAsAdmin1"/> + <!-- Create a configurable product via the UI --> + <actionGroup ref="CreateConfigurableProductActionGroup" stepKey="createProduct"> + <argument name="product" value="_defaultProduct"/> + <argument name="category" value="$$createCategory$$"/> + </actionGroup> + <!-- TODO: REMOVE AFTER FIX MC-21717 --> + <magentoCLI command="indexer:reindex" stepKey="reindex"/> + <magentoCLI command="cache:flush eav" stepKey="flushCache"/> + </before> + + <after> + <deleteData createDataKey="createCategory" stepKey="deleteCategory"/> + <actionGroup stepKey="deleteProduct" ref="DeleteProductBySkuActionGroup"> + <argument name="sku" value="{{_defaultProduct.sku}}"/> + </actionGroup> + <actionGroup ref="AdminLogoutActionGroup" stepKey="adminLogout"/> + </after> + + <!-- Verify the storefront category grid view --> + <amOnPage url="{{StorefrontCategoryPage.url($$createCategory.name$$)}}" stepKey="amOnCategoryPage1"/> + <waitForPageLoad stepKey="wait1"/> + <seeElement selector="{{StorefrontCategoryMainSection.productImage}}" stepKey="seePhoto"/> + <see selector="{{StorefrontCategoryMainSection.productLink}}" userInput="{{_defaultProduct.name}}" stepKey="seeName"/> + <seeElement selector="{{StorefrontCategoryMainSection.productLinkByHref(_defaultProduct.urlKey)}}" stepKey="seeNameLink"/> + <see selector="{{StorefrontCategoryMainSection.productPrice}}" userInput="{{colorProductAttribute1.price}}" stepKey="seePrice"/> + </test> +</tests> diff --git a/app/code/Magento/ConfigurableProduct/Test/Mftf/Test/StorefrontConfigurableProductViewTest/StorefrontConfigurableProductListViewTest.xml b/app/code/Magento/ConfigurableProduct/Test/Mftf/Test/StorefrontConfigurableProductViewTest/StorefrontConfigurableProductListViewTest.xml new file mode 100644 index 0000000000000..619e50acba848 --- /dev/null +++ b/app/code/Magento/ConfigurableProduct/Test/Mftf/Test/StorefrontConfigurableProductViewTest/StorefrontConfigurableProductListViewTest.xml @@ -0,0 +1,47 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + /** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +--> +<tests xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/testSchema.xsd"> + <test name="StorefrontConfigurableProductListViewTest"> + <annotations> + <features value="ConfigurableProduct"/> + <stories value="View configurable product in a category in storefront"/> + <title value="customer should see the configurable product in the category list"/> + <description value="customer should see the configurable product in the category list"/> + <severity value="CRITICAL"/> + <testCaseId value="MC-61"/> + <group value="ConfigurableProduct"/> + </annotations> + + <before> + <createData entity="ApiCategory" stepKey="createCategory"/> + <actionGroup ref="AdminLoginActionGroup" stepKey="loginAsAdmin1"/> + + <!-- Create a configurable product via the UI --> + <actionGroup ref="CreateConfigurableProductActionGroup" stepKey="createProduct"> + <argument name="product" value="_defaultProduct"/> + <argument name="category" value="$$createCategory$$"/> + </actionGroup> + </before> + + <after> + <deleteData createDataKey="createCategory" stepKey="deleteCategory"/> + <actionGroup ref="AdminLogoutActionGroup" stepKey="adminLogout"/> + </after> + + <!-- Verify storefront category list view --> + <amOnPage url="{{StorefrontCategoryPage.url($$createCategory.name$$)}}" stepKey="amOnCategoryPage1"/> + <waitForPageLoad stepKey="wait1"/> + <click selector="{{StorefrontCategoryMainSection.modeListButton}}" stepKey="clickListView"/> + <waitForPageLoad stepKey="wait2"/> + <seeElement selector="{{StorefrontCategoryMainSection.productImage}}" stepKey="seePhoto"/> + <see selector="{{StorefrontCategoryMainSection.productLink}}" userInput="{{_defaultProduct.name}}" stepKey="seeName"/> + <seeElement selector="{{StorefrontCategoryMainSection.productLinkByHref(_defaultProduct.urlKey)}}" stepKey="seeNameLink"/> + <see selector="{{StorefrontCategoryMainSection.productPrice}}" userInput="{{colorProductAttribute1.price}}" stepKey="seePrice"/> + </test> +</tests> diff --git a/app/code/Magento/ConfigurableProduct/Test/Mftf/Test/StorefrontConfigurableProductWithFileCustomOptionTest.xml b/app/code/Magento/ConfigurableProduct/Test/Mftf/Test/StorefrontConfigurableProductWithFileCustomOptionTest.xml index e24c8e4d94916..e983559fb6bcf 100644 --- a/app/code/Magento/ConfigurableProduct/Test/Mftf/Test/StorefrontConfigurableProductWithFileCustomOptionTest.xml +++ b/app/code/Magento/ConfigurableProduct/Test/Mftf/Test/StorefrontConfigurableProductWithFileCustomOptionTest.xml @@ -25,10 +25,10 @@ <after> <deleteData createDataKey="createCategory" stepKey="deleteCategory"/> - <actionGroup ref="logout" stepKey="adminLogout"/> + <actionGroup ref="AdminLogoutActionGroup" stepKey="adminLogout"/> </after> - <actionGroup ref="LoginAsAdmin" stepKey="loginAsAdmin"/> + <actionGroup ref="AdminLoginActionGroup" stepKey="loginAsAdmin"/> <!-- Create a configurable product via the UI --> <actionGroup ref="CreateConfigurableProductActionGroup" stepKey="createProduct"> @@ -40,8 +40,7 @@ <click selector="{{AdminProductFormActionSection.saveButton}}" stepKey="saveProduct"/> <!--Go to storefront--> - <amOnPage url="{{StorefrontHomePage.url}}" stepKey="goToHomePage"/> - <waitForPageLoad stepKey="waitForHomePageLoad"/> + <actionGroup ref="StorefrontOpenHomePageActionGroup" stepKey="goToHomePage"/> <click selector="{{StorefrontNavigationSection.topCategory($$createCategory.name$$)}}" stepKey="goToCategoryStorefront"/> <waitForPageLoad stepKey="waitForCategoryPageLoad"/> <see selector="{{StorefrontCategoryMainSection.CategoryTitle}}" userInput="$$createCategory.name$$" stepKey="seeOnCategoryPage"/> diff --git a/app/code/Magento/ConfigurableProduct/Test/Mftf/Test/StorefrontGalleryConfigurableProductWithSeveralAttributesPrependMediaTest.xml b/app/code/Magento/ConfigurableProduct/Test/Mftf/Test/StorefrontGalleryConfigurableProductWithSeveralAttributesPrependMediaTest.xml new file mode 100644 index 0000000000000..3e74edfb68074 --- /dev/null +++ b/app/code/Magento/ConfigurableProduct/Test/Mftf/Test/StorefrontGalleryConfigurableProductWithSeveralAttributesPrependMediaTest.xml @@ -0,0 +1,474 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + /** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +--> + +<tests xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/testSchema.xsd"> + <test name="StorefrontGalleryConfigurableProductWithSeveralAttributesPrependMediaTest"> + <annotations> + <features value="ConfigurableProduct"/> + <stories value="Prepend variation media on storefront"/> + <title value="Storefront Gallery - Configurable Product with several attributes: prepend variation media"/> + <description value="Storefront Gallery - Configurable Product with several attributes: prepend variation media"/> + <severity value="AVERAGE"/> + <testCaseId value="MC-26396"/> + <group value="catalog"/> + <group value="configurableProduct"/> + <group value="swatch"/> + </annotations> + + <before> + <!--Create 1 configurable product with 2 variations--> + <createData entity="ApiConfigurableProductWithDescription" stepKey="createConfigurableProduct"/> + <!--Create product drop down attribute--> + <createData entity="productDropDownAttribute" stepKey="createDropdownAttribute"/> + <createData entity="productAttributeOption1" stepKey="dropdownAttributeFirstOption"> + <requiredEntity createDataKey="createDropdownAttribute"/> + </createData> + <createData entity="productAttributeOption2" stepKey="dropdownAttributeSecondOption"> + <requiredEntity createDataKey="createDropdownAttribute"/> + </createData> + + <!-- Create product swatch attribute with 2 variations --> + <createData entity="VisualSwatchProductAttributeForm" stepKey="createVisualSwatchAttribute"/> + <createData entity="SwatchProductAttributeOption1" stepKey="swatchAttributeFirstOption"> + <requiredEntity createDataKey="createVisualSwatchAttribute"/> + </createData> + <createData entity="SwatchProductAttributeOption2" stepKey="swatchAttributeSecondOption"> + <requiredEntity createDataKey="createVisualSwatchAttribute"/> + </createData> + + <actionGroup ref="AdminLoginActionGroup" stepKey="loginAsAdmin"/> + + <!-- Open configurable product edit page --> + <amOnPage url="{{AdminProductEditPage.url($createConfigurableProduct.id$)}}" stepKey="goToProductIndex"/> + + <!-- Add attributes to configurable product--> + <conditionalClick selector="{{AdminProductFormConfigurationsSection.sectionHeader}}" dependentSelector="{{AdminProductFormConfigurationsSection.createConfigurations}}" visible="false" stepKey="openConfigurationSection"/> + <click selector="{{AdminProductFormConfigurationsSection.createConfigurations}}" stepKey="openConfigurationPanel"/> + + <!-- Find Dropdown attribute in grid and select it --> + <conditionalClick selector="{{AdminDataGridHeaderSection.clearFilters}}" dependentSelector="{{AdminDataGridHeaderSection.clearFilters}}" visible="true" stepKey="clearAttributeGridFiltersToFindDropdownAttribute"/> + <click selector="{{AdminDataGridHeaderSection.filters}}" stepKey="openFiltersPaneForDropdownAttribute"/> + <fillField selector="{{AdminDataGridHeaderSection.attributeCodeFilterInput}}" userInput="$createDropdownAttribute.attribute_code$" stepKey="fillAttributeCodeFilterFieldForDropdownAttribute"/> + <click selector="{{AdminDataGridHeaderSection.applyFilters}}" stepKey="clickApplyFiltersButtonForDropdownAttribute"/> + <click selector="{{AdminDataGridTableSection.rowCheckbox('1')}}" stepKey="selectDropdownAttribute"/> + <!-- Find Swatch attribute in grid and select it --> + <conditionalClick selector="{{AdminDataGridHeaderSection.clearFilters}}" dependentSelector="{{AdminDataGridHeaderSection.clearFilters}}" visible="true" stepKey="clearAttributeGridFiltersToFindSwatchAttribute"/> + <click selector="{{AdminDataGridHeaderSection.filters}}" stepKey="openFiltersPaneForSwatchAttribute"/> + <fillField selector="{{AdminDataGridHeaderSection.attributeCodeFilterInput}}" userInput="$createVisualSwatchAttribute.attribute_code$" stepKey="fillAttributeCodeFilterFieldForSwatchAttribute"/> + <click selector="{{AdminDataGridHeaderSection.applyFilters}}" stepKey="clickApplyFiltersButtonForSwatchAttribute"/> + <click selector="{{AdminDataGridTableSection.rowCheckbox('1')}}" stepKey="selectSwatchAttribute"/> + + <click selector="{{AdminCreateProductConfigurationsPanel.next}}" stepKey="clickNextToSelectOptions"/> + <click selector="{{AdminCreateProductConfigurationsPanel.selectAllByAttribute($createDropdownAttribute.default_frontend_label$)}}" stepKey="selectAllDropdownAttributeOptions"/> + <click selector="{{AdminCreateProductConfigurationsPanel.selectAllByAttribute($createVisualSwatchAttribute.frontend_label[0]$)}}" stepKey="selectAllSwatchAttributeOptions"/> + <click selector="{{AdminCreateProductConfigurationsPanel.next}}" stepKey="clickNextToApplyQuantity"/> + <click selector="{{AdminCreateProductConfigurationsPanel.applySingleQuantityToEachSkus}}" stepKey="clickOnApplySingleQuantityToEachSku"/> + <fillField selector="{{AdminCreateProductConfigurationsPanel.quantity}}" userInput="100" stepKey="enterAttributeQuantity"/> + <click selector="{{AdminCreateProductConfigurationsPanel.next}}" stepKey="clickOnNextToProceedToSummary"/> + <click selector="{{AdminCreateProductConfigurationsPanel.next}}" stepKey="clickGenerateProductsButton"/> + + <!-- Load media for configurable product --> + <actionGroup ref="AddProductImageActionGroup" stepKey="addFirstImageToConfigurableProduct"> + <argument name="image" value="Magento2"/> + </actionGroup> + <actionGroup ref="AddProductImageActionGroup" stepKey="addSecondImageToConfigurableProduct"> + <argument name="image" value="Magento3"/> + </actionGroup> + <actionGroup ref="AdminAddProductVideoWithPreviewActionGroup" stepKey="addVideoToConfigurableProduct"> + <argument name="video" value="VimeoProductVideo"/> + <argument name="image" value="{{TestImageNew.file}}"/> + </actionGroup> + <actionGroup ref="AssertProductVideoAdminProductPageActionGroup" stepKey="assertVideoAddedToConfigurableProduct"> + <argument name="video" value="VimeoProductVideo"/> + </actionGroup> + <actionGroup ref="SaveConfigurableProductAddToCurrentAttributeSetActionGroup" stepKey="saveConfigurableProduct"/> + + <!-- Load media for configurable product variation option1-option1--> + <actionGroup ref="FilterAndSelectProductActionGroup" stepKey="openConfigurableProductVariationOption1Option1"> + <argument name="productSku" value="$createConfigurableProduct.sku$-$dropdownAttributeFirstOption.option[store_labels][0][label]$-$swatchAttributeFirstOption.option[store_labels][0][label]$"/> + </actionGroup> + <actionGroup ref="AddProductImageActionGroup" stepKey="addFirstImageToConfigurableProductVariationOption1Option1"> + <argument name="image" value="MagentoLogo"/> + </actionGroup> + <actionGroup ref="AddProductImageActionGroup" stepKey="addSecondImageToConfigurableProductVariationOption1Option1"> + <argument name="image" value="TestImageNew"/> + </actionGroup> + <actionGroup ref="AdminAddProductVideoWithPreviewActionGroup" stepKey="addVideoToConfigurableProductVariationOption1Option1"> + <argument name="video" value="VimeoProductVideo"/> + <argument name="image" value="{{placeholderSmallImage.file}}"/> + </actionGroup> + <actionGroup ref="AssertProductVideoAdminProductPageActionGroup" stepKey="assertVideoAddedToConfigurableProductVariationOption1Option1"> + <argument name="video" value="VimeoProductVideo"/> + </actionGroup> + <actionGroup ref="SaveProductFormActionGroup" stepKey="saveConfigurableProductVariationOption1Option1"/> + + <!-- Load media for configurable product variation option1-option2--> + <actionGroup ref="FilterAndSelectProductActionGroup" stepKey="openConfigurableProductVariationOption1Option2"> + <argument name="productSku" value="$createConfigurableProduct.sku$-$dropdownAttributeFirstOption.option[store_labels][0][label]$-$swatchAttributeSecondOption.option[store_labels][0][label]$"/> + </actionGroup> + <actionGroup ref="AdminAddProductVideoWithPreviewActionGroup" stepKey="addFirstVideoToConfigurableProductVariationOption1Option2"> + <argument name="video" value="VimeoProductVideo"/> + <argument name="image" value="{{Magento3.file}}"/> + </actionGroup> + <actionGroup ref="AssertProductVideoAdminProductPageActionGroup" stepKey="assertFirstVideoAddedToConfigurableProductVariationOption1Option2"> + <argument name="video" value="VimeoProductVideo"/> + </actionGroup> + <actionGroup ref="AddProductImageActionGroup" stepKey="addFirstImageToConfigurableProductVariationOption1Option2"> + <argument name="image" value="MagentoLogo"/> + </actionGroup> + <actionGroup ref="AdminAddProductVideoWithPreviewActionGroup" stepKey="addSecondVideoToConfigurableProductVariationOption1Option2"> + <argument name="video" value="VimeoProductVideo"/> + <argument name="image" value="{{placeholderThumbnailImage.file}}"/> + </actionGroup> + <actionGroup ref="AssertProductVideoAdminProductPageActionGroup" stepKey="assertSecondVideoAddedToConfigurableProductVariationOption1Option2"> + <argument name="video" value="VimeoProductVideo"/> + </actionGroup> + <actionGroup ref="SaveProductFormActionGroup" stepKey="saveConfigurableProductVariationOption1Option2"/> + + <!-- Load media for configurable product variation option2-option2--> + <actionGroup ref="FilterAndSelectProductActionGroup" stepKey="openConfigurableProductVariationOption2Option2"> + <argument name="productSku" value="$createConfigurableProduct.sku$-$dropdownAttributeSecondOption.option[store_labels][0][label]$-$swatchAttributeSecondOption.option[store_labels][0][label]$"/> + </actionGroup> + <actionGroup ref="AddProductImageActionGroup" stepKey="addFirstImageToConfigurableProductVariationOption2Option2"> + <argument name="image" value="ProductImage"/> + </actionGroup> + <actionGroup ref="SaveProductFormActionGroup" stepKey="saveConfigurableProductVariationOption2Option2"/> + + <!-- Reindex invalidated indices after product attribute has been created --> + <magentoCron groups="index" stepKey="reindexInvalidatedIndicesAfterCreateAttributes"/> + </before> + + <after> + <actionGroup ref="DeleteProductUsingProductGridActionGroup" stepKey="deleteConfigurableProductsWithAllVariations"> + <argument name="product" value="$createConfigurableProduct$"/> + </actionGroup> + <waitForElementVisible selector="{{AdminMessagesSection.success}}" stepKey="waitForDeleteSuccessMessage"/> + <see selector="{{AdminMessagesSection.success}}" userInput="A total of 5 record(s) have been deleted." stepKey="seeDeleteSuccessMessage"/> + <actionGroup ref="ClearFiltersAdminDataGridActionGroup" stepKey="clearProductGridFilters"/> + <actionGroup ref="DeleteProductAttributeActionGroup" stepKey="deleteProductAttributeB"> + <argument name="ProductAttribute" value="$createDropdownAttribute$"/> + </actionGroup> + <actionGroup ref="DeleteProductAttributeActionGroup" stepKey="deleteProductAttributeF"> + <argument name="ProductAttribute" value="$createVisualSwatchAttribute$"/> + </actionGroup> + <actionGroup ref="ClearFiltersAdminDataGridActionGroup" stepKey="clearProductAttributeGridFilters"/> + <actionGroup ref="AdminLogoutActionGroup" stepKey="logoutFromAdmin"/> + <!-- Reindex invalidated indices after product attribute has been created --> + <magentoCron groups="index" stepKey="reindexInvalidatedIndicesAfterDeleteAttributes"/> + </after> + + <actionGroup ref="StorefrontOpenProductPageActionGroup" stepKey="openConfigurableProductPage"> + <argument name="productUrl" value="$createConfigurableProduct.custom_attributes[url_key]$"/> + </actionGroup> + + <!--CASE 0: Selected options = none; Expected media : C1, C2, C3--> + <waitForElementVisible selector="{{StorefrontProductMediaSection.fotoramaAnyMedia}}" stepKey="waitForThumbnailsAppearCase0"/> + <grabMultiple userInput="src" selector="{{StorefrontProductMediaSection.fotoramaAnyMedia}}" stepKey="getListOfThumbnailsCase0"/> + <assertRegExp stepKey="checkPositionInThumbnailForImage1Case0"> + <actualResult type="variable">getListOfThumbnailsCase0[0]</actualResult> + <expectedResult type="string">|{{Magento2.filename}}.*.jpg|</expectedResult> + </assertRegExp> + <assertRegExp stepKey="checkPositionInThumbnailForImage2Case0"> + <actualResult type="variable">getListOfThumbnailsCase0[1]</actualResult> + <expectedResult type="string">|{{Magento3.filename}}.*.jpg|</expectedResult> + </assertRegExp> + <assertRegExp stepKey="checkPositionInThumbnailForImage3Case0"> + <actualResult type="variable">getListOfThumbnailsCase0[2]</actualResult> + <expectedResult type="string">|{{TestImageNew.filename}}.*.jpg|</expectedResult> + </assertRegExp> + <actionGroup ref="StorefrontProductPageOpenImageFullscreenActionGroup" stepKey="openFullScreenPageCase0"/> + <grabMultiple userInput="src" selector="{{StorefrontProductMediaSection.fotoramaAnyMedia}}" stepKey="getListOfThumbnailsFullScreenPageCase0"/> + <assertEquals stepKey="checkPositionInThumbnailForImagesFromFullScreenPageCase0"> + <expectedResult type="variable">getListOfThumbnailsCase0</expectedResult> + <actualResult type="variable">getListOfThumbnailsFullScreenPageCase0</actualResult> + </assertEquals> + <actionGroup ref="StorefrontProductPageCloseFullscreenGalleryActionGroup" stepKey="closeFullScreenPageCase0"/> + + <!--CASE 1: Selected options = F2; Expected media : E1, E2, E3, C1, C2, C3--> + <click selector="{{StorefrontProductInfoMainSection.swatchOptionByLabel($swatchAttributeSecondOption.option[store_labels][0][label]$)}}" stepKey="chooseOptionF2Case1"/> + <waitForElementVisible selector="{{StorefrontProductMediaSection.fotoramaAnyMedia}}" stepKey="waitForThumbnailsAppearCase1"/> + <grabMultiple userInput="src" selector="{{StorefrontProductMediaSection.fotoramaAnyMedia}}" stepKey="getListOfThumbnailsCase1"/> + <assertRegExp stepKey="checkPositionInThumbnailForImage1Case1"> + <actualResult type="variable">getListOfThumbnailsCase1[0]</actualResult> + <expectedResult type="string">|{{Magento3.filename}}.*.jpg|</expectedResult> + </assertRegExp> + <assertRegExp stepKey="checkPositionInThumbnailForImage2Case1"> + <actualResult type="variable">getListOfThumbnailsCase1[1]</actualResult> + <expectedResult type="string">|{{MagentoLogo.filename}}.*.png|</expectedResult> + </assertRegExp> + <assertRegExp stepKey="checkPositionInThumbnailForImage3Case1"> + <actualResult type="variable">getListOfThumbnailsCase1[2]</actualResult> + <expectedResult type="string">|{{placeholderThumbnailImage.name}}.*.jpg|</expectedResult> + </assertRegExp> + <assertRegExp stepKey="checkPositionInThumbnailForImage4Case1"> + <actualResult type="variable">getListOfThumbnailsCase1[3]</actualResult> + <expectedResult type="string">|{{Magento2.filename}}.*.jpg|</expectedResult> + </assertRegExp> + <assertRegExp stepKey="checkPositionInThumbnailForImage5Case1"> + <actualResult type="variable">getListOfThumbnailsCase1[4]</actualResult> + <expectedResult type="string">|{{Magento3.filename}}.*.jpg|</expectedResult> + </assertRegExp> + <assertRegExp stepKey="checkPositionInThumbnailForImage6Case1"> + <actualResult type="variable">getListOfThumbnailsCase1[5]</actualResult> + <expectedResult type="string">|{{TestImageNew.filename}}.*.jpg|</expectedResult> + </assertRegExp> + <actionGroup ref="StorefrontProductPageOpenImageFullscreenActionGroup" stepKey="openFullScreenPageCase1"/> + <grabMultiple userInput="src" selector="{{StorefrontProductMediaSection.fotoramaAnyMedia}}" stepKey="getListOfThumbnailsFullScreenPageCase1"/> + <assertEquals stepKey="checkPositionInThumbnailForImagesFromFullScreenPageCase1"> + <expectedResult type="variable">getListOfThumbnailsCase1</expectedResult> + <actualResult type="variable">getListOfThumbnailsFullScreenPageCase1</actualResult> + </assertEquals> + <actionGroup ref="StorefrontProductPageCloseFullscreenGalleryActionGroup" stepKey="closeFullScreenPageCase1"/> + + <!--CASE 2: Selected options = F1; Expected media : D1, D2, D3, C1, C2, C3--> + <click selector="{{StorefrontProductInfoMainSection.swatchOptionByLabel($swatchAttributeFirstOption.option[store_labels][0][label]$)}}" stepKey="chooseOptionF1Case2"/> + <waitForElementVisible selector="{{StorefrontProductMediaSection.fotoramaAnyMedia}}" stepKey="waitForThumbnailsAppearCase2"/> + <grabMultiple userInput="src" selector="{{StorefrontProductMediaSection.fotoramaAnyMedia}}" stepKey="getListOfThumbnailsCase2"/> + <assertRegExp stepKey="checkPositionInThumbnailForImage1Case2"> + <actualResult type="variable">getListOfThumbnailsCase2[0]</actualResult> + <expectedResult type="string">|{{MagentoLogo.filename}}.*.png|</expectedResult> + </assertRegExp> + <assertRegExp stepKey="checkPositionInThumbnailForImage2Case2"> + <actualResult type="variable">getListOfThumbnailsCase2[1]</actualResult> + <expectedResult type="string">|{{TestImageNew.filename}}.*.jpg|</expectedResult> + </assertRegExp> + <assertRegExp stepKey="checkPositionInThumbnailForImage3Case2"> + <actualResult type="variable">getListOfThumbnailsCase2[2]</actualResult> + <expectedResult type="string">|{{placeholderSmallImage.name}}.*.jpg|</expectedResult> + </assertRegExp> + <assertRegExp stepKey="checkPositionInThumbnailForImage4Case2"> + <actualResult type="variable">getListOfThumbnailsCase2[3]</actualResult> + <expectedResult type="string">|{{Magento2.filename}}.*.jpg|</expectedResult> + </assertRegExp> + <assertRegExp stepKey="checkPositionInThumbnailForImage5Case2"> + <actualResult type="variable">getListOfThumbnailsCase2[4]</actualResult> + <expectedResult type="string">|{{Magento3.filename}}.*.jpg|</expectedResult> + </assertRegExp> + <assertRegExp stepKey="checkPositionInThumbnailForImage6Case2"> + <actualResult type="variable">getListOfThumbnailsCase2[5]</actualResult> + <expectedResult type="string">|{{TestImageNew.filename}}.*.jpg|</expectedResult> + </assertRegExp> + <actionGroup ref="StorefrontProductPageOpenImageFullscreenActionGroup" stepKey="openFullScreenPageCase2"/> + <grabMultiple userInput="src" selector="{{StorefrontProductMediaSection.fotoramaAnyMedia}}" stepKey="getListOfThumbnailsFullScreenPageCase2"/> + <assertEquals stepKey="checkPositionInThumbnailForImagesFromFullScreenPageCase2"> + <expectedResult type="variable">getListOfThumbnailsCase2</expectedResult> + <actualResult type="variable">getListOfThumbnailsFullScreenPageCase2</actualResult> + </assertEquals> + <actionGroup ref="StorefrontProductPageCloseFullscreenGalleryActionGroup" stepKey="closeFullScreenPageCase2"/> + + <!--CASE 3: Selected options = B2,F1; Expected media : C1, C2, C3--> + <selectOption userInput="$dropdownAttributeSecondOption.option[store_labels][0][label]$" selector="{{StorefrontProductInfoMainSection.attributeSelectByAttributeID($createDropdownAttribute.default_frontend_label$)}}" stepKey="chooseOptionB2Case3"/> + <waitForElementVisible selector="{{StorefrontProductMediaSection.fotoramaAnyMedia}}" stepKey="waitForThumbnailsAppearCase3"/> + <grabMultiple userInput="src" selector="{{StorefrontProductMediaSection.fotoramaAnyMedia}}" stepKey="getListOfThumbnailsCase3"/> + <assertRegExp stepKey="checkPositionInThumbnailForImage1Case3"> + <actualResult type="variable">getListOfThumbnailsCase3[0]</actualResult> + <expectedResult type="string">|{{Magento2.filename}}.*.jpg|</expectedResult> + </assertRegExp> + <assertRegExp stepKey="checkPositionInThumbnailForImage2Case3"> + <actualResult type="variable">getListOfThumbnailsCase3[1]</actualResult> + <expectedResult type="string">|{{Magento3.filename}}.*.jpg|</expectedResult> + </assertRegExp> + <assertRegExp stepKey="checkPositionInThumbnailForImage3Case3"> + <actualResult type="variable">getListOfThumbnailsCase3[2]</actualResult> + <expectedResult type="string">|{{TestImageNew.filename}}.*.jpg|</expectedResult> + </assertRegExp> + <actionGroup ref="StorefrontProductPageOpenImageFullscreenActionGroup" stepKey="openFullScreenPageCase3"/> + <grabMultiple userInput="src" selector="{{StorefrontProductMediaSection.fotoramaAnyMedia}}" stepKey="getListOfThumbnailsFullScreenPageCase3"/> + <assertEquals stepKey="checkPositionInThumbnailForImagesFromFullScreenPageCase3"> + <expectedResult type="variable">getListOfThumbnailsCase3</expectedResult> + <actualResult type="variable">getListOfThumbnailsFullScreenPageCase3</actualResult> + </assertEquals> + <actionGroup ref="StorefrontProductPageCloseFullscreenGalleryActionGroup" stepKey="closeFullScreenPageCase3"/> + + <!--CASE 4: Selected options = B2,F2, Expected media : G1, C1, C2, C3--> + <click selector="{{StorefrontProductInfoMainSection.swatchOptionByLabel($swatchAttributeSecondOption.option[store_labels][0][label]$)}}" stepKey="chooseOptionF2Case4"/> + <waitForElementVisible selector="{{StorefrontProductMediaSection.fotoramaAnyMedia}}" stepKey="waitForThumbnailsAppearCase4"/> + <grabMultiple userInput="src" selector="{{StorefrontProductMediaSection.fotoramaAnyMedia}}" stepKey="getListOfThumbnailsCase4"/> + <assertRegExp stepKey="checkPositionInThumbnailForImage1Case4"> + <actualResult type="variable">getListOfThumbnailsCase4[0]</actualResult> + <expectedResult type="string">|{{ProductImage.filename}}.*.png|</expectedResult> + </assertRegExp> + <assertRegExp stepKey="checkPositionInThumbnailForImage2Case4"> + <actualResult type="variable">getListOfThumbnailsCase4[1]</actualResult> + <expectedResult type="string">|{{Magento2.filename}}.*.jpg|</expectedResult> + </assertRegExp> + <assertRegExp stepKey="checkPositionInThumbnailForImage3Case4"> + <actualResult type="variable">getListOfThumbnailsCase4[2]</actualResult> + <expectedResult type="string">|{{Magento3.filename}}.*.jpg|</expectedResult> + </assertRegExp> + <assertRegExp stepKey="checkPositionInThumbnailForImage4Case4"> + <actualResult type="variable">getListOfThumbnailsCase4[3]</actualResult> + <expectedResult type="string">|{{TestImageNew.filename}}.*.jpg|</expectedResult> + </assertRegExp> + <actionGroup ref="StorefrontProductPageOpenImageFullscreenActionGroup" stepKey="openFullScreenPageCase4"/> + <grabMultiple userInput="src" selector="{{StorefrontProductMediaSection.fotoramaAnyMedia}}" stepKey="getListOfThumbnailsFullScreenPageCase4"/> + <assertEquals stepKey="checkPositionInThumbnailForImagesFromFullScreenPageCase4"> + <expectedResult type="variable">getListOfThumbnailsCase4</expectedResult> + <actualResult type="variable">getListOfThumbnailsFullScreenPageCase4</actualResult> + </assertEquals> + <actionGroup ref="StorefrontProductPageCloseFullscreenGalleryActionGroup" stepKey="closeFullScreenPageCase4"/> + + <!--CASE 5: Selected options = B2, Expected media : C1, C2, C3--> + <conditionalClick selector="{{StorefrontProductInfoMainSection.swatchAttributeSelectedOption}}" dependentSelector="{{StorefrontProductInfoMainSection.swatchAttributeSelectedOption}}" visible="true" stepKey="unchooseF2Case5"/> + <waitForElementVisible selector="{{StorefrontProductMediaSection.fotoramaAnyMedia}}" stepKey="waitForThumbnailsAppearCase5"/> + <grabMultiple userInput="src" selector="{{StorefrontProductMediaSection.fotoramaAnyMedia}}" stepKey="getListOfThumbnailsCase5"/> + <assertRegExp stepKey="checkPositionInThumbnailForImage1Case5"> + <actualResult type="variable">getListOfThumbnailsCase5[0]</actualResult> + <expectedResult type="string">|{{Magento2.filename}}.*.jpg|</expectedResult> + </assertRegExp> + <assertRegExp stepKey="checkPositionInThumbnailForImage2Case5"> + <actualResult type="variable">getListOfThumbnailsCase5[1]</actualResult> + <expectedResult type="string">|{{Magento3.filename}}.*.jpg|</expectedResult> + </assertRegExp> + <assertRegExp stepKey="checkPositionInThumbnailForImage3Case5"> + <actualResult type="variable">getListOfThumbnailsCase5[2]</actualResult> + <expectedResult type="string">|{{TestImageNew.filename}}.*.jpg|</expectedResult> + </assertRegExp> + <actionGroup ref="StorefrontProductPageOpenImageFullscreenActionGroup" stepKey="openFullScreenPageCase5"/> + <grabMultiple userInput="src" selector="{{StorefrontProductMediaSection.fotoramaAnyMedia}}" stepKey="getListOfThumbnailsFullScreenPageCase5"/> + <assertEquals stepKey="checkPositionInThumbnailForImagesFromFullScreenPageCase5"> + <expectedResult type="variable">getListOfThumbnailsCase5</expectedResult> + <actualResult type="variable">getListOfThumbnailsFullScreenPageCase5</actualResult> + </assertEquals> + <actionGroup ref="StorefrontProductPageCloseFullscreenGalleryActionGroup" stepKey="closeFullScreenPageCase5"/> + + <!--CASE 6: Selected options = B1, Expected media : D1, D2, D3, C1, C2, C3--> + <selectOption userInput="$dropdownAttributeFirstOption.option[store_labels][0][label]$" selector="{{StorefrontProductInfoMainSection.attributeSelectByAttributeID($createDropdownAttribute.default_frontend_label$)}}" stepKey="chooseOptionB1Case6"/> + <waitForElementVisible selector="{{StorefrontProductMediaSection.fotoramaAnyMedia}}" stepKey="waitForThumbnailsAppearCase6"/> + <grabMultiple userInput="src" selector="{{StorefrontProductMediaSection.fotoramaAnyMedia}}" stepKey="getListOfThumbnailsCase6"/> + <assertRegExp stepKey="checkPositionInThumbnailForImage1Case6"> + <actualResult type="variable">getListOfThumbnailsCase6[0]</actualResult> + <expectedResult type="string">|{{MagentoLogo.filename}}.*.png|</expectedResult> + </assertRegExp> + <assertRegExp stepKey="checkPositionInThumbnailForImage2Case6"> + <actualResult type="variable">getListOfThumbnailsCase6[1]</actualResult> + <expectedResult type="string">|{{TestImageNew.filename}}.*.jpg|</expectedResult> + </assertRegExp> + <assertRegExp stepKey="checkPositionInThumbnailForImage3Case6"> + <actualResult type="variable">getListOfThumbnailsCase6[2]</actualResult> + <expectedResult type="string">|{{placeholderSmallImage.name}}.*.jpg|</expectedResult> + </assertRegExp> + <assertRegExp stepKey="checkPositionInThumbnailForImage4Case6"> + <actualResult type="variable">getListOfThumbnailsCase6[3]</actualResult> + <expectedResult type="string">|{{Magento2.filename}}.*.jpg|</expectedResult> + </assertRegExp> + <assertRegExp stepKey="checkPositionInThumbnailForImage5Case6"> + <actualResult type="variable">getListOfThumbnailsCase6[4]</actualResult> + <expectedResult type="string">|{{Magento3.filename}}.*.jpg|</expectedResult> + </assertRegExp> + <assertRegExp stepKey="checkPositionInThumbnailForImage6Case6"> + <actualResult type="variable">getListOfThumbnailsCase6[5]</actualResult> + <expectedResult type="string">|{{TestImageNew.filename}}.*.jpg|</expectedResult> + </assertRegExp> + <actionGroup ref="StorefrontProductPageOpenImageFullscreenActionGroup" stepKey="openFullScreenPageCase6"/> + <grabMultiple userInput="src" selector="{{StorefrontProductMediaSection.fotoramaAnyMedia}}" stepKey="getListOfThumbnailsFullScreenPageCase6"/> + <assertEquals stepKey="checkPositionInThumbnailForImagesFromFullScreenPageCase6"> + <expectedResult type="variable">getListOfThumbnailsCase6</expectedResult> + <actualResult type="variable">getListOfThumbnailsFullScreenPageCase6</actualResult> + </assertEquals> + <actionGroup ref="StorefrontProductPageCloseFullscreenGalleryActionGroup" stepKey="closeFullScreenPageCase6"/> + + <!--CASE 7: Selected options = B1,F2, Expected media : E1, E2, E3, C1, C2, C3--> + <click selector="{{StorefrontProductInfoMainSection.swatchOptionByLabel($swatchAttributeSecondOption.option[store_labels][0][label]$)}}" stepKey="chooseOptionF2Case7"/> + <waitForElementVisible selector="{{StorefrontProductMediaSection.fotoramaAnyMedia}}" stepKey="waitForThumbnailsAppearCase7"/> + <grabMultiple userInput="src" selector="{{StorefrontProductMediaSection.fotoramaAnyMedia}}" stepKey="getListOfThumbnailsCase7"/> + <assertRegExp stepKey="checkPositionInThumbnailForImage1Case7"> + <actualResult type="variable">getListOfThumbnailsCase7[0]</actualResult> + <expectedResult type="string">|{{Magento3.filename}}.*.jpg|</expectedResult> + </assertRegExp> + <assertRegExp stepKey="checkPositionInThumbnailForImage2Case7"> + <actualResult type="variable">getListOfThumbnailsCase7[1]</actualResult> + <expectedResult type="string">|{{MagentoLogo.filename}}.*.png|</expectedResult> + </assertRegExp> + <assertRegExp stepKey="checkPositionInThumbnailForImage3Case7"> + <actualResult type="variable">getListOfThumbnailsCase7[2]</actualResult> + <expectedResult type="string">|{{placeholderThumbnailImage.name}}.*.jpg|</expectedResult> + </assertRegExp> + <assertRegExp stepKey="checkPositionInThumbnailForImage4Case7"> + <actualResult type="variable">getListOfThumbnailsCase7[3]</actualResult> + <expectedResult type="string">|{{Magento2.filename}}.*.jpg|</expectedResult> + </assertRegExp> + <assertRegExp stepKey="checkPositionInThumbnailForImage5Case7"> + <actualResult type="variable">getListOfThumbnailsCase7[4]</actualResult> + <expectedResult type="string">|{{Magento3.filename}}.*.jpg|</expectedResult> + </assertRegExp> + <assertRegExp stepKey="checkPositionInThumbnailForImage6Case7"> + <actualResult type="variable">getListOfThumbnailsCase7[5]</actualResult> + <expectedResult type="string">|{{TestImageNew.filename}}.*.jpg|</expectedResult> + </assertRegExp> + <actionGroup ref="StorefrontProductPageOpenImageFullscreenActionGroup" stepKey="openFullScreenPageCase7"/> + <grabMultiple userInput="src" selector="{{StorefrontProductMediaSection.fotoramaAnyMedia}}" stepKey="getListOfThumbnailsFullScreenPageCase7"/> + <assertEquals stepKey="checkPositionInThumbnailForImagesFromFullScreenPageCase7"> + <expectedResult type="variable">getListOfThumbnailsCase7</expectedResult> + <actualResult type="variable">getListOfThumbnailsFullScreenPageCase7</actualResult> + </assertEquals> + <actionGroup ref="StorefrontProductPageCloseFullscreenGalleryActionGroup" stepKey="closeFullScreenPageCase7"/> + + <!--CASE 8: Selected options = B1,F1, Expected media : D1, D2, D3, C1, C2, C3--> + <click selector="{{StorefrontProductInfoMainSection.swatchOptionByLabel($swatchAttributeFirstOption.option[store_labels][0][label]$)}}" stepKey="chooseOptionF1Case8"/> + <waitForElementVisible selector="{{StorefrontProductMediaSection.fotoramaAnyMedia}}" stepKey="waitForThumbnailsAppearCase8"/> + <grabMultiple userInput="src" selector="{{StorefrontProductMediaSection.fotoramaAnyMedia}}" stepKey="getListOfThumbnailsCase8"/> + <assertRegExp stepKey="checkPositionInThumbnailForImage1Case8"> + <actualResult type="variable">getListOfThumbnailsCase8[0]</actualResult> + <expectedResult type="string">|{{MagentoLogo.filename}}.*.png|</expectedResult> + </assertRegExp> + <assertRegExp stepKey="checkPositionInThumbnailForImage2Case8"> + <actualResult type="variable">getListOfThumbnailsCase8[1]</actualResult> + <expectedResult type="string">|{{TestImageNew.filename}}.*.jpg|</expectedResult> + </assertRegExp> + <assertRegExp stepKey="checkPositionInThumbnailForImage3Case8"> + <actualResult type="variable">getListOfThumbnailsCase8[2]</actualResult> + <expectedResult type="string">|{{placeholderSmallImage.name}}.*.jpg|</expectedResult> + </assertRegExp> + <assertRegExp stepKey="checkPositionInThumbnailForImage4Case8"> + <actualResult type="variable">getListOfThumbnailsCase8[3]</actualResult> + <expectedResult type="string">|{{Magento2.filename}}.*.jpg|</expectedResult> + </assertRegExp> + <assertRegExp stepKey="checkPositionInThumbnailForImage5Case8"> + <actualResult type="variable">getListOfThumbnailsCase8[4]</actualResult> + <expectedResult type="string">|{{Magento3.filename}}.*.jpg|</expectedResult> + </assertRegExp> + <assertRegExp stepKey="checkPositionInThumbnailForImage6Case8"> + <actualResult type="variable">getListOfThumbnailsCase8[5]</actualResult> + <expectedResult type="string">|{{TestImageNew.filename}}.*.jpg|</expectedResult> + </assertRegExp> + <actionGroup ref="StorefrontProductPageOpenImageFullscreenActionGroup" stepKey="openFullScreenPageCase8"/> + <grabMultiple userInput="src" selector="{{StorefrontProductMediaSection.fotoramaAnyMedia}}" stepKey="getListOfThumbnailsFullScreenPageCase8"/> + <assertEquals stepKey="checkPositionInThumbnailForImagesFromFullScreenPageCase8"> + <expectedResult type="variable">getListOfThumbnailsCase8</expectedResult> + <actualResult type="variable">getListOfThumbnailsFullScreenPageCase8</actualResult> + </assertEquals> + <actionGroup ref="StorefrontProductPageCloseFullscreenGalleryActionGroup" stepKey="closeFullScreenPageCase8"/> + + <!--CASE 9: Selected options = none, Expected media : C1, C2, C3--> + <selectOption userInput="Choose an Option..." selector="{{StorefrontProductInfoMainSection.attributeSelectByAttributeID($createDropdownAttribute.default_frontend_label$)}}" stepKey="unselectB1Case9"/> + <conditionalClick selector="{{StorefrontProductInfoMainSection.swatchAttributeSelectedOption}}" dependentSelector="{{StorefrontProductInfoMainSection.swatchAttributeSelectedOption}}" visible="true" stepKey="unchooseF1Case9"/> + <waitForElementVisible selector="{{StorefrontProductMediaSection.fotoramaAnyMedia}}" stepKey="waitForThumbnailsAppearCase9"/> + <grabMultiple userInput="src" selector="{{StorefrontProductMediaSection.fotoramaAnyMedia}}" stepKey="getListOfThumbnailsCase9"/> + <assertRegExp stepKey="checkPositionInThumbnailForImage1Case9"> + <actualResult type="variable">getListOfThumbnailsCase9[0]</actualResult> + <expectedResult type="string">|{{Magento2.filename}}.*.jpg|</expectedResult> + </assertRegExp> + <assertRegExp stepKey="checkPositionInThumbnailForImage2Case9"> + <actualResult type="variable">getListOfThumbnailsCase9[1]</actualResult> + <expectedResult type="string">|{{Magento3.filename}}.*.jpg|</expectedResult> + </assertRegExp> + <assertRegExp stepKey="checkPositionInThumbnailForImage3Case9"> + <actualResult type="variable">getListOfThumbnailsCase9[2]</actualResult> + <expectedResult type="string">|{{TestImageNew.filename}}.*.jpg|</expectedResult> + </assertRegExp> + <actionGroup ref="StorefrontProductPageOpenImageFullscreenActionGroup" stepKey="openFullScreenPageCase9"/> + <grabMultiple userInput="src" selector="{{StorefrontProductMediaSection.fotoramaAnyMedia}}" stepKey="getListOfThumbnailsFullScreenPageCase9"/> + <assertEquals stepKey="checkPositionInThumbnailForImagesFromFullScreenPageCase9"> + <expectedResult type="variable">getListOfThumbnailsCase9</expectedResult> + <actualResult type="variable">getListOfThumbnailsFullScreenPageCase9</actualResult> + </assertEquals> + <actionGroup ref="StorefrontProductPageCloseFullscreenGalleryActionGroup" stepKey="closeFullScreenPageCase9"/> + </test> +</tests> diff --git a/app/code/Magento/ConfigurableProduct/Test/Mftf/Test/StorefrontGalleryConfigurableProductWithVisualSwatchAttributePrependMediaTest.xml b/app/code/Magento/ConfigurableProduct/Test/Mftf/Test/StorefrontGalleryConfigurableProductWithVisualSwatchAttributePrependMediaTest.xml new file mode 100644 index 0000000000000..c76d49a76d947 --- /dev/null +++ b/app/code/Magento/ConfigurableProduct/Test/Mftf/Test/StorefrontGalleryConfigurableProductWithVisualSwatchAttributePrependMediaTest.xml @@ -0,0 +1,290 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + /** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +--> + +<tests xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/testSchema.xsd"> + <test name="StorefrontGalleryConfigurableProductWithVisualSwatchAttributePrependMediaTest"> + <annotations> + <features value="ConfigurableProduct"/> + <stories value="Prepend variation media on storefront"/> + <title value="Storefront Gallery - Configurable Product with Visual Swatch: prepend variation media"/> + <description value="Verify functionality of updating Gallery items on 'view Product' Storefront page for Configurable Product with Visual Swatch input type attribute. Verify that Configurable Product media in Gallery is prepended with media from selected variation"/> + <severity value="CRITICAL"/> + <testCaseId value="MC-26184"/> + <group value="catalog"/> + <group value="configurableProduct"/> + <group value="swatch"/> + </annotations> + + <before> + <!--Create 1 configurable product --> + <createData entity="ApiConfigurableProductWithDescription" stepKey="createConfigurableProduct"/> + <!-- Create product swatch attribute with 2 variations --> + <createData entity="VisualSwatchProductAttributeForm" stepKey="createVisualSwatchAttribute"/> + <createData entity="SwatchProductAttributeOption1" stepKey="swatchAttributeFirstOption"> + <requiredEntity createDataKey="createVisualSwatchAttribute"/> + </createData> + <createData entity="SwatchProductAttributeOption2" stepKey="swatchAttributeSecondOption"> + <requiredEntity createDataKey="createVisualSwatchAttribute"/> + </createData> + <createData entity="SwatchProductAttributeOption3" stepKey="swatchAttributeThirdOption"> + <requiredEntity createDataKey="createVisualSwatchAttribute"/> + </createData> + + <actionGroup ref="AdminLoginActionGroup" stepKey="loginAsAdmin"/> + + <!-- Open configurable product edit page --> + <amOnPage url="{{AdminProductEditPage.url($createConfigurableProduct.id$)}}" stepKey="goToProductIndex"/> + + <!-- Add attribute to configurable product--> + <conditionalClick selector="{{AdminProductFormConfigurationsSection.sectionHeader}}" dependentSelector="{{AdminProductFormConfigurationsSection.createConfigurations}}" visible="false" stepKey="openConfigurationSection"/> + <actionGroup ref="GenerateConfigurationsByAttributeCodeActionGroup" stepKey="createProductConfigurations"> + <argument name="attributeCode" value="$createVisualSwatchAttribute.attribute_code$"/> + </actionGroup> + + <!-- Load media for configurable product --> + <actionGroup ref="AddProductImageActionGroup" stepKey="addFirstImageToConfigurableProduct"> + <argument name="image" value="Magento2"/> + </actionGroup> + <actionGroup ref="AddProductImageActionGroup" stepKey="addSecondImageToConfigurableProduct"> + <argument name="image" value="Magento3"/> + </actionGroup> + <actionGroup ref="AdminAddProductVideoWithPreviewActionGroup" stepKey="addVideoToConfigurableProduct"> + <argument name="video" value="VimeoProductVideo"/> + <argument name="image" value="{{TestImageNew.file}}"/> + </actionGroup> + <actionGroup ref="AssertProductVideoAdminProductPageActionGroup" stepKey="assertVideoAddedToConfigurableProduct"> + <argument name="video" value="VimeoProductVideo"/> + </actionGroup> + <actionGroup ref="SaveConfigurableProductAddToCurrentAttributeSetActionGroup" stepKey="saveConfigurableProduct"/> + + <!-- Load media for configurable product variation option1 --> + <actionGroup ref="FilterAndSelectProductActionGroup" stepKey="openConfigurableProductVariationOption1"> + <argument name="productSku" value="$createConfigurableProduct.sku$-$swatchAttributeFirstOption.option[store_labels][0][label]$"/> + </actionGroup> + <actionGroup ref="AddProductImageActionGroup" stepKey="addFirstImageToConfigurableProductVariationOption1"> + <argument name="image" value="MagentoLogo"/> + </actionGroup> + <actionGroup ref="AddProductImageActionGroup" stepKey="addSecondImageToConfigurableProductVariationOption1"> + <argument name="image" value="TestImageNew"/> + </actionGroup> + <actionGroup ref="AdminAddProductVideoWithPreviewActionGroup" stepKey="addVideoToConfigurableProductVariationOption1"> + <argument name="video" value="VimeoProductVideo"/> + <argument name="image" value="{{placeholderSmallImage.file}}"/> + </actionGroup> + <actionGroup ref="AssertProductVideoAdminProductPageActionGroup" stepKey="assertVideoAddedToConfigurableProductVariationOption1"> + <argument name="video" value="VimeoProductVideo"/> + </actionGroup> + <actionGroup ref="SaveProductFormActionGroup" stepKey="saveConfigurableProductVariationOption1"/> + + <!-- Load media for configurable product variation option3 --> + <actionGroup ref="FilterAndSelectProductActionGroup" stepKey="openConfigurableProductVariationOption3"> + <argument name="productSku" value="$createConfigurableProduct.sku$-$swatchAttributeThirdOption.option[store_labels][0][label]$"/> + </actionGroup> + <actionGroup ref="AdminAddProductVideoWithPreviewActionGroup" stepKey="addVideoToConfigurableProductVariationOption3"> + <argument name="video" value="VimeoProductVideo"/> + <argument name="image" value="{{Magento3.file}}"/> + </actionGroup> + <actionGroup ref="AssertProductVideoAdminProductPageActionGroup" stepKey="assertVideoAddedToConfigurableProductVariationOption3"> + <argument name="video" value="VimeoProductVideo"/> + </actionGroup> + <actionGroup ref="AddProductImageActionGroup" stepKey="addSecondImageToConfigurableProductVariationOption3"> + <argument name="image" value="TestImageAdobe"/> + </actionGroup> + <actionGroup ref="AdminAddProductVideoWithPreviewActionGroup" stepKey="addSecondVideoToConfigurableProductVariationOption3"> + <argument name="video" value="VimeoProductVideo"/> + <argument name="image" value="{{MagentoLogo.file}}"/> + </actionGroup> + <actionGroup ref="AssertProductVideoAdminProductPageActionGroup" stepKey="assertSecondVideoAddedToConfigurableProductVariationOption3"> + <argument name="video" value="VimeoProductVideo"/> + </actionGroup> + <actionGroup ref="SaveProductFormActionGroup" stepKey="saveConfigurableProductVariationOption3"/> + + <!-- Reindex invalidated indices after product attribute has been created --> + <magentoCron groups="index" stepKey="reindexInvalidatedIndicesAfterCreateAttributes"/> + </before> + + <after> + <actionGroup ref="DeleteProductUsingProductGridActionGroup" stepKey="deleteConfigurableProductsWithAllVariations"> + <argument name="product" value="$createConfigurableProduct$"/> + </actionGroup> + <waitForElementVisible selector="{{AdminMessagesSection.success}}" stepKey="waitForDeleteSuccessMessage"/> + <see selector="{{AdminMessagesSection.success}}" userInput="A total of 4 record(s) have been deleted." stepKey="seeDeleteSuccessMessage"/> + <actionGroup ref="ClearFiltersAdminDataGridActionGroup" stepKey="clearProductGridFilters"/> + <actionGroup ref="DeleteProductAttributeActionGroup" stepKey="deleteProductVisualSwatchAttribute"> + <argument name="ProductAttribute" value="$createVisualSwatchAttribute$"/> + </actionGroup> + <actionGroup ref="ClearFiltersAdminDataGridActionGroup" stepKey="clearProductAttributeGridFilters"/> + <actionGroup ref="AdminLogoutActionGroup" stepKey="logoutFromAdmin"/> + <!-- Reindex invalidated indices after product attribute has been created --> + <magentoCron groups="index" stepKey="reindexInvalidatedIndicesAfterDeleteAttributes"/> + </after> + + <actionGroup ref="StorefrontOpenProductPageActionGroup" stepKey="openConfigurableProductPage"> + <argument name="productUrl" value="$createConfigurableProduct.custom_attributes[url_key]$"/> + </actionGroup> + + <!--CASE 0: Selected options = none; Expected media : C1, C2, C3--> + <waitForElementVisible selector="{{StorefrontProductMediaSection.fotoramaAnyMedia}}" stepKey="waitForThumbnailsAppearCase0"/> + <grabMultiple userInput="src" selector="{{StorefrontProductMediaSection.fotoramaAnyMedia}}" stepKey="getListOfThumbnailsCase0"/> + <assertRegExp stepKey="checkPositionInThumbnailForImage1Case0"> + <expectedResult type="string">|{{Magento2.filename}}.*.jpg|</expectedResult> + <actualResult type="variable">getListOfThumbnailsCase0[0]</actualResult> + </assertRegExp> + <assertRegExp stepKey="checkPositionInThumbnailForImage2Case0"> + <expectedResult type="string">|{{Magento3.filename}}.*.jpg|</expectedResult> + <actualResult type="variable">getListOfThumbnailsCase0[1]</actualResult> + </assertRegExp> + <assertRegExp stepKey="checkPositionInThumbnailForImage3Case0"> + <expectedResult type="string">|{{TestImageNew.filename}}.*.jpg|</expectedResult> + <actualResult type="variable">getListOfThumbnailsCase0[2]</actualResult> + </assertRegExp> + <actionGroup ref="AssertStorefrontProductImageAppearsOnProductPagePreviewActionGroup" stepKey="seeImageOnPreviewCase0"> + <argument name="productImage" value="{{Magento2.filename}}"/> + </actionGroup> + <actionGroup ref="StorefrontProductPageOpenImageFullscreenActionGroup" stepKey="openFullScreenPageCase0"/> + <grabMultiple userInput="src" selector="{{StorefrontProductMediaSection.fotoramaAnyMedia}}" stepKey="getListOfThumbnailsFullScreenPageCase0"/> + <assertEquals stepKey="checkPositionInThumbnailForImagesFromFullScreenPageCase0"> + <expectedResult type="variable">getListOfThumbnailsCase0</expectedResult> + <actualResult type="variable">getListOfThumbnailsFullScreenPageCase0</actualResult> + </assertEquals> + <actionGroup ref="StorefrontProductPageCloseFullscreenGalleryActionGroup" stepKey="closeFullScreenPageCase0"/> + + <!--CASE 1: Selected options = B1; Expected media : D1, D2, D3, C1, C2, C3--> + <click selector="{{StorefrontProductInfoMainSection.swatchOptionByLabel($swatchAttributeFirstOption.option[store_labels][0][label]$)}}" stepKey="chooseFirstOptionCase1"/> + <waitForElementVisible selector="{{StorefrontProductMediaSection.fotoramaAnyMedia}}" stepKey="waitForThumbnailsAppearCase1"/> + <grabMultiple userInput="src" selector="{{StorefrontProductMediaSection.fotoramaAnyMedia}}" stepKey="getListOfThumbnailsCase1"/> + <assertRegExp stepKey="checkPositionInThumbnailForImage1Case1"> + <expectedResult type="string">|{{MagentoLogo.filename}}.*.png|</expectedResult> + <actualResult type="variable">getListOfThumbnailsCase1[0]</actualResult> + </assertRegExp> + <assertRegExp stepKey="checkPositionInThumbnailForImage2Case1"> + <expectedResult type="string">|{{TestImageNew.filename}}.*.jpg|</expectedResult> + <actualResult type="variable">getListOfThumbnailsCase1[1]</actualResult> + </assertRegExp> + <assertRegExp stepKey="checkPositionInThumbnailForImage3Case1"> + <expectedResult type="string">|{{placeholderSmallImage.name}}.*.jpg|</expectedResult> + <actualResult type="variable">getListOfThumbnailsCase1[2]</actualResult> + </assertRegExp> + <assertRegExp stepKey="checkPositionInThumbnailForImage4Case1"> + <expectedResult type="string">|{{Magento2.filename}}.*.jpg|</expectedResult> + <actualResult type="variable">getListOfThumbnailsCase1[3]</actualResult> + </assertRegExp> + <assertRegExp stepKey="checkPositionInThumbnailForImage5Case1"> + <expectedResult type="string">|{{Magento3.filename}}.*.jpg|</expectedResult> + <actualResult type="variable">getListOfThumbnailsCase1[4]</actualResult> + </assertRegExp> + <assertRegExp stepKey="checkPositionInThumbnailForImage6Case1"> + <expectedResult type="string">|{{TestImageNew.filename}}.*.jpg|</expectedResult> + <actualResult type="variable">getListOfThumbnailsCase1[5]</actualResult> + </assertRegExp> + <actionGroup ref="AssertStorefrontProductImageAppearsOnProductPagePreviewActionGroup" stepKey="seeImageOnPreviewCase1"> + <argument name="productImage" value="{{MagentoLogo.filename}}"/> + </actionGroup> + <actionGroup ref="StorefrontProductPageOpenImageFullscreenActionGroup" stepKey="openFullScreenPageCase1"/> + <grabMultiple userInput="src" selector="{{StorefrontProductMediaSection.fotoramaAnyMedia}}" stepKey="getListOfThumbnailsFullScreenPageCase1"/> + <assertEquals stepKey="checkPositionInThumbnailForImagesFromFullScreenPageCase1"> + <expectedResult type="variable">getListOfThumbnailsCase1</expectedResult> + <actualResult type="variable">getListOfThumbnailsFullScreenPageCase1</actualResult> + </assertEquals> + <actionGroup ref="StorefrontProductPageCloseFullscreenGalleryActionGroup" stepKey="closeFullScreenPageCase1"/> + + <!--CASE 2: Selected options = B2; Expected media : C1, C2, C3--> + <click selector="{{StorefrontProductInfoMainSection.swatchOptionByLabel($swatchAttributeSecondOption.option[store_labels][0][label]$)}}" stepKey="chooseFirstOptionCase2"/> + <waitForElementVisible selector="{{StorefrontProductMediaSection.fotoramaAnyMedia}}" stepKey="waitForThumbnailsAppearCase2"/> + <grabMultiple userInput="src" selector="{{StorefrontProductMediaSection.fotoramaAnyMedia}}" stepKey="getListOfThumbnailsCase2"/> + <assertRegExp stepKey="checkPositionInThumbnailForImage1Case2"> + <expectedResult type="string">|{{Magento2.filename}}.*.jpg|</expectedResult> + <actualResult type="variable">getListOfThumbnailsCase2[0]</actualResult> + </assertRegExp> + <assertRegExp stepKey="checkPositionInThumbnailForImage2Case2"> + <expectedResult type="string">|{{Magento3.filename}}.*.jpg|</expectedResult> + <actualResult type="variable">getListOfThumbnailsCase2[1]</actualResult> + </assertRegExp> + <assertRegExp stepKey="checkPositionInThumbnailForImage3Case2"> + <expectedResult type="string">|{{TestImageNew.filename}}.*.jpg|</expectedResult> + <actualResult type="variable">getListOfThumbnailsCase2[2]</actualResult> + </assertRegExp> + <actionGroup ref="AssertStorefrontProductImageAppearsOnProductPagePreviewActionGroup" stepKey="seeImageOnPreviewCase2"> + <argument name="productImage" value="{{Magento2.filename}}"/> + </actionGroup> + <actionGroup ref="StorefrontProductPageOpenImageFullscreenActionGroup" stepKey="openFullScreenPageCase2"/> + <grabMultiple userInput="src" selector="{{StorefrontProductMediaSection.fotoramaAnyMedia}}" stepKey="getListOfThumbnailsFullScreenPageCase2"/> + <assertEquals stepKey="checkPositionInThumbnailForImagesFromFullScreenPageCase2"> + <expectedResult type="variable">getListOfThumbnailsCase2</expectedResult> + <actualResult type="variable">getListOfThumbnailsFullScreenPageCase2</actualResult> + </assertEquals> + <actionGroup ref="StorefrontProductPageCloseFullscreenGalleryActionGroup" stepKey="closeFullScreenPageCase2"/> + + <!--CASE 3: Selected options = B3; Expected media : E1, E2, E3, C1, C2, C3--> + <click selector="{{StorefrontProductInfoMainSection.swatchOptionByLabel($swatchAttributeThirdOption.option[store_labels][0][label]$)}}" stepKey="chooseFirstOptionCase3"/> + <waitForElementVisible selector="{{StorefrontProductMediaSection.fotoramaAnyMedia}}" stepKey="waitForThumbnailsAppearCase3"/> + <grabMultiple userInput="src" selector="{{StorefrontProductMediaSection.fotoramaAnyMedia}}" stepKey="getListOfThumbnailsCase3"/> + <assertRegExp stepKey="checkPositionInThumbnailForImage1Case3"> + <expectedResult type="string">|{{Magento3.filename}}.*.jpg|</expectedResult> + <actualResult type="variable">getListOfThumbnailsCase3[0]</actualResult> + </assertRegExp> + <assertRegExp stepKey="checkPositionInThumbnailForImage2Case3"> + <expectedResult type="string">|{{TestImageAdobe.filename}}.*.jpg|</expectedResult> + <actualResult type="variable">getListOfThumbnailsCase3[1]</actualResult> + </assertRegExp> + <assertRegExp stepKey="checkPositionInThumbnailForImage3Case3"> + <expectedResult type="string">|{{MagentoLogo.filename}}.*.png|</expectedResult> + <actualResult type="variable">getListOfThumbnailsCase3[2]</actualResult> + </assertRegExp> + <assertRegExp stepKey="checkPositionInThumbnailForImage4Case3"> + <expectedResult type="string">|{{Magento2.filename}}.*.jpg|</expectedResult> + <actualResult type="variable">getListOfThumbnailsCase3[3]</actualResult> + </assertRegExp> + <assertRegExp stepKey="checkPositionInThumbnailForImage5Case3"> + <expectedResult type="string">|{{Magento3.filename}}.*.jpg|</expectedResult> + <actualResult type="variable">getListOfThumbnailsCase3[4]</actualResult> + </assertRegExp> + <assertRegExp stepKey="checkPositionInThumbnailForImage6Case3"> + <expectedResult type="string">|{{TestImageNew.filename}}.*.jpg|</expectedResult> + <actualResult type="variable">getListOfThumbnailsCase3[5]</actualResult> + </assertRegExp> + <actionGroup ref="AssertStorefrontProductImageAppearsOnProductPagePreviewActionGroup" stepKey="seeImageOnPreviewCase3"> + <argument name="productImage" value="{{Magento3.filename}}"/> + </actionGroup> + <actionGroup ref="StorefrontProductPageOpenImageFullscreenActionGroup" stepKey="openFullScreenPageCase3"/> + <grabMultiple userInput="src" selector="{{StorefrontProductMediaSection.fotoramaAnyMedia}}" stepKey="getListOfThumbnailsFullScreenPageCase3"/> + <assertEquals stepKey="checkPositionInThumbnailForImagesFromFullScreenPageCase3"> + <expectedResult type="variable">getListOfThumbnailsCase3</expectedResult> + <actualResult type="variable">getListOfThumbnailsFullScreenPageCase3</actualResult> + </assertEquals> + <actionGroup ref="StorefrontProductPageCloseFullscreenGalleryActionGroup" stepKey="closeFullScreenPageCase3"/> + + <!--CASE 4: Selected options = none; Expected media : C1, C2, C3--> + <click selector="{{StorefrontProductInfoMainSection.swatchOptionByLabel($swatchAttributeThirdOption.option[store_labels][0][label]$)}}" stepKey="unselectThirdOptionCase4"/> + <waitForElementVisible selector="{{StorefrontProductMediaSection.fotoramaAnyMedia}}" stepKey="waitForThumbnailsAppearCase4"/> + <grabMultiple userInput="src" selector="{{StorefrontProductMediaSection.fotoramaAnyMedia}}" stepKey="getListOfThumbnailsCase4"/> + <assertRegExp stepKey="checkPositionInThumbnailForImage1Case4"> + <expectedResult type="string">|{{Magento2.filename}}.*.jpg|</expectedResult> + <actualResult type="variable">getListOfThumbnailsCase4[0]</actualResult> + </assertRegExp> + <assertRegExp stepKey="checkPositionInThumbnailForImage2Case4"> + <expectedResult type="string">|{{Magento3.filename}}.*.jpg|</expectedResult> + <actualResult type="variable">getListOfThumbnailsCase4[1]</actualResult> + </assertRegExp> + <assertRegExp stepKey="checkPositionInThumbnailForImage3Case4"> + <expectedResult type="string">|{{TestImageNew.filename}}.*.jpg|</expectedResult> + <actualResult type="variable">getListOfThumbnailsCase4[2]</actualResult> + </assertRegExp> + <actionGroup ref="AssertStorefrontProductImageAppearsOnProductPagePreviewActionGroup" stepKey="seeImageOnPreviewCase4"> + <argument name="productImage" value="{{Magento2.filename}}"/> + </actionGroup> + <actionGroup ref="StorefrontProductPageOpenImageFullscreenActionGroup" stepKey="openFullScreenPageCase4"/> + <grabMultiple userInput="src" selector="{{StorefrontProductMediaSection.fotoramaAnyMedia}}" stepKey="getListOfThumbnailsFullScreenPageCase4"/> + <assertEquals stepKey="checkPositionInThumbnailForImagesFromFullScreenPageCase4"> + <expectedResult type="variable">getListOfThumbnailsCase4</expectedResult> + <actualResult type="variable">getListOfThumbnailsFullScreenPageCase4</actualResult> + </assertEquals> + <actionGroup ref="StorefrontProductPageCloseFullscreenGalleryActionGroup" stepKey="closeFullScreenPageCase4"/> + </test> +</tests> diff --git a/app/code/Magento/ConfigurableProduct/Test/Mftf/Test/StorefrontShouldSeeOnlyConfigurableProductChildAssignedToSeparateCategoryTest.xml b/app/code/Magento/ConfigurableProduct/Test/Mftf/Test/StorefrontShouldSeeOnlyConfigurableProductChildAssignedToSeparateCategoryTest.xml new file mode 100644 index 0000000000000..7662779a6955f --- /dev/null +++ b/app/code/Magento/ConfigurableProduct/Test/Mftf/Test/StorefrontShouldSeeOnlyConfigurableProductChildAssignedToSeparateCategoryTest.xml @@ -0,0 +1,138 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + /** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +--> + +<tests xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/testSchema.xsd"> + <test name="StorefrontShouldSeeOnlyConfigurableProductChildAssignedToSeparateCategoryTest"> + <annotations> + <features value="ConfigurableProduct"/> + <stories value="View products"/> + <title value="It should be possible to only view the child product of a configurable product"/> + <description value="Create configurable product, add to category such that only child variation is visible in category"/> + <severity value="CRITICAL"/> + <testCaseId value="MC-25651"/> + <group value="configurable_product"/> + <group value="catalog"/> + </annotations> + <before> + <!-- Create the category --> + <createData entity="ApiCategory" stepKey="createCategory"/> + <createData entity="_defaultCategory" stepKey="secondCategory"/> + + <!-- Create an attribute with two options to be used in the first child product --> + <createData entity="productAttributeWithTwoOptions" stepKey="createConfigProductAttribute"/> + <createData entity="productAttributeOption1" stepKey="createConfigProductAttributeOption1"> + <requiredEntity createDataKey="createConfigProductAttribute"/> + </createData> + <createData entity="productAttributeOption2" stepKey="createConfigProductAttributeOption2"> + <requiredEntity createDataKey="createConfigProductAttribute"/> + </createData> + + <!-- Add the attribute we just created to default attribute set --> + <createData entity="AddToDefaultSet" stepKey="createConfigAddToAttributeSet"> + <requiredEntity createDataKey="createConfigProductAttribute"/> + </createData> + + <!-- Get the first option of the attribute we created --> + <getData entity="ProductAttributeOptionGetter" index="1" stepKey="getConfigAttributeOption1"> + <requiredEntity createDataKey="createConfigProductAttribute"/> + </getData> + + <!-- Get the second option of the attribute we created --> + <getData entity="ProductAttributeOptionGetter" index="2" stepKey="getConfigAttributeOption2"> + <requiredEntity createDataKey="createConfigProductAttribute"/> + </getData> + + <!-- Create the configurable product and add it to the category --> + <createData entity="ApiConfigurableProduct" stepKey="createConfigProduct"> + <requiredEntity createDataKey="createCategory"/> + </createData> + + <!-- Create a simple product and give it the attribute with the first option --> + <createData entity="ApiSimpleOne" stepKey="createConfigChildProduct1"> + <requiredEntity createDataKey="createConfigProductAttribute"/> + <requiredEntity createDataKey="getConfigAttributeOption1"/> + </createData> + + <!-- Create a simple product and give it the attribute with the second option --> + <createData entity="ApiSimpleTwo" stepKey="createConfigChildProduct2"> + <requiredEntity createDataKey="createConfigProductAttribute"/> + <requiredEntity createDataKey="getConfigAttributeOption2"/> + </createData> + + <createData entity="ConfigurableProductTwoOptions" stepKey="createConfigProductOption"> + <requiredEntity createDataKey="createConfigProduct"/> + <requiredEntity createDataKey="createConfigProductAttribute"/> + <requiredEntity createDataKey="getConfigAttributeOption1"/> + <requiredEntity createDataKey="getConfigAttributeOption2"/> + </createData> + + <!-- Add the first simple product to the configurable product --> + <createData entity="ConfigurableProductAddChild" stepKey="createConfigProductAddChild1"> + <requiredEntity createDataKey="createConfigProduct"/> + <requiredEntity createDataKey="createConfigChildProduct1"/> + </createData> + + <!-- Add the second simple product to the configurable product --> + <createData entity="ConfigurableProductAddChild" stepKey="createConfigProductAddChild2"> + <requiredEntity createDataKey="createConfigProduct"/> + <requiredEntity createDataKey="createConfigChildProduct2"/> + </createData> + + <actionGroup ref="AdminLoginActionGroup" stepKey="loginToAdmin"/> + </before> + + <after> + <deleteData createDataKey="createConfigProduct" stepKey="deleteConfigProduct"/> + <deleteData createDataKey="createConfigChildProduct1" stepKey="deleteConfigChildProduct1"/> + <deleteData createDataKey="createConfigChildProduct2" stepKey="deleteConfigChildProduct2"/> + <deleteData createDataKey="createConfigProductAttribute" stepKey="deleteConfigProductAttribute"/> + <deleteData createDataKey="createCategory" stepKey="deleteApiCategory"/> + <deleteData createDataKey="secondCategory" stepKey="deleteSecondCategory"/> + <actionGroup ref="AdminLogoutActionGroup" stepKey="logoutFromAdmin"/> + + <!-- Reindex invalidated indices after product attribute has been created/deleted --> + <magentoCron groups="index" stepKey="reindexInvalidatedIndices"/> + </after> + + <!-- Go to the product page for the first product --> + <actionGroup ref="AdminProductPageOpenByIdActionGroup" stepKey="openConfigChildProduct1Page"> + <argument name="productId" value="$$createConfigChildProduct1.id$$"/> + </actionGroup> + + <!-- Edit the visibility the first simple product --> + <selectOption selector="{{AdminProductFormSection.visibility}}" userInput="Catalog, Search" stepKey="selectVisibilityCatalogSearch"/> + <!--Add to category--> + <actionGroup ref="AdminAssignCategoryToProductAndSaveActionGroup" stepKey="addProductToCategoryAndSaveProduct"> + <argument name="categoryName" value="$$secondCategory.name$$"/> + </actionGroup> + + <magentoCLI command="indexer:reindex" arguments="catalogsearch_fulltext" stepKey="reindexSearchIndex"/> + + <!-- Go to storefront to view child product --> + <actionGroup ref="StorefrontNavigateCategoryPageActionGroup" stepKey="goToSecondCategoryStorefront"> + <argument name="category" value="$$secondCategory$$"/> + </actionGroup> + <actionGroup ref="StorefrontCheckCategorySimpleProductActionGroup" stepKey="seeChildProductInCategory"> + <argument name="product" value="$createConfigChildProduct1$"/> + </actionGroup> + <actionGroup ref="StorefrontCheckProductIsMissingInCategoryProductsPageActionGroup" stepKey="dontSeeOtherChildProduct"> + <argument name="productName" value="$$createConfigChildProduct2.name$$"/> + </actionGroup> + <actionGroup ref="StorefrontCheckProductIsMissingInCategoryProductsPageActionGroup" stepKey="dontSeeParentProduct"> + <argument name="productName" value="$$createConfigProduct.name$$"/> + </actionGroup> + <actionGroup ref="StorefrontOpenProductFromCategoryPageActionGroup" stepKey="openConfigChildProductFromCategoryPage"> + <argument name="productName" value="$$createConfigChildProduct1.name$$"/> + </actionGroup> + <actionGroup ref="AssertStorefrontProductDetailPageNameAndUrlActionGroup" stepKey="checkStorefrontConfigChildProductPage"> + <argument name="productName" value="$$createConfigChildProduct1.name$$"/> + <argument name="productUrl" value="$$createConfigChildProduct1.custom_attributes[url_key]$$"/> + </actionGroup> + </test> +</tests> diff --git a/app/code/Magento/ConfigurableProduct/Test/Mftf/Test/StorefrontSortingByPriceForConfigurableWithCatalogRuleAppliedTest.xml b/app/code/Magento/ConfigurableProduct/Test/Mftf/Test/StorefrontSortingByPriceForConfigurableWithCatalogRuleAppliedTest.xml index 7828478bc963e..ef9f71da0ebca 100644 --- a/app/code/Magento/ConfigurableProduct/Test/Mftf/Test/StorefrontSortingByPriceForConfigurableWithCatalogRuleAppliedTest.xml +++ b/app/code/Magento/ConfigurableProduct/Test/Mftf/Test/StorefrontSortingByPriceForConfigurableWithCatalogRuleAppliedTest.xml @@ -8,15 +8,18 @@ <tests xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/testSchema.xsd"> - <test name="StorefrontSortingByPriceForConfigurableProductWithCatalogRuleAppliedTest"> + <test name="StorefrontCheckSortingByPriceForConfigurableWithCatalogRuleAppliedTest"> <annotations> <features value="ConfigurableProduct"/> - <stories value="View soting by price in storefront"/> - <title value="Sorting by price for Configurable with Catalog Rule applied"/> + <stories value="Check sorting by price on storefront"/> + <title value="Check sorting by price for Configurable product with Catalog Rule applied"/> <description value="Sort by price should be correct if the apply Catalog Rule to child product of configurable product"/> <severity value="CRITICAL"/> - <testCaseId value="MAGETWO-69988"/> - <group value="configurable_product"/> + <testCaseId value="MC-11926"/> + <useCaseId value="MAGETWO-66688"/> + <group value="catalog"/> + <group value="catalogRule"/> + <group value="configurableProduct"/> </annotations> <before> <createData entity="ApiCategory" stepKey="createCategory"/> @@ -28,66 +31,66 @@ <requiredEntity createDataKey="createCategory"/> <field key="price">10.00</field> </createData> - <createData entity="ApiConfigurableProduct" stepKey="createConfigProduct"> + <createData entity="ApiConfigurableProduct" stepKey="createConfigurableProduct"> <requiredEntity createDataKey="createCategory"/> </createData> - <createData entity="productAttributeWithTwoOptions" stepKey="createConfigProductAttribute"/> - <createData entity="productAttributeOption1" stepKey="createConfigProductAttributeOption1"> - <requiredEntity createDataKey="createConfigProductAttribute"/> + <createData entity="productAttributeWithTwoOptions" stepKey="createConfigurableProductAttribute"/> + <createData entity="productAttributeOption1" stepKey="createConfigurableProductAttributeOption1"> + <requiredEntity createDataKey="createConfigurableProductAttribute"/> </createData> - <createData entity="productAttributeOption2" stepKey="createConfigProductAttributeOption2"> - <requiredEntity createDataKey="createConfigProductAttribute"/> + <createData entity="productAttributeOption2" stepKey="createConfigurableProductAttributeOption2"> + <requiredEntity createDataKey="createConfigurableProductAttribute"/> </createData> - <createData entity="productAttributeOption3" stepKey="createConfigProductAttributeOption3"> - <requiredEntity createDataKey="createConfigProductAttribute"/> + <createData entity="productAttributeOption3" stepKey="createConfigurableProductAttributeOption3"> + <requiredEntity createDataKey="createConfigurableProductAttribute"/> </createData> <createData entity="AddToDefaultSet" stepKey="createConfigAddToAttributeSet"> - <requiredEntity createDataKey="createConfigProductAttribute"/> + <requiredEntity createDataKey="createConfigurableProductAttribute"/> </createData> <getData entity="ProductAttributeOptionGetter" index="1" stepKey="getConfigAttributeOption1"> - <requiredEntity createDataKey="createConfigProductAttribute"/> + <requiredEntity createDataKey="createConfigurableProductAttribute"/> </getData> <getData entity="ProductAttributeOptionGetter" index="2" stepKey="getConfigAttributeOption2"> - <requiredEntity createDataKey="createConfigProductAttribute"/> + <requiredEntity createDataKey="createConfigurableProductAttribute"/> </getData> <getData entity="ProductAttributeOptionGetter" index="3" stepKey="getConfigAttributeOption3"> - <requiredEntity createDataKey="createConfigProductAttribute"/> + <requiredEntity createDataKey="createConfigurableProductAttribute"/> </getData> <createData entity="ApiSimpleOne" stepKey="createConfigChildProduct1"> - <requiredEntity createDataKey="createConfigProductAttribute"/> + <requiredEntity createDataKey="createConfigurableProductAttribute"/> <requiredEntity createDataKey="getConfigAttributeOption1"/> <field key="price">15.00</field> </createData> <createData entity="ApiSimpleTwo" stepKey="createConfigChildProduct2"> - <requiredEntity createDataKey="createConfigProductAttribute"/> + <requiredEntity createDataKey="createConfigurableProductAttribute"/> <requiredEntity createDataKey="getConfigAttributeOption2"/> <field key="price">20.00</field> </createData> <createData entity="ApiSimpleTwo" stepKey="createConfigChildProduct3"> - <requiredEntity createDataKey="createConfigProductAttribute"/> + <requiredEntity createDataKey="createConfigurableProductAttribute"/> <requiredEntity createDataKey="getConfigAttributeOption3"/> <field key="price">25.00</field> </createData> - <createData entity="ConfigurableProductThreeOptions" stepKey="createConfigProductOption"> - <requiredEntity createDataKey="createConfigProduct"/> - <requiredEntity createDataKey="createConfigProductAttribute"/> + <createData entity="ConfigurableProductThreeOptions" stepKey="createConfigurableProductOption"> + <requiredEntity createDataKey="createConfigurableProduct"/> + <requiredEntity createDataKey="createConfigurableProductAttribute"/> <requiredEntity createDataKey="getConfigAttributeOption1"/> <requiredEntity createDataKey="getConfigAttributeOption2"/> <requiredEntity createDataKey="getConfigAttributeOption3"/> </createData> - <createData entity="ConfigurableProductAddChild" stepKey="createConfigProductAddChild1"> - <requiredEntity createDataKey="createConfigProduct"/> + <createData entity="ConfigurableProductAddChild" stepKey="createConfigurableProductAddChild1"> + <requiredEntity createDataKey="createConfigurableProduct"/> <requiredEntity createDataKey="createConfigChildProduct1"/> </createData> - <createData entity="ConfigurableProductAddChild" stepKey="createConfigProductAddChild2"> - <requiredEntity createDataKey="createConfigProduct"/> + <createData entity="ConfigurableProductAddChild" stepKey="createConfigurableProductAddChild2"> + <requiredEntity createDataKey="createConfigurableProduct"/> <requiredEntity createDataKey="createConfigChildProduct2"/> </createData> - <createData entity="ConfigurableProductAddChild" stepKey="createConfigProductAddChild3"> - <requiredEntity createDataKey="createConfigProduct"/> + <createData entity="ConfigurableProductAddChild" stepKey="createConfigurableProductAddChild3"> + <requiredEntity createDataKey="createConfigurableProduct"/> <requiredEntity createDataKey="createConfigChildProduct3"/> </createData> - <actionGroup ref="LoginAsAdmin" stepKey="loginAsAdmin"/> + <actionGroup ref="AdminLoginActionGroup" stepKey="loginAsAdmin"/> <!--SKU Product Attribute is enabled for Promo Rule Conditions--> <actionGroup ref="NavigateToEditProductAttributeActionGroup" stepKey="navigateToSkuProductAttribute"> <argument name="ProductAttribute" value="sku"/> @@ -95,8 +98,8 @@ <actionGroup ref="ChangeUseForPromoRuleConditionsProductAttributeActionGroup" stepKey="changeUseForPromoRuleConditionsProductAttributeToYes"> <argument name="option" value="Yes"/> </actionGroup> - <magentoCLI command="indexer:reindex" stepKey="reindex1"/> - <magentoCLI command="cache:flush" stepKey="flushCache1"/> + + <magentoCron groups="index" stepKey="reindexInvalidatedIndices"/> </before> <after> @@ -106,8 +109,8 @@ <deleteData createDataKey="createConfigChildProduct1" stepKey="deleteConfigChildProduct1"/> <deleteData createDataKey="createConfigChildProduct2" stepKey="deleteConfigChildProduct2"/> <deleteData createDataKey="createConfigChildProduct3" stepKey="deleteConfigChildProduct3"/> - <deleteData createDataKey="createConfigProduct" stepKey="deleteConfigProduct"/> - <deleteData createDataKey="createConfigProductAttribute" stepKey="deleteConfigProductAttribute"/> + <deleteData createDataKey="createConfigurableProduct" stepKey="deleteConfigProduct"/> + <deleteData createDataKey="createConfigurableProductAttribute" stepKey="deleteConfigProductAttribute"/> <!--SKU Product Attribute is disable for Promo Rule Conditions--> <actionGroup ref="NavigateToEditProductAttributeActionGroup" stepKey="navigateToSkuProductAttribute"> @@ -116,7 +119,13 @@ <actionGroup ref="ChangeUseForPromoRuleConditionsProductAttributeActionGroup" stepKey="changeUseForPromoRuleConditionsProductAttributeToNo"> <argument name="option" value="No"/> </actionGroup> - <actionGroup ref="logout" stepKey="logoutFromAdmin"/> + <actionGroup ref="RemoveCatalogPriceRuleActionGroup" stepKey="deleteCatalogPriceRule"> + <argument name="ruleName" value="{{_defaultCatalogRule.name}}"/> + </actionGroup> + <actionGroup ref="AdminLogoutActionGroup" stepKey="logoutFromAdmin"/> + + <!-- Reindex invalidated indices after product attribute has been created/deleted --> + <magentoCron groups="index" stepKey="reindexInvalidatedIndices"/> </after> <!--Open category with products and Sort by price desc--> @@ -127,20 +136,25 @@ <argument name="sortBy" value="price"/> <argument name="sort" value="desc"/> </actionGroup> - <see selector="{{StorefrontCategoryMainSection.lineProductName('1')}}" userInput="$$createConfigProduct.name$$" stepKey="seeConfigurableProduct"/> + <see selector="{{StorefrontCategoryMainSection.lineProductName('1')}}" userInput="$$createConfigurableProduct.name$$" stepKey="seeConfigurableProduct"/> <see selector="{{StorefrontCategoryMainSection.lineProductName('2')}}" userInput="$$createSimpleProduct2.name$$" stepKey="seeSimpleProductTwo"/> <see selector="{{StorefrontCategoryMainSection.lineProductName('3')}}" userInput="$$createSimpleProduct.name$$" stepKey="seeSimpleProduct"/> <!--Create and apply catalog price rule--> - <actionGroup ref="NewCatalogPriceRuleByUIWithConditionIsSKUActionGroup" stepKey="createCatalogPriceRule"> - <argument name="catalogRule" value="CatalogRuleByPercentWith96Amount" /> - <argument name="productSku" value="$$createConfigChildProduct3.sku$$" /> + <actionGroup ref="AdminOpenNewCatalogPriceRuleFormPageActionGroup" stepKey="startCreatingCatalogPriceRule"/> + <actionGroup ref="AdminCatalogPriceRuleFillMainInfoActionGroup" stepKey="fillMainInfoForCatalogPriceRule"> + <argument name="groups" value="'NOT LOGGED IN'"/> </actionGroup> - <actionGroup ref="SelectNotLoggedInCustomerGroupActionGroup" stepKey="selectNotLoggedInCustomerGroup"/> - <click selector="{{AdminNewCatalogPriceRule.saveAndApply}}" stepKey="clickSaveAndApplyRules"/> - - <magentoCLI command="indexer:reindex" stepKey="reindex"/> - <magentoCLI command="cache:flush" stepKey="flushCache"/> + <actionGroup ref="AdminCatalogPriceRuleAddSkuConditionActionGroup" stepKey="addProductSkuBasedCondition"> + <argument name="productSku" value="$createConfigChildProduct3.sku$"/> + </actionGroup> + <actionGroup ref="AdminCatalogPriceRuleFillActionsActionGroup" stepKey="fillActionsForCatalogPriceRule"> + <argument name="apply" value="{{CatalogRuleByPercentWith96Amount.simple_action}}"/> + <argument name="discountAmount" value="{{CatalogRuleByPercentWith96Amount.discount_amount}}"/> + </actionGroup> + <actionGroup ref="AdminCatalogPriceRuleSaveAndApplyActionGroup" stepKey="saveAndApplyCatalogPriceRule"/> + <magentoCLI command="indexer:reindex" arguments="catalogsearch_fulltext catalog_category_product catalog_product_price catalogrule_rule" stepKey="reindexIndices"/> + <magentoCLI command="cache:clean" arguments="full_page" stepKey="flushCache"/> <!--Reopen category with products and Sort by price desc--> <actionGroup ref="GoToStorefrontCategoryPageByParametersActionGroup" stepKey="goToStorefrontCategoryPage2"> @@ -152,13 +166,6 @@ </actionGroup> <see selector="{{StorefrontCategoryMainSection.lineProductName('1')}}" userInput="$$createSimpleProduct2.name$$" stepKey="seeSimpleProductTwo2"/> <see selector="{{StorefrontCategoryMainSection.lineProductName('2')}}" userInput="$$createSimpleProduct.name$$" stepKey="seeSimpleProduct2"/> - <see selector="{{StorefrontCategoryMainSection.lineProductName('3')}}" userInput="$$createConfigProduct.name$$" stepKey="seeConfigurableProduct2"/> - - <!-- Delete the rule --> - <amOnPage url="{{CatalogRulePage.url}}" stepKey="goToPriceRulePage"/> - <actionGroup ref="deleteEntitySecondaryGrid" stepKey="deletePriceRule"> - <argument name="name" value="{{CatalogRuleByPercentWith96Amount.name}}"/> - <argument name="searchInput" value="{{AdminSecondaryGridSection.catalogRuleIdentifierSearch}}"/> - </actionGroup> + <see selector="{{StorefrontCategoryMainSection.lineProductName('3')}}" userInput="$$createConfigurableProduct.name$$" stepKey="seeConfigurableProduct2"/> </test> </tests> diff --git a/app/code/Magento/ConfigurableProduct/Test/Mftf/Test/StorefrontVerifyConfigurableProductLayeredNavigationTest.xml b/app/code/Magento/ConfigurableProduct/Test/Mftf/Test/StorefrontVerifyConfigurableProductLayeredNavigationTest.xml index 3ebd9d6ef5367..7acece767760d 100644 --- a/app/code/Magento/ConfigurableProduct/Test/Mftf/Test/StorefrontVerifyConfigurableProductLayeredNavigationTest.xml +++ b/app/code/Magento/ConfigurableProduct/Test/Mftf/Test/StorefrontVerifyConfigurableProductLayeredNavigationTest.xml @@ -14,14 +14,14 @@ <title value="Out of stock configurable attribute option doesn't show in Layered Navigation"/> <description value=" Login as admin and verify out of stock configurable attribute option doesn't show in Layered Navigation"/> <testCaseId value="MC-13734"/> - <severity value="CRITICAL"/> + <severity value="BLOCKER"/> <group value="ConfigurableProduct"/> <group value="mtf_migrated"/> </annotations> <before> <!-- Login as Admin --> - <actionGroup ref="LoginAsAdmin" stepKey="loginToAdminPanel"/> + <actionGroup ref="AdminLoginActionGroup" stepKey="loginToAdminPanel"/> <!-- Create Default Category --> <createData entity="_defaultCategory" stepKey="createCategory"/> @@ -116,7 +116,10 @@ <deleteData createDataKey="createConfigChildProduct2" stepKey="deleteConfigChildProduct2"/> <deleteData createDataKey="createConfigChildProduct3" stepKey="deleteConfigChildProduct3"/> <deleteData createDataKey="createConfigProductAttribute" stepKey="deleteAttribute"/> - <actionGroup ref="logout" stepKey="logout"/> + <actionGroup ref="AdminLogoutActionGroup" stepKey="logout"/> + + <!-- Reindex invalidated indices after product attribute has been created/deleted --> + <magentoCron groups="index" stepKey="reindexInvalidatedIndices"/> </after> <!-- Open Product Index Page and Filter First Child product --> diff --git a/app/code/Magento/ConfigurableProduct/Test/Mftf/Test/StorefrontVisibilityOfDuplicateProductTest.xml b/app/code/Magento/ConfigurableProduct/Test/Mftf/Test/StorefrontVisibilityOfDuplicateProductTest.xml index 2cdad86aa0b2c..801dfdb8540e8 100644 --- a/app/code/Magento/ConfigurableProduct/Test/Mftf/Test/StorefrontVisibilityOfDuplicateProductTest.xml +++ b/app/code/Magento/ConfigurableProduct/Test/Mftf/Test/StorefrontVisibilityOfDuplicateProductTest.xml @@ -19,7 +19,7 @@ <group value="ConfigurableProduct"/> </annotations> <before> - <actionGroup ref="LoginAsAdmin" stepKey="loginAsAdmin"/> + <actionGroup ref="AdminLoginActionGroup" stepKey="loginAsAdmin"/> <!--Create configurable product--> <comment userInput="Create configurable product" stepKey="commentCreateConfigProduct"/> <createData entity="SimpleSubCategory" stepKey="createCategory"/> @@ -36,20 +36,22 @@ <actionGroup ref="ResetProductGridToDefaultViewActionGroup" stepKey="resetGridToDefaultKeywordSearch"/> <!--Delete product attributes--> <comment userInput="Delete product attributes" stepKey="deleteCommentAttributes"/> - <actionGroup ref="DeleteProductAttributeByLabelActionGroup" stepKey="deleteProductAttribute"> - <argument name="ProductAttribute" value="colorProductAttribute"/> + <actionGroup ref="AdminDeleteProductAttributeByLabelActionGroup" stepKey="deleteProductAttribute"> + <argument name="productAttributeLabel" value="{{colorProductAttribute.default_label}}"/> </actionGroup> <click selector="{{AdminProductAttributeGridSection.ResetFilter}}" stepKey="resetFiltersOnGridFirst"/> - <actionGroup ref="DeleteProductAttributeByLabelActionGroup" stepKey="deleteProductSecondAttribute"> - <argument name="ProductAttribute" value="productAttributeColor"/> + <actionGroup ref="AdminDeleteProductAttributeByLabelActionGroup" stepKey="deleteProductSecondAttribute"> + <argument name="productAttributeLabel" value="{{productAttributeColor.default_label}}"/> </actionGroup> <click selector="{{AdminProductAttributeGridSection.ResetFilter}}" stepKey="resetFiltersOnGridSecond"/> <deleteData createDataKey="createCategory" stepKey="deleteCategory"/> - <actionGroup ref="logout" stepKey="logout"/> + <actionGroup ref="AdminLogoutActionGroup" stepKey="logout"/> </after> <!--Create attribute and options for product--> <comment userInput="Create attribute and options for product" stepKey="commentCreateAttributesAndOptions"/> - <amOnPage url="{{AdminProductEditPage.url($$createConfigProduct.id$$)}}" stepKey="navigateToConfigProductPage"/> + <actionGroup ref="AdminProductPageOpenByIdActionGroup" stepKey="navigateToConfigProductPage"> + <argument name="productId" value="$$createConfigProduct.id$$"/> + </actionGroup> <waitForPageLoad stepKey="waitForProductPageLoad"/> <actionGroup ref="AddProductImageActionGroup" stepKey="addImageForProduct1"> <argument name="image" value="MagentoLogo"/> @@ -114,7 +116,7 @@ <actionGroup ref="SaveProductFormActionGroup" stepKey="saveProduct"/> <!--Duplicate the product--> <comment userInput="Duplicate the product" stepKey="commentDuplicateProduct"/> - <actionGroup ref="AdminFormSaveAndDuplicate" stepKey="saveAndDuplicateProductForm"/> + <actionGroup ref="AdminFormSaveAndDuplicateActionGroup" stepKey="saveAndDuplicateProductForm"/> <click selector="{{AdminProductFormSection.enableProductLabel}}" stepKey="clickEnableProduct"/> <fillField selector="{{AdminProductFormSection.productName}}" userInput="$$createConfigProduct.name$$-Updated" stepKey="fillProductName"/> <selectOption selector="{{AdminProductFormSection.productStockStatus}}" userInput="1" stepKey="selectInStock"/> diff --git a/app/code/Magento/ConfigurableProduct/Test/Unit/Model/Plugin/ProductRepositorySaveTest.php b/app/code/Magento/ConfigurableProduct/Test/Unit/Model/Plugin/ProductRepositorySaveTest.php index 1c3e421ae924f..ea8bb065a7786 100644 --- a/app/code/Magento/ConfigurableProduct/Test/Unit/Model/Plugin/ProductRepositorySaveTest.php +++ b/app/code/Magento/ConfigurableProduct/Test/Unit/Model/Plugin/ProductRepositorySaveTest.php @@ -9,30 +9,26 @@ use Magento\Catalog\Api\ProductAttributeRepositoryInterface; use Magento\Catalog\Api\ProductRepositoryInterface; use Magento\Catalog\Model\Product; -use Magento\Catalog\Model\ProductFactory; use Magento\ConfigurableProduct\Api\Data\OptionInterface; use Magento\ConfigurableProduct\Model\Plugin\ProductRepositorySave; use Magento\ConfigurableProduct\Model\Product\Type\Configurable; use Magento\ConfigurableProduct\Test\Unit\Model\Product\ProductExtensionAttributes; use Magento\Framework\TestFramework\Unit\Helper\ObjectManager; -use PHPUnit_Framework_MockObject_MockObject as MockObject; +use PHPUnit\Framework\MockObject\MockObject; +use PHPUnit\Framework\TestCase; /** - * Class ProductRepositorySaveTest + * Test for ProductRepositorySave plugin + * * @SuppressWarnings(PHPMD.CouplingBetweenObjects) */ -class ProductRepositorySaveTest extends \PHPUnit\Framework\TestCase +class ProductRepositorySaveTest extends TestCase { /** * @var ProductAttributeRepositoryInterface|MockObject */ private $productAttributeRepository; - /** - * @var ProductFactory|MockObject - */ - private $productFactory; - /** * @var Product|MockObject */ @@ -68,15 +64,13 @@ class ProductRepositorySaveTest extends \PHPUnit\Framework\TestCase */ private $plugin; + /** + * @inheritdoc + */ protected function setUp() { $this->productAttributeRepository = $this->getMockForAbstractClass(ProductAttributeRepositoryInterface::class); - $this->productFactory = $this->getMockBuilder(ProductFactory::class) - ->disableOriginalConstructor() - ->setMethods(['create']) - ->getMock(); - $this->product = $this->getMockBuilder(Product::class) ->disableOriginalConstructor() ->setMethods(['getTypeId', 'getExtensionAttributes']) @@ -102,12 +96,15 @@ protected function setUp() ProductRepositorySave::class, [ 'productAttributeRepository' => $this->productAttributeRepository, - 'productFactory' => $this->productFactory + 'productRepository' => $this->productRepository ] ); } - public function testAfterSaveWhenProductIsSimple() + /** + * Validating the result after saving a configurable product + */ + public function testBeforeSaveWhenProductIsSimple() { $this->product->expects(static::once()) ->method('getTypeId') @@ -116,18 +113,21 @@ public function testAfterSaveWhenProductIsSimple() ->method('getExtensionAttributes'); $this->assertEquals( - $this->result, - $this->plugin->afterSave($this->productRepository, $this->result, $this->product) + $this->product, + $this->plugin->beforeSave($this->productRepository, $this->product)[0] ); } - public function testAfterSaveWithoutOptions() + /** + * Test saving a configurable product without attribute options + */ + public function testBeforeSaveWithoutOptions() { $this->product->expects(static::once()) ->method('getTypeId') ->willReturn(Configurable::TYPE_CODE); - $this->result->expects(static::once()) + $this->product->expects(static::once()) ->method('getExtensionAttributes') ->willReturn($this->extensionAttributes); @@ -142,23 +142,25 @@ public function testAfterSaveWithoutOptions() ->method('get'); $this->assertEquals( - $this->result, - $this->plugin->afterSave($this->productRepository, $this->result, $this->product) + $this->product, + $this->plugin->beforeSave($this->productRepository, $this->product)[0] ); } /** + * Test saving a configurable product with same set of attribute values + * * @expectedException \Magento\Framework\Exception\InputException * @expectedExceptionMessage Products "5" and "4" have the same set of attribute values. */ - public function testAfterSaveWithLinks() + public function testBeforeSaveWithLinks() { $links = [4, 5]; $this->product->expects(static::once()) ->method('getTypeId') ->willReturn(Configurable::TYPE_CODE); - $this->result->expects(static::once()) + $this->product->expects(static::once()) ->method('getExtensionAttributes') ->willReturn($this->extensionAttributes); $this->extensionAttributes->expects(static::once()) @@ -173,27 +175,26 @@ public function testAfterSaveWithLinks() $product = $this->getMockBuilder(Product::class) ->disableOriginalConstructor() - ->setMethods(['load', 'getData', '__wakeup']) + ->setMethods(['getData', '__wakeup']) ->getMock(); - $this->productFactory->expects(static::exactly(2)) - ->method('create') + $this->productRepository->expects(static::exactly(2)) + ->method('getById') ->willReturn($product); - $product->expects(static::exactly(2)) - ->method('load') - ->willReturnSelf(); $product->expects(static::never()) ->method('getData'); - $this->plugin->afterSave($this->productRepository, $this->result, $this->product); + $this->plugin->beforeSave($this->productRepository, $this->product); } /** + * Test saving a configurable product with missing attribute + * * @expectedException \Magento\Framework\Exception\InputException * @expectedExceptionMessage Product with id "4" does not contain required attribute "color". */ - public function testAfterSaveWithLinksWithMissingAttribute() + public function testBeforeSaveWithLinksWithMissingAttribute() { $simpleProductId = 4; $links = [$simpleProductId, 5]; @@ -208,7 +209,7 @@ public function testAfterSaveWithLinksWithMissingAttribute() ->method('getTypeId') ->willReturn(Configurable::TYPE_CODE); - $this->result->expects(static::once()) + $this->product->expects(static::once()) ->method('getExtensionAttributes') ->willReturn($this->extensionAttributes); $this->extensionAttributes->expects(static::once()) @@ -228,29 +229,28 @@ public function testAfterSaveWithLinksWithMissingAttribute() $product = $this->getMockBuilder(Product::class) ->disableOriginalConstructor() - ->setMethods(['load', 'getData', '__wakeup']) + ->setMethods(['getData', '__wakeup']) ->getMock(); - $this->productFactory->expects(static::once()) - ->method('create') + $this->productRepository->expects(static::once()) + ->method('getById') ->willReturn($product); - $product->expects(static::once()) - ->method('load') - ->with($simpleProductId) - ->willReturnSelf(); + $product->expects(static::once()) ->method('getData') ->with($attributeCode) ->willReturn(false); - $this->plugin->afterSave($this->productRepository, $this->result, $this->product); + $this->plugin->beforeSave($this->productRepository, $this->product); } /** + * Test saving a configurable product with duplicate attributes + * * @expectedException \Magento\Framework\Exception\InputException * @expectedExceptionMessage Products "5" and "4" have the same set of attribute values. */ - public function testAfterSaveWithLinksWithDuplicateAttributes() + public function testBeforeSaveWithLinksWithDuplicateAttributes() { $links = [4, 5]; $attributeCode = 'color'; @@ -264,7 +264,7 @@ public function testAfterSaveWithLinksWithDuplicateAttributes() ->method('getTypeId') ->willReturn(Configurable::TYPE_CODE); - $this->result->expects(static::once()) + $this->product->expects(static::once()) ->method('getExtensionAttributes') ->willReturn($this->extensionAttributes); $this->extensionAttributes->expects(static::once()) @@ -284,20 +284,18 @@ public function testAfterSaveWithLinksWithDuplicateAttributes() $product = $this->getMockBuilder(Product::class) ->disableOriginalConstructor() - ->setMethods(['load', 'getData', '__wakeup']) + ->setMethods(['getData', '__wakeup']) ->getMock(); - $this->productFactory->expects(static::exactly(2)) - ->method('create') + $this->productRepository->expects(static::exactly(2)) + ->method('getById') ->willReturn($product); - $product->expects(static::exactly(2)) - ->method('load') - ->willReturnSelf(); + $product->expects(static::exactly(4)) ->method('getData') ->with($attributeCode) ->willReturn($attributeId); - $this->plugin->afterSave($this->productRepository, $this->result, $this->product); + $this->plugin->beforeSave($this->productRepository, $this->product); } } diff --git a/app/code/Magento/ConfigurableProduct/Test/Unit/Model/Product/Type/ConfigurableTest.php b/app/code/Magento/ConfigurableProduct/Test/Unit/Model/Product/Type/ConfigurableTest.php index 165e479d99348..603b5b4c90f46 100644 --- a/app/code/Magento/ConfigurableProduct/Test/Unit/Model/Product/Type/ConfigurableTest.php +++ b/app/code/Magento/ConfigurableProduct/Test/Unit/Model/Product/Type/ConfigurableTest.php @@ -567,12 +567,18 @@ public function testHasOptionsFalse() public function testIsSalable() { $productMock = $this->getMockBuilder(\Magento\Catalog\Model\Product::class) - ->setMethods(['__wakeup', 'getStatus', 'hasData', 'getData', 'getStoreId', 'setData']) + ->setMethods(['__wakeup', 'getStatus', 'hasData', 'getData', 'getStoreId', 'setData', 'getSku']) ->disableOriginalConstructor() ->getMock(); + $productMock + ->expects($this->at(0)) + ->method('getData') + ->with('_cache_instance_store_filter') + ->willReturn(0); $productMock->expects($this->once())->method('getStatus')->willReturn(1); $productMock->expects($this->any())->method('hasData')->willReturn(true); - $productMock->expects($this->at(2))->method('getData')->with('is_salable')->willReturn(true); + $productMock->expects($this->at(1))->method('getSku')->willReturn('SKU-CODE'); + $productMock->expects($this->at(4))->method('getData')->with('is_salable')->willReturn(true); $productCollection = $this->getMockBuilder( \Magento\ConfigurableProduct\Model\ResourceModel\Product\Type\Configurable\Product\Collection::class ) diff --git a/app/code/Magento/ConfigurableProduct/Test/Unit/Model/ResourceModel/Product/LinkedProductSelectBuilderTest.php b/app/code/Magento/ConfigurableProduct/Test/Unit/Model/ResourceModel/Product/LinkedProductSelectBuilderTest.php index 3ef03f32ae05d..0794ba2cb42bf 100644 --- a/app/code/Magento/ConfigurableProduct/Test/Unit/Model/ResourceModel/Product/LinkedProductSelectBuilderTest.php +++ b/app/code/Magento/ConfigurableProduct/Test/Unit/Model/ResourceModel/Product/LinkedProductSelectBuilderTest.php @@ -50,6 +50,7 @@ protected function setUp() public function testBuild() { $productId = 42; + $storeId = 1; /** @var Select|\PHPUnit_Framework_MockObject_MockObject $selectMock */ $selectMock = $this->getMockBuilder(Select::class) @@ -67,6 +68,6 @@ public function testBuild() ->method('process') ->with($selectMock); - $this->assertEquals($expectedResult, $this->subject->build($productId)); + $this->assertEquals($expectedResult, $this->subject->build($productId, $storeId)); } } diff --git a/app/code/Magento/ConfigurableProduct/Test/Unit/Pricing/Price/LowestPriceOptionsProviderTest.php b/app/code/Magento/ConfigurableProduct/Test/Unit/Pricing/Price/LowestPriceOptionsProviderTest.php index 7c83645a9fda3..29f48dbe7a460 100644 --- a/app/code/Magento/ConfigurableProduct/Test/Unit/Pricing/Price/LowestPriceOptionsProviderTest.php +++ b/app/code/Magento/ConfigurableProduct/Test/Unit/Pricing/Price/LowestPriceOptionsProviderTest.php @@ -6,8 +6,8 @@ namespace Magento\ConfigurableProduct\Test\Unit\Pricing\Price; +use Magento\Catalog\Model\Product; use Magento\Framework\TestFramework\Unit\Helper\ObjectManager; -use Magento\Catalog\Api\Data\ProductInterface; use Magento\Catalog\Model\ResourceModel\Product\LinkedProductSelectBuilderInterface; use Magento\Store\Model\StoreManagerInterface; use Magento\Store\Api\Data\StoreInterface; @@ -102,9 +102,11 @@ protected function setUp() public function testGetProducts() { $productId = 1; + $storeId = 1; $linkedProducts = ['some', 'linked', 'products', 'dataobjects']; - $product = $this->getMockBuilder(ProductInterface::class)->disableOriginalConstructor()->getMock(); + $product = $this->createMock(Product::class); $product->expects($this->any())->method('getId')->willReturn($productId); + $product->expects($this->any())->method('getStoreId')->willReturn($storeId); $this->linkedProductSelectBuilder->expects($this->any())->method('build')->with($productId)->willReturn([]); $this->productCollection ->expects($this->once()) diff --git a/app/code/Magento/ConfigurableProduct/Test/Unit/Pricing/Render/TierPriceBoxTest.php b/app/code/Magento/ConfigurableProduct/Test/Unit/Pricing/Render/TierPriceBoxTest.php new file mode 100644 index 0000000000000..95f592daaa06a --- /dev/null +++ b/app/code/Magento/ConfigurableProduct/Test/Unit/Pricing/Render/TierPriceBoxTest.php @@ -0,0 +1,117 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +declare(strict_types=1); + +namespace Magento\ConfigurableProduct\Test\Unit\Pricing\Render; + +use Magento\Catalog\Model\Product; +use Magento\Catalog\Model\Product\Pricing\Renderer\SalableResolverInterface; +use Magento\Catalog\Pricing\Price\MinimalPriceCalculatorInterface; +use Magento\ConfigurableProduct\Pricing\Price\ConfigurableOptionsProviderInterface; +use Magento\ConfigurableProduct\Pricing\Render\TierPriceBox; +use Magento\Framework\Pricing\Price\PriceInterface; +use Magento\Framework\Pricing\PriceInfoInterface; +use Magento\Framework\Pricing\Render\RendererPool; +use Magento\Framework\TestFramework\Unit\Helper\ObjectManager; +use Magento\Framework\View\Element\Template\Context; +use Magento\Msrp\Pricing\Price\MsrpPrice; +use PHPUnit\Framework\MockObject\MockObject; + +class TierPriceBoxTest extends \PHPUnit\Framework\TestCase +{ + /** + * @var Context|MockObject + */ + private $context; + + /** + * @var Product|MockObject + */ + private $saleableItem; + + /** + * @var PriceInterface|MockObject + */ + private $price; + + /** + * @var RendererPool|MockObject + */ + private $rendererPool; + + /** + * @var SalableResolverInterface|MockObject + */ + private $salableResolver; + + /** + * @var MinimalPriceCalculatorInterface|MockObject + */ + private $minimalPriceCalculator; + + /** + * @var ConfigurableOptionsProviderInterface|MockObject + */ + private $configurableOptionsProvider; + + /** + * @var TierPriceBox + */ + private $model; + + /** + * @inheritDoc + */ + protected function setUp(): void + { + $this->context = $this->createPartialMock(Context::class, []); + $this->saleableItem = $this->createPartialMock(Product::class, ['getPriceInfo']); + $this->price = $this->createMock(PriceInterface::class); + $this->rendererPool = $this->createPartialMock(RendererPool::class, []); + $this->salableResolver = $this->createPartialMock(SalableResolverInterface::class, ['isSalable']); + $this->minimalPriceCalculator = $this->createMock(MinimalPriceCalculatorInterface::class); + $this->configurableOptionsProvider = $this->createMock(ConfigurableOptionsProviderInterface::class); + + $this->model = (new ObjectManager($this))->getObject( + TierPriceBox::class, + [ + 'context' => $this->context, + 'saleableItem' => $this->saleableItem, + 'price' => $this->price, + 'rendererPool' => $this->rendererPool, + 'salableResolver' => $this->salableResolver, + 'minimalPriceCalculator' => $this->minimalPriceCalculator, + 'configurableOptionsProvider' => $this->configurableOptionsProvider, + ] + ); + } + + public function testToHtmlEmptyWhenMsrpPriceIsApplicable(): void + { + $msrpPriceMock = $this->createPartialMock( + MsrpPrice::class, + ['canApplyMsrp', 'isMinimalPriceLessMsrp'] + ); + $msrpPriceMock->expects($this->once()) + ->method('canApplyMsrp') + ->willReturn(true); + $msrpPriceMock->expects($this->once()) + ->method('isMinimalPriceLessMsrp') + ->willReturn(true); + + $priceInfoMock = $this->createMock(PriceInfoInterface::class); + $priceInfoMock->expects($this->once()) + ->method('getPrice') + ->willReturn($msrpPriceMock); + + $this->saleableItem->expects($this->once()) + ->method('getPriceInfo') + ->willReturn($priceInfoMock); + + $result = $this->model->toHtml(); + $this->assertSame('', $result); + } +} diff --git a/app/code/Magento/ConfigurableProduct/Ui/DataProvider/Product/Form/Modifier/ConfigurablePanel.php b/app/code/Magento/ConfigurableProduct/Ui/DataProvider/Product/Form/Modifier/ConfigurablePanel.php index e0cc83922e03e..16119dcb5c866 100644 --- a/app/code/Magento/ConfigurableProduct/Ui/DataProvider/Product/Form/Modifier/ConfigurablePanel.php +++ b/app/code/Magento/ConfigurableProduct/Ui/DataProvider/Product/Form/Modifier/ConfigurablePanel.php @@ -447,7 +447,7 @@ protected function getRows() 'smallImage' => '${$.provider}:${$.parentScope}.small_image', ], 'uploaderConfig' => [ - 'url' => $this->urlBuilder->addSessionParam()->getUrl( + 'url' => $this->urlBuilder->getUrl( 'catalog/product_gallery/upload' ), ], diff --git a/app/code/Magento/ConfigurableProduct/Ui/DataProvider/Product/Form/Modifier/ConfigurableQty.php b/app/code/Magento/ConfigurableProduct/Ui/DataProvider/Product/Form/Modifier/ConfigurableQty.php index ade56edeb3dfc..6eb65efa2ecae 100644 --- a/app/code/Magento/ConfigurableProduct/Ui/DataProvider/Product/Form/Modifier/ConfigurableQty.php +++ b/app/code/Magento/ConfigurableProduct/Ui/DataProvider/Product/Form/Modifier/ConfigurableQty.php @@ -41,10 +41,8 @@ public function modifyMeta(array $meta) 'arguments' => [ 'data' => [ 'config' => [ - 'imports' => [ - 'disabled' => '!ns = ${ $.ns }, index = ' - . ConfigurablePanel::CONFIGURABLE_MATRIX . ':isEmpty', - ], + 'component' => 'Magento_ConfigurableProduct/js/' . + 'components/qty-configurable' ], ], ], diff --git a/app/code/Magento/ConfigurableProduct/Ui/DataProvider/Product/Form/Modifier/StockData.php b/app/code/Magento/ConfigurableProduct/Ui/DataProvider/Product/Form/Modifier/StockData.php index 58b58b11c1b1c..db51e7eebefc4 100644 --- a/app/code/Magento/ConfigurableProduct/Ui/DataProvider/Product/Form/Modifier/StockData.php +++ b/app/code/Magento/ConfigurableProduct/Ui/DataProvider/Product/Form/Modifier/StockData.php @@ -28,7 +28,7 @@ public function __construct(LocatorInterface $locator) } /** - * {@inheritdoc} + * @inheritdoc */ public function modifyData(array $data) { @@ -36,7 +36,7 @@ public function modifyData(array $data) } /** - * {@inheritdoc} + * @inheritdoc */ public function modifyMeta(array $meta) { @@ -72,15 +72,7 @@ public function modifyMeta(array $meta) ], ]; - $meta['advanced_inventory_modal'] = [ - 'children' => [ - 'stock_data' => [ - 'children' => [ - 'qty' => $config, - ], - ], - ], - ]; + $meta['advanced_inventory_modal']['children']['stock_data']['children']['qty'] = $config; } return $meta; diff --git a/app/code/Magento/ConfigurableProduct/registration.php b/app/code/Magento/ConfigurableProduct/registration.php index fdb3fba0c9912..80572b8609ec6 100644 --- a/app/code/Magento/ConfigurableProduct/registration.php +++ b/app/code/Magento/ConfigurableProduct/registration.php @@ -4,6 +4,6 @@ * See COPYING.txt for license details. */ -use \Magento\Framework\Component\ComponentRegistrar; +use Magento\Framework\Component\ComponentRegistrar; ComponentRegistrar::register(ComponentRegistrar::MODULE, 'Magento_ConfigurableProduct', __DIR__); diff --git a/app/code/Magento/ConfigurableProduct/view/adminhtml/web/js/components/dynamic-rows-configurable.js b/app/code/Magento/ConfigurableProduct/view/adminhtml/web/js/components/dynamic-rows-configurable.js index 7b04bebd4d73a..4624f07323d59 100644 --- a/app/code/Magento/ConfigurableProduct/view/adminhtml/web/js/components/dynamic-rows-configurable.js +++ b/app/code/Magento/ConfigurableProduct/view/adminhtml/web/js/components/dynamic-rows-configurable.js @@ -219,7 +219,7 @@ define([ _.each(tmpData, function (row, index) { path = this.dataScope + '.' + this.index + '.' + (this.startIndex + index); row.attributes = $('<i></i>').text(row.attributes).html(); - row.sku = $('<i></i>').text(row.sku).html(); + row.sku = row.sku; this.source.set(path, row); }, this); @@ -405,7 +405,7 @@ define([ 'id': row.productId, 'product_link': row.productUrl, 'name': $('<i></i>').text(row.name).html(), - 'sku': $('<i></i>').text(row.sku).html(), + 'sku': row.sku, 'status': row.status, 'price': row.price, 'price_currency': row.priceCurrency, diff --git a/app/code/Magento/ConfigurableProduct/view/adminhtml/web/js/components/qty-configurable.js b/app/code/Magento/ConfigurableProduct/view/adminhtml/web/js/components/qty-configurable.js new file mode 100644 index 0000000000000..6b3840bdec450 --- /dev/null +++ b/app/code/Magento/ConfigurableProduct/view/adminhtml/web/js/components/qty-configurable.js @@ -0,0 +1,45 @@ +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +define([ + 'Magento_Ui/js/form/element/abstract' +], function (Abstract) { + 'use strict'; + + return Abstract.extend({ + defaults: { + imports: { + isConfigurable: '!ns = ${ $.ns }, index = configurable-matrix:isEmpty' + } + }, + + /** @inheritdoc */ + initialize: function () { + this._super(); + // resolve initial disable state + this.handleQtyValue(this.isConfigurable); + + /** important to set this listener in initialize because of a different order of processing. + * Do not move to defaults->listens section */ + this.setListeners({ + isConfigurable: 'handleQtyValue' + }); + + return this; + }, + + /** + * Disable and clear Qty if product type changed to configurable + * + * @param {String} isConfigurable + */ + handleQtyValue: function (isConfigurable) { + this.disabled(!!this.isUseDefault() || isConfigurable); + + if (isConfigurable) { + this.clear(); + } + } + }); +}); diff --git a/app/code/Magento/ConfigurableProduct/view/frontend/requirejs-config.js b/app/code/Magento/ConfigurableProduct/view/frontend/requirejs-config.js index c25096121be5d..b2208686e7fab 100644 --- a/app/code/Magento/ConfigurableProduct/view/frontend/requirejs-config.js +++ b/app/code/Magento/ConfigurableProduct/view/frontend/requirejs-config.js @@ -8,5 +8,12 @@ var config = { '*': { configurable: 'Magento_ConfigurableProduct/js/configurable' } + }, + config: { + mixins: { + 'Magento_Catalog/js/catalog-add-to-cart': { + 'Magento_ConfigurableProduct/js/catalog-add-to-cart-mixin': true + } + } } }; diff --git a/app/code/Magento/ConfigurableProduct/view/frontend/web/js/catalog-add-to-cart-mixin.js b/app/code/Magento/ConfigurableProduct/view/frontend/web/js/catalog-add-to-cart-mixin.js new file mode 100644 index 0000000000000..88bb2ab75dc1d --- /dev/null +++ b/app/code/Magento/ConfigurableProduct/view/frontend/web/js/catalog-add-to-cart-mixin.js @@ -0,0 +1,33 @@ +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +define([ + 'underscore', + 'jquery', + 'Magento_ConfigurableProduct/js/product/view/product-info-resolver' +], function (_, $, productInfoResolver) { + 'use strict'; + + return function (widget) { + + $.widget('mage.catalogAddToCart', widget, { + /** + * @param {jQuery} form + */ + ajaxSubmit: function (form) { + var isConfigurable = !!_.find(form.serializeArray(), function (item) { + return item.name.indexOf('super_attribute') !== -1; + }); + + if (isConfigurable) { + this.options.productInfoResolver = productInfoResolver; + } + + return this._super(form); + } + }); + + return $.mage.catalogAddToCart; + }; +}); diff --git a/app/code/Magento/ConfigurableProduct/view/frontend/web/js/configurable.js b/app/code/Magento/ConfigurableProduct/view/frontend/web/js/configurable.js index faacbd03f20b0..8fd1d761040d7 100644 --- a/app/code/Magento/ConfigurableProduct/view/frontend/web/js/configurable.js +++ b/app/code/Magento/ConfigurableProduct/view/frontend/web/js/configurable.js @@ -13,7 +13,8 @@ define([ 'priceUtils', 'priceBox', 'jquery-ui-modules/widget', - 'jquery/jquery.parsequery' + 'jquery/jquery.parsequery', + 'fotoramaVideoEvents' ], function ($, _, mageTemplate, $t, priceUtils) { 'use strict'; @@ -139,7 +140,12 @@ define([ }); $.each(queryParams, $.proxy(function (key, value) { - this.options.values[key] = value; + if (this.options.spConfig.attributes[key] !== undefined && + _.find(this.options.spConfig.attributes[key].options, function (element) { + return element.id === value; + })) { + this.options.values[key] = value; + } }, this)); }, @@ -155,7 +161,13 @@ define([ if (element.value) { attributeId = element.id.replace(/[a-z]*/, ''); - this.options.values[attributeId] = element.value; + + if (this.options.spConfig.attributes[attributeId] !== undefined && + _.find(this.options.spConfig.attributes[attributeId].options, function (optionElement) { + return optionElement.id === element.value; + })) { + this.options.values[attributeId] = element.value; + } } }, this)); }, @@ -296,9 +308,13 @@ define([ _changeProductImage: function () { var images, initialImages = this.options.mediaGalleryInitial, - galleryObject = $(this.options.mediaGallerySelector).data('gallery'); + gallery = $(this.options.mediaGallerySelector).data('gallery'); + + if (_.isUndefined(gallery)) { + $(this.options.mediaGallerySelector).on('gallery:loaded', function () { + this._changeProductImage(); + }.bind(this)); - if (!galleryObject) { return; } @@ -314,17 +330,35 @@ define([ images = $.extend(true, [], images); images = this._setImageIndex(images); - galleryObject.updateData(images); - - $(this.options.mediaGallerySelector).AddFotoramaVideoEvents({ - selectedOption: this.simpleProduct, - dataMergeStrategy: this.options.gallerySwitchStrategy - }); + gallery.updateData(images); + this._addFotoramaVideoEvents(false); } else { - galleryObject.updateData(initialImages); + gallery.updateData(initialImages); + this._addFotoramaVideoEvents(true); + } + }, + + /** + * Add video events + * + * @param {Boolean} isInitial + * @private + */ + _addFotoramaVideoEvents: function (isInitial) { + if (_.isUndefined($.mage.AddFotoramaVideoEvents)) { + return; + } + + if (isInitial) { $(this.options.mediaGallerySelector).AddFotoramaVideoEvents(); + + return; } + $(this.options.mediaGallerySelector).AddFotoramaVideoEvents({ + selectedOption: this.simpleProduct, + dataMergeStrategy: this.options.gallerySwitchStrategy + }); }, /** diff --git a/app/code/Magento/ConfigurableProduct/view/frontend/web/js/product/view/product-info-resolver.js b/app/code/Magento/ConfigurableProduct/view/frontend/web/js/product/view/product-info-resolver.js new file mode 100644 index 0000000000000..679632606c72d --- /dev/null +++ b/app/code/Magento/ConfigurableProduct/view/frontend/web/js/product/view/product-info-resolver.js @@ -0,0 +1,47 @@ +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +define([ + 'underscore', + 'Magento_Catalog/js/product/view/product-info' +], function (_, productInfo) { + 'use strict'; + + /** + * Returns info about configurable products in form. + * + * @param {jQuery} $form + * @return {Array} + */ + return function ($form) { + var optionValues = [], + product = _.findWhere($form.serializeArray(), { + name: 'product' + }), + productId; + + if (!_.isUndefined(product)) { + productId = product.value; + _.each($form.serializeArray(), function (item) { + if (item.name.indexOf('super_attribute') !== -1) { + optionValues.push(item.value); + } + }); + optionValues.sort(); + productInfo().push( + { + 'id': productId, + 'optionValues': optionValues + } + ); + } + + return _.uniq(productInfo(), function (item) { + var optionValuesStr = item.optionValues ? item.optionValues.join() : ''; + + return item.id + optionValuesStr; + }); + }; +}); + diff --git a/app/code/Magento/ConfigurableProductSales/registration.php b/app/code/Magento/ConfigurableProductSales/registration.php index 99affe39c15c1..a6d88cff482e4 100644 --- a/app/code/Magento/ConfigurableProductSales/registration.php +++ b/app/code/Magento/ConfigurableProductSales/registration.php @@ -4,6 +4,6 @@ * See COPYING.txt for license details. */ -use \Magento\Framework\Component\ComponentRegistrar; +use Magento\Framework\Component\ComponentRegistrar; ComponentRegistrar::register(ComponentRegistrar::MODULE, 'Magento_ConfigurableProductSales', __DIR__); diff --git a/app/code/Magento/Contact/Test/Mftf/Test/StorefrontVerifySecureURLRedirectContactTest.xml b/app/code/Magento/Contact/Test/Mftf/Test/StorefrontVerifySecureURLRedirectContactTest.xml index 3ef941fa2e0ce..0c46ed4729d66 100644 --- a/app/code/Magento/Contact/Test/Mftf/Test/StorefrontVerifySecureURLRedirectContactTest.xml +++ b/app/code/Magento/Contact/Test/Mftf/Test/StorefrontVerifySecureURLRedirectContactTest.xml @@ -8,7 +8,7 @@ <tests xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/testSchema.xsd"> - <test name="StorefrontVerifySecureURLRedirectContact"> + <test name="StorefrontVerifySecureURLRedirectContactTest"> <annotations> <features value="Contact"/> <stories value="Storefront Secure URLs"/> diff --git a/app/code/Magento/Contact/etc/adminhtml/system.xml b/app/code/Magento/Contact/etc/adminhtml/system.xml index b5974e4d42e84..c1437c7778b79 100644 --- a/app/code/Magento/Contact/etc/adminhtml/system.xml +++ b/app/code/Magento/Contact/etc/adminhtml/system.xml @@ -24,15 +24,24 @@ <field id="recipient_email" translate="label" type="text" sortOrder="10" showInDefault="1" showInWebsite="1" showInStore="1" canRestore="1"> <label>Send Emails To</label> <validate>validate-email</validate> + <depends> + <field id="*/contact/enabled">1</field> + </depends> </field> <field id="sender_email_identity" translate="label" type="select" sortOrder="20" showInDefault="1" showInWebsite="1" showInStore="1" canRestore="1"> <label>Email Sender</label> <source_model>Magento\Config\Model\Config\Source\Email\Identity</source_model> + <depends> + <field id="*/contact/enabled">1</field> + </depends> </field> <field id="email_template" translate="label comment" type="select" sortOrder="30" showInDefault="1" showInWebsite="1" showInStore="1" canRestore="1"> <label>Email Template</label> <comment>Email template chosen based on theme fallback when "Default" option is selected.</comment> <source_model>Magento\Config\Model\Config\Source\Email\Template</source_model> + <depends> + <field id="*/contact/enabled">1</field> + </depends> </field> </group> </section> diff --git a/app/code/Magento/Contact/registration.php b/app/code/Magento/Contact/registration.php index b280b2e0b81a2..0d8b36f51eda5 100644 --- a/app/code/Magento/Contact/registration.php +++ b/app/code/Magento/Contact/registration.php @@ -4,6 +4,6 @@ * See COPYING.txt for license details. */ -use \Magento\Framework\Component\ComponentRegistrar; +use Magento\Framework\Component\ComponentRegistrar; ComponentRegistrar::register(ComponentRegistrar::MODULE, 'Magento_Contact', __DIR__); diff --git a/app/code/Magento/Contact/view/frontend/templates/form.phtml b/app/code/Magento/Contact/view/frontend/templates/form.phtml index 3a7c4c8b6d865..d218e650657ac 100644 --- a/app/code/Magento/Contact/view/frontend/templates/form.phtml +++ b/app/code/Magento/Contact/view/frontend/templates/form.phtml @@ -52,7 +52,7 @@ $viewModel = $block->getViewModel(); title="<?= $block->escapeHtmlAttr(__('Phone Number')) ?>" value="<?= $block->escapeHtmlAttr($viewModel->getUserTelephone()) ?>" class="input-text" - type="text" /> + type="tel" /> </div> </div> <div class="field comment required"> diff --git a/app/code/Magento/Cookie/etc/adminhtml/system.xml b/app/code/Magento/Cookie/etc/adminhtml/system.xml index 9790410969055..d1dcbf45ae5be 100644 --- a/app/code/Magento/Cookie/etc/adminhtml/system.xml +++ b/app/code/Magento/Cookie/etc/adminhtml/system.xml @@ -29,7 +29,7 @@ </comment> <source_model>Magento\Config\Model\Config\Source\Yesno</source_model> </field> - <field id="cookie_restriction" translate="label" type="select" sortOrder="50" showInDefault="1" showInWebsite="1" showInStore="0" canRestore="1"> + <field id="cookie_restriction" translate="label" type="select" sortOrder="50" showInDefault="1" showInWebsite="1" canRestore="1"> <label>Cookie Restriction Mode</label> <source_model>Magento\Config\Model\Config\Source\Yesno</source_model> <backend_model>Magento\Cookie\Model\Config\Backend\Cookie</backend_model> diff --git a/app/code/Magento/Cookie/registration.php b/app/code/Magento/Cookie/registration.php index 64f13f19968ce..5b5b7b1f3c96f 100644 --- a/app/code/Magento/Cookie/registration.php +++ b/app/code/Magento/Cookie/registration.php @@ -4,6 +4,6 @@ * See COPYING.txt for license details. */ -use \Magento\Framework\Component\ComponentRegistrar; +use Magento\Framework\Component\ComponentRegistrar; ComponentRegistrar::register(ComponentRegistrar::MODULE, 'Magento_Cookie', __DIR__); diff --git a/app/code/Magento/Cookie/view/frontend/web/js/notices.js b/app/code/Magento/Cookie/view/frontend/web/js/notices.js index 2c4a070130804..d61f8f9d32523 100644 --- a/app/code/Magento/Cookie/view/frontend/web/js/notices.js +++ b/app/code/Magento/Cookie/view/frontend/web/js/notices.js @@ -30,6 +30,7 @@ define([ if ($.mage.cookies.get(this.options.cookieName)) { this.element.hide(); + $(document).trigger('user:allowed:save:cookie'); } else { window.location.href = this.options.noCookiesUrl; } diff --git a/app/code/Magento/Cron/Observer/ProcessCronQueueObserver.php b/app/code/Magento/Cron/Observer/ProcessCronQueueObserver.php index 5c8aa1dc78abd..053ba43c1c20e 100644 --- a/app/code/Magento/Cron/Observer/ProcessCronQueueObserver.php +++ b/app/code/Magento/Cron/Observer/ProcessCronQueueObserver.php @@ -3,9 +3,11 @@ * Copyright © Magento, Inc. All rights reserved. * See COPYING.txt for license details. */ + /** * Handling cron jobs */ + namespace Magento\Cron\Observer; use Magento\Cron\Model\Schedule; @@ -69,6 +71,11 @@ class ProcessCronQueueObserver implements ObserverInterface */ const LOCK_PREFIX = 'CRON_GROUP_'; + /** + * Cron Job name pattern for Profiling + */ + const CRON_TIMERID = 'job %s'; + /** * @var \Magento\Cron\Model\ResourceModel\Schedule\Collection */ @@ -311,7 +318,7 @@ protected function _runJob($scheduledTime, $currentTime, $jobConfig, $schedule, $schedule->setExecutedAt(strftime('%Y-%m-%d %H:%M:%S', $this->dateTime->gmtTimestamp()))->save(); - $this->startProfiling(); + $this->startProfiling($jobCode); try { $this->logger->info(sprintf('Cron Job %s is run', $jobCode)); //phpcs:ignore Magento2.Functions.DiscouragedFunction @@ -323,7 +330,7 @@ protected function _runJob($scheduledTime, $currentTime, $jobConfig, $schedule, 'Cron Job %s has an error: %s. Statistics: %s', $jobCode, $e->getMessage(), - $this->getProfilingStat() + $this->getProfilingStat($jobCode) ) ); if (!$e instanceof \Exception) { @@ -335,7 +342,7 @@ protected function _runJob($scheduledTime, $currentTime, $jobConfig, $schedule, } throw $e; } finally { - $this->stopProfiling(); + $this->stopProfiling($jobCode); } $schedule->setStatus( @@ -351,7 +358,7 @@ protected function _runJob($scheduledTime, $currentTime, $jobConfig, $schedule, sprintf( 'Cron Job %s is successfully finished. Statistics: %s', $jobCode, - $this->getProfilingStat() + $this->getProfilingStat($jobCode) ) ); } @@ -359,32 +366,47 @@ protected function _runJob($scheduledTime, $currentTime, $jobConfig, $schedule, /** * Starts profiling * + * @param string $jobName * @return void */ - private function startProfiling() + private function startProfiling(string $jobName = '') { $this->statProfiler->clear(); - $this->statProfiler->start('job', microtime(true), memory_get_usage(true), memory_get_usage()); + $this->statProfiler->start( + sprintf(self::CRON_TIMERID, $jobName), + microtime(true), + memory_get_usage(true), + memory_get_usage() + ); } /** * Stops profiling * + * @param string $jobName * @return void */ - private function stopProfiling() + private function stopProfiling(string $jobName = '') { - $this->statProfiler->stop('job', microtime(true), memory_get_usage(true), memory_get_usage()); + $this->statProfiler->stop( + sprintf(self::CRON_TIMERID, $jobName), + microtime(true), + memory_get_usage(true), + memory_get_usage() + ); } /** * Retrieves statistics in the JSON format * + * @param string $jobName * @return string */ - private function getProfilingStat() + private function getProfilingStat(string $jobName): string { - $stat = $this->statProfiler->get('job'); + $stat = $this->statProfiler->get( + sprintf(self::CRON_TIMERID, $jobName) + ); unset($stat[Stat::START]); return json_encode($stat); } @@ -418,7 +440,9 @@ private function getNonExitedSchedules($groupId) 'status', [ 'in' => [ - Schedule::STATUS_PENDING, Schedule::STATUS_RUNNING, Schedule::STATUS_SUCCESS + Schedule::STATUS_PENDING, + Schedule::STATUS_RUNNING, + Schedule::STATUS_SUCCESS ] ] ); @@ -478,10 +502,10 @@ private function generateSchedules($groupId) /** * Generate jobs for config information * - * @param array $jobs - * @param array $exists - * @param string $groupId - * @return void + * @param array $jobs + * @param array $exists + * @param string $groupId + * @return void */ protected function _generateJobs($jobs, $exists, $groupId) { diff --git a/app/code/Magento/Cron/etc/adminhtml/system.xml b/app/code/Magento/Cron/etc/adminhtml/system.xml index c8753f1b0b56f..2bd6611e57473 100644 --- a/app/code/Magento/Cron/etc/adminhtml/system.xml +++ b/app/code/Magento/Cron/etc/adminhtml/system.xml @@ -8,36 +8,36 @@ <config xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="urn:magento:module:Magento_Config:etc/system_file.xsd"> <system> <section id="system"> - <group id="cron" translate="label comment" type="text" sortOrder="15" showInDefault="1" showInWebsite="0" showInStore="0"> + <group id="cron" translate="label comment" type="text" sortOrder="15" showInDefault="1"> <label>Cron (Scheduled Tasks)</label> <comment>For correct URLs generated during cron runs please make sure that Web > Secure and Unsecure Base URLs are explicitly set. All the times are in minutes.</comment> - <group id="template" translate="label" type="text" sortOrder="10" showInDefault="1" showInWebsite="0" showInStore="0"> - <label>Cron configuration options for group: </label> - <field id="schedule_generate_every" translate="label" type="text" sortOrder="10" showInDefault="1" showInWebsite="0" showInStore="0" canRestore="1"> + <group id="template" translate="label" type="text" sortOrder="10" showInDefault="1"> + <label>Cron configuration options for group:</label> + <field id="schedule_generate_every" translate="label" type="text" sortOrder="10" showInDefault="1" canRestore="1"> <label>Generate Schedules Every</label> <validate>validate-zero-or-greater validate-digits</validate> </field> - <field id="schedule_ahead_for" translate="label" type="text" sortOrder="20" showInDefault="1" showInWebsite="0" showInStore="0" canRestore="1"> + <field id="schedule_ahead_for" translate="label" type="text" sortOrder="20" showInDefault="1" canRestore="1"> <label>Schedule Ahead for</label> <validate>validate-zero-or-greater validate-digits</validate> </field> - <field id="schedule_lifetime" translate="label" type="text" sortOrder="30" showInDefault="1" showInWebsite="0" showInStore="0" canRestore="1"> + <field id="schedule_lifetime" translate="label" type="text" sortOrder="30" showInDefault="1" canRestore="1"> <label>Missed if Not Run Within</label> <validate>validate-zero-or-greater validate-digits</validate> </field> - <field id="history_cleanup_every" translate="label" type="text" sortOrder="40" showInDefault="1" showInWebsite="0" showInStore="0" canRestore="1"> + <field id="history_cleanup_every" translate="label" type="text" sortOrder="40" showInDefault="1" canRestore="1"> <label>History Cleanup Every</label> <validate>validate-zero-or-greater validate-digits</validate> </field> - <field id="history_success_lifetime" translate="label" type="text" sortOrder="50" showInDefault="1" showInWebsite="0" showInStore="0" canRestore="1"> + <field id="history_success_lifetime" translate="label" type="text" sortOrder="50" showInDefault="1" canRestore="1"> <label>Success History Lifetime</label> <validate>validate-zero-or-greater validate-digits</validate> </field> - <field id="history_failure_lifetime" translate="label" type="text" sortOrder="60" showInDefault="1" showInWebsite="0" showInStore="0" canRestore="1"> + <field id="history_failure_lifetime" translate="label" type="text" sortOrder="60" showInDefault="1" canRestore="1"> <label>Failure History Lifetime</label> <validate>validate-zero-or-greater validate-digits</validate> </field> - <field id="use_separate_process" translate="label" type="select" sortOrder="70" showInDefault="1" showInWebsite="0" showInStore="0" canRestore="1"> + <field id="use_separate_process" translate="label" type="select" sortOrder="70" showInDefault="1" canRestore="1"> <label>Use Separate Process</label> <source_model>Magento\Config\Model\Config\Source\Yesno</source_model> </field> diff --git a/app/code/Magento/Cron/i18n/en_US.csv b/app/code/Magento/Cron/i18n/en_US.csv index b1969d7723315..df9aef7f13747 100644 --- a/app/code/Magento/Cron/i18n/en_US.csv +++ b/app/code/Magento/Cron/i18n/en_US.csv @@ -11,7 +11,7 @@ Monthly,Monthly "Test exception","Test exception" "Cron (Scheduled Tasks)","Cron (Scheduled Tasks)" "For correct URLs generated during cron runs please make sure that Web > Secure and Unsecure Base URLs are explicitly set. All the times are in minutes.","For correct URLs generated during cron runs please make sure that Web > Secure and Unsecure Base URLs are explicitly set. All the times are in minutes." -"Cron configuration options for group: ","Cron configuration options for group: " +"Cron configuration options for group:","Cron configuration options for group:" "Generate Schedules Every","Generate Schedules Every" "Schedule Ahead for","Schedule Ahead for" "Missed if Not Run Within","Missed if Not Run Within" diff --git a/app/code/Magento/Cron/registration.php b/app/code/Magento/Cron/registration.php index d22298a2efc37..97f162d76ec52 100644 --- a/app/code/Magento/Cron/registration.php +++ b/app/code/Magento/Cron/registration.php @@ -4,6 +4,6 @@ * See COPYING.txt for license details. */ -use \Magento\Framework\Component\ComponentRegistrar; +use Magento\Framework\Component\ComponentRegistrar; ComponentRegistrar::register(ComponentRegistrar::MODULE, 'Magento_Cron', __DIR__); diff --git a/app/code/Magento/Csp/Model/Collector/CspWhitelistXml/SchemaLocator.php b/app/code/Magento/Csp/Model/Collector/CspWhitelistXml/SchemaLocator.php index 285d37a1b6270..ed4d4a6fde51d 100644 --- a/app/code/Magento/Csp/Model/Collector/CspWhitelistXml/SchemaLocator.php +++ b/app/code/Magento/Csp/Model/Collector/CspWhitelistXml/SchemaLocator.php @@ -22,7 +22,7 @@ class SchemaLocator implements SchemaLocatorInterface * * @var string */ - private $schema ; + private $schema; /** * @param Reader $moduleReader diff --git a/app/code/Magento/Csp/Test/Unit/Model/Mode/ConfigManagerTest.php b/app/code/Magento/Csp/Test/Unit/Model/Mode/ConfigManagerTest.php new file mode 100644 index 0000000000000..a41c03b512fc5 --- /dev/null +++ b/app/code/Magento/Csp/Test/Unit/Model/Mode/ConfigManagerTest.php @@ -0,0 +1,105 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ + +declare(strict_types=1); + +namespace Magento\Csp\Test\Unit\Model\Mode; + +use Magento\Csp\Model\Mode\ConfigManager; +use Magento\Csp\Model\Mode\Data\ModeConfigured; +use Magento\Framework\App\Area; +use Magento\Framework\App\Config\ScopeConfigInterface; +use Magento\Framework\App\State; +use Magento\Framework\TestFramework\Unit\Helper\ObjectManager; +use Magento\Store\Model\Store; +use PHPUnit\Framework\MockObject\MockObject; +use PHPUnit\Framework\TestCase; +use RuntimeException; + +/** + * Test for \Magento\Csp\Model\Mode\ConfigManager + */ +class ConfigManagerTest extends TestCase +{ + /** + * @var ConfigManager + */ + private $model; + + /** + * @var ScopeConfigInterface|MockObject + */ + private $scopeConfigMock; + + /** + * @var Store|MockObject + */ + private $storeMock; + + /** + * @var State|MockObject + */ + private $stateMock; + + /** + * Set Up + */ + protected function setUp() + { + $objectManager = new ObjectManager($this); + + $this->scopeConfigMock = $this->createMock(ScopeConfigInterface::class); + $this->storeMock = $this->createMock(Store::class); + $this->stateMock = $this->createMock(State::class); + + $this->model = $objectManager->getObject( + ConfigManager::class, + [ + 'config' => $this->scopeConfigMock, + 'storeModel' => $this->storeMock, + 'state' => $this->stateMock + ] + ); + } + + /** + * Test throwing an exception for non storefront or admin areas + * + * @return void + */ + public function testThrownExceptionForCrontabArea() + { + $this->stateMock->expects($this->any()) + ->method('getAreaCode') + ->willReturn(Area::AREA_CRONTAB); + + $this->expectExceptionMessage('CSP can only be configured for storefront or admin area'); + $this->expectException(RuntimeException::class); + + $this->model->getConfigured(); + } + + /** + * Test returning the configured CSP for admin area + * + * @return void + */ + public function testConfiguredCSPForAdminArea() + { + $this->stateMock->expects($this->any()) + ->method('getAreaCode') + ->willReturn(Area::AREA_ADMINHTML); + $this->scopeConfigMock->expects($this->any()) + ->method('isSetFlag') + ->willReturn(true); + $this->scopeConfigMock->expects($this->any()) + ->method('getValue') + ->willReturn('testReportUri'); + $result = $this->model->getConfigured(); + + $this->assertInstanceOf(ModeConfigured::class, $result); + } +} diff --git a/app/code/Magento/Csp/Test/Unit/Model/PolicyRendererPoolTest.php b/app/code/Magento/Csp/Test/Unit/Model/PolicyRendererPoolTest.php new file mode 100644 index 0000000000000..84ee8584ebf59 --- /dev/null +++ b/app/code/Magento/Csp/Test/Unit/Model/PolicyRendererPoolTest.php @@ -0,0 +1,94 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ + +declare(strict_types=1); + +namespace Magento\Csp\Test\Unit\Model; + +use Magento\Csp\Api\Data\PolicyInterface; +use Magento\Csp\Model\Policy\Renderer\SimplePolicyHeaderRenderer; +use Magento\Csp\Model\PolicyRendererPool; +use Magento\Framework\TestFramework\Unit\Helper\ObjectManager; +use PHPUnit\Framework\MockObject\MockObject; +use PHPUnit\Framework\TestCase; +use RuntimeException; + +/** + * Test for \Magento\Csp\Model\PolicyRendererPool + */ +class PolicyRendererPoolTest extends TestCase +{ + private const STUB_POLICY_ID = 'header'; + + /** + * @var PolicyRendererPool + */ + private $model; + + /** + * @var SimplePolicyHeaderRenderer|MockObject + */ + private $simplePolicyHeaderRendererMock; + + /** + * @var PolicyInterface|MockObject + */ + private $policyMock; + + /** + * Set Up + */ + protected function setUp() + { + $objectManager = new ObjectManager($this); + $this->simplePolicyHeaderRendererMock = $this->createPartialMock( + SimplePolicyHeaderRenderer::class, + ['canRender'] + ); + $this->policyMock = $this->createMock(PolicyInterface::class); + + $this->model = $objectManager->getObject( + PolicyRendererPool::class, + [ + 'renderers' => [ + $this->simplePolicyHeaderRendererMock + ] + ] + ); + } + + /** + * Test throwing an exception for not found policy renders + * + * @return void + */ + public function testThrownExceptionForNotFoundPolicyRenders() + { + $this->policyMock->expects($this->any()) + ->method('getId') + ->willReturn(static::STUB_POLICY_ID); + + $this->expectExceptionMessage('Failed to find a renderer for policy'); + $this->expectException(RuntimeException::class); + + $this->model->getRenderer($this->policyMock); + } + + /** + * Test returning a renderer for the given policy + * + * @return void + */ + public function testReturningThePolicyRender() + { + $this->simplePolicyHeaderRendererMock->expects($this->any()) + ->method('canRender') + ->with($this->policyMock) + ->willReturn(true); + + $this->model->getRenderer($this->policyMock); + } +} diff --git a/app/code/Magento/Csp/Test/Unit/Observer/RenderTest.php b/app/code/Magento/Csp/Test/Unit/Observer/RenderTest.php new file mode 100644 index 0000000000000..8c42a4d051bde --- /dev/null +++ b/app/code/Magento/Csp/Test/Unit/Observer/RenderTest.php @@ -0,0 +1,52 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ + +declare(strict_types=1); + +namespace Magento\Csp\Test\Unit\Observer; + +use Magento\Csp\Api\CspRendererInterface; +use Magento\Csp\Observer\Render; +use Magento\Framework\App\Response\Http as ResponseHttp; +use Magento\Framework\Event; +use Magento\Framework\Event\Observer; +use Magento\Framework\TestFramework\Unit\Helper\ObjectManager; +use PHPUnit\Framework\MockObject\MockObject; +use PHPUnit\Framework\TestCase; + +/** + * Unit Test for \Magento\Csp\Observer\Render + */ +class RenderTest extends TestCase +{ + /** + * Check if the render method is called + */ + public function testExecuteExpectsRenderCalled() + { + $eventMock = $this->createMock(Event::class); + $responseMock = $this->createMock(ResponseHttp::class); + $eventMock->expects($this->once()) + ->method('getData') + ->with('response') + ->willReturn($responseMock); + + /** @var MockObject|Observer $eventObserverMock */ + $eventObserverMock = $this->createMock(Observer::class); + $eventObserverMock->expects($this->once())->method('getEvent')->willReturn($eventMock); + + $cspRendererMock = $this->createMock(CspRendererInterface::class); + $cspRendererMock->expects($this->once())->method('render'); + + $objectManagerHelper = new ObjectManager($this); + /** @var Render $renderObserver */ + $renderObserver = $objectManagerHelper->getObject( + Render::class, + ['cspRenderer' => $cspRendererMock] + ); + $renderObserver->execute($eventObserverMock); + } +} diff --git a/app/code/Magento/Csp/registration.php b/app/code/Magento/Csp/registration.php index 90f4a25452858..337cfbc23f2de 100644 --- a/app/code/Magento/Csp/registration.php +++ b/app/code/Magento/Csp/registration.php @@ -4,6 +4,6 @@ * See COPYING.txt for license details. */ -use \Magento\Framework\Component\ComponentRegistrar; +use Magento\Framework\Component\ComponentRegistrar; ComponentRegistrar::register(ComponentRegistrar::MODULE, 'Magento_Csp', __DIR__); diff --git a/app/code/Magento/CurrencySymbol/Controller/Adminhtml/System/Currencysymbol/Save.php b/app/code/Magento/CurrencySymbol/Controller/Adminhtml/System/Currencysymbol/Save.php index 07c7c553ac792..f4d69096475d5 100644 --- a/app/code/Magento/CurrencySymbol/Controller/Adminhtml/System/Currencysymbol/Save.php +++ b/app/code/Magento/CurrencySymbol/Controller/Adminhtml/System/Currencysymbol/Save.php @@ -67,6 +67,6 @@ public function execute() $this->messageManager->addErrorMessage($e->getMessage()); } - return $resultRedirect->setPath('*'); + return $resultRedirect->setPath('adminhtml/*/'); } } diff --git a/app/code/Magento/CurrencySymbol/Model/System/Currencysymbol.php b/app/code/Magento/CurrencySymbol/Model/System/Currencysymbol.php index 6c7019986cce0..d48df02d9de27 100644 --- a/app/code/Magento/CurrencySymbol/Model/System/Currencysymbol.php +++ b/app/code/Magento/CurrencySymbol/Model/System/Currencysymbol.php @@ -292,7 +292,8 @@ protected function _unserializeStoreConfig($configPath, $storeId = null) */ protected function getAllowedCurrencies() { - $allowedCurrencies = explode( + $allowedCurrencies = [[]]; + $allowedCurrencies[] = explode( self::ALLOWED_CURRENCIES_CONFIG_SEPARATOR, $this->_scopeConfig->getValue( self::XML_PATH_ALLOWED_CURRENCIES, @@ -318,23 +319,17 @@ protected function getAllowedCurrencies() if (!$websiteShow) { $websiteShow = true; $websiteSymbols = $website->getConfig(self::XML_PATH_ALLOWED_CURRENCIES); - $allowedCurrencies = array_merge( - $allowedCurrencies, - explode(self::ALLOWED_CURRENCIES_CONFIG_SEPARATOR, $websiteSymbols) - ); + $allowedCurrencies[] = explode(self::ALLOWED_CURRENCIES_CONFIG_SEPARATOR, $websiteSymbols); } $storeSymbols = $this->_scopeConfig->getValue( self::XML_PATH_ALLOWED_CURRENCIES, \Magento\Store\Model\ScopeInterface::SCOPE_STORE, $store ); - $allowedCurrencies = array_merge( - $allowedCurrencies, - explode(self::ALLOWED_CURRENCIES_CONFIG_SEPARATOR, $storeSymbols) - ); + $allowedCurrencies[] = explode(self::ALLOWED_CURRENCIES_CONFIG_SEPARATOR, $storeSymbols); } } } - return array_unique($allowedCurrencies); + return array_unique(array_merge(...$allowedCurrencies)); } } diff --git a/app/code/Magento/CurrencySymbol/Test/Mftf/ActionGroup/AdminNavigateToCurrencySymbolsPageActionGroup.xml b/app/code/Magento/CurrencySymbol/Test/Mftf/ActionGroup/AdminNavigateToCurrencySymbolsPageActionGroup.xml new file mode 100644 index 0000000000000..58854d2ce8162 --- /dev/null +++ b/app/code/Magento/CurrencySymbol/Test/Mftf/ActionGroup/AdminNavigateToCurrencySymbolsPageActionGroup.xml @@ -0,0 +1,15 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + /** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +--> + +<actionGroups xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/actionGroupSchema.xsd"> + <actionGroup name="AdminNavigateToCurrencySymbolsPageActionGroup"> + <amOnPage url="{{AdminCurrencySymbolsPage.url}}" stepKey="navigateToCurrencySymbolsPage"/> + <waitForPageLoad stepKey="waitForPageLoad"/> + </actionGroup> +</actionGroups> diff --git a/app/code/Magento/CurrencySymbol/Test/Mftf/ActionGroup/AssertAdminCurrencySymbolIsDisabledActionGroup.xml b/app/code/Magento/CurrencySymbol/Test/Mftf/ActionGroup/AssertAdminCurrencySymbolIsDisabledActionGroup.xml new file mode 100644 index 0000000000000..bc2314316d8bd --- /dev/null +++ b/app/code/Magento/CurrencySymbol/Test/Mftf/ActionGroup/AssertAdminCurrencySymbolIsDisabledActionGroup.xml @@ -0,0 +1,22 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + /** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +--> + +<actionGroups xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/actionGroupSchema.xsd"> + <actionGroup name="AssertAdminCurrencySymbolIsDisabledActionGroup"> + <arguments> + <argument name="currency" type="string"/> + </arguments> + + <grabAttributeFrom selector="{{AdminCurrencySymbolsGridSection.currencyElement(currency)}}" userInput="disabled" stepKey="grabDisabledAttribute"/> + <assertEquals stepKey="assertInputIsDisabled"> + <actualResult type="const">$grabDisabledAttribute</actualResult> + <expectedResult type="string">true</expectedResult> + </assertEquals> + </actionGroup> +</actionGroups> diff --git a/app/code/Magento/CurrencySymbol/Test/Mftf/Page/AdminCurrencySymbolsPage.xml b/app/code/Magento/CurrencySymbol/Test/Mftf/Page/AdminCurrencySymbolsPage.xml new file mode 100644 index 0000000000000..82c2fa70e301a --- /dev/null +++ b/app/code/Magento/CurrencySymbol/Test/Mftf/Page/AdminCurrencySymbolsPage.xml @@ -0,0 +1,14 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + /** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +--> + +<pages xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Page/etc/PageObject.xsd"> + <page name="AdminCurrencySymbolsPage" url="admin/system_currencysymbol/" area="admin" module="CurrencySymbol"> + <section name="AdminCurrencySymbolsGridSection"/> + </page> +</pages> diff --git a/app/code/Magento/CurrencySymbol/Test/Mftf/Section/AdminCurrencySymbolsGridSection.xml b/app/code/Magento/CurrencySymbol/Test/Mftf/Section/AdminCurrencySymbolsGridSection.xml new file mode 100644 index 0000000000000..bf087d2ed8f4d --- /dev/null +++ b/app/code/Magento/CurrencySymbol/Test/Mftf/Section/AdminCurrencySymbolsGridSection.xml @@ -0,0 +1,14 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + /** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +--> + +<sections xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Page/etc/SectionObject.xsd"> + <section name="AdminCurrencySymbolsGridSection"> + <element name="currencyElement" type="input" selector="#currency-symbols-form #custom_currency_symbol{{currency}}" parameterized="true"/> + </section> +</sections> diff --git a/app/code/Magento/CurrencySymbol/Test/Mftf/Test/AdminCurrencyConverterAPIConfigurationTest.xml b/app/code/Magento/CurrencySymbol/Test/Mftf/Test/AdminCurrencyConverterAPIConfigurationTest.xml index 6232f59bb839a..f7e6e05347345 100644 --- a/app/code/Magento/CurrencySymbol/Test/Mftf/Test/AdminCurrencyConverterAPIConfigurationTest.xml +++ b/app/code/Magento/CurrencySymbol/Test/Mftf/Test/AdminCurrencyConverterAPIConfigurationTest.xml @@ -32,7 +32,7 @@ <createData entity="SimpleProduct" stepKey="createProduct"> <requiredEntity createDataKey="createCategory"/> </createData> - <actionGroup ref="LoginAsAdmin" stepKey="loginAsAdmin"/> + <actionGroup ref="AdminLoginActionGroup" stepKey="loginAsAdmin"/> </before> <after> <!--Set currency allow previous config--> @@ -40,21 +40,21 @@ <!--Delete created data--> <deleteData createDataKey="createProduct" stepKey="deleteProduct"/> <deleteData createDataKey="createCategory" stepKey="deleteCategory"/> - <actionGroup ref="logout" stepKey="logout"/> + <actionGroup ref="AdminLogoutActionGroup" stepKey="logout"/> </after> <!--Import rates from Currency Converter API--> <amOnPage url="{{AdminCurrencyRatesPage.url}}" stepKey="onCurrencyRatePage"/> <actionGroup ref="AdminImportCurrencyRatesActionGroup" stepKey="importCurrencyRates"> <argument name="rateService" value="Currency Converter API"/> </actionGroup> - <see selector="{{AdminMessagesSection.warningMessage}}" userInput='Click "Save" to apply the rates we found.' stepKey="seeImportMessage"/> - <see selector="{{AdminMessagesSection.warningMessage}}" userInput="We can't retrieve a rate from https://free.currconv.com for CHE." stepKey="seeWarningMessageForCHE"/> - <see selector="{{AdminMessagesSection.warningMessage}}" userInput="We can't retrieve a rate from https://free.currconv.com for RHD." stepKey="seeWarningMessageForRHD"/> - <see selector="{{AdminMessagesSection.warningMessage}}" userInput="We can't retrieve a rate from https://free.currconv.com for CHW." stepKey="seeWarningMessageForCHW"/> + <see selector="{{AdminMessagesSection.warning}}" userInput='Click "Save" to apply the rates we found.' stepKey="seeImportMessage"/> + <see selector="{{AdminMessagesSection.warning}}" userInput="We can't retrieve a rate from https://free.currconv.com for CHE." stepKey="seeWarningMessageForCHE"/> + <see selector="{{AdminMessagesSection.warning}}" userInput="We can't retrieve a rate from https://free.currconv.com for RHD." stepKey="seeWarningMessageForRHD"/> + <see selector="{{AdminMessagesSection.warning}}" userInput="We can't retrieve a rate from https://free.currconv.com for CHW." stepKey="seeWarningMessageForCHW"/> <actionGroup ref="AdminSaveCurrencyRatesActionGroup" stepKey="saveCurrencyRates"/> - <see selector="{{AdminMessagesSection.warningMessage}}" userInput='Please correct the input data for "USD => CHE" rate' stepKey="seeCHEMessageAfterSave"/> - <see selector="{{AdminMessagesSection.warningMessage}}" userInput='Please correct the input data for "USD => RHD" rate' stepKey="seeRHDMessageAfterSave"/> - <see selector="{{AdminMessagesSection.warningMessage}}" userInput='Please correct the input data for "USD => CHW" rate' stepKey="seeCHWMessageAfterSave"/> + <see selector="{{AdminMessagesSection.warning}}" userInput='Please correct the input data for "USD => CHE" rate' stepKey="seeCHEMessageAfterSave"/> + <see selector="{{AdminMessagesSection.warning}}" userInput='Please correct the input data for "USD => RHD" rate' stepKey="seeRHDMessageAfterSave"/> + <see selector="{{AdminMessagesSection.warning}}" userInput='Please correct the input data for "USD => CHW" rate' stepKey="seeCHWMessageAfterSave"/> <!--Go to the Storefront and check currency rates--> <amOnPage url="{{StorefrontCategoryPage.url($$createCategory.name$$)}}" stepKey="onCategoryPage"/> <waitForPageLoad stepKey="waitForCategoryPageLoad"/> @@ -74,6 +74,6 @@ <actionGroup ref="AdminImportCurrencyRatesActionGroup" stepKey="importCurrencyRatesGreaterThen10"> <argument name="rateService" value="Currency Converter API"/> </actionGroup> - <see selector="{{AdminMessagesSection.warningMessage}}" userInput="Too many pairs. Maximum of 10 is supported for the free version." stepKey="seeTooManyPairsMessage"/> + <see selector="{{AdminMessagesSection.warning}}" userInput="Too many pairs. Maximum of 10 is supported for the free version." stepKey="seeTooManyPairsMessage"/> </test> </tests> diff --git a/app/code/Magento/CurrencySymbol/Test/Mftf/Test/AdminDefaultCurrencySymbolsAreDisabledTest.xml b/app/code/Magento/CurrencySymbol/Test/Mftf/Test/AdminDefaultCurrencySymbolsAreDisabledTest.xml new file mode 100644 index 0000000000000..f4d6180bae88a --- /dev/null +++ b/app/code/Magento/CurrencySymbol/Test/Mftf/Test/AdminDefaultCurrencySymbolsAreDisabledTest.xml @@ -0,0 +1,39 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + /** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +--> + +<tests xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/testSchema.xsd"> + <test name="AdminCurrencySymbolsAreDisabledTest"> + <annotations> + <features value="CurrencySymbol"/> + <stories value="Currency Symbols"/> + <title value="Currency symbols are disabled by default"/> + <description value="Currency symbols should be disabled by default"/> + <severity value="MINOR"/> + <group value="currency"/> + </annotations> + <before> + <magentoCLI command="config:set --scope={{SetAllowedCurrenciesConfigForUSD.scope}} --scope-code={{SetAllowedCurrenciesConfigForUSD.scope_code}} {{SetAllowedCurrenciesConfigForUSD.path}} {{SetAllowedCurrenciesConfigForUSD.value}},{{SetAllowedCurrenciesConfigForEUR.value}},{{SetAllowedCurrenciesConfigForRUB.value}}" stepKey="setAllowedCurrencyWebsites_EUR_RUB_USD"/> + <actionGroup ref="AdminLoginActionGroup" stepKey="loginAsAdmin"/> + </before> + <after> + <magentoCLI command="config:set --scope={{SetAllowedCurrenciesConfigForUSD.scope}} --scope-code={{SetAllowedCurrenciesConfigForUSD.scope_code}} {{SetAllowedCurrenciesConfigForUSD.path}} {{SetAllowedCurrenciesConfigForUSD.value}},{{SetAllowedCurrenciesConfigForEUR.value}}" stepKey="setAllowedCurrencyWebsites_EUR_USD"/> + <actionGroup ref="AdminLogoutActionGroup" stepKey="logout"/> + </after> + <actionGroup ref="AdminNavigateToCurrencySymbolsPageActionGroup" stepKey="navigateToCurrencySymbolsPage"/> + <actionGroup ref="AssertAdminCurrencySymbolIsDisabledActionGroup" stepKey="assertEURDisabledInput"> + <argument name="currency" value="{{SetAllowedCurrenciesConfigForEUR.value}}"/> + </actionGroup> + <actionGroup ref="AssertAdminCurrencySymbolIsDisabledActionGroup" stepKey="assertUSDDisabledInput"> + <argument name="currency" value="{{SetAllowedCurrenciesConfigForUSD.value}}"/> + </actionGroup> + <actionGroup ref="AssertAdminCurrencySymbolIsDisabledActionGroup" stepKey="assertRUBDisabledInput"> + <argument name="currency" value="{{SetAllowedCurrenciesConfigForRUB.value}}"/> + </actionGroup> + </test> +</tests> diff --git a/app/code/Magento/CurrencySymbol/Test/Mftf/Test/AdminOrderRateDisplayWhenChooseThreeAllowedCurrenciesTest.xml b/app/code/Magento/CurrencySymbol/Test/Mftf/Test/AdminOrderRateDisplayWhenChooseThreeAllowedCurrenciesTest.xml index 100aa39613b33..ce9b05882d494 100644 --- a/app/code/Magento/CurrencySymbol/Test/Mftf/Test/AdminOrderRateDisplayWhenChooseThreeAllowedCurrenciesTest.xml +++ b/app/code/Magento/CurrencySymbol/Test/Mftf/Test/AdminOrderRateDisplayWhenChooseThreeAllowedCurrenciesTest.xml @@ -20,7 +20,7 @@ <group value="currency"/> </annotations> <before> - <actionGroup ref="LoginAsAdmin" stepKey="loginAsAdmin"/> + <actionGroup ref="AdminLoginActionGroup" stepKey="loginAsAdmin"/> <!--Create product--> <createData entity="SimpleProduct2" stepKey="createNewProduct"/> <!--Set Currency options for Website--> @@ -32,7 +32,7 @@ <!--Delete created product--> <comment userInput="Delete created product" stepKey="commentDeleteCreatedProduct"/> <deleteData createDataKey="createNewProduct" stepKey="deleteNewProduct"/> - <actionGroup ref="logout" stepKey="logout"/> + <actionGroup ref="AdminLogoutActionGroup" stepKey="logout"/> </after> <!--Set currency rates--> <amOnPage url="{{AdminCurrencyRatesPage.url}}" stepKey="gotToCurrencyRatesPageSecondTime"/> diff --git a/app/code/Magento/CurrencySymbol/Test/Mftf/Test/AdminOrderRateDisplayedInOneLineTest.xml b/app/code/Magento/CurrencySymbol/Test/Mftf/Test/AdminOrderRateDisplayedInOneLineTest.xml index 94c9bc67e34c4..146671e36af68 100644 --- a/app/code/Magento/CurrencySymbol/Test/Mftf/Test/AdminOrderRateDisplayedInOneLineTest.xml +++ b/app/code/Magento/CurrencySymbol/Test/Mftf/Test/AdminOrderRateDisplayedInOneLineTest.xml @@ -20,7 +20,7 @@ <group value="currency"/> </annotations> <before> - <actionGroup ref="LoginAsAdmin" stepKey="loginAsAdmin"/> + <actionGroup ref="AdminLoginActionGroup" stepKey="loginAsAdmin"/> <!--Create product--> <createData entity="SimpleProduct2" stepKey="createProduct"/> <!--Set price scope website--> @@ -46,7 +46,7 @@ <magentoCLI command="config:set --scope={{SetCurrencyUSDBaseConfig.scope}} --scope-code={{SetCurrencyUSDBaseConfig.scope_code}} {{SetCurrencyUSDBaseConfig.path}} {{SetCurrencyUSDBaseConfig.value}}" stepKey="setCurrencyBaseUSDWebsites"/> <magentoCLI command="config:set --scope={{SetDefaultCurrencyUSDConfig.scope}} --scope-code={{SetDefaultCurrencyUSDConfig.scope_code}} {{SetDefaultCurrencyUSDConfig.path}} {{SetDefaultCurrencyUSDConfig.value}}" stepKey="setCurrencyDefaultUSDWebsites"/> <magentoCLI command="config:set --scope={{SetAllowedCurrenciesConfigForUSD.scope}} --scope-code={{SetAllowedCurrenciesConfigForUSD.scope_code}} {{SetAllowedCurrenciesConfigForUSD.path}} {{SetAllowedCurrenciesConfigForUSD.value}}" stepKey="setAllowedCurrencyUSDWebsites"/> - <actionGroup ref="logout" stepKey="logout"/> + <actionGroup ref="AdminLogoutActionGroup" stepKey="logout"/> </after> <!--Open created product on Storefront and place for order--> <amOnPage url="{{StorefrontProductPage.url($$createProduct.custom_attributes[url_key]$$)}}" stepKey="goToProductPage"/> diff --git a/app/code/Magento/CurrencySymbol/Test/Mftf/Test/AdminStoresCurrencyRatesNavigateMenuTest.xml b/app/code/Magento/CurrencySymbol/Test/Mftf/Test/AdminStoresCurrencyRatesNavigateMenuTest.xml index 4a33d40d2a35f..95f1eebee6c87 100644 --- a/app/code/Magento/CurrencySymbol/Test/Mftf/Test/AdminStoresCurrencyRatesNavigateMenuTest.xml +++ b/app/code/Magento/CurrencySymbol/Test/Mftf/Test/AdminStoresCurrencyRatesNavigateMenuTest.xml @@ -20,10 +20,10 @@ <group value="mtf_migrated"/> </annotations> <before> - <actionGroup ref="LoginAsAdmin" stepKey="LoginAsAdmin"/> + <actionGroup ref="AdminLoginActionGroup" stepKey="LoginAsAdmin"/> </before> <after> - <actionGroup ref="logout" stepKey="logout"/> + <actionGroup ref="AdminLogoutActionGroup" stepKey="logout"/> </after> <actionGroup ref="AdminNavigateMenuActionGroup" stepKey="navigateToStoresCurrencyRatesPage"> <argument name="menuUiId" value="{{AdminMenuStores.dataUiId}}"/> diff --git a/app/code/Magento/CurrencySymbol/Test/Mftf/Test/AdminStoresCurrencySymbolsNavigateMenuTest.xml b/app/code/Magento/CurrencySymbol/Test/Mftf/Test/AdminStoresCurrencySymbolsNavigateMenuTest.xml index 978917772f2dd..254b0fc6058f8 100644 --- a/app/code/Magento/CurrencySymbol/Test/Mftf/Test/AdminStoresCurrencySymbolsNavigateMenuTest.xml +++ b/app/code/Magento/CurrencySymbol/Test/Mftf/Test/AdminStoresCurrencySymbolsNavigateMenuTest.xml @@ -20,10 +20,10 @@ <group value="mtf_migrated"/> </annotations> <before> - <actionGroup ref="LoginAsAdmin" stepKey="LoginAsAdmin"/> + <actionGroup ref="AdminLoginActionGroup" stepKey="LoginAsAdmin"/> </before> <after> - <actionGroup ref="logout" stepKey="logout"/> + <actionGroup ref="AdminLogoutActionGroup" stepKey="logout"/> </after> <actionGroup ref="AdminNavigateMenuActionGroup" stepKey="navigateToStoresCurrencySymbolsPage"> <argument name="menuUiId" value="{{AdminMenuStores.dataUiId}}"/> diff --git a/app/code/Magento/CurrencySymbol/Test/Unit/Controller/Adminhtml/System/Currencysymbol/SaveTest.php b/app/code/Magento/CurrencySymbol/Test/Unit/Controller/Adminhtml/System/Currencysymbol/SaveTest.php index b3c69c352ac7d..e4dfc7a3dc765 100644 --- a/app/code/Magento/CurrencySymbol/Test/Unit/Controller/Adminhtml/System/Currencysymbol/SaveTest.php +++ b/app/code/Magento/CurrencySymbol/Test/Unit/Controller/Adminhtml/System/Currencysymbol/SaveTest.php @@ -132,7 +132,7 @@ public function testExecute() ->with(__('You applied the custom currency symbols.')); $redirect = $this->createMock(Redirect::class); - $redirect->expects($this->once())->method('setPath')->with('*')->willReturnSelf(); + $redirect->expects($this->once())->method('setPath')->with('adminhtml/*/')->willReturnSelf(); $this->resultRedirectFactory->method('create')->willReturn($redirect); $this->assertEquals($redirect, $this->action->execute()); diff --git a/app/code/Magento/CurrencySymbol/Test/Unit/Observer/CurrencyDisplayOptionsTest.php b/app/code/Magento/CurrencySymbol/Test/Unit/Observer/CurrencyDisplayOptionsTest.php index dd0d7ffc8cc68..ae8c12c2019b8 100644 --- a/app/code/Magento/CurrencySymbol/Test/Unit/Observer/CurrencyDisplayOptionsTest.php +++ b/app/code/Magento/CurrencySymbol/Test/Unit/Observer/CurrencyDisplayOptionsTest.php @@ -6,6 +6,7 @@ namespace Magento\CurrencySymbol\Test\Unit\Observer; use Magento\CurrencySymbol\Model\System\CurrencysymbolFactory; +use Magento\Framework\Locale\Currency; /** * Test for \Magento\CurrencySymbol\Observer\CurrencyDisplayOptions @@ -65,7 +66,7 @@ protected function setUp() public function testCurrencyDisplayOptionsEmpty() { $baseData = [ - \Magento\Framework\Locale\Currency::CURRENCY_OPTION_NAME => 'US Dollar' + Currency::CURRENCY_OPTION_NAME => 'US Dollar' ]; $sampleCurrencyOptionObject = new \Magento\Framework\DataObject($baseData); @@ -83,7 +84,7 @@ public function testCurrencyDisplayOptionsEmpty() public function testCurrencyDisplayOptions() { $baseData = [ - \Magento\Framework\Locale\Currency::CURRENCY_OPTION_NAME => 'US Dollar' + Currency::CURRENCY_OPTION_NAME => 'US Dollar' ]; $sampleCurrencyOptionObject = new \Magento\Framework\DataObject($baseData); $sampleCurrency = 'USD'; @@ -92,9 +93,9 @@ public function testCurrencyDisplayOptions() $expectedCurrencyOptions = array_merge( $baseData, [ - \Magento\Framework\Locale\Currency::CURRENCY_OPTION_NAME => 'US Dollar', - \Magento\Framework\Locale\Currency::CURRENCY_OPTION_SYMBOL => $sampleCurrencySymbol, - \Magento\Framework\Locale\Currency::CURRENCY_OPTION_DISPLAY => \Magento\Framework\Currency::USE_SYMBOL + Currency::CURRENCY_OPTION_NAME => 'US Dollar', + Currency::CURRENCY_OPTION_SYMBOL => $sampleCurrencySymbol, + Currency::CURRENCY_OPTION_DISPLAY => \Magento\Framework\Currency::USE_SYMBOL ] ); diff --git a/app/code/Magento/CurrencySymbol/registration.php b/app/code/Magento/CurrencySymbol/registration.php index 4dea4264e8b62..bf30fc7fe6a7b 100644 --- a/app/code/Magento/CurrencySymbol/registration.php +++ b/app/code/Magento/CurrencySymbol/registration.php @@ -4,6 +4,6 @@ * See COPYING.txt for license details. */ -use \Magento\Framework\Component\ComponentRegistrar; +use Magento\Framework\Component\ComponentRegistrar; ComponentRegistrar::register(ComponentRegistrar::MODULE, 'Magento_CurrencySymbol', __DIR__); diff --git a/app/code/Magento/CurrencySymbol/view/adminhtml/templates/grid.phtml b/app/code/Magento/CurrencySymbol/view/adminhtml/templates/grid.phtml index 397c2598dc3b0..55195698c5dc8 100644 --- a/app/code/Magento/CurrencySymbol/view/adminhtml/templates/grid.phtml +++ b/app/code/Magento/CurrencySymbol/view/adminhtml/templates/grid.phtml @@ -12,21 +12,28 @@ <form id="currency-symbols-form" action="<?= $block->escapeUrl($block->getFormActionUrl()) ?>" method="post"> <input name="form_key" type="hidden" value="<?= $block->escapeHtmlAttr($block->getFormKey()) ?>" /> <fieldset class="admin__fieldset"> - <?php foreach ($block->getCurrencySymbolsData() as $code => $data) : ?> + <?php foreach ($block->getCurrencySymbolsData() as $code => $data): ?> <div class="admin__field _required"> <label class="admin__field-label" for="custom_currency_symbol<?= $block->escapeHtmlAttr($code) ?>"> <span><?= $block->escapeHtml($code) ?> (<?= $block->escapeHtml($data['displayName']) ?>)</span> </label> <div class="admin__field-control"> <input id="custom_currency_symbol<?= $block->escapeHtmlAttr($code) ?>" - class="required-entry admin__control-text <?= $data['inherited'] ? 'disabled' : '' ?>" + class="required-entry admin__control-text" type="text" value="<?= $block->escapeHtmlAttr($data['displaySymbol']) ?>" - name="custom_currency_symbol[<?= $block->escapeHtmlAttr($code) ?>]"> + name="custom_currency_symbol[<?= $block->escapeHtmlAttr($code) ?>]" + <?= $data['inherited'] ? 'disabled' : '' ?>> <div class="admin__field admin__field-option"> + <?php + $escapedCode = $block->escapeHtmlAttr($block->escapeJs($code)); + $escapedSymbol = $block->escapeJs($data['parentSymbol']); + ?> <input id="custom_currency_symbol_inherit<?= $block->escapeHtmlAttr($code) ?>" class="admin__control-checkbox" type="checkbox" - onclick="toggleUseDefault(<?= '\'' . $block->escapeHtmlAttr($block->escapeJs($code)) . '\',\'' . $block->escapeJs($data['parentSymbol']) . '\'' ?>)" + <?php //@codingStandardsIgnoreStart ?> + onclick="toggleUseDefault(<?= '\'' . $escapedCode . '\',\'' . $escapedSymbol . '\'' ?>)" + <?php //@codingStandardsIgnoreEnd ?> <?= $data['inherited'] ? ' checked="checked"' : '' ?> value="1" name="inherit_custom_currency_symbol[<?= $block->escapeHtmlAttr($code) ?>]"> diff --git a/app/code/Magento/CurrencySymbol/view/adminhtml/web/js/symbols-form.js b/app/code/Magento/CurrencySymbol/view/adminhtml/web/js/symbols-form.js index 68f914ddb1b4d..a2cbfa883554a 100644 --- a/app/code/Magento/CurrencySymbol/view/adminhtml/web/js/symbols-form.js +++ b/app/code/Magento/CurrencySymbol/view/adminhtml/web/js/symbols-form.js @@ -25,12 +25,10 @@ define([ input = $('#custom_currency_symbol' + code); if (checkbox.is(':checked')) { - input.addClass('disabled'); input.val(value); - input.prop('readonly', true); + input.prop('disabled', true); } else { - input.removeClass('disabled'); - input.prop('readonly', false); + input.prop('disabled', false); } } diff --git a/app/code/Magento/Customer/Block/Account/Dashboard.php b/app/code/Magento/Customer/Block/Account/Dashboard.php index 547074d0bcd81..92537009175fd 100644 --- a/app/code/Magento/Customer/Block/Account/Dashboard.php +++ b/app/code/Magento/Customer/Block/Account/Dashboard.php @@ -129,16 +129,6 @@ public function getOrdersUrl() return ''; } - /** - * Retrieve the Url for customer reviews. - * - * @return string - */ - public function getReviewsUrl() - { - return $this->_urlBuilder->getUrl('review/customer/index', ['_secure' => true]); - } - /** * Retrieve the Url for managing customer wishlist. * diff --git a/app/code/Magento/Customer/Block/Adminhtml/Edit/Tab/Cart.php b/app/code/Magento/Customer/Block/Adminhtml/Edit/Tab/Cart.php index 3709f4914c477..656a78d1165e3 100644 --- a/app/code/Magento/Customer/Block/Adminhtml/Edit/Tab/Cart.php +++ b/app/code/Magento/Customer/Block/Adminhtml/Edit/Tab/Cart.php @@ -5,37 +5,51 @@ */ namespace Magento\Customer\Block\Adminhtml\Edit\Tab; -use Magento\Catalog\Model\Product; +use Magento\Backend\Block\Template\Context; +use Magento\Backend\Block\Widget\Form; +use Magento\Backend\Block\Widget\Grid\Extended; +use Magento\Backend\Helper\Data; +use Magento\Customer\Block\Adminhtml\Edit\Tab\View\Grid\Renderer\Item; +use Magento\Customer\Block\Adminhtml\Grid\Renderer\Multiaction; use Magento\Customer\Controller\RegistryConstants; -use Magento\Directory\Model\Currency; +use Magento\Framework\App\ObjectManager; +use Magento\Framework\Data\CollectionFactory; +use Magento\Framework\Data\FormFactory; +use Magento\Framework\Exception\NoSuchEntityException; +use Magento\Framework\Registry; +use Magento\Quote\Api\CartRepositoryInterface; +use Magento\Quote\Model\Quote; +use Magento\Quote\Model\QuoteFactory; +use Magento\Store\Model\System\Store as SystemStore; /** * Adminhtml customer orders grid block * * @api * @since 100.0.2 + * @SuppressWarnings(PHPMD.CouplingBetweenObjects) */ -class Cart extends \Magento\Backend\Block\Widget\Grid\Extended +class Cart extends Extended { /** * Core registry * - * @var \Magento\Framework\Registry + * @var Registry */ protected $_coreRegistry = null; /** - * @var \Magento\Framework\Data\CollectionFactory + * @var CollectionFactory */ protected $_dataCollectionFactory; /** - * @var \Magento\Quote\Api\CartRepositoryInterface + * @var CartRepositoryInterface */ protected $quoteRepository; /** - * @var \Magento\Quote\Model\Quote + * @var Quote */ protected $quote = null; @@ -45,32 +59,46 @@ class Cart extends \Magento\Backend\Block\Widget\Grid\Extended protected $_parentTemplate; /** - * @var \Magento\Quote\Model\QuoteFactory + * @var QuoteFactory */ protected $quoteFactory; + /** + * @var SystemStore + */ + private $systemStore; + /** + * @var FormFactory + */ + private $formFactory; /** - * @param \Magento\Backend\Block\Template\Context $context - * @param \Magento\Backend\Helper\Data $backendHelper - * @param \Magento\Quote\Api\CartRepositoryInterface $quoteRepository - * @param \Magento\Framework\Data\CollectionFactory $dataCollectionFactory - * @param \Magento\Framework\Registry $coreRegistry - * @param \Magento\Quote\Model\QuoteFactory $quoteFactory + * @param Context $context + * @param Data $backendHelper + * @param CartRepositoryInterface $quoteRepository + * @param CollectionFactory $dataCollectionFactory + * @param Registry $coreRegistry + * @param QuoteFactory $quoteFactory * @param array $data + * @param SystemStore|null $systemStore + * @param FormFactory|null $formFactory */ public function __construct( - \Magento\Backend\Block\Template\Context $context, - \Magento\Backend\Helper\Data $backendHelper, - \Magento\Quote\Api\CartRepositoryInterface $quoteRepository, - \Magento\Framework\Data\CollectionFactory $dataCollectionFactory, - \Magento\Framework\Registry $coreRegistry, - \Magento\Quote\Model\QuoteFactory $quoteFactory, - array $data = [] + Context $context, + Data $backendHelper, + CartRepositoryInterface $quoteRepository, + CollectionFactory $dataCollectionFactory, + Registry $coreRegistry, + QuoteFactory $quoteFactory, + array $data = [], + ?SystemStore $systemStore = null, + ?FormFactory $formFactory = null ) { $this->_dataCollectionFactory = $dataCollectionFactory; $this->_coreRegistry = $coreRegistry; $this->quoteRepository = $quoteRepository; $this->quoteFactory = $quoteFactory; + $this->systemStore = $systemStore ?? ObjectManager::getInstance()->get(SystemStore::class); + $this->formFactory = $formFactory ?? ObjectManager::getInstance()->get(FormFactory::class); parent::__construct($context, $backendHelper, $data); } @@ -92,8 +120,11 @@ protected function _construct() */ protected function _prepareGrid() { - $this->setId('customer_cart_grid' . $this->getWebsiteId()); + $this->setId('customer_cart_grid'); parent::_prepareGrid(); + if (!$this->_storeManager->isSingleStoreMode()) { + $this->prepareWebsiteFilter(); + } } /** @@ -105,14 +136,13 @@ protected function _prepareCollection() { $quote = $this->getQuote(); - if ($quote) { + if ($quote && $quote->getId()) { $collection = $quote->getItemsCollection(false); + $collection->addFieldToFilter('parent_item_id', ['null' => true]); } else { $collection = $this->_dataCollectionFactory->create(); } - $collection->addFieldToFilter('parent_item_id', ['null' => true]); - $this->setCollection($collection); return parent::_prepareCollection(); @@ -130,7 +160,7 @@ protected function _prepareColumns() [ 'header' => __('Product'), 'index' => 'name', - 'renderer' => \Magento\Customer\Block\Adminhtml\Edit\Tab\View\Grid\Renderer\Item::class + 'renderer' => Item::class ] ); @@ -168,7 +198,7 @@ protected function _prepareColumns() [ 'header' => __('Action'), 'index' => 'quote_item_id', - 'renderer' => \Magento\Customer\Block\Adminhtml\Grid\Renderer\Multiaction::class, + 'renderer' => Multiaction::class, 'filter' => false, 'sortable' => false, 'actions' => [ @@ -246,10 +276,59 @@ protected function getQuote() try { $this->quote = $this->quoteRepository->getForCustomer($customerId, $storeIds); - } catch (\Magento\Framework\Exception\NoSuchEntityException $e) { + } catch (NoSuchEntityException $e) { $this->quote = $this->quoteFactory->create()->setSharedStoreIds($storeIds); } } return $this->quote; } + + /** + * Add website filter block to the layout + * + * @return void + */ + private function prepareWebsiteFilter(): void + { + $form = $this->formFactory->create(); + $form->addField( + 'website_filter', + 'select', + [ + 'name' => 'website_id', + 'values' => $this->systemStore->getWebsiteOptionHash(), + 'value' => $this->getWebsiteId() ?? $this->_storeManager->getWebsite()->getId(), + 'no_span' => true, + 'onchange' => "{$this->getJsObjectName()}.loadByElement(this);", + ] + ); + /** + * @var Form $formWidget + */ + $formWidget = $this->getLayout()->createBlock(Form::class); + $formWidget->setForm($form); + $formWidget->setTemplate('Magento_Customer::tab/cart_website_filter_form.phtml'); + $this->setChild( + 'website_filter_block', + $formWidget + ); + } + + /** + * @inheritDoc + */ + public function getMainButtonsHtml() + { + return $this->getWebsiteFilterHtml() . parent::getMainButtonsHtml(); + } + + /** + * Generate website filter + * + * @return string + */ + private function getWebsiteFilterHtml(): string + { + return $this->getChildHtml('website_filter_block'); + } } diff --git a/app/code/Magento/Customer/Block/Adminhtml/Edit/Tab/Reviews.php b/app/code/Magento/Customer/Block/Adminhtml/Edit/Tab/Reviews.php deleted file mode 100644 index a10842fc0c6de..0000000000000 --- a/app/code/Magento/Customer/Block/Adminhtml/Edit/Tab/Reviews.php +++ /dev/null @@ -1,33 +0,0 @@ -<?php -/** - * Copyright © Magento, Inc. All rights reserved. - * See COPYING.txt for license details. - */ -namespace Magento\Customer\Block\Adminhtml\Edit\Tab; - -/** - * @api - * @since 100.0.2 - */ -class Reviews extends \Magento\Review\Block\Adminhtml\Grid -{ - /** - * Hide grid mass action elements - * - * @return \Magento\Customer\Block\Adminhtml\Edit\Tab\Reviews - */ - protected function _prepareMassaction() - { - return $this; - } - - /** - * Determine ajax url for grid refresh - * - * @return string - */ - public function getGridUrl() - { - return $this->getUrl('customer/*/productReviews', ['_current' => true]); - } -} diff --git a/app/code/Magento/Customer/Block/Widget/Dob.php b/app/code/Magento/Customer/Block/Widget/Dob.php index e020de79a3a60..90ce9ba210ed2 100644 --- a/app/code/Magento/Customer/Block/Widget/Dob.php +++ b/app/code/Magento/Customer/Block/Widget/Dob.php @@ -9,7 +9,7 @@ use Magento\Framework\Api\ArrayObjectSearch; /** - * Class Dob + * Customer date of birth attribute block * * @SuppressWarnings(PHPMD.DepthOfInheritance) */ @@ -267,7 +267,9 @@ public function getHtmlExtraParams() $validators['validate-date'] = [ 'dateFormat' => $this->getDateFormat() ]; - $validators['validate-dob'] = true; + $validators['validate-dob'] = [ + 'dateFormat' => $this->getDateFormat() + ]; return 'data-validate="' . $this->_escaper->escapeHtml(json_encode($validators)) . '"'; } diff --git a/app/code/Magento/Customer/Controller/AbstractAccount.php b/app/code/Magento/Customer/Controller/AbstractAccount.php index 21611329ed9bc..4f2c80711d292 100644 --- a/app/code/Magento/Customer/Controller/AbstractAccount.php +++ b/app/code/Magento/Customer/Controller/AbstractAccount.php @@ -9,9 +9,11 @@ use Magento\Framework\App\Action\Action; /** - * Class AbstractAccount - * @package Magento\Customer\Controller + * AbstractAccount class is deprecated, in favour of Composition approach to build Controllers + * * @SuppressWarnings(PHPMD.NumberOfChildren) + * @deprecated + * @see \Magento\Customer\Controller\AccountInterface */ abstract class AbstractAccount extends Action implements AccountInterface { diff --git a/app/code/Magento/Customer/Controller/Adminhtml/Index/Cart.php b/app/code/Magento/Customer/Controller/Adminhtml/Index/Cart.php index 1e4c1fb001ea3..6528ac4c1f211 100644 --- a/app/code/Magento/Customer/Controller/Adminhtml/Index/Cart.php +++ b/app/code/Magento/Customer/Controller/Adminhtml/Index/Cart.php @@ -5,84 +5,116 @@ */ namespace Magento\Customer\Controller\Adminhtml\Index; +use Magento\Backend\App\Action\Context; +use Magento\Backend\Model\View\Result\ForwardFactory; use Magento\Customer\Api\AccountManagementInterface; use Magento\Customer\Api\AddressRepositoryInterface; use Magento\Customer\Api\CustomerRepositoryInterface; use Magento\Customer\Api\Data\AddressInterfaceFactory; use Magento\Customer\Api\Data\CustomerInterfaceFactory; +use Magento\Customer\Controller\Adminhtml\Index as BaseAction; +use Magento\Customer\Helper\View; use Magento\Customer\Model\Address\Mapper; -use Magento\Framework\DataObjectFactory as ObjectFactory; +use Magento\Customer\Model\AddressFactory; +use Magento\Customer\Model\CustomerFactory; +use Magento\Customer\Model\Metadata\FormFactory; use Magento\Framework\Api\DataObjectHelper; +use Magento\Framework\Api\ExtensibleDataObjectConverter; +use Magento\Framework\App\Action\HttpGetActionInterface; +use Magento\Framework\App\Action\HttpPostActionInterface as HttpPostActionInterface; +use Magento\Framework\App\Response\Http\FileFactory; +use Magento\Framework\Controller\Result\JsonFactory; +use Magento\Framework\DataObjectFactory as ObjectFactory; +use Magento\Framework\Exception\NoSuchEntityException; +use Magento\Framework\Math\Random; +use Magento\Framework\Reflection\DataObjectProcessor; +use Magento\Framework\Registry; +use Magento\Framework\View\Result\Layout; +use Magento\Framework\View\Result\LayoutFactory; +use Magento\Framework\View\Result\PageFactory; +use Magento\Newsletter\Model\SubscriberFactory; +use Magento\Quote\Api\CartRepositoryInterface; +use Magento\Quote\Model\Quote; +use Magento\Quote\Model\QuoteFactory; +use Magento\Store\Model\StoreManagerInterface; /** + * Admin customer shopping cart controller + * * @SuppressWarnings(PHPMD.CouplingBetweenObjects) * @deprecated 100.2.0 */ -class Cart extends \Magento\Customer\Controller\Adminhtml\Index +class Cart extends BaseAction implements HttpGetActionInterface, HttpPostActionInterface { /** - * @var \Magento\Quote\Model\QuoteFactory + * @var QuoteFactory */ private $quoteFactory; + /** + * @var StoreManagerInterface + */ + private $storeManager; /** * Constructor * - * @param \Magento\Backend\App\Action\Context $context - * @param \Magento\Framework\Registry $coreRegistry - * @param \Magento\Framework\App\Response\Http\FileFactory $fileFactory - * @param \Magento\Customer\Model\CustomerFactory $customerFactory - * @param \Magento\Customer\Model\AddressFactory $addressFactory - * @param \Magento\Customer\Model\Metadata\FormFactory $formFactory - * @param \Magento\Newsletter\Model\SubscriberFactory $subscriberFactory - * @param \Magento\Customer\Helper\View $viewHelper - * @param \Magento\Framework\Math\Random $random + * @param Context $context + * @param Registry $coreRegistry + * @param FileFactory $fileFactory + * @param CustomerFactory $customerFactory + * @param AddressFactory $addressFactory + * @param FormFactory $formFactory + * @param SubscriberFactory $subscriberFactory + * @param View $viewHelper + * @param Random $random * @param CustomerRepositoryInterface $customerRepository - * @param \Magento\Framework\Api\ExtensibleDataObjectConverter $extensibleDataObjectConverter + * @param ExtensibleDataObjectConverter $extensibleDataObjectConverter * @param Mapper $addressMapper * @param AccountManagementInterface $customerAccountManagement * @param AddressRepositoryInterface $addressRepository * @param CustomerInterfaceFactory $customerDataFactory * @param AddressInterfaceFactory $addressDataFactory * @param \Magento\Customer\Model\Customer\Mapper $customerMapper - * @param \Magento\Framework\Reflection\DataObjectProcessor $dataObjectProcessor + * @param DataObjectProcessor $dataObjectProcessor * @param DataObjectHelper $dataObjectHelper * @param ObjectFactory $objectFactory * @param \Magento\Framework\View\LayoutFactory $layoutFactory - * @param \Magento\Framework\View\Result\LayoutFactory $resultLayoutFactory - * @param \Magento\Framework\View\Result\PageFactory $resultPageFactory - * @param \Magento\Backend\Model\View\Result\ForwardFactory $resultForwardFactory - * @param \Magento\Framework\Controller\Result\JsonFactory $resultJsonFactory - * @param \Magento\Quote\Model\QuoteFactory|null $quoteFactory + * @param LayoutFactory $resultLayoutFactory + * @param PageFactory $resultPageFactory + * @param ForwardFactory $resultForwardFactory + * @param JsonFactory $resultJsonFactory + * @param QuoteFactory|null $quoteFactory + * @param StoreManagerInterface|null $storeManager * @SuppressWarnings(PHPMD.ExcessiveParameterList) */ public function __construct( - \Magento\Backend\App\Action\Context $context, - \Magento\Framework\Registry $coreRegistry, - \Magento\Framework\App\Response\Http\FileFactory $fileFactory, - \Magento\Customer\Model\CustomerFactory $customerFactory, - \Magento\Customer\Model\AddressFactory $addressFactory, - \Magento\Customer\Model\Metadata\FormFactory $formFactory, - \Magento\Newsletter\Model\SubscriberFactory $subscriberFactory, - \Magento\Customer\Helper\View $viewHelper, - \Magento\Framework\Math\Random $random, + Context $context, + Registry $coreRegistry, + FileFactory $fileFactory, + CustomerFactory $customerFactory, + AddressFactory $addressFactory, + FormFactory $formFactory, + SubscriberFactory $subscriberFactory, + View $viewHelper, + Random $random, CustomerRepositoryInterface $customerRepository, - \Magento\Framework\Api\ExtensibleDataObjectConverter $extensibleDataObjectConverter, + ExtensibleDataObjectConverter $extensibleDataObjectConverter, Mapper $addressMapper, AccountManagementInterface $customerAccountManagement, AddressRepositoryInterface $addressRepository, CustomerInterfaceFactory $customerDataFactory, AddressInterfaceFactory $addressDataFactory, \Magento\Customer\Model\Customer\Mapper $customerMapper, - \Magento\Framework\Reflection\DataObjectProcessor $dataObjectProcessor, + DataObjectProcessor $dataObjectProcessor, DataObjectHelper $dataObjectHelper, ObjectFactory $objectFactory, \Magento\Framework\View\LayoutFactory $layoutFactory, - \Magento\Framework\View\Result\LayoutFactory $resultLayoutFactory, - \Magento\Framework\View\Result\PageFactory $resultPageFactory, - \Magento\Backend\Model\View\Result\ForwardFactory $resultForwardFactory, - \Magento\Framework\Controller\Result\JsonFactory $resultJsonFactory, - \Magento\Quote\Model\QuoteFactory $quoteFactory = null + LayoutFactory $resultLayoutFactory, + PageFactory $resultPageFactory, + ForwardFactory $resultForwardFactory, + JsonFactory $resultJsonFactory, + QuoteFactory $quoteFactory = null, + ?StoreManagerInterface $storeManager = null ) { parent::__construct( $context, @@ -111,13 +143,14 @@ public function __construct( $resultForwardFactory, $resultJsonFactory ); - $this->quoteFactory = $quoteFactory ?: $this->_objectManager->get(\Magento\Quote\Model\QuoteFactory::class); + $this->quoteFactory = $quoteFactory ?: $this->_objectManager->get(QuoteFactory::class); + $this->storeManager = $storeManager ?? $this->_objectManager->get(StoreManagerInterface::class); } /** * Handle and then get cart grid contents * - * @return \Magento\Framework\View\Result\Layout + * @return Layout */ public function execute() { @@ -127,16 +160,17 @@ public function execute() // delete an item from cart $deleteItemId = $this->getRequest()->getPost('delete'); if ($deleteItemId) { - /** @var \Magento\Quote\Api\CartRepositoryInterface $quoteRepository */ - $quoteRepository = $this->_objectManager->create(\Magento\Quote\Api\CartRepositoryInterface::class); - /** @var \Magento\Quote\Model\Quote $quote */ + /** @var CartRepositoryInterface $quoteRepository */ + $quoteRepository = $this->_objectManager->create(CartRepositoryInterface::class); + /** @var Quote $quote */ try { - $quote = $quoteRepository->getForCustomer($customerId); - } catch (\Magento\Framework\Exception\NoSuchEntityException $e) { + $storeIds = $this->storeManager->getWebsite($websiteId)->getStoreIds(); + $quote = $quoteRepository->getForCustomer($customerId, $storeIds); + } catch (NoSuchEntityException $e) { $quote = $this->quoteFactory->create(); } $quote->setWebsite( - $this->_objectManager->get(\Magento\Store\Model\StoreManagerInterface::class)->getWebsite($websiteId) + $this->storeManager->getWebsite($websiteId) ); $item = $quote->getItemById($deleteItemId); if ($item && $item->getId()) { diff --git a/app/code/Magento/Customer/Controller/Adminhtml/Index/ProductReviews.php b/app/code/Magento/Customer/Controller/Adminhtml/Index/ProductReviews.php deleted file mode 100644 index dca7820c3dbc4..0000000000000 --- a/app/code/Magento/Customer/Controller/Adminhtml/Index/ProductReviews.php +++ /dev/null @@ -1,23 +0,0 @@ -<?php -/** - * Copyright © Magento, Inc. All rights reserved. - * See COPYING.txt for license details. - */ -namespace Magento\Customer\Controller\Adminhtml\Index; - -class ProductReviews extends \Magento\Customer\Controller\Adminhtml\Index -{ - /** - * Get customer's product reviews list - * - * @return \Magento\Framework\View\Result\Layout - */ - public function execute() - { - $customerId = $this->initCurrentCustomer(); - $resultLayout = $this->resultLayoutFactory->create(); - $block = $resultLayout->getLayout()->getBlock('admin.customer.reviews'); - $block->setCustomerId($customerId)->setUseAjax(true); - return $resultLayout; - } -} diff --git a/app/code/Magento/Customer/Controller/Adminhtml/Index/Save.php b/app/code/Magento/Customer/Controller/Adminhtml/Index/Save.php index b85b735ea9c4f..a65bfa5d77f9e 100644 --- a/app/code/Magento/Customer/Controller/Adminhtml/Index/Save.php +++ b/app/code/Magento/Customer/Controller/Adminhtml/Index/Save.php @@ -348,8 +348,14 @@ public function execute() ['customer' => $customer, 'request' => $this->getRequest()] ); - if (isset($customerData['sendemail_store_id'])) { + if (isset($customerData['sendemail_store_id']) && $customerData['sendemail_store_id'] !== false) { $customer->setStoreId($customerData['sendemail_store_id']); + try { + $this->customerAccountManagement->validateCustomerStoreIdByWebsiteId($customer); + } catch (LocalizedException $exception) { + throw new LocalizedException(__("The Store View selected for sending Welcome email from". + " is not related to the customer's associated website.")); + } } // Save customer diff --git a/app/code/Magento/Customer/Controller/Plugin/Account.php b/app/code/Magento/Customer/Controller/Plugin/Account.php index 179da148e7f78..bbdb58d626108 100644 --- a/app/code/Magento/Customer/Controller/Plugin/Account.php +++ b/app/code/Magento/Customer/Controller/Plugin/Account.php @@ -3,21 +3,31 @@ * Copyright © Magento, Inc. All rights reserved. * See COPYING.txt for license details. */ +declare(strict_types=1); + namespace Magento\Customer\Controller\Plugin; +use Closure; +use Magento\Customer\Controller\AccountInterface; use Magento\Customer\Model\Session; -use Magento\Framework\App\ActionInterface; use Magento\Framework\App\RequestInterface; use Magento\Framework\App\ResponseInterface; -use Magento\Framework\App\Action\AbstractAction; use Magento\Framework\Controller\ResultInterface; +/** + * Plugin verifies permissions using Action Name against injected (`fontend/di.xml`) rules + */ class Account { /** * @var Session */ - protected $session; + private $session; + + /** + * @var RequestInterface + */ + private $request; /** * @var array @@ -25,50 +35,46 @@ class Account private $allowedActions = []; /** + * @param RequestInterface $request * @param Session $customerSession * @param array $allowedActions List of actions that are allowed for not authorized users */ public function __construct( + RequestInterface $request, Session $customerSession, array $allowedActions = [] ) { + $this->request = $request; $this->session = $customerSession; $this->allowedActions = $allowedActions; } /** - * Dispatch actions allowed for not authorized users + * Executes original method if allowed, otherwise - redirects to log in * - * @param AbstractAction $subject - * @param RequestInterface $request - * @return void + * @param AccountInterface $controllerAction + * @param Closure $proceed + * @return ResultInterface|ResponseInterface|void + * @SuppressWarnings(PHPMD.UnusedFormalParameter) */ - public function beforeDispatch(AbstractAction $subject, RequestInterface $request) + public function aroundExecute(AccountInterface $controllerAction, Closure $proceed) { - $action = strtolower($request->getActionName()); - $pattern = '/^(' . implode('|', $this->allowedActions) . ')$/i'; - - if (!preg_match($pattern, $action)) { - if (!$this->session->authenticate()) { - $subject->getActionFlag()->set('', ActionInterface::FLAG_NO_DISPATCH, true); - } - } else { - $this->session->setNoReferer(true); + /** @FIXME Move Authentication and redirect out of Session model */ + if ($this->isActionAllowed() || $this->session->authenticate()) { + return $proceed(); } } /** - * Remove No-referer flag from customer session + * Validates whether currently requested action is one of the allowed * - * @param AbstractAction $subject - * @param ResponseInterface|ResultInterface $result - * @param RequestInterface $request - * @return ResponseInterface|ResultInterface - * @SuppressWarnings(PHPMD.UnusedFormalParameter) + * @return bool */ - public function afterDispatch(AbstractAction $subject, $result, RequestInterface $request) + private function isActionAllowed(): bool { - $this->session->unsNoReferer(false); - return $result; + $action = strtolower($this->request->getActionName()); + $pattern = '/^(' . implode('|', $this->allowedActions) . ')$/i'; + + return (bool)preg_match($pattern, $action); } } diff --git a/app/code/Magento/Customer/Controller/Review.php b/app/code/Magento/Customer/Controller/Review.php deleted file mode 100644 index 3a26f58c9f7d7..0000000000000 --- a/app/code/Magento/Customer/Controller/Review.php +++ /dev/null @@ -1,37 +0,0 @@ -<?php -/** - * Copyright © Magento, Inc. All rights reserved. - * See COPYING.txt for license details. - */ -namespace Magento\Customer\Controller; - -use Magento\Framework\App\Action\Context; -use Magento\Framework\View\Result\PageFactory; - -class Review extends \Magento\Framework\App\Action\Action -{ - /** - * @var PageFactory - */ - protected $resultPageFactory; - - /** - * @param Context $context - * @param PageFactory $resultPageFactory - */ - public function __construct( - Context $context, - PageFactory $resultPageFactory - ) { - parent::__construct($context); - $this->resultPageFactory = $resultPageFactory; - } - - /** - * @return \Magento\Framework\View\Result\Page - */ - public function execute() - { - return $this->resultPageFactory->create(); - } -} diff --git a/app/code/Magento/Customer/Controller/Review/Index.php b/app/code/Magento/Customer/Controller/Review/Index.php deleted file mode 100644 index e11dea17d4df7..0000000000000 --- a/app/code/Magento/Customer/Controller/Review/Index.php +++ /dev/null @@ -1,11 +0,0 @@ -<?php -/** - * - * Copyright © Magento, Inc. All rights reserved. - * See COPYING.txt for license details. - */ -namespace Magento\Customer\Controller\Review; - -class Index extends \Magento\Customer\Controller\Review -{ -} diff --git a/app/code/Magento/Customer/Controller/Review/View.php b/app/code/Magento/Customer/Controller/Review/View.php deleted file mode 100644 index 679452de680f2..0000000000000 --- a/app/code/Magento/Customer/Controller/Review/View.php +++ /dev/null @@ -1,11 +0,0 @@ -<?php -/** - * - * Copyright © Magento, Inc. All rights reserved. - * See COPYING.txt for license details. - */ -namespace Magento\Customer\Controller\Review; - -class View extends \Magento\Customer\Controller\Review -{ -} diff --git a/app/code/Magento/Customer/Controller/Section/Load.php b/app/code/Magento/Customer/Controller/Section/Load.php index 37cd071b13623..6c3aa06b9f022 100644 --- a/app/code/Magento/Customer/Controller/Section/Load.php +++ b/app/code/Magento/Customer/Controller/Section/Load.php @@ -78,8 +78,8 @@ public function execute() $response = $this->sectionPool->getSectionsData($sectionNames, (bool)$forceNewSectionTimestamp); } catch (\Exception $e) { $resultJson->setStatusHeader( - \Zend\Http\Response::STATUS_CODE_400, - \Zend\Http\AbstractMessage::VERSION_11, + \Laminas\Http\Response::STATUS_CODE_400, + \Laminas\Http\AbstractMessage::VERSION_11, 'Bad Request' ); $response = ['message' => $this->escaper->escapeHtml($e->getMessage())]; diff --git a/app/code/Magento/Customer/CustomerData/SectionConfigConverter.php b/app/code/Magento/Customer/CustomerData/SectionConfigConverter.php index 5f4bbb03d3936..51a063b5562bb 100644 --- a/app/code/Magento/Customer/CustomerData/SectionConfigConverter.php +++ b/app/code/Magento/Customer/CustomerData/SectionConfigConverter.php @@ -5,6 +5,9 @@ */ namespace Magento\Customer\CustomerData; +/** + * Class that receives xml merged source and process it. + */ class SectionConfigConverter implements \Magento\Framework\Config\ConverterInterface { /** @@ -13,7 +16,7 @@ class SectionConfigConverter implements \Magento\Framework\Config\ConverterInter const INVALIDATE_ALL_SECTIONS_MARKER = '*'; /** - * {@inheritdoc} + * @inheritdoc */ public function convert($source) { @@ -24,7 +27,7 @@ public function convert($source) $sections[$actionName][] = strtolower($section->getAttribute('name')); } if (!isset($sections[$actionName])) { - $sections[$actionName] = self::INVALIDATE_ALL_SECTIONS_MARKER; + $sections[$actionName][] = self::INVALIDATE_ALL_SECTIONS_MARKER; } } return [ diff --git a/app/code/Magento/Customer/Model/Account/Redirect.php b/app/code/Magento/Customer/Model/Account/Redirect.php index 2ccaaea45680c..0389a380b36dc 100644 --- a/app/code/Magento/Customer/Model/Account/Redirect.php +++ b/app/code/Magento/Customer/Model/Account/Redirect.php @@ -7,24 +7,28 @@ use Magento\Customer\Model\Session; use Magento\Customer\Model\Url as CustomerUrl; +use Magento\Framework\App\Config\ScopeConfigInterface; +use Magento\Framework\App\ObjectManager; use Magento\Framework\App\RequestInterface; +use Magento\Framework\Controller\Result\Forward as ResultForward; +use Magento\Framework\Controller\Result\Redirect as ResultRedirect; use Magento\Framework\Controller\ResultFactory; +use Magento\Framework\Stdlib\Cookie\CookieMetadataFactory; +use Magento\Framework\Stdlib\CookieManagerInterface; +use Magento\Framework\Url\DecoderInterface; use Magento\Framework\Url\HostChecker; use Magento\Framework\UrlInterface; use Magento\Store\Model\ScopeInterface; use Magento\Store\Model\StoreManagerInterface; -use Magento\Framework\App\Config\ScopeConfigInterface; -use Magento\Framework\Controller\Result\Redirect as ResultRedirect; -use Magento\Framework\Controller\Result\Forward as ResultForward; -use Magento\Framework\Url\DecoderInterface; -use Magento\Framework\App\ObjectManager; -use Magento\Framework\Stdlib\CookieManagerInterface; /** + * Account Redirect * @SuppressWarnings(PHPMD.CouplingBetweenObjects) + * @SuppressWarnings(PHPMD.CookieAndSessionMisuse) */ class Redirect { + /** URL to redirect user on successful login or registration */ const LOGIN_REDIRECT_URL = 'login_redirect'; @@ -64,10 +68,15 @@ class Redirect */ protected $resultFactory; + /** + * @var CookieMetadataFactory + */ + protected $cookieMetadataFactory; + /** * @var CookieManagerInterface */ - protected $cookieManager; + private $cookieManager; /** * @var HostChecker @@ -80,6 +89,7 @@ class Redirect private $session; /** + * @SuppressWarnings(PHPMD.ExcessiveParameterList) * @param RequestInterface $request * @param Session $customerSession * @param ScopeConfigInterface $scopeConfig @@ -88,6 +98,7 @@ class Redirect * @param DecoderInterface $urlDecoder * @param CustomerUrl $customerUrl * @param ResultFactory $resultFactory + * @param CookieMetadataFactory $cookieMetadataFactory * @param HostChecker|null $hostChecker */ public function __construct( @@ -99,6 +110,7 @@ public function __construct( DecoderInterface $urlDecoder, CustomerUrl $customerUrl, ResultFactory $resultFactory, + CookieMetadataFactory $cookieMetadataFactory, HostChecker $hostChecker = null ) { $this->request = $request; @@ -108,6 +120,7 @@ public function __construct( $this->url = $url; $this->urlDecoder = $urlDecoder; $this->customerUrl = $customerUrl; + $this->cookieMetadataFactory = $cookieMetadataFactory; $this->resultFactory = $resultFactory; $this->hostChecker = $hostChecker ?: ObjectManager::getInstance()->get(HostChecker::class); } @@ -238,7 +251,8 @@ private function applyRedirect($url) /** * Get Cookie manager. For release backward compatibility. * - * @deprecated 100.0.10 + * @deprecated 100.0.10 This is legacy method to pass login_redirect cookie + * @see Magento/Checkout/view/frontend/web/js/sidebar.js * @return CookieManagerInterface */ protected function getCookieManager() @@ -252,7 +266,8 @@ protected function getCookieManager() /** * Set cookie manager. For unit tests. * - * @deprecated 100.0.10 + * @deprecated 100.0.10 This is legacy method to pass login_redirect cookie + * @see Magento/Checkout/view/frontend/web/js/sidebar.js * @param object $value * @return void */ @@ -264,6 +279,8 @@ public function setCookieManager($value) /** * Get redirect route from cookie for case of successful login/registration * + * @deprecated 100.0.10 This is legacy method to pass login_redirect cookie + * @see Magento/Checkout/view/frontend/web/js/sidebar.js * @return null|string */ public function getRedirectCookie() @@ -274,21 +291,36 @@ public function getRedirectCookie() /** * Save redirect route to cookie for case of successful login/registration * + * @deprecated 100.0.10 This is legacy method to pass login_redirect cookie + * @see Magento/Checkout/view/frontend/web/js/sidebar.js * @param string $route * @return void */ public function setRedirectCookie($route) { - $this->getCookieManager()->setPublicCookie(self::LOGIN_REDIRECT_URL, $route); + $this->getCookieManager()->setPublicCookie( + self::LOGIN_REDIRECT_URL, + $route, + $this->cookieMetadataFactory->createPublicCookieMetadata() + ->setHttpOnly(true) + ->setDuration(3600) + ->setPath($this->storeManager->getStore()->getStorePath()) + ); } /** * Clear cookie with requested route * + * @deprecated 100.0.10 This is legacy method to pass login_redirect cookie + * @see Magento/Checkout/view/frontend/web/js/sidebar.js * @return void */ public function clearRedirectCookie() { - $this->getCookieManager()->deleteCookie(self::LOGIN_REDIRECT_URL); + $this->getCookieManager()->deleteCookie( + self::LOGIN_REDIRECT_URL, + $this->cookieMetadataFactory->createPublicCookieMetadata() + ->setPath($this->storeManager->getStore()->getStorePath()) + ); } } diff --git a/app/code/Magento/Customer/Model/AccountManagement.php b/app/code/Magento/Customer/Model/AccountManagement.php index 985cfe0621bf8..122a062beeff8 100644 --- a/app/code/Magento/Customer/Model/AccountManagement.php +++ b/app/code/Magento/Customer/Model/AccountManagement.php @@ -68,49 +68,55 @@ class AccountManagement implements AccountManagementInterface /** * Configuration paths for create account email template * - * @deprecated + * @deprecated Get rid of Helpers in Password Security Management + * @see EmailNotification::XML_PATH_REGISTER_EMAIL_TEMPLATE */ const XML_PATH_REGISTER_EMAIL_TEMPLATE = 'customer/create_account/email_template'; /** * Configuration paths for register no password email template * - * @deprecated + * @deprecated Get rid of Helpers in Password Security Management + * @see EmailNotification::XML_PATH_REGISTER_EMAIL_TEMPLATE */ const XML_PATH_REGISTER_NO_PASSWORD_EMAIL_TEMPLATE = 'customer/create_account/email_no_password_template'; /** * Configuration paths for remind email identity * - * @deprecated + * @deprecated Get rid of Helpers in Password Security Management + * @see EmailNotification::XML_PATH_REGISTER_EMAIL_TEMPLATE */ const XML_PATH_REGISTER_EMAIL_IDENTITY = 'customer/create_account/email_identity'; /** * Configuration paths for remind email template * - * @deprecated + * @deprecated Get rid of Helpers in Password Security Management + * @see EmailNotification::XML_PATH_REGISTER_EMAIL_TEMPLATE */ const XML_PATH_REMIND_EMAIL_TEMPLATE = 'customer/password/remind_email_template'; /** * Configuration paths for forgot email email template * - * @deprecated + * @deprecated Get rid of Helpers in Password Security Management + * @see EmailNotification::XML_PATH_REGISTER_EMAIL_TEMPLATE */ const XML_PATH_FORGOT_EMAIL_TEMPLATE = 'customer/password/forgot_email_template'; /** * Configuration paths for forgot email identity * - * @deprecated + * @deprecated Get rid of Helpers in Password Security Management + * @see EmailNotification::XML_PATH_REGISTER_EMAIL_TEMPLATE */ const XML_PATH_FORGOT_EMAIL_IDENTITY = 'customer/password/forgot_email_identity'; /** * Configuration paths for account confirmation required * - * @deprecated + * @deprecated Get rid of Helpers in Password Security Management * @see AccountConfirmation::XML_PATH_IS_CONFIRM */ const XML_PATH_IS_CONFIRM = 'customer/create_account/confirm'; @@ -118,42 +124,48 @@ class AccountManagement implements AccountManagementInterface /** * Configuration paths for account confirmation email template * - * @deprecated + * @deprecated Get rid of Helpers in Password Security Management + * @see EmailNotification::XML_PATH_REGISTER_EMAIL_TEMPLATE */ const XML_PATH_CONFIRM_EMAIL_TEMPLATE = 'customer/create_account/email_confirmation_template'; /** * Configuration paths for confirmation confirmed email template * - * @deprecated + * @deprecated Get rid of Helpers in Password Security Management + * @see EmailNotification::XML_PATH_REGISTER_EMAIL_TEMPLATE */ const XML_PATH_CONFIRMED_EMAIL_TEMPLATE = 'customer/create_account/email_confirmed_template'; /** * Constants for the type of new account email to be sent * - * @deprecated + * @deprecated Get rid of Helpers in Password Security Management + * @see EmailNotificationInterface::NEW_ACCOUNT_EMAIL_REGISTERED */ const NEW_ACCOUNT_EMAIL_REGISTERED = 'registered'; /** * Welcome email, when password setting is required * - * @deprecated + * @deprecated Get rid of Helpers in Password Security Management + * @see EmailNotificationInterface::NEW_ACCOUNT_EMAIL_REGISTERED */ const NEW_ACCOUNT_EMAIL_REGISTERED_NO_PASSWORD = 'registered_no_password'; /** * Welcome email, when confirmation is enabled * - * @deprecated + * @deprecated Get rid of Helpers in Password Security Management + * @see EmailNotificationInterface::NEW_ACCOUNT_EMAIL_REGISTERED */ const NEW_ACCOUNT_EMAIL_CONFIRMATION = 'confirmation'; /** * Confirmation email, when account is confirmed * - * @deprecated + * @deprecated Get rid of Helpers in Password Security Management + * @see EmailNotificationInterface::NEW_ACCOUNT_EMAIL_REGISTERED */ const NEW_ACCOUNT_EMAIL_CONFIRMED = 'confirmed'; @@ -179,14 +191,15 @@ class AccountManagement implements AccountManagementInterface /** * Configuration path to customer reset password email template * - * @deprecated + * @deprecated Get rid of Helpers in Password Security Management + * @see Magento/Customer/Model/EmailNotification::XML_PATH_REGISTER_EMAIL_TEMPLATE */ const XML_PATH_RESET_PASSWORD_TEMPLATE = 'customer/password/reset_password_template'; /** * Minimum password length * - * @deprecated + * @deprecated Get rid of Helpers in Password Security Management */ const MIN_PASSWORD_LENGTH = 6; @@ -526,6 +539,7 @@ public function resendConfirmation($email, $websiteId = null, $redirectUrl = '') // If we are not able to send a new account email, this should be ignored $this->logger->critical($e); } + return true; } /** @@ -671,17 +685,16 @@ public function initiatePasswordReset($email, $template, $websiteId = null) */ private function handleUnknownTemplate($template) { - throw new InputException( - __( - 'Invalid value of "%value" provided for the %fieldName field. Possible values: %template1 or %template2.', - [ - 'value' => $template, - 'fieldName' => 'template', - 'template1' => AccountManagement::EMAIL_REMINDER, - 'template2' => AccountManagement::EMAIL_RESET - ] - ) + $phrase = __( + 'Invalid value of "%value" provided for the %fieldName field. Possible values: %template1 or %template2.', + [ + 'value' => $template, + 'fieldName' => 'template', + 'template1' => AccountManagement::EMAIL_REMINDER, + 'template2' => AccountManagement::EMAIL_RESET + ] ); + throw new InputException($phrase); } /** @@ -713,12 +726,6 @@ public function resetPassword($email, $resetToken, $newPassword) $customerSecure->setRpTokenCreatedAt(null); $customerSecure->setPasswordHash($this->createPasswordHash($newPassword)); $this->destroyCustomerSessions($customer->getId()); - if ($this->sessionManager->isSessionExists()) { - //delete old session and move data to the new session - //use this instead of $this->sessionManager->regenerateId because last one doesn't delete old session - // phpcs:ignore Magento2.Functions.DiscouragedFunction - session_regenerate_id(true); - } $this->customerRepository->save($customer); return true; @@ -865,7 +872,6 @@ public function createAccountWithPasswordHash(CustomerInterface $customer, $hash if ($customer->getId()) { $customer = $this->customerRepository->get($customer->getEmail()); $websiteId = $customer->getWebsiteId(); - if ($this->isCustomerInStore($websiteId, $customer->getStoreId())) { throw new InputException(__('This customer already exists in this store.')); } @@ -889,13 +895,10 @@ public function createAccountWithPasswordHash(CustomerInterface $customer, $hash $customer->setWebsiteId($websiteId); } + $this->validateCustomerStoreIdByWebsiteId($customer); + // Update 'created_in' value with actual store name if ($customer->getId() === null) { - $websiteId = $customer->getWebsiteId(); - if ($websiteId && !$this->isCustomerInStore($websiteId, $customer->getStoreId())) { - throw new LocalizedException(__('The store view is not in the associated website.')); - } - $storeName = $this->storeManager->getStore($customer->getStoreId())->getName(); $customer->setCreatedIn($storeName); } @@ -1042,10 +1045,10 @@ private function changePasswordForCustomer($customer, $currentPassword, $newPass } $customerEmail = $customer->getEmail(); $this->credentialsValidator->checkPasswordDifferentFromEmail($customerEmail, $newPassword); + $this->checkPasswordStrength($newPassword); $customerSecure = $this->customerRegistry->retrieveSecureData($customer->getId()); $customerSecure->setRpToken(null); $customerSecure->setRpTokenCreatedAt(null); - $this->checkPasswordStrength($newPassword); $customerSecure->setPasswordHash($this->createPasswordHash($newPassword)); $this->destroyCustomerSessions($customer->getId()); $this->disableAddressValidation($customer); @@ -1137,6 +1140,22 @@ public function isCustomerInStore($customerWebsiteId, $storeId) return in_array($storeId, $ids); } + /** + * Validate customer store id by customer website id. + * + * @param CustomerInterface $customer + * @return bool + * @throws LocalizedException + */ + public function validateCustomerStoreIdByWebsiteId(CustomerInterface $customer) + { + if (!$this->isCustomerInStore($customer->getWebsiteId(), $customer->getStoreId())) { + throw new LocalizedException(__('The store view is not in the associated website.')); + } + + return true; + } + /** * Validate the Reset Password Token for a customer. * @@ -1611,6 +1630,7 @@ private function getEmailNotification() */ private function destroyCustomerSessions($customerId) { + $this->sessionManager->regenerateId(); $sessionLifetime = $this->scopeConfig->getValue( \Magento\Framework\Session\Config::XML_PATH_COOKIE_LIFETIME, \Magento\Store\Model\ScopeInterface::SCOPE_STORE diff --git a/app/code/Magento/Customer/Model/Address/AbstractAddress.php b/app/code/Magento/Customer/Model/Address/AbstractAddress.php index 158461b4d9c17..fb067decd0b37 100644 --- a/app/code/Magento/Customer/Model/Address/AbstractAddress.php +++ b/app/code/Magento/Customer/Model/Address/AbstractAddress.php @@ -281,7 +281,13 @@ public function setData($key, $value = null) $key = $this->_implodeArrayField($key); } elseif (is_array($value) && $this->isAddressMultilineAttribute($key)) { $value = $this->_implodeArrayValues($value); + } elseif (self::CUSTOM_ATTRIBUTES === $key && is_array($value)) { + foreach ($value as &$attribute) { + $attribute = is_array($attribute) ? $attribute : $attribute->__toArray(); + $attribute = $this->processCustomAttribute($attribute); + } } + return parent::setData($key, $value); } @@ -637,8 +643,8 @@ public function unsRegion() * Is company required * * @return bool - * @since 100.2.0 * @throws \Magento\Framework\Exception\LocalizedException + * @since 100.2.0 */ protected function isCompanyRequired() { @@ -649,8 +655,8 @@ protected function isCompanyRequired() * Is telephone required * * @return bool - * @since 100.2.0 * @throws \Magento\Framework\Exception\LocalizedException + * @since 100.2.0 */ protected function isTelephoneRequired() { @@ -661,11 +667,30 @@ protected function isTelephoneRequired() * Is fax required * * @return bool - * @since 100.2.0 * @throws \Magento\Framework\Exception\LocalizedException + * @since 100.2.0 */ protected function isFaxRequired() { return ($this->_eavConfig->getAttribute('customer_address', 'fax')->getIsRequired()); } + + /** + * Unify attribute format. + * + * @param array $attribute + * @return array + */ + private function processCustomAttribute(array $attribute): array + { + if (isset($attribute['attribute_code']) && + isset($attribute['value']) && + is_array($attribute['value']) && + $this->isAddressMultilineAttribute($attribute['attribute_code']) + ) { + $attribute['value'] = $this->_implodeArrayValues($attribute['value']); + } + + return $attribute; + } } diff --git a/app/code/Magento/Customer/Model/Address/CompositeValidator.php b/app/code/Magento/Customer/Model/Address/CompositeValidator.php index 1d16a929532f5..4c77f10c11de4 100644 --- a/app/code/Magento/Customer/Model/Address/CompositeValidator.php +++ b/app/code/Magento/Customer/Model/Address/CompositeValidator.php @@ -30,11 +30,11 @@ public function __construct( */ public function validate(AbstractAddress $address) { - $errors = []; + $errors = [[]]; foreach ($this->validators as $validator) { - $errors = array_merge($errors, $validator->validate($address)); + $errors[] = $validator->validate($address); } - return $errors; + return array_merge(...$errors); } } diff --git a/app/code/Magento/Customer/Model/Address/Validator/General.php b/app/code/Magento/Customer/Model/Address/Validator/General.php index 7cbb6ef1ab623..4888cd227db48 100644 --- a/app/code/Magento/Customer/Model/Address/Validator/General.php +++ b/app/code/Magento/Customer/Model/Address/Validator/General.php @@ -5,6 +5,7 @@ */ namespace Magento\Customer\Model\Address\Validator; +use Magento\Customer\Api\AddressMetadataInterface; use Magento\Customer\Model\Address\AbstractAddress; use Magento\Customer\Model\Address\ValidatorInterface; @@ -87,6 +88,7 @@ private function checkRequiredFields(AbstractAddress $address) */ private function checkOptionalFields(AbstractAddress $address) { + $this->reloadAddressAttributes($address); $errors = []; if ($this->isTelephoneRequired() && !\Zend_Validate::is($address->getTelephone(), 'NotEmpty') @@ -148,4 +150,17 @@ private function isFaxRequired() { return $this->eavConfig->getAttribute('customer_address', 'fax')->getIsRequired(); } + + /** + * Reload address attributes for the certain store + * + * @param AbstractAddress $address + * @return void + */ + private function reloadAddressAttributes(AbstractAddress $address): void + { + $attributeSetId = $address->getAttributeSetId() ?: AddressMetadataInterface::ATTRIBUTE_SET_ID_ADDRESS; + $address->setData('attribute_set_id', $attributeSetId); + $this->eavConfig->getEntityAttributes(AddressMetadataInterface::ENTITY_TYPE_ADDRESS, $address); + } } diff --git a/app/code/Magento/Customer/Model/App/Action/ContextPlugin.php b/app/code/Magento/Customer/Model/App/Action/ContextPlugin.php index b8c83551ee381..5d926b47ca446 100644 --- a/app/code/Magento/Customer/Model/App/Action/ContextPlugin.php +++ b/app/code/Magento/Customer/Model/App/Action/ContextPlugin.php @@ -8,13 +8,12 @@ use Magento\Customer\Model\Context; use Magento\Customer\Model\GroupManagement; -use Magento\Framework\App\Action\AbstractAction; -use Magento\Framework\App\RequestInterface; use Magento\Customer\Model\Session; +use Magento\Framework\App\ActionInterface; use Magento\Framework\App\Http\Context as HttpContext; /** - * Class ContextPlugin + * Introduces Context information for ActionInterface of Customer Action */ class ContextPlugin { @@ -41,12 +40,11 @@ public function __construct(Session $customerSession, HttpContext $httpContext) /** * Set customer group and customer session id to HTTP context * - * @param AbstractAction $subject - * @param RequestInterface $request + * @param ActionInterface $subject * @return void * @SuppressWarnings(PHPMD.UnusedFormalParameter) */ - public function beforeDispatch(AbstractAction $subject, RequestInterface $request) + public function beforeExecute(ActionInterface $subject) { $this->httpContext->setValue( Context::CONTEXT_GROUP, diff --git a/app/code/Magento/Customer/Model/AttributeMetadataResolver.php b/app/code/Magento/Customer/Model/AttributeMetadataResolver.php index c936de1bd0230..27c5f77674577 100644 --- a/app/code/Magento/Customer/Model/AttributeMetadataResolver.php +++ b/app/code/Magento/Customer/Model/AttributeMetadataResolver.php @@ -6,17 +6,17 @@ */ namespace Magento\Customer\Model; +use Magento\Customer\Api\Data\AddressInterface; +use Magento\Customer\Api\Data\CustomerInterface; +use Magento\Customer\Model\Config\Share as ShareConfig; use Magento\Customer\Model\ResourceModel\Address\Attribute\Source\CountryWithWebsites; +use Magento\Eav\Api\Data\AttributeInterface; use Magento\Eav\Model\Entity\Attribute\AbstractAttribute; -use Magento\Customer\Api\Data\AddressInterface; -use Magento\Ui\DataProvider\EavValidationRules; -use Magento\Ui\Component\Form\Field; use Magento\Eav\Model\Entity\Type; -use Magento\Eav\Api\Data\AttributeInterface; +use Magento\Framework\App\ObjectManager; use Magento\Framework\View\Element\UiComponent\ContextInterface; -use Magento\Customer\Api\Data\CustomerInterface; -use Magento\Customer\Model\Config\Share as ShareConfig; -use Magento\Customer\Model\FileUploaderDataResolver; +use Magento\Ui\Component\Form\Field; +use Magento\Ui\DataProvider\EavValidationRules; /** * Class to build meta data of the customer or customer address attribute @@ -75,25 +75,33 @@ class AttributeMetadataResolver */ private $shareConfig; + /** + * @var GroupManagement + */ + private $groupManagement; + /** * @param CountryWithWebsites $countryWithWebsiteSource * @param EavValidationRules $eavValidationRules * @param FileUploaderDataResolver $fileUploaderDataResolver * @param ContextInterface $context * @param ShareConfig $shareConfig + * @param GroupManagement|null $groupManagement */ public function __construct( CountryWithWebsites $countryWithWebsiteSource, EavValidationRules $eavValidationRules, FileUploaderDataResolver $fileUploaderDataResolver, ContextInterface $context, - ShareConfig $shareConfig + ShareConfig $shareConfig, + ?GroupManagement $groupManagement = null ) { $this->countryWithWebsiteSource = $countryWithWebsiteSource; $this->eavValidationRules = $eavValidationRules; $this->fileUploaderDataResolver = $fileUploaderDataResolver; $this->context = $context; $this->shareConfig = $shareConfig; + $this->groupManagement = $groupManagement ?? ObjectManager::getInstance()->get(GroupManagement::class); } /** @@ -111,6 +119,7 @@ public function getAttributesMeta( bool $allowToShowHiddenAttributes ): array { $meta = $this->modifyBooleanAttributeMeta($attribute); + $this->modifyGroupAttributeMeta($attribute); // use getDataUsingMethod, since some getters are defined and apply additional processing of returning value foreach (self::$metaProperties as $metaName => $origName) { $value = $attribute->getDataUsingMethod($origName); @@ -196,6 +205,21 @@ private function modifyBooleanAttributeMeta(AttributeInterface $attribute): arra return $meta; } + /** + * Modify group attribute meta data + * + * @param AttributeInterface $attribute + * @return void + */ + private function modifyGroupAttributeMeta(AttributeInterface $attribute): void + { + if ($attribute->getAttributeCode() === 'group_id') { + $defaultGroup = $this->groupManagement->getDefaultGroup(); + $defaultGroupId = !empty($defaultGroup) ? $defaultGroup->getId() : null; + $attribute->setDataUsingMethod(self::$metaProperties['default'], $defaultGroupId); + } + } + /** * Add global scope parameter and filter options to website meta * diff --git a/app/code/Magento/Customer/Model/Customer.php b/app/code/Magento/Customer/Model/Customer.php index 2692d1edf0143..ea52994735c63 100644 --- a/app/code/Magento/Customer/Model/Customer.php +++ b/app/code/Magento/Customer/Model/Customer.php @@ -21,6 +21,7 @@ use Magento\Store\Model\ScopeInterface; use Magento\Framework\App\ObjectManager; use Magento\Framework\Math\Random; +use Magento\Framework\Indexer\IndexerInterface; /** * Customer model @@ -62,8 +63,7 @@ class Customer extends \Magento\Framework\Model\AbstractModel const XML_PATH_RESET_PASSWORD_TEMPLATE = 'customer/password/reset_password_template'; /** - * @deprecated - * @see AccountConfirmation::XML_PATH_IS_CONFIRM + * @deprecated @see \Magento\Customer\Model\AccountConfirmation::XML_PATH_IS_CONFIRM */ const XML_PATH_IS_CONFIRM = 'customer/create_account/confirm'; @@ -227,6 +227,11 @@ class Customer extends \Magento\Framework\Model\AbstractModel */ private $storedAddress; + /** + * @var IndexerInterface|null + */ + private $indexer; + /** * @param \Magento\Framework\Model\Context $context * @param \Magento\Framework\Registry $registry @@ -304,6 +309,19 @@ public function __construct( ); } + /** + * Micro-caching optimization + * + * @return IndexerInterface + */ + private function getIndexer() : IndexerInterface + { + if ($this->indexer === null) { + $this->indexer = $this->indexerRegistry->get(self::CUSTOMER_GRID_INDEXER_ID); + } + return $this->indexer; + } + /** * Initialize customer model * @@ -985,6 +1003,7 @@ public function getSharedWebsiteIds() */ public function getAttributeSetId() { + // phpstan:ignore "Call to an undefined static method*" return parent::getAttributeSetId() ?: CustomerMetadataInterface::ATTRIBUTE_SET_ID_CUSTOMER; } @@ -1075,8 +1094,7 @@ public function resetErrors() */ public function afterSave() { - $indexer = $this->indexerRegistry->get(self::CUSTOMER_GRID_INDEXER_ID); - if ($indexer->getState()->getStatus() == StateInterface::STATUS_VALID) { + if ($this->getIndexer()->getState()->getStatus() == StateInterface::STATUS_VALID) { $this->_getResource()->addCommitCallback([$this, 'reindex']); } return parent::afterSave(); @@ -1100,9 +1118,7 @@ public function afterDeleteCommit() */ public function reindex() { - /** @var \Magento\Framework\Indexer\IndexerInterface $indexer */ - $indexer = $this->indexerRegistry->get(self::CUSTOMER_GRID_INDEXER_ID); - $indexer->reindexRow($this->getId()); + $this->getIndexer()->reindexRow($this->getId()); } /** diff --git a/app/code/Magento/Customer/Model/CustomerRegistry.php b/app/code/Magento/Customer/Model/CustomerRegistry.php index b89cce8957487..d68904f6d1645 100644 --- a/app/code/Magento/Customer/Model/CustomerRegistry.php +++ b/app/code/Magento/Customer/Model/CustomerRegistry.php @@ -195,7 +195,7 @@ public function removeByEmail($customerEmail, $websiteId = null) $websiteId = $this->storeManager->getStore()->getWebsiteId(); } $emailKey = $this->getEmailKey($customerEmail, $websiteId); - if ($emailKey) { + if (isset($this->customerRegistryByEmail[$emailKey])) { /** @var Customer $customer */ $customer = $this->customerRegistryByEmail[$emailKey]; unset($this->customerRegistryByEmail[$emailKey]); diff --git a/app/code/Magento/Customer/Model/Data/Customer.php b/app/code/Magento/Customer/Model/Data/Customer.php index 7a135904f6663..f4318d68f6aa4 100644 --- a/app/code/Magento/Customer/Model/Data/Customer.php +++ b/app/code/Magento/Customer/Model/Data/Customer.php @@ -9,7 +9,8 @@ use \Magento\Framework\Api\AttributeValueFactory; /** - * Class Customer + * Customer data model + * * @SuppressWarnings(PHPMD.ExcessivePublicCount) */ class Customer extends \Magento\Framework\Api\AbstractExtensibleObject implements @@ -39,7 +40,7 @@ public function __construct( } /** - * {@inheritdoc} + * @inheritdoc */ protected function getCustomAttributesCodes() { @@ -50,6 +51,8 @@ protected function getCustomAttributesCodes() } /** + * Get default billing address id + * * @return string|null */ public function getDefaultBilling() @@ -150,7 +153,7 @@ public function getGender() /** * Get group id * - * @return string|null + * @return int|null */ public function getGroupId() { @@ -489,7 +492,7 @@ public function setDisableAutoGroupChange($disableAutoGroupChange) } /** - * {@inheritdoc} + * @inheritdoc * * @return \Magento\Customer\Api\Data\CustomerExtensionInterface|null */ @@ -499,7 +502,7 @@ public function getExtensionAttributes() } /** - * {@inheritdoc} + * @inheritdoc * * @param \Magento\Customer\Api\Data\CustomerExtensionInterface $extensionAttributes * @return $this diff --git a/app/code/Magento/Customer/Model/EmailNotification.php b/app/code/Magento/Customer/Model/EmailNotification.php index 432317444f4b7..b72929a9de3a5 100644 --- a/app/code/Magento/Customer/Model/EmailNotification.php +++ b/app/code/Magento/Customer/Model/EmailNotification.php @@ -3,6 +3,7 @@ * Copyright © Magento, Inc. All rights reserved. * See COPYING.txt for license details. */ +declare(strict_types=1); namespace Magento\Customer\Model; @@ -15,6 +16,8 @@ use Magento\Customer\Api\Data\CustomerInterface; use Magento\Framework\Reflection\DataObjectProcessor; use Magento\Framework\Exception\LocalizedException; +use Magento\Store\Model\ScopeInterface; +use Magento\Customer\Model\Data\CustomerSecure; /** * Customer email notification @@ -124,7 +127,7 @@ public function __construct( $this->customerViewHelper = $customerViewHelper; $this->dataProcessor = $dataProcessor; $this->scopeConfig = $scopeConfig; - $this->senderResolver = $senderResolver ?: ObjectManager::getInstance()->get(SenderResolverInterface::class); + $this->senderResolver = $senderResolver ?? ObjectManager::getInstance()->get(SenderResolverInterface::class); } /** @@ -139,7 +142,7 @@ public function credentialsChanged( CustomerInterface $savedCustomer, $origCustomerEmail, $isPasswordChanged = false - ) { + ): void { if ($origCustomerEmail != $savedCustomer->getEmail()) { if ($isPasswordChanged) { $this->emailAndPasswordChanged($savedCustomer, $origCustomerEmail); @@ -164,7 +167,7 @@ public function credentialsChanged( * @param string $email * @return void */ - private function emailAndPasswordChanged(CustomerInterface $customer, $email) + private function emailAndPasswordChanged(CustomerInterface $customer, $email): void { $storeId = $customer->getStoreId(); if (!$storeId) { @@ -190,7 +193,7 @@ private function emailAndPasswordChanged(CustomerInterface $customer, $email) * @param string $email * @return void */ - private function emailChanged(CustomerInterface $customer, $email) + private function emailChanged(CustomerInterface $customer, $email): void { $storeId = $customer->getStoreId(); if (!$storeId) { @@ -215,7 +218,7 @@ private function emailChanged(CustomerInterface $customer, $email) * @param CustomerInterface $customer * @return void */ - private function passwordReset(CustomerInterface $customer) + private function passwordReset(CustomerInterface $customer): void { $storeId = $customer->getStoreId(); if (!$storeId) { @@ -252,15 +255,15 @@ private function sendEmailTemplate( $templateParams = [], $storeId = null, $email = null - ) { - $templateId = $this->scopeConfig->getValue($template, 'store', $storeId); + ): void { + $templateId = $this->scopeConfig->getValue($template, ScopeInterface::SCOPE_STORE, $storeId); if ($email === null) { $email = $customer->getEmail(); } /** @var array $from */ $from = $this->senderResolver->resolve( - $this->scopeConfig->getValue($sender, 'store', $storeId), + $this->scopeConfig->getValue($sender, ScopeInterface::SCOPE_STORE, $storeId), $storeId ); @@ -278,15 +281,15 @@ private function sendEmailTemplate( * Create an object with data merged from Customer and CustomerSecure * * @param CustomerInterface $customer - * @return \Magento\Customer\Model\Data\CustomerSecure + * @return CustomerSecure */ - private function getFullCustomerObject($customer) + private function getFullCustomerObject($customer): CustomerSecure { // No need to flatten the custom attributes or nested objects since the only usage is for email templates and // object passed for events $mergedCustomerData = $this->customerRegistry->retrieveSecureData($customer->getId()); $customerData = $this->dataProcessor - ->buildOutputDataArray($customer, \Magento\Customer\Api\Data\CustomerInterface::class); + ->buildOutputDataArray($customer, CustomerInterface::class); $mergedCustomerData->addData($customerData); $mergedCustomerData->setData('name', $this->customerViewHelper->getCustomerName($customer)); return $mergedCustomerData; @@ -299,7 +302,7 @@ private function getFullCustomerObject($customer) * @param int|string|null $defaultStoreId * @return int */ - private function getWebsiteStoreId($customer, $defaultStoreId = null) + private function getWebsiteStoreId($customer, $defaultStoreId = null): int { if ($customer->getWebsiteId() != 0 && empty($defaultStoreId)) { $storeIds = $this->storeManager->getWebsite($customer->getWebsiteId())->getStoreIds(); @@ -314,7 +317,7 @@ private function getWebsiteStoreId($customer, $defaultStoreId = null) * @param CustomerInterface $customer * @return void */ - public function passwordReminder(CustomerInterface $customer) + public function passwordReminder(CustomerInterface $customer): void { $storeId = $customer->getStoreId(); if (!$storeId) { @@ -338,7 +341,7 @@ public function passwordReminder(CustomerInterface $customer) * @param CustomerInterface $customer * @return void */ - public function passwordResetConfirmation(CustomerInterface $customer) + public function passwordResetConfirmation(CustomerInterface $customer): void { $storeId = $customer->getStoreId(); if (!$storeId) { @@ -373,7 +376,7 @@ public function newAccount( $backUrl = '', $storeId = 0, $sendemailStoreId = null - ) { + ): void { $types = self::TEMPLATE_TYPES; if (!isset($types[$type])) { diff --git a/app/code/Magento/Customer/Model/Indexer/Processor.php b/app/code/Magento/Customer/Model/Indexer/Processor.php new file mode 100644 index 0000000000000..6b44b674b405a --- /dev/null +++ b/app/code/Magento/Customer/Model/Indexer/Processor.php @@ -0,0 +1,16 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +namespace Magento\Customer\Model\Indexer; + +use Magento\Customer\Model\Customer; + +/** + * Customer indexer + */ +class Processor extends \Magento\Framework\Indexer\AbstractProcessor +{ + const INDEXER_ID = Customer::CUSTOMER_GRID_INDEXER_ID; +} diff --git a/app/code/Magento/Customer/Model/Layout/DepersonalizePlugin.php b/app/code/Magento/Customer/Model/Layout/DepersonalizePlugin.php index 2bb9934933839..66b8f8419deae 100644 --- a/app/code/Magento/Customer/Model/Layout/DepersonalizePlugin.php +++ b/app/code/Magento/Customer/Model/Layout/DepersonalizePlugin.php @@ -1,108 +1,113 @@ <?php /** - * Depersonalize customer session data - * * Copyright © Magento, Inc. All rights reserved. * See COPYING.txt for license details. */ +declare(strict_types=1); + namespace Magento\Customer\Model\Layout; +use Magento\Customer\Model\CustomerFactory; +use Magento\Customer\Model\Session as CustomerSession; +use Magento\Customer\Model\Visitor; +use Magento\Framework\Data\Form\FormKey; +use Magento\Framework\Session\SessionManagerInterface; +use Magento\Framework\View\LayoutInterface; use Magento\PageCache\Model\DepersonalizeChecker; /** - * Class DepersonalizePlugin + * Depersonalize customer data. + * + * @SuppressWarnings(PHPMD.CookieAndSessionMisuse) */ class DepersonalizePlugin { /** * @var DepersonalizeChecker */ - protected $depersonalizeChecker; + private $depersonalizeChecker; /** - * @var \Magento\Framework\Session\SessionManagerInterface + * @var SessionManagerInterface */ - protected $session; + private $session; /** - * @var \Magento\Customer\Model\Session + * @var CustomerSession */ - protected $customerSession; + private $customerSession; /** - * @var \Magento\Customer\Model\CustomerFactory + * @var CustomerFactory */ - protected $customerFactory; + private $customerFactory; /** - * @var \Magento\Customer\Model\Visitor + * @var Visitor */ - protected $visitor; + private $visitor; /** * @var int */ - protected $customerGroupId; + private $customerGroupId; /** * @var string */ - protected $formKey; + private $formKey; /** * @param DepersonalizeChecker $depersonalizeChecker - * @param \Magento\Framework\Session\SessionManagerInterface $session - * @param \Magento\Customer\Model\Session $customerSession - * @param \Magento\Customer\Model\CustomerFactory $customerFactory - * @param \Magento\Customer\Model\Visitor $visitor + * @param SessionManagerInterface $session + * @param CustomerSession $customerSession + * @param CustomerFactory $customerFactory + * @param Visitor $visitor */ public function __construct( DepersonalizeChecker $depersonalizeChecker, - \Magento\Framework\Session\SessionManagerInterface $session, - \Magento\Customer\Model\Session $customerSession, - \Magento\Customer\Model\CustomerFactory $customerFactory, - \Magento\Customer\Model\Visitor $visitor + SessionManagerInterface $session, + CustomerSession $customerSession, + CustomerFactory $customerFactory, + Visitor $visitor ) { + $this->depersonalizeChecker = $depersonalizeChecker; $this->session = $session; $this->customerSession = $customerSession; $this->customerFactory = $customerFactory; $this->visitor = $visitor; - $this->depersonalizeChecker = $depersonalizeChecker; } /** - * Before generate Xml + * Retrieve sensitive customer data. * - * @param \Magento\Framework\View\LayoutInterface $subject - * @return array + * @param LayoutInterface $subject + * @return void */ - public function beforeGenerateXml(\Magento\Framework\View\LayoutInterface $subject) + public function beforeGenerateXml(LayoutInterface $subject) { if ($this->depersonalizeChecker->checkIfDepersonalize($subject)) { $this->customerGroupId = $this->customerSession->getCustomerGroupId(); - $this->formKey = $this->session->getData(\Magento\Framework\Data\Form\FormKey::FORM_KEY); + $this->formKey = $this->session->getData(FormKey::FORM_KEY); } - return []; } /** - * After generate Xml + * Change sensitive customer data if the depersonalization is needed. * - * @param \Magento\Framework\View\LayoutInterface $subject - * @param \Magento\Framework\View\LayoutInterface $result - * @return \Magento\Framework\View\LayoutInterface + * @param LayoutInterface $subject + * @return void */ - public function afterGenerateXml(\Magento\Framework\View\LayoutInterface $subject, $result) + public function afterGenerateElements(LayoutInterface $subject) { if ($this->depersonalizeChecker->checkIfDepersonalize($subject)) { $this->visitor->setSkipRequestLogging(true); $this->visitor->unsetData(); $this->session->clearStorage(); $this->customerSession->clearStorage(); - $this->session->setData(\Magento\Framework\Data\Form\FormKey::FORM_KEY, $this->formKey); + $this->session->setData(FormKey::FORM_KEY, $this->formKey); $this->customerSession->setCustomerGroupId($this->customerGroupId); $this->customerSession->setCustomer($this->customerFactory->create()->setGroupId($this->customerGroupId)); } - return $result; } } diff --git a/app/code/Magento/Customer/Model/Metadata/Form.php b/app/code/Magento/Customer/Model/Metadata/Form.php index 6df969f625376..85637ebf508b8 100644 --- a/app/code/Magento/Customer/Model/Metadata/Form.php +++ b/app/code/Magento/Customer/Model/Metadata/Form.php @@ -9,6 +9,8 @@ use Magento\Customer\Api\CustomerMetadataInterface; /** + * Customer Form metadata model + * * @api * @since 100.0.2 */ @@ -361,11 +363,11 @@ public function validateData(array $data) { $validator = $this->_getValidator($data); if (!$validator->isValid(false)) { - $messages = []; + $messages = [[]]; foreach ($validator->getMessages() as $errorMessages) { - $messages = array_merge($messages, (array)$errorMessages); + $messages[] = (array)$errorMessages; } - return $messages; + return array_merge(...$messages); } return true; } diff --git a/app/code/Magento/Customer/Model/Plugin/CustomerNotification.php b/app/code/Magento/Customer/Model/Plugin/CustomerNotification.php index 577c97a19268a..db694ad3295ce 100644 --- a/app/code/Magento/Customer/Model/Plugin/CustomerNotification.php +++ b/app/code/Magento/Customer/Model/Plugin/CustomerNotification.php @@ -3,22 +3,23 @@ * Copyright © Magento, Inc. All rights reserved. * See COPYING.txt for license details. */ + namespace Magento\Customer\Model\Plugin; use Magento\Customer\Api\CustomerRepositoryInterface; use Magento\Customer\Model\Customer\NotificationStorage; use Magento\Customer\Model\Session; -use Magento\Framework\App\Action\AbstractAction; +use Magento\Framework\App\ActionInterface; use Magento\Framework\App\Area; +use Magento\Framework\App\HttpRequestInterface; +use Magento\Framework\App\ObjectManager; use Magento\Framework\App\RequestInterface; use Magento\Framework\App\State; use Magento\Framework\Exception\NoSuchEntityException; use Psr\Log\LoggerInterface; /** - * Plugin before \Magento\Framework\App\Action\AbstractAction::dispatch. - * - * Plugin to remove notifications from cache. + * Refresh the Customer session if `UpdateSession` notification registered */ class CustomerNotification { @@ -47,6 +48,11 @@ class CustomerNotification */ private $logger; + /** + * @var RequestInterface|\Magento\Framework\App\Request\Http + */ + private $request; + /** * Initialize dependencies. * @@ -55,39 +61,37 @@ class CustomerNotification * @param State $state * @param CustomerRepositoryInterface $customerRepository * @param LoggerInterface $logger + * @param RequestInterface|null $request */ public function __construct( Session $session, NotificationStorage $notificationStorage, State $state, CustomerRepositoryInterface $customerRepository, - LoggerInterface $logger + LoggerInterface $logger, + RequestInterface $request ) { $this->session = $session; $this->notificationStorage = $notificationStorage; $this->state = $state; $this->customerRepository = $customerRepository; $this->logger = $logger; + $this->request = $request; } /** - * Removes notifications from cache. + * Refresh the customer session on frontend post requests if an update session notification is registered. * - * @param AbstractAction $subject - * @param RequestInterface $request + * @param ActionInterface $subject * @return void + * @throws \Magento\Framework\Exception\LocalizedException * @SuppressWarnings(PHPMD.UnusedFormalParameter) */ - public function beforeDispatch(AbstractAction $subject, RequestInterface $request) + public function beforeExecute(ActionInterface $subject) { $customerId = $this->session->getCustomerId(); - if ($this->state->getAreaCode() == Area::AREA_FRONTEND && $request->isPost() - && $this->notificationStorage->isExists( - NotificationStorage::UPDATE_CUSTOMER_SESSION, - $customerId - ) - ) { + if ($this->isFrontendRequest() && $this->isPostRequest() && $this->isSessionUpdateRegisteredFor($customerId)) { try { $this->session->regenerateId(); $customer = $this->customerRepository->getById($customerId); @@ -99,4 +103,36 @@ public function beforeDispatch(AbstractAction $subject, RequestInterface $reques } } } + + /** + * Because RequestInterface has no isPost method the check is requied before calling it. + * + * @return bool + */ + private function isPostRequest(): bool + { + return $this->request instanceof HttpRequestInterface && $this->request->isPost(); + } + + /** + * Check if the current application area is frontend. + * + * @return bool + * @throws \Magento\Framework\Exception\LocalizedException + */ + private function isFrontendRequest(): bool + { + return $this->state->getAreaCode() === Area::AREA_FRONTEND; + } + + /** + * True if the session for the given customer ID needs to be refreshed. + * + * @param int $customerId + * @return bool + */ + private function isSessionUpdateRegisteredFor($customerId): bool + { + return $this->notificationStorage->isExists(NotificationStorage::UPDATE_CUSTOMER_SESSION, $customerId); + } } diff --git a/app/code/Magento/Customer/Model/ResourceModel/Address/Attribute/Source/CountryWithWebsites.php b/app/code/Magento/Customer/Model/ResourceModel/Address/Attribute/Source/CountryWithWebsites.php index ce6278b446302..020067570efb4 100644 --- a/app/code/Magento/Customer/Model/ResourceModel/Address/Attribute/Source/CountryWithWebsites.php +++ b/app/code/Magento/Customer/Model/ResourceModel/Address/Attribute/Source/CountryWithWebsites.php @@ -12,18 +12,28 @@ namespace Magento\Customer\Model\ResourceModel\Address\Attribute\Source; use Magento\Customer\Model\Config\Share; +use Magento\Customer\Model\Config\Share as CustomerShareConfig; use Magento\Directory\Model\AllowedCountries; +use Magento\Directory\Model\ResourceModel\Country\Collection as CountryCollection; +use Magento\Directory\Model\ResourceModel\Country\CollectionFactory as CountryCollectionFactory; +use Magento\Eav\Model\Entity\Attribute\Source\Table; +use Magento\Eav\Model\ResourceModel\Entity\Attribute\Option\CollectionFactory as OptionCollectionFactory; +use Magento\Eav\Model\ResourceModel\Entity\Attribute\OptionFactory as AttrubuteOptionFactory; use Magento\Store\Model\ScopeInterface; +use Magento\Store\Model\StoreManagerInterface; -class CountryWithWebsites extends \Magento\Eav\Model\Entity\Attribute\Source\Table +/** + * Return allowed countries for specified website + */ +class CountryWithWebsites extends Table { /** - * @var \Magento\Directory\Model\ResourceModel\Country\CollectionFactory + * @var CountryCollectionFactory */ private $countriesFactory; /** - * @var \Magento\Directory\Model\AllowedCountries + * @var AllowedCountries */ private $allowedCountriesReader; @@ -33,7 +43,7 @@ class CountryWithWebsites extends \Magento\Eav\Model\Entity\Attribute\Source\Tab private $options; /** - * @var \Magento\Store\Model\StoreManagerInterface + * @var StoreManagerInterface */ private $storeManager; @@ -43,21 +53,20 @@ class CountryWithWebsites extends \Magento\Eav\Model\Entity\Attribute\Source\Tab private $shareConfig; /** - * CountryWithWebsites constructor. - * @param \Magento\Eav\Model\ResourceModel\Entity\Attribute\Option\CollectionFactory $attrOptionCollectionFactory - * @param \Magento\Eav\Model\ResourceModel\Entity\Attribute\OptionFactory $attrOptionFactory - * @param \Magento\Directory\Model\ResourceModel\Country\CollectionFactory $countriesFactory + * @param OptionCollectionFactory $attrOptionCollectionFactory + * @param AttrubuteOptionFactory $attrOptionFactory + * @param CountryCollectionFactory $countriesFactory * @param AllowedCountries $allowedCountriesReader - * @param \Magento\Store\Model\StoreManagerInterface $storeManager + * @param StoreManagerInterface $storeManager * @param Share $shareConfig */ public function __construct( - \Magento\Eav\Model\ResourceModel\Entity\Attribute\Option\CollectionFactory $attrOptionCollectionFactory, - \Magento\Eav\Model\ResourceModel\Entity\Attribute\OptionFactory $attrOptionFactory, - \Magento\Directory\Model\ResourceModel\Country\CollectionFactory $countriesFactory, - \Magento\Directory\Model\AllowedCountries $allowedCountriesReader, - \Magento\Store\Model\StoreManagerInterface $storeManager, - \Magento\Customer\Model\Config\Share $shareConfig + OptionCollectionFactory $attrOptionCollectionFactory, + AttrubuteOptionFactory $attrOptionFactory, + CountryCollectionFactory $countriesFactory, + AllowedCountries $allowedCountriesReader, + StoreManagerInterface $storeManager, + CustomerShareConfig $shareConfig ) { $this->countriesFactory = $countriesFactory; $this->allowedCountriesReader = $allowedCountriesReader; @@ -72,19 +81,22 @@ public function __construct( public function getAllOptions($withEmpty = true, $defaultValues = false) { if (!$this->options) { - $allowedCountries = []; $websiteIds = []; if (!$this->shareConfig->isGlobalScope()) { + $allowedCountries = [[]]; + foreach ($this->storeManager->getWebsites() as $website) { $countries = $this->allowedCountriesReader ->getAllowedCountries(ScopeInterface::SCOPE_WEBSITE, $website->getId()); - $allowedCountries = array_merge($allowedCountries, $countries); + $allowedCountries[] = $countries; foreach ($countries as $countryCode) { $websiteIds[$countryCode][] = $website->getId(); } } + + $allowedCountries = array_unique(array_merge(...$allowedCountries)); } else { $allowedCountries = $this->allowedCountriesReader->getAllowedCountries(); } @@ -106,7 +118,7 @@ public function getAllOptions($withEmpty = true, $defaultValues = false) /** * Create Countries Collection with all countries * - * @return \Magento\Directory\Model\ResourceModel\Country\Collection + * @return CountryCollection */ private function createCountriesCollection() { diff --git a/app/code/Magento/Customer/Model/ResourceModel/Address/Relation.php b/app/code/Magento/Customer/Model/ResourceModel/Address/Relation.php index ae342a1b10dd8..cf837e2924161 100644 --- a/app/code/Magento/Customer/Model/ResourceModel/Address/Relation.php +++ b/app/code/Magento/Customer/Model/ResourceModel/Address/Relation.php @@ -1,14 +1,18 @@ <?php /** - * Customer address entity resource model - * * Copyright © Magento, Inc. All rights reserved. * See COPYING.txt for license details. */ +declare(strict_types=1); + namespace Magento\Customer\Model\ResourceModel\Address; +use Magento\Customer\Api\Data\CustomerInterface; use Magento\Customer\Model\Address; use Magento\Customer\Model\Customer; +use Magento\Customer\Model\CustomerFactory; +use Magento\Customer\Model\CustomerRegistry; +use Magento\Framework\Model\AbstractModel; use Magento\Framework\Model\ResourceModel\Db\VersionControl\RelationInterface; /** @@ -17,29 +21,36 @@ class Relation implements RelationInterface { /** - * @var \Magento\Customer\Model\CustomerFactory + * @var CustomerFactory */ protected $customerFactory; /** - * @param \Magento\Customer\Model\CustomerFactory $customerFactory + * @var CustomerRegistry */ - public function __construct(\Magento\Customer\Model\CustomerFactory $customerFactory) - { + private $customerRegistry; + + /** + * @param CustomerFactory $customerFactory + * @param CustomerRegistry $customerRegistry + */ + public function __construct( + CustomerFactory $customerFactory, + CustomerRegistry $customerRegistry + ) { $this->customerFactory = $customerFactory; + $this->customerRegistry = $customerRegistry; } /** * Process object relations * - * @param \Magento\Framework\Model\AbstractModel $object + * @param AbstractModel $object * @return void */ - public function processRelation(\Magento\Framework\Model\AbstractModel $object) + public function processRelation(AbstractModel $object): void { - /** - * @var $object Address - */ + /** @var $object Address */ if (!$object->getIsCustomerSaveTransaction() && $object->getId()) { $customer = $this->customerFactory->create()->load($object->getCustomerId()); @@ -53,6 +64,7 @@ public function processRelation(\Magento\Framework\Model\AbstractModel $object) $changedAddresses, $customer->getResource()->getConnection()->quoteInto('entity_id = ?', $customer->getId()) ); + $this->updateCustomerRegistry($customer, $changedAddresses); } } } @@ -71,12 +83,12 @@ private function getDefaultBillingChangedAddress( array $changedAddresses ): array { if ($object->getIsDefaultBilling()) { - $changedAddresses['default_billing'] = $object->getId(); + $changedAddresses[CustomerInterface::DEFAULT_BILLING] = $object->getId(); } elseif ($customer->getDefaultBillingAddress() && $object->getIsDefaultBilling() === false && (int)$customer->getDefaultBillingAddress()->getId() === (int)$object->getId() ) { - $changedAddresses['default_billing'] = null; + $changedAddresses[CustomerInterface::DEFAULT_BILLING] = null; } return $changedAddresses; @@ -96,27 +108,47 @@ private function getDefaultShippingChangedAddress( array $changedAddresses ): array { if ($object->getIsDefaultShipping()) { - $changedAddresses['default_shipping'] = $object->getId(); + $changedAddresses[CustomerInterface::DEFAULT_SHIPPING] = $object->getId(); } elseif ($customer->getDefaultShippingAddress() && $object->getIsDefaultShipping() === false && (int)$customer->getDefaultShippingAddress()->getId() === (int)$object->getId() ) { - $changedAddresses['default_shipping'] = null; + $changedAddresses[CustomerInterface::DEFAULT_SHIPPING] = null; } return $changedAddresses; } + /** + * Push updated customer entity to the registry. + * + * @param Customer $customer + * @param array $changedAddresses + * @return void + */ + private function updateCustomerRegistry(Customer $customer, array $changedAddresses): void + { + if (array_key_exists(CustomerInterface::DEFAULT_BILLING, $changedAddresses)) { + $customer->setDefaultBilling($changedAddresses[CustomerInterface::DEFAULT_BILLING]); + } + + if (array_key_exists(CustomerInterface::DEFAULT_SHIPPING, $changedAddresses)) { + $customer->setDefaultShipping($changedAddresses[CustomerInterface::DEFAULT_SHIPPING]); + } + + $this->customerRegistry->push($customer); + } + /** * Checks if address has chosen as default and has had an id * * @deprecated Is not used anymore due to changes in logic of save of address. * If address was default and becomes not default than default address id for customer must be * set to null - * @param \Magento\Framework\Model\AbstractModel $object + * @param AbstractModel $object * @return bool */ - protected function isAddressDefault(\Magento\Framework\Model\AbstractModel $object) + protected function isAddressDefault(AbstractModel $object) { return $object->getId() && ($object->getIsDefaultBilling() || $object->getIsDefaultShipping()); } diff --git a/app/code/Magento/Customer/Model/ResourceModel/CustomerRepository.php b/app/code/Magento/Customer/Model/ResourceModel/CustomerRepository.php index 03cf4b1bdddec..0611a2df641e7 100644 --- a/app/code/Magento/Customer/Model/ResourceModel/CustomerRepository.php +++ b/app/code/Magento/Customer/Model/ResourceModel/CustomerRepository.php @@ -17,6 +17,7 @@ use Magento\Customer\Model\Data\CustomerSecureFactory; use Magento\Customer\Model\Delegation\Data\NewOperation; use Magento\Customer\Model\Delegation\Storage as DelegatedStorage; +use Magento\Customer\Model\ResourceModel\Customer\Collection; use Magento\Framework\Api\DataObjectHelper; use Magento\Framework\Api\ExtensibleDataObjectConverter; use Magento\Framework\Api\ExtensionAttribute\JoinProcessorInterface; @@ -31,6 +32,8 @@ /** * Customer repository. * + * CRUD operations for customer entity + * * @SuppressWarnings(PHPMD.CouplingBetweenObjects) * @SuppressWarnings(PHPMD.TooManyFields) */ @@ -187,8 +190,7 @@ public function save(CustomerInterface $customer, $passwordHash = null) { /** @var NewOperation|null $delegatedNewOperation */ $delegatedNewOperation = !$customer->getId() ? $this->delegatedStorage->consumeNewOperation() : null; - $prevCustomerData = null; - $prevCustomerDataArr = null; + $prevCustomerData = $prevCustomerDataArr = null; if ($customer->getId()) { $prevCustomerData = $this->getById($customer->getId()); $prevCustomerDataArr = $prevCustomerData->__toArray(); @@ -214,6 +216,7 @@ public function save(CustomerInterface $customer, $passwordHash = null) $prevCustomerData ? $prevCustomerData->getStoreId() : $this->storeManager->getStore()->getId() ); } + $this->setCustomerGroupId($customerModel, $customerArr, $prevCustomerDataArr); // Need to use attribute set or future updates can cause data loss if (!$customerModel->getAttributeSetId()) { $customerModel->setAttributeSetId(CustomerMetadataInterface::ATTRIBUTE_SET_ID_CUSTOMER); @@ -452,4 +455,18 @@ private function setValidationFlag($customerArray, $customerModel) $customerModel->setData('ignore_validation_flag', true); } } + + /** + * Set customer group id + * + * @param Customer $customerModel + * @param array $customerArr + * @param array $prevCustomerDataArr + */ + private function setCustomerGroupId($customerModel, $customerArr, $prevCustomerDataArr) + { + if (!isset($customerArr['group_id']) && $prevCustomerDataArr && isset($prevCustomerDataArr['group_id'])) { + $customerModel->setGroupId($prevCustomerDataArr['group_id']); + } + } } diff --git a/app/code/Magento/Customer/Model/Session.php b/app/code/Magento/Customer/Model/Session.php index e9dc7700ec090..55e31cb4555b4 100644 --- a/app/code/Magento/Customer/Model/Session.php +++ b/app/code/Magento/Customer/Model/Session.php @@ -19,6 +19,7 @@ * @method string getNoReferer() * @SuppressWarnings(PHPMD.CouplingBetweenObjects) * @SuppressWarnings(PHPMD.CookieAndSessionMisuse) + * @SuppressWarnings(PHPMD.TooManyFields) * @since 100.0.2 */ class Session extends \Magento\Framework\Session\SessionManager @@ -108,6 +109,11 @@ class Session extends \Magento\Framework\Session\SessionManager */ protected $response; + /** + * @var AccountConfirmation + */ + private $accountConfirmation; + /** * Session constructor. * @@ -304,7 +310,11 @@ public function setCustomer(Customer $customerModel) public function getCustomer() { if ($this->_customerModel === null) { - $this->_customerModel = $this->_customerFactory->create()->load($this->getCustomerId()); + $this->_customerModel = $this->_customerFactory->create(); + + if ($this->getCustomerId()) { + $this->_customerResource->load($this->_customerModel, $this->getCustomerId()); + } } return $this->_customerModel; @@ -511,13 +521,6 @@ public function authenticate($loginUrl = null) $this->response->setRedirect($loginUrl); } else { $arguments = $this->_customerUrl->getLoginUrlParams(); - if ($this->_createUrl()->getUseSession()) { - $arguments += [ - '_query' => [ - $this->sidResolver->getSessionIdQueryParam($this->_session) => $this->_session->getSessionId(), - ] - ]; - } $this->response->setRedirect( $this->_createUrl()->getUrl(\Magento\Customer\Model\Url::ROUTE_ACCOUNT_LOGIN, $arguments) ); @@ -535,8 +538,6 @@ public function authenticate($loginUrl = null) */ protected function _setAuthUrl($key, $url) { - $url = $this->_coreUrl->removeRequestParam($url, $this->sidResolver->getSessionIdQueryParam($this)); - // Add correct session ID to URL if needed $url = $this->_createUrl()->getRebuiltUrl($url); return $this->storage->setData($key, $url); } diff --git a/app/code/Magento/Customer/Model/Visitor.php b/app/code/Magento/Customer/Model/Visitor.php index 6935b9dca7f28..53745aa7a30c6 100644 --- a/app/code/Magento/Customer/Model/Visitor.php +++ b/app/code/Magento/Customer/Model/Visitor.php @@ -6,25 +6,39 @@ namespace Magento\Customer\Model; +use Magento\Customer\Api\Data\CustomerInterface; +use Magento\Framework\App\Config\ScopeConfigInterface; use Magento\Framework\App\ObjectManager; +use Magento\Framework\App\Request; use Magento\Framework\App\RequestSafetyInterface; +use Magento\Framework\Data\Collection\AbstractDb; +use Magento\Framework\Event\Observer as EventObserver; +use Magento\Framework\HTTP\Header; +use Magento\Framework\Indexer\IndexerRegistry; +use Magento\Framework\Model\AbstractModel; +use Magento\Framework\Model\Context as ModelContext; +use Magento\Framework\Model\ResourceModel\AbstractResource; +use Magento\Framework\Registry; +use Magento\Framework\Session\Config as SessionConfig; +use Magento\Framework\Session\SessionManagerInterface; +use Magento\Framework\Stdlib\DateTime; +use Magento\Store\Model\ScopeInterface; /** * Class Visitor * - * @package Magento\Customer\Model + * Used to track sessions of the logged in customers + * * @SuppressWarnings(PHPMD.CouplingBetweenObjects) * @SuppressWarnings(PHPMD.CookieAndSessionMisuse) */ -class Visitor extends \Magento\Framework\Model\AbstractModel +class Visitor extends AbstractModel { const VISITOR_TYPE_CUSTOMER = 'c'; - const VISITOR_TYPE_VISITOR = 'v'; - const DEFAULT_ONLINE_MINUTES_INTERVAL = 15; - const XML_PATH_ONLINE_INTERVAL = 'customer/online_customers/online_minutes_interval'; + private const SECONDS_24_HOURS = 86400; /** * @var string[] @@ -32,12 +46,12 @@ class Visitor extends \Magento\Framework\Model\AbstractModel protected $ignoredUserAgents; /** - * @var \Magento\Framework\Session\SessionManagerInterface + * @var SessionManagerInterface */ protected $session; /** - * @var \Magento\Framework\HTTP\Header + * @var Header */ protected $httpHeader; @@ -56,17 +70,17 @@ class Visitor extends \Magento\Framework\Model\AbstractModel /** * Core store config * - * @var \Magento\Framework\App\Config\ScopeConfigInterface + * @var ScopeConfigInterface */ protected $scopeConfig; /** - * @var \Magento\Framework\Stdlib\DateTime + * @var DateTime */ protected $dateTime; /** - * @var \Magento\Framework\Indexer\IndexerRegistry + * @var IndexerRegistry */ protected $indexerRegistry; @@ -76,15 +90,15 @@ class Visitor extends \Magento\Framework\Model\AbstractModel private $requestSafety; /** - * @param \Magento\Framework\Model\Context $context - * @param \Magento\Framework\Registry $registry - * @param \Magento\Framework\Session\SessionManagerInterface $session - * @param \Magento\Framework\HTTP\Header $httpHeader - * @param \Magento\Framework\App\Config\ScopeConfigInterface $scopeConfig - * @param \Magento\Framework\Stdlib\DateTime $dateTime - * @param \Magento\Framework\Indexer\IndexerRegistry $indexerRegistry - * @param \Magento\Framework\Model\ResourceModel\AbstractResource|null $resource - * @param \Magento\Framework\Data\Collection\AbstractDb|null $resourceCollection + * @param ModelContext $context + * @param Registry $registry + * @param SessionManagerInterface $session + * @param Header $httpHeader + * @param ScopeConfigInterface $scopeConfig + * @param DateTime $dateTime + * @param IndexerRegistry $indexerRegistry + * @param AbstractResource|null $resource + * @param AbstractDb|null $resourceCollection * @param array $ignoredUserAgents * @param array $ignores * @param array $data @@ -93,15 +107,15 @@ class Visitor extends \Magento\Framework\Model\AbstractModel * @SuppressWarnings(PHPMD.ExcessiveParameterList) */ public function __construct( - \Magento\Framework\Model\Context $context, - \Magento\Framework\Registry $registry, - \Magento\Framework\Session\SessionManagerInterface $session, - \Magento\Framework\HTTP\Header $httpHeader, - \Magento\Framework\App\Config\ScopeConfigInterface $scopeConfig, - \Magento\Framework\Stdlib\DateTime $dateTime, - \Magento\Framework\Indexer\IndexerRegistry $indexerRegistry, - \Magento\Framework\Model\ResourceModel\AbstractResource $resource = null, - \Magento\Framework\Data\Collection\AbstractDb $resourceCollection = null, + ModelContext $context, + Registry $registry, + SessionManagerInterface $session, + Header $httpHeader, + ScopeConfigInterface $scopeConfig, + DateTime $dateTime, + IndexerRegistry $indexerRegistry, + AbstractResource $resource = null, + AbstractDb $resourceCollection = null, array $ignoredUserAgents = [], array $ignores = [], array $data = [], @@ -125,7 +139,7 @@ public function __construct( */ protected function _construct() { - $this->_init(\Magento\Customer\Model\ResourceModel\Visitor::class); + $this->_init(ResourceModel\Visitor::class); $userAgent = $this->httpHeader->getHttpUserAgent(); if ($this->ignoredUserAgents) { if (in_array($userAgent, $this->ignoredUserAgents)) { @@ -138,7 +152,7 @@ protected function _construct() * Skip request logging * * @param bool $skipRequestLogging - * @return \Magento\Customer\Model\Visitor + * @return Visitor */ public function setSkipRequestLogging($skipRequestLogging) { @@ -147,12 +161,10 @@ public function setSkipRequestLogging($skipRequestLogging) } /** - * Initialization visitor by request - * - * Used in event "controller_action_predispatch" + * Initialization visitor by request. Used in event "controller_action_predispatch" * - * @param \Magento\Framework\Event\Observer $observer - * @return \Magento\Customer\Model\Visitor + * @param EventObserver $observer + * @return Visitor */ public function initByRequest($observer) { @@ -167,13 +179,13 @@ public function initByRequest($observer) } } - $this->setLastVisitAt((new \DateTime())->format(\Magento\Framework\Stdlib\DateTime::DATETIME_PHP_FORMAT)); + $this->setLastVisitAt((new \DateTime())->format(DateTime::DATETIME_PHP_FORMAT)); // prevent saving Visitor for safe methods, e.g. GET request if ($this->requestSafety->isSafeMethod()) { return $this; } - + if (!$this->getId()) { $this->setSessionId($this->session->getSessionId()); $this->save(); @@ -188,8 +200,8 @@ public function initByRequest($observer) * * Used in event "controller_action_postdispatch" * - * @param \Magento\Framework\Event\Observer $observer - * @return \Magento\Customer\Model\Visitor + * @param EventObserver $observer + * @return Visitor */ public function saveByRequest($observer) { @@ -199,6 +211,9 @@ public function saveByRequest($observer) } try { + if ($this->session->getSessionId() && $this->getSessionId() != $this->session->getSessionId()) { + $this->setSessionId($this->session->getSessionId()); + } $this->save(); $this->_eventManager->dispatch('visitor_activity_save', ['visitor' => $this]); $this->session->setVisitorData($this->getData()); @@ -211,13 +226,13 @@ public function saveByRequest($observer) /** * Returns true if the module is required * - * @param \Magento\Framework\Event\Observer $observer + * @param EventObserver $observer * @return bool */ public function isModuleIgnored($observer) { if (is_array($this->ignores) && $observer) { - $curModule = $observer->getEvent()->getControllerAction()->getRequest()->getRouteName(); + $curModule = $this->requestSafety->getRouteName(); if (isset($this->ignores[$curModule])) { return true; } @@ -230,12 +245,12 @@ public function isModuleIgnored($observer) * * Used in event "customer_login" * - * @param \Magento\Framework\Event\Observer $observer - * @return \Magento\Customer\Model\Visitor + * @param EventObserver $observer + * @return Visitor */ public function bindCustomerLogin($observer) { - /** @var \Magento\Customer\Api\Data\CustomerInterface $customer */ + /** @var CustomerInterface $customer */ $customer = $observer->getEvent()->getCustomer(); if (!$this->getCustomerId()) { $this->setDoCustomerLogin(true); @@ -249,8 +264,8 @@ public function bindCustomerLogin($observer) * * Used in event "customer_logout" * - * @param \Magento\Framework\Event\Observer $observer - * @return \Magento\Customer\Model\Visitor + * @param EventObserver $observer + * @return Visitor * @SuppressWarnings(PHPMD.UnusedFormalParameter) */ public function bindCustomerLogout($observer) @@ -264,8 +279,8 @@ public function bindCustomerLogout($observer) /** * Create binding of checkout quote * - * @param \Magento\Framework\Event\Observer $observer - * @return \Magento\Customer\Model\Visitor + * @param EventObserver $observer + * @return Visitor */ public function bindQuoteCreate($observer) { @@ -282,8 +297,8 @@ public function bindQuoteCreate($observer) /** * Destroy binding of checkout quote * - * @param \Magento\Framework\Event\Observer $observer - * @return \Magento\Customer\Model\Visitor + * @param EventObserver $observer + * @return Visitor */ public function bindQuoteDestroy($observer) { @@ -301,10 +316,10 @@ public function bindQuoteDestroy($observer) */ public function getCleanTime() { - return $this->scopeConfig->getValue( - \Magento\Framework\Session\Config::XML_PATH_COOKIE_LIFETIME, - \Magento\Store\Model\ScopeInterface::SCOPE_STORE - ) + 86400; + return self::SECONDS_24_HOURS + $this->scopeConfig->getValue( + SessionConfig::XML_PATH_COOKIE_LIFETIME, + ScopeInterface::SCOPE_STORE + ); } /** @@ -327,7 +342,7 @@ public function getOnlineInterval() { $configValue = (int)$this->scopeConfig->getValue( static::XML_PATH_ONLINE_INTERVAL, - \Magento\Store\Model\ScopeInterface::SCOPE_STORE + ScopeInterface::SCOPE_STORE ); return $configValue ?: static::DEFAULT_ONLINE_MINUTES_INTERVAL; } diff --git a/app/code/Magento/Customer/Setup/Patch/Data/UpdateCustomerAddressAttributesSortOrder.php b/app/code/Magento/Customer/Setup/Patch/Data/UpdateCustomerAddressAttributesSortOrder.php new file mode 100644 index 0000000000000..b35a21751ebea --- /dev/null +++ b/app/code/Magento/Customer/Setup/Patch/Data/UpdateCustomerAddressAttributesSortOrder.php @@ -0,0 +1,106 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +declare(strict_types=1); + +namespace Magento\Customer\Setup\Patch\Data; + +use Magento\Customer\Setup\CustomerSetup; +use Magento\Customer\Setup\CustomerSetupFactory; +use Magento\Framework\Setup\ModuleDataSetupInterface; +use Magento\Framework\Setup\Patch\DataPatchInterface; + +/** + * Update Customer Address Attributes to be displayed in following order: country, region, city, postcode + */ +class UpdateCustomerAddressAttributesSortOrder implements DataPatchInterface +{ + /** + * @var ModuleDataSetupInterface + */ + private $moduleDataSetup; + + /** + * @var CustomerSetupFactory + */ + private $customerSetupFactory; + + /** + * UpdateCustomerAddressAttributesSortOrder constructor. + * @param ModuleDataSetupInterface $moduleDataSetup + * @param CustomerSetupFactory $customerSetupFactory + */ + public function __construct( + ModuleDataSetupInterface $moduleDataSetup, + CustomerSetupFactory $customerSetupFactory + ) { + $this->moduleDataSetup = $moduleDataSetup; + $this->customerSetupFactory = $customerSetupFactory; + } + + /** + * @inheritDoc + */ + public function apply() + { + /** @var CustomerSetup $customerSetup */ + $customerSetup = $this->customerSetupFactory->create(['setup' => $this->moduleDataSetup]); + $this->updateCustomerAddressAttributesSortOrder($customerSetup); + + return $this; + } + + /** + * @inheritDoc + */ + public function getAliases() + { + return []; + } + + /** + * @inheritDoc + */ + public static function getDependencies() + { + return [ + DefaultCustomerGroupsAndAttributes::class, + ]; + } + + /** + * Update customer address attributes sort order + * + * @param CustomerSetup $customerSetup + * + * @return void + * @SuppressWarnings(PHPMD.ExcessiveMethodLength) + */ + private function updateCustomerAddressAttributesSortOrder($customerSetup) + { + $entityAttributes = [ + 'customer_address' => [ + 'country_id' => [ + 'sort_order' => 80, + 'position' => 80 + ], + 'region' => [ + 'sort_order' => 90, + 'position' => 90 + ], + 'region_id' => [ + 'sort_order' => 90, + 'position' => 90 + ], + 'city' => [ + 'sort_order' => 100, + 'position' => 100 + ], + ], + ]; + + $customerSetup->upgradeAttributes($entityAttributes); + } +} diff --git a/app/code/Magento/Customer/Setup/RecurringData.php b/app/code/Magento/Customer/Setup/RecurringData.php index fbef4c05d126d..76ff818ca7e5e 100644 --- a/app/code/Magento/Customer/Setup/RecurringData.php +++ b/app/code/Magento/Customer/Setup/RecurringData.php @@ -7,6 +7,7 @@ namespace Magento\Customer\Setup; use Magento\Framework\Indexer\IndexerRegistry; +use Magento\Framework\Indexer\StateInterface; use Magento\Framework\Setup\InstallDataInterface; use Magento\Framework\Setup\ModuleContextInterface; use Magento\Framework\Setup\ModuleDataSetupInterface; @@ -27,17 +28,34 @@ class RecurringData implements InstallDataInterface * * @param IndexerRegistry $indexerRegistry */ - public function __construct(IndexerRegistry $indexerRegistry) - { + public function __construct( + IndexerRegistry $indexerRegistry + ) { $this->indexerRegistry = $indexerRegistry; } /** - * {@inheritdoc} + * @inheritDoc */ public function install(ModuleDataSetupInterface $setup, ModuleContextInterface $context) { - $indexer = $this->indexerRegistry->get(Customer::CUSTOMER_GRID_INDEXER_ID); - $indexer->reindexAll(); + if ($this->isNeedToDoReindex($setup)) { + $indexer = $this->indexerRegistry->get(Customer::CUSTOMER_GRID_INDEXER_ID); + $indexer->reindexAll(); + } + } + + /** + * Check is re-index needed + * + * @param ModuleDataSetupInterface $setup + * @return bool + */ + private function isNeedToDoReindex(ModuleDataSetupInterface $setup) : bool + { + return !$setup->tableExists('customer_grid_flat') + || $this->indexerRegistry->get(Customer::CUSTOMER_GRID_INDEXER_ID) + ->getState() + ->getStatus() == StateInterface::STATUS_INVALID; } } diff --git a/app/code/Magento/Customer/Test/Mftf/ActionGroup/AdminAssertCustomerSubscribeNewsletterActionGroup.xml b/app/code/Magento/Customer/Test/Mftf/ActionGroup/AdminAssertCustomerSubscribeNewsletterActionGroup.xml deleted file mode 100644 index 49373bb7bebf9..0000000000000 --- a/app/code/Magento/Customer/Test/Mftf/ActionGroup/AdminAssertCustomerSubscribeNewsletterActionGroup.xml +++ /dev/null @@ -1,34 +0,0 @@ -<?xml version="1.0" encoding="UTF-8"?> -<!-- - /** - * Copyright © Magento, Inc. All rights reserved. - * See COPYING.txt for license details. - */ ---> - -<actionGroups xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" - xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/actionGroupSchema.xsd"> - <actionGroup name="AdminAssertCustomerIsSubscribedToNewsletters"> - <annotations> - <description>Verify that check box "Newsletter subscribed" is checked on "Newsletter" tab on customer edit page.</description> - </annotations> - <arguments> - <argument name="websiteId" type="string" defaultValue="1"/> - </arguments> - - <click selector="{{AdminEditCustomerInformationSection.newsLetter}}" stepKey="clickToNewsletterTabHeader"/> - <waitForPageLoad stepKey="waitForShowNewsletterTab"/> - <seeCheckboxIsChecked selector="{{AdminEditCustomerNewsletterSection.subscribedStatus(websiteId)}}" stepKey="assertSubscribedToNewsletter"/> - </actionGroup> - - <actionGroup name="AdminAssertCustomerIsSubscribedToNewslettersAndSelectedStoreView" extends="AdminAssertCustomerIsSubscribedToNewsletters"> - <annotations> - <description>Verify that check box "Newsletter subscribed" is checked and Store View is selected on "Newsletter" tab on customer edit page.</description> - </annotations> - <arguments> - <argument name="storeView"/> - </arguments> - - <seeOptionIsSelected selector="{{AdminEditCustomerNewsletterSection.subscribedStore(websiteId)}}" userInput="{{storeView.name}}" stepKey="assertSubscribedStoreView" after="assertSubscribedToNewsletter"/> - </actionGroup> -</actionGroups> \ No newline at end of file diff --git a/app/code/Magento/Customer/Test/Mftf/ActionGroup/AdminAssertCustomerSubscribeNewsletterActionGroup/AdminAssertCustomerIsSubscribedToNewslettersActionGroup.xml b/app/code/Magento/Customer/Test/Mftf/ActionGroup/AdminAssertCustomerSubscribeNewsletterActionGroup/AdminAssertCustomerIsSubscribedToNewslettersActionGroup.xml new file mode 100644 index 0000000000000..5c3789417a636 --- /dev/null +++ b/app/code/Magento/Customer/Test/Mftf/ActionGroup/AdminAssertCustomerSubscribeNewsletterActionGroup/AdminAssertCustomerIsSubscribedToNewslettersActionGroup.xml @@ -0,0 +1,22 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + /** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +--> +<actionGroups xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/actionGroupSchema.xsd"> + <actionGroup name="AdminAssertCustomerIsSubscribedToNewsletters"> + <annotations> + <description>Verify that check box "Newsletter subscribed" is checked on "Newsletter" tab on customer edit page.</description> + </annotations> + <arguments> + <argument name="websiteId" type="string" defaultValue="1"/> + </arguments> + + <click selector="{{AdminEditCustomerInformationSection.newsLetter}}" stepKey="clickToNewsletterTabHeader"/> + <waitForPageLoad stepKey="waitForShowNewsletterTab"/> + <seeCheckboxIsChecked selector="{{AdminEditCustomerNewsletterSection.subscribedStatus(websiteId)}}" stepKey="assertSubscribedToNewsletter"/> + </actionGroup> +</actionGroups> diff --git a/app/code/Magento/Customer/Test/Mftf/ActionGroup/AdminAssertCustomerSubscribeNewsletterActionGroup/AdminAssertCustomerIsSubscribedToNewslettersAndSelectedStoreViewActionGroup.xml b/app/code/Magento/Customer/Test/Mftf/ActionGroup/AdminAssertCustomerSubscribeNewsletterActionGroup/AdminAssertCustomerIsSubscribedToNewslettersAndSelectedStoreViewActionGroup.xml new file mode 100644 index 0000000000000..809e244031068 --- /dev/null +++ b/app/code/Magento/Customer/Test/Mftf/ActionGroup/AdminAssertCustomerSubscribeNewsletterActionGroup/AdminAssertCustomerIsSubscribedToNewslettersAndSelectedStoreViewActionGroup.xml @@ -0,0 +1,20 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + /** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +--> +<actionGroups xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/actionGroupSchema.xsd"> + <actionGroup name="AdminAssertCustomerIsSubscribedToNewslettersAndSelectedStoreView" extends="AdminAssertCustomerIsSubscribedToNewsletters"> + <annotations> + <description>Verify that check box "Newsletter subscribed" is checked and Store View is selected on "Newsletter" tab on customer edit page.</description> + </annotations> + <arguments> + <argument name="storeView"/> + </arguments> + + <seeOptionIsSelected selector="{{AdminEditCustomerNewsletterSection.subscribedStore(websiteId)}}" userInput="{{storeView.name}}" stepKey="assertSubscribedStoreView" after="assertSubscribedToNewsletter"/> + </actionGroup> +</actionGroups> diff --git a/app/code/Magento/Customer/Test/Mftf/ActionGroup/AdminClearCustomersFiltersActionGroup.xml b/app/code/Magento/Customer/Test/Mftf/ActionGroup/AdminClearCustomersFiltersActionGroup.xml new file mode 100644 index 0000000000000..3ca41bb014d47 --- /dev/null +++ b/app/code/Magento/Customer/Test/Mftf/ActionGroup/AdminClearCustomersFiltersActionGroup.xml @@ -0,0 +1,20 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + /** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +--> + +<actionGroups xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/actionGroupSchema.xsd"> + <actionGroup name="AdminClearCustomersFiltersActionGroup"> + <annotations> + <description>Goes to the Admin Customers grid page. Clicks on 'Clear Filters'.</description> + </annotations> + + <amOnPage url="{{AdminCustomerPage.url}}" stepKey="amOnCustomersPage"/> + <waitForPageLoad stepKey="WaitForPageToLoad"/> + <conditionalClick selector="{{AdminDataGridHeaderSection.clearFilters}}" dependentSelector="{{AdminDataGridHeaderSection.clearFilters}}" visible="true" stepKey="clickOnButtonToRemoveFiltersIfPresent"/> + </actionGroup> +</actionGroups> diff --git a/app/code/Magento/Customer/Test/Mftf/ActionGroup/AdminCreateCustomerWithWebSiteAndGroupActionGroup.xml b/app/code/Magento/Customer/Test/Mftf/ActionGroup/AdminCreateCustomerWithWebSiteAndGroupActionGroup.xml new file mode 100644 index 0000000000000..fb4c1980fe975 --- /dev/null +++ b/app/code/Magento/Customer/Test/Mftf/ActionGroup/AdminCreateCustomerWithWebSiteAndGroupActionGroup.xml @@ -0,0 +1,34 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + /** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +--> + +<actionGroups xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/actionGroupSchema.xsd"> + <actionGroup name="AdminCreateCustomerWithWebSiteAndGroupActionGroup"> + <annotations> + <description>Goes to the Customer grid page. Click on 'Add New Customer'. Fills provided Customer Data. Fill provided Customer Address data. Assigns Product to Website and Store View. Clicks on Save.</description> + </annotations> + <arguments> + <argument name="customerData" defaultValue="Simple_US_Customer"/> + <argument name="website" type="string" defaultValue="{{_defaultWebsite.name}}"/> + <argument name="storeView" type="string" defaultValue="{{_defaultStore.name}}"/> + </arguments> + + <amOnPage url="{{AdminCustomerPage.url}}" stepKey="goToCustomersPage"/> + <click stepKey="addNewCustomer" selector="{{AdminCustomerGridMainActionsSection.addNewCustomer}}"/> + <selectOption stepKey="selectWebSite" selector="{{AdminCustomerAccountInformationSection.associateToWebsite}}" userInput="{{website}}"/> + <selectOption selector="{{AdminCustomerAccountInformationSection.group}}" userInput="{{customerData.group}}" stepKey="selectCustomerGroup"/> + <fillField stepKey="FillFirstName" selector="{{AdminCustomerAccountInformationSection.firstName}}" userInput="{{customerData.firstname}}"/> + <fillField stepKey="FillLastName" selector="{{AdminCustomerAccountInformationSection.lastName}}" userInput="{{customerData.lastname}}"/> + <fillField stepKey="FillEmail" selector="{{AdminCustomerAccountInformationSection.email}}" userInput="{{customerData.email}}"/> + <selectOption stepKey="selectStoreView" selector="{{AdminCustomerAccountInformationSection.storeView}}" userInput="{{storeView}}"/> + <waitForElement selector="{{AdminCustomerAccountInformationSection.storeView}}" stepKey="waitForCustomerStoreViewExpand"/> + <click stepKey="save" selector="{{AdminCustomerAccountInformationSection.saveCustomer}}"/> + <waitForPageLoad stepKey="waitForCustomersPage"/> + <see stepKey="seeSuccessMessage" userInput="You saved the customer."/> + </actionGroup> +</actionGroups> diff --git a/app/code/Magento/Customer/Test/Mftf/ActionGroup/AdminCreateCustomerWithWebsiteAndStoreViewActionGroup.xml b/app/code/Magento/Customer/Test/Mftf/ActionGroup/AdminCreateCustomerWithWebsiteAndStoreViewActionGroup.xml index 9741d4e4133bf..5757f79a7ac61 100644 --- a/app/code/Magento/Customer/Test/Mftf/ActionGroup/AdminCreateCustomerWithWebsiteAndStoreViewActionGroup.xml +++ b/app/code/Magento/Customer/Test/Mftf/ActionGroup/AdminCreateCustomerWithWebsiteAndStoreViewActionGroup.xml @@ -47,28 +47,4 @@ <click stepKey="saveAddress" selector="{{AdminCustomerAddressesSection.saveAddress}}"/> <waitForPageLoad stepKey="waitForAddressSave"/> </actionGroup> - - <actionGroup name="AdminCreateCustomerWithWebSiteAndGroup"> - <annotations> - <description>Goes to the Customer grid page. Click on 'Add New Customer'. Fills provided Customer Data. Fill provided Customer Address data. Assigns Product to Website and Store View. Clicks on Save.</description> - </annotations> - <arguments> - <argument name="customerData" defaultValue="Simple_US_Customer"/> - <argument name="website" type="string" defaultValue="{{_defaultWebsite.name}}"/> - <argument name="storeView" type="string" defaultValue="{{_defaultStore.name}}"/> - </arguments> - - <amOnPage url="{{AdminCustomerPage.url}}" stepKey="goToCustomersPage"/> - <click stepKey="addNewCustomer" selector="{{AdminCustomerGridMainActionsSection.addNewCustomer}}"/> - <selectOption stepKey="selectWebSite" selector="{{AdminCustomerAccountInformationSection.associateToWebsite}}" userInput="{{website}}"/> - <selectOption selector="{{AdminCustomerAccountInformationSection.group}}" userInput="{{customerData.group}}" stepKey="selectCustomerGroup"/> - <fillField stepKey="FillFirstName" selector="{{AdminCustomerAccountInformationSection.firstName}}" userInput="{{customerData.firstname}}"/> - <fillField stepKey="FillLastName" selector="{{AdminCustomerAccountInformationSection.lastName}}" userInput="{{customerData.lastname}}"/> - <fillField stepKey="FillEmail" selector="{{AdminCustomerAccountInformationSection.email}}" userInput="{{customerData.email}}"/> - <selectOption stepKey="selectStoreView" selector="{{AdminCustomerAccountInformationSection.storeView}}" userInput="{{storeView}}"/> - <waitForElement selector="{{AdminCustomerAccountInformationSection.storeView}}" stepKey="waitForCustomerStoreViewExpand"/> - <click stepKey="save" selector="{{AdminCustomerAccountInformationSection.saveCustomer}}"/> - <waitForPageLoad stepKey="waitForCustomersPage"/> - <see stepKey="seeSuccessMessage" userInput="You saved the customer."/> - </actionGroup> </actionGroups> diff --git a/app/code/Magento/Customer/Test/Mftf/ActionGroup/AdminCustomerShowDateOfBirthActionGroup.xml b/app/code/Magento/Customer/Test/Mftf/ActionGroup/AdminCustomerShowDateOfBirthActionGroup.xml new file mode 100644 index 0000000000000..009c37c568c44 --- /dev/null +++ b/app/code/Magento/Customer/Test/Mftf/ActionGroup/AdminCustomerShowDateOfBirthActionGroup.xml @@ -0,0 +1,26 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + /** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +--> + +<actionGroups xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/actionGroupSchema.xsd"> + <actionGroup name="AdminCustomerShowDateOfBirthActionGroup"> + <annotations> + <description>Goes to the customer configuration. Set "Show Date of Birth" with provided value.</description> + </annotations> + <arguments> + <argument name="value" type="string" defaultValue="{{ShowDateOfBirth.optional}}"/> + </arguments> + <amOnPage url="{{AdminCustomerConfigPage.url('#customer_address-link')}}" stepKey="openCustomerConfigPage"/> + <waitForPageLoad stepKey="waitCustomerConfigPage"/> + <scrollTo selector="{{AdminCustomerConfigSection.showDateOfBirth}}" x="0" y="-100" stepKey="scrollToShowDateOfBirth"/> + <uncheckOption selector="{{AdminCustomerConfigSection.showDateOfBirthInherit}}" stepKey="uncheckUseSystem"/> + <selectOption selector="{{AdminCustomerConfigSection.showDateOfBirth}}" userInput="{{value}}" stepKey="fillShowDateOfBirth"/> + <click selector="{{AdminMainActionsSection.save}}" stepKey="clickSave"/> + <seeElement selector="{{AdminMessagesSection.success}}" stepKey="seeSuccessMessage"/> + </actionGroup> +</actionGroups> diff --git a/app/code/Magento/Customer/Test/Mftf/ActionGroup/AdminCustomerSubscribeNewsletterActionGroup.xml b/app/code/Magento/Customer/Test/Mftf/ActionGroup/AdminCustomerSubscribeNewsletterActionGroup.xml deleted file mode 100644 index 49ea772569cc0..0000000000000 --- a/app/code/Magento/Customer/Test/Mftf/ActionGroup/AdminCustomerSubscribeNewsletterActionGroup.xml +++ /dev/null @@ -1,36 +0,0 @@ -<?xml version="1.0" encoding="UTF-8"?> -<!-- - /** - * Copyright © Magento, Inc. All rights reserved. - * See COPYING.txt for license details. - */ ---> - -<actionGroups xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" - xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/actionGroupSchema.xsd"> - <actionGroup name="AdminSubscribeCustomerToNewsletters"> - <annotations> - <description>Set checkbox "Newsletter subscribed" on "Newsletter" tab on customer edit page.</description> - </annotations> - <arguments> - <argument name="websiteId" type="string" defaultValue="1"/> - </arguments> - - <click selector="{{AdminEditCustomerInformationSection.newsLetter}}" stepKey="clickToNewsletterTabHeader"/> - <waitForPageLoad stepKey="waitForShowNewsletterTab"/> - <checkOption selector="{{AdminEditCustomerNewsletterSection.subscribedStatus(websiteId)}}" stepKey="subscribeToNewsletter"/> - <click selector="{{AdminCustomerMainActionsSection.saveAndContinue}}" stepKey="saveAndContinue"/> - <waitForPageLoad stepKey="waitForSaving"/> - <see userInput="You saved the customer." stepKey="seeSuccessMessage"/> - </actionGroup> - - <actionGroup name="AdminSubscribeCustomerToNewslettersAndSelectStoreView" extends="AdminSubscribeCustomerToNewsletters"> - <annotations> - <description>Set checkbox "Newsletter subscribed" and select Store View on "Newsletter" tab on customer edit page.</description> - </annotations> - <arguments> - <argument name="storeView"/> - </arguments> - <selectOption selector="{{AdminEditCustomerNewsletterSection.subscribedStore(websiteId)}}" userInput="{{storeView.name}}" stepKey="selectSubscribeStoreView" after="subscribeToNewsletter"/> - </actionGroup> -</actionGroups> \ No newline at end of file diff --git a/app/code/Magento/Customer/Test/Mftf/ActionGroup/AdminCustomerSubscribeNewsletterActionGroup/AdminSubscribeCustomerToNewslettersActionGroup.xml b/app/code/Magento/Customer/Test/Mftf/ActionGroup/AdminCustomerSubscribeNewsletterActionGroup/AdminSubscribeCustomerToNewslettersActionGroup.xml new file mode 100644 index 0000000000000..87253fb833a1f --- /dev/null +++ b/app/code/Magento/Customer/Test/Mftf/ActionGroup/AdminCustomerSubscribeNewsletterActionGroup/AdminSubscribeCustomerToNewslettersActionGroup.xml @@ -0,0 +1,25 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + /** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +--> +<actionGroups xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/actionGroupSchema.xsd"> + <actionGroup name="AdminSubscribeCustomerToNewsletters"> + <annotations> + <description>Set checkbox "Newsletter subscribed" on "Newsletter" tab on customer edit page.</description> + </annotations> + <arguments> + <argument name="websiteId" type="string" defaultValue="1"/> + </arguments> + + <click selector="{{AdminEditCustomerInformationSection.newsLetter}}" stepKey="clickToNewsletterTabHeader"/> + <waitForPageLoad stepKey="waitForShowNewsletterTab"/> + <checkOption selector="{{AdminEditCustomerNewsletterSection.subscribedStatus(websiteId)}}" stepKey="subscribeToNewsletter"/> + <click selector="{{AdminCustomerMainActionsSection.saveAndContinue}}" stepKey="saveAndContinue"/> + <waitForPageLoad stepKey="waitForSaving"/> + <see userInput="You saved the customer." stepKey="seeSuccessMessage"/> + </actionGroup> +</actionGroups> diff --git a/app/code/Magento/Customer/Test/Mftf/ActionGroup/AdminCustomerSubscribeNewsletterActionGroup/AdminSubscribeCustomerToNewslettersAndSelectStoreViewActionGroup.xml b/app/code/Magento/Customer/Test/Mftf/ActionGroup/AdminCustomerSubscribeNewsletterActionGroup/AdminSubscribeCustomerToNewslettersAndSelectStoreViewActionGroup.xml new file mode 100644 index 0000000000000..f80a84e083435 --- /dev/null +++ b/app/code/Magento/Customer/Test/Mftf/ActionGroup/AdminCustomerSubscribeNewsletterActionGroup/AdminSubscribeCustomerToNewslettersAndSelectStoreViewActionGroup.xml @@ -0,0 +1,19 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + /** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +--> +<actionGroups xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/actionGroupSchema.xsd"> + <actionGroup name="AdminSubscribeCustomerToNewslettersAndSelectStoreView" extends="AdminSubscribeCustomerToNewsletters"> + <annotations> + <description>Set checkbox "Newsletter subscribed" and select Store View on "Newsletter" tab on customer edit page.</description> + </annotations> + <arguments> + <argument name="storeView"/> + </arguments> + <selectOption selector="{{AdminEditCustomerNewsletterSection.subscribedStore(websiteId)}}" userInput="{{storeView.name}}" stepKey="selectSubscribeStoreView" after="subscribeToNewsletter"/> + </actionGroup> +</actionGroups> diff --git a/app/code/Magento/Customer/Test/Mftf/ActionGroup/AdminDeleteCustomerActionGroup.xml b/app/code/Magento/Customer/Test/Mftf/ActionGroup/AdminDeleteCustomerActionGroup.xml index ebbd9ce469587..87c612db08698 100644 --- a/app/code/Magento/Customer/Test/Mftf/ActionGroup/AdminDeleteCustomerActionGroup.xml +++ b/app/code/Magento/Customer/Test/Mftf/ActionGroup/AdminDeleteCustomerActionGroup.xml @@ -15,9 +15,13 @@ <arguments> <argument name="customerEmail"/> </arguments> - + <amOnPage url="{{AdminCustomerPage.url}}" stepKey="navigateToCustomersPage"/> <conditionalClick selector="{{AdminCustomerFiltersSection.clearAll}}" dependentSelector="{{AdminCustomerFiltersSection.clearAll}}" visible="true" stepKey="clickClearFilters"/> + <waitForPageLoad stepKey="waitForFiltersClear"/> + <click selector="{{AdminCustomerFiltersSection.filtersButton}}" stepKey="openFilters"/> + <fillField selector="{{AdminCustomerFiltersSection.emailInput}}" userInput="{{customerEmail}}" stepKey="fillEmail"/> + <click selector="{{AdminCustomerFiltersSection.apply}}" stepKey="clickApplyFilters"/> <click stepKey="chooseCustomer" selector="{{AdminCustomerGridMainActionsSection.customerCheckbox(customerEmail)}}"/> <click stepKey="openActions" selector="{{AdminCustomerGridMainActionsSection.actions}}"/> <waitForPageLoad stepKey="waitActions"/> diff --git a/app/code/Magento/Customer/Test/Mftf/ActionGroup/AdminEditCustomerAddressNoZipNoStateActionGroup.xml b/app/code/Magento/Customer/Test/Mftf/ActionGroup/AdminEditCustomerAddressNoZipNoStateActionGroup.xml index 4c3660ac605a6..2ccef6f72e23f 100644 --- a/app/code/Magento/Customer/Test/Mftf/ActionGroup/AdminEditCustomerAddressNoZipNoStateActionGroup.xml +++ b/app/code/Magento/Customer/Test/Mftf/ActionGroup/AdminEditCustomerAddressNoZipNoStateActionGroup.xml @@ -8,7 +8,7 @@ <actionGroups xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/actionGroupSchema.xsd"> - <actionGroup name="AdminEditCustomerAddressNoZipNoState" extends="AdminEditCustomerAddressesFrom"> + <actionGroup name="AdminEditCustomerAddressNoZipNoStateActionGroup" extends="AdminEditCustomerAddressesFromActionGroup"> <annotations> <description>EXTENDS: AdminEditCustomerAddressesFrom. Removes 'selectState' and 'fillZipCode'. Clicks on 'Set Default' for Billing/Shipping.</description> </annotations> diff --git a/app/code/Magento/Customer/Test/Mftf/ActionGroup/AdminEditCustomerAddressSetDefaultShippingAndBillingActionGroup.xml b/app/code/Magento/Customer/Test/Mftf/ActionGroup/AdminEditCustomerAddressSetDefaultShippingAndBillingActionGroup.xml index 3551375c0e76b..e39800f8d9dd9 100644 --- a/app/code/Magento/Customer/Test/Mftf/ActionGroup/AdminEditCustomerAddressSetDefaultShippingAndBillingActionGroup.xml +++ b/app/code/Magento/Customer/Test/Mftf/ActionGroup/AdminEditCustomerAddressSetDefaultShippingAndBillingActionGroup.xml @@ -8,7 +8,7 @@ <actionGroups xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/actionGroupSchema.xsd"> - <actionGroup name="AdminEditCustomerAddressSetDefaultShippingAndBilling" extends="AdminEditCustomerAddressesFrom"> + <actionGroup name="AdminEditCustomerAddressSetDefaultShippingAndBillingActionGroup" extends="AdminEditCustomerAddressesFromActionGroup"> <annotations> <description>EXTENDS: AdminEditCustomerAddressesFrom. Removes 'selectState' and 'fillZipCode'.</description> </annotations> diff --git a/app/code/Magento/Customer/Test/Mftf/ActionGroup/AdminEditCustomerAddressesFromActionGroup.xml b/app/code/Magento/Customer/Test/Mftf/ActionGroup/AdminEditCustomerAddressesFromActionGroup.xml index 8ac4779de7a9a..c2b41251c05b1 100644 --- a/app/code/Magento/Customer/Test/Mftf/ActionGroup/AdminEditCustomerAddressesFromActionGroup.xml +++ b/app/code/Magento/Customer/Test/Mftf/ActionGroup/AdminEditCustomerAddressesFromActionGroup.xml @@ -9,7 +9,7 @@ <actionGroups xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/actionGroupSchema.xsd"> <!-- Same as "EditCustomerAddressesFromAdminActionGroup" but taking country and state from input "customerAddress" --> - <actionGroup name="AdminEditCustomerAddressesFrom"> + <actionGroup name="AdminEditCustomerAddressesFromActionGroup"> <annotations> <description>Adds the provided Address to a Customer from the Admin Customers creation/edit page.</description> </annotations> @@ -34,38 +34,4 @@ <click selector="{{AdminEditCustomerAddressesSection.save}}" stepKey="saveAddress"/> <waitForPageLoad stepKey="waitForAddressSaved"/> </actionGroup> - - <actionGroup name="AdminEditCustomerAddressSetDefaultShippingAndBilling" extends="AdminEditCustomerAddressesFrom"> - <annotations> - <description>EXTENDS: AdminEditCustomerAddressesFrom. Clicks on 'Set Default' for Billing/Shipping.</description> - </annotations> - - <click selector="{{AdminEditCustomerAddressesSection.defaultBillingAddressButton}}" stepKey="setDefaultBilling" before="setDefaultShipping"/> - <click selector="{{AdminEditCustomerAddressesSection.defaultShippingAddressButton}}" stepKey="setDefaultShipping" before="fillPrefixName"/> - </actionGroup> - - <actionGroup name="AdminEditCustomerAddressNoZipNoState" extends="AdminEditCustomerAddressesFrom"> - <annotations> - <description>EXTENDS: AdminEditCustomerAddressesFrom. Removes 'selectState' and 'fillZipCode'. Clicks on 'Set Default' for Billing/Shipping.</description> - </annotations> - - <remove keyForRemoval="selectState"/> - <remove keyForRemoval="fillZipCode"/> - <click selector="{{AdminEditCustomerAddressesSection.defaultBillingAddressButton}}" stepKey="setDefaultBilling" before="setDefaultShipping"/> - <click selector="{{AdminEditCustomerAddressesSection.defaultShippingAddressButton}}" stepKey="setDefaultShipping" before="fillPrefixName"/> - </actionGroup> - - <actionGroup name="SelectDropdownCustomerAddressAttributeValueActionGroup"> - <annotations> - <description>Selects the provided Option in the provided Customer Address Attribute drop down menu. Clicks on Save.</description> - </annotations> - <arguments> - <argument name="customerAddressAttribute"/> - <argument name="optionValue" type="string"/> - </arguments> - - <selectOption selector="{{AdminEditCustomerAddressesSection.dropDownAttribute(customerAddressAttribute.code)}}" userInput="{{optionValue}}" stepKey="selectOptionValue"/> - <click selector="{{AdminEditCustomerAddressesSection.save}}" stepKey="saveAddress"/> - <waitForPageLoad stepKey="waitForAddressSaved"/> - </actionGroup> </actionGroups> diff --git a/app/code/Magento/Customer/Test/Mftf/ActionGroup/AdminGridCustomerGroupEditByCodeActionGroup.xml b/app/code/Magento/Customer/Test/Mftf/ActionGroup/AdminGridCustomerGroupEditByCodeActionGroup.xml new file mode 100644 index 0000000000000..68620931393c4 --- /dev/null +++ b/app/code/Magento/Customer/Test/Mftf/ActionGroup/AdminGridCustomerGroupEditByCodeActionGroup.xml @@ -0,0 +1,17 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. +--> + +<actionGroups xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/actionGroupSchema.xsd"> + <actionGroup name="AdminGridCustomerGroupEditByCodeActionGroup"> + <arguments> + <argument name="customerGroupCode" type="string"/> + </arguments> + + <click selector="{{AdminCustomerGroupMainSection.editButtonByCustomerGroupCode(customerGroupCode)}}" stepKey="clickOnEditCustomerGroup" /> + </actionGroup> +</actionGroups> diff --git a/app/code/Magento/Customer/Test/Mftf/ActionGroup/AdminNavigateCustomerEditPageAddressesTabActionGroup.xml b/app/code/Magento/Customer/Test/Mftf/ActionGroup/AdminNavigateCustomerEditPageAddressesTabActionGroup.xml new file mode 100644 index 0000000000000..790a99c9092bc --- /dev/null +++ b/app/code/Magento/Customer/Test/Mftf/ActionGroup/AdminNavigateCustomerEditPageAddressesTabActionGroup.xml @@ -0,0 +1,19 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + /** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +--> + +<actionGroups xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/actionGroupSchema.xsd"> + <actionGroup name="AdminNavigateCustomerEditPageAddressesTabActionGroup" extends="AdminOpenCustomerEditPageActionGroup"> + <annotations> + <description>EXTENDS: AdminOpenCustomerEditPageActionGroup. Navigates to Addresses Tab in Admin Customer Edit page for the provided Customer ID #.</description> + </annotations> + + <click selector="{{AdminEditCustomerInformationSection.addresses}}" after="waitForPageLoad" stepKey="navigateToAddressesTab"/> + <waitForPageLoad stepKey="waitForPageLoad"/> + </actionGroup> +</actionGroups> diff --git a/app/code/Magento/Customer/Test/Mftf/ActionGroup/AdminNavigateToCustomerGroupPageActionGroup.xml b/app/code/Magento/Customer/Test/Mftf/ActionGroup/AdminNavigateToCustomerGroupPageActionGroup.xml new file mode 100644 index 0000000000000..b782436a20949 --- /dev/null +++ b/app/code/Magento/Customer/Test/Mftf/ActionGroup/AdminNavigateToCustomerGroupPageActionGroup.xml @@ -0,0 +1,19 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + /** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +--> + +<actionGroups xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/actionGroupSchema.xsd"> + <actionGroup name="AdminNavigateToCustomerGroupPageActionGroup"> + <annotations> + <description>Goes to the Admin Customer Groups page.</description> + </annotations> + + <amOnPage url="{{AdminCustomerGroupPage.url}}" stepKey="openCustomersGridPage"/> + <waitForPageLoad stepKey="waitForPageLoad"/> + </actionGroup> +</actionGroups> diff --git a/app/code/Magento/Customer/Test/Mftf/ActionGroup/AdminOpenCustomerGroupEditPageFromGridActionGroup.xml b/app/code/Magento/Customer/Test/Mftf/ActionGroup/AdminOpenCustomerGroupEditPageFromGridActionGroup.xml new file mode 100644 index 0000000000000..f27f418496204 --- /dev/null +++ b/app/code/Magento/Customer/Test/Mftf/ActionGroup/AdminOpenCustomerGroupEditPageFromGridActionGroup.xml @@ -0,0 +1,18 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + /** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +--> +<actionGroups xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/actionGroupSchema.xsd"> + <actionGroup name="AdminOpenCustomerGroupEditPageFromGridActionGroup"> + <arguments> + <argument name="groupCode" type="string"/> + </arguments> + <conditionalClick selector="{{AdminCustomerGroupMainSection.selectFirstRow}}" dependentSelector="{{AdminCustomerGroupMainSection.selectFirstRow}}" visible="true" stepKey="clickSelectButton"/> + <click selector="{{AdminCustomerGroupMainSection.editButtonByCustomerGroupCode(groupCode)}}" stepKey="clickOnEditCustomerGroup" /> + <waitForPageLoad stepKey="waitForCustomerGroupEditPage"/> + </actionGroup> +</actionGroups> diff --git a/app/code/Magento/Customer/Test/Mftf/ActionGroup/AdminOpenCustomerGroupsGridPageActionGroup.xml b/app/code/Magento/Customer/Test/Mftf/ActionGroup/AdminOpenCustomerGroupsGridPageActionGroup.xml new file mode 100644 index 0000000000000..d6fd64426e3cf --- /dev/null +++ b/app/code/Magento/Customer/Test/Mftf/ActionGroup/AdminOpenCustomerGroupsGridPageActionGroup.xml @@ -0,0 +1,15 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + /** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +--> + +<actionGroups xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/actionGroupSchema.xsd"> + <actionGroup name="AdminOpenCustomerGroupsGridPageActionGroup"> + <amOnPage url="{{AdminCustomerGroupsIndexPage.url}}" stepKey="goToAdminCustomerGroupIndexPage"/> + <waitForPageLoad time="30" stepKey="waitForCustomerGroupIndexPageLoad"/> + </actionGroup> +</actionGroups> \ No newline at end of file diff --git a/app/code/Magento/Customer/Test/Mftf/ActionGroup/AssertAdminCustomerDateOfBirthValidationMessageActionGroup.xml b/app/code/Magento/Customer/Test/Mftf/ActionGroup/AssertAdminCustomerDateOfBirthValidationMessageActionGroup.xml new file mode 100644 index 0000000000000..03682f9232eab --- /dev/null +++ b/app/code/Magento/Customer/Test/Mftf/ActionGroup/AssertAdminCustomerDateOfBirthValidationMessageActionGroup.xml @@ -0,0 +1,25 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + /** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +--> + +<actionGroups xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/actionGroupSchema.xsd"> + <actionGroup name="AssertAdminCustomerDateOfBirthValidationMessageActionGroup"> + <annotations> + <description>Fills 'Date of Birth' input with provided 'dob' value, clicks 'save' button, checks + there is no provided validation error message for the 'Date of Birth' input on the page.</description> + </annotations> + <arguments> + <argument name="message" type="string" defaultValue="The Date of Birth should not be greater than today."/> + <argument name="dob" type="string" defaultValue="15/01/1970"/> + </arguments> + + <fillField userInput="{{dob}}" selector="{{AdminCustomerAccountInformationSection.dateOfBirth}}" stepKey="fillDateOfBirth"/> + <click selector="{{AdminCustomerMainActionsSection.saveButton}}" stepKey="saveCustomer"/> + <dontSee selector="{{AdminCustomerAccountInformationSection.dateOfBirthValidationErrorField}}" userInput="{{message}}" stepKey="seeTheErrorMessageIsDisplayed"/> + </actionGroup> +</actionGroups> diff --git a/app/code/Magento/Customer/Test/Mftf/ActionGroup/AssertAuthorizationPopUpPasswordAutoCompleteOffActionGroup.xml b/app/code/Magento/Customer/Test/Mftf/ActionGroup/AssertAuthorizationPopUpPasswordAutoCompleteOffActionGroup.xml index c5c9c0a6c9cf8..b29ff9ad3efeb 100644 --- a/app/code/Magento/Customer/Test/Mftf/ActionGroup/AssertAuthorizationPopUpPasswordAutoCompleteOffActionGroup.xml +++ b/app/code/Magento/Customer/Test/Mftf/ActionGroup/AssertAuthorizationPopUpPasswordAutoCompleteOffActionGroup.xml @@ -14,6 +14,8 @@ </annotations> <waitForElementVisible selector="{{StorefrontCustomerSignInPopupFormSection.password}}" stepKey="waitPasswordFieldVisible"/> - <assertElementContainsAttribute selector="{{StorefrontCustomerSignInPopupFormSection.password}}" attribute="autocomplete" expectedValue="off" stepKey="assertAuthorizationPopupPasswordAutocompleteOff"/> + <assertElementContainsAttribute stepKey="assertAuthorizationPopupPasswordAutocompleteOff"> + <expectedResult selector="{{StorefrontCustomerSignInPopupFormSection.password}}" attribute="autocomplete" type="string">off</expectedResult> + </assertElementContainsAttribute> </actionGroup> </actionGroups> diff --git a/app/code/Magento/Customer/Test/Mftf/ActionGroup/AssertDeleteCustomerGroupButtonMissingActionGroup.xml b/app/code/Magento/Customer/Test/Mftf/ActionGroup/AssertDeleteCustomerGroupButtonMissingActionGroup.xml new file mode 100644 index 0000000000000..e45490e6330e5 --- /dev/null +++ b/app/code/Magento/Customer/Test/Mftf/ActionGroup/AssertDeleteCustomerGroupButtonMissingActionGroup.xml @@ -0,0 +1,14 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + /** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +--> + +<actionGroups xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/actionGroupSchema.xsd"> + <actionGroup name="AssertDeleteCustomerGroupButtonMissingActionGroup"> + <dontSeeElement selector="AdminEditCustomerGroupSection.deleteButton" stepKey="dontSeeDeleteButton"/> + </actionGroup> +</actionGroups> diff --git a/app/code/Magento/Customer/Test/Mftf/ActionGroup/AssertSignedUpNewsletterActionGroup.xml b/app/code/Magento/Customer/Test/Mftf/ActionGroup/AssertSignedUpNewsletterActionGroup.xml new file mode 100644 index 0000000000000..deabec7d671d6 --- /dev/null +++ b/app/code/Magento/Customer/Test/Mftf/ActionGroup/AssertSignedUpNewsletterActionGroup.xml @@ -0,0 +1,26 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + /** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +--> + +<actionGroups xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/actionGroupSchema.xsd"> + <actionGroup name="AssertSignedUpNewsletterActionGroup"> + <annotations> + <description>Validates that the provided Customer details are present and correct on the Storefront Customer Dashboard page.</description> + </annotations> + <arguments> + <argument name="customer" defaultValue="CustomerEntityOne"/> + <argument name="storeName" defaultValue="Main Website" type="string"/> + </arguments> + + <see stepKey="successMessage" userInput="Thank you for registering with {{storeName}} Store." selector="{{AdminCustomerMessagesSection.successMessage}}"/> + <see stepKey="seeFirstName" userInput="{{customer.firstname}}" selector="{{StorefrontCustomerDashboardAccountInformationSection.ContactInformation}}"/> + <see stepKey="seeLastName" userInput="{{customer.lastname}}" selector="{{StorefrontCustomerDashboardAccountInformationSection.ContactInformation}}"/> + <see stepKey="seeEmail" userInput="{{customer.email}}" selector="{{StorefrontCustomerDashboardAccountInformationSection.ContactInformation}}"/> + <seeInCurrentUrl url="{{StorefrontCustomerDashboardPage.url}}" stepKey="seeAssertInCurrentUrl"/> + </actionGroup> +</actionGroups> diff --git a/app/code/Magento/Customer/Test/Mftf/ActionGroup/AssertStorefrontAccountPasswordFieldsNotAvailableActionGroup.xml b/app/code/Magento/Customer/Test/Mftf/ActionGroup/AssertStorefrontAccountPasswordFieldsNotAvailableActionGroup.xml new file mode 100644 index 0000000000000..1e828414935da --- /dev/null +++ b/app/code/Magento/Customer/Test/Mftf/ActionGroup/AssertStorefrontAccountPasswordFieldsNotAvailableActionGroup.xml @@ -0,0 +1,16 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + /** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +--> + +<actionGroups xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/actionGroupSchema.xsd"> + <actionGroup name="AssertStorefrontAccountPasswordFieldsNotAvailableActionGroup"> + <dontSee stepKey="dontSeeCurrentPasswordField" selector="{{StorefrontCustomerAccountInformationSection.currentPassword}}"/> + <dontSee stepKey="dontSeeNewPasswordField" selector="{{StorefrontCustomerAccountInformationSection.newPassword}}"/> + <dontSee stepKey="dontSeeConfirmPasswordField" selector="{{StorefrontCustomerAccountInformationSection.confirmNewPassword}}"/> + </actionGroup> +</actionGroups> diff --git a/app/code/Magento/Customer/Test/Mftf/ActionGroup/AssertStorefrontCustomerMessagesActionGroup.xml b/app/code/Magento/Customer/Test/Mftf/ActionGroup/AssertStorefrontCustomerMessagesActionGroup.xml new file mode 100644 index 0000000000000..50e052207bd9e --- /dev/null +++ b/app/code/Magento/Customer/Test/Mftf/ActionGroup/AssertStorefrontCustomerMessagesActionGroup.xml @@ -0,0 +1,19 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + /** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +--> + +<actionGroups xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/actionGroupSchema.xsd"> + <actionGroup name="AssertStorefrontCustomerMessagesActionGroup"> + <arguments> + <argument name="message" type="string"/> + </arguments> + + <waitForElementVisible selector="{{StorefrontCustomerMessagesSection.successMessage}}" stepKey="waitForElement"/> + <see userInput="{{message}}" selector="{{StorefrontCustomerMessagesSection.successMessage}}" stepKey="seeMessage"/> + </actionGroup> +</actionGroups> diff --git a/app/code/Magento/Customer/Test/Mftf/ActionGroup/AssertStorefrontDefaultWelcomeMessageActionGroup.xml b/app/code/Magento/Customer/Test/Mftf/ActionGroup/AssertStorefrontDefaultWelcomeMessageActionGroup.xml new file mode 100644 index 0000000000000..7cfbc0283d505 --- /dev/null +++ b/app/code/Magento/Customer/Test/Mftf/ActionGroup/AssertStorefrontDefaultWelcomeMessageActionGroup.xml @@ -0,0 +1,20 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + /** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +--> + +<actionGroups xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/actionGroupSchema.xsd"> + <actionGroup name="AssertStorefrontDefaultWelcomeMessageActionGroup"> + <annotations> + <description>Validates that the Welcome message is present and correct and not you link absent.</description> + </annotations> + + <waitForElementVisible selector="{{StorefrontPanelHeaderSection.welcomeMessage}}" stepKey="waitDefaultMessage"/> + <see userInput="Default welcome msg!" selector="{{StorefrontPanelHeaderSection.welcomeMessage}}" stepKey="verifyDefaultMessage"/> + <dontSeeElement selector="{{StorefrontPanelHeaderSection.notYouLink}}" stepKey="checkAbsenceLinkNotYou"/> + </actionGroup> +</actionGroups> diff --git a/app/code/Magento/Customer/Test/Mftf/ActionGroup/AssertStorefrontPasswordAutocompleteOffActionGroup.xml b/app/code/Magento/Customer/Test/Mftf/ActionGroup/AssertStorefrontPasswordAutocompleteOffActionGroup.xml index 1b2ec80f87340..6e54222d69b76 100644 --- a/app/code/Magento/Customer/Test/Mftf/ActionGroup/AssertStorefrontPasswordAutocompleteOffActionGroup.xml +++ b/app/code/Magento/Customer/Test/Mftf/ActionGroup/AssertStorefrontPasswordAutocompleteOffActionGroup.xml @@ -14,6 +14,8 @@ </annotations> <amOnPage stepKey="amOnSignInPage" url="{{StorefrontCustomerSignInPage.url}}"/> - <assertElementContainsAttribute selector="{{StorefrontCustomerSignInFormSection.passwordField}}" attribute="autocomplete" expectedValue="off" stepKey="assertSignInPasswordAutocompleteOff"/> + <assertElementContainsAttribute stepKey="assertSignInPasswordAutocompleteOff"> + <expectedResult selector="{{StorefrontCustomerSignInFormSection.passwordField}}" attribute="autocomplete" type="string">off</expectedResult> + </assertElementContainsAttribute> </actionGroup> </actionGroups> diff --git a/app/code/Magento/Customer/Test/Mftf/ActionGroup/DeleteCustomerActionGroup.xml b/app/code/Magento/Customer/Test/Mftf/ActionGroup/DeleteCustomerActionGroup.xml index 58777ec0d3515..62f4c87545c6f 100644 --- a/app/code/Magento/Customer/Test/Mftf/ActionGroup/DeleteCustomerActionGroup.xml +++ b/app/code/Magento/Customer/Test/Mftf/ActionGroup/DeleteCustomerActionGroup.xml @@ -15,7 +15,7 @@ <arguments> <argument name="lastName" defaultValue=""/> </arguments> - + <!--Clear filter if exist--> <amOnPage url="{{AdminCustomerPage.url}}" stepKey="navigateToCustomers"/> <conditionalClick selector="{{AdminDataGridHeaderSection.clearFilters}}" dependentSelector="{{AdminDataGridHeaderSection.clearFilters}}" visible="true" stepKey="clearExistingCustomerFilters"/> @@ -28,29 +28,6 @@ <waitForPageLoad stepKey="waitForDeleteItemPopup" time="10"/> <click stepKey="clickOnOk" selector="{{CustomersPageSection.ok}}"/> <waitForElementVisible stepKey="waitForSuccessfullyDeletedMessage" selector="{{CustomersPageSection.deletedSuccessMessage}}" time="10"/> - </actionGroup> - - <actionGroup name="DeleteCustomerByEmailActionGroup"> - <annotations> - <description>Goes to the Admin Customers grid page. Deletes a Customer based on the provided Email Address.</description> - </annotations> - <arguments> - <argument name="email" type="string"/> - </arguments> - - <amOnPage url="{{AdminCustomerPage.url}}" stepKey="navigateToCustomers"/> - <waitForPageLoad stepKey="waitForAdminCustomerPageLoad"/> - <click selector="{{AdminCustomerFiltersSection.filtersButton}}" stepKey="clickFilterButton"/> - <conditionalClick selector="{{AdminDataGridHeaderSection.clearFilters}}" dependentSelector="{{AdminDataGridHeaderSection.clearFilters}}" visible="true" stepKey="cleanFiltersIfTheySet"/> - <waitForPageLoad stepKey="waitForClearFilters"/> - <fillField selector="{{AdminCustomerFiltersSection.emailInput}}" userInput="{{email}}" stepKey="filterEmail"/> - <click selector="{{AdminCustomerFiltersSection.apply}}" stepKey="applyFilter"/> - <waitForPageLoad stepKey="waitForPageToLoad"/> - <click selector="{{AdminCustomerGridSection.selectFirstRow}}" stepKey="clickOnEditButton1"/> - <click selector="{{CustomersPageSection.actions}}" stepKey="clickActionsDropdown"/> - <click selector="{{CustomersPageSection.delete}}" stepKey="clickDelete"/> - <waitForElementVisible selector="{{CustomersPageSection.ok}}" stepKey="waitForOkToVisible"/> - <click selector="{{CustomersPageSection.ok}}" stepKey="clickOkConfirmationButton"/> - <waitForElementVisible stepKey="waitForSuccessfullyDeletedMessage" selector="{{CustomersPageSection.deletedSuccessMessage}}" time="30"/> + <waitForLoadingMaskToDisappear stepKey="waitForLoadingMaskDisappear"/> </actionGroup> </actionGroups> diff --git a/app/code/Magento/Customer/Test/Mftf/ActionGroup/DeleteCustomerByEmailActionGroup.xml b/app/code/Magento/Customer/Test/Mftf/ActionGroup/DeleteCustomerByEmailActionGroup.xml new file mode 100644 index 0000000000000..5920596633f76 --- /dev/null +++ b/app/code/Magento/Customer/Test/Mftf/ActionGroup/DeleteCustomerByEmailActionGroup.xml @@ -0,0 +1,34 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + /** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +--> + +<actionGroups xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/actionGroupSchema.xsd"> + <actionGroup name="DeleteCustomerByEmailActionGroup"> + <annotations> + <description>Goes to the Admin Customers grid page. Deletes a Customer based on the provided Email Address.</description> + </annotations> + <arguments> + <argument name="email" type="string"/> + </arguments> + + <amOnPage url="{{AdminCustomerPage.url}}" stepKey="navigateToCustomers"/> + <waitForPageLoad stepKey="waitForAdminCustomerPageLoad"/> + <click selector="{{AdminCustomerFiltersSection.filtersButton}}" stepKey="clickFilterButton"/> + <conditionalClick selector="{{AdminDataGridHeaderSection.clearFilters}}" dependentSelector="{{AdminDataGridHeaderSection.clearFilters}}" visible="true" stepKey="cleanFiltersIfTheySet"/> + <waitForPageLoad stepKey="waitForClearFilters"/> + <fillField selector="{{AdminCustomerFiltersSection.emailInput}}" userInput="{{email}}" stepKey="filterEmail"/> + <click selector="{{AdminCustomerFiltersSection.apply}}" stepKey="applyFilter"/> + <waitForPageLoad stepKey="waitForPageToLoad"/> + <click selector="{{AdminCustomerGridSection.selectFirstRow}}" stepKey="clickOnEditButton1"/> + <click selector="{{CustomersPageSection.actions}}" stepKey="clickActionsDropdown"/> + <click selector="{{CustomersPageSection.delete}}" stepKey="clickDelete"/> + <waitForElementVisible selector="{{CustomersPageSection.ok}}" stepKey="waitForOkToVisible"/> + <click selector="{{CustomersPageSection.ok}}" stepKey="clickOkConfirmationButton"/> + <waitForElementVisible stepKey="waitForSuccessfullyDeletedMessage" selector="{{CustomersPageSection.deletedSuccessMessage}}" time="30"/> + </actionGroup> +</actionGroups> diff --git a/app/code/Magento/Customer/Test/Mftf/ActionGroup/DeleteCustomerFromAdminActionGroup.xml b/app/code/Magento/Customer/Test/Mftf/ActionGroup/DeleteCustomerFromAdminActionGroup.xml new file mode 100644 index 0000000000000..04083f688b75d --- /dev/null +++ b/app/code/Magento/Customer/Test/Mftf/ActionGroup/DeleteCustomerFromAdminActionGroup.xml @@ -0,0 +1,31 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + /** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +--> + +<actionGroups xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/actionGroupSchema.xsd"> + <actionGroup name="DeleteCustomerFromAdminActionGroup"> + <annotations> + <description>Goes to the Admin Customers grid page. Deletes the provided Customer from the grid. Validates that the Success message is present and correct.</description> + </annotations> + <arguments> + <argument name="customer" defaultValue="CustomerEntityOne"/> + </arguments> + + <amOnPage url="{{AdminCustomerPage.url}}" stepKey="navigateToCustomers"/> + <conditionalClick selector="{{AdminDataGridHeaderSection.clearFilters}}" dependentSelector="{{AdminDataGridHeaderSection.clearFilters}}" visible="true" stepKey="clickOnButtonToRemoveFiltersIfPresent"/> + <fillField selector="{{AdminDataGridHeaderSection.search}}" userInput="{{customer.email}}" stepKey="fillSearch"/> + <click selector="{{AdminDataGridHeaderSection.submitSearch}}" stepKey="clickSubmit"/> + <waitForAjaxLoad stepKey="waitForLoadAjax"/> + <click selector="{{AdminCustomerGridMainActionsSection.multicheck}}" stepKey="selectAll"/> + <click selector="{{AdminCustomerGridMainActionsSection.actions}}" stepKey="clickActions"/> + <click selector="{{AdminCustomerGridMainActionsSection.delete}}" stepKey="clickDelete"/> + <waitForAjaxLoad stepKey="waitForLoadConfirmation"/> + <click selector="{{AdminConfirmationModalSection.ok}}" stepKey="confirmDelete"/> + <see selector="{{AdminMessagesSection.success}}" userInput="A total of 1 record(s) were deleted" stepKey="seeSuccess"/> + </actionGroup> +</actionGroups> diff --git a/app/code/Magento/Customer/Test/Mftf/ActionGroup/EnterCustomerAddressInfoActionGroup.xml b/app/code/Magento/Customer/Test/Mftf/ActionGroup/EnterCustomerAddressInfoActionGroup.xml new file mode 100644 index 0000000000000..c72010fe5c3bf --- /dev/null +++ b/app/code/Magento/Customer/Test/Mftf/ActionGroup/EnterCustomerAddressInfoActionGroup.xml @@ -0,0 +1,33 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + /** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +--> + +<actionGroups xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/actionGroupSchema.xsd"> + <actionGroup name="EnterCustomerAddressInfoActionGroup"> + <annotations> + <description>Fills in the provided Customer details (First/Last Name, Company, Phone # and Address) on the Admin Customer creation/edit page. Clicks on the Save button.</description> + </annotations> + <arguments> + <argument name="Address"/> + </arguments> + + <amOnPage url="customer/address/new/" stepKey="goToAddressPage"/> + <waitForPageLoad stepKey="waitForAddressPage"/> + <fillField stepKey="fillFirstName" selector="{{StorefrontCustomerAddressSection.firstName}}" userInput="{{Address.firstname}}"/> + <fillField stepKey="fillLastName" selector="{{StorefrontCustomerAddressSection.lastName}}" userInput="{{Address.lastname}}"/> + <fillField stepKey="fillCompany" selector="{{StorefrontCustomerAddressSection.company}}" userInput="{{Address.company}}"/> + <fillField stepKey="fillPhoneNumber" selector="{{StorefrontCustomerAddressSection.phoneNumber}}" userInput="{{Address.telephone}}"/> + <fillField stepKey="fillStreetAddress1" selector="{{StorefrontCustomerAddressSection.streetAddress1}}" userInput="{{Address.street[0]}}"/> + <fillField stepKey="fillStreetAddress2" selector="{{StorefrontCustomerAddressSection.streetAddress2}}" userInput="{{Address.street[1]}}"/> + <fillField stepKey="fillCityName" selector="{{StorefrontCustomerAddressSection.city}}" userInput="{{Address.city}}"/> + <selectOption stepKey="selectCounty" selector="{{StorefrontCustomerAddressSection.country}}" userInput="{{Address.country_id}}"/> + <selectOption stepKey="selectState" selector="{{StorefrontCustomerAddressSection.stateProvince}}" userInput="{{Address.state}}"/> + <fillField stepKey="fillZip" selector="{{StorefrontCustomerAddressSection.zip}}" userInput="{{Address.postcode}}"/> + <click stepKey="saveAddress" selector="{{StorefrontCustomerAddressSection.saveAddress}}"/> + </actionGroup> +</actionGroups> diff --git a/app/code/Magento/Customer/Test/Mftf/ActionGroup/EnterCustomerAddressInfoFillStateActionGroup.xml b/app/code/Magento/Customer/Test/Mftf/ActionGroup/EnterCustomerAddressInfoFillStateActionGroup.xml new file mode 100644 index 0000000000000..257c9d5039c61 --- /dev/null +++ b/app/code/Magento/Customer/Test/Mftf/ActionGroup/EnterCustomerAddressInfoFillStateActionGroup.xml @@ -0,0 +1,18 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + /** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +--> + +<actionGroups xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/actionGroupSchema.xsd"> + <actionGroup name="EnterCustomerAddressInfoFillStateActionGroup" extends="EnterCustomerAddressInfoActionGroup"> + <annotations> + <description>EXTENDS: EnterCustomerAddressInfoActionGroup. Fills the State field.</description> + </annotations> + + <fillField stepKey="selectState" selector="{{StorefrontCustomerAddressSection.stateProvinceFill}}" userInput="{{Address.state}}"/> + </actionGroup> +</actionGroups> diff --git a/app/code/Magento/Customer/Test/Mftf/ActionGroup/NavigateCustomerGroupActionGroup.xml b/app/code/Magento/Customer/Test/Mftf/ActionGroup/NavigateCustomerGroupActionGroup.xml deleted file mode 100644 index 78e9a2f18da95..0000000000000 --- a/app/code/Magento/Customer/Test/Mftf/ActionGroup/NavigateCustomerGroupActionGroup.xml +++ /dev/null @@ -1,19 +0,0 @@ -<?xml version="1.0" encoding="UTF-8"?> -<!-- - /** - * Copyright © Magento, Inc. All rights reserved. - * See COPYING.txt for license details. - */ ---> - -<actionGroups xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" - xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/actionGroupSchema.xsd"> - <actionGroup name="NavigateToCustomerGroupPage"> - <annotations> - <description>Goes to the Admin Customer Groups page.</description> - </annotations> - - <amOnPage url="{{AdminCustomerGroupPage.url}}" stepKey="openCustomersGridPage"/> - <waitForPageLoad stepKey="waitForPageLoad"/> - </actionGroup> -</actionGroups> diff --git a/app/code/Magento/Customer/Test/Mftf/ActionGroup/OpenEditCustomerAddressFromAdminActionGroup.xml b/app/code/Magento/Customer/Test/Mftf/ActionGroup/OpenEditCustomerAddressFromAdminActionGroup.xml new file mode 100644 index 0000000000000..86ac1dc650bbc --- /dev/null +++ b/app/code/Magento/Customer/Test/Mftf/ActionGroup/OpenEditCustomerAddressFromAdminActionGroup.xml @@ -0,0 +1,32 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + /** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +--> + +<actionGroups xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/actionGroupSchema.xsd"> + <actionGroup name="OpenEditCustomerAddressFromAdminActionGroup"> + <annotations> + <description>Filters the Admin Customers Addresses based on the provided Address. Clicks on Edit.</description> + </annotations> + <arguments> + <argument name="address"/> + </arguments> + + <click selector="{{AdminCustomerAccountInformationSection.addressesButton}}" stepKey="openAddressesTab"/> + <waitForElementVisible selector="{{AdminCustomerAddressFiltersSection.filtersButton}}" stepKey="waitForComponentLoad"/> + <click selector="{{AdminCustomerAddressFiltersSection.filtersButton}}" stepKey="openAddressesFilter"/> + <fillField userInput="{{address.firstname}}" selector="{{AdminCustomerAddressFiltersSection.firstnameInput}}" stepKey="fillFirstname"/> + <fillField userInput="{{address.lastname}}" selector="{{AdminCustomerAddressFiltersSection.lastnameInput}}" stepKey="fillLastname"/> + <fillField userInput="{{address.telephone}}" selector="{{AdminCustomerAddressFiltersSection.telephoneInput}}" stepKey="fillCountry"/> + <fillField userInput="{{address.postcode}}" selector="{{AdminCustomerAddressFiltersSection.postcodeInput}}" stepKey="fillPostcode"/> + <click selector="{{AdminCustomerAddressFiltersSection.applyFilter}}" stepKey="applyAddressesFilter"/> + <waitForLoadingMaskToDisappear stepKey="waitForLoadingMaskToDisappear"/> + <click selector="{{AdminCustomerAddressGridSection.firstRowSelectActionLink}}" stepKey="clickAction"/> + <click selector="{{AdminCustomerAddressGridSection.firstRowEditActionLink}}" stepKey="clickEdit"/> + <waitForPageLoad stepKey="waitForModalWindow"/> + </actionGroup> +</actionGroups> diff --git a/app/code/Magento/Customer/Test/Mftf/ActionGroup/OpenEditCustomerFromAdminActionGroup.xml b/app/code/Magento/Customer/Test/Mftf/ActionGroup/OpenEditCustomerFromAdminActionGroup.xml index e338d1ae4bbd0..c9d5375d9b3d7 100644 --- a/app/code/Magento/Customer/Test/Mftf/ActionGroup/OpenEditCustomerFromAdminActionGroup.xml +++ b/app/code/Magento/Customer/Test/Mftf/ActionGroup/OpenEditCustomerFromAdminActionGroup.xml @@ -26,57 +26,4 @@ <click selector="{{AdminCustomerGridSection.firstRowEditLink}}" stepKey="clickEdit"/> <waitForPageLoad stepKey="waitForPageLoad3"/> </actionGroup> - - <actionGroup name="OpenEditCustomerAddressFromAdminActionGroup"> - <annotations> - <description>Filters the Admin Customers Addresses based on the provided Address. Clicks on Edit.</description> - </annotations> - <arguments> - <argument name="address"/> - </arguments> - - <click selector="{{AdminCustomerAccountInformationSection.addressesButton}}" stepKey="openAddressesTab"/> - <waitForElementVisible selector="{{AdminCustomerAddressFiltersSection.filtersButton}}" stepKey="waitForComponentLoad"/> - <click selector="{{AdminCustomerAddressFiltersSection.filtersButton}}" stepKey="openAddressesFilter"/> - <fillField userInput="{{address.firstname}}" selector="{{AdminCustomerAddressFiltersSection.firstnameInput}}" stepKey="fillFirstname"/> - <fillField userInput="{{address.lastname}}" selector="{{AdminCustomerAddressFiltersSection.lastnameInput}}" stepKey="fillLastname"/> - <fillField userInput="{{address.telephone}}" selector="{{AdminCustomerAddressFiltersSection.telephoneInput}}" stepKey="fillCountry"/> - <fillField userInput="{{address.postcode}}" selector="{{AdminCustomerAddressFiltersSection.postcodeInput}}" stepKey="fillPostcode"/> - <click selector="{{AdminCustomerAddressFiltersSection.applyFilter}}" stepKey="applyAddressesFilter"/> - <waitForLoadingMaskToDisappear stepKey="waitForLoadingMaskToDisappear"/> - <click selector="{{AdminCustomerAddressGridSection.firstRowSelectActionLink}}" stepKey="clickAction"/> - <click selector="{{AdminCustomerAddressGridSection.firstRowEditActionLink}}" stepKey="clickEdit"/> - <waitForPageLoad stepKey="waitForModalWindow"/> - </actionGroup> - - <actionGroup name="DeleteCustomerFromAdminActionGroup"> - <annotations> - <description>Goes to the Admin Customers grid page. Deletes the provided Customer from the grid. Validates that the Success message is present and correct.</description> - </annotations> - <arguments> - <argument name="customer" defaultValue="CustomerEntityOne"/> - </arguments> - - <amOnPage url="{{AdminCustomerPage.url}}" stepKey="navigateToCustomers"/> - <conditionalClick selector="{{AdminDataGridHeaderSection.clearFilters}}" dependentSelector="{{AdminDataGridHeaderSection.clearFilters}}" visible="true" stepKey="clickOnButtonToRemoveFiltersIfPresent"/> - <fillField selector="{{AdminDataGridHeaderSection.search}}" userInput="{{customer.email}}" stepKey="fillSearch"/> - <click selector="{{AdminDataGridHeaderSection.submitSearch}}" stepKey="clickSubmit"/> - <waitForAjaxLoad stepKey="waitForLoadAjax"/> - <click selector="{{AdminCustomerGridMainActionsSection.multicheck}}" stepKey="selectAll"/> - <click selector="{{AdminCustomerGridMainActionsSection.actions}}" stepKey="clickActions"/> - <click selector="{{AdminCustomerGridMainActionsSection.delete}}" stepKey="clickDelete"/> - <waitForAjaxLoad stepKey="waitForLoadConfirmation"/> - <click selector="{{AdminConfirmationModalSection.ok}}" stepKey="confirmDelete"/> - <see selector="{{AdminMessagesSection.success}}" userInput="A total of 1 record(s) were deleted" stepKey="seeSuccess"/> - </actionGroup> - - <actionGroup name="AdminClearCustomersFiltersActionGroup"> - <annotations> - <description>Goes to the Admin Customers grid page. Clicks on 'Clear Filters'.</description> - </annotations> - - <amOnPage url="{{AdminCustomerPage.url}}" stepKey="amOnCustomersPage"/> - <waitForPageLoad stepKey="WaitForPageToLoad"/> - <conditionalClick selector="{{AdminDataGridHeaderSection.clearFilters}}" dependentSelector="{{AdminDataGridHeaderSection.clearFilters}}" visible="true" stepKey="clickOnButtonToRemoveFiltersIfPresent"/> - </actionGroup> </actionGroups> diff --git a/app/code/Magento/Customer/Test/Mftf/ActionGroup/SaveRegistrationFormActionGroup.xml b/app/code/Magento/Customer/Test/Mftf/ActionGroup/SaveRegistrationFormActionGroup.xml new file mode 100644 index 0000000000000..babef0fdd60d9 --- /dev/null +++ b/app/code/Magento/Customer/Test/Mftf/ActionGroup/SaveRegistrationFormActionGroup.xml @@ -0,0 +1,15 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + /** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +--> + +<actionGroups xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/actionGroupSchema.xsd"> + <actionGroup name="SaveRegistrationFormActionGroup"> + <click stepKey="clickCreateAccountButton" selector="{{StorefrontCustomerCreateFormSection.createAccountButton}}"/> + <waitForPageLoad stepKey="waitForCreateAccountButtonToLoad" /> + </actionGroup> +</actionGroups> diff --git a/app/code/Magento/Customer/Test/Mftf/ActionGroup/SelectDropdownCustomerAddressAttributeValueActionGroup.xml b/app/code/Magento/Customer/Test/Mftf/ActionGroup/SelectDropdownCustomerAddressAttributeValueActionGroup.xml new file mode 100644 index 0000000000000..5d0fb2e7b5c8d --- /dev/null +++ b/app/code/Magento/Customer/Test/Mftf/ActionGroup/SelectDropdownCustomerAddressAttributeValueActionGroup.xml @@ -0,0 +1,24 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + /** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +--> + +<actionGroups xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/actionGroupSchema.xsd"> + <actionGroup name="SelectDropdownCustomerAddressAttributeValueActionGroup"> + <annotations> + <description>Selects the provided Option in the provided Customer Address Attribute drop down menu. Clicks on Save.</description> + </annotations> + <arguments> + <argument name="customerAddressAttribute"/> + <argument name="optionValue" type="string"/> + </arguments> + + <selectOption selector="{{AdminEditCustomerAddressesSection.dropDownAttribute(customerAddressAttribute.code)}}" userInput="{{optionValue}}" stepKey="selectOptionValue"/> + <click selector="{{AdminEditCustomerAddressesSection.save}}" stepKey="saveAddress"/> + <waitForPageLoad stepKey="waitForAddressSaved"/> + </actionGroup> +</actionGroups> diff --git a/app/code/Magento/Customer/Test/Mftf/ActionGroup/SignUpNewCustomerStorefrontActionGroup.xml b/app/code/Magento/Customer/Test/Mftf/ActionGroup/SignUpNewCustomerStorefrontActionGroup.xml new file mode 100644 index 0000000000000..f994bafda73e0 --- /dev/null +++ b/app/code/Magento/Customer/Test/Mftf/ActionGroup/SignUpNewCustomerStorefrontActionGroup.xml @@ -0,0 +1,19 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + /** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +--> + +<actionGroups xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/actionGroupSchema.xsd"> + <actionGroup name="SignUpNewCustomerStorefrontActionGroup" extends="SignUpNewUserFromStorefrontActionGroup"> + <annotations> + <description>EXTENDS: SignUpNewUserFromStorefrontActionGroup. Adds a waitForPageLoad action to the Action Group. Removes the action for 'seeThankYouMessage'.</description> + </annotations> + + <waitForPageLoad stepKey="waitForRegistered" after="clickCreateAccountButton"/> + <remove keyForRemoval="seeThankYouMessage"/> + </actionGroup> +</actionGroups> diff --git a/app/code/Magento/Customer/Test/Mftf/ActionGroup/SignUpNewUserFromStorefrontActionGroup.xml b/app/code/Magento/Customer/Test/Mftf/ActionGroup/SignUpNewUserFromStorefrontActionGroup.xml index 44988b202ab57..56afa8854ce0d 100644 --- a/app/code/Magento/Customer/Test/Mftf/ActionGroup/SignUpNewUserFromStorefrontActionGroup.xml +++ b/app/code/Magento/Customer/Test/Mftf/ActionGroup/SignUpNewUserFromStorefrontActionGroup.xml @@ -17,6 +17,7 @@ </arguments> <amOnPage url="{{StorefrontHomePage.url}}" stepKey="amOnStorefrontPage"/> + <waitForElementVisible selector="{{StorefrontPanelHeaderSection.createAnAccountLink}}" stepKey="waitForCreateAccountLink"/> <click stepKey="clickOnCreateAccountLink" selector="{{StorefrontPanelHeaderSection.createAnAccountLink}}"/> <fillField stepKey="fillFirstName" userInput="{{Customer.firstname}}" selector="{{StorefrontCustomerCreateFormSection.firstnameField}}"/> <fillField stepKey="fillLastName" userInput="{{Customer.lastname}}" selector="{{StorefrontCustomerCreateFormSection.lastnameField}}"/> @@ -29,193 +30,4 @@ <see stepKey="seeLastName" userInput="{{Customer.lastname}}" selector="{{StorefrontCustomerDashboardAccountInformationSection.ContactInformation}}"/> <see stepKey="seeEmail" userInput="{{Customer.email}}" selector="{{StorefrontCustomerDashboardAccountInformationSection.ContactInformation}}"/> </actionGroup> - - <actionGroup name="StorefrontCreateCustomerSignedUpNewsletterActionGroup"> - <annotations> - <description>Goes to the Storefront. Clicks on 'Create Account'. Fills in the provided Customer details, including Newsletter Sign-Up. Clicks on 'Create Account' button.</description> - </annotations> - <arguments> - <argument name="customer" defaultValue="CustomerEntityOne"/> - </arguments> - - <amOnPage url="{{StorefrontHomePage.url}}" stepKey="amOnStorefrontPage"/> - <waitForPageLoad stepKey="waitForNavigateToCustomersPageLoad"/> - <click stepKey="clickOnCreateAccountLink" selector="{{StorefrontPanelHeaderSection.createAnAccountLink}}"/> - <fillField stepKey="fillFirstName" userInput="{{customer.firstname}}" selector="{{StorefrontCustomerCreateFormSection.firstnameField}}"/> - <fillField stepKey="fillLastName" userInput="{{customer.lastname}}" selector="{{StorefrontCustomerCreateFormSection.lastnameField}}"/> - <checkOption selector="{{StorefrontCustomerCreateFormSection.signUpForNewsletter}}" stepKey="checkSignUpForNewsletter"/> - <fillField stepKey="fillEmail" userInput="{{customer.email}}" selector="{{StorefrontCustomerCreateFormSection.emailField}}"/> - <fillField stepKey="fillPassword" userInput="{{customer.password}}" selector="{{StorefrontCustomerCreateFormSection.passwordField}}"/> - <fillField stepKey="fillConfirmPassword" userInput="{{customer.password}}" selector="{{StorefrontCustomerCreateFormSection.confirmPasswordField}}"/> - <click stepKey="clickCreateAccountButton" selector="{{StorefrontCustomerCreateFormSection.createAccountButton}}"/> - <waitForPageLoad stepKey="waitForCreateAccountButtonToLoad"/> - </actionGroup> - - <actionGroup name="StorefrontFillRegistrationFormActionGroup" extends="StorefrontCreateCustomerSignedUpNewsletterActionGroup"> - <remove keyForRemoval="checkSignUpForNewsletter"/> - <remove keyForRemoval="clickCreateAccountButton"/> - <remove keyForRemoval="waitForCreateAccountButtonToLoad"/> - </actionGroup> - - <actionGroup name="SaveRegistrationFormActionGroup"> - <click stepKey="clickCreateAccountButton" selector="{{StorefrontCustomerCreateFormSection.createAccountButton}}"/> - <waitForPageLoad stepKey="waitForCreateAccountButtonToLoad" /> - </actionGroup> - - <actionGroup name="AssertSignedUpNewsletterActionGroup"> - <annotations> - <description>Validates that the provided Customer details are present and correct on the Storefront Customer Dashboard page.</description> - </annotations> - <arguments> - <argument name="customer" defaultValue="CustomerEntityOne"/> - <argument name="storeName" defaultValue="Main Website" type="string"/> - </arguments> - - <see stepKey="successMessage" userInput="Thank you for registering with {{storeName}} Store." selector="{{AdminCustomerMessagesSection.successMessage}}"/> - <see stepKey="seeFirstName" userInput="{{customer.firstname}}" selector="{{StorefrontCustomerDashboardAccountInformationSection.ContactInformation}}"/> - <see stepKey="seeLastName" userInput="{{customer.lastname}}" selector="{{StorefrontCustomerDashboardAccountInformationSection.ContactInformation}}"/> - <see stepKey="seeEmail" userInput="{{customer.email}}" selector="{{StorefrontCustomerDashboardAccountInformationSection.ContactInformation}}"/> - <seeInCurrentUrl url="{{StorefrontCustomerDashboardPage.url}}" stepKey="seeAssertInCurrentUrl"/> - </actionGroup> - - <actionGroup name="EnterCustomerAddressInfo"> - <annotations> - <description>Fills in the provided Customer details (First/Last Name, Company, Phone # and Address) on the Admin Customer creation/edit page. Clicks on the Save button.</description> - </annotations> - <arguments> - <argument name="Address"/> - </arguments> - - <amOnPage url="customer/address/new/" stepKey="goToAddressPage"/> - <waitForPageLoad stepKey="waitForAddressPage"/> - <fillField stepKey="fillFirstName" selector="{{StorefrontCustomerAddressSection.firstName}}" userInput="{{Address.firstname}}"/> - <fillField stepKey="fillLastName" selector="{{StorefrontCustomerAddressSection.lastName}}" userInput="{{Address.lastname}}"/> - <fillField stepKey="fillCompany" selector="{{StorefrontCustomerAddressSection.company}}" userInput="{{Address.company}}"/> - <fillField stepKey="fillPhoneNumber" selector="{{StorefrontCustomerAddressSection.phoneNumber}}" userInput="{{Address.telephone}}"/> - <fillField stepKey="fillStreetAddress1" selector="{{StorefrontCustomerAddressSection.streetAddress1}}" userInput="{{Address.street[0]}}"/> - <fillField stepKey="fillStreetAddress2" selector="{{StorefrontCustomerAddressSection.streetAddress2}}" userInput="{{Address.street[1]}}"/> - <fillField stepKey="fillCityName" selector="{{StorefrontCustomerAddressSection.city}}" userInput="{{Address.city}}"/> - <selectOption stepKey="selectCounty" selector="{{StorefrontCustomerAddressSection.country}}" userInput="{{Address.country_id}}"/> - <selectOption stepKey="selectState" selector="{{StorefrontCustomerAddressSection.stateProvince}}" userInput="{{Address.state}}"/> - <fillField stepKey="fillZip" selector="{{StorefrontCustomerAddressSection.zip}}" userInput="{{Address.postcode}}"/> - <click stepKey="saveAddress" selector="{{StorefrontCustomerAddressSection.saveAddress}}"/> - </actionGroup> - - <!-- Fills State Field instead of selecting it--> - <actionGroup name="EnterCustomerAddressInfoFillState" extends="EnterCustomerAddressInfo"> - <annotations> - <description>EXTENDS: EnterCustomerAddressInfo. Fills the State field.</description> - </annotations> - - <fillField stepKey="selectState" selector="{{StorefrontCustomerAddressSection.stateProvinceFill}}" userInput="{{Address.state}}"/> - </actionGroup> - - <actionGroup name="VerifyCustomerBillingAddress"> - <annotations> - <description>Goes to the Storefront Customer Dashboard Address area. Validates that the provided Customer Billing Address is present and correct on the Storefront Customer Dashboard Address section.</description> - </annotations> - <arguments> - <argument name="address"/> - </arguments> - - <amOnPage url="customer/address/index/" stepKey="goToAddressPage"/> - <waitForPageLoad stepKey="waitForAddressPageLoad"/> - - <!--Verify customer default billing address--> - <see selector="{{StorefrontCustomerAddressesSection.defaultBillingAddress}}" userInput="{{address.firstname}} {{address.lastname}}" stepKey="seeAssertCustomerDefaultBillingAddressFirstnameAndLastname"/> - <see selector="{{StorefrontCustomerAddressesSection.defaultBillingAddress}}" userInput="{{address.company}}" stepKey="seeAssertCustomerDefaultBillingAddressCompany"/> - <see selector="{{StorefrontCustomerAddressesSection.defaultBillingAddress}}" userInput="{{address.street[0]}}" stepKey="seeAssertCustomerDefaultBillingAddressStreet"/> - <see selector="{{StorefrontCustomerAddressesSection.defaultBillingAddress}}" userInput="{{address.street[1]}}" stepKey="seeAssertCustomerDefaultBillingAddressStreet1"/> - <see selector="{{StorefrontCustomerAddressesSection.defaultBillingAddress}}" userInput="{{address.city}}, {{address.postcode}}" stepKey="seeAssertCustomerDefaultBillingAddressCityAndPostcode"/> - <see selector="{{StorefrontCustomerAddressesSection.defaultBillingAddress}}" userInput="{{address.country}}" stepKey="seeAssertCustomerDefaultBillingAddressCountry"/> - <see selector="{{StorefrontCustomerAddressesSection.defaultBillingAddress}}" userInput="{{address.telephone}}" stepKey="seeAssertCustomerDefaultBillingAddressTelephone"/> - </actionGroup> - - <actionGroup name="VerifyCustomerShippingAddress"> - <annotations> - <description>Goes to the Storefront Customer Dashboard Address area. Validates that the provided Customer Shipping Address is present and correct.</description> - </annotations> - <arguments> - <argument name="address"/> - </arguments> - - <amOnPage url="customer/address/index/" stepKey="goToAddressPage"/> - <waitForPageLoad stepKey="waitForAddressPageLoad"/> - <!--Verify customer default shipping address--> - <see selector="{{StorefrontCustomerAddressesSection.defaultShippingAddress}}" userInput="{{address.firstname}} {{address.lastname}}" stepKey="seeAssertCustomerDefaultShippingAddressFirstnameAndLastname"/> - <see selector="{{StorefrontCustomerAddressesSection.defaultShippingAddress}}" userInput="{{address.company}}" stepKey="seeAssertCustomerDefaultShippingAddressCompany"/> - <see selector="{{StorefrontCustomerAddressesSection.defaultShippingAddress}}" userInput="{{address.street[0]}}" stepKey="seeAssertCustomerDefaultShippingAddressStreet"/> - <see selector="{{StorefrontCustomerAddressesSection.defaultShippingAddress}}" userInput="{{address.street[1]}}" stepKey="seeAssertCustomerDefaultShippingAddressStreet1"/> - <see selector="{{StorefrontCustomerAddressesSection.defaultShippingAddress}}" userInput="{{address.city}}, {{address.postcode}}" stepKey="seeAssertCustomerDefaultShippingAddressCityAndPostcode"/> - <see selector="{{StorefrontCustomerAddressesSection.defaultShippingAddress}}" userInput="{{address.country}}" stepKey="seeAssertCustomerDefaultShippingAddressCountry"/> - <see selector="{{StorefrontCustomerAddressesSection.defaultShippingAddress}}" userInput="{{address.telephone}}" stepKey="seeAssertCustomerDefaultShippingAddressTelephone"/> - </actionGroup> - - <actionGroup name="VerifyCustomerBillingAddressWithState"> - <annotations> - <description>Goes to the Storefront Customer Dashboard Address area. Validates that the provided Customer Billing Address, including the State, is present and correct.</description> - </annotations> - <arguments> - <argument name="address"/> - </arguments> - - <amOnPage url="customer/address/index/" stepKey="goToAddressPage"/> - <waitForPageLoad stepKey="waitForAddressPageLoad"/> - - <!--Verify customer default billing address--> - <see selector="{{StorefrontCustomerAddressesSection.defaultBillingAddress}}" userInput="{{address.firstname}} {{address.lastname}}" stepKey="seeAssertCustomerDefaultBillingAddressFirstnameAndLastname"/> - <see selector="{{StorefrontCustomerAddressesSection.defaultBillingAddress}}" userInput="{{address.company}}" stepKey="seeAssertCustomerDefaultBillingAddressCompany"/> - <see selector="{{StorefrontCustomerAddressesSection.defaultBillingAddress}}" userInput="{{address.street[0]}}" stepKey="seeAssertCustomerDefaultBillingAddressStreet"/> - <see selector="{{StorefrontCustomerAddressesSection.defaultBillingAddress}}" userInput="{{address.street[1]}}" stepKey="seeAssertCustomerDefaultBillingAddressStreet1"/> - <see selector="{{StorefrontCustomerAddressesSection.defaultBillingAddress}}" userInput="{{address.city}}, {{address.state}}, {{address.postcode}}" stepKey="seeAssertCustomerDefaultBillingAddressCityAndPostcode"/> - <see selector="{{StorefrontCustomerAddressesSection.defaultBillingAddress}}" userInput="{{address.country}}" stepKey="seeAssertCustomerDefaultBillingAddressCountry"/> - <see selector="{{StorefrontCustomerAddressesSection.defaultBillingAddress}}" userInput="{{address.telephone}}" stepKey="seeAssertCustomerDefaultBillingAddressTelephone"/> - </actionGroup> - - <actionGroup name="VerifyCustomerShippingAddressWithState"> - <annotations> - <description>Goes to the Storefront Customer Dashboard Address area. Validates that the provided Customer Shipping Address, including the State, is present and correct.</description> - </annotations> - <arguments> - <argument name="address"/> - </arguments> - - <amOnPage url="customer/address/index/" stepKey="goToAddressPage"/> - <waitForPageLoad stepKey="waitForAddressPageLoad"/> - - <!--Verify customer default shipping address--> - <see selector="{{StorefrontCustomerAddressesSection.defaultShippingAddress}}" userInput="{{address.firstname}} {{address.lastname}}" stepKey="seeAssertCustomerDefaultShippingAddressFirstnameAndLastname"/> - <see selector="{{StorefrontCustomerAddressesSection.defaultShippingAddress}}" userInput="{{address.company}}" stepKey="seeAssertCustomerDefaultShippingAddressCompany"/> - <see selector="{{StorefrontCustomerAddressesSection.defaultShippingAddress}}" userInput="{{address.street[0]}}" stepKey="seeAssertCustomerDefaultShippingAddressStreet"/> - <see selector="{{StorefrontCustomerAddressesSection.defaultShippingAddress}}" userInput="{{address.street[1]}}" stepKey="seeAssertCustomerDefaultShippingAddressStreet1"/> - <see selector="{{StorefrontCustomerAddressesSection.defaultShippingAddress}}" userInput="{{address.city}}, {{address.state}}, {{address.postcode}}" stepKey="seeAssertCustomerDefaultShippingAddressCityAndPostcode"/> - <see selector="{{StorefrontCustomerAddressesSection.defaultShippingAddress}}" userInput="{{address.country}}" stepKey="seeAssertCustomerDefaultShippingAddressCountry"/> - <see selector="{{StorefrontCustomerAddressesSection.defaultShippingAddress}}" userInput="{{address.telephone}}" stepKey="seeAssertCustomerDefaultShippingAddressTelephone"/> - </actionGroup> - - <actionGroup name="VerifyCustomerNameOnFrontend"> - <annotations> - <description>Goes to the Storefront Customer Dashboard page. Validates that the Customer First/Last Name is present and correct.</description> - </annotations> - <arguments> - <argument name="customer"/> - </arguments> - - <!--Verify customer name on frontend--> - <amOnPage url="customer/account/edit/" stepKey="goToAddressPage"/> - <waitForPageLoad stepKey="waitForAddressPageLoad"/> - <click selector="{{StorefrontCustomerSidebarSection.sidebarCurrentTab('Account Information')}}" stepKey="clickAccountInformationFromSidebarCurrentTab"/> - <waitForPageLoad stepKey="waitForAccountInformationTabToOpen"/> - <seeInField selector="{{StorefrontCustomerAccountInformationSection.firstName}}" userInput="{{customer.firstname}}" stepKey="seeAssertCustomerFirstName"/> - <seeInField selector="{{StorefrontCustomerAccountInformationSection.lastName}}" userInput="{{customer.lastname}}" stepKey="seeAssertCustomerLastName"/> - </actionGroup> - - <actionGroup name="SignUpNewCustomerStorefrontActionGroup" extends="SignUpNewUserFromStorefrontActionGroup"> - <annotations> - <description>EXTENDS: SignUpNewUserFromStorefrontActionGroup. Adds a waitForPageLoad action to the Action Group. Removes the action for 'seeThankYouMessage'.</description> - </annotations> - - <waitForPageLoad stepKey="waitForRegistered" after="clickCreateAccountButton"/> - <remove keyForRemoval="seeThankYouMessage"/> - </actionGroup> </actionGroups> diff --git a/app/code/Magento/Customer/Test/Mftf/ActionGroup/StorefrontAddCustomerAddressActionGroup.xml b/app/code/Magento/Customer/Test/Mftf/ActionGroup/StorefrontAddCustomerAddressActionGroup.xml deleted file mode 100644 index dc21ce5f52d73..0000000000000 --- a/app/code/Magento/Customer/Test/Mftf/ActionGroup/StorefrontAddCustomerAddressActionGroup.xml +++ /dev/null @@ -1,57 +0,0 @@ -<?xml version="1.0" encoding="UTF-8"?> -<!-- - /** - * Copyright © Magento, Inc. All rights reserved. - * See COPYING.txt for license details. - */ ---> - -<actionGroups xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" - xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/actionGroupSchema.xsd"> - <actionGroup name="StorefrontAddNewCustomerAddressActionGroup"> - <annotations> - <description>Goes to the Storefront Customer Add New Address page. Fills in the provided Address details. Clicks on Save.</description> - </annotations> - <arguments> - <argument name="Address"/> - </arguments> - - <amOnPage url="customer/address/new/" stepKey="OpenCustomerAddNewAddress"/> - <fillField stepKey="fillFirstName" userInput="{{Address.firstname}}" selector="{{StorefrontCustomerAddressFormSection.firstName}}"/> - <fillField stepKey="fillLastName" userInput="{{Address.lastname}}" selector="{{StorefrontCustomerAddressFormSection.lastName}}"/> - <fillField stepKey="fillCompanyName" userInput="{{Address.company}}" selector="{{StorefrontCustomerAddressFormSection.company}}"/> - <fillField stepKey="fillPhoneNumber" userInput="{{Address.telephone}}" selector="{{StorefrontCustomerAddressFormSection.phoneNumber}}"/> - <fillField stepKey="fillStreetAddress" userInput="{{Address.street[0]}}" selector="{{StorefrontCustomerAddressFormSection.streetAddress}}"/> - <fillField stepKey="fillCity" userInput="{{Address.city}}" selector="{{StorefrontCustomerAddressFormSection.city}}"/> - <selectOption stepKey="selectState" userInput="{{Address.state}}" selector="{{StorefrontCustomerAddressFormSection.state}}"/> - <fillField stepKey="fillZip" userInput="{{Address.postcode}}" selector="{{StorefrontCustomerAddressFormSection.zip}}"/> - <selectOption stepKey="selectCountry" userInput="{{Address.country}}" selector="{{StorefrontCustomerAddressFormSection.country}}"/> - <click stepKey="saveCustomerAddress" selector="{{StorefrontCustomerAddressFormSection.saveAddress}}"/> - <see userInput="You saved the address." stepKey="verifyAddressAdded"/> - </actionGroup> - - <actionGroup name="StorefrontAddCustomerDefaultAddressActionGroup"> - <annotations> - <description>Goes to the Storefront Customer Add New Default Address page. Fills in the provided Address details. Clicks on Save.</description> - </annotations> - <arguments> - <argument name="Address"/> - </arguments> - - <amOnPage url="customer/address/new/" stepKey="OpenCustomerAddNewAddress"/> - <fillField stepKey="fillFirstName" userInput="{{Address.firstname}}" selector="{{StorefrontCustomerAddressFormSection.firstName}}"/> - <fillField stepKey="fillLastName" userInput="{{Address.lastname}}" selector="{{StorefrontCustomerAddressFormSection.lastName}}"/> - <fillField stepKey="fillCompanyName" userInput="{{Address.company}}" selector="{{StorefrontCustomerAddressFormSection.company}}"/> - <fillField stepKey="fillPhoneNumber" userInput="{{Address.telephone}}" selector="{{StorefrontCustomerAddressFormSection.phoneNumber}}"/> - <fillField stepKey="fillStreetAddress" userInput="{{Address.street[0]}}" selector="{{StorefrontCustomerAddressFormSection.streetAddress}}"/> - <fillField stepKey="fillCity" userInput="{{Address.city}}" selector="{{StorefrontCustomerAddressFormSection.city}}"/> - <selectOption stepKey="selectState" userInput="{{Address.state}}" selector="{{StorefrontCustomerAddressFormSection.state}}"/> - <fillField stepKey="fillZip" userInput="{{Address.postcode}}" selector="{{StorefrontCustomerAddressFormSection.zip}}"/> - <selectOption stepKey="selectCountry" userInput="{{Address.country}}" selector="{{StorefrontCustomerAddressFormSection.country}}"/> - <click stepKey="checkUseAsDefaultBillingAddressCheckBox" selector="{{StorefrontCustomerAddressFormSection.useAsDefaultBillingAddressCheckBox}}"/> - <click stepKey="checkUseAsDefaultShippingAddressCheckBox" selector="{{StorefrontCustomerAddressFormSection.useAsDefaultShippingAddressCheckBox}}"/> - <click stepKey="saveCustomerAddress" selector="{{StorefrontCustomerAddressFormSection.saveAddress}}"/> - <waitForPageLoad stepKey="waitForPageLoad"/> - <see userInput="You saved the address." stepKey="verifyAddressAdded"/> - </actionGroup> -</actionGroups> diff --git a/app/code/Magento/Customer/Test/Mftf/ActionGroup/StorefrontAddCustomerDefaultAddressActionGroup.xml b/app/code/Magento/Customer/Test/Mftf/ActionGroup/StorefrontAddCustomerDefaultAddressActionGroup.xml new file mode 100644 index 0000000000000..ce26d14bb95f7 --- /dev/null +++ b/app/code/Magento/Customer/Test/Mftf/ActionGroup/StorefrontAddCustomerDefaultAddressActionGroup.xml @@ -0,0 +1,35 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + /** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +--> + +<actionGroups xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/actionGroupSchema.xsd"> + <actionGroup name="StorefrontAddCustomerDefaultAddressActionGroup"> + <annotations> + <description>Goes to the Storefront Customer Add New Default Address page. Fills in the provided Address details. Clicks on Save.</description> + </annotations> + <arguments> + <argument name="Address"/> + </arguments> + + <amOnPage url="customer/address/new/" stepKey="OpenCustomerAddNewAddress"/> + <fillField stepKey="fillFirstName" userInput="{{Address.firstname}}" selector="{{StorefrontCustomerAddressFormSection.firstName}}"/> + <fillField stepKey="fillLastName" userInput="{{Address.lastname}}" selector="{{StorefrontCustomerAddressFormSection.lastName}}"/> + <fillField stepKey="fillCompanyName" userInput="{{Address.company}}" selector="{{StorefrontCustomerAddressFormSection.company}}"/> + <fillField stepKey="fillPhoneNumber" userInput="{{Address.telephone}}" selector="{{StorefrontCustomerAddressFormSection.phoneNumber}}"/> + <fillField stepKey="fillStreetAddress" userInput="{{Address.street[0]}}" selector="{{StorefrontCustomerAddressFormSection.streetAddress}}"/> + <fillField stepKey="fillCity" userInput="{{Address.city}}" selector="{{StorefrontCustomerAddressFormSection.city}}"/> + <selectOption stepKey="selectState" userInput="{{Address.state}}" selector="{{StorefrontCustomerAddressFormSection.state}}"/> + <fillField stepKey="fillZip" userInput="{{Address.postcode}}" selector="{{StorefrontCustomerAddressFormSection.zip}}"/> + <selectOption stepKey="selectCountry" userInput="{{Address.country}}" selector="{{StorefrontCustomerAddressFormSection.country}}"/> + <click stepKey="checkUseAsDefaultBillingAddressCheckBox" selector="{{StorefrontCustomerAddressFormSection.useAsDefaultBillingAddressCheckBox}}"/> + <click stepKey="checkUseAsDefaultShippingAddressCheckBox" selector="{{StorefrontCustomerAddressFormSection.useAsDefaultShippingAddressCheckBox}}"/> + <click stepKey="saveCustomerAddress" selector="{{StorefrontCustomerAddressFormSection.saveAddress}}"/> + <waitForPageLoad stepKey="waitForPageLoad"/> + <see userInput="You saved the address." stepKey="verifyAddressAdded"/> + </actionGroup> +</actionGroups> diff --git a/app/code/Magento/Customer/Test/Mftf/ActionGroup/StorefrontAddNewCustomerAddressActionGroup.xml b/app/code/Magento/Customer/Test/Mftf/ActionGroup/StorefrontAddNewCustomerAddressActionGroup.xml new file mode 100644 index 0000000000000..358930070bc1b --- /dev/null +++ b/app/code/Magento/Customer/Test/Mftf/ActionGroup/StorefrontAddNewCustomerAddressActionGroup.xml @@ -0,0 +1,32 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + /** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +--> + +<actionGroups xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/actionGroupSchema.xsd"> + <actionGroup name="StorefrontAddNewCustomerAddressActionGroup"> + <annotations> + <description>Goes to the Storefront Customer Add New Address page. Fills in the provided Address details. Clicks on Save.</description> + </annotations> + <arguments> + <argument name="Address"/> + </arguments> + + <amOnPage url="customer/address/new/" stepKey="OpenCustomerAddNewAddress"/> + <fillField stepKey="fillFirstName" userInput="{{Address.firstname}}" selector="{{StorefrontCustomerAddressFormSection.firstName}}"/> + <fillField stepKey="fillLastName" userInput="{{Address.lastname}}" selector="{{StorefrontCustomerAddressFormSection.lastName}}"/> + <fillField stepKey="fillCompanyName" userInput="{{Address.company}}" selector="{{StorefrontCustomerAddressFormSection.company}}"/> + <fillField stepKey="fillPhoneNumber" userInput="{{Address.telephone}}" selector="{{StorefrontCustomerAddressFormSection.phoneNumber}}"/> + <fillField stepKey="fillStreetAddress" userInput="{{Address.street[0]}}" selector="{{StorefrontCustomerAddressFormSection.streetAddress}}"/> + <fillField stepKey="fillCity" userInput="{{Address.city}}" selector="{{StorefrontCustomerAddressFormSection.city}}"/> + <selectOption stepKey="selectState" userInput="{{Address.state}}" selector="{{StorefrontCustomerAddressFormSection.state}}"/> + <fillField stepKey="fillZip" userInput="{{Address.postcode}}" selector="{{StorefrontCustomerAddressFormSection.zip}}"/> + <selectOption stepKey="selectCountry" userInput="{{Address.country}}" selector="{{StorefrontCustomerAddressFormSection.country}}"/> + <click stepKey="saveCustomerAddress" selector="{{StorefrontCustomerAddressFormSection.saveAddress}}"/> + <see userInput="You saved the address." stepKey="verifyAddressAdded"/> + </actionGroup> +</actionGroups> diff --git a/app/code/Magento/Customer/Test/Mftf/ActionGroup/StorefrontAssertCustomerAddressItemsActionGroup.xml b/app/code/Magento/Customer/Test/Mftf/ActionGroup/StorefrontAssertCustomerAddressItemsActionGroup.xml new file mode 100644 index 0000000000000..e8dceb8f65926 --- /dev/null +++ b/app/code/Magento/Customer/Test/Mftf/ActionGroup/StorefrontAssertCustomerAddressItemsActionGroup.xml @@ -0,0 +1,29 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + /** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +--> + +<actionGroups xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/actionGroupSchema.xsd"> + <actionGroup name="StorefrontAssertCustomerAddressItemsActionGroup"> + <annotations> + <description>Validate that the Storefront Customer Address contains correct items.</description> + </annotations> + <arguments> + <argument name="address" type="entity"/> + </arguments> + + <seeInField selector="{{StorefrontCustomerAddressFormSection.firstName}}" userInput="{{address.firstName}}" stepKey="seeFirstName"/> + <seeInField selector="{{StorefrontCustomerAddressFormSection.lastName}}" userInput="{{address.lastName}}" stepKey="seeLastName"/> + <seeInField selector="{{StorefrontCustomerAddressFormSection.company}}" userInput="{{address.company}}" stepKey="seeCompany"/> + <seeInField selector="{{StorefrontCustomerAddressFormSection.phoneNumber}}" userInput="{{address.telephone}}" stepKey="seePhoneNumber"/> + <seeInField selector="{{StorefrontCustomerAddressFormSection.streetAddress}}" userInput="{{address.street[0]}}" stepKey="seeStreet"/> + <seeInField selector="{{StorefrontCustomerAddressFormSection.city}}" userInput="{{address.city}}" stepKey="seeCity"/> + <seeInField selector="{{StorefrontCustomerAddressFormSection.state}}" userInput="{{address.state}}" stepKey="seeState"/> + <seeInField selector="{{StorefrontCustomerAddressFormSection.zip}}" userInput="{{address.postcode}}" stepKey="seePostcode"/> + <seeInField selector="{{StorefrontCustomerAddressFormSection.country}}" userInput="{{address.country}}" stepKey="seeCountry"/> + </actionGroup> +</actionGroups> diff --git a/app/code/Magento/Customer/Test/Mftf/ActionGroup/StorefrontCreateCustomerSignedUpNewsletterActionGroup.xml b/app/code/Magento/Customer/Test/Mftf/ActionGroup/StorefrontCreateCustomerSignedUpNewsletterActionGroup.xml new file mode 100644 index 0000000000000..22a90fca78cb2 --- /dev/null +++ b/app/code/Magento/Customer/Test/Mftf/ActionGroup/StorefrontCreateCustomerSignedUpNewsletterActionGroup.xml @@ -0,0 +1,31 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + /** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +--> + +<actionGroups xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/actionGroupSchema.xsd"> + <actionGroup name="StorefrontCreateCustomerSignedUpNewsletterActionGroup"> + <annotations> + <description>Goes to the Storefront. Clicks on 'Create Account'. Fills in the provided Customer details, including Newsletter Sign-Up. Clicks on 'Create Account' button.</description> + </annotations> + <arguments> + <argument name="customer" defaultValue="CustomerEntityOne"/> + </arguments> + + <amOnPage url="{{StorefrontHomePage.url}}" stepKey="amOnStorefrontPage"/> + <waitForPageLoad stepKey="waitForNavigateToCustomersPageLoad"/> + <click stepKey="clickOnCreateAccountLink" selector="{{StorefrontPanelHeaderSection.createAnAccountLink}}"/> + <fillField stepKey="fillFirstName" userInput="{{customer.firstname}}" selector="{{StorefrontCustomerCreateFormSection.firstnameField}}"/> + <fillField stepKey="fillLastName" userInput="{{customer.lastname}}" selector="{{StorefrontCustomerCreateFormSection.lastnameField}}"/> + <checkOption selector="{{StorefrontCustomerCreateFormSection.signUpForNewsletter}}" stepKey="checkSignUpForNewsletter"/> + <fillField stepKey="fillEmail" userInput="{{customer.email}}" selector="{{StorefrontCustomerCreateFormSection.emailField}}"/> + <fillField stepKey="fillPassword" userInput="{{customer.password}}" selector="{{StorefrontCustomerCreateFormSection.passwordField}}"/> + <fillField stepKey="fillConfirmPassword" userInput="{{customer.password}}" selector="{{StorefrontCustomerCreateFormSection.confirmPasswordField}}"/> + <click stepKey="clickCreateAccountButton" selector="{{StorefrontCustomerCreateFormSection.createAccountButton}}"/> + <waitForPageLoad stepKey="waitForCreateAccountButtonToLoad"/> + </actionGroup> +</actionGroups> diff --git a/app/code/Magento/Customer/Test/Mftf/ActionGroup/StorefrontCustomerChangePasswordActionGroup.xml b/app/code/Magento/Customer/Test/Mftf/ActionGroup/StorefrontCustomerChangePasswordActionGroup.xml new file mode 100644 index 0000000000000..802b75213825b --- /dev/null +++ b/app/code/Magento/Customer/Test/Mftf/ActionGroup/StorefrontCustomerChangePasswordActionGroup.xml @@ -0,0 +1,23 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + /** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +--> + +<actionGroups xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/actionGroupSchema.xsd"> + <actionGroup name="StorefrontCustomerChangePasswordActionGroup"> + <arguments> + <argument name="password" type="string" /> + </arguments> + + <checkOption selector="{{StorefrontCustomerAccountInformationSection.changePassword}}" stepKey="clickChangePasswordlCheckbox" /> + <fillField selector="{{StorefrontCustomerAccountInformationSection.currentPassword}}" userInput="{{password}}" stepKey="fillCurrentPassword" /> + <fillField selector="{{StorefrontCustomerAccountInformationSection.newPassword}}" userInput="{{password}}" stepKey="fillNewPassword" /> + <fillField selector="{{StorefrontCustomerAccountInformationSection.confirmNewPassword}}" userInput="{{password}}" stepKey="confirmNewPassword" /> + <click selector="{{StorefrontCustomerAccountInformationSection.saveButton}}" stepKey="saveChange" /> + <waitForPageLoad stepKey="waitForPageLoaded" /> + </actionGroup> +</actionGroups> diff --git a/app/code/Magento/Customer/Test/Mftf/ActionGroup/StorefrontCustomerCreateAccountWithDateOfBirthActionGroup.xml b/app/code/Magento/Customer/Test/Mftf/ActionGroup/StorefrontCustomerCreateAccountWithDateOfBirthActionGroup.xml new file mode 100644 index 0000000000000..22af2752ed1a5 --- /dev/null +++ b/app/code/Magento/Customer/Test/Mftf/ActionGroup/StorefrontCustomerCreateAccountWithDateOfBirthActionGroup.xml @@ -0,0 +1,20 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + /** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +--> + +<actionGroups xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/actionGroupSchema.xsd"> + <actionGroup name="StorefrontCustomerCreateAccountWithDateOfBirthActionGroup" extends="SignUpNewUserFromStorefrontActionGroup"> + <annotations> + <description>EXTENDS: SignUpNewUserFromStorefrontActionGroup. Fills birthday field.</description> + </annotations> + <arguments> + <argument name="dob" defaultValue="{{EN_US_DATE.short4DigitYear}}" type="string"/> + </arguments> + <fillField userInput="{{dob}}" selector="{{StorefrontCustomerCreateFormSection.dobField}}" after="fillLastName" stepKey="fillDob"/> + </actionGroup> +</actionGroups> diff --git a/app/code/Magento/Customer/Test/Mftf/ActionGroup/StorefrontCustomerLogoutActionGroup.xml b/app/code/Magento/Customer/Test/Mftf/ActionGroup/StorefrontCustomerLogoutActionGroup.xml index ed221350918a0..bbe5fcbbf4038 100644 --- a/app/code/Magento/Customer/Test/Mftf/ActionGroup/StorefrontCustomerLogoutActionGroup.xml +++ b/app/code/Magento/Customer/Test/Mftf/ActionGroup/StorefrontCustomerLogoutActionGroup.xml @@ -16,15 +16,4 @@ <amOnPage url="{{StorefrontCustomerLogoutPage.url}}" stepKey="storefrontSignOut"/> <waitForPageLoad stepKey="waitForSignOut"/> </actionGroup> - - <actionGroup name="StorefrontSignOutActionGroup"> - <annotations> - <description>Clicks on Customer Account. Clicks on 'Sign-Out'. Validates that the success message is present and correct. PLEASE NOTE: The Success Message is hardcoded.</description> - </annotations> - - <click selector="{{StoreFrontSignOutSection.customerAccount}}" stepKey="clickCustomerButton"/> - <click selector="{{StoreFrontSignOutSection.signOut}}" stepKey="clickToSignOut"/> - <waitForPageLoad stepKey="waitForPageLoad"/> - <see userInput="You are signed out" stepKey="signOut"/> - </actionGroup> </actionGroups> diff --git a/app/code/Magento/Customer/Test/Mftf/ActionGroup/StorefrontCustomerNavigateToNewsletterPageActionGroup.xml b/app/code/Magento/Customer/Test/Mftf/ActionGroup/StorefrontCustomerNavigateToNewsletterPageActionGroup.xml new file mode 100644 index 0000000000000..559dee27b551d --- /dev/null +++ b/app/code/Magento/Customer/Test/Mftf/ActionGroup/StorefrontCustomerNavigateToNewsletterPageActionGroup.xml @@ -0,0 +1,15 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + /** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +--> + +<actionGroups xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/actionGroupSchema.xsd"> + <actionGroup name="StorefrontCustomerNavigateToNewsletterPageActionGroup"> + <amOnPage url="{{StorefrontCustomerNewsletterManagePage.url}}" stepKey="goToNewsletterPage"/> + <waitForPageLoad stepKey="waitForPageLoad"/> + </actionGroup> +</actionGroups> diff --git a/app/code/Magento/Customer/Test/Mftf/ActionGroup/StorefrontCustomerUpdateGeneralSubscriptionActionGroup.xml b/app/code/Magento/Customer/Test/Mftf/ActionGroup/StorefrontCustomerUpdateGeneralSubscriptionActionGroup.xml new file mode 100644 index 0000000000000..16f8b5d17d7e1 --- /dev/null +++ b/app/code/Magento/Customer/Test/Mftf/ActionGroup/StorefrontCustomerUpdateGeneralSubscriptionActionGroup.xml @@ -0,0 +1,15 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + /** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +--> + +<actionGroups xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/actionGroupSchema.xsd"> + <actionGroup name="StorefrontCustomerUpdateGeneralSubscriptionActionGroup"> + <click selector="{{StorefrontCustomerNewsletterSection.newsletterCheckbox}}" stepKey="checkNewsLetterSubscriptionCheckbox"/> + <click selector="{{StorefrontCustomerNewsletterSection.submit}}" stepKey="clickSubmitButton"/> + </actionGroup> +</actionGroups> diff --git a/app/code/Magento/Customer/Test/Mftf/ActionGroup/StorefrontFakeBrokenSessionActionGroup.xml b/app/code/Magento/Customer/Test/Mftf/ActionGroup/StorefrontFakeBrokenSessionActionGroup.xml new file mode 100644 index 0000000000000..3d989a7833b8a --- /dev/null +++ b/app/code/Magento/Customer/Test/Mftf/ActionGroup/StorefrontFakeBrokenSessionActionGroup.xml @@ -0,0 +1,14 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. +--> + +<actionGroups xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/actionGroupSchema.xsd"> + <actionGroup name="StorefrontFakeBrokenSessionActionGroup"> + <resetCookie userInput="PHPSESSID" stepKey="resetCookieForCart"/> + <resetCookie userInput="form_key" stepKey="resetCookieForCart2"/> + </actionGroup> +</actionGroups> diff --git a/app/code/Magento/Customer/Test/Mftf/ActionGroup/StorefrontFillRegistrationFormActionGroup.xml b/app/code/Magento/Customer/Test/Mftf/ActionGroup/StorefrontFillRegistrationFormActionGroup.xml new file mode 100644 index 0000000000000..984d4f437aeb0 --- /dev/null +++ b/app/code/Magento/Customer/Test/Mftf/ActionGroup/StorefrontFillRegistrationFormActionGroup.xml @@ -0,0 +1,16 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + /** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +--> + +<actionGroups xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/actionGroupSchema.xsd"> + <actionGroup name="StorefrontFillRegistrationFormActionGroup" extends="StorefrontCreateCustomerSignedUpNewsletterActionGroup"> + <remove keyForRemoval="checkSignUpForNewsletter"/> + <remove keyForRemoval="clickCreateAccountButton"/> + <remove keyForRemoval="waitForCreateAccountButtonToLoad"/> + </actionGroup> +</actionGroups> diff --git a/app/code/Magento/Customer/Test/Mftf/ActionGroup/StorefrontNavigateToAccountInformationChangeActionGroup.xml b/app/code/Magento/Customer/Test/Mftf/ActionGroup/StorefrontNavigateToAccountInformationChangeActionGroup.xml new file mode 100644 index 0000000000000..68ab140ff0899 --- /dev/null +++ b/app/code/Magento/Customer/Test/Mftf/ActionGroup/StorefrontNavigateToAccountInformationChangeActionGroup.xml @@ -0,0 +1,17 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + /** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +--> + +<actionGroups xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/actionGroupSchema.xsd"> + <actionGroup name="StorefrontNavigateToAccountInformationChangeActionGroup"> + <amOnPage url="{{StorefrontCustomerEditPage.url}}" stepKey="goToCustomerEditPage"/> + <waitForPageLoad stepKey="waitForEditPage"/> + <conditionalClick selector="{{StorefrontCustomerSidebarSection.sidebarTab('Account Information')}}" dependentSelector="{{StorefrontCustomerSidebarSection.sidebarTab('Account Information')}}" visible="true" stepKey="openAccountInfoTab" /> + <waitForPageLoad stepKey="waitForAccountInfoTabOpened"/> + </actionGroup> +</actionGroups> diff --git a/app/code/Magento/Customer/Test/Mftf/ActionGroup/StorefrontOpenCustomerAccountCreatePageActionGroup.xml b/app/code/Magento/Customer/Test/Mftf/ActionGroup/StorefrontOpenCustomerAccountCreatePageActionGroup.xml index 31a988ac9da0d..b013b1db1c8e7 100644 --- a/app/code/Magento/Customer/Test/Mftf/ActionGroup/StorefrontOpenCustomerAccountCreatePageActionGroup.xml +++ b/app/code/Magento/Customer/Test/Mftf/ActionGroup/StorefrontOpenCustomerAccountCreatePageActionGroup.xml @@ -16,15 +16,4 @@ <amOnPage url="{{StorefrontCustomerCreatePage.url}}" stepKey="goToCustomerAccountCreatePage"/> <waitForPageLoad stepKey="waitForPageLoaded"/> </actionGroup> - - <actionGroup name="StorefrontOpenCustomerAccountCreatePageUsingStoreCodeInUrlActionGroup"> - <annotations> - <description>Goes to the Storefront Customer Create page using Store code in URL option.</description> - </annotations> - <arguments> - <argument name="storeView" type="string" defaultValue="{{customStore.code}}"/> - </arguments> - - <amOnPage url="{{StorefrontStoreHomePage.url(storeView)}}{{StorefrontCustomerCreatePage.url}}" stepKey="goToCustomerAccountCreatePage"/> - </actionGroup> </actionGroups> diff --git a/app/code/Magento/Customer/Test/Mftf/ActionGroup/StorefrontOpenCustomerAccountCreatePageUsingStoreCodeInUrlActionGroup.xml b/app/code/Magento/Customer/Test/Mftf/ActionGroup/StorefrontOpenCustomerAccountCreatePageUsingStoreCodeInUrlActionGroup.xml new file mode 100644 index 0000000000000..f095927c5be1b --- /dev/null +++ b/app/code/Magento/Customer/Test/Mftf/ActionGroup/StorefrontOpenCustomerAccountCreatePageUsingStoreCodeInUrlActionGroup.xml @@ -0,0 +1,21 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + /** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +--> + +<actionGroups xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/actionGroupSchema.xsd"> + <actionGroup name="StorefrontOpenCustomerAccountCreatePageUsingStoreCodeInUrlActionGroup"> + <annotations> + <description>Goes to the Storefront Customer Create page using Store code in URL option.</description> + </annotations> + <arguments> + <argument name="storeView" type="string" defaultValue="{{customStore.code}}"/> + </arguments> + + <amOnPage url="{{StorefrontStoreHomePage.url(storeView)}}{{StorefrontCustomerCreatePage.url}}" stepKey="goToCustomerAccountCreatePage"/> + </actionGroup> +</actionGroups> diff --git a/app/code/Magento/Customer/Test/Mftf/ActionGroup/StorefrontSignOutActionGroup.xml b/app/code/Magento/Customer/Test/Mftf/ActionGroup/StorefrontSignOutActionGroup.xml new file mode 100644 index 0000000000000..61c5f9ef7c66e --- /dev/null +++ b/app/code/Magento/Customer/Test/Mftf/ActionGroup/StorefrontSignOutActionGroup.xml @@ -0,0 +1,21 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + /** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +--> + +<actionGroups xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/actionGroupSchema.xsd"> + <actionGroup name="StorefrontSignOutActionGroup"> + <annotations> + <description>Clicks on Customer Account. Clicks on 'Sign-Out'. Validates that the success message is present and correct. PLEASE NOTE: The Success Message is hardcoded.</description> + </annotations> + + <click selector="{{StoreFrontSignOutSection.customerAccount}}" stepKey="clickCustomerButton"/> + <click selector="{{StoreFrontSignOutSection.signOut}}" stepKey="clickToSignOut"/> + <waitForPageLoad stepKey="waitForPageLoad"/> + <see userInput="You are signed out" stepKey="signOut"/> + </actionGroup> +</actionGroups> diff --git a/app/code/Magento/Customer/Test/Mftf/ActionGroup/VerifyCustomerBillingAddressActionGroup.xml b/app/code/Magento/Customer/Test/Mftf/ActionGroup/VerifyCustomerBillingAddressActionGroup.xml new file mode 100644 index 0000000000000..c439f7529993c --- /dev/null +++ b/app/code/Magento/Customer/Test/Mftf/ActionGroup/VerifyCustomerBillingAddressActionGroup.xml @@ -0,0 +1,31 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + /** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +--> + +<actionGroups xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/actionGroupSchema.xsd"> + <actionGroup name="VerifyCustomerBillingAddressActionGroup"> + <annotations> + <description>Goes to the Storefront Customer Dashboard Address area. Validates that the provided Customer Billing Address is present and correct on the Storefront Customer Dashboard Address section.</description> + </annotations> + <arguments> + <argument name="address"/> + </arguments> + + <amOnPage url="customer/address/index/" stepKey="goToAddressPage"/> + <waitForPageLoad stepKey="waitForAddressPageLoad"/> + + <!--Verify customer default billing address--> + <see selector="{{StorefrontCustomerAddressesSection.defaultBillingAddress}}" userInput="{{address.firstname}} {{address.lastname}}" stepKey="seeAssertCustomerDefaultBillingAddressFirstnameAndLastname"/> + <see selector="{{StorefrontCustomerAddressesSection.defaultBillingAddress}}" userInput="{{address.company}}" stepKey="seeAssertCustomerDefaultBillingAddressCompany"/> + <see selector="{{StorefrontCustomerAddressesSection.defaultBillingAddress}}" userInput="{{address.street[0]}}" stepKey="seeAssertCustomerDefaultBillingAddressStreet"/> + <see selector="{{StorefrontCustomerAddressesSection.defaultBillingAddress}}" userInput="{{address.street[1]}}" stepKey="seeAssertCustomerDefaultBillingAddressStreet1"/> + <see selector="{{StorefrontCustomerAddressesSection.defaultBillingAddress}}" userInput="{{address.city}}, {{address.postcode}}" stepKey="seeAssertCustomerDefaultBillingAddressCityAndPostcode"/> + <see selector="{{StorefrontCustomerAddressesSection.defaultBillingAddress}}" userInput="{{address.country}}" stepKey="seeAssertCustomerDefaultBillingAddressCountry"/> + <see selector="{{StorefrontCustomerAddressesSection.defaultBillingAddress}}" userInput="{{address.telephone}}" stepKey="seeAssertCustomerDefaultBillingAddressTelephone"/> + </actionGroup> +</actionGroups> diff --git a/app/code/Magento/Customer/Test/Mftf/ActionGroup/VerifyCustomerBillingAddressWithStateActionGroup.xml b/app/code/Magento/Customer/Test/Mftf/ActionGroup/VerifyCustomerBillingAddressWithStateActionGroup.xml new file mode 100644 index 0000000000000..7e46fcd7807db --- /dev/null +++ b/app/code/Magento/Customer/Test/Mftf/ActionGroup/VerifyCustomerBillingAddressWithStateActionGroup.xml @@ -0,0 +1,31 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + /** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +--> + +<actionGroups xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/actionGroupSchema.xsd"> + <actionGroup name="VerifyCustomerBillingAddressWithStateActionGroup"> + <annotations> + <description>Goes to the Storefront Customer Dashboard Address area. Validates that the provided Customer Billing Address, including the State, is present and correct.</description> + </annotations> + <arguments> + <argument name="address"/> + </arguments> + + <amOnPage url="customer/address/index/" stepKey="goToAddressPage"/> + <waitForPageLoad stepKey="waitForAddressPageLoad"/> + + <!--Verify customer default billing address--> + <see selector="{{StorefrontCustomerAddressesSection.defaultBillingAddress}}" userInput="{{address.firstname}} {{address.lastname}}" stepKey="seeAssertCustomerDefaultBillingAddressFirstnameAndLastname"/> + <see selector="{{StorefrontCustomerAddressesSection.defaultBillingAddress}}" userInput="{{address.company}}" stepKey="seeAssertCustomerDefaultBillingAddressCompany"/> + <see selector="{{StorefrontCustomerAddressesSection.defaultBillingAddress}}" userInput="{{address.street[0]}}" stepKey="seeAssertCustomerDefaultBillingAddressStreet"/> + <see selector="{{StorefrontCustomerAddressesSection.defaultBillingAddress}}" userInput="{{address.street[1]}}" stepKey="seeAssertCustomerDefaultBillingAddressStreet1"/> + <see selector="{{StorefrontCustomerAddressesSection.defaultBillingAddress}}" userInput="{{address.city}}, {{address.state}}, {{address.postcode}}" stepKey="seeAssertCustomerDefaultBillingAddressCityAndPostcode"/> + <see selector="{{StorefrontCustomerAddressesSection.defaultBillingAddress}}" userInput="{{address.country}}" stepKey="seeAssertCustomerDefaultBillingAddressCountry"/> + <see selector="{{StorefrontCustomerAddressesSection.defaultBillingAddress}}" userInput="{{address.telephone}}" stepKey="seeAssertCustomerDefaultBillingAddressTelephone"/> + </actionGroup> +</actionGroups> diff --git a/app/code/Magento/Customer/Test/Mftf/ActionGroup/VerifyCustomerNameOnFrontendActionGroup.xml b/app/code/Magento/Customer/Test/Mftf/ActionGroup/VerifyCustomerNameOnFrontendActionGroup.xml new file mode 100644 index 0000000000000..49ee62e5e86d2 --- /dev/null +++ b/app/code/Magento/Customer/Test/Mftf/ActionGroup/VerifyCustomerNameOnFrontendActionGroup.xml @@ -0,0 +1,27 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + /** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +--> + +<actionGroups xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/actionGroupSchema.xsd"> + <actionGroup name="VerifyCustomerNameOnFrontendActionGroup"> + <annotations> + <description>Goes to the Storefront Customer Dashboard page. Validates that the Customer First/Last Name is present and correct.</description> + </annotations> + <arguments> + <argument name="customer"/> + </arguments> + + <!--Verify customer name on frontend--> + <amOnPage url="customer/account/edit/" stepKey="goToAddressPage"/> + <waitForPageLoad stepKey="waitForAddressPageLoad"/> + <click selector="{{StorefrontCustomerSidebarSection.sidebarCurrentTab('Account Information')}}" stepKey="clickAccountInformationFromSidebarCurrentTab"/> + <waitForPageLoad stepKey="waitForAccountInformationTabToOpen"/> + <seeInField selector="{{StorefrontCustomerAccountInformationSection.firstName}}" userInput="{{customer.firstname}}" stepKey="seeAssertCustomerFirstName"/> + <seeInField selector="{{StorefrontCustomerAccountInformationSection.lastName}}" userInput="{{customer.lastname}}" stepKey="seeAssertCustomerLastName"/> + </actionGroup> +</actionGroups> diff --git a/app/code/Magento/Customer/Test/Mftf/ActionGroup/VerifyCustomerShippingAddressActionGroup.xml b/app/code/Magento/Customer/Test/Mftf/ActionGroup/VerifyCustomerShippingAddressActionGroup.xml new file mode 100644 index 0000000000000..e6d07ef1afc63 --- /dev/null +++ b/app/code/Magento/Customer/Test/Mftf/ActionGroup/VerifyCustomerShippingAddressActionGroup.xml @@ -0,0 +1,30 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + /** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +--> + +<actionGroups xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/actionGroupSchema.xsd"> + <actionGroup name="VerifyCustomerShippingAddressActionGroup"> + <annotations> + <description>Goes to the Storefront Customer Dashboard Address area. Validates that the provided Customer Shipping Address is present and correct.</description> + </annotations> + <arguments> + <argument name="address"/> + </arguments> + + <amOnPage url="customer/address/index/" stepKey="goToAddressPage"/> + <waitForPageLoad stepKey="waitForAddressPageLoad"/> + <!--Verify customer default shipping address--> + <see selector="{{StorefrontCustomerAddressesSection.defaultShippingAddress}}" userInput="{{address.firstname}} {{address.lastname}}" stepKey="seeAssertCustomerDefaultShippingAddressFirstnameAndLastname"/> + <see selector="{{StorefrontCustomerAddressesSection.defaultShippingAddress}}" userInput="{{address.company}}" stepKey="seeAssertCustomerDefaultShippingAddressCompany"/> + <see selector="{{StorefrontCustomerAddressesSection.defaultShippingAddress}}" userInput="{{address.street[0]}}" stepKey="seeAssertCustomerDefaultShippingAddressStreet"/> + <see selector="{{StorefrontCustomerAddressesSection.defaultShippingAddress}}" userInput="{{address.street[1]}}" stepKey="seeAssertCustomerDefaultShippingAddressStreet1"/> + <see selector="{{StorefrontCustomerAddressesSection.defaultShippingAddress}}" userInput="{{address.city}}, {{address.postcode}}" stepKey="seeAssertCustomerDefaultShippingAddressCityAndPostcode"/> + <see selector="{{StorefrontCustomerAddressesSection.defaultShippingAddress}}" userInput="{{address.country}}" stepKey="seeAssertCustomerDefaultShippingAddressCountry"/> + <see selector="{{StorefrontCustomerAddressesSection.defaultShippingAddress}}" userInput="{{address.telephone}}" stepKey="seeAssertCustomerDefaultShippingAddressTelephone"/> + </actionGroup> +</actionGroups> diff --git a/app/code/Magento/Customer/Test/Mftf/ActionGroup/VerifyCustomerShippingAddressWithStateActionGroup.xml b/app/code/Magento/Customer/Test/Mftf/ActionGroup/VerifyCustomerShippingAddressWithStateActionGroup.xml new file mode 100644 index 0000000000000..a90a05bead69c --- /dev/null +++ b/app/code/Magento/Customer/Test/Mftf/ActionGroup/VerifyCustomerShippingAddressWithStateActionGroup.xml @@ -0,0 +1,31 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + /** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +--> + +<actionGroups xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/actionGroupSchema.xsd"> + <actionGroup name="VerifyCustomerShippingAddressWithStateActionGroup"> + <annotations> + <description>Goes to the Storefront Customer Dashboard Address area. Validates that the provided Customer Shipping Address, including the State, is present and correct.</description> + </annotations> + <arguments> + <argument name="address"/> + </arguments> + + <amOnPage url="customer/address/index/" stepKey="goToAddressPage"/> + <waitForPageLoad stepKey="waitForAddressPageLoad"/> + + <!--Verify customer default shipping address--> + <see selector="{{StorefrontCustomerAddressesSection.defaultShippingAddress}}" userInput="{{address.firstname}} {{address.lastname}}" stepKey="seeAssertCustomerDefaultShippingAddressFirstnameAndLastname"/> + <see selector="{{StorefrontCustomerAddressesSection.defaultShippingAddress}}" userInput="{{address.company}}" stepKey="seeAssertCustomerDefaultShippingAddressCompany"/> + <see selector="{{StorefrontCustomerAddressesSection.defaultShippingAddress}}" userInput="{{address.street[0]}}" stepKey="seeAssertCustomerDefaultShippingAddressStreet"/> + <see selector="{{StorefrontCustomerAddressesSection.defaultShippingAddress}}" userInput="{{address.street[1]}}" stepKey="seeAssertCustomerDefaultShippingAddressStreet1"/> + <see selector="{{StorefrontCustomerAddressesSection.defaultShippingAddress}}" userInput="{{address.city}}, {{address.state}}, {{address.postcode}}" stepKey="seeAssertCustomerDefaultShippingAddressCityAndPostcode"/> + <see selector="{{StorefrontCustomerAddressesSection.defaultShippingAddress}}" userInput="{{address.country}}" stepKey="seeAssertCustomerDefaultShippingAddressCountry"/> + <see selector="{{StorefrontCustomerAddressesSection.defaultShippingAddress}}" userInput="{{address.telephone}}" stepKey="seeAssertCustomerDefaultShippingAddressTelephone"/> + </actionGroup> +</actionGroups> diff --git a/app/code/Magento/Customer/Test/Mftf/Data/AddressData.xml b/app/code/Magento/Customer/Test/Mftf/Data/AddressData.xml index 08e13885d10d4..e31be78185aaf 100644 --- a/app/code/Magento/Customer/Test/Mftf/Data/AddressData.xml +++ b/app/code/Magento/Customer/Test/Mftf/Data/AddressData.xml @@ -327,6 +327,7 @@ <data key="postcode">10789</data> <data key="telephone">333-33-333-33</data> <data key="country">Germany</data> + <data key="state">Berlin</data> </entity> <entity name="US_Address_California"> <data key="firstname">John</data> diff --git a/app/code/Magento/Customer/Test/Mftf/Data/AdminCustomerConfigData.xml b/app/code/Magento/Customer/Test/Mftf/Data/AdminCustomerConfigData.xml new file mode 100644 index 0000000000000..53963b1e95fbd --- /dev/null +++ b/app/code/Magento/Customer/Test/Mftf/Data/AdminCustomerConfigData.xml @@ -0,0 +1,16 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + /** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +--> + +<entities xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:noNamespaceSchemaLocation="urn:magento:mftf:DataGenerator/etc/dataProfileSchema.xsd"> + <entity name="ShowDateOfBirth"> + <data key="no">No</data> + <data key="optional">Optional</data> + <data key="required">Required</data> + </entity> +</entities> diff --git a/app/code/Magento/Customer/Test/Mftf/Data/CustomerData.xml b/app/code/Magento/Customer/Test/Mftf/Data/CustomerData.xml index b9227505871cf..e176c45a1fa00 100644 --- a/app/code/Magento/Customer/Test/Mftf/Data/CustomerData.xml +++ b/app/code/Magento/Customer/Test/Mftf/Data/CustomerData.xml @@ -9,7 +9,7 @@ <entities xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="urn:magento:mftf:DataGenerator/etc/dataProfileSchema.xsd"> <entity name="CustomerEntityOne" type="customer"> - <data key="group_id">0</data> + <data key="group_id">1</data> <data key="default_billing">defaultBillingValue</data> <data key="default_shipping">defaultShippingValue</data> <data key="confirmation">confirmationData</data> @@ -111,7 +111,7 @@ <data key="website_id">0</data> </entity> <entity name="Simple_US_Customer_Multiple_Addresses" type="customer"> - <data key="group_id">0</data> + <data key="group_id">1</data> <data key="default_billing">true</data> <data key="default_shipping">true</data> <data key="email" unique="prefix">John.Doe@example.com</data> @@ -125,7 +125,7 @@ <requiredEntity type="address">UK_Not_Default_Address</requiredEntity> </entity> <entity name="Simple_US_Customer_Multiple_Addresses_No_Default_Address" type="customer"> - <data key="group_id">0</data> + <data key="group_id">1</data> <data key="default_billing">true</data> <data key="default_shipping">true</data> <data key="email" unique="prefix">John.Doe@example.com</data> @@ -139,7 +139,7 @@ <requiredEntity type="address">UK_Not_Default_Address</requiredEntity> </entity> <entity name="Simple_US_Customer_With_Different_Billing_Shipping_Addresses" type="customer"> - <data key="group_id">0</data> + <data key="group_id">1</data> <data key="default_billing">true</data> <data key="default_shipping">true</data> <data key="email" unique="prefix">John.Doe@example.com</data> @@ -153,7 +153,7 @@ <requiredEntity type="address">US_Address_NY_Default_Shipping</requiredEntity> </entity> <entity name="Simple_US_Customer_NY" type="customer"> - <data key="group_id">0</data> + <data key="group_id">1</data> <data key="default_billing">true</data> <data key="default_shipping">true</data> <data key="email" unique="prefix">John.Doe@example.com</data> @@ -166,7 +166,7 @@ <requiredEntity type="address">US_Address_NY</requiredEntity> </entity> <entity name="Simple_US_Customer_CA" type="customer"> - <data key="group_id">0</data> + <data key="group_id">1</data> <data key="default_billing">true</data> <data key="default_shipping">true</data> <data key="email" unique="prefix">John.Doe@example.com</data> @@ -178,6 +178,18 @@ <data key="website_id">0</data> <requiredEntity type="address">US_Address_CA</requiredEntity> </entity> + <entity name="Simple_US_Customer_CA_Without_Email" type="customer"> + <data key="group_id">0</data> + <data key="default_billing">true</data> + <data key="default_shipping">true</data> + <data key="firstname">John</data> + <data key="lastname">Doe</data> + <data key="fullname">John Doe</data> + <data key="password">pwdTest123!</data> + <data key="store_id">0</data> + <data key="website_id">0</data> + <requiredEntity type="address">US_Address_CA</requiredEntity> + </entity> <entity name="Simple_US_Customer_For_Update" type="customer"> <var key="id" entityKey="id" entityType="customer"/> <data key="firstname">Jane</data> @@ -209,7 +221,7 @@ <requiredEntity type="address">US_Address_Utah</requiredEntity> </entity> <entity name="Simple_GB_Customer" type="customer"> - <data key="group_id">0</data> + <data key="group_id">1</data> <data key="default_billing">true</data> <data key="default_shipping">true</data> <data key="email" unique="prefix">Jane.Doe@example.com</data> @@ -245,7 +257,7 @@ <data key="website_id">0</data> </entity> <entity name="UKCustomer" type="customer"> - <data key="group_id">0</data> + <data key="group_id">1</data> <data key="default_billing">true</data> <data key="default_shipping">true</data> <data key="email" unique="prefix">david@email.com</data> @@ -259,7 +271,7 @@ <requiredEntity type="address">updateCustomerUKAddress</requiredEntity> </entity> <entity name="Customer_US_UK_DE" type="customer"> - <data key="group_id">0</data> + <data key="group_id">1</data> <data key="default_billing">true</data> <data key="default_shipping">true</data> <data key="email" unique="prefix">John.Doe@example.com</data> @@ -287,7 +299,7 @@ <requiredEntity type="address">US_Address_CA</requiredEntity> </entity> <entity name="Simple_US_Customer_Two_Addresses" type="customer"> - <data key="group_id">0</data> + <data key="group_id">1</data> <data key="default_billing">true</data> <data key="default_shipping">true</data> <data key="email" unique="prefix">John.Doe@example.com</data> @@ -319,7 +331,7 @@ <data key="password">123123qa</data> </entity> <entity name="Simple_US_Customer_Incorrect_Email" type="customer"> - <data key="group_id">0</data> + <data key="group_id">1</data> <data key="default_billing">true</data> <data key="default_shipping">true</data> <data key="email">><script>alert(1);</script>@example.com</data> @@ -342,7 +354,7 @@ <data key="website_id">0</data> </entity> <entity name="Simple_US_Customer_ArmedForcesEurope" type="customer"> - <data key="group_id">0</data> + <data key="group_id">1</data> <data key="default_billing">true</data> <data key="default_shipping">true</data> <data key="email" unique="prefix">John.Doe@example.com</data> @@ -354,4 +366,18 @@ <data key="website_id">0</data> <requiredEntity type="address">US_Address_AE</requiredEntity> </entity> + <entity name="Simple_US_Customer_CA_NY_Addresses" type="customer"> + <data key="group_id">1</data> + <data key="default_billing">true</data> + <data key="default_shipping">true</data> + <data key="email" unique="prefix">John.Doe@example.com</data> + <data key="firstname">John</data> + <data key="lastname">Doe</data> + <data key="fullname">John Doe</data> + <data key="password">pwdTest123!</data> + <data key="store_id">0</data> + <data key="website_id">0</data> + <requiredEntity type="address">US_Address_CA</requiredEntity> + <requiredEntity type="address">US_Address_NY_Not_Default_Address</requiredEntity> + </entity> </entities> diff --git a/app/code/Magento/Customer/Test/Mftf/Data/DateData.xml b/app/code/Magento/Customer/Test/Mftf/Data/DateData.xml new file mode 100644 index 0000000000000..5fbb1c279e38e --- /dev/null +++ b/app/code/Magento/Customer/Test/Mftf/Data/DateData.xml @@ -0,0 +1,25 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + /** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +--> + +<entities xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:noNamespaceSchemaLocation="urn:magento:mftf:DataGenerator/etc/dataProfileSchema.xsd"> + <entity name="EN_US_DATE"> + <data key="short">9/21/93</data> + <data key="short4DigitYear">9/21/1993</data> + <data key="medium">Sep 21, 1993</data> + <data key="long">September 21, 1993</data> + <data key="full">Tuesday, September 21, 1993</data> + </entity> + <entity name="FR_FR_DATE"> + <data key="short">21/09/1993</data> + <data key="short4DigitYear">21/09/1993</data> + <data key="medium">21 sept. 1993</data> + <data key="long">21 septembre 1993</data> + <data key="full">mardi 21 septembre 1993</data> + </entity> +</entities> diff --git a/app/code/Magento/Customer/Test/Mftf/Metadata/address-meta.xml b/app/code/Magento/Customer/Test/Mftf/Metadata/AddressMeta.xml similarity index 100% rename from app/code/Magento/Customer/Test/Mftf/Metadata/address-meta.xml rename to app/code/Magento/Customer/Test/Mftf/Metadata/AddressMeta.xml diff --git a/app/code/Magento/Customer/Test/Mftf/Metadata/customer_config_account_sharing-meta.xml b/app/code/Magento/Customer/Test/Mftf/Metadata/CustomerConfigAccountSharingMeta.xml similarity index 100% rename from app/code/Magento/Customer/Test/Mftf/Metadata/customer_config_account_sharing-meta.xml rename to app/code/Magento/Customer/Test/Mftf/Metadata/CustomerConfigAccountSharingMeta.xml diff --git a/app/code/Magento/Customer/Test/Mftf/Metadata/customer_create_new_account-meta.xml b/app/code/Magento/Customer/Test/Mftf/Metadata/CustomerCreateNewAccountMeta.xml similarity index 100% rename from app/code/Magento/Customer/Test/Mftf/Metadata/customer_create_new_account-meta.xml rename to app/code/Magento/Customer/Test/Mftf/Metadata/CustomerCreateNewAccountMeta.xml diff --git a/app/code/Magento/Customer/Test/Mftf/Metadata/customer_extension_attribute-meta.xml b/app/code/Magento/Customer/Test/Mftf/Metadata/CustomerExtensionAttributeMeta.xml similarity index 100% rename from app/code/Magento/Customer/Test/Mftf/Metadata/customer_extension_attribute-meta.xml rename to app/code/Magento/Customer/Test/Mftf/Metadata/CustomerExtensionAttributeMeta.xml diff --git a/app/code/Magento/Customer/Test/Mftf/Metadata/customer_group-meta.xml b/app/code/Magento/Customer/Test/Mftf/Metadata/CustomerGroupMeta.xml similarity index 100% rename from app/code/Magento/Customer/Test/Mftf/Metadata/customer_group-meta.xml rename to app/code/Magento/Customer/Test/Mftf/Metadata/CustomerGroupMeta.xml diff --git a/app/code/Magento/Customer/Test/Mftf/Metadata/customer-meta.xml b/app/code/Magento/Customer/Test/Mftf/Metadata/CustomerMeta.xml similarity index 100% rename from app/code/Magento/Customer/Test/Mftf/Metadata/customer-meta.xml rename to app/code/Magento/Customer/Test/Mftf/Metadata/CustomerMeta.xml diff --git a/app/code/Magento/Customer/Test/Mftf/Metadata/customer_nested_extension_attribute-meta.xml b/app/code/Magento/Customer/Test/Mftf/Metadata/CustomerNestedExtensionAttributeMeta.xml similarity index 100% rename from app/code/Magento/Customer/Test/Mftf/Metadata/customer_nested_extension_attribute-meta.xml rename to app/code/Magento/Customer/Test/Mftf/Metadata/CustomerNestedExtensionAttributeMeta.xml diff --git a/app/code/Magento/Customer/Test/Mftf/Metadata/region-meta.xml b/app/code/Magento/Customer/Test/Mftf/Metadata/RegionMeta.xml similarity index 100% rename from app/code/Magento/Customer/Test/Mftf/Metadata/region-meta.xml rename to app/code/Magento/Customer/Test/Mftf/Metadata/RegionMeta.xml diff --git a/app/code/Magento/Customer/Test/Mftf/Page/StorefrontCustomerNewsletterManagePage.xml b/app/code/Magento/Customer/Test/Mftf/Page/StorefrontCustomerNewsletterManagePage.xml new file mode 100644 index 0000000000000..62fa49f7b4b38 --- /dev/null +++ b/app/code/Magento/Customer/Test/Mftf/Page/StorefrontCustomerNewsletterManagePage.xml @@ -0,0 +1,14 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + /** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +--> + +<pages xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Page/etc/PageObject.xsd"> + <page name="StorefrontCustomerNewsletterManagePage" url="/newsletter/manage/" area="storefront" module="Magento_Customer"> + <section name="StorefrontCustomerNewsletterSection"/> + </page> +</pages> diff --git a/app/code/Magento/Customer/Test/Mftf/Section/AdminCustomerAccountInformationSection.xml b/app/code/Magento/Customer/Test/Mftf/Section/AdminCustomerAccountInformationSection.xml index 2c9e66c15bbab..50e923d9b32cc 100644 --- a/app/code/Magento/Customer/Test/Mftf/Section/AdminCustomerAccountInformationSection.xml +++ b/app/code/Magento/Customer/Test/Mftf/Section/AdminCustomerAccountInformationSection.xml @@ -36,5 +36,6 @@ <element name="disabledGroup" type="text" selector="//div[@class='admin__action-group-wrap admin__action-multiselect-wrap action-select-wrap _disabled']"/> <element name="customerAttribute" type="input" selector="//input[contains(@name,'{{attributeCode}}')]" parameterized="true"/> <element name="attributeImage" type="block" selector="//div[contains(concat(' ',normalize-space(@class),' '),' file-uploader-preview ')]//img"/> + <element name="dateOfBirthValidationErrorField" type="text" selector="input[name='customer[dob]'] ~ label.admin__field-error"/> </section> </sections> diff --git a/app/code/Magento/Customer/Test/Mftf/Section/AdminCustomerConfigSection.xml b/app/code/Magento/Customer/Test/Mftf/Section/AdminCustomerConfigSection.xml index a934d71397b8c..f8a441e40a792 100644 --- a/app/code/Magento/Customer/Test/Mftf/Section/AdminCustomerConfigSection.xml +++ b/app/code/Magento/Customer/Test/Mftf/Section/AdminCustomerConfigSection.xml @@ -11,5 +11,7 @@ <element name="accountSharingOptionsTab" type="button" selector="#customer_account_share-head"/> <element name="shareCustomerAccountInherit" type="checkbox" selector="#customer_account_share_scope_inherit"/> <element name="shareCustomerAccount" type="select" selector="#customer_account_share_scope"/> + <element name="showDateOfBirth" type="select" selector="#customer_address_dob_show"/> + <element name="showDateOfBirthInherit" type="select" selector="#customer_address_dob_show_inherit"/> </section> </sections> diff --git a/app/code/Magento/Customer/Test/Mftf/Section/AdminCustomerShoppingCartSection.xml b/app/code/Magento/Customer/Test/Mftf/Section/AdminCustomerShoppingCartSection.xml deleted file mode 100644 index c4a4d650c1e59..0000000000000 --- a/app/code/Magento/Customer/Test/Mftf/Section/AdminCustomerShoppingCartSection.xml +++ /dev/null @@ -1,23 +0,0 @@ -<?xml version="1.0" encoding="UTF-8"?> -<!-- - /** - * Copyright © Magento, Inc. All rights reserved. - * See COPYING.txt for license details. - */ ---> - -<sections xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" - xsi:noNamespaceSchemaLocation="urn:magento:mftf:Page/etc/SectionObject.xsd"> - <section name="AdminCustomerShoppingCartSection"> - <element name="createOrderButton" type="button" selector="button[title='Create Order']"/> - </section> - - <section name="AdminCustomerShoppingCartProductItemSection"> - <element name="productItem" type="button" selector="#dt-products"/> - <element name="productNameFilter" type="input" selector="#source_products_filter_name"/> - <element name="searchButton" type="button" selector="//*[@id='anchor-content']//button[@title='Search']"/> - <element name="firstProductCheckbox" type="checkbox" selector="//*[@id='source_products_table']/tbody/tr[1]//*[@name='source_products']"/> - <element name="addSelectionsToMyCartButton" type="button" selector="//*[@id='products_search']/div[1]//*[text()='Add selections to my cart']"/> - <element name="addedProductName" type="text" selector="//*[@id='order-items_grid']//*[text()='{{var}}']" parameterized="true"/> - </section> -</sections> diff --git a/app/code/Magento/Customer/Test/Mftf/Section/AdminCustomerShoppingCartSection/AdminCustomerShoppingCartProductItemSection.xml b/app/code/Magento/Customer/Test/Mftf/Section/AdminCustomerShoppingCartSection/AdminCustomerShoppingCartProductItemSection.xml new file mode 100644 index 0000000000000..40fc23b6c72c1 --- /dev/null +++ b/app/code/Magento/Customer/Test/Mftf/Section/AdminCustomerShoppingCartSection/AdminCustomerShoppingCartProductItemSection.xml @@ -0,0 +1,18 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + /** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +--> +<sections xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Page/etc/SectionObject.xsd"> + <section name="AdminCustomerShoppingCartProductItemSection"> + <element name="productItem" type="button" selector="#dt-products"/> + <element name="productNameFilter" type="input" selector="#source_products_filter_name"/> + <element name="searchButton" type="button" selector="//*[@id='anchor-content']//button[@title='Search']"/> + <element name="firstProductCheckbox" type="checkbox" selector="//*[@id='source_products_table']/tbody/tr[1]//*[@name='source_products']"/> + <element name="addSelectionsToMyCartButton" type="button" selector="//*[@id='products_search']/div[1]//*[text()='Add selections to my cart']"/> + <element name="addedProductName" type="text" selector="//*[@id='order-items_grid']//*[text()='{{var}}']" parameterized="true"/> + </section> +</sections> diff --git a/app/code/Magento/Customer/Test/Mftf/Section/AdminCustomerShoppingCartSection/AdminCustomerShoppingCartSection.xml b/app/code/Magento/Customer/Test/Mftf/Section/AdminCustomerShoppingCartSection/AdminCustomerShoppingCartSection.xml new file mode 100644 index 0000000000000..06db56c481fd4 --- /dev/null +++ b/app/code/Magento/Customer/Test/Mftf/Section/AdminCustomerShoppingCartSection/AdminCustomerShoppingCartSection.xml @@ -0,0 +1,13 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + /** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +--> +<sections xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Page/etc/SectionObject.xsd"> + <section name="AdminCustomerShoppingCartSection"> + <element name="createOrderButton" type="button" selector="button[title='Create Order']"/> + </section> +</sections> diff --git a/app/code/Magento/Customer/Test/Mftf/Section/AdminEditCustomerGroupSection.xml b/app/code/Magento/Customer/Test/Mftf/Section/AdminEditCustomerGroupSection.xml new file mode 100644 index 0000000000000..917af88338d24 --- /dev/null +++ b/app/code/Magento/Customer/Test/Mftf/Section/AdminEditCustomerGroupSection.xml @@ -0,0 +1,14 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + /** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +--> + +<sections xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Page/etc/SectionObject.xsd"> + <section name="AdminEditCustomerGroupSection"> + <element name="deleteButton" type="button" selector=".page-actions-buttons button#delete"/> + </section> +</sections> diff --git a/app/code/Magento/Customer/Test/Mftf/Section/StorefrontCustomerCreateFormSection.xml b/app/code/Magento/Customer/Test/Mftf/Section/StorefrontCustomerCreateFormSection.xml deleted file mode 100644 index 9fc26a03b04ee..0000000000000 --- a/app/code/Magento/Customer/Test/Mftf/Section/StorefrontCustomerCreateFormSection.xml +++ /dev/null @@ -1,36 +0,0 @@ -<?xml version="1.0" encoding="UTF-8"?> -<!-- - /** - * Copyright © Magento, Inc. All rights reserved. - * See COPYING.txt for license details. - */ ---> - -<sections xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" - xsi:noNamespaceSchemaLocation="urn:magento:mftf:Page/etc/SectionObject.xsd"> - <section name="StorefrontCustomerCreateFormSection"> - <element name="firstnameField" type="input" selector="#firstname"/> - <element name="lastnameField" type="input" selector="#lastname"/> - <element name="lastnameLabel" type="text" selector="//label[@for='lastname']"/> - <element name="signUpForNewsletter" type="checkbox" selector="//div/input[@name='is_subscribed']"/> - <element name="emailField" type="input" selector="#email_address"/> - <element name="passwordField" type="input" selector="#password"/> - <element name="confirmPasswordField" type="input" selector="#password-confirmation"/> - <element name="createAccountButton" type="button" selector="button.action.submit.primary" timeout="30"/> - <element name="passwordErrorMessages" type="text" selector="#password-error"/> - </section> - <section name="StoreFrontCustomerAdvancedAttributesSection"> - <element name="textFieldAttribute" type="input" selector="//input[@id='{{var}}']" parameterized="true" /> - <element name="textAreaAttribute" type="input" selector="//textarea[@id='{{var}}']" parameterized="true" /> - <element name="multiLineFirstAttribute" type="input" selector="//input[@id='{{var}}_0']" parameterized="true" /> - <element name="multiLineSecondAttribute" type="input" selector="//input[@id='{{var}}_1']" parameterized="true" /> - <element name="datedAttribute" type="input" selector="//input[@id='{{var}}']" parameterized="true" /> - <element name="dropDownAttribute" type="select" selector="//select[@id='{{var}}']" parameterized="true" /> - <element name="dropDownOptionAttribute" type="text" selector="//*[@id='{{var}}']/option[2]" parameterized="true" /> - <element name="multiSelectFirstOptionAttribute" type="text" selector="//select[@id='{{var}}']/option[3]" parameterized="true" /> - <element name="yesNoAttribute" type="select" selector="//select[@id='{{var}}']" parameterized="true" /> - <element name="yesNoOptionAttribute" type="select" selector="//select[@id='{{var}}']/option[2]" parameterized="true" /> - <element name="selectedOption" type="text" selector="//select[@id='{{var}}']/option[@selected='selected']" parameterized="true"/> - <element name="attributeLabel" type="text" selector="//span[text()='{{attributeLabel}}']" parameterized="true"/> - </section> -</sections> diff --git a/app/code/Magento/Customer/Test/Mftf/Section/StorefrontCustomerCreateFormSection/StoreFrontCustomerAdvancedAttributesSection.xml b/app/code/Magento/Customer/Test/Mftf/Section/StorefrontCustomerCreateFormSection/StoreFrontCustomerAdvancedAttributesSection.xml new file mode 100644 index 0000000000000..b2f96eb539c08 --- /dev/null +++ b/app/code/Magento/Customer/Test/Mftf/Section/StorefrontCustomerCreateFormSection/StoreFrontCustomerAdvancedAttributesSection.xml @@ -0,0 +1,24 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + /** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +--> +<sections xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Page/etc/SectionObject.xsd"> + <section name="StoreFrontCustomerAdvancedAttributesSection"> + <element name="textFieldAttribute" type="input" selector="//input[@id='{{var}}']" parameterized="true"/> + <element name="textAreaAttribute" type="input" selector="//textarea[@id='{{var}}']" parameterized="true"/> + <element name="multiLineFirstAttribute" type="input" selector="//input[@id='{{var}}_0']" parameterized="true"/> + <element name="multiLineSecondAttribute" type="input" selector="//input[@id='{{var}}_1']" parameterized="true"/> + <element name="datedAttribute" type="input" selector="//input[@id='{{var}}']" parameterized="true"/> + <element name="dropDownAttribute" type="select" selector="//select[@id='{{var}}']" parameterized="true"/> + <element name="dropDownOptionAttribute" type="text" selector="//*[@id='{{var}}']/option[2]" parameterized="true"/> + <element name="multiSelectFirstOptionAttribute" type="text" selector="//select[@id='{{var}}']/option[3]" parameterized="true"/> + <element name="yesNoAttribute" type="select" selector="//select[@id='{{var}}']" parameterized="true"/> + <element name="yesNoOptionAttribute" type="select" selector="//select[@id='{{var}}']/option[2]" parameterized="true"/> + <element name="selectedOption" type="text" selector="//select[@id='{{var}}']/option[@selected='selected']" parameterized="true"/> + <element name="attributeLabel" type="text" selector="//span[text()='{{attributeLabel}}']" parameterized="true"/> + </section> +</sections> diff --git a/app/code/Magento/Customer/Test/Mftf/Section/StorefrontCustomerCreateFormSection/StorefrontCustomerCreateFormSection.xml b/app/code/Magento/Customer/Test/Mftf/Section/StorefrontCustomerCreateFormSection/StorefrontCustomerCreateFormSection.xml new file mode 100644 index 0000000000000..6b65ef861472c --- /dev/null +++ b/app/code/Magento/Customer/Test/Mftf/Section/StorefrontCustomerCreateFormSection/StorefrontCustomerCreateFormSection.xml @@ -0,0 +1,22 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + /** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +--> +<sections xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Page/etc/SectionObject.xsd"> + <section name="StorefrontCustomerCreateFormSection"> + <element name="firstnameField" type="input" selector="#firstname"/> + <element name="lastnameField" type="input" selector="#lastname"/> + <element name="lastnameLabel" type="text" selector="//label[@for='lastname']"/> + <element name="dobField" type="input" selector="#dob"/> + <element name="signUpForNewsletter" type="checkbox" selector="//div/input[@name='is_subscribed']"/> + <element name="emailField" type="input" selector="#email_address"/> + <element name="passwordField" type="input" selector="#password"/> + <element name="confirmPasswordField" type="input" selector="#password-confirmation"/> + <element name="createAccountButton" type="button" selector="button.action.submit.primary" timeout="30"/> + <element name="passwordErrorMessages" type="text" selector="#password-error"/> + </section> +</sections> diff --git a/app/code/Magento/Customer/Test/Mftf/Section/StorefrontCustomerDashboardAccountInformationSection.xml b/app/code/Magento/Customer/Test/Mftf/Section/StorefrontCustomerDashboardAccountInformationSection.xml deleted file mode 100644 index 85d0fd166a77e..0000000000000 --- a/app/code/Magento/Customer/Test/Mftf/Section/StorefrontCustomerDashboardAccountInformationSection.xml +++ /dev/null @@ -1,34 +0,0 @@ -<?xml version="1.0" encoding="UTF-8"?> -<!-- - /** - * Copyright © Magento, Inc. All rights reserved. - * See COPYING.txt for license details. - */ ---> - -<sections xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" - xsi:noNamespaceSchemaLocation="urn:magento:mftf:Page/etc/SectionObject.xsd"> - <section name="StorefrontCustomerDashboardAccountInformationSection"> - <element name="ContactInformation" type="textarea" selector=".box.box-information .box-content"/> - <element name="edit" type="block" selector=".action.edit" timeout="15"/> - <element name="changePassword" type="block" selector=".action.change-password" timeout="15"/> - <element name="editLink" type="text" selector=".box-information .edit"/> - </section> - <section name="StorefrontCustomerAddressSection"> - <element name="firstName" type="input" selector="#firstname"/> - <element name="lastName" type="input" selector="#lastname"/> - <element name="company" type="input" selector="#company"/> - <element name="phoneNumber" type="input" selector="#telephone"/> - <element name="streetAddress1" type="input" selector="#street_1"/> - <element name="streetAddress2" type="input" selector="#street_2"/> - <element name="city" type="input" selector="#city"/> - <element name="stateProvince" type="select" selector="#region_id"/> - <element name="stateProvinceFill" type="input" selector="#region"/> - <element name="zip" type="input" selector="#zip"/> - <element name="country" type="select" selector="#country"/> - <element name="saveAddress" type="button" selector="[data-action='save-address']" timeout="30"/> - </section> - <section name="StorefrontCustomerRecentOrdersSection"> - <element name="orderTotal" type="text" selector=".total .price"/> - </section> -</sections> diff --git a/app/code/Magento/Customer/Test/Mftf/Section/StorefrontCustomerDashboardAccountInformationSection/StorefrontCustomerAddressSection.xml b/app/code/Magento/Customer/Test/Mftf/Section/StorefrontCustomerDashboardAccountInformationSection/StorefrontCustomerAddressSection.xml new file mode 100644 index 0000000000000..c1e0ac6573be7 --- /dev/null +++ b/app/code/Magento/Customer/Test/Mftf/Section/StorefrontCustomerDashboardAccountInformationSection/StorefrontCustomerAddressSection.xml @@ -0,0 +1,24 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + /** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +--> +<sections xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Page/etc/SectionObject.xsd"> + <section name="StorefrontCustomerAddressSection"> + <element name="firstName" type="input" selector="#firstname"/> + <element name="lastName" type="input" selector="#lastname"/> + <element name="company" type="input" selector="#company"/> + <element name="phoneNumber" type="input" selector="#telephone"/> + <element name="streetAddress1" type="input" selector="#street_1"/> + <element name="streetAddress2" type="input" selector="#street_2"/> + <element name="city" type="input" selector="#city"/> + <element name="stateProvince" type="select" selector="#region_id"/> + <element name="stateProvinceFill" type="input" selector="#region"/> + <element name="zip" type="input" selector="#zip"/> + <element name="country" type="select" selector="#country"/> + <element name="saveAddress" type="button" selector="[data-action='save-address']" timeout="30"/> + </section> +</sections> diff --git a/app/code/Magento/Customer/Test/Mftf/Section/StorefrontCustomerDashboardAccountInformationSection/StorefrontCustomerDashboardAccountInformationSection.xml b/app/code/Magento/Customer/Test/Mftf/Section/StorefrontCustomerDashboardAccountInformationSection/StorefrontCustomerDashboardAccountInformationSection.xml new file mode 100644 index 0000000000000..74efa8daf0aca --- /dev/null +++ b/app/code/Magento/Customer/Test/Mftf/Section/StorefrontCustomerDashboardAccountInformationSection/StorefrontCustomerDashboardAccountInformationSection.xml @@ -0,0 +1,16 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + /** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +--> +<sections xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Page/etc/SectionObject.xsd"> + <section name="StorefrontCustomerDashboardAccountInformationSection"> + <element name="ContactInformation" type="textarea" selector=".box.box-information .box-content"/> + <element name="edit" type="block" selector=".action.edit" timeout="15"/> + <element name="changePassword" type="block" selector=".action.change-password" timeout="15"/> + <element name="editLink" type="text" selector=".box-information .edit"/> + </section> +</sections> diff --git a/app/code/Magento/Customer/Test/Mftf/Section/StorefrontCustomerDashboardAccountInformationSection/StorefrontCustomerRecentOrdersSection.xml b/app/code/Magento/Customer/Test/Mftf/Section/StorefrontCustomerDashboardAccountInformationSection/StorefrontCustomerRecentOrdersSection.xml new file mode 100644 index 0000000000000..324b44354bf4f --- /dev/null +++ b/app/code/Magento/Customer/Test/Mftf/Section/StorefrontCustomerDashboardAccountInformationSection/StorefrontCustomerRecentOrdersSection.xml @@ -0,0 +1,13 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + /** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +--> +<sections xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Page/etc/SectionObject.xsd"> + <section name="StorefrontCustomerRecentOrdersSection"> + <element name="orderTotal" type="text" selector=".total .price"/> + </section> +</sections> diff --git a/app/code/Magento/Customer/Test/Mftf/Section/StorefrontCustomerNewsletterSection.xml b/app/code/Magento/Customer/Test/Mftf/Section/StorefrontCustomerNewsletterSection.xml new file mode 100644 index 0000000000000..0275603b26227 --- /dev/null +++ b/app/code/Magento/Customer/Test/Mftf/Section/StorefrontCustomerNewsletterSection.xml @@ -0,0 +1,15 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + /** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +--> + +<sections xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Page/etc/SectionObject.xsd"> + <section name="StorefrontCustomerNewsletterSection"> + <element name="newsletterCheckbox" type="checkbox" selector="#subscription.checkbox"/> + <element name="submit" type="button" selector=".action.save.primary"/> + </section> +</sections> diff --git a/app/code/Magento/Customer/Test/Mftf/Section/StorefrontCustomerSignInFormSection.xml b/app/code/Magento/Customer/Test/Mftf/Section/StorefrontCustomerSignInFormSection.xml deleted file mode 100644 index 6b333a0c714c0..0000000000000 --- a/app/code/Magento/Customer/Test/Mftf/Section/StorefrontCustomerSignInFormSection.xml +++ /dev/null @@ -1,31 +0,0 @@ -<?xml version="1.0" encoding="UTF-8"?> -<!-- - /** - * Copyright © Magento, Inc. All rights reserved. - * See COPYING.txt for license details. - */ ---> - -<sections xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" - xsi:noNamespaceSchemaLocation="urn:magento:mftf:Page/etc/SectionObject.xsd"> - <section name="StorefrontCustomerSignInFormSection"> - <element name="emailField" type="input" selector="#email"/> - <element name="passwordField" type="input" selector="#pass"/> - <element name="signInAccountButton" type="button" selector="#send2" timeout="30"/> - <element name="forgotPasswordLink" type="button" selector=".action.remind" timeout="10"/> - <element name="customerLoginBlock" type="text" selector=".login-container .block.block-customer-login"/> - </section> - <section name="StorefrontCustomerSignInPopupFormSection"> - <element name="errorMessage" type="input" selector="[data-ui-id='checkout-cart-validationmessages-message-error']"/> - <element name="email" type="input" selector="#customer-email"/> - <element name="password" type="input" selector="#pass"/> - <element name="signIn" type="button" selector="#send2" timeout="30"/> - <element name="createAnAccount" type="button" selector="//div[contains(@class,'actions-toolbar')]//a[contains(.,'Create an Account')]" timeout="30"/> - </section> - <section name="StorefrontCustomerSignInLinkSection"> - <element name="signInLink" type="button" selector=".action-auth-toggle" timeout="30"/> - <element name="email" type="input" selector="#login-email"/> - <element name="password" type="input" selector="#login-password"/> - <element name="signInBtn" type="button" selector="//button[contains(@class, 'action-login') and not(contains(@id,'send2'))]" timeout="30"/> - </section> -</sections> diff --git a/app/code/Magento/Customer/Test/Mftf/Section/StorefrontCustomerSignInFormSection/StorefrontCustomerSignInFormSection.xml b/app/code/Magento/Customer/Test/Mftf/Section/StorefrontCustomerSignInFormSection/StorefrontCustomerSignInFormSection.xml new file mode 100644 index 0000000000000..2e40610c18f82 --- /dev/null +++ b/app/code/Magento/Customer/Test/Mftf/Section/StorefrontCustomerSignInFormSection/StorefrontCustomerSignInFormSection.xml @@ -0,0 +1,17 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + /** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +--> +<sections xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Page/etc/SectionObject.xsd"> + <section name="StorefrontCustomerSignInFormSection"> + <element name="emailField" type="input" selector="#email"/> + <element name="passwordField" type="input" selector="#pass"/> + <element name="signInAccountButton" type="button" selector="#send2" timeout="30"/> + <element name="forgotPasswordLink" type="button" selector=".action.remind" timeout="10"/> + <element name="customerLoginBlock" type="text" selector=".login-container .block.block-customer-login"/> + </section> +</sections> diff --git a/app/code/Magento/Customer/Test/Mftf/Section/StorefrontCustomerSignInFormSection/StorefrontCustomerSignInLinkSection.xml b/app/code/Magento/Customer/Test/Mftf/Section/StorefrontCustomerSignInFormSection/StorefrontCustomerSignInLinkSection.xml new file mode 100644 index 0000000000000..3f6867499399e --- /dev/null +++ b/app/code/Magento/Customer/Test/Mftf/Section/StorefrontCustomerSignInFormSection/StorefrontCustomerSignInLinkSection.xml @@ -0,0 +1,16 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + /** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +--> +<sections xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Page/etc/SectionObject.xsd"> + <section name="StorefrontCustomerSignInLinkSection"> + <element name="signInLink" type="button" selector=".action-auth-toggle" timeout="30"/> + <element name="email" type="input" selector="#login-email"/> + <element name="password" type="input" selector="#login-password"/> + <element name="signInBtn" type="button" selector="//button[contains(@class, 'action-login') and not(contains(@id,'send2'))]" timeout="30"/> + </section> +</sections> diff --git a/app/code/Magento/Customer/Test/Mftf/Section/StorefrontCustomerSignInFormSection/StorefrontCustomerSignInPopupFormSection.xml b/app/code/Magento/Customer/Test/Mftf/Section/StorefrontCustomerSignInFormSection/StorefrontCustomerSignInPopupFormSection.xml new file mode 100644 index 0000000000000..c33736a54f442 --- /dev/null +++ b/app/code/Magento/Customer/Test/Mftf/Section/StorefrontCustomerSignInFormSection/StorefrontCustomerSignInPopupFormSection.xml @@ -0,0 +1,17 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + /** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +--> +<sections xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Page/etc/SectionObject.xsd"> + <section name="StorefrontCustomerSignInPopupFormSection"> + <element name="errorMessage" type="input" selector="[data-ui-id='checkout-cart-validationmessages-message-error']"/> + <element name="email" type="input" selector="#customer-email"/> + <element name="password" type="input" selector="#pass"/> + <element name="signIn" type="button" selector="#send2" timeout="30"/> + <element name="createAnAccount" type="button" selector="//div[contains(@class,'actions-toolbar')]//a[contains(.,'Create an Account')]" timeout="30"/> + </section> +</sections> diff --git a/app/code/Magento/Customer/Test/Mftf/Section/StorefrontPanelHeaderSection.xml b/app/code/Magento/Customer/Test/Mftf/Section/StorefrontPanelHeaderSection.xml index 3610532c5356b..39dd719ce10f4 100644 --- a/app/code/Magento/Customer/Test/Mftf/Section/StorefrontPanelHeaderSection.xml +++ b/app/code/Magento/Customer/Test/Mftf/Section/StorefrontPanelHeaderSection.xml @@ -9,9 +9,6 @@ <sections xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="urn:magento:mftf:Page/etc/SectionObject.xsd"> <section name="StorefrontPanelHeaderSection"> - <!-- Element name="WelcomeMessage" Deprecated due to incorrect naming convention please use name="welcomeMessage" --> - <element name="WelcomeMessage" type="text" selector=".greet.welcome span"/> - <element name="welcomeMessage" type="text" selector="header>.panel .greet.welcome" /> <element name="createAnAccountLink" type="select" selector="//div[@class='panel wrapper']//li/a[contains(.,'Create an Account')]" timeout="30"/> <element name="notYouLink" type="button" selector=".greet.welcome span a"/> diff --git a/app/code/Magento/Customer/Test/Mftf/Section/SwitchAccountSection.xml b/app/code/Magento/Customer/Test/Mftf/Section/SwitchAccountSection.xml deleted file mode 100644 index 4442e317694ee..0000000000000 --- a/app/code/Magento/Customer/Test/Mftf/Section/SwitchAccountSection.xml +++ /dev/null @@ -1,21 +0,0 @@ -<?xml version="1.0" encoding="UTF-8"?> -<!-- - /** - * Copyright © Magento, Inc. All rights reserved. - * See COPYING.txt for license details. - */ ---> - -<sections xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" - xsi:noNamespaceSchemaLocation="urn:magento:mftf:Page/etc/SectionObject.xsd"> - <section name="LoginFormSection"> - <element name="username" type="input" selector="#username"/> - <element name="password" type="input" selector="#login"/> - <element name="signIn" type="button" selector=".actions .action-primary" timeout="30"/> - </section> - - <section name="SignOutSection"> - <element name="admin" type="button" selector=".admin__action-dropdown-text"/> - <element name="logout" type="button" selector="//*[contains(text(), 'Sign Out')]"/> - </section> -</sections> diff --git a/app/code/Magento/Customer/Test/Mftf/Section/SwitchAccountSection/LoginFormSection.xml b/app/code/Magento/Customer/Test/Mftf/Section/SwitchAccountSection/LoginFormSection.xml new file mode 100644 index 0000000000000..56b8731e80f72 --- /dev/null +++ b/app/code/Magento/Customer/Test/Mftf/Section/SwitchAccountSection/LoginFormSection.xml @@ -0,0 +1,15 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + /** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +--> +<sections xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Page/etc/SectionObject.xsd"> + <section name="LoginFormSection"> + <element name="username" type="input" selector="#username"/> + <element name="password" type="input" selector="#login"/> + <element name="signIn" type="button" selector=".actions .action-primary" timeout="30"/> + </section> +</sections> diff --git a/app/code/Magento/Customer/Test/Mftf/Section/SwitchAccountSection/SignOutSection.xml b/app/code/Magento/Customer/Test/Mftf/Section/SwitchAccountSection/SignOutSection.xml new file mode 100644 index 0000000000000..763c09923ea2d --- /dev/null +++ b/app/code/Magento/Customer/Test/Mftf/Section/SwitchAccountSection/SignOutSection.xml @@ -0,0 +1,14 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + /** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +--> +<sections xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Page/etc/SectionObject.xsd"> + <section name="SignOutSection"> + <element name="admin" type="button" selector=".admin__action-dropdown-text"/> + <element name="logout" type="button" selector="//*[contains(text(), 'Sign Out')]"/> + </section> +</sections> diff --git a/app/code/Magento/Customer/Test/Mftf/Test/AddingProductWithExpiredSessionTest.xml b/app/code/Magento/Customer/Test/Mftf/Test/AddingProductWithExpiredSessionTest.xml deleted file mode 100644 index 01f35439f23b8..0000000000000 --- a/app/code/Magento/Customer/Test/Mftf/Test/AddingProductWithExpiredSessionTest.xml +++ /dev/null @@ -1,46 +0,0 @@ -<?xml version="1.0" encoding="UTF-8"?> -<!-- - /** - * Copyright © Magento, Inc. All rights reserved. - * See COPYING.txt for license details. - */ ---> - -<tests xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/testSchema.xsd"> - <test name="AddingProductWithExpiredSessionTest"> - <annotations> - <title value="Adding a product to cart from category page with an expired session"/> - <description value="Adding a product to cart from category page with an expired session"/> - <features value="Module/ Catalog"/> - <severity value="MAJOR"/> - <testCaseId value="MAGETWO-93289"/> - <stories value="MAGETWO-66666: Adding a product to cart from category page with an expired session does not allow product to be added"/> - <group value="customer"/> - </annotations> - - <before> - <createData entity="_defaultCategory" stepKey="createCategory"/> - <createData entity="_defaultProduct" stepKey="createSimpleProduct"> - <requiredEntity createDataKey="createCategory"/> - </createData> - </before> - - <!--Navigate to a category page --> - <amOnPage url="$$createSimpleProduct.name$$.html" stepKey="goToProductPage"/> - <waitForPageLoad stepKey="waitForPageLoad"/> - - <!-- Remove PHPSESSID and form_key to replicate an expired session--> - <resetCookie userInput="PHPSESSID" stepKey="resetCookieForCart"/> - <resetCookie userInput="form_key" stepKey="resetCookieForCart2"/> - - <!-- "Add to Cart" any product--> - <click selector="{{StorefrontProductPageSection.addToCartBtn}}" stepKey="addToCart"/> - <see stepKey="assertErrorMessage" userInput="Your session has expired"/> - <after> - <!--Delete created product--> - <deleteData createDataKey="createSimpleProduct" stepKey="deleteProduct"/> - <deleteData createDataKey="createCategory" stepKey="deleteCategory"/> - </after> - - </test> -</tests> diff --git a/app/code/Magento/Customer/Test/Mftf/Test/AdminAddNewDefaultBillingShippingCustomerAddressTest.xml b/app/code/Magento/Customer/Test/Mftf/Test/AdminAddNewDefaultBillingShippingCustomerAddressTest.xml index fba7ebd2d4d5e..b061b6a256471 100644 --- a/app/code/Magento/Customer/Test/Mftf/Test/AdminAddNewDefaultBillingShippingCustomerAddressTest.xml +++ b/app/code/Magento/Customer/Test/Mftf/Test/AdminAddNewDefaultBillingShippingCustomerAddressTest.xml @@ -13,17 +13,17 @@ <stories value="Add new default billing/shipping customer address"/> <title value="Add new default billing/shipping customer address"/> <description value="Add new default billing/shipping customer address on customer addresses tab"/> - <severity value="CRITICAL"/> + <severity value="BLOCKER"/> <testCaseId value="MAGETWO-94814"/> <group value="customer"/> </annotations> <before> <createData entity="Simple_US_Customer_Multiple_Addresses_No_Default_Address" stepKey="customer"/> - <actionGroup ref="LoginAsAdmin" stepKey="login"/> + <actionGroup ref="AdminLoginActionGroup" stepKey="login"/> </before> <after> <deleteData createDataKey="customer" stepKey="deleteCustomer"/> - <actionGroup ref="logout" stepKey="logout"/> + <actionGroup ref="AdminLogoutActionGroup" stepKey="logout"/> </after> <!-- - Step1. Login to admin and go to Customers > All Customers. @@ -63,7 +63,11 @@ <see userInput="{{US_Address_TX.street[0]}}" selector="{{AdminCustomerAddressesDefaultShippingSection.addressDetails}}" stepKey="assertDefaultShippingAddressIsChanged"/> <click selector="{{AdminCustomerAddressesDefaultBillingSection.editButton}}" stepKey="clickEditDefaultBillingAddress"/> <waitForPageLoad stepKey="waitForCustomerAddressAddUpdateFormLoad"/> - <assertElementContainsAttribute selector="{{AdminCustomerAddressesSection.defaultBillingAddressCheckBox}}" attribute="value" expectedValue="1" stepKey="assertDefaultBillingIsEnabledCustomerAddressAddUpdateForm"/> - <assertElementContainsAttribute selector="{{AdminCustomerAddressesSection.defaultShippingAddressCheckBox}}" attribute="value" expectedValue="1" stepKey="assertDefaultShippingIsEnabledOnCustomerAddressAddUpdateForm"/> + <assertElementContainsAttribute stepKey="assertDefaultBillingIsEnabledCustomerAddressAddUpdateForm"> + <expectedResult selector="{{AdminCustomerAddressesSection.defaultBillingAddressCheckBox}}" attribute="value" type="string">1</expectedResult> + </assertElementContainsAttribute> + <assertElementContainsAttribute stepKey="assertDefaultShippingIsEnabledOnCustomerAddressAddUpdateForm"> + <expectedResult selector="{{AdminCustomerAddressesSection.defaultShippingAddressCheckBox}}" attribute="value" type="string">1</expectedResult> + </assertElementContainsAttribute> </test> </tests> diff --git a/app/code/Magento/Customer/Test/Mftf/Test/AdminChangeAllCustomersGroupViaGridTest.xml b/app/code/Magento/Customer/Test/Mftf/Test/AdminChangeAllCustomersGroupViaGridTest.xml new file mode 100644 index 0000000000000..59c50f905878b --- /dev/null +++ b/app/code/Magento/Customer/Test/Mftf/Test/AdminChangeAllCustomersGroupViaGridTest.xml @@ -0,0 +1,22 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + /** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +--> + +<tests xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/testSchema.xsd"> + <test name="AdminChangeAllCustomersGroupViaGridTest" extends="AdminChangeSingleCustomerGroupViaGridTest"> + <annotations> + <title value="Change all customers' group via grid"/> + <description value="Select All customers to change their group"/> + <testCaseId value="MC-26040"/> + </annotations> + + <remove keyForRemoval="filterCustomer"/> + <actionGroup ref="ClearFiltersAdminDataGridActionGroup" before="selectCustomer" stepKey="clearFilters"/> + <actionGroup ref="AdminSelectAllCustomers" stepKey="selectCustomer"/> + </test> +</tests> diff --git a/app/code/Magento/Customer/Test/Mftf/Test/AdminChangeCustomerGenderInCustomersGridTest.xml b/app/code/Magento/Customer/Test/Mftf/Test/AdminChangeCustomerGenderInCustomersGridTest.xml index 7ca9a6993f2fc..423954a7d9bf7 100644 --- a/app/code/Magento/Customer/Test/Mftf/Test/AdminChangeCustomerGenderInCustomersGridTest.xml +++ b/app/code/Magento/Customer/Test/Mftf/Test/AdminChangeCustomerGenderInCustomersGridTest.xml @@ -21,7 +21,7 @@ </annotations> <before> <createData entity="Simple_US_Customer" stepKey="createCustomer"/> - <actionGroup ref="LoginAsAdmin" stepKey="login"/> + <actionGroup ref="AdminLoginActionGroup" stepKey="login"/> </before> <after> <deleteData createDataKey="createCustomer" stepKey="deleteCustomer"/> @@ -30,7 +30,7 @@ <waitForPageLoad stepKey="waitForCustomersGrid"/> <actionGroup ref="AdminResetFilterInCustomerGrid" stepKey="resetFilter"/> - <actionGroup ref="logout" stepKey="logout"/> + <actionGroup ref="AdminLogoutActionGroup" stepKey="logout"/> </after> <!-- Open customers grid page, filter by created customer--> <actionGroup ref="AdminFilterCustomerByEmail" stepKey="filterCustomerGridByEmail"> diff --git a/app/code/Magento/Customer/Test/Mftf/Test/AdminChangeSingleCustomerGroupViaGridTest.xml b/app/code/Magento/Customer/Test/Mftf/Test/AdminChangeSingleCustomerGroupViaGridTest.xml new file mode 100644 index 0000000000000..a7383af2d7eea --- /dev/null +++ b/app/code/Magento/Customer/Test/Mftf/Test/AdminChangeSingleCustomerGroupViaGridTest.xml @@ -0,0 +1,55 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + /** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +--> + +<tests xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/testSchema.xsd"> + <test name="AdminChangeSingleCustomerGroupViaGridTest"> + <annotations> + <features value="Customer"/> + <title value="Change a single customer group via grid"/> + <description value="From the selection of All Customers select a single customer to change their group"/> + <severity value="MAJOR"/> + <testCaseId value="MC-26039"/> + <stories value="Customer Edit"/> + <group value="customer"/> + <group value="mtf_migrated"/> + </annotations> + + <before> + <createData entity="Simple_US_Customer" stepKey="createCustomer"/> + <createData entity="CustomerGroupChange" stepKey="createCustomerGroup"/> + <actionGroup ref="AdminLoginActionGroup" stepKey="loginAsAdmin"/> + </before> + <after> + <!--Delete created data--> + <deleteData createDataKey="createCustomer" stepKey="deleteCustomer"/> + <deleteData createDataKey="createCustomerGroup" stepKey="deleteCustomerGroup"/> + <actionGroup ref="NavigateToAllCustomerPage" stepKey="navigateToCustomersPage"/> + <actionGroup ref="ClearFiltersAdminDataGridActionGroup" stepKey="clearCustomersGridFilter"/> + <actionGroup ref="AdminLogoutActionGroup" stepKey="logoutFromAdmin"/> + </after> + + <actionGroup ref="NavigateToAllCustomerPage" stepKey="navigateToCustomersPage"/> + <actionGroup ref="AdminFilterCustomerGridByEmail" stepKey="filterCustomer"> + <argument name="email" value="$$createCustomer.email$$"/> + </actionGroup> + <actionGroup ref="AdminSelectCustomerByEmail" stepKey="selectCustomer"> + <argument name="customerEmail" value="$$createCustomer.email$$"/> + </actionGroup> + <actionGroup ref="SetCustomerGroupForSelectedCustomersViaGrid" stepKey="setCustomerGroup"> + <argument name="groupName" value="$$createCustomerGroup.code$$"/> + </actionGroup> + <actionGroup ref="AdminFilterCustomerGridByEmail" stepKey="filterCustomerAfterGroupChange"> + <argument name="email" value="$$createCustomer.email$$"/> + </actionGroup> + <actionGroup ref="VerifyCustomerGroupForCustomer" stepKey="verifyCustomerGroupSet"> + <argument name="customerEmail" value="$$createCustomer.email$$"/> + <argument name="groupName" value="$$createCustomerGroup.code$$"/> + </actionGroup> + </test> +</tests> diff --git a/app/code/Magento/Customer/Test/Mftf/Test/AdminCheckDateOfBirthValidationForFranceDateFormatTest.xml b/app/code/Magento/Customer/Test/Mftf/Test/AdminCheckDateOfBirthValidationForFranceDateFormatTest.xml new file mode 100644 index 0000000000000..78a1d122d59d4 --- /dev/null +++ b/app/code/Magento/Customer/Test/Mftf/Test/AdminCheckDateOfBirthValidationForFranceDateFormatTest.xml @@ -0,0 +1,36 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + /** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +--> +<tests xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/testSchema.xsd"> + <test name="AdminCheckDateOfBirthValidationForFranceDateFormatTest"> + <annotations> + <features value="Customer"/> + <stories value="Checks 'Date of Birth' field validation for the France date format value"/> + <title value="Checks 'Date of Birth' field validation for the France date format value"/> + <group value="customer"/> + <group value="ui"/> + </annotations> + <before> + <magentoCLI command="setup:static-content:deploy fr_FR" stepKey="deployStaticContentWithFrenchLocale"/> + <actionGroup ref="AdminLoginActionGroup" stepKey="loginAsAdmin"/> + <actionGroup ref="SetAdminAccountActionGroup" stepKey="setAdminInterfaceLocaleToFrance"> + <argument name="InterfaceLocaleByValue" value="fr_FR"/> + </actionGroup> + </before> + + <after> + <actionGroup ref="SetAdminAccountActionGroup" stepKey="setAdminInterfaceLocaleToDefaultValue"> + <argument name="InterfaceLocaleByValue" value="en_US"/> + </actionGroup> + <actionGroup ref="AdminLogoutActionGroup" stepKey="logout"/> + </after> + + <actionGroup ref="AdminNavigateNewCustomerActionGroup" stepKey="navigateToNewCustomerPage"/> + <actionGroup ref="AssertAdminCustomerDateOfBirthValidationMessageActionGroup" stepKey="assertDateOfBirthValidationMessage"/> + </test> +</tests> diff --git a/app/code/Magento/Customer/Test/Mftf/Test/AdminCheckDefaultValueDisableAutoGroupChangeIsNoTest.xml b/app/code/Magento/Customer/Test/Mftf/Test/AdminCheckDefaultValueDisableAutoGroupChangeIsNoTest.xml index be96765920bf5..ab5e332aeed64 100644 --- a/app/code/Magento/Customer/Test/Mftf/Test/AdminCheckDefaultValueDisableAutoGroupChangeIsNoTest.xml +++ b/app/code/Magento/Customer/Test/Mftf/Test/AdminCheckDefaultValueDisableAutoGroupChangeIsNoTest.xml @@ -21,10 +21,10 @@ <before> <magentoCLI command="config:set customer/create_account/viv_disable_auto_group_assign_default 0" stepKey="setConfigDefaultIsNo"/> <magentoCLI command="cache:flush" stepKey="flushCache"/> - <actionGroup ref="LoginAsAdmin" stepKey="loginAsAdmin1"/> + <actionGroup ref="AdminLoginActionGroup" stepKey="loginAsAdmin1"/> </before> <after> - <actionGroup ref="logout" stepKey="adminLogout"/> + <actionGroup ref="AdminLogoutActionGroup" stepKey="adminLogout"/> </after> <actionGroup ref="AdminNavigateNewCustomerActionGroup" stepKey="navigateToNewCustomer"/> diff --git a/app/code/Magento/Customer/Test/Mftf/Test/AdminCheckDefaultValueDisableAutoGroupChangeIsYesTest.xml b/app/code/Magento/Customer/Test/Mftf/Test/AdminCheckDefaultValueDisableAutoGroupChangeIsYesTest.xml index 87cba0c10dbc1..0bf221d49ab74 100644 --- a/app/code/Magento/Customer/Test/Mftf/Test/AdminCheckDefaultValueDisableAutoGroupChangeIsYesTest.xml +++ b/app/code/Magento/Customer/Test/Mftf/Test/AdminCheckDefaultValueDisableAutoGroupChangeIsYesTest.xml @@ -21,12 +21,12 @@ <before> <magentoCLI command="config:set customer/create_account/viv_disable_auto_group_assign_default 1" stepKey="setConfigDefaultIsYes"/> <magentoCLI command="cache:flush" stepKey="flushCache"/> - <actionGroup ref="LoginAsAdmin" stepKey="loginAsAdmin1"/> + <actionGroup ref="AdminLoginActionGroup" stepKey="loginAsAdmin1"/> </before> <after> <magentoCLI command="config:set customer/create_account/viv_disable_auto_group_assign_default 0" stepKey="setConfigDefaultIsNo"/> <magentoCLI command="cache:flush" stepKey="flushCache"/> - <actionGroup ref="logout" stepKey="adminLogout"/> + <actionGroup ref="AdminLogoutActionGroup" stepKey="adminLogout"/> </after> <actionGroup ref="AdminNavigateNewCustomerActionGroup" stepKey="navigateToNewCustomer"/> diff --git a/app/code/Magento/Customer/Test/Mftf/Test/AdminCreateCustomerGroupAlreadyExistsTest.xml b/app/code/Magento/Customer/Test/Mftf/Test/AdminCreateCustomerGroupAlreadyExistsTest.xml index 6b1c1f29f97fc..c660ce031422e 100644 --- a/app/code/Magento/Customer/Test/Mftf/Test/AdminCreateCustomerGroupAlreadyExistsTest.xml +++ b/app/code/Magento/Customer/Test/Mftf/Test/AdminCreateCustomerGroupAlreadyExistsTest.xml @@ -20,10 +20,10 @@ <group value="mtf_migrated"/> </annotations> <before> - <actionGroup ref="LoginAsAdmin" stepKey="loginAsAdmin"/> + <actionGroup ref="AdminLoginActionGroup" stepKey="loginAsAdmin"/> </before> <after> - <actionGroup ref="logout" stepKey="logout"/> + <actionGroup ref="AdminLogoutActionGroup" stepKey="logout"/> </after> <!-- Steps: 1. Log in to backend as admin user. 2. Navigate to Stores > Other Settings > Customer Groups. diff --git a/app/code/Magento/Customer/Test/Mftf/Test/AdminCreateCustomerRetailerWithoutAddressTest.xml b/app/code/Magento/Customer/Test/Mftf/Test/AdminCreateCustomerRetailerWithoutAddressTest.xml index 36592ab38e91d..3488d2c94dd69 100644 --- a/app/code/Magento/Customer/Test/Mftf/Test/AdminCreateCustomerRetailerWithoutAddressTest.xml +++ b/app/code/Magento/Customer/Test/Mftf/Test/AdminCreateCustomerRetailerWithoutAddressTest.xml @@ -19,13 +19,13 @@ </annotations> <before> - <actionGroup ref="LoginAsAdmin" stepKey="loginToAdminPanel"/> + <actionGroup ref="AdminLoginActionGroup" stepKey="loginToAdminPanel"/> </before> <after> <actionGroup ref="DeleteCustomerByEmailActionGroup" stepKey="deleteCustomer"> <argument name="email" value="{{CustomerEntityOne.email}}"/> </actionGroup> - <actionGroup ref="logout" stepKey="logout"/> + <actionGroup ref="AdminLogoutActionGroup" stepKey="logout"/> </after> <!--Filter the customer From grid--> diff --git a/app/code/Magento/Customer/Test/Mftf/Test/AdminCreateCustomerTest.xml b/app/code/Magento/Customer/Test/Mftf/Test/AdminCreateCustomerTest.xml index a11fb9d0eaa8f..64e8520323184 100644 --- a/app/code/Magento/Customer/Test/Mftf/Test/AdminCreateCustomerTest.xml +++ b/app/code/Magento/Customer/Test/Mftf/Test/AdminCreateCustomerTest.xml @@ -14,7 +14,7 @@ <stories value="Create a Customer via the Admin"/> <title value="Admin should be able to create a customer"/> <description value="Admin should be able to create a customer"/> - <severity value="CRITICAL"/> + <severity value="BLOCKER"/> <testCaseId value="MAGETWO-72095"/> <group value="customer"/> <group value="create"/> @@ -23,10 +23,10 @@ <magentoCLI command="indexer:reindex customer_grid" stepKey="reindexCustomerGrid"/> </before> <after> - <actionGroup ref="logout" stepKey="adminLogout"/> + <actionGroup ref="AdminLogoutActionGroup" stepKey="adminLogout"/> </after> - <actionGroup ref="LoginAsAdmin" stepKey="loginAsAdmin1"/> + <actionGroup ref="AdminLoginActionGroup" stepKey="loginAsAdmin1"/> <amOnPage url="{{AdminCustomerPage.url}}" stepKey="navigateToCustomers"/> <waitForPageLoad stepKey="waitForLoad1"/> <click selector="{{AdminCustomerGridMainActionsSection.addNewCustomer}}" stepKey="clickCreateCustomer"/> diff --git a/app/code/Magento/Customer/Test/Mftf/Test/AdminCreateCustomerWithCountryPolandTest.xml b/app/code/Magento/Customer/Test/Mftf/Test/AdminCreateCustomerWithCountryPolandTest.xml index d2435a093046a..5f496e2c5fba3 100644 --- a/app/code/Magento/Customer/Test/Mftf/Test/AdminCreateCustomerWithCountryPolandTest.xml +++ b/app/code/Magento/Customer/Test/Mftf/Test/AdminCreateCustomerWithCountryPolandTest.xml @@ -19,12 +19,12 @@ </annotations> <before> - <actionGroup ref="LoginAsAdmin" stepKey="loginToAdminPanel"/> + <actionGroup ref="AdminLoginActionGroup" stepKey="loginToAdminPanel"/> <createData entity="Simple_Customer_Without_Address" stepKey="createCustomer"/> </before> <after> <deleteData createDataKey="createCustomer" stepKey="deleteCustomer"/> - <actionGroup ref="logout" stepKey="logout"/> + <actionGroup ref="AdminLogoutActionGroup" stepKey="logout"/> </after> <!--Filter the created customer From grid--> diff --git a/app/code/Magento/Customer/Test/Mftf/Test/AdminCreateCustomerWithCountryUSATest.xml b/app/code/Magento/Customer/Test/Mftf/Test/AdminCreateCustomerWithCountryUSATest.xml index a487571c43534..da2eed2006434 100644 --- a/app/code/Magento/Customer/Test/Mftf/Test/AdminCreateCustomerWithCountryUSATest.xml +++ b/app/code/Magento/Customer/Test/Mftf/Test/AdminCreateCustomerWithCountryUSATest.xml @@ -19,12 +19,12 @@ </annotations> <before> - <actionGroup ref="LoginAsAdmin" stepKey="loginToAdminPanel"/> + <actionGroup ref="AdminLoginActionGroup" stepKey="loginToAdminPanel"/> <createData entity="Simple_Customer_Without_Address" stepKey="createCustomer"/> </before> <after> <deleteData createDataKey="createCustomer" stepKey="deleteCustomer"/> - <actionGroup ref="logout" stepKey="logout"/> + <actionGroup ref="AdminLogoutActionGroup" stepKey="logout"/> </after> <!--Filter the customer From grid--> diff --git a/app/code/Magento/Customer/Test/Mftf/Test/AdminCreateCustomerWithCustomGroupTest.xml b/app/code/Magento/Customer/Test/Mftf/Test/AdminCreateCustomerWithCustomGroupTest.xml index 872da149ed0b2..52a2483096aaf 100644 --- a/app/code/Magento/Customer/Test/Mftf/Test/AdminCreateCustomerWithCustomGroupTest.xml +++ b/app/code/Magento/Customer/Test/Mftf/Test/AdminCreateCustomerWithCustomGroupTest.xml @@ -20,14 +20,14 @@ <before> <createData entity="CustomCustomerGroup" stepKey="customerGroup" /> - <actionGroup ref="LoginAsAdmin" stepKey="login"/> + <actionGroup ref="AdminLoginActionGroup" stepKey="login"/> </before> <after> <actionGroup ref="DeleteCustomerByEmailActionGroup" stepKey="deleteCustomer"> <argument name="email" value="{{CustomerEntityOne.email}}" /> </actionGroup> <deleteData createDataKey="customerGroup" stepKey="deleteCustomerGroup"/> - <actionGroup ref="logout" stepKey="logout"/> + <actionGroup ref="AdminLogoutActionGroup" stepKey="logout"/> </after> <!--Open New Customer Page --> diff --git a/app/code/Magento/Customer/Test/Mftf/Test/AdminCreateCustomerWithPrefixTest.xml b/app/code/Magento/Customer/Test/Mftf/Test/AdminCreateCustomerWithPrefixTest.xml index 1b901a7b3e1cd..591cb2dd2845a 100644 --- a/app/code/Magento/Customer/Test/Mftf/Test/AdminCreateCustomerWithPrefixTest.xml +++ b/app/code/Magento/Customer/Test/Mftf/Test/AdminCreateCustomerWithPrefixTest.xml @@ -19,13 +19,13 @@ </annotations> <before> - <actionGroup ref="LoginAsAdmin" stepKey="loginToAdminPanel"/> + <actionGroup ref="AdminLoginActionGroup" stepKey="loginToAdminPanel"/> </before> <after> <actionGroup ref="DeleteCustomerByEmailActionGroup" stepKey="deleteCustomer"> <argument name="email" value="{{CustomerEntityOne.email}}"/> </actionGroup> - <actionGroup ref="logout" stepKey="logout"/> + <actionGroup ref="AdminLogoutActionGroup" stepKey="logout"/> </after> <!--Open New Customer Page and create a customer with Prefix and Suffix--> diff --git a/app/code/Magento/Customer/Test/Mftf/Test/AdminCreateCustomerWithoutAddressTest.xml b/app/code/Magento/Customer/Test/Mftf/Test/AdminCreateCustomerWithoutAddressTest.xml index fe4bb3ee59e6e..081695f7ebe1e 100644 --- a/app/code/Magento/Customer/Test/Mftf/Test/AdminCreateCustomerWithoutAddressTest.xml +++ b/app/code/Magento/Customer/Test/Mftf/Test/AdminCreateCustomerWithoutAddressTest.xml @@ -19,13 +19,13 @@ </annotations> <before> - <actionGroup ref="LoginAsAdmin" stepKey="loginToAdminPanel"/> + <actionGroup ref="AdminLoginActionGroup" stepKey="loginToAdminPanel"/> </before> <after> <actionGroup ref="DeleteCustomerByEmailActionGroup" stepKey="deleteCustomer"> <argument name="email" value="{{CustomerEntityOne.email}}"/> </actionGroup> - <actionGroup ref="logout" stepKey="logout"/> + <actionGroup ref="AdminLogoutActionGroup" stepKey="logout"/> </after> <!--Open New Customer Page --> diff --git a/app/code/Magento/Customer/Test/Mftf/Test/AdminCreateNewCustomerOnStorefrontSignupNewsletterTest.xml b/app/code/Magento/Customer/Test/Mftf/Test/AdminCreateNewCustomerOnStorefrontSignupNewsletterTest.xml index 5d09f819bcbc0..5440339e3a95e 100644 --- a/app/code/Magento/Customer/Test/Mftf/Test/AdminCreateNewCustomerOnStorefrontSignupNewsletterTest.xml +++ b/app/code/Magento/Customer/Test/Mftf/Test/AdminCreateNewCustomerOnStorefrontSignupNewsletterTest.xml @@ -20,13 +20,13 @@ </annotations> <before> - <actionGroup ref="LoginAsAdmin" stepKey="loginAsAdmin"/> + <actionGroup ref="AdminLoginActionGroup" stepKey="loginAsAdmin"/> </before> <after> <actionGroup ref="DeleteCustomerByEmailActionGroup" stepKey="deleteNewUser"> <argument name="email" value="{{CustomerEntityOne.email}}"/> </actionGroup> - <actionGroup ref="logout" stepKey="logout"/> + <actionGroup ref="AdminLogoutActionGroup" stepKey="logout"/> </after> <!--Create new customer on storefront and signup news letter--> @@ -51,4 +51,4 @@ <waitForPageLoad stepKey="waitForNewsletterTabToOpen"/> <seeCheckboxIsChecked selector="{{AdminEditCustomerNewsletterSection.subscribedStatus('1')}}" stepKey="seeAssertSubscribedToNewsletterCheckboxIsChecked"/> </test> -</tests> \ No newline at end of file +</tests> diff --git a/app/code/Magento/Customer/Test/Mftf/Test/AdminCreateNewCustomerOnStorefrontTest.xml b/app/code/Magento/Customer/Test/Mftf/Test/AdminCreateNewCustomerOnStorefrontTest.xml index fc65a271a8196..444e9aa643181 100644 --- a/app/code/Magento/Customer/Test/Mftf/Test/AdminCreateNewCustomerOnStorefrontTest.xml +++ b/app/code/Magento/Customer/Test/Mftf/Test/AdminCreateNewCustomerOnStorefrontTest.xml @@ -20,21 +20,27 @@ </annotations> <before> - <actionGroup ref="LoginAsAdmin" stepKey="loginAsAdmin"/> + <actionGroup ref="AdminLoginActionGroup" stepKey="loginAsAdmin"/> </before> <after> <actionGroup ref="DeleteCustomerByEmailActionGroup" stepKey="deleteNewUser"> <argument name="email" value="{{CustomerEntityOne.email}}"/> </actionGroup> - <actionGroup ref="logout" stepKey="logout"/> + <actionGroup ref="AdminLogoutActionGroup" stepKey="logout"/> </after> <!--Create new customer on storefront and perform the asserts--> - <actionGroup ref="SignUpNewUserFromStorefrontActionGroup" stepKey="signUpNewUser"> + <actionGroup ref="StorefrontOpenCustomerAccountCreatePageActionGroup" stepKey="openCreateAccountPage"/> + <actionGroup ref="StorefrontFillCustomerAccountCreationFormActionGroup" stepKey="fillCreateAccountForm"> <argument name="customer" value="CustomerEntityOne"/> </actionGroup> + <actionGroup ref="StorefrontClickCreateAnAccountCustomerAccountCreationFormActionGroup" stepKey="submitCreateAccountForm"/> + <actionGroup ref="AssertMessageCustomerCreateAccountActionGroup" stepKey="seeSuccessMessage"> + <argument name="messageType" value="success"/> + <argument name="message" value="Thank you for registering with Main Website Store."/> + </actionGroup> <actionGroup ref="AssertSignedUpNewsletterActionGroup" stepKey="assertSignedUpNewsLetter"> <argument name="customer" value="CustomerEntityOne"/> </actionGroup> </test> -</tests> \ No newline at end of file +</tests> diff --git a/app/code/Magento/Customer/Test/Mftf/Test/AdminCreateNewCustomerTest.xml b/app/code/Magento/Customer/Test/Mftf/Test/AdminCreateNewCustomerTest.xml index de4ab9ffaa121..da25139ee8e60 100644 --- a/app/code/Magento/Customer/Test/Mftf/Test/AdminCreateNewCustomerTest.xml +++ b/app/code/Magento/Customer/Test/Mftf/Test/AdminCreateNewCustomerTest.xml @@ -19,13 +19,13 @@ </annotations> <before> - <actionGroup ref="LoginAsAdmin" stepKey="loginToAdminPanel"/> + <actionGroup ref="AdminLoginActionGroup" stepKey="loginToAdminPanel"/> </before> <after> <actionGroup ref="DeleteCustomerByEmailActionGroup" stepKey="deleteCustomer"> <argument name="email" value="{{CustomerEntityOne.email}}"/> </actionGroup> - <actionGroup ref="logout" stepKey="logout"/> + <actionGroup ref="AdminLogoutActionGroup" stepKey="logout"/> </after> <!--Open New Customer Page --> diff --git a/app/code/Magento/Customer/Test/Mftf/Test/AdminCreateRetailCustomerGroupTest.xml b/app/code/Magento/Customer/Test/Mftf/Test/AdminCreateRetailCustomerGroupTest.xml index 6e08d98a53c56..e8198cb79262e 100644 --- a/app/code/Magento/Customer/Test/Mftf/Test/AdminCreateRetailCustomerGroupTest.xml +++ b/app/code/Magento/Customer/Test/Mftf/Test/AdminCreateRetailCustomerGroupTest.xml @@ -20,14 +20,14 @@ <group value="mtf_migrated"/> </annotations> <before> - <actionGroup ref="LoginAsAdmin" stepKey="loginAsAdmin"/> + <actionGroup ref="AdminLoginActionGroup" stepKey="loginAsAdmin"/> </before> <after> <actionGroup ref="AdminDeleteCustomerGroupActionGroup" stepKey="deleteCustomerGroup"> <argument name="customerGroupName" value="{{CustomCustomerGroup.code}}"/> </actionGroup> <actionGroup ref="ClearFiltersAdminDataGridActionGroup" stepKey="clearFilters"/> - <actionGroup ref="logout" stepKey="logout"/> + <actionGroup ref="AdminLogoutActionGroup" stepKey="logout"/> </after> <!-- Steps: 1. Log in to backend as admin user. 2. Navigate to Stores > Other Settings > Customer Groups. diff --git a/app/code/Magento/Customer/Test/Mftf/Test/AdminCreateTaxClassCustomerGroupTest.xml b/app/code/Magento/Customer/Test/Mftf/Test/AdminCreateTaxClassCustomerGroupTest.xml index 4b539ec350435..3416c64a7e9d7 100644 --- a/app/code/Magento/Customer/Test/Mftf/Test/AdminCreateTaxClassCustomerGroupTest.xml +++ b/app/code/Magento/Customer/Test/Mftf/Test/AdminCreateTaxClassCustomerGroupTest.xml @@ -25,7 +25,7 @@ <getData entity="customerTaxClass" stepKey="customerTaxClassData"> <requiredEntity createDataKey="createCustomerTaxClass"/> </getData> - <actionGroup ref="LoginAsAdmin" stepKey="loginAsAdmin"/> + <actionGroup ref="AdminLoginActionGroup" stepKey="loginAsAdmin"/> </before> <after> <actionGroup ref="AdminDeleteCustomerGroupActionGroup" stepKey="deleteCustomerGroup"> @@ -33,7 +33,7 @@ </actionGroup> <actionGroup ref="ClearFiltersAdminDataGridActionGroup" stepKey="clearFilters"/> <deleteData createDataKey="createCustomerTaxClass" stepKey="deleteCustomerTaxClass"/> - <actionGroup ref="logout" stepKey="logout"/> + <actionGroup ref="AdminLogoutActionGroup" stepKey="logout"/> </after> <!-- Steps: 1. Log in to backend as admin user. 2. Navigate to Stores > Other Settings > Customer Groups. diff --git a/app/code/Magento/Customer/Test/Mftf/Test/AdminCustomerSubscribeNewsletterPerWebsiteTest.xml b/app/code/Magento/Customer/Test/Mftf/Test/AdminCustomerSubscribeNewsletterPerWebsiteTest.xml index 6c1a27c395917..a8391458a1a50 100644 --- a/app/code/Magento/Customer/Test/Mftf/Test/AdminCustomerSubscribeNewsletterPerWebsiteTest.xml +++ b/app/code/Magento/Customer/Test/Mftf/Test/AdminCustomerSubscribeNewsletterPerWebsiteTest.xml @@ -20,7 +20,7 @@ </annotations> <before> <createData entity="CustomerAccountSharingGlobal" stepKey="setConfigCustomerAccountToGlobal"/> - <actionGroup ref="LoginAsAdmin" stepKey="loginAsAdmin"/> + <actionGroup ref="AdminLoginActionGroup" stepKey="loginAsAdmin"/> </before> <after> <actionGroup ref="AdminDeleteStoreViewActionGroup" stepKey="deleteStoreView"> @@ -32,7 +32,7 @@ <actionGroup ref="AdminDeleteWebsiteActionGroup" stepKey="deleteWebsite"> <argument name="websiteName" value="{{secondCustomWebsite.name}}"/> </actionGroup> - <actionGroup ref="logout" stepKey="logout"/> + <actionGroup ref="AdminLogoutActionGroup" stepKey="logout"/> <createData entity="CustomerAccountSharingDefault" stepKey="setConfigCustomerAccountDefault"/> </after> @@ -41,8 +41,7 @@ <argument name="customStore" value="NewStoreViewData"/> </actionGroup> <!-- Switch to the new Store View on storefront --> - <amOnPage url="{{StorefrontHomePage.url}}" stepKey="amOnHomePage"/> - <waitForPageLoad stepKey="waitForNavigateHomePage"/> + <actionGroup ref="StorefrontOpenHomePageActionGroup" stepKey="amOnHomePage"/> <actionGroup ref="StorefrontSwitchStoreViewActionGroup" stepKey="switchToCustomStoreView"> <argument name="storeView" value="NewStoreViewData"/> </actionGroup> @@ -98,4 +97,4 @@ <argument name="storeView" value="SecondStoreUnique"/> </actionGroup> </test> -</tests> \ No newline at end of file +</tests> diff --git a/app/code/Magento/Customer/Test/Mftf/Test/AdminCustomersAllCustomersNavigateMenuTest.xml b/app/code/Magento/Customer/Test/Mftf/Test/AdminCustomersAllCustomersNavigateMenuTest.xml index 76e4407675e4c..4850a6faf438a 100644 --- a/app/code/Magento/Customer/Test/Mftf/Test/AdminCustomersAllCustomersNavigateMenuTest.xml +++ b/app/code/Magento/Customer/Test/Mftf/Test/AdminCustomersAllCustomersNavigateMenuTest.xml @@ -20,10 +20,10 @@ <group value="mtf_migrated"/> </annotations> <before> - <actionGroup ref="LoginAsAdmin" stepKey="LoginAsAdmin"/> + <actionGroup ref="AdminLoginActionGroup" stepKey="LoginAsAdmin"/> </before> <after> - <actionGroup ref="logout" stepKey="logout"/> + <actionGroup ref="AdminLogoutActionGroup" stepKey="logout"/> </after> <actionGroup ref="AdminNavigateMenuActionGroup" stepKey="navigateToAllCustomerPage"> <argument name="menuUiId" value="{{AdminMenuCustomers.dataUiId}}"/> diff --git a/app/code/Magento/Customer/Test/Mftf/Test/AdminCustomersCustomerGroupsNavigateMenuTest.xml b/app/code/Magento/Customer/Test/Mftf/Test/AdminCustomersCustomerGroupsNavigateMenuTest.xml index 13a4b1c714337..bc0c3e00d75aa 100644 --- a/app/code/Magento/Customer/Test/Mftf/Test/AdminCustomersCustomerGroupsNavigateMenuTest.xml +++ b/app/code/Magento/Customer/Test/Mftf/Test/AdminCustomersCustomerGroupsNavigateMenuTest.xml @@ -20,10 +20,10 @@ <group value="mtf_migrated"/> </annotations> <before> - <actionGroup ref="LoginAsAdmin" stepKey="LoginAsAdmin"/> + <actionGroup ref="AdminLoginActionGroup" stepKey="LoginAsAdmin"/> </before> <after> - <actionGroup ref="logout" stepKey="logout"/> + <actionGroup ref="AdminLogoutActionGroup" stepKey="logout"/> </after> <actionGroup ref="AdminNavigateMenuActionGroup" stepKey="navigateToCustomerGroupsPage"> <argument name="menuUiId" value="{{AdminMenuCustomers.dataUiId}}"/> diff --git a/app/code/Magento/Customer/Test/Mftf/Test/AdminCustomersDeleteSystemCustomerGroupTest.xml b/app/code/Magento/Customer/Test/Mftf/Test/AdminCustomersDeleteSystemCustomerGroupTest.xml new file mode 100644 index 0000000000000..124f086044f76 --- /dev/null +++ b/app/code/Magento/Customer/Test/Mftf/Test/AdminCustomersDeleteSystemCustomerGroupTest.xml @@ -0,0 +1,49 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + /** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +--> +<tests xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/testSchema.xsd"> + <test name="AdminCustomersDeleteSystemCustomerGroupTest"> + <annotations> + <features value="Customer"/> + <title value="System Customer Groups"/> + <description value="Admin should not be able to delete system customer groups"/> + <stories value="Delete System Customer Group" /> + <testCaseId value="MC-14588" /> + <group value="customers"/> + <group value="mtf_migrated"/> + </annotations> + <before> + <actionGroup ref="AdminLoginActionGroup" stepKey="loginAsAdmin"/> + </before> + <after> + <actionGroup ref="AdminLogoutActionGroup" stepKey="logout"/> + </after> + + <!--Verify Not Logged In customer group--> + <!--Go to Customer Group grid page--> + <actionGroup ref="AdminOpenCustomerGroupsGridPageActionGroup" stepKey="openCustomerGroupGridPageToCheckNotLoggedInGroup"/> + <actionGroup ref="AdminFilterCustomerGroupByNameActionGroup" stepKey="filterCustomerGroupsByNotLoggedInGroup"> + <argument name="customerGroupName" value="{{NotLoggedInCustomerGroup.code}}"/> + </actionGroup> + <actionGroup ref="AdminOpenCustomerGroupEditPageFromGridActionGroup" stepKey="openNotLoggedInCustomerGroupEditPage"> + <argument name="groupCode" value="{{NotLoggedInCustomerGroup.code}}"/> + </actionGroup> + <actionGroup ref="AssertDeleteCustomerGroupButtonMissingActionGroup" stepKey="verifyThereIsNoDeleteButtonForNotLoggedInGroup"/> + + <!--Verify General customer group--> + <!--Go to Customer Group grid page--> + <actionGroup ref="AdminOpenCustomerGroupsGridPageActionGroup" stepKey="openCustomerGroupGridPageToCheckGeneralGroup"/> + <actionGroup ref="AdminFilterCustomerGroupByNameActionGroup" stepKey="filterCustomerGroupsByGeneralGroup"> + <argument name="customerGroupName" value="{{GeneralCustomerGroup.code}}"/> + </actionGroup> + <actionGroup ref="AdminOpenCustomerGroupEditPageFromGridActionGroup" stepKey="openGeneralCustomerGroupEditPage"> + <argument name="groupCode" value="{{GeneralCustomerGroup.code}}"/> + </actionGroup> + <actionGroup ref="AssertDeleteCustomerGroupButtonMissingActionGroup" stepKey="verifyThereIsNoDeleteButtonForGeneralGroup"/> + </test> +</tests> \ No newline at end of file diff --git a/app/code/Magento/Customer/Test/Mftf/Test/AdminCustomersNowOnlineNavigateMenuTest.xml b/app/code/Magento/Customer/Test/Mftf/Test/AdminCustomersNowOnlineNavigateMenuTest.xml index e9eb7803e01ea..8d5535a48f8a3 100644 --- a/app/code/Magento/Customer/Test/Mftf/Test/AdminCustomersNowOnlineNavigateMenuTest.xml +++ b/app/code/Magento/Customer/Test/Mftf/Test/AdminCustomersNowOnlineNavigateMenuTest.xml @@ -20,10 +20,10 @@ <group value="mtf_migrated"/> </annotations> <before> - <actionGroup ref="LoginAsAdmin" stepKey="LoginAsAdmin"/> + <actionGroup ref="AdminLoginActionGroup" stepKey="LoginAsAdmin"/> </before> <after> - <actionGroup ref="logout" stepKey="logout"/> + <actionGroup ref="AdminLogoutActionGroup" stepKey="logout"/> </after> <actionGroup ref="AdminNavigateMenuActionGroup" stepKey="navigateToNowOnlinePage"> <argument name="menuUiId" value="{{AdminMenuCustomers.dataUiId}}"/> diff --git a/app/code/Magento/Customer/Test/Mftf/Test/AdminDeleteCustomerAddressesFromTheGridTest.xml b/app/code/Magento/Customer/Test/Mftf/Test/AdminDeleteCustomerAddressesFromTheGridTest.xml index 4f501c27352bf..8494a94f0c122 100644 --- a/app/code/Magento/Customer/Test/Mftf/Test/AdminDeleteCustomerAddressesFromTheGridTest.xml +++ b/app/code/Magento/Customer/Test/Mftf/Test/AdminDeleteCustomerAddressesFromTheGridTest.xml @@ -12,7 +12,7 @@ <title value="Admin delete customer addresses from the grid"/> <description value="Admin delete customer addresses from the grid"/> <features value="Module/ Customer"/> - <severity value="MAJOR"/> + <severity value="BLOCKER"/> <testCaseId value="MAGETWO-94850"/> <stories value="MAGETWO-94346: Implement handling of large number of addresses on admin edit customer page"/> <group value="customer"/> @@ -21,11 +21,11 @@ <before> <createData entity="Simple_US_Customer_Multiple_Addresses" stepKey="createCustomer"/> <magentoCLI command="indexer:reindex" stepKey="reindex"/> - <actionGroup ref="LoginAsAdmin" stepKey="login"/> + <actionGroup ref="AdminLoginActionGroup" stepKey="login"/> </before> <after> <deleteData createDataKey="createCustomer" stepKey="deleteCustomer"/> - <actionGroup ref="logout" stepKey="logout"/> + <actionGroup ref="AdminLogoutActionGroup" stepKey="logout"/> </after> <!-- - Step1. Login to admin and go to Customers > All Customerts. diff --git a/app/code/Magento/Customer/Test/Mftf/Test/AdminDeleteCustomerAddressesFromTheGridViaMassActionsTest.xml b/app/code/Magento/Customer/Test/Mftf/Test/AdminDeleteCustomerAddressesFromTheGridViaMassActionsTest.xml index a703c5a7c5d92..340295df04da2 100644 --- a/app/code/Magento/Customer/Test/Mftf/Test/AdminDeleteCustomerAddressesFromTheGridViaMassActionsTest.xml +++ b/app/code/Magento/Customer/Test/Mftf/Test/AdminDeleteCustomerAddressesFromTheGridViaMassActionsTest.xml @@ -21,11 +21,11 @@ <before> <createData entity="Simple_US_Customer_Multiple_Addresses" stepKey="createCustomer"/> <magentoCLI command="indexer:reindex" stepKey="reindex"/> - <actionGroup ref="LoginAsAdmin" stepKey="login"/> + <actionGroup ref="AdminLoginActionGroup" stepKey="login"/> </before> <after> <deleteData createDataKey="createCustomer" stepKey="deleteCustomer"/> - <actionGroup ref="logout" stepKey="logout"/> + <actionGroup ref="AdminLogoutActionGroup" stepKey="logout"/> </after> <!-- - Step1. Login to admin and go to Customers > All Customerts. diff --git a/app/code/Magento/Customer/Test/Mftf/Test/AdminDeleteCustomerTest.xml b/app/code/Magento/Customer/Test/Mftf/Test/AdminDeleteCustomerTest.xml index 7fef916fc458a..03691eb7e7b72 100644 --- a/app/code/Magento/Customer/Test/Mftf/Test/AdminDeleteCustomerTest.xml +++ b/app/code/Magento/Customer/Test/Mftf/Test/AdminDeleteCustomerTest.xml @@ -13,7 +13,7 @@ <stories value="Delete customer"/> <title value="DeleteCustomerBackendEntityTestVariation1"/> <description value="Login as admin and delete the customer"/> - <severity value="CRITICAL"/> + <severity value="BLOCKER"/> <testCaseId value="MC-14587"/> <group value="mtf_migrated"/> </annotations> @@ -22,10 +22,10 @@ <!-- Create Customer --> <createData entity="CustomerEntityOne" stepKey="createCustomer"/> <!-- Login as admin --> - <actionGroup ref="LoginAsAdmin" stepKey="login"/> + <actionGroup ref="AdminLoginActionGroup" stepKey="login"/> </before> <after> - <actionGroup ref="logout" stepKey="logout"/> + <actionGroup ref="AdminLogoutActionGroup" stepKey="logout"/> </after> <!-- Delete created customer --> diff --git a/app/code/Magento/Customer/Test/Mftf/Test/AdminDeleteDefaultBillingCustomerAddressTest.xml b/app/code/Magento/Customer/Test/Mftf/Test/AdminDeleteDefaultBillingCustomerAddressTest.xml index bb455677d5e94..1630743da4922 100644 --- a/app/code/Magento/Customer/Test/Mftf/Test/AdminDeleteDefaultBillingCustomerAddressTest.xml +++ b/app/code/Magento/Customer/Test/Mftf/Test/AdminDeleteDefaultBillingCustomerAddressTest.xml @@ -12,7 +12,7 @@ <title value="Admin delete default billing customer address"/> <description value="Admin delete default billing customer address"/> <features value="Module/ Customer"/> - <severity value="MAJOR"/> + <severity value="BLOCKER"/> <testCaseId value="MAGETWO-94816"/> <stories value="MAGETWO-94346: Implement handling of large number of addresses on admin edit customer page"/> <group value="customer"/> @@ -21,11 +21,11 @@ <before> <createData entity="Simple_US_Customer_Multiple_Addresses" stepKey="createCustomer"/> <magentoCLI command="indexer:reindex" stepKey="reindex"/> - <actionGroup ref="LoginAsAdmin" stepKey="login"/> + <actionGroup ref="AdminLoginActionGroup" stepKey="login"/> </before> <after> <deleteData createDataKey="createCustomer" stepKey="deleteCustomer"/> - <actionGroup ref="logout" stepKey="logout"/> + <actionGroup ref="AdminLogoutActionGroup" stepKey="logout"/> </after> <!-- - Step1. Login to admin and go to Customers > All Customers. diff --git a/app/code/Magento/Customer/Test/Mftf/Test/AdminEditDefaultBillingShippingCustomerAddressTest.xml b/app/code/Magento/Customer/Test/Mftf/Test/AdminEditDefaultBillingShippingCustomerAddressTest.xml index df317c8bf7012..6e44fe96b0d7b 100644 --- a/app/code/Magento/Customer/Test/Mftf/Test/AdminEditDefaultBillingShippingCustomerAddressTest.xml +++ b/app/code/Magento/Customer/Test/Mftf/Test/AdminEditDefaultBillingShippingCustomerAddressTest.xml @@ -13,17 +13,17 @@ <stories value="Edit default billing/shipping customer address"/> <title value="Edit default billing/shipping customer address"/> <description value="Edit default billing/shipping customer address on customer addresses tab"/> - <severity value="CRITICAL"/> + <severity value="BLOCKER"/> <testCaseId value="MAGETWO-94815"/> <group value="customer"/> </annotations> <before> <createData entity="Simple_US_Customer_Multiple_Addresses" stepKey="customer"/> - <actionGroup ref="LoginAsAdmin" stepKey="login"/> + <actionGroup ref="AdminLoginActionGroup" stepKey="login"/> </before> <after> <deleteData createDataKey="customer" stepKey="deleteCustomer"/> - <actionGroup ref="logout" stepKey="logout"/> + <actionGroup ref="AdminLogoutActionGroup" stepKey="logout"/> </after> <!-- - Step1. Login to admin and go to Customers > All Customers. @@ -63,7 +63,11 @@ <see userInput="{{US_Address_TX.street[0]}}" selector="{{AdminCustomerAddressesDefaultShippingSection.addressDetails}}" stepKey="assertDefaultShippingAddressIsChanged"/> <click selector="{{AdminCustomerAddressesDefaultBillingSection.editButton}}" stepKey="clickEditDefaultBillingAddress"/> <waitForPageLoad stepKey="waitForCustomerAddressAddUpdateFormLoad"/> - <assertElementContainsAttribute selector="{{AdminCustomerAddressesSection.defaultBillingAddressCheckBox}}" attribute="value" expectedValue="1" stepKey="assertDefaultBillingIsEnabledCustomerAddressAddUpdateForm"/> - <assertElementContainsAttribute selector="{{AdminCustomerAddressesSection.defaultShippingAddressCheckBox}}" attribute="value" expectedValue="1" stepKey="assertDefaultShippingIsEnabledOnCustomerAddressAddUpdateForm"/> + <assertElementContainsAttribute stepKey="assertDefaultBillingIsEnabledCustomerAddressAddUpdateForm"> + <expectedResult selector="{{AdminCustomerAddressesSection.defaultBillingAddressCheckBox}}" attribute="value" type="string">1</expectedResult> + </assertElementContainsAttribute> + <assertElementContainsAttribute stepKey="assertDefaultShippingIsEnabledOnCustomerAddressAddUpdateForm"> + <expectedResult selector="{{AdminCustomerAddressesSection.defaultShippingAddressCheckBox}}" attribute="value" type="string">1</expectedResult> + </assertElementContainsAttribute> </test> </tests> diff --git a/app/code/Magento/Customer/Test/Mftf/Test/AdminExactMatchSearchInCustomerGridTest.xml b/app/code/Magento/Customer/Test/Mftf/Test/AdminExactMatchSearchInCustomerGridTest.xml index d278b6c52d330..ea4b3645d371f 100644 --- a/app/code/Magento/Customer/Test/Mftf/Test/AdminExactMatchSearchInCustomerGridTest.xml +++ b/app/code/Magento/Customer/Test/Mftf/Test/AdminExactMatchSearchInCustomerGridTest.xml @@ -23,14 +23,14 @@ <createData entity="Simple_US_Customer" stepKey="createSecondCustomer"> <field key="firstname">"Jane Doe"</field> </createData> - <actionGroup ref="LoginAsAdmin" stepKey="login"/> + <actionGroup ref="AdminLoginActionGroup" stepKey="login"/> </before> <after> <deleteData createDataKey="createFirstCustomer" stepKey="deleteFirstCustomer"/> <deleteData createDataKey="createSecondCustomer" stepKey="deleteSecondCustomer"/> <amOnPage url="{{AdminCustomerPage.url}}" stepKey="openCustomersGridPage"/> <actionGroup ref="AdminResetFilterInCustomerAddressGrid" stepKey="clearCustomerGridFilter"/> - <actionGroup ref="logout" stepKey="logout"/> + <actionGroup ref="AdminLogoutActionGroup" stepKey="logout"/> </after> <!--Step 1: Go to Customers > All Customers--> <amOnPage url="{{AdminCustomerPage.url}}" stepKey="openCustomersGridPage"/> diff --git a/app/code/Magento/Customer/Test/Mftf/Test/AdminPanelIsFrozenIfStorefrontIsOpenedViaCustomerViewTest.xml b/app/code/Magento/Customer/Test/Mftf/Test/AdminPanelIsFrozenIfStorefrontIsOpenedViaCustomerViewTest.xml index ee32214435428..77422c6e8da3f 100644 --- a/app/code/Magento/Customer/Test/Mftf/Test/AdminPanelIsFrozenIfStorefrontIsOpenedViaCustomerViewTest.xml +++ b/app/code/Magento/Customer/Test/Mftf/Test/AdminPanelIsFrozenIfStorefrontIsOpenedViaCustomerViewTest.xml @@ -23,12 +23,12 @@ <createData entity="SimpleProduct" stepKey="createSimpleProduct"> <requiredEntity createDataKey="createSimpleCategory"/> </createData> - <actionGroup ref="LoginAsAdmin" stepKey="login"/> + <actionGroup ref="AdminLoginActionGroup" stepKey="login"/> </before> <after> <deleteData createDataKey="createSimpleCategory" stepKey="deleteCategory"/> <deleteData createDataKey="createSimpleProduct" stepKey="deleteSimpleProduct"/> - <actionGroup ref="logout" stepKey="logout"/> + <actionGroup ref="AdminLogoutActionGroup" stepKey="logout"/> </after> <actionGroup ref="NavigateToNewOrderPageExistingCustomerActionGroup" stepKey="navigateToNewOrderPage"> <argument name="customer" value="$simpleCustomer$"/> @@ -46,8 +46,8 @@ <actionGroup ref="StartCreateInvoiceFromOrderPageActionGroup" stepKey="startCreateInvoice"/> <actionGroup ref="SubmitInvoiceActionGroup" stepKey="submitInvoice"/> - <actionGroup ref="goToShipmentIntoOrder" stepKey="goToShipment"/> - <actionGroup ref="submitShipmentIntoOrder" stepKey="submitShipment"/> + <actionGroup ref="GoToShipmentIntoOrderActionGroup" stepKey="goToShipment"/> + <actionGroup ref="SubmitShipmentIntoOrderActionGroup" stepKey="submitShipment"/> <!--Create Credit Memo--> <actionGroup ref="StartToCreateCreditMemoActionGroup" stepKey="startToCreateCreditMemo"> diff --git a/app/code/Magento/Customer/Test/Mftf/Test/AdminProductBackRedirectNavigateFromCustomerViewCartProduct.xml b/app/code/Magento/Customer/Test/Mftf/Test/AdminProductBackRedirectNavigateFromCustomerViewCartProduct.xml deleted file mode 100644 index 18106836ce137..0000000000000 --- a/app/code/Magento/Customer/Test/Mftf/Test/AdminProductBackRedirectNavigateFromCustomerViewCartProduct.xml +++ /dev/null @@ -1,75 +0,0 @@ -<?xml version="1.0" encoding="UTF-8"?> -<!-- - /** - * Copyright © Magento, Inc. All rights reserved. - * See COPYING.txt for license details. - */ ---> - -<tests xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" - xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/testSchema.xsd"> - <test name="AdminProductBackRedirectNavigateFromCustomerViewCartProduct"> - <annotations> - <features value="Customer"/> - <stories value="Product Back Button"/> - <title value="Product back redirect navigate from customer view cart product"/> - <description value="Back button on product page is redirecting to customer page if opened form shopping cart"/> - <severity value="MINOR"/> - <group value="Customer"/> - </annotations> - <before> - <!-- Create new product--> - <createData entity="_defaultCategory" stepKey="createCategory"/> - <createData entity="_defaultProduct" stepKey="createProduct"> - <requiredEntity createDataKey="createCategory"/> - </createData> - - <!-- Create new customer--> - <createData entity="Simple_US_Customer" stepKey="createCustomer"/> - - <!-- Login as admin--> - <actionGroup ref="LoginAsAdmin" stepKey="loginAsAdmin"/> - </before> - - <!-- Go to storefront as customer--> - <actionGroup ref="LoginToStorefrontActionGroup" stepKey="customerLogin"> - <argument name="Customer" value="$$createCustomer$$"/> - </actionGroup> - - <!-- Add product to cart --> - <actionGroup ref="AddSimpleProductToCartActionGroup" stepKey="addProductToCart"> - <argument name="product" value="$$createProduct$$"/> - </actionGroup> - - <!-- Navigate to customer edit page in admin --> - <amOnPage url="{{AdminCustomerPage.url}}edit/id/$$createCustomer.id$$/" stepKey="openCustomerEditPage"/> - <waitForPageLoad stepKey="waitForCustomerEditPage"/> - - <!-- Open shopping cart --> - <click selector="{{AdminCustomerInformationSection.shoppingCart}}" stepKey="clickShoppingCartButton"/> - <waitForPageLoad stepKey="waitForPageLoaded"/> - - <!-- Open product --> - <click selector="{{AdminCustomerCartSection.cartItem('1')}}" stepKey="openProduct"/> - - <!-- Go back to customer page --> - <click selector="{{AdminProductFormActionSection.backButton}}" stepKey="goBackToCustomerPage"/> - - <!-- Check current page is customer page --> - <seeInCurrentUrl stepKey="onCustomerAccountPage" url="{{AdminCustomerPage.url}}edit/id/$$createCustomer.id$$/"/> - - <after> - <!--Delete product--> - <deleteData stepKey="deleteProduct" createDataKey="createProduct"/> - - <!--Delete category--> - <deleteData stepKey="deleteCategory" createDataKey="createCategory"/> - - <!--Delete customer--> - <deleteData stepKey="deleteCustomer" createDataKey="createCustomer"/> - - <!-- Sign out--> - <actionGroup ref="SignOut" stepKey="signOut"/> - </after> - </test> -</tests> diff --git a/app/code/Magento/Customer/Test/Mftf/Test/AdminProductBackRedirectNavigateFromCustomerViewCartProductTest.xml b/app/code/Magento/Customer/Test/Mftf/Test/AdminProductBackRedirectNavigateFromCustomerViewCartProductTest.xml new file mode 100644 index 0000000000000..4c4175bb32198 --- /dev/null +++ b/app/code/Magento/Customer/Test/Mftf/Test/AdminProductBackRedirectNavigateFromCustomerViewCartProductTest.xml @@ -0,0 +1,75 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + /** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +--> + +<tests xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/testSchema.xsd"> + <test name="AdminProductBackRedirectNavigateFromCustomerViewCartProductTest"> + <annotations> + <features value="Customer"/> + <stories value="Product Back Button"/> + <title value="Product back redirect navigate from customer view cart product"/> + <description value="Back button on product page is redirecting to customer page if opened form shopping cart"/> + <severity value="MINOR"/> + <group value="Customer"/> + </annotations> + <before> + <!-- Create new product--> + <createData entity="_defaultCategory" stepKey="createCategory"/> + <createData entity="_defaultProduct" stepKey="createProduct"> + <requiredEntity createDataKey="createCategory"/> + </createData> + + <!-- Create new customer--> + <createData entity="Simple_US_Customer" stepKey="createCustomer"/> + + <!-- Login as admin--> + <actionGroup ref="AdminLoginActionGroup" stepKey="loginAsAdmin"/> + </before> + + <!-- Go to storefront as customer--> + <actionGroup ref="LoginToStorefrontActionGroup" stepKey="customerLogin"> + <argument name="Customer" value="$$createCustomer$$"/> + </actionGroup> + + <!-- Add product to cart --> + <actionGroup ref="AddSimpleProductToCartActionGroup" stepKey="addProductToCart"> + <argument name="product" value="$$createProduct$$"/> + </actionGroup> + + <!-- Navigate to customer edit page in admin --> + <amOnPage url="{{AdminCustomerPage.url}}edit/id/$$createCustomer.id$$/" stepKey="openCustomerEditPage"/> + <waitForPageLoad stepKey="waitForCustomerEditPage"/> + + <!-- Open shopping cart --> + <click selector="{{AdminCustomerInformationSection.shoppingCart}}" stepKey="clickShoppingCartButton"/> + <waitForPageLoad stepKey="waitForPageLoaded"/> + + <!-- Open product --> + <click selector="{{AdminCustomerCartSection.cartItem('1')}}" stepKey="openProduct"/> + + <!-- Go back to customer page --> + <click selector="{{AdminProductFormActionSection.backButton}}" stepKey="goBackToCustomerPage"/> + + <!-- Check current page is customer page --> + <seeInCurrentUrl stepKey="onCustomerAccountPage" url="{{AdminCustomerPage.url}}edit/id/$$createCustomer.id$$/"/> + + <after> + <!--Delete product--> + <deleteData stepKey="deleteProduct" createDataKey="createProduct"/> + + <!--Delete category--> + <deleteData stepKey="deleteCategory" createDataKey="createCategory"/> + + <!--Delete customer--> + <deleteData stepKey="deleteCustomer" createDataKey="createCustomer"/> + + <!-- Sign out--> + <actionGroup ref="SignOut" stepKey="signOut"/> + </after> + </test> +</tests> diff --git a/app/code/Magento/Customer/Test/Mftf/Test/AdminResetCustomerPasswordTest.xml b/app/code/Magento/Customer/Test/Mftf/Test/AdminResetCustomerPasswordTest.xml index fb67838e941b6..5721c46d5e4b9 100644 --- a/app/code/Magento/Customer/Test/Mftf/Test/AdminResetCustomerPasswordTest.xml +++ b/app/code/Magento/Customer/Test/Mftf/Test/AdminResetCustomerPasswordTest.xml @@ -19,11 +19,11 @@ </annotations> <before> <createData entity="Simple_US_Customer" stepKey="customer"/> - <actionGroup ref="LoginAsAdmin" stepKey="login"/> + <actionGroup ref="AdminLoginActionGroup" stepKey="login"/> </before> <after> <deleteData createDataKey="customer" stepKey="deleteCustomer"/> - <actionGroup ref="logout" stepKey="logout"/> + <actionGroup ref="AdminLogoutActionGroup" stepKey="logout"/> </after> <magentoCLI command="indexer:reindex" stepKey="reindex"/> <magentoCLI command="cache:flush" stepKey="flushCache"/> diff --git a/app/code/Magento/Customer/Test/Mftf/Test/AdminSearchCustomerAddressByKeywordTest.xml b/app/code/Magento/Customer/Test/Mftf/Test/AdminSearchCustomerAddressByKeywordTest.xml index 9f1c5e8cd923a..10da9284d45dc 100644 --- a/app/code/Magento/Customer/Test/Mftf/Test/AdminSearchCustomerAddressByKeywordTest.xml +++ b/app/code/Magento/Customer/Test/Mftf/Test/AdminSearchCustomerAddressByKeywordTest.xml @@ -21,11 +21,11 @@ <before> <createData entity="Simple_US_Customer_Multiple_Addresses" stepKey="createCustomer"/> <magentoCLI command="indexer:reindex" stepKey="reindex"/> - <actionGroup ref="LoginAsAdmin" stepKey="login"/> + <actionGroup ref="AdminLoginActionGroup" stepKey="login"/> </before> <after> <deleteData createDataKey="createCustomer" stepKey="deleteCustomer"/> - <actionGroup ref="logout" stepKey="logout"/> + <actionGroup ref="AdminLogoutActionGroup" stepKey="logout"/> </after> <!-- - Step1. Login to admin and go to Customers > All Customerts. @@ -44,4 +44,4 @@ <waitForPageLoad stepKey="waitForCustomerAddressesGridPageLoad"/> <seeNumberOfElements userInput="1" selector="{{AdminCustomerAddressesGridSection.rowsInGrid}}" stepKey="seeOnlyOneCustomerAddressesInGrid"/> </test> -</tests> \ No newline at end of file +</tests> diff --git a/app/code/Magento/Customer/Test/Mftf/Test/AdminSetCustomerDefaultBillingAddressTest.xml b/app/code/Magento/Customer/Test/Mftf/Test/AdminSetCustomerDefaultBillingAddressTest.xml index db8d4e1ee1eea..5ce96a8dcab3c 100644 --- a/app/code/Magento/Customer/Test/Mftf/Test/AdminSetCustomerDefaultBillingAddressTest.xml +++ b/app/code/Magento/Customer/Test/Mftf/Test/AdminSetCustomerDefaultBillingAddressTest.xml @@ -19,11 +19,11 @@ </annotations> <before> <createData entity="Simple_US_Customer_Multiple_Addresses_No_Default_Address" stepKey="customer"/> - <actionGroup ref="LoginAsAdmin" stepKey="login"/> + <actionGroup ref="AdminLoginActionGroup" stepKey="login"/> </before> <after> <deleteData createDataKey="customer" stepKey="deleteCustomer"/> - <actionGroup ref="logout" stepKey="logout"/> + <actionGroup ref="AdminLogoutActionGroup" stepKey="logout"/> </after> <!-- - Step1. Login to admin and go to Customers > All Customers. @@ -52,6 +52,8 @@ <see userInput="{{US_Address_NY_Not_Default_Address.street[0]}}" selector="{{AdminCustomerAddressesDefaultBillingSection.addressDetails}}" stepKey="assertDefaultBillingAddressIsSet"/> <click selector="{{AdminCustomerAddressesDefaultBillingSection.editButton}}" stepKey="clickEditDefaultBillingAddress"/> <waitForPageLoad stepKey="waitForCustomerAddressAddUpdateFormLoad"/> - <assertElementContainsAttribute selector="{{AdminCustomerAddressesSection.defaultBillingAddressCheckBox}}" attribute="value" expectedValue="1" stepKey="assertDefaultBillingCheckboxIsCheckedOnCustomerAddressAddUpdateForm"/> + <assertElementContainsAttribute stepKey="assertDefaultBillingCheckboxIsCheckedOnCustomerAddressAddUpdateForm"> + <expectedResult selector="{{AdminCustomerAddressesSection.defaultBillingAddressCheckBox}}" attribute="value" type="string">1</expectedResult> + </assertElementContainsAttribute> </test> </tests> diff --git a/app/code/Magento/Customer/Test/Mftf/Test/AdminSetCustomerDefaultShippingAddressTest.xml b/app/code/Magento/Customer/Test/Mftf/Test/AdminSetCustomerDefaultShippingAddressTest.xml index 6e83218176904..a9832c86562f1 100644 --- a/app/code/Magento/Customer/Test/Mftf/Test/AdminSetCustomerDefaultShippingAddressTest.xml +++ b/app/code/Magento/Customer/Test/Mftf/Test/AdminSetCustomerDefaultShippingAddressTest.xml @@ -19,11 +19,11 @@ </annotations> <before> <createData entity="Simple_US_Customer_Multiple_Addresses_No_Default_Address" stepKey="customer"/> - <actionGroup ref="LoginAsAdmin" stepKey="login"/> + <actionGroup ref="AdminLoginActionGroup" stepKey="login"/> </before> <after> <deleteData createDataKey="customer" stepKey="deleteCustomer"/> - <actionGroup ref="logout" stepKey="logout"/> + <actionGroup ref="AdminLogoutActionGroup" stepKey="logout"/> </after> <!-- - Step1. Login to admin and go to Customers > All Customers. @@ -52,6 +52,8 @@ <see userInput="{{US_Address_NY_Not_Default_Address.street[0]}}" selector="{{AdminCustomerAddressesDefaultShippingSection.addressDetails}}" stepKey="assertDefaultShippingAddressIsSet"/> <click selector="{{AdminCustomerAddressesDefaultShippingSection.editButton}}" stepKey="clickEditDefaultShippingAddress"/> <waitForPageLoad stepKey="waitForCustomerAddressAddUpdateFormLoad"/> - <assertElementContainsAttribute selector="{{AdminCustomerAddressesSection.defaultShippingAddressCheckBox}}" attribute="value" expectedValue="1" stepKey="assertDefaultShippingCheckboxIsCheckedOnCustomerAddressAddUpdateForm"/> + <assertElementContainsAttribute stepKey="assertDefaultShippingCheckboxIsCheckedOnCustomerAddressAddUpdateForm"> + <expectedResult selector="{{AdminCustomerAddressesSection.defaultShippingAddressCheckBox}}" attribute="value" type="string">1</expectedResult> + </assertElementContainsAttribute> </test> </tests> diff --git a/app/code/Magento/Customer/Test/Mftf/Test/AdminUpdateCustomerTest.xml b/app/code/Magento/Customer/Test/Mftf/Test/AdminUpdateCustomerTest.xml deleted file mode 100644 index f58f23dee4235..0000000000000 --- a/app/code/Magento/Customer/Test/Mftf/Test/AdminUpdateCustomerTest.xml +++ /dev/null @@ -1,308 +0,0 @@ -<?xml version="1.0" encoding="UTF-8"?> -<!-- - /** - * Copyright © Magento, Inc. All rights reserved. - * See COPYING.txt for license details. - */ ---> - -<tests xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" - xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/testSchema.xsd"> - <test name="AdminUpdateCustomerInfoFromDefaultToNonDefaultTest"> - <annotations> - <features value="Customer"/> - <stories value="Update Customer Information in Admin"/> - <title value="Update Customer Info from Default to Non-Default in Admin"/> - <description value="Update Customer Info from Default to Non-Default in Admin"/> - <severity value="CRITICAL"/> - <testCaseId value="MC-13619"/> - <group value="Customer"/> - <group value="mtf_migrated"/> - </annotations> - <before> - <createData stepKey="customer" entity="Simple_Customer_Without_Address"/> - <magentoCLI command="indexer:reindex" stepKey="reindex"/> - <actionGroup ref="LoginAsAdmin" stepKey="login"/> - </before> - <after> - <deleteData stepKey="deleteCustomer" createDataKey="customer"/> - <!-- Reset customer grid filter --> - <amOnPage stepKey="goToCustomersGridPage" url="{{AdminCustomerPage.url}}"/> - <waitForPageLoad stepKey="waitForCustomersGrid"/> - <actionGroup stepKey="resetFilter" ref="AdminResetFilterInCustomerGrid"/> - <actionGroup ref="logout" stepKey="logout"/> - </after> - - <amOnPage url="{{AdminCustomerPage.url}}edit/id/$$customer.id$$/" stepKey="openCustomerEditPage"/> - <waitForPageLoad stepKey="waitForCustomerEditPage"/> - <!-- Update Customer Account Information --> - <actionGroup stepKey="editCustomerInformation" ref="AdminEditCustomerAccountInformationActionGroup"> - <argument name="firstName" value="$$customer.firstname$$updated"/> - <argument name="lastName" value="$$customer.lastname$$updated"/> - <argument name="email" value="updated$$customer.email$$"/> - </actionGroup> - <!--Update Customer Addresses --> - <actionGroup stepKey="editCustomerAddress" ref="AdminEditCustomerAddressSetDefaultShippingAndBilling"> - <argument name="customerAddress" value="CustomerAddressSimple"/> - </actionGroup> - <actionGroup stepKey="saveAndCheckSuccessMessage" ref="AdminSaveCustomerAndAssertSuccessMessage"/> - <!-- Assert Customer in Customer grid --> - <amOnPage stepKey="goToCustomersGridPage" url="{{AdminCustomerPage.url}}"/> - <waitForPageLoad stepKey="waitForCustomersGrid"/> - <actionGroup stepKey="resetFilter" ref="AdminResetFilterInCustomerGrid"/> - <actionGroup stepKey="filterByEamil" ref="AdminFilterCustomerGridByEmail"> - <argument name="email" value="updated$$customer.email$$"/> - </actionGroup> - <actionGroup stepKey="checkCustomerInGrid" ref="AdminAssertCustomerInCustomersGrid"> - <argument name="text" value="updated$$customer.email$$"/> - <argument name="row" value="1"/> - </actionGroup> - <!-- Assert Customer in Customer Form --> - <amOnPage url="{{AdminCustomerPage.url}}edit/id/$$customer.id$$/" stepKey="openCustomerEditPageAfterSave"/> - <waitForPageLoad stepKey="waitForCustomerEditPageAfterSave"/> - <!-- Assert Customer Account Information --> - <actionGroup stepKey="checkCustomerAccountInformation" ref="AdminAssertCustomerAccountInformation"> - <argument name="firstName" value="$$customer.firstname$$updated"/> - <argument name="lastName" value="$$customer.lastname$$updated"/> - <argument name="email" value="updated$$customer.email$$"/> - </actionGroup> - <!-- Assert Customer Default Billing Address --> - <actionGroup stepKey="checkDefaultBilling" ref="AdminAssertCustomerDefaultBillingAddress"> - <argument name="firstName" value="$$customer.firstname$$updated"/> - <argument name="lastName" value="$$customer.lastname$$updated"/> - <argument name="street1" value="{{CustomerAddressSimple.street[0]}}"/> - <argument name="state" value="{{CustomerAddressSimple.state}}"/> - <argument name="postcode" value="{{CustomerAddressSimple.postcode}}"/> - <argument name="country" value="{{CustomerAddressSimple.country_id}}"/> - <argument name="telephone" value="{{CustomerAddressSimple.telephone}}"/> - </actionGroup> - <!-- Assert Customer Default Shipping Address --> - <actionGroup stepKey="checkDefaultShipping" ref="AdminAssertCustomerDefaultShippingAddress"> - <argument name="firstName" value="$$customer.firstname$$updated"/> - <argument name="lastName" value="$$customer.lastname$$updated"/> - <argument name="street1" value="{{CustomerAddressSimple.street[0]}}"/> - <argument name="state" value="{{CustomerAddressSimple.state}}"/> - <argument name="postcode" value="{{CustomerAddressSimple.postcode}}"/> - <argument name="country" value="{{CustomerAddressSimple.country_id}}"/> - <argument name="telephone" value="{{CustomerAddressSimple.telephone}}"/> - </actionGroup> - </test> - - <test name="AdminUpdateCustomerAddressNoZipNoStateTest" extends="AdminUpdateCustomerInfoFromDefaultToNonDefaultTest"> - <annotations> - <features value="Customer"/> - <stories value="Update Customer Information in Admin"/> - <title value="Update Customer Address, without zip/state required, default billing/shipping checked in Admin"/> - <description value="Update Customer Address, without zip/state required, default billing/shipping checked in Admin"/> - <severity value="CRITICAL"/> - <testCaseId value="MC-13621"/> - <group value="Customer"/> - <group value="mtf_migrated"/> - </annotations> - <after> - <remove keyForRemoval="goToCustomersGridPage"/> - <remove keyForRemoval="waitForCustomersGrid"/> - <remove keyForRemoval="resetFilter"/> - </after> - - <!-- Remove steps that are not used for this test --> - <remove keyForRemoval="editCustomerInformation"/> - <remove keyForRemoval="goToCustomersGridPage"/> - <remove keyForRemoval="waitForCustomersGrid"/> - <remove keyForRemoval="resetFilter"/> - <remove keyForRemoval="filterByEamil"/> - <remove keyForRemoval="checkCustomerInGrid"/> - <remove keyForRemoval="checkCustomerAccountInformation"/> - - <!--Update Customer Addresses With No Zip and No State --> - <actionGroup stepKey="editCustomerAddress" ref="AdminEditCustomerAddressNoZipNoState"> - <argument name="customerAddress" value="addressNoZipNoState"/> - </actionGroup> - - <!-- Assert Customer Default Billing Address --> - <actionGroup stepKey="checkDefaultBilling" ref="AdminAssertCustomerDefaultBillingAddress"> - <argument name="firstName" value="$$customer.firstname$$"/> - <argument name="lastName" value="$$customer.lastname$$"/> - <argument name="street1" value="{{addressNoZipNoState.street[0]}}"/> - <argument name="country" value="{{addressNoZipNoState.country_id}}"/> - <argument name="telephone" value="{{addressNoZipNoState.telephone}}"/> - </actionGroup> - <!-- Assert Customer Default Shipping Address --> - <actionGroup stepKey="checkDefaultShipping" ref="AdminAssertCustomerDefaultShippingAddress"> - <argument name="firstName" value="$$customer.firstname$$"/> - <argument name="lastName" value="$$customer.lastname$$"/> - <argument name="street1" value="{{addressNoZipNoState.street[0]}}"/> - <argument name="country" value="{{addressNoZipNoState.country_id}}"/> - <argument name="telephone" value="{{addressNoZipNoState.telephone}}"/> - </actionGroup> - <!-- Assert Customer Login Storefront --> - <actionGroup stepKey="login" ref="StorefrontAssertSuccessLoginToStorefront" after="checkDefaultShipping" > - <argument name="Customer" value="$$customer$$"/> - </actionGroup> - </test> - - <test name="AdminUpdateCustomerAddressNoBillingNoShippingTest" extends="AdminUpdateCustomerInfoFromDefaultToNonDefaultTest"> - <annotations> - <features value="Customer"/> - <stories value="Update Customer Information in Admin"/> - <title value="Update Customer Address, default billing/shipping unchecked in Admin"/> - <description value="Update Customer Address, default billing/shipping unchecked in Admin"/> - <severity value="CRITICAL"/> - <testCaseId value="MC-13622"/> - <group value="Customer"/> - <group value="mtf_migrated"/> - </annotations> - <after> - <remove keyForRemoval="goToCustomersGridPage"/> - <remove keyForRemoval="waitForCustomersGrid"/> - <remove keyForRemoval="resetFilter"/> - </after> - - <!-- Remove steps that are not used for this test --> - <remove keyForRemoval="editCustomerInformation"/> - <remove keyForRemoval="goToCustomersGridPage"/> - <remove keyForRemoval="waitForCustomersGrid"/> - <remove keyForRemoval="resetFilter"/> - <remove keyForRemoval="filterByEamil"/> - <remove keyForRemoval="checkCustomerInGrid"/> - <remove keyForRemoval="checkCustomerAccountInformation"/> - <remove keyForRemoval="checkDefaultBilling"/> - <remove keyForRemoval="checkDefaultShipping"/> - - <!--Update Customer Addresses With Default Billing and Shipping Unchecked --> - <actionGroup stepKey="editCustomerAddress" ref="AdminEditCustomerAddressesFrom"> - <argument name="customerAddress" value="CustomerAddressSimple"/> - </actionGroup> - - <!-- Check Customer Address in Customer Form --> - <actionGroup stepKey="checkNoDefaultBilling" ref="AdminAssertCustomerNoDefaultBillingAddress" after="waitForCustomerEditPageAfterSave"/> - <actionGroup stepKey="checkNoDefaultShipping" ref="AdminAssertCustomerNoDefaultShippingAddress" after="checkNoDefaultBilling"/> - <actionGroup stepKey="resetFilter" ref="AdminResetFilterInCustomerAddressGrid" after="checkNoDefaultShipping"/> - <actionGroup stepKey="searchAddress" ref="AdminFilterCustomerAddressGridByPhoneNumber" after="resetFilter"> - <argument name="phone" value="{{CustomerAddressSimple.telephone}}"/> - </actionGroup> - <actionGroup stepKey="checkAddressStreetInGrid" ref="AdminAssertAddressInCustomersAddressGrid" after="searchAddress"> - <argument name="text" value="{{CustomerAddressSimple.street[0]}}"/> - </actionGroup> - <actionGroup stepKey="checkAddressPhoneInGrid" ref="AdminAssertAddressInCustomersAddressGrid" after="checkAddressStreetInGrid"> - <argument name="text" value="{{CustomerAddressSimple.telephone}}"/> - </actionGroup> - <actionGroup stepKey="checkAddressStateInGrid" ref="AdminAssertAddressInCustomersAddressGrid" after="checkAddressPhoneInGrid"> - <argument name="text" value="{{CustomerAddressSimple.state}}"/> - </actionGroup> - <actionGroup stepKey="checkAddressCityInGrid" ref="AdminAssertAddressInCustomersAddressGrid" after="checkAddressStateInGrid"> - <argument name="text" value="{{CustomerAddressSimple.city}}"/> - </actionGroup> - <actionGroup stepKey="checkAddressCountryInGrid" ref="AdminAssertAddressInCustomersAddressGrid" after="checkAddressCityInGrid"> - <argument name="text" value="{{CustomerAddressSimple.country_id}}"/> - </actionGroup> - <actionGroup stepKey="resetFilterWhenDone" ref="AdminResetFilterInCustomerAddressGrid" after="checkAddressCountryInGrid"/> - <!-- Assert Customer Login Storefront --> - <actionGroup stepKey="login" ref="StorefrontAssertSuccessLoginToStorefront" after="resetFilterWhenDone" > - <argument name="Customer" value="$$customer$$"/> - </actionGroup> - </test> - - <test name="AdminDeleteCustomerAddressTest"> - <annotations> - <features value="Customer"/> - <stories value="Delete Customer Address in Admin"/> - <title value="Delete Customer Address in Admin"/> - <description value="Delete Customer Address in Admin"/> - <severity value="CRITICAL"/> - <testCaseId value="MC-13623"/> - <group value="Customer"/> - <group value="mtf_migrated"/> - </annotations> - <before> - <createData stepKey="customer" entity="Simple_US_Customer_Multiple_Addresses"/> - <magentoCLI command="indexer:reindex" stepKey="reindex"/> - <actionGroup ref="LoginAsAdmin" stepKey="login"/> - </before> - <after> - <deleteData stepKey="deleteCustomer" createDataKey="customer"/> - <actionGroup ref="logout" stepKey="logout"/> - </after> - - <amOnPage url="{{AdminCustomerPage.url}}edit/id/$$customer.id$$/" stepKey="openCustomerEditPage"/> - <waitForPageLoad stepKey="waitForCustomerEditPage"/> - <!-- Assert Customer Default Billing Address --> - <actionGroup stepKey="checkDefaultBilling" ref="AdminAssertCustomerDefaultBillingAddress"> - <argument name="firstName" value="$$customer.firstname$$"/> - <argument name="lastName" value="$$customer.lastname$$"/> - <argument name="street1" value="{{US_Address_NY.street[0]}}"/> - <argument name="state" value="{{US_Address_NY.state}}"/> - <argument name="postcode" value="{{US_Address_NY.postcode}}"/> - <argument name="country" value="{{US_Address_NY.country}}"/> - <argument name="telephone" value="{{US_Address_NY.telephone}}"/> - </actionGroup> - <!-- Assert Customer Default Shipping Address --> - <actionGroup stepKey="checkDefaultShipping" ref="AdminAssertCustomerDefaultShippingAddress"> - <argument name="firstName" value="$$customer.firstname$$"/> - <argument name="lastName" value="$$customer.lastname$$"/> - <argument name="street1" value="{{US_Address_NY.street[0]}}"/> - <argument name="state" value="{{US_Address_NY.state}}"/> - <argument name="postcode" value="{{US_Address_NY.postcode}}"/> - <argument name="country" value="{{US_Address_NY.country}}"/> - <argument name="telephone" value="{{US_Address_NY.telephone}}"/> - </actionGroup> - <actionGroup stepKey="resetFilter" ref="AdminResetFilterInCustomerAddressGrid"/> - <!-- Assert 2 records in Customer Address Grid --> - <actionGroup stepKey="see2Record" ref="AdminAssertNumberOfRecordsInCustomersAddressGrid"> - <argument name="number" value="2"/> - </actionGroup> - <!-- Assert Address 1 in Grid --> - <actionGroup stepKey="checkAddressStreetInGrid" ref="AdminAssertAddressInCustomersAddressGrid"> - <argument name="text" value="{{US_Address_NY.street[0]}}"/> - </actionGroup> - <actionGroup stepKey="checkAddressPhoneInGrid" ref="AdminAssertAddressInCustomersAddressGrid"> - <argument name="text" value="{{US_Address_NY.telephone}}"/> - </actionGroup> - <actionGroup stepKey="checkAddressStateInGrid" ref="AdminAssertAddressInCustomersAddressGrid"> - <argument name="text" value="{{US_Address_NY.state}}"/> - </actionGroup> - <actionGroup stepKey="checkAddressCityInGrid" ref="AdminAssertAddressInCustomersAddressGrid"> - <argument name="text" value="{{US_Address_NY.city}}"/> - </actionGroup> - <actionGroup stepKey="checkAddressCountryInGrid" ref="AdminAssertAddressInCustomersAddressGrid"> - <argument name="text" value="{{US_Address_NY.country}}"/> - </actionGroup> - <!-- Assert Address 2 in Grid --> - <actionGroup stepKey="checkAddressStreetInGrid2" ref="AdminAssertAddressInCustomersAddressGrid"> - <argument name="text" value="{{UK_Not_Default_Address.street[0]}}"/> - </actionGroup> - <actionGroup stepKey="checkAddressPhoneInGrid2" ref="AdminAssertAddressInCustomersAddressGrid"> - <argument name="text" value="{{UK_Not_Default_Address.telephone}}"/> - </actionGroup> - <actionGroup stepKey="checkAddressCityInGrid2" ref="AdminAssertAddressInCustomersAddressGrid"> - <argument name="text" value="{{UK_Not_Default_Address.city}}"/> - </actionGroup> - <!-- Delete Customer in Customer Address Grid --> - <actionGroup stepKey="deleteAddress" ref="AdminDeleteAddressInCustomersAddressGrid"> - <argument name="row" value="0"/> - </actionGroup> - <!-- Assert 1 record in Customer Address Grid --> - <actionGroup stepKey="see1Record" ref="AdminAssertNumberOfRecordsInCustomersAddressGrid"> - <argument name="number" value="1"/> - </actionGroup> - <actionGroup stepKey="saveAndContinue" ref="AdminCustomerSaveAndContinue"/> - <actionGroup stepKey="saveAndCheckSuccessMessage" ref="AdminSaveCustomerAndAssertSuccessMessage"/> - <!-- Assert Customer Login Storefront --> - <actionGroup stepKey="login" ref="StorefrontAssertSuccessLoginToStorefront"> - <argument name="Customer" value="$$customer$$"/> - </actionGroup> - <!-- Assert Customer Address Book --> - <actionGroup stepKey="goToAddressBook" ref="StorefrontCustomerGoToSidebarMenu"> - <argument name="menu" value="Address Book"/> - </actionGroup> - <actionGroup stepKey="assertAddressNumber" ref="StorefrontCustomerAddressBookNumberOfAddresses"> - <argument name="number" value="1"/> - </actionGroup> - <actionGroup stepKey="assertNoAddress1" ref="StorefrontCustomerAddressBookNotContains"> - <argument name="text" value="{{US_Address_NY.street[0]}}"/> - </actionGroup> - <actionGroup stepKey="assertAddress2" ref="StorefrontCustomerAddressBookContains"> - <argument name="text" value="{{UK_Not_Default_Address.street[0]}}"/> - </actionGroup> - </test> -</tests> \ No newline at end of file diff --git a/app/code/Magento/Customer/Test/Mftf/Test/AdminUpdateCustomerTest/AdminDeleteCustomerAddressTest.xml b/app/code/Magento/Customer/Test/Mftf/Test/AdminUpdateCustomerTest/AdminDeleteCustomerAddressTest.xml new file mode 100644 index 0000000000000..c9805ebcc90ed --- /dev/null +++ b/app/code/Magento/Customer/Test/Mftf/Test/AdminUpdateCustomerTest/AdminDeleteCustomerAddressTest.xml @@ -0,0 +1,112 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + /** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +--> +<tests xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/testSchema.xsd"> + <test name="AdminDeleteCustomerAddressTest"> + <annotations> + <features value="Customer"/> + <stories value="Delete Customer Address in Admin"/> + <title value="Delete Customer Address in Admin"/> + <description value="Delete Customer Address in Admin"/> + <severity value="BLOCKER"/> + <testCaseId value="MC-13623"/> + <group value="Customer"/> + <group value="mtf_migrated"/> + </annotations> + <before> + <createData stepKey="customer" entity="Simple_US_Customer_Multiple_Addresses"/> + <magentoCLI command="indexer:reindex" stepKey="reindex"/> + <actionGroup ref="AdminLoginActionGroup" stepKey="login"/> + </before> + <after> + <deleteData stepKey="deleteCustomer" createDataKey="customer"/> + <actionGroup ref="AdminLogoutActionGroup" stepKey="logout"/> + </after> + + <amOnPage url="{{AdminCustomerPage.url}}edit/id/$$customer.id$$/" stepKey="openCustomerEditPage"/> + <waitForPageLoad stepKey="waitForCustomerEditPage"/> + <!-- Assert Customer Default Billing Address --> + <actionGroup stepKey="checkDefaultBilling" ref="AdminAssertCustomerDefaultBillingAddress"> + <argument name="firstName" value="$$customer.firstname$$"/> + <argument name="lastName" value="$$customer.lastname$$"/> + <argument name="street1" value="{{US_Address_NY.street[0]}}"/> + <argument name="state" value="{{US_Address_NY.state}}"/> + <argument name="postcode" value="{{US_Address_NY.postcode}}"/> + <argument name="country" value="{{US_Address_NY.country}}"/> + <argument name="telephone" value="{{US_Address_NY.telephone}}"/> + </actionGroup> + <!-- Assert Customer Default Shipping Address --> + <actionGroup stepKey="checkDefaultShipping" ref="AdminAssertCustomerDefaultShippingAddress"> + <argument name="firstName" value="$$customer.firstname$$"/> + <argument name="lastName" value="$$customer.lastname$$"/> + <argument name="street1" value="{{US_Address_NY.street[0]}}"/> + <argument name="state" value="{{US_Address_NY.state}}"/> + <argument name="postcode" value="{{US_Address_NY.postcode}}"/> + <argument name="country" value="{{US_Address_NY.country}}"/> + <argument name="telephone" value="{{US_Address_NY.telephone}}"/> + </actionGroup> + <actionGroup stepKey="resetFilter" ref="AdminResetFilterInCustomerAddressGrid"/> + <!-- Assert 2 records in Customer Address Grid --> + <actionGroup stepKey="see2Record" ref="AdminAssertNumberOfRecordsInCustomersAddressGrid"> + <argument name="number" value="2"/> + </actionGroup> + <!-- Assert Address 1 in Grid --> + <actionGroup stepKey="checkAddressStreetInGrid" ref="AdminAssertAddressInCustomersAddressGrid"> + <argument name="text" value="{{US_Address_NY.street[0]}}"/> + </actionGroup> + <actionGroup stepKey="checkAddressPhoneInGrid" ref="AdminAssertAddressInCustomersAddressGrid"> + <argument name="text" value="{{US_Address_NY.telephone}}"/> + </actionGroup> + <actionGroup stepKey="checkAddressStateInGrid" ref="AdminAssertAddressInCustomersAddressGrid"> + <argument name="text" value="{{US_Address_NY.state}}"/> + </actionGroup> + <actionGroup stepKey="checkAddressCityInGrid" ref="AdminAssertAddressInCustomersAddressGrid"> + <argument name="text" value="{{US_Address_NY.city}}"/> + </actionGroup> + <actionGroup stepKey="checkAddressCountryInGrid" ref="AdminAssertAddressInCustomersAddressGrid"> + <argument name="text" value="{{US_Address_NY.country}}"/> + </actionGroup> + <!-- Assert Address 2 in Grid --> + <actionGroup stepKey="checkAddressStreetInGrid2" ref="AdminAssertAddressInCustomersAddressGrid"> + <argument name="text" value="{{UK_Not_Default_Address.street[0]}}"/> + </actionGroup> + <actionGroup stepKey="checkAddressPhoneInGrid2" ref="AdminAssertAddressInCustomersAddressGrid"> + <argument name="text" value="{{UK_Not_Default_Address.telephone}}"/> + </actionGroup> + <actionGroup stepKey="checkAddressCityInGrid2" ref="AdminAssertAddressInCustomersAddressGrid"> + <argument name="text" value="{{UK_Not_Default_Address.city}}"/> + </actionGroup> + <!-- Delete Customer in Customer Address Grid --> + <actionGroup stepKey="deleteAddress" ref="AdminDeleteAddressInCustomersAddressGrid"> + <argument name="row" value="0"/> + </actionGroup> + <!-- Assert 1 record in Customer Address Grid --> + <actionGroup stepKey="see1Record" ref="AdminAssertNumberOfRecordsInCustomersAddressGrid"> + <argument name="number" value="1"/> + </actionGroup> + <actionGroup stepKey="saveAndContinue" ref="AdminCustomerSaveAndContinue"/> + <actionGroup stepKey="saveAndCheckSuccessMessage" ref="AdminSaveCustomerAndAssertSuccessMessage"/> + <!-- Assert Customer Login Storefront --> + <actionGroup stepKey="login" ref="StorefrontAssertSuccessLoginToStorefront"> + <argument name="Customer" value="$$customer$$"/> + </actionGroup> + <!-- Assert Customer Address Book --> + <actionGroup stepKey="goToAddressBook" ref="StorefrontCustomerGoToSidebarMenu"> + <argument name="menu" value="Address Book"/> + </actionGroup> + <actionGroup stepKey="assertAddressNumber" ref="StorefrontCustomerAddressBookNumberOfAddresses"> + <argument name="number" value="1"/> + </actionGroup> + <actionGroup stepKey="assertNoAddress1" ref="StorefrontCustomerAddressBookNotContains"> + <argument name="text" value="{{US_Address_NY.street[0]}}"/> + </actionGroup> + <actionGroup stepKey="assertAddress2" ref="StorefrontCustomerAddressBookContains"> + <argument name="text" value="{{UK_Not_Default_Address.street[0]}}"/> + </actionGroup> + </test> +</tests> diff --git a/app/code/Magento/Customer/Test/Mftf/Test/AdminUpdateCustomerTest/AdminUpdateCustomerAddressNoBillingNoShippingTest.xml b/app/code/Magento/Customer/Test/Mftf/Test/AdminUpdateCustomerTest/AdminUpdateCustomerAddressNoBillingNoShippingTest.xml new file mode 100644 index 0000000000000..c6e370fb6a76b --- /dev/null +++ b/app/code/Magento/Customer/Test/Mftf/Test/AdminUpdateCustomerTest/AdminUpdateCustomerAddressNoBillingNoShippingTest.xml @@ -0,0 +1,71 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + /** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +--> +<tests xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/testSchema.xsd"> + <test name="AdminUpdateCustomerAddressNoBillingNoShippingTest" extends="AdminUpdateCustomerInfoFromDefaultToNonDefaultTest"> + <annotations> + <features value="Customer"/> + <stories value="Update Customer Information in Admin"/> + <title value="Update Customer Address, default billing/shipping unchecked in Admin"/> + <description value="Update Customer Address, default billing/shipping unchecked in Admin"/> + <severity value="CRITICAL"/> + <testCaseId value="MC-13622"/> + <group value="Customer"/> + <group value="mtf_migrated"/> + </annotations> + <after> + <remove keyForRemoval="goToCustomersGridPage"/> + <remove keyForRemoval="waitForCustomersGrid"/> + <remove keyForRemoval="resetFilter"/> + </after> + + <!-- Remove steps that are not used for this test --> + <remove keyForRemoval="editCustomerInformation"/> + <remove keyForRemoval="goToCustomersGridPage"/> + <remove keyForRemoval="waitForCustomersGrid"/> + <remove keyForRemoval="resetFilter"/> + <remove keyForRemoval="filterByEamil"/> + <remove keyForRemoval="checkCustomerInGrid"/> + <remove keyForRemoval="checkCustomerAccountInformation"/> + <remove keyForRemoval="checkDefaultBilling"/> + <remove keyForRemoval="checkDefaultShipping"/> + + <!--Update Customer Addresses With Default Billing and Shipping Unchecked --> + <actionGroup stepKey="editCustomerAddress" ref="AdminEditCustomerAddressesFromActionGroup"> + <argument name="customerAddress" value="CustomerAddressSimple"/> + </actionGroup> + + <!-- Check Customer Address in Customer Form --> + <actionGroup stepKey="checkNoDefaultBilling" ref="AdminAssertCustomerNoDefaultBillingAddress" after="waitForCustomerEditPageAfterSave"/> + <actionGroup stepKey="checkNoDefaultShipping" ref="AdminAssertCustomerNoDefaultShippingAddress" after="checkNoDefaultBilling"/> + <actionGroup stepKey="resetFilter" ref="AdminResetFilterInCustomerAddressGrid" after="checkNoDefaultShipping"/> + <actionGroup stepKey="searchAddress" ref="AdminFilterCustomerAddressGridByPhoneNumber" after="resetFilter"> + <argument name="phone" value="{{CustomerAddressSimple.telephone}}"/> + </actionGroup> + <actionGroup stepKey="checkAddressStreetInGrid" ref="AdminAssertAddressInCustomersAddressGrid" after="searchAddress"> + <argument name="text" value="{{CustomerAddressSimple.street[0]}}"/> + </actionGroup> + <actionGroup stepKey="checkAddressPhoneInGrid" ref="AdminAssertAddressInCustomersAddressGrid" after="checkAddressStreetInGrid"> + <argument name="text" value="{{CustomerAddressSimple.telephone}}"/> + </actionGroup> + <actionGroup stepKey="checkAddressStateInGrid" ref="AdminAssertAddressInCustomersAddressGrid" after="checkAddressPhoneInGrid"> + <argument name="text" value="{{CustomerAddressSimple.state}}"/> + </actionGroup> + <actionGroup stepKey="checkAddressCityInGrid" ref="AdminAssertAddressInCustomersAddressGrid" after="checkAddressStateInGrid"> + <argument name="text" value="{{CustomerAddressSimple.city}}"/> + </actionGroup> + <actionGroup stepKey="checkAddressCountryInGrid" ref="AdminAssertAddressInCustomersAddressGrid" after="checkAddressCityInGrid"> + <argument name="text" value="{{CustomerAddressSimple.country_id}}"/> + </actionGroup> + <actionGroup stepKey="resetFilterWhenDone" ref="AdminResetFilterInCustomerAddressGrid" after="checkAddressCountryInGrid"/> + <!-- Assert Customer Login Storefront --> + <actionGroup stepKey="login" ref="StorefrontAssertSuccessLoginToStorefront" after="resetFilterWhenDone"> + <argument name="Customer" value="$$customer$$"/> + </actionGroup> + </test> +</tests> diff --git a/app/code/Magento/Customer/Test/Mftf/Test/AdminUpdateCustomerTest/AdminUpdateCustomerAddressNoZipNoStateTest.xml b/app/code/Magento/Customer/Test/Mftf/Test/AdminUpdateCustomerTest/AdminUpdateCustomerAddressNoZipNoStateTest.xml new file mode 100644 index 0000000000000..d81d7da6b5b07 --- /dev/null +++ b/app/code/Magento/Customer/Test/Mftf/Test/AdminUpdateCustomerTest/AdminUpdateCustomerAddressNoZipNoStateTest.xml @@ -0,0 +1,62 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + /** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +--> +<tests xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/testSchema.xsd"> + <test name="AdminUpdateCustomerAddressNoZipNoStateTest" extends="AdminUpdateCustomerInfoFromDefaultToNonDefaultTest"> + <annotations> + <features value="Customer"/> + <stories value="Update Customer Information in Admin"/> + <title value="Update Customer Address, without zip/state required, default billing/shipping checked in Admin"/> + <description value="Update Customer Address, without zip/state required, default billing/shipping checked in Admin"/> + <severity value="CRITICAL"/> + <testCaseId value="MC-13621"/> + <group value="Customer"/> + <group value="mtf_migrated"/> + </annotations> + <after> + <remove keyForRemoval="goToCustomersGridPage"/> + <remove keyForRemoval="waitForCustomersGrid"/> + <remove keyForRemoval="resetFilter"/> + </after> + + <!-- Remove steps that are not used for this test --> + <remove keyForRemoval="editCustomerInformation"/> + <remove keyForRemoval="goToCustomersGridPage"/> + <remove keyForRemoval="waitForCustomersGrid"/> + <remove keyForRemoval="resetFilter"/> + <remove keyForRemoval="filterByEamil"/> + <remove keyForRemoval="checkCustomerInGrid"/> + <remove keyForRemoval="checkCustomerAccountInformation"/> + + <!--Update Customer Addresses With No Zip and No State --> + <actionGroup stepKey="editCustomerAddress" ref="AdminEditCustomerAddressNoZipNoStateActionGroup"> + <argument name="customerAddress" value="addressNoZipNoState"/> + </actionGroup> + + <!-- Assert Customer Default Billing Address --> + <actionGroup stepKey="checkDefaultBilling" ref="AdminAssertCustomerDefaultBillingAddress"> + <argument name="firstName" value="$$customer.firstname$$"/> + <argument name="lastName" value="$$customer.lastname$$"/> + <argument name="street1" value="{{addressNoZipNoState.street[0]}}"/> + <argument name="country" value="{{addressNoZipNoState.country_id}}"/> + <argument name="telephone" value="{{addressNoZipNoState.telephone}}"/> + </actionGroup> + <!-- Assert Customer Default Shipping Address --> + <actionGroup stepKey="checkDefaultShipping" ref="AdminAssertCustomerDefaultShippingAddress"> + <argument name="firstName" value="$$customer.firstname$$"/> + <argument name="lastName" value="$$customer.lastname$$"/> + <argument name="street1" value="{{addressNoZipNoState.street[0]}}"/> + <argument name="country" value="{{addressNoZipNoState.country_id}}"/> + <argument name="telephone" value="{{addressNoZipNoState.telephone}}"/> + </actionGroup> + <!-- Assert Customer Login Storefront --> + <actionGroup stepKey="login" ref="StorefrontAssertSuccessLoginToStorefront" after="checkDefaultShipping"> + <argument name="Customer" value="$$customer$$"/> + </actionGroup> + </test> +</tests> diff --git a/app/code/Magento/Customer/Test/Mftf/Test/AdminUpdateCustomerTest/AdminUpdateCustomerInfoFromDefaultToNonDefaultTest.xml b/app/code/Magento/Customer/Test/Mftf/Test/AdminUpdateCustomerTest/AdminUpdateCustomerInfoFromDefaultToNonDefaultTest.xml new file mode 100644 index 0000000000000..0d550416167aa --- /dev/null +++ b/app/code/Magento/Customer/Test/Mftf/Test/AdminUpdateCustomerTest/AdminUpdateCustomerInfoFromDefaultToNonDefaultTest.xml @@ -0,0 +1,89 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + /** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +--> +<tests xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/testSchema.xsd"> + <test name="AdminUpdateCustomerInfoFromDefaultToNonDefaultTest"> + <annotations> + <features value="Customer"/> + <stories value="Update Customer Information in Admin"/> + <title value="Update Customer Info from Default to Non-Default in Admin"/> + <description value="Update Customer Info from Default to Non-Default in Admin"/> + <severity value="BLOCKER"/> + <testCaseId value="MC-13619"/> + <group value="Customer"/> + <group value="mtf_migrated"/> + </annotations> + <before> + <createData stepKey="customer" entity="Simple_Customer_Without_Address"/> + <magentoCLI command="indexer:reindex" stepKey="reindex"/> + <actionGroup ref="AdminLoginActionGroup" stepKey="login"/> + </before> + <after> + <deleteData stepKey="deleteCustomer" createDataKey="customer"/> + <!-- Reset customer grid filter --> + <amOnPage stepKey="goToCustomersGridPage" url="{{AdminCustomerPage.url}}"/> + <waitForPageLoad stepKey="waitForCustomersGrid"/> + <actionGroup stepKey="resetFilter" ref="AdminResetFilterInCustomerGrid"/> + <actionGroup ref="AdminLogoutActionGroup" stepKey="logout"/> + </after> + + <amOnPage url="{{AdminCustomerPage.url}}edit/id/$$customer.id$$/" stepKey="openCustomerEditPage"/> + <waitForPageLoad stepKey="waitForCustomerEditPage"/> + <!-- Update Customer Account Information --> + <actionGroup stepKey="editCustomerInformation" ref="AdminEditCustomerAccountInformationActionGroup"> + <argument name="firstName" value="$$customer.firstname$$updated"/> + <argument name="lastName" value="$$customer.lastname$$updated"/> + <argument name="email" value="updated$$customer.email$$"/> + </actionGroup> + <!--Update Customer Addresses --> + <actionGroup stepKey="editCustomerAddress" ref="AdminEditCustomerAddressSetDefaultShippingAndBillingActionGroup"> + <argument name="customerAddress" value="CustomerAddressSimple"/> + </actionGroup> + <actionGroup stepKey="saveAndCheckSuccessMessage" ref="AdminSaveCustomerAndAssertSuccessMessage"/> + <!-- Assert Customer in Customer grid --> + <amOnPage stepKey="goToCustomersGridPage" url="{{AdminCustomerPage.url}}"/> + <waitForPageLoad stepKey="waitForCustomersGrid"/> + <actionGroup stepKey="resetFilter" ref="AdminResetFilterInCustomerGrid"/> + <actionGroup stepKey="filterByEamil" ref="AdminFilterCustomerGridByEmail"> + <argument name="email" value="updated$$customer.email$$"/> + </actionGroup> + <actionGroup stepKey="checkCustomerInGrid" ref="AdminAssertCustomerInCustomersGrid"> + <argument name="text" value="updated$$customer.email$$"/> + <argument name="row" value="1"/> + </actionGroup> + <!-- Assert Customer in Customer Form --> + <amOnPage url="{{AdminCustomerPage.url}}edit/id/$$customer.id$$/" stepKey="openCustomerEditPageAfterSave"/> + <waitForPageLoad stepKey="waitForCustomerEditPageAfterSave"/> + <!-- Assert Customer Account Information --> + <actionGroup stepKey="checkCustomerAccountInformation" ref="AdminAssertCustomerAccountInformation"> + <argument name="firstName" value="$$customer.firstname$$updated"/> + <argument name="lastName" value="$$customer.lastname$$updated"/> + <argument name="email" value="updated$$customer.email$$"/> + </actionGroup> + <!-- Assert Customer Default Billing Address --> + <actionGroup stepKey="checkDefaultBilling" ref="AdminAssertCustomerDefaultBillingAddress"> + <argument name="firstName" value="$$customer.firstname$$updated"/> + <argument name="lastName" value="$$customer.lastname$$updated"/> + <argument name="street1" value="{{CustomerAddressSimple.street[0]}}"/> + <argument name="state" value="{{CustomerAddressSimple.state}}"/> + <argument name="postcode" value="{{CustomerAddressSimple.postcode}}"/> + <argument name="country" value="{{CustomerAddressSimple.country_id}}"/> + <argument name="telephone" value="{{CustomerAddressSimple.telephone}}"/> + </actionGroup> + <!-- Assert Customer Default Shipping Address --> + <actionGroup stepKey="checkDefaultShipping" ref="AdminAssertCustomerDefaultShippingAddress"> + <argument name="firstName" value="$$customer.firstname$$updated"/> + <argument name="lastName" value="$$customer.lastname$$updated"/> + <argument name="street1" value="{{CustomerAddressSimple.street[0]}}"/> + <argument name="state" value="{{CustomerAddressSimple.state}}"/> + <argument name="postcode" value="{{CustomerAddressSimple.postcode}}"/> + <argument name="country" value="{{CustomerAddressSimple.country_id}}"/> + <argument name="telephone" value="{{CustomerAddressSimple.telephone}}"/> + </actionGroup> + </test> +</tests> diff --git a/app/code/Magento/Customer/Test/Mftf/Test/AdminVerifyCreateCustomerRequiredFieldsTest.xml b/app/code/Magento/Customer/Test/Mftf/Test/AdminVerifyCreateCustomerRequiredFieldsTest.xml index 7dab6eefde8ec..72064617ef33b 100644 --- a/app/code/Magento/Customer/Test/Mftf/Test/AdminVerifyCreateCustomerRequiredFieldsTest.xml +++ b/app/code/Magento/Customer/Test/Mftf/Test/AdminVerifyCreateCustomerRequiredFieldsTest.xml @@ -19,10 +19,10 @@ </annotations> <before> - <actionGroup ref="LoginAsAdmin" stepKey="loginToAdminPanel"/> + <actionGroup ref="AdminLoginActionGroup" stepKey="loginToAdminPanel"/> </before> <after> - <actionGroup ref="logout" stepKey="logout"/> + <actionGroup ref="AdminLogoutActionGroup" stepKey="logout"/> </after> <!--Open New Customer Page --> diff --git a/app/code/Magento/Customer/Test/Mftf/Test/AdminVerifyCustomerAddressRequiredFieldsTest.xml b/app/code/Magento/Customer/Test/Mftf/Test/AdminVerifyCustomerAddressRequiredFieldsTest.xml index bfb47dc9e1911..d78d676a822d9 100644 --- a/app/code/Magento/Customer/Test/Mftf/Test/AdminVerifyCustomerAddressRequiredFieldsTest.xml +++ b/app/code/Magento/Customer/Test/Mftf/Test/AdminVerifyCustomerAddressRequiredFieldsTest.xml @@ -20,11 +20,11 @@ <before> <createData entity="Simple_Customer_Without_Address" stepKey="createCustomer"/> - <actionGroup ref="LoginAsAdmin" stepKey="loginToAdminPanel"/> + <actionGroup ref="AdminLoginActionGroup" stepKey="loginToAdminPanel"/> </before> <after> <deleteData createDataKey="createCustomer" stepKey="deleteCustomer"/> - <actionGroup ref="logout" stepKey="logout"/> + <actionGroup ref="AdminLogoutActionGroup" stepKey="logout"/> </after> <!--Open Created Customer --> diff --git a/app/code/Magento/Customer/Test/Mftf/Test/AdminVerifyCustomerAddressStateContainValuesOnceTest.xml b/app/code/Magento/Customer/Test/Mftf/Test/AdminVerifyCustomerAddressStateContainValuesOnceTest.xml index daab5fd2061fb..2aa85f8c966a9 100644 --- a/app/code/Magento/Customer/Test/Mftf/Test/AdminVerifyCustomerAddressStateContainValuesOnceTest.xml +++ b/app/code/Magento/Customer/Test/Mftf/Test/AdminVerifyCustomerAddressStateContainValuesOnceTest.xml @@ -21,7 +21,7 @@ </annotations> <before> <createData entity="Simple_US_Customer_Multiple_Addresses" stepKey="firstCustomer"/> - <actionGroup ref="LoginAsAdmin" stepKey="login"/> + <actionGroup ref="AdminLoginActionGroup" stepKey="login"/> </before> <after> <deleteData createDataKey="firstCustomer" stepKey="deleteFirstCustomer"/> @@ -29,7 +29,7 @@ <argument name="customerEmail" value="Simple_US_Customer.email"/> </actionGroup> <actionGroup ref="AdminClearCustomersFiltersActionGroup" stepKey="clearFilters"/> - <actionGroup ref="logout" stepKey="logout"/> + <actionGroup ref="AdminLogoutActionGroup" stepKey="logout"/> </after> <!-- Go to Customers > All Customers.--> @@ -52,7 +52,7 @@ <seeNumberOfElements userInput="1" selector="{{AdminCustomerAddressesSection.regionId(US_Address_NY.state)}}" stepKey="seeOnlyOneRegionInSelectStateForFirstCustomer"/> <!--Go to Customers > All customers, Click Add new Customers, fill all necessary fields, Save--> - <actionGroup ref="AdminCreateCustomerWithWebSiteAndGroup" stepKey="createSimpleUSCustomerWithoutAddress"/> + <actionGroup ref="AdminCreateCustomerWithWebSiteAndGroupActionGroup" stepKey="createSimpleUSCustomerWithoutAddress"/> <!--Select new created customer, Click Edit mode--> <actionGroup ref="OpenEditCustomerFromAdminActionGroup" stepKey="openEditCustomerPageWithoutAddresses"> diff --git a/app/code/Magento/Customer/Test/Mftf/Test/AdminVerifyDisabledCustomerGroupFieldTest.xml b/app/code/Magento/Customer/Test/Mftf/Test/AdminVerifyDisabledCustomerGroupFieldTest.xml new file mode 100644 index 0000000000000..ff60ac92853c5 --- /dev/null +++ b/app/code/Magento/Customer/Test/Mftf/Test/AdminVerifyDisabledCustomerGroupFieldTest.xml @@ -0,0 +1,40 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + /** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +--> + +<tests xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/testSchema.xsd"> + <test name="AdminVerifyDisabledCustomerGroupFieldTest"> + <annotations> + <stories value="Check that field is disabled in system Customer Group"/> + <title value="Check that field is disabled in system Customer Group"/> + <description value="Checks that customer_group_code field is disabled in NOT LOGGED IN Customer Group"/> + <testCaseId value="MC-14206"/> + <severity value="CRITICAL"/> + <group value="customers"/> + <group value="mtf_migrated"/> + </annotations> + + <actionGroup ref="AdminLoginActionGroup" stepKey="loginAsAdmin"/> + + <actionGroup ref="AdminNavigateToCustomerGroupPageActionGroup" stepKey="amOnCustomerGroupPage"/> + <actionGroup ref="AdminFilterCustomerGroupByNameActionGroup" stepKey="clearFiltersIfTheySet"> + <argument name="customerGroupName" value="{{NotLoggedInCustomerGroup.code}}"/> + </actionGroup> + + <actionGroup ref="AdminGridCustomerGroupEditByCodeActionGroup" stepKey="openCustomerGroup"> + <argument name="customerGroupCode" value="{{NotLoggedInCustomerGroup.code}}"/> + </actionGroup> + + <seeInField selector="{{AdminNewCustomerGroupSection.groupName}}" userInput="{{NotLoggedInCustomerGroup.code}}" stepKey="seeNotLoggedInTextInGroupName" /> + <assertElementContainsAttribute stepKey="checkIfGroupNameIsDisabled"> + <expectedResult selector="{{AdminNewCustomerGroupSection.groupName}}" attribute="disabled" type="string">true</expectedResult> + </assertElementContainsAttribute> + + <actionGroup ref="AdminLogoutActionGroup" stepKey="logoutFromAdmin"/> + </test> +</tests> diff --git a/app/code/Magento/Customer/Test/Mftf/Test/AllowedCountriesRestrictionApplyOnBackendTest.xml b/app/code/Magento/Customer/Test/Mftf/Test/AllowedCountriesRestrictionApplyOnBackendTest.xml index 194c6e6164f8c..60caaf64f05b7 100644 --- a/app/code/Magento/Customer/Test/Mftf/Test/AllowedCountriesRestrictionApplyOnBackendTest.xml +++ b/app/code/Magento/Customer/Test/Mftf/Test/AllowedCountriesRestrictionApplyOnBackendTest.xml @@ -23,7 +23,7 @@ <createData entity="SimpleProduct" stepKey="createSimpleProduct"> <requiredEntity createDataKey="createCategory"/> </createData> - <actionGroup ref="LoginActionGroup" stepKey="login"/> + <actionGroup ref="AdminLoginActionGroup" stepKey="login"/> <!--Create new website,store and store view--> <comment userInput="Create new website,store and store view" stepKey="createWebsite"/> <amOnPage url="{{AdminSystemStorePage.url}}" stepKey="goToAdminSystemStorePage"/> @@ -59,7 +59,7 @@ </actionGroup> <actionGroup ref="SetWebsiteCountryOptionsToDefaultActionGroup" stepKey="setCountryOptionsToDefault"/> <createData entity="CustomerAccountSharingSystemValue" stepKey="setAccountSharingToSystemValue"/> - <actionGroup ref="logout" stepKey="logout"/> + <actionGroup ref="AdminLogoutActionGroup" stepKey="logout"/> <magentoCLI command="indexer:reindex" stepKey="reindex"/> <magentoCLI command="cache:flush" stepKey="flushCache"/> </after> @@ -114,6 +114,9 @@ <waitForPageLoad stepKey="waitForCustomersGrid"/> <click selector="{{AdminDataGridHeaderSection.filters}}" stepKey="openFiltersSectionOnCustomersGrid"/> <executeJS function="var len = document.querySelectorAll('{{AdminCustomerFiltersSection.countryOptions}}').length; return len-1;" stepKey="countriesAmount2"/> - <assertEquals expected="($countriesAmount)" actual="($countriesAmount2)" stepKey="assertCountryAmounts"/> + <assertEquals stepKey="assertCountryAmounts"> + <actualResult type="const">($countriesAmount2)</actualResult> + <expectedResult type="const">($countriesAmount)</expectedResult> + </assertEquals> </test> </tests> diff --git a/app/code/Magento/Customer/Test/Mftf/Test/ChangeCustomerGroupTest.xml b/app/code/Magento/Customer/Test/Mftf/Test/ChangeCustomerGroupTest.xml deleted file mode 100644 index d9b71e1e6e9ba..0000000000000 --- a/app/code/Magento/Customer/Test/Mftf/Test/ChangeCustomerGroupTest.xml +++ /dev/null @@ -1,79 +0,0 @@ -<?xml version="1.0" encoding="UTF-8"?> -<!-- - /** - * Copyright © Magento, Inc. All rights reserved. - * See COPYING.txt for license details. - */ ---> - -<tests xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" - xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/testSchema.xsd"> - <test name="ChangingSingleCustomerGroupViaGrid"> - <annotations> - <title value="Change a single customer group via grid"/> - <description value="From the selection of All Customers select a single customer to change their group"/> - <severity value="MAJOR"/> - <testCaseId value="MC-10921"/> - <stories value="Change Customer Group"/> - <group value="customer"/> - <group value="mtf_migrated"/> - </annotations> - - <before> - <createData entity="Simple_US_Customer" stepKey="createCustomer"/> - - <actionGroup ref="LoginAsAdmin" stepKey="loginAsAdmin"/> - </before> - <after> - <!--Delete created product--> - <deleteData createDataKey="createCustomer" stepKey="deleteCustomer"/> - <actionGroup ref="NavigateToCustomerGroupPage" stepKey="navToCustomers"/> - <actionGroup ref="AdminDeleteCustomerGroupActionGroup" stepKey="deleteCustomerGroup"> - <argument name="customerGroupName" value="{{CustomerGroupChange.code}}"/> - </actionGroup> - - <actionGroup ref="logout" stepKey="logout"/> - </after> - - <actionGroup ref="AdminCreateCustomerGroupActionGroup" stepKey="createCustomerGroup"> - <argument name="groupName" value="{{CustomerGroupChange.code}}"/> - <argument name="taxClass" value="{{CustomerGroupChange.tax_class_name}}"/> - </actionGroup> - <actionGroup ref="NavigateToAllCustomerPage" stepKey="navToCustomers"/> - <actionGroup ref="AdminFilterCustomerByName" stepKey="filterCustomer"> - <argument name="customerName" value="{{Simple_US_Customer.fullname}}"/> - </actionGroup> - <actionGroup ref="AdminSelectCustomerByEmail" stepKey="selectCustomer"> - <argument name="customerEmail" value="$$createCustomer.email$$"/> - </actionGroup> - <actionGroup ref="SetCustomerGroupForSelectedCustomersViaGrid" stepKey="setCustomerGroup"> - <argument name="groupName" value="{{CustomerGroupChange.code}}"/> - </actionGroup> - <actionGroup ref="AdminFilterCustomerByName" stepKey="filterCustomerAfterGroupChange"> - <argument name="customerName" value="{{Simple_US_Customer.fullname}}"/> - </actionGroup> - <actionGroup ref="VerifyCustomerGroupForCustomer" stepKey="verifyCustomerGroupSet"> - <argument name="customerEmail" value="$$createCustomer.email$$"/> - <argument name="groupName" value="{{CustomerGroupChange.code}}"/> - </actionGroup> - </test> - - <test name="ChangingAllCustomerGroupViaGrid" extends="ChangingSingleCustomerGroupViaGrid"> - <annotations> - <title value="Change all customers' group via grid"/> - <description value="Select All customers to change their group"/> - <severity value="MAJOR"/> - <testCaseId value="MC-10924"/> - <stories value="Change Customer Group"/> - <group value="customer"/> - <group value="mtf_migrated"/> - <skip> - <issueId value="MC-17140"/> - </skip> - </annotations> - - <remove keyForRemoval="filterCustomer"/> - <actionGroup ref="ClearFiltersAdminDataGridActionGroup" stepKey="clearFilters" before="selectCustomer"/> - <actionGroup ref="AdminSelectAllCustomers" stepKey="selectCustomer"/> - </test> -</tests> diff --git a/app/code/Magento/Customer/Test/Mftf/Test/DeleteCustomerGroupTest.xml b/app/code/Magento/Customer/Test/Mftf/Test/DeleteCustomerGroupTest.xml index b19966e0102b6..e32ae04495fe5 100644 --- a/app/code/Magento/Customer/Test/Mftf/Test/DeleteCustomerGroupTest.xml +++ b/app/code/Magento/Customer/Test/Mftf/Test/DeleteCustomerGroupTest.xml @@ -25,11 +25,11 @@ <createData entity="UsCustomerAssignedToNewCustomerGroup" stepKey="customer"> <requiredEntity createDataKey="customerGroup"/> </createData> - <actionGroup ref="LoginAsAdmin" stepKey="loginAsAdmin"/> + <actionGroup ref="AdminLoginActionGroup" stepKey="loginAsAdmin"/> </before> <after> <deleteData createDataKey="customer" stepKey="deleteCustomer"/> - <actionGroup ref="logout" stepKey="logout"/> + <actionGroup ref="AdminLogoutActionGroup" stepKey="logout"/> </after> <!--Customer Group success delete message--> diff --git a/app/code/Magento/Customer/Test/Mftf/Test/EndToEndB2CLoggedInUserTest.xml b/app/code/Magento/Customer/Test/Mftf/Test/EndToEndB2CLoggedInUserTest.xml index bf8844b2cc7ab..2b6b1e7d1213c 100644 --- a/app/code/Magento/Customer/Test/Mftf/Test/EndToEndB2CLoggedInUserTest.xml +++ b/app/code/Magento/Customer/Test/Mftf/Test/EndToEndB2CLoggedInUserTest.xml @@ -11,26 +11,34 @@ <test name="EndToEndB2CLoggedInUserTest"> <annotations> <features value="End to End scenarios"/> - <stories value="B2C logged in user - MAGETWO-72524"/> + <stories value="B2C logged in user - MC-25681"/> <group value="e2e"/> <title value="You should be able to pass End to End B2C Logged In User scenario"/> <description value="New user signup and browses catalog, searches for product, adds product to cart, adds product to wishlist, compares products, uses coupon code and checks out."/> <severity value="CRITICAL"/> - <testCaseId value="MAGETWO-87653"/> - <skip> - <issueId value="MC-17140"/> - </skip> + <testCaseId value="MC-25681"/> + <group value="SearchEngineMysql"/> </annotations> <before> <resetCookie userInput="PHPSESSID" stepKey="resetCookieForCart"/> </before> <after> - <actionGroup ref="logout" stepKey="adminLogout"/> + <actionGroup ref="StorefrontCustomerLogoutActionGroup" stepKey="logoutCustomer"/> + <actionGroup ref="AdminLoginActionGroup" stepKey="loginAsAdmin"/> + <actionGroup ref="DeleteCustomerFromAdminActionGroup" stepKey="deleteCustomerFromAdmin"/> + <actionGroup ref="ClearFiltersAdminDataGridActionGroup" stepKey="clearProductsGridFilters"/> + <actionGroup ref="AdminLogoutActionGroup" stepKey="adminLogout"/> </after> <!-- Step 0: User signs up an account --> <comment userInput="Start of signing up user account" stepKey="startOfSigningUpUserAccount" /> - <actionGroup ref="SignUpNewUserFromStorefrontActionGroup" stepKey="SignUpNewUser"> - <argument name="Customer" value="CustomerEntityOne"/> + <actionGroup ref="StorefrontOpenCustomerAccountCreatePageActionGroup" stepKey="openCreateAccountPage"/> + <actionGroup ref="StorefrontFillCustomerAccountCreationFormActionGroup" stepKey="fillCreateAccountForm"> + <argument name="customer" value="CustomerEntityOne"/> + </actionGroup> + <actionGroup ref="StorefrontClickCreateAnAccountCustomerAccountCreationFormActionGroup" stepKey="submitCreateAccountForm"/> + <actionGroup ref="AssertMessageCustomerCreateAccountActionGroup" stepKey="seeSuccessMessage"> + <argument name="messageType" value="success"/> + <argument name="message" value="Thank you for registering with Main Website Store."/> </actionGroup> <comment userInput="End of signing up user account" stepKey="endOfSigningUpUserAccount" /> </test> diff --git a/app/code/Magento/Customer/Test/Mftf/Test/SearchByEmailInCustomerGridTest.xml b/app/code/Magento/Customer/Test/Mftf/Test/SearchByEmailInCustomerGridTest.xml index 5cce64495bbd2..d4351c8bcdc84 100644 --- a/app/code/Magento/Customer/Test/Mftf/Test/SearchByEmailInCustomerGridTest.xml +++ b/app/code/Magento/Customer/Test/Mftf/Test/SearchByEmailInCustomerGridTest.xml @@ -21,14 +21,14 @@ <before> <createData entity="Simple_US_Customer" stepKey="createFirstCustomer"/> <createData entity="Simple_US_Customer" stepKey="createSecondCustomer"/> - <actionGroup ref="LoginAsAdmin" stepKey="login"/> + <actionGroup ref="AdminLoginActionGroup" stepKey="login"/> </before> <after> <deleteData createDataKey="createFirstCustomer" stepKey="deleteFirstCustomer"/> <deleteData createDataKey="createSecondCustomer" stepKey="deleteSecondCustomer"/> <amOnPage url="{{AdminCustomerPage.url}}" stepKey="openCustomersGridPage"/> <actionGroup ref="AdminResetFilterInCustomerAddressGrid" stepKey="clearCustomerGridFilter"/> - <actionGroup ref="logout" stepKey="logout"/> + <actionGroup ref="AdminLogoutActionGroup" stepKey="logout"/> </after> <!--Step 1: Go to Customers > All Customers--> <amOnPage url="{{AdminCustomerPage.url}}" stepKey="openCustomersGridPage"/> diff --git a/app/code/Magento/Customer/Test/Mftf/Test/StorefrontAddCustomerAddressTest.xml b/app/code/Magento/Customer/Test/Mftf/Test/StorefrontAddCustomerAddressTest.xml deleted file mode 100644 index e2c55eb3962f2..0000000000000 --- a/app/code/Magento/Customer/Test/Mftf/Test/StorefrontAddCustomerAddressTest.xml +++ /dev/null @@ -1,121 +0,0 @@ -<?xml version="1.0" encoding="UTF-8"?> -<!-- - /** - * Copyright © Magento, Inc. All rights reserved. - * See COPYING.txt for license details. - */ ---> - -<tests xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" - xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/testSchema.xsd"> - <test name="StorefrontAddNewCustomerAddressTest"> - <annotations> - <features value="Customer address"/> - <stories value="Implement handling of large number of addresses on storefront Address book"/> - <title value="Storefront - My account - Address Book - add new address"/> - <description value="Storefront user should be able to create a new address via the storefront"/> - <severity value="MAJOR"/> - <testCaseId value="MAGETWO-97364"/> - <group value="customer"/> - <group value="create"/> - </annotations> - <before> - <createData entity="Simple_Customer_Without_Address" stepKey="createCustomer"/> - </before> - <after> - <deleteData createDataKey="createCustomer" stepKey="DeleteCustomer"/> - <actionGroup ref="logout" stepKey="adminLogout"/> - </after> - - <!--Log in to Storefront as Customer 1 --> - <actionGroup ref="LoginToStorefrontActionGroup" stepKey="signUp"> - <argument name="Customer" value="$$createCustomer$$"/> - </actionGroup> - <actionGroup ref="StorefrontAddNewCustomerAddressActionGroup" stepKey="AddNewAddress"> - <argument name="Address" value="US_Address_TX"/> - </actionGroup> - <see userInput="{{US_Address_TX.street[0]}}" - selector="{{StorefrontCustomerAddressesSection.defaultBillingAddress}}" stepKey="checkNewAddressesStreetOnDefaultBilling"/> - <see userInput="{{US_Address_TX.city}}" - selector="{{StorefrontCustomerAddressesSection.defaultBillingAddress}}" stepKey="checkNewAddressesCityOnDefaultBilling"/> - <see userInput="{{US_Address_TX.postcode}}" - selector="{{StorefrontCustomerAddressesSection.defaultBillingAddress}}" stepKey="checkNewAddressesPostcodeOnDefaultBilling"/> - <see userInput="{{US_Address_TX.street[0]}}" - selector="{{StorefrontCustomerAddressesSection.defaultShippingAddress}}" stepKey="checkNewAddressesStreetOnDefaultShipping"/> - <see userInput="{{US_Address_TX.city}}" - selector="{{StorefrontCustomerAddressesSection.defaultShippingAddress}}" stepKey="checkNewAddressesCityOnDefaultShipping"/> - <see userInput="{{US_Address_TX.postcode}}" - selector="{{StorefrontCustomerAddressesSection.defaultShippingAddress}}" stepKey="checkNewAddressesPostcodeOnDefaultShipping"/> - </test> - <test name="StorefrontAddCustomerDefaultAddressTest"> - <annotations> - <features value="Customer address"/> - <stories value="Implement handling of large number of addresses on storefront Address book"/> - <title value="Storefront - My account - Address Book - add new default billing/shipping address"/> - <description value="Storefront user should be able to create a new default address via the storefront"/> - <severity value="MAJOR"/> - <testCaseId value="MAGETWO-97364"/> - <group value="customer"/> - <group value="create"/> - </annotations> - <before> - <createData entity="Simple_US_Customer" stepKey="createCustomer"/> - </before> - <after> - <deleteData createDataKey="createCustomer" stepKey="DeleteCustomer"/> - </after> - - <!--Log in to Storefront as Customer 1 --> - <actionGroup ref="LoginToStorefrontActionGroup" stepKey="signUp"> - <argument name="Customer" value="$$createCustomer$$"/> - </actionGroup> - <actionGroup ref="StorefrontAddCustomerDefaultAddressActionGroup" stepKey="AddNewDefaultAddress"> - <argument name="Address" value="US_Address_TX"/> - </actionGroup> - <see userInput="{{US_Address_TX.street[0]}}" - selector="{{StorefrontCustomerAddressesSection.defaultBillingAddress}}" stepKey="checkNewAddressesStreetOnDefaultBilling"/> - <see userInput="{{US_Address_TX.city}}" - selector="{{StorefrontCustomerAddressesSection.defaultBillingAddress}}" stepKey="checkNewAddressesCityOnDefaultBilling"/> - <see userInput="{{US_Address_TX.postcode}}" - selector="{{StorefrontCustomerAddressesSection.defaultBillingAddress}}" stepKey="checkNewAddressesPostcodeOnDefaultBilling"/> - <see userInput="{{US_Address_TX.street[0]}}" - selector="{{StorefrontCustomerAddressesSection.defaultShippingAddress}}" stepKey="checkNewAddressesStreetOnDefaultShipping"/> - <see userInput="{{US_Address_TX.city}}" - selector="{{StorefrontCustomerAddressesSection.defaultShippingAddress}}" stepKey="checkNewAddressesCityOnDefaultShipping"/> - <see userInput="{{US_Address_TX.postcode}}" - selector="{{StorefrontCustomerAddressesSection.defaultShippingAddress}}" stepKey="checkNewAddressesPostcodeOnDefaultShipping"/> - </test> - <test name="StorefrontAddCustomerNonDefaultAddressTest"> - <annotations> - <features value="Customer address"/> - <stories value="Implement handling of large number of addresses on storefront Address book"/> - <title value="Storefront - My account - Address Book - add new non default billing/shipping address"/> - <description value="Storefront user should be able to create a new non default address via the storefront"/> - <severity value="MAJOR"/> - <testCaseId value="MAGETWO-97500"/> - <group value="customer"/> - <group value="create"/> - </annotations> - <before> - <createData entity="Simple_US_Customer_NY" stepKey="createCustomer"/> - </before> - <after> - <deleteData createDataKey="createCustomer" stepKey="DeleteCustomer"/> - <actionGroup ref="logout" stepKey="adminLogout"/> - </after> - - <!--Log in to Storefront as Customer 1 --> - <actionGroup ref="LoginToStorefrontActionGroup" stepKey="signUp"> - <argument name="Customer" value="$$createCustomer$$"/> - </actionGroup> - <actionGroup ref="StorefrontAddNewCustomerAddressActionGroup" stepKey="AddNewNonDefaultAddress"> - <argument name="Address" value="US_Address_TX"/> - </actionGroup> - <see userInput="{{US_Address_TX.street[0]}}" - selector="{{StorefrontCustomerAddressesSection.addressesList}}" stepKey="checkNewAddressesStreetOnDefaultShipping"/> - <see userInput="{{US_Address_TX.city}}" - selector="{{StorefrontCustomerAddressesSection.addressesList}}" stepKey="checkNewAddressesCityOnDefaultShipping"/> - <see userInput="{{US_Address_TX.postcode}}" - selector="{{StorefrontCustomerAddressesSection.addressesList}}" stepKey="checkNewAddressesPostcodeOnDefaultShipping"/> - </test> -</tests> \ No newline at end of file diff --git a/app/code/Magento/Customer/Test/Mftf/Test/StorefrontAddCustomerAddressTest/StorefrontAddCustomerDefaultAddressTest.xml b/app/code/Magento/Customer/Test/Mftf/Test/StorefrontAddCustomerAddressTest/StorefrontAddCustomerDefaultAddressTest.xml new file mode 100644 index 0000000000000..d4f851ee21c25 --- /dev/null +++ b/app/code/Magento/Customer/Test/Mftf/Test/StorefrontAddCustomerAddressTest/StorefrontAddCustomerDefaultAddressTest.xml @@ -0,0 +1,42 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + /** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +--> +<tests xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/testSchema.xsd"> + <test name="StorefrontAddCustomerDefaultAddressTest"> + <annotations> + <features value="Customer address"/> + <stories value="Implement handling of large number of addresses on storefront Address book"/> + <title value="Storefront - My account - Address Book - add new default billing/shipping address"/> + <description value="Storefront user should be able to create a new default address via the storefront"/> + <severity value="BLOCKER"/> + <testCaseId value="MAGETWO-97364"/> + <group value="customer"/> + <group value="create"/> + </annotations> + <before> + <createData entity="Simple_US_Customer" stepKey="createCustomer"/> + </before> + <after> + <deleteData createDataKey="createCustomer" stepKey="DeleteCustomer"/> + </after> + + <!--Log in to Storefront as Customer 1 --> + <actionGroup ref="LoginToStorefrontActionGroup" stepKey="signUp"> + <argument name="Customer" value="$$createCustomer$$"/> + </actionGroup> + <actionGroup ref="StorefrontAddCustomerDefaultAddressActionGroup" stepKey="AddNewDefaultAddress"> + <argument name="Address" value="US_Address_TX"/> + </actionGroup> + <see userInput="{{US_Address_TX.street[0]}}" selector="{{StorefrontCustomerAddressesSection.defaultBillingAddress}}" stepKey="checkNewAddressesStreetOnDefaultBilling"/> + <see userInput="{{US_Address_TX.city}}" selector="{{StorefrontCustomerAddressesSection.defaultBillingAddress}}" stepKey="checkNewAddressesCityOnDefaultBilling"/> + <see userInput="{{US_Address_TX.postcode}}" selector="{{StorefrontCustomerAddressesSection.defaultBillingAddress}}" stepKey="checkNewAddressesPostcodeOnDefaultBilling"/> + <see userInput="{{US_Address_TX.street[0]}}" selector="{{StorefrontCustomerAddressesSection.defaultShippingAddress}}" stepKey="checkNewAddressesStreetOnDefaultShipping"/> + <see userInput="{{US_Address_TX.city}}" selector="{{StorefrontCustomerAddressesSection.defaultShippingAddress}}" stepKey="checkNewAddressesCityOnDefaultShipping"/> + <see userInput="{{US_Address_TX.postcode}}" selector="{{StorefrontCustomerAddressesSection.defaultShippingAddress}}" stepKey="checkNewAddressesPostcodeOnDefaultShipping"/> + </test> +</tests> diff --git a/app/code/Magento/Customer/Test/Mftf/Test/StorefrontAddCustomerAddressTest/StorefrontAddCustomerNonDefaultAddressTest.xml b/app/code/Magento/Customer/Test/Mftf/Test/StorefrontAddCustomerAddressTest/StorefrontAddCustomerNonDefaultAddressTest.xml new file mode 100644 index 0000000000000..cec7f8460de5a --- /dev/null +++ b/app/code/Magento/Customer/Test/Mftf/Test/StorefrontAddCustomerAddressTest/StorefrontAddCustomerNonDefaultAddressTest.xml @@ -0,0 +1,40 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + /** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +--> +<tests xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/testSchema.xsd"> + <test name="StorefrontAddCustomerNonDefaultAddressTest"> + <annotations> + <features value="Customer address"/> + <stories value="Implement handling of large number of addresses on storefront Address book"/> + <title value="Storefront - My account - Address Book - add new non default billing/shipping address"/> + <description value="Storefront user should be able to create a new non default address via the storefront"/> + <severity value="BLOCKER"/> + <testCaseId value="MAGETWO-97500"/> + <group value="customer"/> + <group value="create"/> + </annotations> + <before> + <createData entity="Simple_US_Customer_NY" stepKey="createCustomer"/> + </before> + <after> + <deleteData createDataKey="createCustomer" stepKey="DeleteCustomer"/> + <actionGroup ref="AdminLogoutActionGroup" stepKey="adminLogout"/> + </after> + + <!--Log in to Storefront as Customer 1 --> + <actionGroup ref="LoginToStorefrontActionGroup" stepKey="signUp"> + <argument name="Customer" value="$$createCustomer$$"/> + </actionGroup> + <actionGroup ref="StorefrontAddNewCustomerAddressActionGroup" stepKey="AddNewNonDefaultAddress"> + <argument name="Address" value="US_Address_TX"/> + </actionGroup> + <see userInput="{{US_Address_TX.street[0]}}" selector="{{StorefrontCustomerAddressesSection.addressesList}}" stepKey="checkNewAddressesStreetOnDefaultShipping"/> + <see userInput="{{US_Address_TX.city}}" selector="{{StorefrontCustomerAddressesSection.addressesList}}" stepKey="checkNewAddressesCityOnDefaultShipping"/> + <see userInput="{{US_Address_TX.postcode}}" selector="{{StorefrontCustomerAddressesSection.addressesList}}" stepKey="checkNewAddressesPostcodeOnDefaultShipping"/> + </test> +</tests> diff --git a/app/code/Magento/Customer/Test/Mftf/Test/StorefrontAddCustomerAddressTest/StorefrontAddNewCustomerAddressTest.xml b/app/code/Magento/Customer/Test/Mftf/Test/StorefrontAddCustomerAddressTest/StorefrontAddNewCustomerAddressTest.xml new file mode 100644 index 0000000000000..c3c8bd5d7c40e --- /dev/null +++ b/app/code/Magento/Customer/Test/Mftf/Test/StorefrontAddCustomerAddressTest/StorefrontAddNewCustomerAddressTest.xml @@ -0,0 +1,43 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + /** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +--> +<tests xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/testSchema.xsd"> + <test name="StorefrontAddNewCustomerAddressTest"> + <annotations> + <features value="Customer address"/> + <stories value="Implement handling of large number of addresses on storefront Address book"/> + <title value="Storefront - My account - Address Book - add new address"/> + <description value="Storefront user should be able to create a new address via the storefront"/> + <severity value="BLOCKER"/> + <testCaseId value="MAGETWO-97364"/> + <group value="customer"/> + <group value="create"/> + </annotations> + <before> + <createData entity="Simple_Customer_Without_Address" stepKey="createCustomer"/> + </before> + <after> + <deleteData createDataKey="createCustomer" stepKey="DeleteCustomer"/> + <actionGroup ref="AdminLogoutActionGroup" stepKey="adminLogout"/> + </after> + + <!--Log in to Storefront as Customer 1 --> + <actionGroup ref="LoginToStorefrontActionGroup" stepKey="signUp"> + <argument name="Customer" value="$$createCustomer$$"/> + </actionGroup> + <actionGroup ref="StorefrontAddNewCustomerAddressActionGroup" stepKey="AddNewAddress"> + <argument name="Address" value="US_Address_TX"/> + </actionGroup> + <see userInput="{{US_Address_TX.street[0]}}" selector="{{StorefrontCustomerAddressesSection.defaultBillingAddress}}" stepKey="checkNewAddressesStreetOnDefaultBilling"/> + <see userInput="{{US_Address_TX.city}}" selector="{{StorefrontCustomerAddressesSection.defaultBillingAddress}}" stepKey="checkNewAddressesCityOnDefaultBilling"/> + <see userInput="{{US_Address_TX.postcode}}" selector="{{StorefrontCustomerAddressesSection.defaultBillingAddress}}" stepKey="checkNewAddressesPostcodeOnDefaultBilling"/> + <see userInput="{{US_Address_TX.street[0]}}" selector="{{StorefrontCustomerAddressesSection.defaultShippingAddress}}" stepKey="checkNewAddressesStreetOnDefaultShipping"/> + <see userInput="{{US_Address_TX.city}}" selector="{{StorefrontCustomerAddressesSection.defaultShippingAddress}}" stepKey="checkNewAddressesCityOnDefaultShipping"/> + <see userInput="{{US_Address_TX.postcode}}" selector="{{StorefrontCustomerAddressesSection.defaultShippingAddress}}" stepKey="checkNewAddressesPostcodeOnDefaultShipping"/> + </test> +</tests> diff --git a/app/code/Magento/Customer/Test/Mftf/Test/StorefrontAddProductToCartWithExpiredSessionTest.xml b/app/code/Magento/Customer/Test/Mftf/Test/StorefrontAddProductToCartWithExpiredSessionTest.xml new file mode 100644 index 0000000000000..73d050cfa09b9 --- /dev/null +++ b/app/code/Magento/Customer/Test/Mftf/Test/StorefrontAddProductToCartWithExpiredSessionTest.xml @@ -0,0 +1,42 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + /** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +--> + +<tests xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/testSchema.xsd"> + <test name="StorefrontAddProductToCartWithExpiredSessionTest"> + <annotations> + <title value="Adding a product to cart from category page with an expired session"/> + <description value="Adding a product to cart from category page with an expired session"/> + <features value="Module/ Catalog"/> + <severity value="MAJOR"/> + <testCaseId value="MAGETWO-93289"/> + <stories value="MAGETWO-66666: Adding a product to cart from category page with an expired session does not allow product to be added"/> + <group value="customer"/> + </annotations> + + <before> + <createData entity="_defaultCategory" stepKey="createCategory"/> + <createData entity="_defaultProduct" stepKey="createSimpleProduct"> + <requiredEntity createDataKey="createCategory"/> + </createData> + <magentoCron stepKey="runCronReindex" groups="index"/> + </before> + <after> + <deleteData createDataKey="createSimpleProduct" stepKey="deleteProduct"/> + <deleteData createDataKey="createCategory" stepKey="deleteCategory"/> + </after> + + <actionGroup ref="StorefrontOpenProductEntityPageActionGroup" stepKey="openProductPage"> + <argument name="product" value="$$createSimpleProduct$$"/> + </actionGroup> + <actionGroup ref="StorefrontFakeBrokenSessionActionGroup" stepKey="fakeBrokenSession"/> + <actionGroup ref="StorefrontProductPageAddSimpleProductToCartActionGroup" stepKey="addProductToCart"/> + <actionGroup ref="StorefrontAssertProductAddToCartErrorMessageActionGroup" stepKey="assertFailure"> + <argument name="message" value="Your session has expired"/> + </actionGroup> + </test> +</tests> diff --git a/app/code/Magento/Customer/Test/Mftf/Test/StorefrontCheckTaxAddingValidVATIdTest.xml b/app/code/Magento/Customer/Test/Mftf/Test/StorefrontCheckTaxAddingValidVATIdTest.xml index 4c35cdbdb7cbb..80cdeadb391da 100644 --- a/app/code/Magento/Customer/Test/Mftf/Test/StorefrontCheckTaxAddingValidVATIdTest.xml +++ b/app/code/Magento/Customer/Test/Mftf/Test/StorefrontCheckTaxAddingValidVATIdTest.xml @@ -20,7 +20,7 @@ </annotations> <before> <!--Log In--> - <actionGroup ref="LoginAsAdmin" stepKey="logIn"/> + <actionGroup ref="AdminLoginActionGroup" stepKey="logIn"/> <!--Create category--> <createData entity="_defaultCategory" stepKey="createCategory"/> <!--Create product--> @@ -67,12 +67,14 @@ </actionGroup> <!--Register customer on storefront--> - <actionGroup ref="SignUpNewCustomerStorefrontActionGroup" stepKey="createAnAccount"> - <argument name="Customer" value="CustomerEntityOne"/> + <actionGroup ref="StorefrontOpenCustomerAccountCreatePageActionGroup" stepKey="openCreateAccountPage"/> + <actionGroup ref="StorefrontFillCustomerAccountCreationFormActionGroup" stepKey="fillCreateAccountForm"> + <argument name="customer" value="CustomerEntityOne"/> </actionGroup> + <actionGroup ref="StorefrontClickCreateAnAccountCustomerAccountCreationFormActionGroup" stepKey="submitCreateAccountForm"/> <!--Go to My account > Address book--> - <actionGroup ref="EnterCustomerAddressInfoFillState" stepKey="enterAddressInfo"> + <actionGroup ref="EnterCustomerAddressInfoFillStateActionGroup" stepKey="enterAddressInfo"> <argument name="Address" value="UK_Simple_Address"/> </actionGroup> @@ -143,7 +145,7 @@ </actionGroup> <!--Log Out--> - <actionGroup ref="logout" stepKey="logout"/> + <actionGroup ref="AdminLogoutActionGroup" stepKey="logout"/> </after> </test> </tests> diff --git a/app/code/Magento/Customer/Test/Mftf/Test/StorefrontClearAllCompareProductsTest.xml b/app/code/Magento/Customer/Test/Mftf/Test/StorefrontClearAllCompareProductsTest.xml index 40b05153f1a74..317f2c2825ca7 100644 --- a/app/code/Magento/Customer/Test/Mftf/Test/StorefrontClearAllCompareProductsTest.xml +++ b/app/code/Magento/Customer/Test/Mftf/Test/StorefrontClearAllCompareProductsTest.xml @@ -18,7 +18,7 @@ <group value="catalog"/> <group value="mtf_migrated"/> </annotations> - + <before> <magentoCLI stepKey="addDownloadableDomain" command="downloadable:domains:add static.magento.com"/> <!-- Create Simple Customer --> @@ -106,12 +106,12 @@ <magentoCLI command="cache:flush" stepKey="flushCache"/> <!-- Login --> - <actionGroup ref="LoginAsAdmin" stepKey="loginAsAdmin1"/> + <actionGroup ref="AdminLoginActionGroup" stepKey="loginAsAdmin1"/> </before> <after> <magentoCLI stepKey="removeDownloadableDomain" command="downloadable:domains:remove static.magento.com"/> <!-- Logout --> - <actionGroup ref="logout" stepKey="logoutOfAdmin1"/> + <actionGroup ref="AdminLogoutActionGroup" stepKey="logoutOfAdmin1"/> <!-- Delete Created Entities --> <deleteData createDataKey="createSimpleCustomer1" stepKey="deleteSimpleCustomer1"/> @@ -124,6 +124,10 @@ <deleteData createDataKey="createBundleProduct1" stepKey="deleteBundleProduct1"/> <deleteData createDataKey="createGroupedProduct1" stepKey="deleteGroupedProduct1"/> <deleteData createDataKey="createDownloadableProduct1" stepKey="deleteDownloadableProduct1"/> + <deleteData createDataKey="createConfigProductAttribute" stepKey="deleteConfigProductAttribute"/> + + <!-- Reindex invalidated indices after product attribute has been created/deleted --> + <magentoCron groups="index" stepKey="reindexInvalidatedIndices"/> </after> <actionGroup ref="LoginToStorefrontActionGroup" stepKey="loginAsCustomer1"> diff --git a/app/code/Magento/Customer/Test/Mftf/Test/StorefrontCreateCustomerTest.xml b/app/code/Magento/Customer/Test/Mftf/Test/StorefrontCreateCustomerTest.xml index 7d51f97f2463a..0d64ceb545831 100644 --- a/app/code/Magento/Customer/Test/Mftf/Test/StorefrontCreateCustomerTest.xml +++ b/app/code/Magento/Customer/Test/Mftf/Test/StorefrontCreateCustomerTest.xml @@ -12,19 +12,22 @@ <annotations> <features value="Customer"/> <stories value="Create a Customer via the Storefront"/> - <title value="Admin should be able to create a customer via the storefront"/> - <description value="Admin should be able to create a customer via the storefront"/> - <severity value="CRITICAL"/> + <title value="As a Customer I should be able to register an account on Storefront"/> + <description value="As a Customer I should be able to register an account on Storefront"/> + <severity value="BLOCKER"/> <testCaseId value="MAGETWO-23546"/> <group value="customer"/> <group value="create"/> </annotations> - <after> - <actionGroup ref="logout" stepKey="adminLogout"/> - </after> - <actionGroup ref="SignUpNewUserFromStorefrontActionGroup" stepKey="SignUpNewUser"> - <argument name="Customer" value="CustomerEntityOne"/> + <actionGroup ref="StorefrontOpenCustomerAccountCreatePageActionGroup" stepKey="openCreateAccountPage"/> + <actionGroup ref="StorefrontFillCustomerAccountCreationFormActionGroup" stepKey="fillCreateAccountForm"> + <argument name="customer" value="Simple_US_Customer"/> + </actionGroup> + <actionGroup ref="StorefrontClickCreateAnAccountCustomerAccountCreationFormActionGroup" stepKey="submitCreateAccountForm"/> + <actionGroup ref="AssertMessageCustomerCreateAccountActionGroup" stepKey="seeSuccessMessage"> + <argument name="messageType" value="success"/> + <argument name="message" value="Thank you for registering with Main Website Store."/> </actionGroup> </test> -</tests> \ No newline at end of file +</tests> diff --git a/app/code/Magento/Customer/Test/Mftf/Test/StorefrontCreateCustomerWithDateOfBirthTest.xml b/app/code/Magento/Customer/Test/Mftf/Test/StorefrontCreateCustomerWithDateOfBirthTest.xml new file mode 100644 index 0000000000000..8cd35f4147636 --- /dev/null +++ b/app/code/Magento/Customer/Test/Mftf/Test/StorefrontCreateCustomerWithDateOfBirthTest.xml @@ -0,0 +1,43 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + /** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +--> + +<tests xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/testSchema.xsd"> + <test name="StorefrontCreateCustomerWithDateOfBirthTest"> + <annotations> + <features value="Customer"/> + <stories value="Create a Customer via the Storefront"/> + <title value="Customer should be able to create an account with date of birth via the storefront"/> + <description value="Customer should be able to create an account with date of birth via the storefront"/> + <severity value="CRITICAL"/> + <testCaseId value="MC-32413"/> + <group value="customer"/> + <group value="create"/> + </annotations> + <before> + <actionGroup ref="AdminLoginActionGroup" stepKey="login"/> + <actionGroup ref="AdminCustomerShowDateOfBirthActionGroup" stepKey="showDateOfBirth"> + <argument name="value" value="{{ShowDateOfBirth.required}}"/> + </actionGroup> + </before> + <after> + <actionGroup ref="AdminCustomerShowDateOfBirthActionGroup" stepKey="hideDateOfBirth"> + <argument name="value" value="{{ShowDateOfBirth.no}}"/> + </actionGroup> + <actionGroup ref="AdminLogoutActionGroup" stepKey="logout"/> + </after> + <actionGroup ref="StorefrontCustomerCreateAccountWithDateOfBirthActionGroup" stepKey="SignUpNewUser"> + <argument name="Customer" value="CustomerEntityOne"/> + <argument name="dob" value="{{EN_US_DATE.short4DigitYear}}"/> + </actionGroup> + <magentoCLI command="indexer:reindex" stepKey="reindex"/> + <actionGroup ref="DeleteCustomerByEmailActionGroup" stepKey="deleteNewUser"> + <argument name="email" value="{{CustomerEntityOne.email}}"/> + </actionGroup> + </test> +</tests> diff --git a/app/code/Magento/Customer/Test/Mftf/Test/StorefrontCreateExistingCustomerTest.xml b/app/code/Magento/Customer/Test/Mftf/Test/StorefrontCreateExistingCustomerTest.xml index 952ac235d92a4..07ac295e5cce0 100644 --- a/app/code/Magento/Customer/Test/Mftf/Test/StorefrontCreateExistingCustomerTest.xml +++ b/app/code/Magento/Customer/Test/Mftf/Test/StorefrontCreateExistingCustomerTest.xml @@ -8,29 +8,28 @@ <tests xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/testSchema.xsd"> - <test name="StorefrontCreateExistingCustomerTest"> + <test name="StorefrontCreateExistingCustomerTest" extends="StorefrontCreateCustomerTest"> <annotations> <features value="Customer"/> <stories value="Customer Registration"/> - <title value="Attempt to register customer on storefront with existing email"/> - <description value="Attempt to register customer on storefront with existing email"/> + <title value="As a Customer I should not be able to register an account using already registered e-mail"/> + <description value="As a Customer I should not be able to register an account using already registered e-mail"/> <testCaseId value="MC-10907"/> <severity value="MAJOR"/> <group value="customers"/> <group value="mtf_migrated"/> </annotations> <before> - <createData entity="Simple_US_Customer" stepKey="customer"/> + <createData entity="Simple_US_Customer" stepKey="createCustomer"/> </before> <after> - <deleteData createDataKey="customer" stepKey="deleteCustomer"/> + <deleteData createDataKey="createCustomer" stepKey="deleteCustomer"/> </after> - <actionGroup ref="StorefrontOpenCustomerAccountCreatePageActionGroup" stepKey="openCreateAccountPage"/> <actionGroup ref="StorefrontFillCustomerAccountCreationFormActionGroup" stepKey="fillCreateAccountForm"> - <argument name="customer" value="$$customer$$"/> + <argument name="customer" value="$$createCustomer$$"/> </actionGroup> - <actionGroup ref="StorefrontClickCreateAnAccountCustomerAccountCreationFormActionGroup" stepKey="submitCreateAccountForm"/> + <remove keyForRemoval="seeSuccessMessage"/> <actionGroup ref="AssertMessageCustomerCreateAccountActionGroup" stepKey="seeErrorMessage"> <argument name="messageType" value="error"/> <argument name="message" value="There is already an account with this email address."/> diff --git a/app/code/Magento/Customer/Test/Mftf/Test/StorefrontCustomerSubscribeToNewsletterTest.xml b/app/code/Magento/Customer/Test/Mftf/Test/StorefrontCustomerSubscribeToNewsletterTest.xml new file mode 100644 index 0000000000000..4658b162dcc55 --- /dev/null +++ b/app/code/Magento/Customer/Test/Mftf/Test/StorefrontCustomerSubscribeToNewsletterTest.xml @@ -0,0 +1,34 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + /** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +--> + +<tests xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/testSchema.xsd"> + <test name="StorefrontCustomerSubscribeToNewsletterTest"> + <annotations> + <features value="Newsletter Subscription"/> + <stories value="Subscribe To Newsletter Subscription on StoreFront"/> + <title value="StoreFront Customer Newsletter Subscription"/> + <description value="Customer can be subscribed to Newsletter Subscription on StoreFront"/> + </annotations> + <before> + <createData entity="Simple_US_Customer" stepKey="createCustomer"/> + </before> + <after> + <deleteData createDataKey="createCustomer" stepKey="deleteCreatedCustomer"/> + </after> + + <actionGroup ref="LoginToStorefrontActionGroup" stepKey="loginAsCustomer"> + <argument name="Customer" value="$$createCustomer$$"/> + </actionGroup> + <actionGroup ref="StorefrontCustomerNavigateToNewsletterPageActionGroup" stepKey="navigateToNewsletterPage"/> + <actionGroup ref="StorefrontCustomerUpdateGeneralSubscriptionActionGroup" stepKey="subscribeToNewsletter"/> + <actionGroup ref="AssertStorefrontCustomerMessagesActionGroup" stepKey="assertMessage"> + <argument name="message" value="We have saved your subscription."/> + </actionGroup> + </test> +</tests> diff --git a/app/code/Magento/Customer/Test/Mftf/Test/StorefrontDeleteCustomerAddressTest.xml b/app/code/Magento/Customer/Test/Mftf/Test/StorefrontDeleteCustomerAddressTest.xml index 7a96616885468..fb08a07a7c319 100644 --- a/app/code/Magento/Customer/Test/Mftf/Test/StorefrontDeleteCustomerAddressTest.xml +++ b/app/code/Magento/Customer/Test/Mftf/Test/StorefrontDeleteCustomerAddressTest.xml @@ -27,7 +27,7 @@ <fillField stepKey="fillEmail" userInput="$$createCustomer.email$$" selector="{{StorefrontCustomerSignInFormSection.emailField}}"/> <fillField stepKey="fillPassword" userInput="$$createCustomer.password$$" selector="{{StorefrontCustomerSignInFormSection.passwordField}}"/> <click stepKey="clickSignInAccountButton" selector="{{StorefrontCustomerSignInFormSection.signInAccountButton}}"/> - <actionGroup ref="EnterCustomerAddressInfo" stepKey="enterAddressInfo"> + <actionGroup ref="EnterCustomerAddressInfoActionGroup" stepKey="enterAddressInfo"> <argument name="Address" value="US_Address_NY"/> </actionGroup> <see userInput="You saved the address." stepKey="verifyAddressCreated"/> diff --git a/app/code/Magento/Customer/Test/Mftf/Test/StorefrontForgotPasswordTest.xml b/app/code/Magento/Customer/Test/Mftf/Test/StorefrontForgotPasswordTest.xml deleted file mode 100644 index 0c6c7dd9b800c..0000000000000 --- a/app/code/Magento/Customer/Test/Mftf/Test/StorefrontForgotPasswordTest.xml +++ /dev/null @@ -1,43 +0,0 @@ -<?xml version="1.0" encoding="UTF-8"?> -<!-- - /** - * Copyright © Magento, Inc. All rights reserved. - * See COPYING.txt for license details. - */ ---> - -<tests xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" - xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/testSchema.xsd"> - <test name="StorefrontCustomerForgotPasswordTest"> - <annotations> - <features value="Customer"/> - <stories value="Customer Login"/> - <title value="Forgot Password on Storefront validates customer email input"/> - <description value="Forgot Password on Storefront validates customer email input"/> - <severity value="CRITICAL"/> - <testCaseId value="MC-13679"/> - <group value="Customer"/> - <group value="mtf_migrated"/> - </annotations> - <before> - <magentoCLI command="config:set {{StorefrontCustomerCaptchaDisableConfigData.path}} {{StorefrontCustomerCaptchaDisableConfigData.value}}" stepKey="disableCaptcha"/> - <magentoCLI command="config:set customer/password/max_number_password_reset_requests 30" stepKey="increaseLimit"/> - <magentoCLI command="config:set customer/password/min_time_between_password_reset_requests 1" stepKey="reduceTimeout"/> - <createData stepKey="customer" entity="Simple_US_Customer"/> - </before> - <after> - <magentoCLI command="config:set {{StorefrontCustomerCaptchaEnableConfigData.path}} {{StorefrontCustomerCaptchaEnableConfigData.value}}" stepKey="enableCaptcha"/> - <deleteData stepKey="deleteCustomer" createDataKey="customer" /> - </after> - - <amOnPage stepKey="amOnSignInPage" url="{{StorefrontCustomerSignInPage.url}}"/> - <fillField stepKey="fillEmail" userInput="$$customer.email$$" selector="{{StorefrontCustomerSignInFormSection.emailField}}"/> - <fillField stepKey="fillPassword" userInput="something" selector="{{StorefrontCustomerSignInFormSection.passwordField}}"/> - <click stepKey="clickForgotPasswordLink" selector="{{StorefrontCustomerSignInFormSection.forgotPasswordLink}}"/> - <see stepKey="seePageTitle" userInput="Forgot Your Password" selector="{{StorefrontForgotPasswordSection.pageTitle}}"/> - <fillField stepKey="enterEmail" userInput="$$customer.email$$" selector="{{StorefrontForgotPasswordSection.email}}"/> - <click stepKey="clickResetPassword" selector="{{StorefrontForgotPasswordSection.resetMyPasswordButton}}"/> - <seeInCurrentUrl stepKey="seeInSignInPage" url="account/login"/> - <see stepKey="seeSuccessMessage" userInput="If there is an account associated with $$customer.email$$ you will receive an email with a link to reset your password." selector="{{StorefrontCustomerLoginMessagesSection.successMessage}}"/> - </test> -</tests> diff --git a/app/code/Magento/Customer/Test/Mftf/Test/StorefrontResetCustomerPasswordFailedTest.xml b/app/code/Magento/Customer/Test/Mftf/Test/StorefrontResetCustomerPasswordFailedTest.xml index 5d0eec935e192..a1fa866a0675f 100644 --- a/app/code/Magento/Customer/Test/Mftf/Test/StorefrontResetCustomerPasswordFailedTest.xml +++ b/app/code/Magento/Customer/Test/Mftf/Test/StorefrontResetCustomerPasswordFailedTest.xml @@ -21,9 +21,16 @@ <group value="mtf_migrated"/> </annotations> <before> + <magentoCLI command="config:set {{StorefrontCustomerCaptchaDisableConfigData.path}} {{StorefrontCustomerCaptchaDisableConfigData.value}}" stepKey="disableCaptcha"/> + <magentoCLI command="config:set customer/password/password_reset_protection_type 3" stepKey="setProtectionOnEmail"/> + <magentoCLI command="config:set customer/password/min_time_between_password_reset_requests 30" stepKey="increaseThresholdBetweenRequests"/> <createData stepKey="customer" entity="Simple_US_Customer"/> </before> <after> + <!-- Preferred `Use system value` which is not available from CLI --> + <magentoCLI command="config:set customer/password/password_reset_protection_type 1" stepKey="setDefaultProtection"/> + <magentoCLI command="config:set customer/password/min_time_between_password_reset_requests 30" stepKey="setDefaultThresholdBetweenRequests"/> + <magentoCLI command="config:set {{StorefrontCustomerCaptchaEnableConfigData.path}} {{StorefrontCustomerCaptchaEnableConfigData.value}}" stepKey="enableCaptcha"/> <deleteData stepKey="deleteCustomer" createDataKey="customer" /> </after> diff --git a/app/code/Magento/Customer/Test/Mftf/Test/StorefrontResetCustomerPasswordSuccessTest.xml b/app/code/Magento/Customer/Test/Mftf/Test/StorefrontResetCustomerPasswordSuccessTest.xml new file mode 100644 index 0000000000000..63f372a080acc --- /dev/null +++ b/app/code/Magento/Customer/Test/Mftf/Test/StorefrontResetCustomerPasswordSuccessTest.xml @@ -0,0 +1,44 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + /** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +--> + +<tests xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/testSchema.xsd"> + <test name="StorefrontResetCustomerPasswordSuccessTest"> + <annotations> + <features value="Customer"/> + <stories value="Customer Login"/> + <title value="Forgot Password on Storefront validates customer email input"/> + <description value="Forgot Password on Storefront validates customer email input"/> + <severity value="BLOCKER"/> + <testCaseId value="MC-13679"/> + <group value="Customer"/> + <group value="mtf_migrated"/> + </annotations> + <before> + <magentoCLI command="config:set {{StorefrontCustomerCaptchaDisableConfigData.path}} {{StorefrontCustomerCaptchaDisableConfigData.value}}" stepKey="disableCaptcha"/> + <magentoCLI command="config:set customer/password/password_reset_protection_type 3" stepKey="setProtectionOnEmail"/> + <magentoCLI command="config:set customer/password/max_number_password_reset_requests 30" stepKey="increaseLimit"/> + <magentoCLI command="config:set customer/password/min_time_between_password_reset_requests 0" stepKey="reduceTimeout"/> + <createData stepKey="customer" entity="Simple_US_Customer"/> + </before> + <after> + <!-- Preferred `Use system value` which is not available from CLI --> + <magentoCLI command="config:set {{StorefrontCustomerCaptchaEnableConfigData.path}} {{StorefrontCustomerCaptchaEnableConfigData.value}}" stepKey="enableCaptcha"/> + <magentoCLI command="config:set customer/password/password_reset_protection_type 1" stepKey="setDefaultProtection"/> + <deleteData stepKey="deleteCustomer" createDataKey="customer" /> + </after> + + <actionGroup ref="StorefrontCustomerResetPasswordActionGroup" stepKey="resetPasswordFirstAttempt"> + <argument name="email" value="$$customer.email$$" /> + </actionGroup> + <actionGroup ref="AssertCustomerResetPasswordActionGroup" stepKey="seePageWithSuccessMessage"> + <argument name="url" value="{{StorefrontCustomerSignInPage.url}}"/> + <argument name="message" value="If there is an account associated with $$customer.email$$ you will receive an email with a link to reset your password."/> + </actionGroup> + </test> +</tests> diff --git a/app/code/Magento/Customer/Test/Mftf/Test/StorefrontUpdateCustomerAddressBelgiumTest.xml b/app/code/Magento/Customer/Test/Mftf/Test/StorefrontUpdateCustomerAddressBelgiumTest.xml index d36d640c5ad17..b800b7870b5c5 100644 --- a/app/code/Magento/Customer/Test/Mftf/Test/StorefrontUpdateCustomerAddressBelgiumTest.xml +++ b/app/code/Magento/Customer/Test/Mftf/Test/StorefrontUpdateCustomerAddressBelgiumTest.xml @@ -19,32 +19,38 @@ </annotations> <before> - <actionGroup ref = "LoginAsAdmin" stepKey="loginAsAdmin"/> - <actionGroup ref="SignUpNewUserFromStorefrontActionGroup" stepKey="SignUpNewUser"> - <argument name="Customer" value="CustomerEntityOne"/> + <actionGroup ref="AdminLoginActionGroup" stepKey="loginAsAdmin"/> + <actionGroup ref="StorefrontOpenCustomerAccountCreatePageActionGroup" stepKey="openCreateAccountPage"/> + <actionGroup ref="StorefrontFillCustomerAccountCreationFormActionGroup" stepKey="fillCreateAccountForm"> + <argument name="customer" value="CustomerEntityOne"/> + </actionGroup> + <actionGroup ref="StorefrontClickCreateAnAccountCustomerAccountCreationFormActionGroup" stepKey="submitCreateAccountForm"/> + <actionGroup ref="AssertMessageCustomerCreateAccountActionGroup" stepKey="seeSuccessMessage"> + <argument name="messageType" value="success"/> + <argument name="message" value="Thank you for registering with Main Website Store."/> </actionGroup> </before> <after> <actionGroup ref="DeleteCustomerByEmailActionGroup" stepKey="deleteNewUser"> <argument name="email" value="{{CustomerEntityOne.email}}"/> </actionGroup> - <actionGroup ref="logout" stepKey="logout"/> + <actionGroup ref="AdminLogoutActionGroup" stepKey="logout"/> </after> <!--Update customer address Belgium in storefront--> - <actionGroup ref="EnterCustomerAddressInfo" stepKey="enterAddress"> + <actionGroup ref="EnterCustomerAddressInfoActionGroup" stepKey="enterAddress"> <argument name="Address" value="updateCustomerBelgiumAddress"/> </actionGroup> <!--Verify customer address save success message--> <see selector="{{AdminCustomerMessagesSection.successMessage}}" userInput="You saved the address." stepKey="seeAssertCustomerAddressSuccessSaveMessage"/> <!--Verify customer default billing address--> - <actionGroup ref="VerifyCustomerBillingAddressWithState" stepKey="verifyBillingAddress"> + <actionGroup ref="VerifyCustomerBillingAddressWithStateActionGroup" stepKey="verifyBillingAddress"> <argument name="address" value="updateCustomerBelgiumAddress"/> </actionGroup> <!--Verify customer default shipping address--> - <actionGroup ref="VerifyCustomerShippingAddressWithState" stepKey="verifyShippingAddress"> + <actionGroup ref="VerifyCustomerShippingAddressWithStateActionGroup" stepKey="verifyShippingAddress"> <argument name="address" value="updateCustomerBelgiumAddress"/> </actionGroup> diff --git a/app/code/Magento/Customer/Test/Mftf/Test/StorefrontUpdateCustomerAddressChinaTest.xml b/app/code/Magento/Customer/Test/Mftf/Test/StorefrontUpdateCustomerAddressChinaTest.xml index 285de8d777b48..41e0a5d1b7793 100644 --- a/app/code/Magento/Customer/Test/Mftf/Test/StorefrontUpdateCustomerAddressChinaTest.xml +++ b/app/code/Magento/Customer/Test/Mftf/Test/StorefrontUpdateCustomerAddressChinaTest.xml @@ -19,32 +19,38 @@ </annotations> <before> - <actionGroup ref = "LoginAsAdmin" stepKey="loginAsAdmin"/> - <actionGroup ref="SignUpNewUserFromStorefrontActionGroup" stepKey="SignUpNewUser"> - <argument name="Customer" value="CustomerEntityOne"/> + <actionGroup ref="AdminLoginActionGroup" stepKey="loginAsAdmin"/> + <actionGroup ref="StorefrontOpenCustomerAccountCreatePageActionGroup" stepKey="openCreateAccountPage"/> + <actionGroup ref="StorefrontFillCustomerAccountCreationFormActionGroup" stepKey="fillCreateAccountForm"> + <argument name="customer" value="CustomerEntityOne"/> + </actionGroup> + <actionGroup ref="StorefrontClickCreateAnAccountCustomerAccountCreationFormActionGroup" stepKey="submitCreateAccountForm"/> + <actionGroup ref="AssertMessageCustomerCreateAccountActionGroup" stepKey="seeSuccessMessage"> + <argument name="messageType" value="success"/> + <argument name="message" value="Thank you for registering with Main Website Store."/> </actionGroup> </before> <after> <actionGroup ref="DeleteCustomerByEmailActionGroup" stepKey="deleteNewUser"> <argument name="email" value="{{CustomerEntityOne.email}}"/> </actionGroup> - <actionGroup ref="logout" stepKey="logout"/> + <actionGroup ref="AdminLogoutActionGroup" stepKey="logout"/> </after> <!--Update customer address in storefront--> - <actionGroup ref="EnterCustomerAddressInfo" stepKey="enterAddress"> + <actionGroup ref="EnterCustomerAddressInfoActionGroup" stepKey="enterAddress"> <argument name="Address" value="updateCustomerChinaAddress"/> </actionGroup> <!--Verify customer address save success message--> <see selector="{{AdminCustomerMessagesSection.successMessage}}" userInput="You saved the address." stepKey="seeAssertCustomerAddressSuccessSaveMessage"/> <!--Verify customer default billing address--> - <actionGroup ref="VerifyCustomerBillingAddressWithState" stepKey="verifyBillingAddress"> + <actionGroup ref="VerifyCustomerBillingAddressWithStateActionGroup" stepKey="verifyBillingAddress"> <argument name="address" value="updateCustomerChinaAddress"/> </actionGroup> <!--Verify customer default shipping address--> - <actionGroup ref="VerifyCustomerShippingAddressWithState" stepKey="verifyShippingAddress"> + <actionGroup ref="VerifyCustomerShippingAddressWithStateActionGroup" stepKey="verifyShippingAddress"> <argument name="address" value="updateCustomerChinaAddress"/> </actionGroup> </test> diff --git a/app/code/Magento/Customer/Test/Mftf/Test/StorefrontUpdateCustomerAddressFranceTest.xml b/app/code/Magento/Customer/Test/Mftf/Test/StorefrontUpdateCustomerAddressFranceTest.xml index dae456c96a679..1ecfb20b38e19 100644 --- a/app/code/Magento/Customer/Test/Mftf/Test/StorefrontUpdateCustomerAddressFranceTest.xml +++ b/app/code/Magento/Customer/Test/Mftf/Test/StorefrontUpdateCustomerAddressFranceTest.xml @@ -14,39 +14,45 @@ <title value="Update Customer Address (France) in Storefront"/> <description value="Test log in to Storefront and Update Customer Address (France) in Storefront"/> <testCaseId value="MC-10912"/> - <severity value="CRITICAL"/> + <severity value="BLOCKER"/> <group value="customer"/> <group value="mtf_migrated"/> </annotations> <before> - <actionGroup ref = "LoginAsAdmin" stepKey="loginAsAdmin"/> - <actionGroup ref="SignUpNewUserFromStorefrontActionGroup" stepKey="SignUpNewUser"> - <argument name="Customer" value="CustomerEntityOne"/> + <actionGroup ref="AdminLoginActionGroup" stepKey="loginAsAdmin"/> + <actionGroup ref="StorefrontOpenCustomerAccountCreatePageActionGroup" stepKey="openCreateAccountPage"/> + <actionGroup ref="StorefrontFillCustomerAccountCreationFormActionGroup" stepKey="fillCreateAccountForm"> + <argument name="customer" value="CustomerEntityOne"/> + </actionGroup> + <actionGroup ref="StorefrontClickCreateAnAccountCustomerAccountCreationFormActionGroup" stepKey="submitCreateAccountForm"/> + <actionGroup ref="AssertMessageCustomerCreateAccountActionGroup" stepKey="seeSuccessMessage"> + <argument name="messageType" value="success"/> + <argument name="message" value="Thank you for registering with Main Website Store."/> </actionGroup> </before> <after> <actionGroup ref="DeleteCustomerByEmailActionGroup" stepKey="deleteNewUser"> <argument name="email" value="{{CustomerEntityOne.email}}"/> </actionGroup> - <actionGroup ref="logout" stepKey="logout"/> + <actionGroup ref="AdminLogoutActionGroup" stepKey="logout"/> </after> <!--Update customer address France in storefront--> - <actionGroup ref="EnterCustomerAddressInfo" stepKey="enterAddress"> + <actionGroup ref="EnterCustomerAddressInfoActionGroup" stepKey="enterAddress"> <argument name="Address" value="updateCustomerFranceAddress"/> </actionGroup> <!--Verify customer address save success message--> <see selector="{{AdminCustomerMessagesSection.successMessage}}" userInput="You saved the address." stepKey="seeAssertCustomerAddressSuccessSaveMessage"/> <!--Verify customer default billing address--> - <actionGroup ref="VerifyCustomerBillingAddressWithState" stepKey="verifyBillingAddress"> + <actionGroup ref="VerifyCustomerBillingAddressWithStateActionGroup" stepKey="verifyBillingAddress"> <argument name="address" value="updateCustomerFranceAddress"/> </actionGroup> <!--Verify customer default shipping address--> - <actionGroup ref="VerifyCustomerShippingAddressWithState" stepKey="verifyShippingAddress"> + <actionGroup ref="VerifyCustomerShippingAddressWithStateActionGroup" stepKey="verifyShippingAddress"> <argument name="address" value="updateCustomerFranceAddress"/> </actionGroup> </test> -</tests> \ No newline at end of file +</tests> diff --git a/app/code/Magento/Customer/Test/Mftf/Test/StorefrontUpdateCustomerAddressTest.xml b/app/code/Magento/Customer/Test/Mftf/Test/StorefrontUpdateCustomerAddressTest.xml deleted file mode 100644 index d9d1c9f2e05a0..0000000000000 --- a/app/code/Magento/Customer/Test/Mftf/Test/StorefrontUpdateCustomerAddressTest.xml +++ /dev/null @@ -1,122 +0,0 @@ -<?xml version="1.0" encoding="UTF-8"?> -<!-- - /** - * Copyright © Magento, Inc. All rights reserved. - * See COPYING.txt for license details. - */ ---> - -<tests xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" - xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/testSchema.xsd"> - <test name="StorefrontUpdateCustomerDefaultBillingAddressFromBlockTest"> - <annotations> - <features value="Customer address"/> - <stories value="Implement handling of large number of addresses on storefront Address book"/> - <title value="Add default customer address via the Storefront6"/> - <description value="Storefront user should be able to create a new default address via the storefront"/> - <severity value="MAJOR"/> - <testCaseId value="MAGETWO-97501"/> - <group value="customer"/> - <group value="update"/> - </annotations> - <before> - <createData entity="Simple_US_Customer_With_Different_Billing_Shipping_Addresses" stepKey="createCustomer"/> - </before> - <after> - <deleteData createDataKey="createCustomer" stepKey="DeleteCustomer"/> - </after> - - <!--Log in to Storefront as Customer 1 --> - <actionGroup ref="LoginToStorefrontActionGroup" stepKey="signUp"> - <argument name="Customer" value="$$createCustomer$$"/> - </actionGroup> - <amOnPage url="customer/address/" stepKey="OpenCustomerAddNewAddress"/> - <click stepKey="ClickEditDefaultBillingAddress" selector="{{StorefrontCustomerAddressesSection.editDefaultBillingAddress}}"/> - <fillField stepKey="fillFirstName" userInput="EditedFirstNameBilling" selector="{{StorefrontCustomerAddressFormSection.firstName}}"/> - <fillField stepKey="fillLastName" userInput="EditedLastNameBilling" selector="{{StorefrontCustomerAddressFormSection.lastName}}"/> - <click stepKey="saveCustomerAddress" selector="{{StorefrontCustomerAddressFormSection.saveAddress}}"/> - <see userInput="You saved the address." stepKey="verifyAddressAdded"/> - <see userInput="EditedFirstNameBilling" - selector="{{StorefrontCustomerAddressesSection.defaultBillingAddress}}" stepKey="checkNewAddressesFirstNameOnDefaultBilling"/> - <see userInput="EditedLastNameBilling" - selector="{{StorefrontCustomerAddressesSection.defaultBillingAddress}}" stepKey="checkNewAddressesLastNameOnDefaultBilling"/> - <see userInput="{{US_Address_NY_Default_Shipping.firstname}}" - selector="{{StorefrontCustomerAddressesSection.defaultShippingAddress}}" stepKey="checkNewAddressesFirstNameOnDefaultShipping"/> - <see userInput="{{US_Address_NY_Default_Shipping.lastname}}" - selector="{{StorefrontCustomerAddressesSection.defaultShippingAddress}}" stepKey="checkNewAddressesLastNameOnDefaultShipping"/> - </test> - <test name="StorefrontUpdateCustomerDefaultShippingAddressFromBlockTest"> - <annotations> - <features value="Customer address"/> - <stories value="Implement handling of large number of addresses on storefront Address book"/> - <title value="Add default customer address via the Storefront611"/> - <description value="Storefront user should be able to create a new default address via the storefront"/> - <severity value="MAJOR"/> - <testCaseId value="MAGETWO-97501"/> - <group value="customer"/> - <group value="update"/> - <skip> - <issueId value="MAGETWO-97504"/> - </skip> - </annotations> - <before> - <createData entity="Simple_US_Customer_With_Different_Billing_Shipping_Addresses" stepKey="createCustomer"/> - </before> - <after> - <deleteData createDataKey="createCustomer" stepKey="DeleteCustomer"/> - </after> - - <!--Log in to Storefront as Customer 1 --> - <actionGroup ref="LoginToStorefrontActionGroup" stepKey="signUp"> - <argument name="Customer" value="$$createCustomer$$"/> - </actionGroup> - <amOnPage url="customer/address/" stepKey="OpenCustomerAddNewAddress"/> - <click stepKey="ClickEditDefaultShippingAddress" selector="{{StorefrontCustomerAddressesSection.editDefaultShippingAddress}}"/> - <fillField stepKey="fillFirstName" userInput="EditedFirstNameShipping" selector="{{StorefrontCustomerAddressFormSection.firstName}}"/> - <fillField stepKey="fillLastName" userInput="EditedLastNameShipping" selector="{{StorefrontCustomerAddressFormSection.lastName}}"/> - <click stepKey="saveCustomerAddress" selector="{{StorefrontCustomerAddressFormSection.saveAddress}}"/> - <see userInput="You saved the address." stepKey="verifyAddressAdded"/> - <see userInput="EditedFirstNameShipping" - selector="{{StorefrontCustomerAddressesSection.defaultShippingAddress}}" stepKey="checkNewAddressesFirstNameOnDefaultShipping"/> - <see userInput="EditedLastNameShipping" - selector="{{StorefrontCustomerAddressesSection.defaultBillingAddress}}" stepKey="checkNewAddressesLastNameOnDefaultShipping"/> - <see userInput="{{US_Address_TX_Default_Billing.firstname}}" - selector="{{StorefrontCustomerAddressesSection.defaultBillingAddress}}" stepKey="checkNewAddressesFirstNameOnDefaultBilling"/> - <see userInput="{{US_Address_TX_Default_Billing.lastname}}" - selector="{{StorefrontCustomerAddressesSection.defaultBillingAddress}}" stepKey="checkNewAddressesLastNameOnDefaultBilling"/> - </test> - <test name="StorefrontUpdateCustomerAddressFromGridTest"> - <annotations> - <features value="Customer address"/> - <stories value="Add default customer address via the Storefront7"/> - <title value="Add default customer address via the Storefront7"/> - <description value="Storefront user should be able to create a new default address via the storefront2"/> - <severity value="CRITICAL"/> - <testCaseId value="MAGETWO-97502"/> - <group value="customer"/> - <group value="update"/> - </annotations> - <before> - <createData entity="Simple_US_Customer_Multiple_Addresses" stepKey="createCustomer"/> - </before> - <after> - <deleteData createDataKey="createCustomer" stepKey="DeleteCustomer"/> - </after> - - <!--Log in to Storefront as Customer 1 --> - <actionGroup ref="LoginToStorefrontActionGroup" stepKey="signUp"> - <argument name="Customer" value="$$createCustomer$$"/> - </actionGroup> - - <amOnPage url="customer/address/" stepKey="OpenCustomerAddNewAddress"/> - <click selector="{{StorefrontCustomerAddressesSection.editAdditionalAddress('1')}}" stepKey="editAdditionalAddress"/> - <fillField stepKey="fillFirstName" userInput="EditedFirstName" selector="{{StorefrontCustomerAddressFormSection.firstName}}"/> - <fillField stepKey="fillLastName" userInput="EditedLastName" selector="{{StorefrontCustomerAddressFormSection.lastName}}"/> - <click stepKey="saveCustomerAddress" selector="{{StorefrontCustomerAddressFormSection.saveAddress}}"/> - <see userInput="You saved the address." stepKey="verifyAddressAdded"/> - <see userInput="EditedFirstName" - selector="{{StorefrontCustomerAddressesSection.addressesList}}" stepKey="checkNewAddressFirstNameOnGrid"/> - <see userInput="EditedLastName" - selector="{{StorefrontCustomerAddressesSection.addressesList}}" stepKey="checkNewAddressLastNameOnGrid"/> - </test> -</tests> \ No newline at end of file diff --git a/app/code/Magento/Customer/Test/Mftf/Test/StorefrontUpdateCustomerAddressTest/StorefrontUpdateCustomerAddressFromGridTest.xml b/app/code/Magento/Customer/Test/Mftf/Test/StorefrontUpdateCustomerAddressTest/StorefrontUpdateCustomerAddressFromGridTest.xml new file mode 100644 index 0000000000000..d41b1cf86da59 --- /dev/null +++ b/app/code/Magento/Customer/Test/Mftf/Test/StorefrontUpdateCustomerAddressTest/StorefrontUpdateCustomerAddressFromGridTest.xml @@ -0,0 +1,42 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + /** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +--> +<tests xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/testSchema.xsd"> + <test name="StorefrontUpdateCustomerAddressFromGridTest"> + <annotations> + <features value="Customer address"/> + <stories value="Add default customer address via the Storefront7"/> + <title value="Add default customer address via the Storefront7"/> + <description value="Storefront user should be able to create a new default address via the storefront2"/> + <severity value="CRITICAL"/> + <testCaseId value="MAGETWO-97502"/> + <group value="customer"/> + <group value="update"/> + </annotations> + <before> + <createData entity="Simple_US_Customer_Multiple_Addresses" stepKey="createCustomer"/> + </before> + <after> + <deleteData createDataKey="createCustomer" stepKey="DeleteCustomer"/> + </after> + + <!--Log in to Storefront as Customer 1 --> + <actionGroup ref="LoginToStorefrontActionGroup" stepKey="signUp"> + <argument name="Customer" value="$$createCustomer$$"/> + </actionGroup> + + <amOnPage url="customer/address/" stepKey="OpenCustomerAddNewAddress"/> + <click selector="{{StorefrontCustomerAddressesSection.editAdditionalAddress('1')}}" stepKey="editAdditionalAddress"/> + <fillField stepKey="fillFirstName" userInput="EditedFirstName" selector="{{StorefrontCustomerAddressFormSection.firstName}}"/> + <fillField stepKey="fillLastName" userInput="EditedLastName" selector="{{StorefrontCustomerAddressFormSection.lastName}}"/> + <click stepKey="saveCustomerAddress" selector="{{StorefrontCustomerAddressFormSection.saveAddress}}"/> + <see userInput="You saved the address." stepKey="verifyAddressAdded"/> + <see userInput="EditedFirstName" selector="{{StorefrontCustomerAddressesSection.addressesList}}" stepKey="checkNewAddressFirstNameOnGrid"/> + <see userInput="EditedLastName" selector="{{StorefrontCustomerAddressesSection.addressesList}}" stepKey="checkNewAddressLastNameOnGrid"/> + </test> +</tests> diff --git a/app/code/Magento/Customer/Test/Mftf/Test/StorefrontUpdateCustomerAddressTest/StorefrontUpdateCustomerDefaultBillingAddressFromBlockTest.xml b/app/code/Magento/Customer/Test/Mftf/Test/StorefrontUpdateCustomerAddressTest/StorefrontUpdateCustomerDefaultBillingAddressFromBlockTest.xml new file mode 100644 index 0000000000000..438e875d93749 --- /dev/null +++ b/app/code/Magento/Customer/Test/Mftf/Test/StorefrontUpdateCustomerAddressTest/StorefrontUpdateCustomerDefaultBillingAddressFromBlockTest.xml @@ -0,0 +1,43 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + /** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +--> +<tests xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/testSchema.xsd"> + <test name="StorefrontUpdateCustomerDefaultBillingAddressFromBlockTest"> + <annotations> + <features value="Customer address"/> + <stories value="Implement handling of large number of addresses on storefront Address book"/> + <title value="Add default customer address via the Storefront6"/> + <description value="Storefront user should be able to create a new default address via the storefront"/> + <severity value="MAJOR"/> + <testCaseId value="MAGETWO-97501"/> + <group value="customer"/> + <group value="update"/> + </annotations> + <before> + <createData entity="Simple_US_Customer_With_Different_Billing_Shipping_Addresses" stepKey="createCustomer"/> + </before> + <after> + <deleteData createDataKey="createCustomer" stepKey="DeleteCustomer"/> + </after> + + <!--Log in to Storefront as Customer 1 --> + <actionGroup ref="LoginToStorefrontActionGroup" stepKey="signUp"> + <argument name="Customer" value="$$createCustomer$$"/> + </actionGroup> + <amOnPage url="customer/address/" stepKey="OpenCustomerAddNewAddress"/> + <click stepKey="ClickEditDefaultBillingAddress" selector="{{StorefrontCustomerAddressesSection.editDefaultBillingAddress}}"/> + <fillField stepKey="fillFirstName" userInput="EditedFirstNameBilling" selector="{{StorefrontCustomerAddressFormSection.firstName}}"/> + <fillField stepKey="fillLastName" userInput="EditedLastNameBilling" selector="{{StorefrontCustomerAddressFormSection.lastName}}"/> + <click stepKey="saveCustomerAddress" selector="{{StorefrontCustomerAddressFormSection.saveAddress}}"/> + <see userInput="You saved the address." stepKey="verifyAddressAdded"/> + <see userInput="EditedFirstNameBilling" selector="{{StorefrontCustomerAddressesSection.defaultBillingAddress}}" stepKey="checkNewAddressesFirstNameOnDefaultBilling"/> + <see userInput="EditedLastNameBilling" selector="{{StorefrontCustomerAddressesSection.defaultBillingAddress}}" stepKey="checkNewAddressesLastNameOnDefaultBilling"/> + <see userInput="{{US_Address_NY_Default_Shipping.firstname}}" selector="{{StorefrontCustomerAddressesSection.defaultShippingAddress}}" stepKey="checkNewAddressesFirstNameOnDefaultShipping"/> + <see userInput="{{US_Address_NY_Default_Shipping.lastname}}" selector="{{StorefrontCustomerAddressesSection.defaultShippingAddress}}" stepKey="checkNewAddressesLastNameOnDefaultShipping"/> + </test> +</tests> diff --git a/app/code/Magento/Customer/Test/Mftf/Test/StorefrontUpdateCustomerAddressTest/StorefrontUpdateCustomerDefaultShippingAddressFromBlockTest.xml b/app/code/Magento/Customer/Test/Mftf/Test/StorefrontUpdateCustomerAddressTest/StorefrontUpdateCustomerDefaultShippingAddressFromBlockTest.xml new file mode 100644 index 0000000000000..f79ef0a397e27 --- /dev/null +++ b/app/code/Magento/Customer/Test/Mftf/Test/StorefrontUpdateCustomerAddressTest/StorefrontUpdateCustomerDefaultShippingAddressFromBlockTest.xml @@ -0,0 +1,46 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + /** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +--> +<tests xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/testSchema.xsd"> + <test name="StorefrontUpdateCustomerDefaultShippingAddressFromBlockTest"> + <annotations> + <features value="Customer address"/> + <stories value="Implement handling of large number of addresses on storefront Address book"/> + <title value="Add default customer address via the Storefront611"/> + <description value="Storefront user should be able to create a new default address via the storefront"/> + <severity value="MAJOR"/> + <testCaseId value="MAGETWO-97501"/> + <group value="customer"/> + <group value="update"/> + <skip> + <issueId value="MAGETWO-97504"/> + </skip> + </annotations> + <before> + <createData entity="Simple_US_Customer_With_Different_Billing_Shipping_Addresses" stepKey="createCustomer"/> + </before> + <after> + <deleteData createDataKey="createCustomer" stepKey="DeleteCustomer"/> + </after> + + <!--Log in to Storefront as Customer 1 --> + <actionGroup ref="LoginToStorefrontActionGroup" stepKey="signUp"> + <argument name="Customer" value="$$createCustomer$$"/> + </actionGroup> + <amOnPage url="customer/address/" stepKey="OpenCustomerAddNewAddress"/> + <click stepKey="ClickEditDefaultShippingAddress" selector="{{StorefrontCustomerAddressesSection.editDefaultShippingAddress}}"/> + <fillField stepKey="fillFirstName" userInput="EditedFirstNameShipping" selector="{{StorefrontCustomerAddressFormSection.firstName}}"/> + <fillField stepKey="fillLastName" userInput="EditedLastNameShipping" selector="{{StorefrontCustomerAddressFormSection.lastName}}"/> + <click stepKey="saveCustomerAddress" selector="{{StorefrontCustomerAddressFormSection.saveAddress}}"/> + <see userInput="You saved the address." stepKey="verifyAddressAdded"/> + <see userInput="EditedFirstNameShipping" selector="{{StorefrontCustomerAddressesSection.defaultShippingAddress}}" stepKey="checkNewAddressesFirstNameOnDefaultShipping"/> + <see userInput="EditedLastNameShipping" selector="{{StorefrontCustomerAddressesSection.defaultBillingAddress}}" stepKey="checkNewAddressesLastNameOnDefaultShipping"/> + <see userInput="{{US_Address_TX_Default_Billing.firstname}}" selector="{{StorefrontCustomerAddressesSection.defaultBillingAddress}}" stepKey="checkNewAddressesFirstNameOnDefaultBilling"/> + <see userInput="{{US_Address_TX_Default_Billing.lastname}}" selector="{{StorefrontCustomerAddressesSection.defaultBillingAddress}}" stepKey="checkNewAddressesLastNameOnDefaultBilling"/> + </test> +</tests> diff --git a/app/code/Magento/Customer/Test/Mftf/Test/StorefrontUpdateCustomerAddressUKTest.xml b/app/code/Magento/Customer/Test/Mftf/Test/StorefrontUpdateCustomerAddressUKTest.xml index 7b6e695aa8dc4..8e29452b5495e 100644 --- a/app/code/Magento/Customer/Test/Mftf/Test/StorefrontUpdateCustomerAddressUKTest.xml +++ b/app/code/Magento/Customer/Test/Mftf/Test/StorefrontUpdateCustomerAddressUKTest.xml @@ -14,44 +14,50 @@ <title value="Update Customer Address (UK) in Storefront"/> <description value="Test log in to Storefront and Update Customer Address (UK) in Storefront"/> <testCaseId value="MC-10911"/> - <severity value="CRITICAL"/> + <severity value="BLOCKER"/> <group value="customer"/> <group value="mtf_migrated"/> </annotations> <before> - <actionGroup ref = "LoginAsAdmin" stepKey="loginAsAdmin"/> - <actionGroup ref="SignUpNewUserFromStorefrontActionGroup" stepKey="SignUpNewUser"> - <argument name="Customer" value="CustomerEntityOne"/> + <actionGroup ref="AdminLoginActionGroup" stepKey="loginAsAdmin"/> + <actionGroup ref="StorefrontOpenCustomerAccountCreatePageActionGroup" stepKey="openCreateAccountPage"/> + <actionGroup ref="StorefrontFillCustomerAccountCreationFormActionGroup" stepKey="fillCreateAccountForm"> + <argument name="customer" value="CustomerEntityOne"/> + </actionGroup> + <actionGroup ref="StorefrontClickCreateAnAccountCustomerAccountCreationFormActionGroup" stepKey="submitCreateAccountForm"/> + <actionGroup ref="AssertMessageCustomerCreateAccountActionGroup" stepKey="seeSuccessMessage"> + <argument name="messageType" value="success"/> + <argument name="message" value="Thank you for registering with Main Website Store."/> </actionGroup> </before> <after> <actionGroup ref="DeleteCustomerByEmailActionGroup" stepKey="deleteNewUser"> <argument name="email" value="{{CustomerEntityOne.email}}"/> </actionGroup> - <actionGroup ref="logout" stepKey="logout"/> + <actionGroup ref="AdminLogoutActionGroup" stepKey="logout"/> </after> <!--Update customer address UK in storefront--> - <actionGroup ref="EnterCustomerAddressInfo" stepKey="enterAddress"> + <actionGroup ref="EnterCustomerAddressInfoActionGroup" stepKey="enterAddress"> <argument name="Address" value="updateCustomerUKAddress"/> </actionGroup> <!--Verify customer address save success message--> <see selector="{{AdminCustomerMessagesSection.successMessage}}" userInput="You saved the address." stepKey="seeAssertCustomerAddressSuccessSaveMessage"/> <!--Verify customer default billing address--> - <actionGroup ref="VerifyCustomerBillingAddress" stepKey="verifyBillingAddress"> + <actionGroup ref="VerifyCustomerBillingAddressActionGroup" stepKey="verifyBillingAddress"> <argument name="address" value="updateCustomerUKAddress"/> </actionGroup> <!--Verify customer default shipping address--> - <actionGroup ref="VerifyCustomerShippingAddress" stepKey="verifyShippingAddress"> + <actionGroup ref="VerifyCustomerShippingAddressActionGroup" stepKey="verifyShippingAddress"> <argument name="address" value="updateCustomerUKAddress"/> </actionGroup> <!--Verify customer name on frontend--> - <actionGroup ref="VerifyCustomerNameOnFrontend" stepKey="verifyVerifyCustomerName"> + <actionGroup ref="VerifyCustomerNameOnFrontendActionGroup" stepKey="verifyVerifyCustomerName"> <argument name="customer" value="CustomerEntityOne"/> </actionGroup> </test> -</tests> \ No newline at end of file +</tests> diff --git a/app/code/Magento/Customer/Test/Mftf/Test/StorefrontUpdateCustomerPasswordTest.xml b/app/code/Magento/Customer/Test/Mftf/Test/StorefrontUpdateCustomerPasswordTest.xml deleted file mode 100644 index 9bc253c91af92..0000000000000 --- a/app/code/Magento/Customer/Test/Mftf/Test/StorefrontUpdateCustomerPasswordTest.xml +++ /dev/null @@ -1,81 +0,0 @@ -<?xml version="1.0" encoding="UTF-8"?> -<!-- - /** - * Copyright © Magento, Inc. All rights reserved. - * See COPYING.txt for license details. - */ ---> - -<tests xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" - xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/testSchema.xsd"> - <test name="StorefrontUpdateCustomerPasswordValidCurrentPasswordTest"> - <annotations> - <features value="Customer"/> - <stories value="Customer Update Password"/> - <title value="Update Customer Password on Storefront, Valid Current Password"/> - <description value="Update Customer Password on Storefront, Valid Current Password"/> - <severity value="CRITICAL"/> - <testCaseId value="MC-10916"/> - <group value="Customer"/> - <group value="mtf_migrated"/> - </annotations> - <before> - <createData stepKey="customer" entity="Simple_US_Customer"/> - </before> - <after> - <deleteData stepKey="deleteCustomer" createDataKey="customer" /> - </after> - - <!--Log in to Storefront as Customer --> - <actionGroup stepKey="login" ref="LoginToStorefrontActionGroup"> - <argument name="Customer" value="$$customer$$"/> - </actionGroup> - <seeInCurrentUrl stepKey="onCustomerAccountPage" url="customer/account"/> - <click stepKey="clickChangePassword" selector="{{StorefrontCustomerDashboardAccountInformationSection.changePassword}}"/> - <fillField stepKey="fillValidCurrentPassword" userInput="$$customer.password$$" selector="{{StorefrontCustomerAccountInformationSection.currentPassword}}"/> - <fillField stepKey="fillNewPassword" userInput="$$customer.password$$#" selector="{{StorefrontCustomerAccountInformationSection.newPassword}}"/> - <fillField stepKey="fillNewPasswordConfirmation" userInput="$$customer.password$$#" selector="{{StorefrontCustomerAccountInformationSection.confirmNewPassword}}"/> - <click stepKey="saveChange" selector="{{StorefrontCustomerAccountInformationSection.saveButton}}"/> - <see stepKey="verifyMessage" userInput="You saved the account information." selector="{{StorefrontCustomerMessagesSection.successMessage}}"/> - <actionGroup stepKey="logout" ref="StorefrontCustomerLogoutActionGroup"/> - <actionGroup stepKey="loginWithNewPassword" ref="LoginToStorefrontWithEmailAndPassword"> - <argument name="email" value="$$customer.email$$"/> - <argument name="password" value="$$customer.password$$#"/> - </actionGroup> - <see stepKey="seeMyEmail" userInput="$$customer.email$$" selector="{{StorefrontCustomerDashboardAccountInformationSection.ContactInformation}}"/> - </test> - <test name="StorefrontUpdateCustomerPasswordInvalidCurrentPasswordTest" extends="StorefrontUpdateCustomerPasswordValidCurrentPasswordTest"> - <annotations> - <features value="Customer"/> - <stories value="Customer Update Password"/> - <title value="Update Customer Password on Storefront, Invalid Current Password"/> - <description value="Update Customer Password on Storefront, Invalid Current Password"/> - <severity value="CRITICAL"/> - <testCaseId value="MC-10917"/> - <group value="Customer"/> - <group value="mtf_migrated"/> - </annotations> - - <fillField stepKey="fillValidCurrentPassword" userInput="$$customer.password$$^" selector="{{StorefrontCustomerAccountInformationSection.currentPassword}}"/> - <see stepKey="verifyMessage" userInput="The password doesn't match this account. Verify the password and try again." selector="{{StorefrontCustomerMessagesSection.errorMessage}}"/> - <remove keyForRemoval="loginWithNewPassword"/> - <remove keyForRemoval="seeMyEmail"/> - </test> - <test name="StorefrontUpdateCustomerPasswordInvalidConfirmationPasswordTest" extends="StorefrontUpdateCustomerPasswordValidCurrentPasswordTest"> - <annotations> - <features value="Customer"/> - <stories value="Customer Update Password"/> - <title value="Update Customer Password on Storefront, Invalid Confirmation Password"/> - <description value="Update Customer Password on Storefront, Invalid Confirmation Password"/> - <severity value="CRITICAL"/> - <testCaseId value="MC-10918"/> - <group value="Customer"/> - <group value="mtf_migrated"/> - </annotations> - - <fillField stepKey="fillNewPasswordConfirmation" userInput="$$customer.password$$^" selector="{{StorefrontCustomerAccountInformationSection.confirmNewPassword}}"/> - <see stepKey="verifyMessage" userInput="Please enter the same value again." selector="{{StorefrontCustomerAccountInformationSection.confirmNewPasswordError}}"/> - <remove keyForRemoval="loginWithNewPassword"/> - <remove keyForRemoval="seeMyEmail"/> - </test> -</tests> \ No newline at end of file diff --git a/app/code/Magento/Customer/Test/Mftf/Test/StorefrontUpdateCustomerPasswordTest/StorefrontUpdateCustomerPasswordInvalidConfirmationPasswordTest.xml b/app/code/Magento/Customer/Test/Mftf/Test/StorefrontUpdateCustomerPasswordTest/StorefrontUpdateCustomerPasswordInvalidConfirmationPasswordTest.xml new file mode 100644 index 0000000000000..9e5be5abe95a3 --- /dev/null +++ b/app/code/Magento/Customer/Test/Mftf/Test/StorefrontUpdateCustomerPasswordTest/StorefrontUpdateCustomerPasswordInvalidConfirmationPasswordTest.xml @@ -0,0 +1,27 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + /** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +--> +<tests xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/testSchema.xsd"> + <test name="StorefrontUpdateCustomerPasswordInvalidConfirmationPasswordTest" extends="StorefrontUpdateCustomerPasswordValidCurrentPasswordTest"> + <annotations> + <features value="Customer"/> + <stories value="Customer Update Password"/> + <title value="Update Customer Password on Storefront, Invalid Confirmation Password"/> + <description value="Update Customer Password on Storefront, Invalid Confirmation Password"/> + <severity value="CRITICAL"/> + <testCaseId value="MC-10918"/> + <group value="Customer"/> + <group value="mtf_migrated"/> + </annotations> + + <fillField stepKey="fillNewPasswordConfirmation" userInput="$$customer.password$$^" selector="{{StorefrontCustomerAccountInformationSection.confirmNewPassword}}"/> + <see stepKey="verifyMessage" userInput="Please enter the same value again." selector="{{StorefrontCustomerAccountInformationSection.confirmNewPasswordError}}"/> + <remove keyForRemoval="loginWithNewPassword"/> + <remove keyForRemoval="seeMyEmail"/> + </test> +</tests> diff --git a/app/code/Magento/Customer/Test/Mftf/Test/StorefrontUpdateCustomerPasswordTest/StorefrontUpdateCustomerPasswordInvalidCurrentPasswordTest.xml b/app/code/Magento/Customer/Test/Mftf/Test/StorefrontUpdateCustomerPasswordTest/StorefrontUpdateCustomerPasswordInvalidCurrentPasswordTest.xml new file mode 100644 index 0000000000000..1f2c07c325c15 --- /dev/null +++ b/app/code/Magento/Customer/Test/Mftf/Test/StorefrontUpdateCustomerPasswordTest/StorefrontUpdateCustomerPasswordInvalidCurrentPasswordTest.xml @@ -0,0 +1,27 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + /** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +--> +<tests xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/testSchema.xsd"> + <test name="StorefrontUpdateCustomerPasswordInvalidCurrentPasswordTest" extends="StorefrontUpdateCustomerPasswordValidCurrentPasswordTest"> + <annotations> + <features value="Customer"/> + <stories value="Customer Update Password"/> + <title value="Update Customer Password on Storefront, Invalid Current Password"/> + <description value="Update Customer Password on Storefront, Invalid Current Password"/> + <severity value="CRITICAL"/> + <testCaseId value="MC-10917"/> + <group value="Customer"/> + <group value="mtf_migrated"/> + </annotations> + + <fillField stepKey="fillValidCurrentPassword" userInput="$$customer.password$$^" selector="{{StorefrontCustomerAccountInformationSection.currentPassword}}"/> + <see stepKey="verifyMessage" userInput="The password doesn't match this account. Verify the password and try again." selector="{{StorefrontCustomerMessagesSection.errorMessage}}"/> + <remove keyForRemoval="loginWithNewPassword"/> + <remove keyForRemoval="seeMyEmail"/> + </test> +</tests> diff --git a/app/code/Magento/Customer/Test/Mftf/Test/StorefrontUpdateCustomerPasswordTest/StorefrontUpdateCustomerPasswordValidCurrentPasswordTest.xml b/app/code/Magento/Customer/Test/Mftf/Test/StorefrontUpdateCustomerPasswordTest/StorefrontUpdateCustomerPasswordValidCurrentPasswordTest.xml new file mode 100644 index 0000000000000..00871e5f38688 --- /dev/null +++ b/app/code/Magento/Customer/Test/Mftf/Test/StorefrontUpdateCustomerPasswordTest/StorefrontUpdateCustomerPasswordValidCurrentPasswordTest.xml @@ -0,0 +1,46 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + /** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +--> +<tests xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/testSchema.xsd"> + <test name="StorefrontUpdateCustomerPasswordValidCurrentPasswordTest"> + <annotations> + <features value="Customer"/> + <stories value="Customer Update Password"/> + <title value="Update Customer Password on Storefront, Valid Current Password"/> + <description value="Update Customer Password on Storefront, Valid Current Password"/> + <severity value="BLOCKER"/> + <testCaseId value="MC-10916"/> + <group value="Customer"/> + <group value="mtf_migrated"/> + </annotations> + <before> + <createData stepKey="customer" entity="Simple_US_Customer"/> + </before> + <after> + <deleteData stepKey="deleteCustomer" createDataKey="customer"/> + </after> + + <!--Log in to Storefront as Customer --> + <actionGroup stepKey="login" ref="LoginToStorefrontActionGroup"> + <argument name="Customer" value="$$customer$$"/> + </actionGroup> + <seeInCurrentUrl stepKey="onCustomerAccountPage" url="customer/account"/> + <click stepKey="clickChangePassword" selector="{{StorefrontCustomerDashboardAccountInformationSection.changePassword}}"/> + <fillField stepKey="fillValidCurrentPassword" userInput="$$customer.password$$" selector="{{StorefrontCustomerAccountInformationSection.currentPassword}}"/> + <fillField stepKey="fillNewPassword" userInput="$$customer.password$$#" selector="{{StorefrontCustomerAccountInformationSection.newPassword}}"/> + <fillField stepKey="fillNewPasswordConfirmation" userInput="$$customer.password$$#" selector="{{StorefrontCustomerAccountInformationSection.confirmNewPassword}}"/> + <click stepKey="saveChange" selector="{{StorefrontCustomerAccountInformationSection.saveButton}}"/> + <see stepKey="verifyMessage" userInput="You saved the account information." selector="{{StorefrontCustomerMessagesSection.successMessage}}"/> + <actionGroup stepKey="logout" ref="StorefrontCustomerLogoutActionGroup"/> + <actionGroup stepKey="loginWithNewPassword" ref="LoginToStorefrontWithEmailAndPassword"> + <argument name="email" value="$$customer.email$$"/> + <argument name="password" value="$$customer.password$$#"/> + </actionGroup> + <see stepKey="seeMyEmail" userInput="$$customer.email$$" selector="{{StorefrontCustomerDashboardAccountInformationSection.ContactInformation}}"/> + </test> +</tests> diff --git a/app/code/Magento/Customer/Test/Mftf/Test/StorefrontVerifyNoXssInjectionOnUpdateCustomerInformationAddAddressTest.xml b/app/code/Magento/Customer/Test/Mftf/Test/StorefrontVerifyNoXssInjectionOnUpdateCustomerInformationAddAddressTest.xml index e11404db9a9a9..11aed4a3461e1 100644 --- a/app/code/Magento/Customer/Test/Mftf/Test/StorefrontVerifyNoXssInjectionOnUpdateCustomerInformationAddAddressTest.xml +++ b/app/code/Magento/Customer/Test/Mftf/Test/StorefrontVerifyNoXssInjectionOnUpdateCustomerInformationAddAddressTest.xml @@ -20,38 +20,44 @@ </annotations> <before> - <actionGroup ref = "LoginAsAdmin" stepKey="loginAsAdmin"/> - <actionGroup ref="SignUpNewUserFromStorefrontActionGroup" stepKey="SignUpNewUser"> - <argument name="Customer" value="Colorado_US_Customer"/> + <actionGroup ref="AdminLoginActionGroup" stepKey="loginAsAdmin"/> + <actionGroup ref="StorefrontOpenCustomerAccountCreatePageActionGroup" stepKey="openCreateAccountPage"/> + <actionGroup ref="StorefrontFillCustomerAccountCreationFormActionGroup" stepKey="fillCreateAccountForm"> + <argument name="customer" value="Colorado_US_Customer"/> + </actionGroup> + <actionGroup ref="StorefrontClickCreateAnAccountCustomerAccountCreationFormActionGroup" stepKey="submitCreateAccountForm"/> + <actionGroup ref="AssertMessageCustomerCreateAccountActionGroup" stepKey="seeSuccessMessage"> + <argument name="messageType" value="success"/> + <argument name="message" value="Thank you for registering with Main Website Store."/> </actionGroup> </before> <after> <actionGroup ref="DeleteCustomerByEmailActionGroup" stepKey="deleteNewUser"> <argument name="email" value="{{Colorado_US_Customer.email}}"/> </actionGroup> - <actionGroup ref="logout" stepKey="logout"/> + <actionGroup ref="AdminLogoutActionGroup" stepKey="logout"/> </after> <!--Update customer address in storefront--> - <actionGroup ref="EnterCustomerAddressInfo" stepKey="enterAddress"> + <actionGroup ref="EnterCustomerAddressInfoActionGroup" stepKey="enterAddress"> <argument name="Address" value="updateCustomerNoXSSInjection"/> </actionGroup> <!--Verify customer address save success message--> <see selector="{{AdminCustomerMessagesSection.successMessage}}" userInput="You saved the address." stepKey="seeAssertCustomerAddressSuccessSaveMessage"/> <!--Verify customer default billing address--> - <actionGroup ref="VerifyCustomerBillingAddressWithState" stepKey="verifyBillingAddress"> + <actionGroup ref="VerifyCustomerBillingAddressWithStateActionGroup" stepKey="verifyBillingAddress"> <argument name="address" value="updateCustomerNoXSSInjection"/> </actionGroup> <!--Verify customer default shipping address--> - <actionGroup ref="VerifyCustomerShippingAddressWithState" stepKey="verifyShippingAddress"> + <actionGroup ref="VerifyCustomerShippingAddressWithStateActionGroup" stepKey="verifyShippingAddress"> <argument name="address" value="updateCustomerNoXSSInjection"/> </actionGroup> <!--Verify customer name on frontend--> - <actionGroup ref="VerifyCustomerNameOnFrontend" stepKey="verifyVerifyCustomerName"> + <actionGroup ref="VerifyCustomerNameOnFrontendActionGroup" stepKey="verifyVerifyCustomerName"> <argument name="customer" value="Colorado_US_Customer"/> </actionGroup> </test> -</tests> \ No newline at end of file +</tests> diff --git a/app/code/Magento/Customer/Test/Mftf/Test/StorefrontVerifySecureURLRedirectCustomerTest.xml b/app/code/Magento/Customer/Test/Mftf/Test/StorefrontVerifySecureURLRedirectCustomerTest.xml index da9dddf0539d3..f504af2334e10 100644 --- a/app/code/Magento/Customer/Test/Mftf/Test/StorefrontVerifySecureURLRedirectCustomerTest.xml +++ b/app/code/Magento/Customer/Test/Mftf/Test/StorefrontVerifySecureURLRedirectCustomerTest.xml @@ -8,7 +8,7 @@ <tests xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/testSchema.xsd"> - <test name="StorefrontVerifySecureURLRedirectCustomer"> + <test name="StorefrontVerifySecureURLRedirectCustomerTest"> <annotations> <features value="Customer"/> <stories value="Storefront Secure URLs"/> diff --git a/app/code/Magento/Customer/Test/Mftf/Test/VerifyDisabledCustomerGroupFieldTest.xml b/app/code/Magento/Customer/Test/Mftf/Test/VerifyDisabledCustomerGroupFieldTest.xml deleted file mode 100644 index 0f98184aafb4f..0000000000000 --- a/app/code/Magento/Customer/Test/Mftf/Test/VerifyDisabledCustomerGroupFieldTest.xml +++ /dev/null @@ -1,38 +0,0 @@ -<?xml version="1.0" encoding="UTF-8"?> -<!-- - /** - * Copyright © Magento, Inc. All rights reserved. - * See COPYING.txt for license details. - */ ---> - -<tests xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" - xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/testSchema.xsd"> - <test name="VerifyDisabledCustomerGroupFieldTest"> - <annotations> - <stories value="Check that field is disabled in system Customer Group"/> - <title value="Check that field is disabled in system Customer Group"/> - <description value="Checks that customer_group_code field is disabled in NOT LOGGED IN Customer Group"/> - <testCaseId value="MC-14206"/> - <severity value="CRITICAL"/> - <group value="customers"/> - <group value="mtf_migrated"/> - </annotations> - - <!-- Steps --> - <!-- 1. Login to backend as admin user --> - <actionGroup ref="LoginAsAdmin" stepKey="loginAsAdmin"/> - <waitForPageLoad stepKey="waitForAdminPageLoad" /> - - <!-- 2. Navigate to Customers > Customer Groups --> - <amOnPage url="{{AdminCustomerGroupPage.url}}" stepKey="amOnCustomerGroupPage" /> - <conditionalClick selector="{{AdminDataGridHeaderSection.clearFilters}}" dependentSelector="{{AdminDataGridHeaderSection.clearFilters}}" visible="true" stepKey="clearFiltersIfTheySet"/> - - <!-- 3. Select system Customer Group specified in data set from grid --> - <click selector="{{AdminCustomerGroupMainSection.editButtonByCustomerGroupCode(NotLoggedInCustomerGroup.code)}}" stepKey="clickOnEditCustomerGroup" /> - - <!-- 4. Perform all assertions --> - <seeInField selector="{{AdminNewCustomerGroupSection.groupName}}" userInput="{{NotLoggedInCustomerGroup.code}}" stepKey="seeNotLoggedInTextInGroupName" /> - <assertElementContainsAttribute selector="{{AdminNewCustomerGroupSection.groupName}}" attribute="disabled" expectedValue="true" stepKey="checkIfGroupNameIsDisabled" /> - </test> -</tests> diff --git a/app/code/Magento/Customer/Test/Unit/Block/Widget/DobTest.php b/app/code/Magento/Customer/Test/Unit/Block/Widget/DobTest.php index 1fd7fc340e542..59660ec345814 100644 --- a/app/code/Magento/Customer/Test/Unit/Block/Widget/DobTest.php +++ b/app/code/Magento/Customer/Test/Unit/Block/Widget/DobTest.php @@ -29,6 +29,7 @@ /** * @SuppressWarnings(PHPMD.CouplingBetweenObjects) + * @SuppressWarnings(PHPMD.TooManyPublicMethods) */ class DobTest extends TestCase { @@ -542,18 +543,30 @@ public function testGetMaxDateRangeWithException() */ public function testGetHtmlExtraParamsWithoutRequiredOption() { + $validation = json_encode( + [ + 'validate-date' => [ + 'dateFormat' => self::DATE_FORMAT + ], + 'validate-dob' => [ + 'dateFormat' => self::DATE_FORMAT + ], + ] + ); $this->escaper->expects($this->any()) ->method('escapeHtml') - ->with('{"validate-date":{"dateFormat":"M\/d\/Y"},"validate-dob":true}') - ->will($this->returnValue('{"validate-date":{"dateFormat":"M\/d\/Y"},"validate-dob":true}')); + ->with($validation) + ->will( + $this->returnValue($validation) + ); $this->attribute->expects($this->once()) ->method("isRequired") ->willReturn(false); $this->assertEquals( - $this->_block->getHtmlExtraParams(), - 'data-validate="{"validate-date":{"dateFormat":"M\/d\/Y"},"validate-dob":true}"' + "data-validate=\"$validation\"", + $this->_block->getHtmlExtraParams() ); } @@ -562,22 +575,31 @@ public function testGetHtmlExtraParamsWithoutRequiredOption() */ public function testGetHtmlExtraParamsWithRequiredOption() { + $validation = json_encode( + [ + 'required' => true, + 'validate-date' => [ + 'dateFormat' => self::DATE_FORMAT + ], + 'validate-dob' => [ + 'dateFormat' => self::DATE_FORMAT + ], + ] + ); $this->attribute->expects($this->once()) ->method("isRequired") ->willReturn(true); $this->escaper->expects($this->any()) ->method('escapeHtml') - ->with('{"required":true,"validate-date":{"dateFormat":"M\/d\/Y"},"validate-dob":true}') + ->with($validation) ->will( - $this->returnValue( - '{"required":true,"validate-date":{"dateFormat":"M\/d\/Y"},"validate-dob":true}' - ) + $this->returnValue($validation) ); $this->context->expects($this->any())->method('getEscaper')->will($this->returnValue($this->escaper)); $this->assertEquals( - 'data-validate="{"required":true,"validate-date":{"dateFormat":"M\/d\/Y"},"validate-dob":true}"', + "data-validate=\"$validation\"", $this->_block->getHtmlExtraParams() ); } diff --git a/app/code/Magento/Customer/Test/Unit/Controller/Adminhtml/Index/SaveTest.php b/app/code/Magento/Customer/Test/Unit/Controller/Adminhtml/Index/SaveTest.php index 2e729873961c0..51663861fc8d1 100644 --- a/app/code/Magento/Customer/Test/Unit/Controller/Adminhtml/Index/SaveTest.php +++ b/app/code/Magento/Customer/Test/Unit/Controller/Adminhtml/Index/SaveTest.php @@ -269,7 +269,7 @@ protected function setUp() ->getMock(); $this->managementMock = $this->getMockBuilder(AccountManagement::class) ->disableOriginalConstructor() - ->setMethods(['createAccount']) + ->setMethods(['createAccount', 'validateCustomerStoreIdByWebsiteId']) ->getMock(); $this->addressDataFactoryMock = $this->getMockBuilder(AddressInterfaceFactory::class) ->disableOriginalConstructor() @@ -522,6 +522,9 @@ public function testExecuteWithExistentCustomer() ->with('customer/*/edit', ['id' => $customerId, '_current' => true]) ->willReturn(true); + $this->managementMock->method('validateCustomerStoreIdByWebsiteId') + ->willReturn(true); + $this->assertEquals($redirectMock, $this->model->execute()); } diff --git a/app/code/Magento/Customer/Test/Unit/Controller/Plugin/AccountTest.php b/app/code/Magento/Customer/Test/Unit/Controller/Plugin/AccountTest.php index 2c70a8bda28fe..01ff1ced05ff9 100644 --- a/app/code/Magento/Customer/Test/Unit/Controller/Plugin/AccountTest.php +++ b/app/code/Magento/Customer/Test/Unit/Controller/Plugin/AccountTest.php @@ -3,18 +3,23 @@ * Copyright © Magento, Inc. All rights reserved. * See COPYING.txt for license details. */ + namespace Magento\Customer\Test\Unit\Controller\Plugin; +use Closure; +use Magento\Customer\Controller\AccountInterface; use Magento\Customer\Controller\Plugin\Account; use Magento\Customer\Model\Session; use Magento\Framework\App\ActionFlag; use Magento\Framework\App\ActionInterface; -use Magento\Framework\App\Action\AbstractAction; use Magento\Framework\App\Request\Http; +use Magento\Framework\App\Request\Http as HttpRequest; use Magento\Framework\Controller\ResultInterface; use Magento\Framework\TestFramework\Unit\Helper\ObjectManager; +use PHPUnit\Framework\MockObject\MockObject; +use PHPUnit\Framework\TestCase; -class AccountTest extends \PHPUnit\Framework\TestCase +class AccountTest extends TestCase { /** * @var string @@ -27,59 +32,51 @@ class AccountTest extends \PHPUnit\Framework\TestCase protected $plugin; /** - * @var Session | \PHPUnit_Framework_MockObject_MockObject + * @var Session|MockObject */ - protected $session; + protected $sessionMock; /** - * @var AbstractAction | \PHPUnit_Framework_MockObject_MockObject + * @var AccountInterface|MockObject */ - protected $subject; + protected $actionMock; /** - * @var Http | \PHPUnit_Framework_MockObject_MockObject + * @var Http|MockObject */ - protected $request; + protected $requestMock; /** - * @var ActionFlag | \PHPUnit_Framework_MockObject_MockObject + * @var ActionFlag|MockObject */ - protected $actionFlag; + protected $actionFlagMock; /** - * @var ResultInterface|\PHPUnit_Framework_MockObject_MockObject + * @var ResultInterface|MockObject */ - private $resultInterface; + private $resultMock; protected function setUp() { - $this->session = $this->getMockBuilder(\Magento\Customer\Model\Session::class) + $this->sessionMock = $this->getMockBuilder(Session::class) ->disableOriginalConstructor() - ->setMethods([ - 'setNoReferer', - 'unsNoReferer', - 'authenticate', - ]) + ->setMethods(['setNoReferer', 'unsNoReferer', 'authenticate']) ->getMock(); - $this->subject = $this->getMockBuilder(AbstractAction::class) - ->setMethods([ - 'getActionFlag', - ]) + $this->actionMock = $this->getMockBuilder(AccountInterface::class) + ->setMethods(['getActionFlag']) ->disableOriginalConstructor() ->getMockForAbstractClass(); - $this->request = $this->getMockBuilder(\Magento\Framework\App\Request\Http::class) + $this->requestMock = $this->getMockBuilder(HttpRequest::class) ->disableOriginalConstructor() - ->setMethods([ - 'getActionName', - ]) + ->setMethods(['getActionName']) ->getMock(); - $this->resultInterface = $this->getMockBuilder(ResultInterface::class) + $this->resultMock = $this->getMockBuilder(ResultInterface::class) ->getMockForAbstractClass(); - $this->actionFlag = $this->getMockBuilder(\Magento\Framework\App\ActionFlag::class) + $this->actionFlagMock = $this->getMockBuilder(ActionFlag::class) ->disableOriginalConstructor() ->getMock(); } @@ -87,50 +84,46 @@ protected function setUp() /** * @param string $action * @param array $allowedActions - * @param boolean $isActionAllowed - * @param boolean $isAuthenticated + * @param boolean $isAllowed * - * @dataProvider beforeDispatchDataProvider + * @dataProvider beforeExecuteDataProvider */ - public function testBeforeDispatch( - $action, - $allowedActions, - $isActionAllowed, - $isAuthenticated + public function testAroundExecuteInterruptsOriginalCallWhenNotAllowed( + string $action, + array $allowedActions, + bool $isAllowed ) { - $this->request->expects($this->once()) + /** @var callable|MockObject $proceedMock */ + $proceedMock = $this->getMockBuilder(\stdClass::class) + ->setMethods(['__invoke']) + ->getMock(); + + $closureMock = Closure::fromCallable($proceedMock); + + $this->requestMock->expects($this->once()) ->method('getActionName') ->willReturn($action); - if ($isActionAllowed) { - $this->session->expects($this->once()) - ->method('setNoReferer') - ->with(true) - ->willReturnSelf(); + if ($isAllowed) { + $proceedMock->expects($this->once())->method('__invoke')->willReturn($this->resultMock); } else { - $this->session->expects($this->once()) - ->method('authenticate') - ->willReturn($isAuthenticated); - if (!$isAuthenticated) { - $this->subject->expects($this->once()) - ->method('getActionFlag') - ->willReturn($this->actionFlag); - - $this->actionFlag->expects($this->once()) - ->method('set') - ->with('', ActionInterface::FLAG_NO_DISPATCH, true) - ->willReturnSelf(); - } + $proceedMock->expects($this->never())->method('__invoke'); } - $plugin = new Account($this->session, $allowedActions); - $plugin->beforeDispatch($this->subject, $this->request); + $plugin = new Account($this->requestMock, $this->sessionMock, $allowedActions); + $result = $plugin->aroundExecute($this->actionMock, $closureMock); + + if ($isAllowed) { + $this->assertSame($this->resultMock, $result); + } else { + $this->assertNull($result); + } } /** * @return array */ - public function beforeDispatchDataProvider() + public function beforeExecuteDataProvider() { return [ [ @@ -165,24 +158,4 @@ public function beforeDispatchDataProvider() ], ]; } - - public function testAfterDispatch() - { - $this->session->expects($this->once()) - ->method('unsNoReferer') - ->with(false) - ->willReturnSelf(); - - $plugin = (new ObjectManager($this))->getObject( - Account::class, - [ - 'session' => $this->session, - 'allowedActions' => ['testaction'] - ] - ); - $this->assertSame( - $this->resultInterface, - $plugin->afterDispatch($this->subject, $this->resultInterface, $this->request) - ); - } } diff --git a/app/code/Magento/Customer/Test/Unit/Controller/Section/LoadTest.php b/app/code/Magento/Customer/Test/Unit/Controller/Section/LoadTest.php index 5a7cf42be2c7e..8eca499f849ea 100644 --- a/app/code/Magento/Customer/Test/Unit/Controller/Section/LoadTest.php +++ b/app/code/Magento/Customer/Test/Unit/Controller/Section/LoadTest.php @@ -166,8 +166,8 @@ public function testExecuteWithThrowException() $this->resultJsonMock->expects($this->once()) ->method('setStatusHeader') ->with( - \Zend\Http\Response::STATUS_CODE_400, - \Zend\Http\AbstractMessage::VERSION_11, + \Laminas\Http\Response::STATUS_CODE_400, + \Laminas\Http\AbstractMessage::VERSION_11, 'Bad Request' ); diff --git a/app/code/Magento/Customer/Test/Unit/CustomerData/SectionConfigConverterTest.php b/app/code/Magento/Customer/Test/Unit/CustomerData/SectionConfigConverterTest.php index bc1085e295995..44401ca5eb7bf 100644 --- a/app/code/Magento/Customer/Test/Unit/CustomerData/SectionConfigConverterTest.php +++ b/app/code/Magento/Customer/Test/Unit/CustomerData/SectionConfigConverterTest.php @@ -6,6 +6,7 @@ namespace Magento\Customer\Test\Unit\CustomerData; +use Magento\Framework\App\Arguments\ValidationState; use Magento\Framework\TestFramework\Unit\Helper\ObjectManager as ObjectManagerHelper; class SectionConfigConverterTest extends \PHPUnit\Framework\TestCase @@ -19,6 +20,12 @@ class SectionConfigConverterTest extends \PHPUnit\Framework\TestCase /** @var \DOMDocument */ protected $source; + /** @var \Magento\Framework\Config\Dom config merger */ + private $configMergerClass; + + /** @var ValidationState */ + private $validationStateMock; + protected function setUp() { $this->source = new \DOMDocument(); @@ -26,20 +33,61 @@ protected function setUp() $this->converter = $this->objectManagerHelper->getObject( \Magento\Customer\CustomerData\SectionConfigConverter::class ); + $this->validationStateMock = $this->createMock(ValidationState::class); + } + + /** + * Return newly created instance of a config merger + * + * @param string $mergerClass + * @param string $initialContents + * @return \Magento\Framework\Config\Dom + * @throws \UnexpectedValueException + */ + private function createConfig($mergerClass, $initialContents) + { + $this->validationStateMock->method('isValidationRequired')->willReturn(\false); + return new $mergerClass( + $initialContents, + $this->validationStateMock, + [ + '/config/action' => 'name', + '/config/action/section' => 'name', + ], + null, + null + ); } public function testConvert() { $this->source->loadXML(file_get_contents(__DIR__ . '/_files/sections.xml')); + $this->configMergerClass = $this->createConfig( + 'Magento\Framework\Config\Dom', + file_get_contents(__DIR__ . '/_files/sections.xml') + ); + + $this->configMergerClass->merge(file_get_contents(__DIR__ . '/_files/sections2.xml')); + $this->assertEquals( [ 'sections' => [ - 'customer/account/logout' => '*', - 'customer/account/editpost' => ['account'], + 'sales/guest/reorder' => ['account'], + 'sales/order/reorder' => ['account', 'cart'], + 'stores/store/switch' => ['account', '*', 'cart'], + 'directory/currency/switch' => ['*'], + 'customer/account/logout' => ['account', 'cart'], + 'customer/account/editpost' => ['account', 'acc', 'cart'], + 'checkout/cart/delete' => ['account', 'acc', 'cart', '*'], + 'customer/account/createpost' => ['account','*'], + 'catalog/product_compare/add' => ['*'], + 'catalog/product_compare/remove' => ['account', 'acc'], + 'catalog/product_compare/clear' => ['*'], + 'checkout/cart/add' => ['*'], ], ], - $this->converter->convert($this->source) + $this->converter->convert($this->configMergerClass->getDom()) ); } } diff --git a/app/code/Magento/Customer/Test/Unit/CustomerData/_files/sections.xml b/app/code/Magento/Customer/Test/Unit/CustomerData/_files/sections.xml index 0b4250f678a68..fac1ef42c8a67 100644 --- a/app/code/Magento/Customer/Test/Unit/CustomerData/_files/sections.xml +++ b/app/code/Magento/Customer/Test/Unit/CustomerData/_files/sections.xml @@ -7,8 +7,56 @@ --> <config xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="urn:magento:module:Magento_Customer:etc/sections.xsd"> - <action name="customer/account/logout"/> + <!-- Actions 1-4 are specified in sections.xml file only --> + <!-- 1 - Action has one Section --> + <action name="sales/guest/reorder"> + <section name="account"/> + </action> + <!-- 2 - Action has two Sections --> + <action name="sales/order/reorder"> + <section name="account"/> + <section name="cart"/> + </action> + <!-- 3 - Action has two Sections and "*" --> + <action name="stores/store/switch"> + <section name="account"/> + <section name="*"/> + <section name="cart"/> + </action> + <!-- 4 - Action has "empty_section" --> + <action name="directory/currency/switch"/> + <!-- Actions 5-12 are specified in files sections.xml and sections2.xml for merging --> + <!-- 5 - Action in both files has unique Section --> + <action name="customer/account/logout"> + <section name="account"/> + </action> + <!-- 6 - Action in both files has at least one identical Section --> <action name="customer/account/editPost"> <section name="account"/> + <section name="acc"/> + </action> + <!-- 7 - Action in both files has at least one identical Section and "*" --> + <action name="checkout/cart/delete"> + <section name="account"/> + <section name="acc"/> + </action> + <!-- 8 - Action in both files has Section and "*" --> + <action name="customer/account/createPost"> + <section name="account"/> + </action> + <!-- 9 - Action in both files has "*" and "*" --> + <action name="catalog/product_compare/add"> + <section name="*"/> + </action> + <!-- 10 - Action in both files has Section and "empty_section" --> + <action name="catalog/product_compare/remove"> + <section name="account"/> + <section name="acc"/> + </action> + <!-- 11 - Action in both files has "empty_section" and "empty_section" --> + <action name="catalog/product_compare/clear"/> + <!-- 12 - Action in both files has "*" and "empty_section" --> + <action name="checkout/cart/add"> + <section name="*"/> </action> </config> diff --git a/app/code/Magento/Customer/Test/Unit/CustomerData/_files/sections2.xml b/app/code/Magento/Customer/Test/Unit/CustomerData/_files/sections2.xml new file mode 100644 index 0000000000000..c3c2b29b358bd --- /dev/null +++ b/app/code/Magento/Customer/Test/Unit/CustomerData/_files/sections2.xml @@ -0,0 +1,40 @@ +<?xml version="1.0"?> +<!-- +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +--> +<config xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:noNamespaceSchemaLocation="urn:magento:module:Magento_Customer:etc/sections.xsd"> + <!-- Actions 5-12 are specified in files sections.xml and sections2.xml for merging --> + <!-- 5 - Action in both files has unique Section --> + <action name="customer/account/logout"> + <section name="cart"/> + </action> + <!-- 6 - Action in both files has at least one identical Section --> + <action name="customer/account/editPost"> + <section name="cart"/> + <section name="account"/> + </action> + <!-- 7 - Action in both files has at least one identical Section and "*" --> + <action name="checkout/cart/delete"> + <section name="cart"/> + <section name="*"/> + <section name="account"/> + </action> + <!-- 8 - Action in both files has Section and "*" --> + <action name="customer/account/createPost"> + <section name="*"/> + </action> + <!-- 9 - Action in both files has "*" and "*" --> + <action name="catalog/product_compare/add"> + <section name="*"/> + </action> + <!-- 10 - Action in both files has Section and "empty_section" --> + <action name="catalog/product_compare/remove"/> + <!-- 11 - Action in both files has "empty_section" and "empty_section" --> + <action name="catalog/product_compare/clear"/> + <!-- 12 - Action in both files has "*" and "empty_section" --> + <action name="checkout/cart/add"/> +</config> diff --git a/app/code/Magento/Customer/Test/Unit/Model/Account/RedirectTest.php b/app/code/Magento/Customer/Test/Unit/Model/Account/RedirectTest.php index 0138c6c709b7c..5c57012e0505c 100644 --- a/app/code/Magento/Customer/Test/Unit/Model/Account/RedirectTest.php +++ b/app/code/Magento/Customer/Test/Unit/Model/Account/RedirectTest.php @@ -11,14 +11,19 @@ use Magento\Customer\Model\Account\Redirect; use Magento\Customer\Model\Url as CustomerUrl; use Magento\Framework\Controller\ResultFactory; +use Magento\Framework\Stdlib\Cookie\CookieMetadataFactory; +use Magento\Framework\Stdlib\Cookie\PublicCookieMetadata; +use Magento\Framework\Stdlib\CookieManagerInterface; use Magento\Framework\TestFramework\Unit\Helper\ObjectManager; use Magento\Framework\Url\HostChecker; use Magento\Store\Model\ScopeInterface; +use PHPUnit\Framework\TestCase; +use PHPUnit\FrameworkMockObject\MockObject; /** * @SuppressWarnings(PHPMD.CouplingBetweenObjects) */ -class RedirectTest extends \PHPUnit\Framework\TestCase +class RedirectTest extends TestCase { /** * @var Redirect @@ -26,123 +31,119 @@ class RedirectTest extends \PHPUnit\Framework\TestCase protected $model; /** - * @var \PHPUnit_Framework_MockObject_MockObject | \Magento\Framework\App\RequestInterface + * @var MockObject|\Magento\Framework\App\RequestInterface */ protected $request; /** - * @var \PHPUnit_Framework_MockObject_MockObject | \Magento\Customer\Model\Session + * @var MockObject|\Magento\Customer\Model\Session */ protected $customerSession; /** - * @var \PHPUnit_Framework_MockObject_MockObject | \Magento\Framework\App\Config\ScopeConfigInterface + * @var MockObject|\Magento\Framework\App\Config\ScopeConfigInterface */ protected $scopeConfig; /** - * @var \PHPUnit_Framework_MockObject_MockObject | \Magento\Store\Model\StoreManagerInterface + * @var MockObject|\Magento\Store\Model\StoreManagerInterface */ protected $storeManager; /** - * @var \PHPUnit_Framework_MockObject_MockObject | \Magento\Store\Model\Store + * @var MockObject|\Magento\Store\Model\Store */ protected $store; /** - * @var \PHPUnit_Framework_MockObject_MockObject | \Magento\Framework\UrlInterface + * @var MockObject|\Magento\Framework\UrlInterface */ protected $url; /** - * @var \PHPUnit_Framework_MockObject_MockObject | \Magento\Framework\Url\DecoderInterface + * @var MockObject|\Magento\Framework\Url\DecoderInterface */ protected $urlDecoder; /** - * @var \PHPUnit_Framework_MockObject_MockObject | \Magento\Customer\Model\Url + * @var MockObject|\Magento\Customer\Model\Url */ protected $customerUrl; /** - * @var \PHPUnit_Framework_MockObject_MockObject | \Magento\Framework\Controller\Result\Redirect + * @var MockObject|\Magento\Framework\Controller\Result\Redirect */ protected $resultRedirect; /** - * @var \PHPUnit_Framework_MockObject_MockObject | \Magento\Framework\Controller\Result\Forward + * @var MockObject|\Magento\Framework\Controller\Result\Forward */ protected $resultForward; /** - * @var ResultFactory | \PHPUnit_Framework_MockObject_MockObject + * @var ResultFactory|MockObject */ protected $resultFactory; /** - * @var HostChecker | \PHPUnit_Framework_MockObject_MockObject + * @var CookieMetadataFactory|MockObject + */ + protected $cookieMetadataFactory; + + /** + * @var HostChecker|MockObject */ private $hostChecker; + /** + * @inheritdoc + */ protected function setUp() { $this->request = $this->getMockForAbstractClass(\Magento\Framework\App\RequestInterface::class); - $this->customerSession = $this->getMockBuilder(\Magento\Customer\Model\Session::class) ->disableOriginalConstructor() - ->setMethods([ - 'getLastCustomerId', - 'isLoggedIn', - 'getId', - 'setLastCustomerId', - 'unsBeforeAuthUrl', - 'getBeforeAuthUrl', - 'setBeforeAuthUrl', - 'getAfterAuthUrl', - 'setAfterAuthUrl', - 'getBeforeRequestParams', - 'getBeforeModuleName', - 'getBeforeControllerName', - 'getBeforeAction', - ]) + ->setMethods( + [ + 'getLastCustomerId', + 'isLoggedIn', + 'getId', + 'setLastCustomerId', + 'unsBeforeAuthUrl', + 'getBeforeAuthUrl', + 'setBeforeAuthUrl', + 'getAfterAuthUrl', + 'setAfterAuthUrl', + 'getBeforeRequestParams', + 'getBeforeModuleName', + 'getBeforeControllerName', + 'getBeforeAction', + ] + ) ->getMock(); $this->scopeConfig = $this->getMockForAbstractClass(\Magento\Framework\App\Config\ScopeConfigInterface::class); - - $this->store = $this->getMockBuilder(\Magento\Store\Model\Store::class) - ->disableOriginalConstructor() - ->getMock(); - + $this->store = $this->createMock(\Magento\Store\Model\Store::class); $this->storeManager = $this->getMockForAbstractClass(\Magento\Store\Model\StoreManagerInterface::class); - $this->storeManager->expects($this->once()) - ->method('getStore') - ->willReturn($this->store); - $this->url = $this->getMockForAbstractClass(\Magento\Framework\UrlInterface::class); $this->urlDecoder = $this->getMockForAbstractClass(\Magento\Framework\Url\DecoderInterface::class); - $this->customerUrl = $this->getMockBuilder(\Magento\Customer\Model\Url::class) - ->setMethods(['DashboardUrl', 'getAccountUrl', 'getLoginUrl', 'getLogoutUrl', 'getDashboardUrl']) - ->disableOriginalConstructor() - ->getMock(); - - $this->resultRedirect = $this->getMockBuilder(\Magento\Framework\Controller\Result\Redirect::class) - ->disableOriginalConstructor() - ->getMock(); - - $this->resultForward = $this->getMockBuilder(\Magento\Framework\Controller\Result\Forward::class) - ->disableOriginalConstructor() - ->getMock(); - - $this->resultFactory = $this->getMockBuilder(\Magento\Framework\Controller\ResultFactory::class) - ->disableOriginalConstructor() - ->getMock(); - - $this->hostChecker = $this->getMockBuilder(HostChecker::class) - ->disableOriginalConstructor() + ->setMethods( + [ + 'DashboardUrl', + 'getAccountUrl', + 'getLoginUrl', + 'getLogoutUrl', + 'getDashboardUrl' + ] + )->disableOriginalConstructor() ->getMock(); + $this->resultRedirect = $this->createMock(\Magento\Framework\Controller\Result\Redirect::class); + $this->resultForward = $this->createMock(\Magento\Framework\Controller\Result\Forward::class); + $this->resultFactory = $this->createMock(\Magento\Framework\Controller\ResultFactory::class); + $this->cookieMetadataFactory = $this->createMock(CookieMetadataFactory::class); + $this->hostChecker = $this->createMock(HostChecker::class); $objectManager = new ObjectManager($this); $this->model = $objectManager->getObject( \Magento\Customer\Model\Account\Redirect::class, @@ -155,12 +156,15 @@ protected function setUp() 'urlDecoder' => $this->urlDecoder, 'customerUrl' => $this->customerUrl, 'resultFactory' => $this->resultFactory, + 'cookieMetadataFactory' => $this->cookieMetadataFactory, 'hostChecker' => $this->hostChecker, ] ); } /** + * Verify get redirect method + * * @param int $customerId * @param int $lastCustomerId * @param string $referer @@ -201,6 +205,9 @@ public function testGetRedirect( ->willReturnSelf(); // Preparations for method prepareRedirectUrl() + $this->storeManager->expects($this->once()) + ->method('getStore') + ->willReturn($this->store); $this->store->expects($this->once())->method('getBaseUrl')->willReturn($baseUrl); $this->customerSession->expects($this->any())->method('getBeforeAuthUrl')->willReturn($beforeAuthUrl); @@ -244,6 +251,8 @@ public function testGetRedirect( } /** + * Redirect data provider + * * @return array */ public function getRedirectDataProvider() @@ -292,7 +301,12 @@ public function getRedirectDataProvider() ]; } - public function testBeforeRequestParams() + /** + * Verify before request params + * + * @return void + */ + public function testBeforeRequestParams(): void { $requestParams = [ 'param1' => 'value1', @@ -302,6 +316,9 @@ public function testBeforeRequestParams() $controller = 'controller'; $action = 'action'; + $this->storeManager->expects($this->once()) + ->method('getStore') + ->willReturn($this->store); $this->customerSession->expects($this->exactly(2)) ->method('getBeforeRequestParams') ->willReturn($requestParams); @@ -314,7 +331,6 @@ public function testBeforeRequestParams() $this->customerSession->expects($this->once()) ->method('getBeforeAction') ->willReturn($action); - $this->resultForward->expects($this->once()) ->method('setParams') ->with($requestParams) @@ -331,7 +347,6 @@ public function testBeforeRequestParams() ->method('forward') ->with($action) ->willReturnSelf(); - $this->resultFactory->expects($this->once()) ->method('create') ->with(ResultFactory::TYPE_FORWARD) @@ -340,4 +355,82 @@ public function testBeforeRequestParams() $result = $this->model->getRedirect(); $this->assertSame($this->resultForward, $result); } + + /** + * Verify set redirect cokkie method + * + * @return void + */ + public function testSetRedirectCookie(): void + { + $coockieManagerMock = $this->createMock(CookieManagerInterface::class); + $publicMetadataMock = $this->createMock(PublicCookieMetadata::class); + $routeMock = 'route'; + + $this->model->setCookieManager($coockieManagerMock); + + $this->storeManager->expects($this->once()) + ->method('getStore') + ->willReturn($this->store); + $this->store->expects($this->once()) + ->method('getStorePath') + ->willReturn('storePath'); + $this->cookieMetadataFactory->expects($this->once()) + ->method('createPublicCookieMetadata') + ->willReturn($publicMetadataMock); + $publicMetadataMock->expects($this->once()) + ->method('setHttpOnly') + ->with(true) + ->willReturnSelf(); + $publicMetadataMock->expects($this->once()) + ->method('setDuration') + ->with(3600) + ->willReturnSelf(); + $publicMetadataMock->expects($this->once()) + ->method('setPath') + ->with('storePath') + ->willReturnSelf(); + $coockieManagerMock->expects($this->once()) + ->method('setPublicCookie') + ->with( + Redirect::LOGIN_REDIRECT_URL, + $routeMock, + $publicMetadataMock + ); + $this->model->setRedirectCookie($routeMock); + } + + /** + * Verify clear redirect cookie + * + * @return void + */ + public function testClearRedirectCookie(): void + { + $coockieManagerMock = $this->createMock(CookieManagerInterface::class); + $publicMetadataMock = $this->createMock(PublicCookieMetadata::class); + + $this->model->setCookieManager($coockieManagerMock); + + $this->storeManager->expects($this->once()) + ->method('getStore') + ->willReturn($this->store); + $this->store->expects($this->once()) + ->method('getStorePath') + ->willReturn('storePath'); + $this->cookieMetadataFactory->expects($this->once()) + ->method('createPublicCookieMetadata') + ->willReturn($publicMetadataMock); + $publicMetadataMock->expects($this->once()) + ->method('setPath') + ->with('storePath') + ->willReturnSelf(); + $coockieManagerMock->expects($this->once()) + ->method('deleteCookie') + ->with( + Redirect::LOGIN_REDIRECT_URL, + $publicMetadataMock + ); + $this->model->clearRedirectCookie(); + } } diff --git a/app/code/Magento/Customer/Test/Unit/Model/AccountManagementTest.php b/app/code/Magento/Customer/Test/Unit/Model/AccountManagementTest.php index 3c38cd0f7b4e2..394c3ba43ebd4 100644 --- a/app/code/Magento/Customer/Test/Unit/Model/AccountManagementTest.php +++ b/app/code/Magento/Customer/Test/Unit/Model/AccountManagementTest.php @@ -329,7 +329,6 @@ public function testCreateAccountWithPasswordHashWithExistingCustomer() public function testCreateAccountWithPasswordHashWithCustomerWithoutStoreId() { $websiteId = 1; - $storeId = null; $defaultStoreId = 1; $customerId = 1; $customerEmail = 'email@email.com'; @@ -359,9 +358,9 @@ public function testCreateAccountWithPasswordHashWithCustomerWithoutStoreId() $customer->expects($this->atLeastOnce()) ->method('getWebsiteId') ->willReturn($websiteId); - $customer->expects($this->atLeastOnce()) + $customer->expects($this->at(10)) ->method('getStoreId') - ->willReturn($storeId); + ->willReturn(1); $customer->expects($this->once()) ->method('setStoreId') ->with($defaultStoreId); @@ -378,9 +377,7 @@ public function testCreateAccountWithPasswordHashWithCustomerWithoutStoreId() ->method('get') ->with($customerEmail) ->willReturn($customer); - $this->share - ->expects($this->atLeastOnce()) - ->method('isWebsiteScope') + $this->share->method('isWebsiteScope') ->willReturn(true); $this->storeManager ->expects($this->atLeastOnce()) @@ -405,7 +402,6 @@ public function testCreateAccountWithPasswordHashWithCustomerWithoutStoreId() public function testCreateAccountWithPasswordHashWithLocalizedException() { $websiteId = 1; - $storeId = null; $defaultStoreId = 1; $customerId = 1; $customerEmail = 'email@email.com'; @@ -419,8 +415,7 @@ public function testCreateAccountWithPasswordHashWithLocalizedException() ->method('getId') ->willReturn($defaultStoreId); $website = $this->getMockBuilder(\Magento\Store\Model\Website::class)->disableOriginalConstructor()->getMock(); - $website->expects($this->once()) - ->method('getStoreIds') + $website->method('getStoreIds') ->willReturn([1, 2, 3]); $website->expects($this->once()) ->method('getDefaultStore') @@ -435,9 +430,9 @@ public function testCreateAccountWithPasswordHashWithLocalizedException() $customer->expects($this->atLeastOnce()) ->method('getWebsiteId') ->willReturn($websiteId); - $customer->expects($this->atLeastOnce()) + $customer->expects($this->at(10)) ->method('getStoreId') - ->willReturn($storeId); + ->willReturn(1); $customer->expects($this->once()) ->method('setStoreId') ->with($defaultStoreId); @@ -454,9 +449,7 @@ public function testCreateAccountWithPasswordHashWithLocalizedException() ->method('get') ->with($customerEmail) ->willReturn($customer); - $this->share - ->expects($this->once()) - ->method('isWebsiteScope') + $this->share->method('isWebsiteScope') ->willReturn(true); $this->storeManager ->expects($this->atLeastOnce()) @@ -481,7 +474,6 @@ public function testCreateAccountWithPasswordHashWithLocalizedException() public function testCreateAccountWithPasswordHashWithAddressException() { $websiteId = 1; - $storeId = null; $defaultStoreId = 1; $customerId = 1; $customerEmail = 'email@email.com'; @@ -498,8 +490,7 @@ public function testCreateAccountWithPasswordHashWithAddressException() ->method('getId') ->willReturn($defaultStoreId); $website = $this->getMockBuilder(\Magento\Store\Model\Website::class)->disableOriginalConstructor()->getMock(); - $website->expects($this->once()) - ->method('getStoreIds') + $website->method('getStoreIds') ->willReturn([1, 2, 3]); $website->expects($this->once()) ->method('getDefaultStore') @@ -514,9 +505,9 @@ public function testCreateAccountWithPasswordHashWithAddressException() $customer->expects($this->atLeastOnce()) ->method('getWebsiteId') ->willReturn($websiteId); - $customer->expects($this->atLeastOnce()) + $customer->expects($this->at(10)) ->method('getStoreId') - ->willReturn($storeId); + ->willReturn(1); $customer->expects($this->once()) ->method('setStoreId') ->with($defaultStoreId); @@ -533,9 +524,7 @@ public function testCreateAccountWithPasswordHashWithAddressException() ->method('get') ->with($customerEmail) ->willReturn($customer); - $this->share - ->expects($this->once()) - ->method('isWebsiteScope') + $this->share->method('isWebsiteScope') ->willReturn(true); $this->storeManager ->expects($this->atLeastOnce()) @@ -648,7 +637,6 @@ public function testCreateAccountWithPasswordHashWithNewCustomerAndLocalizedExce public function testCreateAccountWithoutPassword() { $websiteId = 1; - $storeId = null; $defaultStoreId = 1; $customerId = 1; $customerEmail = 'email@email.com'; @@ -683,9 +671,8 @@ public function testCreateAccountWithoutPassword() $customer->expects($this->atLeastOnce()) ->method('getWebsiteId') ->willReturn($websiteId); - $customer->expects($this->atLeastOnce()) - ->method('getStoreId') - ->willReturn($storeId); + $customer->expects($this->at(10))->method('getStoreId') + ->willReturn(1); $customer->expects($this->once()) ->method('setStoreId') ->with($defaultStoreId); @@ -700,8 +687,7 @@ public function testCreateAccountWithoutPassword() ->method('get') ->with($customerEmail) ->willReturn($customer); - $this->share->expects($this->once()) - ->method('isWebsiteScope') + $this->share->method('isWebsiteScope') ->willReturn(true); $this->storeManager->expects($this->atLeastOnce()) ->method('getWebsite') @@ -861,7 +847,6 @@ public function testCreateAccountInputExceptionExtraLongPassword() public function testCreateAccountWithPassword() { $websiteId = 1; - $storeId = null; $defaultStoreId = 1; $customerId = 1; $customerEmail = 'email@email.com'; @@ -940,9 +925,9 @@ public function testCreateAccountWithPassword() $customer->expects($this->atLeastOnce()) ->method('getWebsiteId') ->willReturn($websiteId); - $customer->expects($this->atLeastOnce()) + $customer->expects($this->at(11)) ->method('getStoreId') - ->willReturn($storeId); + ->willReturn(1); $customer->expects($this->once()) ->method('setStoreId') ->with($defaultStoreId); @@ -957,8 +942,7 @@ public function testCreateAccountWithPassword() ->method('get') ->with($customerEmail) ->willReturn($customer); - $this->share->expects($this->once()) - ->method('isWebsiteScope') + $this->share->method('isWebsiteScope') ->willReturn(true); $this->storeManager->expects($this->atLeastOnce()) ->method('getWebsite') @@ -1567,6 +1551,7 @@ public function testChangePassword() ->with($customer); $this->sessionManager->expects($this->atLeastOnce())->method('getSessionId'); + $this->sessionManager->expects($this->atLeastOnce())->method('regenerateId'); $visitor = $this->getMockBuilder(\Magento\Customer\Model\Visitor::class) ->disableOriginalConstructor() @@ -1644,6 +1629,7 @@ function ($string) { $this->sessionManager->method('isSessionExists')->willReturn(false); $this->sessionManager->expects($this->atLeastOnce())->method('getSessionId'); + $this->sessionManager->expects($this->atLeastOnce())->method('regenerateId'); $visitor = $this->getMockBuilder(\Magento\Customer\Model\Visitor::class) ->disableOriginalConstructor() ->setMethods(['getSessionId']) @@ -1810,62 +1796,37 @@ public function dataProviderGetConfirmationStatus() /** * @expectedException \Magento\Framework\Exception\LocalizedException + * @expectedExceptionMessage Exception message */ - public function testCreateAccountWithPasswordHashForGuest() + public function testCreateAccountWithPasswordHashForGuestException() { $storeId = 1; - $storeName = 'store_name'; $websiteId = 1; $hash = '4nj54lkj5jfi03j49f8bgujfgsd'; $storeMock = $this->getMockBuilder(\Magento\Store\Model\Store::class) ->disableOriginalConstructor() ->getMock(); - $storeMock->expects($this->once()) - ->method('getId') + $storeMock->method('getId') ->willReturn($storeId); - $storeMock->expects($this->once()) - ->method('getWebsiteId') - ->willReturn($websiteId); - $storeMock->expects($this->once()) - ->method('getName') - ->willReturn($storeName); - - $this->storeManager->expects($this->exactly(3)) - ->method('getStore') - ->willReturn($storeMock); + $this->storeManager->method('getStores') + ->willReturn([$storeMock]); $customerMock = $this->getMockBuilder(Customer::class) ->disableOriginalConstructor() ->getMock(); - $customerMock->expects($this->exactly(2)) - ->method('getId') - ->willReturn(null); - $customerMock->expects($this->exactly(3)) + $customerMock->expects($this->at(1)) ->method('getStoreId') - ->willReturn(null); - $customerMock->expects($this->exactly(3)) + ->willReturn($storeId); + $customerMock->expects($this->at(4)) + ->method('getStoreId') + ->willReturn($storeId); + $customerMock->expects($this->at(2)) ->method('getWebsiteId') - ->willReturn(null); - $customerMock->expects($this->once()) - ->method('setStoreId') - ->with($storeId) - ->willReturnSelf(); - $customerMock->expects($this->once()) - ->method('setWebsiteId') - ->with($websiteId) - ->willReturnSelf(); - $customerMock->expects($this->once()) - ->method('setCreatedIn') - ->with($storeName) - ->willReturnSelf(); - $customerMock->expects($this->once()) - ->method('getAddresses') - ->willReturn(null); - $customerMock->expects($this->once()) - ->method('setAddresses') - ->with(null) - ->willReturnSelf(); + ->willReturn($websiteId); + $customerMock->expects($this->at(5)) + ->method('getId') + ->willReturn(1); $this->customerRepository ->expects($this->once()) @@ -2030,7 +1991,6 @@ private function prepareDateTimeFactory() public function testCreateAccountUnexpectedValueException(): void { $websiteId = 1; - $storeId = null; $defaultStoreId = 1; $customerId = 1; $customerEmail = 'email@email.com'; @@ -2048,8 +2008,7 @@ public function testCreateAccountUnexpectedValueException(): void ->method('getId') ->willReturn($defaultStoreId); $website = $this->createMock(\Magento\Store\Model\Website::class); - $website->expects($this->atLeastOnce()) - ->method('getStoreIds') + $website->method('getStoreIds') ->willReturn([1, 2, 3]); $website->expects($this->once()) ->method('getDefaultStore') @@ -2064,9 +2023,9 @@ public function testCreateAccountUnexpectedValueException(): void $customer->expects($this->atLeastOnce()) ->method('getWebsiteId') ->willReturn($websiteId); - $customer->expects($this->atLeastOnce()) + $customer->expects($this->at(10)) ->method('getStoreId') - ->willReturn($storeId); + ->willReturn(1); $customer->expects($this->once()) ->method('setStoreId') ->with($defaultStoreId); @@ -2080,8 +2039,7 @@ public function testCreateAccountUnexpectedValueException(): void ->method('get') ->with($customerEmail) ->willReturn($customer); - $this->share->expects($this->once()) - ->method('isWebsiteScope') + $this->share->method('isWebsiteScope') ->willReturn(true); $this->storeManager->expects($this->atLeastOnce()) ->method('getWebsite') @@ -2162,4 +2120,49 @@ public function testCreateAccountWithStoreNotInWebsite() ->willReturn($website); $this->accountManagement->createAccountWithPasswordHash($customerMock, $hash); } + + /** + * Test for validating customer store id by customer website id. + * + * @return void + */ + public function testValidateCustomerStoreIdByWebsiteId(): void + { + $customerMock = $this->getMockBuilder(CustomerInterface::class) + ->disableOriginalConstructor() + ->getMock(); + $customerMock->method('getWebsiteId')->willReturn(1); + $customerMock->method('getStoreId')->willReturn(1); + $storeMock = $this->getMockBuilder(\Magento\Store\Model\Store::class) + ->disableOriginalConstructor() + ->getMock(); + $storeMock->method('getId') + ->willReturn(1); + $this->storeManager->method('getStores') + ->willReturn([$storeMock]); + + $this->assertTrue($this->accountManagement->validateCustomerStoreIdByWebsiteId($customerMock)); + } + + /** + * Test for validating customer store id by customer website id with Exception + * + * @expectedException \Magento\Framework\Exception\LocalizedException + * @expectedExceptionMessage The store view is not in the associated website. + */ + public function testValidateCustomerStoreIdByWebsiteIdException(): void + { + $customerMock = $this->getMockBuilder(CustomerInterface::class) + ->disableOriginalConstructor() + ->getMock(); + $storeMock = $this->getMockBuilder(\Magento\Store\Model\Store::class) + ->disableOriginalConstructor() + ->getMock(); + $storeMock->method('getId') + ->willReturn(1); + $this->storeManager->method('getStores') + ->willReturn([$storeMock]); + + $this->assertTrue($this->accountManagement->validateCustomerStoreIdByWebsiteId($customerMock)); + } } diff --git a/app/code/Magento/Customer/Test/Unit/Model/App/Action/ContextPluginTest.php b/app/code/Magento/Customer/Test/Unit/Model/App/Action/ContextPluginTest.php index 4b89fda7c7051..4e722a36b57da 100644 --- a/app/code/Magento/Customer/Test/Unit/Model/App/Action/ContextPluginTest.php +++ b/app/code/Magento/Customer/Test/Unit/Model/App/Action/ContextPluginTest.php @@ -6,57 +6,56 @@ namespace Magento\Customer\Test\Unit\Model\App\Action; +use Magento\Customer\Model\App\Action\ContextPlugin; use Magento\Customer\Model\Context; +use Magento\Customer\Model\Session; +use Magento\Framework\App\Action\Action; +use Magento\Framework\App\Http\Context as HttpContext; +use PHPUnit\Framework\MockObject\MockObject; +use PHPUnit\Framework\TestCase; /** - * Class ContextPluginTest + * Unit Tests to cover ContextPlugin for Action Context */ -class ContextPluginTest extends \PHPUnit\Framework\TestCase +class ContextPluginTest extends TestCase { + const STUB_CUSTOMER_GROUP = 'UAH'; + const STUB_CUSTOMER_NOT_LOGGED_IN = 0; /** - * @var \Magento\Customer\Model\App\Action\ContextPlugin + * @var ContextPlugin */ protected $plugin; /** - * @var \Magento\Customer\Model\Session|\PHPUnit_Framework_MockObject_MockObject + * @var Session|MockObject */ protected $customerSessionMock; /** - * @var \Magento\Framework\App\Http\Context $httpContext|\PHPUnit_Framework_MockObject_MockObject + * @var HttpContext|MockObject */ protected $httpContextMock; /** - * @var \PHPUnit_Framework_MockObject_MockObject + * @var Action|MockObject */ protected $subjectMock; - /** - * @var \PHPUnit_Framework_MockObject_MockObject - */ - protected $requestMock; - /** * Set up */ protected function setUp() { - $this->customerSessionMock = $this->createMock(\Magento\Customer\Model\Session::class); - $this->httpContextMock = $this->createMock(\Magento\Framework\App\Http\Context::class); - $this->subjectMock = $this->createMock(\Magento\Framework\App\Action\Action::class); - $this->requestMock = $this->createMock(\Magento\Framework\App\RequestInterface::class); - $this->plugin = new \Magento\Customer\Model\App\Action\ContextPlugin( + $this->customerSessionMock = $this->createMock(Session::class); + $this->httpContextMock = $this->createMock(HttpContext::class); + $this->subjectMock = $this->createMock(Action::class); + $this->plugin = new ContextPlugin( $this->customerSessionMock, $this->httpContextMock ); } - /** - * Test aroundDispatch - */ - public function testBeforeDispatch() + public function testBeforeExecute() { $this->customerSessionMock->expects($this->once()) ->method('getCustomerGroupId') @@ -69,11 +68,11 @@ public function testBeforeDispatch() ->will( $this->returnValueMap( [ - [Context::CONTEXT_GROUP, 'UAH', $this->httpContextMock], - [Context::CONTEXT_AUTH, 0, $this->httpContextMock], + [Context::CONTEXT_GROUP, self::STUB_CUSTOMER_GROUP, $this->httpContextMock], + [Context::CONTEXT_AUTH, self::STUB_CUSTOMER_NOT_LOGGED_IN, $this->httpContextMock], ] ) ); - $this->plugin->beforeDispatch($this->subjectMock, $this->requestMock); + $this->plugin->beforeExecute($this->subjectMock); } } diff --git a/app/code/Magento/Customer/Test/Unit/Model/AttributeMetadataResolverTest.php b/app/code/Magento/Customer/Test/Unit/Model/AttributeMetadataResolverTest.php new file mode 100644 index 0000000000000..aef9d8ca40e85 --- /dev/null +++ b/app/code/Magento/Customer/Test/Unit/Model/AttributeMetadataResolverTest.php @@ -0,0 +1,166 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +declare(strict_types=1); + +namespace Magento\Customer\Test\Unit\Model; + +use Magento\Customer\Model\Attribute; +use Magento\Customer\Model\AttributeMetadataResolver; +use Magento\Customer\Model\Config\Share as ShareConfig; +use Magento\Customer\Model\FileUploaderDataResolver; +use Magento\Customer\Model\GroupManagement; +use Magento\Customer\Model\ResourceModel\Address\Attribute\Source\CountryWithWebsites; +use Magento\Eav\Model\Entity\Type; +use Magento\Framework\View\Element\UiComponent\ContextInterface; +use Magento\Ui\DataProvider\EavValidationRules; +use PHPUnit\Framework\MockObject\MockObject; +use PHPUnit\Framework\TestCase; + +/** + * Class AttributeMetadataResolverTest + * + * Validate attributeMetadata contains correct values in meta data array + */ +class AttributeMetadataResolverTest extends TestCase +{ + /** + * @var CountryWithWebsites|MockObject + */ + private $countryWithWebsiteSource; + + /** + * @var EavValidationRules|MockObject + */ + private $eavValidationRules; + + /** + * @var FileUploaderDataResolver|MockObject + */ + private $fileUploaderDataResolver; + + /** + * @var ShareConfig|MockObject + */ + private $shareConfig; + + /** + * @var GroupManagement|MockObject + */ + private $groupManagement; + + /** + * @var ContextInterface|MockObject + */ + private $context; + + /** + * @var AttributeMetadataResolver + */ + private $model; + + /** + * @var Attribute|MockObject + */ + private $attribute; + + /** + * @inheritdoc + */ + public function setUp() + { + $this->countryWithWebsiteSource = $this->getMockBuilder(CountryWithWebsites::class) + ->setMethods(['getAllOptions']) + ->disableOriginalConstructor() + ->getMock(); + $this->eavValidationRules = $this->getMockBuilder(EavValidationRules::class) + ->setMethods(['build']) + ->disableOriginalConstructor() + ->getMock(); + $this->fileUploaderDataResolver = $this->getMockBuilder(FileUploaderDataResolver::class) + ->setMethods(['overrideFileUploaderMetadata']) + ->disableOriginalConstructor() + ->getMock(); + $this->context = $this->getMockBuilder(ContextInterface::class) + ->disableOriginalConstructor() + ->getMock(); + $this->shareConfig = $this->getMockBuilder(ShareConfig::class) + ->disableOriginalConstructor() + ->getMock(); + $this->groupManagement = $this->getMockBuilder(GroupManagement::class) + ->setMethods(['getId', 'getDefaultGroup']) + ->disableOriginalConstructor() + ->getMock(); + $this->attribute = $this->getMockBuilder(Attribute::class) + ->setMethods([ + 'usesSource', + 'getDataUsingMethod', + 'getAttributeCode', + 'getFrontendInput', + 'getSource', + 'setDataUsingMethod' + ]) + ->disableOriginalConstructor() + ->getMock(); + + $this->model = new AttributeMetadataResolver( + $this->countryWithWebsiteSource, + $this->eavValidationRules, + $this->fileUploaderDataResolver, + $this->context, + $this->shareConfig, + $this->groupManagement + ); + } + + /** + * Test to get meta data of the customer or customer address attribute + * + * @return void + */ + public function testGetAttributesMetaHasDefaultAttributeValue(): void + { + $rules = [ + 'required-entry' => true + ]; + $defaultGroupId = '3'; + $allowToShowHiddenAttributes = false; + $usesSource = false; + $entityType = $this->getMockBuilder(Type::class) + ->disableOriginalConstructor() + ->getMock(); + $this->attribute->expects($this->once()) + ->method('usesSource') + ->willReturn($usesSource); + $this->attribute->expects($this->once()) + ->method('getAttributeCode') + ->willReturn('group_id'); + $this->groupManagement->expects($this->once()) + ->method('getDefaultGroup') + ->willReturnSelf(); + $this->groupManagement->expects($this->once()) + ->method('getId') + ->willReturn($defaultGroupId); + $this->attribute->expects($this->at(9)) + ->method('getDataUsingMethod') + ->with('default_value') + ->willReturn($defaultGroupId); + $this->attribute->expects($this->once()) + ->method('setDataUsingMethod') + ->willReturnSelf(); + $this->eavValidationRules->expects($this->once()) + ->method('build') + ->with($this->attribute) + ->willReturn($rules); + $this->fileUploaderDataResolver->expects($this->once()) + ->method('overrideFileUploaderMetadata') + ->with($entityType, $this->attribute) + ->willReturnSelf(); + + $meta = $this->model->getAttributesMeta($this->attribute, $entityType, $allowToShowHiddenAttributes); + $this->assertArrayHasKey('default', $meta['arguments']['data']['config']); + $this->assertEquals($defaultGroupId, $meta['arguments']['data']['config']['default']); + } +} diff --git a/app/code/Magento/Customer/Test/Unit/Model/Data/CustomerTest.php b/app/code/Magento/Customer/Test/Unit/Model/Data/CustomerTest.php new file mode 100644 index 0000000000000..395967c46ae67 --- /dev/null +++ b/app/code/Magento/Customer/Test/Unit/Model/Data/CustomerTest.php @@ -0,0 +1,81 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ + +namespace Magento\Customer\Test\Unit\Model\Data; + +use Magento\Customer\Model\Data\Customer; +use Magento\Framework\TestFramework\Unit\Helper\ObjectManager; +use PHPUnit\Framework\TestCase; + +/** + * Unit test for customer data model + */ +class CustomerTest extends TestCase +{ + /** @var Customer */ + protected $model; + + /** @var ObjectManager */ + protected $objectManager; + + protected function setUp() + { + $this->objectManager = new ObjectManager($this); + + $this->model = $this->objectManager->getObject(Customer::class); + } + + /** + * Test getGroupId() + * + * @return void + */ + public function testGetGroupId() + { + $testGroupId = 3; + $this->model->setGroupId($testGroupId); + $this->assertEquals($testGroupId, $this->model->getGroupId()); + } + + /** + * Test getCreatedIn() + * + * @param array|string $options + * @param array $expectedResult + * + * @dataProvider getCreatedInDataProvider + * + * @return void + */ + public function testGetCreatedIn($options, $expectedResult) + { + $optionsCount = count($options); + $expectedCount = count($expectedResult); + + for ($i = 0; $i < $optionsCount; $i++) { + $this->model->setCreatedIn($options[$i]); + for ($j = $i; $j < $expectedCount; $j++) { + $this->assertEquals($expectedResult[$j], $this->model->getCreatedIn()); + break; + } + } + } + + /** + * Data provider for testGetCreatedIn + * + * @return array + */ + public function getCreatedInDataProvider() + { + return [ + 'array' => [ + 'options' => ['Default', 'Admin', 'US'], + 'expectedResult' => ['Default', 'Admin', 'US'] + ] + ]; + } +} diff --git a/app/code/Magento/Customer/Test/Unit/Model/EmailNotificationTest.php b/app/code/Magento/Customer/Test/Unit/Model/EmailNotificationTest.php index ff83ef62c6aa7..a8bf94247fd6d 100644 --- a/app/code/Magento/Customer/Test/Unit/Model/EmailNotificationTest.php +++ b/app/code/Magento/Customer/Test/Unit/Model/EmailNotificationTest.php @@ -4,6 +4,8 @@ * See COPYING.txt for license details. */ +declare(strict_types=1); + namespace Magento\Customer\Test\Unit\Model; use Magento\Customer\Api\Data\CustomerInterface; @@ -11,65 +13,109 @@ use Magento\Framework\App\Area; use Magento\Framework\Mail\Template\SenderResolverInterface; use Magento\Store\Model\ScopeInterface; +use PHPUnit\Framework\TestCase; +use PHPUnit\Framework\MockObject\MockObject; +use Magento\Store\Model\Store; +use Magento\Customer\Model\Data\CustomerSecure; +use Magento\Store\Model\Website; +use Magento\Framework\TestFramework\Unit\Helper\ObjectManager as ObjectManagerHelper; /** - * Class EmailNotificationTest + * Unit test for \Magento\Customer\Model\EmailNotification * * @SuppressWarnings(PHPMD.CouplingBetweenObjects) */ -class EmailNotificationTest extends \PHPUnit\Framework\TestCase +class EmailNotificationTest extends TestCase { /** - * @var \Magento\Customer\Model\CustomerRegistry|\PHPUnit_Framework_MockObject_MockObject + * @var int + */ + private const STUB_CUSTOMER_ID = 1; + + /** + * @var int + */ + private const STUB_CUSTOMER_STORE_ID = 2; + + /** + * @var int + */ + private const STUB_CUSTOMER_WEBSITE_ID = 1; + + /** + * @var string + */ + private const STUB_CUSTOMER_EMAIL = 'email@email.com'; + + /** + * @var string + */ + private const STUB_CUSTOMER_NAME = 'Customer Name'; + + /** + * @var string + */ + private const STUB_EMAIL_IDENTIFIER = 'Template Identifier'; + + /** + * @var string + */ + private const STUB_SENDER = 'Sender'; + + /** + * @var \Magento\Customer\Model\CustomerRegistry|MockObject */ private $customerRegistryMock; /** - * @var \Magento\Store\Model\StoreManagerInterface|\PHPUnit_Framework_MockObject_MockObject + * @var \Magento\Store\Model\StoreManagerInterface|MockObject */ private $storeManagerMock; /** - * @var \Magento\Framework\Mail\Template\TransportBuilder|\PHPUnit_Framework_MockObject_MockObject + * @var \Magento\Framework\Mail\Template\TransportBuilder|MockObject */ private $transportBuilderMock; /** - * @var \Magento\Customer\Helper\View|\PHPUnit_Framework_MockObject_MockObject + * @var \Magento\Customer\Helper\View|MockObject */ private $customerViewHelperMock; /** - * @var \Magento\Framework\Reflection\DataObjectProcessor|\PHPUnit_Framework_MockObject_MockObject + * @var \Magento\Framework\Reflection\DataObjectProcessor|MockObject */ private $dataProcessorMock; /** - * @var \PHPUnit_Framework_MockObject_MockObject|\Magento\Customer\Model\Data\CustomerSecure + * @var CustomerSecure|MockObject */ private $customerSecureMock; /** - * @var \Magento\Framework\App\Config\ScopeConfigInterface|\PHPUnit_Framework_MockObject_MockObject + * @var \Magento\Framework\App\Config\ScopeConfigInterface|MockObject */ private $scopeConfigMock; /** - * @var \PHPUnit_Framework_MockObject_MockObject|\Magento\Store\Model\Store + * @var \Magento\Store\Model\Store|MockObject */ private $storeMock; /** - * @var \Magento\Customer\Model\EmailNotification + * @var EmailNotification */ private $model; /** - * @var SenderResolverInterface|\PHPUnit_Framework_MockObject_MockObject + * @var SenderResolverInterface|MockObject */ private $senderResolverMock; - public function setUp() + /** + * @inheritdoc + */ + public function setUp(): void { $this->customerRegistryMock = $this->createMock(\Magento\Customer\Model\CustomerRegistry::class); @@ -92,16 +138,16 @@ public function setUp() ->method('getScopeConfig') ->willReturn($this->scopeConfigMock); - $this->customerSecureMock = $this->createMock(\Magento\Customer\Model\Data\CustomerSecure::class); + $this->customerSecureMock = $this->createMock(CustomerSecure::class); - $this->storeMock = $this->createMock(\Magento\Store\Model\Store::class); + $this->storeMock = $this->createMock(Store::class); $this->senderResolverMock = $this->getMockBuilder(SenderResolverInterface::class) ->setMethods(['resolve']) ->disableOriginalConstructor() ->getMockForAbstractClass(); - $objectManager = new \Magento\Framework\TestFramework\Unit\Helper\ObjectManager($this); + $objectManager = new ObjectManagerHelper($this); $this->model = $objectManager->getObject( EmailNotification::class, @@ -118,6 +164,8 @@ public function setUp() } /** + * Test email notify when credentials changed + * * @param int $testNumber * @param string $oldEmail * @param string $newEmail @@ -126,16 +174,10 @@ public function setUp() * @dataProvider sendNotificationEmailsDataProvider * @SuppressWarnings(PHPMD.ExcessiveMethodLength) */ - public function testCredentialsChanged($testNumber, $oldEmail, $newEmail, $isPasswordChanged) + public function testEmailNotifyWhenCredentialsChanged($testNumber, $oldEmail, $newEmail, $isPasswordChanged): void { - $customerId = 1; - $customerStoreId = 2; - $customerWebsiteId = 1; $customerData = ['key' => 'value']; - $customerName = 'Customer Name'; - $templateIdentifier = 'Template Identifier'; - $sender = 'Sender'; - $senderValues = ['name' => $sender, 'email' => $sender]; + $senderValues = ['name' => self::STUB_SENDER, 'email' => self::STUB_SENDER]; $expects = $this->once(); $xmlPathTemplate = EmailNotification::XML_PATH_RESET_PASSWORD_TEMPLATE; @@ -145,7 +187,7 @@ public function testCredentialsChanged($testNumber, $oldEmail, $newEmail, $isPas $expects = $this->once(); break; case 2: - $xmlPathTemplate = \Magento\Customer\Model\EmailNotification::XML_PATH_CHANGE_EMAIL_TEMPLATE; + $xmlPathTemplate = EmailNotification::XML_PATH_CHANGE_EMAIL_TEMPLATE; $expects = $this->exactly(2); break; case 3: @@ -157,44 +199,46 @@ public function testCredentialsChanged($testNumber, $oldEmail, $newEmail, $isPas $this->senderResolverMock ->expects($expects) ->method('resolve') - ->with($sender, $customerStoreId) + ->with(self::STUB_SENDER, self::STUB_CUSTOMER_STORE_ID) ->willReturn($senderValues); - /** @var \PHPUnit_Framework_MockObject_MockObject $origCustomer */ + /** + * @var MockObject $origCustomer + */ $origCustomer = $this->createMock(CustomerInterface::class); $origCustomer->expects($this->any()) ->method('getStoreId') ->willReturn(0); $origCustomer->expects($this->any()) ->method('getId') - ->willReturn($customerId); + ->willReturn(self::STUB_CUSTOMER_ID); $origCustomer->expects($this->any()) ->method('getWebsiteId') - ->willReturn($customerWebsiteId); + ->willReturn(self::STUB_CUSTOMER_WEBSITE_ID); - $storeMock = $this->createMock(\Magento\Store\Model\Store::class); + $storeMock = $this->createMock(Store::class); $storeMock->expects($this->any()) ->method('getId') - ->willReturn($customerStoreId); + ->willReturn(self::STUB_CUSTOMER_STORE_ID); $this->storeManagerMock->expects(clone $expects) ->method('getStore') ->willReturn($storeMock); - $websiteMock = $this->createPartialMock(\Magento\Store\Model\Website::class, ['getStoreIds']); + $websiteMock = $this->createPartialMock(Website::class, ['getStoreIds']); $websiteMock->expects($this->any()) ->method('getStoreIds') - ->willReturn([$customerStoreId]); + ->willReturn([self::STUB_CUSTOMER_STORE_ID]); $this->storeManagerMock->expects(clone $expects) ->method('getWebsite') - ->with($customerWebsiteId) + ->with(self::STUB_CUSTOMER_WEBSITE_ID) ->willReturn($websiteMock); - $customerSecureMock = $this->createMock(\Magento\Customer\Model\Data\CustomerSecure::class); + $customerSecureMock = $this->createMock(CustomerSecure::class); $this->customerRegistryMock->expects(clone $expects) ->method('retrieveSecureData') - ->with($customerId) + ->with(self::STUB_CUSTOMER_ID) ->willReturn($customerSecureMock); $this->dataProcessorMock->expects(clone $expects) @@ -205,7 +249,7 @@ public function testCredentialsChanged($testNumber, $oldEmail, $newEmail, $isPas $this->customerViewHelperMock->expects($this->any()) ->method('getCustomerName') ->with($origCustomer) - ->willReturn($customerName); + ->willReturn(self::STUB_CUSTOMER_NAME); $customerSecureMock->expects(clone $expects) ->method('addData') @@ -213,10 +257,12 @@ public function testCredentialsChanged($testNumber, $oldEmail, $newEmail, $isPas ->willReturnSelf(); $customerSecureMock->expects(clone $expects) ->method('setData') - ->with('name', $customerName) + ->with('name', self::STUB_CUSTOMER_NAME) ->willReturnSelf(); - /** @var CustomerInterface|\PHPUnit_Framework_MockObject_MockObject $savedCustomer */ + /** + * @var CustomerInterface|MockObject $savedCustomer + */ $savedCustomer = clone $origCustomer; $origCustomer->expects($this->any()) @@ -230,28 +276,40 @@ public function testCredentialsChanged($testNumber, $oldEmail, $newEmail, $isPas $this->scopeConfigMock->expects($this->any()) ->method('getValue') ->withConsecutive( - [$xmlPathTemplate, \Magento\Store\Model\ScopeInterface::SCOPE_STORE, $customerStoreId], [ - \Magento\Customer\Model\EmailNotification::XML_PATH_FORGOT_EMAIL_IDENTITY, - \Magento\Store\Model\ScopeInterface::SCOPE_STORE, - $customerStoreId + $xmlPathTemplate, + ScopeInterface::SCOPE_STORE, + self::STUB_CUSTOMER_STORE_ID + ], + [ + EmailNotification::XML_PATH_FORGOT_EMAIL_IDENTITY, + ScopeInterface::SCOPE_STORE, + self::STUB_CUSTOMER_STORE_ID ], - [$xmlPathTemplate, \Magento\Store\Model\ScopeInterface::SCOPE_STORE, $customerStoreId], [ - \Magento\Customer\Model\EmailNotification::XML_PATH_FORGOT_EMAIL_IDENTITY, - \Magento\Store\Model\ScopeInterface::SCOPE_STORE, - $customerStoreId + $xmlPathTemplate, + ScopeInterface::SCOPE_STORE, + self::STUB_CUSTOMER_STORE_ID + ], + [ + EmailNotification::XML_PATH_FORGOT_EMAIL_IDENTITY, + ScopeInterface::SCOPE_STORE, + self::STUB_CUSTOMER_STORE_ID ] - ) - ->willReturnOnConsecutiveCalls($templateIdentifier, $sender, $templateIdentifier, $sender); + )->willReturnOnConsecutiveCalls( + self::STUB_EMAIL_IDENTIFIER, + self::STUB_SENDER, + self::STUB_EMAIL_IDENTIFIER, + self::STUB_SENDER + ); $this->transportBuilderMock->expects(clone $expects) ->method('setTemplateIdentifier') - ->with($templateIdentifier) + ->with(self::STUB_EMAIL_IDENTIFIER) ->willReturnSelf(); $this->transportBuilderMock->expects(clone $expects) ->method('setTemplateOptions') - ->with(['area' => \Magento\Framework\App\Area::AREA_FRONTEND, 'store' => $customerStoreId]) + ->with(['area' => Area::AREA_FRONTEND, 'store' => self::STUB_CUSTOMER_STORE_ID]) ->willReturnSelf(); $this->transportBuilderMock->expects(clone $expects) ->method('setTemplateVars') @@ -264,7 +322,7 @@ public function testCredentialsChanged($testNumber, $oldEmail, $newEmail, $isPas $this->transportBuilderMock->expects(clone $expects) ->method('addTo') - ->withConsecutive([$oldEmail, $customerName], [$newEmail, $customerName]) + ->withConsecutive([$oldEmail, self::STUB_CUSTOMER_NAME], [$newEmail, self::STUB_CUSTOMER_NAME]) ->willReturnSelf(); $transport = $this->createMock(\Magento\Framework\Mail\TransportInterface::class); @@ -280,9 +338,12 @@ public function testCredentialsChanged($testNumber, $oldEmail, $newEmail, $isPas } /** + * Provides Emails Data Provider + * + * @param void * @return array */ - public function sendNotificationEmailsDataProvider() + public function sendNotificationEmailsDataProvider(): array { return [ [ @@ -307,63 +368,60 @@ public function sendNotificationEmailsDataProvider() } /** + * Test Password Reminder Email Notify + * * @SuppressWarnings(PHPMD.ExcessiveMethodLength) */ - public function testPasswordReminder() + public function testPasswordReminder(): void { - $customerId = 1; - $customerWebsiteId = 1; - $customerStoreId = 2; - $customerEmail = 'email@email.com'; $customerData = ['key' => 'value']; - $customerName = 'Customer Name'; - $templateIdentifier = 'Template Identifier'; - $sender = 'Sender'; - $senderValues = ['name' => $sender, 'email' => $sender]; + $senderValues = ['name' => self::STUB_SENDER, 'email' => self::STUB_SENDER]; $storeIds = [1, 2]; $this->senderResolverMock ->expects($this->once()) ->method('resolve') - ->with($sender, $customerStoreId) + ->with(self::STUB_SENDER, self::STUB_CUSTOMER_STORE_ID) ->willReturn($senderValues); - /** @var CustomerInterface|\PHPUnit_Framework_MockObject_MockObject $customer */ + /** + * @var CustomerInterface|MockObject $customer + */ $customer = $this->createMock(CustomerInterface::class); $customer->expects($this->any()) ->method('getWebsiteId') - ->willReturn($customerWebsiteId); + ->willReturn(self::STUB_CUSTOMER_WEBSITE_ID); $customer->expects($this->any()) ->method('getStoreId') - ->willReturn($customerStoreId); + ->willReturn(self::STUB_CUSTOMER_STORE_ID); $customer->expects($this->any()) ->method('getId') - ->willReturn($customerId); + ->willReturn(self::STUB_CUSTOMER_ID); $customer->expects($this->any()) ->method('getEmail') - ->willReturn($customerEmail); + ->willReturn(self::STUB_CUSTOMER_EMAIL); $this->storeMock->expects($this->any()) ->method('getId') - ->willReturn($customerStoreId); + ->willReturn(self::STUB_CUSTOMER_STORE_ID); $this->storeManagerMock->expects($this->at(0)) ->method('getStore') ->willReturn($this->storeMock); - $websiteMock = $this->createPartialMock(\Magento\Store\Model\Website::class, ['getStoreIds']); + $websiteMock = $this->createPartialMock(Website::class, ['getStoreIds']); $websiteMock->expects($this->any()) ->method('getStoreIds') ->willReturn($storeIds); $this->storeManagerMock->expects($this->any()) ->method('getWebsite') - ->with($customerWebsiteId) + ->with(self::STUB_CUSTOMER_WEBSITE_ID) ->willReturn($websiteMock); $this->customerRegistryMock->expects($this->once()) ->method('retrieveSecureData') - ->with($customerId) + ->with(self::STUB_CUSTOMER_ID) ->willReturn($this->customerSecureMock); $this->dataProcessorMock->expects($this->once()) @@ -374,7 +432,7 @@ public function testPasswordReminder() $this->customerViewHelperMock->expects($this->any()) ->method('getCustomerName') ->with($customer) - ->willReturn($customerName); + ->willReturn(self::STUB_CUSTOMER_NAME); $this->customerSecureMock->expects($this->once()) ->method('addData') @@ -382,24 +440,30 @@ public function testPasswordReminder() ->willReturnSelf(); $this->customerSecureMock->expects($this->once()) ->method('setData') - ->with('name', $customerName) + ->with('name', self::STUB_CUSTOMER_NAME) ->willReturnSelf(); $this->scopeConfigMock->expects($this->at(0)) ->method('getValue') - ->with(EmailNotification::XML_PATH_REMIND_EMAIL_TEMPLATE, ScopeInterface::SCOPE_STORE, $customerStoreId) - ->willReturn($templateIdentifier); + ->with( + EmailNotification::XML_PATH_REMIND_EMAIL_TEMPLATE, + ScopeInterface::SCOPE_STORE, + self::STUB_CUSTOMER_STORE_ID + )->willReturn(self::STUB_EMAIL_IDENTIFIER); $this->scopeConfigMock->expects($this->at(1)) ->method('getValue') - ->with(EmailNotification::XML_PATH_FORGOT_EMAIL_IDENTITY, ScopeInterface::SCOPE_STORE, $customerStoreId) - ->willReturn($sender); + ->with( + EmailNotification::XML_PATH_FORGOT_EMAIL_IDENTITY, + ScopeInterface::SCOPE_STORE, + self::STUB_CUSTOMER_STORE_ID + )->willReturn(self::STUB_SENDER); $this->mockDefaultTransportBuilder( - $templateIdentifier, - $customerStoreId, + self::STUB_EMAIL_IDENTIFIER, + self::STUB_CUSTOMER_STORE_ID, $senderValues, - $customerEmail, - $customerName, + self::STUB_CUSTOMER_EMAIL, + self::STUB_CUSTOMER_NAME, ['customer' => $this->customerSecureMock, 'store' => $this->storeMock] ); @@ -407,40 +471,38 @@ public function testPasswordReminder() } /** + * Test password reminder customer withouer store id info + * * @SuppressWarnings(PHPMD.ExcessiveMethodLength) */ - public function testPasswordReminderCustomerWithoutStoreId() + public function testPasswordReminderCustomerWithoutStoreId(): void { - $customerId = 1; - $customerWebsiteId = 1; $customerStoreId = null; - $customerEmail = 'email@email.com'; $customerData = ['key' => 'value']; - $customerName = 'Customer Name'; - $templateIdentifier = 'Template Identifier'; - $sender = 'Sender'; - $senderValues = ['name' => $sender, 'email' => $sender]; + $senderValues = ['name' => self::STUB_SENDER, 'email' => self::STUB_SENDER]; $storeIds = [1, 2]; $defaultStoreId = reset($storeIds); $this->senderResolverMock ->expects($this->once()) ->method('resolve') - ->with($sender, $defaultStoreId) + ->with(self::STUB_SENDER, $defaultStoreId) ->willReturn($senderValues); - /** @var CustomerInterface | \PHPUnit_Framework_MockObject_MockObject $customer */ + /** + * @var CustomerInterface|MockObject $customer + */ $customer = $this->createMock(CustomerInterface::class); $customer->expects($this->any()) ->method('getWebsiteId') - ->willReturn($customerWebsiteId); + ->willReturn(self::STUB_CUSTOMER_WEBSITE_ID); $customer->expects($this->any()) ->method('getStoreId') ->willReturn($customerStoreId); $customer->expects($this->any()) ->method('getId') - ->willReturn($customerId); + ->willReturn(self::STUB_CUSTOMER_ID); $customer->expects($this->any()) ->method('getEmail') - ->willReturn($customerEmail); + ->willReturn(self::STUB_CUSTOMER_EMAIL); $this->storeMock->expects($this->any()) ->method('getId') ->willReturn($defaultStoreId); @@ -451,18 +513,18 @@ public function testPasswordReminderCustomerWithoutStoreId() ->method('getStore') ->with($defaultStoreId) ->willReturn($this->storeMock); - $websiteMock = $this->createPartialMock(\Magento\Store\Model\Website::class, ['getStoreIds']); + $websiteMock = $this->createPartialMock(Website::class, ['getStoreIds']); $websiteMock->expects($this->any()) ->method('getStoreIds') ->willReturn($storeIds); $this->storeManagerMock->expects($this->any()) ->method('getWebsite') - ->with($customerWebsiteId) + ->with(self::STUB_CUSTOMER_WEBSITE_ID) ->willReturn($websiteMock); $this->customerRegistryMock->expects($this->once()) ->method('retrieveSecureData') - ->with($customerId) + ->with(self::STUB_CUSTOMER_ID) ->willReturn($this->customerSecureMock); $this->dataProcessorMock->expects($this->once()) ->method('buildOutputDataArray') @@ -471,69 +533,73 @@ public function testPasswordReminderCustomerWithoutStoreId() $this->customerViewHelperMock->expects($this->any()) ->method('getCustomerName') ->with($customer) - ->willReturn($customerName); + ->willReturn(self::STUB_CUSTOMER_NAME); $this->customerSecureMock->expects($this->once()) ->method('addData') ->with($customerData) ->willReturnSelf(); $this->customerSecureMock->expects($this->once()) ->method('setData') - ->with('name', $customerName) + ->with('name', self::STUB_CUSTOMER_NAME) ->willReturnSelf(); $this->scopeConfigMock->expects($this->at(0)) ->method('getValue') - ->with(EmailNotification::XML_PATH_REMIND_EMAIL_TEMPLATE, ScopeInterface::SCOPE_STORE, $defaultStoreId) - ->willReturn($templateIdentifier); + ->with( + EmailNotification::XML_PATH_REMIND_EMAIL_TEMPLATE, + ScopeInterface::SCOPE_STORE, + $defaultStoreId + )->willReturn(self::STUB_EMAIL_IDENTIFIER); $this->scopeConfigMock->expects($this->at(1)) ->method('getValue') - ->with(EmailNotification::XML_PATH_FORGOT_EMAIL_IDENTITY, ScopeInterface::SCOPE_STORE, $defaultStoreId) - ->willReturn($sender); + ->with( + EmailNotification::XML_PATH_FORGOT_EMAIL_IDENTITY, + ScopeInterface::SCOPE_STORE, + $defaultStoreId + )->willReturn(self::STUB_SENDER); $this->mockDefaultTransportBuilder( - $templateIdentifier, + self::STUB_EMAIL_IDENTIFIER, $defaultStoreId, $senderValues, - $customerEmail, - $customerName, + self::STUB_CUSTOMER_EMAIL, + self::STUB_CUSTOMER_NAME, ['customer' => $this->customerSecureMock, 'store' => $this->storeMock] ); $this->model->passwordReminder($customer); } /** + * Test email notify for password reset confirm + * * @SuppressWarnings(PHPMD.ExcessiveMethodLength) */ - public function testPasswordResetConfirmation() + public function testPasswordResetConfirmation(): void { - $customerId = 1; - $customerStoreId = 2; - $customerEmail = 'email@email.com'; $customerData = ['key' => 'value']; - $customerName = 'Customer Name'; - $templateIdentifier = 'Template Identifier'; - $sender = 'Sender'; - $senderValues = ['name' => $sender, 'email' => $sender]; + $senderValues = ['name' => self::STUB_SENDER, 'email' => self::STUB_SENDER]; $this->senderResolverMock ->expects($this->once()) ->method('resolve') - ->with($sender, $customerStoreId) + ->with(self::STUB_SENDER, self::STUB_CUSTOMER_STORE_ID) ->willReturn($senderValues); - /** @var CustomerInterface|\PHPUnit_Framework_MockObject_MockObject $customer */ + /** + * @var CustomerInterface|MockObject $customer + */ $customer = $this->createMock(CustomerInterface::class); $customer->expects($this->once()) ->method('getStoreId') - ->willReturn($customerStoreId); + ->willReturn(self::STUB_CUSTOMER_STORE_ID); $customer->expects($this->any()) ->method('getId') - ->willReturn($customerId); + ->willReturn(self::STUB_CUSTOMER_ID); $customer->expects($this->any()) ->method('getEmail') - ->willReturn($customerEmail); + ->willReturn(self::STUB_CUSTOMER_EMAIL); $this->storeMock->expects($this->any()) ->method('getId') - ->willReturn($customerStoreId); + ->willReturn(self::STUB_CUSTOMER_STORE_ID); $this->storeManagerMock->expects($this->at(0)) ->method('getStore') @@ -541,7 +607,7 @@ public function testPasswordResetConfirmation() $this->customerRegistryMock->expects($this->once()) ->method('retrieveSecureData') - ->with($customerId) + ->with(self::STUB_CUSTOMER_ID) ->willReturn($this->customerSecureMock); $this->dataProcessorMock->expects($this->once()) @@ -552,7 +618,7 @@ public function testPasswordResetConfirmation() $this->customerViewHelperMock->expects($this->any()) ->method('getCustomerName') ->with($customer) - ->willReturn($customerName); + ->willReturn(self::STUB_CUSTOMER_NAME); $this->customerSecureMock->expects($this->once()) ->method('addData') @@ -560,24 +626,30 @@ public function testPasswordResetConfirmation() ->willReturnSelf(); $this->customerSecureMock->expects($this->once()) ->method('setData') - ->with('name', $customerName) + ->with('name', self::STUB_CUSTOMER_NAME) ->willReturnSelf(); $this->scopeConfigMock->expects($this->at(0)) ->method('getValue') - ->with(EmailNotification::XML_PATH_FORGOT_EMAIL_TEMPLATE, ScopeInterface::SCOPE_STORE, $customerStoreId) - ->willReturn($templateIdentifier); + ->with( + EmailNotification::XML_PATH_FORGOT_EMAIL_TEMPLATE, + ScopeInterface::SCOPE_STORE, + self::STUB_CUSTOMER_STORE_ID + )->willReturn(self::STUB_EMAIL_IDENTIFIER); $this->scopeConfigMock->expects($this->at(1)) ->method('getValue') - ->with(EmailNotification::XML_PATH_FORGOT_EMAIL_IDENTITY, ScopeInterface::SCOPE_STORE, $customerStoreId) - ->willReturn($sender); + ->with( + EmailNotification::XML_PATH_FORGOT_EMAIL_IDENTITY, + ScopeInterface::SCOPE_STORE, + self::STUB_CUSTOMER_STORE_ID + )->willReturn(self::STUB_SENDER); $this->mockDefaultTransportBuilder( - $templateIdentifier, - $customerStoreId, + self::STUB_EMAIL_IDENTIFIER, + self::STUB_CUSTOMER_STORE_ID, $senderValues, - $customerEmail, - $customerName, + self::STUB_CUSTOMER_EMAIL, + self::STUB_CUSTOMER_NAME, ['customer' => $this->customerSecureMock, 'store' => $this->storeMock] ); @@ -585,49 +657,48 @@ public function testPasswordResetConfirmation() } /** + * Test email notify with new account + * + * @param void * @SuppressWarnings(PHPMD.ExcessiveMethodLength) */ - public function testNewAccount() + public function testNewAccount(): void { - $customerId = 1; - $customerStoreId = 2; - $customerEmail = 'email@email.com'; $customerData = ['key' => 'value']; - $customerName = 'Customer Name'; - $templateIdentifier = 'Template Identifier'; - $sender = 'Sender'; - $senderValues = ['name' => $sender, 'email' => $sender]; + $senderValues = ['name' => self::STUB_SENDER, 'email' => self::STUB_SENDER]; $this->senderResolverMock ->expects($this->once()) ->method('resolve') - ->with($sender, $customerStoreId) + ->with(self::STUB_SENDER, self::STUB_CUSTOMER_STORE_ID) ->willReturn($senderValues); - /** @var CustomerInterface|\PHPUnit_Framework_MockObject_MockObject $customer */ + /** + * @var CustomerInterface|MockObject $customer + */ $customer = $this->createMock(CustomerInterface::class); $customer->expects($this->any()) ->method('getStoreId') - ->willReturn($customerStoreId); + ->willReturn(self::STUB_CUSTOMER_STORE_ID); $customer->expects($this->any()) ->method('getId') - ->willReturn($customerId); + ->willReturn(self::STUB_CUSTOMER_ID); $customer->expects($this->any()) ->method('getEmail') - ->willReturn($customerEmail); + ->willReturn(self::STUB_CUSTOMER_EMAIL); $this->storeMock->expects($this->any()) ->method('getId') - ->willReturn($customerStoreId); + ->willReturn(self::STUB_CUSTOMER_STORE_ID); $this->storeManagerMock->expects($this->once()) ->method('getStore') - ->with($customerStoreId) + ->with(self::STUB_CUSTOMER_STORE_ID) ->willReturn($this->storeMock); $this->customerRegistryMock->expects($this->once()) ->method('retrieveSecureData') - ->with($customerId) + ->with(self::STUB_CUSTOMER_ID) ->willReturn($this->customerSecureMock); $this->dataProcessorMock->expects($this->once()) @@ -638,7 +709,7 @@ public function testNewAccount() $this->customerViewHelperMock->expects($this->any()) ->method('getCustomerName') ->with($customer) - ->willReturn($customerName); + ->willReturn(self::STUB_CUSTOMER_NAME); $this->customerSecureMock->expects($this->once()) ->method('addData') @@ -646,28 +717,39 @@ public function testNewAccount() ->willReturnSelf(); $this->customerSecureMock->expects($this->once()) ->method('setData') - ->with('name', $customerName) + ->with('name', self::STUB_CUSTOMER_NAME) ->willReturnSelf(); $this->scopeConfigMock->expects($this->at(0)) ->method('getValue') - ->with(EmailNotification::XML_PATH_REGISTER_EMAIL_TEMPLATE, ScopeInterface::SCOPE_STORE, $customerStoreId) - ->willReturn($templateIdentifier); + ->with( + EmailNotification::XML_PATH_REGISTER_EMAIL_TEMPLATE, + ScopeInterface::SCOPE_STORE, + self::STUB_CUSTOMER_STORE_ID + )->willReturn(self::STUB_EMAIL_IDENTIFIER); $this->scopeConfigMock->expects($this->at(1)) ->method('getValue') - ->with(EmailNotification::XML_PATH_REGISTER_EMAIL_IDENTITY, ScopeInterface::SCOPE_STORE, $customerStoreId) - ->willReturn($sender); + ->with( + EmailNotification::XML_PATH_REGISTER_EMAIL_IDENTITY, + ScopeInterface::SCOPE_STORE, + self::STUB_CUSTOMER_STORE_ID + )->willReturn(self::STUB_SENDER); $this->mockDefaultTransportBuilder( - $templateIdentifier, - $customerStoreId, + self::STUB_EMAIL_IDENTIFIER, + self::STUB_CUSTOMER_STORE_ID, $senderValues, - $customerEmail, - $customerName, + self::STUB_CUSTOMER_EMAIL, + self::STUB_CUSTOMER_NAME, ['customer' => $this->customerSecureMock, 'back_url' => '', 'store' => $this->storeMock] ); - $this->model->newAccount($customer, EmailNotification::NEW_ACCOUNT_EMAIL_REGISTERED, '', $customerStoreId); + $this->model->newAccount( + $customer, + EmailNotification::NEW_ACCOUNT_EMAIL_REGISTERED, + '', + self::STUB_CUSTOMER_STORE_ID + ); } /** diff --git a/app/code/Magento/Customer/Test/Unit/Model/Layout/DepersonalizePluginTest.php b/app/code/Magento/Customer/Test/Unit/Model/Layout/DepersonalizePluginTest.php index 15337d8968305..095a83101322f 100644 --- a/app/code/Magento/Customer/Test/Unit/Model/Layout/DepersonalizePluginTest.php +++ b/app/code/Magento/Customer/Test/Unit/Model/Layout/DepersonalizePluginTest.php @@ -3,143 +3,177 @@ * Copyright © Magento, Inc. All rights reserved. * See COPYING.txt for license details. */ +declare(strict_types=1); + namespace Magento\Customer\Test\Unit\Model\Layout; +use Magento\Customer\Model\Customer; +use Magento\Customer\Model\CustomerFactory; +use Magento\Customer\Model\Layout\DepersonalizePlugin; +use Magento\Customer\Model\Session as CustomerSession; +use Magento\Customer\Model\Visitor as VisitorModel; +use Magento\Framework\Data\Form\FormKey; +use Magento\Framework\Session\Generic as GenericSession; +use Magento\Framework\TestFramework\Unit\Helper\ObjectManager as ObjectManagerHelper; +use Magento\Framework\View\LayoutInterface; +use Magento\PageCache\Model\DepersonalizeChecker; +use PHPUnit\Framework\MockObject\MockObject; +use PHPUnit\Framework\TestCase; + /** - * Class DepersonalizePluginTest + * Unit tests for \Magento\Customer\Model\Layout\DepersonalizePlugin class. * * @SuppressWarnings(PHPMD.CouplingBetweenObjects) */ -class DepersonalizePluginTest extends \PHPUnit\Framework\TestCase +class DepersonalizePluginTest extends TestCase { /** - * @var \Magento\Customer\Model\Layout\DepersonalizePlugin + * @var DepersonalizePlugin */ - protected $plugin; + private $plugin; /** - * @var \Magento\Framework\View\Layout|\PHPUnit_Framework_MockObject_MockObject + * @var LayoutInterface|MockObject */ - protected $layoutMock; + private $layoutMock; /** - * @var \Magento\Framework\Session\Generic|\PHPUnit_Framework_MockObject_MockObject + * @var GenericSession|MockObject */ - protected $sessionMock; + private $sessionMock; /** - * @var \Magento\Customer\Model\Session|\PHPUnit_Framework_MockObject_MockObject + * @var CustomerSession|MockObject */ - protected $customerSessionMock; + private $customerSessionMock; /** - * @var \Magento\Customer\Model\CustomerFactory|\PHPUnit_Framework_MockObject_MockObject + * @var CustomerFactory|MockObject */ - protected $customerFactoryMock; + private $customerFactoryMock; /** - * @var \Magento\Customer\Model\Customer|\PHPUnit_Framework_MockObject_MockObject + * @var Customer|MockObject */ - protected $customerMock; + private $customerMock; /** - * @var \Magento\Customer\Model\Visitor|\PHPUnit_Framework_MockObject_MockObject + * @var VisitorModel|MockObject */ - protected $visitorMock; + private $visitorMock; /** - * @var \Magento\PageCache\Model\DepersonalizeChecker|\PHPUnit_Framework_MockObject_MockObject + * @var DepersonalizeChecker|MockObject */ - protected $depersonalizeCheckerMock; + private $depersonalizeCheckerMock; /** - * SetUp + * @inheritdoc */ protected function setUp() { - $this->layoutMock = $this->createMock(\Magento\Framework\View\Layout::class); + $this->layoutMock = $this->getMockForAbstractClass(LayoutInterface::class); $this->sessionMock = $this->createPartialMock( - \Magento\Framework\Session\Generic::class, + GenericSession::class, ['clearStorage', 'setData', 'getData'] ); $this->customerSessionMock = $this->createPartialMock( - \Magento\Customer\Model\Session::class, + CustomerSession::class, ['getCustomerGroupId', 'setCustomerGroupId', 'clearStorage', 'setCustomer'] ); - $this->customerFactoryMock = $this->createPartialMock( - \Magento\Customer\Model\CustomerFactory::class, - ['create'] - ); + $this->customerFactoryMock = $this->createPartialMock(CustomerFactory::class, ['create']); $this->customerMock = $this->createPartialMock( - \Magento\Customer\Model\Customer::class, + Customer::class, ['setGroupId', '__wakeup'] ); - $this->visitorMock = $this->createMock(\Magento\Customer\Model\Visitor::class); + $this->visitorMock = $this->createMock(VisitorModel::class); $this->customerFactoryMock->expects($this->any()) ->method('create') - ->will($this->returnValue($this->customerMock)); - $this->depersonalizeCheckerMock = $this->createMock(\Magento\PageCache\Model\DepersonalizeChecker::class); - - $this->plugin = new \Magento\Customer\Model\Layout\DepersonalizePlugin( - $this->depersonalizeCheckerMock, - $this->sessionMock, - $this->customerSessionMock, - $this->customerFactoryMock, - $this->visitorMock + ->willReturn($this->customerMock); + $this->depersonalizeCheckerMock = $this->createMock(DepersonalizeChecker::class); + + $this->plugin = (new ObjectManagerHelper($this))->getObject( + DepersonalizePlugin::class, + [ + 'depersonalizeChecker' => $this->depersonalizeCheckerMock, + 'session' => $this->sessionMock, + 'customerSession' => $this->customerSessionMock, + 'customerFactory' => $this->customerFactoryMock, + 'visitor' => $this->visitorMock, + ] ); } - public function testBeforeGenerateXml() + /** + * Test beforeGenerateXml method when depersonalization is needed. + * + * @return void + */ + public function testBeforeGenerateXml(): void { $this->depersonalizeCheckerMock->expects($this->once())->method('checkIfDepersonalize')->willReturn(true); $this->customerSessionMock->expects($this->once())->method('getCustomerGroupId'); $this->sessionMock ->expects($this->once()) ->method('getData') - ->with($this->equalTo(\Magento\Framework\Data\Form\FormKey::FORM_KEY)); - $output = $this->plugin->beforeGenerateXml($this->layoutMock); - $this->assertEquals([], $output); + ->with($this->equalTo(FormKey::FORM_KEY)); + $this->plugin->beforeGenerateXml($this->layoutMock); } - public function testBeforeGenerateXmlNoDepersonalize() + /** + * Test beforeGenerateXml method when depersonalization is not needed. + * + * @return void + */ + public function testBeforeGenerateXmlNoDepersonalize(): void { $this->depersonalizeCheckerMock->expects($this->once())->method('checkIfDepersonalize')->willReturn(false); $this->customerSessionMock->expects($this->never())->method('getCustomerGroupId'); $this->sessionMock ->expects($this->never()) ->method('getData'); - $output = $this->plugin->beforeGenerateXml($this->layoutMock); - $this->assertEquals([], $output); + $this->plugin->beforeGenerateXml($this->layoutMock); } - public function testAfterGenerateXml() + /** + * Test afterGenerateElements method when depersonalization is needed. + * + * @return void + */ + public function testAfterGenerateElements(): void { - $expectedResult = $this->createMock(\Magento\Framework\View\Layout::class); $this->depersonalizeCheckerMock->expects($this->once())->method('checkIfDepersonalize')->willReturn(true); $this->visitorMock->expects($this->once())->method('setSkipRequestLogging')->with($this->equalTo(true)); $this->visitorMock->expects($this->once())->method('unsetData'); $this->sessionMock->expects($this->once())->method('clearStorage'); $this->customerSessionMock->expects($this->once())->method('clearStorage'); $this->customerSessionMock->expects($this->once())->method('setCustomerGroupId')->with($this->equalTo(null)); - $this->customerMock->expects($this->once())->method('setGroupId')->with($this->equalTo(null))->willReturnSelf(); + $this->customerMock + ->expects($this->once()) + ->method('setGroupId') + ->with($this->equalTo(null)) + ->willReturnSelf(); $this->sessionMock ->expects($this->once()) ->method('setData') ->with( - $this->equalTo(\Magento\Framework\Data\Form\FormKey::FORM_KEY), + $this->equalTo(FormKey::FORM_KEY), $this->equalTo(null) ); $this->customerSessionMock ->expects($this->once()) ->method('setCustomer') ->with($this->equalTo($this->customerMock)); - $actualResult = $this->plugin->afterGenerateXml($this->layoutMock, $expectedResult); - $this->assertSame($expectedResult, $actualResult); + $this->assertEmpty($this->plugin->afterGenerateElements($this->layoutMock)); } - public function testAfterGenerateXmlNoDepersonalize() + /** + * Test afterGenerateElements method when depersonalization is not needed. + * + * @return void + */ + public function testAfterGenerateElementsNoDepersonalize(): void { - $expectedResult = $this->createMock(\Magento\Framework\View\Layout::class); $this->depersonalizeCheckerMock->expects($this->once())->method('checkIfDepersonalize')->willReturn(false); $this->visitorMock->expects($this->never())->method('setSkipRequestLogging'); $this->visitorMock->expects($this->never())->method('unsetData'); @@ -149,7 +183,6 @@ public function testAfterGenerateXmlNoDepersonalize() $this->customerMock->expects($this->never())->method('setGroupId'); $this->sessionMock->expects($this->never())->method('setData'); $this->customerSessionMock->expects($this->never())->method('setCustomer'); - $actualResult = $this->plugin->afterGenerateXml($this->layoutMock, $expectedResult); - $this->assertSame($expectedResult, $actualResult); + $this->assertEmpty($this->plugin->afterGenerateElements($this->layoutMock)); } } diff --git a/app/code/Magento/Customer/Test/Unit/Model/Plugin/CustomerNotificationTest.php b/app/code/Magento/Customer/Test/Unit/Model/Plugin/CustomerNotificationTest.php index c3c853bca1469..74a5a002c7845 100644 --- a/app/code/Magento/Customer/Test/Unit/Model/Plugin/CustomerNotificationTest.php +++ b/app/code/Magento/Customer/Test/Unit/Model/Plugin/CustomerNotificationTest.php @@ -3,79 +3,87 @@ * Copyright © Magento, Inc. All rights reserved. * See COPYING.txt for license details. */ + namespace Magento\Customer\Test\Unit\Model\Plugin; -use Magento\Backend\App\AbstractAction; use Magento\Customer\Api\CustomerRepositoryInterface; use Magento\Customer\Api\Data\CustomerInterface; use Magento\Customer\Model\Customer\NotificationStorage; use Magento\Customer\Model\Plugin\CustomerNotification; use Magento\Customer\Model\Session; +use Magento\Framework\App\ActionInterface; use Magento\Framework\App\Area; +use Magento\Framework\App\HttpRequestInterface; use Magento\Framework\App\RequestInterface; use Magento\Framework\App\State; use Magento\Framework\Exception\NoSuchEntityException; +use PHPUnit\Framework\MockObject\MockObject; use Psr\Log\LoggerInterface; class CustomerNotificationTest extends \PHPUnit\Framework\TestCase { - /** @var Session|\PHPUnit_Framework_MockObject_MockObject */ + private const STUB_CUSTOMER_ID = 1; + + /** + * @var Session|MockObject + */ private $sessionMock; - /** @var NotificationStorage|\PHPUnit_Framework_MockObject_MockObject */ + /** + * @var NotificationStorage|MockObject + */ private $notificationStorageMock; - /** @var CustomerRepositoryInterface|\PHPUnit_Framework_MockObject_MockObject */ + /** + * @var CustomerRepositoryInterface|MockObject + */ private $customerRepositoryMock; - /** @var State|\PHPUnit_Framework_MockObject_MockObject */ + /** + * @var State|MockObject + */ private $appStateMock; - /** @var RequestInterface|\PHPUnit_Framework_MockObject_MockObject */ + /** + * @var RequestInterface|MockObject + */ private $requestMock; - /** @var AbstractAction|\PHPUnit_Framework_MockObject_MockObject */ - private $abstractActionMock; + /** + * @var ActionInterface|MockObject + */ + private $actionMock; - /** @var LoggerInterface */ + /** + * @var LoggerInterface|MockObject + */ private $loggerMock; - /** @var CustomerNotification */ + /** + * @var CustomerNotification + */ private $plugin; - /** @var int */ - private static $customerId = 1; - protected function setUp() { - $this->sessionMock = $this->getMockBuilder(Session::class) - ->disableOriginalConstructor() - ->setMethods(['getCustomerId', 'setCustomerData', 'setCustomerGroupId', 'regenerateId']) - ->getMock(); - $this->notificationStorageMock = $this->getMockBuilder(NotificationStorage::class) - ->disableOriginalConstructor() - ->setMethods(['isExists', 'remove']) - ->getMock(); - $this->customerRepositoryMock = $this->getMockBuilder(CustomerRepositoryInterface::class) - ->getMockForAbstractClass(); - $this->abstractActionMock = $this->getMockBuilder(AbstractAction::class) - ->disableOriginalConstructor() - ->getMockForAbstractClass(); - $this->requestMock = $this->getMockBuilder(RequestInterface::class) - ->setMethods(['isPost']) - ->getMockForAbstractClass(); - $this->appStateMock = $this->getMockBuilder(State::class) - ->disableOriginalConstructor() - ->setMethods(['getAreaCode']) + $this->sessionMock = $this->createMock(Session::class); + $this->sessionMock->method('getCustomerId')->willReturn(self::STUB_CUSTOMER_ID); + + $this->customerRepositoryMock = $this->createMock(CustomerRepositoryInterface::class); + $this->actionMock = $this->createMock(ActionInterface::class); + $this->requestMock = $this->getMockBuilder([RequestInterface::class, HttpRequestInterface::class]) ->getMock(); + $this->requestMock->method('isPost')->willReturn(true); + + $this->loggerMock = $this->createMock(LoggerInterface::class); - $this->loggerMock = $this->getMockForAbstractClass(LoggerInterface::class); + $this->appStateMock = $this->createMock(State::class); $this->appStateMock->method('getAreaCode')->willReturn(Area::AREA_FRONTEND); - $this->requestMock->method('isPost')->willReturn(true); - $this->sessionMock->method('getCustomerId')->willReturn(self::$customerId); + + $this->notificationStorageMock = $this->createMock(NotificationStorage::class); $this->notificationStorageMock->expects($this->any()) ->method('isExists') - ->with(NotificationStorage::UPDATE_CUSTOMER_SESSION, self::$customerId) + ->with(NotificationStorage::UPDATE_CUSTOMER_SESSION, self::STUB_CUSTOMER_ID) ->willReturn(true); $this->plugin = new CustomerNotification( @@ -83,41 +91,42 @@ protected function setUp() $this->notificationStorageMock, $this->appStateMock, $this->customerRepositoryMock, - $this->loggerMock + $this->loggerMock, + $this->requestMock ); } - public function testBeforeDispatch() + public function testBeforeExecute() { - $customerGroupId =1; + $customerGroupId = 1; - $customerMock = $this->getMockForAbstractClass(CustomerInterface::class); + $customerMock = $this->createMock(CustomerInterface::class); $customerMock->method('getGroupId')->willReturn($customerGroupId); - $customerMock->method('getId')->willReturn(self::$customerId); + $customerMock->method('getId')->willReturn(self::STUB_CUSTOMER_ID); $this->customerRepositoryMock->expects($this->once()) ->method('getById') - ->with(self::$customerId) + ->with(self::STUB_CUSTOMER_ID) ->willReturn($customerMock); $this->notificationStorageMock->expects($this->once()) ->method('remove') - ->with(NotificationStorage::UPDATE_CUSTOMER_SESSION, self::$customerId); + ->with(NotificationStorage::UPDATE_CUSTOMER_SESSION, self::STUB_CUSTOMER_ID); $this->sessionMock->expects($this->once())->method('setCustomerData')->with($customerMock); $this->sessionMock->expects($this->once())->method('setCustomerGroupId')->with($customerGroupId); $this->sessionMock->expects($this->once())->method('regenerateId'); - $this->plugin->beforeDispatch($this->abstractActionMock, $this->requestMock); + $this->plugin->beforeExecute($this->actionMock); } public function testBeforeDispatchWithNoCustomerFound() { $this->customerRepositoryMock->method('getById') - ->with(self::$customerId) + ->with(self::STUB_CUSTOMER_ID) ->willThrowException(new NoSuchEntityException()); $this->loggerMock->expects($this->once()) ->method('error'); - $this->plugin->beforeDispatch($this->abstractActionMock, $this->requestMock); + $this->plugin->beforeExecute($this->actionMock); } } diff --git a/app/code/Magento/Customer/Test/Unit/Model/ResourceModel/CustomerRepositoryTest.php b/app/code/Magento/Customer/Test/Unit/Model/ResourceModel/CustomerRepositoryTest.php index 8032399e14881..015213847e7ee 100644 --- a/app/code/Magento/Customer/Test/Unit/Model/ResourceModel/CustomerRepositoryTest.php +++ b/app/code/Magento/Customer/Test/Unit/Model/ResourceModel/CustomerRepositoryTest.php @@ -211,6 +211,7 @@ public function testSave() 'setFirstFailure', 'setLockExpires', 'save', + 'setGroupId' ] ); @@ -245,9 +246,15 @@ public function testSave() $this->customer->expects($this->atLeastOnce()) ->method('getId') ->willReturn($customerId); - $this->customer->expects($this->atLeastOnce()) + $this->customer->expects($this->at(4)) ->method('__toArray') ->willReturn([]); + $this->customer->expects($this->at(3)) + ->method('__toArray') + ->willReturn(['group_id' => 1]); + $customerModel->expects($this->once()) + ->method('setGroupId') + ->with(1); $this->customerRegistry->expects($this->atLeastOnce()) ->method('retrieve') ->with($customerId) diff --git a/app/code/Magento/Customer/Test/Unit/Model/SessionTest.php b/app/code/Magento/Customer/Test/Unit/Model/SessionTest.php index 8565790990df1..01bccf3bd6c6c 100644 --- a/app/code/Magento/Customer/Test/Unit/Model/SessionTest.php +++ b/app/code/Magento/Customer/Test/Unit/Model/SessionTest.php @@ -7,48 +7,69 @@ */ namespace Magento\Customer\Test\Unit\Model; +use Magento\Customer\Api\CustomerRepositoryInterface; +use Magento\Customer\Api\Data\CustomerInterface; +use Magento\Customer\Model\Customer; +use Magento\Customer\Model\CustomerFactory; +use Magento\Customer\Model\ResourceModel\Customer as ResourceCustomer; +use Magento\Customer\Model\Session; +use Magento\Customer\Model\Session\Storage; +use Magento\Framework\App\Http\Context; +use Magento\Framework\App\Response\Http; +use Magento\Framework\Event\ManagerInterface; +use Magento\Framework\TestFramework\Unit\Helper\ObjectManager as ObjectManagerHelper; +use Magento\Framework\Url; +use Magento\Framework\UrlFactory; +use PHPUnit\Framework\MockObject\MockObject; +use PHPUnit\Framework\TestCase; + /** * @SuppressWarnings(PHPMD.CouplingBetweenObjects) */ -class SessionTest extends \PHPUnit\Framework\TestCase +class SessionTest extends TestCase { /** - * @var \PHPUnit_Framework_MockObject_MockObject + * @var ResourceCustomer|MockObject + */ + protected $_customerResourceMock; + + /** + * @var Storage|MockObject */ protected $_storageMock; /** - * @var \PHPUnit_Framework_MockObject_MockObject + * @var ManagerInterface|MockObject */ protected $_eventManagerMock; /** - * @var \PHPUnit_Framework_MockObject_MockObject + * @var Context|MockObject */ protected $_httpContextMock; /** - * @var \Magento\Framework\UrlFactory|\PHPUnit_Framework_MockObject_MockObject + * @var UrlFactory|MockObject */ protected $urlFactoryMock; /** - * @var \Magento\Customer\Model\CustomerFactory|\PHPUnit_Framework_MockObject_MockObject + * @var CustomerFactory|MockObject */ protected $customerFactoryMock; /** - * @var \Magento\Customer\Api\CustomerRepositoryInterface|\PHPUnit_Framework_MockObject_MockObject + * @var CustomerRepositoryInterface|MockObject */ protected $customerRepositoryMock; /** - * @var \Magento\Framework\App\Response\Http|\PHPUnit_Framework_MockObject_MockObject + * @var Http|MockObject */ protected $responseMock; /** - * @var \Magento\Customer\Model\Session + * @var Session */ protected $_model; @@ -58,21 +79,25 @@ class SessionTest extends \PHPUnit\Framework\TestCase protected function setUp() { $this->_storageMock = $this->createPartialMock( - \Magento\Customer\Model\Session\Storage::class, + Storage::class, ['getIsCustomerEmulated', 'getData', 'unsIsCustomerEmulated', '__sleep', '__wakeup'] ); - $this->_eventManagerMock = $this->createMock(\Magento\Framework\Event\ManagerInterface::class); - $this->_httpContextMock = $this->createMock(\Magento\Framework\App\Http\Context::class); - $this->urlFactoryMock = $this->createMock(\Magento\Framework\UrlFactory::class); - $this->customerFactoryMock = $this->getMockBuilder(\Magento\Customer\Model\CustomerFactory::class) + $this->_eventManagerMock = $this->createMock(ManagerInterface::class); + $this->_httpContextMock = $this->createMock(Context::class); + $this->urlFactoryMock = $this->createMock(UrlFactory::class); + $this->customerFactoryMock = $this->getMockBuilder(CustomerFactory::class) ->disableOriginalConstructor() ->setMethods(['create', 'save']) ->getMock(); - $this->customerRepositoryMock = $this->createMock(\Magento\Customer\Api\CustomerRepositoryInterface::class); - $helper = new \Magento\Framework\TestFramework\Unit\Helper\ObjectManager($this); - $this->responseMock = $this->createMock(\Magento\Framework\App\Response\Http::class); + $this->_customerResourceMock = $this->getMockBuilder(ResourceCustomer::class) + ->disableOriginalConstructor() + ->setMethods(['load', 'save']) + ->getMock(); + $this->customerRepositoryMock = $this->createMock(CustomerRepositoryInterface::class); + $helper = new ObjectManagerHelper($this); + $this->responseMock = $this->createMock(Http::class); $this->_model = $helper->getObject( - \Magento\Customer\Model\Session::class, + Session::class, [ 'customerFactory' => $this->customerFactoryMock, 'storage' => $this->_storageMock, @@ -81,6 +106,7 @@ protected function setUp() 'urlFactory' => $this->urlFactoryMock, 'customerRepository' => $this->customerRepositoryMock, 'response' => $this->responseMock, + '_customerResource' => $this->_customerResourceMock, ] ); } @@ -90,8 +116,8 @@ protected function setUp() */ public function testSetCustomerAsLoggedIn() { - $customer = $this->createMock(\Magento\Customer\Model\Customer::class); - $customerDto = $this->createMock(\Magento\Customer\Api\Data\CustomerInterface::class); + $customer = $this->createMock(Customer::class); + $customerDto = $this->createMock(CustomerInterface::class); $customer->expects($this->any()) ->method('getDataModel') ->will($this->returnValue($customerDto)); @@ -113,8 +139,8 @@ public function testSetCustomerAsLoggedIn() */ public function testSetCustomerDataAsLoggedIn() { - $customer = $this->createMock(\Magento\Customer\Model\Customer::class); - $customerDto = $this->createMock(\Magento\Customer\Api\Data\CustomerInterface::class); + $customer = $this->createMock(Customer::class); + $customerDto = $this->createMock(CustomerInterface::class); $this->customerFactoryMock->expects($this->once()) ->method('create') @@ -140,17 +166,17 @@ public function testSetCustomerDataAsLoggedIn() */ public function testAuthenticate() { - $urlMock = $this->createMock(\Magento\Framework\Url::class); + $urlMock = $this->createMock(Url::class); $urlMock->expects($this->exactly(2)) ->method('getUrl') ->willReturn(''); $urlMock->expects($this->once()) ->method('getRebuiltUrl') ->willReturn(''); - $this->urlFactoryMock->expects($this->exactly(4)) + $this->urlFactoryMock->expects($this->exactly(3)) ->method('create') ->willReturn($urlMock); - $urlMock->expects($this->once()) + $urlMock->expects($this->never()) ->method('getUseSession') ->willReturn(false); @@ -185,13 +211,13 @@ public function testLoginById() */ protected function prepareLoginDataMock($customerId) { - $customerDataMock = $this->createMock(\Magento\Customer\Api\Data\CustomerInterface::class); + $customerDataMock = $this->createMock(CustomerInterface::class); $customerDataMock->expects($this->once()) ->method('getId') ->will($this->returnValue($customerId)); $customerMock = $this->createPartialMock( - \Magento\Customer\Model\Customer::class, + Customer::class, ['getId', 'getConfirmation', 'updateData', 'getGroupId'] ); $customerMock->expects($this->exactly(3)) @@ -259,8 +285,60 @@ public function getIsLoggedInDataProvider() */ public function testSetCustomerRemovesFlagThatShowsIfCustomerIsEmulated() { - $customerMock = $this->createMock(\Magento\Customer\Model\Customer::class); + $customerMock = $this->createMock(Customer::class); $this->_storageMock->expects($this->once())->method('unsIsCustomerEmulated'); $this->_model->setCustomer($customerMock); } + + /** + * Test "getCustomer()" for guest user + * + * @return void + */ + public function testGetCustomerForGuestUser() + { + $customerMock = $this->getMockBuilder(Customer::class) + ->disableOriginalConstructor() + ->getMock(); + + $this->customerFactoryMock + ->expects($this->once()) + ->method('create') + ->will($this->returnValue($customerMock)); + + $this->assertSame($customerMock, $this->_model->getCustomer()); + } + + /** + * Test "getCustomer()" for registered user + * + * @return void + */ + public function testGetCustomerForRegisteredUser() + { + $customerId = 1; + + $customerMock = $this->getMockBuilder(Customer::class) + ->disableOriginalConstructor() + ->getMock(); + + $this->customerFactoryMock + ->expects($this->once()) + ->method('create') + ->will($this->returnValue($customerMock)); + + $this->_storageMock + ->expects($this->exactly(4)) + ->method('getData') + ->with('customer_id') + ->willReturn($customerId); + + $this->_customerResourceMock + ->expects($this->once()) + ->method('load') + ->with($customerMock, $customerId) + ->will($this->returnValue($customerMock)); + + $this->assertSame($customerMock, $this->_model->getCustomer()); + } } diff --git a/app/code/Magento/Customer/Test/Unit/Model/VisitorTest.php b/app/code/Magento/Customer/Test/Unit/Model/VisitorTest.php index ec787b9d3c873..c9cd525d71c49 100644 --- a/app/code/Magento/Customer/Test/Unit/Model/VisitorTest.php +++ b/app/code/Magento/Customer/Test/Unit/Model/VisitorTest.php @@ -6,16 +6,24 @@ namespace Magento\Customer\Test\Unit\Model; +use Magento\Customer\Model\ResourceModel\Visitor as VisitorResourceModel; +use Magento\Customer\Model\Session; +use Magento\Customer\Model\Visitor as VisitorModel; +use Magento\Framework\App\Request\Http as HttpRequest; +use Magento\Framework\DataObject; +use Magento\Framework\Registry; +use Magento\Framework\Session\SessionManagerInterface; use Magento\Framework\TestFramework\Unit\Helper\ObjectManager as ObjectManagerHelper; +use PHPUnit\Framework\MockObject\MockObject; +use PHPUnit\Framework\TestCase; /** - * Class VisitorTest - * @package Magento\Customer\Model + * Unit Tests to cover Visitor Model */ -class VisitorTest extends \PHPUnit\Framework\TestCase +class VisitorTest extends TestCase { /** - * @var \Magento\Customer\Model\Visitor + * @var VisitorModel */ protected $visitor; @@ -25,31 +33,37 @@ class VisitorTest extends \PHPUnit\Framework\TestCase protected $objectManagerHelper; /** - * @var \Magento\Framework\Registry|\PHPUnit_Framework_MockObject_MockObject + * @var Registry|MockObject */ - protected $registry; + protected $registryMock; /** - * @var \Magento\Customer\Model\ResourceModel\Visitor|\PHPUnit_Framework_MockObject_MockObject + * @var VisitorResourceModel|MockObject */ - protected $resource; + protected $visitorResourceModelMock; /** - * @var \Magento\Framework\Session\SessionManagerInterface|\PHPUnit_Framework_MockObject_MockObject + * @var SessionManagerInterface|MockObject */ - protected $session; + protected $sessionMock; + + /** + * @var HttpRequest|MockObject + */ + private $httpRequestMock; protected function setUp() { - $this->registry = $this->createMock(\Magento\Framework\Registry::class); - $this->session = $this->getMockBuilder(\Magento\Customer\Model\Session::class) + $this->registryMock = $this->createMock(Registry::class); + $this->sessionMock = $this->getMockBuilder(Session::class) ->disableOriginalConstructor() ->setMethods(['getSessionId', 'getVisitorData', 'setVisitorData']) ->getMock(); + $this->httpRequestMock = $this->createMock(HttpRequest::class); $this->objectManagerHelper = new ObjectManagerHelper($this); - $this->resource = $this->getMockBuilder(\Magento\Customer\Model\ResourceModel\Visitor::class) + $this->visitorResourceModelMock = $this->getMockBuilder(VisitorResourceModel::class) ->setMethods([ 'beginTransaction', '__sleep', @@ -60,27 +74,28 @@ protected function setUp() 'commit', 'clean', ])->disableOriginalConstructor()->getMock(); - $this->resource->expects($this->any())->method('getIdFieldName')->willReturn('visitor_id'); - $this->resource->expects($this->any())->method('addCommitCallback')->willReturnSelf(); + $this->visitorResourceModelMock->expects($this->any())->method('getIdFieldName')->willReturn('visitor_id'); + $this->visitorResourceModelMock->expects($this->any())->method('addCommitCallback')->willReturnSelf(); $arguments = $this->objectManagerHelper->getConstructArguments( - \Magento\Customer\Model\Visitor::class, + VisitorModel::class, [ - 'registry' => $this->registry, - 'session' => $this->session, - 'resource' => $this->resource, + 'registry' => $this->registryMock, + 'session' => $this->sessionMock, + 'resource' => $this->visitorResourceModelMock, + 'request' => $this->httpRequestMock, ] ); - $this->visitor = $this->objectManagerHelper->getObject(\Magento\Customer\Model\Visitor::class, $arguments); + $this->visitor = $this->objectManagerHelper->getObject(VisitorModel::class, $arguments); } public function testInitByRequest() { $oldSessionId = 'asdfhasdfjhkj2198sadf8sdf897'; $newSessionId = 'bsdfhasdfjhkj2198sadf8sdf897'; - $this->session->expects($this->any())->method('getSessionId')->willReturn($newSessionId); - $this->session->expects($this->atLeastOnce())->method('getVisitorData') + $this->sessionMock->expects($this->any())->method('getSessionId')->willReturn($newSessionId); + $this->sessionMock->expects($this->atLeastOnce())->method('getVisitorData') ->willReturn(['session_id' => $oldSessionId]); $this->visitor->initByRequest(null); $this->assertEquals($newSessionId, $this->visitor->getSessionId()); @@ -88,33 +103,32 @@ public function testInitByRequest() public function testSaveByRequest() { - $this->session->expects($this->once())->method('setVisitorData')->will($this->returnSelf()); + $this->sessionMock->expects($this->once())->method('setVisitorData')->will($this->returnSelf()); $this->assertSame($this->visitor, $this->visitor->saveByRequest(null)); } public function testIsModuleIgnored() { $this->visitor = $this->objectManagerHelper->getObject( - \Magento\Customer\Model\Visitor::class, + VisitorModel::class, [ - 'registry' => $this->registry, - 'session' => $this->session, - 'resource' => $this->resource, + 'registry' => $this->registryMock, + 'session' => $this->sessionMock, + 'resource' => $this->visitorResourceModelMock, 'ignores' => ['test_route_name' => true], + 'requestSafety' => $this->httpRequestMock, ] ); - $request = new \Magento\Framework\DataObject(['route_name' => 'test_route_name']); - $action = new \Magento\Framework\DataObject(['request' => $request]); - $event = new \Magento\Framework\DataObject(['controller_action' => $action]); - $observer = new \Magento\Framework\DataObject(['event' => $event]); + $this->httpRequestMock->method('getRouteName')->willReturn('test_route_name'); + $observer = new DataObject(); $this->assertTrue($this->visitor->isModuleIgnored($observer)); } public function testBindCustomerLogin() { - $customer = new \Magento\Framework\DataObject(['id' => '1']); - $observer = new \Magento\Framework\DataObject([ - 'event' => new \Magento\Framework\DataObject(['customer' => $customer]), + $customer = new DataObject(['id' => '1']); + $observer = new DataObject([ + 'event' => new DataObject(['customer' => $customer]), ]); $this->visitor->bindCustomerLogin($observer); @@ -130,7 +144,7 @@ public function testBindCustomerLogin() public function testBindCustomerLogout() { - $observer = new \Magento\Framework\DataObject(); + $observer = new DataObject(); $this->visitor->setCustomerId('1'); $this->visitor->bindCustomerLogout($observer); @@ -143,9 +157,9 @@ public function testBindCustomerLogout() public function testBindQuoteCreate() { - $quote = new \Magento\Framework\DataObject(['id' => '1', 'is_checkout_cart' => true]); - $observer = new \Magento\Framework\DataObject([ - 'event' => new \Magento\Framework\DataObject(['quote' => $quote]), + $quote = new DataObject(['id' => '1', 'is_checkout_cart' => true]); + $observer = new DataObject([ + 'event' => new DataObject(['quote' => $quote]), ]); $this->visitor->bindQuoteCreate($observer); $this->assertTrue($this->visitor->getDoQuoteCreate()); @@ -153,9 +167,9 @@ public function testBindQuoteCreate() public function testBindQuoteDestroy() { - $quote = new \Magento\Framework\DataObject(['id' => '1']); - $observer = new \Magento\Framework\DataObject([ - 'event' => new \Magento\Framework\DataObject(['quote' => $quote]), + $quote = new DataObject(['id' => '1']); + $observer = new DataObject([ + 'event' => new DataObject(['quote' => $quote]), ]); $this->visitor->bindQuoteDestroy($observer); $this->assertTrue($this->visitor->getDoQuoteDestroy()); @@ -163,7 +177,10 @@ public function testBindQuoteDestroy() public function testClean() { - $this->resource->expects($this->once())->method('clean')->with($this->visitor)->willReturnSelf(); + $this->visitorResourceModelMock->expects($this->once()) + ->method('clean') + ->with($this->visitor) + ->willReturnSelf(); $this->visitor->clean(); } } diff --git a/app/code/Magento/Customer/Test/Unit/Setup/RecurringDataTest.php b/app/code/Magento/Customer/Test/Unit/Setup/RecurringDataTest.php new file mode 100644 index 0000000000000..ee1af91552f6d --- /dev/null +++ b/app/code/Magento/Customer/Test/Unit/Setup/RecurringDataTest.php @@ -0,0 +1,129 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +declare(strict_types=1); + +namespace Magento\Customer\Test\Unit\Setup; + +use Magento\Framework\Indexer\IndexerInterface; +use Magento\Framework\Indexer\StateInterface; +use Magento\Framework\Indexer\IndexerRegistry; +use Magento\Framework\Setup\ModuleDataSetupInterface; +use Magento\Framework\Setup\ModuleContextInterface; +use Magento\Customer\Model\Customer; +use Magento\Customer\Setup\RecurringData; +use Magento\Framework\TestFramework\Unit\Helper\ObjectManager as ObjectManagerHelper; + +/** + * Test for recurring data + */ +class RecurringDataTest extends \PHPUnit\Framework\TestCase +{ + /** + * @var ObjectManagerHelper + */ + private $objectManagerHelper; + + /** + * @var IndexerInterface|\PHPUnit_Framework_MockObject_MockObject + */ + private $indexer; + + /** + * @var StateInterface|\PHPUnit_Framework_MockObject_MockObject + */ + private $state; + + /** + * @var IndexerRegistry|\PHPUnit_Framework_MockObject_MockObject + */ + private $indexerRegistry; + + /** + * @var ModuleDataSetupInterface|\PHPUnit_Framework_MockObject_MockObject + */ + private $setup; + + /** + * @var ModuleContextInterface|\PHPUnit_Framework_MockObject_MockObject + */ + private $context; + + /** + * @var RecurringData + */ + private $recurringData; + + /** + * @inheritdoc + */ + protected function setUp() + { + $this->objectManagerHelper = new ObjectManagerHelper($this); + $this->state = $this->getMockBuilder(StateInterface::class) + ->setMethods(['getStatus']) + ->getMockForAbstractClass(); + $this->indexer = $this->getMockBuilder(IndexerInterface::class) + ->setMethods(['getState', 'reindexAll']) + ->getMockForAbstractClass(); + $this->indexer->expects($this->any()) + ->method('getState') + ->willReturn($this->state); + $this->indexerRegistry = $this->getMockBuilder(IndexerRegistry::class) + ->disableOriginalConstructor() + ->setMethods(['get']) + ->getMock(); + $this->indexerRegistry->expects($this->any()) + ->method('get') + ->with(Customer::CUSTOMER_GRID_INDEXER_ID) + ->willReturn($this->indexer); + $this->setup = $this->getMockBuilder(ModuleDataSetupInterface::class) + ->setMethods(['tableExists']) + ->getMockForAbstractClass(); + $this->context = $this->getMockBuilder(ModuleContextInterface::class) + ->getMockForAbstractClass(); + + $this->recurringData = $this->objectManagerHelper->getObject( + RecurringData::class, + [ + 'indexerRegistry' => $this->indexerRegistry + ] + ); + } + + /** + * @param bool $isTableExists + * @param string $indexerState + * @param int $countReindex + * @return void + * @dataProvider installDataProvider + */ + public function testInstall(bool $isTableExists, string $indexerState, int $countReindex) + { + $this->setup->expects($this->any()) + ->method('tableExists') + ->with('customer_grid_flat') + ->willReturn($isTableExists); + $this->state->expects($this->any()) + ->method('getStatus') + ->willReturn($indexerState); + $this->indexer->expects($this->exactly($countReindex)) + ->method('reindexAll'); + $this->recurringData->install($this->setup, $this->context); + } + + /** + * @return array + */ + public function installDataProvider() : array + { + return [ + [true, StateInterface::STATUS_INVALID, 1], + [false, StateInterface::STATUS_INVALID, 1], + [true, StateInterface::STATUS_VALID, 0], + [false, StateInterface::STATUS_VALID, 1], + ]; + } +} diff --git a/app/code/Magento/Customer/Test/Unit/Ui/Component/Listing/Column/GroupActionsTest.php b/app/code/Magento/Customer/Test/Unit/Ui/Component/Listing/Column/GroupActionsTest.php new file mode 100644 index 0000000000000..02cacea5c2601 --- /dev/null +++ b/app/code/Magento/Customer/Test/Unit/Ui/Component/Listing/Column/GroupActionsTest.php @@ -0,0 +1,313 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +namespace Magento\Customer\Test\Unit\Ui\Component\Listing\Column; + +use Magento\Customer\Api\GroupManagementInterface; +use Magento\Customer\Ui\Component\Listing\Column\GroupActions; +use Magento\Framework\Escaper; +use Magento\Framework\TestFramework\Unit\Helper\ObjectManager; +use Magento\Framework\UrlInterface; +use Magento\Framework\View\Element\UiComponent\ContextInterface; +use Magento\Framework\View\Element\UiComponentFactory; +use PHPUnit\Framework\MockObject\MockObject; +use PHPUnit\Framework\TestCase; + +/** + * Class GroupActionsTest + * + * Testing GroupAction grid column + */ +class GroupActionsTest extends TestCase +{ + /** + * @var int + */ + private const STUB_NOT_LOGGED_IN_CUSTOMER_GROUP_ID = 0; + + /** + * @var string + */ + private const STUB_NOT_LOGGED_IN_CUSTOMER_GROUP_NAME = 'Not Logged In'; + + /** + * @var int + */ + private const STUB_GENERAL_CUSTOMER_GROUP_ID = 1; + + /** + * @var string + */ + private const STUB_GENERAL_CUSTOMER_GROUP_NAME = 'General'; + + /** + * @var string + */ + private const STUB_GROUP_EDIT_URL = 'http://magento.com/customer/group/edit'; + + /** + * @var string + */ + private const STUB_GROUP_DELETE_URL = 'http://magento.com/customer/group/delete'; + + /** + * @var GroupActions + */ + private $component; + + /** + * @var ContextInterface|MockObject + */ + private $contextMock; + + /** + * @var UiComponentFactory|MockObject + */ + private $uiComponentFactoryMock; + + /** + * @var UrlInterface|MockObject + */ + private $urlBuilderMock; + + /** + * @var Escaper|MockObject + */ + private $escaperMock; + + /** + * @var GroupManagementInterface|MockObject + */ + private $groupManagementMock; + + /** + * Set Up + */ + public function setUp() + { + $objectManager = new ObjectManager($this); + + $this->contextMock = $this->getMockBuilder(ContextInterface::class)->getMockForAbstractClass(); + $this->uiComponentFactoryMock = $this->createMock(UiComponentFactory::class); + $this->escaperMock = $this->createMock(Escaper::class); + $this->groupManagementMock = $this->createMock(GroupManagementInterface::class); + $this->urlBuilderMock = $this->getMockForAbstractClass( + UrlInterface::class, + [], + '', + false + ); + + $this->component = $objectManager->getObject( + GroupActions::class, + [ + 'context' => $this->contextMock, + 'uiComponentFactory' => $this->uiComponentFactoryMock, + 'urlBuilder' => $this->urlBuilderMock, + 'escaper' => $this->escaperMock, + 'components' => [], + 'data' => [ + 'name' => 'name' + ], + 'groupManagement' => $this->groupManagementMock + ] + ); + } + + /** + * Test data source with a non default customer group + * + * @dataProvider customerGroupsDataProvider + * + * @param array $items + * @param bool $isDefaultGroup + * @param array $expected + */ + public function testPrepareDataSourceWithNonDefaultGroup(array $items, bool $isDefaultGroup, array $expected) + { + $dataSource = [ + 'data' => [ + 'items' => $items + ] + ]; + $expectedDataSource = [ + 'data' => [ + 'items' => $expected + ] + ]; + + $this->groupManagementMock->expects($this->any()) + ->method('isReadonly') + ->with(static::STUB_GENERAL_CUSTOMER_GROUP_ID) + ->willReturn($isDefaultGroup); + $this->escaperMock->expects($this->any()) + ->method('escapeHtml') + ->with(static::STUB_GENERAL_CUSTOMER_GROUP_NAME) + ->willReturn(static::STUB_GENERAL_CUSTOMER_GROUP_NAME); + $this->urlBuilderMock->expects($this->any()) + ->method('getUrl') + ->willReturnMap( + [ + [ + 'customer/group/edit', + [ + 'id' => static::STUB_GENERAL_CUSTOMER_GROUP_ID + ], + static::STUB_GROUP_EDIT_URL], + [ + 'customer/group/delete', + [ + 'id' => static::STUB_GENERAL_CUSTOMER_GROUP_ID + ], + static::STUB_GROUP_DELETE_URL + ] + ] + ); + + $dataSource = $this->component->prepareDataSource($dataSource); + $this->assertEquals($expectedDataSource, $dataSource); + } + + /** + * Test data source with a default customer group + * + * @dataProvider customerGroupsDataProvider + */ + public function testPrepareDataSourceWithDefaultGroup() + { + $isDefaultGroup = true; + $dataSource = [ + 'data' => [ + 'items' => [ + [ + 'customer_group_id' => static::STUB_GENERAL_CUSTOMER_GROUP_ID, + 'customer_group_code' => static::STUB_GENERAL_CUSTOMER_GROUP_NAME, + ], + [ + 'customer_group_id' => static::STUB_NOT_LOGGED_IN_CUSTOMER_GROUP_ID, + 'customer_group_code' => static::STUB_NOT_LOGGED_IN_CUSTOMER_GROUP_NAME, + ], + ] + ] + ]; + $expectedDataSource = [ + 'data' => [ + 'items' => [ + [ + 'customer_group_id' => static::STUB_GENERAL_CUSTOMER_GROUP_ID, + 'customer_group_code' => static::STUB_GENERAL_CUSTOMER_GROUP_NAME, + 'name' => [ + 'edit' => [ + 'href' => static::STUB_GROUP_EDIT_URL, + 'label' => __('Edit'), + '__disableTmpl' => true, + ] + ] + ], + [ + 'customer_group_id' => static::STUB_NOT_LOGGED_IN_CUSTOMER_GROUP_ID, + 'customer_group_code' => static::STUB_NOT_LOGGED_IN_CUSTOMER_GROUP_NAME, + 'name' => [ + 'edit' => [ + 'href' => static::STUB_GROUP_EDIT_URL, + 'label' => __('Edit'), + '__disableTmpl' => true, + ] + ] + ] + ] + ] + ]; + + $this->groupManagementMock->expects($this->any()) + ->method('isReadonly') + ->willReturn($isDefaultGroup); + $this->escaperMock->expects($this->any()) + ->method('escapeHtml') + ->willReturnMap( + [ + [ + static::STUB_GENERAL_CUSTOMER_GROUP_NAME, + null, + static::STUB_GENERAL_CUSTOMER_GROUP_NAME + ], + [ + static::STUB_NOT_LOGGED_IN_CUSTOMER_GROUP_NAME, + null, + static::STUB_NOT_LOGGED_IN_CUSTOMER_GROUP_NAME + ] + ] + ); + $this->urlBuilderMock->expects($this->any()) + ->method('getUrl') + ->willReturnMap( + [ + [ + 'customer/group/edit', + [ + 'id' => static::STUB_GENERAL_CUSTOMER_GROUP_ID + ], + static::STUB_GROUP_EDIT_URL + ], + [ + 'customer/group/edit', + [ + 'id' => static::STUB_NOT_LOGGED_IN_CUSTOMER_GROUP_ID + ], + static::STUB_GROUP_EDIT_URL + ] + ] + ); + + $dataSource = $this->component->prepareDataSource($dataSource); + $this->assertEquals($expectedDataSource, $dataSource); + } + + /** + * Providing customer group data + * + * @return array + */ + public function customerGroupsDataProvider(): array + { + return [ + [ + [ + [ + 'customer_group_id' => static::STUB_GENERAL_CUSTOMER_GROUP_ID, + 'customer_group_code' => static::STUB_GENERAL_CUSTOMER_GROUP_NAME, + ], + ], + false, + [ + [ + 'customer_group_id' => static::STUB_GENERAL_CUSTOMER_GROUP_ID, + 'customer_group_code' => static::STUB_GENERAL_CUSTOMER_GROUP_NAME, + 'name' => [ + 'edit' => [ + 'href' => static::STUB_GROUP_EDIT_URL, + 'label' => __('Edit'), + '__disableTmpl' => true, + ], + 'delete' => [ + 'href' => static::STUB_GROUP_DELETE_URL, + 'label' => __('Delete'), + 'post' => true, + '__disableTmpl' => true, + 'confirm' => [ + 'title' => __('Delete %1', 'General'), + 'message' => __( + 'Are you sure you want to delete a %1 record?', + 'General' + ) + ], + ] + ] + ] + ] + ] + ]; + } +} diff --git a/app/code/Magento/Customer/Test/Unit/ViewModel/Customer/StoreTest.php b/app/code/Magento/Customer/Test/Unit/ViewModel/Customer/StoreTest.php new file mode 100644 index 0000000000000..2e34bcf7ab698 --- /dev/null +++ b/app/code/Magento/Customer/Test/Unit/ViewModel/Customer/StoreTest.php @@ -0,0 +1,210 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ + +declare(strict_types=1); + +namespace Magento\Customer\Test\Unit\ViewModel\Customer; + +use Magento\Customer\Model\Config\Share as ConfigShare; +use Magento\Framework\App\Request\DataPersistorInterface; +use Magento\Store\Model\StoreManagerInterface; +use PHPUnit\Framework\TestCase; +use Magento\Framework\TestFramework\Unit\Helper\ObjectManager as ObjectManagerHelper; +use Magento\Customer\ViewModel\Customer\Store as CustomerStore; +use Magento\Store\Model\System\Store as SystemStore; +use Magento\Store\Model\Store; + +/** + * Test for customer's store view model + */ +class StoreTest extends TestCase +{ + /** @var ObjectManagerHelper */ + private $objectManagerHelper; + + /** + * @var CustomerStore + */ + private $customerStore; + + /** + * @var SystemStore + */ + private $systemStore; + + /** + * @var Store + */ + private $store; + + /** + * @var ConfigShare + */ + protected $configShare; + + /** + * @var StoreManagerInterface + */ + protected $storeManager; + + /** + * @var DataPersistorInterface + */ + private $dataPersistor; + + protected function setUp() + { + $this->systemStore = $this->createMock(SystemStore::class); + $this->store = $this->createMock(Store::class); + $this->configShare = $this->createMock(ConfigShare::class); + $this->storeManager = $this->createMock(StoreManagerInterface::class); + $this->dataPersistor = $this->createMock(DataPersistorInterface::class); + $this->objectManagerHelper = new ObjectManagerHelper($this); + $this->customerStore = $this->objectManagerHelper->getObject( + CustomerStore::class, + [ + 'systemStore' => $this->systemStore, + 'configShare' => $this->configShare, + 'storeManager' => $this->storeManager, + 'dataPersistor' => $this->dataPersistor + ] + ); + } + + /** + * Test that method return correct array of options + * + * @param array $options + * @param bool $isWebsiteScope + * @param bool $isCustomerDataInSession + * @dataProvider dataProviderOptionsArray + * @return void + */ + public function testToOptionArray(array $options, bool $isWebsiteScope, bool $isCustomerDataInSession): void + { + $this->configShare->method('isWebsiteScope') + ->willReturn($isWebsiteScope); + $this->store->method('getWebsiteId') + ->willReturn(1); + + if ($isCustomerDataInSession) { + $this->dataPersistor->method('get') + ->with('customer') + ->willReturn([ + 'account' => ['website_id' => '1'] + ]); + } else { + $this->storeManager->method('getDefaultStoreView') + ->willReturn($this->store); + } + + $this->systemStore->method('getStoreData') + ->willReturn($this->store); + $this->systemStore->method('getStoreValuesForForm') + ->willReturn([ + [ + 'label' => 'Main Website', + 'value' => [], + '__disableTmpl' => true, + ], + [ + 'label' => 'Main Website', + 'value' => [ + [ + 'label' => '    Default Store View', + 'value' => '1', + ] + ], + '__disableTmpl' => true, + ] + ]); + + $this->assertEquals($options, $this->customerStore->toOptionArray()); + } + + /** + * Data provider for testToOptionArray test + * + * @return array + */ + public function dataProviderOptionsArray(): array + { + return [ + [ + 'options' => [ + [ + 'label' => 'Main Website', + 'value' => [], + '__disableTmpl' => true, + 'website_id' => '1', + ], + [ + 'label' => 'Main Website', + 'value' => [ + [ + 'label' => '    Default Store View', + 'value' => '1', + 'website_id' => '1', + ] + ], + '__disableTmpl' => true, + 'website_id' => '1', + ] + ], + 'isWebsiteScope' => true, + 'isCustomerDataInSession' => false, + ], + [ + 'options' => [ + [ + 'label' => 'Main Website', + 'value' => [], + '__disableTmpl' => true, + 'website_id' => '1', + ], + [ + 'label' => 'Main Website', + 'value' => [ + [ + 'label' => '    Default Store View', + 'value' => '1', + 'website_id' => '1', + ] + ], + '__disableTmpl' => true, + 'website_id' => '1', + ] + ], + 'isWebsiteScope' => false, + 'isCustomerDataInSession' => false, + ], + [ + 'options' => [ + [ + 'label' => 'Main Website', + 'value' => [], + '__disableTmpl' => true, + 'website_id' => '1', + ], + [ + 'label' => 'Main Website', + 'value' => [ + [ + 'label' => '    Default Store View', + 'value' => '1', + 'website_id' => '1', + ] + ], + '__disableTmpl' => true, + 'website_id' => '1', + ] + ], + 'isWebsiteScope' => false, + 'isCustomerDataInSession' => true, + ] + ]; + } +} diff --git a/app/code/Magento/Customer/Test/Unit/ViewModel/Customer/WebsiteTest.php b/app/code/Magento/Customer/Test/Unit/ViewModel/Customer/WebsiteTest.php new file mode 100644 index 0000000000000..50862fa36f0f9 --- /dev/null +++ b/app/code/Magento/Customer/Test/Unit/ViewModel/Customer/WebsiteTest.php @@ -0,0 +1,105 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ + +declare(strict_types=1); + +namespace Magento\Customer\Test\Unit\ViewModel\Customer; + +use Magento\Framework\App\Config\ScopeConfigInterface; +use PHPUnit\Framework\TestCase; +use Magento\Framework\TestFramework\Unit\Helper\ObjectManager as ObjectManagerHelper; +use Magento\Customer\ViewModel\Customer\Website as CustomerWebsite; +use Magento\Store\Model\System\Store as SystemStore; + +/** + * Test for customer's website view model + */ +class WebsiteTest extends TestCase +{ + /** @var ObjectManagerHelper */ + private $objectManagerHelper; + + /** + * @var CustomerWebsite + */ + private $customerWebsite; + + /** + * @var SystemStore + */ + private $systemStore; + + /** + * @var ScopeConfigInterface + */ + private $scopeConfig; + + protected function setUp() + { + $this->systemStore = $this->createMock(SystemStore::class); + $this->scopeConfig = $this->createMock(ScopeConfigInterface::class); + $this->objectManagerHelper = new ObjectManagerHelper($this); + $this->customerWebsite = $this->objectManagerHelper->getObject( + CustomerWebsite::class, + [ + 'systemStore' => $this->systemStore, + 'scopeConfig' => $this->scopeConfig + ] + ); + } + + /** + * Test that method return correct array of options + * + * @param array $options + * @dataProvider dataProviderOptionsArray + * @return void + */ + public function testToOptionArray(array $options): void + { + $this->scopeConfig->method('getValue') + ->willReturn(1); + + $this->systemStore->method('getWebsiteValuesForForm') + ->willReturn([ + [ + 'label' => 'Main Website', + 'value' => '1', + ], + [ + 'label' => 'Second Website', + 'value' => '2', + ], + ]); + + $this->assertEquals($options, $this->customerWebsite->toOptionArray()); + } + + /** + * Data provider for testToOptionArray test + * + * @return array + */ + public function dataProviderOptionsArray(): array + { + return [ + [ + 'options' => [ + [ + 'label' => 'Main Website', + 'value' => '1', + 'group_id' => '1', + ], + [ + 'label' => 'Second Website', + 'value' => '2', + 'group_id' => '1', + ], + ], + ], + ]; + } +} diff --git a/app/code/Magento/Customer/Ui/Component/ColumnFactory.php b/app/code/Magento/Customer/Ui/Component/ColumnFactory.php index cb66dc3db7c77..a69e84ab41a2c 100644 --- a/app/code/Magento/Customer/Ui/Component/ColumnFactory.php +++ b/app/code/Magento/Customer/Ui/Component/ColumnFactory.php @@ -130,8 +130,6 @@ protected function getJsComponent($dataType) */ protected function getDataType($frontendType) { - return isset($this->dataTypeMap[$frontendType]) - ? $this->dataTypeMap[$frontendType] - : $this->dataTypeMap['default']; + return $this->dataTypeMap[$frontendType] ?? $this->dataTypeMap['default']; } } diff --git a/app/code/Magento/Customer/Ui/Component/FilterFactory.php b/app/code/Magento/Customer/Ui/Component/FilterFactory.php index 9bf07b877cc07..3e57db06246d4 100644 --- a/app/code/Magento/Customer/Ui/Component/FilterFactory.php +++ b/app/code/Magento/Customer/Ui/Component/FilterFactory.php @@ -76,6 +76,6 @@ public function create(array $attributeData, $context) */ protected function getFilterType($frontendInput) { - return isset($this->filterMap[$frontendInput]) ? $this->filterMap[$frontendInput] : $this->filterMap['default']; + return $this->filterMap[$frontendInput] ?? $this->filterMap['default']; } } diff --git a/app/code/Magento/Customer/Ui/Component/Listing/AttributeRepository.php b/app/code/Magento/Customer/Ui/Component/Listing/AttributeRepository.php index eb8359de93f32..0a89cb6ea1449 100644 --- a/app/code/Magento/Customer/Ui/Component/Listing/AttributeRepository.php +++ b/app/code/Magento/Customer/Ui/Component/Listing/AttributeRepository.php @@ -14,7 +14,7 @@ use Magento\Customer\Model\Indexer\Attribute\Filter; /** - * Class AttributeRepository + * Attribute Repository Managment */ class AttributeRepository { @@ -156,10 +156,10 @@ protected function getOptionArray(array $options) * Return customer group's metadata by given group code * * @param string $code - * @return [] + * @return array | null */ public function getMetadataByCode($code) { - return isset($this->getList()[$code]) ? $this->getList()[$code] : null; + return $this->getList()[$code] ?? null; } } diff --git a/app/code/Magento/Customer/Ui/Component/Listing/Column/GroupActions.php b/app/code/Magento/Customer/Ui/Component/Listing/Column/GroupActions.php index 5d974088b0d54..e5a536dc6ecd6 100644 --- a/app/code/Magento/Customer/Ui/Component/Listing/Column/GroupActions.php +++ b/app/code/Magento/Customer/Ui/Component/Listing/Column/GroupActions.php @@ -8,6 +8,10 @@ namespace Magento\Customer\Ui\Component\Listing\Column; +use Magento\Customer\Api\GroupManagementInterface; +use Magento\Framework\App\ObjectManager; +use Magento\Framework\Exception\LocalizedException; +use Magento\Framework\Exception\NoSuchEntityException; use Magento\Framework\UrlInterface; use Magento\Framework\View\Element\UiComponent\ContextInterface; use Magento\Framework\View\Element\UiComponentFactory; @@ -16,6 +20,8 @@ /** * Class GroupActions + * + * Customer Groups actions column */ class GroupActions extends Column { @@ -25,6 +31,11 @@ class GroupActions extends Column const URL_PATH_EDIT = 'customer/group/edit'; const URL_PATH_DELETE = 'customer/group/delete'; + /** + * @var GroupManagementInterface + */ + private $groupManagement; + /** * @var UrlInterface */ @@ -44,6 +55,7 @@ class GroupActions extends Column * @param Escaper $escaper * @param array $components * @param array $data + * @param GroupManagementInterface $groupManagement */ public function __construct( ContextInterface $context, @@ -51,10 +63,13 @@ public function __construct( UrlInterface $urlBuilder, Escaper $escaper, array $components = [], - array $data = [] + array $data = [], + GroupManagementInterface $groupManagement = null ) { $this->urlBuilder = $urlBuilder; $this->escaper = $escaper; + $this->groupManagement = $groupManagement ?: ObjectManager::getInstance()->get(GroupManagementInterface::class); + parent::__construct($context, $uiComponentFactory, $components, $data); } @@ -63,6 +78,8 @@ public function __construct( * * @param array $dataSource * @return array + * @throws LocalizedException + * @throws NoSuchEntityException */ public function prepareDataSource(array $dataSource) { @@ -83,29 +100,26 @@ public function prepareDataSource(array $dataSource) ], ]; - // hide delete action for 'NOT LOGGED IN' group - if ($item['customer_group_id'] == 0 && $item['customer_group_code']) { - continue; + if (!$this->groupManagement->isReadonly($item['customer_group_id'])) { + $item[$this->getData('name')]['delete'] = [ + 'href' => $this->urlBuilder->getUrl( + static::URL_PATH_DELETE, + [ + 'id' => $item['customer_group_id'] + ] + ), + 'label' => __('Delete'), + 'confirm' => [ + 'title' => __('Delete %1', $this->escaper->escapeHtml($title)), + 'message' => __( + 'Are you sure you want to delete a %1 record?', + $this->escaper->escapeHtml($title) + ) + ], + 'post' => true, + '__disableTmpl' => true + ]; } - - $item[$this->getData('name')]['delete'] = [ - 'href' => $this->urlBuilder->getUrl( - static::URL_PATH_DELETE, - [ - 'id' => $item['customer_group_id'] - ] - ), - 'label' => __('Delete'), - 'confirm' => [ - 'title' => __('Delete %1', $this->escaper->escapeHtml($title)), - 'message' => __( - 'Are you sure you want to delete a %1 record?', - $this->escaper->escapeHtml($title) - ) - ], - 'post' => true, - '__disableTmpl' => true - ]; } } } diff --git a/app/code/Magento/Customer/Ui/Component/Listing/Column/ValidationRules.php b/app/code/Magento/Customer/Ui/Component/Listing/Column/ValidationRules.php index 6befec8e942a1..346f6bdad43bd 100644 --- a/app/code/Magento/Customer/Ui/Component/Listing/Column/ValidationRules.php +++ b/app/code/Magento/Customer/Ui/Component/Listing/Column/ValidationRules.php @@ -63,9 +63,7 @@ public function getValidationRules($isRequired, $validationRules) protected function getValidationClass(ValidationRuleInterface $rule) { $key = $rule->getName() == 'input_validation' ? $rule->getValue() : $rule->getName(); - return isset($this->inputValidationMap[$key]) - ? $this->inputValidationMap[$key] - : $key; + return $this->inputValidationMap[$key] ?? $key; } /** diff --git a/app/code/Magento/Customer/Ui/Component/Listing/Columns.php b/app/code/Magento/Customer/Ui/Component/Listing/Columns.php index 70f8d085cc5ea..9bc164d7904d1 100644 --- a/app/code/Magento/Customer/Ui/Component/Listing/Columns.php +++ b/app/code/Magento/Customer/Ui/Component/Listing/Columns.php @@ -12,6 +12,9 @@ use Magento\Customer\Ui\Component\Listing\Column\InlineEditUpdater; use Magento\Customer\Api\CustomerMetadataInterface; +/** + * Columns component + */ class Columns extends \Magento\Ui\Component\Listing\Columns { /** @@ -29,6 +32,11 @@ class Columns extends \Magento\Ui\Component\Listing\Columns */ protected $inlineEditUpdater; + /** + * @var ColumnFactory + */ + private $columnFactory; + /** * @var array */ @@ -63,6 +71,8 @@ public function __construct( } /** + * Return default sort order + * * @return int */ protected function getDefaultSortOrder() @@ -94,7 +104,7 @@ protected function updateActionColumnSortOrder() } /** - * {@inheritdoc} + * @inheritdoc */ public function prepare() { @@ -113,6 +123,8 @@ public function prepare() } /** + * Add column to the component + * * @param array $attributeData * @param string $columnName * @return void @@ -129,6 +141,8 @@ public function addColumn(array $attributeData, $columnName) } /** + * Update column in component + * * @param array $attributeData * @param string $newAttributeCode * @return void @@ -200,6 +214,6 @@ public function addOptions(UiComponentInterface $component, array $attributeData */ protected function getFilterType($frontendInput) { - return isset($this->filterMap[$frontendInput]) ? $this->filterMap[$frontendInput] : $this->filterMap['default']; + return $this->filterMap[$frontendInput] ?? $this->filterMap['default']; } } diff --git a/app/code/Magento/Customer/ViewModel/Address.php b/app/code/Magento/Customer/ViewModel/Address.php new file mode 100644 index 0000000000000..58df4c1adc1d4 --- /dev/null +++ b/app/code/Magento/Customer/ViewModel/Address.php @@ -0,0 +1,113 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ + +namespace Magento\Customer\ViewModel; + +use Magento\Directory\Helper\Data as DataHelper; +use Magento\Customer\Helper\Address as AddressHelper; +use Magento\Framework\Exception\NoSuchEntityException; +use Magento\Framework\View\Element\Block\ArgumentInterface; + + /** + * Custom address view model + */ +class Address implements ArgumentInterface +{ + /** + * @var DataHelper + */ + private $helperData; + + /** + * @var AddressHelper + */ + private $helperAddress; + + /** + * Constructor + * + * @param DataHelper $helperData + * @param AddressHelper $helperAddress + * @SuppressWarnings(PHPMD.UnusedFormalParameter) + */ + public function __construct( + DataHelper $helperData, + AddressHelper $helperAddress + ) { + $this->helperData= $helperData; + $this->helperAddress= $helperAddress; + } + + /** + * Get string with frontend validation classes for attribute + * + * @param string $attributeCode + * @return string + * @throws \Magento\Framework\Exception\LocalizedException + */ + public function dataGetAttributeValidationClass($attributeCode) + { + return $this->helperData->getAttributeValidationClass($attributeCode); + } + + /** + * Get string with frontend validation classes for attribute + * + * @param string $attributeCode + * @return string + * @throws \Magento\Framework\Exception\LocalizedException + */ + public function addressGetAttributeValidationClass($attributeCode) + { + return $this->helperAddress->getAttributeValidationClass($attributeCode); + } + + /** + * Return Number of Lines in a Street Address for store + * + * @param \Magento\Store\Model\Store|int|string $store + * + * @return int + * @throws NoSuchEntityException + * @throws \Magento\Framework\Exception\LocalizedException + */ + public function addressGetStreetLines() + { + return $this->helperAddress->getStreetLines(); + } + + /** + * Check if VAT ID address attribute has to be shown on frontend (on Customer Address management forms) + * + * @return boolean + */ + public function addressIsVatAttributeVisible() + { + return $this->helperAddress->isVatAttributeVisible(); + } + + /** + * Retrieve regions data json + * + * @return string + * @throws NoSuchEntityException + */ + public function dataGetRegionJson() + { + return $this->helperData->getRegionJson(); + } + + /** + * Return ISO2 country codes, which have optional Zip/Postal pre-configured + * + * @param bool $asJson + * @return array|string + */ + public function dataGetCountriesWithOptionalZip($asJson) + { + return $this->helperData->getCountriesWithOptionalZip($asJson); + } +} diff --git a/app/code/Magento/Customer/ViewModel/Customer/Store.php b/app/code/Magento/Customer/ViewModel/Customer/Store.php new file mode 100644 index 0000000000000..1e6ca69e2d77a --- /dev/null +++ b/app/code/Magento/Customer/ViewModel/Customer/Store.php @@ -0,0 +1,131 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ + +declare(strict_types=1); + +namespace Magento\Customer\ViewModel\Customer; + +use Magento\Customer\Model\Config\Share as ConfigShare; +use Magento\Framework\App\Request\DataPersistorInterface; +use Magento\Framework\Data\OptionSourceInterface; +use Magento\Store\Model\StoreManagerInterface; +use Magento\Store\Model\System\Store as SystemStore; + +/** + * Customer's store view model + */ +class Store implements OptionSourceInterface +{ + /** + * @var SystemStore + */ + private $systemStore; + + /** + * @var ConfigShare + */ + private $configShare; + + /** + * @var StoreManagerInterface + */ + private $storeManager; + + /** + * @var DataPersistorInterface + */ + private $dataPersistor; + + /** + * Store constructor. + * + * @param SystemStore $systemStore + * @param ConfigShare $configShare + * @param StoreManagerInterface $storeManager + * @param DataPersistorInterface $dataPersistor + */ + public function __construct( + SystemStore $systemStore, + ConfigShare $configShare, + StoreManagerInterface $storeManager, + DataPersistorInterface $dataPersistor + ) { + $this->systemStore = $systemStore; + $this->configShare = $configShare; + $this->storeManager = $storeManager; + $this->dataPersistor = $dataPersistor; + } + + /** + * @inheritdoc + */ + public function toOptionArray(): array + { + return (bool)$this->configShare->isWebsiteScope() ? $this->getStoreOptions() + : $this->getStoreOptionsWithCurrentWebsiteId(); + } + + /** + * Adding website ID to options list + * + * @return array + */ + private function getStoreOptions(): array + { + $options = $this->systemStore->getStoreValuesForForm(); + + $websiteKey = null; + foreach ($options as $key => $option) { + if ($websiteKey === null) { + $websiteKey = $key; + } + if (is_array($option['value']) && !empty($option['value'])) { + $websiteId = null; + foreach ($option['value'] as $storeViewKey => $storeView) { + $websiteId = $this->systemStore->getStoreData($storeView['value'])->getWebsiteId(); + $options[$key]['value'][$storeViewKey]['website_id'] = $websiteId; + } + if ($websiteId) { + $options[$key]['website_id'] = $websiteId; + if ($websiteKey !== null) { + $options[$websiteKey]['website_id'] = $websiteId; + $websiteKey = null; + } + } + } + } + + return $options; + } + + /** + * Adding current website ID to options list + * + * @return array + */ + private function getStoreOptionsWithCurrentWebsiteId(): array + { + $options = $this->systemStore->getStoreValuesForForm(); + + if (!empty($this->dataPersistor->get('customer')['account'])) { + $currentWebsiteId = (string)$this->dataPersistor->get('customer')['account']['website_id']; + } else { + $currentWebsiteId = $this->storeManager->getDefaultStoreView()->getWebsiteId(); + } + + foreach ($options as $key => $option) { + $options[$key]['website_id'] = $currentWebsiteId; + if (is_array($option['value']) && !empty($option['value'])) { + foreach ($option['value'] as $storeViewKey => $storeView) { + $storeView['website_id'] = $currentWebsiteId; + $options[$key]['value'][$storeViewKey] = $storeView; + } + } + } + + return $options; + } +} diff --git a/app/code/Magento/Customer/ViewModel/Customer/Website.php b/app/code/Magento/Customer/ViewModel/Customer/Website.php new file mode 100644 index 0000000000000..bb25f4766aa77 --- /dev/null +++ b/app/code/Magento/Customer/ViewModel/Customer/Website.php @@ -0,0 +1,74 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ + +declare(strict_types=1); + +namespace Magento\Customer\ViewModel\Customer; + +use Magento\Customer\Model\GroupManagement; +use Magento\Framework\App\Config\ScopeConfigInterface; +use Magento\Framework\Data\OptionSourceInterface; +use Magento\Store\Model\ScopeInterface; +use Magento\Store\Model\System\Store as SystemStore; + +/** + * Customer's website view model + */ +class Website implements OptionSourceInterface +{ + /** + * @var SystemStore + */ + private $systemStore; + + /** + * @var ScopeConfigInterface + */ + private $scopeConfig; + + /** + * Store constructor. + * + * @param SystemStore $systemStore + * @param ScopeConfigInterface $scopeConfig + */ + public function __construct( + SystemStore $systemStore, + ScopeConfigInterface $scopeConfig + ) { + $this->systemStore = $systemStore; + $this->scopeConfig = $scopeConfig; + } + + /** + * @inheritdoc + */ + public function toOptionArray(): array + { + return $this->getWebsiteOptions(); + } + + /** + * Adding group ID to options list + * + * @return array + */ + private function getWebsiteOptions(): array + { + $options = $this->systemStore->getWebsiteValuesForForm(); + foreach ($options as $key => $option) { + $websiteId = $option['value']; + $groupId = $this->scopeConfig->getValue( + GroupManagement::XML_PATH_DEFAULT_ID, + ScopeInterface::SCOPE_WEBSITE, + $websiteId + ); + $options[$key]['group_id'] = $groupId; + } + + return $options; + } +} diff --git a/app/code/Magento/Customer/composer.json b/app/code/Magento/Customer/composer.json index 3e98818a67b74..a9bc41c36e703 100644 --- a/app/code/Magento/Customer/composer.json +++ b/app/code/Magento/Customer/composer.json @@ -19,7 +19,6 @@ "magento/module-newsletter": "*", "magento/module-page-cache": "*", "magento/module-quote": "*", - "magento/module-review": "*", "magento/module-sales": "*", "magento/module-store": "*", "magento/module-tax": "*", diff --git a/app/code/Magento/Customer/etc/adminhtml/system.xml b/app/code/Magento/Customer/etc/adminhtml/system.xml index 2bd1041214801..fca625d847a1d 100644 --- a/app/code/Magento/Customer/etc/adminhtml/system.xml +++ b/app/code/Magento/Customer/etc/adminhtml/system.xml @@ -15,10 +15,10 @@ <label>Customer Configuration</label> <tab>customer</tab> <resource>Magento_Customer::config_customer</resource> - <group id="account_share" translate="label" type="text" sortOrder="10" showInDefault="1" showInWebsite="0" showInStore="0"> + <group id="account_share" translate="label" type="text" sortOrder="10" showInDefault="1"> <label>Account Sharing Options</label> <hide_in_single_store_mode>1</hide_in_single_store_mode> - <field id="scope" translate="label" type="select" sortOrder="1" showInDefault="1" showInWebsite="0" showInStore="0" canRestore="1"> + <field id="scope" translate="label" type="select" sortOrder="1" showInDefault="1" canRestore="1"> <label>Share Customer Accounts</label> <backend_model>Magento\Customer\Model\Config\Share</backend_model> <source_model>Magento\Customer\Model\Config\Share</source_model> @@ -26,7 +26,7 @@ </group> <group id="create_account" translate="label" sortOrder="20" showInDefault="1" showInWebsite="1" showInStore="1"> <label>Create New Account Options</label> - <field id="auto_group_assign" translate="label" type="select" sortOrder="10" showInDefault="1" showInWebsite="1" showInStore="1"> + <field id="auto_group_assign" translate="label" type="select" sortOrder="10" showInDefault="1" showInWebsite="1" showInStore="1" canRestore="1"> <label>Enable Automatic Assignment to Customer Group</label> <source_model>Magento\Config\Model\Config\Source\Yesno</source_model> </field> @@ -76,12 +76,12 @@ <field id="auto_group_assign">1</field> </depends> </field> - <field id="viv_disable_auto_group_assign_default" translate="label" type="select" sortOrder="57" showInDefault="1" showInWebsite="0" showInStore="0"> + <field id="viv_disable_auto_group_assign_default" translate="label" type="select" sortOrder="57" showInDefault="1" canRestore="1"> <label>Default Value for Disable Automatic Group Changes Based on VAT ID</label> <source_model>Magento\Config\Model\Config\Source\Yesno</source_model> <backend_model>Magento\Customer\Model\Config\Backend\CreateAccount\DisableAutoGroupAssignDefault</backend_model> </field> - <field id="vat_frontend_visibility" translate="label comment" type="select" sortOrder="58" showInDefault="1" showInWebsite="1" showInStore="0" canRestore="1"> + <field id="vat_frontend_visibility" translate="label comment" type="select" sortOrder="58" showInDefault="1" showInWebsite="1" canRestore="1"> <label>Show VAT Number on Storefront</label> <comment>To show VAT number on Storefront, set Show VAT Number on Storefront option to Yes.</comment> <source_model>Magento\Config\Model\Config\Source\Yesno</source_model> @@ -106,7 +106,7 @@ <label>Email Sender</label> <source_model>Magento\Config\Model\Config\Source\Email\Identity</source_model> </field> - <field id="confirm" translate="label" type="select" sortOrder="90" showInDefault="1" showInWebsite="1" showInStore="0" canRestore="1"> + <field id="confirm" translate="label" type="select" sortOrder="90" showInDefault="1" showInWebsite="1" canRestore="1"> <label>Require Emails Confirmation</label> <source_model>Magento\Config\Model\Config\Source\Yesno</source_model> </field> @@ -123,7 +123,7 @@ ]]></comment> <source_model>Magento\Config\Model\Config\Source\Email\Template</source_model> </field> - <field id="generate_human_friendly_id" translate="label" type="select" sortOrder="120" showInDefault="1" showInWebsite="0" showInStore="0"> + <field id="generate_human_friendly_id" translate="label" type="select" sortOrder="120" showInDefault="1" canRestore="1"> <label>Generate Human-Friendly Customer ID</label> <source_model>Magento\Config\Model\Config\Source\Yesno</source_model> </field> @@ -149,33 +149,33 @@ <label>Password Template Email Sender</label> <source_model>Magento\Config\Model\Config\Source\Email\Identity</source_model> </field> - <field id="reset_link_expiration_period" translate="label comment" type="text" sortOrder="60" showInDefault="1" showInWebsite="0" showInStore="0" canRestore="1"> + <field id="reset_link_expiration_period" translate="label comment" type="text" sortOrder="60" showInDefault="1" canRestore="1"> <label>Recovery Link Expiration Period (hours)</label> <comment>Please enter a number 1 or greater in this field.</comment> <validate>required-entry validate-digits validate-digits-range digits-range-1-</validate> <backend_model>Magento\Customer\Model\Config\Backend\Password\Link\Expirationperiod</backend_model> </field> - <field id="required_character_classes_number" translate="label comment" type="text" sortOrder="70" showInDefault="1" showInWebsite="0" showInStore="0" canRestore="1"> + <field id="required_character_classes_number" translate="label comment" type="text" sortOrder="70" showInDefault="1" canRestore="1"> <label>Number of Required Character Classes</label> <comment>Number of different character classes required in password: Lowercase, Uppercase, Digits, Special Characters.</comment> <validate>required-entry validate-digits validate-digits-range digits-range-1-4</validate> </field> - <field id="minimum_password_length" translate="label comment" type="text" sortOrder="80" showInDefault="1" showInWebsite="0" showInStore="0" canRestore="1"> + <field id="minimum_password_length" translate="label comment" type="text" sortOrder="80" showInDefault="1" canRestore="1"> <label>Minimum Password Length</label> <comment>Please enter a number 1 or greater in this field.</comment> <validate>required-entry validate-digits validate-digits-range digits-range-1-</validate> </field> - <field id="lockout_failures" translate="label comment" sortOrder="70" showInDefault="1" showInWebsite="0" showInStore="0" canRestore="1"> + <field id="lockout_failures" translate="label comment" sortOrder="70" showInDefault="1" canRestore="1"> <label>Maximum Login Failures to Lockout Account</label> <comment>Use 0 to disable account locking.</comment> <frontend_class>required-entry validate-digits</frontend_class> </field> - <field id="lockout_threshold" translate="label comment" sortOrder="80" showInDefault="1" showInWebsite="0" showInStore="0" canRestore="1"> + <field id="lockout_threshold" translate="label comment" sortOrder="80" showInDefault="1" canRestore="1"> <label>Lockout Time (minutes)</label> <comment>Account will be unlocked after provided time.</comment> <frontend_class>required-entry validate-digits</frontend_class> </field> - <field id="autocomplete_on_storefront" type="select" translate="label" sortOrder="65" showInDefault="1" showInWebsite="1" showInStore="0" canRestore="1"> + <field id="autocomplete_on_storefront" type="select" translate="label" sortOrder="65" showInDefault="1" showInWebsite="1" canRestore="1"> <label>Enable Autocomplete on login/forgot password forms</label> <source_model>Magento\Config\Model\Config\Source\Yesno</source_model> </field> @@ -195,68 +195,68 @@ </group> <group id="address" translate="label" sortOrder="40" showInDefault="1" showInWebsite="1" showInStore="1"> <label>Name and Address Options</label> - <field id="street_lines" translate="label comment" sortOrder="10" showInDefault="1" showInWebsite="1" showInStore="0" canRestore="1"> + <field id="street_lines" translate="label comment" sortOrder="10" showInDefault="1" showInWebsite="1" canRestore="1"> <label>Number of Lines in a Street Address</label> <backend_model>Magento\Customer\Model\Config\Backend\Address\Street</backend_model> <comment>Valid range: 1-4</comment> <validate>required-entry validate-digits validate-digits-range digits-range-1-4</validate> </field> - <field id="prefix_show" translate="label comment" type="select" sortOrder="20" showInDefault="1" showInWebsite="1" showInStore="0"> + <field id="prefix_show" translate="label comment" type="select" sortOrder="20" showInDefault="1" showInWebsite="1" canRestore="1"> <label>Show Prefix</label> <source_model>Magento\Config\Model\Config\Source\Nooptreq</source_model> <backend_model>Magento\Customer\Model\Config\Backend\Show\Address</backend_model> <comment>The title that goes before name (Mr., Mrs., etc.)</comment> </field> - <field id="prefix_options" translate="label comment" sortOrder="30" showInDefault="1" showInWebsite="1" showInStore="0"> + <field id="prefix_options" translate="label comment" sortOrder="30" showInDefault="1" showInWebsite="1" canRestore="1"> <label>Prefix Dropdown Options</label> <comment> <![CDATA[Semicolon (;) separated values.<br/>Leave empty for open text field.]]> </comment> </field> - <field id="middlename_show" translate="label comment" type="select" sortOrder="40" showInDefault="1" showInWebsite="1" showInStore="0"> + <field id="middlename_show" translate="label comment" type="select" sortOrder="40" showInDefault="1" showInWebsite="1" canRestore="1"> <label>Show Middle Name (initial)</label> <source_model>Magento\Config\Model\Config\Source\Yesno</source_model> <comment>Always optional.</comment> <backend_model>Magento\Customer\Model\Config\Backend\Show\Address</backend_model> </field> - <field id="suffix_show" translate="label comment" type="select" sortOrder="50" showInDefault="1" showInWebsite="1" showInStore="0"> + <field id="suffix_show" translate="label comment" type="select" sortOrder="50" showInDefault="1" showInWebsite="1" canRestore="1"> <label>Show Suffix</label> <source_model>Magento\Config\Model\Config\Source\Nooptreq</source_model> <comment>The suffix that goes after name (Jr., Sr., etc.)</comment> <backend_model>Magento\Customer\Model\Config\Backend\Show\Address</backend_model> </field> - <field id="suffix_options" translate="label comment" sortOrder="60" showInDefault="1" showInWebsite="1" showInStore="0"> + <field id="suffix_options" translate="label comment" sortOrder="60" showInDefault="1" showInWebsite="1" canRestore="1"> <label>Suffix Dropdown Options</label> <comment> <![CDATA[Semicolon (;) separated values.<br/>Leave empty for open text field.]]> </comment> </field> - <field id="dob_show" translate="label" type="select" sortOrder="70" showInDefault="1" showInWebsite="1" showInStore="0"> + <field id="dob_show" translate="label" type="select" sortOrder="70" showInDefault="1" showInWebsite="1" canRestore="1"> <label>Show Date of Birth</label> <source_model>Magento\Config\Model\Config\Source\Nooptreq</source_model> <backend_model>Magento\Customer\Model\Config\Backend\Show\Customer</backend_model> </field> - <field id="taxvat_show" translate="label" type="select" sortOrder="80" showInDefault="1" showInWebsite="1" showInStore="0"> + <field id="taxvat_show" translate="label" type="select" sortOrder="80" showInDefault="1" showInWebsite="1" canRestore="1"> <label>Show Tax/VAT Number</label> <source_model>Magento\Config\Model\Config\Source\Nooptreq</source_model> <backend_model>Magento\Customer\Model\Config\Backend\Show\Customer</backend_model> </field> - <field id="gender_show" translate="label" type="select" sortOrder="90" showInDefault="1" showInWebsite="1" showInStore="0"> + <field id="gender_show" translate="label" type="select" sortOrder="90" showInDefault="1" showInWebsite="1" canRestore="1"> <label>Show Gender</label> <source_model>Magento\Config\Model\Config\Source\Nooptreq</source_model> <backend_model>Magento\Customer\Model\Config\Backend\Show\Customer</backend_model> </field> - <field id="telephone_show" translate="label" type="select" sortOrder="100" showInDefault="1" showInWebsite="1" showInStore="0"> + <field id="telephone_show" translate="label" type="select" sortOrder="100" showInDefault="1" showInWebsite="1" canRestore="1"> <label>Show Telephone</label> <source_model>Magento\Config\Model\Config\Source\Nooptreq</source_model> <backend_model>Magento\Customer\Model\Config\Backend\Show\AddressOnly</backend_model> </field> - <field id="company_show" translate="label" type="select" sortOrder="110" showInDefault="1" showInWebsite="1" showInStore="0"> + <field id="company_show" translate="label" type="select" sortOrder="110" showInDefault="1" showInWebsite="1" canRestore="1"> <label>Show Company</label> <source_model>Magento\Config\Model\Config\Source\Nooptreq</source_model> <backend_model>Magento\Customer\Model\Config\Backend\Show\AddressOnly</backend_model> </field> - <field id="fax_show" translate="label" type="select" sortOrder="120" showInDefault="1" showInWebsite="1" showInStore="0"> + <field id="fax_show" translate="label" type="select" sortOrder="120" showInDefault="1" showInWebsite="1" canRestore="1"> <label>Show Fax</label> <source_model>Magento\Config\Model\Config\Source\Nooptreq</source_model> <backend_model>Magento\Customer\Model\Config\Backend\Show\AddressOnly</backend_model> @@ -264,7 +264,7 @@ </group> <group id="startup" translate="label" sortOrder="90" showInDefault="1" showInWebsite="1" showInStore="1"> <label>Login Options</label> - <field id="redirect_dashboard" translate="label comment" type="select" sortOrder="1" showInDefault="1" showInWebsite="1" showInStore="0" canRestore="1"> + <field id="redirect_dashboard" translate="label comment" type="select" sortOrder="1" showInDefault="1" showInWebsite="1" canRestore="1"> <label>Redirect Customer to Account Dashboard after Logging in</label> <source_model>Magento\Config\Model\Config\Source\Yesno</source_model> <comment>Customer will stay on the current page if "No" is selected.</comment> @@ -286,14 +286,14 @@ <label>PDF</label> </field> </group> - <group id="online_customers" translate="label" type="text" sortOrder="10" showInDefault="1" showInWebsite="0" showInStore="0"> + <group id="online_customers" translate="label" type="text" sortOrder="10" showInDefault="1"> <label>Online Customers Options</label> - <field id="online_minutes_interval" translate="label comment" type="text" sortOrder="1" showInDefault="1" showInWebsite="0" showInStore="0"> + <field id="online_minutes_interval" translate="label comment" type="text" sortOrder="1" showInDefault="1"> <label>Online Minutes Interval</label> <validate>validate-number validate-greater-than-zero</validate> <comment>Leave empty for default (15 minutes).</comment> </field> - <field id="section_data_lifetime" translate="label comment" type="text" sortOrder="1" showInDefault="1" showInWebsite="0" showInStore="0"> + <field id="section_data_lifetime" translate="label comment" type="text" sortOrder="1" showInDefault="1"> <label>Customer Data Lifetime</label> <validate>validate-number validate-greater-than-zero</validate> <comment>Please specify value in minutes.</comment> @@ -302,7 +302,7 @@ </section> <section id="general"> <group id="store_information"> - <field id="validate_vat_number" translate="button_label" sortOrder="62" showInDefault="1" showInWebsite="1" showInStore="0"> + <field id="validate_vat_number" translate="button_label" sortOrder="62" showInDefault="1" showInWebsite="1"> <button_label>Validate VAT Number</button_label> <frontend_model>Magento\Customer\Block\Adminhtml\System\Config\Validatevat</frontend_model> </field> diff --git a/app/code/Magento/Customer/etc/config.xml b/app/code/Magento/Customer/etc/config.xml index da4b80536e631..ab2020580a2eb 100644 --- a/app/code/Magento/Customer/etc/config.xml +++ b/app/code/Magento/Customer/etc/config.xml @@ -12,16 +12,19 @@ <scope>1</scope> </account_share> <create_account> + <auto_group_assign>0</auto_group_assign> <confirm>0</confirm> <default_group>1</default_group> <tax_calculation_address_type>billing</tax_calculation_address_type> - <email_domain>example.com</email_domain> <email_identity>general</email_identity> <email_template>customer_create_account_email_template</email_template> <email_no_password_template>customer_create_account_email_no_password_template</email_no_password_template> <email_confirmation_template>customer_create_account_email_confirmation_template</email_confirmation_template> <email_confirmed_template>customer_create_account_email_confirmed_template</email_confirmed_template> + <viv_disable_auto_group_assign_default>0</viv_disable_auto_group_assign_default> <vat_frontend_visibility>0</vat_frontend_visibility> + <email_domain>example.com</email_domain> + <generate_human_friendly_id>0</generate_human_friendly_id> </create_account> <default> <group>1</group> @@ -50,6 +53,7 @@ <suffix_show /> <suffix_options /> <dob_show /> + <taxvat_show /> <gender_show /> <telephone_show>req</telephone_show> <company_show>opt</company_show> diff --git a/app/code/Magento/Customer/etc/di.xml b/app/code/Magento/Customer/etc/di.xml index be219a81fd990..ba0bb3bac6a98 100644 --- a/app/code/Magento/Customer/etc/di.xml +++ b/app/code/Magento/Customer/etc/di.xml @@ -354,9 +354,14 @@ <type name="Magento\Directory\Model\AllowedCountries"> <plugin name="customerAllowedCountries" type="Magento\Customer\Model\Plugin\AllowedCountries"/> </type> - <type name="Magento\Framework\App\Action\AbstractAction"> + <type name="Magento\Framework\App\ActionInterface"> <plugin name="customerNotification" type="Magento\Customer\Model\Plugin\CustomerNotification"/> </type> + <type name="Magento\Customer\Model\Authorization\CustomerSessionUserContext"> + <arguments> + <argument name="customerSession" xsi:type="object">Magento\Customer\Model\Session\Proxy</argument> + </arguments> + </type> <type name="Magento\PageCache\Observer\FlushFormKey"> <plugin name="customerFlushFormKey" type="Magento\Customer\Model\Plugin\CustomerFlushFormKey"/> </type> diff --git a/app/code/Magento/Customer/etc/fieldset.xml b/app/code/Magento/Customer/etc/fieldset.xml index f89b653981520..ebd0fb2e57efe 100644 --- a/app/code/Magento/Customer/etc/fieldset.xml +++ b/app/code/Magento/Customer/etc/fieldset.xml @@ -57,23 +57,6 @@ <aspect name="update" /> </field> </fieldset> - <fieldset id="customer_address"> - <field name="vat_id"> - <aspect name="to_quote_address" /> - </field> - <field name="vat_is_valid"> - <aspect name="to_quote_address" /> - </field> - <field name="vat_request_id"> - <aspect name="to_quote_address" /> - </field> - <field name="vat_request_date"> - <aspect name="to_quote_address" /> - </field> - <field name="vat_request_success"> - <aspect name="to_quote_address" /> - </field> - </fieldset> <fieldset id="sales_convert_order_address"> <field name="vat_id"> <aspect name="to_quote_address" /> diff --git a/app/code/Magento/Customer/etc/frontend/di.xml b/app/code/Magento/Customer/etc/frontend/di.xml index 3b9675178c052..8867042cc6dc9 100644 --- a/app/code/Magento/Customer/etc/frontend/di.xml +++ b/app/code/Magento/Customer/etc/frontend/di.xml @@ -20,8 +20,8 @@ <plugin name="customer-session-depersonalize" type="Magento\Customer\Model\Layout\DepersonalizePlugin" sortOrder="10"/> </type> - <type name="Magento\Framework\App\Action\AbstractAction"> - <plugin name="customer-app-action-dispatchController-context-plugin" + <type name="Magento\Framework\App\ActionInterface"> + <plugin name="customer-app-action-executeController-context-plugin" type="Magento\Customer\Model\App\Action\ContextPlugin" sortOrder="10"/> </type> <preference for="Magento\Customer\CustomerData\SectionPoolInterface" @@ -51,7 +51,7 @@ </argument> </arguments> </type> - <type name="Magento\Customer\Controller\AbstractAccount"> + <type name="Magento\Customer\Controller\AccountInterface"> <plugin name="customer_account" type="Magento\Customer\Controller\Plugin\Account" /> </type> <type name="Magento\Checkout\Block\Cart\Sidebar"> diff --git a/app/code/Magento/Customer/etc/frontend/sections.xml b/app/code/Magento/Customer/etc/frontend/sections.xml index 7938f28590ca8..e6a45dfdeaa49 100644 --- a/app/code/Magento/Customer/etc/frontend/sections.xml +++ b/app/code/Magento/Customer/etc/frontend/sections.xml @@ -7,10 +7,18 @@ --> <config xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="urn:magento:module:Magento_Customer:etc/sections.xsd"> - <action name="customer/account/logout"/> - <action name="customer/account/loginPost"/> - <action name="customer/account/createPost"/> - <action name="customer/account/editPost"/> + <action name="customer/account/logout"> + <section name="*"/> + </action> + <action name="customer/account/loginPost"> + <section name="*"/> + </action> + <action name="customer/account/createPost"> + <section name="*"/> + </action> + <action name="customer/account/editPost"> + <section name="*"/> + </action> <action name="customer/ajax/login"> <section name="checkout-data"/> <section name="cart"/> diff --git a/app/code/Magento/Customer/i18n/en_US.csv b/app/code/Magento/Customer/i18n/en_US.csv index a70aa08dba735..bb6c4b10bf75e 100644 --- a/app/code/Magento/Customer/i18n/en_US.csv +++ b/app/code/Magento/Customer/i18n/en_US.csv @@ -432,7 +432,6 @@ Strong,Strong "Default Value for Disable Automatic Group Changes Based on VAT ID","Default Value for Disable Automatic Group Changes Based on VAT ID" "Show VAT Number on Storefront","Show VAT Number on Storefront" "To show VAT number on Storefront, set Show VAT Number on Storefront option to Yes.","To show VAT number on Storefront, set Show VAT Number on Storefront option to Yes." -"Default Email Domain","Default Email Domain" "Default Welcome Email","Default Welcome Email" "Email template chosen based on theme fallback when ""Default"" option is selected.","Email template chosen based on theme fallback when ""Default"" option is selected." "Default Welcome Email Without Password","Default Welcome Email Without Password" @@ -540,3 +539,5 @@ Addresses,Addresses "Middle Name/Initial","Middle Name/Initial" "Suffix","Suffix" "The Date of Birth should not be greater than today.","The Date of Birth should not be greater than today." +"The store view is not in the associated website.","The store view is not in the associated website." +"The Store View selected for sending Welcome email from is not related to the customer's associated website.","The Store View selected for sending Welcome email from is not related to the customer's associated website." diff --git a/app/code/Magento/Customer/registration.php b/app/code/Magento/Customer/registration.php index 744defc54ab27..4b88dbdab1d0e 100644 --- a/app/code/Magento/Customer/registration.php +++ b/app/code/Magento/Customer/registration.php @@ -4,6 +4,6 @@ * See COPYING.txt for license details. */ -use \Magento\Framework\Component\ComponentRegistrar; +use Magento\Framework\Component\ComponentRegistrar; ComponentRegistrar::register(ComponentRegistrar::MODULE, 'Magento_Customer', __DIR__); diff --git a/app/code/Magento/Customer/view/adminhtml/layout/customer_index_productreviews.xml b/app/code/Magento/Customer/view/adminhtml/layout/customer_index_productreviews.xml deleted file mode 100644 index dfbfcac04ac67..0000000000000 --- a/app/code/Magento/Customer/view/adminhtml/layout/customer_index_productreviews.xml +++ /dev/null @@ -1,12 +0,0 @@ -<?xml version="1.0"?> -<!-- -/** - * Copyright © Magento, Inc. All rights reserved. - * See COPYING.txt for license details. - */ ---> -<layout xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="urn:magento:framework:View/Layout/etc/layout_generic.xsd"> - <container name="root" label="Root"> - <block class="Magento\Customer\Block\Adminhtml\Edit\Tab\Reviews" name="admin.customer.reviews"/> - </container> -</layout> diff --git a/app/code/Magento/Customer/view/adminhtml/templates/tab/cart_website_filter_form.phtml b/app/code/Magento/Customer/view/adminhtml/templates/tab/cart_website_filter_form.phtml new file mode 100644 index 0000000000000..ec903fa978fce --- /dev/null +++ b/app/code/Magento/Customer/view/adminhtml/templates/tab/cart_website_filter_form.phtml @@ -0,0 +1,10 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ + +/** @var $block \Magento\Backend\Block\Widget\Form */ +?> +<?= $block->getFormHtml() ?> +<?= $block->getChildHtml('form_after') ?> diff --git a/app/code/Magento/Customer/view/adminhtml/ui_component/customer_address_form.xml b/app/code/Magento/Customer/view/adminhtml/ui_component/customer_address_form.xml index 3af0172b3fca8..517d8151b5f10 100644 --- a/app/code/Magento/Customer/view/adminhtml/ui_component/customer_address_form.xml +++ b/app/code/Magento/Customer/view/adminhtml/ui_component/customer_address_form.xml @@ -174,17 +174,7 @@ <label translate="true">Company</label> </settings> </field> - <field name="city" sortOrder="80" formElement="input"> - <settings> - <dataType>text</dataType> - <label translate="true">City</label> - <visible>true</visible> - <validation> - <rule name="required-entry" xsi:type="boolean">true</rule> - </validation> - </settings> - </field> - <field name="country_id" component="Magento_Customer/js/form/element/country" sortOrder="90" formElement="select"> + <field name="country_id" component="Magento_Customer/js/form/element/country" sortOrder="80" formElement="select"> <settings> <validation> <rule name="required-entry" xsi:type="boolean">true</rule> @@ -212,6 +202,16 @@ </select> </formElements> </field> + <field name="city" sortOrder="100" formElement="input"> + <settings> + <dataType>text</dataType> + <label translate="true">City</label> + <visible>true</visible> + <validation> + <rule name="required-entry" xsi:type="boolean">true</rule> + </validation> + </settings> + </field> <field name="postcode" component="Magento_Ui/js/form/element/post-code" sortOrder="120" formElement="input"> <settings> <dataType>text</dataType> diff --git a/app/code/Magento/Customer/view/adminhtml/web/js/form/element/website.js b/app/code/Magento/Customer/view/adminhtml/web/js/form/element/website.js new file mode 100644 index 0000000000000..7c2f6489a51d3 --- /dev/null +++ b/app/code/Magento/Customer/view/adminhtml/web/js/form/element/website.js @@ -0,0 +1,30 @@ +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ + +define([ + 'Magento_Ui/js/form/element/website', + 'uiRegistry' +], function (Website, registry) { + 'use strict'; + + return Website.extend({ + /** + * On value change handler. + * + * @param {String} value + */ + onUpdate: function (value) { + var groupIdFieldKey = 'group_id', + groupId = registry.get('index = ' + groupIdFieldKey), + option = this.getOption(value); + + if (groupId) { + groupId.value(option[groupIdFieldKey]); + } + + return this._super(); + } + }); +}); diff --git a/app/code/Magento/Customer/view/adminhtml/web/template/default-address.html b/app/code/Magento/Customer/view/adminhtml/web/template/default-address.html index 8d68b93445b85..6dd154726010c 100644 --- a/app/code/Magento/Customer/view/adminhtml/web/template/default-address.html +++ b/app/code/Magento/Customer/view/adminhtml/web/template/default-address.html @@ -1,3 +1,9 @@ +<!-- +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +--> <div data-bind="attr: {class: contentClass}"> <div data-bind="attr: {class: 'fieldset-wrapper address-information ' + defaultAddressClass}"> <address> @@ -20,7 +26,7 @@ </if> <if args="address.street"> <text args="address.street" if="_.isString(address.street)"/> - <text args="_.values(address.street).filter(value => _.isString(value)).join(', ')" + <text args="_.filter(_.values(address.street), function (value) { return _.isString(value)}).join(', ')" ifnot="_.isString(address.street)"/> <br/> </if> diff --git a/app/code/Magento/Customer/view/base/ui_component/customer_form.xml b/app/code/Magento/Customer/view/base/ui_component/customer_form.xml index 7caaeab4f39d6..b9487037da2cc 100644 --- a/app/code/Magento/Customer/view/base/ui_component/customer_form.xml +++ b/app/code/Magento/Customer/view/base/ui_component/customer_form.xml @@ -117,7 +117,7 @@ <visible>false</visible> </settings> </field> - <field name="website_id" component="Magento_Ui/js/form/element/website" formElement="select"> + <field name="website_id" component="Magento_Customer/js/form/element/website" formElement="select"> <argument name="data" xsi:type="array"> <item name="config" xsi:type="array"> <item name="source" xsi:type="string">customer</item> @@ -136,6 +136,13 @@ <link name="customerId">${ $.provider }:data.customer.entity_id</link> </imports> </settings> + <formElements> + <select> + <settings> + <options class="Magento\Customer\ViewModel\Customer\Website"/> + </settings> + </select> + </formElements> </field> <field name="prefix" formElement="input"> <argument name="data" xsi:type="array"> @@ -301,7 +308,7 @@ <visible>true</visible> </settings> </field> - <field name="sendemail_store_id" formElement="select"> + <field name="sendemail_store_id" component="Magento_Ui/js/form/element/select" formElement="select"> <argument name="data" xsi:type="array"> <item name="config" xsi:type="array"> <item name="source" xsi:type="string">customer</item> @@ -317,7 +324,11 @@ <formElements> <select> <settings> - <options class="Magento\Store\Model\System\Store"/> + <options class="Magento\Customer\ViewModel\Customer\Store"/> + <filterBy> + <field>website_id</field> + <target>${ $.provider }:${ $.parentScope }.website_id</target> + </filterBy> </settings> </select> </formElements> diff --git a/app/code/Magento/Customer/view/frontend/layout/customer_address_form.xml b/app/code/Magento/Customer/view/frontend/layout/customer_address_form.xml index f5ee2b347a5b2..29343c6066846 100644 --- a/app/code/Magento/Customer/view/frontend/layout/customer_address_form.xml +++ b/app/code/Magento/Customer/view/frontend/layout/customer_address_form.xml @@ -21,6 +21,7 @@ <arguments> <argument name="attribute_data" xsi:type="object">Magento\Customer\Block\DataProviders\AddressAttributeData</argument> <argument name="post_code_config" xsi:type="object">Magento\Customer\Block\DataProviders\PostCodesPatternsAttributeData</argument> + <argument name="view_model" xsi:type="object">Magento\Customer\ViewModel\Address</argument> </arguments> </block> </referenceContainer> diff --git a/app/code/Magento/Customer/view/frontend/templates/address/edit.phtml b/app/code/Magento/Customer/view/frontend/templates/address/edit.phtml index cf5b60568ad05..e7519c7c3320b 100644 --- a/app/code/Magento/Customer/view/frontend/templates/address/edit.phtml +++ b/app/code/Magento/Customer/view/frontend/templates/address/edit.phtml @@ -5,63 +5,77 @@ */ /** @var \Magento\Customer\Block\Address\Edit $block */ +/** @var \Magento\Customer\ViewModel\Address $viewModel */ +/** @var \Magento\Framework\Escaper $escaper */ +$viewModel = $block->getViewModel(); ?> <?php $_company = $block->getLayout()->createBlock(\Magento\Customer\Block\Widget\Company::class) ?> <?php $_telephone = $block->getLayout()->createBlock(\Magento\Customer\Block\Widget\Telephone::class) ?> <?php $_fax = $block->getLayout()->createBlock(\Magento\Customer\Block\Widget\Fax::class) ?> +<?php $_country_id = $block->getAttributeData()->getFrontendLabel('country_id'); ?> +<?php $_street = $block->getAttributeData()->getFrontendLabel('street'); ?> +<?php $_city = $block->getAttributeData()->getFrontendLabel('city'); ?> +<?php $_region = $block->getAttributeData()->getFrontendLabel('region'); ?> +<?php $_selectRegion = 'Please select a region, state or province.'; ?> +<?php $_displayAll = $block->getConfig('general/region/display_all'); ?> + +<?php $_vatidValidationClass = $viewModel->addressGetAttributeValidationClass('vat_id'); ?> +<?php $_cityValidationClass = $viewModel->addressGetAttributeValidationClass('city'); ?> +<?php $_postcodeValidationClass_value = $viewModel->addressGetAttributeValidationClass('postcode'); ?> +<?php $_postcodeValidationClass = $_postcodeValidationClass_value; ?> +<?php $_streetValidationClass = $viewModel->addressGetAttributeValidationClass('street'); ?> +<?php $_streetValidationClassNotRequired = trim(str_replace('required-entry', '', $_streetValidationClass)); ?> +<?php $_regionValidationClass = $viewModel->addressGetAttributeValidationClass('region'); ?> <form class="form-address-edit" - action="<?= $block->escapeUrl($block->getSaveUrl()) ?>" + action="<?= $escaper->escapeUrl($block->getSaveUrl()) ?>" method="post" id="form-validate" enctype="multipart/form-data" - data-hasrequired="<?= $block->escapeHtmlAttr(__('* Required Fields')) ?>"> + data-hasrequired="<?= $escaper->escapeHtmlAttr(__('* Required Fields')) ?>"> <fieldset class="fieldset"> - <legend class="legend"><span><?= $block->escapeHtml(__('Contact Information')) ?></span></legend><br> + <legend class="legend"><span><?= $escaper->escapeHtml(__('Contact Information')) ?></span></legend><br> <?= $block->getBlockHtml('formkey') ?> - <input type="hidden" name="success_url" value="<?= $block->escapeUrl($block->getSuccessUrl()) ?>"> - <input type="hidden" name="error_url" value="<?= $block->escapeUrl($block->getErrorUrl()) ?>"> + <input type="hidden" name="success_url" value="<?= $escaper->escapeUrl($block->getSuccessUrl()) ?>"> + <input type="hidden" name="error_url" value="<?= $escaper->escapeUrl($block->getErrorUrl()) ?>"> <?= $block->getNameBlockHtml() ?> - <?php if ($_company->isEnabled()) : ?> + <?php if ($_company->isEnabled()): ?> <?= $_company->setCompany($block->getAddress()->getCompany())->toHtml() ?> <?php endif ?> - <?php if ($_telephone->isEnabled()) : ?> + <?php if ($_telephone->isEnabled()): ?> <?= $_telephone->setTelephone($block->getAddress()->getTelephone())->toHtml() ?> <?php endif ?> - <?php if ($_fax->isEnabled()) : ?> + <?php if ($_fax->isEnabled()): ?> <?= $_fax->setFax($block->getAddress()->getFax())->toHtml() ?> <?php endif ?> </fieldset> <fieldset class="fieldset"> - <legend class="legend"><span><?= $block->escapeHtml(__('Address')) ?></span></legend><br> - <?php $_streetValidationClass = $this->helper(\Magento\Customer\Helper\Address::class)->getAttributeValidationClass('street'); ?> + <legend class="legend"><span><?= $escaper->escapeHtml(__('Address')) ?></span></legend><br> <div class="field street required"> - <label for="street_1" class="label"> - <span><?= /* @noEscape */ $block->getAttributeData()->getFrontendLabel('street') ?></span> - </label> + <label for="street_1" class="label"><span><?= /* @noEscape */ $_street ?></span></label> <div class="control"> <input type="text" name="street[]" - value="<?= $block->escapeHtmlAttr($block->getStreetLine(1)) ?>" - title="<?= /* @noEscape */ $block->getAttributeData()->getFrontendLabel('street') ?>" + value="<?= $escaper->escapeHtmlAttr($block->getStreetLine(1)) ?>" + title="<?= /* @noEscape */ $_street ?>" id="street_1" - class="input-text <?= $block->escapeHtmlAttr($_streetValidationClass) ?>"/> + class="input-text <?= $escaper->escapeHtmlAttr($_streetValidationClass) ?>"/> <div class="nested"> - <?php $_streetValidationClass = trim(str_replace('required-entry', '', $_streetValidationClass)); ?> - <?php for ($_i = 1, $_n = $this->helper(\Magento\Customer\Helper\Address::class)->getStreetLines(); $_i < $_n; $_i++) : ?> + <?php for ($_i = 1, $_n = $viewModel->addressGetStreetLines(); $_i < $_n; $_i++): ?> <div class="field additional"> <label class="label" for="street_<?= /* @noEscape */ $_i + 1 ?>"> - <span><?= $block->escapeHtml(__('Street Address %1', $_i + 1)) ?></span> + <span><?= $escaper->escapeHtml(__('Street Address %1', $_i + 1)) ?></span> </label> <div class="control"> <input type="text" name="street[]" - value="<?= $block->escapeHtmlAttr($block->getStreetLine($_i + 1)) ?>" - title="<?= $block->escapeHtmlAttr(__('Street Address %1', $_i + 1)) ?>" + value="<?= $escaper->escapeHtmlAttr($block->getStreetLine($_i + 1)) ?>" + title="<?= $escaper->escapeHtmlAttr(__('Street Address %1', $_i + 1)) ?>" id="street_<?= /* @noEscape */ $_i + 1 ?>" - class="input-text <?= $block->escapeHtmlAttr($_streetValidationClass) ?>"> + class="input-text + <?= $escaper->escapeHtmlAttr($_streetValidationClassNotRequired) ?>"> </div> </div> <?php endfor; ?> @@ -69,7 +83,7 @@ </div> </div> - <?php if ($this->helper(\Magento\Customer\Helper\Address::class)->isVatAttributeVisible()) : ?> + <?php if ($viewModel->addressIsVatAttributeVisible()): ?> <div class="field taxvat"> <label class="label" for="vat_id"> <span><?= /* @noEscape */ $block->getAttributeData()->getFrontendLabel('vat_id') ?></span> @@ -77,40 +91,53 @@ <div class="control"> <input type="text" name="vat_id" - value="<?= $block->escapeHtmlAttr($block->getAddress()->getVatId()) ?>" + value="<?= $escaper->escapeHtmlAttr($block->getAddress()->getVatId()) ?>" title="<?= /* @noEscape */ $block->getAttributeData()->getFrontendLabel('vat_id') ?>" - class="input-text <?= $block->escapeHtmlAttr($this->helper(\Magento\Customer\Helper\Address::class)->getAttributeValidationClass('vat_id')) ?>" + class="input-text <?= $escaper->escapeHtmlAttr($_vatidValidationClass) ?>" id="vat_id"> </div> </div> <?php endif; ?> - <div class="field city required"> - <label class="label" for="city"><span><?= /* @noEscape */ $block->getAttributeData()->getFrontendLabel('city') ?></span></label> + <div class="field country required"> + <label class="label" for="country"> + <span><?= /* @noEscape */ $block->getAttributeData()->getFrontendLabel('country_id') ?></span> + </label> <div class="control"> - <input type="text" - name="city" - value="<?= $block->escapeHtmlAttr($block->getAddress()->getCity()) ?>" - title="<?= $block->escapeHtmlAttr(__('City')) ?>" - class="input-text <?= $block->escapeHtmlAttr($this->helper(\Magento\Customer\Helper\Address::class)->getAttributeValidationClass('city')) ?>" - id="city"> + <?= $block->getCountryHtmlSelect() ?> </div> </div> <div class="field region required"> <label class="label" for="region_id"> - <span><?= /* @noEscape */ $block->getAttributeData()->getFrontendLabel('region') ?></span> + <span><?= /* @noEscape */ $_region ?></span> </label> <div class="control"> <select id="region_id" name="region_id" - title="<?= /* @noEscape */ $block->getAttributeData()->getFrontendLabel('region') ?>" - class="validate-select region_id" <?= /* @noEscape */ !$block->getConfig('general/region/display_all') ? ' disabled="disabled"' : '' ?>> - <option value=""><?= $block->escapeHtml(__('Please select a region, state or province.')) ?></option> + title="<?= /* @noEscape */ $_region ?>" + class="validate-select region_id" + <?= /* @noEscape */ !$_displayAll ? ' disabled="disabled"' : '' ?>> + <option value=""><?= $escaper->escapeHtml(__($_selectRegion)) ?></option> </select> <input type="text" id="region" name="region" - value="<?= $block->escapeHtmlAttr($block->getRegion()) ?>" - title="<?= /* @noEscape */ $block->getAttributeData()->getFrontendLabel('region') ?>" - class="input-text validate-not-number-first <?= $block->escapeHtmlAttr($this->helper(\Magento\Customer\Helper\Address::class)->getAttributeValidationClass('region')) ?>"<?= !$block->getConfig('general/region/display_all') ? ' disabled="disabled"' : '' ?>/> + value="<?= $escaper->escapeHtmlAttr($block->getRegion()) ?>" + title="<?= /* @noEscape */ $_region ?>" + class="input-text validate-not-number-first + <?= $escaper->escapeHtmlAttr($_regionValidationClass) ?>" + <?= !$_displayAll ? ' disabled="disabled"' : '' ?>/> + </div> + </div> + <div class="field city required"> + <label class="label" for="city"> + <span><?= /* @noEscape */ $block->getAttributeData()->getFrontendLabel('city') ?></span> + </label> + <div class="control"> + <input type="text" + name="city" + value="<?= $escaper->escapeHtmlAttr($block->getAddress()->getCity()) ?>" + title="<?= $escaper->escapeHtmlAttr(__('City')) ?>" + class="input-text <?= $escaper->escapeHtmlAttr($_cityValidationClass) ?>" + id="city"> </div> </div> <div class="field zip required"> @@ -120,49 +147,44 @@ <div class="control"> <input type="text" name="postcode" - value="<?= $block->escapeHtmlAttr($block->getAddress()->getPostcode()) ?>" + value="<?= $escaper->escapeHtmlAttr($block->getAddress()->getPostcode()) ?>" title="<?= /* @noEscape */ $block->getAttributeData()->getFrontendLabel('postcode') ?>" id="zip" - class="input-text validate-zip-international <?= $block->escapeHtmlAttr($this->helper(\Magento\Customer\Helper\Address::class)->getAttributeValidationClass('postcode')) ?>"> + class="input-text validate-zip-international + <?= $escaper->escapeHtmlAttr($_postcodeValidationClass) ?>"> <div role="alert" class="message warning" style="display:none"> <span></span> </div> </div> </div> - <div class="field country required"> - <label class="label" for="country"><span><?= /* @noEscape */ $block->getAttributeData()->getFrontendLabel('country_id') ?></span></label> - <div class="control"> - <?= $block->getCountryHtmlSelect() ?> - </div> - </div> - <?php if ($block->isDefaultBilling()) : ?> + <?php if ($block->isDefaultBilling()): ?> <div class="message info"> - <span><?= $block->escapeHtml(__("It's a default billing address.")) ?></span> + <span><?= $escaper->escapeHtml(__("It's a default billing address.")) ?></span> </div> - <?php elseif ($block->canSetAsDefaultBilling()) : ?> + <?php elseif ($block->canSetAsDefaultBilling()): ?> <div class="field choice set billing"> <input type="checkbox" id="primary_billing" name="default_billing" value="1" class="checkbox"> <label class="label" for="primary_billing"> - <span><?= $block->escapeHtml(__('Use as my default billing address')) ?></span> + <span><?= $escaper->escapeHtml(__('Use as my default billing address')) ?></span> </label> </div> - <?php else : ?> + <?php else: ?> <input type="hidden" name="default_billing" value="1" /> <?php endif; ?> - <?php if ($block->isDefaultShipping()) : ?> + <?php if ($block->isDefaultShipping()): ?> <div class="message info"> - <span><?= $block->escapeHtml(__("It's a default shipping address.")) ?></span> + <span><?= $escaper->escapeHtml(__("It's a default shipping address.")) ?></span> </div> - <?php elseif ($block->canSetAsDefaultShipping()) : ?> + <?php elseif ($block->canSetAsDefaultShipping()): ?> <div class="field choice set shipping"> <input type="checkbox" id="primary_shipping" name="default_shipping" value="1" class="checkbox"> <label class="label" for="primary_shipping"> - <span><?= $block->escapeHtml(__('Use as my default shipping address')) ?></span> + <span><?= $escaper->escapeHtml(__('Use as my default shipping address')) ?></span> </label> </div> - <?php else : ?> + <?php else: ?> <input type="hidden" name="default_shipping" value="1"> <?php endif; ?> </fieldset> @@ -171,13 +193,13 @@ <button type="submit" class="action save primary" data-action="save-address" - title="<?= $block->escapeHtmlAttr(__('Save Address')) ?>"> - <span><?= $block->escapeHtml(__('Save Address')) ?></span> + title="<?= $escaper->escapeHtmlAttr(__('Save Address')) ?>"> + <span><?= $escaper->escapeHtml(__('Save Address')) ?></span> </button> </div> <div class="secondary"> - <a class="action back" href="<?= $block->escapeUrl($block->getBackUrl()) ?>"> - <span><?= $block->escapeHtml(__('Go back')) ?></span> + <a class="action back" href="<?= $escaper->escapeUrl($block->getBackUrl()) ?>"> + <span><?= $escaper->escapeHtml(__('Go back')) ?></span> </a> </div> </div> @@ -186,19 +208,19 @@ { "#form-validate": { "addressValidation": { - "postCodes": <?= /* @noEscape */ $block->getPostCodeConfig()->getSerializedPostCodes(); ?> + "postCodes": <?= /* @noEscape */ $block->getPostCodeConfig()->getSerializedPostCodes() ?> } }, "#country": { "regionUpdater": { - "optionalRegionAllowed": <?= /* @noEscape */ $block->getConfig('general/region/display_all') ? 'true' : 'false' ?>, + "optionalRegionAllowed": <?= /* @noEscape */ $_displayAll ? 'true' : 'false' ?>, "regionListId": "#region_id", "regionInputId": "#region", "postcodeId": "#zip", "form": "#form-validate", - "regionJson": <?= /* @noEscape */ $this->helper(\Magento\Directory\Helper\Data::class)->getRegionJson() ?>, + "regionJson": <?= /* @noEscape */ $viewModel->dataGetRegionJson() ?>, "defaultRegion": "<?= (int) $block->getRegionId() ?>", - "countriesWithOptionalZip": <?= /* @noEscape */ $this->helper(\Magento\Directory\Helper\Data::class)->getCountriesWithOptionalZip(true) ?> + "countriesWithOptionalZip": <?= /* @noEscape */ $viewModel->dataGetCountriesWithOptionalZip(true) ?> } } } diff --git a/app/code/Magento/Customer/view/frontend/templates/form/edit.phtml b/app/code/Magento/Customer/view/frontend/templates/form/edit.phtml index e2b6792439576..89b86f8af8e55 100644 --- a/app/code/Magento/Customer/view/frontend/templates/form/edit.phtml +++ b/app/code/Magento/Customer/view/frontend/templates/form/edit.phtml @@ -4,56 +4,81 @@ * See COPYING.txt for license details. */ +use Magento\Customer\Block\Widget\Name; + /** @var \Magento\Customer\Block\Form\Edit $block */ ?> -<form class="form form-edit-account" action="<?= $block->escapeUrl($block->getUrl('customer/account/editPost')) ?>" method="post" id="form-validate" enctype="multipart/form-data" data-hasrequired="<?= $block->escapeHtmlAttr(__('* Required Fields')) ?>" autocomplete="off"> +<form class="form form-edit-account" + action="<?= $block->escapeUrl($block->getUrl('customer/account/editPost')) ?>" + method="post" id="form-validate" + enctype="multipart/form-data" + data-hasrequired="<?= $block->escapeHtmlAttr(__('* Required Fields')) ?>" + autocomplete="off"> <fieldset class="fieldset info"> <?= $block->getBlockHtml('formkey') ?> <legend class="legend"><span><?= $block->escapeHtml(__('Account Information')) ?></span></legend><br> - <?= $block->getLayout()->createBlock(\Magento\Customer\Block\Widget\Name::class)->setObject($block->getCustomer())->toHtml() ?> + <?= $block->getLayout()->createBlock(Name::class)->setObject($block->getCustomer())->toHtml() ?> <?php $_dob = $block->getLayout()->createBlock(\Magento\Customer\Block\Widget\Dob::class) ?> <?php $_taxvat = $block->getLayout()->createBlock(\Magento\Customer\Block\Widget\Taxvat::class) ?> <?php $_gender = $block->getLayout()->createBlock(\Magento\Customer\Block\Widget\Gender::class) ?> - <?php if ($_dob->isEnabled()) : ?> + <?php if ($_dob->isEnabled()): ?> <?= $_dob->setDate($block->getCustomer()->getDob())->toHtml() ?> <?php endif ?> - <?php if ($_taxvat->isEnabled()) : ?> + <?php if ($_taxvat->isEnabled()): ?> <?= $_taxvat->setTaxvat($block->getCustomer()->getTaxvat())->toHtml() ?> <?php endif ?> - <?php if ($_gender->isEnabled()) : ?> + <?php if ($_gender->isEnabled()): ?> <?= $_gender->setGender($block->getCustomer()->getGender())->toHtml() ?> <?php endif ?> <div class="field choice"> - <input type="checkbox" name="change_email" id="change-email" data-role="change-email" value="1" title="<?= $block->escapeHtmlAttr(__('Change Email')) ?>" class="checkbox" /> - <label class="label" for="change-email"><span><?= $block->escapeHtml(__('Change Email')) ?></span></label> + <input type="checkbox" name="change_email" id="change-email" data-role="change-email" value="1" + title="<?= $block->escapeHtmlAttr(__('Change Email')) ?>" class="checkbox" /> + <label class="label" for="change-email"> + <span><?= $block->escapeHtml(__('Change Email')) ?></span> + </label> </div> <div class="field choice"> - <input type="checkbox" name="change_password" id="change-password" data-role="change-password" value="1" title="<?= $block->escapeHtmlAttr(__('Change Password')) ?>"<?php if ($block->getChangePassword()) : ?> checked="checked"<?php endif; ?> class="checkbox" /> - <label class="label" for="change-password"><span><?= $block->escapeHtml(__('Change Password')) ?></span></label> + <input type="checkbox" name="change_password" id="change-password" data-role="change-password" value="1" + title="<?= $block->escapeHtmlAttr(__('Change Password')) ?>" + <?php if ($block->getChangePassword()): ?> checked="checked"<?php endif; ?> class="checkbox" /> + <label class="label" for="change-password"> + <span><?= $block->escapeHtml(__('Change Password')) ?></span> + </label> </div> </fieldset> <fieldset class="fieldset password" data-container="change-email-password"> - <legend class="legend"><span data-title="change-email-password"><?= $block->escapeHtml(__('Change Email and Password')) ?></span></legend><br> + <legend class="legend"> + <span data-title="change-email-password"><?= $block->escapeHtml(__('Change Email and Password')) ?></span> + </legend><br> <div class="field email required" data-container="change-email"> <label class="label" for="email"><span><?= $block->escapeHtml(__('Email')) ?></span></label> <div class="control"> - <input type="email" name="email" id="email" autocomplete="email" data-input="change-email" value="<?= $block->escapeHtmlAttr($block->getCustomer()->getEmail()) ?>" title="<?= $block->escapeHtmlAttr(__('Email')) ?>" class="input-text" data-validate="{required:true, 'validate-email':true}" /> + <input type="email" name="email" id="email" autocomplete="email" data-input="change-email" + value="<?= $block->escapeHtmlAttr($block->getCustomer()->getEmail()) ?>" + title="<?= $block->escapeHtmlAttr(__('Email')) ?>" + class="input-text" + data-validate="{required:true, 'validate-email':true}" /> </div> </div> <div class="field password current required"> - <label class="label" for="current-password"><span><?= $block->escapeHtml(__('Current Password')) ?></span></label> + <label class="label" for="current-password"> + <span><?= $block->escapeHtml(__('Current Password')) ?></span> + </label> <div class="control"> - <input type="password" class="input-text" name="current_password" id="current-password" data-input="current-password" autocomplete="off" /> + <input type="password" class="input-text" name="current_password" id="current-password" + data-input="current-password" + autocomplete="off" /> </div> </div> <div class="field new password required" data-container="new-password"> <label class="label" for="password"><span><?= $block->escapeHtml(__('New Password')) ?></span></label> <div class="control"> + <?php $minCharacterSets = $block->getRequiredCharacterClassesNumber() ?> <input type="password" class="input-text" name="password" id="password" data-password-min-length="<?= $block->escapeHtml($block->getMinimumPasswordLength()) ?>" - data-password-min-character-sets="<?= $block->escapeHtml($block->getRequiredCharacterClassesNumber()) ?>" + data-password-min-character-sets="<?= $block->escapeHtml($minCharacterSets) ?>" data-input="new-password" data-validate="{required:true, 'validate-customer-password':true}" autocomplete="off" /> @@ -68,7 +93,9 @@ </div> </div> <div class="field confirmation password required" data-container="confirm-password"> - <label class="label" for="password-confirmation"><span><?= $block->escapeHtml(__('Confirm New Password')) ?></span></label> + <label class="label" for="password-confirmation"> + <span><?= $block->escapeHtml(__('Confirm New Password')) ?></span> + </label> <div class="control"> <input type="password" class="input-text" name="password_confirmation" id="password-confirmation" data-input="confirm-password" @@ -79,10 +106,14 @@ <?= $block->getChildHtml('form_additional_info') ?> <div class="actions-toolbar"> <div class="primary"> - <button type="submit" class="action save primary" title="<?= $block->escapeHtmlAttr(__('Save')) ?>"><span><?= $block->escapeHtml(__('Save')) ?></span></button> + <button type="submit" class="action save primary" title="<?= $block->escapeHtmlAttr(__('Save')) ?>"> + <span><?= $block->escapeHtml(__('Save')) ?></span> + </button> </div> <div class="secondary"> - <a class="action back" href="<?= $block->escapeUrl($block->getBackUrl()) ?>"><span><?= $block->escapeHtml(__('Go back')) ?></span></a> + <a class="action back" href="<?= $block->escapeUrl($block->getBackUrl()) ?>"> + <span><?= $block->escapeHtml(__('Go back')) ?></span> + </a> </div> </div> </form> @@ -95,7 +126,7 @@ var ignore = <?= /* @noEscape */ $_dob->isEnabled() ? '\'input[id$="full"]\'' : 'null' ?>; dataForm.mage('validation', { - <?php if ($_dob->isEnabled()) : ?> + <?php if ($_dob->isEnabled()): ?> errorPlacement: function(error, element) { if (element.prop('id').search('full') !== -1) { var dobElement = $(element).parents('.customer-dob'), @@ -109,20 +140,21 @@ } }, ignore: ':hidden:not(' + ignore + ')' - <?php else : ?> + <?php else: ?> ignore: ignore ? ':hidden:not(' + ignore + ')' : ':hidden' <?php endif ?> }); }); </script> +<?php $changeEmailAndPasswordTitle = $block->escapeHtml(__('Change Email and Password')) ?> <script type="text/x-magento-init"> { "[data-role=change-email], [data-role=change-password]": { "changeEmailPassword": { "titleChangeEmail": "<?= $block->escapeJs($block->escapeHtml(__('Change Email'))) ?>", "titleChangePassword": "<?= $block->escapeJs($block->escapeHtml(__('Change Password'))) ?>", - "titleChangeEmailAndPassword": "<?= $block->escapeJs($block->escapeHtml(__('Change Email and Password'))) ?>" + "titleChangeEmailAndPassword": "<?= $block->escapeJs($changeEmailAndPasswordTitle) ?>" } }, "[data-container=new-password]": { diff --git a/app/code/Magento/Customer/view/frontend/templates/form/register.phtml b/app/code/Magento/Customer/view/frontend/templates/form/register.phtml index da0bb6e4cbc8b..f7d10f6df1728 100644 --- a/app/code/Magento/Customer/view/frontend/templates/form/register.phtml +++ b/app/code/Magento/Customer/view/frontend/templates/form/register.phtml @@ -3,78 +3,123 @@ * Copyright © Magento, Inc. All rights reserved. * See COPYING.txt for license details. */ +// phpcs:disable Magento2.Templates.ThisInTemplate.FoundHelper +// phpcs:disable Magento2.Templates.ThisInTemplate.FoundThis + +use Magento\Customer\Helper\Address; /** @var \Magento\Customer\Block\Form\Register $block */ +/** @var \Magento\Framework\Escaper $escaper */ +$formData = $block->getFormData(); ?> +<?php $displayAll = $block->getConfig('general/region/display_all'); ?> <?= $block->getChildHtml('form_fields_before') ?> <?php /* Extensions placeholder */ ?> <?= $block->getChildHtml('customer.form.register.extra') ?> -<form class="form create account form-create-account" action="<?= $block->escapeUrl($block->getPostActionUrl()) ?>" method="post" id="form-validate" enctype="multipart/form-data" autocomplete="off"> - <?= /* @noEscape */ $block->getBlockHtml('formkey'); ?> +<form class="form create account form-create-account" + action="<?= $escaper->escapeUrl($block->getPostActionUrl()) ?>" + method="post" + id="form-validate" + enctype="multipart/form-data" + autocomplete="off"> + <?= /* @noEscape */ $block->getBlockHtml('formkey') ?> <fieldset class="fieldset create info"> - <legend class="legend"><span><?= $block->escapeHtml(__('Personal Information')) ?></span></legend><br> - <input type="hidden" name="success_url" value="<?= $block->escapeUrl($block->getSuccessUrl()) ?>"> - <input type="hidden" name="error_url" value="<?= $block->escapeUrl($block->getErrorUrl()) ?>"> - <?= $block->getLayout()->createBlock(\Magento\Customer\Block\Widget\Name::class)->setObject($block->getFormData())->setForceUseCustomerAttributes(true)->toHtml() ?> - <?php if ($block->isNewsletterEnabled()) : ?> + <legend class="legend"><span><?= $escaper->escapeHtml(__('Personal Information')) ?></span></legend><br> + <input type="hidden" name="success_url" value="<?= $escaper->escapeUrl($block->getSuccessUrl()) ?>"> + <input type="hidden" name="error_url" value="<?= $escaper->escapeUrl($block->getErrorUrl()) ?>"> + <?= $block->getLayout() + ->createBlock(\Magento\Customer\Block\Widget\Name::class) + ->setObject($formData) + ->setForceUseCustomerAttributes(true) + ->toHtml() ?> + <?php if ($block->isNewsletterEnabled()): ?> <div class="field choice newsletter"> - <input type="checkbox" name="is_subscribed" title="<?= $block->escapeHtmlAttr(__('Sign Up for Newsletter')) ?>" value="1" id="is_subscribed"<?php if ($block->getFormData()->getIsSubscribed()) : ?> checked="checked"<?php endif; ?> class="checkbox"> - <label for="is_subscribed" class="label"><span><?= $block->escapeHtml(__('Sign Up for Newsletter')) ?></span></label> + <input type="checkbox" + name="is_subscribed" + title="<?= $escaper->escapeHtmlAttr(__('Sign Up for Newsletter')) ?>" + value="1" + id="is_subscribed" + <?php if ($formData->getIsSubscribed()): ?>checked="checked"<?php endif; ?> + class="checkbox"> + <label for="is_subscribed" class="label"> + <span><?= $escaper->escapeHtml(__('Sign Up for Newsletter')) ?></span> + </label> </div> <?php /* Extensions placeholder */ ?> <?= $block->getChildHtml('customer.form.register.newsletter') ?> <?php endif ?> <?php $_dob = $block->getLayout()->createBlock(\Magento\Customer\Block\Widget\Dob::class) ?> - <?php if ($_dob->isEnabled()) : ?> - <?= $_dob->setDate($block->getFormData()->getDob())->toHtml() ?> + <?php if ($_dob->isEnabled()): ?> + <?= $_dob->setDate($formData->getDob())->toHtml() ?> <?php endif ?> <?php $_taxvat = $block->getLayout()->createBlock(\Magento\Customer\Block\Widget\Taxvat::class) ?> - <?php if ($_taxvat->isEnabled()) : ?> - <?= $_taxvat->setTaxvat($block->getFormData()->getTaxvat())->toHtml() ?> + <?php if ($_taxvat->isEnabled()): ?> + <?= $_taxvat->setTaxvat($formData->getTaxvat())->toHtml() ?> <?php endif ?> <?php $_gender = $block->getLayout()->createBlock(\Magento\Customer\Block\Widget\Gender::class) ?> - <?php if ($_gender->isEnabled()) : ?> - <?= $_gender->setGender($block->getFormData()->getGender())->toHtml() ?> + <?php if ($_gender->isEnabled()): ?> + <?= $_gender->setGender($formData->getGender())->toHtml() ?> <?php endif ?> </fieldset> - <?php if ($block->getShowAddressFields()) : ?> + <?php if ($block->getShowAddressFields()): ?> + <?php $cityValidationClass = $this->helper(Address::class)->getAttributeValidationClass('city'); ?> + <?php $postcodeValidationClass = $this->helper(Address::class)->getAttributeValidationClass('postcode'); ?> + <?php $regionValidationClass = $this->helper(Address::class)->getAttributeValidationClass('region'); ?> <fieldset class="fieldset address"> - <legend class="legend"><span><?= $block->escapeHtml(__('Address Information')) ?></span></legend><br> + <legend class="legend"><span><?= $escaper->escapeHtml(__('Address Information')) ?></span></legend><br> <input type="hidden" name="create_address" value="1" /> <?php $_company = $block->getLayout()->createBlock(\Magento\Customer\Block\Widget\Company::class) ?> - <?php if ($_company->isEnabled()) : ?> - <?= $_company->setCompany($block->getFormData()->getCompany())->toHtml() ?> + <?php if ($_company->isEnabled()): ?> + <?= $_company->setCompany($formData->getCompany())->toHtml() ?> <?php endif ?> <?php $_telephone = $block->getLayout()->createBlock(\Magento\Customer\Block\Widget\Telephone::class) ?> - <?php if ($_telephone->isEnabled()) : ?> - <?= $_telephone->setTelephone($block->getFormData()->getTelephone())->toHtml() ?> + <?php if ($_telephone->isEnabled()): ?> + <?= $_telephone->setTelephone($formData->getTelephone())->toHtml() ?> <?php endif ?> <?php $_fax = $block->getLayout()->createBlock(\Magento\Customer\Block\Widget\Fax::class) ?> - <?php if ($_fax->isEnabled()) : ?> - <?= $_fax->setFax($block->getFormData()->getFax())->toHtml() ?> + <?php if ($_fax->isEnabled()): ?> + <?= $_fax->setFax($formData->getFax())->toHtml() ?> <?php endif ?> - <?php $_streetValidationClass = $this->helper(\Magento\Customer\Helper\Address::class)->getAttributeValidationClass('street'); ?> + <?php + $_streetValidationClass = $this->helper(Address::class) + ->getAttributeValidationClass('street'); + ?> <div class="field street required"> - <label for="street_1" class="label"><span><?= /* @noEscape */ $block->getAttributeData()->getFrontendLabel('street') ?></span></label> + <label for="street_1" class="label"> + <span><?= /* @noEscape */ $block->getAttributeData()->getFrontendLabel('street') ?></span> + </label> <div class="control"> - <input type="text" name="street[]" value="<?= $block->escapeHtmlAttr($block->getFormData()->getStreet(0)) ?>" title="<?= /* @noEscape */ $block->getAttributeData()->getFrontendLabel('street') ?>" id="street_1" class="input-text <?= $block->escapeHtmlAttr($_streetValidationClass) ?>"> + <input type="text" + name="street[]" + value="<?= $escaper->escapeHtmlAttr($formData->getStreet(0)) ?>" + title="<?= /* @noEscape */ $block->getAttributeData()->getFrontendLabel('street') ?>" + id="street_1" + class="input-text <?= $escaper->escapeHtmlAttr($_streetValidationClass) ?>"> <div class="nested"> - <?php $_streetValidationClass = trim(str_replace('required-entry', '', $_streetValidationClass)); ?> - <?php for ($_i = 2, $_n = $this->helper(\Magento\Customer\Helper\Address::class)->getStreetLines(); $_i <= $_n; $_i++) : ?> + <?php + $_streetValidationClass = trim(str_replace('required-entry', '', $_streetValidationClass)); + $streetLines = $this->helper(Address::class)->getStreetLines(); + ?> + <?php for ($_i = 2, $_n = $streetLines; $_i <= $_n; $_i++): ?> <div class="field additional"> <label class="label" for="street_<?= /* @noEscape */ $_i ?>"> - <span><?= $block->escapeHtml(__('Address')) ?></span> + <span><?= $escaper->escapeHtml(__('Address')) ?></span> </label> <div class="control"> - <input type="text" name="street[]" value="<?= $block->escapeHtml($block->getFormData()->getStreetLine($_i - 1)) ?>" title="<?= $block->escapeHtmlAttr(__('Street Address %1', $_i)) ?>" id="street_<?= /* @noEscape */ $_i ?>" class="input-text <?= $block->escapeHtmlAttr($_streetValidationClass) ?>"> + <input type="text" + name="street[]" + value="<?= $escaper->escapeHtml($formData->getStreetLine($_i - 1)) ?>" + title="<?= $escaper->escapeHtmlAttr(__('Street Address %1', $_i)) ?>" + id="street_<?= /* @noEscape */ $_i ?>" + class="input-text <?= $escaper->escapeHtmlAttr($_streetValidationClass) ?>"> </div> </div> <?php endfor; ?> @@ -82,38 +127,70 @@ </div> </div> - <div class="field required"> - <label for="city" class="label"><span><?= /* @noEscape */ $block->getAttributeData()->getFrontendLabel('city') ?></span></label> + <div class="field country required"> + <label for="country" class="label"> + <span><?= /* @noEscape */ $block->getAttributeData()->getFrontendLabel('country_id') ?></span> + </label> <div class="control"> - <input type="text" name="city" value="<?= $block->escapeHtmlAttr($block->getFormData()->getCity()) ?>" title="<?= /* @noEscape */ $block->getAttributeData()->getFrontendLabel('city') ?>" class="input-text <?= $block->escapeHtmlAttr($this->helper(\Magento\Customer\Helper\Address::class)->getAttributeValidationClass('city')) ?>" id="city"> + <?= $block->getCountryHtmlSelect() ?> </div> </div> <div class="field region required"> - <label for="region_id" class="label"><span><?= /* @noEscape */ $block->getAttributeData()->getFrontendLabel('region') ?></span></label> + <label for="region_id" class="label"> + <span><?= /* @noEscape */ $block->getAttributeData()->getFrontendLabel('region') ?></span> + </label> <div class="control"> - <select id="region_id" name="region_id" title="<?= /* @noEscape */ $block->getAttributeData()->getFrontendLabel('region') ?>" class="validate-select region_id" style="display:none;"> - <option value=""><?= $block->escapeHtml(__('Please select a region, state or province.')) ?></option> + <select id="region_id" + name="region_id" + title="<?= /* @noEscape */ $block->getAttributeData()->getFrontendLabel('region') ?>" + class="validate-select region_id" + style="display: none;"> + <option value=""> + <?= $escaper->escapeHtml(__('Please select a region, state or province.')) ?> + </option> </select> - <input type="text" id="region" name="region" value="<?= $block->escapeHtml($block->getRegion()) ?>" title="<?= /* @noEscape */ $block->getAttributeData()->getFrontendLabel('region') ?>" class="input-text <?= $block->escapeHtmlAttr($this->helper(\Magento\Customer\Helper\Address::class)->getAttributeValidationClass('region')) ?>" style="display:none;"> + <input type="text" + id="region" + name="region" + value="<?= $escaper->escapeHtml($block->getRegion()) ?>" + title="<?= /* @noEscape */ $block->getAttributeData()->getFrontendLabel('region') ?>" + class="input-text <?= $escaper->escapeHtmlAttr($regionValidationClass) ?>" + style="display:none;"> </div> </div> - <div class="field zip required"> - <label for="zip" class="label"><span><?= /* @noEscape */ $block->getAttributeData()->getFrontendLabel('postcode') ?></span></label> + <div class="field required"> + <label for="city" class="label"> + <span><?= /* @noEscape */ $block->getAttributeData()->getFrontendLabel('city') ?></span> + </label> <div class="control"> - <input type="text" name="postcode" value="<?= $block->escapeHtmlAttr($block->getFormData()->getPostcode()) ?>" title="<?= /* @noEscape */ $block->getAttributeData()->getFrontendLabel('postcode') ?>" id="zip" class="input-text validate-zip-international <?= $block->escapeHtmlAttr($this->helper(\Magento\Customer\Helper\Address::class)->getAttributeValidationClass('postcode')) ?>"> + <input type="text" + name="city" + value="<?= $escaper->escapeHtmlAttr($formData->getCity()) ?>" + title="<?= /* @noEscape */ $block->getAttributeData()->getFrontendLabel('city') ?>" + class="input-text <?= $escaper->escapeHtmlAttr($cityValidationClass) ?>" + id="city"> </div> </div> - <div class="field country required"> - <label for="country" class="label"><span><?= /* @noEscape */ $block->getAttributeData()->getFrontendLabel('country_id') ?></span></label> + <div class="field zip required"> + <label for="zip" class="label"> + <span><?= /* @noEscape */ $block->getAttributeData()->getFrontendLabel('postcode') ?></span> + </label> <div class="control"> - <?= $block->getCountryHtmlSelect() ?> + <input type="text" + name="postcode" + value="<?= $escaper->escapeHtmlAttr($formData->getPostcode()) ?>" + title="<?= /* @noEscape */ $block->getAttributeData()->getFrontendLabel('postcode') ?>" + id="zip" + class="input-text validate-zip-international + <?= $escaper->escapeHtmlAttr($postcodeValidationClass) ?>"> </div> </div> + <?php $addressAttributes = $block->getChildBlock('customer_form_address_user_attributes');?> - <?php if ($addressAttributes) : ?> + <?php if ($addressAttributes): ?> <?php $addressAttributes->setEntityType('customer_address'); ?> <?php $addressAttributes->setFieldIdFormat('address:%1$s')->setFieldNameFormat('address[%1$s]');?> <?php $block->restoreSessionData($addressAttributes->getMetadataForm(), 'address');?> @@ -124,29 +201,40 @@ </fieldset> <?php endif; ?> - <fieldset class="fieldset create account" data-hasrequired="<?= $block->escapeHtmlAttr(__('* Required Fields')) ?>"> - <legend class="legend"><span><?= $block->escapeHtml(__('Sign-in Information')) ?></span></legend><br> + <fieldset class="fieldset create account" + data-hasrequired="<?= $escaper->escapeHtmlAttr(__('* Required Fields')) ?>"> + <legend class="legend"><span><?= $escaper->escapeHtml(__('Sign-in Information')) ?></span></legend><br> <div class="field required"> - <label for="email_address" class="label"><span><?= $block->escapeHtml(__('Email')) ?></span></label> + <label for="email_address" class="label"><span><?= $escaper->escapeHtml(__('Email')) ?></span></label> <div class="control"> - <input type="email" name="email" autocomplete="email" id="email_address" value="<?= $block->escapeHtmlAttr($block->getFormData()->getEmail()) ?>" title="<?= $block->escapeHtmlAttr(__('Email')) ?>" class="input-text" data-mage-init='{"mage/trim-input":{}}' data-validate="{required:true, 'validate-email':true}"> + <input type="email" + name="email" + autocomplete="email" + id="email_address" + value="<?= $escaper->escapeHtmlAttr($formData->getEmail()) ?>" + title="<?= $escaper->escapeHtmlAttr(__('Email')) ?>" + class="input-text" + data-mage-init='{"mage/trim-input":{}}' + data-validate="{required:true, 'validate-email':true}"> </div> </div> <div class="field password required"> - <label for="password" class="label"><span><?= $block->escapeHtml(__('Password')) ?></span></label> + <label for="password" class="label"><span><?= $escaper->escapeHtml(__('Password')) ?></span></label> <div class="control"> <input type="password" name="password" id="password" - title="<?= $block->escapeHtmlAttr(__('Password')) ?>" + title="<?= $escaper->escapeHtmlAttr(__('Password')) ?>" class="input-text" - data-password-min-length="<?= $block->escapeHtmlAttr($block->getMinimumPasswordLength()) ?>" - data-password-min-character-sets="<?= $block->escapeHtmlAttr($block->getRequiredCharacterClassesNumber()) ?>" + data-password-min-length="<?= + $escaper->escapeHtmlAttr($block->getMinimumPasswordLength()) ?>" + data-password-min-character-sets="<?= + $escaper->escapeHtmlAttr($block->getRequiredCharacterClassesNumber()) ?>" data-validate="{required:true, 'validate-customer-password':true}" autocomplete="off"> <div id="password-strength-meter-container" data-role="password-strength-meter" aria-live="polite"> <div id="password-strength-meter" class="password-strength-meter"> - <?= $block->escapeHtml(__('Password Strength')) ?>: + <?= $escaper->escapeHtml(__('Password Strength')) ?>: <span id="password-strength-meter-label" data-role="password-strength-meter-label"> - <?= $block->escapeHtml(__('No Password')) ?> + <?= $escaper->escapeHtml(__('No Password')) ?> </span> </div> </div> @@ -154,19 +242,34 @@ </div> <div class="field confirmation required"> - <label for="password-confirmation" class="label"><span><?= $block->escapeHtml(__('Confirm Password')) ?></span></label> + <label for="password-confirmation" class="label"> + <span><?= $escaper->escapeHtml(__('Confirm Password')) ?></span> + </label> <div class="control"> - <input type="password" name="password_confirmation" title="<?= $block->escapeHtmlAttr(__('Confirm Password')) ?>" id="password-confirmation" class="input-text" data-validate="{required:true, equalTo:'#password'}" autocomplete="off"> + <input type="password" + name="password_confirmation" + title="<?= $escaper->escapeHtmlAttr(__('Confirm Password')) ?>" + id="password-confirmation" + class="input-text" + data-validate="{required:true, equalTo:'#password'}" + autocomplete="off"> </div> </div> <?= $block->getChildHtml('form_additional_info') ?> </fieldset> <div class="actions-toolbar"> <div class="primary"> - <button type="submit" class="action submit primary" title="<?= $block->escapeHtmlAttr(__('Create an Account')) ?>"><span><?= $block->escapeHtml(__('Create an Account')) ?></span></button> + <button type="submit" + class="action submit primary" + title="<?= $escaper->escapeHtmlAttr(__('Create an Account')) ?>"> + <span><?= $escaper->escapeHtml(__('Create an Account')) ?></span> + </button> </div> <div class="secondary"> - <a class="action back" href="<?= $block->escapeUrl($block->getBackUrl()) ?>"><span><?= $block->escapeHtml(__('Back')) ?></span></a> + <a class="action back" + href="<?= $escaper->escapeUrl($block->getBackUrl()) ?>"> + <span><?= $escaper->escapeHtml(__('Back')) ?></span> + </a> </div> </div> </form> @@ -180,7 +283,7 @@ require([ var ignore = <?= /* @noEscape */ $_dob->isEnabled() ? '\'input[id$="full"]\'' : 'null' ?>; dataForm.mage('validation', { - <?php if ($_dob->isEnabled()) : ?> + <?php if ($_dob->isEnabled()): ?> errorPlacement: function(error, element) { if (element.prop('id').search('full') !== -1) { var dobElement = $(element).parents('.customer-dob'), @@ -194,26 +297,34 @@ require([ } }, ignore: ':hidden:not(' + ignore + ')' - <?php else : ?> + <?php else: ?> ignore: ignore ? ':hidden:not(' + ignore + ')' : ':hidden' <?php endif ?> }).find('input:text').attr('autocomplete', 'off'); + dataForm.submit(function () { + $(this).find(':submit').attr('disabled', 'disabled'); + }); + dataForm.bind("invalid-form.validate", function () { + $(this).find(':submit').prop('disabled', false); + }); }); </script> -<?php if ($block->getShowAddressFields()) : ?> +<?php if ($block->getShowAddressFields()): ?> <script type="text/x-magento-init"> { "#country": { "regionUpdater": { - "optionalRegionAllowed": <?= /* @noEscape */ $block->getConfig('general/region/display_all') ? 'true' : 'false' ?>, + "optionalRegionAllowed": <?= /* @noEscape */ $displayAll ? 'true' : 'false' ?>, "regionListId": "#region_id", "regionInputId": "#region", "postcodeId": "#zip", "form": "#form-validate", - "regionJson": <?= /* @noEscape */ $this->helper(\Magento\Directory\Helper\Data::class)->getRegionJson() ?>, - "defaultRegion": "<?= (int) $block->getFormData()->getRegionId() ?>", - "countriesWithOptionalZip": <?= /* @noEscape */ $this->helper(\Magento\Directory\Helper\Data::class)->getCountriesWithOptionalZip(true) ?> + "regionJson": <?= /* @noEscape */ $this->helper(\Magento\Directory\Helper\Data::class) + ->getRegionJson() ?>, + "defaultRegion": "<?= (int) $formData->getRegionId() ?>", + "countriesWithOptionalZip": <?= /* @noEscape */ $this->helper(\Magento\Directory\Helper\Data::class) + ->getCountriesWithOptionalZip(true) ?> } } } diff --git a/app/code/Magento/Customer/view/frontend/templates/widget/telephone.phtml b/app/code/Magento/Customer/view/frontend/templates/widget/telephone.phtml index 4c3432233189b..eb460e69118a9 100644 --- a/app/code/Magento/Customer/view/frontend/templates/widget/telephone.phtml +++ b/app/code/Magento/Customer/view/frontend/templates/widget/telephone.phtml @@ -14,7 +14,7 @@ </span> </label> <div class="control"> - <input type="text" + <input type="tel" name="telephone" id="telephone" value="<?= $block->escapeHtmlAttr($block->getTelephone()) ?>" diff --git a/app/code/Magento/Customer/view/frontend/web/js/customer-data.js b/app/code/Magento/Customer/view/frontend/web/js/customer-data.js index de3ff10bb057b..770ea47d754d3 100644 --- a/app/code/Magento/Customer/view/frontend/web/js/customer-data.js +++ b/app/code/Magento/Customer/view/frontend/web/js/customer-data.js @@ -214,6 +214,11 @@ define([ this.reload(storageInvalidation.keys(), false); } } + + if (!_.isEmpty($.cookieStorage.get('section_data_clean'))) { + this.reload(sectionConfig.getSectionNames(), true); + $.cookieStorage.set('section_data_clean', ''); + } }, /** diff --git a/app/code/Magento/Customer/view/frontend/web/js/section-config.js b/app/code/Magento/Customer/view/frontend/web/js/section-config.js index d346d5b070729..107a177d0832f 100644 --- a/app/code/Magento/Customer/view/frontend/web/js/section-config.js +++ b/app/code/Magento/Customer/view/frontend/web/js/section-config.js @@ -6,64 +6,65 @@ define(['underscore'], function (_) { 'use strict'; - var baseUrls, sections, clientSideSections, sectionNames, canonize; + var baseUrls = [], + sections = [], + clientSideSections = [], + sectionNames = [], + canonize; /** * @param {String} url * @return {String} */ canonize = function (url) { - var route = url, - key; + var route = url; - for (key in baseUrls) { //eslint-disable-line guard-for-in - route = url.replace(baseUrls[key], ''); + _.some(baseUrls, function (baseUrl) { + route = url.replace(baseUrl, ''); - if (route != url) { //eslint-disable-line eqeqeq - break; - } - } + return route !== url; + }); return route.replace(/^\/?index.php\/?/, '').toLowerCase(); }; return { /** - * @param {String} url - * @return {Array} + * Returns a list of sections which should be invalidated for given URL. + * @param {String} url - URL which was requested. + * @return {Array} - List of sections to invalidate. */ getAffectedSections: function (url) { var route = canonize(url), actions = _.find(sections, function (val, section) { var matched; + // Covers the case where "*" works as a glob pattern. if (section.indexOf('*') >= 0) { section = section.replace(/\*/g, '[^/]+') + '$'; matched = route.match(section); - return matched && matched[0] == route; //eslint-disable-line eqeqeq + return matched && matched[0] === route; } return route.indexOf(section) === 0; }); - return _.union(_.toArray(actions), _.toArray(sections['*'])); + return _.union(_.toArray(actions), sections['*']); }, /** - * @param {*} allSections - * @return {*} + * Filters the list of given sections to the ones defined as client side. + * @param {Array} allSections - List of sections to check. + * @return {Array} - List of filtered sections. */ filterClientSideSections: function (allSections) { - if (Array.isArray(allSections)) { - return _.difference(allSections, clientSideSections); - } - - return allSections; + return _.difference(allSections, clientSideSections); }, /** - * @param {String} sectionName + * Tells if section is defined as client side. + * @param {String} sectionName - Name of the section to check. * @return {Boolean} */ isClientSideSection: function (sectionName) { @@ -72,7 +73,6 @@ define(['underscore'], function (_) { /** * Returns array of section names. - * * @returns {Array} */ getSectionNames: function () { diff --git a/app/code/Magento/Customer/view/frontend/web/js/validation.js b/app/code/Magento/Customer/view/frontend/web/js/validation.js index 67a714212026a..1f7f24d5ac031 100644 --- a/app/code/Magento/Customer/view/frontend/web/js/validation.js +++ b/app/code/Magento/Customer/view/frontend/web/js/validation.js @@ -1,19 +1,23 @@ define([ 'jquery', 'moment', + 'mageUtils', 'jquery/validate', + 'validation', 'mage/translate' -], function ($, moment) { +], function ($, moment, utils) { 'use strict'; $.validator.addMethod( 'validate-dob', - function (value) { + function (value, element, params) { + var dateFormat = utils.convertToMomentFormat(params.dateFormat); + if (value === '') { return true; } - return moment(value).isBefore(moment()); + return moment(value, dateFormat).isBefore(moment()); }, $.mage.__('The Date of Birth should not be greater than today.') ); diff --git a/app/code/Magento/CustomerGraphQl/Model/Customer/ValidateCustomerData.php b/app/code/Magento/CustomerGraphQl/Model/Customer/ValidateCustomerData.php index 794cb0048592d..3861ce324ea7d 100644 --- a/app/code/Magento/CustomerGraphQl/Model/Customer/ValidateCustomerData.php +++ b/app/code/Magento/CustomerGraphQl/Model/Customer/ValidateCustomerData.php @@ -8,9 +8,10 @@ namespace Magento\CustomerGraphQl\Model\Customer; use Magento\Framework\GraphQl\Exception\GraphQlInputException; +use Magento\Framework\Validator\EmailAddress as EmailAddressValidator; /** - * Class ValidateCustomerData + * Customer data validation used during customer account creation and updating */ class ValidateCustomerData { @@ -21,14 +22,23 @@ class ValidateCustomerData */ private $getAllowedCustomerAttributes; + /** + * @var EmailAddressValidator + */ + private $emailAddressValidator; + /** * ValidateCustomerData constructor. * * @param GetAllowedCustomerAttributes $getAllowedCustomerAttributes + * @param EmailAddressValidator $emailAddressValidator */ - public function __construct(GetAllowedCustomerAttributes $getAllowedCustomerAttributes) - { + public function __construct( + GetAllowedCustomerAttributes $getAllowedCustomerAttributes, + EmailAddressValidator $emailAddressValidator + ) { $this->getAllowedCustomerAttributes = $getAllowedCustomerAttributes; + $this->emailAddressValidator = $emailAddressValidator; } /** @@ -59,5 +69,11 @@ public function execute(array $customerData): void __('Required parameters are missing: %1', [implode(', ', $errorInput)]) ); } + + if (isset($customerData['email']) && !$this->emailAddressValidator->isValid($customerData['email'])) { + throw new GraphQlInputException( + __('"%1" is not a valid email address.', $customerData['email']) + ); + } } } diff --git a/app/code/Magento/CustomerGraphQl/Test/Unit/Model/Resolver/RevokeCustomerTokenTest.php b/app/code/Magento/CustomerGraphQl/Test/Unit/Model/Resolver/RevokeCustomerTokenTest.php new file mode 100644 index 0000000000000..4f8d626ca4e64 --- /dev/null +++ b/app/code/Magento/CustomerGraphQl/Test/Unit/Model/Resolver/RevokeCustomerTokenTest.php @@ -0,0 +1,172 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ + +namespace Magento\CustomerGraphQl\Test\Unit\Model\Resolver; + +use Magento\CustomerGraphQl\Model\Resolver\RevokeCustomerToken; +use Magento\Framework\TestFramework\Unit\Helper\ObjectManager; +use Magento\Framework\GraphQl\Config\Element\Field; +use Magento\Framework\GraphQl\Schema\Type\ResolveInfo; +use Magento\GraphQl\Model\Query\ContextInterface; +use Magento\GraphQl\Model\Query\ContextExtensionInterface; +use Magento\Integration\Api\CustomerTokenServiceInterface; +use PHPUnit\Framework\MockObject\MockObject; +use PHPUnit\Framework\TestCase; + +/** + * Test class for \Magento\CustomerGraphQl\Model\Resolver\RevokeCustomerToken + */ +class RevokeCustomerTokenTest extends TestCase +{ + /** + * Object Manager Instance + * + * @var ObjectManager + */ + private $objectManager; + + /** + * Testable Object + * + * @var RevokeCustomerToken + */ + private $resolver; + + /** + * @var ContextInterface|MockObject + */ + private $contextMock; + + /** + * @var ContextExtensionInterface|MockObject + */ + private $contextExtensionMock; + + /** + * @var CustomerTokenServiceInterface|MockObject + */ + private $customerTokenServiceMock; + + /** + * @var Field|MockObject + */ + private $fieldMock; + + /** + * @var ResolveInfo|MockObject + */ + private $resolveInfoMock; + + /** + * @inheritdoc + */ + protected function setUp() : void + { + $this->objectManager = new ObjectManager($this); + + $this->contextMock = $this->getMockBuilder(ContextInterface::class) + ->disableOriginalConstructor() + ->setMethods( + [ + 'getExtensionAttributes', + 'getUserId', + 'getUserType', + ] + ) + ->getMock(); + + $this->contextExtensionMock = $this->getMockBuilder(ContextExtensionInterface::class) + ->setMethods( + [ + 'getIsCustomer', + 'getStore', + 'setStore', + 'setIsCustomer', + ] + ) + ->getMock(); + + $this->fieldMock = $this->getMockBuilder(Field::class) + ->disableOriginalConstructor() + ->getMock(); + + $this->customerTokenServiceMock = $this->getMockBuilder(CustomerTokenServiceInterface::class) + ->getMockForAbstractClass(); + + $this->resolveInfoMock = $this->getMockBuilder(ResolveInfo::class) + ->disableOriginalConstructor() + ->getMock(); + + $this->resolver = $this->objectManager->getObject( + RevokeCustomerToken::class, + [ + 'customerTokenService' => $this->customerTokenServiceMock, + ] + ); + } + + /** + * Test revoke customer token + */ + public function testRevokeCustomerToken() + { + $isCustomer = true; + $revokeCustomerTokenResult = true; + + $this->contextMock + ->expects($this->once()) + ->method('getExtensionAttributes') + ->willReturn($this->contextExtensionMock); + + $this->contextExtensionMock + ->expects($this->once()) + ->method('getIsCustomer') + ->willReturn($isCustomer); + + $this->customerTokenServiceMock + ->expects($this->once()) + ->method('revokeCustomerAccessToken') + ->willReturn($revokeCustomerTokenResult); + + $this->assertEquals( + [ + 'result' => true + ], + $this->resolver->resolve( + $this->fieldMock, + $this->contextMock, + $this->resolveInfoMock + ) + ); + } + + /** + * Test mutation when customer isn't authorized. + * + * @expectedException \Magento\Framework\GraphQl\Exception\GraphQlAuthorizationException + * @expectedExceptionMessage The current customer isn't authorized. + */ + public function testCustomerNotAuthorized() + { + $isCustomer = false; + + $this->contextMock + ->expects($this->once()) + ->method('getExtensionAttributes') + ->willReturn($this->contextExtensionMock); + + $this->contextExtensionMock + ->expects($this->once()) + ->method('getIsCustomer') + ->willReturn($isCustomer); + + $this->resolver->resolve( + $this->fieldMock, + $this->contextMock, + $this->resolveInfoMock + ); + } +} diff --git a/app/code/Magento/CustomerImportExport/Model/Import/Address.php b/app/code/Magento/CustomerImportExport/Model/Import/Address.php index 1a8859d5bd7bf..09d76ec3fb71f 100644 --- a/app/code/Magento/CustomerImportExport/Model/Import/Address.php +++ b/app/code/Magento/CustomerImportExport/Model/Import/Address.php @@ -3,6 +3,7 @@ * Copyright © Magento, Inc. All rights reserved. * See COPYING.txt for license details. */ + namespace Magento\CustomerImportExport\Model\Import; use Magento\Customer\Model\ResourceModel\Address\Attribute\Source\CountryWithWebsites as CountryWithWebsitesSource; @@ -12,6 +13,7 @@ use Magento\Store\Model\Store; use Magento\CustomerImportExport\Model\ResourceModel\Import\Address\Storage as AddressStorage; use Magento\ImportExport\Model\Import\AbstractSource; +use Magento\Customer\Model\Indexer\Processor; /** * Customer address import @@ -224,7 +226,11 @@ class Address extends AbstractCustomer * @array */ protected $validColumnNames = [ - "region_id", "vat_is_valid", "vat_request_date", "vat_request_id", "vat_request_success" + "region_id", + "vat_is_valid", + "vat_request_date", + "vat_request_id", + "vat_request_success" ]; /** @@ -249,6 +255,11 @@ class Address extends AbstractCustomer */ private $addressStorage; + /** + * @var Processor + */ + private $indexerProcessor; + /** * @param \Magento\Framework\Stdlib\StringUtils $string * @param \Magento\Framework\App\Config\ScopeConfigInterface $scopeConfig @@ -269,6 +280,7 @@ class Address extends AbstractCustomer * @param array $data * @param CountryWithWebsitesSource|null $countryWithWebsites * @param AddressStorage|null $addressStorage + * @param Processor $indexerProcessor * * @SuppressWarnings(PHPMD.NPathComplexity) * @SuppressWarnings(PHPMD.ExcessiveParameterList) @@ -291,8 +303,9 @@ public function __construct( \Magento\Framework\Stdlib\DateTime $dateTime, \Magento\Customer\Model\Address\Validator\Postcode $postcodeValidator, array $data = [], - CountryWithWebsitesSource $countryWithWebsites = null, - AddressStorage $addressStorage = null + ?CountryWithWebsitesSource $countryWithWebsites = null, + ?AddressStorage $addressStorage = null, + ?Processor $indexerProcessor = null ) { $this->_customerFactory = $customerFactory; $this->_addressFactory = $addressFactory; @@ -343,6 +356,9 @@ public function __construct( $this->addressStorage = $addressStorage ?: ObjectManager::getInstance()->get(AddressStorage::class); + $this->indexerProcessor = $indexerProcessor + ?: ObjectManager::getInstance()->get(Processor::class); + $this->_initAttributes(); $this->_initCountryRegions(); } @@ -475,7 +491,7 @@ public function prepareCustomerData($rows): void $ids = []; foreach ($customersPresent as $customerData) { - $id = $this->getCustomerStorage()->getCustomerId( + $id = $this->getCustomerStorage()->getLoadedCustomerId( $customerData['email'], $customerData['website_id'] ); @@ -507,11 +523,12 @@ public function validateData() protected function _importData() { //Preparing data for mass validation/import. - $rows = []; + $rows = [[]]; while ($bunch = $this->_dataSourceModel->getNextBunch()) { - $rows = array_merge($rows, $bunch); + $rows[] = $bunch; } - $this->prepareCustomerData($rows); + + $this->prepareCustomerData(array_merge(...$rows)); unset($bunch, $rows); $this->_dataSourceModel->getIterator()->rewind(); @@ -556,6 +573,7 @@ protected function _importData() $this->_deleteAddressEntities($deleteRowIds); } + $this->indexerProcessor->markIndexerAsInvalid(); return true; } @@ -586,7 +604,7 @@ protected function _mergeEntityAttributes(array $newAttributes, array $attribute * @SuppressWarnings(PHPMD.CyclomaticComplexity) * @SuppressWarnings(PHPMD.NPathComplexity) */ - protected function _prepareDataForUpdate(array $rowData):array + protected function _prepareDataForUpdate(array $rowData): array { $email = strtolower($rowData[self::COLUMN_EMAIL]); $customerId = $this->_getCustomerId($email, $rowData[self::COLUMN_WEBSITE]); @@ -600,9 +618,10 @@ protected function _prepareDataForUpdate(array $rowData):array $newAddress = true; // get address id if ($rowData[self::COLUMN_ADDRESS_ID] + && $customerId && $this->addressStorage->doesExist( - $rowData[self::COLUMN_ADDRESS_ID], - (string)$customerId + (int) $rowData[self::COLUMN_ADDRESS_ID], + $customerId ) ) { $newAddress = false; @@ -636,7 +655,7 @@ protected function _prepareDataForUpdate(array $rowData):array if ($attributeParams['is_static']) { $entityRow[$attributeAlias] = $value; } else { - $attributes[$attributeParams['table']][$addressId][$attributeParams['id']]= $value; + $attributes[$attributeParams['table']][$addressId][$attributeParams['id']] = $value; } } } @@ -850,7 +869,7 @@ protected function _validateRowForUpdate(array $rowData, $rowNumber) if ($this->_checkUniqueKey($rowData, $rowNumber)) { $email = strtolower($rowData[self::COLUMN_EMAIL]); $website = $rowData[self::COLUMN_WEBSITE]; - $addressId = $rowData[self::COLUMN_ADDRESS_ID]; + $addressId = (int) $rowData[self::COLUMN_ADDRESS_ID]; $customerId = $this->_getCustomerId($email, $website); if ($customerId === false) { @@ -919,16 +938,16 @@ protected function _validateRowForDelete(array $rowData, $rowNumber) if ($this->_checkUniqueKey($rowData, $rowNumber)) { $email = strtolower($rowData[self::COLUMN_EMAIL]); $website = $rowData[self::COLUMN_WEBSITE]; - $addressId = $rowData[self::COLUMN_ADDRESS_ID]; + $addressId = (int) $rowData[self::COLUMN_ADDRESS_ID]; $customerId = $this->_getCustomerId($email, $website); if ($customerId === false) { $this->addRowError(self::ERROR_CUSTOMER_NOT_FOUND, $rowNumber); - } elseif (!strlen($addressId)) { + } elseif (!$addressId) { $this->addRowError(self::ERROR_ADDRESS_ID_IS_EMPTY, $rowNumber); } elseif (!$this->addressStorage->doesExist( - (string)$addressId, - (string)$customerId + $addressId, + $customerId )) { $this->addRowError(self::ERROR_ADDRESS_NOT_FOUND, $rowNumber); } @@ -942,11 +961,11 @@ protected function _validateRowForDelete(array $rowData, $rowNumber) * @param int $addressId * @return bool */ - protected function _checkRowDuplicate($customerId, $addressId) + protected function _checkRowDuplicate(int $customerId, int $addressId) { $isAddressExists = $this->addressStorage->doesExist( - (string)$addressId, - (string)$customerId + $addressId, + $customerId ); $isPkRowSet = isset($this->_importedRowPks[$customerId][$addressId]); diff --git a/app/code/Magento/CustomerImportExport/Model/Import/Customer.php b/app/code/Magento/CustomerImportExport/Model/Import/Customer.php index f86ebaea69730..8f5bb951ce737 100644 --- a/app/code/Magento/CustomerImportExport/Model/Import/Customer.php +++ b/app/code/Magento/CustomerImportExport/Model/Import/Customer.php @@ -11,6 +11,8 @@ use Magento\ImportExport\Model\Import; use Magento\ImportExport\Model\Import\ErrorProcessing\ProcessingErrorAggregatorInterface; use Magento\ImportExport\Model\Import\AbstractSource; +use Magento\Customer\Model\Indexer\Processor; +use Magento\Framework\App\ObjectManager; /** * Customer entity import @@ -168,6 +170,11 @@ class Customer extends AbstractCustomer 'lock_expires', ]; + /** + * @var Processor + */ + private $indexerProcessor; + /** * @param \Magento\Framework\Stdlib\StringUtils $string * @param \Magento\Framework\App\Config\ScopeConfigInterface $scopeConfig @@ -182,6 +189,7 @@ class Customer extends AbstractCustomer * @param \Magento\Customer\Model\ResourceModel\Attribute\CollectionFactory $attrCollectionFactory * @param \Magento\Customer\Model\CustomerFactory $customerFactory * @param array $data + * @param Processor $indexerProcessor * @SuppressWarnings(PHPMD.ExcessiveParameterList) */ public function __construct( @@ -197,7 +205,8 @@ public function __construct( \Magento\CustomerImportExport\Model\ResourceModel\Import\Customer\StorageFactory $storageFactory, \Magento\Customer\Model\ResourceModel\Attribute\CollectionFactory $attrCollectionFactory, \Magento\Customer\Model\CustomerFactory $customerFactory, - array $data = [] + array $data = [], + ?Processor $indexerProcessor = null ) { $this->_resourceHelper = $resourceHelper; @@ -254,6 +263,7 @@ public function __construct( /** @var $customerResource \Magento\Customer\Model\ResourceModel\Customer */ $customerResource = $this->_customerModel->getResource(); $this->_entityTable = $customerResource->getEntityTable(); + $this->indexerProcessor = $indexerProcessor ?: ObjectManager::getInstance()->get(Processor::class); } /** @@ -408,7 +418,7 @@ protected function _prepareDataForUpdate(array $rowData) $createdAt = (new \DateTime())->setTimestamp(strtotime($rowData['created_at'])); } - $emailInLowercase = strtolower($rowData[self::COLUMN_EMAIL]); + $emailInLowercase = strtolower(trim($rowData[self::COLUMN_EMAIL])); $newCustomer = false; $entityId = $this->_getCustomerId($emailInLowercase, $rowData[self::COLUMN_WEBSITE]); if (!$entityId) { @@ -478,6 +488,8 @@ protected function _prepareDataForUpdate(array $rowData) $entityRow['updated_at'] = $now->format(\Magento\Framework\Stdlib\DateTime::DATETIME_PHP_FORMAT); if (!empty($rowData[self::COLUMN_STORE])) { $entityRow['store_id'] = $this->_storeCodeToId[$rowData[self::COLUMN_STORE]]; + } else { + $entityRow['store_id'] = $this->getCustomerStoreId($emailInLowercase, $rowData[self::COLUMN_WEBSITE]); } $entitiesToUpdate[] = $entityRow; } @@ -500,8 +512,8 @@ protected function _importData() { while ($bunch = $this->_dataSourceModel->getNextBunch()) { $this->prepareCustomerData($bunch); - $entitiesToCreate = []; - $entitiesToUpdate = []; + $entitiesToCreate = [[]]; + $entitiesToUpdate = [[]]; $entitiesToDelete = []; $attributesToSave = []; @@ -521,10 +533,10 @@ protected function _importData() ); } elseif ($this->getBehavior($rowData) == \Magento\ImportExport\Model\Import::BEHAVIOR_ADD_UPDATE) { $processedData = $this->_prepareDataForUpdate($rowData); - // phpcs:disable Magento2.Performance.ForeachArrayMerge - $entitiesToCreate = array_merge($entitiesToCreate, $processedData[self::ENTITIES_TO_CREATE_KEY]); - $entitiesToUpdate = array_merge($entitiesToUpdate, $processedData[self::ENTITIES_TO_UPDATE_KEY]); - // phpcs:enable + + $entitiesToCreate[] = $processedData[self::ENTITIES_TO_CREATE_KEY]; + $entitiesToUpdate[] = $processedData[self::ENTITIES_TO_UPDATE_KEY]; + foreach ($processedData[self::ATTRIBUTES_TO_SAVE_KEY] as $tableName => $customerAttributes) { if (!isset($attributesToSave[$tableName])) { $attributesToSave[$tableName] = []; @@ -534,6 +546,10 @@ protected function _importData() } } } + + $entitiesToCreate = array_merge(...$entitiesToCreate); + $entitiesToUpdate = array_merge(...$entitiesToUpdate); + $this->updateItemsCounterStats($entitiesToCreate, $entitiesToUpdate, $entitiesToDelete); /** * Save prepared data @@ -548,7 +564,7 @@ protected function _importData() $this->_deleteCustomerEntities($entitiesToDelete); } } - + $this->indexerProcessor->markIndexerAsInvalid(); return true; } @@ -666,4 +682,22 @@ public function getValidColumnNames() ) ); } + + /** + * Get customer store ID by email and website ID. + * + * @param string $email + * @param string $websiteCode + * @return bool|int + */ + private function getCustomerStoreId(string $email, string $websiteCode) + { + $websiteId = (int) $this->getWebsiteId($websiteCode); + $storeId = $this->getCustomerStorage()->getCustomerStoreId($email, $websiteId); + if ($storeId === null || $storeId === false) { + $defaultStore = $this->_storeManager->getWebsite($websiteId)->getDefaultStore(); + $storeId = $defaultStore ? $defaultStore->getId() : \Magento\Store\Model\Store::DEFAULT_STORE_ID; + } + return $storeId; + } } diff --git a/app/code/Magento/CustomerImportExport/Model/Import/CustomerComposite.php b/app/code/Magento/CustomerImportExport/Model/Import/CustomerComposite.php index 0f4c5f82bfe1d..4a22dc83a1f31 100644 --- a/app/code/Magento/CustomerImportExport/Model/Import/CustomerComposite.php +++ b/app/code/Magento/CustomerImportExport/Model/Import/CustomerComposite.php @@ -6,6 +6,7 @@ namespace Magento\CustomerImportExport\Model\Import; use Magento\ImportExport\Model\Import\ErrorProcessing\ProcessingErrorAggregatorInterface; +use Magento\Customer\Model\Indexer\Processor; /** * Import entity customer combined model @@ -148,6 +149,11 @@ class CustomerComposite extends \Magento\ImportExport\Model\Import\AbstractEntit */ protected $masterAttributeCode = 'email'; + /** + * @var Processor + */ + private $indexerProcessor; + /** * @param \Magento\Framework\Stdlib\StringUtils $string * @param \Magento\Framework\App\Config\ScopeConfigInterface $scopeConfig @@ -158,6 +164,7 @@ class CustomerComposite extends \Magento\ImportExport\Model\Import\AbstractEntit * @param \Magento\CustomerImportExport\Model\ResourceModel\Import\CustomerComposite\DataFactory $dataFactory * @param \Magento\CustomerImportExport\Model\Import\CustomerFactory $customerFactory * @param \Magento\CustomerImportExport\Model\Import\AddressFactory $addressFactory + * @param Processor $indexerProcessor * @param array $data * @throws \Magento\Framework\Exception\LocalizedException * @@ -173,6 +180,7 @@ public function __construct( \Magento\CustomerImportExport\Model\ResourceModel\Import\CustomerComposite\DataFactory $dataFactory, \Magento\CustomerImportExport\Model\Import\CustomerFactory $customerFactory, \Magento\CustomerImportExport\Model\Import\AddressFactory $addressFactory, + Processor $indexerProcessor, array $data = [] ) { parent::__construct($string, $scopeConfig, $importFactory, $resourceHelper, $resource, $errorAggregator, $data); @@ -230,6 +238,7 @@ public function __construct( } else { $this->_nextCustomerId = $resourceHelper->getNextAutoincrement($this->_customerEntity->getEntityTable()); } + $this->indexerProcessor = $indexerProcessor; } /** @@ -273,11 +282,12 @@ protected function _importData() $this->countItemsCreated += $this->_customerEntity->getCreatedItemsCount(); $this->countItemsUpdated += $this->_customerEntity->getUpdatedItemsCount(); $this->countItemsDeleted += $this->_customerEntity->getDeletedItemsCount(); - if ($this->getBehavior() != \Magento\ImportExport\Model\Import::BEHAVIOR_DELETE) { - return $result && $this->_addressEntity->setCustomerAttributes($this->_customerAttributes)->importData(); + $result = $result && $this->_addressEntity->setCustomerAttributes($this->_customerAttributes)->importData(); + } + if ($result) { + $this->indexerProcessor->markIndexerAsInvalid(); } - return $result; } diff --git a/app/code/Magento/CustomerImportExport/Model/ResourceModel/Import/Address/Storage.php b/app/code/Magento/CustomerImportExport/Model/ResourceModel/Import/Address/Storage.php index 6d29d7ae792ff..f722aef3239bf 100644 --- a/app/code/Magento/CustomerImportExport/Model/ResourceModel/Import/Address/Storage.php +++ b/app/code/Magento/CustomerImportExport/Model/ResourceModel/Import/Address/Storage.php @@ -23,7 +23,7 @@ class Storage /** * IDs of addresses grouped by customer IDs. * - * @var string[][] + * @var int[][] */ private $addresses = []; @@ -62,16 +62,16 @@ public function __construct( /** * Record existing address. * - * @param string $customerId - * @param string $addressId - * + * @param int $customerId + * @param int $addressId * @return void */ - private function addRecord(string $customerId, string $addressId): void + private function addRecord(int $customerId, int $addressId): void { if (!$customerId || !$addressId) { return; } + if (!array_key_exists($customerId, $this->addresses)) { $this->addresses[$customerId] = []; } @@ -84,7 +84,7 @@ private function addRecord(string $customerId, string $addressId): void /** * Load addresses IDs for given customers. * - * @param string[] $customerIds + * @param int[] $customerIds * * @return void */ @@ -95,27 +95,31 @@ private function loadAddresses(array $customerIds): void $collection->removeAttributeToSelect(); $select = $collection->getSelect(); $tableId = array_keys($select->getPart(Select::FROM))[0]; - $select->where($tableId .'.parent_id in (?)', $customerIds); + $select->reset(Select::COLUMNS)->columns([$tableId . '.entity_id', $tableId . '.parent_id']); - $this->collectionIterator->iterate( - $collection, - $this->config->getValue(AbstractEntity::XML_PATH_PAGE_SIZE), - [ - function (DataObject $record) { - $this->addRecord($record->getParentId(), $record->getId()); - } - ] - ); + $pageSize = $this->config->getValue(AbstractEntity::XML_PATH_PAGE_SIZE); + $getChuck = function (int $offset) use ($customerIds, $pageSize) { + return array_slice($customerIds, $offset, $pageSize); + }; + $offset = 0; + for ($idsChunk = $getChuck($offset); !empty($idsChunk); $offset += $pageSize, $idsChunk = $getChuck($offset)) { + $chunkSelect = clone $select; + $chunkSelect->where($tableId .'.parent_id IN (?)', $idsChunk); + $addresses = $collection->getConnection()->fetchAll($chunkSelect); + foreach ($addresses as $address) { + $this->addRecord((int) $address['parent_id'], (int) $address['entity_id']); + } + } } /** * Check if given address exists for given customer. * - * @param string $addressId - * @param string $forCustomerId + * @param int $addressId + * @param int $forCustomerId * @return bool */ - public function doesExist(string $addressId, string $forCustomerId): bool + public function doesExist(int $addressId, int $forCustomerId): bool { return array_key_exists($forCustomerId, $this->addresses) && in_array( @@ -128,7 +132,7 @@ public function doesExist(string $addressId, string $forCustomerId): bool /** * Pre-load addresses for given customers. * - * @param string[] $forCustomersIds + * @param int[] $forCustomersIds * @return void */ public function prepareAddresses(array $forCustomersIds): void @@ -138,13 +142,7 @@ public function prepareAddresses(array $forCustomersIds): void } $forCustomersIds = array_unique($forCustomersIds); - $customerIdsToUse = []; - foreach ($forCustomersIds as $customerId) { - if (!array_key_exists((string)$customerId, $this->addresses)) { - $customerIdsToUse[] = $customerId; - } - } - + $customerIdsToUse = array_diff($forCustomersIds, array_keys($this->addresses)); $this->loadAddresses($customerIdsToUse); } } diff --git a/app/code/Magento/CustomerImportExport/Model/ResourceModel/Import/Customer/Storage.php b/app/code/Magento/CustomerImportExport/Model/ResourceModel/Import/Customer/Storage.php index 43623019c005e..11f326e6dfc8f 100644 --- a/app/code/Magento/CustomerImportExport/Model/ResourceModel/Import/Customer/Storage.php +++ b/app/code/Magento/CustomerImportExport/Model/ResourceModel/Import/Customer/Storage.php @@ -5,13 +5,10 @@ */ namespace Magento\CustomerImportExport\Model\ResourceModel\Import\Customer; -use Magento\CustomerImportExport\Test\Unit\Model\Import\CustomerCompositeTest; +use Magento\Customer\Model\ResourceModel\Customer\Collection as CustomerCollection; +use Magento\Customer\Model\ResourceModel\Customer\CollectionFactory as CustomerCollectionFactory; use Magento\Framework\DataObject; use Magento\Framework\DB\Select; -use Magento\Customer\Model\ResourceModel\Customer\CollectionFactory as CustomerCollectionFactory; -use Magento\Customer\Model\ResourceModel\Customer\Collection as CustomerCollection; -use Magento\ImportExport\Model\ResourceModel\CollectionByPagesIteratorFactory; -use Magento\ImportExport\Model\ResourceModel\CollectionByPagesIterator; /** * Storage to check existing customers. @@ -39,13 +36,6 @@ class Storage */ protected $_pageSize; - /** - * Collection by pages iterator. - * - * @var CollectionByPagesIterator - */ - protected $_byPagesIterator; - /** * @var CustomerCollectionFactory */ @@ -56,67 +46,63 @@ class Storage */ public $_customerCollection; + /** + * Existing customers store IDs. In form of: + * + * [customer email] => array( + * [website id 1] => store id 1, + * [website id 2] => store id 2, + * ... => ... , + * [website id n] => store id n, + * ) + * + * @var array + */ + private $customerStoreIds = []; + /** * @param CustomerCollectionFactory $collectionFactory - * @param CollectionByPagesIteratorFactory $colIteratorFactory * @param array $data */ public function __construct( CustomerCollectionFactory $collectionFactory, - CollectionByPagesIteratorFactory $colIteratorFactory, array $data = [] ) { $this->_customerCollection = isset( $data['customer_collection'] ) ? $data['customer_collection'] : $collectionFactory->create(); - $this->_pageSize = isset($data['page_size']) ? $data['page_size'] : 0; - $this->_byPagesIterator = isset( - $data['collection_by_pages_iterator'] - ) ? $data['collection_by_pages_iterator'] : $colIteratorFactory->create(); + $this->_pageSize = isset($data['page_size']) ? (int) $data['page_size'] : 0; $this->customerCollectionFactory = $collectionFactory; } /** - * Create new collection to load customer data with proper filters. + * Load customer's data that can be found by given identifiers. * - * @param array[] $customerIdentifiers With keys "email" and "website_id". - * - * @return CustomerCollection + * @param array $customerIdentifiers With keys "email" and "website_id". + * @return void */ - private function prepareCollection(array $customerIdentifiers): CustomerCollection + private function loadCustomersData(array $customerIdentifiers): void { /** @var CustomerCollection $collection */ $collection = $this->customerCollectionFactory->create(); $collection->removeAttributeToSelect(); $select = $collection->getSelect(); $customerTableId = array_keys($select->getPart(Select::FROM))[0]; - $select->where( - $customerTableId .'.email in (?)', - array_map( - function (array $customer) { - return $customer['email']; - }, - $customerIdentifiers - ) - ); - - return $collection; - } - /** - * Load customers' data that can be found by given identifiers. - * - * @param array $customerIdentifiers With keys "email" and "website_id". - * - * @return void - */ - private function loadCustomersData(array $customerIdentifiers) - { - $this->_byPagesIterator->iterate( - $this->prepareCollection($customerIdentifiers), - $this->_pageSize, - [[$this, 'addCustomer']] - ); + $pageSize = $this->_pageSize ?: count($customerIdentifiers); + $getChuck = function (int $offset) use ($customerIdentifiers, $pageSize) { + return array_slice($customerIdentifiers, $offset, $pageSize); + }; + $offset = 0; + for ($chunk = $getChuck($offset); !empty($chunk); $offset += $pageSize, $chunk = $getChuck($offset)) { + $emails = array_column($chunk, 'email'); + $chunkSelect = clone $select; + $chunkSelect->where($customerTableId . '.email IN (?)', $emails); + $customers = $collection->getConnection()->fetchAll($chunkSelect); + foreach ($customers as $customer) { + $this->addCustomerByArray($customer); + } + } } /** @@ -127,11 +113,16 @@ private function loadCustomersData(array $customerIdentifiers) */ public function addCustomerByArray(array $customer): Storage { - $email = strtolower(trim($customer['email'])); + $email = mb_strtolower(trim($customer['email'])); if (!isset($this->_customerIds[$email])) { $this->_customerIds[$email] = []; } - $this->_customerIds[$email][$customer['website_id']] = $customer['entity_id']; + if (!isset($this->customerStoreIds[$email])) { + $this->customerStoreIds[$email] = []; + } + $websiteId = (int) $customer['website_id']; + $this->_customerIds[$email][$websiteId] = (int) $customer['entity_id']; + $this->customerStoreIds[$email][$websiteId] = $customer['store_id'] ?? null; return $this; } @@ -164,11 +155,7 @@ public function addCustomer(DataObject $customer): Storage public function getCustomerId(string $email, int $websiteId) { $email = mb_strtolower($email); - //Trying to load the customer. - if (!array_key_exists($email, $this->_customerIds) || !array_key_exists($websiteId, $this->_customerIds[$email]) - ) { - $this->loadCustomersData([['email' => $email, 'website_id' => $websiteId]]); - } + $this->loadCustomerData($email, $websiteId); if (isset($this->_customerIds[$email][$websiteId])) { return $this->_customerIds[$email][$websiteId]; @@ -177,6 +164,37 @@ public function getCustomerId(string $email, int $websiteId) return false; } + /** + * Get previously loaded customer id. + * + * @param string $email + * @param int $websiteId + * @return int|null + */ + public function getLoadedCustomerId(string $email, int $websiteId): ?int + { + return $this->_customerIds[mb_strtolower($email)][$websiteId] ?? null; + } + + /** + * Find customer store ID for unique pair of email and website ID. + * + * @param string $email + * @param int $websiteId + * @return bool|int + */ + public function getCustomerStoreId(string $email, int $websiteId) + { + $email = mb_strtolower($email); + $this->loadCustomerData($email, $websiteId); + + if (isset($this->customerStoreIds[$email][$websiteId])) { + return $this->customerStoreIds[$email][$websiteId]; + } + + return false; + } + /** * Pre-load customers for future checks. * @@ -189,12 +207,10 @@ public function prepareCustomers(array $customersToFind): void foreach ($customersToFind as $customerToFind) { $email = mb_strtolower($customerToFind['email']); $websiteId = $customerToFind['website_id']; - if (!array_key_exists($email, $this->_customerIds) - || !array_key_exists($websiteId, $this->_customerIds[$email]) - ) { + if (!$this->isLoadedCustomerData($email, $websiteId)) { //Only looking for customers we don't already have ID for. //We need unique identifiers. - $uniqueKey = $email .'_' .$websiteId; + $uniqueKey = $email . '_' . $websiteId; $identifiers[$uniqueKey] = [ 'email' => $email, 'website_id' => $websiteId, @@ -202,8 +218,10 @@ public function prepareCustomers(array $customersToFind): void //Recording that we've searched for a customer. if (!array_key_exists($email, $this->_customerIds)) { $this->_customerIds[$email] = []; + $this->customerStoreIds[$email] = []; } $this->_customerIds[$email][$websiteId] = null; + $this->customerStoreIds[$email][$websiteId] = null; } } if (!$identifiers) { @@ -213,4 +231,31 @@ public function prepareCustomers(array $customersToFind): void //Loading customers data. $this->loadCustomersData($identifiers); } + + /** + * Load customer data if it's not loaded. + * + * @param string $email + * @param int $websiteId + * @return void + */ + private function loadCustomerData(string $email, int $websiteId): void + { + if (!$this->isLoadedCustomerData($email, $websiteId)) { + $this->loadCustomersData([['email' => $email, 'website_id' => $websiteId]]); + } + } + + /** + * Check if customer data is loaded + * + * @param string $email + * @param int $websiteId + * @return bool + */ + private function isLoadedCustomerData(string $email, int $websiteId): bool + { + return array_key_exists($email, $this->_customerIds) + && array_key_exists($websiteId, $this->_customerIds[$email]); + } } diff --git a/app/code/Magento/CustomerImportExport/Test/Unit/Model/Import/AddressTest.php b/app/code/Magento/CustomerImportExport/Test/Unit/Model/Import/AddressTest.php index 126a9e1791779..3df8b1ae5ef72 100644 --- a/app/code/Magento/CustomerImportExport/Test/Unit/Model/Import/AddressTest.php +++ b/app/code/Magento/CustomerImportExport/Test/Unit/Model/Import/AddressTest.php @@ -298,9 +298,6 @@ protected function _createCustomerEntityMock() public function getWebsites($withDefault = false) { $websites = []; - if (!$withDefault) { - unset($websites[0]); - } foreach ($this->_websites as $id => $code) { if (!$withDefault && $id == \Magento\Store\Model\Store::DEFAULT_STORE_ID) { continue; @@ -330,97 +327,6 @@ public function iterate(\Magento\Framework\Data\Collection $collection, $pageSiz } } - /** - * Create mock for custom behavior test - * - * @return Address|\PHPUnit_Framework_MockObject_MockObject - */ - protected function _getModelMockForTestImportDataWithCustomBehaviour() - { - // input data - $customBehaviorRows = [ - [ - AbstractEntity::COLUMN_ACTION => 'update', - Address::COLUMN_ADDRESS_ID => $this->_customBehaviour['update_id'], - ], - [ - AbstractEntity::COLUMN_ACTION => AbstractEntity::COLUMN_ACTION_VALUE_DELETE, - Address::COLUMN_ADDRESS_ID => $this->_customBehaviour['delete_id'] - ], - ]; - $updateResult = [ - 'entity_row_new' => [], - 'entity_row_update' => $this->_customBehaviour['update_id'], - 'attributes' => [], - 'defaults' => [], - ]; - // entity adapter mock - $modelMock = $this->createPartialMock( - \Magento\CustomerImportExport\Model\Import\Address::class, - [ - 'validateRow', - '_prepareDataForUpdate', - '_saveAddressEntities', - '_saveAddressAttributes', - '_saveCustomerDefaults', - '_deleteAddressEntities', - '_mergeEntityAttributes', - 'getErrorAggregator', - 'getCustomerStorage', - 'prepareCustomerData', - ] - ); - //Adding behaviours - $availableBehaviors = new \ReflectionProperty($modelMock, '_availableBehaviors'); - $availableBehaviors->setAccessible(true); - $availableBehaviors->setValue($modelMock, $this->_availableBehaviors); - // mock to imitate data source model - $dataSourceMock = $this->createPartialMock( - \Magento\ImportExport\Model\ResourceModel\Import\Data::class, - ['getNextBunch', '__wakeup', 'getIterator'] - ); - $dataSourceMock->expects($this->at(0))->method('getNextBunch')->will($this->returnValue($customBehaviorRows)); - $dataSourceMock->expects($this->at(1))->method('getNextBunch')->will($this->returnValue(null)); - $dataSourceMock->expects($this->any()) - ->method('getIterator') - ->willReturn($this->getMockForAbstractClass(\Iterator::class)); - - $dataSourceModel = new \ReflectionProperty( - \Magento\CustomerImportExport\Model\Import\Address::class, - '_dataSourceModel' - ); - $dataSourceModel->setAccessible(true); - $dataSourceModel->setValue($modelMock, $dataSourceMock); - // mock expects for entity adapter - $modelMock->expects($this->any())->method('validateRow')->will($this->returnValue(true)); - $modelMock->expects($this->any()) - ->method('getErrorAggregator') - ->will($this->returnValue($this->errorAggregator)); - $modelMock->expects($this->any())->method('_prepareDataForUpdate')->will($this->returnValue($updateResult)); - $modelMock->expects( - $this->any() - )->method( - '_saveAddressEntities' - )->will( - $this->returnCallback([$this, 'validateSaveAddressEntities']) - ); - $modelMock->expects($this->any())->method('_saveAddressAttributes')->will($this->returnValue($modelMock)); - $modelMock->expects($this->any())->method('_saveCustomerDefaults')->will($this->returnValue($modelMock)); - $modelMock->expects( - $this->any() - )->method( - '_deleteAddressEntities' - )->will( - $this->returnCallback([$this, 'validateDeleteAddressEntities']) - ); - $modelMock->expects($this->any())->method('_mergeEntityAttributes')->will($this->returnValue([])); - $modelMock->expects($this->any()) - ->method('getCustomerStorage') - ->willReturn($this->_createCustomerStorageMock()); - - return $modelMock; - } - /** * Create mock for customer address model class * @@ -449,7 +355,9 @@ protected function _getModelMock() new \Magento\Framework\Stdlib\DateTime(), $this->createMock(\Magento\Customer\Model\Address\Validator\Postcode::class), $this->_getModelDependencies(), - $this->countryWithWebsites + $this->countryWithWebsites, + $this->createMock(\Magento\CustomerImportExport\Model\ResourceModel\Import\Address\Storage::class), + $this->createMock(\Magento\Customer\Model\Indexer\Processor::class) ); $property = new \ReflectionProperty($modelMock, '_availableBehaviors'); @@ -606,20 +514,6 @@ public function testGetDefaultAddressAttributeMapping() ); } - /** - * Test if correct methods are invoked according to different custom behaviours - * - * @covers \Magento\CustomerImportExport\Model\Import\Address::_importData - */ - public function testImportDataWithCustomBehaviour() - { - $this->_model = $this->_getModelMockForTestImportDataWithCustomBehaviour(); - $this->_model->setParameters(['behavior' => \Magento\ImportExport\Model\Import::BEHAVIOR_CUSTOM]); - - // validation in validateSaveAddressEntities and validateDeleteAddressEntities - $this->_model->importData(); - } - /** * Validation method for _saveAddressEntities (callback for _saveAddressEntities) * diff --git a/app/code/Magento/CustomerImportExport/Test/Unit/Model/Import/CustomerCompositeTest.php b/app/code/Magento/CustomerImportExport/Test/Unit/Model/Import/CustomerCompositeTest.php index 1b900c2139588..7aff0f911c2b0 100644 --- a/app/code/Magento/CustomerImportExport/Test/Unit/Model/Import/CustomerCompositeTest.php +++ b/app/code/Magento/CustomerImportExport/Test/Unit/Model/Import/CustomerCompositeTest.php @@ -15,7 +15,7 @@ use Magento\ImportExport\Model\Import\Source\Csv; /** - * Customer composite test + * The test for Customer composite model * * @SuppressWarnings(PHPMD.CouplingBetweenObjects) */ @@ -88,6 +88,12 @@ class CustomerCompositeTest extends \PHPUnit\Framework\TestCase */ protected $errorFactory; + /** + * @var \Magento\Customer\Model\Indexer\Processor + * |\PHPUnit\Framework\MockObject\MockObject + */ + private $indexerProcessor; + /** * Expected prepared data after method CustomerComposite::_prepareRowForDb * @@ -141,6 +147,7 @@ protected function setUp() ->getMock(); $this->_scopeConfigMock = $this->createMock(\Magento\Framework\App\Config\ScopeConfigInterface::class); + $this->indexerProcessor = $this->createMock(\Magento\Customer\Model\Indexer\Processor::class); } /** @@ -159,6 +166,7 @@ protected function _createModelMock($data) $this->_dataFactory, $this->_customerFactory, $this->_addressFactory, + $this->indexerProcessor, $data ); } diff --git a/app/code/Magento/CustomerImportExport/Test/Unit/Model/Import/CustomerTest.php b/app/code/Magento/CustomerImportExport/Test/Unit/Model/Import/CustomerTest.php deleted file mode 100644 index 9a7183d5b5f72..0000000000000 --- a/app/code/Magento/CustomerImportExport/Test/Unit/Model/Import/CustomerTest.php +++ /dev/null @@ -1,236 +0,0 @@ -<?php -/** - * Copyright © Magento, Inc. All rights reserved. - * See COPYING.txt for license details. - */ - -/** - * Test class for \Magento\CustomerImportExport\Model\Import\Customer - */ -namespace Magento\CustomerImportExport\Test\Unit\Model\Import; - -use Magento\CustomerImportExport\Model\Import\Customer; -use Magento\CustomerImportExport\Model\ResourceModel\Import\Customer\Storage; - -class CustomerTest extends \PHPUnit\Framework\TestCase -{ - /** - * Customer entity import model - * - * @var Customer|\PHPUnit_Framework_MockObject_MockObject - */ - protected $_model; - - /** - * Available behaviours - * - * @var array - */ - protected $_availableBehaviors = [ - \Magento\ImportExport\Model\Import::BEHAVIOR_ADD_UPDATE, - \Magento\ImportExport\Model\Import::BEHAVIOR_DELETE, - \Magento\ImportExport\Model\Import::BEHAVIOR_CUSTOM, - ]; - - /** - * Custom behavior input rows - * - * @var array - */ - protected $_inputRows = [ - 'create' => [ - Customer::COLUMN_ACTION => 'create', - Customer::COLUMN_EMAIL => 'create@email.com', - Customer::COLUMN_WEBSITE => 'website1', - ], - 'update' => [ - Customer::COLUMN_ACTION => 'update', - Customer::COLUMN_EMAIL => 'update@email.com', - Customer::COLUMN_WEBSITE => 'website1', - ], - 'delete' => [ - Customer::COLUMN_ACTION => Customer::COLUMN_ACTION_VALUE_DELETE, - Customer::COLUMN_EMAIL => 'delete@email.com', - Customer::COLUMN_WEBSITE => 'website1', - ], - ]; - - /** - * Customer ids for all custom behavior input rows - * - * @var array - */ - protected $_customerIds = ['create' => 1, 'update' => 2, 'delete' => 3]; - - /** - * Unset entity adapter model - */ - protected function tearDown() - { - unset($this->_model); - - parent::tearDown(); - } - - /** - * Create mock for import with custom behavior test - * - * @return Customer|\PHPUnit_Framework_MockObject_MockObject - */ - protected function _getModelMockForTestImportDataWithCustomBehaviour() - { - // entity adapter mock - $modelMock = $this->getMockBuilder(\Magento\CustomerImportExport\Model\Import\Customer::class) - ->disableOriginalConstructor() - ->setMethods( - [ - 'validateRow', - '_getCustomerId', - '_prepareDataForUpdate', - '_saveCustomerEntities', - '_saveCustomerAttributes', - '_deleteCustomerEntities', - 'getErrorAggregator', - 'getCustomerStorage', - ] - ) - ->getMock(); - - $errorAggregator = $this->createPartialMock( - \Magento\ImportExport\Model\Import\ErrorProcessing\ProcessingErrorAggregator::class, - ['hasToBeTerminated'] - ); - - $availableBehaviors = new \ReflectionProperty($modelMock, '_availableBehaviors'); - $availableBehaviors->setAccessible(true); - $availableBehaviors->setValue($modelMock, $this->_availableBehaviors); - - // mock to imitate data source model - $dataSourceModelMock = $this->getMockBuilder(\Magento\ImportExport\Model\ResourceModel\Import\Data::class) - ->disableOriginalConstructor() - ->setMethods([ - 'getNextBunch', - '__wakeup', - ]) - ->getMock(); - - $dataSourceModelMock->expects($this->at(0)) - ->method('getNextBunch') - ->will($this->returnValue($this->_inputRows)); - $dataSourceModelMock->expects($this->at(1)) - ->method('getNextBunch') - ->will($this->returnValue(null)); - - $property = new \ReflectionProperty( - \Magento\CustomerImportExport\Model\Import\Customer::class, - '_dataSourceModel' - ); - $property->setAccessible(true); - $property->setValue($modelMock, $dataSourceModelMock); - - $modelMock->expects($this->any()) - ->method('validateRow') - ->will($this->returnValue(true)); - - $modelMock->expects($this->any()) - ->method('_getCustomerId') - ->will($this->returnValue($this->_customerIds['delete'])); - - $modelMock->expects($this->any()) - ->method('_prepareDataForUpdate') - ->will($this->returnCallback([$this, 'prepareForUpdateMock'])); - - $modelMock->expects($this->any()) - ->method('_saveCustomerEntities') - ->will($this->returnCallback([$this, 'validateSaveCustomerEntities'])); - - $modelMock->expects($this->any()) - ->method('_saveCustomerAttributes') - ->will($this->returnValue($modelMock)); - - $modelMock->expects($this->any()) - ->method('_deleteCustomerEntities') - ->will($this->returnCallback([$this, 'validateDeleteCustomerEntities'])); - - $modelMock->expects($this->any()) - ->method('getErrorAggregator') - ->will($this->returnValue($errorAggregator)); - /** @var \PHPUnit_Framework_MockObject_MockObject $storageMock */ - $storageMock = $this->createMock(Storage::class); - $storageMock->expects($this->any())->method('prepareCustomers'); - $modelMock->expects($this->any()) - ->method('getCustomerStorage') - ->willReturn($storageMock); - - return $modelMock; - } - - /** - * Test whether correct methods are invoked in case of custom behaviour for each row in action column - */ - public function testImportDataWithCustomBehaviour() - { - $this->_model = $this->_getModelMockForTestImportDataWithCustomBehaviour(); - $this->_model->setParameters(['behavior' => \Magento\ImportExport\Model\Import::BEHAVIOR_CUSTOM]); - - // validation in validateSaveCustomerEntities and validateDeleteCustomerEntities - $this->_model->importData(); - } - - /** - * Emulate data preparing depending on value in row action column - * - * @param array $rowData - * @return int - */ - public function prepareForUpdateMock(array $rowData) - { - $preparedResult = [ - Customer::ENTITIES_TO_CREATE_KEY => [], - Customer::ENTITIES_TO_UPDATE_KEY => [], - Customer::ATTRIBUTES_TO_SAVE_KEY => ['table' => []], - ]; - - $actionColumnKey = Customer::COLUMN_ACTION; - if ($rowData[$actionColumnKey] == 'create') { - $preparedResult[Customer::ENTITIES_TO_CREATE_KEY] = [ - ['entity_id' => $this->_customerIds['create']], - ]; - } elseif ($rowData[$actionColumnKey] == 'update') { - $preparedResult[Customer::ENTITIES_TO_UPDATE_KEY] = [ - ['entity_id' => $this->_customerIds['update']], - ]; - } - - return $preparedResult; - } - - /** - * Validation method for _saveCustomerEntities - * - * @param array $entitiesToCreate - * @param array $entitiesToUpdate - * @return Customer|\PHPUnit_Framework_MockObject_MockObject - */ - public function validateSaveCustomerEntities(array $entitiesToCreate, array $entitiesToUpdate) - { - $this->assertCount(1, $entitiesToCreate); - $this->assertEquals($this->_customerIds['create'], $entitiesToCreate[0]['entity_id']); - $this->assertCount(1, $entitiesToUpdate); - $this->assertEquals($this->_customerIds['update'], $entitiesToUpdate[0]['entity_id']); - return $this->_model; - } - - /** - * Validation method for _deleteCustomerEntities - * - * @param array $customerIdsToDelete - * @return Customer|\PHPUnit_Framework_MockObject_MockObject - */ - public function validateDeleteCustomerEntities(array $customerIdsToDelete) - { - $this->assertCount(1, $customerIdsToDelete); - $this->assertEquals($this->_customerIds['delete'], $customerIdsToDelete[0]); - return $this->_model; - } -} diff --git a/app/code/Magento/CustomerImportExport/Test/Unit/Model/ResourceModel/Import/Customer/StorageTest.php b/app/code/Magento/CustomerImportExport/Test/Unit/Model/ResourceModel/Import/Customer/StorageTest.php deleted file mode 100644 index 1f2f9f3fed583..0000000000000 --- a/app/code/Magento/CustomerImportExport/Test/Unit/Model/ResourceModel/Import/Customer/StorageTest.php +++ /dev/null @@ -1,139 +0,0 @@ -<?php -/** - * Copyright © Magento, Inc. All rights reserved. - * See COPYING.txt for license details. - */ - -/** - * Test class for \Magento\CustomerImportExport\Model\ResourceModel\Import\Customer\Storage - */ -namespace Magento\CustomerImportExport\Test\Unit\Model\ResourceModel\Import\Customer; - -use Magento\CustomerImportExport\Model\ResourceModel\Import\Customer\Storage; -use Magento\Customer\Model\ResourceModel\Customer\Collection; -use Magento\Customer\Model\ResourceModel\Customer\CollectionFactory; -use Magento\ImportExport\Model\ResourceModel\CollectionByPagesIteratorFactory; -use Magento\Framework\DataObject; -use Magento\Framework\DB\Select; -use Magento\ImportExport\Model\ResourceModel\CollectionByPagesIterator; - -class StorageTest extends \PHPUnit\Framework\TestCase -{ - /** - * @var Storage - */ - private $model; - - /** - * @var CollectionByPagesIterator|\PHPUnit_Framework_MockObject_MockObject - */ - private $iteratorMock; - - /** - * @var Collection|\PHPUnit_Framework_MockObject_MockObject - */ - private $collectionMock; - - protected function setUp() - { - $this->iteratorMock = $this->createMock( - CollectionByPagesIterator::class - ); - /** @var \PHPUnit_Framework_MockObject_MockObject|CollectionByPagesIteratorFactory $iteratorFactoryMock */ - $iteratorFactoryMock = $this->createMock( - CollectionByPagesIteratorFactory::class - ); - $iteratorFactoryMock->expects($this->any()) - ->method('create') - ->willReturn($this->iteratorMock); - $this->collectionMock = $this->createMock(Collection::class); - /** @var CollectionFactory|\PHPUnit_Framework_MockObject_MockObject $collectionFactoryMock */ - $collectionFactoryMock = $this->createMock( - CollectionFactory::class - ); - $collectionFactoryMock->expects($this->any()) - ->method('create') - ->willReturn($this->collectionMock); - /** @var \PHPUnit_Framework_MockObject_MockObject $selectMock */ - $selectMock = $this->createMock(Select::class); - $selectMock->expects($this->any()) - ->method('getPart') - ->with(Select::FROM) - ->willReturn(['e' => []]); - $this->collectionMock->expects($this->any()) - ->method('getSelect') - ->willReturn($selectMock); - - $this->model = new Storage( - $collectionFactoryMock, - $iteratorFactoryMock, - [] - ); - } - - protected function tearDown() - { - unset($this->model); - } - - public function testAddCustomerByArray() - { - $propertyName = '_customerIds'; - $customer = $this->_addCustomerToStorage(); - - $this->assertAttributeCount(1, $propertyName, $this->model); - $expectedCustomerData = [$customer['website_id'] => $customer['entity_id']]; - $this->assertAttributeContains($expectedCustomerData, $propertyName, $this->model); - } - - public function testGetCustomerId() - { - $existingEmail = 'test@magento.com'; - $existingWebsiteId = 0; - $existingId = 1; - $nonExistingEmail = 'test1@magento.com'; - $nonExistingWebsiteId = 2; - - $this->iteratorMock->expects($this->at(0)) - ->method('iterate') - ->willReturnCallback( - function (...$args) use ( - $existingId, - $existingEmail, - $existingWebsiteId - ) { - /** @var callable $callable */ - foreach ($args[2] as $callable) { - $callable( - new DataObject([ - 'id' => $existingId, - 'email' => $existingEmail, - 'website_id' => $existingWebsiteId, - ]) - ); - } - } - ); - $this->assertEquals( - $existingId, - $this->model->getCustomerId($existingEmail, $existingWebsiteId) - ); - $this->assertFalse( - $this->model->getCustomerId( - $nonExistingEmail, - $nonExistingWebsiteId - ) - ); - } - - /** - * @return array - */ - protected function _addCustomerToStorage() - { - $customer = ['entity_id' => 1, 'website_id' => 1, 'email' => 'test@test.com']; - $this->model->addCustomerByArray($customer); - - return $customer; - } -} diff --git a/app/code/Magento/CustomerImportExport/registration.php b/app/code/Magento/CustomerImportExport/registration.php index 1cfb0ed8899ad..68a23fe6be57c 100644 --- a/app/code/Magento/CustomerImportExport/registration.php +++ b/app/code/Magento/CustomerImportExport/registration.php @@ -4,6 +4,6 @@ * See COPYING.txt for license details. */ -use \Magento\Framework\Component\ComponentRegistrar; +use Magento\Framework\Component\ComponentRegistrar; ComponentRegistrar::register(ComponentRegistrar::MODULE, 'Magento_CustomerImportExport', __DIR__); diff --git a/app/code/Magento/Deploy/Model/Mode.php b/app/code/Magento/Deploy/Model/Mode.php index fe24fb297e978..0d97042af460b 100644 --- a/app/code/Magento/Deploy/Model/Mode.php +++ b/app/code/Magento/Deploy/Model/Mode.php @@ -205,7 +205,7 @@ public function enableDefaultMode() public function getMode() { $env = $this->reader->load(); - return isset($env[State::PARAM_MODE]) ? $env[State::PARAM_MODE] : null; + return $env[State::PARAM_MODE] ?? null; } /** diff --git a/app/code/Magento/Deploy/Package/Package.php b/app/code/Magento/Deploy/Package/Package.php index 423f3072c4620..2a83d0d4c56ec 100644 --- a/app/code/Magento/Deploy/Package/Package.php +++ b/app/code/Magento/Deploy/Package/Package.php @@ -219,7 +219,7 @@ public function isVirtual() */ public function getParam($name) { - return isset($this->params[$name]) ? $this->params[$name] : null; + return $this->params[$name] ?? null; } /** @@ -253,7 +253,7 @@ public function getThemeModel() */ public function getFile($fileId) { - return isset($this->files[$fileId]) ? $this->files[$fileId] : false; + return $this->files[$fileId] ?? false; } /** @@ -443,12 +443,11 @@ public function getResultMap() */ public function getParentMap() { - $map = []; + $map = [[]]; foreach ($this->getParentPackages() as $parentPackage) { - // phpcs:ignore Magento2.Performance.ForeachArrayMerge.ForeachArrayMerge - $map = array_merge($map, $parentPackage->getMap()); + $map[] = $parentPackage->getMap(); } - return $map; + return array_merge(...$map); } /** @@ -462,10 +461,8 @@ public function getParentFiles($type = null) $files = [[]]; foreach ($this->getParentPackages() as $parentPackage) { if ($type === null) { - // phpcs:ignore Magento2.Performance.ForeachArrayMerge.ForeachArrayMerge $files[] = $parentPackage->getFiles(); } else { - // phpcs:ignore Magento2.Performance.ForeachArrayMerge.ForeachArrayMerge $files[] = $parentPackage->getFilesByType($type); } } diff --git a/app/code/Magento/Deploy/Package/PackagePool.php b/app/code/Magento/Deploy/Package/PackagePool.php index b9c0418df860a..9057f50fb3c91 100644 --- a/app/code/Magento/Deploy/Package/PackagePool.php +++ b/app/code/Magento/Deploy/Package/PackagePool.php @@ -60,16 +60,20 @@ public function __construct( } /** + * Return package + * * @param string $path * @return Package|null */ public function getPackage($path) { $this->collect(); - return isset($this->packages[$path]) ? $this->packages[$path] : null; + return $this->packages[$path] ?? null; } /** + * Return packages + * * @return Package[] */ public function getPackages() @@ -79,6 +83,8 @@ public function getPackages() } /** + * Return theme model + * * @param string $areaCode * @param string $themePath * @return ThemeInterface|null @@ -93,6 +99,8 @@ public function getThemeModel($areaCode, $themePath) } /** + * Return packages from deployment + * * @param array $options * @return Package[] */ @@ -140,6 +148,8 @@ private function isAncestorForDeployedPackages(Package $excludedPackage, array $ } /** + * Return theme by full path + * * @param string $fullPath * @return ThemeInterface|null */ @@ -154,6 +164,8 @@ private function getThemeByFullPath($fullPath) } /** + * Collect packages + * * @param bool $recollect * @return void */ @@ -244,6 +256,8 @@ private function checkPackageSkip(Package $package, array $options) } /** + * Check if can deploy area + * * @param Package $package * @param array $options * @return bool @@ -264,6 +278,8 @@ private function canDeployArea(Package $package, array $options) } /** + * Verify can deploy theme + * * @param Package $package * @param array $options * @return bool @@ -281,6 +297,8 @@ private function canDeployTheme(Package $package, array $options) } /** + * Verify can deploy locale + * * @param Package $package * @param array $options * @return bool @@ -297,6 +315,8 @@ private function canDeployLocale(Package $package, array $options) } /** + * Check if included entity + * * @param string $entity * @param array $includedEntities * @param array $excludedEntities @@ -316,16 +336,20 @@ private function isIncluded($entity, array $includedEntities, array $excludedEnt } /** + * Return option by name + * * @param string $name * @param array $options * @return mixed|null */ private function getOption($name, $options) { - return isset($options[$name]) ? $options[$name] : null; + return $options[$name] ?? null; } /** + * Ensure package exist + * * @param array $params * @return void */ diff --git a/app/code/Magento/Deploy/Package/Processor/PreProcessor/Css.php b/app/code/Magento/Deploy/Package/Processor/PreProcessor/Css.php index b3461e3ab4a22..42775a2e2f6bf 100644 --- a/app/code/Magento/Deploy/Package/Processor/PreProcessor/Css.php +++ b/app/code/Magento/Deploy/Package/Processor/PreProcessor/Css.php @@ -3,12 +3,14 @@ * Copyright © Magento, Inc. All rights reserved. * See COPYING.txt for license details. */ + namespace Magento\Deploy\Package\Processor\PreProcessor; use Magento\Deploy\Console\DeployStaticOptions; use Magento\Deploy\Package\Package; use Magento\Deploy\Package\PackageFile; use Magento\Deploy\Package\Processor\ProcessorInterface; +use Magento\Framework\Css\PreProcessor\Instruction\Import; use Magento\Framework\Filesystem; use Magento\Framework\Filesystem\Directory\ReadInterface; use Magento\Framework\App\Filesystem\DirectoryList; @@ -92,8 +94,7 @@ public function process(Package $package, array $options) } /** - * Checks if there are imports of CSS files or images within the given CSS file - * which exists in the current package + * Checks if there are imports of CSS files or images within the given CSS file which exists in the current package * * @param PackageFile $parentFile * @param Package $package @@ -126,6 +127,7 @@ private function hasOverrides(PackageFile $parentFile, Package $package) * @param string $filePath * @param string $fullPath * @return void + * phpcs:disable Magento2.Functions.DiscouragedFunction */ private function buildMap($packagePath, $filePath, $fullPath) { @@ -141,11 +143,7 @@ private function buildMap($packagePath, $filePath, $fullPath) $packagePath . '/' . pathinfo($filePath, PATHINFO_DIRNAME) . '/' . $matchContent['path'] ); }; - preg_replace_callback( - \Magento\Framework\Css\PreProcessor\Instruction\Import::REPLACE_PATTERN, - $callback, - $content - ); + preg_replace_callback(Import::REPLACE_PATTERN, $callback, $content); preg_match_all(CssResolver::REGEX_CSS_RELATIVE_URLS, $content, $matches); if (!empty($matches[0]) && !empty($matches[1])) { @@ -177,6 +175,7 @@ private function buildMap($packagePath, $filePath, $fullPath) * * @param string $fileName * @return array + * phpcs:disable Magento2.Performance.ForeachArrayMerge */ private function collectFileMap($fileName) { diff --git a/app/code/Magento/Deploy/Service/DeployStaticContent.php b/app/code/Magento/Deploy/Service/DeployStaticContent.php index b6333d6fec71e..6c8d804c61efe 100644 --- a/app/code/Magento/Deploy/Service/DeployStaticContent.php +++ b/app/code/Magento/Deploy/Service/DeployStaticContent.php @@ -156,7 +156,7 @@ public function deploy(array $options) */ private function getProcessesAmount(array $options) { - return isset($options[Options::JOBS_AMOUNT]) ? (int)$options[Options::JOBS_AMOUNT] : 0; + return (int)($options[Options::JOBS_AMOUNT] ?? 0); } /** diff --git a/app/code/Magento/Deploy/Source/SourcePool.php b/app/code/Magento/Deploy/Source/SourcePool.php index 3179a0a5c0aa3..2c391e78e94c8 100644 --- a/app/code/Magento/Deploy/Source/SourcePool.php +++ b/app/code/Magento/Deploy/Source/SourcePool.php @@ -44,6 +44,6 @@ public function getAll() */ public function getSource($name) { - return isset($this->sources[$name]) ? $this->sources[$name] : null; + return $this->sources[$name] ?? null; } } diff --git a/app/code/Magento/Deploy/registration.php b/app/code/Magento/Deploy/registration.php index 138d03fd303cf..6cd92601dff88 100644 --- a/app/code/Magento/Deploy/registration.php +++ b/app/code/Magento/Deploy/registration.php @@ -4,6 +4,6 @@ * See COPYING.txt for license details. */ -use \Magento\Framework\Component\ComponentRegistrar; +use Magento\Framework\Component\ComponentRegistrar; ComponentRegistrar::register(ComponentRegistrar::MODULE, 'Magento_Deploy', __DIR__); diff --git a/app/code/Magento/Developer/Console/Command/GeneratePatchCommand.php b/app/code/Magento/Developer/Console/Command/GeneratePatchCommand.php index 78531c7e6c22c..5800f7162b7f0 100644 --- a/app/code/Magento/Developer/Console/Command/GeneratePatchCommand.php +++ b/app/code/Magento/Developer/Console/Command/GeneratePatchCommand.php @@ -7,13 +7,12 @@ namespace Magento\Developer\Console\Command; -use Magento\Framework\App\ObjectManager; use Magento\Framework\Component\ComponentRegistrar; use Magento\Framework\Console\Cli; use Magento\Framework\Exception\FileSystemException; -use Magento\Framework\Filesystem\DirectoryList; use Magento\Framework\Filesystem\Directory\ReadFactory; use Magento\Framework\Filesystem\Directory\WriteFactory; +use Magento\Framework\Filesystem\DirectoryList; use Symfony\Component\Console\Command\Command; use Symfony\Component\Console\Input\InputArgument; use Symfony\Component\Console\Input\InputInterface; @@ -58,20 +57,20 @@ class GeneratePatchCommand extends Command * GeneratePatchCommand constructor. * * @param ComponentRegistrar $componentRegistrar - * @param DirectoryList|null $directoryList - * @param ReadFactory|null $readFactory - * @param WriteFactory|null $writeFactory + * @param DirectoryList $directoryList + * @param ReadFactory $readFactory + * @param WriteFactory $writeFactory */ public function __construct( ComponentRegistrar $componentRegistrar, - DirectoryList $directoryList = null, - ReadFactory $readFactory = null, - WriteFactory $writeFactory = null + DirectoryList $directoryList, + ReadFactory $readFactory, + WriteFactory $writeFactory ) { $this->componentRegistrar = $componentRegistrar; - $this->directoryList = $directoryList ?: ObjectManager::getInstance()->get(DirectoryList::class); - $this->readFactory = $readFactory ?: ObjectManager::getInstance()->get(ReadFactory::class); - $this->writeFactory = $writeFactory ?: ObjectManager::getInstance()->get(WriteFactory::class); + $this->directoryList = $directoryList; + $this->readFactory = $readFactory; + $this->writeFactory = $writeFactory; parent::__construct(); } @@ -120,6 +119,7 @@ protected function configure() * * @param InputInterface $input * @param OutputInterface $output + * * @return int * @throws FileSystemException */ @@ -194,8 +194,7 @@ protected function execute(InputInterface $input, OutputInterface $output): int private function getPatchTemplate(): string { $read = $this->readFactory->create(__DIR__ . '/'); - $content = $read->readFile('patch_template.php.dist'); - return $content; + return $read->readFile('patch_template.php.dist'); } /** @@ -207,7 +206,6 @@ private function getPatchTemplate(): string private function getRevertMethodTemplate(): string { $read = $this->readFactory->create(__DIR__ . '/'); - $content = $read->readFile('template_revert_function.php.dist'); - return $content; + return $read->readFile('template_revert_function.php.dist'); } } diff --git a/app/code/Magento/Developer/Console/Command/TemplateHintsStatusCommand.php b/app/code/Magento/Developer/Console/Command/TemplateHintsStatusCommand.php new file mode 100644 index 0000000000000..bc44df911213b --- /dev/null +++ b/app/code/Magento/Developer/Console/Command/TemplateHintsStatusCommand.php @@ -0,0 +1,87 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +declare(strict_types=1); + +namespace Magento\Developer\Console\Command; + +use Magento\Framework\App\Config\ReinitableConfigInterface; +use Magento\Framework\App\Config\ScopeConfigInterface; +use Magento\Framework\Console\Cli; +use Symfony\Component\Console\Command\Command; +use Symfony\Component\Console\Input\InputInterface; +use Symfony\Component\Console\Output\OutputInterface; + +/** + * Command to show frontend template hints status + */ +class TemplateHintsStatusCommand extends Command +{ + const COMMAND_NAME = 'dev:template-hints:status'; + const TEMPLATE_HINTS_STOREFRONT_PATH = 'dev/debug/template_hints_storefront'; + + /** + * @var ScopeConfigInterface + */ + private $scopeConfig; + + /** + * @var ReinitableConfigInterface + */ + private $reinitableConfig; + + /** + * Initialize dependencies. + * + * @param ScopeConfigInterface $scopeConfig + * @param ReinitableConfigInterface $reinitableConfig + */ + public function __construct( + ScopeConfigInterface $scopeConfig, + ReinitableConfigInterface $reinitableConfig + ) { + parent::__construct(); + $this->scopeConfig = $scopeConfig; + $this->reinitableConfig = $reinitableConfig; + } + + /** + * @inheritdoc + */ + public function configure() + { + $this->setName(self::COMMAND_NAME) + ->setDescription('Show frontend template hints status.'); + + parent::configure(); + } + + /** + * @inheritdoc + * @throws \InvalidArgumentException + */ + public function execute(InputInterface $input, OutputInterface $output) + { + $this->reinitableConfig->reinit(); + $templateHintsStatus = + ($this->isTemplateHintsEnabled()) + ? 'enabled' + : 'disabled'; + $templateHintsMessage = __("Template hints are %status", ['status' => $templateHintsStatus]); + $output->writeln("<info>$templateHintsMessage</info>"); + + return Cli::RETURN_SUCCESS; + } + + /** + * Check if template hints enabled + * + * @return bool + */ + private function isTemplateHintsEnabled(): bool + { + return $this->scopeConfig->isSetFlag(self::TEMPLATE_HINTS_STOREFRONT_PATH, 'default'); + } +} diff --git a/app/code/Magento/Developer/Console/Command/XmlCatalogGenerateCommand.php b/app/code/Magento/Developer/Console/Command/XmlCatalogGenerateCommand.php index 323b8e1016291..9e473ccaa2d92 100644 --- a/app/code/Magento/Developer/Console/Command/XmlCatalogGenerateCommand.php +++ b/app/code/Magento/Developer/Console/Command/XmlCatalogGenerateCommand.php @@ -6,7 +6,12 @@ namespace Magento\Developer\Console\Command; +use Magento\Developer\Model\XmlCatalog\Format\FormatInterface; +use Magento\Framework\App\Utility\Files; +use Magento\Framework\Config\Dom\UrnResolver; +use Magento\Framework\Console\Cli; use Magento\Framework\Exception\InputException; +use Magento\Framework\Filesystem\Directory\ReadFactory; use Symfony\Component\Console\Command\Command; use Symfony\Component\Console\Input\InputArgument; use Symfony\Component\Console\Input\InputOption; @@ -34,37 +39,37 @@ class XmlCatalogGenerateCommand extends Command const IDE_FILE_PATH_ARGUMENT = 'path'; /** - * @var \Magento\Framework\App\Utility\Files + * @var Files */ private $filesUtility; /** - * @var \Magento\Framework\Config\Dom\UrnResolver + * @var UrnResolver */ private $urnResolver; /** - * @var \Magento\Framework\Filesystem\Directory\ReadFactory + * @var ReadFactory */ private $readFactory; /** * Supported formats * - * @var \Magento\Developer\Model\XmlCatalog\Format\FormatInterface[] + * @var FormatInterface[] */ private $formats; /** - * @param \Magento\Framework\App\Utility\Files $filesUtility - * @param \Magento\Framework\Config\Dom\UrnResolver $urnResolver - * @param \Magento\Framework\Filesystem\Directory\ReadFactory $readFactory - * @param \Magento\Developer\Model\XmlCatalog\Format\FormatInterface[] $formats + * @param Files $filesUtility + * @param UrnResolver $urnResolver + * @param ReadFactory $readFactory + * @param FormatInterface[] $formats */ public function __construct( - \Magento\Framework\App\Utility\Files $filesUtility, - \Magento\Framework\Config\Dom\UrnResolver $urnResolver, - \Magento\Framework\Filesystem\Directory\ReadFactory $readFactory, + Files $filesUtility, + UrnResolver $urnResolver, + ReadFactory $readFactory, array $formats = [] ) { $this->filesUtility = $filesUtility; @@ -75,7 +80,7 @@ public function __construct( } /** - * {@inheritdoc} + * @inheritdoc */ protected function configure() { @@ -86,7 +91,7 @@ protected function configure() self::IDE_OPTION, null, InputOption::VALUE_REQUIRED, - 'Format in which catalog will be generated. Supported: ['. + 'Format in which catalog will be generated. Supported: [' . implode(', ', $this->getSupportedFormats()) . ']', 'phpstorm' ), @@ -111,19 +116,21 @@ private function getUrnDictionary(OutputInterface $output) $files = $this->filesUtility->getXmlCatalogFiles('*.xml'); $files = array_merge($files, $this->filesUtility->getXmlCatalogFiles('*.xsd')); - $urns = []; + $urns = [[]]; foreach ($files as $file) { + // phpcs:ignore Magento2.Functions.DiscouragedFunction $fileDir = dirname($file[0]); + // phpcs:ignore Magento2.Functions.DiscouragedFunction $fileName = basename($file[0]); $read = $this->readFactory->create($fileDir); $content = $read->readFile($fileName); $matches = []; preg_match_all('/schemaLocation="(urn\:magento\:[^"]*)"/i', $content, $matches); if (isset($matches[1])) { - $urns = array_merge($urns, $matches[1]); + $urns[] = $matches[1]; } } - $urns = array_unique($urns); + $urns = array_unique(array_merge(...$urns)); $paths = []; foreach ($urns as $urn) { try { @@ -139,27 +146,32 @@ private function getUrnDictionary(OutputInterface $output) } /** - * {@inheritdoc} + * @inheritdoc + * * @throws \InvalidArgumentException */ protected function execute(InputInterface $input, OutputInterface $output) { $ideName = $input->getOption(self::IDE_OPTION); - $ideFilePath = $input->getArgument(self::IDE_FILE_PATH_ARGUMENT); + $ideFilePath = $input->getArgument(self::IDE_FILE_PATH_ARGUMENT); $urnDictionary = $this->getUrnDictionary($output); - if ($formatter = $this->getFormatters($ideName)) { - $formatter->generateCatalog($urnDictionary, $ideFilePath); - } else { + + $formatter = $this->getFormatters($ideName); + if (!$formatter instanceof FormatInterface) { throw new InputException(__("Format for IDE '%1' is not supported", $ideName)); } + + $formatter->generateCatalog($urnDictionary, $ideFilePath); + + return Cli::RETURN_SUCCESS; } /** * Get formatter based on format * * @param string $format - * @return \Magento\Developer\Model\XmlCatalog\Format\FormatInterface|false + * @return FormatInterface|false */ private function getFormatters($format) { diff --git a/app/code/Magento/Developer/Model/Css/PreProcessor/FileGenerator/PublicationDecorator.php b/app/code/Magento/Developer/Model/Css/PreProcessor/FileGenerator/PublicationDecorator.php index 1b2a0986fa0ad..10474d8455b1b 100644 --- a/app/code/Magento/Developer/Model/Css/PreProcessor/FileGenerator/PublicationDecorator.php +++ b/app/code/Magento/Developer/Model/Css/PreProcessor/FileGenerator/PublicationDecorator.php @@ -3,11 +3,12 @@ * Copyright © Magento, Inc. All rights reserved. * See COPYING.txt for license details. */ +declare(strict_types=1); + namespace Magento\Developer\Model\Css\PreProcessor\FileGenerator; use Magento\Developer\Model\Config\Source\WorkflowType; use Magento\Framework\App\Config\ScopeConfigInterface; -use Magento\Framework\App\ObjectManager; use Magento\Framework\App\State; use Magento\Framework\App\View\Asset\Publisher; use Magento\Framework\Css\PreProcessor\File\Temporary; @@ -31,22 +32,21 @@ class PublicationDecorator extends RelatedGenerator private $scopeConfig; /** - * @var bool + * @var State */ - private $hasRelatedPublishing; + private $state; /** - * @var State + * @var bool */ - private $state; + private $hasRelatedPublishing; /** - * Constructor - * * @param Repository $assetRepository * @param Temporary $temporaryFile * @param Publisher $assetPublisher * @param ScopeConfigInterface $scopeConfig + * @param State $state * @param bool $hasRelatedPublishing */ public function __construct( @@ -54,11 +54,13 @@ public function __construct( Temporary $temporaryFile, Publisher $assetPublisher, ScopeConfigInterface $scopeConfig, + State $state, $hasRelatedPublishing = false ) { parent::__construct($assetRepository, $temporaryFile); $this->assetPublisher = $assetPublisher; $this->scopeConfig = $scopeConfig; + $this->state = $state; $this->hasRelatedPublishing = $hasRelatedPublishing; } @@ -69,7 +71,7 @@ protected function generateRelatedFile($relatedFileId, LocalInterface $asset) { $relatedAsset = parent::generateRelatedFile($relatedFileId, $asset); $isClientSideCompilation = - $this->getState()->getMode() !== State::MODE_PRODUCTION + $this->state->getMode() !== State::MODE_PRODUCTION && WorkflowType::CLIENT_SIDE_COMPILATION === $this->scopeConfig->getValue(WorkflowType::CONFIG_NAME_PATH); if ($this->hasRelatedPublishing || $isClientSideCompilation) { @@ -78,17 +80,4 @@ protected function generateRelatedFile($relatedFileId, LocalInterface $asset) return $relatedAsset; } - - /** - * @return State - * @deprecated 100.2.0 - */ - private function getState() - { - if (null === $this->state) { - $this->state = ObjectManager::getInstance()->get(State::class); - } - - return $this->state; - } } diff --git a/app/code/Magento/Developer/Model/TemplateEngine/Plugin/DebugHints.php b/app/code/Magento/Developer/Model/TemplateEngine/Plugin/DebugHints.php index a10d7bca1b89e..07eb1257f9561 100644 --- a/app/code/Magento/Developer/Model/TemplateEngine/Plugin/DebugHints.php +++ b/app/code/Magento/Developer/Model/TemplateEngine/Plugin/DebugHints.php @@ -1,7 +1,5 @@ <?php /** - * Plugin for the template engine factory that makes a decision of whether to activate debugging hints or not - * * Copyright © Magento, Inc. All rights reserved. * See COPYING.txt for license details. */ @@ -16,6 +14,9 @@ use Magento\Store\Model\StoreManagerInterface; use Magento\Framework\App\Request\Http; +/** + * Plugin for the template engine factory that makes a decision of whether to activate debugging hints or not + */ class DebugHints { /** @@ -26,22 +27,27 @@ class DebugHints /** * @var ScopeConfigInterface */ - protected $scopeConfig; + private $scopeConfig; /** * @var StoreManagerInterface */ - protected $storeManager; + private $storeManager; /** * @var DevHelper */ - protected $devHelper; + private $devHelper; /** * @var DebugHintsFactory */ - protected $debugHintsFactory; + private $debugHintsFactory; + + /** + * @var Http + */ + private $http; /** * XPath of configuration of the debug hints @@ -52,7 +58,7 @@ class DebugHints * * @var string */ - protected $debugHintsPath; + private $debugHintsPath; /** * XPath of configuration of the debug hints show with parameter @@ -77,8 +83,8 @@ class DebugHints * @param StoreManagerInterface $storeManager * @param DevHelper $devHelper * @param DebugHintsFactory $debugHintsFactory - * @param string $debugHintsPath * @param Http $http + * @param string $debugHintsPath * @param string $debugHintsWithParam * @param string $debugHintsParameter */ @@ -87,8 +93,8 @@ public function __construct( StoreManagerInterface $storeManager, DevHelper $devHelper, DebugHintsFactory $debugHintsFactory, + Http $http, $debugHintsPath, - Http $http = null, $debugHintsWithParam = null, $debugHintsParameter = null ) { @@ -96,10 +102,8 @@ public function __construct( $this->storeManager = $storeManager; $this->devHelper = $devHelper; $this->debugHintsFactory = $debugHintsFactory; + $this->http = $http; $this->debugHintsPath = $debugHintsPath; - $this->http = $http ?: \Magento\Framework\App\ObjectManager::getInstance()->get( - \Magento\Framework\App\Request\Http::class - ); $this->debugHintsWithParam = $debugHintsWithParam; $this->debugHintsParameter = $debugHintsParameter; } @@ -152,6 +156,7 @@ public function afterCreate( ]); } } + return $invocationResult; } } diff --git a/app/code/Magento/Developer/Model/View/Asset/PreProcessor/PreprocessorStrategy.php b/app/code/Magento/Developer/Model/View/Asset/PreProcessor/PreprocessorStrategy.php index 6e230d351a25f..fac74a9e2d2bd 100644 --- a/app/code/Magento/Developer/Model/View/Asset/PreProcessor/PreprocessorStrategy.php +++ b/app/code/Magento/Developer/Model/View/Asset/PreProcessor/PreprocessorStrategy.php @@ -42,32 +42,34 @@ class PreprocessorStrategy implements PreProcessorInterface private $state; /** - * Constructor - * * @param AlternativeSourceInterface $alternativeSource * @param FrontendCompilation $frontendCompilation * @param ScopeConfigInterface $scopeConfig + * @param State|null $state */ public function __construct( AlternativeSourceInterface $alternativeSource, FrontendCompilation $frontendCompilation, - ScopeConfigInterface $scopeConfig + ScopeConfigInterface $scopeConfig, + ?State $state = null ) { $this->frontendCompilation = $frontendCompilation; $this->alternativeSource = $alternativeSource; $this->scopeConfig = $scopeConfig; + $this->state = $state ?? ObjectManager::getInstance()->get(State::class); } /** * Transform content and/or content type for the specified pre-processing chain object * * @param PreProcessor\Chain $chain + * * @return void */ public function process(PreProcessor\Chain $chain) { $isClientSideCompilation = - $this->getState()->getMode() !== State::MODE_PRODUCTION + $this->state->getMode() !== State::MODE_PRODUCTION && WorkflowType::CLIENT_SIDE_COMPILATION === $this->scopeConfig->getValue(WorkflowType::CONFIG_NAME_PATH); if ($isClientSideCompilation) { @@ -76,16 +78,4 @@ public function process(PreProcessor\Chain $chain) $this->alternativeSource->process($chain); } } - - /** - * @return State - * @deprecated 100.2.0 - */ - private function getState() - { - if (null === $this->state) { - $this->state = ObjectManager::getInstance()->get(State::class); - } - return $this->state; - } } diff --git a/app/code/Magento/Developer/Model/XmlCatalog/Format/PhpStorm.php b/app/code/Magento/Developer/Model/XmlCatalog/Format/PhpStorm.php index aef58a9a37e89..2e780d37bdf85 100644 --- a/app/code/Magento/Developer/Model/XmlCatalog/Format/PhpStorm.php +++ b/app/code/Magento/Developer/Model/XmlCatalog/Format/PhpStorm.php @@ -3,14 +3,15 @@ * Copyright © Magento, Inc. All rights reserved. * See COPYING.txt for license details. */ +declare(strict_types=1); namespace Magento\Developer\Model\XmlCatalog\Format; -use Magento\Framework\App\ObjectManager; use Magento\Framework\DomDocument\DomDocumentFactory; use Magento\Framework\Exception\FileSystemException; use Magento\Framework\Filesystem\Directory\ReadFactory; use Magento\Framework\Filesystem\Directory\ReadInterface; +use Magento\Framework\Filesystem\DriverPool; use Magento\Framework\Filesystem\File\WriteFactory; /** @@ -43,11 +44,11 @@ class PhpStorm implements FormatInterface public function __construct( ReadFactory $readFactory, WriteFactory $fileWriteFactory, - DomDocumentFactory $domDocumentFactory = null + DomDocumentFactory $domDocumentFactory ) { $this->currentDirRead = $readFactory->create(getcwd()); $this->fileWriteFactory = $fileWriteFactory; - $this->domDocumentFactory = $domDocumentFactory ?: ObjectManager::getInstance()->get(DomDocumentFactory::class); + $this->domDocumentFactory = $domDocumentFactory; } /** @@ -55,6 +56,7 @@ public function __construct( * * @param string[] $dictionary * @param string $configFilePath relative path to the PhpStorm misc.xml + * * @return void */ public function generateCatalog(array $dictionary, $configFilePath) @@ -65,7 +67,7 @@ public function generateCatalog(array $dictionary, $configFilePath) try { $file = $this->fileWriteFactory->create( $configFilePath, - \Magento\Framework\Filesystem\DriverPool::FILE, + DriverPool::FILE, 'r' ); $dom = $this->domDocumentFactory->create(); @@ -103,7 +105,7 @@ public function generateCatalog(array $dictionary, $configFilePath) $dom->formatOutput = true; $file = $this->fileWriteFactory->create( $configFilePath, - \Magento\Framework\Filesystem\DriverPool::FILE, + DriverPool::FILE, 'w' ); $file->write($dom->saveXML()); diff --git a/app/code/Magento/Developer/Model/XmlCatalog/Format/VsCode.php b/app/code/Magento/Developer/Model/XmlCatalog/Format/VsCode.php new file mode 100644 index 0000000000000..f45aebf6fad68 --- /dev/null +++ b/app/code/Magento/Developer/Model/XmlCatalog/Format/VsCode.php @@ -0,0 +1,145 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +declare (strict_types = 1); + +namespace Magento\Developer\Model\XmlCatalog\Format; + +use Magento\Framework\DomDocument\DomDocumentFactory; +use Magento\Framework\Exception\FileSystemException; +use Magento\Framework\Filesystem\Directory\ReadFactory; +use Magento\Framework\Filesystem\Directory\ReadInterface; +use Magento\Framework\Filesystem\DriverPool; +use Magento\Framework\Filesystem\File\WriteFactory; + +/** + * Class VsCode generates URN catalog for VsCode + */ +class VsCode implements FormatInterface +{ + private const PROJECT_PATH_IDENTIFIER = '..'; + public const XMLNS = 'urn:oasis:names:tc:entity:xmlns:xml:catalog'; + public const FILE_MODE_READ = 'r'; + public const FILE_MODE_WRITE = 'w'; + + /** + * @var ReadInterface + */ + private $currentDirRead; + + /** + * @var WriteFactory + */ + private $fileWriteFactory; + + /** + * @var DomDocumentFactory + */ + private $domDocumentFactory; + + /** + * @param ReadFactory $readFactory + * @param WriteFactory $fileWriteFactory + * @param DomDocumentFactory $domDocumentFactory + */ + public function __construct( + ReadFactory $readFactory, + WriteFactory $fileWriteFactory, + DomDocumentFactory $domDocumentFactory + ) { + $this->currentDirRead = $readFactory->create(getcwd()); + $this->fileWriteFactory = $fileWriteFactory; + $this->domDocumentFactory = $domDocumentFactory; + } + + /** + * Generate Catalog of URNs for the VsCode + * + * @param string[] $dictionary + * @param string $configFile relative path to the VsCode catalog.xml + * @return void + */ + public function generateCatalog(array $dictionary, $configFile): void + { + $catalogNode = null; + + try { + $file = $this->fileWriteFactory->create($configFile, DriverPool::FILE, self::FILE_MODE_READ); + $dom = $this->domDocumentFactory->create(); + $fileContent = $file->readAll(); + if (!empty($fileContent)) { + $dom->loadXML($fileContent); + } else { + $this->initEmptyFile($dom); + } + $catalogNode = $dom->getElementsByTagName('catalog')->item(0); + + if ($catalogNode == null) { + $dom = $this->domDocumentFactory->create(); + $catalogNode = $this->initEmptyFile($dom); + } + $file->close(); + } catch (FileSystemException $f) { + //create file if does not exists + $dom = $this->domDocumentFactory->create(); + $catalogNode = $this->initEmptyFile($dom); + } + + $xpath = new \DOMXPath($dom); + $xpath->registerNamespace('xmlns', self::XMLNS); + + foreach ($dictionary as $urn => $xsdPath) { + // Find an existing urn + $existingNode = $xpath->query("/xmlns:catalog/xmlns:system[@systemId='" . $urn . "']")->item(0); + $node = $existingNode ?? $dom->createElement('system'); + $node->setAttribute('systemId', $urn); + $node->setAttribute('uri', $this->getFileLocationInProject($xsdPath)); + $catalogNode->appendChild($node); + } + $dom->formatOutput = true; + $dom->preserveWhiteSpace = false; + + // Reload to keep pretty format + $dom->loadXML($dom->saveXML()); + + $file = $this->fileWriteFactory->create($configFile, DriverPool::FILE, self::FILE_MODE_WRITE); + $file->write($dom->saveXML()); + $file->close(); + } + + /** + * Setup basic empty dom elements + * + * @param \DOMDocument $dom + * @return \DOMElement + */ + private function initEmptyFile(\DOMDocument $dom): \DOMElement + { + $copyrightComment = $dom->createComment(' +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +'); + $dom->appendChild($copyrightComment); + + $catalogNode = $dom->createElement('catalog'); + $catalogNode->setAttribute('xmlns', self::XMLNS); + $dom->appendChild($catalogNode); + + return $catalogNode; + } + + /** + * Resolve xsdpath to xml project path + * + * @param string $xsdPath + * @return string + */ + private function getFileLocationInProject(string $xsdPath): string + { + return self::PROJECT_PATH_IDENTIFIER . DIRECTORY_SEPARATOR . $this->currentDirRead->getRelativePath($xsdPath); + } +} diff --git a/app/code/Magento/Developer/Test/Unit/Console/Command/TemplateHintsStatusCommandTest.php b/app/code/Magento/Developer/Test/Unit/Console/Command/TemplateHintsStatusCommandTest.php new file mode 100644 index 0000000000000..1aec1d71b434d --- /dev/null +++ b/app/code/Magento/Developer/Test/Unit/Console/Command/TemplateHintsStatusCommandTest.php @@ -0,0 +1,74 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +declare(strict_types=1); + +namespace Magento\Developer\Test\Unit\Console\Command; + +use Magento\Developer\Console\Command\TemplateHintsStatusCommand; +use Magento\Framework\App\Config\ReinitableConfigInterface; +use Magento\Framework\App\Config\ScopeConfigInterface; +use Magento\Framework\Console\Cli; +use PHPUnit\Framework\TestCase; +use Symfony\Component\Console\Tester\CommandTester; + +/** + * Class TemplateHintsStatusCommandTest + * + * Tests dev:template-hints:status command. + */ +class TemplateHintsStatusCommandTest extends TestCase +{ + /** + * @var TemplateHintsStatusCommand + */ + private $command; + /** + * @var ScopeConfigInterface + */ + private $scopeConfigMock; + /** + * @var ReinitableConfigInterface + */ + private $reinitableConfigMock; + + /** + * @inheritDoc + */ + protected function setUp() + { + $this->scopeConfigMock = $this->getMockForAbstractClass(ScopeConfigInterface::class); + $this->reinitableConfigMock = $this->getMockForAbstractClass(ReinitableConfigInterface::class); + + $this->command = new TemplateHintsStatusCommand( + $this->scopeConfigMock, + $this->reinitableConfigMock + ); + } + + /** + * Verify ScopeConfigInterface instance + */ + public function testScopeConfigInterfaceInstance() + { + $this->assertInstanceOf(ScopeConfigInterface::class, $this->scopeConfigMock); + } + + /** + * Verify ReinitableConfigInterface instance + */ + public function testReinitableConfigInterfaceInstance() + { + $this->assertInstanceOf(ReinitableConfigInterface::class, $this->reinitableConfigMock); + } + + /** + * Verify TemplateHintsStatusCommand instance + */ + public function testCommandInstance() + { + $this->assertInstanceOf(TemplateHintsStatusCommand::class, $this->command); + } +} diff --git a/app/code/Magento/Developer/Test/Unit/Console/Command/XmlCatalogGenerateCommandTest.php b/app/code/Magento/Developer/Test/Unit/Console/Command/XmlCatalogGenerateCommandTest.php index e5c6525cfeb55..04d41efb793b8 100644 --- a/app/code/Magento/Developer/Test/Unit/Console/Command/XmlCatalogGenerateCommandTest.php +++ b/app/code/Magento/Developer/Test/Unit/Console/Command/XmlCatalogGenerateCommandTest.php @@ -66,4 +66,58 @@ public function testExecuteBadType() $commandTester->execute([XmlCatalogGenerateCommand::IDE_FILE_PATH_ARGUMENT => 'test']); $this->assertEquals('', $commandTester->getDisplay()); } + + public function testExecuteVsCodeFormat() + { + $fixtureXmlFile = __DIR__ . '/_files/test.xml'; + + $filesMock = $this->createPartialMock(\Magento\Framework\App\Utility\Files::class, ['getXmlCatalogFiles']); + $filesMock->expects($this->at(0)) + ->method('getXmlCatalogFiles') + ->will($this->returnValue([[$fixtureXmlFile]])); + $filesMock->expects($this->at(1)) + ->method('getXmlCatalogFiles') + ->will($this->returnValue([])); + $urnResolverMock = $this->createMock(\Magento\Framework\Config\Dom\UrnResolver::class); + $urnResolverMock->expects($this->once()) + ->method('getRealPath') + ->with($this->equalTo('urn:magento:framework:Module/etc/module.xsd')) + ->will($this->returnValue($fixtureXmlFile)); + + $vscodeFormatMock = $this->createMock(\Magento\Developer\Model\XmlCatalog\Format\VsCode::class); + $vscodeFormatMock->expects($this->once()) + ->method('generateCatalog') + ->with( + $this->equalTo(['urn:magento:framework:Module/etc/module.xsd' => $fixtureXmlFile]), + $this->equalTo('test') + )->will($this->returnValue(null)); + + $formats = ['vscode' => $vscodeFormatMock]; + $readFactory = $this->createMock(\Magento\Framework\Filesystem\Directory\ReadFactory::class); + $readDirMock = $this->createMock(\Magento\Framework\Filesystem\Directory\ReadInterface::class); + + $content = file_get_contents($fixtureXmlFile); + + $readDirMock->expects($this->once()) + ->method('readFile') + ->with($this->equalTo('test.xml')) + ->will($this->returnValue($content)); + $readFactory->expects($this->once()) + ->method('create') + ->will($this->returnValue($readDirMock)); + + $this->command = new XmlCatalogGenerateCommand( + $filesMock, + $urnResolverMock, + $readFactory, + $formats + ); + + $commandTester = new CommandTester($this->command); + $commandTester->execute([ + '--' . XmlCatalogGenerateCommand::IDE_OPTION => 'vscode', + XmlCatalogGenerateCommand::IDE_FILE_PATH_ARGUMENT => 'test', + ]); + $this->assertEquals('', $commandTester->getDisplay()); + } } diff --git a/app/code/Magento/Developer/Test/Unit/Model/TemplateEngine/Plugin/DebugHintsTest.php b/app/code/Magento/Developer/Test/Unit/Model/TemplateEngine/Plugin/DebugHintsTest.php index a918a562a926d..d7ab4fdea2c37 100644 --- a/app/code/Magento/Developer/Test/Unit/Model/TemplateEngine/Plugin/DebugHintsTest.php +++ b/app/code/Magento/Developer/Test/Unit/Model/TemplateEngine/Plugin/DebugHintsTest.php @@ -3,60 +3,103 @@ * Copyright © Magento, Inc. All rights reserved. * See COPYING.txt for license details. */ +declare(strict_types=1); + namespace Magento\Developer\Test\Unit\Model\TemplateEngine\Plugin; +use Magento\Developer\Helper\Data; +use Magento\Developer\Model\TemplateEngine\Decorator\DebugHints as DebugHintsDecorator; use Magento\Developer\Model\TemplateEngine\Decorator\DebugHintsFactory; use Magento\Developer\Model\TemplateEngine\Plugin\DebugHints; +use Magento\Framework\App\Config\ScopeConfigInterface; +use Magento\Framework\App\Request\Http; +use Magento\Framework\TestFramework\Unit\Helper\ObjectManager; +use Magento\Framework\View\TemplateEngineFactory; +use Magento\Framework\View\TemplateEngineInterface; +use Magento\Store\Api\Data\StoreInterface; use Magento\Store\Model\ScopeInterface; +use Magento\Store\Model\StoreManagerInterface; +use PHPUnit\Framework\MockObject\MockObject; +use PHPUnit\Framework\TestCase; -class DebugHintsTest extends \PHPUnit\Framework\TestCase +/** + * @SuppressWarnings(PHPMD.CouplingBetweenObjects) + */ +class DebugHintsTest extends TestCase { + private const STORE_CODE = 'default'; + + /** + * @var ObjectManager + */ + private $objectManager; + /** - * @var \Magento\Framework\App\Config\ScopeConfigInterface|\PHPUnit_Framework_MockObject_MockObject + * @var ScopeConfigInterface|MockObject */ - protected $scopeConfigMock; + private $scopeConfigMock; /** - * @var \Magento\Store\Model\StoreManagerInterface|\PHPUnit_Framework_MockObject_MockObject + * @var StoreManagerInterface|MockObject */ - protected $storeManager; + private $storeManagerMock; /** - * @var \Magento\Developer\Helper\Data|\PHPUnit_Framework_MockObject_MockObject + * @var Data|MockObject */ - protected $devHelperMock; + private $devHelperMock; /** - * @var DebugHintsFactory | \PHPUnit_Framework_MockObject_MockObject + * @var DebugHintsFactory|MockObject */ - protected $debugHintsFactory; + private $debugHintsFactoryMock; + + /** + * @var Http|MockObject + */ + private $httpMock; /** * @return void */ protected function setUp() { - $this->scopeConfigMock = $this->getMockBuilder(\Magento\Framework\App\Config\ScopeConfigInterface::class) + $this->scopeConfigMock = $this->getMockBuilder(ScopeConfigInterface::class) ->getMockForAbstractClass(); - $this->storeManager = $this->getMockBuilder(\Magento\Store\Model\StoreManagerInterface::class) + $this->storeManagerMock = $this->getMockBuilder(StoreManagerInterface::class) ->getMockForAbstractClass(); - $this->devHelperMock = $this->getMockBuilder(\Magento\Developer\Helper\Data::class) + $this->devHelperMock = $this->getMockBuilder(Data::class) ->disableOriginalConstructor() ->getMock(); - $this->debugHintsFactory = $this->getMockBuilder( - \Magento\Developer\Model\TemplateEngine\Decorator\DebugHintsFactory::class + $this->debugHintsFactoryMock = $this->getMockBuilder( + DebugHintsFactory::class ) ->setMethods(['create']) ->disableOriginalConstructor() ->getMock(); + + $this->httpMock = $this->createMock(Http::class); + + $storeMock = $this->createMock(StoreInterface::class); + $storeMock->expects($this->once()) + ->method('getCode') + ->willReturn(static::STORE_CODE); + $this->storeManagerMock->expects($this->once()) + ->method('getStore') + ->willReturn($storeMock); + + $this->objectManager = new ObjectManager($this); } /** * @param string $debugHintsPath * @param bool $showBlockHints + * @param bool $debugHintsWithParam + * @param bool $debugHintsParameter + * * @return void * @dataProvider afterCreateActiveDataProvider */ @@ -72,40 +115,41 @@ public function testAfterCreateActive( $this->setupConfigFixture($debugHintsPath, true, $showBlockHints); - $engine = $this->createMock(\Magento\Framework\View\TemplateEngineInterface::class); + $engine = $this->createMock(TemplateEngineInterface::class); - $debugHintsDecorator = $this->getMockBuilder( - \Magento\Developer\Model\TemplateEngine\Decorator\DebugHints::class + $debugHintsDecoratorMock = $this->getMockBuilder( + DebugHintsDecorator::class ) ->disableOriginalConstructor() ->getMock(); - $this->debugHintsFactory->expects($this->once()) + $this->debugHintsFactoryMock->expects($this->once()) ->method('create') ->with([ 'subject' => $engine, 'showBlockHints' => $showBlockHints, ]) - ->willReturn($debugHintsDecorator); + ->willReturn($debugHintsDecoratorMock); - $subjectMock = $this->getMockBuilder(\Magento\Framework\View\TemplateEngineFactory::class) + $subjectMock = $this->getMockBuilder(TemplateEngineFactory::class) ->disableOriginalConstructor() ->getMock(); - $this->httpMock = $this->createMock(\Magento\Framework\App\Request\Http::class); - - $debugHints = new DebugHints( - $this->scopeConfigMock, - $this->storeManager, - $this->devHelperMock, - $this->debugHintsFactory, - $debugHintsPath, - $this->httpMock, - $debugHintsWithParam, - $debugHintsParameter + $debugHints = $this->objectManager->getObject( + DebugHints::class, + [ + 'scopeConfig' => $this->scopeConfigMock, + 'storeManager' => $this->storeManagerMock, + 'devHelper' => $this->devHelperMock, + 'debugHintsFactory' => $this->debugHintsFactoryMock, + 'http' => $this->httpMock, + 'debugHintsPath' => $debugHintsPath, + 'debugHintsWithParam' => $debugHintsWithParam, + 'debugHintsParameter' => $debugHintsParameter + ] ); - $this->assertEquals($debugHintsDecorator, $debugHints->afterCreate($subjectMock, $engine)); + $this->assertEquals($debugHintsDecoratorMock, $debugHints->afterCreate($subjectMock, $engine)); } /** @@ -125,6 +169,9 @@ public function afterCreateActiveDataProvider() * @param string $debugHintsPath * @param bool $isDevAllowed * @param bool $showTemplateHints + * @param bool $debugHintsWithParam + * @param bool $debugHintsParameter + * * @return void * @dataProvider afterCreateInactiveDataProvider */ @@ -135,29 +182,30 @@ public function testAfterCreateInactive( $debugHintsWithParam, $debugHintsParameter ) { - $this->devHelperMock->expects($this->any()) + $this->devHelperMock ->method('isDevAllowed') ->willReturn($isDevAllowed); $this->setupConfigFixture($debugHintsPath, $showTemplateHints, true); - $engine = $this->createMock(\Magento\Framework\View\TemplateEngineInterface::class); + $engine = $this->createMock(TemplateEngineInterface::class); - $subjectMock = $this->getMockBuilder(\Magento\Framework\View\TemplateEngineFactory::class) + $subjectMock = $this->getMockBuilder(TemplateEngineFactory::class) ->disableOriginalConstructor() ->getMock(); - $this->httpMock = $this->createMock(\Magento\Framework\App\Request\Http::class); - - $debugHints = new DebugHints( - $this->scopeConfigMock, - $this->storeManager, - $this->devHelperMock, - $this->debugHintsFactory, - $debugHintsPath, - $this->httpMock, - $debugHintsWithParam, - $debugHintsParameter + $debugHints = $this->objectManager->getObject( + DebugHints::class, + [ + 'scopeConfig' => $this->scopeConfigMock, + 'storeManager' => $this->storeManagerMock, + 'devHelper' => $this->devHelperMock, + 'debugHintsFactory' => $this->debugHintsFactoryMock, + 'http' => $this->httpMock, + 'debugHintsPath' => $debugHintsPath, + 'debugHintsWithParam' => $debugHintsWithParam, + 'debugHintsParameter' => $debugHintsParameter + ] ); $this->assertSame($engine, $debugHints->afterCreate($subjectMock, $engine)); @@ -184,32 +232,24 @@ public function afterCreateInactiveDataProvider() * @param string $debugHintsPath * @param bool $showTemplateHints * @param bool $showBlockHints + * * @return void */ - protected function setupConfigFixture($debugHintsPath, $showTemplateHints, $showBlockHints) + private function setupConfigFixture($debugHintsPath, $showTemplateHints, $showBlockHints): void { - $storeCode = 'default'; - $storeMock = $this->createMock(\Magento\Store\Api\Data\StoreInterface::class); - $storeMock->expects($this->once()) - ->method('getCode') - ->willReturn($storeCode); - $this->storeManager->expects($this->once()) - ->method('getStore') - ->willReturn($storeMock); - $this->scopeConfigMock->expects($this->atLeastOnce()) ->method('getValue') ->willReturnMap([ [ $debugHintsPath, ScopeInterface::SCOPE_STORE, - $storeCode, + static::STORE_CODE, $showTemplateHints, ], [ DebugHints::XML_PATH_DEBUG_TEMPLATE_HINTS_BLOCKS, ScopeInterface::SCOPE_STORE, - $storeCode, + static::STORE_CODE, $showBlockHints ] ]); diff --git a/app/code/Magento/Developer/Test/Unit/Model/View/Asset/PreProcessor/PreprocessorStrategyTest.php b/app/code/Magento/Developer/Test/Unit/Model/View/Asset/PreProcessor/PreprocessorStrategyTest.php index 8372092c0e763..4b1b3110b4b9b 100644 --- a/app/code/Magento/Developer/Test/Unit/Model/View/Asset/PreProcessor/PreprocessorStrategyTest.php +++ b/app/code/Magento/Developer/Test/Unit/Model/View/Asset/PreProcessor/PreprocessorStrategyTest.php @@ -3,6 +3,8 @@ * Copyright © Magento, Inc. All rights reserved. * See COPYING.txt for license details. */ +declare(strict_types=1); + namespace Magento\Developer\Test\Unit\Model\View\Asset\PreProcessor; use Magento\Developer\Model\Config\Source\WorkflowType; @@ -13,14 +15,14 @@ use Magento\Framework\TestFramework\Unit\Helper\ObjectManager; use Magento\Framework\View\Asset\PreProcessor\AlternativeSourceInterface; use Magento\Framework\View\Asset\PreProcessor\Chain; +use PHPUnit\Framework\MockObject\MockObject; +use PHPUnit\Framework\TestCase; /** - * Class PreprocessorStrategyTest - * * @see \Magento\Developer\Model\View\Asset\PreProcessor\PreprocessorStrategy * @SuppressWarnings(PHPMD.CouplingBetweenObjects) */ -class PreprocessorStrategyTest extends \PHPUnit\Framework\TestCase +class PreprocessorStrategyTest extends TestCase { /** * @var PreprocessorStrategy @@ -28,27 +30,27 @@ class PreprocessorStrategyTest extends \PHPUnit\Framework\TestCase private $preprocessorStrategy; /** - * @var FrontendCompilation|\PHPUnit_Framework_MockObject_MockObject + * @var FrontendCompilation|MockObject */ private $frontendCompilationMock; /** - * @var AlternativeSourceInterface|\PHPUnit_Framework_MockObject_MockObject + * @var AlternativeSourceInterface|MockObject */ private $alternativeSourceMock; /** - * @var ScopeConfigInterface|\PHPUnit_Framework_MockObject_MockObject + * @var ScopeConfigInterface|MockObject */ private $scopeConfigMock; /** - * @var State|\PHPUnit_Framework_MockObject_MockObject + * @var State|MockObject */ private $stateMock; /** - * @var \Magento\Framework\App\ObjectManager|\PHPUnit_Framework_MockObject_MockObject + * @var \Magento\Framework\App\ObjectManager|MockObject */ private $objectMangerMock; @@ -71,12 +73,15 @@ protected function setUp() ->disableOriginalConstructor() ->getMock(); - $this->preprocessorStrategy = (new ObjectManager($this))->getObject(PreprocessorStrategy::class, [ - 'alternativeSource' => $this->alternativeSourceMock, - 'frontendCompilation' => $this->frontendCompilationMock, - 'scopeConfig' => $this->scopeConfigMock, - 'state' => $this->stateMock, - ]); + $this->preprocessorStrategy = (new ObjectManager($this))->getObject( + PreprocessorStrategy::class, + [ + 'alternativeSource' => $this->alternativeSourceMock, + 'frontendCompilation' => $this->frontendCompilationMock, + 'scopeConfig' => $this->scopeConfigMock, + 'state' => $this->stateMock, + ] + ); } /** @@ -148,14 +153,12 @@ public function testProcessAlternativeSource() } /** - * @return Chain|\PHPUnit_Framework_MockObject_MockObject + * @return Chain|MockObject */ private function getChainMock() { - $chainMock = $this->getMockBuilder(Chain::class) + return $this->getMockBuilder(Chain::class) ->disableOriginalConstructor() ->getMock(); - - return $chainMock; } } diff --git a/app/code/Magento/Developer/Test/Unit/Model/XmlCatalog/Format/VsCodeTest.php b/app/code/Magento/Developer/Test/Unit/Model/XmlCatalog/Format/VsCodeTest.php new file mode 100644 index 0000000000000..55d2a811b2eee --- /dev/null +++ b/app/code/Magento/Developer/Test/Unit/Model/XmlCatalog/Format/VsCodeTest.php @@ -0,0 +1,282 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ + +namespace Magento\Developer\Test\Unit\Model\XmlCatalog\Format; + +use Magento\Developer\Model\XmlCatalog\Format\VsCode; +use Magento\Framework\DomDocument\DomDocumentFactory; +use Magento\Framework\Exception\FileSystemException; +use Magento\Framework\Filesystem\Directory\ReadFactory; +use Magento\Framework\Filesystem\Directory\ReadInterface; +use Magento\Framework\Filesystem\DriverPool; +use Magento\Framework\Filesystem\File\Read; +use Magento\Framework\Filesystem\File\Write; +use Magento\Framework\Filesystem\File\WriteFactory; +use Magento\Framework\TestFramework\Unit\Helper\ObjectManager; +use PHPUnit\Framework\MockObject\MockObject; +use PHPUnit\Framework\TestCase; + +class VsCodeTest extends TestCase +{ + /** + * @var VsCode + */ + private $vscodeFormat; + + /** + * @var MockObject|ReadFactory + */ + private $readFactoryMock; + + /** + * @var MockObject|WriteFactory + */ + private $fileWriteFactoryMock; + + /** + * @var DomDocumentFactory + */ + private $domFactory; + + /** + * @var ObjectManager + */ + private $objectManagerHelper; + + public function setUp() + { + $this->objectManagerHelper = new ObjectManager($this); + + $currentDirReadMock = $this->createMock(ReadInterface::class); + $currentDirReadMock->expects($this->any()) + ->method('getRelativePath') + ->willReturnCallback(function ($xsdPath) { + return $xsdPath; + }); + + $this->readFactoryMock = $this->createMock(ReadFactory::class); + $this->readFactoryMock->expects($this->once()) + ->method('create') + ->withAnyParameters() + ->willReturn($currentDirReadMock); + + $this->fileWriteFactoryMock = $this->createMock(WriteFactory::class); + $this->domFactory = $this->objectManagerHelper->getObject(DomDocumentFactory::class); + + $vscodeFormatArgs = $this->objectManagerHelper->getConstructArguments( + VsCode::class, + [ + 'readFactory' => $this->readFactoryMock, + 'fileWriteFactory' => $this->fileWriteFactoryMock, + 'domDocumentFactory' => $this->domFactory, + ] + ); + + $this->vscodeFormat = $this->objectManagerHelper->getObject(VsCode::class, $vscodeFormatArgs); + } + + /** + * Test generation of new valid catalog + * + * @param string $content + * @param array $dictionary + * @dataProvider dictionaryDataProvider + * @return void + */ + public function testGenerateNewValidCatalog($content, $dictionary) + { + $configFile = 'test'; + + $message = __("The \"%1.xml\" file doesn't exist.", $configFile); + + $this->fileWriteFactoryMock->expects($this->at(0)) + ->method('create') + ->with( + $configFile, + DriverPool::FILE, + VsCode::FILE_MODE_READ + ) + ->willThrowException(new FileSystemException($message)); + + $fileMock = $this->createMock(Write::class); + $fileMock->expects($this->once()) + ->method('write') + ->with($content); + + $this->fileWriteFactoryMock->expects($this->at(1)) + ->method('create') + ->with( + $configFile, + DriverPool::FILE, + VsCode::FILE_MODE_WRITE + ) + ->willReturn($fileMock); + + $this->vscodeFormat->generateCatalog($dictionary, $configFile); + } + + /** + * Test modify existing valid catalog + * + * @param string $content + * @param array $dictionary + * @dataProvider dictionaryDataProvider + * @return void + */ + public function testGenerateExistingValidCatalog($content, $dictionary) + { + $configFile = 'test'; + + $fileMock = $this->createMock(Read::class); + $fileMock->expects($this->once()) + ->method('readAll') + ->withAnyParameters() + ->willReturn($content); + + $this->fileWriteFactoryMock->expects($this->at(0)) + ->method('create') + ->with( + $configFile, + DriverPool::FILE, + VsCode::FILE_MODE_READ + ) + ->willReturn($fileMock); + + $fileMock = $this->createMock(Write::class); + $fileMock->expects($this->once()) + ->method('write') + ->with($content); + + $this->fileWriteFactoryMock->expects($this->at(1)) + ->method('create') + ->with( + $configFile, + DriverPool::FILE, + VsCode::FILE_MODE_WRITE + ) + ->willReturn($fileMock); + + $this->vscodeFormat->generateCatalog($dictionary, $configFile); + } + + /** + * Test modify existing empty catalog + * + * @param string $content + * @param array $dictionary + * @dataProvider dictionaryDataProvider + * @return void + */ + public function testGenerateExistingEmptyValidCatalog($content, $dictionary) + { + $configFile = 'test'; + + $fileMock = $this->createMock(Read::class); + $fileMock->expects($this->once()) + ->method('readAll') + ->withAnyParameters() + ->willReturn(''); + + $this->fileWriteFactoryMock->expects($this->at(0)) + ->method('create') + ->with( + $configFile, + DriverPool::FILE, + VsCode::FILE_MODE_READ + ) + ->willReturn($fileMock); + + $fileMock = $this->createMock(Write::class); + $fileMock->expects($this->once()) + ->method('write') + ->with($content); + + $this->fileWriteFactoryMock->expects($this->at(1)) + ->method('create') + ->with( + $configFile, + DriverPool::FILE, + VsCode::FILE_MODE_WRITE + ) + ->willReturn($fileMock); + + $this->vscodeFormat->generateCatalog($dictionary, $configFile); + } + + /** + * Test modify existing invalid catalog + * + * @param string $content + * @param array $dictionary + * @dataProvider dictionaryDataProvider + * @return void + */ + public function testGenerateExistingInvalidValidCatalog($content, $dictionary, $invalidContent) + { + $configFile = 'test'; + + $fileMock = $this->createMock(Read::class); + $fileMock->expects($this->once()) + ->method('readAll') + ->withAnyParameters() + ->willReturn($invalidContent); + + $this->fileWriteFactoryMock->expects($this->at(0)) + ->method('create') + ->with( + $configFile, + DriverPool::FILE, + VsCode::FILE_MODE_READ + ) + ->willReturn($fileMock); + + $fileMock = $this->createMock(Write::class); + $fileMock->expects($this->once()) + ->method('write') + ->with($content); + + $this->fileWriteFactoryMock->expects($this->at(1)) + ->method('create') + ->with( + $configFile, + DriverPool::FILE, + VsCode::FILE_MODE_WRITE + ) + ->willReturn($fileMock); + + $this->vscodeFormat->generateCatalog($dictionary, $configFile); + } + + /** + * Data provider for test + * + * @return array + */ + public function dictionaryDataProvider() + { + $fixtureXmlFile = __DIR__ . '/_files/valid_catalog.xml'; + $content = file_get_contents($fixtureXmlFile); + $invalidXmlFile = __DIR__ . '/_files/invalid_catalog.xml'; + $invalidContent = file_get_contents($invalidXmlFile); + + return [ + [ + $content, + [ + 'urn:magento:framework:Acl/etc/acl.xsd' => + 'vendor/magento/framework/Acl/etc/acl.xsd', + 'urn:magento:module:Magento_Store:etc/config.xsd' => + 'vendor/magento/module-store/etc/config.xsd', + 'urn:magento:module:Magento_Cron:etc/crontab.xsd' => + 'vendor/magento/module-cron/etc/crontab.xsd', + 'urn:magento:framework:Setup/Declaration/Schema/etc/schema.xsd' => + 'vendor/magento/framework/Setup/Declaration/Schema/etc/schema.xsd', + ], + $invalidContent, + ], + ]; + } +} diff --git a/app/code/Magento/Developer/Test/Unit/Model/XmlCatalog/Format/_files/invalid_catalog.xml b/app/code/Magento/Developer/Test/Unit/Model/XmlCatalog/Format/_files/invalid_catalog.xml new file mode 100644 index 0000000000000..213448bfc7a68 --- /dev/null +++ b/app/code/Magento/Developer/Test/Unit/Model/XmlCatalog/Format/_files/invalid_catalog.xml @@ -0,0 +1,8 @@ +<?xml version="1.0"?> +<!-- +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +--> +<root /> diff --git a/app/code/Magento/Developer/Test/Unit/Model/XmlCatalog/Format/_files/valid_catalog.xml b/app/code/Magento/Developer/Test/Unit/Model/XmlCatalog/Format/_files/valid_catalog.xml new file mode 100644 index 0000000000000..43fee113e9930 --- /dev/null +++ b/app/code/Magento/Developer/Test/Unit/Model/XmlCatalog/Format/_files/valid_catalog.xml @@ -0,0 +1,13 @@ +<?xml version="1.0"?> +<!-- +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +--> +<catalog xmlns="urn:oasis:names:tc:entity:xmlns:xml:catalog"> + <system systemId="urn:magento:framework:Acl/etc/acl.xsd" uri="../vendor/magento/framework/Acl/etc/acl.xsd"/> + <system systemId="urn:magento:module:Magento_Store:etc/config.xsd" uri="../vendor/magento/module-store/etc/config.xsd"/> + <system systemId="urn:magento:module:Magento_Cron:etc/crontab.xsd" uri="../vendor/magento/module-cron/etc/crontab.xsd"/> + <system systemId="urn:magento:framework:Setup/Declaration/Schema/etc/schema.xsd" uri="../vendor/magento/framework/Setup/Declaration/Schema/etc/schema.xsd"/> +</catalog> diff --git a/app/code/Magento/Developer/etc/adminhtml/system.xml b/app/code/Magento/Developer/etc/adminhtml/system.xml index 197dc6f981acf..812776ba1da28 100644 --- a/app/code/Magento/Developer/etc/adminhtml/system.xml +++ b/app/code/Magento/Developer/etc/adminhtml/system.xml @@ -9,9 +9,9 @@ <section id="dev"> <group id="front_end_development_workflow" translate="label" type="text" sortOrder="8" showInDefault="1" showInWebsite="1" showInStore="1"> <label>Frontend Development Workflow</label> - <field id="type" translate="label comment" type="select" sortOrder="1" showInDefault="1" showInWebsite="0" showInStore="0" canRestore="1"> + <field id="type" translate="label comment" type="select" sortOrder="1" showInDefault="1" canRestore="1"> <label>Workflow type</label> - <comment>Not available in production mode.</comment> + <comment>Modifying this configuration has no effect in production mode.</comment> <source_model>Magento\Developer\Model\Config\Source\WorkflowType</source_model> <frontend_model>Magento\Developer\Block\Adminhtml\System\Config\WorkflowType</frontend_model> <backend_model>Magento\Developer\Model\Config\Backend\WorkflowType</backend_model> diff --git a/app/code/Magento/Developer/etc/di.xml b/app/code/Magento/Developer/etc/di.xml index 98adcbb3a8295..3e08466c6a001 100644 --- a/app/code/Magento/Developer/etc/di.xml +++ b/app/code/Magento/Developer/etc/di.xml @@ -18,6 +18,7 @@ <arguments> <argument name="formats" xsi:type="array"> <item name="phpstorm" xsi:type="object">Magento\Developer\Model\XmlCatalog\Format\PhpStorm</item> + <item name="vscode" xsi:type="object">Magento\Developer\Model\XmlCatalog\Format\VsCode</item> </argument> </arguments> </type> @@ -104,6 +105,7 @@ <item name="dev_query_log_disable" xsi:type="object">Magento\Developer\Console\Command\QueryLogDisableCommand</item> <item name="dev_template_hints_disable" xsi:type="object">Magento\Developer\Console\Command\TemplateHintsDisableCommand</item> <item name="dev_template_hints_enable" xsi:type="object">Magento\Developer\Console\Command\TemplateHintsEnableCommand</item> + <item name="dev_template_hints_status" xsi:type="object">Magento\Developer\Console\Command\TemplateHintsStatusCommand</item> <item name="dev_profiler_disable" xsi:type="object">Magento\Developer\Console\Command\ProfilerDisableCommand</item> <item name="dev_profiler_enable" xsi:type="object">Magento\Developer\Console\Command\ProfilerEnableCommand</item> <item name="dev_generate_patch" xsi:type="object">Magento\Developer\Console\Command\GeneratePatchCommand</item> diff --git a/app/code/Magento/Developer/i18n/en_US.csv b/app/code/Magento/Developer/i18n/en_US.csv index 59e67445b2b58..649779cdf853d 100644 --- a/app/code/Magento/Developer/i18n/en_US.csv +++ b/app/code/Magento/Developer/i18n/en_US.csv @@ -9,4 +9,4 @@ "Allowed IPs (comma separated)","Allowed IPs (comma separated)" "Leave empty for access from any location.","Leave empty for access from any location." "Log to File","Log to File" -"Not available in production mode.","Not available in production mode." +"Modifying this configuration has no effect in production mode.","Modifying this configuration has no effect in production mode." diff --git a/app/code/Magento/Developer/registration.php b/app/code/Magento/Developer/registration.php index 1d3fb6148ce1f..e99b1ed24b104 100644 --- a/app/code/Magento/Developer/registration.php +++ b/app/code/Magento/Developer/registration.php @@ -4,6 +4,6 @@ * See COPYING.txt for license details. */ -use \Magento\Framework\Component\ComponentRegistrar; +use Magento\Framework\Component\ComponentRegistrar; ComponentRegistrar::register(ComponentRegistrar::MODULE, 'Magento_Developer', __DIR__); diff --git a/app/code/Magento/Dhl/Model/Carrier.php b/app/code/Magento/Dhl/Model/Carrier.php index ad76f5070b35b..6587b462099be 100644 --- a/app/code/Magento/Dhl/Model/Carrier.php +++ b/app/code/Magento/Dhl/Model/Carrier.php @@ -704,7 +704,7 @@ public function getDhlProductTitle($code) $contentType = $this->getConfigData('content_type'); $dhlProducts = $this->getDhlProducts($contentType); - return isset($dhlProducts[$code]) ? $dhlProducts[$code] : false; + return $dhlProducts[$code] ?? false; } /** @@ -1373,7 +1373,7 @@ protected function getCountryParams($countryCode) if (isset($this->_countryParams->{$countryCode})) { $countryParams = new \Magento\Framework\DataObject($this->_countryParams->{$countryCode}->asArray()); } - return isset($countryParams) ? $countryParams : new \Magento\Framework\DataObject(); + return $countryParams ?? new \Magento\Framework\DataObject(); } /** diff --git a/app/code/Magento/Dhl/Test/Mftf/Test/AdminCheckInputFieldsDisabledAfterAppConfigDumpTest.xml b/app/code/Magento/Dhl/Test/Mftf/Test/AdminCheckInputFieldsDisabledAfterAppConfigDumpTest.xml index f5e1e8ef0c8ec..cb6bdc18b1f9f 100644 --- a/app/code/Magento/Dhl/Test/Mftf/Test/AdminCheckInputFieldsDisabledAfterAppConfigDumpTest.xml +++ b/app/code/Magento/Dhl/Test/Mftf/Test/AdminCheckInputFieldsDisabledAfterAppConfigDumpTest.xml @@ -15,32 +15,74 @@ <conditionalClick selector="{{AdminShippingMethodDHLSection.carriersDHLTab}}" dependentSelector="{{AdminShippingMethodDHLSection.carriersDHLActive}}" visible="false" stepKey="expandDHLTab"/> <waitForElementVisible selector="{{AdminShippingMethodDHLSection.carriersDHLActive}}" stepKey="waitDHLTabOpen"/> <grabAttributeFrom selector="{{AdminShippingMethodDHLSection.carriersDHLActive}}" userInput="disabled" stepKey="grabDHLActiveDisabled"/> - <assertEquals expected='true' expectedType="string" actual="$grabDHLActiveDisabled" stepKey="assertDHLActiveDisabled"/> + <assertEquals stepKey="assertDHLActiveDisabled"> + <actualResult type="const">$grabDHLActiveDisabled</actualResult> + <expectedResult type="string">true</expectedResult> + </assertEquals> <grabAttributeFrom selector="{{AdminShippingMethodDHLSection.carriersDHLTitle}}" userInput="disabled" stepKey="grabDHLTitleDisabled"/> - <assertEquals expected='true' expectedType="string" actual="$grabDHLTitleDisabled" stepKey="assertDHLTitleDisabled"/> + <assertEquals stepKey="assertDHLTitleDisabled"> + <actualResult type="const">$grabDHLTitleDisabled</actualResult> + <expectedResult type="string">true</expectedResult> + </assertEquals> <grabAttributeFrom selector="{{AdminShippingMethodDHLSection.carriersDHLAccessId}}" userInput="disabled" stepKey="grabDHLAccessIdDisabled"/> - <assertEquals expected='true' expectedType="string" actual="$grabDHLAccessIdDisabled" stepKey="assertDHLAccessIdDisabled"/> + <assertEquals stepKey="assertDHLAccessIdDisabled"> + <actualResult type="const">$grabDHLAccessIdDisabled</actualResult> + <expectedResult type="string">true</expectedResult> + </assertEquals> <grabAttributeFrom selector="{{AdminShippingMethodDHLSection.carriersDHLPassword}}" userInput="disabled" stepKey="grabDHLPasswordDisabled"/> - <assertEquals expected='true' expectedType="string" actual="$grabDHLPasswordDisabled" stepKey="assertDHLPasswordDisabled"/> + <assertEquals stepKey="assertDHLPasswordDisabled"> + <actualResult type="const">$grabDHLPasswordDisabled</actualResult> + <expectedResult type="string">true</expectedResult> + </assertEquals> <grabAttributeFrom selector="{{AdminShippingMethodDHLSection.carriersDHLAccount}}" userInput="disabled" stepKey="grabDHLAccountDisabled"/> - <assertEquals expected='true' expectedType="string" actual="$grabDHLAccountDisabled" stepKey="assertDHLAccountDisabled"/> + <assertEquals stepKey="assertDHLAccountDisabled"> + <actualResult type="const">$grabDHLAccountDisabled</actualResult> + <expectedResult type="string">true</expectedResult> + </assertEquals> <grabAttributeFrom selector="{{AdminShippingMethodDHLSection.carriersDHLContentType}}" userInput="disabled" stepKey="grabDHLContentTypeDisabled"/> - <assertEquals expected='true' expectedType="string" actual="$grabDHLContentTypeDisabled" stepKey="assertDHLContentTypeDisabled"/> + <assertEquals stepKey="assertDHLContentTypeDisabled"> + <actualResult type="const">$grabDHLContentTypeDisabled</actualResult> + <expectedResult type="string">true</expectedResult> + </assertEquals> <grabAttributeFrom selector="{{AdminShippingMethodDHLSection.carriersDHLHandlingType}}" userInput="disabled" stepKey="grabDHLHandlingTypeDisabled"/> - <assertEquals expected='true' expectedType="string" actual="$grabDHLHandlingTypeDisabled" stepKey="assertDHLHandlingTypeDisabled"/> + <assertEquals stepKey="assertDHLHandlingTypeDisabled"> + <actualResult type="const">$grabDHLHandlingTypeDisabled</actualResult> + <expectedResult type="string">true</expectedResult> + </assertEquals> <grabAttributeFrom selector="{{AdminShippingMethodDHLSection.carriersDHLHandlingAction}}" userInput="disabled" stepKey="grabDHLHandlingDisabled"/> - <assertEquals expected='true' expectedType="string" actual="$grabDHLHandlingDisabled" stepKey="assertDHLHandlingDisabled"/> + <assertEquals stepKey="assertDHLHandlingDisabled"> + <actualResult type="const">$grabDHLHandlingDisabled</actualResult> + <expectedResult type="string">true</expectedResult> + </assertEquals> <grabAttributeFrom selector="{{AdminShippingMethodDHLSection.carriersDHLDivideOrderWeight}}" userInput="disabled" stepKey="grabDHLDivideOrderWeightDisabled"/> - <assertEquals expected='true' expectedType="string" actual="$grabDHLDivideOrderWeightDisabled" stepKey="assertDHLDivideOrderWeightDisabled"/> + <assertEquals stepKey="assertDHLDivideOrderWeightDisabled"> + <actualResult type="const">$grabDHLDivideOrderWeightDisabled</actualResult> + <expectedResult type="string">true</expectedResult> + </assertEquals> <grabAttributeFrom selector="{{AdminShippingMethodDHLSection.carriersDHLUnitOfMeasure}}" userInput="disabled" stepKey="grabDHLUnitOfMeasureDisabled"/> - <assertEquals expected='true' expectedType="string" actual="$grabDHLUnitOfMeasureDisabled" stepKey="assertDHLUnitOfMeasureDisabled"/> + <assertEquals stepKey="assertDHLUnitOfMeasureDisabled"> + <actualResult type="const">$grabDHLUnitOfMeasureDisabled</actualResult> + <expectedResult type="string">true</expectedResult> + </assertEquals> <grabAttributeFrom selector="{{AdminShippingMethodDHLSection.carriersDHLSize}}" userInput="disabled" stepKey="grabDHLSizeDisabled"/> - <assertEquals expected='true' expectedType="string" actual="$grabDHLSizeDisabled" stepKey="assertDHLSizeDisabled"/> + <assertEquals stepKey="assertDHLSizeDisabled"> + <actualResult type="const">$grabDHLSizeDisabled</actualResult> + <expectedResult type="string">true</expectedResult> + </assertEquals> <grabAttributeFrom selector="{{AdminShippingMethodDHLSection.carriersDHLNonDocAllowedMethod}}" userInput="disabled" stepKey="grabDHLNonDocAllowedMethodDisabled"/> - <assertEquals expected='true' expectedType="string" actual="$grabDHLNonDocAllowedMethodDisabled" stepKey="assertDHLNonDocAllowedMethodDisabled"/> + <assertEquals stepKey="assertDHLNonDocAllowedMethodDisabled"> + <actualResult type="const">$grabDHLNonDocAllowedMethodDisabled</actualResult> + <expectedResult type="string">true</expectedResult> + </assertEquals> <grabAttributeFrom selector="{{AdminShippingMethodDHLSection.carriersDHLSmartPostHubId}}" userInput="disabled" stepKey="grabDHLSmartPostHubIdDisabled"/> - <assertEquals expected='true' expectedType="string" actual="$grabDHLSmartPostHubIdDisabled" stepKey="assertDHLSmartPostHubIdDisabled"/> + <assertEquals stepKey="assertDHLSmartPostHubIdDisabled"> + <actualResult type="const">$grabDHLSmartPostHubIdDisabled</actualResult> + <expectedResult type="string">true</expectedResult> + </assertEquals> <grabAttributeFrom selector="{{AdminShippingMethodDHLSection.carriersDHLSpecificErrMsg}}" userInput="disabled" stepKey="grabDHLSpecificErrMsgDisabled"/> - <assertEquals expected='true' expectedType="string" actual="$grabDHLSpecificErrMsgDisabled" stepKey="assertDHLSpecificErrMsgDisabled"/> + <assertEquals stepKey="assertDHLSpecificErrMsgDisabled"> + <actualResult type="const">$grabDHLSpecificErrMsgDisabled</actualResult> + <expectedResult type="string">true</expectedResult> + </assertEquals> </test> </tests> diff --git a/app/code/Magento/Dhl/Test/Unit/Model/CarrierTest.php b/app/code/Magento/Dhl/Test/Unit/Model/CarrierTest.php index d1b35c8e2b77f..1b9d1619a8d4c 100644 --- a/app/code/Magento/Dhl/Test/Unit/Model/CarrierTest.php +++ b/app/code/Magento/Dhl/Test/Unit/Model/CarrierTest.php @@ -187,7 +187,7 @@ public function scopeConfigGetValue($path) 'carriers/dhl/debug' => 1, 'shipping/origin/country_id' => 'GB' ]; - return isset($pathMap[$path]) ? $pathMap[$path] : null; + return $pathMap[$path] ?? null; } /** diff --git a/app/code/Magento/Dhl/etc/adminhtml/system.xml b/app/code/Magento/Dhl/etc/adminhtml/system.xml index 93a16821e385d..1728219236d9f 100644 --- a/app/code/Magento/Dhl/etc/adminhtml/system.xml +++ b/app/code/Magento/Dhl/etc/adminhtml/system.xml @@ -10,39 +10,39 @@ <section id="carriers"> <group id="dhl" translate="label" type="text" sortOrder="140" showInDefault="1" showInWebsite="1" showInStore="1"> <label>DHL</label> - <field id="active" translate="label" type="select" sortOrder="10" showInDefault="1" showInWebsite="1" showInStore="0" canRestore="1"> + <field id="active" translate="label" type="select" sortOrder="10" showInDefault="1" showInWebsite="1" canRestore="1"> <label>Enabled for Checkout</label> <source_model>Magento\Config\Model\Config\Source\Yesno</source_model> </field> <field id="title" translate="label" type="text" sortOrder="20" showInDefault="1" showInWebsite="1" showInStore="1" canRestore="1"> <label>Title</label> </field> - <field id="id" translate="label" type="obscure" sortOrder="50" showInDefault="1" showInWebsite="1" showInStore="0"> + <field id="id" translate="label" type="obscure" sortOrder="50" showInDefault="1" showInWebsite="1"> <label>Access ID</label> <backend_model>Magento\Config\Model\Config\Backend\Encrypted</backend_model> </field> - <field id="password" translate="label" type="obscure" sortOrder="60" showInDefault="1" showInWebsite="1" showInStore="0"> + <field id="password" translate="label" type="obscure" sortOrder="60" showInDefault="1" showInWebsite="1"> <label>Password</label> <backend_model>Magento\Config\Model\Config\Backend\Encrypted</backend_model> </field> - <field id="account" translate="label" type="text" sortOrder="70" showInDefault="1" showInWebsite="1" showInStore="0" canRestore="1"> + <field id="account" translate="label" type="text" sortOrder="70" showInDefault="1" showInWebsite="1" canRestore="1"> <label>Account Number</label> </field> - <field id="content_type" translate="label comment" type="select" sortOrder="90" showInDefault="1" showInWebsite="1" showInStore="0" canRestore="1"> + <field id="content_type" translate="label comment" type="select" sortOrder="90" showInDefault="1" showInWebsite="1" canRestore="1"> <label>Content Type (Non Domestic)</label> <comment>Whether to use Documents or NonDocuments service for non domestic shipments. (Shipments within the EU are classed as domestic)</comment> <source_model>Magento\Dhl\Model\Source\Contenttype</source_model> </field> - <field id="handling_type" translate="label" type="select" sortOrder="100" showInDefault="1" showInWebsite="1" showInStore="0" canRestore="1"> + <field id="handling_type" translate="label" type="select" sortOrder="100" showInDefault="1" showInWebsite="1" canRestore="1"> <label>Calculate Handling Fee</label> <source_model>Magento\Shipping\Model\Source\HandlingType</source_model> </field> - <field id="handling_action" translate="label comment" type="select" sortOrder="110" showInDefault="1" showInWebsite="1" showInStore="0" canRestore="1"> + <field id="handling_action" translate="label comment" type="select" sortOrder="110" showInDefault="1" showInWebsite="1" canRestore="1"> <label>Handling Applied</label> <comment>"Per Order" allows a single handling fee for the entire order. "Per Package" allows an individual handling fee for each package.</comment> <source_model>Magento\Shipping\Model\Source\HandlingAction</source_model> </field> - <field id="handling_fee" translate="label" type="text" sortOrder="120" showInDefault="1" showInWebsite="1" showInStore="0"> + <field id="handling_fee" translate="label" type="text" sortOrder="120" showInDefault="1" showInWebsite="1"> <label>Handling Fee</label> <validate>validate-number validate-zero-or-greater</validate> </field> @@ -78,22 +78,22 @@ <field id="size">1</field> </depends> </field> - <field id="doc_methods" translate="label" type="multiselect" sortOrder="170" showInDefault="1" showInWebsite="1" showInStore="0" canRestore="1"> + <field id="doc_methods" translate="label" type="multiselect" sortOrder="170" showInDefault="1" showInWebsite="1" canRestore="1"> <label>Documents Allowed Methods</label> <source_model>Magento\Dhl\Model\Source\Method\Doc</source_model> </field> - <field id="nondoc_methods" translate="label" type="multiselect" sortOrder="170" showInDefault="1" showInWebsite="1" showInStore="0" canRestore="1"> + <field id="nondoc_methods" translate="label" type="multiselect" sortOrder="170" showInDefault="1" showInWebsite="1" canRestore="1"> <label>Non Documents Allowed Methods</label> <source_model>Magento\Dhl\Model\Source\Method\Nondoc</source_model> </field> - <field id="ready_time" translate="label comment" type="text" sortOrder="180" showInDefault="1" showInWebsite="1" showInStore="0" canRestore="1"> + <field id="ready_time" translate="label comment" type="text" sortOrder="180" showInDefault="1" showInWebsite="1" canRestore="1"> <label>Ready time</label> <comment>Package ready time after order submission (in hours).</comment> </field> <field id="specificerrmsg" translate="label" type="textarea" sortOrder="800" showInDefault="1" showInWebsite="1" showInStore="1" canRestore="1"> <label>Displayed Error Message</label> </field> - <field id="free_method_doc" translate="label" type="select" sortOrder="1200" showInDefault="1" showInWebsite="1" showInStore="0"> + <field id="free_method_doc" translate="label" type="select" sortOrder="1200" showInDefault="1" showInWebsite="1"> <label>Free Method</label> <frontend_class>free-method</frontend_class> <source_model>Magento\Dhl\Model\Source\Method\Freedoc</source_model> @@ -101,7 +101,7 @@ <field id="content_type">D</field> </depends> </field> - <field id="free_method_nondoc" translate="label" type="select" sortOrder="1200" showInDefault="1" showInWebsite="1" showInStore="0"> + <field id="free_method_nondoc" translate="label" type="select" sortOrder="1200" showInDefault="1" showInWebsite="1"> <label>Free Method</label> <frontend_class>free-method</frontend_class> <source_model>Magento\Dhl\Model\Source\Method\Freenondoc</source_model> @@ -109,41 +109,41 @@ <field id="content_type">N</field> </depends> </field> - <field id="free_shipping_enable" translate="label" type="select" sortOrder="1210" showInDefault="1" showInWebsite="1" showInStore="0"> + <field id="free_shipping_enable" translate="label" type="select" sortOrder="1210" showInDefault="1" showInWebsite="1"> <label>Enable Free Shipping Threshold</label> <source_model>Magento\Config\Model\Config\Source\Enabledisable</source_model> </field> - <field id="free_shipping_subtotal" translate="label" type="text" sortOrder="1220" showInDefault="1" showInWebsite="1" showInStore="0"> + <field id="free_shipping_subtotal" translate="label" type="text" sortOrder="1220" showInDefault="1" showInWebsite="1"> <label>Free Shipping Amount Threshold</label> <validate>validate-number validate-zero-or-greater</validate> <depends> <field id="free_shipping_enable">1</field> </depends> </field> - <field id="sallowspecific" translate="label" type="select" sortOrder="1900" showInDefault="1" showInWebsite="1" showInStore="0" canRestore="1"> + <field id="sallowspecific" translate="label" type="select" sortOrder="1900" showInDefault="1" showInWebsite="1" canRestore="1"> <label>Ship to Applicable Countries</label> <frontend_class>shipping-applicable-country</frontend_class> <source_model>Magento\Shipping\Model\Config\Source\Allspecificcountries</source_model> </field> - <field id="specificcountry" translate="label" type="multiselect" sortOrder="1910" showInDefault="1" showInWebsite="1" showInStore="0"> + <field id="specificcountry" translate="label" type="multiselect" sortOrder="1910" showInDefault="1" showInWebsite="1"> <label>Ship to Specific Countries</label> <source_model>Magento\Directory\Model\Config\Source\Country</source_model> <can_be_empty>1</can_be_empty> </field> - <field id="showmethod" translate="label" type="select" sortOrder="1940" showInDefault="1" showInWebsite="1" showInStore="0"> + <field id="showmethod" translate="label" type="select" sortOrder="1940" showInDefault="1" showInWebsite="1"> <label>Show Method if Not Applicable</label> <frontend_class>shipping-skip-hide</frontend_class> <source_model>Magento\Config\Model\Config\Source\Yesno</source_model> </field> - <field id="sort_order" translate="label" type="text" sortOrder="2000" showInDefault="1" showInWebsite="1" showInStore="0"> + <field id="sort_order" translate="label" type="text" sortOrder="2000" showInDefault="1" showInWebsite="1"> <label>Sort Order</label> <validate>validate-number validate-zero-or-greater</validate> </field> - <field id="debug" translate="label" type="select" sortOrder="1950" showInDefault="1" showInWebsite="1" showInStore="0"> + <field id="debug" translate="label" type="select" sortOrder="1950" showInDefault="1" showInWebsite="1"> <label>Debug</label> <source_model>Magento\Config\Model\Config\Source\Yesno</source_model> </field> - <field id="sandbox_mode" translate="label" type="select" sortOrder="1960" showInDefault="1" showInWebsite="1" showInStore="0"> + <field id="sandbox_mode" translate="label" type="select" sortOrder="1960" showInDefault="1" showInWebsite="1"> <label>Sandbox Mode</label> <source_model>Magento\Config\Model\Config\Source\Yesno</source_model> </field> diff --git a/app/code/Magento/Dhl/etc/module.xml b/app/code/Magento/Dhl/etc/module.xml index b1af1baf97fc2..e46bb72ad7b3c 100644 --- a/app/code/Magento/Dhl/etc/module.xml +++ b/app/code/Magento/Dhl/etc/module.xml @@ -6,6 +6,5 @@ */ --> <config xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="urn:magento:framework:Module/etc/module.xsd"> - <module name="Magento_Dhl" > - </module> + <module name="Magento_Dhl"/> </config> diff --git a/app/code/Magento/Dhl/registration.php b/app/code/Magento/Dhl/registration.php index 4d69e96f1ff8a..a8a0b58a5c6f3 100644 --- a/app/code/Magento/Dhl/registration.php +++ b/app/code/Magento/Dhl/registration.php @@ -4,6 +4,6 @@ * See COPYING.txt for license details. */ -use \Magento\Framework\Component\ComponentRegistrar; +use Magento\Framework\Component\ComponentRegistrar; ComponentRegistrar::register(ComponentRegistrar::MODULE, 'Magento_Dhl', __DIR__); diff --git a/app/code/Magento/Directory/Model/AllowedCountries.php b/app/code/Magento/Directory/Model/AllowedCountries.php index b9f2d829dd1a6..2ceeb70ba5b01 100644 --- a/app/code/Magento/Directory/Model/AllowedCountries.php +++ b/app/code/Magento/Directory/Model/AllowedCountries.php @@ -62,13 +62,11 @@ public function getAllowedCountries( switch ($scope) { case ScopeInterface::SCOPE_WEBSITES: case ScopeInterface::SCOPE_STORES: - $allowedCountries = []; + $allowedCountries = [[]]; foreach ($scopeCode as $singleFilter) { - $allowedCountries = array_merge( - $allowedCountries, - $this->getCountriesFromConfig($this->getSingleScope($scope), $singleFilter) - ); + $allowedCountries[] = $this->getCountriesFromConfig($this->getSingleScope($scope), $singleFilter); } + $allowedCountries = array_merge(...$allowedCountries); break; default: $allowedCountries = $this->getCountriesFromConfig($scope, $scopeCode); diff --git a/app/code/Magento/Directory/Setup/Patch/Data/AddDataForItaly.php b/app/code/Magento/Directory/Setup/Patch/Data/AddDataForItaly.php new file mode 100644 index 0000000000000..a5d4bcdc0b8be --- /dev/null +++ b/app/code/Magento/Directory/Setup/Patch/Data/AddDataForItaly.php @@ -0,0 +1,196 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +declare(strict_types=1); + +namespace Magento\Directory\Setup\Patch\Data; + +use Magento\Directory\Setup\DataInstaller; +use Magento\Directory\Setup\DataInstallerFactory; +use Magento\Framework\Setup\ModuleDataSetupInterface; +use Magento\Framework\Setup\Patch\DataPatchInterface; + +/** + * Add Italy States + */ +class AddDataForItaly implements DataPatchInterface +{ + /** + * @var ModuleDataSetupInterface + */ + private $moduleDataSetup; + + /** + * @var DataInstallerFactory + */ + private $dataInstallerFactory; + + /** + * @param ModuleDataSetupInterface $moduleDataSetup + * @param DataInstallerFactory $dataInstallerFactory + */ + public function __construct( + ModuleDataSetupInterface $moduleDataSetup, + DataInstallerFactory $dataInstallerFactory + ) { + $this->moduleDataSetup = $moduleDataSetup; + $this->dataInstallerFactory = $dataInstallerFactory; + } + + /** + * @inheritdoc + */ + public function apply() + { + /** @var DataInstaller $dataInstaller */ + $dataInstaller = $this->dataInstallerFactory->create(); + $dataInstaller->addCountryRegions( + $this->moduleDataSetup->getConnection(), + $this->getDataForItaly() + ); + + return $this; + } + + /** + * Italy states data. + * + * @return array + * @SuppressWarnings(PHPMD.ExcessiveMethodLength) + */ + private function getDataForItaly() + { + return [ + ['IT', 'AG', 'Agrigento'], + ['IT', 'AL', 'Alessandria'], + ['IT', 'AN', 'Ancona'], + ['IT', 'AO', 'Aosta'], + ['IT', 'AQ', 'L\'Aquila'], + ['IT', 'AR', 'Arezzo'], + ['IT', 'AP', 'Ascoli-Piceno'], + ['IT', 'AT', 'Asti'], + ['IT', 'AV', 'Avellino'], + ['IT', 'BA', 'Bari'], + ['IT', 'BT', 'Barletta-Andria-Trani'], + ['IT', 'BL', 'Belluno'], + ['IT', 'BN', 'Benevento'], + ['IT', 'BG', 'Bergamo'], + ['IT', 'BI', 'Biella'], + ['IT', 'BO', 'Bologna'], + ['IT', 'BZ', 'Bolzano'], + ['IT', 'BS', 'Brescia'], + ['IT', 'BR', 'Brindisi'], + ['IT', 'CA', 'Cagliari'], + ['IT', 'CL', 'Caltanissetta'], + ['IT', 'CB', 'Campobasso'], + ['IT', 'CI', 'Carbonia Iglesias'], + ['IT', 'CE', 'Caserta'], + ['IT', 'CT', 'Catania'], + ['IT', 'CZ', 'Catanzaro'], + ['IT', 'CH', 'Chieti'], + ['IT', 'CO', 'Como'], + ['IT', 'CS', 'Cosenza'], + ['IT', 'CR', 'Cremona'], + ['IT', 'KR', 'Crotone'], + ['IT', 'CN', 'Cuneo'], + ['IT', 'EN', 'Enna'], + ['IT', 'FM', 'Fermo'], + ['IT', 'FE', 'Ferrara'], + ['IT', 'FI', 'Firenze'], + ['IT', 'FG', 'Foggia'], + ['IT', 'FC', 'Forli-Cesena'], + ['IT', 'FR', 'Frosinone'], + ['IT', 'GE', 'Genova'], + ['IT', 'GO', 'Gorizia'], + ['IT', 'GR', 'Grosseto'], + ['IT', 'IM', 'Imperia'], + ['IT', 'IS', 'Isernia'], + ['IT', 'SP', 'La-Spezia'], + ['IT', 'LT', 'Latina'], + ['IT', 'LE', 'Lecce'], + ['IT', 'LC', 'Lecco'], + ['IT', 'LI', 'Livorno'], + ['IT', 'LO', 'Lodi'], + ['IT', 'LU', 'Lucca'], + ['IT', 'MC', 'Macerata'], + ['IT', 'MN', 'Mantova'], + ['IT', 'MS', 'Massa-Carrara'], + ['IT', 'MT', 'Matera'], + ['IT', 'VS', 'Medio Campidano'], + ['IT', 'ME', 'Messina'], + ['IT', 'MI', 'Milano'], + ['IT', 'MO', 'Modena'], + ['IT', 'MB', 'Monza-Brianza'], + ['IT', 'NA', 'Napoli'], + ['IT', 'NO', 'Novara'], + ['IT', 'NU', 'Nuoro'], + ['IT', 'OG', 'Ogliastra'], + ['IT', 'OT', 'Olbia Tempio'], + ['IT', 'OR', 'Oristano'], + ['IT', 'PD', 'Padova'], + ['IT', 'PA', 'Palermo'], + ['IT', 'PR', 'Parma'], + ['IT', 'PV', 'Pavia'], + ['IT', 'PG', 'Perugia'], + ['IT', 'PU', 'Pesaro-Urbino'], + ['IT', 'PE', 'Pescara'], + ['IT', 'PC', 'Piacenza'], + ['IT', 'PI', 'Pisa'], + ['IT', 'PT', 'Pistoia'], + ['IT', 'PN', 'Pordenone'], + ['IT', 'PZ', 'Potenza'], + ['IT', 'PO', 'Prato'], + ['IT', 'RG', 'Ragusa'], + ['IT', 'RA', 'Ravenna'], + ['IT', 'RC', 'Reggio-Calabria'], + ['IT', 'RE', 'Reggio-Emilia'], + ['IT', 'RI', 'Rieti'], + ['IT', 'RN', 'Rimini'], + ['IT', 'RM', 'Roma'], + ['IT', 'RO', 'Rovigo'], + ['IT', 'SA', 'Salerno'], + ['IT', 'SS', 'Sassari'], + ['IT', 'SV', 'Savona'], + ['IT', 'SI', 'Siena'], + ['IT', 'SR', 'Siracusa'], + ['IT', 'SO', 'Sondrio'], + ['IT', 'TA', 'Taranto'], + ['IT', 'TE', 'Teramo'], + ['IT', 'TR', 'Terni'], + ['IT', 'TO', 'Torino'], + ['IT', 'TP', 'Trapani'], + ['IT', 'TN', 'Trento'], + ['IT', 'TV', 'Treviso'], + ['IT', 'TS', 'Trieste'], + ['IT', 'UD', 'Udine'], + ['IT', 'VA', 'Varese'], + ['IT', 'VE', 'Venezia'], + ['IT', 'VB', 'Verbania'], + ['IT', 'VC', 'Vercelli'], + ['IT', 'VR', 'Verona'], + ['IT', 'VV', 'Vibo-Valentia'], + ['IT', 'VI', 'Vicenza'], + ['IT', 'VT', 'Viterbo'], + ]; + } + + /** + * @inheritdoc + */ + public static function getDependencies() + { + return [ + InitializeDirectoryData::class, + ]; + } + + /** + * @inheritdoc + */ + public function getAliases() + { + return []; + } +} diff --git a/app/code/Magento/Directory/Test/Mftf/Data/GeneralStateOptionsConfigData.xml b/app/code/Magento/Directory/Test/Mftf/Data/GeneralStateOptionsConfigData.xml new file mode 100644 index 0000000000000..8b72e3e93efb9 --- /dev/null +++ b/app/code/Magento/Directory/Test/Mftf/Data/GeneralStateOptionsConfigData.xml @@ -0,0 +1,21 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + /** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +--> + +<entities xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:noNamespaceSchemaLocation="urn:magento:mftf:DataGenerator/etc/dataProfileSchema.xsd"> + <entity name="AllowToChooseStateIfItIsOptionalForCountryEnabled"> + <data key="path">general/region/display_all</data> + <data key="label">Yes</data> + <data key="value">1</data> + </entity> + <entity name="AllowToChooseStateIfItIsOptionalForCountryDisabled"> + <data key="path">general/region/display_all</data> + <data key="label">No</data> + <data key="value">0</data> + </entity> +</entities> diff --git a/app/code/Magento/Directory/Test/Mftf/Test/AdminScheduledImportSettingsHiddenTest.xml b/app/code/Magento/Directory/Test/Mftf/Test/AdminScheduledImportSettingsHiddenTest.xml index 0320b6f422cd6..5f3504fe0921e 100644 --- a/app/code/Magento/Directory/Test/Mftf/Test/AdminScheduledImportSettingsHiddenTest.xml +++ b/app/code/Magento/Directory/Test/Mftf/Test/AdminScheduledImportSettingsHiddenTest.xml @@ -6,25 +6,26 @@ */ --> <tests xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" - xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/testSchema.xsd"> + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/testSchema.xsd"> <test name="AdminScheduledImportSettingsHiddenTest"> <annotations> <features value="Directory"/> - <title value="Scheduled import settings hidden" /> - <description value="Scheduled Import Settings' should hide fields when 'Enabled' is 'No'"/> + <title value="Scheduled import settings hidden"/> + <stories value="Fields visibility according to 'Enable' value"/> + <description value="Scheduled Import Settings should hide fields when 'Enabled' is 'No'"/> <severity value="MINOR"/> </annotations> <before> - <actionGroup ref="LoginAsAdmin" stepKey="loginAsAdmin"/> + <actionGroup ref="AdminLoginActionGroup" stepKey="loginAsAdmin"/> <magentoCLI command="config:set currency/import/enabled 1" stepKey="enableCurrencyImport"/> </before> <after> - <actionGroup ref="logout" stepKey="logout"/> + <actionGroup ref="AdminLogoutActionGroup" stepKey="logout"/> <magentoCLI command="config:set currency/import/enabled 0" stepKey="disableCurrencyImport"/> </after> - <amOnPage url="{{AdminCurrencySetupPage.url}}" stepKey="openCurrencyOptionsPage" /> + <amOnPage url="{{AdminCurrencySetupPage.url}}" stepKey="openCurrencyOptionsPage"/> <conditionalClick dependentSelector="{{AdminScheduledImportSettingsSection.enabled}}" visible="false" selector="{{AdminScheduledImportSettingsSection.head}}" stepKey="openCollapsibleBlock"/> <see selector="{{AdminScheduledImportSettingsSection.service}}" userInput="Fixer.io" stepKey="seeServiceFixerIo"/> <selectOption selector="{{AdminScheduledImportSettingsSection.enabled}}" userInput="0" stepKey="disableCurrencyImportOption"/> diff --git a/app/code/Magento/Directory/Test/Unit/Block/Adminhtml/Frontend/Currency/BaseTest.php b/app/code/Magento/Directory/Test/Unit/Block/Adminhtml/Frontend/Currency/BaseTest.php new file mode 100644 index 0000000000000..9d38a2c72f3ac --- /dev/null +++ b/app/code/Magento/Directory/Test/Unit/Block/Adminhtml/Frontend/Currency/BaseTest.php @@ -0,0 +1,110 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ + +declare(strict_types=1); + +namespace Magento\Directory\Test\Unit\Block\Adminhtml\Frontend\Currency; + +use Magento\Directory\Block\Adminhtml\Frontend\Currency\Base; +use Magento\Framework\App\Config\ScopeConfigInterface; +use Magento\Framework\App\RequestInterface; +use Magento\Framework\Data\Form\Element\AbstractElement; +use Magento\Framework\TestFramework\Unit\Helper\ObjectManager as ObjectManagerHelper; +use Magento\Store\Model\Store; +use PHPUnit\Framework\MockObject\MockObject; +use PHPUnit\Framework\TestCase; + +/** + * Unit test for \Magento\Directory\Block\Adminhtml\Frontend\Currency\Base + */ +class BaseTest extends TestCase +{ + const STUB_WEBSITE_PARAM = 'website'; + + /** + * @var AbstractElement|MockObject + */ + private $elementMock; + + /** + * @var RequestInterface|MockObject + */ + private $requestMock; + + /** + * @var ScopeConfigInterface|MockObject + */ + private $scopeConfigMock; + + /** + * @var Base + */ + private $baseCurrency; + + /** + * @inheritDoc + */ + protected function setUp() + { + $this->elementMock = $this->createMock(AbstractElement::class); + $this->scopeConfigMock = $this->createMock(ScopeConfigInterface::class); + $this->requestMock = $this->getMockBuilder(RequestInterface::class) + ->disableOriginalConstructor() + ->setMethods(['getParam']) + ->getMockForAbstractClass(); + + $this->baseCurrency = (new ObjectManagerHelper($this))->getObject( + Base::class, + ['_request' => $this->requestMock, '_scopeConfig' => $this->scopeConfigMock] + ); + } + + /** + * Test case when no Website param provided + */ + public function testRenderWithoutWebsiteParam() + { + $this->requestMock->expects($this->once()) + ->method('getParam') + ->willReturn(''); + $this->scopeConfigMock->expects($this->never())->method('getValue'); + + $result = $this->baseCurrency->render(($this->elementMock)); + $this->assertFalse(empty($result), 'Result should not be empty.'); + } + + /** + * Test case when Website param is provided and Price Scope is set to Global + */ + public function testRenderWhenWebsiteParamSetAndPriceScopeGlobal() + { + $this->requestMock->expects($this->once()) + ->method('getParam') + ->willReturn(self::STUB_WEBSITE_PARAM); + $this->scopeConfigMock->expects($this->once()) + ->method('getValue') + ->willReturn(Store::PRICE_SCOPE_GLOBAL); + + $result = $this->baseCurrency->render(($this->elementMock)); + $this->assertEquals('', $result, 'Result should be an empty string.'); + } + + /** + * Test case when Website param is provided and Price Scope is not Global + */ + public function testRenderWhenWebsiteParamSetAndPriceScopeOther() + { + $this->requestMock->expects($this->once()) + ->method('getParam') + ->willReturn(self::STUB_WEBSITE_PARAM); + $this->scopeConfigMock->expects($this->once()) + ->method('getValue') + ->willReturn(Store::PRICE_SCOPE_WEBSITE); + + $result = $this->baseCurrency->render(($this->elementMock)); + $this->assertFalse(empty($result), 'Result should not be empty.'); + } +} diff --git a/app/code/Magento/Directory/etc/adminhtml/system.xml b/app/code/Magento/Directory/etc/adminhtml/system.xml index 5f97a5e8d90d6..6fa47037d2645 100644 --- a/app/code/Magento/Directory/etc/adminhtml/system.xml +++ b/app/code/Magento/Directory/etc/adminhtml/system.xml @@ -13,7 +13,7 @@ <resource>Magento_Config::currency</resource> <group id="options" translate="label" sortOrder="30" showInDefault="1" showInWebsite="1" showInStore="1"> <label>Currency Options</label> - <field id="base" translate="label comment" type="select" sortOrder="1" showInDefault="1" showInWebsite="1" showInStore="0" canRestore="1"> + <field id="base" translate="label comment" type="select" sortOrder="1" showInDefault="1" showInWebsite="1" canRestore="1"> <label>Base Currency</label> <frontend_model>Magento\Directory\Block\Adminhtml\Frontend\Currency\Base</frontend_model> <source_model>Magento\Config\Model\Config\Source\Locale\Currency</source_model> @@ -34,31 +34,31 @@ <can_be_empty>1</can_be_empty> </field> </group> - <group id="fixerio" translate="label" sortOrder="35" showInDefault="1" showInWebsite="0" showInStore="0"> + <group id="fixerio" translate="label" sortOrder="35" showInDefault="1"> <label>Fixer.io</label> - <field id="api_key" translate="label" type="obscure" sortOrder="5" showInDefault="1" showInWebsite="0" showInStore="0"> + <field id="api_key" translate="label" type="obscure" sortOrder="5" showInDefault="1"> <label>API Key</label> <config_path>currency/fixerio/api_key</config_path> <backend_model>Magento\Config\Model\Config\Backend\Encrypted</backend_model> </field> - <field id="timeout" translate="label" type="text" sortOrder="10" showInDefault="1" showInWebsite="0" showInStore="0"> + <field id="timeout" translate="label" type="text" sortOrder="10" showInDefault="1"> <label>Connection Timeout in Seconds</label> <validate>validate-zero-or-greater validate-number</validate> </field> </group> - <group id="currencyconverterapi" translate="label" sortOrder="45" showInDefault="1" showInWebsite="0" showInStore="0"> + <group id="currencyconverterapi" translate="label" sortOrder="45" showInDefault="1"> <label>Currency Converter API</label> - <field id="api_key" translate="label" type="obscure" sortOrder="5" showInDefault="1" showInWebsite="0" showInStore="0"> + <field id="api_key" translate="label" type="obscure" sortOrder="5" showInDefault="1"> <label>API Key</label> <config_path>currency/currencyconverterapi/api_key</config_path> <backend_model>Magento\Config\Model\Config\Backend\Encrypted</backend_model> </field> - <field id="timeout" translate="label" type="text" sortOrder="10" showInDefault="1" showInWebsite="0" showInStore="0"> + <field id="timeout" translate="label" type="text" sortOrder="10" showInDefault="1"> <label>Connection Timeout in Seconds</label> <validate>validate-zero-or-greater validate-number</validate> </field> </group> - <group id="import" translate="label" type="text" sortOrder="50" showInDefault="1" showInWebsite="0" showInStore="0"> + <group id="import" translate="label" type="text" sortOrder="50" showInDefault="1"> <label>Scheduled Import Settings</label> <field id="enabled" translate="label" type="select" sortOrder="1" showInDefault="1" showInWebsite="1" showInStore="1" canRestore="1"> <label>Enabled</label> @@ -71,14 +71,14 @@ <field id="enabled">1</field> </depends> </field> - <field id="error_email_identity" translate="label" type="select" sortOrder="6" showInDefault="1" showInWebsite="1" showInStore="0" canRestore="1"> + <field id="error_email_identity" translate="label" type="select" sortOrder="6" showInDefault="1" showInWebsite="1" canRestore="1"> <label>Error Email Sender</label> <source_model>Magento\Config\Model\Config\Source\Email\Identity</source_model> <depends> <field id="enabled">1</field> </depends> </field> - <field id="error_email_template" translate="label comment" type="select" sortOrder="7" showInDefault="1" showInWebsite="1" showInStore="0" canRestore="1"> + <field id="error_email_template" translate="label comment" type="select" sortOrder="7" showInDefault="1" showInWebsite="1" canRestore="1"> <label>Error Email Template</label> <comment>Email template chosen based on theme fallback when "Default" option is selected.</comment> <source_model>Magento\Config\Model\Config\Source\Email\Template</source_model> @@ -110,9 +110,9 @@ </group> </section> <section id="system"> - <group id="currency" translate="label" type="text" sortOrder="50" showInDefault="1" showInWebsite="0" showInStore="0"> + <group id="currency" translate="label" type="text" sortOrder="50" showInDefault="1"> <label>Currency</label> - <field id="installed" translate="label" type="multiselect" sortOrder="1" showInDefault="1" showInWebsite="0" showInStore="0" canRestore="1"> + <field id="installed" translate="label" type="multiselect" sortOrder="1" showInDefault="1" canRestore="1"> <label>Installed Currencies</label> <backend_model>Magento\Config\Model\Config\Backend\Locale</backend_model> <source_model>Magento\Config\Model\Config\Source\Locale\Currency\All</source_model> @@ -122,20 +122,20 @@ </section> <section id="general"> <group id="country"> - <field id="optional_zip_countries" translate="label" type="multiselect" sortOrder="3" showInDefault="1" showInWebsite="0" showInStore="0" canRestore="1"> + <field id="optional_zip_countries" translate="label" type="multiselect" sortOrder="3" showInDefault="1" canRestore="1"> <label>Zip/Postal Code is Optional for</label> <source_model>Magento\Directory\Model\Config\Source\Country</source_model> <can_be_empty>1</can_be_empty> </field> </group> - <group id="region" translate="label" type="text" sortOrder="4" showInDefault="1" showInWebsite="0" showInStore="0"> + <group id="region" translate="label" type="text" sortOrder="4" showInDefault="1"> <label>State Options</label> - <field id="state_required" translate="label" type="multiselect" sortOrder="1" showInDefault="1" showInWebsite="0" showInStore="0"> + <field id="state_required" translate="label" type="multiselect" sortOrder="1" showInDefault="1"> <label>State is Required for</label> <source_model>Magento\Directory\Model\Config\Source\Country</source_model> <can_be_empty>1</can_be_empty> </field> - <field id="display_all" translate="label" type="select" sortOrder="8" showInDefault="1" showInWebsite="0" showInStore="0"> + <field id="display_all" translate="label" type="select" sortOrder="8" showInDefault="1"> <label>Allow to Choose State if It is Optional for Country</label> <source_model>Magento\Config\Model\Config\Source\Yesno</source_model> </field> diff --git a/app/code/Magento/Directory/etc/db_schema.xml b/app/code/Magento/Directory/etc/db_schema.xml index 163e972423b98..a9fb2c536a3fd 100644 --- a/app/code/Magento/Directory/etc/db_schema.xml +++ b/app/code/Magento/Directory/etc/db_schema.xml @@ -45,7 +45,7 @@ </table> <table name="directory_country_region_name" resource="default" engine="innodb" comment="Directory Country Region Name"> - <column xsi:type="varchar" name="locale" nullable="false" length="8" comment="Locale"/> + <column xsi:type="varchar" name="locale" nullable="false" length="16" comment="Locale"/> <column xsi:type="int" name="region_id" padding="10" unsigned="true" nullable="false" identity="false" default="0" comment="Region ID"/> <column xsi:type="varchar" name="name" nullable="true" length="255" comment="Region Name"/> diff --git a/app/code/Magento/Directory/etc/frontend/sections.xml b/app/code/Magento/Directory/etc/frontend/sections.xml index a2bc5696abf08..48d63ec82d95b 100644 --- a/app/code/Magento/Directory/etc/frontend/sections.xml +++ b/app/code/Magento/Directory/etc/frontend/sections.xml @@ -7,5 +7,7 @@ --> <config xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="urn:magento:module:Magento_Customer:etc/sections.xsd"> - <action name="directory/currency/switch"/> + <action name="directory/currency/switch"> + <section name="*"/> + </action> </config> diff --git a/app/code/Magento/Directory/registration.php b/app/code/Magento/Directory/registration.php index 17dece4a9d79d..142b6a3114384 100644 --- a/app/code/Magento/Directory/registration.php +++ b/app/code/Magento/Directory/registration.php @@ -4,6 +4,6 @@ * See COPYING.txt for license details. */ -use \Magento\Framework\Component\ComponentRegistrar; +use Magento\Framework\Component\ComponentRegistrar; ComponentRegistrar::register(ComponentRegistrar::MODULE, 'Magento_Directory', __DIR__); diff --git a/app/code/Magento/Downloadable/Block/Adminhtml/Catalog/Product/Edit/Tab/Downloadable/Links.php b/app/code/Magento/Downloadable/Block/Adminhtml/Catalog/Product/Edit/Tab/Downloadable/Links.php index 47c66c98fc8fb..e0026765f269b 100644 --- a/app/code/Magento/Downloadable/Block/Adminhtml/Catalog/Product/Edit/Tab/Downloadable/Links.php +++ b/app/code/Magento/Downloadable/Block/Adminhtml/Catalog/Product/Edit/Tab/Downloadable/Links.php @@ -11,7 +11,7 @@ * @author Magento Core Team <core@magentocommerce.com> * @SuppressWarnings(PHPMD.CouplingBetweenObjects) * - * @deprecated + * @deprecated in favor of new class which adds grid links * @see \Magento\Downloadable\Ui\DataProvider\Product\Form\Modifier\Links */ class Links extends \Magento\Backend\Block\Template @@ -398,7 +398,7 @@ public function getFileFieldName($type) */ public function getUploadUrl($type) { - return $this->_urlFactory->create()->addSessionParam()->getUrl( + return $this->_urlFactory->create()->getUrl( 'adminhtml/downloadable_file/upload', ['type' => $type, '_secure' => true] ); diff --git a/app/code/Magento/Downloadable/Block/Adminhtml/Catalog/Product/Edit/Tab/Downloadable/Samples.php b/app/code/Magento/Downloadable/Block/Adminhtml/Catalog/Product/Edit/Tab/Downloadable/Samples.php index f245aeeeead67..83a5a93405158 100644 --- a/app/code/Magento/Downloadable/Block/Adminhtml/Catalog/Product/Edit/Tab/Downloadable/Samples.php +++ b/app/code/Magento/Downloadable/Block/Adminhtml/Catalog/Product/Edit/Tab/Downloadable/Samples.php @@ -10,7 +10,7 @@ * * @author Magento Core Team <core@magentocommerce.com> * - * @deprecated + * @deprecated because of new class which adds grids samples * @see \Magento\Downloadable\Ui\DataProvider\Product\Form\Modifier\Samples */ class Samples extends \Magento\Backend\Block\Widget @@ -250,7 +250,7 @@ public function getUploadButtonHtml() */ public function getConfigJson() { - $url = $this->_urlFactory->create()->addSessionParam()->getUrl( + $url = $this->_urlFactory->create()->getUrl( 'adminhtml/downloadable_file/upload', ['type' => 'samples', '_secure' => true] ); diff --git a/app/code/Magento/Downloadable/Model/Link/Builder.php b/app/code/Magento/Downloadable/Model/Link/Builder.php index ff76f7eeda440..eefed4c5453ab 100644 --- a/app/code/Magento/Downloadable/Model/Link/Builder.php +++ b/app/code/Magento/Downloadable/Model/Link/Builder.php @@ -3,6 +3,7 @@ * Copyright © Magento, Inc. All rights reserved. * See COPYING.txt for license details. */ + namespace Magento\Downloadable\Model\Link; use Magento\Downloadable\Helper\File; @@ -12,7 +13,8 @@ use Magento\Framework\DataObject\Copy; /** - * Class Builder + * Builder download link model for downloadable product + * * @api * @since 100.1.0 */ @@ -116,7 +118,7 @@ public function build(\Magento\Downloadable\Api\Data\LinkInterface $link) $link->setLinkFile($linkFileName); $link->setLinkUrl(null); } - + if (isset($this->data['sample'])) { $link = $this->buildSample($link, $this->data['sample']); } @@ -132,6 +134,12 @@ public function build(\Magento\Downloadable\Api\Data\LinkInterface $link) if (isset($this->data['is_unlimited']) && $this->data['is_unlimited']) { $link->setNumberOfDownloads(0); } + + $useDefaultTitle = $this->data['use_default_title'] ?? false; + + if ($useDefaultTitle) { + $link->setTitle(null); + } $this->resetData(); return $link; diff --git a/app/code/Magento/Downloadable/Model/Sample/Builder.php b/app/code/Magento/Downloadable/Model/Sample/Builder.php index 7cc7cb640ae74..368d190319766 100644 --- a/app/code/Magento/Downloadable/Model/Sample/Builder.php +++ b/app/code/Magento/Downloadable/Model/Sample/Builder.php @@ -3,6 +3,7 @@ * Copyright © Magento, Inc. All rights reserved. * See COPYING.txt for license details. */ + namespace Magento\Downloadable\Model\Sample; use Magento\Downloadable\Api\Data\SampleInterface; @@ -14,7 +15,8 @@ use Magento\Framework\DataObject\Copy; /** - * Class Builder + * Builder download sample link model for downloadable product + * * @api * @since 100.1.0 */ @@ -24,7 +26,7 @@ class Builder * @var Sample */ private $component; - + /** * @var File */ @@ -71,6 +73,8 @@ public function __construct( } /** + * Init data for builder + * * @param array $data * @return $this; * @since 100.1.0 @@ -82,6 +86,8 @@ public function setData(array $data) } /** + * Build sample link + * * @param SampleInterface $sample * @return SampleInterface * @throws \Magento\Framework\Exception\LocalizedException @@ -116,12 +122,20 @@ public function build(SampleInterface $sample) if (!$sample->getSortOrder()) { $sample->setSortOrder(1); } + + $useDefaultTitle = $this->data['use_default_title'] ?? false; + + if ($useDefaultTitle) { + $sample->setTitle(null); + } $this->resetData(); return $sample; } /** + * Reset data + * * @return void */ private function resetData() @@ -130,6 +144,8 @@ private function resetData() } /** + * Get component + * * @return Sample */ private function getComponent() diff --git a/app/code/Magento/Downloadable/Model/Url/DomainValidator.php b/app/code/Magento/Downloadable/Model/Url/DomainValidator.php index cab7fb134ea33..68ab34c65a8d1 100644 --- a/app/code/Magento/Downloadable/Model/Url/DomainValidator.php +++ b/app/code/Magento/Downloadable/Model/Url/DomainValidator.php @@ -9,7 +9,7 @@ use Magento\Downloadable\Api\DomainManagerInterface as DomainManager; use Magento\Framework\Validator\Ip as IpValidator; -use Zend\Uri\Uri as UriHandler; +use Laminas\Uri\Uri as UriHandler; /** * Class is responsible for checking if downloadable product link domain is allowed. diff --git a/app/code/Magento/Downloadable/Setup/Patch/Data/AddDownloadableHostsConfig.php b/app/code/Magento/Downloadable/Setup/Patch/Data/AddDownloadableHostsConfig.php index 0e88bd166b604..48a0eebf0205b 100644 --- a/app/code/Magento/Downloadable/Setup/Patch/Data/AddDownloadableHostsConfig.php +++ b/app/code/Magento/Downloadable/Setup/Patch/Data/AddDownloadableHostsConfig.php @@ -7,21 +7,22 @@ namespace Magento\Downloadable\Setup\Patch\Data; +use Laminas\Uri\Uri as UriHandler; +use Magento\Backend\App\Area\FrontNameResolver; use Magento\Config\Model\Config\Backend\Admin\Custom; +use Magento\Downloadable\Api\DomainManagerInterface as DomainManager; use Magento\Framework\App\Config\ScopeConfigInterface; use Magento\Framework\Exception\NoSuchEntityException; +use Magento\Framework\Setup\ModuleDataSetupInterface; use Magento\Framework\Setup\Patch\DataPatchInterface; +use Magento\Framework\Url\ScopeResolverInterface; use Magento\Framework\UrlInterface; use Magento\Store\Model\ScopeInterface; use Magento\Store\Model\Store; -use Zend\Uri\Uri as UriHandler; -use Magento\Framework\Url\ScopeResolverInterface; -use Magento\Downloadable\Api\DomainManagerInterface as DomainManager; -use Magento\Framework\Setup\ModuleDataSetupInterface; -use Magento\Backend\App\Area\FrontNameResolver; /** * Adding base url as allowed downloadable domain. + * @SuppressWarnings(PHPMD.CouplingBetweenObjects) */ class AddDownloadableHostsConfig implements DataPatchInterface { @@ -79,7 +80,7 @@ public function __construct( } /** - * @inheritdoc + * @inheritDoc */ public function apply() { @@ -141,6 +142,8 @@ public function apply() } $this->domainManager->addDomains($this->whitelist); + + return $this; } /** diff --git a/app/code/Magento/Downloadable/Test/Mftf/Metadata/downloadable_link-meta.xml b/app/code/Magento/Downloadable/Test/Mftf/Metadata/DownloadableLinkMeta.xml similarity index 100% rename from app/code/Magento/Downloadable/Test/Mftf/Metadata/downloadable_link-meta.xml rename to app/code/Magento/Downloadable/Test/Mftf/Metadata/DownloadableLinkMeta.xml diff --git a/app/code/Magento/Downloadable/Test/Mftf/Metadata/downloadable_link_sample-meta.xml b/app/code/Magento/Downloadable/Test/Mftf/Metadata/DownloadableLinkSampleMeta.xml similarity index 100% rename from app/code/Magento/Downloadable/Test/Mftf/Metadata/downloadable_link_sample-meta.xml rename to app/code/Magento/Downloadable/Test/Mftf/Metadata/DownloadableLinkSampleMeta.xml diff --git a/app/code/Magento/Downloadable/Test/Mftf/Metadata/link_file_content-meta.xml b/app/code/Magento/Downloadable/Test/Mftf/Metadata/LinkFileContentMeta.xml similarity index 100% rename from app/code/Magento/Downloadable/Test/Mftf/Metadata/link_file_content-meta.xml rename to app/code/Magento/Downloadable/Test/Mftf/Metadata/LinkFileContentMeta.xml diff --git a/app/code/Magento/Downloadable/Test/Mftf/Metadata/sample_file_content-meta.xml b/app/code/Magento/Downloadable/Test/Mftf/Metadata/SampleFileContentMeta.xml similarity index 100% rename from app/code/Magento/Downloadable/Test/Mftf/Metadata/sample_file_content-meta.xml rename to app/code/Magento/Downloadable/Test/Mftf/Metadata/SampleFileContentMeta.xml diff --git a/app/code/Magento/Downloadable/Test/Mftf/Test/AdminAddDefaultImageDownloadableProductTest.xml b/app/code/Magento/Downloadable/Test/Mftf/Test/AdminAddDefaultImageDownloadableProductTest.xml index e5633707824dd..c634a8426eac0 100644 --- a/app/code/Magento/Downloadable/Test/Mftf/Test/AdminAddDefaultImageDownloadableProductTest.xml +++ b/app/code/Magento/Downloadable/Test/Mftf/Test/AdminAddDefaultImageDownloadableProductTest.xml @@ -20,7 +20,7 @@ </annotations> <before> <magentoCLI stepKey="addDownloadableDomain" command="downloadable:domains:add static.magento.com"/> - <actionGroup ref="LoginAsAdmin" stepKey="loginAsAdmin"/> + <actionGroup ref="AdminLoginActionGroup" stepKey="loginAsAdmin"/> </before> <after> <actionGroup ref="DeleteProductUsingProductGridActionGroup" stepKey="deleteProduct"> diff --git a/app/code/Magento/Downloadable/Test/Mftf/Test/AdminAddDefaultVideoDownloadableProductTest.xml b/app/code/Magento/Downloadable/Test/Mftf/Test/AdminAddDefaultVideoDownloadableProductTest.xml index c78c237935a90..ddc11fa6420ec 100644 --- a/app/code/Magento/Downloadable/Test/Mftf/Test/AdminAddDefaultVideoDownloadableProductTest.xml +++ b/app/code/Magento/Downloadable/Test/Mftf/Test/AdminAddDefaultVideoDownloadableProductTest.xml @@ -17,6 +17,9 @@ <severity value="MAJOR"/> <testCaseId value="MC-114"/> <group value="Downloadable"/> + <skip> + <issueId value="MC-32197"/> + </skip> </annotations> <before> <magentoCLI stepKey="addDownloadableDomain" command="downloadable:domains:add static.magento.com" before="enableAdminAccountSharing"/> diff --git a/app/code/Magento/Downloadable/Test/Mftf/Test/AdminCreateAndEditDownloadableProductSettingsTest.xml b/app/code/Magento/Downloadable/Test/Mftf/Test/AdminCreateAndEditDownloadableProductSettingsTest.xml index ebd36dddc0b6c..650cfd5ba8198 100644 --- a/app/code/Magento/Downloadable/Test/Mftf/Test/AdminCreateAndEditDownloadableProductSettingsTest.xml +++ b/app/code/Magento/Downloadable/Test/Mftf/Test/AdminCreateAndEditDownloadableProductSettingsTest.xml @@ -20,7 +20,7 @@ </annotations> <before> <!-- Login as admin --> - <actionGroup ref="LoginAsAdmin" stepKey="loginAsAdmin"/> + <actionGroup ref="AdminLoginActionGroup" stepKey="loginAsAdmin"/> </before> <after> <!-- Delete created downloadable product --> @@ -29,7 +29,7 @@ </actionGroup> <!-- Log out --> - <actionGroup ref="logout" stepKey="logout"/> + <actionGroup ref="AdminLogoutActionGroup" stepKey="logout"/> </after> <!-- Create new downloadable product --> @@ -97,7 +97,7 @@ <!-- Verify Url Key after changing --> <actionGroup ref="StorefrontOpenProductPageActionGroup" stepKey="openProductPage"> - <argument name="productUrl" value="{{ApiDownloadableProduct.name}}"/> + <argument name="productUrl" value="{{ApiDownloadableProduct.urlKey}}"/> </actionGroup> <!-- Assert product design settings "left bar is present at product page with 2 columns" --> diff --git a/app/code/Magento/Downloadable/Test/Mftf/Test/AdminCreateDownloadableProductAndAssignItToCustomStoreTest.xml b/app/code/Magento/Downloadable/Test/Mftf/Test/AdminCreateDownloadableProductAndAssignItToCustomStoreTest.xml index e0a38e7db7552..2f43c6f8278cc 100644 --- a/app/code/Magento/Downloadable/Test/Mftf/Test/AdminCreateDownloadableProductAndAssignItToCustomStoreTest.xml +++ b/app/code/Magento/Downloadable/Test/Mftf/Test/AdminCreateDownloadableProductAndAssignItToCustomStoreTest.xml @@ -25,7 +25,7 @@ <createData entity="SimpleSubCategory" stepKey="createCategory"/> <!-- Login as admin --> - <actionGroup ref="LoginAsAdmin" stepKey="LoginAsAdmin"/> + <actionGroup ref="AdminLoginActionGroup" stepKey="LoginAsAdmin"/> </before> <after> <magentoCLI stepKey="removeDownloadableDomain" command="downloadable:domains:remove static.magento.com"/> @@ -41,7 +41,7 @@ <actionGroup ref="AdminDeleteStoreViewActionGroup" stepKey="deleteCreatedStoreView"/> <!-- Log out --> - <actionGroup ref="logout" stepKey="logout"/> + <actionGroup ref="AdminLogoutActionGroup" stepKey="logout"/> </after> <!-- Create store view --> @@ -77,8 +77,7 @@ <actionGroup ref="SaveProductFormActionGroup" stepKey="saveProduct"/> <!-- Switch default store view on store view created below --> - <amOnPage url="{{StorefrontHomePage.url}}" stepKey="amOnStorefrontPage"/> - <waitForPageLoad stepKey="waitForStorefrontPageLoad"/> + <actionGroup ref="StorefrontOpenHomePageActionGroup" stepKey="amOnStorefrontPage"/> <actionGroup ref="StorefrontSwitchStoreViewActionGroup" stepKey="SwitchStoreView"> <argument name="storeView" value="customStore"/> </actionGroup> diff --git a/app/code/Magento/Downloadable/Test/Mftf/Test/AdminCreateDownloadableProductWithCustomOptionsTest.xml b/app/code/Magento/Downloadable/Test/Mftf/Test/AdminCreateDownloadableProductWithCustomOptionsTest.xml index fb5983bb58b6d..f1ea344d4e45c 100644 --- a/app/code/Magento/Downloadable/Test/Mftf/Test/AdminCreateDownloadableProductWithCustomOptionsTest.xml +++ b/app/code/Magento/Downloadable/Test/Mftf/Test/AdminCreateDownloadableProductWithCustomOptionsTest.xml @@ -25,7 +25,7 @@ <createData entity="SimpleSubCategory" stepKey="createCategory"/> <!-- Login as admin --> - <actionGroup ref="LoginAsAdmin" stepKey="LoginAsAdmin"/> + <actionGroup ref="AdminLoginActionGroup" stepKey="LoginAsAdmin"/> </before> <after> <magentoCLI stepKey="removeDownloadableDomain" command="downloadable:domains:remove static.magento.com"/> @@ -38,7 +38,7 @@ </actionGroup> <!-- Log out --> - <actionGroup ref="logout" stepKey="logout"/> + <actionGroup ref="AdminLogoutActionGroup" stepKey="logout"/> </after> <!-- Create Downloadable product --> @@ -88,6 +88,8 @@ <!-- Save product --> <actionGroup ref="SaveProductFormActionGroup" stepKey="saveProduct"/> + <magentoCLI stepKey="runCronIndex" command="cron:run --group=index"/> + <!-- Go to storefront category page --> <amOnPage url="$$createCategory.name$$.html" stepKey="amOnCategoryPage"/> <waitForPageLoad stepKey="waitForCategoryPageLoad"/> diff --git a/app/code/Magento/Downloadable/Test/Mftf/Test/AdminCreateDownloadableProductWithDefaultSetLinksTest.xml b/app/code/Magento/Downloadable/Test/Mftf/Test/AdminCreateDownloadableProductWithDefaultSetLinksTest.xml index 18dedca393178..850a73cd354a5 100644 --- a/app/code/Magento/Downloadable/Test/Mftf/Test/AdminCreateDownloadableProductWithDefaultSetLinksTest.xml +++ b/app/code/Magento/Downloadable/Test/Mftf/Test/AdminCreateDownloadableProductWithDefaultSetLinksTest.xml @@ -27,7 +27,7 @@ <magentoCLI command="indexer:reindex" stepKey="reindex"/> <magentoCLI command="cache:flush" arguments="full_page" stepKey="flushCache"/> <!-- Login as admin --> - <actionGroup ref="LoginAsAdmin" stepKey="LoginAsAdmin"/> + <actionGroup ref="AdminLoginActionGroup" stepKey="LoginAsAdmin"/> </before> <after> <magentoCLI stepKey="removeDownloadableDomain" command="downloadable:domains:remove static.magento.com"/> @@ -40,7 +40,7 @@ </actionGroup> <!-- Log out --> - <actionGroup ref="logout" stepKey="logout"/> + <actionGroup ref="AdminLogoutActionGroup" stepKey="logout"/> </after> <!-- Create downloadable product --> diff --git a/app/code/Magento/Downloadable/Test/Mftf/Test/AdminCreateDownloadableProductWithGroupPriceTest.xml b/app/code/Magento/Downloadable/Test/Mftf/Test/AdminCreateDownloadableProductWithGroupPriceTest.xml index 64920432f2e01..ba2e5e89005cf 100644 --- a/app/code/Magento/Downloadable/Test/Mftf/Test/AdminCreateDownloadableProductWithGroupPriceTest.xml +++ b/app/code/Magento/Downloadable/Test/Mftf/Test/AdminCreateDownloadableProductWithGroupPriceTest.xml @@ -25,7 +25,7 @@ <createData entity="SimpleSubCategory" stepKey="createCategory"/> <!-- Login as admin --> - <actionGroup ref="LoginAsAdmin" stepKey="LoginAsAdmin"/> + <actionGroup ref="AdminLoginActionGroup" stepKey="LoginAsAdmin"/> </before> <after> <magentoCLI stepKey="removeDownloadableDomain" command="downloadable:domains:remove static.magento.com"/> @@ -38,7 +38,7 @@ </actionGroup> <!-- Log out --> - <actionGroup ref="logout" stepKey="logout"/> + <actionGroup ref="AdminLogoutActionGroup" stepKey="logout"/> </after> <!-- Create downloadable product --> diff --git a/app/code/Magento/Downloadable/Test/Mftf/Test/AdminCreateDownloadableProductWithLinkTest.xml b/app/code/Magento/Downloadable/Test/Mftf/Test/AdminCreateDownloadableProductWithLinkTest.xml index 3b315d030cc8c..9ae046210181b 100644 --- a/app/code/Magento/Downloadable/Test/Mftf/Test/AdminCreateDownloadableProductWithLinkTest.xml +++ b/app/code/Magento/Downloadable/Test/Mftf/Test/AdminCreateDownloadableProductWithLinkTest.xml @@ -25,7 +25,7 @@ <createData entity="SimpleSubCategory" stepKey="createCategory"/> <!-- Login as admin --> - <actionGroup ref="LoginAsAdmin" stepKey="LoginAsAdmin"/> + <actionGroup ref="AdminLoginActionGroup" stepKey="LoginAsAdmin"/> </before> <after> <magentoCLI stepKey="removeDownloadableDomain" command="downloadable:domains:remove static.magento.com"/> @@ -38,7 +38,7 @@ </actionGroup> <!-- Log out --> - <actionGroup ref="logout" stepKey="logout"/> + <actionGroup ref="AdminLogoutActionGroup" stepKey="logout"/> </after> <!-- Create downloadable product --> @@ -69,6 +69,7 @@ <!-- Save product --> <actionGroup ref="SaveProductFormActionGroup" stepKey="saveProduct"/> + <magentoCron stepKey="runIndexCronJobs" groups="index"/> <!-- Assert product in storefront category page --> <amOnPage url="$$createCategory.name$$.html" stepKey="amOnCategoryPage"/> diff --git a/app/code/Magento/Downloadable/Test/Mftf/Test/AdminCreateDownloadableProductWithManageStockTest.xml b/app/code/Magento/Downloadable/Test/Mftf/Test/AdminCreateDownloadableProductWithManageStockTest.xml index 5ab683034bad8..0ff7c9bab26ca 100644 --- a/app/code/Magento/Downloadable/Test/Mftf/Test/AdminCreateDownloadableProductWithManageStockTest.xml +++ b/app/code/Magento/Downloadable/Test/Mftf/Test/AdminCreateDownloadableProductWithManageStockTest.xml @@ -25,7 +25,7 @@ <createData entity="SimpleSubCategory" stepKey="createCategory"/> <!-- Login as admin --> - <actionGroup ref="LoginAsAdmin" stepKey="LoginAsAdmin"/> + <actionGroup ref="AdminLoginActionGroup" stepKey="LoginAsAdmin"/> </before> <after> <magentoCLI stepKey="removeDownloadableDomain" command="downloadable:domains:remove static.magento.com"/> @@ -38,7 +38,7 @@ </actionGroup> <!-- Log out --> - <actionGroup ref="logout" stepKey="logout"/> + <actionGroup ref="AdminLogoutActionGroup" stepKey="logout"/> </after> <!-- Create downloadable product --> diff --git a/app/code/Magento/Downloadable/Test/Mftf/Test/AdminCreateDownloadableProductWithOutOfStockStatusTest.xml b/app/code/Magento/Downloadable/Test/Mftf/Test/AdminCreateDownloadableProductWithOutOfStockStatusTest.xml index 3348603563ff1..5615c66762c52 100644 --- a/app/code/Magento/Downloadable/Test/Mftf/Test/AdminCreateDownloadableProductWithOutOfStockStatusTest.xml +++ b/app/code/Magento/Downloadable/Test/Mftf/Test/AdminCreateDownloadableProductWithOutOfStockStatusTest.xml @@ -25,7 +25,7 @@ <createData entity="SimpleSubCategory" stepKey="createCategory"/> <!-- Login as admin --> - <actionGroup ref="LoginAsAdmin" stepKey="LoginAsAdmin"/> + <actionGroup ref="AdminLoginActionGroup" stepKey="LoginAsAdmin"/> </before> <after> <magentoCLI stepKey="removeDownloadableDomain" command="downloadable:domains:remove static.magento.com"/> @@ -38,7 +38,7 @@ </actionGroup> <!-- Log out --> - <actionGroup ref="logout" stepKey="logout"/> + <actionGroup ref="AdminLogoutActionGroup" stepKey="logout"/> </after> <!-- Create Downloadable product --> diff --git a/app/code/Magento/Downloadable/Test/Mftf/Test/AdminCreateDownloadableProductWithSpecialPriceTest.xml b/app/code/Magento/Downloadable/Test/Mftf/Test/AdminCreateDownloadableProductWithSpecialPriceTest.xml index 0ac14680cff46..f1d00d83b6666 100644 --- a/app/code/Magento/Downloadable/Test/Mftf/Test/AdminCreateDownloadableProductWithSpecialPriceTest.xml +++ b/app/code/Magento/Downloadable/Test/Mftf/Test/AdminCreateDownloadableProductWithSpecialPriceTest.xml @@ -25,7 +25,7 @@ <createData entity="SimpleSubCategory" stepKey="createCategory"/> <!-- Login as admin --> - <actionGroup ref="LoginAsAdmin" stepKey="LoginAsAdmin"/> + <actionGroup ref="AdminLoginActionGroup" stepKey="LoginAsAdmin"/> </before> <after> <magentoCLI stepKey="removeDownloadableDomain" command="downloadable:domains:remove static.magento.com"/> @@ -38,7 +38,7 @@ </actionGroup> <!-- Log out --> - <actionGroup ref="logout" stepKey="logout"/> + <actionGroup ref="AdminLogoutActionGroup" stepKey="logout"/> </after> <!-- Create downloadable product --> diff --git a/app/code/Magento/Downloadable/Test/Mftf/Test/AdminCreateDownloadableProductWithTierPriceTest.xml b/app/code/Magento/Downloadable/Test/Mftf/Test/AdminCreateDownloadableProductWithTierPriceTest.xml new file mode 100644 index 0000000000000..131193e7743e2 --- /dev/null +++ b/app/code/Magento/Downloadable/Test/Mftf/Test/AdminCreateDownloadableProductWithTierPriceTest.xml @@ -0,0 +1,35 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + /** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +--> + +<tests xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/testSchema.xsd"> + <test name="AdminCreateDownloadableProductWithTierPriceTest" extends="AdminCreateDownloadableProductWithGroupPriceTest"> + <annotations> + <features value="Catalog"/> + <stories value="Create Downloadable Product"/> + <title value="Create downloadable product with tier price test"/> + <description value="Admin should be able to create downloadable with tier price"/> + <severity value="CRITICAL"/> + <testCaseId value="MC-14515"/> + <group value="Downloadable"/> + <group value="mtf_migrated"/> + </annotations> + <remove keyForRemoval="addCustomerGroupPrice"/> + + <!-- Add tier price to product --> + <actionGroup ref="AdminAddAdvancedPricingToTheProductActionGroup" stepKey="addCustomerTierPrice" after="createProduct"> + <argument name="index" value="0"/> + <argument name="groupPrice" value="tierProductPrice"/> + </actionGroup> + + <remove keyForRemoval="assertGroupPrice"/> + + <!-- Assert tier price in storefront product page --> + <see selector="{{StorefrontProductInfoMainSection.tierPriceText}}" userInput="Buy {{tierProductPrice.quantity}} for ${{tierProductPrice.price}} each and save 27%" stepKey="assertTierPriceText"/> + </test> +</tests> diff --git a/app/code/Magento/Downloadable/Test/Mftf/Test/AdminCreateDownloadableProductWithTierPriceText.xml b/app/code/Magento/Downloadable/Test/Mftf/Test/AdminCreateDownloadableProductWithTierPriceText.xml deleted file mode 100644 index ca4e560506ad0..0000000000000 --- a/app/code/Magento/Downloadable/Test/Mftf/Test/AdminCreateDownloadableProductWithTierPriceText.xml +++ /dev/null @@ -1,35 +0,0 @@ -<?xml version="1.0" encoding="UTF-8"?> -<!-- - /** - * Copyright © Magento, Inc. All rights reserved. - * See COPYING.txt for license details. - */ ---> - -<tests xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" - xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/testSchema.xsd"> - <test name="AdminCreateDownloadableProductWithTierPriceText" extends="AdminCreateDownloadableProductWithGroupPriceTest"> - <annotations> - <features value="Catalog"/> - <stories value="Create Downloadable Product"/> - <title value="Create downloadable product with tier price test"/> - <description value="Admin should be able to create downloadable with tier price"/> - <severity value="CRITICAL"/> - <testCaseId value="MC-14515"/> - <group value="Downloadable"/> - <group value="mtf_migrated"/> - </annotations> - <remove keyForRemoval="addCustomerGroupPrice"/> - - <!-- Add tier price to product --> - <actionGroup ref="AdminAddAdvancedPricingToTheProductActionGroup" stepKey="addCustomerTierPrice" after="createProduct"> - <argument name="index" value="0"/> - <argument name="groupPrice" value="tierProductPrice"/> - </actionGroup> - - <remove keyForRemoval="assertGroupPrice"/> - - <!-- Assert tier price in storefront product page --> - <see selector="{{StorefrontProductInfoMainSection.tierPriceText}}" userInput="Buy {{tierProductPrice.quantity}} for ${{tierProductPrice.price}} each and save 27%" stepKey="assertTierPriceText"/> - </test> -</tests> diff --git a/app/code/Magento/Downloadable/Test/Mftf/Test/AdminCreateDownloadableProductWithoutFillingQuantityAndStockTest.xml b/app/code/Magento/Downloadable/Test/Mftf/Test/AdminCreateDownloadableProductWithoutFillingQuantityAndStockTest.xml index c80c42bc53ed9..fb0532d9d1fbe 100644 --- a/app/code/Magento/Downloadable/Test/Mftf/Test/AdminCreateDownloadableProductWithoutFillingQuantityAndStockTest.xml +++ b/app/code/Magento/Downloadable/Test/Mftf/Test/AdminCreateDownloadableProductWithoutFillingQuantityAndStockTest.xml @@ -25,7 +25,7 @@ <createData entity="SimpleSubCategory" stepKey="createCategory"/> <!-- Login as admin --> - <actionGroup ref="LoginAsAdmin" stepKey="LoginAsAdmin"/> + <actionGroup ref="AdminLoginActionGroup" stepKey="LoginAsAdmin"/> </before> <after> <magentoCLI stepKey="removeDownloadableDomain" command="downloadable:domains:remove static.magento.com"/> @@ -38,7 +38,7 @@ </actionGroup> <!-- Log out --> - <actionGroup ref="logout" stepKey="logout"/> + <actionGroup ref="AdminLogoutActionGroup" stepKey="logout"/> </after> <!-- Create downloadable product --> diff --git a/app/code/Magento/Downloadable/Test/Mftf/Test/AdminCreateDownloadableProductWithoutTaxClassIdTest.xml b/app/code/Magento/Downloadable/Test/Mftf/Test/AdminCreateDownloadableProductWithoutTaxClassIdTest.xml index 27a27f22c87ff..50a2215d441ad 100644 --- a/app/code/Magento/Downloadable/Test/Mftf/Test/AdminCreateDownloadableProductWithoutTaxClassIdTest.xml +++ b/app/code/Magento/Downloadable/Test/Mftf/Test/AdminCreateDownloadableProductWithoutTaxClassIdTest.xml @@ -25,7 +25,7 @@ <createData entity="SimpleSubCategory" stepKey="createCategory"/> <!-- Login as admin --> - <actionGroup ref="LoginAsAdmin" stepKey="LoginAsAdmin"/> + <actionGroup ref="AdminLoginActionGroup" stepKey="LoginAsAdmin"/> </before> <after> <magentoCLI stepKey="removeDownloadableDomain" command="downloadable:domains:remove static.magento.com"/> @@ -38,7 +38,7 @@ </actionGroup> <!-- Log out --> - <actionGroup ref="logout" stepKey="logout"/> + <actionGroup ref="AdminLogoutActionGroup" stepKey="logout"/> </after> <!-- Create downloadable product --> diff --git a/app/code/Magento/Downloadable/Test/Mftf/Test/AdminDeleteDownloadableProductTest.xml b/app/code/Magento/Downloadable/Test/Mftf/Test/AdminDeleteDownloadableProductTest.xml index 0d93bac16569f..7062b15aeedbf 100644 --- a/app/code/Magento/Downloadable/Test/Mftf/Test/AdminDeleteDownloadableProductTest.xml +++ b/app/code/Magento/Downloadable/Test/Mftf/Test/AdminDeleteDownloadableProductTest.xml @@ -19,7 +19,7 @@ </annotations> <before> <magentoCLI stepKey="addDownloadableDomain" command="downloadable:domains:add example.com static.magento.com"/> - <actionGroup ref="LoginAsAdmin" stepKey="loginAsAdmin"/> + <actionGroup ref="AdminLoginActionGroup" stepKey="loginAsAdmin"/> <createData entity="_defaultCategory" stepKey="createCategory"/> <createData entity="DownloadableProductWithTwoLink" stepKey="createDownloadableProduct"> <requiredEntity createDataKey="createCategory"/> @@ -34,7 +34,7 @@ <after> <magentoCLI stepKey="removeDownloadableDomain" command="downloadable:domains:remove example.com static.magento.com"/> <deleteData createDataKey="createCategory" stepKey="deleteCategory"/> - <actionGroup ref="logout" stepKey="logout"/> + <actionGroup ref="AdminLogoutActionGroup" stepKey="logout"/> </after> <actionGroup ref="DeleteProductUsingProductGridActionGroup" stepKey="deleteDownloadableProductFilteredBySkuAndName"> <argument name="product" value="$$createDownloadableProduct$$"/> diff --git a/app/code/Magento/Downloadable/Test/Mftf/Test/AdminDownloadableSetEditRelatedProductsTest.xml b/app/code/Magento/Downloadable/Test/Mftf/Test/AdminDownloadableSetEditRelatedProductsTest.xml index a09076b7dc06e..27d505e070f5b 100644 --- a/app/code/Magento/Downloadable/Test/Mftf/Test/AdminDownloadableSetEditRelatedProductsTest.xml +++ b/app/code/Magento/Downloadable/Test/Mftf/Test/AdminDownloadableSetEditRelatedProductsTest.xml @@ -34,6 +34,8 @@ <argument name="product" value="DownloadableProduct"/> </actionGroup> + <magentoCLI command="cron:run --group=index" stepKey="runCronIndexer"/> + <!--See related product in storefront--> <amOnPage url="{{DownloadableProduct.sku}}.html" stepKey="goToStorefront"/> </test> diff --git a/app/code/Magento/Downloadable/Test/Mftf/Test/AdminProductTypeSwitchingOnEditingTest.xml b/app/code/Magento/Downloadable/Test/Mftf/Test/AdminProductTypeSwitchingOnEditingTest.xml deleted file mode 100644 index 4a13a3f60e385..0000000000000 --- a/app/code/Magento/Downloadable/Test/Mftf/Test/AdminProductTypeSwitchingOnEditingTest.xml +++ /dev/null @@ -1,83 +0,0 @@ -<?xml version="1.0" encoding="UTF-8"?> -<!-- - /** - * Copyright © Magento, Inc. All rights reserved. - * See COPYING.txt for license details. - */ ---> - -<tests xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" - xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/testSchema.xsd"> - <test name="AdminDownloadableProductTypeSwitchingToConfigurableProductTest" extends="AdminSimpleProductTypeSwitchingToConfigurableProductTest"> - <annotations> - <features value="Downloadable"/> - <stories value="Product type switching"/> - <title value="Downloadable product type switching on editing to configurable product"/> - <description value="Downloadable product type switching on editing to configurable product"/> - <testCaseId value="MC-17957"/> - <useCaseId value="MAGETWO-44170"/> - <severity value="MAJOR"/> - <group value="catalog"/> - </annotations> - <!-- Open Dropdown and select downloadable product option --> - <click selector="{{AdminProductDownloadableSection.sectionHeader}}" stepKey="openDownloadableSection" after="waitForSimpleProductPageLoad"/> - <uncheckOption selector="{{AdminProductDownloadableSection.isDownloadableProduct}}" stepKey="checkOptionIsDownloadable" after="openDownloadableSection"/> - <selectOption selector="{{AdminProductFormSection.productWeightSelect}}" userInput="This item has weight" stepKey="selectWeightForProduct" after="checkOptionIsDownloadable"/> - <actionGroup ref="SaveProductFormActionGroup" stepKey="saveDownloadableProductForm" after="selectWeightForProduct"/> - </test> - <test name="AdminSimpleProductTypeSwitchingToDownloadableProductTest"> - <annotations> - <features value="Downloadable"/> - <stories value="Product type switching"/> - <title value="Simple product type switching on editing to downloadable product"/> - <description value="Simple product type switching on editing to downloadable product"/> - <testCaseId value="MC-17956"/> - <useCaseId value="MAGETWO-44170"/> - <severity value="MAJOR"/> - <group value="catalog"/> - </annotations> - <before> - <!-- Add downloadable domains --> - <magentoCLI stepKey="addDownloadableDomain" command="downloadable:domains:add example.com static.magento.com"/> - <actionGroup ref="LoginAsAdmin" stepKey="loginAsAdmin"/> - <!--Create product--> - <comment userInput="Create product" stepKey="commentCreateProduct"/> - <createData entity="SimpleProduct2" stepKey="createProduct"/> - </before> - <after> - <!-- Remove downloadable domains --> - <magentoCLI stepKey="removeDownloadableDomain" command="downloadable:domains:remove example.com static.magento.com"/> - <!--Delete product--> - <comment userInput="Delete product" stepKey="commentDeleteProduct"/> - <deleteData createDataKey="createProduct" stepKey="deleteProduct"/> - <actionGroup ref="logout" stepKey="logout"/> - </after> - <!--Change product type to Downloadable--> - <comment userInput="Change product type to Downloadable" stepKey="commentCreateDownloadable"/> - <amOnPage url="{{AdminProductEditPage.url($$createProduct.id$$)}}" stepKey="gotToDownloadableProductPage"/> - <waitForPageLoad stepKey="waitForDownloadableProductPageLoad"/> - <selectOption selector="{{AdminProductFormSection.productWeightSelect}}" userInput="This item has no weight" stepKey="selectNoWeightForProduct"/> - <actionGroup ref="AdminAddDownloadableLinkInformationActionGroup" stepKey="addDownloadableLinkInformation"/> - <checkOption selector="{{AdminProductDownloadableSection.isLinksPurchasedSeparately}}" stepKey="checkOptionPurchaseSeparately"/> - <actionGroup ref="AddDownloadableProductLinkWithMaxDownloadsActionGroup" stepKey="addDownloadableProductLink"> - <argument name="link" value="downloadableLinkWithMaxDownloads"/> - </actionGroup> - <actionGroup ref="SaveProductFormActionGroup" stepKey="saveDownloadableProductForm"/> - <!--Assert downloadable product on Admin product page grid--> - <comment userInput="Assert configurable product in Admin product page grid" stepKey="commentAssertDownloadableProductOnAdmin"/> - <amOnPage url="{{AdminCatalogProductPage.url}}" stepKey="goToCatalogProductPage"/> - <actionGroup ref="FilterProductGridBySku2ActionGroup" stepKey="filterProductGridBySku"> - <argument name="sku" value="$$createProduct.sku$$"/> - </actionGroup> - <see selector="{{AdminProductGridSection.productGridCell('1', 'Name')}}" userInput="$$createProduct.name$$" stepKey="seeDownloadableProductNameInGrid"/> - <see selector="{{AdminProductGridSection.productGridCell('1', 'Type')}}" userInput="Downloadable Product" stepKey="seeDownloadableProductTypeInGrid"/> - <actionGroup ref="AdminClearFiltersActionGroup" stepKey="clearDownloadableProductFilters"/> - <!--Assert downloadable product on storefront--> - <comment userInput="Assert downloadable product on storefront" stepKey="commentAssertDownloadableProductOnStorefront"/> - <amOnPage url="{{StorefrontProductPage.url($$createProduct.name$$)}}" stepKey="openDownloadableProductPage"/> - <waitForPageLoad stepKey="waitForStorefrontDownloadableProductPageLoad"/> - <see userInput="IN STOCK" selector="{{StorefrontProductInfoMainSection.productStockStatus}}" stepKey="assertDownloadableProductInStock"/> - <scrollTo selector="{{StorefrontDownloadableProductSection.downloadableLinkBlock}}" stepKey="scrollToLinksInStorefront"/> - <seeElement selector="{{StorefrontDownloadableProductSection.downloadableLinkLabel(downloadableLinkWithMaxDownloads.title)}}" stepKey="seeDownloadableLink" /> - </test> -</tests> diff --git a/app/code/Magento/Downloadable/Test/Mftf/Test/AdminProductTypeSwitchingOnEditingTest/AdminDownloadableProductTypeSwitchingToConfigurableProductTest.xml b/app/code/Magento/Downloadable/Test/Mftf/Test/AdminProductTypeSwitchingOnEditingTest/AdminDownloadableProductTypeSwitchingToConfigurableProductTest.xml new file mode 100644 index 0000000000000..bfa53f9beb4f8 --- /dev/null +++ b/app/code/Magento/Downloadable/Test/Mftf/Test/AdminProductTypeSwitchingOnEditingTest/AdminDownloadableProductTypeSwitchingToConfigurableProductTest.xml @@ -0,0 +1,27 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + /** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +--> +<tests xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/testSchema.xsd"> + <test name="AdminDownloadableProductTypeSwitchingToConfigurableProductTest" extends="AdminSimpleProductTypeSwitchingToConfigurableProductTest"> + <annotations> + <features value="Downloadable"/> + <stories value="Product type switching"/> + <title value="Downloadable product type switching on editing to configurable product"/> + <description value="Downloadable product type switching on editing to configurable product"/> + <testCaseId value="MC-17957"/> + <useCaseId value="MAGETWO-44170"/> + <severity value="MAJOR"/> + <group value="catalog"/> + </annotations> + <!-- Open Dropdown and select downloadable product option --> + <click selector="{{AdminProductDownloadableSection.sectionHeader}}" stepKey="openDownloadableSection" after="waitForSimpleProductPageLoad"/> + <uncheckOption selector="{{AdminProductDownloadableSection.isDownloadableProduct}}" stepKey="checkOptionIsDownloadable" after="openDownloadableSection"/> + <selectOption selector="{{AdminProductFormSection.productWeightSelect}}" userInput="This item has weight" stepKey="selectWeightForProduct" after="checkOptionIsDownloadable"/> + <actionGroup ref="SaveProductFormActionGroup" stepKey="saveDownloadableProductForm" after="selectWeightForProduct"/> + </test> +</tests> diff --git a/app/code/Magento/Downloadable/Test/Mftf/Test/AdminProductTypeSwitchingOnEditingTest/AdminSimpleProductTypeSwitchingToDownloadableProductTest.xml b/app/code/Magento/Downloadable/Test/Mftf/Test/AdminProductTypeSwitchingOnEditingTest/AdminSimpleProductTypeSwitchingToDownloadableProductTest.xml new file mode 100644 index 0000000000000..aa94de681de1d --- /dev/null +++ b/app/code/Magento/Downloadable/Test/Mftf/Test/AdminProductTypeSwitchingOnEditingTest/AdminSimpleProductTypeSwitchingToDownloadableProductTest.xml @@ -0,0 +1,67 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + /** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +--> +<tests xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/testSchema.xsd"> + <test name="AdminSimpleProductTypeSwitchingToDownloadableProductTest"> + <annotations> + <features value="Downloadable"/> + <stories value="Product type switching"/> + <title value="Simple product type switching on editing to downloadable product"/> + <description value="Simple product type switching on editing to downloadable product"/> + <testCaseId value="MC-17956"/> + <useCaseId value="MAGETWO-44170"/> + <severity value="MAJOR"/> + <group value="catalog"/> + </annotations> + <before> + <!-- Add downloadable domains --> + <magentoCLI stepKey="addDownloadableDomain" command="downloadable:domains:add example.com static.magento.com"/> + <actionGroup ref="AdminLoginActionGroup" stepKey="loginAsAdmin"/> + <!--Create product--> + <comment userInput="Create product" stepKey="commentCreateProduct"/> + <createData entity="SimpleProduct2" stepKey="createProduct"/> + </before> + <after> + <!-- Remove downloadable domains --> + <magentoCLI stepKey="removeDownloadableDomain" command="downloadable:domains:remove example.com static.magento.com"/> + <!--Delete product--> + <comment userInput="Delete product" stepKey="commentDeleteProduct"/> + <deleteData createDataKey="createProduct" stepKey="deleteProduct"/> + <actionGroup ref="AdminLogoutActionGroup" stepKey="logout"/> + </after> + <!--Change product type to Downloadable--> + <comment userInput="Change product type to Downloadable" stepKey="commentCreateDownloadable"/> + <actionGroup ref="AdminProductPageOpenByIdActionGroup" stepKey="gotToDownloadableProductPage"> + <argument name="productId" value="$$createProduct.id$$"/> + </actionGroup> + <waitForPageLoad stepKey="waitForDownloadableProductPageLoad"/> + <selectOption selector="{{AdminProductFormSection.productWeightSelect}}" userInput="This item has no weight" stepKey="selectNoWeightForProduct"/> + <actionGroup ref="AdminAddDownloadableLinkInformationActionGroup" stepKey="addDownloadableLinkInformation"/> + <checkOption selector="{{AdminProductDownloadableSection.isLinksPurchasedSeparately}}" stepKey="checkOptionPurchaseSeparately"/> + <actionGroup ref="AddDownloadableProductLinkWithMaxDownloadsActionGroup" stepKey="addDownloadableProductLink"> + <argument name="link" value="downloadableLinkWithMaxDownloads"/> + </actionGroup> + <actionGroup ref="SaveProductFormActionGroup" stepKey="saveDownloadableProductForm"/> + <!--Assert downloadable product on Admin product page grid--> + <comment userInput="Assert configurable product in Admin product page grid" stepKey="commentAssertDownloadableProductOnAdmin"/> + <amOnPage url="{{AdminCatalogProductPage.url}}" stepKey="goToCatalogProductPage"/> + <actionGroup ref="FilterProductGridBySku2ActionGroup" stepKey="filterProductGridBySku"> + <argument name="sku" value="$$createProduct.sku$$"/> + </actionGroup> + <see selector="{{AdminProductGridSection.productGridCell('1', 'Name')}}" userInput="$$createProduct.name$$" stepKey="seeDownloadableProductNameInGrid"/> + <see selector="{{AdminProductGridSection.productGridCell('1', 'Type')}}" userInput="Downloadable Product" stepKey="seeDownloadableProductTypeInGrid"/> + <actionGroup ref="AdminClearFiltersActionGroup" stepKey="clearDownloadableProductFilters"/> + <!--Assert downloadable product on storefront--> + <comment userInput="Assert downloadable product on storefront" stepKey="commentAssertDownloadableProductOnStorefront"/> + <amOnPage url="{{StorefrontProductPage.url($$createProduct.name$$)}}" stepKey="openDownloadableProductPage"/> + <waitForPageLoad stepKey="waitForStorefrontDownloadableProductPageLoad"/> + <see userInput="IN STOCK" selector="{{StorefrontProductInfoMainSection.productStockStatus}}" stepKey="assertDownloadableProductInStock"/> + <scrollTo selector="{{StorefrontDownloadableProductSection.downloadableLinkBlock}}" stepKey="scrollToLinksInStorefront"/> + <seeElement selector="{{StorefrontDownloadableProductSection.downloadableLinkLabel(downloadableLinkWithMaxDownloads.title)}}" stepKey="seeDownloadableLink"/> + </test> +</tests> diff --git a/app/code/Magento/Downloadable/Test/Mftf/Test/AdminRemoveDefaultImageDownloadableProductTest.xml b/app/code/Magento/Downloadable/Test/Mftf/Test/AdminRemoveDefaultImageDownloadableProductTest.xml index e4a3be9494268..27d3d3d10a0b7 100644 --- a/app/code/Magento/Downloadable/Test/Mftf/Test/AdminRemoveDefaultImageDownloadableProductTest.xml +++ b/app/code/Magento/Downloadable/Test/Mftf/Test/AdminRemoveDefaultImageDownloadableProductTest.xml @@ -20,7 +20,7 @@ </annotations> <before> <magentoCLI stepKey="addDownloadableDomain" command="downloadable:domains:add static.magento.com"/> - <actionGroup ref="LoginAsAdmin" stepKey="loginAsAdmin"/> + <actionGroup ref="AdminLoginActionGroup" stepKey="loginAsAdmin"/> </before> <after> <magentoCLI stepKey="removeDownloadableDomain" command="downloadable:domains:remove static.magento.com"/> diff --git a/app/code/Magento/Downloadable/Test/Mftf/Test/AdminRemoveDefaultVideoDownloadableProductTest.xml b/app/code/Magento/Downloadable/Test/Mftf/Test/AdminRemoveDefaultVideoDownloadableProductTest.xml index 5792fd3cc7eb7..bd2e7615ac252 100644 --- a/app/code/Magento/Downloadable/Test/Mftf/Test/AdminRemoveDefaultVideoDownloadableProductTest.xml +++ b/app/code/Magento/Downloadable/Test/Mftf/Test/AdminRemoveDefaultVideoDownloadableProductTest.xml @@ -17,6 +17,9 @@ <severity value="MAJOR"/> <testCaseId value="MC-207"/> <group value="Downloadable"/> + <skip> + <issueId value="MC-32197"/> + </skip> </annotations> <before> <magentoCLI stepKey="addDownloadableDomain" command="downloadable:domains:add static.magento.com" before="enableAdminAccountSharing"/> diff --git a/app/code/Magento/Downloadable/Test/Mftf/Test/AdvanceCatalogSearchDownloadableProductTest.xml b/app/code/Magento/Downloadable/Test/Mftf/Test/AdvanceCatalogSearchDownloadableProductTest.xml deleted file mode 100644 index b5f437996d69b..0000000000000 --- a/app/code/Magento/Downloadable/Test/Mftf/Test/AdvanceCatalogSearchDownloadableProductTest.xml +++ /dev/null @@ -1,131 +0,0 @@ -<?xml version="1.0" encoding="UTF-8"?> -<!-- - /** - * Copyright © Magento, Inc. All rights reserved. - * See COPYING.txt for license details. - */ ---> - -<tests xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" - xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/testSchema.xsd"> - <test name="AdvanceCatalogSearchDownloadableByNameTest" extends="AdvanceCatalogSearchSimpleProductByNameTest"> - <annotations> - <features value="Downloadable"/> - <stories value="Advanced Catalog Product Search for all product types"/> - <title value="Guest customer should be able to advance search Downloadable product with product name"/> - <description value="Guest customer should be able to advance search Downloadable product with product name"/> - <severity value="MAJOR"/> - <testCaseId value="MC-142"/> - <group value="Downloadable"/> - </annotations> - <before> - <magentoCLI stepKey="addDownloadableDomain" command="downloadable:domains:add static.magento.com" before="product"/> - <createData entity="ApiDownloadableProduct" stepKey="product"/> - <createData entity="ApiDownloadableLink" stepKey="addDownloadableLink1"> - <requiredEntity createDataKey="product"/> - </createData> - <createData entity="ApiDownloadableLink" stepKey="addDownloadableLink2"> - <requiredEntity createDataKey="product"/> - </createData> - </before> - <after> - <magentoCLI stepKey="removeDownloadableDomain" command="downloadable:domains:remove static.magento.com" before="delete"/> - </after> - </test> - <test name="AdvanceCatalogSearchDownloadableBySkuTest" extends="AdvanceCatalogSearchSimpleProductBySkuTest"> - <annotations> - <features value="Downloadable"/> - <stories value="Advanced Catalog Product Search for all product types"/> - <title value="Guest customer should be able to advance search Downloadable product with product sku"/> - <description value="Guest customer should be able to advance search Downloadable product with product sku"/> - <severity value="MAJOR"/> - <testCaseId value="MC-252"/> - <group value="Downloadable"/> - </annotations> - <before> - <magentoCLI stepKey="addDownloadableDomain" command="downloadable:domains:add static.magento.com" before="product"/> - <createData entity="ApiDownloadableProductUnderscoredSku" stepKey="product"/> - <createData entity="ApiDownloadableLink" stepKey="addDownloadableLink1"> - <requiredEntity createDataKey="product"/> - </createData> - <createData entity="ApiDownloadableLink" stepKey="addDownloadableLink2"> - <requiredEntity createDataKey="product"/> - </createData> - </before> - <after> - <magentoCLI stepKey="removeDownloadableDomain" command="downloadable:domains:remove static.magento.com" before="delete"/> - </after> - </test> - <test name="AdvanceCatalogSearchDownloadableByDescriptionTest" extends="AdvanceCatalogSearchSimpleProductByDescriptionTest"> - <annotations> - <features value="Downloadable"/> - <stories value="Advanced Catalog Product Search for all product types"/> - <title value="Guest customer should be able to advance search Downloadable product with product description"/> - <description value="Guest customer should be able to advance search Downloadable product with product description"/> - <severity value="MAJOR"/> - <testCaseId value="MC-243"/> - <group value="Downloadable"/> - </annotations> - <before> - <magentoCLI stepKey="addDownloadableDomain" command="downloadable:domains:add static.magento.com" before="product"/> - <createData entity="ApiDownloadableProduct" stepKey="product"/> - <createData entity="ApiDownloadableLink" stepKey="addDownloadableLink1"> - <requiredEntity createDataKey="product"/> - </createData> - <createData entity="ApiDownloadableLink" stepKey="addDownloadableLink2"> - <requiredEntity createDataKey="product"/> - </createData> - </before> - <after> - <magentoCLI stepKey="removeDownloadableDomain" command="downloadable:domains:remove static.magento.com" before="delete"/> - </after> - </test> - <test name="AdvanceCatalogSearchDownloadableByShortDescriptionTest" extends="AdvanceCatalogSearchSimpleProductByShortDescriptionTest"> - <annotations> - <features value="Downloadable"/> - <stories value="Advanced Catalog Product Search for all product types"/> - <title value="Guest customer should be able to advance search Downloadable product with product short description"/> - <description value="Guest customer should be able to advance search Downloadable product with product short description"/> - <severity value="MAJOR"/> - <testCaseId value="MC-245"/> - <group value="Downloadable"/> - </annotations> - <before> - <magentoCLI stepKey="addDownloadableDomain" command="downloadable:domains:add static.magento.com" before="product"/> - <createData entity="ApiDownloadableProduct" stepKey="product"/> - <createData entity="ApiDownloadableLink" stepKey="addDownloadableLink1"> - <requiredEntity createDataKey="product"/> - </createData> - <createData entity="ApiDownloadableLink" stepKey="addDownloadableLink2"> - <requiredEntity createDataKey="product"/> - </createData> - </before> - <after> - <magentoCLI stepKey="removeDownloadableDomain" command="downloadable:domains:remove static.magento.com" before="delete"/> - </after> - </test> - <test name="AdvanceCatalogSearchDownloadableByPriceTest" extends="AdvanceCatalogSearchSimpleProductByPriceTest"> - <annotations> - <features value="Downloadable"/> - <stories value="Advanced Catalog Product Search for all product types"/> - <title value="Guest customer should be able to advance search Downloadable product with product price"/> - <description value="Guest customer should be able to advance search Downloadable product with product price"/> - <severity value="MAJOR"/> - <testCaseId value="MC-246"/> - <group value="Downloadable"/> - </annotations> - <before> - <magentoCLI stepKey="addDownloadableDomain" command="downloadable:domains:add static.magento.com" before="product"/> - <createData entity="ApiDownloadableProduct" stepKey="product"/> - <createData entity="ApiDownloadableLink" stepKey="addDownloadableLink1"> - <requiredEntity createDataKey="product"/> - </createData> - <createData entity="ApiDownloadableLink" stepKey="addDownloadableLink2"> - <requiredEntity createDataKey="product"/> - </createData> - </before> - <after> - <magentoCLI stepKey="removeDownloadableDomain" command="downloadable:domains:remove static.magento.com" before="delete"/> - </after> - </test> -</tests> diff --git a/app/code/Magento/Downloadable/Test/Mftf/Test/AdvanceCatalogSearchDownloadableProductTest/AdvanceCatalogSearchDownloadableByDescriptionTest.xml b/app/code/Magento/Downloadable/Test/Mftf/Test/AdvanceCatalogSearchDownloadableProductTest/AdvanceCatalogSearchDownloadableByDescriptionTest.xml new file mode 100644 index 0000000000000..24af53fdf613b --- /dev/null +++ b/app/code/Magento/Downloadable/Test/Mftf/Test/AdvanceCatalogSearchDownloadableProductTest/AdvanceCatalogSearchDownloadableByDescriptionTest.xml @@ -0,0 +1,34 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + /** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +--> +<tests xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/testSchema.xsd"> + <test name="AdvanceCatalogSearchDownloadableByDescriptionTest" extends="AdvanceCatalogSearchSimpleProductByDescriptionTest"> + <annotations> + <features value="Downloadable"/> + <stories value="Advanced Catalog Product Search for all product types"/> + <title value="Guest customer should be able to advance search Downloadable product with product description"/> + <description value="Guest customer should be able to advance search Downloadable product with product description"/> + <severity value="MAJOR"/> + <testCaseId value="MC-243"/> + <group value="Downloadable"/> + </annotations> + <before> + <magentoCLI stepKey="addDownloadableDomain" command="downloadable:domains:add static.magento.com" before="product"/> + <createData entity="ApiDownloadableProduct" stepKey="product"/> + <createData entity="ApiDownloadableLink" stepKey="addDownloadableLink1"> + <requiredEntity createDataKey="product"/> + </createData> + <createData entity="ApiDownloadableLink" stepKey="addDownloadableLink2"> + <requiredEntity createDataKey="product"/> + </createData> + </before> + <after> + <magentoCLI stepKey="removeDownloadableDomain" command="downloadable:domains:remove static.magento.com" before="delete"/> + </after> + </test> +</tests> diff --git a/app/code/Magento/Downloadable/Test/Mftf/Test/AdvanceCatalogSearchDownloadableProductTest/AdvanceCatalogSearchDownloadableByNameTest.xml b/app/code/Magento/Downloadable/Test/Mftf/Test/AdvanceCatalogSearchDownloadableProductTest/AdvanceCatalogSearchDownloadableByNameTest.xml new file mode 100644 index 0000000000000..603270c70913c --- /dev/null +++ b/app/code/Magento/Downloadable/Test/Mftf/Test/AdvanceCatalogSearchDownloadableProductTest/AdvanceCatalogSearchDownloadableByNameTest.xml @@ -0,0 +1,34 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + /** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +--> +<tests xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/testSchema.xsd"> + <test name="AdvanceCatalogSearchDownloadableByNameTest" extends="AdvanceCatalogSearchSimpleProductByNameTest"> + <annotations> + <features value="Downloadable"/> + <stories value="Advanced Catalog Product Search for all product types"/> + <title value="Guest customer should be able to advance search Downloadable product with product name"/> + <description value="Guest customer should be able to advance search Downloadable product with product name"/> + <severity value="MAJOR"/> + <testCaseId value="MC-142"/> + <group value="Downloadable"/> + </annotations> + <before> + <magentoCLI stepKey="addDownloadableDomain" command="downloadable:domains:add static.magento.com" before="product"/> + <createData entity="ApiDownloadableProduct" stepKey="product"/> + <createData entity="ApiDownloadableLink" stepKey="addDownloadableLink1"> + <requiredEntity createDataKey="product"/> + </createData> + <createData entity="ApiDownloadableLink" stepKey="addDownloadableLink2"> + <requiredEntity createDataKey="product"/> + </createData> + </before> + <after> + <magentoCLI stepKey="removeDownloadableDomain" command="downloadable:domains:remove static.magento.com" before="delete"/> + </after> + </test> +</tests> diff --git a/app/code/Magento/Downloadable/Test/Mftf/Test/AdvanceCatalogSearchDownloadableProductTest/AdvanceCatalogSearchDownloadableByPriceTest.xml b/app/code/Magento/Downloadable/Test/Mftf/Test/AdvanceCatalogSearchDownloadableProductTest/AdvanceCatalogSearchDownloadableByPriceTest.xml new file mode 100644 index 0000000000000..c2011ae11a0ac --- /dev/null +++ b/app/code/Magento/Downloadable/Test/Mftf/Test/AdvanceCatalogSearchDownloadableProductTest/AdvanceCatalogSearchDownloadableByPriceTest.xml @@ -0,0 +1,34 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + /** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +--> +<tests xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/testSchema.xsd"> + <test name="AdvanceCatalogSearchDownloadableByPriceTest" extends="AdvanceCatalogSearchSimpleProductByPriceTest"> + <annotations> + <features value="Downloadable"/> + <stories value="Advanced Catalog Product Search for all product types"/> + <title value="Guest customer should be able to advance search Downloadable product with product price"/> + <description value="Guest customer should be able to advance search Downloadable product with product price"/> + <severity value="MAJOR"/> + <testCaseId value="MC-246"/> + <group value="Downloadable"/> + </annotations> + <before> + <magentoCLI stepKey="addDownloadableDomain" command="downloadable:domains:add static.magento.com" before="product"/> + <createData entity="ApiDownloadableProduct" stepKey="product"/> + <createData entity="ApiDownloadableLink" stepKey="addDownloadableLink1"> + <requiredEntity createDataKey="product"/> + </createData> + <createData entity="ApiDownloadableLink" stepKey="addDownloadableLink2"> + <requiredEntity createDataKey="product"/> + </createData> + </before> + <after> + <magentoCLI stepKey="removeDownloadableDomain" command="downloadable:domains:remove static.magento.com" before="delete"/> + </after> + </test> +</tests> diff --git a/app/code/Magento/Downloadable/Test/Mftf/Test/AdvanceCatalogSearchDownloadableProductTest/AdvanceCatalogSearchDownloadableByShortDescriptionTest.xml b/app/code/Magento/Downloadable/Test/Mftf/Test/AdvanceCatalogSearchDownloadableProductTest/AdvanceCatalogSearchDownloadableByShortDescriptionTest.xml new file mode 100644 index 0000000000000..308d631ff230a --- /dev/null +++ b/app/code/Magento/Downloadable/Test/Mftf/Test/AdvanceCatalogSearchDownloadableProductTest/AdvanceCatalogSearchDownloadableByShortDescriptionTest.xml @@ -0,0 +1,34 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + /** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +--> +<tests xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/testSchema.xsd"> + <test name="AdvanceCatalogSearchDownloadableByShortDescriptionTest" extends="AdvanceCatalogSearchSimpleProductByShortDescriptionTest"> + <annotations> + <features value="Downloadable"/> + <stories value="Advanced Catalog Product Search for all product types"/> + <title value="Guest customer should be able to advance search Downloadable product with product short description"/> + <description value="Guest customer should be able to advance search Downloadable product with product short description"/> + <severity value="MAJOR"/> + <testCaseId value="MC-245"/> + <group value="Downloadable"/> + </annotations> + <before> + <magentoCLI stepKey="addDownloadableDomain" command="downloadable:domains:add static.magento.com" before="product"/> + <createData entity="ApiDownloadableProduct" stepKey="product"/> + <createData entity="ApiDownloadableLink" stepKey="addDownloadableLink1"> + <requiredEntity createDataKey="product"/> + </createData> + <createData entity="ApiDownloadableLink" stepKey="addDownloadableLink2"> + <requiredEntity createDataKey="product"/> + </createData> + </before> + <after> + <magentoCLI stepKey="removeDownloadableDomain" command="downloadable:domains:remove static.magento.com" before="delete"/> + </after> + </test> +</tests> diff --git a/app/code/Magento/Downloadable/Test/Mftf/Test/AdvanceCatalogSearchDownloadableProductTest/AdvanceCatalogSearchDownloadableBySkuTest.xml b/app/code/Magento/Downloadable/Test/Mftf/Test/AdvanceCatalogSearchDownloadableProductTest/AdvanceCatalogSearchDownloadableBySkuTest.xml new file mode 100644 index 0000000000000..3ec4177a55556 --- /dev/null +++ b/app/code/Magento/Downloadable/Test/Mftf/Test/AdvanceCatalogSearchDownloadableProductTest/AdvanceCatalogSearchDownloadableBySkuTest.xml @@ -0,0 +1,34 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + /** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +--> +<tests xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/testSchema.xsd"> + <test name="AdvanceCatalogSearchDownloadableBySkuTest" extends="AdvanceCatalogSearchSimpleProductBySkuTest"> + <annotations> + <features value="Downloadable"/> + <stories value="Advanced Catalog Product Search for all product types"/> + <title value="Guest customer should be able to advance search Downloadable product with product sku"/> + <description value="Guest customer should be able to advance search Downloadable product with product sku"/> + <severity value="MAJOR"/> + <testCaseId value="MC-252"/> + <group value="Downloadable"/> + </annotations> + <before> + <magentoCLI stepKey="addDownloadableDomain" command="downloadable:domains:add static.magento.com" before="product"/> + <createData entity="ApiDownloadableProductUnderscoredSku" stepKey="product"/> + <createData entity="ApiDownloadableLink" stepKey="addDownloadableLink1"> + <requiredEntity createDataKey="product"/> + </createData> + <createData entity="ApiDownloadableLink" stepKey="addDownloadableLink2"> + <requiredEntity createDataKey="product"/> + </createData> + </before> + <after> + <magentoCLI stepKey="removeDownloadableDomain" command="downloadable:domains:remove static.magento.com" before="delete"/> + </after> + </test> +</tests> diff --git a/app/code/Magento/Downloadable/Test/Mftf/Test/EditDownloadableProductWithSeparateLinksFromCartTest.xml b/app/code/Magento/Downloadable/Test/Mftf/Test/EditDownloadableProductWithSeparateLinksFromCartTest.xml index 6535bf11d43c4..30e31be6c8ec4 100644 --- a/app/code/Magento/Downloadable/Test/Mftf/Test/EditDownloadableProductWithSeparateLinksFromCartTest.xml +++ b/app/code/Magento/Downloadable/Test/Mftf/Test/EditDownloadableProductWithSeparateLinksFromCartTest.xml @@ -25,7 +25,7 @@ <createData entity="SimpleSubCategory" stepKey="createCategory"/> <!-- Login as admin --> - <actionGroup ref="LoginAsAdmin" stepKey="LoginAsAdmin"/> + <actionGroup ref="AdminLoginActionGroup" stepKey="LoginAsAdmin"/> <!-- Create downloadable product --> <amOnPage url="{{AdminProductIndexPage.url}}" stepKey="amOnProductGridPage"/> @@ -76,7 +76,7 @@ </actionGroup> <!-- Log out --> - <actionGroup ref="logout" stepKey="logout"/> + <actionGroup ref="AdminLogoutActionGroup" stepKey="logout"/> </after> <!-- Step 1: Navigate to store front Product page as guest --> diff --git a/app/code/Magento/Downloadable/Test/Mftf/Test/ManualSelectAllDownloadableLinksDownloadableProductTest.xml b/app/code/Magento/Downloadable/Test/Mftf/Test/ManualSelectAllDownloadableLinksDownloadableProductTest.xml index 3ab64be9ad2ca..4a7f1dde227da 100644 --- a/app/code/Magento/Downloadable/Test/Mftf/Test/ManualSelectAllDownloadableLinksDownloadableProductTest.xml +++ b/app/code/Magento/Downloadable/Test/Mftf/Test/ManualSelectAllDownloadableLinksDownloadableProductTest.xml @@ -25,7 +25,7 @@ <createData entity="SimpleSubCategory" stepKey="createCategory"/> <!-- Login as admin --> - <actionGroup ref="LoginAsAdmin" stepKey="LoginAsAdmin"/> + <actionGroup ref="AdminLoginActionGroup" stepKey="LoginAsAdmin"/> <!-- Create downloadable product --> <amOnPage url="{{AdminProductIndexPage.url}}" stepKey="amOnProductGridPage"/> @@ -76,7 +76,7 @@ </actionGroup> <!-- Log out --> - <actionGroup ref="logout" stepKey="logout"/> + <actionGroup ref="AdminLogoutActionGroup" stepKey="logout"/> </after> <!-- Step 1: Navigate to store front Product page as guest --> diff --git a/app/code/Magento/Downloadable/Test/Mftf/Test/SelectAllDownloadableLinksDownloadableProductTest.xml b/app/code/Magento/Downloadable/Test/Mftf/Test/SelectAllDownloadableLinksDownloadableProductTest.xml index 3f77eff56193d..0ed826e944a4f 100644 --- a/app/code/Magento/Downloadable/Test/Mftf/Test/SelectAllDownloadableLinksDownloadableProductTest.xml +++ b/app/code/Magento/Downloadable/Test/Mftf/Test/SelectAllDownloadableLinksDownloadableProductTest.xml @@ -25,7 +25,7 @@ <createData entity="SimpleSubCategory" stepKey="createCategory"/> <!-- Login as admin --> - <actionGroup ref="LoginAsAdmin" stepKey="LoginAsAdmin"/> + <actionGroup ref="AdminLoginActionGroup" stepKey="LoginAsAdmin"/> <!-- Create downloadable product --> <amOnPage url="{{AdminProductIndexPage.url}}" stepKey="amOnProductGridPage"/> @@ -76,7 +76,7 @@ </actionGroup> <!-- Log out --> - <actionGroup ref="logout" stepKey="logout"/> + <actionGroup ref="AdminLogoutActionGroup" stepKey="logout"/> </after> <!-- Step 1: Navigate to store front Product page as guest --> diff --git a/app/code/Magento/Downloadable/Test/Mftf/Test/StorefrontVerifySecureURLRedirectDownloadableTest.xml b/app/code/Magento/Downloadable/Test/Mftf/Test/StorefrontVerifySecureURLRedirectDownloadableTest.xml index 6e039ca413a08..d7e0ce3b2ca22 100644 --- a/app/code/Magento/Downloadable/Test/Mftf/Test/StorefrontVerifySecureURLRedirectDownloadableTest.xml +++ b/app/code/Magento/Downloadable/Test/Mftf/Test/StorefrontVerifySecureURLRedirectDownloadableTest.xml @@ -8,7 +8,7 @@ <tests xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/testSchema.xsd"> - <test name="StorefrontVerifySecureURLRedirectDownloadable"> + <test name="StorefrontVerifySecureURLRedirectDownloadableTest"> <annotations> <features value="Downloadable"/> <stories value="Storefront Secure URLs"/> diff --git a/app/code/Magento/Downloadable/Test/Mftf/Test/VerifyDisableDownloadableProductSamplesAreNotAccessibleTest.xml b/app/code/Magento/Downloadable/Test/Mftf/Test/VerifyDisableDownloadableProductSamplesAreNotAccessibleTest.xml index b0f7edaeaa3a9..1529750cbb293 100644 --- a/app/code/Magento/Downloadable/Test/Mftf/Test/VerifyDisableDownloadableProductSamplesAreNotAccessibleTest.xml +++ b/app/code/Magento/Downloadable/Test/Mftf/Test/VerifyDisableDownloadableProductSamplesAreNotAccessibleTest.xml @@ -53,7 +53,7 @@ <deleteData createDataKey="createCategory" stepKey="deleteCategory"/> <!-- Admin logout --> - <actionGroup ref="logout" stepKey="adminLogout"/> + <actionGroup ref="AdminLogoutActionGroup" stepKey="adminLogout"/> </after> <!-- Open Downloadable product from precondition on Storefront --> @@ -84,10 +84,10 @@ <closeTab stepKey="closeLinkSampleTab"/> <!-- Login as admin --> - <actionGroup ref="LoginAsAdmin" stepKey="loginAsAdmin"/> + <actionGroup ref="AdminLoginActionGroup" stepKey="loginAsAdmin"/> <!-- Open Downloadable product from precondition --> - <actionGroup ref="GoToProductPageViaIDActionGroup" stepKey="openProductEditPage"> + <actionGroup ref="AdminProductPageOpenByIdActionGroup" stepKey="openProductEditPage"> <argument name="productId" value="$createProduct.id$"/> </actionGroup> diff --git a/app/code/Magento/Downloadable/Test/Unit/Helper/DownloadTest.php b/app/code/Magento/Downloadable/Test/Unit/Helper/DownloadTest.php index 9551cfe982bd5..f90c3f27b27c7 100644 --- a/app/code/Magento/Downloadable/Test/Unit/Helper/DownloadTest.php +++ b/app/code/Magento/Downloadable/Test/Unit/Helper/DownloadTest.php @@ -17,6 +17,9 @@ */ class DownloadTest extends \PHPUnit\Framework\TestCase { + /** @var array Result of get_headers() function */ + public static $headers; + /** @var DownloadHelper */ protected $_helper; @@ -230,6 +233,7 @@ protected function _setupUrlMocks($size = self::FILE_SIZE, $url = self::URL, $ad $this->returnValue($this->_handleMock) ); + self::$headers = ['200 OK']; $this->_helper->setResource($url, DownloadHelper::LINK_TYPE_URL); } diff --git a/app/code/Magento/Downloadable/Test/Unit/Model/Link/BuilderTest.php b/app/code/Magento/Downloadable/Test/Unit/Model/Link/BuilderTest.php index 26c5ccd90c463..46f4ca0408c73 100644 --- a/app/code/Magento/Downloadable/Test/Unit/Model/Link/BuilderTest.php +++ b/app/code/Magento/Downloadable/Test/Unit/Model/Link/BuilderTest.php @@ -3,6 +3,7 @@ * Copyright © Magento, Inc. All rights reserved. * See COPYING.txt for license details. */ + namespace Magento\Downloadable\Test\Unit\Model\Link; use Magento\Downloadable\Api\Data\LinkInterface; @@ -11,7 +12,7 @@ use Magento\Downloadable\Helper\Download; /** - * Class BuilderTest + * Unit test for downloadable products' builder link class */ class BuilderTest extends \PHPUnit\Framework\TestCase { @@ -68,7 +69,7 @@ protected function setUp() $this->linkMock = $this->getMockBuilder(LinkInterface::class) ->disableOriginalConstructor() ->getMockForAbstractClass(); - + $this->service = $objectManagerHelper->getObject( Builder::class, [ @@ -160,6 +161,10 @@ public function testBuild($data, $expectedPrice) if (isset($data['is_unlimited'])) { $this->linkMock->expects($this->once())->method('setNumberOfDownloads')->with(0); } + $useDefaultTitle = $data['use_default_title'] ?? false; + if ($useDefaultTitle) { + $this->linkMock->expects($this->once())->method('setTitle')->with(null); + } if (isset($data['price'])) { $this->linkMock->expects($this->once())->method('getPrice')->willReturn($data['price']); } else { @@ -219,6 +224,7 @@ public function buildProvider() [ 'file' => 'cXVlIHRhbA==', 'type' => 'file', + 'use_default_title' => '1', 'sample' => [ 'file' => 'cXVlIHRhbA==', 'type' => 'file' diff --git a/app/code/Magento/Downloadable/Test/Unit/Model/Sample/BuilderTest.php b/app/code/Magento/Downloadable/Test/Unit/Model/Sample/BuilderTest.php index 28ffa6db05e0b..938082fa0aef3 100644 --- a/app/code/Magento/Downloadable/Test/Unit/Model/Sample/BuilderTest.php +++ b/app/code/Magento/Downloadable/Test/Unit/Model/Sample/BuilderTest.php @@ -3,6 +3,7 @@ * Copyright © Magento, Inc. All rights reserved. * See COPYING.txt for license details. */ + namespace Magento\Downloadable\Test\Unit\Model\Sample; use Magento\Downloadable\Api\Data\SampleInterface; @@ -11,7 +12,7 @@ use Magento\Downloadable\Model\Sample\Builder; /** - * Class BuilderTest + * Unit test for downloadable products' builder sample class */ class BuilderTest extends \PHPUnit\Framework\TestCase { @@ -84,6 +85,7 @@ public function testBuild() { $data = [ 'file' => 'cXVlIHRhbA==', + 'use_default_title' => '1', 'type' => 'file' ]; $downloadableData = ['sort_order' => 1]; @@ -122,8 +124,12 @@ public function testBuild() )->willReturn($fileName); $this->sampleMock->expects($this->once())->method('setSampleFile')->with($fileName); $this->sampleMock->expects($this->once())->method('setSortOrder')->with(1); + $useDefaultTitle = $data['use_default_title'] ?? false; + if ($useDefaultTitle) { + $this->sampleMock->expects($this->once())->method('setTitle')->with(null); + } $this->service->setData($data); - + $this->service->build($this->sampleMock); } } diff --git a/app/code/Magento/Downloadable/Test/Unit/Model/Sample/DeleteHandlerTest.php b/app/code/Magento/Downloadable/Test/Unit/Model/Sample/DeleteHandlerTest.php new file mode 100644 index 0000000000000..b633c843138c5 --- /dev/null +++ b/app/code/Magento/Downloadable/Test/Unit/Model/Sample/DeleteHandlerTest.php @@ -0,0 +1,100 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ + +declare(strict_types=1); + +namespace Magento\Downloadable\Test\Unit\Model\Sample; + +use Magento\Catalog\Api\Data\ProductInterface; +use Magento\Catalog\Model\Product; +use Magento\Downloadable\Api\Data\SampleInterface; +use Magento\Downloadable\Api\SampleRepositoryInterface as SampleRepository; +use Magento\Downloadable\Model\Product\Type; +use Magento\Downloadable\Model\Sample\DeleteHandler; +use Magento\Framework\TestFramework\Unit\Helper\ObjectManager as ObjectManagerHelper; +use PHPUnit\Framework\MockObject\MockObject; +use PHPUnit\Framework\TestCase; + +/** + * Unit Test for \Magento\Downloadable\Model\Sample\DeleteHandler + */ +class DeleteHandlerTest extends TestCase +{ + const STUB_PRODUCT_TYPE = 'simple'; + const STUB_PRODUCT_SKU = 'sku'; + const STUB_SAMPLE_ID = 1; + + /** + * @var ProductInterface|MockObject + */ + private $entityMock; + + /** + * @var SampleRepository|MockObject + */ + private $sampleRepositoryMock; + + /** + * @var DeleteHandler + */ + private $deleteHandler; + + /** + * @inheritDoc + */ + protected function setUp() + { + $this->entityMock = $this->createMock(Product::class); + $this->sampleRepositoryMock = $this->getMockBuilder(SampleRepository::class) + ->disableOriginalConstructor() + ->setMethods(['getList', 'delete']) + ->getMockForAbstractClass(); + + $this->deleteHandler = (new ObjectManagerHelper($this))->getObject( + DeleteHandler::class, + ['sampleRepository' => $this->sampleRepositoryMock] + ); + } + + /** + * Test case when provided Product has type Downloadable. + */ + public function testExecuteWithDownloadableProduct() + { + $this->entityMock->expects($this->once()) + ->method('getTypeId') + ->willReturn(Type::TYPE_DOWNLOADABLE); + $this->entityMock->expects($this->once()) + ->method('getSku') + ->willReturn(self::STUB_PRODUCT_SKU); + + $sampleMock = $this->createMock(SampleInterface::class); + $sampleMock->expects($this->once()) + ->method('getId') + ->willReturn(self::STUB_SAMPLE_ID); + + $this->sampleRepositoryMock->expects($this->once())->method('delete'); + $this->sampleRepositoryMock->expects($this->once()) + ->method('getList') + ->willReturn([$sampleMock]); + + $this->assertSame($this->entityMock, $this->deleteHandler->execute($this->entityMock)); + } + + /** + * Test case when provided Product is not Downloadable. + */ + public function testExecuteWithOtherProduct() + { + $this->entityMock->expects($this->once()) + ->method('getTypeId') + ->willReturn(self::STUB_PRODUCT_TYPE); + + $this->sampleRepositoryMock->expects($this->never())->method('getList'); + $this->sampleRepositoryMock->expects($this->never())->method('delete'); + $this->assertSame($this->entityMock, $this->deleteHandler->execute($this->entityMock)); + } +} diff --git a/app/code/Magento/Downloadable/Test/Unit/Ui/DataProvider/Product/Form/Modifier/LinksTest.php b/app/code/Magento/Downloadable/Test/Unit/Ui/DataProvider/Product/Form/Modifier/LinksTest.php index cff48420938b1..4a1039340342f 100644 --- a/app/code/Magento/Downloadable/Test/Unit/Ui/DataProvider/Product/Form/Modifier/LinksTest.php +++ b/app/code/Magento/Downloadable/Test/Unit/Ui/DataProvider/Product/Form/Modifier/LinksTest.php @@ -17,7 +17,7 @@ use Magento\Framework\Stdlib\ArrayManager; /** - * Class LinksTest + * Test for class Magento\Downloadable\Ui\DataProvider\Product\Form\Modifier\Links * @SuppressWarnings(PHPMD.CouplingBetweenObjects) */ class LinksTest extends \PHPUnit\Framework\TestCase @@ -169,7 +169,7 @@ public function testModifyMeta() ->method('toOptionArray'); $this->shareableMock->expects($this->once()) ->method('toOptionArray'); - $this->urlBuilderMock->expects($this->exactly(2)) + $this->urlBuilderMock->expects($this->never()) ->method('addSessionParam') ->willReturnSelf(); $this->urlBuilderMock->expects($this->exactly(2)) diff --git a/app/code/Magento/Downloadable/Test/Unit/Ui/DataProvider/Product/Form/Modifier/SamplesTest.php b/app/code/Magento/Downloadable/Test/Unit/Ui/DataProvider/Product/Form/Modifier/SamplesTest.php index 420950d08e101..41d56f0f380bb 100644 --- a/app/code/Magento/Downloadable/Test/Unit/Ui/DataProvider/Product/Form/Modifier/SamplesTest.php +++ b/app/code/Magento/Downloadable/Test/Unit/Ui/DataProvider/Product/Form/Modifier/SamplesTest.php @@ -16,7 +16,7 @@ use Magento\Framework\Stdlib\ArrayManager; /** - * Class SamplesTest + * Test for class Magento\Downloadable\Ui\DataProvider\Product\Form\Modifier\Samples * @SuppressWarnings(PHPMD.CouplingBetweenObjects) */ class SamplesTest extends \PHPUnit\Framework\TestCase @@ -141,7 +141,7 @@ public function testModifyMeta() ->method('isSingleStoreMode'); $this->typeUploadMock->expects($this->once()) ->method('toOptionArray'); - $this->urlBuilderMock->expects($this->once()) + $this->urlBuilderMock->expects($this->never()) ->method('addSessionParam') ->willReturnSelf(); $this->urlBuilderMock->expects($this->once()) diff --git a/app/code/Magento/Downloadable/Test/Unit/_files/download_mock.php b/app/code/Magento/Downloadable/Test/Unit/_files/download_mock.php index e634f0ffa341d..7ab3bc939f4d0 100644 --- a/app/code/Magento/Downloadable/Test/Unit/_files/download_mock.php +++ b/app/code/Magento/Downloadable/Test/Unit/_files/download_mock.php @@ -22,3 +22,13 @@ function mime_content_type() { return DownloadTest::$mimeContentType; } + +/** + * Override standard function + * + * @return array + */ +function get_headers() +{ + return DownloadTest::$headers; +} diff --git a/app/code/Magento/Downloadable/Ui/DataProvider/Product/Form/Modifier/Data/Links.php b/app/code/Magento/Downloadable/Ui/DataProvider/Product/Form/Modifier/Data/Links.php index 0a3ea2fc6ba19..3be1094f7a4b7 100644 --- a/app/code/Magento/Downloadable/Ui/DataProvider/Product/Form/Modifier/Data/Links.php +++ b/app/code/Magento/Downloadable/Ui/DataProvider/Product/Form/Modifier/Data/Links.php @@ -16,7 +16,7 @@ use Magento\Framework\Exception\ValidatorException; /** - * Class Links + * Grid class to add links * @SuppressWarnings(PHPMD.CouplingBetweenObjects) */ class Links @@ -162,7 +162,7 @@ protected function addSampleFile(array $linkData, LinkInterface $link) 'name' => $this->downloadableFile->getFileFromPathFile($sampleFile), 'size' => $this->downloadableFile->getFileSize($file), 'status' => 'old', - 'url' => $this->urlBuilder->addSessionParam()->getUrl( + 'url' => $this->urlBuilder->getUrl( 'adminhtml/downloadable_product_edit/link', ['id' => $link->getId(), 'type' => 'sample', '_secure' => true] ), @@ -191,7 +191,7 @@ protected function addLinkFile(array $linkData, LinkInterface $link) 'name' => $this->downloadableFile->getFileFromPathFile($linkFile), 'size' => $this->downloadableFile->getFileSize($file), 'status' => 'old', - 'url' => $this->urlBuilder->addSessionParam()->getUrl( + 'url' => $this->urlBuilder->getUrl( 'adminhtml/downloadable_product_edit/link', ['id' => $link->getId(), 'type' => 'link', '_secure' => true] ), diff --git a/app/code/Magento/Downloadable/Ui/DataProvider/Product/Form/Modifier/Data/Samples.php b/app/code/Magento/Downloadable/Ui/DataProvider/Product/Form/Modifier/Data/Samples.php index 988f429de1d87..bf2faede3a6dd 100644 --- a/app/code/Magento/Downloadable/Ui/DataProvider/Product/Form/Modifier/Data/Samples.php +++ b/app/code/Magento/Downloadable/Ui/DataProvider/Product/Form/Modifier/Data/Samples.php @@ -16,7 +16,7 @@ use Magento\Downloadable\Api\Data\SampleInterface; /** - * Class Samples + * Class to add samples * @SuppressWarnings(PHPMD.CouplingBetweenObjects) */ class Samples @@ -143,7 +143,7 @@ protected function addSampleFile(array $sampleData, SampleInterface $sample) 'name' => $this->downloadableFile->getFileFromPathFile($sampleFile), 'size' => $this->downloadableFile->getFileSize($file), 'status' => 'old', - 'url' => $this->urlBuilder->addSessionParam()->getUrl( + 'url' => $this->urlBuilder->getUrl( 'adminhtml/downloadable_product_edit/sample', ['id' => $sample->getId(), '_secure' => true] ), diff --git a/app/code/Magento/Downloadable/Ui/DataProvider/Product/Form/Modifier/Links.php b/app/code/Magento/Downloadable/Ui/DataProvider/Product/Form/Modifier/Links.php index c4824f913daf8..8c98d871a12d2 100644 --- a/app/code/Magento/Downloadable/Ui/DataProvider/Product/Form/Modifier/Links.php +++ b/app/code/Magento/Downloadable/Ui/DataProvider/Product/Form/Modifier/Links.php @@ -339,7 +339,7 @@ protected function getFileColumn() 'elementTmpl' => 'Magento_Downloadable/components/file-uploader', 'fileInputName' => 'links', 'uploaderConfig' => [ - 'url' => $this->urlBuilder->addSessionParam()->getUrl( + 'url' => $this->urlBuilder->getUrl( 'adminhtml/downloadable_file/upload', ['type' => 'links', '_secure' => true] ), @@ -406,7 +406,7 @@ protected function getSampleColumn() 'fileInputName' => 'link_samples', 'labelVisible' => false, 'uploaderConfig' => [ - 'url' => $this->urlBuilder->addSessionParam()->getUrl( + 'url' => $this->urlBuilder->getUrl( 'adminhtml/downloadable_file/upload', ['type' => 'link_samples', '_secure' => true] ), diff --git a/app/code/Magento/Downloadable/Ui/DataProvider/Product/Form/Modifier/Samples.php b/app/code/Magento/Downloadable/Ui/DataProvider/Product/Form/Modifier/Samples.php index 81c5918eb24ae..474fc56c10043 100644 --- a/app/code/Magento/Downloadable/Ui/DataProvider/Product/Form/Modifier/Samples.php +++ b/app/code/Magento/Downloadable/Ui/DataProvider/Product/Form/Modifier/Samples.php @@ -273,7 +273,7 @@ protected function getSampleColumn() 'elementTmpl' => 'Magento_Downloadable/components/file-uploader', 'fileInputName' => 'samples', 'uploaderConfig' => [ - 'url' => $this->urlBuilder->addSessionParam()->getUrl( + 'url' => $this->urlBuilder->getUrl( 'adminhtml/downloadable_file/upload', ['type' => 'samples', '_secure' => true] ), diff --git a/app/code/Magento/Downloadable/etc/adminhtml/system.xml b/app/code/Magento/Downloadable/etc/adminhtml/system.xml index 77f2cc0503631..31b2f64ec018c 100644 --- a/app/code/Magento/Downloadable/etc/adminhtml/system.xml +++ b/app/code/Magento/Downloadable/etc/adminhtml/system.xml @@ -10,15 +10,15 @@ <section id="catalog"> <group id="downloadable" translate="label" type="text" sortOrder="600" showInDefault="1" showInWebsite="1" showInStore="1"> <label>Downloadable Product Options</label> - <field id="order_item_status" translate="label" type="select" sortOrder="100" showInDefault="1" showInWebsite="1" showInStore="0" canRestore="1"> + <field id="order_item_status" translate="label" type="select" sortOrder="100" showInDefault="1" showInWebsite="1" canRestore="1"> <label>Order Item Status to Enable Downloads</label> <source_model>Magento\Downloadable\Model\System\Config\Source\Orderitemstatus</source_model> </field> - <field id="downloads_number" translate="label" type="text" sortOrder="200" showInDefault="1" showInWebsite="1" showInStore="0" canRestore="1"> + <field id="downloads_number" translate="label" type="text" sortOrder="200" showInDefault="1" showInWebsite="1" canRestore="1"> <label>Default Maximum Number of Downloads</label> <validate>validate-digits validate-zero-or-greater</validate> </field> - <field id="shareable" translate="label" type="select" sortOrder="300" showInDefault="1" showInWebsite="1" showInStore="0"> + <field id="shareable" translate="label" type="select" sortOrder="300" showInDefault="1" showInWebsite="1" canRestore="1"> <label>Shareable</label> <source_model>Magento\Config\Model\Config\Source\Yesno</source_model> </field> @@ -28,15 +28,15 @@ <field id="links_title" translate="label" type="text" sortOrder="500" showInDefault="1" showInWebsite="1" showInStore="1" canRestore="1"> <label>Default Link Title</label> </field> - <field id="links_target_new_window" translate="label" type="select" sortOrder="600" showInDefault="1" showInWebsite="1" showInStore="0" canRestore="1"> + <field id="links_target_new_window" translate="label" type="select" sortOrder="600" showInDefault="1" showInWebsite="1" canRestore="1"> <label>Open Links in New Window</label> <source_model>Magento\Config\Model\Config\Source\Yesno</source_model> </field> - <field id="content_disposition" translate="label" type="select" sortOrder="700" showInDefault="1" showInWebsite="1" showInStore="1"> + <field id="content_disposition" translate="label" type="select" sortOrder="700" showInDefault="1" showInWebsite="1" showInStore="1" canRestore="1"> <label>Use Content-Disposition</label> <source_model>Magento\Downloadable\Model\System\Config\Source\Contentdisposition</source_model> </field> - <field id="disable_guest_checkout" translate="label comment" type="select" sortOrder="800" showInDefault="1" showInWebsite="1" showInStore="0" canRestore="1"> + <field id="disable_guest_checkout" translate="label comment" type="select" sortOrder="800" showInDefault="1" showInWebsite="1" canRestore="1"> <label>Disable Guest Checkout if Cart Contains Downloadable Items</label> <source_model>Magento\Config\Model\Config\Source\Yesno</source_model> <comment>Guest checkout will only work with shareable.</comment> diff --git a/app/code/Magento/Downloadable/etc/config.xml b/app/code/Magento/Downloadable/etc/config.xml index 578ff1e008660..26f2457f6bf3c 100644 --- a/app/code/Magento/Downloadable/etc/config.xml +++ b/app/code/Magento/Downloadable/etc/config.xml @@ -9,8 +9,9 @@ <default> <catalog> <downloadable> - <downloads_number>0</downloads_number> <order_item_status>9</order_item_status> + <downloads_number>0</downloads_number> + <shareable>0</shareable> <samples_title>Samples</samples_title> <links_title>Links</links_title> <links_target_new_window>1</links_target_new_window> diff --git a/app/code/Magento/Downloadable/registration.php b/app/code/Magento/Downloadable/registration.php index c50856603fa50..a99b7129eebc8 100644 --- a/app/code/Magento/Downloadable/registration.php +++ b/app/code/Magento/Downloadable/registration.php @@ -4,6 +4,6 @@ * See COPYING.txt for license details. */ -use \Magento\Framework\Component\ComponentRegistrar; +use Magento\Framework\Component\ComponentRegistrar; ComponentRegistrar::register(ComponentRegistrar::MODULE, 'Magento_Downloadable', __DIR__); diff --git a/app/code/Magento/DownloadableGraphQl/Model/ResourceModel/GetPurchasedDownloadableProducts.php b/app/code/Magento/DownloadableGraphQl/Model/ResourceModel/GetPurchasedDownloadableProducts.php index 82f448ec450f6..4bbdfdb360415 100644 --- a/app/code/Magento/DownloadableGraphQl/Model/ResourceModel/GetPurchasedDownloadableProducts.php +++ b/app/code/Magento/DownloadableGraphQl/Model/ResourceModel/GetPurchasedDownloadableProducts.php @@ -39,13 +39,13 @@ public function execute(int $customerId): array { $connection = $this->resourceConnection->getConnection(); $allowedItemsStatuses = [Item::LINK_STATUS_PENDING_PAYMENT, Item::LINK_STATUS_PAYMENT_REVIEW]; - $downloadablePurchasedTable = $connection->getTableName('downloadable_link_purchased'); + $downloadablePurchasedTable = $this->resourceConnection->getTableName('downloadable_link_purchased'); /* The fields names are hardcoded since there's no existing name reference in the code */ $selectQuery = $connection->select() ->from($downloadablePurchasedTable) ->joinLeft( - ['item' => $connection->getTableName('downloadable_link_purchased_item')], + ['item' => $this->resourceConnection->getTableName('downloadable_link_purchased_item')], "$downloadablePurchasedTable.purchased_id = item.purchased_id" ) ->where("$downloadablePurchasedTable.customer_id = ?", $customerId) diff --git a/app/code/Magento/DownloadableImportExport/Helper/Data.php b/app/code/Magento/DownloadableImportExport/Helper/Data.php index fa4f7d656cdbe..91e290dbbcdf3 100644 --- a/app/code/Magento/DownloadableImportExport/Helper/Data.php +++ b/app/code/Magento/DownloadableImportExport/Helper/Data.php @@ -8,7 +8,7 @@ use Magento\DownloadableImportExport\Model\Import\Product\Type\Downloadable; /** - * Class Data + * Helper for import-export downloadable product */ class Data extends \Magento\Framework\App\Helper\AbstractHelper { @@ -47,6 +47,7 @@ public function isRowDownloadableNoValid(array $rowData) * @param array $option * @param array $existingOptions * @return array + * @SuppressWarnings(PHPMD.CyclomaticComplexity) */ public function fillExistOptions(array $base, array $option, array $existingOptions) { @@ -59,6 +60,9 @@ public function fillExistOptions(array $base, array $option, array $existingOpti && $option['sample_file'] == $existingOption['sample_file'] && $option['sample_type'] == $existingOption['sample_type'] && $option['product_id'] == $existingOption['product_id']) { + if (empty($existingOption['website_id'])) { + unset($existingOption['website_id']); + } $result = array_replace($base, $option, $existingOption); } } diff --git a/app/code/Magento/DownloadableImportExport/Helper/Uploader.php b/app/code/Magento/DownloadableImportExport/Helper/Uploader.php index 197250faaea91..e6ead5d5cc021 100644 --- a/app/code/Magento/DownloadableImportExport/Helper/Uploader.php +++ b/app/code/Magento/DownloadableImportExport/Helper/Uploader.php @@ -8,7 +8,7 @@ use Magento\Framework\App\Filesystem\DirectoryList; /** - * Class Uploader + * Uploader helper for downloadable products */ class Uploader extends \Magento\Framework\App\Helper\AbstractHelper { @@ -105,6 +105,17 @@ public function getUploader($type, $parameters) return $this->fileUploader; } + /** + * Check a file or directory exists + * + * @param string $fileName + * @return bool + */ + public function isFileExist(string $fileName): bool + { + return $this->mediaDirectory->isExist($this->fileUploader->getDestDir().$fileName); + } + /** * Get all allowed extensions * diff --git a/app/code/Magento/DownloadableImportExport/Model/Export/Product/Type/Downloadable.php b/app/code/Magento/DownloadableImportExport/Model/Export/Product/Type/Downloadable.php new file mode 100644 index 0000000000000..716e65e00d1aa --- /dev/null +++ b/app/code/Magento/DownloadableImportExport/Model/Export/Product/Type/Downloadable.php @@ -0,0 +1,15 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +namespace Magento\DownloadableImportExport\Model\Export\Product\Type; + +use Magento\CatalogImportExport\Model\Export\Product\Type\AbstractType; + +/** + * Class Downloadable for composite CatalogImportExport + */ +class Downloadable extends AbstractType +{ +} diff --git a/app/code/Magento/DownloadableImportExport/Model/Export/RowCustomizer.php b/app/code/Magento/DownloadableImportExport/Model/Export/RowCustomizer.php new file mode 100644 index 0000000000000..daa874e829e54 --- /dev/null +++ b/app/code/Magento/DownloadableImportExport/Model/Export/RowCustomizer.php @@ -0,0 +1,170 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +declare(strict_types=1); + +namespace Magento\DownloadableImportExport\Model\Export; + +use Magento\Downloadable\Model\LinkRepository; +use Magento\Downloadable\Model\SampleRepository; +use Magento\Catalog\Model\ResourceModel\Product\Collection as ProductCollection; +use Magento\CatalogImportExport\Model\Export\RowCustomizerInterface; +use Magento\CatalogImportExport\Model\Import\Product as ImportProduct; +use Magento\Downloadable\Model\Product\Type as Type; +use Magento\ImportExport\Model\Import; +use Magento\Store\Model\Store; +use Magento\Store\Model\StoreManagerInterface; +use Magento\DownloadableImportExport\Model\Import\Product\Type\Downloadable; + +/** + * Customizes output during export + */ +class RowCustomizer implements RowCustomizerInterface +{ + /** + * @var array + */ + private $downloadableData = []; + + /** + * @var string[] + */ + private $downloadableColumns = [ + Downloadable::COL_DOWNLOADABLE_LINKS, + Downloadable::COL_DOWNLOADABLE_SAMPLES, + ]; + + /** + * @var LinkRepository + */ + private $linkRepository; + + /** + * @var SampleRepository + */ + private $sampleRepository; + + /** + * @var StoreManagerInterface + */ + private $storeManager; + + /** + * @param StoreManagerInterface $storeManager + * @param LinkRepository $linkRepository + * @param SampleRepository $sampleRepository + */ + public function __construct( + StoreManagerInterface $storeManager, + LinkRepository $linkRepository, + SampleRepository $sampleRepository + ) { + $this->storeManager = $storeManager; + $this->linkRepository = $linkRepository; + $this->sampleRepository = $sampleRepository; + } + + /** + * Prepare configurable data for export + * + * @param ProductCollection $collection + * @param int[] $productIds + * @return void + */ + public function prepareData($collection, $productIds): void + { + $productCollection = clone $collection; + $productCollection->addAttributeToFilter('entity_id', ['in' => $productIds]) + ->addAttributeToFilter('type_id', ['eq' => Type::TYPE_DOWNLOADABLE]) + ->addAttributeToSelect('links_title') + ->addAttributeToSelect('samples_title'); + // set global scope during export + $this->storeManager->setCurrentStore(Store::DEFAULT_STORE_ID); + foreach ($collection as $product) { + $productLinks = $this->linkRepository->getLinksByProduct($product); + $productSamples = $this->sampleRepository->getSamplesByProduct($product); + $this->downloadableData[$product->getId()] = []; + $linksData = []; + $samplesData = []; + foreach ($productLinks as $linkId => $link) { + $linkData = $link->getData(); + $linkData['group_title'] = $product->getData('links_title'); + $linksData[$linkId] = $this->optionRowToCellString($linkData); + } + foreach ($productSamples as $sampleId => $sample) { + $sampleData = $sample->getData(); + $sampleData['group_title'] = $product->getData('samples_title'); + $samplesData[$sampleId] = $this->optionRowToCellString($sampleData); + } + $this->downloadableData[$product->getId()] = [ + Downloadable::COL_DOWNLOADABLE_LINKS => implode( + ImportProduct::PSEUDO_MULTI_LINE_SEPARATOR, + $linksData + ), + Downloadable::COL_DOWNLOADABLE_SAMPLES => implode( + Import::DEFAULT_GLOBAL_MULTI_VALUE_SEPARATOR, + $samplesData + )]; + } + } + + /** + * Convert option row to cell string + * + * @param array $option + * @return string + */ + private function optionRowToCellString(array $option): string + { + $result = []; + foreach ($option as $attributeCode => $value) { + if ($value) { + $result[] = $attributeCode . ImportProduct::PAIR_NAME_VALUE_SEPARATOR . $value; + } + } + return implode(Import::DEFAULT_GLOBAL_MULTI_VALUE_SEPARATOR, $result); + } + + /** + * Set headers columns + * + * @param array $columns + * @return array + */ + public function addHeaderColumns($columns): array + { + return array_merge($columns, $this->downloadableColumns); + } + + /** + * Add downloadable data for export + * + * @param array $dataRow + * @param int $productId + * @return array + */ + public function addData($dataRow, $productId): array + { + if (!empty($this->downloadableData[$productId])) { + $dataRow = array_merge($dataRow, $this->downloadableData[$productId]); + } + return $dataRow; + } + + /** + * Calculate the largest links block + * + * @param array $additionalRowsCount + * @param int $productId + * @return array + */ + public function getAdditionalRowsCount($additionalRowsCount, $productId): array + { + if (!empty($this->downloadableData[$productId])) { + $additionalRowsCount = max($additionalRowsCount, count($this->downloadableData[$productId])); + } + return $additionalRowsCount; + } +} diff --git a/app/code/Magento/DownloadableImportExport/Model/Import/Product/Type/Downloadable.php b/app/code/Magento/DownloadableImportExport/Model/Import/Product/Type/Downloadable.php index c9cdf52f55dd1..a45fded76325f 100644 --- a/app/code/Magento/DownloadableImportExport/Model/Import/Product/Type/Downloadable.php +++ b/app/code/Magento/DownloadableImportExport/Model/Import/Product/Type/Downloadable.php @@ -17,6 +17,7 @@ * * phpcs:disable Magento2.Commenting.ConstantsPHPDocFormatting * @SuppressWarnings(PHPMD.TooManyFields) + * @SuppressWarnings(PHPMD.ExcessiveClassComplexity) */ class Downloadable extends \Magento\CatalogImportExport\Model\Import\Product\Type\AbstractType { @@ -332,7 +333,7 @@ public function isRowValid(array $rowData, $rowNum, $isNewProduct = true) { $this->rowNum = $rowNum; $error = false; - if (!$this->downloadableHelper->isRowDownloadableNoValid($rowData)) { + if (!$this->downloadableHelper->isRowDownloadableNoValid($rowData) && $isNewProduct) { $this->_entityModel->addRowError(self::ERROR_OPTIONS_NOT_FOUND, $this->rowNum); $error = true; } @@ -365,11 +366,6 @@ protected function isRowValidSample(array $rowData) $sampleData = $this->prepareSampleData($rowData[static::COL_DOWNLOADABLE_SAMPLES]); - if ($this->sampleGroupTitle($rowData) == '') { - $result = true; - $this->_entityModel->addRowError(self::ERROR_GROUP_TITLE_NOT_FOUND, $this->rowNum); - } - $result = $result ?? $this->isTitle($sampleData); foreach ($sampleData as $link) { @@ -406,11 +402,6 @@ protected function isRowValidLink(array $rowData) $linkData = $this->prepareLinkData($rowData[self::COL_DOWNLOADABLE_LINKS]); - if ($this->linksAdditionalAttributes($rowData, 'group_title', self::DEFAULT_GROUP_TITLE) == '') { - $this->_entityModel->addRowError(self::ERROR_GROUP_TITLE_NOT_FOUND, $this->rowNum); - $result = true; - } - $result = $result ?? $this->isTitle($linkData); foreach ($linkData as $link) { @@ -896,12 +887,16 @@ protected function parseSampleOption($values) protected function uploadDownloadableFiles($fileName, $type = 'links', $renameFileOff = false) { try { - $res = $this->uploaderHelper->getUploader($type, $this->parameters)->move($fileName, $renameFileOff); - return $res['file']; + $uploader = $this->uploaderHelper->getUploader($type, $this->parameters); + if (!$this->uploaderHelper->isFileExist($fileName)) { + $res = $uploader->move($fileName, $renameFileOff); + $fileName = $res['file']; + } } catch (\Exception $e) { $this->_entityModel->addRowError(self::ERROR_MOVE_FILE, $this->rowNum); - return ''; + $fileName = ''; } + return $fileName; } /** diff --git a/app/code/Magento/DownloadableImportExport/Test/Unit/Model/Import/Product/Type/DownloadableTest.php b/app/code/Magento/DownloadableImportExport/Test/Unit/Model/Import/Product/Type/DownloadableTest.php index 9cb6b061b14ee..2c5b91486d347 100644 --- a/app/code/Magento/DownloadableImportExport/Test/Unit/Model/Import/Product/Type/DownloadableTest.php +++ b/app/code/Magento/DownloadableImportExport/Test/Unit/Model/Import/Product/Type/DownloadableTest.php @@ -6,10 +6,11 @@ namespace Magento\DownloadableImportExport\Test\Unit\Model\Import\Product\Type; +use Magento\Downloadable\Model\Url\DomainValidator; use Magento\Framework\TestFramework\Unit\Helper\ObjectManager as ObjectManager; /** - * Class DownloadableTest + * Class DownloadableTest for downloadable products import * * @SuppressWarnings(PHPMD.CouplingBetweenObjects) */ @@ -39,6 +40,11 @@ class DownloadableTest extends \Magento\ImportExport\Test\Unit\Model\Import\Abst */ protected $prodAttrColFacMock; + /** + * @var DomainValidator + */ + private $domainValidator; + /** * @var \Magento\Catalog\Model\ResourceModel\Product\Attribute\Collection|\PHPUnit_Framework_MockObject_MockObject */ @@ -164,7 +170,7 @@ protected function setUp() // 7. $fileHelper $this->uploaderHelper = $this->createPartialMock( \Magento\DownloadableImportExport\Helper\Uploader::class, - ['getUploader'] + ['getUploader', 'isFileExist'] ); $this->uploaderHelper->expects($this->any())->method('getUploader')->willReturn($this->uploaderMock); $this->downloadableHelper = $this->createPartialMock( @@ -498,7 +504,7 @@ public function dataForSave() /** * @dataProvider isRowValidData */ - public function testIsRowValid(array $rowData, $rowNum, $isNewProduct = true) + public function testIsRowValid(array $rowData, $rowNum, $isNewProduct, $isDomainValid, $expectedResult) { $this->connectionMock->expects($this->any())->method('fetchAll')->with( $this->select @@ -514,6 +520,13 @@ public function testIsRowValid(array $rowData, $rowNum, $isNewProduct = true) ], ] ); + + $this->domainValidator = $this->createMock(DomainValidator::class); + $this->domainValidator + ->expects($this->any())->method('isValid') + ->withAnyParameters() + ->willReturn($isDomainValid); + $this->downloadableModelMock = $this->objectManagerHelper->getObject( \Magento\DownloadableImportExport\Model\Import\Product\Type\Downloadable::class, [ @@ -522,11 +535,12 @@ public function testIsRowValid(array $rowData, $rowNum, $isNewProduct = true) 'resource' => $this->resourceMock, 'params' => $this->paramsArray, 'uploaderHelper' => $this->uploaderHelper, - 'downloadableHelper' => $this->downloadableHelper + 'downloadableHelper' => $this->downloadableHelper, + 'domainValidator' => $this->domainValidator ] ); $result = $this->downloadableModelMock->isRowValid($rowData, $rowNum, $isNewProduct); - $this->assertNotNull($result); + $this->assertEquals($expectedResult, $result); } /** @@ -539,7 +553,7 @@ public function isRowValidData() { return [ [ - [ + 'row_data' => [ 'sku' => 'downloadablesku1', 'product_type' => 'downloadable', 'name' => 'Downloadable Product 1', @@ -549,11 +563,13 @@ public function isRowValidData() . 'downloads=unlimited, file=media/file_link.mp4,sortorder=1|group_title=Group Title, ' . 'title=Title 2, price=10, downloads=unlimited, url=media/file2.mp4,sortorder=0', ], - 0, - true + 'row_num' => 0, + 'is_new_product' => true, + 'is_domain_valid' => true, + 'expected_result' => true ], [ - [ + 'row_data' => [ 'sku' => 'downloadablesku12', 'product_type' => 'downloadable', 'name' => 'Downloadable Product 2', @@ -563,84 +579,98 @@ public function isRowValidData() . ' downloads=unlimited, file=media/file.mp4,sortorder=1|group_title=Group Title,' . ' title=Title 2, price=10, downloads=unlimited, url=media/file2.mp4,sortorder=0', ], - 1, - true + 'row_num' => 1, + 'is_new_product' => true, + 'is_domain_valid' => true, + 'expected_result' => true ], [ - [ + 'row_data' => [ 'sku' => 'downloadablesku12', 'product_type' => 'downloadable', 'name' => 'Downloadable Product 2', + 'downloadable_samples' => 'group_title=Group Title Samples, title=Title 1, file=media/file.mp4' + .',sortorder=1|group_title=Group Title, title=Title 2, url=media/file2.mp4,sortorder=0', + 'downloadable_links' => 'group_title=Group Title Links, title=Title 1, price=10,' + .' downloads=unlimited, file=media/file.mp4,sortorder=1|group_title=Group Title,' + .' title=Title 2, price=10, downloads=unlimited, url=media/file2.mp4,sortorder=0', ], - 2, - true + 'row_num' => 3, + 'is_new_product' => true, + 'is_domain_valid' => true, + 'expected_result' => true ], [ - [ + 'row_data' => [ 'sku' => 'downloadablesku12', 'product_type' => 'downloadable', 'name' => 'Downloadable Product 2', - 'downloadable_samples' => 'title=Title 1, file=media/file.mp4,sortorder=1|title=Title 2,' - . ' url=media/file2.mp4,sortorder=0', + 'downloadable_samples' => 'title=Title 1, file=media/file.mp4,sortorder=1|title=Title 2,' . + ' group_title=Group Title, url=media/file2.mp4,sortorder=0', 'downloadable_links' => 'title=Title 1, price=10, downloads=unlimited, file=media/file.mp4,' . 'sortorder=1|group_title=Group Title, title=Title 2, price=10, downloads=unlimited,' . ' url=media/file2.mp4,sortorder=0', ], - 3, - true + 'row_num' => 4, + 'is_new_product' => true, + 'is_domain_valid' => true, + 'expected_result' => true ], - [ - [ + [ //empty group title samples + 'row_data' => [ 'sku' => 'downloadablesku12', 'product_type' => 'downloadable', 'name' => 'Downloadable Product 2', - 'downloadable_samples' => 'file=media/file.mp4,sortorder=1|group_title=Group Title, ' - . 'url=media/file2.mp4,sortorder=0', - 'downloadable_links' => 'title=Title 1, price=10, downloads=unlimited, file=media/file.mp4,' - . 'sortorder=1|group_title=Group Title, title=Title 2, price=10, downloads=unlimited,' - . ' url=media/file2.mp4,sortorder=0', + 'downloadable_samples' => 'group_title=Group Title Samples, title=Title 1, file=media/file.mp4' + .',sortorder=1|group_title=Group Title, title=Title 2, url=media/file2.mp4,sortorder=0', + 'downloadable_links' => 'group_title=Group Title Links, title=Title 1, price=10,' + .' downloads=unlimited, file=media/file.mp4,sortorder=1|group_title=Group Title,' + .' title=Title 2, price=10, downloads=unlimited, url=media/file2.mp4,sortorder=0', ], - 4, - true + 'row_num' => 5, + 'is_new_product' => true, + 'is_domain_valid' => true, + 'expected_result' => true ], - [ //empty group title samples - [ + [ //empty group title links + 'row_data' => [ 'sku' => 'downloadablesku12', 'product_type' => 'downloadable', 'name' => 'Downloadable Product 2', - 'downloadable_samples' => 'group_title=, title=Title 1, file=media/file.mp4,sortorder=1' - . '|group_title=, title=Title 2, url=media/file2.mp4,sortorder=0', + 'downloadable_samples' => 'group_title=Group Title Samples, title=Title 1, file=media/file.mp4' + .',sortorder=1|group_title=Group Title, title=Title 2, url=media/file2.mp4,sortorder=0', 'downloadable_links' => 'group_title=Group Title Links, title=Title 1, price=10,' - . ' downloads=unlimited, file=media/file_link.mp4,sortorder=1|group_title=Group Title,' - . ' title=Title 2, price=10, downloads=unlimited, url=media/file2.mp4,sortorder=0', + .' downloads=unlimited, file=media/file.mp4,sortorder=1|group_title=Group Title,' + .' title=Title 2, price=10, downloads=unlimited, url=media/file2.mp4,sortorder=0', ], - 5, - true + 'row_num' => 6, + 'is_new_product' => true, + 'is_domain_valid' => true, + 'expected_result' => true ], - [ //empty group title links - [ + [ + 'row_data' => [ 'sku' => 'downloadablesku12', 'product_type' => 'downloadable', 'name' => 'Downloadable Product 2', - 'downloadable_samples' => 'group_title=Group Title Samples, title=Title 1, file=media/file.mp4,' - . 'sortorder=1|group_title=Group Title, title=Title 2, url=media/file2.mp4,sortorder=0', - 'downloadable_links' => 'group_title=, title=Title 1, price=10, downloads=unlimited, ' - . 'file=media/file_link.mp4,sortorder=1|group_title=, title=Title 2, price=10, ' - . 'downloads=unlimited, url=media/file2.mp4,sortorder=0', ], - 6, - true + 'row_num' => 2, + 'is_new_product' => false, + 'is_domain_valid' => true, + 'expected_result' => true ], [ - [ + 'row_data' => [ 'sku' => 'downloadablesku12', 'product_type' => 'downloadable', 'name' => 'Downloadable Product 2', 'downloadable_samples' => '', 'downloadable_links' => '', ], - 7, - true + 'row_num' => 7, + 'is_new_product' => true, + 'is_domain_valid' => true, + 'expected_result' => false ], ]; } @@ -660,6 +690,7 @@ public function testSetUploaderDirFalse($newSku, $bunch, $allowImport, $parsedOp $metadataPoolMock->expects($this->any())->method('getLinkField')->willReturn('entity_id'); $this->downloadableHelper->expects($this->atLeastOnce()) ->method('fillExistOptions')->willReturn($parsedOptions['link']); + $this->uploaderHelper->method('isFileExist')->willReturn(false); $this->downloadableModelMock = $this->objectManagerHelper->getObject( \Magento\DownloadableImportExport\Model\Import\Product\Type\Downloadable::class, diff --git a/app/code/Magento/DownloadableImportExport/etc/di.xml b/app/code/Magento/DownloadableImportExport/etc/di.xml new file mode 100644 index 0000000000000..06768d3e72a8b --- /dev/null +++ b/app/code/Magento/DownloadableImportExport/etc/di.xml @@ -0,0 +1,16 @@ +<?xml version="1.0"?> +<!-- +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +--> +<config xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="urn:magento:framework:ObjectManager/etc/config.xsd"> + <type name="Magento\CatalogImportExport\Model\Export\RowCustomizer\Composite"> + <arguments> + <argument name="customizers" xsi:type="array"> + <item name="downloadableProduct" xsi:type="string">Magento\DownloadableImportExport\Model\Export\RowCustomizer</item> + </argument> + </arguments> + </type> +</config> diff --git a/app/code/Magento/DownloadableImportExport/etc/export.xml b/app/code/Magento/DownloadableImportExport/etc/export.xml new file mode 100644 index 0000000000000..b6e419cc2c389 --- /dev/null +++ b/app/code/Magento/DownloadableImportExport/etc/export.xml @@ -0,0 +1,10 @@ +<?xml version="1.0"?> +<!-- +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +--> +<config xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="urn:magento:module:Magento_ImportExport:etc/export.xsd"> + <entityType entity="catalog_product" name="downloadable" model="Magento\DownloadableImportExport\Model\Export\Product\Type\Downloadable" /> +</config> diff --git a/app/code/Magento/DownloadableImportExport/registration.php b/app/code/Magento/DownloadableImportExport/registration.php index 0dcd082deb6d8..35147034c8d1e 100644 --- a/app/code/Magento/DownloadableImportExport/registration.php +++ b/app/code/Magento/DownloadableImportExport/registration.php @@ -4,6 +4,6 @@ * See COPYING.txt for license details. */ -use \Magento\Framework\Component\ComponentRegistrar; +use Magento\Framework\Component\ComponentRegistrar; ComponentRegistrar::register(ComponentRegistrar::MODULE, 'Magento_DownloadableImportExport', __DIR__); diff --git a/app/code/Magento/Eav/Block/Adminhtml/Attribute/Edit/Main/AbstractMain.php b/app/code/Magento/Eav/Block/Adminhtml/Attribute/Edit/Main/AbstractMain.php index be9d2700664c7..fe660f01c8244 100644 --- a/app/code/Magento/Eav/Block/Adminhtml/Attribute/Edit/Main/AbstractMain.php +++ b/app/code/Magento/Eav/Block/Adminhtml/Attribute/Edit/Main/AbstractMain.php @@ -130,7 +130,8 @@ protected function _prepareForm() 'label' => __('Default Label'), 'title' => __('Default label'), 'required' => true, - 'value' => is_array($labels) ? $labels[0] : $labels + 'value' => is_array($labels) ? $labels[0] : $labels, + 'class' => 'validate-no-html-tags', ] ); @@ -282,7 +283,8 @@ protected function _initFormValues() * Adding js block to the end of this block * * @param string $html - * @return string + * + * @return string */ protected function _afterToHtml($html) { diff --git a/app/code/Magento/Eav/Block/Adminhtml/Attribute/Edit/Options/Options.php b/app/code/Magento/Eav/Block/Adminhtml/Attribute/Edit/Options/Options.php index 7af7bf447c45a..69f417e1ea732 100644 --- a/app/code/Magento/Eav/Block/Adminhtml/Attribute/Edit/Options/Options.php +++ b/app/code/Magento/Eav/Block/Adminhtml/Attribute/Edit/Options/Options.php @@ -152,10 +152,10 @@ protected function _prepareOptionValues( $inputType = ''; } - $values = []; + $values = [[]]; $isSystemAttribute = is_array($optionCollection); if ($isSystemAttribute) { - $values = $this->getPreparedValues($optionCollection, $isSystemAttribute, $inputType, $defaultValues); + $values[] = $this->getPreparedValues($optionCollection, $isSystemAttribute, $inputType, $defaultValues); } else { $optionCollection->setPageSize(200); $pageCount = $optionCollection->getLastPageNumber(); @@ -163,15 +163,12 @@ protected function _prepareOptionValues( while ($currentPage <= $pageCount) { $optionCollection->clear(); $optionCollection->setCurPage($currentPage); - $values = array_merge( - $values, - $this->getPreparedValues($optionCollection, $isSystemAttribute, $inputType, $defaultValues) - ); + $values[] = $this->getPreparedValues($optionCollection, $isSystemAttribute, $inputType, $defaultValues); $currentPage++; } } - return $values; + return array_merge(...$values); } /** diff --git a/app/code/Magento/Eav/Model/Api/SearchCriteria/CollectionProcessor/FilterProcessor.php b/app/code/Magento/Eav/Model/Api/SearchCriteria/CollectionProcessor/FilterProcessor.php index c24f87d36d64b..8f0a3fb2baa69 100644 --- a/app/code/Magento/Eav/Model/Api/SearchCriteria/CollectionProcessor/FilterProcessor.php +++ b/app/code/Magento/Eav/Model/Api/SearchCriteria/CollectionProcessor/FilterProcessor.php @@ -11,6 +11,9 @@ use Magento\Framework\Api\SearchCriteriaInterface; use Magento\Framework\Data\Collection\AbstractDb; +/** + * SearchCriteria FilterProcessor + */ class FilterProcessor implements CollectionProcessorInterface { /** @@ -113,6 +116,6 @@ private function getCustomFilterForField($field) */ private function getFieldMapping($field) { - return isset($this->fieldMapping[$field]) ? $this->fieldMapping[$field] : $field; + return $this->fieldMapping[$field] ?? $field; } } diff --git a/app/code/Magento/Eav/Model/Config.php b/app/code/Magento/Eav/Model/Config.php index 20126d5146c35..718ef1a748590 100644 --- a/app/code/Magento/Eav/Model/Config.php +++ b/app/code/Magento/Eav/Model/Config.php @@ -157,12 +157,12 @@ class Config /** * @param \Magento\Framework\App\CacheInterface $cache - * @param \Magento\Eav\Model\Entity\TypeFactory $entityTypeFactory - * @param \Magento\Eav\Model\ResourceModel\Entity\Type\CollectionFactory $entityTypeCollectionFactory + * @param Entity\TypeFactory $entityTypeFactory + * @param ResourceModel\Entity\Type\CollectionFactory $entityTypeCollectionFactory * @param \Magento\Framework\App\Cache\StateInterface $cacheState * @param \Magento\Framework\Validator\UniversalFactory $universalFactory - * @param SerializerInterface $serializer - * @param ScopeConfigInterface $scopeConfig + * @param SerializerInterface|null $serializer + * @param ScopeConfigInterface|null $scopeConfig * @param array $attributesForPreload * @codeCoverageIgnore */ @@ -228,7 +228,7 @@ public function clear() */ protected function _load($id) { - return isset($this->_objects[$id]) ? $this->_objects[$id] : null; + return $this->_objects[$id] ?? null; } /** @@ -239,14 +239,14 @@ protected function _load($id) */ private function loadAttributes($entityTypeCode) { - return isset($this->attributes[$entityTypeCode]) ? $this->attributes[$entityTypeCode] : []; + return $this->attributes[$entityTypeCode] ?? []; } /** * Associate object with identifier * - * @param mixed $obj - * @param mixed $id + * @param mixed $obj + * @param mixed $id * @return void * @codeCoverageIgnore */ @@ -271,8 +271,8 @@ private function saveAttribute(AbstractAttribute $attribute, $entityTypeCode, $a /** * Specify reference for entity type id * - * @param int $id - * @param string $code + * @param int $id + * @param string $code * @return $this * @codeCoverageIgnore */ @@ -290,15 +290,15 @@ protected function _addEntityTypeReference($id, $code) */ protected function _getEntityTypeReference($id) { - return isset($this->_references['entity'][$id]) ? $this->_references['entity'][$id] : null; + return $this->_references['entity'][$id] ?? null; } /** * Specify reference between entity attribute id and attribute code * - * @param int $id - * @param string $code - * @param string $entityTypeCode + * @param int $id + * @param string $code + * @param string $entityTypeCode * @return $this */ protected function _addAttributeReference($id, $code, $entityTypeCode) @@ -374,7 +374,9 @@ protected function _initEntityTypes() } \Magento\Framework\Profiler::start('EAV: ' . __METHOD__, ['group' => 'EAV', 'method' => __METHOD__]); - if ($this->isCacheEnabled() && ($cache = $this->_cache->load(self::ENTITIES_CACHE_ID))) { + if ($this->isCacheEnabled() && + ($cache = $this->_cache->load(self::ENTITIES_CACHE_ID)) + ) { $this->_entityTypeData = $this->serializer->unserialize($cache); foreach ($this->_entityTypeData as $typeCode => $data) { $typeId = $data['entity_type_id']; @@ -522,9 +524,9 @@ public function getAttributes($entityType) /** * Get attribute by code for entity type * - * @param mixed $entityType - * @param mixed $code - * @return AbstractAttribute + * @param mixed $entityType + * @param mixed $code + * @return AbstractAttribute * @throws LocalizedException */ public function getAttribute($entityType, $code) @@ -737,8 +739,8 @@ public function getEntityAttributeCodes($entityType, $object = null) /** * Get all entity type attributes * - * @param int|string|Type $entityType - * @param \Magento\Framework\DataObject|null $object + * @param int|string|Type $entityType + * @param \Magento\Framework\DataObject|null $object * @return AbstractAttribute[] * * @SuppressWarnings(PHPMD.CyclomaticComplexity) @@ -822,6 +824,10 @@ protected function _createAttribute($entityType, $attributeData) $fullAttributeData = array_key_exists('is_required', $attributeData); if ($existsFullAttribute || (!$existsFullAttribute && !$fullAttributeData)) { + $scopeIsRequired = $attributeData['scope_is_required'] ?? null; + if ($scopeIsRequired !== null) { + $attribute->setData('scope_is_required', $scopeIsRequired); + } return $attribute; } } diff --git a/app/code/Magento/Eav/Model/Entity/AbstractEntity.php b/app/code/Magento/Eav/Model/Entity/AbstractEntity.php index 7649c89a07955..5b6252f2c0d6c 100644 --- a/app/code/Magento/Eav/Model/Entity/AbstractEntity.php +++ b/app/code/Magento/Eav/Model/Entity/AbstractEntity.php @@ -1021,6 +1021,8 @@ public function load($object, $entityId, $attributes = []) protected function loadAttributesMetadata($attributes) { $this->loadAttributesForObject($attributes); + + return $this; } /** @@ -1433,8 +1435,10 @@ protected function _processSaveData($saveData) $insertEntity = true; $entityTable = $this->getEntityTable(); $entityIdField = $this->getEntityIdField(); + // phpstan:ignore "Undefined variable" $entityId = $newObject->getId(); + // phpstan:ignore "Undefined variable" unset($entityRow[$entityIdField]); if (!empty($entityId) && is_numeric($entityId)) { $bind = ['entity_id' => $entityId]; @@ -1450,6 +1454,7 @@ protected function _processSaveData($saveData) /** * Process base row */ + // phpstan:ignore "Undefined variable" $entityObject = new DataObject($entityRow); $entityRow = $this->_prepareDataForTable($entityObject, $entityTable); if ($insertEntity) { @@ -1460,6 +1465,7 @@ protected function _processSaveData($saveData) $connection->insert($entityTable, $entityRow); $entityId = $connection->lastInsertId($entityTable); } + // phpstan:ignore "Undefined variable" $newObject->setId($entityId); } else { $where = sprintf('%s=%d', $connection->quoteIdentifier($entityIdField), $entityId); @@ -1472,6 +1478,7 @@ protected function _processSaveData($saveData) if (!empty($insert)) { foreach ($insert as $attributeId => $value) { $attribute = $this->getAttribute($attributeId); + // phpstan:ignore "Undefined variable" $this->_insertAttribute($newObject, $attribute, $value); } } @@ -1482,6 +1489,7 @@ protected function _processSaveData($saveData) if (!empty($update)) { foreach ($update as $attributeId => $v) { $attribute = $this->getAttribute($attributeId); + // phpstan:ignore "Undefined variable" $this->_updateAttribute($newObject, $attribute, $v['value_id'], $v['value']); } } @@ -1491,12 +1499,14 @@ protected function _processSaveData($saveData) */ if (!empty($delete)) { foreach ($delete as $table => $values) { + // phpstan:ignore "Undefined variable" $this->_deleteAttributes($newObject, $table, $values); } } $this->_processAttributeValues(); + // phpstan:ignore "Undefined variable" $newObject->isObjectNew(false); return $this; @@ -1573,7 +1583,7 @@ protected function _processAttributeValues() { $connection = $this->getConnection(); foreach ($this->_attributeValuesToSave as $table => $data) { - $connection->insertOnDuplicate($table, $data, ['value']); + $connection->insertOnDuplicate($table, $data, array_keys($data[0])); } foreach ($this->_attributeValuesToDelete as $table => $valueIds) { @@ -1607,7 +1617,9 @@ protected function _prepareValueForSave($value, AbstractAttribute $attribute) self::$_attributeBackendTables[$backendTable] = $this->getConnection()->describeTable($backendTable); } $describe = self::$_attributeBackendTables[$backendTable]; - return $this->getConnection()->prepareColumnValue($describe['value'], $value); + $columnName = $attribute->isStatic() ? $attribute->getAttributeCode() : 'value'; + + return $this->getConnection()->prepareColumnValue($describe[$columnName], $value); } /** diff --git a/app/code/Magento/Eav/Model/Entity/Attribute/Backend/Datetime.php b/app/code/Magento/Eav/Model/Entity/Attribute/Backend/Datetime.php index ba6f6c6ea2aaa..6099d329571e8 100644 --- a/app/code/Magento/Eav/Model/Entity/Attribute/Backend/Datetime.php +++ b/app/code/Magento/Eav/Model/Entity/Attribute/Backend/Datetime.php @@ -7,6 +7,8 @@ namespace Magento\Eav\Model\Entity\Attribute\Backend; /** + * Prepare date for save in DB + * * @api * @since 100.0.2 */ @@ -61,8 +63,8 @@ public function beforeSave($object) /** * Prepare date for save in DB * - * string format used from input fields (all date input fields need apply locale settings) - * int value can be declared in code (this meen whot we use valid date) + * String format is used in input fields (all date input fields need apply locale settings) + * int (Unix) format can be used in other parts of the code * * @param string|int|\DateTimeInterface $date * @return string @@ -72,7 +74,7 @@ public function formatDate($date) if (empty($date)) { return null; } - // unix timestamp given - simply instantiate date object + // Unix timestamp given - simply instantiate date object if (is_scalar($date) && preg_match('/^[0-9]+$/', $date)) { $date = (new \DateTime())->setTimestamp($date); } elseif (!($date instanceof \DateTimeInterface)) { diff --git a/app/code/Magento/Eav/Model/Entity/Attribute/Source/AbstractSource.php b/app/code/Magento/Eav/Model/Entity/Attribute/Source/AbstractSource.php index dd4cd4217a127..23b6e8792100c 100644 --- a/app/code/Magento/Eav/Model/Entity/Attribute/Source/AbstractSource.php +++ b/app/code/Magento/Eav/Model/Entity/Attribute/Source/AbstractSource.php @@ -69,7 +69,7 @@ public function getOptionText($value) if (count($options) > 0) { foreach ($options as $option) { if (isset($option['value']) && $option['value'] == $value) { - return isset($option['label']) ? $option['label'] : $option['value']; + return $option['label'] ?? $option['value']; } } } diff --git a/app/code/Magento/Eav/Model/Entity/Collection/AbstractCollection.php b/app/code/Magento/Eav/Model/Entity/Collection/AbstractCollection.php index e50abbc11e54a..1fc513ed0ea80 100644 --- a/app/code/Magento/Eav/Model/Entity/Collection/AbstractCollection.php +++ b/app/code/Magento/Eav/Model/Entity/Collection/AbstractCollection.php @@ -24,7 +24,7 @@ abstract class AbstractCollection extends AbstractDb implements SourceProviderInterface { /** - * Attribute table alias prefix + * Define default prefix for attribute table alias */ const ATTRIBUTE_TABLE_ALIAS_PREFIX = 'at_'; @@ -495,7 +495,7 @@ public function addAttributeToSelect($attribute, $joinType = false) $entity = clone $this->getEntity(); $attributes = $entity->loadAllAttributes()->getAttributesByCode(); foreach ($attributes as $attrCode => $attr) { - $this->_selectAttributes[$attrCode] = $attr->getId(); + $this->_selectAttributes[$attrCode] = (int) $attr->getId(); } } else { if (isset($this->_joinAttributes[$attribute])) { @@ -511,7 +511,7 @@ public function addAttributeToSelect($attribute, $joinType = false) ) ); } - $this->_selectAttributes[$attrInstance->getAttributeCode()] = $attrInstance->getId(); + $this->_selectAttributes[$attrInstance->getAttributeCode()] = (int) $attrInstance->getId(); } return $this; } @@ -1173,7 +1173,7 @@ public function _loadAttributes($printQuery = false, $logQuery = false) } $attribute = $this->_eavConfig->getAttribute($entity->getType(), $attributeCode); if ($attribute && !$attribute->isStatic()) { - $tableAttributes[$attribute->getBackendTable()][] = $attributeId; + $tableAttributes[$attribute->getBackendTable()][] = (int) $attributeId; if (!isset($attributeTypes[$attribute->getBackendTable()])) { $attributeTypes[$attribute->getBackendTable()] = $attribute->getBackendType(); } diff --git a/app/code/Magento/Eav/Model/Entity/Setup/PropertyMapperAbstract.php b/app/code/Magento/Eav/Model/Entity/Setup/PropertyMapperAbstract.php index 87ff3a9d22bfd..fc775594c65fd 100644 --- a/app/code/Magento/Eav/Model/Entity/Setup/PropertyMapperAbstract.php +++ b/app/code/Magento/Eav/Model/Entity/Setup/PropertyMapperAbstract.php @@ -7,6 +7,9 @@ */ namespace Magento\Eav\Model\Entity\Setup; +/** + * @inheritdoc + */ abstract class PropertyMapperAbstract implements PropertyMapperInterface { /** @@ -22,6 +25,6 @@ protected function _getValue($array, $key, $default = null) if (isset($array[$key]) && is_bool($array[$key])) { $array[$key] = (int)$array[$key]; } - return isset($array[$key]) ? $array[$key] : $default; + return $array[$key] ?? $default; } } diff --git a/app/code/Magento/Eav/Model/Entity/Type.php b/app/code/Magento/Eav/Model/Entity/Type.php index 444d58bf546d4..24dbc0459ae97 100644 --- a/app/code/Magento/Eav/Model/Entity/Type.php +++ b/app/code/Magento/Eav/Model/Entity/Type.php @@ -264,7 +264,7 @@ public function fetchNewIncrementId($storeId = null) */ public function getEntityIdField() { - return isset($this->_data['entity_id_field']) ? $this->_data['entity_id_field'] : null; + return $this->_data['entity_id_field'] ?? null; } /** @@ -319,7 +319,7 @@ public function getEntityTablePrefix() */ public function getDefaultAttributeSetId() { - return isset($this->_data['default_attribute_set_id']) ? $this->_data['default_attribute_set_id'] : null; + return $this->_data['default_attribute_set_id'] ?? null; } /** @@ -329,7 +329,7 @@ public function getDefaultAttributeSetId() */ public function getEntityTypeId() { - return isset($this->_data['entity_type_id']) ? $this->_data['entity_type_id'] : null; + return $this->_data['entity_type_id'] ?? null; } /** @@ -339,7 +339,7 @@ public function getEntityTypeId() */ public function getEntityTypeCode() { - return isset($this->_data['entity_type_code']) ? $this->_data['entity_type_code'] : null; + return $this->_data['entity_type_code'] ?? null; } /** diff --git a/app/code/Magento/Eav/Model/Form.php b/app/code/Magento/Eav/Model/Form.php index a34b53eede354..074c6cf46a2f4 100644 --- a/app/code/Magento/Eav/Model/Form.php +++ b/app/code/Magento/Eav/Model/Form.php @@ -3,6 +3,7 @@ * Copyright © Magento, Inc. All rights reserved. * See COPYING.txt for license details. */ + namespace Magento\Eav\Model; use Magento\Framework\App\RequestInterface; @@ -10,6 +11,7 @@ /** * EAV Entity Form Model * + * phpcs:disable Magento2.Classes.AbstractApi * @api * @SuppressWarnings(PHPMD.TooManyFields) * @SuppressWarnings(PHPMD.CouplingBetweenObjects) @@ -274,8 +276,8 @@ public function getStore() /** * Return current form code * - * @throws \Magento\Framework\Exception\LocalizedException * @return string + * @throws \Magento\Framework\Exception\LocalizedException */ public function getFormCode() { @@ -303,8 +305,8 @@ public function getEntityType() /** * Return current entity instance * - * @throws \Magento\Framework\Exception\LocalizedException * @return \Magento\Framework\Model\AbstractModel + * @throws \Magento\Framework\Exception\LocalizedException */ public function getEntity() { @@ -485,11 +487,11 @@ public function validateData(array $data) { $validator = $this->_getValidator($data); if (!$validator->isValid($this->getEntity())) { - $messages = []; + $messages = [[]]; foreach ($validator->getMessages() as $errorMessages) { - $messages = array_merge($messages, (array)$errorMessages); + $messages[] = (array)$errorMessages; } - return $messages; + return array_merge(...$messages); } return true; } diff --git a/app/code/Magento/Eav/Model/ResourceModel/Entity/Attribute/Set.php b/app/code/Magento/Eav/Model/ResourceModel/Entity/Attribute/Set.php index 4e1762730a8d9..64c76cdbf2627 100644 --- a/app/code/Magento/Eav/Model/ResourceModel/Entity/Attribute/Set.php +++ b/app/code/Magento/Eav/Model/ResourceModel/Entity/Attribute/Set.php @@ -6,6 +6,9 @@ namespace Magento\Eav\Model\ResourceModel\Entity\Attribute; +/** + * Basic implementation for attribute sets + */ class Set extends \Magento\Framework\Model\ResourceModel\Db\AbstractDb { /** @@ -24,8 +27,6 @@ class Set extends \Magento\Framework\Model\ResourceModel\Db\AbstractDb protected $eavConfig; /** - * Constructor - * * @param \Magento\Framework\Model\ResourceModel\Db\Context $context * @param GroupFactory $attrGroupFactory * @param \Magento\Eav\Model\Config $eavConfig @@ -54,7 +55,7 @@ protected function _construct() } /** - * Perform actions after object save + * Perform actions after object save. * * @param \Magento\Framework\Model\AbstractModel $object * @return $this diff --git a/app/code/Magento/Eav/Model/TypeLocator/SimpleType.php b/app/code/Magento/Eav/Model/TypeLocator/SimpleType.php index 73b0516117df6..eb9b173ed52be 100644 --- a/app/code/Magento/Eav/Model/TypeLocator/SimpleType.php +++ b/app/code/Magento/Eav/Model/TypeLocator/SimpleType.php @@ -6,9 +6,9 @@ namespace Magento\Eav\Model\TypeLocator; +use Magento\Eav\Api\AttributeRepositoryInterface; use Magento\Framework\Exception\NoSuchEntityException; use Magento\Framework\Reflection\TypeProcessor; -use Magento\Eav\Api\AttributeRepositoryInterface; use Magento\Framework\Webapi\CustomAttribute\ServiceTypeListInterface; use Magento\Framework\Webapi\CustomAttributeTypeLocatorInterface; @@ -28,9 +28,10 @@ class SimpleType implements CustomAttributeTypeLocatorInterface private $serviceTypeList; /** - * Initialize dependencies. + * Constructor * * @param AttributeRepositoryInterface $attributeRepository + * @param ServiceTypeListInterface $serviceTypeList */ public function __construct( AttributeRepositoryInterface $attributeRepository, @@ -41,7 +42,7 @@ public function __construct( } /** - * {@inheritdoc} + * @inheritdoc */ public function getType($attributeCode, $entityType) { @@ -59,12 +60,13 @@ public function getType($attributeCode, $entityType) 'datetime' => TypeProcessor::NORMALIZED_STRING_TYPE, 'decimal' => TypeProcessor::NORMALIZED_DOUBLE_TYPE, ]; - return isset($backendTypeMap[$backendType]) - ? $backendTypeMap[$backendType] : TypeProcessor::NORMALIZED_ANY_TYPE; + return $backendTypeMap[$backendType] ?? TypeProcessor::NORMALIZED_ANY_TYPE; } /** - * {@inheritDoc} + * Get data Types from service type list + * + * @return void */ public function getAllServiceDataInterfaces() { diff --git a/app/code/Magento/Eav/Test/Unit/Model/Entity/Collection/AbstractCollectionStub.php b/app/code/Magento/Eav/Test/Unit/Model/Entity/Collection/AbstractCollectionStub.php index 57374ed05618f..f82dbf0ee3dc9 100644 --- a/app/code/Magento/Eav/Test/Unit/Model/Entity/Collection/AbstractCollectionStub.php +++ b/app/code/Magento/Eav/Test/Unit/Model/Entity/Collection/AbstractCollectionStub.php @@ -30,4 +30,14 @@ protected function _construct() { return $this->_init(\Magento\Framework\DataObject::class, 'test_entity_model'); } + + /** + * Retrieve collection empty item + * + * @return \Magento\Framework\DataObject + */ + public function getNewEmptyItem() + { + return new \Magento\Framework\DataObject(); + } } diff --git a/app/code/Magento/Eav/Test/Unit/Model/Entity/Collection/AbstractCollectionTest.php b/app/code/Magento/Eav/Test/Unit/Model/Entity/Collection/AbstractCollectionTest.php index bc4ed7d4bd9e4..051c870a04b80 100644 --- a/app/code/Magento/Eav/Test/Unit/Model/Entity/Collection/AbstractCollectionTest.php +++ b/app/code/Magento/Eav/Test/Unit/Model/Entity/Collection/AbstractCollectionTest.php @@ -8,10 +8,15 @@ /** * AbstractCollection test * + * Test for AbstractCollection class * @SuppressWarnings(PHPMD.CouplingBetweenObjects) */ class AbstractCollectionTest extends \PHPUnit\Framework\TestCase { + const ATTRIBUTE_CODE = 'any_attribute'; + const ATTRIBUTE_ID_STRING = '15'; + const ATTRIBUTE_ID_INT = 15; + /** * @var AbstractCollectionStub|\PHPUnit_Framework_MockObject_MockObject */ @@ -105,6 +110,26 @@ protected function setUp() $entityMock = $this->createMock(\Magento\Eav\Model\Entity\AbstractEntity::class); $entityMock->expects($this->any())->method('getConnection')->will($this->returnValue($connectionMock)); $entityMock->expects($this->any())->method('getDefaultAttributes')->will($this->returnValue([])); + $entityMock->expects($this->any())->method('getLinkField')->willReturn('entity_id'); + + $attributeMock = $this->createMock(\Magento\Eav\Model\Attribute::class); + $attributeMock->expects($this->any())->method('isStatic')->willReturn(false); + $attributeMock->expects($this->any())->method('getAttributeCode')->willReturn(self::ATTRIBUTE_CODE); + $attributeMock->expects($this->any())->method('getBackendTable')->willReturn('eav_entity_int'); + $attributeMock->expects($this->any())->method('getBackendType')->willReturn('int'); + $attributeMock->expects($this->any())->method('getId')->willReturn(self::ATTRIBUTE_ID_STRING); + + $entityMock + ->expects($this->any()) + ->method('getAttribute') + ->with(self::ATTRIBUTE_CODE) + ->willReturn($attributeMock); + + $this->configMock + ->expects($this->any()) + ->method('getAttribute') + ->with(null, self::ATTRIBUTE_CODE) + ->willReturn($attributeMock); $this->validatorFactoryMock->expects( $this->any() @@ -193,6 +218,34 @@ public function testRemoveItemByKey($values, $count) $this->assertNull($this->model->getItemById($testId)); } + /** + * @dataProvider getItemsDataProvider + */ + public function testAttributeIdIsInt($values) + { + $this->resourceHelperMock->expects($this->any())->method('getLoadAttributesSelectGroups')->willReturn([]); + $this->fetchStrategyMock->expects($this->any())->method('fetchAll')->will($this->returnValue($values)); + $selectMock = $this->coreResourceMock->getConnection()->select(); + $selectMock->expects($this->any())->method('from')->willReturn($selectMock); + $selectMock->expects($this->any())->method('join')->willReturn($selectMock); + $selectMock->expects($this->any())->method('where')->willReturn($selectMock); + $selectMock->expects($this->any())->method('columns')->willReturn($selectMock); + + $this->model + ->addAttributeToSelect(self::ATTRIBUTE_CODE) + ->_loadEntities() + ->_loadAttributes(); + + $_selectAttributesActualValue = $this->readAttribute($this->model, '_selectAttributes'); + + $this->assertAttributeEquals( + [self::ATTRIBUTE_CODE => self::ATTRIBUTE_ID_STRING], + '_selectAttributes', + $this->model + ); + $this->assertSame($_selectAttributesActualValue[self::ATTRIBUTE_CODE], self::ATTRIBUTE_ID_INT); + } + /** * @return array */ diff --git a/app/code/Magento/Eav/etc/adminhtml/system.xml b/app/code/Magento/Eav/etc/adminhtml/system.xml index 86916abe812d9..2fe1564786997 100644 --- a/app/code/Magento/Eav/etc/adminhtml/system.xml +++ b/app/code/Magento/Eav/etc/adminhtml/system.xml @@ -8,9 +8,9 @@ <config xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="urn:magento:module:Magento_Config:etc/system_file.xsd"> <system> <section id="dev"> - <group id="caching" translate="label" type="text" sortOrder="120" showInDefault="1" showInWebsite="0" showInStore="0"> + <group id="caching" translate="label" type="text" sortOrder="120" showInDefault="1"> <label>Caching Settings</label> - <field id="cache_user_defined_attributes" translate="label comment" type="select" sortOrder="10" showInDefault="1" showInWebsite="0" showInStore="0" canRestore="1"> + <field id="cache_user_defined_attributes" translate="label comment" type="select" sortOrder="10" showInDefault="1" canRestore="1"> <label>Cache User Defined Attributes</label> <source_model>Magento\Config\Model\Config\Source\Yesno</source_model> <comment>By default only system EAV attributes are cached.</comment> diff --git a/app/code/Magento/Eav/etc/di.xml b/app/code/Magento/Eav/etc/di.xml index a09dc28399858..21f248f1b1094 100644 --- a/app/code/Magento/Eav/etc/di.xml +++ b/app/code/Magento/Eav/etc/di.xml @@ -209,4 +209,14 @@ </argument> </arguments> </type> + <virtualType name="configured_eav_cache" type="Magento\Framework\App\Cache"> + <arguments> + <argument name="cacheIdentifier" xsi:type="string">eav</argument> + </arguments> + </virtualType> + <type name="Magento\Eav\Model\Config"> + <arguments> + <argument name="cache" xsi:type="object">configured_eav_cache</argument> + </arguments> + </type> </config> diff --git a/app/code/Magento/Eav/registration.php b/app/code/Magento/Eav/registration.php index 6506d5cd06738..07b222f67f633 100644 --- a/app/code/Magento/Eav/registration.php +++ b/app/code/Magento/Eav/registration.php @@ -4,6 +4,6 @@ * See COPYING.txt for license details. */ -use \Magento\Framework\Component\ComponentRegistrar; +use Magento\Framework\Component\ComponentRegistrar; ComponentRegistrar::register(ComponentRegistrar::MODULE, 'Magento_Eav', __DIR__); diff --git a/app/code/Magento/Elasticsearch/Block/Adminhtml/System/Config/Elasticsearch5/TestConnection.php b/app/code/Magento/Elasticsearch/Block/Adminhtml/System/Config/Elasticsearch5/TestConnection.php index aa40928ce2b16..2b2da7522dfa6 100644 --- a/app/code/Magento/Elasticsearch/Block/Adminhtml/System/Config/Elasticsearch5/TestConnection.php +++ b/app/code/Magento/Elasticsearch/Block/Adminhtml/System/Config/Elasticsearch5/TestConnection.php @@ -8,11 +8,12 @@ /** * Elasticsearch 5x test connection block * @codeCoverageIgnore + * @deprecated because of EOL for Elasticsearch5 */ class TestConnection extends \Magento\AdvancedSearch\Block\Adminhtml\System\Config\TestConnection { /** - * {@inheritdoc} + * @inheritdoc */ protected function _getFieldMapping() { diff --git a/app/code/Magento/Elasticsearch/Block/Adminhtml/System/Config/TestConnection.php b/app/code/Magento/Elasticsearch/Block/Adminhtml/System/Config/TestConnection.php deleted file mode 100644 index 5dc4476794da7..0000000000000 --- a/app/code/Magento/Elasticsearch/Block/Adminhtml/System/Config/TestConnection.php +++ /dev/null @@ -1,31 +0,0 @@ -<?php -/** - * Copyright © Magento, Inc. All rights reserved. - * See COPYING.txt for license details. - */ -namespace Magento\Elasticsearch\Block\Adminhtml\System\Config; - -/** - * Elasticsearch test connection block - * @codeCoverageIgnore - */ -class TestConnection extends \Magento\AdvancedSearch\Block\Adminhtml\System\Config\TestConnection -{ - /** - * {@inheritdoc} - */ - protected function _getFieldMapping() - { - $fields = [ - 'engine' => 'catalog_search_engine', - 'hostname' => 'catalog_search_elasticsearch_server_hostname', - 'port' => 'catalog_search_elasticsearch_server_port', - 'index' => 'catalog_search_elasticsearch_index_prefix', - 'enableAuth' => 'catalog_search_elasticsearch_enable_auth', - 'username' => 'catalog_search_elasticsearch_username', - 'password' => 'catalog_search_elasticsearch_password', - 'timeout' => 'catalog_search_elasticsearch_server_timeout', - ]; - return array_merge(parent::_getFieldMapping(), $fields); - } -} diff --git a/app/code/Magento/Elasticsearch/Elasticsearch5/Model/Adapter/BatchDataMapper/CategoryFieldsProvider.php b/app/code/Magento/Elasticsearch/Elasticsearch5/Model/Adapter/BatchDataMapper/CategoryFieldsProvider.php index eb7874a936140..dd9a9d904ddfe 100644 --- a/app/code/Magento/Elasticsearch/Elasticsearch5/Model/Adapter/BatchDataMapper/CategoryFieldsProvider.php +++ b/app/code/Magento/Elasticsearch/Elasticsearch5/Model/Adapter/BatchDataMapper/CategoryFieldsProvider.php @@ -3,14 +3,14 @@ * Copyright © Magento, Inc. All rights reserved. * See COPYING.txt for license details. */ +declare(strict_types=1); namespace Magento\Elasticsearch\Elasticsearch5\Model\Adapter\BatchDataMapper; -use Magento\Elasticsearch\Model\ResourceModel\Index; use Magento\AdvancedSearch\Model\Adapter\DataMapper\AdditionalFieldsProviderInterface; use Magento\Elasticsearch\Model\Adapter\FieldMapper\Product\AttributeProvider; -use Magento\Framework\App\ObjectManager; use Magento\Elasticsearch\Model\Adapter\FieldMapper\Product\FieldProvider\FieldName\ResolverInterface; +use Magento\Elasticsearch\Model\ResourceModel\Index; /** * Provide data mapping for categories fields @@ -34,19 +34,17 @@ class CategoryFieldsProvider implements AdditionalFieldsProviderInterface /** * @param Index $resourceIndex - * @param AttributeProvider|null $attributeAdapterProvider - * @param ResolverInterface|null $fieldNameResolver + * @param AttributeProvider $attributeAdapterProvider + * @param ResolverInterface $fieldNameResolver */ public function __construct( Index $resourceIndex, - AttributeProvider $attributeAdapterProvider = null, - ResolverInterface $fieldNameResolver = null + AttributeProvider $attributeAdapterProvider, + ResolverInterface $fieldNameResolver ) { $this->resourceIndex = $resourceIndex; - $this->attributeAdapterProvider = $attributeAdapterProvider ?: ObjectManager::getInstance() - ->get(AttributeProvider::class); - $this->fieldNameResolver = $fieldNameResolver ?: ObjectManager::getInstance() - ->get(ResolverInterface::class); + $this->attributeAdapterProvider = $attributeAdapterProvider; + $this->fieldNameResolver = $fieldNameResolver; } /** diff --git a/app/code/Magento/Elasticsearch/Elasticsearch5/Model/Adapter/DataMapper/ProductDataMapper.php b/app/code/Magento/Elasticsearch/Elasticsearch5/Model/Adapter/DataMapper/ProductDataMapper.php deleted file mode 100644 index f0b7380397235..0000000000000 --- a/app/code/Magento/Elasticsearch/Elasticsearch5/Model/Adapter/DataMapper/ProductDataMapper.php +++ /dev/null @@ -1,454 +0,0 @@ -<?php -/** - * Copyright © Magento, Inc. All rights reserved. - * See COPYING.txt for license details. - */ - -namespace Magento\Elasticsearch\Elasticsearch5\Model\Adapter\DataMapper; - -use Magento\Catalog\Model\ResourceModel\Eav\Attribute; -use Magento\Elasticsearch\Model\Adapter\Container\Attribute as AttributeContainer; -use Magento\Elasticsearch\Model\Adapter\Document\Builder; -use Magento\Store\Model\StoreManagerInterface; -use Magento\Customer\Api\Data\GroupInterface; -use Magento\Elasticsearch\Model\ResourceModel\Index; -use Magento\Elasticsearch\Model\Adapter\FieldMapperInterface; -use Magento\Elasticsearch\Model\Adapter\DataMapperInterface; -use Magento\Elasticsearch\Model\Adapter\FieldType\Date as DateFieldType; -use Magento\Elasticsearch\Model\Adapter\FieldMapper\Product\AttributeProvider; -use Magento\Framework\App\ObjectManager; -use Magento\Elasticsearch\Model\Adapter\FieldMapper\Product\FieldProvider\FieldName\ResolverInterface; - -/** - * Don't use this product data mapper class. - * - * @deprecated 100.2.0 - * @see \Magento\Elasticsearch\Model\Adapter\BatchDataMapperInterface - */ -class ProductDataMapper implements DataMapperInterface -{ - /** - * Attribute code for image - */ - const MEDIA_ROLE_IMAGE = 'image'; - - /** - * Attribute code for small image - */ - const MEDIA_ROLE_SMALL_IMAGE = 'small_image'; - - /** - * Attribute code for thumbnail - */ - const MEDIA_ROLE_THUMBNAIL = 'thumbnail'; - - /** - * Attribute code for swatches - */ - const MEDIA_ROLE_SWATCH_IMAGE = 'swatch_image'; - - /** - * @var Builder - */ - private $builder; - - /** - * @var AttributeContainer - */ - private $attributeContainer; - - /** - * @var Index - */ - private $resourceIndex; - - /** - * @var FieldMapperInterface - */ - private $fieldMapper; - - /** - * @var StoreManagerInterface - */ - private $storeManager; - - /** - * @var DateFieldType - */ - private $dateFieldType; - - /** - * Media gallery roles - * - * @var array - */ - protected $mediaGalleryRoles; - - /** - * @var AttributeProvider - */ - private $attributeAdapterProvider; - - /** - * @var ResolverInterface - */ - private $fieldNameResolver; - - /** - * Construction for DocumentDataMapper - * - * @param Builder $builder - * @param AttributeContainer $attributeContainer - * @param Index $resourceIndex - * @param FieldMapperInterface $fieldMapper - * @param StoreManagerInterface $storeManager - * @param DateFieldType $dateFieldType - * @param AttributeProvider|null $attributeAdapterProvider - * @param ResolverInterface|null $fieldNameResolver - */ - public function __construct( - Builder $builder, - AttributeContainer $attributeContainer, - Index $resourceIndex, - FieldMapperInterface $fieldMapper, - StoreManagerInterface $storeManager, - DateFieldType $dateFieldType, - AttributeProvider $attributeAdapterProvider = null, - ResolverInterface $fieldNameResolver = null - ) { - $this->builder = $builder; - $this->attributeContainer = $attributeContainer; - $this->resourceIndex = $resourceIndex; - $this->fieldMapper = $fieldMapper; - $this->storeManager = $storeManager; - $this->dateFieldType = $dateFieldType; - $this->attributeAdapterProvider = $attributeAdapterProvider ?: ObjectManager::getInstance() - ->get(AttributeProvider::class); - $this->fieldNameResolver = $fieldNameResolver ?: ObjectManager::getInstance() - ->get(ResolverInterface::class); - - $this->mediaGalleryRoles = [ - self::MEDIA_ROLE_IMAGE, - self::MEDIA_ROLE_SMALL_IMAGE, - self::MEDIA_ROLE_THUMBNAIL, - self::MEDIA_ROLE_SWATCH_IMAGE - ]; - } - - /** - * Prepare index data for using in search engine metadata. - * - * @param int $productId - * @param array $indexData - * @param int $storeId - * @param array $context - * @return array|false - */ - public function map($productId, array $indexData, $storeId, $context = []) - { - $this->builder->addField('store_id', $storeId); - if (count($indexData)) { - $productIndexData = $this->resourceIndex->getFullProductIndexData($productId, $indexData); - } - - foreach ($productIndexData as $attributeCode => $value) { - // Prepare processing attribute info - if (strpos($attributeCode, '_value') !== false) { - $this->builder->addField($attributeCode, $value); - continue; - } - $attribute = $this->attributeContainer->getAttribute($attributeCode); - if (!$attribute || - in_array( - $attributeCode, - [ - 'price', - 'media_gallery', - 'tier_price', - 'quantity_and_stock_status', - 'media_gallery', - 'giftcard_amounts' - ] - ) - ) { - continue; - } - $attribute->setStoreId($storeId); - $value = $this->checkValue($value, $attribute, $storeId); - $this->builder->addField( - $this->fieldMapper->getFieldName( - $attributeCode, - $context - ), - $value - ); - } - $this->processAdvancedAttributes($productId, $productIndexData, $storeId); - - return $this->builder->build(); - } - - /** - * Process advanced attribute values - * - * @param int $productId - * @param array $productIndexData - * @param int $storeId - * @return void - */ - protected function processAdvancedAttributes($productId, array $productIndexData, $storeId) - { - $mediaGalleryRoles = array_fill_keys($this->mediaGalleryRoles, ''); - $productPriceIndexData = $this->attributeContainer->getAttribute('price') - ? $this->resourceIndex->getPriceIndexData([$productId], $storeId) - : []; - $productCategoryIndexData = $this->resourceIndex->getFullCategoryProductIndexData( - $storeId, - [$productId => $productId] - ); - foreach ($productIndexData as $attributeCode => $value) { - if (in_array($attributeCode, $this->mediaGalleryRoles)) { - $mediaGalleryRoles[$attributeCode] = $value; - } elseif ($attributeCode == 'tier_price') { - $this->builder->addFields($this->getProductTierPriceData($value)); - } elseif ($attributeCode == 'quantity_and_stock_status') { - $this->builder->addFields($this->getQtyAndStatus($value)); - } elseif ($attributeCode == 'media_gallery') { - $this->builder->addFields( - $this->getProductMediaGalleryData( - $value, - $mediaGalleryRoles - ) - ); - } - } - $this->builder->addFields($this->getProductPriceData($productId, $storeId, $productPriceIndexData)); - $this->builder->addFields($this->getProductCategoryData($productId, $productCategoryIndexData)); - } - - /** - * Check value. - * - * @param mixed $value - * @param Attribute $attribute - * @param string $storeId - * @return array|mixed|null|string - */ - protected function checkValue($value, $attribute, $storeId) - { - if (in_array($attribute->getBackendType(), ['datetime', 'timestamp']) - || $attribute->getFrontendInput() === 'date') { - return $this->dateFieldType->formatDate($storeId, $value); - } elseif ($attribute->getFrontendInput() === 'multiselect') { - return str_replace(',', ' ', $value); - } else { - return $value; - } - } - - /** - * Prepare tier price data for product - * - * @param array $data - * @return array - */ - protected function getProductTierPriceData($data) - { - $result = []; - if (!empty($data)) { - $i = 0; - foreach ($data as $tierPrice) { - $result['tier_price_id_' . $i] = $tierPrice['price_id']; - $result['tier_website_id_' . $i] = $tierPrice['website_id']; - $result['tier_all_groups_' . $i] = $tierPrice['all_groups']; - $result['tier_cust_group_' . $i] = $tierPrice['cust_group'] == GroupInterface::CUST_GROUP_ALL - ? '' : $tierPrice['cust_group']; - $result['tier_price_qty_' . $i] = $tierPrice['price_qty']; - $result['tier_website_price_' . $i] = $tierPrice['website_price']; - $result['tier_price_' . $i] = $tierPrice['price']; - $i++; - } - } - - return $result; - } - - /** - * Prepare media gallery data for product - * - * @param array $media - * @param array $roles - * @return array - */ - protected function getProductMediaGalleryData($media, $roles) - { - $result = []; - - if (!empty($media['images'])) { - $i = 0; - foreach ($media['images'] as $data) { - if ($data['media_type'] === 'image') { - $result['image_file_' . $i] = $data['file']; - $result['image_position_' . $i] = $data['position']; - $result['image_disabled_' . $i] = $data['disabled']; - $result['image_label_' . $i] = $data['label']; - $result['image_title_' . $i] = $data['label']; - $result['image_base_image_' . $i] = $this->getMediaRoleImage($data['file'], $roles); - $result['image_small_image_' . $i] = $this->getMediaRoleSmallImage($data['file'], $roles); - $result['image_thumbnail_' . $i] = $this->getMediaRoleThumbnail($data['file'], $roles); - $result['image_swatch_image_' . $i] = $this->getMediaRoleSwatchImage($data['file'], $roles); - } else { - $result['video_file_' . $i] = $data['file']; - $result['video_position_' . $i] = $data['position']; - $result['video_disabled_' . $i] = $data['disabled']; - $result['video_label_' . $i] = $data['label']; - $result['video_title_' . $i] = $data['video_title']; - $result['video_base_image_' . $i] = $this->getMediaRoleImage($data['file'], $roles); - $result['video_small_image_' . $i] = $this->getMediaRoleSmallImage($data['file'], $roles); - $result['video_thumbnail_' . $i] = $this->getMediaRoleThumbnail($data['file'], $roles); - $result['video_swatch_image_' . $i] = $this->getMediaRoleSwatchImage($data['file'], $roles); - $result['video_url_' . $i] = $data['video_url']; - $result['video_description_' . $i] = $data['video_description']; - $result['video_metadata_' . $i] = $data['video_metadata']; - $result['video_provider_' . $i] = $data['video_provider']; - } - $i++; - } - } - return $result; - } - - /** - * Get media role image. - * - * @param string $file - * @param array $roles - * @return string - */ - protected function getMediaRoleImage($file, $roles) - { - return $file == $roles[self::MEDIA_ROLE_IMAGE] ? '1' : '0'; - } - - /** - * Get media role small image. - * - * @param string $file - * @param array $roles - * @return string - */ - protected function getMediaRoleSmallImage($file, $roles) - { - return $file == $roles[self::MEDIA_ROLE_SMALL_IMAGE] ? '1' : '0'; - } - - /** - * Get media role thumbnail. - * - * @param string $file - * @param array $roles - * @return string - */ - protected function getMediaRoleThumbnail($file, $roles) - { - return $file == $roles[self::MEDIA_ROLE_THUMBNAIL] ? '1' : '0'; - } - - /** - * Get media role swatch image. - * - * @param string $file - * @param array $roles - * @return string - */ - protected function getMediaRoleSwatchImage($file, $roles) - { - return $file == $roles[self::MEDIA_ROLE_SWATCH_IMAGE] ? '1' : '0'; - } - - /** - * Prepare quantity and stock status for product - * - * @param array $data - * @return array - */ - protected function getQtyAndStatus($data) - { - $result = []; - if (!is_array($data)) { - $result['is_in_stock'] = $data ? 1 : 0; - $result['qty'] = $data; - } else { - $result['is_in_stock'] = $data['is_in_stock'] ? 1 : 0; - $result['qty'] = $data['qty']; - } - return $result; - } - - /** - * Prepare price index for product - * - * @param int $productId - * @param int $storeId - * @param array $priceIndexData - * @return array - */ - protected function getProductPriceData($productId, $storeId, array $priceIndexData) - { - $result = []; - if (array_key_exists($productId, $priceIndexData)) { - $productPriceIndexData = $priceIndexData[$productId]; - foreach ($productPriceIndexData as $customerGroupId => $price) { - $fieldName = $this->fieldMapper->getFieldName( - 'price', - ['customerGroupId' => $customerGroupId, 'websiteId' => $storeId] - ); - $result[$fieldName] = sprintf('%F', $price); - } - } - return $result; - } - - /** - * Prepare category index data for product - * - * @param int $productId - * @param array $categoryIndexData - * @return array - */ - protected function getProductCategoryData($productId, array $categoryIndexData) - { - $result = []; - $categoryIds = []; - - if (array_key_exists($productId, $categoryIndexData)) { - $indexData = $categoryIndexData[$productId]; - $result = $indexData; - } - - if (array_key_exists($productId, $categoryIndexData)) { - $indexData = $categoryIndexData[$productId]; - foreach ($indexData as $categoryData) { - $categoryIds[] = (int)$categoryData['id']; - } - if (count($categoryIds)) { - $result = ['category_ids' => implode(' ', $categoryIds)]; - $positionAttribute = $this->attributeAdapterProvider->getByAttributeCode('position'); - $categoryNameAttribute = $this->attributeAdapterProvider->getByAttributeCode('category_name'); - foreach ($indexData as $data) { - $categoryPositionKey = $this->fieldNameResolver->getFieldName( - $positionAttribute, - ['categoryId' => $data['id']] - ); - $categoryNameKey = $this->fieldNameResolver->getFieldName( - $categoryNameAttribute, - ['categoryId' => $data['id']] - ); - $result[$categoryPositionKey] = $data['position']; - $result[$categoryNameKey] = $data['name']; - } - } - } - return $result; - } -} diff --git a/app/code/Magento/Elasticsearch/Elasticsearch5/Model/Adapter/DataMapper/ProductDataMapperProxy.php b/app/code/Magento/Elasticsearch/Elasticsearch5/Model/Adapter/DataMapper/ProductDataMapperProxy.php deleted file mode 100644 index 0fc6100979f21..0000000000000 --- a/app/code/Magento/Elasticsearch/Elasticsearch5/Model/Adapter/DataMapper/ProductDataMapperProxy.php +++ /dev/null @@ -1,54 +0,0 @@ -<?php -/** - * Copyright © Magento, Inc. All rights reserved. - * See COPYING.txt for license details. - */ -namespace Magento\Elasticsearch\Elasticsearch5\Model\Adapter\DataMapper; - -use Magento\AdvancedSearch\Model\Client\ClientResolver; -use Magento\Elasticsearch\Model\Adapter\DataMapperInterface; - -/** - * Proxy for product data mappers - */ -class ProductDataMapperProxy implements DataMapperInterface -{ - /** - * @var ClientResolver - */ - private $clientResolver; - - /** - * @var DataMapperInterface[] - */ - private $dataMappers; - - /** - * CategoryFieldsProviderProxy constructor. - * @param ClientResolver $clientResolver - * @param DataMapperInterface[] $dataMappers - */ - public function __construct( - ClientResolver $clientResolver, - array $dataMappers - ) { - $this->clientResolver = $clientResolver; - $this->dataMappers = $dataMappers; - } - - /** - * @return DataMapperInterface - */ - private function getDataMapper() - { - return $this->dataMappers[$this->clientResolver->getCurrentEngine()]; - } - - /** - * @inheritdoc - */ - public function map($entityId, array $entityIndexData, $storeId, $context = []) - { - return $this->getDataMapper()->map($entityId, $entityIndexData, $storeId, $context); - } -} diff --git a/app/code/Magento/Elasticsearch/Elasticsearch5/Model/Adapter/FieldMapper/ProductFieldMapper.php b/app/code/Magento/Elasticsearch/Elasticsearch5/Model/Adapter/FieldMapper/ProductFieldMapper.php index 5aea87e5e6ae1..9a556460426f6 100644 --- a/app/code/Magento/Elasticsearch/Elasticsearch5/Model/Adapter/FieldMapper/ProductFieldMapper.php +++ b/app/code/Magento/Elasticsearch/Elasticsearch5/Model/Adapter/FieldMapper/ProductFieldMapper.php @@ -3,55 +3,20 @@ * Copyright © Magento, Inc. All rights reserved. * See COPYING.txt for license details. */ +declare(strict_types=1); namespace Magento\Elasticsearch\Elasticsearch5\Model\Adapter\FieldMapper; -use Magento\Eav\Model\Config; use Magento\Elasticsearch\Model\Adapter\FieldMapper\Product\AttributeProvider; +use Magento\Elasticsearch\Model\Adapter\FieldMapper\Product\FieldProvider\FieldName\ResolverInterface; use Magento\Elasticsearch\Model\Adapter\FieldMapper\Product\FieldProviderInterface; use Magento\Elasticsearch\Model\Adapter\FieldMapperInterface; -use Magento\Elasticsearch\Model\Adapter\FieldMapper\Product\FieldProvider\FieldName\ResolverInterface; -use Magento\Framework\App\ObjectManager; -use Magento\Elasticsearch\Elasticsearch5\Model\Adapter\FieldType; -use Magento\Framework\Registry; -use Magento\Store\Model\StoreManagerInterface as StoreManager; -use \Magento\Customer\Model\Session as CustomerSession; /** - * Class ProductFieldMapper + * Class ProductFieldMapper provides field name by attribute code and retrieve all attribute types */ class ProductFieldMapper implements FieldMapperInterface { - /** - * @deprecated - * @var Config - */ - protected $eavConfig; - - /** - * @deprecated - * @var FieldType - */ - protected $fieldType; - - /** - * @deprecated - * @var CustomerSession - */ - protected $customerSession; - - /** - * @deprecated - * @var StoreManager - */ - protected $storeManager; - - /** - * @deprecated - * @var Registry - */ - protected $coreRegistry; - /** * @var AttributeProvider */ @@ -68,36 +33,18 @@ class ProductFieldMapper implements FieldMapperInterface private $fieldProvider; /** - * @param Config $eavConfig - * @param FieldType $fieldType - * @param CustomerSession $customerSession - * @param StoreManager $storeManager - * @param Registry $coreRegistry - * @param ResolverInterface|null $fieldNameResolver - * @param AttributeProvider|null $attributeAdapterProvider - * @param FieldProviderInterface|null $fieldProvider + * @param ResolverInterface $fieldNameResolver + * @param AttributeProvider $attributeAdapterProvider + * @param FieldProviderInterface $fieldProvider */ public function __construct( - Config $eavConfig, - FieldType $fieldType, - CustomerSession $customerSession, - StoreManager $storeManager, - Registry $coreRegistry, - ResolverInterface $fieldNameResolver = null, - AttributeProvider $attributeAdapterProvider = null, - FieldProviderInterface $fieldProvider = null + ResolverInterface $fieldNameResolver, + AttributeProvider $attributeAdapterProvider, + FieldProviderInterface $fieldProvider ) { - $this->eavConfig = $eavConfig; - $this->fieldType = $fieldType; - $this->customerSession = $customerSession; - $this->storeManager = $storeManager; - $this->coreRegistry = $coreRegistry; - $this->fieldNameResolver = $fieldNameResolver ?: ObjectManager::getInstance() - ->get(ResolverInterface::class); - $this->attributeAdapterProvider = $attributeAdapterProvider ?: ObjectManager::getInstance() - ->get(AttributeProvider::class); - $this->fieldProvider = $fieldProvider ?: ObjectManager::getInstance() - ->get(FieldProviderInterface::class); + $this->fieldNameResolver = $fieldNameResolver; + $this->attributeAdapterProvider = $attributeAdapterProvider; + $this->fieldProvider = $fieldProvider; } /** diff --git a/app/code/Magento/Elasticsearch/Elasticsearch5/Model/Adapter/FieldType.php b/app/code/Magento/Elasticsearch/Elasticsearch5/Model/Adapter/FieldType.php deleted file mode 100644 index 6891b8c693624..0000000000000 --- a/app/code/Magento/Elasticsearch/Elasticsearch5/Model/Adapter/FieldType.php +++ /dev/null @@ -1,69 +0,0 @@ -<?php -/** - * Copyright © Magento, Inc. All rights reserved. - * See COPYING.txt for license details. - */ -namespace Magento\Elasticsearch\Elasticsearch5\Model\Adapter; - -use Magento\Eav\Model\Entity\Attribute\AbstractAttribute; -use Magento\Elasticsearch\Model\Adapter\FieldMapper\Product\FieldProvider\FieldType\ResolverInterface; - -/** - * Class FieldType - * - * @api - * @since 100.1.0 - * - * @deprecated This class provide not full data about field type. Only basic rules apply on this class. - * @see ResolverInterface - */ -class FieldType -{ - /**#@+ - * @deprecated - * @see \Magento\Elasticsearch\Model\Adapter\FieldMapper\Product\FieldProvider\FieldType\ConverterInterface - * - * Text flags for Elasticsearch field types - */ - const ES_DATA_TYPE_TEXT = 'text'; - const ES_DATA_TYPE_KEYWORD = 'keyword'; - const ES_DATA_TYPE_FLOAT = 'float'; - const ES_DATA_TYPE_INT = 'integer'; - const ES_DATA_TYPE_DATE = 'date'; - - /** @deprecated */ - const ES_DATA_TYPE_ARRAY = 'array'; - /**#@-*/ - - /** - * Get field type. - * - * @deprecated - * @see ResolverInterface::getFieldType - * - * @param AbstractAttribute $attribute - * @return string - * @since 100.1.0 - */ - public function getFieldType($attribute) - { - trigger_error('Class is deprecated', E_USER_DEPRECATED); - $backendType = $attribute->getBackendType(); - $frontendInput = $attribute->getFrontendInput(); - - if ($backendType === 'timestamp') { - $fieldType = self::ES_DATA_TYPE_DATE; - } elseif ((in_array($backendType, ['int', 'smallint'], true) - || (in_array($frontendInput, ['select', 'boolean'], true) && $backendType !== 'varchar')) - && !$attribute->getIsUserDefined() - ) { - $fieldType = self::ES_DATA_TYPE_INT; - } elseif ($backendType === 'decimal') { - $fieldType = self::ES_DATA_TYPE_FLOAT; - } else { - $fieldType = self::ES_DATA_TYPE_TEXT; - } - - return $fieldType; - } -} diff --git a/app/code/Magento/Elasticsearch/Elasticsearch5/Model/Client/Elasticsearch.php b/app/code/Magento/Elasticsearch/Elasticsearch5/Model/Client/Elasticsearch.php index b9102bc5e00c4..bd9a380230652 100644 --- a/app/code/Magento/Elasticsearch/Elasticsearch5/Model/Client/Elasticsearch.php +++ b/app/code/Magento/Elasticsearch/Elasticsearch5/Model/Client/Elasticsearch.php @@ -10,6 +10,8 @@ /** * Elasticsearch client + * + * @deprecated the Elasticsearch 5 doesn't supported due to EOL */ class Elasticsearch implements ClientInterface { @@ -285,7 +287,7 @@ public function addFieldsMapping(array $fields, $index, $entityType) 'match_mapping_type' => 'string', 'mapping' => [ 'type' => 'integer', - 'index' => false, + 'index' => true, ], ], ], @@ -296,7 +298,7 @@ public function addFieldsMapping(array $fields, $index, $entityType) 'mapping' => $this->prepareFieldInfo( [ 'type' => 'text', - 'index' => false, + 'index' => true, ] ), ], diff --git a/app/code/Magento/Elasticsearch/Elasticsearch5/SearchAdapter/Adapter.php b/app/code/Magento/Elasticsearch/Elasticsearch5/SearchAdapter/Adapter.php index 0ae347d5791ad..d77652c616c59 100644 --- a/app/code/Magento/Elasticsearch/Elasticsearch5/SearchAdapter/Adapter.php +++ b/app/code/Magento/Elasticsearch/Elasticsearch5/SearchAdapter/Adapter.php @@ -5,13 +5,13 @@ */ namespace Magento\Elasticsearch\Elasticsearch5\SearchAdapter; -use Magento\Framework\App\ObjectManager; +use Magento\Elasticsearch\SearchAdapter\Aggregation\Builder as AggregationBuilder; +use Magento\Elasticsearch\SearchAdapter\ConnectionManager; +use Magento\Elasticsearch\SearchAdapter\QueryContainerFactory; +use Magento\Elasticsearch\SearchAdapter\ResponseFactory; use Magento\Framework\Search\AdapterInterface; use Magento\Framework\Search\RequestInterface; use Magento\Framework\Search\Response\QueryResponse; -use Magento\Elasticsearch\SearchAdapter\Aggregation\Builder as AggregationBuilder; -use Magento\Elasticsearch\SearchAdapter\ConnectionManager; -use \Magento\Elasticsearch\SearchAdapter\ResponseFactory; use Psr\Log\LoggerInterface; /** @@ -24,27 +24,27 @@ class Adapter implements AdapterInterface * * @var Mapper */ - protected $mapper; + private $mapper; /** * Response Factory * * @var ResponseFactory */ - protected $responseFactory; + private $responseFactory; /** * @var ConnectionManager */ - protected $connectionManager; + private $connectionManager; /** * @var AggregationBuilder */ - protected $aggregationBuilder; + private $aggregationBuilder; /** - * @var \Magento\Elasticsearch\SearchAdapter\QueryContainerFactory + * @var QueryContainerFactory */ private $queryContainerFactory; @@ -54,19 +54,15 @@ class Adapter implements AdapterInterface * @var array */ private static $emptyRawResponse = [ - "hits" => - [ - "hits" => [] - ], - "aggregations" => - [ - "price_bucket" => [], - "category_bucket" => - [ - "buckets" => [] - - ] + 'hits' => [ + 'hits' => [] + ], + 'aggregations' => [ + 'price_bucket' => [], + 'category_bucket' => [ + 'buckets' => [] ] + ] ]; /** @@ -79,7 +75,7 @@ class Adapter implements AdapterInterface * @param Mapper $mapper * @param ResponseFactory $responseFactory * @param AggregationBuilder $aggregationBuilder - * @param \Magento\Elasticsearch\SearchAdapter\QueryContainerFactory $queryContainerFactory + * @param QueryContainerFactory $queryContainerFactory * @param LoggerInterface $logger */ public function __construct( @@ -87,16 +83,15 @@ public function __construct( Mapper $mapper, ResponseFactory $responseFactory, AggregationBuilder $aggregationBuilder, - \Magento\Elasticsearch\SearchAdapter\QueryContainerFactory $queryContainerFactory, - LoggerInterface $logger = null + QueryContainerFactory $queryContainerFactory, + LoggerInterface $logger ) { $this->connectionManager = $connectionManager; $this->mapper = $mapper; $this->responseFactory = $responseFactory; $this->aggregationBuilder = $aggregationBuilder; $this->queryContainerFactory = $queryContainerFactory; - $this->logger = $logger ?: ObjectManager::getInstance() - ->get(LoggerInterface::class); + $this->logger = $logger; } /** diff --git a/app/code/Magento/Elasticsearch/Elasticsearch5/SearchAdapter/Aggregation/Interval.php b/app/code/Magento/Elasticsearch/Elasticsearch5/SearchAdapter/Aggregation/Interval.php index a1fcbeb061481..c1170a14d6970 100644 --- a/app/code/Magento/Elasticsearch/Elasticsearch5/SearchAdapter/Aggregation/Interval.php +++ b/app/code/Magento/Elasticsearch/Elasticsearch5/SearchAdapter/Aggregation/Interval.php @@ -87,7 +87,7 @@ public function __construct( } /** - * {@inheritdoc} + * @inheritdoc */ public function load($limit, $offset = null, $lower = null, $upper = null) { @@ -116,7 +116,7 @@ public function load($limit, $offset = null, $lower = null, $upper = null) } /** - * {@inheritdoc} + * @inheritdoc */ public function loadPrevious($data, $index, $lower = null) { @@ -141,11 +141,15 @@ public function loadPrevious($data, $index, $lower = null) return false; } + if (is_array($offset)) { + $offset = $offset['value']; + } + return $this->load($index - $offset + 1, $offset - 1, $lower); } /** - * {@inheritdoc} + * @inheritdoc */ public function loadNext($data, $rightIndex, $upper = null) { @@ -166,6 +170,10 @@ public function loadNext($data, $rightIndex, $upper = null) return false; } + if (is_array($offset)) { + $offset = $offset['value']; + } + $from = ['gte' => $data - self::DELTA]; if ($upper !== null) { $to = ['lt' => $data - self::DELTA]; diff --git a/app/code/Magento/Elasticsearch/Elasticsearch5/SearchAdapter/Query/Builder.php b/app/code/Magento/Elasticsearch/Elasticsearch5/SearchAdapter/Query/Builder.php index 75c675663f03f..b75621191dae7 100644 --- a/app/code/Magento/Elasticsearch/Elasticsearch5/SearchAdapter/Query/Builder.php +++ b/app/code/Magento/Elasticsearch/Elasticsearch5/SearchAdapter/Query/Builder.php @@ -56,17 +56,20 @@ class Builder * @param SearchIndexNameResolver $searchIndexNameResolver * @param AggregationBuilder $aggregationBuilder * @param ScopeResolverInterface $scopeResolver + * @param Sort|null $sortBuilder */ public function __construct( Config $clientConfig, SearchIndexNameResolver $searchIndexNameResolver, AggregationBuilder $aggregationBuilder, - ScopeResolverInterface $scopeResolver + ScopeResolverInterface $scopeResolver, + ?Sort $sortBuilder = null ) { $this->clientConfig = $clientConfig; $this->searchIndexNameResolver = $searchIndexNameResolver; $this->aggregationBuilder = $aggregationBuilder; $this->scopeResolver = $scopeResolver; + $this->sortBuilder = $sortBuilder ?: ObjectManager::getInstance()->get(Sort::class); } /** @@ -88,7 +91,7 @@ public function initQuery(RequestInterface $request) 'from' => $request->getFrom(), 'size' => $request->getSize(), 'stored_fields' => ['_id', '_score'], - 'sort' => $this->getSortBuilder()->getSort($request), + 'sort' => $this->sortBuilder->getSort($request), 'query' => [], ], ]; @@ -109,17 +112,4 @@ public function initAggregations( ) { return $this->aggregationBuilder->build($request, $searchQuery); } - - /** - * Get sort builder instance. - * - * @return Sort - */ - private function getSortBuilder() - { - if (null === $this->sortBuilder) { - $this->sortBuilder = ObjectManager::getInstance()->get(Sort::class); - } - return $this->sortBuilder; - } } diff --git a/app/code/Magento/Elasticsearch/Model/Adapter/BatchDataMapper/CategoryFieldsProvider.php b/app/code/Magento/Elasticsearch/Model/Adapter/BatchDataMapper/CategoryFieldsProvider.php deleted file mode 100644 index 0e130c24e79d3..0000000000000 --- a/app/code/Magento/Elasticsearch/Model/Adapter/BatchDataMapper/CategoryFieldsProvider.php +++ /dev/null @@ -1,103 +0,0 @@ -<?php -/** - * Copyright © Magento, Inc. All rights reserved. - * See COPYING.txt for license details. - */ - -namespace Magento\Elasticsearch\Model\Adapter\BatchDataMapper; - -use Magento\Elasticsearch\Model\ResourceModel\Index; -use Magento\AdvancedSearch\Model\Adapter\DataMapper\AdditionalFieldsProviderInterface; -use Magento\Elasticsearch\Model\Adapter\FieldMapper\Product\AttributeProvider; -use Magento\Framework\App\ObjectManager; -use Magento\Elasticsearch\Model\Adapter\FieldMapper\Product\FieldProvider\FieldName\ResolverInterface; - -/** - * Provide data mapping for categories fields - */ -class CategoryFieldsProvider implements AdditionalFieldsProviderInterface -{ - /** - * @var Index - */ - private $resourceIndex; - - /** - * @var AttributeProvider - */ - private $attributeAdapterProvider; - - /** - * @var ResolverInterface - */ - private $fieldNameResolver; - - /** - * @param Index $resourceIndex - * @param AttributeProvider|null $attributeAdapterProvider - * @param ResolverInterface|null $fieldNameResolver - */ - public function __construct( - Index $resourceIndex, - AttributeProvider $attributeAdapterProvider = null, - ResolverInterface $fieldNameResolver = null - ) { - $this->resourceIndex = $resourceIndex; - $this->attributeAdapterProvider = $attributeAdapterProvider ?: ObjectManager::getInstance() - ->get(AttributeProvider::class); - $this->fieldNameResolver = $fieldNameResolver ?: ObjectManager::getInstance() - ->get(ResolverInterface::class); - } - - /** - * @inheritdoc - */ - public function getFields(array $productIds, $storeId) - { - $categoryData = $this->resourceIndex->getFullCategoryProductIndexData($storeId, $productIds); - - $fields = []; - foreach ($productIds as $productId) { - $fields[$productId] = $this->getProductCategoryData($productId, $categoryData); - } - - return $fields; - } - - /** - * Prepare category index data for product - * - * @param int $productId - * @param array $categoryIndexData - * @return array - */ - private function getProductCategoryData($productId, array $categoryIndexData) - { - $result = []; - - if (array_key_exists($productId, $categoryIndexData)) { - $indexData = $categoryIndexData[$productId]; - $categoryIds = array_column($indexData, 'id'); - - if (count($categoryIds)) { - $result = ['category_ids' => implode(' ', $categoryIds)]; - $positionAttribute = $this->attributeAdapterProvider->getByAttributeCode('position'); - $categoryNameAttribute = $this->attributeAdapterProvider->getByAttributeCode('category_name'); - foreach ($indexData as $data) { - $categoryPositionKey = $this->fieldNameResolver->getFieldName( - $positionAttribute, - ['categoryId' => $data['id']] - ); - $categoryNameKey = $this->fieldNameResolver->getFieldName( - $categoryNameAttribute, - ['categoryId' => $data['id']] - ); - $result[$categoryPositionKey] = $data['position']; - $result[$categoryNameKey] = $data['name']; - } - } - } - - return $result; - } -} diff --git a/app/code/Magento/Elasticsearch/Model/Adapter/BatchDataMapper/DataMapperFactory.php b/app/code/Magento/Elasticsearch/Model/Adapter/BatchDataMapper/DataMapperFactory.php index 29bdb036e206d..212c0f7b7af9b 100644 --- a/app/code/Magento/Elasticsearch/Model/Adapter/BatchDataMapper/DataMapperFactory.php +++ b/app/code/Magento/Elasticsearch/Model/Adapter/BatchDataMapper/DataMapperFactory.php @@ -11,7 +11,7 @@ use Magento\Elasticsearch\Model\Adapter\BatchDataMapperInterface; /** - * Data mapper factory + * Data mapper factory uses to create appropriate mapper class */ class DataMapperFactory { diff --git a/app/code/Magento/Elasticsearch/Model/Adapter/BatchDataMapper/DataMapperResolver.php b/app/code/Magento/Elasticsearch/Model/Adapter/BatchDataMapper/DataMapperResolver.php index fd7a64eb0c9b7..b0a5b805e387f 100644 --- a/app/code/Magento/Elasticsearch/Model/Adapter/BatchDataMapper/DataMapperResolver.php +++ b/app/code/Magento/Elasticsearch/Model/Adapter/BatchDataMapper/DataMapperResolver.php @@ -34,7 +34,7 @@ public function __construct(DataMapperFactory $dataMapperFactory) } /** - * {@inheritdoc} + * @inheritdoc */ public function map(array $documentData, $storeId, array $context = []) { diff --git a/app/code/Magento/Elasticsearch/Model/Adapter/BatchDataMapper/PriceFieldsProvider.php b/app/code/Magento/Elasticsearch/Model/Adapter/BatchDataMapper/PriceFieldsProvider.php index 56c84593256be..33e0993daba8d 100644 --- a/app/code/Magento/Elasticsearch/Model/Adapter/BatchDataMapper/PriceFieldsProvider.php +++ b/app/code/Magento/Elasticsearch/Model/Adapter/BatchDataMapper/PriceFieldsProvider.php @@ -3,16 +3,16 @@ * Copyright © Magento, Inc. All rights reserved. * See COPYING.txt for license details. */ +declare(strict_types=1); namespace Magento\Elasticsearch\Model\Adapter\BatchDataMapper; -use Magento\Elasticsearch\Model\ResourceModel\Index; -use Magento\Store\Model\StoreManagerInterface; use Magento\AdvancedSearch\Model\Adapter\DataMapper\AdditionalFieldsProviderInterface; use Magento\CatalogSearch\Model\Indexer\Fulltext\Action\DataProvider; use Magento\Elasticsearch\Model\Adapter\FieldMapper\Product\AttributeProvider; -use Magento\Framework\App\ObjectManager; use Magento\Elasticsearch\Model\Adapter\FieldMapper\Product\FieldProvider\FieldName\ResolverInterface; +use Magento\Elasticsearch\Model\ResourceModel\Index; +use Magento\Store\Model\StoreManagerInterface; /** * Provide data mapping for price fields @@ -48,23 +48,21 @@ class PriceFieldsProvider implements AdditionalFieldsProviderInterface * @param Index $resourceIndex * @param DataProvider $dataProvider * @param StoreManagerInterface $storeManager - * @param AttributeProvider|null $attributeAdapterProvider - * @param ResolverInterface|null $fieldNameResolver + * @param AttributeProvider $attributeAdapterProvider + * @param ResolverInterface $fieldNameResolver */ public function __construct( Index $resourceIndex, DataProvider $dataProvider, StoreManagerInterface $storeManager, - AttributeProvider $attributeAdapterProvider = null, - ResolverInterface $fieldNameResolver = null + AttributeProvider $attributeAdapterProvider, + ResolverInterface $fieldNameResolver ) { $this->resourceIndex = $resourceIndex; $this->dataProvider = $dataProvider; $this->storeManager = $storeManager; - $this->attributeAdapterProvider = $attributeAdapterProvider ?: ObjectManager::getInstance() - ->get(AttributeProvider::class); - $this->fieldNameResolver = $fieldNameResolver ?: ObjectManager::getInstance() - ->get(ResolverInterface::class); + $this->attributeAdapterProvider = $attributeAdapterProvider; + $this->fieldNameResolver = $fieldNameResolver; } /** @@ -73,7 +71,7 @@ public function __construct( public function getFields(array $productIds, $storeId) { $websiteId = $this->storeManager->getStore($storeId)->getWebsiteId(); - + $priceData = $this->dataProvider->getSearchableAttribute('price') ? $this->resourceIndex->getPriceIndexData($productIds, $storeId) : []; diff --git a/app/code/Magento/Elasticsearch/Model/Adapter/BatchDataMapper/ProductDataMapper.php b/app/code/Magento/Elasticsearch/Model/Adapter/BatchDataMapper/ProductDataMapper.php index 270ca37e2d42c..abdb064506641 100644 --- a/app/code/Magento/Elasticsearch/Model/Adapter/BatchDataMapper/ProductDataMapper.php +++ b/app/code/Magento/Elasticsearch/Model/Adapter/BatchDataMapper/ProductDataMapper.php @@ -77,6 +77,13 @@ class ProductDataMapper implements BatchDataMapperInterface 'tax_class_id' ]; + /** + * @var string[] + */ + private $sortableAttributesValuesToImplode = [ + 'name', + ]; + /** * Construction for DocumentDataMapper * @@ -86,6 +93,7 @@ class ProductDataMapper implements BatchDataMapperInterface * @param AdditionalFieldsProviderInterface $additionalFieldsProvider * @param DataProvider $dataProvider * @param array $excludedAttributes + * @param array $sortableAttributesValuesToImplode */ public function __construct( Builder $builder, @@ -93,12 +101,17 @@ public function __construct( DateFieldType $dateFieldType, AdditionalFieldsProviderInterface $additionalFieldsProvider, DataProvider $dataProvider, - array $excludedAttributes = [] + array $excludedAttributes = [], + array $sortableAttributesValuesToImplode = [] ) { $this->builder = $builder; $this->fieldMapper = $fieldMapper; $this->dateFieldType = $dateFieldType; $this->excludedAttributes = array_merge($this->defaultExcludedAttributes, $excludedAttributes); + $this->sortableAttributesValuesToImplode = array_merge( + $this->sortableAttributesValuesToImplode, + $sortableAttributesValuesToImplode + ); $this->additionalFieldsProvider = $additionalFieldsProvider; $this->dataProvider = $dataProvider; $this->attributeOptionsCache = []; @@ -241,6 +254,13 @@ private function prepareAttributeValues( } } + if ($attribute->getUsedForSortBy() + && in_array($attribute->getAttributeCode(), $this->sortableAttributesValuesToImplode) + && count($attributeValues) > 1 + ) { + $attributeValues = [$productId => implode(' ', $attributeValues)]; + } + return $attributeValues; } @@ -252,9 +272,14 @@ private function prepareAttributeValues( */ private function prepareMultiselectValues(array $values): array { - return \array_merge(...\array_map(function (string $value) { - return \explode(',', $value); - }, $values)); + return \array_merge( + ...\array_map( + function (string $value) { + return \explode(',', $value); + }, + $values + ) + ); } /** @@ -285,9 +310,9 @@ private function getValuesLabels(Attribute $attribute, array $attributeValues): return $attributeLabels; } - foreach ($options as $option) { - if (\in_array($option->getValue(), $attributeValues)) { - $attributeLabels[] = $option->getLabel(); + foreach ($attributeValues as $attributeValue) { + if (isset($options[$attributeValue])) { + $attributeLabels[] = $options[$attributeValue]->getLabel(); } } @@ -304,7 +329,11 @@ private function getAttributeOptions(Attribute $attribute): array { if (!isset($this->attributeOptionsCache[$attribute->getId()])) { $options = $attribute->getOptions() ?? []; - $this->attributeOptionsCache[$attribute->getId()] = $options; + $optionsByValue = []; + foreach ($options as $option) { + $optionsByValue[$option->getValue()] = $option; + } + $this->attributeOptionsCache[$attribute->getId()] = $optionsByValue; } return $this->attributeOptionsCache[$attribute->getId()]; diff --git a/app/code/Magento/Elasticsearch/Model/Adapter/Container/Attribute.php b/app/code/Magento/Elasticsearch/Model/Adapter/Container/Attribute.php deleted file mode 100644 index 12ca1e1a741ff..0000000000000 --- a/app/code/Magento/Elasticsearch/Model/Adapter/Container/Attribute.php +++ /dev/null @@ -1,99 +0,0 @@ -<?php -/** - * Copyright © Magento, Inc. All rights reserved. - * See COPYING.txt for license details. - */ -namespace Magento\Elasticsearch\Model\Adapter\Container; - -use Magento\Catalog\Model\ResourceModel\Product\Attribute\Collection; -use Magento\Catalog\Model\ResourceModel\Eav\Attribute as EavAttribute; - -/** - * @deprecated 100.2.0 - * This class is used only in deprecated \Magento\Elasticsearch\Model\Adapter\DataMapper\ProductDataMapper - * and must not be used for new code - */ -class Attribute -{ - /** - * @var string[] - */ - private $idToCodeMap = []; - - /** - * @var Collection - */ - private $attributeCollection; - - /** - * @var EavAttribute[] - */ - private $attributes = []; - - /** - * @param Collection $attributeCollection - */ - public function __construct(Collection $attributeCollection) - { - $this->attributeCollection = $attributeCollection; - } - - /** - * @param int $attributeId - * @return string - */ - public function getAttributeCodeById($attributeId) - { - if (!array_key_exists($attributeId, $this->idToCodeMap)) { - $code = $attributeId === 'options' - ? 'options' - : $this->attributeCollection->getItemById($attributeId)->getAttributeCode(); - $this->idToCodeMap[$attributeId] = $code; - } - return $this->idToCodeMap[$attributeId]; - } - - /** - * @param string $attributeCode - * @return int - */ - public function getAttributeIdByCode($attributeCode) - { - if (!array_key_exists($attributeCode, array_flip($this->idToCodeMap))) { - $attributeId = $attributeCode === 'options' - ? 'options' - : $this->attributeCollection->getItemByColumnValue('attribute_code', $attributeCode)->getId(); - $this->idToCodeMap[$attributeId] = $attributeCode; - } - $codeToIdMap = array_flip($this->idToCodeMap); - return $codeToIdMap[$attributeCode]; - } - - /** - * @param string $attributeCode - * @return EavAttribute|null - */ - public function getAttribute($attributeCode) - { - $searchableAttributes = $this->getAttributes(); - return array_key_exists($attributeCode, $searchableAttributes) - ? $searchableAttributes[$attributeCode] - : null; - } - - /** - * @return EavAttribute[] - */ - public function getAttributes() - { - if (0 === count($this->attributes)) { - /** @var Collection $attributesCollection */ - $attributesCollection = $this->attributeCollection; - foreach ($attributesCollection as $attribute) { - /** @var EavAttribute $attribute */ - $this->attributes[$attribute->getAttributeCode()] = $attribute; - } - } - return $this->attributes; - } -} diff --git a/app/code/Magento/Elasticsearch/Model/Adapter/DataMapper/DataMapperResolver.php b/app/code/Magento/Elasticsearch/Model/Adapter/DataMapper/DataMapperResolver.php deleted file mode 100644 index dbb37f3363b3d..0000000000000 --- a/app/code/Magento/Elasticsearch/Model/Adapter/DataMapper/DataMapperResolver.php +++ /dev/null @@ -1,92 +0,0 @@ -<?php -/** - * Copyright © Magento, Inc. All rights reserved. - * See COPYING.txt for license details. - */ -namespace Magento\Elasticsearch\Model\Adapter\DataMapper; - -use Magento\Framework\ObjectManagerInterface; -use Magento\Elasticsearch\Model\Adapter\DataMapperInterface; -use Magento\Elasticsearch\Model\Config; - -/** - * @deprecated 100.2.0 - * @see \Magento\Elasticsearch\Model\Adapter\BatchDataMapperInterface - */ -class DataMapperResolver implements DataMapperInterface -{ - /** - * Object Manager instance - * - * @var ObjectManagerInterface - */ - private $objectManager; - - /** - * @var string[] - */ - private $dataMappers; - - /** - * Data Mapper instance - * - * @var DataMapperInterface - */ - private $dataMapperEntity; - - /** - * @param ObjectManagerInterface $objectManager - * @param string[] $dataMappers - */ - public function __construct( - ObjectManagerInterface $objectManager, - array $dataMappers = [] - ) { - $this->objectManager = $objectManager; - $this->dataMappers = $dataMappers; - } - - /** - * {@inheritdoc} - */ - public function map( - $entityId, - array $entityIndexData, - $storeId, - $context = [] - ) { - $entityType = isset($context['entityType']) ? $context['entityType'] : Config::ELASTICSEARCH_TYPE_DEFAULT; - return $this->getEntity($entityType)->map($entityId, $entityIndexData, $storeId, $context); - } - - /** - * Get instance of current data mapper - * - * @param string $entityType - * @return DataMapperInterface - * @throws \Exception - */ - private function getEntity($entityType = '') - { - if (empty($this->dataMapperEntity)) { - if (empty($entityType)) { - throw new \Exception( - 'No entity type given' - ); - } - if (!isset($this->dataMappers[$entityType])) { - throw new \LogicException( - 'There is no such data mapper: ' . $entityType - ); - } - $dataMapperClass = $this->dataMappers[$entityType]; - $this->dataMapperEntity = $this->objectManager->create($dataMapperClass); - if (!($this->dataMapperEntity instanceof DataMapperInterface)) { - throw new \InvalidArgumentException( - 'Data mapper must implement \Magento\Elasticsearch\Model\Adapter\DataMapperInterface' - ); - } - } - return $this->dataMapperEntity; - } -} diff --git a/app/code/Magento/Elasticsearch/Model/Adapter/DataMapper/ProductDataMapper.php b/app/code/Magento/Elasticsearch/Model/Adapter/DataMapper/ProductDataMapper.php deleted file mode 100644 index 24b740b554fcb..0000000000000 --- a/app/code/Magento/Elasticsearch/Model/Adapter/DataMapper/ProductDataMapper.php +++ /dev/null @@ -1,19 +0,0 @@ -<?php -/** - * Copyright © Magento, Inc. All rights reserved. - * See COPYING.txt for license details. - */ -namespace Magento\Elasticsearch\Model\Adapter\DataMapper; - -use Magento\Elasticsearch\Model\Adapter\DataMapperInterface; -use Magento\Elasticsearch\Elasticsearch5\Model\Adapter\DataMapper\ProductDataMapper as ElasticSearch5ProductDataMapper; - -/** - * Don't use this product data mapper class. - * - * @deprecated 100.2.0 - * @see \Magento\Elasticsearch\Model\Adapter\BatchDataMapperInterface - */ -class ProductDataMapper extends ElasticSearch5ProductDataMapper implements DataMapperInterface -{ -} diff --git a/app/code/Magento/Elasticsearch/Model/Adapter/DataMapperInterface.php b/app/code/Magento/Elasticsearch/Model/Adapter/DataMapperInterface.php deleted file mode 100644 index e43a4da065314..0000000000000 --- a/app/code/Magento/Elasticsearch/Model/Adapter/DataMapperInterface.php +++ /dev/null @@ -1,24 +0,0 @@ -<?php -/** - * Copyright © Magento, Inc. All rights reserved. - * See COPYING.txt for license details. - */ -namespace Magento\Elasticsearch\Model\Adapter; - -/** - * @deprecated 100.2.0 - * @see \Magento\Elasticsearch\Model\Adapter\BatchDataMapperInterface - */ -interface DataMapperInterface -{ - /** - * Prepare index data for using in search engine metadata - * - * @param int $entityId - * @param array $entityIndexData - * @param int $storeId - * @param array $context - * @return array - */ - public function map($entityId, array $entityIndexData, $storeId, $context = []); -} diff --git a/app/code/Magento/Elasticsearch/Model/Adapter/Elasticsearch.php b/app/code/Magento/Elasticsearch/Model/Adapter/Elasticsearch.php index fa193d86c03c7..5ab6669a34cc4 100644 --- a/app/code/Magento/Elasticsearch/Model/Adapter/Elasticsearch.php +++ b/app/code/Magento/Elasticsearch/Model/Adapter/Elasticsearch.php @@ -6,8 +6,6 @@ namespace Magento\Elasticsearch\Model\Adapter; -use Magento\Framework\App\ObjectManager; - /** * Elasticsearch adapter * @SuppressWarnings(PHPMD.CouplingBetweenObjects) @@ -31,12 +29,6 @@ class Elasticsearch /**#@-*/ protected $connectionManager; - /** - * @var DataMapperInterface - * @deprecated 100.2.0 Will be replaced with BatchDataMapperInterface - */ - protected $documentDataMapper; - /** * @var \Magento\Elasticsearch\Model\Adapter\Index\IndexNameResolver */ @@ -53,7 +45,7 @@ class Elasticsearch protected $clientConfig; /** - * @var \Magento\Elasticsearch\Model\Client\Elasticsearch + * @var \Magento\AdvancedSearch\Model\Client\ClientInterface */ protected $client; @@ -78,39 +70,33 @@ class Elasticsearch private $batchDocumentDataMapper; /** - * Constructor for Elasticsearch adapter. - * * @param \Magento\Elasticsearch\SearchAdapter\ConnectionManager $connectionManager - * @param DataMapperInterface $documentDataMapper * @param FieldMapperInterface $fieldMapper * @param \Magento\Elasticsearch\Model\Config $clientConfig - * @param \Magento\Elasticsearch\Model\Adapter\Index\BuilderInterface $indexBuilder + * @param Index\BuilderInterface $indexBuilder * @param \Psr\Log\LoggerInterface $logger - * @param \Magento\Elasticsearch\Model\Adapter\Index\IndexNameResolver $indexNameResolver - * @param array $options + * @param Index\IndexNameResolver $indexNameResolver * @param BatchDataMapperInterface $batchDocumentDataMapper + * @param array $options * @throws \Magento\Framework\Exception\LocalizedException */ public function __construct( \Magento\Elasticsearch\SearchAdapter\ConnectionManager $connectionManager, - DataMapperInterface $documentDataMapper, FieldMapperInterface $fieldMapper, \Magento\Elasticsearch\Model\Config $clientConfig, \Magento\Elasticsearch\Model\Adapter\Index\BuilderInterface $indexBuilder, \Psr\Log\LoggerInterface $logger, \Magento\Elasticsearch\Model\Adapter\Index\IndexNameResolver $indexNameResolver, - $options = [], - BatchDataMapperInterface $batchDocumentDataMapper = null + BatchDataMapperInterface $batchDocumentDataMapper, + $options = [] ) { $this->connectionManager = $connectionManager; - $this->documentDataMapper = $documentDataMapper; $this->fieldMapper = $fieldMapper; $this->clientConfig = $clientConfig; $this->indexBuilder = $indexBuilder; $this->logger = $logger; $this->indexNameResolver = $indexNameResolver; - $this->batchDocumentDataMapper = $batchDocumentDataMapper ?: - ObjectManager::getInstance()->get(BatchDataMapperInterface::class); + $this->batchDocumentDataMapper = $batchDocumentDataMapper; try { $this->client = $this->connectionManager->getConnection($options); diff --git a/app/code/Magento/Elasticsearch6/Model/Adapter/FieldMapper/AddDefaultSearchField.php b/app/code/Magento/Elasticsearch/Model/Adapter/FieldMapper/AddDefaultSearchField.php similarity index 84% rename from app/code/Magento/Elasticsearch6/Model/Adapter/FieldMapper/AddDefaultSearchField.php rename to app/code/Magento/Elasticsearch/Model/Adapter/FieldMapper/AddDefaultSearchField.php index 27767f6567d96..472a0e490d024 100644 --- a/app/code/Magento/Elasticsearch6/Model/Adapter/FieldMapper/AddDefaultSearchField.php +++ b/app/code/Magento/Elasticsearch/Model/Adapter/FieldMapper/AddDefaultSearchField.php @@ -5,7 +5,7 @@ */ declare(strict_types=1); -namespace Magento\Elasticsearch6\Model\Adapter\FieldMapper; +namespace Magento\Elasticsearch\Model\Adapter\FieldMapper; use Magento\Elasticsearch\Model\Adapter\FieldsMappingPreprocessorInterface; @@ -21,7 +21,7 @@ class AddDefaultSearchField implements FieldsMappingPreprocessorInterface /** * Add default search field (catch all field) to the fields. * - * Emulates catch all field (_all) for elasticsearch version 6.0+ + * Emulates catch all field (_all) for elasticsearch * * @param array $mapping * @return array diff --git a/app/code/Magento/Elasticsearch6/Model/Adapter/FieldMapper/CopySearchableFieldsToSearchField.php b/app/code/Magento/Elasticsearch/Model/Adapter/FieldMapper/CopySearchableFieldsToSearchField.php similarity index 91% rename from app/code/Magento/Elasticsearch6/Model/Adapter/FieldMapper/CopySearchableFieldsToSearchField.php rename to app/code/Magento/Elasticsearch/Model/Adapter/FieldMapper/CopySearchableFieldsToSearchField.php index 6179eacba5ad7..fc653ec953dc7 100644 --- a/app/code/Magento/Elasticsearch6/Model/Adapter/FieldMapper/CopySearchableFieldsToSearchField.php +++ b/app/code/Magento/Elasticsearch/Model/Adapter/FieldMapper/CopySearchableFieldsToSearchField.php @@ -5,7 +5,7 @@ */ declare(strict_types=1); -namespace Magento\Elasticsearch6\Model\Adapter\FieldMapper; +namespace Magento\Elasticsearch\Model\Adapter\FieldMapper; use Magento\Elasticsearch\Model\Adapter\FieldsMappingPreprocessorInterface; @@ -21,7 +21,7 @@ class CopySearchableFieldsToSearchField implements FieldsMappingPreprocessorInte /** * Add "copy_to" parameter for default search field to index fields. * - * Emulates catch all field (_all) for elasticsearch version 6.0+ + * Emulates catch all field (_all) for elasticsearch * * @param array $mapping * @return array diff --git a/app/code/Magento/Elasticsearch/Model/Adapter/FieldMapper/Product/CompositeFieldProvider.php b/app/code/Magento/Elasticsearch/Model/Adapter/FieldMapper/Product/CompositeFieldProvider.php index 8038c8c05bc1c..b276b67ff7fba 100644 --- a/app/code/Magento/Elasticsearch/Model/Adapter/FieldMapper/Product/CompositeFieldProvider.php +++ b/app/code/Magento/Elasticsearch/Model/Adapter/FieldMapper/Product/CompositeFieldProvider.php @@ -40,12 +40,12 @@ public function __construct(array $providers) */ public function getFields(array $context = []): array { - $allAttributes = []; + $allAttributes = [[]]; foreach ($this->providers as $provider) { - $allAttributes = array_merge($allAttributes, $provider->getFields($context)); + $allAttributes[] = $provider->getFields($context); } - return $allAttributes; + return array_merge(...$allAttributes); } } diff --git a/app/code/Magento/Elasticsearch/Model/Adapter/FieldMapper/Product/FieldProvider/DynamicField.php b/app/code/Magento/Elasticsearch/Model/Adapter/FieldMapper/Product/FieldProvider/DynamicField.php index 76bc7a15e47a7..12ef730df7b3f 100644 --- a/app/code/Magento/Elasticsearch/Model/Adapter/FieldMapper/Product/FieldProvider/DynamicField.php +++ b/app/code/Magento/Elasticsearch/Model/Adapter/FieldMapper/Product/FieldProvider/DynamicField.php @@ -7,33 +7,23 @@ namespace Magento\Elasticsearch\Model\Adapter\FieldMapper\Product\FieldProvider; -use Magento\Catalog\Api\CategoryListInterface; use Magento\Customer\Api\GroupRepositoryInterface; use Magento\Elasticsearch\Model\Adapter\FieldMapper\Product\AttributeProvider; -use Magento\Elasticsearch\Model\Adapter\FieldMapper\Product\FieldProviderInterface; -use Magento\Elasticsearch\Model\Adapter\FieldMapper\Product\FieldProvider\FieldType\ConverterInterface - as FieldTypeConverterInterface; use Magento\Elasticsearch\Model\Adapter\FieldMapper\Product\FieldProvider\FieldIndex\ConverterInterface as IndexTypeConverterInterface; use Magento\Elasticsearch\Model\Adapter\FieldMapper\Product\FieldProvider\FieldName\ResolverInterface as FieldNameResolver; +use Magento\Elasticsearch\Model\Adapter\FieldMapper\Product\FieldProvider\FieldType\ConverterInterface + as FieldTypeConverterInterface; +use Magento\Elasticsearch\Model\Adapter\FieldMapper\Product\FieldProviderInterface; use Magento\Framework\Api\SearchCriteriaBuilder; use Magento\Catalog\Model\ResourceModel\Category\Collection; -use Magento\Framework\App\ObjectManager; /** * Provide dynamic fields for product. */ class DynamicField implements FieldProviderInterface { - /** - * Category list. - * - * @deprecated - * @var CategoryListInterface - */ - private $categoryList; - /** * Category collection. * @@ -80,30 +70,26 @@ class DynamicField implements FieldProviderInterface * @param IndexTypeConverterInterface $indexTypeConverter * @param GroupRepositoryInterface $groupRepository * @param SearchCriteriaBuilder $searchCriteriaBuilder - * @param CategoryListInterface $categoryList * @param FieldNameResolver $fieldNameResolver * @param AttributeProvider $attributeAdapterProvider - * @param Collection|null $categoryCollection + * @param Collection $categoryCollection */ public function __construct( FieldTypeConverterInterface $fieldTypeConverter, IndexTypeConverterInterface $indexTypeConverter, GroupRepositoryInterface $groupRepository, SearchCriteriaBuilder $searchCriteriaBuilder, - CategoryListInterface $categoryList, FieldNameResolver $fieldNameResolver, AttributeProvider $attributeAdapterProvider, - Collection $categoryCollection = null + Collection $categoryCollection ) { $this->groupRepository = $groupRepository; $this->searchCriteriaBuilder = $searchCriteriaBuilder; $this->fieldTypeConverter = $fieldTypeConverter; $this->indexTypeConverter = $indexTypeConverter; - $this->categoryList = $categoryList; $this->fieldNameResolver = $fieldNameResolver; $this->attributeAdapterProvider = $attributeAdapterProvider; - $this->categoryCollection = $categoryCollection ?: - ObjectManager::getInstance()->get(Collection::class); + $this->categoryCollection = $categoryCollection; } /** diff --git a/app/code/Magento/Elasticsearch/Model/Adapter/FieldMapper/Product/FieldProvider/FieldName/Resolver/CategoryName.php b/app/code/Magento/Elasticsearch/Model/Adapter/FieldMapper/Product/FieldProvider/FieldName/Resolver/CategoryName.php index 5824aca6cdd54..03240034b959d 100644 --- a/app/code/Magento/Elasticsearch/Model/Adapter/FieldMapper/Product/FieldProvider/FieldName/Resolver/CategoryName.php +++ b/app/code/Magento/Elasticsearch/Model/Adapter/FieldMapper/Product/FieldProvider/FieldName/Resolver/CategoryName.php @@ -8,10 +8,9 @@ namespace Magento\Elasticsearch\Model\Adapter\FieldMapper\Product\FieldProvider\FieldName\Resolver; use Magento\Elasticsearch\Model\Adapter\FieldMapper\Product\AttributeAdapter; +use Magento\Elasticsearch\Model\Adapter\FieldMapper\Product\FieldProvider\FieldName\ResolverInterface; use Magento\Framework\Registry; use Magento\Store\Model\StoreManagerInterface as StoreManager; -use Magento\Elasticsearch\Model\Adapter\FieldMapper\Product\FieldProvider\FieldName\ResolverInterface; -use Magento\Framework\App\ObjectManager; /** * Resolver field name for Category name attribute. @@ -33,13 +32,11 @@ class CategoryName implements ResolverInterface * @param Registry $coreRegistry */ public function __construct( - StoreManager $storeManager = null, - Registry $coreRegistry = null + StoreManager $storeManager, + Registry $coreRegistry ) { - $this->storeManager = $storeManager ?: ObjectManager::getInstance() - ->get(StoreManager::class); - $this->coreRegistry = $coreRegistry ?: ObjectManager::getInstance() - ->get(Registry::class); + $this->storeManager = $storeManager; + $this->coreRegistry = $coreRegistry; } /** diff --git a/app/code/Magento/Elasticsearch/Model/Adapter/FieldMapper/Product/FieldProvider/FieldName/Resolver/Position.php b/app/code/Magento/Elasticsearch/Model/Adapter/FieldMapper/Product/FieldProvider/FieldName/Resolver/Position.php index 044d5d8da9a6c..9f91e82275434 100644 --- a/app/code/Magento/Elasticsearch/Model/Adapter/FieldMapper/Product/FieldProvider/FieldName/Resolver/Position.php +++ b/app/code/Magento/Elasticsearch/Model/Adapter/FieldMapper/Product/FieldProvider/FieldName/Resolver/Position.php @@ -8,10 +8,9 @@ namespace Magento\Elasticsearch\Model\Adapter\FieldMapper\Product\FieldProvider\FieldName\Resolver; use Magento\Elasticsearch\Model\Adapter\FieldMapper\Product\AttributeAdapter; +use Magento\Elasticsearch\Model\Adapter\FieldMapper\Product\FieldProvider\FieldName\ResolverInterface; use Magento\Framework\Registry; use Magento\Store\Model\StoreManagerInterface as StoreManager; -use Magento\Elasticsearch\Model\Adapter\FieldMapper\Product\FieldProvider\FieldName\ResolverInterface; -use Magento\Framework\App\ObjectManager; /** * Resolver field name for position attribute. @@ -33,13 +32,11 @@ class Position implements ResolverInterface * @param Registry $coreRegistry */ public function __construct( - StoreManager $storeManager = null, - Registry $coreRegistry = null + StoreManager $storeManager, + Registry $coreRegistry ) { - $this->storeManager = $storeManager ?: ObjectManager::getInstance() - ->get(StoreManager::class); - $this->coreRegistry = $coreRegistry ?: ObjectManager::getInstance() - ->get(Registry::class); + $this->storeManager = $storeManager; + $this->coreRegistry = $coreRegistry; } /** diff --git a/app/code/Magento/Elasticsearch/Model/Adapter/FieldMapper/Product/FieldProvider/FieldName/Resolver/Price.php b/app/code/Magento/Elasticsearch/Model/Adapter/FieldMapper/Product/FieldProvider/FieldName/Resolver/Price.php index 12e53ca2bd714..b55707ea7009f 100644 --- a/app/code/Magento/Elasticsearch/Model/Adapter/FieldMapper/Product/FieldProvider/FieldName/Resolver/Price.php +++ b/app/code/Magento/Elasticsearch/Model/Adapter/FieldMapper/Product/FieldProvider/FieldName/Resolver/Price.php @@ -7,14 +7,14 @@ namespace Magento\Elasticsearch\Model\Adapter\FieldMapper\Product\FieldProvider\FieldName\Resolver; -use Magento\Framework\App\ObjectManager; use Magento\Customer\Model\Session as CustomerSession; -use Magento\Store\Model\StoreManagerInterface as StoreManager; use Magento\Elasticsearch\Model\Adapter\FieldMapper\Product\AttributeAdapter; use Magento\Elasticsearch\Model\Adapter\FieldMapper\Product\FieldProvider\FieldName\ResolverInterface; +use Magento\Store\Model\StoreManagerInterface as StoreManager; /** * Resolver field name for price attribute. + * @SuppressWarnings(PHPMD.CookieAndSessionMisuse) */ class Price implements ResolverInterface { @@ -33,13 +33,11 @@ class Price implements ResolverInterface * @param StoreManager $storeManager */ public function __construct( - CustomerSession $customerSession = null, - StoreManager $storeManager = null + CustomerSession $customerSession, + StoreManager $storeManager ) { - $this->storeManager = $storeManager ?: ObjectManager::getInstance() - ->get(StoreManager::class); - $this->customerSession = $customerSession ?: ObjectManager::getInstance() - ->get(CustomerSession::class); + $this->storeManager = $storeManager; + $this->customerSession = $customerSession; } /** diff --git a/app/code/Magento/Elasticsearch/Model/Adapter/FieldMapper/Product/FieldProvider/StaticField.php b/app/code/Magento/Elasticsearch/Model/Adapter/FieldMapper/Product/FieldProvider/StaticField.php index 348a1c708a78c..f7dfcd29e5036 100644 --- a/app/code/Magento/Elasticsearch/Model/Adapter/FieldMapper/Product/FieldProvider/StaticField.php +++ b/app/code/Magento/Elasticsearch/Model/Adapter/FieldMapper/Product/FieldProvider/StaticField.php @@ -7,19 +7,18 @@ namespace Magento\Elasticsearch\Model\Adapter\FieldMapper\Product\FieldProvider; -use Magento\Framework\App\ObjectManager; use Magento\Eav\Model\Config; use Magento\Catalog\Api\Data\ProductAttributeInterface; use Magento\Elasticsearch\Model\Adapter\FieldMapper\Product\AttributeProvider; -use Magento\Elasticsearch\Model\Adapter\FieldMapper\Product\FieldProviderInterface; -use Magento\Elasticsearch\Model\Adapter\FieldMapper\Product\FieldProvider\FieldType\ConverterInterface - as FieldTypeConverterInterface; use Magento\Elasticsearch\Model\Adapter\FieldMapper\Product\FieldProvider\FieldIndex\ConverterInterface as IndexTypeConverterInterface; -use Magento\Elasticsearch\Model\Adapter\FieldMapper\Product\FieldProvider\FieldType\ResolverInterface - as FieldTypeResolver; use Magento\Elasticsearch\Model\Adapter\FieldMapper\Product\FieldProvider\FieldIndex\ResolverInterface as FieldIndexResolver; +use Magento\Elasticsearch\Model\Adapter\FieldMapper\Product\FieldProvider\FieldType\ConverterInterface + as FieldTypeConverterInterface; +use Magento\Elasticsearch\Model\Adapter\FieldMapper\Product\FieldProvider\FieldType\ResolverInterface + as FieldTypeResolver; +use Magento\Elasticsearch\Model\Adapter\FieldMapper\Product\FieldProviderInterface; use Magento\Elasticsearch\Model\Adapter\FieldMapperInterface; /** @@ -62,6 +61,11 @@ class StaticField implements FieldProviderInterface */ private $fieldNameResolver; + /** + * @var array + */ + private $excludedAttributes; + /** * @param Config $eavConfig * @param FieldTypeConverterInterface $fieldTypeConverter @@ -69,7 +73,8 @@ class StaticField implements FieldProviderInterface * @param FieldTypeResolver $fieldTypeResolver * @param FieldIndexResolver $fieldIndexResolver * @param AttributeProvider $attributeAdapterProvider - * @param FieldName\ResolverInterface|null $fieldNameResolver + * @param FieldName\ResolverInterface $fieldNameResolver + * @param array $excludedAttributes */ public function __construct( Config $eavConfig, @@ -78,7 +83,8 @@ public function __construct( FieldTypeResolver $fieldTypeResolver, FieldIndexResolver $fieldIndexResolver, AttributeProvider $attributeAdapterProvider, - FieldName\ResolverInterface $fieldNameResolver = null + FieldName\ResolverInterface $fieldNameResolver, + array $excludedAttributes = [] ) { $this->eavConfig = $eavConfig; $this->fieldTypeConverter = $fieldTypeConverter; @@ -86,8 +92,8 @@ public function __construct( $this->fieldTypeResolver = $fieldTypeResolver; $this->fieldIndexResolver = $fieldIndexResolver; $this->attributeAdapterProvider = $attributeAdapterProvider; - $this->fieldNameResolver = $fieldNameResolver ?: ObjectManager::getInstance() - ->get(FieldName\ResolverInterface::class); + $this->fieldNameResolver = $fieldNameResolver; + $this->excludedAttributes = $excludedAttributes; } /** @@ -103,6 +109,9 @@ public function getFields(array $context = []): array $allAttributes = []; foreach ($attributes as $attribute) { + if (in_array($attribute->getAttributeCode(), $this->excludedAttributes, true)) { + continue; + } $attributeAdapter = $this->attributeAdapterProvider->getByAttributeCode($attribute->getAttributeCode()); $fieldName = $this->fieldNameResolver->getFieldName($attributeAdapter); diff --git a/app/code/Magento/Elasticsearch/Model/Adapter/FieldMapper/ProductFieldMapper.php b/app/code/Magento/Elasticsearch/Model/Adapter/FieldMapper/ProductFieldMapper.php deleted file mode 100644 index 657605bbd019b..0000000000000 --- a/app/code/Magento/Elasticsearch/Model/Adapter/FieldMapper/ProductFieldMapper.php +++ /dev/null @@ -1,17 +0,0 @@ -<?php -/** - * Copyright © Magento, Inc. All rights reserved. - * See COPYING.txt for license details. - */ -namespace Magento\Elasticsearch\Model\Adapter\FieldMapper; - -use Magento\Elasticsearch\Model\Adapter\FieldMapperInterface; -use Magento\Elasticsearch\Elasticsearch5\Model\Adapter\FieldMapper\ProductFieldMapper - as Elasticsearch5ProductFieldMapper; - -/** - * Class ProductFieldMapper - */ -class ProductFieldMapper extends Elasticsearch5ProductFieldMapper implements FieldMapperInterface -{ -} diff --git a/app/code/Magento/Elasticsearch/Model/Adapter/Index/IndexNameResolver.php b/app/code/Magento/Elasticsearch/Model/Adapter/Index/IndexNameResolver.php index f69f7001d5bce..40d3ac95cf49c 100644 --- a/app/code/Magento/Elasticsearch/Model/Adapter/Index/IndexNameResolver.php +++ b/app/code/Magento/Elasticsearch/Model/Adapter/Index/IndexNameResolver.php @@ -6,7 +6,7 @@ namespace Magento\Elasticsearch\Model\Adapter\Index; -use Magento\Elasticsearch\Model\Client\Elasticsearch as ElasticsearchClient; +use Magento\AdvancedSearch\Model\Client\ClientInterface as ElasticsearchClient; use Magento\Elasticsearch\SearchAdapter\ConnectionManager; use Magento\Elasticsearch\Model\Config; use Psr\Log\LoggerInterface; @@ -14,7 +14,7 @@ use Magento\CatalogSearch\Model\Indexer\Fulltext; /** - * Index name resolver + * Index name resolver for Elasticsearch * @api * @since 100.1.0 */ diff --git a/app/code/Magento/Elasticsearch/Model/Client/Elasticsearch.php b/app/code/Magento/Elasticsearch/Model/Client/Elasticsearch.php deleted file mode 100644 index d933d8bb5d0b5..0000000000000 --- a/app/code/Magento/Elasticsearch/Model/Client/Elasticsearch.php +++ /dev/null @@ -1,343 +0,0 @@ -<?php -/** - * Copyright © Magento, Inc. All rights reserved. - * See COPYING.txt for license details. - */ - -namespace Magento\Elasticsearch\Model\Client; - -use Magento\Framework\Exception\LocalizedException; -use Magento\AdvancedSearch\Model\Client\ClientInterface; - -/** - * Elasticsearch client - */ -class Elasticsearch implements ClientInterface -{ - /** - * Elasticsearch Client instances - * - * @var \Elasticsearch\Client[] - */ - private $client; - - /** - * @var array - */ - private $clientOptions; - - /** - * @var bool - */ - private $pingResult; - - /** - * Initialize Elasticsearch Client - * - * @param array $options - * @param \Elasticsearch\Client|null $elasticsearchClient - * @throws LocalizedException - */ - public function __construct( - $options = [], - $elasticsearchClient = null - ) { - if (empty($options['hostname']) || ((!empty($options['enableAuth']) && - ($options['enableAuth'] == 1)) && (empty($options['username']) || empty($options['password'])))) { - throw new LocalizedException( - __('The search failed because of a search engine misconfiguration.') - ); - } - - if (!($elasticsearchClient instanceof \Elasticsearch\Client)) { - $config = $this->buildConfig($options); - $elasticsearchClient = \Elasticsearch\ClientBuilder::fromConfig($config, true); - } - $this->client[getmypid()] = $elasticsearchClient; - $this->clientOptions = $options; - } - - /** - * Get Elasticsearch Client - * - * @return \Elasticsearch\Client - */ - private function getClient() - { - $pid = getmypid(); - if (!isset($this->client[$pid])) { - $config = $this->buildConfig($this->clientOptions); - $this->client[$pid] = \Elasticsearch\ClientBuilder::fromConfig($config, true); - } - return $this->client[$pid]; - } - - /** - * Ping the Elasticsearch client - * - * @return bool - */ - public function ping() - { - if ($this->pingResult === null) { - $this->pingResult = $this->getClient()->ping(['client' => ['timeout' => $this->clientOptions['timeout']]]); - } - return $this->pingResult; - } - - /** - * Validate connection params - * - * @return bool - */ - public function testConnection() - { - return $this->ping(); - } - - /** - * Build config. - * - * @param array $options - * @return array - */ - private function buildConfig($options = []) - { - $hostname = preg_replace('/http[s]?:\/\//i', '', $options['hostname']); - // @codingStandardsIgnoreStart - $protocol = parse_url($options['hostname'], PHP_URL_SCHEME); - // @codingStandardsIgnoreEnd - if (!$protocol) { - $protocol = 'http'; - } - - $authString = ''; - if (!empty($options['enableAuth']) && (int)$options['enableAuth'] === 1) { - $authString = "{$options['username']}:{$options['password']}@"; - } - - $portString = ''; - if (!empty($options['port'])) { - $portString = ':' . $options['port']; - } - - $host = $protocol . '://' . $authString . $hostname . $portString; - - $options['hosts'] = [$host]; - - return $options; - } - - /** - * Performs bulk query over Elasticsearch index - * - * @param array $query - * @return void - */ - public function bulkQuery($query) - { - $this->getClient()->bulk($query); - } - - /** - * Creates an Elasticsearch index. - * - * @param string $index - * @param array $settings - * @return void - */ - public function createIndex($index, $settings) - { - $this->getClient()->indices()->create( - [ - 'index' => $index, - 'body' => $settings, - ] - ); - } - - /** - * Delete an Elasticsearch index. - * - * @param string $index - * @return void - */ - public function deleteIndex($index) - { - $this->getClient()->indices()->delete(['index' => $index]); - } - - /** - * Check if index is empty. - * - * @param string $index - * @return bool - */ - public function isEmptyIndex($index) - { - $stats = $this->getClient()->indices()->stats(['index' => $index, 'metric' => 'docs']); - if ($stats['indices'][$index]['primaries']['docs']['count'] == 0) { - return true; - } - return false; - } - - /** - * Updates alias. - * - * @param string $alias - * @param string $newIndex - * @param string $oldIndex - * @return void - */ - public function updateAlias($alias, $newIndex, $oldIndex = '') - { - $params['body'] = ['actions' => []]; - if ($oldIndex) { - $params['body']['actions'][] = ['remove' => ['alias' => $alias, 'index' => $oldIndex]]; - } - if ($newIndex) { - $params['body']['actions'][] = ['add' => ['alias' => $alias, 'index' => $newIndex]]; - } - - $this->getClient()->indices()->updateAliases($params); - } - - /** - * Checks whether Elasticsearch index exists - * - * @param string $index - * @return bool - */ - public function indexExists($index) - { - return $this->getClient()->indices()->exists(['index' => $index]); - } - - /** - * Check if alias exists. - * - * @param string $alias - * @param string $index - * @return bool - */ - public function existsAlias($alias, $index = '') - { - $params = ['name' => $alias]; - if ($index) { - $params['index'] = $index; - } - return $this->getClient()->indices()->existsAlias($params); - } - - /** - * Get alias. - * - * @param string $alias - * @return array - */ - public function getAlias($alias) - { - return $this->getClient()->indices()->getAlias(['name' => $alias]); - } - - /** - * Add mapping to Elasticsearch index - * - * @param array $fields - * @param string $index - * @param string $entityType - * @return void - */ - public function addFieldsMapping(array $fields, $index, $entityType) - { - $params = [ - 'index' => $index, - 'type' => $entityType, - 'body' => [ - $entityType => [ - '_all' => [ - 'enabled' => true, - 'type' => 'string' - ], - 'properties' => [], - 'dynamic_templates' => [ - [ - 'price_mapping' => [ - 'match' => 'price_*', - 'match_mapping' => 'string', - 'mapping' => [ - 'type' => 'float', - 'store' => true - ], - ], - ], - [ - 'position_mapping' => [ - 'match' => 'position_*', - 'match_mapping' => 'string', - 'mapping' => [ - 'type' => 'integer', - 'index' => 'no' - ], - ], - ], - [ - 'string_mapping' => [ - 'match' => '*', - 'match_mapping' => 'string', - 'mapping' => [ - 'type' => 'string', - 'index' => 'no' - ], - ], - ] - ], - ], - ], - ]; - foreach ($fields as $field => $fieldInfo) { - $params['body'][$entityType]['properties'][$field] = $fieldInfo; - } - $this->getClient()->indices()->putMapping($params); - } - - /** - * Delete mapping in Elasticsearch index - * - * @param string $index - * @param string $entityType - * @return void - */ - public function deleteMapping($index, $entityType) - { - $this->getClient()->indices()->deleteMapping( - [ - 'index' => $index, - 'type' => $entityType, - ] - ); - } - - /** - * Execute search by $query - * - * @param array $query - * @return array - */ - public function query($query) - { - return $this->getClient()->search($query); - } - - /** - * Execute suggest query - * - * @param array $query - * @return array - */ - public function suggest($query) - { - return $this->getClient()->suggest($query); - } -} diff --git a/app/code/Magento/Elasticsearch/Model/Config.php b/app/code/Magento/Elasticsearch/Model/Config.php index 0bf23f318c3bd..3766d825aefb5 100644 --- a/app/code/Magento/Elasticsearch/Model/Config.php +++ b/app/code/Magento/Elasticsearch/Model/Config.php @@ -7,11 +7,9 @@ use Magento\Framework\App\Config\ScopeConfigInterface; use Magento\Framework\Search\EngineResolverInterface; -use Magento\Search\Model\EngineResolver; use Magento\Store\Model\ScopeInterface; use Magento\AdvancedSearch\Model\Client\ClientOptionsInterface; use Magento\AdvancedSearch\Model\Client\ClientResolver; -use Magento\Framework\App\ObjectManager; /** * Elasticsearch config model @@ -70,21 +68,21 @@ class Config implements ClientOptionsInterface /** * @param ScopeConfigInterface $scopeConfig - * @param ClientResolver|null $clientResolver - * @param EngineResolverInterface|null $engineResolver + * @param ClientResolver $clientResolver + * @param EngineResolverInterface $engineResolver * @param string|null $prefix * @param array $engineList */ public function __construct( ScopeConfigInterface $scopeConfig, - ClientResolver $clientResolver = null, - EngineResolverInterface $engineResolver = null, + ClientResolver $clientResolver, + EngineResolverInterface $engineResolver, $prefix = null, $engineList = [] ) { $this->scopeConfig = $scopeConfig; - $this->clientResolver = $clientResolver ?: ObjectManager::getInstance()->get(ClientResolver::class); - $this->engineResolver = $engineResolver ?: ObjectManager::getInstance()->get(EngineResolverInterface::class); + $this->clientResolver = $clientResolver; + $this->engineResolver = $engineResolver; $this->prefix = $prefix ?: $this->clientResolver->getCurrentEngine(); $this->engineList = $engineList; } diff --git a/app/code/Magento/Elasticsearch6/Model/DataProvider/Suggestions.php b/app/code/Magento/Elasticsearch/Model/DataProvider/Base/Suggestions.php similarity index 98% rename from app/code/Magento/Elasticsearch6/Model/DataProvider/Suggestions.php rename to app/code/Magento/Elasticsearch/Model/DataProvider/Base/Suggestions.php index d05471734bb8f..8364b6c116b7d 100644 --- a/app/code/Magento/Elasticsearch6/Model/DataProvider/Suggestions.php +++ b/app/code/Magento/Elasticsearch/Model/DataProvider/Base/Suggestions.php @@ -3,7 +3,7 @@ * Copyright © Magento, Inc. All rights reserved. * See COPYING.txt for license details. */ -namespace Magento\Elasticsearch6\Model\DataProvider; +namespace Magento\Elasticsearch\Model\DataProvider\Base; use Magento\Elasticsearch\Model\Adapter\FieldMapper\Product\FieldProviderInterface; use Magento\Store\Model\ScopeInterface; @@ -17,7 +17,7 @@ use Magento\Store\Model\StoreManagerInterface as StoreManager; /** - * Class Suggestions + * Default implementation to provide suggestions mechanism for Elasticsearch */ class Suggestions implements SuggestedQueriesInterface { diff --git a/app/code/Magento/Elasticsearch/Model/DataProvider/Suggestions.php b/app/code/Magento/Elasticsearch/Model/DataProvider/Suggestions.php index c4fab39dfde61..54e9890e02e59 100644 --- a/app/code/Magento/Elasticsearch/Model/DataProvider/Suggestions.php +++ b/app/code/Magento/Elasticsearch/Model/DataProvider/Suggestions.php @@ -3,37 +3,42 @@ * Copyright © Magento, Inc. All rights reserved. * See COPYING.txt for license details. */ +declare(strict_types=1); + namespace Magento\Elasticsearch\Model\DataProvider; -use Magento\Store\Model\ScopeInterface; -use Magento\Search\Model\QueryInterface; use Magento\AdvancedSearch\Model\SuggestedQueriesInterface; use Magento\Elasticsearch\Model\Config; use Magento\Elasticsearch\SearchAdapter\ConnectionManager; -use Magento\Search\Model\QueryResultFactory; -use Magento\Framework\App\Config\ScopeConfigInterface; use Magento\Elasticsearch\SearchAdapter\SearchIndexNameResolver; +use Magento\Framework\App\Config\ScopeConfigInterface; +use Magento\Search\Model\QueryInterface; +use Magento\Search\Model\QueryResultFactory; +use Magento\Store\Model\ScopeInterface; use Magento\Store\Model\StoreManagerInterface as StoreManager; /** - * Class Suggestions + * The implementation to provide suggestions mechanism for Elasticsearch5 + * + * @deprecated because of EOL for Elasticsearch5 + * @see \Magento\Elasticsearch\Model\DataProvider\Base\Suggestions */ class Suggestions implements SuggestedQueriesInterface { /** - * @deprecated + * @deprecated moved to interface * @see SuggestedQueriesInterface::SEARCH_SUGGESTION_COUNT */ const CONFIG_SUGGESTION_COUNT = 'catalog/search/search_suggestion_count'; /** - * @deprecated + * @deprecated moved to interface * @see SuggestedQueriesInterface::SEARCH_SUGGESTION_COUNT_RESULTS_ENABLED */ const CONFIG_SUGGESTION_COUNT_RESULTS_ENABLED = 'catalog/search/search_suggestion_count_results_enabled'; /** - * @deprecated + * @deprecated moved to interface * @see SuggestedQueriesInterface::SEARCH_SUGGESTION_ENABLED */ const CONFIG_SUGGESTION_ENABLED = 'catalog/search/search_suggestion_enabled'; @@ -126,7 +131,7 @@ public function getItems(QueryInterface $query) public function isResultsCountEnabled() { return $this->scopeConfig->isSetFlag( - self::CONFIG_SUGGESTION_COUNT_RESULTS_ENABLED, + self::SEARCH_SUGGESTION_COUNT_RESULTS_ENABLED, ScopeInterface::SCOPE_STORE ); } @@ -203,7 +208,7 @@ private function fetchQuery(array $query) private function getSearchSuggestionsCount() { return (int)$this->scopeConfig->getValue( - self::CONFIG_SUGGESTION_COUNT, + self::SEARCH_SUGGESTION_COUNT, ScopeInterface::SCOPE_STORE ); } @@ -216,7 +221,7 @@ private function getSearchSuggestionsCount() private function isSuggestionsAllowed() { $isSuggestionsEnabled = $this->scopeConfig->isSetFlag( - self::CONFIG_SUGGESTION_ENABLED, + self::SEARCH_SUGGESTION_ENABLED, ScopeInterface::SCOPE_STORE ); $isEnabled = $this->config->isElasticsearchEnabled(); diff --git a/app/code/Magento/Elasticsearch/Model/Indexer/Fulltext/Plugin/Category/Product/Action/Rows.php b/app/code/Magento/Elasticsearch/Model/Indexer/Fulltext/Plugin/Category/Product/Action/Rows.php new file mode 100644 index 0000000000000..1d1d23017e68c --- /dev/null +++ b/app/code/Magento/Elasticsearch/Model/Indexer/Fulltext/Plugin/Category/Product/Action/Rows.php @@ -0,0 +1,124 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +declare(strict_types=1); + +namespace Magento\Elasticsearch\Model\Indexer\Fulltext\Plugin\Category\Product\Action; + +use Magento\Catalog\Model\Indexer\Category\Product\TableMaintainer; +use Magento\Framework\App\ResourceConnection; +use Magento\Framework\Indexer\IndexerRegistry; +use Magento\CatalogSearch\Model\Indexer\Fulltext as FulltextIndexer; +use Magento\Catalog\Model\Indexer\Category\Product\Action\Rows as ActionRows; +use Magento\Store\Model\StoreManagerInterface; +use Magento\Framework\DB\Adapter\AdapterInterface; + +/** + * Catalog search indexer plugin for catalog category products assignment. + */ +class Rows +{ + /** + * @var IndexerRegistry + */ + private $indexerRegistry; + + /** + * @var StoreManagerInterface + */ + private $storeManager; + + /** + * @var AdapterInterface + */ + private $connection; + + /** + * @var TableMaintainer + */ + private $tableMaintainer; + + /** + * @param IndexerRegistry $indexerRegistry + * @param StoreManagerInterface $storeManager + * @param ResourceConnection $resource + * @param TableMaintainer $tableMaintainer + */ + public function __construct( + IndexerRegistry $indexerRegistry, + StoreManagerInterface $storeManager, + ResourceConnection $resource, + TableMaintainer $tableMaintainer + ) { + $this->indexerRegistry = $indexerRegistry; + $this->storeManager = $storeManager; + $this->connection = $resource->getConnection(); + $this->tableMaintainer = $tableMaintainer; + } + + /** + * Reindex after catalog category product reindex. + * + * @param ActionRows $subject + * @param ActionRows $result + * @param array $entityIds + * @param bool $useTempTable + * @return ActionRows + * + * @SuppressWarnings(PHPMD.UnusedFormalParameter) + */ + public function afterExecute( + ActionRows $subject, + ActionRows $result, + array $entityIds, + bool $useTempTable = false + ): ActionRows { + $indexer = $this->indexerRegistry->get(FulltextIndexer::INDEXER_ID); + if (!empty($entityIds) && $indexer->isScheduled()) { + foreach ($this->storeManager->getStores() as $store) { + $indexTable = $this->getIndexTable((int) $store->getId(), $useTempTable); + $productIds = $this->getProductIdsFromIndex($indexTable, $entityIds); + if (!empty($productIds)) { + $indexer->reindexList($productIds); + } + } + } + + return $result; + } + + /** + * Return index table name. + * + * @param int $storeId + * @param bool $useTempTable + * + * @return string + */ + private function getIndexTable(int $storeId, bool $useTempTable) + { + return $useTempTable + ? $this->tableMaintainer->getMainReplicaTable($storeId) + : $this->tableMaintainer->getMainTable($storeId); + } + + /** + * Get all category products from index table. + * + * @param string $indexTable + * @param array $categoryIds + * + * @return array + */ + private function getProductIdsFromIndex(string $indexTable, array $categoryIds): array + { + return $this->connection->fetchCol( + $this->connection->select() + ->from($indexTable, ['product_id']) + ->where('category_id IN (?)', $categoryIds) + ->distinct() + ); + } +} diff --git a/app/code/Magento/Elasticsearch/Model/ResourceModel/Fulltext/Collection/SearchCriteriaResolver.php b/app/code/Magento/Elasticsearch/Model/ResourceModel/Fulltext/Collection/SearchCriteriaResolver.php index ce88fc290e23c..64ce76c729334 100644 --- a/app/code/Magento/Elasticsearch/Model/ResourceModel/Fulltext/Collection/SearchCriteriaResolver.php +++ b/app/code/Magento/Elasticsearch/Model/ResourceModel/Fulltext/Collection/SearchCriteriaResolver.php @@ -80,6 +80,9 @@ public function resolve(): SearchCriteria $searchCriteria->setRequestName($this->searchRequestName); $searchCriteria->setSortOrders($this->orders); $searchCriteria->setCurrentPage($this->currentPage - 1); + if ($this->size) { + $searchCriteria->setPageSize($this->size); + } return $searchCriteria; } diff --git a/app/code/Magento/Elasticsearch/Model/ResourceModel/Fulltext/Collection/SearchResultApplier.php b/app/code/Magento/Elasticsearch/Model/ResourceModel/Fulltext/Collection/SearchResultApplier.php index ad52f81bf8eda..54b8c1966ee12 100644 --- a/app/code/Magento/Elasticsearch/Model/ResourceModel/Fulltext/Collection/SearchResultApplier.php +++ b/app/code/Magento/Elasticsearch/Model/ResourceModel/Fulltext/Collection/SearchResultApplier.php @@ -7,8 +7,8 @@ namespace Magento\Elasticsearch\Model\ResourceModel\Fulltext\Collection; use Magento\CatalogSearch\Model\ResourceModel\Fulltext\Collection\SearchResultApplierInterface; -use Magento\Framework\Data\Collection; use Magento\Framework\Api\Search\SearchResultInterface; +use Magento\Framework\Data\Collection; /** * Resolve specific attributes for search criteria. @@ -60,6 +60,7 @@ public function apply() { if (empty($this->searchResult->getItems())) { $this->collection->getSelect()->where('NULL'); + return; } @@ -68,10 +69,22 @@ public function apply() foreach ($items as $item) { $ids[] = (int)$item->getId(); } - $this->collection->getSelect()->where('e.entity_id IN (?)', $ids); - $orderList = join(',', $ids); - $this->collection->getSelect()->reset(\Magento\Framework\DB\Select::ORDER); - $this->collection->getSelect()->order("FIELD(e.entity_id,$orderList)"); + $this->collection->getSelect() + ->where('e.entity_id IN (?)', $ids) + ->reset(\Magento\Framework\DB\Select::ORDER); + $sortOrder = $this->searchResult->getSearchCriteria() + ->getSortOrders(); + if (!empty($sortOrder['price']) && $this->collection->getLimitationFilters()->isUsingPriceIndex()) { + $sortDirection = $sortOrder['price']; + $this->collection->getSelect() + ->order( + new \Zend_Db_Expr("price_index.min_price = 0, price_index.min_price {$sortDirection}") + ); + } else { + $orderList = join(',', $ids); + $this->collection->getSelect() + ->order(new \Zend_Db_Expr("FIELD(e.entity_id,$orderList)")); + } } /** @@ -85,13 +98,33 @@ public function apply() private function sliceItems(array $items, int $size, int $currentPage): array { if ($size !== 0) { - $offset = ($currentPage - 1) * $size; - if ($offset < 0) { - $offset = 0; + // Check that current page is in a range of allowed page numbers, based on items count and items per page, + // than calculate offset for slicing items array. + $itemsCount = count($items); + $maxAllowedPageNumber = ceil($itemsCount/$size); + if ($currentPage < 1) { + $currentPage = 1; + } + if ($currentPage > $maxAllowedPageNumber) { + $currentPage = $maxAllowedPageNumber; } - $items = array_slice($items, $offset, $this->size); + + $offset = $this->getOffset($currentPage, $size); + $items = array_slice($items, $offset, $size); } return $items; } + + /** + * Get offset for given page. + * + * @param int $pageNumber + * @param int $pageSize + * @return int + */ + private function getOffset(int $pageNumber, int $pageSize): int + { + return ($pageNumber - 1) * $pageSize; + } } diff --git a/app/code/Magento/Elasticsearch/SearchAdapter/Adapter.php b/app/code/Magento/Elasticsearch/SearchAdapter/Adapter.php deleted file mode 100644 index 6f9ef552351fd..0000000000000 --- a/app/code/Magento/Elasticsearch/SearchAdapter/Adapter.php +++ /dev/null @@ -1,93 +0,0 @@ -<?php -/** - * Copyright © Magento, Inc. All rights reserved. - * See COPYING.txt for license details. - */ -namespace Magento\Elasticsearch\SearchAdapter; - -use Magento\Framework\App\ObjectManager; -use Magento\Framework\Search\AdapterInterface; -use Magento\Framework\Search\RequestInterface; -use Magento\Framework\Search\Response\QueryResponse; -use Magento\Elasticsearch\SearchAdapter\Aggregation\Builder as AggregationBuilder; - -/** - * Elasticsearch Search Adapter - */ -class Adapter implements AdapterInterface -{ - /** - * Mapper instance - * - * @var Mapper - */ - protected $mapper; - - /** - * Response Factory - * - * @var ResponseFactory - */ - protected $responseFactory; - - /** - * @var ConnectionManager - */ - protected $connectionManager; - - /** - * @var AggregationBuilder - */ - protected $aggregationBuilder; - - /** - * @var QueryContainerFactory - */ - private $queryContainerFactory; - - /** - * @param ConnectionManager $connectionManager - * @param Mapper $mapper - * @param ResponseFactory $responseFactory - * @param AggregationBuilder $aggregationBuilder - * @param QueryContainerFactory $queryContainerFactory - */ - public function __construct( - ConnectionManager $connectionManager, - Mapper $mapper, - ResponseFactory $responseFactory, - AggregationBuilder $aggregationBuilder, - QueryContainerFactory $queryContainerFactory = null - ) { - $this->connectionManager = $connectionManager; - $this->mapper = $mapper; - $this->responseFactory = $responseFactory; - $this->aggregationBuilder = $aggregationBuilder; - $this->queryContainerFactory = $queryContainerFactory - ?: ObjectManager::getInstance()->get(QueryContainerFactory::class); - } - - /** - * @inheritdoc - */ - public function query(RequestInterface $request) - { - $client = $this->connectionManager->getConnection(); - $aggregationBuilder = $this->aggregationBuilder; - - $query = $this->mapper->buildQuery($request); - $aggregationBuilder->setQuery($this->queryContainerFactory->create(['query' => $query])); - $rawResponse = $client->query($query); - - $rawDocuments = isset($rawResponse['hits']['hits']) ? $rawResponse['hits']['hits'] : []; - - $queryResponse = $this->responseFactory->create( - [ - 'documents' => $rawDocuments, - 'aggregations' => $aggregationBuilder->build($request, $rawResponse), - 'total' => isset($rawResponse['hits']['total']) ? $rawResponse['hits']['total'] : 0 - ] - ); - return $queryResponse; - } -} diff --git a/app/code/Magento/Elasticsearch/SearchAdapter/Aggregation/Builder.php b/app/code/Magento/Elasticsearch/SearchAdapter/Aggregation/Builder.php index 1e9b60da74a5b..35b0b7d8b4fa6 100644 --- a/app/code/Magento/Elasticsearch/SearchAdapter/Aggregation/Builder.php +++ b/app/code/Magento/Elasticsearch/SearchAdapter/Aggregation/Builder.php @@ -3,26 +3,29 @@ * Copyright © Magento, Inc. All rights reserved. * See COPYING.txt for license details. */ +declare(strict_types=1); namespace Magento\Elasticsearch\SearchAdapter\Aggregation; +use Magento\Elasticsearch\SearchAdapter\Aggregation\Builder\BucketBuilderInterface; use Magento\Elasticsearch\SearchAdapter\QueryContainer; -use Magento\Framework\App\ObjectManager; use Magento\Framework\Search\RequestInterface; use Magento\Framework\Search\Dynamic\DataProviderInterface; -use Magento\Elasticsearch\SearchAdapter\Aggregation\Builder\BucketBuilderInterface; +/** + * Elasticsearch aggregation builder + */ class Builder { /** * @var DataProviderInterface[] */ - protected $dataProviderContainer; + private $dataProviderContainer; /** * @var BucketBuilderInterface[] */ - protected $aggregationContainer; + private $aggregationContainer; /** * @var DataProviderFactory @@ -32,32 +35,31 @@ class Builder /** * @var QueryContainer */ - private $query = null; + private $query; /** * @param DataProviderInterface[] $dataProviderContainer * @param BucketBuilderInterface[] $aggregationContainer - * @param DataProviderFactory|null $dataProviderFactory + * @param DataProviderFactory $dataProviderFactory */ public function __construct( array $dataProviderContainer, array $aggregationContainer, - DataProviderFactory $dataProviderFactory = null + DataProviderFactory $dataProviderFactory ) { $this->dataProviderContainer = array_map( - function (DataProviderInterface $dataProvider) { + static function (DataProviderInterface $dataProvider) { return $dataProvider; }, $dataProviderContainer ); $this->aggregationContainer = array_map( - function (BucketBuilderInterface $bucketBuilder) { + static function (BucketBuilderInterface $bucketBuilder) { return $bucketBuilder; }, $aggregationContainer ); - $this->dataProviderFactory = $dataProviderFactory - ?: ObjectManager::getInstance()->get(DataProviderFactory::class); + $this->dataProviderFactory = $dataProviderFactory; } /** diff --git a/app/code/Magento/Elasticsearch/SearchAdapter/Aggregation/Interval.php b/app/code/Magento/Elasticsearch/SearchAdapter/Aggregation/Interval.php deleted file mode 100644 index 33ab1a4071560..0000000000000 --- a/app/code/Magento/Elasticsearch/SearchAdapter/Aggregation/Interval.php +++ /dev/null @@ -1,287 +0,0 @@ -<?php -/** - * Copyright © Magento, Inc. All rights reserved. - * See COPYING.txt for license details. - */ -namespace Magento\Elasticsearch\SearchAdapter\Aggregation; - -use Magento\Framework\Search\Dynamic\IntervalInterface; -use Magento\Elasticsearch\SearchAdapter\ConnectionManager; -use Magento\Elasticsearch\Model\Adapter\FieldMapperInterface; -use Magento\Elasticsearch\Model\Config; -use Magento\Elasticsearch\SearchAdapter\SearchIndexNameResolver; -use Magento\CatalogSearch\Model\Indexer\Fulltext; - -class Interval implements IntervalInterface -{ - /** - * Minimal possible value - */ - const DELTA = 0.005; - - /** - * @var ConnectionManager - */ - protected $connectionManager; - - /** - * @var FieldMapperInterface - */ - protected $fieldMapper; - - /** - * @var Config - */ - protected $clientConfig; - - /** - * @var string - */ - private $fieldName; - - /** - * @var string - */ - private $storeId; - - /** - * @var array - */ - private $entityIds; - - /** - * @var SearchIndexNameResolver - */ - private $searchIndexNameResolver; - - /** - * @param ConnectionManager $connectionManager - * @param FieldMapperInterface $fieldMapper - * @param Config $clientConfig - * @param SearchIndexNameResolver $searchIndexNameResolver - * @param string $fieldName - * @param string $storeId - * @param array $entityIds - */ - public function __construct( - ConnectionManager $connectionManager, - FieldMapperInterface $fieldMapper, - Config $clientConfig, - SearchIndexNameResolver $searchIndexNameResolver, - $fieldName, - $storeId, - $entityIds - ) { - $this->connectionManager = $connectionManager; - $this->fieldMapper = $fieldMapper; - $this->clientConfig = $clientConfig; - $this->fieldName = $fieldName; - $this->storeId = $storeId; - $this->entityIds = $entityIds; - $this->searchIndexNameResolver = $searchIndexNameResolver; - } - - /** - * {@inheritdoc} - */ - public function load($limit, $offset = null, $lower = null, $upper = null) - { - $from = $to = []; - if ($lower) { - $from = ['gte' => $lower - self::DELTA]; - } - if ($upper) { - $to = ['lt' => $upper - self::DELTA]; - } - - $requestQuery = [ - 'index' => $this->searchIndexNameResolver->getIndexName($this->storeId, Fulltext::INDEXER_ID), - 'type' => $this->clientConfig->getEntityType(), - 'body' => [ - 'fields' => [ - '_id', - $this->fieldName, - ], - 'query' => [ - 'filtered' => [ - 'query' => [ - 'match_all' => [], - ], - 'filter' => [ - 'bool' => [ - 'must' => [ - [ - 'terms' => [ - '_id' => $this->entityIds, - ], - ], - [ - 'range' => [ - $this->fieldName => array_merge($from, $to), - ], - ], - ], - ], - ], - ], - ], - 'sort' => [ - $this->fieldName, - ], - 'size' => $limit, - ], - ]; - if ($offset) { - $requestQuery['body']['from'] = $offset; - } - $queryResult = $this->connectionManager->getConnection() - ->query($requestQuery); - - return $this->arrayValuesToFloat($queryResult['hits']['hits'], $this->fieldName); - } - - /** - * {@inheritdoc} - */ - public function loadPrevious($data, $index, $lower = null) - { - if ($lower) { - $from = ['gte' => $lower - self::DELTA]; - } - if ($data) { - $to = ['lt' => $data - self::DELTA]; - } - - $requestQuery = [ - 'index' => $this->searchIndexNameResolver->getIndexName($this->storeId, Fulltext::INDEXER_ID), - 'type' => $this->clientConfig->getEntityType(), - 'search_type' => 'count', - 'body' => [ - 'fields' => [ - '_id' - ], - 'query' => [ - 'filtered' => [ - 'query' => [ - 'match_all' => [], - ], - 'filter' => [ - 'bool' => [ - 'must' => [ - [ - 'terms' => [ - '_id' => $this->entityIds, - ], - ], - [ - 'range' => [ - $this->fieldName => array_merge($from, $to), - ], - ], - ], - ], - ], - ], - ], - 'sort' => [ - $this->fieldName, - ], - ], - ]; - $queryResult = $this->connectionManager->getConnection() - ->query($requestQuery); - - $offset = $queryResult['hits']['total']; - if (!$offset) { - return false; - } - - return $this->load($index - $offset + 1, $offset - 1, $lower); - } - - /** - * {@inheritdoc} - */ - public function loadNext($data, $rightIndex, $upper = null) - { - $from = ['gt' => $data + self::DELTA]; - $to = ['lt' => $data - self::DELTA]; - - $requestCountQuery = [ - 'index' => $this->searchIndexNameResolver->getIndexName($this->storeId, Fulltext::INDEXER_ID), - 'type' => $this->clientConfig->getEntityType(), - 'search_type' => 'count', - 'body' => [ - 'fields' => [ - '_id' - ], - 'query' => [ - 'filtered' => [ - 'query' => [ - 'match_all' => [], - ], - 'filter' => [ - 'bool' => [ - 'must' => [ - [ - 'terms' => [ - '_id' => $this->entityIds, - ], - ], - [ - 'range' => [ - $this->fieldName => array_merge($from, $to), - ], - ], - ], - ], - ], - ], - ], - 'sort' => [ - $this->fieldName, - ], - ], - ]; - $queryCountResult = $this->connectionManager->getConnection() - ->query($requestCountQuery); - - $offset = $queryCountResult['hits']['total']; - if (!$offset) { - return false; - } - - $from = ['gte' => $data - self::DELTA]; - if ($upper !== null) { - $to = ['lt' => $data - self::DELTA]; - } - - $requestQuery = $requestCountQuery; - $requestCountQuery['body']['query']['filtered']['filter']['bool']['must']['range'] = - [$this->fieldName => array_merge($from, $to)]; - - $requestCountQuery['body']['from'] = $offset - 1; - $requestCountQuery['body']['size'] = $rightIndex - $offset + 1; - - $queryResult = $this->connectionManager->getConnection() - ->query($requestQuery); - - return array_reverse($this->arrayValuesToFloat($queryResult['hits']['hits'], $this->fieldName)); - } - - /** - * @param array $hits - * @param string $fieldName - * - * @return float[] - */ - private function arrayValuesToFloat($hits, $fieldName) - { - $returnPrices = []; - foreach ($hits as $hit) { - $returnPrices[] = (float) $hit['fields'][$fieldName][0]; - } - - return $returnPrices; - } -} diff --git a/app/code/Magento/Elasticsearch/SearchAdapter/ConnectionManager.php b/app/code/Magento/Elasticsearch/SearchAdapter/ConnectionManager.php index 77c42077323d4..57973b7fb92e4 100644 --- a/app/code/Magento/Elasticsearch/SearchAdapter/ConnectionManager.php +++ b/app/code/Magento/Elasticsearch/SearchAdapter/ConnectionManager.php @@ -7,10 +7,12 @@ use Magento\AdvancedSearch\Model\Client\ClientOptionsInterface; use Magento\AdvancedSearch\Model\Client\ClientFactoryInterface; -use Magento\Elasticsearch\Model\Client\Elasticsearch; +use Magento\AdvancedSearch\Model\Client\ClientInterface as Elasticsearch; use Psr\Log\LoggerInterface; /** + * Class provides interface for Elasticsearch connection + * * @api * @since 100.1.0 */ diff --git a/app/code/Magento/Elasticsearch/SearchAdapter/DocumentFactory.php b/app/code/Magento/Elasticsearch/SearchAdapter/DocumentFactory.php index 83652e08d4246..3372a2c26ae0e 100644 --- a/app/code/Magento/Elasticsearch/SearchAdapter/DocumentFactory.php +++ b/app/code/Magento/Elasticsearch/SearchAdapter/DocumentFactory.php @@ -5,7 +5,6 @@ */ namespace Magento\Elasticsearch\SearchAdapter; -use Magento\Framework\ObjectManagerInterface; use Magento\Framework\Search\EntityMetadata; use Magento\Framework\Api\AttributeInterface; use Magento\Framework\Api\AttributeValue; @@ -14,33 +13,22 @@ use Magento\Framework\Api\Search\DocumentInterface; /** - * Document Factory + * Document Factory to create Search Document instance * @api * @since 100.1.0 */ class DocumentFactory { - /** - * Object Manager instance - * - * @var ObjectManagerInterface - * @deprecated 100.1.0 - * @since 100.1.0 - */ - protected $objectManager; - /** * @var EntityMetadata */ private $entityMetadata; /** - * @param ObjectManagerInterface $objectManager * @param EntityMetadata $entityMetadata */ - public function __construct(ObjectManagerInterface $objectManager, EntityMetadata $entityMetadata) + public function __construct(EntityMetadata $entityMetadata) { - $this->objectManager = $objectManager; $this->entityMetadata = $entityMetadata; } diff --git a/app/code/Magento/Elasticsearch/SearchAdapter/Filter/Builder.php b/app/code/Magento/Elasticsearch/SearchAdapter/Filter/Builder.php index 8efc342d26452..db6bb3cc7c413 100644 --- a/app/code/Magento/Elasticsearch/SearchAdapter/Filter/Builder.php +++ b/app/code/Magento/Elasticsearch/SearchAdapter/Filter/Builder.php @@ -12,32 +12,15 @@ use Magento\Elasticsearch\SearchAdapter\Filter\Builder\Term; use Magento\Elasticsearch\SearchAdapter\Filter\Builder\Wildcard; +/** + * Class Builder to build Elasticsearch filter + */ class Builder implements BuilderInterface { - /** - * Text flag for Elasticsearch filter query condition types - * - * @deprecated - * @see BuilderInterface::FILTER_QUERY_CONDITION_MUST - */ - const QUERY_CONDITION_MUST = 'must'; - - /** - * @deprecated - * @see BuilderInterface::FILTER_QUERY_CONDITION_SHOULD - */ - const QUERY_CONDITION_SHOULD = 'should'; - - /** - * @deprecated - * @see BuilderInterface::FILTER_QUERY_CONDITION_MUST_NOT - */ - const QUERY_CONDITION_MUST_NOT = 'must_not'; - /** * @var FilterInterface[] */ - protected $filters; + private $filters; /** * @param Range $range @@ -57,7 +40,7 @@ public function __construct( } /** - * {@inheritdoc} + * @inheritdoc */ public function build(RequestFilterInterface $filter, $conditionType) { @@ -65,11 +48,13 @@ public function build(RequestFilterInterface $filter, $conditionType) } /** + * Processes filter object + * * @param RequestFilterInterface $filter * @param string $conditionType * @return array */ - protected function processFilter(RequestFilterInterface $filter, $conditionType) + private function processFilter(RequestFilterInterface $filter, $conditionType) { if (RequestFilterInterface::TYPE_BOOL == $filter->getType()) { $query = $this->processBoolFilter($filter, $this->isNegation($conditionType)); @@ -88,6 +73,8 @@ protected function processFilter(RequestFilterInterface $filter, $conditionType) } /** + * Processes filter + * * @param RequestFilterInterface|BoolExpression $filter * @param bool $isNegation * @return array @@ -96,12 +83,12 @@ protected function processBoolFilter(RequestFilterInterface $filter, $isNegation { $must = $this->buildFilters( $filter->getMust(), - $this->mapConditionType(self::QUERY_CONDITION_MUST, $isNegation) + $this->mapConditionType(BuilderInterface::FILTER_QUERY_CONDITION_MUST, $isNegation) ); - $should = $this->buildFilters($filter->getShould(), self::QUERY_CONDITION_SHOULD); + $should = $this->buildFilters($filter->getShould(), BuilderInterface::FILTER_QUERY_CONDITION_SHOULD); $mustNot = $this->buildFilters( $filter->getMustNot(), - $this->mapConditionType(self::QUERY_CONDITION_MUST_NOT, $isNegation) + $this->mapConditionType(BuilderInterface::FILTER_QUERY_CONDITION_MUST_NOT, $isNegation) ); $queries = [ @@ -116,6 +103,8 @@ protected function processBoolFilter(RequestFilterInterface $filter, $isNegation } /** + * Build filters + * * @param RequestFilterInterface[] $filters * @param string $conditionType * @return string @@ -126,25 +115,31 @@ private function buildFilters(array $filters, $conditionType) foreach ($filters as $filter) { $filterQuery = $this->processFilter($filter, $conditionType); if (isset($filterQuery['bool'][$conditionType])) { + // phpcs:ignore Magento2.Performance.ForeachArrayMerge $queries['bool'][$conditionType] = array_merge( isset($queries['bool'][$conditionType]) ? $queries['bool'][$conditionType] : [], $filterQuery['bool'][$conditionType] ); } } + return $queries; } /** + * Returns is condition type navigation + * * @param string $conditionType * @return bool */ - protected function isNegation($conditionType) + private function isNegation($conditionType) { - return self::QUERY_CONDITION_MUST_NOT === $conditionType; + return BuilderInterface::FILTER_QUERY_CONDITION_MUST_NOT === $conditionType; } /** + * Maps condition type + * * @param string $conditionType * @param bool $isNegation * @return string @@ -152,10 +147,10 @@ protected function isNegation($conditionType) private function mapConditionType($conditionType, $isNegation) { if ($isNegation) { - if ($conditionType == self::QUERY_CONDITION_MUST) { - $conditionType = self::QUERY_CONDITION_MUST_NOT; - } elseif ($conditionType == self::QUERY_CONDITION_MUST_NOT) { - $conditionType = self::QUERY_CONDITION_MUST; + if ($conditionType == BuilderInterface::FILTER_QUERY_CONDITION_MUST) { + $conditionType = BuilderInterface::FILTER_QUERY_CONDITION_MUST_NOT; + } elseif ($conditionType == BuilderInterface::FILTER_QUERY_CONDITION_MUST_NOT) { + $conditionType = BuilderInterface::FILTER_QUERY_CONDITION_MUST; } } return $conditionType; diff --git a/app/code/Magento/Elasticsearch/SearchAdapter/Filter/Builder/Term.php b/app/code/Magento/Elasticsearch/SearchAdapter/Filter/Builder/Term.php index d88c7e53d813a..76a2f00f44fe2 100644 --- a/app/code/Magento/Elasticsearch/SearchAdapter/Filter/Builder/Term.php +++ b/app/code/Magento/Elasticsearch/SearchAdapter/Filter/Builder/Term.php @@ -3,10 +3,11 @@ * Copyright © Magento, Inc. All rights reserved. * See COPYING.txt for license details. */ +declare(strict_types=1); + namespace Magento\Elasticsearch\SearchAdapter\Filter\Builder; use Magento\Elasticsearch\Model\Adapter\FieldMapper\Product\AttributeProvider; -use Magento\Framework\App\ObjectManager; use Magento\Framework\Search\Request\Filter\Term as TermFilterRequest; use Magento\Framework\Search\Request\FilterInterface as RequestFilterInterface; use Magento\Elasticsearch\Model\Adapter\FieldMapperInterface; @@ -21,7 +22,7 @@ class Term implements FilterInterface /** * @var FieldMapperInterface */ - protected $fieldMapper; + private $fieldMapper; /** * @var AttributeProvider @@ -41,12 +42,11 @@ class Term implements FilterInterface */ public function __construct( FieldMapperInterface $fieldMapper, - AttributeProvider $attributeAdapterProvider = null, + AttributeProvider $attributeAdapterProvider, array $integerTypeAttributes = [] ) { $this->fieldMapper = $fieldMapper; - $this->attributeAdapterProvider = $attributeAdapterProvider - ?? ObjectManager::getInstance()->get(AttributeProvider::class); + $this->attributeAdapterProvider = $attributeAdapterProvider; $this->integerTypeAttributes = array_merge($this->integerTypeAttributes, $integerTypeAttributes); } diff --git a/app/code/Magento/Elasticsearch/SearchAdapter/Mapper.php b/app/code/Magento/Elasticsearch/SearchAdapter/Mapper.php index fd6ae3424b9da..d76086ee2f809 100644 --- a/app/code/Magento/Elasticsearch/SearchAdapter/Mapper.php +++ b/app/code/Magento/Elasticsearch/SearchAdapter/Mapper.php @@ -3,22 +3,23 @@ * Copyright © Magento, Inc. All rights reserved. * See COPYING.txt for license details. */ +declare(strict_types=1); + namespace Magento\Elasticsearch\SearchAdapter; use Magento\Framework\Search\RequestInterface; -use Magento\Framework\Search\Request\QueryInterface as RequestQueryInterface; use Magento\Framework\Search\Request\Query\BoolExpression as BoolQuery; -use Magento\Framework\Search\Request\Query\Filter as FilterQuery; -use Magento\Framework\Search\Request\Query\Match as MatchQuery; use Magento\Elasticsearch\SearchAdapter\Query\Builder as QueryBuilder; use Magento\Elasticsearch\SearchAdapter\Query\Builder\Match as MatchQueryBuilder; use Magento\Elasticsearch\SearchAdapter\Filter\Builder as FilterBuilder; use Magento\Elasticsearch\Elasticsearch5\SearchAdapter\Mapper as Elasticsearch5Mapper; /** - * Mapper class + * Mapper class for Elasticsearch2 + * * @api * @since 100.1.0 + * @deprecated because of EOL for Elasticsearch2 */ class Mapper extends Elasticsearch5Mapper { diff --git a/app/code/Magento/Elasticsearch/SearchAdapter/Query/Builder.php b/app/code/Magento/Elasticsearch/SearchAdapter/Query/Builder.php index 0bea8683692f2..778e73e6518e8 100644 --- a/app/code/Magento/Elasticsearch/SearchAdapter/Query/Builder.php +++ b/app/code/Magento/Elasticsearch/SearchAdapter/Query/Builder.php @@ -3,11 +3,15 @@ * Copyright © Magento, Inc. All rights reserved. * See COPYING.txt for license details. */ +declare(strict_types=1); namespace Magento\Elasticsearch\SearchAdapter\Query; +use Magento\Elasticsearch\Model\Config; +use Magento\Elasticsearch\SearchAdapter\Query\Builder\Aggregation as AggregationBuilder; use Magento\Elasticsearch\SearchAdapter\Query\Builder\Sort; -use Magento\Framework\App\ObjectManager; +use Magento\Elasticsearch\SearchAdapter\SearchIndexNameResolver; +use Magento\Framework\App\ScopeResolverInterface; use Magento\Framework\Search\RequestInterface; use Magento\Elasticsearch\Elasticsearch5\SearchAdapter\Query\Builder as Elasticsearch5Builder; @@ -24,6 +28,24 @@ class Builder extends Elasticsearch5Builder */ private $sortBuilder; + /** + * @param Config $clientConfig + * @param SearchIndexNameResolver $searchIndexNameResolver + * @param AggregationBuilder $aggregationBuilder + * @param ScopeResolverInterface $scopeResolver + * @param Sort $sortBuilder + */ + public function __construct( + Config $clientConfig, + SearchIndexNameResolver $searchIndexNameResolver, + AggregationBuilder $aggregationBuilder, + ScopeResolverInterface $scopeResolver, + Sort $sortBuilder + ) { + parent::__construct($clientConfig, $searchIndexNameResolver, $aggregationBuilder, $scopeResolver); + $this->sortBuilder = $sortBuilder; + } + /** * Set initial settings for query. * @@ -42,23 +64,10 @@ public function initQuery(RequestInterface $request) 'from' => $request->getFrom(), 'size' => $request->getSize(), 'fields' => ['_id', '_score'], - 'sort' => $this->getSortBuilder()->getSort($request), + 'sort' => $this->sortBuilder->getSort($request), 'query' => [], ], ]; return $searchQuery; } - - /** - * Get sort builder instance. - * - * @return Sort - */ - private function getSortBuilder() - { - if (null === $this->sortBuilder) { - $this->sortBuilder = ObjectManager::getInstance()->get(Sort::class); - } - return $this->sortBuilder; - } } diff --git a/app/code/Magento/Elasticsearch/SearchAdapter/Query/Builder/Match.php b/app/code/Magento/Elasticsearch/SearchAdapter/Query/Builder/Match.php index 8a44b58d35fb8..1012918b15a8b 100644 --- a/app/code/Magento/Elasticsearch/SearchAdapter/Query/Builder/Match.php +++ b/app/code/Magento/Elasticsearch/SearchAdapter/Query/Builder/Match.php @@ -9,7 +9,6 @@ use Magento\Elasticsearch\Model\Adapter\FieldMapper\Product\FieldProvider\FieldType\ResolverInterface as TypeResolver; use Magento\Elasticsearch\Model\Config; use Magento\Elasticsearch\SearchAdapter\Query\ValueTransformerPool; -use Magento\Framework\App\ObjectManager; use Magento\Framework\Search\Request\Query\BoolExpression; use Magento\Framework\Search\Request\QueryInterface as RequestQueryInterface; use Magento\Elasticsearch\Model\Adapter\FieldMapperInterface; @@ -30,13 +29,6 @@ class Match implements QueryInterface */ private $fieldMapper; - /** - * @deprecated - * @see \Magento\Elasticsearch\SearchAdapter\Query\ValueTransformer\TextTransformer - * @var PreprocessorInterface[] - */ - protected $preprocessorContainer; - /** * @var AttributeProvider */ @@ -58,29 +50,23 @@ class Match implements QueryInterface /** * @param FieldMapperInterface $fieldMapper - * @param PreprocessorInterface[] $preprocessorContainer - * @param AttributeProvider|null $attributeProvider - * @param TypeResolver|null $fieldTypeResolver - * @param ValueTransformerPool|null $valueTransformerPool - * @param Config|null $config + * @param AttributeProvider $attributeProvider + * @param TypeResolver $fieldTypeResolver + * @param ValueTransformerPool $valueTransformerPool + * @param Config $config */ public function __construct( FieldMapperInterface $fieldMapper, - array $preprocessorContainer, - AttributeProvider $attributeProvider = null, - TypeResolver $fieldTypeResolver = null, - ValueTransformerPool $valueTransformerPool = null, - Config $config = null + AttributeProvider $attributeProvider, + TypeResolver $fieldTypeResolver, + ValueTransformerPool $valueTransformerPool, + Config $config ) { $this->fieldMapper = $fieldMapper; - $this->preprocessorContainer = $preprocessorContainer; - $this->attributeProvider = $attributeProvider ?? ObjectManager::getInstance() - ->get(AttributeProvider::class); - $this->fieldTypeResolver = $fieldTypeResolver ?? ObjectManager::getInstance() - ->get(TypeResolver::class); - $this->valueTransformerPool = $valueTransformerPool ?? ObjectManager::getInstance() - ->get(ValueTransformerPool::class); - $this->config = $config ?? ObjectManager::getInstance()->get(Config::class); + $this->attributeProvider = $attributeProvider; + $this->fieldTypeResolver = $fieldTypeResolver; + $this->valueTransformerPool = $valueTransformerPool; + $this->config = $config; } /** @@ -183,20 +169,4 @@ protected function buildQueries(array $matches, array $queryValue) return $conditions; } - - /** - * Escape a value for special query characters such as ':', '(', ')', '*', '?', etc. - * - * @deprecated - * @see \Magento\Elasticsearch\SearchAdapter\Query\ValueTransformer\TextTransformer - * @param string $value - * @return string - */ - protected function escape($value) - { - $pattern = '/(\+|-|&&|\|\||!|\(|\)|\{|}|\[|]|\^|"|~|\*|\?|:|\\\)/'; - $replace = '\\\$1'; - - return preg_replace($pattern, $replace, $value); - } } diff --git a/app/code/Magento/Elasticsearch/SearchAdapter/Query/Preprocessor/Stopwords.php b/app/code/Magento/Elasticsearch/SearchAdapter/Query/Preprocessor/Stopwords.php index d8d1a071611c1..e21baa231b14b 100644 --- a/app/code/Magento/Elasticsearch/SearchAdapter/Query/Preprocessor/Stopwords.php +++ b/app/code/Magento/Elasticsearch/SearchAdapter/Query/Preprocessor/Stopwords.php @@ -5,12 +5,20 @@ */ namespace Magento\Elasticsearch\SearchAdapter\Query\Preprocessor; -use Magento\Framework\Filesystem\Directory\ReadFactory; use Magento\Elasticsearch\Model\Adapter\Index\Config\EsConfigInterface; -use Magento\Framework\Search\Adapter\Preprocessor\PreprocessorInterface; +use Magento\Framework\App\Cache\Type\Config; +use Magento\Framework\App\ObjectManager; +use Magento\Framework\Filesystem\Directory\ReadFactory; +use Magento\Framework\Locale\Resolver; use Magento\Framework\Module\Dir; +use Magento\Framework\Module\Dir\Reader; +use Magento\Framework\Search\Adapter\Preprocessor\PreprocessorInterface; +use Magento\Framework\Serialize\SerializerInterface; +use Magento\Store\Model\StoreManagerInterface; /** + * Elasticsearch stopwords preprocessor + * * @api * @since 100.1.0 */ @@ -27,13 +35,13 @@ class Stopwords implements PreprocessorInterface const STOPWORDS_FILE_MODIFICATION_TIME_GAP = 900; /** - * @var \Magento\Store\Model\StoreManagerInterface + * @var StoreManagerInterface * @since 100.1.0 */ protected $storeManager; /** - * @var \Magento\Framework\Locale\Resolver + * @var Resolver * @since 100.1.0 */ protected $localeResolver; @@ -45,7 +53,7 @@ class Stopwords implements PreprocessorInterface protected $readFactory; /** - * @var \Magento\Framework\App\Cache\Type\Config + * @var Config * @since 100.1.0 */ protected $configCache; @@ -57,7 +65,7 @@ class Stopwords implements PreprocessorInterface protected $esConfig; /** - * @var \Magento\Framework\Module\Dir\Reader + * @var Reader * @since 100.1.0 */ protected $moduleDirReader; @@ -73,31 +81,33 @@ class Stopwords implements PreprocessorInterface private $stopwordsDirectory; /** - * @var \Magento\Framework\Serialize\SerializerInterface + * @var SerializerInterface */ private $serializer; /** * Initialize dependencies. * - * @param \Magento\Store\Model\StoreManagerInterface $storeManager - * @param \Magento\Framework\Locale\Resolver $localeResolver + * @param StoreManagerInterface $storeManager + * @param Resolver $localeResolver * @param ReadFactory $readFactory - * @param \Magento\Framework\App\Cache\Type\Config $configCache + * @param Config $configCache * @param EsConfigInterface $esConfig - * @param \Magento\Framework\Module\Dir\Reader $moduleDirReader + * @param Reader $moduleDirReader * @param string $stopwordsModule * @param string $stopwordsDirectory + * @param SerializerInterface|null $serializer */ public function __construct( - \Magento\Store\Model\StoreManagerInterface $storeManager, - \Magento\Framework\Locale\Resolver $localeResolver, + StoreManagerInterface $storeManager, + Resolver $localeResolver, ReadFactory $readFactory, - \Magento\Framework\App\Cache\Type\Config $configCache, + Config $configCache, EsConfigInterface $esConfig, - \Magento\Framework\Module\Dir\Reader $moduleDirReader, + Reader $moduleDirReader, $stopwordsModule = '', - $stopwordsDirectory = '' + $stopwordsDirectory = '', + ?SerializerInterface $serializer = null ) { $this->storeManager = $storeManager; $this->localeResolver = $localeResolver; @@ -107,10 +117,11 @@ public function __construct( $this->moduleDirReader = $moduleDirReader; $this->stopwordsModule = $stopwordsModule; $this->stopwordsDirectory = $stopwordsDirectory; + $this->serializer = $serializer ?: ObjectManager::getInstance()->get(SerializerInterface::class); } /** - * {@inheritdoc} + * @inheritDoc * @since 100.1.0 */ public function process($query) @@ -136,11 +147,11 @@ protected function getStopwordsList() $fileStats = $source->stat($filename); if (((time() - $fileStats['mtime']) > self::STOPWORDS_FILE_MODIFICATION_TIME_GAP) && ($cachedValue = $this->configCache->load(self::CACHE_ID))) { - $stopwords = $this->getSerializer()->unserialize($cachedValue); + $stopwords = $this->serializer->unserialize($cachedValue); } else { $fileContent = $source->readFile($filename); $stopwords = explode("\n", $fileContent); - $this->configCache->save($this->getSerializer()->serialize($stopwords), self::CACHE_ID); + $this->configCache->save($this->serializer->serialize($stopwords), self::CACHE_ID); } return $stopwords; } @@ -160,19 +171,4 @@ protected function getStopwordsFile() $stopwordsFile = isset($stopwordsInfo[$locale]) ? $stopwordsInfo[$locale] : $stopwordsInfo['default']; return $stopwordsFile; } - - /** - * Get serializer - * - * @return \Magento\Framework\Serialize\SerializerInterface - * @deprecated 100.2.0 - */ - private function getSerializer() - { - if (null === $this->serializer) { - $this->serializer = \Magento\Framework\App\ObjectManager::getInstance() - ->get(\Magento\Framework\Serialize\SerializerInterface::class); - } - return $this->serializer; - } } diff --git a/app/code/Magento/Elasticsearch/Test/Mftf/ActionGroup/AssertSearchResultActionGroup.xml b/app/code/Magento/Elasticsearch/Test/Mftf/ActionGroup/AssertSearchResultActionGroup.xml new file mode 100644 index 0000000000000..5a2aba8df9f91 --- /dev/null +++ b/app/code/Magento/Elasticsearch/Test/Mftf/ActionGroup/AssertSearchResultActionGroup.xml @@ -0,0 +1,26 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + /** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +--> + +<actionGroups xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/actionGroupSchema.xsd"> + <actionGroup name="AssertSearchResultActionGroup"> + <annotations> + <description>Check search result on Storefront</description> + </annotations> + <arguments> + <argument name="keyword" defaultValue="Simple Product A" type="string"/> + <argument name="product" type="string" defaultValue="Simple Product A"/> + </arguments> + <fillField userInput="{{keyword}}" selector="{{StorefrontQuickSearchSection.searchPhrase}}" stepKey="fillSearchBar"/> + <waitForPageLoad stepKey="waitForSearchButton"/> + <click selector="{{StorefrontQuickSearchSection.searchButton}}" stepKey="clickSearchButton"/> + <waitForPageLoad stepKey="waitForPageLoad"/> + <seeInCurrentUrl url="{{StorefrontCatalogSearchPage.url}}" stepKey="checkUrl"/> + <seeElement selector="{{StorefrontCategoryMainSection.specifiedProductItemInfo(product)}}" stepKey="foundProductAOnPage"/> + </actionGroup> +</actionGroups> \ No newline at end of file diff --git a/app/code/Magento/Elasticsearch/Test/Mftf/ActionGroup/ModifyCustomAttributeValueActionGroup.xml b/app/code/Magento/Elasticsearch/Test/Mftf/ActionGroup/ModifyCustomAttributeValueActionGroup.xml new file mode 100644 index 0000000000000..6389ca31ba534 --- /dev/null +++ b/app/code/Magento/Elasticsearch/Test/Mftf/ActionGroup/ModifyCustomAttributeValueActionGroup.xml @@ -0,0 +1,25 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + /** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +--> + +<actionGroups xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/actionGroupSchema.xsd"> + <actionGroup name="ModifyCustomAttributeValueActionGroup"> + <annotations> + <description>Update value for custom attribute</description> + </annotations> + <arguments> + <argument name="attributeValue" defaultValue="3.5" type="string"/> + <argument name="product" type="string" defaultValue="SKU0001"/> + </arguments> + <click selector="{{AdminProductGridSection.productRowBySku(product)}}" stepKey="clickOnSimpleProductRow"/> + <waitForPageLoad stepKey="waitForPageLoad1"/> + <click selector="{{AdminProductMultiselectAttributeSection.option(attributeValue)}}" stepKey="selectFirstOption"/> + <click selector="{{AdminProductFormSection.save}}" stepKey="save"/> + <waitForPageLoad stepKey="waitForProductSaved"/> + </actionGroup> +</actionGroups> diff --git a/app/code/Magento/Elasticsearch/Test/Mftf/Data/ConfigData.xml b/app/code/Magento/Elasticsearch/Test/Mftf/Data/ConfigData.xml new file mode 100644 index 0000000000000..5a1118d079158 --- /dev/null +++ b/app/code/Magento/Elasticsearch/Test/Mftf/Data/ConfigData.xml @@ -0,0 +1,16 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + /** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +--> +<entities xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:noNamespaceSchemaLocation="urn:magento:mftf:DataGenerator/etc/dataProfileSchema.xsd"> + <entity name="SearchEngineElasticsearchConfigData"> + <data key="path">catalog/search/engine</data> + <data key="scope_id">1</data> + <data key="label">Elasticsearch {{_ENV.ELASTICSEARCH_VERSION}}.0+</data> + <data key="value">elasticsearch{{_ENV.ELASTICSEARCH_VERSION}}</data> + </entity> +</entities> diff --git a/app/code/Magento/Elasticsearch6/Test/Mftf/Suite/SearchEngineElasticsearchSuite.xml b/app/code/Magento/Elasticsearch/Test/Mftf/Suite/SearchEngineElasticsearchSuite.xml similarity index 100% rename from app/code/Magento/Elasticsearch6/Test/Mftf/Suite/SearchEngineElasticsearchSuite.xml rename to app/code/Magento/Elasticsearch/Test/Mftf/Suite/SearchEngineElasticsearchSuite.xml diff --git a/app/code/Magento/Elasticsearch/Test/Mftf/Test/ProductQuickSearchUsingElasticSearchTest.xml b/app/code/Magento/Elasticsearch/Test/Mftf/Test/ProductQuickSearchUsingElasticSearchTest.xml index 1a03ae331f077..03999a70322b1 100644 --- a/app/code/Magento/Elasticsearch/Test/Mftf/Test/ProductQuickSearchUsingElasticSearchTest.xml +++ b/app/code/Magento/Elasticsearch/Test/Mftf/Test/ProductQuickSearchUsingElasticSearchTest.xml @@ -11,12 +11,13 @@ <test name="ProductQuickSearchUsingElasticSearchTest"> <annotations> <features value="Search"/> - <stories value="Quick Search of products on Storefront when ES 5.0+ is enabled"/> + <stories value="Quick Search of products on Storefront when ES 5.x is enabled"/> <title value="Product quick search doesn't throw exception after ES is chosen as search engine"/> <description value="Verify no elastic search exception is thrown when searching for product before catalogsearch reindexing"/> - <severity value="CRITICAL"/> + <severity value="BLOCKER"/> <testCaseId value="MAGETWO-94995"/> <group value="Catalog"/> + <group value="SearchEngineElasticsearch" /> </annotations> <before> <createData entity="SimpleSubCategory" stepKey="categoryFirst"/> @@ -28,21 +29,21 @@ <after> <deleteData createDataKey="simpleProduct1" stepKey="deleteSimpleProduct1"/> <deleteData createDataKey="categoryFirst" stepKey="deleteCategory"/> - <actionGroup ref="ResetSearchEngineConfigurationActionGroup" stepKey="resetCatalogSearchConfiguration"/> - <actionGroup ref="UpdateIndexerOnSaveActionGroup" stepKey="resetIndexerBackToOriginalState"> + <comment userInput="The test was moved to elasticsearch suite" stepKey="resetCatalogSearchConfiguration"/> + <actionGroup ref="updateIndexerOnSave" stepKey="resetIndexerBackToOriginalState"> <argument name="indexerName" value="catalogsearch_fulltext"/> </actionGroup> - <actionGroup ref="logout" stepKey="logoutOfAdmin"/> + <actionGroup ref="AdminLogoutActionGroup" stepKey="logoutOfAdmin"/> </after> - <actionGroup ref="LoginAsAdmin" stepKey="loginAsAdmin"/> + <actionGroup ref="AdminLoginActionGroup" stepKey="loginAsAdmin"/> <comment userInput="Change Catalog search engine option to Elastic Search 5.0+" stepKey="chooseElasticSearch5"/> - <actionGroup ref="ChooseElasticSearchAsSearchEngineActionGroup" stepKey="chooseES5"/> + <comment userInput="The test was moved to elasticsearch suite" stepKey="chooseES5"/> <actionGroup ref="ClearPageCacheActionGroup" stepKey="clearing"/> <actionGroup ref="UpdateIndexerByScheduleActionGroup" stepKey="updateAnIndexerBySchedule"> <argument name="indexerName" value="catalogsearch_fulltext"/> </actionGroup> - <actionGroup ref="logout" stepKey="logoutOfAdmin"/> + <actionGroup ref="AdminLogoutActionGroup" stepKey="logoutOfAdmin"/> <!--Navigate to storefront and do a quick search for the product --> <comment userInput="Navigate to Storefront to check if quick search works" stepKey="commentCheckQuickSearch" /> <amOnPage url="{{StorefrontHomePage.url}}" stepKey="goToHomePage"/> @@ -55,6 +56,6 @@ <seeInTitle userInput="Search results for: 'Simple'" stepKey="assertQuickSearchTitle"/> <see userInput="Search results for: 'Simple'" selector="{{StorefrontCatalogSearchMainSection.SearchTitle}}" stepKey="assertQuickSearchName"/> <comment userInput="End of searching products" stepKey="endOfSearchingProducts"/> - <actionGroup ref="LoginAsAdmin" stepKey="loginAsAdmin2"/> + <actionGroup ref="AdminLoginActionGroup" stepKey="loginAsAdmin2"/> </test> </tests> diff --git a/app/code/Magento/Elasticsearch/Test/Mftf/Test/StorefrontCheckAdvancedSearchOnElasticSearchTest.xml b/app/code/Magento/Elasticsearch/Test/Mftf/Test/StorefrontCheckAdvancedSearchOnElasticSearchTest.xml new file mode 100644 index 0000000000000..a94a6a2e3d133 --- /dev/null +++ b/app/code/Magento/Elasticsearch/Test/Mftf/Test/StorefrontCheckAdvancedSearchOnElasticSearchTest.xml @@ -0,0 +1,154 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + /** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +--> + +<tests xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/testSchema.xsd"> + <test name="StorefrontCheckAdvancedSearchOnElasticSearchTest"> + <annotations> + <features value="CatalogSearch"/> + <stories value="Storefront Search"/> + <title value="Check Advanced Search on ElasticSearch"/> + <description value="Check Advanced Search on ElasticSearch"/> + <severity value="CRITICAL"/> + <testCaseId value="MC-31745"/> + <group value="SearchEngineElasticsearch"/> + <group value="configurableProduct"/> + <group value="catalog_search"/> + </annotations> + + <before> + <!--Delete all product if exists--> + <actionGroup ref="AdminLoginActionGroup" stepKey="loginAsAdmin"/> + <actionGroup ref="AdminOpenProductIndexPageActionGroup" stepKey="navigateToProductIndexPage"/> + <actionGroup ref="DeleteProductsIfTheyExistActionGroup" stepKey="deleteAllProducts"/> + + <actionGroup ref="CreateApiConfigurableProductWithDescriptionActionGroup" stepKey="createConfigurableProduct"> + <argument name="productName" value="Product A"/> + </actionGroup> + <actionGroup ref="CreateApiConfigurableProductWithDescriptionActionGroup" stepKey="createConfigurableProductTwo"> + <argument name="productName" value="Product1234"/> + </actionGroup> + + <!-- Reindex invalidated indices after product attribute has been created/deleted --> + <magentoCron groups="index" stepKey="reindexInvalidatedIndices"/> + <magentoCLI command="cache:flush" arguments="full_page" stepKey="flushFullPageCache"/> + </before> + + <after> + <!-- Delete configurable products data --> + <deleteData createDataKey="createConfigChildProduct1CreateConfigurableProduct" stepKey="deleteConfigChildProduct1"/> + <deleteData createDataKey="createConfigChildProduct2CreateConfigurableProduct" stepKey="deleteConfigChildProduct2"/> + <deleteData createDataKey="createConfigProductCreateConfigurableProduct" stepKey="deleteConfigProduct"/> + <deleteData createDataKey="createConfigProductAttributeCreateConfigurableProduct" stepKey="deleteConfigProductAttribute"/> + + <deleteData createDataKey="createConfigChildProduct1CreateConfigurableProductTwo" stepKey="deleteConfigChildProduct1ForSecondProduct"/> + <deleteData createDataKey="createConfigChildProduct2CreateConfigurableProductTwo" stepKey="deleteConfigChildProduct2ForSecondProduct"/> + <deleteData createDataKey="createConfigProductCreateConfigurableProductTwo" stepKey="deleteConfigProductTwo"/> + <deleteData createDataKey="createConfigProductAttributeCreateConfigurableProductTwo" stepKey="deleteConfigProductAttributeForSecondProduct"/> + + <!-- Reindex invalidated indices after product attribute has been created/deleted --> + <magentoCron groups="index" stepKey="reindexInvalidatedIndices"/> + <actionGroup ref="AdminLogoutActionGroup" stepKey="logoutFromAdmin"/> + </after> + + <!-- Navigate to Frontend --> + <actionGroup ref="StorefrontOpenHomePageActionGroup" stepKey="goToStorefront"/> + + <!-- Click "Advanced Search" --> + <actionGroup ref="StorefrontOpenAdvancedSearchActionGroup" stepKey="openAdvancedSearch"/> + + <!-- Fill Configurable name in to field. Click "Search" button and assert product present--> + <actionGroup ref="StorefrontFillFormAdvancedSearchActionGroup" stepKey="searchConfigurableProductByName"> + <argument name="productName" value="$createConfigProductCreateConfigurableProduct.name$"/> + </actionGroup> + <actionGroup ref="StorefrontCheckAdvancedSearchResultActionGroup" stepKey="storefrontCheckAdvancedSearchResult"/> + <actionGroup ref="AssertStorefrontProductIsPresentOnCategoryPageActionGroup" stepKey="assertConfigurableProductIsPresentOnCategoryPage"> + <argument name="productName" value="$createConfigProductCreateConfigurableProduct.name$"/> + </actionGroup> + + <actionGroup ref="StorefrontOpenAdvancedSearchActionGroup" stepKey="goToStoreViewAdvancedCatalogSearch"/> + <!-- Fill Configurable Two name in to field. Click "Search" button and assert product present--> + <actionGroup ref="StorefrontFillFormAdvancedSearchActionGroup" stepKey="searchConfigurableProductTwoByName"> + <argument name="productName" value="$createConfigProductCreateConfigurableProductTwo.name$"/> + </actionGroup> + <actionGroup ref="StorefrontCheckAdvancedSearchResultActionGroup" stepKey="checkResultSearchConfigurableProductByName"/> + <actionGroup ref="AssertStorefrontProductIsPresentOnCategoryPageActionGroup" stepKey="assertConfigurableProductTwoIsPresentOnCategoryPage"> + <argument name="productName" value="$createConfigProductCreateConfigurableProductTwo.name$"/> + </actionGroup> + + <actionGroup ref="StorefrontOpenAdvancedSearchActionGroup" stepKey="goToAdvancedCatalogSearchPage"/> + <!-- Fill Configurable partial name in to field. Click "Search" button and assert product present--> + <actionGroup ref="StorefrontFillFormAdvancedSearchActionGroup" stepKey="searchConfigurableProductByPartialName"> + <argument name="productName" value="Product"/> + </actionGroup> + <actionGroup ref="StorefrontCheckAdvancedSearchResultActionGroup" stepKey="checkResultSearchConfigurableProductByPartialName"/> + <actionGroup ref="AssertStorefrontProductIsPresentOnCategoryPageActionGroup" stepKey="assertConfigurableProductOneIsPresentAfterSearchByPartialName"> + <argument name="productName" value="$createConfigProductCreateConfigurableProduct.name$"/> + </actionGroup> + <actionGroup ref="AssertStorefrontProductIsPresentOnCategoryPageActionGroup" stepKey="assertConfigurableProductTwoIsPresentAfterSearchByPartialName"> + <argument name="productName" value="$createConfigProductCreateConfigurableProductTwo.name$"/> + </actionGroup> + + <actionGroup ref="StorefrontOpenAdvancedSearchActionGroup" stepKey="goToAdvancedSearchForSearchProductByDescription"/> + <!-- Fill Configurable short description in to field. Click "Search" button and assert product present--> + <actionGroup ref="StorefrontFillFormAdvancedSearchActionGroup" stepKey="searchConfigurableProductByDescription"> + <argument name="description" value="$createConfigProductCreateConfigurableProduct.product[custom_attributes][0][value]$"/> + </actionGroup> + <actionGroup ref="StorefrontCheckAdvancedSearchResultActionGroup" stepKey="storefrontCheckAdvancedSearchResultAfterSearchByDescription"/> + <actionGroup ref="AssertStorefrontProductIsPresentOnCategoryPageActionGroup" stepKey="assertConfigurableProductIsPresentAfterSearchByDescription"> + <argument name="productName" value="$createConfigProductCreateConfigurableProduct.name$"/> + </actionGroup> + + <actionGroup ref="StorefrontOpenAdvancedSearchActionGroup" stepKey="goToAdvancedSearchForSearchProductByShortDescription"/> + <!-- Fill Configurable short description in to field. Click "Search" button and assert product present--> + <actionGroup ref="StorefrontFillFormAdvancedSearchActionGroup" stepKey="searchConfigurableProductByShortDescription"> + <argument name="short_description" value="$createConfigProductCreateConfigurableProduct.product[custom_attributes][1][value]$"/> + </actionGroup> + <actionGroup ref="StorefrontCheckAdvancedSearchResultActionGroup" stepKey="storefrontCheckAdvancedSearchResultAfterSearchByShortDescription"/> + <actionGroup ref="AssertStorefrontProductIsPresentOnCategoryPageActionGroup" stepKey="assertConfigurableProductIsPresentAfterSearchByShortDescription"> + <argument name="productName" value="$createConfigProductCreateConfigurableProduct.name$"/> + </actionGroup> + + <actionGroup ref="StorefrontOpenAdvancedSearchActionGroup" stepKey="goToAdvancedSearchForSearchProductsByPrice"/> + <!-- Fill Configurable price in to fields. Click "Search" button and assert product present--> + <actionGroup ref="StorefrontFillFormAdvancedSearchActionGroup" stepKey="searchConfigurableProductByPrice"> + <argument name="price_from" value="40"/> + <argument name="price_to" value="123"/> + </actionGroup> + <actionGroup ref="StorefrontCheckAdvancedSearchResultActionGroup" stepKey="storefrontCheckAdvancedSearchResultAfterSearchByPrice"/> + <actionGroup ref="AssertStorefrontProductIsPresentOnCategoryPageActionGroup" stepKey="assertConfigurableProductIsPresentAfterSearchByPrice"> + <argument name="productName" value="$createConfigProductCreateConfigurableProduct.name$"/> + </actionGroup> + <actionGroup ref="AssertStorefrontProductIsPresentOnCategoryPageActionGroup" stepKey="assertConfigurableProductTwoIsPresentAfterSearchByPrice"> + <argument name="productName" value="$createConfigProductCreateConfigurableProductTwo.name$"/> + </actionGroup> + + <actionGroup ref="StorefrontOpenAdvancedSearchActionGroup" stepKey="goToAdvancedCatalogSearchPageForSearchByDescriptionAndAttribute"/> + <!-- Fill Configurable description in to field and select attribute. Click "Search" button and assert product present--> + <actionGroup ref="StorefrontFillFormAdvancedSearchWithCustomDropDownAttributeActionGroup" stepKey="searchConfigurableProductByDescriptionAndAttribute"> + <argument name="description" value="$createConfigProductCreateConfigurableProduct.product[custom_attributes][0][value]$"/> + <argument name="attributeCode" value="$createConfigProductAttributeCreateConfigurableProduct.attribute[attribute_code]$"/> + <argument name="optionName" value="$createConfigProductAttributeOption1CreateConfigurableProduct.option[store_labels][0][label]$"/> + </actionGroup> + <actionGroup ref="StorefrontCheckAdvancedSearchResultActionGroup" stepKey="storefrontCheckAdvancedSearchResultAfterSearchByDescriptionAndAttribute"/> + <actionGroup ref="AssertStorefrontProductIsPresentOnCategoryPageActionGroup" stepKey="assertConfigurableProductIsPresentAfterSearchByDescriptionAndAttribute"> + <argument name="productName" value="$createConfigProductCreateConfigurableProduct.name$"/> + </actionGroup> + + <actionGroup ref="StorefrontOpenAdvancedSearchActionGroup" stepKey="goToAdvancedSearchForSearchProductByDescriptionAndShortDescription"/> + <!-- Fill Configurable description and short description in to fields. Click "Search" button and assert product present--> + <actionGroup ref="StorefrontFillFormAdvancedSearchActionGroup" stepKey="searchConfigurableProductByDescriptionAndShortDescription"> + <argument name="description" value="$createConfigProductCreateConfigurableProduct.product[custom_attributes][0][value]$"/> + <argument name="short_description" value="$createConfigProductCreateConfigurableProduct.product[custom_attributes][1][value]$"/> + </actionGroup> + <actionGroup ref="StorefrontCheckAdvancedSearchResultActionGroup" stepKey="storefrontCheckAdvancedSearchResultAfterSearchByDescriptionAndShortDescription"/> + <actionGroup ref="AssertStorefrontProductIsPresentOnCategoryPageActionGroup" stepKey="assertConfigurableProductIsPresentAfterSearchByDescriptionAndShortDescription"> + <argument name="productName" value="$createConfigProductCreateConfigurableProduct.name$"/> + </actionGroup> + </test> +</tests> diff --git a/app/code/Magento/Elasticsearch/Test/Mftf/Test/StorefrontProductQuickSearchWithDecimalAttributeUsingElasticSearchTest.xml b/app/code/Magento/Elasticsearch/Test/Mftf/Test/StorefrontProductQuickSearchWithDecimalAttributeUsingElasticSearchTest.xml new file mode 100644 index 0000000000000..d9988577009bc --- /dev/null +++ b/app/code/Magento/Elasticsearch/Test/Mftf/Test/StorefrontProductQuickSearchWithDecimalAttributeUsingElasticSearchTest.xml @@ -0,0 +1,98 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + /** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +--> + +<tests xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/testSchema.xsd"> + <test name="StorefrontProductQuickSearchWithDecimalAttributeUsingElasticSearchTest"> + <annotations> + <features value="Search"/> + <stories value="Elasticsearch 7.x.x Upgrade"/> + <title value="Search decimal attribute with ElasticSearch"/> + <description value="User should be able to search decimal attribute using ElasticSearch"/> + <severity value="CRITICAL"/> + <testCaseId value="MC-31239"/> + <group value="SearchEngineElasticsearch" /> + </annotations> + <before> + <!-- Create product attribute with 3 options --> + <createData entity="multipleSelectProductAttribute" stepKey="customAttribute"/> + <createData entity="ProductAttributeOption10" stepKey="option1"> + <requiredEntity createDataKey="customAttribute"/> + </createData> + <createData entity="ProductAttributeOption11" stepKey="option2"> + <requiredEntity createDataKey="customAttribute"/> + </createData> + <createData entity="ProductAttributeOption12" stepKey="option3"> + <requiredEntity createDataKey="customAttribute"/> + </createData> + + <!-- Add custom attribute to Default Set --> + <createData entity="AddToDefaultSet" stepKey="addToDefaultAttributeSet"> + <requiredEntity createDataKey="customAttribute"/> + </createData> + + <!-- Create subcategory --> + <createData entity="SimpleSubCategory" stepKey="newCategory"/> + <createData entity="SimpleProduct" stepKey="product1"> + <requiredEntity createDataKey="newCategory"/> + <requiredEntity createDataKey="customAttribute"/> + </createData> + <createData entity="ApiSimpleProduct" stepKey="product2"> + <requiredEntity createDataKey="newCategory"/> + <requiredEntity createDataKey="customAttribute"/> + </createData> + <actionGroup ref="AdminLoginActionGroup" stepKey="loginAsAdmin"/> + </before> + <after> + <deleteData createDataKey="product1" stepKey="deleteProduct1"/> + <deleteData createDataKey="product2" stepKey="deleteProduct2"/> + <deleteData createDataKey="newCategory" stepKey="deleteCategory"/> + <!--Delete attribute--> + <deleteData createDataKey="customAttribute" stepKey="deleteCustomAttribute"/> + <!--Reindex and clear cache--> + <magentoCLI command="indexer:reindex" stepKey="reindex"/> + <magentoCLI command="cache:clean" stepKey="cleanCache"/> + <actionGroup ref="AdminLogoutActionGroup" stepKey="logoutOfAdmin"/> + </after> + <!--Navigate to backend and update value for custom attribute --> + <actionGroup ref="SearchForProductOnBackendActionGroup" stepKey="searchForSimpleProduct"> + <argument name="product" value="$$product1$$"/> + </actionGroup> + <actionGroup ref="ModifyCustomAttributeValueActionGroup" stepKey="ModifyCustomAttributeValueProduct1"> + <argument name="attributeValue" value="3.5"/> + <argument name="product" value="$$product1.sku$$"/> + </actionGroup> + + <actionGroup ref="SearchForProductOnBackendActionGroup" stepKey="searchForApiSimpleProduct"> + <argument name="product" value="$$product2$$"/> + </actionGroup> + <actionGroup ref="ModifyCustomAttributeValueActionGroup" stepKey="ModifyCustomAttributeValueProduct2"> + <argument name="attributeValue" value="10.12"/> + <argument name="product" value="$$product2.sku$$"/> + </actionGroup> + + <!--Reindex and clear cache--> + <magentoCLI command="indexer:reindex" stepKey="reindex"/> + <magentoCLI command="cache:clean" stepKey="cleanCache"/> + <actionGroup ref="StorefrontOpenHomePageActionGroup" stepKey="goToHomePage"/> + + <!-- Navigate to Storefront and search --> + <actionGroup ref="AssertSearchResultActionGroup" stepKey="assertSearchResult0"> + <argument name="keyword" value="$$product1.name$$"/> + <argument name="product" value="$$product1.name$$"/> + </actionGroup> + <actionGroup ref="AssertSearchResultActionGroup" stepKey="assertSearchResult1"> + <argument name="keyword" value="3.5"/> + <argument name="product" value="$$product1.name$$"/> + </actionGroup> + <actionGroup ref="AssertSearchResultActionGroup" stepKey="assertSearchResult2"> + <argument name="keyword" value="10.12"/> + <argument name="product" value="$$product2.name$$"/> + </actionGroup> + </test> +</tests> diff --git a/app/code/Magento/Elasticsearch/Test/Unit/Model/Adapter/Index/IndexNameResolverTest.php b/app/code/Magento/Elasticsearch/Test/Unit/Elasticsearch5/Model/Adapter/Index/IndexNameResolverTest.php similarity index 97% rename from app/code/Magento/Elasticsearch/Test/Unit/Model/Adapter/Index/IndexNameResolverTest.php rename to app/code/Magento/Elasticsearch/Test/Unit/Elasticsearch5/Model/Adapter/Index/IndexNameResolverTest.php index 3a294ba20e206..e7e4faadd9815 100644 --- a/app/code/Magento/Elasticsearch/Test/Unit/Model/Adapter/Index/IndexNameResolverTest.php +++ b/app/code/Magento/Elasticsearch/Test/Unit/Elasticsearch5/Model/Adapter/Index/IndexNameResolverTest.php @@ -3,13 +3,13 @@ * Copyright © Magento, Inc. All rights reserved. * See COPYING.txt for license details. */ -namespace Magento\Elasticsearch\Test\Unit\Model\Adapter\Index; +namespace Magento\Elasticsearch\Test\Unit\Elasticsearch5\Model\Adapter\Index; use Magento\Elasticsearch\Model\Adapter\Index\IndexNameResolver; use Psr\Log\LoggerInterface; use Magento\Elasticsearch\SearchAdapter\ConnectionManager; use Magento\AdvancedSearch\Model\Client\ClientOptionsInterface; -use Magento\Elasticsearch\Model\Client\Elasticsearch as ElasticsearchClient; +use Magento\AdvancedSearch\Model\Client\ClientInterface as ElasticsearchClient; use Magento\Framework\TestFramework\Unit\Helper\ObjectManager as ObjectManagerHelper; /** @@ -112,7 +112,7 @@ protected function setUp() $elasticsearchClientMock->expects($this->any()) ->method('indices') ->willReturn($indicesMock); - $this->client = $this->getMockBuilder(\Magento\Elasticsearch\Model\Client\Elasticsearch::class) + $this->client = $this->getMockBuilder(\Magento\Elasticsearch\Elasticsearch5\Model\Client\Elasticsearch::class) ->setConstructorArgs([ 'options' => $this->getClientOptions(), 'elasticsearchClient' => $elasticsearchClientMock diff --git a/app/code/Magento/Elasticsearch/Test/Unit/Elasticsearch5/Model/Client/ElasticsearchTest.php b/app/code/Magento/Elasticsearch/Test/Unit/Elasticsearch5/Model/Client/ElasticsearchTest.php index 99fd416b5cd3e..69442631a87bf 100644 --- a/app/code/Magento/Elasticsearch/Test/Unit/Elasticsearch5/Model/Client/ElasticsearchTest.php +++ b/app/code/Magento/Elasticsearch/Test/Unit/Elasticsearch5/Model/Client/ElasticsearchTest.php @@ -3,20 +3,20 @@ * Copyright © Magento, Inc. All rights reserved. * See COPYING.txt for license details. */ +declare(strict_types=1); namespace Magento\Elasticsearch\Test\Unit\Elasticsearch5\Model\Client; use Magento\Elasticsearch\Elasticsearch5\Model\Client\Elasticsearch; -use Magento\Elasticsearch\Model\Client\Elasticsearch as ElasticsearchClient; use Magento\Framework\TestFramework\Unit\Helper\ObjectManager as ObjectManagerHelper; /** - * Class ElasticsearchTest + * Test elasticsearch client methods. */ class ElasticsearchTest extends \PHPUnit\Framework\TestCase { /** - * @var ElasticsearchClient + * @var \Magento\Elasticsearch\Elasticsearch5\Model\Client\Elasticsearch */ protected $model; @@ -93,34 +93,6 @@ protected function setUp(): void ); } - /** - * @expectedException \Magento\Framework\Exception\LocalizedException - */ - public function testConstructorOptionsException() - { - $result = $this->objectManager->getObject( - \Magento\Elasticsearch\Model\Client\Elasticsearch::class, - [ - 'options' => [] - ] - ); - $this->assertNotNull($result); - } - - /** - * Test client creation from the list of options - */ - public function testConstructorWithOptions() - { - $result = $this->objectManager->getObject( - \Magento\Elasticsearch\Model\Client\Elasticsearch::class, - [ - 'options' => $this->getOptions() - ] - ); - $this->assertNotNull($result); - } - /** * Test ping functionality */ @@ -148,23 +120,6 @@ public function testTestConnectionFalse() $this->assertEquals(true, $this->model->testConnection()); } - /** - * Test validation of connection parameters - */ - public function testTestConnectionPing() - { - $this->model = $this->objectManager->getObject( - \Magento\Elasticsearch\Model\Client\Elasticsearch::class, - [ - 'options' => $this->getEmptyIndexOption(), - 'elasticsearchClient' => $this->elasticsearchClientMock - ] - ); - - $this->model->ping(); - $this->assertEquals(true, $this->model->testConnection()); - } - /** * Test bulkQuery() method */ @@ -379,7 +334,7 @@ public function testAddFieldsMapping() 'match_mapping_type' => 'string', 'mapping' => [ 'type' => 'integer', - 'index' => false + 'index' => true ], ], ], @@ -389,7 +344,7 @@ public function testAddFieldsMapping() 'match_mapping_type' => 'string', 'mapping' => [ 'type' => 'text', - 'index' => false, + 'index' => true, ], ], ], @@ -449,7 +404,7 @@ public function testAddFieldsMappingFailure() 'match_mapping_type' => 'string', 'mapping' => [ 'type' => 'integer', - 'index' => false + 'index' => true, ], ], ], @@ -459,7 +414,7 @@ public function testAddFieldsMappingFailure() 'match_mapping_type' => 'string', 'mapping' => [ 'type' => 'text', - 'index' => false, + 'index' => true, ], ], ] @@ -563,9 +518,9 @@ public function testQuery() $query = 'test phrase query'; $this->elasticsearchClientMock->expects($this->once()) ->method('search') - ->with($query) + ->with([$query]) ->willReturn([]); - $this->assertEquals([], $this->model->query($query)); + $this->assertEquals([], $this->model->query([$query])); } /** diff --git a/app/code/Magento/Elasticsearch/Test/Unit/Model/Adapter/Container/AttributeTest.php b/app/code/Magento/Elasticsearch/Test/Unit/Model/Adapter/Container/AttributeTest.php deleted file mode 100644 index ca5d570d735f5..0000000000000 --- a/app/code/Magento/Elasticsearch/Test/Unit/Model/Adapter/Container/AttributeTest.php +++ /dev/null @@ -1,237 +0,0 @@ -<?php -/** - * Copyright © Magento, Inc. All rights reserved. - * See COPYING.txt for license details. - */ -namespace Magento\Elasticsearch\Test\Unit\Model\Adapter\Container; - -use Magento\Catalog\Model\ResourceModel\Product\Attribute\Collection; -use Magento\Framework\TestFramework\Unit\Helper\ObjectManager as ObjectManagerHelper; - -/** - * Unit test for Magento\Elasticsearch\Model\Adapter\Container\Attribute - */ -class AttributeTest extends \PHPUnit\Framework\TestCase -{ - /** - * @var \Magento\Elasticsearch\Model\Adapter\Container\Attribute - */ - private $attribute; - - /** - * @var Collection|\PHPUnit_Framework_MockObject_MockObject - */ - private $collectionMock; - - /** - * {@inheritdoc} - */ - protected function setUp() - { - $this->collectionMock = $this->getMockBuilder(Collection::class) - ->disableOriginalConstructor() - ->getMock(); - - $objectManager = new ObjectManagerHelper($this); - $this->attribute = $objectManager->getObject( - \Magento\Elasticsearch\Model\Adapter\Container\Attribute::class, - [ - 'attributeCollection' => $this->collectionMock, - ] - ); - } - - /** - * @return void - */ - public function testGetAttributeCodeById() - { - $attributeId = 555; - $attributeCode = 'test_attr_code1'; - $expected = 'test_attr_code1'; - $this->mockAttributeById($attributeId, $attributeCode); - $result = $this->attribute->getAttributeCodeById($attributeId); - $this->assertEquals($expected, $result); - } - - /** - * @return void - */ - public function testGetOptionsAttributeCodeById() - { - $attributeId = 'options'; - $expected = 'options'; - $result = $this->attribute->getAttributeCodeById($attributeId); - $this->assertEquals($expected, $result); - } - - /** - * @return void - */ - public function testGetAttributeIdByCode() - { - $attributeId = 100; - $attributeCode = 'test_attribute_code'; - $this->mockAttributeByCode($attributeId, $attributeCode); - $result = $this->attribute->getAttributeIdByCode($attributeCode); - $this->assertEquals($attributeId, $result); - } - - /** - * Test getAttributeIdByCode() method. - */ - public function testGetOptionsAttributeIdByCode() - { - $attributeCode = 'options'; - $expected = 'options'; - $result = $this->attribute->getAttributeIdByCode($attributeCode); - $this->assertEquals($expected, $result); - } - - /** - * @return void - */ - public function testGetMultipleAttributeIdsByCode() - { - $firstAttributeId = 100; - $firstAttributeCode = 'test_attribute_code_100'; - $this->mockAttributeByCode($firstAttributeId, $firstAttributeCode, 0); - $this->assertEquals($firstAttributeId, $this->attribute->getAttributeIdByCode($firstAttributeCode)); - - $secondAttributeId = 200; - $secondAttributeCode = 'test_attribute_code_200'; - $this->mockAttributeByCode($secondAttributeId, $secondAttributeCode, 0); - $this->assertEquals($secondAttributeId, $this->attribute->getAttributeIdByCode($secondAttributeCode)); - } - - /** - * @return void - */ - public function testGetAttributeByIdTwice() - { - $attributeId = 555; - $attributeCode = 'test_attr_code2'; - $expected = 'test_attr_code2'; - $this->mockAttributeById($attributeId, $attributeCode, 0); - $this->assertEquals($expected, $this->attribute->getAttributeCodeById($attributeId)); - $this->assertEquals($expected, $this->attribute->getAttributeCodeById($attributeId)); - } - - /** - * @return void - */ - public function testGetAttributeByIdCachedInGetAttributeByCode() - { - $attributeId = 100; - $attributeCode = 'test_attribute_code'; - $this->mockAttributeByCode($attributeId, $attributeCode); - $this->assertEquals($attributeId, $this->attribute->getAttributeIdByCode($attributeCode)); - $this->assertEquals($attributeCode, $this->attribute->getAttributeCodeById($attributeId)); - } - - /** - * @return void - */ - public function testGetAttribute() - { - $attributeCode = 'attr_code_120'; - $attribute = $this->createAttributeMock(120, $attributeCode); - $attributes = [ - $attribute - ]; - $this->mockAttributes($attributes); - $this->assertEquals($attribute, $this->attribute->getAttribute($attributeCode)); - } - - /** - * @return void - */ - public function testGetUnknownAttribute() - { - $attributeCode = 'attr_code_120'; - $attributes = [ - $this->createAttributeMock(120, 'attribute_code') - ]; - $this->mockAttributes($attributes); - $this->assertEquals(null, $this->attribute->getAttribute($attributeCode)); - } - - /** - * @return void - */ - public function testGetAttributes() - { - $attributes = [ - 'attr_1_mock' => $this->createAttributeMock(1, 'attr_1_mock'), - 'attr_20_mock' => $this->createAttributeMock(20, 'attr_20_mock'), - 'attr_25_mock' => $this->createAttributeMock(25, 'attr_25_mock'), - 'attr_40_mock' => $this->createAttributeMock(40, 'attr_40_mock'), - 'attr_73_mock' => $this->createAttributeMock(73, 'attr_73_mock'), - 'attr_52_mock' => $this->createAttributeMock(52, 'attr_52_mock'), - 'attr_97_mock' => $this->createAttributeMock(97, 'attr_97_mock'), - ]; - $this->mockAttributes($attributes); - $this->assertEquals($attributes, $this->attribute->getAttributes()); - } - - /** - * @param array $attributes - * @return void - */ - private function mockAttributes(array $attributes) - { - $this->collectionMock->expects($this->once()) - ->method('getIterator') - ->willReturn(new \ArrayIterator($attributes)); - } - - /** - * @param int $attributeId - * @param string $attributeCode - * @param int $sequence - * @return \PHPUnit_Framework_MockObject_MockObject - */ - private function mockAttributeById($attributeId, $attributeCode, $sequence = 0) - { - $attribute = $this->createAttributeMock($attributeId, $attributeCode); - $this->collectionMock->expects($this->at($sequence)) - ->method('getItemById') - ->with($attributeId) - ->willReturn($attribute); - return $attribute; - } - - /** - * @param int $attributeId - * @param string $attributeCode - * @param int $sequence - * @return \PHPUnit_Framework_MockObject_MockObject - */ - private function mockAttributeByCode($attributeId, $attributeCode, $sequence = 0) - { - $attribute = $this->createAttributeMock($attributeId, $attributeCode); - $this->collectionMock->expects($this->at($sequence)) - ->method('getItemByColumnValue') - ->with('attribute_code', $attributeCode) - ->willReturn($attribute); - return $attribute; - } - - /** - * @param int $attributeId - * @param string $attributeCode - * @return \PHPUnit_Framework_MockObject_MockObject - */ - private function createAttributeMock($attributeId, $attributeCode) - { - $attribute = $this->getMockBuilder(\Magento\Catalog\Model\ResourceModel\Eav\Attribute::class) - ->setMethods(['getAttributeCode', 'getId']) - ->disableOriginalConstructor() - ->getMock(); - $attribute->method('getAttributeCode') - ->willReturn($attributeCode); - $attribute->method('getId') - ->willReturn($attributeId); - return $attribute; - } -} diff --git a/app/code/Magento/Elasticsearch/Test/Unit/Model/Adapter/DataMapper/ProductDataMapperTest.php b/app/code/Magento/Elasticsearch/Test/Unit/Model/Adapter/DataMapper/ProductDataMapperTest.php deleted file mode 100644 index ced529660af06..0000000000000 --- a/app/code/Magento/Elasticsearch/Test/Unit/Model/Adapter/DataMapper/ProductDataMapperTest.php +++ /dev/null @@ -1,400 +0,0 @@ -<?php -/** - * Copyright © Magento, Inc. All rights reserved. - * See COPYING.txt for license details. - */ -namespace Magento\Elasticsearch\Test\Unit\Model\Adapter\DataMapper; - -use Magento\Catalog\Model\ResourceModel\Eav\Attribute; -use Magento\Framework\TestFramework\Unit\Helper\ObjectManager as ObjectManagerHelper; -use Magento\Framework\App\Config\ScopeConfigInterface; -use Magento\Framework\Stdlib\DateTime; -use Magento\Framework\Stdlib\DateTime\TimezoneInterface; -use Magento\Elasticsearch\Model\Adapter\Container\Attribute as AttributeContainer; -use Magento\Elasticsearch\Model\Adapter\Document\Builder; -use Magento\Elasticsearch\Model\Adapter\FieldMapperInterface; -use Magento\Store\Model\StoreManagerInterface; -use Magento\Elasticsearch\Model\Adapter\DataMapper\ProductDataMapper; -use Magento\Elasticsearch\Model\ResourceModel\Index; -use Magento\AdvancedSearch\Model\ResourceModel\Index as AdvancedSearchIndex; -use Magento\Store\Api\Data\StoreInterface; - -/** - * Class ProductDataMapperTest - * - * @SuppressWarnings(PHPMD.CouplingBetweenObjects) - */ -class ProductDataMapperTest extends \PHPUnit\Framework\TestCase -{ - /** - * @var ProductDataMapper - */ - protected $model; - - /** - * @var Builder|\PHPUnit_Framework_MockObject_MockObject - */ - private $builderMock; - - /** - * @var AttributeContainer|\PHPUnit_Framework_MockObject_MockObject - */ - private $attributeContainerMock; - - /** - * @var Attribute|\PHPUnit_Framework_MockObject_MockObject - */ - private $attribute; - - /** - * @var Index|\PHPUnit_Framework_MockObject_MockObject - */ - private $resourceIndex; - - /** - * @var AdvancedSearchIndex|\PHPUnit_Framework_MockObject_MockObject - */ - private $advancedSearchIndex; - - /** - * @var FieldMapperInterface|\PHPUnit_Framework_MockObject_MockObject - */ - private $fieldMapperMock; - - /** - * @var DateTime|\PHPUnit_Framework_MockObject_MockObject - */ - private $dateTimeMock; - - /** - * @var TimezoneInterface|\PHPUnit_Framework_MockObject_MockObject - */ - private $localeDateMock; - - /** - * @var ScopeConfigInterface|\PHPUnit_Framework_MockObject_MockObject - */ - private $scopeConfigMock; - - /** - * @var StoreManagerInterface|\PHPUnit_Framework_MockObject_MockObject - */ - private $storeManagerMock; - - /** - * @var StoreInterface|\PHPUnit_Framework_MockObject_MockObject - */ - private $storeInterface; - - /** - * Set up test environment. - */ - protected function setUp() - { - $this->builderMock = $this->getMockBuilder(\Magento\Elasticsearch\Model\Adapter\Document\Builder::class) - ->setMethods(['addField', 'addFields', 'build']) - ->disableOriginalConstructor() - ->getMock(); - - $this->attributeContainerMock = $this->getMockBuilder( - \Magento\Elasticsearch\Model\Adapter\Container\Attribute::class - )->setMethods(['getAttribute', 'setStoreId', 'getBackendType', 'getFrontendInput']) - ->disableOriginalConstructor() - ->getMock(); - - $this->resourceIndex = $this->getMockBuilder(\Magento\Elasticsearch\Model\ResourceModel\Index::class) - ->disableOriginalConstructor() - ->setMethods([ - 'getPriceIndexData', - 'getFullCategoryProductIndexData', - 'getFullProductIndexData', - ]) - ->getMock(); - - $this->fieldMapperMock = $this->getMockBuilder(\Magento\Elasticsearch\Model\Adapter\FieldMapperInterface::class) - ->setMethods(['getFieldName', 'getAllAttributesTypes']) - ->disableOriginalConstructor() - ->getMock(); - - $this->dateTimeMock = $this->getMockBuilder(\Magento\Framework\Stdlib\DateTime::class) - ->setMethods(['isEmptyDate', 'setTimezone', 'format']) - ->disableOriginalConstructor() - ->getMock(); - - $this->localeDateMock = $this->getMockBuilder(\Magento\Framework\Stdlib\DateTime\TimezoneInterface::class) - ->disableOriginalConstructor() - ->getMock(); - - $this->scopeConfigMock = $this->getMockBuilder(\Magento\Framework\App\Config\ScopeConfigInterface::class) - ->disableOriginalConstructor() - ->getMock(); - - $this->storeManagerMock = $this->getMockBuilder(\Magento\Store\Model\StoreManagerInterface::class) - ->disableOriginalConstructor() - ->getMock(); - - $this->advancedSearchIndex = $this->getMockBuilder(\Magento\AdvancedSearch\Model\ResourceModel\Index::class) - ->disableOriginalConstructor() - ->getMock(); - - $this->attribute = $this->getMockBuilder(\Magento\Catalog\Model\ResourceModel\Eav\Attribute::class) - ->disableOriginalConstructor() - ->getMock(); - - $this->storeInterface = $this->getMockBuilder(\Magento\Store\Api\Data\StoreInterface::class) - ->disableOriginalConstructor() - ->getMock(); - - $objectManager = new ObjectManagerHelper($this); - $this->model = $objectManager->getObject( - \Magento\Elasticsearch\Model\Adapter\DataMapper\ProductDataMapper::class, - [ - 'builder' => $this->builderMock, - 'attributeContainer' => $this->attributeContainerMock, - 'resourceIndex' => $this->resourceIndex, - 'fieldMapper' => $this->fieldMapperMock, - 'dateTime' => $this->dateTimeMock, - 'localeDate' => $this->localeDateMock, - 'scopeConfig' => $this->scopeConfigMock, - 'storeManager' => $this->storeManagerMock - ] - ); - } - - /** - * Tests modules data returns array - * - * @dataProvider mapProvider - * @param int $productId - * @param array $productData - * @param int $storeId - * @param bool $emptyDate - * @param string $type - * @param string $frontendInput - * - * @return void - */ - public function testGetMap($productId, $productData, $storeId, $emptyDate, $type, $frontendInput) - { - $this->attributeContainerMock->expects($this->any())->method('getAttribute')->will( - $this->returnValue($this->attribute) - ); - $this->resourceIndex->expects($this->any()) - ->method('getPriceIndexData') - ->with([1, ], 1) - ->willReturn([ - 1 => [1] - ]); - $this->resourceIndex->expects($this->any()) - ->method('getFullCategoryProductIndexData') - ->willReturn([ - 1 => [ - 0 => [ - 'id' => 2, - 'name' => 'Default Category', - 'position' => '1', - ], - 1 => [ - 'id' => 3, - 'name' => 'Gear', - 'position' => '1', - ], - 2 => [ - 'id' => 4, - 'name' => 'Bags', - 'position' => '1', - ], - ], - ]); - $this->storeManagerMock->expects($this->any()) - ->method('getStore') - ->willReturn($this->storeInterface); - $this->storeInterface->expects($this->any()) - ->method('getWebsiteId') - ->willReturn(1); - $this->attributeContainerMock->expects($this->any())->method('setStoreId')->will( - $this->returnValue($this->attributeContainerMock) - ); - $this->attribute->expects($this->any())->method('getBackendType')->will( - $this->returnValue($type) - ); - $this->attribute->expects($this->any())->method('getFrontendInput')->will( - $this->returnValue($frontendInput) - ); - $this->dateTimeMock->expects($this->any())->method('isEmptyDate')->will( - $this->returnValue($emptyDate) - ); - $this->scopeConfigMock->expects($this->any())->method('getValue')->will( - $this->returnValue('Europe/London') - ); - $this->builderMock->expects($this->any())->method('addField')->will( - $this->returnValue([]) - ); - $this->builderMock->expects($this->any())->method('addFields')->will( - $this->returnValue([]) - ); - $this->builderMock->expects($this->any())->method('build')->will( - $this->returnValue([]) - ); - - $this->resourceIndex->expects($this->once()) - ->method('getFullProductIndexData') - ->willReturn($productData); - - $this->assertInternalType( - 'array', - $this->model->map($productId, $productData, $storeId) - ); - } - - /** - * @return array - * @SuppressWarnings(PHPMD.ExcessiveMethodLength) - */ - public static function mapProvider() - { - return [ - [ - '1', - ['price'=>'11','created_at'=>'00-00-00 00:00:00', 'color_value'=>'11'], - '1', - false, - 'datetime', - 'select', - ], - [ - '1', - ['price'=>'11','created_at'=>'00-00-00 00:00:00', 'color_value'=>'11'], - '1', - false, - 'time', - 'multiselect', - ], - [ - '1', - ['price'=>'11','created_at'=>null,'color_value'=>'11', ], - '1', - true, - 'datetime', - 'select', - ], - [ - '1', - [ - 'tier_price'=> - [[ - 'price_id'=>'1', - 'website_id'=>'1', - 'all_groups'=>'1', - 'cust_group'=>'1', - 'price_qty'=>'1', - 'website_price'=>'1', - 'price'=>'1' - ]], - 'created_at'=>'00-00-00 00:00:00' - ], - '1', - false, - 'string', - 'select', - ], - [ - '1', - ['image'=>'11','created_at'=>'00-00-00 00:00:00'], - '1', - false, - 'string', - 'select', - ], - [ - '1', - [ - 'image' => '1', - 'small_image' => '1', - 'thumbnail' => '1', - 'swatch_image' => '1', - 'media_gallery'=> - [ - 'images' => - [[ - 'file'=>'1', - 'media_type'=>'image', - 'position'=>'1', - 'disabled'=>'1', - 'label'=>'1', - 'title'=>'1', - 'base_image'=>'1', - 'small_image'=>'1', - 'thumbnail'=>'1', - 'swatch_image'=>'1' - ]] - ], - 'created_at'=>'00-00-00 00:00:00' - ], - '1', - false, - 'string', - 'select', - ], - [ - '1', - [ - 'image' => '1', - 'small_image' => '1', - 'thumbnail' => '1', - 'swatch_image' => '1', - 'media_gallery'=> - [ - 'images' => - [[ - 'file'=>'1', - 'media_type'=>'video', - 'position'=>'1', - 'disabled'=>'1', - 'label'=>'1', - 'title'=>'1', - 'base_image'=>'1', - 'small_image'=>'1', - 'thumbnail'=>'1', - 'swatch_image'=>'1', - 'video_title'=>'1', - 'video_url'=>'1', - 'video_description'=>'1', - 'video_metadata'=>'1', - 'video_provider'=>'1' - ]] - ], - 'created_at'=>'00-00-00 00:00:00' - ], - '1', - false, - 'string', - 'select', - ], - [ - '1', - ['quantity_and_stock_status'=>'11','created_at'=>'00-00-00 00:00:00'], - '1', - false, - 'string', - 'select', - ], - [ - '1', - ['quantity_and_stock_status'=>['is_in_stock' => '1', 'qty' => '12'],'created_at'=>'00-00-00 00:00:00'], - '1', - false, - 'string', - 'select', - ], - [ - '1', - ['price'=>'11','created_at'=>'1995-12-31 23:59:59','options'=>['value1','value2']], - '1', - false, - 'string', - 'select', - ], - ]; - } -} diff --git a/app/code/Magento/Elasticsearch/Test/Unit/Model/Adapter/ElasticsearchTest.php b/app/code/Magento/Elasticsearch/Test/Unit/Model/Adapter/ElasticsearchTest.php index 326c04aad6165..e549f254cb235 100644 --- a/app/code/Magento/Elasticsearch/Test/Unit/Model/Adapter/ElasticsearchTest.php +++ b/app/code/Magento/Elasticsearch/Test/Unit/Model/Adapter/ElasticsearchTest.php @@ -12,12 +12,12 @@ use Magento\Elasticsearch\Model\Adapter\FieldMapperInterface; use Magento\Elasticsearch\Model\Adapter\Index\BuilderInterface; use Psr\Log\LoggerInterface; -use Magento\Elasticsearch\Model\Client\Elasticsearch as ElasticsearchClient; +use Magento\AdvancedSearch\Model\Client\ClientInterface as ElasticsearchClient; use Magento\Framework\TestFramework\Unit\Helper\ObjectManager as ObjectManagerHelper; use Magento\Elasticsearch\Model\Adapter\Index\IndexNameResolver; /** - * Class ElasticsearchTest + * Test for Elasticsearch client * * @SuppressWarnings(PHPMD.CouplingBetweenObjects) */ @@ -86,9 +86,6 @@ protected function setUp() ->disableOriginalConstructor() ->setMethods(['getConnection']) ->getMock(); - $this->documentDataMapper = $this->getMockBuilder( - \Magento\Elasticsearch\Model\Adapter\DataMapperInterface::class - )->disableOriginalConstructor()->getMock(); $this->fieldMapper = $this->getMockBuilder(\Magento\Elasticsearch\Model\Adapter\FieldMapperInterface::class) ->disableOriginalConstructor() ->getMock(); @@ -135,7 +132,7 @@ protected function setUp() $elasticsearchClientMock->expects($this->any()) ->method('indices') ->willReturn($indicesMock); - $this->client = $this->getMockBuilder(\Magento\Elasticsearch\Model\Client\Elasticsearch::class) + $this->client = $this->getMockBuilder(\Magento\Elasticsearch\Elasticsearch5\Model\Client\Elasticsearch::class) ->setConstructorArgs( [ 'options' => $this->getClientOptions(), diff --git a/app/code/Magento/Elasticsearch6/Test/Unit/Model/Adapter/FieldMapper/AddDefaultSearchFieldTest.php b/app/code/Magento/Elasticsearch/Test/Unit/Model/Adapter/FieldMapper/AddDefaultSearchFieldTest.php similarity index 93% rename from app/code/Magento/Elasticsearch6/Test/Unit/Model/Adapter/FieldMapper/AddDefaultSearchFieldTest.php rename to app/code/Magento/Elasticsearch/Test/Unit/Model/Adapter/FieldMapper/AddDefaultSearchFieldTest.php index 1a1e7f4e0d643..a0e2837ba8496 100644 --- a/app/code/Magento/Elasticsearch6/Test/Unit/Model/Adapter/FieldMapper/AddDefaultSearchFieldTest.php +++ b/app/code/Magento/Elasticsearch/Test/Unit/Model/Adapter/FieldMapper/AddDefaultSearchFieldTest.php @@ -5,9 +5,9 @@ */ declare(strict_types=1); -namespace Magento\Elasticsearch6\Test\Unit\Model\Adapter\FieldMapper; +namespace Magento\Elasticsearch\Test\Unit\Model\Adapter\FieldMapper; -use Magento\Elasticsearch6\Model\Adapter\FieldMapper\AddDefaultSearchField; +use Magento\Elasticsearch\Model\Adapter\FieldMapper\AddDefaultSearchField; use Magento\Framework\TestFramework\Unit\Helper\ObjectManager; use PHPUnit\Framework\TestCase; diff --git a/app/code/Magento/Elasticsearch6/Test/Unit/Model/Adapter/FieldMapper/CopySearchableFieldsToSearchFieldTest.php b/app/code/Magento/Elasticsearch/Test/Unit/Model/Adapter/FieldMapper/CopySearchableFieldsToSearchFieldTest.php similarity index 95% rename from app/code/Magento/Elasticsearch6/Test/Unit/Model/Adapter/FieldMapper/CopySearchableFieldsToSearchFieldTest.php rename to app/code/Magento/Elasticsearch/Test/Unit/Model/Adapter/FieldMapper/CopySearchableFieldsToSearchFieldTest.php index cfe8b71095d21..33a772950a111 100644 --- a/app/code/Magento/Elasticsearch6/Test/Unit/Model/Adapter/FieldMapper/CopySearchableFieldsToSearchFieldTest.php +++ b/app/code/Magento/Elasticsearch/Test/Unit/Model/Adapter/FieldMapper/CopySearchableFieldsToSearchFieldTest.php @@ -5,9 +5,9 @@ */ declare(strict_types=1); -namespace Magento\Elasticsearch6\Test\Unit\Model\Adapter\FieldMapper; +namespace Magento\Elasticsearch\Test\Unit\Model\Adapter\FieldMapper; -use Magento\Elasticsearch6\Model\Adapter\FieldMapper\CopySearchableFieldsToSearchField; +use Magento\Elasticsearch\Model\Adapter\FieldMapper\CopySearchableFieldsToSearchField; use Magento\Framework\TestFramework\Unit\Helper\ObjectManager; use PHPUnit\Framework\TestCase; diff --git a/app/code/Magento/Elasticsearch/Test/Unit/Model/Adapter/FieldMapper/Product/FieldProvider/StaticFieldTest.php b/app/code/Magento/Elasticsearch/Test/Unit/Model/Adapter/FieldMapper/Product/FieldProvider/StaticFieldTest.php index c08a4298b42d2..a603d6ab47824 100644 --- a/app/code/Magento/Elasticsearch/Test/Unit/Model/Adapter/FieldMapper/Product/FieldProvider/StaticFieldTest.php +++ b/app/code/Magento/Elasticsearch/Test/Unit/Model/Adapter/FieldMapper/Product/FieldProvider/StaticFieldTest.php @@ -8,6 +8,7 @@ namespace Magento\Elasticsearch\Test\Unit\Model\Adapter\FieldMapper\Product\FieldProvider; use Magento\Eav\Model\Entity\Attribute\AbstractAttribute; +use Magento\Elasticsearch\Model\Adapter\FieldMapper\Product\FieldProvider\StaticField; use Magento\Framework\TestFramework\Unit\Helper\ObjectManager as ObjectManagerHelper; use Magento\Eav\Model\Config; use Magento\Elasticsearch\Model\Adapter\FieldMapper\Product\AttributeProvider; @@ -22,56 +23,56 @@ as FieldIndexResolver; use Magento\Elasticsearch\Model\Adapter\FieldMapper\Product\FieldProvider\FieldName\ResolverInterface as FieldNameResolver; +use PHPUnit\Framework\MockObject\MockObject; +use PHPUnit\Framework\TestCase; /** - * @SuppressWarnings(PHPMD) + * Unit tests for \Magento\Elasticsearch\Model\Adapter\FieldMapper\Product\FieldProvider\StaticField class. */ -class StaticFieldTest extends \PHPUnit\Framework\TestCase +class StaticFieldTest extends TestCase { /** - * @var \Magento\Elasticsearch\Model\Adapter\FieldMapper\Product\FieldProvider\StaticField + * @var StaticField */ private $provider; /** - * @var \Magento\Eav\Model\Config|\PHPUnit_Framework_MockObject_MockObject + * @var Config|MockObject */ private $eavConfig; /** - * @var FieldTypeConverterInterface + * @var FieldTypeConverterInterface|MockObject */ private $fieldTypeConverter; /** - * @var IndexTypeConverterInterface + * @var IndexTypeConverterInterface|MockObject */ private $indexTypeConverter; /** - * @var AttributeProvider + * @var AttributeProvider|MockObject */ private $attributeAdapterProvider; /** - * @var FieldIndexResolver + * @var FieldIndexResolver|MockObject */ private $fieldIndexResolver; /** - * @var FieldTypeResolver + * @var FieldTypeResolver|MockObject */ private $fieldTypeResolver; /** - * @var FieldNameResolver + * @var FieldNameResolver|MockObject */ private $fieldNameResolver; /** - * Set up test environment - * - * @return void + * @inheritdoc */ protected function setUp() { @@ -105,7 +106,7 @@ protected function setUp() $objectManager = new ObjectManagerHelper($this); $this->provider = $objectManager->getObject( - \Magento\Elasticsearch\Model\Adapter\FieldMapper\Product\FieldProvider\StaticField::class, + StaticField::class, [ 'eavConfig' => $this->eavConfig, 'fieldTypeConverter' => $this->fieldTypeConverter, @@ -114,37 +115,42 @@ protected function setUp() 'fieldIndexResolver' => $this->fieldIndexResolver, 'fieldTypeResolver' => $this->fieldTypeResolver, 'fieldNameResolver' => $this->fieldNameResolver, + 'excludedAttributes' => ['price'], ] ); } /** - * @dataProvider attributeProvider * @param string $attributeCode * @param string $inputType - * @param $indexType - * @param $isComplexType - * @param $complexType - * @param $isSortable - * @param $fieldName - * @param $compositeFieldName - * @param $sortFieldName + * @param string|bool $indexType + * @param bool $isComplexType + * @param string $complexType + * @param bool $isSortable + * @param bool $isTextType + * @param string $fieldName + * @param string $compositeFieldName + * @param string $sortFieldName * @param array $expected * @return void + * @dataProvider attributeProvider + * @SuppressWarnings(PHPMD.CyclomaticComplexity) + * @SuppressWarnings(PHPMD.ExcessiveMethodLength) + * @SuppressWarnings(PHPMD.ExcessiveParameterList) */ public function testGetAllAttributesTypes( - $attributeCode, - $inputType, + string $attributeCode, + string $inputType, $indexType, - $isComplexType, - $complexType, - $isSortable, - $isTextType, - $fieldName, - $compositeFieldName, - $sortFieldName, - $expected - ) { + bool $isComplexType, + string $complexType, + bool $isSortable, + bool $isTextType, + string $fieldName, + string $compositeFieldName, + string $sortFieldName, + array $expected + ): void { $this->fieldTypeResolver->expects($this->any()) ->method('getFieldType') ->willReturn($inputType); @@ -154,32 +160,28 @@ public function testGetAllAttributesTypes( $this->indexTypeConverter->expects($this->any()) ->method('convert') ->with($this->anything()) - ->will( - $this->returnCallback( - function ($type) { - if ($type === 'no_index') { - return 'no'; - } elseif ($type === 'no_analyze') { - return 'not_analyzed'; - } + ->willReturnCallback( + function ($type) { + if ($type === 'no_index') { + return 'no'; + } elseif ($type === 'no_analyze') { + return 'not_analyzed'; } - ) + } ); $this->fieldNameResolver->expects($this->any()) ->method('getFieldName') ->with($this->anything()) - ->will( - $this->returnCallback( - function ($attributeMock, $context) use ($fieldName, $compositeFieldName, $sortFieldName) { - if (empty($context)) { - return $fieldName; - } elseif ($context['type'] === 'sort') { - return $sortFieldName; - } elseif ($context['type'] === 'text') { - return $compositeFieldName; - } + ->willReturnCallback( + function ($attributeMock, $context) use ($fieldName, $compositeFieldName, $sortFieldName) { + if (empty($context)) { + return $fieldName; + } elseif ($context['type'] === 'sort') { + return $sortFieldName; + } elseif ($context['type'] === 'text') { + return $compositeFieldName; } - ) + } ); $productAttributeMock = $this->getMockBuilder(AbstractAttribute::class) @@ -215,23 +217,21 @@ function ($attributeMock, $context) use ($fieldName, $compositeFieldName, $sortF $this->fieldTypeConverter->expects($this->any()) ->method('convert') ->with($this->anything()) - ->will( - $this->returnCallback( - function ($type) use ($complexType) { - static $callCount = []; - $callCount[$type] = !isset($callCount[$type]) ? 1 : ++$callCount[$type]; + ->willReturnCallback( + function ($type) use ($complexType) { + static $callCount = []; + $callCount[$type] = !isset($callCount[$type]) ? 1 : ++$callCount[$type]; - if ($type === 'string') { - return 'string'; - } elseif ($type === 'float') { - return 'float'; - } elseif ($type === 'keyword') { - return 'string'; - } else { - return $complexType; - } + if ($type === 'string') { + return 'string'; + } elseif ($type === 'float') { + return 'float'; + } elseif ($type === 'keyword') { + return 'string'; + } else { + return $complexType; } - ) + } ); $this->assertEquals( @@ -242,8 +242,9 @@ function ($type) use ($complexType) { /** * @return array + * @SuppressWarnings(PHPMD.ExcessiveMethodLength) */ - public function attributeProvider() + public function attributeProvider(): array { return [ [ @@ -264,25 +265,25 @@ public function attributeProvider() 'fields' => [ 'keyword' => [ 'type' => 'string', - 'index' => 'not_analyzed' - ] - ] + 'index' => 'not_analyzed', + ], + ], ], 'category_ids_value' => [ - 'type' => 'string' + 'type' => 'string', ], 'store_id' => [ 'type' => 'string', - 'index' => 'no' - ] - ] + 'index' => 'no', + ], + ], ], [ 'attr_code', 'text', 'no', false, - null, + 'text', false, true, 'attr_code', @@ -295,22 +296,22 @@ public function attributeProvider() 'fields' => [ 'keyword' => [ 'type' => 'string', - 'index' => 'not_analyzed' - ] - ] + 'index' => 'not_analyzed', + ], + ], ], 'store_id' => [ 'type' => 'string', - 'index' => 'no' - ] + 'index' => 'no', + ], ], ], [ 'attr_code', 'text', - null, false, - null, + false, + 'text', false, false, 'attr_code', @@ -318,20 +319,21 @@ public function attributeProvider() '', [ 'attr_code' => [ - 'type' => 'text' + 'type' => 'text', + 'index' => false, ], 'store_id' => [ 'type' => 'string', - 'index' => 'no' - ] - ] + 'index' => 'no', + ], + ], ], [ 'attr_code', 'text', - null, false, - null, + false, + 'text', true, false, 'attr_code', @@ -340,19 +342,38 @@ public function attributeProvider() [ 'attr_code' => [ 'type' => 'text', + 'index' => false, 'fields' => [ 'sort_attr_code' => [ 'type' => 'string', - 'index' => 'not_analyzed' - ] - ] + 'index' => 'not_analyzed', + ], + ], ], 'store_id' => [ 'type' => 'string', - 'index' => 'no' - ] - ] - ] + 'index' => 'no', + ], + ], + ], + [ + 'price', + 'text', + false, + false, + 'text', + false, + false, + 'price', + '', + '', + [ + 'store_id' => [ + 'type' => 'string', + 'index' => 'no', + ], + ], + ], ]; } } diff --git a/app/code/Magento/Elasticsearch6/Test/Unit/Model/DataProvider/SuggestionsTest.php b/app/code/Magento/Elasticsearch/Test/Unit/Model/DataProvider/Base/SuggestionsTest.php similarity index 97% rename from app/code/Magento/Elasticsearch6/Test/Unit/Model/DataProvider/SuggestionsTest.php rename to app/code/Magento/Elasticsearch/Test/Unit/Model/DataProvider/Base/SuggestionsTest.php index b3c60b70ffa8e..f948cd0ed194d 100644 --- a/app/code/Magento/Elasticsearch6/Test/Unit/Model/DataProvider/SuggestionsTest.php +++ b/app/code/Magento/Elasticsearch/Test/Unit/Model/DataProvider/Base/SuggestionsTest.php @@ -3,7 +3,7 @@ * Copyright © Magento, Inc. All rights reserved. * See COPYING.txt for license details. */ -namespace Magento\Elasticsearch6\Test\Unit\Model\DataProvider; +namespace Magento\Elasticsearch\Test\Unit\Model\DataProvider\Base; use Magento\Framework\TestFramework\Unit\Helper\ObjectManager as ObjectManagerHelper; use Magento\Elasticsearch\Model\DataProvider\Suggestions; @@ -103,7 +103,7 @@ protected function setUp() $objectManager = new ObjectManagerHelper($this); $this->model = $objectManager->getObject( - \Magento\Elasticsearch6\Model\DataProvider\Suggestions::class, + \Magento\Elasticsearch\Model\DataProvider\Base\Suggestions::class, [ 'queryResultFactory' => $this->queryResultFactory, 'connectionManager' => $this->connectionManager, diff --git a/app/code/Magento/Elasticsearch/Test/Unit/Model/DataProvider/SuggestionsTest.php b/app/code/Magento/Elasticsearch/Test/Unit/Model/DataProvider/SuggestionsTest.php index 584b89545499d..1be84a3ec21d2 100644 --- a/app/code/Magento/Elasticsearch/Test/Unit/Model/DataProvider/SuggestionsTest.php +++ b/app/code/Magento/Elasticsearch/Test/Unit/Model/DataProvider/SuggestionsTest.php @@ -148,9 +148,10 @@ public function testGetItems() ->method('getQueryText') ->willReturn('query'); - $client = $this->getMockBuilder(\Magento\Elasticsearch\Model\Client\Elasticsearch::class) + $client = $this->getMockBuilder(\Magento\AdvancedSearch\Model\Client\ClientInterface::class) ->disableOriginalConstructor() - ->getMock(); + ->setMethods(['suggest']) + ->getMockForAbstractClass(); $this->connectionManager->expects($this->any()) ->method('getConnection') diff --git a/app/code/Magento/Elasticsearch/Test/Unit/Model/Indexer/IndexerHandlerTest.php b/app/code/Magento/Elasticsearch/Test/Unit/Model/Indexer/IndexerHandlerTest.php index 7b86a13563b1e..426e23c11844d 100644 --- a/app/code/Magento/Elasticsearch/Test/Unit/Model/Indexer/IndexerHandlerTest.php +++ b/app/code/Magento/Elasticsearch/Test/Unit/Model/Indexer/IndexerHandlerTest.php @@ -8,6 +8,10 @@ use Magento\Framework\TestFramework\Unit\Helper\ObjectManager as ObjectManagerHelper; use Magento\Elasticsearch\Model\Indexer\IndexerHandler; +/** + * Test for \Magento\Elasticsearch\Model\Indexer\IndexerHandler + * @SuppressWarnings(PHPMD.CouplingBetweenObjects) + */ class IndexerHandlerTest extends \PHPUnit\Framework\TestCase { /** @@ -41,7 +45,7 @@ class IndexerHandlerTest extends \PHPUnit\Framework\TestCase private $indexNameResolver; /** - * @var \Magento\Elasticsearch\Model\Client\Elasticsearch|\PHPUnit_Framework_MockObject_MockObject + * @var \Magento\AdvancedSearch\Model\Client\ClientInterface|\PHPUnit_Framework_MockObject_MockObject */ private $client; @@ -89,8 +93,8 @@ protected function setUp() ->disableOriginalConstructor() ->getMock(); - $this->client = $this->getMockBuilder(\Magento\Elasticsearch\Model\Client\Elasticsearch::class) - ->setMethods(['ping']) + $this->client = $this->getMockBuilder(\Magento\AdvancedSearch\Model\Client\ClientInterface::class) + ->setMethods(['ping', 'testConnection','prepareDocsPerStore','addDocs', 'cleanIndex']) ->disableOriginalConstructor() ->getMock(); diff --git a/app/code/Magento/Elasticsearch/Test/Unit/SearchAdapter/AdapterTest.php b/app/code/Magento/Elasticsearch/Test/Unit/SearchAdapter/AdapterTest.php deleted file mode 100644 index 424edd4768a81..0000000000000 --- a/app/code/Magento/Elasticsearch/Test/Unit/SearchAdapter/AdapterTest.php +++ /dev/null @@ -1,173 +0,0 @@ -<?php -/** - * Copyright © Magento, Inc. All rights reserved. - * See COPYING.txt for license details. - */ -namespace Magento\Elasticsearch\Test\Unit\SearchAdapter; - -use Magento\Elasticsearch\SearchAdapter\Adapter; -use Magento\Elasticsearch\SearchAdapter\QueryContainer; -use Magento\Elasticsearch\SearchAdapter\QueryContainerFactory; -use Magento\Framework\TestFramework\Unit\Helper\ObjectManager as ObjectManagerHelper; - -/** - * Class AdapterTest - */ -class AdapterTest extends \PHPUnit\Framework\TestCase -{ - /** - * @var QueryContainerFactory|\PHPUnit_Framework_MockObject_MockObject - */ - private $queryContainerFactory; - - /** - * @var Adapter - */ - protected $model; - - /** - * @var \Magento\Elasticsearch\SearchAdapter\ConnectionManager|\PHPUnit_Framework_MockObject_MockObject - */ - protected $connectionManager; - - /** - * @var \Magento\Elasticsearch\SearchAdapter\Mapper|\PHPUnit_Framework_MockObject_MockObject - */ - protected $mapper; - - /** - * @var \Magento\Elasticsearch\SearchAdapter\ResponseFactory|\PHPUnit_Framework_MockObject_MockObject - */ - protected $responseFactory; - - /** - * @var \Magento\Framework\Search\RequestInterface|\PHPUnit_Framework_MockObject_MockObject - */ - protected $request; - - /** - * @var \Magento\Elasticsearch\SearchAdapter\Aggregation\Builder|\PHPUnit_Framework_MockObject_MockObject - */ - protected $aggregationBuilder; - - /** - * Setup method - * @return void - */ - protected function setUp() - { - $this->connectionManager = $this->getMockBuilder(\Magento\Elasticsearch\SearchAdapter\ConnectionManager::class) - ->disableOriginalConstructor() - ->getMock(); - - $this->mapper = $this->getMockBuilder(\Magento\Elasticsearch\SearchAdapter\Mapper::class) - ->setMethods([ - 'buildQuery', - ]) - ->disableOriginalConstructor() - ->getMock(); - - $this->responseFactory = $this->getMockBuilder(\Magento\Elasticsearch\SearchAdapter\ResponseFactory::class) - ->setMethods(['create']) - ->disableOriginalConstructor() - ->getMock(); - - $this->request = $this->getMockBuilder(\Magento\Framework\Search\RequestInterface::class) - ->disableOriginalConstructor() - ->getMock(); - - $this->aggregationBuilder = $this->getMockBuilder( - \Magento\Elasticsearch\SearchAdapter\Aggregation\Builder::class - ) - ->setMethods([ - 'build', - 'setQuery' - ]) - ->disableOriginalConstructor() - ->getMock(); - - $this->queryContainerFactory = $this->getMockBuilder(QueryContainerFactory::class) - ->setMethods(['create']) - ->disableOriginalConstructor() - ->getMock(); - - $objectManager = new ObjectManagerHelper($this); - $this->model = $objectManager->getObject( - \Magento\Elasticsearch\SearchAdapter\Adapter::class, - [ - 'connectionManager' => $this->connectionManager, - 'mapper' => $this->mapper, - 'responseFactory' => $this->responseFactory, - 'aggregationBuilder' => $this->aggregationBuilder, - 'queryContainerFactory' => $this->queryContainerFactory, - ] - ); - } - - /** - * Test query() method - * - * @return void - */ - public function testQuery() - { - $searchQuery = [ - 'index' => 'indexName', - 'type' => 'product', - 'body' => [ - 'from' => 0, - 'size' => 1000, - 'fields' => ['_id', '_score'], - 'query' => [], - ], - ]; - - $client = $this->getMockBuilder(\Magento\Elasticsearch\Model\Client\Elasticsearch::class) - ->setMethods(['query']) - ->disableOriginalConstructor() - ->getMock(); - - $this->connectionManager->expects($this->once()) - ->method('getConnection') - ->willReturn($client); - - $queryContainer = $this->getMockBuilder(QueryContainer::class) - ->disableOriginalConstructor() - ->getMock(); - - $this->queryContainerFactory->expects($this->once()) - ->method('create') - ->with(['query' => $searchQuery]) - ->willReturn($queryContainer); - - $this->aggregationBuilder->expects($this->once()) - ->method('setQuery') - ->with($queryContainer); - - $this->mapper->expects($this->once()) - ->method('buildQuery') - ->with($this->request) - ->willReturn($searchQuery); - - $client->expects($this->once()) - ->method('query') - ->willReturn([ - 'hits' => [ - 'total' => 1, - 'hits' => [ - [ - '_index' => 'indexName', - '_type' => 'product', - '_id' => 1, - '_score' => 1.0, - ], - ], - ], - ]); - $this->aggregationBuilder->expects($this->once()) - ->method('build') - ->willReturn($client); - - $this->model->query($this->request); - } -} diff --git a/app/code/Magento/Elasticsearch/Test/Unit/SearchAdapter/Aggregation/IntervalTest.php b/app/code/Magento/Elasticsearch/Test/Unit/SearchAdapter/Aggregation/IntervalTest.php deleted file mode 100644 index 169c6d71062ba..0000000000000 --- a/app/code/Magento/Elasticsearch/Test/Unit/SearchAdapter/Aggregation/IntervalTest.php +++ /dev/null @@ -1,358 +0,0 @@ -<?php -/** - * Copyright © Magento, Inc. All rights reserved. - * See COPYING.txt for license details. - */ -namespace Magento\Elasticsearch\Test\Unit\SearchAdapter\Aggregation; - -use Magento\Elasticsearch\SearchAdapter\Aggregation\Interval; -use Magento\Framework\TestFramework\Unit\Helper\ObjectManager as ObjectManagerHelper; -use Magento\Elasticsearch\SearchAdapter\ConnectionManager; -use Magento\Elasticsearch\Model\Adapter\FieldMapperInterface; -use Magento\Store\Model\StoreManagerInterface; -use Magento\Customer\Model\Session as CustomerSession; -use Magento\Elasticsearch\Model\Config; -use Magento\Elasticsearch\Model\Client\Elasticsearch as ElasticsearchClient; -use Magento\Store\Api\Data\StoreInterface; -use Magento\Elasticsearch\SearchAdapter\SearchIndexNameResolver; - -/** - * @SuppressWarnings(PHPMD.CouplingBetweenObjects) - */ -class IntervalTest extends \PHPUnit\Framework\TestCase -{ - /** - * @var Interval - */ - protected $model; - - /** - * @var ConnectionManager|\PHPUnit_Framework_MockObject_MockObject - */ - protected $connectionManager; - - /** - * @var FieldMapperInterface|\PHPUnit_Framework_MockObject_MockObject - */ - protected $fieldMapper; - - /** - * @var Config|\PHPUnit_Framework_MockObject_MockObject - */ - protected $clientConfig; - - /** - * @var StoreManagerInterface|\PHPUnit_Framework_MockObject_MockObject - */ - protected $storeManager; - - /** - * @var CustomerSession|\PHPUnit_Framework_MockObject_MockObject - */ - protected $customerSession; - - /** - * @var ElasticsearchClient|\PHPUnit_Framework_MockObject_MockObject - */ - protected $clientMock; - - /** - * @var StoreInterface|\PHPUnit_Framework_MockObject_MockObject - */ - protected $storeMock; - - /** - * @var SearchIndexNameResolver|\PHPUnit_Framework_MockObject_MockObject - */ - protected $searchIndexNameResolver; - - /** - * Set up test environment. - * - * @return void - */ - protected function setUp() - { - $this->connectionManager = $this->getMockBuilder(\Magento\Elasticsearch\SearchAdapter\ConnectionManager::class) - ->setMethods(['getConnection']) - ->disableOriginalConstructor() - ->getMock(); - $this->fieldMapper = $this->getMockBuilder(\Magento\Elasticsearch\Model\Adapter\FieldMapperInterface::class) - ->disableOriginalConstructor() - ->getMock(); - $this->clientConfig = $this->getMockBuilder(\Magento\Elasticsearch\Model\Config::class) - ->setMethods([ - 'getIndexName', - 'getEntityType', - ]) - ->disableOriginalConstructor() - ->getMock(); - $this->storeManager = $this->getMockBuilder(\Magento\Store\Model\StoreManagerInterface::class) - ->disableOriginalConstructor() - ->getMock(); - $this->customerSession = $this->getMockBuilder(\Magento\Customer\Model\Session::class) - ->setMethods(['getCustomerGroupId']) - ->disableOriginalConstructor() - ->getMock(); - $this->customerSession->expects($this->any()) - ->method('getCustomerGroupId') - ->willReturn(1); - $this->storeMock = $this->getMockBuilder(\Magento\Store\Api\Data\StoreInterface::class) - ->disableOriginalConstructor() - ->getMock(); - $this->searchIndexNameResolver = $this - ->getMockBuilder(\Magento\Elasticsearch\SearchAdapter\SearchIndexNameResolver::class) - ->disableOriginalConstructor() - ->getMock(); - $this->storeMock->expects($this->any()) - ->method('getWebsiteId') - ->willReturn(1); - $this->storeMock->expects($this->any()) - ->method('getId') - ->willReturn(1); - $this->clientConfig->expects($this->any()) - ->method('getIndexName') - ->willReturn('indexName'); - $this->clientConfig->expects($this->any()) - ->method('getEntityType') - ->willReturn('product'); - $this->clientMock = $this->getMockBuilder(\Magento\Elasticsearch\Model\Client\Elasticsearch::class) - ->setMethods(['query']) - ->disableOriginalConstructor() - ->getMock(); - $this->connectionManager->expects($this->any()) - ->method('getConnection') - ->willReturn($this->clientMock); - - $objectManagerHelper = new ObjectManagerHelper($this); - $this->model = $objectManagerHelper->getObject( - \Magento\Elasticsearch\SearchAdapter\Aggregation\Interval::class, - [ - 'connectionManager' => $this->connectionManager, - 'fieldMapper' => $this->fieldMapper, - 'clientConfig' => $this->clientConfig, - 'searchIndexNameResolver' => $this->searchIndexNameResolver, - 'fieldName' => 'price_0_1', - 'storeId' => 1, - 'entityIds' => [265, 313, 281] - ] - ); - } - - /** - * @dataProvider loadParamsProvider - * @param string $limit - * @param string $offset - * @param string $lower - * @param string $upper - * Test load() method - */ - public function testLoad($limit, $offset, $lower, $upper) - { - $this->storeMock = $this->getMockBuilder(\Magento\Store\Api\Data\StoreInterface::class) - ->disableOriginalConstructor() - ->getMock(); - $this->searchIndexNameResolver->expects($this->any()) - ->method('getIndexName') - ->willReturn('magento2_product_1'); - $this->clientConfig->expects($this->any()) - ->method('getEntityType') - ->willReturn('document'); - - $expectedResult = [25]; - - $this->clientMock->expects($this->once()) - ->method('query') - ->willReturn([ - 'hits' => [ - 'hits' => [ - [ - 'fields' => [ - - 'price_0_1' => [25], - - ], - ], - ], - ], - ]); - $this->assertEquals( - $expectedResult, - $this->model->load($limit, $offset, $lower, $upper) - ); - } - - /** - * @dataProvider loadPrevParamsProvider - * @param string $data - * @param string $index - * @param string $lower - * Test loadPrevious() method with offset - */ - public function testLoadPrevArray($data, $index, $lower) - { - $queryResult = [ - 'hits' => [ - 'total'=> '1', - 'hits' => [ - [ - 'fields' => [ - 'price_0_1' => ['25'] - ] - ], - ], - ], - ]; - - $this->storeMock = $this->getMockBuilder(\Magento\Store\Api\Data\StoreInterface::class) - ->disableOriginalConstructor() - ->getMock(); - $this->searchIndexNameResolver->expects($this->any()) - ->method('getIndexName') - ->willReturn('magento2_product_1'); - $this->clientConfig->expects($this->any()) - ->method('getEntityType') - ->willReturn('document'); - - $expectedResult = ['25.0']; - - $this->clientMock->expects($this->any()) - ->method('query') - ->willReturn($queryResult); - $this->assertEquals( - $expectedResult, - $this->model->loadPrevious($data, $index, $lower) - ); - } - - /** - * @dataProvider loadPrevParamsProvider - * @param string $data - * @param string $index - * @param string $lower - * Test loadPrevious() method without offset - */ - public function testLoadPrevFalse($data, $index, $lower) - { - $queryResult = ['hits' => ['total'=> '0']]; - - $this->storeMock = $this->getMockBuilder(\Magento\Store\Api\Data\StoreInterface::class) - ->disableOriginalConstructor() - ->getMock(); - $this->searchIndexNameResolver->expects($this->any()) - ->method('getIndexName') - ->willReturn('magento2_product_1'); - $this->clientConfig->expects($this->any()) - ->method('getEntityType') - ->willReturn('document'); - - $this->clientMock->expects($this->any()) - ->method('query') - ->willReturn($queryResult); - $this->assertFalse( - $this->model->loadPrevious($data, $index, $lower) - ); - } - - /** - * @dataProvider loadNextParamsProvider - * @param string $data - * @param string $rightIndex - * @param string $upper - * Test loadNext() method with offset - */ - public function testLoadNextArray($data, $rightIndex, $upper) - { - $queryResult = [ - 'hits' => [ - 'total'=> '1', - 'hits' => [ - [ - 'fields' => [ - 'price_0_1' => ['25'] - ] - ], - ], - ] - ]; - - $this->storeMock = $this->getMockBuilder(\Magento\Store\Api\Data\StoreInterface::class) - ->disableOriginalConstructor() - ->getMock(); - $this->searchIndexNameResolver->expects($this->any()) - ->method('getIndexName') - ->willReturn('magento2_product_1'); - $this->clientConfig->expects($this->any()) - ->method('getEntityType') - ->willReturn('document'); - - $expectedResult = ['25.0']; - - $this->clientMock->expects($this->any()) - ->method('query') - ->willReturn($queryResult); - $this->assertEquals( - $expectedResult, - $this->model->loadNext($data, $rightIndex, $upper) - ); - } - - /** - * @dataProvider loadNextParamsProvider - * @param string $data - * @param string $rightIndex - * @param string $upper - * Test loadNext() method without offset - */ - public function testLoadNextFalse($data, $rightIndex, $upper) - { - $queryResult = ['hits' => ['total'=> '0']]; - - $this->storeMock = $this->getMockBuilder(\Magento\Store\Api\Data\StoreInterface::class) - ->disableOriginalConstructor() - ->getMock(); - $this->searchIndexNameResolver->expects($this->any()) - ->method('getIndexName') - ->willReturn('magento2_product_1'); - $this->clientConfig->expects($this->any()) - ->method('getEntityType') - ->willReturn('document'); - - $this->clientMock->expects($this->any()) - ->method('query') - ->willReturn($queryResult); - $this->assertFalse( - $this->model->loadNext($data, $rightIndex, $upper) - ); - } - - /** - * @return array - */ - public static function loadParamsProvider() - { - return [ - ['6', '2', '24', '42'], - ]; - } - - /** - * @return array - */ - public static function loadPrevParamsProvider() - { - return [ - ['24', '1', '24'], - ]; - } - - /** - * @return array - */ - public static function loadNextParamsProvider() - { - return [ - ['24', '2', '42'], - ]; - } -} diff --git a/app/code/Magento/Elasticsearch/Test/Unit/SearchAdapter/ConnectionManagerTest.php b/app/code/Magento/Elasticsearch/Test/Unit/SearchAdapter/ConnectionManagerTest.php index 5d80ef62a5d20..7d5865650e54a 100644 --- a/app/code/Magento/Elasticsearch/Test/Unit/SearchAdapter/ConnectionManagerTest.php +++ b/app/code/Magento/Elasticsearch/Test/Unit/SearchAdapter/ConnectionManagerTest.php @@ -12,7 +12,7 @@ use Magento\Framework\TestFramework\Unit\Helper\ObjectManager as ObjectManagerHelper; /** - * Class ConnectionManagerTest + * Test for Magento\Elasticsearch\SearchAdapter\ConnectionManager */ class ConnectionManagerTest extends \PHPUnit\Framework\TestCase { @@ -81,7 +81,7 @@ protected function setUp() */ public function testGetConnectionSuccessfull() { - $client = $this->getMockBuilder(\Magento\Elasticsearch\Model\Client\Elasticsearch::class) + $client = $this->getMockBuilder(\Magento\AdvancedSearch\Model\Client\ClientInterface::class) ->disableOriginalConstructor() ->getMock(); $this->clientFactory->expects($this->once()) diff --git a/app/code/Magento/Elasticsearch/Test/Unit/SearchAdapter/Dynamic/DataProviderTest.php b/app/code/Magento/Elasticsearch/Test/Unit/SearchAdapter/Dynamic/DataProviderTest.php index 6258a4a20d694..9e2d3c3b35a98 100644 --- a/app/code/Magento/Elasticsearch/Test/Unit/SearchAdapter/Dynamic/DataProviderTest.php +++ b/app/code/Magento/Elasticsearch/Test/Unit/SearchAdapter/Dynamic/DataProviderTest.php @@ -66,7 +66,7 @@ class DataProviderTest extends \PHPUnit\Framework\TestCase protected $storeMock; /** - * @var \Magento\Elasticsearch\Model\Client\Elasticsearch|\PHPUnit_Framework_MockObject_MockObject + * @var \Magento\AdvancedSearch\Model\Client\ClientInterface|\PHPUnit_Framework_MockObject_MockObject */ protected $clientMock; @@ -145,8 +145,8 @@ private function setUpMockObjects() $this->clientConfig->expects($this->any()) ->method('getEntityType') ->willReturn('product'); - $this->clientMock = $this->getMockBuilder(\Magento\Elasticsearch\Model\Client\Elasticsearch::class) - ->setMethods(['query']) + $this->clientMock = $this->getMockBuilder(\Magento\AdvancedSearch\Model\Client\ClientInterface::class) + ->setMethods(['query', 'testConnection', ]) ->disableOriginalConstructor() ->getMock(); $this->connectionManager->expects($this->any()) diff --git a/app/code/Magento/Elasticsearch/Test/Unit/SearchAdapter/MapperTest.php b/app/code/Magento/Elasticsearch/Test/Unit/SearchAdapter/MapperTest.php index 845e1acb2d646..1e552e395c37f 100644 --- a/app/code/Magento/Elasticsearch/Test/Unit/SearchAdapter/MapperTest.php +++ b/app/code/Magento/Elasticsearch/Test/Unit/SearchAdapter/MapperTest.php @@ -3,6 +3,8 @@ * Copyright © Magento, Inc. All rights reserved. * See COPYING.txt for license details. */ +declare(strict_types=1); + namespace Magento\Elasticsearch\Test\Unit\SearchAdapter; use Magento\Elasticsearch\SearchAdapter\Mapper; diff --git a/app/code/Magento/Elasticsearch/composer.json b/app/code/Magento/Elasticsearch/composer.json index 3cf5444d86223..9a4898ee330e2 100644 --- a/app/code/Magento/Elasticsearch/composer.json +++ b/app/code/Magento/Elasticsearch/composer.json @@ -12,7 +12,7 @@ "magento/module-store": "*", "magento/module-catalog-inventory": "*", "magento/framework": "*", - "elasticsearch/elasticsearch": "~2.0||~5.1||~6.1" + "elasticsearch/elasticsearch": "~7.6" }, "suggest": { "magento/module-config": "*" diff --git a/app/code/Magento/Elasticsearch/etc/adminhtml/system.xml b/app/code/Magento/Elasticsearch/etc/adminhtml/system.xml index 41c6710412db9..1727e51371383 100644 --- a/app/code/Magento/Elasticsearch/etc/adminhtml/system.xml +++ b/app/code/Magento/Elasticsearch/etc/adminhtml/system.xml @@ -9,115 +9,53 @@ <system> <section id="catalog"> <group id="search"> - <!-- Elasticsearch 2.0+ --> - <field id="elasticsearch_server_hostname" translate="label" type="text" sortOrder="61" showInDefault="1" showInWebsite="0" showInStore="0"> - <label>Elasticsearch Server Hostname</label> - <depends> - <field id="engine">elasticsearch</field> - </depends> - </field> - <field id="elasticsearch_server_port" translate="label" type="text" sortOrder="62" showInDefault="1" showInWebsite="0" showInStore="0"> - <label>Elasticsearch Server Port</label> - <depends> - <field id="engine">elasticsearch</field> - </depends> - </field> - <field id="elasticsearch_index_prefix" translate="label" type="text" sortOrder="63" showInDefault="1" showInWebsite="0" showInStore="0"> - <label>Elasticsearch Index Prefix</label> - <depends> - <field id="engine">elasticsearch</field> - </depends> - </field> - <field id="elasticsearch_enable_auth" translate="label" type="select" sortOrder="64" showInDefault="1" showInWebsite="0" showInStore="0"> - <label>Enable Elasticsearch HTTP Auth</label> - <source_model>Magento\Config\Model\Config\Source\Yesno</source_model> - <depends> - <field id="engine">elasticsearch</field> - </depends> - </field> - <field id="elasticsearch_username" translate="label" type="text" sortOrder="65" showInDefault="1" showInWebsite="0" showInStore="0"> - <label>Elasticsearch HTTP Username</label> - <depends> - <field id="engine">elasticsearch</field> - <field id="elasticsearch_enable_auth">1</field> - </depends> - </field> - <field id="elasticsearch_password" translate="label" type="text" sortOrder="66" showInDefault="1" showInWebsite="0" showInStore="0"> - <label>Elasticsearch HTTP Password</label> - <depends> - <field id="engine">elasticsearch</field> - <field id="elasticsearch_enable_auth">1</field> - </depends> - </field> - <field id="elasticsearch_server_timeout" translate="label" type="text" sortOrder="67" showInDefault="1" showInWebsite="0" showInStore="0"> - <label>Elasticsearch Server Timeout</label> - <depends> - <field id="engine">elasticsearch</field> - </depends> - </field> - <field id="elasticsearch_test_connect_wizard" translate="button_label" sortOrder="68" showInDefault="1" showInWebsite="0" showInStore="0"> - <label/> - <button_label>Test Connection</button_label> - <frontend_model>Magento\Elasticsearch\Block\Adminhtml\System\Config\TestConnection</frontend_model> - <depends> - <field id="engine">elasticsearch</field> - </depends> - </field> - <field id="elasticsearch_minimum_should_match" translate="label" type="text" sortOrder="93" showInDefault="1" showInWebsite="0" showInStore="0"> - <label>Minimum Terms to Match</label> - <depends> - <field id="engine">elasticsearch</field> - </depends> - <comment><![CDATA[<a href="https://docs.magento.com/m2/ce/user_guide/catalog/search-elasticsearch.html">Learn more</a> about valid syntax.]]></comment> - <backend_model>Magento\Elasticsearch\Model\Config\Backend\MinimumShouldMatch</backend_model> - </field> - <!-- Elasticsearch 5.0+ --> - <field id="elasticsearch5_server_hostname" translate="label" type="text" sortOrder="61" showInDefault="1" showInWebsite="0" showInStore="0"> + <!-- Elasticsearch 5.x --> + <field id="elasticsearch5_server_hostname" translate="label" type="text" sortOrder="61" showInDefault="1"> <label>Elasticsearch Server Hostname</label> <depends> <field id="engine">elasticsearch5</field> </depends> </field> - <field id="elasticsearch5_server_port" translate="label" type="text" sortOrder="62" showInDefault="1" showInWebsite="0" showInStore="0"> + <field id="elasticsearch5_server_port" translate="label" type="text" sortOrder="62" showInDefault="1"> <label>Elasticsearch Server Port</label> <depends> <field id="engine">elasticsearch5</field> </depends> </field> - <field id="elasticsearch5_index_prefix" translate="label" type="text" sortOrder="63" showInDefault="1" showInWebsite="0" showInStore="0"> + <field id="elasticsearch5_index_prefix" translate="label" type="text" sortOrder="63" showInDefault="1"> <label>Elasticsearch Index Prefix</label> <depends> <field id="engine">elasticsearch5</field> </depends> </field> - <field id="elasticsearch5_enable_auth" translate="label" type="select" sortOrder="64" showInDefault="1" showInWebsite="0" showInStore="0"> + <field id="elasticsearch5_enable_auth" translate="label" type="select" sortOrder="64" showInDefault="1"> <label>Enable Elasticsearch HTTP Auth</label> <source_model>Magento\Config\Model\Config\Source\Yesno</source_model> <depends> <field id="engine">elasticsearch5</field> </depends> </field> - <field id="elasticsearch5_username" translate="label" type="text" sortOrder="65" showInDefault="1" showInWebsite="0" showInStore="0"> + <field id="elasticsearch5_username" translate="label" type="text" sortOrder="65" showInDefault="1"> <label>Elasticsearch HTTP Username</label> <depends> <field id="engine">elasticsearch5</field> <field id="elasticsearch5_enable_auth">1</field> </depends> </field> - <field id="elasticsearch5_password" translate="label" type="text" sortOrder="66" showInDefault="1" showInWebsite="0" showInStore="0"> + <field id="elasticsearch5_password" translate="label" type="text" sortOrder="66" showInDefault="1"> <label>Elasticsearch HTTP Password</label> <depends> <field id="engine">elasticsearch5</field> <field id="elasticsearch5_enable_auth">1</field> </depends> </field> - <field id="elasticsearch5_server_timeout" translate="label" type="text" sortOrder="67" showInDefault="1" showInWebsite="0" showInStore="0"> + <field id="elasticsearch5_server_timeout" translate="label" type="text" sortOrder="67" showInDefault="1"> <label>Elasticsearch Server Timeout</label> <depends> <field id="engine">elasticsearch5</field> </depends> </field> - <field id="elasticsearch5_test_connect_wizard" translate="button_label" sortOrder="68" showInDefault="1" showInWebsite="0" showInStore="0"> + <field id="elasticsearch5_test_connect_wizard" translate="button_label" sortOrder="68" showInDefault="1"> <label/> <button_label>Test Connection</button_label> <frontend_model>Magento\Elasticsearch\Block\Adminhtml\System\Config\Elasticsearch5\TestConnection</frontend_model> @@ -125,7 +63,7 @@ <field id="engine">elasticsearch5</field> </depends> </field> - <field id="elasticsearch5_minimum_should_match" translate="label" type="text" sortOrder="93" showInDefault="1" showInWebsite="0" showInStore="0"> + <field id="elasticsearch5_minimum_should_match" translate="label" type="text" sortOrder="93" showInDefault="1"> <label>Minimum Terms to Match</label> <depends> <field id="engine">elasticsearch5</field> diff --git a/app/code/Magento/Elasticsearch/etc/config.xml b/app/code/Magento/Elasticsearch/etc/config.xml index 9df21978b5414..93778cc81c6f4 100644 --- a/app/code/Magento/Elasticsearch/etc/config.xml +++ b/app/code/Magento/Elasticsearch/etc/config.xml @@ -9,13 +9,6 @@ <default> <catalog> <search> - <elasticsearch_server_hostname>localhost</elasticsearch_server_hostname> - <elasticsearch_server_port>9200</elasticsearch_server_port> - <elasticsearch_index_prefix>magento2</elasticsearch_index_prefix> - <elasticsearch_enable_auth>0</elasticsearch_enable_auth> - <elasticsearch_server_timeout>15</elasticsearch_server_timeout> - <elasticsearch_minimum_should_match></elasticsearch_minimum_should_match> - <elasticsearch5_server_hostname>localhost</elasticsearch5_server_hostname> <elasticsearch5_server_port>9200</elasticsearch5_server_port> <elasticsearch5_index_prefix>magento2</elasticsearch5_index_prefix> diff --git a/app/code/Magento/Elasticsearch/etc/di.xml b/app/code/Magento/Elasticsearch/etc/di.xml index f42d957276d76..d4ab69129ad68 100644 --- a/app/code/Magento/Elasticsearch/etc/di.xml +++ b/app/code/Magento/Elasticsearch/etc/di.xml @@ -13,10 +13,12 @@ <preference for="Magento\Elasticsearch\Model\Adapter\FieldMapper\Product\FieldProvider\FieldIndex\ConverterInterface" type="Magento\Elasticsearch\Model\Adapter\FieldMapper\Product\FieldProvider\FieldIndex\Converter" /> <preference for="Magento\Elasticsearch\Model\Adapter\FieldMapper\Product\FieldProvider\FieldType\ConverterInterface" type="Magento\Elasticsearch\Model\Adapter\FieldMapper\Product\FieldProvider\FieldType\Converter" /> <preference for="Magento\Elasticsearch\Model\Adapter\FieldMapper\Product\FieldProviderInterface" type="Magento\Elasticsearch\Model\Adapter\FieldMapper\Product\CompositeFieldProvider" /> + <type name="Magento\Catalog\Model\Indexer\Category\Product\Action\Rows"> + <plugin name="catalogsearchFulltextProductAssignment" type="Magento\Elasticsearch\Model\Indexer\Fulltext\Plugin\Category\Product\Action\Rows"/> + </type> <type name="Magento\Elasticsearch\Model\Config"> <arguments> <argument name="engineList" xsi:type="array"> - <item name="elasticsearch" xsi:type="string">elasticsearch</item> <item name="elasticsearch5" xsi:type="string">elasticsearch5</item> </argument> </arguments> @@ -61,7 +63,6 @@ <arguments> <argument name="factories" xsi:type="array"> <item name="mysql" xsi:type="object">Magento\CatalogSearch\Model\ResourceModel\Fulltext\SearchCollectionFactory</item> - <item name="elasticsearch" xsi:type="object">elasticsearchFulltextSearchCollectionFactory</item> <item name="elasticsearch5" xsi:type="object">elasticsearchFulltextSearchCollectionFactory</item> </argument> </arguments> @@ -84,7 +85,6 @@ <arguments> <argument name="factories" xsi:type="array"> <item name="mysql" xsi:type="object">Magento\CatalogSearch\Model\ResourceModel\Fulltext\CollectionFactory</item> - <item name="elasticsearch" xsi:type="object">elasticsearchCategoryCollectionFactory</item> <item name="elasticsearch5" xsi:type="object">elasticsearchCategoryCollectionFactory</item> </argument> </arguments> @@ -106,7 +106,6 @@ <type name="Magento\CatalogSearch\Model\Search\ItemCollectionProvider"> <arguments> <argument name="factories" xsi:type="array"> - <item name="elasticsearch" xsi:type="object">elasticsearchAdvancedCollectionFactory</item> <item name="elasticsearch5" xsi:type="object">elasticsearchAdvancedCollectionFactory</item> </argument> </arguments> @@ -114,7 +113,6 @@ <type name="Magento\CatalogSearch\Model\Advanced\ProductCollectionPrepareStrategyProvider"> <arguments> <argument name="strategies" xsi:type="array"> - <item name="elasticsearch" xsi:type="object">Magento\Elasticsearch\Model\Advanced\ProductCollectionPrepareStrategy</item> <item name="elasticsearch5" xsi:type="object">Magento\Elasticsearch\Model\Advanced\ProductCollectionPrepareStrategy</item> </argument> </arguments> @@ -141,7 +139,6 @@ </argument> </arguments> </type> - <preference for="Magento\Elasticsearch\Model\Adapter\DataMapperInterface" type="Magento\Elasticsearch\Model\Adapter\DataMapper\DataMapperResolver" /> <virtualType name="additionalFieldsProviderForElasticsearch" type="Magento\AdvancedSearch\Model\Adapter\DataMapper\AdditionalFieldsProvider"> <arguments> <argument name="fieldsProviders" xsi:type="array"> @@ -168,31 +165,20 @@ <type name="Magento\Search\Model\Adminhtml\System\Config\Source\Engine"> <arguments> <argument name="engines" xsi:type="array"> - <item name="elasticsearch" xsi:type="string">Elasticsearch</item> - <item name="elasticsearch5" xsi:type="string">Elasticsearch 5.0+</item> + <item sortOrder="10" name="elasticsearch5" xsi:type="string">Elasticsearch 5.0+ (Deprecated)</item> </argument> </arguments> </type> <type name="Magento\Elasticsearch\Elasticsearch5\Model\Adapter\BatchDataMapper\CategoryFieldsProviderProxy"> <arguments> <argument name="categoryFieldsProviders" xsi:type="array"> - <item name="elasticsearch" xsi:type="object">Magento\Elasticsearch\Model\Adapter\BatchDataMapper\CategoryFieldsProvider</item> <item name="elasticsearch5" xsi:type="object">Magento\Elasticsearch\Elasticsearch5\Model\Adapter\BatchDataMapper\CategoryFieldsProvider</item> </argument> </arguments> </type> - <type name="Magento\Elasticsearch\Elasticsearch5\Model\Adapter\DataMapper\ProductDataMapperProxy"> - <arguments> - <argument name="dataMappers" xsi:type="array"> - <item name="elasticsearch" xsi:type="object">Magento\Elasticsearch\Model\Adapter\DataMapper\ProductDataMapper</item> - <item name="elasticsearch5" xsi:type="object">Magento\Elasticsearch\Elasticsearch5\Model\Adapter\DataMapper\ProductDataMapper</item> - </argument> - </arguments> - </type> <type name="Magento\Elasticsearch\Elasticsearch5\Model\Adapter\FieldMapper\ProductFieldMapperProxy"> <arguments> <argument name="productFieldMappers" xsi:type="array"> - <item name="elasticsearch" xsi:type="object">Magento\Elasticsearch\Model\Adapter\FieldMapper\ProductFieldMapper</item> <item name="elasticsearch5" xsi:type="object">Magento\Elasticsearch\Elasticsearch5\Model\Adapter\FieldMapper\ProductFieldMapper</item> </argument> </arguments> @@ -200,19 +186,16 @@ <type name="Magento\AdvancedSearch\Model\Client\ClientResolver"> <arguments> <argument name="clientFactories" xsi:type="array"> - <item name="elasticsearch" xsi:type="string">\Magento\Elasticsearch\Model\Client\ElasticsearchFactory</item> <item name="elasticsearch5" xsi:type="string">\Magento\Elasticsearch\Elasticsearch5\Model\Client\ElasticsearchFactory</item> </argument> <argument name="clientOptions" xsi:type="array"> - <item name="elasticsearch" xsi:type="string">\Magento\Elasticsearch\Model\Config</item> - <item name="elasticsearch5" xsi:type="string">\Magento\Elasticsearch\Model\Config</item> + <item name="elasticsearch5" xsi:type="string">Magento\Elasticsearch\Model\Config</item> </argument> </arguments> </type> <type name="Magento\CatalogSearch\Model\Indexer\IndexerHandlerFactory"> <arguments> <argument name="handlers" xsi:type="array"> - <item name="elasticsearch" xsi:type="string">Magento\Elasticsearch\Model\Indexer\IndexerHandler</item> <item name="elasticsearch5" xsi:type="string">Magento\Elasticsearch\Model\Indexer\IndexerHandler</item> </argument> </arguments> @@ -220,7 +203,6 @@ <type name="Magento\CatalogSearch\Model\Indexer\IndexStructureFactory"> <arguments> <argument name="structures" xsi:type="array"> - <item name="elasticsearch" xsi:type="string">Magento\Elasticsearch\Model\Indexer\IndexStructure</item> <item name="elasticsearch5" xsi:type="string">Magento\Elasticsearch\Model\Indexer\IndexStructure</item> </argument> </arguments> @@ -228,7 +210,6 @@ <type name="Magento\CatalogSearch\Model\ResourceModel\EngineProvider"> <arguments> <argument name="engines" xsi:type="array"> - <item name="elasticsearch" xsi:type="string">Magento\Elasticsearch\Model\ResourceModel\Engine</item> <item name="elasticsearch5" xsi:type="string">Magento\Elasticsearch\Model\ResourceModel\Engine</item> </argument> </arguments> @@ -236,7 +217,6 @@ <type name="Magento\Search\Model\AdapterFactory"> <arguments> <argument name="adapters" xsi:type="array"> - <item name="elasticsearch" xsi:type="string">Magento\Elasticsearch\SearchAdapter\Adapter</item> <item name="elasticsearch5" xsi:type="string">Magento\Elasticsearch\Elasticsearch5\SearchAdapter\Adapter</item> </argument> </arguments> @@ -244,7 +224,6 @@ <type name="Magento\Search\Model\EngineResolver"> <arguments> <argument name="engines" xsi:type="array"> - <item name="elasticsearch" xsi:type="string">elasticsearch</item> <item name="elasticsearch5" xsi:type="string">elasticsearch5</item> </argument> </arguments> @@ -279,7 +258,6 @@ <type name="Magento\Elasticsearch\Elasticsearch5\Model\Client\ClientFactoryProxy"> <arguments> <argument name="clientFactories" xsi:type="array"> - <item name="elasticsearch" xsi:type="object">Magento\Elasticsearch\Model\Client\ElasticsearchFactory</item> <item name="elasticsearch5" xsi:type="object">Magento\Elasticsearch\Elasticsearch5\Model\Client\ElasticsearchFactory</item> </argument> </arguments> @@ -292,7 +270,6 @@ <type name="Magento\Framework\Search\Dynamic\IntervalFactory"> <arguments> <argument name="intervals" xsi:type="array"> - <item name="elasticsearch" xsi:type="string">Magento\Elasticsearch\SearchAdapter\Aggregation\Interval</item> <item name="elasticsearch5" xsi:type="string">Magento\Elasticsearch\Elasticsearch5\SearchAdapter\Aggregation\Interval</item> </argument> </arguments> @@ -300,7 +277,6 @@ <type name="Magento\Framework\Search\Dynamic\DataProviderFactory"> <arguments> <argument name="dataProviders" xsi:type="array"> - <item name="elasticsearch" xsi:type="string">Magento\Elasticsearch\SearchAdapter\Dynamic\DataProvider</item> <item name="elasticsearch5" xsi:type="string">Magento\Elasticsearch\SearchAdapter\Dynamic\DataProvider</item> </argument> </arguments> @@ -347,15 +323,9 @@ <argument name="cacheId" xsi:type="string">elasticsearch_index_config</argument> </arguments> </type> - <virtualType name="Magento\Elasticsearch\Model\Client\ElasticsearchFactory" type="Magento\AdvancedSearch\Model\Client\ClientFactory"> - <arguments> - <argument name="clientClass" xsi:type="string">Magento\Elasticsearch\Model\Client\Elasticsearch</argument> - </arguments> - </virtualType> <type name="Magento\AdvancedSearch\Model\SuggestedQueries"> <arguments> <argument name="data" xsi:type="array"> - <item name="elasticsearch" xsi:type="string">Magento\Elasticsearch\Model\DataProvider\Suggestions</item> <item name="elasticsearch5" xsi:type="string">Magento\Elasticsearch\Model\DataProvider\Suggestions</item> </argument> </arguments> @@ -368,22 +338,11 @@ <type name="Magento\Config\Model\Config\TypePool"> <arguments> <argument name="sensitive" xsi:type="array"> - <item name="catalog/search/elasticsearch_password" xsi:type="string">1</item> - <item name="catalog/search/elasticsearch_server_hostname" xsi:type="string">1</item> - <item name="catalog/search/elasticsearch_username" xsi:type="string">1</item> - <item name="catalog/search/elasticsearch5_password" xsi:type="string">1</item> <item name="catalog/search/elasticsearch5_server_hostname" xsi:type="string">1</item> <item name="catalog/search/elasticsearch5_username" xsi:type="string">1</item> </argument> <argument name="environment" xsi:type="array"> - <item name="catalog/search/elasticsearch_enable_auth" xsi:type="string">1</item> - <item name="catalog/search/elasticsearch_index_prefix" xsi:type="string">1</item> - <item name="catalog/search/elasticsearch_password" xsi:type="string">1</item> - <item name="catalog/search/elasticsearch_server_hostname" xsi:type="string">1</item> - <item name="catalog/search/elasticsearch_server_port" xsi:type="string">1</item> - <item name="catalog/search/elasticsearch_username" xsi:type="string">1</item> - <item name="catalog/search/elasticsearch_server_timeout" xsi:type="string">1</item> <item name="catalog/search/elasticsearch5_enable_auth" xsi:type="string">1</item> <item name="catalog/search/elasticsearch5_index_prefix" xsi:type="string">1</item> @@ -400,11 +359,6 @@ <argument name="fieldNameResolver" xsi:type="object">elasticsearch5FieldNameResolver</argument> </arguments> </type> - <type name="Magento\Elasticsearch\Elasticsearch5\Model\Adapter\DataMapper\ProductDataMapper"> - <arguments> - <argument name="fieldNameResolver" xsi:type="object">elasticsearch5FieldNameResolver</argument> - </arguments> - </type> <type name="Magento\Elasticsearch\Model\Adapter\FieldMapper\Product\FieldProvider\FieldName\Resolver\CompositeResolver"> <arguments> <argument name="items" xsi:type="array"> @@ -518,13 +472,6 @@ <argument name="fieldNameResolver" xsi:type="object">elasticsearch5FieldNameResolver</argument> </arguments> </type> - <type name="Magento\Elasticsearch\Model\Adapter\FieldMapper\ProductFieldMapper"> - <arguments> - <argument name="attributeAdapterProvider" xsi:type="object">Magento\Elasticsearch\Model\Adapter\FieldMapper\Product\AttributeProvider</argument> - <argument name="fieldProvider" xsi:type="object">Magento\Elasticsearch\Model\Adapter\FieldMapper\Product\FieldProviderInterface</argument> - <argument name="fieldNameResolver" xsi:type="object">Magento\Elasticsearch\Model\Adapter\FieldMapper\Product\FieldProvider\FieldName\ResolverInterface</argument> - </arguments> - </type> <type name="Magento\Elasticsearch\Elasticsearch5\Model\Adapter\FieldMapper\Product\FieldProvider\FieldIndex\IndexResolver"> <arguments> <argument name="converter" xsi:type="object">Magento\Elasticsearch\Elasticsearch5\Model\Adapter\FieldMapper\Product\FieldProvider\FieldIndex\Converter</argument> @@ -535,7 +482,6 @@ <type name="Magento\Search\Model\Search\PageSizeProvider"> <arguments> <argument name="pageSizeBySearchEngine" xsi:type="array"> - <item name="elasticsearch" xsi:type="number">10000</item> <item name="elasticsearch5" xsi:type="number">10000</item> </argument> </arguments> @@ -558,4 +504,11 @@ </argument> </arguments> </type> + <type name="Magento\Elasticsearch\Model\Adapter\FieldMapper\Product\FieldProvider\StaticField"> + <arguments> + <argument name="excludedAttributes" xsi:type="array"> + <item name="price" xsi:type="string">price</item> + </argument> + </arguments> + </type> </config> diff --git a/app/code/Magento/Elasticsearch/etc/search_engine.xml b/app/code/Magento/Elasticsearch/etc/search_engine.xml index 51af3038b9c8d..72dd49504fe81 100644 --- a/app/code/Magento/Elasticsearch/etc/search_engine.xml +++ b/app/code/Magento/Elasticsearch/etc/search_engine.xml @@ -6,9 +6,6 @@ */ --> <engines xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="urn:magento:framework:Search/etc/search_engine.xsd"> - <engine name="elasticsearch"> - <feature name="synonyms" support="true" /> - </engine> <engine name="elasticsearch5"> <feature name="synonyms" support="true" /> </engine> diff --git a/app/code/Magento/Elasticsearch/i18n/en_US.csv b/app/code/Magento/Elasticsearch/i18n/en_US.csv index 85c9aefdb9f25..3a0ec556dbf8d 100644 --- a/app/code/Magento/Elasticsearch/i18n/en_US.csv +++ b/app/code/Magento/Elasticsearch/i18n/en_US.csv @@ -10,3 +10,4 @@ "Elasticsearch HTTP Password","Elasticsearch HTTP Password" "Elasticsearch Server Timeout","Elasticsearch Server Timeout" "Test Connection","Test Connection" +"Minimum Terms to Match","Minimum Terms to Match" diff --git a/app/code/Magento/Elasticsearch6/Model/Client/Elasticsearch.php b/app/code/Magento/Elasticsearch6/Model/Client/Elasticsearch.php index ff299be786aa8..2c1c283c5b24d 100644 --- a/app/code/Magento/Elasticsearch6/Model/Client/Elasticsearch.php +++ b/app/code/Magento/Elasticsearch6/Model/Client/Elasticsearch.php @@ -292,7 +292,7 @@ public function addFieldsMapping(array $fields, $index, $entityType) 'match_mapping_type' => 'string', 'mapping' => [ 'type' => 'integer', - 'index' => false, + 'index' => true, ], ], ], @@ -302,7 +302,7 @@ public function addFieldsMapping(array $fields, $index, $entityType) 'match_mapping_type' => 'string', 'mapping' => [ 'type' => 'text', - 'index' => false, + 'index' => true, 'copy_to' => '_search' ], ], diff --git a/app/code/Magento/Elasticsearch6/Test/Mftf/ActionGroup/AdminElasticConnectionTestActionGroup.xml b/app/code/Magento/Elasticsearch6/Test/Mftf/ActionGroup/AdminElasticConnectionTestActionGroup.xml index d00c1c59a0f8d..0c51142ac0aae 100644 --- a/app/code/Magento/Elasticsearch6/Test/Mftf/ActionGroup/AdminElasticConnectionTestActionGroup.xml +++ b/app/code/Magento/Elasticsearch6/Test/Mftf/ActionGroup/AdminElasticConnectionTestActionGroup.xml @@ -19,6 +19,9 @@ <click selector="{{AdminCatalogSearchConfigurationSection.elastic6ConnectionWizard}}" stepKey="clickOnTestConnectionButton"/> <waitForPageLoad stepKey="waitForConnectionEstablishment"/> <grabTextFrom selector="{{AdminCatalogSearchConfigurationSection.connectionStatus}}" stepKey="grabConnectionStatus"/> - <assertEquals expected="{{AdminElasticsearch6TestConnectionMessageData.successMessage}}" expectedType="string" actual="$grabConnectionStatus" stepKey="assertThatConnectionSuccessful"/> + <assertEquals stepKey="assertThatConnectionSuccessful"> + <actualResult type="const">$grabConnectionStatus</actualResult> + <expectedResult type="string">{{AdminElasticsearch6TestConnectionMessageData.successMessage}}</expectedResult> + </assertEquals> </actionGroup> </actionGroups> diff --git a/app/code/Magento/Elasticsearch6/Test/Mftf/Data/ConfigData.xml b/app/code/Magento/Elasticsearch6/Test/Mftf/Data/ConfigData.xml deleted file mode 100644 index f1f2f39f4457b..0000000000000 --- a/app/code/Magento/Elasticsearch6/Test/Mftf/Data/ConfigData.xml +++ /dev/null @@ -1,16 +0,0 @@ -<?xml version="1.0" encoding="UTF-8"?> -<!-- - /** - * Copyright © Magento, Inc. All rights reserved. - * See COPYING.txt for license details. - */ ---> -<entities xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" - xsi:noNamespaceSchemaLocation="urn:magento:mftf:DataGenerator/etc/dataProfileSchema.xsd"> - <entity name="SearchEngineElasticsearchConfigData"> - <data key="path">catalog/search/engine</data> - <data key="scope_id">1</data> - <data key="label">Elasticsearch 6.0+</data> - <data key="value">elasticsearch6</data> - </entity> -</entities> diff --git a/app/code/Magento/Elasticsearch6/Test/Mftf/Test/StorefrontElasticSearchForChineseLocaleTest.xml b/app/code/Magento/Elasticsearch6/Test/Mftf/Test/StorefrontElasticSearchForChineseLocaleTest.xml index fd18a0f4e1e5e..2bdbe58ecd97e 100644 --- a/app/code/Magento/Elasticsearch6/Test/Mftf/Test/StorefrontElasticSearchForChineseLocaleTest.xml +++ b/app/code/Magento/Elasticsearch6/Test/Mftf/Test/StorefrontElasticSearchForChineseLocaleTest.xml @@ -14,17 +14,18 @@ <stories value="Elasticsearch6 for Chinese"/> <title value="Elastic search for Chinese locale"/> <description value="Elastic search for Chinese locale"/> - <severity value="CRITICAL"/> + <severity value="BLOCKER"/> <testCaseId value="MC-6310"/> <useCaseId value="MAGETWO-91625"/> <group value="elasticsearch"/> + <group value="SearchEngineElasticsearch"/> </annotations> <before> <!-- Set search engine to Elastic 6, set Locale to China, create category and product, then go to Storefront --> - <actionGroup ref="LoginAsAdmin" stepKey="login"/> + <actionGroup ref="AdminLoginActionGroup" stepKey="login"/> <magentoCLI command="config:set --scope={{GeneralLocalCodeConfigsForChina.scope}} --scope-code={{GeneralLocalCodeConfigsForChina.scope_code}} {{GeneralLocalCodeConfigsForChina.path}} {{GeneralLocalCodeConfigsForChina.value}}" stepKey="setLocaleToChina"/> - <magentoCLI command="config:set {{EnableElasticSearch6Config.path}} {{EnableElasticSearch6Config.value}}" stepKey="enableElasticsearch6"/> - <actionGroup ref="AdminElasticConnectionTestActionGroup" stepKey="checkConnection"/> + <comment userInput="Moved to appropriate test suite" stepKey="enableElasticsearch6"/> + <comment userInput="Moved to appropriate test suite" stepKey="checkConnection"/> <magentoCLI command="cache:flush" stepKey="flushCache"/> <createData entity="ApiCategory" stepKey="createCategory"/> <createData entity="ApiSimpleProduct" stepKey="createProduct"> @@ -35,10 +36,10 @@ <after> <!-- Delete created data and reset initial configuration --> <magentoCLI command="config:set --scope={{GeneralLocalCodeConfigsForUS.scope}} --scope-code={{GeneralLocalCodeConfigsForUS.scope_code}} {{GeneralLocalCodeConfigsForUS.path}} {{GeneralLocalCodeConfigsForUS.value}}" stepKey="setLocaleToUS"/> - <magentoCLI command="config:set {{SetDefaultSearchEngineConfig.path}} {{SetDefaultSearchEngineConfig.value}}" stepKey="resetSearchEnginePreviousState"/> + <comment userInput="Moved to appropriate test suite" stepKey="resetSearchEnginePreviousState"/> <deleteData createDataKey="createCategory" stepKey="deleteCategory"/> <deleteData createDataKey="createProduct" stepKey="deleteSimpleProduct"/> - <actionGroup ref="logout" stepKey="logoutOfAdmin"/> + <actionGroup ref="AdminLogoutActionGroup" stepKey="logoutOfAdmin"/> </after> <!-- Search for product by name --> <actionGroup ref="StorefrontCheckQuickSearchStringActionGroup" stepKey="quickSearchByProductName"> diff --git a/app/code/Magento/Elasticsearch6/Test/Mftf/Test/StorefrontElasticsearch6SearchInvalidValueTest.xml b/app/code/Magento/Elasticsearch6/Test/Mftf/Test/StorefrontElasticsearch6SearchInvalidValueTest.xml index 050ce1263d10d..237562f256692 100644 --- a/app/code/Magento/Elasticsearch6/Test/Mftf/Test/StorefrontElasticsearch6SearchInvalidValueTest.xml +++ b/app/code/Magento/Elasticsearch6/Test/Mftf/Test/StorefrontElasticsearch6SearchInvalidValueTest.xml @@ -14,13 +14,13 @@ <stories value="Search Product on Storefront"/> <title value="Elasticsearch: try to search by invalid value of 'Searchable' attribute"/> <description value="Elasticsearch: try to search by invalid value of 'Searchable' attribute"/> - <severity value="MAJOR"/> + <severity value="BLOCKER"/> <testCaseId value="MC-17906"/> <useCaseId value="MC-15759"/> <group value="elasticsearch"/> </annotations> <before> - <actionGroup ref="LoginAsAdmin" stepKey="loginAsAdmin"/> + <actionGroup ref="AdminLoginActionGroup" stepKey="loginAsAdmin"/> <!--Create category--> <createData entity="SimpleSubCategory" stepKey="createCategory"/> <!--Set Minimal Query Length--> @@ -50,7 +50,7 @@ <actionGroup ref="ResetProductGridToDefaultViewActionGroup" stepKey="resetFiltersIfExist"/> <magentoCLI command="indexer:reindex catalogsearch_fulltext" stepKey="reindex"/> <magentoCLI command="cache:flush config" stepKey="flushCache"/> - <actionGroup ref="logout" stepKey="logout"/> + <actionGroup ref="AdminLogoutActionGroup" stepKey="logout"/> </after> <!--Create new searchable product attribute--> <amOnPage url="{{AdminProductAttributeGridPage.url}}" stepKey="goToProductAttributes"/> @@ -83,8 +83,7 @@ <magentoCLI command="indexer:reindex" stepKey="reindex"/> <magentoCLI command="cache:flush eav" stepKey="flushCache"/> <!--Assert search results on storefront--> - <amOnPage url="{{StorefrontHomePage.url}}" stepKey="goToStorefrontPage"/> - <waitForPageLoad stepKey="waitForStorefrontPageLoad"/> + <actionGroup ref="StorefrontOpenHomePageActionGroup" stepKey="goToStorefrontPage"/> <actionGroup ref="StorefrontCheckQuickSearchStringActionGroup" stepKey="quickSearchForFirstSearchTerm"> <argument name="phrase" value="?searchable;"/> </actionGroup> diff --git a/app/code/Magento/Elasticsearch6/Test/Mftf/Test/StorefrontProductQuickSearchUsingElasticSearch6Test.xml b/app/code/Magento/Elasticsearch6/Test/Mftf/Test/StorefrontProductQuickSearchUsingElasticSearch6Test.xml new file mode 100644 index 0000000000000..c3b76d4ff1c9e --- /dev/null +++ b/app/code/Magento/Elasticsearch6/Test/Mftf/Test/StorefrontProductQuickSearchUsingElasticSearch6Test.xml @@ -0,0 +1,74 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + /** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +--> + +<tests xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/testSchema.xsd"> + <test name="StorefrontProductQuickSearchUsingElasticSearch6Test"> + <annotations> + <features value="CatalogSearch"/> + <stories value="Storefront Search"/> + <title value="Product quick search doesn't throw exception after ES is chosen as search engine with different amount per page"/> + <description value="Verify no elastic search exception is thrown when searching for products, when displayed products per page are greater or equal the size of available products."/> + <severity value="BLOCKER"/> + <testCaseId value="MC-28917"/> + <useCaseId value="MC-25138"/> + <group value="catalog"/> + <group value="elasticsearch"/> + <group value="SearchEngineElasticsearch"/> + <group value="catalog_search"/> + </annotations> + <before> + <createData entity="SimpleProduct2" stepKey="createFirstProduct"> + <field key="name">AAA Product Simple AAA</field> + </createData> + <createData entity="SimpleProduct2" stepKey="createSecondProduct"> + <field key="name">Product Simple AAA</field> + </createData> + <magentoCLI command="config:set {{CustomGridPerPageValuesConfigData.path}} {{CustomGridPerPageValuesConfigData.value}}" stepKey="setCustomGridPerPageValues"/> + <magentoCLI command="config:set {{CustomGridPerPageDefaultConfigData.path}} {{CustomGridPerPageDefaultConfigData.value}}" stepKey="setCustomGridPerPageDefaults"/> + <magentoCLI stepKey="flushConfigCache" command="cache:flush config"/> + <magentoCron groups="index" stepKey="runCronIndex"/> + </before> + + <after> + <deleteData createDataKey="createFirstProduct" stepKey="deleteFirstProduct"/> + <deleteData createDataKey="createSecondProduct" stepKey="deleteSecondProduct"/> + <magentoCLI command="config:set {{DefaultGridPerPageValuesConfigData.path}} {{DefaultGridPerPageValuesConfigData.value}}" stepKey="setDefaultGridPerPageValues"/> + <magentoCLI command="config:set {{DefaultGridPerPageDefaultConfigData.path}} {{DefaultGridPerPageDefaultConfigData.value}}" stepKey="setDefaultGridPerPageDefaults"/> + </after> + + <actionGroup ref="StorefrontOpenHomePageActionGroup" stepKey="openStorefrontHomePage"/> + <actionGroup ref="StorefrontCheckQuickSearchStringActionGroup" stepKey="quickSearchSimpleProduct"> + <argument name="phrase" value="AAA"/> + </actionGroup> + <actionGroup ref="AssertStorefrontProductIsPresentOnCategoryPageActionGroup" stepKey="assertFirstProductOnCatalogSearchPage"> + <argument name="productName" value="$createFirstProduct.name$"/> + </actionGroup> + <actionGroup ref="StorefrontCheckProductIsMissingInCategoryProductsPageActionGroup" stepKey="assertSecondProductIsMissingOnCatalogSearchPage"> + <argument name="productName" value="$createSecondProduct.name$"/> + </actionGroup> + <click selector="{{StorefrontCategoryBottomToolbarSection.nextPage}}" stepKey="clickNextPageCatalogSearchPager"/> + <actionGroup ref="AssertStorefrontProductIsPresentOnCategoryPageActionGroup" stepKey="assertSecondProductOnCatalogSearchPage"> + <argument name="productName" value="$createSecondProduct.name$"/> + </actionGroup> + <actionGroup ref="StorefrontCheckProductIsMissingInCategoryProductsPageActionGroup" stepKey="assertFirstProductIsMissingOnCatalogSearchPage"> + <argument name="productName" value="$createFirstProduct.name$"/> + </actionGroup> + <selectOption selector="{{StorefrontCategoryBottomToolbarSection.perPage}}" userInput="2" stepKey="selectDisplayedProductInGridPerPage"/> + <waitForPageLoad stepKey="waitForPageLoad"/> + + <dontSeeInCurrentUrl stepKey="assertRedirectedToFirstPage" url="?p=2"/> + + <actionGroup ref="AssertStorefrontProductIsPresentOnCategoryPageActionGroup" stepKey="assertFirstProductDisplayedOnCatalogSearchPage"> + <argument name="productName" value="$createFirstProduct.name$"/> + </actionGroup> + <actionGroup ref="AssertStorefrontProductIsPresentOnCategoryPageActionGroup" stepKey="assertSecondProductDisplayedOnCatalogSearchPage"> + <argument name="productName" value="$createSecondProduct.name$"/> + </actionGroup> + </test> +</tests> diff --git a/app/code/Magento/Elasticsearch6/Test/Mftf/Test/StorefrontProductQuickSearchUsingElasticSearch6WithNotAvailablePageTest.xml b/app/code/Magento/Elasticsearch6/Test/Mftf/Test/StorefrontProductQuickSearchUsingElasticSearch6WithNotAvailablePageTest.xml new file mode 100644 index 0000000000000..1dceb21a7c95c --- /dev/null +++ b/app/code/Magento/Elasticsearch6/Test/Mftf/Test/StorefrontProductQuickSearchUsingElasticSearch6WithNotAvailablePageTest.xml @@ -0,0 +1,35 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + /** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +--> + +<tests xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/testSchema.xsd"> + <test name="StorefrontProductQuickSearchUsingElasticSearch6WithNotAvailablePageTest" extends="StorefrontProductQuickSearchUsingElasticSearch6Test"> + <annotations> + <features value="CatalogSearch"/> + <stories value="Storefront Search"/> + <title value="Product quick search doesn't throw exception after ES is chosen as search engine with selected page out of range"/> + <description value="Verify no elastic search exception is thrown when try to get page with selected page out of range."/> + <severity value="BLOCKER"/> + <testCaseId value="MC-29383"/> + <useCaseId value="MC-25138"/> + <group value="catalog"/> + <group value="elasticsearch"/> + <group value="SearchEngineElasticsearch"/> + <group value="catalog_search"/> + </annotations> + <remove keyForRemoval="selectDisplayedProductInGridPerPage"/> + <remove keyForRemoval="assertFirstProductDisplayedOnCatalogSearchPage"/> + <remove keyForRemoval="assertSecondProductDisplayedOnCatalogSearchPage"/> + <remove keyForRemoval="selectDisplayedProductInGridPerPage"/> + <remove keyForRemoval="assertStillOnSecondPage"/> + <actionGroup ref="StorefrontQuickSearchWithPaginationActionGroup" stepKey="navigateToUnavailableCatalogSearchResultPage" before="waitForPageLoad"> + <argument name="phrase" value="AAA"/> + <argument name="pageNumber" value="999"/> + </actionGroup> + </test> +</tests> diff --git a/app/code/Magento/Elasticsearch6/Test/Unit/Model/Client/ElasticsearchTest.php b/app/code/Magento/Elasticsearch6/Test/Unit/Model/Client/ElasticsearchTest.php index 607624d7b5e8e..446a3471976a0 100644 --- a/app/code/Magento/Elasticsearch6/Test/Unit/Model/Client/ElasticsearchTest.php +++ b/app/code/Magento/Elasticsearch6/Test/Unit/Model/Client/ElasticsearchTest.php @@ -6,8 +6,8 @@ namespace Magento\Elasticsearch6\Test\Unit\Model\Client; -use Magento\Elasticsearch\Model\Client\Elasticsearch as ElasticsearchClient; -use Magento\Elasticsearch6\Model\Adapter\FieldMapper\AddDefaultSearchField; +use Magento\AdvancedSearch\Model\Client\ClientInterface as ElasticsearchClient; +use Magento\Elasticsearch\Model\Adapter\FieldMapper\AddDefaultSearchField; use Magento\Framework\TestFramework\Unit\Helper\ObjectManager as ObjectManagerHelper; use Magento\Elasticsearch6\Model\Client\Elasticsearch; @@ -445,7 +445,7 @@ public function testAddFieldsMapping() 'match_mapping_type' => 'string', 'mapping' => [ 'type' => 'integer', - 'index' => false + 'index' => true, ], ], ], @@ -455,7 +455,7 @@ public function testAddFieldsMapping() 'match_mapping_type' => 'string', 'mapping' => [ 'type' => 'text', - 'index' => false, + 'index' => true, 'copy_to' => '_search' ], ], @@ -515,7 +515,7 @@ public function testAddFieldsMappingFailure() 'match_mapping_type' => 'string', 'mapping' => [ 'type' => 'integer', - 'index' => false + 'index' => true, ], ], ], @@ -525,7 +525,7 @@ public function testAddFieldsMappingFailure() 'match_mapping_type' => 'string', 'mapping' => [ 'type' => 'text', - 'index' => false, + 'index' => true, 'copy_to' => '_search' ], ], @@ -593,7 +593,7 @@ public function testDeleteMappingFailure() */ public function testQuery() { - $query = 'test phrase query'; + $query = ['test phrase query']; $this->elasticsearchClientMock->expects($this->once()) ->method('search') ->with($query) diff --git a/app/code/Magento/Elasticsearch6/composer.json b/app/code/Magento/Elasticsearch6/composer.json index b2411f6740fae..26cd5b1ad8305 100644 --- a/app/code/Magento/Elasticsearch6/composer.json +++ b/app/code/Magento/Elasticsearch6/composer.json @@ -7,9 +7,8 @@ "magento/module-advanced-search": "*", "magento/module-catalog-search": "*", "magento/module-search": "*", - "magento/module-store": "*", "magento/module-elasticsearch": "*", - "elasticsearch/elasticsearch": "~2.0||~5.1||~6.1" + "elasticsearch/elasticsearch": "~7.6" }, "suggest": { "magento/module-config": "*" diff --git a/app/code/Magento/Elasticsearch6/etc/adminhtml/system.xml b/app/code/Magento/Elasticsearch6/etc/adminhtml/system.xml index beccb0c1ceb81..fee234ada43b4 100644 --- a/app/code/Magento/Elasticsearch6/etc/adminhtml/system.xml +++ b/app/code/Magento/Elasticsearch6/etc/adminhtml/system.xml @@ -10,33 +10,29 @@ <system> <section id="catalog"> <group id="search"> - <!-- Elasticsearch 6.0+ --> - <field id="elasticsearch6_server_hostname" translate="label" type="text" sortOrder="71" - showInDefault="1" showInWebsite="0" showInStore="0"> + <!-- Elasticsearch 6.x --> + <field id="elasticsearch6_server_hostname" translate="label" type="text" sortOrder="71" showInDefault="1"> <label>Elasticsearch Server Hostname</label> <depends> <field id="engine">elasticsearch6</field> </depends> </field> - <field id="elasticsearch6_server_port" translate="label" type="text" sortOrder="72" showInDefault="1" - showInWebsite="0" showInStore="0"> + <field id="elasticsearch6_server_port" translate="label" type="text" sortOrder="72" showInDefault="1"> <label>Elasticsearch Server Port</label> <depends> <field id="engine">elasticsearch6</field> </depends> </field> - <field id="elasticsearch6_index_prefix" translate="label" type="text" sortOrder="73" showInDefault="1" - showInWebsite="0" showInStore="0"> + <field id="elasticsearch6_index_prefix" translate="label" type="text" sortOrder="73" showInDefault="1"> <label>Elasticsearch Index Prefix</label> <depends> <field id="engine">elasticsearch6</field> </depends> </field> - <field id="elasticsearch6_enable_auth" translate="label" type="select" sortOrder="74" showInDefault="1" - showInWebsite="0" showInStore="0"> + <field id="elasticsearch6_enable_auth" translate="label" type="select" sortOrder="74" showInDefault="1"> <label>Enable Elasticsearch HTTP Auth</label> <source_model>Magento\Config\Model\Config\Source\Yesno</source_model> <depends> @@ -44,8 +40,7 @@ </depends> </field> - <field id="elasticsearch6_username" translate="label" type="text" sortOrder="75" showInDefault="1" - showInWebsite="0" showInStore="0"> + <field id="elasticsearch6_username" translate="label" type="text" sortOrder="75" showInDefault="1"> <label>Elasticsearch HTTP Username</label> <depends> <field id="engine">elasticsearch6</field> @@ -53,8 +48,7 @@ </depends> </field> - <field id="elasticsearch6_password" translate="label" type="text" sortOrder="76" showInDefault="1" - showInWebsite="0" showInStore="0"> + <field id="elasticsearch6_password" translate="label" type="text" sortOrder="76" showInDefault="1"> <label>Elasticsearch HTTP Password</label> <depends> <field id="engine">elasticsearch6</field> @@ -62,16 +56,14 @@ </depends> </field> - <field id="elasticsearch6_server_timeout" translate="label" type="text" sortOrder="77" showInDefault="1" - showInWebsite="0" showInStore="0"> + <field id="elasticsearch6_server_timeout" translate="label" type="text" sortOrder="77" showInDefault="1"> <label>Elasticsearch Server Timeout</label> <depends> <field id="engine">elasticsearch6</field> </depends> </field> - <field id="elasticsearch6_test_connect_wizard" translate="button_label" sortOrder="78" showInDefault="1" - showInWebsite="0" showInStore="0"> + <field id="elasticsearch6_test_connect_wizard" translate="button_label" sortOrder="78" showInDefault="1"> <label/> <button_label>Test Connection</button_label> <frontend_model>Magento\Elasticsearch6\Block\Adminhtml\System\Config\TestConnection</frontend_model> @@ -79,8 +71,7 @@ <field id="engine">elasticsearch6</field> </depends> </field> - <field id="elasticsearch6_minimum_should_match" translate="label" type="text" sortOrder="93" showInDefault="1" - showInWebsite="0" showInStore="0"> + <field id="elasticsearch6_minimum_should_match" translate="label" type="text" sortOrder="93" showInDefault="1"> <label>Minimum Terms to Match</label> <depends> <field id="engine">elasticsearch6</field> diff --git a/app/code/Magento/Elasticsearch6/etc/di.xml b/app/code/Magento/Elasticsearch6/etc/di.xml index 6419b0aa3ee73..e0974dda1d998 100644 --- a/app/code/Magento/Elasticsearch6/etc/di.xml +++ b/app/code/Magento/Elasticsearch6/etc/di.xml @@ -17,7 +17,7 @@ <type name="Magento\Search\Model\Adminhtml\System\Config\Source\Engine"> <arguments> <argument name="engines" xsi:type="array"> - <item name="elasticsearch6" xsi:type="string">Elasticsearch 6.0+</item> + <item sortOrder="20" name="elasticsearch6" xsi:type="string">Elasticsearch 6.x</item> </argument> </arguments> </type> @@ -29,23 +29,6 @@ </argument> </arguments> </type> - - <type name="Magento\Elasticsearch\Elasticsearch5\Model\Adapter\DataMapper\ProductDataMapperProxy"> - <arguments> - <argument name="dataMappers" xsi:type="array"> - <item name="elasticsearch6" xsi:type="object">Magento\Elasticsearch\Elasticsearch5\Model\Adapter\DataMapper\ProductDataMapper</item> - </argument> - </arguments> - </type> - - <type name="Magento\Elasticsearch\Elasticsearch5\Model\Adapter\FieldMapper\ProductFieldMapperProxy"> - <arguments> - <argument name="productFieldMappers" xsi:type="array"> - <item name="elasticsearch6" xsi:type="object">Magento\Elasticsearch6\Model\Adapter\FieldMapper\ProductFieldMapper</item> - </argument> - </arguments> - </type> - <type name="Magento\AdvancedSearch\Model\Client\ClientResolver"> <arguments> <argument name="clientFactories" xsi:type="array"> @@ -114,8 +97,8 @@ <type name="Magento\Elasticsearch6\Model\Client\Elasticsearch"> <arguments> <argument name="fieldsMappingPreprocessors" xsi:type="array"> - <item name="elasticsearch6_copy_searchable_fields_to_search_field" xsi:type="object">Magento\Elasticsearch6\Model\Adapter\FieldMapper\CopySearchableFieldsToSearchField</item> - <item name="elasticsearch6_add_default_search_field" xsi:type="object">Magento\Elasticsearch6\Model\Adapter\FieldMapper\AddDefaultSearchField</item> + <item name="elasticsearch6_copy_searchable_fields_to_search_field" xsi:type="object">Magento\Elasticsearch\Model\Adapter\FieldMapper\CopySearchableFieldsToSearchField</item> + <item name="elasticsearch6_add_default_search_field" xsi:type="object">Magento\Elasticsearch\Model\Adapter\FieldMapper\AddDefaultSearchField</item> </argument> </arguments> </type> @@ -145,12 +128,18 @@ </arguments> </type> - <type name="Magento\Elasticsearch6\Model\DataProvider\Suggestions"> + <virtualType name="Magento\Elasticsearch6\Model\DataProvider\Suggestions" type="Magento\Elasticsearch\Model\DataProvider\Base\Suggestions"> <arguments> <argument name="fieldProvider" xsi:type="object">elasticsearch5FieldProvider</argument> </arguments> + </virtualType> + <type name="Magento\Elasticsearch\Elasticsearch5\Model\Adapter\FieldMapper\ProductFieldMapperProxy"> + <arguments> + <argument name="productFieldMappers" xsi:type="array"> + <item name="elasticsearch6" xsi:type="object">Magento\Elasticsearch6\Model\Adapter\FieldMapper\ProductFieldMapper</item> + </argument> + </arguments> </type> - <virtualType name="elasticsearch6FieldNameResolver" type="\Magento\Elasticsearch\Model\Adapter\FieldMapper\Product\FieldProvider\FieldName\Resolver\CompositeResolver"> <arguments> <argument name="items" xsi:type="array"> diff --git a/app/code/Magento/Elasticsearch7/Block/Adminhtml/System/Config/TestConnection.php b/app/code/Magento/Elasticsearch7/Block/Adminhtml/System/Config/TestConnection.php new file mode 100644 index 0000000000000..e35f292778ab1 --- /dev/null +++ b/app/code/Magento/Elasticsearch7/Block/Adminhtml/System/Config/TestConnection.php @@ -0,0 +1,33 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +declare(strict_types=1); + +namespace Magento\Elasticsearch7\Block\Adminhtml\System\Config; + +/** + * Elasticsearch 7.x test connection block + */ +class TestConnection extends \Magento\AdvancedSearch\Block\Adminhtml\System\Config\TestConnection +{ + /** + * @inheritdoc + */ + protected function _getFieldMapping(): array + { + $fields = [ + 'engine' => 'catalog_search_engine', + 'hostname' => 'catalog_search_elasticsearch7_server_hostname', + 'port' => 'catalog_search_elasticsearch7_server_port', + 'index' => 'catalog_search_elasticsearch7_index_prefix', + 'enableAuth' => 'catalog_search_elasticsearch7_enable_auth', + 'username' => 'catalog_search_elasticsearch7_username', + 'password' => 'catalog_search_elasticsearch7_password', + 'timeout' => 'catalog_search_elasticsearch7_server_timeout', + ]; + + return array_merge(parent::_getFieldMapping(), $fields); + } +} diff --git a/app/code/Magento/Authorizenet/LICENSE.txt b/app/code/Magento/Elasticsearch7/LICENSE.txt similarity index 100% rename from app/code/Magento/Authorizenet/LICENSE.txt rename to app/code/Magento/Elasticsearch7/LICENSE.txt diff --git a/app/code/Magento/Authorizenet/LICENSE_AFL.txt b/app/code/Magento/Elasticsearch7/LICENSE_AFL.txt similarity index 100% rename from app/code/Magento/Authorizenet/LICENSE_AFL.txt rename to app/code/Magento/Elasticsearch7/LICENSE_AFL.txt diff --git a/app/code/Magento/Elasticsearch7/Model/Adapter/FieldMapper/Product/FieldProvider/FieldName/Resolver/DefaultResolver.php b/app/code/Magento/Elasticsearch7/Model/Adapter/FieldMapper/Product/FieldProvider/FieldName/Resolver/DefaultResolver.php new file mode 100644 index 0000000000000..2fe5bc3f4a597 --- /dev/null +++ b/app/code/Magento/Elasticsearch7/Model/Adapter/FieldMapper/Product/FieldProvider/FieldName/Resolver/DefaultResolver.php @@ -0,0 +1,48 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +declare(strict_types=1); + +namespace Magento\Elasticsearch7\Model\Adapter\FieldMapper\Product\FieldProvider\FieldName\Resolver; + +use Magento\Elasticsearch\Model\Adapter\FieldMapper\Product\AttributeAdapter; +use Magento\Elasticsearch\Model\Adapter\FieldMapper\Product\FieldProvider\FieldName\ResolverInterface; + +/** + * Default name resolver for Elasticsearch 7 + */ +class DefaultResolver implements ResolverInterface +{ + /** + * @var ResolverInterface + */ + private $baseResolver; + + /** + * DefaultResolver constructor. + * @param ResolverInterface $baseResolver + */ + public function __construct(ResolverInterface $baseResolver) + { + $this->baseResolver = $baseResolver; + } + + /** + * Get field name. + * + * @param AttributeAdapter $attribute + * @param array $context + * @return string|null + */ + public function getFieldName(AttributeAdapter $attribute, $context = []): ?string + { + $fieldName = $this->baseResolver->getFieldName($attribute, $context); + if ($fieldName === '_all') { + $fieldName = '_search'; + } + + return $fieldName; + } +} diff --git a/app/code/Magento/Elasticsearch7/Model/Client/Elasticsearch.php b/app/code/Magento/Elasticsearch7/Model/Client/Elasticsearch.php new file mode 100644 index 0000000000000..feacca8d62804 --- /dev/null +++ b/app/code/Magento/Elasticsearch7/Model/Client/Elasticsearch.php @@ -0,0 +1,371 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +declare(strict_types=1); + +namespace Magento\Elasticsearch7\Model\Client; + +use Magento\Elasticsearch\Model\Adapter\FieldsMappingPreprocessorInterface; +use Magento\Framework\Exception\LocalizedException; +use Magento\AdvancedSearch\Model\Client\ClientInterface; + +/** + * Elasticsearch client + */ +class Elasticsearch implements ClientInterface +{ + /** + * @var array + */ + private $clientOptions; + + /** + * Elasticsearch Client instances + * + * @var \Elasticsearch\Client[] + */ + private $client; + + /** + * @var bool + */ + private $pingResult; + + /** + * @var FieldsMappingPreprocessorInterface[] + */ + private $fieldsMappingPreprocessors; + + /** + * Initialize Elasticsearch 7 Client + * + * @param array $options + * @param \Elasticsearch\Client|null $elasticsearchClient + * @param array $fieldsMappingPreprocessors + * @throws LocalizedException + */ + public function __construct( + $options = [], + $elasticsearchClient = null, + $fieldsMappingPreprocessors = [] + ) { + if (empty($options['hostname']) || ((!empty($options['enableAuth']) && + ($options['enableAuth'] == 1)) && (empty($options['username']) || empty($options['password'])))) { + throw new LocalizedException( + __('The search failed because of a search engine misconfiguration.') + ); + } + // phpstan:ignore + if ($elasticsearchClient instanceof \Elasticsearch\Client) { + $this->client[getmypid()] = $elasticsearchClient; + } + $this->clientOptions = $options; + $this->fieldsMappingPreprocessors = $fieldsMappingPreprocessors; + } + + /** + * Execute suggest query for Elasticsearch 7 + * + * @param array $query + * @return array + */ + public function suggest(array $query) : array + { + return $this->getElasticsearchClient()->suggest($query); + } + + /** + * Get Elasticsearch 7 Client + * + * @return \Elasticsearch\Client + */ + private function getElasticsearchClient(): \Elasticsearch\Client + { + $pid = getmypid(); + if (!isset($this->client[$pid])) { + $config = $this->buildESConfig($this->clientOptions); + $this->client[$pid] = \Elasticsearch\ClientBuilder::fromConfig($config, true); + } + return $this->client[$pid]; + } + + /** + * Ping the Elasticsearch 7 client + * + * @return bool + */ + public function ping() : bool + { + if ($this->pingResult === null) { + $this->pingResult = $this->getElasticsearchClient() + ->ping(['client' => ['timeout' => $this->clientOptions['timeout']]]); + } + + return $this->pingResult; + } + + /** + * Validate connection params for Elasticsearch 7 + * + * @return bool + */ + public function testConnection() : bool + { + return $this->ping(); + } + + /** + * Build config for Elasticsearch 7 + * + * @param array $options + * @return array + */ + private function buildESConfig(array $options = []) : array + { + $hostname = preg_replace('/http[s]?:\/\//i', '', $options['hostname']); + // @codingStandardsIgnoreStart + $protocol = parse_url($options['hostname'], PHP_URL_SCHEME); + // @codingStandardsIgnoreEnd + if (!$protocol) { + $protocol = 'http'; + } + + $authString = ''; + if (!empty($options['enableAuth']) && (int)$options['enableAuth'] === 1) { + $authString = "{$options['username']}:{$options['password']}@"; + } + + $portString = ''; + if (!empty($options['port'])) { + $portString = ':' . $options['port']; + } + + $host = $protocol . '://' . $authString . $hostname . $portString; + + $options['hosts'] = [$host]; + + return $options; + } + + /** + * Performs bulk query over Elasticsearch 7 index + * + * @param array $query + * @return void + */ + public function bulkQuery(array $query) + { + $this->getElasticsearchClient()->bulk($query); + } + + /** + * Creates an Elasticsearch 7 index. + * + * @param string $index + * @param array $settings + * @return void + */ + public function createIndex(string $index, array $settings) + { + $this->getElasticsearchClient()->indices()->create( + [ + 'index' => $index, + 'body' => $settings, + ] + ); + } + + /** + * Delete an Elasticsearch 7 index. + * + * @param string $index + * @return void + */ + public function deleteIndex(string $index) + { + $this->getElasticsearchClient()->indices()->delete(['index' => $index]); + } + + /** + * Check if index is empty. + * + * @param string $index + * @return bool + */ + public function isEmptyIndex(string $index) : bool + { + $stats = $this->getElasticsearchClient()->indices()->stats(['index' => $index, 'metric' => 'docs']); + if ($stats['indices'][$index]['primaries']['docs']['count'] === 0) { + return true; + } + return false; + } + + /** + * Updates alias. + * + * @param string $alias + * @param string $newIndex + * @param string $oldIndex + * @return void + */ + public function updateAlias(string $alias, string $newIndex, string $oldIndex = '') + { + $params = [ + 'body' => [ + 'actions' => [] + ] + ]; + if ($oldIndex) { + $params['body']['actions'][] = ['remove' => ['alias' => $alias, 'index' => $oldIndex]]; + } + if ($newIndex) { + $params['body']['actions'][] = ['add' => ['alias' => $alias, 'index' => $newIndex]]; + } + + $this->getElasticsearchClient()->indices()->updateAliases($params); + } + + /** + * Checks whether Elasticsearch 7 index exists + * + * @param string $index + * @return bool + */ + public function indexExists(string $index) : bool + { + return $this->getElasticsearchClient()->indices()->exists(['index' => $index]); + } + + /** + * Exists alias. + * + * @param string $alias + * @param string $index + * @return bool + */ + public function existsAlias(string $alias, string $index = '') : bool + { + $params = ['name' => $alias]; + if ($index) { + $params['index'] = $index; + } + return $this->getElasticsearchClient()->indices()->existsAlias($params); + } + + /** + * Get alias. + * + * @param string $alias + * @return array + */ + public function getAlias(string $alias) : array + { + return $this->getElasticsearchClient()->indices()->getAlias(['name' => $alias]); + } + + /** + * Add mapping to Elasticsearch 7 index + * + * @param array $fields + * @param string $index + * @param string $entityType + * @return void + */ + public function addFieldsMapping(array $fields, string $index, string $entityType) + { + $params = [ + 'index' => $index, + 'type' => $entityType, + 'include_type_name' => true, + 'body' => [ + $entityType => [ + 'properties' => [], + 'dynamic_templates' => [ + [ + 'price_mapping' => [ + 'match' => 'price_*', + 'match_mapping_type' => 'string', + 'mapping' => [ + 'type' => 'float', + 'store' => true, + ], + ], + ], + [ + 'position_mapping' => [ + 'match' => 'position_*', + 'match_mapping_type' => 'string', + 'mapping' => [ + 'type' => 'integer', + 'index' => true, + ], + ], + ], + [ + 'string_mapping' => [ + 'match' => '*', + 'match_mapping_type' => 'string', + 'mapping' => [ + 'type' => 'text', + 'index' => true, + 'copy_to' => '_search' + ], + ], + ], + ], + ], + ], + ]; + + foreach ($this->applyFieldsMappingPreprocessors($fields) as $field => $fieldInfo) { + $params['body'][$entityType]['properties'][$field] = $fieldInfo; + } + + $this->getElasticsearchClient()->indices()->putMapping($params); + } + + /** + * Execute search by $query + * + * @param array $query + * @return array + */ + public function query(array $query) : array + { + return $this->getElasticsearchClient()->search($query); + } + + /** + * Delete mapping in Elasticsearch 7 index + * + * @param string $index + * @param string $entityType + * @return void + */ + public function deleteMapping(string $index, string $entityType) + { + $this->getElasticsearchClient()->indices()->deleteMapping( + [ + 'index' => $index, + 'type' => $entityType, + ] + ); + } + + /** + * Apply fields mapping preprocessors + * + * @param array $properties + * @return array + */ + private function applyFieldsMappingPreprocessors(array $properties): array + { + foreach ($this->fieldsMappingPreprocessors as $preprocessor) { + $properties = $preprocessor->process($properties); + } + return $properties; + } +} diff --git a/app/code/Magento/Elasticsearch7/README.md b/app/code/Magento/Elasticsearch7/README.md new file mode 100644 index 0000000000000..f8331665360c5 --- /dev/null +++ b/app/code/Magento/Elasticsearch7/README.md @@ -0,0 +1,2 @@ +Magento\Elasticsearch7 module allows to use Elastic search engine (v7) for product searching capabilities. +The module implements Magento\Search library interfaces. diff --git a/app/code/Magento/Elasticsearch7/SearchAdapter/Adapter.php b/app/code/Magento/Elasticsearch7/SearchAdapter/Adapter.php new file mode 100644 index 0000000000000..bbc7985f4519d --- /dev/null +++ b/app/code/Magento/Elasticsearch7/SearchAdapter/Adapter.php @@ -0,0 +1,134 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +declare(strict_types=1); + +namespace Magento\Elasticsearch7\SearchAdapter; + +use Magento\Framework\Search\RequestInterface; +use Magento\Framework\Search\Response\QueryResponse; +use Magento\Elasticsearch\SearchAdapter\Aggregation\Builder as AggregationBuilder; +use Magento\Elasticsearch\SearchAdapter\ConnectionManager; +use Magento\Elasticsearch\SearchAdapter\ResponseFactory; +use Psr\Log\LoggerInterface; +use Magento\Framework\Search\AdapterInterface; +use Magento\Elasticsearch\SearchAdapter\QueryContainerFactory; + +/** + * Elasticsearch Search Adapter + */ +class Adapter implements AdapterInterface +{ + /** + * Mapper instance + * + * @var Mapper + */ + private $mapper; + + /** + * Response Factory + * + * @var ResponseFactory + */ + private $responseFactory; + + /** + * @var ConnectionManager + */ + private $connectionManager; + + /** + * @var AggregationBuilder + */ + private $aggregationBuilder; + + /** + * @var QueryContainerFactory + */ + private $queryContainerFactory; + + /** + * Empty response from Elasticsearch + * + * @var array + */ + private static $emptyRawResponse = [ + "hits" => + [ + "hits" => [] + ], + "aggregations" => + [ + "price_bucket" => [], + "category_bucket" => + [ + "buckets" => [] + + ] + ] + ]; + + /** + * @var LoggerInterface + */ + private $logger; + + /** + * @param ConnectionManager $connectionManager + * @param Mapper $mapper + * @param ResponseFactory $responseFactory + * @param AggregationBuilder $aggregationBuilder + * @param QueryContainerFactory $queryContainerFactory + * @param LoggerInterface $logger + */ + public function __construct( + ConnectionManager $connectionManager, + Mapper $mapper, + ResponseFactory $responseFactory, + AggregationBuilder $aggregationBuilder, + QueryContainerFactory $queryContainerFactory, + LoggerInterface $logger + ) { + $this->connectionManager = $connectionManager; + $this->mapper = $mapper; + $this->responseFactory = $responseFactory; + $this->aggregationBuilder = $aggregationBuilder; + $this->queryContainerFactory = $queryContainerFactory; + $this->logger = $logger; + } + + /** + * Search query + * + * @param RequestInterface $request + * @return QueryResponse + */ + public function query(RequestInterface $request) : QueryResponse + { + $client = $this->connectionManager->getConnection(); + $aggregationBuilder = $this->aggregationBuilder; + $query = $this->mapper->buildQuery($request); + $aggregationBuilder->setQuery($this->queryContainerFactory->create(['query' => $query])); + + try { + $rawResponse = $client->query($query); + } catch (\Exception $e) { + $this->logger->critical($e); + // return empty search result in case an exception is thrown from Elasticsearch + $rawResponse = self::$emptyRawResponse; + } + + $rawDocuments = $rawResponse['hits']['hits'] ?? []; + $queryResponse = $this->responseFactory->create( + [ + 'documents' => $rawDocuments, + 'aggregations' => $aggregationBuilder->build($request, $rawResponse), + 'total' => $rawResponse['hits']['total']['value'] ?? 0 + ] + ); + return $queryResponse; + } +} diff --git a/app/code/Magento/Elasticsearch7/SearchAdapter/Mapper.php b/app/code/Magento/Elasticsearch7/SearchAdapter/Mapper.php new file mode 100644 index 0000000000000..a47d9b6b19cca --- /dev/null +++ b/app/code/Magento/Elasticsearch7/SearchAdapter/Mapper.php @@ -0,0 +1,44 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ + +declare(strict_types=1); + +namespace Magento\Elasticsearch7\SearchAdapter; + +use Magento\Framework\Search\RequestInterface; + +/** + * Elasticsearch7 mapper class + */ +class Mapper +{ + /** + * @var \Magento\Elasticsearch\Elasticsearch5\SearchAdapter\Mapper + */ + private $mapper; + + /** + * Mapper constructor. + * @param \Magento\Elasticsearch\Elasticsearch5\SearchAdapter\Mapper $mapper + */ + public function __construct(\Magento\Elasticsearch\Elasticsearch5\SearchAdapter\Mapper $mapper) + { + $this->mapper = $mapper; + } + + /** + * Build adapter dependent query + * + * @param RequestInterface $request + * @return array + */ + public function buildQuery(RequestInterface $request) : array + { + $searchQuery = $this->mapper->buildQuery($request); + $searchQuery['track_total_hits'] = true; + return $searchQuery; + } +} diff --git a/app/code/Magento/Elasticsearch7/Test/Unit/Model/Client/ElasticsearchTest.php b/app/code/Magento/Elasticsearch7/Test/Unit/Model/Client/ElasticsearchTest.php new file mode 100644 index 0000000000000..f4be22150bba7 --- /dev/null +++ b/app/code/Magento/Elasticsearch7/Test/Unit/Model/Client/ElasticsearchTest.php @@ -0,0 +1,652 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ + +declare(strict_types=1); + +namespace Magento\Elasticsearch7\Test\Unit\Model\Client; + +use Magento\AdvancedSearch\Model\Client\ClientInterface as ElasticsearchClient; +use Magento\Framework\TestFramework\Unit\Helper\ObjectManager as ObjectManagerHelper; +use Magento\Elasticsearch7\Model\Client\Elasticsearch; +use Magento\Elasticsearch\Model\Adapter\FieldMapper\AddDefaultSearchField; + +/** + * Class ElasticsearchTest to test Elasticsearch 7 + */ +class ElasticsearchTest extends \PHPUnit\Framework\TestCase +{ + /** + * @var ElasticsearchClient + */ + private $model; + + /** + * @var \Elasticsearch\Client|\PHPUnit_Framework_MockObject_MockObject + */ + private $elasticsearchClientMock; + + /** + * @var \Elasticsearch\Namespaces\IndicesNamespace|\PHPUnit_Framework_MockObject_MockObject + */ + private $indicesMock; + + /** + * @var ObjectManagerHelper + */ + private $objectManager; + + /** + * Setup + * + * @return void + */ + protected function setUp() + { + $this->elasticsearchClientMock = $this->getMockBuilder(\Elasticsearch\Client::class) + ->setMethods( + [ + 'indices', + 'ping', + 'bulk', + 'search', + 'scroll', + 'suggest', + 'info', + ] + ) + ->disableOriginalConstructor() + ->getMock(); + $this->indicesMock = $this->getMockBuilder(\Elasticsearch\Namespaces\IndicesNamespace::class) + ->setMethods( + [ + 'exists', + 'getSettings', + 'create', + 'delete', + 'putMapping', + 'deleteMapping', + 'stats', + 'updateAliases', + 'existsAlias', + 'getAlias', + ] + ) + ->disableOriginalConstructor() + ->getMock(); + $this->elasticsearchClientMock->expects($this->any()) + ->method('indices') + ->willReturn($this->indicesMock); + $this->elasticsearchClientMock->expects($this->any()) + ->method('ping') + ->willReturn(true); + $this->elasticsearchClientMock->expects($this->any()) + ->method('info') + ->willReturn(['version' => ['number' => '7.0.0']]); + + $this->objectManager = new ObjectManagerHelper($this); + $this->model = $this->objectManager->getObject( + Elasticsearch::class, + [ + 'options' => $this->getOptions(), + 'elasticsearchClient' => $this->elasticsearchClientMock, + 'fieldsMappingPreprocessors' => [new AddDefaultSearchField()] + ] + ); + } + + /** + * @expectedException \Magento\Framework\Exception\LocalizedException + */ + public function testConstructorOptionsException() + { + $result = $this->objectManager->getObject( + Elasticsearch::class, + [ + 'options' => [] + ] + ); + $this->assertNotNull($result); + } + + /** + * Test client creation from the list of options + */ + public function testConstructorWithOptions() + { + $result = $this->objectManager->getObject( + \Magento\Elasticsearch7\Model\Client\Elasticsearch::class, + [ + 'options' => $this->getOptions() + ] + ); + $this->assertNotNull($result); + } + + /** + * Ensure that configuration returns correct url. + * + * @param array $options + * @param string $expectedResult + * @throws \Magento\Framework\Exception\LocalizedException + * @throws \ReflectionException + * @dataProvider getOptionsDataProvider + */ + public function testBuildConfig(array $options, $expectedResult): void + { + $buildConfig = new Elasticsearch($options); + $config = $this->getPrivateMethod(Elasticsearch::class, 'buildESConfig'); + $result = $config->invoke($buildConfig, $options); + $this->assertEquals($expectedResult, $result['hosts'][0]); + } + + /** + * Return private method for elastic search class. + * + * @param $className + * @param $methodName + * @return \ReflectionMethod + * @throws \ReflectionException + */ + private function getPrivateMethod($className, $methodName) + { + $reflector = new \ReflectionClass($className); + $method = $reflector->getMethod($methodName); + $method->setAccessible(true); + + return $method; + } + + /** + * Get options data provider. + */ + public function getOptionsDataProvider() + { + return [ + [ + 'without_protocol' => [ + 'hostname' => 'localhost', + 'port' => '9200', + 'timeout' => 15, + 'index' => 'magento2', + 'enableAuth' => 0, + ], + 'expected_result' => 'http://localhost:9200' + ], + [ + 'with_protocol' => [ + 'hostname' => 'https://localhost', + 'port' => '9200', + 'timeout' => 15, + 'index' => 'magento2', + 'enableAuth' => 0, + ], + 'expected_result' => 'https://localhost:9200' + ] + ]; + } + + /** + * Test ping functionality + */ + public function testPing() + { + $this->elasticsearchClientMock->expects($this->once())->method('ping')->willReturn(true); + $this->assertEquals(true, $this->model->ping()); + } + + /** + * Test validation of connection parameters + */ + public function testTestConnection() + { + $this->elasticsearchClientMock->expects($this->once())->method('ping')->willReturn(true); + $this->assertEquals(true, $this->model->testConnection()); + } + + /** + * Test validation of connection parameters returns false + */ + public function testTestConnectionFalse() + { + $this->elasticsearchClientMock->expects($this->once())->method('ping')->willReturn(false); + $this->assertEquals(true, $this->model->testConnection()); + } + + /** + * Test validation of connection parameters + */ + public function testTestConnectionPing() + { + $this->model = $this->objectManager->getObject( + \Magento\Elasticsearch7\Model\Client\Elasticsearch::class, + [ + 'options' => $this->getEmptyIndexOption(), + 'elasticsearchClient' => $this->elasticsearchClientMock + ] + ); + + $this->model->ping(); + $this->assertEquals(true, $this->model->testConnection()); + } + + /** + * Test bulkQuery() method + */ + public function testBulkQuery() + { + $this->elasticsearchClientMock->expects($this->once()) + ->method('bulk') + ->with([]); + $this->model->bulkQuery([]); + } + + /** + * Test createIndex() method, case when such index exists + */ + public function testCreateIndexExists() + { + $this->indicesMock->expects($this->once()) + ->method('create') + ->with( + [ + 'index' => 'indexName', + 'body' => [], + ] + ); + $this->model->createIndex('indexName', []); + } + + /** + * Test deleteIndex() method. + */ + public function testDeleteIndex() + { + $this->indicesMock->expects($this->once()) + ->method('delete') + ->with(['index' => 'indexName']); + $this->model->deleteIndex('indexName'); + } + + /** + * Test isEmptyIndex() method. + */ + public function testIsEmptyIndex() + { + $indexName = 'magento2_index'; + $stats['indices'][$indexName]['primaries']['docs']['count'] = 0; + + $this->indicesMock->expects($this->once()) + ->method('stats') + ->with(['index' => $indexName, 'metric' => 'docs']) + ->willReturn($stats); + $this->assertTrue($this->model->isEmptyIndex($indexName)); + } + + /** + * Test isEmptyIndex() method returns false. + */ + public function testIsEmptyIndexFalse() + { + $indexName = 'magento2_index'; + $stats['indices'][$indexName]['primaries']['docs']['count'] = 1; + + $this->indicesMock->expects($this->once()) + ->method('stats') + ->with(['index' => $indexName, 'metric' => 'docs']) + ->willReturn($stats); + $this->assertFalse($this->model->isEmptyIndex($indexName)); + } + + /** + * Test updateAlias() method with new index. + */ + public function testUpdateAlias() + { + $alias = 'alias1'; + $index = 'index1'; + + $params['body']['actions'][] = ['add' => ['alias' => $alias, 'index' => $index]]; + + $this->indicesMock->expects($this->once()) + ->method('updateAliases') + ->with($params); + $this->model->updateAlias($alias, $index); + } + + /** + * Test updateAlias() method with new and old index. + */ + public function testUpdateAliasRemoveOldIndex() + { + $alias = 'alias1'; + $newIndex = 'index1'; + $oldIndex = 'indexOld'; + + $params['body']['actions'][] = ['remove' => ['alias' => $alias, 'index' => $oldIndex]]; + $params['body']['actions'][] = ['add' => ['alias' => $alias, 'index' => $newIndex]]; + + $this->indicesMock->expects($this->once()) + ->method('updateAliases') + ->with($params); + $this->model->updateAlias($alias, $newIndex, $oldIndex); + } + + /** + * Test indexExists() method, case when no such index exists + */ + public function testIndexExists() + { + $this->indicesMock->expects($this->once()) + ->method('exists') + ->with(['index' => 'indexName']) + ->willReturn(true); + $this->model->indexExists('indexName'); + } + + /** + * Tests existsAlias() method checking for alias. + */ + public function testExistsAlias() + { + $alias = 'alias1'; + $params = ['name' => $alias]; + $this->indicesMock->expects($this->once()) + ->method('existsAlias') + ->with($params) + ->willReturn(true); + $this->assertTrue($this->model->existsAlias($alias)); + } + + /** + * Tests existsAlias() method checking for alias and index. + */ + public function testExistsAliasWithIndex() + { + $alias = 'alias1'; + $index = 'index1'; + $params = ['name' => $alias, 'index' => $index]; + $this->indicesMock->expects($this->once()) + ->method('existsAlias') + ->with($params) + ->willReturn(true); + $this->assertTrue($this->model->existsAlias($alias, $index)); + } + + /** + * Test getAlias() method. + */ + public function testGetAlias() + { + $alias = 'alias1'; + $params = ['name' => $alias]; + $this->indicesMock->expects($this->once()) + ->method('getAlias') + ->with($params) + ->willReturn([]); + $this->assertEquals([], $this->model->getAlias($alias)); + } + + /** + * Test createIndexIfNotExists() method, case when operation fails + * @expectedException \Exception + */ + public function testCreateIndexFailure() + { + $this->indicesMock->expects($this->once()) + ->method('create') + ->with( + [ + 'index' => 'indexName', + 'body' => [], + ] + ) + ->willThrowException(new \Exception('Something went wrong')); + $this->model->createIndex('indexName', []); + } + + /** + * Test testAddFieldsMapping() method + */ + public function testAddFieldsMapping() + { + $this->indicesMock->expects($this->once()) + ->method('putMapping') + ->with( + [ + 'index' => 'indexName', + 'type' => 'product', + 'include_type_name' => true, + 'body' => [ + 'product' => [ + 'properties' => [ + '_search' => [ + 'type' => 'text', + ], + 'name' => [ + 'type' => 'text', + ], + ], + 'dynamic_templates' => [ + [ + 'price_mapping' => [ + 'match' => 'price_*', + 'match_mapping_type' => 'string', + 'mapping' => [ + 'type' => 'float', + 'store' => true, + ], + ], + ], + [ + 'position_mapping' => [ + 'match' => 'position_*', + 'match_mapping_type' => 'string', + 'mapping' => [ + 'type' => 'integer', + 'index' => true, + ], + ], + ], + [ + 'string_mapping' => [ + 'match' => '*', + 'match_mapping_type' => 'string', + 'mapping' => [ + 'type' => 'text', + 'index' => true, + 'copy_to' => '_search' + ], + ], + ] + ], + ], + ], + ] + ); + $this->model->addFieldsMapping( + [ + 'name' => [ + 'type' => 'text', + ], + ], + 'indexName', + 'product' + ); + } + + /** + * Test testAddFieldsMapping() method + * @expectedException \Exception + */ + public function testAddFieldsMappingFailure() + { + $this->indicesMock->expects($this->once()) + ->method('putMapping') + ->with( + [ + 'index' => 'indexName', + 'type' => 'product', + 'include_type_name' => true, + 'body' => [ + 'product' => [ + 'properties' => [ + '_search' => [ + 'type' => 'text', + ], + 'name' => [ + 'type' => 'text', + ], + ], + 'dynamic_templates' => [ + [ + 'price_mapping' => [ + 'match' => 'price_*', + 'match_mapping_type' => 'string', + 'mapping' => [ + 'type' => 'float', + 'store' => true, + ], + ], + ], + [ + 'position_mapping' => [ + 'match' => 'position_*', + 'match_mapping_type' => 'string', + 'mapping' => [ + 'type' => 'integer', + 'index' => true, + ], + ], + ], + [ + 'string_mapping' => [ + 'match' => '*', + 'match_mapping_type' => 'string', + 'mapping' => [ + 'type' => 'text', + 'index' => true, + 'copy_to' => '_search' + ], + ], + ] + ], + ], + ], + ] + ) + ->willThrowException(new \Exception('Something went wrong')); + $this->model->addFieldsMapping( + [ + 'name' => [ + 'type' => 'text', + ], + ], + 'indexName', + 'product' + ); + } + + /** + * Test deleteMapping() method + */ + public function testDeleteMapping() + { + $this->indicesMock->expects($this->once()) + ->method('deleteMapping') + ->with( + [ + 'index' => 'indexName', + 'type' => 'product', + ] + ); + $this->model->deleteMapping( + 'indexName', + 'product' + ); + } + + /** + * Test deleteMapping() method + * @expectedException \Exception + */ + public function testDeleteMappingFailure() + { + $this->indicesMock->expects($this->once()) + ->method('deleteMapping') + ->with( + [ + 'index' => 'indexName', + 'type' => 'product', + ] + ) + ->willThrowException(new \Exception('Something went wrong')); + $this->model->deleteMapping( + 'indexName', + 'product' + ); + } + + /** + * Test query() method + * @return void + */ + public function testQuery() + { + $query = ['test phrase query']; + $this->elasticsearchClientMock->expects($this->once()) + ->method('search') + ->with($query) + ->willReturn([]); + $this->assertEquals([], $this->model->query($query)); + } + + /** + * Test suggest() method + * @return void + */ + public function testSuggest() + { + $query = ['query']; + $this->elasticsearchClientMock->expects($this->once()) + ->method('suggest') + ->willReturn([]); + $this->assertEquals([], $this->model->suggest($query)); + } + + /** + * Get elasticsearch client options + * + * @return array + */ + protected function getOptions() + { + return [ + 'hostname' => 'localhost', + 'port' => '9200', + 'timeout' => 15, + 'index' => 'magento2', + 'enableAuth' => 1, + 'username' => 'user', + 'password' => 'passwd', + ]; + } + + /** + * @return array + */ + private function getEmptyIndexOption() + { + return [ + 'hostname' => 'localhost', + 'port' => '9200', + 'index' => '', + 'timeout' => 15, + 'enableAuth' => 1, + 'username' => 'user', + 'password' => 'passwd', + ]; + } +} diff --git a/app/code/Magento/Elasticsearch7/composer.json b/app/code/Magento/Elasticsearch7/composer.json new file mode 100644 index 0000000000000..f5ca96990ee18 --- /dev/null +++ b/app/code/Magento/Elasticsearch7/composer.json @@ -0,0 +1,29 @@ +{ + "name": "magento/module-elasticsearch-7", + "description": "N/A", + "require": { + "php": "~7.1.3||~7.2.0||~7.3.0", + "magento/framework": "*", + "magento/module-elasticsearch": "*", + "elasticsearch/elasticsearch": "~7.6", + "magento/module-advanced-search": "*", + "magento/module-catalog-search": "*" + }, + "suggest": { + "magento/module-config": "*", + "magento/module-search": "*" + }, + "type": "magento2-module", + "license": [ + "OSL-3.0", + "AFL-3.0" + ], + "autoload": { + "files": [ + "registration.php" + ], + "psr-4": { + "Magento\\Elasticsearch7\\": "" + } + } +} diff --git a/app/code/Magento/Elasticsearch7/etc/adminhtml/system.xml b/app/code/Magento/Elasticsearch7/etc/adminhtml/system.xml new file mode 100644 index 0000000000000..8a6f991eaa5a0 --- /dev/null +++ b/app/code/Magento/Elasticsearch7/etc/adminhtml/system.xml @@ -0,0 +1,93 @@ +<?xml version="1.0"?> +<!-- +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +--> +<config xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:noNamespaceSchemaLocation="urn:magento:module:Magento_Config:etc/system_file.xsd"> + <system> + <section id="catalog"> + <group id="search"> + <!-- Elasticsearch 7.0+ --> + <field id="elasticsearch7_server_hostname" translate="label" type="text" sortOrder="81" + showInDefault="1" showInWebsite="0" showInStore="0"> + <label>Elasticsearch Server Hostname</label> + <depends> + <field id="engine">elasticsearch7</field> + </depends> + </field> + + <field id="elasticsearch7_server_port" translate="label" type="text" sortOrder="82" showInDefault="1" + showInWebsite="0" showInStore="0"> + <label>Elasticsearch Server Port</label> + <depends> + <field id="engine">elasticsearch7</field> + </depends> + </field> + + <field id="elasticsearch7_index_prefix" translate="label" type="text" sortOrder="83" showInDefault="1" + showInWebsite="0" showInStore="0"> + <label>Elasticsearch Index Prefix</label> + <depends> + <field id="engine">elasticsearch7</field> + </depends> + </field> + + <field id="elasticsearch7_enable_auth" translate="label" type="select" sortOrder="84" showInDefault="1" + showInWebsite="0" showInStore="0"> + <label>Enable Elasticsearch HTTP Auth</label> + <source_model>Magento\Config\Model\Config\Source\Yesno</source_model> + <depends> + <field id="engine">elasticsearch7</field> + </depends> + </field> + + <field id="elasticsearch7_username" translate="label" type="text" sortOrder="85" showInDefault="1" + showInWebsite="0" showInStore="0"> + <label>Elasticsearch HTTP Username</label> + <depends> + <field id="engine">elasticsearch7</field> + <field id="elasticsearch7_enable_auth">1</field> + </depends> + </field> + + <field id="elasticsearch7_password" translate="label" type="text" sortOrder="86" showInDefault="1" + showInWebsite="0" showInStore="0"> + <label>Elasticsearch HTTP Password</label> + <depends> + <field id="engine">elasticsearch7</field> + <field id="elasticsearch7_enable_auth">1</field> + </depends> + </field> + + <field id="elasticsearch7_server_timeout" translate="label" type="text" sortOrder="87" showInDefault="1" + showInWebsite="0" showInStore="0"> + <label>Elasticsearch Server Timeout</label> + <depends> + <field id="engine">elasticsearch7</field> + </depends> + </field> + + <field id="elasticsearch7_test_connect_wizard" translate="button_label" sortOrder="88" showInDefault="1" + showInWebsite="0" showInStore="0"> + <label/> + <button_label>Test Connection</button_label> + <frontend_model>Magento\Elasticsearch7\Block\Adminhtml\System\Config\TestConnection</frontend_model> + <depends> + <field id="engine">elasticsearch7</field> + </depends> + </field> + <field id="elasticsearch7_minimum_should_match" translate="label" type="text" sortOrder="89" showInDefault="1"> + <label>Minimum Terms to Match</label> + <depends> + <field id="engine">elasticsearch7</field> + </depends> + <comment><![CDATA[<a href="https://docs.magento.com/m2/ce/user_guide/catalog/search-elasticsearch.html">Learn more</a> about valid syntax.]]></comment> + <backend_model>Magento\Elasticsearch\Model\Config\Backend\MinimumShouldMatch</backend_model> + </field> + </group> + </section> + </system> +</config> diff --git a/app/code/Magento/Elasticsearch7/etc/config.xml b/app/code/Magento/Elasticsearch7/etc/config.xml new file mode 100644 index 0000000000000..72657975faebb --- /dev/null +++ b/app/code/Magento/Elasticsearch7/etc/config.xml @@ -0,0 +1,21 @@ +<?xml version="1.0"?> +<!-- +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +--> +<config xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="urn:magento:module:Magento_Store:etc/config.xsd"> + <default> + <catalog> + <search> + <elasticsearch7_server_hostname>localhost</elasticsearch7_server_hostname> + <elasticsearch7_server_port>9200</elasticsearch7_server_port> + <elasticsearch7_index_prefix>magento2</elasticsearch7_index_prefix> + <elasticsearch7_enable_auth>0</elasticsearch7_enable_auth> + <elasticsearch7_server_timeout>15</elasticsearch7_server_timeout> + <elasticsearch7_minimum_should_match/> + </search> + </catalog> + </default> +</config> diff --git a/app/code/Magento/Elasticsearch7/etc/di.xml b/app/code/Magento/Elasticsearch7/etc/di.xml new file mode 100644 index 0000000000000..a54b92a8b6f66 --- /dev/null +++ b/app/code/Magento/Elasticsearch7/etc/di.xml @@ -0,0 +1,224 @@ +<?xml version="1.0"?> +<!-- +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +--> +<config xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="urn:magento:framework:ObjectManager/etc/config.xsd"> + <type name="Magento\Elasticsearch\Model\Config"> + <arguments> + <argument name="engineList" xsi:type="array"> + <item name="elasticsearch7" xsi:type="string">elasticsearch7</item> + </argument> + </arguments> + </type> + + <type name="Magento\Search\Model\Adminhtml\System\Config\Source\Engine"> + <arguments> + <argument name="engines" xsi:type="array"> + <item sortOrder="30" name="elasticsearch7" xsi:type="string">Elasticsearch 7.0+</item> + </argument> + </arguments> + </type> + + <type name="Magento\Elasticsearch\Elasticsearch5\Model\Adapter\BatchDataMapper\CategoryFieldsProviderProxy"> + <arguments> + <argument name="categoryFieldsProviders" xsi:type="array"> + <item name="elasticsearch7" xsi:type="object">Magento\Elasticsearch\Elasticsearch5\Model\Adapter\BatchDataMapper\CategoryFieldsProvider</item> + </argument> + </arguments> + </type> + <type name="Magento\Elasticsearch\Elasticsearch5\Model\Adapter\FieldMapper\ProductFieldMapperProxy"> + <arguments> + <argument name="productFieldMappers" xsi:type="array"> + <item name="elasticsearch7" xsi:type="object">Magento\Elasticsearch7\Model\Adapter\FieldMapper\ProductFieldMapper</item> + </argument> + </arguments> + </type> + + <type name="Magento\AdvancedSearch\Model\Client\ClientResolver"> + <arguments> + <argument name="clientFactories" xsi:type="array"> + <item name="elasticsearch7" xsi:type="string">\Magento\Elasticsearch7\Model\Client\ElasticsearchFactory</item> + </argument> + <argument name="clientOptions" xsi:type="array"> + <item name="elasticsearch7" xsi:type="string">\Magento\Elasticsearch\Model\Config</item> + </argument> + </arguments> + </type> + + <type name="Magento\CatalogSearch\Model\Indexer\IndexerHandlerFactory"> + <arguments> + <argument name="handlers" xsi:type="array"> + <item name="elasticsearch7" xsi:type="string">Magento\Elasticsearch\Model\Indexer\IndexerHandler</item> + </argument> + </arguments> + </type> + + <type name="Magento\CatalogSearch\Model\Indexer\IndexStructureFactory"> + <arguments> + <argument name="structures" xsi:type="array"> + <item name="elasticsearch7" xsi:type="string">Magento\Elasticsearch\Model\Indexer\IndexStructure</item> + </argument> + </arguments> + </type> + + <type name="Magento\CatalogSearch\Model\ResourceModel\EngineProvider"> + <arguments> + <argument name="engines" xsi:type="array"> + <item name="elasticsearch7" xsi:type="string">Magento\Elasticsearch\Model\ResourceModel\Engine</item> + </argument> + </arguments> + </type> + + <type name="Magento\Search\Model\AdapterFactory"> + <arguments> + <argument name="adapters" xsi:type="array"> + <item name="elasticsearch7" xsi:type="string">\Magento\Elasticsearch7\SearchAdapter\Adapter</item> + </argument> + </arguments> + </type> + + <type name="Magento\Search\Model\EngineResolver"> + <arguments> + <argument name="engines" xsi:type="array"> + <item name="elasticsearch7" xsi:type="string">elasticsearch7</item> + </argument> + </arguments> + </type> + + <virtualType name="Magento\Elasticsearch7\Model\Client\ElasticsearchFactory" type="Magento\AdvancedSearch\Model\Client\ClientFactory"> + <arguments> + <argument name="clientClass" xsi:type="string">Magento\Elasticsearch7\Model\Client\Elasticsearch</argument> + </arguments> + </virtualType> + + <type name="Magento\Elasticsearch\Elasticsearch5\Model\Client\ClientFactoryProxy"> + <arguments> + <argument name="clientFactories" xsi:type="array"> + <item name="elasticsearch7" xsi:type="object">Magento\Elasticsearch7\Model\Client\ElasticsearchFactory</item> + </argument> + </arguments> + </type> + + <type name="Magento\Framework\Search\Dynamic\IntervalFactory"> + <arguments> + <argument name="intervals" xsi:type="array"> + <item name="elasticsearch7" xsi:type="string">Magento\Elasticsearch\Elasticsearch5\SearchAdapter\Aggregation\Interval</item> + </argument> + </arguments> + </type> + + <type name="Magento\Framework\Search\Dynamic\DataProviderFactory"> + <arguments> + <argument name="dataProviders" xsi:type="array"> + <item name="elasticsearch7" xsi:type="string">Magento\Elasticsearch\SearchAdapter\Dynamic\DataProvider</item> + </argument> + </arguments> + </type> + + <virtualType name="Magento\Elasticsearch7\Model\DataProvider\Suggestions" type="Magento\Elasticsearch\Model\DataProvider\Base\Suggestions"> + <arguments> + <argument name="fieldProvider" xsi:type="object">elasticsearch5FieldProvider</argument> + </arguments> + </virtualType> + <type name="Magento\AdvancedSearch\Model\SuggestedQueries"> + <arguments> + <argument name="data" xsi:type="array"> + <item name="elasticsearch7" xsi:type="string">Magento\Elasticsearch7\Model\DataProvider\Suggestions</item> + </argument> + </arguments> + </type> + <virtualType name="\Magento\Elasticsearch7\Model\Adapter\FieldMapper\Product\FieldProvider\FieldName\Resolver\CompositeResolver" type="\Magento\Elasticsearch\Model\Adapter\FieldMapper\Product\FieldProvider\FieldName\Resolver\CompositeResolver"> + <arguments> + <argument name="items" xsi:type="array"> + <item name="notEav" xsi:type="object">\Magento\Elasticsearch\Model\Adapter\FieldMapper\Product\FieldProvider\FieldName\Resolver\NotEavAttribute</item> + <item name="special" xsi:type="object">\Magento\Elasticsearch\Model\Adapter\FieldMapper\Product\FieldProvider\FieldName\Resolver\SpecialAttribute</item> + <item name="price" xsi:type="object">\Magento\Elasticsearch\Model\Adapter\FieldMapper\Product\FieldProvider\FieldName\Resolver\Price</item> + <item name="categoryName" xsi:type="object">\Magento\Elasticsearch\Model\Adapter\FieldMapper\Product\FieldProvider\FieldName\Resolver\CategoryName</item> + <item name="position" xsi:type="object">\Magento\Elasticsearch\Model\Adapter\FieldMapper\Product\FieldProvider\FieldName\Resolver\Position</item> + <item name="default" xsi:type="object">Magento\Elasticsearch7\Model\Adapter\FieldMapper\Product\FieldProvider\FieldName\Resolver\DefaultResolver</item> + </argument> + </arguments> + </virtualType> + <type name="Magento\Elasticsearch7\Model\Adapter\FieldMapper\Product\FieldProvider\FieldName\Resolver\DefaultResolver"> + <arguments> + <argument name="baseResolver" xsi:type="object">Magento\Elasticsearch\Model\Adapter\FieldMapper\Product\FieldProvider\FieldName\Resolver\DefaultResolver</argument> + </arguments> + </type> + <virtualType name="Magento\Elasticsearch7\Model\Adapter\FieldMapper\ProductFieldMapper" + type="Magento\Elasticsearch\Elasticsearch5\Model\Adapter\FieldMapper\ProductFieldMapper"> + <arguments> + <argument name="fieldProvider" xsi:type="object">elasticsearch5FieldProvider</argument> + <argument name="fieldNameResolver" xsi:type="object">\Magento\Elasticsearch7\Model\Adapter\FieldMapper\Product\FieldProvider\FieldName\Resolver\CompositeResolver</argument> + </arguments> + </virtualType> + + <type name="Magento\Search\Model\Search\PageSizeProvider"> + <arguments> + <argument name="pageSizeBySearchEngine" xsi:type="array"> + <item name="elasticsearch7" xsi:type="number">10000</item> + </argument> + </arguments> + </type> + + <virtualType name="elasticsearchLayerCategoryItemCollectionProvider" type="Magento\Elasticsearch\Model\Layer\Category\ItemCollectionProvider"> + <arguments> + <argument name="factories" xsi:type="array"> + <item name="elasticsearch7" xsi:type="object">elasticsearchCategoryCollectionFactory</item> + </argument> + </arguments> + </virtualType> + + <type name="Magento\CatalogSearch\Model\Search\ItemCollectionProvider"> + <arguments> + <argument name="factories" xsi:type="array"> + <item name="elasticsearch7" xsi:type="object">elasticsearchAdvancedCollectionFactory</item> + </argument> + </arguments> + </type> + + <type name="Magento\CatalogSearch\Model\Advanced\ProductCollectionPrepareStrategyProvider"> + <arguments> + <argument name="strategies" xsi:type="array"> + <item name="elasticsearch7" xsi:type="object">Magento\Elasticsearch\Model\Advanced\ProductCollectionPrepareStrategy</item> + </argument> + </arguments> + </type> + + <virtualType name="elasticsearchLayerSearchItemCollectionProvider" type="Magento\Elasticsearch\Model\Layer\Search\ItemCollectionProvider"> + <arguments> + <argument name="factories" xsi:type="array"> + <item name="elasticsearch7" xsi:type="object">elasticsearchFulltextSearchCollectionFactory</item> + </argument> + </arguments> + </virtualType> + + <type name="Magento\Config\Model\Config\TypePool"> + <arguments> + <argument name="sensitive" xsi:type="array"> + <item name="catalog/search/elasticsearch7_password" xsi:type="string">1</item> + <item name="catalog/search/elasticsearch7_server_hostname" xsi:type="string">1</item> + <item name="catalog/search/elasticsearch7_username" xsi:type="string">1</item> + </argument> + <argument name="environment" xsi:type="array"> + <item name="catalog/search/elasticsearch7_enable_auth" xsi:type="string">1</item> + <item name="catalog/search/elasticsearch7_index_prefix" xsi:type="string">1</item> + <item name="catalog/search/elasticsearch7_password" xsi:type="string">1</item> + <item name="catalog/search/elasticsearch7_server_hostname" xsi:type="string">1</item> + <item name="catalog/search/elasticsearch7_server_port" xsi:type="string">1</item> + <item name="catalog/search/elasticsearch7_username" xsi:type="string">1</item> + <item name="catalog/search/elasticsearch7_server_timeout" xsi:type="string">1</item> + </argument> + </arguments> + </type> + <type name="Magento\Elasticsearch7\Model\Client\Elasticsearch"> + <arguments> + <argument name="fieldsMappingPreprocessors" xsi:type="array"> + <item name="elasticsearch7_copy_searchable_fields_to_search_field" xsi:type="object">Magento\Elasticsearch\Model\Adapter\FieldMapper\CopySearchableFieldsToSearchField</item> + <item name="elasticsearch7_add_default_search_field" xsi:type="object">Magento\Elasticsearch\Model\Adapter\FieldMapper\AddDefaultSearchField</item> + </argument> + </arguments> + </type> +</config> diff --git a/app/code/Magento/Elasticsearch7/etc/module.xml b/app/code/Magento/Elasticsearch7/etc/module.xml new file mode 100644 index 0000000000000..836068b59ed1e --- /dev/null +++ b/app/code/Magento/Elasticsearch7/etc/module.xml @@ -0,0 +1,14 @@ +<?xml version="1.0"?> +<!-- +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +--> +<config xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="urn:magento:framework:Module/etc/module.xsd"> + <module name="Magento_Elasticsearch7"> + <sequence> + <module name="Magento_Elasticsearch"/> + </sequence> + </module> +</config> diff --git a/app/code/Magento/Elasticsearch7/etc/search_engine.xml b/app/code/Magento/Elasticsearch7/etc/search_engine.xml new file mode 100644 index 0000000000000..9633d18669141 --- /dev/null +++ b/app/code/Magento/Elasticsearch7/etc/search_engine.xml @@ -0,0 +1,12 @@ +<?xml version="1.0"?> +<!-- +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +--> +<engines xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="urn:magento:framework:Search/etc/search_engine.xsd"> + <engine name="elasticsearch7"> + <feature name="synonyms" support="true" /> + </engine> +</engines> diff --git a/app/code/Magento/Elasticsearch7/registration.php b/app/code/Magento/Elasticsearch7/registration.php new file mode 100644 index 0000000000000..63e13cfbed8f0 --- /dev/null +++ b/app/code/Magento/Elasticsearch7/registration.php @@ -0,0 +1,12 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +declare(strict_types=1); + +\Magento\Framework\Component\ComponentRegistrar::register( + \Magento\Framework\Component\ComponentRegistrar::MODULE, + 'Magento_Elasticsearch7', + __DIR__ +); diff --git a/app/code/Magento/Email/Controller/Adminhtml/Email/Template/Save.php b/app/code/Magento/Email/Controller/Adminhtml/Email/Template/Save.php index 135506f4068a8..8e0b4d30ec511 100644 --- a/app/code/Magento/Email/Controller/Adminhtml/Email/Template/Save.php +++ b/app/code/Magento/Email/Controller/Adminhtml/Email/Template/Save.php @@ -1,15 +1,63 @@ <?php /** - * * Copyright © Magento, Inc. All rights reserved. * See COPYING.txt for license details. */ namespace Magento\Email\Controller\Adminhtml\Email\Template; +use Exception; +use Magento\Backend\App\Action\Context; +use Magento\Backend\Model\Session; +use Magento\Email\Controller\Adminhtml\Email\Template; +use Magento\Email\Model\ResourceModel\Template as TemplateResource; +use Magento\Framework\App\Action\HttpPostActionInterface; +use Magento\Framework\App\ObjectManager; use Magento\Framework\App\TemplateTypesInterface; +use Magento\Framework\Registry; +use Magento\Framework\Stdlib\DateTime\DateTime; -class Save extends \Magento\Email\Controller\Adminhtml\Email\Template +/** + * Save Controller + */ +class Save extends Template implements HttpPostActionInterface { + /** + * @var DateTime + */ + private $dateTime; + + /** + * @var TemplateResource + */ + private $templateResource; + + /** + * @var Session + */ + private $backendSession; + + /** + * Save constructor + * + * @param Context $context + * @param Registry $coreRegistry + * @param DateTime|null $dateTime + * @param TemplateResource|null $templateResource + * @param Session|null $backendSession + */ + public function __construct( + Context $context, + Registry $coreRegistry, + DateTime $dateTime = null, + TemplateResource $templateResource = null, + Session $backendSession = null + ) { + $this->dateTime = $dateTime ?: ObjectManager::getInstance()->get(DateTime::class); + $this->templateResource = $templateResource ?: ObjectManager::getInstance()->get(TemplateResource::class); + $this->backendSession = $backendSession ?: ObjectManager::getInstance()->get(Session::class); + parent::__construct($context, $coreRegistry); + } + /** * Save transactional email action * @@ -18,10 +66,10 @@ class Save extends \Magento\Email\Controller\Adminhtml\Email\Template public function execute() { $request = $this->getRequest(); - $id = $this->getRequest()->getParam('id'); + $templateId = $this->getRequest()->getParam('id'); $template = $this->_initTemplate('id'); - if (!$template->getId() && $id) { + if (!$template->getId() && $templateId) { $this->messageManager->addErrorMessage(__('This email template no longer exists.')); $this->_redirect('adminhtml/*/'); return; @@ -37,7 +85,7 @@ public function execute() )->setTemplateStyles( $request->getParam('template_styles') )->setModifiedAt( - $this->_objectManager->get(\Magento\Framework\Stdlib\DateTime\DateTime::class)->gmtDate() + $this->dateTime->gmtDate() )->setOrigTemplateCode( $request->getParam('orig_template_code') )->setOrigTemplateVariables( @@ -53,17 +101,13 @@ public function execute() $template->setTemplateStyles(''); } - $template->save(); - $this->_objectManager->get(\Magento\Backend\Model\Session::class)->setFormData(false); + $this->templateResource->save($template); + + $this->backendSession->setFormData(false); $this->messageManager->addSuccessMessage(__('You saved the email template.')); $this->_redirect('adminhtml/*'); - } catch (\Exception $e) { - $this->_objectManager->get( - \Magento\Backend\Model\Session::class - )->setData( - 'email_template_form_data', - $request->getParams() - ); + } catch (Exception $e) { + $this->backendSession->setData('email_template_form_data', $request->getParams()); $this->messageManager->addErrorMessage($e->getMessage()); $this->_forward('new'); } diff --git a/app/code/Magento/Email/Model/AbstractTemplate.php b/app/code/Magento/Email/Model/AbstractTemplate.php index 8acb0a9fddb75..3d4fc252b57ff 100644 --- a/app/code/Magento/Email/Model/AbstractTemplate.php +++ b/app/code/Magento/Email/Model/AbstractTemplate.php @@ -339,7 +339,6 @@ public function loadDefault($templateId) public function getProcessedTemplate(array $variables = []) { $processor = $this->getTemplateFilter() - ->setUseSessionInUrl(false) ->setPlainTemplateMode($this->isPlain()) ->setIsChildTemplate($this->isChildTemplate()) ->setTemplateProcessor([$this, 'getTemplateContent']); diff --git a/app/code/Magento/Email/Model/Transport.php b/app/code/Magento/Email/Model/Transport.php index 79ceb56a8834d..8811809207313 100644 --- a/app/code/Magento/Email/Model/Transport.php +++ b/app/code/Magento/Email/Model/Transport.php @@ -13,12 +13,12 @@ use Magento\Framework\Mail\TransportInterface; use Magento\Framework\Phrase; use Magento\Store\Model\ScopeInterface; -use Zend\Mail\Message; -use Zend\Mail\Transport\Sendmail; +use Laminas\Mail\Message; +use Laminas\Mail\Transport\Sendmail; /** * Class that responsible for filling some message data before transporting it. - * @see \Zend\Mail\Transport\Sendmail is used for transport + * @see \Laminas\Mail\Transport\Sendmail is used for transport */ class Transport implements TransportInterface { @@ -53,7 +53,7 @@ class Transport implements TransportInterface /** * @var Sendmail */ - private $zendTransport; + private $laminasTransport; /** * @var MessageInterface @@ -79,7 +79,7 @@ public function __construct( ScopeInterface::SCOPE_STORE ); - $this->zendTransport = new Sendmail($parameters); + $this->laminasTransport = new Sendmail($parameters); $this->message = $message; } @@ -89,16 +89,16 @@ public function __construct( public function sendMessage() { try { - $zendMessage = Message::fromString($this->message->getRawMessage())->setEncoding('utf-8'); + $laminasMessage = Message::fromString($this->message->getRawMessage())->setEncoding('utf-8'); if (2 === $this->isSetReturnPath && $this->returnPathValue) { - $zendMessage->setSender($this->returnPathValue); - } elseif (1 === $this->isSetReturnPath && $zendMessage->getFrom()->count()) { - $fromAddressList = $zendMessage->getFrom(); + $laminasMessage->setSender($this->returnPathValue); + } elseif (1 === $this->isSetReturnPath && $laminasMessage->getFrom()->count()) { + $fromAddressList = $laminasMessage->getFrom(); $fromAddressList->rewind(); - $zendMessage->setSender($fromAddressList->current()->getEmail()); + $laminasMessage->setSender($fromAddressList->current()->getEmail()); } - $this->zendTransport->send($zendMessage); + $this->laminasTransport->send($laminasMessage); } catch (\Exception $e) { throw new MailException(new Phrase($e->getMessage()), $e); } diff --git a/app/code/Magento/Email/Test/Mftf/ActionGroup/AdminUploadTransactionEmailsImageActionGroup.xml b/app/code/Magento/Email/Test/Mftf/ActionGroup/AdminUploadTransactionEmailsImageActionGroup.xml new file mode 100644 index 0000000000000..a34736a71163e --- /dev/null +++ b/app/code/Magento/Email/Test/Mftf/ActionGroup/AdminUploadTransactionEmailsImageActionGroup.xml @@ -0,0 +1,30 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + /** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +--> + +<actionGroups xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/actionGroupSchema.xsd"> + <actionGroup name="AdminUploadTransactionEmailsImageActionGroup"> + <annotations> + <description>Upload logo image for email</description> + </annotations> + <arguments> + <argument name="image" type="string" defaultValue="{{MagentoLogo.file}}"/> + <argument name="width" type="string" defaultValue="200"/> + <argument name="height" type="string" defaultValue="100"/> + </arguments> + + <conditionalClick selector="{{AdminDesignConfigSection.transactionalEmailSectionHeader}}" dependentSelector="{{AdminDesignConfigSection.transactionalEmailSectionBody}}" visible="false" stepKey="openTransactionalEmailSection"/> + <waitForElementVisible selector="{{AdminDesignConfigSection.logoImageAlt}}" stepKey="waitVisibleUploadLogo"/> + <attachFile selector="{{AdminDesignConfigSection.logoUpload}}" userInput="{{image}}" stepKey="attachLogo"/> + <waitForElementVisible selector="{{AdminDesignConfigSection.logoPreview}}" stepKey="waitingForLogoToUpload"/> + <seeElement selector="{{AdminDesignConfigSection.logoPreview}}" stepKey="logoPreviewIsVisible"/> + <fillField selector="{{AdminDesignConfigSection.logoImageAlt}}" userInput="{{image}}" stepKey="fillFieldImageAlt"/> + <fillField selector="{{AdminDesignConfigSection.logoImageWidth}}" userInput="{{width}}" stepKey="fillFieldImageWidth"/> + <fillField selector="{{AdminDesignConfigSection.logoImageHeight}}" userInput="{{height}}" stepKey="fillFieldImageHeight"/> + </actionGroup> +</actionGroups> diff --git a/app/code/Magento/Email/Test/Mftf/Page/AdminEmailTemplateEditPage.xml b/app/code/Magento/Email/Test/Mftf/Page/AdminEmailTemplateEditPage.xml index f369e84abf374..9a0d852cada2c 100644 --- a/app/code/Magento/Email/Test/Mftf/Page/AdminEmailTemplateEditPage.xml +++ b/app/code/Magento/Email/Test/Mftf/Page/AdminEmailTemplateEditPage.xml @@ -7,7 +7,7 @@ --> <pages xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" - xsi:noNamespaceSchemaLocation="urn:magento:mftf:/Page/etc/PageObject.xsd"> + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Page/etc/PageObject.xsd"> <page name="AdminEmailTemplateEditPage" url="/admin/email_template/edit/id/{{templateId}}/" area="admin" module="Magento_Email" parameterized="true"> <section name="AdminEmailTemplateEditSection"/> </page> diff --git a/app/code/Magento/Email/Test/Mftf/Page/AdminEmailTemplateIndexPage.xml b/app/code/Magento/Email/Test/Mftf/Page/AdminEmailTemplateIndexPage.xml index c4ba7aa006203..ba61c42893d41 100644 --- a/app/code/Magento/Email/Test/Mftf/Page/AdminEmailTemplateIndexPage.xml +++ b/app/code/Magento/Email/Test/Mftf/Page/AdminEmailTemplateIndexPage.xml @@ -7,7 +7,7 @@ --> <pages xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" - xsi:noNamespaceSchemaLocation="urn:magento:mftf:/Page/etc/PageObject.xsd"> + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Page/etc/PageObject.xsd"> <page name="AdminEmailTemplateIndexPage" url="/admin/email_template/" area="admin" module="Magento_Email"> <section name="AdminEmailTemplateIndexSection"/> </page> diff --git a/app/code/Magento/Email/Test/Mftf/Page/AdminEmailTemplatePreviewPage.xml b/app/code/Magento/Email/Test/Mftf/Page/AdminEmailTemplatePreviewPage.xml index aae010be27fd8..ab9c84b057c1c 100644 --- a/app/code/Magento/Email/Test/Mftf/Page/AdminEmailTemplatePreviewPage.xml +++ b/app/code/Magento/Email/Test/Mftf/Page/AdminEmailTemplatePreviewPage.xml @@ -7,7 +7,7 @@ --> <pages xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" - xsi:noNamespaceSchemaLocation="urn:magento:mftf:/Page/etc/PageObject.xsd"> + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Page/etc/PageObject.xsd"> <page name="AdminEmailTemplatePreviewPage" url="/admin/email_template/preview/" area="admin" module="Magento_Email"> <section name="AdminEmailTemplatePreviewSection"/> </page> diff --git a/app/code/Magento/Email/Test/Mftf/Test/AdminEmailTemplatePreviewTest.xml b/app/code/Magento/Email/Test/Mftf/Test/AdminEmailTemplatePreviewTest.xml index ca73de1f88fcc..0d9ca6a2c195a 100644 --- a/app/code/Magento/Email/Test/Mftf/Test/AdminEmailTemplatePreviewTest.xml +++ b/app/code/Magento/Email/Test/Mftf/Test/AdminEmailTemplatePreviewTest.xml @@ -22,7 +22,7 @@ <before> <!--Login to Admin Area--> - <actionGroup ref="LoginAsAdmin" stepKey="loginToAdminArea"/> + <actionGroup ref="AdminLoginActionGroup" stepKey="loginToAdminArea"/> </before> <after> @@ -30,7 +30,7 @@ <actionGroup ref="DeleteEmailTemplateActionGroup" stepKey="deleteTemplate"/> <click selector="{{AdminDataGridHeaderSection.clearFilters}}" stepKey="clearFilters"/> <!--Logout from Admin Area--> - <actionGroup ref="logout" stepKey="logoutOfAdmin"/> + <actionGroup ref="AdminLogoutActionGroup" stepKey="logoutOfAdmin"/> </after> <actionGroup ref="CreateCustomTemplateActionGroup" stepKey="createTemplate"/> diff --git a/app/code/Magento/Email/Test/Mftf/Test/AdminMarketingEmailTemplatesNavigateMenuTest.xml b/app/code/Magento/Email/Test/Mftf/Test/AdminMarketingEmailTemplatesNavigateMenuTest.xml index d512fc263ef2c..2dace58274e29 100644 --- a/app/code/Magento/Email/Test/Mftf/Test/AdminMarketingEmailTemplatesNavigateMenuTest.xml +++ b/app/code/Magento/Email/Test/Mftf/Test/AdminMarketingEmailTemplatesNavigateMenuTest.xml @@ -20,10 +20,10 @@ <group value="mtf_migrated"/> </annotations> <before> - <actionGroup ref="LoginAsAdmin" stepKey="LoginAsAdmin"/> + <actionGroup ref="AdminLoginActionGroup" stepKey="LoginAsAdmin"/> </before> <after> - <actionGroup ref="logout" stepKey="logout"/> + <actionGroup ref="AdminLogoutActionGroup" stepKey="logout"/> </after> <actionGroup ref="AdminNavigateMenuActionGroup" stepKey="navigateToMarketingEmailTemplatesPage"> <argument name="menuUiId" value="{{AdminMenuMarketing.dataUiId}}"/> diff --git a/app/code/Magento/Email/Test/Mftf/Test/TransactionalEmailsLogoUploadTest.xml b/app/code/Magento/Email/Test/Mftf/Test/TransactionalEmailsLogoUploadTest.xml index 9e1d9c5c3cdbb..89b07e4be44e9 100644 --- a/app/code/Magento/Email/Test/Mftf/Test/TransactionalEmailsLogoUploadTest.xml +++ b/app/code/Magento/Email/Test/Mftf/Test/TransactionalEmailsLogoUploadTest.xml @@ -11,33 +11,44 @@ <test name="TransactionalEmailsLogoUploadTest"> <annotations> <features value="Email"/> - <stories value="Email"/> - <title value="MC-13908: Uploading a Transactional Emails logo"/> + <stories value="Transactional Emails logo"/> + <title value="Uploading a Transactional Emails logo"/> <description value="Transactional Emails Logo should be able to be uploaded in the admin and previewed"/> <severity value="CRITICAL"/> - <testCaseId value="MC-13908"/> - <group value="LogoUpload"/> - <skip> - <issueId value="MC-18496"/> - </skip> + <testCaseId value="MC-27620"/> + <useCaseId value="MC-10932"/> + <group value="theme"/> + <group value="email"/> </annotations> - <!--Login to Admin Area--> <before> - <actionGroup ref="LoginAsAdmin" stepKey="loginToAdminArea"/> + <!--Login to Admin Area--> + <actionGroup ref="AdminLoginActionGroup" stepKey="loginAsAdmin"/> </before> - <!--Logout from Admin Area--> <after> - <actionGroup ref="logout" stepKey="logoutOfAdmin"/> + <!-- Clear filter on Design Config Page --> + <amOnPage url="{{DesignConfigPage.url}}" stepKey="navigateToDesignConfigPage" /> + <waitForPageLoad stepKey="waitForPageLoadToViewDesignConfigPage"/> + <actionGroup ref="ClearFiltersAdminDataGridActionGroup" stepKey="clearFilter"/> + <!--Logout from Admin Area--> + <actionGroup ref="AdminLogoutActionGroup" stepKey="logoutOfAdmin"/> </after> <!--Navigate to content->Design->Config page--> <amOnPage url="{{DesignConfigPage.url}}" stepKey="navigateToDesignConfigPage" /> - <waitForPageLoad stepKey="waitForPageloadToViewDesignConfigPage"/> - <click selector="{{AdminDesignConfigSection.scopeRow('3')}}" stepKey="editStoreView"/> + <waitForPageLoad stepKey="waitForPageLoadToViewDesignConfigPage"/> + <actionGroup ref="AdminGridFilterSearchResultsBySelectActionGroup" stepKey="filterThemeDesignConfiguration"> + <argument name="attributeSelector" value="store_id"/> + <argument name="attributeValue" value="{{_defaultStore.name}}"/> + </actionGroup> + <click selector="{{AdminDesignConfigSection.scopeRow('1')}}" stepKey="editStoreView"/> <waitForPageLoad stepKey="waitForPageLoadToOpenStoreViewEditPage"/> - <!--Click Upload logo in Transactional Emails and upload the image and preview it--> - <click selector="{{AdminDesignConfigSection.logoWrapperOpen}}" stepKey="openTab" /> - <attachFile selector="{{AdminDesignConfigSection.logoUpload}}" userInput="{{MagentoLogo.file}}" stepKey="attachLogo"/> - <wait time="5" stepKey="waitingForLogoToUpload" /> - <seeElement selector="{{AdminDesignConfigSection.logoPreview}}" stepKey="LogoPreviewIsVisible"/> + <!-- Upload Image --> + <actionGroup ref="AdminUploadTransactionEmailsImageActionGroup" stepKey="uploadImage"> + <argument name="width" value="200"/> + <argument name="height" value="100"/> + </actionGroup> + <!--Save Design Configuration --> + <actionGroup ref="ClickSaveButtonActionGroup" stepKey="saveDesignConfiguration"> + <argument name="message" value="You saved the configuration."/> + </actionGroup> </test> </tests> diff --git a/app/code/Magento/Email/Test/Unit/Model/AbstractTemplateTest.php b/app/code/Magento/Email/Test/Unit/Model/AbstractTemplateTest.php index 036ab1b273fb0..8598138e77c50 100644 --- a/app/code/Magento/Email/Test/Unit/Model/AbstractTemplateTest.php +++ b/app/code/Magento/Email/Test/Unit/Model/AbstractTemplateTest.php @@ -69,9 +69,6 @@ protected function setUp() $this->design = $this->getMockBuilder(\Magento\Framework\View\DesignInterface::class) ->disableOriginalConstructor() ->getMock(); - $this->registry = $this->getMockBuilder(\Magento\Framework\Registry::class) - ->disableOriginalConstructor() - ->getMock(); $this->appEmulation = $this->getMockBuilder(\Magento\Store\Model\App\Emulation::class) ->disableOriginalConstructor() ->getMock(); @@ -177,7 +174,7 @@ public function testGetProcessedTemplate($variables, $templateType, $storeId, $e ->disableOriginalConstructor() ->getMock(); - $filterTemplate->expects($this->once()) + $filterTemplate->expects($this->never()) ->method('setUseSessionInUrl') ->with(false) ->will($this->returnSelf()); @@ -253,7 +250,6 @@ public function testGetProcessedTemplateException() $filterTemplate = $this->getMockBuilder(\Magento\Email\Model\Template\Filter::class) ->setMethods( [ - 'setUseSessionInUrl', 'setPlainTemplateMode', 'setIsChildTemplate', 'setDesignParams', @@ -267,9 +263,7 @@ public function testGetProcessedTemplateException() ) ->disableOriginalConstructor() ->getMock(); - $filterTemplate->expects($this->once()) - ->method('setUseSessionInUrl') - ->will($this->returnSelf()); + $filterTemplate->expects($this->once()) ->method('setPlainTemplateMode') ->will($this->returnSelf()); diff --git a/app/code/Magento/Email/etc/config.xml b/app/code/Magento/Email/etc/config.xml index 0731fc79c15f7..a9837a4f2917c 100644 --- a/app/code/Magento/Email/etc/config.xml +++ b/app/code/Magento/Email/etc/config.xml @@ -27,6 +27,7 @@ <disable>0</disable> <host>localhost</host> <port>25</port> + <set_return_path>0</set_return_path> </smtp> </system> <trans_email> diff --git a/app/code/Magento/Email/registration.php b/app/code/Magento/Email/registration.php index 70601ab3d3b04..132f509e93cc0 100644 --- a/app/code/Magento/Email/registration.php +++ b/app/code/Magento/Email/registration.php @@ -4,6 +4,6 @@ * See COPYING.txt for license details. */ -use \Magento\Framework\Component\ComponentRegistrar; +use Magento\Framework\Component\ComponentRegistrar; ComponentRegistrar::register(ComponentRegistrar::MODULE, 'Magento_Email', __DIR__); diff --git a/app/code/Magento/EncryptionKey/Controller/Adminhtml/Crypt/Key/Index.php b/app/code/Magento/EncryptionKey/Controller/Adminhtml/Crypt/Key/Index.php index 86fc0082f7a5a..9b841fc32c383 100644 --- a/app/code/Magento/EncryptionKey/Controller/Adminhtml/Crypt/Key/Index.php +++ b/app/code/Magento/EncryptionKey/Controller/Adminhtml/Crypt/Key/Index.php @@ -1,18 +1,40 @@ <?php /** - * * Copyright © Magento, Inc. All rights reserved. * See COPYING.txt for license details. */ +declare(strict_types=1); + namespace Magento\EncryptionKey\Controller\Adminhtml\Crypt\Key; -use Magento\Framework\App\Action\HttpGetActionInterface as HttpGetActionInterface; +use Magento\Backend\App\Action\Context; +use Magento\EncryptionKey\Block\Adminhtml\Crypt\Key\Form; +use Magento\EncryptionKey\Controller\Adminhtml\Crypt\Key; +use Magento\Framework\App\Action\HttpGetActionInterface; +use Magento\Framework\App\DeploymentConfig\Writer; /** * Key Index action */ -class Index extends \Magento\EncryptionKey\Controller\Adminhtml\Crypt\Key implements HttpGetActionInterface +class Index extends Key implements HttpGetActionInterface { + /** + * @var Writer + */ + private $writer; + + /** + * @param Context $context + * @param Writer $writer + */ + public function __construct( + Context $context, + Writer $writer + ) { + parent::__construct($context); + $this->writer = $writer; + } + /** * Render main page with form * @@ -20,10 +42,8 @@ class Index extends \Magento\EncryptionKey\Controller\Adminhtml\Crypt\Key implem */ public function execute() { - /** @var \Magento\Framework\App\DeploymentConfig\Writer $writer */ - $writer = $this->_objectManager->get(\Magento\Framework\App\DeploymentConfig\Writer::class); - if (!$writer->checkIfWritable()) { - $this->messageManager->addError(__('Deployment configuration file is not writable.')); + if (!$this->writer->checkIfWritable()) { + $this->messageManager->addErrorMessage(__('Deployment configuration file is not writable.')); } $this->_view->loadLayout(); @@ -31,8 +51,8 @@ public function execute() $this->_view->getPage()->getConfig()->getTitle()->prepend(__('Encryption Key')); if (($formBlock = $this->_view->getLayout()->getBlock('crypt.key.form')) && - ($data = $this->_objectManager->get(\Magento\Backend\Model\Session::class)->getFormData(true))) { - /* @var \Magento\EncryptionKey\Block\Adminhtml\Crypt\Key\Form $formBlock */ + ($data = $this->_session->getFormData(true))) { + /* @var Form $formBlock */ $formBlock->setFormData($data); } diff --git a/app/code/Magento/EncryptionKey/Setup/Patch/Data/SodiumChachaPatch.php b/app/code/Magento/EncryptionKey/Setup/Patch/Data/SodiumChachaPatch.php index 9331b68675b67..e04fabeb0db34 100644 --- a/app/code/Magento/EncryptionKey/Setup/Patch/Data/SodiumChachaPatch.php +++ b/app/code/Magento/EncryptionKey/Setup/Patch/Data/SodiumChachaPatch.php @@ -7,8 +7,14 @@ namespace Magento\EncryptionKey\Setup\Patch\Data; +use Magento\Config\Model\Config\Backend\Encrypted; +use Magento\Config\Model\Config\Structure; +use Magento\Framework\App\Area; +use Magento\Framework\App\State; +use Magento\Framework\Config\ScopeInterface; +use Magento\Framework\Encryption\EncryptorInterface; +use Magento\Framework\Setup\ModuleDataSetupInterface; use Magento\Framework\Setup\Patch\DataPatchInterface; -use Magento\Framework\App\ObjectManager; /** * Migrate encrypted configuration values to the latest cipher @@ -16,50 +22,50 @@ class SodiumChachaPatch implements DataPatchInterface { /** - * @var \Magento\Framework\Config\ScopeInterface - */ - private $scope; - - /** - * @var \Magento\Framework\Setup\ModuleDataSetupInterface + * @var ModuleDataSetupInterface */ private $moduleDataSetup; /** - * @var \Magento\Config\Model\Config\Structure + * @var Structure */ private $structure; /** - * @var \Magento\Framework\Encryption\EncryptorInterface + * @var EncryptorInterface */ private $encryptor; /** - * @var \Magento\Framework\App\State + * @var State */ private $state; + /** + * @var ScopeInterface + */ + private $scope; + /** * SodiumChachaPatch constructor. - * @param \Magento\Framework\Setup\ModuleDataSetupInterface $moduleDataSetup - * @param \Magento\Config\Model\Config\Structure\Proxy $structure - * @param \Magento\Framework\Encryption\EncryptorInterface $encryptor - * @param \Magento\Framework\App\State $state - * @param \Magento\Framework\Config\ScopeInterface|null $scope + * @param ModuleDataSetupInterface $moduleDataSetup + * @param Structure $structure + * @param EncryptorInterface $encryptor + * @param State $state + * @param ScopeInterface $scope */ public function __construct( - \Magento\Framework\Setup\ModuleDataSetupInterface $moduleDataSetup, - \Magento\Config\Model\Config\Structure\Proxy $structure, - \Magento\Framework\Encryption\EncryptorInterface $encryptor, - \Magento\Framework\App\State $state, - \Magento\Framework\Config\ScopeInterface $scope = null + ModuleDataSetupInterface $moduleDataSetup, + Structure $structure, + EncryptorInterface $encryptor, + State $state, + ScopeInterface $scope ) { $this->moduleDataSetup = $moduleDataSetup; $this->structure = $structure; $this->encryptor = $encryptor; $this->state = $state; - $this->scope = $scope ?? ObjectManager::getInstance()->get(\Magento\Framework\Config\ScopeInterface::class); + $this->scope = $scope; } /** @@ -72,6 +78,8 @@ public function apply() $this->reEncryptSystemConfigurationValues(); $this->moduleDataSetup->endSetup(); + + return $this; } /** @@ -106,19 +114,25 @@ private function reEncryptSystemConfigurationValues() $currentScope = $this->scope->getCurrentScope(); $structure = $this->structure; $paths = $this->state->emulateAreaCode( - \Magento\Framework\App\Area::AREA_ADMINHTML, + Area::AREA_ADMINHTML, function () use ($structure) { - $this->scope->setCurrentScope(\Magento\Framework\App\Area::AREA_ADMINHTML); + $this->scope->setCurrentScope(Area::AREA_ADMINHTML); /** Returns list of structure paths to be re encrypted */ $paths = $structure->getFieldPathsByAttribute( 'backend_model', - \Magento\Config\Model\Config\Backend\Encrypted::class + Encrypted::class ); /** Returns list of mapping between configPath => [structurePaths] */ $mappedPaths = $structure->getFieldPaths(); foreach ($mappedPaths as $mappedPath => $data) { foreach ($data as $structurePath) { - if ($structurePath !== $mappedPath && $key = array_search($structurePath, $paths)) { + if ($structurePath === $mappedPath) { + continue; + } + + $key = array_search($structurePath, $paths); + + if ($key) { $paths[$key] = $mappedPath; } } diff --git a/app/code/Magento/EncryptionKey/Test/Mftf/Test/AdminEncryptionKeyAutoGenerateKeyTest.xml b/app/code/Magento/EncryptionKey/Test/Mftf/Test/AdminEncryptionKeyAutoGenerateKeyTest.xml index ded57f4aad019..0d73802268ad1 100644 --- a/app/code/Magento/EncryptionKey/Test/Mftf/Test/AdminEncryptionKeyAutoGenerateKeyTest.xml +++ b/app/code/Magento/EncryptionKey/Test/Mftf/Test/AdminEncryptionKeyAutoGenerateKeyTest.xml @@ -20,12 +20,12 @@ <before> <!--Login to Admin Area--> - <actionGroup ref="LoginAsAdmin" stepKey="loginToAdminArea"/> + <actionGroup ref="AdminLoginActionGroup" stepKey="loginToAdminArea"/> </before> <after> <!--Logout from Admin Area--> - <actionGroup ref="logout" stepKey="logoutOfAdmin"/> + <actionGroup ref="AdminLogoutActionGroup" stepKey="logoutOfAdmin"/> </after> <actionGroup ref="AdminEncryptionKeyNavigateToChangePageActionGroup" stepKey="navigateToPage"/> diff --git a/app/code/Magento/EncryptionKey/Test/Mftf/Test/AdminEncryptionKeyManualGenerateKeyTest.xml b/app/code/Magento/EncryptionKey/Test/Mftf/Test/AdminEncryptionKeyManualGenerateKeyTest.xml index f3a9849969263..427be2edd1184 100644 --- a/app/code/Magento/EncryptionKey/Test/Mftf/Test/AdminEncryptionKeyManualGenerateKeyTest.xml +++ b/app/code/Magento/EncryptionKey/Test/Mftf/Test/AdminEncryptionKeyManualGenerateKeyTest.xml @@ -20,12 +20,12 @@ <before> <!--Login to Admin Area--> - <actionGroup ref="LoginAsAdmin" stepKey="loginToAdminArea"/> + <actionGroup ref="AdminLoginActionGroup" stepKey="loginToAdminArea"/> </before> <after> <!--Logout from Admin Area--> - <actionGroup ref="logout" stepKey="logoutOfAdmin"/> + <actionGroup ref="AdminLogoutActionGroup" stepKey="logoutOfAdmin"/> </after> <actionGroup ref="AdminEncryptionKeyNavigateToChangePageActionGroup" stepKey="navigateToPage"/> diff --git a/app/code/Magento/EncryptionKey/etc/di.xml b/app/code/Magento/EncryptionKey/etc/di.xml new file mode 100644 index 0000000000000..b4e471f4e40ef --- /dev/null +++ b/app/code/Magento/EncryptionKey/etc/di.xml @@ -0,0 +1,14 @@ +<?xml version="1.0"?> +<!-- +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +--> +<config xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="urn:magento:framework:ObjectManager/etc/config.xsd"> + <type name="\Magento\EncryptionKey\Setup\Patch\Data\SodiumChachaPatch"> + <arguments> + <argument name="structure" xsi:type="object">Magento\Config\Model\Config\Structure\Proxy</argument> + </arguments> + </type> +</config> diff --git a/app/code/Magento/EncryptionKey/registration.php b/app/code/Magento/EncryptionKey/registration.php index 4426a9200e571..773cf44a207c4 100644 --- a/app/code/Magento/EncryptionKey/registration.php +++ b/app/code/Magento/EncryptionKey/registration.php @@ -4,6 +4,6 @@ * See COPYING.txt for license details. */ -use \Magento\Framework\Component\ComponentRegistrar; +use Magento\Framework\Component\ComponentRegistrar; ComponentRegistrar::register(ComponentRegistrar::MODULE, 'Magento_EncryptionKey', __DIR__); diff --git a/app/code/Magento/Fedex/Model/Source/Generic.php b/app/code/Magento/Fedex/Model/Source/Generic.php index f245685ce0274..c918dd7ad0013 100644 --- a/app/code/Magento/Fedex/Model/Source/Generic.php +++ b/app/code/Magento/Fedex/Model/Source/Generic.php @@ -3,9 +3,13 @@ * Copyright © Magento, Inc. All rights reserved. * See COPYING.txt for license details. */ + namespace Magento\Fedex\Model\Source; -class Generic implements \Magento\Framework\Option\ArrayInterface +/** + * Fedex generic source implementation + */ +class Generic implements \Magento\Framework\Data\OptionSourceInterface { /** * @var \Magento\Fedex\Model\Carrier @@ -36,9 +40,19 @@ public function toOptionArray() { $configData = $this->_shippingFedex->getCode($this->_code); $arr = []; - foreach ($configData as $code => $title) { - $arr[] = ['value' => $code, 'label' => $title]; + if ($configData) { + $arr = array_map( + function ($code, $title) { + return [ + 'value' => $code, + 'label' => $title + ]; + }, + array_keys($configData), + $configData + ); } + return $arr; } } diff --git a/app/code/Magento/Fedex/Test/Mftf/Test/AdminCheckInputFieldsDisabledAfterAppConfigDumpTest.xml b/app/code/Magento/Fedex/Test/Mftf/Test/AdminCheckInputFieldsDisabledAfterAppConfigDumpTest.xml index f599d7ca223ae..c2678153d13f2 100644 --- a/app/code/Magento/Fedex/Test/Mftf/Test/AdminCheckInputFieldsDisabledAfterAppConfigDumpTest.xml +++ b/app/code/Magento/Fedex/Test/Mftf/Test/AdminCheckInputFieldsDisabledAfterAppConfigDumpTest.xml @@ -15,38 +15,89 @@ <conditionalClick selector="{{AdminShippingMethodFedExSection.carriersFedExTab}}" dependentSelector="{{AdminShippingMethodFedExSection.carriersFedExActive}}" visible="false" stepKey="expandFedExTab"/> <waitForElementVisible selector="{{AdminShippingMethodFedExSection.carriersFedExActive}}" stepKey="waitFedExTabOpen"/> <grabAttributeFrom selector="{{AdminShippingMethodFedExSection.carriersFedExActive}}" userInput="disabled" stepKey="grabFedExActiveDisabled"/> - <assertEquals expected='true' expectedType="string" actual="$grabFedExActiveDisabled" stepKey="assertFedExActiveDisabled"/> + <assertEquals stepKey="assertFedExActiveDisabled"> + <actualResult type="const">$grabFedExActiveDisabled</actualResult> + <expectedResult type="string">true</expectedResult> + </assertEquals> <grabAttributeFrom selector="{{AdminShippingMethodFedExSection.carriersFedExTitle}}" userInput="disabled" stepKey="grabFedExTitleDisabled"/> - <assertEquals expected='true' expectedType="string" actual="$grabFedExTitleDisabled" stepKey="assertFedExTitleDisabled"/> + <assertEquals stepKey="assertFedExTitleDisabled"> + <actualResult type="const">$grabFedExTitleDisabled</actualResult> + <expectedResult type="string">true</expectedResult> + </assertEquals> <grabAttributeFrom selector="{{AdminShippingMethodFedExSection.carriersFedExAccountId}}" userInput="disabled" stepKey="grabFedExAccountIdDisabled"/> - <assertEquals expected='true' expectedType="string" actual="$grabFedExAccountIdDisabled" stepKey="assertFedExAccountIdDisabled"/> + <assertEquals stepKey="assertFedExAccountIdDisabled"> + <actualResult type="const">$grabFedExAccountIdDisabled</actualResult> + <expectedResult type="string">true</expectedResult> + </assertEquals> <grabAttributeFrom selector="{{AdminShippingMethodFedExSection.carriersFedExMeterNumber}}" userInput="disabled" stepKey="grabFedExMeterNumberDisabled"/> - <assertEquals expected='true' expectedType="string" actual="$grabFedExMeterNumberDisabled" stepKey="assertFedExMeterNumberDisabled"/> + <assertEquals stepKey="assertFedExMeterNumberDisabled"> + <actualResult type="const">$grabFedExMeterNumberDisabled</actualResult> + <expectedResult type="string">true</expectedResult> + </assertEquals> <grabAttributeFrom selector="{{AdminShippingMethodFedExSection.carriersFedExKey}}" userInput="disabled" stepKey="grabFedExKeyDisabled"/> - <assertEquals expected='true' expectedType="string" actual="$grabFedExKeyDisabled" stepKey="assertFedExKeyDisabled"/> + <assertEquals stepKey="assertFedExKeyDisabled"> + <actualResult type="const">$grabFedExKeyDisabled</actualResult> + <expectedResult type="string">true</expectedResult> + </assertEquals> <grabAttributeFrom selector="{{AdminShippingMethodFedExSection.carriersFedExPassword}}" userInput="disabled" stepKey="grabFedExPasswordDisabled"/> - <assertEquals expected='true' expectedType="string" actual="$grabFedExPasswordDisabled" stepKey="assertFedExPasswordDisabled"/> + <assertEquals stepKey="assertFedExPasswordDisabled"> + <actualResult type="const">$grabFedExPasswordDisabled</actualResult> + <expectedResult type="string">true</expectedResult> + </assertEquals> <grabAttributeFrom selector="{{AdminShippingMethodFedExSection.carriersFedExSandboxMode}}" userInput="disabled" stepKey="grabFedExSandboxDisabled"/> - <assertEquals expected='true' expectedType="string" actual="$grabFedExSandboxDisabled" stepKey="assertFedExSandboxDisabled"/> + <assertEquals stepKey="assertFedExSandboxDisabled"> + <actualResult type="const">$grabFedExSandboxDisabled</actualResult> + <expectedResult type="string">true</expectedResult> + </assertEquals> <grabAttributeFrom selector="{{AdminShippingMethodFedExSection.carriersFedExShipmentRequestType}}" userInput="disabled" stepKey="grabFedExShipmentRequestTypeDisabled"/> - <assertEquals expected='true' expectedType="string" actual="$grabFedExShipmentRequestTypeDisabled" stepKey="assertFedExShipmentRequestTypeDisabled"/> + <assertEquals stepKey="assertFedExShipmentRequestTypeDisabled"> + <actualResult type="const">$grabFedExShipmentRequestTypeDisabled</actualResult> + <expectedResult type="string">true</expectedResult> + </assertEquals> <grabAttributeFrom selector="{{AdminShippingMethodFedExSection.carriersFedExPackaging}}" userInput="disabled" stepKey="grabFedExPackagingDisabled"/> - <assertEquals expected='true' expectedType="string" actual="$grabFedExPackagingDisabled" stepKey="assertFedExPackagingDisabled"/> + <assertEquals stepKey="assertFedExPackagingDisabled"> + <actualResult type="const">$grabFedExPackagingDisabled</actualResult> + <expectedResult type="string">true</expectedResult> + </assertEquals> <grabAttributeFrom selector="{{AdminShippingMethodFedExSection.carriersFedExDropoff}}" userInput="disabled" stepKey="grabFedExDropoffDisabled"/> - <assertEquals expected='true' expectedType="string" actual="$grabFedExDropoffDisabled" stepKey="assertFedExDropoffDisabled"/> + <assertEquals stepKey="assertFedExDropoffDisabled"> + <actualResult type="const">$grabFedExDropoffDisabled</actualResult> + <expectedResult type="string">true</expectedResult> + </assertEquals> <grabAttributeFrom selector="{{AdminShippingMethodFedExSection.carriersFedExUnitOfMeasure}}" userInput="disabled" stepKey="grabFedExUnitOfMeasureDisabled"/> - <assertEquals expected='true' expectedType="string" actual="$grabFedExUnitOfMeasureDisabled" stepKey="assertFedExUnitOfMeasureDisabled"/> + <assertEquals stepKey="assertFedExUnitOfMeasureDisabled"> + <actualResult type="const">$grabFedExUnitOfMeasureDisabled</actualResult> + <expectedResult type="string">true</expectedResult> + </assertEquals> <grabAttributeFrom selector="{{AdminShippingMethodFedExSection.carriersFedExMaxPackageWeight}}" userInput="disabled" stepKey="grabFedExMaxPackageWeightDisabled"/> - <assertEquals expected='true' expectedType="string" actual="$grabFedExMaxPackageWeightDisabled" stepKey="assertFedExMaxPackageWeightDisabled"/> + <assertEquals stepKey="assertFedExMaxPackageWeightDisabled"> + <actualResult type="const">$grabFedExMaxPackageWeightDisabled</actualResult> + <expectedResult type="string">true</expectedResult> + </assertEquals> <grabAttributeFrom selector="{{AdminShippingMethodFedExSection.carriersFedExHandlingType}}" userInput="disabled" stepKey="grabFedExHandlingTypeDisabled"/> - <assertEquals expected='true' expectedType="string" actual="$grabFedExHandlingTypeDisabled" stepKey="assertFedExHandlingTypeDisabled"/> + <assertEquals stepKey="assertFedExHandlingTypeDisabled"> + <actualResult type="const">$grabFedExHandlingTypeDisabled</actualResult> + <expectedResult type="string">true</expectedResult> + </assertEquals> <grabAttributeFrom selector="{{AdminShippingMethodFedExSection.carriersFedExHandlingAction}}" userInput="disabled" stepKey="grabFedExHandlingActionDisabled"/> - <assertEquals expected='true' expectedType="string" actual="$grabFedExHandlingActionDisabled" stepKey="assertFedExHandlingActionDisabled"/> + <assertEquals stepKey="assertFedExHandlingActionDisabled"> + <actualResult type="const">$grabFedExHandlingActionDisabled</actualResult> + <expectedResult type="string">true</expectedResult> + </assertEquals> <grabAttributeFrom selector="{{AdminShippingMethodFedExSection.carriersFedExSpecificErrMsg}}" userInput="disabled" stepKey="grabFedExSpecificErrMsgDisabled"/> - <assertEquals expected='true' expectedType="string" actual="$grabFedExSpecificErrMsgDisabled" stepKey="assertFedExSpecificErrMsgDisabled"/> + <assertEquals stepKey="assertFedExSpecificErrMsgDisabled"> + <actualResult type="const">$grabFedExSpecificErrMsgDisabled</actualResult> + <expectedResult type="string">true</expectedResult> + </assertEquals> <grabAttributeFrom selector="{{AdminShippingMethodFedExSection.carriersFedExAllowSpecific}}" userInput="disabled" stepKey="grabFedExAllowSpecificDisabled"/> - <assertEquals expected='true' expectedType="string" actual="$grabFedExAllowSpecificDisabled" stepKey="assertFedExAllowSpecificDisabled"/> + <assertEquals stepKey="assertFedExAllowSpecificDisabled"> + <actualResult type="const">$grabFedExAllowSpecificDisabled</actualResult> + <expectedResult type="string">true</expectedResult> + </assertEquals> <grabAttributeFrom selector="{{AdminShippingMethodFedExSection.carriersFedExSpecificCountry}}" userInput="disabled" stepKey="grabFedExSpecificCountryDisabled"/> - <assertEquals expected='true' expectedType="string" actual="$grabFedExSpecificCountryDisabled" stepKey="assertFedExSpecificCountryDisabled"/> + <assertEquals stepKey="assertFedExSpecificCountryDisabled"> + <actualResult type="const">$grabFedExSpecificCountryDisabled</actualResult> + <expectedResult type="string">true</expectedResult> + </assertEquals> </test> </tests> diff --git a/app/code/Magento/Fedex/Test/Mftf/Test/AdminCreatingShippingLabelTest.xml b/app/code/Magento/Fedex/Test/Mftf/Test/AdminCreatingShippingLabelTest.xml index 928ce5a2a918f..3850bfabc0c34 100644 --- a/app/code/Magento/Fedex/Test/Mftf/Test/AdminCreatingShippingLabelTest.xml +++ b/app/code/Magento/Fedex/Test/Mftf/Test/AdminCreatingShippingLabelTest.xml @@ -23,7 +23,7 @@ </skip> </annotations> <before> - <actionGroup ref="LoginAsAdmin" stepKey="loginAsAdmin"/> + <actionGroup ref="AdminLoginActionGroup" stepKey="loginAsAdmin"/> <!-- Create product --> <createData entity="SimpleSubCategory" stepKey="createCategory"/> <createData entity="SimpleProduct" stepKey="createProduct"> @@ -75,10 +75,12 @@ <!--Delete created data--> <deleteData createDataKey="createProduct" stepKey="deleteProduct"/> <deleteData createDataKey="createCategory" stepKey="deleteCategory"/> - <actionGroup ref="logout" stepKey="logout"/> + <actionGroup ref="AdminLogoutActionGroup" stepKey="logout"/> </after> <!--Add country of manufacture to product--> - <amOnPage url="{{AdminProductEditPage.url($$createProduct.id$$)}}" stepKey="amOnEditPage"/> + <actionGroup ref="AdminProductPageOpenByIdActionGroup" stepKey="amOnEditPage"> + <argument name="productId" value="$$createProduct.id$$"/> + </actionGroup> <waitForPageLoad stepKey="waitForEditPage"/> <actionGroup ref="AdminFillProductCountryOfManufactureActionGroup" stepKey="fillCountryOfManufacture"> <argument name="countryId" value="DE"/> @@ -111,7 +113,7 @@ <!--Create Invoice--> <actionGroup ref="AdminCreateInvoiceActionGroup" stepKey="createInvoice"/> <!--Create shipping label--> - <actionGroup ref="goToShipmentIntoOrder" stepKey="goToShipmentIntoOrder"/> + <actionGroup ref="GoToShipmentIntoOrderActionGroup" stepKey="goToShipmentIntoOrder"/> <checkOption selector="{{AdminShipmentTotalSection.createShippingLabel}}" stepKey="checkCreateShippingLabel"/> <click selector="{{AdminShipmentMainActionsSection.submitShipment}}" stepKey="clickSubmitShipment"/> <actionGroup ref="AdminShipmentCreateShippingLabelActionGroup" stepKey="createPackage"> @@ -124,7 +126,13 @@ <seeElement selector="{{AdminShipmentTrackingInformationShippingSection.shippingNumber}}" stepKey="seeShippingNumberElement"/> <grabTextFrom selector="{{AdminShipmentTrackingInformationShippingSection.shippingMethod}}" stepKey="grabShippingMethod"/> <grabTextFrom selector="{{AdminShipmentTrackingInformationShippingSection.shippingMethodTitle}}" stepKey="grabShippingMethodTitle"/> - <assertEquals actual="$grabShippingMethod" expectedType="string" expected="Federal Express" stepKey="assertShippingMethodIsFedEx"/> - <assertEquals actual="$grabShippingMethodTitle" expectedType="string" expected="Federal Express" stepKey="assertShippingMethodTitleIsFedEx"/> + <assertEquals stepKey="assertShippingMethodIsFedEx"> + <actualResult type="const">$grabShippingMethod</actualResult> + <expectedResult type="string">Federal Express</expectedResult> + </assertEquals> + <assertEquals stepKey="assertShippingMethodTitleIsFedEx"> + <actualResult type="const">$grabShippingMethodTitle</actualResult> + <expectedResult type="string">Federal Express</expectedResult> + </assertEquals> </test> </tests> diff --git a/app/code/Magento/Fedex/Test/Unit/Model/Source/GenericTest.php b/app/code/Magento/Fedex/Test/Unit/Model/Source/GenericTest.php new file mode 100644 index 0000000000000..ac63442124920 --- /dev/null +++ b/app/code/Magento/Fedex/Test/Unit/Model/Source/GenericTest.php @@ -0,0 +1,94 @@ +<?php + +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ + +declare(strict_types=1); + +namespace Magento\Fedex\Test\Unit\Model\Source; + +use Magento\Fedex\Model\Source\Generic; +use PHPUnit\Framework\TestCase; +use PHPUnit\Framework\MockObject\MockObject; +use Magento\Fedex\Model\Carrier; +use Magento\Framework\TestFramework\Unit\Helper\ObjectManager as ObjectManagerHelper; + +/** + * Unit test for Magento\Fedex\Test\Unit\Model\Source\Generic + */ +class GenericTest extends TestCase +{ + /** + * @var Generic + */ + private $model; + + /** + * @var Carrier|MockObject + */ + private $shippingFedexMock; + + /** + * @inheritdoc + */ + protected function setUp(): void + { + $this->shippingFedexMock = $this->createMock(Carrier::class); + + $objectManager = new ObjectManagerHelper($this); + $this->model = $objectManager->getObject( + Generic::class, + [ + 'shippingFedex' => $this->shippingFedexMock + ] + ); + } + + /** + * Test toOptionArray + * + * @param string $code + * @param array|false $methods + * @param array $result + * @return void + * @dataProvider toOptionArrayDataProvider + */ + public function testToOptionArray($code, $methods, $result): void + { + $this->model->code = $code; + $this->shippingFedexMock->expects($this->once()) + ->method('getCode') + ->willReturn($methods); + + $this->assertEquals($result, $this->model->toOptionArray()); + } + + /** + * Data provider for testToOptionArray() + * + * @return array + */ + public function toOptionArrayDataProvider(): array + { + return [ + [ + 'method', + [ + 'FEDEX_GROUND' => __('Ground'), + 'FIRST_OVERNIGHT' => __('First Overnight') + ], + [ + ['value' => 'FEDEX_GROUND', 'label' => __('Ground')], + ['value' => 'FIRST_OVERNIGHT', 'label' => __('First Overnight')] + ] + ], + [ + '', + false, + [] + ] + ]; + } +} diff --git a/app/code/Magento/Fedex/Test/Unit/Plugin/Block/DataProviders/Tracking/ChangeTitleTest.php b/app/code/Magento/Fedex/Test/Unit/Plugin/Block/DataProviders/Tracking/ChangeTitleTest.php new file mode 100644 index 0000000000000..ff1276044d731 --- /dev/null +++ b/app/code/Magento/Fedex/Test/Unit/Plugin/Block/DataProviders/Tracking/ChangeTitleTest.php @@ -0,0 +1,80 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ + +declare(strict_types=1); + +namespace Magento\Fedex\Test\Unit\Plugin\Block\DataProviders\Tracking; + +use Magento\Fedex\Model\Carrier; +use Magento\Fedex\Plugin\Block\DataProviders\Tracking\ChangeTitle; +use Magento\Framework\Phrase; +use Magento\Framework\TestFramework\Unit\Helper\ObjectManager; +use Magento\Shipping\Block\DataProviders\Tracking\DeliveryDateTitle; +use Magento\Shipping\Model\Tracking\Result\Status; +use PHPUnit\Framework\MockObject\MockObject; +use PHPUnit\Framework\TestCase; + +/** + * Unit Test for @see ChangeTitle + */ +class ChangeTitleTest extends TestCase +{ + /** + * @var ChangeTitle|MockObject + */ + private $plugin; + + /** + * @inheritDoc + */ + protected function setUp() + { + $objectManagerHelper = new ObjectManager($this); + $this->plugin = $objectManagerHelper->getObject(ChangeTitle::class); + } + + /** + * Check if Title was changed + * + * @param string $carrierCode + * @param string $originalResult + * @param Phrase|string $finalResult + * @dataProvider testAfterGetTitleDataProvider + */ + public function testAfterGetTitle(string $carrierCode, string $originalResult, $finalResult) + { + /** @var DeliveryDateTitle|MockObject $subjectMock */ + $subjectMock = $this->getMockBuilder(DeliveryDateTitle::class) + ->disableOriginalConstructor() + ->getMock(); + + /** @var Status|MockObject $trackingStatusMock */ + $trackingStatusMock = $this->getMockBuilder(Status::class) + ->disableOriginalConstructor() + ->setMethods(['getCarrier']) + ->getMock(); + $trackingStatusMock->expects($this::once()) + ->method('getCarrier') + ->willReturn($carrierCode); + + $actual = $this->plugin->afterGetTitle($subjectMock, $originalResult, $trackingStatusMock); + + $this->assertEquals($finalResult, $actual); + } + + /** + * Data provider + * + * @return array + */ + public function testAfterGetTitleDataProvider(): array + { + return [ + [Carrier::CODE, 'Original Title', __('Expected Delivery:')], + ['not-fedex', 'Original Title', 'Original Title'], + ]; + } +} diff --git a/app/code/Magento/Fedex/Test/Unit/Plugin/Block/Tracking/PopupDeliveryDateTest.php b/app/code/Magento/Fedex/Test/Unit/Plugin/Block/Tracking/PopupDeliveryDateTest.php new file mode 100644 index 0000000000000..f9865793129d3 --- /dev/null +++ b/app/code/Magento/Fedex/Test/Unit/Plugin/Block/Tracking/PopupDeliveryDateTest.php @@ -0,0 +1,122 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ + +declare(strict_types=1); + +namespace Magento\Fedex\Test\Unit\Plugin\Block\Tracking; + +use Magento\Fedex\Model\Carrier; +use Magento\Fedex\Plugin\Block\Tracking\PopupDeliveryDate; +use Magento\Framework\TestFramework\Unit\Helper\ObjectManager; +use Magento\Shipping\Block\Tracking\Popup; +use Magento\Shipping\Model\Tracking\Result\Status; +use PHPUnit\Framework\MockObject\MockObject; +use PHPUnit\Framework\TestCase; + +/** + * Unit Test for \Magento\Fedex\Plugin\Block\Tracking\PopupDeliveryDate + */ +class PopupDeliveryDateTest extends TestCase +{ + const STUB_CARRIER_CODE_NOT_FEDEX = 'not-fedex'; + const STUB_DELIVERY_DATE = '2020-02-02'; + const STUB_DELIVERY_TIME = '12:00'; + + /** + * @var MockObject|PopupDeliveryDate + */ + private $plugin; + + /** + * @var MockObject|Status $trackingStatusMock + */ + private $trackingStatusMock; + + /** + * @var MockObject|Popup $subjectMock + */ + private $subjectMock; + + /** + * @inheritDoc + */ + protected function setUp() + { + $this->trackingStatusMock = $this->getStatusMock(); + $this->subjectMock = $this->getPopupMock(); + $this->subjectMock->expects($this->once()) + ->method('getTrackingInfo') + ->willReturn([[$this->trackingStatusMock]]); + + $objectManagerHelper = new ObjectManager($this); + $this->plugin = $objectManagerHelper->getObject(PopupDeliveryDate::class); + } + + /** + * Test the method with Fedex carrier + */ + public function testAfterFormatDeliveryDateTimeWithFedexCarrier() + { + $this->trackingStatusMock->expects($this::once()) + ->method('getCarrier') + ->willReturn(Carrier::CODE); + $this->subjectMock->expects($this->once())->method('formatDeliveryDate'); + + $this->executeOriginalMethod(); + } + + /** + * Test the method with a different carrier + */ + public function testAfterFormatDeliveryDateTimeWithOtherCarrier() + { + $this->trackingStatusMock->expects($this::once()) + ->method('getCarrier') + ->willReturn(self::STUB_CARRIER_CODE_NOT_FEDEX); + $this->subjectMock->expects($this->never())->method('formatDeliveryDate'); + + $this->executeOriginalMethod(); + } + + /** + * Returns Mock for \Magento\Shipping\Model\Tracking\Result\Status + * + * @return MockObject + */ + private function getStatusMock(): MockObject + { + return $this->getMockBuilder(Status::class) + ->disableOriginalConstructor() + ->setMethods(['getCarrier']) + ->getMock(); + } + + /** + * Returns Mock for \Magento\Shipping\Block\Tracking\Popup + * + * @return MockObject + */ + private function getPopupMock(): MockObject + { + return $this->getMockBuilder(Popup::class) + ->disableOriginalConstructor() + ->setMethods(['formatDeliveryDate', 'getTrackingInfo']) + ->getMock(); + } + + /** + * Run plugin's original method + */ + private function executeOriginalMethod() + { + $this->plugin->afterFormatDeliveryDateTime( + $this->subjectMock, + 'Test Result', + self::STUB_DELIVERY_DATE, + self::STUB_DELIVERY_TIME + ); + } +} diff --git a/app/code/Magento/Fedex/etc/adminhtml/system.xml b/app/code/Magento/Fedex/etc/adminhtml/system.xml index fdbe10a1a352e..f164a8e21e0ae 100644 --- a/app/code/Magento/Fedex/etc/adminhtml/system.xml +++ b/app/code/Magento/Fedex/etc/adminhtml/system.xml @@ -10,101 +10,101 @@ <section id="carriers"> <group id="fedex" translate="label" type="text" sortOrder="120" showInDefault="1" showInWebsite="1" showInStore="1"> <label>FedEx</label> - <field id="active" translate="label" type="select" sortOrder="10" showInDefault="1" showInWebsite="1" showInStore="0" canRestore="1"> + <field id="active" translate="label" type="select" sortOrder="10" showInDefault="1" showInWebsite="1" canRestore="1"> <label>Enabled for Checkout</label> <source_model>Magento\Config\Model\Config\Source\Yesno</source_model> </field> <field id="title" translate="label" type="text" sortOrder="20" showInDefault="1" showInWebsite="1" showInStore="1" canRestore="1"> <label>Title</label> </field> - <field id="account" translate="label comment" type="obscure" sortOrder="40" showInDefault="1" showInWebsite="1" showInStore="0"> + <field id="account" translate="label comment" type="obscure" sortOrder="40" showInDefault="1" showInWebsite="1"> <label>Account ID</label> <backend_model>Magento\Config\Model\Config\Backend\Encrypted</backend_model> <comment>Please make sure to use only digits here. No dashes are allowed.</comment> </field> - <field id="meter_number" translate="label" type="obscure" sortOrder="50" showInDefault="1" showInWebsite="1" showInStore="0"> + <field id="meter_number" translate="label" type="obscure" sortOrder="50" showInDefault="1" showInWebsite="1"> <label>Meter Number</label> <backend_model>Magento\Config\Model\Config\Backend\Encrypted</backend_model> </field> - <field id="key" translate="label" type="obscure" sortOrder="60" showInDefault="1" showInWebsite="1" showInStore="0"> + <field id="key" translate="label" type="obscure" sortOrder="60" showInDefault="1" showInWebsite="1"> <label>Key</label> <backend_model>Magento\Config\Model\Config\Backend\Encrypted</backend_model> </field> - <field id="password" translate="label" type="obscure" sortOrder="70" showInDefault="1" showInWebsite="1" showInStore="0"> + <field id="password" translate="label" type="obscure" sortOrder="70" showInDefault="1" showInWebsite="1"> <label>Password</label> <backend_model>Magento\Config\Model\Config\Backend\Encrypted</backend_model> </field> - <field id="sandbox_mode" translate="label" type="select" sortOrder="80" showInDefault="1" showInWebsite="1" showInStore="0" canRestore="1"> + <field id="sandbox_mode" translate="label" type="select" sortOrder="80" showInDefault="1" showInWebsite="1" canRestore="1"> <label>Sandbox Mode</label> <source_model>Magento\Config\Model\Config\Source\Yesno</source_model> </field> - <field id="production_webservices_url" translate="label" type="text" sortOrder="90" showInDefault="1" showInWebsite="1" showInStore="0" canRestore="1"> + <field id="production_webservices_url" translate="label" type="text" sortOrder="90" showInDefault="1" showInWebsite="1" canRestore="1"> <label>Web-Services URL (Production)</label> <depends> <field id="sandbox_mode">0</field> </depends> </field> - <field id="sandbox_webservices_url" translate="label" type="text" sortOrder="100" showInDefault="1" showInWebsite="1" showInStore="0" canRestore="1"> + <field id="sandbox_webservices_url" translate="label" type="text" sortOrder="100" showInDefault="1" showInWebsite="1" canRestore="1"> <label>Web-Services URL (Sandbox)</label> <depends> <field id="sandbox_mode">1</field> </depends> </field> - <field id="shipment_requesttype" translate="label" type="select" sortOrder="110" showInDefault="1" showInWebsite="1" showInStore="0" canRestore="1"> + <field id="shipment_requesttype" translate="label" type="select" sortOrder="110" showInDefault="1" showInWebsite="1" canRestore="1"> <label>Packages Request Type</label> <source_model>Magento\Shipping\Model\Config\Source\Online\Requesttype</source_model> </field> - <field id="packaging" translate="label" type="select" sortOrder="120" showInDefault="1" showInWebsite="1" showInStore="0" canRestore="1"> + <field id="packaging" translate="label" type="select" sortOrder="120" showInDefault="1" showInWebsite="1" canRestore="1"> <label>Packaging</label> <source_model>Magento\Fedex\Model\Source\Packaging</source_model> </field> - <field id="dropoff" translate="label" type="select" sortOrder="130" showInDefault="1" showInWebsite="1" showInStore="0" canRestore="1"> + <field id="dropoff" translate="label" type="select" sortOrder="130" showInDefault="1" showInWebsite="1" canRestore="1"> <label>Dropoff</label> <source_model>Magento\Fedex\Model\Source\Dropoff</source_model> </field> - <field id="unit_of_measure" translate="label" type="select" sortOrder="135" showInDefault="1" showInWebsite="1" showInStore="0" canRestore="1"> + <field id="unit_of_measure" translate="label" type="select" sortOrder="135" showInDefault="1" showInWebsite="1" canRestore="1"> <label>Weight Unit</label> <source_model>Magento\Fedex\Model\Source\Unitofmeasure</source_model> </field> - <field id="max_package_weight" translate="label" type="text" sortOrder="140" showInDefault="1" showInWebsite="1" showInStore="0" canRestore="1"> + <field id="max_package_weight" translate="label" type="text" sortOrder="140" showInDefault="1" showInWebsite="1" canRestore="1"> <label>Maximum Package Weight (Please consult your shipping carrier for maximum supported shipping weight)</label> <validate>validate-number validate-zero-or-greater</validate> </field> - <field id="handling_type" translate="label" type="select" sortOrder="150" showInDefault="1" showInWebsite="1" showInStore="0" canRestore="1"> + <field id="handling_type" translate="label" type="select" sortOrder="150" showInDefault="1" showInWebsite="1" canRestore="1"> <label>Calculate Handling Fee</label> <source_model>Magento\Shipping\Model\Source\HandlingType</source_model> </field> - <field id="handling_action" translate="label" type="select" sortOrder="160" showInDefault="1" showInWebsite="1" showInStore="0" canRestore="1"> + <field id="handling_action" translate="label" type="select" sortOrder="160" showInDefault="1" showInWebsite="1" canRestore="1"> <label>Handling Applied</label> <source_model>Magento\Shipping\Model\Source\HandlingAction</source_model> </field> - <field id="handling_fee" translate="label" type="text" sortOrder="170" showInDefault="1" showInWebsite="1" showInStore="0"> + <field id="handling_fee" translate="label" type="text" sortOrder="170" showInDefault="1" showInWebsite="1"> <label>Handling Fee</label> <validate>validate-number validate-zero-or-greater</validate> </field> - <field id="residence_delivery" translate="label" type="select" sortOrder="180" showInDefault="1" showInWebsite="1" showInStore="0"> + <field id="residence_delivery" translate="label" type="select" sortOrder="180" showInDefault="1" showInWebsite="1"> <label>Residential Delivery</label> <source_model>Magento\Config\Model\Config\Source\Yesno</source_model> </field> - <field id="allowed_methods" translate="label" type="multiselect" sortOrder="190" showInDefault="1" showInWebsite="1" showInStore="0" canRestore="1"> + <field id="allowed_methods" translate="label" type="multiselect" sortOrder="190" showInDefault="1" showInWebsite="1" canRestore="1"> <label>Allowed Methods</label> <source_model>Magento\Fedex\Model\Source\Method</source_model> <can_be_empty>1</can_be_empty> </field> - <field id="smartpost_hubid" translate="label comment" type="text" sortOrder="200" showInDefault="1" showInWebsite="1" showInStore="0"> + <field id="smartpost_hubid" translate="label comment" type="text" sortOrder="200" showInDefault="1" showInWebsite="1"> <label>Hub ID</label> <comment>The field is applicable if the Smart Post method is selected.</comment> </field> - <field id="free_method" translate="label" type="select" sortOrder="210" showInDefault="1" showInWebsite="1" showInStore="0" canRestore="1"> + <field id="free_method" translate="label" type="select" sortOrder="210" showInDefault="1" showInWebsite="1" canRestore="1"> <label>Free Method</label> <frontend_class>free-method</frontend_class> <source_model>Magento\Fedex\Model\Source\Freemethod</source_model> </field> - <field id="free_shipping_enable" translate="label" type="select" sortOrder="220" showInDefault="1" showInWebsite="1" showInStore="0"> + <field id="free_shipping_enable" translate="label" type="select" sortOrder="220" showInDefault="1" showInWebsite="1"> <label>Enable Free Shipping Threshold</label> <source_model>Magento\Config\Model\Config\Source\Enabledisable</source_model> </field> - <field id="free_shipping_subtotal" translate="label" type="text" sortOrder="230" showInDefault="1" showInWebsite="1" showInStore="0"> + <field id="free_shipping_subtotal" translate="label" type="text" sortOrder="230" showInDefault="1" showInWebsite="1"> <label>Free Shipping Amount Threshold</label> <validate>validate-number validate-zero-or-greater</validate> <depends> @@ -114,26 +114,26 @@ <field id="specificerrmsg" translate="label" type="textarea" sortOrder="240" showInDefault="1" showInWebsite="1" showInStore="1" canRestore="1"> <label>Displayed Error Message</label> </field> - <field id="sallowspecific" translate="label" type="select" sortOrder="250" showInDefault="1" showInWebsite="1" showInStore="0" canRestore="1"> + <field id="sallowspecific" translate="label" type="select" sortOrder="250" showInDefault="1" showInWebsite="1" canRestore="1"> <label>Ship to Applicable Countries</label> <frontend_class>shipping-applicable-country</frontend_class> <source_model>Magento\Shipping\Model\Config\Source\Allspecificcountries</source_model> </field> - <field id="specificcountry" translate="label" type="multiselect" sortOrder="260" showInDefault="1" showInWebsite="1" showInStore="0"> + <field id="specificcountry" translate="label" type="multiselect" sortOrder="260" showInDefault="1" showInWebsite="1"> <label>Ship to Specific Countries</label> <source_model>Magento\Directory\Model\Config\Source\Country</source_model> <can_be_empty>1</can_be_empty> </field> - <field id="debug" translate="label" type="select" sortOrder="270" showInDefault="1" showInWebsite="1" showInStore="0"> + <field id="debug" translate="label" type="select" sortOrder="270" showInDefault="1" showInWebsite="1"> <label>Debug</label> <source_model>Magento\Config\Model\Config\Source\Yesno</source_model> </field> - <field id="showmethod" translate="label" type="select" sortOrder="280" showInDefault="1" showInWebsite="1" showInStore="0"> + <field id="showmethod" translate="label" type="select" sortOrder="280" showInDefault="1" showInWebsite="1"> <label>Show Method if Not Applicable</label> <frontend_class>shipping-skip-hide</frontend_class> <source_model>Magento\Config\Model\Config\Source\Yesno</source_model> </field> - <field id="sort_order" translate="label" type="text" sortOrder="290" showInDefault="1" showInWebsite="1" showInStore="0"> + <field id="sort_order" translate="label" type="text" sortOrder="290" showInDefault="1" showInWebsite="1"> <label>Sort Order</label> <validate>validate-number validate-zero-or-greater</validate> </field> diff --git a/app/code/Magento/Fedex/registration.php b/app/code/Magento/Fedex/registration.php index bb44e20ec4674..3562091f9ba68 100644 --- a/app/code/Magento/Fedex/registration.php +++ b/app/code/Magento/Fedex/registration.php @@ -4,6 +4,6 @@ * See COPYING.txt for license details. */ -use \Magento\Framework\Component\ComponentRegistrar; +use Magento\Framework\Component\ComponentRegistrar; ComponentRegistrar::register(ComponentRegistrar::MODULE, 'Magento_Fedex', __DIR__); diff --git a/app/code/Magento/GiftMessage/Model/Plugin/OrderItemGet.php b/app/code/Magento/GiftMessage/Model/Plugin/OrderItemGet.php new file mode 100644 index 0000000000000..3de69da37eef8 --- /dev/null +++ b/app/code/Magento/GiftMessage/Model/Plugin/OrderItemGet.php @@ -0,0 +1,76 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +declare(strict_types=1); + +namespace Magento\GiftMessage\Model\Plugin; + +use Magento\Framework\Exception\NoSuchEntityException; +use Magento\GiftMessage\Api\OrderItemRepositoryInterface as GiftMessageItemRepositoryInterface; +use Magento\Sales\Api\Data\OrderItemExtensionFactory; +use Magento\Sales\Api\Data\OrderItemInterface; +use Magento\Sales\Api\OrderItemRepositoryInterface; + +/** + * Plugin for adding gift message to order item + */ +class OrderItemGet +{ + + /** + * @var OrderItemExtensionFactory + */ + private $orderItemExtensionFactory; + + /** + * @var GiftMessageItemRepositoryInterface + */ + private $giftMessageItemRepository; + + /** + * OrderItemGet constructor. + * + * @param GiftMessageItemRepositoryInterface $giftMessageItemRepository + * @param OrderItemExtensionFactory $orderItemExtensionFactory + */ + public function __construct( + GiftMessageItemRepositoryInterface $giftMessageItemRepository, + OrderItemExtensionFactory $orderItemExtensionFactory + ) { + $this->giftMessageItemRepository = $giftMessageItemRepository; + $this->orderItemExtensionFactory = $orderItemExtensionFactory; + } + + /** + * Add gift message for order item + * + * @param OrderItemRepositoryInterface $subject + * @param OrderItemInterface $orderItem + * @return OrderItemInterface + * @SuppressWarnings(PHPMD.UnusedFormalParameter) + */ + public function afterGet(OrderItemRepositoryInterface $subject, OrderItemInterface $orderItem) + { + $extensionAttributes = $orderItem->getExtensionAttributes(); + if ($extensionAttributes && $extensionAttributes->getGiftMessage()) { + return $orderItem; + } + try { + /* @var \Magento\GiftMessage\Api\Data\MessageInterface $giftMessage */ + $giftMessage = $this->giftMessageItemRepository->get( + $orderItem->getOrderId(), + $orderItem->getItemId() + ); + } catch (NoSuchEntityException $e) { + return $orderItem; + } + /** @var \Magento\Sales\Api\Data\OrderItemExtension $orderItemExtension */ + $orderItemExtension = $extensionAttributes ?: $this->orderItemExtensionFactory->create(); + $orderItemExtension->setGiftMessage($giftMessage); + $orderItem->setExtensionAttributes($orderItemExtension); + + return $orderItem; + } +} diff --git a/app/code/Magento/GiftMessage/Test/Mftf/Metadata/gift_options-meta.xml b/app/code/Magento/GiftMessage/Test/Mftf/Metadata/GiftOptionsMeta.xml similarity index 100% rename from app/code/Magento/GiftMessage/Test/Mftf/Metadata/gift_options-meta.xml rename to app/code/Magento/GiftMessage/Test/Mftf/Metadata/GiftOptionsMeta.xml diff --git a/app/code/Magento/GiftMessage/Test/Unit/Model/Plugin/MergeQuoteItemsTest.php b/app/code/Magento/GiftMessage/Test/Unit/Model/Plugin/MergeQuoteItemsTest.php new file mode 100644 index 0000000000000..873a31aba46df --- /dev/null +++ b/app/code/Magento/GiftMessage/Test/Unit/Model/Plugin/MergeQuoteItemsTest.php @@ -0,0 +1,87 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ + +declare(strict_types=1); + +namespace Magento\GiftMessage\Test\Unit\Model\Plugin; + +use Magento\Framework\TestFramework\Unit\Helper\ObjectManager as ObjectManagerHelper; +use Magento\GiftMessage\Model\Plugin\MergeQuoteItems; +use Magento\Quote\Model\Quote\Item; +use Magento\Quote\Model\Quote\Item\Processor; +use PHPUnit\Framework\MockObject\MockObject; +use PHPUnit\Framework\TestCase; + +/** + * Unit Test for \Magento\GiftMessage\Model\Plugin\MergeQuoteItems + */ +class MergeQuoteItemsTest extends TestCase +{ + private const STUB_GIFT_MESSAGE = 'message'; + + /** + * @var MergeQuoteItems + */ + private $plugin; + + /** + * @var Processor|MockObject + */ + private $processorMock; + + /** + * @var Item|MockObject + */ + private $resultMock; + + /** + * @var Item|MockObject + */ + private $sourceMock; + + /** + * @inheritDoc + */ + protected function setUp(): void + { + $this->plugin = (new ObjectManagerHelper($this))->getObject(MergeQuoteItems::class); + $this->processorMock = $this->createMock(Processor::class); + $this->resultMock = $this->createPartialMock(Item::class, ['setGiftMessageId']); + $this->sourceMock = $this->createPartialMock(Item::class, ['getGiftMessageId']); + } + + /** + * Test case when a source item has a Gift message. + */ + public function testAfterMergeExpectsSetGiftMessageIdCalled(): void + { + $this->sourceMock->expects($this->once()) + ->method('getGiftMessageId') + ->willReturn(self::STUB_GIFT_MESSAGE); + $this->resultMock->expects($this->once()) + ->method('setGiftMessageId') + ->with(self::STUB_GIFT_MESSAGE); + + $this->assertSame( + $this->resultMock, + $this->plugin->afterMerge($this->processorMock, $this->resultMock, $this->sourceMock) + ); + } + + /** + * Test case when a source item doesn't have a Gift message. + */ + public function testAfterMergeWithoutGiftMessageId(): void + { + $this->sourceMock->expects($this->once())->method('getGiftMessageId')->willReturn(null); + $this->resultMock->expects($this->never())->method('setGiftMessageId'); + + $this->assertSame( + $this->resultMock, + $this->plugin->afterMerge($this->processorMock, $this->resultMock, $this->sourceMock) + ); + } +} diff --git a/app/code/Magento/GiftMessage/etc/adminhtml/system.xml b/app/code/Magento/GiftMessage/etc/adminhtml/system.xml index 6779ea6f28174..fa437a0735cf4 100644 --- a/app/code/Magento/GiftMessage/etc/adminhtml/system.xml +++ b/app/code/Magento/GiftMessage/etc/adminhtml/system.xml @@ -8,13 +8,13 @@ <config xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="urn:magento:module:Magento_Config:etc/system_file.xsd"> <system> <section id="sales"> - <group id="gift_options" translate="label" type="text" sortOrder="100" showInDefault="1" showInWebsite="1" showInStore="0"> + <group id="gift_options" translate="label" type="text" sortOrder="100" showInDefault="1" showInWebsite="1"> <label>Gift Options</label> - <field id="allow_order" translate="label" type="select" sortOrder="1" showInDefault="1" showInWebsite="1" showInStore="0" canRestore="1"> + <field id="allow_order" translate="label" type="select" sortOrder="1" showInDefault="1" showInWebsite="1" canRestore="1"> <label>Allow Gift Messages on Order Level</label> <source_model>Magento\Config\Model\Config\Source\Yesno</source_model> </field> - <field id="allow_items" translate="label" type="select" sortOrder="5" showInDefault="1" showInWebsite="1" showInStore="0" canRestore="1"> + <field id="allow_items" translate="label" type="select" sortOrder="5" showInDefault="1" showInWebsite="1" canRestore="1"> <label>Allow Gift Messages for Order Items</label> <source_model>Magento\Config\Model\Config\Source\Yesno</source_model> </field> diff --git a/app/code/Magento/GiftMessage/etc/di.xml b/app/code/Magento/GiftMessage/etc/di.xml index 5333084c90b75..1b079f3c9fd55 100644 --- a/app/code/Magento/GiftMessage/etc/di.xml +++ b/app/code/Magento/GiftMessage/etc/di.xml @@ -37,4 +37,7 @@ </argument> </arguments> </type> + <type name="Magento\Sales\Api\OrderItemRepositoryInterface"> + <plugin name="get_gift_message" type="Magento\GiftMessage\Model\Plugin\OrderItemGet"/> + </type> </config> diff --git a/app/code/Magento/GiftMessage/registration.php b/app/code/Magento/GiftMessage/registration.php index c7268d09cf929..78676ef9c32d0 100644 --- a/app/code/Magento/GiftMessage/registration.php +++ b/app/code/Magento/GiftMessage/registration.php @@ -4,6 +4,6 @@ * See COPYING.txt for license details. */ -use \Magento\Framework\Component\ComponentRegistrar; +use Magento\Framework\Component\ComponentRegistrar; ComponentRegistrar::register(ComponentRegistrar::MODULE, 'Magento_GiftMessage', __DIR__); diff --git a/app/code/Magento/GoogleAdwords/Test/Mftf/ActionGroup/AdminNavigateToGoogleAdwordsConfigurationActionGroup.xml b/app/code/Magento/GoogleAdwords/Test/Mftf/ActionGroup/AdminNavigateToGoogleAdwordsConfigurationActionGroup.xml new file mode 100644 index 0000000000000..c581862a3b34c --- /dev/null +++ b/app/code/Magento/GoogleAdwords/Test/Mftf/ActionGroup/AdminNavigateToGoogleAdwordsConfigurationActionGroup.xml @@ -0,0 +1,15 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + /** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +--> + +<actionGroups xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/actionGroupSchema.xsd"> + <actionGroup name="AdminNavigateToGoogleAdwordsConfigurationActionGroup"> + <amOnPage url="{{AdminGoogleAdwordsConfigPage.url}}" stepKey="navigateToConfigurationPage"/> + <waitForPageLoad stepKey="waitForPageLoad"/> + </actionGroup> +</actionGroups> diff --git a/app/code/Magento/GoogleAdwords/Test/Mftf/Page/AdminGoogleAdwordsConfigPage.xml b/app/code/Magento/GoogleAdwords/Test/Mftf/Page/AdminGoogleAdwordsConfigPage.xml new file mode 100644 index 0000000000000..842b867ed9407 --- /dev/null +++ b/app/code/Magento/GoogleAdwords/Test/Mftf/Page/AdminGoogleAdwordsConfigPage.xml @@ -0,0 +1,14 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- + /** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +--> +<pages xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Page/etc/PageObject.xsd"> + <page name="AdminGoogleAdwordsConfigPage" url="admin/system_config/edit/section/google/" area="admin" + module="Magento_Config"> + <section name="AdminGoogleAdwordsConfigSection"/> + </page> +</pages> diff --git a/app/code/Magento/GoogleAdwords/Test/Mftf/Section/AdminGoogleAdwordsConfigSection.xml b/app/code/Magento/GoogleAdwords/Test/Mftf/Section/AdminGoogleAdwordsConfigSection.xml new file mode 100644 index 0000000000000..d6ee2ced1afe1 --- /dev/null +++ b/app/code/Magento/GoogleAdwords/Test/Mftf/Section/AdminGoogleAdwordsConfigSection.xml @@ -0,0 +1,14 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- + /** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +--> +<sections xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Page/etc/SectionObject.xsd"> + <section name="AdminGoogleAdwordsConfigSection"> + <element name="active" type="text" selector="#google_adwords_active"/> + <element name="conversionId" type="text" selector="#google_adwords_conversion_id"/> + </section> +</sections> diff --git a/app/code/Magento/GoogleAdwords/Test/Mftf/Test/AdminValidateConversionIdConfigTest.xml b/app/code/Magento/GoogleAdwords/Test/Mftf/Test/AdminValidateConversionIdConfigTest.xml new file mode 100644 index 0000000000000..3010c32cd3ed8 --- /dev/null +++ b/app/code/Magento/GoogleAdwords/Test/Mftf/Test/AdminValidateConversionIdConfigTest.xml @@ -0,0 +1,41 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + /** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +--> + +<tests xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/testSchema.xsd"> + <test name="AdminValidateConversionIdConfigTest"> + <annotations> + <stories value="Admin validates the conversion ID when configuring the Google Adwords"/> + <title value="Admin validates the conversion ID when configuring the Google Adwords"/> + <description value="Testing for a required Conversion ID when configuring the Google Adwords"/> + <severity value="MINOR"/> + </annotations> + <before> + <actionGroup ref="AdminLoginActionGroup" stepKey="loginAsAdmin"/> + </before> + <after> + <actionGroup ref="AdminLogoutActionGroup" stepKey="logout"/> + </after> + <actionGroup ref="AdminNavigateToGoogleAdwordsConfigurationActionGroup" stepKey="goToConfigPage"/> + <actionGroup ref="AdminExpandConfigSectionActionGroup" stepKey="expandingGoogleAdwordsSection"> + <argument name="sectionName" value="Google AdWords"/> + </actionGroup> + <actionGroup ref="AdminUncheckUseSystemValueActionGroup" stepKey="uncheckUseSystemValue"> + <argument name="rowId" value="row_google_adwords_active"/> + </actionGroup> + <actionGroup ref="AdminToggleEnabledActionGroup" stepKey="enableGoogleAdwordsConfig"> + <argument name="element" value="{{AdminGoogleAdwordsConfigSection.active}}"/> + </actionGroup> + <actionGroup ref="AdminClickFormActionButtonActionGroup" stepKey="clickSaveCustomVariable"> + <argument name="buttonSelector" value="{{AdminMainActionsSection.save}}"/> + </actionGroup> + <actionGroup ref="AssertAdminValidationErrorActionGroup" stepKey="seeRequiredValidationErrorForConversionId"> + <argument name="inputId" value="google_adwords_conversion_id"/> + </actionGroup> + </test> +</tests> diff --git a/app/code/Magento/GoogleAdwords/etc/adminhtml/system.xml b/app/code/Magento/GoogleAdwords/etc/adminhtml/system.xml index c312028cf63be..74a16ae6acc62 100644 --- a/app/code/Magento/GoogleAdwords/etc/adminhtml/system.xml +++ b/app/code/Magento/GoogleAdwords/etc/adminhtml/system.xml @@ -17,6 +17,7 @@ <field id="conversion_id" translate="label" type="text" sortOrder="11" showInDefault="1" showInWebsite="1" showInStore="1"> <label>Conversion ID</label> <backend_model>Magento\GoogleAdwords\Model\Config\Backend\ConversionId</backend_model> + <validate>required-entry validate-number</validate> <depends> <field id="*/*/active">1</field> </depends> diff --git a/app/code/Magento/GoogleAdwords/registration.php b/app/code/Magento/GoogleAdwords/registration.php index 76aa83a3a55f9..e6ef3f7625f19 100644 --- a/app/code/Magento/GoogleAdwords/registration.php +++ b/app/code/Magento/GoogleAdwords/registration.php @@ -4,6 +4,6 @@ * See COPYING.txt for license details. */ -use \Magento\Framework\Component\ComponentRegistrar; +use Magento\Framework\Component\ComponentRegistrar; ComponentRegistrar::register(ComponentRegistrar::MODULE, 'Magento_GoogleAdwords', __DIR__); diff --git a/app/code/Magento/GoogleAnalytics/etc/adminhtml/system.xml b/app/code/Magento/GoogleAnalytics/etc/adminhtml/system.xml index 3491b4d456b81..dd59de40a8528 100644 --- a/app/code/Magento/GoogleAnalytics/etc/adminhtml/system.xml +++ b/app/code/Magento/GoogleAnalytics/etc/adminhtml/system.xml @@ -13,7 +13,7 @@ <resource>Magento_GoogleAnalytics::google</resource> <group id="analytics" translate="label" type="text" sortOrder="10" showInDefault="1" showInWebsite="1" showInStore="1"> <label>Google Analytics</label> - <field id="active" translate="label" type="select" sortOrder="10" showInDefault="1" showInWebsite="1" showInStore="1"> + <field id="active" translate="label" type="select" sortOrder="10" showInDefault="1" showInWebsite="1" showInStore="1" canRestore="1"> <label>Enable</label> <source_model>Magento\Config\Model\Config\Source\Yesno</source_model> </field> diff --git a/app/code/Magento/GoogleAnalytics/etc/config.xml b/app/code/Magento/GoogleAnalytics/etc/config.xml new file mode 100644 index 0000000000000..f3c9cc67f86ae --- /dev/null +++ b/app/code/Magento/GoogleAnalytics/etc/config.xml @@ -0,0 +1,16 @@ +<?xml version="1.0"?> +<!-- +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +--> +<config xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="urn:magento:module:Magento_Store:etc/config.xsd"> + <default> + <google> + <analytics> + <active>0</active> + </analytics> + </google> + </default> +</config> diff --git a/app/code/Magento/GoogleAnalytics/registration.php b/app/code/Magento/GoogleAnalytics/registration.php index c2a8471a50ba2..6eae65b9d159e 100644 --- a/app/code/Magento/GoogleAnalytics/registration.php +++ b/app/code/Magento/GoogleAnalytics/registration.php @@ -4,6 +4,6 @@ * See COPYING.txt for license details. */ -use \Magento\Framework\Component\ComponentRegistrar; +use Magento\Framework\Component\ComponentRegistrar; ComponentRegistrar::register(ComponentRegistrar::MODULE, 'Magento_GoogleAnalytics', __DIR__); diff --git a/app/code/Magento/GoogleOptimizer/registration.php b/app/code/Magento/GoogleOptimizer/registration.php index 36b94ef06eb18..4ba336ece8916 100644 --- a/app/code/Magento/GoogleOptimizer/registration.php +++ b/app/code/Magento/GoogleOptimizer/registration.php @@ -4,6 +4,6 @@ * See COPYING.txt for license details. */ -use \Magento\Framework\Component\ComponentRegistrar; +use Magento\Framework\Component\ComponentRegistrar; ComponentRegistrar::register(ComponentRegistrar::MODULE, 'Magento_GoogleOptimizer', __DIR__); diff --git a/app/code/Magento/GraphQl/etc/graphql/di.xml b/app/code/Magento/GraphQl/etc/graphql/di.xml index 03bae5c80e12c..2bcd44e9ae410 100644 --- a/app/code/Magento/GraphQl/etc/graphql/di.xml +++ b/app/code/Magento/GraphQl/etc/graphql/di.xml @@ -7,6 +7,7 @@ --> <config xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="urn:magento:framework:ObjectManager/etc/config.xsd"> <preference for="Magento\Framework\App\FrontControllerInterface" type="Magento\GraphQl\Controller\GraphQl" /> + <preference for="Magento\Framework\Authorization\RoleLocatorInterface" type="Magento\Webapi\Model\WebapiRoleLocator" /> <type name="Magento\Authorization\Model\CompositeUserContext"> <arguments> <argument name="userContexts" xsi:type="array"> diff --git a/app/code/Magento/GroupedCatalogInventory/registration.php b/app/code/Magento/GroupedCatalogInventory/registration.php index 8899a48edf6d5..f9b2729acb45a 100644 --- a/app/code/Magento/GroupedCatalogInventory/registration.php +++ b/app/code/Magento/GroupedCatalogInventory/registration.php @@ -4,6 +4,6 @@ * See COPYING.txt for license details. */ -use \Magento\Framework\Component\ComponentRegistrar; +use Magento\Framework\Component\ComponentRegistrar; ComponentRegistrar::register(ComponentRegistrar::MODULE, 'Magento_GroupedCatalogInventory', __DIR__); diff --git a/app/code/Magento/GroupedImportExport/registration.php b/app/code/Magento/GroupedImportExport/registration.php index 90beb26820c47..de4aec530794e 100644 --- a/app/code/Magento/GroupedImportExport/registration.php +++ b/app/code/Magento/GroupedImportExport/registration.php @@ -4,6 +4,6 @@ * See COPYING.txt for license details. */ -use \Magento\Framework\Component\ComponentRegistrar; +use Magento\Framework\Component\ComponentRegistrar; ComponentRegistrar::register(ComponentRegistrar::MODULE, 'Magento_GroupedImportExport', __DIR__); diff --git a/app/code/Magento/GroupedProduct/Block/Cart/Item/Renderer/Grouped.php b/app/code/Magento/GroupedProduct/Block/Cart/Item/Renderer/Grouped.php index e85d4eeca730a..197be38fb7f5f 100644 --- a/app/code/Magento/GroupedProduct/Block/Cart/Item/Renderer/Grouped.php +++ b/app/code/Magento/GroupedProduct/Block/Cart/Item/Renderer/Grouped.php @@ -45,10 +45,10 @@ public function getGroupedProduct() */ public function getIdentities() { - $identities = parent::getIdentities(); + $identities = [parent::getIdentities()]; if ($this->getItem()) { - $identities = array_merge($identities, $this->getGroupedProduct()->getIdentities()); + $identities[] = $this->getGroupedProduct()->getIdentities(); } - return $identities; + return array_merge(...$identities); } } diff --git a/app/code/Magento/GroupedProduct/Block/Order/Email/Items/CreditMemo/Grouped.php b/app/code/Magento/GroupedProduct/Block/Order/Email/Items/CreditMemo/Grouped.php new file mode 100644 index 0000000000000..6df890b3e94dc --- /dev/null +++ b/app/code/Magento/GroupedProduct/Block/Order/Email/Items/CreditMemo/Grouped.php @@ -0,0 +1,40 @@ +<?php +/** + * Order Email items grouped renderer + * + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +namespace Magento\GroupedProduct\Block\Order\Email\Items\CreditMemo; + +use Magento\Sales\Block\Order\Email\Items\DefaultItems; + +/** + * Class renders grouped product(s) in the CreditMemo email + * + * @api + */ +class Grouped extends DefaultItems +{ + /** + * Prepare item html + * + * This method uses renderer for real product type + * + * @return string + */ + protected function _toHtml() + { + if ($this->getItem()->getOrderItem()) { + $item = $this->getItem()->getOrderItem(); + } else { + $item = $this->getItem(); + } + if ($productType = $item->getRealProductType()) { + $renderer = $this->getRenderedBlock()->getItemRenderer($productType); + $renderer->setItem($this->getItem()); + return $renderer->toHtml(); + } + return parent::_toHtml(); + } +} diff --git a/app/code/Magento/GroupedProduct/Block/Stockqty/Type/Grouped.php b/app/code/Magento/GroupedProduct/Block/Stockqty/Type/Grouped.php index f666cf8d90af9..97dc90ec93493 100644 --- a/app/code/Magento/GroupedProduct/Block/Stockqty/Type/Grouped.php +++ b/app/code/Magento/GroupedProduct/Block/Stockqty/Type/Grouped.php @@ -32,10 +32,10 @@ protected function _getChildProducts() */ public function getIdentities() { - $identities = []; + $identities = [[]]; foreach ($this->getChildProducts() as $item) { - $identities = array_merge($identities, $item->getIdentities()); + $identities[] = $item->getIdentities(); } - return $identities; + return array_merge(...$identities); } } diff --git a/app/code/Magento/GroupedProduct/Model/Product/Type/Grouped.php b/app/code/Magento/GroupedProduct/Model/Product/Type/Grouped.php index 187fd27fa0554..8eac8d0b0e163 100644 --- a/app/code/Magento/GroupedProduct/Model/Product/Type/Grouped.php +++ b/app/code/Magento/GroupedProduct/Model/Product/Type/Grouped.php @@ -344,7 +344,7 @@ protected function getProductInfo(\Magento\Framework\DataObject $buyRequest, $pr } foreach ($associatedProducts as $subProduct) { if (!isset($productsInfo[$subProduct->getId()])) { - if ($isStrictProcessMode && !$subProduct->getQty()) { + if ($isStrictProcessMode && !$subProduct->getQty() && $subProduct->isSalable()) { return __('Please specify the quantity of product(s).')->render(); } $productsInfo[$subProduct->getId()] = $subProduct->isSalable() ? (float)$subProduct->getQty() : 0; diff --git a/app/code/Magento/GroupedProduct/Model/Wishlist/Product/Item.php b/app/code/Magento/GroupedProduct/Model/Wishlist/Product/Item.php new file mode 100644 index 0000000000000..d84df510195f3 --- /dev/null +++ b/app/code/Magento/GroupedProduct/Model/Wishlist/Product/Item.php @@ -0,0 +1,93 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +declare(strict_types=1); + +namespace Magento\GroupedProduct\Model\Wishlist\Product; + +use Magento\Wishlist\Model\Item as WishlistItem; +use Magento\GroupedProduct\Model\Product\Type\Grouped as TypeGrouped; +use Magento\Catalog\Model\Product; + +/** + * Wishlist logic for grouped product + */ +class Item +{ + /** + * Modify Wishlist item based on associated product qty + * + * @param WishlistItem $subject + * @param Product $product + * @return array + * @throws \Magento\Framework\Exception\LocalizedException + */ + public function beforeRepresentProduct( + WishlistItem $subject, + Product $product + ): array { + if ($product->getTypeId() === TypeGrouped::TYPE_CODE + && $product->getId() === $subject->getProduct()->getId() + ) { + $itemOptions = $subject->getOptionsByCode(); + $productOptions = $product->getCustomOptions(); + + $diff = array_diff_key($itemOptions, $productOptions); + + if (!$diff) { + $buyRequest = $subject->getBuyRequest(); + $superGroupInfo = $buyRequest->getData('super_group'); + + foreach ($itemOptions as $key => $itemOption) { + if (preg_match('/associated_product_\d+/', $key)) { + $simpleId = str_replace('associated_product_', '', $key); + $prodQty = $productOptions[$key]->getValue(); + + $itemOption->setValue($itemOption->getValue() + $prodQty); + + if (isset($superGroupInfo[$simpleId])) { + $superGroupInfo[$simpleId] = $itemOptions[$key]->getValue(); + } + } + } + + $buyRequest->setData('super_group', $superGroupInfo); + + $subject->setOptions($itemOptions); + $subject->mergeBuyRequest($buyRequest); + } + } + + return [$product]; + } + + /** + * Remove associated_product_id key. associated_product_id contains qty + * + * @param WishlistItem $subject + * @param array $options1 + * @param array $options2 + * @return array + * @SuppressWarnings(PHPMD.UnusedFormalParameter) + */ + public function beforeCompareOptions( + WishlistItem $subject, + array $options1, + array $options2 + ): array { + $diff = array_diff_key($options1, $options2); + + if (!$diff) { + foreach (array_keys($options1) as $key) { + if (preg_match('/associated_product_\d+/', $key)) { + unset($options1[$key]); + unset($options2[$key]); + } + } + } + + return [$options1, $options2]; + } +} diff --git a/app/code/Magento/GroupedProduct/Test/Mftf/Section/AdminAddProductsToGroupPanelSection.xml b/app/code/Magento/GroupedProduct/Test/Mftf/Section/AdminAddProductsToGroupPanelSection/AdminAddProductsToGroupPanelSection.xml similarity index 77% rename from app/code/Magento/GroupedProduct/Test/Mftf/Section/AdminAddProductsToGroupPanelSection.xml rename to app/code/Magento/GroupedProduct/Test/Mftf/Section/AdminAddProductsToGroupPanelSection/AdminAddProductsToGroupPanelSection.xml index f71bb49aea10a..93ea32fa8f677 100644 --- a/app/code/Magento/GroupedProduct/Test/Mftf/Section/AdminAddProductsToGroupPanelSection.xml +++ b/app/code/Magento/GroupedProduct/Test/Mftf/Section/AdminAddProductsToGroupPanelSection/AdminAddProductsToGroupPanelSection.xml @@ -5,9 +5,8 @@ * See COPYING.txt for license details. */ --> - <sections xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" - xsi:noNamespaceSchemaLocation="urn:magento:mftf:Page/etc/SectionObject.xsd"> + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Page/etc/SectionObject.xsd"> <section name="AdminAddProductsToGroupPanel"> <element name="addSelectedProducts" type="button" selector=".product_form_product_form_grouped_grouped_products_modal button.action-primary" timeout="30"/> <element name="filters" type="button" selector=".product_form_product_form_grouped_grouped_products_modal [data-action='grid-filter-expand']" timeout="30"/> @@ -17,8 +16,4 @@ <element name="nThCheckbox" type="input" selector="tr[data-repeat-index='{{n}}'] .admin__control-checkbox" parameterized="true"/> <element name="clearFilters" type="button" selector=".product_form_product_form_grouped_grouped_products_modal [data-action='grid-filter-reset']" timeout="30"/> </section> - - <section name="AdminAddedProductsToGroupGrid"> - <element name="inputByProductName" type="input" selector="//div[@data-index='grouped']//table//tr[td[@data-index='name']//span[text()='{{productName}}']]//td[@data-index='qty']//input" parameterized="true"/> - </section> -</sections> \ No newline at end of file +</sections> diff --git a/app/code/Magento/GroupedProduct/Test/Mftf/Section/AdminAddProductsToGroupPanelSection/AdminAddedProductsToGroupGridSection.xml b/app/code/Magento/GroupedProduct/Test/Mftf/Section/AdminAddProductsToGroupPanelSection/AdminAddedProductsToGroupGridSection.xml new file mode 100644 index 0000000000000..290c88b87429f --- /dev/null +++ b/app/code/Magento/GroupedProduct/Test/Mftf/Section/AdminAddProductsToGroupPanelSection/AdminAddedProductsToGroupGridSection.xml @@ -0,0 +1,13 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + /** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +--> +<sections xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Page/etc/SectionObject.xsd"> + <section name="AdminAddedProductsToGroupGrid"> + <element name="inputByProductName" type="input" selector="//div[@data-index='grouped']//table//tr[td[@data-index='name']//span[text()='{{productName}}']]//td[@data-index='qty']//input" parameterized="true"/> + </section> +</sections> diff --git a/app/code/Magento/GroupedProduct/Test/Mftf/Test/AdminAddDefaultImageGroupedProductTest.xml b/app/code/Magento/GroupedProduct/Test/Mftf/Test/AdminAddDefaultImageGroupedProductTest.xml index d71ee505f8356..04b704b9193ca 100644 --- a/app/code/Magento/GroupedProduct/Test/Mftf/Test/AdminAddDefaultImageGroupedProductTest.xml +++ b/app/code/Magento/GroupedProduct/Test/Mftf/Test/AdminAddDefaultImageGroupedProductTest.xml @@ -19,7 +19,7 @@ <group value="GroupedProduct"/> </annotations> <before> - <actionGroup ref="LoginAsAdmin" stepKey="loginAsAdmin"/> + <actionGroup ref="AdminLoginActionGroup" stepKey="loginAsAdmin"/> <createData entity="_defaultCategory" stepKey="createCategory"/> <createData entity="ApiSimpleProduct" stepKey="createProductOne"> <requiredEntity createDataKey="createCategory"/> diff --git a/app/code/Magento/GroupedProduct/Test/Mftf/Test/AdminAddDefaultVideoGroupedProductTest.xml b/app/code/Magento/GroupedProduct/Test/Mftf/Test/AdminAddDefaultVideoGroupedProductTest.xml index f9af2284ae71a..5eee2d77befab 100644 --- a/app/code/Magento/GroupedProduct/Test/Mftf/Test/AdminAddDefaultVideoGroupedProductTest.xml +++ b/app/code/Magento/GroupedProduct/Test/Mftf/Test/AdminAddDefaultVideoGroupedProductTest.xml @@ -17,6 +17,9 @@ <severity value="MAJOR"/> <testCaseId value="MC-108"/> <group value="GroupedProduct"/> + <skip> + <issueId value="MC-32197"/> + </skip> </annotations> <before> <createData entity="SimpleProduct2" stepKey="simpleProduct1"/> diff --git a/app/code/Magento/GroupedProduct/Test/Mftf/Test/AdminAssociateGroupedProductToWebsitesTest.xml b/app/code/Magento/GroupedProduct/Test/Mftf/Test/AdminAssociateGroupedProductToWebsitesTest.xml index 41096c416d05e..bd6785eb5e41b 100644 --- a/app/code/Magento/GroupedProduct/Test/Mftf/Test/AdminAssociateGroupedProductToWebsitesTest.xml +++ b/app/code/Magento/GroupedProduct/Test/Mftf/Test/AdminAssociateGroupedProductToWebsitesTest.xml @@ -33,7 +33,7 @@ </createData> <!-- Login as Admin --> - <actionGroup ref="LoginAsAdmin" stepKey="loginAsAdmin"/> + <actionGroup ref="AdminLoginActionGroup" stepKey="loginAsAdmin"/> <!--Create website--> <actionGroup ref="AdminCreateWebsiteActionGroup" stepKey="createSecondWebsite"> @@ -72,7 +72,7 @@ <actionGroup ref="NavigateToAndResetProductGridToDefaultViewActionGroup" stepKey="resetProductGridFilter"/> <!-- Admin logout --> - <actionGroup ref="logout" stepKey="logout"/> + <actionGroup ref="AdminLogoutActionGroup" stepKey="logout"/> </after> <!-- Open product page and assign grouped project to second website --> diff --git a/app/code/Magento/GroupedProduct/Test/Mftf/Test/AdminCreateAndEditGroupedProductSettingsTest.xml b/app/code/Magento/GroupedProduct/Test/Mftf/Test/AdminCreateAndEditGroupedProductSettingsTest.xml index e3b76dafdd100..b842dad9c8c4e 100644 --- a/app/code/Magento/GroupedProduct/Test/Mftf/Test/AdminCreateAndEditGroupedProductSettingsTest.xml +++ b/app/code/Magento/GroupedProduct/Test/Mftf/Test/AdminCreateAndEditGroupedProductSettingsTest.xml @@ -26,7 +26,7 @@ <createData entity="SimpleProduct2" stepKey="createProduct"/> <!-- Login as admin --> - <actionGroup ref="LoginAsAdmin" stepKey="loginAsAdmin"/> + <actionGroup ref="AdminLoginActionGroup" stepKey="loginAsAdmin"/> </before> <after> <!-- Delete grouped product --> @@ -43,7 +43,7 @@ <deleteData createDataKey="createProduct" stepKey="deleteSimpleProduct"/> <!-- Log out --> - <actionGroup ref="logout" stepKey="logout"/> + <actionGroup ref="AdminLogoutActionGroup" stepKey="logout"/> </after> <!-- Create new grouped product --> diff --git a/app/code/Magento/GroupedProduct/Test/Mftf/Test/AdminDeleteGroupedProductTest.xml b/app/code/Magento/GroupedProduct/Test/Mftf/Test/AdminDeleteGroupedProductTest.xml index f7b9357f1b34a..b88f909d977ab 100644 --- a/app/code/Magento/GroupedProduct/Test/Mftf/Test/AdminDeleteGroupedProductTest.xml +++ b/app/code/Magento/GroupedProduct/Test/Mftf/Test/AdminDeleteGroupedProductTest.xml @@ -18,7 +18,7 @@ <group value="mtf_migrated"/> </annotations> <before> - <actionGroup ref="LoginAsAdmin" stepKey="loginAsAdmin"/> + <actionGroup ref="AdminLoginActionGroup" stepKey="loginAsAdmin"/> <createData entity="ApiProductWithDescription" stepKey="createSimpleProduct"/> <createData entity="_defaultCategory" stepKey="createCategory"/> <createData entity="ApiGroupedProduct2" stepKey="createGroupedProduct"> @@ -31,7 +31,7 @@ </before> <after> <deleteData createDataKey="createCategory" stepKey="deleteCategory"/> - <actionGroup ref="logout" stepKey="logout"/> + <actionGroup ref="AdminLogoutActionGroup" stepKey="logout"/> </after> <actionGroup ref="DeleteProductUsingProductGridActionGroup" stepKey="deleteGroupedProductFilteredBySkuAndName"> <argument name="product" value="$$createGroupedProduct$$"/> diff --git a/app/code/Magento/GroupedProduct/Test/Mftf/Test/AdminGroupedProductsListTest.xml b/app/code/Magento/GroupedProduct/Test/Mftf/Test/AdminGroupedProductsListTest.xml index d80e68e37c44c..6514b5ddc5f78 100644 --- a/app/code/Magento/GroupedProduct/Test/Mftf/Test/AdminGroupedProductsListTest.xml +++ b/app/code/Magento/GroupedProduct/Test/Mftf/Test/AdminGroupedProductsListTest.xml @@ -8,7 +8,7 @@ <tests xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/testSchema.xsd"> - <test name="AdminGroupedProductsAreListedWhenOutOfStock"> + <test name="AdminGroupedProductsAreListedWhenOutOfStockTest"> <annotations> <features value="GroupedProduct"/> <stories value="MAGETWO-93181: Grouped product doesn't take care about his Linked Products when SalableQuantity < ProductLink.ExtensionAttributes.Qty after Source Deduction"/> @@ -27,13 +27,13 @@ <createData entity="SimpleProduct4" stepKey="simpleProduct2"> <requiredEntity createDataKey="category1"/> </createData> - <actionGroup ref="LoginAsAdmin" stepKey="loginAsAdmin"/> + <actionGroup ref="AdminLoginActionGroup" stepKey="loginAsAdmin"/> </before> <after> <deleteData createDataKey="simpleProduct1" stepKey="deleteSimpleProduct1"/> <deleteData createDataKey="simpleProduct2" stepKey="deleteSimpleProduct2"/> <deleteData createDataKey="category1" stepKey="deleteCategory"/> - <actionGroup ref="logout" stepKey="logout"/> + <actionGroup ref="AdminLogoutActionGroup" stepKey="logout"/> </after> <!-- Create product --> diff --git a/app/code/Magento/GroupedProduct/Test/Mftf/Test/AdminGroupedSetEditRelatedProductsTest.xml b/app/code/Magento/GroupedProduct/Test/Mftf/Test/AdminGroupedSetEditRelatedProductsTest.xml index fdffd286d632f..a0698224b780c 100644 --- a/app/code/Magento/GroupedProduct/Test/Mftf/Test/AdminGroupedSetEditRelatedProductsTest.xml +++ b/app/code/Magento/GroupedProduct/Test/Mftf/Test/AdminGroupedSetEditRelatedProductsTest.xml @@ -34,6 +34,8 @@ <argument name="product" value="GroupedProduct"/> </actionGroup> + <magentoCLI command="cron:run --group=index" stepKey="runCronIndexer"/> + <!--See related product in storefront--> <amOnPage url="{{GroupedProduct.sku}}.html" stepKey="goToStorefront"/> </test> diff --git a/app/code/Magento/GroupedProduct/Test/Mftf/Test/AdminRemoveDefaultImageGroupedProductTest.xml b/app/code/Magento/GroupedProduct/Test/Mftf/Test/AdminRemoveDefaultImageGroupedProductTest.xml index 452b55b835739..053949fa20fb2 100644 --- a/app/code/Magento/GroupedProduct/Test/Mftf/Test/AdminRemoveDefaultImageGroupedProductTest.xml +++ b/app/code/Magento/GroupedProduct/Test/Mftf/Test/AdminRemoveDefaultImageGroupedProductTest.xml @@ -19,7 +19,7 @@ <group value="GroupedProduct"/> </annotations> <before> - <actionGroup ref="LoginAsAdmin" stepKey="loginAsAdmin"/> + <actionGroup ref="AdminLoginActionGroup" stepKey="loginAsAdmin"/> <createData entity="_defaultCategory" stepKey="createCategory"/> <createData entity="ApiSimpleProduct" stepKey="createProductOne"> <requiredEntity createDataKey="createCategory"/> diff --git a/app/code/Magento/GroupedProduct/Test/Mftf/Test/AdminRemoveDefaultVideoGroupedProductTest.xml b/app/code/Magento/GroupedProduct/Test/Mftf/Test/AdminRemoveDefaultVideoGroupedProductTest.xml index 572ae3a44a953..4486bc66ffb98 100644 --- a/app/code/Magento/GroupedProduct/Test/Mftf/Test/AdminRemoveDefaultVideoGroupedProductTest.xml +++ b/app/code/Magento/GroupedProduct/Test/Mftf/Test/AdminRemoveDefaultVideoGroupedProductTest.xml @@ -17,6 +17,9 @@ <severity value="MAJOR"/> <testCaseId value="MC-203"/> <group value="GroupedProduct"/> + <skip> + <issueId value="MC-32197"/> + </skip> </annotations> <before> <createData entity="SimpleProduct2" stepKey="simpleProduct1"/> diff --git a/app/code/Magento/GroupedProduct/Test/Mftf/Test/AdminSortingAssociatedProductsTest.xml b/app/code/Magento/GroupedProduct/Test/Mftf/Test/AdminSortingAssociatedProductsTest.xml index bac294d59ce51..7f03765720069 100644 --- a/app/code/Magento/GroupedProduct/Test/Mftf/Test/AdminSortingAssociatedProductsTest.xml +++ b/app/code/Magento/GroupedProduct/Test/Mftf/Test/AdminSortingAssociatedProductsTest.xml @@ -19,7 +19,7 @@ <group value="GroupedProduct"/> </annotations> <before> - <actionGroup ref="LoginAsAdmin" stepKey="login"/> + <actionGroup ref="AdminLoginActionGroup" stepKey="login"/> <createData entity="_defaultCategory" stepKey="category"/> <!-- Create 23 products so that grid can have more than one page --> <createData entity="ApiSimpleProduct" stepKey="product1"> @@ -123,7 +123,7 @@ <deleteData createDataKey="product22" stepKey="deleteProduct22"/> <deleteData createDataKey="product23" stepKey="deleteProduct23"/> <deleteData createDataKey="category" stepKey="deleteCategory"/> - <actionGroup ref="logout" stepKey="logout"/> + <actionGroup ref="AdminLogoutActionGroup" stepKey="logout"/> </after> <!--Create grouped Product--> diff --git a/app/code/Magento/GroupedProduct/Test/Mftf/Test/AdvanceCatalogSearchGroupedProductTest.xml b/app/code/Magento/GroupedProduct/Test/Mftf/Test/AdvanceCatalogSearchGroupedProductTest.xml deleted file mode 100644 index 4fd06ccaa27ec..0000000000000 --- a/app/code/Magento/GroupedProduct/Test/Mftf/Test/AdvanceCatalogSearchGroupedProductTest.xml +++ /dev/null @@ -1,323 +0,0 @@ -<?xml version="1.0" encoding="UTF-8"?> -<!-- - /** - * Copyright © Magento, Inc. All rights reserved. - * See COPYING.txt for license details. - */ ---> - -<tests xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" - xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/testSchema.xsd"> - <test name="AdvanceCatalogSearchGroupedProductByNameTest" extends="AdvanceCatalogSearchSimpleProductByNameTest"> - <annotations> - <features value="GroupedProduct"/> - <stories value="Advanced Catalog Product Search for all product types"/> - <title value="Guest customer should be able to advance search Grouped product with product name"/> - <description value="Guest customer should be able to advance search Grouped product with product name"/> - <severity value="MAJOR"/> - <testCaseId value="MC-141"/> - <group value="GroupedProduct"/> - <group value="SearchEngineElasticsearch"/> - </annotations> - <before> - <createData entity="ApiProductWithDescription" stepKey="simple1" before="simple2"/> - <createData entity="ApiProductWithDescription" stepKey="simple2" before="product"/> - <createData entity="ApiGroupedProduct" stepKey="product"/> - <createData entity="OneSimpleProductLink" stepKey="addProductOne"> - <requiredEntity createDataKey="product"/> - <requiredEntity createDataKey="simple1"/> - </createData> - <updateData entity="OneMoreSimpleProductLink" createDataKey="addProductOne" stepKey="addProductTwo"> - <requiredEntity createDataKey="product"/> - <requiredEntity createDataKey="simple2"/> - </updateData> - <magentoCLI command="indexer:reindex" stepKey="reindex"/> - <magentoCLI command="cache:flush" stepKey="flushCache"/> - </before> - <after> - <deleteData createDataKey="simple1" stepKey="deleteSimple1" before="deleteSimple2"/> - <deleteData createDataKey="simple2" stepKey="deleteSimple2" before="delete"/> - </after> - <see userInput="3 items" selector="{{StorefrontCatalogSearchAdvancedResultMainSection.itemFound}}" stepKey="see"/> - <see userInput="$$product.name$$" selector="{{StorefrontCatalogSearchAdvancedResultMainSection.nthProductName('1')}}" stepKey="seeProductName"/> - <see userInput="$$simple1.name$$" selector="{{StorefrontCatalogSearchAdvancedResultMainSection.nthProductName('2')}}" stepKey="seeSimple1ProductName"/> - <see userInput="$$simple2.name$$" selector="{{StorefrontCatalogSearchAdvancedResultMainSection.nthProductName('3')}}" stepKey="seeSimple2ProductName"/> - </test> - <test name="AdvanceCatalogSearchGroupedProductByNameMysqlTest" extends="AdvanceCatalogSearchSimpleProductByNameTest"> - <annotations> - <features value="GroupedProduct"/> - <stories value="Advanced Catalog Product Search for all product types"/> - <title value="Guest customer should be able to advance search Grouped product with product name using the MySQL search engine"/> - <description value="Guest customer should be able to advance search Grouped product with product name using the MySQL search engine"/> - <severity value="MAJOR"/> - <testCaseId value="MC-20464"/> - <group value="GroupedProduct"/> - <group value="SearchEngineMysql"/> - </annotations> - <before> - <createData entity="ApiProductWithDescription" stepKey="simple1" before="simple2"/> - <createData entity="ApiProductWithDescription" stepKey="simple2" before="product"/> - <createData entity="ApiGroupedProduct" stepKey="product"/> - <createData entity="OneSimpleProductLink" stepKey="addProductOne"> - <requiredEntity createDataKey="product"/> - <requiredEntity createDataKey="simple1"/> - </createData> - <updateData entity="OneMoreSimpleProductLink" createDataKey="addProductOne" stepKey="addProductTwo"> - <requiredEntity createDataKey="product"/> - <requiredEntity createDataKey="simple2"/> - </updateData> - <magentoCLI command="indexer:reindex" stepKey="reindex"/> - <magentoCLI command="cache:flush" stepKey="flushCache"/> - </before> - <after> - <deleteData createDataKey="simple1" stepKey="deleteSimple1" before="deleteSimple2"/> - <deleteData createDataKey="simple2" stepKey="deleteSimple2" before="delete"/> - </after> - </test> - <test name="AdvanceCatalogSearchGroupedProductBySkuTest" extends="AdvanceCatalogSearchSimpleProductBySkuTest"> - <annotations> - <features value="GroupedProduct"/> - <stories value="Advanced Catalog Product Search for all product types"/> - <title value="Guest customer should be able to advance search Grouped product with product sku"/> - <description value="Guest customer should be able to advance search Grouped product with product sku"/> - <severity value="MAJOR"/> - <testCaseId value="MC-146"/> - <group value="GroupedProduct"/> - </annotations> - <before> - <createData entity="ApiProductWithDescription" stepKey="simple1" before="simple2"/> - <createData entity="ApiProductWithDescription" stepKey="simple2" before="product"/> - <createData entity="ApiGroupedProductAndUnderscoredSku" stepKey="product"/> - <createData entity="OneSimpleProductLink" stepKey="addProductOne"> - <requiredEntity createDataKey="product"/> - <requiredEntity createDataKey="simple1"/> - </createData> - <updateData entity="OneMoreSimpleProductLink" createDataKey="addProductOne" stepKey="addProductTwo"> - <requiredEntity createDataKey="product"/> - <requiredEntity createDataKey="simple2"/> - </updateData> - <magentoCLI command="indexer:reindex" stepKey="reindex"/> - <magentoCLI command="cache:flush" stepKey="flushCache"/> - </before> - <after> - <deleteData createDataKey="simple1" stepKey="deleteSimple1" before="deleteSimple2"/> - <deleteData createDataKey="simple2" stepKey="deleteSimple2" before="delete"/> - </after> - </test> - <test name="AdvanceCatalogSearchGroupedProductByDescriptionTest" extends="AdvanceCatalogSearchSimpleProductByDescriptionTest"> - <annotations> - <features value="GroupedProduct"/> - <stories value="Advanced Catalog Product Search for all product types"/> - <title value="Guest customer should be able to advance search Grouped product with product description"/> - <description value="Guest customer should be able to advance search Grouped product with product description"/> - <severity value="MAJOR"/> - <testCaseId value="MC-282"/> - <group value="GroupedProduct"/> - <group value="SearchEngineElasticsearch"/> - </annotations> - <before> - <createData entity="ApiProductWithDescription" stepKey="simple1" before="simple2"/> - <createData entity="ApiProductWithDescription" stepKey="simple2" before="product"/> - <createData entity="ApiGroupedProduct" stepKey="product"/> - <createData entity="OneSimpleProductLink" stepKey="addProductOne"> - <requiredEntity createDataKey="product"/> - <requiredEntity createDataKey="simple1"/> - </createData> - <updateData entity="OneMoreSimpleProductLink" createDataKey="addProductOne" stepKey="addProductTwo"> - <requiredEntity createDataKey="product"/> - <requiredEntity createDataKey="simple2"/> - </updateData> - <magentoCLI command="indexer:reindex" stepKey="reindex"/> - <magentoCLI command="cache:flush" stepKey="flushCache"/> - </before> - <after> - <deleteData createDataKey="simple1" stepKey="deleteSimple1" before="deleteSimple2"/> - <deleteData createDataKey="simple2" stepKey="deleteSimple2" before="delete"/> - </after> - <see userInput="3 items" selector="{{StorefrontCatalogSearchAdvancedResultMainSection.itemFound}}" stepKey="see"/> - <see userInput="$$product.name$$" selector="{{StorefrontCatalogSearchAdvancedResultMainSection.nthProductName('1')}}" stepKey="seeProductName"/> - <see userInput="$$simple1.name$$" selector="{{StorefrontCatalogSearchAdvancedResultMainSection.nthProductName('2')}}" stepKey="seeSimple1ProductName"/> - <see userInput="$$simple2.name$$" selector="{{StorefrontCatalogSearchAdvancedResultMainSection.nthProductName('3')}}" stepKey="seeSimple2ProductName"/> - </test> - <test name="AdvanceCatalogSearchGroupedProductByDescriptionMysqlTest" extends="AdvanceCatalogSearchSimpleProductByDescriptionTest"> - <annotations> - <features value="GroupedProduct"/> - <stories value="Advanced Catalog Product Search for all product types"/> - <title value="Guest customer should be able to advance search Grouped product with product description using the MySQL search engine"/> - <description value="Guest customer should be able to advance search Grouped product with product description using the MYSQL search engine"/> - <severity value="MAJOR"/> - <testCaseId value="MC-20468"/> - <group value="GroupedProduct"/> - <group value="SearchEngineMysql"/> - </annotations> - <before> - <createData entity="ApiProductWithDescription" stepKey="simple1" before="simple2"/> - <createData entity="ApiProductWithDescription" stepKey="simple2" before="product"/> - <createData entity="ApiGroupedProduct" stepKey="product"/> - <createData entity="OneSimpleProductLink" stepKey="addProductOne"> - <requiredEntity createDataKey="product"/> - <requiredEntity createDataKey="simple1"/> - </createData> - <updateData entity="OneMoreSimpleProductLink" createDataKey="addProductOne" stepKey="addProductTwo"> - <requiredEntity createDataKey="product"/> - <requiredEntity createDataKey="simple2"/> - </updateData> - <magentoCLI command="indexer:reindex" stepKey="reindex"/> - <magentoCLI command="cache:flush" stepKey="flushCache"/> - </before> - <after> - <deleteData createDataKey="simple1" stepKey="deleteSimple1" before="deleteSimple2"/> - <deleteData createDataKey="simple2" stepKey="deleteSimple2" before="delete"/> - </after> - </test> - <test name="AdvanceCatalogSearchGroupedProductByShortDescriptionTest" extends="AdvanceCatalogSearchSimpleProductByShortDescriptionTest"> - <annotations> - <features value="GroupedProduct"/> - <stories value="Advanced Catalog Product Search for all product types"/> - <title value="Guest customer should be able to advance search Grouped product with product short description"/> - <description value="Guest customer should be able to advance search Grouped product with product short description"/> - <severity value="MAJOR"/> - <testCaseId value="MC-283"/> - <group value="GroupedProduct"/> - <group value="SearchEngineElasticsearch"/> - </annotations> - <before> - <createData entity="ApiProductWithDescription" stepKey="simple1" before="simple2"/> - <createData entity="ApiProductWithDescription" stepKey="simple2" before="product"/> - <createData entity="ApiGroupedProduct" stepKey="product"/> - <createData entity="OneSimpleProductLink" stepKey="addProductOne"> - <requiredEntity createDataKey="product"/> - <requiredEntity createDataKey="simple1"/> - </createData> - <updateData entity="OneMoreSimpleProductLink" createDataKey="addProductOne" stepKey="addProductTwo"> - <requiredEntity createDataKey="product"/> - <requiredEntity createDataKey="simple2"/> - </updateData> - <magentoCLI command="indexer:reindex" stepKey="reindex"/> - <magentoCLI command="cache:flush" stepKey="flushCache"/> - </before> - <after> - <deleteData createDataKey="simple1" stepKey="deleteSimple1" before="deleteSimple2"/> - <deleteData createDataKey="simple2" stepKey="deleteSimple2" before="delete"/> - </after> - <see userInput="3 items" selector="{{StorefrontCatalogSearchAdvancedResultMainSection.itemFound}}" stepKey="see"/> - <see userInput="$$product.name$$" selector="{{StorefrontCatalogSearchAdvancedResultMainSection.nthProductName('1')}}" stepKey="seeProductName"/> - <see userInput="$$simple1.name$$" selector="{{StorefrontCatalogSearchAdvancedResultMainSection.nthProductName('2')}}" stepKey="seeSimple1ProductName"/> - <see userInput="$$simple2.name$$" selector="{{StorefrontCatalogSearchAdvancedResultMainSection.nthProductName('3')}}" stepKey="seeSimple2ProductName"/> - </test> - <test name="AdvanceCatalogSearchGroupedProductByShortDescriptionMysqlTest" extends="AdvanceCatalogSearchSimpleProductByShortDescriptionTest"> - <annotations> - <features value="GroupedProduct"/> - <stories value="Advanced Catalog Product Search for all product types"/> - <title value="Guest customer should be able to advance search Grouped product with product short description using the MySQL search engine"/> - <description value="Guest customer should be able to advance search Grouped product with product short description using the MySQL search engine"/> - <severity value="MAJOR"/> - <testCaseId value="MC-20469"/> - <group value="GroupedProduct"/> - <group value="SearchEngineMysql"/> - </annotations> - <before> - <createData entity="ApiProductWithDescription" stepKey="simple1" before="simple2"/> - <createData entity="ApiProductWithDescription" stepKey="simple2" before="product"/> - <createData entity="ApiGroupedProduct" stepKey="product"/> - <createData entity="OneSimpleProductLink" stepKey="addProductOne"> - <requiredEntity createDataKey="product"/> - <requiredEntity createDataKey="simple1"/> - </createData> - <updateData entity="OneMoreSimpleProductLink" createDataKey="addProductOne" stepKey="addProductTwo"> - <requiredEntity createDataKey="product"/> - <requiredEntity createDataKey="simple2"/> - </updateData> - <magentoCLI command="indexer:reindex" stepKey="reindex"/> - <magentoCLI command="cache:flush" stepKey="flushCache"/> - </before> - <after> - <deleteData createDataKey="simple1" stepKey="deleteSimple1" before="deleteSimple2"/> - <deleteData createDataKey="simple2" stepKey="deleteSimple2" before="delete"/> - </after> - </test> - <test name="AdvanceCatalogSearchGroupedProductByPriceTest" extends="AdvanceCatalogSearchSimpleProductByPriceTest"> - <annotations> - <features value="GroupedProduct"/> - <stories value="Advanced Catalog Product Search for all product types"/> - <title value="Guest customer should be able to advance search Grouped product with product price"/> - <description value="Guest customer should be able to advance search Grouped product with product price"/> - <severity value="MAJOR"/> - <testCaseId value="MC-284"/> - <group value="GroupedProduct"/> - <group value="SearchEngineElasticsearch"/> - </annotations> - <before> - <createData entity="ApiProductWithDescription" stepKey="simple1" before="simple2"/> - <createData entity="ApiProductWithDescription" stepKey="simple2" before="product"/> - <createData entity="ApiGroupedProduct" stepKey="product"/> - <createData entity="OneSimpleProductLink" stepKey="addProductOne"> - <requiredEntity createDataKey="product"/> - <requiredEntity createDataKey="simple1"/> - </createData> - <updateData entity="OneMoreSimpleProductLink" createDataKey="addProductOne" stepKey="addProductTwo"> - <requiredEntity createDataKey="product"/> - <requiredEntity createDataKey="simple2"/> - </updateData> - <getData entity="GetProduct3" stepKey="arg1"> - <requiredEntity createDataKey="product"/> - </getData> - <getData entity="GetProduct" stepKey="arg2"> - <requiredEntity createDataKey="simple1"/> - </getData> - <getData entity="GetProduct" stepKey="arg3"> - <requiredEntity createDataKey="simple2"/> - </getData> - <magentoCLI command="indexer:reindex" stepKey="reindex"/> - <magentoCLI command="cache:flush" stepKey="flushCache"/> - </before> - <after> - <deleteData createDataKey="simple1" stepKey="deleteSimple1" before="deleteSimple2"/> - <deleteData createDataKey="simple2" stepKey="deleteSimple2" before="delete"/> - </after> - <see userInput="3 items" selector="{{StorefrontCatalogSearchAdvancedResultMainSection.itemFound}}" stepKey="see"/> - <see userInput="$$product.name$$" selector="{{StorefrontCatalogSearchAdvancedResultMainSection.nthProductName('1')}}" stepKey="seeProductName"/> - <see userInput="$$simple1.name$$" selector="{{StorefrontCatalogSearchAdvancedResultMainSection.nthProductName('2')}}" stepKey="seeSimple1ProductName"/> - <see userInput="$$simple2.name$$" selector="{{StorefrontCatalogSearchAdvancedResultMainSection.nthProductName('3')}}" stepKey="seeSimple2ProductName"/> - </test> - <test name="AdvanceCatalogSearchGroupedProductByPriceMysqlTest" extends="AdvanceCatalogSearchSimpleProductByPriceTest"> - <annotations> - <features value="GroupedProduct"/> - <stories value="Advanced Catalog Product Search for all product types"/> - <title value="Guest customer should be able to advance search Grouped product with product price using the MySQL search engine"/> - <description value="Guest customer should be able to advance search Grouped product with product price using the MySQL search engine"/> - <severity value="MAJOR"/> - <testCaseId value="MC-20470"/> - <group value="GroupedProduct"/> - <group value="SearchEngineMysql"/> - </annotations> - <before> - <createData entity="ApiProductWithDescription" stepKey="simple1" before="simple2"/> - <createData entity="ApiProductWithDescription" stepKey="simple2" before="product"/> - <createData entity="ApiGroupedProduct" stepKey="product"/> - <createData entity="OneSimpleProductLink" stepKey="addProductOne"> - <requiredEntity createDataKey="product"/> - <requiredEntity createDataKey="simple1"/> - </createData> - <updateData entity="OneMoreSimpleProductLink" createDataKey="addProductOne" stepKey="addProductTwo"> - <requiredEntity createDataKey="product"/> - <requiredEntity createDataKey="simple2"/> - </updateData> - <getData entity="GetProduct3" stepKey="arg1"> - <requiredEntity createDataKey="product"/> - </getData> - <getData entity="GetProduct" stepKey="arg2"> - <requiredEntity createDataKey="simple1"/> - </getData> - <getData entity="GetProduct" stepKey="arg3"> - <requiredEntity createDataKey="simple2"/> - </getData> - <magentoCLI command="indexer:reindex" stepKey="reindex"/> - <magentoCLI command="cache:flush" stepKey="flushCache"/> - </before> - <after> - <deleteData createDataKey="simple1" stepKey="deleteSimple1" before="deleteSimple2"/> - <deleteData createDataKey="simple2" stepKey="deleteSimple2" before="delete"/> - </after> - </test> -</tests> diff --git a/app/code/Magento/GroupedProduct/Test/Mftf/Test/AdvanceCatalogSearchGroupedProductTest/AdvanceCatalogSearchGroupedProductByDescriptionMysqlTest.xml b/app/code/Magento/GroupedProduct/Test/Mftf/Test/AdvanceCatalogSearchGroupedProductTest/AdvanceCatalogSearchGroupedProductByDescriptionMysqlTest.xml new file mode 100644 index 0000000000000..910c770fb1d3e --- /dev/null +++ b/app/code/Magento/GroupedProduct/Test/Mftf/Test/AdvanceCatalogSearchGroupedProductTest/AdvanceCatalogSearchGroupedProductByDescriptionMysqlTest.xml @@ -0,0 +1,41 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + /** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +--> +<tests xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/testSchema.xsd"> + <test name="AdvanceCatalogSearchGroupedProductByDescriptionMysqlTest" extends="AdvanceCatalogSearchSimpleProductByDescriptionTest"> + <annotations> + <features value="GroupedProduct"/> + <stories value="Advanced Catalog Product Search for all product types"/> + <title value="Guest customer should be able to advance search Grouped product with product description using the MySQL search engine"/> + <description value="Guest customer should be able to advance search Grouped product with product description using the MYSQL search engine"/> + <severity value="MAJOR"/> + <testCaseId value="MC-20468"/> + <group value="GroupedProduct"/> + <group value="SearchEngineMysql"/> + </annotations> + <before> + <createData entity="ApiProductWithDescription" stepKey="simple1" before="simple2"/> + <createData entity="ApiProductWithDescription" stepKey="simple2" before="product"/> + <createData entity="ApiGroupedProduct" stepKey="product"/> + <createData entity="OneSimpleProductLink" stepKey="addProductOne"> + <requiredEntity createDataKey="product"/> + <requiredEntity createDataKey="simple1"/> + </createData> + <updateData entity="OneMoreSimpleProductLink" createDataKey="addProductOne" stepKey="addProductTwo"> + <requiredEntity createDataKey="product"/> + <requiredEntity createDataKey="simple2"/> + </updateData> + <magentoCLI command="indexer:reindex" stepKey="reindex"/> + <magentoCLI command="cache:flush" stepKey="flushCache"/> + </before> + <after> + <deleteData createDataKey="simple1" stepKey="deleteSimple1" before="deleteSimple2"/> + <deleteData createDataKey="simple2" stepKey="deleteSimple2" before="delete"/> + </after> + </test> +</tests> diff --git a/app/code/Magento/GroupedProduct/Test/Mftf/Test/AdvanceCatalogSearchGroupedProductTest/AdvanceCatalogSearchGroupedProductByDescriptionTest.xml b/app/code/Magento/GroupedProduct/Test/Mftf/Test/AdvanceCatalogSearchGroupedProductTest/AdvanceCatalogSearchGroupedProductByDescriptionTest.xml new file mode 100644 index 0000000000000..599736e7e817e --- /dev/null +++ b/app/code/Magento/GroupedProduct/Test/Mftf/Test/AdvanceCatalogSearchGroupedProductTest/AdvanceCatalogSearchGroupedProductByDescriptionTest.xml @@ -0,0 +1,45 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + /** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +--> +<tests xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/testSchema.xsd"> + <test name="AdvanceCatalogSearchGroupedProductByDescriptionTest" extends="AdvanceCatalogSearchSimpleProductByDescriptionTest"> + <annotations> + <features value="GroupedProduct"/> + <stories value="Advanced Catalog Product Search for all product types"/> + <title value="Guest customer should be able to advance search Grouped product with product description"/> + <description value="Guest customer should be able to advance search Grouped product with product description"/> + <severity value="MAJOR"/> + <testCaseId value="MC-282"/> + <group value="GroupedProduct"/> + <group value="SearchEngineElasticsearch"/> + </annotations> + <before> + <createData entity="ApiProductWithDescription" stepKey="simple1" before="simple2"/> + <createData entity="ApiProductWithDescription" stepKey="simple2" before="product"/> + <createData entity="ApiGroupedProduct" stepKey="product"/> + <createData entity="OneSimpleProductLink" stepKey="addProductOne"> + <requiredEntity createDataKey="product"/> + <requiredEntity createDataKey="simple1"/> + </createData> + <updateData entity="OneMoreSimpleProductLink" createDataKey="addProductOne" stepKey="addProductTwo"> + <requiredEntity createDataKey="product"/> + <requiredEntity createDataKey="simple2"/> + </updateData> + <magentoCLI command="indexer:reindex" stepKey="reindex"/> + <magentoCLI command="cache:flush" stepKey="flushCache"/> + </before> + <after> + <deleteData createDataKey="simple1" stepKey="deleteSimple1" before="deleteSimple2"/> + <deleteData createDataKey="simple2" stepKey="deleteSimple2" before="delete"/> + </after> + <see userInput="3 items" selector="{{StorefrontCatalogSearchAdvancedResultMainSection.itemFound}}" stepKey="see"/> + <see userInput="$$product.name$$" selector="{{StorefrontCatalogSearchAdvancedResultMainSection.nthProductName('1')}}" stepKey="seeProductName"/> + <see userInput="$$simple1.name$$" selector="{{StorefrontCatalogSearchAdvancedResultMainSection.nthProductName('2')}}" stepKey="seeSimple1ProductName"/> + <see userInput="$$simple2.name$$" selector="{{StorefrontCatalogSearchAdvancedResultMainSection.nthProductName('3')}}" stepKey="seeSimple2ProductName"/> + </test> +</tests> diff --git a/app/code/Magento/GroupedProduct/Test/Mftf/Test/AdvanceCatalogSearchGroupedProductTest/AdvanceCatalogSearchGroupedProductByNameMysqlTest.xml b/app/code/Magento/GroupedProduct/Test/Mftf/Test/AdvanceCatalogSearchGroupedProductTest/AdvanceCatalogSearchGroupedProductByNameMysqlTest.xml new file mode 100644 index 0000000000000..b6e4fb71aa40b --- /dev/null +++ b/app/code/Magento/GroupedProduct/Test/Mftf/Test/AdvanceCatalogSearchGroupedProductTest/AdvanceCatalogSearchGroupedProductByNameMysqlTest.xml @@ -0,0 +1,41 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + /** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +--> +<tests xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/testSchema.xsd"> + <test name="AdvanceCatalogSearchGroupedProductByNameMysqlTest" extends="AdvanceCatalogSearchSimpleProductByNameTest"> + <annotations> + <features value="GroupedProduct"/> + <stories value="Advanced Catalog Product Search for all product types"/> + <title value="Guest customer should be able to advance search Grouped product with product name using the MySQL search engine"/> + <description value="Guest customer should be able to advance search Grouped product with product name using the MySQL search engine"/> + <severity value="MAJOR"/> + <testCaseId value="MC-20464"/> + <group value="GroupedProduct"/> + <group value="SearchEngineMysql"/> + </annotations> + <before> + <createData entity="ApiProductWithDescription" stepKey="simple1" before="simple2"/> + <createData entity="ApiProductWithDescription" stepKey="simple2" before="product"/> + <createData entity="ApiGroupedProduct" stepKey="product"/> + <createData entity="OneSimpleProductLink" stepKey="addProductOne"> + <requiredEntity createDataKey="product"/> + <requiredEntity createDataKey="simple1"/> + </createData> + <updateData entity="OneMoreSimpleProductLink" createDataKey="addProductOne" stepKey="addProductTwo"> + <requiredEntity createDataKey="product"/> + <requiredEntity createDataKey="simple2"/> + </updateData> + <magentoCLI command="indexer:reindex" stepKey="reindex"/> + <magentoCLI command="cache:flush" stepKey="flushCache"/> + </before> + <after> + <deleteData createDataKey="simple1" stepKey="deleteSimple1" before="deleteSimple2"/> + <deleteData createDataKey="simple2" stepKey="deleteSimple2" before="delete"/> + </after> + </test> +</tests> diff --git a/app/code/Magento/GroupedProduct/Test/Mftf/Test/AdvanceCatalogSearchGroupedProductTest/AdvanceCatalogSearchGroupedProductByNameTest.xml b/app/code/Magento/GroupedProduct/Test/Mftf/Test/AdvanceCatalogSearchGroupedProductTest/AdvanceCatalogSearchGroupedProductByNameTest.xml new file mode 100644 index 0000000000000..853304c557c8f --- /dev/null +++ b/app/code/Magento/GroupedProduct/Test/Mftf/Test/AdvanceCatalogSearchGroupedProductTest/AdvanceCatalogSearchGroupedProductByNameTest.xml @@ -0,0 +1,45 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + /** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +--> +<tests xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/testSchema.xsd"> + <test name="AdvanceCatalogSearchGroupedProductByNameTest" extends="AdvanceCatalogSearchSimpleProductByNameTest"> + <annotations> + <features value="GroupedProduct"/> + <stories value="Advanced Catalog Product Search for all product types"/> + <title value="Guest customer should be able to advance search Grouped product with product name"/> + <description value="Guest customer should be able to advance search Grouped product with product name"/> + <severity value="MAJOR"/> + <testCaseId value="MC-141"/> + <group value="GroupedProduct"/> + <group value="SearchEngineElasticsearch"/> + </annotations> + <before> + <createData entity="ApiProductWithDescription" stepKey="simple1" before="simple2"/> + <createData entity="ApiProductWithDescription" stepKey="simple2" before="product"/> + <createData entity="ApiGroupedProduct" stepKey="product"/> + <createData entity="OneSimpleProductLink" stepKey="addProductOne"> + <requiredEntity createDataKey="product"/> + <requiredEntity createDataKey="simple1"/> + </createData> + <updateData entity="OneMoreSimpleProductLink" createDataKey="addProductOne" stepKey="addProductTwo"> + <requiredEntity createDataKey="product"/> + <requiredEntity createDataKey="simple2"/> + </updateData> + <magentoCLI command="indexer:reindex" stepKey="reindex"/> + <magentoCLI command="cache:flush" stepKey="flushCache"/> + </before> + <after> + <deleteData createDataKey="simple1" stepKey="deleteSimple1" before="deleteSimple2"/> + <deleteData createDataKey="simple2" stepKey="deleteSimple2" before="delete"/> + </after> + <see userInput="3 items" selector="{{StorefrontCatalogSearchAdvancedResultMainSection.itemFound}}" stepKey="see"/> + <see userInput="$$product.name$$" selector="{{StorefrontCatalogSearchAdvancedResultMainSection.nthProductName('1')}}" stepKey="seeProductName"/> + <see userInput="$$simple1.name$$" selector="{{StorefrontCatalogSearchAdvancedResultMainSection.nthProductName('2')}}" stepKey="seeSimple1ProductName"/> + <see userInput="$$simple2.name$$" selector="{{StorefrontCatalogSearchAdvancedResultMainSection.nthProductName('3')}}" stepKey="seeSimple2ProductName"/> + </test> +</tests> diff --git a/app/code/Magento/GroupedProduct/Test/Mftf/Test/AdvanceCatalogSearchGroupedProductTest/AdvanceCatalogSearchGroupedProductByPriceMysqlTest.xml b/app/code/Magento/GroupedProduct/Test/Mftf/Test/AdvanceCatalogSearchGroupedProductTest/AdvanceCatalogSearchGroupedProductByPriceMysqlTest.xml new file mode 100644 index 0000000000000..74dd95fd3249e --- /dev/null +++ b/app/code/Magento/GroupedProduct/Test/Mftf/Test/AdvanceCatalogSearchGroupedProductTest/AdvanceCatalogSearchGroupedProductByPriceMysqlTest.xml @@ -0,0 +1,50 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + /** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +--> +<tests xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/testSchema.xsd"> + <test name="AdvanceCatalogSearchGroupedProductByPriceMysqlTest" extends="AdvanceCatalogSearchSimpleProductByPriceTest"> + <annotations> + <features value="GroupedProduct"/> + <stories value="Advanced Catalog Product Search for all product types"/> + <title value="Guest customer should be able to advance search Grouped product with product price using the MySQL search engine"/> + <description value="Guest customer should be able to advance search Grouped product with product price using the MySQL search engine"/> + <severity value="MAJOR"/> + <testCaseId value="MC-20470"/> + <group value="GroupedProduct"/> + <group value="SearchEngineMysql"/> + </annotations> + <before> + <createData entity="ApiProductWithDescription" stepKey="simple1" before="simple2"/> + <createData entity="ApiProductWithDescription" stepKey="simple2" before="product"/> + <createData entity="ApiGroupedProduct" stepKey="product"/> + <createData entity="OneSimpleProductLink" stepKey="addProductOne"> + <requiredEntity createDataKey="product"/> + <requiredEntity createDataKey="simple1"/> + </createData> + <updateData entity="OneMoreSimpleProductLink" createDataKey="addProductOne" stepKey="addProductTwo"> + <requiredEntity createDataKey="product"/> + <requiredEntity createDataKey="simple2"/> + </updateData> + <getData entity="GetProduct3" stepKey="arg1"> + <requiredEntity createDataKey="product"/> + </getData> + <getData entity="GetProduct" stepKey="arg2"> + <requiredEntity createDataKey="simple1"/> + </getData> + <getData entity="GetProduct" stepKey="arg3"> + <requiredEntity createDataKey="simple2"/> + </getData> + <magentoCLI command="indexer:reindex" stepKey="reindex"/> + <magentoCLI command="cache:flush" stepKey="flushCache"/> + </before> + <after> + <deleteData createDataKey="simple1" stepKey="deleteSimple1" before="deleteSimple2"/> + <deleteData createDataKey="simple2" stepKey="deleteSimple2" before="delete"/> + </after> + </test> +</tests> diff --git a/app/code/Magento/GroupedProduct/Test/Mftf/Test/AdvanceCatalogSearchGroupedProductTest/AdvanceCatalogSearchGroupedProductByPriceTest.xml b/app/code/Magento/GroupedProduct/Test/Mftf/Test/AdvanceCatalogSearchGroupedProductTest/AdvanceCatalogSearchGroupedProductByPriceTest.xml new file mode 100644 index 0000000000000..4e67f2bd50439 --- /dev/null +++ b/app/code/Magento/GroupedProduct/Test/Mftf/Test/AdvanceCatalogSearchGroupedProductTest/AdvanceCatalogSearchGroupedProductByPriceTest.xml @@ -0,0 +1,54 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + /** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +--> +<tests xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/testSchema.xsd"> + <test name="AdvanceCatalogSearchGroupedProductByPriceTest" extends="AdvanceCatalogSearchSimpleProductByPriceTest"> + <annotations> + <features value="GroupedProduct"/> + <stories value="Advanced Catalog Product Search for all product types"/> + <title value="Guest customer should be able to advance search Grouped product with product price"/> + <description value="Guest customer should be able to advance search Grouped product with product price"/> + <severity value="MAJOR"/> + <testCaseId value="MC-284"/> + <group value="GroupedProduct"/> + <group value="SearchEngineElasticsearch"/> + </annotations> + <before> + <createData entity="ApiProductWithDescription" stepKey="simple1" before="simple2"/> + <createData entity="ApiProductWithDescription" stepKey="simple2" before="product"/> + <createData entity="ApiGroupedProduct" stepKey="product"/> + <createData entity="OneSimpleProductLink" stepKey="addProductOne"> + <requiredEntity createDataKey="product"/> + <requiredEntity createDataKey="simple1"/> + </createData> + <updateData entity="OneMoreSimpleProductLink" createDataKey="addProductOne" stepKey="addProductTwo"> + <requiredEntity createDataKey="product"/> + <requiredEntity createDataKey="simple2"/> + </updateData> + <getData entity="GetProduct3" stepKey="arg1"> + <requiredEntity createDataKey="product"/> + </getData> + <getData entity="GetProduct" stepKey="arg2"> + <requiredEntity createDataKey="simple1"/> + </getData> + <getData entity="GetProduct" stepKey="arg3"> + <requiredEntity createDataKey="simple2"/> + </getData> + <magentoCLI command="indexer:reindex" stepKey="reindex"/> + <magentoCLI command="cache:flush" stepKey="flushCache"/> + </before> + <after> + <deleteData createDataKey="simple1" stepKey="deleteSimple1" before="deleteSimple2"/> + <deleteData createDataKey="simple2" stepKey="deleteSimple2" before="delete"/> + </after> + <see userInput="3 items" selector="{{StorefrontCatalogSearchAdvancedResultMainSection.itemFound}}" stepKey="see"/> + <see userInput="$$product.name$$" selector="{{StorefrontCatalogSearchAdvancedResultMainSection.nthProductName('1')}}" stepKey="seeProductName"/> + <see userInput="$$simple1.name$$" selector="{{StorefrontCatalogSearchAdvancedResultMainSection.nthProductName('2')}}" stepKey="seeSimple1ProductName"/> + <see userInput="$$simple2.name$$" selector="{{StorefrontCatalogSearchAdvancedResultMainSection.nthProductName('3')}}" stepKey="seeSimple2ProductName"/> + </test> +</tests> diff --git a/app/code/Magento/GroupedProduct/Test/Mftf/Test/AdvanceCatalogSearchGroupedProductTest/AdvanceCatalogSearchGroupedProductByShortDescriptionMysqlTest.xml b/app/code/Magento/GroupedProduct/Test/Mftf/Test/AdvanceCatalogSearchGroupedProductTest/AdvanceCatalogSearchGroupedProductByShortDescriptionMysqlTest.xml new file mode 100644 index 0000000000000..45822ce9b63e6 --- /dev/null +++ b/app/code/Magento/GroupedProduct/Test/Mftf/Test/AdvanceCatalogSearchGroupedProductTest/AdvanceCatalogSearchGroupedProductByShortDescriptionMysqlTest.xml @@ -0,0 +1,41 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + /** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +--> +<tests xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/testSchema.xsd"> + <test name="AdvanceCatalogSearchGroupedProductByShortDescriptionMysqlTest" extends="AdvanceCatalogSearchSimpleProductByShortDescriptionTest"> + <annotations> + <features value="GroupedProduct"/> + <stories value="Advanced Catalog Product Search for all product types"/> + <title value="Guest customer should be able to advance search Grouped product with product short description using the MySQL search engine"/> + <description value="Guest customer should be able to advance search Grouped product with product short description using the MySQL search engine"/> + <severity value="MAJOR"/> + <testCaseId value="MC-20469"/> + <group value="GroupedProduct"/> + <group value="SearchEngineMysql"/> + </annotations> + <before> + <createData entity="ApiProductWithDescription" stepKey="simple1" before="simple2"/> + <createData entity="ApiProductWithDescription" stepKey="simple2" before="product"/> + <createData entity="ApiGroupedProduct" stepKey="product"/> + <createData entity="OneSimpleProductLink" stepKey="addProductOne"> + <requiredEntity createDataKey="product"/> + <requiredEntity createDataKey="simple1"/> + </createData> + <updateData entity="OneMoreSimpleProductLink" createDataKey="addProductOne" stepKey="addProductTwo"> + <requiredEntity createDataKey="product"/> + <requiredEntity createDataKey="simple2"/> + </updateData> + <magentoCLI command="indexer:reindex" stepKey="reindex"/> + <magentoCLI command="cache:flush" stepKey="flushCache"/> + </before> + <after> + <deleteData createDataKey="simple1" stepKey="deleteSimple1" before="deleteSimple2"/> + <deleteData createDataKey="simple2" stepKey="deleteSimple2" before="delete"/> + </after> + </test> +</tests> diff --git a/app/code/Magento/GroupedProduct/Test/Mftf/Test/AdvanceCatalogSearchGroupedProductTest/AdvanceCatalogSearchGroupedProductByShortDescriptionTest.xml b/app/code/Magento/GroupedProduct/Test/Mftf/Test/AdvanceCatalogSearchGroupedProductTest/AdvanceCatalogSearchGroupedProductByShortDescriptionTest.xml new file mode 100644 index 0000000000000..4b86a2c085003 --- /dev/null +++ b/app/code/Magento/GroupedProduct/Test/Mftf/Test/AdvanceCatalogSearchGroupedProductTest/AdvanceCatalogSearchGroupedProductByShortDescriptionTest.xml @@ -0,0 +1,45 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + /** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +--> +<tests xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/testSchema.xsd"> + <test name="AdvanceCatalogSearchGroupedProductByShortDescriptionTest" extends="AdvanceCatalogSearchSimpleProductByShortDescriptionTest"> + <annotations> + <features value="GroupedProduct"/> + <stories value="Advanced Catalog Product Search for all product types"/> + <title value="Guest customer should be able to advance search Grouped product with product short description"/> + <description value="Guest customer should be able to advance search Grouped product with product short description"/> + <severity value="MAJOR"/> + <testCaseId value="MC-283"/> + <group value="GroupedProduct"/> + <group value="SearchEngineElasticsearch"/> + </annotations> + <before> + <createData entity="ApiProductWithDescription" stepKey="simple1" before="simple2"/> + <createData entity="ApiProductWithDescription" stepKey="simple2" before="product"/> + <createData entity="ApiGroupedProduct" stepKey="product"/> + <createData entity="OneSimpleProductLink" stepKey="addProductOne"> + <requiredEntity createDataKey="product"/> + <requiredEntity createDataKey="simple1"/> + </createData> + <updateData entity="OneMoreSimpleProductLink" createDataKey="addProductOne" stepKey="addProductTwo"> + <requiredEntity createDataKey="product"/> + <requiredEntity createDataKey="simple2"/> + </updateData> + <magentoCLI command="indexer:reindex" stepKey="reindex"/> + <magentoCLI command="cache:flush" stepKey="flushCache"/> + </before> + <after> + <deleteData createDataKey="simple1" stepKey="deleteSimple1" before="deleteSimple2"/> + <deleteData createDataKey="simple2" stepKey="deleteSimple2" before="delete"/> + </after> + <see userInput="3 items" selector="{{StorefrontCatalogSearchAdvancedResultMainSection.itemFound}}" stepKey="see"/> + <see userInput="$$product.name$$" selector="{{StorefrontCatalogSearchAdvancedResultMainSection.nthProductName('1')}}" stepKey="seeProductName"/> + <see userInput="$$simple1.name$$" selector="{{StorefrontCatalogSearchAdvancedResultMainSection.nthProductName('2')}}" stepKey="seeSimple1ProductName"/> + <see userInput="$$simple2.name$$" selector="{{StorefrontCatalogSearchAdvancedResultMainSection.nthProductName('3')}}" stepKey="seeSimple2ProductName"/> + </test> +</tests> diff --git a/app/code/Magento/GroupedProduct/Test/Mftf/Test/AdvanceCatalogSearchGroupedProductTest/AdvanceCatalogSearchGroupedProductBySkuTest.xml b/app/code/Magento/GroupedProduct/Test/Mftf/Test/AdvanceCatalogSearchGroupedProductTest/AdvanceCatalogSearchGroupedProductBySkuTest.xml new file mode 100644 index 0000000000000..6e67e41fa447b --- /dev/null +++ b/app/code/Magento/GroupedProduct/Test/Mftf/Test/AdvanceCatalogSearchGroupedProductTest/AdvanceCatalogSearchGroupedProductBySkuTest.xml @@ -0,0 +1,40 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + /** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +--> +<tests xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/testSchema.xsd"> + <test name="AdvanceCatalogSearchGroupedProductBySkuTest" extends="AdvanceCatalogSearchSimpleProductBySkuTest"> + <annotations> + <features value="GroupedProduct"/> + <stories value="Advanced Catalog Product Search for all product types"/> + <title value="Guest customer should be able to advance search Grouped product with product sku"/> + <description value="Guest customer should be able to advance search Grouped product with product sku"/> + <severity value="MAJOR"/> + <testCaseId value="MC-146"/> + <group value="GroupedProduct"/> + </annotations> + <before> + <createData entity="ApiProductWithDescription" stepKey="simple1" before="simple2"/> + <createData entity="ApiProductWithDescription" stepKey="simple2" before="product"/> + <createData entity="ApiGroupedProductAndUnderscoredSku" stepKey="product"/> + <createData entity="OneSimpleProductLink" stepKey="addProductOne"> + <requiredEntity createDataKey="product"/> + <requiredEntity createDataKey="simple1"/> + </createData> + <updateData entity="OneMoreSimpleProductLink" createDataKey="addProductOne" stepKey="addProductTwo"> + <requiredEntity createDataKey="product"/> + <requiredEntity createDataKey="simple2"/> + </updateData> + <magentoCLI command="indexer:reindex" stepKey="reindex"/> + <magentoCLI command="cache:flush" stepKey="flushCache"/> + </before> + <after> + <deleteData createDataKey="simple1" stepKey="deleteSimple1" before="deleteSimple2"/> + <deleteData createDataKey="simple2" stepKey="deleteSimple2" before="delete"/> + </after> + </test> +</tests> diff --git a/app/code/Magento/GroupedProduct/Test/Unit/Model/Product/Type/GroupedTest.php b/app/code/Magento/GroupedProduct/Test/Unit/Model/Product/Type/GroupedTest.php index e50d6491a6aca..407224bf28f32 100644 --- a/app/code/Magento/GroupedProduct/Test/Unit/Model/Product/Type/GroupedTest.php +++ b/app/code/Magento/GroupedProduct/Test/Unit/Model/Product/Type/GroupedTest.php @@ -8,6 +8,8 @@ use Magento\GroupedProduct\Model\Product\Type\Grouped; /** + * Tests for Grouped product + * * @SuppressWarnings(PHPMD.CouplingBetweenObjects) */ class GroupedTest extends \PHPUnit\Framework\TestCase @@ -42,6 +44,9 @@ class GroupedTest extends \PHPUnit\Framework\TestCase */ private $serializer; + /** + * @inheritdoc + */ protected function setUp() { $this->objectHelper = new \Magento\Framework\TestFramework\Unit\Helper\ObjectManager($this); @@ -74,12 +79,22 @@ protected function setUp() ); } - public function testHasWeightFalse() + /** + * Verify has weight is false + * + * @return void + */ + public function testHasWeightFalse(): void { $this->assertFalse($this->_model->hasWeight(), 'This product has weight, but it should not'); } - public function testGetChildrenIds() + /** + * Verify children ids. + * + * @return void + */ + public function testGetChildrenIds(): void { $parentId = 12345; $childrenIds = [100, 200, 300]; @@ -96,7 +111,12 @@ public function testGetChildrenIds() $this->assertEquals($childrenIds, $this->_model->getChildrenIds($parentId)); } - public function testGetParentIdsByChild() + /** + * Verify get parents by child products + * + * @return void + */ + public function testGetParentIdsByChild(): void { $childId = 12345; $parentIds = [100, 200, 300]; @@ -113,7 +133,12 @@ public function testGetParentIdsByChild() $this->assertEquals($parentIds, $this->_model->getParentIdsByChild($childId)); } - public function testGetAssociatedProducts() + /** + * Verify get associated products + * + * @return void + */ + public function testGetAssociatedProducts(): void { $cached = true; $associatedProducts = [5, 7, 11, 13, 17]; @@ -123,12 +148,14 @@ public function testGetAssociatedProducts() } /** + * Verify able to set status filter + * * @param int $status * @param array $filters * @param array $result * @dataProvider addStatusFilterDataProvider */ - public function testAddStatusFilter($status, $filters, $result) + public function testAddStatusFilter($status, $filters, $result): void { $this->product->expects($this->once())->method('getData')->will($this->returnValue($filters)); $this->product->expects($this->once())->method('setData')->with('_cache_instance_status_filters', $result); @@ -136,14 +163,21 @@ public function testAddStatusFilter($status, $filters, $result) } /** + * Data Provider for Status Filter + * * @return array */ - public function addStatusFilterDataProvider() + public function addStatusFilterDataProvider(): array { return [[1, [], [1]], [1, false, [1]]]; } - public function testSetSaleableStatus() + /** + * Verify able to set salable status + * + * @return void + */ + public function testSetSaleableStatus(): void { $key = '_cache_instance_status_filters'; $saleableIds = [300, 800, 500]; @@ -159,7 +193,12 @@ public function testSetSaleableStatus() $this->assertEquals($this->_model, $this->_model->setSaleableStatus($this->product)); } - public function testGetStatusFiltersNoData() + /** + * Verify status filter with no data. + * + * @return void + */ + public function testGetStatusFiltersNoData(): void { $result = [ \Magento\Catalog\Model\Product\Attribute\Source\Status::STATUS_ENABLED, @@ -169,7 +208,12 @@ public function testGetStatusFiltersNoData() $this->assertEquals($result, $this->_model->getStatusFilters($this->product)); } - public function testGetStatusFiltersWithData() + /** + * Verify status filter with data + * + * @return void + */ + public function testGetStatusFiltersWithData(): void { $result = [ \Magento\Catalog\Model\Product\Attribute\Source\Status::STATUS_ENABLED, @@ -180,7 +224,12 @@ public function testGetStatusFiltersWithData() $this->assertEquals($result, $this->_model->getStatusFilters($this->product)); } - public function testGetAssociatedProductIdsCached() + /** + * Verify AssociatedProducts Ids with cache + * + * @return void + */ + public function testGetAssociatedProductIdsCached(): void { $key = '_cache_instance_associated_product_ids'; $cachedData = [300, 303, 306]; @@ -192,7 +241,12 @@ public function testGetAssociatedProductIdsCached() $this->assertEquals($cachedData, $this->_model->getAssociatedProductIds($this->product)); } - public function testGetAssociatedProductIdsNonCached() + /** + * Verify AssociatedProducts Ids with no cached. + * + * @return void + */ + public function testGetAssociatedProductIdsNonCached(): void { $args = $this->objectHelper->getConstructArguments( \Magento\GroupedProduct\Model\Product\Type\Grouped::class, @@ -236,7 +290,12 @@ public function testGetAssociatedProductIdsNonCached() $this->assertEquals($associatedIds, $model->getAssociatedProductIds($this->product)); } - public function testGetAssociatedProductCollection() + /** + * Verify Associated Product collection + * + * @return void + */ + public function testGetAssociatedProductCollection(): void { $link = $this->createPartialMock( \Magento\Catalog\Model\Product\Link::class, @@ -261,6 +320,8 @@ public function testGetAssociatedProductCollection() } /** + * Verify Proccess buy request + * * @param array $superGroup * @param array $result * @dataProvider processBuyRequestDataProvider @@ -274,9 +335,11 @@ public function testProcessBuyRequest($superGroup, $result) } /** + * dataProvider for buy request + * * @return array */ - public function processBuyRequestDataProvider() + public function processBuyRequestDataProvider(): array { return [ 'positive' => [[1, 2, 3], ['super_group' => [1, 2, 3]]], @@ -285,9 +348,12 @@ public function processBuyRequestDataProvider() } /** + * Get Children Msrp when children product with Msrp + * + * @return void * @SuppressWarnings(PHPMD.UnusedLocalVariable) */ - public function testGetChildrenMsrpWhenNoChildrenWithMsrp() + public function testGetChildrenMsrpWhenNoChildrenWithMsrp(): void { $key = '_cache_instance_associated_products'; @@ -298,7 +364,12 @@ public function testGetChildrenMsrpWhenNoChildrenWithMsrp() $this->assertEquals(0, $this->_model->getChildrenMsrp($this->product)); } - public function testPrepareForCartAdvancedEmpty() + /** + * Prepare for card method with advanced empty + * + * @return void + */ + public function testPrepareForCartAdvancedEmpty(): void { $this->product = $this->createMock(\Magento\Catalog\Model\Product::class); $buyRequest = new \Magento\Framework\DataObject(); @@ -381,7 +452,12 @@ public function testPrepareForCartAdvancedEmpty() ); } - public function testPrepareForCartAdvancedNoProductsStrictTrue() + /** + * Prepare for card with no products set strict option true + * + * @return void + */ + public function testPrepareForCartAdvancedNoProductsStrictTrue(): void { $buyRequest = new \Magento\Framework\DataObject(); $buyRequest->setSuperGroup([0 => 0]); @@ -404,7 +480,12 @@ public function testPrepareForCartAdvancedNoProductsStrictTrue() ); } - public function testPrepareForCartAdvancedNoProductsStrictFalse() + /** + * Prepare for card with no products and set strict to false + * + * @return void + */ + public function testPrepareForCartAdvancedNoProductsStrictFalse(): void { $buyRequest = new \Magento\Framework\DataObject(); $buyRequest->setSuperGroup([0 => 0]); @@ -429,7 +510,12 @@ public function testPrepareForCartAdvancedNoProductsStrictFalse() ); } - public function testPrepareForCartAdvancedWithProductsStrictFalseStringResult() + /** + * Verify Prepare for cart product with Product strict flase and string result + * + * @return false + */ + public function testPrepareForCartAdvancedWithProductsStrictFalseStringResult(): void { $associatedProduct = $this->createMock(\Magento\Catalog\Model\Product::class); $associatedId = 9384; @@ -463,7 +549,12 @@ public function testPrepareForCartAdvancedWithProductsStrictFalseStringResult() ); } - public function testPrepareForCartAdvancedWithProductsStrictFalseEmptyArrayResult() + /** + * Verify prepare for cart with strict option set to false and empty array + * + * @return void + */ + public function testPrepareForCartAdvancedWithProductsStrictFalseEmptyArrayResult(): void { $expectedMsg = "Cannot process the item."; $associatedProduct = $this->createMock(\Magento\Catalog\Model\Product::class); @@ -498,7 +589,12 @@ public function testPrepareForCartAdvancedWithProductsStrictFalseEmptyArrayResul ); } - public function testPrepareForCartAdvancedWithProductsStrictFalse() + /** + * Prepare for cart product with Product strict option st to false. + * + * @return void + */ + public function testPrepareForCartAdvancedWithProductsStrictFalse(): void { $associatedProduct = $this->createMock(\Magento\Catalog\Model\Product::class); $associatedId = 9384; @@ -541,61 +637,53 @@ public function testPrepareForCartAdvancedWithProductsStrictFalse() ); } - public function testPrepareForCartAdvancedWithProductsStrictTrue() - { - $associatedProduct = $this->createMock(\Magento\Catalog\Model\Product::class); - $associatedId = 9384; - $associatedProduct->expects($this->atLeastOnce())->method('getId')->will($this->returnValue($associatedId)); - - $typeMock = $this->createPartialMock( - \Magento\Catalog\Model\Product\Type\AbstractType::class, - ['_prepareProduct', 'deleteTypeSpecificData'] - ); - $associatedPrepareResult = $this->getMockBuilder(\Magento\Catalog\Model\Product::class) - ->setMockClassName('resultProduct') - ->disableOriginalConstructor() - ->getMock(); - $typeMock->expects($this->once())->method('_prepareProduct')->willReturn([$associatedPrepareResult]); - - $associatedProduct->expects($this->once())->method('getTypeInstance')->willReturn($typeMock); - - $buyRequest = new \Magento\Framework\DataObject(); - $buyRequest->setSuperGroup([$associatedId => 1]); - - $this->serializer->expects($this->any()) - ->method('serialize') - ->willReturn(json_encode($buyRequest->getData())); - - $cached = true; - $this->product - ->expects($this->atLeastOnce()) - ->method('hasData') - ->will($this->returnValue($cached)); - $this->product - ->expects($this->atLeastOnce()) - ->method('getData') - ->will($this->returnValue([$associatedProduct])); - - $associatedPrepareResult->expects($this->at(1))->method('addCustomOption')->with( - 'product_type', - 'grouped', - $this->product - ); + /** + * Test prepareForCartAdvanced() method in full mode + * + * @dataProvider prepareForCartAdvancedWithProductsStrictTrueDataProvider + * @param array $subProducts + * @param array $buyRequest + * @param mixed $expectedResult + */ + public function testPrepareForCartAdvancedWithProductsStrictTrue( + array $subProducts, + array $buyRequest, + $expectedResult + ) { + $associatedProducts = $this->configureProduct($subProducts); + $buyRequestObject = new \Magento\Framework\DataObject(); + $buyRequestObject->setSuperGroup($buyRequest); + $associatedProductsById = []; + foreach ($associatedProducts as $associatedProduct) { + $associatedProductsById[$associatedProduct->getId()] = $associatedProduct; + } + if (is_array($expectedResult)) { + $expectedResultArray = $expectedResult; + $expectedResult = []; + foreach ($expectedResultArray as $id) { + $expectedResult[] = $associatedProductsById[$id]; + } + } $this->assertEquals( - [$associatedPrepareResult], - $this->_model->prepareForCartAdvanced($buyRequest, $this->product) + $expectedResult, + $this->_model->prepareForCartAdvanced($buyRequestObject, $this->product) ); } - public function testPrepareForCartAdvancedZeroQty() + /** + * Verify prepare for card with sold out option + * + * @return void + */ + public function testPrepareForCartAdvancedZeroQtyAndSoldOutOption(): void { $expectedMsg = "Please specify the quantity of product(s)."; - $associatedId = 9384; + $associatedId = 91; $associatedProduct = $this->createMock(\Magento\Catalog\Model\Product::class); - $associatedProduct->expects($this->atLeastOnce())->method('getId')->will($this->returnValue($associatedId)); - + $associatedProduct->expects($this->atLeastOnce())->method('getId')->will($this->returnValue(90)); + $associatedProduct->expects($this->once())->method('isSalable')->willReturn(true); $buyRequest = new \Magento\Framework\DataObject(); - $buyRequest->setSuperGroup([$associatedId => 0]); + $buyRequest->setSuperGroup([$associatedId => 90]); $cached = true; $this->product @@ -609,7 +697,12 @@ public function testPrepareForCartAdvancedZeroQty() $this->assertEquals($expectedMsg, $this->_model->prepareForCartAdvanced($buyRequest, $this->product)); } - public function testFlushAssociatedProductsCache() + /** + * Verify flush cache for associated products + * + * @return void + */ + public function testFlushAssociatedProductsCache(): void { $productMock = $this->createPartialMock(\Magento\Catalog\Model\Product::class, ['unsetData']); $productMock->expects($this->once()) @@ -618,4 +711,120 @@ public function testFlushAssociatedProductsCache() ->willReturnSelf(); $this->assertEquals($productMock, $this->_model->flushAssociatedProductsCache($productMock)); } + + /** + * @return array + */ + public function prepareForCartAdvancedWithProductsStrictTrueDataProvider(): array + { + return [ + [ + [ + [ + 'getId' => 1, + 'getQty' => 100, + 'isSalable' => true + ], + [ + 'getId' => 2, + 'getQty' => 200, + 'isSalable' => true + ] + ], + [ + 1 => 2, + 2 => 1, + ], + [1, 2] + ], + [ + [ + [ + 'getId' => 1, + 'getQty' => 100, + 'isSalable' => true + ], + [ + 'getId' => 2, + 'getQty' => 0, + 'isSalable' => false + ] + ], + [ + 1 => 2, + ], + [1] + ], + [ + [ + [ + 'getId' => 1, + 'getQty' => 0, + 'isSalable' => true + ], + [ + 'getId' => 2, + 'getQty' => 0, + 'isSalable' => false + ] + ], + [ + ], + 'Please specify the quantity of product(s).' + ], + [ + [ + [ + 'getId' => 1, + 'getQty' => 0, + 'isSalable' => false + ], + [ + 'getId' => 2, + 'getQty' => 0, + 'isSalable' => false + ] + ], + [ + ], + 'Please specify the quantity of product(s).' + ] + ]; + } + + /** + * Configure sub-products of grouped product + * + * @param array $subProducts + * @return array + */ + private function configureProduct(array $subProducts): array + { + $associatedProducts = []; + foreach ($subProducts as $data) { + $associatedProduct = $this->createMock(\Magento\Catalog\Model\Product::class); + foreach ($data as $method => $value) { + $associatedProduct->method($method)->willReturn($value); + } + $associatedProducts[] = $associatedProduct; + + $typeMock = $this->createPartialMock( + \Magento\Catalog\Model\Product\Type\AbstractType::class, + ['_prepareProduct', 'deleteTypeSpecificData'] + ); + $typeMock->method('_prepareProduct')->willReturn([$associatedProduct]); + $associatedProduct->method('getTypeInstance')->willReturn($typeMock); + } + $this->product + ->expects($this->atLeastOnce()) + ->method('hasData') + ->with('_cache_instance_associated_products') + ->willReturn(true); + $this->product + ->expects($this->atLeastOnce()) + ->method('getData') + ->with('_cache_instance_associated_products') + ->willReturn($associatedProducts); + return $associatedProducts; + } } diff --git a/app/code/Magento/GroupedProduct/Test/Unit/Model/Wishlist/Product/ItemTest.php b/app/code/Magento/GroupedProduct/Test/Unit/Model/Wishlist/Product/ItemTest.php new file mode 100644 index 0000000000000..1edf5e8ce2d95 --- /dev/null +++ b/app/code/Magento/GroupedProduct/Test/Unit/Model/Wishlist/Product/ItemTest.php @@ -0,0 +1,183 @@ +<?php +/** + * + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +namespace Magento\GroupedProduct\Test\Unit\Model\Wishlist\Product; + +use Magento\GroupedProduct\Model\Product\Type\Grouped as TypeGrouped; + +/** + * Unit test for Wishlist Item Plugin. + */ +class ItemTest extends \PHPUnit\Framework\TestCase +{ + /** + * @var \Magento\GroupedProduct\Model\Wishlist\Product\Item + */ + protected $model; + + /** + * @var \Magento\Catalog\Model\Product|\PHPUnit_Framework_MockObject_MockObject + */ + protected $productMock; + + /** + * @var \Magento\Wishlist\Model\Item|\PHPUnit_Framework_MockObject_MockObject + */ + protected $subjectMock; + + /** + * Init Mock Objects + */ + protected function setUp() + { + $this->subjectMock = $this->createPartialMock( + \Magento\Wishlist\Model\Item::class, + [ + 'getOptionsByCode', + 'getBuyRequest', + 'setOptions', + 'mergeBuyRequest', + 'getProduct' + ] + ); + + $this->productMock = $this->createPartialMock( + \Magento\Catalog\Model\Product::class, + [ + 'getId', + 'getTypeId', + 'getCustomOptions' + ] + ); + + $this->model = new \Magento\GroupedProduct\Model\Wishlist\Product\Item(); + } + + /** + * Test Before Represent Product method + */ + public function testBeforeRepresentProduct() + { + $testSimpleProdId = 34; + $prodInitQty = 2; + $prodQtyInWishlist = 3; + $resWishlistQty = $prodInitQty + $prodQtyInWishlist; + $superGroup = [ + 'super_group' => [ + 33 => "0", + 34 => 3, + 35 => "0" + ] + ]; + + $superGroupObj = new \Magento\Framework\DataObject($superGroup); + + $this->productMock->expects($this->once())->method('getId')->willReturn($testSimpleProdId); + $this->productMock->expects($this->once())->method('getTypeId') + ->willReturn(TypeGrouped::TYPE_CODE); + $this->productMock->expects($this->once())->method('getCustomOptions') + ->willReturn( + $this->getProductAssocOption($prodInitQty, $testSimpleProdId) + ); + + $wishlistItemProductMock = $this->createPartialMock( + \Magento\Catalog\Model\Product::class, + [ + 'getId', + ] + ); + $wishlistItemProductMock->expects($this->once())->method('getId')->willReturn($testSimpleProdId); + + $this->subjectMock->expects($this->once())->method('getProduct') + ->willReturn($wishlistItemProductMock); + $this->subjectMock->expects($this->once())->method('getOptionsByCode') + ->willReturn( + $this->getWishlistAssocOption($prodQtyInWishlist, $resWishlistQty, $testSimpleProdId) + ); + $this->subjectMock->expects($this->once())->method('getBuyRequest')->willReturn($superGroupObj); + + $this->model->beforeRepresentProduct($this->subjectMock, $this->productMock); + } + + /** + * Test Before Compare Options method with same keys + */ + public function testBeforeCompareOptionsSameKeys() + { + $options1 = ['associated_product_34' => 3]; + $options2 = ['associated_product_34' => 2]; + + $res = $this->model->beforeCompareOptions($this->subjectMock, $options1, $options2); + + $this->assertEquals([], $res[0]); + $this->assertEquals([], $res[1]); + } + + /** + * Test Before Compare Options method with diff keys + */ + public function testBeforeCompareOptionsDiffKeys() + { + $options1 = ['associated_product_1' => 3]; + $options2 = ['associated_product_34' => 2]; + + $res = $this->model->beforeCompareOptions($this->subjectMock, $options1, $options2); + + $this->assertEquals($options1, $res[0]); + $this->assertEquals($options2, $res[1]); + } + + /** + * Return mock array with wishlist options + * + * @param int $initVal + * @param int $resVal + * @param int $prodId + * @return array + */ + private function getWishlistAssocOption($initVal, $resVal, $prodId) + { + $items = []; + + $optionMock = $this->createPartialMock( + \Magento\Wishlist\Model\Item\Option::class, + [ + 'getValue', + ] + ); + $optionMock->expects($this->at(0))->method('getValue')->willReturn($initVal); + $optionMock->expects($this->at(1))->method('getValue')->willReturn($resVal); + + $items['associated_product_' . $prodId] = $optionMock; + + return $items; + } + + /** + * Return mock array with product options + * + * @param int $initVal + * @param int $prodId + * @return array + */ + private function getProductAssocOption($initVal, $prodId) + { + $items = []; + + $optionMock = $this->createPartialMock( + \Magento\Catalog\Model\Product\Configuration\Item\Option::class, + [ + 'getValue', + ] + ); + + $optionMock->expects($this->once())->method('getValue')->willReturn($initVal); + + $items['associated_product_' . $prodId] = $optionMock; + + return $items; + } +} diff --git a/app/code/Magento/GroupedProduct/composer.json b/app/code/Magento/GroupedProduct/composer.json index 68063c05ddf7b..3cb41387d2c6d 100644 --- a/app/code/Magento/GroupedProduct/composer.json +++ b/app/code/Magento/GroupedProduct/composer.json @@ -18,7 +18,8 @@ "magento/module-quote": "*", "magento/module-sales": "*", "magento/module-store": "*", - "magento/module-ui": "*" + "magento/module-ui": "*", + "magento/module-wishlist": "*" }, "suggest": { "magento/module-grouped-product-sample-data": "*" diff --git a/app/code/Magento/GroupedProduct/etc/frontend/di.xml b/app/code/Magento/GroupedProduct/etc/frontend/di.xml new file mode 100644 index 0000000000000..21ed87e91cb6c --- /dev/null +++ b/app/code/Magento/GroupedProduct/etc/frontend/di.xml @@ -0,0 +1,12 @@ +<?xml version="1.0"?> +<!-- +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +--> +<config xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="urn:magento:framework:ObjectManager/etc/config.xsd"> + <type name="Magento\Wishlist\Model\Item"> + <plugin name="groupedProductWishlistProcessor" type="Magento\GroupedProduct\Model\Wishlist\Product\Item" /> + </type> +</config> diff --git a/app/code/Magento/GroupedProduct/registration.php b/app/code/Magento/GroupedProduct/registration.php index 8fd2f11a2f628..c3a7c4c12b1c6 100644 --- a/app/code/Magento/GroupedProduct/registration.php +++ b/app/code/Magento/GroupedProduct/registration.php @@ -4,6 +4,6 @@ * See COPYING.txt for license details. */ -use \Magento\Framework\Component\ComponentRegistrar; +use Magento\Framework\Component\ComponentRegistrar; ComponentRegistrar::register(ComponentRegistrar::MODULE, 'Magento_GroupedProduct', __DIR__); diff --git a/app/code/Magento/GroupedProduct/view/frontend/layout/sales_email_order_creditmemo_renderers.xml b/app/code/Magento/GroupedProduct/view/frontend/layout/sales_email_order_creditmemo_renderers.xml index 77875ac70427c..2e64b3047cbbb 100644 --- a/app/code/Magento/GroupedProduct/view/frontend/layout/sales_email_order_creditmemo_renderers.xml +++ b/app/code/Magento/GroupedProduct/view/frontend/layout/sales_email_order_creditmemo_renderers.xml @@ -8,7 +8,7 @@ <page xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="urn:magento:framework:View/Layout/etc/page_configuration.xsd" label="Email Creditmemo Items List" design_abstraction="custom"> <body> <referenceBlock name="sales.email.order.creditmemo.renderers"> - <block class="Magento\GroupedProduct\Block\Order\Email\Items\Order\Grouped" name="sales.email.order.creditmemo.renderers.grouped" as="grouped" template="Magento_Sales::email/items/creditmemo/default.phtml"/> + <block class="Magento\GroupedProduct\Block\Order\Email\Items\CreditMemo\Grouped" name="sales.email.order.creditmemo.renderers.grouped" as="grouped" template="Magento_Sales::email/items/creditmemo/default.phtml"/> </referenceBlock> </body> </page> diff --git a/app/code/Magento/GroupedProduct/view/frontend/web/js/product-ids-resolver.js b/app/code/Magento/GroupedProduct/view/frontend/web/js/product-ids-resolver.js index e6294d8043a50..ea5f841d738a3 100644 --- a/app/code/Magento/GroupedProduct/view/frontend/web/js/product-ids-resolver.js +++ b/app/code/Magento/GroupedProduct/view/frontend/web/js/product-ids-resolver.js @@ -4,8 +4,9 @@ */ define([ 'jquery', - 'Magento_Catalog/js/product/view/product-ids' -], function ($, productIds) { + 'Magento_Catalog/js/product/view/product-ids', + 'Magento_Catalog/js/product/view/product-info' +], function ($, productIds, productInfo) { 'use strict'; /** @@ -18,6 +19,11 @@ define([ return function (config, element) { $(element).find('div[data-product-id]').each(function () { productIds.push($(this).data('productId').toString()); + productInfo.push( + { + 'id': $(this).data('productId').toString() + } + ); }); return productIds(); diff --git a/app/code/Magento/ImportExport/Controller/Adminhtml/Export/File/Delete.php b/app/code/Magento/ImportExport/Controller/Adminhtml/Export/File/Delete.php index 1e9d194653c9c..1b7fdc2881073 100644 --- a/app/code/Magento/ImportExport/Controller/Adminhtml/Export/File/Delete.php +++ b/app/code/Magento/ImportExport/Controller/Adminhtml/Export/File/Delete.php @@ -9,9 +9,7 @@ use Magento\Backend\App\Action; use Magento\Framework\App\Action\HttpPostActionInterface; -use Magento\Framework\Controller\ResultFactory; use Magento\Framework\Exception\FileSystemException; -use Magento\Framework\Exception\LocalizedException; use Magento\Framework\App\Filesystem\DirectoryList; use Magento\ImportExport\Controller\Adminhtml\Export as ExportController; use Magento\Framework\Filesystem; @@ -56,29 +54,33 @@ public function __construct( /** * Controller basic method implementation. * - * @return \Magento\Framework\App\ResponseInterface|\Magento\Framework\Controller\ResultInterface - * @throws LocalizedException + * @return \Magento\Framework\Controller\ResultInterface */ public function execute() { + /** @var \Magento\Framework\Controller\Result\Redirect $resultRedirect */ + $resultRedirect = $this->resultRedirectFactory->create(); + $resultRedirect->setPath('adminhtml/export/index'); + $fileName = $this->getRequest()->getParam('filename'); + if (empty($fileName) || preg_match('/\.\.(\\\|\/)/', $fileName) !== 0) { + $this->messageManager->addErrorMessage(__('Please provide valid export file name')); + + return $resultRedirect; + } try { - if (empty($fileName = $this->getRequest()->getParam('filename'))) { - throw new LocalizedException(__('Please provide export file name')); - } $directory = $this->filesystem->getDirectoryRead(DirectoryList::VAR_DIR); $path = $directory->getAbsolutePath() . 'export/' . $fileName; - if (!$directory->isFile($path)) { - throw new LocalizedException(__('Sorry, but the data is invalid or the file is not uploaded.')); + if ($directory->isFile($path)) { + $this->file->deleteFile($path); + $this->messageManager->addSuccessMessage(__('File %1 deleted', $fileName)); + } else { + $this->messageManager->addErrorMessage(__('%1 is not a valid file', $fileName)); } - - $this->file->deleteFile($path); - /** @var \Magento\Backend\Model\View\Result\Redirect $resultRedirect */ - $resultRedirect = $this->resultFactory->create(ResultFactory::TYPE_REDIRECT); - $resultRedirect->setPath('adminhtml/export/index'); - return $resultRedirect; } catch (FileSystemException $exception) { - throw new LocalizedException(__('There are no export file with such name %1', $fileName)); + $this->messageManager->addErrorMessage($exception->getMessage()); } + + return $resultRedirect; } } diff --git a/app/code/Magento/ImportExport/Controller/Adminhtml/Export/File/Download.php b/app/code/Magento/ImportExport/Controller/Adminhtml/Export/File/Download.php index 8dbd9a0ae44ba..4107e19860328 100644 --- a/app/code/Magento/ImportExport/Controller/Adminhtml/Export/File/Download.php +++ b/app/code/Magento/ImportExport/Controller/Adminhtml/Export/File/Download.php @@ -10,7 +10,6 @@ use Magento\Backend\App\Action; use Magento\Framework\App\Action\HttpGetActionInterface; use Magento\Framework\App\Response\Http\FileFactory; -use Magento\Framework\Exception\LocalizedException; use Magento\Framework\App\Filesystem\DirectoryList; use Magento\ImportExport\Controller\Adminhtml\Export as ExportController; use Magento\Framework\Filesystem; @@ -55,12 +54,17 @@ public function __construct( * Controller basic method implementation. * * @return \Magento\Framework\App\ResponseInterface - * @throws LocalizedException */ public function execute() { - if (empty($fileName = $this->getRequest()->getParam('filename'))) { - throw new LocalizedException(__('Please provide export file name')); + /** @var \Magento\Framework\Controller\Result\Redirect $resultRedirect */ + $resultRedirect = $this->resultRedirectFactory->create(); + $resultRedirect->setPath('adminhtml/export/index'); + $fileName = $this->getRequest()->getParam('filename'); + if (empty($fileName) || preg_match('/\.\.(\\\|\/)/', $fileName) !== 0) { + $this->messageManager->addErrorMessage(__('Please provide valid export file name')); + + return $resultRedirect; } try { $path = 'export/' . $fileName; @@ -72,8 +76,11 @@ public function execute() DirectoryList::VAR_DIR ); } - } catch (LocalizedException | \Exception $exception) { - throw new LocalizedException(__('There are no export file with such name %1', $fileName)); + $this->messageManager->addErrorMessage(__('%1 is not a valid file', $fileName)); + } catch (\Exception $exception) { + $this->messageManager->addErrorMessage($exception->getMessage()); } + + return $resultRedirect; } } diff --git a/app/code/Magento/ImportExport/Model/Export/Adapter/Csv.php b/app/code/Magento/ImportExport/Model/Export/Adapter/Csv.php index 25081b5797c25..09b17371ae4e8 100644 --- a/app/code/Magento/ImportExport/Model/Export/Adapter/Csv.php +++ b/app/code/Magento/ImportExport/Model/Export/Adapter/Csv.php @@ -5,13 +5,16 @@ */ namespace Magento\ImportExport\Model\Export\Adapter; +use Magento\Framework\Exception\LocalizedException; +use Magento\Framework\Filesystem\File\Write; + /** * Export adapter csv. * * @api * @since 100.0.2 */ -class Csv extends \Magento\ImportExport\Model\Export\Adapter\AbstractAdapter +class Csv extends AbstractAdapter { /** * Field delimiter. @@ -30,21 +33,20 @@ class Csv extends \Magento\ImportExport\Model\Export\Adapter\AbstractAdapter /** * Source file handler. * - * @var \Magento\Framework\Filesystem\File\Write + * @var Write */ protected $_fileHandler; /** - * {@inheritdoc } + * Object destructor */ - public function __construct(\Magento\Framework\Filesystem $filesystem, $destination = null) + public function __destruct() { - register_shutdown_function([$this, 'destruct']); - parent::__construct($filesystem, $destination); + $this->destruct(); } /** - * Object destructor. + * Clean cached values * * @return void */ @@ -52,6 +54,7 @@ public function destruct() { if (is_object($this->_fileHandler)) { $this->_fileHandler->close(); + $this->_directoryHandle->delete($this->_destination); } } @@ -96,7 +99,7 @@ public function getFileExtension() public function setHeaderCols(array $headerColumns) { if (null !== $this->_headerCols) { - throw new \Magento\Framework\Exception\LocalizedException(__('The header column names are already set.')); + throw new LocalizedException(__('The header column names are already set.')); } if ($headerColumns) { foreach ($headerColumns as $columnName) { diff --git a/app/code/Magento/ImportExport/Model/Export/Config.php b/app/code/Magento/ImportExport/Model/Export/Config.php index 9e7cd6a29dc9a..d580af6f92baf 100644 --- a/app/code/Magento/ImportExport/Model/Export/Config.php +++ b/app/code/Magento/ImportExport/Model/Export/Config.php @@ -15,10 +15,11 @@ class Config extends \Magento\Framework\Config\Data implements \Magento\ImportEx /** * Constructor * - * @param Config\Reader $reader + * @param \Magento\ImportExport\Model\Export\Config\Reader $reader * @param \Magento\Framework\Config\CacheInterface $cache - * @param string|null $cacheId - * @param SerializerInterface|null $serializer + * @param string $cacheId + * @param SerializerInterface $serializer + * phpcs:disable Generic.CodeAnalysis.UselessOverridingMethod */ public function __construct( \Magento\ImportExport\Model\Export\Config\Reader $reader, @@ -48,7 +49,7 @@ public function getEntities() public function getEntityTypes($entity) { $entities = $this->getEntities(); - return isset($entities[$entity]) ? $entities[$entity]['types'] : []; + return $entities[$entity]['types'] ?? []; } /** diff --git a/app/code/Magento/ImportExport/Model/Import/Entity/AbstractEav.php b/app/code/Magento/ImportExport/Model/Import/Entity/AbstractEav.php index 82c4478874844..7cd700daf05f9 100644 --- a/app/code/Magento/ImportExport/Model/Import/Entity/AbstractEav.php +++ b/app/code/Magento/ImportExport/Model/Import/Entity/AbstractEav.php @@ -10,8 +10,8 @@ /** * Import EAV entity abstract model * + * phpcs:disable Magento2.Classes.AbstractApi * @api - * * @SuppressWarnings(PHPMD.CouplingBetweenObjects) * @since 100.0.2 */ @@ -19,6 +19,8 @@ abstract class AbstractEav extends \Magento\ImportExport\Model\Import\AbstractEn { /** * Attribute collection name + * + * Name of collection class */ const ATTRIBUTE_COLLECTION_NAME = \Magento\Framework\Data\Collection::class; @@ -129,7 +131,7 @@ public function __construct( public function getWebsiteId($websiteCode) { if (isset($this->_websiteCodeToId[$websiteCode])) { - return $this->_websiteCodeToId[$websiteCode]; + return (int) $this->_websiteCodeToId[$websiteCode]; } return false; @@ -232,6 +234,7 @@ public function getAttributeOptions( } } } + // phpcs:ignore Magento2.CodeAnalysis.EmptyBlock } catch (\Exception $e) { // ignore exceptions connected with source models } diff --git a/app/code/Magento/ImportExport/Model/Import/ErrorProcessing/ProcessingErrorAggregator.php b/app/code/Magento/ImportExport/Model/Import/ErrorProcessing/ProcessingErrorAggregator.php index 028bf2c464d4b..5ea6227231543 100644 --- a/app/code/Magento/ImportExport/Model/Import/ErrorProcessing/ProcessingErrorAggregator.php +++ b/app/code/Magento/ImportExport/Model/Import/ErrorProcessing/ProcessingErrorAggregator.php @@ -237,16 +237,12 @@ public function hasFatalExceptions() */ public function getAllErrors() { - $result = []; - if (empty($this->items)) { - return $result; - } - - foreach (array_values($this->items['rows']) as $errors) { - $result = array_merge($result, $errors); + if (empty($this->items) || empty($this->items['rows'])) { + return []; } - return $result; + $errors = array_values($this->items['rows']); + return array_merge(...$errors); } /** @@ -257,14 +253,14 @@ public function getAllErrors() */ public function getErrorsByCode(array $codes) { - $result = []; + $result = [[]]; foreach ($codes as $code) { if (isset($this->items['codes'][$code])) { - $result = array_merge($result, $this->items['codes'][$code]); + $result[] = $this->items['codes'][$code]; } } - return $result; + return array_merge(...$result); } /** diff --git a/app/code/Magento/ImportExport/Test/Mftf/Section/AdminExportAttributeSection.xml b/app/code/Magento/ImportExport/Test/Mftf/Section/AdminExportAttributeSection.xml index f9b07a59c8763..d6970feb4bb36 100644 --- a/app/code/Magento/ImportExport/Test/Mftf/Section/AdminExportAttributeSection.xml +++ b/app/code/Magento/ImportExport/Test/Mftf/Section/AdminExportAttributeSection.xml @@ -18,5 +18,9 @@ <element name="download" type="button" selector="//tr[@data-repeat-index='{{var}}']//a[text()='Download']" parameterized="true" timeout="30"/> <element name="delete" type="button" selector="//tr[@data-repeat-index='{{var}}']//a[text()='Delete']" parameterized="true" timeout="30"/> <element name="exportFileNameByPosition" type="text" selector="[data-role='grid'] tr[data-repeat-index='{{position}}'] div.data-grid-cell-content" parameterized="true"/> + <element name="fileName" type="text" selector="//div[@class='data-grid-cell-content'][text()='{{fileName}}']" parameterized="true"/> + <element name="selectByFileName" type="button" selector="//div[@class='data-grid-cell-content'][text()='{{fileName}}']/../..//button[@class='action-select']" parameterized="true"/> + <element name="downloadByFileName" type="button" selector="//div[@class='data-grid-cell-content'][text()='{{fileName}}']/../..//a[text()='Download']" parameterized="true" timeout="30"/> + <element name="deleteByFileName" type="button" selector="//div[@class='data-grid-cell-content'][text()='{{fileName}}']/../..//a[text()='Delete']" parameterized="true" timeout="30"/> </section> </sections> diff --git a/app/code/Magento/ImportExport/Test/Mftf/Section/AdminImportMainSection.xml b/app/code/Magento/ImportExport/Test/Mftf/Section/AdminImportMainSection.xml index ba1deeebbd89a..0f647aa027c23 100644 --- a/app/code/Magento/ImportExport/Test/Mftf/Section/AdminImportMainSection.xml +++ b/app/code/Magento/ImportExport/Test/Mftf/Section/AdminImportMainSection.xml @@ -17,5 +17,6 @@ <element name="messageError" type="text" selector=".messages div.message-error"/> <element name="validationStrategy" type="select" selector="#basic_behaviorvalidation_strategy"/> <element name="allowedErrorsCount" type="input" selector="#basic_behavior_allowed_error_count"/> + <element name="importImagesFileDirNote" type="input" selector="#import_images_file_dir-note"/> </section> </sections> diff --git a/app/code/Magento/ImportExport/Test/Mftf/Test/AdminCheckDoubleImportOfProductsTest.xml b/app/code/Magento/ImportExport/Test/Mftf/Test/AdminCheckDoubleImportOfProductsTest.xml deleted file mode 100644 index e710d9add4c21..0000000000000 --- a/app/code/Magento/ImportExport/Test/Mftf/Test/AdminCheckDoubleImportOfProductsTest.xml +++ /dev/null @@ -1,73 +0,0 @@ -<?xml version="1.0" encoding="UTF-8"?> -<!-- - /** - * Copyright © Magento, Inc. All rights reserved. - * See COPYING.txt for license details. - */ ---> - -<tests xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" - xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/testSchema.xsd"> - <test name="AdminCheckDoubleImportOfProductsTest"> - <annotations> - <description value="Checking double Import of products CSV file"/> - <stories value="Import Products"/> - <features value="Import/Export"/> - <title value="Admin check double import of products test"/> - <severity value="CRITICAL"/> - <testCaseId value="MC-6311"/> - <group value="importExport"/> - </annotations> - <before> - <!-- Login as admin --> - <actionGroup ref="LoginAsAdmin" stepKey="loginAsAdmin"/> - - <!-- Create additional store views --> - <actionGroup ref="AdminCreateStoreViewActionGroup" stepKey="createFirstStoreView"> - <argument name="customStore" value="secondStoreView"/> - </actionGroup> - <actionGroup ref="AdminCreateStoreViewActionGroup" stepKey="createSecondStoreView"> - <argument name="customStore" value="thirdStoreView"/> - </actionGroup> - </before> - <after> - <!-- Delete all imported products --> - <actionGroup ref="AdminOpenProductIndexPageActionGroup" stepKey="openProductIndexPage"/> - <actionGroup ref="ClearFiltersAdminDataGridActionGroup" stepKey="clearGridFilter"/> - <actionGroup ref="AdminDataGridSelectPerPageActionGroup" stepKey="selectNumberOfProductsPerPage"> - <argument name="perPage" value="100"/> - </actionGroup> - <actionGroup ref="DeleteProductsIfTheyExistActionGroup" stepKey="deleteAllProducts"/> - - <!-- Delete additional store views --> - <actionGroup ref="AdminDeleteStoreViewActionGroup" stepKey="deleteFirstStoreView"> - <argument name="customStore" value="secondStoreView"/> - </actionGroup> - <actionGroup ref="AdminDeleteStoreViewActionGroup" stepKey="deleteSecondStoreView"> - <argument name="customStore" value="thirdStoreView"/> - </actionGroup> - - <!-- Delete category --> - <actionGroup ref="DeleteCategoryActionGroup" stepKey="deleteCategory"> - <argument name="categoryEntity" value="Gear"/> - </actionGroup> - - <!-- Log out --> - <actionGroup ref="logout" stepKey="logout"/> - </after> - - <!-- Import products with add/update behavior --> - <actionGroup ref="AdminImportProductsActionGroup" stepKey="adminImportProductsFirstTime"> - <argument name="behavior" value="Add/Update"/> - <argument name="importFile" value="prepared-for-sample-data.csv"/> - <argument name="importNoticeMessage" value="Created: 100, Updated: 3, Deleted: 0"/> - </actionGroup> - - <!-- Import products with add/update behavior again --> - <actionGroup ref="AdminImportProductsActionGroup" stepKey="adminImportProductsSecondTime"> - <argument name="behavior" value="Add/Update"/> - <argument name="importFile" value="prepared-for-sample-data.csv"/> - <argument name="importNoticeMessage" value="Created: 0, Updated: 300, Deleted: 0"/> - </actionGroup> - </test> -</tests> diff --git a/app/code/Magento/ImportExport/Test/Mftf/Test/AdminCheckThatSomeAttributesChangedValueToEmptyAfterImportTest.xml b/app/code/Magento/ImportExport/Test/Mftf/Test/AdminCheckThatSomeAttributesChangedValueToEmptyAfterImportTest.xml index 6a2f6ca60acf4..a45783767e6a2 100644 --- a/app/code/Magento/ImportExport/Test/Mftf/Test/AdminCheckThatSomeAttributesChangedValueToEmptyAfterImportTest.xml +++ b/app/code/Magento/ImportExport/Test/Mftf/Test/AdminCheckThatSomeAttributesChangedValueToEmptyAfterImportTest.xml @@ -20,7 +20,7 @@ <group value="importExport"/> </annotations> <before> - <actionGroup ref="LoginAsAdmin" stepKey="loginAsAdmin"/> + <actionGroup ref="AdminLoginActionGroup" stepKey="loginAsAdmin"/> <actionGroup ref="AdminOpenProductIndexPageActionGroup" stepKey="navigateToProductIndexPage"/> <actionGroup ref="ResetProductGridToDefaultViewActionGroup" stepKey="resetProductGridToDefaultView"/> <actionGroup ref="DeleteProductsIfTheyExistActionGroup" stepKey="deleteAllProducts"/> @@ -41,7 +41,10 @@ <deleteData createDataKey="createCategory" stepKey="deleteCategory"/> <!--Delete attribute--> <deleteData createDataKey="productAttribute" stepKey="deleteProductAttribute"/> - <actionGroup ref="logout" stepKey="logoutFromAdmin"/> + <actionGroup ref="AdminLogoutActionGroup" stepKey="logoutFromAdmin"/> + + <!-- Reindex invalidated indices after product attribute has been created/deleted --> + <magentoCron groups="index" stepKey="reindexInvalidatedIndices"/> </after> <!--Create product--> <actionGroup ref="GoToSpecifiedCreateProductPageActionGroup" stepKey="openProductFillForm"/> diff --git a/app/code/Magento/ImportExport/Test/Mftf/Test/AdminExportPageNavigateMenuTest.xml b/app/code/Magento/ImportExport/Test/Mftf/Test/AdminExportPageNavigateMenuTest.xml index e8fb12ca521c2..98df7b8d8900e 100644 --- a/app/code/Magento/ImportExport/Test/Mftf/Test/AdminExportPageNavigateMenuTest.xml +++ b/app/code/Magento/ImportExport/Test/Mftf/Test/AdminExportPageNavigateMenuTest.xml @@ -20,10 +20,10 @@ <group value="mtf_migrated"/> </annotations> <before> - <actionGroup ref="LoginAsAdmin" stepKey="LoginAsAdmin"/> + <actionGroup ref="AdminLoginActionGroup" stepKey="LoginAsAdmin"/> </before> <after> - <actionGroup ref="logout" stepKey="logout"/> + <actionGroup ref="AdminLogoutActionGroup" stepKey="logout"/> </after> <actionGroup ref="AdminNavigateMenuActionGroup" stepKey="navigateToExportPage"> <argument name="menuUiId" value="{{AdminMenuSystem.dataUiId}}"/> diff --git a/app/code/Magento/ImportExport/Test/Mftf/Test/AdminExportPagerGridTest.xml b/app/code/Magento/ImportExport/Test/Mftf/Test/AdminExportPagerGridTest.xml index b203a4d11a2d2..0c5bf2d3fcee3 100644 --- a/app/code/Magento/ImportExport/Test/Mftf/Test/AdminExportPagerGridTest.xml +++ b/app/code/Magento/ImportExport/Test/Mftf/Test/AdminExportPagerGridTest.xml @@ -18,10 +18,10 @@ <group value="importExport"/> </annotations> <before> - <actionGroup ref="LoginAsAdmin" stepKey="LoginAsAdmin"/> + <actionGroup ref="AdminLoginActionGroup" stepKey="LoginAsAdmin"/> </before> <after> - <actionGroup ref="logout" stepKey="logout"/> + <actionGroup ref="AdminLogoutActionGroup" stepKey="logout"/> </after> <actionGroup ref="AdminNavigateToExportPageActionGroup" stepKey="navigateToExportPage"/> <actionGroup ref="AdminAssertVisiblePagerActionGroup" stepKey="seeGridPager"/> diff --git a/app/code/Magento/ImportExport/Test/Mftf/Test/AdminImagesFileDirectoryCorrectExplanationTest.xml b/app/code/Magento/ImportExport/Test/Mftf/Test/AdminImagesFileDirectoryCorrectExplanationTest.xml new file mode 100644 index 0000000000000..58ff334ee9f93 --- /dev/null +++ b/app/code/Magento/ImportExport/Test/Mftf/Test/AdminImagesFileDirectoryCorrectExplanationTest.xml @@ -0,0 +1,33 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + /** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +--> + +<tests xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/testSchema.xsd"> + <test name="AdminImagesFileDirectoryCorrectExplanationTest"> + <annotations> + <features value="Import/Export"/> + <stories value="Import CSV file"/> + <title value="Images file directory correct explanation test"/> + <description value="Check correct explanation for the 'Images File Directory' option."/> + <severity value="MINOR"/> + <group value="importExport"/> + </annotations> + <before> + <actionGroup ref="AdminLoginActionGroup" stepKey="loginAsAdmin"/> + </before> + <after> + <actionGroup ref="AdminLogoutActionGroup" stepKey="logoutFromAdmin"/> + </after> + <amOnPage url="{{AdminImportIndexPage.url}}" stepKey="goToImportIndexPage"/> + <waitForPageLoad stepKey="adminImportMainSectionLoad"/> + <selectOption selector="{{AdminImportMainSection.entityType}}" userInput="Products" stepKey="selectProductsOption"/> + <waitForElementVisible selector="{{AdminImportMainSection.importBehavior}}" stepKey="waitForImportBehaviorElementVisible"/> + + <see selector="{{AdminImportMainSection.importImagesFileDirNote}}" userInput='For Type "Local Server" use relative path to <Magento root directory>/var/import/images, e.g. ' stepKey="seeCorrectExplanation"/> + </test> +</tests> diff --git a/app/code/Magento/ImportExport/Test/Mftf/Test/AdminImportCSVWithSpecialCharactersTest.xml b/app/code/Magento/ImportExport/Test/Mftf/Test/AdminImportCSVWithSpecialCharactersTest.xml index 38c1a09dc534c..111ba187c6f26 100644 --- a/app/code/Magento/ImportExport/Test/Mftf/Test/AdminImportCSVWithSpecialCharactersTest.xml +++ b/app/code/Magento/ImportExport/Test/Mftf/Test/AdminImportCSVWithSpecialCharactersTest.xml @@ -20,10 +20,10 @@ <group value="importExport"/> </annotations> <before> - <actionGroup ref="LoginAsAdmin" stepKey="loginAsAdmin"/> + <actionGroup ref="AdminLoginActionGroup" stepKey="loginAsAdmin"/> </before> <after> - <actionGroup ref="logout" stepKey="logoutFromAdmin"/> + <actionGroup ref="AdminLogoutActionGroup" stepKey="logoutFromAdmin"/> </after> <actionGroup ref="AdminCheckDataForImportProductActionGroup" stepKey="adminImportProducts"> <argument name="behavior" value="Add/Update"/> diff --git a/app/code/Magento/ImportExport/Test/Mftf/Test/AdminImportProductsWithAddUpdateBehaviorTest.xml b/app/code/Magento/ImportExport/Test/Mftf/Test/AdminImportProductsWithAddUpdateBehaviorTest.xml index 796732d572290..583ceb0b2496f 100644 --- a/app/code/Magento/ImportExport/Test/Mftf/Test/AdminImportProductsWithAddUpdateBehaviorTest.xml +++ b/app/code/Magento/ImportExport/Test/Mftf/Test/AdminImportProductsWithAddUpdateBehaviorTest.xml @@ -14,7 +14,7 @@ <stories value="Import Products"/> <features value="Import/Export"/> <title value="Verify Magento native import products with add/update behavior."/> - <severity value="CRITICAL"/> + <severity value="BLOCKER"/> <testCaseId value="MC-14077"/> <group value="importExport"/> <group value="mtf_migrated"/> @@ -29,7 +29,7 @@ </createData> <!-- Login as Admin --> - <actionGroup ref="LoginAsAdmin" stepKey="loginAsAdmin"/> + <actionGroup ref="AdminLoginActionGroup" stepKey="loginAsAdmin"/> <!-- Create Website --> <actionGroup ref="AdminCreateWebsiteActionGroup" stepKey="AdminCreateWebsite"> @@ -65,7 +65,7 @@ </actionGroup> <!-- Logout --> - <actionGroup ref="logout" stepKey="logoutFromAdmin"/> + <actionGroup ref="AdminLogoutActionGroup" stepKey="logoutFromAdmin"/> </after> <!-- Import products with add/update behavior --> diff --git a/app/code/Magento/ImportExport/Test/Mftf/Test/AdminImportProductsWithDeleteBehaviorTest.xml b/app/code/Magento/ImportExport/Test/Mftf/Test/AdminImportProductsWithDeleteBehaviorTest.xml index 7ec48a3a7e8fd..503037401b9f7 100644 --- a/app/code/Magento/ImportExport/Test/Mftf/Test/AdminImportProductsWithDeleteBehaviorTest.xml +++ b/app/code/Magento/ImportExport/Test/Mftf/Test/AdminImportProductsWithDeleteBehaviorTest.xml @@ -14,7 +14,7 @@ <stories value="Verify Magento native import products with delete behavior."/> <features value="Import/Export"/> <title value="Verify Magento native import products with delete behavior."/> - <severity value="CRITICAL"/> + <severity value="BLOCKER"/> <testCaseId value="MAGETWO-30587"/> <group value="importExport"/> </annotations> @@ -34,11 +34,11 @@ <field key="name">Api Downloadable Product for Test</field> <field key="sku">Api Downloadable Product for Test</field> </createData> - <actionGroup ref="LoginAsAdmin" stepKey="loginAsAdmin"/> + <actionGroup ref="AdminLoginActionGroup" stepKey="loginAsAdmin"/> </before> <after> <actionGroup ref="AdminClearFiltersActionGroup" stepKey="clearProductFilters"/> - <actionGroup ref="logout" stepKey="logoutFromAdmin"/> + <actionGroup ref="AdminLogoutActionGroup" stepKey="logoutFromAdmin"/> </after> <amOnPage url="{{AdminImportIndexPage.url}}" stepKey="goToImportIndexPage"/> <selectOption selector="{{AdminImportMainSection.entityType}}" userInput="Products" stepKey="selectProductsOption"/> diff --git a/app/code/Magento/ImportExport/Test/Mftf/Test/AdminImportProductsWithErrorEntriesTest.xml b/app/code/Magento/ImportExport/Test/Mftf/Test/AdminImportProductsWithErrorEntriesTest.xml index e3065f005218b..3c208eabc9558 100644 --- a/app/code/Magento/ImportExport/Test/Mftf/Test/AdminImportProductsWithErrorEntriesTest.xml +++ b/app/code/Magento/ImportExport/Test/Mftf/Test/AdminImportProductsWithErrorEntriesTest.xml @@ -19,7 +19,7 @@ </annotations> <before> <!--Login to Admin Page--> - <actionGroup ref="LoginAsAdmin" stepKey="loginAsAdmin"/> + <actionGroup ref="AdminLoginActionGroup" stepKey="loginAsAdmin"/> </before> <after> <!--Clear products grid filters--> @@ -27,7 +27,7 @@ <!--Delete all imported products--> <actionGroup ref="DeleteProductsIfTheyExistActionGroup" stepKey="deleteAllProducts"/> <!--Logout from Admin page--> - <actionGroup ref="logout" stepKey="logoutFromAdminPage"/> + <actionGroup ref="AdminLogoutActionGroup" stepKey="logoutFromAdminPage"/> </after> <!--Import products with "Skip error entries"--> diff --git a/app/code/Magento/ImportExport/Test/Mftf/Test/AdminProductImportCSVFileCorrectDifferentFilesTest.xml b/app/code/Magento/ImportExport/Test/Mftf/Test/AdminProductImportCSVFileCorrectDifferentFilesTest.xml index 56c1c43bc28d2..209bb3b48834f 100644 --- a/app/code/Magento/ImportExport/Test/Mftf/Test/AdminProductImportCSVFileCorrectDifferentFilesTest.xml +++ b/app/code/Magento/ImportExport/Test/Mftf/Test/AdminProductImportCSVFileCorrectDifferentFilesTest.xml @@ -14,18 +14,18 @@ <features value="Import/Export"/> <stories value="Product Import"/> <title value="Product import from CSV file correct from different files."/> - <severity value="MAJOR"/> + <severity value="BLOCKER"/> <testCaseId value="MC-17104"/> <useCaseId value="MAGETWO-70803"/> <group value="importExport"/> </annotations> <before> <!--Login as Admin--> - <actionGroup ref="LoginAsAdmin" stepKey="loginAsAdmin"/> + <actionGroup ref="AdminLoginActionGroup" stepKey="loginAsAdmin"/> </before> <after> <!--Logout from Admin--> - <actionGroup ref="logout" stepKey="logoutFromAdmin"/> + <actionGroup ref="AdminLogoutActionGroup" stepKey="logoutFromAdmin"/> </after> <!--Check data products with add/update behavior--> <actionGroup ref="AdminCheckDataForImportProductActionGroup" stepKey="adminImportProducts"> diff --git a/app/code/Magento/ImportExport/Test/Mftf/Test/AdminProductVisibilityDifferentStoreViewsAfterImportTest.xml b/app/code/Magento/ImportExport/Test/Mftf/Test/AdminProductVisibilityDifferentStoreViewsAfterImportTest.xml index f0d721075fdfd..b9b61a2ae28ee 100644 --- a/app/code/Magento/ImportExport/Test/Mftf/Test/AdminProductVisibilityDifferentStoreViewsAfterImportTest.xml +++ b/app/code/Magento/ImportExport/Test/Mftf/Test/AdminProductVisibilityDifferentStoreViewsAfterImportTest.xml @@ -14,13 +14,13 @@ <stories value="Import Products"/> <title value="Checking product visibility in different store views after product importing"/> <description value="Checking product visibility in different store views after product importing"/> - <severity value="MAJOR"/> + <severity value="BLOCKER"/> <testCaseId value="MC-6406"/> <useCaseId value="MAGETWO-59265"/> <group value="importExport"/> </annotations> <before> - <actionGroup ref="LoginAsAdmin" stepKey="loginAsAdmin"/> + <actionGroup ref="AdminLoginActionGroup" stepKey="loginAsAdmin"/> <!--Create English and Chinese store views--> <actionGroup ref="AdminCreateStoreViewActionGroup" stepKey="createEnglishStoreView"> <argument name="StoreGroup" value="_defaultStoreGroup"/> @@ -46,7 +46,7 @@ <actionGroup ref="AdminDeleteStoreViewActionGroup" stepKey="deleteChineseStoreView"> <argument name="customStore" value="storeViewChinese"/> </actionGroup> - <actionGroup ref="logout" stepKey="logoutFromAdmin"/> + <actionGroup ref="AdminLogoutActionGroup" stepKey="logoutFromAdmin"/> </after> <!--Import products from file--> <actionGroup ref="AdminImportProductsActionGroup" stepKey="importProducts"> diff --git a/app/code/Magento/ImportExport/Test/Mftf/Test/AdminSystemImportNavigateMenuTest.xml b/app/code/Magento/ImportExport/Test/Mftf/Test/AdminSystemImportNavigateMenuTest.xml index 9913933d857a8..69fb7598848aa 100644 --- a/app/code/Magento/ImportExport/Test/Mftf/Test/AdminSystemImportNavigateMenuTest.xml +++ b/app/code/Magento/ImportExport/Test/Mftf/Test/AdminSystemImportNavigateMenuTest.xml @@ -20,10 +20,10 @@ <group value="mtf_migrated"/> </annotations> <before> - <actionGroup ref="LoginAsAdmin" stepKey="LoginAsAdmin"/> + <actionGroup ref="AdminLoginActionGroup" stepKey="LoginAsAdmin"/> </before> <after> - <actionGroup ref="logout" stepKey="logout"/> + <actionGroup ref="AdminLogoutActionGroup" stepKey="logout"/> </after> <actionGroup ref="AdminNavigateMenuActionGroup" stepKey="navigateToImportPage"> <argument name="menuUiId" value="{{AdminMenuSystem.dataUiId}}"/> diff --git a/app/code/Magento/ImportExport/Test/Mftf/Test/AdminURLKeyWorksWhenUpdatingProductThroughImportingCSVTest.xml b/app/code/Magento/ImportExport/Test/Mftf/Test/AdminURLKeyWorksWhenUpdatingProductThroughImportingCSVTest.xml index 8d56d9d8dad9d..99622caf0697e 100644 --- a/app/code/Magento/ImportExport/Test/Mftf/Test/AdminURLKeyWorksWhenUpdatingProductThroughImportingCSVTest.xml +++ b/app/code/Magento/ImportExport/Test/Mftf/Test/AdminURLKeyWorksWhenUpdatingProductThroughImportingCSVTest.xml @@ -14,7 +14,7 @@ <stories value="Import Products"/> <title value="Check that new URL Key works after updating a product through importing CSV file"/> <description value="Check that new URL Key works after updating a product through importing CSV file"/> - <severity value="MAJOR"/> + <severity value="BLOCKER"/> <testCaseId value="MC-6317"/> <useCaseId value="MAGETWO-91544"/> <group value="importExport"/> @@ -25,13 +25,13 @@ <createData entity="SimpleProductBeforeUpdate" stepKey="createProduct"> <requiredEntity createDataKey="createCategory"/> </createData> - <actionGroup ref="LoginAsAdmin" stepKey="loginAsAdmin"/> + <actionGroup ref="AdminLoginActionGroup" stepKey="loginAsAdmin"/> </before> <after> <!--Delete created data--> <deleteData createDataKey="createProduct" stepKey="deleteProduct"/> <deleteData createDataKey="createCategory" stepKey="deleteCategory"/> - <actionGroup ref="logout" stepKey="logoutFromAdmin"/> + <actionGroup ref="AdminLogoutActionGroup" stepKey="logoutFromAdmin"/> </after> <!--Import product from CSV file--> <actionGroup ref="AdminImportProductsActionGroup" stepKey="importProduct"> diff --git a/app/code/Magento/ImportExport/Test/Unit/Controller/Adminhtml/Export/File/DeleteTest.php b/app/code/Magento/ImportExport/Test/Unit/Controller/Adminhtml/Export/File/DeleteTest.php new file mode 100644 index 0000000000000..ef40651f95be9 --- /dev/null +++ b/app/code/Magento/ImportExport/Test/Unit/Controller/Adminhtml/Export/File/DeleteTest.php @@ -0,0 +1,198 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +declare(strict_types=1); + +namespace Magento\ImportExport\Test\Unit\Controller\Adminhtml\Export\File; + +use Magento\Backend\App\Action\Context; +use Magento\Backend\Model\View\Result\Redirect; +use Magento\Framework\App\Request\Http; +use Magento\Framework\Controller\Result\Raw; +use Magento\Framework\Controller\Result\RedirectFactory; +use Magento\Framework\Filesystem; +use Magento\Framework\Filesystem\Directory\ReadInterface; +use Magento\Framework\Filesystem\DriverInterface; +use Magento\Framework\Message\ManagerInterface; +use Magento\Framework\TestFramework\Unit\Helper\ObjectManager as ObjectManagerHelper; +use Magento\ImportExport\Controller\Adminhtml\Export\File\Delete; +use PHPUnit\Framework\MockObject\MockObject; +use PHPUnit\Framework\TestCase; + +/** + * @SuppressWarnings(PHPMD.CouplingBetweenObjects) + */ +class DeleteTest extends TestCase +{ + /** + * @var Context|MockObject + */ + private $contextMock; + + /** + * @var ObjectManagerHelper + */ + private $objectManagerHelper; + + /** + * @var Http|MockObject + */ + private $requestMock; + + /** + * @var Raw|MockObject + */ + private $redirectMock; + + /** + * @var RedirectFactory|MockObject + */ + private $resultRedirectFactoryMock; + + /** + * @var Filesystem|MockObject + */ + private $fileSystemMock; + + /** + * @var DriverInterface|MockObject + */ + private $fileMock; + + /** + * @var Delete|MockObject + */ + private $deleteControllerMock; + + /** + * @var ManagerInterface|MockObject + */ + private $messageManagerMock; + + /** + * @var ReadInterface|MockObject + */ + private $directoryMock; + + /** + * Set up + */ + protected function setUp() + { + $this->requestMock = $this->getMockBuilder(Http::class) + ->disableOriginalConstructor() + ->getMock(); + + $this->fileSystemMock = $this->getMockBuilder(Filesystem::class) + ->disableOriginalConstructor() + ->getMock(); + + $this->directoryMock = $this->getMockBuilder(ReadInterface::class) + ->disableOriginalConstructor() + ->getMock(); + + $this->fileMock = $this->getMockBuilder(DriverInterface::class) + ->disableOriginalConstructor() + ->getMock(); + + $this->messageManagerMock = $this->getMockBuilder(ManagerInterface::class) + ->disableOriginalConstructor() + ->getMock(); + + $this->contextMock = $this->createPartialMock( + Context::class, + ['getRequest', 'getResultRedirectFactory', 'getMessageManager'] + ); + + $this->redirectMock = $this->createPartialMock(Redirect::class, ['setPath']); + + $this->resultRedirectFactoryMock = $this->createPartialMock( + RedirectFactory::class, + ['create'] + ); + $this->resultRedirectFactoryMock->expects($this->any())->method('create')->willReturn($this->redirectMock); + $this->contextMock->expects($this->any())->method('getRequest')->willReturn($this->requestMock); + $this->contextMock->expects($this->any()) + ->method('getResultRedirectFactory') + ->willReturn($this->resultRedirectFactoryMock); + + $this->contextMock->expects($this->any()) + ->method('getMessageManager') + ->willReturn($this->messageManagerMock); + + $this->objectManagerHelper = new ObjectManagerHelper($this); + $this->deleteControllerMock = $this->objectManagerHelper->getObject( + Delete::class, + [ + 'context' => $this->contextMock, + 'filesystem' => $this->fileSystemMock, + 'file' => $this->fileMock + ] + ); + } + + /** + * Tests download controller with different file names in request. + */ + public function testExecuteSuccess() + { + $this->requestMock->method('getParam') + ->with('filename') + ->willReturn('sampleFile'); + + $this->fileSystemMock->expects($this->once()) + ->method('getDirectoryRead') + ->will($this->returnValue($this->directoryMock)); + $this->directoryMock->expects($this->once())->method('isFile')->willReturn(true); + $this->fileMock->expects($this->once())->method('deleteFile')->willReturn(true); + $this->messageManagerMock->expects($this->once())->method('addSuccessMessage'); + + $this->deleteControllerMock->execute(); + } + + /** + * Tests download controller with different file names in request. + */ + public function testExecuteFileDoesntExists() + { + $this->requestMock->method('getParam') + ->with('filename') + ->willReturn('sampleFile'); + + $this->fileSystemMock->expects($this->once()) + ->method('getDirectoryRead') + ->will($this->returnValue($this->directoryMock)); + $this->directoryMock->expects($this->once())->method('isFile')->willReturn(false); + $this->messageManagerMock->expects($this->once())->method('addErrorMessage'); + + $this->deleteControllerMock->execute(); + } + + /** + * Test execute() with invalid file name + * @param string $requestFilename + * @dataProvider invalidFileDataProvider + */ + public function testExecuteInvalidFileName($requestFilename) + { + $this->requestMock->method('getParam')->with('filename')->willReturn($requestFilename); + $this->messageManagerMock->expects($this->once())->method('addErrorMessage'); + + $this->deleteControllerMock->execute(); + } + + /** + * Data provider to test possible invalid filenames + * @return array + */ + public function invalidFileDataProvider() + { + return [ + 'Relative file name' => ['../.htaccess'], + 'Empty file name' => [''], + 'Null file name' => [null], + ]; + } +} diff --git a/app/code/Magento/ImportExport/Test/Unit/Controller/Adminhtml/Export/File/DownloadTest.php b/app/code/Magento/ImportExport/Test/Unit/Controller/Adminhtml/Export/File/DownloadTest.php new file mode 100644 index 0000000000000..4512aa6365ca5 --- /dev/null +++ b/app/code/Magento/ImportExport/Test/Unit/Controller/Adminhtml/Export/File/DownloadTest.php @@ -0,0 +1,206 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +declare(strict_types=1); + +namespace Magento\ImportExport\Test\Unit\Controller\Adminhtml\Export\File; + +use Magento\Backend\App\Action\Context; +use Magento\Backend\Model\View\Result\Redirect; +use Magento\Framework\App\Request\Http; +use Magento\Framework\App\Response\Http\FileFactory; +use Magento\Framework\Controller\Result\Raw; +use Magento\Framework\Controller\Result\RedirectFactory; +use Magento\Framework\Filesystem; +use Magento\Framework\Filesystem\Directory\ReadInterface; +use Magento\Framework\Message\ManagerInterface; +use Magento\Framework\TestFramework\Unit\Helper\ObjectManager as ObjectManagerHelper; +use Magento\ImportExport\Controller\Adminhtml\Export\File\Download; +use PHPUnit\Framework\MockObject\MockObject; +use PHPUnit\Framework\TestCase; + +/** + * @SuppressWarnings(PHPMD.CouplingBetweenObjects) + */ +class DownloadTest extends TestCase +{ + /** + * @var Context|MockObject + */ + private $contextMock; + + /** + * @var ObjectManagerHelper + */ + private $objectManagerHelper; + + /** + * @var Http|MockObject + */ + private $requestMock; + + /** + * @var Raw|MockObject + */ + private $redirectMock; + + /** + * @var RedirectFactory|MockObject + */ + private $resultRedirectFactoryMock; + + /** + * @var Filesystem|MockObject + */ + private $fileSystemMock; + + /** + * @var FileFactory|MockObject + */ + private $fileFactoryMock; + + /** + * @var Download|MockObject + */ + private $downloadControllerMock; + + /** + * @var ManagerInterface|MockObject + */ + private $messageManagerMock; + + /** + * @var ReadInterface|MockObject + */ + private $directoryMock; + + /** + * Set up + */ + protected function setUp() + { + $this->requestMock = $this->getMockBuilder(Http::class) + ->disableOriginalConstructor() + ->getMock(); + + $this->fileSystemMock = $this->getMockBuilder(Filesystem::class) + ->disableOriginalConstructor() + ->getMock(); + + $this->directoryMock = $this->getMockBuilder(ReadInterface::class) + ->disableOriginalConstructor() + ->getMock(); + + $this->fileFactoryMock = $this->getMockBuilder(FileFactory::class) + ->disableOriginalConstructor() + ->getMock(); + + $this->messageManagerMock = $this->getMockBuilder(ManagerInterface::class) + ->disableOriginalConstructor() + ->getMock(); + + $this->contextMock = $this->createPartialMock( + Context::class, + ['getRequest', 'getResultRedirectFactory', 'getMessageManager'] + ); + + $this->redirectMock = $this->createPartialMock( + Redirect::class, + ['setPath'] + ); + + $this->resultRedirectFactoryMock = $this->createPartialMock( + RedirectFactory::class, + ['create'] + ); + $this->resultRedirectFactoryMock->expects($this->any()) + ->method('create') + ->willReturn($this->redirectMock); + + $this->contextMock->expects($this->any()) + ->method('getRequest') + ->willReturn($this->requestMock); + + $this->contextMock->expects($this->any()) + ->method('getResultRedirectFactory') + ->willReturn($this->resultRedirectFactoryMock); + + $this->contextMock->expects($this->any()) + ->method('getMessageManager') + ->willReturn($this->messageManagerMock); + + $this->objectManagerHelper = new ObjectManagerHelper($this); + $this->downloadControllerMock = $this->objectManagerHelper->getObject( + Download::class, + [ + 'context' => $this->contextMock, + 'filesystem' => $this->fileSystemMock, + 'fileFactory' => $this->fileFactoryMock + ] + ); + } + + /** + * Tests download controller with successful file downloads + */ + public function testExecuteSuccess() + { + $this->requestMock->method('getParam') + ->with('filename') + ->willReturn('sampleFile.csv'); + + $this->fileSystemMock->expects($this->once()) + ->method('getDirectoryRead') + ->will($this->returnValue($this->directoryMock)); + $this->directoryMock->expects($this->once())->method('isFile')->willReturn(true); + $this->fileFactoryMock->expects($this->once())->method('create'); + + $this->downloadControllerMock->execute(); + } + + /** + * Tests download controller with file that doesn't exist + */ + public function testExecuteFileDoesntExists() + { + $this->requestMock->method('getParam') + ->with('filename') + ->willReturn('sampleFile'); + + $this->fileSystemMock->expects($this->once()) + ->method('getDirectoryRead') + ->will($this->returnValue($this->directoryMock)); + $this->directoryMock->expects($this->once())->method('isFile')->willReturn(false); + $this->messageManagerMock->expects($this->once())->method('addErrorMessage'); + + $this->downloadControllerMock->execute(); + } + + /** + * Test execute() with invalid file name + * @param ?string $requestFilename + * @dataProvider invalidFileDataProvider + */ + public function testExecuteInvalidFileName($requestFilename) + { + $this->requestMock->method('getParam')->with('filename')->willReturn($requestFilename); + $this->messageManagerMock->expects($this->once())->method('addErrorMessage'); + + $this->downloadControllerMock->execute(); + } + + /** + * Data provider to test possible invalid filenames + * @return array + */ + public function invalidFileDataProvider() + { + return [ + 'Relative file name' => ['../.htaccess'], + 'Empty file name' => [''], + 'Null file name' => [null], + ]; + } +} diff --git a/app/code/Magento/ImportExport/Test/Unit/Model/Source/Export/EntityTest.php b/app/code/Magento/ImportExport/Test/Unit/Model/Source/Export/EntityTest.php new file mode 100644 index 0000000000000..d839e97be8278 --- /dev/null +++ b/app/code/Magento/ImportExport/Test/Unit/Model/Source/Export/EntityTest.php @@ -0,0 +1,95 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ + +declare(strict_types=1); + +namespace Magento\ImportExport\Test\Unit\Model\Source\Export; + +use Magento\Framework\TestFramework\Unit\Helper\ObjectManager; +use Magento\ImportExport\Model\Export\ConfigInterface; +use Magento\ImportExport\Model\Source\Export\Entity; +use PHPUnit\Framework\MockObject\MockObject; +use PHPUnit\Framework\TestCase; + +class EntityTest extends TestCase +{ + /** + * @var ConfigInterface|MockObject + */ + private $exportConfigMock; + + /** + * @var Entity + */ + private $model; + + /** + * Setup environment for test + */ + protected function setUp() + { + $this->exportConfigMock = $this->createMock(ConfigInterface::class); + + $objectManager = new ObjectManager($this); + $this->model = $objectManager->getObject( + Entity::class, + [ + 'exportConfig' => $this->exportConfigMock + ] + ); + } + + /** + * Test toOptionArray with data provider + * + * @param array $entities + * @param array $expected + * @dataProvider toOptionArrayDataProvider + */ + public function testToOptionArray($entities, $expected) + { + $this->exportConfigMock->expects($this->any())->method('getEntities')->willReturn($entities); + + $this->assertEquals($expected, $this->model->toOptionArray()); + } + + /** + * Data Provider for test toOptionArray + * + * @return array + */ + public function toOptionArrayDataProvider() + { + return [ + 'Empty Entity' => [ + [], + [ + [ + 'label' => (string)__('-- Please Select --'), + 'value' => '' + ] + ] + ], + 'Has entities' => [ + [ + 'entity1' => [ + 'label' => 'Entity 1' + ] + ], + [ + [ + 'label' => (string)__('-- Please Select --'), + 'value' => '' + ], + [ + 'label' => (string)__('Entity 1'), + 'value' => 'entity1' + ] + ] + ] + ]; + } +} diff --git a/app/code/Magento/ImportExport/Test/Unit/Model/Source/Export/FormatTest.php b/app/code/Magento/ImportExport/Test/Unit/Model/Source/Export/FormatTest.php new file mode 100644 index 0000000000000..416bab2c124f1 --- /dev/null +++ b/app/code/Magento/ImportExport/Test/Unit/Model/Source/Export/FormatTest.php @@ -0,0 +1,86 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ + +declare(strict_types=1); + +namespace Magento\ImportExport\Test\Unit\Model\Source\Export; + +use Magento\Framework\TestFramework\Unit\Helper\ObjectManager; +use Magento\ImportExport\Model\Export\ConfigInterface; +use Magento\ImportExport\Model\Source\Export\Format; +use PHPUnit\Framework\MockObject\MockObject; +use PHPUnit\Framework\TestCase; + +class FormatTest extends TestCase +{ + /** + * @var ConfigInterface|MockObject + */ + private $exportConfigMock; + + /** + * @var Format + */ + private $model; + + /** + * Setup environment for test + */ + protected function setUp() + { + $this->exportConfigMock = $this->createMock(ConfigInterface::class); + + $objectManager = new ObjectManager($this); + $this->model = $objectManager->getObject( + Format::class, + [ + 'exportConfig' => $this->exportConfigMock + ] + ); + } + + /** + * Test toOptionArray with data provider + * + * @param array $fileFormats + * @param array $expected + * @dataProvider toOptionArrayDataProvider + */ + public function testToOptionArray($fileFormats, $expected) + { + $this->exportConfigMock->expects($this->any())->method('getFileFormats')->willReturn($fileFormats); + + $this->assertEquals($expected, $this->model->toOptionArray()); + } + + /** + * Data Provider for test toOptionArray + * + * @return array + */ + public function toOptionArrayDataProvider() + { + return [ + 'Empty file format' => [ + [], + [] + ], + 'Has file format' => [ + [ + 'fileFormat1' => [ + 'label' => 'File Format 1' + ] + ], + [ + [ + 'label' => (string)__('File Format 1'), + 'value' => 'fileFormat1' + ] + ] + ] + ]; + } +} diff --git a/app/code/Magento/ImportExport/Ui/Component/Columns/ExportGridActions.php b/app/code/Magento/ImportExport/Ui/Component/Columns/ExportGridActions.php index b5e36ccd9fbab..2cdc39866a521 100644 --- a/app/code/Magento/ImportExport/Ui/Component/Columns/ExportGridActions.php +++ b/app/code/Magento/ImportExport/Ui/Component/Columns/ExportGridActions.php @@ -56,11 +56,17 @@ public function prepareDataSource(array $dataSource) $name = $this->getData('name'); if (isset($item['file_name'])) { $item[$name]['view'] = [ - 'href' => $this->urlBuilder->getUrl(Download::URL, ['filename' => $item['file_name']]), + 'href' => $this->urlBuilder->getUrl( + Download::URL, + ['_query' => ['filename' => $item['file_name']]] + ), 'label' => __('Download') ]; $item[$name]['delete'] = [ - 'href' => $this->urlBuilder->getUrl(Delete::URL, ['filename' => $item['file_name']]), + 'href' => $this->urlBuilder->getUrl( + Delete::URL, + ['_query' => ['filename' => $item['file_name']]] + ), 'label' => __('Delete'), 'confirm' => [ 'title' => __('Delete'), diff --git a/app/code/Magento/ImportExport/Ui/DataProvider/ExportFileDataProvider.php b/app/code/Magento/ImportExport/Ui/DataProvider/ExportFileDataProvider.php index 83203f1ad8aff..57d1982d3500f 100644 --- a/app/code/Magento/ImportExport/Ui/DataProvider/ExportFileDataProvider.php +++ b/app/code/Magento/ImportExport/Ui/DataProvider/ExportFileDataProvider.php @@ -100,7 +100,7 @@ public function getData() } $result = []; foreach ($files as $file) { - $result['items'][]['file_name'] = $this->fileIO->getPathInfo($file)['basename']; + $result['items'][]['file_name'] = $this->getPathToExportFile($this->fileIO->getPathInfo($file)); } $pageSize = (int) $this->request->getParam('paging')['pageSize']; @@ -112,6 +112,31 @@ public function getData() return $result; } + /** + * Return relative export file path after "var/export" + * + * @param mixed $file + * @return string + */ + private function getPathToExportFile($file): string + { + $directory = $this->fileSystem->getDirectoryRead(DirectoryList::VAR_DIR); + $delimiter = '/'; + $cutPath = explode( + $delimiter, + $directory->getAbsolutePath() . 'export' + ); + $filePath = explode( + $delimiter, + $file['dirname'] + ); + + return ltrim( + implode($delimiter, array_diff($filePath, $cutPath)) . $delimiter . $file['basename'], + $delimiter + ); + } + /** * Get files from directory path, sort them by date modified and return sorted array of full path to files * @@ -127,8 +152,10 @@ private function getExportFiles(string $directoryPath): array return []; } foreach ($files as $filePath) { - //phpcs:ignore Magento2.Functions.DiscouragedFunction - $sortedFiles[filemtime($filePath)] = $filePath; + if ($this->file->isFile($filePath)) { + //phpcs:ignore Magento2.Functions.DiscouragedFunction + $sortedFiles[filemtime($filePath)] = $filePath; + } } //sort array elements using key value krsort($sortedFiles); diff --git a/app/code/Magento/ImportExport/i18n/en_US.csv b/app/code/Magento/ImportExport/i18n/en_US.csv index fae93c78baa09..a4943fe72826f 100644 --- a/app/code/Magento/ImportExport/i18n/en_US.csv +++ b/app/code/Magento/ImportExport/i18n/en_US.csv @@ -124,3 +124,6 @@ Summary,Summary "Message is added to queue, wait to get your file soon. Make sure your cron job is running to export the file","Message is added to queue, wait to get your file soon. Make sure your cron job is running to export the file" "Invalid data","Invalid data" "Invalid response","Invalid response" +"File %1 deleted","File %1 deleted" +"Please provide valid export file name","Please provide valid export file name" +"%1 is not a valid file","%1 is not a valid file" diff --git a/app/code/Magento/ImportExport/registration.php b/app/code/Magento/ImportExport/registration.php index 5ecc71fa8676c..85df9b374fb4c 100644 --- a/app/code/Magento/ImportExport/registration.php +++ b/app/code/Magento/ImportExport/registration.php @@ -4,6 +4,6 @@ * See COPYING.txt for license details. */ -use \Magento\Framework\Component\ComponentRegistrar; +use Magento\Framework\Component\ComponentRegistrar; ComponentRegistrar::register(ComponentRegistrar::MODULE, 'Magento_ImportExport', __DIR__); diff --git a/app/code/Magento/Indexer/Block/Backend/Grid/Column/Renderer/ScheduleStatus.php b/app/code/Magento/Indexer/Block/Backend/Grid/Column/Renderer/ScheduleStatus.php new file mode 100644 index 0000000000000..eba7591c17286 --- /dev/null +++ b/app/code/Magento/Indexer/Block/Backend/Grid/Column/Renderer/ScheduleStatus.php @@ -0,0 +1,106 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +declare(strict_types=1); + +namespace Magento\Indexer\Block\Backend\Grid\Column\Renderer; + +use Magento\Backend\Block\Context; +use Magento\Backend\Block\Widget\Grid\Column\Renderer\AbstractRenderer; +use Magento\Framework\DataObject; +use Magento\Framework\Escaper; +use Magento\Framework\Phrase; +use Magento\Indexer\Model\IndexerFactory; + +/** + * Renderer for 'Schedule Status' column in indexer grid + */ +class ScheduleStatus extends AbstractRenderer +{ + /** + * @var Escaper + */ + private $escaper; + + /** + * @var IndexerFactory + */ + private $indexerFactory; + + /** + * @param Context $context + * @param Escaper $escaper + * @param IndexerFactory $indexerFactory + * @param array $data + */ + public function __construct( + Context $context, + Escaper $escaper, + IndexerFactory $indexerFactory, + array $data = [] + ) { + parent::__construct($context, $data); + $this->escaper = $escaper; + $this->indexerFactory = $indexerFactory; + } + + /** + * Render indexer status + * + * @param DataObject $row + * @return string + */ + public function render(DataObject $row) + { + try { + if (!$row->getIsScheduled()) { + return ''; + } + + try { + $indexer = $this->indexerFactory->create(); + $indexer->load($row->getIndexerId()); + $view = $indexer->getView(); + } catch (\InvalidArgumentException $exception) { + // No view for this index. + return ''; + } + + $state = $view->getState()->loadByView($view->getId()); + $changelog = $view->getChangelog()->setViewId($view->getId()); + $currentVersionId = $changelog->getVersion(); + $count = count($changelog->getList($state->getVersionId(), $currentVersionId)); + + if ($count > 1000) { + $class = 'grid-severity-critical'; + } elseif ($count > 100) { + $class = 'grid-severity-major'; + } elseif ($count > 10) { + $class = 'grid-severity-minor'; + } else { + $class = 'grid-severity-notice'; + } + + if ($state->getStatus() !== $state::STATUS_IDLE) { + $class = 'grid-severity-minor'; + } + + $text = new Phrase( + "%status (%count in backlog)", + [ + 'status' => $state->getStatus(), + 'count' => $count, + ] + ); + + return '<span class="' . $class . '"><span>' . $text . '</span></span>'; + } catch (\Exception $exception) { + return '<span class="grid-severity-minor"><span>' . + $this->escaper->escapeHtml( + get_class($exception) . ': ' . $exception->getMessage() + ) . '</span></span>'; + } + } +} diff --git a/app/code/Magento/Indexer/Block/Backend/Grid/Column/Renderer/Scheduled.php b/app/code/Magento/Indexer/Block/Backend/Grid/Column/Renderer/Scheduled.php index d5a749270a66f..adfe3dd5b346b 100644 --- a/app/code/Magento/Indexer/Block/Backend/Grid/Column/Renderer/Scheduled.php +++ b/app/code/Magento/Indexer/Block/Backend/Grid/Column/Renderer/Scheduled.php @@ -5,6 +5,9 @@ */ namespace Magento\Indexer\Block\Backend\Grid\Column\Renderer; +/** + * Renderer for 'Scheduled' column in indexer grid + */ class Scheduled extends \Magento\Backend\Block\Widget\Grid\Column\Renderer\AbstractRenderer { /** diff --git a/app/code/Magento/Indexer/Block/Backend/Grid/Column/Renderer/Status.php b/app/code/Magento/Indexer/Block/Backend/Grid/Column/Renderer/Status.php index d8a017ef5a4b4..e8d6004b2727d 100644 --- a/app/code/Magento/Indexer/Block/Backend/Grid/Column/Renderer/Status.php +++ b/app/code/Magento/Indexer/Block/Backend/Grid/Column/Renderer/Status.php @@ -5,6 +5,9 @@ */ namespace Magento\Indexer\Block\Backend\Grid\Column\Renderer; +/** + * Renderer for 'Status' column in indexer grid + */ class Status extends \Magento\Backend\Block\Widget\Grid\Column\Renderer\AbstractRenderer { /** @@ -27,7 +30,7 @@ public function render(\Magento\Framework\DataObject $row) $text = __('Ready'); break; case \Magento\Framework\Indexer\StateInterface::STATUS_WORKING: - $class = 'grid-severity-major'; + $class = 'grid-severity-minor'; $text = __('Processing'); break; } diff --git a/app/code/Magento/Indexer/Block/Backend/Grid/Column/Renderer/Updated.php b/app/code/Magento/Indexer/Block/Backend/Grid/Column/Renderer/Updated.php index 92ded9c1f0dc1..6c84ef0e628f7 100644 --- a/app/code/Magento/Indexer/Block/Backend/Grid/Column/Renderer/Updated.php +++ b/app/code/Magento/Indexer/Block/Backend/Grid/Column/Renderer/Updated.php @@ -5,6 +5,9 @@ */ namespace Magento\Indexer\Block\Backend\Grid\Column\Renderer; +/** + * Renderer for 'Updated' column in indexer grid + */ class Updated extends \Magento\Backend\Block\Widget\Grid\Column\Renderer\Datetime { /** diff --git a/app/code/Magento/Indexer/Console/Command/IndexerReindexCommand.php b/app/code/Magento/Indexer/Console/Command/IndexerReindexCommand.php index fffa4503e14a7..c7207c853b95e 100644 --- a/app/code/Magento/Indexer/Console/Command/IndexerReindexCommand.php +++ b/app/code/Magento/Indexer/Console/Command/IndexerReindexCommand.php @@ -6,16 +6,16 @@ namespace Magento\Indexer\Console\Command; +use Magento\Framework\App\ObjectManagerFactory; use Magento\Framework\Console\Cli; use Magento\Framework\Exception\LocalizedException; use Magento\Framework\Indexer\Config\DependencyInfoProvider; +use Magento\Framework\Indexer\ConfigInterface; use Magento\Framework\Indexer\IndexerInterface; use Magento\Framework\Indexer\IndexerRegistry; use Magento\Framework\Indexer\StateInterface; use Symfony\Component\Console\Input\InputInterface; use Symfony\Component\Console\Output\OutputInterface; -use Magento\Framework\Indexer\ConfigInterface; -use Magento\Framework\App\ObjectManagerFactory; /** * Command to run indexers @@ -78,6 +78,9 @@ protected function execute(InputInterface $input, OutputInterface $output) foreach ($this->getIndexers($input) as $indexer) { try { $this->validateIndexerStatus($indexer); + + $output->write($indexer->getTitle() . ' index '); + $startTime = microtime(true); $indexerConfig = $this->getConfig()->getIndexer($indexer->getId()); $sharedIndex = $indexerConfig['shared_index']; @@ -90,17 +93,21 @@ protected function execute(InputInterface $input, OutputInterface $output) } } $resultTime = microtime(true) - $startTime; + $output->writeln( - $indexer->getTitle() . ' index has been rebuilt successfully in ' . gmdate('H:i:s', $resultTime) + __('has been rebuilt successfully in %time', ['time' => gmdate('H:i:s', $resultTime)]) ); $returnValue = Cli::RETURN_SUCCESS; } catch (LocalizedException $e) { - $output->writeln($e->getMessage()); + $output->writeln(__('exception: %message', ['message' => $e->getMessage()])); } catch (\Exception $e) { - $output->writeln($indexer->getTitle() . ' indexer process unknown error:'); + $output->writeln('process unknown error:'); $output->writeln($e->getMessage()); + + $output->writeln($e->getTraceAsString(), OutputInterface::VERBOSITY_DEBUG); } } + return $returnValue; } @@ -111,25 +118,23 @@ protected function execute(InputInterface $input, OutputInterface $output) */ protected function getIndexers(InputInterface $input) { - $indexers = parent::getIndexers($input); + $indexers = parent::getIndexers($input); $allIndexers = $this->getAllIndexers(); if (!array_diff_key($allIndexers, $indexers)) { return $indexers; } - $relatedIndexers = []; - $dependentIndexers = []; + $relatedIndexers = [[]]; + $dependentIndexers = [[]]; + foreach ($indexers as $indexer) { - $relatedIndexers = array_merge( - $relatedIndexers, - $this->getRelatedIndexerIds($indexer->getId()) - ); - $dependentIndexers = array_merge( - $dependentIndexers, - $this->getDependentIndexerIds($indexer->getId()) - ); + $relatedIndexers[] = $this->getRelatedIndexerIds($indexer->getId()); + $dependentIndexers[] = $this->getDependentIndexerIds($indexer->getId()); } + $relatedIndexers = array_merge(...$relatedIndexers); + $dependentIndexers = array_merge(...$dependentIndexers); + $invalidRelatedIndexers = []; foreach (array_unique($relatedIndexers) as $relatedIndexer) { if ($allIndexers[$relatedIndexer]->isInvalid()) { @@ -157,18 +162,15 @@ protected function getIndexers(InputInterface $input) * @param string $indexerId * @return array */ - private function getRelatedIndexerIds(string $indexerId) + private function getRelatedIndexerIds(string $indexerId): array { - $relatedIndexerIds = []; + $relatedIndexerIds = [[]]; foreach ($this->getDependencyInfoProvider()->getIndexerIdsToRunBefore($indexerId) as $relatedIndexerId) { - $relatedIndexerIds = array_merge( - $relatedIndexerIds, - [$relatedIndexerId], - $this->getRelatedIndexerIds($relatedIndexerId) - ); + $relatedIndexerIds[] = [$relatedIndexerId]; + $relatedIndexerIds[] = $this->getRelatedIndexerIds($relatedIndexerId); } - return array_unique($relatedIndexerIds); + return array_unique(array_merge(...$relatedIndexerIds)); } /** @@ -177,21 +179,18 @@ private function getRelatedIndexerIds(string $indexerId) * @param string $indexerId * @return array */ - private function getDependentIndexerIds(string $indexerId) + private function getDependentIndexerIds(string $indexerId): array { - $dependentIndexerIds = []; + $dependentIndexerIds = [[]]; foreach (array_keys($this->getConfig()->getIndexers()) as $id) { $dependencies = $this->getDependencyInfoProvider()->getIndexerIdsToRunBefore($id); if (array_search($indexerId, $dependencies) !== false) { - $dependentIndexerIds = array_merge( - $dependentIndexerIds, - [$id], - $this->getDependentIndexerIds($id) - ); + $dependentIndexerIds[] = [$id]; + $dependentIndexerIds[] = $this->getDependentIndexerIds($id); } } - return array_unique($dependentIndexerIds); + return array_unique(array_merge(...$dependentIndexerIds)); } /** diff --git a/app/code/Magento/Indexer/Controller/Adminhtml/Indexer.php b/app/code/Magento/Indexer/Controller/Adminhtml/Indexer.php index 85c5736398d00..f85a3e7577509 100644 --- a/app/code/Magento/Indexer/Controller/Adminhtml/Indexer.php +++ b/app/code/Magento/Indexer/Controller/Adminhtml/Indexer.php @@ -5,6 +5,9 @@ */ namespace Magento\Indexer\Controller\Adminhtml; +/** + * Abstract class used as part of inheritance tree for Indexer controllers + */ abstract class Indexer extends \Magento\Backend\App\Action { /** @@ -20,6 +23,8 @@ protected function _isAllowed() case 'massOnTheFly': case 'massChangelog': return $this->_authorization->isAllowed('Magento_Indexer::changeMode'); + case 'massInvalidate': + return $this->_authorization->isAllowed('Magento_Indexer::invalidate'); } return false; } diff --git a/app/code/Magento/Indexer/Controller/Adminhtml/Indexer/ListAction.php b/app/code/Magento/Indexer/Controller/Adminhtml/Indexer/ListAction.php index 35cefbcda43e7..f0d7094d832d6 100644 --- a/app/code/Magento/Indexer/Controller/Adminhtml/Indexer/ListAction.php +++ b/app/code/Magento/Indexer/Controller/Adminhtml/Indexer/ListAction.php @@ -8,6 +8,9 @@ use Magento\Framework\App\Action\HttpGetActionInterface as HttpGetActionInterface; +/** + * Controller for indexer grid + */ class ListAction extends \Magento\Indexer\Controller\Adminhtml\Indexer implements HttpGetActionInterface { /** diff --git a/app/code/Magento/Indexer/Controller/Adminhtml/Indexer/MassChangelog.php b/app/code/Magento/Indexer/Controller/Adminhtml/Indexer/MassChangelog.php index 3dffd514218d6..6934284c8e65e 100644 --- a/app/code/Magento/Indexer/Controller/Adminhtml/Indexer/MassChangelog.php +++ b/app/code/Magento/Indexer/Controller/Adminhtml/Indexer/MassChangelog.php @@ -8,6 +8,9 @@ use Magento\Framework\App\Action\HttpPostActionInterface as HttpPostActionInterface; +/** + * Controller endpoint for mass action: set index mode as 'Update by Schedule' + */ class MassChangelog extends \Magento\Indexer\Controller\Adminhtml\Indexer implements HttpPostActionInterface { /** diff --git a/app/code/Magento/Indexer/Controller/Adminhtml/Indexer/MassInvalidate.php b/app/code/Magento/Indexer/Controller/Adminhtml/Indexer/MassInvalidate.php new file mode 100644 index 0000000000000..0cc203a547b3a --- /dev/null +++ b/app/code/Magento/Indexer/Controller/Adminhtml/Indexer/MassInvalidate.php @@ -0,0 +1,65 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +namespace Magento\Indexer\Controller\Adminhtml\Indexer; + +use Magento\Backend\App\Action\Context; +use Magento\Framework\App\Action\HttpPostActionInterface as HttpPostActionInterface; +use Magento\Framework\Indexer\IndexerRegistry; + +/** + * Controller endpoint for mass action: invalidate index + */ +class MassInvalidate extends \Magento\Indexer\Controller\Adminhtml\Indexer implements HttpPostActionInterface +{ + /** + * @var IndexerRegistry $indexerRegistry + */ + private $indexerRegistry; + + /** + * @param Context $context + * @param IndexerRegistry $indexerRegistry + */ + public function __construct( + Context $context, + IndexerRegistry $indexerRegistry + ) { + parent::__construct($context); + $this->indexerRegistry = $indexerRegistry; + } + + /** + * Turn mview on for the given indexers + * + * @return void + */ + public function execute() + { + $indexerIds = $this->getRequest()->getParam('indexer_ids'); + if (!is_array($indexerIds)) { + $this->messageManager->addError(__('Please select indexers.')); + } else { + try { + foreach ($indexerIds as $indexerId) { + /** @var \Magento\Framework\Indexer\IndexerInterface $model */ + $model = $this->indexerRegistry->get($indexerId); + $model->invalidate(); + } + $this->messageManager->addSuccess( + __('%1 indexer(s) were invalidated.', count($indexerIds)) + ); + } catch (\Magento\Framework\Exception\LocalizedException $e) { + $this->messageManager->addError($e->getMessage()); + } catch (\Exception $e) { + $this->messageManager->addException( + $e, + __("We couldn't invalidate indexer(s) because of an error.") + ); + } + } + return $this->resultRedirectFactory->create()->setPath('*/*/list'); + } +} diff --git a/app/code/Magento/Indexer/Controller/Adminhtml/Indexer/MassOnTheFly.php b/app/code/Magento/Indexer/Controller/Adminhtml/Indexer/MassOnTheFly.php index 9f7faff8843a3..21fa7a61c621f 100644 --- a/app/code/Magento/Indexer/Controller/Adminhtml/Indexer/MassOnTheFly.php +++ b/app/code/Magento/Indexer/Controller/Adminhtml/Indexer/MassOnTheFly.php @@ -8,6 +8,9 @@ use Magento\Framework\App\Action\HttpPostActionInterface as HttpPostActionInterface; +/** + * Controller endpoint for mass action: set index mode as 'Update on Save' + */ class MassOnTheFly extends \Magento\Indexer\Controller\Adminhtml\Indexer implements HttpPostActionInterface { /** diff --git a/app/code/Magento/Indexer/Model/ResourceModel/Indexer/State.php b/app/code/Magento/Indexer/Model/ResourceModel/Indexer/State.php index d1e27be21ae41..d0ab887b2e335 100644 --- a/app/code/Magento/Indexer/Model/ResourceModel/Indexer/State.php +++ b/app/code/Magento/Indexer/Model/ResourceModel/Indexer/State.php @@ -5,6 +5,11 @@ */ namespace Magento\Indexer\Model\ResourceModel\Indexer; +use Magento\Framework\Indexer\StateInterface; + +/** + * Resource model for indexer state + */ class State extends \Magento\Framework\Model\ResourceModel\Db\AbstractDb { /** @@ -17,4 +22,22 @@ protected function _construct() $this->_init('indexer_state', 'state_id'); $this->addUniqueField(['field' => ['indexer_id'], 'title' => __('State for the same indexer')]); } + + /** + * @inheritDoc + */ + protected function prepareDataForUpdate($object) + { + $data = parent::prepareDataForUpdate($object); + + if (isset($data['status']) && StateInterface::STATUS_VALID === $data['status']) { + $data['status'] = $this->getConnection()->getCheckSql( + $this->getConnection()->quoteInto('status = ?', StateInterface::STATUS_WORKING), + $this->getConnection()->quote($data['status']), + 'status' + ); + } + + return $data; + } } diff --git a/app/code/Magento/Indexer/Setup/Recurring.php b/app/code/Magento/Indexer/Setup/Recurring.php index 2f1946fab1ab7..30e4ad438e73d 100644 --- a/app/code/Magento/Indexer/Setup/Recurring.php +++ b/app/code/Magento/Indexer/Setup/Recurring.php @@ -13,12 +13,15 @@ use Magento\Framework\Setup\InstallSchemaInterface; use Magento\Framework\Setup\ModuleContextInterface; use Magento\Framework\Setup\SchemaSetupInterface; +use Magento\Framework\Indexer\IndexerInterfaceFactory; use Magento\Framework\Indexer\ConfigInterface; use Magento\Indexer\Model\Indexer\State; use Magento\Indexer\Model\Indexer\StateFactory; use Magento\Indexer\Model\ResourceModel\Indexer\State\CollectionFactory; /** + * Indexer recurring setup + * * @codeCoverageIgnore * @SuppressWarnings(PHPMD.CouplingBetweenObjects) */ @@ -51,6 +54,11 @@ class Recurring implements InstallSchemaInterface */ private $stateFactory; + /** + * @var IndexerInterfaceFactory + */ + private $indexerFactory; + /** * Init * @@ -59,23 +67,26 @@ class Recurring implements InstallSchemaInterface * @param ConfigInterface $config * @param EncryptorInterface $encryptor * @param EncoderInterface $encoder + * @param IndexerInterfaceFactory $indexerFactory */ public function __construct( CollectionFactory $statesFactory, StateFactory $stateFactory, ConfigInterface $config, EncryptorInterface $encryptor, - EncoderInterface $encoder + EncoderInterface $encoder, + IndexerInterfaceFactory $indexerFactory ) { $this->statesFactory = $statesFactory; $this->stateFactory = $stateFactory; $this->config = $config; $this->encryptor = $encryptor; $this->encoder = $encoder; + $this->indexerFactory = $indexerFactory; } /** - * {@inheritdoc} + * @inheritdoc */ public function install(SchemaSetupInterface $setup, ModuleContextInterface $context) { @@ -107,6 +118,11 @@ public function install(SchemaSetupInterface $setup, ModuleContextInterface $con $state->setStatus(StateInterface::STATUS_INVALID); $state->save(); } + + $indexer = $this->indexerFactory->create()->load($indexerId); + if ($indexer->isScheduled()) { + $indexer->getView()->unsubscribe()->subscribe(); + } } } } diff --git a/app/code/Magento/Indexer/Setup/RecurringData.php b/app/code/Magento/Indexer/Setup/RecurringData.php deleted file mode 100644 index 1f6ea09ba853f..0000000000000 --- a/app/code/Magento/Indexer/Setup/RecurringData.php +++ /dev/null @@ -1,56 +0,0 @@ -<?php -/** - * Copyright © Magento, Inc. All rights reserved. - * See COPYING.txt for license details. - */ - -namespace Magento\Indexer\Setup; - -use Magento\Framework\Indexer\IndexerInterfaceFactory; -use Magento\Framework\Setup\InstallDataInterface; -use Magento\Framework\Setup\ModuleContextInterface; -use Magento\Framework\Setup\ModuleDataSetupInterface; -use Magento\Framework\Indexer\ConfigInterface; - -/** - * Recurring data upgrade for indexer module - */ -class RecurringData implements InstallDataInterface -{ - /** - * @var IndexerInterfaceFactory - */ - private $indexerFactory; - - /** - * @var ConfigInterface - */ - private $configInterface; - - /** - * RecurringData constructor. - * - * @param IndexerInterfaceFactory $indexerFactory - * @param ConfigInterface $configInterface - */ - public function __construct( - IndexerInterfaceFactory $indexerFactory, - ConfigInterface $configInterface - ) { - $this->indexerFactory = $indexerFactory; - $this->configInterface = $configInterface; - } - - /** - * {@inheritdoc} - */ - public function install(ModuleDataSetupInterface $setup, ModuleContextInterface $context) - { - foreach (array_keys($this->configInterface->getIndexers()) as $indexerId) { - $indexer = $this->indexerFactory->create()->load($indexerId); - if ($indexer->isScheduled()) { - $indexer->getView()->unsubscribe()->subscribe(); - } - } - } -} diff --git a/app/code/Magento/Indexer/Test/Mftf/ActionGroup/IndexerActionGroup/AdminReindexAndFlushCacheActionGroup.xml b/app/code/Magento/Indexer/Test/Mftf/ActionGroup/IndexerActionGroup/AdminReindexAndFlushCacheActionGroup.xml new file mode 100644 index 0000000000000..42b6b047ae73e --- /dev/null +++ b/app/code/Magento/Indexer/Test/Mftf/ActionGroup/IndexerActionGroup/AdminReindexAndFlushCacheActionGroup.xml @@ -0,0 +1,18 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + /** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +--> +<actionGroups xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/actionGroupSchema.xsd"> + <actionGroup name="AdminReindexAndFlushCache"> + <annotations> + <description>Run reindex and flush cache.</description> + </annotations> + + <magentoCLI command="indexer:reindex" stepKey="reindex"/> + <magentoCLI command="cache:flush" stepKey="flushCache"/> + </actionGroup> +</actionGroups> diff --git a/app/code/Magento/Indexer/Test/Mftf/ActionGroup/IndexerActionGroup/UpdateIndexerByScheduleActionGroup.xml b/app/code/Magento/Indexer/Test/Mftf/ActionGroup/IndexerActionGroup/UpdateIndexerByScheduleActionGroup.xml new file mode 100644 index 0000000000000..580f288aff10d --- /dev/null +++ b/app/code/Magento/Indexer/Test/Mftf/ActionGroup/IndexerActionGroup/UpdateIndexerByScheduleActionGroup.xml @@ -0,0 +1,26 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + /** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +--> +<actionGroups xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/actionGroupSchema.xsd"> + <actionGroup name="updateIndexerBySchedule"> + <annotations> + <description>Goes to the Index Management page. Checks the provided Indexer Name. Selects 'Update by Schedule'. Clicks on Submit.</description> + </annotations> + <arguments> + <argument name="indexerName" type="string"/> + </arguments> + + <amOnPage url="{{_ENV.MAGENTO_BACKEND_NAME}}/indexer/indexer/list/" stepKey="amOnIndexManagementPage"/> + <waitForPageLoad stepKey="waitForIndexManagementPageToLoad"/> + <click selector="{{AdminIndexManagementSection.indexerCheckbox(indexerName)}}" stepKey="selectIndexer1"/> + <selectOption selector="{{AdminIndexManagementSection.massActionSelect}}" userInput="change_mode_changelog" stepKey="selectUpdateBySchedule"/> + <click selector="{{AdminIndexManagementSection.massActionSubmit}}" stepKey="submitIndexerForm"/> + <!-- No re-indexing is done as part of this actionGroup since the test required no re-indexing --> + <waitForPageLoad stepKey="waitForSave"/> + </actionGroup> +</actionGroups> diff --git a/app/code/Magento/Indexer/Test/Mftf/ActionGroup/IndexerActionGroup/UpdateIndexerOnSaveActionGroup.xml b/app/code/Magento/Indexer/Test/Mftf/ActionGroup/IndexerActionGroup/UpdateIndexerOnSaveActionGroup.xml new file mode 100644 index 0000000000000..53064e33a5657 --- /dev/null +++ b/app/code/Magento/Indexer/Test/Mftf/ActionGroup/IndexerActionGroup/UpdateIndexerOnSaveActionGroup.xml @@ -0,0 +1,26 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + /** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +--> +<actionGroups xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/actionGroupSchema.xsd"> + <actionGroup name="updateIndexerOnSave"> + <annotations> + <description>Goes to the Index Management page. Checks the provided Indexer Name. Selects 'Update on Save'. Clicks on Submit.</description> + </annotations> + <arguments> + <argument name="indexerName" type="string"/> + </arguments> + + <amOnPage url="{{_ENV.MAGENTO_BACKEND_NAME}}/indexer/indexer/list/" stepKey="amOnIndexManagementPage2"/> + <waitForPageLoad stepKey="waitForIndexManagementPageToLoad2"/> + <click selector="{{AdminIndexManagementSection.indexerCheckbox(indexerName)}}" stepKey="selectIndexer2"/> + <selectOption selector="{{AdminIndexManagementSection.massActionSelect}}" userInput="change_mode_onthefly" stepKey="selectUpdateOnSave"/> + <click selector="{{AdminIndexManagementSection.massActionSubmit}}" stepKey="submitIndexerForm2"/> + <!-- No re-indexing is done as part of this actionGroup since the test required no re-indexing --> + <waitForPageLoad stepKey="waitForSave2"/> + </actionGroup> +</actionGroups> diff --git a/app/code/Magento/Indexer/Test/Mftf/ActionGroup/UpdateIndexerByScheduleActionGroup.xml b/app/code/Magento/Indexer/Test/Mftf/ActionGroup/UpdateIndexerByScheduleActionGroup.xml index 04a5b97469c8e..3b6c8c1504a3a 100644 --- a/app/code/Magento/Indexer/Test/Mftf/ActionGroup/UpdateIndexerByScheduleActionGroup.xml +++ b/app/code/Magento/Indexer/Test/Mftf/ActionGroup/UpdateIndexerByScheduleActionGroup.xml @@ -16,7 +16,7 @@ <argument name="indexerName" type="string"/> </arguments> - <amOnPage url="{{_ENV.MAGENTO_BACKEND_NAME}}/indexer/indexer/list/" stepKey="amOnIndexManagementPage"/> + <amOnPage url="{{AdminIndexManagementPage.url}}" stepKey="amOnIndexManagementPage"/> <waitForPageLoad stepKey="waitForIndexManagementPageToLoad"/> <click selector="{{AdminIndexManagementSection.indexerCheckbox(indexerName)}}" stepKey="selectIndexer1"/> <selectOption selector="{{AdminIndexManagementSection.massActionSelect}}" userInput="change_mode_changelog" stepKey="selectUpdateBySchedule"/> diff --git a/app/code/Magento/Indexer/Test/Mftf/ActionGroup/UpdateIndexerOnSaveActionGroup.xml b/app/code/Magento/Indexer/Test/Mftf/ActionGroup/UpdateIndexerOnSaveActionGroup.xml index 65be57a335006..023b5b8e0aa0e 100644 --- a/app/code/Magento/Indexer/Test/Mftf/ActionGroup/UpdateIndexerOnSaveActionGroup.xml +++ b/app/code/Magento/Indexer/Test/Mftf/ActionGroup/UpdateIndexerOnSaveActionGroup.xml @@ -16,7 +16,7 @@ <argument name="indexerName" type="string"/> </arguments> - <amOnPage url="{{_ENV.MAGENTO_BACKEND_NAME}}/indexer/indexer/list/" stepKey="amOnIndexManagementPage2"/> + <amOnPage url="{{AdminIndexManagementPage.url}}" stepKey="amOnIndexManagementPage2"/> <waitForPageLoad stepKey="waitForIndexManagementPageToLoad2"/> <click selector="{{AdminIndexManagementSection.indexerCheckbox(indexerName)}}" stepKey="selectIndexer2"/> <selectOption selector="{{AdminIndexManagementSection.massActionSelect}}" userInput="change_mode_onthefly" stepKey="selectUpdateOnSave"/> diff --git a/app/code/Magento/Indexer/Test/Mftf/Data/AdminIndexManagementGridData.xml b/app/code/Magento/Indexer/Test/Mftf/Data/AdminIndexManagementGridData.xml new file mode 100644 index 0000000000000..74494ee6b469c --- /dev/null +++ b/app/code/Magento/Indexer/Test/Mftf/Data/AdminIndexManagementGridData.xml @@ -0,0 +1,13 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + /** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +--> + +<entities xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="urn:magento:mftf:DataGenerator/etc/dataProfileSchema.xsd"> + <entity name="AdminIndexManagementGridData"> + <data key="rowProductPrice">Product Price</data> + </entity> +</entities> diff --git a/app/code/Magento/Indexer/Test/Mftf/Section/AdminIndexManagementSection.xml b/app/code/Magento/Indexer/Test/Mftf/Section/AdminIndexManagementSection.xml index 8e7df86d01329..825358e74f2af 100644 --- a/app/code/Magento/Indexer/Test/Mftf/Section/AdminIndexManagementSection.xml +++ b/app/code/Magento/Indexer/Test/Mftf/Section/AdminIndexManagementSection.xml @@ -15,7 +15,9 @@ <element name="massActionSubmit" type="button" selector="#gridIndexer_massaction-form button"/> <element name="indexerSelect" type="select" selector="//select[contains(@class,'action-select-multiselect')]"/> <element name="indexerStatus" type="text" selector="//tr[descendant::td[contains(., '{{status}}')]]//*[contains(@class, 'col-indexer_status')]/span" parameterized="true"/> - <element name="successMessage" type="text" selector="//*[@data-ui-id='messages-message-success']"/> + <element name="successMessage" type="text" selector="//*[@data-ui-id='messages-message-success']" timeout="120"/> <element name="selectMassAction" type="select" selector="#gridIndexer_massaction-mass-select"/> + <element name="columnScheduleStatus" type="text" selector="//th[contains(@class, 'col-indexer_schedule_status')]"/> + <element name="indexerScheduleStatus" type="text" selector="//tr[contains(.,'{{var1}}')]//td[contains(@class,'col-indexer_schedule_status')]" parameterized="true"/> </section> </sections> diff --git a/app/code/Magento/Indexer/Test/Mftf/Test/AdminSystemIndexManagementGridChangesTest.xml b/app/code/Magento/Indexer/Test/Mftf/Test/AdminSystemIndexManagementGridChangesTest.xml new file mode 100644 index 0000000000000..c60f976538028 --- /dev/null +++ b/app/code/Magento/Indexer/Test/Mftf/Test/AdminSystemIndexManagementGridChangesTest.xml @@ -0,0 +1,65 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + /** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +--> + +<tests xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/testSchema.xsd"> + <test name="AdminSystemIndexManagementGridChangesTest"> + <annotations> + <features value="Indexer"/> + <stories value="Menu Navigation"/> + <title value="Admin system index management grid change test"/> + <description value="Verify changes in 'Schedule column' on system index management"/> + </annotations> + <before> + <createData entity="_defaultCategory" stepKey="createCategory"/> + <createData entity="_defaultProduct" stepKey="createProduct"> + <requiredEntity createDataKey="createCategory"/> + </createData> + + <!--Open Index Management Page and Select Index mode "Update by Schedule" --> + <magentoCLI command="indexer:set-mode" arguments="schedule" stepKey="setIndexerModeSchedule"/> + <magentoCLI command="indexer:reindex" stepKey="indexerReindex"/> + <magentoCLI command="cache:flush" stepKey="flushCache"/> + <actionGroup ref="AdminLoginActionGroup" stepKey="LoginAsAdmin"/></before> + <after> + <deleteData createDataKey="createProduct" stepKey="deleteProduct"/> + <deleteData createDataKey="createCategory" stepKey="deleteCategory"/> + <magentoCLI command="indexer:set-mode" arguments="realtime" stepKey="setIndexerModeRealTime"/> + <magentoCLI command="indexer:reindex" stepKey="indexerReindex"/> + <magentoCLI command="cache:flush" stepKey="flushCache"/> + <actionGroup ref="AdminLogoutActionGroup" stepKey="logout"/> + </after> + + <actionGroup ref="AdminNavigateMenuActionGroup" stepKey="navigateToIndexManagementPageFirst"> + <argument name="menuUiId" value="{{AdminMenuSystem.dataUiId}}"/> + <argument name="submenuUiId" value="{{AdminMenuSystemToolsIndexManagement.dataUiId}}"/> + </actionGroup> + <grabTextFrom selector="{{AdminIndexManagementSection.indexerScheduleStatus(AdminIndexManagementGridData.rowProductPrice)}}" stepKey="gradScheduleStatusBeforeChange"/> + + <!-- Verify 'Schedule status' column is present --> + <seeElement selector="{{AdminIndexManagementSection.columnScheduleStatus}}" stepKey="seeScheduleStatusColumn"/> + + <!--Adding Special price to product--> + <actionGroup ref="AdminProductPageOpenByIdActionGroup" stepKey="openAdminProductEditPage"> + <argument name="productId" value="$$createProduct.id$$"/> + </actionGroup> + <actionGroup ref="AddSpecialPriceToProductActionGroup" stepKey="addSpecialPrice"/> + <actionGroup ref="SaveProductFormActionGroup" stepKey="saveProductForm"/> + + <actionGroup ref="AdminNavigateMenuActionGroup" stepKey="navigateToIndexManagementPageSecond"> + <argument name="menuUiId" value="{{AdminMenuSystem.dataUiId}}"/> + <argument name="submenuUiId" value="{{AdminMenuSystemToolsIndexManagement.dataUiId}}"/> + </actionGroup> + <grabTextFrom selector="{{AdminIndexManagementSection.indexerScheduleStatus(AdminIndexManagementGridData.rowProductPrice)}}" stepKey="gradScheduleStatusAfterChange"/> + + <!-- Verify 'Schedule Status' column changes for 'Product Price' --> + <assertNotEquals stepKey="assertChange"> + <expectedResult type="string">$gradScheduleStatusBeforeChange</expectedResult> + <actualResult type="string">$gradScheduleStatusAfterChange</actualResult> + </assertNotEquals> + </test> +</tests> diff --git a/app/code/Magento/Indexer/Test/Mftf/Test/AdminSystemIndexManagementNavigateMenuTest.xml b/app/code/Magento/Indexer/Test/Mftf/Test/AdminSystemIndexManagementNavigateMenuTest.xml index 140c93f7f2b48..4c0c4940ec130 100644 --- a/app/code/Magento/Indexer/Test/Mftf/Test/AdminSystemIndexManagementNavigateMenuTest.xml +++ b/app/code/Magento/Indexer/Test/Mftf/Test/AdminSystemIndexManagementNavigateMenuTest.xml @@ -20,10 +20,10 @@ <group value="mtf_migrated"/> </annotations> <before> - <actionGroup ref="LoginAsAdmin" stepKey="LoginAsAdmin"/> + <actionGroup ref="AdminLoginActionGroup" stepKey="LoginAsAdmin"/> </before> <after> - <actionGroup ref="logout" stepKey="logout"/> + <actionGroup ref="AdminLogoutActionGroup" stepKey="logout"/> </after> <actionGroup ref="AdminNavigateMenuActionGroup" stepKey="navigateToIndexManagementPage"> <argument name="menuUiId" value="{{AdminMenuSystem.dataUiId}}"/> diff --git a/app/code/Magento/Indexer/Test/Unit/Block/Backend/Grid/Column/Renderer/StatusTest.php b/app/code/Magento/Indexer/Test/Unit/Block/Backend/Grid/Column/Renderer/StatusTest.php index 705a9e3998ae8..7249f764a66a1 100644 --- a/app/code/Magento/Indexer/Test/Unit/Block/Backend/Grid/Column/Renderer/StatusTest.php +++ b/app/code/Magento/Indexer/Test/Unit/Block/Backend/Grid/Column/Renderer/StatusTest.php @@ -49,7 +49,7 @@ public function renderDataProvider() ], 'set3' => [ [\Magento\Framework\Indexer\StateInterface::STATUS_WORKING], - ['class' => 'grid-severity-major', 'text' => 'Processing'] + ['class' => 'grid-severity-minor', 'text' => 'Processing'] ] ]; } diff --git a/app/code/Magento/Indexer/Test/Unit/Console/Command/IndexerReindexCommandTest.php b/app/code/Magento/Indexer/Test/Unit/Console/Command/IndexerReindexCommandTest.php index bdfeff8a89eb9..3a1bf113b942a 100644 --- a/app/code/Magento/Indexer/Test/Unit/Console/Command/IndexerReindexCommandTest.php +++ b/app/code/Magento/Indexer/Test/Unit/Console/Command/IndexerReindexCommandTest.php @@ -22,6 +22,7 @@ */ class IndexerReindexCommandTest extends AbstractIndexerCommandCommonSetup { + const STUB_INDEXER_NAME = 'Indexer Name'; /** * Command being tested * @@ -107,7 +108,7 @@ public function testExecuteAll() [ $this->getIndexerMock( ['reindexAll', 'getStatus'], - ['indexer_id' => 'id_indexerOne', 'title' => 'Title_indexerOne'] + ['indexer_id' => 'id_indexerOne', 'title' => self::STUB_INDEXER_NAME] ) ] ); @@ -117,7 +118,10 @@ public function testExecuteAll() $commandTester->execute([]); $actualValue = $commandTester->getDisplay(); $this->assertSame(Cli::RETURN_SUCCESS, $commandTester->getStatusCode()); - $this->assertStringStartsWith('Title_indexerOne index has been rebuilt successfully in', $actualValue); + $this->assertStringStartsWith( + self::STUB_INDEXER_NAME . ' index has been rebuilt successfully in', + $actualValue + ); } /** @@ -174,6 +178,7 @@ public function testExecuteWithIndex( $this->objectManagerFactory, $this->indexerRegistryMock ); + $commandTester = new CommandTester($this->command); $commandTester->execute(['index' => $inputIndexers]); $this->assertSame(Cli::RETURN_SUCCESS, $commandTester->getStatusCode()); @@ -344,7 +349,8 @@ public function executeWithIndexDataProvider() ], 'With dependencies and multiple indexers in request' => [ 'inputIndexers' => [ - 'indexer_1', 'indexer_3' + 'indexer_1', + 'indexer_3' ], 'indexers' => [ 'indexer_2' => [ @@ -405,7 +411,10 @@ public function executeWithIndexDataProvider() public function testExecuteWithLocalizedException() { $this->configureAdminArea(); - $indexerOne = $this->getIndexerMock(['reindexAll', 'getStatus'], ['indexer_id' => 'indexer_1']); + $indexerOne = $this->getIndexerMock( + ['reindexAll', 'getStatus'], + ['indexer_id' => 'indexer_1', 'title' => self::STUB_INDEXER_NAME] + ); $localizedException = new LocalizedException(new Phrase('Some Exception Message')); $indexerOne->expects($this->once())->method('reindexAll')->will($this->throwException($localizedException)); $this->initIndexerCollectionByItems([$indexerOne]); @@ -414,7 +423,10 @@ public function testExecuteWithLocalizedException() $commandTester->execute(['index' => ['indexer_1']]); $actualValue = $commandTester->getDisplay(); $this->assertSame(Cli::RETURN_FAILURE, $commandTester->getStatusCode()); - $this->assertStringStartsWith('Some Exception Message', $actualValue); + $this->assertStringStartsWith( + self::STUB_INDEXER_NAME . ' index exception: Some Exception Message', + $actualValue + ); } public function testExecuteWithException() @@ -433,7 +445,7 @@ public function testExecuteWithException() $commandTester->execute(['index' => ['indexer_1']]); $actualValue = $commandTester->getDisplay(); $this->assertSame(Cli::RETURN_FAILURE, $commandTester->getStatusCode()); - $this->assertStringStartsWith('Title_indexer_1' . ' indexer process unknown error:', $actualValue); + $this->assertStringStartsWith('Title_indexer_1' . ' index process unknown error:', $actualValue); } public function testExecuteWithExceptionInGetIndexers() diff --git a/app/code/Magento/Indexer/Test/Unit/Controller/Adminhtml/Indexer/MassInvalidateTest.php b/app/code/Magento/Indexer/Test/Unit/Controller/Adminhtml/Indexer/MassInvalidateTest.php new file mode 100644 index 0000000000000..e0d4a505bc64d --- /dev/null +++ b/app/code/Magento/Indexer/Test/Unit/Controller/Adminhtml/Indexer/MassInvalidateTest.php @@ -0,0 +1,281 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +namespace Magento\Indexer\Test\Unit\Controller\Adminhtml\Indexer; + +/** + * Test for Mass invalidate action + * + * @SuppressWarnings(PHPMD.CouplingBetweenObjects) + */ +class MassInvalidateTest extends \PHPUnit\Framework\TestCase +{ + /** + * @var \Magento\Indexer\Controller\Adminhtml\Indexer\MassInvalidate + */ + protected $controller; + + /** + * @var \Magento\Backend\App\Action\Context + */ + protected $contextMock; + + /** + * @var \Magento\Framework\App\ViewInterface + */ + protected $view; + + /** + * @var \Magento\Framework\View\Result\Page + */ + protected $page; + + /** + * @var \Magento\Framework\View\Page\Config + */ + protected $config; + + /** + * @var \Magento\Framework\View\Page\Title + */ + protected $title; + + /** + * @var \Magento\Framework\App\RequestInterface + */ + protected $request; + + /** + * @var \Magento\Framework\TestFramework\Unit\Helper\ObjectManager + */ + protected $objectManager; + + /** + * @var \Magento\Framework\Message\ManagerInterface + */ + protected $messageManager; + + /** + * @var \Magento\Framework\Indexer\IndexerRegistry + */ + protected $indexReg; + + /** + * @var \Magento\Framework\App\ResponseInterface + */ + protected $response; + + /** + * @var \Magento\Framework\App\ActionFlag + */ + protected $actionFlag; + + /** + * @var \Magento\Backend\Helper\Data + */ + protected $helper; + + /** + * @var \Magento\Backend\Model\Session + */ + protected $session; + + /** + * @var \Magento\Framework\Controller\Result\Redirect + */ + protected $resultRedirect; + + /** + * Set up test + * @SuppressWarnings(PHPMD.ExcessiveMethodLength) + */ + protected function setUp() + { + $this->contextMock = $this->createPartialMock( + \Magento\Backend\App\Action\Context::class, + [ + 'getAuthorization', + 'getSession', + 'getActionFlag', + 'getAuth', + 'getView', + 'getHelper', + 'getBackendUrl', + 'getFormKeyValidator', + 'getLocaleResolver', + 'getCanUseBaseUrl', + 'getRequest', + 'getResponse', + 'getObjectManager', + 'getMessageManager', + 'getResultRedirectFactory', + ] + ); + + $this->response = $this->createPartialMock( + \Magento\Framework\App\ResponseInterface::class, + ['setRedirect', 'sendResponse'] + ); + + $this->view = $this->createPartialMock( + \Magento\Framework\App\ViewInterface::class, + [ + 'loadLayout', + 'getPage', + 'getConfig', + 'getTitle', + 'renderLayout', + 'loadLayoutUpdates', + 'getDefaultLayoutHandle', + 'addPageLayoutHandles', + 'generateLayoutBlocks', + 'generateLayoutXml', + 'getLayout', + 'addActionLayoutHandles', + 'setIsLayoutLoaded', + 'isLayoutLoaded' + ] + ); + + $this->session = $this->createPartialMock(\Magento\Backend\Model\Session::class, ['setIsUrlNotice']); + $this->session->expects($this->any())->method('setIsUrlNotice')->willReturn($this->objectManager); + $this->actionFlag = $this->createPartialMock(\Magento\Framework\App\ActionFlag::class, ['get']); + $this->actionFlag->expects($this->any())->method("get")->willReturn($this->objectManager); + $this->objectManager = $this->createPartialMock( + \Magento\Framework\TestFramework\Unit\Helper\ObjectManager::class, + ['get'] + ); + $this->request = $this->getMockForAbstractClass( + \Magento\Framework\App\RequestInterface::class, + ['getParam', 'getRequest'], + '', + false + ); + + $resultRedirectFactory = $this->createPartialMock( + \Magento\Backend\Model\View\Result\RedirectFactory::class, + ['create'] + ); + $this->resultRedirect = $this->createPartialMock( + \Magento\Framework\Controller\Result\Redirect::class, + ['setPath'] + ); + $this->contextMock->expects($this->any())->method('getResultRedirectFactory') + ->willReturn($resultRedirectFactory); + $resultRedirectFactory->expects($this->any())->method('create') + ->willReturn($this->resultRedirect); + + $this->response->expects($this->any())->method("setRedirect")->willReturn(1); + $this->page = $this->createMock(\Magento\Framework\View\Result\Page::class); + $this->config = $this->createMock(\Magento\Framework\View\Result\Page::class); + $this->title = $this->createMock(\Magento\Framework\View\Page\Title::class); + $this->messageManager = $this->getMockForAbstractClass( + \Magento\Framework\Message\ManagerInterface::class, + ['addError', 'addSuccess'], + '', + false + ); + + $this->indexReg = $this->createPartialMock( + \Magento\Framework\Indexer\IndexerRegistry::class, + ['get', 'setScheduled'] + ); + $this->helper = $this->createPartialMock(\Magento\Backend\Helper\Data::class, ['getUrl']); + $this->contextMock->expects($this->any())->method("getObjectManager")->willReturn($this->objectManager); + $this->contextMock->expects($this->any())->method("getRequest")->willReturn($this->request); + $this->contextMock->expects($this->any())->method("getResponse")->willReturn($this->response); + $this->contextMock->expects($this->any())->method("getMessageManager")->willReturn($this->messageManager); + $this->contextMock->expects($this->any())->method("getSession")->willReturn($this->session); + $this->contextMock->expects($this->any())->method("getActionFlag")->willReturn($this->actionFlag); + $this->contextMock->expects($this->any())->method("getHelper")->willReturn($this->helper); + } + + /** + * @param array $indexerIds + * @param \Exception $exception + * @dataProvider executeDataProvider + */ + public function testExecute($indexerIds, $exception) + { + $this->controller = new \Magento\Indexer\Controller\Adminhtml\Indexer\MassInvalidate( + $this->contextMock, + $this->indexReg + ); + $this->request->expects($this->any()) + ->method('getParam')->with('indexer_ids') + ->will($this->returnValue($indexerIds)); + + if (!is_array($indexerIds)) { + $this->messageManager->expects($this->once()) + ->method('addError')->with(__('Please select indexers.')) + ->will($this->returnValue(1)); + } else { + $indexerInterface = $this->getMockForAbstractClass( + \Magento\Framework\Indexer\IndexerInterface::class, + ['invalidate'], + '', + false + ); + $this->indexReg->expects($this->any()) + ->method('get')->with(1) + ->will($this->returnValue($indexerInterface)); + + $indexerInterface->expects($this->any()) + ->method('invalidate')->with(true) + ->will($this->returnValue(1)); + + $this->messageManager->expects($this->any()) + ->method('addSuccess') + ->will($this->returnValue(1)); + + if ($exception) { + $this->indexReg->expects($this->any()) + ->method('get')->with(2) + ->will($this->throwException($exception)); + + if ($exception instanceof \Magento\Framework\Exception\LocalizedException) { + $this->messageManager->expects($this->once()) + ->method('addError') + ->with($exception->getMessage()); + } else { + $this->messageManager->expects($this->once()) + ->method('addException') + ->with($exception, "We couldn't invalidate indexer(s) because of an error."); + } + } + } + + $this->helper->expects($this->any())->method("getUrl")->willReturn("magento.com"); + $this->response->expects($this->any())->method("setRedirect")->willReturn(1); + $this->resultRedirect->expects($this->once())->method('setPath')->with('*/*/list'); + + $this->controller->execute(); + } + + /** + * @return array + */ + public function executeDataProvider() + { + return [ + 'set1' => [ + 'indexers' => 1, + 'exception' => null, + ], + 'set2' => [ + 'indexers' => [1], + 'exception' => null, + ], + 'set3' => [ + 'indexers' => [2], + 'exception' => new \Magento\Framework\Exception\LocalizedException(__('Test Phrase')), + ], + 'set4' => [ + 'indexers' => [2], + 'exception' => new \Exception(), + ] + ]; + } +} diff --git a/app/code/Magento/Indexer/Test/Unit/Model/IndexerTest.php b/app/code/Magento/Indexer/Test/Unit/Model/IndexerTest.php index ca2da9585f934..a43ea2b55514c 100644 --- a/app/code/Magento/Indexer/Test/Unit/Model/IndexerTest.php +++ b/app/code/Magento/Indexer/Test/Unit/Model/IndexerTest.php @@ -154,15 +154,7 @@ public function testGetLatestUpdated($getViewIsEnabled, $getViewGetUpdated, $get $this->stateFactoryMock->expects($this->once())->method('create')->will($this->returnValue($stateMock)); if ($getViewIsEnabled && $getViewGetUpdated) { - if (!$getStateGetUpdated) { - $this->assertEquals($getViewGetUpdated, $this->model->getLatestUpdated()); - } else { - if ($getViewGetUpdated == $getStateGetUpdated) { - $this->assertEquals($getViewGetUpdated, $this->model->getLatestUpdated()); - } else { - $this->assertEquals($getViewGetUpdated, $this->model->getLatestUpdated()); - } - } + $this->assertEquals($getViewGetUpdated, $this->model->getLatestUpdated()); } else { $getLatestUpdated = $this->model->getLatestUpdated(); $this->assertEquals($getStateGetUpdated, $getLatestUpdated); diff --git a/app/code/Magento/Indexer/etc/acl.xml b/app/code/Magento/Indexer/etc/acl.xml index fc9f45c21b272..520ab343bdab3 100644 --- a/app/code/Magento/Indexer/etc/acl.xml +++ b/app/code/Magento/Indexer/etc/acl.xml @@ -13,6 +13,7 @@ <resource id="Magento_Backend::tools"> <resource id="Magento_Indexer::index" title="Index Management" translate="title" sortOrder="30" /> <resource id="Magento_Indexer::changeMode" title="Change indexer mode" translate="title" sortOrder="40" /> + <resource id="Magento_Indexer::invalidate" title="Invalidate index" translate="title" sortOrder="50" /> </resource> </resource> </resource> diff --git a/app/code/Magento/Indexer/i18n/en_US.csv b/app/code/Magento/Indexer/i18n/en_US.csv index 85ee5b1f91ba9..2935243856b4d 100644 --- a/app/code/Magento/Indexer/i18n/en_US.csv +++ b/app/code/Magento/Indexer/i18n/en_US.csv @@ -1,6 +1,7 @@ "Indexer Management","Indexer Management" "Update by Schedule","Update by Schedule" "Update on Save","Update on Save" +"Invalidate index","Invalidate index" "Reindex required","Reindex required" Ready,Ready Processing,Processing @@ -11,6 +12,8 @@ Never,Never "%1 indexer(s) are in ""Update by Schedule"" mode.","%1 indexer(s) are in ""Update by Schedule"" mode." "We couldn't change indexer(s)' mode because of an error.","We couldn't change indexer(s)' mode because of an error." "%1 indexer(s) are in ""Update on Save"" mode.","%1 indexer(s) are in ""Update on Save"" mode." +"We couldn't invalidate indexer(s) because of an error.","We couldn't invalidate indexer(s) because of an error." +"%1 indexer(s) were invalidated.","%1 indexer(s) were invalidated." "One or more <a href=""%1"">indexers are invalid</a>. Make sure your <a href=""%2"" target=""_blank"">Magento cron job</a> is running.","One or more <a href=""%1"">indexers are invalid</a>. Make sure your <a href=""%2"" target=""_blank"">Magento cron job</a> is running." "State for the same indexer","State for the same indexer" "State for the same view","State for the same view" @@ -23,3 +26,4 @@ Description,Description Mode,Mode Status,Status Updated,Updated +"Are you sure you want to invalidate the selected index(es)?","Are you sure you want to invalidate the selected index(es)?" diff --git a/app/code/Magento/Indexer/registration.php b/app/code/Magento/Indexer/registration.php index dda2240601779..0a5f37e97ac82 100644 --- a/app/code/Magento/Indexer/registration.php +++ b/app/code/Magento/Indexer/registration.php @@ -4,6 +4,6 @@ * See COPYING.txt for license details. */ -use \Magento\Framework\Component\ComponentRegistrar; +use Magento\Framework\Component\ComponentRegistrar; ComponentRegistrar::register(ComponentRegistrar::MODULE, 'Magento_Indexer', __DIR__); diff --git a/app/code/Magento/Indexer/view/adminhtml/layout/indexer_indexer_list_grid.xml b/app/code/Magento/Indexer/view/adminhtml/layout/indexer_indexer_list_grid.xml index f1099c4133bca..b5f5a41bf18aa 100644 --- a/app/code/Magento/Indexer/view/adminhtml/layout/indexer_indexer_list_grid.xml +++ b/app/code/Magento/Indexer/view/adminhtml/layout/indexer_indexer_list_grid.xml @@ -30,6 +30,11 @@ <item name="label" xsi:type="string" translate="true">Update by Schedule</item> <item name="url" xsi:type="string">*/indexer/massChangelog</item> </item> + <item name="invalidate_index" xsi:type="array"> + <item name="label" xsi:type="string" translate="true">Invalidate index</item> + <item name="url" xsi:type="string">*/indexer/massInvalidate</item> + <item name="confirm" xsi:type="string" translate="true">Are you sure you want to invalidate the selected index(es)?</item> + </item> </argument> </arguments> </block> @@ -76,6 +81,15 @@ <argument name="column_css_class" xsi:type="string">indexer-status</argument> </arguments> </block> + <block class="Magento\Backend\Block\Widget\Grid\Column" name="adminhtml.indexer.grid.columnSet.indexer_schedule_status" as="indexer_schedule_status"> + <arguments> + <argument name="header" xsi:type="string" translate="true">Schedule Status</argument> + <argument name="index" xsi:type="string">schedule_status</argument> + <argument name="renderer" xsi:type="string">Magento\Indexer\Block\Backend\Grid\Column\Renderer\ScheduleStatus</argument> + <argument name="sortable" xsi:type="string">0</argument> + <argument name="column_css_class" xsi:type="string">indexer-schedule-status</argument> + </arguments> + </block> <block class="Magento\Backend\Block\Widget\Grid\Column" name="adminhtml.indexer.grid.columnSet.indexer_updated" as="indexer_updated"> <arguments> <argument name="header" xsi:type="string" translate="true">Updated</argument> diff --git a/app/code/Magento/InstantPurchase/Test/Mftf/ActionGroup/AssertStorefrontInstantPurchaseConfirmationDataActionGroup.xml b/app/code/Magento/InstantPurchase/Test/Mftf/ActionGroup/AssertStorefrontInstantPurchaseConfirmationDataActionGroup.xml new file mode 100644 index 0000000000000..0245ba309e3b2 --- /dev/null +++ b/app/code/Magento/InstantPurchase/Test/Mftf/ActionGroup/AssertStorefrontInstantPurchaseConfirmationDataActionGroup.xml @@ -0,0 +1,25 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + /** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +--> +<actionGroups xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/actionGroupSchema.xsd"> + <actionGroup name="AssertStorefrontInstantPurchaseConfirmationDataActionGroup"> + <annotations> + <description>Click on "Instant Purchase" button and assert shipping and billing information</description> + </annotations> + <arguments> + <argument name="shippingStreet" type="string" defaultValue="{{US_Address_TX.street[0]}}"/> + <argument name="billingStreet" type="string" defaultValue="{{US_Address_TX_Default_Billing.street[0]}}"/> + <argument name="cardEnding" type="string" defaultValue="{{StoredPaymentMethods.cardNumberEnding}}"/> + </arguments> + <click selector="{{StorefrontInstantPurchaseSection.instantPurchaseButton}}" stepKey="clickInstantPurchaseButton"/> + <waitForElementVisible selector="{{ModalConfirmationSection.OkButton}}" stepKey="waitForButtonAppears"/> + <seeElement selector="{{StorefrontInstantPurchasePopupSection.shippingAddress(shippingStreet)}}" stepKey="assertShippingAddress"/> + <seeElement selector="{{StorefrontInstantPurchasePopupSection.billingAddress(billingStreet)}}" stepKey="assertBillingAddress"/> + <seeElement selector="{{StorefrontInstantPurchasePopupSection.paymentMethod(cardEnding)}}" stepKey="assertCardEnding"/> + </actionGroup> +</actionGroups> diff --git a/app/code/Magento/InstantPurchase/Test/Mftf/Section/StorefrontInstantPurchasePopupSection.xml b/app/code/Magento/InstantPurchase/Test/Mftf/Section/StorefrontInstantPurchasePopupSection.xml index 8e93d049ea141..2336868086a69 100644 --- a/app/code/Magento/InstantPurchase/Test/Mftf/Section/StorefrontInstantPurchasePopupSection.xml +++ b/app/code/Magento/InstantPurchase/Test/Mftf/Section/StorefrontInstantPurchasePopupSection.xml @@ -11,5 +11,8 @@ <section name="StorefrontInstantPurchasePopupSection"> <element name="modalTitle" type="text" selector=".modal-popup .modal-title"/> <element name="cancel" type="button" selector=".modal-popup.confirm button.action-dismiss"/> + <element name="shippingAddress" type="text" selector="//aside[contains(@class, 'modal-popup')]//strong[contains(text(),'Shipping Address:')]/following-sibling::p[contains(text(),'{{Data}}')][1]" parameterized="true"/> + <element name="billingAddress" type="text" selector="//aside[contains(@class, 'modal-popup')]//strong[contains(text(),'Billing Address:')]/following-sibling::p[contains(text(),'{{Data}}')]" parameterized="true"/> + <element name="paymentMethod" type="text" selector="//aside[contains(@class, 'modal-popup')]//strong[contains(text(),'Payment Method:')]/following-sibling::p[contains(text(),'{{Data}}')]" parameterized="true"/> </section> </sections> diff --git a/app/code/Magento/InstantPurchase/Test/Mftf/Test/StorefrontInstantPurchaseFunctionalityNegativeScenarioTest.xml b/app/code/Magento/InstantPurchase/Test/Mftf/Test/StorefrontInstantPurchaseFunctionalityNegativeScenarioTest.xml new file mode 100644 index 0000000000000..fc1f92dba7ad6 --- /dev/null +++ b/app/code/Magento/InstantPurchase/Test/Mftf/Test/StorefrontInstantPurchaseFunctionalityNegativeScenarioTest.xml @@ -0,0 +1,228 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + /** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +--> + +<tests xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/testSchema.xsd"> + <test name="StorefrontInstantPurchaseFunctionalityNegativeScenarioTest"> + <annotations> + <features value="InstantPurchase"/> + <stories value="Using Instant Purchase"/> + <title value="Checks negative Instant Purchase functionality scenario"/> + <description value="Checks that Instant Purchase button does not appear in a different situation"/> + <testCaseId value="MC-25949"/> + <severity value="CRITICAL"/> + <group value="instant_purchase"/> + <group value="vault"/> + <group value="braintree"/> + </annotations> + <before> + <magentoCLI command="downloadable:domains:add" arguments="example.com static.magento.com" stepKey="addDownloadableDomain"/> + <!-- Configure Braintree payment method --> + <createData entity="BraintreeConfig" stepKey="configureBraintreePayment"/> + <!-- Enable Braintree with Vault --> + <createData entity="CustomBraintreeConfigurationData" stepKey="enableBraintreeAndVault"/> + <!-- Create customers: without address, with address, with saved shipping and billing --> + <createData entity="Simple_Customer_Without_Address" stepKey="customerWithoutAddress"/> + <createData entity="Simple_US_Customer_Multiple_Addresses_No_Default_Address" stepKey="customerWithAddress"/> + <createData entity="Simple_US_Customer_Multiple_Addresses" stepKey="customerWithDefaultAddress"/> + <!-- Create all product variations --> + <createData entity="SimpleProduct2" stepKey="createSimpleProduct"/> + <createData entity="VirtualProduct" stepKey="createVirtualProduct"/> + <actionGroup ref="AdminCreateApiConfigurableProductActionGroup" stepKey="createConfigurableProduct"/> + <!-- Create Bundle Product --> + <createData entity="ApiFixedBundleProduct" stepKey="createBundleProduct"/> + <createData entity="DropDownBundleOption" stepKey="createBundleOption"> + <requiredEntity createDataKey="createBundleProduct"/> + </createData> + <createData entity="ApiBundleLink" stepKey="createBundleLink"> + <requiredEntity createDataKey="createBundleProduct"/> + <requiredEntity createDataKey="createBundleOption"/> + <requiredEntity createDataKey="createSimpleProduct"/> + </createData> + <!-- Create Downloadable Product --> + <createData entity="ApiDownloadableProduct" stepKey="createDownloadableProduct"/> + <createData entity="downloadableLink1" stepKey="addDownloadableLink"> + <requiredEntity createDataKey="createDownloadableProduct"/> + </createData> + <!-- Create Grouped Product --> + <createData entity="ApiGroupedProduct" stepKey="createGroupedProduct"/> + <createData entity="OneSimpleProductLink" stepKey="createLinkForGroupedProduct"> + <requiredEntity createDataKey="createGroupedProduct"/> + <requiredEntity createDataKey="createSimpleProduct"/> + </createData> + <!-- Reindex invalidated indices after product attribute has been created/deleted --> + <magentoCron groups="index" stepKey="reindexInvalidatedIndices"/> + <!-- Log in as a customer --> + <actionGroup ref="LoginToStorefrontActionGroup" stepKey="customerLoginToStorefront"> + <argument name="Customer" value="$customerWithDefaultAddress$"/> + </actionGroup> + <!-- Customer placed order from storefront with payment method --> + <actionGroup ref="AddSimpleProductToCartActionGroup" stepKey="addProductToCart"> + <argument name="product" value="$createSimpleProduct$"/> + </actionGroup> + <actionGroup ref="GoToCheckoutFromMinicartActionGroup" stepKey="goToCheckoutFromMinicart"/> + <actionGroup ref="StorefrontSetShippingMethodActionGroup" stepKey="setShippingMethodFlatRate"/> + <actionGroup ref="StorefrontCheckoutClickNextOnShippingStepActionGroup" stepKey="goToCheckoutPaymentStep"/> + <!-- Fill Braintree card data --> + <click selector="{{BraintreeConfigurationPaymentSection.creditCart}}" stepKey="selectBraintreePaymentMethod"/> + <waitForPageLoad stepKey="waitForBraintreeFormLoad"/> + <scrollTo selector="{{BraintreeConfigurationPaymentSection.creditCart}}" stepKey="scrollToCreditCardSection"/> + <actionGroup ref="StorefrontFillCartDataActionGroup" stepKey="fillCardData"> + <argument name="cartData" value="VisaDefaultCard"/> + </actionGroup> + <waitForPageLoad stepKey="waitForFillCardData"/> + <checkOption selector="{{StorefrontOnePageCheckoutPaymentSection.saveForLaterUse}}" stepKey="checkSaveForLaterUse"/> + <actionGroup ref="CheckoutPlaceOrderActionGroup" stepKey="clickOnPlaceOrder"> + <argument name="orderNumberMessage" value="CONST.successCheckoutOrderNumberMessage"/> + <argument name="emailYouMessage" value="CONST.successCheckoutEmailYouMessage"/> + </actionGroup> + </before> + <after> + <actionGroup ref="StorefrontCustomerLogoutActionGroup" stepKey="logoutCustomer"/> + <deleteData createDataKey="customerWithoutAddress" stepKey="deleteCustomerWithoutAddress"/> + <deleteData createDataKey="customerWithAddress" stepKey="deleteCustomerWithAddress"/> + <deleteData createDataKey="customerWithDefaultAddress" stepKey="deleteCustomerWithDefaultAddress"/> + <!-- Set configs to default --> + <magentoCLI command="config:set {{EnableFlatRateConfigData.path}} {{EnableFlatRateConfigData.value}}" stepKey="restoreToDefaultFlatRate"/> + <magentoCLI command="config:set {{Disable3DSecureBraintree.path}} {{Disable3DSecureBraintree.value}}" stepKey="restoreToDefault3DSecureVerification"/> + <createData entity="DefaultBraintreeConfig" stepKey="defaultBraintreeConfig"/> + <createData entity="RollBackCustomBraintreeConfigurationData" stepKey="rollBackCustomBraintreeConfigurationData"/> + <!-- Remove created products/attributes --> + <deleteData createDataKey="createSimpleProduct" stepKey="deleteSimpleProduct"/> + <deleteData createDataKey="createVirtualProduct" stepKey="deleteVirtualProduct"/> + <deleteData createDataKey="createBundleProduct" stepKey="deleteBundleProduct"/> + <deleteData createDataKey="createGroupedProduct" stepKey="deleteGroupedProduct"/> + <!-- Remove Downloadable Product --> + <magentoCLI command="downloadable:domains:remove static.magento.com" stepKey="removeDownloadableDomain"/> + <deleteData createDataKey="createDownloadableProduct" stepKey="deleteDownloadableProduct"/> + <!-- Remove Configurable Product --> + <deleteData createDataKey="createConfigProductCreateConfigurableProduct" stepKey="deleteConfigProduct"/> + <deleteData createDataKey="createConfigProductAttributeCreateConfigurableProduct" stepKey="deleteConfigProductAttribute"/> + <deleteData createDataKey="createConfigChildProduct1CreateConfigurableProduct" stepKey="deleteConfigChildProduct1"/> + <deleteData createDataKey="createConfigChildProduct2CreateConfigurableProduct" stepKey="deleteConfigChildProduct2"/> + <!-- Reindex invalidated indices after product attribute has been created/deleted --> + <magentoCron groups="index" stepKey="reindexInvalidatedIndicesAfterTest"/> + </after> + <!-- 1. Ensure customer is a guest --> + <actionGroup ref="StorefrontCustomerLogoutActionGroup" stepKey="logoutCustomer"/> + <!-- 2. Browse all product pages and verify that the "Instant Purchase" button does not appear --> + <!-- Simple product --> + <actionGroup ref="StorefrontOpenProductEntityPageActionGroup" stepKey="openProductPage"> + <argument name="product" value="$createSimpleProduct$"/> + </actionGroup> + <dontSeeElement selector="{{StorefrontInstantPurchaseSection.instantPurchaseButton}}" stepKey="dontSeeButtonOnSimpleProductPage"/> + <!-- Virtual product --> + <actionGroup ref="StorefrontOpenProductEntityPageActionGroup" stepKey="openVirtualProductPage"> + <argument name="product" value="$createVirtualProduct$"/> + </actionGroup> + <dontSeeElement selector="{{StorefrontInstantPurchaseSection.instantPurchaseButton}}" stepKey="dontSeeButtonOnVirtualProductPage"/> + <!-- Downloadable Product --> + <actionGroup ref="StorefrontOpenProductEntityPageActionGroup" stepKey="openDownloadableProductPage"> + <argument name="product" value="$createDownloadableProduct$"/> + </actionGroup> + <dontSeeElement selector="{{StorefrontInstantPurchaseSection.instantPurchaseButton}}" stepKey="dontSeeButtonOnDownloadableProductPage"/> + <!-- Bundle Product --> + <actionGroup ref="StorefrontOpenProductEntityPageActionGroup" stepKey="openBundleProductPage"> + <argument name="product" value="$createBundleProduct$"/> + </actionGroup> + <waitForElementVisible selector="{{StorefrontBundleProductActionSection.customizeAndAddToCartButton}}" stepKey="waitForCustomizeAndAddToCartButton"/> + <click selector="{{StorefrontBundleProductActionSection.customizeAndAddToCartButton}}" stepKey="clickCustomizeAndAddToCart"/> + <waitForPageLoad stepKey="waitForBundleProductPageLoad"/> + <dontSeeElement selector="{{StorefrontInstantPurchaseSection.instantPurchaseButton}}" stepKey="dontSeeButtonOnBundleProductPage"/> + <!-- Grouped product --> + <actionGroup ref="StorefrontOpenProductEntityPageActionGroup" stepKey="openGroupedProductPage"> + <argument name="product" value="$createGroupedProduct$"/> + </actionGroup> + <dontSeeElement selector="{{StorefrontInstantPurchaseSection.instantPurchaseButton}}" stepKey="dontSeeButtonOnGroupedProductPage"/> + <!-- Configurable Product --> + <actionGroup ref="StorefrontOpenProductEntityPageActionGroup" stepKey="openConfigurableProductPage"> + <argument name="product" value="$createConfigProductCreateConfigurableProduct$"/> + </actionGroup> + <dontSeeElement selector="{{StorefrontInstantPurchaseSection.instantPurchaseButton}}" stepKey="dontSeeButtonOnConfigurableProductPage"/> + <!-- 3. Log in as a customer without address --> + <actionGroup ref="LoginToStorefrontActionGroup" stepKey="customerWithoutAddressLoginToStorefront"> + <argument name="Customer" value="$customerWithoutAddress$"/> + </actionGroup> + <!-- 4. Browse simple product page and check that Instant Purchase button does not show up --> + <actionGroup ref="StorefrontOpenProductEntityPageActionGroup" stepKey="openSimpleProductPageWithCustomerWithoutAddress"> + <argument name="product" value="$createSimpleProduct$"/> + </actionGroup> + <dontSeeElement selector="{{StorefrontInstantPurchaseSection.instantPurchaseButton}}" stepKey="dontSeeButtonOnSimpleProductAsCustomerWithoutAddress"/> + <!-- 5. Log in as a customer with address --> + <actionGroup ref="StorefrontCustomerLogoutActionGroup" stepKey="logoutCustomerWithoutAddress"/> + <actionGroup ref="LoginToStorefrontActionGroup" stepKey="customerWithAddressLoginToStorefront"> + <argument name="Customer" value="$customerWithAddress$"/> + </actionGroup> + <!-- 6. Browse simple product page and check that Instant Purchase button does not show up --> + <actionGroup ref="StorefrontOpenProductEntityPageActionGroup" stepKey="openSimpleProductPageWithCustomerWithAddress"> + <argument name="product" value="$createSimpleProduct$"/> + </actionGroup> + <dontSeeElement selector="{{StorefrontInstantPurchaseSection.instantPurchaseButton}}" stepKey="dontSeeButtonOnSimpleProductPageAsCustomerWithAddress"/> + <!-- 7. Log in as a customer with default address --> + <actionGroup ref="StorefrontCustomerLogoutActionGroup" stepKey="logoutCustomerWithAddress"/> + <actionGroup ref="LoginToStorefrontActionGroup" stepKey="customerWithDefaultAddressLoginToStorefront"> + <argument name="Customer" value="$customerWithDefaultAddress$"/> + </actionGroup> + <!-- 8. Browse simple product page and check that Instant Purchase button show up --> + <actionGroup ref="StorefrontOpenProductEntityPageActionGroup" stepKey="openSimpleProductPageWithCustomerWithDefaultAddress"> + <argument name="product" value="$createSimpleProduct$"/> + </actionGroup> + <waitForElementVisible selector="{{StorefrontInstantPurchaseSection.instantPurchaseButton}}" stepKey="waitForInstantPurchaseButton"/> + <!-- 9-10. Configure Braintree Payment Method(without Vault). Configure 3d Secure Verification --> + <magentoCLI command="config:set {{DisableVaultBraintree.path}} {{DisableVaultBraintree.value}}" stepKey="disableVault"/> + <magentoCLI command="config:set {{Enable3DSecureBraintree.path}} {{Enable3DSecureBraintree.value}}" stepKey="enable3DSecureVerification"/> + <!-- New session should be started --> + <actionGroup ref="StorefrontCustomerLogoutActionGroup" stepKey="logoutCustomerWithDefaultAddressAfter3dSecureEnabled"/> + <actionGroup ref="LoginToStorefrontActionGroup" stepKey="customerWithDefaultAddressLoginToStorefrontAfter3dSecureEnabled"> + <argument name="Customer" value="$customerWithDefaultAddress$"/> + </actionGroup> + <!-- 11. Browse simple product page and check that Instant Purchase button does not show up --> + <actionGroup ref="StorefrontOpenProductEntityPageActionGroup" stepKey="openSimpleProductPageWith3dSecureEnabled"> + <argument name="product" value="$createSimpleProduct$"/> + </actionGroup> + <dontSeeElement selector="{{StorefrontInstantPurchaseSection.instantPurchaseButton}}" stepKey="dontSeeButtonOnSimpleProductPageWith3dSecureEnabled"/> + <!-- 12. Disable all supported payment methods --> + <createData entity="DefaultBraintreeConfig" stepKey="restoreToDefaultBraintreeConfig"/> + <createData entity="RollBackCustomBraintreeConfigurationData" stepKey="restoreToDefaultBraintreeConfigurationData"/> + <!-- New session should be started --> + <actionGroup ref="StorefrontCustomerLogoutActionGroup" stepKey="logoutCustomerWithDefaultAddressAfterPaymentMethodDisabled"/> + <actionGroup ref="LoginToStorefrontActionGroup" stepKey="customerWithDefaultAddressLoginToStorefrontAfterPaymentMethodDisabled"> + <argument name="Customer" value="$customerWithDefaultAddress$"/> + </actionGroup> + <!-- 13. Browse simple product page and check that Instant Purchase button does not show up --> + <actionGroup ref="StorefrontOpenProductEntityPageActionGroup" stepKey="openSimpleProductPageWhilePaymentMethodDisabled"> + <argument name="product" value="$createSimpleProduct$"/> + </actionGroup> + <dontSeeElement selector="{{StorefrontInstantPurchaseSection.instantPurchaseButton}}" stepKey="dontSeeButtonOnSimpleProductWhilePaymentMethodDisabled"/> + <!-- 14. Reenable supported payment method (without 3d secure for Braintree) --> + <magentoCLI command="config:set {{Disable3DSecureBraintree.path}} {{Disable3DSecureBraintree.value}}" stepKey="disable3DSecureVerification"/> + <createData entity="BraintreeConfig" stepKey="reenableBraintreePayment"/> + <createData entity="CustomBraintreeConfigurationData" stepKey="reenableBraintreeAndVault"/> + <!-- New session should be started --> + <actionGroup ref="StorefrontCustomerLogoutActionGroup" stepKey="logoutCustomerWithDefaultAddressAfterReenablePaymentMethod"/> + <actionGroup ref="LoginToStorefrontActionGroup" stepKey="customerWithDefaultAddressLoginToStorefrontAfterReenablePaymentMethod"> + <argument name="Customer" value="$customerWithDefaultAddress$"/> + </actionGroup> + <!-- 15. Browse simple product page and check that Instant Purchase button show up --> + <actionGroup ref="StorefrontOpenProductEntityPageActionGroup" stepKey="openSimpleProductPageWithReenabledPaymentMethod"> + <argument name="product" value="$createSimpleProduct$"/> + </actionGroup> + <waitForElementVisible selector="{{StorefrontInstantPurchaseSection.instantPurchaseButton}}" stepKey="waitForInstantPurchaseButtonWithReenabledPaymentMethod"/> + <!-- 16. Disable shipping method for customer with default address --> + <magentoCLI command="config:set {{DisableFlatRateConfigData.path}} {{DisableFlatRateConfigData.value}}" stepKey="disableFlatRate"/> + <!-- New session should be started --> + <actionGroup ref="StorefrontCustomerLogoutActionGroup" stepKey="logoutCustomerWithDefaultAddressAfterFlatRateDisabled"/> + <actionGroup ref="LoginToStorefrontActionGroup" stepKey="customerWithDefaultAddressLoginToStorefrontAfterFlatRateDisabled"> + <argument name="Customer" value="$customerWithDefaultAddress$"/> + </actionGroup> + <!-- 17. Browse simple product page and check that Instant Purchase button does not show up --> + <actionGroup ref="StorefrontOpenProductEntityPageActionGroup" stepKey="openSimpleProductPageWhileFlatRateDisabled"> + <argument name="product" value="$createSimpleProduct$"/> + </actionGroup> + <dontSeeElement selector="{{StorefrontInstantPurchaseSection.instantPurchaseButton}}" stepKey="dontSeeButtonOnSimpleProductPageWhileFlatRateDisabled"/> + </test> +</tests> diff --git a/app/code/Magento/InstantPurchase/Test/Mftf/Test/StorefrontInstantPurchaseFunctionalityTest.xml b/app/code/Magento/InstantPurchase/Test/Mftf/Test/StorefrontInstantPurchaseFunctionalityTest.xml new file mode 100644 index 0000000000000..4283f42660f1f --- /dev/null +++ b/app/code/Magento/InstantPurchase/Test/Mftf/Test/StorefrontInstantPurchaseFunctionalityTest.xml @@ -0,0 +1,173 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + /** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +--> + +<tests xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/testSchema.xsd"> + <test name="StorefrontInstantPurchaseFunctionalityTest"> + <annotations> + <features value="InstantPurchase"/> + <stories value="Using Instant Purchase"/> + <title value="Checks that Instant Purchase functionality works fine"/> + <description value="Checks that customer with different billing and shipping addresses work with Instant Purchase functionality fine"/> + <useCaseId value="MAGETWO-90898"/> + <testCaseId value="MC-25924"/> + <severity value="CRITICAL"/> + <group value="instant_purchase"/> + <group value="vault"/> + <group value="braintree"/> + </annotations> + <before> + <magentoCLI command="downloadable:domains:add" arguments="example.com static.magento.com" stepKey="addDownloadableDomain"/> + <!-- Configure Braintree payment method --> + <createData entity="BraintreeConfig" stepKey="configureBraintreePayment"/> + <!-- Enable Braintree with Vault --> + <createData entity="CustomBraintreeConfigurationData" stepKey="enableBraintreeAndVault"/> + <createData entity="Simple_US_Customer_Multiple_Addresses" stepKey="createCustomer"/> + <!-- Create all product variations --> + <createData entity="SimpleProduct2" stepKey="createSimpleProduct"/> + <createData entity="VirtualProduct" stepKey="createVirtualProduct"/> + <actionGroup ref="AdminCreateApiConfigurableProductActionGroup" stepKey="createConfigurableProduct"/> + <!-- Create Bundle Product --> + <createData entity="ApiFixedBundleProduct" stepKey="createBundleProduct"/> + <createData entity="DropDownBundleOption" stepKey="createBundleOption"> + <requiredEntity createDataKey="createBundleProduct"/> + </createData> + <createData entity="ApiBundleLink" stepKey="createBundleLink"> + <requiredEntity createDataKey="createBundleProduct"/> + <requiredEntity createDataKey="createBundleOption"/> + <requiredEntity createDataKey="createSimpleProduct"/> + </createData> + <!-- Create Downloadable Product --> + <createData entity="ApiDownloadableProduct" stepKey="createDownloadableProduct"/> + <createData entity="downloadableLink1" stepKey="addDownloadableLink"> + <requiredEntity createDataKey="createDownloadableProduct"/> + </createData> + <!-- Create Grouped Product --> + <createData entity="ApiGroupedProduct" stepKey="createGroupedProduct"/> + <createData entity="OneSimpleProductLink" stepKey="createLinkForGroupedProduct"> + <requiredEntity createDataKey="createGroupedProduct"/> + <requiredEntity createDataKey="createSimpleProduct"/> + </createData> + <!-- Log in as a customer --> + <actionGroup ref="LoginToStorefrontActionGroup" stepKey="customerLoginToStorefront"> + <argument name="Customer" value="$createCustomer$"/> + </actionGroup> + <!-- Customer placed order from storefront with payment method --> + <actionGroup ref="AddSimpleProductToCartActionGroup" stepKey="addProductToCart"> + <argument name="product" value="$createSimpleProduct$"/> + </actionGroup> + <actionGroup ref="GoToCheckoutFromMinicartActionGroup" stepKey="goToCheckoutFromMinicart"/> + <actionGroup ref="StorefrontSetShippingMethodActionGroup" stepKey="setShippingMethodFlatRate"/> + <actionGroup ref="StorefrontCheckoutClickNextOnShippingStepActionGroup" stepKey="goToCheckoutPaymentStep"/> + <!-- Fill Braintree card data --> + <click selector="{{BraintreeConfigurationPaymentSection.creditCart}}" stepKey="selectBraintreePaymentMethod"/> + <waitForPageLoad stepKey="waitForBraintreeFormLoad"/> + <scrollTo selector="{{BraintreeConfigurationPaymentSection.creditCart}}" stepKey="scrollToCreditCardSection"/> + <actionGroup ref="StorefrontFillCartDataActionGroup" stepKey="fillCardData"> + <argument name="cartData" value="PaymentAndShippingInfo"/> + </actionGroup> + <waitForPageLoad stepKey="waitForFillCardData"/> + <checkOption selector="{{StorefrontOnePageCheckoutPaymentSection.saveForLaterUse}}" stepKey="checkSaveForLaterUse"/> + <actionGroup ref="CheckoutPlaceOrderActionGroup" stepKey="clickOnPlaceOrder"> + <argument name="orderNumberMessage" value="CONST.successCheckoutOrderNumberMessage"/> + <argument name="emailYouMessage" value="CONST.successCheckoutEmailYouMessage"/> + </actionGroup> + </before> + <after> + <actionGroup ref="StorefrontCustomerLogoutActionGroup" stepKey="logoutCustomer"/> + <deleteData createDataKey="createCustomer" stepKey="deleteCustomer"/> + <!-- Set configs to default --> + <createData entity="DefaultBraintreeConfig" stepKey="defaultBraintreeConfig"/> + <createData entity="RollBackCustomBraintreeConfigurationData" stepKey="rollBackCustomBraintreeConfigurationData"/> + <!-- Remove created products/attributes --> + <deleteData createDataKey="createSimpleProduct" stepKey="deleteSimpleProduct"/> + <deleteData createDataKey="createVirtualProduct" stepKey="deleteVirtualProduct"/> + <deleteData createDataKey="createBundleProduct" stepKey="deleteBundleProduct"/> + <deleteData createDataKey="createGroupedProduct" stepKey="deleteGroupedProduct"/> + <!-- Remove Downloadable Product --> + <magentoCLI command="downloadable:domains:remove static.magento.com" stepKey="removeDownloadableDomain"/> + <deleteData createDataKey="createDownloadableProduct" stepKey="deleteDownloadableProduct"/> + <!-- Remove Configurable Product --> + <deleteData createDataKey="createConfigProductCreateConfigurableProduct" stepKey="deleteConfigProduct"/> + <deleteData createDataKey="createConfigProductAttributeCreateConfigurableProduct" stepKey="deleteConfigProductAttribute"/> + <deleteData createDataKey="createConfigChildProduct1CreateConfigurableProduct" stepKey="deleteConfigChildProduct1"/> + <deleteData createDataKey="createConfigChildProduct2CreateConfigurableProduct" stepKey="deleteConfigChildProduct2"/> + <!-- Reindex invalidated indices after product attribute has been created/deleted --> + <magentoCron groups="index" stepKey="reindexInvalidatedIndices"/> + </after> + <!-- 1. Browse all product page and verify that the "Instant Purchase" button appears --> + <!-- Virtual product --> + <amOnPage url="{{StorefrontProductPage.url($createVirtualProduct.custom_attributes[url_key]$)}}" stepKey="openVirtualProductPage"/> + <waitForElementVisible selector="{{StorefrontInstantPurchaseSection.instantPurchaseButton}}" stepKey="waitForButtonOnVirtualProductPage"/> + <!-- Downloadable Product --> + <amOnPage url="{{StorefrontProductPage.url($createDownloadableProduct.custom_attributes[url_key]$)}}" stepKey="openDownloadableProductPage"/> + <waitForElementVisible selector="{{StorefrontInstantPurchaseSection.instantPurchaseButton}}" stepKey="waitForButtonOnDownloadableProductPage"/> + <!-- Bundle Product --> + <amOnPage url="{{StorefrontProductPage.url($createBundleProduct.custom_attributes[url_key]$)}}" stepKey="openBundleProductPage"/> + <waitForElementVisible selector="{{StorefrontBundleProductActionSection.customizeAndAddToCartButton}}" stepKey="waitForCustomizeAndAddToCartButton"/> + <click selector="{{StorefrontBundleProductActionSection.customizeAndAddToCartButton}}" stepKey="clickCustomizeAndAddToCart"/> + <waitForElementVisible selector="{{StorefrontInstantPurchaseSection.instantPurchaseButton}}" stepKey="waitForButtonOnBundleProductPage"/> + <!-- Grouped product --> + <amOnPage url="{{StorefrontProductPage.url($createGroupedProduct.custom_attributes[url_key]$)}}" stepKey="openGroupedProductPage"/> + <waitForElementVisible selector="{{StorefrontInstantPurchaseSection.instantPurchaseButton}}" stepKey="waitForButtonOnGroupedProductPage"/> + <!-- Configurable Product --> + <amOnPage url="{{StorefrontProductPage.url($createConfigProductCreateConfigurableProduct.custom_attributes[url_key]$)}}" stepKey="openConfigurableProductPage"/> + <waitForElementVisible selector="{{StorefrontInstantPurchaseSection.instantPurchaseButton}}" stepKey="waitForButtonOnConfigurableProductPage"/> + <!-- 2. Click on "Instant Purchase" and assert information --> + <amOnPage url="{{StorefrontProductPage.url($createSimpleProduct.custom_attributes[url_key]$)}}" stepKey="openSimpleProductPage"/> + <waitForElementVisible selector="{{StorefrontInstantPurchaseSection.instantPurchaseButton}}" stepKey="waitForInstantPurchaseButton"/> + <actionGroup ref="AssertStorefrontInstantPurchaseConfirmationDataActionGroup" stepKey="assertInstantPurchasePopupData"> + <argument name="shippingStreet" value="{{US_Address_NY.street[0]}}"/> + <argument name="billingStreet" value="{{US_Address_NY.street[0]}}"/> + <argument name="cardEnding" value="{{StoredPaymentMethods.cardNumberEnding}}"/> + </actionGroup> + <!-- 3. Confirm Instant Purchase --> + <click selector="{{ModalConfirmationSection.OkButton}}" stepKey="placeOrderAgain"/> + <waitForElementVisible selector="{{StorefrontMessagesSection.success}}" stepKey="waitForSuccessMessage"/> + <see userInput="Your order number is:" selector="{{StorefrontMessagesSection.success}}" stepKey="seePlaceOrderSuccessMessage"/> + <!-- 4. Customer changes his default address --> + <amOnPage url="{{StorefrontCustomerAddressesPage.url}}" stepKey="goToAddressPage"/> + <click selector="{{StorefrontCustomerAddressesSection.editAdditionalAddress('1')}}" stepKey="clickOnEditAdditionalAddressButton"/> + <checkOption selector="{{StorefrontCustomerAddressFormSection.useAsDefaultBillingAddressCheckBox}}" stepKey="checkUseAsDefaultBillingAddressCheckbox"/> + <actionGroup ref="AdminSaveCustomerAddressActionGroup" stepKey="saveAddress"/> + <!-- 5.1 Customer places a new order from the storefront with new payment credentials --> + <actionGroup ref="AddSimpleProductToCartActionGroup" stepKey="addProductToCartAgain"> + <argument name="product" value="$createSimpleProduct$"/> + </actionGroup> + <actionGroup ref="GoToCheckoutFromMinicartActionGroup" stepKey="goToCheckoutFromMinicartAgain"/> + <actionGroup ref="StorefrontSetShippingMethodActionGroup" stepKey="setShippingMethodFlatRateAgain"/> + <click selector="{{CheckoutShippingMethodsSection.shipHereButton}}" stepKey="changeShippingAddress"/> + <actionGroup ref="StorefrontCheckoutClickNextOnShippingStepActionGroup" stepKey="goToCheckoutPaymentStepAgain"/> + <!-- Fill Braintree card data --> + <click selector="{{BraintreeConfigurationPaymentSection.creditCart}}" stepKey="selectBraintreePaymentMethodAgain"/> + <waitForPageLoad stepKey="waitForBraintreeFormLoadAgain"/> + <scrollTo selector="{{BraintreeConfigurationPaymentSection.creditCart}}" stepKey="scrollToCreditCardSectionAgain"/> + <actionGroup ref="StorefrontFillCartDataActionGroup" stepKey="fillCardDataAgain"> + <argument name="cartData" value="VisaDefaultCard"/> + </actionGroup> + <waitForPageLoad stepKey="waitForFillCardDataAgain"/> + <!-- 5.2 Customer save this payment method --> + <checkOption selector="{{StorefrontOnePageCheckoutPaymentSection.saveForLaterUse}}" stepKey="checkSaveForLaterUseAgain"/> + <actionGroup ref="CheckoutPlaceOrderActionGroup" stepKey="clickOnPlaceOrderAgain"> + <argument name="orderNumberMessage" value="CONST.successCheckoutOrderNumberMessage"/> + <argument name="emailYouMessage" value="CONST.successCheckoutEmailYouMessage"/> + </actionGroup> + <!-- 6. Customer opens simple product page --> + <amOnPage url="{{StorefrontProductPage.url($createSimpleProduct.custom_attributes[url_key]$)}}" stepKey="openSimpleProductPageAgain"/> + <waitForElementVisible selector="{{StorefrontInstantPurchaseSection.instantPurchaseButton}}" stepKey="waitForInstantPurchaseButtonAgain"/> + <!-- 7. Click on "Instant Purchase" and verify that information are different from previous --> + <actionGroup ref="AssertStorefrontInstantPurchaseConfirmationDataActionGroup" stepKey="assertInstantPurchasePopupDataAgain"> + <argument name="shippingStreet" value="{{US_Address_NY.street[0]}}"/> + <argument name="billingStreet" value="{{UK_Not_Default_Address.street[0]}}"/> + <argument name="cardEnding" value="{{VisaDefaultCardInfo.cardNumberEnding}}"/> + </actionGroup> + <!-- 8. Confirm Instant Purchase --> + <click selector="{{ModalConfirmationSection.OkButton}}" stepKey="placeOrderFinalTime"/> + <waitForElementVisible selector="{{StorefrontMessagesSection.success}}" stepKey="waitForSuccessMessageAgain"/> + <see userInput="Your order number is:" selector="{{StorefrontMessagesSection.success}}" stepKey="seePlaceOrderSuccessMessageAgain"/> + </test> +</tests> diff --git a/app/code/Magento/InstantPurchase/etc/adminhtml/system.xml b/app/code/Magento/InstantPurchase/etc/adminhtml/system.xml index 76785c023ed0b..d87c91b293717 100644 --- a/app/code/Magento/InstantPurchase/etc/adminhtml/system.xml +++ b/app/code/Magento/InstantPurchase/etc/adminhtml/system.xml @@ -17,6 +17,9 @@ </field> <field id="button_text" translate="label" type="text" sortOrder="1" showInDefault="1" showInWebsite="1" showInStore="1" canRestore="1"> <label>Button Text</label> + <depends> + <field id="active">1</field> + </depends> </field> </group> </section> diff --git a/app/code/Magento/InstantPurchase/registration.php b/app/code/Magento/InstantPurchase/registration.php index 74b4f3e92cf44..1f2fce8c12c07 100644 --- a/app/code/Magento/InstantPurchase/registration.php +++ b/app/code/Magento/InstantPurchase/registration.php @@ -4,6 +4,6 @@ * See COPYING.txt for license details. */ -use \Magento\Framework\Component\ComponentRegistrar; +use Magento\Framework\Component\ComponentRegistrar; ComponentRegistrar::register(ComponentRegistrar::MODULE, 'Magento_InstantPurchase', __DIR__); diff --git a/app/code/Magento/Integration/Block/Adminhtml/Integration/Activate/Permissions/Tab/Webapi.php b/app/code/Magento/Integration/Block/Adminhtml/Integration/Activate/Permissions/Tab/Webapi.php index 3c719a5814615..2d323fea34e7d 100644 --- a/app/code/Magento/Integration/Block/Adminhtml/Integration/Activate/Permissions/Tab/Webapi.php +++ b/app/code/Magento/Integration/Block/Adminhtml/Integration/Activate/Permissions/Tab/Webapi.php @@ -105,7 +105,7 @@ protected function _construct() } /** - * {@inheritdoc} + * @inheritdoc */ public function canShowTab() { @@ -116,7 +116,7 @@ public function canShowTab() } /** - * {@inheritdoc} + * @inheritdoc * * @codeCoverageIgnore */ @@ -126,7 +126,7 @@ public function getTabLabel() } /** - * {@inheritdoc} + * @inheritdoc * * @codeCoverageIgnore */ @@ -136,7 +136,7 @@ public function getTabTitle() } /** - * {@inheritdoc} + * @inheritdoc * * @codeCoverageIgnore */ @@ -222,13 +222,13 @@ public function isTreeEmpty() */ protected function _getAllResourceIds(array $resources) { - $resourceIds = []; + $resourceIds = [[]]; foreach ($resources as $resource) { - $resourceIds[] = $resource['id']; + $resourceIds[] = [$resource['id']]; if (isset($resource['children'])) { - $resourceIds = array_merge($resourceIds, $this->_getAllResourceIds($resource['children'])); + $resourceIds[] = $this->_getAllResourceIds($resource['children']); } } - return $resourceIds; + return array_merge(...$resourceIds); } } diff --git a/app/code/Magento/Integration/Test/Mftf/ActionGroup/AdminFillIntegrationFormActionGroup.xml b/app/code/Magento/Integration/Test/Mftf/ActionGroup/AdminFillIntegrationFormActionGroup.xml index 85e4efb495955..6d4e4ed39f6e2 100644 --- a/app/code/Magento/Integration/Test/Mftf/ActionGroup/AdminFillIntegrationFormActionGroup.xml +++ b/app/code/Magento/Integration/Test/Mftf/ActionGroup/AdminFillIntegrationFormActionGroup.xml @@ -20,6 +20,7 @@ <waitForPageLoad stepKey="waitForApiTab" /> <selectOption userInput="{{integration.resourceAccess}}" selector="{{AdminNewIntegrationFormSection.resourceAccess}}" stepKey="selectResourceAccess" /> - <performOn stepKey="checkNeededResources" selector="{{AdminNewIntegrationFormSection.resourceTree}}" function="function($I,$apiResources={{integration.resources}}){foreach($apiResources as $apiResource){$I->conditionalClick('//li[@data-id=\'' . $apiResource . '\']//*[@class=\'jstree-checkbox\']','//li[@data-id=\'' . $apiResource . '\' and contains(@class, \'jstree-checked\')]',false);}}" /> + <!--TODO waiting for custom action functionality with MQE-1964 --> + <!--<performOn stepKey="checkNeededResources" selector="{{AdminNewIntegrationFormSection.resourceTree}}" function="function($I,$apiResources={{integration.resources}}){foreach($apiResources as $apiResource){$I->conditionalClick('//li[@data-id=\'' . $apiResource . '\']//*[@class=\'jstree-checkbox\']','//li[@data-id=\'' . $apiResource . '\' and contains(@class, \'jstree-checked\')]',false);}}" />--> </actionGroup> </actionGroups> diff --git a/app/code/Magento/Integration/Test/Mftf/Test/AdminCreateIntegrationEntityWithDuplicatedNameTest.xml b/app/code/Magento/Integration/Test/Mftf/Test/AdminCreateIntegrationEntityWithDuplicatedNameTest.xml new file mode 100644 index 0000000000000..f55e4e37585ba --- /dev/null +++ b/app/code/Magento/Integration/Test/Mftf/Test/AdminCreateIntegrationEntityWithDuplicatedNameTest.xml @@ -0,0 +1,50 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + /** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +--> + +<tests xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/testSchema.xsd"> + <test name="AdminCreateIntegrationEntityWithDuplicatedNameTest"> + <annotations> + <features value="Integration"/> + <stories value="Creating System Integration With Duplicated Name"/> + <title value="Admin System Integration With Duplicated Name"/> + <description value="Admin Creates New Integration With Duplicated Name"/> + <severity value="MAJOR"/> + <testCaseId value="MC-19889"/> + <group value="integration"/> + <group value="mtf_migrated"/> + </annotations> + <before> + <actionGroup ref="AdminLoginActionGroup" stepKey="LoginAsAdmin"/> + </before> + <after> + <actionGroup ref="AdminLogoutActionGroup" stepKey="logout"/> + </after> + + <actionGroup ref="AdminNavigateMenuActionGroup" stepKey="navigateToIntegrationsPage"> + <argument name="menuUiId" value="{{AdminMenuSystem.dataUiId}}"/> + <argument name="submenuUiId" value="{{AdminMenuSystemExtensionsIntegrations.dataUiId}}"/> + </actionGroup> + <actionGroup ref="AdminNavigateToCreateIntegrationPageActionGroup" stepKey="clickAddNewIntegrationButton"/> + <actionGroup ref="AdminCreatesNewIntegrationActionGroup" stepKey="createNewIntegration"> + <argument name="name" value="Integration1"/> + <argument name="password" value="{{_ENV.MAGENTO_ADMIN_PASSWORD}}"/> + </actionGroup> + <actionGroup ref="AdminSubmitNewIntegrationFormActionGroup" stepKey="submitTheForm"/> + <actionGroup ref="AdminNavigateToCreateIntegrationPageActionGroup" stepKey="clickAddNewIntegrationButtonSecondTime"/> + <actionGroup ref="AdminCreatesNewIntegrationActionGroup" stepKey="createNewIntegrationWithDuplicatedName"> + <argument name="name" value="Integration1"/> + <argument name="password" value="{{_ENV.MAGENTO_ADMIN_PASSWORD}}"/> + </actionGroup> + <actionGroup ref="AdminSubmitNewIntegrationFormActionGroup" stepKey="submitTheFormWithDuplicatedName"/> + <actionGroup ref="AssertAdminMessageCreateIntegrationEntityActionGroup" stepKey="seeErrorMessage"> + <argument name="message" value="The integration with name "Integration1" exists."/> + <argument value="error" name="messageType"/> + </actionGroup> + </test> +</tests> diff --git a/app/code/Magento/Integration/Test/Mftf/Test/AdminDeleteIntegrationEntityTest.xml b/app/code/Magento/Integration/Test/Mftf/Test/AdminDeleteIntegrationEntityTest.xml index 6f46bbf99d218..eb6c946a03bb5 100644 --- a/app/code/Magento/Integration/Test/Mftf/Test/AdminDeleteIntegrationEntityTest.xml +++ b/app/code/Magento/Integration/Test/Mftf/Test/AdminDeleteIntegrationEntityTest.xml @@ -14,12 +14,14 @@ <stories value="System Integration"/> <title value="Admin system integration"/> <description value="Admin Deletes Created Integration"/> + <severity value="MAJOR"/> + <testCaseId value="MC-28027"/> <group value="integration"/> <group value="mtf_migrated"/> </annotations> <before> <!-- Login As Admin --> - <actionGroup ref="LoginAsAdmin" stepKey="LoginAsAdmin"/> + <actionGroup ref="AdminLoginActionGroup" stepKey="LoginAsAdmin"/> <!-- Navigate To Integrations Page --> <actionGroup ref="AdminNavigateMenuActionGroup" stepKey="navigateToIntegrationsPage"> <argument name="menuUiId" value="{{AdminMenuSystem.dataUiId}}"/> @@ -36,7 +38,7 @@ <actionGroup ref="AdminSubmitNewIntegrationFormActionGroup" stepKey="submitTheForm"/> </before> <after> - <actionGroup ref="logout" stepKey="logout"/> + <actionGroup ref="AdminLogoutActionGroup" stepKey="logout"/> </after> <!-- TEST BODY --> diff --git a/app/code/Magento/Integration/Test/Mftf/Test/AdminSystemIntegrationsNavigateMenuTest.xml b/app/code/Magento/Integration/Test/Mftf/Test/AdminSystemIntegrationsNavigateMenuTest.xml index 3bd149d222c0e..41f7f3225255d 100644 --- a/app/code/Magento/Integration/Test/Mftf/Test/AdminSystemIntegrationsNavigateMenuTest.xml +++ b/app/code/Magento/Integration/Test/Mftf/Test/AdminSystemIntegrationsNavigateMenuTest.xml @@ -20,10 +20,10 @@ <group value="mtf_migrated"/> </annotations> <before> - <actionGroup ref="LoginAsAdmin" stepKey="LoginAsAdmin"/> + <actionGroup ref="AdminLoginActionGroup" stepKey="LoginAsAdmin"/> </before> <after> - <actionGroup ref="logout" stepKey="logout"/> + <actionGroup ref="AdminLogoutActionGroup" stepKey="logout"/> </after> <actionGroup ref="AdminNavigateMenuActionGroup" stepKey="navigateToIntegrationsPage"> <argument name="menuUiId" value="{{AdminMenuSystem.dataUiId}}"/> diff --git a/app/code/Magento/Integration/Test/Unit/Model/Oauth/ConsumerTest.php b/app/code/Magento/Integration/Test/Unit/Model/Oauth/ConsumerTest.php index c6b7ce22fc39c..29b96f229b4b1 100644 --- a/app/code/Magento/Integration/Test/Unit/Model/Oauth/ConsumerTest.php +++ b/app/code/Magento/Integration/Test/Unit/Model/Oauth/ConsumerTest.php @@ -6,7 +6,7 @@ namespace Magento\Integration\Test\Unit\Model\Oauth; use Magento\Framework\Url\Validator as UrlValidator; -use Zend\Validator\Uri as ZendUriValidator; +use Laminas\Validator\Uri as LaminasUriValidator; use Magento\Integration\Model\Oauth\Consumer\Validator\KeyLength; /** @@ -85,7 +85,7 @@ protected function setUp() $this->keyLengthValidator = new KeyLength(); - $this->urlValidator = new UrlValidator(new ZendUriValidator()); + $this->urlValidator = new UrlValidator(new LaminasUriValidator()); $this->oauthDataMock = $this->createPartialMock( \Magento\Integration\Helper\Oauth\Data::class, @@ -111,8 +111,8 @@ protected function setUp() ); $this->validDataArray = [ - 'key' => md5(uniqid()), - 'secret' => md5(uniqid()), + 'key' => md5(uniqid()), // phpcs:ignore Magento2.Security.InsecureFunction + 'secret' => md5(uniqid()), // phpcs:ignore Magento2.Security.InsecureFunction 'callback_url' => 'http://example.com/callback', 'rejected_callback_url' => 'http://example.com/rejectedCallback' ]; diff --git a/app/code/Magento/Integration/etc/adminhtml/system.xml b/app/code/Magento/Integration/etc/adminhtml/system.xml index ddaae76700255..6ef569a1d8a2f 100644 --- a/app/code/Magento/Integration/etc/adminhtml/system.xml +++ b/app/code/Magento/Integration/etc/adminhtml/system.xml @@ -11,58 +11,58 @@ <label>OAuth</label> <tab>service</tab> <resource>Magento_Integration::config_oauth</resource> - <group id="access_token_lifetime" translate="label" type="text" sortOrder="100" showInDefault="1" showInWebsite="0" showInStore="0"> + <group id="access_token_lifetime" translate="label" type="text" sortOrder="100" showInDefault="1"> <label>Access Token Expiration</label> - <field id="customer" translate="label comment" type="text" sortOrder="30" showInDefault="1" showInWebsite="0" showInStore="0" canRestore="1"> + <field id="customer" translate="label comment" type="text" sortOrder="30" showInDefault="1" canRestore="1"> <label>Customer Token Lifetime (hours)</label> <comment>We will disable this feature if the value is empty.</comment> <validate>required-entry validate-zero-or-greater validate-number</validate> </field> - <field id="admin" translate="label comment" type="text" sortOrder="60" showInDefault="1" showInWebsite="0" showInStore="0" canRestore="1"> + <field id="admin" translate="label comment" type="text" sortOrder="60" showInDefault="1" canRestore="1"> <label>Admin Token Lifetime (hours)</label> <comment>We will disable this feature if the value is empty.</comment> <validate>required-entry validate-zero-or-greater validate-number</validate> </field> </group> - <group id="cleanup" translate="label" type="text" sortOrder="300" showInDefault="1" showInWebsite="0" showInStore="0"> + <group id="cleanup" translate="label" type="text" sortOrder="300" showInDefault="1"> <label>Cleanup Settings</label> - <field id="cleanup_probability" translate="label comment" type="text" sortOrder="10" showInDefault="1" showInWebsite="0" showInStore="0" canRestore="1"> + <field id="cleanup_probability" translate="label comment" type="text" sortOrder="10" showInDefault="1" canRestore="1"> <label>Cleanup Probability</label> <comment>Integer. Launch cleanup in X OAuth requests. 0 (not recommended) - to disable cleanup</comment> <validate>required-entry validate-zero-or-greater validate-digits</validate> </field> - <field id="expiration_period" translate="label comment" type="text" sortOrder="20" showInDefault="1" showInWebsite="0" showInStore="0" canRestore="1"> + <field id="expiration_period" translate="label comment" type="text" sortOrder="20" showInDefault="1" canRestore="1"> <label>Expiration Period</label> <comment>Cleanup entries older than X minutes.</comment> <validate>required-entry validate-zero-or-greater validate-number</validate> </field> </group> - <group id="consumer" translate="label" type="text" sortOrder="400" showInDefault="1" showInWebsite="0" showInStore="0"> + <group id="consumer" translate="label" type="text" sortOrder="400" showInDefault="1"> <label>Consumer Settings</label> - <field id="expiration_period" translate="label comment" type="text" sortOrder="30" showInDefault="1" showInWebsite="0" showInStore="0" canRestore="1"> + <field id="expiration_period" translate="label comment" type="text" sortOrder="30" showInDefault="1" canRestore="1"> <label>Expiration Period</label> <comment>Consumer key/secret will expire if not used within X seconds after Oauth token exchange starts.</comment> <validate>required-entry validate-zero-or-greater validate-number</validate> </field> - <field id="post_maxredirects" translate="label comment" type="text" sortOrder="30" showInDefault="1" showInWebsite="0" showInStore="0" canRestore="1"> + <field id="post_maxredirects" translate="label comment" type="text" sortOrder="30" showInDefault="1" canRestore="1"> <label>OAuth consumer credentials HTTP Post maxredirects</label> <comment>Number of maximum redirects for OAuth consumer credentials Post request.</comment> <validate>required-entry validate-zero-or-greater validate-digits</validate> </field> - <field id="post_timeout" translate="label comment" type="text" sortOrder="30" showInDefault="1" showInWebsite="0" showInStore="0" canRestore="1"> + <field id="post_timeout" translate="label comment" type="text" sortOrder="30" showInDefault="1" canRestore="1"> <label>OAuth consumer credentials HTTP Post timeout</label> <comment>Timeout for OAuth consumer credentials Post request within X seconds.</comment> <validate>required-entry validate-zero-or-greater validate-number</validate> </field> </group> - <group id="authentication_lock" translate="label" type="text" sortOrder="400" showInDefault="1" showInWebsite="0" showInStore="0"> + <group id="authentication_lock" translate="label" type="text" sortOrder="400" showInDefault="1"> <label>Authentication Locks</label> - <field id="max_failures_count" translate="label comment" type="text" sortOrder="30" showInDefault="1" showInWebsite="0" showInStore="0" canRestore="1"> + <field id="max_failures_count" translate="label comment" type="text" sortOrder="30" showInDefault="1" canRestore="1"> <label>Maximum Login Failures to Lock Out Account</label> <comment>Maximum Number of authentication failures to lock out account.</comment> <validate>required-entry validate-zero-or-greater validate-digits</validate> </field> - <field id="timeout" translate="label comment" type="text" sortOrder="30" showInDefault="1" showInWebsite="0" showInStore="0" canRestore="1"> + <field id="timeout" translate="label comment" type="text" sortOrder="30" showInDefault="1" canRestore="1"> <label>Lockout Time (seconds)</label> <comment>Period of time in seconds after which account will be unlocked.</comment> <validate>required-entry validate-zero-or-greater validate-number</validate> diff --git a/app/code/Magento/Integration/registration.php b/app/code/Magento/Integration/registration.php index d73b6ae57eeb5..6d0fea09e2a40 100644 --- a/app/code/Magento/Integration/registration.php +++ b/app/code/Magento/Integration/registration.php @@ -4,6 +4,6 @@ * See COPYING.txt for license details. */ -use \Magento\Framework\Component\ComponentRegistrar; +use Magento\Framework\Component\ComponentRegistrar; ComponentRegistrar::register(ComponentRegistrar::MODULE, 'Magento_Integration', __DIR__); diff --git a/app/code/Magento/LayeredNavigation/Test/Mftf/Section/LayeredNavigationSection.xml b/app/code/Magento/LayeredNavigation/Test/Mftf/Section/LayeredNavigationSection/LayeredNavigationSection.xml similarity index 80% rename from app/code/Magento/LayeredNavigation/Test/Mftf/Section/LayeredNavigationSection.xml rename to app/code/Magento/LayeredNavigation/Test/Mftf/Section/LayeredNavigationSection/LayeredNavigationSection.xml index b3e0c430b12e7..944c78e9f7145 100644 --- a/app/code/Magento/LayeredNavigation/Test/Mftf/Section/LayeredNavigationSection.xml +++ b/app/code/Magento/LayeredNavigation/Test/Mftf/Section/LayeredNavigationSection/LayeredNavigationSection.xml @@ -5,9 +5,8 @@ * See COPYING.txt for license details. */ --> - <sections xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" - xsi:noNamespaceSchemaLocation="urn:magento:mftf:Page/etc/SectionObject.xsd"> + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Page/etc/SectionObject.xsd"> <section name="LayeredNavigationSection"> <element name="filterOptionContent" type="text" selector="//div[contains(text(), '{{attribute}}')]//following-sibling::div//a[contains(text(), '{{option}}')]" parameterized="true"/> <element name="layeredNavigation" type="select" selector="#catalog_layered_navigation-head"/> @@ -18,8 +17,4 @@ <element name="PriceNavigationStep" type="button" selector="#catalog_layered_navigation_price_range_step"/> <element name="PriceNavigationStepSystemValue" type="button" selector="#catalog_layered_navigation_price_range_step_inherit"/> </section> - - <section name="StorefrontLayeredNavigationSection"> - <element name="shoppingOptionsByName" type="button" selector="//*[text()='Shopping Options']/..//*[contains(text(),'{{arg}}')]" parameterized="true"/> - </section> </sections> diff --git a/app/code/Magento/LayeredNavigation/Test/Mftf/Section/LayeredNavigationSection/StorefrontLayeredNavigationSection.xml b/app/code/Magento/LayeredNavigation/Test/Mftf/Section/LayeredNavigationSection/StorefrontLayeredNavigationSection.xml new file mode 100644 index 0000000000000..d3a3005c296b2 --- /dev/null +++ b/app/code/Magento/LayeredNavigation/Test/Mftf/Section/LayeredNavigationSection/StorefrontLayeredNavigationSection.xml @@ -0,0 +1,13 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + /** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +--> +<sections xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Page/etc/SectionObject.xsd"> + <section name="StorefrontLayeredNavigationSection"> + <element name="shoppingOptionsByName" type="button" selector="//*[text()='Shopping Options']/..//*[contains(text(),'{{arg}}')]" parameterized="true"/> + </section> +</sections> diff --git a/app/code/Magento/LayeredNavigation/Test/Mftf/Test/AdminSpecifyLayerNavigationConfigurationTest.xml b/app/code/Magento/LayeredNavigation/Test/Mftf/Test/AdminSpecifyLayerNavigationConfigurationTest.xml index fd8763891af93..80280178e4593 100644 --- a/app/code/Magento/LayeredNavigation/Test/Mftf/Test/AdminSpecifyLayerNavigationConfigurationTest.xml +++ b/app/code/Magento/LayeredNavigation/Test/Mftf/Test/AdminSpecifyLayerNavigationConfigurationTest.xml @@ -20,11 +20,11 @@ </annotations> <before> - <actionGroup ref="LoginActionGroup" stepKey="login"/> + <actionGroup ref="AdminLoginActionGroup" stepKey="login"/> </before> <after> - <actionGroup ref="logout" stepKey="logout"/> + <actionGroup ref="AdminLogoutActionGroup" stepKey="logout"/> </after> <!-- Configure Layered Navigation in Stores -> Configuration -> Catalog -> Layered Navigation --> diff --git a/app/code/Magento/LayeredNavigation/Test/Mftf/Test/ShopByButtonInMobile.xml b/app/code/Magento/LayeredNavigation/Test/Mftf/Test/ShopByButtonInMobile.xml deleted file mode 100644 index d2b462d0467a2..0000000000000 --- a/app/code/Magento/LayeredNavigation/Test/Mftf/Test/ShopByButtonInMobile.xml +++ /dev/null @@ -1,62 +0,0 @@ -<?xml version="1.0" encoding="UTF-8"?> -<!-- - /** - * Copyright © Magento, Inc. All rights reserved. - * See COPYING.txt for license details. - */ ---> - -<tests xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" - xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/testSchema.xsd"> - <test name="ShopByButtonInMobile"> - <annotations> - <features value="Layered Navigation"/> - <stories value="Storefront Shop By collapsible button in mobile themes"/> - <title value="Storefront Shop By collapsible button works in a mobile theme"/> - <description value="Storefront Shop By collapsible button should work in a mobile theme"/> - <severity value="CRITICAL"/> - <testCaseId value="MC-6092"/> - <group value="LayeredNavigation"/> - <group value="SearchEngineMysql"/> - </annotations> - <before> - <createData entity="productDropDownAttribute" stepKey="attribute"/> - <createData entity="productAttributeOption1" stepKey="option1"> - <requiredEntity createDataKey="attribute"/> - </createData> - <createData entity="AddToDefaultSet" stepKey="attributeSet"> - <requiredEntity createDataKey="attribute"/> - </createData> - <createData entity="SimpleProduct2" stepKey="simpleProduct1"/> - <createData entity="SimpleProduct2" stepKey="simpleProduct2"/> - <actionGroup ref="LoginAsAdmin" stepKey="loginAsAdmin"/> - </before> - <after> - <deleteData createDataKey="attribute" stepKey="deleteAttribute"/> - <deleteData createDataKey="simpleProduct1" stepKey="deleteSimpleProduct1"/> - <deleteData createDataKey="simpleProduct2" stepKey="deleteSimpleProduct2"/> - <actionGroup ref="logout" stepKey="logoutOfAdmin"/> - <resizeWindow width="1280" height="1024" stepKey="resizeWindowToDesktop"/> - </after> - <!-- Go to default attribute set edit page and add the product attribute to the set --> - <comment userInput="Go to default attribute set edit page and add the product attribute to the set" stepKey="commentAttributeSetEdit" /> - <amOnPage url="{{AdminProductAttributeSetEditPage.url}}/{{AddToDefaultSet.attributeSetId}}/" stepKey="onAttributeSetEdit"/> - <dragAndDrop selector1="{{AdminProductAttributeSetSection.attribute($$attribute.attribute[attribute_code]$$)}}" selector2="{{AdminProductAttributeSetSection.attribute('Product Details')}}" stepKey="assignTestAttributes"/> - <click selector="{{AdminProductAttributeSetSection.saveBtn}}" stepKey="clickAttributeSetSave"/> - <!-- Go to simple product edit page and set the product attribute to a value --> - <comment userInput="Go to simple product edit page and set the product attribute to a value" stepKey="commentProductAttributeEdit" /> - <amOnPage url="{{AdminProductEditPage.url($$simpleProduct1.id$$)}}" stepKey="goToEditPage"/> - <selectOption selector="{{AdminProductFormSection.customSelectField($$attribute.attribute[attribute_code]$$)}}" userInput="option1" stepKey="selectAttribute"/> - <actionGroup ref="SaveProductFormActionGroup" stepKey="saveSimpleProduct"/> - <!-- Check storefront mobile view for shop by button is functioning as expected --> - <comment userInput="Check storefront mobile view for shop by button is functioning as expected" stepKey="commentCheckShopByButton" /> - <amOnPage url="{{StorefrontHomePage.url}}" stepKey="goToHomePage"/> - <waitForPageLoad stepKey="waitForHomePageToLoad"/> - <submitForm selector="#search_mini_form" parameterArray="['q' => 'Test Simple Product']" stepKey="fillSearchBar" /> - <resizeWindow width="600" height="800" stepKey="resizeWindow"/> - <waitForPageLoad stepKey="waitForHomePageToLoad2"/> - <seeElement selector="{{StorefrontCategorySidebarMobileSection.shopByButton}}" stepKey="seeShopByButton"/> - <click selector="{{StorefrontCategorySidebarMobileSection.shopByButton}}" stepKey="clickShopByButton"/> - <seeElement selector="{{StorefrontCategorySidebarSection.filterOptionsTitle('$$attribute.attribute[default_label]$$')}}" stepKey="seeShopByOption"/> - </test> -</tests> diff --git a/app/code/Magento/LayeredNavigation/Test/Mftf/Test/ShopByButtonInMobileTest.xml b/app/code/Magento/LayeredNavigation/Test/Mftf/Test/ShopByButtonInMobileTest.xml new file mode 100644 index 0000000000000..d10564f1265bf --- /dev/null +++ b/app/code/Magento/LayeredNavigation/Test/Mftf/Test/ShopByButtonInMobileTest.xml @@ -0,0 +1,63 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + /** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +--> + +<tests xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/testSchema.xsd"> + <test name="ShopByButtonInMobileTest"> + <annotations> + <features value="Layered Navigation"/> + <stories value="Storefront Shop By collapsible button in mobile themes"/> + <title value="Storefront Shop By collapsible button works in a mobile theme"/> + <description value="Storefront Shop By collapsible button should work in a mobile theme"/> + <severity value="CRITICAL"/> + <testCaseId value="MC-6092"/> + <group value="LayeredNavigation"/> + <group value="SearchEngineMysql"/> + </annotations> + <before> + <createData entity="productDropDownAttribute" stepKey="attribute"/> + <createData entity="productAttributeOption1" stepKey="option1"> + <requiredEntity createDataKey="attribute"/> + </createData> + <createData entity="AddToDefaultSet" stepKey="attributeSet"> + <requiredEntity createDataKey="attribute"/> + </createData> + <createData entity="SimpleProduct2" stepKey="simpleProduct1"/> + <createData entity="SimpleProduct2" stepKey="simpleProduct2"/> + <actionGroup ref="AdminLoginActionGroup" stepKey="loginAsAdmin"/> + </before> + <after> + <deleteData createDataKey="attribute" stepKey="deleteAttribute"/> + <deleteData createDataKey="simpleProduct1" stepKey="deleteSimpleProduct1"/> + <deleteData createDataKey="simpleProduct2" stepKey="deleteSimpleProduct2"/> + <actionGroup ref="AdminLogoutActionGroup" stepKey="logoutOfAdmin"/> + <resizeWindow width="1280" height="1024" stepKey="resizeWindowToDesktop"/> + </after> + <!-- Go to default attribute set edit page and add the product attribute to the set --> + <comment userInput="Go to default attribute set edit page and add the product attribute to the set" stepKey="commentAttributeSetEdit" /> + <amOnPage url="{{AdminProductAttributeSetEditPage.url}}/{{AddToDefaultSet.attributeSetId}}/" stepKey="onAttributeSetEdit"/> + <dragAndDrop selector1="{{AdminProductAttributeSetSection.attribute($$attribute.attribute[attribute_code]$$)}}" selector2="{{AdminProductAttributeSetSection.attribute('Product Details')}}" stepKey="assignTestAttributes"/> + <click selector="{{AdminProductAttributeSetSection.saveBtn}}" stepKey="clickAttributeSetSave"/> + <!-- Go to simple product edit page and set the product attribute to a value --> + <comment userInput="Go to simple product edit page and set the product attribute to a value" stepKey="commentProductAttributeEdit" /> + <actionGroup ref="AdminProductPageOpenByIdActionGroup" stepKey="goToEditPage"> + <argument name="productId" value="$$simpleProduct1.id$$"/> + </actionGroup> + <selectOption selector="{{AdminProductFormSection.customSelectField($$attribute.attribute[attribute_code]$$)}}" userInput="option1" stepKey="selectAttribute"/> + <actionGroup ref="SaveProductFormActionGroup" stepKey="saveSimpleProduct"/> + <!-- Check storefront mobile view for shop by button is functioning as expected --> + <comment userInput="Check storefront mobile view for shop by button is functioning as expected" stepKey="commentCheckShopByButton" /> + <actionGroup ref="StorefrontOpenHomePageActionGroup" stepKey="goToHomePage"/> + <submitForm selector="#search_mini_form" parameterArray="['q' => 'Test Simple Product']" stepKey="fillSearchBar" /> + <resizeWindow width="600" height="800" stepKey="resizeWindow"/> + <waitForPageLoad stepKey="waitForHomePageToLoad2"/> + <seeElement selector="{{StorefrontCategorySidebarMobileSection.shopByButton}}" stepKey="seeShopByButton"/> + <click selector="{{StorefrontCategorySidebarMobileSection.shopByButton}}" stepKey="clickShopByButton"/> + <seeElement selector="{{StorefrontCategorySidebarSection.filterOptionsTitle('$$attribute.attribute[default_label]$$')}}" stepKey="seeShopByOption"/> + </test> +</tests> diff --git a/app/code/Magento/LayeredNavigation/Test/Unit/Observer/Edit/Tab/Front/ProductAttributeFormBuildFrontTabObserverTest.php b/app/code/Magento/LayeredNavigation/Test/Unit/Observer/Edit/Tab/Front/ProductAttributeFormBuildFrontTabObserverTest.php new file mode 100644 index 0000000000000..1541bee97efb3 --- /dev/null +++ b/app/code/Magento/LayeredNavigation/Test/Unit/Observer/Edit/Tab/Front/ProductAttributeFormBuildFrontTabObserverTest.php @@ -0,0 +1,107 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ + +declare(strict_types=1); + +namespace Magento\LayeredNavigation\Test\Unit\Observer\Edit\Tab\Front; + +use Magento\Config\Model\Config\Source\Yesno; +use Magento\Framework\Data\Form; +use Magento\Framework\Data\Form\Element\Fieldset; +use Magento\Framework\Event\Observer; +use Magento\Framework\Module\Manager; +use Magento\Framework\TestFramework\Unit\Helper\ObjectManager; +use Magento\LayeredNavigation\Observer\Edit\Tab\Front\ProductAttributeFormBuildFrontTabObserver; +use PHPUnit\Framework\MockObject\MockObject; +use PHPUnit\Framework\TestCase; + +/** + * Unit Test for \Magento\LayeredNavigation\Observer\Edit\Tab\Front\ProductAttributeFormBuildFrontTabObserver + */ +class ProductAttributeFormBuildFrontTabObserverTest extends TestCase +{ + /** + * @var MockObject|Observer + */ + private $eventObserverMock; + + /** + * @var MockObject|Yesno + */ + private $optionListLock; + + /** + * @var MockObject|Manager + */ + private $moduleManagerMock; + + /** + * @var ProductAttributeFormBuildFrontTabObserver + */ + private $observer; + + /** + * @inheritDoc + */ + protected function setUp(): void + { + $this->optionListLock = $this->createMock(Yesno::class); + $this->moduleManagerMock = $this->createMock(Manager::class); + $this->eventObserverMock = $this->getMockBuilder(Observer::class) + ->disableOriginalConstructor() + ->setMethods(['getForm']) + ->getMock(); + + $objectManager = new ObjectManager($this); + $this->observer = $objectManager->getObject( + ProductAttributeFormBuildFrontTabObserver::class, + [ + 'optionList' => $this->optionListLock, + 'moduleManager' => $this->moduleManagerMock, + ] + ); + } + + /** + * Test case when module output is disabled + */ + public function testExecuteWhenOutputDisabled(): void + { + $this->moduleManagerMock->expects($this->once()) + ->method('isOutputEnabled') + ->with('Magento_LayeredNavigation') + ->willReturn(false); + + $this->eventObserverMock->expects($this->never())->method('getForm'); + + $this->observer->execute($this->eventObserverMock); + } + + /** + * Test case when module output is enabled + */ + public function testExecuteWhenOutputEnabled(): void + { + $this->moduleManagerMock->expects($this->once()) + ->method('isOutputEnabled') + ->with('Magento_LayeredNavigation') + ->willReturn(true); + + $fieldsetMock = $this->createMock(Fieldset::class); + $fieldsetMock->expects($this->exactly(3))->method('addField'); + $formMock = $this->createMock(Form::class); + $formMock->expects($this->once()) + ->method('getElement') + ->with('front_fieldset') + ->willReturn($fieldsetMock); + + $this->eventObserverMock->expects($this->once()) + ->method('getForm') + ->willReturn($formMock); + + $this->observer->execute($this->eventObserverMock); + } +} diff --git a/app/code/Magento/LayeredNavigation/Test/Unit/Observer/Grid/ProductAttributeGridBuildObserverTest.php b/app/code/Magento/LayeredNavigation/Test/Unit/Observer/Grid/ProductAttributeGridBuildObserverTest.php new file mode 100644 index 0000000000000..f21908d11ad44 --- /dev/null +++ b/app/code/Magento/LayeredNavigation/Test/Unit/Observer/Grid/ProductAttributeGridBuildObserverTest.php @@ -0,0 +1,102 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +declare(strict_types=1); + +namespace Magento\LayeredNavigation\Test\Unit\Observer\Grid; + +use Magento\Catalog\Block\Adminhtml\Product\Attribute\Grid; +use Magento\Framework\Event\Observer; +use Magento\Framework\Module\Manager; +use Magento\Framework\TestFramework\Unit\Helper\ObjectManager; +use Magento\LayeredNavigation\Observer\Grid\ProductAttributeGridBuildObserver; +use PHPUnit\Framework\MockObject\MockObject; +use PHPUnit\Framework\TestCase; + +/** + * Class ProductAttributeGridBuildObserverTest + * + * Testing adding new grid column for Layered Navigation + */ +class ProductAttributeGridBuildObserverTest extends TestCase +{ + /** + * @var ProductAttributeGridBuildObserver + */ + private $observer; + + /** + * @var Manager|MockObject + */ + private $moduleManagerMock; + + /** + * @var Observer|MockObject + */ + private $observerMock; + + /** + * @var Grid|MockObject + */ + private $gridMock; + + /** + * Set Up + */ + protected function setUp() + { + $objectManager = new ObjectManager($this); + $this->moduleManagerMock = $this->createMock(Manager::class); + $this->gridMock = $this->createMock(Grid::class); + $this->observerMock = $this->getMockBuilder(Observer::class) + ->disableOriginalConstructor() + ->setMethods(['getGrid']) + ->getMock(); + + $this->observer = $objectManager->getObject( + ProductAttributeGridBuildObserver::class, + [ + 'moduleManager' => $this->moduleManagerMock, + ] + ); + } + + /** + * Testing the column adding if the output is not enabled + */ + public function testColumnAddingOnDisabledOutput() + { + $enabledOutput = false; + + $this->moduleManagerMock->expects($this->once()) + ->method('isOutputEnabled') + ->with('Magento_LayeredNavigation') + ->willReturn($enabledOutput); + + $this->observerMock->expects($this->never()) + ->method('getGrid'); + + $this->observer->execute($this->observerMock); + } + + /** + * Testing the column adding if the output is enabled + */ + public function testColumnAddingOnEnabledOutput() + { + $enabledOutput = true; + + $this->moduleManagerMock->expects($this->once()) + ->method('isOutputEnabled') + ->with('Magento_LayeredNavigation') + ->willReturn($enabledOutput); + + $this->observerMock->expects($this->once()) + ->method('getGrid') + ->willReturn($this->gridMock); + + $this->observer->execute($this->observerMock); + } +} diff --git a/app/code/Magento/LayeredNavigation/ViewModel/Layer/Filter.php b/app/code/Magento/LayeredNavigation/ViewModel/Layer/Filter.php new file mode 100644 index 0000000000000..fbfd5d3432d34 --- /dev/null +++ b/app/code/Magento/LayeredNavigation/ViewModel/Layer/Filter.php @@ -0,0 +1,41 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ + +namespace Magento\LayeredNavigation\ViewModel\Layer; + +use Magento\Catalog\Helper\Data as DataHelper; +use Magento\Framework\View\Element\Block\ArgumentInterface; + +/** + * ViewModel for Product Layer + */ +class Filter implements ArgumentInterface +{ + /** + * @var DataHelper + */ + private $dataHelper; + + /** + * Constructor + * + * @param DataHelper $dataHelper + */ + public function __construct(DataHelper $dataHelper) + { + $this->dataHelper = $dataHelper; + } + + /** + * Check is should display product count on layer + * + * @return bool + */ + public function shouldDisplayProductCountOnLayer(): bool + { + return $this->dataHelper->shouldDisplayProductCountOnLayer(); + } +} diff --git a/app/code/Magento/LayeredNavigation/registration.php b/app/code/Magento/LayeredNavigation/registration.php index 7c4f8dff9c920..9df0a63ae945f 100644 --- a/app/code/Magento/LayeredNavigation/registration.php +++ b/app/code/Magento/LayeredNavigation/registration.php @@ -4,6 +4,6 @@ * See COPYING.txt for license details. */ -use \Magento\Framework\Component\ComponentRegistrar; +use Magento\Framework\Component\ComponentRegistrar; ComponentRegistrar::register(ComponentRegistrar::MODULE, 'Magento_LayeredNavigation', __DIR__); diff --git a/app/code/Magento/LayeredNavigation/view/frontend/layout/catalog_category_view_type_layered.xml b/app/code/Magento/LayeredNavigation/view/frontend/layout/catalog_category_view_type_layered.xml index 647874540f94c..6f0a1af0f5bae 100644 --- a/app/code/Magento/LayeredNavigation/view/frontend/layout/catalog_category_view_type_layered.xml +++ b/app/code/Magento/LayeredNavigation/view/frontend/layout/catalog_category_view_type_layered.xml @@ -11,7 +11,11 @@ <referenceContainer name="sidebar.main"> <block class="Magento\LayeredNavigation\Block\Navigation\Category" name="catalog.leftnav" before="-" template="Magento_LayeredNavigation::layer/view.phtml"> <block class="Magento\LayeredNavigation\Block\Navigation\State" name="catalog.navigation.state" as="state" /> - <block class="Magento\LayeredNavigation\Block\Navigation\FilterRenderer" name="catalog.navigation.renderer" as="renderer" template="Magento_LayeredNavigation::layer/filter.phtml"/> + <block class="Magento\LayeredNavigation\Block\Navigation\FilterRenderer" name="catalog.navigation.renderer" as="renderer" template="Magento_LayeredNavigation::layer/filter.phtml"> + <arguments> + <argument name="product_layer_view_model" xsi:type="object">Magento\LayeredNavigation\ViewModel\Layer\Filter</argument> + </arguments> + </block> </block> </referenceContainer> </body> diff --git a/app/code/Magento/LayeredNavigation/view/frontend/layout/catalogsearch_result_index.xml b/app/code/Magento/LayeredNavigation/view/frontend/layout/catalogsearch_result_index.xml index 3bb21ed09a01c..28006ca70e07b 100644 --- a/app/code/Magento/LayeredNavigation/view/frontend/layout/catalogsearch_result_index.xml +++ b/app/code/Magento/LayeredNavigation/view/frontend/layout/catalogsearch_result_index.xml @@ -11,7 +11,11 @@ <referenceContainer name="sidebar.main"> <block class="Magento\LayeredNavigation\Block\Navigation\Search" name="catalogsearch.leftnav" before="-" template="Magento_LayeredNavigation::layer/view.phtml"> <block class="Magento\LayeredNavigation\Block\Navigation\State" name="catalogsearch.navigation.state" as="state" /> - <block class="Magento\LayeredNavigation\Block\Navigation\FilterRenderer" name="catalogsearch.navigation.renderer" as="renderer" template="Magento_LayeredNavigation::layer/filter.phtml"/> + <block class="Magento\LayeredNavigation\Block\Navigation\FilterRenderer" name="catalogsearch.navigation.renderer" as="renderer" template="Magento_LayeredNavigation::layer/filter.phtml"> + <arguments> + <argument name="product_layer_view_model" xsi:type="object">Magento\LayeredNavigation\ViewModel\Layer\Filter</argument> + </arguments> + </block> </block> </referenceContainer> </body> diff --git a/app/code/Magento/LayeredNavigation/view/frontend/templates/layer/filter.phtml b/app/code/Magento/LayeredNavigation/view/frontend/templates/layer/filter.phtml index cd04b346b16a6..6b65d184b462a 100644 --- a/app/code/Magento/LayeredNavigation/view/frontend/templates/layer/filter.phtml +++ b/app/code/Magento/LayeredNavigation/view/frontend/templates/layer/filter.phtml @@ -7,35 +7,35 @@ // phpcs:disable Magento2.Templates.ThisInTemplate.FoundThis ?> <?php -/** - * Template for filter items block - * - * @var $block \Magento\LayeredNavigation\Block\Navigation\FilterRenderer - */ +/** @var $block \Magento\LayeredNavigation\Block\Navigation\FilterRenderer */ +/** @var \Magento\LayeredNavigation\ViewModel\Layer\Filter $viewModel */ +$viewModel = $block->getData('product_layer_view_model'); ?> <ol class="items"> - <?php foreach ($filterItems as $filterItem) : ?> + <?php foreach ($filterItems as $filterItem): ?> <li class="item"> - <?php if ($filterItem->getCount() > 0) : ?> - <a href="<?= $block->escapeUrl($filterItem->getUrl()) ?>"> + <?php if ($filterItem->getCount() > 0): ?> + <a href="<?= $block->escapeUrl($filterItem->getUrl()) ?>" rel="nofollow"> <?= /* @noEscape */ $filterItem->getLabel() ?> - <?php if ($this->helper(\Magento\Catalog\Helper\Data::class)->shouldDisplayProductCountOnLayer()) : ?> - <span class="count"><?= /* @noEscape */ (int)$filterItem->getCount() ?><span class="filter-count-label"> - <?php if ($filterItem->getCount() == 1) : + <?php if ($viewModel->shouldDisplayProductCountOnLayer()): ?> + <span class="count"><?= /* @noEscape */ (int)$filterItem->getCount() ?> + <span class="filter-count-label"> + <?php if ($filterItem->getCount() == 1): ?> <?= $block->escapeHtml(__('item')) ?><?php - else : + else: ?> <?= $block->escapeHtml(__('item')) ?><?php endif;?></span></span> <?php endif; ?> </a> - <?php else :?> + <?php else: ?> <?= /* @noEscape */ $filterItem->getLabel() ?> - <?php if ($this->helper(\Magento\Catalog\Helper\Data::class)->shouldDisplayProductCountOnLayer()) : ?> - <span class="count"><?= /* @noEscape */ (int)$filterItem->getCount() ?><span class="filter-count-label"> - <?php if ($filterItem->getCount() == 1) : + <?php if ($viewModel->shouldDisplayProductCountOnLayer()): ?> + <span class="count"><?= /* @noEscape */ (int)$filterItem->getCount() ?> + <span class="filter-count-label"> + <?php if ($filterItem->getCount() == 1): ?><?= $block->escapeHtml(__('items')) ?><?php - else : + else: ?><?= $block->escapeHtml(__('items')) ?><?php endif;?></span></span> <?php endif; ?> diff --git a/app/code/Magento/Marketplace/registration.php b/app/code/Magento/Marketplace/registration.php index 7d4bbca27f15f..6ccf343d76427 100644 --- a/app/code/Magento/Marketplace/registration.php +++ b/app/code/Magento/Marketplace/registration.php @@ -4,6 +4,6 @@ * See COPYING.txt for license details. */ -use \Magento\Framework\Component\ComponentRegistrar; +use Magento\Framework\Component\ComponentRegistrar; ComponentRegistrar::register(ComponentRegistrar::MODULE, 'Magento_Marketplace', __DIR__); diff --git a/app/code/Magento/MediaGallery/Model/Asset.php b/app/code/Magento/MediaGallery/Model/Asset.php index f6e1c00044a79..8e232ff0336d9 100644 --- a/app/code/Magento/MediaGallery/Model/Asset.php +++ b/app/code/Magento/MediaGallery/Model/Asset.php @@ -24,6 +24,7 @@ class Asset extends AbstractExtensibleModel implements AssetInterface private const CONTENT_TYPE = 'content_type'; private const WIDTH = 'width'; private const HEIGHT = 'height'; + private const SIZE = 'size'; private const CREATED_AT = 'created_at'; private const UPDATED_AT = 'updated_at'; @@ -89,6 +90,14 @@ public function getHeight(): int return (int) $this->getData(self::HEIGHT); } + /** + * @inheritdoc + */ + public function getSize(): int + { + return (int) $this->getData(self::SIZE); + } + /** * @inheritdoc */ diff --git a/app/code/Magento/MediaGallery/Model/Asset/Command/DeleteByDirectoryPath.php b/app/code/Magento/MediaGallery/Model/Asset/Command/DeleteByDirectoryPath.php new file mode 100644 index 0000000000000..a50095fb3d8e7 --- /dev/null +++ b/app/code/Magento/MediaGallery/Model/Asset/Command/DeleteByDirectoryPath.php @@ -0,0 +1,94 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +declare(strict_types=1); + +namespace Magento\MediaGallery\Model\Asset\Command; + +use Magento\Framework\App\ResourceConnection; +use Magento\Framework\DB\Adapter\AdapterInterface; +use Magento\Framework\Exception\CouldNotDeleteException; +use Magento\MediaGalleryApi\Model\Asset\Command\DeleteByDirectoryPathInterface; +use Psr\Log\LoggerInterface; + +/** + * Class DeleteByDirectoryPath + * + * Remove asset(s) that correspond the provided directory path + */ +class DeleteByDirectoryPath implements DeleteByDirectoryPathInterface +{ + private const TABLE_MEDIA_GALLERY_ASSET = 'media_gallery_asset'; + + private const MEDIA_GALLERY_ASSET_PATH = 'path'; + + /** + * @var ResourceConnection + */ + private $resourceConnection; + + /** + * @var LoggerInterface + */ + private $logger; + + /** + * DeleteById constructor. + * + * @param ResourceConnection $resourceConnection + * @param LoggerInterface $logger + */ + public function __construct( + ResourceConnection $resourceConnection, + LoggerInterface $logger + ) { + $this->resourceConnection = $resourceConnection; + $this->logger = $logger; + } + + /** + * Delete media asset(s) by path + * + * @param string $directoryPath + * + * @return void + * + * @throws CouldNotDeleteException + */ + public function execute(string $directoryPath): void + { + $this->validateDirectoryPath($directoryPath); + try { + // Make sure that the path has a trailing slash + $directoryPath = rtrim($directoryPath, '/') . '/'; + + /** @var AdapterInterface $connection */ + $connection = $this->resourceConnection->getConnection(); + $tableName = $this->resourceConnection->getTableName(self::TABLE_MEDIA_GALLERY_ASSET); + $connection->delete($tableName, [self::MEDIA_GALLERY_ASSET_PATH . ' LIKE ?' => $directoryPath . '%']); + } catch (\Exception $exception) { + $this->logger->critical($exception); + $message = __( + 'Could not delete media assets by path %path: %error', + ['path' => $directoryPath, 'error' => $exception->getMessage()] + ); + throw new CouldNotDeleteException($message, $exception); + } + } + + /** + * Validate the directory path + * + * @param string $directoryPath + * + * @throws CouldNotDeleteException + */ + private function validateDirectoryPath(string $directoryPath): void + { + if (!$directoryPath || trim($directoryPath) === '') { + throw new CouldNotDeleteException(__('Cannot remove assets, the directory path does not exist')); + } + } +} diff --git a/app/code/Magento/MediaGallery/Plugin/Wysiwyg/Images/Storage.php b/app/code/Magento/MediaGallery/Plugin/Wysiwyg/Images/Storage.php index ff0e1528e0597..11331e4b9303f 100644 --- a/app/code/Magento/MediaGallery/Plugin/Wysiwyg/Images/Storage.php +++ b/app/code/Magento/MediaGallery/Plugin/Wysiwyg/Images/Storage.php @@ -8,13 +8,15 @@ namespace Magento\MediaGallery\Plugin\Wysiwyg\Images; +use Magento\Framework\Exception\CouldNotDeleteException; +use Magento\MediaGalleryApi\Model\Asset\Command\DeleteByDirectoryPathInterface; use Magento\MediaGalleryApi\Model\Asset\Command\GetByPathInterface; use Magento\MediaGalleryApi\Model\Asset\Command\DeleteByPathInterface; use Magento\Cms\Model\Wysiwyg\Images\Storage as StorageSubject; use Magento\Framework\App\Filesystem\DirectoryList; use Magento\Framework\Filesystem; -use Magento\Framework\Exception\ValidatorException; use Psr\Log\LoggerInterface; +use Magento\Framework\Exception\ValidatorException; /** * Ensures that metadata is removed from the database when a file is deleted and it is an image @@ -31,6 +33,11 @@ class Storage */ private $deleteMediaAssetByPath; + /** + * @var DeleteByDirectoryPathInterface + */ + private $deleteMediaAssetByDirectoryPath; + /** * @var Filesystem */ @@ -46,17 +53,20 @@ class Storage * * @param GetByPathInterface $getMediaAssetByPath * @param DeleteByPathInterface $deleteMediaAssetByPath + * @param DeleteByDirectoryPathInterface $deleteByDirectoryPath * @param Filesystem $filesystem * @param LoggerInterface $logger */ public function __construct( GetByPathInterface $getMediaAssetByPath, DeleteByPathInterface $deleteMediaAssetByPath, + DeleteByDirectoryPathInterface $deleteByDirectoryPath, Filesystem $filesystem, LoggerInterface $logger ) { $this->getMediaAssetByPath = $getMediaAssetByPath; $this->deleteMediaAssetByPath = $deleteMediaAssetByPath; + $this->deleteMediaAssetByDirectoryPath = $deleteByDirectoryPath; $this->filesystem = $filesystem; $this->logger = $logger; } @@ -69,7 +79,6 @@ public function __construct( * @param string $target * * @return StorageSubject - * @throws ValidatorException * * @SuppressWarnings(PHPMD.UnusedFormalParameter) */ @@ -92,4 +101,32 @@ public function afterDeleteFile(StorageSubject $subject, StorageSubject $result, return $result; } + + /** + * Delete media data after the folder delete action from Wysiwyg + * + * @param StorageSubject $subject + * @param mixed $result + * @param string $path + * + * @return null + * + * @SuppressWarnings(PHPMD.UnusedFormalParameter) + */ + public function afterDeleteDirectory(StorageSubject $subject, $result, $path) + { + if (!is_string($path)) { + return $result; + } + + try { + $mediaDirectoryRead = $this->filesystem->getDirectoryRead(DirectoryList::MEDIA); + $relativePath = $mediaDirectoryRead->getRelativePath($path); + $this->deleteMediaAssetByDirectoryPath->execute($relativePath); + } catch (ValidatorException $exception) { + $this->logger->critical($exception); + } + + return $result; + } } diff --git a/app/code/Magento/MediaGallery/Test/Unit/Model/Asset/Command/DeleteByDirectoryPathTest.php b/app/code/Magento/MediaGallery/Test/Unit/Model/Asset/Command/DeleteByDirectoryPathTest.php new file mode 100644 index 0000000000000..ebc90cdc0470f --- /dev/null +++ b/app/code/Magento/MediaGallery/Test/Unit/Model/Asset/Command/DeleteByDirectoryPathTest.php @@ -0,0 +1,105 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +declare(strict_types=1); + +namespace Magento\MediaGallery\Test\Unit\Model\Asset\Command; + +use Magento\MediaGallery\Model\Asset\Command\DeleteByDirectoryPath; +use Magento\Framework\App\ResourceConnection; +use Magento\Framework\DB\Adapter\AdapterInterface; +use Magento\Framework\Exception\CouldNotDeleteException; +use PHPUnit\Framework\MockObject\MockObject; +use PHPUnit\Framework\TestCase; +use Magento\Framework\TestFramework\Unit\Helper\ObjectManager; +use Psr\Log\LoggerInterface; + +/** + * Test the DeleteByDirectoryPath command model + */ +class DeleteByDirectoryPathTest extends TestCase +{ + private const TABLE_NAME = 'media_gallery_asset'; + private const DIRECTORY_PATH = 'test-directory-path/'; + + /** + * @var ResourceConnection|MockObject + */ + private $resourceConnection; + + /** + * @var DeleteByDirectoryPath + */ + private $deleteMediaAssetByDirectoryPath; + + /** + * @var AdapterInterface|MockObject + */ + private $adapter; + + /** + * @var LoggerInterface|MockObject + */ + private $logger; + + /** + * Initialize basic test class mocks + */ + protected function setUp(): void + { + $this->logger = $this->createMock(LoggerInterface::class); + $this->resourceConnection = $this->createMock(ResourceConnection::class); + + $this->deleteMediaAssetByDirectoryPath = (new ObjectManager($this))->getObject( + DeleteByDirectoryPath::class, + [ + 'resourceConnection' => $this->resourceConnection, + 'logger' => $this->logger, + ] + ); + + $this->adapter = $this->createMock(AdapterInterface::class); + } + + /** + * Test delete media asset by path command + * + * @param string $directoryPath + * @throws CouldNotDeleteException + * @dataProvider directoryPathDataProvider + */ + public function testDeleteByDirectoryPath(string $directoryPath): void + { + if (!empty($directoryPath)) { + $this->resourceConnection->expects($this->once()) + ->method('getConnection') + ->willReturn($this->adapter); + $this->resourceConnection->expects($this->once()) + ->method('getTableName') + ->with(self::TABLE_NAME) + ->willReturn('prefix_' . self::TABLE_NAME); + $this->adapter->expects($this->once()) + ->method('delete') + ->with('prefix_' . self::TABLE_NAME, ['path LIKE ?' => self::DIRECTORY_PATH . '%']); + } else { + self::expectException('\Magento\Framework\Exception\CouldNotDeleteException'); + } + + $this->deleteMediaAssetByDirectoryPath->execute($directoryPath); + } + + /** + * Data provider for directory path + * + * @return array + */ + public function directoryPathDataProvider(): array + { + return [ + 'Existing path' => [self::DIRECTORY_PATH], + 'Empty path' => [''] + ]; + } +} diff --git a/app/code/Magento/MediaGallery/Test/Unit/Model/Asset/Command/GetByIdExceptionDuringMediaAssetInitializationTest.php b/app/code/Magento/MediaGallery/Test/Unit/Model/Asset/Command/GetByIdExceptionDuringMediaAssetInitializationTest.php index 0d0bfc510b518..49a5421e623a5 100644 --- a/app/code/Magento/MediaGallery/Test/Unit/Model/Asset/Command/GetByIdExceptionDuringMediaAssetInitializationTest.php +++ b/app/code/Magento/MediaGallery/Test/Unit/Model/Asset/Command/GetByIdExceptionDuringMediaAssetInitializationTest.php @@ -16,7 +16,7 @@ use Magento\MediaGalleryApi\Api\Data\AssetInterfaceFactory; use PHPUnit\Framework\MockObject\MockObject; use Psr\Log\LoggerInterface; -use Zend\Db\Adapter\Driver\Pdo\Statement; +use Laminas\Db\Adapter\Driver\Pdo\Statement; /** * Test the GetById command with exception during media asset initialization diff --git a/app/code/Magento/MediaGallery/Test/Unit/Model/Asset/Command/GetByIdExceptionNoSuchEntityTest.php b/app/code/Magento/MediaGallery/Test/Unit/Model/Asset/Command/GetByIdExceptionNoSuchEntityTest.php index 0ca9b3a3ffc8a..75b69c441c6d7 100644 --- a/app/code/Magento/MediaGallery/Test/Unit/Model/Asset/Command/GetByIdExceptionNoSuchEntityTest.php +++ b/app/code/Magento/MediaGallery/Test/Unit/Model/Asset/Command/GetByIdExceptionNoSuchEntityTest.php @@ -16,7 +16,7 @@ use Magento\MediaGalleryApi\Api\Data\AssetInterfaceFactory; use PHPUnit\Framework\MockObject\MockObject; use Psr\Log\LoggerInterface; -use Zend\Db\Adapter\Driver\Pdo\Statement; +use Laminas\Db\Adapter\Driver\Pdo\Statement; /** * Test the GetById command with exception thrown in case when there is no such entity diff --git a/app/code/Magento/MediaGallery/Test/Unit/Model/Asset/Command/GetByIdExceptionOnGetDataTest.php b/app/code/Magento/MediaGallery/Test/Unit/Model/Asset/Command/GetByIdExceptionOnGetDataTest.php index a709c2d214bda..f76552487e0f7 100644 --- a/app/code/Magento/MediaGallery/Test/Unit/Model/Asset/Command/GetByIdExceptionOnGetDataTest.php +++ b/app/code/Magento/MediaGallery/Test/Unit/Model/Asset/Command/GetByIdExceptionOnGetDataTest.php @@ -16,7 +16,7 @@ use Magento\MediaGalleryApi\Api\Data\AssetInterfaceFactory; use PHPUnit\Framework\MockObject\MockObject; use Psr\Log\LoggerInterface; -use Zend\Db\Adapter\Driver\Pdo\Statement; +use Laminas\Db\Adapter\Driver\Pdo\Statement; /** * Test the GetById command with exception during get media data diff --git a/app/code/Magento/MediaGallery/Test/Unit/Model/Asset/Command/GetByIdSuccessfulTest.php b/app/code/Magento/MediaGallery/Test/Unit/Model/Asset/Command/GetByIdSuccessfulTest.php index c300d4f121bd2..c9e8416c53156 100644 --- a/app/code/Magento/MediaGallery/Test/Unit/Model/Asset/Command/GetByIdSuccessfulTest.php +++ b/app/code/Magento/MediaGallery/Test/Unit/Model/Asset/Command/GetByIdSuccessfulTest.php @@ -16,7 +16,7 @@ use Magento\MediaGalleryApi\Api\Data\AssetInterfaceFactory; use PHPUnit\Framework\MockObject\MockObject; use Psr\Log\LoggerInterface; -use Zend\Db\Adapter\Driver\Pdo\Statement; +use Laminas\Db\Adapter\Driver\Pdo\Statement; /** * Test the GetById command successful scenario diff --git a/app/code/Magento/MediaGallery/Test/Unit/Model/DataExtractorTest.php b/app/code/Magento/MediaGallery/Test/Unit/Model/DataExtractorTest.php index 7d57f32449f56..f70e4ccdae22c 100644 --- a/app/code/Magento/MediaGallery/Test/Unit/Model/DataExtractorTest.php +++ b/app/code/Magento/MediaGallery/Test/Unit/Model/DataExtractorTest.php @@ -9,10 +9,9 @@ use Magento\Framework\TestFramework\Unit\Helper\ObjectManager; use Magento\MediaGallery\Model\Asset; +use Magento\MediaGallery\Model\DataExtractor; use Magento\MediaGallery\Model\Keyword; use Magento\MediaGalleryApi\Api\Data\AssetInterface; -use Magento\MediaGalleryApi\Api\Data\KeywordInterface; -use Magento\MediaGallery\Model\DataExtractor; use PHPUnit\Framework\MockObject\MockObject; use PHPUnit\Framework\TestCase; @@ -54,8 +53,27 @@ public function testExtractData(string $class, $interfaceClass, array $expectedD 'data' => $data, ] ); - $receivedData = $this->dataExtractor->extract($model, $interfaceClass); - $this->checkValues($expectedData, $receivedData, $model); + if ($interfaceClass) { + $receivedData = $this->dataExtractor->extract($model, $interfaceClass); + $this->checkAssetValues($expectedData, $receivedData, $model); + } else { + $receivedData = $this->dataExtractor->extract($model); + $this->checkKeyWordValues($expectedData, $receivedData, $model); + } + } + + /** + * @param array $expectedData + * @param array $data + * @param object $model + */ + private function checkAssetValues(array $expectedData, array $data, $model) + { + foreach ($expectedData as $expectedDataKey => $expectedDataItem) { + $this->assertEquals($data[$expectedDataKey] ?? null, $model->{$expectedDataItem['method']}()); + $this->assertEquals($data[$expectedDataKey] ?? null, $expectedDataItem['value']); + } + $this->assertEquals(array_keys($expectedData), array_keys($data)); } /** @@ -63,13 +81,13 @@ public function testExtractData(string $class, $interfaceClass, array $expectedD * @param array $data * @param object $model */ - protected function checkValues(array $expectedData, array $data, $model) + private function checkKeyWordValues(array $expectedData, array $data, $model) { foreach ($expectedData as $expectedDataKey => $expectedDataItem) { $this->assertEquals($data[$expectedDataKey] ?? null, $model->{$expectedDataItem['method']}()); $this->assertEquals($data[$expectedDataKey] ?? null, $expectedDataItem['value']); } - $this->assertEquals(array_keys($expectedData), array_keys($expectedData)); + $this->assertEquals(array_keys($expectedData), array_keys(array_slice($data, 0, 2))); } /** @@ -102,13 +120,17 @@ public function assetProvider() 'value' => 'content_type', 'method' => 'getContentType', ], + 'height' => [ + 'value' => 4, + 'method' => 'getHeight', + ], 'width' => [ 'value' => 3, 'method' => 'getWidth', ], - 'height' => [ - 'value' => 4, - 'method' => 'getHeight', + 'size' => [ + 'value' => 300, + 'method' => 'getSize', ], 'created_at' => [ 'value' => '2019-11-28 10:40:09', @@ -122,7 +144,7 @@ public function assetProvider() ], 'Keyword conversion without interface' => [ Keyword::class, - null, + '', [ 'id' => [ 'value' => 2, diff --git a/app/code/Magento/MediaGallery/Test/Unit/Plugin/Images/StorageTest.php b/app/code/Magento/MediaGallery/Test/Unit/Plugin/Images/StorageTest.php new file mode 100644 index 0000000000000..4ac448733c47f --- /dev/null +++ b/app/code/Magento/MediaGallery/Test/Unit/Plugin/Images/StorageTest.php @@ -0,0 +1,145 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +declare(strict_types=1); + +namespace Magento\MediaGallery\Test\Unit\Plugin\Images; + +use Magento\Cms\Model\Wysiwyg\Images\Storage as StorageSubject; +use Magento\Framework\Exception\ValidatorException; +use Magento\Framework\Filesystem; +use Magento\Framework\Filesystem\Directory\ReadInterface; +use Magento\Framework\TestFramework\Unit\Helper\ObjectManager; +use Magento\MediaGallery\Model\Asset\Command\DeleteByDirectoryPath; +use Magento\MediaGallery\Plugin\Wysiwyg\Images\Storage as StoragePlugin; +use Magento\MediaGalleryApi\Model\Asset\Command\DeleteByDirectoryPathInterface; +use Magento\MediaGalleryApi\Model\Asset\Command\DeleteByPathInterface; +use Magento\MediaGalleryApi\Model\Asset\Command\GetByPathInterface; +use PHPUnit\Framework\MockObject\MockObject; +use PHPUnit\Framework\TestCase; +use Psr\Log\LoggerInterface; + +/** + * Test the DeleteByDirectoryPath command model + */ +class StorageTest extends TestCase +{ + private const NON_STRING_PATH = 2020; + private const NON_EXISTENT_PATH = 'non_existent'; + private const INVALID_PATH = '&&'; + private const VALID_PATH = 'test-directory-path/'; + + /** + * @var GetByPathInterface|MockObject + */ + private $getMediaAssetByPath; + + /** + * @var DeleteByPathInterface|MockObject + */ + private $deleteMediaAssetByPath; + + /** + * @var DeleteByDirectoryPathInterface|MockObject + */ + private $deleteMediaAssetByDirectoryPath; + + /** + * @var Filesystem|MockObject + */ + private $filesystem; + + /** + * @var LoggerInterface|MockObject + */ + private $logger; + + /** + * @var StoragePlugin + */ + private $storagePlugin; + + /** + * Initialize basic test class mocks + */ + protected function setUp(): void + { + $this->logger = $this->createMock(LoggerInterface::class); + $this->getMediaAssetByPath = $this->createMock(GetByPathInterface::class); + $this->deleteMediaAssetByPath = $this->createMock(DeleteByPathInterface::class); + $this->deleteMediaAssetByDirectoryPath = $this->createMock(DeleteByDirectoryPath::class); + $this->filesystem = $this->createMock(Filesystem::class); + + $this->storagePlugin = (new ObjectManager($this))->getObject( + StoragePlugin::class, + [ + 'getMediaAssetByPath' => $this->getMediaAssetByPath, + 'deleteMediaAssetByPath' => $this->deleteMediaAssetByPath, + 'deleteByDirectoryPath' => $this->deleteMediaAssetByDirectoryPath, + 'filesystem' => $this->filesystem, + 'logger' => $this->logger + ] + ); + } + + /** + * @param string $path + * + * @dataProvider pathPathDataProvider + */ + public function testAfterDeleteDirectory(string $path): void + { + /** @var StorageSubject|MockObject $storageSubject */ + $storageSubject = $this->getMockBuilder(StorageSubject::class) + ->disableOriginalConstructor() + ->getMock(); + $directoryRead = $this->createMock(ReadInterface::class); + $this->filesystem->expects($this->any()) + ->method('getDirectoryRead') + ->willReturn($directoryRead); + + switch ($path) { + case self::NON_STRING_PATH: + $result = $this->storagePlugin->afterDeleteDirectory($storageSubject, null, (int)$path); + self::assertNull($result); + break; + case self::INVALID_PATH: + $exception = new ValidatorException(__('Path cannot be used with directory')); + $directoryRead->expects($this->once()) + ->method('getRelativePath') + ->with($path) + ->willThrowException($exception); + $this->logger->expects($this->once()) + ->method('critical') + ->with($exception); + $this->storagePlugin->afterDeleteDirectory($storageSubject, null, $path); + break; + case self::VALID_PATH: + $directoryRead->expects($this->once()) + ->method('getRelativePath') + ->with($path) + ->willReturn($path); + $this->deleteMediaAssetByDirectoryPath->expects($this->once()) + ->method('execute') + ->with($path); + $this->storagePlugin->afterDeleteDirectory($storageSubject, null, $path); + break; + } + } + + /** + * Data provider for path + * + * @return array + */ + public function pathPathDataProvider(): array + { + return [ + 'Non string path' => [2020], + 'Invalid path' => [self::INVALID_PATH], + 'Existent path' => [self::VALID_PATH] + ]; + } +} diff --git a/app/code/Magento/MediaGallery/Test/Unit/Plugin/Product/Gallery/ProcessorTest.php b/app/code/Magento/MediaGallery/Test/Unit/Plugin/Product/Gallery/ProcessorTest.php new file mode 100644 index 0000000000000..94c3aaf5c2f19 --- /dev/null +++ b/app/code/Magento/MediaGallery/Test/Unit/Plugin/Product/Gallery/ProcessorTest.php @@ -0,0 +1,133 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ + +declare(strict_types=1); + +namespace Magento\MediaGallery\Test\Unit\Plugin\Product\Gallery; + +use Magento\Catalog\Model\Product; +use Magento\Catalog\Model\Product\Gallery\Processor as ProcessorSubject; +use Magento\Framework\TestFramework\Unit\Helper\ObjectManager as ObjectManagerHelper; +use Magento\MediaGallery\Plugin\Product\Gallery\Processor; +use Magento\MediaGalleryApi\Model\Asset\Command\DeleteByPathInterface; +use PHPUnit\Framework\MockObject\MockObject; +use PHPUnit\Framework\TestCase; +use Psr\Log\LoggerInterface; + +/** + * Unit test for \Magento\MediaGallery\Plugin\Product\Gallery\Processor + */ +class ProcessorTest extends TestCase +{ + private const STUB_FILE_NAME = 'file'; + + /** + * @var DeleteByPathInterface|MockObject + */ + private $deleteMediaAssetByPathMock; + + /** + * @var LoggerInterface|MockObject + */ + private $loggerMock; + + /** + * @var ProcessorSubject|MockObject + */ + private $processorSubjectMock; + + /** + * @var Product|MockObject + */ + private $productMock; + + /** + * @var Processor + */ + private $plugin; + + /** + * @inheritDoc + */ + protected function setUp() + { + $this->processorSubjectMock = $this->createMock(ProcessorSubject::class); + $this->productMock = $this->createMock(Product::class); + + $this->deleteMediaAssetByPathMock = $this->getMockBuilder(DeleteByPathInterface::class) + ->disableOriginalConstructor() + ->setMethods(['execute']) + ->getMockForAbstractClass(); + $this->loggerMock = $this->getMockBuilder(LoggerInterface::class) + ->disableOriginalConstructor() + ->setMethods(['critical']) + ->getMockForAbstractClass(); + + $this->plugin = (new ObjectManagerHelper($this))->getObject( + Processor::class, + [ + 'deleteMediaAssetByPath' => $this->deleteMediaAssetByPathMock, + 'logger' => $this->loggerMock + ] + ); + } + + /** + * Successful test case. + */ + public function testAfterRemoveImageExpectsExecuteCalled() + { + $this->deleteMediaAssetByPathMock->expects($this->once()) + ->method('execute') + ->with(self::STUB_FILE_NAME); + $this->loggerMock->expects($this->never())->method('critical'); + + $actualResult = $this->plugin->afterRemoveImage( + $this->processorSubjectMock, + $this->processorSubjectMock, + $this->productMock, + self::STUB_FILE_NAME + ); + $this->assertSame($this->processorSubjectMock, $actualResult); + } + + /** + * Test case when passed File argument is not a string. + */ + public function testAfterRemoveImageWithIncorrectFile() + { + $this->deleteMediaAssetByPathMock->expects($this->never())->method('execute'); + $this->loggerMock->expects($this->never())->method('critical'); + + $actualResult = $this->plugin->afterRemoveImage( + $this->processorSubjectMock, + $this->processorSubjectMock, + $this->productMock, + ['non-string-argument' => self::STUB_FILE_NAME] + ); + $this->assertSame($this->processorSubjectMock, $actualResult); + } + + /** + * Test case when an Exception is thrown. + */ + public function testAfterRemoveImageExpectsExecuteWillThrowException() + { + $this->deleteMediaAssetByPathMock->expects($this->once()) + ->method('execute') + ->with(self::STUB_FILE_NAME) + ->willThrowException(new \Exception('Some Exception')); + $this->loggerMock->expects($this->once())->method('critical'); + + $actualResult = $this->plugin->afterRemoveImage( + $this->processorSubjectMock, + $this->processorSubjectMock, + $this->productMock, + self::STUB_FILE_NAME + ); + $this->assertSame($this->processorSubjectMock, $actualResult); + } +} diff --git a/app/code/Magento/MediaGallery/Test/Unit/Plugin/Wysiwyg/Images/StorageTest.php b/app/code/Magento/MediaGallery/Test/Unit/Plugin/Wysiwyg/Images/StorageTest.php new file mode 100644 index 0000000000000..0c653a9543b19 --- /dev/null +++ b/app/code/Magento/MediaGallery/Test/Unit/Plugin/Wysiwyg/Images/StorageTest.php @@ -0,0 +1,176 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ + +declare(strict_types=1); + +namespace Magento\MediaGallery\Test\Unit\Plugin\Wysiwyg\Images; + +use Magento\Cms\Model\Wysiwyg\Images\Storage as StorageSubject; +use Magento\Framework\Filesystem; +use Magento\Framework\Filesystem\Directory\ReadInterface; +use Magento\Framework\TestFramework\Unit\Helper\ObjectManager as ObjectManagerHelper; +use Magento\MediaGallery\Plugin\Wysiwyg\Images\Storage; +use Magento\MediaGalleryApi\Model\Asset\Command\DeleteByPathInterface; +use Magento\MediaGalleryApi\Model\Asset\Command\GetByPathInterface; +use PHPUnit\Framework\MockObject\MockObject; +use PHPUnit\Framework\TestCase; +use Psr\Log\LoggerInterface; + +/** + * Unit test for \Magento\MediaGallery\Plugin\Wysiwyg\Images\Storage + */ +class StorageTest extends TestCase +{ + const STUB_TARGET = '/stub/test.png'; + const STUB_RELATIVE_PATH = 'test.png'; + + /** + * @var Storage + */ + private $storage; + + /** + * @var GetByPathInterface|MockObject + */ + private $getMediaAssetByPathMock; + + /** + * @var DeleteByPathInterface|MockObject + */ + private $deleteMediaAssetByPathMock; + + /** + * @var Filesystem|MockObject + */ + private $filesystemMock; + + /** + * @var LoggerInterface|MockObject + */ + private $loggerMock; + + /** + * @var StorageSubject|MockObject + */ + private $storageSubjectMock; + + /** + * @var ReadInterface|MockObject + */ + private $readInterfaceMock; + + /** + * @inheritDoc + */ + protected function setUp() + { + $this->storageSubjectMock = $this->createMock(StorageSubject::class); + $this->filesystemMock = $this->createMock(Filesystem::class); + $this->getMediaAssetByPathMock = $this->createMock(GetByPathInterface::class); + $this->deleteMediaAssetByPathMock = $this->getMockBuilder(DeleteByPathInterface::class) + ->disableOriginalConstructor() + ->setMethods(['execute']) + ->getMockForAbstractClass(); + $this->loggerMock = $this->getMockBuilder(LoggerInterface::class) + ->disableOriginalConstructor() + ->setMethods(['critical']) + ->getMockForAbstractClass(); + $this->readInterfaceMock = $this->getMockBuilder(ReadInterface::class) + ->disableOriginalConstructor() + ->setMethods(['getRelativePath']) + ->getMockForAbstractClass(); + + $this->storage = (new ObjectManagerHelper($this))->getObject( + Storage::class, + [ + 'getMediaAssetByPath' => $this->getMediaAssetByPathMock, + 'deleteMediaAssetByPath' => $this->deleteMediaAssetByPathMock, + 'filesystem' => $this->filesystemMock, + 'logger' => $this->loggerMock + ] + ); + } + + /** + * Test case when an exception is thrown during the method execution. + */ + public function testAfterDeleteFileExpectsDeleteMediaAssetExecuted() + { + $this->setupMocksToReturnCorrectRelativePath(); + $this->deleteMediaAssetByPathMock->expects($this->once())->method('execute'); + $this->loggerMock->expects($this->never())->method('critical'); + + $this->executeOriginalMethodWithCorrectTarget(); + } + + /** + * Test case when an exception is thrown during the method execution. + */ + public function testAfterDeleteFileWithException() + { + $this->setupMocksToReturnCorrectRelativePath(); + $this->deleteMediaAssetByPathMock->expects($this->once()) + ->method('execute') + ->willThrowException(new \Exception()); + $this->loggerMock->expects($this->once())->method('critical'); + + $this->executeOriginalMethodWithCorrectTarget(); + } + + /** + * Test case when the target is not a string. + */ + public function testAfterDeleteFileWhenTargetIsNotString() + { + $target = []; + $this->filesystemMock->expects($this->never())->method('getDirectoryRead'); + $this->deleteMediaAssetByPathMock->expects($this->never())->method('execute'); + $this->assertSame( + $this->storageSubjectMock, + $this->storage->afterDeleteFile($this->storageSubjectMock, $this->storageSubjectMock, $target) + ); + } + + /** + * Test case when there is no Relative Path which is need to be deleted. + */ + public function testAfterDeleteFileWhenRelativePathIsEmpty() + { + $this->readInterfaceMock->expects($this->once()) + ->method('getRelativePath') + ->willReturn(''); + $this->filesystemMock->expects($this->once()) + ->method('getDirectoryRead') + ->willReturn($this->readInterfaceMock); + + $this->deleteMediaAssetByPathMock->expects($this->never())->method('execute'); + $this->executeOriginalMethodWithCorrectTarget(); + } + + /** + * Call the tested method + */ + private function executeOriginalMethodWithCorrectTarget() + { + $this->assertSame( + $this->storageSubjectMock, + $this->storage->afterDeleteFile($this->storageSubjectMock, $this->storageSubjectMock, self::STUB_TARGET) + ); + } + + /** + * Set mocks in order to return the relative path + */ + private function setupMocksToReturnCorrectRelativePath() + { + $this->readInterfaceMock->expects($this->once()) + ->method('getRelativePath') + ->willReturn(self::STUB_RELATIVE_PATH); + $this->filesystemMock->expects($this->once()) + ->method('getDirectoryRead') + ->willReturn($this->readInterfaceMock); + } +} diff --git a/app/code/Magento/MediaGallery/etc/db_schema.xml b/app/code/Magento/MediaGallery/etc/db_schema.xml index fac1342528f2c..8bcad28de2a8e 100644 --- a/app/code/Magento/MediaGallery/etc/db_schema.xml +++ b/app/code/Magento/MediaGallery/etc/db_schema.xml @@ -14,6 +14,7 @@ <column xsi:type="varchar" name="content_type" length="255" nullable="true" comment="Content Type"/> <column xsi:type="int" name="width" padding="10" unsigned="true" nullable="false" identity="false" default="0" comment="Width"/> <column xsi:type="int" name="height" padding="10" unsigned="true" nullable="false" identity="false" default="0" comment="Height"/> + <column xsi:type="int" name="size" padding="10" unsigned="true" nullable="false" identity="false" comment="Asset file size in bytes"/> <column xsi:type="timestamp" name="created_at" on_update="false" nullable="false" default="CURRENT_TIMESTAMP" comment="Created At"/> <column xsi:type="timestamp" name="updated_at" on_update="true" nullable="false" default="CURRENT_TIMESTAMP" comment="Updated At"/> <constraint xsi:type="primary" referenceId="PRIMARY"> diff --git a/app/code/Magento/MediaGallery/etc/db_schema_whitelist.json b/app/code/Magento/MediaGallery/etc/db_schema_whitelist.json index 10db10d5dd5db..9e187e6dea4a5 100644 --- a/app/code/Magento/MediaGallery/etc/db_schema_whitelist.json +++ b/app/code/Magento/MediaGallery/etc/db_schema_whitelist.json @@ -8,6 +8,7 @@ "content_type": true, "width": true, "height": true, + "size": true, "created_at": true, "updated_at": true }, @@ -53,4 +54,4 @@ "MEDIA_GALLERY_ASSET_KEYWORD_ASSET_ID_MEDIA_GALLERY_ASSET_ID": true } } -} \ No newline at end of file +} diff --git a/app/code/Magento/MediaGallery/etc/di.xml b/app/code/Magento/MediaGallery/etc/di.xml index 8c4a856852e1a..24ed42b2b06dd 100644 --- a/app/code/Magento/MediaGallery/etc/di.xml +++ b/app/code/Magento/MediaGallery/etc/di.xml @@ -13,6 +13,7 @@ <preference for="Magento\MediaGalleryApi\Model\Asset\Command\SaveInterface" type="Magento\MediaGallery\Model\Asset\Command\Save"/> <preference for="Magento\MediaGalleryApi\Model\Asset\Command\GetByPathInterface" type="Magento\MediaGallery\Model\Asset\Command\GetByPath"/> <preference for="Magento\MediaGalleryApi\Model\Asset\Command\DeleteByPathInterface" type="Magento\MediaGallery\Model\Asset\Command\DeleteByPath"/> + <preference for="Magento\MediaGalleryApi\Model\Asset\Command\DeleteByDirectoryPathInterface" type="Magento\MediaGallery\Model\Asset\Command\DeleteByDirectoryPath"/> <preference for="Magento\MediaGalleryApi\Model\Keyword\Command\GetAssetKeywordsInterface" type="Magento\MediaGallery\Model\Keyword\Command\GetAssetKeywords"/> <preference for="Magento\MediaGalleryApi\Model\Keyword\Command\SaveAssetKeywordsInterface" type="Magento\MediaGallery\Model\Keyword\Command\SaveAssetKeywords"/> diff --git a/app/code/Magento/MediaGalleryApi/Api/Data/AssetInterface.php b/app/code/Magento/MediaGalleryApi/Api/Data/AssetInterface.php index affae296ca530..0f4b78a6dc603 100644 --- a/app/code/Magento/MediaGalleryApi/Api/Data/AssetInterface.php +++ b/app/code/Magento/MediaGalleryApi/Api/Data/AssetInterface.php @@ -65,6 +65,13 @@ public function getHeight(): int; */ public function getWidth(): int; + /** + * Retrieve asset file size in bytes + * + * @return int + */ + public function getSize(): int; + /** * Get created at * diff --git a/app/code/Magento/MediaGalleryApi/Model/Asset/Command/DeleteByDirectoryPathInterface.php b/app/code/Magento/MediaGalleryApi/Model/Asset/Command/DeleteByDirectoryPathInterface.php new file mode 100644 index 0000000000000..e55f7cb714f77 --- /dev/null +++ b/app/code/Magento/MediaGalleryApi/Model/Asset/Command/DeleteByDirectoryPathInterface.php @@ -0,0 +1,25 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ + +declare(strict_types=1); + +namespace Magento\MediaGalleryApi\Model\Asset\Command; + +/** + * A command represents the media gallery assets delete action. A media gallery asset is filtered by directory + * path value. + */ +interface DeleteByDirectoryPathInterface +{ + /** + * Delete media assets by directory path + * + * @param string $directoryPath + * + * @return void + */ + public function execute(string $directoryPath): void; +} diff --git a/app/code/Magento/MediaStorage/App/Media.php b/app/code/Magento/MediaStorage/App/Media.php index 15bf7bb62e970..ca5ff458c52e9 100644 --- a/app/code/Magento/MediaStorage/App/Media.php +++ b/app/code/Magento/MediaStorage/App/Media.php @@ -20,6 +20,7 @@ use Magento\Framework\AppInterface; use Magento\Framework\Filesystem; use Magento\Framework\Filesystem\Directory\WriteInterface; +use Magento\Framework\Filesystem\Driver\File; use Magento\MediaStorage\Model\File\Storage\Config; use Magento\MediaStorage\Model\File\Storage\ConfigFactory; use Magento\MediaStorage\Model\File\Storage\Response; @@ -28,7 +29,7 @@ use Magento\MediaStorage\Service\ImageResize; /** - * Media Storage + * The class resize original images * * @SuppressWarnings(PHPMD.CouplingBetweenObjects) */ @@ -70,7 +71,12 @@ class Media implements AppInterface /** * @var WriteInterface */ - private $directory; + private $directoryPub; + + /** + * @var \Magento\Framework\Filesystem\Directory\WriteInterface + */ + private $directoryMedia; /** * @var ConfigFactory @@ -109,6 +115,7 @@ class Media implements AppInterface * @param PlaceholderFactory $placeholderFactory * @param State $state * @param ImageResize $imageResize + * @param File $file * @SuppressWarnings(PHPMD.ExcessiveParameterList) */ public function __construct( @@ -122,15 +129,17 @@ public function __construct( Filesystem $filesystem, PlaceholderFactory $placeholderFactory, State $state, - ImageResize $imageResize + ImageResize $imageResize, + File $file ) { $this->response = $response; $this->isAllowed = $isAllowed; - $this->directory = $filesystem->getDirectoryWrite(DirectoryList::PUB); + $this->directoryPub = $filesystem->getDirectoryWrite(DirectoryList::PUB); + $this->directoryMedia = $filesystem->getDirectoryWrite(DirectoryList::MEDIA); $mediaDirectory = trim($mediaDirectory); if (!empty($mediaDirectory)) { // phpcs:ignore Magento2.Functions.DiscouragedFunction - $this->mediaDirectoryPath = str_replace('\\', '/', realpath($mediaDirectory)); + $this->mediaDirectoryPath = str_replace('\\', '/', $file->getRealPath($mediaDirectory)); } $this->configCacheFile = $configCacheFile; $this->relativeFileName = $relativeFileName; @@ -151,7 +160,7 @@ public function launch(): ResponseInterface { $this->appState->setAreaCode(Area::AREA_GLOBAL); - if ($this->mediaDirectoryPath !== $this->directory->getAbsolutePath()) { + if ($this->checkMediaDirectoryChanged()) { // Path to media directory changed or absent - update the config /** @var Config $config */ $config = $this->configFactory->create(['cacheFile' => $this->configCacheFile]); @@ -166,11 +175,11 @@ public function launch(): ResponseInterface try { /** @var Synchronization $sync */ - $sync = $this->syncFactory->create(['directory' => $this->directory]); + $sync = $this->syncFactory->create(['directory' => $this->directoryPub]); $sync->synchronize($this->relativeFileName); $this->imageResize->resizeFromImageName($this->getOriginalImage($this->relativeFileName)); - if ($this->directory->isReadable($this->relativeFileName)) { - $this->response->setFilePath($this->directory->getAbsolutePath($this->relativeFileName)); + if ($this->directoryPub->isReadable($this->relativeFileName)) { + $this->response->setFilePath($this->directoryPub->getAbsolutePath($this->relativeFileName)); } else { $this->setPlaceholderImage(); } @@ -182,7 +191,17 @@ public function launch(): ResponseInterface } /** - * Set Placeholder as a response + * Check if media directory changed + * + * @return bool + */ + private function checkMediaDirectoryChanged(): bool + { + return rtrim($this->mediaDirectoryPath, '/') !== rtrim($this->directoryMedia->getAbsolutePath(), '/'); + } + + /** + * Set placeholder image into response * * @return void */ diff --git a/app/code/Magento/MediaStorage/Service/ImageResize.php b/app/code/Magento/MediaStorage/Service/ImageResize.php index d061ddbd3dc46..d5ce1a7e20f98 100644 --- a/app/code/Magento/MediaStorage/Service/ImageResize.php +++ b/app/code/Magento/MediaStorage/Service/ImageResize.php @@ -15,17 +15,18 @@ use Magento\Framework\App\ObjectManager; use Magento\Framework\Exception\NotFoundException; use Magento\Framework\Filesystem; +use Magento\Framework\Filesystem\Directory\WriteInterface; use Magento\Framework\Image; use Magento\Framework\Image\Factory as ImageFactory; use Magento\Catalog\Model\Product\Media\ConfigInterface as MediaConfig; use Magento\Framework\App\State; use Magento\Framework\View\ConfigInterface as ViewConfig; -use \Magento\Catalog\Model\ResourceModel\Product\Image as ProductImage; +use Magento\Catalog\Model\ResourceModel\Product\Image as ProductImage; use Magento\Store\Model\StoreManagerInterface; use Magento\Theme\Model\Config\Customization as ThemeCustomizationConfig; -use Magento\Theme\Model\ResourceModel\Theme\Collection; +use Magento\Theme\Model\ResourceModel\Theme\Collection as ThemeCollection; use Magento\Framework\App\Filesystem\DirectoryList; -use Magento\MediaStorage\Helper\File\Storage\Database; +use Magento\MediaStorage\Helper\File\Storage\Database as FileStorageDatabase; use Magento\Theme\Model\Theme; /** @@ -76,24 +77,20 @@ class ImageResize private $themeCustomizationConfig; /** - * @var Collection + * @var ThemeCollection */ private $themeCollection; /** - * @var Filesystem + * @var WriteInterface */ private $mediaDirectory; /** - * @var Filesystem - */ - private $filesystem; - - /** - * @var Database + * @var FileStorageDatabase */ private $fileStorageDatabase; + /** * @var StoreManagerInterface */ @@ -108,9 +105,9 @@ class ImageResize * @param ViewConfig $viewConfig * @param AssertImageFactory $assertImageFactory * @param ThemeCustomizationConfig $themeCustomizationConfig - * @param Collection $themeCollection + * @param ThemeCollection $themeCollection * @param Filesystem $filesystem - * @param Database $fileStorageDatabase + * @param FileStorageDatabase $fileStorageDatabase * @param StoreManagerInterface $storeManager * @throws \Magento\Framework\Exception\FileSystemException * @internal param ProductImage $gallery @@ -125,9 +122,9 @@ public function __construct( ViewConfig $viewConfig, AssertImageFactory $assertImageFactory, ThemeCustomizationConfig $themeCustomizationConfig, - Collection $themeCollection, + ThemeCollection $themeCollection, Filesystem $filesystem, - Database $fileStorageDatabase = null, + FileStorageDatabase $fileStorageDatabase = null, StoreManagerInterface $storeManager = null ) { $this->appState = $appState; @@ -140,9 +137,8 @@ public function __construct( $this->themeCustomizationConfig = $themeCustomizationConfig; $this->themeCollection = $themeCollection; $this->mediaDirectory = $filesystem->getDirectoryWrite(DirectoryList::MEDIA); - $this->filesystem = $filesystem; $this->fileStorageDatabase = $fileStorageDatabase ?: - ObjectManager::getInstance()->get(Database::class); + ObjectManager::getInstance()->get(FileStorageDatabase::class); $this->storeManager = $storeManager ?? ObjectManager::getInstance()->get(StoreManagerInterface::class); } @@ -294,7 +290,7 @@ private function makeImage(string $originalImagePath, array $imageParams): Image } /** - * Resize image. + * Resize image if not already resized before * * @param array $imageParams * @param string $originalImagePath @@ -303,13 +299,48 @@ private function makeImage(string $originalImagePath, array $imageParams): Image private function resize(array $imageParams, string $originalImagePath, string $originalImageName) { unset($imageParams['id']); - $image = $this->makeImage($originalImagePath, $imageParams); $imageAsset = $this->assertImageFactory->create( [ 'miscParams' => $imageParams, 'filePath' => $originalImageName, ] ); + $imageAssetPath = $imageAsset->getPath(); + $usingDbAsStorage = $this->fileStorageDatabase->checkDbUsage(); + $mediaStorageFilename = $this->mediaDirectory->getRelativePath($imageAssetPath); + + $alreadyResized = $usingDbAsStorage ? + $this->fileStorageDatabase->fileExists($mediaStorageFilename) : + $this->mediaDirectory->isFile($imageAssetPath); + + if (!$alreadyResized) { + $this->generateResizedImage( + $imageParams, + $originalImagePath, + $imageAssetPath, + $usingDbAsStorage, + $mediaStorageFilename + ); + } + } + + /** + * Generate resized image + * + * @param array $imageParams + * @param string $originalImagePath + * @param string $imageAssetPath + * @param bool $usingDbAsStorage + * @param string $mediaStorageFilename + */ + private function generateResizedImage( + array $imageParams, + string $originalImagePath, + string $imageAssetPath, + bool $usingDbAsStorage, + string $mediaStorageFilename + ) { + $image = $this->makeImage($originalImagePath, $imageParams); if ($imageParams['image_width'] !== null && $imageParams['image_height'] !== null) { $image->resize($imageParams['image_width'], $imageParams['image_height']); @@ -335,11 +366,10 @@ private function resize(array $imageParams, string $originalImagePath, string $o $image->watermark($this->getWatermarkFilePath($imageParams['watermark_file'])); } - $image->save($imageAsset->getPath()); + $image->save($imageAssetPath); - if ($this->fileStorageDatabase->checkDbUsage()) { - $mediastoragefilename = $this->mediaDirectory->getRelativePath($imageAsset->getPath()); - $this->fileStorageDatabase->saveFile($mediastoragefilename); + if ($usingDbAsStorage) { + $this->fileStorageDatabase->saveFile($mediaStorageFilename); } } diff --git a/app/code/Magento/MediaStorage/Test/Unit/App/MediaTest.php b/app/code/Magento/MediaStorage/Test/Unit/App/MediaTest.php index 8d3211684d377..13ff664bf8064 100644 --- a/app/code/Magento/MediaStorage/Test/Unit/App/MediaTest.php +++ b/app/code/Magento/MediaStorage/Test/Unit/App/MediaTest.php @@ -15,6 +15,7 @@ use Magento\Framework\Filesystem; use Magento\Framework\Filesystem\Directory\Read; use Magento\Framework\Filesystem\Directory\WriteInterface; +use Magento\Framework\Filesystem\DriverPool; use Magento\Framework\TestFramework\Unit\Helper\ObjectManager; use Magento\MediaStorage\App\Media; use Magento\MediaStorage\Model\File\Storage\Config; @@ -74,7 +75,12 @@ class MediaTest extends TestCase /** * @var Read|MockObject */ - private $directoryMock; + private $directoryMediaMock; + + /** + * @var \Magento\Framework\Filesystem\Directory\Read|\PHPUnit_Framework_MockObject_MockObject + */ + private $directoryPubMock; protected function setUp() { @@ -96,12 +102,30 @@ protected function setUp() ->will($this->returnValue($this->sync)); $this->filesystemMock = $this->createMock(Filesystem::class); - $this->directoryMock = $this->getMockForAbstractClass(WriteInterface::class); - + $this->directoryPubMock = $this->getMockForAbstractClass( + WriteInterface::class, + [], + '', + false, + true, + true, + ['isReadable', 'getAbsolutePath'] + ); + $this->directoryMediaMock = $this->getMockForAbstractClass( + WriteInterface::class, + [], + '', + false, + true, + true, + ['getAbsolutePath'] + ); $this->filesystemMock->expects($this->any()) ->method('getDirectoryWrite') - ->with(DirectoryList::PUB) - ->will($this->returnValue($this->directoryMock)); + ->willReturnMap([ + [DirectoryList::PUB, DriverPool::FILE, $this->directoryPubMock], + [DirectoryList::MEDIA, DriverPool::FILE, $this->directoryMediaMock], + ]); $this->responseMock = $this->createMock(Response::class); } @@ -116,17 +140,17 @@ public function testProcessRequestCreatesConfigFileMediaDirectoryIsNotProvided() $this->mediaModel = $this->getMediaModel(); $filePath = '/absolute/path/to/test/file.png'; - $this->directoryMock->expects($this->any()) + $this->directoryMediaMock->expects($this->once()) + ->method('getAbsolutePath') + ->with(null) + ->will($this->returnValue(self::MEDIA_DIRECTORY)); + $this->directoryPubMock->expects($this->once()) ->method('getAbsolutePath') - ->will($this->returnValueMap( - [ - [null, self::MEDIA_DIRECTORY], - [self::RELATIVE_FILE_PATH, $filePath], - ] - )); + ->with(self::RELATIVE_FILE_PATH) + ->will($this->returnValue($filePath)); $this->configMock->expects($this->once())->method('save'); $this->sync->expects($this->once())->method('synchronize')->with(self::RELATIVE_FILE_PATH); - $this->directoryMock->expects($this->once()) + $this->directoryPubMock->expects($this->once()) ->method('isReadable') ->with(self::RELATIVE_FILE_PATH) ->will($this->returnValue(true)); @@ -140,18 +164,18 @@ public function testProcessRequestReturnsFileIfItsProperlySynchronized() $filePath = '/absolute/path/to/test/file.png'; $this->sync->expects($this->once())->method('synchronize')->with(self::RELATIVE_FILE_PATH); - $this->directoryMock->expects($this->once()) + $this->directoryMediaMock->expects($this->once()) + ->method('getAbsolutePath') + ->with(null) + ->will($this->returnValue(self::MEDIA_DIRECTORY)); + $this->directoryPubMock->expects($this->once()) ->method('isReadable') ->with(self::RELATIVE_FILE_PATH) ->will($this->returnValue(true)); - $this->directoryMock->expects($this->any()) + $this->directoryPubMock->expects($this->once()) ->method('getAbsolutePath') - ->will($this->returnValueMap( - [ - [null, self::MEDIA_DIRECTORY], - [self::RELATIVE_FILE_PATH, $filePath], - ] - )); + ->with(self::RELATIVE_FILE_PATH) + ->will($this->returnValue($filePath)); $this->responseMock->expects($this->once())->method('setFilePath')->with($filePath); $this->assertSame($this->responseMock, $this->mediaModel->launch()); } @@ -161,11 +185,11 @@ public function testProcessRequestReturnsNotFoundIfFileIsNotSynchronized() $this->mediaModel = $this->getMediaModel(); $this->sync->expects($this->once())->method('synchronize')->with(self::RELATIVE_FILE_PATH); - $this->directoryMock->expects($this->once()) + $this->directoryMediaMock->expects($this->once()) ->method('getAbsolutePath') - ->with() + ->with(null) ->will($this->returnValue(self::MEDIA_DIRECTORY)); - $this->directoryMock->expects($this->once()) + $this->directoryPubMock->expects($this->once()) ->method('isReadable') ->with(self::RELATIVE_FILE_PATH) ->will($this->returnValue(false)); @@ -205,16 +229,10 @@ public function testCatchException($isDeveloper, $setBodyCalls) public function testExceptionWhenIsAllowedReturnsFalse() { $this->mediaModel = $this->getMediaModel(false); - - $filePath = '/absolute/path/to/test/file.png'; - $this->directoryMock->expects($this->any()) + $this->directoryMediaMock->expects($this->once()) ->method('getAbsolutePath') - ->will($this->returnValueMap( - [ - [null, self::MEDIA_DIRECTORY], - [self::RELATIVE_FILE_PATH, $filePath], - ] - )); + ->with(null) + ->will($this->returnValue(self::MEDIA_DIRECTORY)); $this->configMock->expects($this->once())->method('save'); $this->expectException(LogicException::class); diff --git a/app/code/Magento/MediaStorage/Test/Unit/Model/File/Storage/ResponseTest.php b/app/code/Magento/MediaStorage/Test/Unit/Model/File/Storage/ResponseTest.php index cdeb47d2b8490..7c0453fbf3da6 100644 --- a/app/code/Magento/MediaStorage/Test/Unit/Model/File/Storage/ResponseTest.php +++ b/app/code/Magento/MediaStorage/Test/Unit/Model/File/Storage/ResponseTest.php @@ -47,7 +47,7 @@ protected function setUp() public function testSendResponse(): void { $filePath = 'file_path'; - $headers = $this->getMockBuilder(\Zend\Http\Headers::class)->getMock(); + $headers = $this->getMockBuilder(\Laminas\Http\Headers::class)->getMock(); $this->response->setFilePath($filePath); $this->response->setHeaders($headers); $this->transferAdapter diff --git a/app/code/Magento/MediaStorage/Test/Unit/Service/ImageResizeTest.php b/app/code/Magento/MediaStorage/Test/Unit/Service/ImageResizeTest.php index f0e1efa7806e4..29ab475948221 100644 --- a/app/code/Magento/MediaStorage/Test/Unit/Service/ImageResizeTest.php +++ b/app/code/Magento/MediaStorage/Test/Unit/Service/ImageResizeTest.php @@ -24,8 +24,6 @@ use Magento\Framework\App\Filesystem\DirectoryList; /** - * Class ImageResizeTest - * * @SuppressWarnings(PHPMD.TooManyFields) * @SuppressWarnings(PHPMD.CouplingBetweenObjects) */ @@ -249,6 +247,9 @@ public function testResizeFromThemesMediaStorageDatabase() $this->databaseMock->expects($this->any()) ->method('checkDbUsage') ->will($this->returnValue(true)); + $this->databaseMock->expects($this->any()) + ->method('fileExists') + ->will($this->returnValue(false)); $this->productImageMock->expects($this->any()) ->method('getCountUsedProductImages') @@ -289,6 +290,9 @@ public function testResizeFromImageNameMediaStorageDatabase() $this->databaseMock->expects($this->any()) ->method('checkDbUsage') ->will($this->returnValue(true)); + $this->databaseMock->expects($this->any()) + ->method('fileExists') + ->will($this->returnValue(false)); $this->mediaDirectoryMock->expects($this->any()) ->method('isFile') @@ -318,4 +322,65 @@ public function testResizeFromImageNameMediaStorageDatabase() $this->service->resizeFromImageName($this->testfilename); } + + public function testSkipResizingAlreadyResizedImageOnDisk() + { + $this->databaseMock->expects($this->any()) + ->method('checkDbUsage') + ->will($this->returnValue(false)); + + $this->mediaDirectoryMock->expects($this->any()) + ->method('isFile') + ->will($this->returnValue(true)); + + $this->themeCollectionMock->expects($this->any()) + ->method('loadRegisteredThemes') + ->willReturn( + [ new DataObject(['id' => '0']) ] + ); + $this->themeCustomizationConfigMock->expects($this->any()) + ->method('getStoresByThemes') + ->willReturn( + ['0' => []] + ); + + $this->imageFactoryMock->expects($this->never()) + ->method('create'); + + $this->service->resizeFromImageName($this->testfilename); + } + + public function testSkipResizingAlreadyResizedImageInDatabase() + { + $this->databaseMock->expects($this->any()) + ->method('checkDbUsage') + ->will($this->returnValue(true)); + $this->databaseMock->expects($this->any()) + ->method('fileExists') + ->will($this->returnValue(true)); + + $this->mediaDirectoryMock->expects($this->any()) + ->method('isFile') + ->with($this->testfilepath) + ->willReturnOnConsecutiveCalls( + $this->returnValue(false), + $this->returnValue(true) + ); + + $this->themeCollectionMock->expects($this->any()) + ->method('loadRegisteredThemes') + ->willReturn( + [ new DataObject(['id' => '0']) ] + ); + $this->themeCustomizationConfigMock->expects($this->any()) + ->method('getStoresByThemes') + ->willReturn( + ['0' => []] + ); + + $this->databaseMock->expects($this->never()) + ->method('saveFile'); + + $this->service->resizeFromImageName($this->testfilename); + } } diff --git a/app/code/Magento/MediaStorage/etc/adminhtml/system.xml b/app/code/Magento/MediaStorage/etc/adminhtml/system.xml index 2c7219fe8afaa..9c8c2c5b24398 100644 --- a/app/code/Magento/MediaStorage/etc/adminhtml/system.xml +++ b/app/code/Magento/MediaStorage/etc/adminhtml/system.xml @@ -10,11 +10,11 @@ <section id="system" type="text" sortOrder="900" showInDefault="1" showInWebsite="1" showInStore="1"> <group id="media_storage_configuration" translate="label" type="text" sortOrder="900" showInDefault="1" showInWebsite="1" showInStore="1"> <label>Storage Configuration for Media</label> - <field id="media_storage" translate="label" type="select" sortOrder="100" showInDefault="1" showInWebsite="0" showInStore="0"> + <field id="media_storage" translate="label" type="select" sortOrder="100" showInDefault="1"> <label>Media Storage</label> <source_model>Magento\MediaStorage\Model\Config\Source\Storage\Media\Storage</source_model> </field> - <field id="media_database" translate="label" type="select" sortOrder="200" showInDefault="1" showInWebsite="0" showInStore="0"> + <field id="media_database" translate="label" type="select" sortOrder="200" showInDefault="1"> <label>Select Media Database</label> <source_model>Magento\MediaStorage\Model\Config\Source\Storage\Media\Database</source_model> <backend_model>Magento\MediaStorage\Model\Config\Backend\Storage\Media\Database</backend_model> @@ -22,11 +22,11 @@ <field id="media_storage">1</field> </depends> </field> - <field id="synchronize" translate="label comment" type="button" sortOrder="300" showInDefault="1" showInWebsite="0" showInStore="0"> + <field id="synchronize" translate="label comment" type="button" sortOrder="300" showInDefault="1"> <frontend_model>Magento\MediaStorage\Block\System\Config\System\Storage\Media\Synchronize</frontend_model> <comment>After selecting a new media storage location, press the Synchronize button to transfer all media to that location and then "Save Config". Media will not be available in the new location until the synchronization process is complete.</comment> </field> - <field id="configuration_update_time" translate="label" type="text" sortOrder="400" showInDefault="1" showInWebsite="0" showInStore="0" canRestore="1"> + <field id="configuration_update_time" translate="label" type="text" sortOrder="400" showInDefault="1" canRestore="1"> <label>Environment Update Time</label> <validate>validate-zero-or-greater validate-digits</validate> </field> diff --git a/app/code/Magento/MediaStorage/registration.php b/app/code/Magento/MediaStorage/registration.php index 8bfa83028eeeb..552f668426125 100644 --- a/app/code/Magento/MediaStorage/registration.php +++ b/app/code/Magento/MediaStorage/registration.php @@ -4,6 +4,6 @@ * See COPYING.txt for license details. */ -use \Magento\Framework\Component\ComponentRegistrar; +use Magento\Framework\Component\ComponentRegistrar; ComponentRegistrar::register(ComponentRegistrar::MODULE, 'Magento_MediaStorage', __DIR__); diff --git a/app/code/Magento/MessageQueue/Test/Unit/Model/ConsumerRunnerTest.php b/app/code/Magento/MessageQueue/Test/Unit/Model/ConsumerRunnerTest.php index 46a142bf488be..5f4bd9178a890 100644 --- a/app/code/Magento/MessageQueue/Test/Unit/Model/ConsumerRunnerTest.php +++ b/app/code/Magento/MessageQueue/Test/Unit/Model/ConsumerRunnerTest.php @@ -3,20 +3,27 @@ * Copyright © Magento, Inc. All rights reserved. * See COPYING.txt for license details. */ +declare(strict_types=1); namespace Magento\MessageQueue\Test\Unit\Model; -use Magento\Framework\MessageQueue\ConsumerInterface; -use Magento\MessageQueue\Model\ConsumerRunner; +use Magento\Framework\App\MaintenanceMode; use Magento\Framework\Exception\LocalizedException; +use Magento\Framework\MessageQueue\ConsumerFactory; +use Magento\Framework\MessageQueue\ConsumerInterface; use Magento\Framework\Phrase; +use Magento\Framework\TestFramework\Unit\Helper\ObjectManager; +use Magento\MessageQueue\Model\ConsumerRunner; +use PHPUnit\Framework\MockObject\MockObject; +use PHPUnit\Framework\TestCase; -/** - * Unit tests for consumer runner - */ -class ConsumerRunnerTest extends \PHPUnit\Framework\TestCase +class ConsumerRunnerTest extends TestCase { - /** @var \Magento\Framework\TestFramework\Unit\Helper\ObjectManager */ + const STUB_SLEEP_INTERVAL = 0; + + /** + * @var ObjectManager + */ private $objectManager; /** @@ -25,12 +32,12 @@ class ConsumerRunnerTest extends \PHPUnit\Framework\TestCase private $consumerRunner; /** - * @var \Magento\Framework\MessageQueue\ConsumerFactory|\PHPUnit_Framework_MockObject_MockObject + * @var ConsumerFactory|MockObject */ private $consumerFactoryMock; /** - * @var \Magento\Framework\App\MaintenanceMode|\PHPUnit_Framework_MockObject_MockObject + * @var MaintenanceMode|MockObject */ private $maintenanceModeMock; @@ -39,21 +46,23 @@ class ConsumerRunnerTest extends \PHPUnit\Framework\TestCase */ protected function setUp() { - $this->objectManager = new \Magento\Framework\TestFramework\Unit\Helper\ObjectManager($this); - $this->consumerFactoryMock = $this->getMockBuilder(\Magento\Framework\MessageQueue\ConsumerFactory::class) + $this->objectManager = new ObjectManager($this); + + $this->consumerFactoryMock = $this->getMockBuilder(ConsumerFactory::class) ->disableOriginalConstructor() ->getMock(); - $this->maintenanceModeMock = $this->getMockBuilder(\Magento\Framework\App\MaintenanceMode::class) + $this->maintenanceModeMock = $this->getMockBuilder(MaintenanceMode::class) ->disableOriginalConstructor() ->getMock(); + $this->consumerRunner = $this->objectManager->getObject( - \Magento\MessageQueue\Model\ConsumerRunner::class, + ConsumerRunner::class, [ 'consumerFactory' => $this->consumerFactoryMock, - 'maintenanceMode' => $this->maintenanceModeMock + 'maintenanceMode' => $this->maintenanceModeMock, + 'maintenanceSleepInterval' => self::STUB_SLEEP_INTERVAL ] ); - parent::setUp(); } /** @@ -64,8 +73,8 @@ protected function setUp() public function testMagicMethod() { $isMaintenanceModeOn = false; - /** @var ConsumerInterface|\PHPUnit_Framework_MockObject_MockObject $consumerMock */ - $consumerMock = $this->getMockBuilder(\Magento\Framework\MessageQueue\ConsumerInterface::class)->getMock(); + /** @var ConsumerInterface|MockObject $consumerMock */ + $consumerMock = $this->getMockBuilder(ConsumerInterface::class)->getMock(); $consumerMock->expects($this->once())->method('process'); $consumerName = 'someConsumerName'; $this->consumerFactoryMock @@ -81,12 +90,13 @@ public function testMagicMethod() /** * Ensure that exception will be thrown if requested magic method does not correspond to any declared consumer. * - * @expectedException \Magento\Framework\Exception\LocalizedException - * @expectedExceptionMessage "nonDeclaredConsumer" callback method specified in crontab.xml must * @return void */ public function testMagicMethodNoRelatedConsumer() { + $this->expectException(LocalizedException::class); + $this->expectExceptionMessage('"nonDeclaredConsumer" callback method specified in crontab.xml must'); + $consumerName = 'nonDeclaredConsumer'; $this->consumerFactoryMock ->expects($this->once()) @@ -105,8 +115,9 @@ public function testMagicMethodNoRelatedConsumer() public function testMagicMethodMaintenanceModeIsOn() { $isMaintenanceModeOn = true; - /** @var ConsumerInterface|\PHPUnit_Framework_MockObject_MockObject $consumerMock */ - $consumerMock = $this->getMockBuilder(\Magento\Framework\MessageQueue\ConsumerInterface::class)->getMock(); + + /** @var ConsumerInterface|MockObject $consumerMock */ + $consumerMock = $this->getMockBuilder(ConsumerInterface::class)->getMock(); $consumerMock->expects($this->never())->method('process'); $consumerName = 'someConsumerName'; $this->consumerFactoryMock diff --git a/app/code/Magento/MessageQueue/registration.php b/app/code/Magento/MessageQueue/registration.php index e27fa71517427..5608dbce482a9 100644 --- a/app/code/Magento/MessageQueue/registration.php +++ b/app/code/Magento/MessageQueue/registration.php @@ -4,6 +4,6 @@ * See COPYING.txt for license details. */ -use \Magento\Framework\Component\ComponentRegistrar; +use Magento\Framework\Component\ComponentRegistrar; ComponentRegistrar::register(ComponentRegistrar::MODULE, 'Magento_MessageQueue', __DIR__); diff --git a/app/code/Magento/Msrp/Test/Mftf/Data/CustomAttributeData.xml b/app/code/Magento/Msrp/Test/Mftf/Data/CustomAttributeData.xml new file mode 100644 index 0000000000000..6f620c8f9aa7f --- /dev/null +++ b/app/code/Magento/Msrp/Test/Mftf/Data/CustomAttributeData.xml @@ -0,0 +1,14 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + /** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +--> +<entities xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:noNamespaceSchemaLocation="urn:magento:mftf:DataGenerator/etc/dataProfileSchema.xsd"> + <entity name="ApiProductMsrp" type="custom_attribute"> + <data key="attribute_code">msrp</data> + <data key="value">111.11</data> + </entity> +</entities> diff --git a/app/code/Magento/Msrp/Test/Mftf/Data/ProductData.xml b/app/code/Magento/Msrp/Test/Mftf/Data/ProductData.xml new file mode 100644 index 0000000000000..aa79b6032df4b --- /dev/null +++ b/app/code/Magento/Msrp/Test/Mftf/Data/ProductData.xml @@ -0,0 +1,16 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + /** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +--> + +<entities xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:noNamespaceSchemaLocation="urn:magento:mftf:DataGenerator/etc/dataProfileSchema.xsd"> + <entity name="SimpleOutOfStockProductWithSpecialPriceCostAndMsrp" type="product" extends="SimpleOutOfStockProduct"> + <requiredEntity type="custom_attribute_array">ApiProductSpecialPrice</requiredEntity> + <requiredEntity type="custom_attribute_array">ApiProductCost</requiredEntity> + <requiredEntity type="custom_attribute_array">ApiProductMsrp</requiredEntity> + </entity> +</entities> diff --git a/app/code/Magento/Msrp/Test/Mftf/Metadata/msrp_settings-meta.xml b/app/code/Magento/Msrp/Test/Mftf/Metadata/MsrpSettingsMeta.xml similarity index 100% rename from app/code/Magento/Msrp/Test/Mftf/Metadata/msrp_settings-meta.xml rename to app/code/Magento/Msrp/Test/Mftf/Metadata/MsrpSettingsMeta.xml diff --git a/app/code/Magento/Msrp/Test/Mftf/Test/AdminCheckProductListPriceAttributesTest.xml b/app/code/Magento/Msrp/Test/Mftf/Test/AdminCheckProductListPriceAttributesTest.xml new file mode 100644 index 0000000000000..874edf0dff9e3 --- /dev/null +++ b/app/code/Magento/Msrp/Test/Mftf/Test/AdminCheckProductListPriceAttributesTest.xml @@ -0,0 +1,29 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + /** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +--> + +<tests xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/testSchema.xsd"> + <test name="AdminCheckProductListPriceAttributesTest"> + <annotations> + <group value="msrp"/> + </annotations> + <before> + <createData entity="SimpleOutOfStockProductWithSpecialPriceCostAndMsrp" stepKey="createSimpleProduct"/> + </before> + + <actionGroup ref="CheckAdminProductGridColumnOptionActionGroup" stepKey="checkMsrpOption" after="checkCostOption"> + <argument name="optionName" value="Minimum Advertised Price"/> + </actionGroup> + + <actionGroup ref="AssertAdminProductGridCellActionGroup" stepKey="seeCorrectMsrp" after="seeCorrectCost"> + <argument name="row" value="1"/> + <argument name="column" value="Minimum Advertised Price"/> + <argument name="value" value="${{ApiProductMsrp.value}}"/> + </actionGroup> + </test> +</tests> diff --git a/app/code/Magento/Msrp/Test/Mftf/Test/StorefrontProductWithMapAssignedConfigProductIsCorrectTest.xml b/app/code/Magento/Msrp/Test/Mftf/Test/StorefrontProductWithMapAssignedConfigProductIsCorrectTest.xml index ccb724e8bf199..2bfb1239cba60 100644 --- a/app/code/Magento/Msrp/Test/Mftf/Test/StorefrontProductWithMapAssignedConfigProductIsCorrectTest.xml +++ b/app/code/Magento/Msrp/Test/Mftf/Test/StorefrontProductWithMapAssignedConfigProductIsCorrectTest.xml @@ -20,7 +20,7 @@ <group value="Msrp"/> </annotations> <before> - <actionGroup ref="LoginAsAdmin" stepKey="loginAsAdmin"/> + <actionGroup ref="AdminLoginActionGroup" stepKey="loginAsAdmin"/> <!--Create category--> <createData entity="SimpleSubCategory" stepKey="createCategory"/> @@ -104,11 +104,16 @@ <!--Disable Minimum advertised Price--> <createData entity="MsrpDisableMAP" stepKey="disableMAP"/> - <actionGroup ref="logout" stepKey="logoutOfAdmin"/> + <actionGroup ref="AdminLogoutActionGroup" stepKey="logoutOfAdmin"/> + + <!-- Reindex invalidated indices after product attribute has been created/deleted --> + <magentoCron groups="index" stepKey="reindexInvalidatedIndices"/> </after> <!-- Set Manufacturer's Suggested Retail Price to products--> - <amOnPage url="{{AdminProductEditPage.url($$createConfigChildProduct1.id$$)}}" stepKey="goToFirstChildProductEditPage"/> + <actionGroup ref="AdminProductPageOpenByIdActionGroup" stepKey="goToFirstChildProductEditPage"> + <argument name="productId" value="$$createConfigChildProduct1.id$$"/> + </actionGroup> <waitForPageLoad stepKey="waitForProductPageLoad"/> <click selector="{{AdminProductFormSection.advancedPricingLink}}" stepKey="clickOnAdvancedPricingButton"/> <waitForElement selector="{{AdminProductFormAdvancedPricingSection.msrp}}" stepKey="waitForMsrp"/> @@ -116,7 +121,9 @@ <click selector="{{AdminProductFormAdvancedPricingSection.doneButton}}" stepKey="clickDoneButton"/> <actionGroup ref="SaveProductFormActionGroup" stepKey="saveProduct1"/> - <amOnPage url="{{AdminProductEditPage.url($$createConfigChildProduct2.id$$)}}" stepKey="goToSecondChildProductEditPage"/> + <actionGroup ref="AdminProductPageOpenByIdActionGroup" stepKey="goToSecondChildProductEditPage"> + <argument name="productId" value="$$createConfigChildProduct2.id$$"/> + </actionGroup> <waitForPageLoad stepKey="waitForProductPageLoad1"/> <click selector="{{AdminProductFormSection.advancedPricingLink}}" stepKey="clickOnAdvancedPricingButton1"/> <waitForElement selector="{{AdminProductFormAdvancedPricingSection.msrp}}" stepKey="waitForMsrp1"/> @@ -131,28 +138,40 @@ <amOnPage url="{{StorefrontProductPage.url($$createConfigProduct.custom_attributes[url_key]$$)}}" stepKey="navigateToConfigProductPage"/> <waitForPageLoad stepKey="waitForLoadConfigProductPage"/> <grabTextFrom selector="{{StorefrontProductInfoMainSection.mapPrice}}" stepKey="grabMapPrice"/> - <assertEquals expected='$66.00' expectedType="string" actual="($grabMapPrice)" stepKey="assertMapPrice"/> + <assertEquals stepKey="assertMapPrice"> + <actualResult type="const">($grabMapPrice)</actualResult> + <expectedResult type="string">$66.00</expectedResult> + </assertEquals> <seeElement selector="{{StorefrontProductInfoMainSection.clickForPriceLink}}" stepKey="checkClickForPriceLink"/> <!--Check msrp for second child product--> <selectOption selector="{{StorefrontProductInfoMainSection.productAttributeOptionsSelectButton}}" userInput="$$getConfigAttributeOption2.value$$" stepKey="selectSecondOption"/> <waitForElement selector="{{StorefrontProductInfoMainSection.mapPrice}}" stepKey="waitForLoad"/> <grabTextFrom selector="{{StorefrontProductInfoMainSection.mapPrice}}" stepKey="grabSecondProductMapPrice"/> - <assertEquals expected='$66.00' expectedType="string" actual="($grabSecondProductMapPrice)" stepKey="assertSecondProductMapPrice"/> + <assertEquals stepKey="assertSecondProductMapPrice"> + <actualResult type="const">($grabSecondProductMapPrice)</actualResult> + <expectedResult type="string">$66.00</expectedResult> + </assertEquals> <seeElement selector="{{StorefrontProductInfoMainSection.clickForPriceLink}}" stepKey="checkClickForPriceLinkForSecondProduct"/> <!--Check msrp for first child product--> <selectOption selector="{{StorefrontProductInfoMainSection.productAttributeOptionsSelectButton}}" userInput="$$getConfigAttributeOption1.value$$" stepKey="selectFirstOption"/> <waitForElement selector="{{StorefrontProductInfoMainSection.mapPrice}}" stepKey="waitForLoad1"/> <grabTextFrom selector="{{StorefrontProductInfoMainSection.mapPrice}}" stepKey="grabFirstProductMapPrice"/> - <assertEquals expected='$55.00' expectedType="string" actual="($grabFirstProductMapPrice)" stepKey="assertFirstProductMapPrice"/> + <assertEquals stepKey="assertFirstProductMapPrice"> + <actualResult type="const">($grabFirstProductMapPrice)</actualResult> + <expectedResult type="string">$55.00</expectedResult> + </assertEquals> <seeElement selector="{{StorefrontProductInfoMainSection.clickForPriceLink}}" stepKey="checkClickForPriceLinkForFirstProduct"/> <!--Check price for third child product--> <selectOption selector="{{StorefrontProductInfoMainSection.productAttributeOptionsSelectButton}}" userInput="$$getConfigAttributeOption3.value$$" stepKey="selectThirdOption"/> <waitForElement selector="{{StorefrontProductInfoMainSection.mapPrice}}" stepKey="waitForLoad2"/> <grabTextFrom selector="{{StorefrontProductInfoMainSection.productPrice}}" stepKey="grabThirdProductMapPrice"/> - <assertEquals expected='$70.00' expectedType="string" actual="($grabThirdProductMapPrice)" stepKey="assertThirdProductMapPrice"/> + <assertEquals stepKey="assertThirdProductMapPrice"> + <actualResult type="const">($grabThirdProductMapPrice)</actualResult> + <expectedResult type="string">$70.00</expectedResult> + </assertEquals> <dontSeeElement selector="{{StorefrontProductInfoMainSection.clickForPriceLink}}" stepKey="checkClickForPriceLinkForThirdProduct"/> </test> </tests> diff --git a/app/code/Magento/Msrp/etc/adminhtml/di.xml b/app/code/Magento/Msrp/etc/adminhtml/di.xml index 6126e9132cd7a..d517bd0aa4e7f 100644 --- a/app/code/Magento/Msrp/etc/adminhtml/di.xml +++ b/app/code/Magento/Msrp/etc/adminhtml/di.xml @@ -19,4 +19,11 @@ </argument> </arguments> </virtualType> + <type name="Magento\Catalog\Ui\DataProvider\Product\Modifier\PriceAttributes"> + <arguments> + <argument name="priceAttributeList" xsi:type="array"> + <item name="msrp" xsi:type="string">msrp</item> + </argument> + </arguments> + </type> </config> diff --git a/app/code/Magento/Msrp/etc/adminhtml/system.xml b/app/code/Magento/Msrp/etc/adminhtml/system.xml index c20d753a2e794..8f6c3750c3835 100644 --- a/app/code/Magento/Msrp/etc/adminhtml/system.xml +++ b/app/code/Magento/Msrp/etc/adminhtml/system.xml @@ -8,16 +8,16 @@ <config xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="urn:magento:module:Magento_Config:etc/system_file.xsd"> <system> <section id="sales"> - <group id="msrp" translate="label" type="text" sortOrder="110" showInDefault="1" showInWebsite="1" showInStore="0"> + <group id="msrp" translate="label" type="text" sortOrder="110" showInDefault="1" showInWebsite="1"> <label>Minimum Advertised Price</label> - <field id="enabled" translate="label comment" type="select" sortOrder="10" showInDefault="1" showInWebsite="1" showInStore="0" canRestore="1"> + <field id="enabled" translate="label comment" type="select" sortOrder="10" showInDefault="1" showInWebsite="1" canRestore="1"> <label>Enable MAP</label> <source_model>Magento\Config\Model\Config\Source\Yesno</source_model> <comment> <![CDATA[<strong style="color:red">Warning!</strong> Enabling MAP by default will hide all product prices on Storefront.]]> </comment> </field> - <field id="display_price_type" translate="label" type="select" sortOrder="30" showInDefault="1" showInWebsite="1" showInStore="0" canRestore="1"> + <field id="display_price_type" translate="label" type="select" sortOrder="30" showInDefault="1" showInWebsite="1" canRestore="1"> <label>Display Actual Price</label> <source_model>Magento\Msrp\Model\Product\Attribute\Source\Type</source_model> </field> diff --git a/app/code/Magento/Msrp/registration.php b/app/code/Magento/Msrp/registration.php index 0c0269b39eeac..147d789975c3e 100644 --- a/app/code/Magento/Msrp/registration.php +++ b/app/code/Magento/Msrp/registration.php @@ -4,6 +4,6 @@ * See COPYING.txt for license details. */ -use \Magento\Framework\Component\ComponentRegistrar; +use Magento\Framework\Component\ComponentRegistrar; ComponentRegistrar::register(ComponentRegistrar::MODULE, 'Magento_Msrp', __DIR__); diff --git a/app/code/Magento/Msrp/view/base/web/js/msrp.js b/app/code/Magento/Msrp/view/base/web/js/msrp.js index 65af87d85de51..db407fbb22ce0 100644 --- a/app/code/Magento/Msrp/view/base/web/js/msrp.js +++ b/app/code/Magento/Msrp/view/base/web/js/msrp.js @@ -299,8 +299,9 @@ define([ * @param {Event} event * @param {mixed} priceIndex * @param {Object} prices + * @param {Object|undefined} $priceBox */ - onUpdateMsrpPrice: function onUpdateMsrpPrice(event, priceIndex, prices) { + onUpdateMsrpPrice: function onUpdateMsrpPrice(event, priceIndex, prices, $priceBox) { var defaultMsrp, defaultPrice, @@ -322,18 +323,20 @@ define([ finalPrice = prices[priceIndex].finalPrice.amount; if (msrpPrice === null || msrpPrice <= finalPrice) { - this.updateNonMsrpPrice(priceUtils.formatPrice(finalPrice)); + this.updateNonMsrpPrice(priceUtils.formatPrice(finalPrice), $priceBox); } else { this.updateMsrpPrice( priceUtils.formatPrice(finalPrice), priceUtils.formatPrice(msrpPrice), - false); + false, + $priceBox); } } else { this.updateMsrpPrice( priceUtils.formatPrice(defaultPrice), priceUtils.formatPrice(defaultMsrp), - true); + true, + $priceBox); } }, @@ -343,13 +346,14 @@ define([ * @param {String} finalPrice * @param {String} msrpPrice * @param {Boolean} useDefaultPrice + * @param {Object|undefined} $priceBox */ - updateMsrpPrice: function (finalPrice, msrpPrice, useDefaultPrice) { + updateMsrpPrice: function (finalPrice, msrpPrice, useDefaultPrice, $priceBox) { var options = this.tierOptions || this.options; - $(this.options.fallbackPriceContainer).hide(); - $(this.options.displayPriceContainer).show(); - $(this.options.mapInfoLinks).show(); + $(this.options.fallbackPriceContainer, $priceBox).hide(); + $(this.options.displayPriceContainer, $priceBox).show(); + $(this.options.mapInfoLinks, $priceBox).show(); if (useDefaultPrice || !this.wasOpened) { if (this.$popup) { @@ -357,14 +361,14 @@ define([ this.$popup.find(this.options.priceLabelId).html(options.realPrice); } - $(this.options.displayPriceElement).html(msrpPrice); + $(this.options.displayPriceElement, $priceBox).html(msrpPrice); this.wasOpened = true; } if (!useDefaultPrice) { this.$popup.find(this.options.msrpPriceElement).html(msrpPrice); this.$popup.find(this.options.priceElement).html(finalPrice); - $(this.options.displayPriceElement).html(msrpPrice); + $(this.options.displayPriceElement, $priceBox).html(msrpPrice); } }, @@ -372,12 +376,13 @@ define([ * Display non MAP price for irrelevant products * * @param {String} price + * @param {Object|undefined} $priceBox */ - updateNonMsrpPrice: function (price) { - $(this.options.fallbackPriceElement).html(price); - $(this.options.displayPriceContainer).hide(); - $(this.options.mapInfoLinks).hide(); - $(this.options.fallbackPriceContainer).show(); + updateNonMsrpPrice: function (price, $priceBox) { + $(this.options.fallbackPriceElement, $priceBox).html(price); + $(this.options.displayPriceContainer, $priceBox).hide(); + $(this.options.mapInfoLinks, $priceBox).hide(); + $(this.options.fallbackPriceContainer, $priceBox).show(); }, /** diff --git a/app/code/Magento/MsrpConfigurableProduct/registration.php b/app/code/Magento/MsrpConfigurableProduct/registration.php index d4d58ec3c013b..e0772ae9c78fd 100644 --- a/app/code/Magento/MsrpConfigurableProduct/registration.php +++ b/app/code/Magento/MsrpConfigurableProduct/registration.php @@ -4,6 +4,6 @@ * See COPYING.txt for license details. */ -use \Magento\Framework\Component\ComponentRegistrar; +use Magento\Framework\Component\ComponentRegistrar; ComponentRegistrar::register(ComponentRegistrar::MODULE, 'Magento_MsrpConfigurableProduct', __DIR__); diff --git a/app/code/Magento/MsrpGroupedProduct/registration.php b/app/code/Magento/MsrpGroupedProduct/registration.php index c5a261e66c640..a429ec00cd437 100644 --- a/app/code/Magento/MsrpGroupedProduct/registration.php +++ b/app/code/Magento/MsrpGroupedProduct/registration.php @@ -4,6 +4,6 @@ * See COPYING.txt for license details. */ -use \Magento\Framework\Component\ComponentRegistrar; +use Magento\Framework\Component\ComponentRegistrar; ComponentRegistrar::register(ComponentRegistrar::MODULE, 'Magento_MsrpGroupedProduct', __DIR__); diff --git a/app/code/Magento/Multishipping/Model/Cart/CartTotalRepositoryPlugin.php b/app/code/Magento/Multishipping/Model/Cart/CartTotalRepositoryPlugin.php index 732bdee314f7c..bb225a5c46228 100644 --- a/app/code/Magento/Multishipping/Model/Cart/CartTotalRepositoryPlugin.php +++ b/app/code/Magento/Multishipping/Model/Cart/CartTotalRepositoryPlugin.php @@ -33,21 +33,21 @@ public function __construct( } /** - * Overwrite the CartTotalRepository quoteTotal and update the shipping price + * Check multishipping update shipping price after get cart total * - * @param CartTotalRepository $subject - * @param Totals $quoteTotals - * @param String $cartId - * @return Totals - * @throws \Magento\Framework\Exception\NoSuchEntityException + * @param CartTotalRepository $subject + * @param Totals $quoteTotals + * @param int $cartId + * @return Totals + * @throws \Magento\Framework\Exception\NoSuchEntityException * @SuppressWarnings(PHPMD.UnusedLocalVariable) * @SuppressWarnings(PHPMD.UnusedFormalParameter) */ public function afterGet( CartTotalRepository $subject, Totals $quoteTotals, - String $cartId - ) { + $cartId + ) : Totals { $quote = $this->quoteRepository->getActive($cartId); if ($quote->getIsMultiShipping()) { $shippingMethod = $quote->getShippingAddress()->getShippingMethod(); diff --git a/app/code/Magento/Multishipping/Model/Checkout/Type/Multishipping.php b/app/code/Magento/Multishipping/Model/Checkout/Type/Multishipping.php index 7fa674505461e..49212202b5f62 100644 --- a/app/code/Magento/Multishipping/Model/Checkout/Type/Multishipping.php +++ b/app/code/Magento/Multishipping/Model/Checkout/Type/Multishipping.php @@ -1262,7 +1262,7 @@ private function searchQuoteAddressId(OrderInterface $order, array $addresses): } } - throw new NotFoundException(__('Quote address for failed order not found.')); + throw new NotFoundException(__('Quote address for failed order ID "%1" not found.', $order->getEntityId())); } /** diff --git a/app/code/Magento/Multishipping/Test/Mftf/ActionGroup/AdminSalesOrderActionGroup.xml b/app/code/Magento/Multishipping/Test/Mftf/ActionGroup/AdminSalesOrderActionGroup.xml index dcd8bfd8d141b..c03a812f7d372 100644 --- a/app/code/Magento/Multishipping/Test/Mftf/ActionGroup/AdminSalesOrderActionGroup.xml +++ b/app/code/Magento/Multishipping/Test/Mftf/ActionGroup/AdminSalesOrderActionGroup.xml @@ -6,7 +6,7 @@ */ --> <actionGroups xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" - xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/testSchema.xsd"> + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/actionGroupSchema.xsd"> <actionGroup name="AdminSalesOrderActionGroup"> <scrollTo selector="{{AdminOrderTotalSection.subTotal}}" stepKey="scrollToOrderTotalSection"/> <grabTextFrom selector="{{AdminOrderTotalSection.subTotal}}" stepKey="grabValueForSubtotal"/> diff --git a/app/code/Magento/Multishipping/Test/Mftf/ActionGroup/AssertStorefrontSalesOrderMatchesGrandTotalActionGroup.xml b/app/code/Magento/Multishipping/Test/Mftf/ActionGroup/AssertStorefrontSalesOrderMatchesGrandTotalActionGroup.xml index 559d759e0468d..6ff1524d907e3 100644 --- a/app/code/Magento/Multishipping/Test/Mftf/ActionGroup/AssertStorefrontSalesOrderMatchesGrandTotalActionGroup.xml +++ b/app/code/Magento/Multishipping/Test/Mftf/ActionGroup/AssertStorefrontSalesOrderMatchesGrandTotalActionGroup.xml @@ -7,7 +7,7 @@ --> <actionGroups xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" - xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/testSchema.xsd"> + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/actionGroupSchema.xsd"> <actionGroup name="AssertStorefrontSalesOrderMatchesGrandTotalActionGroup"> <arguments> <argument name="dataHref" type="string"/> diff --git a/app/code/Magento/Multishipping/Test/Mftf/ActionGroup/CheckingWithMinicartActionGroup.xml b/app/code/Magento/Multishipping/Test/Mftf/ActionGroup/CheckingWithMinicartActionGroup.xml index 35c42225d458b..973a3d759bb56 100644 --- a/app/code/Magento/Multishipping/Test/Mftf/ActionGroup/CheckingWithMinicartActionGroup.xml +++ b/app/code/Magento/Multishipping/Test/Mftf/ActionGroup/CheckingWithMinicartActionGroup.xml @@ -7,7 +7,7 @@ --> <actionGroups xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" - xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/testSchema.xsd"> + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/actionGroupSchema.xsd"> <actionGroup name="CheckingWithMinicartActionGroup"> <click selector="{{MinicartSection.clickOnCollapsibleDiv}}" stepKey="clickOnCollapsibleDiv"/> <click selector="{{MinicartSection.shippingMethodRadioButton}}" stepKey="clickOnShippingMethodRadioButton"/> diff --git a/app/code/Magento/Multishipping/Test/Mftf/ActionGroup/CheckingWithSingleAddressActionGroup.xml b/app/code/Magento/Multishipping/Test/Mftf/ActionGroup/CheckingWithSingleAddressActionGroup.xml index d911488925427..8d33c7fc4a36e 100644 --- a/app/code/Magento/Multishipping/Test/Mftf/ActionGroup/CheckingWithSingleAddressActionGroup.xml +++ b/app/code/Magento/Multishipping/Test/Mftf/ActionGroup/CheckingWithSingleAddressActionGroup.xml @@ -7,7 +7,7 @@ --> <actionGroups xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" - xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/testSchema.xsd"> + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/actionGroupSchema.xsd"> <actionGroup name="CheckingWithSingleAddressActionGroup"> <click selector="{{SingleShippingSection.checkoutWithMultipleAddresses}}" stepKey="clickOnCheckoutWithMultipleAddresses"/> <waitForPageLoad stepKey="waitForMultipleAddressPageLoad"/> diff --git a/app/code/Magento/Multishipping/Test/Mftf/ActionGroup/PlaceOrderActionGroup.xml b/app/code/Magento/Multishipping/Test/Mftf/ActionGroup/PlaceOrderActionGroup.xml index 8cfbe2665d685..a602eb6417205 100644 --- a/app/code/Magento/Multishipping/Test/Mftf/ActionGroup/PlaceOrderActionGroup.xml +++ b/app/code/Magento/Multishipping/Test/Mftf/ActionGroup/PlaceOrderActionGroup.xml @@ -7,7 +7,7 @@ --> <actionGroups xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" - xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/testSchema.xsd"> + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/actionGroupSchema.xsd"> <actionGroup name="PlaceOrderActionGroup"> <click selector="{{SingleShippingSection.placeOrder}}" stepKey="checkoutMultiShipmentPlaceOrder"/> <waitForPageLoad stepKey="waitForSuccessfullyPlacedOrder"/> diff --git a/app/code/Magento/Multishipping/Test/Mftf/ActionGroup/ReviewOrderActionGroup.xml b/app/code/Magento/Multishipping/Test/Mftf/ActionGroup/ReviewOrderActionGroup.xml index 8a63c42006443..98683f24af48e 100644 --- a/app/code/Magento/Multishipping/Test/Mftf/ActionGroup/ReviewOrderActionGroup.xml +++ b/app/code/Magento/Multishipping/Test/Mftf/ActionGroup/ReviewOrderActionGroup.xml @@ -7,7 +7,7 @@ --> <actionGroups xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" - xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/testSchema.xsd"> + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/actionGroupSchema.xsd"> <actionGroup name="ReviewOrderForSingleShipmentActionGroup"> <arguments> <argument name="totalName" type="string" defaultValue="Shipping & Handling"/> diff --git a/app/code/Magento/Multishipping/Test/Mftf/ActionGroup/SelectBillingInfoActionGroup.xml b/app/code/Magento/Multishipping/Test/Mftf/ActionGroup/SelectBillingInfoActionGroup.xml index 63fbaea72cc50..3a4fd49f1cb88 100644 --- a/app/code/Magento/Multishipping/Test/Mftf/ActionGroup/SelectBillingInfoActionGroup.xml +++ b/app/code/Magento/Multishipping/Test/Mftf/ActionGroup/SelectBillingInfoActionGroup.xml @@ -7,7 +7,7 @@ --> <actionGroups xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" - xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/testSchema.xsd"> + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/actionGroupSchema.xsd"> <actionGroup name="SelectBillingInfoActionGroup"> <waitForPageLoad stepKey="waitForBillingInfoPageLoad"/> <click selector="{{PaymentMethodSection.goToReviewOrder}}" stepKey="goToReviewOrder"/> diff --git a/app/code/Magento/Multishipping/Test/Mftf/ActionGroup/SelectSingleShippingInfoActionGroup.xml b/app/code/Magento/Multishipping/Test/Mftf/ActionGroup/SelectSingleShippingInfoActionGroup.xml index 329f1451788cd..1528b1d15654f 100644 --- a/app/code/Magento/Multishipping/Test/Mftf/ActionGroup/SelectSingleShippingInfoActionGroup.xml +++ b/app/code/Magento/Multishipping/Test/Mftf/ActionGroup/SelectSingleShippingInfoActionGroup.xml @@ -7,7 +7,7 @@ --> <actionGroups xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" - xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/testSchema.xsd"> + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/actionGroupSchema.xsd"> <actionGroup name="SelectSingleShippingInfoActionGroup"> <arguments> <argument name="shippingMethodType" type="string" defaultValue="Fixed"/> diff --git a/app/code/Magento/Multishipping/Test/Mftf/ActionGroup/StorefrontCheckoutShippingSelectMultipleAddressesActionGroup.xml b/app/code/Magento/Multishipping/Test/Mftf/ActionGroup/StorefrontCheckoutShippingSelectMultipleAddressesActionGroup.xml index b698f00078bd6..f3c736feecaf2 100644 --- a/app/code/Magento/Multishipping/Test/Mftf/ActionGroup/StorefrontCheckoutShippingSelectMultipleAddressesActionGroup.xml +++ b/app/code/Magento/Multishipping/Test/Mftf/ActionGroup/StorefrontCheckoutShippingSelectMultipleAddressesActionGroup.xml @@ -7,7 +7,7 @@ --> <actionGroups xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" - xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/testSchema.xsd"> + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/actionGroupSchema.xsd"> <actionGroup name="StorefrontCheckoutShippingSelectMultipleAddressesActionGroup"> <arguments> <argument name="firstAddress" type="string" defaultValue="{{CustomerAddressSimple.street[0]}}"/> diff --git a/app/code/Magento/Multishipping/Test/Mftf/Page/MultishippingCheckoutAddressesPage.xml b/app/code/Magento/Multishipping/Test/Mftf/Page/MultishippingCheckoutAddressesPage.xml new file mode 100644 index 0000000000000..08ff948b200a7 --- /dev/null +++ b/app/code/Magento/Multishipping/Test/Mftf/Page/MultishippingCheckoutAddressesPage.xml @@ -0,0 +1,14 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + /** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +--> + +<pages xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Page/etc/PageObject.xsd"> + <page name="MultishippingCheckoutAddressesPage" url="/multishipping/checkout/addresses" module="Magento_Multishipping" area="storefront"> + <section name="StorefrontMultishippingCheckoutAddressesToolbarSection"/> + </page> +</pages> diff --git a/app/code/Magento/Multishipping/Test/Mftf/Page/MultishippingCheckoutBillingPage.xml b/app/code/Magento/Multishipping/Test/Mftf/Page/MultishippingCheckoutBillingPage.xml new file mode 100644 index 0000000000000..6e1fbaed2d29a --- /dev/null +++ b/app/code/Magento/Multishipping/Test/Mftf/Page/MultishippingCheckoutBillingPage.xml @@ -0,0 +1,14 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + /** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +--> + +<pages xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Page/etc/PageObject.xsd"> + <page name="MultishippingCheckoutBillingPage" url="/multishipping/checkout/billing" module="Magento_Multishipping" area="storefront"> + <section name="StorefrontMultishippingCheckoutBillingToolbarSection"/> + </page> +</pages> diff --git a/app/code/Magento/Multishipping/Test/Mftf/Page/MultishippingCheckoutOverviewPage.xml b/app/code/Magento/Multishipping/Test/Mftf/Page/MultishippingCheckoutOverviewPage.xml new file mode 100644 index 0000000000000..2f56f70f96e02 --- /dev/null +++ b/app/code/Magento/Multishipping/Test/Mftf/Page/MultishippingCheckoutOverviewPage.xml @@ -0,0 +1,14 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + /** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +--> + +<pages xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Page/etc/PageObject.xsd"> + <page name="MultishippingCheckoutOverviewPage" url="/multishipping/checkout/overview" module="Magento_Multishipping" area="storefront"> + <section name="StorefrontMultishippingCheckoutOverviewReviewSection"/> + </page> +</pages> diff --git a/app/code/Magento/Multishipping/Test/Mftf/Page/MultishippingCheckoutShippingPage.xml b/app/code/Magento/Multishipping/Test/Mftf/Page/MultishippingCheckoutShippingPage.xml new file mode 100644 index 0000000000000..a3d54a9ed90a7 --- /dev/null +++ b/app/code/Magento/Multishipping/Test/Mftf/Page/MultishippingCheckoutShippingPage.xml @@ -0,0 +1,14 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + /** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +--> + +<pages xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Page/etc/PageObject.xsd"> + <page name="MultishippingCheckoutShippingPage" url="/multishipping/checkout/shipping" module="Magento_Multishipping" area="storefront"> + <section name="StorefrontMultishippingCheckoutShippingToolbarSection"/> + </page> +</pages> diff --git a/app/code/Magento/Multishipping/Test/Mftf/Section/MinicartSection.xml b/app/code/Magento/Multishipping/Test/Mftf/Section/MinicartSection.xml index 1a31911bd185e..1d7dca36c176f 100644 --- a/app/code/Magento/Multishipping/Test/Mftf/Section/MinicartSection.xml +++ b/app/code/Magento/Multishipping/Test/Mftf/Section/MinicartSection.xml @@ -7,7 +7,7 @@ --> <sections xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" - xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/testSchema.xsd"> + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Page/etc/SectionObject.xsd"> <section name="MinicartSection"> <element name="shippingMethodRadioButton" type="select" selector="//input[@class='radio'][position()=1]"/> <element name="shippingMethodRadioText" type="text" selector="//label[@class='label'][position()=1]//span//span"/> diff --git a/app/code/Magento/Multishipping/Test/Mftf/Section/MultishippingSection.xml b/app/code/Magento/Multishipping/Test/Mftf/Section/MultishippingSection.xml deleted file mode 100644 index cd408f5600e3d..0000000000000 --- a/app/code/Magento/Multishipping/Test/Mftf/Section/MultishippingSection.xml +++ /dev/null @@ -1,30 +0,0 @@ -<?xml version="1.0" encoding="UTF-8"?> -<!-- - /** - * Copyright © Magento, Inc. All rights reserved. - * See COPYING.txt for license details. - */ ---> - -<sections xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" - xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/testSchema.xsd"> - <section name="SingleShippingSection"> - <element name="checkoutWithMultipleAddresses" type="button" selector="//span[text()='Check Out with Multiple Addresses']"/> - <element name="updateAddress" type="button" selector="//button[@class='action update']"/> - <element name="goToShippingInfo" type="button" selector="//span[text()='Go to Shipping Information']"/> - <element name="placeOrder" type="button" selector="#review-button"/> - </section> - <section name="MultishippingSection"> - <element name="checkoutWithMultipleAddresses" type="button" selector="//span[text()='Check Out with Multiple Addresses']"/> - <element name="shippingMultipleCheckout" type="button" selector=".action.multicheckout"/> - <element name="shippingAddressSelector" type="select" selector="//tr[position()={{addressPosition}}]//td[@data-th='Send To']//select" parameterized="true"/> - <element name="shippingAddressOptions" type="select" selector="#multiship-addresses-table tbody tr:nth-of-type({{addressPosition}}) .col.address select option:nth-of-type({{optionIndex}})" parameterized="true"/> - <element name="selectShippingAddress" type="select" selector="(//table[@id='multiship-addresses-table'] //div[@class='field address'] //select)[{{sequenceNumber}}]" parameterized="true"/> - </section> - <section name="StorefrontMultipleShippingMethodSection"> - <element name="orderId" type="text" selector=".shipping-list:nth-child({{rowNum}}) .order-id" parameterized="true"/> - <element name="goToReviewYourOrderButton" type="button" selector="#payment-continue"/> - <element name="continueToBillingInformationButton" type="button" selector=".action.primary.continue"/> - <element name="successMessage" type="text" selector=".multicheckout.success"/> - </section> -</sections> diff --git a/app/code/Magento/Multishipping/Test/Mftf/Section/MultishippingSection/MultishippingSection.xml b/app/code/Magento/Multishipping/Test/Mftf/Section/MultishippingSection/MultishippingSection.xml new file mode 100644 index 0000000000000..fab513cc8d11a --- /dev/null +++ b/app/code/Magento/Multishipping/Test/Mftf/Section/MultishippingSection/MultishippingSection.xml @@ -0,0 +1,17 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + /** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +--> +<sections xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Page/etc/SectionObject.xsd"> + <section name="MultishippingSection"> + <element name="checkoutWithMultipleAddresses" type="button" selector="//span[text()='Check Out with Multiple Addresses']"/> + <element name="shippingMultipleCheckout" type="button" selector=".action.multicheckout"/> + <element name="shippingAddressSelector" type="select" selector="//tr[position()={{addressPosition}}]//td[@data-th='Send To']//select" parameterized="true"/> + <element name="shippingAddressOptions" type="select" selector="#multiship-addresses-table tbody tr:nth-of-type({{addressPosition}}) .col.address select option:nth-of-type({{optionIndex}})" parameterized="true"/> + <element name="selectShippingAddress" type="select" selector="(//table[@id='multiship-addresses-table'] //div[@class='field address'] //select)[{{sequenceNumber}}]" parameterized="true"/> + </section> +</sections> diff --git a/app/code/Magento/Multishipping/Test/Mftf/Section/MultishippingSection/SingleShippingSection.xml b/app/code/Magento/Multishipping/Test/Mftf/Section/MultishippingSection/SingleShippingSection.xml new file mode 100644 index 0000000000000..c0749176b1008 --- /dev/null +++ b/app/code/Magento/Multishipping/Test/Mftf/Section/MultishippingSection/SingleShippingSection.xml @@ -0,0 +1,16 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + /** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +--> +<sections xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Page/etc/SectionObject.xsd"> + <section name="SingleShippingSection"> + <element name="checkoutWithMultipleAddresses" type="button" selector="//span[text()='Check Out with Multiple Addresses']"/> + <element name="updateAddress" type="button" selector="//button[@class='action update']"/> + <element name="goToShippingInfo" type="button" selector="//span[text()='Go to Shipping Information']"/> + <element name="placeOrder" type="button" selector="#review-button"/> + </section> +</sections> diff --git a/app/code/Magento/Multishipping/Test/Mftf/Section/MultishippingSection/StorefrontMultipleShippingMethodSection.xml b/app/code/Magento/Multishipping/Test/Mftf/Section/MultishippingSection/StorefrontMultipleShippingMethodSection.xml new file mode 100644 index 0000000000000..52899327c4df3 --- /dev/null +++ b/app/code/Magento/Multishipping/Test/Mftf/Section/MultishippingSection/StorefrontMultipleShippingMethodSection.xml @@ -0,0 +1,16 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + /** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +--> +<sections xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Page/etc/SectionObject.xsd"> + <section name="StorefrontMultipleShippingMethodSection"> + <element name="orderId" type="text" selector=".shipping-list:nth-child({{rowNum}}) .order-id" parameterized="true"/> + <element name="goToReviewYourOrderButton" type="button" selector="#payment-continue"/> + <element name="continueToBillingInformationButton" type="button" selector=".action.primary.continue"/> + <element name="successMessage" type="text" selector=".multicheckout.success"/> + </section> +</sections> diff --git a/app/code/Magento/Multishipping/Test/Mftf/Section/PaymentMethodSection.xml b/app/code/Magento/Multishipping/Test/Mftf/Section/PaymentMethodSection.xml index 2d47b54d84b9c..fe69cba0059fd 100644 --- a/app/code/Magento/Multishipping/Test/Mftf/Section/PaymentMethodSection.xml +++ b/app/code/Magento/Multishipping/Test/Mftf/Section/PaymentMethodSection.xml @@ -7,7 +7,7 @@ --> <sections xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" - xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/testSchema.xsd"> + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Page/etc/SectionObject.xsd"> <section name="PaymentMethodSection"> <element name="goToReviewOrder" type="button" selector="#payment-continue"/> </section> diff --git a/app/code/Magento/Multishipping/Test/Mftf/Section/ReviewOrderSection.xml b/app/code/Magento/Multishipping/Test/Mftf/Section/ReviewOrderSection.xml index de33c28bfb1f2..2886f04e4806a 100644 --- a/app/code/Magento/Multishipping/Test/Mftf/Section/ReviewOrderSection.xml +++ b/app/code/Magento/Multishipping/Test/Mftf/Section/ReviewOrderSection.xml @@ -7,7 +7,7 @@ --> <sections xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" - xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/testSchema.xsd"> + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Page/etc/SectionObject.xsd"> <section name="ReviewOrderSection"> <element name="shippingMethodBasePrice" type="text" selector="//div[@class='block-content'][position()={{shippingMethodPosition}}]//div[@class='box box-shipping-method'][position()=1]//span[@class='price']" parameterized="true"/> <element name="shippingMethodSubtotalPrice" type="text" selector="//div[@class='block-content'][position()={{shippingMethodPosition}}]//td[@class='amount'][contains(@data-th,'{{priceType}}')]//span[@class='price']" parameterized="true"/> diff --git a/app/code/Magento/Multishipping/Test/Mftf/Section/ShippingMethodSection.xml b/app/code/Magento/Multishipping/Test/Mftf/Section/ShippingMethodSection.xml index c4dd2494f7fe8..ef41ed3f47f3a 100644 --- a/app/code/Magento/Multishipping/Test/Mftf/Section/ShippingMethodSection.xml +++ b/app/code/Magento/Multishipping/Test/Mftf/Section/ShippingMethodSection.xml @@ -7,7 +7,7 @@ --> <sections xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" - xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/testSchema.xsd"> + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Page/etc/SectionObject.xsd"> <section name="ShippingMethodSection"> <element name="shippingMethodRadioButton" type="select" selector="//input[@class='radio']"/> <element name="selectShippingMethod" type="radio" selector="//div[@class='block block-shipping'][position()={{shippingBlockPosition}}]//dd[position()={{shippingMethodPosition}}]//input[@class='radio']" parameterized="true" timeout="5"/> diff --git a/app/code/Magento/Multishipping/Test/Mftf/Section/StorefrontMultishippingCheckoutAddressesToolbarSection.xml b/app/code/Magento/Multishipping/Test/Mftf/Section/StorefrontMultishippingCheckoutAddressesToolbarSection.xml new file mode 100644 index 0000000000000..f238afe672156 --- /dev/null +++ b/app/code/Magento/Multishipping/Test/Mftf/Section/StorefrontMultishippingCheckoutAddressesToolbarSection.xml @@ -0,0 +1,14 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + /** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +--> + +<sections xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Page/etc/SectionObject.xsd"> + <section name="StorefrontMultishippingCheckoutAddressesToolbarSection"> + <element name="goToShippingInformation" type="button" selector="button.action.primary.continue"/> + </section> +</sections> diff --git a/app/code/Magento/Multishipping/Test/Mftf/Section/StorefrontMultishippingCheckoutBillingToolbarSection.xml b/app/code/Magento/Multishipping/Test/Mftf/Section/StorefrontMultishippingCheckoutBillingToolbarSection.xml new file mode 100644 index 0000000000000..6cfc09c1653fd --- /dev/null +++ b/app/code/Magento/Multishipping/Test/Mftf/Section/StorefrontMultishippingCheckoutBillingToolbarSection.xml @@ -0,0 +1,14 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + /** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +--> + +<sections xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Page/etc/SectionObject.xsd"> + <section name="StorefrontMultishippingCheckoutBillingToolbarSection"> + <element name="goToReviewOrder" type="button" selector="button.action.primary.continue"/> + </section> +</sections> diff --git a/app/code/Magento/Multishipping/Test/Mftf/Section/StorefrontMultishippingCheckoutOverviewReviewSection.xml b/app/code/Magento/Multishipping/Test/Mftf/Section/StorefrontMultishippingCheckoutOverviewReviewSection.xml new file mode 100644 index 0000000000000..3c4de2990f9a5 --- /dev/null +++ b/app/code/Magento/Multishipping/Test/Mftf/Section/StorefrontMultishippingCheckoutOverviewReviewSection.xml @@ -0,0 +1,14 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + /** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +--> + +<sections xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Page/etc/SectionObject.xsd"> + <section name="StorefrontMultishippingCheckoutOverviewReviewSection"> + <element name="placeOrder" type="button" selector="button.action.primary.submit"/> + </section> +</sections> diff --git a/app/code/Magento/Multishipping/Test/Mftf/Section/StorefrontMultishippingCheckoutShippingToolbarSection.xml b/app/code/Magento/Multishipping/Test/Mftf/Section/StorefrontMultishippingCheckoutShippingToolbarSection.xml new file mode 100644 index 0000000000000..deea09acf2f2a --- /dev/null +++ b/app/code/Magento/Multishipping/Test/Mftf/Section/StorefrontMultishippingCheckoutShippingToolbarSection.xml @@ -0,0 +1,14 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + /** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +--> + +<sections xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Page/etc/SectionObject.xsd"> + <section name="StorefrontMultishippingCheckoutShippingToolbarSection"> + <element name="continueToBilling" type="button" selector="button.action.primary.continue"/> + </section> +</sections> diff --git a/app/code/Magento/Multishipping/Test/Mftf/Section/StorefrontSalesOrderSection.xml b/app/code/Magento/Multishipping/Test/Mftf/Section/StorefrontSalesOrderSection.xml index 94546dcfef9a0..d38b1f239ef45 100644 --- a/app/code/Magento/Multishipping/Test/Mftf/Section/StorefrontSalesOrderSection.xml +++ b/app/code/Magento/Multishipping/Test/Mftf/Section/StorefrontSalesOrderSection.xml @@ -7,7 +7,7 @@ --> <sections xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" - xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/testSchema.xsd"> + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Page/etc/SectionObject.xsd"> <section name="StorefrontSalesOrderSection"> <element name="orderLinkByPosition" type="text" selector="//li[@class='shipping-list'][position()={{orderLinkPosition}}]//a" parameterized="true"/> <element name="viewOrderLink" type="text" selector="//td[@data-th='Actions']//a[contains(@href,'{{orderLink}}')]//span[text()='View Order']" parameterized="true" timeout="5"/> diff --git a/app/code/Magento/Multishipping/Test/Mftf/Test/StoreFrontCheckingWithMultishipmentTest.xml b/app/code/Magento/Multishipping/Test/Mftf/Test/StoreFrontCheckingWithMultishipmentTest.xml index cfec857329b3d..7ae23e8f871eb 100644 --- a/app/code/Magento/Multishipping/Test/Mftf/Test/StoreFrontCheckingWithMultishipmentTest.xml +++ b/app/code/Magento/Multishipping/Test/Mftf/Test/StoreFrontCheckingWithMultishipmentTest.xml @@ -14,7 +14,7 @@ <stories value="Checking multi shipment with multiple shipment adresses on front end order page"/> <title value="Checking multi shipment with multiple shipment adresses on front end order page"/> <description value="Shipping price shows 0 when you return from multiple checkout to cart"/> - <severity value="MAJOR"/> + <severity value="BLOCKER"/> <testCaseId value="MC-18519"/> <group value="Multishipment"/> </annotations> @@ -31,7 +31,7 @@ <createData entity="FreeShippinMethodConfig" stepKey="enableFreeShipping"/> <createData entity="FlatRateShippingMethodConfig" stepKey="enableFlatRateShipping"/> <magentoCLI command="config:set {{EnableCheckMoneyOrderPaymentMethod.path}} {{EnableCheckMoneyOrderPaymentMethod.value}}" stepKey="enableCheckMoneyOrderPaymentMethod"/> - <actionGroup ref="LoginAsAdmin" stepKey="loginAsAdmin"/> + <actionGroup ref="AdminLoginActionGroup" stepKey="loginAsAdmin"/> <actionGroup ref="LoginToStorefrontActionGroup" stepKey="loginToStorefrontAccount"> <argument name="Customer" value="$$customer$$"/> </actionGroup> @@ -58,7 +58,7 @@ <deleteData stepKey="deleteProduct2" createDataKey="product2"/> <deleteData stepKey="deleteCustomer" createDataKey="customer"/> <createData entity="FreeShippinMethodDefault" stepKey="disableFreeShipping"/> - <actionGroup ref="logout" stepKey="logout"/> + <actionGroup ref="AdminLogoutActionGroup" stepKey="logout"/> </after> </test> </tests> diff --git a/app/code/Magento/Multishipping/Test/Mftf/Test/StoreFrontCheckingWithSingleShipmentTest.xml b/app/code/Magento/Multishipping/Test/Mftf/Test/StoreFrontCheckingWithSingleShipmentTest.xml index dcf0770e5421e..27876df8caefe 100644 --- a/app/code/Magento/Multishipping/Test/Mftf/Test/StoreFrontCheckingWithSingleShipmentTest.xml +++ b/app/code/Magento/Multishipping/Test/Mftf/Test/StoreFrontCheckingWithSingleShipmentTest.xml @@ -31,7 +31,7 @@ <createData entity="FreeShippinMethodConfig" stepKey="enableFreeShipping"/> <createData entity="FlatRateShippingMethodConfig" stepKey="enableFlatRateShipping"/> <magentoCLI command="config:set {{EnableCheckMoneyOrderPaymentMethod.path}} {{EnableCheckMoneyOrderPaymentMethod.value}}" stepKey="enableCheckMoneyOrderPaymentMethod"/> - <actionGroup ref="LoginAsAdmin" stepKey="loginAsAdmin"/> + <actionGroup ref="AdminLoginActionGroup" stepKey="loginAsAdmin"/> <actionGroup ref="LoginToStorefrontActionGroup" stepKey="loginToStorefrontAccount"> <argument name="Customer" value="$$customer$$"/> </actionGroup> @@ -58,7 +58,7 @@ <deleteData stepKey="deleteProduct2" createDataKey="product2"/> <deleteData stepKey="deleteCustomer" createDataKey="customer"/> <createData entity="FreeShippinMethodDefault" stepKey="disableFreeShipping"/> - <actionGroup ref="logout" stepKey="logout"/> + <actionGroup ref="AdminLogoutActionGroup" stepKey="logout"/> </after> </test> </tests> diff --git a/app/code/Magento/Multishipping/Test/Mftf/Test/StoreFrontMinicartWithMultishipmentTest.xml b/app/code/Magento/Multishipping/Test/Mftf/Test/StoreFrontMinicartWithMultishipmentTest.xml index e826d8e03ffbc..f21a8d32d8841 100644 --- a/app/code/Magento/Multishipping/Test/Mftf/Test/StoreFrontMinicartWithMultishipmentTest.xml +++ b/app/code/Magento/Multishipping/Test/Mftf/Test/StoreFrontMinicartWithMultishipmentTest.xml @@ -31,7 +31,7 @@ <createData entity="FreeShippinMethodConfig" stepKey="enableFreeShipping"/> <createData entity="FlatRateShippingMethodConfig" stepKey="enableFlatRateShipping"/> <magentoCLI command="config:set {{EnableCheckMoneyOrderPaymentMethod.path}} {{EnableCheckMoneyOrderPaymentMethod.value}}" stepKey="enableCheckMoneyOrderPaymentMethod"/> - <actionGroup ref="LoginAsAdmin" stepKey="loginAsAdmin"/> + <actionGroup ref="AdminLoginActionGroup" stepKey="loginAsAdmin"/> <actionGroup ref="LoginToStorefrontActionGroup" stepKey="loginToStorefrontAccount"> <argument name="Customer" value="$$customer$$"/> </actionGroup> @@ -44,7 +44,7 @@ <deleteData stepKey="deleteProduct2" createDataKey="product2"/> <deleteData stepKey="deleteCustomer" createDataKey="customer"/> <createData entity="FreeShippinMethodDefault" stepKey="disableFreeShipping"/> - <actionGroup ref="logout" stepKey="logoutAdmin"/> + <actionGroup ref="AdminLogoutActionGroup" stepKey="logoutAdmin"/> </after> <amOnPage url="$$product1.name$$.html" stepKey="goToProduct1"/> diff --git a/app/code/Magento/Multishipping/Test/Mftf/Test/StoreFrontMyAccountWithMultishipmentTest.xml b/app/code/Magento/Multishipping/Test/Mftf/Test/StoreFrontMyAccountWithMultishipmentTest.xml deleted file mode 100644 index ad68b709c5729..0000000000000 --- a/app/code/Magento/Multishipping/Test/Mftf/Test/StoreFrontMyAccountWithMultishipmentTest.xml +++ /dev/null @@ -1,117 +0,0 @@ -<?xml version="1.0" encoding="UTF-8"?> -<!-- - /** - * Copyright © Magento, Inc. All rights reserved. - * See COPYING.txt for license details. - */ ---> - -<tests xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" - xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/testSchema.xsd"> - <test name="StorefrontMyAccountWithMultishipmentTest"> - <annotations> - <features value="Multishipping"/> - <stories value="Shipping price shows 0 on Order view page after multiple address checkout"/> - <title value="Verify Shipping price for Storefront after multiple address checkout"/> - <description value="Verify that shipping price on My account matches with shipping method prices after multiple addresses checkout (Order view page)"/> - <severity value="CRITICAL"/> - <testCaseId value="MC-19303"/> - <group value="multishipping"/> - <skip> - <issueId value="MC-22683"/> - </skip> - </annotations> - - <before> - <createData entity="SimpleSubCategory" stepKey="category"/> - <createData entity="SimpleProduct" stepKey="product1"> - <requiredEntity createDataKey="category"/> - </createData> - <createData entity="SimpleProduct" stepKey="product2"> - <requiredEntity createDataKey="category"/> - </createData> - <createData entity="Simple_US_Customer_Two_Addresses" stepKey="customer"/> - <!-- Set configurations --> - <magentoCLI command="config:set {{EnableMultiShippingCheckoutMultiple.path}} {{EnableMultiShippingCheckoutMultiple.value}}" stepKey="allowShippingToMultipleAddresses"/> - <magentoCLI command="config:set {{EnableFreeShippingMethod.path}} {{EnableFreeShippingMethod.value}}" stepKey="enableFreeShipping"/> - <magentoCLI command="config:set {{EnableFlatRateShippingMethod.path}} {{EnableFlatRateShippingMethod.value}}" stepKey="enableFlatRateShipping"/> - <magentoCLI command="config:set {{EnableCheckMoneyOrderPaymentMethod.path}} {{EnableCheckMoneyOrderPaymentMethod.value}}" stepKey="enableCheckMoneyOrderPaymentMethod"/> - <actionGroup ref="LoginAsAdmin" stepKey="loginAsAdmin"/> - <actionGroup ref="LoginToStorefrontActionGroup" stepKey="loginToStorefrontAccount"> - <argument name="Customer" value="$$customer$$"/> - </actionGroup> - </before> - <after> - <actionGroup ref="StorefrontSignOutActionGroup" stepKey="customerLogout"/> - <magentoCLI command="config:set {{DisableMultiShippingCheckoutMultiple.path}} {{DisableMultiShippingCheckoutMultiple.value}}" stepKey="withdrawShippingToMultipleAddresses"/> - <deleteData createDataKey="category" stepKey="deleteCategory"/> - <deleteData createDataKey="product1" stepKey="deleteProduct1"/> - <deleteData createDataKey="product2" stepKey="deleteProduct2"/> - <deleteData createDataKey="customer" stepKey="deleteCustomer"/> - <magentoCLI command="config:set {{DisableFreeShippingMethod.path}} {{DisableFreeShippingMethod.value}}" stepKey="disableFreeShipping"/> - <actionGroup ref="AdminOrdersGridClearFiltersActionGroup" stepKey="clearAllFilters"/> - <actionGroup ref="logout" stepKey="logoutAdmin"/> - </after> - <actionGroup ref="AddSimpleProductToCartActionGroup" stepKey="addSimpleProduct1ToCart"> - <argument name="product" value="$$product1$$"/> - </actionGroup> - <waitForPageLoad stepKey="waitForSecondProductPageLoad"/> - <actionGroup ref="AddSimpleProductToCartActionGroup" stepKey="addSimpleProduct2ToCart"> - <argument name="product" value="$$product2$$"/> - </actionGroup> - <actionGroup ref="StorefrontOpenCartFromMinicartActionGroup" stepKey="openCart"/> - <actionGroup ref="CheckingWithMultipleAddressesActionGroup" stepKey="checkoutWithMultipleAddresses"/> - <waitForPageLoad stepKey="waitForShippingInfoPageLoad"/> - <actionGroup ref="SelectMultiShippingInfoActionGroup" stepKey="checkoutWithMultipleShipping"/> - <!--Select Check / Money order Payment method--> - <actionGroup ref="CheckoutSelectCheckMoneyOrderPaymentActionGroup" stepKey="selectCheckMoneyPayment"/> - <actionGroup ref="SelectBillingInfoActionGroup" stepKey="checkoutWithPaymentMethod"/> - <waitForPageLoad stepKey="waitForReviewOrderPageLoad"/> - <actionGroup ref="ReviewOrderForMultiShipmentActionGroup" stepKey="reviewOrderForMultiShipment"> - <argument name="totalNameForFirstOrder" value="Shipping & Handling"/> - <argument name="totalPositionForFirstOrder" value="1"/> - <argument name="totalNameForSecondOrder" value="Shipping & Handling"/> - <argument name="totalPositionForSecondOrder" value="2"/> - </actionGroup> - <waitForPageLoad stepKey="waitForPlaceOrderPageLoad"/> - <actionGroup ref="StorefrontPlaceOrderForMultipleAddressesActionGroup" stepKey="placeOrder"> - <argument name="firstOrderPosition" value="1"/> - <argument name="secondOrderPosition" value="2"/> - </actionGroup> - <waitForPageLoad stepKey="waitForOrderPageLoad"/> - <amOnPage url="{{StorefrontCustomerOrdersHistoryPage.url}}" stepKey="goToSalesOrder"/> - <actionGroup ref="AssertStorefrontSalesOrderMatchesGrandTotalActionGroup" stepKey="checkSalesOrderForFirstOrder"> - <argument name="dataHref" value="$dataHrefForFirstOrderPlaceOrder"/> - </actionGroup> - <amOnPage url="{{StorefrontCustomerOrdersHistoryPage.url}}" stepKey="goToSalesOrder2"/> - <waitForPageLoad stepKey="waitForOrderPageLoad2"/> - <actionGroup ref="AssertStorefrontSalesOrderMatchesGrandTotalActionGroup" stepKey="checkSalesOrderForSecondOrder"> - <argument name="dataHref" value="$dataHrefForSecondOrderPlaceOrder"/> - </actionGroup> - <waitForPageLoad stepKey="waitForAdminPageToLoad"/> - <!-- Go to Stores > Configuration > Sales > Orders --> - <amOnPage url="{{AdminOrdersPage.url}}" stepKey="onAdminOrdersPage"/> - <waitForPageLoad stepKey="waitForOrderPageLoad3"/> - <actionGroup ref="AdminOrdersGridClearFiltersActionGroup" stepKey="clearFilters"/> - <waitForLoadingMaskToDisappear stepKey="waitForLoading"/> - <!--Assert order in orders grid --> - <!-- Go to order page --> - <actionGroup ref="OpenOrderByIdActionGroup" stepKey="openFirstOrderPage"> - <argument name="orderId" value="{$getFirstOrderIdPlaceOrder}"/> - </actionGroup> - <!-- Check status --> - <see selector="{{AdminOrderDetailsInformationSection.orderStatus}}" userInput="Pending" stepKey="seeFirstOrderPendingStatus"/> - <actionGroup ref="AdminSalesOrderActionGroup" stepKey="validateOrderTotalsForFirstOrder"/> - <amOnPage url="{{AdminOrdersPage.url}}" stepKey="onAdminOrdersPage2"/> - <waitForPageLoad stepKey="waitForOrderPageLoad4"/> - <actionGroup ref="AdminOrdersGridClearFiltersActionGroup" stepKey="clearFilters2"/> - <!-- Go to order page --> - <actionGroup ref="OpenOrderByIdActionGroup" stepKey="openSecondOrderPage"> - <argument name="orderId" value="{$getSecondOrderIdPlaceOrder}"/> - </actionGroup> - <!-- Check status --> - <see selector="{{AdminOrderDetailsInformationSection.orderStatus}}" userInput="Pending" stepKey="seeSecondOrderPendingStatus"/> - <actionGroup ref="AdminSalesOrderActionGroup" stepKey="validateOrderTotalsForSecondOrder"/> - <amOnPage url="{{StorefrontHomePage.url}}" stepKey="gotToHomePage"/> - </test> -</tests> diff --git a/app/code/Magento/Multishipping/Test/Mftf/Test/StorefrontCheckoutWithMultipleAddressesTest.xml b/app/code/Magento/Multishipping/Test/Mftf/Test/StorefrontCheckoutWithMultipleAddressesTest.xml index dc786f9cbc5db..a49a37e475409 100644 --- a/app/code/Magento/Multishipping/Test/Mftf/Test/StorefrontCheckoutWithMultipleAddressesTest.xml +++ b/app/code/Magento/Multishipping/Test/Mftf/Test/StorefrontCheckoutWithMultipleAddressesTest.xml @@ -21,7 +21,7 @@ </annotations> <before> <!-- Login as Admin --> - <actionGroup ref="LoginAsAdmin" stepKey="login"/> + <actionGroup ref="AdminLoginActionGroup" stepKey="login"/> <!-- Set configurations --> <magentoCLI command="config:set multishipping/options/checkout_multiple 1" stepKey="allowShippingToMultipleAddresses"/> <!-- Create simple products --> @@ -36,7 +36,7 @@ </before> <after> <!-- Delete created data --> - <actionGroup ref="logout" stepKey="logout"/> + <actionGroup ref="AdminLogoutActionGroup" stepKey="logout"/> <deleteData createDataKey="firstProduct" stepKey="deleteFirstProduct"/> <deleteData createDataKey="secondProduct" stepKey="deleteSecondProduct"/> <deleteData createDataKey="createCategory" stepKey="deleteCategory"/> diff --git a/app/code/Magento/Multishipping/Test/Mftf/Test/StorefrontOrderWithMultishippingTest.xml b/app/code/Magento/Multishipping/Test/Mftf/Test/StorefrontOrderWithMultishippingTest.xml new file mode 100644 index 0000000000000..80407a219a841 --- /dev/null +++ b/app/code/Magento/Multishipping/Test/Mftf/Test/StorefrontOrderWithMultishippingTest.xml @@ -0,0 +1,106 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + /** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +--> + +<tests xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/testSchema.xsd"> + <test name="StorefrontOrderWithMultishippingTest"> + <annotations> + <features value="Multishipping"/> + <stories value="Shipping price shows 0 on Order view page after multiple address checkout"/> + <title value="Verify Shipping price for Storefront after multiple address checkout"/> + <description value="Verify that shipping price on My account matches with shipping method prices after multiple addresses checkout (Order view page)"/> + <severity value="CRITICAL"/> + <testCaseId value="MC-28900"/> + <useCaseId value="MC-19303"/> + <group value="catalog"/> + <group value="sales"/> + <group value="multishipping"/> + </annotations> + + <before> + <createData entity="SimpleProduct2" stepKey="createProduct1"/> + <createData entity="SimpleProduct2" stepKey="createProduct2"/> + <createData entity="Simple_US_Customer_Two_Addresses" stepKey="createCustomer"/> + <!-- Set configurations --> + <magentoCLI command="config:set {{EnableMultiShippingCheckoutMultiple.path}} {{EnableMultiShippingCheckoutMultiple.value}}" stepKey="allowShippingToMultipleAddresses"/> + <magentoCLI command="config:set {{EnableFreeShippingMethod.path}} {{EnableFreeShippingMethod.value}}" stepKey="enableFreeShipping"/> + <magentoCLI command="config:set {{EnableFlatRateShippingMethod.path}} {{EnableFlatRateShippingMethod.value}}" stepKey="enableFlatRateShipping"/> + <magentoCLI command="config:set {{EnableCheckMoneyOrderPaymentMethod.path}} {{EnableCheckMoneyOrderPaymentMethod.value}}" stepKey="enableCheckMoneyOrderPaymentMethod"/> + <actionGroup ref="AdminLoginActionGroup" stepKey="loginAsAdmin"/> + <actionGroup ref="LoginToStorefrontActionGroup" stepKey="loginToStorefrontAccount"> + <argument name="Customer" value="$$createCustomer$$"/> + </actionGroup> + </before> + + <after> + <deleteData createDataKey="createProduct1" stepKey="deleteProduct1"/> + <deleteData createDataKey="createProduct2" stepKey="deleteProduct2"/> + <!-- Need logout before customer delete. Fatal error appears otherwise --> + <actionGroup ref="StorefrontCustomerLogoutActionGroup" stepKey="customerLogout"/> + <deleteData createDataKey="createCustomer" stepKey="deleteCustomer"/> + <magentoCLI command="config:set {{DisableMultiShippingCheckoutMultiple.path}} {{DisableMultiShippingCheckoutMultiple.value}}" stepKey="withdrawShippingToMultipleAddresses"/> + <magentoCLI command="config:set {{DisableFreeShippingMethod.path}} {{DisableFreeShippingMethod.value}}" stepKey="disableFreeShipping"/> + <actionGroup ref="AdminOrdersGridClearFiltersActionGroup" stepKey="clearAllOrdersGridFilters"/> + <actionGroup ref="AdminLogoutActionGroup" stepKey="logoutFromAdmin"/> + </after> + + <actionGroup ref="AddSimpleProductToCartActionGroup" stepKey="addSimpleProduct1ToCart"> + <argument name="product" value="$$createProduct1$$"/> + </actionGroup> + <actionGroup ref="AddSimpleProductToCartActionGroup" stepKey="addSimpleProduct2ToCart"> + <argument name="product" value="$$createProduct2$$"/> + </actionGroup> + <actionGroup ref="StorefrontOpenCartFromMinicartActionGroup" stepKey="openCart"/> + <actionGroup ref="CheckingWithMultipleAddressesActionGroup" stepKey="checkoutWithMultipleAddresses"/> + <waitForPageLoad stepKey="waitForShippingInfoPageLoad"/> + <actionGroup ref="SelectMultiShippingInfoActionGroup" stepKey="checkoutWithMultipleShipping"/> + <!--Select Check / Money order Payment method--> + <actionGroup ref="CheckoutSelectCheckMoneyOrderPaymentActionGroup" stepKey="selectCheckMoneyPayment"/> + <actionGroup ref="SelectBillingInfoActionGroup" stepKey="checkoutWithPaymentMethod"/> + <waitForPageLoad stepKey="waitForReviewOrderPageLoad"/> + <actionGroup ref="ReviewOrderForMultiShipmentActionGroup" stepKey="reviewOrderForMultiShipment"> + <argument name="totalNameForFirstOrder" value="Shipping & Handling"/> + <argument name="totalPositionForFirstOrder" value="1"/> + <argument name="totalNameForSecondOrder" value="Shipping & Handling"/> + <argument name="totalPositionForSecondOrder" value="2"/> + </actionGroup> + <waitForPageLoad stepKey="waitForPlaceOrderPageLoad"/> + <actionGroup ref="StorefrontPlaceOrderForMultipleAddressesActionGroup" stepKey="placeOrder"> + <argument name="firstOrderPosition" value="1"/> + <argument name="secondOrderPosition" value="2"/> + </actionGroup> + <waitForPageLoad stepKey="waitForOrderPageLoad"/> + + <!-- Check first order--> + <actionGroup ref="StorefrontCustomerOrdersViewOrderActionGroup" stepKey="openFirstOrder"> + <argument name="orderNumber" value="{$getFirstOrderIdPlaceOrder}"/> + </actionGroup> + <actionGroup ref="AssertStorefrontCustomerOrderMatchesGrandTotalActionGroup" stepKey="checkFirstOrderTotals"/> + <!-- Check second order--> + <actionGroup ref="StorefrontCustomerOrdersViewOrderActionGroup" stepKey="openSecondOrder"> + <argument name="orderNumber" value="{$getSecondOrderIdPlaceOrder}"/> + </actionGroup> + <actionGroup ref="AssertStorefrontCustomerOrderMatchesGrandTotalActionGroup" stepKey="checkSecondOrderTotals"/> + + <!--Assert order in orders grid --> + <!-- Go to order page --> + <actionGroup ref="OpenOrderByIdActionGroup" stepKey="openFirstOrderPage"> + <argument name="orderId" value="{$getFirstOrderIdPlaceOrder}"/> + </actionGroup> + <!-- Check status --> + <actionGroup ref="AdminOrderViewCheckStatusActionGroup" stepKey="seeFirstOrderPendingStatus"/> + <actionGroup ref="AdminSalesOrderActionGroup" stepKey="validateOrderTotalsForFirstOrder"/> + <!-- Go to order page --> + <actionGroup ref="OpenOrderByIdActionGroup" stepKey="openSecondOrderPage"> + <argument name="orderId" value="{$getSecondOrderIdPlaceOrder}"/> + </actionGroup> + <!-- Check status --> + <actionGroup ref="AdminOrderViewCheckStatusActionGroup" stepKey="seeSecondOrderPendingStatus"/> + <actionGroup ref="AdminSalesOrderActionGroup" stepKey="validateOrderTotalsForSecondOrder"/> + </test> +</tests> diff --git a/app/code/Magento/Multishipping/Test/Mftf/Test/StorefrontProcessMultishippingCheckoutWhenCartPageIsOpenedInAnotherTabTest.xml b/app/code/Magento/Multishipping/Test/Mftf/Test/StorefrontProcessMultishippingCheckoutWhenCartPageIsOpenedInAnotherTabTest.xml index f25ac203c3fa5..2925f2c5b1690 100644 --- a/app/code/Magento/Multishipping/Test/Mftf/Test/StorefrontProcessMultishippingCheckoutWhenCartPageIsOpenedInAnotherTabTest.xml +++ b/app/code/Magento/Multishipping/Test/Mftf/Test/StorefrontProcessMultishippingCheckoutWhenCartPageIsOpenedInAnotherTabTest.xml @@ -14,14 +14,14 @@ <stories value="Multishipping"/> <title value="Process multishipping checkout when Cart page is opened in another tab"/> <description value="Process multishipping checkout when Cart page is opened in another tab"/> - <severity value="MAJOR"/> + <severity value="BLOCKER"/> <testCaseId value="MC-17871"/> <useCaseId value="MC-17469"/> <group value="multishipping"/> </annotations> <before> <!-- Login as Admin --> - <actionGroup ref="LoginAsAdmin" stepKey="login"/> + <actionGroup ref="AdminLoginActionGroup" stepKey="login"/> <!-- Set configurations --> <magentoCLI command="config:set multishipping/options/checkout_multiple 1" stepKey="allowShippingToMultipleAddresses"/> <!-- Create two simple products --> @@ -36,7 +36,7 @@ </before> <after> <!-- Delete created data --> - <actionGroup ref="logout" stepKey="logout"/> + <actionGroup ref="AdminLogoutActionGroup" stepKey="logout"/> <deleteData createDataKey="createFirstProduct" stepKey="deleteFirstProduct"/> <deleteData createDataKey="createSecondProduct" stepKey="deleteSecondProduct"/> <deleteData createDataKey="createCategory" stepKey="deleteCategory"/> diff --git a/app/code/Magento/Multishipping/Test/Mftf/Test/StorefrontVerifySecureURLRedirectMultishippingTest.xml b/app/code/Magento/Multishipping/Test/Mftf/Test/StorefrontVerifySecureURLRedirectMultishippingTest.xml index 085a710f2671c..e65747f4d63d0 100644 --- a/app/code/Magento/Multishipping/Test/Mftf/Test/StorefrontVerifySecureURLRedirectMultishippingTest.xml +++ b/app/code/Magento/Multishipping/Test/Mftf/Test/StorefrontVerifySecureURLRedirectMultishippingTest.xml @@ -8,7 +8,7 @@ <tests xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/testSchema.xsd"> - <test name="StorefrontVerifySecureURLRedirectMultishipping"> + <test name="StorefrontVerifySecureURLRedirectMultishippingTest"> <!--todo MC-5858: some urls don't redirect to https--> <annotations> <features value="Multishipping"/> diff --git a/app/code/Magento/Multishipping/Test/Unit/Model/Cart/CartTotalRepositoryPluginTest.php b/app/code/Magento/Multishipping/Test/Unit/Model/Cart/CartTotalRepositoryPluginTest.php index 73b0b9ef3ca7a..8362699efbd45 100644 --- a/app/code/Magento/Multishipping/Test/Unit/Model/Cart/CartTotalRepositoryPluginTest.php +++ b/app/code/Magento/Multishipping/Test/Unit/Model/Cart/CartTotalRepositoryPluginTest.php @@ -6,76 +6,145 @@ namespace Magento\Multishipping\Test\Unit\Model\Cart; +use Magento\Framework\TestFramework\Unit\Helper\ObjectManager; +use Magento\Quote\Api\CartRepositoryInterface; +use Magento\Quote\Model\Cart\CartTotalRepository; +use Magento\Quote\Model\Cart\Totals as QuoteTotals; +use Magento\Quote\Model\Quote\Address as QuoteAddress; +use Magento\Quote\Model\Quote\Address\Rate as QuoteAddressRate; +use Magento\Multishipping\Model\Cart\CartTotalRepositoryPlugin; +use Magento\Store\Model\Store; +use PHPUnit\Framework\MockObject\MockObject; + class CartTotalRepositoryPluginTest extends \PHPUnit\Framework\TestCase { /** - * @var \Magento\Multishipping\Model\Cart\CartTotalRepositoryPlugin + * Stub cart id + */ + private const STUB_CART_ID = 10; + + /** + * Stub shipping method + */ + private const STUB_SHIPPING_METHOD = 'flatrate_flatrate'; + + /** + * Stub shipping price + */ + private const STUB_SHIPPING_PRICE = '10.00'; + + /** + * @var CartTotalRepositoryPlugin */ private $modelRepository; /** - * @var \PHPUnit_Framework_MockObject_MockObject + * @var CartTotalRepository|MockObject + */ + private $quoteTotalRepositoryMock; + + /** + * @var CartRepositoryInterface|MockObject */ private $quoteRepositoryMock; - protected function setUp() - { - $this->quoteRepositoryMock = $this->createMock(\Magento\Quote\Api\CartRepositoryInterface::class); - $this->modelRepository = new \Magento\Multishipping\Model\Cart\CartTotalRepositoryPlugin( - $this->quoteRepositoryMock - ); - } + /** + * @var QuoteTotals|MockObject + */ + private $quoteTotalsMock; /** - * Test quotTotal from cartRepository after get($cartId) function is called + * @var QuoteAddress|MockObject */ - public function testAfterGet() + private $shippingAddressMock; + + /** + * @var QuoteAddressRate|MockObject + */ + private $shippingRateMock; + + /** + * @var Store|MockObject + */ + private $storeMock; + + protected function setUp() { - $cartId = "10"; - $shippingMethod = 'flatrate_flatrate'; - $shippingPrice = '10.00'; - $quoteMock = $this->createPartialMock( - \Magento\Quote\Model\Cart\Totals::class, + $objectManager = new ObjectManager($this); + $this->quoteTotalsMock = $this->createPartialMock( + QuoteTotals::class, [ - 'getStore', - 'getShippingAddress', - 'getIsMultiShipping' + 'getStore', + 'getShippingAddress', + 'getIsMultiShipping' ] ); - $this->quoteRepositoryMock->expects($this->once())->method('getActive')->with($cartId)->willReturn($quoteMock); - $quoteMock->expects($this->once())->method('getIsMultiShipping')->willReturn(true); - $shippingAddressMock = $this->createPartialMock( - \Magento\Quote\Model\Quote\Address::class, + $this->shippingAddressMock = $this->createPartialMock( + QuoteAddress::class, [ - 'getShippingMethod', - 'getShippingRateByCode', - 'getShippingAmount' + 'getShippingMethod', + 'getShippingRateByCode', + 'getShippingAmount' ] ); - $quoteMock->expects($this->any())->method('getShippingAddress')->willReturn($shippingAddressMock); - - $shippingAddressMock->expects($this->once())->method('getShippingMethod')->willReturn($shippingMethod); - $shippingAddressMock->expects($this->any())->method('getShippingAmount')->willReturn($shippingPrice); - $shippingRateMock = $this->createPartialMock( - \Magento\Quote\Model\Quote\Address\Rate::class, + $this->shippingRateMock = $this->createPartialMock( + QuoteAddressRate::class, [ - 'getPrice' + 'getPrice' ] ); - $shippingAddressMock->expects($this->once())->method('getShippingRateByCode')->willReturn($shippingRateMock); - - $shippingRateMock->expects($this->once())->method('getPrice')->willReturn($shippingPrice); - - $storeMock = $this->getMockBuilder(\Magento\Store\Model\Store::class) + $this->storeMock = $this->getMockBuilder(Store::class) ->disableOriginalConstructor() ->getMock(); - $quoteMock->expects($this->any())->method('getStore')->willReturn($storeMock); - $storeMock->expects($this->any())->method('getBaseCurrency')->willReturnSelf(); + $this->quoteRepositoryMock = $this->createMock(CartRepositoryInterface::class); + $this->quoteTotalRepositoryMock = $this->createMock(CartTotalRepository::class); + $this->modelRepository = $objectManager->getObject(CartTotalRepositoryPlugin::class, [ + 'quoteRepository' => $this->quoteRepositoryMock + ]); + } + + /** + * Test quoteTotal from cartRepository after get($cartId) function is called + */ + public function testAfterGetQuoteTotalAddedShippingPrice() + { + $this->quoteRepositoryMock->expects($this->once()) + ->method('getActive') + ->with(self::STUB_CART_ID) + ->willReturn($this->quoteTotalsMock); + $this->quoteTotalsMock->expects($this->once()) + ->method('getIsMultiShipping') + ->willReturn(true); + $this->quoteTotalsMock->expects($this->any()) + ->method('getShippingAddress') + ->willReturn($this->shippingAddressMock); + + $this->shippingAddressMock->expects($this->once()) + ->method('getShippingMethod') + ->willReturn(self::STUB_SHIPPING_METHOD); + $this->shippingAddressMock->expects($this->any()) + ->method('getShippingAmount') + ->willReturn(self::STUB_SHIPPING_PRICE); + + $this->shippingAddressMock->expects($this->once()) + ->method('getShippingRateByCode') + ->willReturn($this->shippingRateMock); + + $this->shippingRateMock->expects($this->once()) + ->method('getPrice') + ->willReturn(self::STUB_SHIPPING_PRICE); + + $this->quoteTotalsMock->expects($this->any()) + ->method('getStore') + ->willReturn($this->storeMock); + $this->storeMock->expects($this->any()) + ->method('getBaseCurrency') + ->willReturnSelf(); $this->modelRepository->afterGet( - $this->createMock(\Magento\Quote\Model\Cart\CartTotalRepository::class), - $quoteMock, - $cartId + $this->quoteTotalRepositoryMock, + $this->quoteTotalsMock, + self::STUB_CART_ID ); } } diff --git a/app/code/Magento/Multishipping/Test/Unit/Model/Checkout/Type/MultishippingTest.php b/app/code/Magento/Multishipping/Test/Unit/Model/Checkout/Type/MultishippingTest.php index fba3245bec68d..747d88353f022 100644 --- a/app/code/Magento/Multishipping/Test/Unit/Model/Checkout/Type/MultishippingTest.php +++ b/app/code/Magento/Multishipping/Test/Unit/Model/Checkout/Type/MultishippingTest.php @@ -3,34 +3,40 @@ * Copyright © Magento, Inc. All rights reserved. * See COPYING.txt for license details. */ +declare(strict_types=1); namespace Magento\Multishipping\Test\Unit\Model\Checkout\Type; +use Magento\Catalog\Model\Product\Type; +use Magento\Catalog\Model\Product\Type\Simple; use Magento\Checkout\Model\Session; use Magento\Customer\Api\AddressRepositoryInterface; use Magento\Customer\Api\Data\AddressInterface; use Magento\Customer\Api\Data\AddressSearchResultsInterface; use Magento\Customer\Api\Data\CustomerInterface; -use Magento\Directory\Model\Currency; -use Magento\Multishipping\Model\Checkout\Type\Multishipping\PlaceOrderDefault; -use Magento\Multishipping\Model\Checkout\Type\Multishipping\PlaceOrderFactory; -use Magento\Quote\Model\Quote\Address; use Magento\Customer\Model\Session as CustomerSession; +use Magento\Directory\Model\AllowedCountries; +use Magento\Directory\Model\Currency; use Magento\Framework\Api\FilterBuilder; use Magento\Framework\Api\SearchCriteria; use Magento\Framework\Api\SearchCriteriaBuilder; use Magento\Framework\App\Config\ScopeConfigInterface; use Magento\Framework\Event\ManagerInterface; +use Magento\Framework\Exception\LocalizedException; use Magento\Framework\Pricing\PriceCurrencyInterface; use Magento\Framework\Session\Generic; use Magento\Framework\TestFramework\Unit\Helper\ObjectManager; use Magento\Multishipping\Helper\Data; use Magento\Multishipping\Model\Checkout\Type\Multishipping; +use Magento\Multishipping\Model\Checkout\Type\Multishipping\PlaceOrderDefault; +use Magento\Multishipping\Model\Checkout\Type\Multishipping\PlaceOrderFactory; +use Magento\Payment\Model\Method\AbstractMethod; use Magento\Payment\Model\Method\SpecificationInterface; use Magento\Quote\Api\CartRepositoryInterface; use Magento\Quote\Api\Data\CartExtension; use Magento\Quote\Api\Data\CartExtensionFactory; use Magento\Quote\Model\Quote; +use Magento\Quote\Model\Quote\Address; use Magento\Quote\Model\Quote\Address as QuoteAddress; use Magento\Quote\Model\Quote\Address\Item as AddressItem; use Magento\Quote\Model\Quote\Address\ToOrder; @@ -38,22 +44,24 @@ use Magento\Quote\Model\Quote\AddressFactory; use Magento\Quote\Model\Quote\Item; use Magento\Quote\Model\Quote\Item\ToOrderItem; +use Magento\Quote\Model\Quote\Payment; use Magento\Quote\Model\Quote\Payment\ToOrderPayment; use Magento\Quote\Model\Quote\ShippingAssignment\ShippingAssignmentProcessor; use Magento\Quote\Model\Quote\TotalsCollector; use Magento\Quote\Model\Shipping; use Magento\Quote\Model\ShippingAssignment; +use Magento\Sales\Api\Data\OrderAddressInterface; +use Magento\Sales\Api\Data\OrderInterface; +use Magento\Sales\Api\Data\OrderPaymentInterface; use Magento\Sales\Model\Order\Email\Sender\OrderSender; use Magento\Sales\Model\OrderFactory; use Magento\Store\Model\Store; use Magento\Store\Model\StoreManagerInterface; -use PHPUnit_Framework_MockObject_MockObject; -use \PHPUnit\Framework\TestCase; -use Magento\Quote\Model\Quote\Payment; -use Magento\Payment\Model\Method\AbstractMethod; -use Magento\Directory\Model\AllowedCountries; +use PHPUnit_Framework_MockObject_MockObject as MockObject; /** + * Test class Multishipping + * * @SuppressWarnings(PHPMD.CouplingBetweenObjects) * @SuppressWarnings(PHPMD.TooManyFields) */ @@ -65,115 +73,118 @@ class MultishippingTest extends \PHPUnit\Framework\TestCase protected $model; /** - * @var PHPUnit_Framework_MockObject_MockObject + * @var MockObject */ protected $checkoutSessionMock; /** - * @var PHPUnit_Framework_MockObject_MockObject + * @var MockObject */ protected $customerSessionMock; /** - * @var PHPUnit_Framework_MockObject_MockObject + * @var MockObject */ protected $customerMock; /** - * @var PHPUnit_Framework_MockObject_MockObject + * @var MockObject */ protected $quoteMock; /** - * @var PHPUnit_Framework_MockObject_MockObject + * @var MockObject */ protected $helperMock; /** - * @var PHPUnit_Framework_MockObject_MockObject + * @var MockObject */ protected $filterBuilderMock; /** - * @var PHPUnit_Framework_MockObject_MockObject + * @var MockObject */ protected $addressRepositoryMock; /** - * @var PHPUnit_Framework_MockObject_MockObject + * @var MockObject */ protected $searchCriteriaBuilderMock; /** - * @var PHPUnit_Framework_MockObject_MockObject + * @var MockObject */ protected $totalsCollectorMock; /** - * @var PHPUnit_Framework_MockObject_MockObject + * @var MockObject */ private $cartExtensionFactoryMock; /** - * @var PHPUnit_Framework_MockObject_MockObject + * @var MockObject */ private $shippingAssignmentProcessorMock; /** - * @var PHPUnit_Framework_MockObject_MockObject + * @var MockObject */ private $quoteRepositoryMock; /** - * @var OrderFactory|PHPUnit_Framework_MockObject_MockObject + * @var OrderFactory|MockObject */ private $orderFactoryMock; /** - * @var \Magento\Framework\Api\DataObjectHelper|PHPUnit_Framework_MockObject_MockObject + * @var \Magento\Framework\Api\DataObjectHelper|MockObject */ private $dataObjectHelperMock; /** - * @var ToOrder|PHPUnit_Framework_MockObject_MockObject + * @var ToOrder|MockObject */ private $toOrderMock; /** - * @var ToOrderAddress|PHPUnit_Framework_MockObject_MockObject + * @var ToOrderAddress|MockObject */ private $toOrderAddressMock; /** - * @var ToOrderPayment|PHPUnit_Framework_MockObject_MockObject + * @var ToOrderPayment|MockObject */ private $toOrderPaymentMock; /** - * @var PriceCurrencyInterface|PHPUnit_Framework_MockObject_MockObject + * @var PriceCurrencyInterface|MockObject */ private $priceMock; /** - * @var ToOrderItem|PHPUnit_Framework_MockObject_MockObject + * @var ToOrderItem|MockObject */ private $toOrderItemMock; /** - * @var PlaceOrderFactory|PHPUnit_Framework_MockObject_MockObject + * @var PlaceOrderFactory|MockObject */ private $placeOrderFactoryMock; /** - * @var Generic|PHPUnit_Framework_MockObject_MockObject + * @var Generic|MockObject */ private $sessionMock; /** - * @var PHPUnit_Framework_MockObject_MockObject + * @var MockObject */ private $scopeConfigMock; + /** + * @inheritDoc + */ protected function setUp() { $this->checkoutSessionMock = $this->createSimpleMock(Session::class); @@ -265,7 +276,13 @@ protected function setUp() ); } - public function testSetShippingItemsInformation() + /** + * Verify set shipping items information. + * + * @return void + * @throws LocalizedException + */ + public function testSetShippingItemsInformation(): void { $info = [ [ @@ -313,10 +330,12 @@ public function testSetShippingItemsInformation() } /** - * @expectedException \Magento\Framework\Exception\LocalizedException - * @expectedExceptionMessage Verify the shipping address information and continue. + * Verify set shipping items information for address leak + * + * @return void + * @throws LocalizedException */ - public function testSetShippingItemsInformationForAddressLeak() + public function testSetShippingItemsInformationForAddressLeak(): void { $info = [ [ @@ -340,10 +359,18 @@ public function testSetShippingItemsInformationForAddressLeak() $this->helperMock->expects($this->once())->method('getMaximumQty')->willReturn(500); $this->customerMock->expects($this->once())->method('getAddresses')->willReturn($customerAddresses); + $this->expectExceptionMessage('Verify the shipping address information and continue.'); + $this->assertEquals($this->model, $this->model->setShippingItemsInformation($info)); } - public function testUpdateQuoteCustomerShippingAddress() + /** + * Verify update quote customer shipping address. + * + * @return void + * @throws LocalizedException + */ + public function testUpdateQuoteCustomerShippingAddress(): void { $addressId = 42; $customerAddressId = 42; @@ -359,10 +386,12 @@ public function testUpdateQuoteCustomerShippingAddress() } /** - * @expectedException \Magento\Framework\Exception\LocalizedException - * @expectedExceptionMessage Verify the shipping address information and continue. + * Verify update quote customer shipping address for address leak + * + * @return void + * @throws LocalizedException */ - public function testUpdateQuoteCustomerShippingAddressForAddressLeak() + public function testUpdateQuoteCustomerShippingAddressForAddressLeak(): void { $addressId = 43; $customerAddressId = 42; @@ -371,11 +400,18 @@ public function testUpdateQuoteCustomerShippingAddressForAddressLeak() $this->getCustomerAddressMock($customerAddressId) ]; $this->customerMock->expects($this->once())->method('getAddresses')->willReturn($customerAddresses); + $this->expectExceptionMessage('Verify the shipping address information and continue.'); $this->assertEquals($this->model, $this->model->updateQuoteCustomerShippingAddress($addressId)); } - public function testSetQuoteCustomerBillingAddress() + /** + * Verify set quote customer billing address. + * + * @return void + * @throws LocalizedException + */ + public function testSetQuoteCustomerBillingAddress(): void { $addressId = 42; $customerAddressId = 42; @@ -389,10 +425,12 @@ public function testSetQuoteCustomerBillingAddress() } /** - * @expectedException \Magento\Framework\Exception\LocalizedException - * @expectedExceptionMessage Verify the billing address information and continue. + * Verify set quote customer billing address for address leak. + * + * @return void + * @throws LocalizedException */ - public function testSetQuoteCustomerBillingAddressForAddressLeak() + public function testSetQuoteCustomerBillingAddressForAddressLeak(): void { $addressId = 43; $customerAddressId = 42; @@ -401,11 +439,17 @@ public function testSetQuoteCustomerBillingAddressForAddressLeak() $this->getCustomerAddressMock($customerAddressId) ]; $this->customerMock->expects($this->once())->method('getAddresses')->willReturn($customerAddresses); + $this->expectExceptionMessage('Verify the billing address information and continue.'); $this->assertEquals($this->model, $this->model->setQuoteCustomerBillingAddress($addressId)); } - public function testGetQuoteShippingAddressesItems() + /** + * Verify get quote shipping addresses items. + * + * @return void + */ + public function testGetQuoteShippingAddressesItems(): void { $quoteItem = $this->getMockBuilder(AddressItem::class) ->disableOriginalConstructor() @@ -415,7 +459,13 @@ public function testGetQuoteShippingAddressesItems() $this->model->getQuoteShippingAddressesItems(); } - public function testSetShippingMethods() + /** + * Verify set shipping methods + * + * @return void + * @throws LocalizedException + */ + public function testSetShippingMethods(): void { $methodsArray = [1 => 'flatrate_flatrate', 2 => 'tablerate_bestway']; $addressId = 1; @@ -444,12 +494,15 @@ public function testSetShippingMethods() } /** + * Verify create orders + * * @return void + * @throws \Exception */ public function testCreateOrders(): void { $addressTotal = 5; - $productType = \Magento\Catalog\Model\Product\Type::TYPE_SIMPLE; + $productType = Type::TYPE_SIMPLE; $infoBuyRequest = [ 'info_buyRequest' => [ 'product' => '1', @@ -460,7 +513,7 @@ public function testCreateOrders(): void $paymentProviderCode = 'checkmo'; $shippingPrice = '0.00'; $currencyCode = 'USD'; - $simpleProductTypeMock = $this->getMockBuilder(\Magento\Catalog\Model\Product\Type\Simple::class) + $simpleProductTypeMock = $this->getMockBuilder(Simple::class) ->disableOriginalConstructor() ->setMethods(['getOrderOptions']) ->getMock(); @@ -484,8 +537,8 @@ public function testCreateOrders(): void ->getMock(); $storeMock->method('getBaseCurrency')->willReturn($currencyMock); $storeMock->method('getCurrentCurrencyCode')->willReturn($currencyCode); - $orderAddressMock = $this->createSimpleMock(\Magento\Sales\Api\Data\OrderAddressInterface::class); - $orderPaymentMock = $this->createSimpleMock(\Magento\Sales\Api\Data\OrderPaymentInterface::class); + $orderAddressMock = $this->createSimpleMock(OrderAddressInterface::class); + $orderPaymentMock = $this->createSimpleMock(OrderPaymentInterface::class); $orderItemMock = $this->getMockBuilder(\Magento\Sales\Model\Order\Item::class) ->disableOriginalConstructor() ->setMethods(['getQuoteItemId']) @@ -499,7 +552,7 @@ public function testCreateOrders(): void $this->orderFactoryMock->expects($this->once())->method('create')->willReturn($orderMock); $this->dataObjectHelperMock->expects($this->once())->method('mergeDataObjects') ->with( - \Magento\Sales\Api\Data\OrderInterface::class, + OrderInterface::class, $orderMock, $orderMock )->willReturnSelf(); @@ -517,6 +570,7 @@ public function testCreateOrders(): void )->willReturn($orderAddressMock); $this->toOrderPaymentMock->method('convert')->willReturn($orderPaymentMock); $this->toOrderItemMock->method('convert')->with($quoteAddressItemMock)->willReturn($orderItemMock); + $this->quoteMock->expects($this->once())->method('collectTotals')->willReturnSelf(); $placeOrderServiceMock = $this->getMockBuilder(PlaceOrderDefault::class) ->disableOriginalConstructor() @@ -530,10 +584,155 @@ public function testCreateOrders(): void } /** + * Create orders verify exception message + * + * @param array $config + * + * @return void + * @dataProvider getConfigCreateOrders + * @throws \Exception + */ + public function testCreateOrdersWithThrowsException(array $config): void + { + $simpleProductTypeMock = $this->getMockBuilder(Simple::class) + ->disableOriginalConstructor() + ->setMethods(['getOrderOptions']) + ->getMock(); + $orderAddressMock = $this->createSimpleMock(OrderAddressInterface::class); + $orderPaymentMock = $this->createSimpleMock(OrderPaymentInterface::class); + $orderItemMock = $this->createMock(\Magento\Sales\Model\Order\Item::class); + $productMock = $this->getProductMock($simpleProductTypeMock); + $orderMock = $this->getOrderMock( + $orderAddressMock, + $orderPaymentMock, + $orderItemMock + ); + $quoteItemMock = $this->getQuoteItemMock($config['productType'], $productMock); + $quoteAddressItemMock = $this->getQuoteAddressItemMock( + $quoteItemMock, + $config['productType'], + $config['infoBuyRequest'] + ); + list($shippingAddressMock, $billingAddressMock) = $this->getQuoteAddressesMock( + $quoteAddressItemMock, + $config['addressTotal'] + ); + $this->setQuoteMockData( + $config['paymentProviderCode'], + $shippingAddressMock, + $billingAddressMock + ); + $currencyMock = $this->getCurrencyMock($config['shippingPrice']); + $storeMock = $this->getStoreMock($currencyMock, $config['currencyCode']); + $placeOrderServiceMock = $this->getMockBuilder(PlaceOrderDefault::class) + ->disableOriginalConstructor() + ->setMethods(['place']) + ->getMock(); + + $orderItemMock->method('getQuoteItemId')->willReturn($config['quoteItemId']); + $simpleProductTypeMock->method('getOrderOptions') + ->with($productMock) + ->willReturn($config['infoBuyRequest']); + $this->getOrderMockData($orderMock, $storeMock, $config['shippingPrice']); + $this->orderFactoryMock->expects($this->once()) + ->method('create') + ->willReturn($orderMock); + $this->dataObjectHelperMock->expects($this->once()) + ->method('mergeDataObjects') + ->with(OrderInterface::class, $orderMock, $orderMock) + ->willReturnSelf(); + $this->priceMock->expects($this->once()) + ->method('round') + ->with($config['addressTotal']) + ->willReturn($config['addressTotal']); + $this->toOrderMock->expects($this->once()) + ->method('convert') + ->with($shippingAddressMock) + ->willReturn($orderMock); + $this->toOrderAddressMock->expects($this->exactly(2)) + ->method('convert') + ->withConsecutive([$billingAddressMock, []], [$shippingAddressMock, []]) + ->willReturn($orderAddressMock); + $this->toOrderPaymentMock->method('convert') + ->willReturn($orderPaymentMock); + $this->toOrderItemMock->method('convert') + ->with($quoteAddressItemMock) + ->willReturn($orderItemMock); + $placeOrderServiceMock->method('place') + ->with([$orderMock]) + ->willReturn([$config['quoteId'] => new \Exception()]); + $this->quoteMock->expects($this->any()) + ->method('__call') + ->willReturnSelf(); + $this->placeOrderFactoryMock->method('create') + ->with($config['paymentProviderCode']) + ->willReturn($placeOrderServiceMock); + $this->quoteMock->expects($this->exactly(2)) + ->method('collectTotals') + ->willReturnSelf(); + $this->quoteRepositoryMock->expects($this->once()) + ->method('save') + ->with($this->quoteMock); + + $this->expectExceptionMessage('Quote address for failed order ID "1" not found.'); + + $this->model->createOrders(); + } + + /** + * Return Store Mock. + * + * @param MockObject $currencyMock + * @param string $currencyCode + * @return MockObject + */ + private function getStoreMock($currencyMock, string $currencyCode): MockObject + { + $storeMock = $this->getMockBuilder(Store::class) + ->disableOriginalConstructor() + ->setMethods(['getBaseCurrency','getCurrentCurrencyCode' ]) + ->getMock(); + $storeMock->method('getBaseCurrency') + ->willReturn($currencyMock); + $storeMock->method('getCurrentCurrencyCode') + ->willReturn($currencyCode); + + return $storeMock; + } + + /** + * Return Order Mock with data + * + * @param MockObject $orderMock + * @param MockObject $storeMock + * @param string $shippingPrice + * @return void + */ + private function getOrderMockData( + MockObject $orderMock, + MockObject $storeMock, + string $shippingPrice + ): void { + $orderMock->expects($this->once()) + ->method('getStore') + ->willReturn($storeMock); + $orderMock->expects($this->once()) + ->method('setBaseShippingAmount') + ->with($shippingPrice) + ->willReturnSelf(); + $orderMock->expects($this->once()) + ->method('setShippingAmount') + ->with($shippingPrice) + ->willReturnSelf(); + } + + /** + * Return Payment Mock. + * * @param string $paymentProviderCode - * @return PHPUnit_Framework_MockObject_MockObject + * @return MockObject */ - private function getPaymentMock(string $paymentProviderCode): PHPUnit_Framework_MockObject_MockObject + private function getPaymentMock(string $paymentProviderCode): MockObject { $abstractMethod = $this->getMockBuilder(AbstractMethod::class) ->disableOriginalConstructor() @@ -552,10 +751,12 @@ private function getPaymentMock(string $paymentProviderCode): PHPUnit_Framework_ } /** - * @param \Magento\Catalog\Model\Product\Type\Simple|PHPUnit_Framework_MockObject_MockObject $simpleProductTypeMock - * @return PHPUnit_Framework_MockObject_MockObject + * Return Product Mock. + * + * @param Simple|MockObject $simpleProductTypeMock + * @return MockObject */ - private function getProductMock($simpleProductTypeMock): PHPUnit_Framework_MockObject_MockObject + private function getProductMock($simpleProductTypeMock): MockObject { $productMock = $this->getMockBuilder(\Magento\Catalog\Model\Product::class) ->disableOriginalConstructor() @@ -567,11 +768,13 @@ private function getProductMock($simpleProductTypeMock): PHPUnit_Framework_MockO } /** + * Return Quote Item Mock. + * * @param string $productType - * @param \Magento\Catalog\Model\Product|PHPUnit_Framework_MockObject_MockObject $productMock - * @return PHPUnit_Framework_MockObject_MockObject + * @param \Magento\Catalog\Model\Product|MockObject $productMock + * @return MockObject */ - private function getQuoteItemMock($productType, $productMock): PHPUnit_Framework_MockObject_MockObject + private function getQuoteItemMock($productType, $productMock): MockObject { $quoteItemMock = $this->getMockBuilder(\Magento\Quote\Model\Quote\Item::class) ->disableOriginalConstructor() @@ -584,16 +787,15 @@ private function getQuoteItemMock($productType, $productMock): PHPUnit_Framework } /** - * @param \Magento\Quote\Model\Quote\Item|PHPUnit_Framework_MockObject_MockObject $quoteItemMock + * Return Quote Address Item Mock + * + * @param \Magento\Quote\Model\Quote\Item|MockObject $quoteItemMock * @param string $productType * @param array $infoBuyRequest - * @return PHPUnit_Framework_MockObject_MockObject + * @return MockObject */ - private function getQuoteAddressItemMock( - $quoteItemMock, - string $productType, - array $infoBuyRequest - ): PHPUnit_Framework_MockObject_MockObject { + private function getQuoteAddressItemMock($quoteItemMock, string $productType, array $infoBuyRequest): MockObject + { $quoteAddressItemMock = $this->getMockBuilder(\Magento\Quote\Model\Quote\Address\Item::class) ->disableOriginalConstructor() ->setMethods(['getQuoteItem', 'setProductType', 'setProductOptions', 'getParentItem']) @@ -607,7 +809,8 @@ private function getQuoteAddressItemMock( } /** - * @param \Magento\Quote\Model\Quote\Address\Item|PHPUnit_Framework_MockObject_MockObject $quoteAddressItemMock + * Return Quote Addresses Mock + * @param \Magento\Quote\Model\Quote\Address\Item|MockObject $quoteAddressItemMock * @param int $addressTotal * @return array */ @@ -650,9 +853,11 @@ private function getQuoteAddressesMock($quoteAddressItemMock, int $addressTotal) } /** + * Set data for Quote Mock. + * * @param string $paymentProviderCode - * @param Address|PHPUnit_Framework_MockObject_MockObject $shippingAddressMock - * @param Address|PHPUnit_Framework_MockObject_MockObject $billingAddressMock + * @param Address|MockObject $shippingAddressMock + * @param Address|MockObject $billingAddressMock * * @return void */ @@ -669,22 +874,20 @@ private function setQuoteMockData(string $paymentProviderCode, $shippingAddressM $this->quoteMock->method('hasVirtualItems') ->willReturn(false); $this->quoteMock->expects($this->once())->method('reserveOrderId')->willReturnSelf(); - $this->quoteMock->expects($this->once())->method('collectTotals')->willReturnSelf(); $this->quoteMock->method('getId')->willReturn($quoteId); $this->quoteMock->method('setIsActive')->with(false)->willReturnSelf(); } /** - * @param \Magento\Sales\Api\Data\OrderAddressInterface|PHPUnit_Framework_MockObject_MockObject $orderAddressMock - * @param \Magento\Sales\Api\Data\OrderPaymentInterface|PHPUnit_Framework_MockObject_MockObject $orderPaymentMock - * @param \Magento\Sales\Model\Order\Item|PHPUnit_Framework_MockObject_MockObject $orderItemMock - * @return PHPUnit_Framework_MockObject_MockObject + * Return Order Mock. + * + * @param OrderAddressInterface|MockObject $orderAddressMock + * @param OrderPaymentInterface|MockObject $orderPaymentMock + * @param \Magento\Sales\Model\Order\Item|MockObject $orderItemMock + * @return MockObject */ - private function getOrderMock( - $orderAddressMock, - $orderPaymentMock, - $orderItemMock - ): PHPUnit_Framework_MockObject_MockObject { + private function getOrderMock($orderAddressMock, $orderPaymentMock, $orderItemMock): MockObject + { $orderMock = $this->getMockBuilder(\Magento\Sales\Model\Order::class) ->disableOriginalConstructor() ->setMethods( @@ -696,6 +899,7 @@ private function getOrderMock( 'addItem', 'getIncrementId', 'getId', + 'getEntityId', 'getCanSendNewEmailFlag', 'getItems', 'setShippingMethod', @@ -712,6 +916,7 @@ private function getOrderMock( $orderMock->method('addItem')->with($orderItemMock)->willReturnSelf(); $orderMock->method('getIncrementId')->willReturn('1'); $orderMock->method('getId')->willReturn('1'); + $orderMock->method('getEntityId')->willReturn('1'); $orderMock->method('getCanSendNewEmailFlag')->willReturn(false); $orderMock->method('getItems')->willReturn([$orderItemMock]); @@ -721,11 +926,13 @@ private function getOrderMock( /** * Tests exception for addresses with country id not in the allowed countries list. * - * @expectedException \Magento\Framework\Exception\LocalizedException - * @expectedExceptionMessage Some addresses can't be used due to the configurations for specific countries. + * @return void + * @throws \Exception */ - public function testCreateOrdersCountryNotPresentInAllowedListException() + public function testCreateOrdersCountryNotPresentInAllowedListException(): void { + $exceptionMessage = 'Some addresses can\'t be used due to the configurations for specific countries.'; + $abstractMethod = $this->getMockBuilder(AbstractMethod::class) ->disableOriginalConstructor() ->setMethods(['isAvailable']) @@ -757,15 +964,81 @@ public function testCreateOrdersCountryNotPresentInAllowedListException() ->willReturn($paymentMock); $this->quoteMock->method('getAllShippingAddresses') ->willReturn([$shippingAddressMock]); + $this->expectExceptionMessage($exceptionMessage); $this->model->createOrders(); } /** + * Verify validate minimum amount multi address is false. + * + * @return void + */ + public function testValidateMinimumAmountMultiAddressFalse(): void + { + $addressMock = $this->createMock(\Magento\Quote\Model\Quote\Address::class); + + $this->scopeConfigMock->expects($this->exactly(2)) + ->method('isSetFlag') + ->withConsecutive( + ['sales/minimum_order/active', \Magento\Store\Model\ScopeInterface::SCOPE_STORE], + ['sales/minimum_order/multi_address', \Magento\Store\Model\ScopeInterface::SCOPE_STORE] + )->willReturnOnConsecutiveCalls(true, false); + + $this->scopeConfigMock->expects($this->exactly(2)) + ->method('getValue') + ->withConsecutive( + ['sales/minimum_order/amount', \Magento\Store\Model\ScopeInterface::SCOPE_STORE], + ['sales/minimum_order/tax_including', \Magento\Store\Model\ScopeInterface::SCOPE_STORE] + )->willReturnOnConsecutiveCalls(100, false); + + $this->checkoutSessionMock->expects($this->atLeastOnce()) + ->method('getQuote') + ->willReturn($this->quoteMock); + $this->quoteMock->expects($this->once()) + ->method('getStoreId') + ->willReturn(1); + $this->quoteMock->expects($this->once()) + ->method('getAllAddresses') + ->willReturn([$addressMock]); + $addressMock->expects($this->once()) + ->method('getBaseSubtotalWithDiscount') + ->willReturn(101); + + $this->assertTrue($this->model->validateMinimumAmount()); + } + + /** + * Verify validate minimum amount multi address is true. + * + * @return void + */ + public function testValidateMinimumAmountMultiAddressTrue(): void + { + $this->scopeConfigMock->expects($this->exactly(2)) + ->method('isSetFlag') + ->withConsecutive( + ['sales/minimum_order/active', \Magento\Store\Model\ScopeInterface::SCOPE_STORE], + ['sales/minimum_order/multi_address', \Magento\Store\Model\ScopeInterface::SCOPE_STORE] + )->willReturnOnConsecutiveCalls(true, true); + + $this->checkoutSessionMock->expects($this->atLeastOnce()) + ->method('getQuote') + ->willReturn($this->quoteMock); + $this->quoteMock->expects($this->once()) + ->method('validateMinimumAmount') + ->willReturn(false); + + $this->assertFalse($this->model->validateMinimumAmount()); + } + + /** + * Return Extension Attributes Mock. + * * @param ShippingAssignment $shippingAssignmentMock - * @return CartExtension|PHPUnit_Framework_MockObject_MockObject + * @return CartExtension|MockObject */ - private function getExtensionAttributesMock(ShippingAssignment $shippingAssignmentMock) + private function getExtensionAttributesMock(ShippingAssignment $shippingAssignmentMock): MockObject { $extensionAttributesMock = $this->getMockBuilder(CartExtension::class) ->setMethods(['setShippingAssignments']) @@ -781,9 +1054,11 @@ private function getExtensionAttributesMock(ShippingAssignment $shippingAssignme } /** - * @return ShippingAssignment | PHPUnit_Framework_MockObject_MockObject + * Return Shipping Assignment Mock. + * + * @return ShippingAssignment | MockObject */ - private function getShippingAssignmentMock() + private function getShippingAssignmentMock(): MockObject { $shippingAssignmentMock = $this->getMockBuilder(ShippingAssignment::class) ->disableOriginalConstructor() @@ -801,7 +1076,12 @@ private function getShippingAssignmentMock() return $shippingAssignmentMock; } - private function mockShippingAssignment() + /** + * Expected shipping assignment + * + * @return void + */ + private function mockShippingAssignment(): void { $shippingAssignmentMock = $this->getShippingAssignmentMock(); @@ -825,10 +1105,12 @@ private function mockShippingAssignment() } /** + * Return Customer Address Mock + * * @param $customerAddressId - * @return Address | PHPUnit_Framework_MockObject_MockObject + * @return Address | MockObject */ - private function getCustomerAddressMock($customerAddressId) + private function getCustomerAddressMock($customerAddressId): MockObject { $customerAddressMock = $this->getMockBuilder(Address::class) ->setMethods(['getId']) @@ -842,46 +1124,59 @@ private function getCustomerAddressMock($customerAddressId) } /** + * Return Simple Mock. + * * @param string $className - * @return PHPUnit_Framework_MockObject_MockObject + * @return MockObject */ - private function createSimpleMock($className) + private function createSimpleMock($className): MockObject { return $this->getMockBuilder($className) ->disableOriginalConstructor() ->getMock(); } - public function testValidateMinimumAmountMultiAddressTrue() + /** + * Return Currency Mock. + * + * @param $shippingPrice + * @return MockObject + */ + private function getCurrencyMock($shippingPrice): MockObject { - $this->scopeConfigMock->expects($this->exactly(2))->method('isSetFlag')->withConsecutive( - ['sales/minimum_order/active', \Magento\Store\Model\ScopeInterface::SCOPE_STORE], - ['sales/minimum_order/multi_address', \Magento\Store\Model\ScopeInterface::SCOPE_STORE] - )->willReturnOnConsecutiveCalls(true, true); - - $this->checkoutSessionMock->expects($this->atLeastOnce())->method('getQuote')->willReturn($this->quoteMock); - $this->quoteMock->expects($this->once())->method('validateMinimumAmount')->willReturn(false); - $this->assertFalse($this->model->validateMinimumAmount()); + $currencyMock = $this->getMockBuilder(Currency::class) + ->disableOriginalConstructor() + ->setMethods([ 'convert' ]) + ->getMock(); + $currencyMock->method('convert')->willReturn($shippingPrice); + return $currencyMock; } - public function testValidateMinimumAmountMultiAddressFalse() + /** + * Data provider + * + * @return array + */ + public function getConfigCreateOrders(): array { - $addressMock = $this->createMock(\Magento\Quote\Model\Quote\Address::class); - $this->scopeConfigMock->expects($this->exactly(2))->method('isSetFlag')->withConsecutive( - ['sales/minimum_order/active', \Magento\Store\Model\ScopeInterface::SCOPE_STORE], - ['sales/minimum_order/multi_address', \Magento\Store\Model\ScopeInterface::SCOPE_STORE] - )->willReturnOnConsecutiveCalls(true, false); - - $this->scopeConfigMock->expects($this->exactly(2))->method('getValue')->withConsecutive( - ['sales/minimum_order/amount', \Magento\Store\Model\ScopeInterface::SCOPE_STORE], - ['sales/minimum_order/tax_including', \Magento\Store\Model\ScopeInterface::SCOPE_STORE] - )->willReturnOnConsecutiveCalls(100, false); - - $this->checkoutSessionMock->expects($this->atLeastOnce())->method('getQuote')->willReturn($this->quoteMock); - $this->quoteMock->expects($this->once())->method('getStoreId')->willReturn(1); - $this->quoteMock->expects($this->once())->method('getAllAddresses')->willReturn([$addressMock]); - $addressMock->expects($this->once())->method('getBaseSubtotalWithDiscount')->willReturn(101); - - $this->assertTrue($this->model->validateMinimumAmount()); + return [ + [ + [ + 'quoteId' => 1, + 'addressTotal' => 5, + 'productType' => Type::TYPE_SIMPLE, + 'infoBuyRequest'=> [ + 'info_buyRequest' => [ + 'product' => '1', + 'qty' => 1, + ], + ], + 'quoteItemId' => 1, + 'paymentProviderCode' => 'checkmo', + 'shippingPrice' => '0.00', + 'currencyCode' => 'USD', + ] + ] + ]; } } diff --git a/app/code/Magento/Multishipping/Test/Unit/Plugin/DisableMultishippingModeTest.php b/app/code/Magento/Multishipping/Test/Unit/Plugin/DisableMultishippingModeTest.php index 02ae1a70ce801..829d5a9ce2763 100644 --- a/app/code/Magento/Multishipping/Test/Unit/Plugin/DisableMultishippingModeTest.php +++ b/app/code/Magento/Multishipping/Test/Unit/Plugin/DisableMultishippingModeTest.php @@ -16,7 +16,7 @@ use Magento\Quote\Model\Quote; /** - * Class DisableMultishippingModeTest + * Set of Unit Tets to cover DisableMultishippingMode */ class DisableMultishippingModeTest extends \PHPUnit\Framework\TestCase { @@ -59,10 +59,10 @@ protected function setUp() public function testExecuteTurnsOffMultishippingModeOnMultishippingQuote(): void { $subject = $this->createMock(Index::class); - $extensionAttributes = $this->createPartialMock( - CartExtensionInterface::class, - ['setShippingAssignments', 'getShippingAssignments'] - ); + $extensionAttributes = $this->getMockBuilder(CartExtensionInterface::class) + ->disableOriginalConstructor() + ->setMethods(['setShippingAssignments', 'getShippingAssignments']) + ->getMockForAbstractClass(); $extensionAttributes->method('getShippingAssignments') ->willReturn( $this->createMock(ShippingAssignmentInterface::class) diff --git a/app/code/Magento/Multishipping/etc/adminhtml/system.xml b/app/code/Magento/Multishipping/etc/adminhtml/system.xml index f30782b357c73..909db7b883904 100644 --- a/app/code/Magento/Multishipping/etc/adminhtml/system.xml +++ b/app/code/Magento/Multishipping/etc/adminhtml/system.xml @@ -7,19 +7,22 @@ --> <config xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="urn:magento:module:Magento_Config:etc/system_file.xsd"> <system> - <section id="multishipping" translate="label" type="text" sortOrder="311" showInDefault="1" showInWebsite="1" showInStore="0"> + <section id="multishipping" translate="label" type="text" sortOrder="311" showInDefault="1" showInWebsite="1"> <label>Multishipping Settings</label> <tab>sales</tab> <resource>Magento_Multishipping::config_multishipping</resource> - <group id="options" translate="label" type="text" sortOrder="2" showInDefault="1" showInWebsite="1" showInStore="0"> + <group id="options" translate="label" type="text" sortOrder="2" showInDefault="1" showInWebsite="1"> <label>Options</label> - <field id="checkout_multiple" translate="label" type="select" sortOrder="1" showInDefault="1" showInWebsite="1" showInStore="0" canRestore="1"> + <field id="checkout_multiple" translate="label" type="select" sortOrder="1" showInDefault="1" showInWebsite="1" canRestore="1"> <label>Allow Shipping to Multiple Addresses</label> <source_model>Magento\Config\Model\Config\Source\Yesno</source_model> </field> - <field id="checkout_multiple_maximum_qty" translate="label" type="text" sortOrder="2" showInDefault="1" showInWebsite="1" showInStore="0" canRestore="1"> + <field id="checkout_multiple_maximum_qty" translate="label" type="text" sortOrder="2" showInDefault="1" showInWebsite="1" canRestore="1"> <label>Maximum Qty Allowed for Shipping to Multiple Addresses</label> <validate>validate-number</validate> + <depends> + <field id="checkout_multiple">1</field> + </depends> </field> </group> </section> diff --git a/app/code/Magento/Multishipping/i18n/en_US.csv b/app/code/Magento/Multishipping/i18n/en_US.csv index 43615e697b931..f9ab587c65fa3 100644 --- a/app/code/Magento/Multishipping/i18n/en_US.csv +++ b/app/code/Magento/Multishipping/i18n/en_US.csv @@ -91,3 +91,4 @@ Options,Options "Ship to:","Ship to:" "Error:","Error:" "We are unable to process your request. Please, try again later.","We are unable to process your request. Please, try again later." +"Quote address for failed order ID "%1" not found.","Quote address for failed order ID "%1" not found." diff --git a/app/code/Magento/Multishipping/registration.php b/app/code/Magento/Multishipping/registration.php index 6598a8e431ca8..f19d2c4d740d8 100644 --- a/app/code/Magento/Multishipping/registration.php +++ b/app/code/Magento/Multishipping/registration.php @@ -4,6 +4,6 @@ * See COPYING.txt for license details. */ -use \Magento\Framework\Component\ComponentRegistrar; +use Magento\Framework\Component\ComponentRegistrar; ComponentRegistrar::register(ComponentRegistrar::MODULE, 'Magento_Multishipping', __DIR__); diff --git a/app/code/Magento/Multishipping/view/frontend/layout/multishipping_checkout_customer_address.xml b/app/code/Magento/Multishipping/view/frontend/layout/multishipping_checkout_customer_address.xml index fee3cb790a522..449f5feeafd9c 100644 --- a/app/code/Magento/Multishipping/view/frontend/layout/multishipping_checkout_customer_address.xml +++ b/app/code/Magento/Multishipping/view/frontend/layout/multishipping_checkout_customer_address.xml @@ -13,6 +13,7 @@ <arguments> <argument name="attribute_data" xsi:type="object">Magento\Customer\Block\DataProviders\AddressAttributeData</argument> <argument name="post_code_config" xsi:type="object">Magento\Customer\Block\DataProviders\PostCodesPatternsAttributeData</argument> + <argument name="view_model" xsi:type="object">Magento\Customer\ViewModel\Address</argument> </arguments> </block> </referenceContainer> diff --git a/app/code/Magento/MysqlMq/etc/adminhtml/system.xml b/app/code/Magento/MysqlMq/etc/adminhtml/system.xml index 045a176a48e87..835283ee263c2 100644 --- a/app/code/Magento/MysqlMq/etc/adminhtml/system.xml +++ b/app/code/Magento/MysqlMq/etc/adminhtml/system.xml @@ -8,22 +8,22 @@ <config xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="urn:magento:module:Magento_Config:etc/system_file.xsd"> <system> <section id="system"> - <group id="mysqlmq" translate="label comment" type="text" sortOrder="15" showInDefault="1" showInWebsite="0" showInStore="0"> + <group id="mysqlmq" translate="label comment" type="text" sortOrder="15" showInDefault="1"> <label>MySQL Message Queue Cleanup</label> <comment>All the times are in minutes. Use "0" if you want to skip automatic clearance.</comment> - <field id="retry_inprogress_after" translate="label" type="text" sortOrder="60" showInDefault="1" showInWebsite="0" showInStore="0"> + <field id="retry_inprogress_after" translate="label" type="text" sortOrder="60" showInDefault="1"> <label>Retry Messages In Progress After</label> <validate>validate-zero-or-greater validate-digits</validate> </field> - <field id="successful_messages_lifetime" translate="label" type="text" sortOrder="50" showInDefault="1" showInWebsite="0" showInStore="0"> + <field id="successful_messages_lifetime" translate="label" type="text" sortOrder="50" showInDefault="1"> <label>Successful Messages Lifetime</label> <validate>validate-zero-or-greater validate-digits</validate> </field> - <field id="failed_messages_lifetime" translate="label" type="text" sortOrder="60" showInDefault="1" showInWebsite="0" showInStore="0"> + <field id="failed_messages_lifetime" translate="label" type="text" sortOrder="60" showInDefault="1"> <label>Failed Messages Lifetime</label> <validate>validate-zero-or-greater validate-digits</validate> </field> - <field id="new_messages_lifetime" translate="label" type="text" sortOrder="60" showInDefault="1" showInWebsite="0" showInStore="0"> + <field id="new_messages_lifetime" translate="label" type="text" sortOrder="60" showInDefault="1"> <label>New Messages Lifetime</label> <validate>validate-zero-or-greater validate-digits</validate> </field> diff --git a/app/code/Magento/MysqlMq/registration.php b/app/code/Magento/MysqlMq/registration.php index e13a38b468005..b0799f79b463f 100644 --- a/app/code/Magento/MysqlMq/registration.php +++ b/app/code/Magento/MysqlMq/registration.php @@ -4,6 +4,6 @@ * See COPYING.txt for license details. */ -use \Magento\Framework\Component\ComponentRegistrar; +use Magento\Framework\Component\ComponentRegistrar; ComponentRegistrar::register(ComponentRegistrar::MODULE, 'Magento_MysqlMq', __DIR__); diff --git a/app/code/Magento/NewRelicReporting/Model/NewRelicWrapper.php b/app/code/Magento/NewRelicReporting/Model/NewRelicWrapper.php index bce42b4e90074..fa7f2f1090629 100644 --- a/app/code/Magento/NewRelicReporting/Model/NewRelicWrapper.php +++ b/app/code/Magento/NewRelicReporting/Model/NewRelicWrapper.php @@ -5,6 +5,8 @@ */ namespace Magento\NewRelicReporting\Model; +use Exception; + /** * Wrapper for New Relic functions * @@ -31,10 +33,10 @@ public function addCustomParameter($param, $value) /** * Wrapper for 'newrelic_notice_error' function * - * @param \Exception $exception + * @param Exception $exception * @return void */ - public function reportError($exception) + public function reportError(Exception $exception) { if ($this->isExtensionInstalled()) { newrelic_notice_error($exception->getMessage(), $exception); @@ -67,6 +69,19 @@ public function setTransactionName(string $transactionName): void } } + /** + * Wrapper for 'newrelic_end_transaction' + * + * @param bool $ignore + * @return void + */ + public function endTransaction($ignore = false) + { + if ($this->isExtensionInstalled()) { + newrelic_end_transaction($ignore); + } + } + /** * Checks whether newrelic-php5 agent is installed * diff --git a/app/code/Magento/NewRelicReporting/Plugin/CommandPlugin.php b/app/code/Magento/NewRelicReporting/Plugin/CommandPlugin.php index 04ad3d0504d34..d21f972da57c6 100644 --- a/app/code/Magento/NewRelicReporting/Plugin/CommandPlugin.php +++ b/app/code/Magento/NewRelicReporting/Plugin/CommandPlugin.php @@ -25,16 +25,24 @@ class CommandPlugin */ private $newRelicWrapper; + /** + * @var string[] + */ + private $skipCommands; + /** * @param Config $config * @param NewRelicWrapper $newRelicWrapper + * @param array $skipCommands */ public function __construct( Config $config, - NewRelicWrapper $newRelicWrapper + NewRelicWrapper $newRelicWrapper, + array $skipCommands = [] ) { $this->config = $config; $this->newRelicWrapper = $newRelicWrapper; + $this->skipCommands = $skipCommands; } /** @@ -46,10 +54,24 @@ public function __construct( */ public function beforeRun(Command $command, ...$args) { - $this->newRelicWrapper->setTransactionName( - sprintf('CLI %s', $command->getName()) - ); + if (!$this->isCommandSkipped($command)) { + $this->newRelicWrapper->setTransactionName( + sprintf('CLI %s', $command->getName()) + ); + } return $args; } + + /** + * Determines whether the Command is declared to be skipped + * + * @param Command $command + * @return bool + */ + private function isCommandSkipped(Command $command): bool + { + $commandName = $command->getName(); + return isset($this->skipCommands[$commandName]) && $this->skipCommands[$commandName] === true; + } } diff --git a/app/code/Magento/NewRelicReporting/Plugin/StatPlugin.php b/app/code/Magento/NewRelicReporting/Plugin/StatPlugin.php new file mode 100644 index 0000000000000..30dddfe11910a --- /dev/null +++ b/app/code/Magento/NewRelicReporting/Plugin/StatPlugin.php @@ -0,0 +1,105 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +declare(strict_types=1); + +namespace Magento\NewRelicReporting\Plugin; + +use Magento\Framework\Profiler\Driver\Standard\Stat; +use Magento\NewRelicReporting\Model\Config; +use Magento\NewRelicReporting\Model\NewRelicWrapper; +use Psr\Log\LoggerInterface; + +/** + * Class StatPlugin handles single Cron Jobs transaction names + */ +class StatPlugin +{ + public const TIMER_NAME_CRON_PREFIX = 'job'; + + /** + * @var Config + */ + private $config; + + /** + * @var NewRelicWrapper + */ + private $newRelicWrapper; + + /** + * @var LoggerInterface + */ + private $logger; + + /** + * @param Config $config + * @param NewRelicWrapper $newRelicWrapper + * @param LoggerInterface $logger + */ + public function __construct( + Config $config, + NewRelicWrapper $newRelicWrapper, + LoggerInterface $logger + ) { + $this->config = $config; + $this->newRelicWrapper = $newRelicWrapper; + $this->logger = $logger; + } + + /** + * Before running original profiler, register NewRelic transaction + * + * @param Stat $schedule + * @param array $args + * @return array + * @see \Magento\Cron\Observer\ProcessCronQueueObserver::startProfiling + * + * @SuppressWarnings(PHPMD.UnusedFormalParameter) + */ + public function beforeStart(Stat $schedule, ...$args): array + { + $timerName = current($args); + + if ($this->isCronJob($timerName)) { + $this->newRelicWrapper->setTransactionName( + sprintf('Cron %s', $timerName) + ); + } + + return $args; + } + + /** + * Before stopping original profiler, close NewRelic transaction + * + * @param Stat $schedule + * @param array $args + * @return array + * + * @SuppressWarnings(PHPMD.UnusedFormalParameter) + */ + public function beforeStop(Stat $schedule, ...$args): array + { + $timerName = current($args); + + if ($this->isCronJob($timerName)) { + $this->newRelicWrapper->endTransaction(); + } + + return $args; + } + + /** + * Determines whether provided name is Cron Job + * + * @param string $timerName + * @return bool + */ + private function isCronJob(string $timerName): bool + { + return 0 === strpos($timerName, static::TIMER_NAME_CRON_PREFIX); + } +} diff --git a/app/code/Magento/NewRelicReporting/Test/Mftf/Test/AdminCheckNewRelicSystemConfigDependencyTest.xml b/app/code/Magento/NewRelicReporting/Test/Mftf/Test/AdminCheckNewRelicSystemConfigDependencyTest.xml index c343f93de2c58..9b210db497e9a 100644 --- a/app/code/Magento/NewRelicReporting/Test/Mftf/Test/AdminCheckNewRelicSystemConfigDependencyTest.xml +++ b/app/code/Magento/NewRelicReporting/Test/Mftf/Test/AdminCheckNewRelicSystemConfigDependencyTest.xml @@ -18,14 +18,14 @@ <group value="NewRelicReporting"/> </annotations> <before> - <actionGroup ref="LoginAsAdmin" stepKey="loginAsAdmin"/> + <actionGroup ref="AdminLoginActionGroup" stepKey="loginAsAdmin"/> <actionGroup ref="AdminNavigateToNewRelicConfigurationActionGroup" stepKey="goToConfigPage"/> <actionGroup ref="AdminExpandConfigSectionActionGroup" stepKey="expandingGeneralSection"> <argument name="sectionName" value="General"/> </actionGroup> </before> <after> - <actionGroup ref="logout" stepKey="logoutOfAdmin"/> + <actionGroup ref="AdminLogoutActionGroup" stepKey="logoutOfAdmin"/> </after> <actionGroup ref="AssertAdminNewRelicConfigFieldIsNotVisibleActionGroup" stepKey="checkingIfApiUrlIsNotVisible"> <argument name="config" value="{{AdminNewRelicConfigSystemSection.apiUrl}}"/> diff --git a/app/code/Magento/NewRelicReporting/Test/Unit/Plugin/CommandPluginTest.php b/app/code/Magento/NewRelicReporting/Test/Unit/Plugin/CommandPluginTest.php new file mode 100644 index 0000000000000..f75997a6302bb --- /dev/null +++ b/app/code/Magento/NewRelicReporting/Test/Unit/Plugin/CommandPluginTest.php @@ -0,0 +1,105 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +declare(strict_types=1); + +namespace Magento\NewRelicReporting\Test\Unit\Plugin; + +use Magento\Framework\TestFramework\Unit\Helper\ObjectManager; +use Magento\NewRelicReporting\Model\NewRelicWrapper; +use Magento\NewRelicReporting\Plugin\CommandPlugin; +use PHPUnit\Framework\MockObject\MockObject; +use PHPUnit\Framework\TestCase; +use Symfony\Component\Console\Command\Command; + +class CommandPluginTest extends TestCase +{ + private const STUB_SKIPPED_COMMAND_NAME = 'skippedCommand'; + private const STUB_NON_SKIPPED_COMMAND_NAME = 'nonSkippedCommand'; + + /** + * @var ObjectManager + */ + private $objectManager; + + /** + * @var MockObject|NewRelicWrapper + */ + private $newRelicWrapperMock; + + /** + * ObjectManager and mocks necessary to run the tests + */ + protected function setUp() + { + $this->newRelicWrapperMock = $this->getMockBuilder(NewRelicWrapper::class) + ->disableOriginalConstructor() + ->setMethods(['setTransactionName']) + ->getMock(); + + $this->objectManager = new ObjectManager($this); + } + + /** + * When Command name is not in the list of skipped, handle New Relic transaction + */ + public function testNewRelicTransactionSetForNonSkippedCommand() + { + $nonSkippedCommand = $this->getCommandMock(self::STUB_NON_SKIPPED_COMMAND_NAME); + + $this->newRelicWrapperMock->expects($this->once()) + ->method('setTransactionName') + ->with(sprintf('CLI %s', self::STUB_NON_SKIPPED_COMMAND_NAME)); + + $commandPlugin = $this->getCommandPlugin([self::STUB_SKIPPED_COMMAND_NAME => true]); + $commandPlugin->beforeRun($nonSkippedCommand); + } + + /** + * When Command name is set to be skipped, do not let run New Relic transaction + */ + public function testNewRelicTransactionOmmitForSkippedCommand() + { + $skippedCommand = $this->getCommandMock(self::STUB_SKIPPED_COMMAND_NAME); + + $this->newRelicWrapperMock->expects($this->never()) + ->method('setTransactionName'); + + $commandPlugin = $this->getCommandPlugin([self::STUB_SKIPPED_COMMAND_NAME => true]); + $commandPlugin->beforeRun($skippedCommand); + } + + /** + * @param string $commandName + * @return Command|MockObject + */ + private function getCommandMock(string $commandName): Command + { + $commandMock = $this->getMockBuilder(Command::class) + ->disableOriginalConstructor() + ->setMethods(['getName']) + ->getMock(); + + $commandMock->method('getName') + ->willReturn($commandName); + + return $commandMock; + } + + /** + * @param string[] $skippedCommands + * @return CommandPlugin + */ + private function getCommandPlugin(array $skippedCommands): CommandPlugin + { + /** @var CommandPlugin $commandPlugin */ + $commandPlugin = $this->objectManager->getObject(CommandPlugin::class, [ + 'skipCommands' => $skippedCommands, + 'newRelicWrapper' => $this->newRelicWrapperMock + ]); + + return $commandPlugin; + } +} diff --git a/app/code/Magento/NewRelicReporting/Test/Unit/Plugin/HttpPluginTest.php b/app/code/Magento/NewRelicReporting/Test/Unit/Plugin/HttpPluginTest.php new file mode 100644 index 0000000000000..e4c6426f9e868 --- /dev/null +++ b/app/code/Magento/NewRelicReporting/Test/Unit/Plugin/HttpPluginTest.php @@ -0,0 +1,98 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +declare(strict_types=1); + +namespace Magento\NewRelicReporting\Test\Unit\Plugin; + +use Exception; +use Magento\Framework\App\Bootstrap; +use Magento\Framework\App\Http; +use Magento\Framework\TestFramework\Unit\Helper\ObjectManager; +use Magento\NewRelicReporting\Model\Config as NewRelicConfig; +use Magento\NewRelicReporting\Model\NewRelicWrapper; +use Magento\NewRelicReporting\Plugin\HttpPlugin; +use PHPUnit\Framework\TestCase; +use PHPUnit\Framework\MockObject\MockObject as MockObject; + +/** + * Test coverage for \Magento\NewRelicReporting\Plugin\HttpPlugin + */ +class HttpPluginTest extends TestCase +{ + /** + * @var HttpPlugin + */ + private $httpPlugin; + + /** + * @var NewRelicConfig|MockObject + */ + private $configMock; + + /** + * @var NewRelicWrapper|MockObject + */ + private $newRelicWrapperMock; + + /** + * @var Http|MockObject + */ + private $httpMock; + + /** + * @var Bootstrap|MockObject + */ + private $bootstrapMock; + + /** + * @var Exception|MockObject + */ + private $exceptionMock; + + /** + * Set Up + */ + public function setUp(): void + { + $objectManager = new ObjectManager($this); + $this->configMock = $this->getMockBuilder(NewRelicConfig::class)->disableOriginalConstructor() + ->getMock(); + $this->newRelicWrapperMock = $this->createMock(NewRelicWrapper::class); + $this->httpMock = $this->createMock(Http::class); + $this->bootstrapMock = $this->createMock(Bootstrap::class); + $this->exceptionMock = $this->createMock(Exception::class); + + $this->httpPlugin = $objectManager->getObject( + HttpPlugin::class, + [ + 'config' => $this->configMock, + 'newRelicWrapper' => $this->newRelicWrapperMock, + ] + ); + } + + /** + * Tests the thrown exception is reported to New Relic + */ + public function testSuccessfullyReportingError(): void + { + $this->configMock->expects($this->once())->method('isNewRelicEnabled')->willReturn(true); + $this->newRelicWrapperMock->expects($this->once())->method('reportError'); + + $this->httpPlugin->beforeCatchException($this->httpMock, $this->bootstrapMock, $this->exceptionMock); + } + + /** + * Tests the thrown exception is not reported to New Relic + */ + public function testNotReportingException(): void + { + $this->configMock->expects($this->once())->method('isNewRelicEnabled')->willReturn(false); + $this->newRelicWrapperMock->expects($this->never())->method('reportError'); + + $this->httpPlugin->beforeCatchException($this->httpMock, $this->bootstrapMock, $this->exceptionMock); + } +} diff --git a/app/code/Magento/NewRelicReporting/Test/Unit/Plugin/StatPluginTest.php b/app/code/Magento/NewRelicReporting/Test/Unit/Plugin/StatPluginTest.php new file mode 100644 index 0000000000000..163f9a69992ed --- /dev/null +++ b/app/code/Magento/NewRelicReporting/Test/Unit/Plugin/StatPluginTest.php @@ -0,0 +1,97 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +declare(strict_types=1); + +namespace Magento\NewRelicReporting\Test\Unit\Plugin; + +use Magento\Framework\Profiler\Driver\Standard\Stat; +use Magento\Framework\TestFramework\Unit\Helper\ObjectManager; +use Magento\NewRelicReporting\Model\NewRelicWrapper; +use Magento\NewRelicReporting\Plugin\StatPlugin; +use PHPUnit\Framework\TestCase; +use PHPUnit_Framework_MockObject_MockObject as MockObject; + +class StatPluginTest extends TestCase +{ + private const STAT_NAME_NOT_CRON_JOB = 'NotCronJob'; + private const STAT_NAME_CRON_JOB = StatPlugin::TIMER_NAME_CRON_PREFIX . 'Name'; + + /** + * @var StatPlugin + */ + private $statPlugin; + + /** + * @var MockObject|NewRelicWrapper + */ + private $newRelicWrapperMock; + + /** + * @var MockObject|Stat + */ + private $statMock; + + /** + * Build class for testing + */ + public function setUp() + { + $objectManager = new ObjectManager($this); + + $this->statPlugin = $objectManager->getObject(StatPlugin::class, [ + 'newRelicWrapper' => $this->getNewRelicWrapperMock() + ]); + + $this->statMock = $this->getMockBuilder(Stat::class)->disableOriginalConstructor()->getMock(); + } + + /** + * Expects that NewRelic wrapper will never be called + */ + public function testNewRelicTransactionNameIsNotSetIfNotCronjobPattern() + { + $this->newRelicWrapperMock + ->expects($this->never()) + ->method('setTransactionName'); + $this->newRelicWrapperMock + ->expects($this->never()) + ->method('endTransaction'); + + $this->statPlugin->beforeStart($this->statMock, self::STAT_NAME_NOT_CRON_JOB); + $this->statPlugin->beforeStop($this->statMock, self::STAT_NAME_NOT_CRON_JOB); + } + + /** + * NewRelic Wrapper is called when Task name fits Cron Job pattern + */ + public function testNewRelicTransactionNameIsSetForCronjobNamePattern() + { + $this->newRelicWrapperMock + ->expects($this->once()) + ->method('setTransactionName'); + $this->newRelicWrapperMock + ->expects($this->once()) + ->method('endTransaction'); + + $this->statPlugin->beforeStart($this->statMock, self::STAT_NAME_CRON_JOB); + $this->statPlugin->beforeStop($this->statMock, self::STAT_NAME_CRON_JOB); + } + + /** + * @return NewRelicWrapper + */ + private function getNewRelicWrapperMock(): NewRelicWrapper + { + if (null === $this->newRelicWrapperMock) { + $this->newRelicWrapperMock = $this->getMockBuilder(NewRelicWrapper::class) + ->disableOriginalConstructor() + ->setMethods(['setTransactionName', 'endTransaction']) + ->getMock(); + } + + return $this->newRelicWrapperMock; + } +} diff --git a/app/code/Magento/NewRelicReporting/Test/Unit/Plugin/StatePluginTest.php b/app/code/Magento/NewRelicReporting/Test/Unit/Plugin/StatePluginTest.php new file mode 100644 index 0000000000000..b2e75d0b435eb --- /dev/null +++ b/app/code/Magento/NewRelicReporting/Test/Unit/Plugin/StatePluginTest.php @@ -0,0 +1,135 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +declare(strict_types=1); + +namespace Magento\NewRelicReporting\Test\Unit\Plugin; + +use Magento\Framework\App\State; +use Magento\Framework\TestFramework\Unit\Helper\ObjectManager; +use Magento\NewRelicReporting\Model\Config as NewRelicConfig; +use Magento\NewRelicReporting\Model\NewRelicWrapper; +use Magento\NewRelicReporting\Plugin\StatePlugin; +use PHPUnit\Framework\TestCase; +use PHPUnit\Framework\MockObject\MockObject as MockObject; +use Psr\Log\LoggerInterface; + +/** + * Test coverage for \Magento\NewRelicReporting\Plugin\StatePlugin + */ +class StatePluginTest extends TestCase +{ + /** + * @var string + */ + private const STUB_APP_NAME = 'app_name'; + + /** + * @var StatePlugin + */ + private $statePlugin; + + /** + * @var NewRelicConfig|MockObject + */ + private $configMock; + + /** + * @var NewRelicWrapper|MockObject + */ + private $newRelicWrapperMock; + + /** + * @var LoggerInterface|MockObject + */ + private $loggerMock; + + /** + * @var State|MockObject + */ + private $stateMock; + + /** + * Set Up + */ + public function setUp(): void + { + $objectManager = new ObjectManager($this); + $this->configMock = $this->getMockBuilder(NewRelicConfig::class)->disableOriginalConstructor() + ->getMock(); + $this->newRelicWrapperMock = $this->createMock(NewRelicWrapper::class); + $this->loggerMock = $this->createMock(LoggerInterface::class); + $this->stateMock = $this->createMock(State::class); + + $this->statePlugin = $objectManager->getObject( + StatePlugin::class, + [ + 'config' => $this->configMock, + 'newRelicWrapper' => $this->newRelicWrapperMock, + 'logger' => $this->loggerMock, + ] + ); + } + + /** + * Tests setting the new relic app name + */ + public function testSuccessfullySettingAppName(): void + { + $this->configMock->expects($this->once())->method('isSeparateApps')->willReturn(true); + $this->configMock->expects($this->any())->method('getNewRelicAppName') + ->willReturn(static::STUB_APP_NAME); + $this->configMock->expects($this->once())->method('isNewRelicEnabled')->willReturn(true); + $this->stateMock->expects($this->once())->method('getAreaCode')->willReturn('frontend'); + $this->newRelicWrapperMock->expects($this->once())->method('setAppName'); + + $this->statePlugin->afterSetAreaCode($this->stateMock, static::STUB_APP_NAME); + } + + /** + * Tests not being able to set the New Relic app name + * + * @param bool $isSeparateApps + * @param string $newRelicAppName + * @param bool $enabled + * + * @dataProvider newRelicConfigDataProvider + */ + public function testSuccessfullySettingAreaCode(bool $isSeparateApps, string $newRelicAppName, bool $enabled): void + { + $this->configMock->expects($this->any())->method('isSeparateApps')->willReturn($isSeparateApps); + $this->configMock->expects($this->any())->method('getNewRelicAppName')->willReturn($newRelicAppName); + $this->configMock->expects($this->any())->method('isNewRelicEnabled')->willReturn($enabled); + $this->newRelicWrapperMock->expects($this->never())->method('setAppName'); + + $this->statePlugin->afterSetAreaCode($this->stateMock, static::STUB_APP_NAME); + } + + /** + * New relic configuration data provider + * + * @return array + */ + public function newRelicConfigDataProvider(): array + { + return [ + 'Separate apps config is disabled' => [ + false, + static::STUB_APP_NAME, + true + ], + 'Application name is not configured' => [ + true, + '', + true + ], + 'New Relic is disabled' => [ + true, + static::STUB_APP_NAME, + false + ] + ]; + } +} diff --git a/app/code/Magento/NewRelicReporting/etc/di.xml b/app/code/Magento/NewRelicReporting/etc/di.xml index 15516f6df89be..cd8b0f46087a4 100644 --- a/app/code/Magento/NewRelicReporting/etc/di.xml +++ b/app/code/Magento/NewRelicReporting/etc/di.xml @@ -41,6 +41,16 @@ </arguments> </type> <type name="Symfony\Component\Console\Command\Command"> - <plugin name="newrelic-describe-commands" type="Magento\NewRelicReporting\Plugin\CommandPlugin"/> + <plugin name="newrelic-describe-commands" type="Magento\NewRelicReporting\Plugin\CommandPlugin"/> + </type> + <type name="Magento\Framework\Profiler\Driver\Standard\Stat"> + <plugin name="newrelic-describe-cronjobs" type="Magento\NewRelicReporting\Plugin\StatPlugin"/> + </type> + <type name="Magento\NewRelicReporting\Plugin\CommandPlugin"> + <arguments> + <argument name="skipCommands" xsi:type="array"> + <item xsi:type="boolean" name="cron:run">true</item> + </argument> + </arguments> </type> </config> diff --git a/app/code/Magento/NewRelicReporting/registration.php b/app/code/Magento/NewRelicReporting/registration.php index 39984a11e26a3..8d43f8d1de563 100644 --- a/app/code/Magento/NewRelicReporting/registration.php +++ b/app/code/Magento/NewRelicReporting/registration.php @@ -4,6 +4,6 @@ * See COPYING.txt for license details. */ -use \Magento\Framework\Component\ComponentRegistrar; +use Magento\Framework\Component\ComponentRegistrar; ComponentRegistrar::register(ComponentRegistrar::MODULE, 'Magento_NewRelicReporting', __DIR__); diff --git a/app/code/Magento/Newsletter/Block/Adminhtml/Subscriber.php b/app/code/Magento/Newsletter/Block/Adminhtml/Subscriber.php index 4d5165db68736..d05657c727192 100644 --- a/app/code/Magento/Newsletter/Block/Adminhtml/Subscriber.php +++ b/app/code/Magento/Newsletter/Block/Adminhtml/Subscriber.php @@ -11,13 +11,18 @@ */ namespace Magento\Newsletter\Block\Adminhtml; +use Magento\Backend\Block\Template; +use Magento\Backend\Block\Template\Context; use Magento\Newsletter\Model\ResourceModel\Queue\Collection; +use Magento\Newsletter\Model\ResourceModel\Queue\CollectionFactory; /** + * Newsletter Subscriber block + * * @api * @since 100.0.2 */ -class Subscriber extends \Magento\Backend\Block\Template +class Subscriber extends Template { /** * Queue collection @@ -32,34 +37,24 @@ class Subscriber extends \Magento\Backend\Block\Template protected $_template = 'Magento_Newsletter::subscriber/list.phtml'; /** - * @var \Magento\Newsletter\Model\ResourceModel\Queue\CollectionFactory + * @var CollectionFactory */ protected $_collectionFactory; /** - * @param \Magento\Backend\Block\Template\Context $context - * @param \Magento\Newsletter\Model\ResourceModel\Queue\CollectionFactory $collectionFactory + * @param Context $context + * @param CollectionFactory $collectionFactory * @param array $data */ public function __construct( - \Magento\Backend\Block\Template\Context $context, - \Magento\Newsletter\Model\ResourceModel\Queue\CollectionFactory $collectionFactory, + Context $context, + CollectionFactory $collectionFactory, array $data = [] ) { $this->_collectionFactory = $collectionFactory; parent::__construct($context, $data); } - /** - * Prepares block to render - * - * @return $this - */ - protected function _beforeToHtml() - { - return parent::_beforeToHtml(); - } - /** * Return queue collection with loaded neversent queues * @@ -68,7 +63,7 @@ protected function _beforeToHtml() public function getQueueCollection() { if ($this->_queueCollection === null) { - /** @var $this->_queueCollection \Magento\Newsletter\Model\ResourceModel\Queue\Collection */ + /** @var $this->_queueCollection Collection */ $this->_queueCollection = $this ->_collectionFactory ->create() diff --git a/app/code/Magento/Newsletter/Controller/Ajax/Status.php b/app/code/Magento/Newsletter/Controller/Ajax/Status.php new file mode 100644 index 0000000000000..5d36b7170738d --- /dev/null +++ b/app/code/Magento/Newsletter/Controller/Ajax/Status.php @@ -0,0 +1,83 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +declare(strict_types=1); + +namespace Magento\Newsletter\Controller\Ajax; + +use Magento\Framework\App\Action\Action; +use Magento\Framework\App\Action\Context; +use Magento\Framework\App\Action\HttpGetActionInterface; +use Magento\Framework\Controller\Result\Json; +use Magento\Framework\Controller\ResultFactory; +use Magento\Framework\Exception\LocalizedException; +use Magento\Framework\Validator\EmailAddress as EmailAddressValidator; +use Magento\Newsletter\Model\GuestSubscriptionChecker; +use Psr\Log\LoggerInterface; + +/** + * Newsletter subscription status verification controller. + */ +class Status extends Action implements HttpGetActionInterface +{ + /** + * @var EmailAddressValidator + */ + private $emailAddressValidator; + + /** + * @var GuestSubscriptionChecker + */ + private $guestSubscriptionChecker; + + /** + * @var LoggerInterface + */ + private $logger; + + /** + * @param Context $context + * @param EmailAddressValidator $emailAddressValidator + * @param GuestSubscriptionChecker $guestSubscriptionChecker + * @param LoggerInterface $logger + */ + public function __construct( + Context $context, + EmailAddressValidator $emailAddressValidator, + GuestSubscriptionChecker $guestSubscriptionChecker, + LoggerInterface $logger + ) { + parent::__construct($context); + $this->emailAddressValidator = $emailAddressValidator; + $this->guestSubscriptionChecker = $guestSubscriptionChecker; + $this->logger = $logger; + } + + /** + * @inheritdoc + */ + public function execute() + { + $email = (string)$this->getRequest()->getParam('email'); + + $response = [ + 'subscribed' => false, + 'errors' => false, + ]; + try { + if (!empty($email) && $this->emailAddressValidator->isValid($email)) { + $response['subscribed'] = $this->guestSubscriptionChecker->isSubscribed($email); + } + } catch (LocalizedException | \DomainException $exception) { + $this->logger->error($exception->getMessage()); + $response['errors'] = true; + } + + /** @var Json $resultJson */ + $resultJson = $this->resultFactory->create(ResultFactory::TYPE_JSON); + + return $resultJson->setData($response); + } +} diff --git a/app/code/Magento/Newsletter/Controller/Subscriber/NewAction.php b/app/code/Magento/Newsletter/Controller/Subscriber/NewAction.php index ea52ae8aaa864..c27a4db926d06 100644 --- a/app/code/Magento/Newsletter/Controller/Subscriber/NewAction.php +++ b/app/code/Magento/Newsletter/Controller/Subscriber/NewAction.php @@ -172,7 +172,10 @@ public function execute() $message = $this->getSuccessMessage((int)$subscriber->getSubscriberStatus()); $this->messageManager->addSuccessMessage($message); } catch (LocalizedException $e) { - $this->messageManager->addErrorMessage($e->getMessage()); + $this->messageManager->addComplexErrorMessage( + 'localizedSubscriptionErrorMessage', + ['message' => $e->getMessage()] + ); } catch (\Exception $e) { $this->messageManager->addExceptionMessage($e, __('Something went wrong with the subscription.')); } diff --git a/app/code/Magento/Newsletter/Model/GuestSubscriptionChecker.php b/app/code/Magento/Newsletter/Model/GuestSubscriptionChecker.php new file mode 100644 index 0000000000000..8e8c68cfc7de5 --- /dev/null +++ b/app/code/Magento/Newsletter/Model/GuestSubscriptionChecker.php @@ -0,0 +1,62 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +declare(strict_types=1); + +namespace Magento\Newsletter\Model; + +use Magento\Framework\App\ResourceConnection; +use Magento\Store\Model\StoreManagerInterface; + +/** + * Checks guest subscription by email. + */ +class GuestSubscriptionChecker +{ + /** + * @var ResourceConnection + */ + private $resourceConnection; + + /** + * @var StoreManagerInterface + */ + private $storeManager; + + /** + * @param ResourceConnection $resourceConnection + * @param StoreManagerInterface $storeManager + */ + public function __construct(ResourceConnection $resourceConnection, StoreManagerInterface $storeManager) + { + $this->resourceConnection = $resourceConnection; + $this->storeManager = $storeManager; + } + + /** + * Check is subscribed by email + * + * @param string $subscriberEmail + * @return bool + */ + public function isSubscribed(string $subscriberEmail): bool + { + if (!empty($subscriberEmail)) { + $storeIds = $this->storeManager->getWebsite()->getStoreIds(); + $connection = $this->resourceConnection->getConnection(); + $select = $connection + ->select() + ->from($this->resourceConnection->getTableName('newsletter_subscriber')) + ->where('subscriber_email = ?', $subscriberEmail) + ->where('store_id IN (?)', $storeIds) + ->where('customer_id = 0') + ->limit(1); + + return (bool)$connection->fetchOne($select); + } + + return false; + } +} diff --git a/app/code/Magento/Newsletter/Model/Plugin/CustomerPlugin.php b/app/code/Magento/Newsletter/Model/Plugin/CustomerPlugin.php index 60b279b659ca6..6bdaa40019f8a 100644 --- a/app/code/Magento/Newsletter/Model/Plugin/CustomerPlugin.php +++ b/app/code/Magento/Newsletter/Model/Plugin/CustomerPlugin.php @@ -17,6 +17,7 @@ use Magento\Newsletter\Model\SubscriptionManagerInterface; use Magento\Store\Model\Store; use Magento\Store\Model\StoreManagerInterface; +use Magento\Framework\Api\SearchResults; use Psr\Log\LoggerInterface; /** @@ -109,8 +110,9 @@ public function afterSave( CustomerInterface $result, CustomerInterface $customer ) { + /** @var Subscriber $subscriber */ $subscriber = $this->getSubscriber($result); - $subscribeStatus = $this->getIsSubscribedFromExtensionAttr($customer) ?? $subscriber->isSubscribed(); + $subscribeStatus = $this->getIsSubscribedFromExtensionAttributes($customer) ?? $subscriber->isSubscribed(); $needToUpdate = $this->isSubscriptionChanged($result, $subscriber, $subscribeStatus); /** @@ -118,7 +120,10 @@ public function afterSave( * and customer is already confirmed registration * than need to subscribe customer */ - if ((int)$subscriber->getStatus() === Subscriber::STATUS_UNCONFIRMED && empty($result->getConfirmation())) { + if ($subscriber->getId() + && (int)$subscriber->getStatus() === Subscriber::STATUS_UNCONFIRMED + && empty($result->getConfirmation()) + ) { $needToUpdate = true; $subscribeStatus = true; } @@ -129,7 +134,7 @@ public function afterSave( : $this->subscriptionManager->unsubscribeCustomer((int)$result->getId(), $storeId); $this->customerSubscriber[(int)$result->getId()] = $subscriber; } - $this->addIsSubscribedExtensionAttr($result, $subscriber->isSubscribed()); + $this->addIsSubscribedExtensionAttribute($result, $subscriber->isSubscribed()); return $result; } @@ -140,14 +145,14 @@ public function afterSave( * @param CustomerInterface $customer * @return bool|null */ - private function getIsSubscribedFromExtensionAttr(CustomerInterface $customer): ?bool + private function getIsSubscribedFromExtensionAttributes(CustomerInterface $customer): ?bool { - $newExtensionAttributes = $customer->getExtensionAttributes(); - if ($newExtensionAttributes === null || $newExtensionAttributes->getIsSubscribed() === null) { + $extensionAttributes = $customer->getExtensionAttributes(); + if ($extensionAttributes === null || $extensionAttributes->getIsSubscribed() === null) { return null; } - return (bool)$newExtensionAttributes->getIsSubscribed(); + return (bool)$extensionAttributes->getIsSubscribed(); } /** @@ -223,19 +228,40 @@ public function afterGetById(CustomerRepositoryInterface $subject, CustomerInter $extensionAttributes = $customer->getExtensionAttributes(); if ($extensionAttributes === null || $extensionAttributes->getIsSubscribed() === null) { $isSubscribed = $this->getSubscriber($customer)->isSubscribed(); - $this->addIsSubscribedExtensionAttr($customer, $isSubscribed); + $this->addIsSubscribedExtensionAttribute($customer, $isSubscribed); } return $customer; } + /** + * Add subscription status to customer list + * + * @param CustomerRepositoryInterface $subject + * @param SearchResults $searchResults + * @return SearchResults + * @SuppressWarnings(PHPMD.UnusedFormalParameter) + */ + public function afterGetList(CustomerRepositoryInterface $subject, SearchResults $searchResults): SearchResults + { + foreach ($searchResults->getItems() as $customer) { + /** @var CustomerExtensionInterface $extensionAttributes */ + $extensionAttributes = $customer->getExtensionAttributes(); + + $isSubscribed = (int) $extensionAttributes->getIsSubscribed() === Subscriber::STATUS_SUBSCRIBED ?: false; + $extensionAttributes->setIsSubscribed($isSubscribed); + } + + return $searchResults; + } + /** * Set Is Subscribed extension attribute * * @param CustomerInterface $customer * @param bool $isSubscribed */ - private function addIsSubscribedExtensionAttr(CustomerInterface $customer, bool $isSubscribed): void + private function addIsSubscribedExtensionAttribute(CustomerInterface $customer, bool $isSubscribed): void { $extensionAttributes = $customer->getExtensionAttributes(); if ($extensionAttributes === null) { diff --git a/app/code/Magento/Newsletter/Test/Mftf/ActionGroup/StorefrontCreateNewSubscriberActionGroup.xml b/app/code/Magento/Newsletter/Test/Mftf/ActionGroup/StorefrontCreateNewSubscriberActionGroup.xml new file mode 100644 index 0000000000000..0aee2cb9b2e3c --- /dev/null +++ b/app/code/Magento/Newsletter/Test/Mftf/ActionGroup/StorefrontCreateNewSubscriberActionGroup.xml @@ -0,0 +1,19 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + /** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +--> + +<actionGroups xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/actionGroupSchema.xsd"> + <actionGroup name="StorefrontCreateNewSubscriberActionGroup"> + <amOnPage url="{{StorefrontHomePage.url}}" stepKey="amOnStorefrontPage"/> + <submitForm selector="{{BasicFrontendNewsletterFormSection.subscribeForm}}" + parameterArray="['email' => '{{_defaultNewsletter.senderEmail}}']" + button="{{BasicFrontendNewsletterFormSection.subscribeButton}}" stepKey="submitForm"/> + <waitForPageLoad stepKey="waitForPageLoad"/> + <waitForElementVisible stepKey="waitForErrorAppears" selector="{{StorefrontMessagesSection.error}}"/> + </actionGroup> +</actionGroups> diff --git a/app/code/Magento/Newsletter/Test/Mftf/Page/AdminNewsletterConfigPage.xml b/app/code/Magento/Newsletter/Test/Mftf/Page/AdminNewsletterConfigPage.xml new file mode 100644 index 0000000000000..00cbaa1656326 --- /dev/null +++ b/app/code/Magento/Newsletter/Test/Mftf/Page/AdminNewsletterConfigPage.xml @@ -0,0 +1,13 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- + /** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +--> +<pages xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Page/etc/PageObject.xsd"> + <page name="AdminNewsletterConfigPage" url="admin/system_config/edit/section/newsletter/" area="admin" module="Magento_Newsletter"> + <section name="AdminNewsletterConfigPageSubscriptionOptionsSection"/> + </page> +</pages> diff --git a/app/code/Magento/Newsletter/Test/Mftf/Page/AdminNewsletterSubscriberPage.xml b/app/code/Magento/Newsletter/Test/Mftf/Page/AdminNewsletterSubscriberPage.xml new file mode 100644 index 0000000000000..0193064c8756c --- /dev/null +++ b/app/code/Magento/Newsletter/Test/Mftf/Page/AdminNewsletterSubscriberPage.xml @@ -0,0 +1,13 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + /** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +--> +<pages xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Page/etc/PageObject.xsd"> + <page name="AdminNewsletterSubscriberPage" url="newsletter/subscriber/index/" area="admin" module="Magento_Newsletter"> + <section name="AdminNewsletterSubscriberGridSection"/> + </page> +</pages> diff --git a/app/code/Magento/Newsletter/Test/Mftf/Page/NewsletterTemplatePage.xml b/app/code/Magento/Newsletter/Test/Mftf/Page/NewsletterTemplatePage.xml deleted file mode 100644 index fa655fadab551..0000000000000 --- a/app/code/Magento/Newsletter/Test/Mftf/Page/NewsletterTemplatePage.xml +++ /dev/null @@ -1,18 +0,0 @@ -<?xml version="1.0" encoding="UTF-8"?> -<!-- - /** - * Copyright © Magento, Inc. All rights reserved. - * See COPYING.txt for license details. - */ ---> -<pages xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" - xsi:noNamespaceSchemaLocation="urn:magento:mftf:Page/etc/PageObject.xsd"> - <page name="NewsletterTemplateForm" url="/newsletter/template/new/" area="admin" module="Magento_Cms"> - <section name="StorefrontNewsletterSection"/> - <section name="StorefrontNewsletterSection"/> - </page> - <page name="NewsletterTemplateGrid" url="/newsletter/template/" area="admin" module="Magento_Cms"> - <section name="StorefrontNewsletterSection"/> - <section name="NewsletterTemplateSection"/> - </page> -</pages> diff --git a/app/code/Magento/Newsletter/Test/Mftf/Page/NewsletterTemplatePage/NewsletterTemplateFormPage.xml b/app/code/Magento/Newsletter/Test/Mftf/Page/NewsletterTemplatePage/NewsletterTemplateFormPage.xml new file mode 100644 index 0000000000000..c24185f6afac6 --- /dev/null +++ b/app/code/Magento/Newsletter/Test/Mftf/Page/NewsletterTemplatePage/NewsletterTemplateFormPage.xml @@ -0,0 +1,14 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + /** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +--> +<pages xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Page/etc/PageObject.xsd"> + <page name="NewsletterTemplateForm" url="/newsletter/template/new/" area="admin" module="Magento_Cms"> + <section name="StorefrontNewsletterSection"/> + <section name="StorefrontNewsletterSection"/> + </page> +</pages> diff --git a/app/code/Magento/Newsletter/Test/Mftf/Page/NewsletterTemplatePage/NewsletterTemplateGridPage.xml b/app/code/Magento/Newsletter/Test/Mftf/Page/NewsletterTemplatePage/NewsletterTemplateGridPage.xml new file mode 100644 index 0000000000000..ce725e1b2de25 --- /dev/null +++ b/app/code/Magento/Newsletter/Test/Mftf/Page/NewsletterTemplatePage/NewsletterTemplateGridPage.xml @@ -0,0 +1,14 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + /** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +--> +<pages xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Page/etc/PageObject.xsd"> + <page name="NewsletterTemplateGrid" url="/newsletter/template/" area="admin" module="Magento_Cms"> + <section name="StorefrontNewsletterSection"/> + <section name="NewsletterTemplateSection"/> + </page> +</pages> diff --git a/app/code/Magento/Newsletter/Test/Mftf/Section/AdminNewsletterConfigPageSubscriptionOptionsSection.xml b/app/code/Magento/Newsletter/Test/Mftf/Section/AdminNewsletterConfigPageSubscriptionOptionsSection.xml new file mode 100644 index 0000000000000..32bedfead088c --- /dev/null +++ b/app/code/Magento/Newsletter/Test/Mftf/Section/AdminNewsletterConfigPageSubscriptionOptionsSection.xml @@ -0,0 +1,14 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + /** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +--> +<sections xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Page/etc/SectionObject.xsd"> + <section name="AdminNewsletterConfigPageSubscriptionOptionsSection"> + <element name="allowGuestSubscription" type="checkbox" selector="input[name='groups[subscription][fields][allow_guest_subscribe][inherit]']" timeout="30"/> + <element name="guestSubscription" type="select" selector="#newsletter_subscription_allow_guest_subscribe" timeout="30"/> + </section> +</sections> diff --git a/app/code/Magento/Newsletter/Test/Mftf/Section/AdminNewsletterSubscriberGridSection.xml b/app/code/Magento/Newsletter/Test/Mftf/Section/AdminNewsletterSubscriberGridSection.xml new file mode 100644 index 0000000000000..3332041817150 --- /dev/null +++ b/app/code/Magento/Newsletter/Test/Mftf/Section/AdminNewsletterSubscriberGridSection.xml @@ -0,0 +1,16 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + /** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +--> +<sections xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Page/etc/SectionObject.xsd"> + <section name="AdminNewsletterSubscriberGridSection"> + <element name="email" type="text" selector="//table[contains(@class, 'data-grid')]/tbody/tr[{{row}}][@data-role='row']/td[@data-column='email']" parameterized="true"/> + <element name="type" type="text" selector="//table[contains(@class, 'data-grid')]/tbody/tr[{{row}}][@data-role='row']/td[@data-column='type']" parameterized="true"/> + <element name="firstName" type="text" selector="//table[contains(@class, 'data-grid')]/tbody/tr[{{row}}][@data-role='row']/td[@data-column='firstname']" parameterized="true"/> + <element name="lastName" type="text" selector="//table[contains(@class, 'data-grid')]/tbody/tr[{{row}}][@data-role='row']/td[@data-column='lastname']" parameterized="true"/> + </section> +</sections> diff --git a/app/code/Magento/Newsletter/Test/Mftf/Section/NewsletterTemplateSection.xml b/app/code/Magento/Newsletter/Test/Mftf/Section/NewsletterTemplateSection.xml deleted file mode 100644 index 4c8f641f78ae3..0000000000000 --- a/app/code/Magento/Newsletter/Test/Mftf/Section/NewsletterTemplateSection.xml +++ /dev/null @@ -1,56 +0,0 @@ -<?xml version="1.0" encoding="UTF-8"?> -<!-- - /** - * Copyright © Magento, Inc. All rights reserved. - * See COPYING.txt for license details. - */ ---> -<sections xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" - xsi:noNamespaceSchemaLocation="urn:magento:mftf:Page/etc/SectionObject.xsd"> - <section name="BasicFieldNewsletterSection"> - <element name="templateName" type="input" selector="#code"/> - <element name="templateSubject" type="input" selector="#subject"/> - <element name="senderName" type="input" selector="#sender_name"/> - <element name="senderEmail" type="input" selector="#sender_email"/> - <element name="save" type="button" selector="button[data-role='template-save']" timeout="60"/> - <element name="searchButton" type="button" selector=".admin__filter-actions button[title=Search]"/> - <element name="searchInput" type="input" selector="input[name=code]"/> - </section> - <section name="NewsletterWYSIWYGSection"> - <element name="TextArea" type="text" selector="#text" /> - <element name="TinyMCE4" type="text" selector=".mce-branding" /> - <element name="ShowHideBtn" type="button" selector="#toggletext" timeout="60"/> - <element name="InsertWidgetBtn" type="button" selector=".action-add-widget"/> - <element name="InsertWidgetIcon" type="button" selector="div[aria-label='Insert Widget']"/> - <element name="InsertVariableIcon" type="button" selector="div[aria-label='Insert Variable']"/> - <element name="InsertVariableBtn" type="button" selector=".add-variable"/> - <element name="InsertImageBtn" type="button" selector=".action-add-image"/> - <element name="InsertImageIcon" type="button" selector="div[aria-label='Insert/edit image']"/> - <element name="Browse" type="button" selector=".mce-i-browse"/> - <element name="BrowseUploadImage" type="file" selector=".fileupload" /> - <element name="image" type="text" selector="//small[text()='{{var1}}']" parameterized="true"/> - <element name="ImageSource" type="input" selector=".mce-combobox.mce-abs-layout-item.mce-last.mce-has-open" /> - <element name="ImageDescription" type="input" selector=".mce-textbox.mce-abs-layout-item.mce-last" /> - <element name="Height" type="input" selector=".mce-textbox.mce-abs-layout-item.mce-first" /> - <element name="UploadImage" type="file" selector=".fileupload" /> - <element name="OkBtn" type="button" selector="//span[text()='Ok']"/> - <element name="InsertFile" type="text" selector="#insert_files"/> - <element name="CreateFolder" type="button" selector="#new_folder" /> - <element name="DeleteSelectedBtn" type="text" selector="#delete_files"/> - <element name="CancelBtn" type="button" selector="#cancel" /> - <element name="Style" type="button" selector=".mce-txt" /> - <element name="Bold" type="button" selector=".mce-i-bold" /> - <element name="Italic" type="button" selector=".mce-i-italic" /> - <element name="Underline" type="button" selector=".mce-i-underline" /> - <element name="AlignLeft" type="button" selector=".mce-i-alignleft" /> - <element name="AlignCenter" type="button" selector=".mce-i-aligncenter" /> - <element name="AlignRight" type="button" selector=".mce-i-alignright" /> - <element name="Bullet" type="button" selector=".mce-i-bullist" /> - <element name="Numlist" type="button" selector=".mce-i-numlist" /> - <element name="InsertLink" type="button" selector=".mce-i-link" /> - <element name="InsertImage" type="button" selector=".mce-i-image" /> - <element name="InsertTable" type="button" selector=".mce-i-table" /> - <element name="SpecialCharacter" type="button" selector=".mce-i-charmap" /> - <element name="Preview" type="text" selector="//td[contains(text(),'{{var1}}')]//following-sibling::td/select//option[contains(text(), 'Preview')]" parameterized="true" timeout="60"/> - </section> -</sections> diff --git a/app/code/Magento/Newsletter/Test/Mftf/Section/NewsletterTemplateSection/BasicFieldNewsletterSection.xml b/app/code/Magento/Newsletter/Test/Mftf/Section/NewsletterTemplateSection/BasicFieldNewsletterSection.xml new file mode 100644 index 0000000000000..8df5053a5bf0c --- /dev/null +++ b/app/code/Magento/Newsletter/Test/Mftf/Section/NewsletterTemplateSection/BasicFieldNewsletterSection.xml @@ -0,0 +1,19 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + /** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +--> +<sections xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Page/etc/SectionObject.xsd"> + <section name="BasicFieldNewsletterSection"> + <element name="templateName" type="input" selector="#code"/> + <element name="templateSubject" type="input" selector="#subject"/> + <element name="senderName" type="input" selector="#sender_name"/> + <element name="senderEmail" type="input" selector="#sender_email"/> + <element name="save" type="button" selector="button[data-role='template-save']" timeout="60"/> + <element name="searchButton" type="button" selector=".admin__filter-actions button[title=Search]"/> + <element name="searchInput" type="input" selector="input[name=code]"/> + </section> +</sections> diff --git a/app/code/Magento/Newsletter/Test/Mftf/Section/NewsletterTemplateSection/BasicFrontendNewsletterFormSection.xml b/app/code/Magento/Newsletter/Test/Mftf/Section/NewsletterTemplateSection/BasicFrontendNewsletterFormSection.xml new file mode 100644 index 0000000000000..8475fb4d55b9e --- /dev/null +++ b/app/code/Magento/Newsletter/Test/Mftf/Section/NewsletterTemplateSection/BasicFrontendNewsletterFormSection.xml @@ -0,0 +1,15 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + /** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +--> +<sections xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Page/etc/SectionObject.xsd"> + <section name="BasicFrontendNewsletterFormSection"> + <element name="newsletterEmail" type="input" selector="#newsletter"/> + <element name="subscribeButton" type="button" selector=".subscribe" timeout="30"/> + <element name="subscribeForm" type="input" selector="#newsletter-validate-detail" timeout="30"/> + </section> +</sections> diff --git a/app/code/Magento/Newsletter/Test/Mftf/Section/NewsletterTemplateSection/NewsletterWYSIWYGSection.xml b/app/code/Magento/Newsletter/Test/Mftf/Section/NewsletterTemplateSection/NewsletterWYSIWYGSection.xml new file mode 100644 index 0000000000000..eb97da28c9437 --- /dev/null +++ b/app/code/Magento/Newsletter/Test/Mftf/Section/NewsletterTemplateSection/NewsletterWYSIWYGSection.xml @@ -0,0 +1,47 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + /** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +--> +<sections xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Page/etc/SectionObject.xsd"> + <section name="NewsletterWYSIWYGSection"> + <element name="TextArea" type="text" selector="#text"/> + <element name="TinyMCE4" type="text" selector=".mce-branding"/> + <element name="ShowHideBtn" type="button" selector="#toggletext" timeout="60"/> + <element name="InsertWidgetBtn" type="button" selector=".action-add-widget"/> + <element name="InsertWidgetIcon" type="button" selector="div[aria-label='Insert Widget']"/> + <element name="InsertVariableIcon" type="button" selector="div[aria-label='Insert Variable']"/> + <element name="InsertVariableBtn" type="button" selector=".add-variable"/> + <element name="InsertImageBtn" type="button" selector=".action-add-image"/> + <element name="InsertImageIcon" type="button" selector="div[aria-label='Insert/edit image']"/> + <element name="Browse" type="button" selector=".mce-i-browse"/> + <element name="BrowseUploadImage" type="file" selector=".fileupload"/> + <element name="image" type="text" selector="//small[text()='{{var1}}']" parameterized="true"/> + <element name="ImageSource" type="input" selector=".mce-combobox.mce-abs-layout-item.mce-last.mce-has-open"/> + <element name="ImageDescription" type="input" selector=".mce-textbox.mce-abs-layout-item.mce-last"/> + <element name="Height" type="input" selector=".mce-textbox.mce-abs-layout-item.mce-first"/> + <element name="UploadImage" type="file" selector=".fileupload"/> + <element name="OkBtn" type="button" selector="//span[text()='Ok']"/> + <element name="InsertFile" type="text" selector="#insert_files"/> + <element name="CreateFolder" type="button" selector="#new_folder"/> + <element name="DeleteSelectedBtn" type="text" selector="#delete_files"/> + <element name="CancelBtn" type="button" selector="#cancel"/> + <element name="Style" type="button" selector=".mce-txt"/> + <element name="Bold" type="button" selector=".mce-i-bold"/> + <element name="Italic" type="button" selector=".mce-i-italic"/> + <element name="Underline" type="button" selector=".mce-i-underline"/> + <element name="AlignLeft" type="button" selector=".mce-i-alignleft"/> + <element name="AlignCenter" type="button" selector=".mce-i-aligncenter"/> + <element name="AlignRight" type="button" selector=".mce-i-alignright"/> + <element name="Bullet" type="button" selector=".mce-i-bullist"/> + <element name="Numlist" type="button" selector=".mce-i-numlist"/> + <element name="InsertLink" type="button" selector=".mce-i-link"/> + <element name="InsertImage" type="button" selector=".mce-i-image"/> + <element name="InsertTable" type="button" selector=".mce-i-table"/> + <element name="SpecialCharacter" type="button" selector=".mce-i-charmap"/> + <element name="Preview" type="text" selector="//td[contains(text(),'{{var1}}')]//following-sibling::td/select//option[contains(text(), 'Preview')]" parameterized="true" timeout="60"/> + </section> +</sections> diff --git a/app/code/Magento/Newsletter/Test/Mftf/Section/VerifySubscribedNewsLetterDisplayedSection.xml b/app/code/Magento/Newsletter/Test/Mftf/Section/VerifySubscribedNewsLetterDisplayedSection.xml deleted file mode 100644 index bb651784d4dcf..0000000000000 --- a/app/code/Magento/Newsletter/Test/Mftf/Section/VerifySubscribedNewsLetterDisplayedSection.xml +++ /dev/null @@ -1,18 +0,0 @@ -<?xml version="1.0" encoding="UTF-8"?> -<!-- - /** - * Copyright © Magento, Inc. All rights reserved. - * See COPYING.txt for license details. - */ ---> - -<sections xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" - xsi:noNamespaceSchemaLocation="urn:magento:mftf:Page/etc/SectionObject.xsd"> - <section name="StorefrontCustomerCreateFormSection"> - <element name="signUpForNewsletter" type="checkbox" selector="//span[contains(text(), 'Sign Up for Newsletter')]"/> - </section> - - <section name="CustomerMyAccountPage"> - <element name="DescriptionNewsletter" type="text" selector=".box-newsletter p"/> - </section> -</sections> diff --git a/app/code/Magento/Newsletter/Test/Mftf/Section/VerifySubscribedNewsLetterDisplayedSection/CustomerMyAccountPageSection.xml b/app/code/Magento/Newsletter/Test/Mftf/Section/VerifySubscribedNewsLetterDisplayedSection/CustomerMyAccountPageSection.xml new file mode 100644 index 0000000000000..d7056c185e928 --- /dev/null +++ b/app/code/Magento/Newsletter/Test/Mftf/Section/VerifySubscribedNewsLetterDisplayedSection/CustomerMyAccountPageSection.xml @@ -0,0 +1,13 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + /** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +--> +<sections xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Page/etc/SectionObject.xsd"> + <section name="CustomerMyAccountPage"> + <element name="DescriptionNewsletter" type="text" selector=".box-newsletter p"/> + </section> +</sections> diff --git a/app/code/Magento/Newsletter/Test/Mftf/Section/VerifySubscribedNewsLetterDisplayedSection/StorefrontCustomerCreateFormSection.xml b/app/code/Magento/Newsletter/Test/Mftf/Section/VerifySubscribedNewsLetterDisplayedSection/StorefrontCustomerCreateFormSection.xml new file mode 100644 index 0000000000000..e5897052e6164 --- /dev/null +++ b/app/code/Magento/Newsletter/Test/Mftf/Section/VerifySubscribedNewsLetterDisplayedSection/StorefrontCustomerCreateFormSection.xml @@ -0,0 +1,13 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + /** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +--> +<sections xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Page/etc/SectionObject.xsd"> + <section name="StorefrontCustomerCreateFormSection"> + <element name="signUpForNewsletter" type="checkbox" selector="//span[contains(text(), 'Sign Up for Newsletter')]"/> + </section> +</sections> diff --git a/app/code/Magento/Newsletter/Test/Mftf/Test/AdminAddImageToWYSIWYGNewsletterTest.xml b/app/code/Magento/Newsletter/Test/Mftf/Test/AdminAddImageToWYSIWYGNewsletterTest.xml index 4992a36c0b3cb..a763f43d9e4d1 100644 --- a/app/code/Magento/Newsletter/Test/Mftf/Test/AdminAddImageToWYSIWYGNewsletterTest.xml +++ b/app/code/Magento/Newsletter/Test/Mftf/Test/AdminAddImageToWYSIWYGNewsletterTest.xml @@ -18,7 +18,7 @@ <testCaseId value="MAGETWO-84377"/> </annotations> <before> - <actionGroup ref="LoginActionGroup" stepKey="login"/> + <actionGroup ref="AdminLoginActionGroup" stepKey="login"/> <actionGroup ref="EnabledWYSIWYGActionGroup" stepKey="enableWYSIWYG"/> <actionGroup ref="SwitchToVersion4ActionGroup" stepKey="switchToTinyMCE4" /> </before> @@ -58,7 +58,7 @@ <closeTab stepKey="closeTab"/> <after> <actionGroup ref="DisabledWYSIWYGActionGroup" stepKey="disableWYSIWYG"/> - <actionGroup ref="logout" stepKey="logout"/> + <actionGroup ref="AdminLogoutActionGroup" stepKey="logout"/> </after> </test> </tests> diff --git a/app/code/Magento/Newsletter/Test/Mftf/Test/AdminAddVariableToWYSIWYGNewsletterTest.xml b/app/code/Magento/Newsletter/Test/Mftf/Test/AdminAddVariableToWYSIWYGNewsletterTest.xml index 958340671ac43..ff2e2a84a612e 100644 --- a/app/code/Magento/Newsletter/Test/Mftf/Test/AdminAddVariableToWYSIWYGNewsletterTest.xml +++ b/app/code/Magento/Newsletter/Test/Mftf/Test/AdminAddVariableToWYSIWYGNewsletterTest.xml @@ -18,7 +18,7 @@ <severity value="AVERAGE"/> </annotations> <before> - <actionGroup ref="LoginActionGroup" stepKey="loginGetFromGeneralFile"/> + <actionGroup ref="AdminLoginActionGroup" stepKey="loginGetFromGeneralFile"/> <actionGroup ref="EnabledWYSIWYGActionGroup" stepKey="enableWYSIWYG"/> <actionGroup ref="SwitchToVersion4ActionGroup" stepKey="switchToTinyMCE4" /> </before> @@ -100,7 +100,7 @@ <closeTab stepKey="closeTab"/> <after> <actionGroup ref="DisabledWYSIWYGActionGroup" stepKey="disableWYSIWYG"/> - <actionGroup ref="logout" stepKey="logout"/> + <actionGroup ref="AdminLogoutActionGroup" stepKey="logout"/> </after> </test> </tests> diff --git a/app/code/Magento/Newsletter/Test/Mftf/Test/AdminAddWidgetToWYSIWYGNewsletterTest.xml b/app/code/Magento/Newsletter/Test/Mftf/Test/AdminAddWidgetToWYSIWYGNewsletterTest.xml index 29c125a52c9e2..73880f283677d 100644 --- a/app/code/Magento/Newsletter/Test/Mftf/Test/AdminAddWidgetToWYSIWYGNewsletterTest.xml +++ b/app/code/Magento/Newsletter/Test/Mftf/Test/AdminAddWidgetToWYSIWYGNewsletterTest.xml @@ -18,7 +18,7 @@ <testCaseId value="MC-6070"/> </annotations> <before> - <actionGroup ref="LoginActionGroup" stepKey="login"/> + <actionGroup ref="AdminLoginActionGroup" stepKey="login"/> <actionGroup ref="EnabledWYSIWYGActionGroup" stepKey="enableWYSIWYG"/> <actionGroup ref="SwitchToVersion4ActionGroup" stepKey="switchToTinyMCE4" /> </before> @@ -55,7 +55,7 @@ <closeTab stepKey="closeTab"/> <after> <actionGroup ref="DisabledWYSIWYGActionGroup" stepKey="disableWYSIWYG"/> - <actionGroup ref="logout" stepKey="logout"/> + <actionGroup ref="AdminLogoutActionGroup" stepKey="logout"/> </after> </test> </tests> diff --git a/app/code/Magento/Newsletter/Test/Mftf/Test/AdminMarketingNewsletterQueueNavigateMenuTest.xml b/app/code/Magento/Newsletter/Test/Mftf/Test/AdminMarketingNewsletterQueueNavigateMenuTest.xml index 31da588250a0a..81fb69f557dfd 100644 --- a/app/code/Magento/Newsletter/Test/Mftf/Test/AdminMarketingNewsletterQueueNavigateMenuTest.xml +++ b/app/code/Magento/Newsletter/Test/Mftf/Test/AdminMarketingNewsletterQueueNavigateMenuTest.xml @@ -20,10 +20,10 @@ <group value="mtf_migrated"/> </annotations> <before> - <actionGroup ref="LoginAsAdmin" stepKey="LoginAsAdmin"/> + <actionGroup ref="AdminLoginActionGroup" stepKey="LoginAsAdmin"/> </before> <after> - <actionGroup ref="logout" stepKey="logout"/> + <actionGroup ref="AdminLogoutActionGroup" stepKey="logout"/> </after> <actionGroup ref="AdminNavigateMenuActionGroup" stepKey="navigateToNewsletterQueuePage"> <argument name="menuUiId" value="{{AdminMenuMarketing.dataUiId}}"/> diff --git a/app/code/Magento/Newsletter/Test/Mftf/Test/AdminMarketingNewsletterSubscribersNavigateMenuTest.xml b/app/code/Magento/Newsletter/Test/Mftf/Test/AdminMarketingNewsletterSubscribersNavigateMenuTest.xml index 8ced2690322f8..9908e4a115820 100644 --- a/app/code/Magento/Newsletter/Test/Mftf/Test/AdminMarketingNewsletterSubscribersNavigateMenuTest.xml +++ b/app/code/Magento/Newsletter/Test/Mftf/Test/AdminMarketingNewsletterSubscribersNavigateMenuTest.xml @@ -20,10 +20,10 @@ <group value="mtf_migrated"/> </annotations> <before> - <actionGroup ref="LoginAsAdmin" stepKey="LoginAsAdmin"/> + <actionGroup ref="AdminLoginActionGroup" stepKey="LoginAsAdmin"/> </before> <after> - <actionGroup ref="logout" stepKey="logout"/> + <actionGroup ref="AdminLogoutActionGroup" stepKey="logout"/> </after> <actionGroup ref="AdminNavigateMenuActionGroup" stepKey="navigateToNewsletterSubscribersPage"> <argument name="menuUiId" value="{{AdminMenuMarketing.dataUiId}}"/> diff --git a/app/code/Magento/Newsletter/Test/Mftf/Test/AdminMarketingNewsletterTemplateNavigateMenuTest.xml b/app/code/Magento/Newsletter/Test/Mftf/Test/AdminMarketingNewsletterTemplateNavigateMenuTest.xml index ca994aa1d6269..bac6023b80446 100644 --- a/app/code/Magento/Newsletter/Test/Mftf/Test/AdminMarketingNewsletterTemplateNavigateMenuTest.xml +++ b/app/code/Magento/Newsletter/Test/Mftf/Test/AdminMarketingNewsletterTemplateNavigateMenuTest.xml @@ -20,10 +20,10 @@ <group value="mtf_migrated"/> </annotations> <before> - <actionGroup ref="LoginAsAdmin" stepKey="LoginAsAdmin"/> + <actionGroup ref="AdminLoginActionGroup" stepKey="LoginAsAdmin"/> </before> <after> - <actionGroup ref="logout" stepKey="logout"/> + <actionGroup ref="AdminLogoutActionGroup" stepKey="logout"/> </after> <actionGroup ref="AdminNavigateMenuActionGroup" stepKey="navigateToNewsletterTemplatePage"> <argument name="menuUiId" value="{{AdminMenuMarketing.dataUiId}}"/> diff --git a/app/code/Magento/Newsletter/Test/Mftf/Test/AdminNameEmptyForGuestTest.xml b/app/code/Magento/Newsletter/Test/Mftf/Test/AdminNameEmptyForGuestTest.xml new file mode 100644 index 0000000000000..5e35f5aab60cd --- /dev/null +++ b/app/code/Magento/Newsletter/Test/Mftf/Test/AdminNameEmptyForGuestTest.xml @@ -0,0 +1,45 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + /** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +--> + +<tests xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/testSchema.xsd"> + <test name="AdminNameEmptyForGuestTest"> + <annotations> + <features value="Newsletter"/> + <stories value="Newsletter Subscribers grid"/> + <group value="Newsletter"/> + <title value="Empty name for Guest Customer"/> + <description value="'Customer First Name' and 'Customer Last Name' should be empty for Guest Customer in Newsletter Subscribers Grid"/> + <severity value="MINOR"/> + </annotations> + + <before> + <actionGroup ref="AdminLoginActionGroup" stepKey="loginAsAdmin"/> + </before> + + <after> + <actionGroup ref="AdminLogoutActionGroup" stepKey="adminLogout"/> + </after> + + <actionGroup ref="StorefrontOpenHomePageActionGroup" stepKey="amOnStorefrontPage"/> + <scrollTo selector="{{StorefrontCustomerFooterSection.footerBlock}}" stepKey="scrollToFooter"/> + <fillField userInput="{{Simple_US_Customer.email}}" selector="{{StorefrontCustomerFooterSection.formSubscribe}}" + stepKey="giveEmail"/> + <click selector="{{StorefrontCustomerFooterSection.buttonSubscribe}}" stepKey="clickSubscribeButton"/> + <waitForPageLoad stepKey="waitForPageLoad"/> + <see selector="{{StorefrontCustomerMessagesSection.successMessage}}" + userInput="Thank you for your subscription." stepKey="checkSuccessMessage"/> + + <amOnPage url="{{AdminNewsletterSubscriberPage.url}}" stepKey="amOnNewsletterSubscribersPage"/> + <see selector="{{AdminNewsletterSubscriberGridSection.email('1')}}" userInput="{{Simple_US_Customer.email}}" + stepKey="checkEmail"/> + <see selector="{{AdminNewsletterSubscriberGridSection.type('1')}}" userInput="Guest" stepKey="checkType"/> + <see selector="{{AdminNewsletterSubscriberGridSection.firstName('1')}}" userInput="" stepKey="checkFirstName"/> + <see selector="{{AdminNewsletterSubscriberGridSection.lastName('1')}}" userInput="" stepKey="checkLastName"/> + </test> +</tests> diff --git a/app/code/Magento/Newsletter/Test/Mftf/Test/AdminReportsNewsletterProblemReportsNavigateMenuTest.xml b/app/code/Magento/Newsletter/Test/Mftf/Test/AdminReportsNewsletterProblemReportsNavigateMenuTest.xml index 3891b90536a17..441deff9a5cf9 100644 --- a/app/code/Magento/Newsletter/Test/Mftf/Test/AdminReportsNewsletterProblemReportsNavigateMenuTest.xml +++ b/app/code/Magento/Newsletter/Test/Mftf/Test/AdminReportsNewsletterProblemReportsNavigateMenuTest.xml @@ -20,10 +20,10 @@ <group value="mtf_migrated"/> </annotations> <before> - <actionGroup ref="LoginAsAdmin" stepKey="LoginAsAdmin"/> + <actionGroup ref="AdminLoginActionGroup" stepKey="LoginAsAdmin"/> </before> <after> - <actionGroup ref="logout" stepKey="logout"/> + <actionGroup ref="AdminLogoutActionGroup" stepKey="logout"/> </after> <actionGroup ref="AdminNavigateMenuActionGroup" stepKey="navigateToNewsletterProblemsReportPage"> <argument name="menuUiId" value="{{AdminMenuReports.dataUiId}}"/> diff --git a/app/code/Magento/Newsletter/Test/Mftf/Test/StorefrontVerifySecureURLRedirectNewsletterTest.xml b/app/code/Magento/Newsletter/Test/Mftf/Test/StorefrontVerifySecureURLRedirectNewsletterTest.xml index 01b5e706fcefb..c38725f263525 100644 --- a/app/code/Magento/Newsletter/Test/Mftf/Test/StorefrontVerifySecureURLRedirectNewsletterTest.xml +++ b/app/code/Magento/Newsletter/Test/Mftf/Test/StorefrontVerifySecureURLRedirectNewsletterTest.xml @@ -8,7 +8,7 @@ <tests xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/testSchema.xsd"> - <test name="StorefrontVerifySecureURLRedirectNewsletter"> + <test name="StorefrontVerifySecureURLRedirectNewsletterTest"> <annotations> <features value="Newsletter"/> <stories value="Storefront Secure URLs"/> diff --git a/app/code/Magento/Newsletter/Test/Mftf/Test/VerifyRegistredLinkDisplayedForGuestSubscriptionNoTest.xml b/app/code/Magento/Newsletter/Test/Mftf/Test/VerifyRegistredLinkDisplayedForGuestSubscriptionNoTest.xml new file mode 100644 index 0000000000000..3288de47dd4b9 --- /dev/null +++ b/app/code/Magento/Newsletter/Test/Mftf/Test/VerifyRegistredLinkDisplayedForGuestSubscriptionNoTest.xml @@ -0,0 +1,18 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + /** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +--> + +<tests xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/testSchema.xsd"> + <test name="VerifyRegistredLinkDisplayedForGuestSubscriptionNoTest"> + <before> + <magentoCLI command="config:set newsletter/subscription/allow_guest_subscribe 0" + stepKey="setConfigGuestSubscriptionDisable"/> + </before> + <actionGroup ref="StorefrontCreateNewSubscriberActionGroup" stepKey="createSubscriber"/> + </test> +</tests> diff --git a/app/code/Magento/Newsletter/Test/Mftf/Test/VerifySubscribedNewsletterDisplayedTest.xml b/app/code/Magento/Newsletter/Test/Mftf/Test/VerifySubscribedNewsletterDisplayedTest.xml index 740b8fbaa0ab7..a568fb1799ac2 100644 --- a/app/code/Magento/Newsletter/Test/Mftf/Test/VerifySubscribedNewsletterDisplayedTest.xml +++ b/app/code/Magento/Newsletter/Test/Mftf/Test/VerifySubscribedNewsletterDisplayedTest.xml @@ -21,7 +21,7 @@ <before> <!--Log in to Magento as admin.--> - <actionGroup ref="LoginActionGroup" stepKey="loginAsAdmin"/> + <actionGroup ref="AdminLoginActionGroup" stepKey="loginAsAdmin"/> <actionGroup ref="AdminCreateWebsiteActionGroup" stepKey="createWebsite"> <argument name="newWebsiteName" value="Second"/> <argument name="websiteCode" value="Base2"/> @@ -49,7 +49,7 @@ </actionGroup> <magentoCLI command="indexer:reindex" stepKey="reindex"/> <magentoCLI command="cache:flush" stepKey="flushCache"/> - <actionGroup ref="logout" stepKey="adminLogout"/> + <actionGroup ref="AdminLogoutActionGroup" stepKey="adminLogout"/> </after> <!--Go to store front (default) and click Create an Account.--> @@ -57,7 +57,7 @@ <argument name="Customer" value="CustomerEntityOne"/> </actionGroup> <!--Sign Out--> - <amOnPage url="customer/account/logout/" stepKey="customerOnLogoutPage"/> + <amOnPage url="{{StorefrontCustomerLogoutPage.url}}" stepKey="customerOnLogoutPage"/> <waitForPageLoad stepKey="waitLogoutCustomer"/> <!--Create new Account with the same email address. (unchecked Sign Up for Newsletter checkbox)--> <actionGroup ref="StorefrontCreateNewAccountNewsletterUncheckedActionGroup" stepKey="createNewAccountNewsletterUnchecked"> diff --git a/app/code/Magento/Newsletter/Test/Mftf/Test/VerifyTinyMCEv4IsNativeWYSIWYGOnNewsletterTest.xml b/app/code/Magento/Newsletter/Test/Mftf/Test/VerifyTinyMCEv4IsNativeWYSIWYGOnNewsletterTest.xml index 0c20357127e6d..3a247402c111d 100644 --- a/app/code/Magento/Newsletter/Test/Mftf/Test/VerifyTinyMCEv4IsNativeWYSIWYGOnNewsletterTest.xml +++ b/app/code/Magento/Newsletter/Test/Mftf/Test/VerifyTinyMCEv4IsNativeWYSIWYGOnNewsletterTest.xml @@ -18,7 +18,7 @@ <testCaseId value="MAGETWO-84683"/> </annotations> <before> - <actionGroup ref="LoginActionGroup" stepKey="loginGetFromGeneralFile"/> + <actionGroup ref="AdminLoginActionGroup" stepKey="loginGetFromGeneralFile"/> <actionGroup ref="EnabledWYSIWYGActionGroup" stepKey="enableWYSIWYG"/> <actionGroup ref="SwitchToVersion4ActionGroup" stepKey="switchToTinyMCE4" /> </before> @@ -49,7 +49,7 @@ <closeTab stepKey="closeTab"/> <after> <actionGroup ref="DisabledWYSIWYGActionGroup" stepKey="disableWYSIWYG"/> - <actionGroup ref="logout" stepKey="logout"/> + <actionGroup ref="AdminLogoutActionGroup" stepKey="logout"/> </after> </test> </tests> diff --git a/app/code/Magento/Newsletter/Test/Unit/Model/Plugin/CustomerPluginTest.php b/app/code/Magento/Newsletter/Test/Unit/Model/Plugin/CustomerPluginTest.php index 52b3df8cb8aa6..c8a846cb2a758 100644 --- a/app/code/Magento/Newsletter/Test/Unit/Model/Plugin/CustomerPluginTest.php +++ b/app/code/Magento/Newsletter/Test/Unit/Model/Plugin/CustomerPluginTest.php @@ -6,10 +6,10 @@ namespace Magento\Newsletter\Test\Unit\Model\Plugin; use Magento\Customer\Api\CustomerRepositoryInterface; +use Magento\Customer\Api\Data\CustomerExtensionInterface; use Magento\Customer\Api\Data\CustomerInterface; use Magento\Customer\Model\Config\Share; use Magento\Customer\Model\ResourceModel\CustomerRepository; -use Magento\Customer\Api\Data\CustomerExtensionInterface; use Magento\Framework\Api\ExtensionAttributesFactory; use Magento\Framework\TestFramework\Unit\Helper\ObjectManager; use Magento\Newsletter\Model\Plugin\CustomerPlugin; @@ -124,13 +124,23 @@ public function testAfterSave(?int $originalStatus, ?bool $newValue, ?bool $expe ->method('loadByCustomer') ->with($customerId, $websiteId) ->willReturnSelf(); - $subscriber->expects($this->once()) - ->method('loadBySubscriberEmail') - ->with($customerEmail, $websiteId) - ->willReturnSelf(); + if ($originalStatus !== null && $originalStatus === Subscriber::STATUS_UNCONFIRMED) { + $subscriber->method('getId')->willReturn(1); + } else { + $subscriber->expects($this->once()) + ->method('loadBySubscriberEmail') + ->with($customerEmail, $websiteId) + ->willReturnSelf(); + } $this->subscriberFactory->method('create')->willReturn($subscriber); - $customerExtension = $this->createPartialMock(CustomerExtensionInterface::class, ['getIsSubscribed']); + $customerExtension = $this->createPartialMock( + CustomerExtensionInterface::class, + [ + 'getIsSubscribed', + 'CustomerExtensionInterface' + ] + ); $customerExtension->method('getIsSubscribed')->willReturn($newValue); /** @var CustomerInterface|MockObject $customer */ $customer = $this->createMock(CustomerInterface::class); @@ -148,7 +158,14 @@ public function testAfterSave(?int $originalStatus, ?bool $newValue, ?bool $expe $this->subscriptionManager->expects($this->never())->method('subscribeCustomer'); $this->subscriptionManager->expects($this->never())->method('unsubscribeCustomer'); } - $resultExtension = $this->createPartialMock(CustomerExtensionInterface::class, ['setIsSubscribed']); + $resultExtension = $this->createPartialMock( + CustomerExtensionInterface::class, + [ + 'setIsSubscribed', + 'getIsSubscribed', + 'CustomerExtensionInterface' + ] + ); $resultExtension->expects($this->once())->method('setIsSubscribed')->with($resultIsSubscribed); /** @var CustomerInterface|MockObject $result */ $result = $this->createMock(CustomerInterface::class); @@ -162,23 +179,25 @@ public function testAfterSave(?int $originalStatus, ?bool $newValue, ?bool $expe } /** + * Data provider for testAfterSave() + * * @return array */ - public function afterSaveDataProvider() + public function afterSaveDataProvider(): array { return [ - [null, null, null], - [null, true, true], - [null, false, null], - [Subscriber::STATUS_SUBSCRIBED, null, null], - [Subscriber::STATUS_SUBSCRIBED, true, null], - [Subscriber::STATUS_SUBSCRIBED, false, false], - [Subscriber::STATUS_UNSUBSCRIBED, null, null], - [Subscriber::STATUS_UNSUBSCRIBED, true, true], - [Subscriber::STATUS_UNSUBSCRIBED, false, null], - [Subscriber::STATUS_UNCONFIRMED, null, true], - [Subscriber::STATUS_UNCONFIRMED, true, true], - [Subscriber::STATUS_UNCONFIRMED, false, true], + 'missing_previous_and_new_status' => [null, null, null], + 'missing_previous_status_and_subscribe' => [null, true, true], + 'new_unsubscribed_value_and_missing_previous_status' => [null, false, null], + 'previous_subscribed_status_without_new_value' => [Subscriber::STATUS_SUBSCRIBED, null, null], + 'same_subscribed_previous_and_new_status' => [Subscriber::STATUS_SUBSCRIBED, true, null], + 'unsubscribe_previously_subscribed_customer' => [Subscriber::STATUS_SUBSCRIBED, false, false], + 'previously_unsubscribed_status_without_new_value' => [Subscriber::STATUS_UNSUBSCRIBED, null, null], + 'subscribe_previously_unsubscribed_customer' => [Subscriber::STATUS_UNSUBSCRIBED, true, true], + 'same_unsubscribed_previous_and_new_status' => [Subscriber::STATUS_UNSUBSCRIBED, false, null], + 'previous_unconfirmed_status_without_new_value' => [Subscriber::STATUS_UNCONFIRMED, null, true], + 'subscribe_previously_unconfirmed_status' => [Subscriber::STATUS_UNCONFIRMED, true, true], + 'unsubscribe_previously_unconfirmed_status' => [Subscriber::STATUS_UNCONFIRMED, false, true], ]; } @@ -285,7 +304,7 @@ public function testAfterGetByIdCreatesExtensionAttributes(): void $customerExtension = $this->createPartialMock( CustomerExtensionInterface::class, - ['getIsSubscribed', 'setIsSubscribed'] + ['getIsSubscribed', 'setIsSubscribed','CustomerExtensionInterface'] ); $customerExtension->expects($this->once())->method('setIsSubscribed')->with($subscribed); $this->extensionFactory->expects($this->once())->method('create')->willReturn($customerExtension); diff --git a/app/code/Magento/Newsletter/Test/Unit/Model/TemplateTest.php b/app/code/Magento/Newsletter/Test/Unit/Model/TemplateTest.php index b53ac39f0e4e2..eb55d365cafa2 100644 --- a/app/code/Magento/Newsletter/Test/Unit/Model/TemplateTest.php +++ b/app/code/Magento/Newsletter/Test/Unit/Model/TemplateTest.php @@ -268,7 +268,7 @@ class_exists(\Magento\Newsletter\Model\Template\Filter::class, true); ) ->disableOriginalConstructor() ->getMock(); - $filterTemplate->expects($this->once()) + $filterTemplate->expects($this->never()) ->method('setUseSessionInUrl') ->with(false) ->will($this->returnSelf()); diff --git a/app/code/Magento/Newsletter/etc/adminhtml/system.xml b/app/code/Magento/Newsletter/etc/adminhtml/system.xml index 16af7b2158dde..01dc4777f5d31 100644 --- a/app/code/Magento/Newsletter/etc/adminhtml/system.xml +++ b/app/code/Magento/Newsletter/etc/adminhtml/system.xml @@ -20,6 +20,9 @@ </group> <group id="subscription" translate="label" type="text" sortOrder="10" showInDefault="1" showInWebsite="1" showInStore="1"> <label>Subscription Options</label> + <depends> + <field id="*/general/active">1</field> + </depends> <field id="allow_guest_subscribe" translate="label" type="select" sortOrder="10" showInDefault="1" showInWebsite="1" showInStore="1" canRestore="1"> <label>Allow Guest Subscription</label> <source_model>Magento\Config\Model\Config\Source\Yesno</source_model> diff --git a/app/code/Magento/Newsletter/etc/extension_attributes.xml b/app/code/Magento/Newsletter/etc/extension_attributes.xml index 50f46af5c033b..09925024e97d5 100644 --- a/app/code/Magento/Newsletter/etc/extension_attributes.xml +++ b/app/code/Magento/Newsletter/etc/extension_attributes.xml @@ -8,6 +8,10 @@ <config xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="urn:magento:framework:Api/etc/extension_attributes.xsd"> <extension_attributes for="Magento\Customer\Api\Data\CustomerInterface"> - <attribute code="is_subscribed" type="boolean" /> + <attribute code="is_subscribed" type="boolean" > + <join reference_table="newsletter_subscriber" reference_field="customer_id" join_on_field="entity_id"> + <field>subscriber_status</field> + </join> + </attribute> </extension_attributes> </config> diff --git a/app/code/Magento/Newsletter/etc/frontend/di.xml b/app/code/Magento/Newsletter/etc/frontend/di.xml index eef123a3304e3..99e90faff4034 100644 --- a/app/code/Magento/Newsletter/etc/frontend/di.xml +++ b/app/code/Magento/Newsletter/etc/frontend/di.xml @@ -13,4 +13,16 @@ </argument> </arguments> </type> + <type name="Magento\Framework\View\Element\Message\MessageConfigurationsPool"> + <arguments> + <argument name="configurationsMap" xsi:type="array"> + <item name="localizedSubscriptionErrorMessage" xsi:type="array"> + <item name="renderer" xsi:type="const">\Magento\Framework\View\Element\Message\Renderer\BlockRenderer::CODE</item> + <item name="data" xsi:type="array"> + <item name="template" xsi:type="string">Magento_Newsletter::messages/localizedSubscriptionErrorMessage.phtml</item> + </item> + </item> + </argument> + </arguments> + </type> </config> diff --git a/app/code/Magento/Newsletter/registration.php b/app/code/Magento/Newsletter/registration.php index 421b594ad6b88..b1a5714ff8a4d 100644 --- a/app/code/Magento/Newsletter/registration.php +++ b/app/code/Magento/Newsletter/registration.php @@ -4,6 +4,6 @@ * See COPYING.txt for license details. */ -use \Magento\Framework\Component\ComponentRegistrar; +use Magento\Framework\Component\ComponentRegistrar; ComponentRegistrar::register(ComponentRegistrar::MODULE, 'Magento_Newsletter', __DIR__); diff --git a/app/code/Magento/Newsletter/view/frontend/layout/customer_account_create.xml b/app/code/Magento/Newsletter/view/frontend/layout/customer_account_create.xml new file mode 100644 index 0000000000000..98c6daa62474a --- /dev/null +++ b/app/code/Magento/Newsletter/view/frontend/layout/customer_account_create.xml @@ -0,0 +1,14 @@ +<?xml version="1.0"?> +<!-- +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +--> +<page xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" layout="1column" xsi:noNamespaceSchemaLocation="urn:magento:framework:View/Layout/etc/page_configuration.xsd"> + <body> + <referenceBlock name="customer_form_register"> + <block name="customer.form.register.newsletter" template="Magento_Newsletter::form/register/newsletter.phtml"/> + </referenceBlock> + </body> +</page> diff --git a/app/code/Magento/Newsletter/view/frontend/requirejs-config.js b/app/code/Magento/Newsletter/view/frontend/requirejs-config.js new file mode 100644 index 0000000000000..a63d47a0cf732 --- /dev/null +++ b/app/code/Magento/Newsletter/view/frontend/requirejs-config.js @@ -0,0 +1,13 @@ +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ + +var config = { + map: { + '*': { + subscriptionStatusResolver: 'Magento_Newsletter/js/subscription-status-resolver', + newsletterSignUp: 'Magento_Newsletter/js/newsletter-sign-up' + } + } +}; diff --git a/app/code/Magento/Newsletter/view/frontend/templates/form/register/newsletter.phtml b/app/code/Magento/Newsletter/view/frontend/templates/form/register/newsletter.phtml new file mode 100644 index 0000000000000..3034b6550e9a6 --- /dev/null +++ b/app/code/Magento/Newsletter/view/frontend/templates/form/register/newsletter.phtml @@ -0,0 +1,16 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +?> +<script type="text/x-magento-init"> +{ + "#email_address": { + "newsletterSignUp": { + "signUpElement": "#is_subscribed", + "submitButton": ".form-create-account button[type='submit']" + } + } +} +</script> diff --git a/app/code/Magento/Newsletter/view/frontend/templates/messages/localizedSubscriptionErrorMessage.phtml b/app/code/Magento/Newsletter/view/frontend/templates/messages/localizedSubscriptionErrorMessage.phtml new file mode 100644 index 0000000000000..46c35002e3995 --- /dev/null +++ b/app/code/Magento/Newsletter/view/frontend/templates/messages/localizedSubscriptionErrorMessage.phtml @@ -0,0 +1,9 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ + +/** @var \Magento\Framework\View\Element\Template $block */ +?> +<?= $block->escapeHtml(__($block->getData('message')), ['a']); ?> diff --git a/app/code/Magento/Newsletter/view/frontend/web/js/newsletter-sign-up.js b/app/code/Magento/Newsletter/view/frontend/web/js/newsletter-sign-up.js new file mode 100644 index 0000000000000..fa1d01ed2ed4e --- /dev/null +++ b/app/code/Magento/Newsletter/view/frontend/web/js/newsletter-sign-up.js @@ -0,0 +1,63 @@ +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ + +define([ + 'jquery', + 'uiElement', + 'mage/url', + 'subscriptionStatusResolver', + 'mage/validation' +], function ($, Component, urlBuilder, subscriptionStatusResolver) { + 'use strict'; + + return Component.extend({ + + defaults: { + signUpElement: '', + submitButton: '', + element: null + }, + + /** @inheritdoc */ + initialize: function (config, element) { + this._super(); + this.element = element; + $(element).on('change', $.proxy(this.updateSignUpStatus, this)); + this.updateSignUpStatus(); + }, + + /** + * Send status request and update subscription element according to result. + */ + updateSignUpStatus: function () { + var element = $(this.element), + email = element.val(), + self = this, + newsletterSubscription; + + if ($(self.signUpElement).is(':checked')) { + return; + } + + if (!email || !$.validator.methods['validate-email'].call(this, email, element)) { + return; + } + + newsletterSubscription = $.Deferred(); + + $(self.submitButton).prop('disabled', true); + + subscriptionStatusResolver(email, newsletterSubscription); + + $.when(newsletterSubscription).done(function (isSubscribed) { + if (isSubscribed) { + $(self.signUpElement).prop('checked', true); + } + }).always(function () { + $(self.submitButton).prop('disabled', false); + }); + } + }); +}); diff --git a/app/code/Magento/Newsletter/view/frontend/web/js/subscription-status-resolver.js b/app/code/Magento/Newsletter/view/frontend/web/js/subscription-status-resolver.js new file mode 100644 index 0000000000000..13a4fc3e45f1c --- /dev/null +++ b/app/code/Magento/Newsletter/view/frontend/web/js/subscription-status-resolver.js @@ -0,0 +1,28 @@ +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ + +define([ + 'jquery', + 'mage/url' +], function ($, urlBuilder) { + 'use strict'; + + return function (email, deferred) { + return $.getJSON( + urlBuilder.build('newsletter/ajax/status'), + { + email: email + } + ).done(function (response) { + if (response.errors) { + deferred.reject(); + } else { + deferred.resolve(response.subscribed); + } + }).fail(function () { + deferred.reject(); + }); + }; +}); diff --git a/app/code/Magento/OfflinePayments/Observer/BeforeOrderPaymentSaveObserver.php b/app/code/Magento/OfflinePayments/Observer/BeforeOrderPaymentSaveObserver.php index daf1cef3fff60..234bec96d46d1 100644 --- a/app/code/Magento/OfflinePayments/Observer/BeforeOrderPaymentSaveObserver.php +++ b/app/code/Magento/OfflinePayments/Observer/BeforeOrderPaymentSaveObserver.php @@ -3,9 +3,11 @@ * Copyright © Magento, Inc. All rights reserved. * See COPYING.txt for license details. */ +declare(strict_types=1); namespace Magento\OfflinePayments\Observer; +use Magento\Framework\Event\Observer; use Magento\Framework\Event\ObserverInterface; use Magento\OfflinePayments\Model\Banktransfer; use Magento\OfflinePayments\Model\Cashondelivery; @@ -19,10 +21,10 @@ class BeforeOrderPaymentSaveObserver implements ObserverInterface /** * Sets current instructions for bank transfer account * - * @param \Magento\Framework\Event\Observer $observer + * @param Observer $observer * @return void */ - public function execute(\Magento\Framework\Event\Observer $observer) + public function execute(Observer $observer) { /** @var \Magento\Sales\Model\Order\Payment $payment */ $payment = $observer->getEvent()->getPayment(); @@ -30,18 +32,26 @@ public function execute(\Magento\Framework\Event\Observer $observer) Banktransfer::PAYMENT_METHOD_BANKTRANSFER_CODE, Cashondelivery::PAYMENT_METHOD_CASHONDELIVERY_CODE ]; - if (in_array($payment->getMethod(), $instructionMethods)) { + if (in_array($payment->getMethod(), $instructionMethods) + && empty($payment->getAdditionalInformation('instructions'))) { $payment->setAdditionalInformation( 'instructions', - $payment->getMethodInstance()->getInstructions() + $payment->getMethodInstance()->getConfigData( + 'instructions', + $payment->getOrder()->getStoreId() + ) ); } elseif ($payment->getMethod() === Checkmo::PAYMENT_METHOD_CHECKMO_CODE) { $methodInstance = $payment->getMethodInstance(); - if (!empty($methodInstance->getPayableTo())) { - $payment->setAdditionalInformation('payable_to', $methodInstance->getPayableTo()); + $storeId = $payment->getOrder()->getStoreId(); + + $payableTo = $methodInstance->getConfigData('payable_to', $storeId); + if (!empty($payableTo)) { + $payment->setAdditionalInformation('payable_to', $payableTo); } - if (!empty($methodInstance->getMailingAddress())) { - $payment->setAdditionalInformation('mailing_address', $methodInstance->getMailingAddress()); + $mailingAddress = $methodInstance->getConfigData('mailing_address', $storeId); + if (!empty($mailingAddress)) { + $payment->setAdditionalInformation('mailing_address', $mailingAddress); } } } diff --git a/app/code/Magento/OfflinePayments/Test/Unit/Block/Form/AbstractInstructionTest.php b/app/code/Magento/OfflinePayments/Test/Unit/Block/Form/AbstractInstructionTest.php index bf77d1975ed53..e0dc15c66b284 100644 --- a/app/code/Magento/OfflinePayments/Test/Unit/Block/Form/AbstractInstructionTest.php +++ b/app/code/Magento/OfflinePayments/Test/Unit/Block/Form/AbstractInstructionTest.php @@ -3,34 +3,41 @@ * Copyright © Magento, Inc. All rights reserved. * See COPYING.txt for license details. */ +declare(strict_types=1); + namespace Magento\OfflinePayments\Test\Unit\Block\Form; -class AbstractInstructionTest extends \PHPUnit\Framework\TestCase +use Magento\Framework\View\Element\Template\Context; +use Magento\OfflinePayments\Block\Form\AbstractInstruction; +use Magento\Payment\Model\MethodInterface; +use PHPUnit\Framework\TestCase; + +class AbstractInstructionTest extends TestCase { /** - * @var \Magento\OfflinePayments\Block\Form\AbstractInstruction + * @var AbstractInstruction */ - protected $_model; + protected $model; - protected function setUp() + protected function setUp(): void { - $context = $this->createMock(\Magento\Framework\View\Element\Template\Context::class); - $this->_model = $this->getMockForAbstractClass( - \Magento\OfflinePayments\Block\Form\AbstractInstruction::class, + $context = $this->createMock(Context::class); + $this->model = $this->getMockForAbstractClass( + AbstractInstruction::class, ['context' => $context] ); } public function testGetInstructions() { - $method = $this->getMockBuilder(\Magento\Payment\Model\MethodInterface::class) + $method = $this->getMockBuilder(MethodInterface::class) ->getMockForAbstractClass(); $method->expects($this->once()) ->method('getConfigData') ->willReturn('instructions'); - $this->_model->setData('method', $method); + $this->model->setData('method', $method); - $this->assertEquals('instructions', $this->_model->getInstructions()); + $this->assertEquals('instructions', $this->model->getInstructions()); } } diff --git a/app/code/Magento/OfflinePayments/Test/Unit/Block/Info/CheckmoTest.php b/app/code/Magento/OfflinePayments/Test/Unit/Block/Info/CheckmoTest.php index 27705c242a012..cf0491167bd28 100644 --- a/app/code/Magento/OfflinePayments/Test/Unit/Block/Info/CheckmoTest.php +++ b/app/code/Magento/OfflinePayments/Test/Unit/Block/Info/CheckmoTest.php @@ -3,22 +3,25 @@ * Copyright © Magento, Inc. All rights reserved. * See COPYING.txt for license details. */ +declare(strict_types=1); + namespace Magento\OfflinePayments\Test\Unit\Block\Info; use Magento\Framework\View\Element\Template\Context; use Magento\OfflinePayments\Block\Info\Checkmo; use Magento\Payment\Model\Info; -use PHPUnit_Framework_MockObject_MockObject as MockObject; +use PHPUnit\Framework\MockObject\MockObject; +use PHPUnit\Framework\TestCase; /** * CheckmoTest contains list of test for block methods testing */ -class CheckmoTest extends \PHPUnit\Framework\TestCase +class CheckmoTest extends TestCase { /** * @var Info|MockObject */ - private $info; + private $infoMock; /** * @var Checkmo @@ -28,14 +31,14 @@ class CheckmoTest extends \PHPUnit\Framework\TestCase /** * @inheritdoc */ - protected function setUp() + protected function setUp(): void { $context = $this->getMockBuilder(Context::class) ->disableOriginalConstructor() ->setMethods([]) ->getMock(); - $this->info = $this->getMockBuilder(Info::class) + $this->infoMock = $this->getMockBuilder(Info::class) ->disableOriginalConstructor() ->setMethods(['getAdditionalInformation']) ->getMock(); @@ -51,11 +54,11 @@ protected function setUp() */ public function testGetPayableTo($details, $expected) { - $this->info->expects(static::at(0)) + $this->infoMock->expects(static::at(0)) ->method('getAdditionalInformation') ->with('payable_to') ->willReturn($details); - $this->block->setData('info', $this->info); + $this->block->setData('info', $this->infoMock); static::assertEquals($expected, $this->block->getPayableTo()); } @@ -80,11 +83,11 @@ public function getPayableToDataProvider() */ public function testGetMailingAddress($details, $expected) { - $this->info->expects(static::at(1)) + $this->infoMock->expects(static::at(1)) ->method('getAdditionalInformation') ->with('mailing_address') ->willReturn($details); - $this->block->setData('info', $this->info); + $this->block->setData('info', $this->infoMock); static::assertEquals($expected, $this->block->getMailingAddress()); } @@ -107,11 +110,11 @@ public function getMailingAddressDataProvider() public function testConvertAdditionalDataIsNeverCalled() { $mailingAddress = 'blah@blah.com'; - $this->info->expects(static::at(1)) + $this->infoMock->expects(static::at(1)) ->method('getAdditionalInformation') ->with('mailing_address') ->willReturn($mailingAddress); - $this->block->setData('info', $this->info); + $this->block->setData('info', $this->infoMock); // First we set the property $this->_mailingAddress $this->block->getMailingAddress(); diff --git a/app/code/Magento/OfflinePayments/Test/Unit/Model/BanktransferTest.php b/app/code/Magento/OfflinePayments/Test/Unit/Model/BanktransferTest.php index 873cf4f217857..f92ca76da43dd 100644 --- a/app/code/Magento/OfflinePayments/Test/Unit/Model/BanktransferTest.php +++ b/app/code/Magento/OfflinePayments/Test/Unit/Model/BanktransferTest.php @@ -3,38 +3,49 @@ * Copyright © Magento, Inc. All rights reserved. * See COPYING.txt for license details. */ +declare(strict_types=1); + namespace Magento\OfflinePayments\Test\Unit\Model; -class BanktransferTest extends \PHPUnit\Framework\TestCase +use Magento\Framework\App\Config\ScopeConfigInterface; +use Magento\Framework\Event\ManagerInterface; +use Magento\Framework\TestFramework\Unit\Helper\ObjectManager; +use Magento\OfflinePayments\Model\Banktransfer; +use Magento\Payment\Block\Info\Instructions; +use Magento\Payment\Helper\Data; +use PHPUnit\Framework\MockObject\MockObject; +use PHPUnit\Framework\TestCase; + +class BanktransferTest extends TestCase { /** - * @var \Magento\OfflinePayments\Model\Banktransfer + * @var Banktransfer */ - protected $_object; + private $object; /** - * @var \PHPUnit_Framework_MockObject_MockObject + * @var ScopeConfigInterface|MockObject */ - protected $_scopeConfig; + private $scopeConfigMock; - protected function setUp() + protected function setUp(): void { - $objectManagerHelper = new \Magento\Framework\TestFramework\Unit\Helper\ObjectManager($this); - $eventManager = $this->createMock(\Magento\Framework\Event\ManagerInterface::class); - $paymentDataMock = $this->createMock(\Magento\Payment\Helper\Data::class); - $this->_scopeConfig = $this->createMock(\Magento\Framework\App\Config\ScopeConfigInterface::class); - $this->_object = $objectManagerHelper->getObject( - \Magento\OfflinePayments\Model\Banktransfer::class, + $objectManagerHelper = new ObjectManager($this); + $eventManager = $this->createMock(ManagerInterface::class); + $paymentDataMock = $this->createMock(Data::class); + $this->scopeConfigMock = $this->createMock(ScopeConfigInterface::class); + $this->object = $objectManagerHelper->getObject( + Banktransfer::class, [ 'eventManager' => $eventManager, 'paymentData' => $paymentDataMock, - 'scopeConfig' => $this->_scopeConfig, + 'scopeConfig' => $this->scopeConfigMock, ] ); } public function testGetInfoBlockType() { - $this->assertEquals(\Magento\Payment\Block\Info\Instructions::class, $this->_object->getInfoBlockType()); + $this->assertEquals(Instructions::class, $this->object->getInfoBlockType()); } } diff --git a/app/code/Magento/OfflinePayments/Test/Unit/Model/CashondeliveryTest.php b/app/code/Magento/OfflinePayments/Test/Unit/Model/CashondeliveryTest.php index 271bb41967255..afe831b3e0a50 100644 --- a/app/code/Magento/OfflinePayments/Test/Unit/Model/CashondeliveryTest.php +++ b/app/code/Magento/OfflinePayments/Test/Unit/Model/CashondeliveryTest.php @@ -3,40 +3,51 @@ * Copyright © Magento, Inc. All rights reserved. * See COPYING.txt for license details. */ +declare(strict_types=1); + namespace Magento\OfflinePayments\Test\Unit\Model; -class CashondeliveryTest extends \PHPUnit\Framework\TestCase +use Magento\Framework\App\Config\ScopeConfigInterface; +use Magento\Framework\Event\ManagerInterface; +use Magento\Framework\TestFramework\Unit\Helper\ObjectManager; +use Magento\OfflinePayments\Model\Cashondelivery; +use Magento\Payment\Block\Info\Instructions; +use Magento\Payment\Helper\Data; +use PHPUnit\Framework\MockObject\MockObject; +use PHPUnit\Framework\TestCase; + +class CashondeliveryTest extends TestCase { /** - * @var \Magento\OfflinePayments\Model\Cashondelivery + * @var Cashondelivery */ - protected $_object; + private $object; /** - * @var \PHPUnit_Framework_MockObject_MockObject + * @var ScopeConfigInterface|MockObject */ - protected $_scopeConfig; + private $scopeConfigMock; - protected function setUp() + protected function setUp(): void { - $helper = new \Magento\Framework\TestFramework\Unit\Helper\ObjectManager($this); + $helper = new ObjectManager($this); - $eventManager = $this->createMock(\Magento\Framework\Event\ManagerInterface::class); - $paymentDataMock = $this->createMock(\Magento\Payment\Helper\Data::class); + $eventManager = $this->createMock(ManagerInterface::class); + $paymentDataMock = $this->createMock(Data::class); - $this->_scopeConfig = $this->createMock(\Magento\Framework\App\Config\ScopeConfigInterface::class); - $this->_object = $helper->getObject( - \Magento\OfflinePayments\Model\Cashondelivery::class, + $this->scopeConfigMock = $this->createMock(ScopeConfigInterface::class); + $this->object = $helper->getObject( + Cashondelivery::class, [ 'eventManager' => $eventManager, 'paymentData' => $paymentDataMock, - 'scopeConfig' => $this->_scopeConfig, + 'scopeConfig' => $this->scopeConfigMock, ] ); } public function testGetInfoBlockType() { - $this->assertEquals(\Magento\Payment\Block\Info\Instructions::class, $this->_object->getInfoBlockType()); + $this->assertEquals(Instructions::class, $this->object->getInfoBlockType()); } } diff --git a/app/code/Magento/OfflinePayments/Test/Unit/Model/CheckmoConfigProviderTest.php b/app/code/Magento/OfflinePayments/Test/Unit/Model/CheckmoConfigProviderTest.php index 8d65146ec102b..dd359b979283a 100644 --- a/app/code/Magento/OfflinePayments/Test/Unit/Model/CheckmoConfigProviderTest.php +++ b/app/code/Magento/OfflinePayments/Test/Unit/Model/CheckmoConfigProviderTest.php @@ -3,34 +3,46 @@ * Copyright © Magento, Inc. All rights reserved. * See COPYING.txt for license details. */ +declare(strict_types=1); + namespace Magento\OfflinePayments\Test\Unit\Model; -use Magento\OfflinePayments\Model\CheckmoConfigProvider; -use Magento\OfflinePayments\Model\Checkmo; use Magento\Framework\Escaper; +use Magento\OfflinePayments\Model\Checkmo; +use Magento\OfflinePayments\Model\CheckmoConfigProvider; +use Magento\Payment\Helper\Data as PaymentHelper; +use PHPUnit\Framework\MockObject\MockObject; +use PHPUnit\Framework\TestCase; -class CheckmoConfigProviderTest extends \PHPUnit\Framework\TestCase +class CheckmoConfigProviderTest extends TestCase { - /** @var CheckmoConfigProvider */ - protected $model; + /** + * @var CheckmoConfigProvider + */ + private $model; - /** @var Checkmo|\PHPUnit_Framework_MockObject_MockObject */ - protected $methodMock; + /** + * @var Checkmo|MockObject + */ + private $methodMock; - /** @var Escaper|\PHPUnit_Framework_MockObject_MockObject */ - protected $escaperMock; + /** + * @var Escaper|MockObject + */ + private $escaperMock; - protected function setUp() + protected function setUp(): void { - $this->methodMock = $this->createMock(\Magento\OfflinePayments\Model\Checkmo::class); + $this->methodMock = $this->createMock(Checkmo::class); - $paymentHelperMock = $this->createMock(\Magento\Payment\Helper\Data::class); + /** @var PaymentHelper|MockObject $paymentHelperMock */ + $paymentHelperMock = $this->createMock(PaymentHelper::class); $paymentHelperMock->expects($this->once()) ->method('getMethodInstance') ->with(Checkmo::PAYMENT_METHOD_CHECKMO_CODE) ->willReturn($this->methodMock); - $this->escaperMock = $this->createMock(\Magento\Framework\Escaper::class); + $this->escaperMock = $this->createMock(Escaper::class); $this->escaperMock->expects($this->any()) ->method('escapeHtml') ->willReturnArgument(0); diff --git a/app/code/Magento/OfflinePayments/Test/Unit/Model/CheckmoTest.php b/app/code/Magento/OfflinePayments/Test/Unit/Model/CheckmoTest.php index a636bee744b6b..d7eae294511fd 100644 --- a/app/code/Magento/OfflinePayments/Test/Unit/Model/CheckmoTest.php +++ b/app/code/Magento/OfflinePayments/Test/Unit/Model/CheckmoTest.php @@ -3,56 +3,63 @@ * Copyright © Magento, Inc. All rights reserved. * See COPYING.txt for license details. */ +declare(strict_types=1); + namespace Magento\OfflinePayments\Test\Unit\Model; -class CheckmoTest extends \PHPUnit\Framework\TestCase +use Magento\Framework\App\Config\ScopeConfigInterface; +use Magento\Framework\Event\ManagerInterface; +use Magento\Framework\TestFramework\Unit\Helper\ObjectManager; +use Magento\OfflinePayments\Model\Checkmo; +use Magento\Payment\Helper\Data; +use PHPUnit\Framework\MockObject\MockObject; +use PHPUnit\Framework\TestCase; + +class CheckmoTest extends TestCase { /** - * @var \Magento\OfflinePayments\Model\Checkmo + * @var Checkmo */ - protected $_object; + private $object; /** - * @var \PHPUnit_Framework_MockObject_MockObject + * @var ScopeConfigInterface|MockObject */ - protected $_scopeConfig; + private $scopeConfigMock; - protected function setUp() + protected function setUp(): void { - $objectManagerHelper = new \Magento\Framework\TestFramework\Unit\Helper\ObjectManager($this); - $eventManager = $this->createMock(\Magento\Framework\Event\ManagerInterface::class); - $paymentDataMock = $this->createMock(\Magento\Payment\Helper\Data::class); - $this->_scopeConfig = $this->createPartialMock( - \Magento\Framework\App\Config\ScopeConfigInterface::class, - ['getValue', 'isSetFlag'] - ); - $this->_object = $objectManagerHelper->getObject( - \Magento\OfflinePayments\Model\Checkmo::class, + $objectManagerHelper = new ObjectManager($this); + $eventManager = $this->createMock(ManagerInterface::class); + $paymentDataMock = $this->createMock(Data::class); + $this->scopeConfigMock = $this->createPartialMock(ScopeConfigInterface::class, ['getValue', 'isSetFlag']); + $this->object = $objectManagerHelper->getObject( + Checkmo::class, [ 'eventManager' => $eventManager, 'paymentData' => $paymentDataMock, - 'scopeConfig' => $this->_scopeConfig, + 'scopeConfig' => $this->scopeConfigMock, ] ); } public function testGetPayableTo() { - $this->_object->setStore(1); - $this->_scopeConfig->expects($this->once()) + $this->object->setStore(1); + $this->scopeConfigMock->expects($this->once()) ->method('getValue') ->with('payment/checkmo/payable_to', 'store', 1) ->willReturn('payable'); - $this->assertEquals('payable', $this->_object->getPayableTo()); + $this->assertEquals('payable', $this->object->getPayableTo()); } public function testGetMailingAddress() { - $this->_object->setStore(1); - $this->_scopeConfig->expects($this->once()) + $this->object->setStore(1); + $this->scopeConfigMock->expects($this->once()) ->method('getValue') ->with('payment/checkmo/mailing_address', 'store', 1) ->willReturn('blah@blah.com'); - $this->assertEquals('blah@blah.com', $this->_object->getMailingAddress()); + $this->assertEquals('blah@blah.com', $this->object->getMailingAddress()); } } diff --git a/app/code/Magento/OfflinePayments/Test/Unit/Model/InstructionsConfigProviderTest.php b/app/code/Magento/OfflinePayments/Test/Unit/Model/InstructionsConfigProviderTest.php index 97a64d8ab59b9..821ad6e7555c5 100644 --- a/app/code/Magento/OfflinePayments/Test/Unit/Model/InstructionsConfigProviderTest.php +++ b/app/code/Magento/OfflinePayments/Test/Unit/Model/InstructionsConfigProviderTest.php @@ -3,40 +3,54 @@ * Copyright © Magento, Inc. All rights reserved. * See COPYING.txt for license details. */ +declare(strict_types=1); + namespace Magento\OfflinePayments\Test\Unit\Model; use Magento\Framework\Escaper; use Magento\OfflinePayments\Model\Banktransfer; use Magento\OfflinePayments\Model\Cashondelivery; use Magento\OfflinePayments\Model\InstructionsConfigProvider; +use Magento\Payment\Helper\Data as PaymentHelper; use Magento\Payment\Model\Method\AbstractMethod; +use PHPUnit\Framework\MockObject\MockObject; +use PHPUnit\Framework\TestCase; -class InstructionsConfigProviderTest extends \PHPUnit\Framework\TestCase +class InstructionsConfigProviderTest extends TestCase { - /** @var InstructionsConfigProvider */ - protected $model; + /** + * @var InstructionsConfigProvider + */ + private $model; - /** @var AbstractMethod|\PHPUnit_Framework_MockObject_MockObject */ - protected $methodOneMock; + /** + * @var AbstractMethod|MockObject + */ + private $methodOneMock; - /** @var AbstractMethod|\PHPUnit_Framework_MockObject_MockObject */ - protected $methodTwoMock; + /** + * @var AbstractMethod|MockObject + */ + private $methodTwoMock; - /** @var Escaper|\PHPUnit_Framework_MockObject_MockObject */ - protected $escaperMock; + /** + * @var Escaper|MockObject + */ + private $escaperMock; - protected function setUp() + protected function setUp(): void { $this->methodOneMock = $this->createPartialMock( - \Magento\Payment\Model\Method\AbstractMethod::class, + AbstractMethod::class, ['isAvailable', 'getInstructions'] ); $this->methodTwoMock = $this->createPartialMock( - \Magento\Payment\Model\Method\AbstractMethod::class, + AbstractMethod::class, ['isAvailable', 'getInstructions'] ); - $paymentHelperMock = $this->createMock(\Magento\Payment\Helper\Data::class); + /** @var PaymentHelper|MockObject $paymentHelperMock */ + $paymentHelperMock = $this->createMock(PaymentHelper::class); $paymentHelperMock->expects($this->exactly(2)) ->method('getMethodInstance') ->willReturnMap([ @@ -44,7 +58,7 @@ protected function setUp() [Cashondelivery::PAYMENT_METHOD_CASHONDELIVERY_CODE, $this->methodTwoMock], ]); - $this->escaperMock = $this->createMock(\Magento\Framework\Escaper::class); + $this->escaperMock = $this->createMock(Escaper::class); $this->escaperMock->expects($this->any()) ->method('escapeHtml') ->willReturnArgument(0); diff --git a/app/code/Magento/OfflinePayments/Test/Unit/Model/PurchaseorderTest.php b/app/code/Magento/OfflinePayments/Test/Unit/Model/PurchaseorderTest.php index 2eb204651fcf4..d16721a743abd 100644 --- a/app/code/Magento/OfflinePayments/Test/Unit/Model/PurchaseorderTest.php +++ b/app/code/Magento/OfflinePayments/Test/Unit/Model/PurchaseorderTest.php @@ -3,11 +3,14 @@ * Copyright © Magento, Inc. All rights reserved. * See COPYING.txt for license details. */ +declare(strict_types=1); + namespace Magento\OfflinePayments\Test\Unit\Model; use Magento\Framework\App\Config\ScopeConfigInterface; use Magento\Framework\DataObject; use Magento\Framework\Event\ManagerInterface as EventManagerInterface; +use Magento\Framework\Exception\LocalizedException; use Magento\Framework\TestFramework\Unit\Helper\ObjectManager; use Magento\OfflinePayments\Model\Purchaseorder; use Magento\Payment\Helper\Data as PaymentHelper; @@ -15,34 +18,36 @@ use Magento\Sales\Api\Data\OrderAddressInterface; use Magento\Sales\Api\Data\OrderInterface; use Magento\Sales\Model\Order\Payment; +use PHPUnit\Framework\MockObject\MockObject; +use PHPUnit\Framework\TestCase; -class PurchaseorderTest extends \PHPUnit\Framework\TestCase +class PurchaseorderTest extends TestCase { /** * @var Purchaseorder */ - protected $_object; + private $object; /** - * @var \PHPUnit_Framework_MockObject_MockObject + * @var ScopeConfigInterface|MockObject */ - protected $_scopeConfig; + private $scopeConfigMock; - protected function setUp() + protected function setUp(): void { $objectManagerHelper = new ObjectManager($this); $eventManager = $this->createMock(EventManagerInterface::class); $paymentDataMock = $this->createMock(PaymentHelper::class); - $this->_scopeConfig = $this->createPartialMock( + $this->scopeConfigMock = $this->createPartialMock( ScopeConfigInterface::class, ['getValue', 'isSetFlag'] ); - $this->_object = $objectManagerHelper->getObject( + $this->object = $objectManagerHelper->getObject( Purchaseorder::class, [ 'eventManager' => $eventManager, 'paymentData' => $paymentDataMock, - 'scopeConfig' => $this->_scopeConfig, + 'scopeConfig' => $this->scopeConfigMock, ] ); } @@ -54,17 +59,16 @@ public function testAssignData() ]); $instance = $this->createMock(PaymentInfo::class); - $this->_object->setData('info_instance', $instance); - $result = $this->_object->assignData($data); - $this->assertEquals($result, $this->_object); + $this->object->setData('info_instance', $instance); + $result = $this->object->assignData($data); + $this->assertEquals($result, $this->object); } - /** - * @expectedException \Magento\Framework\Exception\LocalizedException - * @expectedExceptionMessage Purchase order number is a required field. - */ public function testValidate() { + $this->expectException(LocalizedException::class); + $this->expectExceptionMessage('Purchase order number is a required field.'); + $data = new DataObject([]); $addressMock = $this->createMock(OrderAddressInterface::class); @@ -77,9 +81,9 @@ public function testValidate() $instance->expects($this->once())->method('getOrder')->willReturn($orderMock); - $this->_object->setData('info_instance', $instance); - $this->_object->assignData($data); + $this->object->setData('info_instance', $instance); + $this->object->assignData($data); - $this->_object->validate(); + $this->object->validate(); } } diff --git a/app/code/Magento/OfflinePayments/Test/Unit/Observer/BeforeOrderPaymentSaveObserverTest.php b/app/code/Magento/OfflinePayments/Test/Unit/Observer/BeforeOrderPaymentSaveObserverTest.php index 51edaea0e659c..c7e7006b07c91 100644 --- a/app/code/Magento/OfflinePayments/Test/Unit/Observer/BeforeOrderPaymentSaveObserverTest.php +++ b/app/code/Magento/OfflinePayments/Test/Unit/Observer/BeforeOrderPaymentSaveObserverTest.php @@ -3,6 +3,8 @@ * Copyright © Magento, Inc. All rights reserved. * See COPYING.txt for license details. */ +declare(strict_types=1); + namespace Magento\OfflinePayments\Test\Unit\Observer; use Magento\Framework\Event; @@ -10,73 +12,95 @@ use Magento\Framework\TestFramework\Unit\Helper\ObjectManager; use Magento\OfflinePayments\Model\Banktransfer; use Magento\OfflinePayments\Model\Cashondelivery; +use Magento\OfflinePayments\Model\Checkmo; use Magento\OfflinePayments\Observer\BeforeOrderPaymentSaveObserver; +use Magento\Sales\Model\Order; use Magento\Sales\Model\Order\Payment; -use PHPUnit_Framework_MockObject_MockObject as MockObject; -use Magento\OfflinePayments\Model\Checkmo; +use PHPUnit\Framework\MockObject\MockObject; +use PHPUnit\Framework\TestCase; -class BeforeOrderPaymentSaveObserverTest extends \PHPUnit\Framework\TestCase +/** + * @covers \Magento\OfflinePayments\Observer\BeforeOrderPaymentSaveObserver + */ +class BeforeOrderPaymentSaveObserverTest extends TestCase { + private const STORE_ID = 1; + /** * @var BeforeOrderPaymentSaveObserver */ - protected $_model; + private $model; /** * @var Payment|MockObject */ - private $payment; + private $paymentMock; /** * @var Event|MockObject */ - private $event; + private $eventMock; /** * @var Observer|MockObject */ - private $observer; + private $observerMock; + + /** + * @var Order|MockObject + */ + private $orderMock; /** * @inheritdoc */ - protected function setUp() + protected function setUp(): void { $objectManagerHelper = new ObjectManager($this); - $this->payment = $this->getMockBuilder(Payment::class) + $this->paymentMock = $this->getMockBuilder(Payment::class) ->disableOriginalConstructor() ->getMock(); - $this->event = $this->getMockBuilder(Event::class) + $this->eventMock = $this->getMockBuilder(Event::class) ->disableOriginalConstructor() ->setMethods(['getPayment']) ->getMock(); - $this->event->expects(self::once()) + $this->eventMock->expects(self::once()) ->method('getPayment') - ->willReturn($this->payment); + ->willReturn($this->paymentMock); - $this->observer = $this->getMockBuilder(Observer::class) + $this->observerMock = $this->getMockBuilder(Observer::class) ->disableOriginalConstructor() ->getMock(); - $this->observer->expects(self::once()) + $this->observerMock->expects(self::once()) ->method('getEvent') - ->willReturn($this->event); + ->willReturn($this->eventMock); + + $this->orderMock = $this->getMockBuilder(Order::class) + ->disableOriginalConstructor() + ->getMock(); + $this->orderMock->method('getStoreId') + ->willReturn(static::STORE_ID); + + $this->paymentMock->method('getOrder') + ->willReturn($this->orderMock); - $this->_model = $objectManagerHelper->getObject(BeforeOrderPaymentSaveObserver::class); + $this->model = $objectManagerHelper->getObject(BeforeOrderPaymentSaveObserver::class); } /** + * Checks a case when payment method is either bank transfer or cash on delivery * @param string $methodCode * @dataProvider dataProviderBeforeOrderPaymentSaveWithInstructions */ public function testBeforeOrderPaymentSaveWithInstructions($methodCode) { - $this->payment->expects(self::once()) + $this->paymentMock->expects(self::once()) ->method('getMethod') ->willReturn($methodCode); - $this->payment->expects(self::once()) + $this->paymentMock->expects(self::once()) ->method('setAdditionalInformation') ->with('instructions', 'payment configuration'); $method = $this->getMockBuilder(Banktransfer::class) @@ -84,13 +108,14 @@ public function testBeforeOrderPaymentSaveWithInstructions($methodCode) ->getMock(); $method->expects(self::once()) - ->method('getInstructions') + ->method('getConfigData') + ->with('instructions', static::STORE_ID) ->willReturn('payment configuration'); - $this->payment->expects(self::once()) + $this->paymentMock->expects(self::once()) ->method('getMethodInstance') ->willReturn($method); - $this->_model->execute($this->observer); + $this->model->execute($this->observerMock); } /** @@ -106,33 +131,37 @@ public function dataProviderBeforeOrderPaymentSaveWithInstructions() ]; } + /** + * Checks a case when payment method is Check Money + */ public function testBeforeOrderPaymentSaveWithCheckmo() { - $this->payment->expects(self::exactly(2)) + $this->paymentMock->expects(self::exactly(2)) ->method('getMethod') ->willReturn(Checkmo::PAYMENT_METHOD_CHECKMO_CODE); - $this->payment->expects(self::exactly(2)) + $this->paymentMock->expects(self::exactly(2)) ->method('setAdditionalInformation') ->willReturnMap( [ - ['payable_to', 'payable to', $this->payment], - ['mailing_address', 'mailing address', $this->payment], + ['payable_to', 'payable to', $this->paymentMock], + ['mailing_address', 'mailing address', $this->paymentMock], ] ); $method = $this->getMockBuilder(Checkmo::class) ->disableOriginalConstructor() ->getMock(); - $method->expects(self::exactly(2)) - ->method('getPayableTo') - ->willReturn('payable to'); - $method->expects(self::exactly(2)) - ->method('getMailingAddress') - ->willReturn('mailing address'); - $this->payment->expects(self::once()) + $method->method('getConfigData') + ->willReturnMap( + [ + ['payable_to', static::STORE_ID, 'payable to'], + ['mailing_address', static::STORE_ID, 'mailing address'] + ] + ); + $this->paymentMock->expects(self::once()) ->method('getMethodInstance') ->willReturn($method); - $this->_model->execute($this->observer); + $this->model->execute($this->observerMock); } /** @@ -141,35 +170,58 @@ public function testBeforeOrderPaymentSaveWithCheckmo() */ public function testBeforeOrderPaymentSaveWithCheckmoWithoutConfig() { - $this->payment->expects(self::exactly(2)) + $this->paymentMock->expects(self::exactly(2)) ->method('getMethod') ->willReturn(Checkmo::PAYMENT_METHOD_CHECKMO_CODE); - $this->payment->expects(self::never()) + $this->paymentMock->expects(self::never()) ->method('setAdditionalInformation'); $method = $this->getMockBuilder(Checkmo::class) ->disableOriginalConstructor() ->getMock(); - $method->expects(self::once()) - ->method('getPayableTo') - ->willReturn(null); - $method->expects(self::once()) - ->method('getMailingAddress') - ->willReturn(null); - $this->payment->expects(self::once()) + $method->method('getConfigData') + ->willReturnMap( + [ + ['payable_to', static::STORE_ID, null], + ['mailing_address', static::STORE_ID, null] + ] + ); + $this->paymentMock->expects(self::once()) ->method('getMethodInstance') ->willReturn($method); - $this->_model->execute($this->observer); + $this->model->execute($this->observerMock); } + /** + * Checks a case with payment method not handled by observer + */ public function testBeforeOrderPaymentSaveWithOthers() { - $this->payment->expects(self::exactly(2)) + $this->paymentMock->expects(self::exactly(2)) ->method('getMethod') ->willReturn('somepaymentmethod'); - $this->payment->expects(self::never()) + $this->paymentMock->expects(self::never()) + ->method('setAdditionalInformation'); + + $this->model->execute($this->observerMock); + } + + /** + * @param string $methodCode + * @dataProvider dataProviderBeforeOrderPaymentSaveWithInstructions + */ + public function testBeforeOrderPaymentSaveWithInstructionsAlreadySet($methodCode) + { + $this->paymentMock->method('getMethod') + ->willReturn($methodCode); + + $this->paymentMock->expects(self::once()) + ->method('getAdditionalInformation') + ->willReturn('Test'); + + $this->paymentMock->expects(self::never()) ->method('setAdditionalInformation'); - $this->_model->execute($this->observer); + $this->model->execute($this->observerMock); } } diff --git a/app/code/Magento/OfflinePayments/etc/adminhtml/system.xml b/app/code/Magento/OfflinePayments/etc/adminhtml/system.xml index aedab33239f9f..23793f970a7d9 100644 --- a/app/code/Magento/OfflinePayments/etc/adminhtml/system.xml +++ b/app/code/Magento/OfflinePayments/etc/adminhtml/system.xml @@ -10,26 +10,26 @@ <section id="payment" type="text" sortOrder="400" showInDefault="1" showInWebsite="1" showInStore="1"> <group id="checkmo" translate="label" type="text" sortOrder="30" showInDefault="1" showInWebsite="1" showInStore="1"> <label>Check / Money Order</label> - <field id="active" translate="label" type="select" sortOrder="1" showInDefault="1" showInWebsite="1" showInStore="0" canRestore="1"> + <field id="active" translate="label" type="select" sortOrder="1" showInDefault="1" showInWebsite="1" canRestore="1"> <label>Enabled</label> <source_model>Magento\Config\Model\Config\Source\Yesno</source_model> </field> - <field id="order_status" translate="label" type="select" sortOrder="20" showInDefault="1" showInWebsite="1" showInStore="0" canRestore="1"> + <field id="order_status" translate="label" type="select" sortOrder="20" showInDefault="1" showInWebsite="1" canRestore="1"> <label>New Order Status</label> <source_model>Magento\Sales\Model\Config\Source\Order\Status\NewStatus</source_model> </field> - <field id="sort_order" translate="label" type="text" sortOrder="100" showInDefault="1" showInWebsite="1" showInStore="0"> + <field id="sort_order" translate="label" type="text" sortOrder="100" showInDefault="1" showInWebsite="1"> <label>Sort Order</label> <frontend_class>validate-number</frontend_class> </field> <field id="title" translate="label" type="text" sortOrder="10" showInDefault="1" showInWebsite="1" showInStore="1" canRestore="1"> <label>Title</label> </field> - <field id="allowspecific" translate="label" type="allowspecific" sortOrder="50" showInDefault="1" showInWebsite="1" showInStore="0" canRestore="1"> + <field id="allowspecific" translate="label" type="allowspecific" sortOrder="50" showInDefault="1" showInWebsite="1" canRestore="1"> <label>Payment from Applicable Countries</label> <source_model>Magento\Payment\Model\Config\Source\Allspecificcountries</source_model> </field> - <field id="specificcountry" translate="label" type="multiselect" sortOrder="51" showInDefault="1" showInWebsite="1" showInStore="0"> + <field id="specificcountry" translate="label" type="multiselect" sortOrder="51" showInDefault="1" showInWebsite="1"> <label>Payment from Specific Countries</label> <source_model>Magento\Directory\Model\Config\Source\Country</source_model> <can_be_empty>1</can_be_empty> @@ -40,11 +40,11 @@ <field id="mailing_address" translate="label" type="textarea" sortOrder="62" showInDefault="1" showInWebsite="1" showInStore="1"> <label>Send Check to</label> </field> - <field id="min_order_total" translate="label" type="text" sortOrder="98" showInDefault="1" showInWebsite="1" showInStore="0"> + <field id="min_order_total" translate="label" type="text" sortOrder="98" showInDefault="1" showInWebsite="1"> <label>Minimum Order Total</label> <validate>validate-number validate-zero-or-greater</validate> </field> - <field id="max_order_total" translate="label" type="text" sortOrder="99" showInDefault="1" showInWebsite="1" showInStore="0"> + <field id="max_order_total" translate="label" type="text" sortOrder="99" showInDefault="1" showInWebsite="1"> <label>Maximum Order Total</label> <validate>validate-number validate-zero-or-greater</validate> </field> @@ -52,35 +52,35 @@ </group> <group id="purchaseorder" translate="label" type="text" sortOrder="32" showInDefault="1" showInWebsite="1" showInStore="1"> <label>Purchase Order</label> - <field id="active" translate="label" type="select" sortOrder="1" showInDefault="1" showInWebsite="1" showInStore="0" canRestore="1"> + <field id="active" translate="label" type="select" sortOrder="1" showInDefault="1" showInWebsite="1" canRestore="1"> <label>Enabled</label> <source_model>Magento\Config\Model\Config\Source\Yesno</source_model> </field> - <field id="order_status" translate="label" type="select" sortOrder="2" showInDefault="1" showInWebsite="1" showInStore="0" canRestore="1"> + <field id="order_status" translate="label" type="select" sortOrder="2" showInDefault="1" showInWebsite="1" canRestore="1"> <label>New Order Status</label> <source_model>Magento\Sales\Model\Config\Source\Order\Status\NewStatus</source_model> </field> - <field id="sort_order" translate="label" type="text" sortOrder="100" showInDefault="1" showInWebsite="1" showInStore="0"> + <field id="sort_order" translate="label" type="text" sortOrder="100" showInDefault="1" showInWebsite="1"> <label>Sort Order</label> <frontend_class>validate-number</frontend_class> </field> <field id="title" translate="label" type="text" sortOrder="1" showInDefault="1" showInWebsite="1" showInStore="1" canRestore="1"> <label>Title</label> </field> - <field id="allowspecific" translate="label" type="allowspecific" sortOrder="50" showInDefault="1" showInWebsite="1" showInStore="0" canRestore="1"> + <field id="allowspecific" translate="label" type="allowspecific" sortOrder="50" showInDefault="1" showInWebsite="1" canRestore="1"> <label>Payment from Applicable Countries</label> <source_model>Magento\Payment\Model\Config\Source\Allspecificcountries</source_model> </field> - <field id="specificcountry" translate="label" type="multiselect" sortOrder="51" showInDefault="1" showInWebsite="1" showInStore="0"> + <field id="specificcountry" translate="label" type="multiselect" sortOrder="51" showInDefault="1" showInWebsite="1"> <label>Payment from Specific Countries</label> <source_model>Magento\Directory\Model\Config\Source\Country</source_model> <can_be_empty>1</can_be_empty> </field> - <field id="min_order_total" translate="label" type="text" sortOrder="98" showInDefault="1" showInWebsite="1" showInStore="0"> + <field id="min_order_total" translate="label" type="text" sortOrder="98" showInDefault="1" showInWebsite="1"> <label>Minimum Order Total</label> <validate>validate-number validate-zero-or-greater</validate> </field> - <field id="max_order_total" translate="label" type="text" sortOrder="99" showInDefault="1" showInWebsite="1" showInStore="0"> + <field id="max_order_total" translate="label" type="text" sortOrder="99" showInDefault="1" showInWebsite="1"> <label>Maximum Order Total</label> <validate>validate-number validate-zero-or-greater</validate> </field> @@ -88,22 +88,22 @@ </group> <group id="banktransfer" translate="label" type="text" sortOrder="30" showInDefault="1" showInWebsite="1" showInStore="1"> <label>Bank Transfer Payment</label> - <field id="active" translate="label" type="select" sortOrder="1" showInDefault="1" showInWebsite="1" showInStore="0" canRestore="1"> + <field id="active" translate="label" type="select" sortOrder="1" showInDefault="1" showInWebsite="1" canRestore="1"> <label>Enabled</label> <source_model>Magento\Config\Model\Config\Source\Yesno</source_model> </field> <field id="title" translate="label" type="text" sortOrder="10" showInDefault="1" showInWebsite="1" showInStore="1" canRestore="1"> <label>Title</label> </field> - <field id="order_status" translate="label" type="select" sortOrder="20" showInDefault="1" showInWebsite="1" showInStore="0" canRestore="1"> + <field id="order_status" translate="label" type="select" sortOrder="20" showInDefault="1" showInWebsite="1" canRestore="1"> <label>New Order Status</label> <source_model>Magento\Sales\Model\Config\Source\Order\Status\NewStatus</source_model> </field> - <field id="allowspecific" translate="label" type="allowspecific" sortOrder="50" showInDefault="1" showInWebsite="1" showInStore="0" canRestore="1"> + <field id="allowspecific" translate="label" type="allowspecific" sortOrder="50" showInDefault="1" showInWebsite="1" canRestore="1"> <label>Payment from Applicable Countries</label> <source_model>Magento\Payment\Model\Config\Source\Allspecificcountries</source_model> </field> - <field id="specificcountry" translate="label" type="multiselect" sortOrder="51" showInDefault="1" showInWebsite="1" showInStore="0"> + <field id="specificcountry" translate="label" type="multiselect" sortOrder="51" showInDefault="1" showInWebsite="1"> <label>Payment from Specific Countries</label> <source_model>Magento\Directory\Model\Config\Source\Country</source_model> <can_be_empty>1</can_be_empty> @@ -111,37 +111,37 @@ <field id="instructions" translate="label" type="textarea" sortOrder="62" showInDefault="1" showInWebsite="1" showInStore="1"> <label>Instructions</label> </field> - <field id="min_order_total" translate="label" type="text" sortOrder="98" showInDefault="1" showInWebsite="1" showInStore="0"> + <field id="min_order_total" translate="label" type="text" sortOrder="98" showInDefault="1" showInWebsite="1"> <label>Minimum Order Total</label> <validate>validate-number validate-zero-or-greater</validate> </field> - <field id="max_order_total" translate="label" type="text" sortOrder="99" showInDefault="1" showInWebsite="1" showInStore="0"> + <field id="max_order_total" translate="label" type="text" sortOrder="99" showInDefault="1" showInWebsite="1"> <label>Maximum Order Total</label> <validate>validate-number validate-zero-or-greater</validate> </field> - <field id="sort_order" translate="label" type="text" sortOrder="100" showInDefault="1" showInWebsite="1" showInStore="0"> + <field id="sort_order" translate="label" type="text" sortOrder="100" showInDefault="1" showInWebsite="1"> <label>Sort Order</label> <validate>validate-number</validate> </field> </group> <group id="cashondelivery" translate="label" type="text" sortOrder="30" showInDefault="1" showInWebsite="1" showInStore="1"> <label>Cash On Delivery Payment</label> - <field id="active" translate="label" type="select" sortOrder="1" showInDefault="1" showInWebsite="1" showInStore="0" canRestore="1"> + <field id="active" translate="label" type="select" sortOrder="1" showInDefault="1" showInWebsite="1" canRestore="1"> <label>Enabled</label> <source_model>Magento\Config\Model\Config\Source\Yesno</source_model> </field> <field id="title" translate="label" type="text" sortOrder="10" showInDefault="1" showInWebsite="1" showInStore="1" canRestore="1"> <label>Title</label> </field> - <field id="order_status" translate="label" type="select" sortOrder="20" showInDefault="1" showInWebsite="1" showInStore="0" canRestore="1"> + <field id="order_status" translate="label" type="select" sortOrder="20" showInDefault="1" showInWebsite="1" canRestore="1"> <label>New Order Status</label> <source_model>Magento\Sales\Model\Config\Source\Order\Status\NewStatus</source_model> </field> - <field id="allowspecific" translate="label" type="allowspecific" sortOrder="50" showInDefault="1" showInWebsite="1" showInStore="0" canRestore="1"> + <field id="allowspecific" translate="label" type="allowspecific" sortOrder="50" showInDefault="1" showInWebsite="1" canRestore="1"> <label>Payment from Applicable Countries</label> <source_model>Magento\Payment\Model\Config\Source\Allspecificcountries</source_model> </field> - <field id="specificcountry" translate="label" type="multiselect" sortOrder="51" showInDefault="1" showInWebsite="1" showInStore="0"> + <field id="specificcountry" translate="label" type="multiselect" sortOrder="51" showInDefault="1" showInWebsite="1"> <label>Payment from Specific Countries</label> <source_model>Magento\Directory\Model\Config\Source\Country</source_model> <can_be_empty>1</can_be_empty> @@ -149,15 +149,15 @@ <field id="instructions" translate="label" type="textarea" sortOrder="62" showInDefault="1" showInWebsite="1" showInStore="1"> <label>Instructions</label> </field> - <field id="min_order_total" translate="label" type="text" sortOrder="98" showInDefault="1" showInWebsite="1" showInStore="0"> + <field id="min_order_total" translate="label" type="text" sortOrder="98" showInDefault="1" showInWebsite="1"> <label>Minimum Order Total</label> <validate>validate-number validate-zero-or-greater</validate> </field> - <field id="max_order_total" translate="label" type="text" sortOrder="99" showInDefault="1" showInWebsite="1" showInStore="0"> + <field id="max_order_total" translate="label" type="text" sortOrder="99" showInDefault="1" showInWebsite="1"> <label>Maximum Order Total</label> <validate>validate-number validate-zero-or-greater</validate> </field> - <field id="sort_order" translate="label" type="text" sortOrder="100" showInDefault="1" showInWebsite="1" showInStore="0"> + <field id="sort_order" translate="label" type="text" sortOrder="100" showInDefault="1" showInWebsite="1"> <label>Sort Order</label> <validate>validate-number</validate> </field> diff --git a/app/code/Magento/OfflinePayments/registration.php b/app/code/Magento/OfflinePayments/registration.php index 4b5473194f1e4..f3d1fd1488c76 100644 --- a/app/code/Magento/OfflinePayments/registration.php +++ b/app/code/Magento/OfflinePayments/registration.php @@ -4,6 +4,6 @@ * See COPYING.txt for license details. */ -use \Magento\Framework\Component\ComponentRegistrar; +use Magento\Framework\Component\ComponentRegistrar; ComponentRegistrar::register(ComponentRegistrar::MODULE, 'Magento_OfflinePayments', __DIR__); diff --git a/app/code/Magento/OfflineShipping/Model/Carrier/Tablerate.php b/app/code/Magento/OfflineShipping/Model/Carrier/Tablerate.php index 46c46243fed5c..bbc199c91263a 100644 --- a/app/code/Magento/OfflineShipping/Model/Carrier/Tablerate.php +++ b/app/code/Magento/OfflineShipping/Model/Carrier/Tablerate.php @@ -141,7 +141,9 @@ public function collectRates(RateRequest $request) } } $oldValue = $request->getPackageValue(); - $request->setPackageValue($oldValue - $freePackageValue); + $newPackageValue = $oldValue - $freePackageValue; + $request->setPackageValue($newPackageValue); + $request->setPackageValueWithDiscount($newPackageValue); } if (!$request->getConditionName()) { diff --git a/app/code/Magento/OfflineShipping/etc/adminhtml/system.xml b/app/code/Magento/OfflineShipping/etc/adminhtml/system.xml index cb75bddf4d7bd..768aab0499046 100644 --- a/app/code/Magento/OfflineShipping/etc/adminhtml/system.xml +++ b/app/code/Magento/OfflineShipping/etc/adminhtml/system.xml @@ -10,47 +10,47 @@ <section id="carriers" type="text" sortOrder="320" showInDefault="1" showInWebsite="1" showInStore="1"> <group id="flatrate" translate="label" type="text" sortOrder="0" showInDefault="1" showInWebsite="1" showInStore="1"> <label>Flat Rate</label> - <field id="active" translate="label" type="select" sortOrder="1" showInDefault="1" showInWebsite="1" showInStore="0" canRestore="1"> + <field id="active" translate="label" type="select" sortOrder="1" showInDefault="1" showInWebsite="1" canRestore="1"> <label>Enabled</label> <source_model>Magento\Config\Model\Config\Source\Yesno</source_model> </field> <field id="name" translate="label" type="text" sortOrder="3" showInDefault="1" showInWebsite="1" showInStore="1" canRestore="1"> <label>Method Name</label> </field> - <field id="price" translate="label" type="text" sortOrder="5" showInDefault="1" showInWebsite="1" showInStore="0" canRestore="1"> + <field id="price" translate="label" type="text" sortOrder="5" showInDefault="1" showInWebsite="1" canRestore="1"> <label>Price</label> <validate>validate-number validate-zero-or-greater</validate> </field> - <field id="handling_type" translate="label" type="select" sortOrder="7" showInDefault="1" showInWebsite="1" showInStore="0" canRestore="1"> + <field id="handling_type" translate="label" type="select" sortOrder="7" showInDefault="1" showInWebsite="1" canRestore="1"> <label>Calculate Handling Fee</label> <source_model>Magento\Shipping\Model\Source\HandlingType</source_model> </field> - <field id="handling_fee" translate="label" type="text" sortOrder="8" showInDefault="1" showInWebsite="1" showInStore="0" > + <field id="handling_fee" translate="label" type="text" sortOrder="8" showInDefault="1" showInWebsite="1" > <label>Handling Fee</label> <validate>validate-number validate-zero-or-greater</validate> </field> - <field id="sort_order" translate="label" type="text" sortOrder="100" showInDefault="1" showInWebsite="1" showInStore="0"> + <field id="sort_order" translate="label" type="text" sortOrder="100" showInDefault="1" showInWebsite="1"> <label>Sort Order</label> <validate>validate-number validate-zero-or-greater</validate> </field> <field id="title" translate="label" type="text" sortOrder="2" showInDefault="1" showInWebsite="1" showInStore="1" canRestore="1"> <label>Title</label> </field> - <field id="type" translate="label" type="select" sortOrder="4" showInDefault="1" showInWebsite="1" showInStore="0" canRestore="1"> + <field id="type" translate="label" type="select" sortOrder="4" showInDefault="1" showInWebsite="1" canRestore="1"> <label>Type</label> <source_model>Magento\OfflineShipping\Model\Config\Source\Flatrate</source_model> </field> - <field id="sallowspecific" translate="label" type="select" sortOrder="90" showInDefault="1" showInWebsite="1" showInStore="0" canRestore="1"> + <field id="sallowspecific" translate="label" type="select" sortOrder="90" showInDefault="1" showInWebsite="1" canRestore="1"> <label>Ship to Applicable Countries</label> <frontend_class>shipping-applicable-country</frontend_class> <source_model>Magento\Shipping\Model\Config\Source\Allspecificcountries</source_model> </field> - <field id="specificcountry" translate="label" type="multiselect" sortOrder="91" showInDefault="1" showInWebsite="1" showInStore="0"> + <field id="specificcountry" translate="label" type="multiselect" sortOrder="91" showInDefault="1" showInWebsite="1"> <label>Ship to Specific Countries</label> <source_model>Magento\Directory\Model\Config\Source\Country</source_model> <can_be_empty>1</can_be_empty> </field> - <field id="showmethod" translate="label" type="select" sortOrder="92" showInDefault="1" showInWebsite="1" showInStore="0"> + <field id="showmethod" translate="label" type="select" sortOrder="92" showInDefault="1" showInWebsite="1"> <label>Show Method if Not Applicable</label> <source_model>Magento\Config\Model\Config\Source\Yesno</source_model> <frontend_class>shipping-skip-hide</frontend_class> @@ -61,54 +61,54 @@ </group> <group id="tablerate" translate="label" type="text" sortOrder="10" showInDefault="1" showInWebsite="1" showInStore="1"> <label>Table Rates</label> - <field id="handling_type" translate="label" type="select" sortOrder="7" showInDefault="1" showInWebsite="1" showInStore="0" canRestore="1"> + <field id="handling_type" translate="label" type="select" sortOrder="7" showInDefault="1" showInWebsite="1" canRestore="1"> <label>Calculate Handling Fee</label> <source_model>Magento\Shipping\Model\Source\HandlingType</source_model> </field> - <field id="handling_fee" translate="label" type="text" sortOrder="8" showInDefault="1" showInWebsite="1" showInStore="0"> + <field id="handling_fee" translate="label" type="text" sortOrder="8" showInDefault="1" showInWebsite="1"> <label>Handling Fee</label> <validate>validate-number validate-zero-or-greater</validate> </field> - <field id="active" translate="label" type="select" sortOrder="1" showInDefault="1" showInWebsite="1" showInStore="0" canRestore="1"> + <field id="active" translate="label" type="select" sortOrder="1" showInDefault="1" showInWebsite="1" canRestore="1"> <label>Enabled</label> <source_model>Magento\Config\Model\Config\Source\Yesno</source_model> </field> - <field id="condition_name" translate="label" type="select" sortOrder="4" showInDefault="1" showInWebsite="1" showInStore="0" canRestore="1"> + <field id="condition_name" translate="label" type="select" sortOrder="4" showInDefault="1" showInWebsite="1" canRestore="1"> <label>Condition</label> <source_model>Magento\OfflineShipping\Model\Config\Source\Tablerate</source_model> </field> - <field id="include_virtual_price" translate="label" type="select" sortOrder="5" showInDefault="1" showInWebsite="1" showInStore="0" canRestore="1"> + <field id="include_virtual_price" translate="label" type="select" sortOrder="5" showInDefault="1" showInWebsite="1" canRestore="1"> <label>Include Virtual Products in Price Calculation</label> <source_model>Magento\Config\Model\Config\Source\Yesno</source_model> </field> - <field id="export" translate="label" type="Magento\OfflineShipping\Block\Adminhtml\Form\Field\Export" sortOrder="5" showInDefault="0" showInWebsite="1" showInStore="0"> + <field id="export" translate="label" type="Magento\OfflineShipping\Block\Adminhtml\Form\Field\Export" sortOrder="5" showInWebsite="1"> <label>Export</label> </field> - <field id="import" translate="label" type="Magento\OfflineShipping\Block\Adminhtml\Form\Field\Import" sortOrder="6" showInDefault="0" showInWebsite="1" showInStore="0"> + <field id="import" translate="label" type="Magento\OfflineShipping\Block\Adminhtml\Form\Field\Import" sortOrder="6" showInWebsite="1"> <label>Import</label> <backend_model>Magento\OfflineShipping\Model\Config\Backend\Tablerate</backend_model> </field> <field id="name" translate="label" type="text" sortOrder="3" showInDefault="1" showInWebsite="1" showInStore="1" canRestore="1"> <label>Method Name</label> </field> - <field id="sort_order" translate="label" type="text" sortOrder="100" showInDefault="1" showInWebsite="1" showInStore="0"> + <field id="sort_order" translate="label" type="text" sortOrder="100" showInDefault="1" showInWebsite="1"> <label>Sort Order</label> <validate>validate-number validate-zero-or-greater</validate> </field> <field id="title" translate="label" type="text" sortOrder="2" showInDefault="1" showInWebsite="1" showInStore="1" canRestore="1"> <label>Title</label> </field> - <field id="sallowspecific" translate="label" type="select" sortOrder="90" showInDefault="1" showInWebsite="1" showInStore="0" canRestore="1"> + <field id="sallowspecific" translate="label" type="select" sortOrder="90" showInDefault="1" showInWebsite="1" canRestore="1"> <label>Ship to Applicable Countries</label> <frontend_class>shipping-applicable-country</frontend_class> <source_model>Magento\Shipping\Model\Config\Source\Allspecificcountries</source_model> </field> - <field id="specificcountry" translate="label" type="multiselect" sortOrder="91" showInDefault="1" showInWebsite="1" showInStore="0"> + <field id="specificcountry" translate="label" type="multiselect" sortOrder="91" showInDefault="1" showInWebsite="1"> <label>Ship to Specific Countries</label> <source_model>Magento\Directory\Model\Config\Source\Country</source_model> <can_be_empty>1</can_be_empty> </field> - <field id="showmethod" translate="label" type="select" sortOrder="92" showInDefault="1" showInWebsite="1" showInStore="0"> + <field id="showmethod" translate="label" type="select" sortOrder="92" showInDefault="1" showInWebsite="1"> <label>Show Method if Not Applicable</label> <frontend_class>shipping-skip-hide</frontend_class> <source_model>Magento\Config\Model\Config\Source\Yesno</source_model> @@ -119,39 +119,39 @@ </group> <group id="freeshipping" translate="label" type="text" sortOrder="5" showInDefault="1" showInWebsite="1" showInStore="1"> <label>Free Shipping</label> - <field id="active" translate="label" type="select" sortOrder="1" showInDefault="1" showInWebsite="1" showInStore="0" canRestore="1"> + <field id="active" translate="label" type="select" sortOrder="1" showInDefault="1" showInWebsite="1" canRestore="1"> <label>Enabled</label> <source_model>Magento\Config\Model\Config\Source\Yesno</source_model> </field> - <field id="free_shipping_subtotal" translate="label" type="text" sortOrder="4" showInDefault="1" showInWebsite="1" showInStore="0"> + <field id="free_shipping_subtotal" translate="label" type="text" sortOrder="4" showInDefault="1" showInWebsite="1"> <label>Minimum Order Amount</label> <validate>validate-number validate-zero-or-greater</validate> </field> - <field id="tax_including" translate="label" sortOrder="5" type="select" showInDefault="1" showInWebsite="1" showInStore="0" canRestore="1"> + <field id="tax_including" translate="label" sortOrder="5" type="select" showInDefault="1" showInWebsite="1" canRestore="1"> <label>Include Tax to Amount</label> <source_model>Magento\Config\Model\Config\Source\Yesno</source_model> </field> <field id="name" translate="label" type="text" sortOrder="3" showInDefault="1" showInWebsite="1" showInStore="1" canRestore="1"> <label>Method Name</label> </field> - <field id="sort_order" translate="label" type="text" sortOrder="100" showInDefault="1" showInWebsite="1" showInStore="0"> + <field id="sort_order" translate="label" type="text" sortOrder="100" showInDefault="1" showInWebsite="1"> <label>Sort Order</label> <validate>validate-number validate-zero-or-greater</validate> </field> <field id="title" translate="label" type="text" sortOrder="2" showInDefault="1" showInWebsite="1" showInStore="1" canRestore="1"> <label>Title</label> </field> - <field id="sallowspecific" translate="label" type="select" sortOrder="90" showInDefault="1" showInWebsite="1" showInStore="0" canRestore="1"> + <field id="sallowspecific" translate="label" type="select" sortOrder="90" showInDefault="1" showInWebsite="1" canRestore="1"> <label>Ship to Applicable Countries</label> <frontend_class>shipping-applicable-country</frontend_class> <source_model>Magento\Shipping\Model\Config\Source\Allspecificcountries</source_model> </field> - <field id="specificcountry" translate="label" type="multiselect" sortOrder="91" showInDefault="1" showInWebsite="1" showInStore="0"> + <field id="specificcountry" translate="label" type="multiselect" sortOrder="91" showInDefault="1" showInWebsite="1"> <label>Ship to Specific Countries</label> <source_model>Magento\Directory\Model\Config\Source\Country</source_model> <can_be_empty>1</can_be_empty> </field> - <field id="showmethod" translate="label" type="select" sortOrder="92" showInDefault="1" showInWebsite="1" showInStore="0"> + <field id="showmethod" translate="label" type="select" sortOrder="92" showInDefault="1" showInWebsite="1"> <label>Show Method if Not Applicable</label> <source_model>Magento\Config\Model\Config\Source\Yesno</source_model> <frontend_class>shipping-skip-hide</frontend_class> diff --git a/app/code/Magento/OfflineShipping/registration.php b/app/code/Magento/OfflineShipping/registration.php index ce3611a90c657..5c2ff2d82f1fd 100644 --- a/app/code/Magento/OfflineShipping/registration.php +++ b/app/code/Magento/OfflineShipping/registration.php @@ -4,6 +4,6 @@ * See COPYING.txt for license details. */ -use \Magento\Framework\Component\ComponentRegistrar; +use Magento\Framework\Component\ComponentRegistrar; ComponentRegistrar::register(ComponentRegistrar::MODULE, 'Magento_OfflineShipping', __DIR__); diff --git a/app/code/Magento/PageCache/Model/App/FrontController/BuiltinPlugin.php b/app/code/Magento/PageCache/Model/App/FrontController/BuiltinPlugin.php index 37dcd1302b3a3..7018efa6238b6 100644 --- a/app/code/Magento/PageCache/Model/App/FrontController/BuiltinPlugin.php +++ b/app/code/Magento/PageCache/Model/App/FrontController/BuiltinPlugin.php @@ -53,6 +53,8 @@ public function __construct( } /** + * Add PageCache functionality to Dispatch method + * * @param \Magento\Framework\App\FrontControllerInterface $subject * @param callable $proceed * @param \Magento\Framework\App\RequestInterface $request @@ -90,7 +92,7 @@ public function aroundDispatch( protected function addDebugHeaders(ResponseHttp $result) { $cacheControlHeader = $result->getHeader('Cache-Control'); - if ($cacheControlHeader instanceof \Zend\Http\Header\HeaderInterface) { + if ($cacheControlHeader instanceof \Laminas\Http\Header\HeaderInterface) { $this->addDebugHeader($result, 'X-Magento-Cache-Control', $cacheControlHeader->getFieldValue()); } $this->addDebugHeader($result, 'X-Magento-Cache-Debug', 'MISS', true); diff --git a/app/code/Magento/PageCache/Model/App/Response/HttpPlugin.php b/app/code/Magento/PageCache/Model/App/Response/HttpPlugin.php index bf958707dde49..a6949cccc1add 100644 --- a/app/code/Magento/PageCache/Model/App/Response/HttpPlugin.php +++ b/app/code/Magento/PageCache/Model/App/Response/HttpPlugin.php @@ -6,6 +6,9 @@ namespace Magento\PageCache\Model\App\Response; +use Magento\Framework\App\PageCache\NotCacheableInterface; +use Magento\Framework\App\Response\Http as HttpResponse; + /** * HTTP response plugin for frontend. */ @@ -14,14 +17,15 @@ class HttpPlugin /** * Set proper value of X-Magento-Vary cookie. * - * @param \Magento\Framework\App\Response\Http $subject + * @param HttpResponse $subject * @return void */ - public function beforeSendResponse(\Magento\Framework\App\Response\Http $subject) + public function beforeSendResponse(HttpResponse $subject) { - if ($subject instanceof \Magento\Framework\App\PageCache\NotCacheableInterface) { + if ($subject instanceof NotCacheableInterface || $subject->headersSent()) { return; } + $subject->sendVary(); } } diff --git a/app/code/Magento/PageCache/Model/Cache/Server.php b/app/code/Magento/PageCache/Model/Cache/Server.php index 7f3a4af969d7e..3be54863f2dcd 100644 --- a/app/code/Magento/PageCache/Model/Cache/Server.php +++ b/app/code/Magento/PageCache/Model/Cache/Server.php @@ -9,8 +9,8 @@ use Magento\Framework\App\DeploymentConfig; use Magento\Framework\Config\ConfigOptionsListConstants; use Magento\Framework\App\RequestInterface; -use Zend\Uri\Uri; -use Zend\Uri\UriFactory; +use Laminas\Uri\Uri; +use Laminas\Uri\UriFactory; /** * Cache server model. diff --git a/app/code/Magento/PageCache/Model/Controller/Result/BuiltinPlugin.php b/app/code/Magento/PageCache/Model/Controller/Result/BuiltinPlugin.php index aadae97009cac..20e0155aaf113 100644 --- a/app/code/Magento/PageCache/Model/Controller/Result/BuiltinPlugin.php +++ b/app/code/Magento/PageCache/Model/Controller/Result/BuiltinPlugin.php @@ -11,7 +11,7 @@ use Magento\Framework\Registry; use Magento\Framework\Controller\ResultInterface; use Magento\Framework\App\Response\Http as ResponseHttp; -use Zend\Http\Header\HeaderInterface as HttpHeaderInterface; +use Laminas\Http\Header\HeaderInterface as HttpHeaderInterface; use Magento\PageCache\Model\Cache\Type as CacheType; /** diff --git a/app/code/Magento/PageCache/Model/Layout/DepersonalizePlugin.php b/app/code/Magento/PageCache/Model/Layout/DepersonalizePlugin.php index 52b304d994bb3..c3a5a19bc1c2a 100644 --- a/app/code/Magento/PageCache/Model/Layout/DepersonalizePlugin.php +++ b/app/code/Magento/PageCache/Model/Layout/DepersonalizePlugin.php @@ -3,39 +3,46 @@ * Copyright © Magento, Inc. All rights reserved. * See COPYING.txt for license details. */ +declare(strict_types=1); + namespace Magento\PageCache\Model\Layout; +use Magento\Framework\Event\Manager as EventManager; +use Magento\Framework\Message\Session as MessageSession; +use Magento\Framework\View\LayoutInterface; use Magento\PageCache\Model\DepersonalizeChecker; /** - * Class DepersonalizePlugin + * Depersonalize customer data. + * + * @SuppressWarnings(PHPMD.CookieAndSessionMisuse) */ class DepersonalizePlugin { /** * @var DepersonalizeChecker */ - protected $depersonalizeChecker; + private $depersonalizeChecker; /** - * @var \Magento\Framework\Event\Manager + * @var EventManager */ - protected $eventManager; + private $eventManager; /** - * @var \Magento\Framework\Message\Session + * @var MessageSession */ - protected $messageSession; + private $messageSession; /** * @param DepersonalizeChecker $depersonalizeChecker - * @param \Magento\Framework\Event\Manager $eventManager - * @param \Magento\Framework\Message\Session $messageSession + * @param EventManager $eventManager + * @param MessageSession $messageSession */ public function __construct( DepersonalizeChecker $depersonalizeChecker, - \Magento\Framework\Event\Manager $eventManager, - \Magento\Framework\Message\Session $messageSession + EventManager $eventManager, + MessageSession $messageSession ) { $this->depersonalizeChecker = $depersonalizeChecker; $this->eventManager = $eventManager; @@ -43,19 +50,18 @@ public function __construct( } /** - * After generate Xml + * Change sensitive customer data if the depersonalization is needed. * - * @param \Magento\Framework\View\LayoutInterface $subject - * @param \Magento\Framework\View\LayoutInterface $result - * @return \Magento\Framework\View\LayoutInterface + * @param LayoutInterface $subject + * @return void */ - public function afterGenerateXml(\Magento\Framework\View\LayoutInterface $subject, $result) + public function afterGenerateElements(LayoutInterface $subject) { if ($this->depersonalizeChecker->checkIfDepersonalize($subject)) { $this->eventManager->dispatch('depersonalize_clear_session'); + // phpcs:ignore Magento2.Functions.DiscouragedFunction session_write_close(); $this->messageSession->clearStorage(); } - return $result; } } diff --git a/app/code/Magento/PageCache/Model/Layout/LayoutPlugin.php b/app/code/Magento/PageCache/Model/Layout/LayoutPlugin.php index 88619673ad425..762f393f2a1b9 100644 --- a/app/code/Magento/PageCache/Model/Layout/LayoutPlugin.php +++ b/app/code/Magento/PageCache/Model/Layout/LayoutPlugin.php @@ -3,41 +3,45 @@ * Copyright © Magento, Inc. All rights reserved. * See COPYING.txt for license details. */ +declare(strict_types=1); + namespace Magento\PageCache\Model\Layout; +use Magento\Framework\App\MaintenanceMode; +use Magento\Framework\App\ResponseInterface; +use Magento\Framework\DataObject\IdentityInterface; +use Magento\Framework\View\Layout; +use Magento\PageCache\Model\Config; + /** - * Class LayoutPlugin - * - * Plugin for Magento\Framework\View\Layout + * Append cacheable pages response headers. */ class LayoutPlugin { /** - * @var \Magento\PageCache\Model\Config + * @var Config */ - protected $config; + private $config; /** - * @var \Magento\Framework\App\ResponseInterface + * @var ResponseInterface */ - protected $response; + private $response; /** - * @var \Magento\Framework\App\MaintenanceMode + * @var MaintenanceMode */ private $maintenanceMode; /** - * Constructor - * - * @param \Magento\Framework\App\ResponseInterface $response - * @param \Magento\PageCache\Model\Config $config - * @param \Magento\Framework\App\MaintenanceMode $maintenanceMode + * @param ResponseInterface $response + * @param Config $config + * @param MaintenanceMode $maintenanceMode */ public function __construct( - \Magento\Framework\App\ResponseInterface $response, - \Magento\PageCache\Model\Config $config, - \Magento\Framework\App\MaintenanceMode $maintenanceMode + ResponseInterface $response, + Config $config, + MaintenanceMode $maintenanceMode ) { $this->response = $response; $this->config = $config; @@ -45,47 +49,45 @@ public function __construct( } /** - * Set appropriate Cache-Control headers + * Set appropriate Cache-Control headers. * * We have to set public headers in order to tell Varnish and Builtin app that page should be cached * - * @param \Magento\Framework\View\Layout $subject - * @param mixed $result - * @return mixed + * @param Layout $subject + * @return void */ - public function afterGenerateXml(\Magento\Framework\View\Layout $subject, $result) + public function afterGenerateElements(Layout $subject) { if ($subject->isCacheable() && !$this->maintenanceMode->isOn() && $this->config->isEnabled()) { $this->response->setPublicHeaders($this->config->getTtl()); } - return $result; } /** - * Retrieve all identities from blocks for further cache invalidation + * Retrieve all identities from blocks for further cache invalidation. * - * @param \Magento\Framework\View\Layout $subject + * @param Layout $subject * @param mixed $result * @return mixed */ - public function afterGetOutput(\Magento\Framework\View\Layout $subject, $result) + public function afterGetOutput(Layout $subject, $result) { if ($subject->isCacheable() && $this->config->isEnabled()) { - $tags = []; + $tags = [[]]; foreach ($subject->getAllBlocks() as $block) { - if ($block instanceof \Magento\Framework\DataObject\IdentityInterface) { + if ($block instanceof IdentityInterface) { $isEsiBlock = $block->getTtl() > 0; - $isVarnish = $this->config->getType() == \Magento\PageCache\Model\Config::VARNISH; + $isVarnish = $this->config->getType() == Config::VARNISH; if ($isVarnish && $isEsiBlock) { continue; } - // phpcs:ignore - $tags = array_merge($tags, $block->getIdentities()); + $tags[] = $block->getIdentities(); } } - $tags = array_unique($tags); + $tags = array_unique(array_merge(...$tags)); $this->response->setHeader('X-Magento-Tags', implode(',', $tags)); } + return $result; } } diff --git a/app/code/Magento/PageCache/Observer/FlushAllCache.php b/app/code/Magento/PageCache/Observer/FlushAllCache.php index 0a45ebf265715..aca9fc5e2a8c0 100644 --- a/app/code/Magento/PageCache/Observer/FlushAllCache.php +++ b/app/code/Magento/PageCache/Observer/FlushAllCache.php @@ -1,18 +1,25 @@ <?php /** - * * Copyright © Magento, Inc. All rights reserved. * See COPYING.txt for license details. */ +declare(strict_types=1); + namespace Magento\PageCache\Observer; -use Magento\Framework\App\ObjectManager; +use Magento\Framework\App\PageCache\Cache; +use Magento\Framework\Event\Observer; use Magento\Framework\Event\ObserverInterface; +use Magento\PageCache\Model\Cache\Type; +use Magento\PageCache\Model\Config; +/** + * Observer used to flush all caches with built-in full page cache + */ class FlushAllCache implements ObserverInterface { /** - * @var \Magento\Framework\App\PageCache\Cache + * @var Cache * * @deprecated 100.1.0 */ @@ -21,48 +28,42 @@ class FlushAllCache implements ObserverInterface /** * Application config object * - * @var \Magento\PageCache\Model\Config + * @var Config */ protected $_config; /** - * @var \Magento\PageCache\Model\Cache\Type + * @var Type */ private $fullPageCache; /** - * @param \Magento\PageCache\Model\Config $config - * @param \Magento\Framework\App\PageCache\Cache $cache + * @param Config $config + * @param Cache $cache + * @param Type $fullPageCache */ - public function __construct(\Magento\PageCache\Model\Config $config, \Magento\Framework\App\PageCache\Cache $cache) - { + public function __construct( + Config $config, + Cache $cache, + Type $fullPageCache + ) { $this->_config = $config; $this->_cache = $cache; + $this->fullPageCache = $fullPageCache; } /** * Flash Built-In cache - * @param \Magento\Framework\Event\Observer $observer + * + * @param Observer $observer + * * @return void * @SuppressWarnings(PHPMD.UnusedFormalParameter) */ - public function execute(\Magento\Framework\Event\Observer $observer) - { - if ($this->_config->getType() == \Magento\PageCache\Model\Config::BUILT_IN) { - $this->getCache()->clean(); - } - } - - /** - * TODO: Workaround to support backwards compatibility, will rework to use Dependency Injection in MAGETWO-49547 - * - * @return \Magento\PageCache\Model\Cache\Type - */ - private function getCache() + public function execute(Observer $observer) { - if (!$this->fullPageCache) { - $this->fullPageCache = ObjectManager::getInstance()->get(\Magento\PageCache\Model\Cache\Type::class); + if ($this->_config->getType() == Config::BUILT_IN) { + $this->fullPageCache->clean(); } - return $this->fullPageCache; } } diff --git a/app/code/Magento/PageCache/Observer/FlushCacheByTags.php b/app/code/Magento/PageCache/Observer/FlushCacheByTags.php index 8ce26f7d31781..778557ee0dd2f 100644 --- a/app/code/Magento/PageCache/Observer/FlushCacheByTags.php +++ b/app/code/Magento/PageCache/Observer/FlushCacheByTags.php @@ -1,18 +1,27 @@ <?php /** - * * Copyright © Magento, Inc. All rights reserved. * See COPYING.txt for license details. */ +declare(strict_types=1); + namespace Magento\PageCache\Observer; -use Magento\Framework\App\ObjectManager; +use Magento\Framework\App\Cache\Tag\Resolver; +use Magento\Framework\App\PageCache\Cache; +use Magento\Framework\Event\Observer; use Magento\Framework\Event\ObserverInterface; +use Magento\PageCache\Model\Cache\Type; +use Magento\PageCache\Model\Config; +use Zend_Cache; +/** + * Observer used to cache by tags when using built-in full page cache + */ class FlushCacheByTags implements ObserverInterface { /** - * @var \Magento\Framework\App\PageCache\Cache + * @var Cache * * @deprecated 100.1.0 */ @@ -21,78 +30,59 @@ class FlushCacheByTags implements ObserverInterface /** * Application config object * - * @var \Magento\PageCache\Model\Config + * @var Config */ protected $_config; /** - * @var \Magento\PageCache\Model\Cache\Type + * @var Type */ private $fullPageCache; /** * Invalidation tags resolver * - * @var \Magento\Framework\App\Cache\Tag\Resolver + * @var Resolver */ private $tagResolver; /** - * @param \Magento\PageCache\Model\Config $config - * @param \Magento\Framework\App\PageCache\Cache $cache + * @param Config $config + * @param Cache $cache + * @param Type $fullPageCache + * @param Resolver $tagResolver */ - public function __construct(\Magento\PageCache\Model\Config $config, \Magento\Framework\App\PageCache\Cache $cache) - { + public function __construct( + Config $config, + Cache $cache, + Type $fullPageCache, + Resolver $tagResolver + ) { $this->_config = $config; $this->_cache = $cache; + $this->fullPageCache = $fullPageCache; + $this->tagResolver = $tagResolver; } /** - * If Built-In caching is enabled it collects array of tags - * of incoming object and asks to clean cache. + * If Built-In caching is enabled it collects array of tags of incoming object and asks to clean cache. + * + * @param Observer $observer * - * @param \Magento\Framework\Event\Observer $observer * @return void */ - public function execute(\Magento\Framework\Event\Observer $observer) + public function execute(Observer $observer) { - if ($this->_config->getType() == \Magento\PageCache\Model\Config::BUILT_IN && $this->_config->isEnabled()) { + if ($this->_config->getType() == Config::BUILT_IN && $this->_config->isEnabled()) { $object = $observer->getEvent()->getObject(); if (!is_object($object)) { return; } - $tags = $this->getTagResolver()->getTags($object); + $tags = $this->tagResolver->getTags($object); if (!empty($tags)) { - $this->getCache()->clean(\Zend_Cache::CLEANING_MODE_MATCHING_ANY_TAG, array_unique($tags)); + $this->fullPageCache->clean(Zend_Cache::CLEANING_MODE_MATCHING_ANY_TAG, array_unique($tags)); } } } - - /** - * TODO: Workaround to support backwards compatibility, will rework to use Dependency Injection in MAGETWO-49547 - * - * - * @return \Magento\PageCache\Model\Cache\Type - */ - private function getCache() - { - if (!$this->fullPageCache) { - $this->fullPageCache = ObjectManager::getInstance()->get(\Magento\PageCache\Model\Cache\Type::class); - } - return $this->fullPageCache; - } - - /** - * @deprecated 100.1.2 - * @return \Magento\Framework\App\Cache\Tag\Resolver - */ - private function getTagResolver() - { - if ($this->tagResolver === null) { - $this->tagResolver = \Magento\Framework\App\ObjectManager::getInstance() - ->get(\Magento\Framework\App\Cache\Tag\Resolver::class); - } - return $this->tagResolver; - } } diff --git a/app/code/Magento/PageCache/Test/Mftf/ActionGroup/ClearCacheActionGroup.xml b/app/code/Magento/PageCache/Test/Mftf/ActionGroup/ClearCacheActionGroup.xml index 4bd68fa1e9929..ea76b133bb414 100644 --- a/app/code/Magento/PageCache/Test/Mftf/ActionGroup/ClearCacheActionGroup.xml +++ b/app/code/Magento/PageCache/Test/Mftf/ActionGroup/ClearCacheActionGroup.xml @@ -13,7 +13,7 @@ <description>Goes to the Admin Cache Management page. Clicks on 'Flush Magento Cache'.</description> </annotations> - <amOnPage url="{{_ENV.MAGENTO_BACKEND_NAME}}/admin/cache/" stepKey="goToNewCustomVarialePage"/> + <amOnPage url="{{AdminCacheManagementPage.url}}" stepKey="goToCacheManagement"/> <waitForPageLoad stepKey="waitForPageLoad"/> <click selector="{{AdminCacheManagementSection.FlushMagentoCache}}" stepKey="clickFlushMagentoCache"/> <waitForPageLoad stepKey="waitForCacheFlush"/> diff --git a/app/code/Magento/PageCache/Test/Mftf/ActionGroup/ClearPageCacheActionGroup.xml b/app/code/Magento/PageCache/Test/Mftf/ActionGroup/ClearPageCacheActionGroup.xml index 88ed167e24e1a..3c0d2aa8082b1 100644 --- a/app/code/Magento/PageCache/Test/Mftf/ActionGroup/ClearPageCacheActionGroup.xml +++ b/app/code/Magento/PageCache/Test/Mftf/ActionGroup/ClearPageCacheActionGroup.xml @@ -13,7 +13,7 @@ <description>Goes to the Admin Cache Management page. Selects 'Refresh'. Checks the 'Page Cache' row. Clicks on Submit.</description> </annotations> - <amOnPage url="{{_ENV.MAGENTO_BACKEND_NAME}}/admin/cache/" stepKey="goToCacheManagementPage"/> + <amOnPage url="{{AdminCacheManagementPage.url}}" stepKey="goToCacheManagementPage"/> <waitForPageLoad stepKey="waitForPageLoad"/> <click selector="{{AdminCacheManagementSection.actionDropDown}}" stepKey="actionSelection"/> <click selector="{{AdminCacheManagementSection.refreshOption}}" stepKey="selectRefreshOption"/> diff --git a/app/code/Magento/PageCache/Test/Mftf/Test/AdminFrontendAreaSessionMustNotAffectAdminAreaTest.xml b/app/code/Magento/PageCache/Test/Mftf/Test/AdminFrontendAreaSessionMustNotAffectAdminAreaTest.xml index 375211e5f2f51..d2c738398aae1 100644 --- a/app/code/Magento/PageCache/Test/Mftf/Test/AdminFrontendAreaSessionMustNotAffectAdminAreaTest.xml +++ b/app/code/Magento/PageCache/Test/Mftf/Test/AdminFrontendAreaSessionMustNotAffectAdminAreaTest.xml @@ -40,7 +40,7 @@ </createData> <magentoCLI command="cache:clean" arguments="full_page" stepKey="clearCache"/> - <actionGroup ref="logout" stepKey="logout"/> + <actionGroup ref="AdminLogoutActionGroup" stepKey="logout"/> <actionGroup ref="StorefrontCustomerLogoutActionGroup" stepKey="logoutCustomer"/> <resetCookie userInput="PHPSESSID" stepKey="resetSessionCookie"/> </before> @@ -54,11 +54,11 @@ <deleteData createDataKey="createCategoryB" stepKey="deleteCategoryB"/> <deleteData createDataKey="createCategoryA" stepKey="deleteCategoryA"/> - <actionGroup ref="logout" stepKey="logoutAdmin"/> + <actionGroup ref="AdminLogoutActionGroup" stepKey="logoutAdmin"/> </after> <!-- 1. Login as admin --> - <actionGroup ref="LoginAsAdmin" stepKey="LoginAsAdmin"/> + <actionGroup ref="AdminLoginActionGroup" stepKey="LoginAsAdmin"/> <!-- 2. Navigate Go to "Catalog"->"Products" --> <amOnPage url="{{ProductCatalogPage.url}}" stepKey="onCatalogProductPage"/> diff --git a/app/code/Magento/PageCache/Test/Mftf/Test/FlushStaticFilesCacheButtonVisibilityTest.xml b/app/code/Magento/PageCache/Test/Mftf/Test/FlushStaticFilesCacheButtonVisibilityTest.xml index bd6f7ba362bf4..3b93ec1796838 100644 --- a/app/code/Magento/PageCache/Test/Mftf/Test/FlushStaticFilesCacheButtonVisibilityTest.xml +++ b/app/code/Magento/PageCache/Test/Mftf/Test/FlushStaticFilesCacheButtonVisibilityTest.xml @@ -22,7 +22,7 @@ </annotations> <before> <!-- Log in to Admin Panel --> - <actionGroup ref="LoginAsAdmin" stepKey="loginAsAdmin"/> + <actionGroup ref="AdminLoginActionGroup" stepKey="loginAsAdmin"/> </before> <!-- Open Cache Management page --> diff --git a/app/code/Magento/PageCache/Test/Mftf/Test/NewProductsListWidgetTest.xml b/app/code/Magento/PageCache/Test/Mftf/Test/NewProductsListWidgetTest.xml deleted file mode 100644 index e7dc40c20f4dd..0000000000000 --- a/app/code/Magento/PageCache/Test/Mftf/Test/NewProductsListWidgetTest.xml +++ /dev/null @@ -1,29 +0,0 @@ -<?xml version="1.0" encoding="UTF-8"?> -<!-- - /** - * Copyright © Magento, Inc. All rights reserved. - * See COPYING.txt for license details. - */ ---> - -<tests xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" - xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/testSchema.xsd"> - <test name="NewProductsListWidgetSimpleProductTest"> - <actionGroup ref="ClearPageCacheActionGroup" stepKey="clearPageCache" before="amOnCmsPage"/> - </test> - <test name="NewProductsListWidgetConfigurableProductTest"> - <actionGroup ref="ClearPageCacheActionGroup" stepKey="clearPageCache" after="clickSaveProduct"/> - </test> - <test name="NewProductsListWidgetGroupedProductTest"> - <actionGroup ref="ClearPageCacheActionGroup" stepKey="clearPageCache" after="clickSaveProduct"/> - </test> - <test name="NewProductsListWidgetVirtualProductTest"> - <actionGroup ref="ClearPageCacheActionGroup" stepKey="clearPageCache" before="amOnCmsPage"/> - </test> - <test name="NewProductsListWidgetBundleProductTest"> - <actionGroup ref="ClearPageCacheActionGroup" stepKey="clearPageCache" after="clickSaveProduct"/> - </test> - <test name="NewProductsListWidgetDownloadableProductTest"> - <actionGroup ref="ClearPageCacheActionGroup" stepKey="clearPageCache" after="clickSaveProduct"/> - </test> -</tests> diff --git a/app/code/Magento/PageCache/Test/Mftf/Test/NewProductsListWidgetTest/NewProductsListWidgetBundleProductTest.xml b/app/code/Magento/PageCache/Test/Mftf/Test/NewProductsListWidgetTest/NewProductsListWidgetBundleProductTest.xml new file mode 100644 index 0000000000000..d58455c9312d3 --- /dev/null +++ b/app/code/Magento/PageCache/Test/Mftf/Test/NewProductsListWidgetTest/NewProductsListWidgetBundleProductTest.xml @@ -0,0 +1,13 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + /** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +--> +<tests xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/testSchema.xsd"> + <test name="NewProductsListWidgetBundleProductTest"> + <actionGroup ref="ClearPageCacheActionGroup" stepKey="clearPageCache" after="clickSaveProduct"/> + </test> +</tests> diff --git a/app/code/Magento/PageCache/Test/Mftf/Test/NewProductsListWidgetTest/NewProductsListWidgetConfigurableProductTest.xml b/app/code/Magento/PageCache/Test/Mftf/Test/NewProductsListWidgetTest/NewProductsListWidgetConfigurableProductTest.xml new file mode 100644 index 0000000000000..741d697a3643f --- /dev/null +++ b/app/code/Magento/PageCache/Test/Mftf/Test/NewProductsListWidgetTest/NewProductsListWidgetConfigurableProductTest.xml @@ -0,0 +1,13 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + /** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +--> +<tests xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/testSchema.xsd"> + <test name="NewProductsListWidgetConfigurableProductTest"> + <actionGroup ref="ClearPageCacheActionGroup" stepKey="clearPageCache" after="clickSaveProduct"/> + </test> +</tests> diff --git a/app/code/Magento/PageCache/Test/Mftf/Test/NewProductsListWidgetTest/NewProductsListWidgetDownloadableProductTest.xml b/app/code/Magento/PageCache/Test/Mftf/Test/NewProductsListWidgetTest/NewProductsListWidgetDownloadableProductTest.xml new file mode 100644 index 0000000000000..dc9cf7f03686f --- /dev/null +++ b/app/code/Magento/PageCache/Test/Mftf/Test/NewProductsListWidgetTest/NewProductsListWidgetDownloadableProductTest.xml @@ -0,0 +1,13 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + /** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +--> +<tests xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/testSchema.xsd"> + <test name="NewProductsListWidgetDownloadableProductTest"> + <actionGroup ref="ClearPageCacheActionGroup" stepKey="clearPageCache" after="clickSaveProduct"/> + </test> +</tests> diff --git a/app/code/Magento/PageCache/Test/Mftf/Test/NewProductsListWidgetTest/NewProductsListWidgetGroupedProductTest.xml b/app/code/Magento/PageCache/Test/Mftf/Test/NewProductsListWidgetTest/NewProductsListWidgetGroupedProductTest.xml new file mode 100644 index 0000000000000..29f023cb6ea50 --- /dev/null +++ b/app/code/Magento/PageCache/Test/Mftf/Test/NewProductsListWidgetTest/NewProductsListWidgetGroupedProductTest.xml @@ -0,0 +1,13 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + /** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +--> +<tests xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/testSchema.xsd"> + <test name="NewProductsListWidgetGroupedProductTest"> + <actionGroup ref="ClearPageCacheActionGroup" stepKey="clearPageCache" after="clickSaveProduct"/> + </test> +</tests> diff --git a/app/code/Magento/PageCache/Test/Mftf/Test/NewProductsListWidgetTest/NewProductsListWidgetSimpleProductTest.xml b/app/code/Magento/PageCache/Test/Mftf/Test/NewProductsListWidgetTest/NewProductsListWidgetSimpleProductTest.xml new file mode 100644 index 0000000000000..c8bd51b30c4cd --- /dev/null +++ b/app/code/Magento/PageCache/Test/Mftf/Test/NewProductsListWidgetTest/NewProductsListWidgetSimpleProductTest.xml @@ -0,0 +1,13 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + /** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +--> +<tests xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/testSchema.xsd"> + <test name="NewProductsListWidgetSimpleProductTest"> + <actionGroup ref="ClearPageCacheActionGroup" stepKey="clearPageCache" before="amOnCmsPage"/> + </test> +</tests> diff --git a/app/code/Magento/PageCache/Test/Mftf/Test/NewProductsListWidgetTest/NewProductsListWidgetVirtualProductTest.xml b/app/code/Magento/PageCache/Test/Mftf/Test/NewProductsListWidgetTest/NewProductsListWidgetVirtualProductTest.xml new file mode 100644 index 0000000000000..1b840433a17b3 --- /dev/null +++ b/app/code/Magento/PageCache/Test/Mftf/Test/NewProductsListWidgetTest/NewProductsListWidgetVirtualProductTest.xml @@ -0,0 +1,13 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + /** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +--> +<tests xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/testSchema.xsd"> + <test name="NewProductsListWidgetVirtualProductTest"> + <actionGroup ref="ClearPageCacheActionGroup" stepKey="clearPageCache" before="amOnCmsPage"/> + </test> +</tests> diff --git a/app/code/Magento/PageCache/Test/Unit/Model/App/FrontController/BuiltinPluginTest.php b/app/code/Magento/PageCache/Test/Unit/Model/App/FrontController/BuiltinPluginTest.php index db0edfa6bd779..c9a887595b5a1 100644 --- a/app/code/Magento/PageCache/Test/Unit/Model/App/FrontController/BuiltinPluginTest.php +++ b/app/code/Magento/PageCache/Test/Unit/Model/App/FrontController/BuiltinPluginTest.php @@ -84,7 +84,7 @@ protected function setUp() */ public function testAroundDispatchProcessIfCacheMissed($state) { - $header = \Zend\Http\Header\GenericHeader::fromString('Cache-Control: no-cache'); + $header = \Laminas\Http\Header\GenericHeader::fromString('Cache-Control: no-cache'); $this->configMock ->expects($this->once()) ->method('getType') diff --git a/app/code/Magento/PageCache/Test/Unit/Model/App/Response/HttpPluginTest.php b/app/code/Magento/PageCache/Test/Unit/Model/App/Response/HttpPluginTest.php index 59591c8ee957f..6f8f12098dda8 100644 --- a/app/code/Magento/PageCache/Test/Unit/Model/App/Response/HttpPluginTest.php +++ b/app/code/Magento/PageCache/Test/Unit/Model/App/Response/HttpPluginTest.php @@ -3,37 +3,63 @@ * Copyright © Magento, Inc. All rights reserved. * See COPYING.txt for license details. */ +declare(strict_types=1); namespace Magento\PageCache\Test\Unit\Model\App\Response; +use Magento\Framework\App\Response\Http as HttpResponse; +use Magento\MediaStorage\Model\File\Storage\Response as FileResponse; use Magento\PageCache\Model\App\Response\HttpPlugin; +use PHPUnit\Framework\MockObject\MockObject; +use PHPUnit\Framework\TestCase; -class HttpPluginTest extends \PHPUnit\Framework\TestCase +/** + * Tests \Magento\PageCache\Model\App\Response\HttpPlugin. + */ +class HttpPluginTest extends TestCase { /** - * @param \Magento\Framework\App\Response\FileInterface $responseInstanceClass + * @var HttpPlugin + */ + private $httpPlugin; + + /** + * @inheritdoc + */ + protected function setUp() + { + parent::setUp(); + $this->httpPlugin = new HttpPlugin(); + } + + /** + * @param string $responseClass + * @param bool $headersSent * @param int $sendVaryCalled + * @return void * * @dataProvider beforeSendResponseDataProvider */ - public function testBeforeSendResponse($responseInstanceClass, $sendVaryCalled) + public function testBeforeSendResponse(string $responseClass, bool $headersSent, int $sendVaryCalled): void { - /** @var \Magento\Framework\App\Response\Http | \PHPUnit_Framework_MockObject_MockObject $responseMock */ - $responseMock = $this->createMock($responseInstanceClass); - $responseMock->expects($this->exactly($sendVaryCalled)) - ->method('sendVary'); - $plugin = new HttpPlugin(); - $plugin->beforeSendResponse($responseMock); + /** @var HttpResponse|MockObject $responseMock */ + $responseMock = $this->createMock($responseClass); + $responseMock->expects($this->any())->method('headersSent')->willReturn($headersSent); + $responseMock->expects($this->exactly($sendVaryCalled))->method('sendVary'); + + $this->httpPlugin->beforeSendResponse($responseMock); } /** * @return array */ - public function beforeSendResponseDataProvider() + public function beforeSendResponseDataProvider(): array { return [ - [\Magento\Framework\App\Response\Http::class, 1], - [\Magento\MediaStorage\Model\File\Storage\Response::class, 0] + 'http_response_headers_not_sent' => [HttpResponse::class, false, 1], + 'http_response_headers_sent' => [HttpResponse::class, true, 0], + 'file_response_headers_not_sent' => [FileResponse::class, false, 0], + 'file_response_headers_sent' => [FileResponse::class, true, 0], ]; } } diff --git a/app/code/Magento/PageCache/Test/Unit/Model/Cache/ServerTest.php b/app/code/Magento/PageCache/Test/Unit/Model/Cache/ServerTest.php index a57effe1f31ad..553d86abd6546 100644 --- a/app/code/Magento/PageCache/Test/Unit/Model/Cache/ServerTest.php +++ b/app/code/Magento/PageCache/Test/Unit/Model/Cache/ServerTest.php @@ -7,7 +7,7 @@ use \Magento\Framework\TestFramework\Unit\Helper\ObjectManager; use \Magento\PageCache\Model\Cache\Server; -use \Zend\Uri\UriFactory; +use \Laminas\Uri\UriFactory; class ServerTest extends \PHPUnit\Framework\TestCase { @@ -23,6 +23,9 @@ class ServerTest extends \PHPUnit\Framework\TestCase /** @var \PHPUnit_Framework_MockObject_MockObject | \Magento\Framework\UrlInterface */ protected $urlBuilderMock; + /** @var \PHPUnit_Framework_MockObject_MockObject| \Magento\Framework\Cache\InvalidateLogger */ + private $loggerMock; + protected function setUp() { $this->configMock = $this->createMock(\Magento\Framework\App\DeploymentConfig::class); diff --git a/app/code/Magento/PageCache/Test/Unit/Model/Controller/Result/BuiltinPluginTest.php b/app/code/Magento/PageCache/Test/Unit/Model/Controller/Result/BuiltinPluginTest.php index fc4e056734939..cc431b94eb5bf 100644 --- a/app/code/Magento/PageCache/Test/Unit/Model/Controller/Result/BuiltinPluginTest.php +++ b/app/code/Magento/PageCache/Test/Unit/Model/Controller/Result/BuiltinPluginTest.php @@ -13,7 +13,7 @@ use Magento\Framework\Registry; use Magento\Framework\Controller\ResultInterface; use Magento\Framework\App\Response\Http as ResponseHttp; -use Zend\Http\Header\HeaderInterface as HttpHeaderInterface; +use Laminas\Http\Header\HeaderInterface as HttpHeaderInterface; use Magento\PageCache\Model\Cache\Type as CacheType; /** diff --git a/app/code/Magento/PageCache/Test/Unit/Model/Layout/DepersonalizePluginTest.php b/app/code/Magento/PageCache/Test/Unit/Model/Layout/DepersonalizePluginTest.php index c9638a3d5b8f6..bf10de64ff365 100644 --- a/app/code/Magento/PageCache/Test/Unit/Model/Layout/DepersonalizePluginTest.php +++ b/app/code/Magento/PageCache/Test/Unit/Model/Layout/DepersonalizePluginTest.php @@ -3,81 +3,98 @@ * Copyright © Magento, Inc. All rights reserved. * See COPYING.txt for license details. */ +declare(strict_types=1); namespace Magento\PageCache\Test\Unit\Model\Layout; +use Magento\Framework\Event\Manager; +use Magento\Framework\Message\Session; +use Magento\Framework\TestFramework\Unit\Helper\ObjectManager as ObjectManagerHelper; +use Magento\Framework\View\LayoutInterface; +use Magento\PageCache\Model\DepersonalizeChecker; +use Magento\PageCache\Model\Layout\DepersonalizePlugin; +use PHPUnit\Framework\MockObject\MockObject; +use PHPUnit\Framework\TestCase; + /** - * Class DepersonalizePluginTest + * Unit tests for \Magento\PageCache\Model\Layout\DepersonalizePlugin class. */ -class DepersonalizePluginTest extends \PHPUnit\Framework\TestCase +class DepersonalizePluginTest extends TestCase { /** - * @var \Magento\PageCache\Model\Layout\DepersonalizePlugin + * @var DepersonalizePlugin */ - protected $plugin; + private $plugin; /** - * @var \Magento\Framework\View\LayoutInterface|\PHPUnit_Framework_MockObject_MockObject + * @var LayoutInterface|MockObject */ - protected $layoutMock; + private $layoutMock; /** - * @var \Magento\Framework\Event\Manager|\PHPUnit_Framework_MockObject_MockObject + * @var Manager|MockObject */ - protected $eventManagerMock; + private $eventManagerMock; /** - * @var \Magento\Framework\Message\Session|\PHPUnit_Framework_MockObject_MockObject + * @var Session|MockObject */ - protected $messageSessionMock; + private $messageSessionMock; /** - * @var \Magento\PageCache\Model\DepersonalizeChecker|\PHPUnit_Framework_MockObject_MockObject + * @var DepersonalizeChecker|MockObject */ - protected $depersonalizeCheckerMock; + private $depersonalizeCheckerMock; /** - * SetUp + * @inheritdoc */ protected function setUp() { - $this->layoutMock = $this->createMock(\Magento\Framework\View\Layout::class); - $this->eventManagerMock = $this->createMock(\Magento\Framework\Event\Manager::class); + $this->layoutMock = $this->getMockForAbstractClass(LayoutInterface::class); + $this->eventManagerMock = $this->createMock(Manager::class); $this->messageSessionMock = $this->createPartialMock( - \Magento\Framework\Message\Session::class, + Session::class, ['clearStorage'] ); - $this->depersonalizeCheckerMock = $this->createMock(\Magento\PageCache\Model\DepersonalizeChecker::class); - $this->plugin = new \Magento\PageCache\Model\Layout\DepersonalizePlugin( - $this->depersonalizeCheckerMock, - $this->eventManagerMock, - $this->messageSessionMock + $this->depersonalizeCheckerMock = $this->createMock(DepersonalizeChecker::class); + $this->plugin = (new ObjectManagerHelper($this))->getObject( + DepersonalizePlugin::class, + [ + 'depersonalizeChecker' => $this->depersonalizeCheckerMock, + 'eventManager' => $this->eventManagerMock, + 'messageSession' => $this->messageSessionMock, + ] ); } - public function testAfterGenerateXml() + /** + * Test afterGenerateElements method when depersonalization is needed. + * + * @return void + */ + public function testAfterGenerateElements(): void { - $expectedResult = $this->createMock(\Magento\Framework\View\Layout::class); - $this->eventManagerMock->expects($this->once()) ->method('dispatch') ->with($this->equalTo('depersonalize_clear_session')); $this->messageSessionMock->expects($this->once())->method('clearStorage'); $this->depersonalizeCheckerMock->expects($this->once())->method('checkIfDepersonalize')->willReturn(true); - $actualResult = $this->plugin->afterGenerateXml($this->layoutMock, $expectedResult); - $this->assertEquals($expectedResult, $actualResult); + $this->assertEmpty($this->plugin->afterGenerateElements($this->layoutMock)); } - public function testAfterGenerateXmlNoDepersonalize() + /** + * Test afterGenerateElements method when depersonalization is not needed. + * + * @return void + */ + public function testAfterGenerateElementsNoDepersonalize(): void { $this->depersonalizeCheckerMock->expects($this->once())->method('checkIfDepersonalize')->willReturn(false); - $this->eventManagerMock->expects($this->never()) - ->method('dispatch'); + $this->eventManagerMock->expects($this->never())->method('dispatch'); $this->messageSessionMock->expects($this->never())->method('clearStorage'); - $expectedResult = $this->createMock(\Magento\Framework\View\Layout::class); - $actualResult = $this->plugin->afterGenerateXml($this->layoutMock, $expectedResult); - $this->assertEquals($expectedResult, $actualResult); + $this->assertEmpty($this->plugin->afterGenerateElements($this->layoutMock)); } } diff --git a/app/code/Magento/PageCache/Test/Unit/Model/Layout/LayoutPluginTest.php b/app/code/Magento/PageCache/Test/Unit/Model/Layout/LayoutPluginTest.php index e2bc7f237ab0a..d5d6efa5e35ae 100644 --- a/app/code/Magento/PageCache/Test/Unit/Model/Layout/LayoutPluginTest.php +++ b/app/code/Magento/PageCache/Test/Unit/Model/Layout/LayoutPluginTest.php @@ -3,54 +3,69 @@ * Copyright © Magento, Inc. All rights reserved. * See COPYING.txt for license details. */ +declare(strict_types=1); + namespace Magento\PageCache\Test\Unit\Model\Layout; -class LayoutPluginTest extends \PHPUnit\Framework\TestCase +use Magento\Framework\App\Config\ScopeConfigInterface; +use Magento\Framework\App\MaintenanceMode; +use Magento\Framework\App\Response\Http; +use Magento\Framework\App\ResponseInterface; +use Magento\Framework\TestFramework\Unit\Helper\ObjectManager as ObjectManagerHelper; +use Magento\Framework\View\Layout; +use Magento\PageCache\Model\Config; +use Magento\PageCache\Model\Layout\LayoutPlugin; +use Magento\PageCache\Test\Unit\Block\Controller\StubBlock; +use PHPUnit\Framework\MockObject\MockObject; +use PHPUnit\Framework\TestCase; + +/** + * Unit tests for \Magento\PageCache\Model\Layout\LayoutPlugin class. + */ +class LayoutPluginTest extends TestCase { /** - * @var \Magento\PageCache\Model\Layout\LayoutPlugin + * @var LayoutPlugin */ - protected $model; + private $model; /** - * @var \Magento\Framework\App\ResponseInterface|\PHPUnit_Framework_MockObject_MockObject + * @var ResponseInterface|MockObject */ - protected $responseMock; + private $responseMock; /** - * @var \Magento\Framework\View\Layout|\PHPUnit_Framework_MockObject_MockObject + * @var Layout|MockObject */ - protected $layoutMock; + private $layoutMock; /** - * @var \Magento\Framework\App\Config\ScopeConfigInterface + * @var ScopeConfigInterface|MockObject */ - protected $configMock; + private $configMock; /** - * @var \Magento\Framework\App\MaintenanceMode|\PHPUnit\Framework\MockObject\MockObject + * @var MaintenanceMode|MockObject */ private $maintenanceModeMock; + /** + * @inheritdoc + */ protected function setUp() { - $this->layoutMock = $this->getMockForAbstractClass( - \Magento\Framework\View\Layout::class, - [], - '', - false, - true, - true, - ['isCacheable', 'getAllBlocks'] - ); - $this->responseMock = $this->createMock(\Magento\Framework\App\Response\Http::class); - $this->configMock = $this->createMock(\Magento\PageCache\Model\Config::class); - $this->maintenanceModeMock = $this->createMock(\Magento\Framework\App\MaintenanceMode::class); - - $this->model = new \Magento\PageCache\Model\Layout\LayoutPlugin( - $this->responseMock, - $this->configMock, - $this->maintenanceModeMock + $this->layoutMock = $this->createPartialMock(Layout::class, ['isCacheable', 'getAllBlocks']); + $this->responseMock = $this->createMock(Http::class); + $this->configMock = $this->createMock(Config::class); + $this->maintenanceModeMock = $this->createMock(MaintenanceMode::class); + + $this->model = (new ObjectManagerHelper($this))->getObject( + LayoutPlugin::class, + [ + 'response' => $this->responseMock, + 'config' => $this->configMock, + 'maintenanceMode' => $this->maintenanceModeMock, + ] ); } @@ -58,33 +73,31 @@ protected function setUp() * @param $cacheState * @param $layoutIsCacheable * @param $maintenanceModeIsEnabled - * - * @dataProvider afterGenerateXmlDataProvider + * @return void + * @dataProvider afterGenerateElementsDataProvider */ - public function testAfterGenerateXml($cacheState, $layoutIsCacheable, $maintenanceModeIsEnabled) + public function testAfterGenerateElements($cacheState, $layoutIsCacheable, $maintenanceModeIsEnabled): void { $maxAge = 180; - $result = 'test'; - $this->layoutMock->expects($this->once())->method('isCacheable')->will($this->returnValue($layoutIsCacheable)); - $this->configMock->expects($this->any())->method('isEnabled')->will($this->returnValue($cacheState)); - $this->maintenanceModeMock->expects($this->any())->method('isOn') - ->will($this->returnValue($maintenanceModeIsEnabled)); + $this->layoutMock->expects($this->once())->method('isCacheable')->willReturn($layoutIsCacheable); + $this->configMock->expects($this->any())->method('isEnabled')->willReturn($cacheState); + $this->maintenanceModeMock->expects($this->any())->method('isOn')->willReturn($maintenanceModeIsEnabled); if ($layoutIsCacheable && $cacheState && !$maintenanceModeIsEnabled) { - $this->configMock->expects($this->once())->method('getTtl')->will($this->returnValue($maxAge)); + $this->configMock->expects($this->once())->method('getTtl')->willReturn($maxAge); $this->responseMock->expects($this->once())->method('setPublicHeaders')->with($maxAge); } else { $this->responseMock->expects($this->never())->method('setPublicHeaders'); } - $output = $this->model->afterGenerateXml($this->layoutMock, $result); - $this->assertSame($result, $output); + + $this->assertEmpty($this->model->afterGenerateElements($this->layoutMock)); } /** * @return array */ - public function afterGenerateXmlDataProvider() + public function afterGenerateElementsDataProvider(): array { return [ 'Full_cache state is true, Layout is cache-able' => [true, true, false], @@ -101,14 +114,15 @@ public function afterGenerateXmlDataProvider() * @param $expectedTags * @param $configCacheType * @param $ttl + * @return void * @dataProvider afterGetOutputDataProvider */ - public function testAfterGetOutput($cacheState, $layoutIsCacheable, $expectedTags, $configCacheType, $ttl) + public function testAfterGetOutput($cacheState, $layoutIsCacheable, $expectedTags, $configCacheType, $ttl): void { $html = 'html'; $this->configMock->expects($this->any())->method('isEnabled')->will($this->returnValue($cacheState)); $blockStub = $this->createPartialMock( - \Magento\PageCache\Test\Unit\Block\Controller\StubBlock::class, + StubBlock::class, ['getIdentities'] ); $blockStub->setTtl($ttl); @@ -130,42 +144,42 @@ public function testAfterGetOutput($cacheState, $layoutIsCacheable, $expectedTag /** * @return array */ - public function afterGetOutputDataProvider() + public function afterGetOutputDataProvider(): array { $tags = 'identity1,identity2'; return [ 'Cacheable layout, Full_cache state is true' => [true, true, $tags, null, 0], 'Non-cacheable layout' => [true, false, null, null, 0], - 'Cacheable layout with Varnish' => [true, true, $tags, \Magento\PageCache\Model\Config::VARNISH, 0], + 'Cacheable layout with Varnish' => [true, true, $tags, Config::VARNISH, 0], 'Cacheable layout with Varnish, Full_cache state is false' => [ false, true, $tags, - \Magento\PageCache\Model\Config::VARNISH, + Config::VARNISH, 0, ], 'Cacheable layout with Varnish and esi' => [ true, true, null, - \Magento\PageCache\Model\Config::VARNISH, + Config::VARNISH, 100, ], - 'Cacheable layout with Builtin' => [true, true, $tags, \Magento\PageCache\Model\Config::BUILT_IN, 0], + 'Cacheable layout with Builtin' => [true, true, $tags, Config::BUILT_IN, 0], 'Cacheable layout with Builtin, Full_cache state is false' => [ false, true, $tags, - \Magento\PageCache\Model\Config::BUILT_IN, + Config::BUILT_IN, 0, ], 'Cacheable layout with Builtin and esi' => [ true, false, $tags, - \Magento\PageCache\Model\Config::BUILT_IN, + Config::BUILT_IN, 100, - ] + ], ]; } } diff --git a/app/code/Magento/PageCache/Test/Unit/Observer/FlushAllCacheTest.php b/app/code/Magento/PageCache/Test/Unit/Observer/FlushAllCacheTest.php index 27e1da5a9f144..a0fa99035c8b0 100644 --- a/app/code/Magento/PageCache/Test/Unit/Observer/FlushAllCacheTest.php +++ b/app/code/Magento/PageCache/Test/Unit/Observer/FlushAllCacheTest.php @@ -1,48 +1,61 @@ <?php /** - * * Copyright © Magento, Inc. All rights reserved. * See COPYING.txt for license details. */ namespace Magento\PageCache\Test\Unit\Observer; -class FlushAllCacheTest extends \PHPUnit\Framework\TestCase -{ - /** @var \Magento\PageCache\Observer\FlushAllCache */ - private $_model; +use Magento\Framework\Event\Observer; +use Magento\Framework\TestFramework\Unit\Helper\ObjectManager; +use Magento\PageCache\Model\Cache\Type; +use Magento\PageCache\Model\Config; +use Magento\PageCache\Observer\FlushAllCache; +use PHPUnit\Framework\MockObject\MockObject; +use PHPUnit\Framework\TestCase; - /** @var \PHPUnit_Framework_MockObject_MockObject|\Magento\PageCache\Model\Config */ - private $_configMock; +/** + * Test class for \Magento\PageCache\Observer\FlushAllCache + */ +class FlushAllCacheTest extends TestCase +{ + /** + * @var FlushAllCache + */ + private $model; - /** @var \PHPUnit_Framework_MockObject_MockObject|\Magento\Framework\App\PageCache\Cache */ - private $_cacheMock; + /** + * @var Config|MockObject + */ + private $configMock; - /** @var \PHPUnit_Framework_MockObject_MockObject|\Magento\Framework\Event\Observer */ + /** + * @var Observer|MockObject + */ private $observerMock; - /** @var \PHPUnit_Framework_MockObject_MockObject|\Magento\PageCache\Model\Cache\Type */ + /** + * @var Type|MockObject + */ private $fullPageCacheMock; /** - * Set up all mocks and data for test + * @inheritDoc */ protected function setUp() { - $this->_configMock = $this->createPartialMock(\Magento\PageCache\Model\Config::class, ['getType', 'isEnabled']); - $this->_cacheMock = $this->createPartialMock(\Magento\Framework\App\PageCache\Cache::class, ['clean']); - $this->fullPageCacheMock = $this->createPartialMock(\Magento\PageCache\Model\Cache\Type::class, ['clean']); - $this->observerMock = $this->createMock(\Magento\Framework\Event\Observer::class); + $this->configMock = $this->createPartialMock(Config::class, ['getType', 'isEnabled']); + $this->fullPageCacheMock = $this->createPartialMock(Type::class, ['clean']); + $this->observerMock = $this->createMock(Observer::class); - $this->_model = new \Magento\PageCache\Observer\FlushAllCache( - $this->_configMock, - $this->_cacheMock + $objectManager = new ObjectManager($this); + $this->model = $objectManager->getObject( + FlushAllCache::class, + [ + 'config' => $this->configMock, + 'fullPageCache' => $this->fullPageCacheMock + ] ); - - $reflection = new \ReflectionClass(\Magento\PageCache\Observer\FlushAllCache::class); - $reflectionProperty = $reflection->getProperty('fullPageCache'); - $reflectionProperty->setAccessible(true); - $reflectionProperty->setValue($this->_model, $this->fullPageCacheMock); } /** @@ -50,15 +63,32 @@ protected function setUp() */ public function testExecute() { - $this->_configMock->expects( + $this->configMock->expects( $this->once() )->method( 'getType' - )->will( - $this->returnValue(\Magento\PageCache\Model\Config::BUILT_IN) + )->willReturn( + Config::BUILT_IN ); $this->fullPageCacheMock->expects($this->once())->method('clean'); - $this->_model->execute($this->observerMock); + $this->model->execute($this->observerMock); + } + + /** + * Test case for flushing all the cache with varnish enabled + */ + public function testExecuteWithVarnish() + { + $this->configMock->expects( + $this->once() + )->method( + 'getType' + )->willReturn( + Config::VARNISH + ); + + $this->fullPageCacheMock->expects($this->never())->method('clean'); + $this->model->execute($this->observerMock); } } diff --git a/app/code/Magento/PageCache/Test/Unit/Observer/FlushCacheByTagsTest.php b/app/code/Magento/PageCache/Test/Unit/Observer/FlushCacheByTagsTest.php index 8019c6b2e810f..cc3df162fd35a 100644 --- a/app/code/Magento/PageCache/Test/Unit/Observer/FlushCacheByTagsTest.php +++ b/app/code/Magento/PageCache/Test/Unit/Observer/FlushCacheByTagsTest.php @@ -1,51 +1,66 @@ <?php /** - * * Copyright © Magento, Inc. All rights reserved. * See COPYING.txt for license details. */ namespace Magento\PageCache\Test\Unit\Observer; -class FlushCacheByTagsTest extends \PHPUnit\Framework\TestCase -{ - /** @var \Magento\PageCache\Observer\FlushCacheByTags */ - protected $_model; +use Magento\Framework\App\Cache\Tag\Resolver; +use Magento\Framework\Event; +use Magento\Framework\Event\Observer; +use Magento\Framework\TestFramework\Unit\Helper\ObjectManager; +use Magento\PageCache\Model\Cache\Type; +use Magento\PageCache\Model\Config; +use Magento\PageCache\Observer\FlushCacheByTags; +use Magento\Store\Model\Store; +use PHPUnit\Framework\MockObject\MockObject; +use PHPUnit\Framework\TestCase; - /** @var \PHPUnit_Framework_MockObject_MockObject|\Magento\PageCache\Model\Config */ - protected $_configMock; +/** + * Test class for \Magento\PageCache\Observer\FlushCacheByTags + */ +class FlushCacheByTagsTest extends TestCase +{ + /** + * @var FlushCacheByTags + */ + private $model; - /** @var \PHPUnit_Framework_MockObject_MockObject|\Magento\Framework\App\PageCache\Cache */ - protected $_cacheMock; + /** + * @var Config|MockObject + */ + private $configMock; - /** @var \PHPUnit_Framework_MockObject_MockObject|\Magento\PageCache\Model\Cache\Type */ + /** + * @var Type|MockObject + */ private $fullPageCacheMock; - /** @var \PHPUnit_Framework_MockObject_MockObject|\Magento\Framework\App\Cache\Tag\Resolver */ - private $tagResolver; + /** + * @var Resolver|MockObject + */ + private $tagResolverMock; /** - * Set up all mocks and data for test + * @inheritDoc */ protected function setUp() { - $helper = new \Magento\Framework\TestFramework\Unit\Helper\ObjectManager($this); - $this->_configMock = $this->createPartialMock(\Magento\PageCache\Model\Config::class, ['getType', 'isEnabled']); - $this->_cacheMock = $this->createPartialMock(\Magento\Framework\App\PageCache\Cache::class, ['clean']); - $this->fullPageCacheMock = $this->createPartialMock(\Magento\PageCache\Model\Cache\Type::class, ['clean']); - - $this->_model = new \Magento\PageCache\Observer\FlushCacheByTags( - $this->_configMock, - $this->_cacheMock + $this->configMock = $this->createPartialMock(Config::class, ['getType', 'isEnabled']); + $this->fullPageCacheMock = $this->createPartialMock(Type::class, ['clean']); + + $this->tagResolverMock = $this->createMock(Resolver::class); + + $objectManager = new ObjectManager($this); + $this->model = $objectManager->getObject( + FlushCacheByTags::class, + [ + 'config' => $this->configMock, + 'fullPageCache' => $this->fullPageCacheMock, + 'tagResolver' => $this->tagResolverMock + ] ); - - $this->tagResolver = $this->createMock(\Magento\Framework\App\Cache\Tag\Resolver::class); - - $helper->setBackwardCompatibleProperty($this->_model, 'tagResolver', $this->tagResolver); - $reflection = new \ReflectionClass(\Magento\PageCache\Observer\FlushCacheByTags::class); - $reflectionProperty = $reflection->getProperty('fullPageCache'); - $reflectionProperty->setAccessible(true); - $reflectionProperty->setValue($this->_model, $this->fullPageCacheMock); } /** @@ -56,28 +71,28 @@ protected function setUp() */ public function testExecute($cacheState) { - $this->_configMock->expects($this->any())->method('isEnabled')->will($this->returnValue($cacheState)); - $observerObject = $this->createMock(\Magento\Framework\Event\Observer::class); - $observedObject = $this->createMock(\Magento\Store\Model\Store::class); + $this->configMock->method('isEnabled')->willReturn($cacheState); + $observerObject = $this->createMock(Observer::class); + $observedObject = $this->createMock(Store::class); if ($cacheState) { $tags = ['cache_1', 'cache_group']; $expectedTags = ['cache_1', 'cache_group']; - $eventMock = $this->createPartialMock(\Magento\Framework\Event::class, ['getObject']); - $eventMock->expects($this->once())->method('getObject')->will($this->returnValue($observedObject)); - $observerObject->expects($this->once())->method('getEvent')->will($this->returnValue($eventMock)); - $this->_configMock->expects($this->once()) + $eventMock = $this->createPartialMock(Event::class, ['getObject']); + $eventMock->expects($this->once())->method('getObject')->willReturn($observedObject); + $observerObject->expects($this->once())->method('getEvent')->willReturn($eventMock); + $this->configMock->expects($this->once()) ->method('getType') - ->willReturn(\Magento\PageCache\Model\Config::BUILT_IN); - $this->tagResolver->expects($this->once())->method('getTags')->will($this->returnValue($tags)); + ->willReturn(Config::BUILT_IN); + $this->tagResolverMock->expects($this->once())->method('getTags')->willReturn($tags); $this->fullPageCacheMock->expects($this->once()) ->method('clean') ->with(\Zend_Cache::CLEANING_MODE_MATCHING_ANY_TAG, $this->equalTo($expectedTags)); } - $result = $this->_model->execute($observerObject); + $result = $this->model->execute($observerObject); $this->assertNull($result); } @@ -92,28 +107,31 @@ public function flushCacheByTagsDataProvider() ]; } + /** + * Test case for cache invalidation with empty tags + */ public function testExecuteWithEmptyTags() { - $this->_configMock->expects($this->any())->method('isEnabled')->will($this->returnValue(true)); - $observerObject = $this->createMock(\Magento\Framework\Event\Observer::class); - $observedObject = $this->createMock(\Magento\Store\Model\Store::class); + $this->configMock->method('isEnabled')->willReturn(true); + $observerObject = $this->createMock(Observer::class); + $observedObject = $this->createMock(Store::class); $tags = []; - $eventMock = $this->createPartialMock(\Magento\Framework\Event::class, ['getObject']); - $eventMock->expects($this->once())->method('getObject')->will($this->returnValue($observedObject)); - $observerObject->expects($this->once())->method('getEvent')->will($this->returnValue($eventMock)); - $this->_configMock->expects( + $eventMock = $this->createPartialMock(Event::class, ['getObject']); + $eventMock->expects($this->once())->method('getObject')->willReturn($observedObject); + $observerObject->expects($this->once())->method('getEvent')->willReturn($eventMock); + $this->configMock->expects( $this->once() )->method( 'getType' - )->will( - $this->returnValue(\Magento\PageCache\Model\Config::BUILT_IN) + )->willReturn( + Config::BUILT_IN ); - $this->tagResolver->expects($this->once())->method('getTags')->will($this->returnValue($tags)); + $this->tagResolverMock->expects($this->once())->method('getTags')->willReturn($tags); $this->fullPageCacheMock->expects($this->never())->method('clean'); - $this->_model->execute($observerObject); + $this->model->execute($observerObject); } } diff --git a/app/code/Magento/PageCache/etc/adminhtml/system.xml b/app/code/Magento/PageCache/etc/adminhtml/system.xml index 234e3e48a95d8..4ffc20958748d 100644 --- a/app/code/Magento/PageCache/etc/adminhtml/system.xml +++ b/app/code/Magento/PageCache/etc/adminhtml/system.xml @@ -8,15 +8,15 @@ <config xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="urn:magento:module:Magento_Config:etc/system_file.xsd"> <system> <section id="system"> - <group id="full_page_cache" translate="label" showInDefault="1" showInWebsite="0" showInStore="0" sortOrder="600"> + <group id="full_page_cache" translate="label" showInDefault="1" sortOrder="600"> <label>Full Page Cache</label> - <field id="caching_application" translate="label" type="select" sortOrder="0" showInDefault="1" showInWebsite="0" showInStore="0" canRestore="1"> + <field id="caching_application" translate="label" type="select" sortOrder="0" showInDefault="1" canRestore="1"> <label>Caching Application</label> <source_model>Magento\PageCache\Model\System\Config\Source\Application</source_model> </field> - <group id="varnish" translate="label" showInDefault="1" showInWebsite="0" showInStore="0" sortOrder="605"> + <group id="varnish" translate="label" showInDefault="1" sortOrder="605"> <label>Varnish Configuration</label> - <field id="access_list" type="text" translate="label comment" sortOrder="15" showInDefault="1" showInWebsite="0" showInStore="0"> + <field id="access_list" type="text" translate="label comment" sortOrder="15" showInDefault="1"> <label>Access list</label> <comment>IPs access list separated with ',' that can purge Varnish configuration for config file generation. If field is empty default value localhost will be saved.</comment> @@ -25,7 +25,7 @@ <field id="caching_application">1</field> </depends> </field> - <field id="backend_host" type="text" translate="label comment" sortOrder="20" showInDefault="1" showInWebsite="0" showInStore="0"> + <field id="backend_host" type="text" translate="label comment" sortOrder="20" showInDefault="1"> <label>Backend host</label> <comment>Specify backend host for config file generation. If field is empty default value localhost will be saved.</comment> <backend_model>Magento\PageCache\Model\System\Config\Backend\Varnish</backend_model> @@ -33,7 +33,7 @@ <field id="caching_application">1</field> </depends> </field> - <field id="backend_port" type="text" translate="label comment" sortOrder="25" showInDefault="1" showInWebsite="0" showInStore="0"> + <field id="backend_port" type="text" translate="label comment" sortOrder="25" showInDefault="1"> <label>Backend port</label> <comment>Specify backend port for config file generation. If field is empty default value 8080 will be saved.</comment> <backend_model>Magento\PageCache\Model\System\Config\Backend\Varnish</backend_model> @@ -41,7 +41,7 @@ <field id="caching_application">1</field> </depends> </field> - <field id="grace_period" type="text" translate="label comment" sortOrder="30" showInDefault="1" showInWebsite="0" showInStore="0"> + <field id="grace_period" type="text" translate="label comment" sortOrder="30" showInDefault="1"> <label>Grace period</label> <comment>Specify grace period in seconds for config file generation. If field is empty default value 300 will be saved. This grace period will be used to serve cached content when the server is healthy. If the server is not healthy, cached content will be served for 3 days before failing.</comment> <backend_model>Magento\PageCache\Model\System\Config\Backend\Varnish</backend_model> @@ -49,20 +49,20 @@ <field id="caching_application">1</field> </depends> </field> - <field id="export_button_version4" translate="label" type="button" sortOrder="35" showInDefault="1" showInWebsite="0" showInStore="0"> + <field id="export_button_version4" translate="label" type="button" sortOrder="35" showInDefault="1"> <label>Export Configuration</label> <frontend_model>Magento\PageCache\Block\System\Config\Form\Field\Export\Varnish4</frontend_model> <depends> <field id="caching_application">1</field> </depends> </field> - <field id="export_button_version5" type="button" sortOrder="40" showInDefault="1" showInWebsite="0" showInStore="0"> + <field id="export_button_version5" type="button" sortOrder="40" showInDefault="1"> <frontend_model>Magento\PageCache\Block\System\Config\Form\Field\Export\Varnish5</frontend_model> <depends> <field id="caching_application">1</field> </depends> </field> - <field id="export_button_version6" type="button" sortOrder="40" showInDefault="1" showInWebsite="0" showInStore="0"> + <field id="export_button_version6" type="button" sortOrder="40" showInDefault="1"> <frontend_model>Magento\PageCache\Block\System\Config\Form\Field\Export\Varnish6</frontend_model> <depends> <field id="caching_application">1</field> @@ -72,7 +72,7 @@ <field id="caching_application">2</field> </depends> </group> - <field id="ttl" type="text" translate="label comment" sortOrder="5" showInDefault="1" showInWebsite="0" showInStore="0" canRestore="1"> + <field id="ttl" type="text" translate="label comment" sortOrder="5" showInDefault="1" canRestore="1"> <label>TTL for public content</label> <validate>validate-zero-or-greater validate-digits</validate> <comment>Public content cache lifetime in seconds. If field is empty default value 86400 will be saved. </comment> diff --git a/app/code/Magento/PageCache/etc/varnish4.vcl b/app/code/Magento/PageCache/etc/varnish4.vcl index 6de6b4e917044..f5e25ce36e973 100644 --- a/app/code/Magento/PageCache/etc/varnish4.vcl +++ b/app/code/Magento/PageCache/etc/varnish4.vcl @@ -108,6 +108,11 @@ sub vcl_recv { #unset req.http.Cookie; } + # Authenticated GraphQL requests should not be cached by default + if (req.url ~ "/graphql" && req.http.Authorization ~ "^Bearer") { + return (pass); + } + return (hash); } diff --git a/app/code/Magento/PageCache/etc/varnish5.vcl b/app/code/Magento/PageCache/etc/varnish5.vcl index 4505e74629714..92bb3394486fc 100644 --- a/app/code/Magento/PageCache/etc/varnish5.vcl +++ b/app/code/Magento/PageCache/etc/varnish5.vcl @@ -109,6 +109,11 @@ sub vcl_recv { #unset req.http.Cookie; } + # Authenticated GraphQL requests should not be cached by default + if (req.url ~ "/graphql" && req.http.Authorization ~ "^Bearer") { + return (pass); + } + return (hash); } diff --git a/app/code/Magento/PageCache/etc/varnish6.vcl b/app/code/Magento/PageCache/etc/varnish6.vcl index b43c8a77bca74..eef5e99862538 100644 --- a/app/code/Magento/PageCache/etc/varnish6.vcl +++ b/app/code/Magento/PageCache/etc/varnish6.vcl @@ -109,6 +109,11 @@ sub vcl_recv { #unset req.http.Cookie; } + # Authenticated GraphQL requests should not be cached by default + if (req.url ~ "/graphql" && req.http.Authorization ~ "^Bearer") { + return (pass); + } + return (hash); } diff --git a/app/code/Magento/PageCache/registration.php b/app/code/Magento/PageCache/registration.php index 32628ae4446c9..69d49f120d4cf 100644 --- a/app/code/Magento/PageCache/registration.php +++ b/app/code/Magento/PageCache/registration.php @@ -4,6 +4,6 @@ * See COPYING.txt for license details. */ -use \Magento\Framework\Component\ComponentRegistrar; +use Magento\Framework\Component\ComponentRegistrar; ComponentRegistrar::register(ComponentRegistrar::MODULE, 'Magento_PageCache', __DIR__); diff --git a/app/code/Magento/Payment/Block/Transparent/Redirect.php b/app/code/Magento/Payment/Block/Transparent/Redirect.php new file mode 100644 index 0000000000000..1be6dec4cc1d8 --- /dev/null +++ b/app/code/Magento/Payment/Block/Transparent/Redirect.php @@ -0,0 +1,62 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +namespace Magento\Payment\Block\Transparent; + +use Magento\Framework\UrlInterface; +use Magento\Framework\View\Element\Template; +use Magento\Framework\View\Element\Template\Context; + +/** + * Redirect block for register specific params in layout + * + * @api + */ +class Redirect extends Template +{ + /** + * Route path key to make redirect url. + */ + private const ROUTE_PATH = 'route_path'; + + /** + * @var UrlInterface + */ + private $url; + + /** + * @param Context $context + * @param UrlInterface $url + * @param array $data + */ + public function __construct( + Context $context, + UrlInterface $url, + array $data = [] + ) { + $this->url = $url; + parent::__construct($context, $data); + } + + /** + * Returns url for redirect. + * + * @return string + */ + public function getRedirectUrl(): string + { + return $this->url->getUrl($this->getData(self::ROUTE_PATH)); + } + + /** + * Returns params to be redirected. + * + * @return array + */ + public function getPostParams(): array + { + return (array)$this->_request->getPostValue(); + } +} diff --git a/app/code/Magento/Payment/Gateway/Data/Order/AddressAdapter.php b/app/code/Magento/Payment/Gateway/Data/Order/AddressAdapter.php index f4b05df2e8a20..5b1ee10395a96 100644 --- a/app/code/Magento/Payment/Gateway/Data/Order/AddressAdapter.php +++ b/app/code/Magento/Payment/Gateway/Data/Order/AddressAdapter.php @@ -9,7 +9,7 @@ use Magento\Sales\Api\Data\OrderAddressInterface; /** - * Class AddressAdapter + * @inheritdoc */ class AddressAdapter implements AddressAdapterInterface { @@ -54,7 +54,7 @@ public function getCountryId() public function getStreetLine1() { $street = $this->address->getStreet(); - return isset($street[0]) ? $street[0]: ''; + return $street[0] ?? ''; } /** @@ -65,7 +65,7 @@ public function getStreetLine1() public function getStreetLine2() { $street = $this->address->getStreet(); - return isset($street[1]) ? $street[1]: ''; + return $street[1] ?? ''; } /** diff --git a/app/code/Magento/Payment/Gateway/Data/Quote/AddressAdapter.php b/app/code/Magento/Payment/Gateway/Data/Quote/AddressAdapter.php index 3c8d5899a1083..57805cee16d30 100644 --- a/app/code/Magento/Payment/Gateway/Data/Quote/AddressAdapter.php +++ b/app/code/Magento/Payment/Gateway/Data/Quote/AddressAdapter.php @@ -9,7 +9,7 @@ use Magento\Quote\Api\Data\AddressInterface; /** - * Class AddressAdapter + * @inheritdoc */ class AddressAdapter implements AddressAdapterInterface { @@ -54,7 +54,7 @@ public function getCountryId() public function getStreetLine1() { $street = $this->address->getStreet(); - return isset($street[0]) ? $street[0]: ''; + return $street[0] ?? ''; } /** @@ -65,7 +65,7 @@ public function getStreetLine1() public function getStreetLine2() { $street = $this->address->getStreet(); - return isset($street[1]) ? $street[1]: ''; + return $street[1] ?? ''; } /** diff --git a/app/code/Magento/Payment/Gateway/Validator/ValidatorComposite.php b/app/code/Magento/Payment/Gateway/Validator/ValidatorComposite.php index 8ea97d31ed4d9..f96c08a9605a8 100644 --- a/app/code/Magento/Payment/Gateway/Validator/ValidatorComposite.php +++ b/app/code/Magento/Payment/Gateway/Validator/ValidatorComposite.php @@ -3,6 +3,7 @@ * Copyright © Magento, Inc. All rights reserved. * See COPYING.txt for license details. */ + namespace Magento\Payment\Gateway\Validator; use Magento\Framework\ObjectManager\TMap; @@ -57,26 +58,25 @@ public function __construct( public function validate(array $validationSubject) { $isValid = true; - $failsDescriptionAggregate = []; - $errorCodesAggregate = []; + $failsDescriptionAggregate = [[]]; + $errorCodesAggregate = [[]]; foreach ($this->validators as $key => $validator) { $result = $validator->validate($validationSubject); if (!$result->isValid()) { $isValid = false; - $failsDescriptionAggregate = array_merge( - $failsDescriptionAggregate, - $result->getFailsDescription() - ); - $errorCodesAggregate = array_merge( - $errorCodesAggregate, - $result->getErrorCodes() - ); + $failsDescriptionAggregate[] = $result->getFailsDescription(); + $errorCodesAggregate[] = $result->getErrorCodes(); + if (!empty($this->chainBreakingValidators[$key])) { break; } } } - return $this->createResult($isValid, $failsDescriptionAggregate, $errorCodesAggregate); + return $this->createResult( + $isValid, + array_merge(...$failsDescriptionAggregate), + array_merge(...$errorCodesAggregate) + ); } } diff --git a/app/code/Magento/Payment/Model/Info.php b/app/code/Magento/Payment/Model/Info.php index 3b7f93be776ee..79a26ef4ed45f 100644 --- a/app/code/Magento/Payment/Model/Info.php +++ b/app/code/Magento/Payment/Model/Info.php @@ -38,7 +38,7 @@ class Info extends AbstractExtensibleModel implements InfoInterface * @param \Magento\Framework\Model\Context $context * @param \Magento\Framework\Registry $registry * @param \Magento\Framework\Api\ExtensionAttributesFactory $extensionFactory - * @param \Magento\Framework\Api\AttributeValueFactory $customAttributeFactory, + * @param \Magento\Framework\Api\AttributeValueFactory $customAttributeFactory * @param \Magento\Payment\Helper\Data $paymentData * @param \Magento\Framework\Encryption\EncryptorInterface $encryptor * @param \Magento\Framework\Model\ResourceModel\AbstractResource $resource @@ -177,7 +177,7 @@ public function getAdditionalInformation($key = null) if (null === $key) { return $this->_additionalInformation; } - return isset($this->_additionalInformation[$key]) ? $this->_additionalInformation[$key] : null; + return $this->_additionalInformation[$key] ?? null; } /** @@ -188,6 +188,7 @@ public function getAdditionalInformation($key = null) */ public function unsAdditionalInformation($key = null) { + $this->_initAdditionalInformation(); if ($key && isset($this->_additionalInformation[$key])) { unset($this->_additionalInformation[$key]); return $this->setData('additional_information', $this->_additionalInformation); diff --git a/app/code/Magento/Payment/Model/Method/Adapter.php b/app/code/Magento/Payment/Model/Method/Adapter.php index 6c7ef372a8854..dba48efaae837 100644 --- a/app/code/Magento/Payment/Model/Method/Adapter.php +++ b/app/code/Magento/Payment/Model/Method/Adapter.php @@ -17,6 +17,7 @@ use Magento\Payment\Gateway\Validator\ValidatorPoolInterface; use Magento\Payment\Model\InfoInterface; use Magento\Payment\Model\MethodInterface; +use Magento\Payment\Model\SaleOperationInterface; use Magento\Payment\Observer\AbstractDataAssignObserver; use Magento\Quote\Api\Data\CartInterface; use Psr\Log\LoggerInterface; @@ -30,7 +31,7 @@ * @api Use this class as a base for virtual types declaration * @since 100.0.2 */ -class Adapter implements MethodInterface +class Adapter implements MethodInterface, SaleOperationInterface { /** * @var ValueHandlerPoolInterface @@ -296,6 +297,7 @@ public function isAvailable(CartInterface $quote = null) $checkResult->setData('is_available', $result->isValid()); } + // phpcs:ignore Magento2.CodeAnalysis.EmptyBlock } catch (\Exception $e) { // pass } @@ -366,7 +368,7 @@ private function canPerformCommand($commandCode) * Unifies configured value handling logic * * @param string $field - * @param null $storeId + * @param int|null $storeId * @return mixed */ private function getConfiguredValue($field, $storeId = null) @@ -663,4 +665,23 @@ public function getConfigPaymentAction() { return $this->getConfiguredValue('payment_action'); } + + /** + * @inheritdoc + */ + public function canSale(): bool + { + return $this->canPerformCommand('sale'); + } + + /** + * @inheritdoc + */ + public function sale(InfoInterface $payment, float $amount) + { + $this->executeCommand( + 'sale', + ['payment' => $payment, 'amount' => $amount] + ); + } } diff --git a/app/code/Magento/Payment/Model/MethodList.php b/app/code/Magento/Payment/Model/MethodList.php index 5a426d72e4cfd..746306cbd0bbf 100644 --- a/app/code/Magento/Payment/Model/MethodList.php +++ b/app/code/Magento/Payment/Model/MethodList.php @@ -39,19 +39,29 @@ class MethodList */ private $paymentMethodInstanceFactory; + /** + * @var array + */ + private $additionalChecks; + /** * @param \Magento\Payment\Helper\Data $paymentHelper - * @param Checks\SpecificationFactory $specificationFactory + * @param Checks\SpecificationFactory $specificationFactory + * @param array $additionalChecks */ public function __construct( \Magento\Payment\Helper\Data $paymentHelper, - \Magento\Payment\Model\Checks\SpecificationFactory $specificationFactory + \Magento\Payment\Model\Checks\SpecificationFactory $specificationFactory, + array $additionalChecks = [] ) { $this->paymentHelper = $paymentHelper; $this->methodSpecificationFactory = $specificationFactory; + $this->additionalChecks = $additionalChecks; } /** + * Returns all available payment methods for the given quote. + * * @param \Magento\Quote\Api\Data\CartInterface $quote * @return \Magento\Payment\Model\MethodInterface[] */ @@ -80,12 +90,15 @@ public function getAvailableMethods(\Magento\Quote\Api\Data\CartInterface $quote protected function _canUseMethod($method, \Magento\Quote\Api\Data\CartInterface $quote) { return $this->methodSpecificationFactory->create( - [ - AbstractMethod::CHECK_USE_CHECKOUT, - AbstractMethod::CHECK_USE_FOR_COUNTRY, - AbstractMethod::CHECK_USE_FOR_CURRENCY, - AbstractMethod::CHECK_ORDER_TOTAL_MIN_MAX, - ] + array_merge( + [ + AbstractMethod::CHECK_USE_CHECKOUT, + AbstractMethod::CHECK_USE_FOR_COUNTRY, + AbstractMethod::CHECK_USE_FOR_CURRENCY, + AbstractMethod::CHECK_ORDER_TOTAL_MIN_MAX, + ], + $this->additionalChecks + ) )->isApplicable( $method, $quote diff --git a/app/code/Magento/Payment/Model/SaleOperationInterface.php b/app/code/Magento/Payment/Model/SaleOperationInterface.php new file mode 100644 index 0000000000000..384913a75ae26 --- /dev/null +++ b/app/code/Magento/Payment/Model/SaleOperationInterface.php @@ -0,0 +1,32 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +declare(strict_types=1); + +namespace Magento\Payment\Model; + +/** + * Responsible for support of `sale` payment operation via Magento payment provider gateway. + * + * @api + */ +interface SaleOperationInterface +{ + /** + * Checks `sale` payment operation availability. + * + * @return bool + */ + public function canSale(): bool; + + /** + * Executes `sale` payment operation. + * + * @param InfoInterface $payment + * @param float $amount + * @return void + */ + public function sale(InfoInterface $payment, float $amount); +} diff --git a/app/code/Magento/Payment/Test/Mftf/Metadata/payment_method-meta.xml b/app/code/Magento/Payment/Test/Mftf/Metadata/PaymentMethodMeta.xml similarity index 100% rename from app/code/Magento/Payment/Test/Mftf/Metadata/payment_method-meta.xml rename to app/code/Magento/Payment/Test/Mftf/Metadata/PaymentMethodMeta.xml diff --git a/app/code/Magento/Payment/Test/Unit/Gateway/Data/PaymentDataObjectTest.php b/app/code/Magento/Payment/Test/Unit/Gateway/Data/PaymentDataObjectTest.php index dc9a51b30cfea..72c35a192119f 100644 --- a/app/code/Magento/Payment/Test/Unit/Gateway/Data/PaymentDataObjectTest.php +++ b/app/code/Magento/Payment/Test/Unit/Gateway/Data/PaymentDataObjectTest.php @@ -5,46 +5,62 @@ */ namespace Magento\Payment\Test\Unit\Gateway\Data; -use Magento\Payment\Gateway\Data\PaymentDataObject; use Magento\Payment\Gateway\Data\OrderAdapterInterface; +use Magento\Payment\Gateway\Data\PaymentDataObject; use Magento\Payment\Model\InfoInterface; +use PHPUnit\Framework\MockObject\MockObject; /** - * Class PaymentDataObjectTest + * Tests for PaymentDataObject */ class PaymentDataObjectTest extends \PHPUnit\Framework\TestCase { - /** @var PaymentDataObject */ + /** + * @var PaymentDataObject + */ protected $model; /** - * @var OrderAdapterInterface|\PHPUnit_Framework_MockObject_MockObject + * @var OrderAdapterInterface|MockObject */ protected $orderMock; /** - * @var InfoInterface|\PHPUnit_Framework_MockObject_MockObject + * @var InfoInterface|\MockObject */ protected $paymentMock; + /** + * @inheritdoc + */ protected function setUp() { - $this->orderMock = $this->getMockBuilder(\Magento\Payment\Gateway\Data\OrderAdapterInterface::class) + $this->orderMock = $this->getMockBuilder(OrderAdapterInterface::class) ->getMockForAbstractClass(); - $this->paymentMock = $this->getMockBuilder(\Magento\Payment\Model\InfoInterface::class) + $this->paymentMock = $this->getMockBuilder(InfoInterface::class) ->getMockForAbstractClass(); $this->model = new PaymentDataObject($this->orderMock, $this->paymentMock); } - public function testGetOrder() + /** + * Verify can get order + * + * @return void + */ + public function testGetOrder(): void { - $this->assertSame($this->orderMock, $this->model->getOrder()) ; + $this->assertSame($this->orderMock, $this->model->getOrder()); } - public function testGetPayment() + /** + * Verify can get payment + * + * @return void + */ + public function testGetPayment(): void { - $this->assertSame($this->paymentMock, $this->model->getPayment()) ; + $this->assertSame($this->paymentMock, $this->model->getPayment()); } } diff --git a/app/code/Magento/Payment/Test/Unit/Model/MethodListTest.php b/app/code/Magento/Payment/Test/Unit/Model/MethodListTest.php index 3f4af635974e8..ee42b2bd03507 100644 --- a/app/code/Magento/Payment/Test/Unit/Model/MethodListTest.php +++ b/app/code/Magento/Payment/Test/Unit/Model/MethodListTest.php @@ -6,8 +6,8 @@ namespace Magento\Payment\Test\Unit\Model; -use Magento\Payment\Model\MethodList; use Magento\Payment\Model\Method\AbstractMethod; +use Magento\Payment\Model\MethodList; class MethodListTest extends \PHPUnit\Framework\TestCase { @@ -36,6 +36,11 @@ class MethodListTest extends \PHPUnit\Framework\TestCase */ protected $specificationFactoryMock; + /** + * @var array $additionalChecks + */ + private $additionalChecks; + protected function setUp() { $this->objectManager = new \Magento\Framework\TestFramework\Unit\Helper\ObjectManager($this); @@ -49,10 +54,14 @@ protected function setUp() )->disableOriginalConstructor()->getMock(); $this->specificationFactoryMock = $this->createMock(\Magento\Payment\Model\Checks\SpecificationFactory::class); + + $this->additionalChecks = ['acme_custom_payment_method_check' => 'acme_custom_payment_method_check']; + $this->methodList = $this->objectManager->getObject( \Magento\Payment\Model\MethodList::class, [ - 'specificationFactory' => $this->specificationFactoryMock + 'specificationFactory' => $this->specificationFactoryMock, + 'additionalChecks' => $this->additionalChecks ] ); @@ -68,6 +77,9 @@ protected function setUp() ); } + /** + * Verify available payment methods + */ public function testGetAvailableMethods() { $storeId = 1; @@ -90,13 +102,17 @@ public function testGetAvailableMethods() $this->specificationFactoryMock->expects($this->atLeastOnce()) ->method('create') - ->with([ - AbstractMethod::CHECK_USE_CHECKOUT, - AbstractMethod::CHECK_USE_FOR_COUNTRY, - AbstractMethod::CHECK_USE_FOR_CURRENCY, - AbstractMethod::CHECK_ORDER_TOTAL_MIN_MAX - ]) - ->will($this->returnValue($compositeMock)); + ->with( + array_merge( + [ + AbstractMethod::CHECK_USE_CHECKOUT, + AbstractMethod::CHECK_USE_FOR_COUNTRY, + AbstractMethod::CHECK_USE_FOR_CURRENCY, + AbstractMethod::CHECK_ORDER_TOTAL_MIN_MAX + ], + $this->additionalChecks + ) + )->will($this->returnValue($compositeMock)); $methodMock = $this->getMockForAbstractClass(\Magento\Payment\Api\Data\PaymentMethodInterface::class); $this->paymentMethodList->expects($this->once()) diff --git a/app/code/Magento/Payment/etc/adminhtml/system.xml b/app/code/Magento/Payment/etc/adminhtml/system.xml index d168dc13a397b..1e8b617d31326 100644 --- a/app/code/Magento/Payment/etc/adminhtml/system.xml +++ b/app/code/Magento/Payment/etc/adminhtml/system.xml @@ -13,33 +13,33 @@ <resource>Magento_Payment::payment</resource> <group id="free" translate="label" type="text" sortOrder="30" showInDefault="1" showInWebsite="1" showInStore="1"> <label>Zero Subtotal Checkout</label> - <field id="active" translate="label" type="select" sortOrder="1" showInDefault="1" showInWebsite="1" showInStore="0" canRestore="1"> + <field id="active" translate="label" type="select" sortOrder="1" showInDefault="1" showInWebsite="1" canRestore="1"> <label>Enabled</label> <source_model>Magento\Config\Model\Config\Source\Yesno</source_model> </field> - <field id="order_status" translate="label" type="select" sortOrder="2" showInDefault="1" showInWebsite="1" showInStore="0" canRestore="1"> + <field id="order_status" translate="label" type="select" sortOrder="2" showInDefault="1" showInWebsite="1" canRestore="1"> <label>New Order Status</label> <source_model>Magento\Sales\Model\Config\Source\Order\Status\Newprocessing</source_model> </field> - <field id="payment_action" translate="label" type="select" sortOrder="3" showInDefault="1" showInWebsite="1" showInStore="0" canRestore="1"> + <field id="payment_action" translate="label" type="select" sortOrder="3" showInDefault="1" showInWebsite="1" canRestore="1"> <label>Automatically Invoice All Items</label> <source_model>Magento\Payment\Model\Source\Invoice</source_model> <depends> <field id="order_status" separator=",">processing</field> </depends> </field> - <field id="sort_order" translate="label" type="text" sortOrder="100" showInDefault="1" showInWebsite="1" showInStore="0" canRestore="1"> + <field id="sort_order" translate="label" type="text" sortOrder="100" showInDefault="1" showInWebsite="1" canRestore="1"> <label>Sort Order</label> <frontend_class>validate-number</frontend_class> </field> <field id="title" translate="label" type="text" sortOrder="1" showInDefault="1" showInWebsite="1" showInStore="1" canRestore="1"> <label>Title</label> </field> - <field id="allowspecific" translate="label" type="allowspecific" sortOrder="50" showInDefault="1" showInWebsite="1" showInStore="0" canRestore="1"> + <field id="allowspecific" translate="label" type="allowspecific" sortOrder="50" showInDefault="1" showInWebsite="1" canRestore="1"> <label>Payment from Applicable Countries</label> <source_model>Magento\Payment\Model\Config\Source\Allspecificcountries</source_model> </field> - <field id="specificcountry" translate="label" type="multiselect" sortOrder="51" showInDefault="1" showInWebsite="1" showInStore="0"> + <field id="specificcountry" translate="label" type="multiselect" sortOrder="51" showInDefault="1" showInWebsite="1"> <label>Payment from Specific Countries</label> <source_model>Magento\Directory\Model\Config\Source\Country</source_model> <can_be_empty>1</can_be_empty> diff --git a/app/code/Magento/Payment/registration.php b/app/code/Magento/Payment/registration.php index 12f826a2ad5ce..9b1fb56a89600 100644 --- a/app/code/Magento/Payment/registration.php +++ b/app/code/Magento/Payment/registration.php @@ -4,6 +4,6 @@ * See COPYING.txt for license details. */ -use \Magento\Framework\Component\ComponentRegistrar; +use Magento\Framework\Component\ComponentRegistrar; ComponentRegistrar::register(ComponentRegistrar::MODULE, 'Magento_Payment', __DIR__); diff --git a/app/code/Magento/Payment/view/adminhtml/templates/transparent/redirect.phtml b/app/code/Magento/Payment/view/adminhtml/templates/transparent/redirect.phtml new file mode 100644 index 0000000000000..17fbdf780a40a --- /dev/null +++ b/app/code/Magento/Payment/view/adminhtml/templates/transparent/redirect.phtml @@ -0,0 +1,21 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ + +/** @var \Magento\Payment\Block\Transparent\Redirect $block */ +$params = $block->getPostParams(); +$redirectUrl = $block->getRedirectUrl(); +?> +<html> +<head></head> +<body onload="document.forms['proxy_form'].submit()"> +<form id="proxy_form" action="<?= $block->escapeUrl($redirectUrl) ?>" + method="POST" hidden enctype="application/x-www-form-urlencoded" class="no-display"> + <?php foreach ($params as $name => $value):?> + <input value="<?= $block->escapeHtmlAttr($value) ?>" name="<?= $block->escapeHtmlAttr($name) ?>" type="hidden"/> + <?php endforeach?> +</form> +</body> +</html> diff --git a/app/code/Magento/Payment/view/frontend/templates/transparent/redirect.phtml b/app/code/Magento/Payment/view/frontend/templates/transparent/redirect.phtml new file mode 100644 index 0000000000000..17fbdf780a40a --- /dev/null +++ b/app/code/Magento/Payment/view/frontend/templates/transparent/redirect.phtml @@ -0,0 +1,21 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ + +/** @var \Magento\Payment\Block\Transparent\Redirect $block */ +$params = $block->getPostParams(); +$redirectUrl = $block->getRedirectUrl(); +?> +<html> +<head></head> +<body onload="document.forms['proxy_form'].submit()"> +<form id="proxy_form" action="<?= $block->escapeUrl($redirectUrl) ?>" + method="POST" hidden enctype="application/x-www-form-urlencoded" class="no-display"> + <?php foreach ($params as $name => $value):?> + <input value="<?= $block->escapeHtmlAttr($value) ?>" name="<?= $block->escapeHtmlAttr($name) ?>" type="hidden"/> + <?php endforeach?> +</form> +</body> +</html> diff --git a/app/code/Magento/Paypal/Block/Express/InContext/Minicart/SmartButton.php b/app/code/Magento/Paypal/Block/Express/InContext/Minicart/SmartButton.php index c6a17fa5efb9f..a4732a8b4c579 100644 --- a/app/code/Magento/Paypal/Block/Express/InContext/Minicart/SmartButton.php +++ b/app/code/Magento/Paypal/Block/Express/InContext/Minicart/SmartButton.php @@ -27,6 +27,8 @@ class SmartButton extends Template implements ShortcutInterface { private const ALIAS_ELEMENT_INDEX = 'alias'; + const PAYPAL_BUTTON_ID = 'paypal-express-in-context-checkout-main'; + /** * @var Config */ diff --git a/app/code/Magento/Paypal/Controller/Adminhtml/Transparent/Redirect.php b/app/code/Magento/Paypal/Controller/Adminhtml/Transparent/Redirect.php new file mode 100644 index 0000000000000..8201761cc3a29 --- /dev/null +++ b/app/code/Magento/Paypal/Controller/Adminhtml/Transparent/Redirect.php @@ -0,0 +1,13 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +namespace Magento\Paypal\Controller\Adminhtml\Transparent; + +/** + * Class for redirecting the Paypal response result to Magento controller. + */ +class Redirect extends \Magento\Paypal\Controller\Transparent\Redirect +{ +} diff --git a/app/code/Magento/Paypal/Controller/Hostedpro/Cancel.php b/app/code/Magento/Paypal/Controller/Hostedpro/Cancel.php index 629431e13879c..981d63b88225c 100644 --- a/app/code/Magento/Paypal/Controller/Hostedpro/Cancel.php +++ b/app/code/Magento/Paypal/Controller/Hostedpro/Cancel.php @@ -1,15 +1,24 @@ <?php /** - * * Copyright © Magento, Inc. All rights reserved. * See COPYING.txt for license details. */ +declare(strict_types=1); + namespace Magento\Paypal\Controller\Hostedpro; +use Magento\Framework\App\Action\Action; use Magento\Framework\App\Action\Context; +use Magento\Framework\App\Action\HttpGetActionInterface; +use Magento\Framework\App\CsrfAwareActionInterface; +use Magento\Framework\App\Request\InvalidRequestException; +use Magento\Framework\App\RequestInterface; use Magento\Paypal\Helper\Checkout; -class Cancel extends \Magento\Framework\App\Action\Action +/** + * PayPal Hostedpro cancel controller. + */ +class Cancel extends Action implements CsrfAwareActionInterface, HttpGetActionInterface { /** * @var Checkout @@ -40,4 +49,20 @@ public function execute() $this->_redirect('checkout', ['_fragment' => 'payment']); } + + /** + * @inheritDoc + */ + public function createCsrfValidationException(RequestInterface $request): ?InvalidRequestException + { + return null; + } + + /** + * @inheritDoc + */ + public function validateForCsrf(RequestInterface $request): ?bool + { + return true; + } } diff --git a/app/code/Magento/Paypal/Controller/Hostedpro/ReturnAction.php b/app/code/Magento/Paypal/Controller/Hostedpro/ReturnAction.php index 1643f480d25e9..bb8b5f8fa0b46 100644 --- a/app/code/Magento/Paypal/Controller/Hostedpro/ReturnAction.php +++ b/app/code/Magento/Paypal/Controller/Hostedpro/ReturnAction.php @@ -1,12 +1,23 @@ <?php /** - * * Copyright © Magento, Inc. All rights reserved. * See COPYING.txt for license details. */ +declare(strict_types=1); + namespace Magento\Paypal\Controller\Hostedpro; -class ReturnAction extends \Magento\Framework\App\Action\Action +use Magento\Framework\App\Action\Action; +use Magento\Framework\App\Action\HttpGetActionInterface; +use Magento\Framework\App\Action\HttpPostActionInterface; +use Magento\Framework\App\CsrfAwareActionInterface; +use Magento\Framework\App\Request\InvalidRequestException; +use Magento\Framework\App\RequestInterface; + +/** + * PayPal Hostedpro return controller. + */ +class ReturnAction extends Action implements CsrfAwareActionInterface, HttpPostActionInterface, HttpGetActionInterface { /** * When a customer return to website from gateway. @@ -21,4 +32,20 @@ public function execute() $this->_redirect('checkout/onepage/success'); } } + + /** + * @inheritDoc + */ + public function createCsrfValidationException(RequestInterface $request): ?InvalidRequestException + { + return null; + } + + /** + * @inheritDoc + */ + public function validateForCsrf(RequestInterface $request): ?bool + { + return true; + } } diff --git a/app/code/Magento/Paypal/Controller/Transparent/Redirect.php b/app/code/Magento/Paypal/Controller/Transparent/Redirect.php new file mode 100644 index 0000000000000..1b14a97990275 --- /dev/null +++ b/app/code/Magento/Paypal/Controller/Transparent/Redirect.php @@ -0,0 +1,97 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +namespace Magento\Paypal\Controller\Transparent; + +use Magento\Framework\App\Action\Action; +use Magento\Framework\App\Action\Context; +use Magento\Framework\App\Action\HttpPostActionInterface; +use Magento\Framework\App\CsrfAwareActionInterface; +use Magento\Framework\App\Request\InvalidRequestException; +use Magento\Framework\App\RequestInterface; +use Magento\Framework\Controller\ResultInterface; +use Magento\Framework\Exception\LocalizedException; +use Magento\Framework\View\Result\LayoutFactory; +use Magento\Payment\Model\Method\Logger; +use Magento\Paypal\Model\Payflow\Transparent; + +/** + * Class for redirecting the Paypal response result to Magento controller. + */ +class Redirect extends Action implements CsrfAwareActionInterface, HttpPostActionInterface +{ + /** + * @var LayoutFactory + */ + private $resultLayoutFactory; + + /** + * @var Transparent + */ + private $transparent; + + /** + * @var Logger + */ + private $logger; + + /** + * @param Context $context + * @param LayoutFactory $resultLayoutFactory + * @param Transparent $transparent + * @param Logger $logger + */ + public function __construct( + Context $context, + LayoutFactory $resultLayoutFactory, + Transparent $transparent, + Logger $logger + ) { + $this->resultLayoutFactory = $resultLayoutFactory; + $this->transparent = $transparent; + $this->logger = $logger; + + parent::__construct($context); + } + + /** + * @inheritdoc + */ + public function createCsrfValidationException( + RequestInterface $request + ): ?InvalidRequestException { + return null; + } + + /** + * @inheritdoc + */ + public function validateForCsrf(RequestInterface $request): ?bool + { + return true; + } + + /** + * Saves the payment in quote + * + * @return ResultInterface + * @throws LocalizedException + */ + public function execute() + { + $gatewayResponse = (array)$this->getRequest()->getPostValue(); + $this->logger->debug( + ['PayPal PayflowPro redirect:' => $gatewayResponse], + $this->transparent->getDebugReplacePrivateDataKeys(), + $this->transparent->getDebugFlag() + ); + + $resultLayout = $this->resultLayoutFactory->create(); + $resultLayout->addDefaultHandle(); + $resultLayout->getLayout()->getUpdate()->load(['transparent_payment_redirect']); + + return $resultLayout; + } +} diff --git a/app/code/Magento/Paypal/Model/AbstractIpn.php b/app/code/Magento/Paypal/Model/AbstractIpn.php index 14d8163fd3bb3..f3f8909ecbd97 100644 --- a/app/code/Magento/Paypal/Model/AbstractIpn.php +++ b/app/code/Magento/Paypal/Model/AbstractIpn.php @@ -8,6 +8,9 @@ use Magento\Framework\Exception\RemoteServiceUnavailableException; +/** + * Abstract Ipn class for paypal + */ class AbstractIpn { /** @@ -39,6 +42,11 @@ class AbstractIpn */ protected $_curlFactory; + /** + * @var \Psr\Log\LoggerInterface + */ + private $logger; + /** * @param \Magento\Paypal\Model\ConfigFactory $configFactory * @param \Psr\Log\LoggerInterface $logger @@ -68,7 +76,7 @@ public function getRequestData($key = null) if (null === $key) { return $this->_ipnRequest; } - return isset($this->_ipnRequest[$key]) ? $this->_ipnRequest[$key] : null; + return $this->_ipnRequest[$key] ?? null; } /** @@ -113,6 +121,7 @@ protected function _postBack() if ($response != 'VERIFIED') { $this->_addDebugData('postback', $postbackQuery); $this->_addDebugData('postback_result', $postbackResult); + // phpcs:ignore Magento2.Exceptions.DirectThrow throw new \Exception('PayPal IPN postback failure. See system.log for details.'); } } @@ -171,6 +180,8 @@ protected function _debug() } /** + * Adding debug data + * * @param string $key * @param array|string $value * @return $this diff --git a/app/code/Magento/Paypal/Model/Api/Nvp.php b/app/code/Magento/Paypal/Model/Api/Nvp.php index 2ec88df492fb9..9e4f7693f4bfb 100644 --- a/app/code/Magento/Paypal/Model/Api/Nvp.php +++ b/app/code/Magento/Paypal/Model/Api/Nvp.php @@ -1512,17 +1512,18 @@ protected function _applyStreetAndRegionWorkarounds(DataObject $address) } // attempt to fetch region_id from directory if ($address->getCountryId() && $address->getRegion()) { - $regions = $this->_countryFactory->create()->loadByCode( - $address->getCountryId() - )->getRegionCollection()->addRegionCodeOrNameFilter( - $address->getRegion() - )->setPageSize( - 1 - ); - $regionItems = $regions->getItems(); - $region = array_shift($regionItems); - $address->setRegionId($region->getId()); - $address->setExportedKeys(array_merge($address->getExportedKeys(), ['region_id'])); + $regions = $this->_countryFactory->create() + ->loadByCode($address->getCountryId()) + ->getRegionCollection() + ->addRegionCodeOrNameFilter($address->getRegion()) + ->setPageSize(1); + + if ($regions->count()) { + $regionItems = $regions->getItems(); + $region = array_shift($regionItems); + $address->setRegionId($region->getId()); + $address->setExportedKeys(array_merge($address->getExportedKeys(), ['region_id'])); + } } } @@ -1624,7 +1625,7 @@ protected function _filterPeriodUnit($value) case 'year': return 'Year'; default: - break; + return ''; } } @@ -1653,7 +1654,7 @@ protected function _filterBillingAgreementStatus($value) case 'active': return 'Active'; default: - break; + return ''; } } @@ -1694,7 +1695,7 @@ protected function _filterPaymentStatusFromNvpToInfo($value) case 'Voided': return \Magento\Paypal\Model\Info::PAYMENTSTATUS_VOIDED; default: - break; + return null; } } @@ -1712,7 +1713,7 @@ protected function _filterPaymentReviewAction($value) case \Magento\Paypal\Model\Pro::PAYMENT_REVIEW_DENY: return 'Deny'; default: - break; + return null; } } diff --git a/app/code/Magento/Paypal/Model/Config.php b/app/code/Magento/Paypal/Model/Config.php index e197218752bf5..6f6728ebfa47f 100644 --- a/app/code/Magento/Paypal/Model/Config.php +++ b/app/code/Magento/Paypal/Model/Config.php @@ -823,7 +823,7 @@ public function getCountryMethods($countryCode = null) if ($countryCode === null) { return $countryMethods; } - return isset($countryMethods[$countryCode]) ? $countryMethods[$countryCode] : $countryMethods['other']; + return $countryMethods[$countryCode] ?? $countryMethods['other']; } /** @@ -1330,6 +1330,7 @@ public function getPayflowproCcTypesAsOptionArray() * @param string $code * @return bool * @SuppressWarnings(PHPMD.BooleanGetMethodName) + * phpcs:disable Magento2.Functions.StaticFunction */ public static function getIsCreditCardMethod($code) { diff --git a/app/code/Magento/Paypal/Model/Config/Structure/PaymentSectionModifier.php b/app/code/Magento/Paypal/Model/Config/Structure/PaymentSectionModifier.php index f1c414e6faa9b..61410499e956e 100644 --- a/app/code/Magento/Paypal/Model/Config/Structure/PaymentSectionModifier.php +++ b/app/code/Magento/Paypal/Model/Config/Structure/PaymentSectionModifier.php @@ -3,6 +3,7 @@ * Copyright © Magento, Inc. All rights reserved. * See COPYING.txt for license details. */ + namespace Magento\Paypal\Model\Config\Structure; /** @@ -60,9 +61,9 @@ public function modify(array $initialStructure) unset($childData['children'][$moveInstruction['section']]); unset($moveInstruction['data']['displayIn']); $changedStructure - [$moveInstruction['parent']] - ['children'] - [$moveInstruction['section']] = $moveInstruction['data']; + [$moveInstruction['parent']] + ['children'] + [$moveInstruction['section']] = $moveInstruction['data']; } } if (!isset($moveInstructions[$childSection])) { @@ -83,7 +84,7 @@ public function modify(array $initialStructure) */ private function getMoveInstructions($section, $data) { - $moved = []; + $moved = [[]]; if (array_key_exists('children', $data)) { foreach ($data['children'] as $childSection => $childData) { @@ -91,23 +92,20 @@ private function getMoveInstructions($section, $data) if (isset($movedChildren[$childSection])) { unset($data['children'][$childSection]); } - $moved = array_merge($moved, $movedChildren); + $moved[] = $movedChildren; } } if (isset($data['displayIn']) && in_array($data['displayIn'], self::$specialGroups)) { - $moved = array_merge( - [ - $section => [ + $moved[] = [ + $section => [ 'parent' => $data['displayIn'], 'section' => $section, 'data' => $data - ] - ], - $moved - ); + ] + ]; } - return $moved; + return array_merge(...$moved); } } diff --git a/app/code/Magento/Paypal/Model/Info.php b/app/code/Magento/Paypal/Model/Info.php index bb2e9366e588f..effc72e5780f4 100644 --- a/app/code/Magento/Paypal/Model/Info.php +++ b/app/code/Magento/Paypal/Model/Info.php @@ -334,6 +334,7 @@ public function &exportFromPayment(\Magento\Payment\Model\InfoInterface $payment * * @param \Magento\Payment\Model\InfoInterface $payment * @return bool + * phpcs:disable Magento2.Functions.StaticFunction */ public static function isPaymentReviewRequired(\Magento\Payment\Model\InfoInterface $payment) { @@ -350,6 +351,7 @@ public static function isPaymentReviewRequired(\Magento\Payment\Model\InfoInterf * * @param \Magento\Payment\Model\InfoInterface $payment * @return bool + * phpcs:disable Magento2.Functions.StaticFunction */ public static function isFraudReviewAllowed(\Magento\Payment\Model\InfoInterface $payment) { @@ -365,6 +367,7 @@ public static function isFraudReviewAllowed(\Magento\Payment\Model\InfoInterface * * @param \Magento\Payment\Model\InfoInterface $payment * @return bool + * phpcs:disable Magento2.Functions.StaticFunction */ public static function isPaymentCompleted(\Magento\Payment\Model\InfoInterface $payment) { @@ -377,6 +380,7 @@ public static function isPaymentCompleted(\Magento\Payment\Model\InfoInterface $ * * @param \Magento\Payment\Model\InfoInterface $payment * @return bool + * phpcs:disable Magento2.Functions.StaticFunction */ public static function isPaymentSuccessful(\Magento\Payment\Model\InfoInterface $payment) { @@ -407,6 +411,7 @@ public static function isPaymentSuccessful(\Magento\Payment\Model\InfoInterface * * @param \Magento\Payment\Model\InfoInterface $payment * @return bool + * phpcs:disable Magento2.Functions.StaticFunction */ public static function isPaymentFailed(\Magento\Payment\Model\InfoInterface $payment) { @@ -431,6 +436,7 @@ public static function isPaymentFailed(\Magento\Payment\Model\InfoInterface $pay * @link https://cms.paypal.com/us/cgi-bin/?&cmd=_render-content&content_ID=developer/e_howto_html_IPNandPDTVariables * @link https://cms.paypal.com/us/cgi-bin/?&cmd=_render-content&content_ID=developer/e_howto_api_nvp_r_GetTransactionDetails * @SuppressWarnings(PHPMD.CyclomaticComplexity) + * phpcs:disable Magento2.Functions.StaticFunction */ public static function explainPendingReason($code) { @@ -480,6 +486,7 @@ public static function explainPendingReason($code) * @return string * @link https://cms.paypal.com/us/cgi-bin/?&cmd=_render-content&content_ID=developer/e_howto_html_IPNandPDTVariables * @link https://cms.paypal.com/us/cgi-bin/?&cmd=_render-content&content_ID=developer/e_howto_api_nvp_r_GetTransactionDetails + * phpcs:disable Magento2.Functions.StaticFunction */ public static function explainReasonCode($code) { @@ -518,9 +525,7 @@ public static function explainReasonCode($code) 'duplicate' => __('Buyer claims that a possible duplicate payment was made to the merchant.'), 'merchandise' => __('Buyer claims that the received merchandise is unsatisfactory, defective, or damaged.'), ]; - return isset($comments[$code]) - ? $comments[$code] - : __('Unknown reason. Please contact PayPal customer service.'); + return $comments[$code] ?? __('Unknown reason. Please contact PayPal customer service.'); } /** @@ -528,6 +533,7 @@ public static function explainReasonCode($code) * * @param string $code * @return bool; + * phpcs:disable Magento2.Functions.StaticFunction */ public static function isReversalDisputable($code) { @@ -542,7 +548,7 @@ public static function isReversalDisputable($code) 'chargeback_reimbursement' => false, 'chargeback_settlement' => false, ]; - return isset($listOfDisputeCodes[$code]) ? $listOfDisputeCodes[$code] : false; + return $listOfDisputeCodes[$code] ?? false; } /** @@ -611,9 +617,7 @@ protected function _getLabel($key) self::BUYER_TAX_ID_TYPE => __('Buyer\'s Tax ID Type'), ]; } - return isset($this->_labelCodesCache[self::ITEM_LABELS][$key]) - ? $this->_labelCodesCache[self::ITEM_LABELS][$key] - : ''; + return $this->_labelCodesCache[self::ITEM_LABELS][$key] ?? ''; } /** @@ -621,6 +625,7 @@ protected function _getLabel($key) * * @param string $key * @return string + * phpcs:disable Magento2.Functions.StaticFunction */ public static function getCaseTypeLabel($key) { @@ -664,6 +669,7 @@ protected function _getValue($value, $key) case self::BUYER_TAX_ID_TYPE: $outputValue = $this->_getBuyerIdTypeValue($outputValue); // fall-through intentional + // no break default: return $outputValue; } @@ -705,9 +711,7 @@ protected function _getAvsLabel($value) '4' => __('N/A. Address not checked, or acquirer had no response. Service not available'), ]; } - return isset($this->_labelCodesCache[self::PAYPAL_AVS_CODE][$value]) - ? $this->_labelCodesCache[self::PAYPAL_AVS_CODE][$value] - : $value; + return $this->_labelCodesCache[self::PAYPAL_AVS_CODE][$value] ?? $value; } /** @@ -737,9 +741,7 @@ protected function _getCvv2Label($value) '4' => __('N/A. Service not available'), ]; } - return isset($this->_labelCodesCache[self::PAYPAL_CVV_2_MATCH][$value]) - ? $this->_labelCodesCache[self::PAYPAL_CVV_2_MATCH][$value] - : $value; + return $this->_labelCodesCache[self::PAYPAL_CVV_2_MATCH][$value] ?? $value; } /** @@ -756,8 +758,6 @@ protected function _getBuyerIdTypeValue($code) self::BUYER_TAX_ID_TYPE_CPF => __('CPF'), ]; } - return isset($this->_labelCodesCache[self::BUYER_TAX_ID_TYPE][$code]) - ? $this->_labelCodesCache[self::BUYER_TAX_ID_TYPE][$code] - : ''; + return $this->_labelCodesCache[self::BUYER_TAX_ID_TYPE][$code] ?? ''; } } diff --git a/app/code/Magento/Paypal/Model/Ipn.php b/app/code/Magento/Paypal/Model/Ipn.php index 9107762c54b69..4c128adfbd00e 100644 --- a/app/code/Magento/Paypal/Model/Ipn.php +++ b/app/code/Magento/Paypal/Model/Ipn.php @@ -8,6 +8,7 @@ use Exception; use Magento\Framework\Exception\LocalizedException; +use Magento\Sales\Model\Order; use Magento\Sales\Model\Order\Email\Sender\CreditmemoSender; use Magento\Sales\Model\Order\Email\Sender\OrderSender; @@ -106,6 +107,7 @@ protected function _getConfig() $parameters = ['params' => [$methodCode, $order->getStoreId()]]; $this->_config = $this->_configFactory->create($parameters); if (!$this->_config->isMethodActive($methodCode) || !$this->_config->isMethodAvailable()) { + // phpcs:ignore Magento2.Exceptions.DirectThrow throw new Exception(sprintf('The "%s" method isn\'t available.', $methodCode)); } /** @link https://cms.paypal.com/cgi-bin/marketingweb?cmd=_render-content&content_ID= @@ -117,6 +119,7 @@ protected function _getConfig() } $receiver = $this->getRequestData('business') ?: $this->getRequestData('receiver_email'); if (strtolower($merchantEmail) != strtolower($receiver)) { + // phpcs:ignore Magento2.Exceptions.DirectThrow throw new Exception( sprintf( 'The requested "%s" and the configured "%s" merchant emails don\'t match.', @@ -140,6 +143,7 @@ protected function _getOrder() $incrementId = $this->getRequestData('invoice'); $this->_order = $this->_orderFactory->create()->loadByIncrementId($incrementId); if (!$this->_order->getId()) { + // phpcs:ignore Magento2.Exceptions.DirectThrow throw new Exception(sprintf('The "%s" order ID is incorrect. Verify the ID and try again.', $incrementId)); } return $this->_order; @@ -245,8 +249,11 @@ protected function _registerTransaction() break; // customer attempted to pay via bank account, but failed case Info::PAYMENTSTATUS_FAILED: - // cancel order - $this->_registerPaymentFailure(); + if ($this->_order->getState() === \Magento\Sales\Model\Order::STATE_PAYMENT_REVIEW) { + $this->_registerPaymentDenial(); + } else { + $this->_registerPaymentFailure(); + } break; // payment was obtained, but money were not captured yet case Info::PAYMENTSTATUS_PENDING: @@ -270,6 +277,7 @@ protected function _registerTransaction() $this->_registerPaymentVoid(); break; default: + // phpcs:ignore Magento2.Exceptions.DirectThrow throw new Exception("The '{$paymentStatus}' payment status couldn't be handled."); } } @@ -294,6 +302,9 @@ protected function _registerPaymentCapture($skipFraudDetection = false) $payment->setParentTransactionId($parentTransactionId); $payment->setShouldCloseParentTransaction('Completed' === $this->getRequestData('auth_status')); $payment->setIsTransactionClosed(0); + if ($this->_order->getState() === Order::STATE_PENDING_PAYMENT) { + $this->_order->setState(Order::STATE_PROCESSING); + } $payment->registerCaptureNotification( $this->getRequestData('mc_gross'), $skipFraudDetection && $parentTransactionId @@ -322,11 +333,12 @@ protected function _registerPaymentDenial() { try { $this->_importPaymentInformation(); - $this->_order->getPayment() - ->setTransactionId($this->getRequestData('txn_id')) - ->setNotificationResult(true) - ->setIsTransactionClosed(true) - ->deny(false); + $payment = $this->_order->getPayment(); + $payment->setTransactionId($this->getRequestData('txn_id')); + $payment->setPreparedMessage($this->_createIpnComment('')); + $payment->setNotificationResult(true); + $payment->setIsTransactionClosed(true); + $payment->deny(false); $this->_order->save(); } catch (LocalizedException $e) { if ($e->getMessage() != __('We cannot cancel this order.')) { @@ -360,6 +372,7 @@ public function _registerPaymentPending() return; } if ('order' === $reason) { + // phpcs:ignore Magento2.Exceptions.DirectThrow throw new Exception('The "order" authorizations aren\'t implemented.'); } // case when was placed using PayPal standard @@ -501,6 +514,7 @@ protected function _registerPaymentVoid() /** * Map payment information from IPN to payment object + * * Returns true if there were changes in information * * @return bool @@ -537,8 +551,10 @@ protected function _importPaymentInformation() // collect fraud filters $fraudFilters = []; - for ($i = 1; $value = $this->getRequestData("fraud_management_pending_filters_{$i}"); $i++) { + $index = 1; + while ($value = $this->getRequestData("fraud_management_pending_filters_{$index}")) { $fraudFilters[] = $value; + $index++; } if ($fraudFilters) { $from[Info::FRAUD_FILTERS] = $fraudFilters; @@ -568,6 +584,7 @@ protected function _importPaymentInformation() /** * Generate an "IPN" comment with additional explanation. + * * Returns the generated comment or order status history object * * @param string $comment diff --git a/app/code/Magento/Paypal/Model/Payflow/AvsEmsCodeMapper.php b/app/code/Magento/Paypal/Model/Payflow/AvsEmsCodeMapper.php index 1ec7f4832bcb2..1ef9577105029 100644 --- a/app/code/Magento/Paypal/Model/Payflow/AvsEmsCodeMapper.php +++ b/app/code/Magento/Paypal/Model/Payflow/AvsEmsCodeMapper.php @@ -11,8 +11,7 @@ use Magento\Sales\Api\Data\OrderPaymentInterface; /** - * Processes AVS codes mapping from PayPal Payflow transaction to - * electronic merchant systems standard. + * Processes AVS codes mapping from PayPal Payflow transaction to electronic merchant systems standard. * * @see https://developer.paypal.com/docs/classic/payflow/integration-guide/#credit-card-transaction-responses * @see http://www.emsecommerce.net/avs_cvv2_response_codes.htm @@ -64,6 +63,6 @@ public function getCode(OrderPaymentInterface $orderPayment) $zipCode = $additionalInfo[Info::PAYPAL_AVSZIP]; $key = $zipCode . $streetCode; - return isset(self::$avsMap[$key]) ? self::$avsMap[$key] : self::$unavailableCode; + return self::$avsMap[$key] ?? self::$unavailableCode; } } diff --git a/app/code/Magento/Paypal/Model/Payflow/CvvEmsCodeMapper.php b/app/code/Magento/Paypal/Model/Payflow/CvvEmsCodeMapper.php index 7aee18440ab05..b226c2193f5c5 100644 --- a/app/code/Magento/Paypal/Model/Payflow/CvvEmsCodeMapper.php +++ b/app/code/Magento/Paypal/Model/Payflow/CvvEmsCodeMapper.php @@ -11,8 +11,7 @@ use Magento\Sales\Api\Data\OrderPaymentInterface; /** - * Processes CVV codes mapping from PayPal Payflow transaction to - * electronic merchant systems standard. + * Processes CVV codes mapping from PayPal Payflow transaction to electronic merchant systems standard. * * @see https://developer.paypal.com/docs/classic/payflow/integration-guide/#credit-card-transaction-responses * @see http://www.emsecommerce.net/avs_cvv2_response_codes.htm @@ -58,6 +57,6 @@ public function getCode(OrderPaymentInterface $orderPayment) $cvv = $additionalInfo[Info::PAYPAL_CVV2MATCH]; - return isset(self::$cvvMap[$cvv]) ? self::$cvvMap[$cvv] : self::$notProvidedCode; + return self::$cvvMap[$cvv] ?? self::$notProvidedCode; } } diff --git a/app/code/Magento/Paypal/Model/Payflow/Service/Request/SecureToken.php b/app/code/Magento/Paypal/Model/Payflow/Service/Request/SecureToken.php index 2a4ec764c4172..6e9990f65c450 100644 --- a/app/code/Magento/Paypal/Model/Payflow/Service/Request/SecureToken.php +++ b/app/code/Magento/Paypal/Model/Payflow/Service/Request/SecureToken.php @@ -70,9 +70,9 @@ public function requestToken(Quote $quote, array $urls = []) $request->setCurrency($quote->getBaseCurrencyCode()); $request->setCreatesecuretoken('Y'); $request->setSecuretokenid($this->mathRandom->getUniqueHash()); - $request->setReturnurl($urls['return_url'] ?? $this->url->getUrl('paypal/transparent/response')); - $request->setErrorurl($urls['error_url'] ?? $this->url->getUrl('paypal/transparent/response')); - $request->setCancelurl($urls['cancel_url'] ?? $this->url->getUrl('paypal/transparent/response')); + $request->setReturnurl($urls['return_url'] ?? $this->url->getUrl('paypal/transparent/redirect')); + $request->setErrorurl($urls['error_url'] ?? $this->url->getUrl('paypal/transparent/redirect')); + $request->setCancelurl($urls['cancel_url'] ?? $this->url->getUrl('paypal/transparent/redirect')); $request->setDisablereceipt('TRUE'); $request->setSilenttran('TRUE'); diff --git a/app/code/Magento/Paypal/Model/Payflow/Service/Response/Transaction.php b/app/code/Magento/Paypal/Model/Payflow/Service/Response/Transaction.php index 5db78e6fac520..be650baa22d75 100644 --- a/app/code/Magento/Paypal/Model/Payflow/Service/Response/Transaction.php +++ b/app/code/Magento/Paypal/Model/Payflow/Service/Response/Transaction.php @@ -19,7 +19,8 @@ use Magento\Sales\Api\Data\OrderPaymentInterface; /** - * Class Transaction + * Process PayPal transaction response. + * * @SuppressWarnings(PHPMD.CouplingBetweenObjects) */ class Transaction @@ -90,7 +91,7 @@ public function getResponseObject($gatewayTransactionResponse) $response = $this->transparent->mapGatewayResponse((array) $gatewayTransactionResponse, $response); $this->logger->debug( - (array) $gatewayTransactionResponse, + ['PayPal PayflowPro response:' => (array)$gatewayTransactionResponse], (array) $this->transparent->getDebugReplacePrivateDataKeys(), (bool) $this->transparent->getDebugFlag() ); @@ -116,6 +117,7 @@ public function savePaymentInQuote($response, $cartId) $payment->setData(OrderPaymentInterface::CC_TYPE, $response->getData(OrderPaymentInterface::CC_TYPE)); $payment->setAdditionalInformation(Payflowpro::PNREF, $response->getData(Payflowpro::PNREF)); + $payment->setAdditionalInformation('result_code', $response->getData('result')); $expDate = $response->getData('expdate'); $expMonth = $this->getCcExpMonth($expDate); diff --git a/app/code/Magento/Paypal/Model/Payflow/Transparent.php b/app/code/Magento/Paypal/Model/Payflow/Transparent.php index 6569bdb20edfe..44a9b6f73c80e 100644 --- a/app/code/Magento/Paypal/Model/Payflow/Transparent.php +++ b/app/code/Magento/Paypal/Model/Payflow/Transparent.php @@ -7,23 +7,24 @@ namespace Magento\Paypal\Model\Payflow; +use Magento\Framework\Exception\LocalizedException; +use Magento\Framework\Exception\State\InvalidTransitionException; use Magento\Payment\Helper\Formatter; use Magento\Payment\Model\InfoInterface; -use Magento\Paypal\Model\Payflowpro; -use Magento\Sales\Api\Data\OrderPaymentExtensionInterfaceFactory; -use Magento\Sales\Model\Order\Payment; -use Magento\Paypal\Model\Payflow\Service\Gateway; -use Magento\Framework\Exception\LocalizedException; -use Magento\Payment\Model\Method\TransparentInterface; use Magento\Payment\Model\Method\ConfigInterfaceFactory; -use Magento\Framework\Exception\State\InvalidTransitionException; +use Magento\Payment\Model\Method\TransparentInterface; +use Magento\Payment\Model\MethodInterface; +use Magento\Paypal\Model\Payflow\Service\Gateway; use Magento\Paypal\Model\Payflow\Service\Response\Handler\HandlerInterface; use Magento\Paypal\Model\Payflow\Service\Response\Validator\ResponseValidator; +use Magento\Paypal\Model\Payflowpro; +use Magento\Sales\Api\Data\OrderPaymentExtensionInterfaceFactory; +use Magento\Sales\Model\Order\Payment; use Magento\Vault\Api\Data\PaymentTokenInterface; use Magento\Vault\Api\Data\PaymentTokenInterfaceFactory; /** - * Payflow Pro payment gateway model + * Payflow Pro payment gateway model (transparent redirect). * * @SuppressWarnings(PHPMD.CouplingBetweenObjects) */ @@ -35,6 +36,16 @@ class Transparent extends Payflowpro implements TransparentInterface const CC_VAULT_CODE = 'payflowpro_cc_vault'; + /** + * Result code of account verification transaction request. + */ + private const RESULT_CODE = 'result_code'; + + /** + * Fraud Management Filters config setting. + */ + private const CONFIG_FMF = 'fmf'; + /** * @var string */ @@ -45,6 +56,13 @@ class Transparent extends Payflowpro implements TransparentInterface */ protected $_infoBlockType = \Magento\Paypal\Block\Payment\Info::class; + /** + * Fetch transaction details availability option. + * + * @var bool + */ + protected $_canFetchTransactionInfo = false; + /** * @var ResponseValidator */ @@ -165,6 +183,14 @@ public function validate() */ public function authorize(InfoInterface $payment, $amount) { + if ($this->isFraudDetected($payment)) { + $this->markPaymentAsFraudulent($payment); + return $this; + } + + $zeroAmountAuthorizationId = $this->getZeroAmountAuthorizationId($payment); + /** @var PaymentTokenInterface $vaultPaymentToken */ + $vaultPaymentToken = $payment->getExtensionAttributes()->getVaultPaymentToken(); /** @var Payment $payment */ $request = $this->buildBasicRequest(); @@ -177,9 +203,9 @@ public function authorize(InfoInterface $payment, $amount) $payPalCart = $this->payPalCartFactory->create(['salesModel' => $order]); $payPalCart->getAmounts(); - $token = $payment->getAdditionalInformation(self::PNREF); + $parentTransactionId = $vaultPaymentToken ? $vaultPaymentToken->getGatewayToken() : $zeroAmountAuthorizationId; $request->setData('trxtype', self::TRXTYPE_AUTH_ONLY); - $request->setData('origid', $token); + $request->setData('origid', $parentTransactionId); $request->setData('amt', $this->formatPrice($amount)); $request->setData('currency', $order->getBaseCurrencyCode()); $request->setData('itemamt', $this->formatPrice($payPalCart->getSubtotal())); @@ -200,10 +226,15 @@ public function authorize(InfoInterface $payment, $amount) $this->setTransStatus($payment, $response); - $this->createPaymentToken($payment, $token); + if ($vaultPaymentToken) { + $payment->setParentTransactionId($vaultPaymentToken->getGatewayToken()); + } else { + $this->createPaymentToken($payment, $zeroAmountAuthorizationId); + } $payment->unsAdditionalInformation(self::CC_DETAILS); $payment->unsAdditionalInformation(self::PNREF); + $payment->unsAdditionalInformation(self::RESULT_CODE); return $this; } @@ -291,14 +322,126 @@ private function getPaymentExtensionAttributes(Payment $payment) */ public function capture(InfoInterface $payment, $amount) { + if ($this->isFraudDetected($payment)) { + $this->markPaymentAsFraudulent($payment); + return $this; + } + /** @var Payment $payment */ - $token = $payment->getAdditionalInformation(self::PNREF); + $zeroAmountAuthorizationId = $this->getZeroAmountAuthorizationId($payment); + /** @var PaymentTokenInterface $vaultPaymentToken */ + $vaultPaymentToken = $payment->getExtensionAttributes()->getVaultPaymentToken(); + if ($vaultPaymentToken && empty($zeroAmountAuthorizationId)) { + $payment->setAdditionalInformation(self::PNREF, $vaultPaymentToken->getGatewayToken()); + if (!$payment->getParentTransactionId()) { + $payment->setParentTransactionId($vaultPaymentToken->getGatewayToken()); + } + } parent::capture($payment, $amount); - if ($token && !$payment->getAuthorizationTransaction()) { - $this->createPaymentToken($payment, $token); + if ($zeroAmountAuthorizationId && $vaultPaymentToken === null) { + $this->createPaymentToken($payment, $zeroAmountAuthorizationId); } return $this; } + + /** + * Attempt to accept a pending payment. + * + * Order acquires a payment review state based on results of PayPal account verification transaction (zero-amount + * authorization). For accepting a payment should be created PayPal reference transaction with a real order amount. + * Fraud Protection Service filters do not screen reference transactions. + * + * @param InfoInterface $payment + * @return bool + * @throws InvalidTransitionException + * @throws LocalizedException + */ + public function acceptPayment(InfoInterface $payment) + { + if ($this->getConfigPaymentAction() === MethodInterface::ACTION_AUTHORIZE_CAPTURE) { + $invoices = iterator_to_array($payment->getOrder()->getInvoiceCollection()); + $invoice = count($invoices) ? reset($invoices) : null; + $payment->capture($invoice); + } else { + $amount = $payment->getOrder()->getBaseGrandTotal(); + $payment->authorize(true, $amount); + } + + return true; + } + + /** + * Deny a pending payment. + * + * Order acquires a payment review state based on results of PayPal account verification transaction (zero-amount + * authorization). This transaction type cannot be voided, so we do not send any request to payment gateway. + * + * @param InfoInterface $payment + * @return bool + * @SuppressWarnings(PHPMD.UnusedFormalParameter) + */ + public function denyPayment(InfoInterface $payment) + { + return true; + } + + /** + * Marks payment as fraudulent. + * + * @param InfoInterface $payment + * @throws \Exception + */ + private function markPaymentAsFraudulent(InfoInterface $payment): void + { + $zeroAmountAuthorizationId = $this->getZeroAmountAuthorizationId($payment); + $payment->setTransactionId($zeroAmountAuthorizationId); + $payment->setIsTransactionClosed(0); + $payment->setIsTransactionPending(true); + $payment->setIsFraudDetected(true); + $this->createPaymentToken($payment, $zeroAmountAuthorizationId); + $fraudulentMsg = 'Order is suspended as an account verification transaction is suspected to be fraudulent.'; + $extensionAttributes = $this->getPaymentExtensionAttributes($payment); + $extensionAttributes->setNotificationMessage($fraudulentMsg); + $payment->unsAdditionalInformation(self::CC_DETAILS); + $payment->unsAdditionalInformation(self::PNREF); + $payment->unsAdditionalInformation(self::RESULT_CODE); + } + + /** + * Checks if fraud filters were triggered for the payment. + * + * For current PayPal PayflowPro transparent redirect integration + * Fraud Protection Service filters screen only account verification + * transaction (also known as zero dollar authorization). + * Following reference transaction with real dollar amount will not be screened + * by Fraud Protection Service. + * + * @param InfoInterface $payment + * @return bool + */ + private function isFraudDetected(InfoInterface $payment): bool + { + $resultCode = $payment->getAdditionalInformation(self::RESULT_CODE); + $isFmfEnabled = (bool)$this->getConfig()->getValue(self::CONFIG_FMF); + return $isFmfEnabled && $this->getZeroAmountAuthorizationId($payment) && in_array( + $resultCode, + [self::RESPONSE_CODE_DECLINED_BY_FILTER, self::RESPONSE_CODE_FRAUDSERVICE_FILTER] + ); + } + + /** + * Returns zero dollar authorization transaction id. + * + * PNREF (transaction id) is available in payment additional information only right after + * PayPal account verification transaction (also known as zero dollar authorization). + * + * @param InfoInterface $payment + * @return string + */ + private function getZeroAmountAuthorizationId(InfoInterface $payment): string + { + return (string)$payment->getAdditionalInformation(self::PNREF); + } } diff --git a/app/code/Magento/Paypal/Model/Payflowlink.php b/app/code/Magento/Paypal/Model/Payflowlink.php index 690e09ffd8770..f16b117daf56e 100644 --- a/app/code/Magento/Paypal/Model/Payflowlink.php +++ b/app/code/Magento/Paypal/Model/Payflowlink.php @@ -36,7 +36,7 @@ class Payflowlink extends \Magento\Paypal\Model\Payflowpro * * @var string */ - protected $_code = \Magento\Paypal\Model\Config::METHOD_PAYFLOWLINK; + protected $_code = Config::METHOD_PAYFLOWLINK; /** * @var string @@ -116,6 +116,11 @@ class Payflowlink extends \Magento\Paypal\Model\Payflowpro */ private $mathRandom; + /** + * @var \Magento\Framework\App\RequestInterface + */ + private $_requestHttp; + /** * @param \Magento\Framework\Model\Context $context * @param \Magento\Framework\Registry $registry @@ -237,8 +242,8 @@ public function isActive($storeId = null) public function initialize($paymentAction, $stateObject) { switch ($paymentAction) { - case \Magento\Paypal\Model\Config::PAYMENT_ACTION_AUTH: - case \Magento\Paypal\Model\Config::PAYMENT_ACTION_SALE: + case Config::PAYMENT_ACTION_AUTH: + case Config::PAYMENT_ACTION_SALE: $payment = $this->getInfoInstance(); /** @var Order $order */ $order = $payment->getOrder(); @@ -345,6 +350,7 @@ protected function _processOrder(\Magento\Sales\Model\Order $order) $payment->registerAuthorizationNotification($payment->getBaseAmountAuthorized()); break; case self::TRXTYPE_SALE: + $order->setState(Order::STATE_PROCESSING); $payment->registerCaptureNotification($payment->getBaseAmountAuthorized()); break; default: @@ -500,14 +506,12 @@ public function buildBasicRequest() */ protected function _getTrxTokenType() { - switch ($this->getConfigData('payment_action')) { - case \Magento\Paypal\Model\Config::PAYMENT_ACTION_AUTH: - return self::TRXTYPE_AUTH_ONLY; - case \Magento\Paypal\Model\Config::PAYMENT_ACTION_SALE: - return self::TRXTYPE_SALE; - default: - break; - } + $tokenTypes = [ + Config::PAYMENT_ACTION_AUTH => self::TRXTYPE_AUTH_ONLY, + Config::PAYMENT_ACTION_SALE => self::TRXTYPE_SALE + ]; + + return $tokenTypes[$this->getConfigData('payment_action')] ?? ''; } /** diff --git a/app/code/Magento/Paypal/Model/Payflowpro.php b/app/code/Magento/Paypal/Model/Payflowpro.php index 2ba72c4b26bd7..778cd0c728de3 100644 --- a/app/code/Magento/Paypal/Model/Payflowpro.php +++ b/app/code/Magento/Paypal/Model/Payflowpro.php @@ -556,6 +556,7 @@ public function fetchTransactionInfo(InfoInterface $payment, $transactionId) * * @param string $status * @return bool + * phpcs:disable Magento2.Functions.StaticFunction */ protected static function _isTransactionUnderReview($status) { @@ -980,6 +981,6 @@ private function mapResponseBillToName($billToFirstName, $billToLastName) */ private function mapResponseCreditCardType($ccType) { - return isset($this->ccTypeMap[$ccType]) ? $this->ccTypeMap[$ccType] : $ccType; + return $this->ccTypeMap[$ccType] ?? $ccType; } } diff --git a/app/code/Magento/Paypal/Plugin/TransparentOrderPayment.php b/app/code/Magento/Paypal/Plugin/TransparentOrderPayment.php new file mode 100644 index 0000000000000..ab1d9c210d7d7 --- /dev/null +++ b/app/code/Magento/Paypal/Plugin/TransparentOrderPayment.php @@ -0,0 +1,60 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +declare(strict_types=1); + +namespace Magento\Paypal\Plugin; + +use Magento\Framework\Exception\LocalizedException; +use Magento\Sales\Api\InvoiceRepositoryInterface; +use Magento\Sales\Model\Order\Payment; + +/** + * Updates invoice transaction id for PayPal PayflowPro payment. + */ +class TransparentOrderPayment +{ + /** + * @var InvoiceRepositoryInterface + */ + private $invoiceRepository; + + /** + * @param InvoiceRepositoryInterface $invoiceRepository + */ + public function __construct(InvoiceRepositoryInterface $invoiceRepository) + { + $this->invoiceRepository = $invoiceRepository; + } + + /** + * Updates invoice transaction id. + * + * Accepting PayPal PayflowPro payment actually means executing new reference transaction + * based on account verification. So for existing pending invoice, transaction id should be updated + * with the id of last reference transaction. + * + * @param Payment $subject + * @param Payment $result + * @return Payment + * @throws LocalizedException + */ + public function afterAccept(Payment $subject, Payment $result): Payment + { + $paymentMethod = $subject->getMethodInstance(); + if (!$paymentMethod instanceof \Magento\Paypal\Model\Payflow\Transparent) { + return $result; + } + + $invoices = iterator_to_array($subject->getOrder()->getInvoiceCollection()); + $invoice = reset($invoices); + if ($invoice) { + $invoice->setTransactionId($subject->getLastTransId()); + $this->invoiceRepository->save($invoice); + } + + return $result; + } +} diff --git a/app/code/Magento/Paypal/Plugin/TransparentSessionChecker.php b/app/code/Magento/Paypal/Plugin/TransparentSessionChecker.php new file mode 100644 index 0000000000000..5157ba3208fb7 --- /dev/null +++ b/app/code/Magento/Paypal/Plugin/TransparentSessionChecker.php @@ -0,0 +1,50 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +declare(strict_types=1); + +namespace Magento\Paypal\Plugin; + +use Magento\Framework\App\Request\Http; +use Magento\Framework\Session\SessionStartChecker; + +/** + * Intended to preserve session cookie after submitting POST form from PayPal to Magento controller. + */ +class TransparentSessionChecker +{ + private const TRANSPARENT_REDIRECT_PATH = 'paypal/transparent/redirect'; + + /** + * @var Http + */ + private $request; + + /** + * @param Http $request + */ + public function __construct( + Http $request + ) { + $this->request = $request; + } + + /** + * Prevents session starting while instantiating PayPal transparent redirect controller. + * + * @param SessionStartChecker $subject + * @param bool $result + * @return bool + * @SuppressWarnings(PHPMD.UnusedFormalParameter) + */ + public function afterCheck(SessionStartChecker $subject, bool $result): bool + { + if ($result === false) { + return false; + } + + return strpos((string)$this->request->getPathInfo(), self::TRANSPARENT_REDIRECT_PATH) === false; + } +} diff --git a/app/code/Magento/Paypal/Test/Mftf/ActionGroup/StorefrontPayOrderOnPayPalCheckoutActionGroup.xml b/app/code/Magento/Paypal/Test/Mftf/ActionGroup/StorefrontPayOrderOnPayPalCheckoutActionGroup.xml index 392014d876e46..b7ebf7dab1c8b 100644 --- a/app/code/Magento/Paypal/Test/Mftf/ActionGroup/StorefrontPayOrderOnPayPalCheckoutActionGroup.xml +++ b/app/code/Magento/Paypal/Test/Mftf/ActionGroup/StorefrontPayOrderOnPayPalCheckoutActionGroup.xml @@ -16,6 +16,7 @@ <argument name="productName" type="string"/> </arguments> <click selector="{{PayPalPaymentSection.cartIcon}}" stepKey="openCart"/> + <waitForPageLoad stepKey="waitForCartLoad"/> <seeElement selector="{{PayPalPaymentSection.itemName(productName)}}" stepKey="seeProductName"/> <click selector="{{PayPalPaymentSection.PayPalSubmitBtn}}" stepKey="clickPayPalSubmitBtn"/> <switchToPreviousTab stepKey="switchToPreviousTab"/> diff --git a/app/code/Magento/Paypal/Test/Mftf/Metadata/paypal_config-meta.xml b/app/code/Magento/Paypal/Test/Mftf/Metadata/PaypalConfigMeta.xml similarity index 100% rename from app/code/Magento/Paypal/Test/Mftf/Metadata/paypal_config-meta.xml rename to app/code/Magento/Paypal/Test/Mftf/Metadata/PaypalConfigMeta.xml diff --git a/app/code/Magento/Paypal/Test/Mftf/Section/OtherPayPalPaymentsConfigSection.xml b/app/code/Magento/Paypal/Test/Mftf/Section/OtherPayPalPaymentsConfigSection.xml deleted file mode 100644 index ca8438d5ee06a..0000000000000 --- a/app/code/Magento/Paypal/Test/Mftf/Section/OtherPayPalPaymentsConfigSection.xml +++ /dev/null @@ -1,31 +0,0 @@ -<?xml version="1.0" encoding="UTF-8"?> -<!-- - /** - * Copyright © Magento, Inc. All rights reserved. - * See COPYING.txt for license details. - */ ---> - -<sections xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" - xsi:noNamespaceSchemaLocation="urn:magento:mftf:Page/etc/SectionObject.xsd"> - <section name="OtherPayPalPaymentsConfigSection"> - <element name="expandTab" type="button" selector="#payment_{{countryCode}}_other_paypal_payment_solutions-head" parameterized="true"/> - <element name="expandedTab" type="button" selector="#payment_{{countryCode}}_other_paypal_payment_solutions-head.open" parameterized="true"/> - </section> - <section name="WPSExpressConfigSection"> - <element name="configureBtn" type="button" selector="#payment_{{countryCode}}_paypal_group_all_in_one_wps_express-head" parameterized="true"/> - <element name="enableSolution" type="input" selector="#payment_{{countryCode}}_paypal_group_all_in_one_wps_express_express_checkout_required_enable_express_checkout" parameterized="true"/> - </section> - <section name="PaymentsProHostedWithExpressCheckoutConfigSection"> - <element name="configureBtn" type="button" selector="#payment_{{countryCode}}_paypal_group_all_in_one_payments_pro_hosted_solution_with_express_checkout-head" parameterized="true"/> - <element name="enableSolution" type="input" selector="#payment_{{countryCode}}_paypal_group_all_in_one_payments_pro_hosted_solution_with_express_checkout_pphs_required_settings_pphs_enable" parameterized="true"/> - </section> - <section name="WPSOtherConfigSection"> - <element name="configureBtn" type="button" selector="#payment_{{countryCode}}_paypal_group_all_in_one_wps_other-head" parameterized="true"/> - <element name="enableSolution" type="input" selector="#payment_{{countryCode}}_paypal_group_all_in_one_wps_other_express_checkout_required_enable_express_checkout" parameterized="true"/> - </section> - <section name="WebsitePaymentsPlusConfigSection"> - <element name="configureBtn" type="button" selector="#payment_{{countryCode}}_paypal_group_all_in_one_payments_pro_hosted_solution_{{countryCode}}-head" parameterized="true"/> - <element name="enableSolution" type="input" selector="#payment_{{countryCode}}_paypal_group_all_in_one_payments_pro_hosted_solution_{{countryCode}}_pphs_required_settings_pphs_enable" parameterized="true"/> - </section> -</sections> diff --git a/app/code/Magento/Paypal/Test/Mftf/Section/OtherPayPalPaymentsConfigSection/OtherPayPalPaymentsConfigSection.xml b/app/code/Magento/Paypal/Test/Mftf/Section/OtherPayPalPaymentsConfigSection/OtherPayPalPaymentsConfigSection.xml new file mode 100644 index 0000000000000..bef5949162f18 --- /dev/null +++ b/app/code/Magento/Paypal/Test/Mftf/Section/OtherPayPalPaymentsConfigSection/OtherPayPalPaymentsConfigSection.xml @@ -0,0 +1,14 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + /** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +--> +<sections xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Page/etc/SectionObject.xsd"> + <section name="OtherPayPalPaymentsConfigSection"> + <element name="expandTab" type="button" selector="#payment_{{countryCode}}_other_paypal_payment_solutions-head" parameterized="true"/> + <element name="expandedTab" type="button" selector="#payment_{{countryCode}}_other_paypal_payment_solutions-head.open" parameterized="true"/> + </section> +</sections> diff --git a/app/code/Magento/Paypal/Test/Mftf/Section/OtherPayPalPaymentsConfigSection/PaymentsProHostedWithExpressCheckoutConfigSection.xml b/app/code/Magento/Paypal/Test/Mftf/Section/OtherPayPalPaymentsConfigSection/PaymentsProHostedWithExpressCheckoutConfigSection.xml new file mode 100644 index 0000000000000..6a0e0ead051ea --- /dev/null +++ b/app/code/Magento/Paypal/Test/Mftf/Section/OtherPayPalPaymentsConfigSection/PaymentsProHostedWithExpressCheckoutConfigSection.xml @@ -0,0 +1,14 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + /** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +--> +<sections xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Page/etc/SectionObject.xsd"> + <section name="PaymentsProHostedWithExpressCheckoutConfigSection"> + <element name="configureBtn" type="button" selector="#payment_{{countryCode}}_paypal_group_all_in_one_payments_pro_hosted_solution_with_express_checkout-head" parameterized="true"/> + <element name="enableSolution" type="input" selector="#payment_{{countryCode}}_paypal_group_all_in_one_payments_pro_hosted_solution_with_express_checkout_pphs_required_settings_pphs_enable" parameterized="true"/> + </section> +</sections> diff --git a/app/code/Magento/Paypal/Test/Mftf/Section/OtherPayPalPaymentsConfigSection/WPSExpressConfigSection.xml b/app/code/Magento/Paypal/Test/Mftf/Section/OtherPayPalPaymentsConfigSection/WPSExpressConfigSection.xml new file mode 100644 index 0000000000000..a9e530590048c --- /dev/null +++ b/app/code/Magento/Paypal/Test/Mftf/Section/OtherPayPalPaymentsConfigSection/WPSExpressConfigSection.xml @@ -0,0 +1,14 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + /** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +--> +<sections xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Page/etc/SectionObject.xsd"> + <section name="WPSExpressConfigSection"> + <element name="configureBtn" type="button" selector="#payment_{{countryCode}}_paypal_group_all_in_one_wps_express-head" parameterized="true"/> + <element name="enableSolution" type="input" selector="#payment_{{countryCode}}_paypal_group_all_in_one_wps_express_express_checkout_required_enable_express_checkout" parameterized="true"/> + </section> +</sections> diff --git a/app/code/Magento/Paypal/Test/Mftf/Section/OtherPayPalPaymentsConfigSection/WPSOtherConfigSection.xml b/app/code/Magento/Paypal/Test/Mftf/Section/OtherPayPalPaymentsConfigSection/WPSOtherConfigSection.xml new file mode 100644 index 0000000000000..f5cc20a2bbd24 --- /dev/null +++ b/app/code/Magento/Paypal/Test/Mftf/Section/OtherPayPalPaymentsConfigSection/WPSOtherConfigSection.xml @@ -0,0 +1,14 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + /** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +--> +<sections xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Page/etc/SectionObject.xsd"> + <section name="WPSOtherConfigSection"> + <element name="configureBtn" type="button" selector="#payment_{{countryCode}}_paypal_group_all_in_one_wps_other-head" parameterized="true"/> + <element name="enableSolution" type="input" selector="#payment_{{countryCode}}_paypal_group_all_in_one_wps_other_express_checkout_required_enable_express_checkout" parameterized="true"/> + </section> +</sections> diff --git a/app/code/Magento/Paypal/Test/Mftf/Section/OtherPayPalPaymentsConfigSection/WebsitePaymentsPlusConfigSection.xml b/app/code/Magento/Paypal/Test/Mftf/Section/OtherPayPalPaymentsConfigSection/WebsitePaymentsPlusConfigSection.xml new file mode 100644 index 0000000000000..5a2b9aec4adf2 --- /dev/null +++ b/app/code/Magento/Paypal/Test/Mftf/Section/OtherPayPalPaymentsConfigSection/WebsitePaymentsPlusConfigSection.xml @@ -0,0 +1,14 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + /** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +--> +<sections xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Page/etc/SectionObject.xsd"> + <section name="WebsitePaymentsPlusConfigSection"> + <element name="configureBtn" type="button" selector="#payment_{{countryCode}}_paypal_group_all_in_one_payments_pro_hosted_solution_{{countryCode}}-head" parameterized="true"/> + <element name="enableSolution" type="input" selector="#payment_{{countryCode}}_paypal_group_all_in_one_payments_pro_hosted_solution_{{countryCode}}_pphs_required_settings_pphs_enable" parameterized="true"/> + </section> +</sections> diff --git a/app/code/Magento/Paypal/Test/Mftf/Section/PayPalExpressCheckoutConfigSection.xml b/app/code/Magento/Paypal/Test/Mftf/Section/PayPalExpressCheckoutConfigSection.xml deleted file mode 100644 index af68a7611cd1d..0000000000000 --- a/app/code/Magento/Paypal/Test/Mftf/Section/PayPalExpressCheckoutConfigSection.xml +++ /dev/null @@ -1,67 +0,0 @@ -<?xml version="1.0" encoding="UTF-8"?> -<!-- - /** - * Copyright © Magento, Inc. All rights reserved. - * See COPYING.txt for license details. - */ ---> - -<sections xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" - xsi:noNamespaceSchemaLocation="urn:magento:mftf:Page/etc/SectionObject.xsd"> - <section name="PayPalExpressCheckoutConfigSection"> - <element name="configureBtn" type="button" selector="#payment_{{countryCode}}_paypal_alternative_payment_methods_express_checkout_{{countryCode}}-head" parameterized="true"/> - <element name="email" type="input" selector="#payment_{{countryCode}}_paypal_alternative_payment_methods_express_checkout_{{countryCode}}_express_checkout_required_express_checkout_required_express_checkout_business_account" parameterized="true"/> - <element name="apiMethod" type="input" selector="#payment_{{countryCode}}_paypal_alternative_payment_methods_express_checkout_{{countryCode}}_express_checkout_required_express_checkout_required_express_checkout_api_authentication" parameterized="true"/> - <element name="username" type="input" selector="#payment_{{countryCode}}_paypal_alternative_payment_methods_express_checkout_{{countryCode}}_express_checkout_required_express_checkout_required_express_checkout_api_username" parameterized="true"/> - <element name="password" type="input" selector="#payment_{{countryCode}}_paypal_alternative_payment_methods_express_checkout_{{countryCode}}_express_checkout_required_express_checkout_required_express_checkout_api_password" parameterized="true"/> - <element name="signature" type="input" selector="#payment_{{countryCode}}_paypal_alternative_payment_methods_express_checkout_{{countryCode}}_express_checkout_required_express_checkout_required_express_checkout_api_signature" parameterized="true"/> - <element name="sandboxMode" type="input" selector="#payment_{{countryCode}}_paypal_alternative_payment_methods_express_checkout_{{countryCode}}_express_checkout_required_express_checkout_required_express_checkout_sandbox_flag" parameterized="true"/> - <element name="enableSolution" type="input" selector="#payment_{{countryCode}}_paypal_alternative_payment_methods_express_checkout_{{countryCode}}_express_checkout_required_enable_express_checkout" parameterized="true"/> - <element name="merchantID" type="input" selector="#payment_{{countryCode}}_paypal_alternative_payment_methods_express_checkout_{{countryCode}}_express_checkout_required_merchant_id" parameterized="true"/> - </section> - <section name="PayPalExpressCheckoutOtherCountryConfigSection"> - <element name="configureBtn" type="button" selector="#payment_{{countryCode}}_express_checkout_other-head" parameterized="true"/> - <element name="enableSolution" type="input" selector="#payment_{{countryCode}}_express_checkout_other_express_checkout_required_enable_express_checkout" parameterized="true"/> - </section> - <section name="PayPalAdvancedSettingConfigSection"> - <element name="advancedSettingTab" type="button" selector="#payment_{{countryCode}}_paypal_alternative_payment_methods_express_checkout_{{countryCode}}_settings_ec_settings_ec_advanced-head" parameterized="true"/> - <element name="frontendExperienceSettingsTab" type="button" selector="#payment_{{countryCode}}_paypal_alternative_payment_methods_express_checkout_{{countryCode}}_settings_ec_settings_ec_advanced_express_checkout_frontend-head" parameterized="true"/> - <element name="checkoutPageTab" type="button" selector="#payment_{{countryCode}}_paypal_alternative_payment_methods_express_checkout_{{countryCode}}_settings_ec_settings_ec_advanced_express_checkout_frontend_checkout_page_button-head" parameterized="true"/> - </section> - <section name="ButtonCustomization"> - <element name="customizeDrpDown" type="button" selector="//tr[@id='row_payment_us_paypal_alternative_payment_methods_express_checkout_us_settings_ec_settings_ec_advanced_express_checkout_frontend_checkout_page_button_checkout_page_button_customize']//select[contains(@data-ui-id, 'button-customize')]"/> - <element name="customizeNo" type="text" selector="//tr[@id='row_payment_us_paypal_alternative_payment_methods_express_checkout_us_settings_ec_settings_ec_advanced_express_checkout_frontend_checkout_page_button_checkout_page_button_customize']//select[contains(@data-ui-id, 'button-customize')]/option[@value='0' and @selected='selected']"/> - <element name="label" type="input" selector="//tr[@id='row_payment_us_paypal_alternative_payment_methods_express_checkout_us_settings_ec_settings_ec_advanced_express_checkout_frontend_checkout_page_button']//select[contains(@id, 'button_label')]"/> - <element name="layout" type="input" selector="//tr[@id='row_payment_us_paypal_alternative_payment_methods_express_checkout_us_settings_ec_settings_ec_advanced_express_checkout_frontend_checkout_page_button']//select[contains(@id, 'button_layout')]"/> - <element name="size" type="input" selector="//tr[@id='row_payment_us_paypal_alternative_payment_methods_express_checkout_us_settings_ec_settings_ec_advanced_express_checkout_frontend_checkout_page_button']//select[contains(@id, 'button_size')]"/> - <element name="shape" type="input" selector="//tr[@id='row_payment_us_paypal_alternative_payment_methods_express_checkout_us_settings_ec_settings_ec_advanced_express_checkout_frontend_checkout_page_button']//select[contains(@id, 'button_shape')]"/> - <element name="color" type="input" selector="//tr[@id='row_payment_us_paypal_alternative_payment_methods_express_checkout_us_settings_ec_settings_ec_advanced_express_checkout_frontend_checkout_page_button']//select[contains(@id, 'button_color')]"/> - </section> - <section name="PayPalButtonOnStorefront"> - <element name="label" type="text" selector="[aria-label='{{label}}']" parameterized="true"/> - <element name="layout" type="text" selector="[data-layout='{{layout}}']" parameterized="true"/> - <element name="size" type="text" selector="[data-size='{{size}}']" parameterized="true"/> - <element name="shape" type="text" selector=".paypal-button-shape-{{shape}}" parameterized="true"/> - <element name="color" type="text" selector=".paypal-button-color-{{color}}" parameterized="true"/> - </section> - <section name="CheckoutPaymentSection"> - <element name="email" type="input" selector="#checkout-customer-email"/> - <element name="payPalPaymentBraintree" type="radio" selector="#braintree_paypal"/> - <element name="payPalFrame" type="iframe" selector="//iframe[contains(@class, 'zoid-component-frame zoid-visible')]" timeout="5"/> - <element name="PayPalPaymentRadio" type="radio" selector="input#paypal_express.radio" timeout="30"/> - <element name="PayPalBtn" type="radio" selector=".paypal-button.paypal-button-number-0" timeout="30"/> - </section> - <section name="PayPalPaymentSection"> - <element name="guestCheckout" type="input" selector="#guest"/> - <element name="loginSection" type="input" selector=" #main>#login"/> - <element name="email" type="input" selector="//input[contains(@name, 'email') and not(contains(@style, 'display:none'))]"/> - <element name="password" type="input" selector="//input[contains(@name, 'password') and not(contains(@style, 'display:none'))]"/> - <element name="loginBtn" type="input" selector="button#btnLogin"/> - <element name="reviewUserInfo" type="text" selector="#reviewUserInfo"/> - <element name="cartIcon" type="text" selector="#transactionCart"/> - <element name="itemName" type="text" selector="//span[@title='{{productName}}']" parameterized="true"/> - <element name="PayPalSubmitBtn" type="text" selector="//input[@type='submit']"/> - <element name="nextButton" type="button" selector="#btnNext"/> - <element name="continueButton" type="button" selector=".continueButton"/> - </section> -</sections> diff --git a/app/code/Magento/Paypal/Test/Mftf/Section/PayPalExpressCheckoutConfigSection/ButtonCustomizationSection.xml b/app/code/Magento/Paypal/Test/Mftf/Section/PayPalExpressCheckoutConfigSection/ButtonCustomizationSection.xml new file mode 100644 index 0000000000000..2f95c4eff3d08 --- /dev/null +++ b/app/code/Magento/Paypal/Test/Mftf/Section/PayPalExpressCheckoutConfigSection/ButtonCustomizationSection.xml @@ -0,0 +1,19 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + /** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +--> +<sections xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Page/etc/SectionObject.xsd"> + <section name="ButtonCustomization"> + <element name="customizeDrpDown" type="button" selector="//tr[@id='row_payment_us_paypal_alternative_payment_methods_express_checkout_us_settings_ec_settings_ec_advanced_express_checkout_frontend_checkout_page_button_checkout_page_button_customize']//select[contains(@data-ui-id, 'button-customize')]"/> + <element name="customizeNo" type="text" selector="//tr[@id='row_payment_us_paypal_alternative_payment_methods_express_checkout_us_settings_ec_settings_ec_advanced_express_checkout_frontend_checkout_page_button_checkout_page_button_customize']//select[contains(@data-ui-id, 'button-customize')]/option[@value='0' and @selected='selected']"/> + <element name="label" type="input" selector="//tr[@id='row_payment_us_paypal_alternative_payment_methods_express_checkout_us_settings_ec_settings_ec_advanced_express_checkout_frontend_checkout_page_button']//select[contains(@id, 'button_label')]"/> + <element name="layout" type="input" selector="//tr[@id='row_payment_us_paypal_alternative_payment_methods_express_checkout_us_settings_ec_settings_ec_advanced_express_checkout_frontend_checkout_page_button']//select[contains(@id, 'button_layout')]"/> + <element name="size" type="input" selector="//tr[@id='row_payment_us_paypal_alternative_payment_methods_express_checkout_us_settings_ec_settings_ec_advanced_express_checkout_frontend_checkout_page_button']//select[contains(@id, 'button_size')]"/> + <element name="shape" type="input" selector="//tr[@id='row_payment_us_paypal_alternative_payment_methods_express_checkout_us_settings_ec_settings_ec_advanced_express_checkout_frontend_checkout_page_button']//select[contains(@id, 'button_shape')]"/> + <element name="color" type="input" selector="//tr[@id='row_payment_us_paypal_alternative_payment_methods_express_checkout_us_settings_ec_settings_ec_advanced_express_checkout_frontend_checkout_page_button']//select[contains(@id, 'button_color')]"/> + </section> +</sections> diff --git a/app/code/Magento/Paypal/Test/Mftf/Section/PayPalExpressCheckoutConfigSection/CheckoutPaymentSection.xml b/app/code/Magento/Paypal/Test/Mftf/Section/PayPalExpressCheckoutConfigSection/CheckoutPaymentSection.xml new file mode 100644 index 0000000000000..9c22dd940890e --- /dev/null +++ b/app/code/Magento/Paypal/Test/Mftf/Section/PayPalExpressCheckoutConfigSection/CheckoutPaymentSection.xml @@ -0,0 +1,17 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + /** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +--> +<sections xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Page/etc/SectionObject.xsd"> + <section name="CheckoutPaymentSection"> + <element name="email" type="input" selector="#checkout-customer-email"/> + <element name="payPalPaymentBraintree" type="radio" selector="#braintree_paypal"/> + <element name="payPalFrame" type="iframe" selector="//iframe[contains(@class, 'zoid-component-frame zoid-visible')]" timeout="5"/> + <element name="PayPalPaymentRadio" type="radio" selector="input#paypal_express.radio" timeout="30"/> + <element name="PayPalBtn" type="radio" selector=".paypal-button.paypal-button-number-0" timeout="30"/> + </section> +</sections> diff --git a/app/code/Magento/Paypal/Test/Mftf/Section/PayPalExpressCheckoutConfigSection/PayPalAdvancedSettingConfigSection.xml b/app/code/Magento/Paypal/Test/Mftf/Section/PayPalExpressCheckoutConfigSection/PayPalAdvancedSettingConfigSection.xml new file mode 100644 index 0000000000000..5b4b73804b9dd --- /dev/null +++ b/app/code/Magento/Paypal/Test/Mftf/Section/PayPalExpressCheckoutConfigSection/PayPalAdvancedSettingConfigSection.xml @@ -0,0 +1,15 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + /** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +--> +<sections xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Page/etc/SectionObject.xsd"> + <section name="PayPalAdvancedSettingConfigSection"> + <element name="advancedSettingTab" type="button" selector="#payment_{{countryCode}}_paypal_alternative_payment_methods_express_checkout_{{countryCode}}_settings_ec_settings_ec_advanced-head" parameterized="true"/> + <element name="frontendExperienceSettingsTab" type="button" selector="#payment_{{countryCode}}_paypal_alternative_payment_methods_express_checkout_{{countryCode}}_settings_ec_settings_ec_advanced_express_checkout_frontend-head" parameterized="true"/> + <element name="checkoutPageTab" type="button" selector="#payment_{{countryCode}}_paypal_alternative_payment_methods_express_checkout_{{countryCode}}_settings_ec_settings_ec_advanced_express_checkout_frontend_checkout_page_button-head" parameterized="true"/> + </section> +</sections> diff --git a/app/code/Magento/Paypal/Test/Mftf/Section/PayPalExpressCheckoutConfigSection/PayPalButtonOnStorefrontSection.xml b/app/code/Magento/Paypal/Test/Mftf/Section/PayPalExpressCheckoutConfigSection/PayPalButtonOnStorefrontSection.xml new file mode 100644 index 0000000000000..d97c0260adb0d --- /dev/null +++ b/app/code/Magento/Paypal/Test/Mftf/Section/PayPalExpressCheckoutConfigSection/PayPalButtonOnStorefrontSection.xml @@ -0,0 +1,17 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + /** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +--> +<sections xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Page/etc/SectionObject.xsd"> + <section name="PayPalButtonOnStorefront"> + <element name="label" type="text" selector="[aria-label='{{label}}']" parameterized="true"/> + <element name="layout" type="text" selector="[data-layout='{{layout}}']" parameterized="true"/> + <element name="size" type="text" selector="[data-size='{{size}}']" parameterized="true"/> + <element name="shape" type="text" selector=".paypal-button-shape-{{shape}}" parameterized="true"/> + <element name="color" type="text" selector=".paypal-button-color-{{color}}" parameterized="true"/> + </section> +</sections> diff --git a/app/code/Magento/Paypal/Test/Mftf/Section/PayPalExpressCheckoutConfigSection/PayPalExpressCheckoutConfigSection.xml b/app/code/Magento/Paypal/Test/Mftf/Section/PayPalExpressCheckoutConfigSection/PayPalExpressCheckoutConfigSection.xml new file mode 100644 index 0000000000000..180ca4f744c1d --- /dev/null +++ b/app/code/Magento/Paypal/Test/Mftf/Section/PayPalExpressCheckoutConfigSection/PayPalExpressCheckoutConfigSection.xml @@ -0,0 +1,21 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + /** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +--> +<sections xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Page/etc/SectionObject.xsd"> + <section name="PayPalExpressCheckoutConfigSection"> + <element name="configureBtn" type="button" selector="#payment_{{countryCode}}_paypal_alternative_payment_methods_express_checkout_{{countryCode}}-head" parameterized="true"/> + <element name="email" type="input" selector="#payment_{{countryCode}}_paypal_alternative_payment_methods_express_checkout_{{countryCode}}_express_checkout_required_express_checkout_required_express_checkout_business_account" parameterized="true"/> + <element name="apiMethod" type="input" selector="#payment_{{countryCode}}_paypal_alternative_payment_methods_express_checkout_{{countryCode}}_express_checkout_required_express_checkout_required_express_checkout_api_authentication" parameterized="true"/> + <element name="username" type="input" selector="#payment_{{countryCode}}_paypal_alternative_payment_methods_express_checkout_{{countryCode}}_express_checkout_required_express_checkout_required_express_checkout_api_username" parameterized="true"/> + <element name="password" type="input" selector="#payment_{{countryCode}}_paypal_alternative_payment_methods_express_checkout_{{countryCode}}_express_checkout_required_express_checkout_required_express_checkout_api_password" parameterized="true"/> + <element name="signature" type="input" selector="#payment_{{countryCode}}_paypal_alternative_payment_methods_express_checkout_{{countryCode}}_express_checkout_required_express_checkout_required_express_checkout_api_signature" parameterized="true"/> + <element name="sandboxMode" type="input" selector="#payment_{{countryCode}}_paypal_alternative_payment_methods_express_checkout_{{countryCode}}_express_checkout_required_express_checkout_required_express_checkout_sandbox_flag" parameterized="true"/> + <element name="enableSolution" type="input" selector="#payment_{{countryCode}}_paypal_alternative_payment_methods_express_checkout_{{countryCode}}_express_checkout_required_enable_express_checkout" parameterized="true"/> + <element name="merchantID" type="input" selector="#payment_{{countryCode}}_paypal_alternative_payment_methods_express_checkout_{{countryCode}}_express_checkout_required_merchant_id" parameterized="true"/> + </section> +</sections> diff --git a/app/code/Magento/Paypal/Test/Mftf/Section/PayPalExpressCheckoutConfigSection/PayPalExpressCheckoutOtherCountryConfigSection.xml b/app/code/Magento/Paypal/Test/Mftf/Section/PayPalExpressCheckoutConfigSection/PayPalExpressCheckoutOtherCountryConfigSection.xml new file mode 100644 index 0000000000000..103537da4144a --- /dev/null +++ b/app/code/Magento/Paypal/Test/Mftf/Section/PayPalExpressCheckoutConfigSection/PayPalExpressCheckoutOtherCountryConfigSection.xml @@ -0,0 +1,14 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + /** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +--> +<sections xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Page/etc/SectionObject.xsd"> + <section name="PayPalExpressCheckoutOtherCountryConfigSection"> + <element name="configureBtn" type="button" selector="#payment_{{countryCode}}_express_checkout_other-head" parameterized="true"/> + <element name="enableSolution" type="input" selector="#payment_{{countryCode}}_express_checkout_other_express_checkout_required_enable_express_checkout" parameterized="true"/> + </section> +</sections> diff --git a/app/code/Magento/Paypal/Test/Mftf/Section/PayPalExpressCheckoutConfigSection/PayPalPaymentSection.xml b/app/code/Magento/Paypal/Test/Mftf/Section/PayPalExpressCheckoutConfigSection/PayPalPaymentSection.xml new file mode 100644 index 0000000000000..a64ff13e8f627 --- /dev/null +++ b/app/code/Magento/Paypal/Test/Mftf/Section/PayPalExpressCheckoutConfigSection/PayPalPaymentSection.xml @@ -0,0 +1,23 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + /** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +--> +<sections xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Page/etc/SectionObject.xsd"> + <section name="PayPalPaymentSection"> + <element name="guestCheckout" type="input" selector="#guest"/> + <element name="loginSection" type="input" selector=" #main>#login"/> + <element name="email" type="input" selector="//input[contains(@name, 'email') and not(contains(@style, 'display:none'))]"/> + <element name="password" type="input" selector="//input[contains(@name, 'password') and not(contains(@style, 'display:none'))]"/> + <element name="loginBtn" type="input" selector="button#btnLogin"/> + <element name="reviewUserInfo" type="text" selector="[data-testid=personalized-banner-content]"/> + <element name="cartIcon" type="text" selector="[data-testid='header-show-cart-dropdown-btn']"/> + <element name="itemName" type="text" selector="//p[contains(@class,'CartDropdown_line') and text()='{{productName}}']" parameterized="true"/> + <element name="PayPalSubmitBtn" type="text" selector="#payment-submit-btn"/> + <element name="nextButton" type="button" selector="#btnNext"/> + <element name="continueButton" type="button" selector=".continueButton"/> + </section> +</sections> diff --git a/app/code/Magento/Paypal/Test/Mftf/Test/AdminCheckDefaultValueOfPayPalCustomizeButtonTest.xml b/app/code/Magento/Paypal/Test/Mftf/Test/AdminCheckDefaultValueOfPayPalCustomizeButtonTest.xml index 5c10bc9536fcf..3a3ae03d63bf3 100644 --- a/app/code/Magento/Paypal/Test/Mftf/Test/AdminCheckDefaultValueOfPayPalCustomizeButtonTest.xml +++ b/app/code/Magento/Paypal/Test/Mftf/Test/AdminCheckDefaultValueOfPayPalCustomizeButtonTest.xml @@ -22,11 +22,11 @@ </skip> </annotations> <before> - <actionGroup ref="LoginActionGroup" stepKey="login"/> + <actionGroup ref="AdminLoginActionGroup" stepKey="login"/> <actionGroup ref="ConfigPayPalExpressCheckout" stepKey="ConfigPayPalExpressCheckout"/> </before> <after> - <actionGroup ref="logout" stepKey="logoutFromAdmin"/> + <actionGroup ref="AdminLogoutActionGroup" stepKey="logoutFromAdmin"/> </after> <amOnPage url="{{AdminConfigPaymentMethodsPage.url}}" stepKey="navigateToPaymentConfigurationPage"/> <waitForPageLoad stepKey="waitForPageLoad1"/> diff --git a/app/code/Magento/Paypal/Test/Mftf/Test/AdminConfigPaymentsConflictResolutionForPayPal.xml b/app/code/Magento/Paypal/Test/Mftf/Test/AdminConfigPaymentsConflictResolutionForPayPal.xml deleted file mode 100644 index e918a417dcfde..0000000000000 --- a/app/code/Magento/Paypal/Test/Mftf/Test/AdminConfigPaymentsConflictResolutionForPayPal.xml +++ /dev/null @@ -1,276 +0,0 @@ -<?xml version="1.0" encoding="UTF-8"?> -<!-- - /** - * Copyright © Magento, Inc. All rights reserved. - * See COPYING.txt for license details. - */ ---> - -<tests xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" - xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/testSchema.xsd"> - <test name="AdminConfigPaymentsConflictResolutionForPayPalInUnitedKingdom"> - <annotations> - <features value="PayPal"/> - <stories value="Payment methods"/> - <title value="Conflict resolution for PayPal in United Kingdom"/> - <description value="A popup should show when enabling different paypal solutions when one is already enabled for merchant country United Kingdom"/> - <severity value="MAJOR"/> - <testCaseId value="MC-16679"/> - <group value="paypal"/> - </annotations> - <before> - <actionGroup ref="LoginAsAdmin" stepKey="loginAsAdmin"/> - <actionGroup ref="SampleConfigPayPalExpressCheckoutActionGroup" stepKey="ConfigPayPalExpress"> - <argument name="credentials" value="SamplePaypalExpressConfig"/> - </actionGroup> - </before> - <after> - <magentoCLI command="config:set paypal/general/merchant_country US" stepKey="setMerchantCountry"/> - <magentoCLI command="config:set payment/paypal_express/active 0" stepKey="disablePayPalExpress"/> - <magentoCLI command="config:set payment/wps_express/active 0" stepKey="disableWPSExpress"/> - <magentoCLI command="config:set payment/hosted_pro/active 0" stepKey="disableHostedProExpress"/> - <actionGroup ref="logout" stepKey="logout"/> - </after> - <!-- Change Merchant Country --> - <comment userInput="Change Merchant Country" stepKey="changeMerchantCountryComment"/> - <waitForElementVisible selector="{{PaymentsConfigSection.merchantCountry}}" stepKey="waitForMerchantCountry"/> - <selectOption selector="{{PaymentsConfigSection.merchantCountry}}" userInput="United Kingdom" stepKey="setMerchantCountry"/> - <waitForPageLoad stepKey="waitForPageLoad1"/> - <!-- Enable WPS Express --> - <comment userInput="Enable WPS Express" stepKey="enableWPSExpressComment"/> - <actionGroup ref="EnablePayPalConfigurationActionGroup" stepKey="EnableWPSExpress"> - <argument name="payPalConfigType" value="WPSExpressConfigSection"/> - <argument name="countryCode" value="gb"/> - </actionGroup> - <!-- Check only the correct solution is enabled --> - <comment userInput="Check only the correct solution is enabled" stepKey="checkOnlyTheCorrectSolutionIsEnabledComment1"/> - <actionGroup ref="CheckEnableOptionPayPalConfigurationActionGroup" stepKey="checkPayPalExpressIsDisabled"> - <argument name="payPalConfigType" value="PayPalExpressCheckoutConfigSection"/> - <argument name="enabledOption" value="No"/> - <argument name="countryCode" value="gb"/> - </actionGroup> - <actionGroup ref="CheckEnableOptionPayPalConfigurationActionGroup" stepKey="checkWPSExpressIsEnabled"> - <argument name="payPalConfigType" value="WPSExpressConfigSection"/> - <argument name="enabledOption" value="Yes"/> - <argument name="countryCode" value="gb"/> - </actionGroup> - <!-- Enable Pro Hosted With Express Checkout --> - <comment userInput="Enable Pro Hosted With Express Checkout" stepKey="enableProHostedWithExpressCheckoutComment"/> - <actionGroup ref="EnablePayPalConfigurationActionGroup" stepKey="EnableProHostedWithExpressCheckou"> - <argument name="payPalConfigType" value="PaymentsProHostedWithExpressCheckoutConfigSection"/> - <argument name="countryCode" value="gb"/> - </actionGroup> - <!-- Check only the correct solution is enabled --> - <comment userInput="Check only the correct solution is enabled" stepKey="checkOnlyTheCorrectSolutionIsEnabledComment2"/> - <actionGroup ref="CheckEnableOptionPayPalConfigurationActionGroup" stepKey="checkWPSExpressIsDisabled"> - <argument name="payPalConfigType" value="WPSExpressConfigSection"/> - <argument name="enabledOption" value="No"/> - <argument name="countryCode" value="gb"/> - </actionGroup> - <actionGroup ref="CheckEnableOptionPayPalConfigurationActionGroup" stepKey="checkProHostedIsEnabled"> - <argument name="payPalConfigType" value="PaymentsProHostedWithExpressCheckoutConfigSection"/> - <argument name="enabledOption" value="Yes"/> - <argument name="countryCode" value="gb"/> - </actionGroup> - </test> - <test name="AdminConfigPaymentsConflictResolutionForPayPalInJapan" extends="AdminConfigPaymentsConflictResolutionForPayPalInUnitedKingdom"> - <annotations> - <features value="PayPal"/> - <stories value="Payment methods"/> - <title value="Conflict resolution for PayPal in Japan"/> - <description value="A popup should show when enabling different paypal solutions when one is already enabled for merchant country Japan"/> - <severity value="MAJOR"/> - <testCaseId value="MC-13146"/> - <group value="paypal"/> - </annotations> - <selectOption selector="{{PaymentsConfigSection.merchantCountry}}" userInput="Japan" stepKey="setMerchantCountry"/> - <actionGroup ref="EnablePayPalConfigurationActionGroup" stepKey="EnableWPSExpress"> - <argument name="payPalConfigType" value="WPSOtherConfigSection"/> - <argument name="countryCode" value="jp"/> - </actionGroup> - <actionGroup ref="CheckEnableOptionPayPalConfigurationActionGroup" stepKey="checkPayPalExpressIsDisabled"> - <argument name="payPalConfigType" value="PayPalExpressCheckoutOtherCountryConfigSection"/> - <argument name="enabledOption" value="No"/> - <argument name="countryCode" value="jp"/> - </actionGroup> - <actionGroup ref="CheckEnableOptionPayPalConfigurationActionGroup" stepKey="checkWPSExpressIsEnabled"> - <argument name="payPalConfigType" value="WPSOtherConfigSection"/> - <argument name="enabledOption" value="Yes"/> - <argument name="countryCode" value="jp"/> - </actionGroup> - <actionGroup ref="EnablePayPalConfigurationActionGroup" stepKey="EnableProHostedWithExpressCheckou"> - <argument name="payPalConfigType" value="WebsitePaymentsPlusConfigSection"/> - <argument name="countryCode" value="jp"/> - </actionGroup> - <actionGroup ref="CheckEnableOptionPayPalConfigurationActionGroup" stepKey="checkWPSExpressIsDisabled"> - <argument name="payPalConfigType" value="WPSOtherConfigSection"/> - <argument name="enabledOption" value="No"/> - <argument name="countryCode" value="jp"/> - </actionGroup> - <actionGroup ref="CheckEnableOptionPayPalConfigurationActionGroup" stepKey="checkProHostedIsEnabled"> - <argument name="payPalConfigType" value="WebsitePaymentsPlusConfigSection"/> - <argument name="enabledOption" value="Yes"/> - <argument name="countryCode" value="jp"/> - </actionGroup> - </test> - <test name="AdminConfigPaymentsConflictResolutionForPayPalInFrance" extends="AdminConfigPaymentsConflictResolutionForPayPalInUnitedKingdom"> - <annotations> - <features value="PayPal"/> - <stories value="Payment methods"/> - <title value="Conflict resolution for PayPal in France"/> - <description value="A popup should show when enabling different paypal solutions when one is already enabled for merchant country France"/> - <severity value="MAJOR"/> - <testCaseId value="MC-16675"/> - <group value="paypal"/> - </annotations> - <selectOption selector="{{PaymentsConfigSection.merchantCountry}}" userInput="France" stepKey="setMerchantCountry"/> - <actionGroup ref="EnablePayPalConfigurationActionGroup" stepKey="EnableWPSExpress"> - <argument name="payPalConfigType" value="WPSOtherConfigSection"/> - <argument name="countryCode" value="fr"/> - </actionGroup> - <actionGroup ref="CheckEnableOptionPayPalConfigurationActionGroup" stepKey="checkPayPalExpressIsDisabled"> - <argument name="payPalConfigType" value="PayPalExpressCheckoutOtherCountryConfigSection"/> - <argument name="enabledOption" value="No"/> - <argument name="countryCode" value="fr"/> - </actionGroup> - <actionGroup ref="CheckEnableOptionPayPalConfigurationActionGroup" stepKey="checkWPSExpressIsEnabled"> - <argument name="payPalConfigType" value="WPSOtherConfigSection"/> - <argument name="enabledOption" value="Yes"/> - <argument name="countryCode" value="fr"/> - </actionGroup> - <actionGroup ref="EnablePayPalConfigurationActionGroup" stepKey="EnableProHostedWithExpressCheckou"> - <argument name="payPalConfigType" value="WebsitePaymentsPlusConfigSection"/> - <argument name="countryCode" value="fr"/> - </actionGroup> - <actionGroup ref="CheckEnableOptionPayPalConfigurationActionGroup" stepKey="checkWPSExpressIsDisabled"> - <argument name="payPalConfigType" value="WPSOtherConfigSection"/> - <argument name="enabledOption" value="No"/> - <argument name="countryCode" value="fr"/> - </actionGroup> - <actionGroup ref="CheckEnableOptionPayPalConfigurationActionGroup" stepKey="checkProHostedIsEnabled"> - <argument name="payPalConfigType" value="WebsitePaymentsPlusConfigSection"/> - <argument name="enabledOption" value="Yes"/> - <argument name="countryCode" value="fr"/> - </actionGroup> - </test> - <test name="AdminConfigPaymentsConflictResolutionForPayPalInHongKong" extends="AdminConfigPaymentsConflictResolutionForPayPalInUnitedKingdom"> - <annotations> - <features value="PayPal"/> - <stories value="Payment methods"/> - <title value="Conflict resolution for PayPal in Hong Kong"/> - <description value="A popup should show when enabling different paypal solutions when one is already enabled for merchant country Hong Kong"/> - <severity value="MAJOR"/> - <testCaseId value="MC-16676"/> - <group value="paypal"/> - </annotations> - <selectOption selector="{{PaymentsConfigSection.merchantCountry}}" userInput="Hong Kong SAR China" stepKey="setMerchantCountry"/> - <actionGroup ref="EnablePayPalConfigurationActionGroup" stepKey="EnableWPSExpress"> - <argument name="payPalConfigType" value="WPSOtherConfigSection"/> - <argument name="countryCode" value="hk"/> - </actionGroup> - <actionGroup ref="CheckEnableOptionPayPalConfigurationActionGroup" stepKey="checkPayPalExpressIsDisabled"> - <argument name="payPalConfigType" value="PayPalExpressCheckoutOtherCountryConfigSection"/> - <argument name="enabledOption" value="No"/> - <argument name="countryCode" value="hk"/> - </actionGroup> - <actionGroup ref="CheckEnableOptionPayPalConfigurationActionGroup" stepKey="checkWPSExpressIsEnabled"> - <argument name="payPalConfigType" value="WPSOtherConfigSection"/> - <argument name="enabledOption" value="Yes"/> - <argument name="countryCode" value="hk"/> - </actionGroup> - <actionGroup ref="EnablePayPalConfigurationActionGroup" stepKey="EnableProHostedWithExpressCheckou"> - <argument name="payPalConfigType" value="WebsitePaymentsPlusConfigSection"/> - <argument name="countryCode" value="hk"/> - </actionGroup> - <actionGroup ref="CheckEnableOptionPayPalConfigurationActionGroup" stepKey="checkWPSExpressIsDisabled"> - <argument name="payPalConfigType" value="WPSOtherConfigSection"/> - <argument name="enabledOption" value="No"/> - <argument name="countryCode" value="hk"/> - </actionGroup> - <actionGroup ref="CheckEnableOptionPayPalConfigurationActionGroup" stepKey="checkProHostedIsEnabled"> - <argument name="payPalConfigType" value="WebsitePaymentsPlusConfigSection"/> - <argument name="enabledOption" value="Yes"/> - <argument name="countryCode" value="hk"/> - </actionGroup> - </test> - <test name="AdminConfigPaymentsConflictResolutionForPayPalInItaly" extends="AdminConfigPaymentsConflictResolutionForPayPalInUnitedKingdom"> - <annotations> - <features value="PayPal"/> - <stories value="Payment methods"/> - <title value="Conflict resolution for PayPal in Italy"/> - <description value="A popup should show when enabling different paypal solutions when one is already enabled for merchant country Italy"/> - <severity value="MAJOR"/> - <testCaseId value="MC-16677"/> - <group value="paypal"/> - </annotations> - <selectOption selector="{{PaymentsConfigSection.merchantCountry}}" userInput="Italy" stepKey="setMerchantCountry"/> - <actionGroup ref="EnablePayPalConfigurationActionGroup" stepKey="EnableWPSExpress"> - <argument name="payPalConfigType" value="WPSOtherConfigSection"/> - <argument name="countryCode" value="it"/> - </actionGroup> - <actionGroup ref="CheckEnableOptionPayPalConfigurationActionGroup" stepKey="checkPayPalExpressIsDisabled"> - <argument name="payPalConfigType" value="PayPalExpressCheckoutOtherCountryConfigSection"/> - <argument name="enabledOption" value="No"/> - <argument name="countryCode" value="it"/> - </actionGroup> - <actionGroup ref="CheckEnableOptionPayPalConfigurationActionGroup" stepKey="checkWPSExpressIsEnabled"> - <argument name="payPalConfigType" value="WPSOtherConfigSection"/> - <argument name="enabledOption" value="Yes"/> - <argument name="countryCode" value="it"/> - </actionGroup> - <actionGroup ref="EnablePayPalConfigurationActionGroup" stepKey="EnableProHostedWithExpressCheckou"> - <argument name="payPalConfigType" value="WebsitePaymentsPlusConfigSection"/> - <argument name="countryCode" value="it"/> - </actionGroup> - <actionGroup ref="CheckEnableOptionPayPalConfigurationActionGroup" stepKey="checkWPSExpressIsDisabled"> - <argument name="payPalConfigType" value="WPSOtherConfigSection"/> - <argument name="enabledOption" value="No"/> - <argument name="countryCode" value="it"/> - </actionGroup> - <actionGroup ref="CheckEnableOptionPayPalConfigurationActionGroup" stepKey="checkProHostedIsEnabled"> - <argument name="payPalConfigType" value="WebsitePaymentsPlusConfigSection"/> - <argument name="enabledOption" value="Yes"/> - <argument name="countryCode" value="it"/> - </actionGroup> - </test> - <test name="AdminConfigPaymentsConflictResolutionForPayPalInSpain" extends="AdminConfigPaymentsConflictResolutionForPayPalInUnitedKingdom"> - <annotations> - <features value="PayPal"/> - <stories value="Payment methods"/> - <title value="Conflict resolution for PayPal in Spain"/> - <description value="A popup should show when enabling different paypal solutions when one is already enabled for merchant country Spain"/> - <severity value="MAJOR"/> - <testCaseId value="MC-16678"/> - <group value="paypal"/> - </annotations> - <selectOption selector="{{PaymentsConfigSection.merchantCountry}}" userInput="Spain" stepKey="setMerchantCountry"/> - <actionGroup ref="EnablePayPalConfigurationActionGroup" stepKey="EnableWPSExpress"> - <argument name="payPalConfigType" value="WPSOtherConfigSection"/> - <argument name="countryCode" value="es"/> - </actionGroup> - <actionGroup ref="CheckEnableOptionPayPalConfigurationActionGroup" stepKey="checkPayPalExpressIsDisabled"> - <argument name="payPalConfigType" value="PayPalExpressCheckoutOtherCountryConfigSection"/> - <argument name="enabledOption" value="No"/> - <argument name="countryCode" value="es"/> - </actionGroup> - <actionGroup ref="CheckEnableOptionPayPalConfigurationActionGroup" stepKey="checkWPSExpressIsEnabled"> - <argument name="payPalConfigType" value="WPSOtherConfigSection"/> - <argument name="enabledOption" value="Yes"/> - <argument name="countryCode" value="es"/> - </actionGroup> - <actionGroup ref="EnablePayPalConfigurationActionGroup" stepKey="EnableProHostedWithExpressCheckou"> - <argument name="payPalConfigType" value="WebsitePaymentsPlusConfigSection"/> - <argument name="countryCode" value="es"/> - </actionGroup> - <actionGroup ref="CheckEnableOptionPayPalConfigurationActionGroup" stepKey="checkWPSExpressIsDisabled"> - <argument name="payPalConfigType" value="WPSOtherConfigSection"/> - <argument name="enabledOption" value="No"/> - <argument name="countryCode" value="es"/> - </actionGroup> - <actionGroup ref="CheckEnableOptionPayPalConfigurationActionGroup" stepKey="checkProHostedIsEnabled"> - <argument name="payPalConfigType" value="WebsitePaymentsPlusConfigSection"/> - <argument name="enabledOption" value="Yes"/> - <argument name="countryCode" value="es"/> - </actionGroup> - </test> -</tests> diff --git a/app/code/Magento/Paypal/Test/Mftf/Test/AdminConfigPaymentsConflictResolutionForPayPalTest/AdminConfigPaymentsConflictResolutionForPayPalInFranceTest.xml b/app/code/Magento/Paypal/Test/Mftf/Test/AdminConfigPaymentsConflictResolutionForPayPalTest/AdminConfigPaymentsConflictResolutionForPayPalInFranceTest.xml new file mode 100644 index 0000000000000..3b70bc84037ce --- /dev/null +++ b/app/code/Magento/Paypal/Test/Mftf/Test/AdminConfigPaymentsConflictResolutionForPayPalTest/AdminConfigPaymentsConflictResolutionForPayPalInFranceTest.xml @@ -0,0 +1,50 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + /** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +--> +<tests xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/testSchema.xsd"> + <test name="AdminConfigPaymentsConflictResolutionForPayPalInFranceTest" extends="AdminConfigPaymentsConflictResolutionForPayPalInUnitedKingdomTest"> + <annotations> + <features value="PayPal"/> + <stories value="Payment methods"/> + <title value="Conflict resolution for PayPal in France"/> + <description value="A popup should show when enabling different paypal solutions when one is already enabled for merchant country France"/> + <severity value="MAJOR"/> + <testCaseId value="MC-16675"/> + <group value="paypal"/> + </annotations> + <selectOption selector="{{PaymentsConfigSection.merchantCountry}}" userInput="France" stepKey="setMerchantCountry"/> + <actionGroup ref="EnablePayPalConfigurationActionGroup" stepKey="EnableWPSExpress"> + <argument name="payPalConfigType" value="WPSOtherConfigSection"/> + <argument name="countryCode" value="fr"/> + </actionGroup> + <actionGroup ref="CheckEnableOptionPayPalConfigurationActionGroup" stepKey="checkPayPalExpressIsDisabled"> + <argument name="payPalConfigType" value="PayPalExpressCheckoutOtherCountryConfigSection"/> + <argument name="enabledOption" value="No"/> + <argument name="countryCode" value="fr"/> + </actionGroup> + <actionGroup ref="CheckEnableOptionPayPalConfigurationActionGroup" stepKey="checkWPSExpressIsEnabled"> + <argument name="payPalConfigType" value="WPSOtherConfigSection"/> + <argument name="enabledOption" value="Yes"/> + <argument name="countryCode" value="fr"/> + </actionGroup> + <actionGroup ref="EnablePayPalConfigurationActionGroup" stepKey="EnableProHostedWithExpressCheckou"> + <argument name="payPalConfigType" value="WebsitePaymentsPlusConfigSection"/> + <argument name="countryCode" value="fr"/> + </actionGroup> + <actionGroup ref="CheckEnableOptionPayPalConfigurationActionGroup" stepKey="checkWPSExpressIsDisabled"> + <argument name="payPalConfigType" value="WPSOtherConfigSection"/> + <argument name="enabledOption" value="No"/> + <argument name="countryCode" value="fr"/> + </actionGroup> + <actionGroup ref="CheckEnableOptionPayPalConfigurationActionGroup" stepKey="checkProHostedIsEnabled"> + <argument name="payPalConfigType" value="WebsitePaymentsPlusConfigSection"/> + <argument name="enabledOption" value="Yes"/> + <argument name="countryCode" value="fr"/> + </actionGroup> + </test> +</tests> diff --git a/app/code/Magento/Paypal/Test/Mftf/Test/AdminConfigPaymentsConflictResolutionForPayPalTest/AdminConfigPaymentsConflictResolutionForPayPalInHongKongTest.xml b/app/code/Magento/Paypal/Test/Mftf/Test/AdminConfigPaymentsConflictResolutionForPayPalTest/AdminConfigPaymentsConflictResolutionForPayPalInHongKongTest.xml new file mode 100644 index 0000000000000..038ee1c04c482 --- /dev/null +++ b/app/code/Magento/Paypal/Test/Mftf/Test/AdminConfigPaymentsConflictResolutionForPayPalTest/AdminConfigPaymentsConflictResolutionForPayPalInHongKongTest.xml @@ -0,0 +1,50 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + /** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +--> +<tests xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/testSchema.xsd"> + <test name="AdminConfigPaymentsConflictResolutionForPayPalInHongKongTest" extends="AdminConfigPaymentsConflictResolutionForPayPalInUnitedKingdomTest"> + <annotations> + <features value="PayPal"/> + <stories value="Payment methods"/> + <title value="Conflict resolution for PayPal in Hong Kong"/> + <description value="A popup should show when enabling different paypal solutions when one is already enabled for merchant country Hong Kong"/> + <severity value="MAJOR"/> + <testCaseId value="MC-16676"/> + <group value="paypal"/> + </annotations> + <selectOption selector="{{PaymentsConfigSection.merchantCountry}}" userInput="Hong Kong SAR China" stepKey="setMerchantCountry"/> + <actionGroup ref="EnablePayPalConfigurationActionGroup" stepKey="EnableWPSExpress"> + <argument name="payPalConfigType" value="WPSOtherConfigSection"/> + <argument name="countryCode" value="hk"/> + </actionGroup> + <actionGroup ref="CheckEnableOptionPayPalConfigurationActionGroup" stepKey="checkPayPalExpressIsDisabled"> + <argument name="payPalConfigType" value="PayPalExpressCheckoutOtherCountryConfigSection"/> + <argument name="enabledOption" value="No"/> + <argument name="countryCode" value="hk"/> + </actionGroup> + <actionGroup ref="CheckEnableOptionPayPalConfigurationActionGroup" stepKey="checkWPSExpressIsEnabled"> + <argument name="payPalConfigType" value="WPSOtherConfigSection"/> + <argument name="enabledOption" value="Yes"/> + <argument name="countryCode" value="hk"/> + </actionGroup> + <actionGroup ref="EnablePayPalConfigurationActionGroup" stepKey="EnableProHostedWithExpressCheckou"> + <argument name="payPalConfigType" value="WebsitePaymentsPlusConfigSection"/> + <argument name="countryCode" value="hk"/> + </actionGroup> + <actionGroup ref="CheckEnableOptionPayPalConfigurationActionGroup" stepKey="checkWPSExpressIsDisabled"> + <argument name="payPalConfigType" value="WPSOtherConfigSection"/> + <argument name="enabledOption" value="No"/> + <argument name="countryCode" value="hk"/> + </actionGroup> + <actionGroup ref="CheckEnableOptionPayPalConfigurationActionGroup" stepKey="checkProHostedIsEnabled"> + <argument name="payPalConfigType" value="WebsitePaymentsPlusConfigSection"/> + <argument name="enabledOption" value="Yes"/> + <argument name="countryCode" value="hk"/> + </actionGroup> + </test> +</tests> diff --git a/app/code/Magento/Paypal/Test/Mftf/Test/AdminConfigPaymentsConflictResolutionForPayPalTest/AdminConfigPaymentsConflictResolutionForPayPalInItalyTest.xml b/app/code/Magento/Paypal/Test/Mftf/Test/AdminConfigPaymentsConflictResolutionForPayPalTest/AdminConfigPaymentsConflictResolutionForPayPalInItalyTest.xml new file mode 100644 index 0000000000000..ad24d2c2c95d5 --- /dev/null +++ b/app/code/Magento/Paypal/Test/Mftf/Test/AdminConfigPaymentsConflictResolutionForPayPalTest/AdminConfigPaymentsConflictResolutionForPayPalInItalyTest.xml @@ -0,0 +1,50 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + /** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +--> +<tests xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/testSchema.xsd"> + <test name="AdminConfigPaymentsConflictResolutionForPayPalInItalyTest" extends="AdminConfigPaymentsConflictResolutionForPayPalInUnitedKingdomTest"> + <annotations> + <features value="PayPal"/> + <stories value="Payment methods"/> + <title value="Conflict resolution for PayPal in Italy"/> + <description value="A popup should show when enabling different paypal solutions when one is already enabled for merchant country Italy"/> + <severity value="MAJOR"/> + <testCaseId value="MC-16677"/> + <group value="paypal"/> + </annotations> + <selectOption selector="{{PaymentsConfigSection.merchantCountry}}" userInput="Italy" stepKey="setMerchantCountry"/> + <actionGroup ref="EnablePayPalConfigurationActionGroup" stepKey="EnableWPSExpress"> + <argument name="payPalConfigType" value="WPSOtherConfigSection"/> + <argument name="countryCode" value="it"/> + </actionGroup> + <actionGroup ref="CheckEnableOptionPayPalConfigurationActionGroup" stepKey="checkPayPalExpressIsDisabled"> + <argument name="payPalConfigType" value="PayPalExpressCheckoutOtherCountryConfigSection"/> + <argument name="enabledOption" value="No"/> + <argument name="countryCode" value="it"/> + </actionGroup> + <actionGroup ref="CheckEnableOptionPayPalConfigurationActionGroup" stepKey="checkWPSExpressIsEnabled"> + <argument name="payPalConfigType" value="WPSOtherConfigSection"/> + <argument name="enabledOption" value="Yes"/> + <argument name="countryCode" value="it"/> + </actionGroup> + <actionGroup ref="EnablePayPalConfigurationActionGroup" stepKey="EnableProHostedWithExpressCheckou"> + <argument name="payPalConfigType" value="WebsitePaymentsPlusConfigSection"/> + <argument name="countryCode" value="it"/> + </actionGroup> + <actionGroup ref="CheckEnableOptionPayPalConfigurationActionGroup" stepKey="checkWPSExpressIsDisabled"> + <argument name="payPalConfigType" value="WPSOtherConfigSection"/> + <argument name="enabledOption" value="No"/> + <argument name="countryCode" value="it"/> + </actionGroup> + <actionGroup ref="CheckEnableOptionPayPalConfigurationActionGroup" stepKey="checkProHostedIsEnabled"> + <argument name="payPalConfigType" value="WebsitePaymentsPlusConfigSection"/> + <argument name="enabledOption" value="Yes"/> + <argument name="countryCode" value="it"/> + </actionGroup> + </test> +</tests> diff --git a/app/code/Magento/Paypal/Test/Mftf/Test/AdminConfigPaymentsConflictResolutionForPayPalTest/AdminConfigPaymentsConflictResolutionForPayPalInJapanTest.xml b/app/code/Magento/Paypal/Test/Mftf/Test/AdminConfigPaymentsConflictResolutionForPayPalTest/AdminConfigPaymentsConflictResolutionForPayPalInJapanTest.xml new file mode 100644 index 0000000000000..846f4e6dd5ae4 --- /dev/null +++ b/app/code/Magento/Paypal/Test/Mftf/Test/AdminConfigPaymentsConflictResolutionForPayPalTest/AdminConfigPaymentsConflictResolutionForPayPalInJapanTest.xml @@ -0,0 +1,50 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + /** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +--> +<tests xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/testSchema.xsd"> + <test name="AdminConfigPaymentsConflictResolutionForPayPalInJapanTest" extends="AdminConfigPaymentsConflictResolutionForPayPalInUnitedKingdomTest"> + <annotations> + <features value="PayPal"/> + <stories value="Payment methods"/> + <title value="Conflict resolution for PayPal in Japan"/> + <description value="A popup should show when enabling different paypal solutions when one is already enabled for merchant country Japan"/> + <severity value="MAJOR"/> + <testCaseId value="MC-13146"/> + <group value="paypal"/> + </annotations> + <selectOption selector="{{PaymentsConfigSection.merchantCountry}}" userInput="Japan" stepKey="setMerchantCountry"/> + <actionGroup ref="EnablePayPalConfigurationActionGroup" stepKey="EnableWPSExpress"> + <argument name="payPalConfigType" value="WPSOtherConfigSection"/> + <argument name="countryCode" value="jp"/> + </actionGroup> + <actionGroup ref="CheckEnableOptionPayPalConfigurationActionGroup" stepKey="checkPayPalExpressIsDisabled"> + <argument name="payPalConfigType" value="PayPalExpressCheckoutOtherCountryConfigSection"/> + <argument name="enabledOption" value="No"/> + <argument name="countryCode" value="jp"/> + </actionGroup> + <actionGroup ref="CheckEnableOptionPayPalConfigurationActionGroup" stepKey="checkWPSExpressIsEnabled"> + <argument name="payPalConfigType" value="WPSOtherConfigSection"/> + <argument name="enabledOption" value="Yes"/> + <argument name="countryCode" value="jp"/> + </actionGroup> + <actionGroup ref="EnablePayPalConfigurationActionGroup" stepKey="EnableProHostedWithExpressCheckou"> + <argument name="payPalConfigType" value="WebsitePaymentsPlusConfigSection"/> + <argument name="countryCode" value="jp"/> + </actionGroup> + <actionGroup ref="CheckEnableOptionPayPalConfigurationActionGroup" stepKey="checkWPSExpressIsDisabled"> + <argument name="payPalConfigType" value="WPSOtherConfigSection"/> + <argument name="enabledOption" value="No"/> + <argument name="countryCode" value="jp"/> + </actionGroup> + <actionGroup ref="CheckEnableOptionPayPalConfigurationActionGroup" stepKey="checkProHostedIsEnabled"> + <argument name="payPalConfigType" value="WebsitePaymentsPlusConfigSection"/> + <argument name="enabledOption" value="Yes"/> + <argument name="countryCode" value="jp"/> + </actionGroup> + </test> +</tests> diff --git a/app/code/Magento/Paypal/Test/Mftf/Test/AdminConfigPaymentsConflictResolutionForPayPalTest/AdminConfigPaymentsConflictResolutionForPayPalInSpainTest.xml b/app/code/Magento/Paypal/Test/Mftf/Test/AdminConfigPaymentsConflictResolutionForPayPalTest/AdminConfigPaymentsConflictResolutionForPayPalInSpainTest.xml new file mode 100644 index 0000000000000..b0317f9ac7a3d --- /dev/null +++ b/app/code/Magento/Paypal/Test/Mftf/Test/AdminConfigPaymentsConflictResolutionForPayPalTest/AdminConfigPaymentsConflictResolutionForPayPalInSpainTest.xml @@ -0,0 +1,50 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + /** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +--> +<tests xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/testSchema.xsd"> + <test name="AdminConfigPaymentsConflictResolutionForPayPalInSpainTest" extends="AdminConfigPaymentsConflictResolutionForPayPalInUnitedKingdomTest"> + <annotations> + <features value="PayPal"/> + <stories value="Payment methods"/> + <title value="Conflict resolution for PayPal in Spain"/> + <description value="A popup should show when enabling different paypal solutions when one is already enabled for merchant country Spain"/> + <severity value="MAJOR"/> + <testCaseId value="MC-16678"/> + <group value="paypal"/> + </annotations> + <selectOption selector="{{PaymentsConfigSection.merchantCountry}}" userInput="Spain" stepKey="setMerchantCountry"/> + <actionGroup ref="EnablePayPalConfigurationActionGroup" stepKey="EnableWPSExpress"> + <argument name="payPalConfigType" value="WPSOtherConfigSection"/> + <argument name="countryCode" value="es"/> + </actionGroup> + <actionGroup ref="CheckEnableOptionPayPalConfigurationActionGroup" stepKey="checkPayPalExpressIsDisabled"> + <argument name="payPalConfigType" value="PayPalExpressCheckoutOtherCountryConfigSection"/> + <argument name="enabledOption" value="No"/> + <argument name="countryCode" value="es"/> + </actionGroup> + <actionGroup ref="CheckEnableOptionPayPalConfigurationActionGroup" stepKey="checkWPSExpressIsEnabled"> + <argument name="payPalConfigType" value="WPSOtherConfigSection"/> + <argument name="enabledOption" value="Yes"/> + <argument name="countryCode" value="es"/> + </actionGroup> + <actionGroup ref="EnablePayPalConfigurationActionGroup" stepKey="EnableProHostedWithExpressCheckou"> + <argument name="payPalConfigType" value="WebsitePaymentsPlusConfigSection"/> + <argument name="countryCode" value="es"/> + </actionGroup> + <actionGroup ref="CheckEnableOptionPayPalConfigurationActionGroup" stepKey="checkWPSExpressIsDisabled"> + <argument name="payPalConfigType" value="WPSOtherConfigSection"/> + <argument name="enabledOption" value="No"/> + <argument name="countryCode" value="es"/> + </actionGroup> + <actionGroup ref="CheckEnableOptionPayPalConfigurationActionGroup" stepKey="checkProHostedIsEnabled"> + <argument name="payPalConfigType" value="WebsitePaymentsPlusConfigSection"/> + <argument name="enabledOption" value="Yes"/> + <argument name="countryCode" value="es"/> + </actionGroup> + </test> +</tests> diff --git a/app/code/Magento/Paypal/Test/Mftf/Test/AdminConfigPaymentsConflictResolutionForPayPalTest/AdminConfigPaymentsConflictResolutionForPayPalInUnitedKingdomTest.xml b/app/code/Magento/Paypal/Test/Mftf/Test/AdminConfigPaymentsConflictResolutionForPayPalTest/AdminConfigPaymentsConflictResolutionForPayPalInUnitedKingdomTest.xml new file mode 100644 index 0000000000000..ebdfb9e91ecf1 --- /dev/null +++ b/app/code/Magento/Paypal/Test/Mftf/Test/AdminConfigPaymentsConflictResolutionForPayPalTest/AdminConfigPaymentsConflictResolutionForPayPalInUnitedKingdomTest.xml @@ -0,0 +1,75 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + /** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +--> +<tests xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/testSchema.xsd"> + <test name="AdminConfigPaymentsConflictResolutionForPayPalInUnitedKingdomTest"> + <annotations> + <features value="PayPal"/> + <stories value="Payment methods"/> + <title value="Conflict resolution for PayPal in United Kingdom"/> + <description value="A popup should show when enabling different paypal solutions when one is already enabled for merchant country United Kingdom"/> + <severity value="MAJOR"/> + <testCaseId value="MC-16679"/> + <group value="paypal"/> + </annotations> + <before> + <actionGroup ref="AdminLoginActionGroup" stepKey="loginAsAdmin"/> + <actionGroup ref="SampleConfigPayPalExpressCheckoutActionGroup" stepKey="ConfigPayPalExpress"> + <argument name="credentials" value="SamplePaypalExpressConfig"/> + </actionGroup> + </before> + <after> + <magentoCLI command="config:set paypal/general/merchant_country US" stepKey="setMerchantCountry"/> + <magentoCLI command="config:set payment/paypal_express/active 0" stepKey="disablePayPalExpress"/> + <magentoCLI command="config:set payment/wps_express/active 0" stepKey="disableWPSExpress"/> + <magentoCLI command="config:set payment/hosted_pro/active 0" stepKey="disableHostedProExpress"/> + <actionGroup ref="AdminLogoutActionGroup" stepKey="logout"/> + </after> + <!-- Change Merchant Country --> + <comment userInput="Change Merchant Country" stepKey="changeMerchantCountryComment"/> + <waitForElementVisible selector="{{PaymentsConfigSection.merchantCountry}}" stepKey="waitForMerchantCountry"/> + <selectOption selector="{{PaymentsConfigSection.merchantCountry}}" userInput="United Kingdom" stepKey="setMerchantCountry"/> + <waitForPageLoad stepKey="waitForPageLoad1"/> + <!-- Enable WPS Express --> + <comment userInput="Enable WPS Express" stepKey="enableWPSExpressComment"/> + <actionGroup ref="EnablePayPalConfigurationActionGroup" stepKey="EnableWPSExpress"> + <argument name="payPalConfigType" value="WPSExpressConfigSection"/> + <argument name="countryCode" value="gb"/> + </actionGroup> + <!-- Check only the correct solution is enabled --> + <comment userInput="Check only the correct solution is enabled" stepKey="checkOnlyTheCorrectSolutionIsEnabledComment1"/> + <actionGroup ref="CheckEnableOptionPayPalConfigurationActionGroup" stepKey="checkPayPalExpressIsDisabled"> + <argument name="payPalConfigType" value="PayPalExpressCheckoutConfigSection"/> + <argument name="enabledOption" value="No"/> + <argument name="countryCode" value="gb"/> + </actionGroup> + <actionGroup ref="CheckEnableOptionPayPalConfigurationActionGroup" stepKey="checkWPSExpressIsEnabled"> + <argument name="payPalConfigType" value="WPSExpressConfigSection"/> + <argument name="enabledOption" value="Yes"/> + <argument name="countryCode" value="gb"/> + </actionGroup> + <!-- Enable Pro Hosted With Express Checkout --> + <comment userInput="Enable Pro Hosted With Express Checkout" stepKey="enableProHostedWithExpressCheckoutComment"/> + <actionGroup ref="EnablePayPalConfigurationActionGroup" stepKey="EnableProHostedWithExpressCheckou"> + <argument name="payPalConfigType" value="PaymentsProHostedWithExpressCheckoutConfigSection"/> + <argument name="countryCode" value="gb"/> + </actionGroup> + <!-- Check only the correct solution is enabled --> + <comment userInput="Check only the correct solution is enabled" stepKey="checkOnlyTheCorrectSolutionIsEnabledComment2"/> + <actionGroup ref="CheckEnableOptionPayPalConfigurationActionGroup" stepKey="checkWPSExpressIsDisabled"> + <argument name="payPalConfigType" value="WPSExpressConfigSection"/> + <argument name="enabledOption" value="No"/> + <argument name="countryCode" value="gb"/> + </actionGroup> + <actionGroup ref="CheckEnableOptionPayPalConfigurationActionGroup" stepKey="checkProHostedIsEnabled"> + <argument name="payPalConfigType" value="PaymentsProHostedWithExpressCheckoutConfigSection"/> + <argument name="enabledOption" value="Yes"/> + <argument name="countryCode" value="gb"/> + </actionGroup> + </test> +</tests> diff --git a/app/code/Magento/Paypal/Test/Mftf/Test/AdminConfigPaymentsSectionState.xml b/app/code/Magento/Paypal/Test/Mftf/Test/AdminConfigPaymentsSectionState.xml deleted file mode 100644 index 934449dfd136c..0000000000000 --- a/app/code/Magento/Paypal/Test/Mftf/Test/AdminConfigPaymentsSectionState.xml +++ /dev/null @@ -1,29 +0,0 @@ -<?xml version="1.0" encoding="UTF-8"?> -<!-- - /** - * Copyright © Magento, Inc. All rights reserved. - * See COPYING.txt for license details. - */ ---> - -<tests xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" - xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/testSchema.xsd"> - <test name="AdminConfigPaymentsSectionState"> - <annotations> - <features value="PayPal"/> - <stories value="Payment methods"/> - <title value="Other Payment Methods section in Admin expanded by default"/> - <description value="Other Payment Methods section in Admin expanded by default"/> - <severity value="AVERAGE"/> - <testCaseId value="MAGETWO-92043"/> - </annotations> - <after> - <actionGroup ref="logout" stepKey="amOnLogoutPage"/> - </after> - - <actionGroup ref="LoginAsAdmin" stepKey="loginAsAdmin1"/> - <amOnPage url="{{AdminConfigPaymentMethodsPage.url}}" stepKey="navigateToConfigurationPage"/> - <waitForPageLoad stepKey="waitForPageLoad1"/> - <seeElement selector="{{OtherPaymentsConfigSection.expandedTab}}" stepKey="seeSectionExpanded"/> - </test> -</tests> diff --git a/app/code/Magento/Paypal/Test/Mftf/Test/AdminConfigPaymentsSectionStateTest.xml b/app/code/Magento/Paypal/Test/Mftf/Test/AdminConfigPaymentsSectionStateTest.xml new file mode 100644 index 0000000000000..11b2c1e97b02f --- /dev/null +++ b/app/code/Magento/Paypal/Test/Mftf/Test/AdminConfigPaymentsSectionStateTest.xml @@ -0,0 +1,29 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + /** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +--> + +<tests xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/testSchema.xsd"> + <test name="AdminConfigPaymentsSectionStateTest"> + <annotations> + <features value="PayPal"/> + <stories value="Payment methods"/> + <title value="Other Payment Methods section in Admin expanded by default"/> + <description value="Other Payment Methods section in Admin expanded by default"/> + <severity value="AVERAGE"/> + <testCaseId value="MAGETWO-92043"/> + </annotations> + <after> + <actionGroup ref="AdminLogoutActionGroup" stepKey="amOnLogoutPage"/> + </after> + + <actionGroup ref="AdminLoginActionGroup" stepKey="loginAsAdmin1"/> + <amOnPage url="{{AdminConfigPaymentMethodsPage.url}}" stepKey="navigateToConfigurationPage"/> + <waitForPageLoad stepKey="waitForPageLoad1"/> + <seeElement selector="{{OtherPaymentsConfigSection.expandedTab}}" stepKey="seeSectionExpanded"/> + </test> +</tests> diff --git a/app/code/Magento/Paypal/Test/Mftf/Test/AdminOnePayPalSolutionsEnabledAtTheSameTimeTest.xml b/app/code/Magento/Paypal/Test/Mftf/Test/AdminOnePayPalSolutionsEnabledAtTheSameTimeTest.xml index fd0e74f38287d..c35e5dbe05e76 100644 --- a/app/code/Magento/Paypal/Test/Mftf/Test/AdminOnePayPalSolutionsEnabledAtTheSameTimeTest.xml +++ b/app/code/Magento/Paypal/Test/Mftf/Test/AdminOnePayPalSolutionsEnabledAtTheSameTimeTest.xml @@ -20,7 +20,7 @@ <group value="paypal"/> </annotations> <before> - <actionGroup ref="LoginAsAdmin" stepKey="loginAsAdmin"/> + <actionGroup ref="AdminLoginActionGroup" stepKey="loginAsAdmin"/> <!--Set PayPal Payments Standard Configs--> <comment userInput="Set PayPal Payments Standard Configs" stepKey="commentsetConfigs"/> <magentoCLI command="config:set paypal/wpp/api_authentication 0" stepKey="setApiAuthentication"/> @@ -32,7 +32,7 @@ <magentoCLI command="config:set payment/wps_express/active 1" stepKey="enableWPSExpress"/> </before> <after> - <actionGroup ref="logout" stepKey="logout"/> + <actionGroup ref="AdminLogoutActionGroup" stepKey="logout"/> <magentoCLI command="config:set payment/wps_express/active 0" stepKey="disableWPSExpress"/> <magentoCLI command="config:set payment/paypal_express/active 0" stepKey="disableExpressCheckout"/> </after> diff --git a/app/code/Magento/Paypal/Test/Mftf/Test/AdminReportsPayPalSettlementNavigateMenuTest.xml b/app/code/Magento/Paypal/Test/Mftf/Test/AdminReportsPayPalSettlementNavigateMenuTest.xml index 03f0167230e9f..a18717344c7a3 100644 --- a/app/code/Magento/Paypal/Test/Mftf/Test/AdminReportsPayPalSettlementNavigateMenuTest.xml +++ b/app/code/Magento/Paypal/Test/Mftf/Test/AdminReportsPayPalSettlementNavigateMenuTest.xml @@ -20,10 +20,10 @@ <group value="mtf_migrated"/> </annotations> <before> - <actionGroup ref="LoginAsAdmin" stepKey="LoginAsAdmin"/> + <actionGroup ref="AdminLoginActionGroup" stepKey="LoginAsAdmin"/> </before> <after> - <actionGroup ref="logout" stepKey="logout"/> + <actionGroup ref="AdminLogoutActionGroup" stepKey="logout"/> </after> <actionGroup ref="AdminNavigateMenuActionGroup" stepKey="navigateToPayPalSettlementPage"> <argument name="menuUiId" value="{{AdminMenuReports.dataUiId}}"/> diff --git a/app/code/Magento/Paypal/Test/Mftf/Test/AdminSalesBillingAgreementsNavigateMenuTest.xml b/app/code/Magento/Paypal/Test/Mftf/Test/AdminSalesBillingAgreementsNavigateMenuTest.xml index 8c3735fcbd253..140d2bcef28ae 100644 --- a/app/code/Magento/Paypal/Test/Mftf/Test/AdminSalesBillingAgreementsNavigateMenuTest.xml +++ b/app/code/Magento/Paypal/Test/Mftf/Test/AdminSalesBillingAgreementsNavigateMenuTest.xml @@ -20,10 +20,10 @@ <group value="mtf_migrated"/> </annotations> <before> - <actionGroup ref="LoginAsAdmin" stepKey="LoginAsAdmin"/> + <actionGroup ref="AdminLoginActionGroup" stepKey="LoginAsAdmin"/> </before> <after> - <actionGroup ref="logout" stepKey="logout"/> + <actionGroup ref="AdminLogoutActionGroup" stepKey="logout"/> </after> <actionGroup ref="AdminNavigateMenuActionGroup" stepKey="navigateToBillingAgreementsPage"> <argument name="menuUiId" value="{{AdminMenuSales.dataUiId}}"/> diff --git a/app/code/Magento/Paypal/Test/Mftf/Test/StorefrontCheckCreditButtonConfigurationTest.xml b/app/code/Magento/Paypal/Test/Mftf/Test/StorefrontCheckCreditButtonConfigurationTest.xml index 78a27106bf406..c8089085b7ee5 100644 --- a/app/code/Magento/Paypal/Test/Mftf/Test/StorefrontCheckCreditButtonConfigurationTest.xml +++ b/app/code/Magento/Paypal/Test/Mftf/Test/StorefrontCheckCreditButtonConfigurationTest.xml @@ -28,7 +28,7 @@ </createData> <!-- Create Customer --> <createData entity="Simple_US_Customer" stepKey="createCustomer"/> - <actionGroup ref="LoginActionGroup" stepKey="login"/> + <actionGroup ref="AdminLoginActionGroup" stepKey="login"/> <!--Config PayPal Express Checkout--> <comment userInput="config PayPal Express Checkout" stepKey="commemtConfigPayPalExpressCheckout"/> <actionGroup ref="ConfigPayPalExpressCheckout" stepKey="ConfigPayPalExpressCheckout"/> @@ -37,7 +37,7 @@ <deleteData stepKey="deleteCategory" createDataKey="createPreReqCategory"/> <deleteData stepKey="deleteProduct" createDataKey="createPreReqProduct"/> <deleteData stepKey="deleteCustomer" createDataKey="createCustomer"/> - <actionGroup ref="logout" stepKey="logoutFromAdmin"/> + <actionGroup ref="AdminLogoutActionGroup" stepKey="logoutFromAdmin"/> </after> <amOnPage url="{{AdminConfigPaymentMethodsPage.url}}" stepKey="navigateToPaymentConfigurationPage"/> <waitForPageLoad stepKey="waitForPageLoad1"/> diff --git a/app/code/Magento/Paypal/Test/Mftf/Test/StorefrontPaypalSmartButtonInCheckoutPageTest.xml b/app/code/Magento/Paypal/Test/Mftf/Test/StorefrontPaypalSmartButtonInCheckoutPageTest.xml index 6adba94e96890..b95d645787fed 100644 --- a/app/code/Magento/Paypal/Test/Mftf/Test/StorefrontPaypalSmartButtonInCheckoutPageTest.xml +++ b/app/code/Magento/Paypal/Test/Mftf/Test/StorefrontPaypalSmartButtonInCheckoutPageTest.xml @@ -16,9 +16,6 @@ <description value="Users are able to place order using Paypal Smart Button"/> <severity value="CRITICAL"/> <testCaseId value="MC-13690"/> - <skip> - <issueId value="DEVOPS-3311"/> - </skip> <group value="paypal"/> </annotations> <before> @@ -40,7 +37,7 @@ <createData entity="PaypalConfig" stepKey="createPaypalExpressConfig"/> <!-- Login --> - <actionGroup ref="LoginActionGroup" stepKey="login"/> + <actionGroup ref="AdminLoginActionGroup" stepKey="login"/> </before> <after> <!-- Cleanup Paypal configurations --> @@ -58,7 +55,7 @@ <deleteData stepKey="deleteCustomer" createDataKey="createCustomer"/> <!-- Logout --> - <actionGroup ref="logout" stepKey="logoutFromAdmin"/> + <actionGroup ref="AdminLogoutActionGroup" stepKey="logoutFromAdmin"/> </after> <!--Login to storefront as previously created customer--> diff --git a/app/code/Magento/Paypal/Test/Mftf/Test/StorefrontVerifySecureURLRedirectPaypalTest.xml b/app/code/Magento/Paypal/Test/Mftf/Test/StorefrontVerifySecureURLRedirectPaypalTest.xml index b2fcfa43181dc..cf0e4b3d0b370 100644 --- a/app/code/Magento/Paypal/Test/Mftf/Test/StorefrontVerifySecureURLRedirectPaypalTest.xml +++ b/app/code/Magento/Paypal/Test/Mftf/Test/StorefrontVerifySecureURLRedirectPaypalTest.xml @@ -8,7 +8,7 @@ <tests xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/testSchema.xsd"> - <test name="StorefrontVerifySecureURLRedirectPaypal"> + <test name="StorefrontVerifySecureURLRedirectPaypalTest"> <annotations> <features value="Paypal"/> <stories value="Storefront Secure URLs"/> diff --git a/app/code/Magento/Paypal/Test/Unit/Model/IpnTest.php b/app/code/Magento/Paypal/Test/Unit/Model/IpnTest.php index 53022e2db93f5..3de60edf1e648 100644 --- a/app/code/Magento/Paypal/Test/Unit/Model/IpnTest.php +++ b/app/code/Magento/Paypal/Test/Unit/Model/IpnTest.php @@ -53,6 +53,7 @@ protected function setUp() 'getEmailSent', 'save', 'getState', + 'setState', ]; $this->_orderMock = $this->createPartialMock(\Magento\Sales\Model\OrderFactory::class, $methods); $this->_orderMock->expects($this->any())->method('create')->will($this->returnSelf()); @@ -149,9 +150,12 @@ public function testPaymentReviewRegisterPaymentFraud() ->will($this->returnValue(true)); $this->_orderMock->expects($this->any())->method('getPayment')->will($this->returnValue($paymentMock)); $this->_orderMock->expects($this->any())->method('canFetchPaymentReviewUpdate')->will($this->returnValue(true)); - $this->_orderMock->expects($this->once())->method('getState')->will( + $this->_orderMock->expects($this->any())->method('getState')->will( $this->returnValue(Order::STATE_PENDING_PAYMENT) ); + $this->_orderMock->expects($this->once()) + ->method('setState') + ->with(Order::STATE_PROCESSING); $this->_paypalInfo->expects($this->once()) ->method('importToPayment') ->with( diff --git a/app/code/Magento/Paypal/Test/Unit/Model/Payflow/TransparentTest.php b/app/code/Magento/Paypal/Test/Unit/Model/Payflow/TransparentTest.php index 9587600203561..acadbc80cd559 100644 --- a/app/code/Magento/Paypal/Test/Unit/Model/Payflow/TransparentTest.php +++ b/app/code/Magento/Paypal/Test/Unit/Model/Payflow/TransparentTest.php @@ -196,7 +196,9 @@ private function getPaymentExtensionInterfaceFactory() ->disableOriginalConstructor() ->getMock(); $orderPaymentExtension = $this->getMockBuilder(OrderPaymentExtensionInterface::class) - ->setMethods(['setVaultPaymentToken', 'getVaultPaymentToken']) + ->setMethods( + ['setVaultPaymentToken', 'getVaultPaymentToken', 'setNotificationMessage', 'getNotificationMessage'] + ) ->disableOriginalConstructor() ->getMock(); @@ -290,12 +292,17 @@ private function initPayment() $this->order = $this->getMockBuilder(Order::class) ->disableOriginalConstructor() ->getMock(); - + $paymentExtensionAttributes = $this->getMockBuilder(OrderPaymentExtensionInterface::class) + ->setMethods( + ['setVaultPaymentToken', 'getVaultPaymentToken', 'setNotificationMessage', 'getNotificationMessage'] + ) + ->getMockForAbstractClass(); $this->payment->method('getOrder')->willReturn($this->order); $this->payment->method('setTransactionId')->willReturnSelf(); $this->payment->method('setIsTransactionClosed')->willReturnSelf(); $this->payment->method('getCcExpYear')->willReturn('2019'); $this->payment->method('getCcExpMonth')->willReturn('05'); + $this->payment->method('getExtensionAttributes')->willReturn($paymentExtensionAttributes); return $this->payment; } diff --git a/app/code/Magento/Paypal/etc/adminhtml/system.xml b/app/code/Magento/Paypal/etc/adminhtml/system.xml index 88bb61f2cdc99..80e9523c752e4 100644 --- a/app/code/Magento/Paypal/etc/adminhtml/system.xml +++ b/app/code/Magento/Paypal/etc/adminhtml/system.xml @@ -8,10 +8,10 @@ <config xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="urn:magento:module:Magento_Config:etc/system_file.xsd"> <system> <section id="payment"> - <group id="account" translate="label" sortOrder="1" showInDefault="1" showInWebsite="1" showInStore="0"> + <group id="account" translate="label" sortOrder="1" showInDefault="1" showInWebsite="1"> <label>Merchant Location</label> <frontend_model>Magento\Paypal\Block\Adminhtml\System\Config\Fieldset\Expanded</frontend_model> - <field id="merchant_country" type="select" translate="label comment" sortOrder="5" showInDefault="1" showInWebsite="1" showInStore="0"> + <field id="merchant_country" type="select" translate="label comment" sortOrder="5" showInDefault="1" showInWebsite="1"> <label>Merchant Country</label> <comment>If not specified, Default Country from General Config will be used.</comment> <frontend_model>Magento\Paypal\Block\Adminhtml\System\Config\Field\Country</frontend_model> diff --git a/app/code/Magento/Paypal/etc/adminhtml/system/paypal_payflowpro.xml b/app/code/Magento/Paypal/etc/adminhtml/system/paypal_payflowpro.xml index c87a781f36c00..77ec9eb0d0069 100644 --- a/app/code/Magento/Paypal/etc/adminhtml/system/paypal_payflowpro.xml +++ b/app/code/Magento/Paypal/etc/adminhtml/system/paypal_payflowpro.xml @@ -170,6 +170,12 @@ <source_model>Magento\Config\Model\Config\Source\Yesno</source_model> <attribute type="shared">1</attribute> </field> + <field id="fmf" translate="label comment" type="select" sortOrder="45" showInDefault="1" showInWebsite="1" showInStore="0"> + <label>Fraud Management Filters</label> + <source_model>Magento\Config\Model\Config\Source\Yesno</source_model> + <comment>Be sure to configure Fraud Management Filters in your PayPal account in "Service Settings/Fraud Protection" section. Attention! Please don't use Total Purchase Price Ceiling/Floor Filters. Current integration doesn't support them.</comment> + <config_path>payment/payflowpro/fmf</config_path> + </field> <group id="paypal_payflow_avs_check" translate="label" showInDefault="1" showInWebsite="1" sortOrder="80"> <label>CVV and AVS Settings</label> <field id="heading_avs_settings" translate="label" sortOrder="0" showInDefault="1" showInWebsite="1"> diff --git a/app/code/Magento/Paypal/etc/config.xml b/app/code/Magento/Paypal/etc/config.xml index 6c0601f80137d..24bb54a86103d 100644 --- a/app/code/Magento/Paypal/etc/config.xml +++ b/app/code/Magento/Paypal/etc/config.xml @@ -109,6 +109,7 @@ <cgi_url>https://payflowlink.paypal.com</cgi_url> <transaction_url_test_mode>https://pilot-payflowpro.paypal.com</transaction_url_test_mode> <transaction_url>https://payflowpro.paypal.com</transaction_url> + <fmf>0</fmf> <avs_street>0</avs_street> <avs_zip>0</avs_zip> <avs_international>0</avs_international> diff --git a/app/code/Magento/Paypal/etc/di.xml b/app/code/Magento/Paypal/etc/di.xml index c0141bbb3215e..e148320fdaf17 100644 --- a/app/code/Magento/Paypal/etc/di.xml +++ b/app/code/Magento/Paypal/etc/di.xml @@ -252,4 +252,10 @@ </argument> </arguments> </type> + <type name="Magento\Framework\Session\SessionStartChecker"> + <plugin name="transparent_session_checker" type="Magento\Paypal\Plugin\TransparentSessionChecker"/> + </type> + <type name="Magento\Sales\Model\Order\Payment"> + <plugin name="paypal_transparent" type="Magento\Paypal\Plugin\TransparentOrderPayment"/> + </type> </config> diff --git a/app/code/Magento/Paypal/etc/frontend/page_types.xml b/app/code/Magento/Paypal/etc/frontend/page_types.xml index 133ab1ca76162..1da5d54fb385d 100644 --- a/app/code/Magento/Paypal/etc/frontend/page_types.xml +++ b/app/code/Magento/Paypal/etc/frontend/page_types.xml @@ -14,6 +14,7 @@ <type id="paypal_payflow_form" label="Paypal Payflow Form"/> <type id="transparent" label="Paypal Payflow TR Form"/> <type id="transparent_payment_response" label="Paypal Payflow TR Response"/> + <type id="transparent_payment_redirect" label="Paypal Payflow TR Redirect"/> <type id="paypal_payflow_returnurl" label="Paypal Payflow Return URL"/> <type id="paypal_payflowadvanced_cancelpayment" label="Paypal Payflow Advanced Cancel Payment"/> <type id="paypal_payflowadvanced_form" label="Paypal Payflow Advanced Form"/> diff --git a/app/code/Magento/Paypal/i18n/en_US.csv b/app/code/Magento/Paypal/i18n/en_US.csv index 4e47c4c1f9e9f..54dd611d49073 100644 --- a/app/code/Magento/Paypal/i18n/en_US.csv +++ b/app/code/Magento/Paypal/i18n/en_US.csv @@ -738,3 +738,4 @@ User,User "PayPal Guest Checkout Credit Card Icons","PayPal Guest Checkout Credit Card Icons" "Elektronisches Lastschriftverfahren - German ELV","Elektronisches Lastschriftverfahren - German ELV" "Please enter at least 0 and at most 65535","Please enter at least 0 and at most 65535" +"Order is suspended as an account verification transaction is suspected to be fraudulent.","Order is suspended as an account verification transaction is suspected to be fraudulent." diff --git a/app/code/Magento/Paypal/registration.php b/app/code/Magento/Paypal/registration.php index f915d1d7b18d7..5b86f0d1ed627 100644 --- a/app/code/Magento/Paypal/registration.php +++ b/app/code/Magento/Paypal/registration.php @@ -4,6 +4,6 @@ * See COPYING.txt for license details. */ -use \Magento\Framework\Component\ComponentRegistrar; +use Magento\Framework\Component\ComponentRegistrar; ComponentRegistrar::register(ComponentRegistrar::MODULE, 'Magento_Paypal', __DIR__); diff --git a/app/code/Magento/Paypal/view/adminhtml/layout/transparent_payment_redirect.xml b/app/code/Magento/Paypal/view/adminhtml/layout/transparent_payment_redirect.xml new file mode 100644 index 0000000000000..01acf03c0d077 --- /dev/null +++ b/app/code/Magento/Paypal/view/adminhtml/layout/transparent_payment_redirect.xml @@ -0,0 +1,16 @@ +<?xml version="1.0"?> +<!-- +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +--> +<layout xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="urn:magento:framework:View/Layout/etc/layout_generic.xsd"> + <container name="root" label="Root"> + <block class="Magento\Payment\Block\Transparent\Redirect" name="transparent_redirect" template="Magento_Payment::transparent/redirect.phtml"> + <arguments> + <argument name="route_path" xsi:type="string">paypal/transparent/response</argument> + </arguments> + </block> + </container> +</layout> diff --git a/app/code/Magento/Paypal/view/frontend/layout/transparent_payment_redirect.xml b/app/code/Magento/Paypal/view/frontend/layout/transparent_payment_redirect.xml new file mode 100644 index 0000000000000..01acf03c0d077 --- /dev/null +++ b/app/code/Magento/Paypal/view/frontend/layout/transparent_payment_redirect.xml @@ -0,0 +1,16 @@ +<?xml version="1.0"?> +<!-- +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +--> +<layout xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="urn:magento:framework:View/Layout/etc/layout_generic.xsd"> + <container name="root" label="Root"> + <block class="Magento\Payment\Block\Transparent\Redirect" name="transparent_redirect" template="Magento_Payment::transparent/redirect.phtml"> + <arguments> + <argument name="route_path" xsi:type="string">paypal/transparent/response</argument> + </arguments> + </block> + </container> +</layout> diff --git a/app/code/Magento/Paypal/view/frontend/templates/express/in-context/component.phtml b/app/code/Magento/Paypal/view/frontend/templates/express/in-context/component.phtml index ba98f5caeb15a..c102b21830de8 100644 --- a/app/code/Magento/Paypal/view/frontend/templates/express/in-context/component.phtml +++ b/app/code/Magento/Paypal/view/frontend/templates/express/in-context/component.phtml @@ -3,12 +3,12 @@ * Copyright © Magento, Inc. All rights reserved. * See COPYING.txt for license details. */ -use Magento\Paypal\Block\Express\InContext\Minicart\Button; +use Magento\Paypal\Block\Express\InContext\Minicart\SmartButton; /** @var \Magento\Paypal\Block\Express\InContext\Component $block */ $configuration = [ - 'id' => Button::PAYPAL_BUTTON_ID, + 'id' => SmartButton::PAYPAL_BUTTON_ID, 'path' => $block->getUrl( 'paypal/express/gettoken', [ @@ -21,17 +21,18 @@ $configuration = [ 'locale' => $block->getLocale(), 'environment' => $block->getEnvironment(), 'button' => [ - Button::PAYPAL_BUTTON_ID, + SmartButton::PAYPAL_BUTTON_ID, ] ] ]; ?> -<div style="display: none;" id="<?= /* @noEscape */ Button::PAYPAL_BUTTON_ID ?>"></div> +<div style="display: none;" id="<?= /* @noEscape */ SmartButton::PAYPAL_BUTTON_ID ?>"></div> <script type="text/x-magento-init"> { "*": { - "Magento_Paypal/js/in-context/express-checkout": <?= /* @noEscape */ $this->helper(\Magento\Framework\Json\Helper\Data::class)->jsonEncode($configuration) ?> + "Magento_Paypal/js/in-context/express-checkout": + <?= /* @noEscape */ $this->helper(\Magento\Framework\Json\Helper\Data::class)->jsonEncode($configuration) ?> } } </script> diff --git a/app/code/Magento/PaypalCaptcha/etc/adminhtml/system.xml b/app/code/Magento/PaypalCaptcha/etc/adminhtml/system.xml index 12afd8ceda60e..aa5eb371ba1a8 100644 --- a/app/code/Magento/PaypalCaptcha/etc/adminhtml/system.xml +++ b/app/code/Magento/PaypalCaptcha/etc/adminhtml/system.xml @@ -8,8 +8,8 @@ <config xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="urn:magento:module:Magento_Config:etc/system_file.xsd"> <system> <section id="customer"> - <group id="captcha" translate="label" type="text" sortOrder="110" showInDefault="1" showInWebsite="1" showInStore="0"> - <field id="forms" translate="label comment" type="multiselect" sortOrder="3" showInDefault="1" showInWebsite="1" showInStore="0" canRestore="1"> + <group id="captcha" translate="label" type="text" sortOrder="110" showInDefault="1" showInWebsite="1"> + <field id="forms" translate="label comment" type="multiselect" sortOrder="3" showInDefault="1" showInWebsite="1" canRestore="1"> <comment>CAPTCHA for "Create user", "Forgot password", "Payflow Pro" forms is always enabled if chosen.</comment> </field> </group> diff --git a/app/code/Magento/PaypalCaptcha/registration.php b/app/code/Magento/PaypalCaptcha/registration.php index 4dac0582a6d1b..79f8a56411078 100644 --- a/app/code/Magento/PaypalCaptcha/registration.php +++ b/app/code/Magento/PaypalCaptcha/registration.php @@ -4,6 +4,6 @@ * See COPYING.txt for license details. */ -use \Magento\Framework\Component\ComponentRegistrar; +use Magento\Framework\Component\ComponentRegistrar; ComponentRegistrar::register(ComponentRegistrar::MODULE, 'Magento_PaypalCaptcha', __DIR__); diff --git a/app/code/Magento/PaypalGraphQl/registration.php b/app/code/Magento/PaypalGraphQl/registration.php index 2e1676bc087cc..d3eb5dd3095a6 100644 --- a/app/code/Magento/PaypalGraphQl/registration.php +++ b/app/code/Magento/PaypalGraphQl/registration.php @@ -4,6 +4,6 @@ * See COPYING.txt for license details. */ -use \Magento\Framework\Component\ComponentRegistrar; +use Magento\Framework\Component\ComponentRegistrar; ComponentRegistrar::register(ComponentRegistrar::MODULE, 'Magento_PaypalGraphQl', __DIR__); diff --git a/app/code/Magento/Persistent/Model/Checkout/GuestPaymentInformationManagementPlugin.php b/app/code/Magento/Persistent/Model/Checkout/GuestPaymentInformationManagementPlugin.php index 2641102ca4d72..b4df4125a2c60 100644 --- a/app/code/Magento/Persistent/Model/Checkout/GuestPaymentInformationManagementPlugin.php +++ b/app/code/Magento/Persistent/Model/Checkout/GuestPaymentInformationManagementPlugin.php @@ -11,6 +11,8 @@ /** * Plugin to convert shopping cart from persistent cart to guest cart before order save when customer not logged in + * + * @SuppressWarnings(PHPMD.CookieAndSessionMisuse) */ class GuestPaymentInformationManagementPlugin { @@ -93,7 +95,7 @@ public function __construct( * @return void * @SuppressWarnings(PHPMD.UnusedFormalParameter) */ - public function beforeSavePaymentInformationAndPlaceOrder( + public function beforeSavePaymentInformation( GuestPaymentInformationManagement $subject, $cartId, $email, diff --git a/app/code/Magento/Persistent/Model/Layout/DepersonalizePlugin.php b/app/code/Magento/Persistent/Model/Layout/DepersonalizePlugin.php index 3bb15a2625dbb..16c7be78456d1 100644 --- a/app/code/Magento/Persistent/Model/Layout/DepersonalizePlugin.php +++ b/app/code/Magento/Persistent/Model/Layout/DepersonalizePlugin.php @@ -3,54 +3,51 @@ * Copyright © Magento, Inc. All rights reserved. * See COPYING.txt for license details. */ +declare(strict_types=1); + namespace Magento\Persistent\Model\Layout; +use Magento\Framework\View\LayoutInterface; use Magento\PageCache\Model\DepersonalizeChecker; +use Magento\Persistent\Model\Session as PersistentSession; /** - * Class DepersonalizePlugin + * Depersonalize customer data. */ class DepersonalizePlugin { /** * @var DepersonalizeChecker */ - protected $depersonalizeChecker; + private $depersonalizeChecker; /** - * @var \Magento\Persistent\Model\Session + * @var PersistentSession */ - protected $persistentSession; + private $persistentSession; /** - * Constructor - * * @param DepersonalizeChecker $depersonalizeChecker - * @param \Magento\Persistent\Model\Session $persistentSession + * @param PersistentSession $persistentSession */ public function __construct( DepersonalizeChecker $depersonalizeChecker, - \Magento\Persistent\Model\Session $persistentSession + PersistentSession $persistentSession ) { - $this->persistentSession = $persistentSession; $this->depersonalizeChecker = $depersonalizeChecker; + $this->persistentSession = $persistentSession; } /** - * After generate Xml + * Change sensitive customer data if the depersonalization is needed. * - * @param \Magento\Framework\View\LayoutInterface $subject - * @param \Magento\Framework\View\LayoutInterface $result - * @return \Magento\Framework\View\LayoutInterface + * @param LayoutInterface $subject + * @return void */ - public function afterGenerateXml( - \Magento\Framework\View\LayoutInterface $subject, - \Magento\Framework\View\LayoutInterface $result - ) { + public function afterGenerateElements(LayoutInterface $subject) + { if ($this->depersonalizeChecker->checkIfDepersonalize($subject)) { $this->persistentSession->setCustomerId(null); } - - return $result; } } diff --git a/app/code/Magento/Persistent/Observer/PreventExpressCheckoutObserver.php b/app/code/Magento/Persistent/Observer/PreventExpressCheckoutObserver.php deleted file mode 100644 index d7a54f2c5fec3..0000000000000 --- a/app/code/Magento/Persistent/Observer/PreventExpressCheckoutObserver.php +++ /dev/null @@ -1,89 +0,0 @@ -<?php -/** - * - * Copyright © Magento, Inc. All rights reserved. - * See COPYING.txt for license details. - */ -namespace Magento\Persistent\Observer; - -use Magento\Framework\Event\ObserverInterface; - -class PreventExpressCheckoutObserver implements ObserverInterface -{ - /** - * @var \Magento\Framework\Message\ManagerInterface - */ - protected $messageManager; - - /** - * Url model - * - * @var \Magento\Framework\UrlInterface - */ - protected $_url; - - /** - * Persistent session - * - * @var \Magento\Persistent\Helper\Session - */ - protected $_persistentSession = null; - - /** - * @var \Magento\Customer\Model\Session - */ - protected $_customerSession; - - /** - * @var \Magento\Checkout\Helper\ExpressRedirect - */ - protected $_expressRedirectHelper; - - /** - * @param \Magento\Persistent\Helper\Session $persistentSession - * @param \Magento\Customer\Model\Session $customerSession - * @param \Magento\Framework\UrlInterface $url - * @param \Magento\Framework\Message\ManagerInterface $messageManager - * @param \Magento\Checkout\Helper\ExpressRedirect $expressRedirectHelper - */ - public function __construct( - \Magento\Persistent\Helper\Session $persistentSession, - \Magento\Customer\Model\Session $customerSession, - \Magento\Framework\UrlInterface $url, - \Magento\Framework\Message\ManagerInterface $messageManager, - \Magento\Checkout\Helper\ExpressRedirect $expressRedirectHelper - ) { - $this->_persistentSession = $persistentSession; - $this->_customerSession = $customerSession; - $this->_url = $url; - $this->messageManager = $messageManager; - $this->_expressRedirectHelper = $expressRedirectHelper; - } - - /** - * Prevent express checkout - * - * @param \Magento\Framework\Event\Observer $observer - * @return void - */ - public function execute(\Magento\Framework\Event\Observer $observer) - { - if (!($this->_persistentSession->isPersistent() && !$this->_customerSession->isLoggedIn())) { - return; - } - - /** @var $controllerAction \Magento\Checkout\Controller\Express\RedirectLoginInterface*/ - $controllerAction = $observer->getEvent()->getControllerAction(); - if (!$controllerAction || - !$controllerAction instanceof \Magento\Checkout\Controller\Express\RedirectLoginInterface || - $controllerAction->getRedirectActionName() != $controllerAction->getRequest()->getActionName() - ) { - return; - } - - $this->messageManager->addNotice(__('To check out, please sign in using your email address.')); - $customerBeforeAuthUrl = $this->_url->getUrl('persistent/index/expressCheckout'); - - $this->_expressRedirectHelper->redirectLogin($controllerAction, $customerBeforeAuthUrl); - } -} diff --git a/app/code/Magento/Persistent/Test/Mftf/Metadata/persistent_config-meta.xml b/app/code/Magento/Persistent/Test/Mftf/Metadata/PersistentConfigMeta.xml similarity index 100% rename from app/code/Magento/Persistent/Test/Mftf/Metadata/persistent_config-meta.xml rename to app/code/Magento/Persistent/Test/Mftf/Metadata/PersistentConfigMeta.xml diff --git a/app/code/Magento/Persistent/Test/Mftf/Test/CheckShoppingCartBehaviorAfterSessionExpiredTest.xml b/app/code/Magento/Persistent/Test/Mftf/Test/CheckShoppingCartBehaviorAfterSessionExpiredTest.xml index 6d35319172823..18e19c4276548 100644 --- a/app/code/Magento/Persistent/Test/Mftf/Test/CheckShoppingCartBehaviorAfterSessionExpiredTest.xml +++ b/app/code/Magento/Persistent/Test/Mftf/Test/CheckShoppingCartBehaviorAfterSessionExpiredTest.xml @@ -27,11 +27,17 @@ <requiredEntity createDataKey="createCategory"/> </createData> <!-- Create new customer --> - <actionGroup ref="SignUpNewUserFromStorefrontActionGroup" stepKey="SignUpNewUser"> - <argument name="Customer" value="Simple_US_Customer_NY"/> + <actionGroup ref="StorefrontOpenCustomerAccountCreatePageActionGroup" stepKey="openCreateAccountPage"/> + <actionGroup ref="StorefrontFillCustomerAccountCreationFormActionGroup" stepKey="fillCreateAccountForm"> + <argument name="customer" value="Simple_US_Customer_NY"/> + </actionGroup> + <actionGroup ref="StorefrontClickCreateAnAccountCustomerAccountCreationFormActionGroup" stepKey="submitCreateAccountForm"/> + <actionGroup ref="AssertMessageCustomerCreateAccountActionGroup" stepKey="seeSuccessMessage"> + <argument name="messageType" value="success"/> + <argument name="message" value="Thank you for registering with Main Website Store."/> </actionGroup> <!--Add shipping information--> - <actionGroup ref="EnterCustomerAddressInfo" stepKey="enterAddressInfo"> + <actionGroup ref="EnterCustomerAddressInfoActionGroup" stepKey="enterAddressInfo"> <argument name="Address" value="US_Address_NY"/> </actionGroup> </before> diff --git a/app/code/Magento/Persistent/Test/Mftf/Test/ShippingQuotePersistedForGuestTest.xml b/app/code/Magento/Persistent/Test/Mftf/Test/ShippingQuotePersistedForGuestTest.xml index e81694e044f52..7c4e6948386f3 100644 --- a/app/code/Magento/Persistent/Test/Mftf/Test/ShippingQuotePersistedForGuestTest.xml +++ b/app/code/Magento/Persistent/Test/Mftf/Test/ShippingQuotePersistedForGuestTest.xml @@ -37,7 +37,7 @@ <createData entity="PersistentConfigDefault" stepKey="setDefaultPersistentState"/> <deleteData createDataKey="createProduct" stepKey="deleteProduct"/> <deleteData createDataKey="createCustomer" stepKey="deleteCustomer"/> - <actionGroup ref="logout" stepKey="logout"/> + <actionGroup ref="AdminLogoutActionGroup" stepKey="logout"/> </after> <!--Step 1: Login as a Customer with remember me checked--> <actionGroup ref="CustomerLoginOnStorefrontWithRememberMeCheckedActionGroup" stepKey="loginToStorefrontAccountWithRememberMeChecked"> @@ -52,8 +52,7 @@ <amOnPage url="{{StorefrontCustomerSignOutPage.url}}" stepKey="signOut"/> <waitForLoadingMaskToDisappear stepKey="waitSignOutPage"/> <resetCookie userInput="persistent_shopping_cart" stepKey="resetPersistentCookie"/> - <amOnPage url="{{StorefrontHomePage.url}}" stepKey="amOnHomePageAfterResetPersistentCookie"/> - <waitForPageLoad stepKey="waitHomePageLoadAfterResetCookie"/> + <actionGroup ref="StorefrontOpenHomePageActionGroup" stepKey="amOnHomePageAfterResetPersistentCookie"/> <!--Step 4: Add the product to shopping cart and open cart--> <amOnPage url="{{StorefrontProductPage.url($$createProduct.custom_attributes[url_key]$$)}}" stepKey="navigateToProductPageAsGuestUser"/> <actionGroup ref="AddToCartFromStorefrontProductPageActionGroup" stepKey="addProductToCartAsGuestUser"> diff --git a/app/code/Magento/Persistent/Test/Mftf/Test/StorefrontVerifyShoppingCartPersistenceUnderLongTermCookieTest.xml b/app/code/Magento/Persistent/Test/Mftf/Test/StorefrontVerifyShoppingCartPersistenceUnderLongTermCookieTest.xml index 35f4fd88f2b09..dd24c6ae4279d 100644 --- a/app/code/Magento/Persistent/Test/Mftf/Test/StorefrontVerifyShoppingCartPersistenceUnderLongTermCookieTest.xml +++ b/app/code/Magento/Persistent/Test/Mftf/Test/StorefrontVerifyShoppingCartPersistenceUnderLongTermCookieTest.xml @@ -38,14 +38,14 @@ <deleteData createDataKey="createSimple1" stepKey="deleteSimple1"/> <deleteData createDataKey="createSimple2" stepKey="deleteSimple2"/> <deleteData createDataKey="createCategory" stepKey="deleteCategory"/> - <actionGroup ref="LoginAsAdmin" stepKey="LoginAsAdmin"/> + <actionGroup ref="AdminLoginActionGroup" stepKey="LoginAsAdmin"/> <actionGroup ref="AdminDeleteCustomerActionGroup" stepKey="deleteJohnSmithCustomer"> <argument name="customerEmail" value="John_Smith_Customer.email"/> </actionGroup> <actionGroup ref="AdminDeleteCustomerActionGroup" stepKey="deleteJohnDoeCustomer"> <argument name="customerEmail" value="Simple_Customer_Without_Address.email"/> </actionGroup> - <actionGroup ref="logout" stepKey="logout"/> + <actionGroup ref="AdminLogoutActionGroup" stepKey="logout"/> </after> <!-- 1. Go to storefront and click the Create an Account link--> diff --git a/app/code/Magento/Persistent/Test/Mftf/Test/StorefrontVerifyThatInformationAboutViewingComparisonWishlistIsPersistedUnderLongTermCookieTest.xml b/app/code/Magento/Persistent/Test/Mftf/Test/StorefrontVerifyThatInformationAboutViewingComparisonWishlistIsPersistedUnderLongTermCookieTest.xml index 1b3a996b34e24..159f0c295a5b9 100644 --- a/app/code/Magento/Persistent/Test/Mftf/Test/StorefrontVerifyThatInformationAboutViewingComparisonWishlistIsPersistedUnderLongTermCookieTest.xml +++ b/app/code/Magento/Persistent/Test/Mftf/Test/StorefrontVerifyThatInformationAboutViewingComparisonWishlistIsPersistedUnderLongTermCookieTest.xml @@ -36,7 +36,7 @@ </createData> <createData entity="Simple_US_Customer" stepKey="createCustomer"/> - <actionGroup ref="LoginAsAdmin" stepKey="login"/> + <actionGroup ref="AdminLoginActionGroup" stepKey="login"/> <actionGroup ref="AdminCreateRecentlyProductsWidgetActionGroup" stepKey="createRecentlyComparedProductsWidget"> <argument name="widget" value="RecentlyComparedProductsWidget"/> </actionGroup> @@ -60,14 +60,14 @@ <actionGroup ref="AdminDeleteWidgetActionGroup" stepKey="deleteRecentlyViewedProductsWidget"> <argument name="widget" value="RecentlyViewedProductsWidget"/> </actionGroup> - <actionGroup ref="logout" stepKey="logout"/> + <actionGroup ref="AdminLogoutActionGroup" stepKey="logout"/> </after> <!--Login to storefront from customer--> <actionGroup ref="LoginToStorefrontActionGroup" stepKey="loginCustomer"> <argument name="Customer" value="$$createCustomer$$"/> </actionGroup> - <see userInput="Welcome, $$createCustomer.firstname$$ $$createCustomer.lastname$$!" selector="{{StorefrontPanelHeaderSection.WelcomeMessage}}" stepKey="checkWelcomeMessage"/> + <see userInput="Welcome, $$createCustomer.firstname$$ $$createCustomer.lastname$$!" selector="{{StorefrontPanelHeaderSection.welcomeMessage}}" stepKey="checkWelcomeMessage"/> <!--Open the details page of Simple Product 1, Simple Product 2 and add to cart, get to the category--> <actionGroup ref="AddSimpleProductToCartActionGroup" stepKey="addSimpleProductProductToCart"> @@ -144,7 +144,7 @@ <!--Sign out and check that widgets persist the information about the items--> <actionGroup ref="StorefrontSignOutActionGroup" stepKey="logoutFromCustomerToCheckThatWidgetsPersist"/> <amOnPage url="{{StorefrontCategoryPage.url($$createCategory.custom_attributes[url_key]$$)}}" stepKey="openCategoryPageAfterLogoutFromCustomer"/> - <see userInput="Welcome, $$createCustomer.firstname$$ $$createCustomer.lastname$$!" selector="{{StorefrontPanelHeaderSection.WelcomeMessage}}" stepKey="checkWelcomeMessageAfterLogoutFromCustomer"/> + <see userInput="Welcome, $$createCustomer.firstname$$ $$createCustomer.lastname$$!" selector="{{StorefrontPanelHeaderSection.welcomeMessage}}" stepKey="checkWelcomeMessageAfterLogoutFromCustomer"/> <seeElement selector="{{StorefrontPanelHeaderSection.notYouLink}}" stepKey="checkLinkNotYouAfterLogoutFromCustomer"/> <actionGroup ref="StorefrontAssertProductInRecentlyViewedWidgetActionGroup" stepKey="checkSimpleProductInRecentlyViewedWidgetAfterLogout"> @@ -164,7 +164,7 @@ <click selector="{{StorefrontPanelHeaderSection.notYouLink}}" stepKey="clickLinkNotYou"/> <waitForPageLoad stepKey="waitForPageLoadAfterClickLinkNotYou"/> <amOnPage url="{{StorefrontCategoryPage.url($$createCategory.custom_attributes[url_key]$$)}}" stepKey="openCategoryPageAfterClickNotYou"/> - <see userInput="Default welcome msg!" selector="{{StorefrontPanelHeaderSection.WelcomeMessage}}" stepKey="checkWelcomeMessageAfterClickLinkNotYou"/> + <see userInput="Default welcome msg!" selector="{{StorefrontPanelHeaderSection.welcomeMessage}}" stepKey="checkWelcomeMessageAfterClickLinkNotYou"/> <dontSee selector="{{StorefrontWidgetsSection.widgetRecentlyViewedProductsGrid}}" userInput="$$createSimpleProduct.name$$" stepKey="dontSeeProductInRecentlyViewedWidget"/> <dontSee selector="{{StorefrontCustomerWishlistSidebarSection.ProductTitleByName($$createSimpleProduct.name$$)}}" stepKey="dontSeeProductInWishlistWidget"/> <dontSee selector="{{StorefrontWidgetsSection.widgetRecentlyComparedProductsGrid}}" userInput="$$createSimpleProduct.name$$" stepKey="dontSeeProductInRecentlyComparedWidget"/> @@ -175,7 +175,7 @@ <argument name="Customer" value="$$createCustomer$$"/> </actionGroup> <amOnPage url="{{StorefrontCategoryPage.url($$createCategory.custom_attributes[url_key]$$)}}" stepKey="openCategoryPageToCheckWidgets"/> - <see userInput="Welcome, $$createCustomer.firstname$$ $$createCustomer.lastname$$!" selector="{{StorefrontPanelHeaderSection.WelcomeMessage}}" stepKey="checkWelcomeMessageAfterLogin"/> + <see userInput="Welcome, $$createCustomer.firstname$$ $$createCustomer.lastname$$!" selector="{{StorefrontPanelHeaderSection.welcomeMessage}}" stepKey="checkWelcomeMessageAfterLogin"/> <actionGroup ref="StorefrontCustomerCheckProductInWishlistSidebarActionGroup" stepKey="checkSimpleProductNameInWishlistSidebarAfterLogin"> <argument name="productVar" value="$$createSimpleProduct$$"/> diff --git a/app/code/Magento/Persistent/Test/Unit/Model/Checkout/GuestPaymentInformationManagementPluginTest.php b/app/code/Magento/Persistent/Test/Unit/Model/Checkout/GuestPaymentInformationManagementPluginTest.php index c7f84b476fa7e..01f8c5403ea22 100644 --- a/app/code/Magento/Persistent/Test/Unit/Model/Checkout/GuestPaymentInformationManagementPluginTest.php +++ b/app/code/Magento/Persistent/Test/Unit/Model/Checkout/GuestPaymentInformationManagementPluginTest.php @@ -111,7 +111,7 @@ public function testBeforeSavePaymentInformationAndPlaceOrderCartConvertsToGuest $collectionMock->expects($this->once())->method('walk')->with($walkMethod, $walkArgs); $this->cartRepositoryMock->expects($this->once())->method('save')->with($quoteMock); - $this->plugin->beforeSavePaymentInformationAndPlaceOrder( + $this->plugin->beforeSavePaymentInformation( $this->subjectMock, $cartId, $email, @@ -134,7 +134,7 @@ public function testBeforeSavePaymentInformationAndPlaceOrderShoppingCartNotPers $this->persistentSessionMock->expects($this->once())->method('isPersistent')->willReturn(true); $this->customerSessionMock->expects($this->once())->method('isLoggedIn')->willReturn(false); - $this->plugin->beforeSavePaymentInformationAndPlaceOrder( + $this->plugin->beforeSavePaymentInformation( $this->subjectMock, $cartId, $email, @@ -155,7 +155,7 @@ public function testBeforeSavePaymentInformationAndPlaceOrderPersistentSessionNo $this->persistentSessionMock->expects($this->once())->method('isPersistent')->willReturn(false); - $this->plugin->beforeSavePaymentInformationAndPlaceOrder( + $this->plugin->beforeSavePaymentInformation( $this->subjectMock, $cartId, $email, @@ -177,7 +177,7 @@ public function testBeforeSavePaymentInformationAndPlaceOrderCustomerSessionInLo $this->persistentSessionMock->expects($this->once())->method('isPersistent')->willReturn(true); $this->customerSessionMock->expects($this->once())->method('isLoggedIn')->willReturn(true); - $this->plugin->beforeSavePaymentInformationAndPlaceOrder( + $this->plugin->beforeSavePaymentInformation( $this->subjectMock, $cartId, $email, @@ -201,7 +201,7 @@ public function testBeforeSavePaymentInformationAndPlaceOrderQuoteManagerNotInPe $this->customerSessionMock->expects($this->once())->method('isLoggedIn')->willReturn(false); $this->quoteManagerMock->expects($this->once())->method('isPersistent')->willReturn(false); - $this->plugin->beforeSavePaymentInformationAndPlaceOrder( + $this->plugin->beforeSavePaymentInformation( $this->subjectMock, $cartId, $email, diff --git a/app/code/Magento/Persistent/Test/Unit/Model/Layout/DepersonalizePluginTest.php b/app/code/Magento/Persistent/Test/Unit/Model/Layout/DepersonalizePluginTest.php index 9731811ea8a97..393e2d8ab7799 100644 --- a/app/code/Magento/Persistent/Test/Unit/Model/Layout/DepersonalizePluginTest.php +++ b/app/code/Magento/Persistent/Test/Unit/Model/Layout/DepersonalizePluginTest.php @@ -3,117 +3,84 @@ * Copyright © Magento, Inc. All rights reserved. * See COPYING.txt for license details. */ +declare(strict_types=1); + namespace Magento\Persistent\Test\Unit\Model\Layout; +use Magento\Framework\TestFramework\Unit\Helper\ObjectManager as ObjectManagerHelper; +use Magento\Framework\View\LayoutInterface; +use Magento\PageCache\Model\DepersonalizeChecker; +use Magento\Persistent\Model\Layout\DepersonalizePlugin; +use Magento\Persistent\Model\Session as PersistentSession; +use PHPUnit\Framework\MockObject\MockObject; +use PHPUnit\Framework\TestCase; + /** - * Class DepersonalizePluginTest + * Unit tests for \Magento\Persistent\Model\Layout\DepersonalizePlugin class. */ -class DepersonalizePluginTest extends \PHPUnit\Framework\TestCase +class DepersonalizePluginTest extends TestCase { /** - * @var \Magento\Persistent\Model\Session|\PHPUnit_Framework_MockObject_MockObject + * @var PersistentSession|MockObject */ - protected $persistentSessionMock; + private $persistentSessionMock; /** - * @var \Magento\Framework\TestFramework\Unit\Helper\ObjectManager + * @var DepersonalizePlugin */ - protected $objectManager; + private $plugin; /** - * @var \Magento\Persistent\Model\Layout\DepersonalizePlugin + * @var DepersonalizeChecker|MockObject */ - protected $plugin; + private $depersonalizeCheckerMock; /** - * @var \Magento\PageCache\Model\DepersonalizeChecker|\PHPUnit_Framework_MockObject_MockObject + * @var LayoutInterface|MockObject */ - protected $depersonalizeCheckerMock; + private $layoutMock; /** - * Set up - * - * @return void + * @inheritdoc */ protected function setUp() { - $this->objectManager = new \Magento\Framework\TestFramework\Unit\Helper\ObjectManager($this); - - $this->persistentSessionMock = $this->createPartialMock( - \Magento\Persistent\Model\Session::class, - ['setCustomerId'] - ); + $this->layoutMock = $this->getMockForAbstractClass(LayoutInterface::class); + $this->persistentSessionMock = $this->createPartialMock(PersistentSession::class, ['setCustomerId']); + $this->depersonalizeCheckerMock = $this->createMock(DepersonalizeChecker::class); - $this->requestMock = $this->createMock(\Magento\Framework\App\Request\Http::class); - - $this->moduleManagerMock = $this->createPartialMock(\Magento\Framework\Module\Manager::class, ['isEnabled']); - $this->cacheConfigMock = $this->createPartialMock(\Magento\PageCache\Model\Config::class, ['isEnabled']); - $this->depersonalizeCheckerMock = $this->createMock(\Magento\PageCache\Model\DepersonalizeChecker::class); - - $this->plugin = $this->objectManager->getObject( - \Magento\Persistent\Model\Layout\DepersonalizePlugin::class, + $this->plugin = (new ObjectManagerHelper($this))->getObject( + DepersonalizePlugin::class, [ - 'persistentSession' => $this->persistentSessionMock, 'depersonalizeChecker' => $this->depersonalizeCheckerMock, + 'persistentSession' => $this->persistentSessionMock, ] ); } - public function testAfterGenerateXml() + /** + * Test afterGenerateElements method when depersonalization is needed. + * + * @return void + */ + public function testAfterGenerateElements(): void { - /** @var \Magento\Framework\View\LayoutInterface|\PHPUnit_Framework_MockObject_MockObject $subjectMock */ - $subjectMock = $this->getMockForAbstractClass( - \Magento\Framework\View\LayoutInterface::class, - [], - '', - false, - true, - true, - ['isCacheable'] - ); - /** @var \Magento\Framework\View\LayoutInterface|\PHPUnit_Framework_MockObject_MockObject $resultMock */ - $resultMock = $this->getMockForAbstractClass( - \Magento\Framework\View\LayoutInterface::class, - [], - '', - false, - true, - true, - [] - ); - $this->depersonalizeCheckerMock->expects($this->once())->method('checkIfDepersonalize')->willReturn(true); $this->persistentSessionMock->expects($this->once())->method('setCustomerId')->with(null); - $this->assertEquals($resultMock, $this->plugin->afterGenerateXml($subjectMock, $resultMock)); + $this->assertEmpty($this->plugin->afterGenerateElements($this->layoutMock)); } - public function testAfterGenerateXmlNoDepersonalize() + /** + * Test afterGenerateElements method when depersonalization is not needed. + * + * @return void + */ + public function testAfterGenerateElementsNoDepersonalize(): void { - /** @var \Magento\Framework\View\LayoutInterface|\PHPUnit_Framework_MockObject_MockObject $subjectMock */ - $subjectMock = $this->getMockForAbstractClass( - \Magento\Framework\View\LayoutInterface::class, - [], - '', - false, - true, - true, - ['isCacheable'] - ); - /** @var \Magento\Framework\View\LayoutInterface|\PHPUnit_Framework_MockObject_MockObject $resultMock */ - $resultMock = $this->getMockForAbstractClass( - \Magento\Framework\View\LayoutInterface::class, - [], - '', - false, - true, - true, - [] - ); - $this->depersonalizeCheckerMock->expects($this->once())->method('checkIfDepersonalize')->willReturn(false); $this->persistentSessionMock->expects($this->never())->method('setCustomerId'); - $this->assertEquals($resultMock, $this->plugin->afterGenerateXml($subjectMock, $resultMock)); + $this->assertEmpty($this->plugin->afterGenerateElements($this->layoutMock)); } } diff --git a/app/code/Magento/Persistent/Test/Unit/Observer/PreventExpressCheckoutObserverTest.php b/app/code/Magento/Persistent/Test/Unit/Observer/PreventExpressCheckoutObserverTest.php deleted file mode 100644 index 7749377bbfcd0..0000000000000 --- a/app/code/Magento/Persistent/Test/Unit/Observer/PreventExpressCheckoutObserverTest.php +++ /dev/null @@ -1,168 +0,0 @@ -<?php -/** - * Copyright © Magento, Inc. All rights reserved. - * See COPYING.txt for license details. - */ -namespace Magento\Persistent\Test\Unit\Observer; - -/** - * @SuppressWarnings(PHPMD.CouplingBetweenObjects) - */ -class PreventExpressCheckoutObserverTest extends \PHPUnit\Framework\TestCase -{ - /** - * @var \Magento\Persistent\Observer\PreventExpressCheckoutObserver - */ - protected $_model; - - /** - * @var \Magento\Framework\Event - */ - protected $_event; - - /** - * @var \Magento\Framework\Event\Observer - */ - protected $_observer; - - /** - * Customer session - * - * @var \PHPUnit_Framework_MockObject_MockObject - */ - protected $_customerSession; - - /** - * Persistent session - * - * @var \PHPUnit_Framework_MockObject_MockObject - */ - protected $_persistentSession; - - /** - * - * @var \PHPUnit_Framework_MockObject_MockObject - */ - protected $_messageManager; - - /** - * Url model - * - * @var \PHPUnit_Framework_MockObject_MockObject - */ - protected $_url; - - /** - * @var \PHPUnit_Framework_MockObject_MockObject - */ - protected $_expressRedirectHelper; - - protected function setUp() - { - $helper = new \Magento\Framework\TestFramework\Unit\Helper\ObjectManager($this); - - $this->_event = new \Magento\Framework\Event(); - $this->_observer = new \Magento\Framework\Event\Observer(); - $this->_observer->setEvent($this->_event); - - $this->_customerSession = $this->getMockBuilder( - \Magento\Customer\Model\Session::class - )->disableOriginalConstructor()->setMethods( - ['isLoggedIn'] - )->getMock(); - - $this->_persistentSession = $this->getMockBuilder( - \Magento\Persistent\Helper\Session::class - )->disableOriginalConstructor()->setMethods( - ['isPersistent'] - )->getMock(); - - $this->_messageManager = $this->getMockBuilder( - \Magento\Framework\Message\ManagerInterface::class - )->disableOriginalConstructor()->setMethods( - [] - )->getMock(); - - $this->_url = $this->getMockBuilder( - \Magento\Framework\UrlInterface::class - )->disableOriginalConstructor()->setMethods( - [] - )->getMock(); - - $this->_expressRedirectHelper = $this->getMockBuilder( - \Magento\Checkout\Helper\ExpressRedirect::class - )->disableOriginalConstructor()->setMethods( - ['redirectLogin'] - )->getMock(); - - $this->_model = $helper->getObject( - \Magento\Persistent\Observer\PreventExpressCheckoutObserver::class, - [ - 'customerSession' => $this->_customerSession, - 'persistentSession' => $this->_persistentSession, - 'messageManager' => $this->_messageManager, - 'url' => $this->_url, - 'expressRedirectHelper' => $this->_expressRedirectHelper - ] - ); - } - - public function testPreventExpressCheckoutOnline() - { - $this->_customerSession->expects($this->once())->method('isLoggedIn')->will($this->returnValue(true)); - $this->_persistentSession->expects($this->once())->method('isPersistent')->will($this->returnValue(true)); - $this->_model->execute($this->_observer); - } - - public function testPreventExpressCheckoutEmpty() - { - $this->_customerSession->expects($this->any())->method('isLoggedIn')->will($this->returnValue(false)); - $this->_persistentSession->expects($this->any())->method('isPersistent')->will($this->returnValue(true)); - - $this->_event->setControllerAction(null); - $this->_model->execute($this->_observer); - - $this->_event->setControllerAction(new \StdClass()); - $this->_model->execute($this->_observer); - - $expectedActionName = 'realAction'; - $unexpectedActionName = 'notAction'; - $request = new \Magento\Framework\DataObject(); - $request->setActionName($unexpectedActionName); - $expressRedirectMock = $this->getMockBuilder( - \Magento\Checkout\Controller\Express\RedirectLoginInterface::class - )->disableOriginalConstructor()->setMethods( - [ - 'getActionFlagList', - 'getResponse', - 'getCustomerBeforeAuthUrl', - 'getLoginUrl', - 'getRedirectActionName', - 'getRequest', - ] - )->getMock(); - $expressRedirectMock->expects($this->any())->method('getRequest')->will($this->returnValue($request)); - $expressRedirectMock->expects( - $this->any() - )->method( - 'getRedirectActionName' - )->will( - $this->returnValue($expectedActionName) - ); - $this->_event->setControllerAction($expressRedirectMock); - $this->_model->execute($this->_observer); - - $expectedAuthUrl = 'expectedAuthUrl'; - $request->setActionName($expectedActionName); - $this->_url->expects($this->once())->method('getUrl')->will($this->returnValue($expectedAuthUrl)); - $this->_expressRedirectHelper->expects( - $this->once() - )->method( - 'redirectLogin' - )->with( - $expressRedirectMock, - $expectedAuthUrl - ); - $this->_model->execute($this->_observer); - } -} diff --git a/app/code/Magento/Persistent/etc/adminhtml/system.xml b/app/code/Magento/Persistent/etc/adminhtml/system.xml index 2db7cb0df0bd1..18882c0072877 100644 --- a/app/code/Magento/Persistent/etc/adminhtml/system.xml +++ b/app/code/Magento/Persistent/etc/adminhtml/system.xml @@ -7,46 +7,46 @@ --> <config xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="urn:magento:module:Magento_Config:etc/system_file.xsd"> <system> - <section id="persistent" translate="label" type="text" sortOrder="500" showInDefault="1" showInWebsite="1" showInStore="0"> + <section id="persistent" translate="label" type="text" sortOrder="500" showInDefault="1" showInWebsite="1"> <class>separator-top</class> <label>Persistent Shopping Cart</label> <tab>customer</tab> <resource>Magento_Persistent::persistent</resource> - <group id="options" translate="label" type="text" sortOrder="10" showInDefault="1" showInWebsite="1" showInStore="0"> + <group id="options" translate="label" type="text" sortOrder="10" showInDefault="1" showInWebsite="1"> <label>General Options</label> - <field id="enabled" translate="label" type="select" sortOrder="10" showInDefault="1" showInWebsite="1" showInStore="0" canRestore="1"> + <field id="enabled" translate="label" type="select" sortOrder="10" showInDefault="1" showInWebsite="1" canRestore="1"> <label>Enable Persistence</label> <source_model>Magento\Config\Model\Config\Source\Yesno</source_model> </field> - <field id="lifetime" translate="label" type="text" sortOrder="20" showInDefault="1" showInWebsite="1" showInStore="0" canRestore="1"> + <field id="lifetime" translate="label" type="text" sortOrder="20" showInDefault="1" showInWebsite="1" canRestore="1"> <label>Persistence Lifetime (seconds)</label> <validate>validate-digits validate-digits-range digits-range-0-3153600000</validate> <depends> <field id="enabled">1</field> </depends> </field> - <field id="remember_enabled" translate="label" type="select" sortOrder="30" showInDefault="1" showInWebsite="1" showInStore="0" canRestore="1"> + <field id="remember_enabled" translate="label" type="select" sortOrder="30" showInDefault="1" showInWebsite="1" canRestore="1"> <label>Enable "Remember Me"</label> <source_model>Magento\Config\Model\Config\Source\Yesno</source_model> <depends> <field id="enabled">1</field> </depends> </field> - <field id="remember_default" translate="label" type="select" sortOrder="40" showInDefault="1" showInWebsite="1" showInStore="0" canRestore="1"> + <field id="remember_default" translate="label" type="select" sortOrder="40" showInDefault="1" showInWebsite="1" canRestore="1"> <label>"Remember Me" Default Value</label> <source_model>Magento\Config\Model\Config\Source\Yesno</source_model> <depends> <field id="enabled">1</field> </depends> </field> - <field id="logout_clear" translate="label" type="select" sortOrder="50" showInDefault="1" showInWebsite="1" showInStore="0" canRestore="1"> + <field id="logout_clear" translate="label" type="select" sortOrder="50" showInDefault="1" showInWebsite="1" canRestore="1"> <label>Clear Persistence on Sign Out</label> <source_model>Magento\Config\Model\Config\Source\Yesno</source_model> <depends> <field id="enabled">1</field> </depends> </field> - <field id="shopping_cart" translate="label" type="select" sortOrder="60" showInDefault="1" showInWebsite="1" showInStore="0" canRestore="1"> + <field id="shopping_cart" translate="label" type="select" sortOrder="60" showInDefault="1" showInWebsite="1" canRestore="1"> <label>Persist Shopping Cart</label> <source_model>Magento\Config\Model\Config\Source\Yesno</source_model> <depends> diff --git a/app/code/Magento/Persistent/etc/di.xml b/app/code/Magento/Persistent/etc/di.xml index c28426b4f25bf..f49d4361acb52 100644 --- a/app/code/Magento/Persistent/etc/di.xml +++ b/app/code/Magento/Persistent/etc/di.xml @@ -12,7 +12,4 @@ <type name="Magento\Customer\CustomerData\Customer"> <plugin name="section_data" type="Magento\Persistent\Model\Plugin\CustomerData" /> </type> - <type name="Magento\Framework\App\Http\Context"> - <plugin name="persistent_page_cache_variation" type="Magento\Persistent\Model\Plugin\PersistentCustomerContext" /> - </type> </config> diff --git a/app/code/Magento/Persistent/etc/frontend/di.xml b/app/code/Magento/Persistent/etc/frontend/di.xml index 3c33f8a51c418..fae706fcc5808 100644 --- a/app/code/Magento/Persistent/etc/frontend/di.xml +++ b/app/code/Magento/Persistent/etc/frontend/di.xml @@ -49,4 +49,7 @@ </argument> </arguments> </type> + <type name="Magento\Framework\App\Http\Context"> + <plugin name="persistent_page_cache_variation" type="Magento\Persistent\Model\Plugin\PersistentCustomerContext" /> + </type> </config> diff --git a/app/code/Magento/Persistent/etc/frontend/events.xml b/app/code/Magento/Persistent/etc/frontend/events.xml index 79720695ea6f6..840297b0ff098 100644 --- a/app/code/Magento/Persistent/etc/frontend/events.xml +++ b/app/code/Magento/Persistent/etc/frontend/events.xml @@ -34,7 +34,6 @@ <observer name="persistent_session" instance="Magento\Persistent\Observer\RenewCookieObserver" /> <observer name="persistent_quote" instance="Magento\Persistent\Observer\CheckExpirePersistentQuoteObserver" /> <observer name="persistent_customer" instance="Magento\Persistent\Observer\EmulateCustomerObserver" /> - <observer name="persistent_checkout" instance="Magento\Persistent\Observer\PreventExpressCheckoutObserver" /> </event> <event name="customer_customer_authenticated"> <observer name="persistent" instance="Magento\Persistent\Observer\CustomerAuthenticatedEventObserver" /> diff --git a/app/code/Magento/Persistent/registration.php b/app/code/Magento/Persistent/registration.php index bc77434391517..4ff1c96a45ae8 100644 --- a/app/code/Magento/Persistent/registration.php +++ b/app/code/Magento/Persistent/registration.php @@ -4,6 +4,6 @@ * See COPYING.txt for license details. */ -use \Magento\Framework\Component\ComponentRegistrar; +use Magento\Framework\Component\ComponentRegistrar; ComponentRegistrar::register(ComponentRegistrar::MODULE, 'Magento_Persistent', __DIR__); diff --git a/app/code/Magento/ProductAlert/Block/Email/AbstractEmail.php b/app/code/Magento/ProductAlert/Block/Email/AbstractEmail.php index ca989c9f5235f..57081804fca80 100644 --- a/app/code/Magento/ProductAlert/Block/Email/AbstractEmail.php +++ b/app/code/Magento/ProductAlert/Block/Email/AbstractEmail.php @@ -6,8 +6,6 @@ namespace Magento\ProductAlert\Block\Email; use Magento\Framework\Pricing\PriceCurrencyInterface; -use Magento\Framework\App\ObjectManager; -use Magento\ProductAlert\Block\Product\ImageProvider; /** * Product Alert Abstract Email Block @@ -43,32 +41,23 @@ abstract class AbstractEmail extends \Magento\Framework\View\Element\Template */ protected $imageBuilder; - /** - * @var ImageProvider - */ - private $imageProvider; - /** * @param \Magento\Framework\View\Element\Template\Context $context * @param \Magento\Framework\Filter\Input\MaliciousCode $maliciousCode * @param PriceCurrencyInterface $priceCurrency * @param \Magento\Catalog\Block\Product\ImageBuilder $imageBuilder * @param array $data - * @param ImageProvider $imageProvider */ public function __construct( \Magento\Framework\View\Element\Template\Context $context, \Magento\Framework\Filter\Input\MaliciousCode $maliciousCode, PriceCurrencyInterface $priceCurrency, \Magento\Catalog\Block\Product\ImageBuilder $imageBuilder, - array $data = [], - ImageProvider $imageProvider = null + array $data = [] ) { $this->imageBuilder = $imageBuilder; $this->priceCurrency = $priceCurrency; $this->_maliciousCode = $maliciousCode; - $this->imageProvider = $imageProvider ?: ObjectManager::getInstance()->get(ImageProvider::class); - parent::__construct($context, $data); } @@ -173,6 +162,8 @@ protected function _getUrlParams() } /** + * Get Price Render + * * @return \Magento\Framework\Pricing\Render */ protected function getPriceRender() @@ -227,6 +218,6 @@ public function getProductPriceHtml( */ public function getImage($product, $imageId, $attributes = []) { - return $this->imageProvider->getImage($product, $imageId, $attributes); + return $this->imageBuilder->create($product, $imageId, $attributes); } } diff --git a/app/code/Magento/ProductAlert/Block/Email/Stock.php b/app/code/Magento/ProductAlert/Block/Email/Stock.php index d01960b8eb855..41f149eb5874e 100644 --- a/app/code/Magento/ProductAlert/Block/Email/Stock.php +++ b/app/code/Magento/ProductAlert/Block/Email/Stock.php @@ -27,7 +27,7 @@ public function getProductUnsubscribeUrl($productId) { $params = $this->_getUrlParams(); $params['product'] = $productId; - return $this->getUrl('productalert/unsubscribe/stock', $params); + return $this->getUrl('productalert/unsubscribe/email', $params); } /** diff --git a/app/code/Magento/ProductAlert/Block/Product/ImageProvider.php b/app/code/Magento/ProductAlert/Block/Product/ImageProvider.php deleted file mode 100644 index 61d8d1987c2d7..0000000000000 --- a/app/code/Magento/ProductAlert/Block/Product/ImageProvider.php +++ /dev/null @@ -1,72 +0,0 @@ -<?php -/** - * Copyright © Magento, Inc. All rights reserved. - * See COPYING.txt for license details. - */ -namespace Magento\ProductAlert\Block\Product; - -use Magento\Store\Model\App\Emulation; -use Magento\Catalog\Block\Product\ImageBuilder; -use Magento\Catalog\Model\Product; -use Magento\Store\Model\StoreManagerInterface; -use Magento\Framework\App\Area; -use Magento\Catalog\Block\Product\Image; - -/** - * Provides product image to be used in the Product Alert Email. - */ -class ImageProvider -{ - /** - * @var ImageBuilder - */ - private $imageBuilder; - - /** - * @var StoreManagerInterface - */ - private $storeManager; - - /** - * @var Emulation - */ - private $appEmulation; - - /** - * @param ImageBuilder $imageBuilder - * @param StoreManagerInterface $storeManager - * @param Emulation $appEmulation - */ - public function __construct( - ImageBuilder $imageBuilder, - StoreManagerInterface $storeManager, - Emulation $appEmulation - ) { - $this->imageBuilder = $imageBuilder; - $this->storeManager = $storeManager; - $this->appEmulation = $appEmulation; - } - - /** - * @param Product $product - * @param string $imageId - * @param array $attributes - * @return Image - * @throws \Exception - */ - public function getImage(Product $product, $imageId, $attributes = []) - { - $storeId = $this->storeManager->getStore()->getId(); - $this->appEmulation->startEnvironmentEmulation($storeId, Area::AREA_FRONTEND, true); - - try { - $image = $this->imageBuilder->create($product, $imageId, $attributes); - } catch (\Exception $exception) { - $this->appEmulation->stopEnvironmentEmulation(); - throw $exception; - } - - $this->appEmulation->stopEnvironmentEmulation(); - return $image; - } -} diff --git a/app/code/Magento/ProductAlert/Controller/Add.php b/app/code/Magento/ProductAlert/Controller/Add.php index 9498fa0e3d802..f8291ee5ffe15 100644 --- a/app/code/Magento/ProductAlert/Controller/Add.php +++ b/app/code/Magento/ProductAlert/Controller/Add.php @@ -10,6 +10,9 @@ use Magento\Customer\Model\Session as CustomerSession; use Magento\Framework\App\RequestInterface; +/** + * Abstract controller for notifying. + */ abstract class Add extends Action { /** @@ -39,8 +42,8 @@ public function dispatch(RequestInterface $request) { if (!$this->customerSession->authenticate()) { $this->_actionFlag->set('', 'no-dispatch', true); - if (!$this->customerSession->getBeforeUrl()) { - $this->customerSession->setBeforeUrl($this->_redirect->getRefererUrl()); + if (!$this->customerSession->getBeforeAuthUrl()) { + $this->customerSession->setBeforeAuthUrl($this->_redirect->getRefererUrl()); } } return parent::dispatch($request); diff --git a/app/code/Magento/ProductAlert/Controller/Unsubscribe/Email.php b/app/code/Magento/ProductAlert/Controller/Unsubscribe/Email.php new file mode 100644 index 0000000000000..09f3adc8e1296 --- /dev/null +++ b/app/code/Magento/ProductAlert/Controller/Unsubscribe/Email.php @@ -0,0 +1,59 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ + +declare(strict_types=1); + +namespace Magento\ProductAlert\Controller\Unsubscribe; + +use Magento\Customer\Model\Session as CustomerSession; +use Magento\Framework\App\Action\Context; +use Magento\Framework\App\Action\HttpGetActionInterface; +use Magento\Framework\View\Result\Page; +use Magento\Framework\View\Result\PageFactory; +use Magento\ProductAlert\Controller\Unsubscribe as UnsubscribeController; + +/** + * Unsubscribing from 'Back in stock Alert'. + * + * Is used to transform a Get request that triggered in the email into the Post request endpoint + */ +class Email extends UnsubscribeController implements HttpGetActionInterface +{ + /** + * @var PageFactory + */ + private $resultPageFactory; + + /** + * @param Context $context + * @param PageFactory $resultPageFactory + * @param CustomerSession $customerSession + */ + public function __construct( + Context $context, + PageFactory $resultPageFactory, + CustomerSession $customerSession + ) { + $this->resultPageFactory = $resultPageFactory; + parent::__construct($context, $customerSession); + } + + /** + * Processes the the request triggered in Unsubscription email related to 'back in stock alert'. + * + * @return Page + */ + public function execute(): Page + { + $productId = (int)$this->getRequest()->getParam('product'); + /** @var Page $resultPage */ + $resultPage = $this->resultPageFactory->create(); + /** @var @va \Magento\Framework\View\Element\AbstractBlock $block */ + $block = $resultPage->getLayout()->getBlock('unsubscription_form'); + $block->setProductId($productId); + return $resultPage; + } +} diff --git a/app/code/Magento/ProductAlert/Test/Unit/Block/Email/StockTest.php b/app/code/Magento/ProductAlert/Test/Unit/Block/Email/StockTest.php index c5872701ef9c8..48907197ab7b3 100644 --- a/app/code/Magento/ProductAlert/Test/Unit/Block/Email/StockTest.php +++ b/app/code/Magento/ProductAlert/Test/Unit/Block/Email/StockTest.php @@ -25,11 +25,6 @@ class StockTest extends \PHPUnit\Framework\TestCase */ protected $imageBuilder; - /** - * @var \Magento\ProductAlert\Block\Product\ImageProvider|\PHPUnit_Framework_MockObject_MockObject - */ - private $imageProviderMock; - protected function setUp() { $objectManager = new \Magento\Framework\TestFramework\Unit\Helper\ObjectManager($this); @@ -39,16 +34,11 @@ protected function setUp() ->disableOriginalConstructor() ->getMock(); - $this->imageProviderMock = $this->getMockBuilder(\Magento\ProductAlert\Block\Product\ImageProvider::class) - ->disableOriginalConstructor() - ->getMock(); - $this->_block = $objectManager->getObject( \Magento\ProductAlert\Block\Email\Stock::class, [ 'maliciousCode' => $this->_filter, - 'imageBuilder' => $this->imageBuilder, - 'imageProvider' => $this->imageProviderMock + 'imageBuilder' => $this->imageBuilder ] ); } @@ -88,8 +78,7 @@ public function testGetImage() ->disableOriginalConstructor() ->getMock(); - $this->imageProviderMock->expects($this->atLeastOnce())->method('getImage')->willReturn($productImageMock); - + $this->imageBuilder->expects($this->atLeastOnce())->method('create')->willReturn($productImageMock); $this->assertInstanceOf( \Magento\Catalog\Block\Product\Image::class, $this->_block->getImage($productMock, $imageId, $attributes) diff --git a/app/code/Magento/ProductAlert/Test/Unit/Block/Product/ImageProviderTest.php b/app/code/Magento/ProductAlert/Test/Unit/Block/Product/ImageProviderTest.php deleted file mode 100644 index 172e5f486f30b..0000000000000 --- a/app/code/Magento/ProductAlert/Test/Unit/Block/Product/ImageProviderTest.php +++ /dev/null @@ -1,99 +0,0 @@ -<?php -/** - * Copyright © Magento, Inc. All rights reserved. - * See COPYING.txt for license details. - */ -namespace Magento\ProductAlert\Test\Unit\Block\Product; - -class ImageProviderTest extends \PHPUnit\Framework\TestCase -{ - /** - * @var \Magento\Catalog\Block\Product\ImageBuilder|\PHPUnit_Framework_MockObject_MockObject - */ - private $imageBuilderMock; - - /** - * @var \Magento\Store\Model\StoreManagerInterface|\PHPUnit_Framework_MockObject_MockObject - */ - private $storeManagerMock; - - /** - * @var \Magento\Store\Model\App\Emulation|\PHPUnit_Framework_MockObject_MockObject - */ - private $emulationMock; - - /** - * @var \Magento\ProductAlert\Block\Product\ImageProvider - */ - private $model; - - protected function setUp() - { - $this->imageBuilderMock = $this->getMockBuilder(\Magento\Catalog\Block\Product\ImageBuilder::class) - ->disableOriginalConstructor() - ->getMock(); - $this->storeManagerMock = $this->getMockBuilder(\Magento\Store\Model\StoreManagerInterface::class) - ->disableOriginalConstructor() - ->getMock(); - $this->emulationMock = $this->getMockBuilder(\Magento\Store\Model\App\Emulation::class) - ->disableOriginalConstructor() - ->getMock(); - - $this->model = new \Magento\ProductAlert\Block\Product\ImageProvider( - $this->imageBuilderMock, - $this->storeManagerMock, - $this->emulationMock - ); - } - - /** - * Test that image is created successfully with app emulation enabled. - */ - public function testGetImage() - { - $imageId = 'test_image_id'; - $attributes = []; - - $productMock = $this->createMock(\Magento\Catalog\Model\Product::class); - $imageMock = $this->createMock(\Magento\Catalog\Block\Product\Image::class); - $storeMock = $this->createMock(\Magento\Store\Api\Data\StoreInterface::class); - - $this->storeManagerMock->expects($this->atLeastOnce())->method('getStore')->willReturn($storeMock); - $this->emulationMock->expects($this->once())->method('startEnvironmentEmulation'); - $this->imageBuilderMock->expects($this->once()) - ->method('create') - ->with($productMock, $imageId, $attributes) - ->willReturn($imageMock); - $this->emulationMock->expects($this->once())->method('stopEnvironmentEmulation'); - - $this->assertEquals($imageMock, $this->model->getImage($productMock, $imageId, $attributes)); - } - - /** - * Test that app emulation stops when exception occurs. - * - * @expectedException \Exception - * @expectedExceptionMessage Image Builder Exception - */ - public function testGetImageThrowsAnException() - { - $imageId = 1; - $productMock = $this->getMockBuilder(\Magento\Catalog\Model\Product::class) - ->disableOriginalConstructor() - ->getMock(); - $storeMock = $this->getMockBuilder(\Magento\Store\Api\Data\StoreInterface::class) - ->disableOriginalConstructor() - ->getMock(); - - $this->emulationMock->expects($this->once())->method('startEnvironmentEmulation'); - $this->storeManagerMock->expects($this->atLeastOnce())->method('getStore')->willReturn($storeMock); - - $this->imageBuilderMock->expects($this->once()) - ->method('create') - ->with($productMock, $imageId) - ->willThrowException(new \Exception("Image Builder Exception")); - - $this->emulationMock->expects($this->once())->method('stopEnvironmentEmulation'); - $this->model->getImage($productMock, $imageId); - } -} diff --git a/app/code/Magento/ProductAlert/Test/Unit/Model/ObserverTest.php b/app/code/Magento/ProductAlert/Test/Unit/Model/ObserverTest.php index e3a2056a89ec0..9a5381c094243 100644 --- a/app/code/Magento/ProductAlert/Test/Unit/Model/ObserverTest.php +++ b/app/code/Magento/ProductAlert/Test/Unit/Model/ObserverTest.php @@ -11,6 +11,9 @@ /** * Class ObserverTest + * + * Is used to test Product Alert Observer + * * @SuppressWarnings(PHPMD.CouplingBetweenObjects) * @SuppressWarnings(PHPMD.TooManyFields) */ @@ -168,7 +171,7 @@ protected function setUp() ); $this->storeMock = $this->getMockBuilder(\Magento\Store\Model\Store::class) ->disableOriginalConstructor() - ->setMethods(['getDefaultStore', 'getId']) + ->setMethods(['getDefaultStore', 'getId', 'setWebsiteId']) ->getMock(); $this->customerRepositoryMock = $this->getMockBuilder(\Magento\Customer\Api\CustomerRepositoryInterface::class) ->getMock(); @@ -285,12 +288,13 @@ public function testProcessPriceEmailThrowsException() $this->storeMock->expects($this->any())->method('getDefaultStore')->willReturnSelf(); $this->websiteMock->expects($this->once())->method('getDefaultStore')->willReturn($this->storeMock); $this->storeMock->expects($this->any())->method('getId')->willReturn(2); + $this->storeMock->expects($this->any())->method('setWebsiteId')->willReturnSelf(); $this->scopeConfigMock->expects($this->once())->method('getValue')->willReturn(true); $this->priceColFactoryMock->expects($this->once())->method('create')->willReturnSelf(); $this->priceColFactoryMock->expects($this->once())->method('addWebsiteFilter')->willReturnSelf(); - + $this->storeManagerMock->expects($this->any())->method('getStore')->willReturn($this->storeMock); $items = [ new \Magento\Framework\DataObject([ 'customer_id' => $id diff --git a/app/code/Magento/ProductAlert/composer.json b/app/code/Magento/ProductAlert/composer.json index 25fe8edbede7b..5e3e486d4e4a8 100644 --- a/app/code/Magento/ProductAlert/composer.json +++ b/app/code/Magento/ProductAlert/composer.json @@ -10,7 +10,8 @@ "magento/module-backend": "*", "magento/module-catalog": "*", "magento/module-customer": "*", - "magento/module-store": "*" + "magento/module-store": "*", + "magento/module-theme": "*" }, "suggest": { "magento/module-config": "*" diff --git a/app/code/Magento/ProductAlert/etc/adminhtml/system.xml b/app/code/Magento/ProductAlert/etc/adminhtml/system.xml index edf4940ff504a..2af3b905d2faf 100644 --- a/app/code/Magento/ProductAlert/etc/adminhtml/system.xml +++ b/app/code/Magento/ProductAlert/etc/adminhtml/system.xml @@ -14,7 +14,7 @@ <label>Allow Alert When Product Price Changes</label> <source_model>Magento\Config\Model\Config\Source\Yesno</source_model> </field> - <field id="allow_stock" translate="label" type="select" sortOrder="3" showInDefault="1" showInWebsite="1" showInStore="0" canRestore="1"> + <field id="allow_stock" translate="label" type="select" sortOrder="3" showInDefault="1" showInWebsite="1" canRestore="1"> <label>Allow Alert When Product Comes Back in Stock</label> <source_model>Magento\Config\Model\Config\Source\Yesno</source_model> </field> @@ -33,25 +33,25 @@ <source_model>Magento\Config\Model\Config\Source\Email\Identity</source_model> </field> </group> - <group id="productalert_cron" translate="label" type="text" sortOrder="260" showInDefault="1" showInWebsite="0" showInStore="0"> + <group id="productalert_cron" translate="label" type="text" sortOrder="260" showInDefault="1"> <label>Product Alerts Run Settings</label> - <field id="frequency" translate="label" type="select" sortOrder="1" showInDefault="1" showInWebsite="0" showInStore="0"> + <field id="frequency" translate="label" type="select" sortOrder="1" showInDefault="1"> <label>Frequency</label> <source_model>Magento\Cron\Model\Config\Source\Frequency</source_model> <backend_model>Magento\Cron\Model\Config\Backend\Product\Alert</backend_model> </field> - <field id="time" translate="label" type="time" sortOrder="2" showInDefault="1" showInWebsite="0" showInStore="0"> + <field id="time" translate="label" type="time" sortOrder="2" showInDefault="1"> <label>Start Time</label> </field> - <field id="error_email" translate="label" type="text" sortOrder="3" showInDefault="1" showInWebsite="0" showInStore="0"> + <field id="error_email" translate="label" type="text" sortOrder="3" showInDefault="1"> <label>Error Email Recipient</label> <validate>validate-email</validate> </field> - <field id="error_email_identity" translate="label" type="select" sortOrder="4" showInDefault="1" showInWebsite="0" showInStore="0" canRestore="1"> + <field id="error_email_identity" translate="label" type="select" sortOrder="4" showInDefault="1" canRestore="1"> <label>Error Email Sender</label> <source_model>Magento\Config\Model\Config\Source\Email\Identity</source_model> </field> - <field id="error_email_template" translate="label comment" type="select" sortOrder="5" showInDefault="1" showInWebsite="0" showInStore="0" canRestore="1"> + <field id="error_email_template" translate="label comment" type="select" sortOrder="5" showInDefault="1" canRestore="1"> <label>Error Email Template</label> <comment>Email template chosen based on theme fallback when "Default" option is selected.</comment> <source_model>Magento\Config\Model\Config\Source\Email\Template</source_model> diff --git a/app/code/Magento/ProductAlert/registration.php b/app/code/Magento/ProductAlert/registration.php index d1492a6d10db6..f049de4aef26a 100644 --- a/app/code/Magento/ProductAlert/registration.php +++ b/app/code/Magento/ProductAlert/registration.php @@ -4,6 +4,6 @@ * See COPYING.txt for license details. */ -use \Magento\Framework\Component\ComponentRegistrar; +use Magento\Framework\Component\ComponentRegistrar; ComponentRegistrar::register(ComponentRegistrar::MODULE, 'Magento_ProductAlert', __DIR__); diff --git a/app/code/Magento/ProductAlert/view/frontend/layout/productalert_unsubscribe_email.xml b/app/code/Magento/ProductAlert/view/frontend/layout/productalert_unsubscribe_email.xml new file mode 100644 index 0000000000000..4b759514e4b7f --- /dev/null +++ b/app/code/Magento/ProductAlert/view/frontend/layout/productalert_unsubscribe_email.xml @@ -0,0 +1,14 @@ +<?xml version="1.0"?> +<!-- +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +--> +<page xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" layout="1column" xsi:noNamespaceSchemaLocation="urn:magento:framework:View/Layout/etc/page_configuration.xsd"> + <body> + <referenceContainer name="content"> + <block class="Magento\Framework\View\Element\Template" name="unsubscription_form" cacheable="false" template="Magento_ProductAlert::email/email.phtml" /> + </referenceContainer> + </body> +</page> diff --git a/app/code/Magento/ProductAlert/view/frontend/templates/email/email.phtml b/app/code/Magento/ProductAlert/view/frontend/templates/email/email.phtml new file mode 100644 index 0000000000000..a99fe71d06dfd --- /dev/null +++ b/app/code/Magento/ProductAlert/view/frontend/templates/email/email.phtml @@ -0,0 +1,22 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ + +/** @var $block Magento\Framework\View\Element\Template */ +?> + +<form action="<?= $block->escapeUrl($block->getUrl('productalert/unsubscribe/stock')) ?>" + method="post" + data-form="unsubscription_form"> + <?= /* @noEscape */ $block->getBlockHtml('formkey') ?> + <input type="hidden" id="productId" name="product" value="<?= $block->escapeHtml($block->getProductId()) ?>" /> +</form> +<script type="text/x-magento-init"> + { + "[data-form=unsubscription_form]": { + "Magento_ProductAlert/js/form-submitter": {} + } + } +</script> diff --git a/app/code/Magento/ProductAlert/view/frontend/web/js/form-submitter.js b/app/code/Magento/ProductAlert/view/frontend/web/js/form-submitter.js new file mode 100644 index 0000000000000..7a0d5f663f3f9 --- /dev/null +++ b/app/code/Magento/ProductAlert/view/frontend/web/js/form-submitter.js @@ -0,0 +1,15 @@ +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ + +define([ + 'jquery' +], function ($) { + 'use strict'; + + return function (data, element) { + + $(element).submit(); + }; +}); diff --git a/app/code/Magento/ProductVideo/Test/Mftf/ActionGroup/AdminAddProductVideoWithPreviewActionGroup.xml b/app/code/Magento/ProductVideo/Test/Mftf/ActionGroup/AdminAddProductVideoWithPreviewActionGroup.xml new file mode 100644 index 0000000000000..e491a1676a402 --- /dev/null +++ b/app/code/Magento/ProductVideo/Test/Mftf/ActionGroup/AdminAddProductVideoWithPreviewActionGroup.xml @@ -0,0 +1,20 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + /** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +--> + +<actionGroups xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/actionGroupSchema.xsd"> + <actionGroup name="AdminAddProductVideoWithPreviewActionGroup" extends="AddProductVideoActionGroup"> + <annotations> + <description>Adds product video with a preview image on the Admin Product creation/edit page.</description> + </annotations> + <arguments> + <argument name="image" type="string" defaultValue="{{ImageUpload_1.file}}"/> + </arguments> + <attachFile selector="{{AdminProductNewVideoSection.previewImageUploader}}" userInput="{{image}}" after="waitForSaveButtonVisible" stepKey="addPreviewImage"/> + </actionGroup> +</actionGroups> diff --git a/app/code/Magento/ProductVideo/Test/Mftf/Data/ProductVideoData.xml b/app/code/Magento/ProductVideo/Test/Mftf/Data/ProductVideoData.xml index 5bc4ad86e0f06..2b111b882394c 100644 --- a/app/code/Magento/ProductVideo/Test/Mftf/Data/ProductVideoData.xml +++ b/app/code/Magento/ProductVideo/Test/Mftf/Data/ProductVideoData.xml @@ -13,4 +13,9 @@ <data key="videoTitle">Arctic Monkeys - Do I Wanna Know? (Official Video)</data> <data key="videoShortTitle">Arctic Monkeys</data> </entity> + <entity name="VimeoProductVideo" type="product_video"> + <data key="videoUrl">https://vimeo.com/76979871</data> + <data key="videoTitle">The New Vimeo Player (You Know, For Videos)</data> + <data key="videoShortTitle">The New Vimeo Player</data> + </entity> </entities> diff --git a/app/code/Magento/ProductVideo/Test/Mftf/Metadata/product_video_config-meta.xml b/app/code/Magento/ProductVideo/Test/Mftf/Metadata/ProductVideoConfigMeta.xml similarity index 100% rename from app/code/Magento/ProductVideo/Test/Mftf/Metadata/product_video_config-meta.xml rename to app/code/Magento/ProductVideo/Test/Mftf/Metadata/ProductVideoConfigMeta.xml diff --git a/app/code/Magento/ProductVideo/Test/Mftf/Section/AdminProductNewVideoSection.xml b/app/code/Magento/ProductVideo/Test/Mftf/Section/AdminProductNewVideoSection.xml index 58a1c40a4e470..d1890f9490d98 100644 --- a/app/code/Magento/ProductVideo/Test/Mftf/Section/AdminProductNewVideoSection.xml +++ b/app/code/Magento/ProductVideo/Test/Mftf/Section/AdminProductNewVideoSection.xml @@ -22,5 +22,6 @@ <element name="thumbnailCheckbox" type="checkbox" selector="#video_thumbnail"/> <element name="hideFromProductPageCheckbox" type="checkbox" selector="#new_video_disabled"/> <element name="errorElement" type="text" selector="#{{inputName}}-error" parameterized="true" /> + <element name="previewImageUploader" type="file" selector=".field-new_video_screenshot #new_video_screenshot"/> </section> -</sections> \ No newline at end of file +</sections> diff --git a/app/code/Magento/ProductVideo/Test/Mftf/Test/AdminAddDefaultVideoSimpleProductTest.xml b/app/code/Magento/ProductVideo/Test/Mftf/Test/AdminAddDefaultVideoSimpleProductTest.xml index c2b92e6af452a..766d8027bbb89 100644 --- a/app/code/Magento/ProductVideo/Test/Mftf/Test/AdminAddDefaultVideoSimpleProductTest.xml +++ b/app/code/Magento/ProductVideo/Test/Mftf/Test/AdminAddDefaultVideoSimpleProductTest.xml @@ -11,6 +11,9 @@ <test name="AdminAddDefaultVideoSimpleProductTest"> <annotations> <group value="ProductVideo"/> + <skip> + <issueId value="MC-32197"/> + </skip> </annotations> <before> <!-- Set product video Youtube api key configuration --> diff --git a/app/code/Magento/ProductVideo/Test/Mftf/Test/AdminRemoveDefaultVideoSimpleProductTest.xml b/app/code/Magento/ProductVideo/Test/Mftf/Test/AdminRemoveDefaultVideoSimpleProductTest.xml index c674f12115334..2f47163cff9f6 100644 --- a/app/code/Magento/ProductVideo/Test/Mftf/Test/AdminRemoveDefaultVideoSimpleProductTest.xml +++ b/app/code/Magento/ProductVideo/Test/Mftf/Test/AdminRemoveDefaultVideoSimpleProductTest.xml @@ -11,6 +11,9 @@ <test name="AdminRemoveDefaultVideoSimpleProductTest"> <annotations> <group value="ProductVideo"/> + <skip> + <issueId value="MC-32197"/> + </skip> </annotations> <before> <!-- Set product video Youtube api key configuration --> diff --git a/app/code/Magento/ProductVideo/Test/Mftf/Test/AdminValidateUrlOnGetVideoInformationTest.xml b/app/code/Magento/ProductVideo/Test/Mftf/Test/AdminValidateUrlOnGetVideoInformationTest.xml index 03fb71ac039a7..96aa49a53081b 100644 --- a/app/code/Magento/ProductVideo/Test/Mftf/Test/AdminValidateUrlOnGetVideoInformationTest.xml +++ b/app/code/Magento/ProductVideo/Test/Mftf/Test/AdminValidateUrlOnGetVideoInformationTest.xml @@ -15,14 +15,17 @@ <description value="Testing for a required video url when getting video information"/> <severity value="CRITICAL"/> <group value="ProductVideo"/> + <skip> + <issueId value="MC-32197"/> + </skip> </annotations> <before> - <actionGroup ref="LoginAsAdmin" stepKey="loginAsAdmin"/> + <actionGroup ref="AdminLoginActionGroup" stepKey="loginAsAdmin"/> <createData entity="ProductVideoYoutubeApiKeyConfig" stepKey="setStoreConfig"/> </before> <after> <createData entity="DefaultProductVideoConfig" stepKey="setStoreDefaultConfig"/> - <actionGroup ref="logout" stepKey="logout"/> + <actionGroup ref="AdminLogoutActionGroup" stepKey="logout"/> </after> <actionGroup ref="AdminOpenProductIndexPageActionGroup" stepKey="navigateToProductIndexPage"/> <actionGroup ref="GoToCreateProductPageActionGroup" stepKey="goToCreateProduct"> diff --git a/app/code/Magento/ProductVideo/Test/Mftf/Test/YoutubeVideoWindowOnProductPageTest.xml b/app/code/Magento/ProductVideo/Test/Mftf/Test/YoutubeVideoWindowOnProductPageTest.xml index 13d396b4faf45..92ea0aa86000f 100644 --- a/app/code/Magento/ProductVideo/Test/Mftf/Test/YoutubeVideoWindowOnProductPageTest.xml +++ b/app/code/Magento/ProductVideo/Test/Mftf/Test/YoutubeVideoWindowOnProductPageTest.xml @@ -15,13 +15,16 @@ <testCaseId value="MAGETWO-95254"/> <title value="Youtube video window on the product page"/> <description value="Check Youtube video window on the product page"/> - <severity value="MAJOR"/> + <severity value="BLOCKER"/> <group value="ProductVideo"/> + <skip> + <issueId value="MC-32197"/> + </skip> </annotations> <before> <!--Log In--> - <actionGroup ref="LoginAsAdmin" stepKey="loginAsAdmin"/> + <actionGroup ref="AdminLoginActionGroup" stepKey="loginAsAdmin"/> <!--Create category--> <createData entity="_defaultCategory" stepKey="createCategory"/> <!--Create product--> @@ -85,7 +88,7 @@ <!-- Set product video configuration to default --> <createData entity="DefaultProductVideoConfig" stepKey="setStoreDefaultConfig" before="logout"/> <!--Log Out--> - <actionGroup ref="logout" stepKey="logout"/> + <actionGroup ref="AdminLogoutActionGroup" stepKey="logout"/> </after> </test> </tests> diff --git a/app/code/Magento/ProductVideo/etc/adminhtml/system.xml b/app/code/Magento/ProductVideo/etc/adminhtml/system.xml index aa8749f69b409..aa70f3a5096c5 100644 --- a/app/code/Magento/ProductVideo/etc/adminhtml/system.xml +++ b/app/code/Magento/ProductVideo/etc/adminhtml/system.xml @@ -8,9 +8,9 @@ <config xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="urn:magento:module:Magento_Config:etc/system_file.xsd"> <system> <section id="catalog"> - <group id="product_video" translate="label" type="text" sortOrder="350" showInDefault="1" showInWebsite="1" showInStore="0"> + <group id="product_video" translate="label" type="text" sortOrder="350" showInDefault="1" showInWebsite="1"> <label>Product Video</label> - <field id="youtube_api_key" translate="label" type="text" sortOrder="10" showInDefault="1" showInWebsite="1" showInStore="0"> + <field id="youtube_api_key" translate="label" type="text" sortOrder="10" showInDefault="1" showInWebsite="1"> <label>YouTube API Key</label> </field> <field id="play_if_base" translate="label" type="select" sortOrder="20" showInDefault="1" showInWebsite="1" showInStore="1" canRestore="1"> diff --git a/app/code/Magento/ProductVideo/registration.php b/app/code/Magento/ProductVideo/registration.php index 39f0c452d3611..e49e231283f42 100644 --- a/app/code/Magento/ProductVideo/registration.php +++ b/app/code/Magento/ProductVideo/registration.php @@ -4,6 +4,6 @@ * See COPYING.txt for license details. */ -use \Magento\Framework\Component\ComponentRegistrar; +use Magento\Framework\Component\ComponentRegistrar; ComponentRegistrar::register(ComponentRegistrar::MODULE, 'Magento_ProductVideo', __DIR__); diff --git a/app/code/Magento/Quote/Model/Cart/CartTotalRepository.php b/app/code/Magento/Quote/Model/Cart/CartTotalRepository.php index 2c487cdea63fa..8f7e6504cb7d8 100644 --- a/app/code/Magento/Quote/Model/Cart/CartTotalRepository.php +++ b/app/code/Magento/Quote/Model/Cart/CartTotalRepository.php @@ -3,6 +3,9 @@ * Copyright © Magento, Inc. All rights reserved. * See COPYING.txt for license details. */ + +declare(strict_types=1); + namespace Magento\Quote\Model\Cart; use Magento\Quote\Api; @@ -12,9 +15,11 @@ use Magento\Framework\Api\ExtensibleDataInterface; use Magento\Quote\Model\Cart\Totals\ItemConverter; use Magento\Quote\Api\CouponManagementInterface; +use Magento\Quote\Api\Data\TotalsInterface as QuoteTotalsInterface; /** * Cart totals data object. + * * @SuppressWarnings(PHPMD.CouplingBetweenObjects) */ class CartTotalRepository implements CartTotalRepositoryInterface @@ -79,11 +84,8 @@ public function __construct( /** * @inheritdoc - * - * @param int $cartId The cart ID. - * @return Totals Quote totals data. */ - public function get($cartId) + public function get($cartId): QuoteTotalsInterface { /** @var \Magento\Quote\Model\Quote $quote */ $quote = $this->quoteRepository->getActive($cartId); @@ -96,17 +98,14 @@ public function get($cartId) } unset($addressTotalsData[ExtensibleDataInterface::EXTENSION_ATTRIBUTES_KEY]); - /** @var \Magento\Quote\Api\Data\TotalsInterface $quoteTotals */ + /** @var QuoteTotalsInterface $quoteTotals */ $quoteTotals = $this->totalsFactory->create(); $this->dataObjectHelper->populateWithArray( $quoteTotals, $addressTotalsData, - \Magento\Quote\Api\Data\TotalsInterface::class + QuoteTotalsInterface::class ); - $items = []; - foreach ($quote->getAllVisibleItems() as $index => $item) { - $items[$index] = $this->itemConverter->modelToDataObject($item); - } + $items = array_map([$this->itemConverter, 'modelToDataObject'], $quote->getAllVisibleItems()); $calculatedTotals = $this->totalsConverter->process($addressTotals); $quoteTotals->setTotalSegments($calculatedTotals); diff --git a/app/code/Magento/Quote/Model/Cart/CustomerCartResolver.php b/app/code/Magento/Quote/Model/Cart/CustomerCartResolver.php new file mode 100644 index 0000000000000..0461b9b84b6d1 --- /dev/null +++ b/app/code/Magento/Quote/Model/Cart/CustomerCartResolver.php @@ -0,0 +1,113 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +declare(strict_types=1); + +namespace Magento\Quote\Model\Cart; + +use Magento\Framework\Exception\AlreadyExistsException; +use Magento\Framework\Exception\NoSuchEntityException; +use Magento\Quote\Api\CartManagementInterface; +use Magento\Quote\Model\Quote; +use Magento\Quote\Model\QuoteIdMaskFactory; +use Magento\Quote\Model\QuoteIdToMaskedQuoteIdInterface; +use Magento\Quote\Model\ResourceModel\Quote\QuoteIdMask as QuoteIdMaskResourceModel; + +/** + * Get customer cart or create empty cart. Ensure mask_id is created + */ +class CustomerCartResolver +{ + /** + * @var CartManagementInterface + */ + private $cartManagement; + + /** + * @var QuoteIdMaskFactory + */ + private $quoteIdMaskFactory; + + /** + * @var QuoteIdMaskResourceModel + */ + private $quoteIdMaskResourceModel; + + /** + * @var QuoteIdToMaskedQuoteIdInterface + */ + private $quoteIdToMaskedQuoteId; + + /** + * @param CartManagementInterface $cartManagement + * @param QuoteIdMaskFactory $quoteIdMaskFactory + * @param QuoteIdMaskResourceModel $quoteIdMaskResourceModel + * @param QuoteIdToMaskedQuoteIdInterface $quoteIdToMaskedQuoteId + */ + public function __construct( + CartManagementInterface $cartManagement, + QuoteIdMaskFactory $quoteIdMaskFactory, + QuoteIdMaskResourceModel $quoteIdMaskResourceModel, + QuoteIdToMaskedQuoteIdInterface $quoteIdToMaskedQuoteId + ) { + $this->cartManagement = $cartManagement; + $this->quoteIdMaskFactory = $quoteIdMaskFactory; + $this->quoteIdMaskResourceModel = $quoteIdMaskResourceModel; + $this->quoteIdToMaskedQuoteId = $quoteIdToMaskedQuoteId; + } + + /** + * Get customer cart by customer id with predefined masked quote id + * + * @param int $customerId + * @param string|null $predefinedMaskedQuoteId + * @return Quote + * @throws NoSuchEntityException + * @throws \Magento\Framework\Exception\CouldNotSaveException + */ + public function resolve(int $customerId, string $predefinedMaskedQuoteId = null): Quote + { + try { + /** @var Quote $cart */ + $cart = $this->cartManagement->getCartForCustomer($customerId); + } catch (NoSuchEntityException $e) { + $this->cartManagement->createEmptyCartForCustomer($customerId); + $cart = $this->cartManagement->getCartForCustomer($customerId); + } + try { + $this->ensureQuoteMaskIdExist((int)$cart->getId(), $predefinedMaskedQuoteId); + // phpcs:ignore Magento2.CodeAnalysis.EmptyBlock + } catch (AlreadyExistsException $e) { + // do nothing, we already have masked id + } + + return $cart; + } + + /** + * Create masked id for customer's active quote if it's not exists + * + * @param int $quoteId + * @param string|null $predefinedMaskedQuoteId + * @return void + * @throws AlreadyExistsException + */ + private function ensureQuoteMaskIdExist(int $quoteId, string $predefinedMaskedQuoteId = null): void + { + try { + $maskedId = $this->quoteIdToMaskedQuoteId->execute($quoteId); + } catch (NoSuchEntityException $e) { + $maskedId = ''; + } + if ($maskedId === '') { + $quoteIdMask = $this->quoteIdMaskFactory->create(); + $quoteIdMask->setQuoteId($quoteId); + if (null !== $predefinedMaskedQuoteId) { + $quoteIdMask->setMaskedId($predefinedMaskedQuoteId); + } + $this->quoteIdMaskResourceModel->save($quoteIdMask); + } + } +} diff --git a/app/code/Magento/Quote/Model/Quote.php b/app/code/Magento/Quote/Model/Quote.php index 82b7913446fd0..c7d40c82bbcc2 100644 --- a/app/code/Magento/Quote/Model/Quote.php +++ b/app/code/Magento/Quote/Model/Quote.php @@ -865,6 +865,8 @@ public function beforeSave() } parent::beforeSave(); + + return $this; } /** @@ -945,8 +947,8 @@ public function assignCustomerWithAddressChange( } else { try { $defaultBillingAddress = $this->addressRepository->getById($customer->getDefaultBilling()); + // phpcs:ignore Magento2.CodeAnalysis.EmptyBlock } catch (\Magento\Framework\Exception\NoSuchEntityException $e) { - // } if (isset($defaultBillingAddress)) { /** @var \Magento\Quote\Model\Quote\Address $billingAddress */ @@ -959,8 +961,8 @@ public function assignCustomerWithAddressChange( if (null === $shippingAddress) { try { $defaultShippingAddress = $this->addressRepository->getById($customer->getDefaultShipping()); + // phpcs:ignore Magento2.CodeAnalysis.EmptyBlock } catch (\Magento\Framework\Exception\NoSuchEntityException $e) { - // } if (isset($defaultShippingAddress)) { /** @var \Magento\Quote\Model\Quote\Address $shippingAddress */ @@ -1679,12 +1681,14 @@ public function addProduct( // collect errors instead of throwing first one if ($item->getHasError()) { + $this->deleteItem($item); foreach ($item->getMessage(false) as $message) { if (!in_array($message, $errors)) { // filter duplicate messages $errors[] = $message; } } + break; } } if (!empty($errors)) { diff --git a/app/code/Magento/Quote/Model/Quote/Address.php b/app/code/Magento/Quote/Model/Quote/Address.php index db8ac0d1fe179..39148f990b714 100644 --- a/app/code/Magento/Quote/Model/Quote/Address.php +++ b/app/code/Magento/Quote/Model/Quote/Address.php @@ -6,10 +6,42 @@ namespace Magento\Quote\Model\Quote; use Magento\Customer\Api\AddressMetadataInterface; +use Magento\Customer\Api\Data\AddressInterface; use Magento\Customer\Api\Data\AddressInterfaceFactory; use Magento\Customer\Api\Data\RegionInterfaceFactory; +use Magento\Customer\Model\Address\AbstractAddress; +use Magento\Customer\Model\Address\Mapper; +use Magento\Directory\Helper\Data; +use Magento\Directory\Model\CountryFactory; +use Magento\Directory\Model\RegionFactory; +use Magento\Framework\Api\AttributeValueFactory; +use Magento\Framework\Api\DataObjectHelper; +use Magento\Framework\Api\ExtensionAttributesFactory; +use Magento\Framework\App\Config\ScopeConfigInterface; use Magento\Framework\App\ObjectManager; +use Magento\Framework\Data\Collection\AbstractDb; +use Magento\Framework\DataObject\Copy; +use Magento\Framework\Model\Context; +use Magento\Framework\Model\ResourceModel\AbstractResource; +use Magento\Framework\Model\ResourceModel\Db\Collection\AbstractCollection; +use Magento\Framework\Registry; use Magento\Framework\Serialize\Serializer\Json; +use Magento\Quote\Api\Data\AddressExtensionInterface; +use Magento\Quote\Model\Quote; +use Magento\Quote\Model\Quote\Address\Rate; +use Magento\Quote\Model\Quote\Address\RateCollectorInterfaceFactory; +use Magento\Quote\Model\Quote\Address\RateFactory; +use Magento\Quote\Model\Quote\Address\RateRequest; +use Magento\Quote\Model\Quote\Address\RateRequestFactory; +use Magento\Quote\Model\Quote\Address\Total; +use Magento\Quote\Model\Quote\Address\Total\Collector; +use Magento\Quote\Model\Quote\Address\Total\CollectorFactory; +use Magento\Quote\Model\Quote\Address\TotalFactory; +use Magento\Quote\Model\Quote\Item\AbstractItem; +use Magento\Quote\Model\ResourceModel\Quote\Address\Rate\CollectionFactory; +use Magento\Shipping\Model\CarrierFactoryInterface; +use Magento\Store\Api\Data\StoreInterface; +use Magento\Store\Model\ScopeInterface; use Magento\Store\Model\StoreManagerInterface; /** @@ -22,8 +54,8 @@ * @method Address setCreatedAt(string $value) * @method string getUpdatedAt() * @method Address setUpdatedAt(string $value) - * @method \Magento\Customer\Api\Data\AddressInterface getCustomerAddress() - * @method Address setCustomerAddressData(\Magento\Customer\Api\Data\AddressInterface $value) + * @method AddressInterface getCustomerAddress() + * @method Address setCustomerAddressData(AddressInterface $value) * @method string getAddressType() * @method Address setAddressType(string $value) * @method int getFreeShipping() @@ -90,14 +122,14 @@ * @method int[] getAppliedRuleIds() * @method Address setBaseShippingInclTax(float $value) * - * @property $_objectCopyService \Magento\Framework\DataObject\Copy + * @property $objectCopyService \Magento\Framework\DataObject\Copy * @SuppressWarnings(PHPMD.ExcessivePublicCount) * @SuppressWarnings(PHPMD.TooManyFields) * @SuppressWarnings(PHPMD.ExcessiveClassComplexity) * @SuppressWarnings(PHPMD.CouplingBetweenObjects) * @since 100.0.2 */ -class Address extends \Magento\Customer\Model\Address\AbstractAddress implements +class Address extends AbstractAddress implements \Magento\Quote\Api\Data\AddressInterface { const RATES_FETCH = 1; @@ -125,28 +157,28 @@ class Address extends \Magento\Customer\Model\Address\AbstractAddress implements /** * Quote object * - * @var \Magento\Quote\Model\Quote + * @var Quote */ protected $_items; /** * Quote object * - * @var \Magento\Quote\Model\Quote + * @var Quote */ protected $_quote; /** * Sales Quote address rates * - * @var \Magento\Quote\Model\Quote\Address\Rate + * @var Rate */ protected $_rates; /** * Total models collector * - * @var \Magento\Quote\Model\Quote\Address\Total\Collector + * @var Collector */ protected $_totalCollector; @@ -170,7 +202,7 @@ class Address extends \Magento\Customer\Model\Address\AbstractAddress implements /** * Core store config * - * @var \Magento\Framework\App\Config\ScopeConfigInterface + * @var ScopeConfigInterface */ protected $_scopeConfig; @@ -185,33 +217,33 @@ class Address extends \Magento\Customer\Model\Address\AbstractAddress implements protected $_itemCollectionFactory; /** - * @var \Magento\Quote\Model\Quote\Address\RateCollectorInterfaceFactory + * @var RateCollectorInterfaceFactory */ protected $_rateCollector; /** - * @var \Magento\Quote\Model\ResourceModel\Quote\Address\Rate\CollectionFactory + * @var CollectionFactory */ protected $_rateCollectionFactory; /** - * @var \Magento\Quote\Model\Quote\Address\Total\CollectorFactory + * @var CollectorFactory */ protected $_totalCollectorFactory; /** - * @var \Magento\Quote\Model\Quote\Address\TotalFactory + * @var TotalFactory */ protected $_addressTotalFactory; /** - * @var \Magento\Quote\Model\Quote\Address\RateFactory + * @var RateFactory * @since 100.2.0 */ protected $_addressRateFactory; /** - * @var \Magento\Customer\Api\Data\AddressInterfaceFactory + * @var AddressInterfaceFactory */ protected $addressDataFactory; @@ -221,7 +253,7 @@ class Address extends \Magento\Customer\Model\Address\AbstractAddress implements protected $validator; /** - * @var \Magento\Customer\Model\Address\Mapper + * @var Mapper */ protected $addressMapper; @@ -241,7 +273,7 @@ class Address extends \Magento\Customer\Model\Address\AbstractAddress implements protected $totalsCollector; /** - * @var \Magento\Quote\Model\Quote\TotalsReader + * @var TotalsReader */ protected $totalsReader; @@ -256,37 +288,47 @@ class Address extends \Magento\Customer\Model\Address\AbstractAddress implements private $storeManager; /** - * @param \Magento\Framework\Model\Context $context - * @param \Magento\Framework\Registry $registry - * @param \Magento\Framework\Api\ExtensionAttributesFactory $extensionFactory - * @param \Magento\Framework\Api\AttributeValueFactory $customAttributeFactory - * @param \Magento\Directory\Helper\Data $directoryData + * @var Copy + */ + private $objectCopyService; + + /** + * @var /Magento\Shipping\Model\CarrierFactoryInterface + */ + private $carrierFactory; + + /** + * @param Context $context + * @param Registry $registry + * @param ExtensionAttributesFactory $extensionFactory + * @param AttributeValueFactory $customAttributeFactory + * @param Data $directoryData * @param \Magento\Eav\Model\Config $eavConfig * @param \Magento\Customer\Model\Address\Config $addressConfig - * @param \Magento\Directory\Model\RegionFactory $regionFactory - * @param \Magento\Directory\Model\CountryFactory $countryFactory + * @param RegionFactory $regionFactory + * @param CountryFactory $countryFactory * @param AddressMetadataInterface $metadataService * @param AddressInterfaceFactory $addressDataFactory * @param RegionInterfaceFactory $regionDataFactory - * @param \Magento\Framework\Api\DataObjectHelper $dataObjectHelper - * @param \Magento\Framework\App\Config\ScopeConfigInterface $scopeConfig + * @param DataObjectHelper $dataObjectHelper + * @param ScopeConfigInterface $scopeConfig * @param Address\ItemFactory $addressItemFactory * @param \Magento\Quote\Model\ResourceModel\Quote\Address\Item\CollectionFactory $itemCollectionFactory - * @param \Magento\Quote\Model\Quote\Address\RateFactory $addressRateFactory + * @param RateFactory $addressRateFactory * @param Address\RateCollectorInterfaceFactory $rateCollector - * @param \Magento\Quote\Model\ResourceModel\Quote\Address\Rate\CollectionFactory $rateCollectionFactory + * @param CollectionFactory $rateCollectionFactory * @param Address\RateRequestFactory $rateRequestFactory * @param Address\Total\CollectorFactory $totalCollectorFactory * @param Address\TotalFactory $addressTotalFactory - * @param \Magento\Framework\DataObject\Copy $objectCopyService - * @param \Magento\Shipping\Model\CarrierFactoryInterface $carrierFactory + * @param Copy $objectCopyService + * @param CarrierFactoryInterface $carrierFactory * @param Address\Validator $validator - * @param \Magento\Customer\Model\Address\Mapper $addressMapper + * @param Mapper $addressMapper * @param Address\CustomAttributeListInterface $attributeList * @param TotalsCollector $totalsCollector - * @param \Magento\Quote\Model\Quote\TotalsReader $totalsReader - * @param \Magento\Framework\Model\ResourceModel\AbstractResource|null $resource - * @param \Magento\Framework\Data\Collection\AbstractDb|null $resourceCollection + * @param TotalsReader $totalsReader + * @param AbstractResource|null $resource + * @param AbstractDb|null $resourceCollection * @param array $data * @param Json $serializer * @param StoreManagerInterface $storeManager @@ -294,37 +336,37 @@ class Address extends \Magento\Customer\Model\Address\AbstractAddress implements * @SuppressWarnings(PHPMD.ExcessiveParameterList) */ public function __construct( - \Magento\Framework\Model\Context $context, - \Magento\Framework\Registry $registry, - \Magento\Framework\Api\ExtensionAttributesFactory $extensionFactory, - \Magento\Framework\Api\AttributeValueFactory $customAttributeFactory, - \Magento\Directory\Helper\Data $directoryData, + Context $context, + Registry $registry, + ExtensionAttributesFactory $extensionFactory, + AttributeValueFactory $customAttributeFactory, + Data $directoryData, \Magento\Eav\Model\Config $eavConfig, \Magento\Customer\Model\Address\Config $addressConfig, - \Magento\Directory\Model\RegionFactory $regionFactory, - \Magento\Directory\Model\CountryFactory $countryFactory, + RegionFactory $regionFactory, + CountryFactory $countryFactory, AddressMetadataInterface $metadataService, AddressInterfaceFactory $addressDataFactory, RegionInterfaceFactory $regionDataFactory, - \Magento\Framework\Api\DataObjectHelper $dataObjectHelper, - \Magento\Framework\App\Config\ScopeConfigInterface $scopeConfig, + DataObjectHelper $dataObjectHelper, + ScopeConfigInterface $scopeConfig, \Magento\Quote\Model\Quote\Address\ItemFactory $addressItemFactory, \Magento\Quote\Model\ResourceModel\Quote\Address\Item\CollectionFactory $itemCollectionFactory, - \Magento\Quote\Model\Quote\Address\RateFactory $addressRateFactory, - \Magento\Quote\Model\Quote\Address\RateCollectorInterfaceFactory $rateCollector, - \Magento\Quote\Model\ResourceModel\Quote\Address\Rate\CollectionFactory $rateCollectionFactory, - \Magento\Quote\Model\Quote\Address\RateRequestFactory $rateRequestFactory, - \Magento\Quote\Model\Quote\Address\Total\CollectorFactory $totalCollectorFactory, - \Magento\Quote\Model\Quote\Address\TotalFactory $addressTotalFactory, - \Magento\Framework\DataObject\Copy $objectCopyService, - \Magento\Shipping\Model\CarrierFactoryInterface $carrierFactory, + RateFactory $addressRateFactory, + RateCollectorInterfaceFactory $rateCollector, + CollectionFactory $rateCollectionFactory, + RateRequestFactory $rateRequestFactory, + CollectorFactory $totalCollectorFactory, + TotalFactory $addressTotalFactory, + Copy $objectCopyService, + CarrierFactoryInterface $carrierFactory, Address\Validator $validator, - \Magento\Customer\Model\Address\Mapper $addressMapper, + Mapper $addressMapper, Address\CustomAttributeListInterface $attributeList, - \Magento\Quote\Model\Quote\TotalsCollector $totalsCollector, - \Magento\Quote\Model\Quote\TotalsReader $totalsReader, - \Magento\Framework\Model\ResourceModel\AbstractResource $resource = null, - \Magento\Framework\Data\Collection\AbstractDb $resourceCollection = null, + TotalsCollector $totalsCollector, + TotalsReader $totalsReader, + AbstractResource $resource = null, + AbstractDb $resourceCollection = null, array $data = [], Json $serializer = null, StoreManagerInterface $storeManager = null @@ -338,8 +380,8 @@ public function __construct( $this->_rateRequestFactory = $rateRequestFactory; $this->_totalCollectorFactory = $totalCollectorFactory; $this->_addressTotalFactory = $addressTotalFactory; - $this->_objectCopyService = $objectCopyService; - $this->_carrierFactory = $carrierFactory; + $this->objectCopyService = $objectCopyService; + $this->carrierFactory = $carrierFactory; $this->addressDataFactory = $addressDataFactory; $this->validator = $validator; $this->addressMapper = $addressMapper; @@ -412,7 +454,7 @@ protected function _populateBeforeSaveData() $this->setCustomerAddressId($this->getCustomerAddressData()->getId()); } - if (!$this->getId()) { + if (!$this->getId() || $this->getQuote()->dataHasChangedFor('customer_id')) { $this->setSameAsBilling((int)$this->_isSameAsBilling()); } } @@ -427,7 +469,7 @@ protected function _isSameAsBilling() { $quoteSameAsBilling = $this->getSameAsBilling(); - return $this->getAddressType() == \Magento\Quote\Model\Quote\Address::TYPE_SHIPPING && + return $this->getAddressType() == Address::TYPE_SHIPPING && ($this->_isNotRegisteredCustomer() || $this->_isDefaultShippingNullOrSameAsBillingAddress()) && ($quoteSameAsBilling || $quoteSameAsBilling === 0 || $quoteSameAsBilling === null); } @@ -473,10 +515,10 @@ protected function _isDefaultShippingNullOrSameAsBillingAddress() /** * Declare address quote model object * - * @param \Magento\Quote\Model\Quote $quote + * @param Quote $quote * @return $this */ - public function setQuote(\Magento\Quote\Model\Quote $quote) + public function setQuote(Quote $quote) { $this->_quote = $quote; $this->setQuoteId($quote->getId()); @@ -486,7 +528,7 @@ public function setQuote(\Magento\Quote\Model\Quote $quote) /** * Retrieve quote object * - * @return \Magento\Quote\Model\Quote + * @return Quote */ public function getQuote() { @@ -496,12 +538,12 @@ public function getQuote() /** * Import quote address data from customer address Data Object. * - * @param \Magento\Customer\Api\Data\AddressInterface $address + * @param AddressInterface $address * @return $this */ - public function importCustomerAddressData(\Magento\Customer\Api\Data\AddressInterface $address) + public function importCustomerAddressData(AddressInterface $address) { - $this->_objectCopyService->copyFieldsetToTarget( + $this->objectCopyService->copyFieldsetToTarget( 'customer_address', 'to_quote_address', $this->addressMapper->toFlatArray($address), @@ -519,11 +561,11 @@ public function importCustomerAddressData(\Magento\Customer\Api\Data\AddressInte /** * Export data to customer address Data Object. * - * @return \Magento\Customer\Api\Data\AddressInterface + * @return AddressInterface */ public function exportCustomerAddress() { - $customerAddressData = $this->_objectCopyService->getDataFromFieldset( + $customerAddressData = $this->objectCopyService->getDataFromFieldset( 'sales_convert_quote_address', 'to_customer_address', $this @@ -542,7 +584,7 @@ public function exportCustomerAddress() $this->dataObjectHelper->populateWithArray( $addressDataObject, $customerAddressData, - \Magento\Customer\Api\Data\AddressInterface::class + AddressInterface::class ); return $addressDataObject; } @@ -568,7 +610,7 @@ public function toArray(array $arrAttributes = []) /** * Retrieve address items collection * - * @return \Magento\Framework\Model\ResourceModel\Db\Collection\AbstractCollection + * @return AbstractCollection */ public function getItemsCollection() { @@ -765,13 +807,13 @@ public function removeItem($itemId) /** * Add item to address * - * @param \Magento\Quote\Model\Quote\Item\AbstractItem $item + * @param AbstractItem $item * @param int $qty * @return $this */ - public function addItem(\Magento\Quote\Model\Quote\Item\AbstractItem $item, $qty = null) + public function addItem(AbstractItem $item, $qty = null) { - if ($item instanceof \Magento\Quote\Model\Quote\Item) { + if ($item instanceof Item) { if ($item->getParentItemId()) { return $this; } @@ -808,7 +850,7 @@ public function addItem(\Magento\Quote\Model\Quote\Item\AbstractItem $item, $qty /** * Retrieve collection of quote shipping rates * - * @return \Magento\Framework\Model\ResourceModel\Db\Collection\AbstractCollection + * @return AbstractCollection */ public function getShippingRatesCollection() { @@ -849,13 +891,13 @@ public function getGroupedAllShippingRates() { $rates = []; foreach ($this->getShippingRatesCollection() as $rate) { - if (!$rate->isDeleted() && $this->_carrierFactory->get($rate->getCarrier())) { + if (!$rate->isDeleted() && $this->carrierFactory->get($rate->getCarrier())) { if (!isset($rates[$rate->getCarrier()])) { $rates[$rate->getCarrier()] = []; } $rates[$rate->getCarrier()][] = $rate; - $rates[$rate->getCarrier()][0]->carrier_sort_order = $this->_carrierFactory->get( + $rates[$rate->getCarrier()][0]->carrier_sort_order = $this->carrierFactory->get( $rate->getCarrier() )->getSortOrder(); } @@ -881,7 +923,7 @@ protected function _sortRates($firstItem, $secondItem) * Retrieve shipping rate by identifier * * @param int $rateId - * @return \Magento\Quote\Model\Quote\Address\Rate|false + * @return Rate|false */ public function getShippingRateById($rateId) { @@ -898,7 +940,7 @@ public function getShippingRateById($rateId) * Retrieve shipping rate by code * * @param string $code - * @return \Magento\Quote\Model\Quote\Address\Rate|false + * @return Rate|false */ public function getShippingRateByCode($code) { @@ -927,10 +969,10 @@ public function removeAllShippingRates() /** * Add shipping rate * - * @param \Magento\Quote\Model\Quote\Address\Rate $rate + * @param Rate $rate * @return $this */ - public function addShippingRate(\Magento\Quote\Model\Quote\Address\Rate $rate) + public function addShippingRate(Rate $rate) { $rate->setAddress($this); $this->getShippingRatesCollection()->addItem($rate); @@ -970,14 +1012,14 @@ public function collectShippingRates() * * Returns true if current selected shipping method code corresponds to one of the found rates * - * @param \Magento\Quote\Model\Quote\Item\AbstractItem $item + * @param AbstractItem $item * @return bool * @SuppressWarnings(PHPMD.CyclomaticComplexity) * @SuppressWarnings(PHPMD.NPathComplexity) */ - public function requestShippingRates(\Magento\Quote\Model\Quote\Item\AbstractItem $item = null) + public function requestShippingRates(AbstractItem $item = null) { - /** @var $request \Magento\Quote\Model\Quote\Address\RateRequest */ + /** @var $request RateRequest */ $request = $this->_rateRequestFactory->create(); $request->setAllItems($item ? [$item] : $this->getAllItems()); $request->setDestCountryId($this->getCountryId()); @@ -1041,7 +1083,7 @@ public function requestShippingRates(\Magento\Quote\Model\Quote\Item\AbstractIte $item->setBaseShippingAmount($rate->getPrice()); } else { - /** @var \Magento\Store\Api\Data\StoreInterface */ + /** @var StoreInterface */ $store = $this->storeManager->getStore(); $amountPrice = $store->getBaseCurrency() ->convert($rate->getPrice(), $store->getCurrentCurrencyCode()); @@ -1078,17 +1120,17 @@ public function getTotals() /** * Add total data or model * - * @param \Magento\Quote\Model\Quote\Address\Total|array $total + * @param Total|array $total * @return $this */ public function addTotal($total) { $addressTotal = null; if (is_array($total)) { - /** @var \Magento\Quote\Model\Quote\Address\Total $addressTotal */ - $addressTotal = $this->_addressTotalFactory->create(\Magento\Quote\Model\Quote\Address\Total::class); + /** @var Total $addressTotal */ + $addressTotal = $this->_addressTotalFactory->create(Total::class); $addressTotal->setData($total); - } elseif ($total instanceof \Magento\Quote\Model\Quote\Address\Total) { + } elseif ($total instanceof Total) { $addressTotal = $total; } @@ -1104,7 +1146,7 @@ public function addTotal($total) /** * Rewrite clone method * - * @return \Magento\Quote\Model\Quote\Address + * @return Address */ public function __clone() { @@ -1141,7 +1183,7 @@ public function validateMinimumAmount() $storeId = $this->getQuote()->getStoreId(); $validateEnabled = $this->_scopeConfig->isSetFlag( 'sales/minimum_order/active', - \Magento\Store\Model\ScopeInterface::SCOPE_STORE, + ScopeInterface::SCOPE_STORE, $storeId ); if (!$validateEnabled) { @@ -1154,17 +1196,17 @@ public function validateMinimumAmount() $includeDiscount = $this->_scopeConfig->getValue( 'sales/minimum_order/include_discount_amount', - \Magento\Store\Model\ScopeInterface::SCOPE_STORE, + ScopeInterface::SCOPE_STORE, $storeId ); $amount = $this->_scopeConfig->getValue( 'sales/minimum_order/amount', - \Magento\Store\Model\ScopeInterface::SCOPE_STORE, + ScopeInterface::SCOPE_STORE, $storeId ); $taxInclude = $this->_scopeConfig->getValue( 'sales/minimum_order/tax_including', - \Magento\Store\Model\ScopeInterface::SCOPE_STORE, + ScopeInterface::SCOPE_STORE, $storeId ); @@ -1701,7 +1743,7 @@ public function setSaveInAddressBook($saveInAddressBook) /** * @inheritdoc * - * @return \Magento\Quote\Api\Data\AddressExtensionInterface|null + * @return AddressExtensionInterface|null */ public function getExtensionAttributes() { @@ -1711,10 +1753,10 @@ public function getExtensionAttributes() /** * @inheritdoc * - * @param \Magento\Quote\Api\Data\AddressExtensionInterface $extensionAttributes + * @param AddressExtensionInterface $extensionAttributes * @return $this */ - public function setExtensionAttributes(\Magento\Quote\Api\Data\AddressExtensionInterface $extensionAttributes) + public function setExtensionAttributes(AddressExtensionInterface $extensionAttributes) { return $this->_setExtensionAttributes($extensionAttributes); } diff --git a/app/code/Magento/Quote/Model/Quote/Item.php b/app/code/Magento/Quote/Model/Quote/Item.php index d14ae008ce407..2e4a9c7ded683 100644 --- a/app/code/Magento/Quote/Model/Quote/Item.php +++ b/app/code/Magento/Quote/Model/Quote/Item.php @@ -609,7 +609,7 @@ public function getOptionsByCode() /** * Add option to item * - * @param \Magento\Quote\Model\Quote\Item\Option|\Magento\Framework\DataObject $option + * @param \Magento\Quote\Model\Quote\Item\Option|\Magento\Framework\DataObject|array $option * @return $this * @throws \Magento\Framework\Exception\LocalizedException */ diff --git a/app/code/Magento/Quote/Model/Quote/Item/CartItemPersister.php b/app/code/Magento/Quote/Model/Quote/Item/CartItemPersister.php index 9b5f5c9a126df..86dcd0e4bfc07 100644 --- a/app/code/Magento/Quote/Model/Quote/Item/CartItemPersister.php +++ b/app/code/Magento/Quote/Model/Quote/Item/CartItemPersister.php @@ -6,14 +6,17 @@ namespace Magento\Quote\Model\Quote\Item; -use Magento\Quote\Api\Data\CartInterface; -use Magento\Quote\Api\Data\CartItemInterface; +use Magento\Catalog\Api\ProductRepositoryInterface; use Magento\Framework\Exception\CouldNotSaveException; use Magento\Framework\Exception\InputException; -use Magento\Framework\Exception\NoSuchEntityException; use Magento\Framework\Exception\LocalizedException; -use Magento\Catalog\Api\ProductRepositoryInterface; +use Magento\Framework\Exception\NoSuchEntityException; +use Magento\Quote\Api\Data\CartInterface; +use Magento\Quote\Api\Data\CartItemInterface; +/** + * Cart item save handler + */ class CartItemPersister { /** @@ -39,6 +42,8 @@ public function __construct( } /** + * Save cart item into cart + * * @param CartInterface $quote * @param CartItemInterface $item * @return CartItemInterface @@ -73,12 +78,13 @@ public function save(CartInterface $quote, CartItemInterface $item) $item = $quote->updateItem($itemId, $buyRequestData); } else { if ($item->getQty() !== $currentItem->getQty()) { + $currentItem->clearMessage(); $currentItem->setQty($qty); /** * Qty validation errors are stored as items message * @see \Magento\CatalogInventory\Model\Quote\Item\QuantityValidator::validate */ - if (!empty($currentItem->getMessage())) { + if (!empty($currentItem->getMessage()) && $currentItem->getHasError()) { throw new LocalizedException(__($currentItem->getMessage())); } } diff --git a/app/code/Magento/Quote/Model/Quote/Item/Updater.php b/app/code/Magento/Quote/Model/Quote/Item/Updater.php index 9865ae82ac3d6..270d9160161a8 100644 --- a/app/code/Magento/Quote/Model/Quote/Item/Updater.php +++ b/app/code/Magento/Quote/Model/Quote/Item/Updater.php @@ -8,12 +8,11 @@ use Magento\Catalog\Model\ProductFactory; use Magento\Framework\Locale\FormatInterface; use Magento\Framework\DataObject\Factory as ObjectFactory; -use Magento\Quote\Model\Quote; use Magento\Quote\Model\Quote\Item; -use Zend\Code\Exception\InvalidArgumentException; +use Laminas\Code\Exception\InvalidArgumentException; /** - * Class Updater + * Quote item updater */ class Updater { diff --git a/app/code/Magento/Quote/Model/ResourceModel/Quote.php b/app/code/Magento/Quote/Model/ResourceModel/Quote.php index ae26407c74522..48945dacd1738 100644 --- a/app/code/Magento/Quote/Model/ResourceModel/Quote.php +++ b/app/code/Magento/Quote/Model/ResourceModel/Quote.php @@ -102,6 +102,7 @@ public function loadByCustomerId($quote, $customerId) if ($data) { $quote->setData($data); + $quote->setOrigData(); } $this->_afterLoad($quote); @@ -124,6 +125,7 @@ public function loadActive($quote, $quoteId) $data = $connection->fetchRow($select); if ($data) { $quote->setData($data); + $quote->setOrigData(); } $this->_afterLoad($quote); @@ -148,6 +150,7 @@ public function loadByIdWithoutStore($quote, $quoteId) if ($data) { $quote->setData($data); + $quote->setOrigData(); } } @@ -303,5 +306,7 @@ public function save(\Magento\Framework\Model\AbstractModel $object) if (!$object->isPreventSaving()) { return parent::save($object); } + + return $this; } } diff --git a/app/code/Magento/Quote/Model/ShippingMethodManagement.php b/app/code/Magento/Quote/Model/ShippingMethodManagement.php index f62866539c6cc..73a2a43b2581f 100644 --- a/app/code/Magento/Quote/Model/ShippingMethodManagement.php +++ b/app/code/Magento/Quote/Model/ShippingMethodManagement.php @@ -7,6 +7,7 @@ namespace Magento\Quote\Model; use Magento\Customer\Api\Data\AddressInterfaceFactory; +use Magento\Customer\Model\Session as CustomerSession; use Magento\Framework\App\ObjectManager; use Magento\Framework\Exception\CouldNotSaveException; use Magento\Framework\Exception\InputException; @@ -22,6 +23,7 @@ * Shipping method read service * * @SuppressWarnings(PHPMD.CouplingBetweenObjects) + * @SuppressWarnings(PHPMD.CookieAndSessionMisuse) */ class ShippingMethodManagement implements \Magento\Quote\Api\ShippingMethodManagementInterface, @@ -69,6 +71,11 @@ class ShippingMethodManagement implements */ private $quoteAddressResource; + /** + * @var CustomerSession + */ + private $customerSession; + /** * Constructor * @@ -78,6 +85,7 @@ class ShippingMethodManagement implements * @param Quote\TotalsCollector $totalsCollector * @param AddressInterfaceFactory|null $addressFactory * @param QuoteAddressResource|null $quoteAddressResource + * @param CustomerSession|null $customerSession */ public function __construct( \Magento\Quote\Api\CartRepositoryInterface $quoteRepository, @@ -85,7 +93,8 @@ public function __construct( \Magento\Customer\Api\AddressRepositoryInterface $addressRepository, \Magento\Quote\Model\Quote\TotalsCollector $totalsCollector, AddressInterfaceFactory $addressFactory = null, - QuoteAddressResource $quoteAddressResource = null + QuoteAddressResource $quoteAddressResource = null, + CustomerSession $customerSession = null ) { $this->quoteRepository = $quoteRepository; $this->converter = $converter; @@ -95,10 +104,11 @@ public function __construct( ->get(AddressInterfaceFactory::class); $this->quoteAddressResource = $quoteAddressResource ?: ObjectManager::getInstance() ->get(QuoteAddressResource::class); + $this->customerSession = $customerSession ?? ObjectManager::getInstance()->get(CustomerSession::class); } /** - * {@inheritDoc} + * @inheritDoc */ public function get($cartId) { @@ -126,7 +136,7 @@ public function get($cartId) } /** - * {@inheritDoc} + * @inheritDoc */ public function getList($cartId) { @@ -155,7 +165,7 @@ public function getList($cartId) } /** - * {@inheritDoc} + * @inheritDoc */ public function set($cartId, $carrierCode, $methodCode) { @@ -176,6 +186,8 @@ public function set($cartId, $carrierCode, $methodCode) } /** + * Apply carrier code. + * * @param int $cartId The shopping cart ID. * @param string $carrierCode The carrier code. * @param string $methodCode The shipping method code. @@ -209,7 +221,7 @@ public function apply($cartId, $carrierCode, $methodCode) } /** - * {@inheritDoc} + * @inheritDoc */ public function estimateByAddress($cartId, \Magento\Quote\Api\Data\EstimateAddressInterface $address) { @@ -240,7 +252,7 @@ public function estimateByExtendedAddress($cartId, AddressInterface $address) } /** - * {@inheritDoc} + * @inheritDoc */ public function estimateByAddressId($cartId, $addressId) { @@ -266,6 +278,7 @@ public function estimateByAddressId($cartId, $addressId) * @param string $region * @param \Magento\Framework\Api\ExtensibleDataInterface|null $address * @return \Magento\Quote\Api\Data\ShippingMethodInterface[] An array of shipping methods. + * @SuppressWarnings(PHPMD.UnusedFormalParameter) * @deprecated 100.2.0 */ protected function getEstimatedRates( @@ -277,11 +290,10 @@ protected function getEstimatedRates( $address = null ) { if (!$address) { - $address = $this->getAddressFactory()->create() + $address = $this->addressFactory->create() ->setCountryId($country) ->setPostcode($postcode) - ->setRegionId($regionId) - ->setRegion($region); + ->setRegionId($regionId); } return $this->getShippingMethods($quote, $address); } @@ -301,12 +313,21 @@ private function getShippingMethods(Quote $quote, $address) $shippingAddress->setCollectShippingRates(true); $this->totalsCollector->collectAddressTotals($quote, $shippingAddress); + $quoteCustomerGroupId = $quote->getCustomerGroupId(); + $customerGroupId = $this->customerSession->getCustomerGroupId(); + $isCustomerGroupChanged = $quoteCustomerGroupId !== $customerGroupId; + if ($isCustomerGroupChanged) { + $quote->setCustomerGroupId($customerGroupId); + } $shippingRates = $shippingAddress->getGroupedAllShippingRates(); foreach ($shippingRates as $carrierRates) { foreach ($carrierRates as $rate) { $output[] = $this->converter->modelToDataObject($rate, $quote->getQuoteCurrencyCode()); } } + if ($isCustomerGroupChanged) { + $quote->setCustomerGroupId($quoteCustomerGroupId); + } return $output; } diff --git a/app/code/Magento/Quote/Plugin/UpdateCartId.php b/app/code/Magento/Quote/Plugin/UpdateCartId.php new file mode 100644 index 0000000000000..46aecd176454f --- /dev/null +++ b/app/code/Magento/Quote/Plugin/UpdateCartId.php @@ -0,0 +1,51 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ + +declare(strict_types=1); + +namespace Magento\Quote\Plugin; + +use Magento\Framework\Webapi\Rest\Request as RestRequest; +use Magento\Quote\Api\Data\CartItemInterface; +use Magento\Quote\Api\GuestCartItemRepositoryInterface; + +/** + * Update cart id from request param + */ +class UpdateCartId +{ + /** + * @var RestRequest $request + */ + private $request; + + /** + * @param RestRequest $request + */ + public function __construct(RestRequest $request) + { + $this->request = $request; + } + + /** + * Update id from request if param cartId exist + * + * @param GuestCartItemRepositoryInterface $guestCartItemRepository + * @param CartItemInterface $cartItem + * @return void + * @SuppressWarnings(PHPMD.UnusedFormalParameter) + */ + public function beforeSave( + GuestCartItemRepositoryInterface $guestCartItemRepository, + CartItemInterface $cartItem + ): void { + $cartId = $this->request->getParam('cartId'); + + if ($cartId) { + $cartItem->setQuoteId($cartId); + } + } +} diff --git a/app/code/Magento/Quote/Test/Mftf/Metadata/billing_address-meta.xml b/app/code/Magento/Quote/Test/Mftf/Metadata/BillingAddressMeta.xml similarity index 100% rename from app/code/Magento/Quote/Test/Mftf/Metadata/billing_address-meta.xml rename to app/code/Magento/Quote/Test/Mftf/Metadata/BillingAddressMeta.xml diff --git a/app/code/Magento/Quote/Test/Mftf/Metadata/guest_cart-meta.xml b/app/code/Magento/Quote/Test/Mftf/Metadata/GuestCartMeta.xml similarity index 100% rename from app/code/Magento/Quote/Test/Mftf/Metadata/guest_cart-meta.xml rename to app/code/Magento/Quote/Test/Mftf/Metadata/GuestCartMeta.xml diff --git a/app/code/Magento/Quote/Test/Mftf/Metadata/shipping_address-meta.xml b/app/code/Magento/Quote/Test/Mftf/Metadata/ShippingAddressMeta.xml similarity index 100% rename from app/code/Magento/Quote/Test/Mftf/Metadata/shipping_address-meta.xml rename to app/code/Magento/Quote/Test/Mftf/Metadata/ShippingAddressMeta.xml diff --git a/app/code/Magento/Quote/Test/Mftf/Test/StorefrontGuestCheckoutDisabledProductTest.xml b/app/code/Magento/Quote/Test/Mftf/Test/StorefrontGuestCheckoutDisabledProductTest.xml index 6e31214b0dddf..12427c2caec25 100644 --- a/app/code/Magento/Quote/Test/Mftf/Test/StorefrontGuestCheckoutDisabledProductTest.xml +++ b/app/code/Magento/Quote/Test/Mftf/Test/StorefrontGuestCheckoutDisabledProductTest.xml @@ -87,6 +87,9 @@ <deleteData createDataKey="createConfigProductAttribute" stepKey="deleteConfigProductAttribute"/> <amOnPage url="{{AdminProductIndexPage.url}}" stepKey="navigateToProductListing"/> <actionGroup ref="ResetProductGridToDefaultViewActionGroup" stepKey="resetGridToDefaultKeywordSearch"/> + + <!-- Reindex invalidated indices after product attribute has been created/deleted --> + <magentoCron groups="index" stepKey="reindexInvalidatedIndices"/> </after> <!-- Step 1: Add simple product to shopping cart --> <amOnPage url="{{StorefrontProductPage.url($$createSimpleProduct.name$$)}}" stepKey="amOnSimpleProductPage"/> @@ -100,7 +103,7 @@ <waitForElement selector="{{StorefrontMessagesSection.messageProductAddedToCart($$createConfigProduct.name$$)}}" time="30" stepKey="assertMessage"/> <!--Disabled via admin panel--> <openNewTab stepKey="openNewTab"/> - <actionGroup ref="LoginAsAdmin" stepKey="loginAsAdmin"/> + <actionGroup ref="AdminLoginActionGroup" stepKey="loginAsAdmin"/> <!-- Find the first simple product that we just created using the product grid and go to its page--> <amOnPage url="{{AdminProductIndexPage.url}}" stepKey="visitAdminProductPage"/> <actionGroup ref="FilterProductGridBySkuActionGroup" stepKey="findCreatedProduct"> diff --git a/app/code/Magento/Quote/Test/Unit/Model/Cart/CartTotalRepositoryTest.php b/app/code/Magento/Quote/Test/Unit/Model/Cart/CartTotalRepositoryTest.php index 804f0863d2d2a..8c67ff4314b17 100644 --- a/app/code/Magento/Quote/Test/Unit/Model/Cart/CartTotalRepositoryTest.php +++ b/app/code/Magento/Quote/Test/Unit/Model/Cart/CartTotalRepositoryTest.php @@ -4,73 +4,104 @@ * Copyright © Magento, Inc. All rights reserved. * See COPYING.txt for license details. */ + +declare(strict_types=1); + namespace Magento\Quote\Test\Unit\Model\Cart; -use Magento\Framework\TestFramework\Unit\Helper\ObjectManager; +use Magento\Framework\Api\DataObjectHelper; +use Magento\Framework\TestFramework\Unit\Helper\ObjectManager as ObjectManagerHelper; +use Magento\Quote\Api\CartRepositoryInterface; +use Magento\Quote\Api\CouponManagementInterface; +use Magento\Quote\Api\Data\TotalSegmentInterface; +use Magento\Quote\Model\Cart\CartTotalRepository; +use Magento\Quote\Model\Cart\Totals\ItemConverter; +use Magento\Quote\Model\Quote; +use Magento\Quote\Model\Quote\Address; +use Magento\Quote\Model\Quote\Item as QuoteItem; +use Magento\Quote\Model\Cart\TotalsConverter; +use Magento\Quote\Api\Data\TotalsInterfaceFactory; +use Magento\Quote\Api\Data\TotalsInterface as QuoteTotalsInterface; +use PHPUnit\Framework\TestCase; +use PHPUnit\Framework\MockObject\MockObject; /** + * Test Cart totals object for class \Magento\Quote\Model\Cart\CartTotalRepository + * * @SuppressWarnings(PHPMD.CouplingBetweenObjects) */ -class CartTotalRepositoryTest extends \PHPUnit\Framework\TestCase +class CartTotalRepositoryTest extends TestCase { + private const STUB_CART_ID = 12; + + private const STUB_ITEMS_QTY = 100; + + private const STUB_CURRENCY_CODE = 'en_US'; + + private const STUB_COUPON = 'coupon'; + /** - * @var ObjectManager + * @var ObjectManagerHelper */ protected $objectManager; /** - * @var \PHPUnit_Framework_MockObject_MockObject + * @var ItemConverter|MockObject */ protected $converterMock; /** - * @var \Magento\Quote\Model\Cart\CartTotalRepository + * @var CartTotalRepository */ protected $model; /** - * @var \PHPUnit_Framework_MockObject_MockObject + * @var CartRepositoryInterface|MockObject */ private $quoteRepositoryMock; /** - * @var \PHPUnit_Framework_MockObject_MockObject + * @var MockObject */ private $quoteMock; /** - * @var \PHPUnit_Framework_MockObject_MockObject + * @var TotalsInterfaceFactory|MockObject */ private $totalsFactoryMock; /** - * @var \PHPUnit_Framework_MockObject_MockObject + * @var MockObject */ protected $addressMock; /** - * @var \Magento\Framework\Api\DataObjectHelper|\PHPUnit_Framework_MockObject_MockObject + * @var DataObjectHelper|MockObject */ protected $dataObjectHelperMock; /** - * @var \PHPUnit_Framework_MockObject_MockObject + * @var CouponManagementInterface|MockObject */ protected $couponServiceMock; /** - * @var \PHPUnit_Framework_MockObject_MockObject + * @var TotalsConverter|MockObject */ protected $totalsConverterMock; protected function setUp() { - $this->objectManager = new ObjectManager($this); + $this->objectManager = new ObjectManagerHelper($this); $this->totalsFactoryMock = $this->createPartialMock( - \Magento\Quote\Api\Data\TotalsInterfaceFactory::class, - ['create'] + TotalsInterfaceFactory::class, + [ + 'create' + ] ); - $this->quoteMock = $this->createPartialMock(\Magento\Quote\Model\Quote::class, [ + $this->quoteMock = $this->createPartialMock( + Quote::class, + [ 'isVirtual', 'getShippingAddress', 'getBillingAddress', @@ -79,21 +110,33 @@ protected function setUp() 'getQuoteCurrencyCode', 'getItemsQty', 'collectTotals' - ]); - $this->quoteRepositoryMock = $this->createMock(\Magento\Quote\Api\CartRepositoryInterface::class); + ] + ); + $this->quoteRepositoryMock = $this->createMock( + CartRepositoryInterface::class + ); $this->addressMock = $this->createPartialMock( - \Magento\Quote\Model\Quote\Address::class, - ['getData', 'getTotals'] + Address::class, + [ + 'getData', + 'getTotals' + ] + ); + $this->dataObjectHelperMock = $this->getMockBuilder( + DataObjectHelper::class + )->disableOriginalConstructor()->getMock(); + $this->converterMock = $this->createMock( + ItemConverter::class ); - $this->dataObjectHelperMock = $this->getMockBuilder(\Magento\Framework\Api\DataObjectHelper::class) - ->disableOriginalConstructor() - ->getMock(); - $this->converterMock = $this->createMock(\Magento\Quote\Model\Cart\Totals\ItemConverter::class); - $this->couponServiceMock = $this->createMock(\Magento\Quote\Api\CouponManagementInterface::class); - $this->totalsConverterMock = $this->createMock(\Magento\Quote\Model\Cart\TotalsConverter::class); + $this->couponServiceMock = $this->createMock( + CouponManagementInterface::class + ); + $this->totalsConverterMock = $this->createMock( + TotalsConverter::class + ); - $this->model = new \Magento\Quote\Model\Cart\CartTotalRepository( + $this->model = new CartTotalRepository( $this->totalsFactoryMock, $this->quoteRepositoryMock, $this->dataObjectHelperMock, @@ -104,17 +147,18 @@ protected function setUp() } /** + * Test get cart total + * * @param bool $isVirtual * @param string $getAddressType * @dataProvider getDataProvider + * + * @return void */ - public function testGet($isVirtual, $getAddressType) + public function testGetCartTotal($isVirtual, $getAddressType): void { - $cartId = 12; - $itemsQty = 100; - $coupon = 'coupon'; $addressTotals = ['address' => 'totals']; - $itemMock = $this->createMock(\Magento\Quote\Model\Quote\Item::class); + $itemMock = $this->createMock(QuoteItem::class); $visibleItems = [ 11 => $itemMock, ]; @@ -122,52 +166,95 @@ public function testGet($isVirtual, $getAddressType) 'name' => 'item', 'options' => [ 4 => ['label' => 'justLabel']], ]; - $currencyCode = 'US'; - $this->quoteRepositoryMock->expects($this->once()) ->method('getActive') - ->with($cartId) + ->with(self::STUB_CART_ID) ->willReturn($this->quoteMock); - $this->quoteMock->expects($this->once())->method('isVirtual')->willReturn($isVirtual); - $this->quoteMock->expects($this->exactly(2))->method($getAddressType)->willReturn($this->addressMock); - $this->quoteMock->expects($this->once())->method('getAllVisibleItems')->willReturn($visibleItems); - $this->quoteMock->expects($this->once())->method('getBaseCurrencyCode')->willReturn($currencyCode); - $this->quoteMock->expects($this->once())->method('getQuoteCurrencyCode')->willReturn($currencyCode); - $this->quoteMock->expects($this->once())->method('getItemsQty')->willReturn($itemsQty); - $this->addressMock->expects($this->any())->method('getData')->willReturn($addressTotals); - $this->addressMock->expects($this->once())->method('getTotals')->willReturn($addressTotals); - - $totalsMock = $this->createMock(\Magento\Quote\Api\Data\TotalsInterface::class); - $this->totalsFactoryMock->expects($this->once())->method('create')->willReturn($totalsMock); + $this->quoteMock->expects($this->once()) + ->method('isVirtual') + ->willReturn($isVirtual); + $this->quoteMock->expects($this->exactly(2)) + ->method($getAddressType) + ->willReturn($this->addressMock); + $this->quoteMock->expects($this->once()) + ->method('getAllVisibleItems') + ->willReturn($visibleItems); + $this->quoteMock->expects($this->once()) + ->method('getBaseCurrencyCode') + ->willReturn(self::STUB_CURRENCY_CODE); + $this->quoteMock->expects($this->once()) + ->method('getQuoteCurrencyCode') + ->willReturn(self::STUB_CURRENCY_CODE); + $this->quoteMock->expects($this->once()) + ->method('getItemsQty') + ->willReturn(self::STUB_ITEMS_QTY); + $this->addressMock->expects($this->any()) + ->method('getData') + ->willReturn($addressTotals); + $this->addressMock->expects($this->once()) + ->method('getTotals') + ->willReturn($addressTotals); + + $totalsMock = $this->createMock(QuoteTotalsInterface::class); + $this->totalsFactoryMock->expects($this->once()) + ->method('create') + ->willReturn($totalsMock); $this->dataObjectHelperMock->expects($this->once())->method('populateWithArray'); $this->converterMock->expects($this->once()) ->method('modelToDataObject') ->with($itemMock) ->willReturn($itemArray); - $totalSegmentsMock = $this->createMock(\Magento\Quote\Api\Data\TotalSegmentInterface::class); + $totalSegmentsMock = $this->createMock(TotalSegmentInterface::class); $this->totalsConverterMock->expects($this->once()) ->method('process') ->with($addressTotals) ->willReturn($totalSegmentsMock); - $this->couponServiceMock->expects($this->once())->method('get')->with($cartId)->willReturn($coupon); + $this->couponServiceMock + ->expects($this->once()) + ->method('get') + ->with(self::STUB_CART_ID) + ->willReturn(self::STUB_COUPON); - $totalsMock->expects($this->once())->method('setItems')->with([11 => $itemArray])->willReturnSelf(); - $totalsMock->expects($this->once())->method('setTotalSegments')->with($totalSegmentsMock)->willReturnSelf(); - $totalsMock->expects($this->once())->method('setCouponCode')->with($coupon)->willReturnSelf(); - $totalsMock->expects($this->once())->method('setGrandTotal')->willReturnSelf(); - $totalsMock->expects($this->once())->method('setItemsQty')->with($itemsQty)->willReturnSelf(); - $totalsMock->expects($this->once())->method('setBaseCurrencyCode')->with($currencyCode)->willReturnSelf(); - $totalsMock->expects($this->once())->method('setQuoteCurrencyCode')->with($currencyCode)->willReturnSelf(); + $totalsMock->expects($this->once()) + ->method('setItems') + ->with([11 => $itemArray]) + ->willReturnSelf(); + $totalsMock->expects($this->once()) + ->method('setTotalSegments') + ->with($totalSegmentsMock) + ->willReturnSelf(); + $totalsMock->expects($this->once()) + ->method('setCouponCode') + ->with(self::STUB_COUPON) + ->willReturnSelf(); + $totalsMock->expects($this->once()) + ->method('setGrandTotal') + ->willReturnSelf(); + $totalsMock->expects($this->once()) + ->method('setItemsQty') + ->with(self::STUB_ITEMS_QTY) + ->willReturnSelf(); + $totalsMock->expects($this->once()) + ->method('setBaseCurrencyCode') + ->with(self::STUB_CURRENCY_CODE) + ->willReturnSelf(); + $totalsMock->expects($this->once()) + ->method('setQuoteCurrencyCode') + ->with(self::STUB_CURRENCY_CODE) + ->willReturnSelf(); - $this->assertEquals($totalsMock, $this->model->get($cartId)); + $this->assertEquals($totalsMock, $this->model->get(self::STUB_CART_ID)); } /** + * Provide data for test different cases + * + * @param void * @return array */ - public function getDataProvider() + public function getDataProvider(): array { return [ 'Virtual Quote' => [ diff --git a/app/code/Magento/Quote/Test/Unit/Model/Product/Plugin/UpdateQuoteItemsTest.php b/app/code/Magento/Quote/Test/Unit/Model/Product/Plugin/UpdateQuoteItemsTest.php index cd7a54455a994..279b2c4b4091f 100644 --- a/app/code/Magento/Quote/Test/Unit/Model/Product/Plugin/UpdateQuoteItemsTest.php +++ b/app/code/Magento/Quote/Test/Unit/Model/Product/Plugin/UpdateQuoteItemsTest.php @@ -15,7 +15,7 @@ class UpdateQuoteItemsTest extends \PHPUnit\Framework\TestCase /** * @var \PHPUnit_Framework_MockObject_MockObject|\Magento\Quote\Model\ResourceModel\Quote */ - private $quoteResource ; + private $quoteResource; protected function setUp() { diff --git a/app/code/Magento/Quote/etc/db_schema.xml b/app/code/Magento/Quote/etc/db_schema.xml index d41591c619cde..44a5f275b4d9f 100644 --- a/app/code/Magento/Quote/etc/db_schema.xml +++ b/app/code/Magento/Quote/etc/db_schema.xml @@ -56,7 +56,7 @@ <column xsi:type="varchar" name="customer_lastname" nullable="true" length="255" comment="Customer Lastname"/> <column xsi:type="varchar" name="customer_suffix" nullable="true" length="40" comment="Customer Suffix"/> <column xsi:type="datetime" name="customer_dob" on_update="false" nullable="true" comment="Customer Dob"/> - <column xsi:type="varchar" name="customer_note" nullable="true" length="255" comment="Customer Note"/> + <column xsi:type="text" name="customer_note" nullable="true" comment="Customer Note"/> <column xsi:type="smallint" name="customer_note_notify" padding="5" unsigned="true" nullable="true" identity="false" default="1" comment="Customer Note Notify"/> <column xsi:type="smallint" name="customer_is_guest" padding="5" unsigned="true" nullable="true" diff --git a/app/code/Magento/Quote/etc/webapi_rest/di.xml b/app/code/Magento/Quote/etc/webapi_rest/di.xml index 27d5ff7753425..a55d2146be156 100644 --- a/app/code/Magento/Quote/etc/webapi_rest/di.xml +++ b/app/code/Magento/Quote/etc/webapi_rest/di.xml @@ -13,4 +13,7 @@ <plugin name="accessControl" type="Magento\Quote\Model\QuoteRepository\Plugin\AccessChangeQuoteControl" /> <plugin name="authorization" type="Magento\Quote\Model\QuoteRepository\Plugin\Authorization" /> </type> + <type name="Magento\Quote\Api\GuestCartItemRepositoryInterface"> + <plugin name="updateCartIdFromRequest" type="Magento\Quote\Plugin\UpdateCartId" /> + </type> </config> diff --git a/app/code/Magento/Quote/i18n/en_US.csv b/app/code/Magento/Quote/i18n/en_US.csv index b24179297493a..c899c432c70d4 100644 --- a/app/code/Magento/Quote/i18n/en_US.csv +++ b/app/code/Magento/Quote/i18n/en_US.csv @@ -31,6 +31,7 @@ Subtotal,Subtotal "Cart %1 does not contain item %2","Cart %1 does not contain item %2" "Could not save quote","Could not save quote" "Cart %1 doesn't contain item %2","Cart %1 doesn't contain item %2" +"The cart doesn't contain the item","The cart doesn't contain the item" "Could not remove item from quote","Could not remove item from quote" "The qty value is required to update quote item.","The qty value is required to update quote item." "Minimum order amount is %1","Minimum order amount is %1" diff --git a/app/code/Magento/Quote/registration.php b/app/code/Magento/Quote/registration.php index f74a3ac5ff9e5..da3425918cf0f 100644 --- a/app/code/Magento/Quote/registration.php +++ b/app/code/Magento/Quote/registration.php @@ -4,6 +4,6 @@ * See COPYING.txt for license details. */ -use \Magento\Framework\Component\ComponentRegistrar; +use Magento\Framework\Component\ComponentRegistrar; ComponentRegistrar::register(ComponentRegistrar::MODULE, 'Magento_Quote', __DIR__); diff --git a/app/code/Magento/QuoteGraphQl/Model/Cart/AddProductsToCart.php b/app/code/Magento/QuoteGraphQl/Model/Cart/AddProductsToCart.php index 91c77a1a3ecc5..0360d9ccf5476 100644 --- a/app/code/Magento/QuoteGraphQl/Model/Cart/AddProductsToCart.php +++ b/app/code/Magento/QuoteGraphQl/Model/Cart/AddProductsToCart.php @@ -13,7 +13,7 @@ use Magento\Quote\Model\Quote; /** - * Add products to cart + * Adding products to cart using GraphQL */ class AddProductsToCart { @@ -54,16 +54,6 @@ public function execute(Quote $cart, array $cartItems): void $this->addProductToCart->execute($cart, $cartItemData); } - if ($cart->getData('has_error')) { - $e = new GraphQlInputException(__('Shopping cart errors')); - $errors = $cart->getErrors(); - foreach ($errors as $error) { - /** @var MessageInterface $error */ - $e->addError(new GraphQlInputException(__($error->getText()))); - } - throw $e; - } - $this->cartRepository->save($cart); } } diff --git a/app/code/Magento/QuoteGraphQl/Model/Cart/Address/SaveQuoteAddressToCustomerAddressBook.php b/app/code/Magento/QuoteGraphQl/Model/Cart/Address/SaveQuoteAddressToCustomerAddressBook.php deleted file mode 100644 index c87101156327e..0000000000000 --- a/app/code/Magento/QuoteGraphQl/Model/Cart/Address/SaveQuoteAddressToCustomerAddressBook.php +++ /dev/null @@ -1,104 +0,0 @@ -<?php -/** - * Copyright © Magento, Inc. All rights reserved. - * See COPYING.txt for license details. - */ -declare(strict_types=1); - -namespace Magento\QuoteGraphQl\Model\Cart\Address; - -use Magento\Customer\Api\AddressRepositoryInterface; -use Magento\Customer\Api\Data\AddressInterface; -use Magento\Customer\Api\Data\AddressInterfaceFactory; -use Magento\Customer\Api\Data\RegionInterface; -use Magento\Customer\Api\Data\RegionInterfaceFactory; -use Magento\Framework\Exception\InputException; -use Magento\Framework\Exception\LocalizedException; -use Magento\Framework\GraphQl\Exception\GraphQlInputException; -use Magento\Quote\Model\Quote\Address as QuoteAddress; - -/** - * Save Address to Customer Address Book. - */ -class SaveQuoteAddressToCustomerAddressBook -{ - /** - * @var AddressInterfaceFactory - */ - private $addressFactory; - - /** - * @var AddressRepositoryInterface - */ - private $addressRepository; - - /** - * @var RegionInterfaceFactory - */ - private $regionFactory; - - /** - * @param AddressInterfaceFactory $addressFactory - * @param AddressRepositoryInterface $addressRepository - * @param RegionInterfaceFactory $regionFactory - */ - public function __construct( - AddressInterfaceFactory $addressFactory, - AddressRepositoryInterface $addressRepository, - RegionInterfaceFactory $regionFactory - ) { - $this->addressFactory = $addressFactory; - $this->addressRepository = $addressRepository; - $this->regionFactory = $regionFactory; - } - - /** - * Save Address to Customer Address Book. - * - * @param QuoteAddress $quoteAddress - * @param int $customerId - * - * @return void - * @throws GraphQlInputException - */ - public function execute(QuoteAddress $quoteAddress, int $customerId): void - { - try { - /** @var AddressInterface $customerAddress */ - $customerAddress = $this->addressFactory->create(); - $customerAddress->setFirstname($quoteAddress->getFirstname()) - ->setLastname($quoteAddress->getLastname()) - ->setMiddlename($quoteAddress->getMiddlename()) - ->setPrefix($quoteAddress->getPrefix()) - ->setSuffix($quoteAddress->getSuffix()) - ->setVatId($quoteAddress->getVatId()) - ->setCountryId($quoteAddress->getCountryId()) - ->setCompany($quoteAddress->getCompany()) - ->setRegionId($quoteAddress->getRegionId()) - ->setFax($quoteAddress->getFax()) - ->setCity($quoteAddress->getCity()) - ->setPostcode($quoteAddress->getPostcode()) - ->setStreet($quoteAddress->getStreet()) - ->setTelephone($quoteAddress->getTelephone()) - ->setCustomerId($customerId); - - /** @var RegionInterface $region */ - $region = $this->regionFactory->create(); - $region->setRegionCode($quoteAddress->getRegionCode()) - ->setRegion($quoteAddress->getRegion()) - ->setRegionId($quoteAddress->getRegionId()); - $customerAddress->setRegion($region); - - $this->addressRepository->save($customerAddress); - } catch (InputException $inputException) { - $graphQlInputException = new GraphQlInputException(__($inputException->getMessage())); - $errors = $inputException->getErrors(); - foreach ($errors as $error) { - $graphQlInputException->addError(new GraphQlInputException(__($error->getMessage()))); - } - throw $graphQlInputException; - } catch (LocalizedException $exception) { - throw new GraphQlInputException(__($exception->getMessage()), $exception); - } - } -} diff --git a/app/code/Magento/QuoteGraphQl/Model/Cart/BuyRequest/BuyRequestBuilder.php b/app/code/Magento/QuoteGraphQl/Model/Cart/BuyRequest/BuyRequestBuilder.php index 90f32e96a5fde..c14cc1324732c 100644 --- a/app/code/Magento/QuoteGraphQl/Model/Cart/BuyRequest/BuyRequestBuilder.php +++ b/app/code/Magento/QuoteGraphQl/Model/Cart/BuyRequest/BuyRequestBuilder.php @@ -45,11 +45,11 @@ public function __construct( */ public function build(array $cartItemData): DataObject { - $requestData = []; + $requestData = [[]]; foreach ($this->providers as $provider) { - $requestData = array_merge($requestData, $provider->execute($cartItemData)); + $requestData[] = $provider->execute($cartItemData); } - return $this->dataObjectFactory->create(['data' => $requestData]); + return $this->dataObjectFactory->create(['data' => array_merge(...$requestData)]); } } diff --git a/app/code/Magento/QuoteGraphQl/Model/Cart/CreateEmptyCartForCustomer.php b/app/code/Magento/QuoteGraphQl/Model/Cart/CreateEmptyCartForCustomer.php index 52fb19df34a8a..442e26ba53b66 100644 --- a/app/code/Magento/QuoteGraphQl/Model/Cart/CreateEmptyCartForCustomer.php +++ b/app/code/Magento/QuoteGraphQl/Model/Cart/CreateEmptyCartForCustomer.php @@ -7,53 +7,35 @@ namespace Magento\QuoteGraphQl\Model\Cart; -use Magento\Quote\Api\CartManagementInterface; -use Magento\Quote\Model\QuoteIdMask; -use Magento\Quote\Model\QuoteIdMaskFactory; use Magento\Quote\Model\QuoteIdToMaskedQuoteIdInterface; -use Magento\Quote\Model\ResourceModel\Quote\QuoteIdMask as QuoteIdMaskResourceModel; +use Magento\Quote\Model\Cart\CustomerCartResolver; /** * Create empty cart for customer + * Masked quote ID will be returned as a result */ class CreateEmptyCartForCustomer { /** - * @var CartManagementInterface - */ - private $cartManagement; - - /** - * @var QuoteIdMaskFactory - */ - private $quoteIdMaskFactory; - - /** - * @var QuoteIdMaskResourceModel + * @var QuoteIdToMaskedQuoteIdInterface */ - private $quoteIdMaskResourceModel; + private $quoteIdToMaskedQuoteId; /** - * @var QuoteIdToMaskedQuoteIdInterface + * @var CustomerCartResolver */ - private $quoteIdToMaskedQuoteId; + private $cartResolver; /** - * @param CartManagementInterface $cartManagement - * @param QuoteIdMaskFactory $quoteIdMaskFactory - * @param QuoteIdMaskResourceModel $quoteIdMaskResourceModel * @param QuoteIdToMaskedQuoteIdInterface $quoteIdToMaskedQuoteId + * @param CustomerCartResolver $cartResolver */ public function __construct( - CartManagementInterface $cartManagement, - QuoteIdMaskFactory $quoteIdMaskFactory, - QuoteIdMaskResourceModel $quoteIdMaskResourceModel, - QuoteIdToMaskedQuoteIdInterface $quoteIdToMaskedQuoteId + QuoteIdToMaskedQuoteIdInterface $quoteIdToMaskedQuoteId, + CustomerCartResolver $cartResolver ) { - $this->cartManagement = $cartManagement; - $this->quoteIdMaskFactory = $quoteIdMaskFactory; - $this->quoteIdMaskResourceModel = $quoteIdMaskResourceModel; $this->quoteIdToMaskedQuoteId = $quoteIdToMaskedQuoteId; + $this->cartResolver = $cartResolver; } /** @@ -62,59 +44,14 @@ public function __construct( * @param int $customerId * @param string|null $predefinedMaskedQuoteId * @return string - */ - public function execute(int $customerId, string $predefinedMaskedQuoteId = null): string - { - $quoteId = (int) $this->cartManagement->createEmptyCartForCustomer($customerId); - - if ($predefinedMaskedQuoteId !== null) { - $maskedId = $this->createPredefinedMaskId($quoteId, $predefinedMaskedQuoteId); - } else { - $maskedId = $this->getQuoteMaskId($quoteId); - } - - return $maskedId; - } - - /** - * Create quote masked id from predefined value - * - * @param int $quoteId - * @param string $maskId - * @return string - * @throws \Magento\Framework\Exception\AlreadyExistsException - */ - private function createPredefinedMaskId(int $quoteId, string $maskId): string - { - /** @var QuoteIdMask $quoteIdMask */ - $quoteIdMask = $this->quoteIdMaskFactory->create(); - $quoteIdMask->setQuoteId($quoteId); - $quoteIdMask->setMaskedId($maskId); - - $this->quoteIdMaskResourceModel->save($quoteIdMask); - - return $quoteIdMask->getMaskedId(); - } - - /** - * Fetch or create masked id for customer's active quote - * - * @param int $quoteId - * @return string - * @throws \Magento\Framework\Exception\AlreadyExistsException + * @throws \Magento\Framework\Exception\CouldNotSaveException * @throws \Magento\Framework\Exception\NoSuchEntityException */ - private function getQuoteMaskId(int $quoteId): string + public function execute(int $customerId, string $predefinedMaskedQuoteId = null): string { - $maskedId = $this->quoteIdToMaskedQuoteId->execute($quoteId); - if ($maskedId === '') { - $quoteIdMask = $this->quoteIdMaskFactory->create(); - $quoteIdMask->setQuoteId($quoteId); - - $this->quoteIdMaskResourceModel->save($quoteIdMask); - $maskedId = $quoteIdMask->getMaskedId(); - } + $cart = $this->cartResolver->resolve($customerId, $predefinedMaskedQuoteId); + $quoteId = (int) $cart->getId(); - return $maskedId; + return $this->quoteIdToMaskedQuoteId->execute($quoteId); } } diff --git a/app/code/Magento/QuoteGraphQl/Model/Cart/GetCartForUser.php b/app/code/Magento/QuoteGraphQl/Model/Cart/GetCartForUser.php index af70809a1053d..21243a4545fa3 100644 --- a/app/code/Magento/QuoteGraphQl/Model/Cart/GetCartForUser.php +++ b/app/code/Magento/QuoteGraphQl/Model/Cart/GetCartForUser.php @@ -72,9 +72,7 @@ public function execute(string $cartHash, ?int $customerId, int $storeId): Quote } if (false === (bool)$cart->getIsActive()) { - throw new GraphQlNoSuchEntityException( - __('Current user does not have an active cart.') - ); + throw new GraphQlNoSuchEntityException(__('The cart isn\'t active.')); } if ((int)$cart->getStoreId() !== $storeId) { diff --git a/app/code/Magento/QuoteGraphQl/Model/Cart/GetShippingAddress.php b/app/code/Magento/QuoteGraphQl/Model/Cart/GetShippingAddress.php index 2a57c281de183..c68b987fe8a96 100644 --- a/app/code/Magento/QuoteGraphQl/Model/Cart/GetShippingAddress.php +++ b/app/code/Magento/QuoteGraphQl/Model/Cart/GetShippingAddress.php @@ -8,14 +8,13 @@ namespace Magento\QuoteGraphQl\Model\Cart; use Magento\Framework\GraphQl\Exception\GraphQlAuthorizationException; -use Magento\GraphQl\Model\Query\ContextInterface; use Magento\Framework\GraphQl\Exception\GraphQlInputException; use Magento\Framework\GraphQl\Exception\GraphQlNoSuchEntityException; +use Magento\GraphQl\Model\Query\ContextInterface; use Magento\Quote\Model\Quote\Address; -use Magento\QuoteGraphQl\Model\Cart\Address\SaveQuoteAddressToCustomerAddressBook; /** - * Get shipping address + * Model for getting shipping address */ class GetShippingAddress { @@ -24,21 +23,13 @@ class GetShippingAddress */ private $quoteAddressFactory; - /** - * @var SaveQuoteAddressToCustomerAddressBook - */ - private $saveQuoteAddressToCustomerAddressBook; - /** * @param QuoteAddressFactory $quoteAddressFactory - * @param SaveQuoteAddressToCustomerAddressBook $saveQuoteAddressToCustomerAddressBook */ public function __construct( - QuoteAddressFactory $quoteAddressFactory, - SaveQuoteAddressToCustomerAddressBook $saveQuoteAddressToCustomerAddressBook + QuoteAddressFactory $quoteAddressFactory ) { $this->quoteAddressFactory = $quoteAddressFactory; - $this->saveQuoteAddressToCustomerAddressBook = $saveQuoteAddressToCustomerAddressBook; } /** @@ -96,25 +87,15 @@ private function createShippingAddress( if (null === $customerAddressId) { $shippingAddress = $this->quoteAddressFactory->createBasedOnInputData($addressInput); - - // need to save address only for registered user and if save_in_address_book = true - if (0 !== $customerId - && isset($addressInput['save_in_address_book']) - && (bool)$addressInput['save_in_address_book'] === true - ) { - $this->saveQuoteAddressToCustomerAddressBook->execute($shippingAddress, $customerId); - } } else { if (false === $context->getExtensionAttributes()->getIsCustomer()) { throw new GraphQlAuthorizationException(__('The current customer isn\'t authorized.')); } - $shippingAddress = $this->quoteAddressFactory->createBasedOnCustomerAddress( (int)$customerAddressId, $customerId ); } - return $shippingAddress; } } diff --git a/app/code/Magento/QuoteGraphQl/Model/Cart/SetBillingAddressOnCart.php b/app/code/Magento/QuoteGraphQl/Model/Cart/SetBillingAddressOnCart.php index 20a3677ef1feb..497819cbeec3b 100644 --- a/app/code/Magento/QuoteGraphQl/Model/Cart/SetBillingAddressOnCart.php +++ b/app/code/Magento/QuoteGraphQl/Model/Cart/SetBillingAddressOnCart.php @@ -13,7 +13,6 @@ use Magento\GraphQl\Model\Query\ContextInterface; use Magento\Quote\Api\Data\CartInterface; use Magento\Quote\Model\Quote\Address; -use Magento\QuoteGraphQl\Model\Cart\Address\SaveQuoteAddressToCustomerAddressBook; /** * Set billing address for a specified shopping cart @@ -30,24 +29,16 @@ class SetBillingAddressOnCart */ private $assignBillingAddressToCart; - /** - * @var SaveQuoteAddressToCustomerAddressBook - */ - private $saveQuoteAddressToCustomerAddressBook; - /** * @param QuoteAddressFactory $quoteAddressFactory * @param AssignBillingAddressToCart $assignBillingAddressToCart - * @param SaveQuoteAddressToCustomerAddressBook $saveQuoteAddressToCustomerAddressBook */ public function __construct( QuoteAddressFactory $quoteAddressFactory, - AssignBillingAddressToCart $assignBillingAddressToCart, - SaveQuoteAddressToCustomerAddressBook $saveQuoteAddressToCustomerAddressBook + AssignBillingAddressToCart $assignBillingAddressToCart ) { $this->quoteAddressFactory = $quoteAddressFactory; $this->assignBillingAddressToCart = $assignBillingAddressToCart; - $this->saveQuoteAddressToCustomerAddressBook = $saveQuoteAddressToCustomerAddressBook; } /** @@ -65,23 +56,18 @@ public function execute(ContextInterface $context, CartInterface $cart, array $b { $customerAddressId = $billingAddressInput['customer_address_id'] ?? null; $addressInput = $billingAddressInput['address'] ?? null; + + if (!$customerAddressId && !isset($billingAddressInput['address']['save_in_address_book']) && $addressInput) { + $addressInput['save_in_address_book'] = true; + } + // Need to keep this for BC of `use_for_shipping` field $sameAsShipping = isset($billingAddressInput['use_for_shipping']) ? (bool)$billingAddressInput['use_for_shipping'] : false; $sameAsShipping = isset($billingAddressInput['same_as_shipping']) ? (bool)$billingAddressInput['same_as_shipping'] : $sameAsShipping; - if (null === $customerAddressId && null === $addressInput) { - throw new GraphQlInputException( - __('The billing address must contain either "customer_address_id" or "address".') - ); - } - - if ($customerAddressId && $addressInput) { - throw new GraphQlInputException( - __('The billing address cannot contain "customer_address_id" and "address" at the same time.') - ); - } + $this->checkForInputExceptions($billingAddressInput); $addresses = $cart->getAllShippingAddresses(); if ($sameAsShipping && count($addresses) > 1) { @@ -95,6 +81,31 @@ public function execute(ContextInterface $context, CartInterface $cart, array $b $this->assignBillingAddressToCart->execute($cart, $billingAddress, $sameAsShipping); } + /** + * Check for the input exceptions + * + * @param array $billingAddressInput + * @throws GraphQlInputException + */ + private function checkForInputExceptions( + ?array $billingAddressInput + ) { + $customerAddressId = $billingAddressInput['customer_address_id'] ?? null; + $addressInput = $billingAddressInput['address'] ?? null; + + if (null === $customerAddressId && null === $addressInput) { + throw new GraphQlInputException( + __('The billing address must contain either "customer_address_id" or "address".') + ); + } + + if ($customerAddressId && $addressInput) { + throw new GraphQlInputException( + __('The billing address cannot contain "customer_address_id" and "address" at the same time.') + ); + } + } + /** * Create billing address * @@ -113,15 +124,6 @@ private function createBillingAddress( ): Address { if (null === $customerAddressId) { $billingAddress = $this->quoteAddressFactory->createBasedOnInputData($addressInput); - - $customerId = $context->getUserId(); - // need to save address only for registered user and if save_in_address_book = true - if (0 !== $customerId - && isset($addressInput['save_in_address_book']) - && (bool)$addressInput['save_in_address_book'] === true - ) { - $this->saveQuoteAddressToCustomerAddressBook->execute($billingAddress, $customerId); - } } else { if (false === $context->getExtensionAttributes()->getIsCustomer()) { throw new GraphQlAuthorizationException(__('The current customer isn\'t authorized.')); @@ -133,7 +135,6 @@ private function createBillingAddress( ); } $errors = $billingAddress->validate(); - if (true !== $errors) { $e = new GraphQlInputException(__('Billing address errors')); foreach ($errors as $error) { @@ -141,7 +142,6 @@ private function createBillingAddress( } throw $e; } - return $billingAddress; } } diff --git a/app/code/Magento/QuoteGraphQl/Model/Cart/SetShippingAddressesOnCart.php b/app/code/Magento/QuoteGraphQl/Model/Cart/SetShippingAddressesOnCart.php index e058913dde1d3..f73daa715c1df 100644 --- a/app/code/Magento/QuoteGraphQl/Model/Cart/SetShippingAddressesOnCart.php +++ b/app/code/Magento/QuoteGraphQl/Model/Cart/SetShippingAddressesOnCart.php @@ -10,7 +10,6 @@ use Magento\Framework\GraphQl\Exception\GraphQlInputException; use Magento\GraphQl\Model\Query\ContextInterface; use Magento\Quote\Api\Data\CartInterface; -use Magento\Quote\Model\Quote\Address; /** * Set single shipping address for a specified shopping cart @@ -49,7 +48,12 @@ public function execute(ContextInterface $context, CartInterface $cart, array $s __('You cannot specify multiple shipping addresses.') ); } - $shippingAddressInput = current($shippingAddressesInput); + $shippingAddressInput = current($shippingAddressesInput) ?? []; + $customerAddressId = $shippingAddressInput['customer_address_id'] ?? null; + + if (!$customerAddressId && !isset($shippingAddressInput['address']['save_in_address_book'])) { + $shippingAddressInput['address']['save_in_address_book'] = true; + } $shippingAddress = $this->getShippingAddress->execute($context, $shippingAddressInput); diff --git a/app/code/Magento/QuoteGraphQl/Model/Resolver/CartItems.php b/app/code/Magento/QuoteGraphQl/Model/Resolver/CartItems.php index 2674b3728619a..8017a91b5cfd2 100644 --- a/app/code/Magento/QuoteGraphQl/Model/Resolver/CartItems.php +++ b/app/code/Magento/QuoteGraphQl/Model/Resolver/CartItems.php @@ -9,6 +9,7 @@ use Magento\Framework\Exception\LocalizedException; use Magento\Framework\GraphQl\Config\Element\Field; +use Magento\Framework\GraphQl\Exception\GraphQlInputException; use Magento\Framework\GraphQl\Query\ResolverInterface; use Magento\Framework\GraphQl\Schema\Type\ResolveInfo; use Magento\Quote\Model\Quote\Item as QuoteItem; @@ -29,6 +30,12 @@ public function resolve(Field $field, $context, ResolveInfo $info, array $value $cart = $value['model']; $itemsData = []; + if ($cart->getData('has_error')) { + $errors = $cart->getErrors(); + foreach ($errors as $error) { + $itemsData[] = new GraphQlInputException(__($error->getText())); + } + } foreach ($cart->getAllVisibleItems() as $cartItem) { /** * @var QuoteItem $cartItem diff --git a/app/code/Magento/QuoteGraphQl/Model/Resolver/RemoveItemFromCart.php b/app/code/Magento/QuoteGraphQl/Model/Resolver/RemoveItemFromCart.php index bf9ccef8ae44a..c2045d4a0e8d5 100644 --- a/app/code/Magento/QuoteGraphQl/Model/Resolver/RemoveItemFromCart.php +++ b/app/code/Magento/QuoteGraphQl/Model/Resolver/RemoveItemFromCart.php @@ -65,7 +65,7 @@ public function resolve(Field $field, $context, ResolveInfo $info, array $value try { $this->cartItemRepository->deleteById((int)$cart->getId(), $itemId); } catch (NoSuchEntityException $e) { - throw new GraphQlNoSuchEntityException(__($e->getMessage()), $e); + throw new GraphQlNoSuchEntityException(__('The cart doesn\'t contain the item')); } catch (LocalizedException $e) { throw new GraphQlInputException(__($e->getMessage()), $e); } diff --git a/app/code/Magento/QuoteGraphQl/Model/Resolver/ShippingAddress/AvailableShippingMethods.php b/app/code/Magento/QuoteGraphQl/Model/Resolver/ShippingAddress/AvailableShippingMethods.php index eebed5aab6cc9..74513382df821 100644 --- a/app/code/Magento/QuoteGraphQl/Model/Resolver/ShippingAddress/AvailableShippingMethods.php +++ b/app/code/Magento/QuoteGraphQl/Model/Resolver/ShippingAddress/AvailableShippingMethods.php @@ -7,16 +7,14 @@ namespace Magento\QuoteGraphQl\Model\Resolver\ShippingAddress; -use Magento\Directory\Model\Currency; use Magento\Framework\Api\ExtensibleDataObjectConverter; use Magento\Framework\Exception\LocalizedException; -use Magento\Framework\Exception\NoSuchEntityException; use Magento\Framework\GraphQl\Config\Element\Field; use Magento\Framework\GraphQl\Query\ResolverInterface; use Magento\Framework\GraphQl\Schema\Type\ResolveInfo; use Magento\Quote\Api\Data\ShippingMethodInterface; use Magento\Quote\Model\Cart\ShippingMethodConverter; -use Magento\Store\Api\Data\StoreInterface; +use Magento\Quote\Model\Quote\TotalsCollector; /** * @inheritdoc @@ -33,16 +31,24 @@ class AvailableShippingMethods implements ResolverInterface */ private $shippingMethodConverter; + /** + * @var TotalsCollector + */ + private $totalsCollector; + /** * @param ExtensibleDataObjectConverter $dataObjectConverter * @param ShippingMethodConverter $shippingMethodConverter + * @param TotalsCollector $totalsCollector */ public function __construct( ExtensibleDataObjectConverter $dataObjectConverter, - ShippingMethodConverter $shippingMethodConverter + ShippingMethodConverter $shippingMethodConverter, + TotalsCollector $totalsCollector ) { $this->dataObjectConverter = $dataObjectConverter; $this->shippingMethodConverter = $shippingMethodConverter; + $this->totalsCollector = $totalsCollector; } /** @@ -53,7 +59,8 @@ public function resolve(Field $field, $context, ResolveInfo $info, array $value if (!isset($value['model'])) { throw new LocalizedException(__('"model" values should be specified')); } - $address = $value['model']; + $address = clone $value['model']; + $address->setLimitCarrier(null); // Allow shipping rates by setting country id for new addresses if (!$address->getCountryId() && $address->getCountryCode()) { @@ -61,9 +68,8 @@ public function resolve(Field $field, $context, ResolveInfo $info, array $value } $address->setCollectShippingRates(true); - $address->collectShippingRates(); $cart = $address->getQuote(); - + $this->totalsCollector->collectAddressTotals($cart, $address); $methods = []; $shippingRates = $address->getGroupedAllShippingRates(); foreach ($shippingRates as $carrierRates) { @@ -88,7 +94,6 @@ public function resolve(Field $field, $context, ResolveInfo $info, array $value * @param array $data * @param string $quoteCurrencyCode * @return array - * @throws NoSuchEntityException */ private function processMoneyTypeData(array $data, string $quoteCurrencyCode): array { diff --git a/app/code/Magento/QuoteGraphQl/etc/schema.graphqls b/app/code/Magento/QuoteGraphQl/etc/schema.graphqls index d334d56c85aac..1ca00d5ef7bdc 100644 --- a/app/code/Magento/QuoteGraphQl/etc/schema.graphqls +++ b/app/code/Magento/QuoteGraphQl/etc/schema.graphqls @@ -112,7 +112,7 @@ input CartAddressInput { postcode: String country_code: String! telephone: String! - save_in_address_book: Boolean + save_in_address_book: Boolean @doc(description: "Determines whether to save the address in the customer's address book. The default value is true") } input SetShippingMethodsOnCartInput { diff --git a/app/code/Magento/Reports/Block/Adminhtml/Config/Form/Field/YtdStart.php b/app/code/Magento/Reports/Block/Adminhtml/Config/Form/Field/YtdStart.php index 4ac12501aa90f..b564e582943ac 100644 --- a/app/code/Magento/Reports/Block/Adminhtml/Config/Form/Field/YtdStart.php +++ b/app/code/Magento/Reports/Block/Adminhtml/Config/Form/Field/YtdStart.php @@ -5,16 +5,17 @@ */ namespace Magento\Reports\Block\Adminhtml\Config\Form\Field; +use Magento\Config\Block\System\Config\Form\Field; use Magento\Framework\Data\Form\Element\AbstractElement; /** * Dashboard Year-To-Date Month and Day starts Field Renderer - * - * @author Magento Core Team <core@magentocommerce.com> */ -class YtdStart extends \Magento\Config\Block\System\Config\Form\Field +class YtdStart extends Field { /** + * Get Month and Day Element + * * @param AbstractElement $element * @return string * @SuppressWarnings(PHPMD.NPathComplexity) @@ -23,36 +24,29 @@ protected function _getElementHtml(AbstractElement $element) { $_months = []; for ($i = 1; $i <= 12; $i++) { - $_months[$i] = $this->_localeDate->date(mktime(null, null, null, $i, 1))->format('m'); + $month = $this->_localeDate->date(mktime(null, null, null, $i, 1)) + ->format('m'); + $_months[$month] = $month; } + ksort($_months); + $_days = []; for ($i = 1; $i <= 31; $i++) { $_days[$i] = $i < 10 ? '0' . $i : $i; } - if ($element->getValue()) { - $values = explode(',', $element->getValue()); - } else { - $values = []; - } - + $values = $element->getValue() ? explode(',', $element->getValue()) : []; $element->setName($element->getName() . '[]'); - $_monthsHtml = $element->setStyle( - 'width:100px;' - )->setValues( - $_months - )->setValue( - isset($values[0]) ? $values[0] : null - )->getElementHtml(); + $_monthsHtml = $element->setStyle('width:100px;') + ->setValues($_months) + ->setValue(isset($values[0]) ? $values[0] : null) + ->getElementHtml(); - $_daysHtml = $element->setStyle( - 'width:50px;' - )->setValues( - $_days - )->setValue( - isset($values[1]) ? $values[1] : null - )->getElementHtml(); + $_daysHtml = $element->setStyle('width:50px;') + ->setValues($_days) + ->setValue(isset($values[1]) ? $values[1] : null) + ->getElementHtml(); return sprintf('%s %s', $_monthsHtml, $_daysHtml); } diff --git a/app/code/Magento/Reports/Block/Adminhtml/Product/Lowstock/Grid.php b/app/code/Magento/Reports/Block/Adminhtml/Product/Lowstock/Grid.php index 5460dab3a7ff8..a7a79ff05640d 100644 --- a/app/code/Magento/Reports/Block/Adminhtml/Product/Lowstock/Grid.php +++ b/app/code/Magento/Reports/Block/Adminhtml/Product/Lowstock/Grid.php @@ -3,8 +3,17 @@ * Copyright © Magento, Inc. All rights reserved. * See COPYING.txt for license details. */ +declare(strict_types=1); + namespace Magento\Reports\Block\Adminhtml\Product\Lowstock; +use Magento\Backend\Block\Template\Context; +use Magento\Backend\Helper\Data; +use Magento\Catalog\Model\Product\Attribute\Source\Status; +use Magento\Framework\Data\Collection as DataCollection; +use Magento\Reports\Model\ResourceModel\Product\Lowstock\Collection; +use Magento\Reports\Model\ResourceModel\Product\Lowstock\CollectionFactory; + /** * Adminhtml low stock products report grid block * @@ -15,20 +24,20 @@ class Grid extends \Magento\Backend\Block\Widget\Grid { /** - * @var \Magento\Reports\Model\ResourceModel\Product\Lowstock\CollectionFactory + * @var CollectionFactory */ protected $_lowstocksFactory; /** - * @param \Magento\Backend\Block\Template\Context $context - * @param \Magento\Backend\Helper\Data $backendHelper - * @param \Magento\Reports\Model\ResourceModel\Product\Lowstock\CollectionFactory $lowstocksFactory + * @param Context $context + * @param Data $backendHelper + * @param CollectionFactory $lowstocksFactory * @param array $data */ public function __construct( - \Magento\Backend\Block\Template\Context $context, - \Magento\Backend\Helper\Data $backendHelper, - \Magento\Reports\Model\ResourceModel\Product\Lowstock\CollectionFactory $lowstocksFactory, + Context $context, + Data $backendHelper, + CollectionFactory $lowstocksFactory, array $data = [] ) { $this->_lowstocksFactory = $lowstocksFactory; @@ -36,6 +45,8 @@ public function __construct( } /** + * @inheritDoc + * * @return \Magento\Backend\Block\Widget\Grid */ protected function _prepareCollection() @@ -56,7 +67,7 @@ protected function _prepareCollection() $storeId = null; } - /** @var $collection \Magento\Reports\Model\ResourceModel\Product\Lowstock\Collection */ + /** @var $collection Collection */ $collection = $this->_lowstocksFactory->create()->addAttributeToSelect( '*' )->filterByIsQtyProductTypes()->joinInventoryItem( @@ -67,7 +78,10 @@ protected function _prepareCollection() $storeId )->setOrder( 'qty', - \Magento\Framework\Data\Collection::SORT_ORDER_ASC + DataCollection::SORT_ORDER_ASC + )->addAttributeToFilter( + 'status', + Status::STATUS_ENABLED ); if ($storeId) { diff --git a/app/code/Magento/Reports/Block/Product/Viewed.php b/app/code/Magento/Reports/Block/Product/Viewed.php index 5f9fdf8533523..ba4d03182213a 100644 --- a/app/code/Magento/Reports/Block/Product/Viewed.php +++ b/app/code/Magento/Reports/Block/Product/Viewed.php @@ -59,8 +59,7 @@ public function getCount() } /** - * Prepare to html - * check has viewed products + * Prepare to html check has viewed products * * @return string */ @@ -77,10 +76,10 @@ protected function _toHtml() */ public function getIdentities() { - $identities = []; + $identities = [[]]; foreach ($this->getItemsCollection() as $item) { - $identities = array_merge($identities, $item->getIdentities()); + $identities[] = $item->getIdentities(); } - return $identities; + return array_merge(...$identities); } } diff --git a/app/code/Magento/Reports/Model/ResourceModel/Order/Collection.php b/app/code/Magento/Reports/Model/ResourceModel/Order/Collection.php index d89a118bff94b..0a74c23fad991 100644 --- a/app/code/Magento/Reports/Model/ResourceModel/Order/Collection.php +++ b/app/code/Magento/Reports/Model/ResourceModel/Order/Collection.php @@ -461,6 +461,7 @@ public function getDateRange($range, $customStart, $customEnd, $returnObjects = $startMonth = isset($startMonthDay[0]) ? (int)$startMonthDay[0] : 1; $startDay = isset($startMonthDay[1]) ? (int)$startMonthDay[1] : 1; $dateStart->setDate($dateStart->format('Y'), $startMonth, $startDay); + $dateStart->modify('-1 year'); if ($range == '2y') { $dateStart->modify('-1 year'); } diff --git a/app/code/Magento/Reports/Model/ResourceModel/Product/Downloads/Collection.php b/app/code/Magento/Reports/Model/ResourceModel/Product/Downloads/Collection.php index 2009cd3ff9d92..d194526858cde 100644 --- a/app/code/Magento/Reports/Model/ResourceModel/Product/Downloads/Collection.php +++ b/app/code/Magento/Reports/Model/ResourceModel/Product/Downloads/Collection.php @@ -104,7 +104,7 @@ public function addFieldToFilter($field, $condition = null) public function getSelectCountSql() { $countSelect = parent::getSelectCountSql(); - $countSelect->reset(\Zend\Db\Sql\Select::GROUP); + $countSelect->reset(\Laminas\Db\Sql\Select::GROUP); return $countSelect; } } diff --git a/app/code/Magento/Reports/Model/ResourceModel/Product/Sold/Collection.php b/app/code/Magento/Reports/Model/ResourceModel/Product/Sold/Collection.php index 35a14e09e314f..bc8a67c2addff 100644 --- a/app/code/Magento/Reports/Model/ResourceModel/Product/Sold/Collection.php +++ b/app/code/Magento/Reports/Model/ResourceModel/Product/Sold/Collection.php @@ -12,6 +12,7 @@ namespace Magento\Reports\Model\ResourceModel\Product\Sold; use Magento\Framework\DB\Select; +use Zend_Db_Select_Exception; /** * Data collection. @@ -25,9 +26,10 @@ class Collection extends \Magento\Reports\Model\ResourceModel\Order\Collection /** * Set Date range to collection. * - * @param int $from - * @param int $to + * @param string $from + * @param string $to * @return $this + * @throws Zend_Db_Select_Exception */ public function setDateRange($from, $to) { @@ -49,6 +51,7 @@ public function setDateRange($from, $to) * @param string $from * @param string $to * @return $this + * @throws Zend_Db_Select_Exception */ public function addOrderedQty($from = '', $to = '') { @@ -84,7 +87,7 @@ public function addOrderedQty($from = '', $to = '') )->columns( 'SUM(order_items.qty_ordered) as ordered_qty' )->group( - 'order_items.product_id' + 'order_items.sku' ); return $this; } diff --git a/app/code/Magento/Reports/Model/ResourceModel/Review/Customer/Collection.php b/app/code/Magento/Reports/Model/ResourceModel/Review/Customer/Collection.php index 02eae4d75d2a7..f37bd6c6a7bd2 100644 --- a/app/code/Magento/Reports/Model/ResourceModel/Review/Customer/Collection.php +++ b/app/code/Magento/Reports/Model/ResourceModel/Review/Customer/Collection.php @@ -11,8 +11,13 @@ */ namespace Magento\Reports\Model\ResourceModel\Review\Customer; +use Magento\Framework\DB\Select; + /** + * Report Customer Review collection + * * @api + * @SuppressWarnings(PHPMD.CouplingBetweenObjects) * @since 100.0.2 */ class Collection extends \Magento\Review\Model\ResourceModel\Review\Collection @@ -104,38 +109,49 @@ protected function _joinCustomers() } /** - * {@inheritdoc} + * @inheritdoc * * Additional processing of 'customer_name' field is required, as it is a concat field, which can not be aliased. * @see _joinCustomers */ public function addFieldToFilter($field, $condition = null) { + if ($field === 'review_cnt') { + $conditionSql = $this->_getConditionSql($field, $condition); + $this->getSelect()->having($conditionSql, null, Select::TYPE_CONDITION); + } + if ($field === 'customer_name') { $field = $this->getConnection()->getConcatSql(['customer.firstname', 'customer.lastname'], ' '); } - return parent::addFieldToFilter($field, $condition); + return ($field === 'review_cnt') ? $this : parent::addFieldToFilter($field, $condition); } /** * Get select count sql * - * @return string + * @return \Magento\Framework\DB\Select + * @throws \Zend_Db_Select_Exception */ public function getSelectCountSql() { $countSelect = clone $this->getSelect(); + $havingClauses = $countSelect->getPart(Select::HAVING); + $whereClauses = $countSelect->getPart(Select::WHERE); $countSelect->reset(\Magento\Framework\DB\Select::ORDER); - $countSelect->reset(\Magento\Framework\DB\Select::GROUP); - $countSelect->reset(\Magento\Framework\DB\Select::HAVING); $countSelect->reset(\Magento\Framework\DB\Select::LIMIT_COUNT); $countSelect->reset(\Magento\Framework\DB\Select::LIMIT_OFFSET); - $countSelect->reset(\Magento\Framework\DB\Select::COLUMNS); - $countSelect->reset(\Magento\Framework\DB\Select::WHERE); - - $countSelect->columns(new \Zend_Db_Expr('COUNT(DISTINCT detail.customer_id)')); - - return $countSelect; + if (empty($whereClauses)) { + $countSelect->reset(\Magento\Framework\DB\Select::WHERE); + } + if (empty($havingClauses)) { + $countSelect->reset(\Magento\Framework\DB\Select::HAVING); + $countSelect->columns(new \Zend_Db_Expr('COUNT(DISTINCT detail.customer_id)')); + } + $aggregateSelect = clone $countSelect; + $aggregateSelect->reset(); + $aggregateSelect->from($countSelect, 'COUNT(*)'); + return $aggregateSelect; } } diff --git a/app/code/Magento/Reports/Model/ResourceModel/Review/Product/Collection.php b/app/code/Magento/Reports/Model/ResourceModel/Review/Product/Collection.php index 5343fbc4f6c7b..6f7738a8273bb 100644 --- a/app/code/Magento/Reports/Model/ResourceModel/Review/Product/Collection.php +++ b/app/code/Magento/Reports/Model/ResourceModel/Review/Product/Collection.php @@ -11,7 +11,11 @@ */ namespace Magento\Reports\Model\ResourceModel\Review\Product; +use Magento\Framework\DB\Select; + /** + * Review collection class + * * @api * @since 100.0.2 */ @@ -51,7 +55,7 @@ protected function _joinReview() 'e.entity_id = r.entity_pk_value', [ 'review_cnt' => new \Zend_Db_Expr(sprintf('(%s)', $subSelect)), - 'created_at' => 'MAX(r.created_at)' + 'last_review' => 'MAX(r.created_at)' ] )->group( 'e.entity_id' @@ -87,7 +91,7 @@ protected function _joinReview() */ public function addAttributeToSort($attribute, $dir = self::SORT_ORDER_ASC) { - if (in_array($attribute, ['review_cnt', 'created_at', 'avg_rating', 'avg_rating_approved'])) { + if (in_array($attribute, ['review_cnt', 'last_review', 'avg_rating', 'avg_rating_approved'])) { $this->getSelect()->order($attribute . ' ' . $dir); return $this; } @@ -95,25 +99,50 @@ public function addAttributeToSort($attribute, $dir = self::SORT_ORDER_ASC) return parent::addAttributeToSort($attribute, $dir); } + /** + * Add attribute to filter + * + * @param string $attribute + * @param array|null $condition + * @param string $joinType + * @return $this|\Magento\Catalog\Model\ResourceModel\Product\Collection + */ + public function addAttributeToFilter($attribute, $condition = null, $joinType = 'inner') + { + $aggregateFilterArray = ['review_cnt', 'last_review', 'avg_rating', 'avg_rating_approved']; + if (is_string($attribute) && + in_array($attribute, $aggregateFilterArray)) { + $conditionSql = $this->_getConditionSql($attribute, $condition); + $this->getSelect()->having($conditionSql, null, Select::TYPE_CONDITION); + } + return in_array($attribute, $aggregateFilterArray) ? $this : + parent::addAttributeToFilter($attribute, $condition, $joinType); + } + /** * Get select count sql * * @return \Magento\Framework\DB\Select + * @throws \Zend_Db_Select_Exception */ public function getSelectCountSql() { $this->_renderFilters(); - /* @var \Magento\Framework\DB\Select $select */ + /* @var Select $select */ $select = clone $this->getSelect(); - $select->reset(\Magento\Framework\DB\Select::ORDER); - $select->reset(\Magento\Framework\DB\Select::LIMIT_COUNT); - $select->reset(\Magento\Framework\DB\Select::LIMIT_OFFSET); - $select->reset(\Magento\Framework\DB\Select::COLUMNS); - $select->resetJoinLeft(); - $select->columns(new \Zend_Db_Expr('1')); - - /* @var \Magento\Framework\DB\Select $countSelect */ + $havingClauses = $select->getPart(Select::HAVING); + $select->reset(Select::ORDER); + $select->reset(Select::LIMIT_COUNT); + $select->reset(Select::LIMIT_OFFSET); + + if (empty($havingClauses)) { + $select->reset(Select::COLUMNS); + $select->reset(Select::HAVING); + $select->resetJoinLeft(); + $select->columns(new \Zend_Db_Expr('1')); + } + /* @var Select $countSelect */ $countSelect = clone $select; $countSelect->reset(); $countSelect->from($select, "COUNT(*)"); diff --git a/app/code/Magento/Reports/Test/Mftf/ActionGroup/AdminGenerateProductsOrderedReportActionGroup.xml b/app/code/Magento/Reports/Test/Mftf/ActionGroup/AdminGenerateProductsOrderedReportActionGroup.xml new file mode 100644 index 0000000000000..f6f8e4d93d033 --- /dev/null +++ b/app/code/Magento/Reports/Test/Mftf/ActionGroup/AdminGenerateProductsOrderedReportActionGroup.xml @@ -0,0 +1,24 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + /** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +--> + +<actionGroups xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/actionGroupSchema.xsd"> + <actionGroup name="AdminGenerateProductsOrderedReportActionGroup"> + <annotations> + <description>Navigate to Reports > Products > Ordered page. Enters the provided Order From/To Dates. Clicks on 'Refresh'.</description> + </annotations> + <arguments> + <argument name="orderFromDate" type="string"/> + <argument name="orderToDate" type="string"/> + </arguments> + + <amOnPage url="{{SoldReportPage.url}}" stepKey="navigateToOrderedProductPage"/> + <fillField selector="{{SoldReportFilterSection.dateFrom}}" userInput="{{orderFromDate}}" stepKey="fillFromDate"/> + <fillField selector="{{SoldReportFilterSection.dateTo}}" userInput="{{orderToDate}}" stepKey="fillToDate"/> + <click selector="{{SoldReportFilterSection.buttonRefresh}}" stepKey="clickRefresh"/> + </actionGroup> +</actionGroups> diff --git a/app/code/Magento/Reports/Test/Mftf/ActionGroup/AdminLowStockReportFilterProductActionGroup.xml b/app/code/Magento/Reports/Test/Mftf/ActionGroup/AdminLowStockReportFilterProductActionGroup.xml new file mode 100644 index 0000000000000..e77502497d83a --- /dev/null +++ b/app/code/Magento/Reports/Test/Mftf/ActionGroup/AdminLowStockReportFilterProductActionGroup.xml @@ -0,0 +1,21 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + /** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +--> + +<actionGroups xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/actionGroupSchema.xsd"> + <actionGroup name="AdminLowStockReportFilterProductActionGroup"> + <annotations> + <description>Filter in "Low Stock" report with by product SKU.</description> + </annotations> + <arguments> + <argument name="sku" type="string" defaultValue="{{_defaultProduct.sku}}"/> + </arguments> + + <fillField selector="{{LowStockReportFilterSection.productSku}}" userInput="{{sku}}" stepKey="fillSkuFilterField" /> + <click selector="{{LowStockReportFilterSection.searchButton}}" stepKey="clickSearch"/> + </actionGroup> +</actionGroups> diff --git a/app/code/Magento/Reports/Test/Mftf/Page/AdminSalesTaxReportPage.xml b/app/code/Magento/Reports/Test/Mftf/Page/AdminSalesTaxReportPage.xml new file mode 100644 index 0000000000000..db60433830001 --- /dev/null +++ b/app/code/Magento/Reports/Test/Mftf/Page/AdminSalesTaxReportPage.xml @@ -0,0 +1,11 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- + /** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +--> +<pages xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="urn:magento:mftf:Page/etc/PageObject.xsd"> + <page name="AdminSalesTaxReportPage" url="reports/report_sales/tax/" area="admin" module="Magento_Reports"> + </page> +</pages> diff --git a/app/code/Magento/Reports/Test/Mftf/Page/SoldReportPage.xml b/app/code/Magento/Reports/Test/Mftf/Page/SoldReportPage.xml new file mode 100644 index 0000000000000..6c883c8234b9a --- /dev/null +++ b/app/code/Magento/Reports/Test/Mftf/Page/SoldReportPage.xml @@ -0,0 +1,12 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- + /** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +--> +<pages xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="urn:magento:mftf:Page/etc/PageObject.xsd"> + <page name="SoldReportPage" url="reports/report_product/sold/" area="admin" module="Reports"> + <section name="SoldReportMainSection"/> + </page> +</pages> diff --git a/app/code/Magento/Reports/Test/Mftf/Section/OrderReportMainSection.xml b/app/code/Magento/Reports/Test/Mftf/Section/OrderReportMainSection.xml deleted file mode 100644 index bc8ac46424040..0000000000000 --- a/app/code/Magento/Reports/Test/Mftf/Section/OrderReportMainSection.xml +++ /dev/null @@ -1,29 +0,0 @@ -<?xml version="1.0" encoding="UTF-8"?> -<!-- - /** - * Copyright © Magento, Inc. All rights reserved. - * See COPYING.txt for license details. - */ ---> - -<sections xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" - xsi:noNamespaceSchemaLocation="urn:magento:mftf:Page/etc/SectionObject.xsd"> - <section name="OrderReportMainSection"> - <element name="showReport" type="button" selector="#filter_form_submit" timeout="60"/> - <element name="here" type="text" selector="//a[contains(text(), 'here')]" timeout="60"/> - </section> - - <section name="OrderReportFilterSection"> - <element name="dateFrom" type="input" selector="#sales_report_from"/> - <element name="dateTo" type="input" selector="#sales_report_to"/> - <element name="orderStatus" type="select" selector="#sales_report_show_order_statuses"/> - <element name="optionAny" type="select" selector="//select[@id='sales_report_show_order_statuses']/option[contains(text(), 'Any')]"/> - <element name="optionSpecified" type="select" selector="//select[@id='sales_report_show_order_statuses']/option[contains(text(), 'Specified')]"/> - <element name="orderStatusSpecified" type="select" selector="#sales_report_order_statuses"/> - </section> - - <section name="GeneratedReportSection"> - <element name="ordersCount" type="text" selector="//tr[@class='totals']/th[@class=' col-orders col-orders_count col-number']"/> - <element name="canceledOrders" type="text" selector="//tr[@class='totals']/th[@class=' col-canceled col-total_canceled_amount a-right']"/> - </section> -</sections> diff --git a/app/code/Magento/Reports/Test/Mftf/Section/OrderReportMainSection/GeneratedReportSection.xml b/app/code/Magento/Reports/Test/Mftf/Section/OrderReportMainSection/GeneratedReportSection.xml new file mode 100644 index 0000000000000..d9b0bfd956f80 --- /dev/null +++ b/app/code/Magento/Reports/Test/Mftf/Section/OrderReportMainSection/GeneratedReportSection.xml @@ -0,0 +1,14 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + /** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +--> +<sections xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Page/etc/SectionObject.xsd"> + <section name="GeneratedReportSection"> + <element name="ordersCount" type="text" selector="//tr[@class='totals']/th[@class=' col-orders col-orders_count col-number']"/> + <element name="canceledOrders" type="text" selector="//tr[@class='totals']/th[@class=' col-canceled col-total_canceled_amount a-right']"/> + </section> +</sections> diff --git a/app/code/Magento/Reports/Test/Mftf/Section/OrderReportMainSection/OrderReportFilterSection.xml b/app/code/Magento/Reports/Test/Mftf/Section/OrderReportMainSection/OrderReportFilterSection.xml new file mode 100644 index 0000000000000..980904ba08183 --- /dev/null +++ b/app/code/Magento/Reports/Test/Mftf/Section/OrderReportMainSection/OrderReportFilterSection.xml @@ -0,0 +1,18 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + /** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +--> +<sections xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Page/etc/SectionObject.xsd"> + <section name="OrderReportFilterSection"> + <element name="dateFrom" type="input" selector="#sales_report_from"/> + <element name="dateTo" type="input" selector="#sales_report_to"/> + <element name="orderStatus" type="select" selector="#sales_report_show_order_statuses"/> + <element name="optionAny" type="select" selector="//select[@id='sales_report_show_order_statuses']/option[contains(text(), 'Any')]"/> + <element name="optionSpecified" type="select" selector="//select[@id='sales_report_show_order_statuses']/option[contains(text(), 'Specified')]"/> + <element name="orderStatusSpecified" type="select" selector="#sales_report_order_statuses"/> + </section> +</sections> diff --git a/app/code/Magento/Reports/Test/Mftf/Section/OrderReportMainSection/OrderReportMainSection.xml b/app/code/Magento/Reports/Test/Mftf/Section/OrderReportMainSection/OrderReportMainSection.xml new file mode 100644 index 0000000000000..4368842e640d2 --- /dev/null +++ b/app/code/Magento/Reports/Test/Mftf/Section/OrderReportMainSection/OrderReportMainSection.xml @@ -0,0 +1,14 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + /** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +--> +<sections xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Page/etc/SectionObject.xsd"> + <section name="OrderReportMainSection"> + <element name="showReport" type="button" selector="#filter_form_submit" timeout="60"/> + <element name="here" type="text" selector="//a[contains(text(), 'here')]" timeout="60"/> + </section> +</sections> diff --git a/app/code/Magento/Reports/Test/Mftf/Section/SoldReportMainSection.xml b/app/code/Magento/Reports/Test/Mftf/Section/SoldReportMainSection.xml new file mode 100644 index 0000000000000..79f00ce217f6d --- /dev/null +++ b/app/code/Magento/Reports/Test/Mftf/Section/SoldReportMainSection.xml @@ -0,0 +1,17 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + /** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +--> + +<sections xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Page/etc/SectionObject.xsd"> + <section name="SoldReportFilterSection"> + <element name="dateFrom" type="input" selector="#gridProductsSold_period_date_from"/> + <element name="dateTo" type="input" selector="#gridProductsSold_period_date_to"/> + <element name="buttonRefresh" type="button" selector="//button/span[contains(text(),'Refresh')]" /> + <element name="gridProduct" type="text" selector="#gridProductsSold_table"/> + </section> +</sections> diff --git a/app/code/Magento/Reports/Test/Mftf/Test/AdminReportsAbandonedCartsNavigateMenuTest.xml b/app/code/Magento/Reports/Test/Mftf/Test/AdminReportsAbandonedCartsNavigateMenuTest.xml index 342955e0684b3..6daf322dd5b10 100644 --- a/app/code/Magento/Reports/Test/Mftf/Test/AdminReportsAbandonedCartsNavigateMenuTest.xml +++ b/app/code/Magento/Reports/Test/Mftf/Test/AdminReportsAbandonedCartsNavigateMenuTest.xml @@ -20,10 +20,10 @@ <group value="mtf_migrated"/> </annotations> <before> - <actionGroup ref="LoginAsAdmin" stepKey="LoginAsAdmin"/> + <actionGroup ref="AdminLoginActionGroup" stepKey="LoginAsAdmin"/> </before> <after> - <actionGroup ref="logout" stepKey="logout"/> + <actionGroup ref="AdminLogoutActionGroup" stepKey="logout"/> </after> <actionGroup ref="AdminNavigateMenuActionGroup" stepKey="navigateToAbandonedCartsPage"> <argument name="menuUiId" value="{{AdminMenuReports.dataUiId}}"/> diff --git a/app/code/Magento/Reports/Test/Mftf/Test/AdminReportsBestsellersNavigateMenuTest.xml b/app/code/Magento/Reports/Test/Mftf/Test/AdminReportsBestsellersNavigateMenuTest.xml index 259f2cde2786a..546b7ad167c25 100644 --- a/app/code/Magento/Reports/Test/Mftf/Test/AdminReportsBestsellersNavigateMenuTest.xml +++ b/app/code/Magento/Reports/Test/Mftf/Test/AdminReportsBestsellersNavigateMenuTest.xml @@ -20,10 +20,10 @@ <group value="mtf_migrated"/> </annotations> <before> - <actionGroup ref="LoginAsAdmin" stepKey="LoginAsAdmin"/> + <actionGroup ref="AdminLoginActionGroup" stepKey="LoginAsAdmin"/> </before> <after> - <actionGroup ref="logout" stepKey="logout"/> + <actionGroup ref="AdminLogoutActionGroup" stepKey="logout"/> </after> <actionGroup ref="AdminNavigateMenuActionGroup" stepKey="navigateToReportsBestsellersPage"> <argument name="menuUiId" value="{{AdminMenuReports.dataUiId}}"/> diff --git a/app/code/Magento/Reports/Test/Mftf/Test/AdminReportsCouponsNavigateMenuTest.xml b/app/code/Magento/Reports/Test/Mftf/Test/AdminReportsCouponsNavigateMenuTest.xml index 321f3078bc63f..14db012e76888 100644 --- a/app/code/Magento/Reports/Test/Mftf/Test/AdminReportsCouponsNavigateMenuTest.xml +++ b/app/code/Magento/Reports/Test/Mftf/Test/AdminReportsCouponsNavigateMenuTest.xml @@ -20,10 +20,10 @@ <group value="mtf_migrated"/> </annotations> <before> - <actionGroup ref="LoginAsAdmin" stepKey="LoginAsAdmin"/> + <actionGroup ref="AdminLoginActionGroup" stepKey="LoginAsAdmin"/> </before> <after> - <actionGroup ref="logout" stepKey="logout"/> + <actionGroup ref="AdminLogoutActionGroup" stepKey="logout"/> </after> <actionGroup ref="AdminNavigateMenuActionGroup" stepKey="navigateToReportsCouponsPage"> <argument name="menuUiId" value="{{AdminMenuReports.dataUiId}}"/> diff --git a/app/code/Magento/Reports/Test/Mftf/Test/AdminReportsDownloadsNavigateMenuTest.xml b/app/code/Magento/Reports/Test/Mftf/Test/AdminReportsDownloadsNavigateMenuTest.xml index 584c1af6683aa..fe578210d13a7 100644 --- a/app/code/Magento/Reports/Test/Mftf/Test/AdminReportsDownloadsNavigateMenuTest.xml +++ b/app/code/Magento/Reports/Test/Mftf/Test/AdminReportsDownloadsNavigateMenuTest.xml @@ -20,10 +20,10 @@ <group value="mtf_migrated"/> </annotations> <before> - <actionGroup ref="LoginAsAdmin" stepKey="LoginAsAdmin"/> + <actionGroup ref="AdminLoginActionGroup" stepKey="LoginAsAdmin"/> </before> <after> - <actionGroup ref="logout" stepKey="logout"/> + <actionGroup ref="AdminLogoutActionGroup" stepKey="logout"/> </after> <actionGroup ref="AdminNavigateMenuActionGroup" stepKey="navigateToReportsDownloadsPage"> <argument name="menuUiId" value="{{AdminMenuReports.dataUiId}}"/> diff --git a/app/code/Magento/Reports/Test/Mftf/Test/AdminReportsInvoicedNavigateMenuTest.xml b/app/code/Magento/Reports/Test/Mftf/Test/AdminReportsInvoicedNavigateMenuTest.xml index 34aec0620cad9..e9ed4caa7ef03 100644 --- a/app/code/Magento/Reports/Test/Mftf/Test/AdminReportsInvoicedNavigateMenuTest.xml +++ b/app/code/Magento/Reports/Test/Mftf/Test/AdminReportsInvoicedNavigateMenuTest.xml @@ -20,10 +20,10 @@ <group value="mtf_migrated"/> </annotations> <before> - <actionGroup ref="LoginAsAdmin" stepKey="LoginAsAdmin"/> + <actionGroup ref="AdminLoginActionGroup" stepKey="LoginAsAdmin"/> </before> <after> - <actionGroup ref="logout" stepKey="logout"/> + <actionGroup ref="AdminLogoutActionGroup" stepKey="logout"/> </after> <actionGroup ref="AdminNavigateMenuActionGroup" stepKey="navigateToReportsInvoicedPage"> <argument name="menuUiId" value="{{AdminMenuReports.dataUiId}}"/> diff --git a/app/code/Magento/Reports/Test/Mftf/Test/AdminReportsLowStockDisableProductTest.xml b/app/code/Magento/Reports/Test/Mftf/Test/AdminReportsLowStockDisableProductTest.xml new file mode 100644 index 0000000000000..c74e4651346d0 --- /dev/null +++ b/app/code/Magento/Reports/Test/Mftf/Test/AdminReportsLowStockDisableProductTest.xml @@ -0,0 +1,39 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + /** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +--> + +<tests xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/testSchema.xsd"> + <test name="AdminReportsLowStockDisableProductTest"> + <annotations> + <features value="Reports"/> + <group value="reports"/> + <title value="The disabled product doesn't present on 'Low Stock' report."/> + <description value="A product must don't presents on 'Low Stock' report if the product is disabled."/> + </annotations> + <before> + <actionGroup ref="AdminLoginActionGroup" stepKey="loginAsAdmin"/> + + <!-- Created disabled simple product with stock quantity zero --> + <createData entity="SimpleProductDisabledStockQuantityZero" stepKey="createProduct"/> + </before> + <after> + <deleteData createDataKey="createProduct" stepKey="deleteProduct"/> + <actionGroup ref="AdminLogoutActionGroup" stepKey="logout"/> + </after> + + <actionGroup ref="AdminNavigateMenuActionGroup" stepKey="navigateToReportsLowStockPage"> + <argument name="menuUiId" value="{{AdminMenuReports.dataUiId}}"/> + <argument name="submenuUiId" value="{{AdminMenuReportsProductsLowStock.dataUiId}}"/> + </actionGroup> + <actionGroup ref="AdminLowStockReportFilterProductActionGroup" stepKey="assertProductInReport"> + <argument name="sku" value="{{SimpleProductDisabledStockQuantityZero.sku}}"/> + </actionGroup> + + <!-- Verify doesn't present in the report --> + <dontSeeElement selector="{{LowStockProductGridSection.productSku}}" stepKey="assertSelector"/> + </test> +</tests> diff --git a/app/code/Magento/Reports/Test/Mftf/Test/AdminReportsLowStockNavigateMenuTest.xml b/app/code/Magento/Reports/Test/Mftf/Test/AdminReportsLowStockNavigateMenuTest.xml index 5d91d65a3a457..ce2c3f5084a21 100644 --- a/app/code/Magento/Reports/Test/Mftf/Test/AdminReportsLowStockNavigateMenuTest.xml +++ b/app/code/Magento/Reports/Test/Mftf/Test/AdminReportsLowStockNavigateMenuTest.xml @@ -20,10 +20,10 @@ <group value="mtf_migrated"/> </annotations> <before> - <actionGroup ref="LoginAsAdmin" stepKey="LoginAsAdmin"/> + <actionGroup ref="AdminLoginActionGroup" stepKey="LoginAsAdmin"/> </before> <after> - <actionGroup ref="logout" stepKey="logout"/> + <actionGroup ref="AdminLogoutActionGroup" stepKey="logout"/> </after> <actionGroup ref="AdminNavigateMenuActionGroup" stepKey="navigateToReportsLowStockPage"> <argument name="menuUiId" value="{{AdminMenuReports.dataUiId}}"/> diff --git a/app/code/Magento/Reports/Test/Mftf/Test/AdminReportsNewNavigateMenuTest.xml b/app/code/Magento/Reports/Test/Mftf/Test/AdminReportsNewNavigateMenuTest.xml index aeb35ba65a380..3b308edffeb2b 100644 --- a/app/code/Magento/Reports/Test/Mftf/Test/AdminReportsNewNavigateMenuTest.xml +++ b/app/code/Magento/Reports/Test/Mftf/Test/AdminReportsNewNavigateMenuTest.xml @@ -20,10 +20,10 @@ <group value="mtf_migrated"/> </annotations> <before> - <actionGroup ref="LoginAsAdmin" stepKey="LoginAsAdmin"/> + <actionGroup ref="AdminLoginActionGroup" stepKey="LoginAsAdmin"/> </before> <after> - <actionGroup ref="logout" stepKey="logout"/> + <actionGroup ref="AdminLogoutActionGroup" stepKey="logout"/> </after> <actionGroup ref="AdminNavigateMenuActionGroup" stepKey="navigateToReportsNewPage"> <argument name="menuUiId" value="{{AdminMenuReports.dataUiId}}"/> diff --git a/app/code/Magento/Reports/Test/Mftf/Test/AdminReportsOrderCountNavigateMenuTest.xml b/app/code/Magento/Reports/Test/Mftf/Test/AdminReportsOrderCountNavigateMenuTest.xml index 1bfbc654746e6..9a3f7ae5cee33 100644 --- a/app/code/Magento/Reports/Test/Mftf/Test/AdminReportsOrderCountNavigateMenuTest.xml +++ b/app/code/Magento/Reports/Test/Mftf/Test/AdminReportsOrderCountNavigateMenuTest.xml @@ -20,10 +20,10 @@ <group value="mtf_migrated"/> </annotations> <before> - <actionGroup ref="LoginAsAdmin" stepKey="LoginAsAdmin"/> + <actionGroup ref="AdminLoginActionGroup" stepKey="LoginAsAdmin"/> </before> <after> - <actionGroup ref="logout" stepKey="logout"/> + <actionGroup ref="AdminLogoutActionGroup" stepKey="logout"/> </after> <actionGroup ref="AdminNavigateMenuActionGroup" stepKey="navigateToReportsOrderCountPage"> <argument name="menuUiId" value="{{AdminMenuReports.dataUiId}}"/> diff --git a/app/code/Magento/Reports/Test/Mftf/Test/AdminReportsOrderTotalNavigateMenuTest.xml b/app/code/Magento/Reports/Test/Mftf/Test/AdminReportsOrderTotalNavigateMenuTest.xml index 88c94b53f5233..be8fc67317be0 100644 --- a/app/code/Magento/Reports/Test/Mftf/Test/AdminReportsOrderTotalNavigateMenuTest.xml +++ b/app/code/Magento/Reports/Test/Mftf/Test/AdminReportsOrderTotalNavigateMenuTest.xml @@ -20,10 +20,10 @@ <group value="mtf_migrated"/> </annotations> <before> - <actionGroup ref="LoginAsAdmin" stepKey="LoginAsAdmin"/> + <actionGroup ref="AdminLoginActionGroup" stepKey="LoginAsAdmin"/> </before> <after> - <actionGroup ref="logout" stepKey="logout"/> + <actionGroup ref="AdminLogoutActionGroup" stepKey="logout"/> </after> <actionGroup ref="AdminNavigateMenuActionGroup" stepKey="navigateToReportsOrderTotalPage"> <argument name="menuUiId" value="{{AdminMenuReports.dataUiId}}"/> diff --git a/app/code/Magento/Reports/Test/Mftf/Test/AdminReportsOrderedGroupedBySkuTest.xml b/app/code/Magento/Reports/Test/Mftf/Test/AdminReportsOrderedGroupedBySkuTest.xml new file mode 100644 index 0000000000000..650097214a0c4 --- /dev/null +++ b/app/code/Magento/Reports/Test/Mftf/Test/AdminReportsOrderedGroupedBySkuTest.xml @@ -0,0 +1,78 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + /** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +--> + +<tests xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/testSchema.xsd"> + <test name="AdminReportsOrderedGroupedBySkuTest"> + <annotations> + <title value="Verify grouped by SKU on report"/> + <description value="Verify the list of configurable product grouped by SKU, on report page 'Reports > Products > Ordered'"/> + <group value="reports"/> + </annotations> + <before> + <createData entity="_defaultCategory" stepKey="createCategory"/> + <actionGroup ref="AdminLoginActionGroup" stepKey="loginAsAdmin"/> + <actionGroup ref="CreateConfigurableProductActionGroup" stepKey="createConfigurableProduct"> + <argument name="product" value="_defaultProduct"/> + <argument name="category" value="$$createCategory$$"/> + </actionGroup> + <createData entity="Simple_US_Customer" stepKey="createCustomer"/> + </before> + <after> + <deleteData createDataKey="createCustomer" stepKey="deleteCustomer"/> + <deleteData createDataKey="createCategory" stepKey="deleteCategory"/> + <actionGroup ref="DeleteProductBySkuActionGroup" stepKey="deleteConfigurableProduct"> + <argument name="sku" value="{{_defaultProduct.sku}}"/> + </actionGroup> + <actionGroup ref="AdminDeleteProductAttributeByLabelActionGroup" stepKey="deleteAttributeSet"> + <argument name="productAttributeLabel" value="{{colorProductAttribute.default_label}}"/> + </actionGroup> + <actionGroup ref="AdminLogoutActionGroup" stepKey="logout"/> + </after> + + <!--Add first configurable product to order--> + <actionGroup ref="NavigateToNewOrderPageExistingCustomerActionGroup" stepKey="navigateToFirstOrderWithExistingCustomer"> + <argument name="customer" value="$$createCustomer$$"/> + </actionGroup> + <actionGroup ref="AddConfigurableProductToOrderActionGroup" stepKey="addFirstConfigurableProductToOrder"> + <argument name="product" value="_defaultProduct"/> + <argument name="attribute" value="colorProductAttribute"/> + <argument name="option" value="colorProductAttribute1"/> + </actionGroup> + <click selector="{{AdminOrderFormActionSection.SubmitOrder}}" stepKey="submitFirstOrder"/> + + <!--Add second configurable product to order--> + <actionGroup ref="NavigateToNewOrderPageExistingCustomerActionGroup" stepKey="navigateToSecondOrderWithExistingCustomer"> + <argument name="customer" value="$$createCustomer$$"/> + </actionGroup> + <actionGroup ref="AddConfigurableProductToOrderActionGroup" stepKey="addSecondConfigurableProductToOrder"> + <argument name="product" value="_defaultProduct"/> + <argument name="attribute" value="colorProductAttribute"/> + <argument name="option" value="colorProductAttribute2"/> + </actionGroup> + <click selector="{{AdminOrderFormActionSection.SubmitOrder}}" stepKey="submitSecondOrder"/> + + <!-- Get date --> + <generateDate stepKey="generateStartDate" date="-1 minute" format="m/d/Y"/> + <generateDate stepKey="generateEndDate" date="+1 minute" format="m/d/Y"/> + <actionGroup ref="AdminGenerateProductsOrderedReportActionGroup" stepKey="generateReport"> + <argument name="orderFromDate" value="$generateStartDate"/> + <argument name="orderToDate" value="$generateEndDate" /> + </actionGroup> + + <!-- Verify data --> + <grabTextFrom selector="{{SoldReportFilterSection.gridProduct}}" stepKey="grabData"/> + <assertContains stepKey="assertFirst"> + <actualResult type="string">{$grabData}</actualResult> + <expectedResult type="string">{{_defaultProduct.sku}}-{{colorProductAttribute1.name}}</expectedResult> + </assertContains> + <assertContains stepKey="assertSecond"> + <actualResult type="string">{$grabData}</actualResult> + <expectedResult type="string">{{_defaultProduct.sku}}-{{colorProductAttribute2.name}}</expectedResult> + </assertContains> + </test> +</tests> diff --git a/app/code/Magento/Reports/Test/Mftf/Test/AdminReportsOrderedNavigateMenuTest.xml b/app/code/Magento/Reports/Test/Mftf/Test/AdminReportsOrderedNavigateMenuTest.xml index e81239539a5b5..40e1005ec7c8c 100644 --- a/app/code/Magento/Reports/Test/Mftf/Test/AdminReportsOrderedNavigateMenuTest.xml +++ b/app/code/Magento/Reports/Test/Mftf/Test/AdminReportsOrderedNavigateMenuTest.xml @@ -20,10 +20,10 @@ <group value="mtf_migrated"/> </annotations> <before> - <actionGroup ref="LoginAsAdmin" stepKey="LoginAsAdmin"/> + <actionGroup ref="AdminLoginActionGroup" stepKey="LoginAsAdmin"/> </before> <after> - <actionGroup ref="logout" stepKey="logout"/> + <actionGroup ref="AdminLogoutActionGroup" stepKey="logout"/> </after> <actionGroup ref="AdminNavigateMenuActionGroup" stepKey="navigateToReportsOrderedPage"> <argument name="menuUiId" value="{{AdminMenuReports.dataUiId}}"/> diff --git a/app/code/Magento/Reports/Test/Mftf/Test/AdminReportsOrdersNavigateMenuTest.xml b/app/code/Magento/Reports/Test/Mftf/Test/AdminReportsOrdersNavigateMenuTest.xml index 13fc8e7353139..ed7eff4430030 100644 --- a/app/code/Magento/Reports/Test/Mftf/Test/AdminReportsOrdersNavigateMenuTest.xml +++ b/app/code/Magento/Reports/Test/Mftf/Test/AdminReportsOrdersNavigateMenuTest.xml @@ -20,10 +20,10 @@ <group value="mtf_migrated"/> </annotations> <before> - <actionGroup ref="LoginAsAdmin" stepKey="LoginAsAdmin"/> + <actionGroup ref="AdminLoginActionGroup" stepKey="LoginAsAdmin"/> </before> <after> - <actionGroup ref="logout" stepKey="logout"/> + <actionGroup ref="AdminLogoutActionGroup" stepKey="logout"/> </after> <actionGroup ref="AdminNavigateMenuActionGroup" stepKey="navigateToReportsOrdersPage"> <argument name="menuUiId" value="{{AdminMenuReports.dataUiId}}"/> diff --git a/app/code/Magento/Reports/Test/Mftf/Test/AdminReportsProductsInCartNavigateMenuTest.xml b/app/code/Magento/Reports/Test/Mftf/Test/AdminReportsProductsInCartNavigateMenuTest.xml index 03877f8e58ecc..0ae7b79f2458d 100644 --- a/app/code/Magento/Reports/Test/Mftf/Test/AdminReportsProductsInCartNavigateMenuTest.xml +++ b/app/code/Magento/Reports/Test/Mftf/Test/AdminReportsProductsInCartNavigateMenuTest.xml @@ -20,10 +20,10 @@ <group value="mtf_migrated"/> </annotations> <before> - <actionGroup ref="LoginAsAdmin" stepKey="LoginAsAdmin"/> + <actionGroup ref="AdminLoginActionGroup" stepKey="LoginAsAdmin"/> </before> <after> - <actionGroup ref="logout" stepKey="logout"/> + <actionGroup ref="AdminLogoutActionGroup" stepKey="logout"/> </after> <actionGroup ref="AdminNavigateMenuActionGroup" stepKey="navigateToReportsProductsInCartPage"> <argument name="menuUiId" value="{{AdminMenuReports.dataUiId}}"/> diff --git a/app/code/Magento/Reports/Test/Mftf/Test/AdminReportsRefreshStatisticsNavigateMenuTest.xml b/app/code/Magento/Reports/Test/Mftf/Test/AdminReportsRefreshStatisticsNavigateMenuTest.xml index d05fc091357df..d8bc9d1a4444a 100644 --- a/app/code/Magento/Reports/Test/Mftf/Test/AdminReportsRefreshStatisticsNavigateMenuTest.xml +++ b/app/code/Magento/Reports/Test/Mftf/Test/AdminReportsRefreshStatisticsNavigateMenuTest.xml @@ -20,10 +20,10 @@ <group value="mtf_migrated"/> </annotations> <before> - <actionGroup ref="LoginAsAdmin" stepKey="LoginAsAdmin"/> + <actionGroup ref="AdminLoginActionGroup" stepKey="LoginAsAdmin"/> </before> <after> - <actionGroup ref="logout" stepKey="logout"/> + <actionGroup ref="AdminLogoutActionGroup" stepKey="logout"/> </after> <actionGroup ref="AdminNavigateMenuActionGroup" stepKey="navigateToReportsRefreshStatisticsPage"> <argument name="menuUiId" value="{{AdminMenuReports.dataUiId}}"/> diff --git a/app/code/Magento/Reports/Test/Mftf/Test/AdminReportsTaxNavigateMenuTest.xml b/app/code/Magento/Reports/Test/Mftf/Test/AdminReportsTaxNavigateMenuTest.xml index 11a065c933a3b..f8091d4f63101 100644 --- a/app/code/Magento/Reports/Test/Mftf/Test/AdminReportsTaxNavigateMenuTest.xml +++ b/app/code/Magento/Reports/Test/Mftf/Test/AdminReportsTaxNavigateMenuTest.xml @@ -20,10 +20,10 @@ <group value="mtf_migrated"/> </annotations> <before> - <actionGroup ref="LoginAsAdmin" stepKey="LoginAsAdmin"/> + <actionGroup ref="AdminLoginActionGroup" stepKey="LoginAsAdmin"/> </before> <after> - <actionGroup ref="logout" stepKey="logout"/> + <actionGroup ref="AdminLogoutActionGroup" stepKey="logout"/> </after> <actionGroup ref="AdminNavigateMenuActionGroup" stepKey="navigateToReportsTaxPage"> <argument name="menuUiId" value="{{AdminMenuReports.dataUiId}}"/> diff --git a/app/code/Magento/Reports/Test/Mftf/Test/AdminReportsViewsNavigateMenuTest.xml b/app/code/Magento/Reports/Test/Mftf/Test/AdminReportsViewsNavigateMenuTest.xml index 9154b96c71e38..0002050bff957 100644 --- a/app/code/Magento/Reports/Test/Mftf/Test/AdminReportsViewsNavigateMenuTest.xml +++ b/app/code/Magento/Reports/Test/Mftf/Test/AdminReportsViewsNavigateMenuTest.xml @@ -20,10 +20,10 @@ <group value="mtf_migrated"/> </annotations> <before> - <actionGroup ref="LoginAsAdmin" stepKey="LoginAsAdmin"/> + <actionGroup ref="AdminLoginActionGroup" stepKey="LoginAsAdmin"/> </before> <after> - <actionGroup ref="logout" stepKey="logout"/> + <actionGroup ref="AdminLogoutActionGroup" stepKey="logout"/> </after> <actionGroup ref="AdminNavigateMenuActionGroup" stepKey="navigateToReportsViewsPage"> <argument name="menuUiId" value="{{AdminMenuReports.dataUiId}}"/> diff --git a/app/code/Magento/Reports/Test/Mftf/Test/CancelOrdersInOrderSalesReportTest.xml b/app/code/Magento/Reports/Test/Mftf/Test/CancelOrdersInOrderSalesReportTest.xml index 324b56757c516..e572febec5a5c 100644 --- a/app/code/Magento/Reports/Test/Mftf/Test/CancelOrdersInOrderSalesReportTest.xml +++ b/app/code/Magento/Reports/Test/Mftf/Test/CancelOrdersInOrderSalesReportTest.xml @@ -21,7 +21,7 @@ <before> <!-- log in as admin --> - <actionGroup ref="LoginAsAdmin" stepKey="loginAsAdmin"/> + <actionGroup ref="AdminLoginActionGroup" stepKey="loginAsAdmin"/> <!-- create new product --> <createData entity="_defaultCategory" stepKey="createCategory"/> @@ -37,7 +37,7 @@ <deleteData createDataKey="createCustomer" stepKey="deleteCustomer"/> <deleteData createDataKey="createCategory" stepKey="deleteCategory"/> <deleteData createDataKey="createSimpleProduct" stepKey="deleteProduct"/> - <actionGroup ref="logout" stepKey="logout"/> + <actionGroup ref="AdminLogoutActionGroup" stepKey="logout"/> </after> @@ -89,6 +89,9 @@ <grabTextFrom selector="{{GeneratedReportSection.ordersCount}}" stepKey="grabCanceledOrdersAny"/> <!-- Compare canceled orders price --> - <assertEquals expected="{$grabCanceledOrdersSpecified}" expectedType="string" actual="{$grabCanceledOrdersAny}" actualType="string" stepKey="assertEquals"/> + <assertEquals stepKey="assertEquals"> + <actualResult type="string">{$grabCanceledOrdersAny}</actualResult> + <expectedResult type="string">{$grabCanceledOrdersSpecified}</expectedResult> + </assertEquals> </test> </tests> diff --git a/app/code/Magento/Reports/Test/Unit/Model/ResourceModel/Product/Sold/Collection/CollectionTest.php b/app/code/Magento/Reports/Test/Unit/Model/ResourceModel/Product/Sold/Collection/CollectionTest.php index c4be25843b128..6e2fd8d847063 100644 --- a/app/code/Magento/Reports/Test/Unit/Model/ResourceModel/Product/Sold/Collection/CollectionTest.php +++ b/app/code/Magento/Reports/Test/Unit/Model/ResourceModel/Product/Sold/Collection/CollectionTest.php @@ -3,52 +3,154 @@ * Copyright © Magento, Inc. All rights reserved. * See COPYING.txt for license details. */ +declare(strict_types=1); namespace Magento\Reports\Test\Unit\Model\ResourceModel\Product\Sold\Collection; +use Magento\Framework\DB\Adapter\AdapterInterface; use Magento\Framework\DB\Select; -use Magento\Framework\TestFramework\Unit\Helper\ObjectManager; use Magento\Reports\Model\ResourceModel\Product\Sold\Collection; +use Magento\Sales\Model\Order; +use PHPUnit\Framework\TestCase; +use PHPUnit_Framework_MockObject_MockObject as MockObject; /** + * Verify data collection class. + * * @SuppressWarnings(PHPMD.CouplingBetweenObjects) */ -class CollectionTest extends \PHPUnit\Framework\TestCase +class CollectionTest extends TestCase { /** - * @var ObjectManager + * @var AdapterInterface|MockObject */ - protected $objectManager; + protected $adapterMock; /** - * @var \PHPUnit_Framework_MockObject_MockObject + * @var Select|MockObject */ protected $selectMock; + /** + * @var Collection; + */ + protected $collection; + + /** + * @inheritDoc + */ protected function setUp() { - $this->objectManager = new ObjectManager($this); $this->selectMock = $this->createMock(Select::class); - } - - public function testGetSelectCountSql() - { - /** @var $collection \PHPUnit_Framework_MockObject_MockObject */ - $collection = $this->getMockBuilder(Collection::class) - ->setMethods(['getSelect']) + $this->adapterMock = $this->createMock(AdapterInterface::class); + $this->collection = $this->getMockBuilder(Collection::class) + ->setMethods([ + 'getSelect', + 'getConnection', + 'getTable' + ]) ->disableOriginalConstructor() ->getMock(); + } - $collection->expects($this->atLeastOnce())->method('getSelect')->willReturn($this->selectMock); - - $this->selectMock->expects($this->atLeastOnce())->method('reset')->willReturnSelf(); - $this->selectMock->expects($this->exactly(2))->method('columns')->willReturnSelf(); + /** + * Verify get select count sql. + * + * @return void + */ + public function testGetSelectCountSql(): void + { + $this->collection->expects($this->atLeastOnce()) + ->method('getSelect') + ->willReturn($this->selectMock); + $this->selectMock->expects($this->atLeastOnce()) + ->method('reset') + ->willReturnSelf(); + $this->selectMock->expects($this->exactly(2)) + ->method('columns') + ->willReturnSelf(); + $this->selectMock->expects($this->at(6)) + ->method('columns') + ->with('COUNT(DISTINCT main_table.entity_id)'); + $this->selectMock->expects($this->at(7)) + ->method('reset') + ->with(Select::COLUMNS); + $this->selectMock->expects($this->at(8)) + ->method('columns') + ->with('COUNT(DISTINCT order_items.item_id)'); - $this->selectMock->expects($this->at(6))->method('columns')->with('COUNT(DISTINCT main_table.entity_id)'); + $this->assertEquals($this->selectMock, $this->collection->getSelectCountSql()); + } - $this->selectMock->expects($this->at(7))->method('reset')->with(Select::COLUMNS); - $this->selectMock->expects($this->at(8))->method('columns')->with('COUNT(DISTINCT order_items.item_id)'); + /** + * Verify add ordered qty. + * + * @return void + */ + public function testAddOrderedQty(): void + { + $this->collection->expects($this->once()) + ->method('getConnection') + ->willReturn($this->adapterMock); + $this->adapterMock->expects($this->once()) + ->method('quoteIdentifier') + ->with('order') + ->willReturn('sales_order'); + $this->adapterMock->expects($this->once()) + ->method('quoteInto') + ->with('sales_order.state <> ?', Order::STATE_CANCELED) + ->willReturn(''); + $this->collection->expects($this->atLeastOnce()) + ->method('getSelect') + ->willReturn($this->selectMock); + $this->collection->expects($this->exactly(2)) + ->method('getTable') + ->withConsecutive( + ['sales_order_item'], + ['sales_order'] + )->willReturnOnConsecutiveCalls( + 'sales_order_item', + 'sales_order' + ); + $this->selectMock->expects($this->atLeastOnce()) + ->method('reset') + ->willReturnSelf(); + $this->selectMock->expects($this->atLeastOnce()) + ->method('from') + ->with( + [ 'order_items' => 'sales_order_item'], + [ + 'ordered_qty' => 'order_items.qty_ordered', + 'order_items_name' => 'order_items.name', + 'order_items_sku' => 'order_items.sku' + ] + ) + ->willReturnSelf(); + $this->selectMock->expects($this->atLeastOnce()) + ->method('joinInner') + ->with( + ['order' => 'sales_order'], + 'sales_order.entity_id = order_items.order_id AND ', + [] + ) + ->willReturnSelf(); + $this->selectMock->expects($this->atLeastOnce()) + ->method('where') + ->with('order_items.parent_item_id IS NULL') + ->willReturnSelf(); + $this->selectMock->expects($this->atLeastOnce()) + ->method('having') + ->with('order_items.qty_ordered > ?', 0) + ->willReturnSelf(); + $this->selectMock->expects($this->atLeastOnce()) + ->method('columns') + ->with('SUM(order_items.qty_ordered) as ordered_qty') + ->willReturnSelf(); + $this->selectMock->expects($this->atLeastOnce()) + ->method('group') + ->with('order_items.sku') + ->willReturnSelf(); - $this->assertEquals($this->selectMock, $collection->getSelectCountSql()); + $this->assertEquals($this->collection, $this->collection->addOrderedQty()); } } diff --git a/app/code/Magento/Reports/Test/Unit/Model/ResourceModel/Review/Customer/CollectionTest.php b/app/code/Magento/Reports/Test/Unit/Model/ResourceModel/Review/Customer/CollectionTest.php new file mode 100644 index 0000000000000..2d22d0b4aa4d3 --- /dev/null +++ b/app/code/Magento/Reports/Test/Unit/Model/ResourceModel/Review/Customer/CollectionTest.php @@ -0,0 +1,83 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ + +namespace Magento\Reports\Test\Unit\Model\ResourceModel\Review\Customer; + +use Magento\Framework\DB\Select; +use Magento\Framework\TestFramework\Unit\Helper\ObjectManager; +use Magento\Reports\Model\ResourceModel\Review\Customer\Collection; + +/** + * Review product collection test + */ +class CollectionTest extends \PHPUnit\Framework\TestCase +{ + /** + * @var ObjectManager + */ + private $objectManager; + + /** + * @var \PHPUnit_Framework_MockObject_MockObject + */ + private $selectMock; + + /** + * @var \PHPUnit_Framework_MockObject_MockObject + */ + private $collectionMock; + + protected function setUp() + { + $this->objectManager = new ObjectManager($this); + $this->selectMock = $this->createMock(Select::class); + $this->collectionMock = $this->getMockBuilder(Collection::class) + ->setMethods(['getSelect']) + ->disableOriginalConstructor() + ->getMock(); + $this->collectionMock->expects($this->atLeastOnce())->method('getSelect')->willReturn($this->selectMock); + } + + public function testGetSelectCountSqlWithoutHavingClauses() + { + $havingClauses = []; + $whereClauses = []; + $this->selectMock->expects($this->atLeastOnce())->method('getPart')->willReturn($havingClauses); + $this->selectMock->expects($this->atLeastOnce())->method('getPart')->willReturn($whereClauses); + $this->selectMock->expects($this->at(2))->method('reset')->with(Select::ORDER); + $this->selectMock->expects($this->at(3))->method('reset')->with(Select::LIMIT_COUNT); + $this->selectMock->expects($this->at(4))->method('reset')->with(Select::LIMIT_OFFSET); + $this->selectMock->expects($this->at(5))->method('reset')->with(Select::WHERE); + $this->selectMock->expects($this->at(6))->method('reset')->with(Select::HAVING); + $this->selectMock->expects($this->atLeastOnce())->method('columns') + ->with(new \Zend_Db_Expr('COUNT(DISTINCT detail.customer_id)'))->willReturnSelf(); + $this->selectMock->expects($this->atLeastOnce())->method('reset')->willReturnSelf(); + $this->selectMock->expects($this->atLeastOnce())->method('from')->willReturnSelf(); + + $this->assertEquals($this->selectMock, $this->collectionMock->getSelectCountSql()); + } + + public function testGetSelectCountSqlWithHavingClauses() + { + $havingClauses = [ + 'clause-1' => '(review_cnt LIKE %4%)', + 'clause-2' => '(avg_rating LIKE %55.00%)' + ]; + $whereClauses = [ + 'customer name LIKE %test%' + ]; + + $this->selectMock->expects($this->atLeastOnce())->method('getPart')->willReturn($havingClauses); + $this->selectMock->expects($this->atLeastOnce())->method('getPart')->willReturn($whereClauses); + $this->selectMock->expects($this->at(2))->method('reset')->with(Select::ORDER); + $this->selectMock->expects($this->at(3))->method('reset')->with(Select::LIMIT_COUNT); + $this->selectMock->expects($this->at(4))->method('reset')->with(Select::LIMIT_OFFSET); + $this->selectMock->expects($this->atLeastOnce())->method('reset')->willReturnSelf(); + $this->selectMock->expects($this->atLeastOnce())->method('from')->willReturnSelf(); + + $this->assertEquals($this->selectMock, $this->collectionMock->getSelectCountSql()); + } +} diff --git a/app/code/Magento/Reports/Test/Unit/Model/ResourceModel/Review/Product/CollectionTest.php b/app/code/Magento/Reports/Test/Unit/Model/ResourceModel/Review/Product/CollectionTest.php new file mode 100644 index 0000000000000..d2c22d972e24a --- /dev/null +++ b/app/code/Magento/Reports/Test/Unit/Model/ResourceModel/Review/Product/CollectionTest.php @@ -0,0 +1,79 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ + +namespace Magento\Reports\Test\Unit\Model\ResourceModel\Review\Product; + +use Magento\Framework\DB\Select; +use Magento\Framework\TestFramework\Unit\Helper\ObjectManager; +use Magento\Reports\Model\ResourceModel\Review\Product\Collection; + +/** + * Review product collection test + */ +class CollectionTest extends \PHPUnit\Framework\TestCase +{ + /** + * @var ObjectManager + */ + private $objectManager; + + /** + * @var \PHPUnit_Framework_MockObject_MockObject + */ + private $selectMock; + + /** + * @var \PHPUnit_Framework_MockObject_MockObject + */ + private $collectionMock; + + protected function setUp() + { + $this->objectManager = new ObjectManager($this); + $this->selectMock = $this->createMock(Select::class); + $this->collectionMock = $this->getMockBuilder(Collection::class) + ->setMethods(['getSelect']) + ->disableOriginalConstructor() + ->getMock(); + $this->collectionMock->expects($this->atLeastOnce())->method('getSelect')->willReturn($this->selectMock); + } + + public function testGetSelectCountSqlWithoutHavingClauses() + { + $havingClauses = []; + $this->selectMock->expects($this->atLeastOnce())->method('getPart')->willReturn($havingClauses); + $this->selectMock->expects($this->at(1))->method('reset')->with(Select::ORDER); + $this->selectMock->expects($this->at(2))->method('reset')->with(Select::LIMIT_COUNT); + $this->selectMock->expects($this->at(3))->method('reset')->with(Select::LIMIT_OFFSET); + $this->selectMock->expects($this->atLeastOnce())->method('columns') + ->with(new \Zend_Db_Expr('1'))->willReturnSelf(); + $this->selectMock->expects($this->atLeastOnce())->method('resetJoinLeft')->willReturnSelf(); + + $this->selectMock->expects($this->at(4))->method('reset')->with(Select::COLUMNS); + $this->selectMock->expects($this->at(5))->method('reset')->with(Select::HAVING); + $this->selectMock->expects($this->atLeastOnce())->method('reset')->willReturnSelf(); + $this->selectMock->expects($this->atLeastOnce())->method('from')->willReturnSelf(); + + $this->assertEquals($this->selectMock, $this->collectionMock->getSelectCountSql()); + } + + public function testGetSelectCountSqlWithHavingClauses() + { + $havingClauses = [ + 'clause-1' => '(review_cnt LIKE %4%)', + 'clause-2' => '(avg_rating LIKE %55.00%)' + ]; + + $this->selectMock->expects($this->atLeastOnce())->method('getPart')->willReturn($havingClauses); + $this->selectMock->expects($this->at(1))->method('reset')->with(Select::ORDER); + $this->selectMock->expects($this->at(2))->method('reset')->with(Select::LIMIT_COUNT); + $this->selectMock->expects($this->at(3))->method('reset')->with(Select::LIMIT_OFFSET); + $this->selectMock->expects($this->atLeastOnce())->method('reset')->willReturnSelf(); + $this->selectMock->expects($this->atLeastOnce())->method('from')->willReturnSelf(); + + $this->assertEquals($this->selectMock, $this->collectionMock->getSelectCountSql()); + } +} diff --git a/app/code/Magento/Reports/Test/Unit/Observer/CatalogProductCompareClearObserverTest.php b/app/code/Magento/Reports/Test/Unit/Observer/CatalogProductCompareClearObserverTest.php new file mode 100644 index 0000000000000..018ab943ef9ed --- /dev/null +++ b/app/code/Magento/Reports/Test/Unit/Observer/CatalogProductCompareClearObserverTest.php @@ -0,0 +1,143 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ + +declare(strict_types=1); + +namespace Magento\Reports\Test\Unit\Observer; + +use Magento\Framework\Event\Observer; +use Magento\Framework\TestFramework\Unit\Helper\ObjectManager; +use Magento\Reports\Model\Event; +use Magento\Reports\Model\Product\Index\Compared; +use Magento\Reports\Model\Product\Index\ComparedFactory; +use Magento\Reports\Model\ReportStatus; +use Magento\Reports\Observer\CatalogProductCompareClearObserver; +use PHPUnit\Framework\MockObject\MockObject; +use PHPUnit\Framework\TestCase; + +/** + * Unit test for Magento\Reports\Test\Unit\Observer\CatalogProductCompareClearObserver + */ +class CatalogProductCompareClearObserverTest extends TestCase +{ + /** + * Testable Object + * + * @var CatalogProductCompareClearObserver + */ + private $observer; + + /** + * @var ObjectManager + */ + private $objectManager; + + /** + * @var Observer|MockObject + */ + private $observerMock; + + /** + * @var ReportStatus|MockObject + */ + private $reportStatusMock; + + /** + * @var ComparedFactory|MockObject + */ + private $productCompFactoryMock; + + /** + * @var Compared|MockObject + */ + private $productCompModelMock; + + /** + * @var Event|MockObject + */ + private $reportEventMock; + + /** + * @inheritDoc + */ + protected function setUp(): void + { + $this->objectManager = new ObjectManager($this); + $this->observerMock = $this->createMock(Observer::class); + + $this->reportStatusMock = $this->getMockBuilder(ReportStatus::class) + ->disableOriginalConstructor() + ->setMethods(['isReportEnabled']) + ->getMock(); + + $this->reportEventMock = $this->getMockBuilder(Event::class) + ->disableOriginalConstructor() + ->getMock(); + + $this->productCompFactoryMock = $this->getMockBuilder(ComparedFactory::class) + ->disableOriginalConstructor() + ->setMethods(['create']) + ->getMock(); + + $this->productCompModelMock = $this->getMockBuilder(Compared::class) + ->disableOriginalConstructor() + ->getMock(); + + $this->observer = $this->objectManager->getObject( + CatalogProductCompareClearObserver::class, + [ + 'reportStatus' => $this->reportStatusMock, + 'productCompFactory' => $this->productCompFactoryMock + ] + ); + } + + /** + * Test for execute(), covers test case for remove all products from compare products + */ + public function testExecuteRemoveProducts(): void + { + $this->reportStatusMock + ->expects($this->once()) + ->method('isReportEnabled') + ->with(Event::EVENT_PRODUCT_VIEW) + ->willReturn(true); + + $this->productCompFactoryMock + ->expects($this->once()) + ->method('create') + ->willReturn($this->productCompModelMock); + + $this->productCompModelMock + ->expects($this->once()) + ->method('calculate') + ->willReturnSelf(); + + $this->observer->execute($this->observerMock); + } + + /** + * Test for execute(), covers test case for remove all products from compare products with report disabled + */ + public function testExecuteRemoveProductsWithReportDisable(): void + { + $this->reportStatusMock + ->expects($this->once()) + ->method('isReportEnabled') + ->with(Event::EVENT_PRODUCT_VIEW) + ->willReturn(false); + + $this->productCompFactoryMock + ->expects($this->never()) + ->method('create'); + + $this->productCompModelMock + ->expects($this->never()) + ->method('calculate'); + + $this->observer->execute($this->observerMock); + } +} diff --git a/app/code/Magento/Reports/Test/Unit/Observer/CheckoutCartAddProductObserverTest.php b/app/code/Magento/Reports/Test/Unit/Observer/CheckoutCartAddProductObserverTest.php new file mode 100644 index 0000000000000..d9a75c96d4304 --- /dev/null +++ b/app/code/Magento/Reports/Test/Unit/Observer/CheckoutCartAddProductObserverTest.php @@ -0,0 +1,164 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ + +declare(strict_types=1); + +namespace Magento\Reports\Test\Unit\Observer; + +use Magento\Framework\Event; +use Magento\Framework\Event\Observer; +use Magento\Framework\TestFramework\Unit\Helper\ObjectManager; +use Magento\Quote\Model\Quote\Item as QuoteItem; +use Magento\Reports\Model\Event as ReportsEventModel; +use Magento\Reports\Model\ReportStatus; +use Magento\Reports\Observer\CheckoutCartAddProductObserver; +use Magento\Reports\Observer\EventSaver; +use PHPUnit\Framework\MockObject\MockObject; +use PHPUnit\Framework\TestCase; + +/** + * Unit Test for \Magento\Reports\Observer\CheckoutCartAddProductObserver + */ +class CheckoutCartAddProductObserverTest extends TestCase +{ + const STUB_QUOTE_PARENT_ITEM_ID = 1; + const STUB_QUOTE_ITEM_ID = 2; + + /** + * @var MockObject|EventSaver + */ + private $eventSaverMock; + + /** + * @var MockObject|ReportStatus + */ + private $reportStatusMock; + + /** + * @var MockObject|Observer + */ + private $eventObserverMock; + + /** + * @var MockObject|Event + */ + private $eventMock; + + /** + * @var MockObject|QuoteItem + */ + private $quoteItemMock; + + /** + * @var CheckoutCartAddProductObserver + */ + private $observer; + + /** + * @inheritDoc + */ + protected function setUp() + { + $this->eventSaverMock = $this->createMock(EventSaver::class); + $this->reportStatusMock = $this->createMock(ReportStatus::class); + $this->eventObserverMock = $this->createMock(Observer::class); + $this->quoteItemMock = $this->createMock(QuoteItem::class); + $this->eventMock = $this->getMockBuilder(Event::class) + ->disableOriginalConstructor() + ->setMethods(['getItem']) + ->getMock(); + + $objectManager = new ObjectManager($this); + $this->observer = $objectManager->getObject( + CheckoutCartAddProductObserver::class, + [ + 'eventSaver' => $this->eventSaverMock, + 'reportStatus' => $this->reportStatusMock + ] + ); + } + + /** + * The case when event has to be successfully saved + */ + public function testExecuteExpectsSaveCalledWhenNewProductAdded() + { + $this->configureMocksWhenReportsEnabled(); + $this->quoteItemMock->expects($this->once()) + ->method('getId') + ->willReturn(null); + $this->quoteItemMock->expects($this->once()) + ->method('getParentItem') + ->willReturn(null); + + $this->eventSaverMock->expects($this->once())->method('save'); + $this->observer->execute($this->eventObserverMock); + } + + /** + * Test the method when 'Product Added To Cart' Report is disabled in configuration + */ + public function testExecuteWhenReportsDisabled() + { + $this->reportStatusMock->expects($this->once()) + ->method('isReportEnabled') + ->with(ReportsEventModel::EVENT_PRODUCT_TO_CART) + ->willReturn(false); + + $this->checkOriginalMethodIsNeverExecuted(); + } + + /** + * Test when Quote Item has Id + */ + public function testExecuteWithQuoteItemIdSet() + { + $this->configureMocksWhenReportsEnabled(); + $this->quoteItemMock->expects($this->any()) + ->method('getId') + ->willReturn(self::STUB_QUOTE_ITEM_ID); + + $this->checkOriginalMethodIsNeverExecuted(); + } + + /** + * Test when Quote Item has Parent Item set + */ + public function testExecuteWithQuoteParentItemIdSet() + { + $this->configureMocksWhenReportsEnabled(); + $this->quoteItemMock->expects($this->any()) + ->method('getParentItem') + ->willReturn(self::STUB_QUOTE_PARENT_ITEM_ID); + + $this->checkOriginalMethodIsNeverExecuted(); + } + + /** + * Common mocks assertions when Report is enabled in configuration + */ + private function configureMocksWhenReportsEnabled() + { + $this->reportStatusMock->expects($this->once()) + ->method('isReportEnabled') + ->willReturn(true); + $this->eventObserverMock->expects($this->once()) + ->method('getEvent') + ->willReturn($this->eventMock); + $this->eventMock->expects($this->once()) + ->method('getItem') + ->willReturn($this->quoteItemMock); + } + + /** + * Checking that the method will be never executed + */ + private function checkOriginalMethodIsNeverExecuted() + { + $this->eventSaverMock->expects($this->never())->method('save'); + $this->observer->execute($this->eventObserverMock); + } +} diff --git a/app/code/Magento/Reports/Test/Unit/Observer/EventSaverTest.php b/app/code/Magento/Reports/Test/Unit/Observer/EventSaverTest.php new file mode 100644 index 0000000000000..07e9eef40f4de --- /dev/null +++ b/app/code/Magento/Reports/Test/Unit/Observer/EventSaverTest.php @@ -0,0 +1,151 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ + +declare(strict_types=1); + +namespace Magento\Reports\Test\Unit\Observer; + +use Magento\Customer\Model\Session; +use Magento\Customer\Model\Visitor; +use Magento\Framework\TestFramework\Unit\Helper\ObjectManager; +use Magento\Reports\Model\Event; +use Magento\Reports\Model\EventFactory; +use Magento\Reports\Observer\EventSaver; +use Magento\Store\Api\Data\StoreInterface; +use Magento\Store\Model\StoreManagerInterface; +use PHPUnit\Framework\MockObject\MockObject; +use PHPUnit\Framework\TestCase; + +/** + * Unit Test for @see EventSaver + */ +class EventSaverTest extends TestCase +{ + const STUB_CUSTOMER_ID = 1; + const STUB_VISITOR_ID = 2; + const STUB_EVENT_TYPE_ID = 1; + const STUB_OBJECT_ID = 1; + const STUB_STORE_ID = 1; + + /** + * @var Session|MockObject + */ + private $customerSessionMock; + + /** + * @var Visitor|MockObject + */ + private $customerVisitorMock; + + /** + * @var StoreInterface|MockObject + */ + private $storeMock; + + /** + * @var StoreManagerInterface|MockObject + */ + private $storeManagerMock; + + /** + * @var EventFactory|MockObject + */ + private $eventFactoryMock; + + /** + * @var Event|MockObject + */ + private $eventMock; + + /** + * @var EventSaver + */ + private $eventSaver; + + /** + * @inheritDoc + */ + public function setUp() + { + $this->customerSessionMock = $this->createMock(Session::class); + $this->customerVisitorMock = $this->createMock(Visitor::class); + + $this->storeMock = $this->createMock(StoreInterface::class); + $this->storeManagerMock = $this->createMock(StoreManagerInterface::class); + $this->storeManagerMock->expects($this->once()) + ->method('getStore') + ->willReturn($this->storeMock); + $this->storeMock->expects($this->once()) + ->method('getId') + ->willReturn(self::STUB_STORE_ID); + + $this->eventMock = $this->createMock(Event::class); + $this->eventFactoryMock = $this->createMock(EventFactory::class); + $this->eventFactoryMock->expects($this->once()) + ->method('create') + ->willReturn($this->eventMock); + $this->eventMock->expects($this->once()) + ->method('setData') + ->willReturnSelf(); + $this->eventMock->expects($this->once()) + ->method('save') + ->willReturnSelf(); + + $objectManagerHelper = new ObjectManager($this); + $this->eventSaver = $objectManagerHelper->getObject( + EventSaver::class, + [ + '_storeManager' => $this->storeManagerMock, + '_eventFactory' => $this->eventFactoryMock, + '_customerSession' => $this->customerSessionMock, + '_customerVisitor' => $this->customerVisitorMock + ] + ); + } + + /** + * Test save with subject ID provided + */ + public function testSaveWithSubject() + { + $subjectId = 5; + $this->customerSessionMock->expects($this->never()) + ->method('isLoggedIn'); + $this->customerSessionMock->expects($this->never()) + ->method('getCustomerId'); + $this->customerVisitorMock->expects($this->never()) + ->method('getId'); + $this->eventSaver->save(self::STUB_EVENT_TYPE_ID, self::STUB_OBJECT_ID, $subjectId); + } + + /** + * Test save with no subject ID provided and customer is logged in + */ + public function testSaveWithoutSubjectWhenLoggedIn() + { + $this->customerSessionMock->expects($this->once()) + ->method('isLoggedIn') + ->willReturn(true); + $this->customerSessionMock->expects($this->once()) + ->method('getCustomerId') + ->willReturn(self::STUB_CUSTOMER_ID); + $this->eventSaver->save(self::STUB_EVENT_TYPE_ID, self::STUB_OBJECT_ID, null); + } + + /** + * Test save with no subject ID provided and customer is not logged in + */ + public function testSaveWithoutSubjectForGuest() + { + $this->customerSessionMock->expects($this->once()) + ->method('isLoggedIn') + ->willReturn(false); + $this->customerVisitorMock->expects($this->once()) + ->method('getId') + ->willReturn(self::STUB_VISITOR_ID); + $this->eventSaver->save(self::STUB_EVENT_TYPE_ID, self::STUB_OBJECT_ID, null); + } +} diff --git a/app/code/Magento/Reports/Test/Unit/Observer/SendfriendProductObserverTest.php b/app/code/Magento/Reports/Test/Unit/Observer/SendfriendProductObserverTest.php new file mode 100644 index 0000000000000..88c30ec5bc236 --- /dev/null +++ b/app/code/Magento/Reports/Test/Unit/Observer/SendfriendProductObserverTest.php @@ -0,0 +1,93 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ + +declare(strict_types=1); + +namespace Magento\Reports\Test\Unit\Observer; + +use Magento\Catalog\Api\Data\ProductInterface; +use Magento\Framework\Event\Observer; +use Magento\Framework\TestFramework\Unit\Helper\ObjectManager as ObjectManagerHelper; +use Magento\Reports\Model\Event; +use Magento\Reports\Model\ReportStatus; +use Magento\Reports\Observer\EventSaver; +use Magento\Reports\Observer\SendfriendProductObserver; +use PHPUnit\Framework\MockObject\MockObject; +use PHPUnit\Framework\TestCase; + +/** + * Unit test for \Magento\Reports\Observer\SendfriendProductObserver + */ +class SendfriendProductObserverTest extends TestCase +{ + /** + * @var Observer|MockObject + */ + private $eventObserverMock; + + /** + * @var EventSaver|MockObject + */ + private $eventSaverMock; + + /** + * @var ReportStatus|MockObject + */ + private $reportStatusMock; + + /** + * @var SendfriendProductObserver + */ + private $observer; + + /** + * @inheritDoc + */ + protected function setUp() + { + $this->eventObserverMock = $this->createMock(Observer::class); + $this->eventSaverMock = $this->createMock(EventSaver::class); + $this->reportStatusMock = $this->createMock(ReportStatus::class); + + $this->observer = (new ObjectManagerHelper($this))->getObject( + SendfriendProductObserver::class, + ['eventSaver' => $this->eventSaverMock, 'reportStatus' => $this->reportStatusMock] + ); + } + + /** + * Test case when report is disabled in config. + */ + public function testExecuteWhenReportIsDisabled() + { + $this->reportStatusMock->expects($this->once()) + ->method('isReportEnabled') + ->with(Event::EVENT_PRODUCT_SEND) + ->willReturn(false); + + $this->eventSaverMock->expects($this->never())->method('save'); + $this->observer->execute($this->eventObserverMock); + } + + /** + * Test case when report is enabled in config. + */ + public function testExecuteWhenReportIsEnabled() + { + $eventMock = $this->createPartialMock(Event::class, ['getProduct']); + $eventMock->expects($this->once()) + ->method('getProduct') + ->willReturn($this->createMock(ProductInterface::class)); + $this->reportStatusMock->expects($this->once()) + ->method('isReportEnabled') + ->with(Event::EVENT_PRODUCT_SEND) + ->willReturn(true); + $this->eventObserverMock->expects($this->once())->method('getEvent')->willReturn($eventMock); + + $this->eventSaverMock->expects($this->once())->method('save'); + $this->observer->execute($this->eventObserverMock); + } +} diff --git a/app/code/Magento/Reports/Test/Unit/Observer/WishlistAddProductObserverTest.php b/app/code/Magento/Reports/Test/Unit/Observer/WishlistAddProductObserverTest.php new file mode 100644 index 0000000000000..5b282bb60ece2 --- /dev/null +++ b/app/code/Magento/Reports/Test/Unit/Observer/WishlistAddProductObserverTest.php @@ -0,0 +1,93 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ + +declare(strict_types=1); + +namespace Magento\Reports\Test\Unit\Observer; + +use Magento\Catalog\Api\Data\ProductInterface; +use Magento\Framework\Event\Observer; +use Magento\Framework\TestFramework\Unit\Helper\ObjectManager as ObjectManagerHelper; +use Magento\Reports\Model\Event; +use Magento\Reports\Model\ReportStatus; +use Magento\Reports\Observer\EventSaver; +use Magento\Reports\Observer\WishlistAddProductObserver; +use PHPUnit\Framework\MockObject\MockObject; +use PHPUnit\Framework\TestCase; + +/** + * Unit test for \Magento\Reports\Observer\WishlistAddProductObserver + */ +class WishlistAddProductObserverTest extends TestCase +{ + /** + * @var Observer|MockObject + */ + private $eventObserverMock; + + /** + * @var EventSaver|MockObject + */ + private $eventSaverMock; + + /** + * @var ReportStatus|MockObject + */ + private $reportStatusMock; + + /** + * @var WishlistAddProductObserver + */ + private $observer; + + /** + * @inheritDoc + */ + protected function setUp() + { + $this->eventObserverMock = $this->createMock(Observer::class); + $this->eventSaverMock = $this->createMock(EventSaver::class); + $this->reportStatusMock = $this->createMock(ReportStatus::class); + + $this->observer = (new ObjectManagerHelper($this))->getObject( + WishlistAddProductObserver::class, + ['eventSaver' => $this->eventSaverMock, 'reportStatus' => $this->reportStatusMock] + ); + } + + /** + * Test case when report is disabled in config. + */ + public function testExecuteWhenReportIsDisabled() + { + $this->reportStatusMock->expects($this->once()) + ->method('isReportEnabled') + ->with(Event::EVENT_PRODUCT_TO_WISHLIST) + ->willReturn(false); + $this->eventSaverMock->expects($this->never())->method('save'); + $this->observer->execute($this->eventObserverMock); + } + + /** + * Test case when report is enabled in config. + */ + public function testExecuteWhenReportIsEnabled() + { + $this->reportStatusMock->expects($this->once()) + ->method('isReportEnabled') + ->with(Event::EVENT_PRODUCT_TO_WISHLIST) + ->willReturn(true); + + $eventMock = $this->createPartialMock(Event::class, ['getProduct']); + $eventMock->expects($this->once()) + ->method('getProduct') + ->willReturn($this->createMock(ProductInterface::class)); + $this->eventObserverMock->expects($this->once())->method('getEvent')->willReturn($eventMock); + + $this->eventSaverMock->expects($this->once())->method('save'); + $this->observer->execute($this->eventObserverMock); + } +} diff --git a/app/code/Magento/Reports/Test/Unit/Observer/WishlistShareObserverTest.php b/app/code/Magento/Reports/Test/Unit/Observer/WishlistShareObserverTest.php new file mode 100644 index 0000000000000..d432fe013b138 --- /dev/null +++ b/app/code/Magento/Reports/Test/Unit/Observer/WishlistShareObserverTest.php @@ -0,0 +1,93 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ + +declare(strict_types=1); + +namespace Magento\Reports\Test\Unit\Observer; + +use Magento\Framework\Event\Observer; +use Magento\Framework\TestFramework\Unit\Helper\ObjectManager as ObjectManagerHelper; +use Magento\Reports\Model\Event; +use Magento\Reports\Model\ReportStatus; +use Magento\Reports\Observer\EventSaver; +use Magento\Reports\Observer\WishlistShareObserver; +use Magento\Wishlist\Model\Wishlist; +use PHPUnit\Framework\MockObject\MockObject; +use PHPUnit\Framework\TestCase; + +/** + * Unit test for \Magento\Reports\Observer\WishlistShareObserver + */ +class WishlistShareObserverTest extends TestCase +{ + /** + * @var Observer|MockObject + */ + private $eventObserverMock; + + /** + * @var EventSaver|MockObject + */ + private $eventSaverMock; + + /** + * @var ReportStatus|MockObject + */ + private $reportStatusMock; + + /** + * @var WishlistShareObserver + */ + private $observer; + + /** + * @inheritDoc + */ + protected function setUp() + { + $this->eventObserverMock = $this->createMock(Observer::class); + $this->eventSaverMock = $this->createMock(EventSaver::class); + $this->reportStatusMock = $this->createMock(ReportStatus::class); + + $this->observer = (new ObjectManagerHelper($this))->getObject( + WishlistShareObserver::class, + ['eventSaver' => $this->eventSaverMock, 'reportStatus' => $this->reportStatusMock] + ); + } + + /** + * Test case when report is disabled in config. + */ + public function testExecuteWhenReportIsDisabled() + { + $this->reportStatusMock->expects($this->once()) + ->method('isReportEnabled') + ->with(Event::EVENT_WISHLIST_SHARE) + ->willReturn(false); + $this->eventSaverMock->expects($this->never())->method('save'); + $this->observer->execute($this->eventObserverMock); + } + + /** + * Test case when report is enabled in config. + */ + public function testExecuteWhenReportIsEnabled() + { + $this->reportStatusMock->expects($this->once()) + ->method('isReportEnabled') + ->with(Event::EVENT_WISHLIST_SHARE) + ->willReturn(true); + + $eventMock = $this->createPartialMock(Event::class, ['getWishlist']); + $eventMock->expects($this->once()) + ->method('getWishlist') + ->willReturn($this->createMock(Wishlist::class)); + $this->eventObserverMock->expects($this->once())->method('getEvent')->willReturn($eventMock); + + $this->eventSaverMock->expects($this->once())->method('save'); + $this->observer->execute($this->eventObserverMock); + } +} diff --git a/app/code/Magento/Reports/etc/adminhtml/system.xml b/app/code/Magento/Reports/etc/adminhtml/system.xml index 40eba9f3a6bb2..66507afda5b3b 100644 --- a/app/code/Magento/Reports/etc/adminhtml/system.xml +++ b/app/code/Magento/Reports/etc/adminhtml/system.xml @@ -8,9 +8,9 @@ <config xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="urn:magento:module:Magento_Config:etc/system_file.xsd"> <system> <section id="catalog"> - <group id="recently_products" translate="label" type="text" sortOrder="350" showInDefault="1" showInWebsite="1" showInStore="0"> + <group id="recently_products" translate="label" type="text" sortOrder="350" showInDefault="1" showInWebsite="1"> <label>Recently Viewed/Compared Products</label> - <field id="scope" translate="label" type="select" sortOrder="1" showInDefault="1" showInWebsite="1" showInStore="0" canRestore="1"> + <field id="scope" translate="label" type="select" sortOrder="1" showInDefault="1" showInWebsite="1" canRestore="1"> <label>Show for Current</label> <source_model>Magento\Config\Model\Config\Source\Reports\Scope</source_model> <hide_in_single_store_mode>1</hide_in_single_store_mode> @@ -25,30 +25,30 @@ </field> </group> </section> - <section id="reports" translate="label" type="text" sortOrder="1000" showInDefault="1" showInWebsite="0" showInStore="0"> + <section id="reports" translate="label" type="text" sortOrder="1000" showInDefault="1"> <label>Reports</label> <tab>general</tab> <resource>Magento_Reports::reports</resource> - <group id="dashboard" translate="label" type="text" sortOrder="1" showInDefault="1" showInWebsite="0" showInStore="0"> + <group id="dashboard" translate="label" type="text" sortOrder="1" showInDefault="1"> <label>Dashboard</label> - <field id="ytd_start" translate="label" type="select" sortOrder="1" showInDefault="1" showInWebsite="0" showInStore="0" canRestore="1"> + <field id="ytd_start" translate="label" type="select" sortOrder="1" showInDefault="1" canRestore="1"> <label>Year-To-Date Starts</label> <frontend_model>Magento\Reports\Block\Adminhtml\Config\Form\Field\YtdStart</frontend_model> </field> - <field id="mtd_start" translate="label comment" type="select" sortOrder="2" showInDefault="1" showInWebsite="0" showInStore="0" canRestore="1"> + <field id="mtd_start" translate="label comment" type="select" sortOrder="2" showInDefault="1" canRestore="1"> <label>Current Month Starts</label> <frontend_model>Magento\Reports\Block\Adminhtml\Config\Form\Field\MtdStart</frontend_model> <comment>Select day of the month.</comment> </field> </group> - <group id="options" translate="label" type="text" sortOrder="1" showInDefault="1" showInWebsite="0" showInStore="0"> + <group id="options" translate="label" type="text" sortOrder="1" showInDefault="1"> <label>General Options</label> - <field id="enabled" translate="label comment" type="select" sortOrder="1" showInDefault="1" showInWebsite="0" showInStore="0"> + <field id="enabled" translate="label comment" type="select" sortOrder="1" showInDefault="1" canRestore="1"> <label>Enable Reports</label> <source_model>Magento\Config\Model\Config\Source\Yesno</source_model> <comment>If disabled, all report events will be disabled</comment> </field> - <field id="product_view_enabled" translate="label comment" type="select" sortOrder="1" showInDefault="1" showInWebsite="0" showInStore="0"> + <field id="product_view_enabled" translate="label comment" type="select" sortOrder="1" showInDefault="1"> <label>Enable "Product View" Report</label> <comment>If enabled, will collect statistic of viewed product pages</comment> <source_model>Magento\Config\Model\Config\Source\Yesno</source_model> @@ -56,7 +56,7 @@ <field id="enabled">1</field> </depends> </field> - <field id="product_send_enabled" translate="label comment" type="select" sortOrder="2" showInDefault="1" showInWebsite="0" showInStore="0"> + <field id="product_send_enabled" translate="label comment" type="select" sortOrder="2" showInDefault="1"> <label>Enable "Send Product Link To Friend" Report</label> <comment>If enabled, will collect statistic of product links sent to friend</comment> <source_model>Magento\Config\Model\Config\Source\Yesno</source_model> @@ -64,7 +64,7 @@ <field id="enabled">1</field> </depends> </field> - <field id="product_compare_enabled" translate="label comment" type="select" sortOrder="3" showInDefault="1" showInWebsite="0" showInStore="0"> + <field id="product_compare_enabled" translate="label comment" type="select" sortOrder="3" showInDefault="1"> <label>Enable "Add Product To Compare List" Report</label> <comment>If enabled, will collect statistic of products added to Compare List</comment> <source_model>Magento\Config\Model\Config\Source\Yesno</source_model> @@ -72,7 +72,7 @@ <field id="enabled">1</field> </depends> </field> - <field id="product_to_cart_enabled" translate="label comment" type="select" sortOrder="4" showInDefault="1" showInWebsite="0" showInStore="0"> + <field id="product_to_cart_enabled" translate="label comment" type="select" sortOrder="4" showInDefault="1"> <label>Enable "Product Added To Cart" Report</label> <comment>If enabled, will collect statistic of products added to Cart</comment> <source_model>Magento\Config\Model\Config\Source\Yesno</source_model> @@ -80,7 +80,7 @@ <field id="enabled">1</field> </depends> </field> - <field id="product_to_wishlist_enabled" translate="label comment" type="select" sortOrder="5" showInDefault="1" showInWebsite="0" showInStore="0"> + <field id="product_to_wishlist_enabled" translate="label comment" type="select" sortOrder="5" showInDefault="1"> <label>Enable "Product Added To WishList" Report</label> <comment>If enabled, will collect statistic of products added to WishList</comment> <source_model>Magento\Config\Model\Config\Source\Yesno</source_model> @@ -88,7 +88,7 @@ <field id="enabled">1</field> </depends> </field> - <field id="wishlist_share_enabled" translate="label comment" type="select" sortOrder="6" showInDefault="1" showInWebsite="0" showInStore="0"> + <field id="wishlist_share_enabled" translate="label comment" type="select" sortOrder="6" showInDefault="1"> <label>Enable "Share WishList" Report</label> <comment>If enabled, will collect statistic of shared WishLists</comment> <source_model>Magento\Config\Model\Config\Source\Yesno</source_model> diff --git a/app/code/Magento/Reports/etc/db_schema_whitelist.json b/app/code/Magento/Reports/etc/db_schema_whitelist.json index 8f55ef4fdbee6..ea65c73ab789b 100644 --- a/app/code/Magento/Reports/etc/db_schema_whitelist.json +++ b/app/code/Magento/Reports/etc/db_schema_whitelist.json @@ -149,4 +149,4 @@ "REPORT_VIEWED_PRD_AGGRED_YEARLY_PRD_ID_SEQUENCE_PRD_SEQUENCE_VAL": true } } -} \ No newline at end of file +} diff --git a/app/code/Magento/Reports/registration.php b/app/code/Magento/Reports/registration.php index f903b78961992..6ab949d92db73 100644 --- a/app/code/Magento/Reports/registration.php +++ b/app/code/Magento/Reports/registration.php @@ -4,6 +4,6 @@ * See COPYING.txt for license details. */ -use \Magento\Framework\Component\ComponentRegistrar; +use Magento\Framework\Component\ComponentRegistrar; ComponentRegistrar::register(ComponentRegistrar::MODULE, 'Magento_Reports', __DIR__); diff --git a/app/code/Magento/RequireJs/registration.php b/app/code/Magento/RequireJs/registration.php index 8e2104a874ad4..4920ac99bbe96 100644 --- a/app/code/Magento/RequireJs/registration.php +++ b/app/code/Magento/RequireJs/registration.php @@ -4,6 +4,6 @@ * See COPYING.txt for license details. */ -use \Magento\Framework\Component\ComponentRegistrar; +use Magento\Framework\Component\ComponentRegistrar; ComponentRegistrar::register(ComponentRegistrar::MODULE, 'Magento_RequireJs', __DIR__); diff --git a/app/code/Magento/Review/Block/Adminhtml/Edit/Tab/Reviews.php b/app/code/Magento/Review/Block/Adminhtml/Edit/Tab/Reviews.php new file mode 100644 index 0000000000000..15d41fad0a595 --- /dev/null +++ b/app/code/Magento/Review/Block/Adminhtml/Edit/Tab/Reviews.php @@ -0,0 +1,38 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +declare(strict_types=1); + +namespace Magento\Review\Block\Adminhtml\Edit\Tab; + +use Magento\Review\Block\Adminhtml\Grid; + +/** + * Review tab in adminhtml area. + * + * @api + */ +class Reviews extends Grid +{ + /** + * Hide grid mass action elements. + * + * @return Reviews + */ + protected function _prepareMassaction() + { + return $this; + } + + /** + * Determine ajax url for grid refresh + * + * @return string + */ + public function getGridUrl() + { + return $this->getUrl('review/customer/productReviews', ['_current' => true]); + } +} diff --git a/app/code/Magento/Review/Block/Adminhtml/ReviewTab.php b/app/code/Magento/Review/Block/Adminhtml/ReviewTab.php index f62f8e2aa0d4c..9533be4acda54 100644 --- a/app/code/Magento/Review/Block/Adminhtml/ReviewTab.php +++ b/app/code/Magento/Review/Block/Adminhtml/ReviewTab.php @@ -11,9 +11,7 @@ use Magento\Ui\Component\Layout\Tabs\TabWrapper; /** - * Class ReviewTab - * - * @package Magento\Review\Block\Adminhtml + * Review tab block. */ class ReviewTab extends TabWrapper { @@ -30,8 +28,6 @@ class ReviewTab extends TabWrapper protected $isAjaxLoaded = true; /** - * Constructor - * * @param Context $context * @param Registry $registry * @param array $data @@ -67,6 +63,6 @@ public function getTabLabel() */ public function getTabUrl() { - return $this->getUrl('customer/*/productReviews', ['_current' => true]); + return $this->getUrl('review/customer/productReviews', ['_current' => true]); } } diff --git a/app/code/Magento/Review/Block/Product/View.php b/app/code/Magento/Review/Block/Product/View.php index c7b813ea8eed9..c66e3e50b919b 100644 --- a/app/code/Magento/Review/Block/Product/View.php +++ b/app/code/Magento/Review/Block/Product/View.php @@ -82,13 +82,20 @@ public function __construct( */ protected function _toHtml() { - $this->getProduct()->setShortDescription(null); + $product = $this->getProduct(); + + if (!$product) { + return ''; + } + + $product->setShortDescription(null); return parent::_toHtml(); } /** * Replace review summary html with more detailed review summary + * * Reviews collection count will be jerked here * * @param \Magento\Catalog\Model\Product $product diff --git a/app/code/Magento/Review/Block/Product/View/ListView.php b/app/code/Magento/Review/Block/Product/View/ListView.php index 5df8a3698e537..2d3d1f6637f1f 100644 --- a/app/code/Magento/Review/Block/Product/View/ListView.php +++ b/app/code/Magento/Review/Block/Product/View/ListView.php @@ -55,7 +55,10 @@ protected function _prepareLayout() */ protected function _beforeToHtml() { - $this->getReviewsCollection()->load()->addRateVotes(); + if ($this->getProductId()) { + $this->getReviewsCollection()->load()->addRateVotes(); + } + return parent::_beforeToHtml(); } diff --git a/app/code/Magento/Review/Controller/Adminhtml/Customer/ProductReviews.php b/app/code/Magento/Review/Controller/Adminhtml/Customer/ProductReviews.php new file mode 100644 index 0000000000000..3ab49154a47a7 --- /dev/null +++ b/app/code/Magento/Review/Controller/Adminhtml/Customer/ProductReviews.php @@ -0,0 +1,57 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +declare(strict_types=1); + +namespace Magento\Review\Controller\Adminhtml\Customer; + +use Magento\Backend\App\Action; +use Magento\Backend\App\Action\Context; +use Magento\Customer\Model\CustomerIdProvider; +use Magento\Framework\App\Action\HttpPostActionInterface; +use Magento\Framework\Controller\ResultFactory; +use Magento\Framework\View\Result\Layout; + +/** + * Customer product reviews page. + */ +class ProductReviews extends Action implements HttpPostActionInterface +{ + /** + * Authorization level of a basic admin session + * + * @see _isAllowed() + */ + const ADMIN_RESOURCE = 'Magento_Review::reviews_all'; + + /** @var CustomerIdProvider */ + private $customerIdProvider; + + /** + * @param Context $context + * @param CustomerIdProvider $customerIdProvider + */ + public function __construct(Context $context, CustomerIdProvider $customerIdProvider) + { + $this->customerIdProvider = $customerIdProvider; + parent::__construct($context); + } + + /** + * Get customer's product reviews list. + * + * @return Layout + */ + public function execute() + { + $customerId = $this->customerIdProvider->getCustomerId(); + /** @var \Magento\Framework\View\Result\Layout $resultLayout */ + $resultLayout = $this->resultFactory->create(ResultFactory::TYPE_LAYOUT); + $block = $resultLayout->getLayout()->getBlock('admin.customer.reviews'); + $block->setCustomerId($customerId)->setUseAjax(true); + + return $resultLayout; + } +} diff --git a/app/code/Magento/Review/Controller/Adminhtml/Product/Pending.php b/app/code/Magento/Review/Controller/Adminhtml/Product/Pending.php index 385b7e12bf32a..6e5e64b25985c 100644 --- a/app/code/Magento/Review/Controller/Adminhtml/Product/Pending.php +++ b/app/code/Magento/Review/Controller/Adminhtml/Product/Pending.php @@ -31,6 +31,7 @@ public function execute() } /** @var \Magento\Backend\Model\View\Result\Page $resultPage */ $resultPage = $this->resultFactory->create(ResultFactory::TYPE_PAGE); + $resultPage->setActiveMenu('Magento_Review::catalog_reviews_ratings_pending'); $resultPage->getConfig()->getTitle()->prepend(__('Customer Reviews')); $resultPage->getConfig()->getTitle()->prepend(__('Pending Reviews')); $this->coreRegistry->register('usePendingFilter', true); diff --git a/app/code/Magento/Review/Model/ResourceModel/Review/Summary.php b/app/code/Magento/Review/Model/ResourceModel/Review/Summary.php index f18bc2094930a..e7597f7c313e4 100644 --- a/app/code/Magento/Review/Model/ResourceModel/Review/Summary.php +++ b/app/code/Magento/Review/Model/ResourceModel/Review/Summary.php @@ -79,14 +79,14 @@ public function reAggregate($summary) * Append review summary fields to product collection * * @param \Magento\Catalog\Model\ResourceModel\Product\Collection $productCollection - * @param string $storeId + * @param int $storeId * @param string $entityCode * @return Summary * @throws \Magento\Framework\Exception\LocalizedException */ public function appendSummaryFieldsToCollection( \Magento\Catalog\Model\ResourceModel\Product\Collection $productCollection, - string $storeId, + int $storeId, string $entityCode ) { if (!$productCollection->isLoaded()) { diff --git a/app/code/Magento/Review/Observer/CatalogProductListCollectionAppendSummaryFieldsObserver.php b/app/code/Magento/Review/Observer/CatalogProductListCollectionAppendSummaryFieldsObserver.php index bb69284b5f0b8..4ee5a5e559fab 100644 --- a/app/code/Magento/Review/Observer/CatalogProductListCollectionAppendSummaryFieldsObserver.php +++ b/app/code/Magento/Review/Observer/CatalogProductListCollectionAppendSummaryFieldsObserver.php @@ -53,7 +53,7 @@ public function execute(EventObserver $observer) $productCollection = $observer->getEvent()->getCollection(); $this->sumResourceFactory->create()->appendSummaryFieldsToCollection( $productCollection, - $this->storeManager->getStore()->getId(), + (int)$this->storeManager->getStore()->getId(), \Magento\Review\Model\Review::ENTITY_PRODUCT_CODE ); diff --git a/app/code/Magento/Review/Test/Mftf/ActionGroup/AdminAddProductReviewActionGroup.xml b/app/code/Magento/Review/Test/Mftf/ActionGroup/AdminAddProductReviewActionGroup.xml new file mode 100644 index 0000000000000..9316194dcc78b --- /dev/null +++ b/app/code/Magento/Review/Test/Mftf/ActionGroup/AdminAddProductReviewActionGroup.xml @@ -0,0 +1,29 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + /** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +--> +<actionGroups xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/actionGroupSchema.xsd"> + <actionGroup name="AdminAddProductReviewActionGroup"> + <arguments> + <argument name="review" type="entity" defaultValue="simpleProductReview"/> + <argument name="sku" type="string"/> + </arguments> + <!--Click on Add New Review --> + <click selector="{{AdminCreateNewReviewSection.addNewReviewButton}}" stepKey="clickOnNewReview"/> + <waitForElementVisible selector="{{AdminCreateNewReviewSection.addNewReviewBySKU(sku)}}" stepKey="waitForVisibleReviewButton"/> + <!--Select Product by SKU and Create Review --> + <click selector="{{AdminCreateNewReviewSection.addNewReviewBySKU(sku)}}" stepKey="addNewReviewBySKU"/> + <waitForElementVisible selector="{{AdminCreateNewReviewSection.select_stores}}" stepKey="waitForVisibleReviewDetails"/> + <selectOption selector="{{AdminCreateNewReviewSection.select_stores}}" userInput="{{review.select_stores[0]}}" stepKey="visibilityField"/> + <fillField selector="{{AdminCreateNewReviewSection.nickname}}" userInput="{{review.nickname}}" stepKey="fillNicknameField"/> + <fillField selector="{{AdminCreateNewReviewSection.title}}" userInput="{{review.title}}" stepKey="fillSummaryField"/> + <fillField selector="{{AdminCreateNewReviewSection.detail}}" userInput="{{review.detail}}" stepKey="fillReviewField"/> + <click selector="{{AdminCreateNewReviewSection.submitReview}}" stepKey="clickSubmitReview"/> + <waitForElementVisible selector="{{AdminCreateNewReviewSection.SuccessMessage}}" stepKey="waitForSuccessMessage"/> + <see selector="{{AdminCreateNewReviewSection.SuccessMessage}}" userInput="You saved the review." stepKey="seeSuccessMessage"/> + </actionGroup> +</actionGroups> diff --git a/app/code/Magento/Review/Test/Mftf/ActionGroup/AdminAssertClassElementRelativeIdActionGroup.xml b/app/code/Magento/Review/Test/Mftf/ActionGroup/AdminAssertClassElementRelativeIdActionGroup.xml new file mode 100644 index 0000000000000..63013ea79b834 --- /dev/null +++ b/app/code/Magento/Review/Test/Mftf/ActionGroup/AdminAssertClassElementRelativeIdActionGroup.xml @@ -0,0 +1,21 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + /** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +--> +<actionGroups xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/actionGroupSchema.xsd"> + <actionGroup name="AdminAssertClassElementRelativeIdActionGroup"> + <arguments> + <argument name="elementId" type="string" defaultValue="{{AdminMenuSection.marketing}}"/> + <argument name="expectClass" type="string" defaultValue="{{AdminMenuUserContentPendingReviews.activeClass}}" /> + </arguments> + + <grabAttributeFrom selector="{{elementId}}" userInput="class" stepKey="grabClass"/> + <assertContains stepKey="assertClass"> + <actualResult type="string">{$grabClass}</actualResult> + <expectedResult type="string">{{expectClass}}</expectedResult> + </assertContains> + </actionGroup> +</actionGroups> diff --git a/app/code/Magento/Review/Test/Mftf/ActionGroup/AdminFilterCustomerReviewActionGroup.xml b/app/code/Magento/Review/Test/Mftf/ActionGroup/AdminFilterCustomerReviewActionGroup.xml new file mode 100644 index 0000000000000..971ec2dae2072 --- /dev/null +++ b/app/code/Magento/Review/Test/Mftf/ActionGroup/AdminFilterCustomerReviewActionGroup.xml @@ -0,0 +1,21 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + /** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +--> +<actionGroups xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/actionGroupSchema.xsd"> + <actionGroup name="AdminFilterCustomerReviewActionGroup"> + <arguments> + <argument name="reviewCount" type="string" defaultValue="0"/> + </arguments> + <!--Sort Review Column in Grid --> + <waitForPageLoad stepKey="waitForGridToAppear"/> + <fillField userInput="{{reviewCount}}" selector="{{AdminCreateNewReviewSection.gridCustomer_filter_review_cnt}}" stepKey="searchReview"/> + <click selector="{{AdminCreateNewReviewSection.CustomerSearchButton}}" stepKey="startSearch"/> + <waitForPageLoad stepKey="waitForResults"/> + <see userInput="{{reviewCount}}" selector="{{AdminCreateNewReviewSection.gridReviewColumn}}" stepKey="assertReviewColumn"/> + </actionGroup> +</actionGroups> diff --git a/app/code/Magento/Review/Test/Mftf/ActionGroup/AdminFilterProductReviewActionGroup.xml b/app/code/Magento/Review/Test/Mftf/ActionGroup/AdminFilterProductReviewActionGroup.xml new file mode 100644 index 0000000000000..daab520025477 --- /dev/null +++ b/app/code/Magento/Review/Test/Mftf/ActionGroup/AdminFilterProductReviewActionGroup.xml @@ -0,0 +1,21 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + /** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +--> +<actionGroups xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/actionGroupSchema.xsd"> + <actionGroup name="AdminFilterProductReviewActionGroup"> + <arguments> + <argument name="reviewCount" type="string"/> + </arguments> + <!--Sort Review Column in Grid --> + <waitForPageLoad stepKey="waitForGridToAppear"/> + <fillField userInput="{{reviewCount}}" selector="{{AdminCreateNewReviewSection.gridProducts_filter_review_cnt}}" stepKey="searchReview"/> + <click selector="{{AdminCreateNewReviewSection.searchButton}}" stepKey="startSearch"/> + <waitForPageLoad stepKey="waitForResults"/> + <see userInput="{{reviewCount}}" selector="{{AdminCreateNewReviewSection.gridReviewColumn}}" stepKey="assertReviewColumn"/> + </actionGroup> +</actionGroups> diff --git a/app/code/Magento/Review/Test/Mftf/Data/AdminMenuData.xml b/app/code/Magento/Review/Test/Mftf/Data/AdminMenuData.xml index 89882707f5ebd..faff1e1f42932 100644 --- a/app/code/Magento/Review/Test/Mftf/Data/AdminMenuData.xml +++ b/app/code/Magento/Review/Test/Mftf/Data/AdminMenuData.xml @@ -13,6 +13,12 @@ <data key="title">Reviews</data> <data key="dataUiId">magento-review-catalog-reviews-ratings-reviews-all</data> </entity> + <entity name="AdminMenuUserContentPendingReviews"> + <data key="pageTitle">Pending Reviews</data> + <data key="title">Pending Reviews</data> + <data key="dataUiId">magento-review-catalog-reviews-ratings-pending</data> + <data key="dataActiveClass">_active</data> + </entity> <entity name="AdminMenuReportsReviewsByCustomers"> <data key="pageTitle">Customer Reviews Report</data> <data key="title">By Customers</data> diff --git a/app/code/Magento/Review/Test/Mftf/Data/ProductReviewData.xml b/app/code/Magento/Review/Test/Mftf/Data/ProductReviewData.xml index 89758328efd54..f66decd1b7bd0 100644 --- a/app/code/Magento/Review/Test/Mftf/Data/ProductReviewData.xml +++ b/app/code/Magento/Review/Test/Mftf/Data/ProductReviewData.xml @@ -12,5 +12,8 @@ <data key="nickname" unique="suffix">user</data> <data key="title">Review title</data> <data key="detail">Simple product review</data> + <array key="select_stores"> + <item>Default Store View</item> + </array> </entity> </entities> diff --git a/app/code/Magento/Review/Test/Mftf/Page/AdminProductReviewPage.xml b/app/code/Magento/Review/Test/Mftf/Page/AdminProductReviewPage.xml new file mode 100644 index 0000000000000..131fc5e82d944 --- /dev/null +++ b/app/code/Magento/Review/Test/Mftf/Page/AdminProductReviewPage.xml @@ -0,0 +1,14 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + /** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +--> + +<pages xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Page/etc/PageObject.xsd"> + <page name="AdminProductReviewPage" url="review/product/new/{{productId}}/" area="admin" module="Review" parameterized="true"> + <section name="AdminCreateNewReviewSection"/> + </page> +</pages> diff --git a/app/code/Magento/Review/Test/Mftf/Section/AdminCreateNewReviewSection.xml b/app/code/Magento/Review/Test/Mftf/Section/AdminCreateNewReviewSection.xml new file mode 100644 index 0000000000000..3b17b20e9da1b --- /dev/null +++ b/app/code/Magento/Review/Test/Mftf/Section/AdminCreateNewReviewSection.xml @@ -0,0 +1,26 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + /** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +--> + +<sections xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Page/etc/SectionObject.xsd"> + <section name="AdminCreateNewReviewSection"> + <element name="addNewReviewButton" type="button" selector="#add" timeout="30"/> + <element name="addNewReviewBySKU" type="text" selector="//td[contains(@class,'col-sku')][contains(text(),'{{sku}}')]" parameterized="true"/> + <element name="select_stores" type="text" selector="#select_stores"/> + <element name="nickname" type="text" selector="#nickname"/> + <element name="title" type="text" selector="#title"/> + <element name="detail" type="textarea" selector="#detail"/> + <element name="submitReview" type="button" selector="#save_button"/> + <element name="SuccessMessage" type="button" selector="div.message-success"/> + <element name="gridProducts_filter_review_cnt" type="button" selector="#gridProducts_filter_review_cnt"/> + <element name="searchButton" type="button" selector="//*[@id='gridProducts']//button[contains(@title, 'Search')]"/> + <element name="gridReviewColumn" type="text" selector="//tbody//td[@data-column='review_cnt']"/> + <element name="gridCustomer_filter_review_cnt" type="button" selector="#customers_grid_filter_review_cnt"/> + <element name="CustomerSearchButton" type="button" selector="//*[@id='customers_grid']//button[contains(@title, 'Search')]"/> + </section> +</sections> diff --git a/app/code/Magento/Review/Test/Mftf/Test/AdminMarketingPendingReviewsNavigateMenuActiveTest.xml b/app/code/Magento/Review/Test/Mftf/Test/AdminMarketingPendingReviewsNavigateMenuActiveTest.xml new file mode 100644 index 0000000000000..5ff1ed0a1062b --- /dev/null +++ b/app/code/Magento/Review/Test/Mftf/Test/AdminMarketingPendingReviewsNavigateMenuActiveTest.xml @@ -0,0 +1,36 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + /** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +--> + +<tests xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/testSchema.xsd"> + <test name="AdminMarketingPendingReviewsNavigateMenuActiveTest"> + <annotations> + <features value="Pending Reviews"/> + <stories value="Menu Navigation"/> + <title value="Admin marketing pending reviews navigate menu"/> + <description value="Admin able see navigate head menu Marketing is active, when open page Marketing > Pending Reviews"/> + <group value="menu"/> + </annotations> + <before> + <actionGroup ref="AdminLoginActionGroup" stepKey="LoginAsAdmin"/> + </before> + <after> + <actionGroup ref="AdminLogoutActionGroup" stepKey="logout"/> + </after> + <actionGroup ref="AdminNavigateMenuActionGroup" stepKey="navigateToReportsViewsPage"> + <argument name="menuUiId" value="{{AdminMenuMarketing.dataUiId}}"/> + <argument name="submenuUiId" value="{{AdminMenuUserContentPendingReviews.dataUiId}}"/> + </actionGroup> + <actionGroup ref="AdminAssertPageTitleActionGroup" stepKey="seePageTitle"> + <argument name="title" value="{{AdminMenuUserContentPendingReviews.pageTitle}}"/> + </actionGroup> + <actionGroup ref="AdminAssertClassElementRelativeIdActionGroup" stepKey="assertClass"> + <argument name="elementId" value="{{AdminMenuSection.marketing}}"/> + <argument name="expectClass" value="{{AdminMenuUserContentPendingReviews.dataActiveClass}}"/> + </actionGroup> + </test> +</tests> diff --git a/app/code/Magento/Review/Test/Mftf/Test/AdminMarketingReviewsNavigateMenuTest.xml b/app/code/Magento/Review/Test/Mftf/Test/AdminMarketingReviewsNavigateMenuTest.xml index fade220d22100..5198949a61024 100644 --- a/app/code/Magento/Review/Test/Mftf/Test/AdminMarketingReviewsNavigateMenuTest.xml +++ b/app/code/Magento/Review/Test/Mftf/Test/AdminMarketingReviewsNavigateMenuTest.xml @@ -20,10 +20,10 @@ <group value="mtf_migrated"/> </annotations> <before> - <actionGroup ref="LoginAsAdmin" stepKey="LoginAsAdmin"/> + <actionGroup ref="AdminLoginActionGroup" stepKey="LoginAsAdmin"/> </before> <after> - <actionGroup ref="logout" stepKey="logout"/> + <actionGroup ref="AdminLogoutActionGroup" stepKey="logout"/> </after> <actionGroup ref="AdminNavigateMenuActionGroup" stepKey="navigateToReportsViewsPage"> <argument name="menuUiId" value="{{AdminMenuMarketing.dataUiId}}"/> diff --git a/app/code/Magento/Review/Test/Mftf/Test/AdminReportsByCustomersNavigateMenuTest.xml b/app/code/Magento/Review/Test/Mftf/Test/AdminReportsByCustomersNavigateMenuTest.xml index 58492424e76f7..c6ef740f69e8f 100644 --- a/app/code/Magento/Review/Test/Mftf/Test/AdminReportsByCustomersNavigateMenuTest.xml +++ b/app/code/Magento/Review/Test/Mftf/Test/AdminReportsByCustomersNavigateMenuTest.xml @@ -20,10 +20,10 @@ <group value="mtf_migrated"/> </annotations> <before> - <actionGroup ref="LoginAsAdmin" stepKey="LoginAsAdmin"/> + <actionGroup ref="AdminLoginActionGroup" stepKey="LoginAsAdmin"/> </before> <after> - <actionGroup ref="logout" stepKey="logout"/> + <actionGroup ref="AdminLogoutActionGroup" stepKey="logout"/> </after> <actionGroup ref="AdminNavigateMenuActionGroup" stepKey="navigateToReportsByCustomersPage"> <argument name="menuUiId" value="{{AdminMenuReports.dataUiId}}"/> diff --git a/app/code/Magento/Review/Test/Mftf/Test/AdminReportsByProductsNavigateMenuTest.xml b/app/code/Magento/Review/Test/Mftf/Test/AdminReportsByProductsNavigateMenuTest.xml index e848aa4f22023..97d0498c5fd22 100644 --- a/app/code/Magento/Review/Test/Mftf/Test/AdminReportsByProductsNavigateMenuTest.xml +++ b/app/code/Magento/Review/Test/Mftf/Test/AdminReportsByProductsNavigateMenuTest.xml @@ -20,10 +20,10 @@ <group value="mtf_migrated"/> </annotations> <before> - <actionGroup ref="LoginAsAdmin" stepKey="LoginAsAdmin"/> + <actionGroup ref="AdminLoginActionGroup" stepKey="LoginAsAdmin"/> </before> <after> - <actionGroup ref="logout" stepKey="logout"/> + <actionGroup ref="AdminLogoutActionGroup" stepKey="logout"/> </after> <actionGroup ref="AdminNavigateMenuActionGroup" stepKey="navigateToReportsByProductsPage"> <argument name="menuUiId" value="{{AdminMenuReports.dataUiId}}"/> diff --git a/app/code/Magento/Review/Test/Mftf/Test/AdminReviewsByProductsReportTest.xml b/app/code/Magento/Review/Test/Mftf/Test/AdminReviewsByProductsReportTest.xml new file mode 100644 index 0000000000000..0809ad0fa8541 --- /dev/null +++ b/app/code/Magento/Review/Test/Mftf/Test/AdminReviewsByProductsReportTest.xml @@ -0,0 +1,64 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + /** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +--> + +<tests xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/testSchema.xsd"> + <test name="AdminReviewsByProductsReportTest"> + <annotations> + <features value="Review"/> + <stories value="Review By Products"/> + <title value="Admin Reports Review by Products"/> + <description value="Review By Products Grid Filters"/> + <severity value="AVERAGE"/> + <testCaseId value="MC-32333"/> + </annotations> + <before> + <!--Login--> + <actionGroup ref="AdminLoginActionGroup" stepKey="LoginAsAdmin"/> + <!--Create product and Category--> + <createData stepKey="category" entity="SimpleSubCategory"/> + <createData stepKey="createProduct1" entity="SimpleProduct"> + <requiredEntity createDataKey="category"/> + </createData> + <createData stepKey="createProduct2" entity="SimpleProduct"> + <requiredEntity createDataKey="category"/> + </createData> + </before> + <after> + <!-- Delete reviews --> + <actionGroup ref="AdminOpenReviewsPageActionGroup" stepKey="openAllReviewsPage"/> + <actionGroup ref="AdminDeleteReviewsByUserNicknameActionGroup" stepKey="deleteCustomerReview"/> + <!--delete Category and Products --> + <deleteData createDataKey="createProduct1" stepKey="deleteProduct1"/> + <deleteData createDataKey="createProduct2" stepKey="deleteProduct2"/> + <deleteData createDataKey="category" stepKey="deleteCategory"/> + <!--Logout--> + <actionGroup ref="AdminLogoutActionGroup" stepKey="logout"/> + </after> + <!-- Navigate to Marketing > User Content> All Review --> + <amOnPage url="{{AdminReviewsPage.url}}" stepKey="openReviewsPage"/> + <waitForPageLoad time="30" stepKey="waitForPageLoadCreatedReviewOne"/> + <actionGroup ref="AdminAddProductReviewActionGroup" stepKey="addFirstReview"> + <argument name="sku" value="$$createProduct1.sku$$"/> + </actionGroup> + <waitForPageLoad time="30" stepKey="waitForPageLoadCreatedReviewTwo"/> + <actionGroup ref="AdminAddProductReviewActionGroup" stepKey="addSecondReview"> + <argument name="sku" value="$$createProduct2.sku$$"/> + </actionGroup> + <!-- Navigate to Reports > Reviews >By Products --> + <actionGroup ref="AdminNavigateMenuActionGroup" stepKey="navigateToReportsByProductsPage"> + <argument name="menuUiId" value="{{AdminMenuReports.dataUiId}}"/> + <argument name="submenuUiId" value="{{AdminMenuReportsReviewsByProducts.dataUiId}}"/> + </actionGroup> + <!--Sort Review Column --> + <grabTextFrom selector="{{AdminCreateNewReviewSection.gridReviewColumn}}" stepKey="grabReviewQuantity"/> + <actionGroup ref="AdminFilterProductReviewActionGroup" stepKey="navigateToReportsReview"> + <argument name="reviewCount" value="$grabReviewQuantity"/> + </actionGroup> + </test> +</tests> diff --git a/app/code/Magento/Review/Test/Mftf/Test/AdminStoresRatingNavigateMenuTest.xml b/app/code/Magento/Review/Test/Mftf/Test/AdminStoresRatingNavigateMenuTest.xml index 511ed5439dc3d..ea03f557962ad 100644 --- a/app/code/Magento/Review/Test/Mftf/Test/AdminStoresRatingNavigateMenuTest.xml +++ b/app/code/Magento/Review/Test/Mftf/Test/AdminStoresRatingNavigateMenuTest.xml @@ -20,10 +20,10 @@ <group value="mtf_migrated"/> </annotations> <before> - <actionGroup ref="LoginAsAdmin" stepKey="LoginAsAdmin"/> + <actionGroup ref="AdminLoginActionGroup" stepKey="LoginAsAdmin"/> </before> <after> - <actionGroup ref="logout" stepKey="logout"/> + <actionGroup ref="AdminLogoutActionGroup" stepKey="logout"/> </after> <actionGroup ref="AdminNavigateMenuActionGroup" stepKey="navigateToStoresRatingPage"> <argument name="menuUiId" value="{{AdminMenuStores.dataUiId}}"/> diff --git a/app/code/Magento/Review/Test/Mftf/Test/AdminVerifyNewRatingFormSingleStoreModeNoTest.xml b/app/code/Magento/Review/Test/Mftf/Test/AdminVerifyNewRatingFormSingleStoreModeNoTest.xml index 77789dd172bdd..e9a08a3e196f5 100644 --- a/app/code/Magento/Review/Test/Mftf/Test/AdminVerifyNewRatingFormSingleStoreModeNoTest.xml +++ b/app/code/Magento/Review/Test/Mftf/Test/AdminVerifyNewRatingFormSingleStoreModeNoTest.xml @@ -20,10 +20,10 @@ </annotations> <before> <magentoCLI command="config:set general/single_store_mode/enabled 0" stepKey="enabledSingleStoreMode"/> - <actionGroup ref="LoginAsAdmin" stepKey="LoginAsAdmin"/> + <actionGroup ref="AdminLoginActionGroup" stepKey="LoginAsAdmin"/> </before> <after> - <actionGroup ref="logout" stepKey="logout"/> + <actionGroup ref="AdminLogoutActionGroup" stepKey="logout"/> </after> <actionGroup ref="AdminNavigateToNewRatingFormActionGroup" stepKey="navigateToNewRatingPage" /> <actionGroup ref="AdminAssertStoreViewRatingTitleWhenSingleStoreModeIsNoActionGroup" stepKey="verifyForm" /> diff --git a/app/code/Magento/Review/Test/Mftf/Test/AdminVerifyNewRatingFormSingleStoreModeYesTest.xml b/app/code/Magento/Review/Test/Mftf/Test/AdminVerifyNewRatingFormSingleStoreModeYesTest.xml index e5368e9192c98..55e709f08d77d 100644 --- a/app/code/Magento/Review/Test/Mftf/Test/AdminVerifyNewRatingFormSingleStoreModeYesTest.xml +++ b/app/code/Magento/Review/Test/Mftf/Test/AdminVerifyNewRatingFormSingleStoreModeYesTest.xml @@ -20,11 +20,11 @@ </annotations> <before> <magentoCLI command="config:set general/single_store_mode/enabled 1" stepKey="enabledSingleStoreMode"/> - <actionGroup ref="LoginAsAdmin" stepKey="LoginAsAdmin"/> + <actionGroup ref="AdminLoginActionGroup" stepKey="LoginAsAdmin"/> </before> <after> <magentoCLI command="config:set general/single_store_mode/enabled 0" stepKey="enabledSingleStoreMode"/> - <actionGroup ref="logout" stepKey="logout"/> + <actionGroup ref="AdminLogoutActionGroup" stepKey="logout"/> </after> <actionGroup ref="AdminNavigateToNewRatingFormActionGroup" stepKey="navigateToNewRatingPage" /> <actionGroup ref="AdminAssertStoreViewRatingTitleWhenSingleStoreModeIsYesActionGroup" stepKey="verifyForm" /> diff --git a/app/code/Magento/Review/Test/Mftf/Test/StoreFrontReviewByCustomerReportTest.xml b/app/code/Magento/Review/Test/Mftf/Test/StoreFrontReviewByCustomerReportTest.xml new file mode 100644 index 0000000000000..b70000ed3f3b0 --- /dev/null +++ b/app/code/Magento/Review/Test/Mftf/Test/StoreFrontReviewByCustomerReportTest.xml @@ -0,0 +1,76 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + /** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +--> + +<tests xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/testSchema.xsd"> + <test name="StoreFrontReviewByCustomerReportTest"> + <annotations> + <features value="Review"/> + <stories value="Review By Customers"/> + <title value="StoreFront Reports Review By Customers"/> + <description value="Review By Customer Grid Filters"/> + <severity value="AVERAGE"/> + <testCaseId value="MC-32340"/> + </annotations> + <before> + <!--Login--> + <actionGroup ref="AdminLoginActionGroup" stepKey="LoginAsAdmin"/> + <!--Create product and Category--> + <createData stepKey="category" entity="SimpleSubCategory"/> + <createData stepKey="createProduct1" entity="SimpleProduct"> + <requiredEntity createDataKey="category"/> + </createData> + <createData stepKey="createProduct2" entity="SimpleProduct"> + <requiredEntity createDataKey="category"/> + </createData> + </before> + <after> + <!-- Delete reviews --> + <actionGroup ref="AdminOpenReviewsPageActionGroup" stepKey="openAllReviewsPage"/> + <actionGroup ref="AdminDeleteReviewsByUserNicknameActionGroup" stepKey="deleteCustomerReview"/> + <actionGroup ref="ClearFiltersAdminDataGridActionGroup" stepKey="clearNickNameReviewFilters"/> + <!-- Delete customer --> + <actionGroup ref="AdminDeleteCustomerActionGroup" stepKey="deleteCustomer"> + <argument name="customerEmail" value="CustomerEntityOne.email"/> + </actionGroup> + <!--delete Category and Products --> + <deleteData createDataKey="createProduct1" stepKey="deleteProduct1"/> + <deleteData createDataKey="createProduct2" stepKey="deleteProduct2"/> + <deleteData createDataKey="category" stepKey="deleteCategory"/> + <!--Logout--> + <actionGroup ref="AdminLogoutActionGroup" stepKey="logout"/> + </after> + <!-- Go to frontend and make a user account and login with it --> + <actionGroup ref="SignUpNewUserFromStorefrontActionGroup" stepKey="signUpNewUser"> + <argument name="Customer" value="CustomerEntityOne"/> + </actionGroup> + <!-- Go to the product view page --> + <actionGroup ref="StorefrontOpenProductPageActionGroup" stepKey="openStorefrontProductPage"> + <argument name="productUrl" value="$$createProduct1.custom_attributes[url_key]$$"/> + </actionGroup> + <!-- Click on reviews and add reviews with current user --> + <actionGroup ref="StorefrontAddProductReviewActionGroup" stepKey="addReview"/> + <!-- Go to Pending reviews page and clear filters --> + <actionGroup ref="AdminOpenPendingReviewsPageActionGroup" stepKey="openReviewsPage"/> + <actionGroup ref="ClearFiltersAdminDataGridActionGroup" stepKey="clearFilters"/> + <!-- Moderate first product reviews: change review status from pending to approved, save --> + <actionGroup ref="AdminOpenReviewByUserNicknameActionGroup" stepKey="openFirstCustomerReviews"/> + <actionGroup ref="AdminChangeReviewStatusActionGroup" stepKey="changeFirstReviewStatus"/> + <actionGroup ref="AdminSaveReviewActionGroup" stepKey="saveModeratedFirstReview"/> + <!-- Navigate to Reports > Reviews >By Customers --> + <actionGroup ref="AdminNavigateMenuActionGroup" stepKey="navigateToReportsByCustomersPage"> + <argument name="menuUiId" value="{{AdminMenuReports.dataUiId}}"/> + <argument name="submenuUiId" value="{{AdminMenuReportsReviewsByCustomers.dataUiId}}"/> + </actionGroup> + <!--Sort Review Column --> + <grabTextFrom selector="{{AdminCreateNewReviewSection.gridReviewColumn}}" stepKey="grabCustomerReviewQuantity"/> + <actionGroup ref="AdminFilterCustomerReviewActionGroup" stepKey="navigateToCustomerReportsReview"> + <argument name="reviewCount" value="$grabCustomerReviewQuantity"/> + </actionGroup> + </test> +</tests> diff --git a/app/code/Magento/Review/Test/Mftf/Test/StorefrontNoJavascriptErrorOnAddYourReviewClickTest.xml b/app/code/Magento/Review/Test/Mftf/Test/StorefrontNoJavascriptErrorOnAddYourReviewClickTest.xml new file mode 100644 index 0000000000000..81f5db6d2fd10 --- /dev/null +++ b/app/code/Magento/Review/Test/Mftf/Test/StorefrontNoJavascriptErrorOnAddYourReviewClickTest.xml @@ -0,0 +1,51 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + /** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +--> + +<tests xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/testSchema.xsd"> + <test name="StorefrontNoJavascriptErrorOnAddYourReviewClickTest"> + <annotations> + <features value="Review"/> + <stories value="Storefront Add Review"/> + <title value="Storefront no javascript error on 'Add Your Review' click test"/> + <description value="Verify no javascript error occurs when customer clicks 'Add Your Review' link"/> + <severity value="MAJOR"/> + <group value="review"/> + </annotations> + <before> + <actionGroup ref="AdminLoginActionGroup" stepKey="loginAsAdmin"/> + <createData entity="textProductAttribute" stepKey="createProductAttribute"/> + <createData entity="CatalogAttributeSet" stepKey="createAttributeSet"/> + <amOnPage url="{{AdminProductAttributeSetEditPage.url}}/$$createAttributeSet.attribute_set_id$$/" + stepKey="onAttributeSetEdit"/> + <actionGroup ref="AssignAttributeToGroupActionGroup" stepKey="assignAttributeToGroup"> + <argument name="group" value="Product Details"/> + <argument name="attribute" value="$$createProductAttribute.attribute_code$$"/> + </actionGroup> + <actionGroup ref="SaveAttributeSetActionGroup" stepKey="SaveAttributeSet"/> + <createData entity="_defaultCategory" stepKey="createCategory"/> + <createData entity="SimpleProductWithCustomAttributeSet" stepKey="createProduct"> + <requiredEntity createDataKey="createCategory"/> + <requiredEntity createDataKey="createAttributeSet"/> + </createData> + </before> + <after> + <deleteData createDataKey="createProduct" stepKey="deleteProductCustom"/> + <deleteData createDataKey="createCategory" stepKey="deleteCategory"/> + <deleteData createDataKey="createAttributeSet" stepKey="deleteAttributeSet"/> + <deleteData createDataKey="createProductAttribute" stepKey="deleteProductAttribute"/> + <actionGroup ref="AdminLogoutActionGroup" stepKey="logout"/> + </after> + + <amOnPage url="{{StorefrontProductPage.url($$createProduct.name$$)}}" stepKey="goToProductPage"/> + <dontSeeElement selector="{{StorefrontProductInfoDetailsSection.productNameForReview}}" stepKey="dontSeeReviewTab"/> + <click selector="{{StorefrontProductInfoMainSection.addReviewLink}}" stepKey="clickAddReview"/> + <dontSeeJsError stepKey="dontSeeJsError"/> + <seeElement selector="{{StorefrontProductInfoDetailsSection.productNameForReview}}" stepKey="seeReviewTab"/> + </test> +</tests> diff --git a/app/code/Magento/Review/Test/Mftf/Test/StorefrontVerifySecureURLRedirectReviewTest.xml b/app/code/Magento/Review/Test/Mftf/Test/StorefrontVerifySecureURLRedirectReviewTest.xml index b10af7a303cc7..8a2f441e5c4e8 100644 --- a/app/code/Magento/Review/Test/Mftf/Test/StorefrontVerifySecureURLRedirectReviewTest.xml +++ b/app/code/Magento/Review/Test/Mftf/Test/StorefrontVerifySecureURLRedirectReviewTest.xml @@ -8,7 +8,7 @@ <tests xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/testSchema.xsd"> - <test name="StorefrontVerifySecureURLRedirectReview"> + <test name="StorefrontVerifySecureURLRedirectReviewTest"> <annotations> <features value="Review"/> <stories value="Storefront Secure URLs"/> diff --git a/app/code/Magento/Review/Test/Unit/Block/Product/ListViewTest.php b/app/code/Magento/Review/Test/Unit/Block/Product/ListViewTest.php new file mode 100644 index 0000000000000..a1cc7a05dfef5 --- /dev/null +++ b/app/code/Magento/Review/Test/Unit/Block/Product/ListViewTest.php @@ -0,0 +1,49 @@ +<?php +/** + * Test class for \Magento\Review\Block\Product\View\ListView + * + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ + +namespace Magento\Review\Test\Unit\Block\Product; + +use Magento\Framework\TestFramework\Unit\Helper\ObjectManager; +use Magento\Review\Block\Product\View\ListView; +use PHPUnit\Framework\TestCase; + +/** + * Class ViewTest + */ +class ListViewTest extends TestCase +{ + /** + * @var ListView + */ + private $listView; + + /** + * @var ObjectManager + */ + private $objectManager; + + /** + * @inheritDoc + */ + protected function setUp() + { + $this->objectManager = new ObjectManager($this); + $this->listView = $this->objectManager->getObject( + ListView::class + ); + } + + /** + * Validate that ListView->toHtml() would not crush if provided product is null + */ + public function testBlockShouldNotFailWithNullProduct() + { + $output = $this->listView->toHtml(); + $this->assertEquals('', $output); + } +} diff --git a/app/code/Magento/Review/Test/Unit/Observer/CatalogProductListCollectionAppendSummaryFieldsObserverTest.php b/app/code/Magento/Review/Test/Unit/Observer/CatalogProductListCollectionAppendSummaryFieldsObserverTest.php new file mode 100644 index 0000000000000..e67b1c815b28f --- /dev/null +++ b/app/code/Magento/Review/Test/Unit/Observer/CatalogProductListCollectionAppendSummaryFieldsObserverTest.php @@ -0,0 +1,149 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +declare(strict_types = 1); + +namespace Magento\Review\Test\Unit\Observer; + +use Magento\CatalogSearch\Model\ResourceModel\Fulltext\Collection; +use Magento\Framework\Event; +use Magento\Framework\Event\Observer; +use Magento\Review\Model\ResourceModel\Review\Summary; +use Magento\Review\Model\ResourceModel\Review\SummaryFactory; +use Magento\Review\Observer\CatalogProductListCollectionAppendSummaryFieldsObserver; +use Magento\Store\Api\Data\StoreInterface; +use Magento\Store\Model\StoreManagerInterface; +use MockObject; +use PHPUnit\Framework\TestCase; + +/** + * Test class for \Magento\Review\Observer\CatalogProductListCollectionAppendSummaryFieldsObserver + */ +class CatalogProductListCollectionAppendSummaryFieldsObserverTest extends TestCase +{ + private const STORE_ID = 1; + + /** + * @var Event|MockObject + */ + private $eventMock; + + /** + * Testable Object + * + * @var CatalogProductListCollectionAppendSummaryFieldsObserver + */ + private $observer; + + /** + * @var Observer|MockObject + */ + private $observerMock; + + /** + * @var Collection|MockObject + */ + private $productCollectionMock; + + /** + * @var StoreInterface|MockObject + */ + private $storeMock; + + /** + * @var StoreManagerInterface|MockObject + */ + private $storeManagerMock; + + /** + * @var Summary|MockObject + */ + private $sumResourceMock; + + /** + * @var SummaryFactory|MockObject + */ + private $sumResourceFactoryMock; + + /** + * @inheritdoc + */ + protected function setUp() : void + { + $this->eventMock = $this->getMockBuilder(Event::class) + ->disableOriginalConstructor() + ->setMethods(['getCollection']) + ->getMock(); + + $this->observerMock = $this->createMock(Observer::class); + + $this->productCollectionMock = $this->getMockBuilder(Collection::class) + ->disableOriginalConstructor() + ->getMock(); + + $this->storeManagerMock = $this->getMockBuilder(StoreManagerInterface::class) + ->disableOriginalConstructor() + ->setMethods(['getStore']) + ->getMockForAbstractClass(); + + $this->storeMock = $this->getMockBuilder(StoreInterface::class) + ->disableOriginalConstructor() + ->setMethods(['getId']) + ->getMockForAbstractClass(); + + $this->sumResourceMock = $this->createPartialMock( + Summary::class, + ['appendSummaryFieldsToCollection'] + ); + + $this->sumResourceFactoryMock = $this->getMockBuilder(SummaryFactory::class) + ->disableOriginalConstructor() + ->setMethods(['create']) + ->getMock(); + + $this->observer = new CatalogProductListCollectionAppendSummaryFieldsObserver( + $this->sumResourceFactoryMock, + $this->storeManagerMock + ); + } + + /** + * Product listing test + */ + public function testAddSummaryFieldToProductsCollection() : void + { + $this->eventMock + ->expects($this->once()) + ->method('getCollection') + ->willReturn($this->productCollectionMock); + + $this->observerMock + ->expects($this->once()) + ->method('getEvent') + ->willReturn($this->eventMock); + + $this->storeManagerMock + ->expects($this->once()) + ->method('getStore') + ->willReturn($this->storeMock); + + $this->storeMock + ->expects($this->once()) + ->method('getId') + ->willReturn(self::STORE_ID); + + $this->sumResourceFactoryMock + ->expects($this->once()) + ->method('create') + ->willReturn($this->sumResourceMock); + + $this->sumResourceMock + ->expects($this->once()) + ->method('appendSummaryFieldsToCollection') + ->willReturn($this->sumResourceMock); + + $this->observer->execute($this->observerMock); + } +} diff --git a/app/code/Magento/Review/etc/adminhtml/system.xml b/app/code/Magento/Review/etc/adminhtml/system.xml index a24ed29dc2c23..1ddae2118c5a7 100644 --- a/app/code/Magento/Review/etc/adminhtml/system.xml +++ b/app/code/Magento/Review/etc/adminhtml/system.xml @@ -8,15 +8,18 @@ <config xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="urn:magento:module:Magento_Config:etc/system_file.xsd"> <system> <section id="catalog"> - <group id="review" translate="label" type="text" sortOrder="100" showInDefault="1" showInWebsite="1" showInStore="0"> + <group id="review" translate="label" type="text" sortOrder="100" showInDefault="1" showInWebsite="1"> <label>Product Reviews</label> <field id="active" translate="label" type="select" sortOrder="1" showInDefault="1" showInWebsite="1" showInStore="1" canRestore="1"> <label>Enabled</label> <source_model>Magento\Config\Model\Config\Source\Yesno</source_model> </field> - <field id="allow_guest" translate="label" type="select" sortOrder="2" showInDefault="1" showInWebsite="1" showInStore="0" canRestore="1"> + <field id="allow_guest" translate="label" type="select" sortOrder="2" showInDefault="1" showInWebsite="1" canRestore="1"> <label>Allow Guests to Write Reviews</label> <source_model>Magento\Config\Model\Config\Source\Yesno</source_model> + <depends> + <field id="active">1</field> + </depends> </field> </group> </section> diff --git a/app/code/Magento/Review/registration.php b/app/code/Magento/Review/registration.php index 3325f6194118f..0588c9a1a7620 100644 --- a/app/code/Magento/Review/registration.php +++ b/app/code/Magento/Review/registration.php @@ -4,6 +4,6 @@ * See COPYING.txt for license details. */ -use \Magento\Framework\Component\ComponentRegistrar; +use Magento\Framework\Component\ComponentRegistrar; ComponentRegistrar::register(ComponentRegistrar::MODULE, 'Magento_Review', __DIR__); diff --git a/app/code/Magento/Review/view/adminhtml/layout/review_customer_productreviews.xml b/app/code/Magento/Review/view/adminhtml/layout/review_customer_productreviews.xml new file mode 100644 index 0000000000000..ee1cc4bfb0871 --- /dev/null +++ b/app/code/Magento/Review/view/adminhtml/layout/review_customer_productreviews.xml @@ -0,0 +1,12 @@ +<?xml version="1.0"?> +<!-- +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +--> +<layout xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="urn:magento:framework:View/Layout/etc/layout_generic.xsd"> + <container name="root" label="Root"> + <block class="Magento\Review\Block\Adminhtml\Edit\Tab\Reviews" name="admin.customer.reviews"/> + </container> +</layout> diff --git a/app/code/Magento/Robots/Controller/Index/Index.php b/app/code/Magento/Robots/Controller/Index/Index.php index 679066d723dce..3a7eb152afdd2 100644 --- a/app/code/Magento/Robots/Controller/Index/Index.php +++ b/app/code/Magento/Robots/Controller/Index/Index.php @@ -3,17 +3,18 @@ * Copyright © Magento, Inc. All rights reserved. * See COPYING.txt for license details. */ +declare(strict_types=1); + namespace Magento\Robots\Controller\Index; -use Magento\Framework\App\Action\Action; -use Magento\Framework\App\Action\Context; +use Magento\Framework\App\Action\HttpGetActionInterface; use Magento\Framework\View\Result\Page; use Magento\Framework\View\Result\PageFactory; /** * Processes request to robots.txt file and returns robots.txt content as result */ -class Index extends Action +class Index implements HttpGetActionInterface { /** * @var PageFactory @@ -21,16 +22,12 @@ class Index extends Action private $resultPageFactory; /** - * @param Context $context * @param PageFactory $resultPageFactory */ public function __construct( - Context $context, PageFactory $resultPageFactory ) { $this->resultPageFactory = $resultPageFactory; - - parent::__construct($context); } /** @@ -44,6 +41,7 @@ public function execute() $resultPage = $this->resultPageFactory->create(true); $resultPage->addHandle('robots_index_index'); $resultPage->setHeader('Content-Type', 'text/plain'); + return $resultPage; } } diff --git a/app/code/Magento/Robots/Test/Unit/Controller/Index/IndexTest.php b/app/code/Magento/Robots/Test/Unit/Controller/Index/IndexTest.php index d3a7a97c7ea80..9ba2a33257751 100644 --- a/app/code/Magento/Robots/Test/Unit/Controller/Index/IndexTest.php +++ b/app/code/Magento/Robots/Test/Unit/Controller/Index/IndexTest.php @@ -3,40 +3,42 @@ * Copyright © Magento, Inc. All rights reserved. * See COPYING.txt for license details. */ +declare(strict_types=1); namespace Magento\Robots\Test\Unit\Controller\Index; -class IndexTest extends \PHPUnit\Framework\TestCase +use Magento\Framework\TestFramework\Unit\Helper\ObjectManager; +use Magento\Framework\View\Result\Page; +use Magento\Framework\View\Result\PageFactory; +use Magento\Robots\Controller\Index\Index; +use PHPUnit\Framework\MockObject\MockObject; +use PHPUnit\Framework\TestCase; + +class IndexTest extends TestCase { /** - * @var \Magento\Framework\App\Action\Context|\PHPUnit_Framework_MockObject_MockObject + * @var Index */ - private $contextMock; + private $controller; /** - * @var \Magento\Framework\Controller\Result\RawFactory|\PHPUnit_Framework_MockObject_MockObject + * @var PageFactory|MockObject */ - private $resultPageFactory; - - /** - * @var \Magento\Robots\Controller\Index\Index - */ - private $controller; + private $resultPageFactoryMock; protected function setUp() { - $this->contextMock = $this->getMockBuilder(\Magento\Framework\App\Action\Context::class) - ->disableOriginalConstructor() - ->getMock(); - - $this->resultPageFactory = $this->getMockBuilder(\Magento\Framework\View\Result\PageFactory::class) + $this->resultPageFactoryMock = $this->getMockBuilder(PageFactory::class) ->disableOriginalConstructor() ->setMethods(['create']) ->getMock(); - $this->controller = new \Magento\Robots\Controller\Index\Index( - $this->contextMock, - $this->resultPageFactory + $objectManager = new ObjectManager($this); + $this->controller = $objectManager->getObject( + Index::class, + [ + 'resultPageFactory' => $this->resultPageFactoryMock + ] ); } @@ -45,7 +47,7 @@ protected function setUp() */ public function testExecute() { - $resultPageMock = $this->getMockBuilder(\Magento\Framework\View\Result\Page::class) + $resultPageMock = $this->getMockBuilder(Page::class) ->disableOriginalConstructor() ->getMock(); $resultPageMock->expects($this->once()) @@ -55,13 +57,12 @@ public function testExecute() ->method('setHeader') ->with('Content-Type', 'text/plain'); - $this->resultPageFactory->expects($this->any()) - ->method('create') + $this->resultPageFactoryMock->method('create') ->with(true) ->willReturn($resultPageMock); $this->assertInstanceOf( - \Magento\Framework\View\Result\Page::class, + Page::class, $this->controller->execute() ); } diff --git a/app/code/Magento/Robots/registration.php b/app/code/Magento/Robots/registration.php index 36d898abfa9fb..343344d3910ff 100644 --- a/app/code/Magento/Robots/registration.php +++ b/app/code/Magento/Robots/registration.php @@ -4,6 +4,6 @@ * See COPYING.txt for license details. */ -use \Magento\Framework\Component\ComponentRegistrar; +use Magento\Framework\Component\ComponentRegistrar; ComponentRegistrar::register(ComponentRegistrar::MODULE, 'Magento_Robots', __DIR__); diff --git a/app/code/Magento/Rss/Test/Unit/Controller/Adminhtml/Feed/IndexTest.php b/app/code/Magento/Rss/Test/Unit/Controller/Adminhtml/Feed/IndexTest.php index a601f8fb2d1d7..92348d2b9cb58 100644 --- a/app/code/Magento/Rss/Test/Unit/Controller/Adminhtml/Feed/IndexTest.php +++ b/app/code/Magento/Rss/Test/Unit/Controller/Adminhtml/Feed/IndexTest.php @@ -6,11 +6,8 @@ namespace Magento\Rss\Test\Unit\Controller\Adminhtml\Feed; use Magento\Framework\TestFramework\Unit\Helper\ObjectManager as ObjectManagerHelper; -use Zend\Feed\Writer\Exception\InvalidArgumentException; /** - * Class IndexTest - * @package Magento\Rss\Controller\Feed * @SuppressWarnings(PHPMD.CouplingBetweenObjects) */ class IndexTest extends \PHPUnit\Framework\TestCase diff --git a/app/code/Magento/Rss/Test/Unit/Controller/Feed/IndexTest.php b/app/code/Magento/Rss/Test/Unit/Controller/Feed/IndexTest.php index 30415155d5f6e..51d6b01b48bb5 100644 --- a/app/code/Magento/Rss/Test/Unit/Controller/Feed/IndexTest.php +++ b/app/code/Magento/Rss/Test/Unit/Controller/Feed/IndexTest.php @@ -6,11 +6,9 @@ namespace Magento\Rss\Test\Unit\Controller\Feed; use Magento\Framework\TestFramework\Unit\Helper\ObjectManager as ObjectManagerHelper; -use Zend\Feed\Writer\Exception\InvalidArgumentException; /** - * Class IndexTest - * @package Magento\Rss\Controller\Feed + * Test for \Magento\Rss\Controller\Feed\Index */ class IndexTest extends \PHPUnit\Framework\TestCase { diff --git a/app/code/Magento/Rss/Test/Unit/Model/RssTest.php b/app/code/Magento/Rss/Test/Unit/Model/RssTest.php index f2888e4296b40..75652a1f75221 100644 --- a/app/code/Magento/Rss/Test/Unit/Model/RssTest.php +++ b/app/code/Magento/Rss/Test/Unit/Model/RssTest.php @@ -43,7 +43,7 @@ class RssTest extends \PHPUnit\Framework\TestCase <link>http://magento.com/rss/link</link> <description><![CDATA[Feed Description]]></description> <pubDate>Sat, 22 Apr 2017 13:21:12 +0200</pubDate> - <generator>Zend\Feed</generator> + <generator>Laminas\Feed</generator> <docs>http://blogs.law.harvard.edu/tech/rss</docs> <item> <title><![CDATA[Feed 1 Title]]> diff --git a/app/code/Magento/Rss/etc/module.xml b/app/code/Magento/Rss/etc/module.xml index 196d8c3ac5655..c00b53e716230 100644 --- a/app/code/Magento/Rss/etc/module.xml +++ b/app/code/Magento/Rss/etc/module.xml @@ -6,6 +6,5 @@ */ --> - - + diff --git a/app/code/Magento/Rss/registration.php b/app/code/Magento/Rss/registration.php index 4ab2ddf494665..dc8ba0d5d88ba 100644 --- a/app/code/Magento/Rss/registration.php +++ b/app/code/Magento/Rss/registration.php @@ -4,6 +4,6 @@ * See COPYING.txt for license details. */ -use \Magento\Framework\Component\ComponentRegistrar; +use Magento\Framework\Component\ComponentRegistrar; ComponentRegistrar::register(ComponentRegistrar::MODULE, 'Magento_Rss', __DIR__); diff --git a/app/code/Magento/Rule/Model/Condition/Product/AbstractProduct.php b/app/code/Magento/Rule/Model/Condition/Product/AbstractProduct.php index e216e2ae658ba..2206dbef38640 100644 --- a/app/code/Magento/Rule/Model/Condition/Product/AbstractProduct.php +++ b/app/code/Magento/Rule/Model/Condition/Product/AbstractProduct.php @@ -625,6 +625,8 @@ public function getMappedSqlField() $mappedSqlField = $this->getEavAttributeTableAlias() . '.value'; } elseif ($this->getAttribute() == 'category_ids') { $mappedSqlField = 'e.entity_id'; + } elseif ($this->getAttribute() == 'attribute_set_id') { + $mappedSqlField = 'e.attribute_set_id'; } else { $mappedSqlField = parent::getMappedSqlField(); } diff --git a/app/code/Magento/Rule/Test/Unit/Model/Condition/Product/AbstractProductTest.php b/app/code/Magento/Rule/Test/Unit/Model/Condition/Product/AbstractProductTest.php index 80f07c9d6550d..62d91de3ca8e2 100644 --- a/app/code/Magento/Rule/Test/Unit/Model/Condition/Product/AbstractProductTest.php +++ b/app/code/Magento/Rule/Test/Unit/Model/Condition/Product/AbstractProductTest.php @@ -3,85 +3,103 @@ * Copyright © Magento, Inc. All rights reserved. * See COPYING.txt for license details. */ - namespace Magento\Rule\Test\Unit\Model\Condition\Product; -use ReflectionMethod; -use ReflectionProperty; +use Magento\Catalog\Model\ProductCategoryList; +use Magento\Catalog\Model\ResourceModel\Eav\Attribute; +use Magento\Catalog\Model\ResourceModel\Product; +use Magento\Eav\Model\Config; +use Magento\Eav\Model\Entity\Attribute\Source\AbstractSource; +use Magento\Eav\Model\Entity\Type; +use Magento\Eav\Model\ResourceModel\Entity\Attribute\Set\Collection; +use Magento\Framework\DataObject; +use Magento\Framework\Model\AbstractModel; +use Magento\Rule\Model\Condition\Product\AbstractProduct; +use PHPUnit\Framework\MockObject\MockObject; +use PHPUnit\Framework\TestCase; -class AbstractProductTest extends \PHPUnit\Framework\TestCase +/** + * Class to test Abstract Rule product condition data model + */ +class AbstractProductTest extends TestCase { /** * Tested condition * - * @var \Magento\Rule\Model\Condition\Product\AbstractProduct|\PHPUnit_Framework_MockObject_MockObject + * @var AbstractProduct|MockObject */ protected $_condition; /** * Framework object * - * @var \Magento\Framework\DataObject|\PHPUnit_Framework_MockObject_MockObject + * @var DataObject|MockObject */ protected $_object; /** - * Reflection for Magento\Rule\Model\Condition\Product\AbstractProduct::$_entityAttributeValues + * Reflection for AbstractProduct::$_entityAttributeValues * * @var \ReflectionProperty */ protected $_entityAttributeValuesProperty; /** - * Reflection for Magento\Rule\Model\Condition\Product\AbstractProduct::$_config + * Reflection for AbstractProduct::$_config * * @var \ReflectionProperty */ protected $_configProperty; /** - * Reflection for Magento\Rule\Model\Condition\Product\AbstractProduct::$productCategoryListProperty + * Reflection for AbstractProduct::$productCategoryListProperty * * @var \ReflectionProperty */ private $productCategoryListProperty; + /** + * @inheritdoc + */ protected function setUp() { $this->_condition = $this->getMockForAbstractClass( - \Magento\Rule\Model\Condition\Product\AbstractProduct::class, + AbstractProduct::class, [], '', false ); $this->productCategoryListProperty = new \ReflectionProperty( - \Magento\Rule\Model\Condition\Product\AbstractProduct::class, + AbstractProduct::class, 'productCategoryList' ); $this->productCategoryListProperty->setAccessible(true); $this->_entityAttributeValuesProperty = new \ReflectionProperty( - \Magento\Rule\Model\Condition\Product\AbstractProduct::class, + AbstractProduct::class, '_entityAttributeValues' ); $this->_entityAttributeValuesProperty->setAccessible(true); $this->_configProperty = new \ReflectionProperty( - \Magento\Rule\Model\Condition\Product\AbstractProduct::class, + AbstractProduct::class, '_config' ); $this->_configProperty->setAccessible(true); } + /** + * Test to validate equal category id condition + */ public function testValidateAttributeEqualCategoryId() { - $product = $this->createPartialMock(\Magento\Framework\Model\AbstractModel::class, ["getAttribute"]); + $product = $this->createPartialMock(AbstractModel::class, ["getAttribute"]); $this->_condition->setAttribute('category_ids'); $this->_condition->setValueParsed('1'); $this->_condition->setOperator('{}'); - $productCategoryList = $this->getMockBuilder(\Magento\Catalog\Model\ProductCategoryList::class) + $productCategoryList = $this->getMockBuilder(ProductCategoryList::class) ->disableOriginalConstructor() ->getMock(); $productCategoryList->method('getCategoryIds')->willReturn([1, 2]); @@ -91,7 +109,7 @@ public function testValidateAttributeEqualCategoryId() ); $this->_configProperty->setValue( $this->_condition, - $this->getMockBuilder(\Magento\Eav\Model\Config::class) + $this->getMockBuilder(Config::class) ->disableOriginalConstructor() ->getMock() ); @@ -99,10 +117,13 @@ public function testValidateAttributeEqualCategoryId() $this->assertTrue($this->_condition->validate($product)); } + /** + * Test to validate empty attribute condition + */ public function testValidateEmptyEntityAttributeValues() { $product = $this->createPartialMock( - \Magento\Framework\Model\AbstractModel::class, + AbstractModel::class, ["getAttribute", 'getResource'] ); $product->expects($this->once()) @@ -110,7 +131,7 @@ public function testValidateEmptyEntityAttributeValues() ->willReturn(null); $product->setId(1); $configProperty = new \ReflectionProperty( - \Magento\Rule\Model\Condition\Product\AbstractProduct::class, + AbstractProduct::class, '_entityAttributeValues' ); $configProperty->setAccessible(true); @@ -118,10 +139,13 @@ public function testValidateEmptyEntityAttributeValues() $this->assertFalse($this->_condition->validate($product)); } + /** + * Test to validate empty attribute value condition + */ public function testValidateEmptyEntityAttributeValuesWithResource() { $product = $this->createPartialMock( - \Magento\Framework\Model\AbstractModel::class, + AbstractModel::class, ["getAttribute", 'getResource'] ); $product->setId(1); @@ -132,18 +156,18 @@ public function testValidateEmptyEntityAttributeValuesWithResource() $this->_configProperty->setValue( $this->_condition, - $this->createMock(\Magento\Eav\Model\Config::class) + $this->createMock(Config::class) ); - $attribute = new \Magento\Framework\DataObject(); + $attribute = new DataObject(); $attribute->setBackendType('datetime'); - $newResource = $this->createPartialMock(\Magento\Catalog\Model\ResourceModel\Product::class, ['getAttribute']); + $newResource = $this->createPartialMock(Product::class, ['getAttribute']); $newResource->expects($this->any()) ->method('getAttribute') ->with('someAttribute') ->will($this->returnValue($attribute)); - $newResource->_config = $this->createMock(\Magento\Eav\Model\Config::class); + $newResource->_config = $this->createMock(Config::class); $product->expects($this->atLeastOnce()) ->method('getResource') ->willReturn($newResource); @@ -154,22 +178,25 @@ public function testValidateEmptyEntityAttributeValuesWithResource() $attribute->setBackendType('null'); $attribute->setFrontendInput('multiselect'); - $newResource = $this->createPartialMock(\Magento\Catalog\Model\ResourceModel\Product::class, ['getAttribute']); + $newResource = $this->createPartialMock(Product::class, ['getAttribute']); $newResource->expects($this->any()) ->method('getAttribute') ->with('someAttribute') ->will($this->returnValue($attribute)); - $newResource->_config = $this->createMock(\Magento\Eav\Model\Config::class); + $newResource->_config = $this->createMock(Config::class); $product->setResource($newResource); $this->assertFalse($this->_condition->validate($product)); } + /** + * Test to validate set entity attribute value with resource condition + */ public function testValidateSetEntityAttributeValuesWithResource() { $this->_condition->setAttribute('someAttribute'); $product = $this->createPartialMock( - \Magento\Framework\Model\AbstractModel::class, + AbstractModel::class, ['getAttribute', 'getResource'] ); $product->setAtribute('attribute'); @@ -177,22 +204,22 @@ public function testValidateSetEntityAttributeValuesWithResource() $this->_configProperty->setValue( $this->_condition, - $this->createMock(\Magento\Eav\Model\Config::class) + $this->createMock(Config::class) ); $this->_entityAttributeValuesProperty->setValue( $this->_condition, - $this->createMock(\Magento\Eav\Model\Config::class) + $this->createMock(Config::class) ); - $attribute = new \Magento\Framework\DataObject(); + $attribute = new DataObject(); $attribute->setBackendType('datetime'); - $newResource = $this->createPartialMock(\Magento\Catalog\Model\ResourceModel\Product::class, ['getAttribute']); + $newResource = $this->createPartialMock(Product::class, ['getAttribute']); $newResource->expects($this->any()) ->method('getAttribute') ->with('someAttribute') ->will($this->returnValue($attribute)); - $newResource->_config = $this->createMock(\Magento\Eav\Model\Config::class); + $newResource->_config = $this->createMock(Config::class); $product->expects($this->atLeastOnce()) ->method('getResource') @@ -209,10 +236,13 @@ public function testValidateSetEntityAttributeValuesWithResource() $this->assertFalse($this->_condition->validate($product)); } + /** + * Test to validate set entity attribute value without resource condition + */ public function testValidateSetEntityAttributeValuesWithoutResource() { $product = $this->createPartialMock( - \Magento\Framework\Model\AbstractModel::class, + AbstractModel::class, ['someMethod', 'getResource', 'load'] ); $this->_condition->setAttribute('someAttribute'); @@ -221,23 +251,23 @@ public function testValidateSetEntityAttributeValuesWithoutResource() $this->_configProperty->setValue( $this->_condition, - $this->createMock(\Magento\Eav\Model\Config::class) + $this->createMock(Config::class) ); $this->_entityAttributeValuesProperty->setValue( $this->_condition, - $this->createMock(\Magento\Eav\Model\Config::class) + $this->createMock(Config::class) ); - $attribute = new \Magento\Framework\DataObject(); + $attribute = new DataObject(); $attribute->setBackendType('multiselect'); - $newResource = $this->createPartialMock(\Magento\Catalog\Model\ResourceModel\Product::class, ['getAttribute']); + $newResource = $this->createPartialMock(Product::class, ['getAttribute']); $newResource->expects($this->any()) ->method('getAttribute') ->with('someAttribute') ->will($this->returnValue($attribute)); - $newResource->_config = $this->createMock(\Magento\Eav\Model\Config::class); + $newResource->_config = $this->createMock(Config::class); $product->expects($this->atLeastOnce()) ->method('getResource') @@ -254,16 +284,16 @@ public function testValidateSetEntityAttributeValuesWithoutResource() $this->assertFalse($this->_condition->validate($product)); - $attribute = new \Magento\Framework\DataObject(); + $attribute = new DataObject(); $attribute->setBackendType(null); $attribute->setFrontendInput('multiselect'); - $newResource = $this->createPartialMock(\Magento\Catalog\Model\ResourceModel\Product::class, ['getAttribute']); + $newResource = $this->createPartialMock(Product::class, ['getAttribute']); $newResource->expects($this->any()) ->method('getAttribute') ->with('someAttribute') ->will($this->returnValue($attribute)); - $newResource->_config = $this->createMock(\Magento\Eav\Model\Config::class); + $newResource->_config = $this->createMock(Config::class); $product->setResource($newResource); $product->setId(1); @@ -272,19 +302,29 @@ public function testValidateSetEntityAttributeValuesWithoutResource() $this->assertFalse($this->_condition->validate($product)); } + /** + * Test to get tables to join + */ public function testGetjointTables() { $this->_condition->setAttribute('category_ids'); $this->assertEquals([], $this->_condition->getTablesToJoin()); } + /** + * Test to get mapped sql field + */ public function testGetMappedSqlField() { $this->_condition->setAttribute('category_ids'); $this->assertEquals('e.entity_id', $this->_condition->getMappedSqlField()); + $this->_condition->setAttribute('attribute_set_id'); + $this->assertEquals('e.attribute_set_id', $this->_condition->getMappedSqlField()); } /** + * Test to prepare value options + * * @param array $setData * @param string $attributeObjectFrontendInput * @param array $attrObjectSourceAllOptionsValue @@ -308,7 +348,7 @@ public function testPrepareValueOptions( $this->_condition->setData($key, $value); } - $attrObjectSourceMock = $this->getMockBuilder(\Magento\Eav\Model\Entity\Attribute\Source\AbstractSource::class) + $attrObjectSourceMock = $this->getMockBuilder(AbstractSource::class) ->setMethods(['getAllOptions']) ->disableOriginalConstructor() ->getMock(); @@ -318,7 +358,7 @@ public function testPrepareValueOptions( ->with($expectedAttrObjSourceAllOptionsParam) ->willReturn($attrObjectSourceAllOptionsValue); - $attributeObjectMock = $this->getMockBuilder(\Magento\Catalog\Model\ResourceModel\Eav\Attribute::class) + $attributeObjectMock = $this->getMockBuilder(Attribute::class) ->setMethods(['usesSource', 'getFrontendInput', 'getSource', 'getAllOptions']) ->disableOriginalConstructor() ->getMock(); @@ -329,28 +369,28 @@ public function testPrepareValueOptions( ->willReturn($attributeObjectFrontendInput); $attributeObjectMock->method('getSource')->willReturn($attrObjectSourceMock); - $entityTypeMock = $this->getMockBuilder(\Magento\Framework\Model\AbstractModel\Type::class) + $entityTypeMock = $this->getMockBuilder(Type::class) ->setMethods(['getId']) ->disableOriginalConstructor() ->getMock(); $entityTypeMock->method('getId')->willReturn('SomeEntityType'); $configValueMock = $this->createPartialMock( - \Magento\Eav\Model\Config::class, + Config::class, ['getAttribute', 'getEntityType'] ); $configValueMock->method('getAttribute')->willReturn($attributeObjectMock); $configValueMock->method('getEntityType')->willReturn($entityTypeMock); - $configProperty = new ReflectionProperty( - \Magento\Rule\Model\Condition\Product\AbstractProduct::class, + $configProperty = new \ReflectionProperty( + AbstractProduct::class, '_config' ); $configProperty->setAccessible(true); $configProperty->setValue($this->_condition, $configValueMock); $attrSetCollectionValueMock = $this - ->getMockBuilder(\Magento\Eav\Model\ResourceModel\Entity\Attribute\Set\Collection::class) + ->getMockBuilder(Collection::class) ->setMethods(['setEntityTypeFilter', 'load', 'toOptionArray']) ->disableOriginalConstructor() ->getMock(); @@ -362,15 +402,15 @@ public function testPrepareValueOptions( ->method('toOptionArray') ->willReturn($attrSetCollectionOptionsArray); - $attrSetCollectionProperty = new ReflectionProperty( - \Magento\Rule\Model\Condition\Product\AbstractProduct::class, + $attrSetCollectionProperty = new \ReflectionProperty( + AbstractProduct::class, '_attrSetCollection' ); $attrSetCollectionProperty->setAccessible(true); $attrSetCollectionProperty->setValue($this->_condition, $attrSetCollectionValueMock); - $testedMethod = new ReflectionMethod( - \Magento\Rule\Model\Condition\Product\AbstractProduct::class, + $testedMethod = new \ReflectionMethod( + AbstractProduct::class, '_prepareValueOptions' ); $testedMethod->setAccessible(true); @@ -395,7 +435,6 @@ public function prepareValueOptionsDataProvider() 'value_option' => ['k' => 'v'], ], null, null, null, null, ['key' => 'value'], ['k' => 'v'], ], - [ ['attribute' => 'attribute_set_id'], null, @@ -414,7 +453,6 @@ public function prepareValueOptionsDataProvider() 'value2' => 'Label for value 2' ] ], - [ [ 'value_select_options' => [ @@ -436,7 +474,6 @@ public function prepareValueOptionsDataProvider() 'value4' => 'Label for value 4' ] ], - [ [ 'value_select_options' => [ @@ -458,7 +495,6 @@ public function prepareValueOptionsDataProvider() 'value6' => 'Label for value 6' ] ], - [ [], 'multiselect', @@ -477,7 +513,6 @@ public function prepareValueOptionsDataProvider() 'value8' => 'Label for value 8', ], ], - [ [], 'multiselect', @@ -495,7 +530,6 @@ public function prepareValueOptionsDataProvider() 'valueA' => 'Label for value A', ], ], - [ [], 'select', diff --git a/app/code/Magento/Rule/registration.php b/app/code/Magento/Rule/registration.php index 00c72c5c0a9ba..21bce7ba05833 100644 --- a/app/code/Magento/Rule/registration.php +++ b/app/code/Magento/Rule/registration.php @@ -4,6 +4,6 @@ * See COPYING.txt for license details. */ -use \Magento\Framework\Component\ComponentRegistrar; +use Magento\Framework\Component\ComponentRegistrar; ComponentRegistrar::register(ComponentRegistrar::MODULE, 'Magento_Rule', __DIR__); diff --git a/app/code/Magento/Sales/Api/Data/OrderPaymentInterface.php b/app/code/Magento/Sales/Api/Data/OrderPaymentInterface.php index ea0cf5d7b32be..ac400206b8a2f 100644 --- a/app/code/Magento/Sales/Api/Data/OrderPaymentInterface.php +++ b/app/code/Magento/Sales/Api/Data/OrderPaymentInterface.php @@ -1042,6 +1042,14 @@ public function setCcNumberEnc($ccNumberEnc); */ public function setCcTransId($id); + /** + * Set the additional information for the order payment. + * + * @param string[] $additionalInformation + * @return $this + */ + public function setAdditionalInformation($additionalInformation); + /** * Sets the address status for the order payment. * diff --git a/app/code/Magento/Sales/Block/Adminhtml/Items/Column/DefaultColumn.php b/app/code/Magento/Sales/Block/Adminhtml/Items/Column/DefaultColumn.php index 80dcce2fd1be4..efef617acf900 100644 --- a/app/code/Magento/Sales/Block/Adminhtml/Items/Column/DefaultColumn.php +++ b/app/code/Magento/Sales/Block/Adminhtml/Items/Column/DefaultColumn.php @@ -68,19 +68,19 @@ public function getItem() */ public function getOrderOptions() { - $result = []; + $result = [[]]; if ($options = $this->getItem()->getProductOptions()) { if (isset($options['options'])) { - $result = array_merge($result, $options['options']); + $result[] = $options['options']; } if (isset($options['additional_options'])) { - $result = array_merge($result, $options['additional_options']); + $result[] = $options['additional_options']; } if (!empty($options['attributes_info'])) { - $result = array_merge($options['attributes_info'], $result); + $result[] = $options['attributes_info']; } } - return $result; + return array_merge(...$result); } /** diff --git a/app/code/Magento/Sales/Block/Adminhtml/Order/Create/Form/Account.php b/app/code/Magento/Sales/Block/Adminhtml/Order/Create/Form/Account.php index 03915c0499367..6a62ba7fbf0fd 100644 --- a/app/code/Magento/Sales/Block/Adminhtml/Order/Create/Form/Account.php +++ b/app/code/Magento/Sales/Block/Adminhtml/Order/Create/Form/Account.php @@ -38,7 +38,7 @@ class Account extends AbstractForm * @var \Magento\Framework\Api\ExtensibleDataObjectConverter */ protected $_extensibleDataObjectConverter; - + private const XML_PATH_EMAIL_REQUIRED_CREATE_ORDER = 'customer/create_account/email_required_create_order'; /** * @param \Magento\Backend\Block\Template\Context $context * @param \Magento\Backend\Model\Session\Quote $sessionQuote @@ -147,7 +147,7 @@ protected function _addAdditionalFormElementData(AbstractElement $element) { switch ($element->getId()) { case 'email': - $element->setRequired(1); + $element->setRequired($this->isEmailRequiredToCreateOrder()); $element->setClass('validate-email admin__control-text'); break; } @@ -164,7 +164,7 @@ public function getFormValues() try { $customer = $this->customerRepository->getById($this->getCustomerId()); } catch (\Exception $e) { - /** If customer does not exist do nothing. */ + $data = []; } $data = isset($customer) ? $this->_extensibleDataObjectConverter->toFlatArray( @@ -204,4 +204,17 @@ private function extractValuesFromAttributes(array $attributes): array return $formValues; } + + /** + * Retrieve email is required field for admin order creation + * + * @return bool + */ + private function isEmailRequiredToCreateOrder() + { + return $this->_scopeConfig->getValue( + self::XML_PATH_EMAIL_REQUIRED_CREATE_ORDER, + \Magento\Store\Model\ScopeInterface::SCOPE_STORE + ); + } } diff --git a/app/code/Magento/Sales/Block/Adminhtml/Order/Create/Messages.php b/app/code/Magento/Sales/Block/Adminhtml/Order/Create/Messages.php index d62497d6b47e9..18c1431f61104 100644 --- a/app/code/Magento/Sales/Block/Adminhtml/Order/Create/Messages.php +++ b/app/code/Magento/Sales/Block/Adminhtml/Order/Create/Messages.php @@ -14,6 +14,9 @@ */ class Messages extends \Magento\Framework\View\Element\Messages { + + private const ITEMS_GRID = 'items_grid'; + /** * Preparing global layout * @@ -22,6 +25,24 @@ class Messages extends \Magento\Framework\View\Element\Messages protected function _prepareLayout() { $this->addMessages($this->messageManager->getMessages(true)); + $itemsBlock = $this->getLayout()->getBlock(self::ITEMS_GRID); + if (!$itemsBlock) { + return; + } + $items = $itemsBlock->getItems(); + foreach ($items as $item) { + if ($item->getHasError()) { + $messageCollection = $this->getMessageCollection(); + foreach ($messageCollection->getItems() as $blockMessage) { + if ($item->getMessage(true) === $blockMessage->getText()) { + /* Remove duplicated messages.*/ + $messageCollection->deleteMessageByIdentifier($blockMessage->getIdentifier()); + } + } + $this->setMessages($messageCollection); + } + } + parent::_prepareLayout(); } } diff --git a/app/code/Magento/Sales/Block/Adminhtml/Order/Creditmemo/Create/Adjustments.php b/app/code/Magento/Sales/Block/Adminhtml/Order/Creditmemo/Create/Adjustments.php index 50d29c195968c..a7649fecaf2bb 100644 --- a/app/code/Magento/Sales/Block/Adminhtml/Order/Creditmemo/Create/Adjustments.php +++ b/app/code/Magento/Sales/Block/Adminhtml/Order/Creditmemo/Create/Adjustments.php @@ -6,6 +6,8 @@ namespace Magento\Sales\Block\Adminhtml\Order\Creditmemo\Create; use Magento\Framework\Pricing\PriceCurrencyInterface; +use Magento\Sales\Model\Order; +use Zend_Currency; /** * Credit memo adjustments block @@ -60,7 +62,7 @@ public function initTotals() { $parent = $this->getParentBlock(); $this->_source = $parent->getSource(); - $total = new \Magento\Framework\DataObject(['code' => 'agjustments', 'block_name' => $this->getNameInLayout()]); + $total = new \Magento\Framework\DataObject(['code' => 'adjustments', 'block_name' => $this->getNameInLayout()]); $parent->removeTotal('shipping'); $parent->removeTotal('adjustment_positive'); $parent->removeTotal('adjustment_negative'); @@ -68,6 +70,27 @@ public function initTotals() return $this; } + /** + * Format value based on order currency + * + * @param null|float $value + * + * @return string + */ + public function formatValue($value) + { + /** @var Order $order */ + $order = $this->getSource()->getOrder(); + + return $order->getOrderCurrency()->formatPrecision( + $value, + 2, + ['display' => Zend_Currency::NO_SYMBOL], + false, + false + ); + } + /** * Get source object * diff --git a/app/code/Magento/Sales/Block/Adminhtml/Order/Status/NewStatus/Form.php b/app/code/Magento/Sales/Block/Adminhtml/Order/Status/NewStatus/Form.php index 1b275c4d809cb..dd62411ea3454 100644 --- a/app/code/Magento/Sales/Block/Adminhtml/Order/Status/NewStatus/Form.php +++ b/app/code/Magento/Sales/Block/Adminhtml/Order/Status/NewStatus/Form.php @@ -100,7 +100,7 @@ protected function _addStoresFieldset($model, $form) 'fieldset_html_class' => 'store' ] ); - return ; + return; } $renderer = $this->getLayout()->createBlock( diff --git a/app/code/Magento/Sales/Block/Order/Email/Creditmemo/Items.php b/app/code/Magento/Sales/Block/Order/Email/Creditmemo/Items.php index 0b691eff5757b..0a1e87e5e0a27 100644 --- a/app/code/Magento/Sales/Block/Order/Email/Creditmemo/Items.php +++ b/app/code/Magento/Sales/Block/Order/Email/Creditmemo/Items.php @@ -5,6 +5,13 @@ */ namespace Magento\Sales\Block\Order\Email\Creditmemo; +use Magento\Framework\App\ObjectManager; +use Magento\Framework\View\Element\Template\Context; +use Magento\Sales\Api\CreditmemoRepositoryInterface; +use Magento\Sales\Api\Data\OrderInterface; +use Magento\Sales\Api\Data\CreditmemoInterface; +use Magento\Sales\Api\OrderRepositoryInterface; + /** * Sales Order Email creditmemo items * @@ -14,6 +21,36 @@ */ class Items extends \Magento\Sales\Block\Items\AbstractItems { + /** + * @var OrderRepositoryInterface + */ + private $orderRepository; + + /** + * @var CreditmemoRepositoryInterface + */ + private $creditmemoRepository; + + /** + * @param Context $context + * @param array $data + * @param OrderRepositoryInterface|null $orderRepository + * @param CreditmemoRepositoryInterface|null $creditmemoRepository + */ + public function __construct( + Context $context, + array $data = [], + ?OrderRepositoryInterface $orderRepository = null, + ?CreditmemoRepositoryInterface $creditmemoRepository = null + ) { + $this->orderRepository = + $orderRepository ?: ObjectManager::getInstance()->get(OrderRepositoryInterface::class); + $this->creditmemoRepository = + $creditmemoRepository ?: ObjectManager::getInstance()->get(CreditmemoRepositoryInterface::class); + + parent::__construct($context, $data); + } + /** * Prepare item before output * @@ -25,4 +62,54 @@ protected function _prepareItem(\Magento\Framework\View\Element\AbstractBlock $r $renderer->getItem()->setOrder($this->getOrder()); $renderer->getItem()->setSource($this->getCreditmemo()); } + + /** + * Returns order. + * + * Custom email templates are only allowed to use scalar values for variable data. + * So order is loaded by order_id, that is passed to block from email template. + * For legacy custom email templates it can pass as an object. + * + * @return OrderInterface|null + */ + public function getOrder() + { + $order = $this->getData('order'); + if ($order !== null) { + return $order; + } + + $orderId = (int)$this->getData('order_id'); + if ($orderId) { + $order = $this->orderRepository->get($orderId); + $this->setData('order', $order); + } + + return $this->getData('order'); + } + + /** + * Returns creditmemo. + * + * Custom email templates are only allowed to use scalar values for variable data. + * So creditmemo is loaded by creditmemo_id, that is passed to block from email template. + * For legacy custom email templates it can pass as an object. + * + * @return CreditmemoInterface|null + */ + public function getCreditmemo() + { + $creditmemo = $this->getData('creditmemo'); + if ($creditmemo !== null) { + return $creditmemo; + } + + $creditmemoId = (int)$this->getData('creditmemo_id'); + if ($creditmemoId) { + $creditmemo = $this->creditmemoRepository->get($creditmemoId); + $this->setData('creditmemo', $creditmemo); + } + + return $this->getData('creditmemo'); + } } diff --git a/app/code/Magento/Sales/Block/Order/Email/Invoice/Items.php b/app/code/Magento/Sales/Block/Order/Email/Invoice/Items.php index bc7756816d32a..cc2b197ab0eb2 100644 --- a/app/code/Magento/Sales/Block/Order/Email/Invoice/Items.php +++ b/app/code/Magento/Sales/Block/Order/Email/Invoice/Items.php @@ -6,6 +6,13 @@ namespace Magento\Sales\Block\Order\Email\Invoice; +use Magento\Framework\App\ObjectManager; +use Magento\Framework\View\Element\Template\Context; +use Magento\Sales\Api\Data\InvoiceInterface; +use Magento\Sales\Api\Data\OrderInterface; +use Magento\Sales\Api\InvoiceRepositoryInterface; +use Magento\Sales\Api\OrderRepositoryInterface; + /** * Sales Order Email Invoice items * @@ -14,6 +21,36 @@ */ class Items extends \Magento\Sales\Block\Items\AbstractItems { + /** + * @var OrderRepositoryInterface + */ + private $orderRepository; + + /** + * @var InvoiceRepositoryInterface + */ + private $invoiceRepository; + + /** + * @param Context $context + * @param array $data + * @param OrderRepositoryInterface|null $orderRepository + * @param InvoiceRepositoryInterface|null $invoiceRepository + */ + public function __construct( + Context $context, + array $data = [], + ?OrderRepositoryInterface $orderRepository = null, + ?InvoiceRepositoryInterface $invoiceRepository = null + ) { + $this->orderRepository = + $orderRepository ?: ObjectManager::getInstance()->get(OrderRepositoryInterface::class); + $this->invoiceRepository = + $invoiceRepository ?: ObjectManager::getInstance()->get(InvoiceRepositoryInterface::class); + + parent::__construct($context, $data); + } + /** * Prepare item before output * @@ -25,4 +62,54 @@ protected function _prepareItem(\Magento\Framework\View\Element\AbstractBlock $r $renderer->getItem()->setOrder($this->getOrder()); $renderer->getItem()->setSource($this->getInvoice()); } + + /** + * Returns order. + * + * Custom email templates are only allowed to use scalar values for variable data. + * So order is loaded by order_id, that is passed to block from email template. + * For legacy custom email templates it can pass as an object. + * + * @return OrderInterface|null + */ + public function getOrder() + { + $order = $this->getData('order'); + if ($order !== null) { + return $order; + } + + $orderId = (int)$this->getData('order_id'); + if ($orderId) { + $order = $this->orderRepository->get($orderId); + $this->setData('order', $order); + } + + return $this->getData('order'); + } + + /** + * Returns invoice. + * + * Custom email templates are only allowed to use scalar values for variable data. + * So invoice is loaded by invoice_id, that is passed to block from email template. + * For legacy custom email templates it can pass as an object. + * + * @return InvoiceInterface|null + */ + public function getInvoice() + { + $invoice = $this->getData('invoice'); + if ($invoice !== null) { + return $invoice; + } + + $invoiceId = (int)$this->getData('invoice_id'); + if ($invoiceId) { + $invoice = $this->invoiceRepository->get($invoiceId); + $this->setData('invoice', $invoice); + } + + return $this->getData('invoice'); + } } diff --git a/app/code/Magento/Sales/Block/Order/Email/Items.php b/app/code/Magento/Sales/Block/Order/Email/Items.php index ddce387b91068..e11981285f04f 100644 --- a/app/code/Magento/Sales/Block/Order/Email/Items.php +++ b/app/code/Magento/Sales/Block/Order/Email/Items.php @@ -11,10 +11,61 @@ */ namespace Magento\Sales\Block\Order\Email; +use Magento\Framework\App\ObjectManager; +use Magento\Framework\View\Element\Template\Context; +use Magento\Sales\Api\Data\OrderInterface; +use Magento\Sales\Api\OrderRepositoryInterface; + /** + * Sales Order Email items. + * * @api * @since 100.0.2 */ class Items extends \Magento\Sales\Block\Items\AbstractItems { + /** + * @var OrderRepositoryInterface + */ + private $orderRepository; + + /** + * @param Context $context + * @param array $data + * @param OrderRepositoryInterface|null $orderRepository + */ + public function __construct( + Context $context, + array $data = [], + ?OrderRepositoryInterface $orderRepository = null + ) { + $this->orderRepository = $orderRepository ?: ObjectManager::getInstance()->get(OrderRepositoryInterface::class); + + parent::__construct($context, $data); + } + + /** + * Returns order. + * + * Custom email templates are only allowed to use scalar values for variable data. + * So order is loaded by order_id, that is passed to block from email template. + * For legacy custom email templates it can pass as an object. + * + * @return OrderInterface|null + */ + public function getOrder() + { + $order = $this->getData('order'); + + if ($order !== null) { + return $order; + } + $orderId = (int)$this->getData('order_id'); + if ($orderId) { + $order = $this->orderRepository->get($orderId); + $this->setData('order', $order); + } + + return $this->getData('order'); + } } diff --git a/app/code/Magento/Sales/Block/Order/Email/Items/DefaultItems.php b/app/code/Magento/Sales/Block/Order/Email/Items/DefaultItems.php index 0aa4f807700b6..064405daf89a8 100644 --- a/app/code/Magento/Sales/Block/Order/Email/Items/DefaultItems.php +++ b/app/code/Magento/Sales/Block/Order/Email/Items/DefaultItems.php @@ -29,27 +29,31 @@ public function getOrder() } /** + * Returns Items options as array + * * @return array */ public function getItemOptions() { - $result = []; + $result = [[]]; if ($options = $this->getItem()->getOrderItem()->getProductOptions()) { if (isset($options['options'])) { - $result = array_merge($result, $options['options']); + $result[] = $options['options']; } if (isset($options['additional_options'])) { - $result = array_merge($result, $options['additional_options']); + $result[] = $options['additional_options']; } if (isset($options['attributes_info'])) { - $result = array_merge($result, $options['attributes_info']); + $result[] = $options['attributes_info']; } } - return $result; + return array_merge(...$result); } /** + * Formats the value in HTML + * * @param string|array $value * @return string */ @@ -70,7 +74,9 @@ public function getValueHtml($value) } /** - * @param mixed $item + * Returns Product SKU for Item provided + * + * @param OrderItem $item * @return mixed */ public function getSku($item) diff --git a/app/code/Magento/Sales/Block/Order/Email/Items/Order/DefaultOrder.php b/app/code/Magento/Sales/Block/Order/Email/Items/Order/DefaultOrder.php index ae2dec6aefa6e..0291a1275c350 100644 --- a/app/code/Magento/Sales/Block/Order/Email/Items/Order/DefaultOrder.php +++ b/app/code/Magento/Sales/Block/Order/Email/Items/Order/DefaultOrder.php @@ -3,6 +3,7 @@ * Copyright © Magento, Inc. All rights reserved. * See COPYING.txt for license details. */ + namespace Magento\Sales\Block\Order\Email\Items\Order; use Magento\Sales\Model\Order\Item as OrderItem; @@ -27,48 +28,49 @@ public function getOrder() } /** + * Returns array of Item options + * * @return array */ public function getItemOptions() { - $result = []; + $result = [[]]; if ($options = $this->getItem()->getProductOptions()) { if (isset($options['options'])) { - $result = array_merge($result, $options['options']); + $result[] = $options['options']; } if (isset($options['additional_options'])) { - $result = array_merge($result, $options['additional_options']); + $result[] = $options['additional_options']; } if (isset($options['attributes_info'])) { - $result = array_merge($result, $options['attributes_info']); + $result[] = $options['attributes_info']; } } - return $result; + return array_merge(...$result); } /** + * Formats the value in HTML + * * @param string|array $value * @return string */ public function getValueHtml($value) { if (is_array($value)) { - return sprintf( - '%d', - $value['qty'] - ) . ' x ' . $this->escapeHtml( - $value['title'] - ) . " " . $this->getItem()->getOrder()->formatPrice( - $value['price'] - ); + return sprintf('%d', $value['qty']) + . ' x ' . $this->escapeHtml($value['title']) + . " " . $this->getItem()->getOrder()->formatPrice($value['price']); } else { return $this->escapeHtml($value); } } /** - * @param mixed $item + * Returns Product SKU for Item provided + * + * @param OrderItem $item * @return mixed */ public function getSku($item) diff --git a/app/code/Magento/Sales/Block/Order/Email/Shipment/Items.php b/app/code/Magento/Sales/Block/Order/Email/Shipment/Items.php index a4c9a7b80a00d..1f9b353180fd9 100644 --- a/app/code/Magento/Sales/Block/Order/Email/Shipment/Items.php +++ b/app/code/Magento/Sales/Block/Order/Email/Shipment/Items.php @@ -6,6 +6,13 @@ namespace Magento\Sales\Block\Order\Email\Shipment; +use Magento\Framework\App\ObjectManager; +use Magento\Framework\View\Element\Template\Context; +use Magento\Sales\Api\Data\ShipmentInterface; +use Magento\Sales\Api\Data\OrderInterface; +use Magento\Sales\Api\OrderRepositoryInterface; +use Magento\Sales\Api\ShipmentRepositoryInterface; + /** * Sales Order Email Shipment items * @@ -14,6 +21,36 @@ */ class Items extends \Magento\Sales\Block\Items\AbstractItems { + /** + * @var OrderRepositoryInterface + */ + private $orderRepository; + + /** + * @var ShipmentRepositoryInterface + */ + private $shipmentRepository; + + /** + * @param Context $context + * @param array $data + * @param OrderRepositoryInterface|null $orderRepository + * @param ShipmentRepositoryInterface|null $creditmemoRepository + */ + public function __construct( + Context $context, + array $data = [], + ?OrderRepositoryInterface $orderRepository = null, + ?ShipmentRepositoryInterface $creditmemoRepository = null + ) { + $this->orderRepository = + $orderRepository ?: ObjectManager::getInstance()->get(OrderRepositoryInterface::class); + $this->shipmentRepository = + $creditmemoRepository ?: ObjectManager::getInstance()->get(ShipmentRepositoryInterface::class); + + parent::__construct($context, $data); + } + /** * Prepare item before output * @@ -25,4 +62,54 @@ protected function _prepareItem(\Magento\Framework\View\Element\AbstractBlock $r $renderer->getItem()->setOrder($this->getOrder()); $renderer->getItem()->setSource($this->getShipment()); } + + /** + * Returns order. + * + * Custom email templates are only allowed to use scalar values for variable data. + * So order is loaded by order_id, that is passed to block from email template. + * For legacy custom email templates it can pass as an object. + * + * @return OrderInterface|null + */ + public function getOrder() + { + $order = $this->getData('order'); + if ($order !== null) { + return $order; + } + + $orderId = (int)$this->getData('order_id'); + if ($orderId) { + $order = $this->orderRepository->get($orderId); + $this->setData('order', $order); + } + + return $this->getData('order'); + } + + /** + * Returns shipment. + * + * Custom email templates are only allowed to use scalar values for variable data. + * So shipment is loaded by shipment_id, that is passed to block from email template. + * For legacy custom email templates it can pass as an object. + * + * @return ShipmentInterface|null + */ + public function getShipment() + { + $shipment = $this->getData('shipment'); + if ($shipment !== null) { + return $shipment; + } + + $shipmentId = (int)$this->getData('shipment_id'); + if ($shipmentId) { + $shipment = $this->shipmentRepository->get($shipmentId); + $this->setData('shipment', $shipment); + } + + return $this->getData('shipment'); + } } diff --git a/app/code/Magento/Sales/Block/Order/History.php b/app/code/Magento/Sales/Block/Order/History.php index c06a5d8b24c1e..09300424212fe 100644 --- a/app/code/Magento/Sales/Block/Order/History.php +++ b/app/code/Magento/Sales/Block/Order/History.php @@ -188,4 +188,14 @@ public function getBackUrl() { return $this->getUrl('customer/account/'); } + + /** + * Get message for no orders. + * + * @return \Magento\Framework\Phrase + */ + public function getEmptyOrdersMessage() + { + return __('You have placed no orders.'); + } } diff --git a/app/code/Magento/Sales/Block/Order/Item/Renderer/DefaultRenderer.php b/app/code/Magento/Sales/Block/Order/Item/Renderer/DefaultRenderer.php index 2e119d0bf887a..bca6d49760d9a 100644 --- a/app/code/Magento/Sales/Block/Order/Item/Renderer/DefaultRenderer.php +++ b/app/code/Magento/Sales/Block/Order/Item/Renderer/DefaultRenderer.php @@ -6,7 +6,12 @@ namespace Magento\Sales\Block\Order\Item\Renderer; -use Magento\Sales\Model\Order\CreditMemo\Item as CreditMemoItem; +use Magento\Catalog\Model\Product\OptionFactory; +use Magento\Framework\DataObject; +use Magento\Framework\Stdlib\StringUtils; +use Magento\Framework\View\Element\AbstractBlock; +use Magento\Framework\View\Element\Template\Context; +use Magento\Sales\Model\Order\Creditmemo\Item as CreditMemoItem; use Magento\Sales\Model\Order\Invoice\Item as InvoiceItem; use Magento\Sales\Model\Order\Item as OrderItem; @@ -21,25 +26,25 @@ class DefaultRenderer extends \Magento\Framework\View\Element\Template /** * Magento string lib * - * @var \Magento\Framework\Stdlib\StringUtils + * @var StringUtils */ protected $string; /** - * @var \Magento\Catalog\Model\Product\OptionFactory + * @var OptionFactory */ protected $_productOptionFactory; /** - * @param \Magento\Framework\View\Element\Template\Context $context - * @param \Magento\Framework\Stdlib\StringUtils $string - * @param \Magento\Catalog\Model\Product\OptionFactory $productOptionFactory + * @param Context $context + * @param StringUtils $string + * @param OptionFactory $productOptionFactory * @param array $data */ public function __construct( - \Magento\Framework\View\Element\Template\Context $context, - \Magento\Framework\Stdlib\StringUtils $string, - \Magento\Catalog\Model\Product\OptionFactory $productOptionFactory, + Context $context, + StringUtils $string, + OptionFactory $productOptionFactory, array $data = [] ) { $this->string = $string; @@ -50,10 +55,10 @@ public function __construct( /** * Set item. * - * @param \Magento\Framework\DataObject $item + * @param DataObject $item * @return $this */ - public function setItem(\Magento\Framework\DataObject $item) + public function setItem(DataObject $item) { $this->setData('item', $item); return $this; @@ -86,7 +91,7 @@ public function getOrder() */ public function getOrderItem() { - if ($this->getItem() instanceof \Magento\Sales\Model\Order\Item) { + if ($this->getItem() instanceof OrderItem) { return $this->getItem(); } else { return $this->getItem()->getOrderItem(); @@ -100,20 +105,20 @@ public function getOrderItem() */ public function getItemOptions() { - $result = []; + $result = [[]]; $options = $this->getOrderItem()->getProductOptions(); if ($options) { if (isset($options['options'])) { - $result = array_merge($result, $options['options']); + $result[] = $options['options']; } if (isset($options['additional_options'])) { - $result = array_merge($result, $options['additional_options']); + $result[] = $options['additional_options']; } if (isset($options['attributes_info'])) { - $result = array_merge($result, $options['attributes_info']); + $result[] = $options['attributes_info']; } } - return $result; + return array_merge(...$result); } /** diff --git a/app/code/Magento/Sales/Block/Status/Grid/Column/State.php b/app/code/Magento/Sales/Block/Status/Grid/Column/State.php index d2163fae72428..10f8edb524c34 100644 --- a/app/code/Magento/Sales/Block/Status/Grid/Column/State.php +++ b/app/code/Magento/Sales/Block/Status/Grid/Column/State.php @@ -6,6 +6,8 @@ namespace Magento\Sales\Block\Status\Grid\Column; /** + * Column State class + * * @api * @since 100.0.2 */ @@ -53,12 +55,15 @@ public function getFrameCallback() */ public function decorateState($value, $row, $column, $isExport) { - $status = $row->getStatus(); - if ($value) { - $cell = $value . '[' . $this->_config->getStateLabelByStateAndStatus($value, $status) . ']'; - } else { - $cell = $value; + $states = $this->_config->getStates(); + if (isset($states[$value])) { + return sprintf( + "%s[%s]", + $value, + $states[$value] + ); } - return $cell; + + return $value; } } diff --git a/app/code/Magento/Sales/Controller/AbstractController/Reorder.php b/app/code/Magento/Sales/Controller/AbstractController/Reorder.php index 65cb537e89fec..062ad78e5001d 100644 --- a/app/code/Magento/Sales/Controller/AbstractController/Reorder.php +++ b/app/code/Magento/Sales/Controller/AbstractController/Reorder.php @@ -4,16 +4,19 @@ * See COPYING.txt for license details. */ +declare(strict_types=1); + namespace Magento\Sales\Controller\AbstractController; use Magento\Framework\App\Action; -use Magento\Framework\Registry; use Magento\Framework\App\Action\HttpPostActionInterface; +use Magento\Framework\App\ObjectManager; +use Magento\Framework\Exception\LocalizedException; +use Magento\Framework\Registry; +use Magento\Sales\Helper\Reorder as ReorderHelper; /** * Abstract class for controllers Reorder(Customer) and Reorder(Guest) - * - * @package Magento\Sales\Controller\AbstractController */ abstract class Reorder extends Action\Action implements HttpPostActionInterface { @@ -28,18 +31,31 @@ abstract class Reorder extends Action\Action implements HttpPostActionInterface protected $_coreRegistry; /** + * @var \Magento\Sales\Model\Reorder\Reorder + */ + private $reorder; + + /** + * Constructor + * * @param Action\Context $context * @param OrderLoaderInterface $orderLoader * @param Registry $registry + * @param ReorderHelper|null $reorderHelper + * @param \Magento\Sales\Model\Reorder\Reorder|null $reorder + * @SuppressWarnings(PHPMD.UnusedFormalParameter) */ public function __construct( Action\Context $context, OrderLoaderInterface $orderLoader, - Registry $registry + Registry $registry, + ReorderHelper $reorderHelper = null, + \Magento\Sales\Model\Reorder\Reorder $reorder = null ) { $this->orderLoader = $orderLoader; $this->_coreRegistry = $registry; parent::__construct($context); + $this->reorder = $reorder ?: ObjectManager::getInstance()->get(\Magento\Sales\Model\Reorder\Reorder::class); } /** @@ -54,32 +70,27 @@ public function execute() return $result; } $order = $this->_coreRegistry->registry('current_order'); + /** @var \Magento\Framework\Controller\Result\Redirect $resultRedirect */ $resultRedirect = $this->resultRedirectFactory->create(); - /* @var $cart \Magento\Checkout\Model\Cart */ - $cart = $this->_objectManager->get(\Magento\Checkout\Model\Cart::class); - $items = $order->getItemsCollection(); - foreach ($items as $item) { - try { - $cart->addOrderItem($item); - } catch (\Magento\Framework\Exception\LocalizedException $e) { - if ($this->_objectManager->get(\Magento\Checkout\Model\Session::class)->getUseNotice(true)) { - $this->messageManager->addNoticeMessage($e->getMessage()); - } else { - $this->messageManager->addErrorMessage($e->getMessage()); - } - return $resultRedirect->setPath('*/*/history'); - } catch (\Exception $e) { - $this->messageManager->addExceptionMessage( - $e, - __('We can\'t add this item to your shopping cart right now.') - ); - return $resultRedirect->setPath('checkout/cart'); + try { + $reorderOutput = $this->reorder->execute($order->getIncrementId(), $order->getStoreId()); + } catch (LocalizedException $localizedException) { + $this->messageManager->addErrorMessage($localizedException->getMessage()); + return $resultRedirect->setPath('checkout/cart'); + } + + $errors = $reorderOutput->getErrors(); + if (!empty($errors)) { + $useNotice = $this->_objectManager->get(\Magento\Checkout\Model\Session::class)->getUseNotice(true); + foreach ($errors as $error) { + $useNotice + ? $this->messageManager->addNoticeMessage($error->getMessage()) + : $this->messageManager->addErrorMessage($error->getMessage()); } } - $cart->save(); return $resultRedirect->setPath('checkout/cart'); } } diff --git a/app/code/Magento/Sales/Controller/Adminhtml/Order/Create/Reorder.php b/app/code/Magento/Sales/Controller/Adminhtml/Order/Create/Reorder.php index 995a6c216d3c2..eeaf4bee1b1c2 100644 --- a/app/code/Magento/Sales/Controller/Adminhtml/Order/Create/Reorder.php +++ b/app/code/Magento/Sales/Controller/Adminhtml/Order/Create/Reorder.php @@ -3,16 +3,29 @@ * Copyright © Magento, Inc. All rights reserved. * See COPYING.txt for license details. */ +declare(strict_types=1); + namespace Magento\Sales\Controller\Adminhtml\Order\Create; use Magento\Backend\App\Action; +use Magento\Backend\Model\View\Result\Forward; use Magento\Backend\Model\View\Result\ForwardFactory; +use Magento\Backend\Model\View\Result\Redirect; +use Magento\Catalog\Helper\Product; +use Magento\Framework\App\Action\HttpGetActionInterface; +use Magento\Framework\Escaper; use Magento\Framework\View\Result\PageFactory; -use Magento\Sales\Model\Order\Reorder\UnavailableProductsProvider; use Magento\Sales\Api\OrderRepositoryInterface; +use Magento\Sales\Controller\Adminhtml\Order\Create; use Magento\Sales\Helper\Reorder as ReorderHelper; +use Magento\Sales\Model\Order; +use Magento\Sales\Model\Order\Reorder\UnavailableProductsProvider; +use Psr\Log\LoggerInterface; -class Reorder extends \Magento\Sales\Controller\Adminhtml\Order\Create +/** + * Controller create order. + */ +class Reorder extends Create implements HttpGetActionInterface { /** * @var UnavailableProductsProvider @@ -29,29 +42,37 @@ class Reorder extends \Magento\Sales\Controller\Adminhtml\Order\Create */ private $reorderHelper; + /** + * @var LoggerInterface + */ + private $logger; + /** * @param Action\Context $context - * @param \Magento\Catalog\Helper\Product $productHelper - * @param \Magento\Framework\Escaper $escaper + * @param Product $productHelper + * @param Escaper $escaper * @param PageFactory $resultPageFactory * @param ForwardFactory $resultForwardFactory * @param UnavailableProductsProvider $unavailableProductsProvider * @param OrderRepositoryInterface $orderRepository * @param ReorderHelper $reorderHelper + * @param LoggerInterface $logger */ public function __construct( Action\Context $context, - \Magento\Catalog\Helper\Product $productHelper, - \Magento\Framework\Escaper $escaper, + Product $productHelper, + Escaper $escaper, PageFactory $resultPageFactory, ForwardFactory $resultForwardFactory, UnavailableProductsProvider $unavailableProductsProvider, OrderRepositoryInterface $orderRepository, - ReorderHelper $reorderHelper + ReorderHelper $reorderHelper, + LoggerInterface $logger ) { $this->unavailableProductsProvider = $unavailableProductsProvider; $this->orderRepository = $orderRepository; $this->reorderHelper = $reorderHelper; + $this->logger = $logger; parent::__construct( $context, $productHelper, @@ -62,19 +83,21 @@ public function __construct( } /** - * @return \Magento\Backend\Model\View\Result\Forward|\Magento\Backend\Model\View\Result\Redirect + * Adminhtml controller create order. + * + * @return Forward|Redirect */ public function execute() { $this->_getSession()->clearStorage(); $orderId = $this->getRequest()->getParam('order_id'); - /** @var \Magento\Sales\Model\Order $order */ + /** @var Order $order */ $order = $this->orderRepository->get($orderId); if (!$this->reorderHelper->canReorder($order->getEntityId())) { return $this->resultForwardFactory->create()->forward('noroute'); } - /** @var \Magento\Backend\Model\View\Result\Redirect $resultRedirect */ + /** @var Redirect $resultRedirect */ $resultRedirect = $this->resultRedirectFactory->create(); if (!$order->getId()) { $resultRedirect->setPath('sales/order/'); @@ -90,10 +113,20 @@ public function execute() } $resultRedirect->setPath('sales/order/view', ['order_id' => $orderId]); } else { - $order->setReordered(true); - $this->_getSession()->setUseOldShippingMethod(true); - $this->_getOrderCreateModel()->initFromOrder($order); - $resultRedirect->setPath('sales/*'); + try { + $order->setReordered(true); + $this->_getSession()->setUseOldShippingMethod(true); + $this->_getOrderCreateModel()->initFromOrder($order); + $resultRedirect->setPath('sales/*'); + } catch (\Magento\Framework\Exception\LocalizedException $e) { + $this->logger->critical($e); + $this->messageManager->addErrorMessage($e->getMessage()); + return $resultRedirect->setPath('sales/*'); + } catch (\Exception $e) { + $this->logger->critical($e); + $this->messageManager->addException($e, __('Error while processing order.')); + return $resultRedirect->setPath('sales/*'); + } } return $resultRedirect; diff --git a/app/code/Magento/Sales/Model/AdminOrder/Create.php b/app/code/Magento/Sales/Model/AdminOrder/Create.php index cf6d55d8c76f3..d1b3e67815098 100644 --- a/app/code/Magento/Sales/Model/AdminOrder/Create.php +++ b/app/code/Magento/Sales/Model/AdminOrder/Create.php @@ -33,6 +33,7 @@ class Create extends \Magento\Framework\DataObject implements \Magento\Checkout\ */ const XML_PATH_DEFAULT_EMAIL_DOMAIN = 'customer/create_account/email_domain'; + private const XML_PATH_EMAIL_REQUIRED_CREATE_ORDER = 'customer/create_account/email_required_create_order'; /** * Quote session object * @@ -1368,7 +1369,7 @@ protected function _setQuoteAddress(\Magento\Quote\Model\Quote\Address $address, $data = isset($data['region']) && is_array($data['region']) ? array_merge($data, $data['region']) : $data; $addressForm = $this->_metadataFormFactory->create( - + AddressMetadataInterface::ENTITY_TYPE_ADDRESS, 'adminhtml_customer_address', $data, @@ -2036,7 +2037,47 @@ protected function _validate() */ protected function _getNewCustomerEmail() { - return $this->getData('account/email'); + $email = $this->getData('account/email'); + + if ($email || $this->isEmailRequired()) { + return $email; + } + + return $this->generateEmail(); + } + + /** + * Check email is require + * + * @return bool + */ + private function isEmailRequired(): bool + { + return (bool)$this->_scopeConfig->getValue( + self::XML_PATH_EMAIL_REQUIRED_CREATE_ORDER, + \Magento\Store\Model\ScopeInterface::SCOPE_STORE, + $this->_session->getStore()->getId() + ); + } + + /** + * Generate Email + * + * @return string + */ + private function generateEmail(): string + { + $host = $this->_scopeConfig->getValue( + self::XML_PATH_DEFAULT_EMAIL_DOMAIN, + \Magento\Store\Model\ScopeInterface::SCOPE_STORE + ); + $account = time(); + $email = $account . '@' . $host; + $account = $this->getData('account'); + $account['email'] = $email; + $this->setData('account', $account); + + return $email; } /** diff --git a/app/code/Magento/Sales/Model/Order.php b/app/code/Magento/Sales/Model/Order.php index 89564f97ccf16..598b204a33097 100644 --- a/app/code/Magento/Sales/Model/Order.php +++ b/app/code/Magento/Sales/Model/Order.php @@ -51,7 +51,7 @@ * @method bool hasCustomerNoteNotify() * @method bool hasForcedCanCreditmemo() * @method bool getIsInProcess() - * @method \Magento\Customer\Model\Customer getCustomer() + * @method \Magento\Customer\Model\Customer|null getCustomer() * @method \Magento\Sales\Model\Order setSendEmail(bool $value) * @SuppressWarnings(PHPMD.ExcessivePublicCount) * @SuppressWarnings(PHPMD.TooManyFields) @@ -858,7 +858,7 @@ public function canEdit() */ public function canReorder() { - return $this->_canReorder(false); + return $this->_canReorder(); } /** @@ -1326,7 +1326,7 @@ public function getTrackingNumbers() */ public function getShippingMethod($asObject = false) { - $shippingMethod = parent::getShippingMethod(); + $shippingMethod = $this->getData('shipping_method'); if (!$asObject || !$shippingMethod) { return $shippingMethod; } else { diff --git a/app/code/Magento/Sales/Model/Order/Creditmemo/Total/Discount.php b/app/code/Magento/Sales/Model/Order/Creditmemo/Total/Discount.php index 06bfbcf24daac..43a66382aa1c2 100644 --- a/app/code/Magento/Sales/Model/Order/Creditmemo/Total/Discount.php +++ b/app/code/Magento/Sales/Model/Order/Creditmemo/Total/Discount.php @@ -5,17 +5,38 @@ */ namespace Magento\Sales\Model\Order\Creditmemo\Total; +use Magento\Tax\Model\Config; + /** * Discount total calculator */ class Discount extends AbstractTotal { + /** + * @var Config + */ + private $taxConfig; + + /** + * @param Config $taxConfig + * @param array $data + */ + public function __construct( + Config $taxConfig, + array $data = [] + ) { + $this->taxConfig = $taxConfig; + + parent::__construct($data); + } + /** * Collect discount * * @param \Magento\Sales\Model\Order\Creditmemo $creditmemo * @return $this * @throws \Magento\Framework\Exception\LocalizedException + * @SuppressWarnings(PHPMD.CyclomaticComplexity) */ public function collect(\Magento\Sales\Model\Order\Creditmemo $creditmemo) { @@ -31,7 +52,7 @@ public function collect(\Magento\Sales\Model\Order\Creditmemo $creditmemo) * Calculate how much shipping discount should be applied * basing on how much shipping should be refunded. */ - $baseShippingAmount = $this->getBaseShippingAmount($creditmemo); + $baseShippingAmount = $this->getBaseShippingAmount($creditmemo, $order); /** * If credit memo's shipping amount is set and Order's shipping amount is 0, @@ -43,10 +64,14 @@ public function collect(\Magento\Sales\Model\Order\Creditmemo $creditmemo) ); } if ($baseShippingAmount) { + $orderBaseShippingAmount = $this->isShippingInclTax((int)$order->getStoreId()) ? + $order->getBaseShippingInclTax() : $order->getBaseShippingAmount(); + $orderShippingAmount = $this->isShippingInclTax((int)$order->getStoreId()) ? + $order->getShippingInclTax() : $order->getShippingAmount(); $baseShippingDiscount = $baseShippingAmount * $order->getBaseShippingDiscountAmount() / - $order->getBaseShippingAmount(); - $shippingDiscount = $order->getShippingAmount() * $baseShippingDiscount / $order->getBaseShippingAmount(); + $orderBaseShippingAmount; + $shippingDiscount = $orderShippingAmount * $baseShippingDiscount / $orderBaseShippingAmount; $totalDiscountAmount = $totalDiscountAmount + $shippingDiscount; $baseTotalDiscountAmount = $baseTotalDiscountAmount + $baseShippingDiscount; } @@ -104,8 +129,20 @@ private function getBaseShippingAmount(\Magento\Sales\Model\Order\Creditmemo $cr if (!$baseShippingAmount) { $baseShippingInclTax = (float)$creditmemo->getBaseShippingInclTax(); $baseShippingTaxAmount = (float)$creditmemo->getBaseShippingTaxAmount(); - $baseShippingAmount = $baseShippingInclTax - $baseShippingTaxAmount; + $baseShippingAmount = $this->isShippingInclTax((int)$creditmemo->getStoreId()) ? + $baseShippingInclTax : $baseShippingInclTax - $baseShippingTaxAmount; } return $baseShippingAmount; } + + /** + * Returns whether the user specified a shipping amount that already includes tax + * + * @param int $storeId + * @return bool + */ + private function isShippingInclTax(int $storeId): bool + { + return (bool)$this->taxConfig->displaySalesShippingInclTax($storeId); + } } diff --git a/app/code/Magento/Sales/Model/Order/CreditmemoFactory.php b/app/code/Magento/Sales/Model/Order/CreditmemoFactory.php index 73afd0a06f710..8138e193e7978 100644 --- a/app/code/Magento/Sales/Model/Order/CreditmemoFactory.php +++ b/app/code/Magento/Sales/Model/Order/CreditmemoFactory.php @@ -6,10 +6,15 @@ namespace Magento\Sales\Model\Order; use Magento\Bundle\Ui\DataProvider\Product\Listing\Collector\BundlePrice; +use Magento\Framework\App\ObjectManager; +use Magento\Framework\Locale\FormatInterface; +use Magento\Framework\Serialize\Serializer\Json as JsonSerializer; use Magento\Sales\Api\Data\OrderItemInterface; /** * Factory class for @see \Magento\Sales\Model\Order\Creditmemo + * + * @SuppressWarnings(PHPMD.CouplingBetweenObjects) */ class CreditmemoFactory { @@ -32,7 +37,12 @@ class CreditmemoFactory protected $unserialize; /** - * @var \Magento\Framework\Serialize\Serializer\Json + * @var FormatInterface + */ + private $localeFormat; + + /** + * @var JsonSerializer */ private $serializer; @@ -41,18 +51,19 @@ class CreditmemoFactory * * @param \Magento\Sales\Model\Convert\OrderFactory $convertOrderFactory * @param \Magento\Tax\Model\Config $taxConfig - * @param \Magento\Framework\Serialize\Serializer\Json $serializer + * @param JsonSerializer $serializer + * @param FormatInterface $localeFormat */ public function __construct( \Magento\Sales\Model\Convert\OrderFactory $convertOrderFactory, \Magento\Tax\Model\Config $taxConfig, - \Magento\Framework\Serialize\Serializer\Json $serializer = null + JsonSerializer $serializer = null, + FormatInterface $localeFormat = null ) { $this->convertor = $convertOrderFactory->create(); $this->taxConfig = $taxConfig; - $this->serializer = $serializer ?: \Magento\Framework\App\ObjectManager::getInstance()->get( - \Magento\Framework\Serialize\Serializer\Json::class - ); + $this->serializer = $serializer ?: ObjectManager::getInstance()->get(JsonSerializer::class); + $this->localeFormat = $localeFormat ?: ObjectManager::getInstance()->get(FormatInterface::class); } /** @@ -166,6 +177,7 @@ protected function canRefundItem($item, $qtys = [], $invoiceQtysRefundLimits = [ return isset($qtys[$parent->getId()]) && $qtys[$parent->getId()] > 0; } } + return false; } else { return $this->canRefundNoDummyItem($item, $invoiceQtysRefundLimits); } @@ -199,14 +211,17 @@ protected function canRefundNoDummyItem($item, $invoiceQtysRefundLimits = []) protected function initData($creditmemo, $data) { if (isset($data['shipping_amount'])) { - $creditmemo->setBaseShippingAmount((double)$data['shipping_amount']); - $creditmemo->setBaseShippingInclTax((double)$data['shipping_amount']); + $shippingAmount = $this->parseNumber($data['shipping_amount']); + $creditmemo->setBaseShippingAmount($shippingAmount); + $creditmemo->setBaseShippingInclTax($shippingAmount); } if (isset($data['adjustment_positive'])) { - $creditmemo->setAdjustmentPositive($data['adjustment_positive']); + $adjustmentPositiveAmount = $this->parseAdjustmentAmount($data['adjustment_positive']); + $creditmemo->setAdjustmentPositive($adjustmentPositiveAmount); } if (isset($data['adjustment_negative'])) { - $creditmemo->setAdjustmentNegative($data['adjustment_negative']); + $adjustmentNegativeAmount = $this->parseAdjustmentAmount($data['adjustment_negative']); + $creditmemo->setAdjustmentNegative($adjustmentNegativeAmount); } } @@ -340,4 +355,32 @@ private function getShippingAmount(Invoice $invoice): float return (float)$amount; } + + /** + * Parse adjustment amount value to number + * + * @param string|null $amount + * + * @return float|null + */ + private function parseAdjustmentAmount($amount) + { + $amount = trim($amount); + $percentAmount = substr($amount, -1) == '%'; + $amount = $this->parseNumber($amount); + + return $percentAmount ? $amount . '%' : $amount; + } + + /** + * Parse value to number + * + * @param string|null $value + * + * @return float|null + */ + private function parseNumber($value) + { + return $this->localeFormat->getNumber($value); + } } diff --git a/app/code/Magento/Sales/Model/Order/Email/Sender/CreditmemoSender.php b/app/code/Magento/Sales/Model/Order/Email/Sender/CreditmemoSender.php index e6d528fb93a34..c27afe9fb5b0d 100644 --- a/app/code/Magento/Sales/Model/Order/Email/Sender/CreditmemoSender.php +++ b/app/code/Magento/Sales/Model/Order/Email/Sender/CreditmemoSender.php @@ -17,7 +17,7 @@ use Magento\Framework\DataObject; /** - * Class CreditmemoSender + * Sends order creditmemo email to the customer. * * @SuppressWarnings(PHPMD.CouplingBetweenObjects) */ @@ -108,7 +108,9 @@ public function send(Creditmemo $creditmemo, $forceSyncMode = false) $transport = [ 'order' => $order, + 'order_id' => $order->getId(), 'creditmemo' => $creditmemo, + 'creditmemo_id' => $creditmemo->getId(), 'comment' => $creditmemo->getCustomerNoteNotify() ? $creditmemo->getCustomerNote() : '', 'billing' => $order->getBillingAddress(), 'payment_html' => $this->getPaymentHtml($order), diff --git a/app/code/Magento/Sales/Model/Order/Email/Sender/InvoiceSender.php b/app/code/Magento/Sales/Model/Order/Email/Sender/InvoiceSender.php index 79133af6d6fb8..05164d1b7b5f3 100644 --- a/app/code/Magento/Sales/Model/Order/Email/Sender/InvoiceSender.php +++ b/app/code/Magento/Sales/Model/Order/Email/Sender/InvoiceSender.php @@ -17,7 +17,7 @@ use Magento\Framework\DataObject; /** - * Class InvoiceSender + * Sends order invoice email to the customer. * * @SuppressWarnings(PHPMD.CouplingBetweenObjects) */ @@ -108,7 +108,9 @@ public function send(Invoice $invoice, $forceSyncMode = false) $transport = [ 'order' => $order, + 'order_id' => $order->getId(), 'invoice' => $invoice, + 'invoice_id' => $invoice->getId(), 'comment' => $invoice->getCustomerNoteNotify() ? $invoice->getCustomerNote() : '', 'billing' => $order->getBillingAddress(), 'payment_html' => $this->getPaymentHtml($order), diff --git a/app/code/Magento/Sales/Model/Order/Email/Sender/OrderSender.php b/app/code/Magento/Sales/Model/Order/Email/Sender/OrderSender.php index c67804475cd65..a2d61c3b2d31d 100644 --- a/app/code/Magento/Sales/Model/Order/Email/Sender/OrderSender.php +++ b/app/code/Magento/Sales/Model/Order/Email/Sender/OrderSender.php @@ -16,7 +16,8 @@ use Magento\Framework\DataObject; /** - * Class OrderSender + * Sends order email to the customer. + * * @SuppressWarnings(PHPMD.CouplingBetweenObjects) */ class OrderSender extends Sender @@ -125,6 +126,7 @@ protected function prepareTemplate(Order $order) { $transport = [ 'order' => $order, + 'order_id' => $order->getId(), 'billing' => $order->getBillingAddress(), 'payment_html' => $this->getPaymentHtml($order), 'store' => $order->getStore(), diff --git a/app/code/Magento/Sales/Model/Order/Email/Sender/ShipmentSender.php b/app/code/Magento/Sales/Model/Order/Email/Sender/ShipmentSender.php index 4dbc10308f3be..4c8e1744ac0e0 100644 --- a/app/code/Magento/Sales/Model/Order/Email/Sender/ShipmentSender.php +++ b/app/code/Magento/Sales/Model/Order/Email/Sender/ShipmentSender.php @@ -17,8 +17,10 @@ use Magento\Framework\DataObject; /** - * Class ShipmentSender + * Sends order shipment email to the customer. * + * @deprecated since this class works only with the concrete model and no data interface + * @see \Magento\Sales\Model\Order\Shipment\Sender\EmailSender * @SuppressWarnings(PHPMD.CouplingBetweenObjects) */ class ShipmentSender extends Sender @@ -108,7 +110,9 @@ public function send(Shipment $shipment, $forceSyncMode = false) $transport = [ 'order' => $order, + 'order_id' => $order->getId(), 'shipment' => $shipment, + 'shipment_id' => $shipment->getId(), 'comment' => $shipment->getCustomerNoteNotify() ? $shipment->getCustomerNote() : '', 'billing' => $order->getBillingAddress(), 'payment_html' => $this->getPaymentHtml($order), diff --git a/app/code/Magento/Sales/Model/Order/Item.php b/app/code/Magento/Sales/Model/Order/Item.php index a0eff45179ac8..c133d3aea267d 100644 --- a/app/code/Magento/Sales/Model/Order/Item.php +++ b/app/code/Magento/Sales/Model/Order/Item.php @@ -385,6 +385,8 @@ public function getStatus() * * @param string $statusId * @return \Magento\Framework\Phrase + * + * phpcs:disable Magento2.Functions.StaticFunction */ public static function getStatusName($statusId) { @@ -422,6 +424,8 @@ public function cancel() * Retrieve order item statuses array * * @return array + * + * phpcs:disable Magento2.Functions.StaticFunction */ public static function getStatuses() { @@ -1319,7 +1323,13 @@ public function getParentItemId() */ public function getPrice() { - return $this->getData(OrderItemInterface::PRICE); + $price = $this->getData(OrderItemInterface::PRICE); + + if ($price === null) { + return $price; + } + + return (float) $price; } /** diff --git a/app/code/Magento/Sales/Model/Order/Payment.php b/app/code/Magento/Sales/Model/Order/Payment.php index dcf6d86b44cae..3076c6dfd2ba7 100644 --- a/app/code/Magento/Sales/Model/Order/Payment.php +++ b/app/code/Magento/Sales/Model/Order/Payment.php @@ -8,10 +8,12 @@ use Magento\Framework\App\ObjectManager; use Magento\Framework\Pricing\PriceCurrencyInterface; +use Magento\Payment\Model\SaleOperationInterface; use Magento\Sales\Api\Data\OrderPaymentInterface; use Magento\Sales\Api\OrderRepositoryInterface; use Magento\Sales\Model\Order; use Magento\Sales\Model\Order\Payment\Info; +use Magento\Sales\Model\Order\Payment\Operations\SaleOperation; use Magento\Sales\Model\Order\Payment\Transaction; use Magento\Sales\Model\Order\Payment\Transaction\ManagerInterface; use Magento\Sales\Api\CreditmemoManagementInterface as CreditmemoManager; @@ -115,6 +117,11 @@ class Payment extends Info implements OrderPaymentInterface */ private $creditmemoManager = null; + /** + * @var SaleOperation + */ + private $saleOperation; + /** * @param \Magento\Framework\Model\Context $context * @param \Magento\Framework\Registry $registry @@ -133,6 +140,7 @@ class Payment extends Info implements OrderPaymentInterface * @param \Magento\Framework\Data\Collection\AbstractDb $resourceCollection * @param array $data * @param CreditmemoManager $creditmemoManager + * @param SaleOperation $saleOperation * @SuppressWarnings(PHPMD.ExcessiveParameterList) */ public function __construct( @@ -152,7 +160,8 @@ public function __construct( \Magento\Framework\Model\ResourceModel\AbstractResource $resource = null, \Magento\Framework\Data\Collection\AbstractDb $resourceCollection = null, array $data = [], - CreditmemoManager $creditmemoManager = null + CreditmemoManager $creditmemoManager = null, + SaleOperation $saleOperation = null ) { $this->priceCurrency = $priceCurrency; $this->creditmemoFactory = $creditmemoFactory; @@ -162,6 +171,8 @@ public function __construct( $this->orderPaymentProcessor = $paymentProcessor; $this->orderRepository = $orderRepository; $this->creditmemoManager = $creditmemoManager ?: ObjectManager::getInstance()->get(CreditmemoManager::class); + $this->saleOperation = $saleOperation ?: ObjectManager::getInstance()->get(SaleOperation::class); + parent::__construct( $context, $registry, @@ -451,7 +462,11 @@ protected function processAction($action, Order $order) case \Magento\Payment\Model\Method\AbstractMethod::ACTION_AUTHORIZE_CAPTURE: $this->setAmountAuthorized($totalDue); $this->setBaseAmountAuthorized($baseTotalDue); - $this->capture(null); + if ($this->canSale()) { + $this->saleOperation->execute($this); + } else { + $this->capture(null); + } break; default: break; @@ -2572,4 +2587,16 @@ private function collectTotalAmounts(Order $order, array $keys) return $result; } + + /** + * Check sale operation availability for payment method. + * + * @return bool + */ + private function canSale(): bool + { + $method = $this->getMethodInstance(); + + return $method instanceof SaleOperationInterface && $method->canSale(); + } } diff --git a/app/code/Magento/Sales/Model/Order/Payment/Info.php b/app/code/Magento/Sales/Model/Order/Payment/Info.php index fee846fe6a62c..479d96b5842d9 100644 --- a/app/code/Magento/Sales/Model/Order/Payment/Info.php +++ b/app/code/Magento/Sales/Model/Order/Payment/Info.php @@ -192,6 +192,7 @@ public function getAdditionalInformation($key = null) */ public function unsAdditionalInformation($key = null) { + $this->initAdditionalInformation(); if ($key && isset($this->additionalInformation[$key])) { unset($this->additionalInformation[$key]); return $this->setData('additional_information', $this->additionalInformation); diff --git a/app/code/Magento/Sales/Model/Order/Payment/Operations/CaptureOperation.php b/app/code/Magento/Sales/Model/Order/Payment/Operations/CaptureOperation.php index 3784ba0504be6..34193f06f7a6a 100644 --- a/app/code/Magento/Sales/Model/Order/Payment/Operations/CaptureOperation.php +++ b/app/code/Magento/Sales/Model/Order/Payment/Operations/CaptureOperation.php @@ -9,10 +9,45 @@ use Magento\Sales\Api\Data\OrderPaymentInterface; use Magento\Sales\Model\Order; use Magento\Sales\Model\Order\Payment; -use Magento\Sales\Model\Order\Payment\Transaction; +use Magento\Framework\Event\ManagerInterface as EventManagerInterface; +use Magento\Sales\Model\Order\Payment\State\CommandInterface; +use Magento\Sales\Model\Order\Payment\Transaction\BuilderInterface; +use Magento\Sales\Model\Order\Payment\Transaction\ManagerInterface; +/** + * Capture operation implementation. + */ class CaptureOperation extends AbstractOperation { + /** + * @var ProcessInvoiceOperation + */ + private $processInvoiceOperation; + + /** + * @param CommandInterface $stateCommand + * @param BuilderInterface $transactionBuilder + * @param ManagerInterface $transactionManager + * @param EventManagerInterface $eventManager + * @param ProcessInvoiceOperation $processInvoiceOperation + */ + public function __construct( + CommandInterface $stateCommand, + BuilderInterface $transactionBuilder, + ManagerInterface $transactionManager, + EventManagerInterface $eventManager, + ProcessInvoiceOperation $processInvoiceOperation + ) { + $this->processInvoiceOperation = $processInvoiceOperation; + + parent::__construct( + $stateCommand, + $transactionBuilder, + $transactionManager, + $eventManager + ); + } + /** * Captures payment. * @@ -34,81 +69,7 @@ public function capture(OrderPaymentInterface $payment, $invoice) } return $payment; } - $amountToCapture = $payment->formatAmount($invoice->getBaseGrandTotal()); - $order = $payment->getOrder(); - - $payment->setTransactionId( - $this->transactionManager->generateTransactionId( - $payment, - Transaction::TYPE_CAPTURE, - $payment->getAuthorizationTransaction() - ) - ); - - $this->eventManager->dispatch( - 'sales_order_payment_capture', - ['payment' => $payment, 'invoice' => $invoice] - ); - - /** - * Fetch an update about existing transaction. It can determine whether the transaction can be paid - * Capture attempt will happen only when invoice is not yet paid and the transaction can be paid - */ - if ($invoice->getTransactionId()) { - $method = $payment->getMethodInstance(); - $method->setStore( - $order->getStoreId() - ); - if ($method->canFetchTransactionInfo()) { - $method->fetchTransactionInfo( - $payment, - $invoice->getTransactionId() - ); - } - } - - if ($invoice->getIsPaid()) { - throw new \Magento\Framework\Exception\LocalizedException( - __('The transaction "%1" cannot be captured yet.', $invoice->getTransactionId()) - ); - } - - // attempt to capture: this can trigger "is_transaction_pending" - $method = $payment->getMethodInstance(); - $method->setStore( - $order->getStoreId() - ); - //TODO replace for sale usage - $method->capture($payment, $amountToCapture); - - // prepare parent transaction and its amount - $paidWorkaround = 0; - if (!$invoice->wasPayCalled()) { - $paidWorkaround = (double)$amountToCapture; - } - if ($payment->isCaptureFinal($paidWorkaround)) { - $payment->setShouldCloseParentTransaction(true); - } - - $transactionBuilder = $this->transactionBuilder->setPayment($payment); - $transactionBuilder->setOrder($order); - $transactionBuilder->setFailSafe(true); - $transactionBuilder->setTransactionId($payment->getTransactionId()); - $transactionBuilder->setAdditionalInformation($payment->getTransactionAdditionalInfo()); - $transactionBuilder->setSalesDocument($invoice); - $transaction = $transactionBuilder->build(Transaction::TYPE_CAPTURE); - - $message = $this->stateCommand->execute($payment, $amountToCapture, $order); - if ($payment->getIsTransactionPending()) { - $invoice->setIsPaid(false); - } else { - $invoice->setIsPaid(true); - $this->updateTotals($payment, ['base_amount_paid_online' => $amountToCapture]); - } - $message = $payment->prependMessage($message); - $payment->addTransactionCommentsToOrder($transaction, $message); - $invoice->setTransactionId($payment->getLastTransId()); - return $payment; + return $this->processInvoiceOperation->execute($payment, $invoice, 'capture'); } } diff --git a/app/code/Magento/Sales/Model/Order/Payment/Operations/ProcessInvoiceOperation.php b/app/code/Magento/Sales/Model/Order/Payment/Operations/ProcessInvoiceOperation.php new file mode 100644 index 0000000000000..83565bbb1698e --- /dev/null +++ b/app/code/Magento/Sales/Model/Order/Payment/Operations/ProcessInvoiceOperation.php @@ -0,0 +1,115 @@ +formatAmount($invoice->getBaseGrandTotal(), true); + $order = $payment->getOrder(); + + $payment->setTransactionId( + $this->transactionManager->generateTransactionId( + $payment, + Transaction::TYPE_CAPTURE, + $payment->getAuthorizationTransaction() + ) + ); + + $this->eventManager->dispatch( + 'sales_order_payment_capture', + ['payment' => $payment, 'invoice' => $invoice] + ); + + /** + * Fetch an update about existing transaction. It can determine whether the transaction can be paid + * Capture attempt will happen only when invoice is not yet paid and the transaction can be paid + */ + if ($invoice->getTransactionId()) { + $method = $payment->getMethodInstance(); + $method->setStore( + $order->getStoreId() + ); + if ($method->canFetchTransactionInfo()) { + $method->fetchTransactionInfo( + $payment, + $invoice->getTransactionId() + ); + } + } + + if ($invoice->getIsPaid()) { + throw new LocalizedException( + __('The transaction "%1" cannot be captured yet.', $invoice->getTransactionId()) + ); + } + + // attempt to capture: this can trigger "is_transaction_pending" + $method = $payment->getMethodInstance(); + $method->setStore( + $order->getStoreId() + ); + + $method->$operationMethod($payment, $amountToCapture); + + // prepare parent transaction and its amount + $paidWorkaround = 0; + if (!$invoice->wasPayCalled()) { + $paidWorkaround = (double)$amountToCapture; + } + if ($payment->isCaptureFinal($paidWorkaround)) { + $payment->setShouldCloseParentTransaction(true); + } + + $transactionBuilder = $this->transactionBuilder->setPayment($payment); + $transactionBuilder->setOrder($order); + $transactionBuilder->setFailSafe(true); + $transactionBuilder->setTransactionId($payment->getTransactionId()); + $transactionBuilder->setAdditionalInformation($payment->getTransactionAdditionalInfo()); + $transactionBuilder->setSalesDocument($invoice); + $transaction = $transactionBuilder->build(Transaction::TYPE_CAPTURE); + + $message = $this->stateCommand->execute($payment, $amountToCapture, $order); + if ($payment->getIsTransactionPending()) { + $invoice->setIsPaid(false); + } else { + $invoice->setIsPaid(true); + $this->updateTotals($payment, ['base_amount_paid_online' => $amountToCapture]); + } + $message = $payment->prependMessage($message); + $payment->addTransactionCommentsToOrder($transaction, $message); + $invoice->setTransactionId($payment->getLastTransId()); + + return $payment; + } +} diff --git a/app/code/Magento/Sales/Model/Order/Payment/Operations/SaleOperation.php b/app/code/Magento/Sales/Model/Order/Payment/Operations/SaleOperation.php new file mode 100644 index 0000000000000..7ff271078eda6 --- /dev/null +++ b/app/code/Magento/Sales/Model/Order/Payment/Operations/SaleOperation.php @@ -0,0 +1,58 @@ +processInvoiceOperation = $processInvoiceOperation; + } + + /** + * Authorize and Capture payment. + * + * @param OrderPaymentInterface $payment + * @return OrderPaymentInterface + * @throws LocalizedException + */ + public function execute(OrderPaymentInterface $payment): OrderPaymentInterface + { + /** @var $payment Payment */ + $invoice = $payment->getOrder()->prepareInvoice(); + $invoice->register(); + $this->processInvoiceOperation->execute($payment, $invoice, 'sale'); + if ($invoice->getIsPaid()) { + $invoice->pay(); + } + $payment->getOrder()->addRelatedObject($invoice); + $payment->setCreatedInvoice($invoice); + if ($payment->getIsFraudDetected()) { + $payment->getOrder()->setStatus(Order::STATUS_FRAUD); + } + + return $payment; + } +} diff --git a/app/code/Magento/Sales/Model/Order/Payment/State/AuthorizeCommand.php b/app/code/Magento/Sales/Model/Order/Payment/State/AuthorizeCommand.php index 98cf1babdc92d..89731b5130605 100644 --- a/app/code/Magento/Sales/Model/Order/Payment/State/AuthorizeCommand.php +++ b/app/code/Magento/Sales/Model/Order/Payment/State/AuthorizeCommand.php @@ -11,6 +11,9 @@ use Magento\Sales\Model\Order; use Magento\Sales\Model\Order\StatusResolver; +/** + * Process order state and status after authorize operation. + */ class AuthorizeCommand implements CommandInterface { /** @@ -28,6 +31,8 @@ public function __construct(StatusResolver $statusResolver = null) } /** + * Run command. + * * @param OrderPaymentInterface $payment * @param string|float $amount * @param OrderInterface $order @@ -50,6 +55,8 @@ public function execute(OrderPaymentInterface $payment, $amount, OrderInterface $message .= ' Order is suspended as its authorizing amount %1 is suspected to be fraudulent.'; } + $message = $this->getNotificationMessage($payment) ?? $message; + if (!isset($status)) { $status = $this->statusResolver->getOrderStatusByState($order, $state); } @@ -61,12 +68,29 @@ public function execute(OrderPaymentInterface $payment, $amount, OrderInterface } /** - * @deprecated 100.2.0 Replaced by a StatusResolver class call. + * Returns payment notification message. + * + * @param OrderPaymentInterface $payment + * @return string|null + */ + private function getNotificationMessage(OrderPaymentInterface $payment): ?string + { + $extensionAttributes = $payment->getExtensionAttributes(); + if ($extensionAttributes && $extensionAttributes->getNotificationMessage()) { + return $extensionAttributes->getNotificationMessage(); + } + + return null; + } + + /** + * Sets order state and status. * * @param Order $order * @param string $status * @param string $state * @return void + * @deprecated 100.2.0 Replaced by a StatusResolver class call. */ protected function setOrderStateAndStatus(Order $order, $status, $state) { diff --git a/app/code/Magento/Sales/Model/Order/Payment/State/CaptureCommand.php b/app/code/Magento/Sales/Model/Order/Payment/State/CaptureCommand.php index 0e4135a1bf814..f57e1933a7e5a 100644 --- a/app/code/Magento/Sales/Model/Order/Payment/State/CaptureCommand.php +++ b/app/code/Magento/Sales/Model/Order/Payment/State/CaptureCommand.php @@ -11,6 +11,9 @@ use Magento\Sales\Model\Order; use Magento\Sales\Model\Order\StatusResolver; +/** + * Process order state and status after capture operation. + */ class CaptureCommand implements CommandInterface { /** @@ -28,6 +31,8 @@ public function __construct(StatusResolver $statusResolver = null) } /** + * Run command. + * * @param OrderPaymentInterface $payment * @param string|float $amount * @param OrderInterface $order @@ -50,6 +55,8 @@ public function execute(OrderPaymentInterface $payment, $amount, OrderInterface $message .= ' Order is suspended as its capturing amount %1 is suspected to be fraudulent.'; } + $message = $this->getNotificationMessage($payment) ?? $message; + if (!isset($status)) { $status = $this->statusResolver->getOrderStatusByState($order, $state); } @@ -61,12 +68,13 @@ public function execute(OrderPaymentInterface $payment, $amount, OrderInterface } /** - * @deprecated 100.2.0 Replaced by a StatusResolver class call. + * Sets order state and status. * * @param Order $order * @param string $status * @param string $state * @return void + * @deprecated 100.2.0 Replaced by a StatusResolver class call. */ protected function setOrderStateAndStatus(Order $order, $status, $state) { @@ -76,4 +84,20 @@ protected function setOrderStateAndStatus(Order $order, $status, $state) $order->setState($state)->setStatus($status); } + + /** + * Returns payment notification message. + * + * @param OrderPaymentInterface $payment + * @return string|null + */ + private function getNotificationMessage(OrderPaymentInterface $payment): ?string + { + $extensionAttributes = $payment->getExtensionAttributes(); + if ($extensionAttributes && $extensionAttributes->getNotificationMessage()) { + return $extensionAttributes->getNotificationMessage(); + } + + return null; + } } diff --git a/app/code/Magento/Sales/Model/Order/Payment/State/RegisterCaptureNotificationCommand.php b/app/code/Magento/Sales/Model/Order/Payment/State/RegisterCaptureNotificationCommand.php index d38e58d7341c1..2551092a64e9a 100644 --- a/app/code/Magento/Sales/Model/Order/Payment/State/RegisterCaptureNotificationCommand.php +++ b/app/code/Magento/Sales/Model/Order/Payment/State/RegisterCaptureNotificationCommand.php @@ -11,6 +11,11 @@ use Magento\Sales\Model\Order; use Magento\Sales\Model\Order\StatusResolver; +/** + * Class RegisterCaptureNotificationCommand + * + * Command that Register Capture Notification + */ class RegisterCaptureNotificationCommand implements CommandInterface { /** @@ -23,11 +28,12 @@ class RegisterCaptureNotificationCommand implements CommandInterface */ public function __construct(StatusResolver $statusResolver = null) { - $this->statusResolver = $statusResolver - ? : ObjectManager::getInstance()->get(StatusResolver::class); + $this->statusResolver = $statusResolver ?: ObjectManager::getInstance()->get(StatusResolver::class); } /** + * Registers a capture event for this payment + * * @param OrderPaymentInterface $payment * @param string|float|int $amount * @param OrderInterface $order @@ -35,7 +41,11 @@ public function __construct(StatusResolver $statusResolver = null) */ public function execute(OrderPaymentInterface $payment, $amount, OrderInterface $order) { - $state = $order->getState() ?: Order::STATE_PROCESSING; + $state = $order->getState(); + if (!$state || $state === Order::STATE_NEW || $state === Order::STATE_PENDING_PAYMENT) { + $state = Order::STATE_PROCESSING; + } + $status = null; $message = 'Registered notification about captured amount of %1.'; @@ -61,6 +71,8 @@ public function execute(OrderPaymentInterface $payment, $amount, OrderInterface } /** + * Sets the state and status of the order + * * @deprecated 100.2.0 Replaced by a StatusResolver class call. * * @param Order $order diff --git a/app/code/Magento/Sales/Model/Order/Pdf/Items/AbstractItems.php b/app/code/Magento/Sales/Model/Order/Pdf/Items/AbstractItems.php index 422ff1746c9a6..29e011217ef20 100644 --- a/app/code/Magento/Sales/Model/Order/Pdf/Items/AbstractItems.php +++ b/app/code/Magento/Sales/Model/Order/Pdf/Items/AbstractItems.php @@ -6,41 +6,54 @@ namespace Magento\Sales\Model\Order\Pdf\Items; use Magento\Framework\App\Filesystem\DirectoryList; +use Magento\Framework\Data\Collection\AbstractDb; +use Magento\Framework\DataObject; +use Magento\Framework\Filesystem; +use Magento\Framework\Filesystem\Directory\ReadInterface; +use Magento\Framework\Filter\FilterManager; +use Magento\Framework\Model\AbstractModel; +use Magento\Framework\Model\Context; +use Magento\Framework\Model\ResourceModel\AbstractResource; +use Magento\Framework\Registry; +use Magento\Sales\Model\Order; +use Magento\Sales\Model\Order\Pdf\AbstractPdf; +use Magento\Tax\Helper\Data as TaxHelper; /** * Sales Order Pdf Items renderer Abstract * + * phpcs:disable Magento2.Classes.AbstractApi * @api * @SuppressWarnings(PHPMD.CouplingBetweenObjects) * @since 100.0.2 */ -abstract class AbstractItems extends \Magento\Framework\Model\AbstractModel +abstract class AbstractItems extends AbstractModel { /** * Order model * - * @var \Magento\Sales\Model\Order + * @var Order */ protected $_order; /** * Source model (invoice, shipment, creditmemo) * - * @var \Magento\Framework\Model\AbstractModel + * @var AbstractModel */ protected $_source; /** * Item object * - * @var \Magento\Framework\DataObject + * @var DataObject */ protected $_item; /** * Pdf object * - * @var \Magento\Sales\Model\Order\Pdf\AbstractPdf + * @var AbstractPdf */ protected $_pdf; @@ -54,38 +67,38 @@ abstract class AbstractItems extends \Magento\Framework\Model\AbstractModel /** * Tax data * - * @var \Magento\Tax\Helper\Data + * @var TaxHelper */ protected $_taxData; /** - * @var \Magento\Framework\Filesystem\Directory\ReadInterface + * @var ReadInterface */ protected $_rootDirectory; /** - * @var \Magento\Framework\Filter\FilterManager + * @var FilterManager */ protected $filterManager; /** - * @param \Magento\Framework\Model\Context $context - * @param \Magento\Framework\Registry $registry - * @param \Magento\Tax\Helper\Data $taxData - * @param \Magento\Framework\Filesystem $filesystem , - * @param \Magento\Framework\Filter\FilterManager $filterManager - * @param \Magento\Framework\Model\ResourceModel\AbstractResource $resource - * @param \Magento\Framework\Data\Collection\AbstractDb $resourceCollection + * @param Context $context + * @param Registry $registry + * @param TaxHelper $taxData + * @param Filesystem $filesystem , + * @param FilterManager $filterManager + * @param AbstractResource $resource + * @param AbstractDb $resourceCollection * @param array $data */ public function __construct( - \Magento\Framework\Model\Context $context, - \Magento\Framework\Registry $registry, - \Magento\Tax\Helper\Data $taxData, - \Magento\Framework\Filesystem $filesystem, - \Magento\Framework\Filter\FilterManager $filterManager, - \Magento\Framework\Model\ResourceModel\AbstractResource $resource = null, - \Magento\Framework\Data\Collection\AbstractDb $resourceCollection = null, + Context $context, + Registry $registry, + TaxHelper $taxData, + Filesystem $filesystem, + FilterManager $filterManager, + AbstractResource $resource = null, + AbstractDb $resourceCollection = null, array $data = [] ) { $this->filterManager = $filterManager; @@ -97,10 +110,10 @@ public function __construct( /** * Set order model * - * @param \Magento\Sales\Model\Order $order + * @param Order $order * @return $this */ - public function setOrder(\Magento\Sales\Model\Order $order) + public function setOrder(Order $order) { $this->_order = $order; return $this; @@ -109,10 +122,10 @@ public function setOrder(\Magento\Sales\Model\Order $order) /** * Set Source model * - * @param \Magento\Framework\Model\AbstractModel $source + * @param AbstractModel $source * @return $this */ - public function setSource(\Magento\Framework\Model\AbstractModel $source) + public function setSource(AbstractModel $source) { $this->_source = $source; return $this; @@ -121,10 +134,10 @@ public function setSource(\Magento\Framework\Model\AbstractModel $source) /** * Set item object * - * @param \Magento\Framework\DataObject $item + * @param DataObject $item * @return $this */ - public function setItem(\Magento\Framework\DataObject $item) + public function setItem(DataObject $item) { $this->_item = $item; return $this; @@ -133,10 +146,10 @@ public function setItem(\Magento\Framework\DataObject $item) /** * Set Pdf model * - * @param \Magento\Sales\Model\Order\Pdf\AbstractPdf $pdf + * @param AbstractPdf $pdf * @return $this */ - public function setPdf(\Magento\Sales\Model\Order\Pdf\AbstractPdf $pdf) + public function setPdf(AbstractPdf $pdf) { $this->_pdf = $pdf; return $this; @@ -313,20 +326,20 @@ public function getItemPricesForDisplay() */ public function getItemOptions() { - $result = []; + $result = [[]]; $options = $this->getItem()->getOrderItem()->getProductOptions(); if ($options) { if (isset($options['options'])) { - $result = array_merge($result, $options['options']); + $result[] = $options['options']; } if (isset($options['additional_options'])) { - $result = array_merge($result, $options['additional_options']); + $result[] = $options['additional_options']; } if (isset($options['attributes_info'])) { - $result = array_merge($result, $options['attributes_info']); + $result[] = $options['attributes_info']; } } - return $result; + return array_merge(...$result); } /** diff --git a/app/code/Magento/Sales/Model/Order/ProductOption.php b/app/code/Magento/Sales/Model/Order/ProductOption.php index dc9ec42e27e60..9a4f847b135e7 100644 --- a/app/code/Magento/Sales/Model/Order/ProductOption.php +++ b/app/code/Magento/Sales/Model/Order/ProductOption.php @@ -59,6 +59,7 @@ public function add(OrderItemInterface $orderItem): void { /** @var DataObject $request */ $request = $orderItem->getBuyRequest(); + $request->setProductOptions($orderItem->getProductOptions()); $productType = $orderItem->getProductType(); if (isset($this->processorPool[$productType]) diff --git a/app/code/Magento/Sales/Model/Order/Shipment/Sender/EmailSender.php b/app/code/Magento/Sales/Model/Order/Shipment/Sender/EmailSender.php index 1d4418c50047d..fe68555d9f7c7 100644 --- a/app/code/Magento/Sales/Model/Order/Shipment/Sender/EmailSender.php +++ b/app/code/Magento/Sales/Model/Order/Shipment/Sender/EmailSender.php @@ -104,7 +104,9 @@ public function send( $transport = [ 'order' => $order, + 'order_id' => $order->getId(), 'shipment' => $shipment, + 'shipment_id' => $shipment->getId(), 'comment' => $comment ? $comment->getComment() : '', 'billing' => $order->getBillingAddress(), 'payment_html' => $this->getPaymentHtml($order), diff --git a/app/code/Magento/Sales/Model/Reorder/Data/Error.php b/app/code/Magento/Sales/Model/Reorder/Data/Error.php new file mode 100644 index 0000000000000..10332b7ccaadd --- /dev/null +++ b/app/code/Magento/Sales/Model/Reorder/Data/Error.php @@ -0,0 +1,54 @@ +message = $message; + $this->code = $code; + } + + /** + * Get error message + * + * @return string + */ + public function getMessage(): string + { + return $this->message; + } + + /** + * Get error code + * + * @return string + */ + public function getCode(): string + { + return $this->code; + } +} diff --git a/app/code/Magento/Sales/Model/Reorder/Data/ReorderOutput.php b/app/code/Magento/Sales/Model/Reorder/Data/ReorderOutput.php new file mode 100644 index 0000000000000..70b7ddbe0d1a7 --- /dev/null +++ b/app/code/Magento/Sales/Model/Reorder/Data/ReorderOutput.php @@ -0,0 +1,55 @@ +cart = $cart; + $this->errors = $errors; + } + + /** + * Get Shopping Cart + * + * @return CartInterface + */ + public function getCart(): CartInterface + { + return $this->cart; + } + + /** + * Get errors happened during reorder + * + * @return Error[] + */ + public function getErrors(): array + { + return $this->errors; + } +} diff --git a/app/code/Magento/Sales/Model/Reorder/Reorder.php b/app/code/Magento/Sales/Model/Reorder/Reorder.php new file mode 100644 index 0000000000000..21f5b6944e640 --- /dev/null +++ b/app/code/Magento/Sales/Model/Reorder/Reorder.php @@ -0,0 +1,270 @@ + self::ERROR_NOT_SALABLE, + 'Product that you are trying to add is not available' => self::ERROR_NOT_SALABLE, + 'This product is out of stock' => self::ERROR_NOT_SALABLE, + 'There are no source items' => self::ERROR_NOT_SALABLE, + 'The fewest you may purchase is' => self::ERROR_INSUFFICIENT_STOCK, + 'The most you may purchase is' => self::ERROR_INSUFFICIENT_STOCK, + 'The requested qty is not available' => self::ERROR_INSUFFICIENT_STOCK, + ]; + + /** + * @var OrderFactory + */ + private $orderFactory; + + /** + * @var ReorderHelper + */ + private $reorderHelper; + + /** + * @var \Psr\Log\LoggerInterface + */ + private $logger; + + /** + * @var CartRepositoryInterface + */ + private $cartRepository; + + /** + * @var ProductRepositoryInterface + */ + private $productRepository; + + /** + * @var Data\Error[] + */ + private $errors = []; + + /** + * @var CustomerCartResolver + */ + private $customerCartProvider; + + /** + * @param OrderFactory $orderFactory + * @param CustomerCartResolver $customerCartProvider + * @param CartRepositoryInterface $cartRepository + * @param ProductRepositoryInterface $productRepository + * @param ReorderHelper $reorderHelper + * @param \Psr\Log\LoggerInterface $logger + */ + public function __construct( + OrderFactory $orderFactory, + CustomerCartResolver $customerCartProvider, + CartRepositoryInterface $cartRepository, + ProductRepositoryInterface $productRepository, + ReorderHelper $reorderHelper, + \Psr\Log\LoggerInterface $logger + ) { + $this->orderFactory = $orderFactory; + $this->cartRepository = $cartRepository; + $this->productRepository = $productRepository; + $this->reorderHelper = $reorderHelper; + $this->logger = $logger; + $this->customerCartProvider = $customerCartProvider; + } + + /** + * Allows customer quickly to reorder previously added products and put them to the Cart + * + * @param string $orderNumber + * @param string $storeId + * @return Data\ReorderOutput + * @throws InputException Order is not found + * @throws NoSuchEntityException The specified customer does not exist. + * @throws \Magento\Framework\Exception\CouldNotSaveException Could not create customer Cart + */ + public function execute(string $orderNumber, string $storeId): Data\ReorderOutput + { + $order = $this->orderFactory->create()->loadByIncrementIdAndStoreId($orderNumber, $storeId); + + if (!$order->getId()) { + throw new InputException( + __('Cannot find order number "%1" in store "%2"', $orderNumber, $storeId) + ); + } + $customerId = (int)$order->getCustomerId(); + $this->errors = []; + + $cart = $this->customerCartProvider->resolve($customerId); + if (!$this->reorderHelper->isAllowed($order->getStore())) { + $this->addError((string)__('Reorders are not allowed.'), self::ERROR_REORDER_NOT_AVAILABLE); + return $this->prepareOutput($cart); + } + + $items = $order->getItemsCollection(); + foreach ($items as $item) { + $this->addOrderItem($cart, $item); + } + + try { + $this->cartRepository->save($cart); + } catch (\Magento\Framework\Exception\LocalizedException $e) { + // handle exception from \Magento\Quote\Model\QuoteRepository\SaveHandler::save + $this->addError($e->getMessage()); + } + + $cart = $this->cartRepository->get($cart->getId()); + + return $this->prepareOutput($cart); + } + + /** + * Convert order item to quote item + * + * @param \Magento\Quote\Model\Quote $cart + * @param Item $orderItem + * @return void + */ + private function addOrderItem(\Magento\Quote\Model\Quote $cart, $orderItem): void + { + /* @var $orderItem Item */ + if ($orderItem->getParentItem() === null) { + $info = $orderItem->getProductOptionByCode('info_buyRequest'); + $info = new \Magento\Framework\DataObject($info); + $info->setQty($orderItem->getQtyOrdered()); + + try { + /** @var Product $product */ + $product = $this->productRepository->getById($orderItem->getProductId(), false, null, true); + } catch (NoSuchEntityException $e) { + $this->addError( + (string)__('Could not find a product with ID "%1"', $orderItem->getProductId()), + self::ERROR_PRODUCT_NOT_FOUND + ); + return; + } + $addProductResult = null; + try { + $addProductResult = $cart->addProduct($product, $info); + } catch (\Magento\Framework\Exception\LocalizedException $e) { + $this->addError($this->getCartItemErrorMessage($orderItem, $product, $e->getMessage())); + } catch (\Throwable $e) { + $this->logger->critical($e); + $this->addError($this->getCartItemErrorMessage($orderItem, $product), self::ERROR_UNDEFINED); + } + + // error happens in case the result is string + if (is_string($addProductResult)) { + $errors = array_unique(explode("\n", $addProductResult)); + foreach ($errors as $error) { + $this->addError($this->getCartItemErrorMessage($orderItem, $product, $error)); + } + } + } + } + + /** + * Add order line item error + * + * @param string $message + * @param string|null $code + * @return void + */ + private function addError(string $message, string $code = null): void + { + $this->errors[] = new Data\Error( + $message, + $code ?? $this->getErrorCode($message) + ); + } + + /** + * Get message error code. Ad-hoc solution based on message parsing. + * + * @param string $message + * @return string + */ + private function getErrorCode(string $message): string + { + $code = self::ERROR_UNDEFINED; + + $matchedCodes = array_filter( + self::MESSAGE_CODES, + function ($key) use ($message) { + return false !== strpos($message, $key); + }, + ARRAY_FILTER_USE_KEY + ); + + if (!empty($matchedCodes)) { + $code = current($matchedCodes); + } + + return $code; + } + + /** + * Prepare output + * + * @param CartInterface $cart + * @return Data\ReorderOutput + */ + private function prepareOutput(CartInterface $cart): Data\ReorderOutput + { + $output = new Data\ReorderOutput($cart, $this->errors); + $this->errors = []; + // we already show user errors, do not expose it to cart level + $cart->setHasError(false); + return $output; + } + + /** + * Get error message for a cart item + * + * @param Item $item + * @param Product $product + * @param string|null $message + * @return string + */ + private function getCartItemErrorMessage(Item $item, Product $product, string $message = null): string + { + // try to get sku from line-item first. + // for complex product type: if custom option is not available it can cause error + $sku = $item->getSku() ?? $product->getData('sku'); + return (string)($message + ? __('Could not add the product with SKU "%1" to the shopping cart: %2', $sku, $message) + : __('Could not add the product with SKU "%1" to the shopping cart', $sku)); + } +} diff --git a/app/code/Magento/Sales/Model/ResourceModel/Order/Invoice/Grid/Collection.php b/app/code/Magento/Sales/Model/ResourceModel/Order/Invoice/Grid/Collection.php deleted file mode 100644 index 9c261fa31bc25..0000000000000 --- a/app/code/Magento/Sales/Model/ResourceModel/Order/Invoice/Grid/Collection.php +++ /dev/null @@ -1,42 +0,0 @@ -providers = $tmapFactory->create( [ 'array' => $providers, @@ -38,11 +37,11 @@ public function __construct( */ public function getIds($mainTableName, $gridTableName) { - $result = []; + $result = [[]]; foreach ($this->providers as $provider) { - $result = array_merge($result, $provider->getIds($mainTableName, $gridTableName)); + $result[] = $provider->getIds($mainTableName, $gridTableName); } - return array_unique($result); + return array_unique(array_merge(...$result)); } } diff --git a/app/code/Magento/Sales/Test/Mftf/ActionGroup/AdminAddSimpleProductWithCustomOptionFileToOrderActionGroup.xml b/app/code/Magento/Sales/Test/Mftf/ActionGroup/AdminAddSimpleProductWithCustomOptionFileToOrderActionGroup.xml new file mode 100644 index 0000000000000..5b08ba30a8fec --- /dev/null +++ b/app/code/Magento/Sales/Test/Mftf/ActionGroup/AdminAddSimpleProductWithCustomOptionFileToOrderActionGroup.xml @@ -0,0 +1,24 @@ + + + + + + + Add product to order with custom option type file. Start on create order page. + + + + + + + + + + + + diff --git a/app/code/Magento/Sales/Test/Mftf/ActionGroup/AdminAssertProductQtyInGridActionGroup.xml b/app/code/Magento/Sales/Test/Mftf/ActionGroup/AdminAssertProductQtyInGridActionGroup.xml index cffda5665549e..48cbd803470aa 100644 --- a/app/code/Magento/Sales/Test/Mftf/ActionGroup/AdminAssertProductQtyInGridActionGroup.xml +++ b/app/code/Magento/Sales/Test/Mftf/ActionGroup/AdminAssertProductQtyInGridActionGroup.xml @@ -10,7 +10,7 @@ xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/actionGroupSchema.xsd"> - Goes tot he Admin Catalog Product grid page. Filters the grid based on the provided Product SKU. Validates that the provided Product Qty is present. + Goes to the Admin Catalog Product grid page. Filters the grid based on the provided Product SKU. Validates that the provided Product Qty is present. diff --git a/app/code/Magento/Sales/Test/Mftf/ActionGroup/AdminChangeCustomerOptionFileActionGroup.xml b/app/code/Magento/Sales/Test/Mftf/ActionGroup/AdminChangeCustomerOptionFileActionGroup.xml new file mode 100644 index 0000000000000..fb9f68d861faf --- /dev/null +++ b/app/code/Magento/Sales/Test/Mftf/ActionGroup/AdminChangeCustomerOptionFileActionGroup.xml @@ -0,0 +1,26 @@ + + + + + + + Change custom option file on admin order page. + + + + + + + + + + + + + + diff --git a/app/code/Magento/Sales/Test/Mftf/ActionGroup/AdminCreateInvoiceAndCreditMemoActionGroup.xml b/app/code/Magento/Sales/Test/Mftf/ActionGroup/AdminCreateInvoiceAndCreditMemoActionGroup.xml index a69cd8688c6c5..c4e70a1cacd1c 100644 --- a/app/code/Magento/Sales/Test/Mftf/ActionGroup/AdminCreateInvoiceAndCreditMemoActionGroup.xml +++ b/app/code/Magento/Sales/Test/Mftf/ActionGroup/AdminCreateInvoiceAndCreditMemoActionGroup.xml @@ -13,7 +13,7 @@ - + diff --git a/app/code/Magento/Sales/Test/Mftf/ActionGroup/AdminOpenCreditMemoFromOrderPageActionGroup.xml b/app/code/Magento/Sales/Test/Mftf/ActionGroup/AdminOpenCreditMemoFromOrderPageActionGroup.xml new file mode 100644 index 0000000000000..fab25c7f40681 --- /dev/null +++ b/app/code/Magento/Sales/Test/Mftf/ActionGroup/AdminOpenCreditMemoFromOrderPageActionGroup.xml @@ -0,0 +1,20 @@ + + + + + + + Admin open creditmemo from order + + + + + + + diff --git a/app/code/Magento/Sales/Test/Mftf/ActionGroup/AdminOpenInvoiceFromOrderPageActionGroup.xml b/app/code/Magento/Sales/Test/Mftf/ActionGroup/AdminOpenInvoiceFromOrderPageActionGroup.xml new file mode 100644 index 0000000000000..ec4352c15e1a8 --- /dev/null +++ b/app/code/Magento/Sales/Test/Mftf/ActionGroup/AdminOpenInvoiceFromOrderPageActionGroup.xml @@ -0,0 +1,20 @@ + + + + + + + Admin open invoice from order + + + + + + + diff --git a/app/code/Magento/Sales/Test/Mftf/ActionGroup/AdminOpenOrderByEntityIdActionGroup.xml b/app/code/Magento/Sales/Test/Mftf/ActionGroup/AdminOpenOrderByEntityIdActionGroup.xml new file mode 100644 index 0000000000000..c1beb0ee9790a --- /dev/null +++ b/app/code/Magento/Sales/Test/Mftf/ActionGroup/AdminOpenOrderByEntityIdActionGroup.xml @@ -0,0 +1,17 @@ + + + + + + + + + + + diff --git a/app/code/Magento/Sales/Test/Mftf/ActionGroup/AdminOpenShipmentFromOrderPageActionGroup.xml b/app/code/Magento/Sales/Test/Mftf/ActionGroup/AdminOpenShipmentFromOrderPageActionGroup.xml new file mode 100644 index 0000000000000..1ed31b93cb075 --- /dev/null +++ b/app/code/Magento/Sales/Test/Mftf/ActionGroup/AdminOpenShipmentFromOrderPageActionGroup.xml @@ -0,0 +1,20 @@ + + + + + + + Admin open shipment from order + + + + + + + diff --git a/app/code/Magento/Sales/Test/Mftf/ActionGroup/AdminOrderActionOnGridActionGroup.xml b/app/code/Magento/Sales/Test/Mftf/ActionGroup/AdminOrderActionOnGridActionGroup.xml index a9091deb039fc..20f475d4c0d78 100644 --- a/app/code/Magento/Sales/Test/Mftf/ActionGroup/AdminOrderActionOnGridActionGroup.xml +++ b/app/code/Magento/Sales/Test/Mftf/ActionGroup/AdminOrderActionOnGridActionGroup.xml @@ -19,11 +19,4 @@ - - - - - - - diff --git a/app/code/Magento/Sales/Test/Mftf/ActionGroup/AdminOrderCheckStatusActionGroup.xml b/app/code/Magento/Sales/Test/Mftf/ActionGroup/AdminOrderCheckStatusActionGroup.xml new file mode 100644 index 0000000000000..f5d46d165efe5 --- /dev/null +++ b/app/code/Magento/Sales/Test/Mftf/ActionGroup/AdminOrderCheckStatusActionGroup.xml @@ -0,0 +1,21 @@ + + + + + + + Check order status on order view page. + + + + + + + + diff --git a/app/code/Magento/Sales/Test/Mftf/ActionGroup/AdminStartCreateCreditMemoFromOrderPageActionGroup.xml b/app/code/Magento/Sales/Test/Mftf/ActionGroup/AdminStartCreateCreditMemoFromOrderPageActionGroup.xml new file mode 100644 index 0000000000000..e10c438652d90 --- /dev/null +++ b/app/code/Magento/Sales/Test/Mftf/ActionGroup/AdminStartCreateCreditMemoFromOrderPageActionGroup.xml @@ -0,0 +1,22 @@ + + + + + + + Start to create Credit Memo from order page + + + + + + + + diff --git a/app/code/Magento/Sales/Test/Mftf/ActionGroup/AdminStartToCreateCreditMemoFromOrderPageActionGroup.xml b/app/code/Magento/Sales/Test/Mftf/ActionGroup/AdminStartToCreateCreditMemoFromOrderPageActionGroup.xml new file mode 100644 index 0000000000000..e5c29ac33294e --- /dev/null +++ b/app/code/Magento/Sales/Test/Mftf/ActionGroup/AdminStartToCreateCreditMemoFromOrderPageActionGroup.xml @@ -0,0 +1,19 @@ + + + + + + + Admin start to create credit memo from order + + + + + + diff --git a/app/code/Magento/Sales/Test/Mftf/ActionGroup/AdminTwoOrderActionOnGridActionGroup.xml b/app/code/Magento/Sales/Test/Mftf/ActionGroup/AdminTwoOrderActionOnGridActionGroup.xml new file mode 100644 index 0000000000000..4cb0a45078125 --- /dev/null +++ b/app/code/Magento/Sales/Test/Mftf/ActionGroup/AdminTwoOrderActionOnGridActionGroup.xml @@ -0,0 +1,18 @@ + + + + + + + + + + + + diff --git a/app/code/Magento/Sales/Test/Mftf/ActionGroup/AssertAdminCreditMemoGrandTotalActionGroup.xml b/app/code/Magento/Sales/Test/Mftf/ActionGroup/AssertAdminCreditMemoGrandTotalActionGroup.xml new file mode 100644 index 0000000000000..471e3aa80835e --- /dev/null +++ b/app/code/Magento/Sales/Test/Mftf/ActionGroup/AssertAdminCreditMemoGrandTotalActionGroup.xml @@ -0,0 +1,24 @@ + + + + + + + Admin assert creditmemo grant total sum + + + + + + + getGrandTotal + {{expectedGrandTotal}} + + + diff --git a/app/code/Magento/Sales/Test/Mftf/ActionGroup/AssertAdminCreditMemoNewPageTotalsActionGroup.xml b/app/code/Magento/Sales/Test/Mftf/ActionGroup/AssertAdminCreditMemoNewPageTotalsActionGroup.xml new file mode 100644 index 0000000000000..8465383e0c40c --- /dev/null +++ b/app/code/Magento/Sales/Test/Mftf/ActionGroup/AssertAdminCreditMemoNewPageTotalsActionGroup.xml @@ -0,0 +1,27 @@ + + + + + + + Checks totals values on the Credit Memo new page. + + + + + + + + + + + + + + diff --git a/app/code/Magento/Sales/Test/Mftf/ActionGroup/AssertAdminCreditMemoViewPageTotalsActionGroup.xml b/app/code/Magento/Sales/Test/Mftf/ActionGroup/AssertAdminCreditMemoViewPageTotalsActionGroup.xml new file mode 100644 index 0000000000000..7bea25a6c5252 --- /dev/null +++ b/app/code/Magento/Sales/Test/Mftf/ActionGroup/AssertAdminCreditMemoViewPageTotalsActionGroup.xml @@ -0,0 +1,27 @@ + + + + + + + Checks totals values on the Credit Memo view page. + + + + + + + + + + + + + + diff --git a/app/code/Magento/Sales/Test/Mftf/ActionGroup/AssertAdminItemOrderedErrorActionGroup.xml b/app/code/Magento/Sales/Test/Mftf/ActionGroup/AssertAdminItemOrderedErrorActionGroup.xml new file mode 100644 index 0000000000000..a2f35b9c5fca8 --- /dev/null +++ b/app/code/Magento/Sales/Test/Mftf/ActionGroup/AssertAdminItemOrderedErrorActionGroup.xml @@ -0,0 +1,21 @@ + + + + + + + Assert that item in "Item Ordered" grid has an error/notice + + + + + + + + + diff --git a/app/code/Magento/Sales/Test/Mftf/ActionGroup/AssertAdminItemOrderedErrorNotVisibleActionGroup.xml b/app/code/Magento/Sales/Test/Mftf/ActionGroup/AssertAdminItemOrderedErrorNotVisibleActionGroup.xml new file mode 100644 index 0000000000000..83bac652d7dff --- /dev/null +++ b/app/code/Magento/Sales/Test/Mftf/ActionGroup/AssertAdminItemOrderedErrorNotVisibleActionGroup.xml @@ -0,0 +1,21 @@ + + + + + + + Assert that item in "Item Ordered" grid does not have an error/notice + + + + + + + + + diff --git a/app/code/Magento/Sales/Test/Mftf/ActionGroup/AssertStorefrontCustomerOrderMatchesGrandTotalActionGroup.xml b/app/code/Magento/Sales/Test/Mftf/ActionGroup/AssertStorefrontCustomerOrderMatchesGrandTotalActionGroup.xml new file mode 100644 index 0000000000000..a297d98fd370f --- /dev/null +++ b/app/code/Magento/Sales/Test/Mftf/ActionGroup/AssertStorefrontCustomerOrderMatchesGrandTotalActionGroup.xml @@ -0,0 +1,32 @@ + + + + + + + Check that order grand total equals sum of all totals. + + + + + + + + + sumTotalValue + grandTotalValue + + + diff --git a/app/code/Magento/Sales/Test/Mftf/ActionGroup/CheckRequiredFieldsNewOrderFormActionGroup.xml b/app/code/Magento/Sales/Test/Mftf/ActionGroup/CheckRequiredFieldsNewOrderFormActionGroup.xml index 25936ad3f6002..d3154a4385be8 100644 --- a/app/code/Magento/Sales/Test/Mftf/ActionGroup/CheckRequiredFieldsNewOrderFormActionGroup.xml +++ b/app/code/Magento/Sales/Test/Mftf/ActionGroup/CheckRequiredFieldsNewOrderFormActionGroup.xml @@ -26,7 +26,6 @@ - diff --git a/app/code/Magento/Sales/Test/Mftf/ActionGroup/FilterOrderStatusByLabelAndCodeActionGroup.xml b/app/code/Magento/Sales/Test/Mftf/ActionGroup/FilterOrderStatusByLabelAndCodeActionGroup.xml index 96e562cb95c6f..06ff1597dc608 100644 --- a/app/code/Magento/Sales/Test/Mftf/ActionGroup/FilterOrderStatusByLabelAndCodeActionGroup.xml +++ b/app/code/Magento/Sales/Test/Mftf/ActionGroup/FilterOrderStatusByLabelAndCodeActionGroup.xml @@ -7,7 +7,7 @@ --> + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/actionGroupSchema.xsd"> diff --git a/app/code/Magento/Sales/Test/Mftf/ActionGroup/NavigateToNewOrderPageExistingCustomerActionGroup.xml b/app/code/Magento/Sales/Test/Mftf/ActionGroup/NavigateToNewOrderPageExistingCustomerActionGroup.xml index a8f9d61ad6803..220d47e0495f9 100644 --- a/app/code/Magento/Sales/Test/Mftf/ActionGroup/NavigateToNewOrderPageExistingCustomerActionGroup.xml +++ b/app/code/Magento/Sales/Test/Mftf/ActionGroup/NavigateToNewOrderPageExistingCustomerActionGroup.xml @@ -10,7 +10,7 @@ xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/actionGroupSchema.xsd"> - Goes tot he Admin Orders grid page. Clicks on 'Create New Order'. Filters the grid for the provided Customer. Clicks on the Customer. Selects the provided Store View, if present. Validates that the Page Title is present and correct. + Goes to the Admin Orders grid page. Clicks on 'Create New Order'. Filters the grid for the provided Customer. Clicks on the Customer. Selects the provided Store View, if present. Validates that the Page Title is present and correct. diff --git a/app/code/Magento/Sales/Test/Mftf/ActionGroup/OrderSelectFlatRateShippingActionGroup.xml b/app/code/Magento/Sales/Test/Mftf/ActionGroup/OrderSelectFlatRateShippingActionGroup.xml index b85dc21e0b1b5..aca3ee0fdfef6 100644 --- a/app/code/Magento/Sales/Test/Mftf/ActionGroup/OrderSelectFlatRateShippingActionGroup.xml +++ b/app/code/Magento/Sales/Test/Mftf/ActionGroup/OrderSelectFlatRateShippingActionGroup.xml @@ -14,7 +14,7 @@ - + diff --git a/app/code/Magento/Sales/Test/Mftf/ActionGroup/SeeProductInComparisonListActionGroup.xml b/app/code/Magento/Sales/Test/Mftf/ActionGroup/SeeProductInComparisonListActionGroup.xml new file mode 100644 index 0000000000000..44dc8353d314d --- /dev/null +++ b/app/code/Magento/Sales/Test/Mftf/ActionGroup/SeeProductInComparisonListActionGroup.xml @@ -0,0 +1,24 @@ + + + + + + + Validate that the Product is present in the comparison list + + + + + + + + + + diff --git a/app/code/Magento/Sales/Test/Mftf/ActionGroup/SelectActionForOrdersActionGroup.xml b/app/code/Magento/Sales/Test/Mftf/ActionGroup/SelectActionForOrdersActionGroup.xml index 073eb03b11bfa..4351c44ed75aa 100644 --- a/app/code/Magento/Sales/Test/Mftf/ActionGroup/SelectActionForOrdersActionGroup.xml +++ b/app/code/Magento/Sales/Test/Mftf/ActionGroup/SelectActionForOrdersActionGroup.xml @@ -7,7 +7,7 @@ --> + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/actionGroupSchema.xsd"> diff --git a/app/code/Magento/Sales/Test/Mftf/ActionGroup/StorefrontCustomerOrdersViewOrderActionGroup.xml b/app/code/Magento/Sales/Test/Mftf/ActionGroup/StorefrontCustomerOrdersViewOrderActionGroup.xml new file mode 100644 index 0000000000000..88d79268d440e --- /dev/null +++ b/app/code/Magento/Sales/Test/Mftf/ActionGroup/StorefrontCustomerOrdersViewOrderActionGroup.xml @@ -0,0 +1,22 @@ + + + + + + + Navigate to customer dashboard -> orders. Press 'View Order' button for specified order number. Notice: customer should be logged in. + + + + + + + + + diff --git a/app/code/Magento/Sales/Test/Mftf/ActionGroup/SubmitCreditMemoActionGroup.xml b/app/code/Magento/Sales/Test/Mftf/ActionGroup/SubmitCreditMemoActionGroup.xml index fbd2a0047d558..2605adbfc91a7 100644 --- a/app/code/Magento/Sales/Test/Mftf/ActionGroup/SubmitCreditMemoActionGroup.xml +++ b/app/code/Magento/Sales/Test/Mftf/ActionGroup/SubmitCreditMemoActionGroup.xml @@ -10,8 +10,8 @@ xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/actionGroupSchema.xsd"> - - + + diff --git a/app/code/Magento/Sales/Test/Mftf/ActionGroup/VerifyCreatedOrderInformationActionGroup.xml b/app/code/Magento/Sales/Test/Mftf/ActionGroup/VerifyCreatedOrderInformationActionGroup.xml index 9a62771fb54cd..490496c1f1e9e 100644 --- a/app/code/Magento/Sales/Test/Mftf/ActionGroup/VerifyCreatedOrderInformationActionGroup.xml +++ b/app/code/Magento/Sales/Test/Mftf/ActionGroup/VerifyCreatedOrderInformationActionGroup.xml @@ -16,6 +16,8 @@ - + + $getOrderId + diff --git a/app/code/Magento/Sales/Test/Mftf/ActionGroup/VerifyCreatedOrderInformationWithGeneratedEmailActionGroup.xml b/app/code/Magento/Sales/Test/Mftf/ActionGroup/VerifyCreatedOrderInformationWithGeneratedEmailActionGroup.xml new file mode 100644 index 0000000000000..9b1666763b2bc --- /dev/null +++ b/app/code/Magento/Sales/Test/Mftf/ActionGroup/VerifyCreatedOrderInformationWithGeneratedEmailActionGroup.xml @@ -0,0 +1,19 @@ + + + + + + + Validate customer email on order page. Starts on order page. + + + + + + + diff --git a/app/code/Magento/Sales/Test/Mftf/Data/ConfigData.xml b/app/code/Magento/Sales/Test/Mftf/Data/ConfigData.xml index 730bebc047f93..25e25006d040a 100644 --- a/app/code/Magento/Sales/Test/Mftf/Data/ConfigData.xml +++ b/app/code/Magento/Sales/Test/Mftf/Data/ConfigData.xml @@ -20,4 +20,12 @@ No 0 + + customer/create_account/email_required_create_order + 0 + + + customer/create_account/email_required_create_order + 1 + diff --git a/app/code/Magento/Sales/Test/Mftf/Metadata/sales_config-meta.xml b/app/code/Magento/Sales/Test/Mftf/Metadata/SalesConfigMeta.xml similarity index 100% rename from app/code/Magento/Sales/Test/Mftf/Metadata/sales_config-meta.xml rename to app/code/Magento/Sales/Test/Mftf/Metadata/SalesConfigMeta.xml diff --git a/app/code/Magento/Sales/Test/Mftf/Page/AdminCreditMemoViewPage.xml b/app/code/Magento/Sales/Test/Mftf/Page/AdminCreditMemoViewPage.xml index 61646f8b30230..2e61424b29a56 100644 --- a/app/code/Magento/Sales/Test/Mftf/Page/AdminCreditMemoViewPage.xml +++ b/app/code/Magento/Sales/Test/Mftf/Page/AdminCreditMemoViewPage.xml @@ -8,7 +8,7 @@ - +
diff --git a/app/code/Magento/Sales/Test/Mftf/Page/AdminOrderCreatePage.xml b/app/code/Magento/Sales/Test/Mftf/Page/AdminOrderCreatePage.xml index 680d44ebb34fe..a7e2b2ec1f0d9 100644 --- a/app/code/Magento/Sales/Test/Mftf/Page/AdminOrderCreatePage.xml +++ b/app/code/Magento/Sales/Test/Mftf/Page/AdminOrderCreatePage.xml @@ -20,5 +20,6 @@
+
diff --git a/app/code/Magento/Sales/Test/Mftf/Page/StorefrontCustomerSignOutPage.xml b/app/code/Magento/Sales/Test/Mftf/Page/StorefrontCustomerSignOutPage.xml index 4e89e5476c3bc..0cfc9f2231f85 100644 --- a/app/code/Magento/Sales/Test/Mftf/Page/StorefrontCustomerSignOutPage.xml +++ b/app/code/Magento/Sales/Test/Mftf/Page/StorefrontCustomerSignOutPage.xml @@ -7,6 +7,6 @@ --> + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Page/etc/PageObject.xsd"> diff --git a/app/code/Magento/Sales/Test/Mftf/Page/StorefrontSalesOrderPrintPage.xml b/app/code/Magento/Sales/Test/Mftf/Page/StorefrontSalesOrderPrintPage.xml index 874e6889ec58c..437449aa887d2 100644 --- a/app/code/Magento/Sales/Test/Mftf/Page/StorefrontSalesOrderPrintPage.xml +++ b/app/code/Magento/Sales/Test/Mftf/Page/StorefrontSalesOrderPrintPage.xml @@ -7,7 +7,7 @@ --> + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Page/etc/PageObject.xsd">
diff --git a/app/code/Magento/Sales/Test/Mftf/Section/AdminCreditMemoTotalSection.xml b/app/code/Magento/Sales/Test/Mftf/Section/AdminCreditMemoTotalSection.xml index 6b4bdf69361a5..60ca8669670c8 100644 --- a/app/code/Magento/Sales/Test/Mftf/Section/AdminCreditMemoTotalSection.xml +++ b/app/code/Magento/Sales/Test/Mftf/Section/AdminCreditMemoTotalSection.xml @@ -18,7 +18,8 @@ - + + diff --git a/app/code/Magento/Sales/Test/Mftf/Section/AdminOrderDetailsMainActionsSection.xml b/app/code/Magento/Sales/Test/Mftf/Section/AdminOrderDetailsMainActionsSection.xml index 45fdfebfa8145..ecdf9e34de55a 100644 --- a/app/code/Magento/Sales/Test/Mftf/Section/AdminOrderDetailsMainActionsSection.xml +++ b/app/code/Magento/Sales/Test/Mftf/Section/AdminOrderDetailsMainActionsSection.xml @@ -11,6 +11,7 @@
+ diff --git a/app/code/Magento/Sales/Test/Mftf/Section/AdminOrderFormAccountSection.xml b/app/code/Magento/Sales/Test/Mftf/Section/AdminOrderFormAccountSection.xml index 430211e52bfb5..65d86c2940bab 100644 --- a/app/code/Magento/Sales/Test/Mftf/Section/AdminOrderFormAccountSection.xml +++ b/app/code/Magento/Sales/Test/Mftf/Section/AdminOrderFormAccountSection.xml @@ -12,7 +12,7 @@ - +
diff --git a/app/code/Magento/Sales/Test/Mftf/Section/AdminOrderFormBillingAddressSection.xml b/app/code/Magento/Sales/Test/Mftf/Section/AdminOrderFormBillingAddressSection.xml index 4fde9db1d21d8..279cf1dde15b3 100644 --- a/app/code/Magento/Sales/Test/Mftf/Section/AdminOrderFormBillingAddressSection.xml +++ b/app/code/Magento/Sales/Test/Mftf/Section/AdminOrderFormBillingAddressSection.xml @@ -28,8 +28,6 @@ - - diff --git a/app/code/Magento/Sales/Test/Mftf/Section/AdminOrderFormCustomOptionsSection.xml b/app/code/Magento/Sales/Test/Mftf/Section/AdminOrderFormCustomOptionsSection.xml new file mode 100644 index 0000000000000..066aa4181e7ef --- /dev/null +++ b/app/code/Magento/Sales/Test/Mftf/Section/AdminOrderFormCustomOptionsSection.xml @@ -0,0 +1,16 @@ + + + + +
+ + + + +
+
diff --git a/app/code/Magento/Sales/Test/Mftf/Section/AdminOrderFormItemsOrderedSection.xml b/app/code/Magento/Sales/Test/Mftf/Section/AdminOrderFormItemsOrderedSection.xml index e3417e7c662b9..4437f6e6775f2 100644 --- a/app/code/Magento/Sales/Test/Mftf/Section/AdminOrderFormItemsOrderedSection.xml +++ b/app/code/Magento/Sales/Test/Mftf/Section/AdminOrderFormItemsOrderedSection.xml @@ -18,5 +18,6 @@ +
diff --git a/app/code/Magento/Sales/Test/Mftf/Section/AdminOrderShipmentsTabSection.xml b/app/code/Magento/Sales/Test/Mftf/Section/AdminOrderShipmentsTabSection.xml index e471fcfe18114..66bea45b83f3d 100644 --- a/app/code/Magento/Sales/Test/Mftf/Section/AdminOrderShipmentsTabSection.xml +++ b/app/code/Magento/Sales/Test/Mftf/Section/AdminOrderShipmentsTabSection.xml @@ -18,5 +18,6 @@ +
- \ No newline at end of file + diff --git a/app/code/Magento/Sales/Test/Mftf/Section/SalesOrderPrintSection.xml b/app/code/Magento/Sales/Test/Mftf/Section/SalesOrderPrintSection.xml index b08a66140fabf..7c1d7319e30ea 100644 --- a/app/code/Magento/Sales/Test/Mftf/Section/SalesOrderPrintSection.xml +++ b/app/code/Magento/Sales/Test/Mftf/Section/SalesOrderPrintSection.xml @@ -7,7 +7,7 @@ --> + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Page/etc/SectionObject.xsd">
diff --git a/app/code/Magento/Sales/Test/Mftf/Section/StorefrontCustomerOrdersGridSection.xml b/app/code/Magento/Sales/Test/Mftf/Section/StorefrontCustomerOrdersGridSection.xml index c0deb9ab55d2b..0964ef5811cec 100644 --- a/app/code/Magento/Sales/Test/Mftf/Section/StorefrontCustomerOrdersGridSection.xml +++ b/app/code/Magento/Sales/Test/Mftf/Section/StorefrontCustomerOrdersGridSection.xml @@ -9,7 +9,7 @@
- - + +
diff --git a/app/code/Magento/Sales/Test/Mftf/Section/StorefrontOrderDetailsSection.xml b/app/code/Magento/Sales/Test/Mftf/Section/StorefrontOrderDetailsSection.xml index d262dfa9b010c..d1c94965640c5 100644 --- a/app/code/Magento/Sales/Test/Mftf/Section/StorefrontOrderDetailsSection.xml +++ b/app/code/Magento/Sales/Test/Mftf/Section/StorefrontOrderDetailsSection.xml @@ -9,6 +9,7 @@
+ diff --git a/app/code/Magento/Sales/Test/Mftf/Test/AddConfigurableProductToOrderFromShoppingCartTest.xml b/app/code/Magento/Sales/Test/Mftf/Test/AddConfigurableProductToOrderFromShoppingCartTest.xml index 6d9f35efc7903..6f4073bf70f46 100644 --- a/app/code/Magento/Sales/Test/Mftf/Test/AddConfigurableProductToOrderFromShoppingCartTest.xml +++ b/app/code/Magento/Sales/Test/Mftf/Test/AddConfigurableProductToOrderFromShoppingCartTest.xml @@ -14,7 +14,7 @@ <description value="Add configurable product to order from shopping cart"/> - <severity value="CRITICAL"/> + <severity value="BLOCKER"/> <testCaseId value="MC-16008"/> <group value="sales"/> <group value="mtf_migrated"/> @@ -57,7 +57,7 @@ </before> <after> <!-- Admin log out --> - <actionGroup ref="logout" stepKey="logout"/> + <actionGroup ref="AdminLogoutActionGroup" stepKey="logout"/> <!-- Customer log out --> <actionGroup ref="StorefrontCustomerLogoutActionGroup" stepKey="customerLogout"/> @@ -72,6 +72,9 @@ <!-- Delete category --> <deleteData createDataKey="createCategory" stepKey="deleteCategory"/> + + <!-- Reindex invalidated indices after product attribute has been created/deleted --> + <magentoCron groups="index" stepKey="reindexInvalidatedIndices"/> </after> <!-- Login as customer --> @@ -88,7 +91,7 @@ </actionGroup> <!-- Login as admin --> - <actionGroup ref="LoginAsAdmin" stepKey="loginAsAdmin"/> + <actionGroup ref="AdminLoginActionGroup" stepKey="loginAsAdmin"/> <!-- Search and open customer --> <actionGroup ref="AdminFilterCustomerByEmail" stepKey="filterCreatedCustomer"> diff --git a/app/code/Magento/Sales/Test/Mftf/Test/AddSimpleProductToOrderFromShoppingCartTest.xml b/app/code/Magento/Sales/Test/Mftf/Test/AddSimpleProductToOrderFromShoppingCartTest.xml index 589da3e49dc89..d8a9effa56dac 100644 --- a/app/code/Magento/Sales/Test/Mftf/Test/AddSimpleProductToOrderFromShoppingCartTest.xml +++ b/app/code/Magento/Sales/Test/Mftf/Test/AddSimpleProductToOrderFromShoppingCartTest.xml @@ -14,7 +14,7 @@ <stories value="Add Products to Order from Shopping Cart"/> <title value="Add simple product to order from shopping cart test"/> <description value="Add simple product to order from shopping cart"/> - <severity value="CRITICAL"/> + <severity value="BLOCKER"/> <testCaseId value="MC-16007"/> <group value="sales"/> <group value="mtf_migrated"/> @@ -28,7 +28,7 @@ </before> <after> <!-- Admin log out --> - <actionGroup ref="logout" stepKey="logout"/> + <actionGroup ref="AdminLogoutActionGroup" stepKey="logout"/> <!-- Customer log out --> <actionGroup ref="StorefrontCustomerLogoutActionGroup" stepKey="customerLogout"/> @@ -52,7 +52,7 @@ </actionGroup> <!-- Login as admin --> - <actionGroup ref="LoginAsAdmin" stepKey="loginAsAdmin"/> + <actionGroup ref="AdminLoginActionGroup" stepKey="loginAsAdmin"/> <!-- Search and open customer --> <actionGroup ref="AdminFilterCustomerByEmail" stepKey="filterCreatedCustomer"> diff --git a/app/code/Magento/Sales/Test/Mftf/Test/AdminAddSelectedProductToOrderTest.xml b/app/code/Magento/Sales/Test/Mftf/Test/AdminAddSelectedProductToOrderTest.xml new file mode 100644 index 0000000000000..452fd511f2ce2 --- /dev/null +++ b/app/code/Magento/Sales/Test/Mftf/Test/AdminAddSelectedProductToOrderTest.xml @@ -0,0 +1,78 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + /** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +--> + +<tests xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/testSchema.xsd"> + <test name="AdminAddSelectedProductToOrderTest"> + <annotations> + <features value="CatalogInventory"/> + <stories value="Admin create order"/> + <title value="Add selected products to order in Admin when requested qty more than available"/> + <description value="Trying to add selected products to order in Admin when requested qty more than available"/> + <useCaseId value="MC-29184"/> + <testCaseId value="MC-31589"/> + <severity value="MAJOR"/> + <group value="sales"/> + <group value="catalogInventory"/> + </annotations> + <before> + <createData entity="Simple_US_Customer" stepKey="simpleCustomer"/> + <createData entity="SimpleProduct2" stepKey="simpleProduct"/> + <actionGroup ref="AdminLoginActionGroup" stepKey="loginAsAdmin"/> + </before> + <after> + <deleteData createDataKey="simpleCustomer" stepKey="deleteSimpleCustomer"/> + <deleteData createDataKey="simpleProduct" stepKey="deleteSimpleProduct"/> + <actionGroup ref="AdminLogoutActionGroup" stepKey="logoutFromAdmin"/> + </after> + + <!-- Initiate create new order --> + <actionGroup ref="NavigateToNewOrderPageExistingCustomerActionGroup" stepKey="navigateToNewOrderPageWithExistingCustomer"> + <argument name="customer" value="$simpleCustomer$"/> + </actionGroup> + <!-- Add to order maximum available quantity - 1 --> + <executeJS function="return {{SimpleProduct2.quantity}} - 1" stepKey="maxQtyMinusOne"/> + <actionGroup ref="AddSimpleProductToOrderActionGroup" stepKey="addProductToOrderWithMaxQtyMinusOne"> + <argument name="product" value="$simpleProduct$"/> + <argument name="productQty" value="{$maxQtyMinusOne}"/> + </actionGroup> + <!-- Check that there is no error or notice --> + <actionGroup ref="AssertAdminItemOrderedErrorNotVisibleActionGroup" stepKey="assertNoticeAbsent"> + <argument name="productName" value="$simpleProduct.name$"/> + <argument name="messageType" value="notice"/> + </actionGroup> + <actionGroup ref="AssertAdminItemOrderedErrorNotVisibleActionGroup" stepKey="assertErrorAbsent"> + <argument name="productName" value="$simpleProduct.name$"/> + <argument name="messageType" value="error"/> + </actionGroup> + <!-- Add to order maximum available quantity --> + <actionGroup ref="AddSimpleProductToOrderActionGroup" stepKey="addProductToOrder"> + <argument name="product" value="$simpleProduct$"/> + <argument name="productQty" value="1"/> + </actionGroup> + <!-- Check that there is no error or notice --> + <actionGroup ref="AssertAdminItemOrderedErrorNotVisibleActionGroup" stepKey="assertNoticeAbsentAgain"> + <argument name="productName" value="$simpleProduct.name$"/> + <argument name="messageType" value="notice"/> + </actionGroup> + <actionGroup ref="AssertAdminItemOrderedErrorNotVisibleActionGroup" stepKey="assertErrorAbsentAgain"> + <argument name="productName" value="$simpleProduct.name$"/> + <argument name="messageType" value="error"/> + </actionGroup> + <!-- Add to order one more quantity --> + <actionGroup ref="AddSimpleProductToOrderActionGroup" stepKey="addProductToOrderAgain"> + <argument name="product" value="$simpleProduct$"/> + <argument name="productQty" value="1"/> + </actionGroup> + <!-- Check that error remains --> + <actionGroup ref="AssertAdminItemOrderedErrorActionGroup" stepKey="assertProductErrorRemains"> + <argument name="productName" value="$simpleProduct.name$"/> + <argument name="messageType" value="error"/> + </actionGroup> + </test> +</tests> diff --git a/app/code/Magento/Sales/Test/Mftf/Test/AdminAvailabilityCreditMemoWithNoPaymentTest.xml b/app/code/Magento/Sales/Test/Mftf/Test/AdminAvailabilityCreditMemoWithNoPaymentTest.xml index ec518c79a0808..11a9957fe0041 100644 --- a/app/code/Magento/Sales/Test/Mftf/Test/AdminAvailabilityCreditMemoWithNoPaymentTest.xml +++ b/app/code/Magento/Sales/Test/Mftf/Test/AdminAvailabilityCreditMemoWithNoPaymentTest.xml @@ -26,7 +26,7 @@ <!-- Enable *Free Shipping* --> <createData entity="FreeShippingMethodsSettingConfig" stepKey="freeShippingMethodsSettingConfig"/> <magentoCLI command="cache:flush" stepKey="flushCache"/> - <actionGroup ref="LoginAsAdmin" stepKey="loginAsAdmin"/> + <actionGroup ref="AdminLoginActionGroup" stepKey="loginAsAdmin"/> </before> <after> <!-- Disable *Free Shipping* --> @@ -38,7 +38,7 @@ <argument name="customerEmail" value="Simple_US_Customer.email"/> </actionGroup> <magentoCLI command="cache:flush" stepKey="flushCache"/> - <actionGroup ref="logout" stepKey="logOut"/> + <actionGroup ref="AdminLogoutActionGroup" stepKey="logOut"/> </after> <!--Proceed to Admin panel > SALES > Orders. Created order should be in Processing status--> diff --git a/app/code/Magento/Sales/Test/Mftf/Test/AdminCancelTheCreatedOrderWithBankTransferPaymentMethodTest.xml b/app/code/Magento/Sales/Test/Mftf/Test/AdminCancelTheCreatedOrderWithBankTransferPaymentMethodTest.xml index 726b4a99cdec9..bb1940357a7f4 100644 --- a/app/code/Magento/Sales/Test/Mftf/Test/AdminCancelTheCreatedOrderWithBankTransferPaymentMethodTest.xml +++ b/app/code/Magento/Sales/Test/Mftf/Test/AdminCancelTheCreatedOrderWithBankTransferPaymentMethodTest.xml @@ -20,7 +20,7 @@ </annotations> <before> - <actionGroup ref="LoginAsAdmin" stepKey="loginAsAdmin"/> + <actionGroup ref="AdminLoginActionGroup" stepKey="loginAsAdmin"/> <!-- Enable Bank Transfer payment --> <magentoCLI command="config:set {{EnablePaymentBankTransferConfigData.path}} {{EnablePaymentBankTransferConfigData.value}}" stepKey="enableBankTransferPayment"/> @@ -40,7 +40,7 @@ <magentoCLI command="config:set {{DisablePaymentBankTransferConfigData.path}} {{DisablePaymentBankTransferConfigData.value}}" stepKey="disableBankTransferPayment"/> <deleteData createDataKey="simpleCustomer" stepKey="deleteSimpleCustomer"/> <deleteData createDataKey="simpleProduct" stepKey="deleteSimpleProduct"/> - <actionGroup ref="logout" stepKey="logout"/> + <actionGroup ref="AdminLogoutActionGroup" stepKey="logout"/> </after> <!--Create new customer order--> diff --git a/app/code/Magento/Sales/Test/Mftf/Test/AdminCancelTheCreatedOrderWithCashOnDeliveryPaymentMethodTest.xml b/app/code/Magento/Sales/Test/Mftf/Test/AdminCancelTheCreatedOrderWithCashOnDeliveryPaymentMethodTest.xml index eb46621a458ab..dafd00ff60b29 100644 --- a/app/code/Magento/Sales/Test/Mftf/Test/AdminCancelTheCreatedOrderWithCashOnDeliveryPaymentMethodTest.xml +++ b/app/code/Magento/Sales/Test/Mftf/Test/AdminCancelTheCreatedOrderWithCashOnDeliveryPaymentMethodTest.xml @@ -20,7 +20,7 @@ </annotations> <before> - <actionGroup ref="LoginAsAdmin" stepKey="loginAsAdmin"/> + <actionGroup ref="AdminLoginActionGroup" stepKey="loginAsAdmin"/> <!-- Enable Cash On Delivery payment method --> <magentoCLI command="config:set {{EnableCashOnDeliveryConfigData.path}} {{EnableCashOnDeliveryConfigData.value}}" stepKey="enableCashOnDeliveryPayment"/> @@ -40,7 +40,7 @@ <magentoCLI command="config:set {{DisableCashOnDeliveryConfigData.path}} {{DisableCashOnDeliveryConfigData.value}}" stepKey="disableCashOnDeliveryPayment"/> <deleteData createDataKey="simpleCustomer" stepKey="deleteSimpleCustomer"/> <deleteData createDataKey="simpleProduct" stepKey="deleteSimpleProduct"/> - <actionGroup ref="logout" stepKey="logout"/> + <actionGroup ref="AdminLogoutActionGroup" stepKey="logout"/> </after> <!-- Create new customer order --> diff --git a/app/code/Magento/Sales/Test/Mftf/Test/AdminCancelTheCreatedOrderWithCheckMoneyOrderPaymentMethodTest.xml b/app/code/Magento/Sales/Test/Mftf/Test/AdminCancelTheCreatedOrderWithCheckMoneyOrderPaymentMethodTest.xml index b303364bbf324..c0ebbe450119e 100644 --- a/app/code/Magento/Sales/Test/Mftf/Test/AdminCancelTheCreatedOrderWithCheckMoneyOrderPaymentMethodTest.xml +++ b/app/code/Magento/Sales/Test/Mftf/Test/AdminCancelTheCreatedOrderWithCheckMoneyOrderPaymentMethodTest.xml @@ -19,7 +19,7 @@ <group value="mtf_migrated"/> </annotations> <before> - <actionGroup ref="LoginAsAdmin" stepKey="loginAsAdmin"/> + <actionGroup ref="AdminLoginActionGroup" stepKey="loginAsAdmin"/> <!-- Set default flat rate shipping method settings --> <comment userInput="Set default flat rate shipping method settings" stepKey="setDefaultFlatRateShippingMethodComment"/> <createData entity="FlatRateShippingMethodDefault" stepKey="setDefaultFlatRateShippingMethod"/> @@ -107,7 +107,10 @@ <deleteData createDataKey="createConfigProduct" stepKey="deleteConfigurableProduct"/> <deleteData createDataKey="createConfigProductAttribute" stepKey="deleteProductAttribute"/> <deleteData createDataKey="createCategory" stepKey="deleteCategory"/> - <actionGroup ref="logout" stepKey="logout"/> + <actionGroup ref="AdminLogoutActionGroup" stepKey="logout"/> + + <!-- Reindex invalidated indices after product attribute has been created/deleted --> + <magentoCron groups="index" stepKey="reindexInvalidatedIndices"/> </after> <!-- Create new customer order --> <comment userInput="Create new customer order" stepKey="createNewCustomerOrderComment"/> diff --git a/app/code/Magento/Sales/Test/Mftf/Test/AdminCancelTheCreatedOrderWithProductQtyWithoutStockDecreaseTest.xml b/app/code/Magento/Sales/Test/Mftf/Test/AdminCancelTheCreatedOrderWithProductQtyWithoutStockDecreaseTest.xml index a8076bfb9f681..256417c0d0d10 100644 --- a/app/code/Magento/Sales/Test/Mftf/Test/AdminCancelTheCreatedOrderWithProductQtyWithoutStockDecreaseTest.xml +++ b/app/code/Magento/Sales/Test/Mftf/Test/AdminCancelTheCreatedOrderWithProductQtyWithoutStockDecreaseTest.xml @@ -14,13 +14,13 @@ <stories value="Cancel Created Order"/> <title value="Cancel the created order with product quantity without stock decrease"/> <description value="Create an order with product quantity without stock decrease, cancel the order and verify product quantity in backend"/> - <severity value="CRITICAL"/> + <severity value="BLOCKER"/> <testCaseId value="MC-16071"/> <group value="mtf_migrated"/> </annotations> <before> - <actionGroup ref="LoginAsAdmin" stepKey="loginAsAdmin"/> + <actionGroup ref="AdminLoginActionGroup" stepKey="loginAsAdmin"/> <magentoCLI command="config:set {{DisableCatalogInventoryConfigData.path}} {{DisableCatalogInventoryConfigData.value}}" stepKey="disableDecreaseInQuantityAfterOrder"/> <!--Set default flat rate shipping method settings--> @@ -43,7 +43,7 @@ <deleteData createDataKey="simpleCustomer" stepKey="deleteSimpleCustomer"/> <deleteData createDataKey="simpleProduct" stepKey="deleteSimpleProduct"/> <deleteData createDataKey="createCategory" stepKey="deleteCategory"/> - <actionGroup ref="logout" stepKey="logout"/> + <actionGroup ref="AdminLogoutActionGroup" stepKey="logout"/> </after> <!--Create new customer order--> diff --git a/app/code/Magento/Sales/Test/Mftf/Test/AdminCancelTheCreatedOrderWithPurchaseOrderPaymentMethodTest.xml b/app/code/Magento/Sales/Test/Mftf/Test/AdminCancelTheCreatedOrderWithPurchaseOrderPaymentMethodTest.xml index c85a3fffc2c69..477676085cf2e 100644 --- a/app/code/Magento/Sales/Test/Mftf/Test/AdminCancelTheCreatedOrderWithPurchaseOrderPaymentMethodTest.xml +++ b/app/code/Magento/Sales/Test/Mftf/Test/AdminCancelTheCreatedOrderWithPurchaseOrderPaymentMethodTest.xml @@ -20,7 +20,7 @@ </annotations> <before> - <actionGroup ref="LoginAsAdmin" stepKey="loginAsAdmin"/> + <actionGroup ref="AdminLoginActionGroup" stepKey="loginAsAdmin"/> <!-- Enable Purchase Order payment method --> <magentoCLI command="config:set {{EnablePurchaseOrderConfigData.path}} {{EnablePurchaseOrderConfigData.value}}" stepKey="enableCPurchaseOrderPayment"/> @@ -42,7 +42,7 @@ <magentoCLI command="config:set {{DisablePurchaseOrderConfigData.path}} {{DisablePurchaseOrderConfigData.value}}" stepKey="disablePurchaseOrderPayment"/> <deleteData createDataKey="simpleCustomer" stepKey="deleteSimpleCustomer"/> <deleteData createDataKey="simpleProduct" stepKey="deleteSimpleProduct"/> - <actionGroup ref="logout" stepKey="logout"/> + <actionGroup ref="AdminLogoutActionGroup" stepKey="logout"/> </after> <!--Create new customer order--> diff --git a/app/code/Magento/Sales/Test/Mftf/Test/AdminCancelTheCreatedOrderWithZeroSubtotalCheckoutTest.xml b/app/code/Magento/Sales/Test/Mftf/Test/AdminCancelTheCreatedOrderWithZeroSubtotalCheckoutTest.xml index a8014466c5773..d22e11bca3d0e 100644 --- a/app/code/Magento/Sales/Test/Mftf/Test/AdminCancelTheCreatedOrderWithZeroSubtotalCheckoutTest.xml +++ b/app/code/Magento/Sales/Test/Mftf/Test/AdminCancelTheCreatedOrderWithZeroSubtotalCheckoutTest.xml @@ -22,7 +22,7 @@ <before> <!-- Enable Zero Subtotal Checkout --> <magentoCLI command="config:set {{EnableZeroSubtotalCheckoutConfigData.path}} {{EnableZeroSubtotalCheckoutConfigData.value}}" stepKey="enableZeroSubtotalCheckout"/> - <actionGroup ref="LoginAsAdmin" stepKey="loginAsAdmin"/> + <actionGroup ref="AdminLoginActionGroup" stepKey="loginAsAdmin"/> <!--Set Free shipping method settings--> <createData entity="FreeShippingMethodsSettingConfig" stepKey="freeShippingMethodsSettingConfig"/> @@ -45,7 +45,7 @@ <deleteData createDataKey="createSalesRule" stepKey="deleteSalesRule"/> <deleteData createDataKey="simpleCustomer" stepKey="deleteSimpleCustomer"/> <deleteData createDataKey="simpleProduct" stepKey="deleteSimpleProduct"/> - <actionGroup ref="logout" stepKey="logout"/> + <actionGroup ref="AdminLogoutActionGroup" stepKey="logout"/> </after> <!--Create new customer order--> diff --git a/app/code/Magento/Sales/Test/Mftf/Test/AdminChangeCustomerGroupInNewOrder.xml b/app/code/Magento/Sales/Test/Mftf/Test/AdminChangeCustomerGroupInNewOrder.xml deleted file mode 100644 index e19002d5ecb4c..0000000000000 --- a/app/code/Magento/Sales/Test/Mftf/Test/AdminChangeCustomerGroupInNewOrder.xml +++ /dev/null @@ -1,38 +0,0 @@ -<?xml version="1.0" encoding="UTF-8"?> -<!-- - /** - * Copyright © Magento, Inc. All rights reserved. - * See COPYING.txt for license details. - */ ---> - -<tests xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" - xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/testSchema.xsd"> - <test name="AdminChangeCustomerGroupInNewOrder"> - <annotations> - <title value="Customer account group cannot be selected while creating a new customer in order"/> - <stories value="MC-15290: Customer account group cannot be selected while creating a new customer in order"/> - <description value="Customer account group cannot be selected while creating a new customer in order"/> - <severity value="MAJOR"/> - <testCaseId value="MC-15290"/> - <useCaseId value="MC-15289"/> - <group value="sales"/> - </annotations> - - <before> - <actionGroup ref="LoginAsAdmin" stepKey="login"/> - </before> - <after> - <actionGroup ref="logout" stepKey="logout"/> - </after> - - <actionGroup ref="NavigateToNewOrderPageNewCustomerActionGroup" stepKey="openNewOrder"/> - <selectOption selector="{{AdminOrderFormAccountSection.group}}" userInput="Retailer" stepKey="selectCustomerGroup"/> - <waitForPageLoad stepKey="waitForPageLoad"/> - <grabValueFrom selector="{{AdminOrderFormAccountSection.group}}" stepKey="grabGroupValue"/> - <assertEquals stepKey="assertValueIsStillSelected"> - <actualResult type="variable">$grabGroupValue</actualResult> - <expectedResult type="string">3</expectedResult> - </assertEquals> - </test> -</tests> diff --git a/app/code/Magento/Sales/Test/Mftf/Test/AdminChangeCustomerGroupInNewOrderTest.xml b/app/code/Magento/Sales/Test/Mftf/Test/AdminChangeCustomerGroupInNewOrderTest.xml new file mode 100644 index 0000000000000..b2bfa93678dfd --- /dev/null +++ b/app/code/Magento/Sales/Test/Mftf/Test/AdminChangeCustomerGroupInNewOrderTest.xml @@ -0,0 +1,38 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + /** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +--> + +<tests xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/testSchema.xsd"> + <test name="AdminChangeCustomerGroupInNewOrderTest"> + <annotations> + <title value="Customer account group cannot be selected while creating a new customer in order"/> + <stories value="MC-15290: Customer account group cannot be selected while creating a new customer in order"/> + <description value="Customer account group cannot be selected while creating a new customer in order"/> + <severity value="MAJOR"/> + <testCaseId value="MC-15290"/> + <useCaseId value="MC-15289"/> + <group value="sales"/> + </annotations> + + <before> + <actionGroup ref="AdminLoginActionGroup" stepKey="login"/> + </before> + <after> + <actionGroup ref="AdminLogoutActionGroup" stepKey="logout"/> + </after> + + <actionGroup ref="NavigateToNewOrderPageNewCustomerActionGroup" stepKey="openNewOrder"/> + <selectOption selector="{{AdminOrderFormAccountSection.group}}" userInput="Retailer" stepKey="selectCustomerGroup"/> + <waitForPageLoad stepKey="waitForPageLoad"/> + <grabValueFrom selector="{{AdminOrderFormAccountSection.group}}" stepKey="grabGroupValue"/> + <assertEquals stepKey="assertValueIsStillSelected"> + <actualResult type="variable">$grabGroupValue</actualResult> + <expectedResult type="string">3</expectedResult> + </assertEquals> + </test> +</tests> diff --git a/app/code/Magento/Sales/Test/Mftf/Test/AdminCheckNewCreditMemoTotalsForFranceTest.xml b/app/code/Magento/Sales/Test/Mftf/Test/AdminCheckNewCreditMemoTotalsForFranceTest.xml new file mode 100644 index 0000000000000..ce6848c3e9d66 --- /dev/null +++ b/app/code/Magento/Sales/Test/Mftf/Test/AdminCheckNewCreditMemoTotalsForFranceTest.xml @@ -0,0 +1,52 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + /** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +--> + +<tests xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/testSchema.xsd"> + <test name="AdminCheckNewCreditMemoTotalsForFranceTest" extends="AdminCreateCreditMemoWithCashOnDeliveryTest"> + <annotations> + <stories value="Credit memo entity for France locale"/> + <title value="Credit memo entity for France locale"/> + <description value="Create Credit Memo with cash on delivery payment and assert 0 shipping refund for France locale"/> + <group value="sales"/> + </annotations> + <before> + <magentoCLI command="setup:static-content:deploy fr_FR" stepKey="deployStaticContentWithFrenchLocale" before="LoginAsAdmin"/> + <actionGroup ref="SetAdminAccountActionGroup" stepKey="setAdminInterfaceLocaleToFrance" after="LoginAsAdmin"> + <argument name="InterfaceLocaleByValue" value="fr_FR"/> + </actionGroup> + </before> + <after> + <actionGroup ref="SetAdminAccountActionGroup" stepKey="setAdminInterfaceLocaleToDefaultValue" before="logout"> + <argument name="InterfaceLocaleByValue" value="en_US"/> + </actionGroup> + </after> + + <actionGroup ref="AdminOpenAndFillCreditMemoRefundActionGroup" stepKey="fillCreditMemoRefund"> + <argument name="itemQtyToRefund" value="1"/> + <argument name="shippingRefund" value="0"/> + <argument name="adjustmentRefund" value="5,31"/> + <argument name="adjustmentFee" value="10,31"/> + </actionGroup> + + <actionGroup ref="AssertAdminCreditMemoNewPageTotalsActionGroup" stepKey="assertCreditMemoRefundTotals" after="fillCreditMemoRefund"> + <argument name="refundShipping" value="0,00"/> + <argument name="adjustmentRefund" value="5,31"/> + <argument name="adjustmentFee" value="10,31"/> + <argument name="subtotalRow" value="560,00"/> + <argument name="grandTotal" value="555,00"/> + </actionGroup> + + <actionGroup ref="AssertAdminCreditMemoViewPageTotalsActionGroup" stepKey="assertCreditMemoViewPageTotals"> + <argument name="subtotal" value="560,00"/> + <argument name="adjustmentRefund" value="5,31"/> + <argument name="adjustmentFee" value="10,31"/> + <argument name="grandTotal" value="555,00"/> + </actionGroup> + </test> +</tests> diff --git a/app/code/Magento/Sales/Test/Mftf/Test/AdminCheckingCreditMemoUpdateTotalsTest.xml b/app/code/Magento/Sales/Test/Mftf/Test/AdminCheckingCreditMemoUpdateTotalsTest.xml index 251d29df43dd1..37a4782ce2e73 100644 --- a/app/code/Magento/Sales/Test/Mftf/Test/AdminCheckingCreditMemoUpdateTotalsTest.xml +++ b/app/code/Magento/Sales/Test/Mftf/Test/AdminCheckingCreditMemoUpdateTotalsTest.xml @@ -25,14 +25,14 @@ <!--Create customer--> <createData entity="Simple_US_CA_Customer" stepKey="createCustomer"/> <!--Login to admin page--> - <actionGroup ref="LoginAsAdmin" stepKey="loginAsAdmin"/> + <actionGroup ref="AdminLoginActionGroup" stepKey="loginAsAdmin"/> </before> <after> <!--Delete simple product--> <deleteData createDataKey="createSimpleProduct" stepKey="deleteSimpleProduct"/> <!--Delete customer--> <deleteData createDataKey="createCustomer" stepKey="deleteCustomer"/> - <actionGroup ref="logout" stepKey="logout"/> + <actionGroup ref="AdminLogoutActionGroup" stepKey="logout"/> </after> <actionGroup ref="CreateOrderActionGroup" stepKey="createOrder"> diff --git a/app/code/Magento/Sales/Test/Mftf/Test/AdminCheckingDateAfterChangeInterfaceLocaleTest.xml b/app/code/Magento/Sales/Test/Mftf/Test/AdminCheckingDateAfterChangeInterfaceLocaleTest.xml index ad3b6cf45d5bb..ca3733d5f0b96 100644 --- a/app/code/Magento/Sales/Test/Mftf/Test/AdminCheckingDateAfterChangeInterfaceLocaleTest.xml +++ b/app/code/Magento/Sales/Test/Mftf/Test/AdminCheckingDateAfterChangeInterfaceLocaleTest.xml @@ -27,7 +27,7 @@ <createData entity="SimpleProduct2" stepKey="createProduct"/> <createData entity="Simple_US_Customer" stepKey="createCustomer"/> <!--Login to Admin page--> - <actionGroup ref="LoginAsAdmin" stepKey="loginAsAdmin"/> + <actionGroup ref="AdminLoginActionGroup" stepKey="loginAsAdmin"/> </before> <after> <!--Delete entities--> @@ -40,7 +40,7 @@ <!--Clear filters--> <actionGroup ref="AdminOrdersGridClearFiltersActionGroup" stepKey="clearOrderFilters"/> <!--Logout from Admin page--> - <actionGroup ref="logout" stepKey="logout"/> + <actionGroup ref="AdminLogoutActionGroup" stepKey="logout"/> </after> <!--Create order--> diff --git a/app/code/Magento/Sales/Test/Mftf/Test/AdminCorrectnessInvoicedItemInBundleProductTest.xml b/app/code/Magento/Sales/Test/Mftf/Test/AdminCorrectnessInvoicedItemInBundleProductTest.xml index 622718d721577..69e3ade9231d0 100644 --- a/app/code/Magento/Sales/Test/Mftf/Test/AdminCorrectnessInvoicedItemInBundleProductTest.xml +++ b/app/code/Magento/Sales/Test/Mftf/Test/AdminCorrectnessInvoicedItemInBundleProductTest.xml @@ -20,7 +20,7 @@ <group value="sales"/> </annotations> <before> - <actionGroup ref="LoginAsAdmin" stepKey="loginAsAdmin"/> + <actionGroup ref="AdminLoginActionGroup" stepKey="loginAsAdmin"/> <!--Create category and simple product--> <createData entity="SimpleSubCategory" stepKey="createCategory"/> @@ -48,11 +48,13 @@ <deleteData createDataKey="createSimpleProduct" stepKey="deleteSimpleProduct"/> <deleteData createDataKey="createBundleProduct" stepKey="deleteBundleProduct"/> - <actionGroup ref="logout" stepKey="logOut"/> + <actionGroup ref="AdminLogoutActionGroup" stepKey="logOut"/> </after> <!--Complete Bundle product creation--> - <amOnPage url="{{AdminProductEditPage.url($$createBundleProduct.id$$)}}" stepKey="goToProductEditPage"/> + <actionGroup ref="AdminProductPageOpenByIdActionGroup" stepKey="goToProductEditPage"> + <argument name="productId" value="$$createBundleProduct.id$$"/> + </actionGroup> <actionGroup ref="SaveProductFormActionGroup" stepKey="saveProduct"/> <!--Run re-index task--> @@ -83,13 +85,16 @@ <waitForPageLoad stepKey="waitForCreatedOrderPageOpened"/> <actionGroup ref="GoToInvoiceIntoOrderActionGroup" stepKey="goToInvoiceIntoOrderPage"/> <fillField selector="{{AdminInvoiceItemsSection.qtyToInvoiceColumn}}" userInput="5" stepKey="ChangeQtyToInvoice"/> - <click selector="{{AdminInvoiceItemsSection.updateQty}}" stepKey="updateQunatity"/> + <click selector="{{AdminInvoiceItemsSection.updateQty}}" stepKey="updateQuantity"/> <waitForPageLoad stepKey="waitPageToBeLoaded"/> <actionGroup ref="SubmitInvoiceActionGroup" stepKey="submitInvoice"/> <!--Verify invoiced items qty in ship tab--> - <actionGroup ref="goToShipmentIntoOrder" stepKey="goToShipment"/> + <actionGroup ref="GoToShipmentIntoOrderActionGroup" stepKey="goToShipment"/> <grabTextFrom selector="{{AdminShipmentItemsSection.itemQtyInvoiced('1')}}" stepKey="grabInvoicedItemQty"/> - <assertEquals expected="5" expectedType="string" actual="$grabInvoicedItemQty" stepKey="assertInvoicedItemsQty"/> + <assertEquals stepKey="assertInvoicedItemsQty"> + <actualResult type="const">$grabInvoicedItemQty</actualResult> + <expectedResult type="string">5</expectedResult> + </assertEquals> </test> </tests> diff --git a/app/code/Magento/Sales/Test/Mftf/Test/AdminCreateCreditMemoBankTransferPaymentTest.xml b/app/code/Magento/Sales/Test/Mftf/Test/AdminCreateCreditMemoBankTransferPaymentTest.xml index 4b582a09deacf..2935a56a6c0a1 100644 --- a/app/code/Magento/Sales/Test/Mftf/Test/AdminCreateCreditMemoBankTransferPaymentTest.xml +++ b/app/code/Magento/Sales/Test/Mftf/Test/AdminCreateCreditMemoBankTransferPaymentTest.xml @@ -19,7 +19,7 @@ <group value="mtf_migrated"/> </annotations> <before> - <actionGroup ref="LoginAsAdmin" stepKey="LoginAsAdmin"/> + <actionGroup ref="AdminLoginActionGroup" stepKey="LoginAsAdmin"/> <!-- Create Data --> <createData entity="Simple_US_Customer" stepKey="createCustomer"/> @@ -39,7 +39,7 @@ <deleteData createDataKey="createCustomer" stepKey="deleteCustomer"/> <deleteData createDataKey="createCategory" stepKey="deleteCategory"/> <deleteData createDataKey="createProduct" stepKey="deleteProduct"/> - <actionGroup ref="logout" stepKey="logout"/> + <actionGroup ref="AdminLogoutActionGroup" stepKey="logout"/> </after> <!-- Create Order --> <actionGroup ref="NavigateToNewOrderPageExistingCustomerActionGroup" stepKey="navigateToNewOrderPage"> @@ -69,7 +69,9 @@ <!-- Go to Sales > Orders > find out placed order and open --> <grabTextFrom selector="|Order # (\d+)|" stepKey="grabOrderId" /> - <assertNotEmpty actual="$grabOrderId" stepKey="assertOrderIdIsNotEmpty" after="grabOrderId"/> + <assertNotEmpty stepKey="assertOrderIdIsNotEmpty" after="grabOrderId"> + <actualResult type="const">$grabOrderId</actualResult> + </assertNotEmpty> <actionGroup ref="OpenOrderByIdActionGroup" stepKey="openOrder"> <argument name="orderId" value="{$grabOrderId}"/> </actionGroup> @@ -96,7 +98,9 @@ <click selector="{{AdminOrderDetailsOrderViewSection.creditMemos}}" stepKey="clickCreditMemoTab"/> <waitForPageLoad stepKey="waitForTabLoad"/> <grabTextFrom selector="{{AdminCreditMemosGridSection.memoId}}" stepKey="grabMemoId"/> - <assertNotEmpty actual="$grabMemoId" stepKey="assertMemoIdIsNotEmpty" after="grabMemoId"/> + <assertNotEmpty stepKey="assertMemoIdIsNotEmpty" after="grabMemoId"> + <actualResult type="const">$grabMemoId</actualResult> + </assertNotEmpty> <click selector="{{AdminCreditMemosGridSection.memoId}}" stepKey="clickView"/> <waitForPageLoad stepKey="waitForCreditMemo"/> <scrollTo selector="{{AdminCreditMemoViewTotalSection.subtotal}}" stepKey="scrollToTotal"/> diff --git a/app/code/Magento/Sales/Test/Mftf/Test/AdminCreateCreditMemoConfigurableProductTest.xml b/app/code/Magento/Sales/Test/Mftf/Test/AdminCreateCreditMemoConfigurableProductTest.xml index 2a841b04bd647..ab3a2cc647740 100644 --- a/app/code/Magento/Sales/Test/Mftf/Test/AdminCreateCreditMemoConfigurableProductTest.xml +++ b/app/code/Magento/Sales/Test/Mftf/Test/AdminCreateCreditMemoConfigurableProductTest.xml @@ -14,17 +14,12 @@ <title value="Create Credit Memo for Offline Payment Methods"/> <description value="Create CreditMemo return to stock only one unit of configurable product"/> <severity value="CRITICAL"/> - <testCaseId value="MC-15865"/> + <testCaseId value="MC-28444"/> <group value="sales"/> <group value="mtf_migrated"/> </annotations> <before> - <actionGroup ref="LoginAsAdmin" stepKey="LoginAsAdmin"/> - - <!-- Create Data --> <createData entity="Simple_US_Customer" stepKey="createCustomer"/> - - <!-- Create the category --> <createData entity="ApiCategory" stepKey="createCategory"/> <!-- Create the configurable product and add it to the category --> @@ -88,6 +83,7 @@ <!-- Enable payment method one of "Check/Money Order" and shipping method one of "Flat Rate" --> <magentoCLI command="config:set {{enabledCheckMoneyOrder.label}} {{enabledCheckMoneyOrder.value}}" stepKey="enableCheckMoneyOrder"/> <createData entity="FlatRateShippingMethodConfig" stepKey="enableFlatRate"/> + <actionGroup ref="AdminLoginActionGroup" stepKey="loginAsAdmin"/> </before> <after> <!-- Delete data --> @@ -97,7 +93,11 @@ <deleteData createDataKey="createConfigChildProduct2" stepKey="deleteConfigChildProduct2"/> <deleteData createDataKey="createConfigProductAttribute" stepKey="deleteConfigProductAttribute"/> <deleteData createDataKey="createCategory" stepKey="deleteApiCategory"/> - <actionGroup ref="logout" stepKey="logout"/> + <amOnPage url="{{AdminProductIndexPage.url}}" stepKey="navigateToProductIndex"/> + <actionGroup ref="ClearFiltersAdminDataGridActionGroup" stepKey="clearProductGridFilters"/> + <actionGroup ref="AdminLogoutActionGroup" stepKey="logout"/> + <!-- Reindex invalidated indices after product attribute has been created/deleted --> + <magentoCron groups="index" stepKey="reindexInvalidatedIndices"/> </after> <!-- Create Order --> <actionGroup ref="NavigateToNewOrderPageExistingCustomerActionGroup" stepKey="navigateToNewOrderPage"> @@ -106,9 +106,9 @@ <!--Add configurable product to order--> <actionGroup ref="AddConfigurableProductToOrderFromAdminActionGroup" stepKey="addConfigurableProductToOrder"> - <argument name="product" value="$$createConfigProduct$$"/> - <argument name="attribute" value="$$createConfigProductAttribute$$"/> - <argument name="option" value="$$getConfigAttributeOption1$$"/> + <argument name="product" value="$createConfigProduct$"/> + <argument name="attribute" value="$createConfigProductAttribute$"/> + <argument name="option" value="$getConfigAttributeOption1$"/> </actionGroup> <actionGroup ref="FillOrderCustomerInformationActionGroup" stepKey="fillCustomerInfo"> @@ -118,19 +118,16 @@ <actionGroup ref="OrderSelectFlatRateShippingActionGroup" stepKey="selectFlatRate"/> <click selector="{{OrdersGridSection.submitOrder}}" stepKey="submitOrder"/> <waitForPageLoad stepKey="waitForSubmitOrderPage"/> - <see stepKey="seeSuccessMessageForOrder" userInput="You created the order."/> + <waitForElementVisible selector="{{AdminMessagesSection.success}}" stepKey="waitForSuccessMessageForOrderAppears"/> + <see selector="{{AdminMessagesSection.success}}" userInput="You created the order." stepKey="seeSuccessMessageForOrder"/> <!-- Create Invoice --> <actionGroup ref="StartCreateInvoiceFromOrderPageActionGroup" stepKey="startInvoice"/> - <click selector="{{AdminInvoiceMainActionsSection.submitInvoice}}" stepKey="clickSubmitInvoice"/> - <waitForElementVisible selector="{{AdminMessagesSection.success}}" stepKey="waitForMessageAppears"/> - <see selector="{{AdminMessagesSection.success}}" userInput="The invoice has been created." stepKey="seeInvoiceCreateSuccess"/> + <actionGroup ref="SubmitInvoiceActionGroup" stepKey="clickSubmitInvoice"/> <!-- Go to Sales > Orders > find out placed order and open --> - <grabTextFrom selector="|Order # (\d+)|" stepKey="grabOrderId" /> - <assertNotEmpty actual="$grabOrderId" stepKey="assertOrderIdIsNotEmpty" after="grabOrderId"/> - <actionGroup ref="OpenOrderByIdActionGroup" stepKey="openOrder"> - <argument name="orderId" value="{$grabOrderId}"/> + <actionGroup ref="AdminOpenOrderByEntityIdActionGroup" stepKey="openOrder"> + <argument name="entityId" value="{$grabOrderIdClickSubmitInvoice}"/> </actionGroup> <!-- Click 'Credit Memo' button and fill data from dataset: partial refund --> @@ -140,11 +137,7 @@ </actionGroup> <!-- On order's page click 'Refund offline' button --> - <click selector="{{AdminCreditMemoTotalSection.submitRefundOffline}}" stepKey="clickRefundOffline"/> - <waitForPageLoad stepKey="waitForResultPage"/> - - <!-- Perform all assertions: assert refund success create message --> - <see selector="{{AdminIndexManagementSection.successMessage}}" userInput="You created the credit memo." stepKey="assertRefundSuccessCreateMessage"/> + <actionGroup ref="SubmitCreditMemoActionGroup" stepKey="clickRefundOffline"/> <!-- Assert product Qty decreased after CreditMemo --> <actionGroup ref="AdminAssertProductQtyInGridActionGroup" stepKey="assertQtyDecreased"> diff --git a/app/code/Magento/Sales/Test/Mftf/Test/AdminCreateCreditMemoPartialRefundTest.xml b/app/code/Magento/Sales/Test/Mftf/Test/AdminCreateCreditMemoPartialRefundTest.xml index 2b6008c93084e..564cde1151660 100644 --- a/app/code/Magento/Sales/Test/Mftf/Test/AdminCreateCreditMemoPartialRefundTest.xml +++ b/app/code/Magento/Sales/Test/Mftf/Test/AdminCreateCreditMemoPartialRefundTest.xml @@ -13,13 +13,13 @@ <stories value="Credit memo entity"/> <title value="Create Credit Memo for Offline Payment Methods"/> <description value="Assert items return to stock (partial refund)"/> - <severity value="CRITICAL"/> + <severity value="BLOCKER"/> <testCaseId value="MC-15861"/> <group value="sales"/> <group value="mtf_migrated"/> </annotations> <before> - <actionGroup ref="LoginAsAdmin" stepKey="LoginAsAdmin"/> + <actionGroup ref="AdminLoginActionGroup" stepKey="LoginAsAdmin"/> <!-- Create Data --> <createData entity="Simple_US_Customer" stepKey="createCustomer"/> @@ -37,7 +37,7 @@ <deleteData createDataKey="createCustomer" stepKey="deleteCustomer"/> <deleteData createDataKey="createCategory" stepKey="deleteCategory"/> <deleteData createDataKey="createProduct" stepKey="deleteProduct"/> - <actionGroup ref="logout" stepKey="logout"/> + <actionGroup ref="AdminLogoutActionGroup" stepKey="logout"/> </after> <!-- Create Order --> <actionGroup ref="NavigateToNewOrderPageExistingCustomerActionGroup" stepKey="navigateToNewOrderPage"> @@ -64,7 +64,9 @@ <!-- Go to Sales > Orders > find out placed order and open --> <grabTextFrom selector="|Order # (\d+)|" stepKey="grabOrderId" /> - <assertNotEmpty actual="$grabOrderId" stepKey="assertOrderIdIsNotEmpty" after="grabOrderId"/> + <assertNotEmpty stepKey="assertOrderIdIsNotEmpty" after="grabOrderId"> + <actualResult type="const">$grabOrderId</actualResult> + </assertNotEmpty> <actionGroup ref="OpenOrderByIdActionGroup" stepKey="openOrder"> <argument name="orderId" value="{$grabOrderId}"/> </actionGroup> @@ -90,7 +92,9 @@ <click selector="{{AdminOrderDetailsOrderViewSection.creditMemos}}" stepKey="clickCreditMemoTab"/> <waitForPageLoad stepKey="waitForTabLoad"/> <grabTextFrom selector="{{AdminCreditMemosGridSection.memoId}}" stepKey="grabMemoId"/> - <assertNotEmpty actual="$grabMemoId" stepKey="assertMemoIdIsNotEmpty" after="grabMemoId"/> + <assertNotEmpty stepKey="assertMemoIdIsNotEmpty" after="grabMemoId"> + <actualResult type="const">$grabMemoId</actualResult> + </assertNotEmpty> <click selector="{{AdminCreditMemosGridSection.memoId}}" stepKey="clickView"/> <waitForPageLoad stepKey="waitForCreditMemo"/> <scrollTo selector="{{AdminCreditMemoTotalSection.grandTotal}}" stepKey="scrollToTotal"/> diff --git a/app/code/Magento/Sales/Test/Mftf/Test/AdminCreateCreditMemoWithCashOnDeliveryTest.xml b/app/code/Magento/Sales/Test/Mftf/Test/AdminCreateCreditMemoWithCashOnDeliveryTest.xml index 452c54d188c57..d888e6841e34d 100644 --- a/app/code/Magento/Sales/Test/Mftf/Test/AdminCreateCreditMemoWithCashOnDeliveryTest.xml +++ b/app/code/Magento/Sales/Test/Mftf/Test/AdminCreateCreditMemoWithCashOnDeliveryTest.xml @@ -19,7 +19,7 @@ <group value="mtf_migrated"/> </annotations> <before> - <actionGroup ref="LoginAsAdmin" stepKey="LoginAsAdmin"/> + <actionGroup ref="AdminLoginActionGroup" stepKey="LoginAsAdmin"/> <!-- Create Data --> <createData entity="Simple_US_Customer" stepKey="createCustomer"/> @@ -39,7 +39,7 @@ <deleteData createDataKey="createCustomer" stepKey="deleteCustomer"/> <deleteData createDataKey="createCategory" stepKey="deleteCategory"/> <deleteData createDataKey="createProduct" stepKey="deleteProduct"/> - <actionGroup ref="logout" stepKey="logout"/> + <actionGroup ref="AdminLogoutActionGroup" stepKey="logout"/> </after> <!-- Create Order --> @@ -70,7 +70,9 @@ <!-- Go to Sales > Orders > find out placed order and open --> <grabTextFrom selector="|Order # (\d+)|" stepKey="grabOrderId" /> - <assertNotEmpty actual="$grabOrderId" stepKey="assertOrderIdIsNotEmpty" after="grabOrderId"/> + <assertNotEmpty stepKey="assertOrderIdIsNotEmpty" after="grabOrderId"> + <actualResult type="const">$grabOrderId</actualResult> + </assertNotEmpty> <actionGroup ref="OpenOrderByIdActionGroup" stepKey="openOrder"> <argument name="orderId" value="{$grabOrderId}"/> </actionGroup> @@ -94,14 +96,18 @@ <click selector="{{AdminOrderDetailsOrderViewSection.creditMemos}}" stepKey="clickCreditMemoTab"/> <waitForPageLoad stepKey="waitForTabLoad"/> <grabTextFrom selector="{{AdminCreditMemosGridSection.memoId}}" stepKey="grabMemoId"/> - <assertNotEmpty actual="$grabMemoId" stepKey="assertMemoIdIsNotEmpty" after="grabMemoId"/> + <assertNotEmpty stepKey="assertMemoIdIsNotEmpty" after="grabMemoId"> + <actualResult type="const">$grabMemoId</actualResult> + </assertNotEmpty> <click selector="{{AdminCreditMemosGridSection.memoId}}" stepKey="clickView"/> <waitForPageLoad stepKey="waitForCreditMemo"/> <scrollTo selector="{{AdminCreditMemoViewTotalSection.subtotal}}" stepKey="scrollToTotal"/> - <see selector="{{AdminCreditMemoViewTotalSection.subtotal}}" userInput="$560.00" stepKey="seeSubtotal"/> - <see selector="{{AdminCreditMemoViewTotalSection.adjustmentRefund}}" userInput="$5.00" stepKey="seeAdjustmentRefund"/> - <see selector="{{AdminCreditMemoViewTotalSection.adjustmentFee}}" userInput="$10.00" stepKey="seeAdjustmentFee"/> - <see selector="{{AdminCreditMemoViewTotalSection.grandTotal}}" userInput="$555.00" stepKey="assertRefundOnCreditMemoTab"/> + <actionGroup ref="AssertAdminCreditMemoViewPageTotalsActionGroup" stepKey="assertCreditMemoViewPageTotals"> + <argument name="subtotal" value="$560.00"/> + <argument name="adjustmentRefund" value="$5.00"/> + <argument name="adjustmentFee" value="$10.00"/> + <argument name="grandTotal" value="$555.00"/> + </actionGroup> <!--Login to storefront as previously created customer--> <actionGroup ref="LoginToStorefrontActionGroup" stepKey="loginAsCustomer"> diff --git a/app/code/Magento/Sales/Test/Mftf/Test/AdminCreateCreditMemoWithPurchaseOrderTest.xml b/app/code/Magento/Sales/Test/Mftf/Test/AdminCreateCreditMemoWithPurchaseOrderTest.xml index 231ff78d7d8fb..7974d594eb99c 100644 --- a/app/code/Magento/Sales/Test/Mftf/Test/AdminCreateCreditMemoWithPurchaseOrderTest.xml +++ b/app/code/Magento/Sales/Test/Mftf/Test/AdminCreateCreditMemoWithPurchaseOrderTest.xml @@ -13,13 +13,13 @@ <stories value="Credit memo entity"/> <title value="Create Credit Memo with purchase order payment method"/> <description value="Create Credit Memo with purchase order payment payment and assert 0 shipping refund"/> - <severity value="CRITICAL"/> + <severity value="BLOCKER"/> <testCaseId value="MC-15864"/> <group value="sales"/> <group value="mtf_migrated"/> </annotations> <before> - <actionGroup ref="LoginAsAdmin" stepKey="LoginAsAdmin"/> + <actionGroup ref="AdminLoginActionGroup" stepKey="LoginAsAdmin"/> <!-- Create Data --> <createData entity="Simple_US_Customer" stepKey="createCustomer"/> @@ -39,7 +39,7 @@ <deleteData createDataKey="createCustomer" stepKey="deleteCustomer"/> <deleteData createDataKey="createCategory" stepKey="deleteCategory"/> <deleteData createDataKey="createProduct" stepKey="deleteProduct"/> - <actionGroup ref="logout" stepKey="logout"/> + <actionGroup ref="AdminLogoutActionGroup" stepKey="logout"/> </after> <!-- Create Order --> @@ -73,7 +73,9 @@ <!-- Go to Sales > Orders > find out placed order and open --> <grabTextFrom selector="|Order # (\d+)|" stepKey="grabOrderId" /> - <assertNotEmpty actual="$grabOrderId" stepKey="assertOrderIdIsNotEmpty" after="grabOrderId"/> + <assertNotEmpty stepKey="assertOrderIdIsNotEmpty" after="grabOrderId"> + <actualResult type="const">$grabOrderId</actualResult> + </assertNotEmpty> <actionGroup ref="OpenOrderByIdActionGroup" stepKey="openOrder"> <argument name="orderId" value="{$grabOrderId}"/> </actionGroup> @@ -97,7 +99,9 @@ <click selector="{{AdminOrderDetailsOrderViewSection.creditMemos}}" stepKey="clickCreditMemoTab"/> <waitForPageLoad stepKey="waitForTabLoad"/> <grabTextFrom selector="{{AdminCreditMemosGridSection.memoId}}" stepKey="grabMemoId"/> - <assertNotEmpty actual="$grabMemoId" stepKey="assertMemoIdIsNotEmpty" after="grabMemoId"/> + <assertNotEmpty stepKey="assertMemoIdIsNotEmpty" after="grabMemoId"> + <actualResult type="const">$grabMemoId</actualResult> + </assertNotEmpty> <click selector="{{AdminCreditMemosGridSection.memoId}}" stepKey="clickView"/> <waitForPageLoad stepKey="waitForCreditMemo"/> <scrollTo selector="{{AdminCreditMemoViewTotalSection.subtotal}}" stepKey="scrollToTotal"/> diff --git a/app/code/Magento/Sales/Test/Mftf/Test/AdminCreateInvoiceTest.xml b/app/code/Magento/Sales/Test/Mftf/Test/AdminCreateInvoiceTest.xml index 73c126bb7794f..cdc81c66b833d 100644 --- a/app/code/Magento/Sales/Test/Mftf/Test/AdminCreateInvoiceTest.xml +++ b/app/code/Magento/Sales/Test/Mftf/Test/AdminCreateInvoiceTest.xml @@ -25,7 +25,7 @@ </createData> </before> <after> - <actionGroup ref="logout" stepKey="adminLogout"/> + <actionGroup ref="AdminLogoutActionGroup" stepKey="adminLogout"/> <deleteData createDataKey="createCategory" stepKey="deleteProduct1"/> <deleteData createDataKey="createProduct" stepKey="deleteCategory1"/> </after> @@ -36,7 +36,7 @@ <moveMouseOver selector="{{StorefrontCategoryMainSection.ProductItemInfo}}" stepKey="hoverProduct"/> <click selector="{{StorefrontCategoryMainSection.AddToCartBtn}}" stepKey="addToCart"/> <waitForElementVisible selector="{{StorefrontCategoryMainSection.SuccessMsg}}" time="30" stepKey="waitForProductAdded"/> - <click selector="{{StorefrontMinicartSection.showCart}}" stepKey="clickCart"/> + <actionGroup ref="StorefrontClickOnMiniCartActionGroup" stepKey="clickCart"/> <click selector="{{StorefrontMinicartSection.goToCheckout}}" stepKey="goToCheckout"/> <waitForPageLoad stepKey="waitForPageLoad2"/> <fillField selector="{{CheckoutShippingGuestInfoSection.email}}" userInput="{{CustomerEntityOne.email}}" stepKey="enterEmail"/> @@ -59,7 +59,7 @@ <grabTextFrom selector="{{CheckoutSuccessMainSection.orderNumber}}" stepKey="grabOrderNumber"/> <!-- end todo --> - <actionGroup ref="LoginAsAdmin" stepKey="loginAsAdmin1"/> + <actionGroup ref="AdminLoginActionGroup" stepKey="loginAsAdmin1"/> <amOnPage url="{{AdminOrdersPage.url}}" stepKey="onOrdersPage"/> <waitForLoadingMaskToDisappear stepKey="waitForLoadingMask3"/> diff --git a/app/code/Magento/Sales/Test/Mftf/Test/AdminCreateOrderAddProductCheckboxTest.xml b/app/code/Magento/Sales/Test/Mftf/Test/AdminCreateOrderAddProductCheckboxTest.xml index 8fe49558cf091..baef605ad52af 100644 --- a/app/code/Magento/Sales/Test/Mftf/Test/AdminCreateOrderAddProductCheckboxTest.xml +++ b/app/code/Magento/Sales/Test/Mftf/Test/AdminCreateOrderAddProductCheckboxTest.xml @@ -26,7 +26,7 @@ <createData entity="ApiProductWithDescription" stepKey="createSimpleProduct"/> <!-- Login to Admin Panel --> - <actionGroup ref="LoginAsAdmin" stepKey="loginAsAdmin"/> + <actionGroup ref="AdminLoginActionGroup" stepKey="loginAsAdmin"/> </before> <!-- Initiate create new order --> @@ -42,7 +42,7 @@ <seeCheckboxIsChecked selector="{{AdminOrderFormItemsSection.rowCheck('1')}}" stepKey="verifyProductChecked"/> <after> - <actionGroup ref="logout" stepKey="logout"/> + <actionGroup ref="AdminLogoutActionGroup" stepKey="logout"/> <deleteData createDataKey="createSimpleProduct" stepKey="deleteSimpleProduct"/> <deleteData createDataKey="createSimpleCustomer" stepKey="deleteSimpleCustomer"/> </after> diff --git a/app/code/Magento/Sales/Test/Mftf/Test/AdminCreateOrderAndCheckTheReorderTest.xml b/app/code/Magento/Sales/Test/Mftf/Test/AdminCreateOrderAndCheckTheReorderTest.xml index 338765d650d13..ab5c089dfcc30 100644 --- a/app/code/Magento/Sales/Test/Mftf/Test/AdminCreateOrderAndCheckTheReorderTest.xml +++ b/app/code/Magento/Sales/Test/Mftf/Test/AdminCreateOrderAndCheckTheReorderTest.xml @@ -26,7 +26,7 @@ <createData entity="SimpleProduct_25" stepKey="simpleProduct"> <field key="price">5</field> </createData> - <actionGroup ref="LoginAsAdmin" stepKey="loginAsAdmin"/> + <actionGroup ref="AdminLoginActionGroup" stepKey="loginAsAdmin"/> </before> <actionGroup ref="NavigateToNewOrderPageExistingCustomerActionGroup" stepKey="navigateToNewOrderWithExistingCustomer"> <argument name="customer" value="$$simpleCustomer$$"/> @@ -52,7 +52,7 @@ <actionGroup ref="StorefrontCustomerReorderButtonNotVisibleActionGroup" stepKey="checkReorderButton"/> <after> <actionGroup ref="StorefrontCustomerLogoutActionGroup" stepKey="customerLogout"/> - <actionGroup ref="logout" stepKey="adminLogout"/> + <actionGroup ref="AdminLogoutActionGroup" stepKey="adminLogout"/> <magentoCLI command="config:set payment/cashondelivery/active 0" stepKey="disableCashOnDeliveryMethod"/> <deleteData createDataKey="simpleCustomer" stepKey="deleteSimpleCustomer"/> <deleteData createDataKey="simpleProduct" stepKey="deleteSimpleProduct"/> diff --git a/app/code/Magento/Sales/Test/Mftf/Test/AdminCreateOrderForCustomerWithTwoAddressesTaxableAndNonTaxableTest.xml b/app/code/Magento/Sales/Test/Mftf/Test/AdminCreateOrderForCustomerWithTwoAddressesTaxableAndNonTaxableTest.xml index 1c35a9e900a8a..2f89573b00101 100644 --- a/app/code/Magento/Sales/Test/Mftf/Test/AdminCreateOrderForCustomerWithTwoAddressesTaxableAndNonTaxableTest.xml +++ b/app/code/Magento/Sales/Test/Mftf/Test/AdminCreateOrderForCustomerWithTwoAddressesTaxableAndNonTaxableTest.xml @@ -14,7 +14,7 @@ <description value="Tax should not be displayed for non taxable address when switching from taxable address"/> <testCaseId value="MC-21721"/> <features value="Sales"/> - <severity value="MAJOR"/> + <severity value="BLOCKER"/> <group value="Sales"/> </annotations> <before> @@ -33,7 +33,7 @@ <!--Create tax rule for US-CA--> <createData entity="defaultTaxRule" stepKey="createTaxRule"/> <!--Login as admin--> - <actionGroup ref="LoginAsAdmin" stepKey="loginAsAdmin"/> + <actionGroup ref="AdminLoginActionGroup" stepKey="loginAsAdmin"/> </before> <!--Step 1: Create new order for customer--> <actionGroup ref="NavigateToNewOrderPageExistingCustomerActionGroup" stepKey="navigateToNewOrderWithExistingCustomer"> @@ -45,6 +45,10 @@ </actionGroup> <!--Step 2: Select taxable address as billing address--> <selectOption selector="{{AdminOrderFormBillingAddressSection.selectAddress}}" userInput="{{US_Address_CA.state}}" stepKey="selectTaxableAddress" /> + <waitForPageLoad stepKey="waitForChangeBillingAddress"/> + <!--Step 3: Set shipping address same as billing --> + <checkOption selector="{{AdminOrderFormShippingAddressSection.SameAsBilling}}" stepKey="checkSameAsBillingAddressCheckbox"/> + <waitForPageLoad stepKey="waitForChangeShippingAddress"/> <!--Step 3: Select FlatRate shipping method--> <actionGroup ref="OrderSelectFlatRateShippingActionGroup" stepKey="selectFlatRateShippingMethod"/> <!--Step 4: Verify that tax is applied to the order--> @@ -67,7 +71,7 @@ <!--Delete tax rule--> <deleteData createDataKey="createTaxRule" stepKey="deleteTaxRule"/> <!--Logout--> - <actionGroup ref="logout" stepKey="logout"/> + <actionGroup ref="AdminLogoutActionGroup" stepKey="logout"/> <!--Disable free shipping method --> <magentoCLI command="config:set {{DisableFreeShippingConfigData.path}} {{DisableFreeShippingConfigData.value}}" stepKey="disableFreeShipping"/> </after> diff --git a/app/code/Magento/Sales/Test/Mftf/Test/AdminCreateOrderStatusDuplicatingCodeTest.xml b/app/code/Magento/Sales/Test/Mftf/Test/AdminCreateOrderStatusDuplicatingCodeTest.xml index 40a731410a899..23dca916781f1 100644 --- a/app/code/Magento/Sales/Test/Mftf/Test/AdminCreateOrderStatusDuplicatingCodeTest.xml +++ b/app/code/Magento/Sales/Test/Mftf/Test/AdminCreateOrderStatusDuplicatingCodeTest.xml @@ -19,10 +19,10 @@ <group value="mtf_migrated"/> </annotations> <before> - <actionGroup ref="LoginAsAdmin" stepKey="loginAsAdmin"/> + <actionGroup ref="AdminLoginActionGroup" stepKey="loginAsAdmin"/> </before> <after> - <actionGroup ref="logout" stepKey="logout"/> + <actionGroup ref="AdminLogoutActionGroup" stepKey="logout"/> </after> <!-- Go to new order status page --> diff --git a/app/code/Magento/Sales/Test/Mftf/Test/AdminCreateOrderStatusDuplicatingLabelTest.xml b/app/code/Magento/Sales/Test/Mftf/Test/AdminCreateOrderStatusDuplicatingLabelTest.xml index d1381bbb1efb0..d3cd3e8b8549c 100644 --- a/app/code/Magento/Sales/Test/Mftf/Test/AdminCreateOrderStatusDuplicatingLabelTest.xml +++ b/app/code/Magento/Sales/Test/Mftf/Test/AdminCreateOrderStatusDuplicatingLabelTest.xml @@ -19,10 +19,10 @@ <group value="mtf_migrated"/> </annotations> <before> - <actionGroup ref="LoginAsAdmin" stepKey="loginAsAdmin"/> + <actionGroup ref="AdminLoginActionGroup" stepKey="loginAsAdmin"/> </before> <after> - <actionGroup ref="logout" stepKey="logout"/> + <actionGroup ref="AdminLogoutActionGroup" stepKey="logout"/> </after> <!-- Go to new order status page --> diff --git a/app/code/Magento/Sales/Test/Mftf/Test/AdminCreateOrderStatusTest.xml b/app/code/Magento/Sales/Test/Mftf/Test/AdminCreateOrderStatusTest.xml index c2daaac84dd42..a30040045a4ca 100644 --- a/app/code/Magento/Sales/Test/Mftf/Test/AdminCreateOrderStatusTest.xml +++ b/app/code/Magento/Sales/Test/Mftf/Test/AdminCreateOrderStatusTest.xml @@ -19,10 +19,10 @@ <group value="mtf_migrated"/> </annotations> <before> - <actionGroup ref="LoginAsAdmin" stepKey="loginAsAdmin"/> + <actionGroup ref="AdminLoginActionGroup" stepKey="loginAsAdmin"/> </before> <after> - <actionGroup ref="logout" stepKey="logout"/> + <actionGroup ref="AdminLogoutActionGroup" stepKey="logout"/> </after> <!-- Go to new order status page --> diff --git a/app/code/Magento/Sales/Test/Mftf/Test/AdminCreateOrderWithBundleProductTest.xml b/app/code/Magento/Sales/Test/Mftf/Test/AdminCreateOrderWithBundleProductTest.xml index 1749fcd6c218e..1401930131b13 100644 --- a/app/code/Magento/Sales/Test/Mftf/Test/AdminCreateOrderWithBundleProductTest.xml +++ b/app/code/Magento/Sales/Test/Mftf/Test/AdminCreateOrderWithBundleProductTest.xml @@ -76,7 +76,7 @@ <requiredEntity createDataKey="simple2"/> <field key="qty">2</field> </createData> - <actionGroup ref="LoginAsAdmin" stepKey="loginAsAdmin"/> + <actionGroup ref="AdminLoginActionGroup" stepKey="loginAsAdmin"/> </before> <!--Create new customer order--> @@ -101,7 +101,7 @@ <actionGroup ref="VerifyCreatedOrderInformationActionGroup" stepKey="verifyCreatedOrderInformation"/> <after> - <actionGroup ref="logout" stepKey="logout"/> + <actionGroup ref="AdminLogoutActionGroup" stepKey="logout"/> <deleteData createDataKey="product" stepKey="delete"/> <deleteData createDataKey="simpleCustomer" stepKey="deleteSimpleCustomer"/> diff --git a/app/code/Magento/Sales/Test/Mftf/Test/AdminCreateOrderWithCustomerWithoutEmailTest.xml b/app/code/Magento/Sales/Test/Mftf/Test/AdminCreateOrderWithCustomerWithoutEmailTest.xml new file mode 100644 index 0000000000000..0be7e20be5aea --- /dev/null +++ b/app/code/Magento/Sales/Test/Mftf/Test/AdminCreateOrderWithCustomerWithoutEmailTest.xml @@ -0,0 +1,61 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + /** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +--> + +<tests xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/testSchema.xsd"> + <test name="AdminCreateOrderWithCustomerWithoutEmailTest"> + <annotations> + <title value="Admin Create Order"/> + <stories value="Admin create order with customer without email."/> + <description value="Verify, admin able to create order with customer without email."/> + <severity value="MINOR"/> + <group value="Sales"/> + </annotations> + <before> + <!--Disable required 'email' field on create order page.--> + <magentoCLI command="config:set {{DisableEmailRequiredForOrder.path}} {{DisableEmailRequiredForOrder.value}}" stepKey="disableRequiredFieldEmailForAdminOrderCreation"/> + <!--Create test data.--> + <createData entity="_defaultCategory" stepKey="category"/> + <createData entity="SimpleProduct" stepKey="simpleProduct"> + <requiredEntity createDataKey="category"/> + </createData> + <actionGroup ref="AdminLoginActionGroup" stepKey="loginAsAdmin"/> + <magentoCLI command="cache:flush" stepKey="flushCache"/> + </before> + <after> + <!--Clean up created test data.--> + <deleteData createDataKey="simpleProduct" stepKey="deleteSimpleProduct"/> + <deleteData createDataKey="category" stepKey="deleteCategory"/> + <!--Enable required 'email' field on create order page.--> + <magentoCLI command="config:set {{EnableEmailRequiredForOrder.path}} {{EnableEmailRequiredForOrder.value}}" stepKey="enableRequiredFieldEmailForAdminOrderCreation"/> + <actionGroup ref="AdminLogoutActionGroup" stepKey="logout"/> + <magentoCLI command="cache:flush" stepKey="flushCache"/> + </after> + + <!--Create order.--> + <actionGroup ref="NavigateToNewOrderPageNewCustomerActionGroup" stepKey="navigateToNewOrderPageNewCustomerActionGroup" /> + <actionGroup ref="AddSimpleProductToOrderActionGroup" stepKey="addSimpleProductToOrder"> + <argument name="product" value="$$simpleProduct$$"/> + <argument name="productQty" value="{{SimpleProduct.quantity}}"/> + </actionGroup> + <!--Fill customer address without 'email'--> + <actionGroup ref="FillOrderCustomerInformationActionGroup" stepKey="fillCustomerInformation"> + <argument name="customer" value="Simple_US_Customer_CA_Without_Email"/> + <argument name="address" value="US_Address_CA"/> + </actionGroup> + <actionGroup ref="OrderSelectFlatRateShippingActionGroup" stepKey="orderSelectFlatRateShippingMethod"/> + <actionGroup ref="AdminSubmitOrderActionGroup" stepKey="submitOrder"/> + <!--Verify, 'email' is generated.--> + <actionGroup ref="VerifyCreatedOrderInformationWithGeneratedEmailActionGroup" stepKey="verifyCustomerEmail"> + <argument name="email" value="@example.com"/> + </actionGroup> + <grabTextFrom selector="{{AdminOrderDetailsInformationSection.customerEmail}}" stepKey="generatedCustomerEmail"/> + <actionGroup ref="DeleteCustomerByEmailActionGroup" stepKey="deleteCustomer"> + <argument name="email" value="$generatedCustomerEmail"/> + </actionGroup> + </test> +</tests> diff --git a/app/code/Magento/Sales/Test/Mftf/Test/AdminCreateOrderWithDateTimeOptionUITest.xml b/app/code/Magento/Sales/Test/Mftf/Test/AdminCreateOrderWithDateTimeOptionUITest.xml new file mode 100644 index 0000000000000..b0926948f2bf6 --- /dev/null +++ b/app/code/Magento/Sales/Test/Mftf/Test/AdminCreateOrderWithDateTimeOptionUITest.xml @@ -0,0 +1,54 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + /** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +--> + +<tests xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/testSchema.xsd"> + <test name="AdminCreateOrderWithDateTimeOptionUITest"> + <annotations> + <stories value="Create Order"/> + <title value="Admin create order with date time option UI test"/> + <description value="Check asterisk rendered correctly for Product with custom option (datetime) at backend"/> + <features value="Sales"/> + <stories value="Create order in Admin"/> + <severity value="MINOR"/> + <group value="Sales"/> + </annotations> + + <before> + <createData entity="Simple_US_Customer" stepKey="createCustomer"/> + <createData entity="_defaultCategory" stepKey="createCategory"/> + <createData entity="_defaultProduct" stepKey="createProduct"> + <requiredEntity createDataKey="createCategory"/> + </createData> + <updateData createDataKey="createProduct" entity="productWithDateTimeOption" stepKey="updateProductWithOption"/> + <actionGroup ref="AdminLoginActionGroup" stepKey="loginAsAdmin"/> + </before> + <after> + <actionGroup ref="AdminLogoutActionGroup" stepKey="logout"/> + <deleteData createDataKey="createProduct" stepKey="deleteSimpleProduct"/> + <deleteData createDataKey="createCustomer" stepKey="deleteSimpleCustomer"/> + </after> + + <actionGroup ref="NavigateToNewOrderPageExistingCustomerActionGroup" stepKey="navigateToNewOrderWithExistingCustomer"> + <argument name="customer" value="$$createCustomer$$"/> + </actionGroup> + + <click selector="{{AdminOrderFormItemsSection.addProducts}}" stepKey="clickAddProducts"/> + <fillField selector="{{AdminOrderFormItemsSection.skuFilter}}" userInput="$$createProduct.sku$$" stepKey="fillSkuFilter"/> + <click selector="{{AdminOrderFormItemsSection.search}}" stepKey="clickSearch"/> + <scrollTo selector="{{AdminOrderFormItemsSection.rowCheck('1')}}" x="0" y="-100" stepKey="scrollToCheckColumn"/> + <checkOption selector="{{AdminOrderFormItemsSection.rowCheck('1')}}" stepKey="selectProduct"/> + <waitForAjaxLoad stepKey="waitForAjaxLoad"/> + <executeJS function="{{AdminProductCustomizableOptionsSection.requiredFieldIndicator}}" stepKey="dateTimeRequiredFieldIndicator"/> + <assertEquals message="pass" stepKey="assertRequiredFieldIndicator"> + <actualResult type="variable">dateTimeRequiredFieldIndicator</actualResult> + <expectedResult type="string">"*"</expectedResult> + </assertEquals> + </test> +</tests> + diff --git a/app/code/Magento/Sales/Test/Mftf/Test/AdminCreateOrderWithMinimumAmountEnabledTest.xml b/app/code/Magento/Sales/Test/Mftf/Test/AdminCreateOrderWithMinimumAmountEnabledTest.xml index bfaf31007b10a..ade1f783c1309 100644 --- a/app/code/Magento/Sales/Test/Mftf/Test/AdminCreateOrderWithMinimumAmountEnabledTest.xml +++ b/app/code/Magento/Sales/Test/Mftf/Test/AdminCreateOrderWithMinimumAmountEnabledTest.xml @@ -24,7 +24,7 @@ <createData entity="SimpleProduct" stepKey="createProduct"> <requiredEntity createDataKey="createCategory"/> </createData> - <actionGroup ref="LoginAsAdmin" stepKey="loginAsAdmin"/> + <actionGroup ref="AdminLoginActionGroup" stepKey="loginAsAdmin"/> <actionGroup ref="ClearCacheActionGroup" stepKey="clearCacheBefore"/> </before> <after> @@ -71,7 +71,9 @@ <see selector="{{AdminOrderDetailsMessagesSection.successMessage}}" userInput="You created the order." stepKey="seeSuccessMessage"/> <see selector="{{AdminOrderDetailsInformationSection.orderStatus}}" userInput="Pending" stepKey="seeOrderPendingStatus"/> <grabTextFrom selector="|Order # (\d+)|" stepKey="orderId"/> - <assertNotEmpty actual="$orderId" stepKey="assertOrderIdIsNotEmpty"/> + <assertNotEmpty stepKey="assertOrderIdIsNotEmpty"> + <actualResult type="const">$orderId</actualResult> + </assertNotEmpty> <actionGroup ref="VerifyBasicOrderInformationActionGroup" stepKey="verifyOrderInformation"> <argument name="customer" value="Simple_US_Customer"/> <argument name="shippingAddress" value="US_Address_TX"/> diff --git a/app/code/Magento/Sales/Test/Mftf/Test/AdminCreateOrderWithSelectedShoppingCartItemsTest.xml b/app/code/Magento/Sales/Test/Mftf/Test/AdminCreateOrderWithSelectedShoppingCartItemsTest.xml index 58a680ea61c87..6e8ec14fc67cb 100644 --- a/app/code/Magento/Sales/Test/Mftf/Test/AdminCreateOrderWithSelectedShoppingCartItemsTest.xml +++ b/app/code/Magento/Sales/Test/Mftf/Test/AdminCreateOrderWithSelectedShoppingCartItemsTest.xml @@ -13,7 +13,7 @@ <stories value="MC-17838: Shopping cart items added to the order created in the admin"/> <description value="Shopping cart items must not be added to the order unless they were moved manually"/> <features value="Sales"/> - <severity value="AVERAGE"/> + <severity value="BLOCKER"/> <group value="Sales"/> </annotations> <before> @@ -37,7 +37,7 @@ </createData> <!--Login as admin--> - <actionGroup ref="LoginAsAdmin" stepKey="loginAsAdmin"/> + <actionGroup ref="AdminLoginActionGroup" stepKey="loginAsAdmin"/> </before> <!-- Step 1: Go to Storefront as Customer --> @@ -97,7 +97,7 @@ <deleteData createDataKey="simpleCustomer" stepKey="deleteSimpleCustomer"/> <!--Logout--> - <actionGroup ref="logout" stepKey="logout"/> + <actionGroup ref="AdminLogoutActionGroup" stepKey="logout"/> </after> </test> </tests> diff --git a/app/code/Magento/Sales/Test/Mftf/Test/AdminCreateOrderWithSimpleProductCustomOptionFileTest.xml b/app/code/Magento/Sales/Test/Mftf/Test/AdminCreateOrderWithSimpleProductCustomOptionFileTest.xml new file mode 100644 index 0000000000000..82e9606175764 --- /dev/null +++ b/app/code/Magento/Sales/Test/Mftf/Test/AdminCreateOrderWithSimpleProductCustomOptionFileTest.xml @@ -0,0 +1,54 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + /** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +--> + +<tests xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/testSchema.xsd"> + <test name="AdminCreateOrderWithSimpleProductCustomOptionFileTest"> + <annotations> + <title value="Create Order with simple product with custom option."/> + <description value="Verify, admin able to change file for custom option during order creation."/> + <features value="Sales"/> + <severity value="MAJOR"/> + <group value="Sales"/> + </annotations> + <before> + <!--Create test data.--> + <createData entity="_defaultCategory" stepKey="category"/> + <createData entity="SimpleProduct" stepKey="simpleProduct"> + <requiredEntity createDataKey="category"/> + </createData> + <createData entity="Simple_US_Customer_CA" stepKey="customer"/> + <actionGroup ref="AdminLoginActionGroup" stepKey="loginAsAdmin"/> + </before> + <after> + <!--Clean up created test data.--> + <deleteData createDataKey="simpleProduct" stepKey="deleteSimpleProduct"/> + <deleteData createDataKey="category" stepKey="deleteCategory"/> + <deleteData createDataKey="customer" stepKey="deleteCustomer" /> + <actionGroup ref="AdminLogoutActionGroup" stepKey="logout"/> + </after> + + <!--Add option to product.--> + <actionGroup ref="AdminProductPageOpenByIdActionGroup" stepKey="navigateToProductEditPage"> + <argument name="productId" value="$simpleProduct.id$"/> + </actionGroup> + <actionGroup ref="AddProductCustomOptionFileActionGroup" stepKey="addOption"> + <argument name="option" value="ProductOptionFile"/> + </actionGroup> + <actionGroup ref="SaveProductFormActionGroup" stepKey="saveProduct"/> + <!--Create order.--> + <actionGroup ref="NavigateToNewOrderPageExistingCustomerActionGroup" stepKey="navigateToNewOrderWithExistingCustomer"> + <argument name="customer" value="$customer$"/> + </actionGroup> + <actionGroup ref="AdminAddSimpleProductWithCustomOptionFileToOrderActionGroup" stepKey="addSimpleProductToOrder"> + <argument name="product" value="$simpleProduct$"/> + <argument name="productQty" value="$simpleProduct.quantity$"/> + </actionGroup> + <!--Verify, admin able to change file for custom option.--> + <actionGroup ref="AdminChangeCustomerOptionFileActionGroup" stepKey="changeFile"/> + </test> +</tests> diff --git a/app/code/Magento/Sales/Test/Mftf/Test/AdminCreateOrderWithSimpleProductTest.xml b/app/code/Magento/Sales/Test/Mftf/Test/AdminCreateOrderWithSimpleProductTest.xml index 76f916d55ee92..1c8cf2219f13b 100644 --- a/app/code/Magento/Sales/Test/Mftf/Test/AdminCreateOrderWithSimpleProductTest.xml +++ b/app/code/Magento/Sales/Test/Mftf/Test/AdminCreateOrderWithSimpleProductTest.xml @@ -26,7 +26,7 @@ <createData entity="SimpleProduct_25" stepKey="simpleProduct"> <field key="price">5</field> </createData> - <actionGroup ref="LoginAsAdmin" stepKey="loginAsAdmin"/> + <actionGroup ref="AdminLoginActionGroup" stepKey="loginAsAdmin"/> </before> <actionGroup ref="NavigateToNewOrderPageExistingCustomerActionGroup" stepKey="navigateToNewOrderWithExistingCustomer"> <argument name="customer" value="$$simpleCustomer$$"/> @@ -50,7 +50,7 @@ <argument name="stockStatus" value="Out of Stock"/> </actionGroup> <after> - <actionGroup ref="logout" stepKey="logout"/> + <actionGroup ref="AdminLogoutActionGroup" stepKey="logout"/> <magentoCLI command="config:set payment/cashondelivery/active 0" stepKey="disableCashOnDeliveryMethod"/> <deleteData createDataKey="simpleCustomer" stepKey="deleteSimpleCustomer"/> <deleteData createDataKey="simpleProduct" stepKey="deleteSimpleProduct"/> diff --git a/app/code/Magento/Sales/Test/Mftf/Test/AdminFreeShippingNotAvailableIfMinimumOrderAmountNotMatchOrderTotalTest.xml b/app/code/Magento/Sales/Test/Mftf/Test/AdminFreeShippingNotAvailableIfMinimumOrderAmountNotMatchOrderTotalTest.xml index 9ff6f3bdac985..1c59f6f936cef 100644 --- a/app/code/Magento/Sales/Test/Mftf/Test/AdminFreeShippingNotAvailableIfMinimumOrderAmountNotMatchOrderTotalTest.xml +++ b/app/code/Magento/Sales/Test/Mftf/Test/AdminFreeShippingNotAvailableIfMinimumOrderAmountNotMatchOrderTotalTest.xml @@ -27,7 +27,7 @@ <createData entity="DisableFlatRateShippingMethodConfig" stepKey="disableFlatRate"/> <createData entity="FreeShippinMethodConfig" stepKey="enableFreeShippingMethod"/> <createData entity="setFreeShippingSubtotal" stepKey="setFreeShippingSubtotal"/> - <actionGroup ref="LoginAsAdmin" stepKey="loginAsAdmin"/> + <actionGroup ref="AdminLoginActionGroup" stepKey="loginAsAdmin"/> <magentoCLI command="cache:flush" stepKey="flushCache"/> </before> <after> @@ -37,7 +37,7 @@ <createData entity="FlatRateShippingMethodConfig" stepKey="enableFlatRate"/> <createData entity="FreeShippinMethodDefault" stepKey="disableFreeShippingMethod"/> <createData entity="setFreeShippingSubtotalToDefault" stepKey="setFreeShippingSubtotalToDefault"/> - <actionGroup ref="logout" stepKey="logout"/> + <actionGroup ref="AdminLogoutActionGroup" stepKey="logout"/> <magentoCLI command="cache:flush" stepKey="flushCache2"/> </after> <!--Create new order with existing customer--> diff --git a/app/code/Magento/Sales/Test/Mftf/Test/AdminHoldCreatedOrderTest.xml b/app/code/Magento/Sales/Test/Mftf/Test/AdminHoldCreatedOrderTest.xml index 5dc5dbf3ab79d..8d328beab1adc 100644 --- a/app/code/Magento/Sales/Test/Mftf/Test/AdminHoldCreatedOrderTest.xml +++ b/app/code/Magento/Sales/Test/Mftf/Test/AdminHoldCreatedOrderTest.xml @@ -20,7 +20,7 @@ </annotations> <before> - <actionGroup ref="LoginAsAdmin" stepKey="loginAsAdmin"/> + <actionGroup ref="AdminLoginActionGroup" stepKey="loginAsAdmin"/> <!--Set default flat rate shipping method settings--> <createData entity="FlatRateShippingMethodDefault" stepKey="setDefaultFlatRateShippingMethod"/> @@ -40,7 +40,7 @@ <deleteData createDataKey="simpleCustomer" stepKey="deleteSimpleCustomer"/> <deleteData createDataKey="simpleProduct" stepKey="deleteSimpleProduct"/> <deleteData createDataKey="simpleProduct1" stepKey="deleteSimpleProduct1"/> - <actionGroup ref="logout" stepKey="logout"/> + <actionGroup ref="AdminLogoutActionGroup" stepKey="logout"/> </after> <!--Create new customer order--> diff --git a/app/code/Magento/Sales/Test/Mftf/Test/AdminMassOrdersCancelCompleteAndClosedTest.xml b/app/code/Magento/Sales/Test/Mftf/Test/AdminMassOrdersCancelCompleteAndClosedTest.xml index ec624a80f1613..a89e9f7ce6ebe 100644 --- a/app/code/Magento/Sales/Test/Mftf/Test/AdminMassOrdersCancelCompleteAndClosedTest.xml +++ b/app/code/Magento/Sales/Test/Mftf/Test/AdminMassOrdersCancelCompleteAndClosedTest.xml @@ -19,7 +19,7 @@ <group value="mtf_migrated"/> </annotations> <before> - <actionGroup ref="LoginAsAdmin" stepKey="LoginAsAdmin"/> + <actionGroup ref="AdminLoginActionGroup" stepKey="LoginAsAdmin"/> <!-- Create Data --> <createData entity="Simple_US_Customer" stepKey="createCustomer"/> @@ -33,7 +33,7 @@ <deleteData createDataKey="createCustomer" stepKey="deleteCustomer"/> <deleteData createDataKey="createCategory" stepKey="deleteCategory"/> <deleteData createDataKey="createProduct" stepKey="deleteProduct"/> - <actionGroup ref="logout" stepKey="logout"/> + <actionGroup ref="AdminLogoutActionGroup" stepKey="logout"/> </after> <!-- Create first order --> @@ -42,7 +42,9 @@ <argument name="customer" value="$$createCustomer$$"/> </actionGroup> <grabTextFrom selector="|Order # (\d+)|" stepKey="getFirstOrderId"/> - <assertNotEmpty actual="$getFirstOrderId" stepKey="assertOrderIdIsNotEmpty" after="getFirstOrderId"/> + <assertNotEmpty stepKey="assertOrderIdIsNotEmpty" after="getFirstOrderId"> + <actualResult type="const">$getFirstOrderId</actualResult> + </assertNotEmpty> <!-- Create Shipment for first Order --> <actionGroup ref="AdminCreateInvoiceAndShipmentActionGroup" stepKey="createShipmentForFirstOrder"/> @@ -53,7 +55,9 @@ <argument name="customer" value="$$createCustomer$$"/> </actionGroup> <grabTextFrom selector="|Order # (\d+)|" stepKey="getSecondOrderId"/> - <assertNotEmpty actual="$getSecondOrderId" stepKey="assertSecondOrderIdIsNotEmpty" after="getSecondOrderId"/> + <assertNotEmpty stepKey="assertSecondOrderIdIsNotEmpty" after="getSecondOrderId"> + <actualResult type="const">$getSecondOrderId</actualResult> + </assertNotEmpty> <!-- Create CreditMemo for second Order --> <actionGroup ref="AdminCreateInvoiceAndCreditMemoActionGroup" stepKey="createCreditMemo"/> diff --git a/app/code/Magento/Sales/Test/Mftf/Test/AdminMassOrdersCancelProcessingAndClosedTest.xml b/app/code/Magento/Sales/Test/Mftf/Test/AdminMassOrdersCancelProcessingAndClosedTest.xml index 5e524bcf6e05c..45cbe23042e03 100644 --- a/app/code/Magento/Sales/Test/Mftf/Test/AdminMassOrdersCancelProcessingAndClosedTest.xml +++ b/app/code/Magento/Sales/Test/Mftf/Test/AdminMassOrdersCancelProcessingAndClosedTest.xml @@ -19,7 +19,7 @@ <group value="mtf_migrated"/> </annotations> <before> - <actionGroup ref="LoginAsAdmin" stepKey="LoginAsAdmin"/> + <actionGroup ref="AdminLoginActionGroup" stepKey="LoginAsAdmin"/> <!-- Create Data --> <createData entity="Simple_US_Customer" stepKey="createCustomer"/> @@ -33,7 +33,7 @@ <deleteData createDataKey="createCustomer" stepKey="deleteCustomer"/> <deleteData createDataKey="createCategory" stepKey="deleteCategory"/> <deleteData createDataKey="createProduct" stepKey="deleteProduct"/> - <actionGroup ref="logout" stepKey="logout"/> + <actionGroup ref="AdminLogoutActionGroup" stepKey="logout"/> </after> <!-- Create first order --> @@ -42,7 +42,9 @@ <argument name="customer" value="$$createCustomer$$"/> </actionGroup> <grabTextFrom selector="|Order # (\d+)|" stepKey="getFirstOrderId"/> - <assertNotEmpty actual="$getFirstOrderId" stepKey="assertOrderIdIsNotEmpty" after="getFirstOrderId"/> + <assertNotEmpty stepKey="assertOrderIdIsNotEmpty" after="getFirstOrderId"> + <actualResult type="const">$getFirstOrderId</actualResult> + </assertNotEmpty> <!-- Create Invoice for first Order --> <actionGroup ref="AdminCreateInvoiceActionGroup" stepKey="createInvoice"/> @@ -53,7 +55,9 @@ <argument name="customer" value="$$createCustomer$$"/> </actionGroup> <grabTextFrom selector="|Order # (\d+)|" stepKey="getSecondOrderId"/> - <assertNotEmpty actual="$getSecondOrderId" stepKey="assertSecondOrderIdIsNotEmpty" after="getSecondOrderId"/> + <assertNotEmpty stepKey="assertSecondOrderIdIsNotEmpty" after="getSecondOrderId"> + <actualResult type="const">$getSecondOrderId</actualResult> + </assertNotEmpty> <!-- Create CreditMemo for second Order --> <actionGroup ref="AdminCreateInvoiceAndCreditMemoActionGroup" stepKey="createCreditMemo"/> diff --git a/app/code/Magento/Sales/Test/Mftf/Test/AdminMassOrdersHoldOnCompleteTest.xml b/app/code/Magento/Sales/Test/Mftf/Test/AdminMassOrdersHoldOnCompleteTest.xml index 179e1aa35a4e9..22b2d69a73090 100644 --- a/app/code/Magento/Sales/Test/Mftf/Test/AdminMassOrdersHoldOnCompleteTest.xml +++ b/app/code/Magento/Sales/Test/Mftf/Test/AdminMassOrdersHoldOnCompleteTest.xml @@ -19,7 +19,7 @@ <group value="mtf_migrated"/> </annotations> <before> - <actionGroup ref="LoginAsAdmin" stepKey="LoginAsAdmin"/> + <actionGroup ref="AdminLoginActionGroup" stepKey="LoginAsAdmin"/> <!-- Create Data --> <createData entity="Simple_US_Customer" stepKey="createCustomer"/> @@ -33,7 +33,7 @@ <deleteData createDataKey="createCustomer" stepKey="deleteCustomer"/> <deleteData createDataKey="createCategory" stepKey="deleteCategory"/> <deleteData createDataKey="createProduct" stepKey="deleteProduct"/> - <actionGroup ref="logout" stepKey="logout"/> + <actionGroup ref="AdminLogoutActionGroup" stepKey="logout"/> </after> <!-- Create order --> @@ -42,7 +42,9 @@ <argument name="customer" value="$$createCustomer$$"/> </actionGroup> <grabTextFrom selector="|Order # (\d+)|" stepKey="getOrderId"/> - <assertNotEmpty actual="$getOrderId" stepKey="assertOrderIdIsNotEmpty" after="getOrderId"/> + <assertNotEmpty stepKey="assertOrderIdIsNotEmpty" after="getOrderId"> + <actualResult type="const">$getOrderId</actualResult> + </assertNotEmpty> <!-- Create Shipment for Order --> <actionGroup ref="AdminCreateInvoiceAndShipmentActionGroup" stepKey="createShipment"/> diff --git a/app/code/Magento/Sales/Test/Mftf/Test/AdminMassOrdersHoldOnPendingAndProcessingTest.xml b/app/code/Magento/Sales/Test/Mftf/Test/AdminMassOrdersHoldOnPendingAndProcessingTest.xml index ec0ec8ca222da..4b690a00ee9ed 100644 --- a/app/code/Magento/Sales/Test/Mftf/Test/AdminMassOrdersHoldOnPendingAndProcessingTest.xml +++ b/app/code/Magento/Sales/Test/Mftf/Test/AdminMassOrdersHoldOnPendingAndProcessingTest.xml @@ -19,7 +19,7 @@ <group value="mtf_migrated"/> </annotations> <before> - <actionGroup ref="LoginAsAdmin" stepKey="LoginAsAdmin"/> + <actionGroup ref="AdminLoginActionGroup" stepKey="LoginAsAdmin"/> <!-- Create Data --> <createData entity="Simple_US_Customer" stepKey="createCustomer"/> @@ -33,7 +33,7 @@ <deleteData createDataKey="createCustomer" stepKey="deleteCustomer"/> <deleteData createDataKey="createCategory" stepKey="deleteCategory"/> <deleteData createDataKey="createProduct" stepKey="deleteProduct"/> - <actionGroup ref="logout" stepKey="logout"/> + <actionGroup ref="AdminLogoutActionGroup" stepKey="logout"/> </after> <!-- Create first order --> @@ -42,7 +42,9 @@ <argument name="customer" value="$$createCustomer$$"/> </actionGroup> <grabTextFrom selector="|Order # (\d+)|" stepKey="getFirstOrderId"/> - <assertNotEmpty actual="$getFirstOrderId" stepKey="assertOrderIdIsNotEmpty" after="getFirstOrderId"/> + <assertNotEmpty stepKey="assertOrderIdIsNotEmpty" after="getFirstOrderId"> + <actualResult type="const">$getFirstOrderId</actualResult> + </assertNotEmpty> <!-- Create second order --> <actionGroup ref="CreateOrderActionGroup" stepKey="createSecondOrder"> @@ -50,7 +52,9 @@ <argument name="customer" value="$$createCustomer$$"/> </actionGroup> <grabTextFrom selector="|Order # (\d+)|" stepKey="getSecondOrderId"/> - <assertNotEmpty actual="$getSecondOrderId" stepKey="assertSecondOrderIdIsNotEmpty" after="getSecondOrderId"/> + <assertNotEmpty stepKey="assertSecondOrderIdIsNotEmpty" after="getSecondOrderId"> + <actualResult type="const">$getSecondOrderId</actualResult> + </assertNotEmpty> <!-- Create Invoice for second Order --> <actionGroup ref="AdminCreateInvoiceActionGroup" stepKey="createInvoice"/> diff --git a/app/code/Magento/Sales/Test/Mftf/Test/AdminMassOrdersReleasePendingOrderTest.xml b/app/code/Magento/Sales/Test/Mftf/Test/AdminMassOrdersReleasePendingOrderTest.xml index 6c97c4add6313..e1d934f794142 100644 --- a/app/code/Magento/Sales/Test/Mftf/Test/AdminMassOrdersReleasePendingOrderTest.xml +++ b/app/code/Magento/Sales/Test/Mftf/Test/AdminMassOrdersReleasePendingOrderTest.xml @@ -19,7 +19,7 @@ <group value="mtf_migrated"/> </annotations> <before> - <actionGroup ref="LoginAsAdmin" stepKey="LoginAsAdmin"/> + <actionGroup ref="AdminLoginActionGroup" stepKey="LoginAsAdmin"/> <!-- Create Data --> <createData entity="Simple_US_Customer" stepKey="createCustomer"/> @@ -33,7 +33,7 @@ <deleteData createDataKey="createCustomer" stepKey="deleteCustomer"/> <deleteData createDataKey="createCategory" stepKey="deleteCategory"/> <deleteData createDataKey="createProduct" stepKey="deleteProduct"/> - <actionGroup ref="logout" stepKey="logout"/> + <actionGroup ref="AdminLogoutActionGroup" stepKey="logout"/> </after> <!-- Create order --> @@ -42,7 +42,9 @@ <argument name="customer" value="$$createCustomer$$"/> </actionGroup> <grabTextFrom selector="|Order # (\d+)|" stepKey="getOrderId"/> - <assertNotEmpty actual="$getOrderId" stepKey="assertOrderIdIsNotEmpty" after="getOrderId"/> + <assertNotEmpty stepKey="assertOrderIdIsNotEmpty" after="getOrderId"> + <actualResult type="const">$getOrderId</actualResult> + </assertNotEmpty> <!-- Navigate to backend: Go to Sales > Orders --> <amOnPage url="{{AdminOrdersPage.url}}" stepKey="onOrderPage"/> diff --git a/app/code/Magento/Sales/Test/Mftf/Test/AdminMassOrdersUpdateCancelPendingOrderTest.xml b/app/code/Magento/Sales/Test/Mftf/Test/AdminMassOrdersUpdateCancelPendingOrderTest.xml index d7c9664b8bce2..86a3e381cb237 100644 --- a/app/code/Magento/Sales/Test/Mftf/Test/AdminMassOrdersUpdateCancelPendingOrderTest.xml +++ b/app/code/Magento/Sales/Test/Mftf/Test/AdminMassOrdersUpdateCancelPendingOrderTest.xml @@ -19,7 +19,7 @@ <group value="mtf_migrated"/> </annotations> <before> - <actionGroup ref="LoginAsAdmin" stepKey="LoginAsAdmin"/> + <actionGroup ref="AdminLoginActionGroup" stepKey="LoginAsAdmin"/> <!-- Create Data --> <createData entity="Simple_US_Customer" stepKey="createCustomer"/> <createData entity="_defaultCategory" stepKey="createCategory"/> @@ -32,7 +32,7 @@ <deleteData createDataKey="createCustomer" stepKey="deleteCustomer"/> <deleteData createDataKey="createCategory" stepKey="deleteCategory"/> <deleteData createDataKey="createProduct" stepKey="deleteProduct"/> - <actionGroup ref="logout" stepKey="logout"/> + <actionGroup ref="AdminLogoutActionGroup" stepKey="logout"/> </after> <!-- Create order --> @@ -41,7 +41,9 @@ <argument name="customer" value="$$createCustomer$$"/> </actionGroup> <grabTextFrom selector="|Order # (\d+)|" stepKey="getOrderId"/> - <assertNotEmpty actual="$getOrderId" stepKey="assertOrderIdIsNotEmpty" after="getOrderId"/> + <assertNotEmpty stepKey="assertOrderIdIsNotEmpty" after="getOrderId"> + <actualResult type="const">$getOrderId</actualResult> + </assertNotEmpty> <!-- Navigate to backend: Go to Sales > Orders --> <amOnPage url="{{AdminOrdersPage.url}}" stepKey="onOrderPage"/> diff --git a/app/code/Magento/Sales/Test/Mftf/Test/AdminOrdersReleaseInUnholdStatusTest.xml b/app/code/Magento/Sales/Test/Mftf/Test/AdminOrdersReleaseInUnholdStatusTest.xml index 009f86256a910..bfd75a69b81d6 100644 --- a/app/code/Magento/Sales/Test/Mftf/Test/AdminOrdersReleaseInUnholdStatusTest.xml +++ b/app/code/Magento/Sales/Test/Mftf/Test/AdminOrdersReleaseInUnholdStatusTest.xml @@ -19,7 +19,7 @@ <group value="mtf_migrated"/> </annotations> <before> - <actionGroup ref="LoginAsAdmin" stepKey="LoginAsAdmin"/> + <actionGroup ref="AdminLoginActionGroup" stepKey="LoginAsAdmin"/> <!-- Create Data --> <createData entity="Simple_US_Customer" stepKey="createCustomer"/> @@ -33,7 +33,7 @@ <deleteData createDataKey="createCustomer" stepKey="deleteCustomer"/> <deleteData createDataKey="createCategory" stepKey="deleteCategory"/> <deleteData createDataKey="createProduct" stepKey="deleteProduct"/> - <actionGroup ref="logout" stepKey="logout"/> + <actionGroup ref="AdminLogoutActionGroup" stepKey="logout"/> </after> <!-- Create order --> @@ -42,7 +42,9 @@ <argument name="customer" value="$$createCustomer$$"/> </actionGroup> <grabTextFrom selector="|Order # (\d+)|" stepKey="getOrderId"/> - <assertNotEmpty actual="$getOrderId" stepKey="assertOrderIdIsNotEmpty" after="getOrderId"/> + <assertNotEmpty stepKey="assertOrderIdIsNotEmpty" after="getOrderId"> + <actualResult type="const">$getOrderId</actualResult> + </assertNotEmpty> <!-- Hold Order --> <click selector="{{AdminOrderDetailsMainActionsSection.hold}}" stepKey="pushButtonHold"/> diff --git a/app/code/Magento/Sales/Test/Mftf/Test/AdminProductInTheShoppingCartCouldBeReachedByAdminDuringOrderCreationWithMultiWebsiteConfigTest.xml b/app/code/Magento/Sales/Test/Mftf/Test/AdminProductInTheShoppingCartCouldBeReachedByAdminDuringOrderCreationWithMultiWebsiteConfigTest.xml index d30074a240546..338975d3c1f25 100644 --- a/app/code/Magento/Sales/Test/Mftf/Test/AdminProductInTheShoppingCartCouldBeReachedByAdminDuringOrderCreationWithMultiWebsiteConfigTest.xml +++ b/app/code/Magento/Sales/Test/Mftf/Test/AdminProductInTheShoppingCartCouldBeReachedByAdminDuringOrderCreationWithMultiWebsiteConfigTest.xml @@ -23,7 +23,7 @@ <before> <magentoCLI command="config:set {{StorefrontEnableAddStoreCodeToUrls.path}} {{StorefrontEnableAddStoreCodeToUrls.value}}" stepKey="addStoreCodeToUrlEnable"/> <createData entity="SimpleProduct2" stepKey="createProduct"/> - <actionGroup ref="LoginAsAdmin" stepKey="loginAsAdmin"/> + <actionGroup ref="AdminLoginActionGroup" stepKey="loginAsAdmin"/> <actionGroup ref="AdminCreateWebsiteActionGroup" stepKey="createWebsite"> <argument name="newWebsiteName" value="{{customWebsite.name}}"/> <argument name="websiteCode" value="{{customWebsite.code}}"/> @@ -37,7 +37,7 @@ <argument name="StoreGroup" value="customStoreGroup"/> <argument name="customStore" value="customStore"/> </actionGroup> - <actionGroup ref="GoToProductPageViaIDActionGroup" stepKey="goToProductEditPage"> + <actionGroup ref="AdminProductPageOpenByIdActionGroup" stepKey="goToProductEditPage"> <argument name="productId" value="$$createProduct.id$$"/> </actionGroup> <actionGroup ref="ProductSetWebsiteActionGroup" stepKey="assignProductToSecondWebsite"> @@ -53,7 +53,7 @@ <actionGroup ref="AdminDeleteWebsiteActionGroup" stepKey="deleteWebsite"> <argument name="websiteName" value="{{customWebsite.name}}"/> </actionGroup> - <actionGroup ref="logout" stepKey="logout"/> + <actionGroup ref="AdminLogoutActionGroup" stepKey="logout"/> </after> <!--Create customer account for Second Website--> diff --git a/app/code/Magento/Sales/Test/Mftf/Test/AdminReorderWithCatalogPriceRuleDiscountTest.xml b/app/code/Magento/Sales/Test/Mftf/Test/AdminReorderWithCatalogPriceRuleDiscountTest.xml new file mode 100644 index 0000000000000..0a4445f338960 --- /dev/null +++ b/app/code/Magento/Sales/Test/Mftf/Test/AdminReorderWithCatalogPriceRuleDiscountTest.xml @@ -0,0 +1,68 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + /** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +--> +<tests xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/testSchema.xsd"> + <test name="AdminReorderWithCatalogPriceRuleDiscountTest"> + <annotations> + <features value="Sales"/> + <stories value="Admin create order"/> + <title value="Reorder doesn't show discount price in Order Totals block"/> + <description value="Reorder doesn't show discount price in Order Totals block"/> + <severity value="CRITICAL"/> + <testCaseId value="MC-28642"/> + <useCaseId value="MAGETWO-99691"/> + <group value="sales"/> + <group value="catalogRule"/> + </annotations> + + <before> + <!--Create product--> + <createData entity="SimpleProduct2" stepKey="createSimpleProductApi"/> + <actionGroup ref="AdminLoginActionGroup" stepKey="loginAsAdmin"/> + <!--Create the catalog price rule --> + <createData entity="CatalogRuleToPercent" stepKey="createCatalogRule"/> + <!--Create order via API--> + <createData entity="GuestCart" stepKey="createGuestCart"/> + <createData entity="SimpleCartItem" stepKey="addCartItem"> + <requiredEntity createDataKey="createGuestCart"/> + <requiredEntity createDataKey="createSimpleProductApi"/> + </createData> + <createData entity="GuestAddressInformation" stepKey="addGuestOrderAddress"> + <requiredEntity createDataKey="createGuestCart"/> + </createData> + <updateData createDataKey="createGuestCart" entity="GuestOrderPaymentMethod" stepKey="sendGuestPaymentInformation"> + <requiredEntity createDataKey="createGuestCart"/> + </updateData> + <!--END Create order via API--> + </before> + + <after> + <deleteData createDataKey="createSimpleProductApi" stepKey="deleteSimpleProductApi"/> + <actionGroup ref="RemoveCatalogPriceRuleActionGroup" stepKey="deleteCatalogPriceRule"> + <argument name="ruleName" value="{{CatalogRuleToPercent.name}}"/> + </actionGroup> + <actionGroup ref="AdminLogoutActionGroup" stepKey="logoutFromAdmin"/> + </after> + + <!--Open order by Id--> + <amOnPage url="{{AdminOrderPage.url($createGuestCart.return$)}}" stepKey="navigateToOrderPage"/> + <!--Reorder--> + <click selector="{{AdminOrderDetailsMainActionsSection.reorder}}" stepKey="clickReorder"/> + <!--Verify order item row--> + <waitForElementVisible selector="{{AdminOrderItemsOrderedSection.productPrice('2')}}" stepKey="waitOrderItemPriceToBeVisible"/> + <see selector="{{AdminOrderItemsOrderedSection.productPrice('2')}}" userInput="{{AdminOrderSimpleProductWithCatalogRule.subtotal}}" stepKey="seeOrderItemPrice"/> + <!--Verify totals on Order page--> + <scrollTo selector="{{AdminOrderFormTotalSection.grandTotal}}" stepKey="scrollToOrderGrandTotal"/> + <waitForElementVisible selector="{{AdminOrderFormTotalSection.total('Subtotal')}}" stepKey="waitOrderSubtotalToBeVisible"/> + <see selector="{{AdminOrderFormTotalSection.total('Subtotal')}}" userInput="{{AdminOrderSimpleProductWithCatalogRule.subtotal}}" stepKey="seeOrderSubTotal"/> + <waitForElementVisible selector="{{AdminOrderFormTotalSection.total('Shipping')}}" stepKey="waitOrderShippingToBeVisible"/> + <see selector="{{AdminOrderFormTotalSection.total('Shipping')}}" userInput="{{AdminOrderSimpleProductWithCatalogRule.shipping}}" stepKey="seeOrderShipping"/> + <waitForElementVisible selector="{{AdminOrderFormTotalSection.grandTotal}}" stepKey="waitOrderGrandTotalToBeVisible"/> + <see selector="{{AdminOrderFormTotalSection.grandTotal}}" userInput="{{AdminOrderSimpleProductWithCatalogRule.grandTotal}}" stepKey="seeCorrectGrandTotal"/> + </test> +</tests> diff --git a/app/code/Magento/Sales/Test/Mftf/Test/AdminReorderWithCatalogPriceTest.xml b/app/code/Magento/Sales/Test/Mftf/Test/AdminReorderWithCatalogPriceTest.xml deleted file mode 100644 index d0502b41e2856..0000000000000 --- a/app/code/Magento/Sales/Test/Mftf/Test/AdminReorderWithCatalogPriceTest.xml +++ /dev/null @@ -1,73 +0,0 @@ -<?xml version="1.0" encoding="UTF-8"?> -<!-- - /** - * Copyright © Magento, Inc. All rights reserved. - * See COPYING.txt for license details. - */ ---> -<tests xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" - xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/testSchema.xsd"> - <test name="AdminReorderWithCatalogPriceTest"> - <annotations> - <features value="Sales"/> - <stories value="Admin create order"/> - <title value="Reorder doesn't show discount price in Order Totals block"/> - <description value="Reorder doesn't show discount price in Order Totals block"/> - <severity value="CRITICAL"/> - <testCaseId value="MC-16695"/> - <useCaseId value="MAGETWO-99691"/> - <group value="sales"/> - <group value="catalogRule"/> - </annotations> - <before> - <!--Create the catalog price rule --> - <createData entity="CatalogRuleToPercent" stepKey="createCatalogRule"/> - <!--Create product--> - <createData entity="SimpleProduct2" stepKey="createSimpleProductApi"/> - <!--Create order via API--> - <createData entity="GuestCart" stepKey="createGuestCart"/> - <createData entity="SimpleCartItem" stepKey="addCartItem"> - <requiredEntity createDataKey="createGuestCart"/> - <requiredEntity createDataKey="createSimpleProductApi"/> - </createData> - <createData entity="GuestAddressInformation" stepKey="addGuestOrderAddress"> - <requiredEntity createDataKey="createGuestCart"/> - </createData> - <updateData createDataKey="createGuestCart" entity="GuestOrderPaymentMethod" stepKey="sendGuestPaymentInformation"> - <requiredEntity createDataKey="createGuestCart"/> - </updateData> - <!--END Create order via API--> - </before> - <after> - <deleteData createDataKey="createSimpleProductApi" stepKey="deleteSimpleProductApi"/> - <!-- Delete the rule --> - <actionGroup ref="RemoveCatalogPriceRuleActionGroup" stepKey="deletePriceRule"> - <argument name="ruleName" value="{{CatalogRuleToPercent.name}}" /> - </actionGroup> - <!--Clear all filters in grid--> - <actionGroup ref="ClearFiltersAdminDataGridActionGroup" stepKey="resetCatalogRuleGridFilters"/> - <amOnPage url="{{AdminOrdersPage.url}}" stepKey="onOrdersPage"/> - <actionGroup ref="ClearFiltersAdminDataGridActionGroup" stepKey="clearGridFilter"/> - <actionGroup ref="logout" stepKey="logout"/> - </after> - <!-- Login as admin --> - <actionGroup ref="LoginAsAdmin" stepKey="loginAsAdmin"/> - <!--Open order by Id--> - <actionGroup ref="OpenOrderByIdActionGroup" stepKey="openOrderById"> - <argument name="orderId" value="$createGuestCart.return$"/> - </actionGroup> - <!--Reorder--> - <click selector="{{AdminOrderDetailsMainActionsSection.reorder}}" stepKey="clickReorder"/> - <!--Verify order item row--> - <waitForElementVisible selector="{{AdminOrderItemsOrderedSection.productPrice('2')}}" stepKey="waitOrderItemPriceToBeVisible"/> - <see selector="{{AdminOrderItemsOrderedSection.productPrice('2')}}" userInput="${{AdminOrderSimpleProductWithCatalogRule.subtotal}}" stepKey="seeOrderItemPrice"/> - <!--Verify totals on Order page--> - <scrollTo selector="{{AdminOrderFormTotalSection.grandTotal}}" stepKey="scrollToOrderGrandTotal"/> - <waitForElementVisible selector="{{AdminOrderFormTotalSection.total('Subtotal')}}" stepKey="waitOrderSubtotalToBeVisible"/> - <see selector="{{AdminOrderFormTotalSection.total('Subtotal')}}" userInput="${{AdminOrderSimpleProductWithCatalogRule.subtotal}}" stepKey="seeOrderSubTotal"/> - <waitForElementVisible selector="{{AdminOrderFormTotalSection.total('Shipping')}}" stepKey="waitOrderShippingToBeVisible"/> - <see selector="{{AdminOrderFormTotalSection.total('Shipping')}}" userInput="${{AdminOrderSimpleProductWithCatalogRule.shipping}}" stepKey="seeOrderShipping"/> - <waitForElementVisible selector="{{AdminOrderFormTotalSection.grandTotal}}" stepKey="waitOrderGrandTotalToBeVisible"/> - <see selector="{{AdminOrderFormTotalSection.grandTotal}}" userInput="${{AdminOrderSimpleProductWithCatalogRule.grandTotal}}" stepKey="seeCorrectGrandTotal"/> - </test> -</tests> diff --git a/app/code/Magento/Sales/Test/Mftf/Test/AdminSalesCreditMemosNavigateMenuTest.xml b/app/code/Magento/Sales/Test/Mftf/Test/AdminSalesCreditMemosNavigateMenuTest.xml index af7cc1822d215..39b5cabdadd72 100644 --- a/app/code/Magento/Sales/Test/Mftf/Test/AdminSalesCreditMemosNavigateMenuTest.xml +++ b/app/code/Magento/Sales/Test/Mftf/Test/AdminSalesCreditMemosNavigateMenuTest.xml @@ -20,10 +20,10 @@ <group value="mtf_migrated"/> </annotations> <before> - <actionGroup ref="LoginAsAdmin" stepKey="LoginAsAdmin"/> + <actionGroup ref="AdminLoginActionGroup" stepKey="LoginAsAdmin"/> </before> <after> - <actionGroup ref="logout" stepKey="logout"/> + <actionGroup ref="AdminLogoutActionGroup" stepKey="logout"/> </after> <actionGroup ref="AdminNavigateMenuActionGroup" stepKey="navigateToSalesCreditMemosPage"> <argument name="menuUiId" value="{{AdminMenuSales.dataUiId}}"/> diff --git a/app/code/Magento/Sales/Test/Mftf/Test/AdminSalesInvoicesNavigateMenuTest.xml b/app/code/Magento/Sales/Test/Mftf/Test/AdminSalesInvoicesNavigateMenuTest.xml index 5a38a66d1f4b2..567a0466d12e0 100644 --- a/app/code/Magento/Sales/Test/Mftf/Test/AdminSalesInvoicesNavigateMenuTest.xml +++ b/app/code/Magento/Sales/Test/Mftf/Test/AdminSalesInvoicesNavigateMenuTest.xml @@ -20,10 +20,10 @@ <group value="mtf_migrated"/> </annotations> <before> - <actionGroup ref="LoginAsAdmin" stepKey="LoginAsAdmin"/> + <actionGroup ref="AdminLoginActionGroup" stepKey="LoginAsAdmin"/> </before> <after> - <actionGroup ref="logout" stepKey="logout"/> + <actionGroup ref="AdminLogoutActionGroup" stepKey="logout"/> </after> <actionGroup ref="AdminNavigateMenuActionGroup" stepKey="navigateToSalesInvoicesPage"> <argument name="menuUiId" value="{{AdminMenuSales.dataUiId}}"/> diff --git a/app/code/Magento/Sales/Test/Mftf/Test/AdminSalesOrdersNavigateMenuTest.xml b/app/code/Magento/Sales/Test/Mftf/Test/AdminSalesOrdersNavigateMenuTest.xml index 8099254923a2c..4023be10f390e 100644 --- a/app/code/Magento/Sales/Test/Mftf/Test/AdminSalesOrdersNavigateMenuTest.xml +++ b/app/code/Magento/Sales/Test/Mftf/Test/AdminSalesOrdersNavigateMenuTest.xml @@ -20,10 +20,10 @@ <group value="mtf_migrated"/> </annotations> <before> - <actionGroup ref="LoginAsAdmin" stepKey="LoginAsAdmin"/> + <actionGroup ref="AdminLoginActionGroup" stepKey="LoginAsAdmin"/> </before> <after> - <actionGroup ref="logout" stepKey="logout"/> + <actionGroup ref="AdminLogoutActionGroup" stepKey="logout"/> </after> <actionGroup ref="AdminNavigateMenuActionGroup" stepKey="navigateToSalesOrderPage"> <argument name="menuUiId" value="{{AdminMenuSales.dataUiId}}"/> diff --git a/app/code/Magento/Sales/Test/Mftf/Test/AdminSalesShipmentsNavigateMenuTest.xml b/app/code/Magento/Sales/Test/Mftf/Test/AdminSalesShipmentsNavigateMenuTest.xml index 5717c6c90fc17..07d75664f1fda 100644 --- a/app/code/Magento/Sales/Test/Mftf/Test/AdminSalesShipmentsNavigateMenuTest.xml +++ b/app/code/Magento/Sales/Test/Mftf/Test/AdminSalesShipmentsNavigateMenuTest.xml @@ -20,10 +20,10 @@ <group value="mtf_migrated"/> </annotations> <before> - <actionGroup ref="LoginAsAdmin" stepKey="LoginAsAdmin"/> + <actionGroup ref="AdminLoginActionGroup" stepKey="LoginAsAdmin"/> </before> <after> - <actionGroup ref="logout" stepKey="logout"/> + <actionGroup ref="AdminLogoutActionGroup" stepKey="logout"/> </after> <actionGroup ref="AdminNavigateMenuActionGroup" stepKey="navigateToSalesShipmentsPage"> <argument name="menuUiId" value="{{AdminMenuSales.dataUiId}}"/> diff --git a/app/code/Magento/Sales/Test/Mftf/Test/AdminSalesTransactionsNavigateMenuTest.xml b/app/code/Magento/Sales/Test/Mftf/Test/AdminSalesTransactionsNavigateMenuTest.xml index 68933be92efe6..544bb5adf57cd 100644 --- a/app/code/Magento/Sales/Test/Mftf/Test/AdminSalesTransactionsNavigateMenuTest.xml +++ b/app/code/Magento/Sales/Test/Mftf/Test/AdminSalesTransactionsNavigateMenuTest.xml @@ -20,10 +20,10 @@ <group value="mtf_migrated"/> </annotations> <before> - <actionGroup ref="LoginAsAdmin" stepKey="LoginAsAdmin"/> + <actionGroup ref="AdminLoginActionGroup" stepKey="LoginAsAdmin"/> </before> <after> - <actionGroup ref="logout" stepKey="logout"/> + <actionGroup ref="AdminLogoutActionGroup" stepKey="logout"/> </after> <actionGroup ref="AdminNavigateMenuActionGroup" stepKey="navigateToSalesTransactionsPage"> <argument name="menuUiId" value="{{AdminMenuSales.dataUiId}}"/> diff --git a/app/code/Magento/Sales/Test/Mftf/Test/AdminSaveInAddressBookCheckboxStateTest.xml b/app/code/Magento/Sales/Test/Mftf/Test/AdminSaveInAddressBookCheckboxStateTest.xml index f2341278becba..ac0af3f5b80db 100644 --- a/app/code/Magento/Sales/Test/Mftf/Test/AdminSaveInAddressBookCheckboxStateTest.xml +++ b/app/code/Magento/Sales/Test/Mftf/Test/AdminSaveInAddressBookCheckboxStateTest.xml @@ -27,7 +27,7 @@ <createData entity="ApiSimpleProduct" stepKey="createSimpleProduct"> <requiredEntity createDataKey="createCategory"/> </createData> - <actionGroup ref="LoginActionGroup" stepKey="login"/> + <actionGroup ref="AdminLoginActionGroup" stepKey="login"/> </before> <after> <!-- Delete created data and log out --> @@ -35,7 +35,7 @@ <deleteData createDataKey="createCustomer" stepKey="deleteCustomer"/> <deleteData createDataKey="createSimpleProduct" stepKey="deleteSimpleProduct"/> <deleteData createDataKey="createCategory" stepKey="deleteCategory"/> - <actionGroup ref="logout" stepKey="logOut"/> + <actionGroup ref="AdminLogoutActionGroup" stepKey="logOut"/> </after> <!-- Create new order and choose an existing customer --> <comment userInput="Create new order and choose an existing customer" stepKey="createOrderAndAddCustomer"/> diff --git a/app/code/Magento/Sales/Test/Mftf/Test/AdminStoresOrderStatusNavigateMenuTest.xml b/app/code/Magento/Sales/Test/Mftf/Test/AdminStoresOrderStatusNavigateMenuTest.xml index d55cde1449033..d521c3dc647e2 100644 --- a/app/code/Magento/Sales/Test/Mftf/Test/AdminStoresOrderStatusNavigateMenuTest.xml +++ b/app/code/Magento/Sales/Test/Mftf/Test/AdminStoresOrderStatusNavigateMenuTest.xml @@ -20,10 +20,10 @@ <group value="mtf_migrated"/> </annotations> <before> - <actionGroup ref="LoginAsAdmin" stepKey="LoginAsAdmin"/> + <actionGroup ref="AdminLoginActionGroup" stepKey="LoginAsAdmin"/> </before> <after> - <actionGroup ref="logout" stepKey="logout"/> + <actionGroup ref="AdminLogoutActionGroup" stepKey="logout"/> </after> <actionGroup ref="AdminNavigateMenuActionGroup" stepKey="navigateToStoresOrderStatusPage"> <argument name="menuUiId" value="{{AdminMenuStores.dataUiId}}"/> diff --git a/app/code/Magento/Sales/Test/Mftf/Test/AdminSubmitConfigurableProductOrderTest.xml b/app/code/Magento/Sales/Test/Mftf/Test/AdminSubmitConfigurableProductOrderTest.xml index 65bba7512e228..54ae549967a3b 100644 --- a/app/code/Magento/Sales/Test/Mftf/Test/AdminSubmitConfigurableProductOrderTest.xml +++ b/app/code/Magento/Sales/Test/Mftf/Test/AdminSubmitConfigurableProductOrderTest.xml @@ -87,7 +87,7 @@ <requiredEntity createDataKey="createConfigChildProduct2"/> </createData> - <actionGroup ref="LoginAsAdmin" stepKey="loginAsAdmin"/> + <actionGroup ref="AdminLoginActionGroup" stepKey="loginAsAdmin"/> </before> <!--Create new customer order--> @@ -122,7 +122,7 @@ <actionGroup ref="VerifyCreatedOrderInformationActionGroup" stepKey="verifyCreatedOrderInformation"/> <after> - <actionGroup ref="logout" stepKey="logout"/> + <actionGroup ref="AdminLogoutActionGroup" stepKey="logout"/> <deleteData createDataKey="simpleCustomer" stepKey="deleteSimpleCustomer"/> @@ -131,6 +131,9 @@ <deleteData createDataKey="createConfigChildProduct2" stepKey="deleteConfigChildProduct2"/> <deleteData createDataKey="createConfigProductAttribute" stepKey="deleteConfigProductAttribute"/> <deleteData createDataKey="createCategory" stepKey="deleteApiCategory"/> + + <!-- Reindex invalidated indices after product attribute has been created/deleted --> + <magentoCron groups="index" stepKey="reindexInvalidatedIndices"/> </after> </test> </tests> diff --git a/app/code/Magento/Sales/Test/Mftf/Test/AdminSubmitsOrderPaymentMethodValidationTest.xml b/app/code/Magento/Sales/Test/Mftf/Test/AdminSubmitsOrderPaymentMethodValidationTest.xml index b2d424ca5d7d3..85665dfc1b00e 100644 --- a/app/code/Magento/Sales/Test/Mftf/Test/AdminSubmitsOrderPaymentMethodValidationTest.xml +++ b/app/code/Magento/Sales/Test/Mftf/Test/AdminSubmitsOrderPaymentMethodValidationTest.xml @@ -23,13 +23,13 @@ <createData entity="_defaultProduct" stepKey="createSimpleProduct"> <requiredEntity createDataKey="createCategory"/> </createData> - <actionGroup ref="LoginAsAdmin" stepKey="loginAsAdmin"/> + <actionGroup ref="AdminLoginActionGroup" stepKey="loginAsAdmin"/> </before> <after> <magentoCLI stepKey="allowSpecificValue" command="config:set payment/cashondelivery/active 0" /> <deleteData createDataKey="createSimpleProduct" stepKey="deleteProduct"/> <deleteData createDataKey="createCategory" stepKey="deleteCategory"/> - <actionGroup ref="logout" stepKey="adminLogout"/> + <actionGroup ref="AdminLogoutActionGroup" stepKey="adminLogout"/> </after> <!--Create order via Admin--> <comment userInput="Admin creates order" stepKey="adminCreateOrderComment"/> diff --git a/app/code/Magento/Sales/Test/Mftf/Test/AdminSubmitsOrderWithAndWithoutEmailTest.xml b/app/code/Magento/Sales/Test/Mftf/Test/AdminSubmitsOrderWithAndWithoutEmailTest.xml index f2d0fffe9b4cf..7615cc219d430 100644 --- a/app/code/Magento/Sales/Test/Mftf/Test/AdminSubmitsOrderWithAndWithoutEmailTest.xml +++ b/app/code/Magento/Sales/Test/Mftf/Test/AdminSubmitsOrderWithAndWithoutEmailTest.xml @@ -13,7 +13,7 @@ <stories value="Create orders"/> <title value="Email is required to create an order from Admin Panel"/> <description value="Admin should not be able to submit orders without an email address"/> - <severity value="MAJOR"/> + <severity value="BLOCKER"/> <testCaseId value="MAGETWO-92980"/> <group value="sales"/> </annotations> @@ -26,10 +26,10 @@ <after> <deleteData createDataKey="createSimpleProduct" stepKey="deleteProduct"/> <deleteData createDataKey="createCategory" stepKey="deleteCategory"/> - <actionGroup ref="logout" stepKey="adminLogout"/> + <actionGroup ref="AdminLogoutActionGroup" stepKey="adminLogout"/> </after> <!--Create order via Admin--> - <actionGroup ref="LoginAsAdmin" stepKey="loginAsAdmin"/> + <actionGroup ref="AdminLoginActionGroup" stepKey="loginAsAdmin"/> <comment userInput="Admin creates order" stepKey="adminCreateOrderComment"/> <!--<actionGroup ref="NavigateToNewOrderPageNewCustomerActionGroup" stepKey="navigateToNewOrderPage"/>--> <amOnPage url="{{AdminOrdersPage.url}}" stepKey="navigateToOrderIndexPage"/> diff --git a/app/code/Magento/Sales/Test/Mftf/Test/AdminSubmitsOrderWithAndWithoutFieldsValidationTest.xml b/app/code/Magento/Sales/Test/Mftf/Test/AdminSubmitsOrderWithAndWithoutFieldsValidationTest.xml index 4dbd80a351ee7..fd26ca1ca601e 100644 --- a/app/code/Magento/Sales/Test/Mftf/Test/AdminSubmitsOrderWithAndWithoutFieldsValidationTest.xml +++ b/app/code/Magento/Sales/Test/Mftf/Test/AdminSubmitsOrderWithAndWithoutFieldsValidationTest.xml @@ -25,10 +25,10 @@ <after> <deleteData createDataKey="createSimpleProduct" stepKey="deleteProduct"/> <deleteData createDataKey="createCategory" stepKey="deleteCategory"/> - <actionGroup ref="logout" stepKey="adminLogout"/> + <actionGroup ref="AdminLogoutActionGroup" stepKey="adminLogout"/> </after> <!--Create order via Admin--> - <actionGroup ref="LoginAsAdmin" stepKey="loginAsAdmin"/> + <actionGroup ref="AdminLoginActionGroup" stepKey="loginAsAdmin"/> <comment userInput="Admin creates order" stepKey="adminCreateOrderComment"/> <!--<actionGroup ref="NavigateToNewOrderPageNewCustomerActionGroup" stepKey="navigateToNewOrderPage"/>--> <amOnPage url="{{AdminOrdersPage.url}}" stepKey="navigateToOrderIndexPage"/> diff --git a/app/code/Magento/Sales/Test/Mftf/Test/AdminUnassignCustomOrderStatusTest.xml b/app/code/Magento/Sales/Test/Mftf/Test/AdminUnassignCustomOrderStatusTest.xml index 5f7a7c7606619..692f293ef3a75 100644 --- a/app/code/Magento/Sales/Test/Mftf/Test/AdminUnassignCustomOrderStatusTest.xml +++ b/app/code/Magento/Sales/Test/Mftf/Test/AdminUnassignCustomOrderStatusTest.xml @@ -18,10 +18,10 @@ <group value="mtf_migrated"/> </annotations> <before> - <actionGroup ref="LoginAsAdmin" stepKey="loginAsAdmin"/> + <actionGroup ref="AdminLoginActionGroup" stepKey="loginAsAdmin"/> </before> <after> - <actionGroup ref="logout" stepKey="logout"/> + <actionGroup ref="AdminLogoutActionGroup" stepKey="logout"/> </after> <!--Go to new order status page--> diff --git a/app/code/Magento/Sales/Test/Mftf/Test/AssignCustomOrderStatusNotVisibleOnStorefrontTest.xml b/app/code/Magento/Sales/Test/Mftf/Test/AssignCustomOrderStatusNotVisibleOnStorefrontTest.xml index ac377a0d31606..f0f4cf9d1a468 100644 --- a/app/code/Magento/Sales/Test/Mftf/Test/AssignCustomOrderStatusNotVisibleOnStorefrontTest.xml +++ b/app/code/Magento/Sales/Test/Mftf/Test/AssignCustomOrderStatusNotVisibleOnStorefrontTest.xml @@ -27,7 +27,7 @@ <createData entity="SimpleProduct2" stepKey="createSimpleProduct"/> <!-- Login as admin --> - <actionGroup ref="LoginAsAdmin" stepKey="loginAsAdmin"/> + <actionGroup ref="AdminLoginActionGroup" stepKey="loginAsAdmin"/> </before> <after> <!-- Disable created order status --> @@ -43,7 +43,7 @@ <deleteData createDataKey="createCustomer" stepKey="deleteCustomer"/> <!-- Log out --> - <actionGroup ref="logout" stepKey="logout"/> + <actionGroup ref="AdminLogoutActionGroup" stepKey="logout"/> </after> <!-- Create order status --> diff --git a/app/code/Magento/Sales/Test/Mftf/Test/CheckXSSVulnerabilityDuringOrderCreationTest.xml b/app/code/Magento/Sales/Test/Mftf/Test/CheckXSSVulnerabilityDuringOrderCreationTest.xml index 6eb9e37a3ab79..df6a797372e62 100644 --- a/app/code/Magento/Sales/Test/Mftf/Test/CheckXSSVulnerabilityDuringOrderCreationTest.xml +++ b/app/code/Magento/Sales/Test/Mftf/Test/CheckXSSVulnerabilityDuringOrderCreationTest.xml @@ -27,7 +27,7 @@ <deleteData createDataKey="createProduct" stepKey="deleteProduct"/> <!-- Log out --> - <actionGroup ref="logout" stepKey="logout"/> + <actionGroup ref="AdminLogoutActionGroup" stepKey="logout"/> </after> <!-- Add product to the shopping cart --> @@ -49,7 +49,7 @@ <actionGroup ref="AssertStorefrontEmailValidationMessageOnCheckoutActionGroup" stepKey="assertErrorMessageStorefront"/> <!-- Login as admin --> - <actionGroup ref="LoginAsAdmin" stepKey="loginAsAdmin"/> + <actionGroup ref="AdminLoginActionGroup" stepKey="loginAsAdmin"/> <!-- Try to create order in admin with provided email --> <actionGroup ref="NavigateToNewOrderPageNewCustomerSingleStoreActionGroup" stepKey="navigateToNewOrderPage"/> diff --git a/app/code/Magento/Sales/Test/Mftf/Test/CreateInvoiceAndCheckInvoiceOrderTest.xml b/app/code/Magento/Sales/Test/Mftf/Test/CreateInvoiceAndCheckInvoiceOrderTest.xml index 0b065e0da54f0..d8a3db76da05e 100644 --- a/app/code/Magento/Sales/Test/Mftf/Test/CreateInvoiceAndCheckInvoiceOrderTest.xml +++ b/app/code/Magento/Sales/Test/Mftf/Test/CreateInvoiceAndCheckInvoiceOrderTest.xml @@ -13,14 +13,14 @@ <stories value="Create Invoice for Offline Payment Methods"/> <title value="Create invoice and check invoice order test"/> <description value="Create invoice for offline payment methods and check invoice order on admin dashboard"/> - <severity value="CRITICAL"/> + <severity value="BLOCKER"/> <testCaseId value="MC-15868"/> <group value="sales"/> <group value="mtf_migrated"/> </annotations> <before> <!-- Login as admin --> - <actionGroup ref="LoginAsAdmin" stepKey="LoginAsAdmin"/> + <actionGroup ref="AdminLoginActionGroup" stepKey="LoginAsAdmin"/> <!-- Create customer --> <createData entity="Simple_US_Customer" stepKey="createCustomer"/> @@ -47,7 +47,7 @@ <deleteData createDataKey="createSimpleProduct" stepKey="deleteSimpleProduct"/> <!-- Log out --> - <actionGroup ref="logout" stepKey="logout"/> + <actionGroup ref="AdminLogoutActionGroup" stepKey="logout"/> </after> <!-- Create order --> @@ -143,6 +143,7 @@ <!-- Assert invoiced amount on frontend --> <click selector="{{StorefrontCustomerOrderSection.viewOrder}}" stepKey="clickViewOrder"/> + <dontSeeElement selector="{{StorefrontOrderDetailsSection.orderDateTagElement}}" stepKey="dontSeeDateTag"/> <click selector="{{StorefrontOrderInvoicesSection.invoiceTab}}" stepKey="clickInvoiceTabOnStorefront"/> <see selector="{{StorefrontOrderInvoicesSection.grandTotalPrice}}" userInput="$110.00" stepKey="seePrice"/> </test> diff --git a/app/code/Magento/Sales/Test/Mftf/Test/CreateInvoiceWithCashOnDeliveryPaymentMethodTest.xml b/app/code/Magento/Sales/Test/Mftf/Test/CreateInvoiceWithCashOnDeliveryPaymentMethodTest.xml index 295d388ced96e..c58b95a41b157 100644 --- a/app/code/Magento/Sales/Test/Mftf/Test/CreateInvoiceWithCashOnDeliveryPaymentMethodTest.xml +++ b/app/code/Magento/Sales/Test/Mftf/Test/CreateInvoiceWithCashOnDeliveryPaymentMethodTest.xml @@ -20,7 +20,7 @@ </annotations> <before> <!-- Login as admin --> - <actionGroup ref="LoginAsAdmin" stepKey="LoginAsAdmin"/> + <actionGroup ref="AdminLoginActionGroup" stepKey="LoginAsAdmin"/> <!-- Create customer --> <createData entity="Simple_US_Customer" stepKey="createCustomer"/> @@ -47,7 +47,7 @@ <deleteData createDataKey="createSimpleProduct" stepKey="deleteSimpleProduct"/> <!-- Log out --> - <actionGroup ref="logout" stepKey="logout"/> + <actionGroup ref="AdminLogoutActionGroup" stepKey="logout"/> </after> <!-- Create order --> diff --git a/app/code/Magento/Sales/Test/Mftf/Test/CreateInvoiceWithShipmentAndCheckInvoicedOrderTest.xml b/app/code/Magento/Sales/Test/Mftf/Test/CreateInvoiceWithShipmentAndCheckInvoicedOrderTest.xml index 2ccecf34a5a0c..1c92c2dae3712 100644 --- a/app/code/Magento/Sales/Test/Mftf/Test/CreateInvoiceWithShipmentAndCheckInvoicedOrderTest.xml +++ b/app/code/Magento/Sales/Test/Mftf/Test/CreateInvoiceWithShipmentAndCheckInvoicedOrderTest.xml @@ -20,7 +20,7 @@ </annotations> <before> <!-- Login as admin --> - <actionGroup ref="LoginAsAdmin" stepKey="LoginAsAdmin"/> + <actionGroup ref="AdminLoginActionGroup" stepKey="LoginAsAdmin"/> <!-- Create customer --> <createData entity="Simple_US_Customer" stepKey="createCustomer"/> @@ -45,7 +45,7 @@ <deleteData createDataKey="createSimpleProduct" stepKey="deleteSimpleProduct"/> <!-- Log out --> - <actionGroup ref="logout" stepKey="logout"/> + <actionGroup ref="AdminLogoutActionGroup" stepKey="logout"/> </after> <!-- Create order--> diff --git a/app/code/Magento/Sales/Test/Mftf/Test/CreateInvoiceWithZeroSubtotalCheckoutTest.xml b/app/code/Magento/Sales/Test/Mftf/Test/CreateInvoiceWithZeroSubtotalCheckoutTest.xml index 944a1ed75cebd..b562073a1276f 100644 --- a/app/code/Magento/Sales/Test/Mftf/Test/CreateInvoiceWithZeroSubtotalCheckoutTest.xml +++ b/app/code/Magento/Sales/Test/Mftf/Test/CreateInvoiceWithZeroSubtotalCheckoutTest.xml @@ -20,7 +20,7 @@ </annotations> <before> <!-- Login as admin --> - <actionGroup ref="LoginAsAdmin" stepKey="LoginAsAdmin"/> + <actionGroup ref="AdminLoginActionGroup" stepKey="LoginAsAdmin"/> <!-- Create customer --> <createData entity="Simple_US_Customer" stepKey="createCustomer"/> @@ -58,7 +58,7 @@ <deleteData createDataKey="createSimpleProduct" stepKey="deleteSimpleProduct"/> <!-- Log out --> - <actionGroup ref="logout" stepKey="logout"/> + <actionGroup ref="AdminLogoutActionGroup" stepKey="logout"/> </after> <!-- Create order --> diff --git a/app/code/Magento/Sales/Test/Mftf/Test/CreateOrderFromEditCustomerPageTest.xml b/app/code/Magento/Sales/Test/Mftf/Test/CreateOrderFromEditCustomerPageTest.xml index 0fcfa483f1adf..776d84ac230b8 100644 --- a/app/code/Magento/Sales/Test/Mftf/Test/CreateOrderFromEditCustomerPageTest.xml +++ b/app/code/Magento/Sales/Test/Mftf/Test/CreateOrderFromEditCustomerPageTest.xml @@ -14,13 +14,13 @@ <stories value="Create Order"/> <title value="Create order from edit customer page and add products to wish list and shopping cart"/> <description value="Create an order from edit customer page and add products to the wish list and shopping cart "/> - <severity value="CRITICAL"/> + <severity value="BLOCKER"/> <testCaseId value="MC-16161"/> <group value="mtf_migrated"/> </annotations> <before> - <actionGroup ref="LoginAsAdmin" stepKey="loginAsAdmin"/> + <actionGroup ref="AdminLoginActionGroup" stepKey="loginAsAdmin"/> <createData entity="FreeShippingMethodsSettingConfig" stepKey="freeShippingMethodsSettingConfig"/> <!--Create simple customer--> @@ -89,7 +89,10 @@ <deleteData createDataKey="createCategory" stepKey="deleteCategory"/> <amOnPage url="{{AdminCustomerPage.url}}" stepKey="openCustomerIndexPage"/> <actionGroup ref="ClearFiltersAdminDataGridActionGroup" stepKey="clearCustomerGridFilter"/> - <actionGroup ref="logout" stepKey="logout"/> + <actionGroup ref="AdminLogoutActionGroup" stepKey="logout"/> + + <!-- Reindex invalidated indices after product attribute has been created/deleted --> + <magentoCron groups="index" stepKey="reindexInvalidatedIndices"/> </after> <!--Filter and Open the customer edit page --> diff --git a/app/code/Magento/Sales/Test/Mftf/Test/CreditMemoTotalAfterShippingDiscountTest.xml b/app/code/Magento/Sales/Test/Mftf/Test/CreditMemoTotalAfterShippingDiscountTest.xml index 8ca8dbf21a3a2..36f8df7b271d3 100644 --- a/app/code/Magento/Sales/Test/Mftf/Test/CreditMemoTotalAfterShippingDiscountTest.xml +++ b/app/code/Magento/Sales/Test/Mftf/Test/CreditMemoTotalAfterShippingDiscountTest.xml @@ -23,7 +23,7 @@ <createData entity="_defaultProduct" stepKey="createProduct"> <requiredEntity createDataKey="createCategory"/> </createData> - <actionGroup ref="LoginActionGroup" stepKey="loginAsAdmin"/> + <actionGroup ref="AdminLoginActionGroup" stepKey="loginAsAdmin"/> <actionGroup ref="AdminOrdersGridClearFiltersActionGroup" stepKey="clearOrderFilters"/> <actionGroup ref="SetTaxClassForShippingActionGroup" stepKey="setShippingTaxClass"/> </before> @@ -33,7 +33,7 @@ <argument name="ruleName" value="{{ApiSalesRule.name}}"/> </actionGroup> <actionGroup ref="AdminOrdersGridClearFiltersActionGroup" stepKey="clearOrderFilters"/> - <actionGroup ref="logout" stepKey="adminLogout"/> + <actionGroup ref="AdminLogoutActionGroup" stepKey="adminLogout"/> <deleteData createDataKey="createCategory" stepKey="deleteProduct1"/> <deleteData createDataKey="createProduct" stepKey="deleteCategory1"/> </after> @@ -58,7 +58,7 @@ <click selector="{{AdminCartPriceRulesFormSection.save}}" stepKey="clickSaveButton"/> <see selector="{{AdminCartPriceRulesSection.messages}}" userInput="You saved the rule." stepKey="seeSuccessMessage"/> - <actionGroup ref="logout" stepKey="adminLogout"/> + <actionGroup ref="AdminLogoutActionGroup" stepKey="adminLogout"/> <!-- Place an order from Storefront as a Guest --> <amOnPage url="{{StorefrontCategoryPage.url($$createCategory.name$$)}}" stepKey="onCategoryPage"/> @@ -66,7 +66,7 @@ <moveMouseOver selector="{{StorefrontCategoryMainSection.ProductItemInfo}}" stepKey="hoverOverProduct"/> <click selector="{{StorefrontCategoryMainSection.AddToCartBtn}}" stepKey="addToCart"/> <waitForElementVisible selector="{{StorefrontCategoryMainSection.SuccessMsg}}" time="30" stepKey="waitForProductToAdd"/> - <click selector="{{StorefrontMinicartSection.showCart}}" stepKey="clickCart"/> + <actionGroup ref="StorefrontClickOnMiniCartActionGroup" stepKey="clickCart"/> <click selector="{{StorefrontMinicartSection.goToCheckout}}" stepKey="goToCheckout"/> <waitForPageLoad stepKey="waitForPageLoad2"/> <!-- fill out customer information --> @@ -92,7 +92,7 @@ <waitForElement selector="{{CheckoutPaymentSection.placeOrder}}" time="30" stepKey="waitForPlaceOrderButton"/> <click selector="{{CheckoutPaymentSection.placeOrder}}" stepKey="clickPlaceOrder"/> <grabTextFrom selector="{{CheckoutSuccessMainSection.orderNumber}}" stepKey="grabOrderNumber"/> - <actionGroup ref="LoginAsAdmin" stepKey="loginAsAdmin1"/> + <actionGroup ref="AdminLoginActionGroup" stepKey="loginAsAdmin1"/> <!-- Search for Order in the order grid --> <amOnPage url="{{AdminOrdersPage.url}}" stepKey="onOrdersPage"/> @@ -115,11 +115,17 @@ <see selector="{{AdminInvoiceTotalSection.total('Shipping')}}" userInput="${{AdminOrderSimpleProduct.shipping}}" stepKey="seeShippingAndHandling"/> <scrollTo selector="{{AdminInvoiceTotalSection.total('Shipping')}}" stepKey="scrollToInvoiceTotals"/> <grabTextFrom selector="{{AdminInvoiceTotalSection.total('Shipping')}}" stepKey="grabShippingCost"/> - <assertEquals expected='$5.00' expectedType="string" actual="($grabShippingCost)" message="ExpectedShipping" stepKey="assertShippingAndHandling"/> + <assertEquals message="ExpectedShipping" stepKey="assertShippingAndHandling"> + <actualResult type="const">($grabShippingCost)</actualResult> + <expectedResult type="string">$5.00</expectedResult> + </assertEquals> <see selector="{{AdminInvoiceTotalSection.total('Discount')}}" userInput="-$15.00" stepKey="seeShippingAndHandling2"/> <grabTextFrom selector="{{AdminInvoiceTotalSection.total('Discount')}}" stepKey="grabInvoiceDiscount"/> - <assertEquals expected='-$15.00' expectedType="string" actual="($grabInvoiceDiscount)" message="ExpectedDiscount" stepKey="assertDiscountValue"/> + <assertEquals message="ExpectedDiscount" stepKey="assertDiscountValue"> + <actualResult type="const">($grabInvoiceDiscount)</actualResult> + <expectedResult type="string">-$15.00</expectedResult> + </assertEquals> <see selector="{{AdminInvoiceTotalSection.grandTotal}}" userInput="$113.00" stepKey="seeCorrectGrandTotal"/> <grabTextFrom selector="{{AdminInvoiceTotalSection.grandTotal}}" stepKey="grabInvoiceGrandTotal" after="seeCorrectGrandTotal"/> @@ -137,9 +143,15 @@ <!-- Verify Refund Totals --> <see selector="{{AdminCreditMemoTotalSection.total('Subtotal')}}" userInput="${{AdminOrderSimpleProduct.subtotal}}" stepKey="seeRefundSubTotal"/> <grabTextFrom selector="{{AdminCreditMemoTotalSection.total('Discount')}}" stepKey="grabRefundDiscountValue"/> - <assertEquals expected='-$15.00' expectedType="string" actual="($grabRefundDiscountValue)" message="notExpectedDiscountOnRefundPage" stepKey="assertDiscountValue1"/> + <assertEquals message="notExpectedDiscountOnRefundPage" stepKey="assertDiscountValue1"> + <actualResult type="const">($grabRefundDiscountValue)</actualResult> + <expectedResult type="string">-$15.00</expectedResult> + </assertEquals> <grabTextFrom selector="{{AdminInvoiceTotalSection.grandTotal}}" stepKey="grabRefundGrandTotal"/> - <assertEquals expected="($grabInvoiceGrandTotal)" actual="($grabRefundGrandTotal)" message="RefundGrandTotalMatchesWithInvoiceGrandTotal" stepKey="compareRefundGrandTotalAndInvoiceGrandTotal"/> + <assertEquals message="RefundGrandTotalMatchesWithInvoiceGrandTotal" stepKey="compareRefundGrandTotalAndInvoiceGrandTotal"> + <actualResult type="const">($grabRefundGrandTotal)</actualResult> + <expectedResult type="const">($grabInvoiceGrandTotal)</expectedResult> + </assertEquals> </test> </tests> diff --git a/app/code/Magento/Sales/Test/Mftf/Test/EndToEndB2CAdminTest.xml b/app/code/Magento/Sales/Test/Mftf/Test/EndToEndB2CAdminTest.xml index 6a1cefae7553d..6ba1c3ac3deec 100644 --- a/app/code/Magento/Sales/Test/Mftf/Test/EndToEndB2CAdminTest.xml +++ b/app/code/Magento/Sales/Test/Mftf/Test/EndToEndB2CAdminTest.xml @@ -72,7 +72,9 @@ <see selector="{{AdminOrderDetailsMessagesSection.successMessage}}" userInput="You created the order." stepKey="seeSuccessMessage" after="seeViewOrderPage"/> <see selector="{{AdminOrderDetailsInformationSection.orderStatus}}" userInput="Pending" stepKey="seeOrderPendingStatus" after="seeSuccessMessage"/> <grabTextFrom selector="|Order # (\d+)|" stepKey="getOrderId" after="seeOrderPendingStatus"/> - <assertNotEmpty actual="$getOrderId" stepKey="assertOrderIdIsNotEmpty" after="getOrderId"/> + <assertNotEmpty stepKey="assertOrderIdIsNotEmpty" after="getOrderId"> + <actualResult type="const">$getOrderId</actualResult> + </assertNotEmpty> <actionGroup ref="VerifyBasicOrderInformationActionGroup" stepKey="verifyOrderInformation" after="assertOrderIdIsNotEmpty"> <argument name="customer" value="Simple_US_Customer"/> <argument name="shippingAddress" value="US_Address_TX"/> @@ -235,7 +237,10 @@ <click selector="{{AdminDataGridTableSection.columnHeader('Status')}}" stepKey="clickStatusToSortAsc" after="sortOrderGridByStatusComment"/> <grabTextFrom selector="{{AdminDataGridTableSection.gridCell('1', 'Status')}}" stepKey="getOrderStatusFirstRow" after="clickStatusToSortAsc"/> <grabTextFrom selector="{{AdminDataGridTableSection.gridCell('2', 'Status')}}" stepKey="getOrderStatusSecondRow" after="getOrderStatusFirstRow"/> - <assertGreaterThanOrEqual expected="$getOrderStatusFirstRow" actual="$getOrderStatusSecondRow" stepKey="checkStatusSortOrderAsc" after="getOrderStatusSecondRow"/> + <assertGreaterThanOrEqual stepKey="checkStatusSortOrderAsc" after="getOrderStatusSecondRow"> + <actualResult type="const">$getOrderStatusSecondRow</actualResult> + <expectedResult type="const">$getOrderStatusFirstRow</expectedResult> + </assertGreaterThanOrEqual> <!--@TODO improve sort assertion and check price and date column when MQE-690 is resolved--> <!--Use paging on order grid--> diff --git a/app/code/Magento/Sales/Test/Mftf/Test/MoveConfigurableProductsInComparedOnOrderPageTest.xml b/app/code/Magento/Sales/Test/Mftf/Test/MoveConfigurableProductsInComparedOnOrderPageTest.xml index 980d5cc82b93d..c3058ca6ede87 100644 --- a/app/code/Magento/Sales/Test/Mftf/Test/MoveConfigurableProductsInComparedOnOrderPageTest.xml +++ b/app/code/Magento/Sales/Test/Mftf/Test/MoveConfigurableProductsInComparedOnOrderPageTest.xml @@ -14,7 +14,7 @@ <stories value="Add Products to Order from Products in Comparison List Section"/> <title value="Move configurable products in compared on order page test"/> <description value="Move configurable products in compared on order page test"/> - <severity value="CRITICAL"/> + <severity value="BLOCKER"/> <testCaseId value="MC-16104"/> <group value="sales"/> <group value="mtf_migrated"/> @@ -86,7 +86,7 @@ </before> <after> <!-- Admin logout --> - <actionGroup ref="logout" stepKey="logout"/> + <actionGroup ref="AdminLogoutActionGroup" stepKey="logout"/> <!-- Logout customer --> <actionGroup ref="StorefrontCustomerLogoutActionGroup" stepKey="logoutCustomer"/> @@ -101,6 +101,9 @@ <!-- Delete category --> <deleteData createDataKey="createCategory" stepKey="deleteCategory"/> + + <!-- Reindex invalidated indices after product attribute has been created/deleted --> + <magentoCron groups="index" stepKey="reindexInvalidatedIndices"/> </after> <!-- Login as customer --> @@ -127,7 +130,7 @@ </actionGroup> <!-- Login as admin --> - <actionGroup ref="LoginAsAdmin" stepKey="loginAsAdmin"/> + <actionGroup ref="AdminLoginActionGroup" stepKey="loginAsAdmin"/> <!-- Open Customers -> All Customers --> <amOnPage url="{{AdminCustomerPage.url}}" stepKey="openCustomersGridPage"/> diff --git a/app/code/Magento/Sales/Test/Mftf/Test/MoveLastOrderedConfigurableProductOnOrderPageTest.xml b/app/code/Magento/Sales/Test/Mftf/Test/MoveLastOrderedConfigurableProductOnOrderPageTest.xml index b3c3f045f0d59..c635e6b0ad6b2 100644 --- a/app/code/Magento/Sales/Test/Mftf/Test/MoveLastOrderedConfigurableProductOnOrderPageTest.xml +++ b/app/code/Magento/Sales/Test/Mftf/Test/MoveLastOrderedConfigurableProductOnOrderPageTest.xml @@ -14,14 +14,14 @@ <stories value="Add Products to Order from Last Ordered Products Section"/> <title value="Move last ordered configurable product on order page test"/> <description value="Move last ordered configurable product on order page"/> - <severity value="CRITICAL"/> + <severity value="BLOCKER"/> <testCaseId value="MC-16155"/> <group value="sales"/> <group value="mtf_migrated"/> </annotations> <before> <!-- Login as admin --> - <actionGroup ref="LoginAsAdmin" stepKey="loginAsAdmin"/> + <actionGroup ref="AdminLoginActionGroup" stepKey="loginAsAdmin"/> <!-- Create customer --> <createData entity="Simple_US_CA_Customer" stepKey="createCustomer"/> @@ -60,12 +60,15 @@ </before> <after> <!-- Delete created data --> - <actionGroup ref="logout" stepKey="logout"/> + <actionGroup ref="AdminLogoutActionGroup" stepKey="logout"/> <deleteData createDataKey="createCustomer" stepKey="deleteCustomer"/> <deleteData createDataKey="createConfigChildProduct" stepKey="deleteConfigChildProduct"/> <deleteData createDataKey="createConfigProduct" stepKey="deleteConfigProduct"/> <deleteData createDataKey="createConfigProductAttribute" stepKey="deleteConfigProductAttribute"/> <deleteData createDataKey="createCategory" stepKey="deleteCategory"/> + + <!-- Reindex invalidated indices after product attribute has been created/deleted --> + <magentoCron groups="index" stepKey="reindexInvalidatedIndices"/> </after> <!-- Create order --> diff --git a/app/code/Magento/Sales/Test/Mftf/Test/MoveLastOrderedSimpleProductOnOrderPageTest.xml b/app/code/Magento/Sales/Test/Mftf/Test/MoveLastOrderedSimpleProductOnOrderPageTest.xml index f13a934276e35..eb28ebfd068da 100644 --- a/app/code/Magento/Sales/Test/Mftf/Test/MoveLastOrderedSimpleProductOnOrderPageTest.xml +++ b/app/code/Magento/Sales/Test/Mftf/Test/MoveLastOrderedSimpleProductOnOrderPageTest.xml @@ -14,14 +14,14 @@ <stories value="Add Products to Order from Last Ordered Products Section"/> <title value="Move last ordered simple product on order page test"/> <description value="Move last ordered simple product on order page"/> - <severity value="CRITICAL"/> + <severity value="BLOCKER"/> <testCaseId value="MC-16154"/> <group value="sales"/> <group value="mtf_migrated"/> </annotations> <before> <!-- Login as admin --> - <actionGroup ref="LoginAsAdmin" stepKey="loginAsAdmin"/> + <actionGroup ref="AdminLoginActionGroup" stepKey="loginAsAdmin"/> <!-- Create customer --> <createData entity="Simple_US_CA_Customer" stepKey="createCustomer"/> @@ -33,7 +33,7 @@ <!-- Delete created data --> <deleteData createDataKey="createProduct" stepKey="deleteProduct"/> <deleteData createDataKey="createCustomer" stepKey="deleteCustomer"/> - <actionGroup ref="logout" stepKey="logOut"/> + <actionGroup ref="AdminLogoutActionGroup" stepKey="logOut"/> </after> <!-- Create order --> diff --git a/app/code/Magento/Sales/Test/Mftf/Test/MoveRecentlyViewedBundleFixedProductOnOrderPageTest.xml b/app/code/Magento/Sales/Test/Mftf/Test/MoveRecentlyViewedBundleFixedProductOnOrderPageTest.xml index 0a39e8a0ac852..f374741c247d4 100644 --- a/app/code/Magento/Sales/Test/Mftf/Test/MoveRecentlyViewedBundleFixedProductOnOrderPageTest.xml +++ b/app/code/Magento/Sales/Test/Mftf/Test/MoveRecentlyViewedBundleFixedProductOnOrderPageTest.xml @@ -21,7 +21,7 @@ </annotations> <before> <!-- Login as admin --> - <actionGroup ref="LoginAsAdmin" stepKey="loginAsAdmin"/> + <actionGroup ref="AdminLoginActionGroup" stepKey="loginAsAdmin"/> <!-- Create customer --> <createData entity="Simple_US_CA_Customer" stepKey="createCustomer"/> @@ -61,7 +61,7 @@ </before> <after> <!-- Admin logout --> - <actionGroup ref="logout" stepKey="logout"/> + <actionGroup ref="AdminLogoutActionGroup" stepKey="logout"/> <!-- Customer logout --> <actionGroup ref="StorefrontCustomerLogoutActionGroup" stepKey="customerLogout"/> diff --git a/app/code/Magento/Sales/Test/Mftf/Test/MoveRecentlyViewedConfigurableProductOnOrderPageTest.xml b/app/code/Magento/Sales/Test/Mftf/Test/MoveRecentlyViewedConfigurableProductOnOrderPageTest.xml index aed7447050e5f..0e021600ab3e3 100644 --- a/app/code/Magento/Sales/Test/Mftf/Test/MoveRecentlyViewedConfigurableProductOnOrderPageTest.xml +++ b/app/code/Magento/Sales/Test/Mftf/Test/MoveRecentlyViewedConfigurableProductOnOrderPageTest.xml @@ -14,14 +14,14 @@ <stories value="Add Products to Order from Recently Viewed Products Section"/> <title value="Move recently viewed configurable product on order page test"/> <description value="Move recently viewed configurable product on order page"/> - <severity value="CRITICAL"/> + <severity value="BLOCKER"/> <testCaseId value="MC-16163"/> <group value="sales"/> <group value="mtf_migrated"/> </annotations> <before> <!-- Login as admin --> - <actionGroup ref="LoginAsAdmin" stepKey="loginAsAdmin"/> + <actionGroup ref="AdminLoginActionGroup" stepKey="loginAsAdmin"/> <!-- Create customer --> <createData entity="Simple_US_CA_Customer" stepKey="createCustomer"/> @@ -62,7 +62,7 @@ </before> <after> <!-- Admin logout --> - <actionGroup ref="logout" stepKey="logout"/> + <actionGroup ref="AdminLogoutActionGroup" stepKey="logout"/> <!-- Customer logout --> <actionGroup ref="StorefrontCustomerLogoutActionGroup" stepKey="customerLogout"/> @@ -80,6 +80,9 @@ <!-- Change configuration --> <magentoCLI command="config:set reports/options/enabled 0" stepKey="disableReportModule"/> + + <!-- Reindex invalidated indices after product attribute has been created/deleted --> + <magentoCron groups="index" stepKey="reindexInvalidatedIndices"/> </after> <!-- Login as customer --> diff --git a/app/code/Magento/Sales/Test/Mftf/Test/MoveSimpleProductsInComparedOnOrderPageTest.xml b/app/code/Magento/Sales/Test/Mftf/Test/MoveSimpleProductsInComparedOnOrderPageTest.xml index 3c01878b59e59..176fb05bc74b3 100644 --- a/app/code/Magento/Sales/Test/Mftf/Test/MoveSimpleProductsInComparedOnOrderPageTest.xml +++ b/app/code/Magento/Sales/Test/Mftf/Test/MoveSimpleProductsInComparedOnOrderPageTest.xml @@ -14,7 +14,7 @@ <stories value="Add Products to Order from Products in Comparison List Section"/> <title value="Move simple products in compared on order page test"/> <description value="Move simple products in compared on order page test"/> - <severity value="CRITICAL"/> + <severity value="BLOCKER"/> <testCaseId value="MC-16103"/> <group value="sales"/> <group value="mtf_migrated"/> @@ -33,7 +33,7 @@ </before> <after> <!-- Admin logout --> - <actionGroup ref="logout" stepKey="logout"/> + <actionGroup ref="AdminLogoutActionGroup" stepKey="logout"/> <!-- Logout customer --> <actionGroup ref="StorefrontCustomerLogoutActionGroup" stepKey="logoutCustomer"/> @@ -68,7 +68,7 @@ </actionGroup> <!-- Login as admin --> - <actionGroup ref="LoginAsAdmin" stepKey="loginAsAdmin"/> + <actionGroup ref="AdminLoginActionGroup" stepKey="loginAsAdmin"/> <!-- Open Customers -> All Customers --> <amOnPage url="{{AdminCustomerPage.url}}" stepKey="openCustomersGridPage"/> diff --git a/app/code/Magento/Sales/Test/Mftf/Test/StorefrontOrderPagerDisplayedTest.xml b/app/code/Magento/Sales/Test/Mftf/Test/StorefrontOrderPagerDisplayedTest.xml index 0e58bb84988a2..5cc4fae330d05 100644 --- a/app/code/Magento/Sales/Test/Mftf/Test/StorefrontOrderPagerDisplayedTest.xml +++ b/app/code/Magento/Sales/Test/Mftf/Test/StorefrontOrderPagerDisplayedTest.xml @@ -19,7 +19,7 @@ <group value="mtf_migrated"/> </annotations> <before> - <actionGroup ref="LoginAsAdmin" stepKey="LoginAsAdmin"/> + <actionGroup ref="AdminLoginActionGroup" stepKey="LoginAsAdmin"/> <!-- 21 products created and category --> <createData entity="_defaultCategory" stepKey="createCategory"/> <createData entity="SimpleProduct" stepKey="createProduct01"> @@ -89,6 +89,9 @@ <!-- Customer is created --> <createData entity="Simple_US_Customer" stepKey="createCustomer"/> + <!-- Reindex and flush the cache to display products on the category page --> + <magentoCLI command="indexer:reindex" stepKey="reindex"/> + <magentoCLI command="cache:flush" stepKey="flushCache"/> </before> <after> <!-- Delete category and products --> @@ -117,7 +120,7 @@ <!-- Delete Customer --> <deleteData createDataKey="createCustomer" stepKey="deleteCustomer"/> - <actionGroup ref="logout" stepKey="logout"/> + <actionGroup ref="AdminLogoutActionGroup" stepKey="logout"/> </after> <!-- Login to Storefront as Customer --> diff --git a/app/code/Magento/Sales/Test/Mftf/Test/StorefrontOrderPagerIsAbsentTest.xml b/app/code/Magento/Sales/Test/Mftf/Test/StorefrontOrderPagerIsAbsentTest.xml index 3ff8a7791d88b..20261de502ea3 100644 --- a/app/code/Magento/Sales/Test/Mftf/Test/StorefrontOrderPagerIsAbsentTest.xml +++ b/app/code/Magento/Sales/Test/Mftf/Test/StorefrontOrderPagerIsAbsentTest.xml @@ -19,7 +19,7 @@ <group value="mtf_migrated"/> </annotations> <before> - <actionGroup ref="LoginAsAdmin" stepKey="LoginAsAdmin"/> + <actionGroup ref="AdminLoginActionGroup" stepKey="LoginAsAdmin"/> <!-- 20 products created and category --> <createData entity="_defaultCategory" stepKey="createCategory"/> <createData entity="SimpleProduct" stepKey="createProduct01"> @@ -113,7 +113,7 @@ <!-- Delete Customer --> <deleteData createDataKey="createCustomer" stepKey="deleteCustomer"/> - <actionGroup ref="logout" stepKey="logout"/> + <actionGroup ref="AdminLogoutActionGroup" stepKey="logout"/> </after> <!-- Login to Storefront as Customer --> diff --git a/app/code/Magento/Sales/Test/Mftf/Test/StorefrontPrintOrderGuestTest.xml b/app/code/Magento/Sales/Test/Mftf/Test/StorefrontPrintOrderGuestTest.xml index 31f3449866984..00117c56de439 100644 --- a/app/code/Magento/Sales/Test/Mftf/Test/StorefrontPrintOrderGuestTest.xml +++ b/app/code/Magento/Sales/Test/Mftf/Test/StorefrontPrintOrderGuestTest.xml @@ -13,14 +13,14 @@ <stories value="Print Order"/> <title value="Print Order from Guest on Frontend"/> <description value="Print Order from Guest on Frontend"/> - <severity value="CRITICAL"/> + <severity value="BLOCKER"/> <testCaseId value="MC-16225"/> <group value="sales"/> <group value="mtf_migrated"/> </annotations> <before> <magentoCLI command="downloadable:domains:add" arguments="example.com static.magento.com" stepKey="addDownloadableDomain"/> - <actionGroup ref="LoginAsAdmin" stepKey="LoginAsAdmin"/> + <actionGroup ref="AdminLoginActionGroup" stepKey="LoginAsAdmin"/> <createData entity="ApiCategory" stepKey="createCategory"/> <!-- Create downloadable Product --> @@ -116,7 +116,9 @@ <argument name="product" value="$$createConfigProduct$$"/> </actionGroup> <grabTextFrom selector="{{AdminConfigurableProductFormSection.currentAttribute}}" stepKey="grabAttribute"/> - <assertNotEmpty actual="$grabAttribute" stepKey="assertNotEmpty"/> + <assertNotEmpty stepKey="assertNotEmpty"> + <actualResult type="const">$grabAttribute</actualResult> + </assertNotEmpty> <!-- Create bundle Product --> <createData entity="SimpleSubCategory" stepKey="createSubCategory"/> @@ -149,7 +151,9 @@ <argument name="product" value="$$createBundleProduct$$"/> </actionGroup> <grabTextFrom selector="{{AdminProductFormBundleSection.currentBundleOption}}" stepKey="grabBundleOption"/> - <assertNotEmpty actual="$grabBundleOption" stepKey="assertBundleOptionNotEmpty"/> + <assertNotEmpty stepKey="assertBundleOptionNotEmpty"> + <actualResult type="const">$grabBundleOption</actualResult> + </assertNotEmpty> <!-- Create sales rule --> <createData entity="ActiveSalesRuleCoupon50" stepKey="createCartPriceRule"/> @@ -225,13 +229,18 @@ <deleteData createDataKey="createCartPriceRule" stepKey="deleteCartPriceRule"/> <deleteData createDataKey="createCustomer" stepKey="deleteCustomer"/> - <actionGroup ref="logout" stepKey="logout"/> + <actionGroup ref="AdminLogoutActionGroup" stepKey="logout"/> + + <!-- Reindex invalidated indices after product attribute has been created/deleted --> + <magentoCron groups="index" stepKey="reindexInvalidatedIndices"/> </after> <actionGroup ref="AdminSubmitOrderActionGroup" stepKey="submitOrder"/> <grabTextFrom selector="|Order # (\d+)|" stepKey="getOrderId"/> - <assertNotEmpty actual="$getOrderId" stepKey="assertOrderIdIsNotEmpty" /> + <assertNotEmpty stepKey="assertOrderIdIsNotEmpty"> + <actualResult type="const">$getOrderId</actualResult> + </assertNotEmpty> <!-- Find the Order on frontend > Navigate to: Orders and Returns --> <amOnPage url="{{StorefrontGuestOrderSearchPage.url}}" stepKey="amOnOrdersAndReturns"/> diff --git a/app/code/Magento/Sales/Test/Mftf/Test/StorefrontRedirectToOrderHistory.xml b/app/code/Magento/Sales/Test/Mftf/Test/StorefrontRedirectToOrderHistory.xml deleted file mode 100644 index ad3a411d92414..0000000000000 --- a/app/code/Magento/Sales/Test/Mftf/Test/StorefrontRedirectToOrderHistory.xml +++ /dev/null @@ -1,79 +0,0 @@ -<?xml version="1.0" encoding="UTF-8"?> -<!-- - /** - * Copyright © Magento, Inc. All rights reserved. - * See COPYING.txt for license details. - */ ---> - -<tests xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" - xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/testSchema.xsd"> - <test name="StorefrontRedirectToOrderHistory"> - <annotations> - <features value="Redirection Rules"/> - <stories value="Create Invoice"/> - <title value="Create Invoice"/> - <description - value="Check while order printing URL with an id of not relevant order redirects to order history"/> - <severity value="MAJOR"/> - <testCaseId value="MAGETWO-92854"/> - <group value="sales"/> - </annotations> - <before> - <createData entity="_defaultCategory" stepKey="createCategory"/> - <createData entity="_defaultProduct" stepKey="createProduct"> - <requiredEntity createDataKey="createCategory"/> - </createData> - <createData entity="Simple_US_Customer" stepKey="createCustomer"/> - <createData entity="Simple_US_Customer" stepKey="createCustomer2"/> - </before> - <after> - <deleteData createDataKey="createCustomer" stepKey="deleteCustomer"/> - <deleteData createDataKey="createCustomer2" stepKey="deleteCustomer2"/> - <deleteData createDataKey="createCategory" stepKey="deleteCategory"/> - <deleteData createDataKey="createProduct" stepKey="deleteProduct"/> - </after> - - <!--Log in to Storefront as Customer 1 --> - <actionGroup ref="LoginToStorefrontActionGroup" stepKey="signUp"> - <argument name="Customer" value="$$createCustomer$$"/> - </actionGroup> - - <!--Create an order at Storefront as Customer 1 --> - <actionGroup ref="CreateOrderToPrintPageWithSelectedPaymentMethodActionGroup" stepKey="createOrderToPrint"> - <argument name="Category" value="$$createCategory$$"/> - </actionGroup> - - <!--Go to 'print order' page by grabbed order id--> - <grabFromCurrentUrl regex="~/order_id/(\d+)/~" stepKey="grabOrderIdFromURL"/> - <switchToNextTab stepKey="switchToPrintPage"/> - <waitForElement selector="{{SalesOrderPrintSection.isOrderPrintPage}}" stepKey="checkPrintPage"/> - <openNewTab stepKey="openNewTab"/> - <switchToNextTab stepKey="switchForward"/> - <amOnPage url="{{StorefrontSalesOrderPrintPage.url({$grabOrderIdFromURL})}}" stepKey="duplicatePrintPage"/> - - <!--Log out as customer 1--> - <switchToNextTab stepKey="switchForward2"/> - <openNewTab stepKey="openNewTab2"/> - <amOnPage url="{{StorefrontCustomerSignOutPage.url}}" stepKey="signOut"/> - <waitForLoadingMaskToDisappear stepKey="waitSignOutPage"/> - - <!--Log in to Storefront as Customer 2 --> - <actionGroup ref="LoginToStorefrontActionGroup" stepKey="signUp2"> - <argument name="Customer" value="$$createCustomer2$$"/> - </actionGroup> - - <!--Create an order at Storefront as Customer 2 --> - <actionGroup ref="CreateOrderToPrintPageWithSelectedPaymentMethodActionGroup" stepKey="createOrderToPrint2"> - <argument name="Category" value="$$createCategory$$"/> - </actionGroup> - - <!--Try to load 'print order' page with not relevant order id to be redirected to 'order history' page--> - <switchToNextTab stepKey="switchToPrintPage2"/> - <waitForElement selector="{{SalesOrderPrintSection.isOrderPrintPage}}" stepKey="checkPrintPage2"/> - <openNewTab stepKey="openNewTab3"/> - <switchToNextTab stepKey="switchForward4"/> - <amOnPage url="{{StorefrontSalesOrderPrintPage.url({$grabOrderIdFromURL})}}" stepKey="duplicatePrintPage2"/> - <seeElement selector="{{StorefrontCustomerOrderSection.isMyOrdersSection}}" stepKey="waitOrderHistoryPage"/> - </test> -</tests> diff --git a/app/code/Magento/Sales/Test/Mftf/Test/StorefrontRedirectToOrderHistoryTest.xml b/app/code/Magento/Sales/Test/Mftf/Test/StorefrontRedirectToOrderHistoryTest.xml new file mode 100644 index 0000000000000..ceb8c5f9b1aa2 --- /dev/null +++ b/app/code/Magento/Sales/Test/Mftf/Test/StorefrontRedirectToOrderHistoryTest.xml @@ -0,0 +1,79 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + /** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +--> + +<tests xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/testSchema.xsd"> + <test name="StorefrontRedirectToOrderHistoryTest"> + <annotations> + <features value="Redirection Rules"/> + <stories value="Create Invoice"/> + <title value="Create Invoice"/> + <description + value="Check while order printing URL with an id of not relevant order redirects to order history"/> + <severity value="MAJOR"/> + <testCaseId value="MAGETWO-92854"/> + <group value="sales"/> + </annotations> + <before> + <createData entity="_defaultCategory" stepKey="createCategory"/> + <createData entity="_defaultProduct" stepKey="createProduct"> + <requiredEntity createDataKey="createCategory"/> + </createData> + <createData entity="Simple_US_Customer" stepKey="createCustomer"/> + <createData entity="Simple_US_Customer" stepKey="createCustomer2"/> + </before> + <after> + <deleteData createDataKey="createCustomer" stepKey="deleteCustomer"/> + <deleteData createDataKey="createCustomer2" stepKey="deleteCustomer2"/> + <deleteData createDataKey="createCategory" stepKey="deleteCategory"/> + <deleteData createDataKey="createProduct" stepKey="deleteProduct"/> + </after> + + <!--Log in to Storefront as Customer 1 --> + <actionGroup ref="LoginToStorefrontActionGroup" stepKey="signUp"> + <argument name="Customer" value="$$createCustomer$$"/> + </actionGroup> + + <!--Create an order at Storefront as Customer 1 --> + <actionGroup ref="CreateOrderToPrintPageWithSelectedPaymentMethodActionGroup" stepKey="createOrderToPrint"> + <argument name="Category" value="$$createCategory$$"/> + </actionGroup> + + <!--Go to 'print order' page by grabbed order id--> + <grabFromCurrentUrl regex="~/order_id/(\d+)/~" stepKey="grabOrderIdFromURL"/> + <switchToNextTab stepKey="switchToPrintPage"/> + <waitForElement selector="{{SalesOrderPrintSection.isOrderPrintPage}}" stepKey="checkPrintPage"/> + <openNewTab stepKey="openNewTab"/> + <switchToNextTab stepKey="switchForward"/> + <amOnPage url="{{StorefrontSalesOrderPrintPage.url({$grabOrderIdFromURL})}}" stepKey="duplicatePrintPage"/> + + <!--Log out as customer 1--> + <switchToNextTab stepKey="switchForward2"/> + <openNewTab stepKey="openNewTab2"/> + <amOnPage url="{{StorefrontCustomerSignOutPage.url}}" stepKey="signOut"/> + <waitForLoadingMaskToDisappear stepKey="waitSignOutPage"/> + + <!--Log in to Storefront as Customer 2 --> + <actionGroup ref="LoginToStorefrontActionGroup" stepKey="signUp2"> + <argument name="Customer" value="$$createCustomer2$$"/> + </actionGroup> + + <!--Create an order at Storefront as Customer 2 --> + <actionGroup ref="CreateOrderToPrintPageWithSelectedPaymentMethodActionGroup" stepKey="createOrderToPrint2"> + <argument name="Category" value="$$createCategory$$"/> + </actionGroup> + + <!--Try to load 'print order' page with not relevant order id to be redirected to 'order history' page--> + <switchToNextTab stepKey="switchToPrintPage2"/> + <waitForElement selector="{{SalesOrderPrintSection.isOrderPrintPage}}" stepKey="checkPrintPage2"/> + <openNewTab stepKey="openNewTab3"/> + <switchToNextTab stepKey="switchForward4"/> + <amOnPage url="{{StorefrontSalesOrderPrintPage.url({$grabOrderIdFromURL})}}" stepKey="duplicatePrintPage2"/> + <seeElement selector="{{StorefrontCustomerOrderSection.isMyOrdersSection}}" stepKey="waitOrderHistoryPage"/> + </test> +</tests> diff --git a/app/code/Magento/Sales/Test/Mftf/Test/StorefrontVerifySecureURLRedirectSalesTest.xml b/app/code/Magento/Sales/Test/Mftf/Test/StorefrontVerifySecureURLRedirectSalesTest.xml index 505493e4e5682..d49ea4cfcbec7 100644 --- a/app/code/Magento/Sales/Test/Mftf/Test/StorefrontVerifySecureURLRedirectSalesTest.xml +++ b/app/code/Magento/Sales/Test/Mftf/Test/StorefrontVerifySecureURLRedirectSalesTest.xml @@ -8,7 +8,7 @@ <tests xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/testSchema.xsd"> - <test name="StorefrontVerifySecureURLRedirectSales"> + <test name="StorefrontVerifySecureURLRedirectSalesTest"> <annotations> <features value="Sales"/> <stories value="Storefront Secure URLs"/> diff --git a/app/code/Magento/Sales/Test/Unit/Block/Status/Grid/Column/StateTest.php b/app/code/Magento/Sales/Test/Unit/Block/Status/Grid/Column/StateTest.php index 86e0c561ca5ee..76ebeb2487ef3 100644 --- a/app/code/Magento/Sales/Test/Unit/Block/Status/Grid/Column/StateTest.php +++ b/app/code/Magento/Sales/Test/Unit/Block/Status/Grid/Column/StateTest.php @@ -55,6 +55,7 @@ public function testDecorateState() [ 'status' => 'fraud', 'state' => 'processing', + 'is_default' => '0', 'label' => 'Suspected Fraud', ] ), @@ -62,6 +63,7 @@ public function testDecorateState() [ 'status' => 'processing', 'state' => 'processing', + 'is_default' => '1', 'label' => 'Processing', ] ) @@ -78,6 +80,6 @@ public function testDecorateState() ->will($this->returnValue($statuses)); $result = $this->stateColumn->decorateState('processing', $rowMock, $columnMock, false); - $this->assertSame('processing[Suspected Fraud]', $result); + $this->assertSame('processing[Processing]', $result); } } diff --git a/app/code/Magento/Sales/Test/Unit/Controller/Adminhtml/Order/Create/ReorderTest.php b/app/code/Magento/Sales/Test/Unit/Controller/Adminhtml/Order/Create/ReorderTest.php index b11d73de736d4..ce777046b584d 100644 --- a/app/code/Magento/Sales/Test/Unit/Controller/Adminhtml/Order/Create/ReorderTest.php +++ b/app/code/Magento/Sales/Test/Unit/Controller/Adminhtml/Order/Create/ReorderTest.php @@ -3,6 +3,8 @@ * Copyright © Magento, Inc. All rights reserved. * See COPYING.txt for license details. */ +declare(strict_types=1); + namespace Magento\Sales\Test\Unit\Controller\Adminhtml\Order\Create; use Magento\Backend\App\Action\Context; @@ -14,19 +16,24 @@ use Magento\Framework\App\RequestInterface; use Magento\Framework\Message\ManagerInterface; use Magento\Framework\ObjectManagerInterface; +use Magento\Framework\TestFramework\Unit\Helper\ObjectManager; use Magento\Sales\Api\OrderRepositoryInterface; use Magento\Sales\Controller\Adminhtml\Order\Create\Reorder; use Magento\Sales\Model\AdminOrder\Create; use Magento\Sales\Model\Order; use Magento\Sales\Model\Order\Reorder\UnavailableProductsProvider; use Magento\Sales\Helper\Reorder as ReorderHelper; +use Magento\Framework\Exception\NoSuchEntityException; +use PHPUnit\Framework\TestCase; +use PHPUnit_Framework_MockObject_MockObject as MockObject; /** - * Class ReorderTest + * Verify reorder class. + * * @SuppressWarnings(PHPMD.CouplingBetweenObjects) * @SuppressWarnings(PHPMD.TooManyFields) */ -class ReorderTest extends \PHPUnit\Framework\TestCase +class ReorderTest extends TestCase { /** * @var Reorder @@ -39,67 +46,67 @@ class ReorderTest extends \PHPUnit\Framework\TestCase private $context; /** - * @var RequestInterface|\PHPUnit_Framework_MockObject_MockObject + * @var RequestInterface|MockObject */ private $requestMock; /** - * @var ObjectManagerInterface|\PHPUnit_Framework_MockObject_MockObject + * @var ObjectManagerInterface|MockObject */ private $objectManagerMock; /** - * @var Order|\PHPUnit_Framework_MockObject_MockObject + * @var Order|MockObject */ private $orderMock; /** - * @var ManagerInterface|\PHPUnit_Framework_MockObject_MockObject + * @var ManagerInterface|MockObject */ private $messageManagerMock; /** - * @var ForwardFactory|\PHPUnit_Framework_MockObject_MockObject + * @var ForwardFactory|MockObject */ private $resultForwardFactoryMock; /** - * @var RedirectFactory|\PHPUnit_Framework_MockObject_MockObject + * @var RedirectFactory|MockObject */ private $resultRedirectFactoryMock; /** - * @var Redirect|\PHPUnit_Framework_MockObject_MockObject + * @var Redirect|MockObject */ private $resultRedirectMock; /** - * @var Forward|\PHPUnit_Framework_MockObject_MockObject + * @var Forward|MockObject */ private $resultForwardMock; /** - * @var Quote|\PHPUnit_Framework_MockObject_MockObject + * @var Quote|MockObject */ private $quoteSessionMock; /** - * @var OrderRepositoryInterface|\PHPUnit_Framework_MockObject_MockObject + * @var OrderRepositoryInterface|MockObject */ private $orderRepositoryMock; /** - * @var ReorderHelper|\PHPUnit_Framework_MockObject_MockObject + * @var ReorderHelper|MockObject */ private $reorderHelperMock; /** - * @var UnavailableProductsProvider|\PHPUnit_Framework_MockObject_MockObject + * @var UnavailableProductsProvider|MockObject */ private $unavailableProductsProviderMock; /** - * @var Create|\PHPUnit_Framework_MockObject_MockObject + * @var Create|MockObject */ private $orderCreateMock; @@ -109,39 +116,33 @@ class ReorderTest extends \PHPUnit\Framework\TestCase private $orderId; /** - * @return void + * @inheritDoc */ protected function setUp() { $this->orderId = 111; - $this->orderRepositoryMock = $this->getMockBuilder(OrderRepositoryInterface::class)->getMockForAbstractClass(); + $this->orderRepositoryMock = $this->createMock(OrderRepositoryInterface::class); + $this->requestMock = $this->createMock(RequestInterface::class); + $this->objectManagerMock = $this->createMock(ObjectManagerInterface::class); + $this->resultForwardFactoryMock = $this->createMock(ForwardFactory::class); + $this->resultRedirectFactoryMock = $this->createMock(RedirectFactory::class); + $this->resultRedirectMock = $this->createMock(Redirect::class); + $this->resultForwardMock = $this->createMock(Forward::class); + $this->reorderHelperMock = $this->createMock(ReorderHelper::class); + $this->unavailableProductsProviderMock = $this->createMock(UnavailableProductsProvider::class); + $this->orderCreateMock = $this->createMock(Create::class); $this->orderMock = $this->getMockBuilder(Order::class) ->disableOriginalConstructor() ->setMethods(['getEntityId', 'getId', 'setReordered']) ->getMock(); - $this->requestMock = $this->getMockBuilder(RequestInterface::class)->getMockForAbstractClass(); - $this->objectManagerMock = $this->getMockBuilder(ObjectManagerInterface::class)->getMockForAbstractClass(); - $this->resultForwardFactoryMock = $this->getMockBuilder(ForwardFactory::class) - ->disableOriginalConstructor() - ->getMock(); - $this->resultRedirectFactoryMock = $this->getMockBuilder(RedirectFactory::class) - ->disableOriginalConstructor() - ->getMock(); - $this->resultRedirectMock = $this->getMockBuilder(Redirect::class)->disableOriginalConstructor()->getMock(); - $this->resultForwardMock = $this->getMockBuilder(Forward::class)->disableOriginalConstructor()->getMock(); $this->quoteSessionMock = $this->getMockBuilder(Quote::class) ->disableOriginalConstructor() ->setMethods(['clearStorage', 'setUseOldShippingMethod']) ->getMock(); - $this->reorderHelperMock = $this->getMockBuilder(ReorderHelper::class) - ->disableOriginalConstructor() - ->getMock(); - $this->messageManagerMock = $this->getMockBuilder(ManagerInterface::class)->getMockForAbstractClass(); - $this->unavailableProductsProviderMock = $this->getMockBuilder(UnavailableProductsProvider::class) - ->disableOriginalConstructor() - ->getMock(); - $this->orderCreateMock = $this->getMockBuilder(Create::class)->disableOriginalConstructor()->getMock(); - $objectManager = new \Magento\Framework\TestFramework\Unit\Helper\ObjectManager($this); + $this->messageManagerMock = $this->getMockBuilder(ManagerInterface::class) + ->getMockForAbstractClass(); + + $objectManager = new ObjectManager($this); $this->context = $objectManager->getObject( Context::class, [ @@ -165,9 +166,11 @@ protected function setUp() } /** + * Verify execute with no route. + * * @return void */ - public function testExecuteForward() + public function testExecuteForward(): void { $this->clearStorage(); $this->getOrder(); @@ -178,9 +181,11 @@ public function testExecuteForward() } /** + * Verify execute redirect order grid + * * @return void */ - public function testExecuteRedirectOrderGrid() + public function testExecuteRedirectOrderGrid(): void { $this->clearStorage(); $this->getOrder(); @@ -193,9 +198,11 @@ public function testExecuteRedirectOrderGrid() } /** + * Verify execute redirect back. + * * @return void */ - public function testExecuteRedirectBack() + public function testExecuteRedirectBack(): void { $this->clearStorage(); $this->getOrder(); @@ -210,9 +217,11 @@ public function testExecuteRedirectBack() } /** + * Verify execute redirect new order. + * * @return void */ - public function testExecuteRedirectNewOrder() + public function testExecuteRedirectNewOrder(): void { $this->clearStorage(); $this->getOrder(); @@ -227,9 +236,75 @@ public function testExecuteRedirectNewOrder() } /** + * Verify redirect new order with throws exception. + * + * @return void + */ + public function testExecuteRedirectNewOrderWithThrowsException(): void + { + $exception = new NoSuchEntityException(); + + $this->clearStorage(); + $this->getOrder(); + $this->canReorder(true); + $this->createRedirect(); + $this->getOrderId($this->orderId); + $this->getUnavailableProducts([]); + + $this->orderMock->expects($this->once()) + ->method('setReordered') + ->with(true) + ->willThrowException($exception); + $this->messageManagerMock + ->expects($this->once()) + ->method('addErrorMessage') + ->willReturnSelf(); + $this->resultRedirectMock + ->expects($this->once()) + ->method('setPath') + ->with('sales/*') + ->willReturnSelf(); + $this->assertInstanceOf(Redirect::class, $this->reorder->execute()); + } + + /** + * Verify redirect new order with exception. + * * @return void */ - private function clearStorage() + public function testExecuteRedirectNewOrderWithException(): void + { + $exception = new \Exception(); + + $this->clearStorage(); + $this->getOrder(); + $this->canReorder(true); + $this->createRedirect(); + $this->getOrderId($this->orderId); + $this->getUnavailableProducts([]); + $this->orderMock->expects($this->once()) + ->method('setReordered') + ->with(true) + ->willThrowException(new $exception); + $this->messageManagerMock + ->expects($this->once()) + ->method('addException') + ->with($exception, __('Error while processing order.')) + ->willReturnSelf(); + $this->resultRedirectMock + ->expects($this->once()) + ->method('setPath') + ->with('sales/*') + ->willReturnSelf(); + $this->assertInstanceOf(Redirect::class, $this->reorder->execute()); + } + + /** + * Mock clear storage. + * + * @return void + */ + private function clearStorage(): void { $this->objectManagerMock->expects($this->at(0)) ->method('get') @@ -239,9 +314,11 @@ private function clearStorage() } /** + * Mock get order. + * * @return void */ - private function getOrder() + private function getOrder(): void { $this->requestMock->expects($this->once()) ->method('getParam') @@ -254,9 +331,12 @@ private function getOrder() } /** + * Mock and return 'canReorder' method. + * * @param bool $result + * @return void */ - private function canReorder($result) + private function canReorder(bool $result): void { $entityId = 1; $this->orderMock->expects($this->once())->method('getEntityId')->willReturn($entityId); @@ -267,18 +347,22 @@ private function canReorder($result) } /** + * Mock result forward. + * * @return void */ - private function prepareForward() + private function prepareForward(): void { $this->resultForwardFactoryMock->expects($this->once())->method('create')->willReturn($this->resultForwardMock); $this->resultForwardMock->expects($this->once())->method('forward')->with('noroute')->willReturnSelf(); } /** + * Mock create. + * * @return void */ - private function createRedirect() + private function createRedirect(): void { $this->resultRedirectFactoryMock->expects($this->once()) ->method('create') @@ -286,26 +370,35 @@ private function createRedirect() } /** + * Mock order 'getId' method. + * * @param null|int $orderId + * @return void */ - private function getOrderId($orderId) + private function getOrderId($orderId): void { $this->orderMock->expects($this->once())->method('getId')->willReturn($orderId); } /** + * Mock result redirect 'setPath' method. + * * @param string $path * @param null|array $params + * @return void */ - private function setPath($path, $params = []) + private function setPath(string $path, $params = []): void { $this->resultRedirectMock->expects($this->once())->method('setPath')->with($path, $params); } /** + * Mock unavailable products provider. + * * @param array $unavailableProducts + * @return void */ - private function getUnavailableProducts(array $unavailableProducts) + private function getUnavailableProducts(array $unavailableProducts): void { $this->unavailableProductsProviderMock->expects($this->any()) ->method('getForOrder') @@ -314,9 +407,11 @@ private function getUnavailableProducts(array $unavailableProducts) } /** + * Mock init form order. + * * @return void */ - private function initFromOrder() + private function initFromOrder(): void { $this->orderMock->expects($this->once())->method('setReordered')->with(true)->willReturnSelf(); $this->objectManagerMock->expects($this->at(1)) diff --git a/app/code/Magento/Sales/Test/Unit/Model/Order/ConfigTest.php b/app/code/Magento/Sales/Test/Unit/Model/Order/ConfigTest.php index feee2816b2cd4..0bd20150cd450 100644 --- a/app/code/Magento/Sales/Test/Unit/Model/Order/ConfigTest.php +++ b/app/code/Magento/Sales/Test/Unit/Model/Order/ConfigTest.php @@ -9,7 +9,7 @@ use Magento\Sales\Model\ResourceModel\Order\Status\Collection; /** - * Class ConfigTest + * Test for Magento\Sales\Model\Order\Config class */ class ConfigTest extends \PHPUnit\Framework\TestCase { diff --git a/app/code/Magento/Sales/Test/Unit/Model/Order/Creditmemo/Total/DiscountTest.php b/app/code/Magento/Sales/Test/Unit/Model/Order/Creditmemo/Total/DiscountTest.php index 8a45aa8c7958e..07826ff1d0cbd 100644 --- a/app/code/Magento/Sales/Test/Unit/Model/Order/Creditmemo/Total/DiscountTest.php +++ b/app/code/Magento/Sales/Test/Unit/Model/Order/Creditmemo/Total/DiscountTest.php @@ -6,9 +6,6 @@ namespace Magento\Sales\Test\Unit\Model\Order\Creditmemo\Total; -/** - * Class DiscountTest - */ class DiscountTest extends \PHPUnit\Framework\TestCase { /** @@ -36,6 +33,11 @@ class DiscountTest extends \PHPUnit\Framework\TestCase */ protected $orderItemMock; + /** + * @var \Magento\Tax\Model\Config|\PHPUnit\Framework\MockObject\MockObject + */ + private $taxConfig; + protected function setUp() { $this->orderMock = $this->createPartialMock( @@ -54,7 +56,9 @@ protected function setUp() 'getHasChildren', 'getBaseCost', 'getQty', 'getOrderItem', 'setDiscountAmount', 'setBaseDiscountAmount', 'isLast' ]); - $this->total = new \Magento\Sales\Model\Order\Creditmemo\Total\Discount(); + $this->taxConfig = $this->createMock(\Magento\Tax\Model\Config::class); + + $this->total = new \Magento\Sales\Model\Order\Creditmemo\Total\Discount($this->taxConfig); } public function testCollect() @@ -74,7 +78,7 @@ public function testCollect() $this->orderMock->expects($this->once()) ->method('getBaseShippingDiscountAmount') ->willReturn(1); - $this->orderMock->expects($this->exactly(3)) + $this->orderMock->expects($this->exactly(2)) ->method('getBaseShippingAmount') ->willReturn(1); $this->orderMock->expects($this->once()) @@ -150,7 +154,7 @@ public function testCollectNoBaseShippingAmount() $this->orderMock->expects($this->once()) ->method('getBaseShippingDiscountAmount') ->willReturn(1); - $this->orderMock->expects($this->exactly(3)) + $this->orderMock->expects($this->exactly(2)) ->method('getBaseShippingAmount') ->willReturn(1); $this->orderMock->expects($this->once()) diff --git a/app/code/Magento/Sales/Test/Unit/Model/Order/Email/Sender/AbstractSenderTest.php b/app/code/Magento/Sales/Test/Unit/Model/Order/Email/Sender/AbstractSenderTest.php index 2f4e0e927db2c..39c85b955d9b5 100644 --- a/app/code/Magento/Sales/Test/Unit/Model/Order/Email/Sender/AbstractSenderTest.php +++ b/app/code/Magento/Sales/Test/Unit/Model/Order/Email/Sender/AbstractSenderTest.php @@ -5,6 +5,22 @@ */ namespace Magento\Sales\Test\Unit\Model\Order\Email\Sender; +use Magento\Framework\App\Config; +use Magento\Framework\App\Config\ScopeConfigInterface; +use Magento\Framework\Event\Manager; +use Magento\Payment\Helper\Data; +use Magento\Payment\Model\Info; +use Magento\Sales\Model\Order; +use Magento\Sales\Model\Order\Address; +use Magento\Sales\Model\Order\Address\Renderer; +use Magento\Sales\Model\Order\Email\Container\Template; +use Magento\Sales\Model\Order\Email\Sender; +use Magento\Sales\Model\Order\Email\SenderBuilderFactory; +use Magento\Store\Model\Store; +use PHPUnit\Framework\MockObject\Matcher\InvokedCount; +use PHPUnit\Framework\MockObject\MockObject; +use Psr\Log\LoggerInterface; + /** * Class AbstractSenderTest * @@ -13,89 +29,89 @@ abstract class AbstractSenderTest extends \PHPUnit\Framework\TestCase { /** - * @var \Magento\Sales\Model\Order\Email\Sender|\PHPUnit_Framework_MockObject_MockObject + * @var Sender|MockObject */ protected $senderMock; /** - * @var \PHPUnit_Framework_MockObject_MockObject + * @var MockObject */ protected $senderBuilderFactoryMock; /** - * @var \PHPUnit_Framework_MockObject_MockObject + * @var MockObject */ protected $templateContainerMock; /** - * @var \PHPUnit_Framework_MockObject_MockObject + * @var MockObject */ protected $identityContainerMock; /** - * @var \PHPUnit_Framework_MockObject_MockObject + * @var MockObject */ protected $storeMock; /** - * @var \PHPUnit_Framework_MockObject_MockObject + * @var MockObject */ protected $orderMock; /** - * @var \PHPUnit_Framework_MockObject_MockObject + * @var MockObject */ protected $paymentHelper; /** - * @var \Magento\Sales\Model\Order\Address\Renderer|\PHPUnit_Framework_MockObject_MockObject + * @var Renderer|MockObject */ protected $addressRenderer; /** * Global configuration storage mock. * - * @var \Magento\Framework\App\Config\ScopeConfigInterface|\PHPUnit_Framework_MockObject_MockObject + * @var ScopeConfigInterface|MockObject */ protected $globalConfig; /** - * @var \Magento\Sales\Model\Order\Address|\PHPUnit_Framework_MockObject_MockObject + * @var Address|MockObject */ protected $addressMock; /** - * @var \Magento\Framework\Event\Manager | \PHPUnit_Framework_MockObject_MockObject + * @var Manager|MockObject */ protected $eventManagerMock; /** - * @var \PHPUnit_Framework_MockObject_MockObject + * @var MockObject */ protected $loggerMock; public function stepMockSetup() { $this->senderMock = $this->createPartialMock( - \Magento\Sales\Model\Order\Email\Sender::class, + Sender::class, ['send', 'sendCopyTo'] ); $this->senderBuilderFactoryMock = $this->createPartialMock( - \Magento\Sales\Model\Order\Email\SenderBuilderFactory::class, + SenderBuilderFactory::class, ['create'] ); $this->templateContainerMock = $this->createPartialMock( - \Magento\Sales\Model\Order\Email\Container\Template::class, + Template::class, ['setTemplateVars'] ); - $this->storeMock = $this->createPartialMock(\Magento\Store\Model\Store::class, ['getStoreId', '__wakeup']); + $this->storeMock = $this->createPartialMock(Store::class, ['getStoreId', '__wakeup']); $this->orderMock = $this->createPartialMock( - \Magento\Sales\Model\Order::class, + Order::class, [ - 'getStore', 'getBillingAddress', 'getPayment', + 'getId', 'getStore', 'getBillingAddress', 'getPayment', '__wakeup', 'getCustomerIsGuest', 'getCustomerName', 'getCustomerEmail', 'getShippingAddress', 'setSendEmail', 'setEmailSent', 'getCreatedAtFormatted', 'getIsNotVirtual', @@ -105,23 +121,23 @@ public function stepMockSetup() $this->orderMock->expects($this->any()) ->method('getStore') ->will($this->returnValue($this->storeMock)); - $paymentInfoMock = $this->createMock(\Magento\Payment\Model\Info::class); + $paymentInfoMock = $this->createMock(Info::class); $this->orderMock->expects($this->any()) ->method('getPayment') ->will($this->returnValue($paymentInfoMock)); - $this->addressRenderer = $this->createMock(\Magento\Sales\Model\Order\Address\Renderer::class); - $this->addressMock = $this->createMock(\Magento\Sales\Model\Order\Address::class); - $this->eventManagerMock = $this->createMock(\Magento\Framework\Event\Manager::class); + $this->addressRenderer = $this->createMock(Renderer::class); + $this->addressMock = $this->createMock(Address::class); + $this->eventManagerMock = $this->createMock(Manager::class); - $this->paymentHelper = $this->createPartialMock(\Magento\Payment\Helper\Data::class, ['getInfoBlockHtml']); + $this->paymentHelper = $this->createPartialMock(Data::class, ['getInfoBlockHtml']); $this->paymentHelper->expects($this->any()) ->method('getInfoBlockHtml') ->will($this->returnValue('payment')); - $this->globalConfig = $this->createPartialMock(\Magento\Framework\App\Config::class, ['getValue']); + $this->globalConfig = $this->createPartialMock(Config::class, ['getValue']); - $this->loggerMock = $this->createMock(\Psr\Log\LoggerInterface::class); + $this->loggerMock = $this->createMock(LoggerInterface::class); } /** @@ -168,14 +184,14 @@ public function stepIdentityContainerInit($identityMockClassName) } /** - * @param \PHPUnit_Framework_MockObject_Matcher_InvokedCount $sendExpects - * @param \PHPUnit_Framework_MockObject_Matcher_InvokedCount $sendCopyToExpects + * @param InvokedCount $sendExpects + * @param InvokedCount $sendCopyToExpects */ protected function stepSend( - \PHPUnit\Framework\MockObject\Matcher\InvokedCount $sendExpects, - \PHPUnit\Framework\MockObject\Matcher\InvokedCount $sendCopyToExpects + InvokedCount $sendExpects, + InvokedCount $sendCopyToExpects ) { - $senderMock = $this->createPartialMock(\Magento\Sales\Model\Order\Email\Sender::class, ['send', 'sendCopyTo']); + $senderMock = $this->createPartialMock(Sender::class, ['send', 'sendCopyTo']); $senderMock->expects($sendExpects) ->method('send'); $senderMock->expects($sendCopyToExpects) diff --git a/app/code/Magento/Sales/Test/Unit/Model/Order/Email/Sender/CreditmemoSenderTest.php b/app/code/Magento/Sales/Test/Unit/Model/Order/Email/Sender/CreditmemoSenderTest.php index 72a51a15db592..6bcabea7df202 100644 --- a/app/code/Magento/Sales/Test/Unit/Model/Order/Email/Sender/CreditmemoSenderTest.php +++ b/app/code/Magento/Sales/Test/Unit/Model/Order/Email/Sender/CreditmemoSenderTest.php @@ -5,25 +5,36 @@ */ namespace Magento\Sales\Test\Unit\Model\Order\Email\Sender; +use Magento\Sales\Api\Data\OrderInterface; +use Magento\Sales\Model\Order\Address; +use Magento\Sales\Model\Order\Creditmemo; +use Magento\Sales\Model\ResourceModel\Order\Creditmemo as CreditmemoResource; +use Magento\Sales\Model\Order\Email\Container\CreditmemoIdentity; use Magento\Sales\Model\Order\Email\Sender\CreditmemoSender; +use Magento\Sales\Model\ResourceModel\EntityAbstract; +use PHPUnit\Framework\MockObject\MockObject; /** * Test for Magento\Sales\Model\Order\Email\Sender\CreditmemoSender class. */ class CreditmemoSenderTest extends AbstractSenderTest { + private const CREDITMEMO_ID = 1; + + private const ORDER_ID = 1; + /** - * @var \Magento\Sales\Model\Order\Email\Sender\CreditmemoSender + * @var CreditmemoSender */ protected $sender; /** - * @var \Magento\Sales\Model\Order\Creditmemo|\PHPUnit_Framework_MockObject_MockObject + * @var Creditmemo|MockObject */ protected $creditmemoMock; /** - * @var \Magento\Sales\Model\ResourceModel\EntityAbstract|\PHPUnit_Framework_MockObject_MockObject + * @var EntityAbstract|MockObject */ protected $creditmemoResourceMock; @@ -32,14 +43,15 @@ protected function setUp() $this->stepMockSetup(); $this->creditmemoResourceMock = $this->createPartialMock( - \Magento\Sales\Model\ResourceModel\Order\Creditmemo::class, + CreditmemoResource::class, ['saveAttribute'] ); $this->creditmemoMock = $this->createPartialMock( - \Magento\Sales\Model\Order\Creditmemo::class, + Creditmemo::class, [ 'getStore', + 'getId', '__wakeup', 'getOrder', 'setSendEmail', @@ -54,9 +66,13 @@ protected function setUp() $this->creditmemoMock->expects($this->any()) ->method('getOrder') ->will($this->returnValue($this->orderMock)); + $this->creditmemoMock->method('getId') + ->willReturn(self::CREDITMEMO_ID); + $this->orderMock->method('getId') + ->willReturn(self::ORDER_ID); $this->identityContainerMock = $this->createPartialMock( - \Magento\Sales\Model\Order\Email\Container\CreditmemoIdentity::class, + CreditmemoIdentity::class, ['getStore', 'isEnabled', 'getConfigValue', 'getTemplateId', 'getGuestTemplateId', 'getCopyMethod'] ); $this->identityContainerMock->expects($this->any()) @@ -104,7 +120,7 @@ public function testSend($configValue, $forceSyncMode, $customerNoteNotify, $ema ->willReturn($configValue); if (!$configValue || $forceSyncMode) { - $addressMock = $this->createMock(\Magento\Sales\Model\Order\Address::class); + $addressMock = $this->createMock(Address::class); $this->addressRenderer->expects($this->exactly(2)) ->method('format') @@ -142,7 +158,9 @@ public function testSend($configValue, $forceSyncMode, $customerNoteNotify, $ema ->with( [ 'order' => $this->orderMock, + 'order_id' => self::ORDER_ID, 'creditmemo' => $this->creditmemoMock, + 'creditmemo_id' => self::CREDITMEMO_ID, 'comment' => $customerNoteNotify ? $comment : '', 'billing' => $addressMock, 'payment_html' => 'payment', @@ -240,7 +258,7 @@ public function testSendVirtualOrder($isVirtualOrder, $formatCallCount, $expecte $frontendStatusLabel = 'Complete'; $isNotVirtual = false; - $this->orderMock->setData(\Magento\Sales\Api\Data\OrderInterface::IS_VIRTUAL, $isVirtualOrder); + $this->orderMock->setData(OrderInterface::IS_VIRTUAL, $isVirtualOrder); $this->orderMock->expects($this->any()) ->method('getCustomerName') @@ -267,7 +285,7 @@ public function testSendVirtualOrder($isVirtualOrder, $formatCallCount, $expecte ->with('sales_email/general/async_sending') ->willReturn(false); - $addressMock = $this->createMock(\Magento\Sales\Model\Order\Address::class); + $addressMock = $this->createMock(Address::class); $this->addressRenderer->expects($this->exactly($formatCallCount)) ->method('format') @@ -285,7 +303,9 @@ public function testSendVirtualOrder($isVirtualOrder, $formatCallCount, $expecte ->with( [ 'order' => $this->orderMock, + 'order_id' => self::ORDER_ID, 'creditmemo' => $this->creditmemoMock, + 'creditmemo_id' => self::CREDITMEMO_ID, 'comment' => '', 'billing' => $addressMock, 'payment_html' => 'payment', diff --git a/app/code/Magento/Sales/Test/Unit/Model/Order/Email/Sender/InvoiceSenderTest.php b/app/code/Magento/Sales/Test/Unit/Model/Order/Email/Sender/InvoiceSenderTest.php index 00a1855055a84..206db7b9c7e0b 100644 --- a/app/code/Magento/Sales/Test/Unit/Model/Order/Email/Sender/InvoiceSenderTest.php +++ b/app/code/Magento/Sales/Test/Unit/Model/Order/Email/Sender/InvoiceSenderTest.php @@ -5,25 +5,36 @@ */ namespace Magento\Sales\Test\Unit\Model\Order\Email\Sender; +use Magento\Sales\Api\Data\OrderInterface; +use Magento\Sales\Model\Order\Address; +use Magento\Sales\Model\Order\Email\Container\InvoiceIdentity; use Magento\Sales\Model\Order\Email\Sender\InvoiceSender; +use Magento\Sales\Model\Order\Invoice; +use Magento\Sales\Model\ResourceModel\Order\Invoice as InvoiceResource; +use Magento\Sales\Model\ResourceModel\EntityAbstract; +use PHPUnit\Framework\MockObject\MockObject; /** * Test for Magento\Sales\Model\Order\Email\Sender\InvoiceSender class. */ class InvoiceSenderTest extends AbstractSenderTest { + private const INVOICE_ID = 1; + + private const ORDER_ID = 1; + /** - * @var \Magento\Sales\Model\Order\Email\Sender\InvoiceSender + * @var InvoiceSender */ protected $sender; /** - * @var \Magento\Sales\Model\Order\Invoice|\PHPUnit_Framework_MockObject_MockObject + * @var Invoice|MockObject */ protected $invoiceMock; /** - * @var \Magento\Sales\Model\ResourceModel\EntityAbstract|\PHPUnit_Framework_MockObject_MockObject + * @var EntityAbstract|MockObject */ protected $invoiceResourceMock; @@ -32,14 +43,15 @@ protected function setUp() $this->stepMockSetup(); $this->invoiceResourceMock = $this->createPartialMock( - \Magento\Sales\Model\ResourceModel\Order\Invoice::class, + InvoiceResource::class, ['saveAttribute'] ); $this->invoiceMock = $this->createPartialMock( - \Magento\Sales\Model\Order\Invoice::class, + Invoice::class, [ 'getStore', + 'getId', '__wakeup', 'getOrder', 'setSendEmail', @@ -55,8 +67,13 @@ protected function setUp() ->method('getOrder') ->will($this->returnValue($this->orderMock)); + $this->invoiceMock->method('getId') + ->willReturn(self::INVOICE_ID); + $this->orderMock->method('getId') + ->willReturn(self::ORDER_ID); + $this->identityContainerMock = $this->createPartialMock( - \Magento\Sales\Model\Order\Email\Container\InvoiceIdentity::class, + InvoiceIdentity::class, ['getStore', 'isEnabled', 'getConfigValue', 'getTemplateId', 'getGuestTemplateId', 'getCopyMethod'] ); $this->identityContainerMock->expects($this->any()) @@ -104,7 +121,7 @@ public function testSend($configValue, $forceSyncMode, $customerNoteNotify, $ema ->willReturn($configValue); if (!$configValue || $forceSyncMode) { - $addressMock = $this->createMock(\Magento\Sales\Model\Order\Address::class); + $addressMock = $this->createMock(Address::class); $this->addressRenderer->expects($this->any()) ->method('format') @@ -148,7 +165,9 @@ public function testSend($configValue, $forceSyncMode, $customerNoteNotify, $ema ->with( [ 'order' => $this->orderMock, + 'order_id' => self::ORDER_ID, 'invoice' => $this->invoiceMock, + 'invoice_id' => self::INVOICE_ID, 'comment' => $customerNoteNotify ? $comment : '', 'billing' => $addressMock, 'payment_html' => 'payment', @@ -240,7 +259,7 @@ public function sendDataProvider() public function testSendVirtualOrder($isVirtualOrder, $formatCallCount, $expectedShippingAddress) { $billingAddress = 'address_test'; - $this->orderMock->setData(\Magento\Sales\Api\Data\OrderInterface::IS_VIRTUAL, $isVirtualOrder); + $this->orderMock->setData(OrderInterface::IS_VIRTUAL, $isVirtualOrder); $customerName = 'Test Customer'; $frontendStatusLabel = 'Complete'; $isNotVirtual = false; @@ -254,7 +273,7 @@ public function testSendVirtualOrder($isVirtualOrder, $formatCallCount, $expecte ->with('sales_email/general/async_sending') ->willReturn(false); - $addressMock = $this->createMock(\Magento\Sales\Model\Order\Address::class); + $addressMock = $this->createMock(Address::class); $this->addressRenderer->expects($this->exactly($formatCallCount)) ->method('format') @@ -287,7 +306,9 @@ public function testSendVirtualOrder($isVirtualOrder, $formatCallCount, $expecte ->with( [ 'order' => $this->orderMock, + 'order_id' => self::ORDER_ID, 'invoice' => $this->invoiceMock, + 'invoice_id' => self::INVOICE_ID, 'comment' => '', 'billing' => $addressMock, 'payment_html' => 'payment', diff --git a/app/code/Magento/Sales/Test/Unit/Model/Order/Email/Sender/OrderSenderTest.php b/app/code/Magento/Sales/Test/Unit/Model/Order/Email/Sender/OrderSenderTest.php index a033e41dd8e8b..7d5cb7028ddc3 100644 --- a/app/code/Magento/Sales/Test/Unit/Model/Order/Email/Sender/OrderSenderTest.php +++ b/app/code/Magento/Sales/Test/Unit/Model/Order/Email/Sender/OrderSenderTest.php @@ -5,17 +5,25 @@ */ namespace Magento\Sales\Test\Unit\Model\Order\Email\Sender; +use Magento\Sales\Api\Data\OrderInterface; +use Magento\Sales\Model\Order\Address; +use Magento\Sales\Model\Order\Email\Container\OrderIdentity; use Magento\Sales\Model\Order\Email\Sender\OrderSender; +use Magento\Sales\Model\ResourceModel\EntityAbstract; +use Magento\Sales\Model\ResourceModel\Order; +use PHPUnit\Framework\MockObject\MockObject; class OrderSenderTest extends AbstractSenderTest { + private const ORDER_ID = 1; + /** - * @var \Magento\Sales\Model\Order\Email\Sender\OrderSender + * @var OrderSender */ protected $sender; /** - * @var \Magento\Sales\Model\ResourceModel\EntityAbstract|\PHPUnit_Framework_MockObject_MockObject + * @var EntityAbstract|MockObject */ protected $orderResourceMock; @@ -24,18 +32,21 @@ protected function setUp() $this->stepMockSetup(); $this->orderResourceMock = $this->createPartialMock( - \Magento\Sales\Model\ResourceModel\Order::class, + Order::class, ['saveAttribute'] ); $this->identityContainerMock = $this->createPartialMock( - \Magento\Sales\Model\Order\Email\Container\OrderIdentity::class, + OrderIdentity::class, ['getStore', 'isEnabled', 'getConfigValue', 'getTemplateId', 'getGuestTemplateId', 'getCopyMethod'] ); $this->identityContainerMock->expects($this->any()) ->method('getStore') ->will($this->returnValue($this->storeMock)); + $this->orderMock->method('getId') + ->willReturn(self::ORDER_ID); + $this->sender = new OrderSender( $this->templateContainerMock, $this->identityContainerMock, @@ -86,7 +97,7 @@ public function testSend($configValue, $forceSyncMode, $emailSendingResult, $sen ->method('getCopyMethod') ->willReturn('copy'); - $addressMock = $this->createMock(\Magento\Sales\Model\Order\Address::class); + $addressMock = $this->createMock(Address::class); $this->addressRenderer->expects($this->any()) ->method('format') @@ -127,6 +138,7 @@ public function testSend($configValue, $forceSyncMode, $emailSendingResult, $sen ->with( [ 'order' => $this->orderMock, + 'order_id' => self::ORDER_ID, 'billing' => $addressMock, 'payment_html' => 'payment', 'store' => $this->storeMock, @@ -237,7 +249,7 @@ public function sendDataProvider() public function testSendVirtualOrder($isVirtualOrder, $formatCallCount, $expectedShippingAddress) { $address = 'address_test'; - $this->orderMock->setData(\Magento\Sales\Api\Data\OrderInterface::IS_VIRTUAL, $isVirtualOrder); + $this->orderMock->setData(OrderInterface::IS_VIRTUAL, $isVirtualOrder); $createdAtFormatted='Oct 14, 2019, 4:11:58 PM'; $customerName = 'test customer'; $frontendStatusLabel = 'Complete'; @@ -260,7 +272,7 @@ public function testSendVirtualOrder($isVirtualOrder, $formatCallCount, $expecte ->method('getCopyMethod') ->willReturn('copy'); - $addressMock = $this->createMock(\Magento\Sales\Model\Order\Address::class); + $addressMock = $this->createMock(Address::class); $this->addressRenderer->expects($this->exactly($formatCallCount)) ->method('format') @@ -295,6 +307,7 @@ public function testSendVirtualOrder($isVirtualOrder, $formatCallCount, $expecte ->with( [ 'order' => $this->orderMock, + 'order_id' => self::ORDER_ID, 'billing' => $addressMock, 'payment_html' => 'payment', 'store' => $this->storeMock, diff --git a/app/code/Magento/Sales/Test/Unit/Model/Order/Email/Sender/ShipmentSenderTest.php b/app/code/Magento/Sales/Test/Unit/Model/Order/Email/Sender/ShipmentSenderTest.php index dc6fc53e5ec43..7dfda5f9e0b41 100644 --- a/app/code/Magento/Sales/Test/Unit/Model/Order/Email/Sender/ShipmentSenderTest.php +++ b/app/code/Magento/Sales/Test/Unit/Model/Order/Email/Sender/ShipmentSenderTest.php @@ -5,25 +5,39 @@ */ namespace Magento\Sales\Test\Unit\Model\Order\Email\Sender; +use Magento\Sales\Api\Data\OrderInterface; +use Magento\Sales\Model\Order\Address; +use Magento\Sales\Model\Order\Email\Container\ShipmentIdentity; use Magento\Sales\Model\Order\Email\Sender\ShipmentSender; +use Magento\Sales\Model\Order\Shipment; +use Magento\Sales\Model\ResourceModel\Order\Shipment as ShipmentResource; +use Magento\Sales\Model\ResourceModel\EntityAbstract; +use PHPUnit\Framework\MockObject\MockObject; /** - * Test for Magento\Sales\Model\Order\Email\Sender\ShipmentSender class. + * Test for Magento\Sales\Model\Order\Email\Sender\ShipmentSender class + * + * @deprecated since ShipmentSender is deprecated + * @see \Magento\Sales\Model\Order\Email\Sender\ShipmentSender */ class ShipmentSenderTest extends AbstractSenderTest { + private const SHIPMENT_ID = 1; + + private const ORDER_ID = 1; + /** - * @var \Magento\Sales\Model\Order\Email\Sender\ShipmentSender + * @var ShipmentSender */ protected $sender; /** - * @var \Magento\Sales\Model\Order\Shipment|\PHPUnit_Framework_MockObject_MockObject + * @var Shipment|MockObject */ protected $shipmentMock; /** - * @var \Magento\Sales\Model\ResourceModel\EntityAbstract|\PHPUnit_Framework_MockObject_MockObject + * @var EntityAbstract|MockObject */ protected $shipmentResourceMock; @@ -32,14 +46,15 @@ protected function setUp() $this->stepMockSetup(); $this->shipmentResourceMock = $this->createPartialMock( - \Magento\Sales\Model\ResourceModel\Order\Shipment::class, + ShipmentResource::class, ['saveAttribute'] ); $this->shipmentMock = $this->createPartialMock( - \Magento\Sales\Model\Order\Shipment::class, + Shipment::class, [ 'getStore', + 'getId', '__wakeup', 'getOrder', 'setSendEmail', @@ -55,8 +70,13 @@ protected function setUp() ->method('getOrder') ->will($this->returnValue($this->orderMock)); + $this->shipmentMock->method('getId') + ->willReturn(self::SHIPMENT_ID); + $this->orderMock->method('getId') + ->willReturn(self::ORDER_ID); + $this->identityContainerMock = $this->createPartialMock( - \Magento\Sales\Model\Order\Email\Container\ShipmentIdentity::class, + ShipmentIdentity::class, ['getStore', 'isEnabled', 'getConfigValue', 'getTemplateId', 'getGuestTemplateId', 'getCopyMethod'] ); $this->identityContainerMock->expects($this->any()) @@ -104,7 +124,7 @@ public function testSend($configValue, $forceSyncMode, $customerNoteNotify, $ema ->willReturn($configValue); if (!$configValue || $forceSyncMode) { - $addressMock = $this->createMock(\Magento\Sales\Model\Order\Address::class); + $addressMock = $this->createMock(Address::class); $this->addressRenderer->expects($this->any()) ->method('format') @@ -148,7 +168,9 @@ public function testSend($configValue, $forceSyncMode, $customerNoteNotify, $ema ->with( [ 'order' => $this->orderMock, + 'order_id' => self::ORDER_ID, 'shipment' => $this->shipmentMock, + 'shipment_id' => self::SHIPMENT_ID, 'comment' => $customerNoteNotify ? $comment : '', 'billing' => $addressMock, 'payment_html' => 'payment', @@ -240,7 +262,7 @@ public function sendDataProvider() public function testSendVirtualOrder($isVirtualOrder, $formatCallCount, $expectedShippingAddress) { $address = 'address_test'; - $this->orderMock->setData(\Magento\Sales\Api\Data\OrderInterface::IS_VIRTUAL, $isVirtualOrder); + $this->orderMock->setData(OrderInterface::IS_VIRTUAL, $isVirtualOrder); $customerName = 'Test Customer'; $frontendStatusLabel = 'Complete'; $isNotVirtual = false; @@ -254,7 +276,7 @@ public function testSendVirtualOrder($isVirtualOrder, $formatCallCount, $expecte ->with('sales_email/general/async_sending') ->willReturn(false); - $addressMock = $this->createMock(\Magento\Sales\Model\Order\Address::class); + $addressMock = $this->createMock(Address::class); $this->addressRenderer->expects($this->exactly($formatCallCount)) ->method('format') @@ -288,7 +310,9 @@ public function testSendVirtualOrder($isVirtualOrder, $formatCallCount, $expecte ->with( [ 'order' => $this->orderMock, + 'order_id' => self::ORDER_ID, 'shipment' => $this->shipmentMock, + 'shipment_id' => self::SHIPMENT_ID, 'comment' => '', 'billing' => $addressMock, 'payment_html' => 'payment', diff --git a/app/code/Magento/Sales/Test/Unit/Model/Order/ItemTest.php b/app/code/Magento/Sales/Test/Unit/Model/Order/ItemTest.php index 76bfd62a7b889..3c7042c10f4d3 100644 --- a/app/code/Magento/Sales/Test/Unit/Model/Order/ItemTest.php +++ b/app/code/Magento/Sales/Test/Unit/Model/Order/ItemTest.php @@ -7,13 +7,11 @@ namespace Magento\Sales\Test\Unit\Model\Order; use Magento\Framework\Serialize\Serializer\Json; +use Magento\Sales\Api\Data\OrderItemInterface; use Magento\Sales\Model\ResourceModel\OrderFactory; -use \Magento\Sales\Model\Order; /** - * Class ItemTest - * - * @package Magento\Sales\Model\Order + * Unit test for order item class. */ class ItemTest extends \PHPUnit\Framework\TestCase { @@ -72,7 +70,7 @@ public function testSetParentItem() public function testGetPatentItem() { $item = $this->objectManager->getObject(\Magento\Sales\Model\Order\Item::class, []); - $this->model->setData(\Magento\Sales\Api\Data\OrderItemInterface::PARENT_ITEM, $item); + $this->model->setData(OrderItemInterface::PARENT_ITEM, $item); $this->assertEquals($item, $this->model->getParentItem()); } @@ -184,7 +182,7 @@ public function testGetOriginalPrice() $this->assertEquals($price, $this->model->getOriginalPrice()); $originalPrice = 5.55; - $this->model->setData(\Magento\Sales\Api\Data\OrderItemInterface::ORIGINAL_PRICE, $originalPrice); + $this->model->setData(OrderItemInterface::ORIGINAL_PRICE, $originalPrice); $this->assertEquals($originalPrice, $this->model->getOriginalPrice()); } @@ -348,4 +346,24 @@ public function getItemQtyVariants() ] ]; } + + /** + * Test getPrice() method returns float + */ + public function testGetPriceReturnsFloat() + { + $price = 9.99; + $this->model->setPrice($price); + $this->assertEquals($price, $this->model->getPrice()); + } + + /** + * Test getPrice() method returns null + */ + public function testGetPriceReturnsNull() + { + $nullablePrice = null; + $this->model->setData(OrderItemInterface::PRICE, $nullablePrice); + $this->assertEquals($nullablePrice, $this->model->getPrice()); + } } diff --git a/app/code/Magento/Sales/Test/Unit/Model/Order/Payment/Operations/CaptureOperationTest.php b/app/code/Magento/Sales/Test/Unit/Model/Order/Payment/Operations/CaptureOperationTest.php index a2165e6792c45..33d7caf9a94c0 100644 --- a/app/code/Magento/Sales/Test/Unit/Model/Order/Payment/Operations/CaptureOperationTest.php +++ b/app/code/Magento/Sales/Test/Unit/Model/Order/Payment/Operations/CaptureOperationTest.php @@ -3,116 +3,151 @@ * Copyright © Magento, Inc. All rights reserved. * See COPYING.txt for license details. */ +declare(strict_types=1); namespace Magento\Sales\Test\Unit\Model\Order\Payment\Operations; -use Magento\Framework\ObjectManager\ObjectManager; -use Magento\Payment\Model\Method; -use Magento\Framework\TestFramework\Unit\Helper\ObjectManager as ObjectManagerHelper; +use Magento\Framework\Event\ManagerInterface as EventManagerInterface; +use Magento\Framework\Exception\LocalizedException; +use Magento\Payment\Model\MethodInterface; +use Magento\Sales\Model\Order; +use Magento\Sales\Model\Order\Invoice; +use Magento\Sales\Model\Order\Payment; +use Magento\Sales\Model\Order\Payment\Operations\CaptureOperation; +use Magento\Sales\Model\Order\Payment\Operations\ProcessInvoiceOperation; +use Magento\Sales\Model\Order\Payment\State\CommandInterface; +use Magento\Sales\Model\Order\Payment\Transaction\BuilderInterface; +use Magento\Sales\Model\Order\Payment\Transaction\ManagerInterface as TransactionManagerInterface; class CaptureOperationTest extends \PHPUnit\Framework\TestCase { /** - * @var \PHPUnit_Framework_MockObject_MockObject + * @var TransactionManagerInterface|\PHPUnit_Framework_MockObject_MockObject */ - protected $transactionManager; + private $transactionManager; /** - * @var \PHPUnit_Framework_MockObject_MockObject + * @var EventManagerInterface|\PHPUnit_Framework_MockObject_MockObject */ - protected $eventManager; + private $eventManager; /** - * @var \PHPUnit_Framework_MockObject_MockObject + * @var BuilderInterface|\PHPUnit_Framework_MockObject_MockObject */ - protected $transactionBuilder; + private $transactionBuilder; /** - * @var \PHPUnit_Framework_MockObject_MockObject + * @var CommandInterface|\PHPUnit_Framework_MockObject_MockObject */ - protected $stateCommand; + private $stateCommand; /** - * @var \Magento\Sales\Model\Order\Payment\Operations\CaptureOperation + * @var ProcessInvoiceOperation|\PHPUnit_Framework_MockObject_MockObject */ - protected $model; + private $processInvoiceOperation; + + /** + * @var CaptureOperation + */ + private $model; protected function setUp() { - $transactionClass = \Magento\Sales\Model\Order\Payment\Transaction\ManagerInterface::class; - $transactionBuilderClass = \Magento\Sales\Model\Order\Payment\Transaction\BuilderInterface::class; - $this->transactionManager = $this->getMockBuilder($transactionClass) - ->disableOriginalConstructor() - ->getMock(); - $this->eventManager = $this->getMockBuilder(\Magento\Framework\Event\ManagerInterface::class) - ->disableOriginalConstructor() - ->getMock(); - $this->transactionBuilder = $this->getMockBuilder($transactionBuilderClass) + $this->transactionManager = $this->getMockForAbstractClass(TransactionManagerInterface::class); + $this->eventManager = $this->getMockForAbstractClass(EventManagerInterface::class); + $this->transactionBuilder = $this->getMockForAbstractClass(BuilderInterface::class); + $this->stateCommand = $this->getMockForAbstractClass(CommandInterface::class); + $this->processInvoiceOperation = $this->getMockBuilder(ProcessInvoiceOperation::class) ->disableOriginalConstructor() ->getMock(); - $this->stateCommand = $this->getMockBuilder(\Magento\Sales\Model\Order\Payment\State\CommandInterface::class) - ->disableOriginalConstructor() - ->getMock(); - $objectManagerHelper = new ObjectManagerHelper($this); - $this->model = $objectManagerHelper->getObject( - \Magento\Sales\Model\Order\Payment\Operations\CaptureOperation::class, - [ - 'transactionManager' => $this->transactionManager, - 'eventManager' => $this->eventManager, - 'transactionBuilder' => $this->transactionBuilder, - 'stateCommand' => $this->stateCommand - ] + + $this->model = new CaptureOperation( + $this->stateCommand, + $this->transactionBuilder, + $this->transactionManager, + $this->eventManager, + $this->processInvoiceOperation ); } - public function testCapture() + /** + * Tests a case when capture operation is called with null invoice. + * + * @throws LocalizedException + */ + public function testCaptureWithoutInvoice() { - $baseGrandTotal = 10; - - $order = $this->getMockBuilder(\Magento\Sales\Model\Order::class) + $invoice = $this->getMockBuilder(Invoice::class) ->disableOriginalConstructor() ->getMock(); + $invoice->expects($this->once()) + ->method('register'); + $invoice->expects($this->once()) + ->method('capture'); - $paymentMethod = $this->getMockBuilder(\Magento\Payment\Model\MethodInterface::class) + $order = $this->getMockBuilder(Order::class) + ->setMethods(['prepareInvoice', 'addRelatedObject', 'setStatus']) ->disableOriginalConstructor() ->getMock(); + $order->expects($this->once()) + ->method('prepareInvoice') + ->willReturn($invoice); + $order->expects($this->once()) + ->method('addRelatedObject'); + $order->expects($this->once()) + ->method('setStatus') + ->with(Order::STATUS_FRAUD); + + /** @var MethodInterface $paymentMethod */ + $paymentMethod = $this->getMockForAbstractClass(MethodInterface::class); + $paymentMethod->method('canCapture') + ->willReturn(true); - $orderPayment = $this->getMockBuilder(\Magento\Sales\Model\Order\Payment::class) + /** @var Payment|\PHPUnit_Framework_MockObject_MockObject $orderPayment| */ + $orderPayment = $this->getMockBuilder(Payment::class) + ->setMethods(['setCreatedInvoice', 'getOrder', 'getMethodInstance', 'getIsFraudDetected']) ->disableOriginalConstructor() ->getMock(); - $orderPayment->expects($this->any()) - ->method('formatAmount') - ->with($baseGrandTotal) - ->willReturnArgument(0); - $orderPayment->expects($this->any()) - ->method('getOrder') - ->willReturn($order); - $orderPayment->expects($this->any()) - ->method('getMethodInstance') - ->willReturn($paymentMethod); $orderPayment->expects($this->once()) - ->method('getIsTransactionPending') + ->method('setCreatedInvoice') + ->with($invoice); + $orderPayment->method('getIsFraudDetected') ->willReturn(true); - $orderPayment->expects($this->once()) - ->method('getTransactionAdditionalInfo') - ->willReturn([]); + $orderPayment->method('getOrder') + ->willReturn($order); + $orderPayment->method('getMethodInstance') + ->willReturn($paymentMethod); - $paymentMethod->expects($this->once()) - ->method('capture') - ->with($orderPayment, $baseGrandTotal); + $this->assertInstanceOf( + Payment::class, + $this->model->capture($orderPayment, null) + ); + } - $this->transactionBuilder->expects($this->once()) - ->method('setPayment') - ->with($orderPayment) - ->willReturnSelf(); + /** + * Tests a case when capture operation is called with null invoice. + * + * @throws LocalizedException + */ + public function testCaptureWithInvoice() + { + /** @var Invoice|\PHPUnit_Framework_MockObject_MockObject $invoice */ + $invoice = $this->getMockBuilder(Invoice::class) + ->disableOriginalConstructor() + ->getMock(); - $invoice = $this->getMockBuilder(\Magento\Sales\Model\Order\Invoice::class) + /** @var Payment|\PHPUnit_Framework_MockObject_MockObject $orderPayment| */ + $orderPayment = $this->getMockBuilder(Payment::class) ->disableOriginalConstructor() ->getMock(); - $invoice->expects($this->any()) - ->method('getBaseGrandTotal') - ->willReturn($baseGrandTotal); - $this->model->capture($orderPayment, $invoice); + $this->processInvoiceOperation->expects($this->once()) + ->method('execute') + ->willReturn($orderPayment); + + $this->assertInstanceOf( + Payment::class, + $this->model->capture($orderPayment, $invoice) + ); } } diff --git a/app/code/Magento/Sales/Test/Unit/Model/Order/Payment/Operations/ProcessInvoiceOperationTest.php b/app/code/Magento/Sales/Test/Unit/Model/Order/Payment/Operations/ProcessInvoiceOperationTest.php new file mode 100644 index 0000000000000..511579f0eb90c --- /dev/null +++ b/app/code/Magento/Sales/Test/Unit/Model/Order/Payment/Operations/ProcessInvoiceOperationTest.php @@ -0,0 +1,131 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +declare(strict_types=1); + +namespace Magento\Sales\Test\Unit\Model\Order\Payment\Operations; + +use Magento\Framework\Event\ManagerInterface as EventManagerInterface; +use Magento\Framework\Exception\LocalizedException; +use Magento\Payment\Model\Method\Adapter; +use Magento\Sales\Model\Order; +use Magento\Sales\Model\Order\Invoice; +use Magento\Sales\Model\Order\Payment; +use Magento\Sales\Model\Order\Payment\Operations\ProcessInvoiceOperation; +use Magento\Sales\Model\Order\Payment\State\CommandInterface; +use Magento\Sales\Model\Order\Payment\Transaction; +use Magento\Sales\Model\Order\Payment\Transaction\BuilderInterface; +use Magento\Sales\Model\Order\Payment\Transaction\ManagerInterface as TransactionManagerInterface; + +class ProcessInvoiceOperationTest extends \PHPUnit\Framework\TestCase +{ + /** + * @var TransactionManagerInterface|\PHPUnit_Framework_MockObject_MockObject + */ + private $transactionManager; + + /** + * @var EventManagerInterface|\PHPUnit_Framework_MockObject_MockObject + */ + private $eventManager; + + /** + * @var BuilderInterface|\PHPUnit_Framework_MockObject_MockObject + */ + private $transactionBuilder; + + /** + * @var CommandInterface|\PHPUnit_Framework_MockObject_MockObject + */ + private $stateCommand; + + /** + * @var ProcessInvoiceOperation|\PHPUnit_Framework_MockObject_MockObject + */ + protected $model; + + protected function setUp() + { + $this->transactionManager = $this->getMockForAbstractClass(TransactionManagerInterface::class); + $this->eventManager = $this->getMockForAbstractClass(EventManagerInterface::class); + $this->transactionBuilder = $this->getMockForAbstractClass(BuilderInterface::class); + $this->stateCommand = $this->getMockForAbstractClass(CommandInterface::class); + + $this->model = new ProcessInvoiceOperation( + $this->stateCommand, + $this->transactionBuilder, + $this->transactionManager, + $this->eventManager + ); + } + + public function testExecute() + { + $amountToCapture = $baseGrandTotal = 10; + $operationMethod = 'sale'; + $storeId = 1; + $transactionId = '1ASD3456'; + + /** @var Order|\PHPUnit_Framework_MockObject_MockObject $order */ + $order = $this->getMockBuilder(Order::class) + ->disableOriginalConstructor() + ->getMock(); + $order->method('getStoreId') + ->willReturn($storeId); + + /** @var Adapter|\PHPUnit_Framework_MockObject_MockObject $paymentMethod */ + $paymentMethod = $this->getMockBuilder(Adapter::class) + ->disableOriginalConstructor() + ->getMock(); + + $orderPayment = $this->getMockBuilder(Payment::class) + ->disableOriginalConstructor() + ->getMock(); + $orderPayment->method('formatAmount') + ->with($baseGrandTotal) + ->willReturnArgument(0); + $orderPayment->method('getOrder') + ->willReturn($order); + $orderPayment->method('getMethodInstance') + ->willReturn($paymentMethod); + $orderPayment->expects($this->once()) + ->method('setTransactionId') + ->with($transactionId); + $authTransaction = $this->createMock(Transaction::class); + $orderPayment->expects($this->once()) + ->method('getAuthorizationTransaction') + ->willReturn($authTransaction); + $orderPayment->expects($this->once()) + ->method('getIsTransactionPending') + ->willReturn(true); + $orderPayment->expects($this->once()) + ->method('getTransactionAdditionalInfo') + ->willReturn([]); + + $this->transactionManager->expects($this->once()) + ->method('generateTransactionId') + ->with($orderPayment, Transaction::TYPE_CAPTURE, $authTransaction) + ->willReturn($transactionId); + + $paymentMethod->method('setStore') + ->with($storeId); + $paymentMethod->expects($this->once()) + ->method($operationMethod) + ->with($orderPayment, $amountToCapture); + + $this->transactionBuilder->expects($this->once()) + ->method('setPayment') + ->with($orderPayment) + ->willReturnSelf(); + + $invoice = $this->getMockBuilder(Invoice::class) + ->disableOriginalConstructor() + ->getMock(); + $invoice->method('getBaseGrandTotal') + ->willReturn($baseGrandTotal); + + $this->model->execute($orderPayment, $invoice, $operationMethod); + } +} diff --git a/app/code/Magento/Sales/Test/Unit/Model/Order/Payment/Operations/SaleOperationTest.php b/app/code/Magento/Sales/Test/Unit/Model/Order/Payment/Operations/SaleOperationTest.php new file mode 100644 index 0000000000000..9e125ecb3d295 --- /dev/null +++ b/app/code/Magento/Sales/Test/Unit/Model/Order/Payment/Operations/SaleOperationTest.php @@ -0,0 +1,134 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +declare(strict_types=1); + +namespace Magento\Sales\Test\Unit\Model\Order\Payment\Operations; + +use Magento\Framework\Exception\LocalizedException; +use Magento\Payment\Model\MethodInterface; +use Magento\Sales\Model\Order; +use Magento\Sales\Model\Order\Invoice; +use Magento\Sales\Model\Order\Payment; +use Magento\Sales\Model\Order\Payment\Operations\SaleOperation; +use Magento\Sales\Model\Order\Payment\Operations\ProcessInvoiceOperation; + +class SaleOperationTest extends \PHPUnit\Framework\TestCase +{ + /** + * @var ProcessInvoiceOperation|\PHPUnit_Framework_MockObject_MockObject + */ + private $processInvoiceOperation; + + /** + * @var SaleOperation + */ + private $model; + + protected function setUp() + { + $this->processInvoiceOperation = $this->getMockBuilder(ProcessInvoiceOperation::class) + ->disableOriginalConstructor() + ->getMock(); + + $this->model = new SaleOperation( + $this->processInvoiceOperation + ); + } + + /** + * Tests a case when 'sale' operation is called with fraud payment. + * + * @throws LocalizedException + * @dataProvider saleDataProvider + */ + public function testExecute(Invoice $invoice) + { + $order = $this->getMockBuilder(Order::class) + ->setMethods(['prepareInvoice', 'addRelatedObject', 'setStatus']) + ->disableOriginalConstructor() + ->getMock(); + $order->expects($this->once()) + ->method('prepareInvoice') + ->willReturn($invoice); + $order->expects($this->once()) + ->method('addRelatedObject'); + $order->expects($this->once()) + ->method('setStatus') + ->with(Order::STATUS_FRAUD); + + /** @var MethodInterface|\PHPUnit_Framework_MockObject_MockObject $paymentMethod */ + $paymentMethod = $this->getMockForAbstractClass(MethodInterface::class); + + /** @var Payment|\PHPUnit_Framework_MockObject_MockObject $orderPayment| */ + $orderPayment = $this->getMockBuilder(Payment::class) + ->setMethods(['setCreatedInvoice', 'getOrder', 'getMethodInstance', 'getIsFraudDetected']) + ->disableOriginalConstructor() + ->getMock(); + $orderPayment->expects($this->once()) + ->method('setCreatedInvoice') + ->with($invoice); + $orderPayment->method('getIsFraudDetected') + ->willReturn(true); + $orderPayment->method('getOrder') + ->willReturn($order); + $orderPayment->method('getMethodInstance') + ->willReturn($paymentMethod); + + $this->assertInstanceOf( + Payment::class, + $this->model->execute($orderPayment) + ); + } + + /** + * @return array + */ + public function saleDataProvider() + { + return [ + ['paid invoice' => $this->getPaidInvoice()], + ['unpaid invoice' => $this->getUnpaidInvoice()] + ]; + } + + /** + * @return \PHPUnit_Framework_MockObject_MockObject + */ + private function getPaidInvoice(): \PHPUnit_Framework_MockObject_MockObject + { + $invoice = $this->getMockBuilder(Invoice::class) + ->setMethods(['register', 'getIsPaid', 'pay']) + ->disableOriginalConstructor() + ->getMock(); + $invoice->expects($this->once()) + ->method('register'); + $invoice->method('getIsPaid') + ->willReturn(true); + $invoice->expects($this->once()) + ->method('pay'); + + return $invoice; + } + + /** + * @return \PHPUnit_Framework_MockObject_MockObject + */ + private function getUnpaidInvoice(): \PHPUnit_Framework_MockObject_MockObject + { + $invoice = $this->getMockBuilder(Invoice::class) + ->setMethods(['register', 'getIsPaid', 'pay']) + ->disableOriginalConstructor() + ->getMock(); + $invoice->expects($this->once()) + ->method('register'); + $invoice->method('getIsPaid') + ->willReturn(false); + $invoice->expects($this->never()) + ->method('pay'); + + return $invoice; + } +} diff --git a/app/code/Magento/Sales/Test/Unit/Model/Order/Payment/State/RegisterCaptureNotificationCommandTest.php b/app/code/Magento/Sales/Test/Unit/Model/Order/Payment/State/RegisterCaptureNotificationCommandTest.php index 1b762fafe0b71..c5c333e55a551 100644 --- a/app/code/Magento/Sales/Test/Unit/Model/Order/Payment/State/RegisterCaptureNotificationCommandTest.php +++ b/app/code/Magento/Sales/Test/Unit/Model/Order/Payment/State/RegisterCaptureNotificationCommandTest.php @@ -80,6 +80,22 @@ public function commandResultDataProvider() $this->newOrderStatus, 'Registered notification about captured amount of %1.', ], + [ + false, + false, + Order::STATE_NEW, + Order::STATE_PROCESSING, + $this->newOrderStatus, + 'Registered notification about captured amount of %1.', + ], + [ + false, + false, + Order::STATE_PENDING_PAYMENT, + Order::STATE_PROCESSING, + $this->newOrderStatus, + 'Registered notification about captured amount of %1.', + ], [ true, false, diff --git a/app/code/Magento/Sales/Test/Unit/Model/Order/PaymentTest.php b/app/code/Magento/Sales/Test/Unit/Model/Order/PaymentTest.php index 9d0f10a30e6ef..d27c9619cff9e 100644 --- a/app/code/Magento/Sales/Test/Unit/Model/Order/PaymentTest.php +++ b/app/code/Magento/Sales/Test/Unit/Model/Order/PaymentTest.php @@ -9,18 +9,19 @@ use Magento\Framework\Model\Context; use Magento\Payment\Helper\Data; use Magento\Payment\Model\Method\AbstractMethod; +use Magento\Payment\Model\Method\Adapter; use Magento\Sales\Api\CreditmemoManagementInterface; +use Magento\Sales\Api\Data\OrderStatusHistoryInterface; use Magento\Sales\Model\Order; use Magento\Sales\Model\Order\Creditmemo; use Magento\Sales\Model\Order\Invoice; use Magento\Sales\Model\Order\OrderStateResolverInterface; use Magento\Sales\Model\Order\Payment; +use Magento\Sales\Model\Order\Payment\Operations\SaleOperation; use Magento\Sales\Model\Order\Payment\Transaction; -use PHPUnit_Framework_MockObject_MockObject as MockObject; +use PHPUnit\Framework\MockObject\MockObject; /** - * Class PaymentTest - * * @SuppressWarnings(PHPMD.CouplingBetweenObjects) * @SuppressWarnings(PHPMD.TooManyFields) */ @@ -49,17 +50,17 @@ class PaymentTest extends \PHPUnit\Framework\TestCase private $helper; /** - * @var \Magento\Framework\Event\Manager | \PHPUnit_Framework_MockObject_MockObject + * @var \Magento\Framework\Event\Manager|MockObject */ protected $eventManagerMock; /** - * @var \Magento\Directory\Model\PriceCurrency | \PHPUnit_Framework_MockObject_MockObject + * @var \Magento\Directory\Model\PriceCurrency|MockObject */ protected $priceCurrencyMock; /** - * @var \Magento\Directory\Model\Currency | \PHPUnit_Framework_MockObject_MockObject + * @var \Magento\Directory\Model\Currency|MockObject */ protected $currencyMock; @@ -84,51 +85,56 @@ class PaymentTest extends \PHPUnit\Framework\TestCase private $transactionId; /** - * @var \PHPUnit_Framework_MockObject_MockObject + * @var MockObject */ protected $transactionCollectionFactory; /** - * @var \Magento\Sales\Model\Order\CreditmemoFactory|\PHPUnit_Framework_MockObject_MockObject + * @var \Magento\Sales\Model\Order\CreditmemoFactory|MockObject */ protected $creditmemoFactoryMock; /** - * @var \Magento\Sales\Model\Order\Creditmemo | \PHPUnit_Framework_MockObject_MockObject + * @var \Magento\Sales\Model\Order\Creditmemo|MockObject */ protected $creditMemoMock; /** - * @var \Magento\Sales\Model\Order\Payment\Transaction\Repository | \PHPUnit_Framework_MockObject_MockObject + * @var \Magento\Sales\Model\Order\Payment\Transaction\Repository|MockObject */ protected $transactionRepositoryMock; /** - * @var \Magento\Sales\Model\Order\Payment\Transaction\ManagerInterface| \PHPUnit_Framework_MockObject_MockObject + * @var \Magento\Sales\Model\Order\Payment\Transaction\ManagerInterface|MockObject */ protected $transactionManagerMock; /** - * @var \Magento\Sales\Model\Order\Payment\Transaction\BuilderInterface | \PHPUnit_Framework_MockObject_MockObject + * @var \Magento\Sales\Model\Order\Payment\Transaction\BuilderInterface|MockObject */ protected $transactionBuilderMock; /** - * @var \Magento\Sales\Model\Order\Payment\Processor|\PHPUnit_Framework_MockObject_MockObject + * @var \Magento\Sales\Model\Order\Payment\Processor|MockObject */ protected $paymentProcessor; /** - * @var \Magento\Sales\Model\OrderRepository|\PHPUnit_Framework_MockObject_MockObject + * @var \Magento\Sales\Model\OrderRepository|MockObject */ protected $orderRepository; /** - * @var CreditmemoManagementInterface + * @var CreditmemoManagementInterface|MockObject */ private $creditmemoManagerMock; + /** + * @var SaleOperation|MockObject + */ + private $saleOperation; + /** * @return void * @SuppressWarnings(PHPMD.ExcessiveMethodLength) @@ -140,6 +146,10 @@ protected function setUp() ->disableOriginalConstructor() ->getMock(); + $this->saleOperation = $this->getMockBuilder(SaleOperation::class) + ->disableOriginalConstructor() + ->getMock(); + $this->context = $this->getMockBuilder(Context::class) ->disableOriginalConstructor() ->getMock(); @@ -177,28 +187,8 @@ function ($value) { } ); - $this->paymentMethod = $this->getMockBuilder(AbstractMethod::class) + $this->paymentMethod = $this->getMockBuilder(Adapter::class) ->disableOriginalConstructor() - ->setMethods( - [ - 'canVoid', - 'authorize', - 'getConfigData', - 'getConfigPaymentAction', - 'validate', - 'setStore', - 'acceptPayment', - 'denyPayment', - 'fetchTransactionInfo', - 'canCapture', - 'canRefund', - 'canOrder', - 'order', - 'isInitializeNeeded', - 'initialize', - 'refund' - ] - ) ->getMock(); $this->invoice = $this->getMockBuilder(Invoice::class) @@ -377,7 +367,7 @@ public function testPlaceActionOrder() ->willReturnSelf(); $this->mockPlaceEvents(); $statusHistory = $this->getMockForAbstractClass( - \Magento\Sales\Api\Data\OrderStatusHistoryInterface::class + OrderStatusHistoryInterface::class ); $this->order->expects($this->any())->method('getCustomerNote')->willReturn($customerNote); $this->order->expects($this->any()) @@ -437,7 +427,7 @@ public function testPlaceActionAuthorizeInitializeNeeded() ->willReturnSelf(); $this->mockPlaceEvents(); $statusHistory = $this->getMockForAbstractClass( - \Magento\Sales\Api\Data\OrderStatusHistoryInterface::class + OrderStatusHistoryInterface::class ); $this->order->expects($this->any())->method('getCustomerNote')->willReturn($customerNote); $this->order->expects($this->any()) @@ -473,7 +463,7 @@ public function testPlaceActionAuthorizeFraud() ->with('order_status', null) ->willReturn($newOrderStatus); $statusHistory = $this->getMockForAbstractClass( - \Magento\Sales\Api\Data\OrderStatusHistoryInterface::class + OrderStatusHistoryInterface::class ); $this->order->expects($this->any())->method('getCustomerNote')->willReturn($customerNote); $this->order->expects($this->any()) @@ -519,7 +509,7 @@ public function testPlaceActionAuthorizeCapture() ->with('order_status', null) ->willReturn($newOrderStatus); $statusHistory = $this->getMockForAbstractClass( - \Magento\Sales\Api\Data\OrderStatusHistoryInterface::class + OrderStatusHistoryInterface::class ); $this->order->expects($this->any())->method('getCustomerNote')->willReturn($customerNote); $this->order->expects($this->any()) @@ -545,6 +535,52 @@ public function testPlaceActionAuthorizeCapture() $this->assertEquals($sum, $this->payment->getBaseAmountAuthorized()); } + /** + * Tests place order flow with supported 'sale' payment operation. + */ + public function testPlaceWithSaleOperationSupported() + { + $newOrderStatus = 'new_status'; + $customerNote = 'blabla'; + $sum = 10; + + $this->paymentMethod->method('canSale') + ->willReturn(true); + $this->order->method('getTotalDue') + ->willReturn($sum); + $this->order->method('getBaseTotalDue') + ->willReturn($sum); + $this->helper->method('getMethodInstance') + ->willReturn($this->paymentMethod); + $this->paymentMethod->method('getConfigPaymentAction') + ->willReturn(AbstractMethod::ACTION_AUTHORIZE_CAPTURE); + $this->paymentMethod->method('getConfigData') + ->with('order_status', null) + ->willReturn($newOrderStatus); + $statusHistory = $this->getMockForAbstractClass(OrderStatusHistoryInterface::class); + $this->order->method('getCustomerNote') + ->willReturn($customerNote); + $this->order->method('addStatusHistoryComment') + ->with($customerNote) + ->willReturn($statusHistory); + $this->mockGetDefaultStatus(Order::STATE_PROCESSING, $newOrderStatus, ['first', 'second']); + $this->order->method('setState') + ->with(Order::STATE_PROCESSING) + ->willReturnSelf(); + $this->order->method('setStatus') + ->with($newOrderStatus) + ->willReturnSelf(); + $this->paymentMethod->expects($this->once()) + ->method('getConfigPaymentAction') + ->willReturn(null); + $this->saleOperation->expects($this->once()) + ->method('execute'); + + $this->assertEquals($this->payment, $this->payment->place()); + $this->assertEquals($sum, $this->payment->getAmountAuthorized()); + $this->assertEquals($sum, $this->payment->getBaseAmountAuthorized()); + } + /** * @param bool $isOnline * @param float $amount @@ -1594,7 +1630,8 @@ protected function initPayment() 'transactionBuilder' => $this->transactionBuilderMock, 'paymentProcessor' => $this->paymentProcessor, 'orderRepository' => $this->orderRepository, - 'creditmemoManager' => $this->creditmemoManagerMock + 'creditmemoManager' => $this->creditmemoManagerMock, + 'saleOperation' => $this->saleOperation ] ); } @@ -1621,7 +1658,7 @@ protected function assertOrderUpdated( ->willReturnSelf(); $statusHistory = $this->getMockForAbstractClass( - \Magento\Sales\Api\Data\OrderStatusHistoryInterface::class + OrderStatusHistoryInterface::class ); $this->order->expects($this->any()) ->method('addStatusHistoryComment') diff --git a/app/code/Magento/Sales/Test/Unit/Model/Order/Shipment/Sender/EmailSenderTest.php b/app/code/Magento/Sales/Test/Unit/Model/Order/Shipment/Sender/EmailSenderTest.php index 2262fbf03c1a1..4bca8e6a4b730 100644 --- a/app/code/Magento/Sales/Test/Unit/Model/Order/Shipment/Sender/EmailSenderTest.php +++ b/app/code/Magento/Sales/Test/Unit/Model/Order/Shipment/Sender/EmailSenderTest.php @@ -5,96 +5,120 @@ */ namespace Magento\Sales\Test\Unit\Model\Order\Shipment\Sender; +use Magento\Framework\App\Config\ScopeConfigInterface; +use Magento\Framework\Event\ManagerInterface; +use Magento\Payment\Helper\Data; +use Magento\Payment\Model\Info; +use Magento\Sales\Api\Data\ShipmentCommentCreationInterface; +use Magento\Sales\Api\Data\ShipmentInterface; +use Magento\Sales\Model\Order; +use Magento\Sales\Model\Order\Address; +use Magento\Sales\Model\Order\Address\Renderer; +use Magento\Sales\Model\Order\Email\Container\ShipmentIdentity; +use Magento\Sales\Model\Order\Email\Container\Template; +use Magento\Sales\Model\Order\Email\Sender; +use Magento\Sales\Model\Order\Email\SenderBuilderFactory; +use Magento\Sales\Model\Order\Shipment\Sender\EmailSender; +use Magento\Sales\Model\ResourceModel\Order\Shipment; +use Magento\Store\Model\Store; +use PHPUnit\Framework\MockObject\MockObject; +use PHPUnit\Framework\TestCase; +use Psr\Log\LoggerInterface; + /** * Unit test for email notification sender for Shipment. * * @SuppressWarnings(PHPMD.TooManyFields) * @SuppressWarnings(PHPMD.CouplingBetweenObjects) */ -class EmailSenderTest extends \PHPUnit\Framework\TestCase +class EmailSenderTest extends TestCase { + private const SHIPMENT_ID = 1; + + private const ORDER_ID = 1; + /** - * @var \Magento\Sales\Model\Order\Shipment\Sender\EmailSender + * @var EmailSender */ private $subject; /** - * @var \Magento\Sales\Model\Order|\PHPUnit_Framework_MockObject_MockObject + * @var Order|MockObject */ private $orderMock; /** - * @var \Magento\Store\Model\Store|\PHPUnit_Framework_MockObject_MockObject + * @var Store|MockObject */ private $storeMock; /** - * @var \Magento\Sales\Model\Order\Email\Sender|\PHPUnit_Framework_MockObject_MockObject + * @var Sender|MockObject */ private $senderMock; /** - * @var \Psr\Log\LoggerInterface|\PHPUnit_Framework_MockObject_MockObject + * @var LoggerInterface|MockObject */ private $loggerMock; /** - * @var \Magento\Sales\Api\Data\ShipmentInterface|\PHPUnit_Framework_MockObject_MockObject + * @var ShipmentInterface|MockObject */ private $shipmentMock; /** - * @var \Magento\Sales\Api\Data\ShipmentCommentCreationInterface|\PHPUnit_Framework_MockObject_MockObject + * @var ShipmentCommentCreationInterface|MockObject */ private $commentMock; /** - * @var \Magento\Sales\Model\Order\Address|\PHPUnit_Framework_MockObject_MockObject + * @var Address|MockObject */ private $addressMock; /** - * @var \Magento\Framework\App\Config\ScopeConfigInterface|\PHPUnit_Framework_MockObject_MockObject + * @var ScopeConfigInterface|MockObject */ private $globalConfigMock; /** - * @var \Magento\Framework\Event\ManagerInterface|\PHPUnit_Framework_MockObject_MockObject + * @var ManagerInterface|MockObject */ private $eventManagerMock; /** - * @var \Magento\Payment\Model\Info|\PHPUnit_Framework_MockObject_MockObject + * @var Info|MockObject */ private $paymentInfoMock; /** - * @var \Magento\Payment\Helper\Data|\PHPUnit_Framework_MockObject_MockObject + * @var Data|MockObject */ private $paymentHelperMock; /** - * @var \Magento\Sales\Model\ResourceModel\Order\Shipment|\PHPUnit_Framework_MockObject_MockObject + * @var Shipment|MockObject */ private $shipmentResourceMock; /** - * @var \Magento\Sales\Model\Order\Address\Renderer|\PHPUnit_Framework_MockObject_MockObject + * @var Renderer|MockObject */ private $addressRendererMock; /** - * @var \Magento\Sales\Model\Order\Email\Container\Template|\PHPUnit_Framework_MockObject_MockObject + * @var Template|MockObject */ private $templateContainerMock; /** - * @var \Magento\Sales\Model\Order\Email\Container\ShipmentIdentity|\PHPUnit_Framework_MockObject_MockObject + * @var ShipmentIdentity|MockObject */ private $identityContainerMock; /** - * @var \Magento\Sales\Model\Order\Email\SenderBuilderFactory|\PHPUnit_Framework_MockObject_MockObject + * @var SenderBuilderFactory|MockObject */ private $senderBuilderFactoryMock; @@ -103,11 +127,11 @@ class EmailSenderTest extends \PHPUnit\Framework\TestCase */ protected function setUp() { - $this->orderMock = $this->getMockBuilder(\Magento\Sales\Model\Order::class) + $this->orderMock = $this->getMockBuilder(Order::class) ->disableOriginalConstructor() ->getMock(); - $this->storeMock = $this->getMockBuilder(\Magento\Store\Model\Store::class) + $this->storeMock = $this->getMockBuilder(Store::class) ->setMethods(['getStoreId']) ->disableOriginalConstructor() ->getMock(); @@ -119,21 +143,21 @@ protected function setUp() ->method('getStore') ->willReturn($this->storeMock); - $this->senderMock = $this->getMockBuilder(\Magento\Sales\Model\Order\Email\Sender::class) + $this->senderMock = $this->getMockBuilder(Sender::class) ->disableOriginalConstructor() ->setMethods(['send', 'sendCopyTo']) ->getMock(); - $this->loggerMock = $this->getMockBuilder(\Psr\Log\LoggerInterface::class) + $this->loggerMock = $this->getMockBuilder(LoggerInterface::class) ->disableOriginalConstructor() ->getMockForAbstractClass(); - $this->shipmentMock = $this->getMockBuilder(\Magento\Sales\Model\Order\Shipment::class) + $this->shipmentMock = $this->getMockBuilder(Order\Shipment::class) ->disableOriginalConstructor() - ->setMethods(['setSendEmail', 'setEmailSent']) + ->setMethods(['setSendEmail', 'setEmailSent', 'getId']) ->getMock(); - $this->commentMock = $this->getMockBuilder(\Magento\Sales\Api\Data\ShipmentCommentCreationInterface::class) + $this->commentMock = $this->getMockBuilder(ShipmentCommentCreationInterface::class) ->disableOriginalConstructor() ->getMockForAbstractClass(); @@ -141,7 +165,7 @@ protected function setUp() ->method('getComment') ->willReturn('Comment text'); - $this->addressMock = $this->getMockBuilder(\Magento\Sales\Model\Order\Address::class) + $this->addressMock = $this->getMockBuilder(Address::class) ->disableOriginalConstructor() ->getMock(); @@ -151,16 +175,19 @@ protected function setUp() $this->orderMock->expects($this->any()) ->method('getShippingAddress') ->willReturn($this->addressMock); + $this->orderMock->expects($this->any()) + ->method('getId') + ->willReturn(self::ORDER_ID); - $this->globalConfigMock = $this->getMockBuilder(\Magento\Framework\App\Config\ScopeConfigInterface::class) + $this->globalConfigMock = $this->getMockBuilder(ScopeConfigInterface::class) ->disableOriginalConstructor() ->getMockForAbstractClass(); - $this->eventManagerMock = $this->getMockBuilder(\Magento\Framework\Event\ManagerInterface::class) + $this->eventManagerMock = $this->getMockBuilder(ManagerInterface::class) ->disableOriginalConstructor() ->getMockForAbstractClass(); - $this->paymentInfoMock = $this->getMockBuilder(\Magento\Payment\Model\Info::class) + $this->paymentInfoMock = $this->getMockBuilder(Info::class) ->disableOriginalConstructor() ->getMock(); @@ -168,7 +195,7 @@ protected function setUp() ->method('getPayment') ->willReturn($this->paymentInfoMock); - $this->paymentHelperMock = $this->getMockBuilder(\Magento\Payment\Helper\Data::class) + $this->paymentHelperMock = $this->getMockBuilder(Data::class) ->disableOriginalConstructor() ->getMock(); @@ -177,11 +204,11 @@ protected function setUp() ->with($this->paymentInfoMock, 1) ->willReturn('Payment Info Block'); - $this->shipmentResourceMock = $this->getMockBuilder(\Magento\Sales\Model\ResourceModel\Order\Shipment::class) + $this->shipmentResourceMock = $this->getMockBuilder(Shipment::class) ->disableOriginalConstructor() ->getMock(); - $this->addressRendererMock = $this->getMockBuilder(\Magento\Sales\Model\Order\Address\Renderer::class) + $this->addressRendererMock = $this->getMockBuilder(Renderer::class) ->disableOriginalConstructor() ->getMock(); @@ -190,12 +217,12 @@ protected function setUp() ->with($this->addressMock, 'html') ->willReturn('Formatted address'); - $this->templateContainerMock = $this->getMockBuilder(\Magento\Sales\Model\Order\Email\Container\Template::class) + $this->templateContainerMock = $this->getMockBuilder(Template::class) ->disableOriginalConstructor() ->getMock(); $this->identityContainerMock = $this->getMockBuilder( - \Magento\Sales\Model\Order\Email\Container\ShipmentIdentity::class + ShipmentIdentity::class ) ->disableOriginalConstructor() ->getMock(); @@ -205,13 +232,13 @@ protected function setUp() ->willReturn($this->storeMock); $this->senderBuilderFactoryMock = $this->getMockBuilder( - \Magento\Sales\Model\Order\Email\SenderBuilderFactory::class + SenderBuilderFactory::class ) ->disableOriginalConstructor() ->setMethods(['create']) ->getMock(); - $this->subject = new \Magento\Sales\Model\Order\Shipment\Sender\EmailSender( + $this->subject = new EmailSender( $this->templateContainerMock, $this->identityContainerMock, $this->senderBuilderFactoryMock, @@ -247,6 +274,9 @@ public function testSend($configValue, $forceSyncMode, $isComment, $emailSending $this->commentMock = null; } + $this->shipmentMock->expects($this->any()) + ->method('getId') + ->willReturn(self::SHIPMENT_ID); $this->shipmentMock->expects($this->once()) ->method('setSendEmail') ->with($emailSendingResult); @@ -254,7 +284,9 @@ public function testSend($configValue, $forceSyncMode, $isComment, $emailSending if (!$configValue || $forceSyncMode) { $transport = [ 'order' => $this->orderMock, + 'order_id' => self::ORDER_ID, 'shipment' => $this->shipmentMock, + 'shipment_id' => self::SHIPMENT_ID, 'comment' => $isComment ? 'Comment text' : '', 'billing' => $this->addressMock, 'payment_html' => 'Payment Info Block', diff --git a/app/code/Magento/Sales/Test/Unit/Ui/Component/DataProvider/DocumentTest.php b/app/code/Magento/Sales/Test/Unit/Ui/Component/DataProvider/DocumentTest.php deleted file mode 100644 index 8052c06eee8d9..0000000000000 --- a/app/code/Magento/Sales/Test/Unit/Ui/Component/DataProvider/DocumentTest.php +++ /dev/null @@ -1,98 +0,0 @@ -<?php -/** - * Copyright © Magento, Inc. All rights reserved. - * See COPYING.txt for license details. - */ - -namespace Magento\Sales\Test\Unit\Ui\Component\DataProvider; - -use Magento\Customer\Api\Data\GroupInterface; -use Magento\Customer\Api\GroupRepositoryInterface; -use Magento\Framework\Api\AttributeValue; -use Magento\Framework\Api\AttributeValueFactory; -use Magento\Sales\Model\Order\Invoice; -use Magento\Sales\Ui\Component\DataProvider\Document; -use PHPUnit_Framework_MockObject_MockObject as MockObject; - -/** - * Class DocumentTest - */ -class DocumentTest extends \PHPUnit\Framework\TestCase -{ - /** - * @var GroupRepositoryInterface|MockObject - */ - private $groupRepository; - - /** - * @var AttributeValueFactory|MockObject - */ - private $attributeValueFactory; - - /** - * @var Document - */ - private $document; - - protected function setUp() - { - $this->initAttributeValueFactoryMock(); - - $this->groupRepository = $this->getMockForAbstractClass(GroupRepositoryInterface::class); - - $this->document = new Document($this->attributeValueFactory, $this->groupRepository); - } - - /** - * @covers \Magento\Sales\Ui\Component\DataProvider\Document::getCustomAttribute - */ - public function testGetStateAttribute() - { - $this->document->setData('state', Invoice::STATE_PAID); - - $this->groupRepository->expects(static::never()) - ->method('getById'); - - $attribute = $this->document->getCustomAttribute('state'); - static::assertEquals('Paid', $attribute->getValue()); - } - - /** - * @covers \Magento\Sales\Ui\Component\DataProvider\Document::getCustomAttribute - */ - public function testGetCustomerGroupAttribute() - { - $this->document->setData('customer_group_id', 1); - - $group = $this->getMockForAbstractClass(GroupInterface::class); - - $this->groupRepository->expects(static::once()) - ->method('getById') - ->willReturn($group); - - $group->expects(static::once()) - ->method('getCode') - ->willReturn('General'); - - $attribute = $this->document->getCustomAttribute('customer_group_id'); - static::assertEquals('General', $attribute->getValue()); - } - - /** - * Create mock for attribute value factory - * @return void - */ - private function initAttributeValueFactoryMock() - { - $this->attributeValueFactory = $this->getMockBuilder(AttributeValueFactory::class) - ->disableOriginalConstructor() - ->setMethods(['create']) - ->getMock(); - - $attributeValue = new AttributeValue(); - - $this->attributeValueFactory->expects(static::once()) - ->method('create') - ->willReturn($attributeValue); - } -} diff --git a/app/code/Magento/Sales/Ui/Component/DataProvider/Document.php b/app/code/Magento/Sales/Ui/Component/DataProvider/Document.php deleted file mode 100644 index 3030a16cc18b9..0000000000000 --- a/app/code/Magento/Sales/Ui/Component/DataProvider/Document.php +++ /dev/null @@ -1,91 +0,0 @@ -<?php -/** - * Copyright © Magento, Inc. All rights reserved. - * See COPYING.txt for license details. - */ -namespace Magento\Sales\Ui\Component\DataProvider; - -use Magento\Framework\Exception\NoSuchEntityException; -use Magento\Sales\Model\Order\Invoice; -use Magento\Customer\Api\GroupRepositoryInterface; -use Magento\Framework\Api\AttributeValueFactory; - -/** - * Class Document - */ -class Document extends \Magento\Framework\View\Element\UiComponent\DataProvider\Document -{ - /** - * @var string - */ - private static $stateAttributeCode = 'state'; - - /** - * @var string - */ - private static $customerGroupAttributeCode = 'customer_group_id'; - - /** - * @var GroupRepositoryInterface - */ - private $groupRepository; - - /** - * Document constructor. - * @param AttributeValueFactory $attributeValueFactory - * @param GroupRepositoryInterface $groupRepository - */ - public function __construct( - AttributeValueFactory $attributeValueFactory, - GroupRepositoryInterface $groupRepository - ) { - parent::__construct($attributeValueFactory); - $this->groupRepository = $groupRepository; - } - - /** - * @inheritdoc - */ - public function getCustomAttribute($attributeCode) - { - switch ($attributeCode) { - case self::$stateAttributeCode: - $this->setStateValue(); - break; - case self::$customerGroupAttributeCode: - $this->setCustomerGroupValue(); - break; - } - return parent::getCustomAttribute($attributeCode); - } - - /** - * Update invoice state value - * Method set text label instead id value - * @return void - */ - private function setStateValue() - { - $value = $this->getData(self::$stateAttributeCode); - /** @var \Magento\Framework\Phrase $state */ - $state = Invoice::getStates()[$value]; - - $this->setCustomAttribute(self::$stateAttributeCode, $state->getText()); - } - - /** - * Update customer group value - * Method set group code instead id value - * @return void - */ - private function setCustomerGroupValue() - { - $value = $this->getData(self::$customerGroupAttributeCode); - try { - $group = $this->groupRepository->getById($value); - $this->setCustomAttribute(self::$customerGroupAttributeCode, $group->getCode()); - } catch (NoSuchEntityException $e) { - $this->setCustomAttribute(self::$customerGroupAttributeCode, 'N/A'); - } - } -} diff --git a/app/code/Magento/Sales/etc/adminhtml/system.xml b/app/code/Magento/Sales/etc/adminhtml/system.xml index 473d3068d69c7..a3834b28e05a6 100644 --- a/app/code/Magento/Sales/etc/adminhtml/system.xml +++ b/app/code/Magento/Sales/etc/adminhtml/system.xml @@ -17,31 +17,31 @@ <resource>Magento_Sales::config_sales</resource> <group id="general" translate="label" type="text" sortOrder="5" showInDefault="1" showInWebsite="1" showInStore="1"> <label>General</label> - <field id="hide_customer_ip" translate="label comment" type="select" showInDefault="1" showInWebsite="1" showInStore="1"> + <field id="hide_customer_ip" translate="label comment" type="select" showInDefault="1" showInWebsite="1" showInStore="1" canRestore="1"> <label>Hide Customer IP</label> <comment>Choose whether a customer IP is shown in orders, invoices, shipments, and credit memos.</comment> <source_model>Magento\Config\Model\Config\Source\Yesno</source_model> </field> </group> - <group id="totals_sort" translate="label" type="text" sortOrder="10" showInDefault="1" showInWebsite="1" showInStore="0"> + <group id="totals_sort" translate="label" type="text" sortOrder="10" showInDefault="1" showInWebsite="1"> <label>Checkout Totals Sort Order</label> - <field id="discount" translate="label" type="text" sortOrder="2" showInDefault="1" showInWebsite="1" showInStore="0" canRestore="1"> + <field id="discount" translate="label" type="text" sortOrder="2" showInDefault="1" showInWebsite="1" canRestore="1"> <label>Discount</label> <validate>required-number validate-number</validate> </field> - <field id="grand_total" translate="label" type="text" sortOrder="5" showInDefault="1" showInWebsite="1" showInStore="0" canRestore="1"> + <field id="grand_total" translate="label" type="text" sortOrder="5" showInDefault="1" showInWebsite="1" canRestore="1"> <label>Grand Total</label> <validate>required-number validate-number</validate> </field> - <field id="shipping" translate="label" type="text" sortOrder="3" showInDefault="1" showInWebsite="1" showInStore="0" canRestore="1"> + <field id="shipping" translate="label" type="text" sortOrder="3" showInDefault="1" showInWebsite="1" canRestore="1"> <label>Shipping</label> <validate>required-number validate-number</validate> </field> - <field id="subtotal" translate="label" type="text" sortOrder="1" showInDefault="1" showInWebsite="1" showInStore="0" canRestore="1"> + <field id="subtotal" translate="label" type="text" sortOrder="1" showInDefault="1" showInWebsite="1" canRestore="1"> <label>Subtotal</label> <validate>required-number validate-number</validate> </field> - <field id="tax" translate="label" type="text" sortOrder="4" showInDefault="1" showInWebsite="1" showInStore="0" canRestore="1"> + <field id="tax" translate="label" type="text" sortOrder="4" showInDefault="1" showInWebsite="1" canRestore="1"> <label>Tax</label> <validate>required-number validate-number</validate> </field> @@ -86,21 +86,21 @@ </group> <group id="minimum_order" translate="label" sortOrder="50" showInDefault="1" showInWebsite="1" showInStore="1"> <label>Minimum Order Amount</label> - <field id="active" translate="label" sortOrder="5" type="select" showInDefault="1" showInWebsite="1" showInStore="0"> + <field id="active" translate="label" sortOrder="5" type="select" showInDefault="1" showInWebsite="1"> <label>Enable</label> <source_model>Magento\Config\Model\Config\Source\Yesno</source_model> </field> - <field id="amount" translate="label comment" sortOrder="10" showInDefault="1" showInWebsite="1" showInStore="0"> + <field id="amount" translate="label comment" sortOrder="10" showInDefault="1" showInWebsite="1"> <label>Minimum Amount</label> <validate>validate-number validate-greater-than-zero</validate> <comment>Subtotal after discount.</comment> </field> - <field id="include_discount_amount" translate="label" sortOrder="12" type="select" showInDefault="1" showInWebsite="1" showInStore="0" canRestore="1"> + <field id="include_discount_amount" translate="label" sortOrder="12" type="select" showInDefault="1" showInWebsite="1" canRestore="1"> <label>Include Discount Amount</label> <source_model>Magento\Config\Model\Config\Source\Yesno</source_model> <comment>Choosing yes will be used subtotal after discount, otherwise only subtotal will be used.</comment> </field> - <field id="tax_including" translate="label" sortOrder="15" type="select" showInDefault="1" showInWebsite="1" showInStore="0" canRestore="1"> + <field id="tax_including" translate="label" sortOrder="15" type="select" showInDefault="1" showInWebsite="1" canRestore="1"> <label>Include Tax to Amount</label> <source_model>Magento\Config\Model\Config\Source\Yesno</source_model> </field> @@ -111,7 +111,7 @@ <field id="error_message" translate="label" sortOrder="30" type="textarea" showInDefault="1" showInWebsite="1" showInStore="1"> <label>Error to Show in Shopping Cart</label> </field> - <field id="multi_address" translate="label" sortOrder="40" type="select" showInDefault="1" showInWebsite="1" showInStore="0"> + <field id="multi_address" translate="label" sortOrder="40" type="select" showInDefault="1" showInWebsite="1"> <label>Validate Each Address Separately in Multi-address Checkout</label> <source_model>Magento\Config\Model\Config\Source\Yesno</source_model> </field> @@ -124,17 +124,17 @@ <comment>We'll use the default error above if you leave this empty.</comment> </field> </group> - <group id="dashboard" translate="label" sortOrder="60" showInDefault="1" showInWebsite="0" showInStore="0"> + <group id="dashboard" translate="label" sortOrder="60" showInDefault="1"> <label>Dashboard</label> - <field id="use_aggregated_data" translate="label comment" sortOrder="10" type="select" showInDefault="1" showInWebsite="0" showInStore="0" canRestore="1"> + <field id="use_aggregated_data" translate="label comment" sortOrder="10" type="select" showInDefault="1" canRestore="1"> <label>Use Aggregated Data</label> <source_model>Magento\Config\Model\Config\Source\Yesno</source_model> <comment>Improves dashboard performance but provides non-realtime data.</comment> </field> </group> - <group id="orders" translate="label" sortOrder="70" showInDefault="1" showInWebsite="1" showInStore="0"> + <group id="orders" translate="label" sortOrder="70" showInDefault="1" showInWebsite="1"> <label>Orders Cron Settings</label> - <field id="delete_pending_after" translate="label" type="text" sortOrder="6" showInDefault="1" showInWebsite="1" showInStore="0" canRestore="1"> + <field id="delete_pending_after" translate="label" type="text" sortOrder="6" showInDefault="1" showInWebsite="1" canRestore="1"> <label>Pending Payment Order Lifetime (minutes)</label> <validate>validate-number validate-greater-than-zero</validate> </field> @@ -144,14 +144,14 @@ <label>Sales Emails</label> <tab>sales</tab> <resource>Magento_Sales::sales_email</resource> - <group id="general" type="text" sortOrder="0" showInDefault="1" showInWebsite="0" showInStore="0"> + <group id="general" type="text" sortOrder="0" showInDefault="1"> <label>General Settings</label> - <field id="async_sending" translate="label" type="select" sortOrder="1" showInDefault="1" showInWebsite="0" showInStore="0" canRestore="1"> + <field id="async_sending" translate="label" type="select" sortOrder="1" showInDefault="1" canRestore="1"> <label>Asynchronous sending</label> <source_model>Magento\Config\Model\Config\Source\Enabledisable</source_model> <backend_model>Magento\Sales\Model\Config\Backend\Email\AsyncSending</backend_model> </field> - <field id="sending_limit" translate="label" type="text" sortOrder="2" showInDefault="1" showInWebsite="0" showInStore="0" canRestore="1"> + <field id="sending_limit" translate="label" type="text" sortOrder="2" showInDefault="1" canRestore="1"> <label>Limit per cron run</label> <comment>Limit how many entities (orders/shipments/etc) will be processed during one cron run.</comment> <validate>required-number validate-number validate-greater-than-zero</validate> @@ -169,25 +169,40 @@ <field id="identity" translate="label" type="select" sortOrder="1" showInDefault="1" showInWebsite="1" showInStore="1" canRestore="1"> <label>New Order Confirmation Email Sender</label> <source_model>Magento\Config\Model\Config\Source\Email\Identity</source_model> + <depends> + <field id="enabled">1</field> + </depends> </field> <field id="template" translate="label comment" type="select" sortOrder="2" showInDefault="1" showInWebsite="1" showInStore="1" canRestore="1"> <label>New Order Confirmation Template</label> <comment>Email template chosen based on theme fallback when "Default" option is selected.</comment> <source_model>Magento\Config\Model\Config\Source\Email\Template</source_model> + <depends> + <field id="enabled">1</field> + </depends> </field> <field id="guest_template" translate="label comment" type="select" sortOrder="3" showInDefault="1" showInWebsite="1" showInStore="1" canRestore="1"> <label>New Order Confirmation Template for Guest</label> <comment>Email template chosen based on theme fallback when "Default" option is selected.</comment> <source_model>Magento\Config\Model\Config\Source\Email\Template</source_model> + <depends> + <field id="enabled">1</field> + </depends> </field> <field id="copy_to" translate="label comment" type="text" sortOrder="4" showInDefault="1" showInWebsite="1" showInStore="1"> <label>Send Order Email Copy To</label> <comment>Comma-separated.</comment> <validate>validate-emails</validate> + <depends> + <field id="enabled">1</field> + </depends> </field> <field id="copy_method" translate="label" type="select" sortOrder="5" showInDefault="1" showInWebsite="1" showInStore="1" canRestore="1"> <label>Send Order Email Copy Method</label> <source_model>Magento\Config\Model\Config\Source\Email\Method</source_model> + <depends> + <field id="enabled">1</field> + </depends> </field> </group> <group id="order_comment" translate="label" type="text" sortOrder="2" showInDefault="1" showInWebsite="1" showInStore="1"> @@ -199,25 +214,40 @@ <field id="identity" translate="label" type="select" sortOrder="1" showInDefault="1" showInWebsite="1" showInStore="1" canRestore="1"> <label>Order Comment Email Sender</label> <source_model>Magento\Config\Model\Config\Source\Email\Identity</source_model> + <depends> + <field id="enabled">1</field> + </depends> </field> <field id="template" translate="label comment" type="select" sortOrder="2" showInDefault="1" showInWebsite="1" showInStore="1" canRestore="1"> <label>Order Comment Email Template</label> <comment>Email template chosen based on theme fallback when "Default" option is selected.</comment> <source_model>Magento\Config\Model\Config\Source\Email\Template</source_model> + <depends> + <field id="enabled">1</field> + </depends> </field> <field id="guest_template" translate="label comment" type="select" sortOrder="3" showInDefault="1" showInWebsite="1" showInStore="1" canRestore="1"> <label>Order Comment Email Template for Guest</label> <comment>Email template chosen based on theme fallback when "Default" option is selected.</comment> <source_model>Magento\Config\Model\Config\Source\Email\Template</source_model> + <depends> + <field id="enabled">1</field> + </depends> </field> <field id="copy_to" translate="label comment" type="text" sortOrder="4" showInDefault="1" showInWebsite="1" showInStore="1"> <label>Send Order Comment Email Copy To</label> <comment>Comma-separated.</comment> <validate>validate-emails</validate> + <depends> + <field id="enabled">1</field> + </depends> </field> <field id="copy_method" translate="label" type="select" sortOrder="5" showInDefault="1" showInWebsite="1" showInStore="1" canRestore="1"> <label>Send Order Comments Email Copy Method</label> <source_model>Magento\Config\Model\Config\Source\Email\Method</source_model> + <depends> + <field id="enabled">1</field> + </depends> </field> </group> <group id="invoice" translate="label" type="text" sortOrder="3" showInDefault="1" showInWebsite="1" showInStore="1"> @@ -229,25 +259,40 @@ <field id="identity" translate="label" type="select" sortOrder="1" showInDefault="1" showInWebsite="1" showInStore="1" canRestore="1"> <label>Invoice Email Sender</label> <source_model>Magento\Config\Model\Config\Source\Email\Identity</source_model> + <depends> + <field id="enabled">1</field> + </depends> </field> <field id="template" translate="label comment" type="select" sortOrder="2" showInDefault="1" showInWebsite="1" showInStore="1" canRestore="1"> <label>Invoice Email Template</label> <comment>Email template chosen based on theme fallback when "Default" option is selected.</comment> <source_model>Magento\Config\Model\Config\Source\Email\Template</source_model> + <depends> + <field id="enabled">1</field> + </depends> </field> <field id="guest_template" translate="label comment" type="select" sortOrder="3" showInDefault="1" showInWebsite="1" showInStore="1" canRestore="1"> <label>Invoice Email Template for Guest</label> <comment>Email template chosen based on theme fallback when "Default" option is selected.</comment> <source_model>Magento\Config\Model\Config\Source\Email\Template</source_model> + <depends> + <field id="enabled">1</field> + </depends> </field> <field id="copy_to" translate="label comment" type="text" sortOrder="4" showInDefault="1" showInWebsite="1" showInStore="1"> <label>Send Invoice Email Copy To</label> <comment>Comma-separated.</comment> <validate>validate-emails</validate> + <depends> + <field id="enabled">1</field> + </depends> </field> <field id="copy_method" translate="label" type="select" sortOrder="5" showInDefault="1" showInWebsite="1" showInStore="1" canRestore="1"> <label>Send Invoice Email Copy Method</label> <source_model>Magento\Config\Model\Config\Source\Email\Method</source_model> + <depends> + <field id="enabled">1</field> + </depends> </field> </group> <group id="invoice_comment" translate="label" type="text" sortOrder="4" showInDefault="1" showInWebsite="1" showInStore="1"> @@ -259,25 +304,40 @@ <field id="identity" translate="label" type="select" sortOrder="1" showInDefault="1" showInWebsite="1" showInStore="1" canRestore="1"> <label>Invoice Comment Email Sender</label> <source_model>Magento\Config\Model\Config\Source\Email\Identity</source_model> + <depends> + <field id="enabled">1</field> + </depends> </field> <field id="template" translate="label comment" type="select" sortOrder="2" showInDefault="1" showInWebsite="1" showInStore="1" canRestore="1"> <label>Invoice Comment Email Template</label> <comment>Email template chosen based on theme fallback when "Default" option is selected.</comment> <source_model>Magento\Config\Model\Config\Source\Email\Template</source_model> + <depends> + <field id="enabled">1</field> + </depends> </field> <field id="guest_template" translate="label comment" type="select" sortOrder="3" showInDefault="1" showInWebsite="1" showInStore="1" canRestore="1"> <label>Invoice Comment Email Template for Guest</label> <comment>Email template chosen based on theme fallback when "Default" option is selected.</comment> <source_model>Magento\Config\Model\Config\Source\Email\Template</source_model> + <depends> + <field id="enabled">1</field> + </depends> </field> <field id="copy_to" translate="label comment" type="text" sortOrder="4" showInDefault="1" showInWebsite="1" showInStore="1"> <label>Send Invoice Comment Email Copy To</label> <comment>Comma-separated.</comment> <validate>validate-emails</validate> + <depends> + <field id="enabled">1</field> + </depends> </field> <field id="copy_method" translate="label" type="select" sortOrder="5" showInDefault="1" showInWebsite="1" showInStore="1" canRestore="1"> <label>Send Invoice Comments Email Copy Method</label> <source_model>Magento\Config\Model\Config\Source\Email\Method</source_model> + <depends> + <field id="enabled">1</field> + </depends> </field> </group> <group id="shipment" translate="label" type="text" sortOrder="5" showInDefault="1" showInWebsite="1" showInStore="1"> @@ -289,25 +349,40 @@ <field id="identity" translate="label" type="select" sortOrder="1" showInDefault="1" showInWebsite="1" showInStore="1" canRestore="1"> <label>Shipment Email Sender</label> <source_model>Magento\Config\Model\Config\Source\Email\Identity</source_model> + <depends> + <field id="enabled">1</field> + </depends> </field> <field id="template" translate="label comment" type="select" sortOrder="2" showInDefault="1" showInWebsite="1" showInStore="1" canRestore="1"> <label>Shipment Email Template</label> <comment>Email template chosen based on theme fallback when "Default" option is selected.</comment> <source_model>Magento\Config\Model\Config\Source\Email\Template</source_model> + <depends> + <field id="enabled">1</field> + </depends> </field> <field id="guest_template" translate="label comment" type="select" sortOrder="3" showInDefault="1" showInWebsite="1" showInStore="1" canRestore="1"> <label>Shipment Email Template for Guest</label> <comment>Email template chosen based on theme fallback when "Default" option is selected.</comment> <source_model>Magento\Config\Model\Config\Source\Email\Template</source_model> + <depends> + <field id="enabled">1</field> + </depends> </field> <field id="copy_to" translate="label comment" type="text" sortOrder="4" showInDefault="1" showInWebsite="1" showInStore="1"> <label>Send Shipment Email Copy To</label> <comment>Comma-separated.</comment> <validate>validate-emails</validate> + <depends> + <field id="enabled">1</field> + </depends> </field> <field id="copy_method" translate="label" type="select" sortOrder="5" showInDefault="1" showInWebsite="1" showInStore="1" canRestore="1"> <label>Send Shipment Email Copy Method</label> <source_model>Magento\Config\Model\Config\Source\Email\Method</source_model> + <depends> + <field id="enabled">1</field> + </depends> </field> </group> <group id="shipment_comment" translate="label" type="text" sortOrder="6" showInDefault="1" showInWebsite="1" showInStore="1"> @@ -319,25 +394,40 @@ <field id="identity" translate="label" type="select" sortOrder="1" showInDefault="1" showInWebsite="1" showInStore="1" canRestore="1"> <label>Shipment Comment Email Sender</label> <source_model>Magento\Config\Model\Config\Source\Email\Identity</source_model> + <depends> + <field id="enabled">1</field> + </depends> </field> <field id="template" translate="label comment" type="select" sortOrder="2" showInDefault="1" showInWebsite="1" showInStore="1" canRestore="1"> <label>Shipment Comment Email Template</label> <comment>Email template chosen based on theme fallback when "Default" option is selected.</comment> <source_model>Magento\Config\Model\Config\Source\Email\Template</source_model> + <depends> + <field id="enabled">1</field> + </depends> </field> <field id="guest_template" translate="label comment" type="select" sortOrder="3" showInDefault="1" showInWebsite="1" showInStore="1" canRestore="1"> <label>Shipment Comment Email Template for Guest</label> <comment>Email template chosen based on theme fallback when "Default" option is selected.</comment> <source_model>Magento\Config\Model\Config\Source\Email\Template</source_model> + <depends> + <field id="enabled">1</field> + </depends> </field> <field id="copy_to" translate="label comment" type="text" sortOrder="4" showInDefault="1" showInWebsite="1" showInStore="1"> <label>Send Shipment Comment Email Copy To</label> <comment>Comma-separated.</comment> <validate>validate-emails</validate> + <depends> + <field id="enabled">1</field> + </depends> </field> <field id="copy_method" translate="label" type="select" sortOrder="5" showInDefault="1" showInWebsite="1" showInStore="1" canRestore="1"> <label>Send Shipment Comments Email Copy Method</label> <source_model>Magento\Config\Model\Config\Source\Email\Method</source_model> + <depends> + <field id="enabled">1</field> + </depends> </field> </group> <group id="creditmemo" translate="label" type="text" sortOrder="7" showInDefault="1" showInWebsite="1" showInStore="1"> @@ -349,25 +439,40 @@ <field id="identity" translate="label" type="select" sortOrder="1" showInDefault="1" showInWebsite="1" showInStore="1" canRestore="1"> <label>Credit Memo Email Sender</label> <source_model>Magento\Config\Model\Config\Source\Email\Identity</source_model> + <depends> + <field id="enabled">1</field> + </depends> </field> <field id="template" translate="label comment" type="select" sortOrder="2" showInDefault="1" showInWebsite="1" showInStore="1" canRestore="1"> <label>Credit Memo Email Template</label> <comment>Email template chosen based on theme fallback when "Default" option is selected.</comment> <source_model>Magento\Config\Model\Config\Source\Email\Template</source_model> + <depends> + <field id="enabled">1</field> + </depends> </field> <field id="guest_template" translate="label comment" type="select" sortOrder="3" showInDefault="1" showInWebsite="1" showInStore="1" canRestore="1"> <label>Credit Memo Email Template for Guest</label> <comment>Email template chosen based on theme fallback when "Default" option is selected.</comment> <source_model>Magento\Config\Model\Config\Source\Email\Template</source_model> + <depends> + <field id="enabled">1</field> + </depends> </field> <field id="copy_to" translate="label comment" type="text" sortOrder="4" showInDefault="1" showInWebsite="1" showInStore="1"> <label>Send Credit Memo Email Copy To</label> <comment>Comma-separated.</comment> <validate>validate-emails</validate> + <depends> + <field id="enabled">1</field> + </depends> </field> <field id="copy_method" translate="label" type="select" sortOrder="5" showInDefault="1" showInWebsite="1" showInStore="1" canRestore="1"> <label>Send Credit Memo Email Copy Method</label> <source_model>Magento\Config\Model\Config\Source\Email\Method</source_model> + <depends> + <field id="enabled">1</field> + </depends> </field> </group> <group id="creditmemo_comment" translate="label" type="text" sortOrder="8" showInDefault="1" showInWebsite="1" showInStore="1"> @@ -379,25 +484,40 @@ <field id="identity" translate="label" type="select" sortOrder="1" showInDefault="1" showInWebsite="1" showInStore="1" canRestore="1"> <label>Credit Memo Comment Email Sender</label> <source_model>Magento\Config\Model\Config\Source\Email\Identity</source_model> + <depends> + <field id="enabled">1</field> + </depends> </field> <field id="template" translate="label comment" type="select" sortOrder="2" showInDefault="1" showInWebsite="1" showInStore="1" canRestore="1"> <label>Credit Memo Comment Email Template</label> <comment>Email template chosen based on theme fallback when "Default" option is selected.</comment> <source_model>Magento\Config\Model\Config\Source\Email\Template</source_model> + <depends> + <field id="enabled">1</field> + </depends> </field> <field id="guest_template" translate="label comment" type="select" sortOrder="3" showInDefault="1" showInWebsite="1" showInStore="1" canRestore="1"> <label>Credit Memo Comment Email Template for Guest</label> <comment>Email template chosen based on theme fallback when "Default" option is selected.</comment> <source_model>Magento\Config\Model\Config\Source\Email\Template</source_model> + <depends> + <field id="enabled">1</field> + </depends> </field> <field id="copy_to" translate="label comment" type="text" sortOrder="4" showInDefault="1" showInWebsite="1" showInStore="1"> <label>Send Credit Memo Comment Email Copy To</label> <comment>Comma-separated.</comment> <validate>validate-emails</validate> + <depends> + <field id="enabled">1</field> + </depends> </field> <field id="copy_method" translate="label" type="select" sortOrder="5" showInDefault="1" showInWebsite="1" showInStore="1" canRestore="1"> <label>Send Credit Memo Comments Email Copy Method</label> <source_model>Magento\Config\Model\Config\Source\Email\Method</source_model> + <depends> + <field id="enabled">1</field> + </depends> </field> </group> </section> @@ -437,14 +557,24 @@ </group> </section> <section id="dev"> - <group id="grid" translate="label" type="text" sortOrder="131" showInDefault="1" showInWebsite="0" showInStore="0"> + <group id="grid" translate="label" type="text" sortOrder="131" showInDefault="1"> <label>Grid Settings</label> - <field id="async_indexing" translate="label" type="select" sortOrder="1" showInDefault="1" showInWebsite="0" showInStore="0" canRestore="1"> + <field id="async_indexing" translate="label" type="select" sortOrder="1" showInDefault="1" canRestore="1"> <label>Asynchronous indexing</label> <source_model>Magento\Config\Model\Config\Source\Enabledisable</source_model> <backend_model>Magento\Sales\Model\Config\Backend\Grid\AsyncIndexing</backend_model> </field> </group> </section> + <section id="customer"> + <group id="create_account" translate="label" sortOrder="20" showInDefault="1" showInWebsite="1" showInStore="1"> + <label>Create New Account Options</label> + <field id="email_required_create_order" translate="label comment" type="select" sortOrder="58" showInDefault="1" showInWebsite="1" showInStore="1" canRestore="1"> + <label>Email is required field for Admin order creation</label> + <comment>If set YES Email field will be required during Admin order creation for new Customer.</comment> + <source_model>Magento\Config\Model\Config\Source\Yesno</source_model> + </field> + </group> + </section> </system> </config> diff --git a/app/code/Magento/Sales/etc/config.xml b/app/code/Magento/Sales/etc/config.xml index 2480da4ad214b..5127735a329d6 100644 --- a/app/code/Magento/Sales/etc/config.xml +++ b/app/code/Magento/Sales/etc/config.xml @@ -8,6 +8,9 @@ <config xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="urn:magento:module:Magento_Store:etc/config.xsd"> <default> <sales> + <general> + <hide_customer_ip>0</hide_customer_ip> + </general> <totals_sort> <discount>20</discount> <grand_total>100</grand_total> @@ -111,5 +114,10 @@ <async_indexing>0</async_indexing> </grid> </dev> + <customer> + <create_account> + <email_required_create_order>1</email_required_create_order> + </create_account> + </customer> </default> </config> diff --git a/app/code/Magento/Sales/etc/di.xml b/app/code/Magento/Sales/etc/di.xml index f6618c9884d60..b4dadfa944a5b 100644 --- a/app/code/Magento/Sales/etc/di.xml +++ b/app/code/Magento/Sales/etc/di.xml @@ -819,14 +819,6 @@ </argument> </arguments> </type> - <type name="Magento\Framework\View\Element\UiComponent\DataProvider\FilterPool"> - <arguments> - <argument name="appliers" xsi:type="array"> - <item name="regular" xsi:type="object">Magento\Framework\View\Element\UiComponent\DataProvider\RegularFilter</item> - <item name="fulltext" xsi:type="object">Magento\Framework\View\Element\UiComponent\DataProvider\FulltextFilter</item> - </argument> - </arguments> - </type> <type name="Magento\Framework\View\Element\UiComponent\DataProvider\CollectionFactory"> <arguments> <argument name="collections" xsi:type="array"> @@ -840,6 +832,12 @@ </argument> </arguments> </type> + <virtualType name="Magento\Sales\Model\ResourceModel\Order\Invoice\Grid\Collection" type="Magento\Framework\View\Element\UiComponent\DataProvider\SearchResult"> + <arguments> + <argument name="mainTable" xsi:type="string">sales_invoice_grid</argument> + <argument name="resourceModel" xsi:type="string">Magento\Sales\Model\ResourceModel\Order\Invoice</argument> + </arguments> + </virtualType> <type name="Magento\Sales\Model\Order\Config"> <arguments> <argument name="state" xsi:type="object">Magento\Framework\App\State\Proxy</argument> @@ -943,6 +941,11 @@ <argument name="stateCommand" xsi:type="object">Magento\Sales\Model\Order\Payment\State\CaptureCommand</argument> </arguments> </type> + <type name="Magento\Sales\Model\Order\Payment\Operations\ProcessInvoiceOperation"> + <arguments> + <argument name="stateCommand" xsi:type="object">Magento\Sales\Model\Order\Payment\State\CaptureCommand</argument> + </arguments> + </type> <type name="Magento\Sales\Model\Order\Payment\Operations\OrderOperation"> <arguments> <argument name="stateCommand" xsi:type="object">Magento\Sales\Model\Order\Payment\State\OrderCommand</argument> @@ -1008,9 +1011,6 @@ <item name="sales_email/shipment/copy_to" xsi:type="string">1</item> <item name="sales_email/shipment_comment/copy_to" xsi:type="string">1</item> </argument> - <argument name="environment" xsi:type="array"> - <item name="customer/create_account/email_domain" xsi:type="string">1</item> - </argument> </arguments> </type> <preference diff --git a/app/code/Magento/Sales/etc/extension_attributes.xml b/app/code/Magento/Sales/etc/extension_attributes.xml index 222f61cdc7324..08e295cb6721c 100644 --- a/app/code/Magento/Sales/etc/extension_attributes.xml +++ b/app/code/Magento/Sales/etc/extension_attributes.xml @@ -13,4 +13,7 @@ <extension_attributes for="Magento\Sales\Api\Data\OrderInterface"> <attribute code="payment_additional_info" type="Magento\Payment\Api\Data\PaymentAdditionalInfoInterface[]" /> </extension_attributes> + <extension_attributes for="Magento\Sales\Api\Data\OrderPaymentInterface"> + <attribute code="notification_message" type="string" /> + </extension_attributes> </config> diff --git a/app/code/Magento/Sales/etc/webapi.xml b/app/code/Magento/Sales/etc/webapi.xml index 492dff8057039..63d7c24ecd16f 100644 --- a/app/code/Magento/Sales/etc/webapi.xml +++ b/app/code/Magento/Sales/etc/webapi.xml @@ -253,7 +253,7 @@ <resource ref="Magento_Sales::ship" /> </resources> </route> - <route url="/V1/orders/" method="POST"> + <route url="/V1/orders" method="POST"> <service class="Magento\Sales\Api\OrderRepositoryInterface" method="save"/> <resources> <resource ref="Magento_Sales::create" /> diff --git a/app/code/Magento/Sales/i18n/en_US.csv b/app/code/Magento/Sales/i18n/en_US.csv index f30315437533f..44626027bac69 100644 --- a/app/code/Magento/Sales/i18n/en_US.csv +++ b/app/code/Magento/Sales/i18n/en_US.csv @@ -797,5 +797,8 @@ Created,Created Refunds,Refunds "Allow Zero GrandTotal for Creditmemo","Allow Zero GrandTotal for Creditmemo" "Allow Zero GrandTotal","Allow Zero GrandTotal" +Email is required field for Admin order creation,Email is required field for Admin order creation +If set YES Email field will be required during Admin order creation for new Customer.,If set YES Email field will be required during Admin order creation for new Customer. "Could not save the shipment tracking","Could not save the shipment tracking" "Please enter a coupon code!","Please enter a coupon code!" +"Reorder is not available.","Reorder is not available." diff --git a/app/code/Magento/Sales/registration.php b/app/code/Magento/Sales/registration.php index db3b2aebce0bb..6945521f30abe 100644 --- a/app/code/Magento/Sales/registration.php +++ b/app/code/Magento/Sales/registration.php @@ -4,6 +4,6 @@ * See COPYING.txt for license details. */ -use \Magento\Framework\Component\ComponentRegistrar; +use Magento\Framework\Component\ComponentRegistrar; ComponentRegistrar::register(ComponentRegistrar::MODULE, 'Magento_Sales', __DIR__); diff --git a/app/code/Magento/Sales/view/adminhtml/templates/order/creditmemo/create/totals/adjustments.phtml b/app/code/Magento/Sales/view/adminhtml/templates/order/creditmemo/create/totals/adjustments.phtml index f46b8c11cc240..fc624bfd803b6 100644 --- a/app/code/Magento/Sales/view/adminhtml/templates/order/creditmemo/create/totals/adjustments.phtml +++ b/app/code/Magento/Sales/view/adminhtml/templates/order/creditmemo/create/totals/adjustments.phtml @@ -5,33 +5,42 @@ */ ?> <?php $_source = $block->getSource() ?> -<?php if ($_source) : ?> +<?php if ($_source): ?> <tr> - <td class="label"><?= $block->escapeHtml($block->getShippingLabel()) ?><div id="shipping_amount_adv"></div></td> + <td class="label"> + <?= $block->escapeHtml($block->getShippingLabel()) ?> + <div id="shipping_amount_adv"></div> + </td> <td> <input type="text" name="creditmemo[shipping_amount]" - value="<?= $block->escapeHtmlAttr($block->getShippingAmount()) ?>" + value="<?= /* @noEscape */ $block->formatValue($block->getShippingAmount()) ?>" class="input-text admin__control-text not-negative-amount" id="shipping_amount" /> </td> </tr> <tr> - <td class="label"><?= $block->escapeHtml(__('Adjustment Refund')) ?><div id="adjustment_positive_adv"></div></td> + <td class="label"> + <?= $block->escapeHtml(__('Adjustment Refund')) ?> + <div id="adjustment_positive_adv"></div> + </td> <td> <input type="text" name="creditmemo[adjustment_positive]" - value="<?= $block->escapeHtmlAttr($_source->getBaseAdjustmentPositive()) ?>" + value="<?= /* @noEscape */ $block->formatValue($_source->getBaseAdjustmentPositive()) ?>" class="input-text admin__control-text not-negative-amount" id="adjustment_positive" /> </td> </tr> <tr> - <td class="label"><?= $block->escapeHtml(__('Adjustment Fee')) ?><div id="adjustment_negative_adv"></div></td> + <td class="label"> + <?= $block->escapeHtml(__('Adjustment Fee')) ?> + <div id="adjustment_negative_adv"></div> + </td> <td> <input type="text" name="creditmemo[adjustment_negative]" - value="<?= $block->escapeHtmlAttr($_source->getBaseAdjustmentNegative()) ?>" + value="<?= /* @noEscape */ $block->formatValue($_source->getBaseAdjustmentNegative()) ?>" class="input-text admin__control-text not-negative-amount" id="adjustment_negative"/> <script> @@ -39,12 +48,16 @@ //<![CDATA[ Validation.addAllThese([ - ['not-negative-amount', '<?= $block->escapeJs(__('Please enter a positive number in this field.')) ?>', function(v) { - if(v.length) - return /^\s*\d+([,.]\d+)*\s*%?\s*$/.test(v); - else - return true; - }] + [ + 'not-negative-amount', + '<?= $block->escapeJs(__('Please enter a positive number in this field.')) ?>', + function (v) { + if (v.length) + return /^\s*\d+([,.]\d+)*\s*%?\s*$/.test(v); + else + return true; + } + ] ]); if ($('shipping_amount')) { diff --git a/app/code/Magento/Sales/view/adminhtml/web/order/create/scripts.js b/app/code/Magento/Sales/view/adminhtml/web/order/create/scripts.js index 8803e2ea48f02..e138112ac3f5a 100644 --- a/app/code/Magento/Sales/view/adminhtml/web/order/create/scripts.js +++ b/app/code/Magento/Sales/view/adminhtml/web/order/create/scripts.js @@ -19,17 +19,17 @@ define([ window.AdminOrder = new Class.create(); AdminOrder.prototype = { - initialize : function(data){ - if(!data) data = {}; - this.loadBaseUrl = false; - this.customerId = data.customer_id ? data.customer_id : false; - this.storeId = data.store_id ? data.store_id : false; - this.quoteId = data['quote_id'] ? data['quote_id'] : false; - this.currencyId = false; + initialize: function (data) { + if (!data) data = {}; + this.loadBaseUrl = false; + this.customerId = data.customer_id ? data.customer_id : false; + this.storeId = data.store_id ? data.store_id : false; + this.quoteId = data['quote_id'] ? data['quote_id'] : false; + this.currencyId = false; this.currencySymbol = data.currency_symbol ? data.currency_symbol : ''; - this.addresses = data.addresses ? data.addresses : $H({}); + this.addresses = data.addresses ? data.addresses : $H({}); this.shippingAsBilling = data.shippingAsBilling ? data.shippingAsBilling : false; - this.gridProducts = $H({}); + this.gridProducts = $H({}); this.gridProductsGift = $H({}); this.billingAddressContainer = ''; this.shippingAddressContainer = ''; @@ -56,10 +56,10 @@ define([ } }); - jQuery.async('#order-items', (function(){ + jQuery.async('#order-items', (function () { this.dataArea = new OrderFormArea('data', $(this.getAreaId('data')), this); this.itemsArea = Object.extend(new OrderFormArea('items', $(this.getAreaId('items')), this), { - addControlButton: function(button){ + addControlButton: function (button) { var controlButtonArea = $(this.node).select('.actions')[0]; if (typeof controlButtonArea != 'undefined') { var buttons = controlButtonArea.childElements(); @@ -76,7 +76,7 @@ define([ var searchButtonId = 'add_products', searchButton = new ControlButton(jQuery.mage.__('Add Products'), searchButtonId), searchAreaId = this.getAreaId('search'); - searchButton.onClick = function() { + searchButton.onClick = function () { $(searchAreaId).show(); var el = this; window.setTimeout(function () { @@ -85,13 +85,13 @@ define([ }; if (jQuery('#' + this.getAreaId('items')).is(':visible')) { - this.dataArea.onLoad = this.dataArea.onLoad.wrap(function(proceed) { + this.dataArea.onLoad = this.dataArea.onLoad.wrap(function (proceed) { proceed(); this._parent.itemsArea.setNode($(this._parent.getAreaId('items'))); this._parent.itemsArea.onLoad(); }); - this.itemsArea.onLoad = this.itemsArea.onLoad.wrap(function(proceed) { + this.itemsArea.onLoad = this.itemsArea.onLoad.wrap(function (proceed) { proceed(); if ($(searchAreaId) && !$(searchAreaId).visible() && !$(searchButtonId)) { this.addControlButton(searchButton); @@ -103,35 +103,35 @@ define([ }).bind(this)); jQuery('#edit_form') - .on('submitOrder', function(){ + .on('submitOrder', function () { jQuery(this).trigger('realOrder'); }) .on('realOrder', this._realSubmit.bind(this)); }, - areasLoaded: function(){ + areasLoaded: function () { }, - itemsLoaded: function(){ + itemsLoaded: function () { }, - dataLoaded: function(){ + dataLoaded: function () { this.dataShow(); }, - setLoadBaseUrl : function(url){ + setLoadBaseUrl: function (url) { this.loadBaseUrl = url; }, - setAddresses : function(addresses){ + setAddresses: function (addresses) { this.addresses = addresses; }, - addExcludedPaymentMethod : function(method){ + addExcludedPaymentMethod: function (method) { this.excludedPaymentMethods.push(method); }, - setCustomerId : function(id){ + setCustomerId: function (id) { this.customerId = id; this.loadArea('header', true); $(this.getAreaId('header')).callback = 'setCustomerAfter'; @@ -139,18 +139,17 @@ define([ $('reset_order_top_button').show(); }, - setCustomerAfter : function () { + setCustomerAfter: function () { this.customerSelectorHide(); if (this.storeId) { $(this.getAreaId('data')).callback = 'dataLoaded'; this.loadArea(['data'], true); - } - else { + } else { this.storeSelectorShow(); } }, - setStoreId : function(id){ + setStoreId: function (id) { this.storeId = id; this.storeSelectorHide(); this.sidebarShow(); @@ -159,19 +158,18 @@ define([ this.loadArea(['header', 'data'], true); }, - setCurrencyId : function(id){ + setCurrencyId: function (id) { this.currencyId = id; //this.loadArea(['sidebar', 'data'], true); this.loadArea(['data'], true); }, - setCurrencySymbol : function(symbol){ + setCurrencySymbol: function (symbol) { this.currencySymbol = symbol; }, - selectAddress : function(el, container){ - - id = el.value; + selectAddress: function (el, container) { + var id = el.value; if (id.length == 0) { id = '0'; } @@ -190,10 +188,9 @@ define([ this.resetPaymentMethod(); if (this.isShippingField(container) && !this.isShippingMethodReseted) { this.resetShippingMethod(data); - } else{ + } else { this.saveData(data); } - }, /** @@ -325,13 +322,13 @@ define([ }); }, - fillAddressFields: function(container, data){ + fillAddressFields: function (container, data) { var regionIdElem = false; var regionIdElemValue = false; var fields = $(container).select('input', 'select', 'textarea'); var re = /[^\[]*\[[^\]]*\]\[([^\]]*)\](\[(\d)\])?/; - for(var i=0;i<fields.length;i++){ + for (var i = 0; i < fields.length; i++) { // skip input type file @Security error code: 1000 if (fields[i].tagName.toLowerCase() == 'input' && fields[i].type.toLowerCase() == 'file') { continue; @@ -343,9 +340,9 @@ define([ var name = matchRes[1]; var index = matchRes[3]; - if (index){ + if (index) { // multiply line - if (data[name]){ + if (data[name]) { var values = data[name].split("\n"); fields[i].value = values[index] ? values[index] : ''; } else { @@ -370,7 +367,7 @@ define([ fields[i].changeUpdater(); } - if (name == 'region' && data['region_id'] && !data['region']){ + if (name == 'region' && data['region_id'] && !data['region']) { fields[i].value = data['region_id']; } @@ -378,7 +375,7 @@ define([ } }, - disableShippingAddress : function(flag) { + disableShippingAddress: function (flag) { this.shippingAsBilling = flag; if ($('order-shipping_address_customer_address_id')) { $('order-shipping_address_customer_address_id').disabled = flag; @@ -388,7 +385,7 @@ define([ for (var i = 0; i < dataFields.length; i++) { dataFields[i].disabled = flag; - if(this.isOnlyVirtualProduct) { + if (this.isOnlyVirtualProduct) { dataFields[i].setValue(''); } } @@ -456,7 +453,7 @@ define([ return false; }, - setShippingMethod: function(method) { + setShippingMethod: function (method) { var data = {}; data['order[shipping_method]'] = method; @@ -473,10 +470,10 @@ define([ * * @return boolean */ - loadPaymentMethods: function() { + loadPaymentMethods: function () { var data = this.serializeData(this.billingAddressContainer).toObject(); - this.loadArea(['billing_method','totals'], true, data); + this.loadArea(['billing_method', 'totals'], true, data); return false; }, @@ -489,35 +486,35 @@ define([ this.loadArea(['card_validation'], true, data); }, - setPaymentMethod : function(method){ - if (this.paymentMethod && $('payment_form_'+this.paymentMethod)) { - var form = 'payment_form_'+this.paymentMethod; - [form + '_before', form, form + '_after'].each(function(el) { + setPaymentMethod: function (method) { + if (this.paymentMethod && $('payment_form_' + this.paymentMethod)) { + var form = 'payment_form_' + this.paymentMethod; + [form + '_before', form, form + '_after'].each(function (el) { var block = $(el); if (block) { block.hide(); - block.select('input', 'select', 'textarea').each(function(field) { + block.select('input', 'select', 'textarea').each(function (field) { field.disabled = true; }); } }); } - if(!this.paymentMethod || method){ - $('order-billing_method_form').select('input', 'select', 'textarea').each(function(elem){ - if(elem.type != 'radio') elem.disabled = true; + if (!this.paymentMethod || method) { + $('order-billing_method_form').select('input', 'select', 'textarea').each(function (elem) { + if (elem.type != 'radio') elem.disabled = true; }) } - if ($('payment_form_'+method)){ + if ($('payment_form_' + method)) { jQuery('#' + this.getAreaId('billing_method')).trigger('contentUpdated'); this.paymentMethod = method; - var form = 'payment_form_'+method; - [form + '_before', form, form + '_after'].each(function(el) { + var form = 'payment_form_' + method; + [form + '_before', form, form + '_after'].each(function (el) { var block = $(el); if (block) { block.show(); - block.select('input', 'select', 'textarea').each(function(field) { + block.select('input', 'select', 'textarea').each(function (field) { field.disabled = false; if (!el.include('_before') && !el.include('_after') && !field.bindChange) { field.bindChange = true; @@ -525,15 +522,15 @@ define([ field.method = method; field.observe('change', this.changePaymentData.bind(this)) } - },this); + }, this); } - },this); + }, this); } }, - changePaymentData : function(event){ + changePaymentData: function (event) { var elem = Event.element(event); - if(elem && elem.method){ + if (elem && elem.method) { var data = this.getPaymentData(elem.method); if (data) { this.loadArea(['card_validation'], true, data); @@ -543,8 +540,8 @@ define([ } }, - getPaymentData : function(currentMethod){ - if (typeof(currentMethod) == 'undefined') { + getPaymentData: function (currentMethod) { + if (typeof (currentMethod) == 'undefined') { if (this.paymentMethod) { currentMethod = this.paymentMethod; } else { @@ -556,7 +553,7 @@ define([ } var data = {}; var fields = $('payment_form_' + currentMethod).select('input', 'select'); - for(var i=0;i<fields.length;i++){ + for (var i = 0; i < fields.length; i++) { data[fields[i].name] = fields[i].getValue(); } if ((typeof data['payment[cc_type]']) != 'undefined' && (!data['payment[cc_type]'] || !data['payment[cc_number]'])) { @@ -565,35 +562,41 @@ define([ return data; }, - applyCoupon : function(code){ - this.loadArea(['items', 'shipping_method', 'totals', 'billing_method'], true, {'order[coupon][code]':code, reset_shipping: 0}); + applyCoupon: function (code) { + this.loadArea(['items', 'shipping_method', 'totals', 'billing_method'], true, { + 'order[coupon][code]': code, + reset_shipping: 0 + }); this.orderItemChanged = false; jQuery('html, body').animate({ scrollTop: 0 }); }, - addProduct : function(id){ - this.loadArea(['items', 'shipping_method', 'totals', 'billing_method'], true, {add_product:id, reset_shipping: true}); + addProduct: function (id) { + this.loadArea(['items', 'shipping_method', 'totals', 'billing_method'], true, { + add_product: id, + reset_shipping: true + }); }, - removeQuoteItem : function(id){ + removeQuoteItem: function (id) { this.loadArea(['items', 'shipping_method', 'totals', 'billing_method'], true, - {remove_item:id, from:'quote',reset_shipping: true}); + {remove_item: id, from: 'quote', reset_shipping: true}); }, - moveQuoteItem : function(id, to){ - this.loadArea(['sidebar_'+to, 'items', 'shipping_method', 'totals', 'billing_method'], this.getAreaId('items'), - {move_item:id, to:to, reset_shipping: true}); + moveQuoteItem: function (id, to) { + this.loadArea(['sidebar_' + to, 'items', 'shipping_method', 'totals', 'billing_method'], this.getAreaId('items'), + {move_item: id, to: to, reset_shipping: true}); }, - productGridShow : function(buttonElement){ + productGridShow: function (buttonElement) { this.productGridShowButton = buttonElement; Element.hide(buttonElement); this.showArea('search'); }, - productGridRowInit : function(grid, row){ + productGridRowInit: function (grid, row) { var checkbox = $(row).select('.checkbox')[0]; var inputs = $(row).select('.input-text'); if (checkbox && inputs.length > 0) { @@ -616,29 +619,39 @@ define([ input.disabled = !checkbox.checked || input.hasClassName('input-inactive'); - Event.observe(input,'keyup', this.productGridRowInputChange.bind(this)); - Event.observe(input,'change',this.productGridRowInputChange.bind(this)); + Event.observe(input, 'keyup', this.productGridRowInputChange.bind(this)); + Event.observe(input, 'change', this.productGridRowInputChange.bind(this)); } } }, - productGridRowInputChange : function(event){ + productGridRowInputChange: function (event) { var element = Event.element(event); - if (element && element.checkboxElement && element.checkboxElement.checked){ - if (element.name!='giftmessage' || element.checked) { + if (element && element.checkboxElement && element.checkboxElement.checked) { + if (element.name != 'giftmessage' || element.checked) { this.gridProducts.get(element.checkboxElement.value)[element.name] = element.value; - } else if (element.name=='giftmessage' && this.gridProducts.get(element.checkboxElement.value)[element.name]) { - delete(this.gridProducts.get(element.checkboxElement.value)[element.name]); + } else if (element.name == 'giftmessage' && this.gridProducts.get(element.checkboxElement.value)[element.name]) { + delete (this.gridProducts.get(element.checkboxElement.value)[element.name]); } } }, - productGridRowClick : function(grid, event){ + productGridRowClick: function (grid, event) { var trElement = Event.findElement(event, 'tr'); var qtyElement = trElement.select('input[name="qty"]')[0]; var eventElement = Event.element(event); - var isInputCheckbox = eventElement.tagName == 'INPUT' && eventElement.type == 'checkbox'; - var isInputQty = eventElement.tagName == 'INPUT' && eventElement.name == 'qty'; + + if (eventElement.tagName === 'LABEL' + && trElement.querySelector('#' + eventElement.htmlFor) + && trElement.querySelector('#' + eventElement.htmlFor).type === 'checkbox' + ) { + event.stopPropagation(); + trElement.querySelector('#' + eventElement.htmlFor).trigger('click'); + return; + } + + var isInputCheckbox = (eventElement.tagName === 'INPUT' && eventElement.type === 'checkbox'); + var isInputQty = grid.targetElement && grid.targetElement.tagName === 'INPUT' && grid.targetElement.name === 'qty'; if (trElement && !isInputQty) { var checkbox = Element.select(trElement, 'input[type="checkbox"]')[0]; var confLink = Element.select(trElement, 'a')[0]; @@ -660,10 +673,10 @@ define([ if (!priceBase) { this.productPriceBase[productId] = 0; } else { - this.productPriceBase[productId] = parseFloat(priceBase[1].replace(/,/g,'')); + this.productPriceBase[productId] = parseFloat(priceBase[1].replace(/,/g, '')); } } - productConfigure.setConfirmCallback(listType, function() { + productConfigure.setConfirmCallback(listType, function () { // sync qty of popup and qty of grid var confirmedCurrentQty = productConfigure.getCurrentConfirmedQtyElement(); if (qtyElement && confirmedCurrentQty && !isNaN(confirmedCurrentQty.value)) { @@ -679,12 +692,12 @@ define([ // and set checkbox checked grid.setCheckboxChecked(checkbox, true); }.bind(this)); - productConfigure.setCancelCallback(listType, function() { + productConfigure.setCancelCallback(listType, function () { if (!$(productConfigure.confirmedCurrentId) || !$(productConfigure.confirmedCurrentId).innerHTML) { grid.setCheckboxChecked(checkbox, false); } }); - productConfigure.setShowWindowCallback(listType, function() { + productConfigure.setShowWindowCallback(listType, function () { // sync qty of grid and qty of popup var formCurrentQty = productConfigure.getCurrentFormQtyElement(); if (formCurrentQty && qtyElement && !isNaN(qtyElement.value)) { @@ -700,7 +713,7 @@ define([ /** * Is need to summarize price */ - _isSummarizePrice: function(elm) { + _isSummarizePrice: function (elm) { if (elm && elm.hasAttribute('summarizePrice')) { this.summarizePrice = parseInt(elm.readAttribute('summarizePrice')); } @@ -727,9 +740,9 @@ define([ } return 0; }; - for(var i = 0; i < elms.length; i++) { + for (var i = 0; i < elms.length; i++) { if (elms[i].type == 'select-one' || elms[i].type == 'select-multiple') { - for(var ii = 0; ii < elms[i].options.length; ii++) { + for (var ii = 0; ii < elms[i].options.length; ii++) { if (elms[i].options[ii].selected) { if (this._isSummarizePrice(elms[i].options[ii])) { productPrice += getPrice(elms[i].options[ii]); @@ -738,8 +751,7 @@ define([ } } } - } - else if (((elms[i].type == 'checkbox' || elms[i].type == 'radio') && elms[i].checked) + } else if (((elms[i].type == 'checkbox' || elms[i].type == 'radio') && elms[i].checked) || ((elms[i].type == 'file' || elms[i].type == 'text' || elms[i].type == 'textarea' || elms[i].type == 'hidden') && Form.Element.getValue(elms[i])) ) { @@ -758,9 +770,9 @@ define([ return productPrice; }, - productGridCheckboxCheck : function(grid, element, checked){ + productGridCheckboxCheck: function (grid, element, checked) { if (checked) { - if(element.inputElements) { + if (element.inputElements) { this.gridProducts.set(element.value, {}); var product = this.gridProducts.get(element.value); for (var i = 0; i < element.inputElements.length; i++) { @@ -775,19 +787,19 @@ define([ if (input.checked || input.name != 'giftmessage') { product[input.name] = input.value; } else if (product[input.name]) { - delete(product[input.name]); + delete (product[input.name]); } } } } else { - if(element.inputElements){ - for(var i = 0; i < element.inputElements.length; i++) { + if (element.inputElements) { + for (var i = 0; i < element.inputElements.length; i++) { element.inputElements[i].disabled = true; } } this.gridProducts.unset(element.value); } - grid.reloadParams = {'products[]':this.gridProducts.keys()}; + grid.reloadParams = {'products[]': this.gridProducts.keys()}; }, productGridFilterKeyPress: function (grid, event) { @@ -807,18 +819,18 @@ define([ /** * Submit configured products to quote */ - productGridAddSelected : function(){ - if(this.productGridShowButton) Element.show(this.productGridShowButton); - var area = ['search', 'items', 'shipping_method', 'totals', 'giftmessage','billing_method']; + productGridAddSelected: function () { + if (this.productGridShowButton) Element.show(this.productGridShowButton); + var area = ['search', 'items', 'shipping_method', 'totals', 'giftmessage', 'billing_method']; // prepare additional fields and filtered items of products var fieldsPrepare = {}; var itemsFilter = []; var products = this.gridProducts.toObject(); for (var productId in products) { itemsFilter.push(productId); - var paramKey = 'item['+productId+']'; + var paramKey = 'item[' + productId + ']'; for (var productParamKey in products[productId]) { - paramKey += '['+productParamKey+']'; + paramKey += '[' + productParamKey + ']'; fieldsPrepare[paramKey] = products[productId][productParamKey]; } } @@ -828,47 +840,47 @@ define([ this.gridProducts = $H({}); }, - selectCustomer : function(grid, event){ + selectCustomer: function (grid, event) { var element = Event.findElement(event, 'tr'); - if (element.title){ + if (element.title) { this.setCustomerId(element.title); } }, - customerSelectorHide : function(){ + customerSelectorHide: function () { this.hideArea('customer-selector'); }, - customerSelectorShow : function(){ + customerSelectorShow: function () { this.showArea('customer-selector'); }, - storeSelectorHide : function(){ + storeSelectorHide: function () { this.hideArea('store-selector'); }, - storeSelectorShow : function(){ + storeSelectorShow: function () { this.showArea('store-selector'); }, - dataHide : function(){ + dataHide: function () { this.hideArea('data'); }, - dataShow : function(){ + dataShow: function () { if ($('submit_order_top_button')) { $('submit_order_top_button').show(); } this.showArea('data'); }, - clearShoppingCart : function(confirmMessage){ + clearShoppingCart: function (confirmMessage) { var self = this; confirm({ content: confirmMessage, actions: { - confirm: function() { + confirm: function () { self.collectElementsValue = false; order.sidebarApplyChanges({'sidebar[empty_customer_cart]': 1}); self.collectElementsValue = true; @@ -877,12 +889,12 @@ define([ }); }, - sidebarApplyChanges : function(auxiliaryParams) { + sidebarApplyChanges: function (auxiliaryParams) { if ($(this.getAreaId('sidebar'))) { var data = {}; if (this.collectElementsValue) { var elems = $(this.getAreaId('sidebar')).select('input'); - for (var i=0; i < elems.length; i++) { + for (var i = 0; i < elems.length; i++) { if (elems[i].getValue()) { data[elems[i].name] = elems[i].getValue(); } @@ -894,20 +906,20 @@ define([ } } data.reset_shipping = true; - this.loadArea(['sidebar', 'items', 'shipping_method', 'billing_method','totals', 'giftmessage'], true, data); + this.loadArea(['sidebar', 'items', 'shipping_method', 'billing_method', 'totals', 'giftmessage'], true, data); } }, - sidebarHide : function(){ - if(this.storeId === false && $('page:left') && $('page:container')){ + sidebarHide: function () { + if (this.storeId === false && $('page:left') && $('page:container')) { $('page:left').hide(); $('page:container').removeClassName('container'); $('page:container').addClassName('container-collapsed'); } }, - sidebarShow : function(){ - if($('page:left') && $('page:container')){ + sidebarShow: function () { + if ($('page:left') && $('page:container')) { $('page:left').show(); $('page:container').removeClassName('container-collapsed'); $('page:container').addClassName('container'); @@ -928,7 +940,7 @@ define([ for (var i in params) { if (params[i] === null) { unset(params[i]); - } else if (typeof(params[i]) == 'boolean') { + } else if (typeof (params[i]) == 'boolean') { params[i] = params[i] ? 1 : 0; } } @@ -937,15 +949,15 @@ define([ fields.push(new Element('input', {type: 'hidden', name: name, value: params[name]})); } // add additional fields before triggered submit - productConfigure.setBeforeSubmitCallback(listType, function() { + productConfigure.setBeforeSubmitCallback(listType, function () { productConfigure.addFields(fields); }.bind(this)); // response handler - productConfigure.setOnLoadIFrameCallback(listType, function(response) { + productConfigure.setOnLoadIFrameCallback(listType, function (response) { if (!response.ok) { return; } - this.loadArea(['items', 'shipping_method', 'billing_method','totals', 'giftmessage'], true); + this.loadArea(['items', 'shipping_method', 'billing_method', 'totals', 'giftmessage'], true); }.bind(this)); // show item configuration itemId = itemId ? itemId : productId; @@ -953,17 +965,17 @@ define([ return false; }, - removeSidebarItem : function(id, from){ - this.loadArea(['sidebar_'+from], 'sidebar_data_'+from, {remove_item:id, from:from}); + removeSidebarItem: function (id, from) { + this.loadArea(['sidebar_' + from], 'sidebar_data_' + from, {remove_item: id, from: from}); }, - itemsUpdate : function(){ - var area = ['sidebar', 'items', 'shipping_method', 'billing_method','totals', 'giftmessage']; + itemsUpdate: function () { + var area = ['sidebar', 'items', 'shipping_method', 'billing_method', 'totals', 'giftmessage']; // prepare additional fields var fieldsPrepare = {update_items: 1}; var info = $('order-items_grid').select('input', 'select', 'textarea'); - for(var i=0; i<info.length; i++){ - if(!info[i].disabled && (info[i].type != 'checkbox' || info[i].checked)) { + for (var i = 0; i < info.length; i++) { + if (!info[i].disabled && (info[i].type != 'checkbox' || info[i].checked)) { fieldsPrepare[info[i].name] = info[i].getValue(); } } @@ -972,17 +984,17 @@ define([ this.orderItemChanged = false; }, - itemsOnchangeBind : function(){ + itemsOnchangeBind: function () { var elems = $('order-items_grid').select('input', 'select', 'textarea'); - for(var i=0; i<elems.length; i++){ - if(!elems[i].bindOnchange){ + for (var i = 0; i < elems.length; i++) { + if (!elems[i].bindOnchange) { elems[i].bindOnchange = true; elems[i].observe('change', this.itemChange.bind(this)) } } }, - itemChange : function(event){ + itemChange: function (event) { this.giftmessageOnItemChange(event); this.orderItemChanged = true; }, @@ -995,7 +1007,7 @@ define([ * @param fieldsPrepare * @param itemsFilter */ - productConfigureSubmit : function(listType, area, fieldsPrepare, itemsFilter) { + productConfigureSubmit: function (listType, area, fieldsPrepare, itemsFilter) { // prepare loading areas and build url area = this.prepareArea(area); this.loadingAreas = area; @@ -1020,7 +1032,7 @@ define([ // prepare and do submit productConfigure.addListType(listType, {urlSubmit: url}); - productConfigure.setOnLoadIFrameCallback(listType, function(response){ + productConfigure.setOnLoadIFrameCallback(listType, function (response) { this.loadAreaResponseHandler(response); }.bind(this)); productConfigure.submit(listType); @@ -1033,20 +1045,20 @@ define([ * * @param itemId */ - showQuoteItemConfiguration: function(itemId){ + showQuoteItemConfiguration: function (itemId) { var listType = 'quote_items'; - var qtyElement = $('order-items_grid').select('input[name="item\['+itemId+'\]\[qty\]"]')[0]; - productConfigure.setConfirmCallback(listType, function() { + var qtyElement = $('order-items_grid').select('input[name="item\[' + itemId + '\]\[qty\]"]')[0]; + productConfigure.setConfirmCallback(listType, function () { // sync qty of popup and qty of grid var confirmedCurrentQty = productConfigure.getCurrentConfirmedQtyElement(); if (qtyElement && confirmedCurrentQty && !isNaN(confirmedCurrentQty.value)) { qtyElement.value = confirmedCurrentQty.value; } - this.productConfigureAddFields['item['+itemId+'][configured]'] = 1; + this.productConfigureAddFields['item[' + itemId + '][configured]'] = 1; this.itemsUpdate(); }.bind(this)); - productConfigure.setShowWindowCallback(listType, function() { + productConfigure.setShowWindowCallback(listType, function () { // sync qty of grid and qty of popup var formCurrentQty = productConfigure.getCurrentFormQtyElement(); if (formCurrentQty && qtyElement && !isNaN(qtyElement.value)) { @@ -1056,60 +1068,59 @@ define([ productConfigure.showItemConfiguration(listType, itemId); }, - accountFieldsBind : function(container){ - if($(container)){ + accountFieldsBind: function (container) { + if ($(container)) { var fields = $(container).select('input', 'select', 'textarea'); - for(var i=0; i<fields.length; i++){ - if(fields[i].id == 'group_id'){ + for (var i = 0; i < fields.length; i++) { + if (fields[i].id == 'group_id') { fields[i].observe('change', this.accountGroupChange.bind(this)) - } - else{ + } else { fields[i].observe('change', this.accountFieldChange.bind(this)) } } } }, - accountGroupChange : function(){ + accountGroupChange: function () { this.loadArea(['data'], true, this.serializeData('order-form_account').toObject()); }, - accountFieldChange : function(){ + accountFieldChange: function () { this.saveData(this.serializeData('order-form_account')); }, - commentFieldsBind : function(container){ - if($(container)){ + commentFieldsBind: function (container) { + if ($(container)) { var fields = $(container).select('input', 'textarea'); - for(var i=0; i<fields.length; i++) + for (var i = 0; i < fields.length; i++) fields[i].observe('change', this.commentFieldChange.bind(this)) } }, - commentFieldChange : function(){ + commentFieldChange: function () { this.saveData(this.serializeData('order-comment')); }, - giftmessageFieldsBind : function(container){ - if($(container)){ + giftmessageFieldsBind: function (container) { + if ($(container)) { var fields = $(container).select('input', 'textarea'); - for(var i=0; i<fields.length; i++) + for (var i = 0; i < fields.length; i++) fields[i].observe('change', this.giftmessageFieldChange.bind(this)) } }, - giftmessageFieldChange : function(){ + giftmessageFieldChange: function () { this.giftMessageDataChanged = true; }, - giftmessageOnItemChange : function(event) { + giftmessageOnItemChange: function (event) { var element = Event.element(event); - if(element.name.indexOf("giftmessage") != -1 && element.type == "checkbox" && !element.checked) { + if (element.name.indexOf("giftmessage") != -1 && element.type == "checkbox" && !element.checked) { var messages = $("order-giftmessage").select('textarea'); var name; - for(var i=0; i<messages.length; i++) { + for (var i = 0; i < messages.length; i++) { name = messages[i].id.split("_"); - if(name.length < 2) continue; + if (name.length < 2) continue; if (element.name.indexOf("[" + name[1] + "]") != -1 && messages[i].value != "") { alert({ content: "First, clean the Message field in Gift Message form" @@ -1120,7 +1131,7 @@ define([ } }, - loadArea : function(area, indicator, params){ + loadArea: function (area, indicator, params) { var deferred = new jQuery.Deferred(); var url = this.loadBaseUrl; if (area) { @@ -1134,20 +1145,19 @@ define([ if (indicator) { this.loadingAreas = area; new Ajax.Request(url, { - parameters:params, + parameters: params, loaderArea: indicator, - onSuccess: function(transport) { + onSuccess: function (transport) { var response = transport.responseText.evalJSON(); this.loadAreaResponseHandler(response); deferred.resolve(); }.bind(this) }); - } - else { + } else { new Ajax.Request(url, { - parameters:params, + parameters: params, loaderArea: indicator, - onSuccess: function(transport) { + onSuccess: function (transport) { deferred.resolve(); } }); @@ -1158,7 +1168,7 @@ define([ return deferred.promise(); }, - loadAreaResponseHandler : function (response) { + loadAreaResponseHandler: function (response) { if (response.error) { alert({ content: response.message @@ -1193,45 +1203,44 @@ define([ } }, - prepareArea : function(area) { + prepareArea: function (area) { if (this.giftMessageDataChanged) { return area.without('giftmessage'); } return area; }, - saveData : function(data){ + saveData: function (data) { this.loadArea(false, false, data); }, - showArea : function(area){ + showArea: function (area) { var id = this.getAreaId(area); - if($(id)) { + if ($(id)) { $(id).show(); this.areaOverlay(); } }, - hideArea : function(area){ + hideArea: function (area) { var id = this.getAreaId(area); - if($(id)) { + if ($(id)) { $(id).hide(); this.areaOverlay(); } }, - areaOverlay : function() - { - $H(order.overlayData).each(function(e){ + areaOverlay: function () { + $H(order.overlayData).each(function (e) { e.value.fx(); }); }, - getAreaId : function(area){ - return 'order-'+area; + getAreaId: function (area) { + return 'order-' + area; }, - prepareParams : function(params){ + prepareParams: function (params) { if (!params) { params = {}; } @@ -1251,7 +1260,7 @@ define([ if (this.isPaymentValidationAvailable()) { var data = this.serializeData('order-billing_method'); if (data) { - data.each(function(value) { + data.each(function (value) { params[value[0]] = value[1]; }); } @@ -1266,7 +1275,7 @@ define([ * * @returns {boolean} */ - isPaymentValidationAvailable : function(){ + isPaymentValidationAvailable: function () { return ((typeof this.paymentMethod) == 'undefined' || this.excludedPaymentMethods.indexOf(this.paymentMethod) == -1); }, @@ -1284,21 +1293,19 @@ define([ return $H(data); }, - toggleCustomPrice: function(checkbox, elemId, tierBlock) { + toggleCustomPrice: function (checkbox, elemId, tierBlock) { if (checkbox.checked) { $(elemId).disabled = false; $(elemId).show(); - if($(tierBlock)) $(tierBlock).hide(); - } - else { + if ($(tierBlock)) $(tierBlock).hide(); + } else { $(elemId).disabled = true; $(elemId).hide(); - if($(tierBlock)) $(tierBlock).show(); + if ($(tierBlock)) $(tierBlock).show(); } }, - submit : function() - { + submit: function () { var $editForm = jQuery('#edit_form'); if ($editForm.valid()) { @@ -1308,9 +1315,9 @@ define([ }, _realSubmit: function () { - var disableAndSave = function() { + var disableAndSave = function () { disableElements('save'); - jQuery('#edit_form').on('invalid-form.validate', function() { + jQuery('#edit_form').on('invalid-form.validate', function () { enableElements('save'); jQuery('#edit_form').trigger('processStop'); jQuery('#edit_form').off('invalid-form.validate'); @@ -1325,11 +1332,11 @@ define([ confirm({ content: jQuery.mage.__('You have item changes'), actions: { - confirm: function() { + confirm: function () { jQuery('#edit_form').trigger('processStart'); disableAndSave(); }, - cancel: function() { + cancel: function () { self.itemsUpdate(); } } @@ -1339,8 +1346,10 @@ define([ } }, - overlay : function(elId, show, observe) { - if (typeof(show) == 'undefined') { show = true; } + overlay: function (elId, show, observe) { + if (typeof (show) == 'undefined') { + show = true; + } var orderObj = this; var obj = this.overlayData.get(elId); @@ -1349,7 +1358,7 @@ define([ show: show, el: elId, order: orderObj, - fx: function(event) { + fx: function (event) { this.order.processOverlay(this.el, this.show); } }; @@ -1365,7 +1374,7 @@ define([ this.processOverlay(elId, show); }, - processOverlay : function(elId, show) { + processOverlay: function (elId, show) { var el = $(elId); if (!el) { @@ -1397,8 +1406,7 @@ define([ }); }, - validateVat: function(parameters) - { + validateVat: function (parameters) { var params = { country: $(parameters.countryElementId).value, vat: $(parameters.vatElementId).value @@ -1413,7 +1421,7 @@ define([ new Ajax.Request(parameters.validateUrl, { parameters: params, - onSuccess: function(response) { + onSuccess: function (response) { var message = ''; var groupActionRequired = null; try { @@ -1452,8 +1460,7 @@ define([ alert({ content: message }); - } - else { + } else { this.processCustomerGroupChange( parameters.groupIdHtmlId, message, @@ -1467,8 +1474,7 @@ define([ }); }, - processCustomerGroupChange: function(groupIdHtmlId, message, customerGroupMessage, errorMessage, groupId, action) - { + processCustomerGroupChange: function (groupIdHtmlId, message, customerGroupMessage, errorMessage, groupId, action) { var groupMessage = ''; try { var currentCustomerGroupId = $(groupIdHtmlId).value; @@ -1508,8 +1514,8 @@ define([ _parent: null, _callbackName: null, - initialize: function(name, node, parent){ - if(!node) + initialize: function (name, node, parent) { + if (!node) return; this._name = name; this._parent = parent; @@ -1518,7 +1524,7 @@ define([ this._callbackName = name + 'Loaded'; node.callback = this._callbackName; } - parent[this._callbackName] = parent[this._callbackName].wrap((function (proceed){ + parent[this._callbackName] = parent[this._callbackName].wrap((function (proceed) { proceed(); this.onLoad(); }).bind(this)); @@ -1526,14 +1532,14 @@ define([ this.setNode(node); }, - setNode: function(node){ + setNode: function (node) { if (!node.callback) { node.callback = this._callbackName; } this.node = node; }, - onLoad: function(){ + onLoad: function () { } }; @@ -1543,21 +1549,21 @@ define([ _label: '', _node: null, - initialize: function(label, id){ + initialize: function (label, id) { this._label = label; this._node = new Element('button', { 'class': 'action-secondary action-add', - 'type': 'button' + 'type': 'button' }); if (typeof id !== 'undefined') { this._node.setAttribute('id', id) } }, - onClick: function(){ + onClick: function () { }, - insertIn: function(element, position){ + insertIn: function (element, position) { var node = Object.extend(this._node), content = {}; node.observe('click', this.onClick); @@ -1566,7 +1572,7 @@ define([ Element.insert(element, content); }, - getLabel: function(){ + getLabel: function () { return this._label; } }; diff --git a/app/code/Magento/Sales/view/frontend/email/creditmemo_new.html b/app/code/Magento/Sales/view/frontend/email/creditmemo_new.html index 5ae6f5f9d82c7..f475503528dc9 100644 --- a/app/code/Magento/Sales/view/frontend/email/creditmemo_new.html +++ b/app/code/Magento/Sales/view/frontend/email/creditmemo_new.html @@ -22,6 +22,8 @@ "var store_hours":"Store Hours", "var creditmemo":"Credit Memo", "var order":"Order", +"var order_id": "Order DB Id", +"var creditmemo_id": "Credit Memo DB Id", "var order_data.is_not_virtual":"Order Type" } @--> {{template config_path="design/email/header_template"}} @@ -82,7 +84,7 @@ <h3>{{trans "Shipping Method"}}</h3> {{/depend}} </tr> </table> - {{layout handle="sales_email_order_creditmemo_items" creditmemo=$creditmemo order=$order}} + {{layout handle="sales_email_order_creditmemo_items" creditmemo_id=$creditmemo_id order_id=$order_id}} </td> </tr> </table> diff --git a/app/code/Magento/Sales/view/frontend/email/creditmemo_new_guest.html b/app/code/Magento/Sales/view/frontend/email/creditmemo_new_guest.html index 657de2aae2045..d8a8a0baeca98 100644 --- a/app/code/Magento/Sales/view/frontend/email/creditmemo_new_guest.html +++ b/app/code/Magento/Sales/view/frontend/email/creditmemo_new_guest.html @@ -21,7 +21,9 @@ "var store_hours":"Store Hours", "var creditmemo":"Credit Memo", "var order":"Order", -"var order_data.is_not_virtual":"Order Type" +"var order_data.is_not_virtual":"Order Type", +"var order_id": "Order DB Id", +"var creditmemo_id": "Credit Memo DB Id" } @--> {{template config_path="design/email/header_template"}} @@ -80,7 +82,7 @@ <h3>{{trans "Shipping Method"}}</h3> {{/depend}} </tr> </table> - {{layout handle="sales_email_order_creditmemo_items" creditmemo=$creditmemo order=$order}} + {{layout handle="sales_email_order_creditmemo_items" creditmemo_id=$creditmemo_id order_id=$order_id}} </td> </tr> </table> diff --git a/app/code/Magento/Sales/view/frontend/email/invoice_new.html b/app/code/Magento/Sales/view/frontend/email/invoice_new.html index 68773ee9d7570..c4818172449a2 100644 --- a/app/code/Magento/Sales/view/frontend/email/invoice_new.html +++ b/app/code/Magento/Sales/view/frontend/email/invoice_new.html @@ -22,6 +22,8 @@ "var store_hours":"Store Hours", "var invoice": "Invoice", "var order": "Order", +"var order_id": "Order DB Id", +"var invoice_id": "Invoice DB Id", "var order_data.is_not_virtual": "Order Type" } @--> {{template config_path="design/email/header_template"}} @@ -82,7 +84,7 @@ <h3>{{trans "Shipping Method"}}</h3> {{/depend}} </tr> </table> - {{layout area="frontend" handle="sales_email_order_invoice_items" invoice=$invoice order=$order}} + {{layout area="frontend" handle="sales_email_order_invoice_items" invoice_id=$invoice_id order_id=$order_id}} </td> </tr> </table> diff --git a/app/code/Magento/Sales/view/frontend/email/invoice_new_guest.html b/app/code/Magento/Sales/view/frontend/email/invoice_new_guest.html index 5053ccc2ac635..c06630fd249ab 100644 --- a/app/code/Magento/Sales/view/frontend/email/invoice_new_guest.html +++ b/app/code/Magento/Sales/view/frontend/email/invoice_new_guest.html @@ -21,6 +21,8 @@ "var store_hours":"Store Hours", "var invoice": "Invoice", "var order": "Order", +"var order_id": "Order DB Id", +"var invoice_id": "Invoice DB Id", "var order_data.is_not_virtual": "Order Type" } @--> {{template config_path="design/email/header_template"}} @@ -80,7 +82,7 @@ <h3>{{trans "Shipping Method"}}</h3> {{/depend}} </tr> </table> - {{layout handle="sales_email_order_invoice_items" invoice=$invoice order=$order}} + {{layout handle="sales_email_order_invoice_items" invoice_id=$invoice_id order_id=$order_id}} </td> </tr> </table> diff --git a/app/code/Magento/Sales/view/frontend/email/order_new.html b/app/code/Magento/Sales/view/frontend/email/order_new.html index 13c436b131b82..a411e8d5b29b3 100644 --- a/app/code/Magento/Sales/view/frontend/email/order_new.html +++ b/app/code/Magento/Sales/view/frontend/email/order_new.html @@ -22,6 +22,7 @@ "var this.getUrl($store,'customer/account/',[_nosid:1])":"Customer Account URL", "var order_data.is_not_virtual":"Order Type", "var order":"Order", +"var order_id": "Order DB Id", "var order_data.customer_name":"Customer Name" } @--> @@ -90,7 +91,7 @@ <h3>{{trans "Shipping Method"}}</h3> {{/depend}} </tr> </table> - {{layout handle="sales_email_order_items" order=$order area="frontend"}} + {{layout handle="sales_email_order_items" order_id=$order_id area="frontend"}} </td> </tr> </table> diff --git a/app/code/Magento/Sales/view/frontend/email/order_new_guest.html b/app/code/Magento/Sales/view/frontend/email/order_new_guest.html index 866a1ad87f9b1..dc3a8e9f69aca 100644 --- a/app/code/Magento/Sales/view/frontend/email/order_new_guest.html +++ b/app/code/Magento/Sales/view/frontend/email/order_new_guest.html @@ -21,6 +21,7 @@ "var store_email":"Store Email", "var store_hours":"Store Hours", "var order_data.is_not_virtual":"Order Type", +"var order_id": "Order DB Id", "var order":"Order" } @--> {{template config_path="design/email/header_template"}} @@ -85,7 +86,7 @@ <h3>{{trans "Shipping Method"}}</h3> {{/depend}} </tr> </table> - {{layout handle="sales_email_order_items" order=$order}} + {{layout handle="sales_email_order_items" order_id=$order_id }} </td> </tr> </table> diff --git a/app/code/Magento/Sales/view/frontend/email/shipment_new.html b/app/code/Magento/Sales/view/frontend/email/shipment_new.html index 39823a0c9d80b..39397979d2aaa 100644 --- a/app/code/Magento/Sales/view/frontend/email/shipment_new.html +++ b/app/code/Magento/Sales/view/frontend/email/shipment_new.html @@ -23,7 +23,9 @@ "var store_hours":"Store Hours", "var order_data.is_not_virtual": "Order Type", "var shipment": "Shipment", -"var order": "Order" +"var order": "Order", +"var order_id": "Order DB Id", +"var shipment_id": "Shipment DB Id" } @--> {{template config_path="design/email/header_template"}} @@ -59,7 +61,7 @@ <h1>{{trans "Your Shipment #%shipment_id for Order #%order_id" shipment_id=$ship </tr> </table> {{/depend}} - {{layout handle="sales_email_order_shipment_track" shipment=$shipment order=$order}} + {{layout handle="sales_email_order_shipment_track" shipment_id=$shipment_id order_id=$order_id}} <table class="order-details"> <tr> <td class="address-details"> @@ -86,7 +88,7 @@ <h3>{{trans "Shipping Method"}}</h3> {{/depend}} </tr> </table> - {{layout handle="sales_email_order_shipment_items" shipment=$shipment order=$order}} + {{layout handle="sales_email_order_shipment_items" shipment_id=$shipment_id order_id=$order_id}} </td> </tr> </table> diff --git a/app/code/Magento/Sales/view/frontend/email/shipment_new_guest.html b/app/code/Magento/Sales/view/frontend/email/shipment_new_guest.html index ed2f52ed85066..54c7f08506497 100644 --- a/app/code/Magento/Sales/view/frontend/email/shipment_new_guest.html +++ b/app/code/Magento/Sales/view/frontend/email/shipment_new_guest.html @@ -22,7 +22,9 @@ "var store_hours":"Store Hours", "var order_data.is_not_virtual": "Order Type", "var shipment": "Shipment", -"var order": "Order" +"var order": "Order", +"var order_id": "Order DB Id", +"var shipment_id": "Shipment DB Id" } @--> {{template config_path="design/email/header_template"}} @@ -57,7 +59,7 @@ <h1>{{trans "Your Shipment #%shipment_id for Order #%order_id" shipment_id=$ship </tr> </table> {{/depend}} - {{layout handle="sales_email_order_shipment_track" shipment=$shipment order=$order}} + {{layout handle="sales_email_order_shipment_track" shipment_id=$shipment_id order_id=$order_id}} <table class="order-details"> <tr> <td class="address-details"> @@ -84,7 +86,7 @@ <h3>{{trans "Shipping Method"}}</h3> {{/depend}} </tr> </table> - {{layout handle="sales_email_order_shipment_items" shipment=$shipment order=$order}} + {{layout handle="sales_email_order_shipment_items" shipment_id=$shipment_id order_id=$order_id}} </td> </tr> </table> diff --git a/app/code/Magento/Sales/view/frontend/layout/sales_email_order_shipment_track.xml b/app/code/Magento/Sales/view/frontend/layout/sales_email_order_shipment_track.xml index bbc7f04ce94fd..489317cfa65c7 100644 --- a/app/code/Magento/Sales/view/frontend/layout/sales_email_order_shipment_track.xml +++ b/app/code/Magento/Sales/view/frontend/layout/sales_email_order_shipment_track.xml @@ -8,10 +8,10 @@ <page xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="urn:magento:framework:View/Layout/etc/page_configuration.xsd"> <update handle="sales_email_order_shipment_renderers"/> <body> - <block class="Magento\Framework\View\Element\Template" name="sales.order.email.shipment.track" template="Magento_Sales::email/shipment/track.phtml"> + <block class="Magento\Sales\Block\Order\Email\Shipment\Items" name="sales.order.email.shipment.track" template="Magento_Sales::email/shipment/track.phtml"> <arguments> <argument name="tracking_url" xsi:type="object">Magento\Sales\Block\DataProviders\Email\Shipment\TrackingUrl</argument> </arguments> </block> </body> -</page> \ No newline at end of file +</page> diff --git a/app/code/Magento/Sales/view/frontend/templates/order/history.phtml b/app/code/Magento/Sales/view/frontend/templates/order/history.phtml index a785ca93511ad..bc1887c7669ca 100644 --- a/app/code/Magento/Sales/view/frontend/templates/order/history.phtml +++ b/app/code/Magento/Sales/view/frontend/templates/order/history.phtml @@ -59,5 +59,5 @@ <div class="order-products-toolbar toolbar bottom"><?= $block->getPagerHtml() ?></div> <?php endif ?> <?php else : ?> - <div class="message info empty"><span><?= $block->escapeHtml(__('You have placed no orders.')) ?></span></div> + <div class="message info empty"><span><?= $block->escapeHtml($block->getEmptyOrdersMessage()) ?></span></div> <?php endif ?> diff --git a/app/code/Magento/Sales/view/frontend/templates/order/order_date.phtml b/app/code/Magento/Sales/view/frontend/templates/order/order_date.phtml index 80a0ea02499cc..3e2b653e8b3bd 100644 --- a/app/code/Magento/Sales/view/frontend/templates/order/order_date.phtml +++ b/app/code/Magento/Sales/view/frontend/templates/order/order_date.phtml @@ -5,5 +5,11 @@ */ ?> <div class="order-date"> - <?= $block->escapeHtml(__('<span class="label">Order Date:</span> %1', '<date>' . $block->formatDate($block->getOrder()->getCreatedAt(), \IntlDateFormatter::LONG) . '</date>'), ['span', 'date']) ?> + <?= $block->escapeHtml( + __( + '<span class="label">Order Date:</span> %1', + '<span>' . $block->formatDate($block->getOrder()->getCreatedAt(), \IntlDateFormatter::LONG) . '</span>' + ), + ['span'] + )?> </div> diff --git a/app/code/Magento/SalesGraphQl/Model/Resolver/Reorder.php b/app/code/Magento/SalesGraphQl/Model/Resolver/Reorder.php new file mode 100644 index 0000000000000..8bf4220d1ec3d --- /dev/null +++ b/app/code/Magento/SalesGraphQl/Model/Resolver/Reorder.php @@ -0,0 +1,95 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +declare(strict_types=1); + +namespace Magento\SalesGraphQl\Model\Resolver; + +use Magento\Framework\GraphQl\Config\Element\Field; +use Magento\Framework\GraphQl\Exception\GraphQlAuthorizationException; +use Magento\Framework\GraphQl\Query\ResolverInterface; +use Magento\Framework\GraphQl\Schema\Type\ResolveInfo; +use Magento\GraphQl\Model\Query\ContextInterface; +use Magento\Sales\Model\Reorder\Data\Error; +use Magento\Sales\Model\OrderFactory; +use Magento\Framework\GraphQl\Exception\GraphQlInputException; + +/** + * ReOrder customer order + */ +class Reorder implements ResolverInterface +{ + /** + * Order number + */ + private const ARGUMENT_ORDER_NUMBER = 'orderNumber'; + + /** + * @var OrderFactory + */ + private $orderFactory; + + /** + * @var \Magento\Sales\Model\Reorder\Reorder + */ + private $reorder; + + /** + * @param \Magento\Sales\Model\Reorder\Reorder $reorder + * @param OrderFactory $orderFactory + */ + public function __construct( + \Magento\Sales\Model\Reorder\Reorder $reorder, + OrderFactory $orderFactory + ) { + $this->orderFactory = $orderFactory; + $this->reorder = $reorder; + } + + /** + * @inheritdoc + */ + public function resolve( + Field $field, + $context, + ResolveInfo $info, + array $value = null, + array $args = null + ) { + /** @var ContextInterface $context */ + if (false === $context->getExtensionAttributes()->getIsCustomer()) { + throw new GraphQlAuthorizationException(__('The current customer isn\'t authorized.')); + } + + $currentUserId = $context->getUserId(); + $orderNumber = $args['orderNumber'] ?? ''; + $storeId = (string)$context->getExtensionAttributes()->getStore()->getId(); + + $order = $this->orderFactory->create()->loadByIncrementIdAndStoreId($orderNumber, $storeId); + if ((int)$order->getCustomerId() !== $currentUserId) { + throw new GraphQlInputException( + __('Order number "%1" doesn\'t belong to the current customer', $orderNumber) + ); + } + + $reorderOutput = $this->reorder->execute($orderNumber, $storeId); + + return [ + 'cart' => [ + 'model' => $reorderOutput->getCart(), + ], + 'userInputErrors' => \array_map( + function (Error $error) { + return [ + 'path' => [self::ARGUMENT_ORDER_NUMBER], + 'code' => $error->getCode(), + 'message' => $error->getMessage(), + ]; + }, + $reorderOutput->getErrors() + ) + ]; + } +} diff --git a/app/code/Magento/SalesGraphQl/etc/schema.graphqls b/app/code/Magento/SalesGraphQl/etc/schema.graphqls index a687ee59031ea..f823c25cf2d9f 100644 --- a/app/code/Magento/SalesGraphQl/etc/schema.graphqls +++ b/app/code/Magento/SalesGraphQl/etc/schema.graphqls @@ -17,3 +17,26 @@ type CustomerOrder @doc(description: "Order mapping fields") { type CustomerOrders { items: [CustomerOrder] @doc(description: "Array of orders") } + +type Mutation { + reorderItems(orderNumber: String!): ReorderItemsOutput @doc(description:"Adds all products from a customer's previous order to the cart.") @resolver(class: "Magento\\SalesGraphQl\\Model\\Resolver\\Reorder") +} + +type ReorderItemsOutput { + cart: Cart! @doc(description:"Contains detailed information about the customer's cart.") + userInputErrors:[CheckoutUserInputError]! @doc(description:"An array of reordering errors.") +} + +type CheckoutUserInputError @doc(description:"An error encountered while adding an item the the cart."){ + message: String! @doc(description: "Localized error message") + path: [String]! @doc(description: "Path to the input field that caused an error. See the GraphQL specification about path errors for details: http://spec.graphql.org/draft/#sec-Errors") + code: CheckoutUserInputErrorCodes! @doc(description: "Checkout-specific error code") +} + +enum CheckoutUserInputErrorCodes { + REORDER_NOT_AVAILABLE + PRODUCT_NOT_FOUND + NOT_SALABLE + INSUFFICIENT_STOCK + UNDEFINED +} diff --git a/app/code/Magento/SalesInventory/registration.php b/app/code/Magento/SalesInventory/registration.php index a6c9a6bc3e669..f2c0adf20a039 100644 --- a/app/code/Magento/SalesInventory/registration.php +++ b/app/code/Magento/SalesInventory/registration.php @@ -4,6 +4,6 @@ * See COPYING.txt for license details. */ -use \Magento\Framework\Component\ComponentRegistrar; +use Magento\Framework\Component\ComponentRegistrar; ComponentRegistrar::register(ComponentRegistrar::MODULE, 'Magento_SalesInventory', __DIR__); diff --git a/app/code/Magento/SalesRule/Block/Adminhtml/Promo/Quote/Edit/Tab/Conditions.php b/app/code/Magento/SalesRule/Block/Adminhtml/Promo/Quote/Edit/Tab/Conditions.php index 1038f289eada2..ff905bf5cb9ff 100644 --- a/app/code/Magento/SalesRule/Block/Adminhtml/Promo/Quote/Edit/Tab/Conditions.php +++ b/app/code/Magento/SalesRule/Block/Adminhtml/Promo/Quote/Edit/Tab/Conditions.php @@ -3,10 +3,19 @@ * Copyright © Magento, Inc. All rights reserved. * See COPYING.txt for license details. */ +declare(strict_types=1); + namespace Magento\SalesRule\Block\Adminhtml\Promo\Quote\Edit\Tab; use Magento\Framework\App\ObjectManager; +use Magento\Framework\Data\Form\Element\Fieldset; +use Magento\SalesRule\Model\Rule; +/** + * Block for rendering Conditions tab on Sales Rules creation page. + * + * @SuppressWarnings(PHPMD.CouplingBetweenObjects) + */ class Conditions extends \Magento\Backend\Block\Widget\Form\Generic implements \Magento\Ui\Component\Layout\Tabs\TabInterface { @@ -33,8 +42,6 @@ class Conditions extends \Magento\Backend\Block\Widget\Form\Generic implements private $ruleFactory; /** - * Constructor - * * @param \Magento\Backend\Block\Template\Context $context * @param \Magento\Framework\Registry $registry * @param \Magento\Framework\Data\FormFactory $formFactory @@ -60,7 +67,8 @@ public function __construct( } /** - * {@inheritdoc} + * @inheritdoc + * * @codeCoverageIgnore */ public function getTabClass() @@ -69,7 +77,7 @@ public function getTabClass() } /** - * {@inheritdoc} + * @inheritdoc */ public function getTabUrl() { @@ -77,7 +85,7 @@ public function getTabUrl() } /** - * {@inheritdoc} + * @inheritdoc */ public function isAjaxLoaded() { @@ -85,7 +93,7 @@ public function isAjaxLoaded() } /** - * {@inheritdoc} + * @inheritdoc */ public function getTabLabel() { @@ -93,7 +101,7 @@ public function getTabLabel() } /** - * {@inheritdoc} + * @inheritdoc */ public function getTabTitle() { @@ -101,7 +109,7 @@ public function getTabTitle() } /** - * {@inheritdoc} + * @inheritdoc */ public function canShowTab() { @@ -109,7 +117,7 @@ public function canShowTab() } /** - * {@inheritdoc} + * @inheritdoc */ public function isHidden() { @@ -133,7 +141,7 @@ protected function _prepareForm() /** * Handles addition of conditions tab to supplied form. * - * @param \Magento\SalesRule\Model\Rule $model + * @param Rule $model * @param string $fieldsetId * @param string $formName * @return \Magento\Framework\Data\Form diff --git a/app/code/Magento/SalesRule/Controller/Adminhtml/Promo/Quote/ExportCouponsCsv.php b/app/code/Magento/SalesRule/Controller/Adminhtml/Promo/Quote/ExportCouponsCsv.php index 0f073ab4d65ce..53459f2c3e52f 100644 --- a/app/code/Magento/SalesRule/Controller/Adminhtml/Promo/Quote/ExportCouponsCsv.php +++ b/app/code/Magento/SalesRule/Controller/Adminhtml/Promo/Quote/ExportCouponsCsv.php @@ -1,33 +1,40 @@ <?php /** - * * Copyright © Magento, Inc. All rights reserved. * See COPYING.txt for license details. */ + +declare(strict_types=1); + namespace Magento\SalesRule\Controller\Adminhtml\Promo\Quote; use Magento\Framework\App\Filesystem\DirectoryList; +use Magento\Framework\Controller\ResultFactory; +use Magento\SalesRule\Controller\Adminhtml\Promo\Quote; +use Magento\SalesRule\Block\Adminhtml\Promo\Quote\Edit\Tab\Coupons\Grid; +use Magento\Framework\View\Result\Layout; +use Magento\Framework\App\ResponseInterface; +use Magento\Framework\App\Action\HttpGetActionInterface; -class ExportCouponsCsv extends \Magento\SalesRule\Controller\Adminhtml\Promo\Quote +/** + * Export Coupons to csv file + * + * Class \Magento\SalesRule\Controller\Adminhtml\Promo\Quote\ExportCouponsCsv + */ +class ExportCouponsCsv extends Quote implements HttpGetActionInterface { /** * Export coupon codes as CSV file * - * @return \Magento\Framework\App\ResponseInterface|null + * @return ResponseInterface|null */ public function execute() { $this->_initRule(); - $rule = $this->_coreRegistry->registry(\Magento\SalesRule\Model\RegistryConstants::CURRENT_SALES_RULE); - if ($rule->getId()) { - $fileName = 'coupon_codes.csv'; - $content = $this->_view->getLayout()->createBlock( - \Magento\SalesRule\Block\Adminhtml\Promo\Quote\Edit\Tab\Coupons\Grid::class - )->getCsvFile(); - return $this->_fileFactory->create($fileName, $content, DirectoryList::VAR_DIR); - } else { - $this->_redirect('sales_rule/*/detail', ['_current' => true]); - return; - } + $fileName = 'coupon_codes.csv'; + /** @var Layout $resultLayout */ + $resultLayout = $this->resultFactory->create(ResultFactory::TYPE_LAYOUT); + $content = $resultLayout->getLayout()->createBlock(Grid::class)->getCsvFile(); + return $this->_fileFactory->create($fileName, $content, DirectoryList::VAR_DIR); } } diff --git a/app/code/Magento/SalesRule/Controller/Adminhtml/Promo/Quote/ExportCouponsXml.php b/app/code/Magento/SalesRule/Controller/Adminhtml/Promo/Quote/ExportCouponsXml.php index 35d81d3a971fc..fa3d4455410c4 100644 --- a/app/code/Magento/SalesRule/Controller/Adminhtml/Promo/Quote/ExportCouponsXml.php +++ b/app/code/Magento/SalesRule/Controller/Adminhtml/Promo/Quote/ExportCouponsXml.php @@ -1,35 +1,40 @@ <?php /** - * * Copyright © Magento, Inc. All rights reserved. * See COPYING.txt for license details. */ + +declare(strict_types=1); + namespace Magento\SalesRule\Controller\Adminhtml\Promo\Quote; use Magento\Framework\App\Filesystem\DirectoryList; +use Magento\Framework\Controller\ResultFactory; +use Magento\SalesRule\Controller\Adminhtml\Promo\Quote; +use Magento\SalesRule\Block\Adminhtml\Promo\Quote\Edit\Tab\Coupons\Grid; +use Magento\Framework\View\Result\Layout; +use Magento\Framework\App\ResponseInterface; +use Magento\Framework\App\Action\HttpGetActionInterface; -class ExportCouponsXml extends \Magento\SalesRule\Controller\Adminhtml\Promo\Quote +/** + * Export coupons to xml file + * + * Class \Magento\SalesRule\Controller\Adminhtml\Promo\Quote\ExportCouponsXml + */ +class ExportCouponsXml extends Quote implements HttpGetActionInterface { /** * Export coupon codes as excel xml file * - * @return \Magento\Framework\App\ResponseInterface|null + * @return ResponseInterface|null */ public function execute() { $this->_initRule(); - $rule = $this->_coreRegistry->registry(\Magento\SalesRule\Model\RegistryConstants::CURRENT_SALES_RULE); - if ($rule->getId()) { - $fileName = 'coupon_codes.xml'; - $content = $this->_view->getLayout()->createBlock( - \Magento\SalesRule\Block\Adminhtml\Promo\Quote\Edit\Tab\Coupons\Grid::class - )->getExcelFile( - $fileName - ); - return $this->_fileFactory->create($fileName, $content, DirectoryList::VAR_DIR); - } else { - $this->_redirect('sales_rule/*/detail', ['_current' => true]); - return; - } + $fileName = 'coupon_codes.xml'; + /** @var Layout $resultLayout */ + $resultLayout = $this->resultFactory->create(ResultFactory::TYPE_LAYOUT); + $content = $resultLayout->getLayout()->createBlock(Grid::class)->getExcelFile($fileName); + return $this->_fileFactory->create($fileName, $content, DirectoryList::VAR_DIR); } } diff --git a/app/code/Magento/SalesRule/Controller/Adminhtml/Promo/Quote/NewActionHtml.php b/app/code/Magento/SalesRule/Controller/Adminhtml/Promo/Quote/NewActionHtml.php index 56c08864c90c4..af28547456a9d 100644 --- a/app/code/Magento/SalesRule/Controller/Adminhtml/Promo/Quote/NewActionHtml.php +++ b/app/code/Magento/SalesRule/Controller/Adminhtml/Promo/Quote/NewActionHtml.php @@ -47,6 +47,7 @@ public function execute() if ($model instanceof AbstractCondition) { $model->setJsFormObject($formName); $model->setFormName($formName); + $this->setJsFormObject($model); $html = $model->asHtmlRecursive(); } else { $html = ''; @@ -54,4 +55,32 @@ public function execute() $this->getResponse() ->setBody($html); } + + /** + * Set jsFormObject for the model object + * + * @return void + * @param AbstractCondition $model + */ + private function setJsFormObject(AbstractCondition $model): void + { + $requestJsFormName = $this->getRequest()->getParam('form'); + $actualJsFormName = $this->getJsFormObjectName($model->getFormName()); + if ($requestJsFormName === $actualJsFormName) { //new + $model->setJsFormObject($actualJsFormName); + } else { //edit + $model->setJsFormObject($requestJsFormName); + } + } + + /** + * Get jsFormObject name + * + * @param string $formName + * @return string + */ + private function getJsFormObjectName(string $formName): string + { + return $formName . 'rule_actions_fieldset_'; + } } diff --git a/app/code/Magento/SalesRule/Controller/Adminhtml/Promo/Quote/NewConditionHtml.php b/app/code/Magento/SalesRule/Controller/Adminhtml/Promo/Quote/NewConditionHtml.php index 50545fd864866..3646f9592c497 100644 --- a/app/code/Magento/SalesRule/Controller/Adminhtml/Promo/Quote/NewConditionHtml.php +++ b/app/code/Magento/SalesRule/Controller/Adminhtml/Promo/Quote/NewConditionHtml.php @@ -6,11 +6,13 @@ namespace Magento\SalesRule\Controller\Adminhtml\Promo\Quote; use Magento\Framework\App\Action\HttpPostActionInterface; +use Magento\Rule\Model\Condition\AbstractCondition; +use Magento\SalesRule\Controller\Adminhtml\Promo\Quote; /** * Controller class NewConditionHtml. Returns condition html */ -class NewConditionHtml extends \Magento\SalesRule\Controller\Adminhtml\Promo\Quote implements HttpPostActionInterface +class NewConditionHtml extends Quote implements HttpPostActionInterface { /** * New condition html action @@ -39,13 +41,40 @@ public function execute() $model->setAttribute($typeArr[1]); } - if ($model instanceof \Magento\Rule\Model\Condition\AbstractCondition) { + if ($model instanceof AbstractCondition) { $model->setJsFormObject($this->getRequest()->getParam('form')); $model->setFormName($formName); + $this->setJsFormObject($model); $html = $model->asHtmlRecursive(); } else { $html = ''; } $this->getResponse()->setBody($html); } + + /** + * Set jsFormObject for the model object + * + * @return void + * @param AbstractCondition $model + */ + private function setJsFormObject(AbstractCondition $model): void + { + $requestJsFormName = $this->getRequest()->getParam('form'); + $actualJsFormName = $this->getJsFormObjectName($model->getFormName()); + if ($requestJsFormName === $actualJsFormName) { //new + $model->setJsFormObject($actualJsFormName); + } + } + + /** + * Get jsFormObject name + * + * @param string $formName + * @return string + */ + private function getJsFormObjectName(string $formName): string + { + return $formName . 'rule_conditions_fieldset_'; + } } diff --git a/app/code/Magento/SalesRule/Model/Coupon/UpdateCouponUsages.php b/app/code/Magento/SalesRule/Model/Coupon/UpdateCouponUsages.php index c4f7652e1a334..3236c80e1b7ed 100644 --- a/app/code/Magento/SalesRule/Model/Coupon/UpdateCouponUsages.php +++ b/app/code/Magento/SalesRule/Model/Coupon/UpdateCouponUsages.php @@ -7,7 +7,7 @@ namespace Magento\SalesRule\Model\Coupon; -use Magento\Sales\Model\Order; +use Magento\Sales\Api\Data\OrderInterface; use Magento\SalesRule\Model\Coupon; use Magento\SalesRule\Model\ResourceModel\Coupon\Usage; use Magento\SalesRule\Model\Rule\CustomerFactory; @@ -59,11 +59,11 @@ public function __construct( /** * Executes the current command. * - * @param Order $subject + * @param OrderInterface $subject * @param bool $increment - * @return Order + * @return OrderInterface */ - public function execute(Order $subject, bool $increment): Order + public function execute(OrderInterface $subject, bool $increment): OrderInterface { if (!$subject || !$subject->getAppliedRuleIds()) { return $subject; @@ -133,11 +133,11 @@ private function updateCustomerRuleUsages(bool $increment, int $ruleId, int $cus /** * Update the number of coupon usages. * - * @param Order $subject + * @param OrderInterface $subject * @param bool $increment * @param int $customerId */ - private function updateCouponUsages(Order $subject, bool $increment, int $customerId): void + private function updateCouponUsages(OrderInterface $subject, bool $increment, int $customerId): void { $this->coupon->load($subject->getCouponCode(), 'code'); if ($this->coupon->getId()) { diff --git a/app/code/Magento/SalesRule/Model/Quote/Address/Total/ShippingDiscount.php b/app/code/Magento/SalesRule/Model/Quote/Address/Total/ShippingDiscount.php index 53adcd268f81d..9f116cedcb340 100644 --- a/app/code/Magento/SalesRule/Model/Quote/Address/Total/ShippingDiscount.php +++ b/app/code/Magento/SalesRule/Model/Quote/Address/Total/ShippingDiscount.php @@ -53,6 +53,10 @@ public function collect(Quote $quote, ShippingAssignment $shippingAssignment, To $address->setShippingDiscountAmount(0); $address->setBaseShippingDiscountAmount(0); + if ($total->getShippingAmountForDiscount() !== null) { + $address->setShippingAmountForDiscount($total->getShippingAmountForDiscount()); + $address->setBaseShippingAmountForDiscount($total->getBaseShippingAmountForDiscount()); + } if ($address->getShippingAmount()) { $this->calculator->processShippingAmount($address); $total->addTotalAmount(DiscountCollector::COLLECTOR_TYPE_CODE, -$address->getShippingDiscountAmount()); diff --git a/app/code/Magento/SalesRule/Model/Quote/Discount.php b/app/code/Magento/SalesRule/Model/Quote/Discount.php index 69abac8309f90..a580a8f9d2eaa 100644 --- a/app/code/Magento/SalesRule/Model/Quote/Discount.php +++ b/app/code/Magento/SalesRule/Model/Quote/Discount.php @@ -85,6 +85,7 @@ public function __construct( * @param \Magento\Quote\Model\Quote\Address\Total $total * @return $this * @SuppressWarnings(PHPMD.CyclomaticComplexity) + * @SuppressWarnings(PHPMD.NPathComplexity) */ public function collect( \Magento\Quote\Model\Quote $quote, @@ -95,6 +96,11 @@ public function collect( $store = $this->storeManager->getStore($quote->getStoreId()); $address = $shippingAssignment->getShipping()->getAddress(); + + if ($quote->currentPaymentWasSet()) { + $address->setPaymentMethod($quote->getPayment()->getMethod()); + } + $this->calculator->reset($address); $items = $shippingAssignment->getItems(); diff --git a/app/code/Magento/SalesRule/Model/Rule/Action/Discount/CartFixed.php b/app/code/Magento/SalesRule/Model/Rule/Action/Discount/CartFixed.php index 2ae1c1c7ac63a..e44200614fa00 100644 --- a/app/code/Magento/SalesRule/Model/Rule/Action/Discount/CartFixed.php +++ b/app/code/Magento/SalesRule/Model/Rule/Action/Discount/CartFixed.php @@ -64,25 +64,13 @@ public function calculate($rule, $item, $qty) $ruleTotals = $this->validator->getRuleItemTotalsInfo($rule->getId()); $quote = $item->getQuote(); - $address = $item->getAddress(); $itemPrice = $this->validator->getItemPrice($item); $baseItemPrice = $this->validator->getItemBasePrice($item); $itemOriginalPrice = $this->validator->getItemOriginalPrice($item); $baseItemOriginalPrice = $this->validator->getItemBaseOriginalPrice($item); - /** - * prevent applying whole cart discount for every shipping order, but only for first order - */ - if ($quote->getIsMultiShipping()) { - $usedForAddressId = $this->getCartFixedRuleUsedForAddress($rule->getId()); - if ($usedForAddressId && $usedForAddressId != $address->getId()) { - return $discountData; - } else { - $this->setCartFixedRuleUsedForAddress($rule->getId(), $address->getId()); - } - } - $cartRules = $address->getCartFixedRules(); + $cartRules = $quote->getCartFixedRules(); if (!isset($cartRules[$rule->getId()])) { $cartRules[$rule->getId()] = $rule->getDiscountAmount(); } @@ -122,7 +110,7 @@ public function calculate($rule, $item, $qty) $discountData->setOriginalAmount(min($itemOriginalPrice * $qty, $quoteAmount)); $discountData->setBaseOriginalAmount($this->priceCurrency->round($baseItemOriginalPrice)); } - $address->setCartFixedRules($cartRules); + $quote->setCartFixedRules($cartRules); return $discountData; } @@ -130,6 +118,7 @@ public function calculate($rule, $item, $qty) /** * Set information about usage cart fixed rule by quote address * + * @deprecated should be removed as it is not longer used * @param int $ruleId * @param int $itemId * @return void @@ -142,6 +131,7 @@ protected function setCartFixedRuleUsedForAddress($ruleId, $itemId) /** * Retrieve information about usage cart fixed rule by quote address * + * @deprecated should be removed as it is not longer used * @param int $ruleId * @return int|null */ diff --git a/app/code/Magento/SalesRule/Model/Rule/Condition/Address.php b/app/code/Magento/SalesRule/Model/Rule/Condition/Address.php index 29cdf34c5a784..cf6301cb31a9c 100644 --- a/app/code/Magento/SalesRule/Model/Rule/Condition/Address.php +++ b/app/code/Magento/SalesRule/Model/Rule/Condition/Address.php @@ -65,6 +65,7 @@ public function loadAttributeOptions() 'base_subtotal' => __('Subtotal'), 'total_qty' => __('Total Items Quantity'), 'weight' => __('Total Weight'), + 'payment_method' => __('Payment Method'), 'shipping_method' => __('Shipping Method'), 'postcode' => __('Shipping Postcode'), 'region' => __('Shipping Region'), diff --git a/app/code/Magento/SalesRule/Model/Rule/Condition/Product/Combine.php b/app/code/Magento/SalesRule/Model/Rule/Condition/Product/Combine.php index 1649dea80ef5b..6ade7a064e849 100644 --- a/app/code/Magento/SalesRule/Model/Rule/Condition/Product/Combine.php +++ b/app/code/Magento/SalesRule/Model/Rule/Condition/Product/Combine.php @@ -145,7 +145,8 @@ private function validateEntity($cond, \Magento\Framework\Model\AbstractModel $e private function retrieveValidateEntities($attributeScope, \Magento\Framework\Model\AbstractModel $entity) { if ($attributeScope === 'parent') { - $validateEntities = [$entity]; + $parentItem = $entity->getParentItem(); + $validateEntities = $parentItem ? [$parentItem] : [$entity]; } elseif ($attributeScope === 'children') { $validateEntities = $entity->getChildren() ?: [$entity]; } else { diff --git a/app/code/Magento/SalesRule/Model/Rule/QuoteResetAppliedRules.php b/app/code/Magento/SalesRule/Model/Rule/QuoteResetAppliedRules.php new file mode 100644 index 0000000000000..5c1d98b65a0e4 --- /dev/null +++ b/app/code/Magento/SalesRule/Model/Rule/QuoteResetAppliedRules.php @@ -0,0 +1,24 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +declare(strict_types=1); + +namespace Magento\SalesRule\Model\Rule; + +use Magento\SalesRule\Model\Spi\QuoteResetAppliedRulesInterface; + +/** + * Reset applied rules to quote + */ +class QuoteResetAppliedRules implements QuoteResetAppliedRulesInterface +{ + /** + * @inheritDoc + */ + public function execute(\Magento\Quote\Api\Data\CartInterface $quote): void + { + $quote->setCartFixedRules([]); + } +} diff --git a/app/code/Magento/SalesRule/Model/Rule/RuleQuoteRecollectTotalsOnDemand.php b/app/code/Magento/SalesRule/Model/Rule/RuleQuoteRecollectTotalsOnDemand.php new file mode 100644 index 0000000000000..b983a59d79c5e --- /dev/null +++ b/app/code/Magento/SalesRule/Model/Rule/RuleQuoteRecollectTotalsOnDemand.php @@ -0,0 +1,52 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ + +declare(strict_types=1); + +namespace Magento\SalesRule\Model\Rule; + +use Magento\Quote\Model\ResourceModel\Quote; +use Magento\SalesRule\Model\Spi\RuleQuoteRecollectTotalsInterface; + +/** + * Forces related quotes to be recollected on demand. + */ +class RuleQuoteRecollectTotalsOnDemand implements RuleQuoteRecollectTotalsInterface +{ + /** + * @var Quote + */ + private $quoteResourceModel; + + /** + * Initializes dependencies + * + * @param Quote $quoteResourceModel + */ + public function __construct(Quote $quoteResourceModel) + { + $this->quoteResourceModel = $quoteResourceModel; + } + + /** + * Set "trigger_recollect" flag for active quotes which the given rule is applied to. + * + * @param int $ruleId + * @return void + */ + public function execute(int $ruleId): void + { + $this->quoteResourceModel->getConnection() + ->update( + $this->quoteResourceModel->getMainTable(), + ['trigger_recollect' => 1], + [ + 'is_active = ?' => 1, + 'FIND_IN_SET(?, applied_rule_ids)' => $ruleId + ] + ); + } +} diff --git a/app/code/Magento/SalesRule/Model/Spi/QuoteResetAppliedRulesInterface.php b/app/code/Magento/SalesRule/Model/Spi/QuoteResetAppliedRulesInterface.php new file mode 100644 index 0000000000000..ba73ab83608db --- /dev/null +++ b/app/code/Magento/SalesRule/Model/Spi/QuoteResetAppliedRulesInterface.php @@ -0,0 +1,22 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +declare(strict_types=1); + +namespace Magento\SalesRule\Model\Spi; + +/** + * Reset applied rules to quote + */ +interface QuoteResetAppliedRulesInterface +{ + /** + * Reset applied rules to quote + * + * @param \Magento\Quote\Api\Data\CartInterface $quote + * @return void + */ + public function execute(\Magento\Quote\Api\Data\CartInterface $quote): void; +} diff --git a/app/code/Magento/SalesRule/Model/Spi/RuleQuoteRecollectTotalsInterface.php b/app/code/Magento/SalesRule/Model/Spi/RuleQuoteRecollectTotalsInterface.php new file mode 100644 index 0000000000000..e5562b22db6ca --- /dev/null +++ b/app/code/Magento/SalesRule/Model/Spi/RuleQuoteRecollectTotalsInterface.php @@ -0,0 +1,23 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ + +declare(strict_types=1); + +namespace Magento\SalesRule\Model\Spi; + +/** + * Recollect totals for rule related quotes + */ +interface RuleQuoteRecollectTotalsInterface +{ + /** + * Recollect totals for rule related quotes. + * + * @param int $ruleId + * @return void + */ + public function execute(int $ruleId): void; +} diff --git a/app/code/Magento/SalesRule/Model/Validator.php b/app/code/Magento/SalesRule/Model/Validator.php index dad35165051ce..cdaac97fe6fb5 100644 --- a/app/code/Magento/SalesRule/Model/Validator.php +++ b/app/code/Magento/SalesRule/Model/Validator.php @@ -383,13 +383,7 @@ public function initTotals($items, Address $address) foreach ($items as $item) { //Skipping child items to avoid double calculations - if ($item->getParentItemId()) { - continue; - } - if (!$rule->getActions()->validate($item)) { - continue; - } - if (!$this->canApplyDiscount($item)) { + if (!$this->isValidItemForRule($item, $rule)) { continue; } $qty = $this->validatorUtility->getItemQty($item, $rule); @@ -409,6 +403,30 @@ public function initTotals($items, Address $address) return $this; } + /** + * Determine if quote item is valid for a given sales rule + * + * @param AbstractItem $item + * @param Rule $rule + * @return bool + */ + private function isValidItemForRule(AbstractItem $item, Rule $rule) + { + if ($item->getParentItemId()) { + return false; + } + if ($item->getParentItem()) { + return false; + } + if (!$rule->getActions()->validate($item)) { + return false; + } + if (!$this->canApplyDiscount($item)) { + return false; + } + return true; + } + /** * Return item price * diff --git a/app/code/Magento/SalesRule/Observer/QuoteResetAppliedRulesObserver.php b/app/code/Magento/SalesRule/Observer/QuoteResetAppliedRulesObserver.php new file mode 100644 index 0000000000000..d5ca055849d75 --- /dev/null +++ b/app/code/Magento/SalesRule/Observer/QuoteResetAppliedRulesObserver.php @@ -0,0 +1,39 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +declare(strict_types=1); + +namespace Magento\SalesRule\Observer; + +use Magento\Framework\Event\Observer; +use Magento\Framework\Event\ObserverInterface; +use Magento\SalesRule\Model\Spi\QuoteResetAppliedRulesInterface; + +/** + * Reset applied rules to quote before collecting totals + */ +class QuoteResetAppliedRulesObserver implements ObserverInterface +{ + /** + * @var QuoteResetAppliedRulesInterface + */ + private $resetAppliedRules; + + /** + * @param QuoteResetAppliedRulesInterface $resetAppliedRules + */ + public function __construct(QuoteResetAppliedRulesInterface $resetAppliedRules) + { + $this->resetAppliedRules = $resetAppliedRules; + } + + /** + * @inheritDoc + */ + public function execute(Observer $observer) + { + $this->resetAppliedRules->execute($observer->getQuote()); + } +} diff --git a/app/code/Magento/SalesRule/Observer/RuleQuoteRecollectTotalsObserver.php b/app/code/Magento/SalesRule/Observer/RuleQuoteRecollectTotalsObserver.php new file mode 100644 index 0000000000000..c2d73d59c020b --- /dev/null +++ b/app/code/Magento/SalesRule/Observer/RuleQuoteRecollectTotalsObserver.php @@ -0,0 +1,50 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ + +declare(strict_types=1); + +namespace Magento\SalesRule\Observer; + +use Magento\Framework\Event\Observer; +use Magento\Framework\Event\ObserverInterface; +use Magento\SalesRule\Model\Rule; +use Magento\SalesRule\Model\Spi\RuleQuoteRecollectTotalsInterface; + +/** + * Forces related quotes to be recollected for inactive rule. + */ +class RuleQuoteRecollectTotalsObserver implements ObserverInterface +{ + /** + * @var RuleQuoteRecollectTotalsInterface + */ + private $recollectTotals; + + /** + * Initializes dependencies + * + * @param RuleQuoteRecollectTotalsInterface $recollectTotals + */ + public function __construct(RuleQuoteRecollectTotalsInterface $recollectTotals) + { + $this->recollectTotals = $recollectTotals; + } + + /** + * Forces related quotes to be recollected, if the rule was disabled or deleted. + * + * @param Observer $observer + * @return void + */ + public function execute(Observer $observer): void + { + /** @var Rule $rule */ + $rule = $observer->getRule(); + if (!$rule->isObjectNew() && (!$rule->getIsActive() || $rule->isDeleted())) { + $this->recollectTotals->execute((int) $rule->getId()); + } + } +} diff --git a/app/code/Magento/SalesRule/Plugin/CouponUsagesDecrement.php b/app/code/Magento/SalesRule/Plugin/CouponUsagesDecrement.php index 5f28632a54cea..87a7c2ed1bd38 100644 --- a/app/code/Magento/SalesRule/Plugin/CouponUsagesDecrement.php +++ b/app/code/Magento/SalesRule/Plugin/CouponUsagesDecrement.php @@ -7,7 +7,8 @@ namespace Magento\SalesRule\Plugin; -use Magento\Sales\Model\Order; +use Magento\Sales\Model\OrderRepository; +use Magento\Sales\Model\Service\OrderService; use Magento\SalesRule\Model\Coupon\UpdateCouponUsages; /** @@ -20,30 +21,39 @@ class CouponUsagesDecrement */ private $updateCouponUsages; + /** + * @var OrderRepository + */ + private $orderRepository; + /** * @param UpdateCouponUsages $updateCouponUsages + * @param OrderRepository $orderRepository */ public function __construct( - UpdateCouponUsages $updateCouponUsages + UpdateCouponUsages $updateCouponUsages, + OrderRepository $orderRepository ) { $this->updateCouponUsages = $updateCouponUsages; + $this->orderRepository = $orderRepository; } /** * Decrements number of coupon usages after cancelling order. * - * @param Order $subject - * @param callable $proceed - * @return Order + * @param OrderService $subject + * @param bool $result + * @param int $orderId + * @return bool + * @SuppressWarnings(PHPMD.UnusedFormalParameter) */ - public function aroundCancel(Order $subject, callable $proceed): Order + public function afterCancel(OrderService $subject, bool $result, $orderId): bool { - $canCancel = $subject->canCancel(); - $returnValue = $proceed(); - if ($canCancel) { - $returnValue = $this->updateCouponUsages->execute($returnValue, false); + $order = $this->orderRepository->get($orderId); + if ($result) { + $this->updateCouponUsages->execute($order, false); } - return $returnValue; + return $result; } } diff --git a/app/code/Magento/SalesRule/Plugin/CouponUsagesIncrement.php b/app/code/Magento/SalesRule/Plugin/CouponUsagesIncrement.php index 473a368afb25a..14bbb5fce02a5 100644 --- a/app/code/Magento/SalesRule/Plugin/CouponUsagesIncrement.php +++ b/app/code/Magento/SalesRule/Plugin/CouponUsagesIncrement.php @@ -7,7 +7,8 @@ namespace Magento\SalesRule\Plugin; -use Magento\Sales\Model\Order; +use Magento\Sales\Api\Data\OrderInterface; +use Magento\Sales\Model\Service\OrderService; use Magento\SalesRule\Model\Coupon\UpdateCouponUsages; /** @@ -32,15 +33,15 @@ public function __construct( /** * Increments number of coupon usages after placing order. * - * @param Order $subject - * @param Order $result - * @return Order + * @param OrderService $subject + * @param OrderInterface $result + * @return OrderInterface * @SuppressWarnings(PHPMD.UnusedFormalParameter) */ - public function afterPlace(Order $subject, Order $result): Order + public function afterPlace(OrderService $subject, OrderInterface $result): OrderInterface { - $this->updateCouponUsages->execute($subject, true); + $this->updateCouponUsages->execute($result, true); - return $subject; + return $result; } } diff --git a/app/code/Magento/SalesRule/Test/Mftf/ActionGroup/StorefrontApplyDiscountCodeActionGroup.xml b/app/code/Magento/SalesRule/Test/Mftf/ActionGroup/StorefrontApplyDiscountCodeActionGroup.xml index 063409e9fc7ea..c8363a3df6221 100644 --- a/app/code/Magento/SalesRule/Test/Mftf/ActionGroup/StorefrontApplyDiscountCodeActionGroup.xml +++ b/app/code/Magento/SalesRule/Test/Mftf/ActionGroup/StorefrontApplyDiscountCodeActionGroup.xml @@ -15,7 +15,7 @@ <click selector="{{DiscountSection.DiscountTab}}" stepKey="clickToAddDiscount"/> <fillField selector="{{DiscountSection.DiscountInput}}" userInput="{{discountCode}}" stepKey="fillFieldDiscountCode"/> <click selector="{{DiscountSection.ApplyCodeBtn}}" stepKey="clickToApplyDiscount"/> - <waitForPageLoad stepKey="waitForDiscountToBeAdded"/> + <waitForElementVisible selector="{{DiscountSection.DiscountVerificationMsg}}" time="30" stepKey="waitForDiscountToBeAdded"/> <see selector="{{DiscountSection.DiscountVerificationMsg}}" userInput="Your coupon was successfully applied" stepKey="assertDiscountApplyMessage"/> </actionGroup> </actionGroups> diff --git a/app/code/Magento/SalesRule/Test/Mftf/Metadata/sales_rule-condition-meta.xml b/app/code/Magento/SalesRule/Test/Mftf/Metadata/SalesRuleConditionMeta.xml similarity index 100% rename from app/code/Magento/SalesRule/Test/Mftf/Metadata/sales_rule-condition-meta.xml rename to app/code/Magento/SalesRule/Test/Mftf/Metadata/SalesRuleConditionMeta.xml diff --git a/app/code/Magento/SalesRule/Test/Mftf/Metadata/sales_rule-coupon-meta.xml b/app/code/Magento/SalesRule/Test/Mftf/Metadata/SalesRuleCouponMeta.xml similarity index 100% rename from app/code/Magento/SalesRule/Test/Mftf/Metadata/sales_rule-coupon-meta.xml rename to app/code/Magento/SalesRule/Test/Mftf/Metadata/SalesRuleCouponMeta.xml diff --git a/app/code/Magento/SalesRule/Test/Mftf/Metadata/sales_rule-label-meta.xml b/app/code/Magento/SalesRule/Test/Mftf/Metadata/SalesRuleLabelMeta.xml similarity index 100% rename from app/code/Magento/SalesRule/Test/Mftf/Metadata/sales_rule-label-meta.xml rename to app/code/Magento/SalesRule/Test/Mftf/Metadata/SalesRuleLabelMeta.xml diff --git a/app/code/Magento/SalesRule/Test/Mftf/Metadata/sales_rule-meta.xml b/app/code/Magento/SalesRule/Test/Mftf/Metadata/SalesRuleMeta.xml similarity index 100% rename from app/code/Magento/SalesRule/Test/Mftf/Metadata/sales_rule-meta.xml rename to app/code/Magento/SalesRule/Test/Mftf/Metadata/SalesRuleMeta.xml diff --git a/app/code/Magento/SalesRule/Test/Mftf/Section/DiscountSection.xml b/app/code/Magento/SalesRule/Test/Mftf/Section/DiscountSection.xml index 45dce325f3cb8..90b591a7bb1b1 100644 --- a/app/code/Magento/SalesRule/Test/Mftf/Section/DiscountSection.xml +++ b/app/code/Magento/SalesRule/Test/Mftf/Section/DiscountSection.xml @@ -13,7 +13,7 @@ <element name="DiscountInput" type="input" selector="#discount-code"/> <element name="ApplyCodeBtn" type="button" selector="//span[text()='Apply Discount']"/> <element name="CancelCoupon" type="button" selector="//button[@value='Cancel Coupon']"/> - <element name="DiscountVerificationMsg" type="button" selector=".message-success div"/> + <element name="DiscountVerificationMsg" type="text" selector=".message-success div"/> <element name="CancelCouponBtn" type="button" selector="#discount-form .action-cancel"/> </section> </sections> diff --git a/app/code/Magento/SalesRule/Test/Mftf/Test/AdminCartRulesAppliedForProductInCartTest.xml b/app/code/Magento/SalesRule/Test/Mftf/Test/AdminCartRulesAppliedForProductInCartTest.xml index 916416dcd9141..1433d660d3535 100644 --- a/app/code/Magento/SalesRule/Test/Mftf/Test/AdminCartRulesAppliedForProductInCartTest.xml +++ b/app/code/Magento/SalesRule/Test/Mftf/Test/AdminCartRulesAppliedForProductInCartTest.xml @@ -28,7 +28,7 @@ <field key="quantity">500</field> </createData> - <actionGroup ref="LoginAsAdmin" stepKey="loginAsAdmin"/> + <actionGroup ref="AdminLoginActionGroup" stepKey="loginAsAdmin"/> </before> <after> @@ -46,7 +46,7 @@ </actionGroup> <actionGroup ref="ClearFiltersAdminDataGridActionGroup" stepKey="clearFilters1"/> - <actionGroup ref="logout" stepKey="logout"/> + <actionGroup ref="AdminLogoutActionGroup" stepKey="logout"/> </after> <!--Start creating a bundle product--> diff --git a/app/code/Magento/SalesRule/Test/Mftf/Test/AdminCreateBuyXGetYFreeTest.xml b/app/code/Magento/SalesRule/Test/Mftf/Test/AdminCreateBuyXGetYFreeTest.xml index addb65a68fad3..ed2dd16b7df9d 100644 --- a/app/code/Magento/SalesRule/Test/Mftf/Test/AdminCreateBuyXGetYFreeTest.xml +++ b/app/code/Magento/SalesRule/Test/Mftf/Test/AdminCreateBuyXGetYFreeTest.xml @@ -21,7 +21,7 @@ <before> <createData entity="_defaultCategory" stepKey="createPreReqCategory"/> - <actionGroup ref="LoginAsAdmin" stepKey="loginAsAdmin"/> + <actionGroup ref="AdminLoginActionGroup" stepKey="loginAsAdmin"/> </before> <after> @@ -30,7 +30,7 @@ <argument name="ruleName" value="{{_defaultCoupon.code}}"/> </actionGroup> <deleteData createDataKey="createPreReqCategory" stepKey="deletePreReqCategory"/> - <actionGroup ref="logout" stepKey="adminLogout"/> + <actionGroup ref="AdminLogoutActionGroup" stepKey="adminLogout"/> </after> <!-- Create a cart price rule of type Buy X get Y free --> diff --git a/app/code/Magento/SalesRule/Test/Mftf/Test/AdminCreateCartPriceRuleAndVerifyRuleConditionAndFreeShippingIsAppliedTest.xml b/app/code/Magento/SalesRule/Test/Mftf/Test/AdminCreateCartPriceRuleAndVerifyRuleConditionAndFreeShippingIsAppliedTest.xml index 0d23e0e035432..34152ea06745c 100644 --- a/app/code/Magento/SalesRule/Test/Mftf/Test/AdminCreateCartPriceRuleAndVerifyRuleConditionAndFreeShippingIsAppliedTest.xml +++ b/app/code/Magento/SalesRule/Test/Mftf/Test/AdminCreateCartPriceRuleAndVerifyRuleConditionAndFreeShippingIsAppliedTest.xml @@ -19,7 +19,7 @@ </annotations> <before> - <actionGroup ref = "LoginAsAdmin" stepKey="loginAsAdmin"/> + <actionGroup ref="AdminLoginActionGroup" stepKey="loginAsAdmin"/> <createData entity="defaultSimpleProduct" stepKey="initialSimpleProduct"/> </before> <after> @@ -27,7 +27,7 @@ <actionGroup ref="DeleteCartPriceRuleByName" stepKey="deleteCreatedCartPriceRule"> <argument name="ruleName" value="{{CartPriceRuleConditionAndFreeShippingApplied.name}}"/> </actionGroup> - <actionGroup ref="logout" stepKey="logout"/> + <actionGroup ref="AdminLogoutActionGroup" stepKey="logout"/> </after> <!--Create cart price rule as per data and verify AssertCartPriceRuleSuccessSaveMessage--> diff --git a/app/code/Magento/SalesRule/Test/Mftf/Test/AdminCreateCartPriceRuleAndVerifyRuleConditionIsNotAppliedTest.xml b/app/code/Magento/SalesRule/Test/Mftf/Test/AdminCreateCartPriceRuleAndVerifyRuleConditionIsNotAppliedTest.xml index b41e139536223..9ac73ceae586e 100644 --- a/app/code/Magento/SalesRule/Test/Mftf/Test/AdminCreateCartPriceRuleAndVerifyRuleConditionIsNotAppliedTest.xml +++ b/app/code/Magento/SalesRule/Test/Mftf/Test/AdminCreateCartPriceRuleAndVerifyRuleConditionIsNotAppliedTest.xml @@ -22,14 +22,14 @@ <magentoCLI command="config:set {{EnableFlatRateConfigData.path}} {{EnableFlatRateConfigData.value}}" stepKey="enableFlatRate"/> <magentoCLI command="config:set {{EnableFlatRateDefaultPriceConfigData.path}} {{EnableFlatRateDefaultPriceConfigData.value}}" stepKey="enableFlatRateDefaultPrice"/> <createData entity="defaultSimpleProduct" stepKey="initialSimpleProduct"/> - <actionGroup ref="LoginAsAdmin" stepKey="loginAsAdmin"/> + <actionGroup ref="AdminLoginActionGroup" stepKey="loginAsAdmin"/> </before> <after> <deleteData stepKey="deleteProduct" createDataKey="initialSimpleProduct"/> <actionGroup ref="DeleteCartPriceRuleByName" stepKey="deleteCreatedCartPriceRule"> <argument name="ruleName" value="{{CartPriceRuleConditionNotApplied.name}}"/> </actionGroup> - <actionGroup ref="logout" stepKey="logoutAsAdmin"/> + <actionGroup ref="AdminLogoutActionGroup" stepKey="logoutAsAdmin"/> </after> <!--Create cart price rule as per data and verify AssertCartPriceRuleSuccessSaveMessage--> diff --git a/app/code/Magento/SalesRule/Test/Mftf/Test/AdminCreateCartPriceRuleEmptyFromDateTest.xml b/app/code/Magento/SalesRule/Test/Mftf/Test/AdminCreateCartPriceRuleEmptyFromDateTest.xml index f0075a92a2b82..67e75d63e016e 100644 --- a/app/code/Magento/SalesRule/Test/Mftf/Test/AdminCreateCartPriceRuleEmptyFromDateTest.xml +++ b/app/code/Magento/SalesRule/Test/Mftf/Test/AdminCreateCartPriceRuleEmptyFromDateTest.xml @@ -24,7 +24,7 @@ <createData entity="SimpleProduct" stepKey="product"> <requiredEntity createDataKey="category"/> </createData> - <actionGroup ref="LoginAsAdmin" stepKey="loginAsAdmin"/> + <actionGroup ref="AdminLoginActionGroup" stepKey="loginAsAdmin"/> </before> <after> @@ -34,7 +34,7 @@ </actionGroup> <deleteData createDataKey="category" stepKey="deleteCategory"/> <deleteData createDataKey="product" stepKey="deleteProduct"/> - <actionGroup ref="logout" stepKey="logout"/> + <actionGroup ref="AdminLogoutActionGroup" stepKey="logout"/> </after> <!--Set timezone--> diff --git a/app/code/Magento/SalesRule/Test/Mftf/Test/AdminCreateCartPriceRuleForCouponCodeTest.xml b/app/code/Magento/SalesRule/Test/Mftf/Test/AdminCreateCartPriceRuleForCouponCodeTest.xml index 6242c1f3d1baf..2bf96cf0377d4 100644 --- a/app/code/Magento/SalesRule/Test/Mftf/Test/AdminCreateCartPriceRuleForCouponCodeTest.xml +++ b/app/code/Magento/SalesRule/Test/Mftf/Test/AdminCreateCartPriceRuleForCouponCodeTest.xml @@ -21,7 +21,7 @@ <before> <createData entity="_defaultCategory" stepKey="createPreReqCategory"/> - <actionGroup ref="LoginAsAdmin" stepKey="loginAsAdmin"/> + <actionGroup ref="AdminLoginActionGroup" stepKey="loginAsAdmin"/> </before> <after> @@ -30,7 +30,7 @@ <argument name="ruleName" value="{{_defaultCoupon.code}}"/> </actionGroup> <deleteData createDataKey="createPreReqCategory" stepKey="deletePreReqCategory"/> - <actionGroup ref="logout" stepKey="logout"/> + <actionGroup ref="AdminLogoutActionGroup" stepKey="logout"/> </after> <!-- Create a cart price rule based on a coupon code --> diff --git a/app/code/Magento/SalesRule/Test/Mftf/Test/AdminCreateCartPriceRuleForGeneratedCouponTest.xml b/app/code/Magento/SalesRule/Test/Mftf/Test/AdminCreateCartPriceRuleForGeneratedCouponTest.xml index 78943a0648b51..9833231f8ed84 100644 --- a/app/code/Magento/SalesRule/Test/Mftf/Test/AdminCreateCartPriceRuleForGeneratedCouponTest.xml +++ b/app/code/Magento/SalesRule/Test/Mftf/Test/AdminCreateCartPriceRuleForGeneratedCouponTest.xml @@ -21,7 +21,7 @@ <before> <createData entity="_defaultCategory" stepKey="createPreReqCategory"/> - <actionGroup ref="LoginAsAdmin" stepKey="loginAsAdmin"/> + <actionGroup ref="AdminLoginActionGroup" stepKey="loginAsAdmin"/> </before> <after> @@ -30,7 +30,7 @@ <argument name="ruleName" value="{{_defaultCoupon.code}}"/> </actionGroup> <deleteData createDataKey="createPreReqCategory" stepKey="deletePreReqCategory"/> - <actionGroup ref="logout" stepKey="adminLogout"/> + <actionGroup ref="AdminLogoutActionGroup" stepKey="adminLogout"/> </after> <!-- Create a cart price rule --> diff --git a/app/code/Magento/SalesRule/Test/Mftf/Test/AdminCreateCartPriceRuleForMatchingSubtotalAndVerifyRuleConditionIsAppliedTest.xml b/app/code/Magento/SalesRule/Test/Mftf/Test/AdminCreateCartPriceRuleForMatchingSubtotalAndVerifyRuleConditionIsAppliedTest.xml index 385c9e35da393..34714e9637d46 100644 --- a/app/code/Magento/SalesRule/Test/Mftf/Test/AdminCreateCartPriceRuleForMatchingSubtotalAndVerifyRuleConditionIsAppliedTest.xml +++ b/app/code/Magento/SalesRule/Test/Mftf/Test/AdminCreateCartPriceRuleForMatchingSubtotalAndVerifyRuleConditionIsAppliedTest.xml @@ -19,7 +19,7 @@ </annotations> <before> - <actionGroup ref = "LoginAsAdmin" stepKey="loginAsAdmin"/> + <actionGroup ref="AdminLoginActionGroup" stepKey="loginAsAdmin"/> <createData entity="defaultSimpleProduct" stepKey="initialSimpleProduct"/> </before> <after> @@ -27,7 +27,7 @@ <actionGroup ref="DeleteCartPriceRuleByName" stepKey="deleteCreatedCartPriceRule"> <argument name="ruleName" value="{{CartPriceRuleConditionAppliedForSubtotal.name}}"/> </actionGroup> - <actionGroup ref="logout" stepKey="logout"/> + <actionGroup ref="AdminLogoutActionGroup" stepKey="logout"/> </after> <!--Create cart price rule as per data and verify AssertCartPriceRuleSuccessSaveMessage--> diff --git a/app/code/Magento/SalesRule/Test/Mftf/Test/AdminCreateCartPriceRuleWithMatchingCategoryAndVerifyRuleConditionIsAppliedTest.xml b/app/code/Magento/SalesRule/Test/Mftf/Test/AdminCreateCartPriceRuleWithMatchingCategoryAndVerifyRuleConditionIsAppliedTest.xml index a56e332ce1a85..a3e6331e31cf6 100644 --- a/app/code/Magento/SalesRule/Test/Mftf/Test/AdminCreateCartPriceRuleWithMatchingCategoryAndVerifyRuleConditionIsAppliedTest.xml +++ b/app/code/Magento/SalesRule/Test/Mftf/Test/AdminCreateCartPriceRuleWithMatchingCategoryAndVerifyRuleConditionIsAppliedTest.xml @@ -19,7 +19,7 @@ </annotations> <before> - <actionGroup ref = "LoginAsAdmin" stepKey="loginAsAdmin"/> + <actionGroup ref="AdminLoginActionGroup" stepKey="loginAsAdmin"/> <createData entity="_defaultCategory" stepKey="createCategory"/> <actionGroup ref="FillAdminSimpleProductFormActionGroup" stepKey="fillProductFieldsInAdmin"> <argument name="category" value="$$createCategory$$"/> @@ -34,7 +34,7 @@ <actionGroup ref="DeleteCartPriceRuleByName" stepKey="deleteCreatedCartPriceRule"> <argument name="ruleName" value="{{CartPriceRuleConditionAppliedForCategory.name}}"/> </actionGroup> - <actionGroup ref="logout" stepKey="logout"/> + <actionGroup ref="AdminLogoutActionGroup" stepKey="logout"/> </after> <!--Create cart price rule as per data and verify AssertCartPriceRuleSuccessSaveMessage--> diff --git a/app/code/Magento/SalesRule/Test/Mftf/Test/AdminCreateCartPriceRuleWithMatchingTotalWeightAndVerifyRuleConditionIsAppliedTest.xml b/app/code/Magento/SalesRule/Test/Mftf/Test/AdminCreateCartPriceRuleWithMatchingTotalWeightAndVerifyRuleConditionIsAppliedTest.xml index 0541ee2dd4eba..e9f7f3ec6c70a 100644 --- a/app/code/Magento/SalesRule/Test/Mftf/Test/AdminCreateCartPriceRuleWithMatchingTotalWeightAndVerifyRuleConditionIsAppliedTest.xml +++ b/app/code/Magento/SalesRule/Test/Mftf/Test/AdminCreateCartPriceRuleWithMatchingTotalWeightAndVerifyRuleConditionIsAppliedTest.xml @@ -19,7 +19,7 @@ </annotations> <before> - <actionGroup ref = "LoginAsAdmin" stepKey="loginAsAdmin"/> + <actionGroup ref="AdminLoginActionGroup" stepKey="loginAsAdmin"/> <createData entity="defaultSimpleProductWeight200" stepKey="initialSimpleProduct"/> </before> <after> @@ -27,7 +27,7 @@ <actionGroup ref="DeleteCartPriceRuleByName" stepKey="deleteCreatedCartPriceRule"> <argument name="ruleName" value="{{CartPriceRuleConditionAppliedForWeight.name}}"/> </actionGroup> - <actionGroup ref="logout" stepKey="logout"/> + <actionGroup ref="AdminLogoutActionGroup" stepKey="logout"/> </after> <!--Create cart price rule as per data and verify AssertCartPriceRuleSuccessSaveMessage--> diff --git a/app/code/Magento/SalesRule/Test/Mftf/Test/AdminCreateFixedAmountDiscountTest.xml b/app/code/Magento/SalesRule/Test/Mftf/Test/AdminCreateFixedAmountDiscountTest.xml index c65a5a865e779..0d98abfba3f62 100644 --- a/app/code/Magento/SalesRule/Test/Mftf/Test/AdminCreateFixedAmountDiscountTest.xml +++ b/app/code/Magento/SalesRule/Test/Mftf/Test/AdminCreateFixedAmountDiscountTest.xml @@ -21,7 +21,7 @@ <before> <createData entity="_defaultCategory" stepKey="createPreReqCategory"/> - <actionGroup ref="LoginAsAdmin" stepKey="loginAsAdmin"/> + <actionGroup ref="AdminLoginActionGroup" stepKey="loginAsAdmin"/> </before> <after> @@ -30,7 +30,7 @@ <argument name="ruleName" value="{{_defaultCoupon.code}}"/> </actionGroup> <deleteData createDataKey="createPreReqCategory" stepKey="deletePreReqCategory"/> - <actionGroup ref="logout" stepKey="adminLogout"/> + <actionGroup ref="AdminLogoutActionGroup" stepKey="adminLogout"/> </after> <!-- Create a cart price rule for $10 Fixed amount discount --> diff --git a/app/code/Magento/SalesRule/Test/Mftf/Test/AdminCreateFixedAmountWholeCartDiscountTest.xml b/app/code/Magento/SalesRule/Test/Mftf/Test/AdminCreateFixedAmountWholeCartDiscountTest.xml index 69ba7aef393b5..bc4139435ab55 100644 --- a/app/code/Magento/SalesRule/Test/Mftf/Test/AdminCreateFixedAmountWholeCartDiscountTest.xml +++ b/app/code/Magento/SalesRule/Test/Mftf/Test/AdminCreateFixedAmountWholeCartDiscountTest.xml @@ -21,7 +21,7 @@ <before> <createData entity="_defaultCategory" stepKey="createPreReqCategory"/> - <actionGroup ref="LoginAsAdmin" stepKey="loginAsAdmin"/> + <actionGroup ref="AdminLoginActionGroup" stepKey="loginAsAdmin"/> </before> <after> @@ -30,7 +30,7 @@ <argument name="ruleName" value="{{SimpleSalesRule.name}}"/> </actionGroup> <deleteData createDataKey="createPreReqCategory" stepKey="deletePreReqCategory"/> - <actionGroup ref="logout" stepKey="adminLogout"/> + <actionGroup ref="AdminLogoutActionGroup" stepKey="adminLogout"/> </after> <!-- Create a cart price rule for Fixed amount discount for whole cart --> diff --git a/app/code/Magento/SalesRule/Test/Mftf/Test/AdminCreateInvalidRuleTest.xml b/app/code/Magento/SalesRule/Test/Mftf/Test/AdminCreateInvalidRuleTest.xml index 620112e323ff5..56c4506196d24 100644 --- a/app/code/Magento/SalesRule/Test/Mftf/Test/AdminCreateInvalidRuleTest.xml +++ b/app/code/Magento/SalesRule/Test/Mftf/Test/AdminCreateInvalidRuleTest.xml @@ -19,11 +19,11 @@ </annotations> <before> - <actionGroup ref="LoginAsAdmin" stepKey="loginAsAdmin"/> + <actionGroup ref="AdminLoginActionGroup" stepKey="loginAsAdmin"/> </before> <after> - <actionGroup ref="logout" stepKey="adminLogout"/> + <actionGroup ref="AdminLogoutActionGroup" stepKey="adminLogout"/> </after> <amOnPage url="{{AdminCartPriceRulesPage.url}}" stepKey="amOnCartPriceList"/> diff --git a/app/code/Magento/SalesRule/Test/Mftf/Test/AdminCreatePercentOfProductPriceTest.xml b/app/code/Magento/SalesRule/Test/Mftf/Test/AdminCreatePercentOfProductPriceTest.xml index fd3ebd2ad11d7..23e472518ba84 100644 --- a/app/code/Magento/SalesRule/Test/Mftf/Test/AdminCreatePercentOfProductPriceTest.xml +++ b/app/code/Magento/SalesRule/Test/Mftf/Test/AdminCreatePercentOfProductPriceTest.xml @@ -21,7 +21,9 @@ <before> <createData entity="_defaultCategory" stepKey="createPreReqCategory"/> - <actionGroup ref="LoginAsAdmin" stepKey="loginAsAdmin"/> + <!-- Reindex invalidated indices after product attribute has been created/deleted --> + <magentoCron groups="index" stepKey="reindexInvalidatedIndices"/> + <actionGroup ref="AdminLoginActionGroup" stepKey="loginAsAdmin"/> </before> <after> @@ -30,7 +32,7 @@ <argument name="ruleName" value="{{_defaultCoupon.code}}"/> </actionGroup> <deleteData createDataKey="createPreReqCategory" stepKey="deletePreReqCategory"/> - <actionGroup ref="logout" stepKey="adminLogout"/> + <actionGroup ref="AdminLogoutActionGroup" stepKey="adminLogout"/> </after> <!-- Create a cart price rule for 50 percent of product price --> diff --git a/app/code/Magento/SalesRule/Test/Mftf/Test/AdminDeleteActiveSalesRuleWithComplexConditionsAndVerifyDeleteMessageTest.xml b/app/code/Magento/SalesRule/Test/Mftf/Test/AdminDeleteActiveSalesRuleWithComplexConditionsAndVerifyDeleteMessageTest.xml index 9a71210aac1c6..3aacc176acdc5 100644 --- a/app/code/Magento/SalesRule/Test/Mftf/Test/AdminDeleteActiveSalesRuleWithComplexConditionsAndVerifyDeleteMessageTest.xml +++ b/app/code/Magento/SalesRule/Test/Mftf/Test/AdminDeleteActiveSalesRuleWithComplexConditionsAndVerifyDeleteMessageTest.xml @@ -19,7 +19,7 @@ </annotations> <before> - <actionGroup ref="LoginAsAdmin" stepKey="loginAsAdmin"/> + <actionGroup ref="AdminLoginActionGroup" stepKey="loginAsAdmin"/> <!--Create active cart price rule--> <actionGroup ref="AdminCreateCartPriceRuleRuleInfoSectionActionGroup" stepKey="createActiveCartPriceRuleRuleInfoSection"> <argument name="ruleName" value="ActiveSalesRuleWithComplexConditions"/> @@ -62,7 +62,7 @@ <actionGroup ref="AssertCartPriceRuleSuccessSaveMessageActionGroup" stepKey="assertVerifyCartPriceRuleSuccessSaveMessage"/> </before> <after> - <actionGroup ref="logout" stepKey="logout"/> + <actionGroup ref="AdminLogoutActionGroup" stepKey="logout"/> </after> <!--Delete active cart price rule--> diff --git a/app/code/Magento/SalesRule/Test/Mftf/Test/AdminDeleteActiveSalesRuleWithPercentPriceAndVerifyDeleteMessageTest.xml b/app/code/Magento/SalesRule/Test/Mftf/Test/AdminDeleteActiveSalesRuleWithPercentPriceAndVerifyDeleteMessageTest.xml index 34b8363e8b5ce..8c02f401992ee 100644 --- a/app/code/Magento/SalesRule/Test/Mftf/Test/AdminDeleteActiveSalesRuleWithPercentPriceAndVerifyDeleteMessageTest.xml +++ b/app/code/Magento/SalesRule/Test/Mftf/Test/AdminDeleteActiveSalesRuleWithPercentPriceAndVerifyDeleteMessageTest.xml @@ -20,7 +20,7 @@ </annotations> <before> - <actionGroup ref="LoginAsAdmin" stepKey="loginAsAdmin"/> + <actionGroup ref="AdminLoginActionGroup" stepKey="loginAsAdmin"/> <!--Create active cart price rule--> <actionGroup ref="AdminCreateCartPriceRuleWithCouponCodeActionGroup" stepKey="createActiveCartPriceRule"> <argument name="ruleName" value="ActiveSalesRuleWithPercentPriceDiscountCoupon"/> @@ -28,7 +28,7 @@ </actionGroup> </before> <after> - <actionGroup ref="logout" stepKey="logout"/> + <actionGroup ref="AdminLogoutActionGroup" stepKey="logout"/> </after> <!--Delete active cart price rule--> diff --git a/app/code/Magento/SalesRule/Test/Mftf/Test/AdminDeleteInactiveSalesRuleAndVerifyDeleteMessageTest.xml b/app/code/Magento/SalesRule/Test/Mftf/Test/AdminDeleteInactiveSalesRuleAndVerifyDeleteMessageTest.xml index 39a5d0f6a7131..18183085060d2 100644 --- a/app/code/Magento/SalesRule/Test/Mftf/Test/AdminDeleteInactiveSalesRuleAndVerifyDeleteMessageTest.xml +++ b/app/code/Magento/SalesRule/Test/Mftf/Test/AdminDeleteInactiveSalesRuleAndVerifyDeleteMessageTest.xml @@ -20,7 +20,7 @@ </annotations> <before> - <actionGroup ref="LoginAsAdmin" stepKey="loginAsAdmin"/> + <actionGroup ref="AdminLoginActionGroup" stepKey="loginAsAdmin"/> <createData entity="defaultSimpleProduct" stepKey="initialSimpleProduct"/> <!--Create inactive cart price rule--> <actionGroup ref="AdminInactiveCartPriceRuleActionGroup" stepKey="createInactiveCartPriceRule"> @@ -30,7 +30,7 @@ </before> <after> <deleteData createDataKey="initialSimpleProduct" stepKey="deleteProduct"/> - <actionGroup ref="logout" stepKey="logout"/> + <actionGroup ref="AdminLogoutActionGroup" stepKey="logout"/> </after> <!--Delete inactive cart price rule--> diff --git a/app/code/Magento/SalesRule/Test/Mftf/Test/AdminMarketingCartPriceRulesNavigateMenuTest.xml b/app/code/Magento/SalesRule/Test/Mftf/Test/AdminMarketingCartPriceRulesNavigateMenuTest.xml index f281b0abf87a0..d001ac98eefeb 100644 --- a/app/code/Magento/SalesRule/Test/Mftf/Test/AdminMarketingCartPriceRulesNavigateMenuTest.xml +++ b/app/code/Magento/SalesRule/Test/Mftf/Test/AdminMarketingCartPriceRulesNavigateMenuTest.xml @@ -20,10 +20,10 @@ <group value="mtf_migrated"/> </annotations> <before> - <actionGroup ref="LoginAsAdmin" stepKey="LoginAsAdmin"/> + <actionGroup ref="AdminLoginActionGroup" stepKey="LoginAsAdmin"/> </before> <after> - <actionGroup ref="logout" stepKey="logout"/> + <actionGroup ref="AdminLogoutActionGroup" stepKey="logout"/> </after> <actionGroup ref="AdminNavigateMenuActionGroup" stepKey="navigateToMarketingCartPriceRulesPage"> <argument name="menuUiId" value="{{AdminMenuMarketing.dataUiId}}"/> diff --git a/app/code/Magento/SalesRule/Test/Mftf/Test/CartPriceRuleForConfigurableProductTest.xml b/app/code/Magento/SalesRule/Test/Mftf/Test/CartPriceRuleForConfigurableProductTest.xml index ec9246f7c33bd..bc608c0e06086 100644 --- a/app/code/Magento/SalesRule/Test/Mftf/Test/CartPriceRuleForConfigurableProductTest.xml +++ b/app/code/Magento/SalesRule/Test/Mftf/Test/CartPriceRuleForConfigurableProductTest.xml @@ -74,7 +74,10 @@ <requiredEntity createDataKey="createConfigProduct"/> <requiredEntity createDataKey="createConfigChildProduct2"/> </createData> - <actionGroup ref="LoginAsAdmin" stepKey="loginAsAdmin"/> + + <magentoCLI command="cron:run --group=index" stepKey="runCronIndexer"/> + + <actionGroup ref="AdminLoginActionGroup" stepKey="loginAsAdmin"/> </before> <after> @@ -86,7 +89,10 @@ <deleteData createDataKey="createConfigChildProduct2" stepKey="deleteConfigChildProduct2"/> <deleteData createDataKey="createConfigProductAttribute" stepKey="deleteConfigProductAttribute"/> <deleteData createDataKey="createCategory" stepKey="deleteApiCategory"/> - <actionGroup ref="logout" stepKey="logout"/> + <actionGroup ref="AdminLogoutActionGroup" stepKey="logout"/> + + <!-- Reindex invalidated indices after product attribute has been created/deleted --> + <magentoCron groups="index" stepKey="reindexInvalidatedIndices"/> </after> <!-- Create the rule --> diff --git a/app/code/Magento/SalesRule/Test/Mftf/Test/EndToEndB2CGuestUserTest.xml b/app/code/Magento/SalesRule/Test/Mftf/Test/EndToEndB2CGuestUserTest.xml deleted file mode 100644 index e4e9a62780948..0000000000000 --- a/app/code/Magento/SalesRule/Test/Mftf/Test/EndToEndB2CGuestUserTest.xml +++ /dev/null @@ -1,89 +0,0 @@ -<?xml version="1.0" encoding="UTF-8"?> -<!-- - /** - * Copyright © Magento, Inc. All rights reserved. - * See COPYING.txt for license details. - */ ---> - -<tests xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" - xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/testSchema.xsd"> - <test name="EndToEndB2CGuestUserTest"> - <before> - <createData entity="ApiSalesRule" stepKey="createSalesRule"/> - <createData entity="ApiSalesRuleCoupon" stepKey="createSalesRuleCoupon"> - <requiredEntity createDataKey="createSalesRule"/> - </createData> - </before> - <after> - <deleteData createDataKey="createSalesRule" stepKey="deleteSalesRule"/> - </after> - - <!-- Step 5: User uses coupon codes --> - <comment userInput="Start of using coupon code" stepKey="startOfUsingCouponCode" after="endOfComparingProducts" /> - <actionGroup ref="StorefrontOpenCartFromMinicartActionGroup" stepKey="couponOpenCart" after="startOfUsingCouponCode"/> - - <actionGroup ref="StorefrontApplyCouponActionGroup" stepKey="couponApplyCoupon" after="couponOpenCart"> - <argument name="coupon" value="$$createSalesRuleCoupon$$"/> - </actionGroup> - - <actionGroup ref="StorefrontCheckCouponAppliedActionGroup" stepKey="couponCheckAppliedDiscount" after="couponApplyCoupon"> - <argument name="rule" value="$$createSalesRule$$"/> - <argument name="discount" value="48.00"/> - </actionGroup> - <actionGroup ref="StorefrontCheckCartActionGroup" stepKey="couponCheckCartWithDiscount" after="couponCheckAppliedDiscount"> - <argument name="subtotal" value="480.00"/> - <argument name="shipping" value="15.00"/> - <argument name="shippingMethod" value="Flat Rate - Fixed"/> - <argument name="total" value="447.00"/> - </actionGroup> - - <actionGroup ref="StorefrontCancelCouponActionGroup" stepKey="couponCancelCoupon" after="couponCheckCartWithDiscount"/> - <actionGroup ref="StorefrontCheckCartActionGroup" stepKey="cartAssertCartAfterCancelCoupon" after="couponCancelCoupon"> - <argument name="subtotal" value="480.00"/> - <argument name="shipping" value="15.00"/> - <argument name="shippingMethod" value="Flat Rate - Fixed"/> - <argument name="total" value="495.00"/> - </actionGroup> - <comment userInput="End of using coupon code" stepKey="endOfUsingCouponCode" after="cartAssertCartAfterCancelCoupon" /> - </test> - <test name="EndToEndB2CGuestUserMysqlTest"> - <before> - <createData entity="ApiSalesRule" stepKey="createSalesRule"/> - <createData entity="ApiSalesRuleCoupon" stepKey="createSalesRuleCoupon"> - <requiredEntity createDataKey="createSalesRule"/> - </createData> - </before> - <after> - <deleteData createDataKey="createSalesRule" stepKey="deleteSalesRule"/> - </after> - - <!-- Step 5: User uses coupon codes --> - <comment userInput="Start of using coupon code" stepKey="startOfUsingCouponCode" after="endOfComparingProducts" /> - <actionGroup ref="StorefrontOpenCartFromMinicartActionGroup" stepKey="couponOpenCart" after="startOfUsingCouponCode"/> - - <actionGroup ref="StorefrontApplyCouponActionGroup" stepKey="couponApplyCoupon" after="couponOpenCart"> - <argument name="coupon" value="$$createSalesRuleCoupon$$"/> - </actionGroup> - - <actionGroup ref="StorefrontCheckCouponAppliedActionGroup" stepKey="couponCheckAppliedDiscount" after="couponApplyCoupon"> - <argument name="rule" value="$$createSalesRule$$"/> - <argument name="discount" value="48.00"/> - </actionGroup> - <actionGroup ref="StorefrontCheckCartActionGroup" stepKey="couponCheckCartWithDiscount" after="couponCheckAppliedDiscount"> - <argument name="subtotal" value="480.00"/> - <argument name="shipping" value="15.00"/> - <argument name="shippingMethod" value="Flat Rate - Fixed"/> - <argument name="total" value="447.00"/> - </actionGroup> - - <actionGroup ref="StorefrontCancelCouponActionGroup" stepKey="couponCancelCoupon" after="couponCheckCartWithDiscount"/> - <actionGroup ref="StorefrontCheckCartActionGroup" stepKey="cartAssertCartAfterCancelCoupon" after="couponCancelCoupon"> - <argument name="subtotal" value="480.00"/> - <argument name="shipping" value="15.00"/> - <argument name="shippingMethod" value="Flat Rate - Fixed"/> - <argument name="total" value="495.00"/> - </actionGroup> - <comment userInput="End of using coupon code" stepKey="endOfUsingCouponCode" after="cartAssertCartAfterCancelCoupon" /> - </test> -</tests> diff --git a/app/code/Magento/SalesRule/Test/Mftf/Test/EndToEndB2CGuestUserTest/EndToEndB2CGuestUserMysqlTest.xml b/app/code/Magento/SalesRule/Test/Mftf/Test/EndToEndB2CGuestUserTest/EndToEndB2CGuestUserMysqlTest.xml new file mode 100644 index 0000000000000..c128c5aeeff7a --- /dev/null +++ b/app/code/Magento/SalesRule/Test/Mftf/Test/EndToEndB2CGuestUserTest/EndToEndB2CGuestUserMysqlTest.xml @@ -0,0 +1,49 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + /** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +--> +<tests xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/testSchema.xsd"> + <test name="EndToEndB2CGuestUserMysqlTest"> + <before> + <createData entity="ApiSalesRule" stepKey="createSalesRule"/> + <createData entity="ApiSalesRuleCoupon" stepKey="createSalesRuleCoupon"> + <requiredEntity createDataKey="createSalesRule"/> + </createData> + </before> + <after> + <deleteData createDataKey="createSalesRule" stepKey="deleteSalesRule"/> + </after> + + <!-- Step 5: User uses coupon codes --> + <comment userInput="Start of using coupon code" stepKey="startOfUsingCouponCode" after="endOfComparingProducts"/> + <actionGroup ref="StorefrontOpenCartFromMinicartActionGroup" stepKey="couponOpenCart" after="startOfUsingCouponCode"/> + + <actionGroup ref="StorefrontApplyCouponActionGroup" stepKey="couponApplyCoupon" after="couponOpenCart"> + <argument name="coupon" value="$$createSalesRuleCoupon$$"/> + </actionGroup> + + <actionGroup ref="StorefrontCheckCouponAppliedActionGroup" stepKey="couponCheckAppliedDiscount" after="couponApplyCoupon"> + <argument name="rule" value="$$createSalesRule$$"/> + <argument name="discount" value="48.00"/> + </actionGroup> + <actionGroup ref="StorefrontCheckCartActionGroup" stepKey="couponCheckCartWithDiscount" after="couponCheckAppliedDiscount"> + <argument name="subtotal" value="480.00"/> + <argument name="shipping" value="15.00"/> + <argument name="shippingMethod" value="Flat Rate - Fixed"/> + <argument name="total" value="447.00"/> + </actionGroup> + + <actionGroup ref="StorefrontCancelCouponActionGroup" stepKey="couponCancelCoupon" after="couponCheckCartWithDiscount"/> + <actionGroup ref="StorefrontCheckCartActionGroup" stepKey="cartAssertCartAfterCancelCoupon" after="couponCancelCoupon"> + <argument name="subtotal" value="480.00"/> + <argument name="shipping" value="15.00"/> + <argument name="shippingMethod" value="Flat Rate - Fixed"/> + <argument name="total" value="495.00"/> + </actionGroup> + <comment userInput="End of using coupon code" stepKey="endOfUsingCouponCode" after="cartAssertCartAfterCancelCoupon"/> + </test> +</tests> diff --git a/app/code/Magento/SalesRule/Test/Mftf/Test/EndToEndB2CGuestUserTest/EndToEndB2CGuestUserTest.xml b/app/code/Magento/SalesRule/Test/Mftf/Test/EndToEndB2CGuestUserTest/EndToEndB2CGuestUserTest.xml new file mode 100644 index 0000000000000..f227e8cf3e5e0 --- /dev/null +++ b/app/code/Magento/SalesRule/Test/Mftf/Test/EndToEndB2CGuestUserTest/EndToEndB2CGuestUserTest.xml @@ -0,0 +1,49 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + /** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +--> +<tests xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/testSchema.xsd"> + <test name="EndToEndB2CGuestUserTest"> + <before> + <createData entity="ApiSalesRule" stepKey="createSalesRule"/> + <createData entity="ApiSalesRuleCoupon" stepKey="createSalesRuleCoupon"> + <requiredEntity createDataKey="createSalesRule"/> + </createData> + </before> + <after> + <deleteData createDataKey="createSalesRule" stepKey="deleteSalesRule"/> + </after> + + <!-- Step 5: User uses coupon codes --> + <comment userInput="Start of using coupon code" stepKey="startOfUsingCouponCode" after="endOfComparingProducts"/> + <actionGroup ref="StorefrontOpenCartFromMinicartActionGroup" stepKey="couponOpenCart" after="startOfUsingCouponCode"/> + + <actionGroup ref="StorefrontApplyCouponActionGroup" stepKey="couponApplyCoupon" after="couponOpenCart"> + <argument name="coupon" value="$$createSalesRuleCoupon$$"/> + </actionGroup> + + <actionGroup ref="StorefrontCheckCouponAppliedActionGroup" stepKey="couponCheckAppliedDiscount" after="couponApplyCoupon"> + <argument name="rule" value="$$createSalesRule$$"/> + <argument name="discount" value="48.00"/> + </actionGroup> + <actionGroup ref="StorefrontCheckCartActionGroup" stepKey="couponCheckCartWithDiscount" after="couponCheckAppliedDiscount"> + <argument name="subtotal" value="480.00"/> + <argument name="shipping" value="15.00"/> + <argument name="shippingMethod" value="Flat Rate - Fixed"/> + <argument name="total" value="447.00"/> + </actionGroup> + + <actionGroup ref="StorefrontCancelCouponActionGroup" stepKey="couponCancelCoupon" after="couponCheckCartWithDiscount"/> + <actionGroup ref="StorefrontCheckCartActionGroup" stepKey="cartAssertCartAfterCancelCoupon" after="couponCancelCoupon"> + <argument name="subtotal" value="480.00"/> + <argument name="shipping" value="15.00"/> + <argument name="shippingMethod" value="Flat Rate - Fixed"/> + <argument name="total" value="495.00"/> + </actionGroup> + <comment userInput="End of using coupon code" stepKey="endOfUsingCouponCode" after="cartAssertCartAfterCancelCoupon"/> + </test> +</tests> diff --git a/app/code/Magento/SalesRule/Test/Mftf/Test/PriceRuleCategoryNestingTest.xml b/app/code/Magento/SalesRule/Test/Mftf/Test/PriceRuleCategoryNestingTest.xml index 091e09e32f1e6..91fe4f142d570 100644 --- a/app/code/Magento/SalesRule/Test/Mftf/Test/PriceRuleCategoryNestingTest.xml +++ b/app/code/Magento/SalesRule/Test/Mftf/Test/PriceRuleCategoryNestingTest.xml @@ -32,10 +32,10 @@ </before> <after> <deleteData createDataKey="subcategory1" stepKey="deleteCategory1"/> - <amOnPage url="{{_ENV.MAGENTO_BACKEND_NAME}}/admin/auth/logout/" stepKey="amOnLogoutPage"/> + <actionGroup ref="AdminLogoutActionGroup" stepKey="adminLogout"/> </after> <!-- Login as admin and open page for creation new Price Rule --> - <actionGroup ref="LoginAsAdmin" stepKey="loginAsAdmin1"/> + <actionGroup ref="AdminLoginActionGroup" stepKey="loginAsAdmin1"/> <amOnPage url="{{PriceRuleNewPage.url}}" stepKey="openCatalogPriceRulePage"/> <waitForPageLoad stepKey="waitCatalogPriceRulePageLoad"/> <!-- Open Conditions section and select Categories condition --> diff --git a/app/code/Magento/SalesRule/Test/Mftf/Test/StorefrontAutoGeneratedCouponCodeTest.xml b/app/code/Magento/SalesRule/Test/Mftf/Test/StorefrontAutoGeneratedCouponCodeTest.xml index 84537fb69ed41..09b45cd554056 100644 --- a/app/code/Magento/SalesRule/Test/Mftf/Test/StorefrontAutoGeneratedCouponCodeTest.xml +++ b/app/code/Magento/SalesRule/Test/Mftf/Test/StorefrontAutoGeneratedCouponCodeTest.xml @@ -15,7 +15,7 @@ <title value="[Cart Price Rule] Auto generated coupon code considers 'Uses per Coupon' and 'Uses per Customer' options"/> <description value="[Cart Price Rule] Auto generated coupon code considers 'Uses per Coupon' and 'Uses per Customer' options"/> - <severity value="AVERAGE"/> + <severity value="BLOCKER"/> <testCaseId value="MAGETWO-59323"/> <group value="salesRule"/> </annotations> @@ -34,10 +34,10 @@ <deleteData createDataKey="createSalesRule" stepKey="deleteSalesRule"/> <deleteData createDataKey="createCustomer" stepKey="deleteCustomer"/> <deleteData createDataKey="createSimpleProduct" stepKey="deleteSimpleProduct"/> - <actionGroup ref="logout" stepKey="logout"/> + <actionGroup ref="AdminLogoutActionGroup" stepKey="logout"/> </after> <!-- Login as Admin --> - <actionGroup ref="LoginAsAdmin" stepKey="loginAsAdmin"/> + <actionGroup ref="AdminLoginActionGroup" stepKey="loginAsAdmin"/> <!-- Search Cart Price Rule and go to edit Cart Price Rule --> <amOnPage url="{{AdminCartPriceRulesPage.url}}" stepKey="amOnCartPriceList"/> diff --git a/app/code/Magento/SalesRule/Test/Mftf/Test/StorefrontCartPriceRuleCountry.xml b/app/code/Magento/SalesRule/Test/Mftf/Test/StorefrontCartPriceRuleCountry.xml deleted file mode 100644 index 832b9ef8bd4b4..0000000000000 --- a/app/code/Magento/SalesRule/Test/Mftf/Test/StorefrontCartPriceRuleCountry.xml +++ /dev/null @@ -1,92 +0,0 @@ -<?xml version="1.0" encoding="UTF-8"?> -<!-- - /** - * Copyright © Magento, Inc. All rights reserved. - * See COPYING.txt for license details. - */ ---> - -<tests xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" - xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/testSchema.xsd"> - <test name="StorefrontCartPriceRuleCountry"> - <annotations> - <features value="SalesRule"/> - <stories value="Create cart price rule"/> - <title value="Customer should only see cart price rule discount if condition shipping country"/> - <description value="Customer should only see cart price rule discount if condition shipping country"/> - <severity value="AVERAGE"/> - <testCaseId value="MC-241"/> - <group value="SalesRule"/> - </annotations> - - <before> - <createData entity="_defaultCategory" stepKey="createPreReqCategory"/> - <createData entity="_defaultProduct" stepKey="createPreReqProduct"> - <requiredEntity createDataKey="createPreReqCategory"/> - </createData> - <actionGroup ref="LoginAsAdmin" stepKey="loginAsAdmin"/> - </before> - - <after> - <actionGroup ref="DeleteCartPriceRuleByName" stepKey="cleanUpRule"> - <argument name="ruleName" value="{{SimpleSalesRule.name}}"/> - </actionGroup> - <deleteData createDataKey="createPreReqCategory" stepKey="deletePreReqCategory"/> - <deleteData createDataKey="createPreReqProduct" stepKey="deletePreReqProduct"/> - <amOnPage url="admin/admin/auth/logout/" stepKey="amOnLogoutPage"/> - </after> - - <!-- Create the rule... --> - <amOnPage url="{{AdminCartPriceRulesPage.url}}" stepKey="amOnCartPriceList"/> - <waitForPageLoad stepKey="waitForRulesPage"/> - <click selector="{{AdminCartPriceRulesSection.addNewRuleButton}}" stepKey="clickAddNewRule"/> - <fillField selector="{{AdminCartPriceRulesFormSection.ruleName}}" userInput="{{SimpleSalesRule.name}}" stepKey="fillRuleName"/> - <selectOption selector="{{AdminCartPriceRulesFormSection.websites}}" userInput="Main Website" stepKey="selectWebsites"/> - <actionGroup ref="SelectNotLoggedInCustomerGroupActionGroup" stepKey="selectNotLoggedInCustomerGroup"/> - <generateDate date="-1 day" format="m/d/Y" stepKey="yesterdayDate"/> - <fillField selector="{{AdminCartPriceRulesFormSection.fromDate}}" userInput="{$yesterdayDate}" stepKey="fillFromDate"/> - <click selector="{{PriceRuleConditionsSection.conditionsTab}}" stepKey="expandConditions"/> - <!-- Scroll down to fix some flaky behavior... --> - <scrollTo selector="{{PriceRuleConditionsSection.conditionsTab}}" stepKey="scrollToConditionsTab"/> - <waitForElementVisible selector="{{PriceRuleConditionsSection.createNewRule}}" stepKey="waitForNewRule"/> - <click selector="{{PriceRuleConditionsSection.createNewRule}}" stepKey="clickNewRule"/> - <selectOption selector="{{PriceRuleConditionsSection.rulesDropdown}}" userInput="Shipping Country" stepKey="selectProductAttributes"/> - <waitForPageLoad stepKey="wait1"/> - <click selector="{{PriceRuleConditionsSection.ruleParamLink('1', '1', '2')}}" stepKey="startEditValue"/> - <waitForPageLoad stepKey="wait4"/> - <selectOption selector="{{PriceRuleConditionsSection.valueByIndex('1', '1')}}" userInput="Brazil" stepKey="fillValue"/> - <waitForPageLoad stepKey="wait5"/> - <click selector="{{AdminCartPriceRulesFormSection.actionsHeader}}" stepKey="clickToExpandActions"/> - <selectOption selector="{{AdminCartPriceRulesFormSection.apply}}" userInput="Fixed amount discount for whole cart" stepKey="selectActionType"/> - <fillField selector="{{AdminCartPriceRulesFormSection.discountAmount}}" userInput="9.99" stepKey="fillDiscountAmount"/> - <click selector="{{AdminCartPriceRulesFormSection.save}}" stepKey="clickSaveButton"/> - <see selector="{{AdminCartPriceRulesSection.messages}}" userInput="You saved the rule." stepKey="seeSuccessMessage"/> - - <!-- Add the product we created to our cart --> - <amOnPage url="$$createPreReqProduct.name$$.html" stepKey="goToProductPage"/> - <waitForPageLoad stepKey="waitForProductPageLoad"/> - <fillField selector="{{StorefrontProductActionSection.quantity}}" userInput="1" stepKey="fillQuantity"/> - <click selector="{{StorefrontProductActionSection.addToCart}}" stepKey="addProductToCart"/> - <waitForPageLoad stepKey="waitForAddToCart"/> - <waitForElementVisible selector="{{StorefrontMessagesSection.success}}" stepKey="waitForSuccessMessage"/> - - <!-- Should not see the discount yet because we have not set country --> - <amOnPage url="{{CheckoutCartPage.url}}" stepKey="goToCartPage"/> - <waitForPageLoad stepKey="waitForCartPage"/> - <click selector="{{CheckoutCartSummarySection.shippingHeading}}" stepKey="openEstimateShippingSection"/> - <checkOption selector="{{CheckoutCartSummarySection.flatRateShippingMethod}}" stepKey="selectFlatRateShipping"/> - <see selector="{{CheckoutCartSummarySection.subtotal}}" userInput="$123.00" stepKey="seeSubtotal"/> - <dontSeeElement selector="{{CheckoutCartSummarySection.discountAmount}}" stepKey="dontSeeDiscount"/> - - <!-- See discount if we use valid country --> - <selectOption selector="{{CheckoutCartSummarySection.country}}" userInput="Brazil" stepKey="fillCountry"/> - <waitForPageLoad stepKey="waitForCountry1"/> - <waitForElementVisible selector="{{CheckoutCartSummarySection.discountAmount}}" stepKey="waitForDiscountElement"/> - <see selector="{{CheckoutCartSummarySection.discountAmount}}" userInput="-$9.99" stepKey="seeDiscountTotal"/> - - <!-- Do not see discount with other country --> - <selectOption selector="{{CheckoutCartSummarySection.country}}" userInput="United States" stepKey="fillCountry2"/> - <waitForPageLoad stepKey="waitForCountry2"/> - <dontSeeElement selector="{{CheckoutCartSummarySection.discountAmount}}" stepKey="dontSeeDiscount2"/> - </test> -</tests> diff --git a/app/code/Magento/SalesRule/Test/Mftf/Test/StorefrontCartPriceRuleCountryTest.xml b/app/code/Magento/SalesRule/Test/Mftf/Test/StorefrontCartPriceRuleCountryTest.xml new file mode 100644 index 0000000000000..4cc331eebc4ee --- /dev/null +++ b/app/code/Magento/SalesRule/Test/Mftf/Test/StorefrontCartPriceRuleCountryTest.xml @@ -0,0 +1,92 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + /** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +--> + +<tests xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/testSchema.xsd"> + <test name="StorefrontCartPriceRuleCountryTest"> + <annotations> + <features value="SalesRule"/> + <stories value="Create cart price rule"/> + <title value="Customer should only see cart price rule discount if condition shipping country"/> + <description value="Customer should only see cart price rule discount if condition shipping country"/> + <severity value="AVERAGE"/> + <testCaseId value="MC-241"/> + <group value="SalesRule"/> + </annotations> + + <before> + <createData entity="_defaultCategory" stepKey="createPreReqCategory"/> + <createData entity="_defaultProduct" stepKey="createPreReqProduct"> + <requiredEntity createDataKey="createPreReqCategory"/> + </createData> + <actionGroup ref="AdminLoginActionGroup" stepKey="loginAsAdmin"/> + </before> + + <after> + <actionGroup ref="DeleteCartPriceRuleByName" stepKey="cleanUpRule"> + <argument name="ruleName" value="{{SimpleSalesRule.name}}"/> + </actionGroup> + <deleteData createDataKey="createPreReqCategory" stepKey="deletePreReqCategory"/> + <deleteData createDataKey="createPreReqProduct" stepKey="deletePreReqProduct"/> + <amOnPage url="admin/admin/auth/logout/" stepKey="amOnLogoutPage"/> + </after> + + <!-- Create the rule... --> + <amOnPage url="{{AdminCartPriceRulesPage.url}}" stepKey="amOnCartPriceList"/> + <waitForPageLoad stepKey="waitForRulesPage"/> + <click selector="{{AdminCartPriceRulesSection.addNewRuleButton}}" stepKey="clickAddNewRule"/> + <fillField selector="{{AdminCartPriceRulesFormSection.ruleName}}" userInput="{{SimpleSalesRule.name}}" stepKey="fillRuleName"/> + <selectOption selector="{{AdminCartPriceRulesFormSection.websites}}" userInput="Main Website" stepKey="selectWebsites"/> + <actionGroup ref="SelectNotLoggedInCustomerGroupActionGroup" stepKey="selectNotLoggedInCustomerGroup"/> + <generateDate date="-1 day" format="m/d/Y" stepKey="yesterdayDate"/> + <fillField selector="{{AdminCartPriceRulesFormSection.fromDate}}" userInput="{$yesterdayDate}" stepKey="fillFromDate"/> + <click selector="{{PriceRuleConditionsSection.conditionsTab}}" stepKey="expandConditions"/> + <!-- Scroll down to fix some flaky behavior... --> + <scrollTo selector="{{PriceRuleConditionsSection.conditionsTab}}" stepKey="scrollToConditionsTab"/> + <waitForElementVisible selector="{{PriceRuleConditionsSection.createNewRule}}" stepKey="waitForNewRule"/> + <click selector="{{PriceRuleConditionsSection.createNewRule}}" stepKey="clickNewRule"/> + <selectOption selector="{{PriceRuleConditionsSection.rulesDropdown}}" userInput="Shipping Country" stepKey="selectProductAttributes"/> + <waitForPageLoad stepKey="wait1"/> + <click selector="{{PriceRuleConditionsSection.ruleParamLink('1', '1', '2')}}" stepKey="startEditValue"/> + <waitForPageLoad stepKey="wait4"/> + <selectOption selector="{{PriceRuleConditionsSection.valueByIndex('1', '1')}}" userInput="Brazil" stepKey="fillValue"/> + <waitForPageLoad stepKey="wait5"/> + <click selector="{{AdminCartPriceRulesFormSection.actionsHeader}}" stepKey="clickToExpandActions"/> + <selectOption selector="{{AdminCartPriceRulesFormSection.apply}}" userInput="Fixed amount discount for whole cart" stepKey="selectActionType"/> + <fillField selector="{{AdminCartPriceRulesFormSection.discountAmount}}" userInput="9.99" stepKey="fillDiscountAmount"/> + <click selector="{{AdminCartPriceRulesFormSection.save}}" stepKey="clickSaveButton"/> + <see selector="{{AdminCartPriceRulesSection.messages}}" userInput="You saved the rule." stepKey="seeSuccessMessage"/> + + <!-- Add the product we created to our cart --> + <amOnPage url="$$createPreReqProduct.name$$.html" stepKey="goToProductPage"/> + <waitForPageLoad stepKey="waitForProductPageLoad"/> + <fillField selector="{{StorefrontProductActionSection.quantity}}" userInput="1" stepKey="fillQuantity"/> + <click selector="{{StorefrontProductActionSection.addToCart}}" stepKey="addProductToCart"/> + <waitForPageLoad stepKey="waitForAddToCart"/> + <waitForElementVisible selector="{{StorefrontMessagesSection.success}}" stepKey="waitForSuccessMessage"/> + + <!-- Should not see the discount yet because we have not set country --> + <amOnPage url="{{CheckoutCartPage.url}}" stepKey="goToCartPage"/> + <waitForPageLoad stepKey="waitForCartPage"/> + <click selector="{{CheckoutCartSummarySection.shippingHeading}}" stepKey="openEstimateShippingSection"/> + <checkOption selector="{{CheckoutCartSummarySection.flatRateShippingMethod}}" stepKey="selectFlatRateShipping"/> + <see selector="{{CheckoutCartSummarySection.subtotal}}" userInput="$123.00" stepKey="seeSubtotal"/> + <dontSeeElement selector="{{CheckoutCartSummarySection.discountAmount}}" stepKey="dontSeeDiscount"/> + + <!-- See discount if we use valid country --> + <selectOption selector="{{CheckoutCartSummarySection.country}}" userInput="Brazil" stepKey="fillCountry"/> + <waitForPageLoad stepKey="waitForCountry1"/> + <waitForElementVisible selector="{{CheckoutCartSummarySection.discountAmount}}" stepKey="waitForDiscountElement"/> + <see selector="{{CheckoutCartSummarySection.discountAmount}}" userInput="-$9.99" stepKey="seeDiscountTotal"/> + + <!-- Do not see discount with other country --> + <selectOption selector="{{CheckoutCartSummarySection.country}}" userInput="United States" stepKey="fillCountry2"/> + <waitForPageLoad stepKey="waitForCountry2"/> + <dontSeeElement selector="{{CheckoutCartSummarySection.discountAmount}}" stepKey="dontSeeDiscount2"/> + </test> +</tests> diff --git a/app/code/Magento/SalesRule/Test/Mftf/Test/StorefrontCartPriceRulePostcode.xml b/app/code/Magento/SalesRule/Test/Mftf/Test/StorefrontCartPriceRulePostcode.xml deleted file mode 100644 index 9882b04bdc956..0000000000000 --- a/app/code/Magento/SalesRule/Test/Mftf/Test/StorefrontCartPriceRulePostcode.xml +++ /dev/null @@ -1,96 +0,0 @@ -<?xml version="1.0" encoding="UTF-8"?> -<!-- - /** - * Copyright © Magento, Inc. All rights reserved. - * See COPYING.txt for license details. - */ ---> - -<tests xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" - xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/testSchema.xsd"> - <test name="StorefrontCartPriceRulePostcode"> - <annotations> - <features value="SalesRule"/> - <stories value="Create cart price rule"/> - <title value="Customer should only see cart price rule discount if condition shipping postcode"/> - <description value="Customer should only see cart price rule discount if condition shipping postcode"/> - <severity value="AVERAGE"/> - <testCaseId value="MC-238"/> - <group value="SalesRule"/> - </annotations> - - <before> - <createData entity="_defaultCategory" stepKey="createPreReqCategory"/> - <createData entity="_defaultProduct" stepKey="createPreReqProduct"> - <requiredEntity createDataKey="createPreReqCategory"/> - </createData> - <actionGroup ref="LoginAsAdmin" stepKey="loginAsAdmin"/> - </before> - - <after> - <actionGroup ref="DeleteCartPriceRuleByName" stepKey="cleanUpRule"> - <argument name="ruleName" value="{{SimpleSalesRule.name}}"/> - </actionGroup> - <deleteData createDataKey="createPreReqCategory" stepKey="deletePreReqCategory"/> - <deleteData createDataKey="createPreReqProduct" stepKey="deletePreReqProduct"/> - <amOnPage url="admin/admin/auth/logout/" stepKey="amOnLogoutPage"/> - </after> - - <!-- Create the rule... --> - <amOnPage url="{{AdminCartPriceRulesPage.url}}" stepKey="amOnCartPriceList"/> - <waitForPageLoad stepKey="waitForRulesPage"/> - <click selector="{{AdminCartPriceRulesSection.addNewRuleButton}}" stepKey="clickAddNewRule"/> - <fillField selector="{{AdminCartPriceRulesFormSection.ruleName}}" userInput="{{SimpleSalesRule.name}}" stepKey="fillRuleName"/> - <selectOption selector="{{AdminCartPriceRulesFormSection.websites}}" userInput="Main Website" stepKey="selectWebsites"/> - <actionGroup ref="SelectNotLoggedInCustomerGroupActionGroup" stepKey="selectNotLoggedInCustomerGroup"/> - <generateDate date="-1 day" format="m/d/Y" stepKey="yesterdayDate"/> - <fillField selector="{{AdminCartPriceRulesFormSection.fromDate}}" userInput="{$yesterdayDate}" stepKey="fillFromDate"/> - <click selector="{{PriceRuleConditionsSection.conditionsTab}}" stepKey="expandConditions"/> - <!-- Scroll down to fix some flaky behavior... --> - <scrollTo selector="{{PriceRuleConditionsSection.conditionsTab}}" stepKey="scrollToConditionsTab"/> - <waitForElementVisible selector="{{PriceRuleConditionsSection.createNewRule}}" stepKey="waitForNewRule"/> - <click selector="{{PriceRuleConditionsSection.createNewRule}}" stepKey="clickNewRule"/> - <selectOption selector="{{PriceRuleConditionsSection.rulesDropdown}}" userInput="Shipping Postcode" stepKey="selectProductAttributes"/> - <waitForPageLoad stepKey="wait1"/> - <click selector="{{PriceRuleConditionsSection.ruleParamLink('1', '1', '1')}}" stepKey="startEditOperator"/> - <waitForPageLoad stepKey="wait2"/> - <selectOption selector="{{PriceRuleConditionsSection.operatorByIndex('1', '1')}}" userInput="is one of" stepKey="fillOperator"/> - <waitForPageLoad stepKey="wait3"/> - <click selector="{{PriceRuleConditionsSection.ruleParamLink('1', '1', '2')}}" stepKey="startEditValue"/> - <waitForPageLoad stepKey="wait4"/> - <fillField selector="{{PriceRuleConditionsSection.valueByIndex('1', '1')}}" userInput="78613" stepKey="fillValue"/> - <waitForPageLoad stepKey="wait5"/> - <click selector="{{AdminCartPriceRulesFormSection.actionsHeader}}" stepKey="clickToExpandActions"/> - <selectOption selector="{{AdminCartPriceRulesFormSection.apply}}" userInput="Fixed amount discount for whole cart" stepKey="selectActionType"/> - <fillField selector="{{AdminCartPriceRulesFormSection.discountAmount}}" userInput="9.99" stepKey="fillDiscountAmount"/> - <click selector="{{AdminCartPriceRulesFormSection.save}}" stepKey="clickSaveButton"/> - <see selector="{{AdminCartPriceRulesSection.messages}}" userInput="You saved the rule." stepKey="seeSuccessMessage"/> - - <!-- Add the product we created to our cart --> - <amOnPage url="$$createPreReqProduct.name$$.html" stepKey="goToProductPage"/> - <waitForPageLoad stepKey="waitForProductPageLoad"/> - <fillField selector="{{StorefrontProductActionSection.quantity}}" userInput="1" stepKey="fillQuantity"/> - <click selector="{{StorefrontProductActionSection.addToCart}}" stepKey="addProductToCart"/> - <waitForPageLoad stepKey="waitForAddToCart"/> - <waitForElementVisible selector="{{StorefrontMessagesSection.success}}" stepKey="waitForSuccessMessage"/> - - <!-- Should not see the discount yet because we have not filled in postcode --> - <amOnPage url="{{CheckoutCartPage.url}}" stepKey="goToCartPage"/> - <waitForPageLoad stepKey="waitForCartPage"/> - <click selector="{{CheckoutCartSummarySection.shippingHeading}}" stepKey="openEstimateShippingSection"/> - <checkOption selector="{{CheckoutCartSummarySection.flatRateShippingMethod}}" stepKey="selectFlatRateShipping"/> - <see selector="{{CheckoutCartSummarySection.subtotal}}" userInput="$123.00" stepKey="seeSubtotal"/> - <dontSeeElement selector="{{CheckoutCartSummarySection.discountAmount}}" stepKey="dontSeeDiscount"/> - - <!-- See discount if we use valid postcode --> - <fillField selector="{{CheckoutCartSummarySection.postcode}}" userInput="78613" stepKey="fillPostcode"/> - <waitForPageLoad stepKey="waitForPostcode1"/> - <waitForElementVisible selector="{{CheckoutCartSummarySection.discountAmount}}" stepKey="waitForDiscountElement"/> - <see selector="{{CheckoutCartSummarySection.discountAmount}}" userInput="-$9.99" stepKey="seeDiscountTotal"/> - - <!-- Do not see discount with other postcode --> - <fillField selector="{{CheckoutCartSummarySection.postcode}}" userInput="90210" stepKey="fillPostcode2"/> - <waitForPageLoad stepKey="waitForPostcode2"/> - <dontSeeElement selector="{{CheckoutCartSummarySection.discountAmount}}" stepKey="dontSeeDiscount2"/> - </test> -</tests> diff --git a/app/code/Magento/SalesRule/Test/Mftf/Test/StorefrontCartPriceRulePostcodeTest.xml b/app/code/Magento/SalesRule/Test/Mftf/Test/StorefrontCartPriceRulePostcodeTest.xml new file mode 100644 index 0000000000000..4d987c00884e1 --- /dev/null +++ b/app/code/Magento/SalesRule/Test/Mftf/Test/StorefrontCartPriceRulePostcodeTest.xml @@ -0,0 +1,96 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + /** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +--> + +<tests xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/testSchema.xsd"> + <test name="StorefrontCartPriceRulePostcodeTest"> + <annotations> + <features value="SalesRule"/> + <stories value="Create cart price rule"/> + <title value="Customer should only see cart price rule discount if condition shipping postcode"/> + <description value="Customer should only see cart price rule discount if condition shipping postcode"/> + <severity value="AVERAGE"/> + <testCaseId value="MC-238"/> + <group value="SalesRule"/> + </annotations> + + <before> + <createData entity="_defaultCategory" stepKey="createPreReqCategory"/> + <createData entity="_defaultProduct" stepKey="createPreReqProduct"> + <requiredEntity createDataKey="createPreReqCategory"/> + </createData> + <actionGroup ref="AdminLoginActionGroup" stepKey="loginAsAdmin"/> + </before> + + <after> + <actionGroup ref="DeleteCartPriceRuleByName" stepKey="cleanUpRule"> + <argument name="ruleName" value="{{SimpleSalesRule.name}}"/> + </actionGroup> + <deleteData createDataKey="createPreReqCategory" stepKey="deletePreReqCategory"/> + <deleteData createDataKey="createPreReqProduct" stepKey="deletePreReqProduct"/> + <amOnPage url="admin/admin/auth/logout/" stepKey="amOnLogoutPage"/> + </after> + + <!-- Create the rule... --> + <amOnPage url="{{AdminCartPriceRulesPage.url}}" stepKey="amOnCartPriceList"/> + <waitForPageLoad stepKey="waitForRulesPage"/> + <click selector="{{AdminCartPriceRulesSection.addNewRuleButton}}" stepKey="clickAddNewRule"/> + <fillField selector="{{AdminCartPriceRulesFormSection.ruleName}}" userInput="{{SimpleSalesRule.name}}" stepKey="fillRuleName"/> + <selectOption selector="{{AdminCartPriceRulesFormSection.websites}}" userInput="Main Website" stepKey="selectWebsites"/> + <actionGroup ref="SelectNotLoggedInCustomerGroupActionGroup" stepKey="selectNotLoggedInCustomerGroup"/> + <generateDate date="-1 day" format="m/d/Y" stepKey="yesterdayDate"/> + <fillField selector="{{AdminCartPriceRulesFormSection.fromDate}}" userInput="{$yesterdayDate}" stepKey="fillFromDate"/> + <click selector="{{PriceRuleConditionsSection.conditionsTab}}" stepKey="expandConditions"/> + <!-- Scroll down to fix some flaky behavior... --> + <scrollTo selector="{{PriceRuleConditionsSection.conditionsTab}}" stepKey="scrollToConditionsTab"/> + <waitForElementVisible selector="{{PriceRuleConditionsSection.createNewRule}}" stepKey="waitForNewRule"/> + <click selector="{{PriceRuleConditionsSection.createNewRule}}" stepKey="clickNewRule"/> + <selectOption selector="{{PriceRuleConditionsSection.rulesDropdown}}" userInput="Shipping Postcode" stepKey="selectProductAttributes"/> + <waitForPageLoad stepKey="wait1"/> + <click selector="{{PriceRuleConditionsSection.ruleParamLink('1', '1', '1')}}" stepKey="startEditOperator"/> + <waitForPageLoad stepKey="wait2"/> + <selectOption selector="{{PriceRuleConditionsSection.operatorByIndex('1', '1')}}" userInput="is one of" stepKey="fillOperator"/> + <waitForPageLoad stepKey="wait3"/> + <click selector="{{PriceRuleConditionsSection.ruleParamLink('1', '1', '2')}}" stepKey="startEditValue"/> + <waitForPageLoad stepKey="wait4"/> + <fillField selector="{{PriceRuleConditionsSection.valueByIndex('1', '1')}}" userInput="78613" stepKey="fillValue"/> + <waitForPageLoad stepKey="wait5"/> + <click selector="{{AdminCartPriceRulesFormSection.actionsHeader}}" stepKey="clickToExpandActions"/> + <selectOption selector="{{AdminCartPriceRulesFormSection.apply}}" userInput="Fixed amount discount for whole cart" stepKey="selectActionType"/> + <fillField selector="{{AdminCartPriceRulesFormSection.discountAmount}}" userInput="9.99" stepKey="fillDiscountAmount"/> + <click selector="{{AdminCartPriceRulesFormSection.save}}" stepKey="clickSaveButton"/> + <see selector="{{AdminCartPriceRulesSection.messages}}" userInput="You saved the rule." stepKey="seeSuccessMessage"/> + + <!-- Add the product we created to our cart --> + <amOnPage url="$$createPreReqProduct.name$$.html" stepKey="goToProductPage"/> + <waitForPageLoad stepKey="waitForProductPageLoad"/> + <fillField selector="{{StorefrontProductActionSection.quantity}}" userInput="1" stepKey="fillQuantity"/> + <click selector="{{StorefrontProductActionSection.addToCart}}" stepKey="addProductToCart"/> + <waitForPageLoad stepKey="waitForAddToCart"/> + <waitForElementVisible selector="{{StorefrontMessagesSection.success}}" stepKey="waitForSuccessMessage"/> + + <!-- Should not see the discount yet because we have not filled in postcode --> + <amOnPage url="{{CheckoutCartPage.url}}" stepKey="goToCartPage"/> + <waitForPageLoad stepKey="waitForCartPage"/> + <click selector="{{CheckoutCartSummarySection.shippingHeading}}" stepKey="openEstimateShippingSection"/> + <checkOption selector="{{CheckoutCartSummarySection.flatRateShippingMethod}}" stepKey="selectFlatRateShipping"/> + <see selector="{{CheckoutCartSummarySection.subtotal}}" userInput="$123.00" stepKey="seeSubtotal"/> + <dontSeeElement selector="{{CheckoutCartSummarySection.discountAmount}}" stepKey="dontSeeDiscount"/> + + <!-- See discount if we use valid postcode --> + <fillField selector="{{CheckoutCartSummarySection.postcode}}" userInput="78613" stepKey="fillPostcode"/> + <waitForPageLoad stepKey="waitForPostcode1"/> + <waitForElementVisible selector="{{CheckoutCartSummarySection.discountAmount}}" stepKey="waitForDiscountElement"/> + <see selector="{{CheckoutCartSummarySection.discountAmount}}" userInput="-$9.99" stepKey="seeDiscountTotal"/> + + <!-- Do not see discount with other postcode --> + <fillField selector="{{CheckoutCartSummarySection.postcode}}" userInput="90210" stepKey="fillPostcode2"/> + <waitForPageLoad stepKey="waitForPostcode2"/> + <dontSeeElement selector="{{CheckoutCartSummarySection.discountAmount}}" stepKey="dontSeeDiscount2"/> + </test> +</tests> diff --git a/app/code/Magento/SalesRule/Test/Mftf/Test/StorefrontCartPriceRuleQuantity.xml b/app/code/Magento/SalesRule/Test/Mftf/Test/StorefrontCartPriceRuleQuantity.xml deleted file mode 100644 index ad060c1412c8c..0000000000000 --- a/app/code/Magento/SalesRule/Test/Mftf/Test/StorefrontCartPriceRuleQuantity.xml +++ /dev/null @@ -1,94 +0,0 @@ -<?xml version="1.0" encoding="UTF-8"?> -<!-- - /** - * Copyright © Magento, Inc. All rights reserved. - * See COPYING.txt for license details. - */ ---> - -<tests xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" - xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/testSchema.xsd"> - <test name="StorefrontCartPriceRuleQuantity"> - <annotations> - <features value="SalesRule"/> - <stories value="Create cart price rule"/> - <title value="Customer should only see cart price rule discount if condition total items quantity greater than"/> - <description value="Customer should only see cart price rule discount if condition total items quantity greater than"/> - <severity value="AVERAGE"/> - <testCaseId value="MC-236"/> - <group value="SalesRule"/> - </annotations> - - <before> - <createData entity="_defaultCategory" stepKey="createPreReqCategory"/> - <createData entity="_defaultProduct" stepKey="createPreReqProduct"> - <requiredEntity createDataKey="createPreReqCategory"/> - </createData> - <actionGroup ref="LoginAsAdmin" stepKey="loginAsAdmin"/> - </before> - - <after> - <actionGroup ref="DeleteCartPriceRuleByName" stepKey="cleanUpRule"> - <argument name="ruleName" value="{{SimpleSalesRule.name}}"/> - </actionGroup> - <deleteData createDataKey="createPreReqCategory" stepKey="deletePreReqCategory"/> - <deleteData createDataKey="createPreReqProduct" stepKey="deletePreReqProduct"/> - <amOnPage url="admin/admin/auth/logout/" stepKey="amOnLogoutPage"/> - </after> - - <!-- Create the rule... --> - <amOnPage url="{{AdminCartPriceRulesPage.url}}" stepKey="amOnCartPriceList"/> - <waitForPageLoad stepKey="waitForRulesPage"/> - <click selector="{{AdminCartPriceRulesSection.addNewRuleButton}}" stepKey="clickAddNewRule"/> - <fillField selector="{{AdminCartPriceRulesFormSection.ruleName}}" userInput="{{SimpleSalesRule.name}}" stepKey="fillRuleName"/> - <selectOption selector="{{AdminCartPriceRulesFormSection.websites}}" userInput="Main Website" stepKey="selectWebsites"/> - <actionGroup ref="SelectNotLoggedInCustomerGroupActionGroup" stepKey="selectNotLoggedInCustomerGroup"/> - <generateDate date="-1 day" format="m/d/Y" stepKey="yesterdayDate"/> - <fillField selector="{{AdminCartPriceRulesFormSection.fromDate}}" userInput="{$yesterdayDate}" stepKey="fillFromDate"/> - <click selector="{{PriceRuleConditionsSection.conditionsTab}}" stepKey="expandConditions"/> - <!-- Scroll down to fix some flaky behavior... --> - <scrollTo selector="{{PriceRuleConditionsSection.conditionsTab}}" stepKey="scrollToConditionsTab"/> - <waitForElementVisible selector="{{PriceRuleConditionsSection.createNewRule}}" stepKey="waitForNewRule"/> - <click selector="{{PriceRuleConditionsSection.createNewRule}}" stepKey="clickNewRule"/> - <selectOption selector="{{PriceRuleConditionsSection.rulesDropdown}}" userInput="Total Items Quantity" stepKey="selectProductAttributes"/> - <waitForPageLoad stepKey="waitForConditions"/> - <click selector="{{PriceRuleConditionsSection.ruleParamLink('1', '1', '1')}}" stepKey="startEditOperator"/> - <selectOption selector="{{PriceRuleConditionsSection.operatorByIndex('1', '1')}}" userInput="greater than" stepKey="fillOperator"/> - <click selector="{{PriceRuleConditionsSection.ruleParamLink('1', '1', '2')}}" stepKey="startEditValue"/> - <fillField selector="{{PriceRuleConditionsSection.valueByIndex('1', '1')}}" userInput="1" stepKey="fillValue"/> - <click selector="{{AdminCartPriceRulesFormSection.actionsHeader}}" stepKey="clickToExpandActions"/> - <selectOption selector="{{AdminCartPriceRulesFormSection.apply}}" userInput="Fixed amount discount for whole cart" stepKey="selectActionType"/> - <fillField selector="{{AdminCartPriceRulesFormSection.discountAmount}}" userInput="1.00" stepKey="fillDiscountAmount"/> - <click selector="{{AdminCartPriceRulesFormSection.save}}" stepKey="clickSaveButton"/> - <see selector="{{AdminCartPriceRulesSection.messages}}" userInput="You saved the rule." stepKey="seeSuccessMessage"/> - - <!-- Add 1 product to the cart --> - <amOnPage url="$$createPreReqProduct.name$$.html" stepKey="goToProductPage"/> - <waitForPageLoad stepKey="waitForProductPageLoad"/> - <fillField selector="{{StorefrontProductActionSection.quantity}}" userInput="1" stepKey="fillQuantity"/> - <click selector="{{StorefrontProductActionSection.addToCart}}" stepKey="addProductToCart"/> - <waitForPageLoad stepKey="waitForAddToCart"/> - <waitForElementVisible selector="{{StorefrontMessagesSection.success}}" stepKey="waitForSuccessMessage"/> - - <!-- Should not see the discount yet because we have only 1 item in our cart --> - <amOnPage url="{{CheckoutCartPage.url}}" stepKey="goToCartPage"/> - <waitForPageLoad stepKey="waitForCartPage"/> - <see selector="{{CheckoutCartSummarySection.subtotal}}" userInput="$123.00" stepKey="seeSubtotal"/> - <dontSeeElement selector="{{CheckoutCartSummarySection.discountAmount}}" stepKey="dontSeeDiscount"/> - - <!-- Add the same product to the cart again (2 total) --> - <amOnPage url="$$createPreReqProduct.name$$.html" stepKey="goToProductPage2"/> - <waitForPageLoad stepKey="waitForProductPageLoad2"/> - <fillField selector="{{StorefrontProductActionSection.quantity}}" userInput="1" stepKey="fillQuantity2"/> - <click selector="{{StorefrontProductActionSection.addToCart}}" stepKey="addProductToCart2"/> - <waitForPageLoad stepKey="waitForAddToCart2"/> - <waitForElementVisible selector="{{StorefrontMessagesSection.success}}" stepKey="waitForSuccessMessage2"/> - - <!-- Now we should see the discount because we have more than 1 item --> - <amOnPage url="{{CheckoutCartPage.url}}" stepKey="goToCartPage2"/> - <waitForPageLoad stepKey="waitForCartPage2"/> - <see selector="{{CheckoutCartSummarySection.subtotal}}" userInput="$246.00" stepKey="seeSubtotal2"/> - <waitForElementVisible selector="{{CheckoutCartSummarySection.discountAmount}}" stepKey="waitForDiscountElement"/> - <see selector="{{CheckoutCartSummarySection.discountAmount}}" userInput="-$1.00" stepKey="seeDiscountTotal"/> - </test> -</tests> diff --git a/app/code/Magento/SalesRule/Test/Mftf/Test/StorefrontCartPriceRuleQuantityTest.xml b/app/code/Magento/SalesRule/Test/Mftf/Test/StorefrontCartPriceRuleQuantityTest.xml new file mode 100644 index 0000000000000..ff0e011210785 --- /dev/null +++ b/app/code/Magento/SalesRule/Test/Mftf/Test/StorefrontCartPriceRuleQuantityTest.xml @@ -0,0 +1,96 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + /** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +--> + +<tests xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/testSchema.xsd"> + <test name="StorefrontCartPriceRuleQuantityTest"> + <annotations> + <features value="SalesRule"/> + <stories value="Create cart price rule"/> + <title value="Customer should only see cart price rule discount if condition total items quantity greater than"/> + <description value="Customer should only see cart price rule discount if condition total items quantity greater than"/> + <severity value="AVERAGE"/> + <testCaseId value="MC-236"/> + <group value="SalesRule"/> + </annotations> + + <before> + <createData entity="_defaultCategory" stepKey="createPreReqCategory"/> + <createData entity="_defaultProduct" stepKey="createPreReqProduct"> + <requiredEntity createDataKey="createPreReqCategory"/> + </createData> + <magentoCron groups="index" stepKey="reindexInvalidatedIndices"/> + <actionGroup ref="AdminLoginActionGroup" stepKey="loginAsAdmin"/> + </before> + + <after> + <actionGroup ref="DeleteCartPriceRuleByName" stepKey="cleanUpRule"> + <argument name="ruleName" value="{{SimpleSalesRule.name}}"/> + </actionGroup> + <deleteData createDataKey="createPreReqCategory" stepKey="deletePreReqCategory"/> + <deleteData createDataKey="createPreReqProduct" stepKey="deletePreReqProduct"/> + <amOnPage url="admin/admin/auth/logout/" stepKey="amOnLogoutPage"/> + </after> + + <!-- Create the rule... --> + <amOnPage url="{{AdminCartPriceRulesPage.url}}" stepKey="amOnCartPriceList"/> + <waitForPageLoad stepKey="waitForRulesPage"/> + <click selector="{{AdminCartPriceRulesSection.addNewRuleButton}}" stepKey="clickAddNewRule"/> + <fillField selector="{{AdminCartPriceRulesFormSection.ruleName}}" userInput="{{SimpleSalesRule.name}}" stepKey="fillRuleName"/> + <selectOption selector="{{AdminCartPriceRulesFormSection.websites}}" userInput="Main Website" stepKey="selectWebsites"/> + <actionGroup ref="SelectNotLoggedInCustomerGroupActionGroup" stepKey="selectNotLoggedInCustomerGroup"/> + <generateDate date="-1 day" format="m/d/Y" stepKey="yesterdayDate"/> + <fillField selector="{{AdminCartPriceRulesFormSection.fromDate}}" userInput="{$yesterdayDate}" stepKey="fillFromDate"/> + <click selector="{{PriceRuleConditionsSection.conditionsTab}}" stepKey="expandConditions"/> + <!-- Scroll down to fix some flaky behavior... --> + <scrollTo selector="{{PriceRuleConditionsSection.conditionsTab}}" stepKey="scrollToConditionsTab"/> + <waitForElementVisible selector="{{PriceRuleConditionsSection.createNewRule}}" stepKey="waitForNewRule"/> + <click selector="{{PriceRuleConditionsSection.createNewRule}}" stepKey="clickNewRule"/> + <selectOption selector="{{PriceRuleConditionsSection.rulesDropdown}}" userInput="Total Items Quantity" stepKey="selectProductAttributes"/> + <waitForPageLoad stepKey="waitForConditions"/> + <click selector="{{PriceRuleConditionsSection.ruleParamLink('1', '1', '1')}}" stepKey="startEditOperator"/> + <selectOption selector="{{PriceRuleConditionsSection.operatorByIndex('1', '1')}}" userInput="greater than" stepKey="fillOperator"/> + <click selector="{{PriceRuleConditionsSection.ruleParamLink('1', '1', '2')}}" stepKey="startEditValue"/> + <fillField selector="{{PriceRuleConditionsSection.valueByIndex('1', '1')}}" userInput="1" stepKey="fillValue"/> + <click selector="{{AdminCartPriceRulesFormSection.actionsHeader}}" stepKey="clickToExpandActions"/> + <selectOption selector="{{AdminCartPriceRulesFormSection.apply}}" userInput="Fixed amount discount for whole cart" stepKey="selectActionType"/> + <fillField selector="{{AdminCartPriceRulesFormSection.discountAmount}}" userInput="1.00" stepKey="fillDiscountAmount"/> + <click selector="{{AdminCartPriceRulesFormSection.save}}" stepKey="clickSaveButton"/> + <see selector="{{AdminCartPriceRulesSection.messages}}" userInput="You saved the rule." stepKey="seeSuccessMessage"/> + <magentoCron groups="index" stepKey="reindexInvalidatedIndices"/> + + <!-- Add 1 product to the cart --> + <amOnPage url="$$createPreReqProduct.name$$.html" stepKey="goToProductPage"/> + <waitForPageLoad stepKey="waitForProductPageLoad"/> + <fillField selector="{{StorefrontProductActionSection.quantity}}" userInput="1" stepKey="fillQuantity"/> + <click selector="{{StorefrontProductActionSection.addToCart}}" stepKey="addProductToCart"/> + <waitForPageLoad stepKey="waitForAddToCart"/> + <waitForElementVisible selector="{{StorefrontMessagesSection.success}}" stepKey="waitForSuccessMessage"/> + + <!-- Should not see the discount yet because we have only 1 item in our cart --> + <amOnPage url="{{CheckoutCartPage.url}}" stepKey="goToCartPage"/> + <waitForPageLoad stepKey="waitForCartPage"/> + <see selector="{{CheckoutCartSummarySection.subtotal}}" userInput="$123.00" stepKey="seeSubtotal"/> + <dontSeeElement selector="{{CheckoutCartSummarySection.discountAmount}}" stepKey="dontSeeDiscount"/> + + <!-- Add the same product to the cart again (2 total) --> + <amOnPage url="$$createPreReqProduct.name$$.html" stepKey="goToProductPage2"/> + <waitForPageLoad stepKey="waitForProductPageLoad2"/> + <fillField selector="{{StorefrontProductActionSection.quantity}}" userInput="1" stepKey="fillQuantity2"/> + <click selector="{{StorefrontProductActionSection.addToCart}}" stepKey="addProductToCart2"/> + <waitForPageLoad stepKey="waitForAddToCart2"/> + <waitForElementVisible selector="{{StorefrontMessagesSection.success}}" stepKey="waitForSuccessMessage2"/> + + <!-- Now we should see the discount because we have more than 1 item --> + <amOnPage url="{{CheckoutCartPage.url}}" stepKey="goToCartPage2"/> + <waitForPageLoad stepKey="waitForCartPage2"/> + <see selector="{{CheckoutCartSummarySection.subtotal}}" userInput="$246.00" stepKey="seeSubtotal2"/> + <waitForElementVisible selector="{{CheckoutCartSummarySection.discountAmount}}" stepKey="waitForDiscountElement"/> + <see selector="{{CheckoutCartSummarySection.discountAmount}}" userInput="-$1.00" stepKey="seeDiscountTotal"/> + </test> +</tests> diff --git a/app/code/Magento/SalesRule/Test/Mftf/Test/StorefrontCartPriceRuleState.xml b/app/code/Magento/SalesRule/Test/Mftf/Test/StorefrontCartPriceRuleState.xml deleted file mode 100644 index 19ffb7c36f992..0000000000000 --- a/app/code/Magento/SalesRule/Test/Mftf/Test/StorefrontCartPriceRuleState.xml +++ /dev/null @@ -1,92 +0,0 @@ -<?xml version="1.0" encoding="UTF-8"?> -<!-- - /** - * Copyright © Magento, Inc. All rights reserved. - * See COPYING.txt for license details. - */ ---> - -<tests xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" - xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/testSchema.xsd"> - <test name="StorefrontCartPriceRuleState"> - <annotations> - <features value="SalesRule"/> - <stories value="Create cart price rule"/> - <title value="Customer should only see cart price rule discount if condition shipping state/province"/> - <description value="Customer should only see cart price rule discount if condition shipping state/province"/> - <severity value="AVERAGE"/> - <testCaseId value="MC-239"/> - <group value="SalesRule"/> - </annotations> - - <before> - <createData entity="_defaultCategory" stepKey="createPreReqCategory"/> - <createData entity="_defaultProduct" stepKey="createPreReqProduct"> - <requiredEntity createDataKey="createPreReqCategory"/> - </createData> - <actionGroup ref="LoginAsAdmin" stepKey="loginAsAdmin"/> - </before> - - <after> - <actionGroup ref="DeleteCartPriceRuleByName" stepKey="cleanUpRule"> - <argument name="ruleName" value="{{SimpleSalesRule.name}}"/> - </actionGroup> - <deleteData createDataKey="createPreReqCategory" stepKey="deletePreReqCategory"/> - <deleteData createDataKey="createPreReqProduct" stepKey="deletePreReqProduct"/> - <amOnPage url="admin/admin/auth/logout/" stepKey="amOnLogoutPage"/> - </after> - - <!-- Create the rule... --> - <amOnPage url="{{AdminCartPriceRulesPage.url}}" stepKey="amOnCartPriceList"/> - <waitForPageLoad stepKey="waitForRulesPage"/> - <click selector="{{AdminCartPriceRulesSection.addNewRuleButton}}" stepKey="clickAddNewRule"/> - <fillField selector="{{AdminCartPriceRulesFormSection.ruleName}}" userInput="{{SimpleSalesRule.name}}" stepKey="fillRuleName"/> - <selectOption selector="{{AdminCartPriceRulesFormSection.websites}}" userInput="Main Website" stepKey="selectWebsites"/> - <actionGroup ref="SelectNotLoggedInCustomerGroupActionGroup" stepKey="selectNotLoggedInCustomerGroup"/> - <generateDate date="-1 day" format="m/d/Y" stepKey="yesterdayDate"/> - <fillField selector="{{AdminCartPriceRulesFormSection.fromDate}}" userInput="{$yesterdayDate}" stepKey="fillFromDate"/> - <click selector="{{PriceRuleConditionsSection.conditionsTab}}" stepKey="expandConditions"/> - <!-- Scroll down to fix some flaky behavior... --> - <scrollTo selector="{{PriceRuleConditionsSection.conditionsTab}}" stepKey="scrollToConditionsTab"/> - <waitForElementVisible selector="{{PriceRuleConditionsSection.createNewRule}}" stepKey="waitForNewRule"/> - <click selector="{{PriceRuleConditionsSection.createNewRule}}" stepKey="clickNewRule"/> - <selectOption selector="{{PriceRuleConditionsSection.rulesDropdown}}" userInput="Shipping State/Province" stepKey="selectProductAttributes"/> - <waitForPageLoad stepKey="wait1"/> - <click selector="{{PriceRuleConditionsSection.ruleParamLink('1', '1', '2')}}" stepKey="startEditValue"/> - <waitForPageLoad stepKey="wait2"/> - <selectOption selector="{{PriceRuleConditionsSection.valueByIndex('1', '1')}}" userInput="Indiana" stepKey="fillValue"/> - <waitForPageLoad stepKey="wait3"/> - <click selector="{{AdminCartPriceRulesFormSection.actionsHeader}}" stepKey="clickToExpandActions"/> - <selectOption selector="{{AdminCartPriceRulesFormSection.apply}}" userInput="Fixed amount discount for whole cart" stepKey="selectActionType"/> - <fillField selector="{{AdminCartPriceRulesFormSection.discountAmount}}" userInput="9.99" stepKey="fillDiscountAmount"/> - <click selector="{{AdminCartPriceRulesFormSection.save}}" stepKey="clickSaveButton"/> - <see selector="{{AdminCartPriceRulesSection.messages}}" userInput="You saved the rule." stepKey="seeSuccessMessage"/> - - <!-- Add the product we created to our cart --> - <amOnPage url="$$createPreReqProduct.name$$.html" stepKey="goToProductPage"/> - <waitForPageLoad stepKey="waitForProductPageLoad"/> - <fillField selector="{{StorefrontProductActionSection.quantity}}" userInput="1" stepKey="fillQuantity"/> - <click selector="{{StorefrontProductActionSection.addToCart}}" stepKey="addProductToCart"/> - <waitForPageLoad stepKey="waitForAddToCart"/> - <waitForElementVisible selector="{{StorefrontMessagesSection.success}}" stepKey="waitForSuccessMessage"/> - - <!-- Should not see the discount yet because we have not filled in postcode --> - <amOnPage url="{{CheckoutCartPage.url}}" stepKey="goToCartPage"/> - <waitForPageLoad stepKey="waitForCartPage"/> - <click selector="{{CheckoutCartSummarySection.shippingHeading}}" stepKey="expandShipping"/> - <checkOption selector="{{CheckoutCartSummarySection.flatRateShippingMethod}}" stepKey="selectFlatRateShipping"/> - <see selector="{{CheckoutCartSummarySection.subtotal}}" userInput="$123.00" stepKey="seeSubtotal"/> - <dontSeeElement selector="{{CheckoutCartSummarySection.discountAmount}}" stepKey="dontSeeDiscount"/> - - <!-- See discount if we use valid postcode --> - <selectOption selector="{{CheckoutCartSummarySection.stateProvince}}" userInput="Indiana" stepKey="fillState"/> - <waitForPageLoad stepKey="waitForPostcode1"/> - <waitForElementVisible selector="{{CheckoutCartSummarySection.discountAmount}}" stepKey="waitForDiscountElement"/> - <see selector="{{CheckoutCartSummarySection.discountAmount}}" userInput="-$9.99" stepKey="seeDiscountTotal"/> - - <!-- Do not see discount with other postcode --> - <selectOption selector="{{CheckoutCartSummarySection.stateProvince}}" userInput="Texas" stepKey="fillState2"/> - <waitForPageLoad stepKey="waitForPostcode2"/> - <dontSeeElement selector="{{CheckoutCartSummarySection.discountAmount}}" stepKey="dontSeeDiscount2"/> - </test> -</tests> diff --git a/app/code/Magento/SalesRule/Test/Mftf/Test/StorefrontCartPriceRuleStateTest.xml b/app/code/Magento/SalesRule/Test/Mftf/Test/StorefrontCartPriceRuleStateTest.xml new file mode 100644 index 0000000000000..036844562af50 --- /dev/null +++ b/app/code/Magento/SalesRule/Test/Mftf/Test/StorefrontCartPriceRuleStateTest.xml @@ -0,0 +1,92 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + /** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +--> + +<tests xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/testSchema.xsd"> + <test name="StorefrontCartPriceRuleStateTest"> + <annotations> + <features value="SalesRule"/> + <stories value="Create cart price rule"/> + <title value="Customer should only see cart price rule discount if condition shipping state/province"/> + <description value="Customer should only see cart price rule discount if condition shipping state/province"/> + <severity value="AVERAGE"/> + <testCaseId value="MC-239"/> + <group value="SalesRule"/> + </annotations> + + <before> + <createData entity="_defaultCategory" stepKey="createPreReqCategory"/> + <createData entity="_defaultProduct" stepKey="createPreReqProduct"> + <requiredEntity createDataKey="createPreReqCategory"/> + </createData> + <actionGroup ref="AdminLoginActionGroup" stepKey="loginAsAdmin"/> + </before> + + <after> + <actionGroup ref="DeleteCartPriceRuleByName" stepKey="cleanUpRule"> + <argument name="ruleName" value="{{SimpleSalesRule.name}}"/> + </actionGroup> + <deleteData createDataKey="createPreReqCategory" stepKey="deletePreReqCategory"/> + <deleteData createDataKey="createPreReqProduct" stepKey="deletePreReqProduct"/> + <amOnPage url="admin/admin/auth/logout/" stepKey="amOnLogoutPage"/> + </after> + + <!-- Create the rule... --> + <amOnPage url="{{AdminCartPriceRulesPage.url}}" stepKey="amOnCartPriceList"/> + <waitForPageLoad stepKey="waitForRulesPage"/> + <click selector="{{AdminCartPriceRulesSection.addNewRuleButton}}" stepKey="clickAddNewRule"/> + <fillField selector="{{AdminCartPriceRulesFormSection.ruleName}}" userInput="{{SimpleSalesRule.name}}" stepKey="fillRuleName"/> + <selectOption selector="{{AdminCartPriceRulesFormSection.websites}}" userInput="Main Website" stepKey="selectWebsites"/> + <actionGroup ref="SelectNotLoggedInCustomerGroupActionGroup" stepKey="selectNotLoggedInCustomerGroup"/> + <generateDate date="-1 day" format="m/d/Y" stepKey="yesterdayDate"/> + <fillField selector="{{AdminCartPriceRulesFormSection.fromDate}}" userInput="{$yesterdayDate}" stepKey="fillFromDate"/> + <click selector="{{PriceRuleConditionsSection.conditionsTab}}" stepKey="expandConditions"/> + <!-- Scroll down to fix some flaky behavior... --> + <scrollTo selector="{{PriceRuleConditionsSection.conditionsTab}}" stepKey="scrollToConditionsTab"/> + <waitForElementVisible selector="{{PriceRuleConditionsSection.createNewRule}}" stepKey="waitForNewRule"/> + <click selector="{{PriceRuleConditionsSection.createNewRule}}" stepKey="clickNewRule"/> + <selectOption selector="{{PriceRuleConditionsSection.rulesDropdown}}" userInput="Shipping State/Province" stepKey="selectProductAttributes"/> + <waitForPageLoad stepKey="wait1"/> + <click selector="{{PriceRuleConditionsSection.ruleParamLink('1', '1', '2')}}" stepKey="startEditValue"/> + <waitForPageLoad stepKey="wait2"/> + <selectOption selector="{{PriceRuleConditionsSection.valueByIndex('1', '1')}}" userInput="Indiana" stepKey="fillValue"/> + <waitForPageLoad stepKey="wait3"/> + <click selector="{{AdminCartPriceRulesFormSection.actionsHeader}}" stepKey="clickToExpandActions"/> + <selectOption selector="{{AdminCartPriceRulesFormSection.apply}}" userInput="Fixed amount discount for whole cart" stepKey="selectActionType"/> + <fillField selector="{{AdminCartPriceRulesFormSection.discountAmount}}" userInput="9.99" stepKey="fillDiscountAmount"/> + <click selector="{{AdminCartPriceRulesFormSection.save}}" stepKey="clickSaveButton"/> + <see selector="{{AdminCartPriceRulesSection.messages}}" userInput="You saved the rule." stepKey="seeSuccessMessage"/> + + <!-- Add the product we created to our cart --> + <amOnPage url="$$createPreReqProduct.name$$.html" stepKey="goToProductPage"/> + <waitForPageLoad stepKey="waitForProductPageLoad"/> + <fillField selector="{{StorefrontProductActionSection.quantity}}" userInput="1" stepKey="fillQuantity"/> + <click selector="{{StorefrontProductActionSection.addToCart}}" stepKey="addProductToCart"/> + <waitForPageLoad stepKey="waitForAddToCart"/> + <waitForElementVisible selector="{{StorefrontMessagesSection.success}}" stepKey="waitForSuccessMessage"/> + + <!-- Should not see the discount yet because we have not filled in postcode --> + <amOnPage url="{{CheckoutCartPage.url}}" stepKey="goToCartPage"/> + <waitForPageLoad stepKey="waitForCartPage"/> + <click selector="{{CheckoutCartSummarySection.shippingHeading}}" stepKey="expandShipping"/> + <checkOption selector="{{CheckoutCartSummarySection.flatRateShippingMethod}}" stepKey="selectFlatRateShipping"/> + <see selector="{{CheckoutCartSummarySection.subtotal}}" userInput="$123.00" stepKey="seeSubtotal"/> + <dontSeeElement selector="{{CheckoutCartSummarySection.discountAmount}}" stepKey="dontSeeDiscount"/> + + <!-- See discount if we use valid postcode --> + <selectOption selector="{{CheckoutCartSummarySection.stateProvince}}" userInput="Indiana" stepKey="fillState"/> + <waitForPageLoad stepKey="waitForPostcode1"/> + <waitForElementVisible selector="{{CheckoutCartSummarySection.discountAmount}}" stepKey="waitForDiscountElement"/> + <see selector="{{CheckoutCartSummarySection.discountAmount}}" userInput="-$9.99" stepKey="seeDiscountTotal"/> + + <!-- Do not see discount with other postcode --> + <selectOption selector="{{CheckoutCartSummarySection.stateProvince}}" userInput="Texas" stepKey="fillState2"/> + <waitForPageLoad stepKey="waitForPostcode2"/> + <dontSeeElement selector="{{CheckoutCartSummarySection.discountAmount}}" stepKey="dontSeeDiscount2"/> + </test> +</tests> diff --git a/app/code/Magento/SalesRule/Test/Mftf/Test/StorefrontCartPriceRuleSubtotal.xml b/app/code/Magento/SalesRule/Test/Mftf/Test/StorefrontCartPriceRuleSubtotal.xml deleted file mode 100644 index 510bfabf303f5..0000000000000 --- a/app/code/Magento/SalesRule/Test/Mftf/Test/StorefrontCartPriceRuleSubtotal.xml +++ /dev/null @@ -1,93 +0,0 @@ -<?xml version="1.0" encoding="UTF-8"?> -<!-- - /** - * Copyright © Magento, Inc. All rights reserved. - * See COPYING.txt for license details. - */ ---> - -<tests xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" - xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/testSchema.xsd"> - <test name="StorefrontCartPriceRuleSubtotal"> - <annotations> - <features value="SalesRule"/> - <stories value="Create cart price rule"/> - <title value="Customer should only see cart price rule discount if condition subtotal equals or greater than"/> - <description value="Customer should only see cart price rule discount if condition subtotal equals or greater than"/> - <severity value="AVERAGE"/> - <testCaseId value="MC-235"/> - <group value="SalesRule"/> - </annotations> - - <before> - <createData entity="_defaultCategory" stepKey="createPreReqCategory"/> - <createData entity="_defaultProduct" stepKey="createPreReqProduct"> - <requiredEntity createDataKey="createPreReqCategory"/> - </createData> - <actionGroup ref="LoginAsAdmin" stepKey="loginAsAdmin"/> - </before> - - <after> - <actionGroup ref="DeleteCartPriceRuleByName" stepKey="cleanUpRule"> - <argument name="ruleName" value="{{SimpleSalesRule.name}}"/> - </actionGroup> - <deleteData createDataKey="createPreReqCategory" stepKey="deletePreReqCategory"/> - <deleteData createDataKey="createPreReqProduct" stepKey="deletePreReqProduct"/> - <amOnPage url="admin/admin/auth/logout/" stepKey="amOnLogoutPage"/> - </after> - - <!-- Create the rule... --> - <amOnPage url="{{AdminCartPriceRulesPage.url}}" stepKey="amOnCartPriceList"/> - <waitForPageLoad stepKey="waitForRulesPage"/> - <click selector="{{AdminCartPriceRulesSection.addNewRuleButton}}" stepKey="clickAddNewRule"/> - <fillField selector="{{AdminCartPriceRulesFormSection.ruleName}}" userInput="{{SimpleSalesRule.name}}" stepKey="fillRuleName"/> - <selectOption selector="{{AdminCartPriceRulesFormSection.websites}}" userInput="Main Website" stepKey="selectWebsites"/> - <actionGroup ref="SelectNotLoggedInCustomerGroupActionGroup" stepKey="selectNotLoggedInCustomerGroup"/> - <generateDate date="-1 day" format="m/d/Y" stepKey="yesterdayDate"/> - <fillField selector="{{AdminCartPriceRulesFormSection.fromDate}}" userInput="{$yesterdayDate}" stepKey="fillFromDate"/> - <click selector="{{PriceRuleConditionsSection.conditionsTab}}" stepKey="expandConditions"/> - <!-- Scroll down to fix some flaky behavior... --> - <scrollTo selector="{{PriceRuleConditionsSection.conditionsTab}}" stepKey="scrollToConditionsTab"/> - <click selector="{{PriceRuleConditionsSection.createNewRule}}" stepKey="clickNewRule"/> - <selectOption selector="{{PriceRuleConditionsSection.rulesDropdown}}" userInput="Subtotal" stepKey="selectProductAttributes"/> - <waitForPageLoad stepKey="waitForConditions"/> - <click selector="{{PriceRuleConditionsSection.ruleParamLink('1', '1', '1')}}" stepKey="startEditOperator"/> - <selectOption selector="{{PriceRuleConditionsSection.operatorByIndex('1', '1')}}" userInput="equals or greater than" stepKey="fillOperator"/> - <click selector="{{PriceRuleConditionsSection.ruleParamLink('1', '1', '2')}}" stepKey="startEditValue"/> - <fillField selector="{{PriceRuleConditionsSection.valueByIndex('1', '1')}}" userInput="200" stepKey="fillValue"/> - <click selector="{{AdminCartPriceRulesFormSection.actionsHeader}}" stepKey="clickToExpandActions"/> - <selectOption selector="{{AdminCartPriceRulesFormSection.apply}}" userInput="Fixed amount discount for whole cart" stepKey="selectActionType"/> - <fillField selector="{{AdminCartPriceRulesFormSection.discountAmount}}" userInput="0.01" stepKey="fillDiscountAmount"/> - <click selector="{{AdminCartPriceRulesFormSection.save}}" stepKey="clickSaveButton"/> - <see selector="{{AdminCartPriceRulesSection.messages}}" userInput="You saved the rule." stepKey="seeSuccessMessage"/> - - <!-- Add 1 product worth $123.00 to the cart --> - <amOnPage url="$$createPreReqProduct.name$$.html" stepKey="goToProductPage"/> - <waitForPageLoad stepKey="waitForProductPageLoad"/> - <fillField selector="{{StorefrontProductActionSection.quantity}}" userInput="1" stepKey="fillQuantity"/> - <click selector="{{StorefrontProductActionSection.addToCart}}" stepKey="addProductToCart"/> - <waitForPageLoad stepKey="waitForAddToCart"/> - <waitForElementVisible selector="{{StorefrontMessagesSection.success}}" stepKey="waitForSuccessMessage"/> - - <!-- Should not see the discount yet because we have not exceeded $200 --> - <amOnPage url="{{CheckoutCartPage.url}}" stepKey="goToCartPage"/> - <waitForPageLoad stepKey="waitForCartPage"/> - <see selector="{{CheckoutCartSummarySection.subtotal}}" userInput="$123.00" stepKey="seeSubtotal"/> - <dontSeeElement selector="{{CheckoutCartSummarySection.discountAmount}}" stepKey="dontSeeDiscount"/> - - <!-- Add the same product to the cart again ($246.00 subtotal) --> - <amOnPage url="$$createPreReqProduct.name$$.html" stepKey="goToProductPage2"/> - <waitForPageLoad stepKey="waitForProductPageLoad2"/> - <fillField selector="{{StorefrontProductActionSection.quantity}}" userInput="1" stepKey="fillQuantity2"/> - <click selector="{{StorefrontProductActionSection.addToCart}}" stepKey="addProductToCart2"/> - <waitForPageLoad stepKey="waitForAddToCart2"/> - <waitForElementVisible selector="{{StorefrontMessagesSection.success}}" stepKey="waitForSuccessMessage2"/> - - <!-- Now we should see the discount because we exceeded $200 --> - <amOnPage url="{{CheckoutCartPage.url}}" stepKey="goToCartPage2"/> - <waitForPageLoad stepKey="waitForCartPage2"/> - <see selector="{{CheckoutCartSummarySection.subtotal}}" userInput="$246.00" stepKey="seeSubtotal2"/> - <waitForElementVisible selector="{{CheckoutCartSummarySection.discountAmount}}" stepKey="waitForDiscountElement"/> - <see selector="{{CheckoutCartSummarySection.discountAmount}}" userInput="-$0.01" stepKey="seeDiscountTotal"/> - </test> -</tests> diff --git a/app/code/Magento/SalesRule/Test/Mftf/Test/StorefrontCartPriceRuleSubtotalTest.xml b/app/code/Magento/SalesRule/Test/Mftf/Test/StorefrontCartPriceRuleSubtotalTest.xml new file mode 100644 index 0000000000000..efd82316f0b0a --- /dev/null +++ b/app/code/Magento/SalesRule/Test/Mftf/Test/StorefrontCartPriceRuleSubtotalTest.xml @@ -0,0 +1,94 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + /** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +--> + +<tests xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/testSchema.xsd"> + <test name="StorefrontCartPriceRuleSubtotalTest"> + <annotations> + <features value="SalesRule"/> + <stories value="Create cart price rule"/> + <title value="Customer should only see cart price rule discount if condition subtotal equals or greater than"/> + <description value="Customer should only see cart price rule discount if condition subtotal equals or greater than"/> + <severity value="BLOCKER"/> + <testCaseId value="MC-235"/> + <group value="SalesRule"/> + </annotations> + + <before> + <createData entity="_defaultCategory" stepKey="createPreReqCategory"/> + <createData entity="_defaultProduct" stepKey="createPreReqProduct"> + <requiredEntity createDataKey="createPreReqCategory"/> + </createData> + <magentoCron groups="index" stepKey="reindexInvalidatedIndices"/> + <actionGroup ref="AdminLoginActionGroup" stepKey="loginAsAdmin"/> + </before> + + <after> + <actionGroup ref="DeleteCartPriceRuleByName" stepKey="cleanUpRule"> + <argument name="ruleName" value="{{SimpleSalesRule.name}}"/> + </actionGroup> + <deleteData createDataKey="createPreReqCategory" stepKey="deletePreReqCategory"/> + <deleteData createDataKey="createPreReqProduct" stepKey="deletePreReqProduct"/> + <amOnPage url="admin/admin/auth/logout/" stepKey="amOnLogoutPage"/> + </after> + + <!-- Create the rule... --> + <amOnPage url="{{AdminCartPriceRulesPage.url}}" stepKey="amOnCartPriceList"/> + <waitForPageLoad stepKey="waitForRulesPage"/> + <click selector="{{AdminCartPriceRulesSection.addNewRuleButton}}" stepKey="clickAddNewRule"/> + <fillField selector="{{AdminCartPriceRulesFormSection.ruleName}}" userInput="{{SimpleSalesRule.name}}" stepKey="fillRuleName"/> + <selectOption selector="{{AdminCartPriceRulesFormSection.websites}}" userInput="Main Website" stepKey="selectWebsites"/> + <actionGroup ref="SelectNotLoggedInCustomerGroupActionGroup" stepKey="selectNotLoggedInCustomerGroup"/> + <generateDate date="-1 day" format="m/d/Y" stepKey="yesterdayDate"/> + <fillField selector="{{AdminCartPriceRulesFormSection.fromDate}}" userInput="{$yesterdayDate}" stepKey="fillFromDate"/> + <click selector="{{PriceRuleConditionsSection.conditionsTab}}" stepKey="expandConditions"/> + <!-- Scroll down to fix some flaky behavior... --> + <scrollTo selector="{{PriceRuleConditionsSection.conditionsTab}}" stepKey="scrollToConditionsTab"/> + <click selector="{{PriceRuleConditionsSection.createNewRule}}" stepKey="clickNewRule"/> + <selectOption selector="{{PriceRuleConditionsSection.rulesDropdown}}" userInput="Subtotal" stepKey="selectProductAttributes"/> + <waitForPageLoad stepKey="waitForConditions"/> + <click selector="{{PriceRuleConditionsSection.ruleParamLink('1', '1', '1')}}" stepKey="startEditOperator"/> + <selectOption selector="{{PriceRuleConditionsSection.operatorByIndex('1', '1')}}" userInput="equals or greater than" stepKey="fillOperator"/> + <click selector="{{PriceRuleConditionsSection.ruleParamLink('1', '1', '2')}}" stepKey="startEditValue"/> + <fillField selector="{{PriceRuleConditionsSection.valueByIndex('1', '1')}}" userInput="200" stepKey="fillValue"/> + <click selector="{{AdminCartPriceRulesFormSection.actionsHeader}}" stepKey="clickToExpandActions"/> + <selectOption selector="{{AdminCartPriceRulesFormSection.apply}}" userInput="Fixed amount discount for whole cart" stepKey="selectActionType"/> + <fillField selector="{{AdminCartPriceRulesFormSection.discountAmount}}" userInput="0.01" stepKey="fillDiscountAmount"/> + <click selector="{{AdminCartPriceRulesFormSection.save}}" stepKey="clickSaveButton"/> + <see selector="{{AdminCartPriceRulesSection.messages}}" userInput="You saved the rule." stepKey="seeSuccessMessage"/> + + <!-- Add 1 product worth $123.00 to the cart --> + <amOnPage url="$$createPreReqProduct.name$$.html" stepKey="goToProductPage"/> + <waitForPageLoad stepKey="waitForProductPageLoad"/> + <fillField selector="{{StorefrontProductActionSection.quantity}}" userInput="1" stepKey="fillQuantity"/> + <click selector="{{StorefrontProductActionSection.addToCart}}" stepKey="addProductToCart"/> + <waitForPageLoad stepKey="waitForAddToCart"/> + <waitForElementVisible selector="{{StorefrontMessagesSection.success}}" stepKey="waitForSuccessMessage"/> + + <!-- Should not see the discount yet because we have not exceeded $200 --> + <amOnPage url="{{CheckoutCartPage.url}}" stepKey="goToCartPage"/> + <waitForPageLoad stepKey="waitForCartPage"/> + <see selector="{{CheckoutCartSummarySection.subtotal}}" userInput="$123.00" stepKey="seeSubtotal"/> + <dontSeeElement selector="{{CheckoutCartSummarySection.discountAmount}}" stepKey="dontSeeDiscount"/> + + <!-- Add the same product to the cart again ($246.00 subtotal) --> + <amOnPage url="$$createPreReqProduct.name$$.html" stepKey="goToProductPage2"/> + <waitForPageLoad stepKey="waitForProductPageLoad2"/> + <fillField selector="{{StorefrontProductActionSection.quantity}}" userInput="1" stepKey="fillQuantity2"/> + <click selector="{{StorefrontProductActionSection.addToCart}}" stepKey="addProductToCart2"/> + <waitForPageLoad stepKey="waitForAddToCart2"/> + <waitForElementVisible selector="{{StorefrontMessagesSection.success}}" stepKey="waitForSuccessMessage2"/> + + <!-- Now we should see the discount because we exceeded $200 --> + <amOnPage url="{{CheckoutCartPage.url}}" stepKey="goToCartPage2"/> + <waitForPageLoad stepKey="waitForCartPage2"/> + <see selector="{{CheckoutCartSummarySection.subtotal}}" userInput="$246.00" stepKey="seeSubtotal2"/> + <waitForElementVisible selector="{{CheckoutCartSummarySection.discountAmount}}" stepKey="waitForDiscountElement"/> + <see selector="{{CheckoutCartSummarySection.discountAmount}}" userInput="-$0.01" stepKey="seeDiscountTotal"/> + </test> +</tests> diff --git a/app/code/Magento/SalesRule/Test/Mftf/Test/StorefrontCartRuleCouponForFreeShippingTest.xml b/app/code/Magento/SalesRule/Test/Mftf/Test/StorefrontCartRuleCouponForFreeShippingTest.xml index 3526ab20aa6b4..7d266e2cdd6f1 100644 --- a/app/code/Magento/SalesRule/Test/Mftf/Test/StorefrontCartRuleCouponForFreeShippingTest.xml +++ b/app/code/Magento/SalesRule/Test/Mftf/Test/StorefrontCartRuleCouponForFreeShippingTest.xml @@ -39,7 +39,7 @@ <deleteData createDataKey="createCartPriceRule" stepKey="deleteSalesRule"/> <deleteData createDataKey="createCustomer" stepKey="deleteCustomer"/> <deleteData createDataKey="createSimpleProduct" stepKey="deleteProduct"/> - <actionGroup ref="logout" stepKey="logoutFromBackend"/> + <actionGroup ref="AdminLogoutActionGroup" stepKey="logoutFromBackend"/> </after> <!-- Login with created Customer --> @@ -86,7 +86,7 @@ <grabFromCurrentUrl regex="~/order_id/(\d+)/~" stepKey="grabOrderId"/> <!-- Login to admin --> - <actionGroup ref="LoginAsAdmin" stepKey="loginAsAdmin"/> + <actionGroup ref="AdminLoginActionGroup" stepKey="loginAsAdmin"/> <!-- Go to created Order --> <amOnPage url="{{AdminOrderPage.url({$grabOrderId})}}" stepKey="goToAdminViewOrder"/> diff --git a/app/code/Magento/SalesRule/Test/Mftf/Test/StorefrontCartTotalValueWithFullDiscountUsingCartRuleTest.xml b/app/code/Magento/SalesRule/Test/Mftf/Test/StorefrontCartTotalValueWithFullDiscountUsingCartRuleTest.xml index f12b8ea7ca331..172f6b6ba24c6 100644 --- a/app/code/Magento/SalesRule/Test/Mftf/Test/StorefrontCartTotalValueWithFullDiscountUsingCartRuleTest.xml +++ b/app/code/Magento/SalesRule/Test/Mftf/Test/StorefrontCartTotalValueWithFullDiscountUsingCartRuleTest.xml @@ -20,7 +20,7 @@ </annotations> <before> <!-- log in --> - <actionGroup ref="LoginAsAdmin" stepKey="login"/> + <actionGroup ref="AdminLoginActionGroup" stepKey="login"/> <!-- Set configurations --> <magentoCLI command="config:set carriers/tablerate/active 1" stepKey="setShippingMethodEnabled"/> <magentoCLI command="config:set carriers/tablerate/condition_name package_value" stepKey="setShippingMethodConditionName"/> @@ -92,7 +92,7 @@ <magentoCLI command="config:set tax/cart_display/subtotal 1" stepKey="unsetSubtotal"/> <magentoCLI command="config:set carriers/freeshipping/active 0" stepKey="unsetFreeShipping"/> <!-- Log out --> - <actionGroup ref="logout" stepKey="logout"/> + <actionGroup ref="AdminLogoutActionGroup" stepKey="logout"/> </after> <!-- Add testing products to the cart --> <amOnPage url="{{StorefrontProductPage.url($$createSimpleProductFirst.custom_attributes[url_key]$$)}}" stepKey="goToProductPage"/> diff --git a/app/code/Magento/SalesRule/Test/Mftf/Test/StorefrontCategoryRulesShouldApplyToComplexProductsTest.xml b/app/code/Magento/SalesRule/Test/Mftf/Test/StorefrontCategoryRulesShouldApplyToComplexProductsTest.xml index dbeb4ba94061b..85a30b3a3a2b4 100644 --- a/app/code/Magento/SalesRule/Test/Mftf/Test/StorefrontCategoryRulesShouldApplyToComplexProductsTest.xml +++ b/app/code/Magento/SalesRule/Test/Mftf/Test/StorefrontCategoryRulesShouldApplyToComplexProductsTest.xml @@ -29,7 +29,7 @@ <actionGroup ref="AdminCreateApiConfigurableProductWithHiddenChildActionGroup" stepKey="createConfigurableProduct2"> <argument name="productName" value="config2"/> </actionGroup> - <actionGroup ref="LoginAsAdmin" stepKey="loginAsAdmin"/> + <actionGroup ref="AdminLoginActionGroup" stepKey="loginAsAdmin"/> <!-- Assign config1 and the associated child products to CAT1 --> <actionGroup ref="AdminAssignProductToCategoryActionGroup" stepKey="assignConfigurableProduct1ToCategory"> <argument name="productId" value="$$createConfigProductCreateConfigurableProduct1.id$$"/> @@ -72,7 +72,7 @@ <deleteData createDataKey="createConfigProductAttributeCreateConfigurableProduct2" stepKey="deleteConfigProductAttribute2"/> <!--Delete Cart Price Rule --> <deleteData createDataKey="createCartPriceRule" stepKey="deleteCartPriceRule"/> - <actionGroup ref="logout" stepKey="logout"/> + <actionGroup ref="AdminLogoutActionGroup" stepKey="logout"/> </after> <!-- 1: Create a cart price rule applying to CAT1 with discount --> <createData entity="SalesRuleNoCouponWithFixedDiscount" stepKey="createCartPriceRule"/> diff --git a/app/code/Magento/SalesRule/Test/Mftf/Test/StorefrontCategoryRulesShouldApplyToGroupedProductWithInvisibleIndividualProductTest.xml b/app/code/Magento/SalesRule/Test/Mftf/Test/StorefrontCategoryRulesShouldApplyToGroupedProductWithInvisibleIndividualProductTest.xml index 13b2661dcb9d0..d63df5fe50a6d 100644 --- a/app/code/Magento/SalesRule/Test/Mftf/Test/StorefrontCategoryRulesShouldApplyToGroupedProductWithInvisibleIndividualProductTest.xml +++ b/app/code/Magento/SalesRule/Test/Mftf/Test/StorefrontCategoryRulesShouldApplyToGroupedProductWithInvisibleIndividualProductTest.xml @@ -14,7 +14,7 @@ <title value="Category rules should apply to grouped product with invisible individual products"/> <description value="Category rules should apply to grouped product with invisible individual products"/> <severity value="CRITICAL"/> - <testCaseId value="MC-13608"/> + <testCaseId value="MC-31863"/> <group value="SalesRule"/> </annotations> <before> @@ -62,7 +62,7 @@ <requiredEntity createDataKey="createSecondGroupedProduct"/> <requiredEntity createDataKey="createFourthSimpleProduct"/> </updateData> - <actionGroup ref="LoginAsAdmin" stepKey="loginAsAdmin"/> + <actionGroup ref="AdminLoginActionGroup" stepKey="loginAsAdmin"/> </before> <after> <deleteData createDataKey="createFirstSimpleProduct" stepKey="deleteFirstSimpleProduct"/> @@ -77,7 +77,7 @@ <argument name="ruleName" value="TestSalesRule"/> </actionGroup> <actionGroup ref="ClearFiltersAdminDataGridActionGroup" stepKey="clearGridFilter"/> - <actionGroup ref="logout" stepKey="logout"/> + <actionGroup ref="AdminLogoutActionGroup" stepKey="logout"/> </after> <!-- Start to create new cart price rule via Category conditions --> <actionGroup ref="AdminCreateCartPriceRuleWithConditionIsCategoryActionGroup" stepKey="createCartPriceRuleWithCondition"> diff --git a/app/code/Magento/SalesRule/Test/Unit/Controller/Adminhtml/Promo/Quote/ExportCouponsCsvTest.php b/app/code/Magento/SalesRule/Test/Unit/Controller/Adminhtml/Promo/Quote/ExportCouponsCsvTest.php new file mode 100644 index 0000000000000..f3658ab52f8bb --- /dev/null +++ b/app/code/Magento/SalesRule/Test/Unit/Controller/Adminhtml/Promo/Quote/ExportCouponsCsvTest.php @@ -0,0 +1,87 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ + +declare(strict_types=1); + +namespace Magento\SalesRule\Test\Unit\Controller\Adminhtml\Promo\Quote; + +use Magento\Framework\TestFramework\Unit\Helper\ObjectManager as ObjectManagerHelper; +use Magento\SalesRule\Controller\Adminhtml\Promo\Quote\ExportCouponsCsv; +use Magento\Framework\Controller\ResultFactory; +use Magento\Framework\App\Filesystem\DirectoryList; +use Magento\Framework\App\Response\Http\FileFactory; +use Magento\Framework\View\Result\Layout; +use Magento\Framework\View\LayoutInterface; +use Magento\Framework\View\Element\AbstractBlock; +use Magento\SalesRule\Block\Adminhtml\Promo\Quote\Edit\Tab\Coupons\Grid; +use PHPUnit\Framework\TestCase; + +class ExportCouponsCsvTest extends TestCase +{ + /** + * @var ExportCouponsCsv + */ + private $controller; + + /** + * @var FileFactory|\PHPUnit_Framework_MockObject_MockObject + */ + private $fileFactoryMock; + + /** + * @var ObjectManagerHelper + */ + private $objectManagerHelper; + + /** + * @var ResultFactory|\PHPUnit_Framework_MockObject_MockObject + */ + private $resultFactoryMock; + + /** + * Setup environment + */ + protected function setUp() + { + $this->objectManagerHelper = new ObjectManagerHelper($this); + $this->fileFactoryMock = $this->createMock(FileFactory::class); + $this->resultFactoryMock = $this->createMock(ResultFactory::class); + + $this->controller = $this->objectManagerHelper->getObject( + ExportCouponsCsv::class, + [ + 'fileFactory' => $this->fileFactoryMock, + 'resultFactory' => $this->resultFactoryMock + ] + ); + } + + /** + * Test execute function + */ + public function testExecute() + { + $fileName = 'coupon_codes.csv'; + + $resultLayoutMock = $this->createMock(Layout::class); + $layoutMock = $this->createMock(LayoutInterface::class); + $contentMock = $this->createPartialMock(AbstractBlock::class, ['getCsvFile']); + $this->resultFactoryMock + ->expects($this->once()) + ->method('create') + ->with(ResultFactory::TYPE_LAYOUT)->willReturn($resultLayoutMock); + $resultLayoutMock->expects($this->once())->method('getLayout')->willReturn($layoutMock); + $layoutMock->expects($this->once())->method('createBlock')->with(Grid::class) + ->willReturn($contentMock); + $contentMock->expects($this->once())->method('getCsvFile')->willReturn('csvFile'); + $this->fileFactoryMock + ->expects($this->once()) + ->method('create') + ->with($fileName, 'csvFile', DirectoryList::VAR_DIR); + + $this->controller->execute(); + } +} diff --git a/app/code/Magento/SalesRule/Test/Unit/Controller/Adminhtml/Promo/Quote/ExportCouponsXmlTest.php b/app/code/Magento/SalesRule/Test/Unit/Controller/Adminhtml/Promo/Quote/ExportCouponsXmlTest.php new file mode 100644 index 0000000000000..83ad95171033f --- /dev/null +++ b/app/code/Magento/SalesRule/Test/Unit/Controller/Adminhtml/Promo/Quote/ExportCouponsXmlTest.php @@ -0,0 +1,89 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ + +declare(strict_types=1); + +namespace Magento\SalesRule\Test\Unit\Controller\Adminhtml\Promo\Quote; + +use Magento\Framework\TestFramework\Unit\Helper\ObjectManager as ObjectManagerHelper; +use Magento\SalesRule\Controller\Adminhtml\Promo\Quote\ExportCouponsXml; +use Magento\Framework\Controller\ResultFactory; +use Magento\Framework\App\Filesystem\DirectoryList; +use Magento\Framework\App\Response\Http\FileFactory; +use Magento\Framework\View\Result\Layout; +use Magento\Framework\View\LayoutInterface; +use Magento\Framework\View\Element\AbstractBlock; +use Magento\SalesRule\Block\Adminhtml\Promo\Quote\Edit\Tab\Coupons\Grid; +use PHPUnit\Framework\TestCase; + +class ExportCouponsXmlTest extends TestCase +{ + /** + * @var ExportCouponsXml + */ + private $controller; + + /** + * @var FileFactory|\PHPUnit_Framework_MockObject_MockObject + */ + private $fileFactoryMock; + + /** + * @var ObjectManagerHelper + */ + private $objectManagerHelper; + + /** + * @var ResultFactory|\PHPUnit_Framework_MockObject_MockObject + */ + private $resultFactoryMock; + + /** + * Setup environment + */ + protected function setUp() + { + $this->objectManagerHelper = new ObjectManagerHelper($this); + $this->fileFactoryMock = $this->createMock(FileFactory::class); + $this->resultFactoryMock = $this->createMock(ResultFactory::class); + + $this->controller = $this->objectManagerHelper->getObject( + ExportCouponsXml::class, + [ + 'fileFactory' => $this->fileFactoryMock, + 'resultFactory' => $this->resultFactoryMock + ] + ); + } + + /** + * Test execute function + */ + public function testExecute() + { + $fileName = 'coupon_codes.xml'; + + $resultLayoutMock = $this->createMock(Layout::class); + $layoutMock = $this->createMock(LayoutInterface::class); + $contentMock = $this->createPartialMock(AbstractBlock::class, ['getExcelFile']); + $this->resultFactoryMock + ->expects($this->once()) + ->method('create') + ->with(ResultFactory::TYPE_LAYOUT)->willReturn($resultLayoutMock); + $resultLayoutMock->expects($this->once())->method('getLayout')->willReturn($layoutMock); + $layoutMock->expects($this->once())->method('createBlock')->with(Grid::class) + ->willReturn($contentMock); + $contentMock->expects($this->once())->method('getExcelFile') + ->with($fileName) + ->willReturn('xmlFile'); + $this->fileFactoryMock + ->expects($this->once()) + ->method('create') + ->with($fileName, 'xmlFile', DirectoryList::VAR_DIR); + + $this->controller->execute(); + } +} diff --git a/app/code/Magento/SalesRule/Test/Unit/Model/Rule/Action/Discount/CartFixedTest.php b/app/code/Magento/SalesRule/Test/Unit/Model/Rule/Action/Discount/CartFixedTest.php index 13f26124c464d..cafe194d05de0 100644 --- a/app/code/Magento/SalesRule/Test/Unit/Model/Rule/Action/Discount/CartFixedTest.php +++ b/app/code/Magento/SalesRule/Test/Unit/Model/Rule/Action/Discount/CartFixedTest.php @@ -65,10 +65,17 @@ protected function setUp() $this->item = $this->createMock(\Magento\Quote\Model\Quote\Item\AbstractItem::class); $this->data = $this->createPartialMock(\Magento\SalesRule\Model\Rule\Action\Discount\Data::class, []); - $this->quote = $this->createMock(\Magento\Quote\Model\Quote::class); + $this->quote = $this->createPartialMock( + \Magento\Quote\Model\Quote::class, + [ + 'getStore', + 'getCartFixedRules', + 'setCartFixedRules', + ] + ); $this->address = $this->createPartialMock( \Magento\Quote\Model\Quote\Address::class, - ['getCartFixedRules', 'setCartFixedRules', '__wakeup'] + ['__wakeup'] ); $this->item->expects($this->any())->method('getQuote')->will($this->returnValue($this->quote)); $this->item->expects($this->any())->method('getAddress')->will($this->returnValue($this->address)); @@ -101,7 +108,7 @@ public function testCalculate() { $this->rule->setData(['id' => 1, 'discount_amount' => 10.0]); - $this->address->expects($this->any())->method('getCartFixedRules')->will($this->returnValue([])); + $this->quote->expects($this->any())->method('getCartFixedRules')->will($this->returnValue([])); $store = $this->createMock(\Magento\Store\Model\Store::class); $this->priceCurrency->expects($this->atLeastOnce())->method('convert')->will($this->returnArgument(0)); $this->priceCurrency->expects($this->atLeastOnce())->method('round')->will($this->returnArgument(0)); @@ -145,7 +152,7 @@ public function testCalculate() $this->returnValue(100) ); - $this->address->expects($this->once())->method('setCartFixedRules')->with([1 => 0.0]); + $this->quote->expects($this->once())->method('setCartFixedRules')->with([1 => 0.0]); $this->model->calculate($this->rule, $this->item, 1); $this->assertEquals($this->data->getAmount(), 10); diff --git a/app/code/Magento/SalesRule/Test/Unit/Model/ValidatorTest.php b/app/code/Magento/SalesRule/Test/Unit/Model/ValidatorTest.php index e86068946ca78..946963dd8d234 100644 --- a/app/code/Magento/SalesRule/Test/Unit/Model/ValidatorTest.php +++ b/app/code/Magento/SalesRule/Test/Unit/Model/ValidatorTest.php @@ -13,7 +13,7 @@ use PHPUnit\Framework\MockObject\MockObject; /** - * Class ValidatorTest + * Tests for Magento\SalesRule\Model\Validator * @@SuppressWarnings(PHPMD.CouplingBetweenObjects) */ class ValidatorTest extends \PHPUnit\Framework\TestCase @@ -346,11 +346,14 @@ public function testInitTotalsCanApplyDiscount() 'getDiscountCalculationPrice', 'getBaseDiscountCalculationPrice', 'getCalculationPrice', - 'getParentItemId' + 'getParentItemId', + 'getParentItem' ] ); $item2 = clone $item1; - $items = [$item1, $item2]; + $item3 = clone $item1; + $item4 = clone $item1; + $items = [$item1, $item2, $item3, $item4]; $rule->expects($this->any()) ->method('getSimpleAction') @@ -367,12 +370,22 @@ public function testInitTotalsCanApplyDiscount() $validator->expects($this->at(0))->method('isValid')->with($item1)->willReturn(false); $validator->expects($this->at(1))->method('isValid')->with($item2)->willReturn(true); - $item1->expects($this->any())->method('getParentItemId')->willReturn(false); + $item1->expects($this->any())->method('getParentItemId')->willReturn(null); + $item1->expects($this->any())->method('getParentItem')->willReturn(null); $item1->expects($this->never())->method('getDiscountCalculationPrice'); $item1->expects($this->never())->method('getBaseDiscountCalculationPrice'); - $item2->expects($this->any())->method('getParentItemId')->willReturn(false); + $item2->expects($this->any())->method('getParentItemId')->willReturn(null); + $item2->expects($this->any())->method('getParentItem')->willReturn(null); $item2->expects($this->any())->method('getDiscountCalculationPrice')->willReturn(50); $item2->expects($this->once())->method('getBaseDiscountCalculationPrice')->willReturn(50); + $item3->expects($this->any())->method('getParentItemId')->willReturn(null); + $item3->expects($this->any())->method('getParentItem')->willReturn($item1); + $item3->expects($this->never())->method('getDiscountCalculationPrice'); + $item3->expects($this->never())->method('getBaseDiscountCalculationPrice'); + $item4->expects($this->any())->method('getParentItemId')->willReturn(12345); + $item4->expects($this->any())->method('getParentItem')->willReturn(null); + $item4->expects($this->never())->method('getDiscountCalculationPrice'); + $item4->expects($this->never())->method('getBaseDiscountCalculationPrice'); $this->utility->expects($this->once())->method('getItemQty')->willReturn(1); $this->utility->expects($this->any())->method('canProcessRule')->willReturn(true); diff --git a/app/code/Magento/SalesRule/Test/Unit/Observer/AssignCouponDataAfterOrderCustomerAssignObserverTest.php b/app/code/Magento/SalesRule/Test/Unit/Observer/AssignCouponDataAfterOrderCustomerAssignObserverTest.php new file mode 100644 index 0000000000000..f72a47a318de0 --- /dev/null +++ b/app/code/Magento/SalesRule/Test/Unit/Observer/AssignCouponDataAfterOrderCustomerAssignObserverTest.php @@ -0,0 +1,150 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ + +declare(strict_types=1); + +namespace Magento\SalesRule\Test\Unit\Observer; + +use Magento\Framework\Event; +use Magento\Framework\Event\Observer; +use Magento\Framework\TestFramework\Unit\Helper\ObjectManager; +use Magento\Sales\Api\Data\OrderInterface; +use Magento\SalesRule\Model\Coupon\UpdateCouponUsages; +use Magento\SalesRule\Observer\AssignCouponDataAfterOrderCustomerAssignObserver; +use PHPUnit\Framework\MockObject\MockObject; +use PHPUnit\Framework\TestCase; + +/** + * Unit test for Magento\SalesRule\Observer\AssignCouponDataAfterOrderCustomerAssignObserver + */ +class AssignCouponDataAfterOrderCustomerAssignObserverTest extends TestCase +{ + /* + * Stub event key order + */ + private const STUB_EVENT_KEY_ORDER = 'order'; + + /* + * Stub customer ID + */ + private const STUB_CUSTOMER_ID = 1; + + /** + * Testable Object + * + * @var AssignCouponDataAfterOrderCustomerAssignObserver + */ + private $observer; + + /** + * @var ObjectManager + */ + private $objectManager; + + /** + * @var Observer|MockObject + */ + private $observerMock; + + /** + * @var Event|MockObject + */ + private $eventMock; + + /** + * @var OrderInterface|MockObject + */ + private $orderMock; + + /** + * @var UpdateCouponUsages|MockObject + */ + private $updateCouponUsagesMock; + + protected function setUp(): void + { + $this->objectManager = new ObjectManager($this); + $this->observerMock = $this->createMock(Observer::class); + + $this->eventMock = $this->getMockBuilder(Event::class) + ->disableOriginalConstructor() + ->setMethods(['getData']) + ->getMock(); + + $this->orderMock = $this->getMockBuilder(OrderInterface::class) + ->disableOriginalConstructor() + ->getMock(); + + $this->updateCouponUsagesMock = $this->getMockBuilder(UpdateCouponUsages::class) + ->disableOriginalConstructor() + ->setMethods(['execute']) + ->getMock(); + + $this->observer = $this->objectManager->getObject( + AssignCouponDataAfterOrderCustomerAssignObserver::class, + [ + 'updateCouponUsages' => $this->updateCouponUsagesMock + ] + ); + } + + /** + * Test for execute(), covers test case for assign coupon data after order customer + */ + public function testExecuteAssignCouponData(): void + { + $this->observerMock + ->expects($this->once()) + ->method('getEvent') + ->willReturn($this->eventMock); + + $this->eventMock + ->expects($this->once()) + ->method('getData') + ->with(self::STUB_EVENT_KEY_ORDER) + ->willReturn($this->orderMock); + + $this->orderMock + ->expects($this->once()) + ->method('getCustomerId') + ->willReturn(self::STUB_CUSTOMER_ID); + + $this->updateCouponUsagesMock + ->expects($this->once()) + ->method('execute') + ->with($this->orderMock, true); + + $this->observer->execute($this->observerMock); + } + + /** + * Test for execute(), covers test case for assign coupon data after order customer with empty customer ID + */ + public function testExecuteAssignCouponDataWithEmptyCustomerId(): void + { + $this->observerMock + ->expects($this->once()) + ->method('getEvent') + ->willReturn($this->eventMock); + + $this->eventMock + ->expects($this->once()) + ->method('getData') + ->with(self::STUB_EVENT_KEY_ORDER) + ->willReturn($this->orderMock); + + $this->orderMock + ->expects($this->once()) + ->method('getCustomerId') + ->willReturn(null); + + $this->updateCouponUsagesMock + ->expects($this->never()) + ->method('execute'); + + $this->observer->execute($this->observerMock); + } +} diff --git a/app/code/Magento/SalesRule/etc/adminhtml/system.xml b/app/code/Magento/SalesRule/etc/adminhtml/system.xml index 0e30d1484c637..2655add5f8bbf 100644 --- a/app/code/Magento/SalesRule/etc/adminhtml/system.xml +++ b/app/code/Magento/SalesRule/etc/adminhtml/system.xml @@ -7,29 +7,29 @@ --> <config xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="urn:magento:module:Magento_Config:etc/system_file.xsd"> <system> - <section id="promo" translate="label" type="text" sortOrder="400" showInDefault="1" showInWebsite="0" showInStore="0"> + <section id="promo" translate="label" type="text" sortOrder="400" showInDefault="1"> <class>separator-top</class> <label>Promotions</label> <tab>customer</tab> <resource>Magento_SalesRule::config_promo</resource> <group id="auto_generated_coupon_codes" translate="label" showInDefault="1" sortOrder="10"> <label>Auto Generated Specific Coupon Codes</label> - <field id="length" translate="label comment" type="text" sortOrder="10" showInDefault="1" showInWebsite="0" showInStore="0" canRestore="1"> + <field id="length" translate="label comment" type="text" sortOrder="10" showInDefault="1" canRestore="1"> <label>Code Length</label> <comment>Excluding prefix, suffix and separators.</comment> <frontend_class>validate-digits</frontend_class> </field> - <field id="format" translate="label" type="select" sortOrder="20" showInDefault="1" showInWebsite="0" showInStore="0" canRestore="1"> + <field id="format" translate="label" type="select" sortOrder="20" showInDefault="1" canRestore="1"> <label>Code Format</label> <source_model>Magento\SalesRule\Model\System\Config\Source\Coupon\Format</source_model> </field> - <field id="prefix" translate="label" type="text" sortOrder="30" showInDefault="1" showInWebsite="0" showInStore="0"> + <field id="prefix" translate="label" type="text" sortOrder="30" showInDefault="1"> <label>Code Prefix</label> </field> - <field id="suffix" translate="label" type="text" sortOrder="40" showInDefault="1" showInWebsite="0" showInStore="0"> + <field id="suffix" translate="label" type="text" sortOrder="40" showInDefault="1"> <label>Code Suffix</label> </field> - <field id="dash" translate="label comment" type="text" sortOrder="50" showInDefault="1" showInWebsite="0" showInStore="0"> + <field id="dash" translate="label comment" type="text" sortOrder="50" showInDefault="1"> <label>Dash Every X Characters</label> <comment>If empty no separation.</comment> <frontend_class>validate-digits</frontend_class> diff --git a/app/code/Magento/SalesRule/etc/di.xml b/app/code/Magento/SalesRule/etc/di.xml index abb581175e36a..c4bc9c3a6decb 100644 --- a/app/code/Magento/SalesRule/etc/di.xml +++ b/app/code/Magento/SalesRule/etc/di.xml @@ -13,7 +13,7 @@ <preference for="Magento\SalesRule\Api\Data\ConditionInterface" type="Magento\SalesRule\Model\Data\Condition" /> <preference for="Magento\SalesRule\Api\Data\RuleSearchResultInterface" - type="Magento\SalesRule\Model\RuleSearchResult" /> + type="Magento\Framework\Api\SearchResults" /> <preference for="Magento\SalesRule\Api\Data\RuleLabelInterface" type="Magento\SalesRule\Model\Data\RuleLabel" /> <preference for="Magento\SalesRule\Api\Data\CouponInterface" @@ -23,7 +23,7 @@ <preference for="Magento\SalesRule\Model\Spi\CouponResourceInterface" type="Magento\SalesRule\Model\ResourceModel\Coupon" /> <preference for="Magento\SalesRule\Api\Data\CouponSearchResultInterface" - type="Magento\SalesRule\Model\CouponSearchResult" /> + type="Magento\Framework\Api\SearchResults" /> <preference for="Magento\SalesRule\Api\Data\CouponGenerationSpecInterface" type="Magento\SalesRule\Model\Data\CouponGenerationSpec" /> <preference for="Magento\SalesRule\Api\Data\CouponMassDeleteResultInterface" @@ -34,6 +34,10 @@ type="Magento\SalesRule\Model\Data\RuleDiscount" /> <preference for="Magento\SalesRule\Api\Data\DiscountDataInterface" type="Magento\SalesRule\Model\Data\DiscountData" /> + <preference for="Magento\SalesRule\Model\Spi\RuleQuoteRecollectTotalsInterface" + type="\Magento\SalesRule\Model\Rule\RuleQuoteRecollectTotalsOnDemand" /> + <preference for="Magento\SalesRule\Model\Spi\QuoteResetAppliedRulesInterface" + type="\Magento\SalesRule\Model\Rule\QuoteResetAppliedRules" /> <type name="Magento\SalesRule\Helper\Coupon"> <arguments> <argument name="couponParameters" xsi:type="array"> @@ -185,9 +189,9 @@ <type name="Magento\Quote\Model\Cart\CartTotalRepository"> <plugin name="coupon_label_plugin" type="Magento\SalesRule\Plugin\CartTotalRepository" /> </type> - <type name="Magento\Sales\Model\Order"> - <plugin name="coupon_uses_increment_plugin" type="Magento\SalesRule\Plugin\CouponUsagesIncrement" sortOrder="20"/> + <type name="Magento\Sales\Model\Service\OrderService"> <plugin name="coupon_uses_decrement_plugin" type="Magento\SalesRule\Plugin\CouponUsagesDecrement" /> + <plugin name="coupon_uses_increment_plugin" type="Magento\SalesRule\Plugin\CouponUsagesIncrement" sortOrder="20"/> </type> <preference for="Magento\SalesRule\Model\Spi\CodeLimitManagerInterface" diff --git a/app/code/Magento/SalesRule/etc/events.xml b/app/code/Magento/SalesRule/etc/events.xml index e421aafa96b55..c55c37de71aac 100644 --- a/app/code/Magento/SalesRule/etc/events.xml +++ b/app/code/Magento/SalesRule/etc/events.xml @@ -30,4 +30,13 @@ <event name="sales_quote_address_collect_totals_before"> <observer name="coupon_code_validation" instance="Magento\SalesRule\Observer\CouponCodeValidation" /> </event> + <event name="salesrule_rule_save_after"> + <observer name="salesrule_quote_recollect_totals_on_disabled" instance="\Magento\SalesRule\Observer\RuleQuoteRecollectTotalsObserver" /> + </event> + <event name="salesrule_rule_delete_after"> + <observer name="salesrule_quote_recollect_totals_on_delete" instance="\Magento\SalesRule\Observer\RuleQuoteRecollectTotalsObserver" /> + </event> + <event name="sales_quote_collect_totals_before"> + <observer name="salesrule_sales_quote_collect_totals_before" instance="\Magento\SalesRule\Observer\QuoteResetAppliedRulesObserver" /> + </event> </config> diff --git a/app/code/Magento/SalesRule/registration.php b/app/code/Magento/SalesRule/registration.php index b3e6cf5bc1b14..53305ca8287f7 100644 --- a/app/code/Magento/SalesRule/registration.php +++ b/app/code/Magento/SalesRule/registration.php @@ -4,6 +4,6 @@ * See COPYING.txt for license details. */ -use \Magento\Framework\Component\ComponentRegistrar; +use Magento\Framework\Component\ComponentRegistrar; ComponentRegistrar::register(ComponentRegistrar::MODULE, 'Magento_SalesRule', __DIR__); diff --git a/app/code/Magento/SalesRule/view/frontend/requirejs-config.js b/app/code/Magento/SalesRule/view/frontend/requirejs-config.js new file mode 100644 index 0000000000000..13b701c6fe65a --- /dev/null +++ b/app/code/Magento/SalesRule/view/frontend/requirejs-config.js @@ -0,0 +1,14 @@ +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ + +var config = { + config: { + mixins: { + 'Magento_Checkout/js/action/select-payment-method': { + 'Magento_SalesRule/js/action/select-payment-method-mixin': true + } + } + } +}; diff --git a/app/code/Magento/SalesRule/view/frontend/web/js/action/select-payment-method-mixin.js b/app/code/Magento/SalesRule/view/frontend/web/js/action/select-payment-method-mixin.js new file mode 100644 index 0000000000000..0b8eba270b030 --- /dev/null +++ b/app/code/Magento/SalesRule/view/frontend/web/js/action/select-payment-method-mixin.js @@ -0,0 +1,51 @@ +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +define([ + 'jquery', + 'mage/utils/wrapper', + 'Magento_Checkout/js/model/quote', + 'Magento_SalesRule/js/model/payment/discount-messages', + 'Magento_Checkout/js/action/set-payment-information-extended', + 'Magento_Checkout/js/action/get-totals', + 'Magento_SalesRule/js/model/coupon' +], function ($, wrapper, quote, messageContainer, setPaymentInformationExtended, getTotalsAction, coupon) { + 'use strict'; + + return function (selectPaymentMethodAction) { + + return wrapper.wrap(selectPaymentMethodAction, function (originalSelectPaymentMethodAction, paymentMethod) { + + originalSelectPaymentMethodAction(paymentMethod); + + $.when( + setPaymentInformationExtended( + messageContainer, + { + method: paymentMethod.method + }, + true + ) + ).done( + function () { + var deferred = $.Deferred(), + + /** + * Update coupon form. + */ + updateCouponCallback = function () { + if (quote.totals() && !quote.totals()['coupon_code']) { + coupon.setCouponCode(''); + coupon.setIsApplied(false); + } + }; + + getTotalsAction([], deferred); + $.when(deferred).done(updateCouponCallback); + } + ); + }); + }; + +}); diff --git a/app/code/Magento/SalesRule/view/frontend/web/js/model/coupon.js b/app/code/Magento/SalesRule/view/frontend/web/js/model/coupon.js new file mode 100644 index 0000000000000..1e3e057bbb401 --- /dev/null +++ b/app/code/Magento/SalesRule/view/frontend/web/js/model/coupon.js @@ -0,0 +1,49 @@ +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +/** + * Coupon model. + */ +define([ + 'ko', + 'domReady!' +], function (ko) { + 'use strict'; + + var couponCode = ko.observable(null), + isApplied = ko.observable(null); + + return { + couponCode: couponCode, + isApplied: isApplied, + + /** + * @return {*} + */ + getCouponCode: function () { + return couponCode; + }, + + /** + * @return {Boolean} + */ + getIsApplied: function () { + return isApplied; + }, + + /** + * @param {*} couponCodeValue + */ + setCouponCode: function (couponCodeValue) { + couponCode(couponCodeValue); + }, + + /** + * @param {Boolean} isAppliedValue + */ + setIsApplied: function (isAppliedValue) { + isApplied(isAppliedValue); + } + }; +}); diff --git a/app/code/Magento/SalesRule/view/frontend/web/js/view/payment/discount.js b/app/code/Magento/SalesRule/view/frontend/web/js/view/payment/discount.js index d2902d8863f3d..9c83cb7ba40ba 100644 --- a/app/code/Magento/SalesRule/view/frontend/web/js/view/payment/discount.js +++ b/app/code/Magento/SalesRule/view/frontend/web/js/view/payment/discount.js @@ -9,18 +9,19 @@ define([ 'uiComponent', 'Magento_Checkout/js/model/quote', 'Magento_SalesRule/js/action/set-coupon-code', - 'Magento_SalesRule/js/action/cancel-coupon' -], function ($, ko, Component, quote, setCouponCodeAction, cancelCouponAction) { + 'Magento_SalesRule/js/action/cancel-coupon', + 'Magento_SalesRule/js/model/coupon' +], function ($, ko, Component, quote, setCouponCodeAction, cancelCouponAction, coupon) { 'use strict'; var totals = quote.getTotals(), - couponCode = ko.observable(null), - isApplied; + couponCode = coupon.getCouponCode(), + isApplied = coupon.getIsApplied(); if (totals()) { couponCode(totals()['coupon_code']); } - isApplied = ko.observable(couponCode() != null); + isApplied(couponCode() != null); return Component.extend({ defaults: { diff --git a/app/code/Magento/SalesSequence/Model/ResourceModel/Meta.php b/app/code/Magento/SalesSequence/Model/ResourceModel/Meta.php index 7a4aabea85680..8422123c0ca84 100644 --- a/app/code/Magento/SalesSequence/Model/ResourceModel/Meta.php +++ b/app/code/Magento/SalesSequence/Model/ResourceModel/Meta.php @@ -132,6 +132,7 @@ protected function _beforeSave(\Magento\Framework\Model\AbstractModel $object) || $object->getData('store_id') === null || !$object->getData('sequence_table') ) { + // phpcs:ignore Magento2.Exceptions.DirectThrow throw new Exception(__('Not enough arguments')); } diff --git a/app/code/Magento/SalesSequence/Model/Sequence/DeleteByStore.php b/app/code/Magento/SalesSequence/Model/Sequence/DeleteByStore.php new file mode 100644 index 0000000000000..e86cc8b1b2e6d --- /dev/null +++ b/app/code/Magento/SalesSequence/Model/Sequence/DeleteByStore.php @@ -0,0 +1,117 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +declare(strict_types=1); + +namespace Magento\SalesSequence\Model\Sequence; + +use Magento\Framework\App\ResourceConnection as AppResource; +use Magento\SalesSequence\Model\MetaFactory; +use Magento\SalesSequence\Model\ResourceModel\Meta as ResourceMetadata; + +/** + * Delete Sequence by Store. + */ +class DeleteByStore +{ + /** + * @var ResourceMetadata + */ + private $resourceMetadata; + + /** + * @var MetaFactory + */ + private $metaFactory; + + /** + * @var AppResource + */ + private $appResource; + + /** + * @param ResourceMetadata $resourceMetadata + * @param MetaFactory $metaFactory + * @param AppResource $appResource + */ + public function __construct( + ResourceMetadata $resourceMetadata, + MetaFactory $metaFactory, + AppResource $appResource + ) { + $this->resourceMetadata = $resourceMetadata; + $this->metaFactory = $metaFactory; + $this->appResource = $appResource; + } + + /** + * Deletes all sequence linked entites + * + * @param int $storeId + * @return void + * @throws \Exception + */ + public function execute($storeId): void + { + $metadataIds = $this->getMetadataIdsByStoreId($storeId); + $profileIds = $this->getProfileIdsByMetadataIds($metadataIds); + + $this->appResource->getConnection()->delete( + $this->appResource->getTableName('sales_sequence_profile'), + ['profile_id IN (?)' => $profileIds] + ); + + foreach ($metadataIds as $metadataId) { + $metadata = $this->metaFactory->create(); + $this->resourceMetadata->load($metadata, $metadataId); + if (!$metadata->getId()) { + continue; + } + + $this->appResource->getConnection()->dropTable( + $metadata->getSequenceTable() + ); + $this->resourceMetadata->delete($metadata); + } + } + + /** + * Retrieves Metadata Ids by store id + * + * @param int $storeId + * @return int[] + */ + private function getMetadataIdsByStoreId($storeId) + { + $connection = $this->appResource->getConnection(); + $bind = ['store_id' => $storeId]; + $select = $connection->select()->from( + $this->appResource->getTableName('sales_sequence_meta'), + ['meta_id'] + )->where( + 'store_id = :store_id' + ); + + return $connection->fetchCol($select, $bind); + } + + /** + * Retrieves Profile Ids by metadata ids + * + * @param int[] $metadataIds + * @return int[] + */ + private function getProfileIdsByMetadataIds(array $metadataIds) + { + $connection = $this->appResource->getConnection(); + $select = $connection->select() + ->from( + $this->appResource->getTableName('sales_sequence_profile'), + ['profile_id'] + )->where('meta_id IN (?)', $metadataIds); + + return $connection->fetchCol($select); + } +} diff --git a/app/code/Magento/SalesSequence/Observer/SequenceRemovalObserver.php b/app/code/Magento/SalesSequence/Observer/SequenceRemovalObserver.php new file mode 100644 index 0000000000000..c10ef80f6eb9b --- /dev/null +++ b/app/code/Magento/SalesSequence/Observer/SequenceRemovalObserver.php @@ -0,0 +1,48 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +declare(strict_types=1); + +namespace Magento\SalesSequence\Observer; + +use Magento\Framework\Event\ObserverInterface; +use Magento\Framework\Event\Observer as EventObserver; +use Magento\SalesSequence\Model\Sequence\DeleteByStore; + +/** + * Observer for Sequence Removal. + */ +class SequenceRemovalObserver implements ObserverInterface +{ + /** + * @var DeleteByStore + */ + private $deleteByStore; + + /** + * @param DeleteByStore $deleteByStore + */ + public function __construct( + DeleteByStore $deleteByStore + ) { + $this->deleteByStore = $deleteByStore; + } + + /** + * Deletes all sequence linked entities. + * + * @param EventObserver $observer + * @return $this + * @throws \Magento\Framework\Exception\LocalizedException + */ + public function execute(EventObserver $observer) + { + if ($store = $observer->getData('store')) { + $this->deleteByStore->execute($store->getId()); + } + + return $this; + } +} diff --git a/app/code/Magento/SalesSequence/Test/Unit/Model/Sequence/DeleteByStoreTest.php b/app/code/Magento/SalesSequence/Test/Unit/Model/Sequence/DeleteByStoreTest.php new file mode 100644 index 0000000000000..17dbd6c37265f --- /dev/null +++ b/app/code/Magento/SalesSequence/Test/Unit/Model/Sequence/DeleteByStoreTest.php @@ -0,0 +1,147 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +namespace Magento\SalesSequence\Test\Unit\Model\Sequence; + +use Magento\Framework\App\ResourceConnection; +use Magento\Framework\DB\Adapter\AdapterInterface; +use Magento\Framework\DB\Select; +use Magento\Framework\TestFramework\Unit\Helper\ObjectManager; +use Magento\SalesSequence\Model\Meta; +use Magento\SalesSequence\Model\MetaFactory; +use Magento\SalesSequence\Model\ResourceModel\Meta as ResourceMeta; +use Magento\SalesSequence\Model\Sequence\DeleteByStore; +use PHPUnit\Framework\MockObject\MockObject; +use PHPUnit\Framework\TestCase; + +/** + * Test for \Magento\SalesSequence\Model\Sequence\DeleteByStore class. + */ +class DeleteByStoreTest extends TestCase +{ + /** + * @var DeleteByStore + */ + private $deleteByStore; + + /** + * @var ResourceMeta | MockObject + */ + private $resourceSequenceMeta; + + /** + * @var Meta | MockObject + */ + private $meta; + + /** + * @var MetaFactory | MockObject + */ + private $metaFactory; + + /** + * @var AdapterInterface | MockObject + */ + private $connectionMock; + + /** + * @var ResourceConnection | MockObject + */ + private $resourceMock; + + /** + * @var Select | MockObject + */ + private $select; + + protected function setUp() + { + $this->connectionMock = $this->getMockForAbstractClass( + AdapterInterface::class, + [], + '', + false, + false, + true, + ['delete', 'query'] + ); + $this->resourceSequenceMeta = $this->createPartialMock( + ResourceMeta::class, + ['load', 'delete'] + ); + $this->meta = $this->createPartialMock( + Meta::class, + ['getSequenceTable'] + ); + $this->resourceMock = $this->createMock(ResourceConnection::class); + $this->select = $this->createMock(Select::class); + $this->metaFactory = $this->createPartialMock(MetaFactory::class, ['create']); + $this->metaFactory->method('create')->willReturn($this->meta); + + $helper = new ObjectManager($this); + $this->deleteByStore = $helper->getObject( + DeleteByStore::class, + [ + 'resourceMetadata' => $this->resourceSequenceMeta, + 'metaFactory' => $this->metaFactory, + 'appResource' => $this->resourceMock, + ] + ); + } + + /** + * @throws \Exception + * @SuppressWarnings(PHPMD.UnusedLocalVariable) + */ + public function testExecute() + { + $profileTableName = 'sales_sequence_profile'; + $storeId = 1; + $metadataIds = [1, 2]; + $profileIds = [10, 11]; + $this->resourceMock->method('getTableName') + ->willReturnCallback( + static function ($tableName) { + return $tableName; + } + ); + $this->resourceMock->method('getConnection') + ->willReturn($this->connectionMock); + $this->connectionMock + ->method('select') + ->willReturn($this->select); + + $this->select->method('from') + ->willReturn($this->select); + $this->select->method('where') + ->willReturn($this->select); + + $this->connectionMock->method('fetchCol') + ->willReturnCallback( + static function ($arg, $arg2) use ($metadataIds, $profileIds) { + if (array_key_exists('store', $arg2)) { + return $metadataIds; + } + + return $profileIds; + } + ); + + $this->connectionMock->expects($this->once()) + ->method('delete') + ->with($profileTableName, ['profile_id IN (?)' => $profileIds]) + ->willReturn(2); + $this->resourceSequenceMeta + ->method('load') + ->willReturn($this->meta); + $this->connectionMock + ->method('dropTable') + ->willReturn(true); + $this->resourceSequenceMeta + ->method('delete') + ->willReturn($this->resourceSequenceMeta); + $this->deleteByStore->execute($storeId); + } +} diff --git a/app/code/Magento/SalesSequence/etc/events.xml b/app/code/Magento/SalesSequence/etc/events.xml new file mode 100644 index 0000000000000..5bbde6ab188a5 --- /dev/null +++ b/app/code/Magento/SalesSequence/etc/events.xml @@ -0,0 +1,12 @@ +<?xml version="1.0"?> +<!-- +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +--> +<config xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="urn:magento:framework:Event/etc/events.xsd"> + <event name="store_delete"> + <observer name="magento_sequence" instance="Magento\SalesSequence\Observer\SequenceRemovalObserver" /> + </event> +</config> diff --git a/app/code/Magento/SalesSequence/registration.php b/app/code/Magento/SalesSequence/registration.php index 57b9c79bc1055..576b3dc5b9efb 100644 --- a/app/code/Magento/SalesSequence/registration.php +++ b/app/code/Magento/SalesSequence/registration.php @@ -4,6 +4,6 @@ * See COPYING.txt for license details. */ -use \Magento\Framework\Component\ComponentRegistrar; +use Magento\Framework\Component\ComponentRegistrar; ComponentRegistrar::register(ComponentRegistrar::MODULE, 'Magento_SalesSequence', __DIR__); diff --git a/app/code/Magento/SampleData/registration.php b/app/code/Magento/SampleData/registration.php index 05c37ae1aec60..2e86fc7e1cc48 100644 --- a/app/code/Magento/SampleData/registration.php +++ b/app/code/Magento/SampleData/registration.php @@ -4,6 +4,6 @@ * See COPYING.txt for license details. */ -use \Magento\Framework\Component\ComponentRegistrar; +use Magento\Framework\Component\ComponentRegistrar; ComponentRegistrar::register(ComponentRegistrar::MODULE, 'Magento_SampleData', __DIR__); diff --git a/app/code/Magento/Search/Block/Term.php b/app/code/Magento/Search/Block/Term.php index b27ef6b01fda2..ed6a2c5215b54 100644 --- a/app/code/Magento/Search/Block/Term.php +++ b/app/code/Magento/Search/Block/Term.php @@ -9,6 +9,8 @@ */ namespace Magento\Search\Block; +use Magento\Framework\DataObject; +use Magento\Framework\Exception\NoSuchEntityException; use Magento\Framework\UrlFactory; use Magento\Framework\UrlInterface; use Magento\Framework\View\Element\Template; @@ -16,6 +18,8 @@ use Magento\Search\Model\ResourceModel\Query\CollectionFactory; /** + * Terms and conditions block + * * @api * @since 100.0.2 */ @@ -37,15 +41,11 @@ class Term extends Template protected $_maxPopularity; /** - * Url factory - * * @var UrlFactory */ protected $_urlFactory; /** - * Query collection factory - * * @var CollectionFactory */ protected $_queryCollectionFactory; @@ -71,17 +71,17 @@ public function __construct( * Load terms and try to sort it by names * * @return $this - * @throws \Magento\Framework\Exception\NoSuchEntityException + * @throws NoSuchEntityException */ protected function _loadTerms() { if (empty($this->_terms)) { $this->_terms = []; - $terms = $this->_queryCollectionFactory->create()->setPopularQueryFilter( - $this->_storeManager->getStore()->getId() - )->setPageSize( - 100 - )->load()->getItems(); + $terms = $this->_queryCollectionFactory->create() + ->setPopularQueryFilter($this->_storeManager->getStore()->getId()) + ->setPageSize(100) + ->load() + ->getItems(); if (count($terms) == 0) { return $this; @@ -91,6 +91,7 @@ protected function _loadTerms() $this->_minPopularity = end($terms)->getPopularity(); $range = $this->_maxPopularity - $this->_minPopularity; $range = $range == 0 ? 1 : $range; + $termKeys = []; foreach ($terms as $term) { if (!$term->getPopularity()) { continue; @@ -99,6 +100,7 @@ protected function _loadTerms() $temp[$term->getQueryText()] = $term; $termKeys[] = $term->getQueryText(); } + natcasesort($termKeys); foreach ($termKeys as $termKey) { @@ -109,8 +111,10 @@ protected function _loadTerms() } /** + * Load and return terms + * * @return array - * @throws \Magento\Framework\Exception\NoSuchEntityException + * @throws NoSuchEntityException */ public function getTerms() { @@ -119,7 +123,9 @@ public function getTerms() } /** - * @param \Magento\Framework\DataObject $obj + * Return search url + * + * @param DataObject $obj * @return string */ public function getSearchUrl($obj) @@ -135,6 +141,8 @@ public function getSearchUrl($obj) } /** + * Return max popularity + * * @return int */ public function getMaxPopularity() @@ -143,6 +151,8 @@ public function getMaxPopularity() } /** + * Return min popularity + * * @return int */ public function getMinPopularity() diff --git a/app/code/Magento/Search/Controller/Term/Popular.php b/app/code/Magento/Search/Controller/Term/Popular.php index 0573fe6a81a12..2b3ac5c73c9dd 100644 --- a/app/code/Magento/Search/Controller/Term/Popular.php +++ b/app/code/Magento/Search/Controller/Term/Popular.php @@ -3,59 +3,78 @@ * Copyright © Magento, Inc. All rights reserved. * See COPYING.txt for license details. */ +declare(strict_types=1); + namespace Magento\Search\Controller\Term; -use Magento\Framework\App\Action\Action; -use Magento\Framework\App\Action\Context; +use Magento\Framework\App\Action\HttpGetActionInterface; use Magento\Framework\App\Config\ScopeConfigInterface; -use Magento\Framework\App\RequestInterface; +use Magento\Framework\Controller\Result\ForwardFactory as ResultForwardFactory; +use Magento\Framework\View\Result\PageFactory as ResultPageFactory; use Magento\Store\Model\ScopeInterface; -use Magento\Framework\Controller\ResultFactory; -class Popular extends Action +/** + * Popular search terms page + */ +class Popular implements HttpGetActionInterface { + private const XML_PATH_SEO_SEARCH_TERMS = 'catalog/seo/search_terms'; + /** - * @var \Magento\Framework\App\Config\ScopeConfigInterface + * @var ResultForwardFactory */ - protected $scopeConfig; + private $resultForwardFactory; /** - * @param \Magento\Framework\App\Action\Context $context - * @param \Magento\Framework\App\Config\ScopeConfigInterface $scopeConfig + * @var ResultPageFactory */ - public function __construct(Context $context, ScopeConfigInterface $scopeConfig) - { + private $resultPageFactory; + + /** + * @var ScopeConfigInterface + */ + private $scopeConfig; + + /** + * @param ResultForwardFactory $resultForwardFactory + * @param ResultPageFactory $resultPageFactory + * @param ScopeConfigInterface $scopeConfig + */ + public function __construct( + ResultForwardFactory $resultForwardFactory, + ResultPageFactory $resultPageFactory, + ScopeConfigInterface $scopeConfig + ) { + $this->resultForwardFactory = $resultForwardFactory; + $this->resultPageFactory = $resultPageFactory; $this->scopeConfig = $scopeConfig; - parent::__construct($context); } /** - * Dispatch request - * - * @param \Magento\Framework\App\RequestInterface $request - * @return \Magento\Framework\App\ResponseInterface - * @throws \Magento\Framework\Exception\NotFoundException + * @inheritDoc */ - public function dispatch(RequestInterface $request) + public function execute() { - $searchTerms = $this->scopeConfig->getValue( - 'catalog/seo/search_terms', - ScopeInterface::SCOPE_STORE - ); - if (!$searchTerms) { - $this->_redirect('noroute'); - $this->_actionFlag->set('', self::FLAG_NO_DISPATCH, true); + if (!$this->checkEnabledSearchTerms()) { + $resultForward = $this->resultForwardFactory->create(); + $resultForward->forward('noroute'); + + return $resultForward; } - return parent::dispatch($request); + + return $this->resultPageFactory->create(); } /** - * @return \Magento\Framework\View\Result\Page + * Check if search terms are enabled + * + * @return bool */ - public function execute() + private function checkEnabledSearchTerms(): bool { - /** @var \Magento\Framework\View\Result\Page $resultPage */ - $resultPage = $this->resultFactory->create(ResultFactory::TYPE_PAGE); - return $resultPage; + return $this->scopeConfig->isSetFlag( + self::XML_PATH_SEO_SEARCH_TERMS, + ScopeInterface::SCOPE_STORE + ); } } diff --git a/app/code/Magento/Search/Model/Autocomplete.php b/app/code/Magento/Search/Model/Autocomplete.php index b50cd330d5d95..45957e8795744 100644 --- a/app/code/Magento/Search/Model/Autocomplete.php +++ b/app/code/Magento/Search/Model/Autocomplete.php @@ -5,6 +5,9 @@ */ namespace Magento\Search\Model; +/** + * Provides list of Autocomplete items + */ class Autocomplete implements AutocompleteInterface { /** @@ -23,15 +26,15 @@ public function __construct( } /** - * {@inheritdoc} + * @inheritdoc */ public function getItems() { - $data = []; + $data = [[]]; foreach ($this->dataProviders as $dataProvider) { - $data = array_merge($data, $dataProvider->getItems()); + $data[] = $dataProvider->getItems(); } - return $data; + return array_merge(...$data); } } diff --git a/app/code/Magento/Search/Model/PopularSearchTerms.php b/app/code/Magento/Search/Model/PopularSearchTerms.php index d5ddc0e1dac5f..0a3a7de4e1271 100644 --- a/app/code/Magento/Search/Model/PopularSearchTerms.php +++ b/app/code/Magento/Search/Model/PopularSearchTerms.php @@ -7,7 +7,7 @@ namespace Magento\Search\Model; /** - * Popular search terms + * Finds top search results in search */ class PopularSearchTerms { @@ -29,7 +29,7 @@ class PopularSearchTerms /** * @param \Magento\Framework\App\Config\ScopeConfigInterface $scopeConfig - * @param \Magento\Search\Model\ResourceModel\Query\Collection + * @param \Magento\Search\Model\ResourceModel\Query\Collection $queryCollection */ public function __construct( \Magento\Framework\App\Config\ScopeConfigInterface $scopeConfig, @@ -48,13 +48,8 @@ public function __construct( */ public function isCacheable(string $term, int $storeId) { - $terms = $this->queryCollection - ->setPopularQueryFilter($storeId) - ->setPageSize($this->getMaxCountCacheableSearchTerms($storeId)) - ->load() - ->getColumnValues('query_text'); - - return in_array($term, $terms); + $maxCountCacheableSearchTerms = $this->getMaxCountCacheableSearchTerms($storeId); + return $this->queryCollection->isTopSearchResult($term, $storeId, $maxCountCacheableSearchTerms); } /** diff --git a/app/code/Magento/Search/Model/ResourceModel/Query/Collection.php b/app/code/Magento/Search/Model/ResourceModel/Query/Collection.php index a1bc1df3f9bdb..2fc71fc6a6d73 100644 --- a/app/code/Magento/Search/Model/ResourceModel/Query/Collection.php +++ b/app/code/Magento/Search/Model/ResourceModel/Query/Collection.php @@ -5,15 +5,25 @@ */ namespace Magento\Search\Model\ResourceModel\Query; +use Magento\Framework\Data\Collection\Db\FetchStrategyInterface; +use Magento\Framework\Data\Collection\EntityFactoryInterface; +use Magento\Framework\DB\Adapter\AdapterInterface; +use Magento\Framework\DB\Helper; +use Magento\Framework\Event\ManagerInterface; +use Magento\Framework\Model\ResourceModel\Db\AbstractDb; +use Magento\Framework\Model\ResourceModel\Db\Collection\AbstractCollection; use Magento\Store\Model\Store; +use Magento\Store\Model\StoreManagerInterface; +use Psr\Log\LoggerInterface; /** * Search query collection * * @api * @since 100.0.2 + * @SuppressWarnings(PHPMD.CouplingBetweenObjects) */ -class Collection extends \Magento\Framework\Model\ResourceModel\Db\Collection\AbstractCollection +class Collection extends AbstractCollection { /** * Store for filter @@ -25,36 +35,38 @@ class Collection extends \Magento\Framework\Model\ResourceModel\Db\Collection\Ab /** * Store manager * - * @var \Magento\Store\Model\StoreManagerInterface + * @var StoreManagerInterface */ protected $_storeManager; /** * Search resource helper * - * @var \Magento\Framework\DB\Helper + * @var Helper */ protected $_resourceHelper; /** - * @param \Magento\Framework\Data\Collection\EntityFactory $entityFactory - * @param \Psr\Log\LoggerInterface $logger - * @param \Magento\Framework\Data\Collection\Db\FetchStrategyInterface $fetchStrategy - * @param \Magento\Framework\Event\ManagerInterface $eventManager - * @param \Magento\Store\Model\StoreManagerInterface $storeManager - * @param \Magento\Framework\DB\Helper $resourceHelper - * @param \Magento\Framework\DB\Adapter\AdapterInterface $connection - * @param \Magento\Framework\Model\ResourceModel\Db\AbstractDb $resource + * Constructor + * + * @param EntityFactoryInterface $entityFactory + * @param LoggerInterface $logger + * @param FetchStrategyInterface $fetchStrategy + * @param ManagerInterface $eventManager + * @param StoreManagerInterface $storeManager + * @param Helper $resourceHelper + * @param AdapterInterface $connection + * @param AbstractDb $resource */ public function __construct( - \Magento\Framework\Data\Collection\EntityFactoryInterface $entityFactory, - \Psr\Log\LoggerInterface $logger, - \Magento\Framework\Data\Collection\Db\FetchStrategyInterface $fetchStrategy, - \Magento\Framework\Event\ManagerInterface $eventManager, - \Magento\Store\Model\StoreManagerInterface $storeManager, - \Magento\Framework\DB\Helper $resourceHelper, - \Magento\Framework\DB\Adapter\AdapterInterface $connection = null, - \Magento\Framework\Model\ResourceModel\Db\AbstractDb $resource = null + EntityFactoryInterface $entityFactory, + LoggerInterface $logger, + FetchStrategyInterface $fetchStrategy, + ManagerInterface $eventManager, + StoreManagerInterface $storeManager, + Helper $resourceHelper, + AdapterInterface $connection = null, + AbstractDb $resource = null ) { $this->_storeManager = $storeManager; $this->_resourceHelper = $resourceHelper; @@ -149,6 +161,36 @@ public function setPopularQueryFilter($storeIds = null) return $this; } + /** + * Determines whether a Search Term belongs to the top results for given storeId + * + * @param string $term + * @param int $storeId + * @param int $maxCountCacheableSearchTerms + * @return bool + */ + public function isTopSearchResult(string $term, int $storeId, int $maxCountCacheableSearchTerms):bool + { + $select = $this->getSelect(); + $select->reset(\Magento\Framework\DB\Select::FROM); + $select->reset(\Magento\Framework\DB\Select::COLUMNS); + $select->distinct(true); + $select->from(['main_table' => $this->getTable('search_query')], ['query_text']); + $select->where('main_table.store_id IN (?)', $storeId); + $select->where('num_results > 0'); + $select->order(['popularity desc']); + + $select->limit($maxCountCacheableSearchTerms); + + $subQuery = new \Zend_Db_Expr('(' . $select->assemble() . ')'); + + $select->reset(); + $select->from(['result' => $subQuery ], []); + $select->where('result.query_text = ?', $term); + + return $this->getSize() > 0; + } + /** * Set Recent Queries Order * diff --git a/app/code/Magento/Search/Model/SynonymGroupRepository.php b/app/code/Magento/Search/Model/SynonymGroupRepository.php index 75d7049afd949..dbc2b66b1f047 100644 --- a/app/code/Magento/Search/Model/SynonymGroupRepository.php +++ b/app/code/Magento/Search/Model/SynonymGroupRepository.php @@ -7,6 +7,7 @@ namespace Magento\Search\Model; use Magento\Framework\Exception\CouldNotDeleteException; +use Magento\Framework\Phrase; use Magento\Search\Api\Data\SynonymGroupInterface; use Magento\Search\Api\SynonymGroupRepositoryInterface; use Magento\Search\Model\ResourceModel\SynonymGroup as SynonymGroupResourceModel; @@ -37,7 +38,7 @@ class SynonymGroupRepository implements SynonymGroupRepositoryInterface * @param SynonymGroupResourceModel $resourceModel */ public function __construct( - \Magento\Search\Model\SynonymGroupFactory $synonymGroupFactory, + SynonymGroupFactory $synonymGroupFactory, SynonymGroupResourceModel $resourceModel ) { $this->synonymGroupFactory = $synonymGroupFactory; @@ -64,8 +65,8 @@ public function save(SynonymGroupInterface $synonymGroup, $errorOnMergeConflict * Deletes a synonym group * * @param SynonymGroupInterface $synonymGroup - * @throws CouldNotDeleteException * @return bool + * @throws CouldNotDeleteException */ public function delete(SynonymGroupInterface $synonymGroup) { @@ -149,17 +150,17 @@ private function create(SynonymGroupInterface $synonymGroup, $errorOnMergeConfli */ private function merge(SynonymGroupInterface $synonymGroupToMerge, array $matchingGroupIds) { - $mergedSynonyms = []; + $mergedSynonyms = [[]]; foreach ($matchingGroupIds as $groupId) { /** @var SynonymGroup $synonymGroupModel */ $synonymGroupModel = $this->synonymGroupFactory->create(); $synonymGroupModel->load($groupId); - $mergedSynonyms = array_merge($mergedSynonyms, explode(',', $synonymGroupModel->getSynonymGroup())); + $mergedSynonyms[] = explode(',', $synonymGroupModel->getSynonymGroup()); $synonymGroupModel->delete(); } - $mergedSynonyms = array_merge($mergedSynonyms, explode(',', $synonymGroupToMerge->getSynonymGroup())); - $mergedSynonyms = array_unique($mergedSynonyms); - return $mergedSynonyms; + $mergedSynonyms[] = explode(',', $synonymGroupToMerge->getSynonymGroup()); + + return array_unique(array_merge(...$mergedSynonyms)); } /** @@ -222,7 +223,7 @@ private function update( * Gets merge conflict exception message * * @param string[] $matchingSynonymGroups - * @return \Magento\Framework\Phrase + * @return Phrase */ private function getExceptionMessage($matchingSynonymGroups) { diff --git a/app/code/Magento/Search/Test/Mftf/ActionGroup/AdminCreateNewSearchTermEntityActionGroup.xml b/app/code/Magento/Search/Test/Mftf/ActionGroup/AdminCreateNewSearchTermEntityActionGroup.xml new file mode 100644 index 0000000000000..5fb8f9c2b6b47 --- /dev/null +++ b/app/code/Magento/Search/Test/Mftf/ActionGroup/AdminCreateNewSearchTermEntityActionGroup.xml @@ -0,0 +1,25 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + /** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +--> + +<actionGroups xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/actionGroupSchema.xsd"> + <actionGroup name="AdminCreateNewSearchTermEntityActionGroup"> + <arguments> + <argument name="query" type="string"/> + <argument name="store" type="string"/> + <argument name="redirectUrl" type="string"/> + </arguments> + + <click selector="{{AdminSearchTermsGridSection.addNewEntity}}" stepKey="clickAddNewButton"/> + <fillField stepKey="fillSearchQueryField" selector="{{AdminSearchTermFormSection.searchQuery}}" userInput="{{query}}"/> + <selectOption stepKey="storeSelect" selector="{{AdminSearchTermFormSection.storeID}}" userInput="{{store}}"/> + <fillField stepKey="fillRedirectUrl" selector="{{AdminSearchTermFormSection.redirectUrl}}" userInput="{{redirectUrl}}"/> + <click stepKey="saveSearchTerm" selector="{{AdminSearchTermFormSection.save}}"/> + <waitForPageLoad stepKey="waitForPageLoad"/> + </actionGroup> +</actionGroups> diff --git a/app/code/Magento/Search/Test/Mftf/ActionGroup/StoreFrontQuickSearchActionGroup.xml b/app/code/Magento/Search/Test/Mftf/ActionGroup/StoreFrontQuickSearchActionGroup.xml new file mode 100644 index 0000000000000..aec874e7b6d85 --- /dev/null +++ b/app/code/Magento/Search/Test/Mftf/ActionGroup/StoreFrontQuickSearchActionGroup.xml @@ -0,0 +1,20 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + /** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +--> + +<actionGroups xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/actionGroupSchema.xsd"> + <actionGroup name="StoreFrontQuickSearchActionGroup"> + <arguments> + <argument name="query" type="string"/> + </arguments> + + <fillField stepKey="fillSearchField" selector="{{StorefrontQuickSearchSection.searchPhrase}}" userInput="{{query}}"/> + <waitForElementVisible selector="{{StorefrontQuickSearchSection.searchButton}}" stepKey="waitForSubmitButton"/> + <click stepKey="clickSearchButton" selector="{{StorefrontQuickSearchSection.searchButton}}"/> + </actionGroup> +</actionGroups> diff --git a/app/code/Magento/Search/Test/Mftf/Metadata/search_term-meta.xml b/app/code/Magento/Search/Test/Mftf/Metadata/SearchTermMeta.xml similarity index 100% rename from app/code/Magento/Search/Test/Mftf/Metadata/search_term-meta.xml rename to app/code/Magento/Search/Test/Mftf/Metadata/SearchTermMeta.xml diff --git a/app/code/Magento/Search/Test/Mftf/Section/AdminSearchTermFormSection.xml b/app/code/Magento/Search/Test/Mftf/Section/AdminSearchTermFormSection.xml new file mode 100644 index 0000000000000..8135ed517c376 --- /dev/null +++ b/app/code/Magento/Search/Test/Mftf/Section/AdminSearchTermFormSection.xml @@ -0,0 +1,18 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + /** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +--> + +<sections xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Page/etc/SectionObject.xsd"> + <section name="AdminSearchTermFormSection"> + <element name="save" type="button" selector=".page-actions-buttons .save"/> + <element name="searchQuery" type="input" selector=".admin__field-control.control #query_text"/> + <element name="storeID" type="input" selector=".admin__field-control.control #store_id"/> + <element name="redirectUrl" type="input" selector=".admin__field-control.control #redirect"/> + <element name="displayInTerms" type="input" selector=".admin__field-control.control #display_in_terms"/> + </section> +</sections> diff --git a/app/code/Magento/Search/Test/Mftf/Section/AdminSearchTermsGridSection.xml b/app/code/Magento/Search/Test/Mftf/Section/AdminSearchTermsGridSection.xml new file mode 100644 index 0000000000000..0eeffdb24f94e --- /dev/null +++ b/app/code/Magento/Search/Test/Mftf/Section/AdminSearchTermsGridSection.xml @@ -0,0 +1,14 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + /** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +--> + +<sections xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Page/etc/SectionObject.xsd"> + <section name="AdminSearchTermsGridSection"> + <element name="addNewEntity" type="button" selector=".page-actions-buttons .add"/> + </section> +</sections> diff --git a/app/code/Magento/Search/Test/Mftf/Test/AdminGlobalSearchOnProductPageTest.xml b/app/code/Magento/Search/Test/Mftf/Test/AdminGlobalSearchOnProductPageTest.xml index 725f45c0bc6e3..82ec95b24d3ca 100644 --- a/app/code/Magento/Search/Test/Mftf/Test/AdminGlobalSearchOnProductPageTest.xml +++ b/app/code/Magento/Search/Test/Mftf/Test/AdminGlobalSearchOnProductPageTest.xml @@ -20,7 +20,7 @@ </annotations> <before> <!-- Login as admin --> - <actionGroup ref="LoginAsAdmin" stepKey="loginAsAdmin"/> + <actionGroup ref="AdminLoginActionGroup" stepKey="loginAsAdmin"/> </before> <after> <!-- Delete product --> @@ -34,7 +34,7 @@ </actionGroup> <!-- Logout --> - <actionGroup ref="logout" stepKey="logout"/> + <actionGroup ref="AdminLogoutActionGroup" stepKey="logout"/> </after> <!-- Create Simple Product --> diff --git a/app/code/Magento/Search/Test/Mftf/Test/AdminMassDeleteSearchTermEntityTest.xml b/app/code/Magento/Search/Test/Mftf/Test/AdminMassDeleteSearchTermEntityTest.xml index 01c361d336541..f5bb414f59197 100644 --- a/app/code/Magento/Search/Test/Mftf/Test/AdminMassDeleteSearchTermEntityTest.xml +++ b/app/code/Magento/Search/Test/Mftf/Test/AdminMassDeleteSearchTermEntityTest.xml @@ -26,11 +26,11 @@ <createData entity="SearchTerm" stepKey="createThirdSearchTerm"/> <!-- Login as admin --> - <actionGroup ref="LoginAsAdmin" stepKey="loginAsAdmin"/> + <actionGroup ref="AdminLoginActionGroup" stepKey="loginAsAdmin"/> </before> <after> <!-- Log out --> - <actionGroup ref="logout" stepKey="logout"/> + <actionGroup ref="AdminLogoutActionGroup" stepKey="logout"/> </after> <!-- Go to the catalog search term page --> @@ -63,8 +63,7 @@ </actionGroup> <!-- Go to storefront page --> - <amOnPage url="{{StorefrontHomePage.url}}" stepKey="goToStorefrontPage"/> - <waitForPageLoad stepKey="waitForStorefrontPageLoad"/> + <actionGroup ref="StorefrontOpenHomePageActionGroup" stepKey="goToStorefrontPage"/> <!-- Verify search term deletion on storefront --> <actionGroup ref="StorefrontCheckQuickSearchActionGroup" stepKey="quickSearchForFirstSearchTerm"> diff --git a/app/code/Magento/Search/Test/Mftf/Test/StorefrontUsingElasticSearchWithWeightAttributeTest.xml b/app/code/Magento/Search/Test/Mftf/Test/StorefrontUsingElasticSearchWithWeightAttributeTest.xml new file mode 100644 index 0000000000000..18f623288621d --- /dev/null +++ b/app/code/Magento/Search/Test/Mftf/Test/StorefrontUsingElasticSearchWithWeightAttributeTest.xml @@ -0,0 +1,61 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + /** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +--> + +<tests xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/testSchema.xsd"> + <test name="StorefrontUsingElasticSearchWithWeightAttributeTest"> + <annotations> + <features value="CatalogSearch"/> + <stories value="Storefront Search"/> + <title value="Using ElasticSearch with weight attribute"/> + <description value="Use ElasticSearch for products with weight attributes"/> + <severity value="CRITICAL"/> + <testCaseId value="MC-31743"/> + <group value="SearchEngineElasticsearch"/> + </annotations> + <before> + <!--Create Simple Product with weight--> + <createData entity="defaultSimpleProduct" stepKey="simpleProduct"/> + <!-- Login as admin --> + <actionGroup ref="AdminLoginActionGroup" stepKey="loginAsAdmin"/> + </before> + <after> + <!-- Delete create product --> + <deleteData createDataKey="simpleProduct" stepKey="deleteProduct"/> + <!-- Change attribute property: Use in Search >No --> + <actionGroup ref="OpenProductAttributeFromSearchResultInGridActionGroup" stepKey="openWeightProductAttributeInAdmin"> + <argument name="productAttributeCode" value="weight"/> + </actionGroup> + <actionGroup ref="AdminSetUseInSearchValueForProductAttributeActionGroup" stepKey="makeAttributeUnsearchableInAQuickSearch"> + <argument name="useInSearchValue" value="No"/> + </actionGroup> + <actionGroup ref="SaveProductAttributeActionGroup" stepKey="saveAttributeChanges"/> + <actionGroup ref="ClearFiltersAdminDataGridActionGroup" stepKey="clearFilter"/> + <!-- Logout from admin --> + <actionGroup ref="AdminLogoutActionGroup" stepKey="logoutFromAdmin"/> + </after> + <!-- Step 2 --> + <actionGroup ref="OpenProductAttributeFromSearchResultInGridActionGroup" stepKey="openWeightProductAttribute"> + <argument name="productAttributeCode" value="weight"/> + </actionGroup> + <!-- Change attribute property: Use in Search >Yes --> + <actionGroup ref="AdminSetUseInSearchValueForProductAttributeActionGroup" stepKey="makeAttributeSearchableInAQuickSearch"/> + <actionGroup ref="SaveProductAttributeActionGroup" stepKey="saveAttribute"/> + <!-- Step 3 --> + <magentoCron groups="index" stepKey="reindexInvalidatedIndices"/> + <!-- Step 4 --> + <magentoCLI command="cache:clean" arguments="full_page" stepKey="clearFPC"/> + <!-- Step 5 --> + <actionGroup ref="StorefrontOpenHomePageActionGroup" stepKey="goToStorefront"/> + <!-- Step 6 --> + <actionGroup ref="StorefrontCheckQuickSearchStringActionGroup" stepKey="quickSearchByAnyValue"> + <argument name="phrase" value="exampleTestValue2020"/> + </actionGroup> + <see selector="{{StorefrontCatalogSearchMainSection.message}}" userInput="Your search returned no results." stepKey="seeCantFindProductMessage"/> + </test> +</tests> diff --git a/app/code/Magento/Search/Test/Mftf/Test/StorefrontVerifySearchSuggestionByProductDescriptionTest.xml b/app/code/Magento/Search/Test/Mftf/Test/StorefrontVerifySearchSuggestionByProductDescriptionTest.xml index 126d4612d7488..3bfa777ac27d8 100644 --- a/app/code/Magento/Search/Test/Mftf/Test/StorefrontVerifySearchSuggestionByProductDescriptionTest.xml +++ b/app/code/Magento/Search/Test/Mftf/Test/StorefrontVerifySearchSuggestionByProductDescriptionTest.xml @@ -19,18 +19,8 @@ </annotations> <before> <!-- Login as admin --> - <comment userInput="Login as admin" stepKey="loginAsAdminComment"/> - <actionGroup ref="LoginAsAdmin" stepKey="loginAsAdmin"/> - <!-- Go to the catalog search term page --> - <comment userInput="Go to the catalog search term page" stepKey="goToSearchTermPageComment"/> - <amOnPage url="{{AdminCatalogSearchTermIndexPage.url}}" stepKey="openAdminCatalogSearchTermIndexPage"/> - <waitForPageLoad stepKey="waitForAdminCatalogSearchTermIndexPageLoad"/> - <!-- Delete all search terms --> - <comment userInput="Delete all search terms" stepKey="deleteAllSearchTermsComment"/> - <actionGroup ref="AdminDeleteAllSearchTermsActionGroup" stepKey="deleteAllSearchTerms"/> - <actionGroup ref="DeleteAllProductsUsingProductGridActionGroup" stepKey="deleteAllProducts"/> + <actionGroup ref="AdminLoginActionGroup" stepKey="loginAsAdmin"/> <!-- Create product with description --> - <comment userInput="Create product with description" stepKey="createProductWithDescriptionComment"/> <createData entity="SimpleProductWithDescription" stepKey="simpleProduct"/> <!-- Perform reindex and flush cache --> @@ -39,51 +29,40 @@ </before> <after> <!-- Delete created product --> - <comment userInput="Delete created product" stepKey="deleteCreatedProductComment"/> <deleteData createDataKey="simpleProduct" stepKey="deleteProduct"/> <!-- Go to the catalog search term page --> - <comment userInput="Go to the catalog search term page" stepKey="goToSearchTermPageComment2"/> <amOnPage url="{{AdminCatalogSearchTermIndexPage.url}}" stepKey="openAdminCatalogSearchTermIndexPage"/> <waitForPageLoad stepKey="waitForAdminCatalogSearchTermIndexPageLoad"/> <!-- Filter the search term --> - <comment userInput="Filter search term" stepKey="filterSearchTermComment"/> <actionGroup ref="AdminSearchTermFilterBySearchQueryActionGroup" stepKey="filterByThirdSearchQuery"> <argument name="searchQuery" value="{{ApiProductDescription.value}}"/> </actionGroup> <!-- Delete created below search terms --> - <comment userInput="Delete created below search terms" stepKey="deleteCreatedBelowSearchTermsComment"/> <actionGroup ref="AdminDeleteSearchTermActionGroup" stepKey="deleteSearchTerms"/> - <actionGroup ref="logout" stepKey="logout"/> + <actionGroup ref="AdminLogoutActionGroup" stepKey="logout"/> </after> <!-- Go to storefront home page --> - <comment userInput="Go to storefront home page" stepKey="goToStorefrontHomePageComment"/> <actionGroup ref="StorefrontOpenHomePageActionGroup" stepKey="openStoreFrontHomePage"/> <!-- Storefront quick search by product name --> - <comment userInput="Storefront quick search by product name" stepKey="storefrontQuickSearchByProductNameComment"/> <actionGroup ref="StorefrontCheckQuickSearchStringActionGroup" stepKey="quickSearchByProductName"> <argument name="phrase" value="{{ApiProductDescription.value}}"/> </actionGroup> <!-- Verify search suggestions and select the suggestion from dropdown options --> - <comment userInput="Verify search suggestions and select the suggestion from dropdown options" stepKey="verifySearchSuggestionsComment"/> <actionGroup ref="StoreFrontSelectDropDownSearchSuggestionActionGroup" stepKey="seeDropDownSearchSuggestion"> <argument name="searchQuery" value="{{ApiProductDescription.value}}"/> </actionGroup> <!-- Assert Product storefront main page --> - <comment userInput="See product name" stepKey="seeProductNameComment"/> <actionGroup ref="StorefrontAssertProductNameOnProductMainPageActionGroup" stepKey="seeProductName"> <argument name="productName" value="$$simpleProduct.name$$"/> </actionGroup> <!-- Go to the catalog search term page --> - <comment userInput="Go to the catalog search term page" stepKey="goToSearchTermPageComment3"/> <amOnPage url="{{AdminCatalogSearchTermIndexPage.url}}" stepKey="openAdminCatalogSearchTermIndexPage"/> <waitForPageLoad stepKey="waitForAdminCatalogSearchTermIndexPageLoad"/> <!-- Filter the search term --> - <comment userInput="Filter search term" stepKey="filterSearchTermComment2"/> <actionGroup ref="AdminSearchTermFilterBySearchQueryActionGroup" stepKey="filterByThirdSearchQuery"> <argument name="searchQuery" value="{{ApiProductDescription.value}}"/> </actionGroup> <!-- Assert Search Term in grid --> - <comment userInput="Check is search term in grid or not" stepKey="isSearchTermInGridComment"/> <see stepKey="seeSearchTermInGrid" selector="{{AdminCatalogSearchTermIndexSection.gridRow}}" userInput="{{ApiProductDescription.value}}" /> <see selector="{{AdminCatalogSearchTermIndexSection.numberOfSearchTermResults}}" userInput="1" stepKey="seeNumberOfSearchTermResultInGrid"/> </test> diff --git a/app/code/Magento/Search/Test/Mftf/Test/StorefrontVerifySearchSuggestionByProductNameTest.xml b/app/code/Magento/Search/Test/Mftf/Test/StorefrontVerifySearchSuggestionByProductNameTest.xml index fc933c90341f9..93a3c8ca8e4a2 100644 --- a/app/code/Magento/Search/Test/Mftf/Test/StorefrontVerifySearchSuggestionByProductNameTest.xml +++ b/app/code/Magento/Search/Test/Mftf/Test/StorefrontVerifySearchSuggestionByProductNameTest.xml @@ -20,7 +20,7 @@ <before> <!-- Login as admin --> - <actionGroup ref="LoginAsAdmin" stepKey="loginAsAdmin"/> + <actionGroup ref="AdminLoginActionGroup" stepKey="loginAsAdmin"/> <!--Create Simple Product --> <createData entity="defaultSimpleProduct" stepKey="simpleProduct"/> @@ -43,7 +43,7 @@ </actionGroup> <!-- Delete created below search terms --> <actionGroup ref="AdminDeleteSearchTermActionGroup" stepKey="deleteSearchTerms"/> - <actionGroup ref="logout" stepKey="logout"/> + <actionGroup ref="AdminLogoutActionGroup" stepKey="logout"/> </after> <!-- Go to storefront home page --> diff --git a/app/code/Magento/Search/Test/Mftf/Test/StorefrontVerifySearchSuggestionByProductShortDescriptionTest.xml b/app/code/Magento/Search/Test/Mftf/Test/StorefrontVerifySearchSuggestionByProductShortDescriptionTest.xml index 65472b9e10282..ebe3b6c129721 100644 --- a/app/code/Magento/Search/Test/Mftf/Test/StorefrontVerifySearchSuggestionByProductShortDescriptionTest.xml +++ b/app/code/Magento/Search/Test/Mftf/Test/StorefrontVerifySearchSuggestionByProductShortDescriptionTest.xml @@ -20,7 +20,7 @@ <before> <!-- Login as admin --> - <actionGroup ref="LoginAsAdmin" stepKey="loginAsAdmin"/> + <actionGroup ref="AdminLoginActionGroup" stepKey="loginAsAdmin"/> <!-- Create product with short description --> <createData entity="ApiProductWithDescription" stepKey="product"/> @@ -45,7 +45,7 @@ <!-- Delete created below search terms --> <actionGroup ref="AdminDeleteSearchTermActionGroup" stepKey="deleteSearchTerms"/> - <actionGroup ref="logout" stepKey="logout"/> + <actionGroup ref="AdminLogoutActionGroup" stepKey="logout"/> </after> <!-- Go to storefront home page --> diff --git a/app/code/Magento/Search/Test/Mftf/Test/StorefrontVerifySearchSuggestionByProductSkuTest.xml b/app/code/Magento/Search/Test/Mftf/Test/StorefrontVerifySearchSuggestionByProductSkuTest.xml index 6e80823b78e0f..e72f614593cfe 100644 --- a/app/code/Magento/Search/Test/Mftf/Test/StorefrontVerifySearchSuggestionByProductSkuTest.xml +++ b/app/code/Magento/Search/Test/Mftf/Test/StorefrontVerifySearchSuggestionByProductSkuTest.xml @@ -20,7 +20,7 @@ <before> <!-- Login as admin --> - <actionGroup ref="LoginAsAdmin" stepKey="loginAsAdmin"/> + <actionGroup ref="AdminLoginActionGroup" stepKey="loginAsAdmin"/> <!--Create Simple Product --> <createData entity="defaultSimpleProduct" stepKey="simpleProduct"/> @@ -45,7 +45,7 @@ <!-- Delete created below search terms --> <actionGroup ref="AdminDeleteSearchTermActionGroup" stepKey="deleteSearchTerms"/> - <actionGroup ref="logout" stepKey="logout"/> + <actionGroup ref="AdminLogoutActionGroup" stepKey="logout"/> </after> <!-- Go to storefront home page --> diff --git a/app/code/Magento/Search/Test/Mftf/Test/StorefrontVerifySearchTermEntityRedirectTest.xml b/app/code/Magento/Search/Test/Mftf/Test/StorefrontVerifySearchTermEntityRedirectTest.xml new file mode 100644 index 0000000000000..6ff4b7de5559d --- /dev/null +++ b/app/code/Magento/Search/Test/Mftf/Test/StorefrontVerifySearchTermEntityRedirectTest.xml @@ -0,0 +1,51 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + /** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +--> + +<tests xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/testSchema.xsd"> + <test name="StorefrontVerifySearchTermEntityRedirectTest"> + <annotations> + <stories value="Search Term Redirect"/> + <title value="Create Search Term Entity With Redirect. Check How Redirect is Working on Storefront"/> + <description value="Storefront search by created search term with redirect. Verifying if created redirect is working"/> + </annotations> + + <before> + <!-- Login As Admin User --> + <actionGroup ref="AdminLoginActionGroup" stepKey="loginAsAdmin"/> + <!-- Navigate To Marketing Search Terms Grid --> + <actionGroup ref="AdminNavigateMenuActionGroup" stepKey="navigateToSearchTermPage"> + <argument name="menuUiId" value="{{AdminMenuMarketing.dataUiId}}"/> + <argument name="submenuUiId" value="{{AdminMenuMarketingSEOAndSearchSearchTerms.dataUiId}}"/> + </actionGroup> + <!-- Create Custom Search Term With Redirect --> + <actionGroup ref="AdminCreateNewSearchTermEntityActionGroup" stepKey="createSearchTerm"> + <argument name="query" value="{{SearchTerm.query_text}}"/> + <argument name="store" value="{{SearchTerm.store_id}}"/> + <argument name="redirectUrl" value="{{SearchTerm.redirect}}"/> + </actionGroup> + </before> + <after> + <amOnPage url="{{AdminCatalogSearchTermIndexPage.url}}" stepKey="navigateToSearchTermPage"/> + <actionGroup ref="AdminSearchTermFilterBySearchQueryActionGroup" stepKey="findCreatedTerm"> + <argument name="searchQuery" value="{{SearchTerm.query_text}}"/> + </actionGroup> + <actionGroup ref="AdminDeleteSearchTermActionGroup" stepKey="deleteCreatedSearchTerm"/> + </after> + + <!-- TEST BODY --> + <!-- Navigate To StoreFront --> + <actionGroup ref="StorefrontOpenHomePageActionGroup" stepKey="openStoreFrontHomePage"/> + <!-- Fill in Search Field, Submit Search Request --> + <actionGroup ref="StoreFrontQuickSearchActionGroup" stepKey="searchByCreatedTerm"> + <argument name="query" value="{{SearchTerm.query_text}}"/> + </actionGroup> + <!-- Assert Current Url --> + <seeCurrentUrlEquals stepKey="checkUrl" url="{{SearchTerm.redirect}}"/> + </test> +</tests> diff --git a/app/code/Magento/Search/Test/Unit/Block/TermsTest.php b/app/code/Magento/Search/Test/Unit/Block/TermsTest.php new file mode 100644 index 0000000000000..d2e7e94a65f8e --- /dev/null +++ b/app/code/Magento/Search/Test/Unit/Block/TermsTest.php @@ -0,0 +1,173 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +namespace Magento\Search\Test\Unit\Block; + +use Magento\Framework\TestFramework\Unit\Helper\ObjectManager; +use Magento\Framework\Url; +use Magento\Framework\UrlFactory; +use Magento\Framework\View\Element\Template\Context; +use Magento\Search\Block\Term; +use Magento\Search\Model\Query; +use Magento\Search\Model\ResourceModel\Query\Collection; +use Magento\Search\Model\ResourceModel\Query\CollectionFactory; +use Magento\Store\Model\Store; +use Magento\Store\Model\StoreManager; +use PHPUnit\Framework\MockObject\MockObject; +use PHPUnit\Framework\TestCase; + +/** + * Tests for Terms block + */ +class TermsTest extends TestCase +{ + /** + * @var Context|MockObject + */ + private $contextMock; + + /** + * @var CollectionFactory|MockObject + */ + private $collectionFactoryMock; + + /** + * @var UrlFactory|MockObject + */ + private $urlFactoryMock; + + /** + * @var Term + */ + private $termsModel; + + /** + * @var StoreManager + */ + private $storeManagerMock; + + /** + * @inheritdoc + */ + public function setUp() + { + $objectManager = new ObjectManager($this); + + $this->contextMock = $this->createMock(Context::class); + $this->collectionFactoryMock = $this->createMock(CollectionFactory::class); + $this->urlFactoryMock = $this->createMock(UrlFactory::class); + $this->storeManagerMock = $this->createMock(StoreManager::class); + + $this->contextMock->expects($this->once()) + ->method('getStoreManager') + ->willReturn($this->storeManagerMock); + $this->termsModel = $objectManager->getObject( + Term::class, + [ + 'context' => $this->contextMock, + '_queryCollectionFactory' => $this->collectionFactoryMock, + '_urlFactory' => $this->urlFactoryMock + ] + ); + } + + /** + * Verify terms + * + * @dataProvider termKeysProvider + * @param string $termKey + * @param bool $popularity + */ + public function testGetTerms(string $termKey, bool $popularity): void + { + $terms = $this->createMock(Collection::class); + $dataObjectMock = $this->getMockBuilder(Query::class) + ->disableOriginalConstructor() + ->setMethods(['getPopularity', 'getQueryText']) + ->getMock(); + $storeMock = $this->createMock(Store::class); + + $this->storeManagerMock->expects($this->once()) + ->method('getStore') + ->willReturn($storeMock); + $storeMock->expects($this->once()) + ->method('getId') + ->willReturn(1); + + $this->collectionFactoryMock->expects($this->once()) + ->method('create') + ->willReturn($terms); + $terms->expects($this->once()) + ->method('setPopularQueryFilter') + ->willReturnSelf(); + $terms->expects($this->once()) + ->method('setPageSize') + ->willReturnSelf(); + $terms->expects($this->once()) + ->method('load') + ->willReturnSelf(); + $terms->expects($this->once()) + ->method('getItems') + ->willReturn([$dataObjectMock]); + $dataObjectMock->expects($this->exactly(!$popularity ? 3 : 4)) + ->method('getPopularity') + ->willReturn($popularity); + $dataObjectMock->expects($this->exactly(!$popularity ? 0 : 2)) + ->method('getQueryText') + ->willReturn($termKey); + + $this->assertEquals(!$popularity ? [] : [$termKey => $dataObjectMock], $this->termsModel->getTerms()); + } + + /** + * Verify get search Url + * + * @return void + */ + public function testGetSearchResult(): void + { + $urlMock = $this->getMockBuilder(Url::class) + ->disableOriginalConstructor() + ->setMethods(['setQueryParam', 'getUrl']) + ->getMock(); + + $dataObjectMock = $this->getMockBuilder(Query::class) + ->disableOriginalConstructor() + ->setMethods(['getPopularity', 'getQueryText']) + ->getMock(); + $this->urlFactoryMock->expects($this->once()) + ->method('create') + ->willReturn($urlMock); + $dataObjectMock->expects($this->once()) + ->method('getQueryText') + ->willReturn('url'); + $urlMock->expects($this->once())->method('setQueryParam'); + $urlMock->expects($this->once()) + ->method('getUrl') + ->with('catalogsearch/result') + ->willReturn('url'); + + $this->assertEquals('url', $this->termsModel->getSearchUrl($dataObjectMock)); + } + + /** + * Terms data key provider + * + * @return array + */ + public function termKeysProvider(): array + { + return [ + [ + 'search', + true + ], + [ + '', + false + ] + ]; + } +} diff --git a/app/code/Magento/Search/Test/Unit/Controller/Term/PopularTest.php b/app/code/Magento/Search/Test/Unit/Controller/Term/PopularTest.php new file mode 100644 index 0000000000000..590d959b95ff3 --- /dev/null +++ b/app/code/Magento/Search/Test/Unit/Controller/Term/PopularTest.php @@ -0,0 +1,103 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +declare(strict_types=1); + +namespace Magento\Search\Test\Unit\Controller\Term; + +use Magento\Framework\App\Config\ScopeConfigInterface; +use Magento\Framework\Controller\Result\Forward as ResultForward; +use Magento\Framework\Controller\Result\ForwardFactory as ResultForwardFactory; +use Magento\Framework\View\Result\Page as ResultPage; +use Magento\Framework\View\Result\PageFactory as ResultPageFactory; +use Magento\Framework\TestFramework\Unit\Helper\ObjectManager; +use Magento\Search\Controller\Term\Popular; +use Magento\Store\Model\ScopeInterface; +use PHPUnit\Framework\MockObject\MockObject; +use PHPUnit\Framework\TestCase; + +class PopularTest extends TestCase +{ + private const XML_PATH_SEO_SEARCH_TERMS = 'catalog/seo/search_terms'; + + /** + * @var Popular + */ + private $action; + + /** + * @var ResultForwardFactory|MockObject + */ + private $resultForwardFactoryMock; + + /** + * @var ResultPageFactory|MockObject + */ + private $resultPageFactoryMock; + + /** + * @var ScopeConfigInterface|MockObject + */ + private $scopeConfigMock; + + protected function setUp() + { + $this->resultForwardFactoryMock = $this->getMockBuilder(ResultForwardFactory::class) + ->disableOriginalConstructor() + ->getMock(); + + $this->resultPageFactoryMock = $this->getMockBuilder(ResultPageFactory::class) + ->disableOriginalConstructor() + ->getMock(); + + $this->scopeConfigMock = $this->getMockBuilder(ScopeConfigInterface::class) + ->getMockForAbstractClass(); + + $objectManager = new ObjectManager($this); + $this->action = $objectManager->getObject( + Popular::class, + [ + 'resultForwardFactory' => $this->resultForwardFactoryMock, + 'resultPageFactory' => $this->resultPageFactoryMock, + 'scopeConfig' => $this->scopeConfigMock + ] + ); + } + + public function testResult() + { + $this->scopeConfigMock->expects($this->once()) + ->method('isSetFlag') + ->with(static::XML_PATH_SEO_SEARCH_TERMS, ScopeInterface::SCOPE_STORE) + ->willReturn(true); + $resultPageMock = $this->getMockBuilder(ResultPage::class) + ->disableOriginalConstructor() + ->getMock(); + $this->resultPageFactoryMock->expects($this->once()) + ->method('create') + ->willReturn($resultPageMock); + + $this->assertSame($resultPageMock, $this->action->execute()); + } + + public function testResultWithDisabledPage() + { + $this->scopeConfigMock->expects($this->once()) + ->method('isSetFlag') + ->with(static::XML_PATH_SEO_SEARCH_TERMS, ScopeInterface::SCOPE_STORE) + ->willReturn(false); + $resultForwardMock = $this->getMockBuilder(ResultForward::class) + ->disableOriginalConstructor() + ->getMock(); + $this->resultForwardFactoryMock->expects($this->once()) + ->method('create') + ->willReturn($resultForwardMock); + $resultForwardMock->expects($this->once()) + ->method('forward') + ->with('noroute'); + + $this->assertSame($resultForwardMock, $this->action->execute()); + } +} diff --git a/app/code/Magento/Search/Test/Unit/Model/PopularSearchTermsTest.php b/app/code/Magento/Search/Test/Unit/Model/PopularSearchTermsTest.php index 849a0c067d459..70f25e2f12e16 100644 --- a/app/code/Magento/Search/Test/Unit/Model/PopularSearchTermsTest.php +++ b/app/code/Magento/Search/Test/Unit/Model/PopularSearchTermsTest.php @@ -49,47 +49,27 @@ protected function setUp() /** * Test isCacheableDataProvider method * - * @dataProvider isCacheableDataProvider - * - * @param string $term - * @param array $terms - * @param $expected $terms - * * @return void */ - public function testIsCacheable($term, $terms, $expected) + public function testIsCacheable() { - $storeId = 7; - $pageSize = 25; + $term = 'test1'; + $storeId = 1; + $pageSize = 35; - $this->scopeConfigMock->expects($this->once())->method('getValue') + $this->scopeConfigMock->expects($this->exactly(2)) + ->method('getValue') ->with( PopularSearchTerms::XML_PATH_MAX_COUNT_CACHEABLE_SEARCH_TERMS, ScopeInterface::SCOPE_STORE, $storeId )->willReturn($pageSize); - $this->queryCollectionMock->expects($this->once())->method('setPopularQueryFilter')->with($storeId) - ->willReturnSelf(); - $this->queryCollectionMock->expects($this->once())->method('setPageSize')->with($pageSize) - ->willReturnSelf(); - $this->queryCollectionMock->expects($this->once())->method('load')->willReturnSelf(); - $this->queryCollectionMock->expects($this->once())->method('getColumnValues')->with('query_text') - ->willReturn($terms); - - $actual = $this->popularSearchTerms->isCacheable($term, $storeId); - self::assertEquals($expected, $actual); - } + $this->queryCollectionMock->expects($this->exactly(2)) + ->method('isTopSearchResult') + ->with($term, $storeId, $pageSize) + ->willReturn(true, false); - /** - * @return array - */ - public function isCacheableDataProvider() - { - return [ - ['test01', [], false], - ['test02', ['test01', 'test02'], true], - ['test03', ['test01', 'test02'], false], - ['test04', ['test04'], true], - ]; + $this->assertTrue($this->popularSearchTerms->isCacheable($term, $storeId)); + $this->assertFalse($this->popularSearchTerms->isCacheable($term, $storeId)); } } diff --git a/app/code/Magento/Search/etc/adminhtml/system.xml b/app/code/Magento/Search/etc/adminhtml/system.xml index d3b7f64fbe0c4..8a539c9528e8e 100644 --- a/app/code/Magento/Search/etc/adminhtml/system.xml +++ b/app/code/Magento/Search/etc/adminhtml/system.xml @@ -9,7 +9,7 @@ <system> <section id="catalog"> <group id="search"> - <field id="engine" translate="label" type="select" sortOrder="19" showInDefault="1" showInWebsite="0" showInStore="0"> + <field id="engine" translate="label" type="select" sortOrder="19" showInDefault="1"> <label>Search Engine</label> <source_model>Magento\Search\Model\Adminhtml\System\Config\Source\Engine</source_model> </field> diff --git a/app/code/Magento/Search/etc/di.xml b/app/code/Magento/Search/etc/di.xml index 4aa211d23f0a2..382174f4327a2 100644 --- a/app/code/Magento/Search/etc/di.xml +++ b/app/code/Magento/Search/etc/di.xml @@ -45,14 +45,6 @@ <preference for="Magento\Search\Model\AutocompleteInterface" type="Magento\Search\Model\Autocomplete" /> <preference for="Magento\Search\Model\Autocomplete\ItemInterface" type="Magento\Search\Model\Autocomplete\Item" /> <preference for="Magento\Framework\Search\SearchEngineInterface" type="Magento\Search\Model\SearchEngine"/> - <type name="Magento\Framework\View\Element\UiComponent\DataProvider\FilterPool"> - <arguments> - <argument name="appliers" xsi:type="array"> - <item name="regular" xsi:type="object">Magento\Framework\View\Element\UiComponent\DataProvider\RegularFilter</item> - <item name="fulltext" xsi:type="object">Magento\Framework\View\Element\UiComponent\DataProvider\FulltextFilter</item> - </argument> - </arguments> - </type> <!-- @api --> <virtualType name="Magento\Search\Model\ResourceModel\Synonyms\Grid\Collection" type="Magento\Framework\View\Element\UiComponent\DataProvider\SearchResult"> <arguments> diff --git a/app/code/Magento/Search/registration.php b/app/code/Magento/Search/registration.php index 177f95971c8af..48210f971c4c0 100644 --- a/app/code/Magento/Search/registration.php +++ b/app/code/Magento/Search/registration.php @@ -4,6 +4,6 @@ * See COPYING.txt for license details. */ -use \Magento\Framework\Component\ComponentRegistrar; +use Magento\Framework\Component\ComponentRegistrar; ComponentRegistrar::register(ComponentRegistrar::MODULE, 'Magento_Search', __DIR__); diff --git a/app/code/Magento/Search/view/frontend/templates/form.mini.phtml b/app/code/Magento/Search/view/frontend/templates/form.mini.phtml index 0dd9c819c855a..35f3876599731 100644 --- a/app/code/Magento/Search/view/frontend/templates/form.mini.phtml +++ b/app/code/Magento/Search/view/frontend/templates/form.mini.phtml @@ -14,7 +14,8 @@ $helper = $this->helper(\Magento\Search\Helper\Data::class); <div class="block block-search"> <div class="block block-title"><strong><?= $block->escapeHtml(__('Search')) ?></strong></div> <div class="block block-content"> - <form class="form minisearch" id="search_mini_form" action="<?= $block->escapeUrl($helper->getResultUrl()) ?>" method="get"> + <form class="form minisearch" id="search_mini_form" + action="<?= $block->escapeUrl($helper->getResultUrl()) ?>" method="get"> <div class="field search"> <label class="label" for="search" data-role="minisearch-label"> <span><?= $block->escapeHtml(__('Search')) ?></span> @@ -29,7 +30,7 @@ $helper = $this->helper(\Magento\Search\Helper\Data::class); }' type="text" name="<?= $block->escapeHtmlAttr($helper->getQueryParamName()) ?>" - value="<?= $block->escapeHtmlAttr($helper->getEscapedQueryText()) ?>" + value="<?= /* @noEscape */ $helper->getEscapedQueryText() ?>" placeholder="<?= $block->escapeHtmlAttr(__('Search entire store here...')) ?>" class="input-text" maxlength="<?= $block->escapeHtmlAttr($helper->getMaxQueryLength()) ?>" diff --git a/app/code/Magento/Security/Api/Data/UserExpirationInterface.php b/app/code/Magento/Security/Api/Data/UserExpirationInterface.php new file mode 100644 index 0000000000000..4f602c8b58a87 --- /dev/null +++ b/app/code/Magento/Security/Api/Data/UserExpirationInterface.php @@ -0,0 +1,67 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ + +declare(strict_types=1); + +namespace Magento\Security\Api\Data; + +use \Magento\Security\Api\Data\UserExpirationExtensionInterface; + +/** + * Interface UserExpirationInterface to be used as a DTO for expires_at property on User model. + */ +interface UserExpirationInterface extends \Magento\Framework\Api\ExtensibleDataInterface +{ + + public const EXPIRES_AT = 'expires_at'; + + public const USER_ID = 'user_id'; + + /** + * `expires_at` getter. + * + * @return string + */ + public function getExpiresAt(); + + /** + * `expires_at` setter. + * + * @param string $expiresAt + * @return $this + */ + public function setExpiresAt($expiresAt); + + /** + * `user_id` getter. + * + * @return string + */ + public function getUserId(); + + /** + * `user_id` setter. + * + * @param string $userId + * @return $this + */ + public function setUserId($userId); + + /** + * Retrieve existing extension attributes object or create a new one. + * + * @return \Magento\Security\Api\Data\UserExpirationExtensionInterface|null + */ + public function getExtensionAttributes(); + + /** + * Set an extension attributes object. + * + * @param \Magento\Security\Api\Data\UserExpirationExtensionInterface $extensionAttributes + * @return $this + */ + public function setExtensionAttributes(UserExpirationExtensionInterface $extensionAttributes); +} diff --git a/app/code/Magento/Security/Model/Plugin/AdminUserForm.php b/app/code/Magento/Security/Model/Plugin/AdminUserForm.php new file mode 100644 index 0000000000000..0c024d15ef4ae --- /dev/null +++ b/app/code/Magento/Security/Model/Plugin/AdminUserForm.php @@ -0,0 +1,112 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +declare(strict_types=1); + +namespace Magento\Security\Model\Plugin; + +use Magento\Framework\Stdlib\DateTime\TimezoneInterface; +use Magento\Security\Model\ResourceModel\UserExpiration; +use Magento\Security\Model\UserExpirationFactory; + +/** + * Add the `expires_at` form field to the User main form. + */ +class AdminUserForm +{ + + /** + * @var TimezoneInterface + */ + private $localeDate; + + /** + * @var UserExpiration + */ + private $userExpirationResource; + + /** + * @var UserExpirationFactory + */ + private $userExpirationFactory; + + /** + * UserForm constructor. + * + * @param TimezoneInterface $localeDate + * @param UserExpirationFactory $userExpirationFactory + * @param UserExpiration $userExpirationResource + */ + public function __construct( + TimezoneInterface $localeDate, + UserExpirationFactory $userExpirationFactory, + UserExpiration $userExpirationResource + ) { + $this->localeDate = $localeDate; + $this->userExpirationResource = $userExpirationResource; + $this->userExpirationFactory = $userExpirationFactory; + } + + /** + * Add the `expires_at` field to the admin user edit form. + * + * @param \Magento\User\Block\User\Edit\Tab\Main $subject + * @param \Closure $proceed + * @return mixed + */ + public function aroundGetFormHtml( + \Magento\User\Block\User\Edit\Tab\Main $subject, + \Closure $proceed + ) { + /** @var \Magento\Framework\Data\Form $form */ + $form = $subject->getForm(); + if (is_object($form)) { + $dateFormat = $this->localeDate->getDateFormat( + \IntlDateFormatter::MEDIUM + ); + $timeFormat = $this->localeDate->getTimeFormat( + \IntlDateFormatter::MEDIUM + ); + $fieldset = $form->getElement('base_fieldset'); + $userIdField = $fieldset->getElements()->searchById('user_id'); + $userExpirationValue = null; + if ($userIdField) { + $userId = $userIdField->getValue(); + $userExpirationValue = $this->loadUserExpirationByUserId($userId); + } + $fieldset->addField( + 'expires_at', + 'date', + [ + 'name' => 'expires_at', + 'label' => __('Expiration Date'), + 'title' => __('Expiration Date'), + 'date_format' => $dateFormat, + 'time_format' => $timeFormat, + 'class' => 'validate-date', + 'value' => $userExpirationValue, + ] + ); + + $subject->setForm($form); + } + + return $proceed(); + } + + /** + * Loads a user expiration record by user ID. + * + * @param string $userId + * @return string + */ + private function loadUserExpirationByUserId($userId) + { + /** @var \Magento\Security\Model\UserExpiration $userExpiration */ + $userExpiration = $this->userExpirationFactory->create(); + $this->userExpirationResource->load($userExpiration, $userId); + return $userExpiration->getExpiresAt(); + } +} diff --git a/app/code/Magento/Security/Model/Plugin/AuthSession.php b/app/code/Magento/Security/Model/Plugin/AuthSession.php index 01203caaa31cd..6dc5e796d8950 100644 --- a/app/code/Magento/Security/Model/Plugin/AuthSession.php +++ b/app/code/Magento/Security/Model/Plugin/AuthSession.php @@ -7,6 +7,7 @@ use Magento\Backend\Model\Auth\Session; use Magento\Security\Model\AdminSessionsManager; +use Magento\Security\Model\UserExpirationManager; /** * Magento\Backend\Model\Auth\Session decorator @@ -33,22 +34,32 @@ class AuthSession */ protected $securityCookie; + /** + * @var UserExpirationManager + */ + private $userExpirationManager; + /** * @param \Magento\Framework\App\RequestInterface $request * @param \Magento\Framework\Message\ManagerInterface $messageManager * @param AdminSessionsManager $sessionsManager * @param \Magento\Security\Model\SecurityCookie $securityCookie + * @param UserExpirationManager|null $userExpirationManager */ public function __construct( \Magento\Framework\App\RequestInterface $request, \Magento\Framework\Message\ManagerInterface $messageManager, AdminSessionsManager $sessionsManager, - \Magento\Security\Model\SecurityCookie $securityCookie + \Magento\Security\Model\SecurityCookie $securityCookie, + \Magento\Security\Model\UserExpirationManager $userExpirationManager = null ) { $this->request = $request; $this->messageManager = $messageManager; $this->sessionsManager = $sessionsManager; $this->securityCookie = $securityCookie; + $this->userExpirationManager = $userExpirationManager ?: + \Magento\Framework\App\ObjectManager::getInstance() + ->get(\Magento\Security\Model\UserExpirationManager::class); } /** @@ -64,6 +75,11 @@ public function aroundProlong(Session $session, \Closure $proceed) $session->destroy(); $this->addUserLogoutNotification(); return null; + } elseif ($this->userExpirationManager->isUserExpired($session->getUser()->getId())) { + $this->userExpirationManager->deactivateExpiredUsersById([$session->getUser()->getId()]); + $session->destroy(); + $this->addUserLogoutNotification(); + return null; } $result = $proceed(); $this->sessionsManager->processProlong(); diff --git a/app/code/Magento/Security/Model/Plugin/UserValidationRules.php b/app/code/Magento/Security/Model/Plugin/UserValidationRules.php new file mode 100644 index 0000000000000..7fddbb21200f4 --- /dev/null +++ b/app/code/Magento/Security/Model/Plugin/UserValidationRules.php @@ -0,0 +1,42 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +declare(strict_types=1); + +namespace Magento\Security\Model\Plugin; + +use Magento\Security\Model\UserExpiration\Validator; + +/** + * \Magento\User\Model\UserValidationRules decorator + */ +class UserValidationRules +{ + /**@var Validator */ + private $validator; + + /** + * UserValidationRules constructor. + * + * @param Validator $validator + */ + public function __construct(Validator $validator) + { + $this->validator = $validator; + } + + /** + * Add the Expires At validator to user validation rules. + * + * @param \Magento\User\Model\UserValidationRules $userValidationRules + * @param \Magento\Framework\Validator\DataObject $result + * @return \Magento\Framework\Validator\DataObject + * @SuppressWarnings(PHPMD.UnusedFormalParameter) + */ + public function afterAddUserInfoRules(\Magento\User\Model\UserValidationRules $userValidationRules, $result) + { + return $result->addRule($this->validator, 'expires_at'); + } +} diff --git a/app/code/Magento/Security/Model/ResourceModel/UserExpiration.php b/app/code/Magento/Security/Model/ResourceModel/UserExpiration.php new file mode 100644 index 0000000000000..71a331a178006 --- /dev/null +++ b/app/code/Magento/Security/Model/ResourceModel/UserExpiration.php @@ -0,0 +1,88 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +declare(strict_types=1); + +namespace Magento\Security\Model\ResourceModel; + +/** + * Admin User Expiration resource model + */ +class UserExpiration extends \Magento\Framework\Model\ResourceModel\Db\AbstractDb +{ + + /** + * Flag that notifies whether Primary key of table is auto-incremented + * + * @var bool + */ + protected $_isPkAutoIncrement = false; + + /** + * @var \Magento\Framework\Stdlib\DateTime\TimezoneInterface + */ + private $timezone; + + /** + * UserExpiration constructor. + * + * @param \Magento\Framework\Model\ResourceModel\Db\Context $context + * @param \Magento\Framework\Stdlib\DateTime\TimezoneInterface $timezone + * @param string $connectionName + */ + public function __construct( + \Magento\Framework\Model\ResourceModel\Db\Context $context, + \Magento\Framework\Stdlib\DateTime\TimezoneInterface $timezone, + ?string $connectionName = null + ) { + parent::__construct($context, $connectionName); + $this->timezone = $timezone; + } + + /** + * Define main table + * + * @return void + */ + protected function _construct() + { + $this->_init('admin_user_expiration', 'user_id'); + } + + /** + * Convert to UTC time. + * + * @param \Magento\Framework\Model\AbstractModel $userExpiration + * @return $this + * @throws \Magento\Framework\Exception\LocalizedException + */ + protected function _beforeSave(\Magento\Framework\Model\AbstractModel $userExpiration) + { + /** @var $userExpiration \Magento\Security\Model\UserExpiration */ + $expiresAt = $userExpiration->getExpiresAt(); + $utcValue = $this->timezone->convertConfigTimeToUtc($expiresAt); + $userExpiration->setExpiresAt($utcValue); + + return $this; + } + + /** + * Convert to store time. + * + * @param \Magento\Framework\Model\AbstractModel $userExpiration + * @return $this|\Magento\Framework\Model\ResourceModel\Db\AbstractDb + * @throws \Exception + */ + protected function _afterLoad(\Magento\Framework\Model\AbstractModel $userExpiration) + { + /** @var $userExpiration \Magento\Security\Model\UserExpiration */ + if ($userExpiration->getExpiresAt()) { + $storeValue = $this->timezone->date($userExpiration->getExpiresAt()); + $userExpiration->setExpiresAt($storeValue->format('Y-m-d H:i:s')); + } + + return $this; + } +} diff --git a/app/code/Magento/Security/Model/ResourceModel/UserExpiration/Collection.php b/app/code/Magento/Security/Model/ResourceModel/UserExpiration/Collection.php new file mode 100644 index 0000000000000..2f2971bc90225 --- /dev/null +++ b/app/code/Magento/Security/Model/ResourceModel/UserExpiration/Collection.php @@ -0,0 +1,75 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +declare(strict_types=1); + +namespace Magento\Security\Model\ResourceModel\UserExpiration; + +/** + * Admin user expiration collection + */ +class Collection extends \Magento\Framework\Model\ResourceModel\Db\Collection\AbstractCollection +{ + /** + * @var string + */ + protected $_idFieldName = 'user_id'; + + /** + * Initialize collection + * + * @return void + */ + protected function _construct() + { + $this->_init( + \Magento\Security\Model\UserExpiration::class, + \Magento\Security\Model\ResourceModel\UserExpiration::class + ); + } + + /** + * Filter for expired, active users. + * + * @return $this + */ + public function addActiveExpiredUsersFilter(): Collection + { + $currentTime = new \DateTime(); + $currentTime->format('Y-m-d H:i:s'); + $this->getSelect()->joinLeft( + ['user' => $this->getTable('admin_user')], + 'main_table.user_id = user.user_id', + ['is_active'] + ); + $this->addFieldToFilter('expires_at', ['lt' => $currentTime]) + ->addFieldToFilter('user.is_active', 1); + + return $this; + } + + /** + * Filter collection by user id. + * + * @param int[] $userIds + * @return Collection + */ + public function addUserIdsFilter(array $userIds = []): Collection + { + return $this->addFieldToFilter('main_table.user_id', ['in' => $userIds]); + } + + /** + * Get any expired records for the given user. + * + * @param string $userId + * @return Collection + */ + public function addExpiredRecordsForUserFilter(string $userId): Collection + { + return $this->addActiveExpiredUsersFilter() + ->addFieldToFilter('main_table.user_id', (int)$userId); + } +} diff --git a/app/code/Magento/Security/Model/UserExpiration.php b/app/code/Magento/Security/Model/UserExpiration.php new file mode 100644 index 0000000000000..e6c711b7ac049 --- /dev/null +++ b/app/code/Magento/Security/Model/UserExpiration.php @@ -0,0 +1,87 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +declare(strict_types=1); + +namespace Magento\Security\Model; + +use Magento\Framework\Model\AbstractExtensibleModel; +use Magento\Security\Api\Data\UserExpirationExtensionInterface; +use Magento\Security\Api\Data\UserExpirationInterface; + +/** + * Admin User Expiration model. + */ +class UserExpiration extends AbstractExtensibleModel implements UserExpirationInterface +{ + + /** + * Resource initialization + * + * @return void + */ + protected function _construct() + { + $this->_init(\Magento\Security\Model\ResourceModel\UserExpiration::class); + } + + /** + * `expires_at` getter. + * + * @return string + */ + public function getExpiresAt() + { + return $this->getData(self::EXPIRES_AT); + } + + /** + * `expires_at` setter. + * + * @param string $expiresAt + * @return $this + */ + public function setExpiresAt($expiresAt) + { + return $this->setData(self::EXPIRES_AT, $expiresAt); + } + + /** + * `user_id` getter. + * + * @return string + */ + public function getUserId() + { + return $this->getData(self::USER_ID); + } + + /** + * `user_id` setter. + * + * @param string $userId + * @return $this + */ + public function setUserId($userId) + { + return $this->setData(self::USER_ID, $userId); + } + + /** + * @inheritDoc + */ + public function getExtensionAttributes() + { + return $this->_getExtensionAttributes(); + } + + /** + * @inheritDoc + */ + public function setExtensionAttributes(UserExpirationExtensionInterface $extensionAttributes) + { + return $this->_setExtensionAttributes($extensionAttributes); + } +} diff --git a/app/code/Magento/Security/Model/UserExpiration/Validator.php b/app/code/Magento/Security/Model/UserExpiration/Validator.php new file mode 100644 index 0000000000000..62dbd7852ff33 --- /dev/null +++ b/app/code/Magento/Security/Model/UserExpiration/Validator.php @@ -0,0 +1,69 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +declare(strict_types=1); + +namespace Magento\Security\Model\UserExpiration; + +use Magento\Framework\Stdlib\DateTime\DateTime; +use Magento\Framework\Stdlib\DateTime\TimezoneInterface; +use Magento\Framework\Validator\AbstractValidator; + +/** + * Validates that the expires_at field is later than the current date/time. + */ +class Validator extends AbstractValidator +{ + + /**@var TimezoneInterface */ + private $timezone; + + /**@var DateTime */ + private $dateTime; + + /** + * Validator constructor. + * + * @param TimezoneInterface $timezone + * @param DateTime $dateTime + */ + public function __construct( + TimezoneInterface $timezone, + DateTime $dateTime + ) { + $this->timezone = $timezone; + $this->dateTime = $dateTime; + } + + /** + * Ensure that the given date is later than the current date. + * + * @param string $value + * @return bool + * @throws \Exception + */ + public function isValid($value) + { + $this->_clearMessages(); + $messages = []; + $expiresAt = $value; + $label = 'Expiration date'; + if (\Zend_Validate::is($expiresAt, 'NotEmpty')) { + if (strtotime($expiresAt)) { + $currentTime = $this->dateTime->gmtTimestamp(); + $utcExpiresAt = $this->timezone->convertConfigTimeToUtc($expiresAt); + $expiresAt = $this->timezone->date($utcExpiresAt)->getTimestamp(); + if ($expiresAt < $currentTime) { + $messages['expires_at'] = __('"%1" must be later than the current date.', $label); + } + } else { + $messages['expires_at'] = __('"%1" is not a valid date.', $label); + } + } + $this->_addMessages($messages); + + return empty($messages); + } +} diff --git a/app/code/Magento/Security/Model/UserExpirationManager.php b/app/code/Magento/Security/Model/UserExpirationManager.php new file mode 100644 index 0000000000000..fe6b87de5a8ec --- /dev/null +++ b/app/code/Magento/Security/Model/UserExpirationManager.php @@ -0,0 +1,153 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +declare(strict_types=1); + +namespace Magento\Security\Model; + +use Magento\Backend\Model\Auth\Session; +use Magento\Framework\Stdlib\DateTime\DateTime; +use Magento\Security\Model\ResourceModel\AdminSessionInfo\CollectionFactory as AdminSessionCollectionFactory; +use Magento\Security\Model\ResourceModel\UserExpiration\Collection as ExpiredUsersCollection; +use Magento\Security\Model\ResourceModel\UserExpiration\CollectionFactory as UserExpirationCollectionFactory; +use Magento\User\Model\ResourceModel\User\CollectionFactory as UserCollectionFactory; + +/** + * Class to handle admin user expirations. Temporary admin users can be created with an expiration + * date that, once past, will not allow them to login to the admin. A cron is run to periodically check for expired + * users and, if found, will deactivate them. + * @SuppressWarnings(PHPMD.CookieAndSessionMisuse) + */ +class UserExpirationManager +{ + + /** + * @var DateTime + */ + private $dateTime; + + /** + * @var ConfigInterface + */ + private $securityConfig; + + /** + * @var ResourceModel\AdminSessionInfo\CollectionFactory + */ + private $adminSessionInfoCollectionFactory; + + /** + * @var Session + */ + private $authSession; + + /** + * @var ResourceModel\UserExpiration\CollectionFactory + */ + private $userExpirationCollectionFactory; + + /** + * @var UserCollectionFactory + */ + private $userCollectionFactory; + + /** + * UserExpirationManager constructor. + * + * @param Session $authSession + * @param ConfigInterface $securityConfig + * @param AdminSessionCollectionFactory $adminSessionInfoCollectionFactory + * @param UserExpirationCollectionFactory $userExpirationCollectionFactory + * @param UserCollectionFactory $userCollectionFactory + * @param DateTime $dateTime + */ + public function __construct( + Session $authSession, + ConfigInterface $securityConfig, + AdminSessionCollectionFactory $adminSessionInfoCollectionFactory, + UserExpirationCollectionFactory $userExpirationCollectionFactory, + UserCollectionFactory $userCollectionFactory, + DateTime $dateTime + ) { + $this->dateTime = $dateTime; + $this->securityConfig = $securityConfig; + $this->adminSessionInfoCollectionFactory = $adminSessionInfoCollectionFactory; + $this->authSession = $authSession; + $this->userExpirationCollectionFactory = $userExpirationCollectionFactory; + $this->userCollectionFactory = $userCollectionFactory; + } + + /** + * Deactivate expired user accounts and invalidate their sessions. + */ + public function deactivateExpiredUsers(): void + { + /** @var ExpiredUsersCollection $expiredRecords */ + $expiredRecords = $this->userExpirationCollectionFactory->create()->addActiveExpiredUsersFilter(); + $this->processExpiredUsers($expiredRecords); + } + + /** + * Deactivate specific expired users. + * + * @param array $userIds + */ + public function deactivateExpiredUsersById(array $userIds): void + { + $expiredRecords = $this->userExpirationCollectionFactory->create() + ->addActiveExpiredUsersFilter() + ->addUserIdsFilter($userIds); + $this->processExpiredUsers($expiredRecords); + } + + /** + * Deactivate expired user accounts and invalidate their sessions. + * + * @param ExpiredUsersCollection $expiredRecords + */ + private function processExpiredUsers(ExpiredUsersCollection $expiredRecords): void + { + if ($expiredRecords->getSize() > 0) { + // get all active sessions for the users and set them to logged out + /** @var \Magento\Security\Model\ResourceModel\AdminSessionInfo\Collection $currentSessions */ + $currentSessions = $this->adminSessionInfoCollectionFactory->create() + ->addFieldToFilter('user_id', ['in' => $expiredRecords->getAllIds()]) + ->filterExpiredSessions($this->securityConfig->getAdminSessionLifetime()); + /** @var \Magento\Security\Model\AdminSessionInfo $currentSession */ + $currentSessions->setDataToAll('status', \Magento\Security\Model\AdminSessionInfo::LOGGED_OUT) + ->save(); + } + + // delete expired records + $expiredRecordIds = $expiredRecords->getAllIds(); + + // set user is_active to 0 + $users = $this->userCollectionFactory->create() + ->addFieldToFilter('main_table.user_id', ['in' => $expiredRecordIds]); + $users->setDataToAll('is_active', 0)->save(); + $expiredRecords->walk('delete'); + } + + /** + * Check if the given user is expired. + * + * @param string $userId + * @return bool + */ + public function isUserExpired(string $userId): bool + { + $isExpired = false; + /** @var \Magento\Security\Api\Data\UserExpirationInterface $expiredRecord */ + $expiredRecord = $this->userExpirationCollectionFactory->create() + ->addExpiredRecordsForUserFilter($userId) + ->getFirstItem(); + if ($expiredRecord && $expiredRecord->getId()) { + $expiresAt = $this->dateTime->timestamp($expiredRecord->getExpiresAt()); + $isExpired = $expiresAt < $this->dateTime->gmtTimestamp(); + } + + return $isExpired; + } +} diff --git a/app/code/Magento/Security/Observer/AdminUserAuthenticateBefore.php b/app/code/Magento/Security/Observer/AdminUserAuthenticateBefore.php new file mode 100644 index 0000000000000..2d0f7bc0f0ac0 --- /dev/null +++ b/app/code/Magento/Security/Observer/AdminUserAuthenticateBefore.php @@ -0,0 +1,69 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +declare(strict_types=1); + +namespace Magento\Security\Observer; + +use Magento\Framework\Event\Observer; +use Magento\Framework\Event\ObserverInterface; +use Magento\Framework\Exception\Plugin\AuthenticationException; +use Magento\Security\Model\UserExpirationManager; +use Magento\User\Model\UserFactory; + +/** + * Check for expired users. + */ +class AdminUserAuthenticateBefore implements ObserverInterface +{ + /** + * @var UserExpirationManager + */ + private $userExpirationManager; + + /** + * @var UserFactory + */ + private $userFactory; + + /** + * AdminUserAuthenticateBefore constructor. + * + * @param UserExpirationManager $userExpirationManager + * @param UserFactory $userFactory + */ + public function __construct( + UserExpirationManager $userExpirationManager, + UserFactory $userFactory + ) { + $this->userExpirationManager = $userExpirationManager; + $this->userFactory = $userFactory; + } + + /** + * Check for expired user when logging in. + * + * @param Observer $observer + * @return void + * @throws AuthenticationException + */ + public function execute(Observer $observer) + { + $username = $observer->getEvent()->getUsername(); + $user = $this->userFactory->create(); + /** @var \Magento\User\Model\User $user */ + $user->loadByUsername($username); + + if ($user->getId() && $this->userExpirationManager->isUserExpired($user->getId())) { + $this->userExpirationManager->deactivateExpiredUsersById([$user->getId()]); + throw new AuthenticationException( + __( + 'The account sign-in was incorrect or your account is disabled temporarily. ' + . 'Please wait and try again later.' + ) + ); + } + } +} diff --git a/app/code/Magento/Security/Observer/AfterAdminUserSave.php b/app/code/Magento/Security/Observer/AfterAdminUserSave.php new file mode 100644 index 0000000000000..d11c1bfdcdf17 --- /dev/null +++ b/app/code/Magento/Security/Observer/AfterAdminUserSave.php @@ -0,0 +1,76 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +declare(strict_types=1); + +namespace Magento\Security\Observer; + +use Magento\Framework\Event\Observer; +use Magento\Framework\Event\ObserverInterface; +use Magento\Security\Model\ResourceModel\UserExpiration as UserExpirationResource; +use Magento\Security\Model\UserExpirationFactory; + +/** + * Save UserExpiration on admin user record. + */ +class AfterAdminUserSave implements ObserverInterface +{ + /** + * @var UserExpirationFactory + */ + private $userExpirationFactory; + + /** + * @var UserExpirationResource + */ + private $userExpirationResource; + + /** + * AfterAdminUserSave constructor. + * + * @param UserExpirationFactory $userExpirationFactory + * @param UserExpirationResource $userExpirationResource + */ + public function __construct( + UserExpirationFactory $userExpirationFactory, + UserExpirationResource $userExpirationResource + ) { + + $this->userExpirationFactory = $userExpirationFactory; + $this->userExpirationResource = $userExpirationResource; + } + + /** + * Save user expiration. + * + * @param Observer $observer + * @return void + * @throws \Magento\Framework\Exception\AlreadyExistsException + */ + public function execute(Observer $observer) + { + /* @var $user \Magento\User\Model\User */ + $user = $observer->getEvent()->getObject(); + if ($user->getId()) { + $expiresAt = $user->getExpiresAt(); + /** @var \Magento\Security\Model\UserExpiration $userExpiration */ + $userExpiration = $this->userExpirationFactory->create(); + $this->userExpirationResource->load($userExpiration, $user->getId()); + + if (empty($expiresAt)) { + // delete it if the admin user clears the field + if ($userExpiration->getId()) { + $this->userExpirationResource->delete($userExpiration); + } + } else { + if (!$userExpiration->getId()) { + $userExpiration->setId($user->getId()); + } + $userExpiration->setExpiresAt($expiresAt); + $this->userExpirationResource->save($userExpiration); + } + } + } +} diff --git a/app/code/Magento/Security/Test/Mftf/ActionGroup/AdminFillInUserWithExpirationActionGroup.xml b/app/code/Magento/Security/Test/Mftf/ActionGroup/AdminFillInUserWithExpirationActionGroup.xml new file mode 100644 index 0000000000000..d1d5427506e40 --- /dev/null +++ b/app/code/Magento/Security/Test/Mftf/ActionGroup/AdminFillInUserWithExpirationActionGroup.xml @@ -0,0 +1,21 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + /** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +--> +<actionGroups xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/actionGroupSchema.xsd"> + <actionGroup name="AdminFillInUserWithExpirationActionGroup" + extends="AdminFillNewUserFormRequiredFieldsActionGroup"> + <annotations> + <description>Goes to the Admin Users grid page. Clicks on Create User. Fills in the provided User details with a expiration date.</description> + </annotations> + <arguments> + <argument name="expires_at" type="string"/> + </arguments> + <fillField selector="{{AdminNewUserFormSection.userExpiresField}}" userInput="{{expires_at}}" + stepKey="fillExpireDate" after="fillPasswordConfirmation"/> + </actionGroup> +</actionGroups> diff --git a/app/code/Magento/Security/Test/Mftf/ActionGroup/AdminSaveUserInvalidExpirationActionGroup.xml b/app/code/Magento/Security/Test/Mftf/ActionGroup/AdminSaveUserInvalidExpirationActionGroup.xml new file mode 100644 index 0000000000000..7cca38e68d8d1 --- /dev/null +++ b/app/code/Magento/Security/Test/Mftf/ActionGroup/AdminSaveUserInvalidExpirationActionGroup.xml @@ -0,0 +1,22 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + /** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +--> +<actionGroups xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/actionGroupSchema.xsd"> + <actionGroup name="AdminSaveUserInvalidExpirationActionGroup"> + <annotations> + <description>Error message for saving an admin user with an invalid expiration date.</description> + </annotations> + <click selector="{{AdminNewUserFormSection.save}}" stepKey="clickSaveUser" /> + <waitForPageLoad stepKey="waitForSaveTheUser" /> + <scrollToTopOfPage stepKey="scrollToTopOfPage"/> + <click selector="{{AdminNewUserFormSection.userInfoTab}}" stepKey="openUserTab"/> + <waitForPageLoad stepKey="waitForUserRoleTabOpened"/> + <see selector="{{AdminMessagesSection.error}}" userInput='"Expiration date" must be later than the current date.' + stepKey="seeErrorMessage"/> + </actionGroup> +</actionGroups> diff --git a/app/code/Magento/Security/Test/Mftf/ActionGroup/AdminSaveUserSuccessActionGroup.xml b/app/code/Magento/Security/Test/Mftf/ActionGroup/AdminSaveUserSuccessActionGroup.xml new file mode 100644 index 0000000000000..7aed1e07112cf --- /dev/null +++ b/app/code/Magento/Security/Test/Mftf/ActionGroup/AdminSaveUserSuccessActionGroup.xml @@ -0,0 +1,18 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + /** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +--> +<actionGroups xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/actionGroupSchema.xsd"> + <actionGroup name="AdminSaveUserSuccessActionGroup"> + <annotations> + <description>Success message for saving an admin user successfully.</description> + </annotations> + <click selector="{{AdminNewUserFormSection.save}}" stepKey="clickSaveUser" /> + <waitForPageLoad stepKey="waitForSaveTheUser" /> + <see userInput="You saved the user." stepKey="seeSuccessMessage" /> + </actionGroup> +</actionGroups> diff --git a/app/code/Magento/Security/Test/Mftf/Section/AdminEditUserSection.xml b/app/code/Magento/Security/Test/Mftf/Section/AdminEditUserSection.xml new file mode 100644 index 0000000000000..d7acf2466c09e --- /dev/null +++ b/app/code/Magento/Security/Test/Mftf/Section/AdminEditUserSection.xml @@ -0,0 +1,13 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- + /** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +--> +<sections xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Page/etc/SectionObject.xsd"> + <section name="AdminEditUserSection"> + <element name="userExpiresField" type="input" selector="#user_expires_at"/> + </section> +</sections> diff --git a/app/code/Magento/Security/Test/Mftf/Section/AdminNewUserFormSection.xml b/app/code/Magento/Security/Test/Mftf/Section/AdminNewUserFormSection.xml new file mode 100644 index 0000000000000..1d1aba4da07dd --- /dev/null +++ b/app/code/Magento/Security/Test/Mftf/Section/AdminNewUserFormSection.xml @@ -0,0 +1,13 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + /** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +--> +<sections xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Page/etc/SectionObject.xsd"> + <section name="AdminNewUserFormSection"> + <element name="userExpiresField" type="input" selector="#page_tabs_main_section_content input[name='expires_at']"/> + </section> +</sections> diff --git a/app/code/Magento/Security/Test/Mftf/Test/AdminCreateNewUserWithInvalidExpirationTest.xml b/app/code/Magento/Security/Test/Mftf/Test/AdminCreateNewUserWithInvalidExpirationTest.xml new file mode 100644 index 0000000000000..fc91c8caddf71 --- /dev/null +++ b/app/code/Magento/Security/Test/Mftf/Test/AdminCreateNewUserWithInvalidExpirationTest.xml @@ -0,0 +1,35 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + /** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +--> +<tests xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/testSchema.xsd"> + <test name="AdminCreateNewUserWithInvalidExpirationTest"> + <annotations> + <features value="Security"/> + <stories value="Create new user with invalid expiration date."/> + <title value="Create new user with invalid expiration date"/> + <description value="Create new user with invalid expiration date."/> + <testCaseId value="" /> + <severity value="CRITICAL"/> + <group value="security"/> + </annotations> + + <before> + <actionGroup ref="AdminLoginActionGroup" stepKey="loginAsAdmin"/> + </before> + <after> + <actionGroup ref="AdminLogoutActionGroup" stepKey="logout"/> + </after> + + <actionGroup ref="AdminOpenNewUserPageActionGroup" stepKey="openNewUserPage" /> + <generateDate date="-5 day" format="M j, Y g:i:s A" stepKey="expiresDateTime"/> + <actionGroup ref="AdminFillInUserWithExpirationActionGroup" stepKey="fillInNewUserWithValidExpiration"> + <argument name="expires_at" value="{$expiresDateTime}"/> + </actionGroup> + <actionGroup ref="AdminSaveUserInvalidExpirationActionGroup" stepKey="saveNewUserWithInvalidExpirationError" /> + </test> +</tests> diff --git a/app/code/Magento/Security/Test/Mftf/Test/AdminCreateNewUserWithValidExpirationTest.xml b/app/code/Magento/Security/Test/Mftf/Test/AdminCreateNewUserWithValidExpirationTest.xml new file mode 100644 index 0000000000000..cd67bf4612520 --- /dev/null +++ b/app/code/Magento/Security/Test/Mftf/Test/AdminCreateNewUserWithValidExpirationTest.xml @@ -0,0 +1,43 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + /** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +--> +<tests xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/testSchema.xsd"> + <test name="AdminCreateNewUserWithValidExpirationTest"> + <annotations> + <features value="Security"/> + <stories value="Create new user with valid expiration date."/> + <title value="Create new user with valid expiration date"/> + <description value="Create new user with valid expiration date."/> + <testCaseId value="" /> + <severity value="CRITICAL"/> + <group value="security"/> + </annotations> + <before> + <actionGroup ref="AdminLoginActionGroup" stepKey="loginAsAdmin"/> + </before> + <after> + <actionGroup ref="AdminLogoutActionGroup" stepKey="logout"/> + </after> + + <actionGroup ref="AdminOpenNewUserPageActionGroup" stepKey="openNewUserPage" /> + <generateDate date="+5 day" format="M j, Y g:i:s A" stepKey="expiresDateTime"/> + <actionGroup ref="AdminFillInUserWithExpirationActionGroup" stepKey="fillInNewUserWithValidExpiration"> + <argument name="expires_at" value="{$expiresDateTime}"/> + </actionGroup> + <actionGroup ref="AdminSaveUserSuccessActionGroup" stepKey="saveNewUserWithValidExpirationSuccess" /> + + <actionGroup ref="AdminOpenUserEditPageActionGroup" stepKey="openUserEditPage"> + <argument name="user" value="NewAdminUser"/> + </actionGroup> + <seeInField userInput="{$expiresDateTime}" selector="input#user_expires_at" stepKey="verifyUserExpirationInField"/> + + <actionGroup ref="AdminDeleteCustomUserActionGroup" stepKey="deleteUser"> + <argument name="user" value="NewAdminUser"/> + </actionGroup> + </test> +</tests> diff --git a/app/code/Magento/Security/Test/Mftf/Test/AdminLockAdminUserWhenCreatingNewIntegrationTest.xml b/app/code/Magento/Security/Test/Mftf/Test/AdminLockAdminUserWhenCreatingNewIntegrationTest.xml index 03681a1753288..a75f65dffeca3 100644 --- a/app/code/Magento/Security/Test/Mftf/Test/AdminLockAdminUserWhenCreatingNewIntegrationTest.xml +++ b/app/code/Magento/Security/Test/Mftf/Test/AdminLockAdminUserWhenCreatingNewIntegrationTest.xml @@ -18,10 +18,12 @@ <testCaseId value="MC-14382" /> <group value="security"/> <group value="mtf_migrated"/> + <!-- skip due to MQE-1964 --> + <group value="skip"/> </annotations> <before> <!-- Log in to Admin Panel --> - <actionGroup ref="LoginAsAdmin" stepKey="loginAsAdmin"/> + <actionGroup ref="AdminLoginActionGroup" stepKey="loginAsAdmin"/> </before> <after> <!-- Unlock Admin user --> @@ -92,7 +94,7 @@ </actionGroup> <!-- Try to login as admin and check error --> - <actionGroup ref="LoginAsAdmin" stepKey="loginAsLockedAdmin"/> + <actionGroup ref="AdminLoginActionGroup" stepKey="loginAsLockedAdmin"/> <actionGroup ref="AssertMessageOnAdminLoginActionGroup" stepKey="checkLoginError" /> </test> </tests> diff --git a/app/code/Magento/Security/Test/Mftf/Test/AdminLockAdminUserWhenCreatingNewRoleTest.xml b/app/code/Magento/Security/Test/Mftf/Test/AdminLockAdminUserWhenCreatingNewRoleTest.xml index c7fb05a3b7358..3d04f3eed4daf 100644 --- a/app/code/Magento/Security/Test/Mftf/Test/AdminLockAdminUserWhenCreatingNewRoleTest.xml +++ b/app/code/Magento/Security/Test/Mftf/Test/AdminLockAdminUserWhenCreatingNewRoleTest.xml @@ -18,10 +18,12 @@ <testCaseId value="MC-14384" /> <group value="security"/> <group value="mtf_migrated"/> + <!-- skip due to MQE-1964 --> + <group value="skip"/> </annotations> <before> <!-- Log in to Admin Panel --> - <actionGroup ref="LoginAsAdmin" stepKey="loginAsAdmin"/> + <actionGroup ref="AdminLoginActionGroup" stepKey="loginAsAdmin"/> </before> <after> <!-- Unlock Admin user --> @@ -90,7 +92,7 @@ </actionGroup> <!-- Try to login as admin and check error message --> - <actionGroup ref="LoginAsAdmin" stepKey="loginAsLockedAdmin"/> + <actionGroup ref="AdminLoginActionGroup" stepKey="loginAsLockedAdmin"/> <actionGroup ref="AssertMessageOnAdminLoginActionGroup" stepKey="checkLoginMessage"/> </test> </tests> diff --git a/app/code/Magento/Security/Test/Mftf/Test/AdminLoginAdminUserWithInvalidExpiration.xml b/app/code/Magento/Security/Test/Mftf/Test/AdminLoginAdminUserWithInvalidExpiration.xml new file mode 100644 index 0000000000000..3fb798521fb45 --- /dev/null +++ b/app/code/Magento/Security/Test/Mftf/Test/AdminLoginAdminUserWithInvalidExpiration.xml @@ -0,0 +1,51 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + /** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +--> + +<tests xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/testSchema.xsd"> + + <test name="AdminLoginAdminUserWithInvalidExpiration"> + <annotations> + <features value="Security"/> + <stories value="Try to login as a user with an invalid expiration date."/> + <title value="Try to login as a user with an invalid expiration date"/> + <description value="Try to login as a user with an invalid expiration date."/> + <testCaseId value=""/> + <severity value="CRITICAL"/> + <group value="security"/> + </annotations> + + <before> + <actionGroup ref="AdminLoginActionGroup" stepKey="loginAsAdmin"/> + </before> + + <actionGroup ref="AdminOpenNewUserPageActionGroup" stepKey="openNewUserPage" /> + <generateDate date="+2 minute" format="M d, Y g:i:s A" stepKey="expiresDateTime"/> + <actionGroup ref="AdminFillInUserWithExpirationActionGroup" stepKey="fillInNewUserWithValidExpiration"> + <argument name="expires_at" value="{$expiresDateTime}"/> + </actionGroup> + <grabValueFrom selector="{{AdminNewUserFormSection.username}}" stepKey="grabUsername"/> + <grabValueFrom selector="{{AdminNewUserFormSection.password}}" stepKey="grabPassword"/> + <scrollToTopOfPage stepKey="scrollToTopOfPage"/> + <click selector="{{AdminNewUserFormSection.userInfoTab}}" stepKey="openUserInfoTab"/> + <actionGroup ref="AdminSaveUserSuccessActionGroup" stepKey="saveNewUserWithValidExpirationSuccess"/> + <actionGroup ref="AdminLogoutActionGroup" stepKey="logout"/> + <wait time="120" stepKey="waitForUserToExpire"/> + <actionGroup ref="AdminLoginActionGroup" stepKey="loginAsNewAdmin"> + <argument name="username" value="{$grabUsername}"/> + <argument name="password" value="{$grabPassword}"/> + </actionGroup> + <actionGroup ref="AssertMessageOnAdminLoginActionGroup" stepKey="checkLoginMessage" /> + + <!-- delete user --> + <actionGroup ref="AdminLoginActionGroup" stepKey="loginAsAdmin"/> + <actionGroup ref="AdminDeleteCustomUserActionGroup" stepKey="deleteUser"> + <argument name="user" value="NewAdminUser" /> + </actionGroup> + </test> +</tests> diff --git a/app/code/Magento/Security/Test/Mftf/Test/AdminLoginAdminUserWithValidExpiration.xml b/app/code/Magento/Security/Test/Mftf/Test/AdminLoginAdminUserWithValidExpiration.xml new file mode 100644 index 0000000000000..5d12650351bc0 --- /dev/null +++ b/app/code/Magento/Security/Test/Mftf/Test/AdminLoginAdminUserWithValidExpiration.xml @@ -0,0 +1,51 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + /** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +--> + +<tests xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/testSchema.xsd"> + + <test name="AdminLoginAdminUserWithValidExpiration"> + <annotations> + <features value="Security"/> + <stories value="Login as a user with a valid expiration date."/> + <title value="Login as a user with a valid expiration date"/> + <description value="Login as a user with a valid expiration date."/> + <testCaseId value=""/> + <severity value="CRITICAL"/> + <group value="security"/> + </annotations> + + <before> + <actionGroup ref="AdminLoginActionGroup" stepKey="loginAsAdmin"/> + </before> + <after> + <actionGroup ref="AdminLogoutActionGroup" stepKey="logout"/> + </after> + + <actionGroup ref="AdminOpenNewUserPageActionGroup" stepKey="openNewUserPage" /> + <generateDate date="+5 day" format="M d, Y g:i:s A" stepKey="expiresDateTime"/> + <actionGroup ref="AdminFillInUserWithExpirationActionGroup" stepKey="fillInNewUserWithValidExpiration"> + <argument name="expires_at" value="{$expiresDateTime}"/> + </actionGroup> + <grabValueFrom selector="{{AdminNewUserFormSection.username}}" stepKey="grabUsername"/> + <grabValueFrom selector="{{AdminNewUserFormSection.password}}" stepKey="grabPassword"/> + <actionGroup ref="AdminSaveUserSuccessActionGroup" stepKey="saveNewUserWithValidExpirationSuccess"/> + <actionGroup ref="AdminLogoutActionGroup" stepKey="logout"/> + <actionGroup ref="AdminLoginActionGroup" stepKey="loginAsNewAdmin"> + <argument name="username" value="{$grabUsername}"/> + <argument name="password" value="{$grabPassword}"/> + </actionGroup> + <actionGroup ref="AssertAdminDashboardPageIsVisibleActionGroup" stepKey="seeDashboardPage"/> + + <actionGroup ref="AdminLogoutActionGroup" stepKey="logoutAsUserWithValidExpiration"/> + <actionGroup ref="AdminLoginActionGroup" stepKey="loginAsAdmin"/> + <actionGroup ref="AdminDeleteCustomUserActionGroup" stepKey="deleteUser"> + <argument name="user" value="NewAdminUser"/> + </actionGroup> + </test> +</tests> diff --git a/app/code/Magento/Security/Test/Mftf/Test/AdminNavigateWhileUserExpiredTest.xml b/app/code/Magento/Security/Test/Mftf/Test/AdminNavigateWhileUserExpiredTest.xml new file mode 100644 index 0000000000000..c1a951afd87ec --- /dev/null +++ b/app/code/Magento/Security/Test/Mftf/Test/AdminNavigateWhileUserExpiredTest.xml @@ -0,0 +1,59 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + /** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +--> +<tests xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/testSchema.xsd"> + + <test name="AdminNavigateWhileUserExpiredTest"> + <annotations> + <features value="Security"/> + <stories value="Navigate to an admin page after user expiration date passes."/> + <title value="Navigate to an admin page after user expiration date passes"/> + <description value="Navigate to an admin page after user expiration date passes."/> + <testCaseId value="" /> + <severity value="CRITICAL"/> + <group value="security"/> + </annotations> + + <before> + <actionGroup ref="AdminLoginActionGroup" stepKey="loginAsAdmin"/> + </before> + <after> + <actionGroup ref="AdminLogoutActionGroup" stepKey="logout"/> + </after> + + <!-- Create user --> + <actionGroup ref="AdminOpenNewUserPageActionGroup" stepKey="openNewUserPage" /> + <generateDate date="+2 minute" format="M d, Y g:i:s A" stepKey="expiresDateTime"/> + <actionGroup ref="AdminFillInUserWithExpirationActionGroup" stepKey="fillInNewUserWithValidExpiration"> + <argument name="expires_at" value="{$expiresDateTime}"/> + </actionGroup> + <grabValueFrom selector="{{AdminNewUserFormSection.username}}" stepKey="grabUsername"/> + <grabValueFrom selector="{{AdminNewUserFormSection.password}}" stepKey="grabPassword"/> + <scrollToTopOfPage stepKey="scrollToTopOfPage"/> + <click selector="{{AdminNewUserFormSection.userInfoTab}}" stepKey="openUserInfoTab"/> + <actionGroup ref="AdminSaveUserSuccessActionGroup" stepKey="saveNewUserWithValidExpirationSuccess"/> + <actionGroup ref="AdminLogoutActionGroup" stepKey="logout"/> + + <!-- Login as that user --> + <actionGroup ref="AdminLoginActionGroup" stepKey="loginAsNewAdmin"> + <argument name="username" value="{$grabUsername}"/> + <argument name="password" value="{$grabPassword}"/> + </actionGroup> + <actionGroup ref="AssertAdminDashboardPageIsVisibleActionGroup" stepKey="seeDashboardPage"/> + <wait time="120" stepKey="waitForUserToExpire"/> + <amOnPage url="{{AdminCustomerPage.url}}" stepKey="navigateToCustomers"/> + <!-- Confirm that user is logged out --> + <seeInCurrentUrl url="{{AdminLoginPage.url}}" stepKey="seeAdminLoginUrl"/> + + <!-- Delete created user --> + <actionGroup ref="AdminLoginActionGroup" stepKey="loginAsAdmin"/> + <actionGroup ref="AdminDeleteCustomUserActionGroup" stepKey="deleteUser"> + <argument name="user" value="NewAdminUser"/> + </actionGroup> + </test> +</tests> diff --git a/app/code/Magento/Security/Test/Mftf/Test/AdminUserLockWhenCreatingNewUserTest.xml b/app/code/Magento/Security/Test/Mftf/Test/AdminUserLockWhenCreatingNewUserTest.xml index 4ceffd676313d..c130de5d8699f 100644 --- a/app/code/Magento/Security/Test/Mftf/Test/AdminUserLockWhenCreatingNewUserTest.xml +++ b/app/code/Magento/Security/Test/Mftf/Test/AdminUserLockWhenCreatingNewUserTest.xml @@ -21,7 +21,7 @@ </annotations> <before> <!-- Log in to Admin Panel --> - <actionGroup ref="LoginAsAdmin" stepKey="loginAsAdmin"/> + <actionGroup ref="AdminLoginActionGroup" stepKey="loginAsAdmin"/> </before> <after> <!-- Unlock Admin user --> @@ -73,7 +73,7 @@ </actionGroup> <!-- Try to login as admin and check error --> - <actionGroup ref="LoginAsAdmin" stepKey="loginAsLockedAdmin"/> + <actionGroup ref="AdminLoginActionGroup" stepKey="loginAsLockedAdmin"/> <actionGroup ref="AssertMessageOnAdminLoginActionGroup" stepKey="seeLoginUserErrorMessage" /> </test> </tests> diff --git a/app/code/Magento/Security/Test/Mftf/Test/AdminUserLockWhenEditingUserTest.xml b/app/code/Magento/Security/Test/Mftf/Test/AdminUserLockWhenEditingUserTest.xml index 9f421668bdc4f..5b4f38ae30566 100644 --- a/app/code/Magento/Security/Test/Mftf/Test/AdminUserLockWhenEditingUserTest.xml +++ b/app/code/Magento/Security/Test/Mftf/Test/AdminUserLockWhenEditingUserTest.xml @@ -22,16 +22,16 @@ <before> <createData entity="NewAdminUser" stepKey="user" /> <!-- Log in to Admin Panel --> - <actionGroup ref="LoginAsAdmin" stepKey="loginAsAdmin"/> + <actionGroup ref="AdminLoginActionGroup" stepKey="loginAsAdmin"/> </before> <after> <!-- Unlock Admin user --> <magentoCLI command="admin:user:unlock {{DefaultAdminUser.username}}" stepKey="unlockAdminUser"/> - <actionGroup ref="LoginAsAdmin" stepKey="loginAsAdmin"/> + <actionGroup ref="AdminLoginActionGroup" stepKey="loginAsAdmin"/> <actionGroup ref="AdminDeleteUserViaCurlActionGroup" stepKey="deleteUser"> <argument name="user" value="$$user$$" /> </actionGroup> - <actionGroup ref="logout" stepKey="logout"/> + <actionGroup ref="AdminLogoutActionGroup" stepKey="logout"/> </after> <actionGroup ref="AdminOpenUserEditPageActionGroup" stepKey="openEditUserPageFirstAttempt"> @@ -92,7 +92,7 @@ </actionGroup> <!-- Try to login as admin and check error --> - <actionGroup ref="LoginAsAdmin" stepKey="loginAsLockedAdmin"/> + <actionGroup ref="AdminLoginActionGroup" stepKey="loginAsLockedAdmin"/> <actionGroup ref="AssertMessageOnAdminLoginActionGroup" stepKey="seeLoginErrorMessage" /> </test> </tests> diff --git a/app/code/Magento/Security/Test/Mftf/Test/NewCustomerPasswordComplexityTest.xml b/app/code/Magento/Security/Test/Mftf/Test/NewCustomerPasswordComplexityTest.xml index 74a9c68cb2f79..d7151aff22fa7 100644 --- a/app/code/Magento/Security/Test/Mftf/Test/NewCustomerPasswordComplexityTest.xml +++ b/app/code/Magento/Security/Test/Mftf/Test/NewCustomerPasswordComplexityTest.xml @@ -15,6 +15,7 @@ <title value="Notify the customer if password complexity does not match the requirements"/> <description value="Notify the customer if password complexity does not match the requirements"/> <testCaseId value="MC-14368"/> + <severity value="CRITICAL"/> <group value="security"/> <group value="mtf_migrated"/> </annotations> diff --git a/app/code/Magento/Security/Test/Mftf/Test/NewCustomerPasswordLengthTest.xml b/app/code/Magento/Security/Test/Mftf/Test/NewCustomerPasswordLengthTest.xml index a10059d0603c5..298b4de11f9ca 100644 --- a/app/code/Magento/Security/Test/Mftf/Test/NewCustomerPasswordLengthTest.xml +++ b/app/code/Magento/Security/Test/Mftf/Test/NewCustomerPasswordLengthTest.xml @@ -15,6 +15,7 @@ <title value="Notify the customer if password length does not match the requirements"/> <description value="Notify the customer if password length does not match the requirements"/> <testCaseId value="MC-14367"/> + <severity value="CRITICAL"/> <group value="security"/> <group value="mtf_migrated"/> </annotations> diff --git a/app/code/Magento/Security/Test/Mftf/Test/StorefrontAccountPasswordFieldsNotAvailableTest.xml b/app/code/Magento/Security/Test/Mftf/Test/StorefrontAccountPasswordFieldsNotAvailableTest.xml new file mode 100644 index 0000000000000..fd7ef0261cd26 --- /dev/null +++ b/app/code/Magento/Security/Test/Mftf/Test/StorefrontAccountPasswordFieldsNotAvailableTest.xml @@ -0,0 +1,43 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + /** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +--> + +<tests xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/testSchema.xsd"> + <test name="StorefrontAccountPasswordFieldsNotAvailableTest"> + <annotations> + <features value="Security"/> + <stories value="Password Fields not Available on Frontend Until Checkbox Change Password Unchecked"/> + <title value="Password Fields not Available"/> + <description value="User Cannot Change Password Until Checkbox Change Password Unchecked"/> + <testCaseId value="MC-14369"/> + <group value="security"/> + <group value="mtf_migrated"/> + </annotations> + + <before> + <createData entity="Simple_US_Customer" stepKey="customer"/> + </before> + <after> + <deleteData createDataKey="customer" stepKey="deleteCustomer"/> + </after> + + <!-- TEST BODY --> + <!-- Go to storefront home page --> + <actionGroup ref="StorefrontOpenHomePageActionGroup" stepKey="openStoreFrontHomePage"/> + <!-- Login as created customer --> + <actionGroup ref="LoginToStorefrontActionGroup" stepKey="loginToStorefrontAccount"> + <argument name="Customer" value="$$customer$$"/> + </actionGroup> + <!-- Navigate to "Change Account Information" tab --> + <actionGroup ref="StorefrontNavigateToAccountInformationChangeActionGroup" stepKey="goToCustomerEditPage"/> + <!-- Assert Account Password Fields not Available --> + <actionGroup ref="AssertStorefrontAccountPasswordFieldsNotAvailableActionGroup" stepKey="uncheckedCheckbox"/> + <!-- END TEST BODY --> + + </test> +</tests> diff --git a/app/code/Magento/Security/Test/Mftf/Test/StorefrontChangeCustomerPasswordTest.xml b/app/code/Magento/Security/Test/Mftf/Test/StorefrontChangeCustomerPasswordTest.xml new file mode 100644 index 0000000000000..242bad033ed47 --- /dev/null +++ b/app/code/Magento/Security/Test/Mftf/Test/StorefrontChangeCustomerPasswordTest.xml @@ -0,0 +1,47 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + /** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +--> + +<tests xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/testSchema.xsd"> + <test name="StorefrontChangeCustomerPasswordTest"> + <annotations> + <features value="Security"/> + <stories value="Changing Customer's Password"/> + <title value="Changing Customer Password Test"/> + <description value="Changing Customer's password using correct value"/> + <testCaseId value="MC-14370"/> + <group value="security"/> + <group value="mtf_migrated"/> + </annotations> + + <before> + <createData entity="Simple_US_Customer" stepKey="customer"/> + </before> + <after> + <deleteData createDataKey="customer" stepKey="deleteCustomer"/> + </after> + + <!-- TEST BODY --> + <!-- Go to storefront home page --> + <actionGroup ref="StorefrontOpenHomePageActionGroup" stepKey="openStoreFrontHomePage"/> + <!-- Login as created customer --> + <actionGroup ref="LoginToStorefrontActionGroup" stepKey="loginToStorefrontAccount"> + <argument name="Customer" value="$$customer$$"/> + </actionGroup> + <!-- Navigate to "Change Account Information" tab --> + <actionGroup ref="StorefrontNavigateToAccountInformationChangeActionGroup" stepKey="goToCustomerEditPage"/> + <!-- Change Password with Correct Data --> + <actionGroup ref="StorefrontCustomerChangePasswordActionGroup" stepKey="changePasswordWithCorrectData"> + <argument name="password" value="$$customer.password$$"/> + </actionGroup> + <!-- See Success Message --> + <actionGroup ref="AssertMessageCustomerChangeAccountInfoActionGroup" stepKey="assertAccountMessageSuccess"/> + <!-- END TEST BODY --> + + </test> +</tests> diff --git a/app/code/Magento/Security/Test/Mftf/Test/StorefrontChangeCustomerPasswordTestWithIncorrectDataTest.xml b/app/code/Magento/Security/Test/Mftf/Test/StorefrontChangeCustomerPasswordTestWithIncorrectDataTest.xml new file mode 100644 index 0000000000000..f08db36e7e20c --- /dev/null +++ b/app/code/Magento/Security/Test/Mftf/Test/StorefrontChangeCustomerPasswordTestWithIncorrectDataTest.xml @@ -0,0 +1,50 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + /** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +--> + +<tests xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/testSchema.xsd"> + <test name="StorefrontChangeCustomerPasswordTestWithIncorrectDataTest"> + <annotations> + <features value="Security"/> + <stories value="Changing Customer's Password With Incorrect Current Password"/> + <title value="Changing Customer Password Test with Incorrect Data"/> + <description value="Changing Customer's password using Incorrect Current Password"/> + <testCaseId value="MC-14371"/> + <group value="security"/> + <group value="mtf_migrated"/> + </annotations> + + <before> + <createData entity="Simple_US_Customer" stepKey="customer"/> + </before> + <after> + <deleteData createDataKey="customer" stepKey="deleteCustomer"/> + </after> + + <!-- TEST BODY --> + <!-- Go to storefront home page --> + <actionGroup ref="StorefrontOpenHomePageActionGroup" stepKey="openStoreFrontHomePage"/> + <!-- Login as created customer --> + <actionGroup ref="LoginToStorefrontActionGroup" stepKey="loginToStorefrontAccount"> + <argument name="Customer" value="$$customer$$"/> + </actionGroup> + <!-- Navigate to "Change Account Information" tab --> + <actionGroup ref="StorefrontNavigateToAccountInformationChangeActionGroup" stepKey="goToCustomerEditPage"/> + <!-- Change Password with InCorrect Current Password --> + <actionGroup ref="StorefrontCustomerChangePasswordActionGroup" stepKey="changePasswordWithInCorrectData"> + <argument name="password" value="{{Colorado_US_Customer.password}}"/> + </actionGroup> + <!-- See Error Message --> + <actionGroup ref="AssertMessageCustomerChangeAccountInfoActionGroup" stepKey="assertAccountMessageFirstAttempt"> + <argument name="message" value="The password doesn't match this account. Verify the password and try again."/> + <argument name="messageType" value="error"/> + </actionGroup> + <!-- END TEST BODY --> + + </test> +</tests> diff --git a/app/code/Magento/Security/Test/Mftf/Test/StorefrontSecureChangingCustomerEmailTest.xml b/app/code/Magento/Security/Test/Mftf/Test/StorefrontSecureChangingCustomerEmailTest.xml new file mode 100644 index 0000000000000..020e1965ddf71 --- /dev/null +++ b/app/code/Magento/Security/Test/Mftf/Test/StorefrontSecureChangingCustomerEmailTest.xml @@ -0,0 +1,63 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + /** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +--> + +<tests xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/testSchema.xsd"> + <test name="StorefrontSecureChangingCustomerEmailTest"> + <annotations> + <features value="Security"/> + <stories value="Changing Customer's email"/> + <title value="Changing Customer Email Test"/> + <description value="Changing Customer's email with correct and wrong passwords"/> + <testCaseId value="MC-14385"/> + <group value="security"/> + <group value="mtf_migrated"/> + </annotations> + + <before> + <createData entity="Simple_US_Customer" stepKey="customer"/> + </before> + <after> + <deleteData createDataKey="customer" stepKey="deleteCustomer"/> + </after> + + <!-- TEST BODY --> + <!-- Go to storefront home page --> + <actionGroup ref="StorefrontOpenHomePageActionGroup" stepKey="openStoreFrontHomePage"/> + <!-- Login as created customer --> + <actionGroup ref="LoginToStorefrontActionGroup" stepKey="loginToStorefrontAccount"> + <argument name="Customer" value="$$customer$$"/> + </actionGroup> + <!-- Navigate to "Account Information" tab First Time--> + <actionGroup ref="StorefrontOpenCustomerAccountInfoEditPageActionGroup" stepKey="goToCustomerEditPageFirstTime"/> + <!-- Checking Email checkbox, entering new email, saving with correct password --> + <actionGroup ref="StorefrontCustomerChangeEmailActionGroup" stepKey="changeEmailCorrectAttempt"> + <argument name="email" value="$$customer.email$$"/> + <argument name="password" value="$$customer.password$$"/> + </actionGroup> + <!-- See Success Notify--> + <actionGroup ref="AssertMessageCustomerChangeAccountInfoActionGroup" stepKey="seeSuccessMessage"> + <argument name="message" value="You saved the account information."/> + </actionGroup> + <!-- Navigate to "Account Information" tab Second Time--> + <actionGroup ref="StorefrontOpenCustomerAccountInfoEditPageActionGroup" stepKey="goToCustomerEditPageSecondTime" /> + <!-- Checking Email checkbox, entering new email, saving with incorrect password --> + <actionGroup ref="StorefrontCustomerChangeEmailActionGroup" stepKey="changeEmailWrongAttempt"> + <argument name="email" value="$$customer.email$$"/> + <argument name="password" value="WRONG_PASSWORD_123123q"/> + </actionGroup> + <!-- See Failure Message--> + <actionGroup ref="AssertMessageCustomerChangeAccountInfoActionGroup" stepKey="seeFailureMessage"> + <argument name="message" value="The password doesn't match this account. Verify the password and try again."/> + <argument name="messageType" value="error"/> + </actionGroup> + + <!-- END TEST BODY --> + + </test> + </tests> diff --git a/app/code/Magento/Security/Test/Unit/Model/Plugin/AuthSessionTest.php b/app/code/Magento/Security/Test/Unit/Model/Plugin/AuthSessionTest.php index 0f7f590b71de4..9870de8fc2cb4 100644 --- a/app/code/Magento/Security/Test/Unit/Model/Plugin/AuthSessionTest.php +++ b/app/code/Magento/Security/Test/Unit/Model/Plugin/AuthSessionTest.php @@ -37,6 +37,12 @@ class AuthSessionTest extends \PHPUnit\Framework\TestCase /** @var \Magento\Framework\TestFramework\Unit\Helper\ObjectManager */ protected $objectManager; + /**@var \Magento\Security\Model\UserExpirationManager */ + protected $userExpirationManagerMock; + + /**@var \Magento\User\Model\User */ + protected $userMock; + /** * Init mocks for tests * @return void @@ -61,20 +67,31 @@ public function setUp() $this->securityCookieMock = $this->createPartialMock(SecurityCookie::class, ['setLogoutReasonCookie']); - $this->authSessionMock = $this->createPartialMock(\Magento\Backend\Model\Auth\Session::class, ['destroy']); + $this->authSessionMock = $this->createPartialMock( + \Magento\Backend\Model\Auth\Session::class, + ['destroy', 'getUser'] + ); $this->currentSessionMock = $this->createPartialMock( \Magento\Security\Model\AdminSessionInfo::class, ['isLoggedInStatus', 'getStatus', 'isActive'] ); + $this->userExpirationManagerMock = $this->createPartialMock( + \Magento\Security\Model\UserExpirationManager::class, + ['isUserExpired', 'deactivateExpiredUsersById'] + ); + + $this->userMock = $this->createMock(\Magento\User\Model\User::class); + $this->model = $this->objectManager->getObject( \Magento\Security\Model\Plugin\AuthSession::class, [ 'request' => $this->requestMock, 'messageManager' => $this->messageManagerMock, 'sessionsManager' => $this->adminSessionsManagerMock, - 'securityCookie' => $this->securityCookieMock + 'securityCookie' => $this->securityCookieMock, + 'userExpirationManager' => $this->userExpirationManagerMock, ] ); @@ -154,6 +171,59 @@ public function testAroundProlongSessionIsNotActiveAndIsAjaxRequest() $this->model->aroundProlong($this->authSessionMock, $proceed); } + /** + * @return void + */ + public function testAroundProlongSessionIsActiveUserIsExpired() + { + $result = 'result'; + $errorMessage = 'Error Message'; + + $proceed = function () use ($result) { + return $result; + }; + + $adminUserId = '12345'; + $this->currentSessionMock->expects($this->once()) + ->method('isLoggedInStatus') + ->willReturn(true); + + $this->authSessionMock->expects($this->exactly(2)) + ->method('getUser') + ->willReturn($this->userMock); + + $this->userMock->expects($this->exactly(2)) + ->method('getId') + ->willReturn($adminUserId); + + $this->requestMock->expects($this->once()) + ->method('getParam') + ->with('isAjax') + ->willReturn(false); + + $this->userExpirationManagerMock->expects($this->once()) + ->method('isUserExpired') + ->with($adminUserId) + ->willReturn(true); + + $this->userExpirationManagerMock->expects($this->once()) + ->method('deactivateExpiredUsersById') + ->with([$adminUserId]); + + $this->authSessionMock->expects($this->once()) + ->method('destroy'); + + $this->adminSessionsManagerMock->expects($this->once()) + ->method('getLogoutReasonMessage') + ->willReturn($errorMessage); + + $this->messageManagerMock->expects($this->once()) + ->method('addErrorMessage') + ->with($errorMessage); + + $this->model->aroundProlong($this->authSessionMock, $proceed); + } + /** * @return void */ @@ -164,10 +234,24 @@ public function testAroundProlongSessionIsActive() return $result; }; + $adminUserId = '12345'; $this->currentSessionMock->expects($this->any()) ->method('isLoggedInStatus') ->willReturn(true); + $this->authSessionMock->expects($this->once()) + ->method('getUser') + ->willReturn($this->userMock); + + $this->userMock->expects($this->once()) + ->method('getId') + ->willReturn($adminUserId); + + $this->userExpirationManagerMock->expects($this->once()) + ->method('isUserExpired') + ->with($adminUserId) + ->willReturn(false); + $this->adminSessionsManagerMock->expects($this->any()) ->method('processProlong'); diff --git a/app/code/Magento/Security/Test/Unit/Model/Plugin/UserValidationRulesTest.php b/app/code/Magento/Security/Test/Unit/Model/Plugin/UserValidationRulesTest.php new file mode 100644 index 0000000000000..00b3356c2e11d --- /dev/null +++ b/app/code/Magento/Security/Test/Unit/Model/Plugin/UserValidationRulesTest.php @@ -0,0 +1,61 @@ +<?php +declare(strict_types=1); +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ + +namespace Magento\Security\Test\Unit\Model\Plugin; + +/** + * Test class for expiration date user validation rule. + */ +class UserValidationRulesTest extends \PHPUnit\Framework\TestCase +{ + + /** + * @var \Magento\Security\Model\Plugin\UserValidationRules|\PHPUnit\Framework\MockObject\MockObject + */ + private $plugin; + + /** + * @var \Magento\User\Model\UserValidationRules|\PHPUnit\Framework\MockObject\MockObject + */ + private $userValidationRules; + + /** + * @var \Magento\Framework\Validator\DataObject|\PHPUnit\Framework\MockObject\MockObject + */ + private $validator; + + /** + * @var \Magento\User\Model\UserValidationRules + */ + private $rules; + + protected function setUp() + { + $objectManager = new \Magento\Framework\TestFramework\Unit\Helper\ObjectManager($this); + $userExpirationValidator = $this->createMock(\Magento\Security\Model\UserExpiration\Validator::class); + $this->userValidationRules = $this->createMock(\Magento\User\Model\UserValidationRules::class); + $this->rules = $objectManager->getObject(\Magento\User\Model\UserValidationRules::class); + $this->validator = $this->createMock(\Magento\Framework\Validator\DataObject::class); + $this->plugin = + $objectManager->getObject( + \Magento\Security\Model\Plugin\UserValidationRules::class, + ['validator' => $userExpirationValidator] + ); + } + + public function testAfterAddUserInfoRules() + { + $this->validator->expects(static::exactly(5))->method('addRule')->willReturn($this->validator); + static::assertSame($this->validator, $this->rules->addUserInfoRules($this->validator)); + static::assertSame($this->validator, $this->callAfterAddUserInfoRulesPlugin($this->validator)); + } + + protected function callAfterAddUserInfoRulesPlugin($validator) + { + return $this->plugin->afterAddUserInfoRules($this->userValidationRules, $validator); + } +} diff --git a/app/code/Magento/Security/Test/Unit/Model/UserExpiration/ValidatorTest.php b/app/code/Magento/Security/Test/Unit/Model/UserExpiration/ValidatorTest.php new file mode 100644 index 0000000000000..28541231cb123 --- /dev/null +++ b/app/code/Magento/Security/Test/Unit/Model/UserExpiration/ValidatorTest.php @@ -0,0 +1,99 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +declare(strict_types=1); + +namespace Magento\Security\Test\Unit\Model\UserExpiration; + +use Magento\Framework\TestFramework\Unit\Helper\ObjectManager; + +/** + * Test class for \Magento\Security\Model\UserExpiration\Validator. + */ +class ValidatorTest extends \PHPUnit\Framework\TestCase +{ + + /** + * @var \Magento\Security\Model\UserExpiration\Validator + */ + private $validator; + + /** + * @var \PHPUnit\Framework\MockObject\MockObject|\Magento\Framework\Stdlib\DateTime\DateTime + */ + private $dateTimeMock; + + /**@var \PHPUnit\Framework\MockObject\MockObject|\Magento\Framework\Stdlib\DateTime\TimezoneInterface */ + private $timezoneMock; + + protected function setUp() + { + $objectManager = new ObjectManager($this); + $this->dateTimeMock = + $this->createPartialMock(\Magento\Framework\Stdlib\DateTime\DateTime::class, ['gmtTimestamp']); + $this->timezoneMock = + $this->createPartialMock( + \Magento\Framework\Stdlib\DateTime\Timezone::class, + ['date', 'convertConfigTimeToUtc'] + ); + $this->validator = $objectManager->getObject( + \Magento\Security\Model\UserExpiration\Validator::class, + ['dateTime' => $this->dateTimeMock, 'timezone' => $this->timezoneMock] + ); + } + + public function testWithInvalidDate() + { + $expireDate = 'invalid_date'; + + static::assertFalse($this->validator->isValid($expireDate)); + static::assertContains( + '"Expiration date" is not a valid date.', + $this->validator->getMessages() + ); + } + + public function testWithPastDate() + { + /** @var \DateTime|\PHPUnit_Framework_MockObject_MockObject $dateObject */ + $dateObject = $this->createMock(\DateTime::class); + $this->timezoneMock->expects(static::once()) + ->method('date') + ->will(static::returnValue($dateObject)); + + $currentDate = new \DateTime(); + $currentDate = $currentDate->getTimestamp(); + $expireDate = new \DateTime(); + $expireDate->modify('-10 days'); + + $this->dateTimeMock->expects(static::once())->method('gmtTimestamp')->willReturn($currentDate); + $this->timezoneMock->expects(static::once())->method('date')->willReturn($expireDate); + $dateObject->expects(static::once())->method('getTimestamp')->willReturn($expireDate->getTimestamp()); + static::assertFalse($this->validator->isValid($expireDate->format('Y-m-d H:i:s'))); + static::assertContains( + '"Expiration date" must be later than the current date.', + $this->validator->getMessages() + ); + } + + public function testWithFutureDate() + { + /** @var \DateTime|\PHPUnit_Framework_MockObject_MockObject $dateObject */ + $dateObject = $this->createMock(\DateTime::class); + $this->timezoneMock->expects(static::once()) + ->method('date') + ->will(static::returnValue($dateObject)); + $currentDate = new \DateTime(); + $currentDate = $currentDate->getTimestamp(); + $expireDate = new \DateTime(); + $expireDate->modify('+10 days'); + + $this->dateTimeMock->expects(static::once())->method('gmtTimestamp')->willReturn($currentDate); + $this->timezoneMock->expects(static::once())->method('date')->willReturn($expireDate); + $dateObject->expects(static::once())->method('getTimestamp')->willReturn($expireDate->getTimestamp()); + static::assertTrue($this->validator->isValid($expireDate->format('Y-m-d H:i:s'))); + static::assertEquals([], $this->validator->getMessages()); + } +} diff --git a/app/code/Magento/Security/Test/Unit/Observer/AdminUserAuthenticateBeforeTest.php b/app/code/Magento/Security/Test/Unit/Observer/AdminUserAuthenticateBeforeTest.php new file mode 100644 index 0000000000000..3f5717abe2a2c --- /dev/null +++ b/app/code/Magento/Security/Test/Unit/Observer/AdminUserAuthenticateBeforeTest.php @@ -0,0 +1,133 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +declare(strict_types=1); + +namespace Magento\Security\Test\Unit\Observer; + +/** + * Test for \Magento\Security\Observer\AdminUserAuthenticateBefore + */ +class AdminUserAuthenticateBeforeTest extends \PHPUnit\Framework\TestCase +{ + /** + * @var \Magento\Framework\TestFramework\Unit\Helper\ObjectManager + */ + private $objectManager; + + /** + * @var \PHPUnit\Framework\MockObject\MockObject|\Magento\Security\Model\UserExpirationManager + */ + private $userExpirationManagerMock; + + /** + * @var \PHPUnit\Framework\MockObject\MockObject|\Magento\User\Model\User + */ + private $userMock; + + /** + * @var \PHPUnit\Framework\MockObject\MockObject|\Magento\User\Model\UserFactory + */ + private $userFactoryMock; + + /** + * @var \Magento\Security\Observer\AdminUserAuthenticateBefore + */ + private $observer; + + /** + * @var \PHPUnit\Framework\MockObject\MockObject|\Magento\Framework\Event\Observer::class + */ + private $eventObserverMock; + + /** + * @var \PHPUnit\Framework\MockObject\MockObject|\Magento\Framework\Event + */ + private $eventMock; + + /** + * @var \PHPUnit\Framework\MockObject\MockObject|\Magento\Security\Api\Data\UserExpirationInterface + */ + private $userExpirationMock; + + /** + * Set Up + * + * @return void + */ + protected function setUp() + { + $this->objectManager = new \Magento\Framework\TestFramework\Unit\Helper\ObjectManager($this); + + $this->userExpirationManagerMock = $this->createPartialMock( + \Magento\Security\Model\UserExpirationManager::class, + ['isUserExpired', 'deactivateExpiredUsersById'] + ); + $this->userFactoryMock = $this->createPartialMock(\Magento\User\Model\UserFactory::class, ['create']); + $this->userMock = $this->createPartialMock(\Magento\User\Model\User::class, ['loadByUsername', 'getId']); + $this->observer = $this->objectManager->getObject( + \Magento\Security\Observer\AdminUserAuthenticateBefore::class, + [ + 'userExpirationManager' => $this->userExpirationManagerMock, + 'userFactory' => $this->userFactoryMock, + ] + ); + $this->eventObserverMock = $this->createPartialMock(\Magento\Framework\Event\Observer::class, ['getEvent']); + $this->eventMock = $this->createPartialMock(\Magento\Framework\Event::class, ['getUsername']); + $this->userExpirationMock = $this->createPartialMock( + \Magento\Security\Api\Data\UserExpirationInterface::class, + [ + 'getUserId', + 'getExpiresAt', + 'setUserId', + 'setExpiresAt', + 'getExtensionAttributes', + 'setExtensionAttributes' + ] + ); + } + + /** + * @expectedException \Magento\Framework\Exception\Plugin\AuthenticationException + * @expectedExceptionMessage The account sign-in was incorrect or your account is disabled temporarily. + * Please wait and try again later + */ + public function testWithExpiredUser() + { + $adminUserId = '123'; + $username = 'testuser'; + $this->eventObserverMock->expects(static::once())->method('getEvent')->willReturn($this->eventMock); + $this->eventMock->expects(static::once())->method('getUsername')->willReturn($username); + $this->userFactoryMock->expects(static::once())->method('create')->willReturn($this->userMock); + $this->userMock->expects(static::once())->method('loadByUsername')->willReturnSelf(); + + $this->userExpirationManagerMock->expects(static::once()) + ->method('isUserExpired') + ->with($adminUserId) + ->willReturn(true); + $this->userMock->expects(static::exactly(3))->method('getId')->willReturn($adminUserId); + $this->userExpirationManagerMock->expects(static::once()) + ->method('deactivateExpiredUsersById') + ->with([$adminUserId]) + ->willReturn(null); + $this->observer->execute($this->eventObserverMock); + } + + public function testWithNonExpiredUser() + { + $adminUserId = '123'; + $username = 'testuser'; + $this->eventObserverMock->expects(static::once())->method('getEvent')->willReturn($this->eventMock); + $this->eventMock->expects(static::once())->method('getUsername')->willReturn($username); + $this->userFactoryMock->expects(static::once())->method('create')->willReturn($this->userMock); + $this->userMock->expects(static::once())->method('loadByUsername')->willReturnSelf(); + $this->userMock->expects(static::exactly(2))->method('getId')->willReturn($adminUserId); + $this->userExpirationManagerMock->expects(static::once()) + ->method('isUserExpired') + ->with($adminUserId) + ->willReturn(false); + $this->observer->execute($this->eventObserverMock); + } +} diff --git a/app/code/Magento/Security/Test/Unit/Observer/AfterAdminUserSaveTest.php b/app/code/Magento/Security/Test/Unit/Observer/AfterAdminUserSaveTest.php new file mode 100644 index 0000000000000..85505632e1eb6 --- /dev/null +++ b/app/code/Magento/Security/Test/Unit/Observer/AfterAdminUserSaveTest.php @@ -0,0 +1,157 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +declare(strict_types=1); + +namespace Magento\Security\Test\Unit\Observer; + +/** + * Test class for \Magento\Security\Observer\AfterAdminUserSave + */ +class AfterAdminUserSaveTest extends \PHPUnit\Framework\TestCase +{ + + /** + * @var \PHPUnit\Framework\MockObject\MockObject|\Magento\Security\Model\UserExpirationFactory + */ + private $userExpirationFactoryMock; + + /** + * @var \PHPUnit\Framework\MockObject\MockObject|\Magento\Security\Model\ResourceModel\UserExpiration + */ + private $userExpirationResourceMock; + + /** + * @var \Magento\Security\Observer\AfterAdminUserSave + */ + private $observer; + + /** + * @var \Magento\Framework\TestFramework\Unit\Helper\ObjectManager + */ + private $objectManager; + + /** + * @var \PHPUnit\Framework\MockObject\MockObject|\Magento\Framework\Event\Observer + */ + private $eventObserverMock; + + /** + * @var \PHPUnit\Framework\MockObject\MockObject|\Magento\Framework\Event + */ + private $eventMock; + + /** + * @var \PHPUnit\Framework\MockObject\MockObject|\Magento\User\Model\User + */ + private $userMock; + + /** + * @var \PHPUnit\Framework\MockObject\MockObject|\Magento\Security\Model\UserExpiration + */ + private $userExpirationMock; + + public function setUp() + { + $this->objectManager = new \Magento\Framework\TestFramework\Unit\Helper\ObjectManager($this); + + $this->userExpirationFactoryMock = $this->createMock(\Magento\Security\Model\UserExpirationFactory::class); + $this->userExpirationResourceMock = $this->createPartialMock( + \Magento\Security\Model\ResourceModel\UserExpiration::class, + ['load', 'save', 'delete'] + ); + $this->observer = $this->objectManager->getObject( + \Magento\Security\Observer\AfterAdminUserSave::class, + [ + 'userExpirationFactory' => $this->userExpirationFactoryMock, + 'userExpirationResource' => $this->userExpirationResourceMock, + ] + ); + $this->eventObserverMock = $this->createPartialMock(\Magento\Framework\Event\Observer::class, ['getEvent']); + $this->eventMock = $this->createPartialMock(\Magento\Framework\Event::class, ['getObject']); + $this->userMock = $this->createPartialMock(\Magento\User\Model\User::class, ['getId', 'getExpiresAt']); + $this->userExpirationMock = $this->createPartialMock( + \Magento\Security\Model\UserExpiration::class, + ['getId', 'getExpiresAt', 'setId', 'setExpiresAt'] + ); + } + + public function testSaveNewUserExpiration() + { + $userId = '123'; + $this->eventObserverMock->expects(static::once())->method('getEvent')->willReturn($this->eventMock); + $this->eventMock->expects(static::once())->method('getObject')->willReturn($this->userMock); + $this->userMock->expects(static::exactly(3))->method('getId')->willReturn($userId); + $this->userMock->expects(static::once())->method('getExpiresAt')->willReturn($this->getExpiresDateTime()); + $this->userExpirationFactoryMock->expects(static::once())->method('create') + ->willReturn($this->userExpirationMock); + $this->userExpirationResourceMock->expects(static::once())->method('load') + ->willReturn($this->userExpirationMock); + + $this->userExpirationMock->expects(static::once())->method('getId')->willReturn(null); + $this->userExpirationMock->expects(static::once())->method('setId')->willReturn($this->userExpirationMock); + $this->userExpirationMock->expects(static::once())->method('setExpiresAt') + ->willReturn($this->userExpirationMock); + $this->userExpirationResourceMock->expects(static::once())->method('save') + ->willReturn($this->userExpirationResourceMock); + $this->observer->execute($this->eventObserverMock); + } + + /** + * @throws \Exception + */ + public function testClearUserExpiration() + { + $userId = '123'; + $this->userExpirationMock->setId($userId); + + $this->eventObserverMock->expects(static::once())->method('getEvent')->willReturn($this->eventMock); + $this->eventMock->expects(static::once())->method('getObject')->willReturn($this->userMock); + $this->userMock->expects(static::exactly(2))->method('getId')->willReturn($userId); + $this->userMock->expects(static::once())->method('getExpiresAt')->willReturn(null); + $this->userExpirationFactoryMock->expects(static::once())->method('create') + ->willReturn($this->userExpirationMock); + $this->userExpirationResourceMock->expects(static::once())->method('load') + ->willReturn($this->userExpirationMock); + + $this->userExpirationMock->expects(static::once())->method('getId')->willReturn($userId); + $this->userExpirationResourceMock->expects(static::once())->method('delete') + ->willReturn($this->userExpirationResourceMock); + $this->observer->execute($this->eventObserverMock); + } + + public function testChangeUserExpiration() + { + $userId = '123'; + $this->userExpirationMock->setId($userId); + + $this->eventObserverMock->expects(static::once())->method('getEvent')->willReturn($this->eventMock); + $this->eventMock->expects(static::once())->method('getObject')->willReturn($this->userMock); + $this->userMock->expects(static::exactly(2))->method('getId')->willReturn($userId); + $this->userMock->expects(static::once())->method('getExpiresAt')->willReturn($this->getExpiresDateTime()); + $this->userExpirationFactoryMock->expects(static::once())->method('create') + ->willReturn($this->userExpirationMock); + $this->userExpirationResourceMock->expects(static::once())->method('load') + ->willReturn($this->userExpirationMock); + + $this->userExpirationMock->expects(static::once())->method('getId')->willReturn($userId); + $this->userExpirationMock->expects(static::once())->method('setExpiresAt') + ->willReturn($this->userExpirationMock); + $this->userExpirationResourceMock->expects(static::once())->method('save') + ->willReturn($this->userExpirationResourceMock); + $this->observer->execute($this->eventObserverMock); + } + + /** + * @return string + * @throws \Exception + */ + private function getExpiresDateTime() + { + $testDate = new \DateTime(); + $testDate->modify('+10 days'); + return $testDate->format('Y-m-d H:i:s'); + } +} diff --git a/app/code/Magento/Security/etc/adminhtml/di.xml b/app/code/Magento/Security/etc/adminhtml/di.xml index 79477e9443097..388a1eac742a5 100644 --- a/app/code/Magento/Security/etc/adminhtml/di.xml +++ b/app/code/Magento/Security/etc/adminhtml/di.xml @@ -15,6 +15,9 @@ <type name="Magento\Backend\Controller\Adminhtml\Auth\Login"> <plugin name="security_login_form" type="Magento\Security\Model\Plugin\LoginController" /> </type> + <type name="Magento\User\Model\UserValidationRules"> + <plugin name="user_expiration_validator" type="Magento\Security\Model\Plugin\UserValidationRules" /> + </type> <type name="Magento\Security\Model\Plugin\AccountManagement"> <arguments> <argument name="passwordRequestEvent" xsi:type="const">Magento\Security\Model\PasswordResetRequestEvent::CUSTOMER_PASSWORD_RESET_REQUEST</argument> @@ -28,4 +31,7 @@ </argument> </arguments> </type> + <type name="Magento\User\Block\User\Edit\Tab\Main"> + <plugin name="user_expiration_user_form_field" type="Magento\Security\Model\Plugin\AdminUserForm"/> + </type> </config> diff --git a/app/code/Magento/Security/etc/crontab.xml b/app/code/Magento/Security/etc/crontab.xml index a30a43730e6fa..7ee046ee44850 100644 --- a/app/code/Magento/Security/etc/crontab.xml +++ b/app/code/Magento/Security/etc/crontab.xml @@ -13,5 +13,8 @@ <job name="security_clean_password_reset_request_event_records" instance="Magento\Security\Model\SecurityManager" method="cleanExpiredRecords"> <schedule>0 0 * * *</schedule> </job> + <job name="security_deactivate_expired_users" instance="Magento\Security\Model\UserExpirationManager" method="deactivateExpiredUsers"> + <schedule>0 * * * *</schedule> + </job> </group> </config> diff --git a/app/code/Magento/Security/etc/db_schema.xml b/app/code/Magento/Security/etc/db_schema.xml index 5052f5642cb53..34bb497954a7e 100644 --- a/app/code/Magento/Security/etc/db_schema.xml +++ b/app/code/Magento/Security/etc/db_schema.xml @@ -55,4 +55,15 @@ <column name="created_at"/> </index> </table> + <table name="admin_user_expiration" resource="default" engine="innodb" comment="Admin User expiration dates table"> + <column xsi:type="int" name="user_id" padding="10" unsigned="true" nullable="false" identity="false" + comment="User ID"/> + <column xsi:type="timestamp" name="expires_at" nullable="false" default="0" comment="User Expiration Date"/> + <constraint xsi:type="primary" referenceId="PRIMARY"> + <column name="user_id"/> + </constraint> + <constraint xsi:type="foreign" referenceId="ADMIN_USER_EXPIRATION_USER_ID_ADMIN_USER_USER_ID" + table="admin_user_expiration" column="user_id" referenceTable="admin_user" + referenceColumn="user_id" onDelete="CASCADE"/> + </table> </schema> diff --git a/app/code/Magento/Security/etc/db_schema_whitelist.json b/app/code/Magento/Security/etc/db_schema_whitelist.json index c387b7591c7a5..1f8183e123956 100644 --- a/app/code/Magento/Security/etc/db_schema_whitelist.json +++ b/app/code/Magento/Security/etc/db_schema_whitelist.json @@ -33,5 +33,15 @@ "constraint": { "PRIMARY": true } + }, + "admin_user_expiration": { + "column": { + "user_id": true, + "expires_at": true + }, + "constraint": { + "PRIMARY": true, + "ADMIN_USER_EXPIRATION_USER_ID_ADMIN_USER_USER_ID": true + } } } \ No newline at end of file diff --git a/app/code/Magento/Security/etc/di.xml b/app/code/Magento/Security/etc/di.xml index 4fe88f219cf74..3b07bb84b1161 100644 --- a/app/code/Magento/Security/etc/di.xml +++ b/app/code/Magento/Security/etc/di.xml @@ -18,4 +18,5 @@ </argument> </arguments> </type> + <preference for="Magento\Security\Api\Data\UserExpirationInterface" type="Magento\Security\Model\UserExpiration"/> </config> diff --git a/app/code/Magento/Security/etc/events.xml b/app/code/Magento/Security/etc/events.xml new file mode 100644 index 0000000000000..f85cfc9387c48 --- /dev/null +++ b/app/code/Magento/Security/etc/events.xml @@ -0,0 +1,16 @@ +<?xml version="1.0"?> +<!-- +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +--> +<config xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:noNamespaceSchemaLocation="urn:magento:framework:Event/etc/events.xsd"> + <event name="admin_user_save_after"> + <observer name="add_user_expiration" instance="Magento\Security\Observer\AfterAdminUserSave"/> + </event> + <event name="admin_user_authenticate_before"> + <observer name="check_user_expiration" instance="Magento\Security\Observer\AdminUserAuthenticateBefore"/> + </event> +</config> diff --git a/app/code/Magento/Security/i18n/en_US.csv b/app/code/Magento/Security/i18n/en_US.csv index 0cf998b21a1c8..3d5bb6e8b59d4 100644 --- a/app/code/Magento/Security/i18n/en_US.csv +++ b/app/code/Magento/Security/i18n/en_US.csv @@ -26,3 +26,6 @@ None,None "Limit the number of password reset request per hour. Use 0 to disable.","Limit the number of password reset request per hour. Use 0 to disable." "Min Time Between Password Reset Requests","Min Time Between Password Reset Requests" "Delay in minutes between password reset requests. Use 0 to disable.","Delay in minutes between password reset requests. Use 0 to disable." +"""%1"" must be later than the current date.","""%1"" must be later than the current date." +"User Expiration","User Expiration" +"""%1"" is not a valid date.","""%1"" is not a valid date." diff --git a/app/code/Magento/Security/registration.php b/app/code/Magento/Security/registration.php index b5bc1db3aec6e..80cc28f8d2103 100644 --- a/app/code/Magento/Security/registration.php +++ b/app/code/Magento/Security/registration.php @@ -4,6 +4,6 @@ * See COPYING.txt for license details. */ -use \Magento\Framework\Component\ComponentRegistrar; +use Magento\Framework\Component\ComponentRegistrar; ComponentRegistrar::register(ComponentRegistrar::MODULE, 'Magento_Security', __DIR__); diff --git a/app/code/Magento/SendFriend/Controller/Product/Sendmail.php b/app/code/Magento/SendFriend/Controller/Product/Sendmail.php index 696c235899370..bff4e7edd14f6 100644 --- a/app/code/Magento/SendFriend/Controller/Product/Sendmail.php +++ b/app/code/Magento/SendFriend/Controller/Product/Sendmail.php @@ -3,31 +3,43 @@ * Copyright © Magento, Inc. All rights reserved. * See COPYING.txt for license details. */ +declare(strict_types=1); namespace Magento\SendFriend\Controller\Product; +use Magento\Catalog\Api\CategoryRepositoryInterface; +use Magento\Catalog\Api\ProductRepositoryInterface; +use Magento\Catalog\Model\Session; +use Magento\Framework\App\Action\Context; use Magento\Framework\App\Action\HttpPostActionInterface; -use Magento\Framework\App\ObjectManager; +use Magento\Framework\Controller\Result\Forward; +use Magento\Framework\Controller\Result\Redirect; +use Magento\Framework\Controller\ResultInterface; +use Magento\Framework\Data\Form\FormKey\Validator; +use Magento\Framework\Exception\LocalizedException; use Magento\Framework\Exception\NoSuchEntityException; use Magento\Framework\Controller\ResultFactory; +use Magento\Framework\Registry; +use Magento\SendFriend\Controller\Product; use Magento\SendFriend\Model\CaptchaValidator; +use Magento\SendFriend\Model\SendFriend; /** * Class Sendmail. Represents request flow logic of 'sendmail' feature * * @SuppressWarnings(PHPMD.CouplingBetweenObjects) */ -class Sendmail extends \Magento\SendFriend\Controller\Product implements HttpPostActionInterface +class Sendmail extends Product implements HttpPostActionInterface { /** - * @var \Magento\Catalog\Api\CategoryRepositoryInterface + * @var CategoryRepositoryInterface */ - protected $categoryRepository; + private $categoryRepository; /** - * @var \Magento\Catalog\Model\Session + * @var Session */ - protected $catalogSession; + private $catalogSession; /** * @var CaptchaValidator @@ -35,50 +47,49 @@ class Sendmail extends \Magento\SendFriend\Controller\Product implements HttpPos private $captchaValidator; /** - * Sendmail class construct - * - * @param \Magento\Framework\App\Action\Context $context - * @param \Magento\Framework\Registry $coreRegistry - * @param \Magento\Framework\Data\Form\FormKey\Validator $formKeyValidator - * @param \Magento\SendFriend\Model\SendFriend $sendFriend - * @param \Magento\Catalog\Api\ProductRepositoryInterface $productRepository - * @param \Magento\Catalog\Api\CategoryRepositoryInterface $categoryRepository - * @param \Magento\Catalog\Model\Session $catalogSession + * @param Context $context + * @param Registry $coreRegistry + * @param Validator $formKeyValidator + * @param SendFriend $sendFriend + * @param ProductRepositoryInterface $productRepository + * @param CategoryRepositoryInterface $categoryRepository + * @param Session $catalogSession * @param CaptchaValidator|null $captchaValidator */ public function __construct( - \Magento\Framework\App\Action\Context $context, - \Magento\Framework\Registry $coreRegistry, - \Magento\Framework\Data\Form\FormKey\Validator $formKeyValidator, - \Magento\SendFriend\Model\SendFriend $sendFriend, - \Magento\Catalog\Api\ProductRepositoryInterface $productRepository, - \Magento\Catalog\Api\CategoryRepositoryInterface $categoryRepository, - \Magento\Catalog\Model\Session $catalogSession, - CaptchaValidator $captchaValidator = null + Context $context, + Registry $coreRegistry, + Validator $formKeyValidator, + SendFriend $sendFriend, + ProductRepositoryInterface $productRepository, + CategoryRepositoryInterface $categoryRepository, + Session $catalogSession, + CaptchaValidator $captchaValidator ) { parent::__construct($context, $coreRegistry, $formKeyValidator, $sendFriend, $productRepository); $this->categoryRepository = $categoryRepository; $this->catalogSession = $catalogSession; - $this->captchaValidator = $captchaValidator ?: ObjectManager::getInstance()->create(CaptchaValidator::class); + $this->captchaValidator = $captchaValidator; } /** * Send Email Post Action * - * @return \Magento\Framework\Controller\ResultInterface + * @return ResultInterface + * * @SuppressWarnings(PHPMD.CyclomaticComplexity) * @SuppressWarnings(PHPMD.NPathComplexity) */ public function execute() { - /** @var \Magento\Framework\Controller\Result\Redirect $resultRedirect */ + /** @var Redirect $resultRedirect */ $resultRedirect = $this->resultFactory->create(ResultFactory::TYPE_REDIRECT); $product = $this->_initProduct(); $data = $this->getRequest()->getPostValue(); if (!$product || !$data) { - /** @var \Magento\Framework\Controller\Result\Forward $resultForward */ + /** @var Forward $resultForward */ $resultForward = $this->resultFactory->create(ResultFactory::TYPE_FORWARD); $resultForward->forward('noroute'); return $resultForward; @@ -108,23 +119,23 @@ public function execute() if ($validate === true) { $this->sendFriend->send(); - $this->messageManager->addSuccess(__('The link to a friend was sent.')); + $this->messageManager->addSuccessMessage(__('The link to a friend was sent.')); $url = $product->getProductUrl(); $resultRedirect->setUrl($this->_redirect->success($url)); return $resultRedirect; - } else { - if (is_array($validate)) { - foreach ($validate as $errorMessage) { - $this->messageManager->addError($errorMessage); - } - } else { - $this->messageManager->addError(__('We found some problems with the data.')); + } + + if (is_array($validate)) { + foreach ($validate as $errorMessage) { + $this->messageManager->addErrorMessage($errorMessage); } + } else { + $this->messageManager->addErrorMessage(__('We found some problems with the data.')); } - } catch (\Magento\Framework\Exception\LocalizedException $e) { - $this->messageManager->addError($e->getMessage()); + } catch (LocalizedException $e) { + $this->messageManager->addErrorMessage($e->getMessage()); } catch (\Exception $e) { - $this->messageManager->addException($e, __('Some emails were not sent.')); + $this->messageManager->addExceptionMessage($e, __('Some emails were not sent.')); } // save form data diff --git a/app/code/Magento/SendFriend/etc/adminhtml/system.xml b/app/code/Magento/SendFriend/etc/adminhtml/system.xml index 5cace4bcf92db..6360c42655a09 100644 --- a/app/code/Magento/SendFriend/etc/adminhtml/system.xml +++ b/app/code/Magento/SendFriend/etc/adminhtml/system.xml @@ -24,22 +24,37 @@ <label>Select Email Template</label> <comment>Email template chosen based on theme fallback when "Default" option is selected.</comment> <source_model>Magento\Config\Model\Config\Source\Email\Template</source_model> + <depends> + <field id="enabled">1</field> + </depends> </field> <field id="allow_guest" translate="label" type="select" sortOrder="3" showInDefault="1" showInWebsite="1" showInStore="1" canRestore="1"> <label>Allow for Guests</label> <source_model>Magento\Config\Model\Config\Source\Yesno</source_model> + <depends> + <field id="enabled">1</field> + </depends> </field> <field id="max_recipients" translate="label" type="text" sortOrder="4" showInDefault="1" showInWebsite="1" showInStore="1" canRestore="1"> <label>Max Recipients</label> <frontend_class>validate-digits</frontend_class> + <depends> + <field id="enabled">1</field> + </depends> </field> <field id="max_per_hour" translate="label" type="text" sortOrder="5" showInDefault="1" showInWebsite="1" showInStore="1" canRestore="1"> <label>Max Products Sent in 1 Hour</label> <frontend_class>validate-digits</frontend_class> + <depends> + <field id="enabled">1</field> + </depends> </field> <field id="check_by" translate="label" type="select" sortOrder="6" showInDefault="1" showInWebsite="1" showInStore="1" canRestore="1"> <label>Limit Sending By</label> <source_model>Magento\SendFriend\Model\Source\Checktype</source_model> + <depends> + <field id="enabled">1</field> + </depends> </field> </group> </section> diff --git a/app/code/Magento/SendFriend/registration.php b/app/code/Magento/SendFriend/registration.php index f8edd64000b50..37453eca71e3a 100644 --- a/app/code/Magento/SendFriend/registration.php +++ b/app/code/Magento/SendFriend/registration.php @@ -4,6 +4,6 @@ * See COPYING.txt for license details. */ -use \Magento\Framework\Component\ComponentRegistrar; +use Magento\Framework\Component\ComponentRegistrar; ComponentRegistrar::register(ComponentRegistrar::MODULE, 'Magento_SendFriend', __DIR__); diff --git a/app/code/Magento/SendFriendGraphQl/Model/Resolver/SendEmailToFriend.php b/app/code/Magento/SendFriendGraphQl/Model/Resolver/SendEmailToFriend.php index 0a4fe1e3e5616..ebc1981ca965c 100644 --- a/app/code/Magento/SendFriendGraphQl/Model/Resolver/SendEmailToFriend.php +++ b/app/code/Magento/SendFriendGraphQl/Model/Resolver/SendEmailToFriend.php @@ -48,8 +48,14 @@ public function __construct( */ public function resolve(Field $field, $context, ResolveInfo $info, array $value = null, array $args = null) { + $storeId = $context->getExtensionAttributes()->getStore()->getId(); + + if (!$this->sendFriendHelper->isEnabled($storeId)) { + throw new GraphQlInputException(__('"Email to a Friend" is not enabled.')); + } + /** @var ContextInterface $context */ - if (!$this->sendFriendHelper->isAllowForGuest() + if (!$this->sendFriendHelper->isAllowForGuest($storeId) && false === $context->getExtensionAttributes()->getIsCustomer() ) { throw new GraphQlAuthorizationException(__('The current customer isn\'t authorized.')); diff --git a/app/code/Magento/SendFriendGraphQl/Model/Resolver/SendFriendConfiguration.php b/app/code/Magento/SendFriendGraphQl/Model/Resolver/SendFriendConfiguration.php new file mode 100644 index 0000000000000..7149dccdec834 --- /dev/null +++ b/app/code/Magento/SendFriendGraphQl/Model/Resolver/SendFriendConfiguration.php @@ -0,0 +1,46 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +declare(strict_types=1); + +namespace Magento\SendFriendGraphQl\Model\Resolver; + +use Magento\Framework\GraphQl\Config\Element\Field; +use Magento\Framework\GraphQl\Query\ResolverInterface; +use Magento\Framework\GraphQl\Schema\Type\ResolveInfo; +use Magento\SendFriend\Helper\Data as SendFriendHelper; + +/** + * Resolve Store Config information for SendFriend + */ +class SendFriendConfiguration implements ResolverInterface +{ + /** + * @var SendFriendHelper + */ + private $sendFriendHelper; + + /** + * @param SendFriendHelper $sendFriendHelper + */ + public function __construct(SendFriendHelper $sendFriendHelper) + { + $this->sendFriendHelper = $sendFriendHelper; + } + + /** + * @inheritDoc + */ + public function resolve(Field $field, $context, ResolveInfo $info, array $value = null, array $args = null) + { + $store = $context->getExtensionAttributes()->getStore(); + $storeId = $store->getId(); + + return [ + 'enabled_for_customers' => $this->sendFriendHelper->isEnabled($storeId), + 'enabled_for_guests' => $this->sendFriendHelper->isAllowForGuest($storeId) + ]; + } +} diff --git a/app/code/Magento/SendFriendGraphQl/etc/schema.graphqls b/app/code/Magento/SendFriendGraphQl/etc/schema.graphqls index 1234b65a7b910..f4967779f3822 100644 --- a/app/code/Magento/SendFriendGraphQl/etc/schema.graphqls +++ b/app/code/Magento/SendFriendGraphQl/etc/schema.graphqls @@ -37,3 +37,12 @@ type SendEmailToFriendRecipient { name: String! email: String! } + +type StoreConfig { + send_friend: SendFriendConfiguration @resolver(class: "\\Magento\\SendFriendGraphQl\\Model\\Resolver\\SendFriendConfiguration") @doc(description: "Email to a Friend configuration.") +} + +type SendFriendConfiguration { + enabled_for_customers: Boolean! @doc(description: "Indicates whether the Email to a Friend feature is enabled.") + enabled_for_guests: Boolean! @doc(description: "Indicates whether the Email to a Friend feature is enabled for guests.") +} diff --git a/app/code/Magento/Shipping/Model/Config/Source/Allmethods.php b/app/code/Magento/Shipping/Model/Config/Source/Allmethods.php index e310df8ed11cb..f64c24856eba5 100644 --- a/app/code/Magento/Shipping/Model/Config/Source/Allmethods.php +++ b/app/code/Magento/Shipping/Model/Config/Source/Allmethods.php @@ -5,6 +5,9 @@ */ namespace Magento\Shipping\Model\Config\Source; +/** + * @inheritdoc + */ class Allmethods implements \Magento\Framework\Option\ArrayInterface { /** @@ -33,6 +36,7 @@ public function __construct( /** * Return array of carriers. + * * If $isActiveOnlyFlag is set to true, will return only active carriers * * @param bool $isActiveOnlyFlag @@ -56,6 +60,11 @@ public function toOptionArray($isActiveOnlyFlag = false) ); $methods[$carrierCode] = ['label' => $carrierTitle, 'value' => []]; foreach ($carrierMethods as $methodCode => $methodTitle) { + + /** Check it $carrierMethods array was well formed */ + if (!$methodCode) { + continue; + } $methods[$carrierCode]['value'][] = [ 'value' => $carrierCode . '_' . $methodCode, 'label' => '[' . $carrierCode . '] ' . $methodTitle, diff --git a/app/code/Magento/Shipping/Test/Mftf/ActionGroup/AdminAssertCreatedShipmentInShipmentsTabActionGroup.xml b/app/code/Magento/Shipping/Test/Mftf/ActionGroup/AdminAssertCreatedShipmentInShipmentsTabActionGroup.xml index 1a7d3355e4ee4..7c252ca8c14c5 100644 --- a/app/code/Magento/Shipping/Test/Mftf/ActionGroup/AdminAssertCreatedShipmentInShipmentsTabActionGroup.xml +++ b/app/code/Magento/Shipping/Test/Mftf/ActionGroup/AdminAssertCreatedShipmentInShipmentsTabActionGroup.xml @@ -13,6 +13,8 @@ <click stepKey="navigateToShipmentsTab" selector="{{AdminOrderDetailsOrderViewSection.shipments}}"/> <waitForPageLoad stepKey="waitForTabLoad"/> <grabTextFrom selector="{{AdminShipmentsGridSection.shipmentId}}" stepKey="grabShipmentId"/> - <assertNotEmpty actual="$grabShipmentId" stepKey="assertShipmentIdIsNotEmpty" after="grabShipmentId"/> + <assertNotEmpty stepKey="assertShipmentIdIsNotEmpty" after="grabShipmentId"> + <actualResult type="const">$grabShipmentId</actualResult> + </assertNotEmpty> </actionGroup> </actionGroups> diff --git a/app/code/Magento/Shipping/Test/Mftf/ActionGroup/AdminGoToShipmentTabActionGroup.xml b/app/code/Magento/Shipping/Test/Mftf/ActionGroup/AdminGoToShipmentTabActionGroup.xml new file mode 100644 index 0000000000000..ff2522d110eeb --- /dev/null +++ b/app/code/Magento/Shipping/Test/Mftf/ActionGroup/AdminGoToShipmentTabActionGroup.xml @@ -0,0 +1,15 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + /** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +--> + +<actionGroups xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/actionGroupSchema.xsd"> + <actionGroup name="AdminGoToShipmentTabActionGroup"> + <click selector="{{AdminOrderDetailsOrderViewSection.shipments}}" stepKey="clickOrderShipmentsTab"/> + <waitForLoadingMaskToDisappear stepKey="waitForShipmentTabLoad" after="clickOrderShipmentsTab"/> + </actionGroup> +</actionGroups> diff --git a/app/code/Magento/Shipping/Test/Mftf/ActionGroup/AdminResetShippingOriginConfigurationActionGroup.xml b/app/code/Magento/Shipping/Test/Mftf/ActionGroup/AdminResetShippingOriginConfigurationActionGroup.xml new file mode 100644 index 0000000000000..0bc5153915cc5 --- /dev/null +++ b/app/code/Magento/Shipping/Test/Mftf/ActionGroup/AdminResetShippingOriginConfigurationActionGroup.xml @@ -0,0 +1,19 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + /** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +--> + +<actionGroups xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/actionGroupSchema.xsd"> + <actionGroup name="AdminResetShippingOriginConfigurationActionGroup"> + <annotations> + <description>Reset Shipping Origin configurations to default</description> + </annotations> + <createData entity="AdminResetShippingOrigin" stepKey="resetShippingOriginConfig"/> + <magentoCLI command="config:set {{AdminShippingSettingsOriginStreetAddressConfigData.path}} ''" stepKey="setOriginStreetAddress"/> + <magentoCLI command="config:set {{AdminShippingSettingsOriginCityConfigData.path}} ''" stepKey="setOriginCity"/> + </actionGroup> +</actionGroups> diff --git a/app/code/Magento/Shipping/Test/Mftf/ActionGroup/AdminSetShippingOriginConfigurationActionGroup.xml b/app/code/Magento/Shipping/Test/Mftf/ActionGroup/AdminSetShippingOriginConfigurationActionGroup.xml new file mode 100644 index 0000000000000..ad92cade0b941 --- /dev/null +++ b/app/code/Magento/Shipping/Test/Mftf/ActionGroup/AdminSetShippingOriginConfigurationActionGroup.xml @@ -0,0 +1,23 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + /** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +--> + +<actionGroups xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/actionGroupSchema.xsd"> + <actionGroup name="AdminSetShippingOriginConfigurationActionGroup"> + <annotations> + <description>Set Shipping Origin configurations</description> + </annotations> + <arguments> + <argument name="address" defaultValue="US_Address_California"/> + </arguments> + <magentoCLI command="config:set {{AdminShippingSettingsOriginCountryConfigData.path}} {{address.country_id}}" stepKey="setOriginCountry"/> + <magentoCLI command="config:set {{AdminShippingSettingsOriginCityConfigData.path}} {{address.city}}" stepKey="setOriginCity"/> + <magentoCLI command="config:set {{AdminShippingSettingsOriginZipCodeConfigData.path}} {{address.postcode}}" stepKey="setOriginZipCode"/> + <magentoCLI command="config:set {{AdminShippingSettingsOriginStreetAddressConfigData.path}} '{{address.street[0]}}'" stepKey="setOriginStreetAddress"/> + </actionGroup> +</actionGroups> diff --git a/app/code/Magento/Shipping/Test/Mftf/ActionGroup/AdminShipmentActionGroup.xml b/app/code/Magento/Shipping/Test/Mftf/ActionGroup/AdminShipmentActionGroup.xml deleted file mode 100644 index 631db885ab3d9..0000000000000 --- a/app/code/Magento/Shipping/Test/Mftf/ActionGroup/AdminShipmentActionGroup.xml +++ /dev/null @@ -1,85 +0,0 @@ -<?xml version="1.0" encoding="UTF-8"?> -<!-- - /** - * Copyright © Magento, Inc. All rights reserved. - * See COPYING.txt for license details. - */ ---> - -<actionGroups xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" - xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/actionGroupSchema.xsd"> - <actionGroup name="verifyBasicShipmentInformation"> - <annotations> - <description>Validates that the provided Customer, Shipping Address, Billing Address and Customer Group are present and correct on the view Admin Order page.</description> - </annotations> - <arguments> - <argument name="customer" defaultValue=""/> - <argument name="shippingAddress" defaultValue=""/> - <argument name="billingAddress" defaultValue=""/> - <argument name="customerGroup" defaultValue="GeneralCustomerGroup"/> - </arguments> - - <see selector="{{AdminShipmentOrderInformationSection.customerName}}" userInput="{{customer.firstname}}" stepKey="seeCustomerName"/> - <see selector="{{AdminShipmentOrderInformationSection.customerEmail}}" userInput="{{customer.email}}" stepKey="seeCustomerEmail"/> - <see selector="{{AdminShipmentOrderInformationSection.customerGroup}}" userInput="{{customerGroup.code}}" stepKey="seeCustomerGroup"/> - <see selector="{{AdminShipmentAddressInformationSection.billingAddress}}" userInput="{{billingAddress.street[0]}}" stepKey="seeBillingAddressStreet"/> - <see selector="{{AdminShipmentAddressInformationSection.billingAddress}}" userInput="{{billingAddress.city}}" stepKey="seeBillingAddressCity"/> - <see selector="{{AdminShipmentAddressInformationSection.billingAddress}}" userInput="{{billingAddress.country_id}}" stepKey="seeBillingAddressCountry"/> - <see selector="{{AdminShipmentAddressInformationSection.billingAddress}}" userInput="{{billingAddress.postcode}}" stepKey="seeBillingAddressPostcode"/> - <see selector="{{AdminShipmentAddressInformationSection.shippingAddress}}" userInput="{{shippingAddress.street[0]}}" stepKey="seeShippingAddressStreet"/> - <see selector="{{AdminShipmentAddressInformationSection.shippingAddress}}" userInput="{{shippingAddress.city}}" stepKey="seeShippingAddressCity"/> - <see selector="{{AdminShipmentAddressInformationSection.shippingAddress}}" userInput="{{shippingAddress.country_id}}" stepKey="seeShippingAddressCountry"/> - <see selector="{{AdminShipmentAddressInformationSection.shippingAddress}}" userInput="{{shippingAddress.postcode}}" stepKey="seeShippingAddressPostcode"/> - </actionGroup> - - <actionGroup name="seeProductInShipmentItems"> - <annotations> - <description>Validates that the provided Product is present and correct on the view Admin Order Shipment page under the 'Items Shipped' section.</description> - </annotations> - <arguments> - <argument name="product"/> - </arguments> - - <see selector="{{AdminShipmentItemsSection.skuColumn}}" userInput="{{product.sku}}" stepKey="seeProductSkuInGrid"/> - </actionGroup> - - <actionGroup name="goToShipmentIntoOrder"> - <annotations> - <description>Clicks on the 'Ship' button on the view Admin Order page. Validates that the URL and Page Title are present and correct.</description> - </annotations> - - <click selector="{{AdminOrderDetailsMainActionsSection.ship}}" stepKey="clickShipAction"/> - <seeInCurrentUrl url="{{AdminShipmentNewPage.url}}" stepKey="seeOrderShipmentUrl"/> - <see selector="{{AdminHeaderSection.pageTitle}}" userInput="New Shipment" stepKey="seePageNameNewInvoicePage"/> - </actionGroup> - - <actionGroup name="submitShipmentIntoOrder"> - <annotations> - <description>Clicks on the 'Submit Shipment' button on the view Admin Order Shipment page. Validates that the URL and Page Title are present and correct.</description> - </annotations> - - <click selector="{{AdminShipmentMainActionsSection.submitShipment}}" stepKey="clickSubmitShipment"/> - <seeInCurrentUrl url="{{AdminOrderDetailsPage.url}}" stepKey="seeViewOrderPageShipping"/> - <see selector="{{AdminOrderDetailsMessagesSection.successMessage}}" userInput="The shipment has been created." stepKey="seeShipmentCreateSuccess"/> - </actionGroup> - <actionGroup name="AdminShipmentCreateShippingLabelActionGroup"> - <arguments> - <argument name="productName" type="string" defaultValue="{{SimpleProduct.name}}"/> - </arguments> - <waitForElementVisible selector="{{AdminShipmentCreatePackageMainSection.addProductsToPackage}}" stepKey="waitForAddProductElement"/> - <click selector="{{AdminShipmentCreatePackageMainSection.addProductsToPackage}}" stepKey="clickAddProducts"/> - <waitForElementVisible selector="{{AdminShipmentCreatePackageProductGridSection.concreteProductCheckbox('productName')}}" stepKey="waitForProductBeVisible"/> - <checkOption selector="{{AdminShipmentCreatePackageProductGridSection.concreteProductCheckbox('productName')}}" stepKey="checkProductCheckbox"/> - <waitForElementVisible selector="{{AdminShipmentCreatePackageMainSection.addSelectedProductToPackage}}" stepKey="waitForAddSelectedProductElement"/> - <click selector="{{AdminShipmentCreatePackageMainSection.addSelectedProductToPackage}}" stepKey="clickAddSelectedProduct"/> - <waitForElementNotVisible selector="{{AdminShipmentCreatePackageMainSection.saveButtonDisabled}}" stepKey="waitForBeEnabled"/> - <click selector="{{AdminShipmentCreatePackageMainSection.save}}" stepKey="clickSave"/> - <waitForLoadingMaskToDisappear stepKey="waitForLoadingMaskDisappear"/> - <waitForPageLoad stepKey="waitForSaving"/> - <see selector="{{AdminOrderDetailsMessagesSection.successMessage}}" userInput="The shipment has been created. You created the shipping label." stepKey="seeShipmentCreateSuccess"/> - </actionGroup> - <actionGroup name="AdminGoToShipmentTabActionGroup"> - <click selector="{{AdminOrderDetailsOrderViewSection.shipments}}" stepKey="clickOrderShipmentsTab"/> - <waitForLoadingMaskToDisappear stepKey="waitForShipmentTabLoad" after="clickOrderShipmentsTab"/> - </actionGroup> -</actionGroups> diff --git a/app/code/Magento/Shipping/Test/Mftf/ActionGroup/AdminShipmentCreateShippingLabelActionGroup.xml b/app/code/Magento/Shipping/Test/Mftf/ActionGroup/AdminShipmentCreateShippingLabelActionGroup.xml new file mode 100644 index 0000000000000..663b6088395c4 --- /dev/null +++ b/app/code/Magento/Shipping/Test/Mftf/ActionGroup/AdminShipmentCreateShippingLabelActionGroup.xml @@ -0,0 +1,27 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + /** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +--> + +<actionGroups xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/actionGroupSchema.xsd"> + <actionGroup name="AdminShipmentCreateShippingLabelActionGroup"> + <arguments> + <argument name="productName" type="string" defaultValue="{{SimpleProduct.name}}"/> + </arguments> + <waitForElementVisible selector="{{AdminShipmentCreatePackageMainSection.addProductsToPackage}}" stepKey="waitForAddProductElement"/> + <click selector="{{AdminShipmentCreatePackageMainSection.addProductsToPackage}}" stepKey="clickAddProducts"/> + <waitForElementVisible selector="{{AdminShipmentCreatePackageProductGridSection.concreteProductCheckbox('productName')}}" stepKey="waitForProductBeVisible"/> + <checkOption selector="{{AdminShipmentCreatePackageProductGridSection.concreteProductCheckbox('productName')}}" stepKey="checkProductCheckbox"/> + <waitForElementVisible selector="{{AdminShipmentCreatePackageMainSection.addSelectedProductToPackage}}" stepKey="waitForAddSelectedProductElement"/> + <click selector="{{AdminShipmentCreatePackageMainSection.addSelectedProductToPackage}}" stepKey="clickAddSelectedProduct"/> + <waitForElementNotVisible selector="{{AdminShipmentCreatePackageMainSection.saveButtonDisabled}}" stepKey="waitForBeEnabled"/> + <click selector="{{AdminShipmentCreatePackageMainSection.save}}" stepKey="clickSave"/> + <waitForLoadingMaskToDisappear stepKey="waitForLoadingMaskDisappear"/> + <waitForPageLoad stepKey="waitForSaving"/> + <see selector="{{AdminOrderDetailsMessagesSection.successMessage}}" userInput="The shipment has been created. You created the shipping label." stepKey="seeShipmentCreateSuccess"/> + </actionGroup> +</actionGroups> diff --git a/app/code/Magento/Shipping/Test/Mftf/ActionGroup/GoToShipmentIntoOrderActionGroup.xml b/app/code/Magento/Shipping/Test/Mftf/ActionGroup/GoToShipmentIntoOrderActionGroup.xml new file mode 100644 index 0000000000000..7a4280cf7d6d5 --- /dev/null +++ b/app/code/Magento/Shipping/Test/Mftf/ActionGroup/GoToShipmentIntoOrderActionGroup.xml @@ -0,0 +1,20 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + /** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +--> + +<actionGroups xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/actionGroupSchema.xsd"> + <actionGroup name="GoToShipmentIntoOrderActionGroup"> + <annotations> + <description>Clicks on the 'Ship' button on the view Admin Order page. Validates that the URL and Page Title are present and correct.</description> + </annotations> + + <click selector="{{AdminOrderDetailsMainActionsSection.ship}}" stepKey="clickShipAction"/> + <seeInCurrentUrl url="{{AdminShipmentNewPage.url}}" stepKey="seeOrderShipmentUrl"/> + <see selector="{{AdminHeaderSection.pageTitle}}" userInput="New Shipment" stepKey="seePageNameNewInvoicePage"/> + </actionGroup> +</actionGroups> diff --git a/app/code/Magento/Shipping/Test/Mftf/ActionGroup/SeeProductInShipmentItemsActionGroup.xml b/app/code/Magento/Shipping/Test/Mftf/ActionGroup/SeeProductInShipmentItemsActionGroup.xml new file mode 100644 index 0000000000000..8ba2eff4def0f --- /dev/null +++ b/app/code/Magento/Shipping/Test/Mftf/ActionGroup/SeeProductInShipmentItemsActionGroup.xml @@ -0,0 +1,21 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + /** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +--> + +<actionGroups xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/actionGroupSchema.xsd"> + <actionGroup name="SeeProductInShipmentItemsActionGroup"> + <annotations> + <description>Validates that the provided Product is present and correct on the view Admin Order Shipment page under the 'Items Shipped' section.</description> + </annotations> + <arguments> + <argument name="product"/> + </arguments> + + <see selector="{{AdminShipmentItemsSection.skuColumn}}" userInput="{{product.sku}}" stepKey="seeProductSkuInGrid"/> + </actionGroup> +</actionGroups> diff --git a/app/code/Magento/Shipping/Test/Mftf/ActionGroup/SubmitShipmentIntoOrderActionGroup.xml b/app/code/Magento/Shipping/Test/Mftf/ActionGroup/SubmitShipmentIntoOrderActionGroup.xml new file mode 100644 index 0000000000000..8592566ff83b7 --- /dev/null +++ b/app/code/Magento/Shipping/Test/Mftf/ActionGroup/SubmitShipmentIntoOrderActionGroup.xml @@ -0,0 +1,20 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + /** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +--> + +<actionGroups xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/actionGroupSchema.xsd"> + <actionGroup name="SubmitShipmentIntoOrderActionGroup"> + <annotations> + <description>Clicks on the 'Submit Shipment' button on the view Admin Order Shipment page. Validates that the URL and Page Title are present and correct.</description> + </annotations> + + <click selector="{{AdminShipmentMainActionsSection.submitShipment}}" stepKey="clickSubmitShipment"/> + <seeInCurrentUrl url="{{AdminOrderDetailsPage.url}}" stepKey="seeViewOrderPageShipping"/> + <see selector="{{AdminOrderDetailsMessagesSection.successMessage}}" userInput="The shipment has been created." stepKey="seeShipmentCreateSuccess"/> + </actionGroup> +</actionGroups> diff --git a/app/code/Magento/Shipping/Test/Mftf/ActionGroup/VerifyBasicShipmentInformationActionGroup.xml b/app/code/Magento/Shipping/Test/Mftf/ActionGroup/VerifyBasicShipmentInformationActionGroup.xml new file mode 100644 index 0000000000000..d76ba42003b81 --- /dev/null +++ b/app/code/Magento/Shipping/Test/Mftf/ActionGroup/VerifyBasicShipmentInformationActionGroup.xml @@ -0,0 +1,34 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + /** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +--> + +<actionGroups xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/actionGroupSchema.xsd"> + <actionGroup name="VerifyBasicShipmentInformationActionGroup"> + <annotations> + <description>Validates that the provided Customer, Shipping Address, Billing Address and Customer Group are present and correct on the view Admin Order page.</description> + </annotations> + <arguments> + <argument name="customer" defaultValue=""/> + <argument name="shippingAddress" defaultValue=""/> + <argument name="billingAddress" defaultValue=""/> + <argument name="customerGroup" defaultValue="GeneralCustomerGroup"/> + </arguments> + + <see selector="{{AdminShipmentOrderInformationSection.customerName}}" userInput="{{customer.firstname}}" stepKey="seeCustomerName"/> + <see selector="{{AdminShipmentOrderInformationSection.customerEmail}}" userInput="{{customer.email}}" stepKey="seeCustomerEmail"/> + <see selector="{{AdminShipmentOrderInformationSection.customerGroup}}" userInput="{{customerGroup.code}}" stepKey="seeCustomerGroup"/> + <see selector="{{AdminShipmentAddressInformationSection.billingAddress}}" userInput="{{billingAddress.street[0]}}" stepKey="seeBillingAddressStreet"/> + <see selector="{{AdminShipmentAddressInformationSection.billingAddress}}" userInput="{{billingAddress.city}}" stepKey="seeBillingAddressCity"/> + <see selector="{{AdminShipmentAddressInformationSection.billingAddress}}" userInput="{{billingAddress.country_id}}" stepKey="seeBillingAddressCountry"/> + <see selector="{{AdminShipmentAddressInformationSection.billingAddress}}" userInput="{{billingAddress.postcode}}" stepKey="seeBillingAddressPostcode"/> + <see selector="{{AdminShipmentAddressInformationSection.shippingAddress}}" userInput="{{shippingAddress.street[0]}}" stepKey="seeShippingAddressStreet"/> + <see selector="{{AdminShipmentAddressInformationSection.shippingAddress}}" userInput="{{shippingAddress.city}}" stepKey="seeShippingAddressCity"/> + <see selector="{{AdminShipmentAddressInformationSection.shippingAddress}}" userInput="{{shippingAddress.country_id}}" stepKey="seeShippingAddressCountry"/> + <see selector="{{AdminShipmentAddressInformationSection.shippingAddress}}" userInput="{{shippingAddress.postcode}}" stepKey="seeShippingAddressPostcode"/> + </actionGroup> +</actionGroups> diff --git a/app/code/Magento/Shipping/Test/Mftf/Data/AdminShippingSettingsConfigData.xml b/app/code/Magento/Shipping/Test/Mftf/Data/AdminShippingSettingsConfigData.xml index 342472aab6f42..789be5aeb8784 100644 --- a/app/code/Magento/Shipping/Test/Mftf/Data/AdminShippingSettingsConfigData.xml +++ b/app/code/Magento/Shipping/Test/Mftf/Data/AdminShippingSettingsConfigData.xml @@ -33,4 +33,18 @@ <data key="hundred">100</data> <data key="default">0</data> </entity> + <entity name="AdminResetShippingOrigin" type="shipping_origin"> + <requiredEntity type="inheritCountryFlag">ShippingOriginCountryInheritValue</requiredEntity> + <requiredEntity type="inheritRegionFlag">ShippingOriginRegionInheritValue</requiredEntity> + <requiredEntity type="inheritPostcodeFlag">ShippingOriginPostcodeInheritValue</requiredEntity> + </entity> + <entity name="ShippingOriginCountryInheritValue" type="inheritCountryFlag"> + <data key="value">1</data> + </entity> + <entity name="ShippingOriginRegionInheritValue" type="inheritRegionFlag"> + <data key="value">1</data> + </entity> + <entity name="ShippingOriginPostcodeInheritValue" type="inheritPostcodeFlag"> + <data key="value">1</data> + </entity> </entities> diff --git a/app/code/Magento/Shipping/Test/Mftf/Metadata/shipping_methods-meta.xml b/app/code/Magento/Shipping/Test/Mftf/Metadata/ShippingMethodsMeta.xml similarity index 100% rename from app/code/Magento/Shipping/Test/Mftf/Metadata/shipping_methods-meta.xml rename to app/code/Magento/Shipping/Test/Mftf/Metadata/ShippingMethodsMeta.xml diff --git a/app/code/Magento/Shipping/Test/Mftf/Metadata/ShippingOriginMeta.xml b/app/code/Magento/Shipping/Test/Mftf/Metadata/ShippingOriginMeta.xml new file mode 100644 index 0000000000000..e0f5e7afc4a9d --- /dev/null +++ b/app/code/Magento/Shipping/Test/Mftf/Metadata/ShippingOriginMeta.xml @@ -0,0 +1,32 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + /** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +--> +<operations xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="urn:magento:mftf:DataGenerator/etc/dataOperation.xsd"> + <operation name="ShippingOriginSetup" dataType="shipping_origin" type="create" auth="adminFormKey" url="/admin/system_config/save/section/shipping/" method="POST" successRegex="/messages-message-success/"> + <object key="groups" dataType="shipping_origin"> + <object key="origin" dataType="shipping_origin"> + <object key="fields" dataType="shipping_origin"> + <object key="country_id" dataType="shipping_origin"> + <object key="inherit" dataType="inheritCountryFlag"> + <field key="value">integer</field> + </object> + </object> + <object key="region_id" dataType="shipping_origin"> + <object key="inherit" dataType="inheritRegionFlag"> + <field key="value">integer</field> + </object> + </object> + <object key="postcode" dataType="shipping_origin"> + <object key="inherit" dataType="inheritPostcodeFlag"> + <field key="value">integer</field> + </object> + </object> + </object> + </object> + </object> + </operation> +</operations> diff --git a/app/code/Magento/Shipping/Test/Mftf/Page/AdminShipmentNewPage.xml b/app/code/Magento/Shipping/Test/Mftf/Page/AdminShipmentNewPage.xml index 597abb5694e30..4ccda7ae7f2b1 100644 --- a/app/code/Magento/Shipping/Test/Mftf/Page/AdminShipmentNewPage.xml +++ b/app/code/Magento/Shipping/Test/Mftf/Page/AdminShipmentNewPage.xml @@ -8,7 +8,7 @@ <pages xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="urn:magento:mftf:Page/etc/PageObject.xsd"> - <page name="AdminShipmentNewPage" url="order_shipment/new/order_id/" area="admin" module="Shipping"> + <page name="AdminShipmentNewPage" url="admin/order_shipment/new/order_id/" area="admin" module="Shipping"> <section name="AdminShipmentMainActionsSection"/> <section name="AdminShipmentOrderInformationSection"/> <section name="AdminShipmentAddressInformationSection"/> @@ -16,4 +16,4 @@ <section name="AdminShipmentItemsSection"/> <section name="AdminShipmentTotalSection"/> </page> -</pages> \ No newline at end of file +</pages> diff --git a/app/code/Magento/Shipping/Test/Mftf/Section/AdminShipmentAddressInformationSection.xml b/app/code/Magento/Shipping/Test/Mftf/Section/AdminShipmentAddressInformationSection.xml index 10878310c262f..61ffedae88c57 100644 --- a/app/code/Magento/Shipping/Test/Mftf/Section/AdminShipmentAddressInformationSection.xml +++ b/app/code/Magento/Shipping/Test/Mftf/Section/AdminShipmentAddressInformationSection.xml @@ -7,7 +7,7 @@ --> <sections xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" - xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/testSchema.xsd"> + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Page/etc/SectionObject.xsd"> <section name="AdminShipmentAddressInformationSection"> <element name="billingAddress" type="text" selector=".order-billing-address address"/> <element name="billingAddressEdit" type="button" selector=".order-billing-address .actions a"/> diff --git a/app/code/Magento/Shipping/Test/Mftf/Section/AdminShipmentCreatePackageSection.xml b/app/code/Magento/Shipping/Test/Mftf/Section/AdminShipmentCreatePackageSection.xml deleted file mode 100644 index 5f33921b5a44f..0000000000000 --- a/app/code/Magento/Shipping/Test/Mftf/Section/AdminShipmentCreatePackageSection.xml +++ /dev/null @@ -1,20 +0,0 @@ -<?xml version="1.0" encoding="UTF-8"?> -<!-- - /** - * Copyright © Magento, Inc. All rights reserved. - * See COPYING.txt for license details. - */ ---> - -<sections xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" - xsi:noNamespaceSchemaLocation="urn:magento:mftf:Page/etc/SectionObject.xsd"> - <section name="AdminShipmentCreatePackageMainSection"> - <element name="addProductsToPackage" type="button" selector="#package_block_1 button[data-action='package-add-items']"/> - <element name="addSelectedProductToPackage" type="button" selector="#package_block_1 button[data-action='package-save-items']"/> - <element name="save" type="button" selector="button[data-action='save-packages']"/> - <element name="saveButtonDisabled" type="button" selector="button[data-action='save-packages']._disabled"/> - </section> - <section name="AdminShipmentCreatePackageProductGridSection"> - <element name="concreteProductCheckbox" type="checkbox" selector="//td[contains(text(), '{{productName}}')]/parent::tr//input[contains(@class,'checkbox')]" parameterized="true"/> - </section> -</sections> diff --git a/app/code/Magento/Shipping/Test/Mftf/Section/AdminShipmentCreatePackageSection/AdminShipmentCreatePackageMainSection.xml b/app/code/Magento/Shipping/Test/Mftf/Section/AdminShipmentCreatePackageSection/AdminShipmentCreatePackageMainSection.xml new file mode 100644 index 0000000000000..13a9ca2214b3e --- /dev/null +++ b/app/code/Magento/Shipping/Test/Mftf/Section/AdminShipmentCreatePackageSection/AdminShipmentCreatePackageMainSection.xml @@ -0,0 +1,16 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + /** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +--> +<sections xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Page/etc/SectionObject.xsd"> + <section name="AdminShipmentCreatePackageMainSection"> + <element name="addProductsToPackage" type="button" selector="#package_block_1 button[data-action='package-add-items']"/> + <element name="addSelectedProductToPackage" type="button" selector="#package_block_1 button[data-action='package-save-items']"/> + <element name="save" type="button" selector="button[data-action='save-packages']"/> + <element name="saveButtonDisabled" type="button" selector="button[data-action='save-packages']._disabled"/> + </section> +</sections> diff --git a/app/code/Magento/Shipping/Test/Mftf/Section/AdminShipmentCreatePackageSection/AdminShipmentCreatePackageProductGridSection.xml b/app/code/Magento/Shipping/Test/Mftf/Section/AdminShipmentCreatePackageSection/AdminShipmentCreatePackageProductGridSection.xml new file mode 100644 index 0000000000000..3597258d503c7 --- /dev/null +++ b/app/code/Magento/Shipping/Test/Mftf/Section/AdminShipmentCreatePackageSection/AdminShipmentCreatePackageProductGridSection.xml @@ -0,0 +1,13 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + /** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +--> +<sections xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Page/etc/SectionObject.xsd"> + <section name="AdminShipmentCreatePackageProductGridSection"> + <element name="concreteProductCheckbox" type="checkbox" selector="//td[contains(text(), '{{productName}}')]/parent::tr//input[contains(@class,'checkbox')]" parameterized="true"/> + </section> +</sections> diff --git a/app/code/Magento/Shipping/Test/Mftf/Test/AdminCheckInputFieldsDisabledAfterAppConfigDumpTest.xml b/app/code/Magento/Shipping/Test/Mftf/Test/AdminCheckInputFieldsDisabledAfterAppConfigDumpTest.xml index 0b7ddd0cfa781..188b12c6a91c3 100644 --- a/app/code/Magento/Shipping/Test/Mftf/Test/AdminCheckInputFieldsDisabledAfterAppConfigDumpTest.xml +++ b/app/code/Magento/Shipping/Test/Mftf/Test/AdminCheckInputFieldsDisabledAfterAppConfigDumpTest.xml @@ -20,10 +20,10 @@ <group value="configuration"/> </annotations> <before> - <actionGroup ref="LoginAsAdmin" stepKey="loginAsAdmin"/> + <actionGroup ref="AdminLoginActionGroup" stepKey="loginAsAdmin"/> </before> <after> - <actionGroup ref="logout" stepKey="logout"/> + <actionGroup ref="AdminLogoutActionGroup" stepKey="logout"/> </after> <!--Assert configuration are disabled in Flat Rate section--> <comment userInput="Assert configuration are disabled in Flat Rate section" stepKey="commentSeeDisabledFlatRateConfigs"/> @@ -31,54 +31,117 @@ <conditionalClick selector="{{AdminShippingMethodFlatRateSection.carriersFlatRateTab}}" dependentSelector="{{AdminShippingMethodFlatRateSection.carriersFlatRateActive}}" visible="false" stepKey="expandFlatRateTab"/> <waitForElementVisible selector="{{AdminShippingMethodFlatRateSection.carriersEnableFlatRateActive}}" stepKey="waitForFlatRateTabOpen"/> <grabAttributeFrom selector="{{AdminShippingMethodFlatRateSection.carriersEnableFlatRateActive}}" userInput="disabled" stepKey="grabFlatRateActiveDisabled"/> - <assertEquals expected='true' expectedType="string" actual="$grabFlatRateActiveDisabled" stepKey="assertFlatRateActiveDisabled"/> + <assertEquals stepKey="assertFlatRateActiveDisabled"> + <actualResult type="const">$grabFlatRateActiveDisabled</actualResult> + <expectedResult type="string">true</expectedResult> + </assertEquals> <grabAttributeFrom selector="{{AdminShippingMethodFlatRateSection.carriersFlatRateTitle}}" userInput="disabled" stepKey="grabFlatRateTitleDisabled"/> - <assertEquals expected='true' expectedType="string" actual="$grabFlatRateTitleDisabled" stepKey="assertFlatRateTitleDisabled"/> + <assertEquals stepKey="assertFlatRateTitleDisabled"> + <actualResult type="const">$grabFlatRateTitleDisabled</actualResult> + <expectedResult type="string">true</expectedResult> + </assertEquals> <grabAttributeFrom selector="{{AdminShippingMethodFlatRateSection.carriersFlatRateName}}" userInput="disabled" stepKey="grabFlatRateNameDisabled"/> - <assertEquals expected='true' expectedType="string" actual="$grabFlatRateNameDisabled" stepKey="assertFlatRateNameDisabled"/> + <assertEquals stepKey="assertFlatRateNameDisabled"> + <actualResult type="const">$grabFlatRateNameDisabled</actualResult> + <expectedResult type="string">true</expectedResult> + </assertEquals> <grabAttributeFrom selector="{{AdminShippingMethodFlatRateSection.carriersFlatRateSpecificErrMsg}}" userInput="disabled" stepKey="grabFlatRateSpecificErrMsgDisabled"/> - <assertEquals expected='true' expectedType="string" actual="$grabFlatRateSpecificErrMsgDisabled" stepKey="assertFlatRateSpecificErrMsgDisabled"/> + <assertEquals stepKey="assertFlatRateSpecificErrMsgDisabled"> + <actualResult type="const">$grabFlatRateSpecificErrMsgDisabled</actualResult> + <expectedResult type="string">true</expectedResult> + </assertEquals> <grabAttributeFrom selector="{{AdminShippingMethodFlatRateSection.carriersFlatRateAllowSpecific}}" userInput="disabled" stepKey="grabFlatRateAllowSpecificDisabled"/> - <assertEquals expected='true' expectedType="string" actual="$grabFlatRateAllowSpecificDisabled" stepKey="assertFlatRateAllowSpecificDisabled"/> + <assertEquals stepKey="assertFlatRateAllowSpecificDisabled"> + <actualResult type="const">$grabFlatRateAllowSpecificDisabled</actualResult> + <expectedResult type="string">true</expectedResult> + </assertEquals> <grabAttributeFrom selector="{{AdminShippingMethodFlatRateSection.carriersFlatRateSpecificCountry}}" userInput="disabled" stepKey="grabFlatRateSpecificCountryDisabled"/> - <assertEquals expected='true' expectedType="string" actual="$grabFlatRateSpecificCountryDisabled" stepKey="assertFlatRateSpecificCountryDisabled"/> + <assertEquals stepKey="assertFlatRateSpecificCountryDisabled"> + <actualResult type="const">$grabFlatRateSpecificCountryDisabled</actualResult> + <expectedResult type="string">true</expectedResult> + </assertEquals> <!--Assert configuration are disabled in Free Shipping section--> <comment userInput="Assert configuration are disabled in Free Shipping section" stepKey="commentSeeDisabledFreeShippingConfigs"/> <conditionalClick selector="{{AdminShippingMethodFreeShippingSection.carriersFreeShippingSectionHead}}" dependentSelector="{{AdminShippingMethodFreeShippingSection.carriersFreeShippingActive}}" visible="false" stepKey="expandFreeShippingTab"/> <waitForElementVisible selector="{{AdminShippingMethodFreeShippingSection.carriersFreeShippingActive}}" stepKey="waitForFreeShippingTabOpen"/> <grabAttributeFrom selector="{{AdminShippingMethodFreeShippingSection.carriersFreeShippingActive}}" userInput="disabled" stepKey="grabFreeShippingActiveDisabled"/> - <assertEquals expected='true' expectedType="string" actual="$grabFreeShippingActiveDisabled" stepKey="assertFreeShippingActiveDisabled"/> + <assertEquals stepKey="assertFreeShippingActiveDisabled"> + <actualResult type="const">$grabFreeShippingActiveDisabled</actualResult> + <expectedResult type="string">true</expectedResult> + </assertEquals> <grabAttributeFrom selector="{{AdminShippingMethodFreeShippingSection.carriersFreeShippingTitle}}" userInput="disabled" stepKey="grabFreeShippingTitleDisabled"/> - <assertEquals expected='true' expectedType="string" actual="$grabFreeShippingTitleDisabled" stepKey="assertFreeShippingTitleDisabled"/> + <assertEquals stepKey="assertFreeShippingTitleDisabled"> + <actualResult type="const">$grabFreeShippingTitleDisabled</actualResult> + <expectedResult type="string">true</expectedResult> + </assertEquals> <grabAttributeFrom selector="{{AdminShippingMethodFreeShippingSection.carriersFreeShippingName}}" userInput="disabled" stepKey="grabFreeShippingNameDisabled"/> - <assertEquals expected='true' expectedType="string" actual="$grabFreeShippingNameDisabled" stepKey="assertFreeShippingNameDisabled"/> + <assertEquals stepKey="assertFreeShippingNameDisabled"> + <actualResult type="const">$grabFreeShippingNameDisabled</actualResult> + <expectedResult type="string">true</expectedResult> + </assertEquals> <grabAttributeFrom selector="{{AdminShippingMethodFreeShippingSection.carriersFreeShippingSpecificErrMsg}}" userInput="disabled" stepKey="grabFreeShippingSpecificErrMsgDisabled"/> - <assertEquals expected='true' expectedType="string" actual="$grabFreeShippingSpecificErrMsgDisabled" stepKey="assertFreeShippingSpecificErrMsgDisabled"/> + <assertEquals stepKey="assertFreeShippingSpecificErrMsgDisabled"> + <actualResult type="const">$grabFreeShippingSpecificErrMsgDisabled</actualResult> + <expectedResult type="string">true</expectedResult> + </assertEquals> <grabAttributeFrom selector="{{AdminShippingMethodFreeShippingSection.carriersFreeShippingAllowSpecific}}" userInput="disabled" stepKey="grabFreeShippingAllowSpecificDisabled"/> - <assertEquals expected='true' expectedType="string" actual="$grabFreeShippingAllowSpecificDisabled" stepKey="assertFreeShippingAllowSpecificDisabled"/> + <assertEquals stepKey="assertFreeShippingAllowSpecificDisabled"> + <actualResult type="const">$grabFreeShippingAllowSpecificDisabled</actualResult> + <expectedResult type="string">true</expectedResult> + </assertEquals> <grabAttributeFrom selector="{{AdminShippingMethodFreeShippingSection.carriersFreeShippingSpecificCountry}}" userInput="disabled" stepKey="grabFreeShippingSpecificCountryDisabled"/> - <assertEquals expected='true' expectedType="string" actual="$grabFreeShippingSpecificCountryDisabled" stepKey="assertFreeShippingSpecificCountryDisabled"/> + <assertEquals stepKey="assertFreeShippingSpecificCountryDisabled"> + <actualResult type="const">$grabFreeShippingSpecificCountryDisabled</actualResult> + <expectedResult type="string">true</expectedResult> + </assertEquals> <!--Assert configuration are disabled in Table Rates section--> <comment userInput="Assert configuration are disabled in Table Rates section" stepKey="commentSeeDisabledTableRatesConfigs"/> <conditionalClick selector="{{AdminShippingMethodTableRatesSection.carriersTableRateTab}}" dependentSelector="{{AdminShippingMethodTableRatesSection.carriersTableRateActive}}" visible="false" stepKey="expandTableRateTab"/> <waitForElementVisible selector="{{AdminShippingMethodTableRatesSection.enabledUseSystemValue}}" stepKey="waitForTableRateTabOpen"/> <grabAttributeFrom selector="{{AdminShippingMethodTableRatesSection.enabledUseSystemValue}}" userInput="disabled" stepKey="grabTableRateActiveDisabled"/> - <assertEquals expected='true' expectedType="string" actual="$grabTableRateActiveDisabled" stepKey="assertTableRateActiveDisabled"/> + <assertEquals stepKey="assertTableRateActiveDisabled"> + <actualResult type="const">$grabTableRateActiveDisabled</actualResult> + <expectedResult type="string">true</expectedResult> + </assertEquals> <grabAttributeFrom selector="{{AdminShippingMethodTableRatesSection.carriersTableRateTitle}}" userInput="disabled" stepKey="grabTableRateTitleDisabled"/> - <assertEquals expected='true' expectedType="string" actual="$grabTableRateTitleDisabled" stepKey="assertTableRateTitleDisabled"/> + <assertEquals stepKey="assertTableRateTitleDisabled"> + <actualResult type="const">$grabTableRateTitleDisabled</actualResult> + <expectedResult type="string">true</expectedResult> + </assertEquals> <grabAttributeFrom selector="{{AdminShippingMethodTableRatesSection.carriersTableRateName}}" userInput="disabled" stepKey="grabTableRateNameDisabled"/> - <assertEquals expected='true' expectedType="string" actual="$grabTableRateNameDisabled" stepKey="assertTableRateNameDisabled"/> + <assertEquals stepKey="assertTableRateNameDisabled"> + <actualResult type="const">$grabTableRateNameDisabled</actualResult> + <expectedResult type="string">true</expectedResult> + </assertEquals> <grabAttributeFrom selector="{{AdminShippingMethodTableRatesSection.carriersTableRateConditionName}}" userInput="disabled" stepKey="grabTableRateConditionNameDisabled"/> - <assertEquals expected='true' expectedType="string" actual="$grabTableRateConditionNameDisabled" stepKey="assertTableRateConditionNameDisabled"/> + <assertEquals stepKey="assertTableRateConditionNameDisabled"> + <actualResult type="const">$grabTableRateConditionNameDisabled</actualResult> + <expectedResult type="string">true</expectedResult> + </assertEquals> <grabAttributeFrom selector="{{AdminShippingMethodTableRatesSection.carriersTableRateIncludeVirtualPrice}}" userInput="disabled" stepKey="grabTableRateIncludeVirtualPriceDisabled"/> - <assertEquals expected='true' expectedType="string" actual="$grabTableRateIncludeVirtualPriceDisabled" stepKey="assertTableRateIncludeVirtualPriceDisabled"/> + <assertEquals stepKey="assertTableRateIncludeVirtualPriceDisabled"> + <actualResult type="const">$grabTableRateIncludeVirtualPriceDisabled</actualResult> + <expectedResult type="string">true</expectedResult> + </assertEquals> <grabAttributeFrom selector="{{AdminShippingMethodTableRatesSection.carriersTableRateHandlingType}}" userInput="disabled" stepKey="grabTableRateHandlingTypeDisabled"/> - <assertEquals expected='true' expectedType="string" actual="$grabTableRateHandlingTypeDisabled" stepKey="assertTableRateHandlingTypeDisabled"/> + <assertEquals stepKey="assertTableRateHandlingTypeDisabled"> + <actualResult type="const">$grabTableRateHandlingTypeDisabled</actualResult> + <expectedResult type="string">true</expectedResult> + </assertEquals> <grabAttributeFrom selector="{{AdminShippingMethodTableRatesSection.carriersTableRateSpecificErrMsg}}" userInput="disabled" stepKey="grabTableRateSpecificErrMsgDisabled"/> - <assertEquals expected='true' expectedType="string" actual="$grabTableRateSpecificErrMsgDisabled" stepKey="assertTableRateSpecificErrMsgDisabled"/> + <assertEquals stepKey="assertTableRateSpecificErrMsgDisabled"> + <actualResult type="const">$grabTableRateSpecificErrMsgDisabled</actualResult> + <expectedResult type="string">true</expectedResult> + </assertEquals> <grabAttributeFrom selector="{{AdminShippingMethodTableRatesSection.carriersTableRateAllowSpecific}}" userInput="disabled" stepKey="grabTableRateAllowSpecificDisabled"/> - <assertEquals expected='true' expectedType="string" actual="$grabTableRateAllowSpecificDisabled" stepKey="assertTableRateAllowSpecificDisabled"/> + <assertEquals stepKey="assertTableRateAllowSpecificDisabled"> + <actualResult type="const">$grabTableRateAllowSpecificDisabled</actualResult> + <expectedResult type="string">true</expectedResult> + </assertEquals> <grabAttributeFrom selector="{{AdminShippingMethodTableRatesSection.carriersTableRateSpecificCountry}}" userInput="disabled" stepKey="grabTableRateSpecificCountryDisabled"/> - <assertEquals expected='true' expectedType="string" actual="$grabTableRateSpecificCountryDisabled" stepKey="assertTableRateSpecificCountryDisabled"/> + <assertEquals stepKey="assertTableRateSpecificCountryDisabled"> + <actualResult type="const">$grabTableRateSpecificCountryDisabled</actualResult> + <expectedResult type="string">true</expectedResult> + </assertEquals> </test> </tests> diff --git a/app/code/Magento/Shipping/Test/Mftf/Test/AdminCheckTheConfirmationPopupTest.xml b/app/code/Magento/Shipping/Test/Mftf/Test/AdminCheckTheConfirmationPopupTest.xml index 87058245c6014..fea499954896d 100644 --- a/app/code/Magento/Shipping/Test/Mftf/Test/AdminCheckTheConfirmationPopupTest.xml +++ b/app/code/Magento/Shipping/Test/Mftf/Test/AdminCheckTheConfirmationPopupTest.xml @@ -19,12 +19,12 @@ <before> <createData entity="Simple_US_Customer" stepKey="createCustomer"/> <createData entity="SimpleProduct2" stepKey="createSimpleProduct"/> - <actionGroup ref="LoginAsAdmin" stepKey="LoginAsAdmin"/> + <actionGroup ref="AdminLoginActionGroup" stepKey="LoginAsAdmin"/> </before> <after> <deleteData createDataKey="createCustomer" stepKey="deleteCustomer"/> <deleteData createDataKey="createSimpleProduct" stepKey="deleteSimpleProduct"/> - <actionGroup ref="logout" stepKey="logout"/> + <actionGroup ref="AdminLogoutActionGroup" stepKey="logout"/> </after> <actionGroup ref="CreateOrderActionGroup" stepKey="goToCreateOrderPage"> <argument name="customer" value="$$createCustomer$$"/> diff --git a/app/code/Magento/Shipping/Test/Mftf/Test/AdminCreateOrderCustomStoreShippingMethodTableRatesTest.xml b/app/code/Magento/Shipping/Test/Mftf/Test/AdminCreateOrderCustomStoreShippingMethodTableRatesTest.xml index 7784fdd31e58e..0e69dba36d41c 100644 --- a/app/code/Magento/Shipping/Test/Mftf/Test/AdminCreateOrderCustomStoreShippingMethodTableRatesTest.xml +++ b/app/code/Magento/Shipping/Test/Mftf/Test/AdminCreateOrderCustomStoreShippingMethodTableRatesTest.xml @@ -22,7 +22,7 @@ <before> <!--Create product and customer--> <createData entity="SimpleProduct2" stepKey="createProduct"/> - <actionGroup ref="LoginAsAdmin" stepKey="loginAsAdmin"/> + <actionGroup ref="AdminLoginActionGroup" stepKey="loginAsAdmin"/> <!--Create website, store group and store view--> <actionGroup ref="AdminCreateWebsiteActionGroup" stepKey="createWebsite"> <argument name="newWebsiteName" value="{{customWebsite.name}}"/> @@ -73,10 +73,12 @@ <actionGroup ref="AdminDeleteWebsiteActionGroup" stepKey="DeleteWebsite"> <argument name="websiteName" value="{{customWebsite.name}}"/> </actionGroup> - <actionGroup ref="logout" stepKey="logout"/> + <actionGroup ref="AdminLogoutActionGroup" stepKey="logout"/> </after> <!--Assign product to custom website--> - <amOnPage url="{{AdminProductEditPage.url($$createProduct.id$$)}}" stepKey="goToProductEditPage"/> + <actionGroup ref="AdminProductPageOpenByIdActionGroup" stepKey="goToProductEditPage"> + <argument name="productId" value="$$createProduct.id$$"/> + </actionGroup> <waitForPageLoad stepKey="waitForProductPageLoad"/> <actionGroup ref="UnassignWebsiteFromProductActionGroup" stepKey="unassignWebsiteInProduct"> <argument name="website" value="{{_defaultWebsite.name}}"/> diff --git a/app/code/Magento/Shipping/Test/Mftf/Test/AdminCreatePartialShipmentEntityTest.xml b/app/code/Magento/Shipping/Test/Mftf/Test/AdminCreatePartialShipmentEntityTest.xml index 0e4fd3ce9a86c..5e57224bfee48 100644 --- a/app/code/Magento/Shipping/Test/Mftf/Test/AdminCreatePartialShipmentEntityTest.xml +++ b/app/code/Magento/Shipping/Test/Mftf/Test/AdminCreatePartialShipmentEntityTest.xml @@ -20,7 +20,7 @@ </annotations> <before> - <actionGroup ref="LoginAsAdmin" stepKey="LoginAsAdmin"/> + <actionGroup ref="AdminLoginActionGroup" stepKey="LoginAsAdmin"/> <!-- Create Data --> <createData entity="Simple_US_Customer" stepKey="createCustomer"/> <createData entity="SimpleProduct2" stepKey="createSimpleProduct"> @@ -35,7 +35,7 @@ <deleteData createDataKey="createCustomer" stepKey="deleteCustomer"/> <deleteData createDataKey="createSimpleProduct" stepKey="deleteProduct"/> <createData entity="FreeShippinMethodDefault" stepKey="disableFreeShippingMethod"/> - <actionGroup ref="logout" stepKey="logout"/> + <actionGroup ref="AdminLogoutActionGroup" stepKey="logout"/> </after> <!-- TEST BODY --> diff --git a/app/code/Magento/Shipping/Test/Mftf/Test/AdminCreateShipmentEntityTest.xml b/app/code/Magento/Shipping/Test/Mftf/Test/AdminCreateShipmentEntityTest.xml index f4087932a0710..6b388ae31e45e 100644 --- a/app/code/Magento/Shipping/Test/Mftf/Test/AdminCreateShipmentEntityTest.xml +++ b/app/code/Magento/Shipping/Test/Mftf/Test/AdminCreateShipmentEntityTest.xml @@ -20,7 +20,7 @@ </annotations> <before> - <actionGroup ref="LoginAsAdmin" stepKey="LoginAsAdmin"/> + <actionGroup ref="AdminLoginActionGroup" stepKey="LoginAsAdmin"/> <!-- Create Data --> <createData entity="Simple_US_Customer" stepKey="createCustomer"/> <createData entity="SimpleProduct2" stepKey="createSimpleProduct"> @@ -35,7 +35,7 @@ <deleteData createDataKey="createCustomer" stepKey="deleteCustomer"/> <deleteData createDataKey="createSimpleProduct" stepKey="deleteProduct"/> <createData entity="FreeShippinMethodDefault" stepKey="disableFreeShippingMethod"/> - <actionGroup ref="logout" stepKey="logout"/> + <actionGroup ref="AdminLogoutActionGroup" stepKey="logout"/> </after> <!-- TEST BODY --> diff --git a/app/code/Magento/Shipping/Test/Mftf/Test/AdminValidateShippingTrackingNumberTest.xml b/app/code/Magento/Shipping/Test/Mftf/Test/AdminValidateShippingTrackingNumberTest.xml index ca4d731eb82b1..0d709e1d08006 100644 --- a/app/code/Magento/Shipping/Test/Mftf/Test/AdminValidateShippingTrackingNumberTest.xml +++ b/app/code/Magento/Shipping/Test/Mftf/Test/AdminValidateShippingTrackingNumberTest.xml @@ -19,12 +19,12 @@ <before> <createData entity="Simple_US_Customer" stepKey="createCustomer"/> <createData entity="SimpleProduct2" stepKey="createSimpleProduct"/> - <actionGroup ref="LoginAsAdmin" stepKey="LoginAsAdmin"/> + <actionGroup ref="AdminLoginActionGroup" stepKey="LoginAsAdmin"/> </before> <after> <deleteData createDataKey="createCustomer" stepKey="deleteCustomer"/> <deleteData createDataKey="createSimpleProduct" stepKey="deleteSimpleProduct"/> - <actionGroup ref="logout" stepKey="logout"/> + <actionGroup ref="AdminLogoutActionGroup" stepKey="logout"/> </after> <actionGroup ref="CreateOrderActionGroup" stepKey="goToCreateOrderPage"> <argument name="customer" value="$$createCustomer$$"/> diff --git a/app/code/Magento/Shipping/Test/Mftf/Test/EndToEndB2CAdminTest.xml b/app/code/Magento/Shipping/Test/Mftf/Test/EndToEndB2CAdminTest.xml index a0edf4e13a80f..f1126e52359a0 100644 --- a/app/code/Magento/Shipping/Test/Mftf/Test/EndToEndB2CAdminTest.xml +++ b/app/code/Magento/Shipping/Test/Mftf/Test/EndToEndB2CAdminTest.xml @@ -14,7 +14,7 @@ <seeInCurrentUrl url="{{AdminShipmentNewPage.url}}" stepKey="seeOrderShipmentUrl" after="clickShipAction"/> <see selector="{{AdminShipmentOrderInformationSection.orderStatus}}" userInput="Processing" stepKey="seeShipmentOrderStatus" after="seeOrderShipmentUrl"/> - <actionGroup ref="verifyBasicShipmentInformation" stepKey="checkBasicShipmentOrderInfo" after="seeShipmentOrderStatus"> + <actionGroup ref="VerifyBasicShipmentInformationActionGroup" stepKey="checkBasicShipmentOrderInfo" after="seeShipmentOrderStatus"> <argument name="customer" value="Simple_US_Customer"/> <argument name="shippingAddress" value="US_Address_TX"/> <argument name="billingAddress" value="US_Address_TX"/> @@ -34,17 +34,17 @@ <see selector="{{AdminOrderShipmentsTabSection.gridRow('1')}}" userInput="{{Simple_US_Customer.firstname}}" stepKey="seeOrderShipmentInTabGrid" after="waitForShipmentTabLoad"/> <click selector="{{AdminOrderShipmentsTabSection.viewGridRow('1')}}" stepKey="clickRowToViewShipment" after="seeOrderShipmentInTabGrid"/> <see selector="{{AdminShipmentOrderInformationSection.orderId}}" userInput="$getOrderId" stepKey="seeOrderIdOnShipment" after="clickRowToViewShipment"/> - <actionGroup ref="verifyBasicShipmentInformation" stepKey="checkShipmentOrderInformation" after="seeOrderIdOnShipment"> + <actionGroup ref="VerifyBasicShipmentInformationActionGroup" stepKey="checkShipmentOrderInformation" after="seeOrderIdOnShipment"> <argument name="customer" value="Simple_US_Customer"/> <argument name="shippingAddress" value="US_Address_TX"/> <argument name="billingAddress" value="US_Address_TX"/> </actionGroup> - <actionGroup ref="seeProductInShipmentItems" stepKey="seeSimpleProductInShipmentItems" after="checkShipmentOrderInformation"> + <actionGroup ref="SeeProductInShipmentItemsActionGroup" stepKey="seeSimpleProductInShipmentItems" after="checkShipmentOrderInformation"> <argument name="product" value="SimpleProduct"/> </actionGroup> - <actionGroup ref="seeProductInShipmentItems" stepKey="seeConfigurableProductInShipmentItems" after="seeSimpleProductInShipmentItems"> + <actionGroup ref="SeeProductInShipmentItemsActionGroup" stepKey="seeConfigurableProductInShipmentItems" after="seeSimpleProductInShipmentItems"> <argument name="product" value="BaseConfigurableProduct"/> </actionGroup> <click selector="{{AdminShipmentOrderInformationSection.orderId}}" stepKey="clickOrderIdOnShipment" after="seeConfigurableProductInShipmentItems"/> </test> -</tests> \ No newline at end of file +</tests> diff --git a/app/code/Magento/Shipping/Test/Mftf/Test/StorefrontDisplayTableRatesShippingMethodForAETest.xml b/app/code/Magento/Shipping/Test/Mftf/Test/StorefrontDisplayTableRatesShippingMethodForAETest.xml index 81fe492ffc005..5fd9a6a29c0e3 100644 --- a/app/code/Magento/Shipping/Test/Mftf/Test/StorefrontDisplayTableRatesShippingMethodForAETest.xml +++ b/app/code/Magento/Shipping/Test/Mftf/Test/StorefrontDisplayTableRatesShippingMethodForAETest.xml @@ -24,7 +24,7 @@ <requiredEntity createDataKey="createCategory"/> </createData> <createData entity="Simple_US_Customer_ArmedForcesEurope" stepKey="createCustomer"/> - <actionGroup ref="LoginAsAdmin" stepKey="loginAsAdmin"/> + <actionGroup ref="AdminLoginActionGroup" stepKey="loginAsAdmin"/> </before> <after> <deleteData createDataKey="createProduct" stepKey="deleteProduct"/> @@ -39,7 +39,7 @@ <argument name="status" value="0"/> </actionGroup> <actionGroup ref="AdminSaveConfigActionGroup" stepKey="saveSystemConfig"/> - <actionGroup ref="logout" stepKey="logout"/> + <actionGroup ref="AdminLogoutActionGroup" stepKey="logout"/> </after> <!--Admin Configuration: enable Table Rates and import CSV file with the rates--> <actionGroup ref="AdminOpenShippingMethodsConfigPageActionGroup" stepKey="openShippingMethodConfigPage"/> diff --git a/app/code/Magento/Shipping/Test/Mftf/Test/TableRatesShippingMethodForDifferentStatesTest.xml b/app/code/Magento/Shipping/Test/Mftf/Test/TableRatesShippingMethodForDifferentStatesTest.xml index 5721af7fdb71b..0f2f7ed26f1e1 100644 --- a/app/code/Magento/Shipping/Test/Mftf/Test/TableRatesShippingMethodForDifferentStatesTest.xml +++ b/app/code/Magento/Shipping/Test/Mftf/Test/TableRatesShippingMethodForDifferentStatesTest.xml @@ -26,7 +26,7 @@ <createData entity="Simple_US_Customer_CA" stepKey="createCustomer"/> <!-- Login as admin --> - <actionGroup ref="LoginAsAdmin" stepKey="loginAsAdmin"/> + <actionGroup ref="AdminLoginActionGroup" stepKey="loginAsAdmin"/> </before> <after> <!-- Delete product --> @@ -36,7 +36,7 @@ <deleteData createDataKey="createCustomer" stepKey="deleteCustomer"/> <!-- Log out --> - <actionGroup ref="logout" stepKey="logout"/> + <actionGroup ref="AdminLogoutActionGroup" stepKey="logout"/> </after> <!-- Go to Stores > Configuration > Sales > Shipping Methods --> diff --git a/app/code/Magento/Shipping/Test/Unit/Model/Config/Source/AllmethodsTest.php b/app/code/Magento/Shipping/Test/Unit/Model/Config/Source/AllmethodsTest.php new file mode 100644 index 0000000000000..985cc0e53bad5 --- /dev/null +++ b/app/code/Magento/Shipping/Test/Unit/Model/Config/Source/AllmethodsTest.php @@ -0,0 +1,106 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +namespace Magento\Shipping\Test\Unit\Model\Config\Source; + +use Magento\Framework\App\Config\ScopeConfigInterface; +use Magento\Shipping\Model\Carrier\AbstractCarrierInterface; +use Magento\Shipping\Model\Config; +use Magento\Shipping\Model\Config\Source\Allmethods; +use PHPUnit\Framework\MockObject\MockObject; + +/** + * Tests for Allmethods Class + */ +class AllmethodsTest extends \PHPUnit\Framework\TestCase +{ + /** + * @var ScopeConfigInterface|MockObject $scopeConfig + */ + private $scopeConfig; + + /** + * @var Config|MockObject $shippingConfig + */ + private $shippingConfig; + + /** + * @var Allmethods $allmethods + */ + private $allmethods; + + /** + * @var MockObject + */ + private $carriersMock; + + /** + * @inheritdoc + */ + protected function setUp() + { + $this->scopeConfig = $this->createMock(ScopeConfigInterface::class); + $this->shippingConfig = $this->createMock(Config::class); + $this->carriersMock = $this->getMockBuilder(AbstractCarrierInterface::class) + ->setMethods( + [ + 'isActive', + 'getAllowedMethods' + ] + )->getMockForAbstractClass(); + + $this->allmethods = new Allmethods( + $this->scopeConfig, + $this->shippingConfig + ); + } + + /** + * Ensure that options converted correctly + * + * @dataProvider getCarriersMethodsProvider + * @param array $expectedArray + * @return void + */ + public function testToOptionArray(array $expectedArray): void + { + $expectedArray['getAllCarriers'] = [$this->carriersMock]; + + $this->shippingConfig->expects($this->once()) + ->method('getAllCarriers') + ->willReturn($expectedArray['getAllCarriers']); + $this->carriersMock->expects($this->once()) + ->method('isActive') + ->willReturn(true); + $this->carriersMock->expects($this->once()) + ->method('getAllowedMethods') + ->willReturn($expectedArray['allowedMethods']); + $this->assertEquals([$expectedArray['expected_result']], $this->allmethods->toOptionArray()); + } + + /** + * Returns providers data for test + * + * @return array + */ + public function getCarriersMethodsProvider(): array + { + return [ + [ + [ + 'allowedMethods' => [null => 'method_title'], + 'expected_result' => [ 'value' => [], 'label' => null], + 'getAllCarriers' => [] + ], + [ + 'allowedMethods' => ['method_code' => 'method_title'], + 'expected_result' => [ 'value' => [], 'label' => 'method_code'], + 'getAllCarriers' => [] + ] + + ] + ]; + } +} diff --git a/app/code/Magento/Shipping/etc/adminhtml/system.xml b/app/code/Magento/Shipping/etc/adminhtml/system.xml index b0d38bc0d61a1..29862bdcfc8b1 100644 --- a/app/code/Magento/Shipping/etc/adminhtml/system.xml +++ b/app/code/Magento/Shipping/etc/adminhtml/system.xml @@ -11,32 +11,32 @@ <label>Shipping Settings</label> <tab>sales</tab> <resource>Magento_Shipping::config_shipping</resource> - <group id="origin" translate="label" type="text" sortOrder="1" showInDefault="1" showInWebsite="1" showInStore="0"> + <group id="origin" translate="label" type="text" sortOrder="1" showInDefault="1" showInWebsite="1"> <label>Origin</label> - <field id="country_id" translate="label" type="select" sortOrder="10" showInDefault="1" showInWebsite="1" showInStore="0" canRestore="1"> + <field id="country_id" translate="label" type="select" sortOrder="10" showInDefault="1" showInWebsite="1" canRestore="1"> <label>Country</label> <frontend_class>countries</frontend_class> <source_model>Magento\Directory\Model\Config\Source\Country</source_model> </field> - <field id="region_id" translate="label" type="text" sortOrder="20" showInDefault="1" showInWebsite="1" showInStore="0" canRestore="1"> + <field id="region_id" translate="label" type="text" sortOrder="20" showInDefault="1" showInWebsite="1" canRestore="1"> <label>Region/State</label> </field> - <field id="postcode" translate="label" type="text" sortOrder="30" showInDefault="1" showInWebsite="1" showInStore="0" canRestore="1"> + <field id="postcode" translate="label" type="text" sortOrder="30" showInDefault="1" showInWebsite="1" canRestore="1"> <label>ZIP/Postal Code</label> </field> - <field id="city" translate="label" type="text" sortOrder="40" showInDefault="1" showInWebsite="1" showInStore="0"> + <field id="city" translate="label" type="text" sortOrder="40" showInDefault="1" showInWebsite="1"> <label>City</label> </field> - <field id="street_line1" translate="label" type="text" sortOrder="50" showInDefault="1" showInWebsite="1" showInStore="0"> + <field id="street_line1" translate="label" type="text" sortOrder="50" showInDefault="1" showInWebsite="1"> <label>Street Address</label> </field> - <field id="street_line2" translate="label" type="text" sortOrder="60" showInDefault="1" showInWebsite="1" showInStore="0"> + <field id="street_line2" translate="label" type="text" sortOrder="60" showInDefault="1" showInWebsite="1"> <label>Street Address Line 2</label> </field> </group> <group id="shipping_policy" translate="label" type="text" sortOrder="120" showInDefault="1" showInWebsite="1" showInStore="1"> <label>Shipping Policy Parameters</label> - <field id="enable_shipping_policy" translate="label" type="select" sortOrder="1" showInDefault="1" showInWebsite="1" showInStore="0"> + <field id="enable_shipping_policy" translate="label" type="select" sortOrder="1" showInDefault="1" showInWebsite="1" canRestore="1"> <label>Apply custom Shipping Policy</label> <source_model>Magento\Config\Model\Config\Source\Yesno</source_model> </field> diff --git a/app/code/Magento/Shipping/etc/config.xml b/app/code/Magento/Shipping/etc/config.xml index 4c2e018191b59..e1a71ef9741bb 100644 --- a/app/code/Magento/Shipping/etc/config.xml +++ b/app/code/Magento/Shipping/etc/config.xml @@ -13,6 +13,9 @@ <postcode>90034</postcode> <region_id>12</region_id> </origin> + <shipping_policy> + <enable_shipping_policy>0</enable_shipping_policy> + </shipping_policy> </shipping> </default> </config> diff --git a/app/code/Magento/Shipping/registration.php b/app/code/Magento/Shipping/registration.php index 10a1bc529a534..9652f5a61a3bc 100644 --- a/app/code/Magento/Shipping/registration.php +++ b/app/code/Magento/Shipping/registration.php @@ -4,6 +4,6 @@ * See COPYING.txt for license details. */ -use \Magento\Framework\Component\ComponentRegistrar; +use Magento\Framework\Component\ComponentRegistrar; ComponentRegistrar::register(ComponentRegistrar::MODULE, 'Magento_Shipping', __DIR__); diff --git a/app/code/Magento/Signifyd/Api/CaseCreationServiceInterface.php b/app/code/Magento/Signifyd/Api/CaseCreationServiceInterface.php deleted file mode 100644 index f7611660b93a1..0000000000000 --- a/app/code/Magento/Signifyd/Api/CaseCreationServiceInterface.php +++ /dev/null @@ -1,29 +0,0 @@ -<?php -/** - * Copyright © Magento, Inc. All rights reserved. - * See COPYING.txt for license details. - */ -namespace Magento\Signifyd\Api; - -/** - * Signifyd case creation interface - * - * Interface of service for new Signifyd case creation and registering it on Magento side. - * Implementation should send request to Signifyd API and create new entity in Magento. - * - * @api - * @since 100.2.0 - */ -interface CaseCreationServiceInterface -{ - /** - * Create new case for order with specified id. - * - * @param int $orderId - * @return bool - * @throws \Magento\Framework\Exception\NotFoundException If order does not exists - * @throws \Magento\Framework\Exception\AlreadyExistsException If case for $orderId already exists - * @since 100.2.0 - */ - public function createForOrder($orderId); -} diff --git a/app/code/Magento/Signifyd/Api/CaseManagementInterface.php b/app/code/Magento/Signifyd/Api/CaseManagementInterface.php deleted file mode 100644 index 69075a308cb96..0000000000000 --- a/app/code/Magento/Signifyd/Api/CaseManagementInterface.php +++ /dev/null @@ -1,36 +0,0 @@ -<?php -/** - * Copyright © Magento, Inc. All rights reserved. - * See COPYING.txt for license details. - */ -namespace Magento\Signifyd\Api; - -/** - * Signifyd management interface - * Allows to performs operations with Signifyd cases. - * - * @api - * @since 100.2.0 - */ -interface CaseManagementInterface -{ - /** - * Creates new Case entity linked to order id. - * - * @param int $orderId - * @return \Magento\Signifyd\Api\Data\CaseInterface - * @throws \Magento\Framework\Exception\NotFoundException If order does not exists - * @throws \Magento\Framework\Exception\AlreadyExistsException If case for $orderId already exists - * @since 100.2.0 - */ - public function create($orderId); - - /** - * Gets Case entity associated with order id. - * - * @param int $orderId - * @return \Magento\Signifyd\Api\Data\CaseInterface|null - * @since 100.2.0 - */ - public function getByOrderId($orderId); -} diff --git a/app/code/Magento/Signifyd/Api/CaseRepositoryInterface.php b/app/code/Magento/Signifyd/Api/CaseRepositoryInterface.php deleted file mode 100644 index 4f148a082b300..0000000000000 --- a/app/code/Magento/Signifyd/Api/CaseRepositoryInterface.php +++ /dev/null @@ -1,60 +0,0 @@ -<?php -/** - * Copyright © Magento, Inc. All rights reserved. - * See COPYING.txt for license details. - */ -namespace Magento\Signifyd\Api; - -/** - * Signifyd Case repository interface - * - * @api - * @since 100.2.0 - */ -interface CaseRepositoryInterface -{ - /** - * Saves case entity. - * - * @param \Magento\Signifyd\Api\Data\CaseInterface $case - * @return \Magento\Signifyd\Api\Data\CaseInterface - * @since 100.2.0 - */ - public function save(\Magento\Signifyd\Api\Data\CaseInterface $case); - - /** - * Gets case entity by order id. - * - * @param int $id - * @return \Magento\Signifyd\Api\Data\CaseInterface - * @since 100.2.0 - */ - public function getById($id); - - /** - * Gets entity by Signifyd case id. - * - * @param int $caseId - * @return \Magento\Signifyd\Api\Data\CaseInterface|null - * @since 100.2.0 - */ - public function getByCaseId($caseId); - - /** - * Deletes case entity. - * - * @param \Magento\Signifyd\Api\Data\CaseInterface $case - * @return bool - * @since 100.2.0 - */ - public function delete(\Magento\Signifyd\Api\Data\CaseInterface $case); - - /** - * Gets list of case entities. - * - * @param \Magento\Framework\Api\SearchCriteriaInterface $searchCriteria - * @return \Magento\Signifyd\Api\Data\CaseSearchResultsInterface - * @since 100.2.0 - */ - public function getList(\Magento\Framework\Api\SearchCriteriaInterface $searchCriteria); -} diff --git a/app/code/Magento/Signifyd/Api/Data/CaseInterface.php b/app/code/Magento/Signifyd/Api/Data/CaseInterface.php deleted file mode 100644 index eff46b972fc02..0000000000000 --- a/app/code/Magento/Signifyd/Api/Data/CaseInterface.php +++ /dev/null @@ -1,235 +0,0 @@ -<?php -/** - * Copyright © Magento, Inc. All rights reserved. - * See COPYING.txt for license details. - */ -namespace Magento\Signifyd\Api\Data; - -use Magento\Signifyd\Model\SignifydGateway\Gateway; - -/** - * Signifyd Case entity interface - * - * @api - * @see https://www.signifyd.com/docs/api/#/reference/cases/retrieve-a-case/get-a-case - * @since 100.2.0 - */ -interface CaseInterface -{ - /**#@+ - * Constants for case available statuses - */ - const STATUS_OPEN = Gateway::STATUS_OPEN; - const STATUS_PENDING = 'PENDING'; - const STATUS_PROCESSING = Gateway::STATUS_PROCESSING; - const STATUS_FLAGGED = Gateway::STATUS_FLAGGED; - const STATUS_DISMISSED = Gateway::STATUS_DISMISSED; - /**#@-*/ - - /**#@+ - * Constants for guarantee available statuses - */ - const GUARANTEE_APPROVED = Gateway::GUARANTEE_APPROVED; - const GUARANTEE_DECLINED = Gateway::GUARANTEE_DECLINED; - const GUARANTEE_PENDING = Gateway::GUARANTEE_PENDING; - const GUARANTEE_CANCELED = Gateway::GUARANTEE_CANCELED; - const GUARANTEE_IN_REVIEW = Gateway::GUARANTEE_IN_REVIEW; - const GUARANTEE_UNREQUESTED = Gateway::GUARANTEE_UNREQUESTED; - /**#@-*/ - - /**#@+ - * Constants for case available review dispositions - */ - const DISPOSITION_GOOD = Gateway::DISPOSITION_GOOD; - const DISPOSITION_FRAUDULENT = Gateway::DISPOSITION_FRAUDULENT; - const DISPOSITION_UNSET = Gateway::DISPOSITION_UNSET; - /**#@-*/ - - /** - * Returns local case entity identifier. - * - * @return int - * @since 100.2.0 - */ - public function getEntityId(); - - /** - * Sets local case entity id. - * - * @param int $id - * @return $this - * @since 100.2.0 - */ - public function setEntityId($id); - - /** - * Returns Signifyd case identifier. - * - * @return int - * @since 100.2.0 - */ - public function getCaseId(); - - /** - * Sets Signifyd case id. - * - * @param int $id - * @return $this - * @since 100.2.0 - */ - public function setCaseId($id); - - /** - * Returns value, which indicates if a guarantee can be requested for a case. - * Returns null if state of guarantee eligible does not set yet. - * - * @return boolean|null - * @since 100.2.0 - */ - public function isGuaranteeEligible(); - - /** - * Sets value-indicator about guarantee availability for a case. - * - * @param bool $guaranteeEligible - * @return $this - * @since 100.2.0 - */ - public function setGuaranteeEligible($guaranteeEligible); - - /** - * Returns decision state of the guarantee. - * - * @return string - * @since 100.2.0 - */ - public function getGuaranteeDisposition(); - - /** - * Sets decision state of the guarantee. - * - * @param string $disposition - * @return $this - * @since 100.2.0 - */ - public function setGuaranteeDisposition($disposition); - - /** - * Returns case status. - * - * @return string - * @since 100.2.0 - */ - public function getStatus(); - - /** - * Sets case status. - * - * @param string $status - * @return $this - * @since 100.2.0 - */ - public function setStatus($status); - - /** - * Returns value, which indicates the likelihood that the order is fraud. - * - * @return int - * @since 100.2.0 - */ - public function getScore(); - - /** - * Sets risk level value. - * - * @param int $score - * @return $this - * @since 100.2.0 - */ - public function setScore($score); - - /** - * Get order id for a case. - * - * @return int - * @since 100.2.0 - */ - public function getOrderId(); - - /** - * Sets order id for a case. - * - * @param int $orderId - * @return $this - * @since 100.2.0 - */ - public function setOrderId($orderId); - - /** - * Returns data about a team associated with a case. - * - * @return array - * @since 100.2.0 - */ - public function getAssociatedTeam(); - - /** - * Sets team data associated with a case. - * - * @param array $team - * @return $this - * @since 100.2.0 - */ - public function setAssociatedTeam(array $team); - - /** - * Returns disposition of an agent's opinion after reviewing the case. - * - * @return string - * @since 100.2.0 - */ - public function getReviewDisposition(); - - /** - * Sets case disposition. - * - * @param string $disposition - * @return $this - * @since 100.2.0 - */ - public function setReviewDisposition($disposition); - - /** - * Returns creation datetime for a case. - * - * @return string - * @since 100.2.0 - */ - public function getCreatedAt(); - - /** - * Sets creation datetime for a case. - * - * @param string $datetime in DATE_ATOM format - * @return $this - * @since 100.2.0 - */ - public function setCreatedAt($datetime); - - /** - * Returns updating datetime for a case. - * - * @return string - * @since 100.2.0 - */ - public function getUpdatedAt(); - - /** - * Sets updating datetime for a case. - * - * @param string $datetime in DATE_ATOM format - * @return $this - * @since 100.2.0 - */ - public function setUpdatedAt($datetime); -} diff --git a/app/code/Magento/Signifyd/Api/Data/CaseSearchResultsInterface.php b/app/code/Magento/Signifyd/Api/Data/CaseSearchResultsInterface.php deleted file mode 100644 index 78d37841cff69..0000000000000 --- a/app/code/Magento/Signifyd/Api/Data/CaseSearchResultsInterface.php +++ /dev/null @@ -1,34 +0,0 @@ -<?php -/** - * Copyright © Magento, Inc. All rights reserved. - * See COPYING.txt for license details. - */ -namespace Magento\Signifyd\Api\Data; - -use Magento\Framework\Api\SearchResultsInterface; - -/** - * Retrieve and set list of case entities. - * - * @api - * @since 100.2.0 - */ -interface CaseSearchResultsInterface extends SearchResultsInterface -{ - /** - * Gets collection of case entities. - * - * @return \Magento\Signifyd\Api\Data\CaseInterface[] - * @since 100.2.0 - */ - public function getItems(); - - /** - * Sets collection of case entities. - * - * @param \Magento\Signifyd\Api\Data\CaseInterface[] $items - * @return $this - * @since 100.2.0 - */ - public function setItems(array $items); -} diff --git a/app/code/Magento/Signifyd/Api/GuaranteeCancelingServiceInterface.php b/app/code/Magento/Signifyd/Api/GuaranteeCancelingServiceInterface.php deleted file mode 100644 index 41f4753d036a7..0000000000000 --- a/app/code/Magento/Signifyd/Api/GuaranteeCancelingServiceInterface.php +++ /dev/null @@ -1,27 +0,0 @@ -<?php -/** - * Copyright © Magento, Inc. All rights reserved. - * See COPYING.txt for license details. - */ -namespace Magento\Signifyd\Api; - -/** - * Signifyd guarantee canceling interface. - * - * Interface allows to submit request to cancel previously created guarantee. - * Implementation should send request to Signifyd API and update existing case entity with guarantee information. - * - * @api - * @since 100.2.0 - */ -interface GuaranteeCancelingServiceInterface -{ - /** - * Cancels Signifyd guarantee for an order. - * - * @param int $orderId - * @return bool - * @since 100.2.0 - */ - public function cancelForOrder($orderId); -} diff --git a/app/code/Magento/Signifyd/Api/GuaranteeCreationServiceInterface.php b/app/code/Magento/Signifyd/Api/GuaranteeCreationServiceInterface.php deleted file mode 100644 index b4502ea861acd..0000000000000 --- a/app/code/Magento/Signifyd/Api/GuaranteeCreationServiceInterface.php +++ /dev/null @@ -1,27 +0,0 @@ -<?php -/** - * Copyright © Magento, Inc. All rights reserved. - * See COPYING.txt for license details. - */ -namespace Magento\Signifyd\Api; - -/** - * Signifyd guarantee creation interface - * - * Interface allows submit previously created Signifyd case for a guaranty. - * Implementation should send request to Signifyd API and update existing case entity with guarantee infromation. - * - * @api - * @since 100.2.0 - */ -interface GuaranteeCreationServiceInterface -{ - /** - * Request Signifyd guarantee for order - * - * @param int $orderId - * @return bool - * @since 100.2.0 - */ - public function createForOrder($orderId); -} diff --git a/app/code/Magento/Signifyd/Block/Adminhtml/CaseInfo.php b/app/code/Magento/Signifyd/Block/Adminhtml/CaseInfo.php deleted file mode 100644 index 87d09e392e091..0000000000000 --- a/app/code/Magento/Signifyd/Block/Adminhtml/CaseInfo.php +++ /dev/null @@ -1,120 +0,0 @@ -<?php -/** - * Copyright © Magento, Inc. All rights reserved. - * See COPYING.txt for license details. - */ -namespace Magento\Signifyd\Block\Adminhtml; - -use Magento\Framework\View\Element\Template; -use Magento\Framework\View\Element\Template\Context; -use Magento\Signifyd\Api\Data\CaseInterface; -use Magento\Signifyd\Model\CaseManagement; - -/** - * Get Signifyd Case Info - * - * @api - * @since 100.2.0 - */ -class CaseInfo extends Template -{ - /** - * @var CaseInterface - */ - private $caseEntity = false; - - /** - * @var CaseManagement - */ - private $caseManagement; - - /** - * @param Context $context - * @param CaseManagement $caseManagement - * @param array $data - */ - public function __construct( - Context $context, - CaseManagement $caseManagement, - array $data = [] - ) { - $this->caseManagement = $caseManagement; - - parent::__construct($context, $data); - } - - /** - * Gets case entity associated with order id. - * - * @return CaseInterface|null - */ - private function getCaseEntity() - { - if ($this->caseEntity === false) { - $this->caseEntity = $this->caseManagement->getByOrderId( - $this->getOrderId() - ); - } - - return $this->caseEntity; - } - - /** - * Default getter for case properties - * - * @param mixed $defaultValue - * @param callable $callback - * @return mixed - */ - private function getCaseProperty($defaultValue, callable $callback) - { - return $this->isEmptyCase() ? $defaultValue : call_user_func($callback); - } - - /** - * Checks if case is exists for order - * - * @return bool - * @since 100.2.0 - */ - public function isEmptyCase() - { - return $this->getCaseEntity() === null; - } - - /** - * Gets case guarantee disposition status. - * - * @return string - * @since 100.2.0 - */ - public function getCaseGuaranteeDisposition() - { - return $this->getCaseProperty('', function () { - $guaranteeStatusMap = [ - CaseInterface::GUARANTEE_APPROVED => __('Approved'), - CaseInterface::GUARANTEE_DECLINED => __('Declined'), - CaseInterface::GUARANTEE_PENDING => __('Pending'), - CaseInterface::GUARANTEE_CANCELED => __('Canceled'), - CaseInterface::GUARANTEE_IN_REVIEW => __('In Review'), - CaseInterface::GUARANTEE_UNREQUESTED => __('Unrequested') - ]; - - $status = isset($guaranteeStatusMap[$this->getCaseEntity()->getGuaranteeDisposition()]) ? - $guaranteeStatusMap[$this->getCaseEntity()->getGuaranteeDisposition()] : - ''; - - return $status; - }); - } - - /** - * Retrieves current order Id. - * - * @return integer - */ - private function getOrderId() - { - return (int) $this->getRequest()->getParam('order_id'); - } -} diff --git a/app/code/Magento/Signifyd/Block/Adminhtml/System/Config/Field/WebhookUrl.php b/app/code/Magento/Signifyd/Block/Adminhtml/System/Config/Field/WebhookUrl.php deleted file mode 100644 index 7964d6b1af397..0000000000000 --- a/app/code/Magento/Signifyd/Block/Adminhtml/System/Config/Field/WebhookUrl.php +++ /dev/null @@ -1,60 +0,0 @@ -<?php -/** - * Copyright © Magento, Inc. All rights reserved. - * See COPYING.txt for license details. - */ -namespace Magento\Signifyd\Block\Adminhtml\System\Config\Field; - -use Magento\Framework\Data\Form\Element\AbstractElement; -use Magento\Config\Block\System\Config\Form\Field; -use Magento\Store\Model\ScopeInterface; -use Magento\Store\Model\Store; - -/** - * Input field transformed to text node with link to store Signifyd webhooks controller. - */ -class WebhookUrl extends Field -{ - /** - * @inheritdoc - */ - protected function _getElementHtml(AbstractElement $element) - { - $url = ''; - $originalData = $element->getOriginalData(); - if (!empty($originalData['handler_url'])) { - $url = $this->getStoreUrl(); - $url .= $originalData['handler_url']; - } - - return '<p class="webhook-url">' . $this->escapeHtml($url) . '</p>'; - } - - /** - * @inheritdoc - */ - protected function _isInheritCheckboxRequired(AbstractElement $element) - { - return false; - } - - /** - * Return base store URL. - * - * @return string - */ - private function getStoreUrl() - { - $website = $this->_storeManager->getWebsite($this->getRequest()->getParam('website')); - - $isSecure = $this->_scopeConfig->isSetFlag( - Store::XML_PATH_SECURE_IN_FRONTEND, - ScopeInterface::SCOPE_WEBSITE, - $website->getCode() - ); - - $configPath = $isSecure ? Store::XML_PATH_SECURE_BASE_LINK_URL : Store::XML_PATH_UNSECURE_BASE_LINK_URL; - - return $this->_scopeConfig->getValue($configPath, ScopeInterface::SCOPE_WEBSITE, $website->getCode()); - } -} diff --git a/app/code/Magento/Signifyd/Block/Adminhtml/System/Config/Fieldset/Info.php b/app/code/Magento/Signifyd/Block/Adminhtml/System/Config/Fieldset/Info.php deleted file mode 100644 index c18c3dc596e21..0000000000000 --- a/app/code/Magento/Signifyd/Block/Adminhtml/System/Config/Fieldset/Info.php +++ /dev/null @@ -1,31 +0,0 @@ -<?php -/** - * Copyright © Magento, Inc. All rights reserved. - * See COPYING.txt for license details. - */ -namespace Magento\Signifyd\Block\Adminhtml\System\Config\Fieldset; - -use Magento\Config\Block\System\Config\Form\Fieldset; - -/** - * Fieldset renderer with url attached to comment. - */ -class Info extends Fieldset -{ - /** - * @inheritdoc - */ - protected function _getHeaderCommentHtml($element) - { - $groupConfig = $element->getGroup(); - - if (!empty($groupConfig['more_url']) && !empty($element->getComment())) { - $comment = $element->getComment(); - $comment .= '<p><a href="' . $this->escapeUrl($groupConfig['more_url']) . '" target="_blank">' . - $this->escapeHtml(__('Learn more')) . '</a></p>'; - $element->setComment($comment); - } - - return parent::_getHeaderCommentHtml($element); - } -} diff --git a/app/code/Magento/Signifyd/Block/Fingerprint.php b/app/code/Magento/Signifyd/Block/Fingerprint.php deleted file mode 100644 index f43bffce1fc1a..0000000000000 --- a/app/code/Magento/Signifyd/Block/Fingerprint.php +++ /dev/null @@ -1,92 +0,0 @@ -<?php -/** - * Copyright © Magento, Inc. All rights reserved. - * See COPYING.txt for license details. - */ -namespace Magento\Signifyd\Block; - -use Magento\Framework\View\Element\Template; -use Magento\Framework\View\Element\Template\Context; -use Magento\Signifyd\Model\Config; -use Magento\Signifyd\Model\QuoteSession\QuoteSessionInterface; -use Magento\Signifyd\Model\SignifydOrderSessionId; - -/** - * Provides data for Signifyd device fingerprinting script. - * - * Signifyd’s device fingerprinting solution uniquely tracks and identifies devices - * used to transact on your site, increasing your protection from fraud. - * - * @api - * @see https://www.signifyd.com/docs/api/#/reference/device-fingerprint/create-a-case - * @since 100.2.0 - */ -class Fingerprint extends Template -{ - /** - * @var SignifydOrderSessionId - */ - private $signifydOrderSessionId; - - /** - * @var Config - */ - private $config; - - /** - * @var QuoteSessionInterface - */ - private $quoteSession; - - /** - * @var string - * @since 100.2.0 - */ - protected $_template = 'Magento_Signifyd::fingerprint.phtml'; - - /** - * @param Context $context - * @param Config $config - * @param SignifydOrderSessionId $signifydOrderSessionId - * @param QuoteSessionInterface $quoteSession - * @param array $data - */ - public function __construct( - Context $context, - Config $config, - SignifydOrderSessionId $signifydOrderSessionId, - QuoteSessionInterface $quoteSession, - array $data = [] - ) { - parent::__construct($context, $data); - $this->signifydOrderSessionId = $signifydOrderSessionId; - $this->config = $config; - $this->quoteSession = $quoteSession; - } - - /** - * Returns a unique Signifyd order session id. - * - * @return string - * @since 100.2.0 - */ - public function getSignifydOrderSessionId() - { - $quoteId = $this->quoteSession->getQuote()->getId(); - - return $this->signifydOrderSessionId->get($quoteId); - } - - /** - * Checks if module is enabled. - * - * @return boolean - * @since 100.2.0 - */ - public function isModuleActive() - { - $storeId = $this->quoteSession->getQuote()->getStoreId(); - - return $this->config->isActive($storeId); - } -} diff --git a/app/code/Magento/Signifyd/Controller/Webhooks/Handler.php b/app/code/Magento/Signifyd/Controller/Webhooks/Handler.php deleted file mode 100644 index 2dee31f4048b9..0000000000000 --- a/app/code/Magento/Signifyd/Controller/Webhooks/Handler.php +++ /dev/null @@ -1,157 +0,0 @@ -<?php -/** - * Copyright © Magento, Inc. All rights reserved. - * See COPYING.txt for license details. - */ -namespace Magento\Signifyd\Controller\Webhooks; - -use Magento\Framework\App\Action\Action; -use Magento\Framework\App\Action\Context; -use Magento\Framework\App\Request\InvalidRequestException; -use Magento\Framework\App\RequestInterface; -use Magento\Framework\Exception\LocalizedException; -use Magento\Signifyd\Api\CaseRepositoryInterface; -use Magento\Signifyd\Model\CaseServices\UpdatingServiceFactory; -use Magento\Signifyd\Model\Config; -use Magento\Signifyd\Model\SignifydGateway\Response\WebhookMessageReader; -use Magento\Signifyd\Model\SignifydGateway\Response\WebhookRequest; -use Magento\Signifyd\Model\SignifydGateway\Response\WebhookRequestValidator; -use Psr\Log\LoggerInterface; - -/** - * Responsible for handling webhook posts from Signifyd service. - * - * @see https://www.signifyd.com/docs/api/#/reference/webhooks/ - */ -class Handler extends Action implements \Magento\Framework\App\CsrfAwareActionInterface -{ - /** - * Event topic of test webhook request. - * - * @var string - */ - private static $eventTopicTest = 'cases/test'; - - /** - * @var WebhookRequest - */ - private $webhookRequest; - - /** - * @var LoggerInterface - */ - private $logger; - - /** - * @var WebhookMessageReader - */ - private $webhookMessageReader; - - /** - * @var UpdatingServiceFactory - */ - private $caseUpdatingServiceFactory; - - /** - * @var WebhookRequestValidator - */ - private $webhookRequestValidator; - - /** - * @var CaseRepositoryInterface - */ - private $caseRepository; - - /** - * @var Config - */ - private $config; - - /** - * @param Context $context - * @param WebhookRequest $webhookRequest - * @param LoggerInterface $logger - * @param WebhookMessageReader $webhookMessageReader - * @param UpdatingServiceFactory $caseUpdatingServiceFactory - * @param WebhookRequestValidator $webhookRequestValidator - * @param CaseRepositoryInterface $caseRepository - * @param Config $config - */ - public function __construct( - Context $context, - WebhookRequest $webhookRequest, - LoggerInterface $logger, - WebhookMessageReader $webhookMessageReader, - UpdatingServiceFactory $caseUpdatingServiceFactory, - WebhookRequestValidator $webhookRequestValidator, - CaseRepositoryInterface $caseRepository, - Config $config - ) { - parent::__construct($context); - $this->webhookRequest = $webhookRequest; - $this->logger = $logger; - $this->webhookMessageReader = $webhookMessageReader; - $this->caseUpdatingServiceFactory = $caseUpdatingServiceFactory; - $this->webhookRequestValidator = $webhookRequestValidator; - $this->caseRepository = $caseRepository; - $this->config = $config; - } - - /** - * Processes webhook request data and updates case entity - * - * @return void - */ - public function execute() - { - if ($this->config->isDebugModeEnabled()) { - $this->logger->debug($this->webhookRequest->getEventTopic() . '|' . $this->webhookRequest->getBody()); - } - - if (!$this->webhookRequestValidator->validate($this->webhookRequest)) { - $this->_redirect('noroute'); - return; - } - - $webhookMessage = $this->webhookMessageReader->read($this->webhookRequest); - if ($webhookMessage->getEventTopic() === self::$eventTopicTest) { - return; - } - - $data = $webhookMessage->getData(); - if (empty($data['caseId'])) { - $this->_redirect('noroute'); - return; - } - - $case = $this->caseRepository->getByCaseId($data['caseId']); - if ($case === null) { - $this->_redirect('noroute'); - return; - } - - $caseUpdatingService = $this->caseUpdatingServiceFactory->create($webhookMessage->getEventTopic()); - try { - $caseUpdatingService->update($case, $data); - } catch (LocalizedException $e) { - $this->getResponse()->setHttpResponseCode(400); - $this->logger->critical($e); - } - } - - /** - * @inheritDoc - */ - public function createCsrfValidationException(RequestInterface $request): ?InvalidRequestException - { - return null; - } - - /** - * @inheritDoc - */ - public function validateForCsrf(RequestInterface $request): ?bool - { - return true; - } -} diff --git a/app/code/Magento/Signifyd/LICENSE.txt b/app/code/Magento/Signifyd/LICENSE.txt deleted file mode 100644 index 49525fd99da9c..0000000000000 --- a/app/code/Magento/Signifyd/LICENSE.txt +++ /dev/null @@ -1,48 +0,0 @@ - -Open Software License ("OSL") v. 3.0 - -This Open Software License (the "License") applies to any original work of authorship (the "Original Work") whose owner (the "Licensor") has placed the following licensing notice adjacent to the copyright notice for the Original Work: - -Licensed under the Open Software License version 3.0 - - 1. Grant of Copyright License. Licensor grants You a worldwide, royalty-free, non-exclusive, sublicensable license, for the duration of the copyright, to do the following: - - 1. to reproduce the Original Work in copies, either alone or as part of a collective work; - - 2. to translate, adapt, alter, transform, modify, or arrange the Original Work, thereby creating derivative works ("Derivative Works") based upon the Original Work; - - 3. to distribute or communicate copies of the Original Work and Derivative Works to the public, with the proviso that copies of Original Work or Derivative Works that You distribute or communicate shall be licensed under this Open Software License; - - 4. to perform the Original Work publicly; and - - 5. to display the Original Work publicly. - - 2. Grant of Patent License. Licensor grants You a worldwide, royalty-free, non-exclusive, sublicensable license, under patent claims owned or controlled by the Licensor that are embodied in the Original Work as furnished by the Licensor, for the duration of the patents, to make, use, sell, offer for sale, have made, and import the Original Work and Derivative Works. - - 3. Grant of Source Code License. The term "Source Code" means the preferred form of the Original Work for making modifications to it and all available documentation describing how to modify the Original Work. Licensor agrees to provide a machine-readable copy of the Source Code of the Original Work along with each copy of the Original Work that Licensor distributes. Licensor reserves the right to satisfy this obligation by placing a machine-readable copy of the Source Code in an information repository reasonably calculated to permit inexpensive and convenient access by You for as long as Licensor continues to distribute the Original Work. - - 4. Exclusions From License Grant. Neither the names of Licensor, nor the names of any contributors to the Original Work, nor any of their trademarks or service marks, may be used to endorse or promote products derived from this Original Work without express prior permission of the Licensor. Except as expressly stated herein, nothing in this License grants any license to Licensor's trademarks, copyrights, patents, trade secrets or any other intellectual property. No patent license is granted to make, use, sell, offer for sale, have made, or import embodiments of any patent claims other than the licensed claims defined in Section 2. No license is granted to the trademarks of Licensor even if such marks are included in the Original Work. Nothing in this License shall be interpreted to prohibit Licensor from licensing under terms different from this License any Original Work that Licensor otherwise would have a right to license. - - 5. External Deployment. The term "External Deployment" means the use, distribution, or communication of the Original Work or Derivative Works in any way such that the Original Work or Derivative Works may be used by anyone other than You, whether those works are distributed or communicated to those persons or made available as an application intended for use over a network. As an express condition for the grants of license hereunder, You must treat any External Deployment by You of the Original Work or a Derivative Work as a distribution under section 1(c). - - 6. Attribution Rights. You must retain, in the Source Code of any Derivative Works that You create, all copyright, patent, or trademark notices from the Source Code of the Original Work, as well as any notices of licensing and any descriptive text identified therein as an "Attribution Notice." You must cause the Source Code for any Derivative Works that You create to carry a prominent Attribution Notice reasonably calculated to inform recipients that You have modified the Original Work. - - 7. Warranty of Provenance and Disclaimer of Warranty. Licensor warrants that the copyright in and to the Original Work and the patent rights granted herein by Licensor are owned by the Licensor or are sublicensed to You under the terms of this License with the permission of the contributor(s) of those copyrights and patent rights. Except as expressly stated in the immediately preceding sentence, the Original Work is provided under this License on an "AS IS" BASIS and WITHOUT WARRANTY, either express or implied, including, without limitation, the warranties of non-infringement, merchantability or fitness for a particular purpose. THE ENTIRE RISK AS TO THE QUALITY OF THE ORIGINAL WORK IS WITH YOU. This DISCLAIMER OF WARRANTY constitutes an essential part of this License. No license to the Original Work is granted by this License except under this disclaimer. - - 8. Limitation of Liability. Under no circumstances and under no legal theory, whether in tort (including negligence), contract, or otherwise, shall the Licensor be liable to anyone for any indirect, special, incidental, or consequential damages of any character arising as a result of this License or the use of the Original Work including, without limitation, damages for loss of goodwill, work stoppage, computer failure or malfunction, or any and all other commercial damages or losses. This limitation of liability shall not apply to the extent applicable law prohibits such limitation. - - 9. Acceptance and Termination. If, at any time, You expressly assented to this License, that assent indicates your clear and irrevocable acceptance of this License and all of its terms and conditions. If You distribute or communicate copies of the Original Work or a Derivative Work, You must make a reasonable effort under the circumstances to obtain the express assent of recipients to the terms of this License. This License conditions your rights to undertake the activities listed in Section 1, including your right to create Derivative Works based upon the Original Work, and doing so without honoring these terms and conditions is prohibited by copyright law and international treaty. Nothing in this License is intended to affect copyright exceptions and limitations (including 'fair use' or 'fair dealing'). This License shall terminate immediately and You may no longer exercise any of the rights granted to You by this License upon your failure to honor the conditions in Section 1(c). - - 10. Termination for Patent Action. This License shall terminate automatically and You may no longer exercise any of the rights granted to You by this License as of the date You commence an action, including a cross-claim or counterclaim, against Licensor or any licensee alleging that the Original Work infringes a patent. This termination provision shall not apply for an action alleging patent infringement by combinations of the Original Work with other software or hardware. - - 11. Jurisdiction, Venue and Governing Law. Any action or suit relating to this License may be brought only in the courts of a jurisdiction wherein the Licensor resides or in which Licensor conducts its primary business, and under the laws of that jurisdiction excluding its conflict-of-law provisions. The application of the United Nations Convention on Contracts for the International Sale of Goods is expressly excluded. Any use of the Original Work outside the scope of this License or after its termination shall be subject to the requirements and penalties of copyright or patent law in the appropriate jurisdiction. This section shall survive the termination of this License. - - 12. Attorneys' Fees. In any action to enforce the terms of this License or seeking damages relating thereto, the prevailing party shall be entitled to recover its costs and expenses, including, without limitation, reasonable attorneys' fees and costs incurred in connection with such action, including any appeal of such action. This section shall survive the termination of this License. - - 13. Miscellaneous. If any provision of this License is held to be unenforceable, such provision shall be reformed only to the extent necessary to make it enforceable. - - 14. Definition of "You" in This License. "You" throughout this License, whether in upper or lower case, means an individual or a legal entity exercising rights under, and complying with all of the terms of, this License. For legal entities, "You" includes any entity that controls, is controlled by, or is under common control with you. For purposes of this definition, "control" means (i) the power, direct or indirect, to cause the direction or management of such entity, whether by contract or otherwise, or (ii) ownership of fifty percent (50%) or more of the outstanding shares, or (iii) beneficial ownership of such entity. - - 15. Right to Use. You may use the Original Work in all ways not otherwise restricted or conditioned by this License or by law, and Licensor promises not to interfere with or be responsible for such uses by You. - - 16. Modification of This License. This License is Copyright (C) 2005 Lawrence Rosen. Permission is granted to copy, distribute, or communicate this License without modification. Nothing in this License permits You to modify this License as applied to the Original Work or to Derivative Works. However, You may modify the text of this License and copy, distribute or communicate your modified version (the "Modified License") and apply it to other original works of authorship subject to the following conditions: (i) You may not indicate in any way that your Modified License is the "Open Software License" or "OSL" and you may not use those names in the name of your Modified License; (ii) You must replace the notice specified in the first paragraph above with the notice "Licensed under <insert your license name here>" or with a notice of your own that is not confusingly similar to the notice in this License; and (iii) You may not claim that your original works are open source software unless your Modified License has been approved by Open Source Initiative (OSI) and You comply with its license review and certification process. \ No newline at end of file diff --git a/app/code/Magento/Signifyd/LICENSE_AFL.txt b/app/code/Magento/Signifyd/LICENSE_AFL.txt deleted file mode 100644 index f39d641b18a19..0000000000000 --- a/app/code/Magento/Signifyd/LICENSE_AFL.txt +++ /dev/null @@ -1,48 +0,0 @@ - -Academic Free License ("AFL") v. 3.0 - -This Academic Free License (the "License") applies to any original work of authorship (the "Original Work") whose owner (the "Licensor") has placed the following licensing notice adjacent to the copyright notice for the Original Work: - -Licensed under the Academic Free License version 3.0 - - 1. Grant of Copyright License. Licensor grants You a worldwide, royalty-free, non-exclusive, sublicensable license, for the duration of the copyright, to do the following: - - 1. to reproduce the Original Work in copies, either alone or as part of a collective work; - - 2. to translate, adapt, alter, transform, modify, or arrange the Original Work, thereby creating derivative works ("Derivative Works") based upon the Original Work; - - 3. to distribute or communicate copies of the Original Work and Derivative Works to the public, under any license of your choice that does not contradict the terms and conditions, including Licensor's reserved rights and remedies, in this Academic Free License; - - 4. to perform the Original Work publicly; and - - 5. to display the Original Work publicly. - - 2. Grant of Patent License. Licensor grants You a worldwide, royalty-free, non-exclusive, sublicensable license, under patent claims owned or controlled by the Licensor that are embodied in the Original Work as furnished by the Licensor, for the duration of the patents, to make, use, sell, offer for sale, have made, and import the Original Work and Derivative Works. - - 3. Grant of Source Code License. The term "Source Code" means the preferred form of the Original Work for making modifications to it and all available documentation describing how to modify the Original Work. Licensor agrees to provide a machine-readable copy of the Source Code of the Original Work along with each copy of the Original Work that Licensor distributes. Licensor reserves the right to satisfy this obligation by placing a machine-readable copy of the Source Code in an information repository reasonably calculated to permit inexpensive and convenient access by You for as long as Licensor continues to distribute the Original Work. - - 4. Exclusions From License Grant. Neither the names of Licensor, nor the names of any contributors to the Original Work, nor any of their trademarks or service marks, may be used to endorse or promote products derived from this Original Work without express prior permission of the Licensor. Except as expressly stated herein, nothing in this License grants any license to Licensor's trademarks, copyrights, patents, trade secrets or any other intellectual property. No patent license is granted to make, use, sell, offer for sale, have made, or import embodiments of any patent claims other than the licensed claims defined in Section 2. No license is granted to the trademarks of Licensor even if such marks are included in the Original Work. Nothing in this License shall be interpreted to prohibit Licensor from licensing under terms different from this License any Original Work that Licensor otherwise would have a right to license. - - 5. External Deployment. The term "External Deployment" means the use, distribution, or communication of the Original Work or Derivative Works in any way such that the Original Work or Derivative Works may be used by anyone other than You, whether those works are distributed or communicated to those persons or made available as an application intended for use over a network. As an express condition for the grants of license hereunder, You must treat any External Deployment by You of the Original Work or a Derivative Work as a distribution under section 1(c). - - 6. Attribution Rights. You must retain, in the Source Code of any Derivative Works that You create, all copyright, patent, or trademark notices from the Source Code of the Original Work, as well as any notices of licensing and any descriptive text identified therein as an "Attribution Notice." You must cause the Source Code for any Derivative Works that You create to carry a prominent Attribution Notice reasonably calculated to inform recipients that You have modified the Original Work. - - 7. Warranty of Provenance and Disclaimer of Warranty. Licensor warrants that the copyright in and to the Original Work and the patent rights granted herein by Licensor are owned by the Licensor or are sublicensed to You under the terms of this License with the permission of the contributor(s) of those copyrights and patent rights. Except as expressly stated in the immediately preceding sentence, the Original Work is provided under this License on an "AS IS" BASIS and WITHOUT WARRANTY, either express or implied, including, without limitation, the warranties of non-infringement, merchantability or fitness for a particular purpose. THE ENTIRE RISK AS TO THE QUALITY OF THE ORIGINAL WORK IS WITH YOU. This DISCLAIMER OF WARRANTY constitutes an essential part of this License. No license to the Original Work is granted by this License except under this disclaimer. - - 8. Limitation of Liability. Under no circumstances and under no legal theory, whether in tort (including negligence), contract, or otherwise, shall the Licensor be liable to anyone for any indirect, special, incidental, or consequential damages of any character arising as a result of this License or the use of the Original Work including, without limitation, damages for loss of goodwill, work stoppage, computer failure or malfunction, or any and all other commercial damages or losses. This limitation of liability shall not apply to the extent applicable law prohibits such limitation. - - 9. Acceptance and Termination. If, at any time, You expressly assented to this License, that assent indicates your clear and irrevocable acceptance of this License and all of its terms and conditions. If You distribute or communicate copies of the Original Work or a Derivative Work, You must make a reasonable effort under the circumstances to obtain the express assent of recipients to the terms of this License. This License conditions your rights to undertake the activities listed in Section 1, including your right to create Derivative Works based upon the Original Work, and doing so without honoring these terms and conditions is prohibited by copyright law and international treaty. Nothing in this License is intended to affect copyright exceptions and limitations (including "fair use" or "fair dealing"). This License shall terminate immediately and You may no longer exercise any of the rights granted to You by this License upon your failure to honor the conditions in Section 1(c). - - 10. Termination for Patent Action. This License shall terminate automatically and You may no longer exercise any of the rights granted to You by this License as of the date You commence an action, including a cross-claim or counterclaim, against Licensor or any licensee alleging that the Original Work infringes a patent. This termination provision shall not apply for an action alleging patent infringement by combinations of the Original Work with other software or hardware. - - 11. Jurisdiction, Venue and Governing Law. Any action or suit relating to this License may be brought only in the courts of a jurisdiction wherein the Licensor resides or in which Licensor conducts its primary business, and under the laws of that jurisdiction excluding its conflict-of-law provisions. The application of the United Nations Convention on Contracts for the International Sale of Goods is expressly excluded. Any use of the Original Work outside the scope of this License or after its termination shall be subject to the requirements and penalties of copyright or patent law in the appropriate jurisdiction. This section shall survive the termination of this License. - - 12. Attorneys' Fees. In any action to enforce the terms of this License or seeking damages relating thereto, the prevailing party shall be entitled to recover its costs and expenses, including, without limitation, reasonable attorneys' fees and costs incurred in connection with such action, including any appeal of such action. This section shall survive the termination of this License. - - 13. Miscellaneous. If any provision of this License is held to be unenforceable, such provision shall be reformed only to the extent necessary to make it enforceable. - - 14. Definition of "You" in This License. "You" throughout this License, whether in upper or lower case, means an individual or a legal entity exercising rights under, and complying with all of the terms of, this License. For legal entities, "You" includes any entity that controls, is controlled by, or is under common control with you. For purposes of this definition, "control" means (i) the power, direct or indirect, to cause the direction or management of such entity, whether by contract or otherwise, or (ii) ownership of fifty percent (50%) or more of the outstanding shares, or (iii) beneficial ownership of such entity. - - 15. Right to Use. You may use the Original Work in all ways not otherwise restricted or conditioned by this License or by law, and Licensor promises not to interfere with or be responsible for such uses by You. - - 16. Modification of This License. This License is Copyright © 2005 Lawrence Rosen. Permission is granted to copy, distribute, or communicate this License without modification. Nothing in this License permits You to modify this License as applied to the Original Work or to Derivative Works. However, You may modify the text of this License and copy, distribute or communicate your modified version (the "Modified License") and apply it to other original works of authorship subject to the following conditions: (i) You may not indicate in any way that your Modified License is the "Academic Free License" or "AFL" and you may not use those names in the name of your Modified License; (ii) You must replace the notice specified in the first paragraph above with the notice "Licensed under <insert your license name here>" or with a notice of your own that is not confusingly similar to the notice in this License; and (iii) You may not claim that your original works are open source software unless your Modified License has been approved by Open Source Initiative (OSI) and You comply with its license review and certification process. diff --git a/app/code/Magento/Signifyd/Model/CaseEntity.php b/app/code/Magento/Signifyd/Model/CaseEntity.php deleted file mode 100644 index c11c72db79f16..0000000000000 --- a/app/code/Magento/Signifyd/Model/CaseEntity.php +++ /dev/null @@ -1,249 +0,0 @@ -<?php -/** - * Copyright © Magento, Inc. All rights reserved. - * See COPYING.txt for license details. - */ -namespace Magento\Signifyd\Model; - -use Magento\Framework\Data\Collection\AbstractDb; -use Magento\Framework\Model\AbstractModel; -use Magento\Framework\Model\Context; -use Magento\Framework\Model\ResourceModel\AbstractResource; -use Magento\Framework\Registry; -use Magento\Framework\Serialize\SerializerInterface; -use Magento\Signifyd\Api\Data\CaseInterface; - -/** - * Implementation of Signifyd Case interface. - */ -class CaseEntity extends AbstractModel implements CaseInterface -{ - /** - * @var string - */ - protected $_eventPrefix = 'signifyd_case'; - - /** - * @var SerializerInterface - */ - private $serializer; - - /** - * CaseEntity constructor. - * - * @param Context $context - * @param Registry $registry - * @param SerializerInterface $serializer - * @param array $data - * @param AbstractResource|null $resource - * @param AbstractDb|null $resourceCollection - */ - public function __construct( - Context $context, - Registry $registry, - SerializerInterface $serializer, - array $data = [], - AbstractResource $resource = null, - AbstractDb $resourceCollection = null - ) { - $this->serializer = $serializer; - parent::__construct($context, $registry, $resource, $resourceCollection, $data); - } - - /** - * @inheritdoc - */ - protected function _construct() - { - $this->_init(ResourceModel\CaseEntity::class); - } - - /** - * @inheritdoc - */ - public function getEntityId() - { - return (int) $this->getData('entity_id'); - } - - /** - * @inheritdoc - */ - public function setEntityId($id) - { - $this->setData('entity_id', (int) $id); - return $this; - } - - /** - * @inheritdoc - */ - public function getCaseId() - { - return (int) $this->getData('case_id'); - } - - /** - * @inheritdoc - */ - public function setCaseId($id) - { - $this->setData('case_id', (int) $id); - return $this; - } - - /** - * @inheritdoc - */ - public function isGuaranteeEligible() - { - $value = $this->getData('guarantee_eligible'); - return ($value === null) ? $value : (bool) $value; - } - - /** - * @inheritdoc - */ - public function setGuaranteeEligible($guaranteeEligible) - { - $this->setData('guarantee_eligible', $guaranteeEligible); - return $this; - } - - /** - * @inheritdoc - */ - public function getGuaranteeDisposition() - { - return (string) $this->getData('guarantee_disposition'); - } - - /** - * @inheritdoc - */ - public function setGuaranteeDisposition($disposition) - { - $this->setData('guarantee_disposition', (string) $disposition); - return $this; - } - - /** - * @inheritdoc - */ - public function getStatus() - { - return (string) $this->getData('status'); - } - - /** - * @inheritdoc - */ - public function setStatus($status) - { - $this->setData('status', (string) $status); - return $this; - } - - /** - * @inheritdoc - */ - public function getScore() - { - return (int) $this->getData('score'); - } - - /** - * @inheritdoc - */ - public function setScore($score) - { - $this->setData('score', (int) $score); - return $this; - } - - /** - * @inheritdoc - */ - public function getOrderId() - { - return (int) $this->getData('order_id'); - } - - /** - * @inheritdoc - */ - public function setOrderId($orderId) - { - $this->setData('order_id', (int) $orderId); - return $this; - } - - /** - * @inheritdoc - */ - public function getAssociatedTeam() - { - $teamData = $this->getData('associated_team'); - return empty($teamData) ? [] : $this->serializer->unserialize($teamData); - } - - /** - * @inheritdoc - */ - public function setAssociatedTeam(array $team) - { - $this->setData('associated_team', $this->serializer->serialize($team)); - return $this; - } - - /** - * @inheritdoc - */ - public function getReviewDisposition() - { - return (string) $this->getData('review_disposition'); - } - - /** - * @inheritdoc - */ - public function setReviewDisposition($disposition) - { - $this->setData('review_disposition', (string) $disposition); - return $this; - } - - /** - * @inheritdoc - */ - public function getCreatedAt() - { - return $this->getData('created_at'); - } - - /** - * @inheritdoc - */ - public function setCreatedAt($datetime) - { - $this->setData('created_at', $datetime); - return $this; - } - - /** - * @inheritdoc - */ - public function getUpdatedAt() - { - return $this->getData('updated_at'); - } - - /** - * @inheritdoc - */ - public function setUpdatedAt($datetime) - { - $this->setData('updated_at', $datetime); - return $this; - } -} diff --git a/app/code/Magento/Signifyd/Model/CaseManagement.php b/app/code/Magento/Signifyd/Model/CaseManagement.php deleted file mode 100644 index 1913f1e7a17b3..0000000000000 --- a/app/code/Magento/Signifyd/Model/CaseManagement.php +++ /dev/null @@ -1,93 +0,0 @@ -<?php -/** - * Copyright © Magento, Inc. All rights reserved. - * See COPYING.txt for license details. - */ -namespace Magento\Signifyd\Model; - -use Magento\Framework\Api\FilterBuilder; -use Magento\Framework\Api\SearchCriteriaBuilder; -use Magento\Signifyd\Api\CaseManagementInterface; -use Magento\Signifyd\Api\CaseRepositoryInterface; -use Magento\Signifyd\Api\Data\CaseInterface; -use Magento\Signifyd\Api\Data\CaseInterfaceFactory; -use Magento\Framework\Exception\AlreadyExistsException; -use Magento\Framework\DB\Adapter\DuplicateException; - -/** - * - * Default case management implementation - */ -class CaseManagement implements CaseManagementInterface -{ - /** - * @var CaseRepositoryInterface - */ - private $caseRepository; - - /** - * @var CaseInterfaceFactory - */ - private $caseFactory; - - /** - * @var FilterBuilder - */ - private $filterBuilder; - - /** - * @var SearchCriteriaBuilder - */ - private $searchCriteriaBuilder; - - /** - * CaseManagement constructor. - * @param CaseRepositoryInterface $caseRepository - * @param CaseInterfaceFactory $caseFactory - * @param FilterBuilder $filterBuilder - * @param SearchCriteriaBuilder $searchCriteriaBuilder - */ - public function __construct( - CaseRepositoryInterface $caseRepository, - CaseInterfaceFactory $caseFactory, - FilterBuilder $filterBuilder, - SearchCriteriaBuilder $searchCriteriaBuilder - ) { - $this->caseRepository = $caseRepository; - $this->caseFactory = $caseFactory; - $this->searchCriteriaBuilder = $searchCriteriaBuilder; - $this->filterBuilder = $filterBuilder; - } - - /** - * @inheritdoc - */ - public function create($orderId) - { - /** @var \Magento\Signifyd\Api\Data\CaseInterface $case */ - $case = $this->caseFactory->create(); - $case->setOrderId($orderId) - ->setStatus(CaseInterface::STATUS_PENDING) - ->setGuaranteeDisposition(CaseInterface::GUARANTEE_PENDING); - try { - return $this->caseRepository->save($case); - } catch (DuplicateException $e) { - throw new AlreadyExistsException(__('This order already has associated case entity'), $e); - } - } - - /** - * @inheritdoc - */ - public function getByOrderId($orderId) - { - $filters = [ - $this->filterBuilder->setField('order_id') - ->setValue($orderId) - ->create() - ]; - $searchCriteria = $this->searchCriteriaBuilder->addFilters($filters)->create(); - $items = $this->caseRepository->getList($searchCriteria)->getItems(); - return !empty($items) ? array_pop($items) : null; - } -} diff --git a/app/code/Magento/Signifyd/Model/CaseRepository.php b/app/code/Magento/Signifyd/Model/CaseRepository.php deleted file mode 100644 index ea3ea3e67aafd..0000000000000 --- a/app/code/Magento/Signifyd/Model/CaseRepository.php +++ /dev/null @@ -1,134 +0,0 @@ -<?php -/** - * Copyright © Magento, Inc. All rights reserved. - * See COPYING.txt for license details. - */ -namespace Magento\Signifyd\Model; - -use Magento\Framework\Api\SearchCriteriaInterface; -use Magento\Framework\Api\SearchCriteria\CollectionProcessorInterface; -use Magento\Signifyd\Api\CaseRepositoryInterface; -use Magento\Signifyd\Api\Data\CaseInterface; -use Magento\Signifyd\Api\Data\CaseInterfaceFactory; -use Magento\Signifyd\Api\Data\CaseSearchResultsInterface; -use Magento\Signifyd\Api\Data\CaseSearchResultsInterfaceFactory; -use Magento\Signifyd\Model\ResourceModel\CaseEntity as CaseResourceModel; -use Magento\Signifyd\Model\ResourceModel\CaseEntity\Collection; -use Magento\Signifyd\Model\ResourceModel\CaseEntity\CollectionFactory; - -/** - * Repository for Case interface - * - * @SuppressWarnings(PHPMD.CouplingBetweenObjects) - */ -class CaseRepository implements CaseRepositoryInterface -{ - /** - * @var CollectionProcessorInterface - */ - private $collectionProcessor; - - /** - * @var CollectionFactory - */ - private $collectionFactory; - - /** - * @var CaseSearchResultsInterfaceFactory - */ - private $searchResultsFactory; - - /** - * @var CaseInterfaceFactory - */ - private $caseFactory; - - /** - * @var CaseResourceModel - */ - private $resourceModel; - - /** - * CaseRepository constructor. - * @param CollectionProcessorInterface $collectionProcessor - * @param CollectionFactory $collectionFactory - * @param CaseSearchResultsInterfaceFactory $searchResultsFactory - * @param CaseInterfaceFactory $caseFactory - * @param CaseResourceModel $resourceModel - */ - public function __construct( - CollectionProcessorInterface $collectionProcessor, - CollectionFactory $collectionFactory, - CaseSearchResultsInterfaceFactory $searchResultsFactory, - CaseInterfaceFactory $caseFactory, - CaseResourceModel $resourceModel - ) { - $this->collectionProcessor = $collectionProcessor; - $this->collectionFactory = $collectionFactory; - $this->searchResultsFactory = $searchResultsFactory; - $this->caseFactory = $caseFactory; - $this->resourceModel = $resourceModel; - } - - /** - * @inheritdoc - */ - public function save(CaseInterface $case) - { - /** @var CaseEntity $case */ - $this->resourceModel->save($case); - - return $case; - } - - /** - * @inheritdoc - */ - public function getById($id) - { - /** @var CaseEntity $case */ - $case = $this->caseFactory->create(); - $this->resourceModel->load($case, $id); - - return $case; - } - - /** - * @inheritdoc - */ - public function getByCaseId($caseId) - { - /** @var CaseEntity $case */ - $case = $this->caseFactory->create(); - $this->resourceModel->load($case, $caseId, 'case_id'); - - return $case->getEntityId() ? $case : null; - } - - /** - * @inheritdoc - */ - public function delete(CaseInterface $case) - { - $this->resourceModel->delete($case); - - return true; - } - - /** - * @inheritdoc - */ - public function getList(SearchCriteriaInterface $searchCriteria) - { - /** @var Collection $collection */ - $collection = $this->collectionFactory->create(); - $this->collectionProcessor->process($searchCriteria, $collection); - - /** @var CaseSearchResultsInterface $searchResults */ - $searchResults = $this->searchResultsFactory->create(); - $searchResults->setSearchCriteria($searchCriteria); - $searchResults->setItems($collection->getItems()); - - return $searchResults; - } -} diff --git a/app/code/Magento/Signifyd/Model/CaseSearchResults.php b/app/code/Magento/Signifyd/Model/CaseSearchResults.php deleted file mode 100644 index ff1ab8839f6cd..0000000000000 --- a/app/code/Magento/Signifyd/Model/CaseSearchResults.php +++ /dev/null @@ -1,18 +0,0 @@ -<?php -/** - * Copyright © Magento, Inc. All rights reserved. - * See COPYING.txt for license details. - */ -declare(strict_types=1); - -namespace Magento\Signifyd\Model; - -use Magento\Framework\Api\SearchResults; -use Magento\Signifyd\Api\Data\CaseSearchResultsInterface; - -/** - * Service Data Object with Case entities search results. - */ -class CaseSearchResults extends SearchResults implements CaseSearchResultsInterface -{ -} diff --git a/app/code/Magento/Signifyd/Model/CaseServices/CreationService.php b/app/code/Magento/Signifyd/Model/CaseServices/CreationService.php deleted file mode 100644 index 8413838bd7d5f..0000000000000 --- a/app/code/Magento/Signifyd/Model/CaseServices/CreationService.php +++ /dev/null @@ -1,101 +0,0 @@ -<?php -/** - * Copyright © Magento, Inc. All rights reserved. - * See COPYING.txt for license details. - */ -namespace Magento\Signifyd\Model\CaseServices; - -use Magento\Signifyd\Api\CaseCreationServiceInterface; -use Magento\Signifyd\Api\CaseManagementInterface; -use Magento\Signifyd\Api\CaseRepositoryInterface; -use Magento\Signifyd\Model\OrderStateService; -use Magento\Signifyd\Model\SalesOrderGrid\OrderGridUpdater; -use Magento\Signifyd\Model\SignifydGateway\Gateway; -use Magento\Signifyd\Model\SignifydGateway\GatewayException; -use Psr\Log\LoggerInterface; - -/** - * Case Creation Service - * - * Creates new Case entity and register it at Signifyd - */ -class CreationService implements CaseCreationServiceInterface -{ - /** - * @var CaseManagementInterface - */ - private $caseManagement; - - /** - * @var Gateway; - */ - private $signifydGateway; - - /** - * @var LoggerInterface - */ - private $logger; - - /** - * @var CaseRepositoryInterface - */ - private $caseRepository; - - /** - * @var OrderGridUpdater - */ - private $orderGridUpdater; - - /** - * @var OrderStateService - */ - private $orderStateService; - - /** - * CreationService constructor. - * - * @param CaseManagementInterface $caseManagement - * @param Gateway $signifydGateway - * @param LoggerInterface $logger - * @param CaseRepositoryInterface $caseRepository - * @param OrderGridUpdater $orderGridUpdater - * @param OrderStateService $orderStateService - */ - public function __construct( - CaseManagementInterface $caseManagement, - Gateway $signifydGateway, - LoggerInterface $logger, - CaseRepositoryInterface $caseRepository, - OrderGridUpdater $orderGridUpdater, - OrderStateService $orderStateService - ) { - $this->caseManagement = $caseManagement; - $this->signifydGateway = $signifydGateway; - $this->logger = $logger; - $this->caseRepository = $caseRepository; - $this->orderGridUpdater = $orderGridUpdater; - $this->orderStateService = $orderStateService; - } - - /** - * {@inheritdoc} - */ - public function createForOrder($orderId) - { - $case = $this->caseManagement->create($orderId); - $this->orderGridUpdater->update($orderId); - - try { - $caseId = $this->signifydGateway->createCase($orderId); - } catch (GatewayException $e) { - $this->logger->error($e->getMessage()); - return true; - } - - $case->setCaseId($caseId); - $this->caseRepository->save($case); - $this->orderStateService->updateByCase($case); - - return true; - } -} diff --git a/app/code/Magento/Signifyd/Model/CaseServices/StubUpdatingService.php b/app/code/Magento/Signifyd/Model/CaseServices/StubUpdatingService.php deleted file mode 100644 index 295d7f13fb0ac..0000000000000 --- a/app/code/Magento/Signifyd/Model/CaseServices/StubUpdatingService.php +++ /dev/null @@ -1,23 +0,0 @@ -<?php -/** - * Copyright © Magento, Inc. All rights reserved. - * See COPYING.txt for license details. - */ -namespace Magento\Signifyd\Model\CaseServices; - -use Magento\Signifyd\Api\Data\CaseInterface; - -/** - * Stub implementation for case updating service interface and might be used - * for test Signifyd webhooks - */ -class StubUpdatingService implements UpdatingServiceInterface -{ - /** - * @inheritdoc - */ - public function update(CaseInterface $case, array $data) - { - // just stub method and doesn't contain any logic - } -} diff --git a/app/code/Magento/Signifyd/Model/CaseServices/UpdatingService.php b/app/code/Magento/Signifyd/Model/CaseServices/UpdatingService.php deleted file mode 100644 index 168ab67f8cf50..0000000000000 --- a/app/code/Magento/Signifyd/Model/CaseServices/UpdatingService.php +++ /dev/null @@ -1,119 +0,0 @@ -<?php -/** - * Copyright © Magento, Inc. All rights reserved. - * See COPYING.txt for license details. - */ -namespace Magento\Signifyd\Model\CaseServices; - -use Magento\Framework\Api\SimpleDataObjectConverter; -use Magento\Framework\Exception\LocalizedException; -use Magento\Signifyd\Api\CaseRepositoryInterface; -use Magento\Signifyd\Api\Data\CaseInterface; -use Magento\Signifyd\Model\CommentsHistoryUpdater; -use Magento\Signifyd\Model\MessageGenerators\GeneratorInterface; -use Magento\Signifyd\Model\OrderStateService; -use Magento\Signifyd\Model\SalesOrderGrid\OrderGridUpdater; - -/** - * Performs Signifyd case entity updating operations. - */ -class UpdatingService implements UpdatingServiceInterface -{ - /** - * @var GeneratorInterface - */ - private $messageGenerator; - - /** - * @var CaseRepositoryInterface - */ - private $caseRepository; - - /** - * @var CommentsHistoryUpdater - */ - private $commentsHistoryUpdater; - - /** - * @var \Magento\Signifyd\Model\SalesOrderGrid\OrderGridUpdater - */ - private $orderGridUpdater; - - /** - * @var OrderStateService - */ - private $orderStateService; - - /** - * UpdatingService constructor. - * - * @param GeneratorInterface $messageGenerator - * @param CaseRepositoryInterface $caseRepository - * @param CommentsHistoryUpdater $commentsHistoryUpdater - * @param \Magento\Signifyd\Model\SalesOrderGrid\OrderGridUpdater $orderGridUpdater - * @param OrderStateService $orderStateService - */ - public function __construct( - GeneratorInterface $messageGenerator, - CaseRepositoryInterface $caseRepository, - CommentsHistoryUpdater $commentsHistoryUpdater, - OrderGridUpdater $orderGridUpdater, - OrderStateService $orderStateService - ) { - $this->messageGenerator = $messageGenerator; - $this->caseRepository = $caseRepository; - $this->commentsHistoryUpdater = $commentsHistoryUpdater; - $this->orderGridUpdater = $orderGridUpdater; - $this->orderStateService = $orderStateService; - } - - /** - * Updates Signifyd Case entity by received data. - * - * @param CaseInterface $case - * @param array $data - * @return void - * @throws LocalizedException - */ - public function update(CaseInterface $case, array $data) - { - if (empty($case->getEntityId()) || empty($case->getCaseId())) { - throw new LocalizedException(__('The case entity should not be empty.')); - } - - try { - $previousDisposition = $case->getGuaranteeDisposition(); - $this->setCaseData($case, $data); - $orderHistoryComment = $this->messageGenerator->generate($data); - $case = $this->caseRepository->save($case); - $this->orderGridUpdater->update($case->getOrderId()); - $this->commentsHistoryUpdater->addComment($case, $orderHistoryComment); - if ($case->getGuaranteeDisposition() !== $previousDisposition) { - $this->orderStateService->updateByCase($case); - } - } catch (\Exception $e) { - throw new LocalizedException(__('Cannot update Case entity.'), $e); - } - } - - /** - * Sets data to case entity. - * - * @param CaseInterface $case - * @param array $data - * @return void - */ - private function setCaseData(CaseInterface $case, array $data) - { - // list of keys which should not be replaced, like order id - $notResolvedKeys = [ - 'orderId' - ]; - foreach ($data as $key => $value) { - $methodName = 'set' . SimpleDataObjectConverter::snakeCaseToUpperCamelCase($key); - if (!in_array($key, $notResolvedKeys) && method_exists($case, $methodName)) { - call_user_func([$case, $methodName], $value); - } - } - } -} diff --git a/app/code/Magento/Signifyd/Model/CaseServices/UpdatingServiceFactory.php b/app/code/Magento/Signifyd/Model/CaseServices/UpdatingServiceFactory.php deleted file mode 100644 index 5415044b5edc4..0000000000000 --- a/app/code/Magento/Signifyd/Model/CaseServices/UpdatingServiceFactory.php +++ /dev/null @@ -1,78 +0,0 @@ -<?php -/** - * Copyright © Magento, Inc. All rights reserved. - * See COPYING.txt for license details. - */ -namespace Magento\Signifyd\Model\CaseServices; - -use Magento\Framework\ObjectManagerInterface; -use Magento\Signifyd\Model\MessageGenerators\GeneratorFactory; -use Magento\Signifyd\Model\Config; - -/** - * Creates instance of case updating service configured with specific message generator. - * The message generator initialization depends on specified type (like, case creation, re-scoring, review and - * guarantee completion). - */ -class UpdatingServiceFactory -{ - /** - * Type of testing Signifyd case - * @var string - */ - private static $caseTest = 'cases/test'; - - /** - * @var ObjectManagerInterface - */ - private $objectManager; - - /** - * @var GeneratorFactory - */ - private $generatorFactory; - - /** - * @var Config - */ - private $config; - - /** - * UpdatingServiceFactory constructor. - * - * @param ObjectManagerInterface $objectManager - * @param GeneratorFactory $generatorFactory - * @param Config $config - */ - public function __construct( - ObjectManagerInterface $objectManager, - GeneratorFactory $generatorFactory, - Config $config - ) { - $this->objectManager = $objectManager; - $this->generatorFactory = $generatorFactory; - $this->config = $config; - } - - /** - * Creates instance of service updating case. - * As param retrieves type of message generator. - * - * @param string $type - * @return UpdatingServiceInterface - * @throws \InvalidArgumentException - */ - public function create($type) - { - if (!$this->config->isActive() || $type === self::$caseTest) { - return $this->objectManager->create(StubUpdatingService::class); - } - - $messageGenerator = $this->generatorFactory->create($type); - $service = $this->objectManager->create(UpdatingService::class, [ - 'messageGenerator' => $messageGenerator - ]); - - return $service; - } -} diff --git a/app/code/Magento/Signifyd/Model/CaseServices/UpdatingServiceInterface.php b/app/code/Magento/Signifyd/Model/CaseServices/UpdatingServiceInterface.php deleted file mode 100644 index daa7b40bfd674..0000000000000 --- a/app/code/Magento/Signifyd/Model/CaseServices/UpdatingServiceInterface.php +++ /dev/null @@ -1,23 +0,0 @@ -<?php -/** - * Copyright © Magento, Inc. All rights reserved. - * See COPYING.txt for license details. - */ -namespace Magento\Signifyd\Model\CaseServices; - -use Magento\Signifyd\Api\Data\CaseInterface; - -/** - * Common abstraction to perform updating operations with Signifyd case entity. - */ -interface UpdatingServiceInterface -{ - /** - * Updates Signifyd Case entity by received data. - * - * @param CaseInterface $case - * @param array $data - * @return void - */ - public function update(CaseInterface $case, array $data); -} diff --git a/app/code/Magento/Signifyd/Model/CommentsHistoryUpdater.php b/app/code/Magento/Signifyd/Model/CommentsHistoryUpdater.php deleted file mode 100644 index f473d157efa4a..0000000000000 --- a/app/code/Magento/Signifyd/Model/CommentsHistoryUpdater.php +++ /dev/null @@ -1,65 +0,0 @@ -<?php -/** - * Copyright © Magento, Inc. All rights reserved. - * See COPYING.txt for license details. - */ -namespace Magento\Signifyd\Model; - -use Magento\Framework\Phrase; -use Magento\Sales\Api\OrderStatusHistoryRepositoryInterface; -use Magento\Sales\Model\Order\Status\HistoryFactory; -use Magento\Signifyd\Api\Data\CaseInterface; - -/** - * Updates case order comments history. - */ -class CommentsHistoryUpdater -{ - /** - * @var HistoryFactory - */ - private $historyFactory; - - /** - * @var OrderStatusHistoryRepositoryInterface - */ - private $historyRepository; - - /** - * CommentsHistoryUpdater constructor. - * - * @param HistoryFactory $historyFactory - * @param OrderStatusHistoryRepositoryInterface $historyRepository - */ - public function __construct( - HistoryFactory $historyFactory, - OrderStatusHistoryRepositoryInterface $historyRepository - ) { - $this->historyFactory = $historyFactory; - $this->historyRepository = $historyRepository; - } - - /** - * Adds comment to case related order. - * Throws an exception if cannot save history comment. - * - * @param CaseInterface $case - * @param Phrase $message - * @param string $status - * @return void - */ - public function addComment(CaseInterface $case, Phrase $message, $status = '') - { - if (!$message->getText()) { - return; - } - - /** @var \Magento\Sales\Api\Data\OrderStatusHistoryInterface $history */ - $history = $this->historyFactory->create(); - $history->setParentId($case->getOrderId()) - ->setComment($message) - ->setEntityName('order') - ->setStatus($status); - $this->historyRepository->save($history); - } -} diff --git a/app/code/Magento/Signifyd/Model/Config.php b/app/code/Magento/Signifyd/Model/Config.php deleted file mode 100644 index 15d3608bd38c4..0000000000000 --- a/app/code/Magento/Signifyd/Model/Config.php +++ /dev/null @@ -1,101 +0,0 @@ -<?php -/** - * Copyright © Magento, Inc. All rights reserved. - * See COPYING.txt for license details. - */ -namespace Magento\Signifyd\Model; - -use Magento\Framework\App\Config\ScopeConfigInterface; -use Magento\Store\Model\ScopeInterface; - -/** - * Signifyd integration configuration. - * - * Class is a proxy service for retrieving configuration settings. - */ -class Config -{ - /** - * @var ScopeConfigInterface - */ - private $scopeConfig; - - /** - * Config constructor. - * - * @param ScopeConfigInterface $scopeConfig - */ - public function __construct(ScopeConfigInterface $scopeConfig) - { - $this->scopeConfig = $scopeConfig; - } - - /** - * If this config option set to false no Signifyd integration should be available - * (only possibility to configure Signifyd setting in admin) - * - * @param int|null $storeId - * @return bool - */ - public function isActive($storeId = null): bool - { - $enabled = $this->scopeConfig->isSetFlag( - 'fraud_protection/signifyd/active', - ScopeInterface::SCOPE_STORE, - $storeId - ); - return $enabled; - } - - /** - * Signifyd API Key used for authentication. - * - * @see https://www.signifyd.com/docs/api/#/introduction/authentication - * @see https://app.signifyd.com/settings - * - * @param int|null $storeId - * @return string - */ - public function getApiKey($storeId = null): string - { - $apiKey = $this->scopeConfig->getValue( - 'fraud_protection/signifyd/api_key', - ScopeInterface::SCOPE_STORE, - $storeId - ); - return $apiKey; - } - - /** - * Base URL to Signifyd REST API. - * Usually equals to https://api.signifyd.com/v2 and should not be changed - * - * @param int|null $storeId - * @return string - */ - public function getApiUrl($storeId = null): string - { - $apiUrl = $this->scopeConfig->getValue( - 'fraud_protection/signifyd/api_url', - ScopeInterface::SCOPE_STORE, - $storeId - ); - return $apiUrl; - } - - /** - * If is "true" extra information about interaction with Signifyd API are written to debug.log file - * - * @param int|null $storeId - * @return bool - */ - public function isDebugModeEnabled($storeId = null): bool - { - $debugModeEnabled = $this->scopeConfig->isSetFlag( - 'fraud_protection/signifyd/debug', - ScopeInterface::SCOPE_STORE, - $storeId - ); - return $debugModeEnabled; - } -} diff --git a/app/code/Magento/Signifyd/Model/CustomerOrders.php b/app/code/Magento/Signifyd/Model/CustomerOrders.php deleted file mode 100644 index c326cf06424c0..0000000000000 --- a/app/code/Magento/Signifyd/Model/CustomerOrders.php +++ /dev/null @@ -1,173 +0,0 @@ -<?php -/** - * Copyright © Magento, Inc. All rights reserved. - * See COPYING.txt for license details. - */ -namespace Magento\Signifyd\Model; - -use Exception; -use Magento\Directory\Model\Currency; -use Magento\Directory\Model\CurrencyFactory; -use Magento\Framework\Api\FilterBuilder; -use Magento\Framework\Api\SearchCriteriaBuilder; -use Magento\Sales\Api\Data\OrderInterface; -use Magento\Sales\Api\OrderRepositoryInterface; -use Psr\Log\LoggerInterface; - -/** - * Provides information about customer orders. - * - * @SuppressWarnings(PHPMD.CouplingBetweenObjects) - */ -class CustomerOrders -{ - /** - * @var SearchCriteriaBuilder - */ - private $searchCriteriaBuilder; - - /** - * @var FilterBuilder - */ - private $filterBuilder; - - /** - * @var OrderRepositoryInterface - */ - private $orderRepository; - - /** - * @var LoggerInterface - */ - private $logger; - - /** - * @var CurrencyFactory - */ - private $currencyFactory; - - /** - * @var array - */ - private $currencies = []; - - /** - * @var string - */ - private static $usdCurrencyCode = 'USD'; - - /** - * @param SearchCriteriaBuilder $searchCriteriaBuilder - * @param FilterBuilder $filterBuilder - * @param OrderRepositoryInterface $orderRepository - * @param CurrencyFactory $currencyFactory - * @param LoggerInterface $logger - */ - public function __construct( - SearchCriteriaBuilder $searchCriteriaBuilder, - FilterBuilder $filterBuilder, - OrderRepositoryInterface $orderRepository, - CurrencyFactory $currencyFactory, - LoggerInterface $logger - ) { - $this->searchCriteriaBuilder = $searchCriteriaBuilder; - $this->filterBuilder = $filterBuilder; - $this->orderRepository = $orderRepository; - $this->currencyFactory = $currencyFactory; - $this->logger = $logger; - } - - /** - * Returns aggregated customer orders count and total amount in USD. - * - * Returned array contains next keys: - * aggregateOrderCount - total count of orders placed by this account since it was created, including the current - * aggregateOrderDollars - total amount spent by this account since it was created, including the current order - * - * @param int $customerId - * @return array - */ - public function getAggregatedOrdersInfo($customerId) - { - $result = [ - 'aggregateOrderCount' => null, - 'aggregateOrderDollars' => null - ]; - - $customerOrders = $this->getCustomerOrders($customerId); - if (!empty($customerOrders)) { - try { - $orderTotalDollars = 0.0; - foreach ($customerOrders as $order) { - $orderTotalDollars += $this->getUsdOrderTotal( - $order->getBaseGrandTotal(), - $order->getBaseCurrencyCode() - ); - } - $result = [ - 'aggregateOrderCount' => count($customerOrders), - 'aggregateOrderDollars' => $orderTotalDollars - ]; - } catch (Exception $e) { - $this->logger->error($e->getMessage()); - } - } - - return $result; - } - - /** - * Returns customer orders. - * - * @param int $customerId - * @return OrderInterface[] - */ - private function getCustomerOrders($customerId) - { - $filters = [ - $this->filterBuilder->setField(OrderInterface::CUSTOMER_ID)->setValue($customerId)->create() - ]; - $this->searchCriteriaBuilder->addFilters($filters); - $searchCriteria = $this->searchCriteriaBuilder->create(); - $searchResults = $this->orderRepository->getList($searchCriteria); - - return $searchResults->getItems(); - } - - /** - * Returns amount in USD. - * - * @param float $amount - * @param string $currency - * @return float - */ - private function getUsdOrderTotal($amount, $currency) - { - if ($currency === self::$usdCurrencyCode) { - return $amount; - } - - $operationCurrency = $this->getCurrencyByCode($currency); - - return $operationCurrency->convert($amount, self::$usdCurrencyCode); - } - - /** - * Returns currency by currency code. - * - * @param string|null $currencyCode - * @return Currency - */ - private function getCurrencyByCode($currencyCode) - { - if (isset($this->currencies[$currencyCode])) { - return $this->currencies[$currencyCode]; - } - - /** @var Currency $currency */ - $currency = $this->currencyFactory->create(); - $this->currencies[$currencyCode] = $currency->load($currencyCode); - - return $this->currencies[$currencyCode]; - } -} diff --git a/app/code/Magento/Signifyd/Model/Guarantee/CancelGuaranteeAbility.php b/app/code/Magento/Signifyd/Model/Guarantee/CancelGuaranteeAbility.php deleted file mode 100644 index 65bede6f0cea5..0000000000000 --- a/app/code/Magento/Signifyd/Model/Guarantee/CancelGuaranteeAbility.php +++ /dev/null @@ -1,84 +0,0 @@ -<?php -/** - * Copyright © Magento, Inc. All rights reserved. - * See COPYING.txt for license details. - */ -namespace Magento\Signifyd\Model\Guarantee; - -use Magento\Framework\Exception\InputException; -use Magento\Framework\Exception\NoSuchEntityException; -use Magento\Sales\Api\Data\OrderInterface; -use Magento\Sales\Api\OrderRepositoryInterface; -use Magento\Signifyd\Model\CaseManagement; - -/** - * Checks if is possible to cancel Guarantee for order. - */ -class CancelGuaranteeAbility -{ - /** - * @var CaseManagement - */ - private $caseManagement; - - /** - * @var OrderRepositoryInterface - */ - private $orderRepository; - - /** - * @param CaseManagement $caseManagement - * @param OrderRepositoryInterface $orderRepository - */ - public function __construct( - CaseManagement $caseManagement, - OrderRepositoryInterface $orderRepository - ) { - $this->caseManagement = $caseManagement; - $this->orderRepository = $orderRepository; - } - - /** - * Checks if it is possible to create Guarantee for order and case. - * - * @param int $orderId - * @return bool - */ - public function isAvailable($orderId) - { - $case = $this->caseManagement->getByOrderId($orderId); - if ($case === null) { - return false; - } - - if (in_array($case->getGuaranteeDisposition(), [null, $case::GUARANTEE_CANCELED])) { - return false; - } - - $order = $this->getOrder($orderId); - if (null === $order) { - return false; - } - - return true; - } - - /** - * Returns order by id - * - * @param int $orderId - * @return OrderInterface|null - */ - private function getOrder($orderId) - { - try { - $order = $this->orderRepository->get($orderId); - } catch (InputException $e) { - return null; - } catch (NoSuchEntityException $e) { - return null; - } - - return $order; - } -} diff --git a/app/code/Magento/Signifyd/Model/Guarantee/CancelingService.php b/app/code/Magento/Signifyd/Model/Guarantee/CancelingService.php deleted file mode 100644 index b30efac8c2190..0000000000000 --- a/app/code/Magento/Signifyd/Model/Guarantee/CancelingService.php +++ /dev/null @@ -1,94 +0,0 @@ -<?php -/** - * Copyright © Magento, Inc. All rights reserved. - * See COPYING.txt for license details. - */ -namespace Magento\Signifyd\Model\Guarantee; - -use Magento\Signifyd\Api\CaseManagementInterface; -use Magento\Signifyd\Api\GuaranteeCancelingServiceInterface; -use Magento\Signifyd\Model\CaseServices\UpdatingServiceFactory; -use Magento\Signifyd\Model\SignifydGateway\Gateway; -use Magento\Signifyd\Model\SignifydGateway\GatewayException; -use Psr\Log\LoggerInterface; - -/** - * Sends request to Signifyd to cancel guarantee and updates case entity. - */ -class CancelingService implements GuaranteeCancelingServiceInterface -{ - /** - * @var CaseManagementInterface - */ - private $caseManagement; - - /** - * @var UpdatingServiceFactory - */ - private $serviceFactory; - - /** - * @var Gateway - */ - private $gateway; - - /** - * @var CancelGuaranteeAbility - */ - private $cancelGuaranteeAbility; - - /** - * @var LoggerInterface - */ - private $logger; - - /** - * CancelingService constructor. - * - * @param CaseManagementInterface $caseManagement - * @param UpdatingServiceFactory $serviceFactory - * @param Gateway $gateway - * @param CancelGuaranteeAbility $cancelGuaranteeAbility - * @param LoggerInterface $logger - */ - public function __construct( - CaseManagementInterface $caseManagement, - UpdatingServiceFactory $serviceFactory, - Gateway $gateway, - CancelGuaranteeAbility $cancelGuaranteeAbility, - LoggerInterface $logger - ) { - $this->caseManagement = $caseManagement; - $this->serviceFactory = $serviceFactory; - $this->gateway = $gateway; - $this->cancelGuaranteeAbility = $cancelGuaranteeAbility; - $this->logger = $logger; - } - - /** - * @inheritdoc - */ - public function cancelForOrder($orderId) - { - if (!$this->cancelGuaranteeAbility->isAvailable($orderId)) { - return false; - } - - $caseEntity = $this->caseManagement->getByOrderId($orderId); - - try { - $disposition = $this->gateway->cancelGuarantee($caseEntity->getCaseId()); - } catch (GatewayException $e) { - $this->logger->error($e->getMessage()); - return false; - } - - $updatingService = $this->serviceFactory->create('guarantees/cancel'); - $data = [ - 'guaranteeDisposition' => $disposition - ]; - $updatingService->update($caseEntity, $data); - - return true; - } -} diff --git a/app/code/Magento/Signifyd/Model/Guarantee/CreateGuaranteeAbility.php b/app/code/Magento/Signifyd/Model/Guarantee/CreateGuaranteeAbility.php deleted file mode 100644 index 15addba3ec4fd..0000000000000 --- a/app/code/Magento/Signifyd/Model/Guarantee/CreateGuaranteeAbility.php +++ /dev/null @@ -1,124 +0,0 @@ -<?php -/** - * Copyright © Magento, Inc. All rights reserved. - * See COPYING.txt for license details. - */ -namespace Magento\Signifyd\Model\Guarantee; - -use Magento\Framework\Exception\InputException; -use Magento\Framework\Exception\NoSuchEntityException; -use Magento\Framework\Intl\DateTimeFactory; -use Magento\Sales\Api\Data\OrderInterface; -use Magento\Sales\Api\OrderRepositoryInterface; -use Magento\Sales\Model\Order; -use Magento\Signifyd\Model\CaseManagement; - -/** - * Checks if is possible to create Guarantee for order. - */ -class CreateGuaranteeAbility -{ - /** - * @var CaseManagement - */ - private $caseManagement; - - /** - * @var OrderRepositoryInterface - */ - private $orderRepository; - - /** - * @var DateTimeFactory - */ - private $dateTimeFactory; - - /** - * Eligible count of days from the order creation date to submit a case for Guarantee. - * - * @var int - */ - private static $guarantyEligibleDays = 7; - - /** - * @param CaseManagement $caseManagement - * @param OrderRepositoryInterface $orderRepository - * @param DateTimeFactory $dateTimeFactory - */ - public function __construct( - CaseManagement $caseManagement, - OrderRepositoryInterface $orderRepository, - DateTimeFactory $dateTimeFactory - ) { - $this->caseManagement = $caseManagement; - $this->orderRepository = $orderRepository; - $this->dateTimeFactory = $dateTimeFactory; - } - - /** - * Checks if it is possible to create Guarantee for order and case. - * - * @param int $orderId - * @return bool - */ - public function isAvailable($orderId) - { - $case = $this->caseManagement->getByOrderId($orderId); - if (null === $case) { - return false; - } - - if ($case->isGuaranteeEligible() === false) { - return false; - } - - $order = $this->getOrder($orderId); - if (null === $order) { - return false; - } - - if (in_array($order->getState(), [Order::STATE_CANCELED, Order::STATE_CLOSED])) { - return false; - } - - if ($this->isOrderOlderThen(static::$guarantyEligibleDays, $order)) { - return false; - } - - return true; - } - - /** - * Checks if Guarantee submit is applicable for order. - * - * @param OrderInterface $order - * @param int $days number of days from the order creation date to submit a case for Guarantee. - * @return bool - */ - private function isOrderOlderThen($days, OrderInterface $order) - { - $orderCreateDate = $this->dateTimeFactory->create($order->getCreatedAt(), new \DateTimeZone('UTC')); - $currentDate = $this->dateTimeFactory->create('now', new \DateTimeZone('UTC')); - - return $orderCreateDate->diff($currentDate)->days >= $days; - } - - /** - * Returns order by id - * - * @param int $orderId - * @return OrderInterface|null - */ - private function getOrder($orderId) - { - try { - $order = $this->orderRepository->get($orderId); - } catch (InputException $e) { - return null; - } catch (NoSuchEntityException $e) { - return null; - } - - return $order; - } -} diff --git a/app/code/Magento/Signifyd/Model/Guarantee/CreationService.php b/app/code/Magento/Signifyd/Model/Guarantee/CreationService.php deleted file mode 100644 index 4080aee453f18..0000000000000 --- a/app/code/Magento/Signifyd/Model/Guarantee/CreationService.php +++ /dev/null @@ -1,97 +0,0 @@ -<?php -/** - * Copyright © Magento, Inc. All rights reserved. - * See COPYING.txt for license details. - */ -namespace Magento\Signifyd\Model\Guarantee; - -use Magento\Framework\Exception\AlreadyExistsException; -use Magento\Framework\Exception\NotFoundException; -use Magento\Signifyd\Api\CaseManagementInterface; -use Magento\Signifyd\Api\GuaranteeCreationServiceInterface; -use Magento\Signifyd\Model\CaseServices\UpdatingServiceFactory; -use Magento\Signifyd\Model\SignifydGateway\Gateway; -use Magento\Signifyd\Model\SignifydGateway\GatewayException; -use Psr\Log\LoggerInterface; - -/** - * Register guarantee at Signifyd and updates case entity - */ -class CreationService implements GuaranteeCreationServiceInterface -{ - /** - * @var CaseManagementInterface - */ - private $caseManagement; - - /** - * @var UpdatingServiceFactory - */ - private $caseUpdatingServiceFactory; - - /** - * @var Gateway - */ - private $gateway; - - /** - * @var CreateGuaranteeAbility - */ - private $createGuaranteeAbility; - - /** - * @var LoggerInterface - */ - private $logger; - - /** - * CreationService constructor. - * - * @param CaseManagementInterface $caseManagement - * @param UpdatingServiceFactory $caseUpdatingServiceFactory - * @param Gateway $gateway - * @param CreateGuaranteeAbility $createGuaranteeAbility - * @param LoggerInterface $logger - */ - public function __construct( - CaseManagementInterface $caseManagement, - UpdatingServiceFactory $caseUpdatingServiceFactory, - Gateway $gateway, - CreateGuaranteeAbility $createGuaranteeAbility, - LoggerInterface $logger - ) { - $this->caseManagement = $caseManagement; - $this->caseUpdatingServiceFactory = $caseUpdatingServiceFactory; - $this->gateway = $gateway; - $this->createGuaranteeAbility = $createGuaranteeAbility; - $this->logger = $logger; - } - - /** - * @inheritdoc - */ - public function createForOrder($orderId) - { - if (!$this->createGuaranteeAbility->isAvailable($orderId)) { - return false; - } - - $caseEntity = $this->caseManagement->getByOrderId($orderId); - - try { - $disposition = $this->gateway->submitCaseForGuarantee($caseEntity->getCaseId()); - } catch (GatewayException $e) { - $this->logger->error($e->getMessage()); - return false; - } - - $updatingService = $this->caseUpdatingServiceFactory->create('guarantees/creation'); - $data = [ - 'caseId' => $caseEntity->getCaseId(), - 'guaranteeDisposition' => $disposition - ]; - $updatingService->update($caseEntity, $data); - - return true; - } -} diff --git a/app/code/Magento/Signifyd/Model/MessageGenerators/CaseRescore.php b/app/code/Magento/Signifyd/Model/MessageGenerators/CaseRescore.php deleted file mode 100644 index d0e89854e3909..0000000000000 --- a/app/code/Magento/Signifyd/Model/MessageGenerators/CaseRescore.php +++ /dev/null @@ -1,51 +0,0 @@ -<?php -/** - * Copyright © Magento, Inc. All rights reserved. - * See COPYING.txt for license details. - */ -namespace Magento\Signifyd\Model\MessageGenerators; - -use Magento\Signifyd\Api\CaseRepositoryInterface; - -/** - * Generates message based on previous and current Case scores. - */ -class CaseRescore implements GeneratorInterface -{ - /** - * @var CaseRepositoryInterface - */ - private $caseRepository; - - /** - * CaseRescore constructor. - * - * @param CaseRepositoryInterface $caseRepository - */ - public function __construct(CaseRepositoryInterface $caseRepository) - { - $this->caseRepository = $caseRepository; - } - - /** - * @inheritdoc - */ - public function generate(array $data) - { - if (empty($data['caseId'])) { - throw new GeneratorException(__('The "%1" should not be empty.', 'caseId')); - } - - $caseEntity = $this->caseRepository->getByCaseId($data['caseId']); - - if ($caseEntity === null) { - throw new GeneratorException(__('Case entity not found.')); - } - - return __( - 'Case Update: New score for the order is %1. Previous score was %2.', - !empty($data['score']) ? $data['score'] : 0, - $caseEntity->getScore() - ); - } -} diff --git a/app/code/Magento/Signifyd/Model/MessageGenerators/GeneratorException.php b/app/code/Magento/Signifyd/Model/MessageGenerators/GeneratorException.php deleted file mode 100644 index 103cb9fc1e2d3..0000000000000 --- a/app/code/Magento/Signifyd/Model/MessageGenerators/GeneratorException.php +++ /dev/null @@ -1,19 +0,0 @@ -<?php -/** - * Copyright © Magento, Inc. All rights reserved. - * See COPYING.txt for license details. - */ -namespace Magento\Signifyd\Model\MessageGenerators; - -use Magento\Framework\Exception\LocalizedException; - -/** - * Common exception for Signifyd message generators. - * - * @api - * @since 100.2.0 - */ -class GeneratorException extends LocalizedException -{ - -} diff --git a/app/code/Magento/Signifyd/Model/MessageGenerators/GeneratorFactory.php b/app/code/Magento/Signifyd/Model/MessageGenerators/GeneratorFactory.php deleted file mode 100644 index 66e02a3803950..0000000000000 --- a/app/code/Magento/Signifyd/Model/MessageGenerators/GeneratorFactory.php +++ /dev/null @@ -1,112 +0,0 @@ -<?php -/** - * Copyright © Magento, Inc. All rights reserved. - * See COPYING.txt for license details. - */ -namespace Magento\Signifyd\Model\MessageGenerators; - -use Magento\Framework\ObjectManagerInterface; -use Magento\Signifyd\Model\MessageGeneratorInterface; - -/** - * Creates instance of message generator based on received type of message. - */ -class GeneratorFactory -{ - /** - * Type of message for Signifyd case creation. - * @var string - */ - private static $caseCreation = 'cases/creation'; - - /** - * Type of message for Signifyd case re-scoring. - * @var string - */ - private static $caseRescore = 'cases/rescore'; - - /** - * Type of message for Signifyd case reviewing - * @var string - */ - private static $caseReview = 'cases/review'; - - /** - * Type of message of Signifyd guarantee completion - * @var string - */ - private static $guaranteeCompletion = 'guarantees/completion'; - - /** - * Type of message of Signifyd guarantee creation - * @var string - */ - private static $guaranteeCreation = 'guarantees/creation'; - - /** - * Type of message of Signifyd guarantee canceling - * @var string - */ - private static $guaranteeCancel = 'guarantees/cancel'; - - /** - * UpdatingServiceFactory constructor. - * - * @param ObjectManagerInterface $objectManager - */ - public function __construct(ObjectManagerInterface $objectManager) - { - $this->objectManager = $objectManager; - } - - /** - * Creates instance of message generator. - * Throws exception if type of message generator does not have implementations. - * - * @param string $type - * @return GeneratorInterface - * @throws \InvalidArgumentException - */ - public function create($type) - { - $className = PatternGenerator::class; - switch ($type) { - case self::$caseCreation: - $classConfig = [ - 'template' => 'Signifyd Case %1 has been created for order.', - 'requiredParams' => ['caseId'] - ]; - break; - case self::$caseRescore: - $classConfig = []; - $className = CaseRescore::class; - break; - case self::$caseReview: - $classConfig = [ - 'template' => 'Case Update: Case Review was completed. Review Deposition is %1.', - 'requiredParams' => ['reviewDisposition'] - ]; - break; - case self::$guaranteeCompletion: - $classConfig = [ - 'template' => 'Case Update: Guarantee Disposition is %1.', - 'requiredParams' => ['guaranteeDisposition'] - ]; - break; - case self::$guaranteeCreation: - $classConfig = [ - 'template' => 'Case Update: Case is submitted for guarantee.' - ]; - break; - case self::$guaranteeCancel: - $classConfig = [ - 'template' => 'Case Update: Case guarantee has been cancelled.' - ]; - break; - default: - throw new \InvalidArgumentException('Specified message type does not supported.'); - } - - return $this->objectManager->create($className, $classConfig); - } -} diff --git a/app/code/Magento/Signifyd/Model/MessageGenerators/GeneratorInterface.php b/app/code/Magento/Signifyd/Model/MessageGenerators/GeneratorInterface.php deleted file mode 100644 index 385cbe35f05ac..0000000000000 --- a/app/code/Magento/Signifyd/Model/MessageGenerators/GeneratorInterface.php +++ /dev/null @@ -1,21 +0,0 @@ -<?php -/** - * Copyright © Magento, Inc. All rights reserved. - * See COPYING.txt for license details. - */ -namespace Magento\Signifyd\Model\MessageGenerators; - -/** - * Represents common abstraction for Signifyd Case/Guarantee messages. - * Each interface implementation might use Case/Guarantee data to generate specific message. - */ -interface GeneratorInterface -{ - /** - * Creates new localized message based on Signifyd Case/Guarantee data. - * @param array $data - * @return \Magento\Framework\Phrase - * @throws GeneratorException - */ - public function generate(array $data); -} diff --git a/app/code/Magento/Signifyd/Model/MessageGenerators/PatternGenerator.php b/app/code/Magento/Signifyd/Model/MessageGenerators/PatternGenerator.php deleted file mode 100644 index 8501934bebbc5..0000000000000 --- a/app/code/Magento/Signifyd/Model/MessageGenerators/PatternGenerator.php +++ /dev/null @@ -1,56 +0,0 @@ -<?php -/** - * Copyright © Magento, Inc. All rights reserved. - * See COPYING.txt for license details. - */ -namespace Magento\Signifyd\Model\MessageGenerators; - -/** - * Common implementation of message generator. - * Takes a message template (placeholders for localization also can be used) and list - * of required params, which should persist in input data. - * - * If template contains placeholders, when required params should be specified in the same order as - * placeholders, for example: - * Message is 'Case Update: New score for the order is %1. Previous score was %2.', then the required params order - * should be ['new_score', 'prev_score']. - */ -class PatternGenerator implements GeneratorInterface -{ - /** - * @var string - */ - private $template; - - /** - * @var array - */ - private $requiredParams; - - /** - * PatternGenerator constructor. - * - * @param string $template - * @param array $requiredParams - */ - public function __construct($template, array $requiredParams = []) - { - $this->template = $template; - $this->requiredParams = $requiredParams; - } - - /** - * @inheritdoc - */ - public function generate(array $data) - { - $placeholders = []; - foreach ($this->requiredParams as $param) { - if (empty($data[$param])) { - throw new GeneratorException(__('The "%1" should not be empty.', $param)); - } - $placeholders[] = $data[$param]; - } - return __($this->template, ...$placeholders); - } -} diff --git a/app/code/Magento/Signifyd/Model/OrderStateService.php b/app/code/Magento/Signifyd/Model/OrderStateService.php deleted file mode 100644 index 2b3f0e155981e..0000000000000 --- a/app/code/Magento/Signifyd/Model/OrderStateService.php +++ /dev/null @@ -1,119 +0,0 @@ -<?php -/** - * Copyright © Magento, Inc. All rights reserved. - * See COPYING.txt for license details. - */ -namespace Magento\Signifyd\Model; - -use Magento\Sales\Api\OrderManagementInterface; -use Magento\Sales\Model\Order; -use Magento\Sales\Model\OrderFactory; -use Magento\Signifyd\Api\Data\CaseInterface; - -/** - * Updates order state. - */ -class OrderStateService -{ - /** - * @var OrderFactory - */ - private $orderFactory; - - /** - * @var OrderManagementInterface - */ - private $orderManagement; - - /** - * @var CommentsHistoryUpdater - */ - private $commentsHistoryUpdater; - - /** - * @param OrderFactory $orderFactory - * @param OrderManagementInterface $orderManagement - * @param CommentsHistoryUpdater $commentsHistoryUpdater - */ - public function __construct( - OrderFactory $orderFactory, - OrderManagementInterface $orderManagement, - CommentsHistoryUpdater $commentsHistoryUpdater - ) { - $this->orderFactory = $orderFactory; - $this->orderManagement = $orderManagement; - $this->commentsHistoryUpdater = $commentsHistoryUpdater; - } - - /** - * Updates order state depending on case guarantee disposition status. - * - * @param CaseInterface $case - * @return void - */ - public function updateByCase(CaseInterface $case) - { - $orderId = $case->getOrderId(); - - switch ($case->getGuaranteeDisposition()) { - case CaseInterface::GUARANTEE_APPROVED: - $this->unHold($orderId); - break; - case CaseInterface::GUARANTEE_DECLINED: - $this->hold($orderId); - break; - case CaseInterface::GUARANTEE_PENDING: - if ($this->hold($orderId)) { - $this->commentsHistoryUpdater->addComment( - $case, - __('Awaiting the Signifyd guarantee disposition.'), - Order::STATE_HOLDED - ); - } - break; - } - } - - /** - * Tries to unhold the order. - * - * @param int $orderId - * @return bool - */ - private function unHold($orderId) - { - $order = $this->getOrder($orderId); - if ($order->canUnhold()) { - return $this->orderManagement->unHold($orderId); - } - - return false; - } - - /** - * Tries to hold the order. - * - * @param int $orderId - * @return bool - */ - private function hold($orderId) - { - $order = $this->getOrder($orderId); - if ($order->canHold()) { - return $this->orderManagement->hold($orderId); - } - - return false; - } - - /** - * Returns the order. - * - * @param int $orderId - * @return Order - */ - private function getOrder($orderId) - { - return $this->orderFactory->create()->load($orderId); - } -} diff --git a/app/code/Magento/Signifyd/Model/PaymentMethodMapper/PaymentMethodMapper.php b/app/code/Magento/Signifyd/Model/PaymentMethodMapper/PaymentMethodMapper.php deleted file mode 100644 index cdf9041510b45..0000000000000 --- a/app/code/Magento/Signifyd/Model/PaymentMethodMapper/PaymentMethodMapper.php +++ /dev/null @@ -1,40 +0,0 @@ -<?php -/** - * Copyright © Magento, Inc. All rights reserved. - * See COPYING.txt for license details. - */ -namespace Magento\Signifyd\Model\PaymentMethodMapper; - -use Magento\Framework\Config\Data; - -/** - * Load and cache configuration data. - */ -class PaymentMethodMapper -{ - /** - * @var Data - */ - private $paymentMethodMapping; - - /** - * PaymentMapper constructor. - * - * @param Data $paymentMapping - */ - public function __construct(Data $paymentMapping) - { - $this->paymentMethodMapping = $paymentMapping; - } - - /** - * Gets the Sygnifyd payment method by the order's payment method. - * - * @param string $paymentMethod - * @return string - */ - public function getSignifydPaymentMethodCode($paymentMethod) - { - return $this->paymentMethodMapping->get($paymentMethod, ''); - } -} diff --git a/app/code/Magento/Signifyd/Model/PaymentMethodMapper/XmlToArrayConfigConverter.php b/app/code/Magento/Signifyd/Model/PaymentMethodMapper/XmlToArrayConfigConverter.php deleted file mode 100644 index 5c3f23bb92729..0000000000000 --- a/app/code/Magento/Signifyd/Model/PaymentMethodMapper/XmlToArrayConfigConverter.php +++ /dev/null @@ -1,88 +0,0 @@ -<?php -/** - * Copyright © Magento, Inc. All rights reserved. - * See COPYING.txt for license details. - */ -namespace Magento\Signifyd\Model\PaymentMethodMapper; - -use Magento\Framework\Config\Dom\ValidationSchemaException; - -/** - * Converts XML config file to payment methods mapping. - */ -class XmlToArrayConfigConverter implements \Magento\Framework\Config\ConverterInterface -{ - /** - * Node type wrapper for magento and signifyd payment codes - * - * @var string - */ - private static $paymentMethodNodeType = 'payment_method'; - - /** - * Node type for payment methods code - * - * @var string - */ - private static $magentoCodeNodeType = 'magento_code'; - - /** - * Node type for Sygnifyd payment methods code - * - * @var string - */ - private static $signifydCodeNodeType = 'signifyd_code'; - - /** - * @inheritdoc - */ - public function convert($source) - { - $paymentMethods = $source->getElementsByTagName(self::$paymentMethodNodeType); - $paymentsList = []; - foreach ($paymentMethods as $paymentMethod) { - $paymentsList += $this->getPaymentMethodMapping($paymentMethod); - } - - return $paymentsList; - } - - /** - * Adds a payment method as key and a Sygnifyd payment method as value - * in the payment list array - * - * @param \DOMElement $payment - * @return array - * @throws ValidationSchemaException - */ - private function getPaymentMethodMapping(\DOMElement $payment) - { - $paymentMethodCode = $this->readSubnodeValue($payment, self::$magentoCodeNodeType); - $signifyPaymentMethodCode = $this->readSubnodeValue($payment, self::$signifydCodeNodeType); - - return [$paymentMethodCode => $signifyPaymentMethodCode]; - } - - /** - * Reads node value by node type - * - * @param \DOMElement $element - * @param string $subNodeType - * @return mixed - * @throws ValidationSchemaException - */ - private function readSubnodeValue(\DOMElement $element, $subNodeType) - { - $domList = $element->getElementsByTagName($subNodeType); - if (empty($domList[0])) { - throw new ValidationSchemaException(__('Only single entrance of "%1" node is required.', $subNodeType)); - } - - $subNodeValue = trim($domList[0]->nodeValue); - if (!$subNodeValue) { - throw new ValidationSchemaException(__('Not empty value for "%1" node is required.', $subNodeType)); - } - - return $subNodeValue; - } -} diff --git a/app/code/Magento/Signifyd/Model/PaymentVerificationFactory.php b/app/code/Magento/Signifyd/Model/PaymentVerificationFactory.php deleted file mode 100644 index 5be5ccbc5e55a..0000000000000 --- a/app/code/Magento/Signifyd/Model/PaymentVerificationFactory.php +++ /dev/null @@ -1,109 +0,0 @@ -<?php -/** - * Copyright © Magento, Inc. All rights reserved. - * See COPYING.txt for license details. - */ -namespace Magento\Signifyd\Model; - -use Magento\Framework\ObjectManagerInterface; -use Magento\Payment\Api\PaymentVerificationInterface; -use Magento\Payment\Gateway\ConfigInterface; -use Magento\Framework\Exception\ConfigurationMismatchException; - -/** - * Creates verification service for provided payment method, or PaymentVerificationInterface::class - * if payment method does not support AVS, CVV verifications. - */ -class PaymentVerificationFactory -{ - /** - * @var ConfigInterface - */ - private $config; - - /** - * @var ObjectManagerInterface - */ - private $objectManager; - - /** - * @var PaymentVerificationInterface - */ - private $avsDefaultAdapter; - - /** - * @var PaymentVerificationInterface - */ - private $cvvDefaultAdapter; - - /** - * @param ObjectManagerInterface $objectManager - * @param ConfigInterface|Config $config - * @param PaymentVerificationInterface $avsDefaultAdapter - * @param PaymentVerificationInterface $cvvDefaultAdapter - */ - public function __construct( - ObjectManagerInterface $objectManager, - ConfigInterface $config, - PaymentVerificationInterface $avsDefaultAdapter, - PaymentVerificationInterface $cvvDefaultAdapter - ) { - $this->config = $config; - $this->objectManager = $objectManager; - $this->avsDefaultAdapter = $avsDefaultAdapter; - $this->cvvDefaultAdapter = $cvvDefaultAdapter; - } - - /** - * Creates instance of CVV code verification. - * Exception will be thrown if CVV mapper does not implement PaymentVerificationInterface. - * - * @param string $paymentCode - * @return PaymentVerificationInterface - * @throws ConfigurationMismatchException - */ - public function createPaymentCvv($paymentCode) - { - return $this->create($this->cvvDefaultAdapter, $paymentCode, 'cvv_ems_adapter'); - } - - /** - * Creates instance of AVS code verification. - * Exception will be thrown if AVS mapper does not implement PaymentVerificationInterface. - * - * @param string $paymentCode - * @return PaymentVerificationInterface - * @throws ConfigurationMismatchException - */ - public function createPaymentAvs($paymentCode) - { - return $this->create($this->avsDefaultAdapter, $paymentCode, 'avs_ems_adapter'); - } - - /** - * Creates instance of PaymentVerificationInterface. - * Default implementation will be returned if payment method does not implement PaymentVerificationInterface. - * - * @param PaymentVerificationInterface $defaultAdapter - * @param string $paymentCode - * @param string $configKey - * @return PaymentVerificationInterface - * @throws ConfigurationMismatchException If payment verification instance - * does not implement PaymentVerificationInterface. - */ - private function create(PaymentVerificationInterface $defaultAdapter, $paymentCode, $configKey) - { - $this->config->setMethodCode($paymentCode); - $verificationClass = $this->config->getValue($configKey); - if ($verificationClass === null) { - return $defaultAdapter; - } - $mapper = $this->objectManager->create($verificationClass); - if (!$mapper instanceof PaymentVerificationInterface) { - throw new ConfigurationMismatchException( - __('%1 must implement %2', $verificationClass, PaymentVerificationInterface::class) - ); - } - return $mapper; - } -} diff --git a/app/code/Magento/Signifyd/Model/PredefinedVerificationCode.php b/app/code/Magento/Signifyd/Model/PredefinedVerificationCode.php deleted file mode 100644 index 618d74b2a52e9..0000000000000 --- a/app/code/Magento/Signifyd/Model/PredefinedVerificationCode.php +++ /dev/null @@ -1,37 +0,0 @@ -<?php -/** - * Copyright © Magento, Inc. All rights reserved. - * See COPYING.txt for license details. - */ -namespace Magento\Signifyd\Model; - -use Magento\Payment\Api\PaymentVerificationInterface; -use Magento\Sales\Api\Data\OrderPaymentInterface; - -/** - * Default implementation of payment verification interface. - * The default code value can be configured via DI. - */ -class PredefinedVerificationCode implements PaymentVerificationInterface -{ - /** - * @var string - */ - private $code; - - /** - * @param string $code - */ - public function __construct($code = '') - { - $this->code = $code; - } - - /** - * @inheritdoc - */ - public function getCode(OrderPaymentInterface $orderPayment) - { - return $this->code; - } -} diff --git a/app/code/Magento/Signifyd/Model/QuoteSession/Adminhtml/BackendSession.php b/app/code/Magento/Signifyd/Model/QuoteSession/Adminhtml/BackendSession.php deleted file mode 100644 index 9be02719545c7..0000000000000 --- a/app/code/Magento/Signifyd/Model/QuoteSession/Adminhtml/BackendSession.php +++ /dev/null @@ -1,40 +0,0 @@ -<?php -/** - * Copyright © Magento, Inc. All rights reserved. - * See COPYING.txt for license details. - */ -namespace Magento\Signifyd\Model\QuoteSession\Adminhtml; - -use Magento\Backend\Model\Session\Quote as BackendQuoteSession; -use Magento\Signifyd\Model\QuoteSession\QuoteSessionInterface; - -/** - * Implementation of QuoteSessionInterface for Magento backend checkout. - */ -class BackendSession implements QuoteSessionInterface -{ - /** - * @var BackendQuoteSession - */ - private $backendQuoteSession; - - /** - * BackendSession constructor. - * - * Class uses backend session for retrieving quote. - * - * @param BackendQuoteSession $backendQuoteSession - */ - public function __construct(BackendQuoteSession $backendQuoteSession) - { - $this->backendQuoteSession = $backendQuoteSession; - } - - /** - * @inheritdoc - */ - public function getQuote() - { - return $this->backendQuoteSession->getQuote(); - } -} diff --git a/app/code/Magento/Signifyd/Model/QuoteSession/FrontendSession.php b/app/code/Magento/Signifyd/Model/QuoteSession/FrontendSession.php deleted file mode 100644 index 44c226ae4a47e..0000000000000 --- a/app/code/Magento/Signifyd/Model/QuoteSession/FrontendSession.php +++ /dev/null @@ -1,39 +0,0 @@ -<?php -/** - * Copyright © Magento, Inc. All rights reserved. - * See COPYING.txt for license details. - */ -namespace Magento\Signifyd\Model\QuoteSession; - -use Magento\Checkout\Model\Session as CheckoutSession; - -/** - * Implementation of QuoteSessionInterface for Magento frontend checkout. - */ -class FrontendSession implements QuoteSessionInterface -{ - /** - * @var CheckoutSession - */ - private $checkoutSession; - - /** - * FrontendSession constructor. - * - * Class uses checkout session for retrieving quote. - * - * @param CheckoutSession $checkoutSession - */ - public function __construct(CheckoutSession $checkoutSession) - { - $this->checkoutSession = $checkoutSession; - } - - /** - * @inheritdoc - */ - public function getQuote() - { - return $this->checkoutSession->getQuote(); - } -} diff --git a/app/code/Magento/Signifyd/Model/QuoteSession/QuoteSessionInterface.php b/app/code/Magento/Signifyd/Model/QuoteSession/QuoteSessionInterface.php deleted file mode 100644 index 14958ac65a6ee..0000000000000 --- a/app/code/Magento/Signifyd/Model/QuoteSession/QuoteSessionInterface.php +++ /dev/null @@ -1,19 +0,0 @@ -<?php -/** - * Copyright © Magento, Inc. All rights reserved. - * See COPYING.txt for license details. - */ -namespace Magento\Signifyd\Model\QuoteSession; - -/** - * Interface QuoteSessionInterface - */ -interface QuoteSessionInterface -{ - /** - * Returns quote from session. - * - * @return \Magento\Quote\Api\Data\CartInterface - */ - public function getQuote(); -} diff --git a/app/code/Magento/Signifyd/Model/ResourceModel/CaseEntity.php b/app/code/Magento/Signifyd/Model/ResourceModel/CaseEntity.php deleted file mode 100644 index 9c6ffde4504b9..0000000000000 --- a/app/code/Magento/Signifyd/Model/ResourceModel/CaseEntity.php +++ /dev/null @@ -1,22 +0,0 @@ -<?php -/** - * Copyright © Magento, Inc. All rights reserved. - * See COPYING.txt for license details. - */ -namespace Magento\Signifyd\Model\ResourceModel; - -use Magento\Framework\Model\ResourceModel\Db\AbstractDb; - -/** - * Implementation of case resource model - */ -class CaseEntity extends AbstractDb -{ - /** - * @inheritdoc - */ - protected function _construct() - { - $this->_init('signifyd_case', 'entity_id'); - } -} diff --git a/app/code/Magento/Signifyd/Model/ResourceModel/CaseEntity/Collection.php b/app/code/Magento/Signifyd/Model/ResourceModel/CaseEntity/Collection.php deleted file mode 100644 index 92e233dd42dbc..0000000000000 --- a/app/code/Magento/Signifyd/Model/ResourceModel/CaseEntity/Collection.php +++ /dev/null @@ -1,24 +0,0 @@ -<?php -/** - * Copyright © Magento, Inc. All rights reserved. - * See COPYING.txt for license details. - */ -namespace Magento\Signifyd\Model\ResourceModel\CaseEntity; - -use Magento\Framework\Model\ResourceModel\Db\Collection\AbstractCollection; -use Magento\Signifyd\Model\CaseEntity; -use Magento\Signifyd\Model\ResourceModel\CaseEntity as CaseResourceModel; - -/** - * Collection of case entities - */ -class Collection extends AbstractCollection -{ - /** - * @inheritdoc - */ - public function _construct() - { - $this->_init(CaseEntity::class, CaseResourceModel::class); - } -} diff --git a/app/code/Magento/Signifyd/Model/SalesOrderGrid/NotSyncedOrderIdListProvider.php b/app/code/Magento/Signifyd/Model/SalesOrderGrid/NotSyncedOrderIdListProvider.php deleted file mode 100644 index 360225ae37b7b..0000000000000 --- a/app/code/Magento/Signifyd/Model/SalesOrderGrid/NotSyncedOrderIdListProvider.php +++ /dev/null @@ -1,54 +0,0 @@ -<?php -/** - * Copyright © Magento, Inc. All rights reserved. - * See COPYING.txt for license details. - */ -namespace Magento\Signifyd\Model\SalesOrderGrid; - -use Magento\Sales\Model\ResourceModel\Provider\NotSyncedDataProviderInterface; -use Magento\Signifyd\Model\ResourceModel; -use Magento\Signifyd\Model\ResourceModel\CaseEntity; - -/** - * Provides order ids list which Signifyd Case guaranty status were changed - */ -class NotSyncedOrderIdListProvider implements NotSyncedDataProviderInterface -{ - /** - * @var ResourceModel\CaseEntity - */ - private $caseEntity; - - /** - * @param ResourceModel\CaseEntity $caseEntity - */ - public function __construct( - CaseEntity $caseEntity - ) { - $this->caseEntity = $caseEntity; - } - - /** - * @inheritdoc - */ - public function getIds($mainTableName, $gridTableName) - { - $connection = $this->caseEntity->getConnection(); - $select = $connection->select() - ->from($this->caseEntity->getMainTable(), ['order_id']) - ->joinLeft( - [$gridTableName => $connection->getTableName($gridTableName)], - sprintf( - '%s.%s = %s.%s', - $this->caseEntity->getMainTable(), - 'order_id', - $gridTableName, - 'entity_id' - ), - [] - ) - ->where('guarantee_disposition != signifyd_guarantee_status'); - - return $connection->fetchAll($select, [], \Zend_Db::FETCH_COLUMN); - } -} diff --git a/app/code/Magento/Signifyd/Model/SalesOrderGrid/OrderGridUpdater.php b/app/code/Magento/Signifyd/Model/SalesOrderGrid/OrderGridUpdater.php deleted file mode 100644 index fff42b300be58..0000000000000 --- a/app/code/Magento/Signifyd/Model/SalesOrderGrid/OrderGridUpdater.php +++ /dev/null @@ -1,55 +0,0 @@ -<?php -/** - * Copyright © Magento, Inc. All rights reserved. - * See COPYING.txt for license details. - */ -namespace Magento\Signifyd\Model\SalesOrderGrid; - -use Magento\Framework\App\Config\ScopeConfigInterface; -use Magento\Sales\Model\ResourceModel\GridInterface; - -/** - * Perfoms sales order grid updating operations. - * - * Serves order grid updates in both synchronous and asynchronous modes. - */ -class OrderGridUpdater -{ - /** - * @var ScopeConfigInterface - */ - private $globalConfig; - - /** - * @var GridInterface - */ - private $entityGrid; - - /** - * @param GridInterface $entityGrid - * @param ScopeConfigInterface $globalConfig - */ - public function __construct( - GridInterface $entityGrid, - ScopeConfigInterface $globalConfig - ) { - $this->globalConfig = $globalConfig; - $this->entityGrid = $entityGrid; - } - - /** - * Handles synchronous updating order entity in grid. - * - * Works only if asynchronous grid indexing is disabled - * in global settings. - * - * @param int $orderId - * @return void - */ - public function update($orderId) - { - if (!$this->globalConfig->getValue('dev/grid/async_indexing')) { - $this->entityGrid->refresh($orderId); - } - } -} diff --git a/app/code/Magento/Signifyd/Model/SignifydGateway/ApiCallException.php b/app/code/Magento/Signifyd/Model/SignifydGateway/ApiCallException.php deleted file mode 100644 index 73338c8ea4d62..0000000000000 --- a/app/code/Magento/Signifyd/Model/SignifydGateway/ApiCallException.php +++ /dev/null @@ -1,39 +0,0 @@ -<?php -/** - * Copyright © Magento, Inc. All rights reserved. - * See COPYING.txt for license details. - */ -namespace Magento\Signifyd\Model\SignifydGateway; - -/** - * Exception of interaction with Signifyd API - */ -class ApiCallException extends GatewayException -{ - /** - * @var string - */ - private $requestData; - - /** - * ApiCallException constructor. - * @param string $message - * @param int $code - * @param \Exception|null $previous - * @param string $requestData in JSON format - */ - public function __construct($message = '', $code = 0, \Exception $previous = null, $requestData = '') - { - $this->requestData = $requestData; - parent::__construct($message, $code, $previous); - } - - /** - * Gets request data for unsuccessful request in JSON format - * @return string - */ - public function getRequestData() - { - return $this->requestData; - } -} diff --git a/app/code/Magento/Signifyd/Model/SignifydGateway/ApiClient.php b/app/code/Magento/Signifyd/Model/SignifydGateway/ApiClient.php deleted file mode 100644 index 2d6d57a510ae3..0000000000000 --- a/app/code/Magento/Signifyd/Model/SignifydGateway/ApiClient.php +++ /dev/null @@ -1,49 +0,0 @@ -<?php -/** - * Copyright © Magento, Inc. All rights reserved. - * See COPYING.txt for license details. - */ -namespace Magento\Signifyd\Model\SignifydGateway; - -use Magento\Signifyd\Model\SignifydGateway\Client\RequestBuilder; - -/** - * Encapsulates Signifyd API protocol. - */ -class ApiClient -{ - /** - * @var RequestBuilder - */ - private $requestBuilder; - - /** - * ApiClient constructor. - * - * @param RequestBuilder $requestBuilder - */ - public function __construct( - RequestBuilder $requestBuilder - ) { - $this->requestBuilder = $requestBuilder; - } - - /** - * Perform call to Signifyd API. - * - * Method returns associative array that corresponds to successful result. - * Current implementation do not expose details in case of failure. - * - * @param string $url - * @param string $method - * @param array $params - * @param int|null $storeId - * @return array - */ - public function makeApiCall($url, $method, array $params = [], $storeId = null): array - { - $result = $this->requestBuilder->doRequest($url, $method, $params, $storeId); - - return $result; - } -} diff --git a/app/code/Magento/Signifyd/Model/SignifydGateway/Client/HttpClientFactory.php b/app/code/Magento/Signifyd/Model/SignifydGateway/Client/HttpClientFactory.php deleted file mode 100644 index 2a9b933b98b5d..0000000000000 --- a/app/code/Magento/Signifyd/Model/SignifydGateway/Client/HttpClientFactory.php +++ /dev/null @@ -1,146 +0,0 @@ -<?php -/** - * Copyright © Magento, Inc. All rights reserved. - * See COPYING.txt for license details. - */ -namespace Magento\Signifyd\Model\SignifydGateway\Client; - -use Magento\Framework\HTTP\ZendClient; -use Magento\Framework\HTTP\ZendClientFactory; -use Magento\Framework\Json\EncoderInterface; -use Magento\Signifyd\Model\Config; - -/** - * Class HttpClientFactory - * Creates and configures HTTP client for RequestBuilder - */ -class HttpClientFactory -{ - /** - * Specifies basic HTTP access authentication Header. - * - * @var string - */ - private static $authorizationType = 'Authorization'; - - /** - * JSON HTTP Content-Type Header. - * - * @var string - */ - private static $jsonDataType = 'application/json'; - - /** - * @var string - */ - private static $urlSeparator = '/'; - - /** - * @var Config - */ - private $config; - - /** - * @var ZendClientFactory - */ - private $clientFactory; - - /** - * @var EncoderInterface - */ - private $dataEncoder; - - /** - * HttpClientCreator constructor. - * - * @param Config $config - * @param ZendClientFactory $clientFactory - * @param EncoderInterface $dataEncoder - */ - public function __construct( - Config $config, - ZendClientFactory $clientFactory, - EncoderInterface $dataEncoder - ) { - $this->config = $config; - $this->clientFactory = $clientFactory; - $this->dataEncoder = $dataEncoder; - } - - /** - * Creates and configures HTTP client. - * - * @param string $url - * @param string $method - * @param array $params - * @param int|null $storeId - * @return ZendClient - */ - public function create($url, $method, array $params = [], $storeId = null): ZendClient - { - $apiKey = $this->getApiKey($storeId); - $apiUrl = $this->buildFullApiUrl($url, $storeId); - - $client = $this->createNewClient(); - $client->setHeaders( - self::$authorizationType, - sprintf('Basic %s', base64_encode($apiKey)) - ); - if (!empty($params)) { - $encodedData = $this->dataEncoder->encode($params); - $client->setRawData($encodedData, self::$jsonDataType); - } - $client->setMethod($method); - $client->setUri($apiUrl); - - return $client; - } - - /** - * @return ZendClient - */ - private function createNewClient() - { - return $this->clientFactory->create(); - } - - /** - * Signifyd API key for merchant account. - * - * @see https://www.signifyd.com/docs/api/#/introduction/authentication - * @param int|null $storeId - * @return string - */ - private function getApiKey($storeId): string - { - return $this->config->getApiKey($storeId); - } - - /** - * Full URL for Singifyd API based on relative URL. - * - * @param string $url - * @param int|null $storeId - * @return string - */ - private function buildFullApiUrl($url, $storeId): string - { - $baseApiUrl = $this->getBaseApiUrl($storeId); - $fullUrl = $baseApiUrl . self::$urlSeparator . ltrim($url, self::$urlSeparator); - - return $fullUrl; - } - - /** - * Base Sigifyd API URL without trailing slash. - * - * @param int|null $storeId - * @return string - */ - private function getBaseApiUrl($storeId): string - { - $baseApiUrl = $this->config->getApiUrl($storeId); - - return rtrim($baseApiUrl, self::$urlSeparator); - } -} diff --git a/app/code/Magento/Signifyd/Model/SignifydGateway/Client/RequestBuilder.php b/app/code/Magento/Signifyd/Model/SignifydGateway/Client/RequestBuilder.php deleted file mode 100644 index ee079a74d345f..0000000000000 --- a/app/code/Magento/Signifyd/Model/SignifydGateway/Client/RequestBuilder.php +++ /dev/null @@ -1,63 +0,0 @@ -<?php -/** - * Copyright © Magento, Inc. All rights reserved. - * See COPYING.txt for license details. - */ -namespace Magento\Signifyd\Model\SignifydGateway\Client; - -/** - * Class RequestBuilder - * Creates HTTP client, sends request to Signifyd and handles response - */ -class RequestBuilder -{ - /** - * @var HttpClientFactory - */ - private $clientCreator; - - /** - * @var RequestSender - */ - private $requestSender; - - /** - * @var ResponseHandler - */ - private $responseHandler; - - /** - * RequestBuilder constructor. - * - * @param HttpClientFactory $clientCreator - * @param RequestSender $requestSender - * @param ResponseHandler $responseHandler - */ - public function __construct( - HttpClientFactory $clientCreator, - RequestSender $requestSender, - ResponseHandler $responseHandler - ) { - $this->clientCreator = $clientCreator; - $this->requestSender = $requestSender; - $this->responseHandler = $responseHandler; - } - - /** - * Creates HTTP client for API call. - * - * @param string $url - * @param string $method - * @param array $params - * @param int|null $storeId - * @return array - */ - public function doRequest($url, $method, array $params = [], $storeId = null): array - { - $client = $this->clientCreator->create($url, $method, $params, $storeId); - $response = $this->requestSender->send($client, $storeId); - $result = $this->responseHandler->handle($response); - - return $result; - } -} diff --git a/app/code/Magento/Signifyd/Model/SignifydGateway/Client/RequestSender.php b/app/code/Magento/Signifyd/Model/SignifydGateway/Client/RequestSender.php deleted file mode 100644 index a63331e055c1c..0000000000000 --- a/app/code/Magento/Signifyd/Model/SignifydGateway/Client/RequestSender.php +++ /dev/null @@ -1,74 +0,0 @@ -<?php -/** - * Copyright © Magento, Inc. All rights reserved. - * See COPYING.txt for license details. - */ -namespace Magento\Signifyd\Model\SignifydGateway\Client; - -use Magento\Signifyd\Model\SignifydGateway\Debugger\DebuggerFactory; -use Magento\Signifyd\Model\SignifydGateway\ApiCallException; -use Magento\Framework\HTTP\ZendClient; - -/** - * Class RequestSender - * Gets HTTP client end sends request to Signifyd API - */ -class RequestSender -{ - /** - * @var DebuggerFactory - */ - private $debuggerFactory; - - /** - * RequestSender constructor. - * - * @param DebuggerFactory $debuggerFactory - */ - public function __construct( - DebuggerFactory $debuggerFactory - ) { - $this->debuggerFactory = $debuggerFactory; - } - - /** - * Sends HTTP request to Signifyd API with configured client. - * - * Each request/response pair is handled by debugger. - * If debug mode for Signifyd integration enabled in configuration - * debug information is recorded to debug.log. - * - * @param ZendClient $client - * @param int|null $storeId - * @return \Zend_Http_Response - * @throws ApiCallException - */ - public function send(ZendClient $client, $storeId = null): \Zend_Http_Response - { - try { - $response = $client->request(); - - $this->debuggerFactory->create($storeId)->success( - $client->getUri(true), - $client->getLastRequest(), - $response->getStatus() . ' ' . $response->getMessage(), - $response->getBody() - ); - - return $response; - } catch (\Exception $e) { - $this->debuggerFactory->create($storeId)->failure( - $client->getUri(true), - $client->getLastRequest(), - $e - ); - - throw new ApiCallException( - 'Unable to process Signifyd API: ' . $e->getMessage(), - $e->getCode(), - $e, - $client->getLastRequest() - ); - } - } -} diff --git a/app/code/Magento/Signifyd/Model/SignifydGateway/Client/ResponseHandler.php b/app/code/Magento/Signifyd/Model/SignifydGateway/Client/ResponseHandler.php deleted file mode 100644 index 614e59b7c29a2..0000000000000 --- a/app/code/Magento/Signifyd/Model/SignifydGateway/Client/ResponseHandler.php +++ /dev/null @@ -1,124 +0,0 @@ -<?php -/** - * Copyright © Magento, Inc. All rights reserved. - * See COPYING.txt for license details. - */ -namespace Magento\Signifyd\Model\SignifydGateway\Client; - -use Magento\Signifyd\Model\SignifydGateway\ApiCallException; -use Magento\Framework\Json\DecoderInterface; - -/** - * Class ResponseHandler - */ -class ResponseHandler -{ - /** - * Successful HTTP response codes. - * - * @var array - */ - private static $successResponseCodes = [200, 201, 204]; - - /** - * Current servers PHP version id. - */ - private static $phpVersionId = PHP_VERSION_ID; - - /** - * Failure HTTP response codes with messages. - * - * @var array - */ - private static $failureResponses = [ - 400 => 'Bad Request - The request could not be parsed. Response: %s', - 401 => 'Unauthorized - user is not logged in, could not be authenticated. Response: %s', - 403 => 'Forbidden - Cannot access resource. Response: %s', - 404 => 'Not Found - resource does not exist. Response: %s', - 409 => 'Conflict - with state of the resource on server. Can occur with (too rapid) PUT requests. Response: %s', - 500 => 'Server error. Response: %s' - ]; - - /** - * Unexpected Signifyd API response message. - * - * @var string - */ - private static $unexpectedResponse = 'Unexpected Signifyd API response code "%s" with content "%s".'; - - /** - * @var DecoderInterface - */ - private $dataDecoder; - - /** - * ResponseHandler constructor. - * - * @param DecoderInterface $dataDecoder - */ - public function __construct( - DecoderInterface $dataDecoder - ) { - $this->dataDecoder = $dataDecoder; - } - - /** - * Reads result of successful operation and throws exception in case of any failure. - * - * @param \Zend_Http_Response $response - * @return array - * @throws ApiCallException - */ - public function handle(\Zend_Http_Response $response) - { - $responseCode = $response->getStatus(); - - if (!in_array($responseCode, self::$successResponseCodes)) { - $errorMessage = $this->buildApiCallFailureMessage($response); - throw new ApiCallException($errorMessage); - } - - $responseBody = (string)$response->getBody(); - - if (self::$phpVersionId < 70000 && empty($responseBody)) { - /* - * Only since PHP 7.0 empty string treated as JSON syntax error - * http://php.net/manual/en/function.json-decode.php - */ - throw new ApiCallException('Response is not valid JSON: Decoding failed: Syntax error'); - } - - try { - $decodedResponseBody = $this->dataDecoder->decode($responseBody); - } catch (\Exception $e) { - throw new ApiCallException( - 'Response is not valid JSON: ' . $e->getMessage(), - $e->getCode(), - $e - ); - } - - return $decodedResponseBody; - } - - /** - * Error message for request rejected by Signify. - * - * @param \Zend_Http_Response $response - * @return string - */ - private function buildApiCallFailureMessage(\Zend_Http_Response $response) - { - $responseBody = $response->getBody(); - - if (key_exists($response->getStatus(), self::$failureResponses)) { - return sprintf(self::$failureResponses[$response->getStatus()], $responseBody); - } - - return sprintf( - self::$unexpectedResponse, - $response->getStatus(), - $responseBody - ); - } -} diff --git a/app/code/Magento/Signifyd/Model/SignifydGateway/Debugger/BlackHole.php b/app/code/Magento/Signifyd/Model/SignifydGateway/Debugger/BlackHole.php deleted file mode 100644 index 7057313b5e415..0000000000000 --- a/app/code/Magento/Signifyd/Model/SignifydGateway/Debugger/BlackHole.php +++ /dev/null @@ -1,33 +0,0 @@ -<?php -/** - * Copyright © Magento, Inc. All rights reserved. - * See COPYING.txt for license details. - */ -namespace Magento\Signifyd\Model\SignifydGateway\Debugger; - -use Exception; - -/** - * This debugger ignores any information. - * Optimal production environment. - */ -class BlackHole implements DebuggerInterface -{ - /** - * {@inheritdoc} - * @SuppressWarnings(PHPMD.UnusedFormalParameter) - */ - public function success($requestUrl, $requestData, $responseStatus, $responseBody) - { - // ignore - } - - /** - * {@inheritdoc} - * @SuppressWarnings(PHPMD.UnusedFormalParameter) - */ - public function failure($requestUrl, $requestData, Exception $exception) - { - // ignore - } -} diff --git a/app/code/Magento/Signifyd/Model/SignifydGateway/Debugger/DebuggerFactory.php b/app/code/Magento/Signifyd/Model/SignifydGateway/Debugger/DebuggerFactory.php deleted file mode 100644 index 1e61a313899cc..0000000000000 --- a/app/code/Magento/Signifyd/Model/SignifydGateway/Debugger/DebuggerFactory.php +++ /dev/null @@ -1,58 +0,0 @@ -<?php -/** - * Copyright © Magento, Inc. All rights reserved. - * See COPYING.txt for license details. - */ -namespace Magento\Signifyd\Model\SignifydGateway\Debugger; - -use Magento\Framework\ObjectManagerInterface; -use Magento\Signifyd\Model\Config; - -/** - * Factory produces debugger based on runtime configuration. - * - * Configuration may be changed by - * - config.xml - * - at Admin panel (Stores > Configuration > Sales > Fraud Detection > Signifyd > Debug) - */ -class DebuggerFactory -{ - /** - * @var ObjectManagerInterface - */ - private $objectManager; - - /** - * @var Config - */ - private $config; - - /** - * DebuggerFactory constructor. - * - * @param ObjectManagerInterface $objectManager - * @param Config $config - */ - public function __construct( - ObjectManagerInterface $objectManager, - Config $config - ) { - $this->objectManager = $objectManager; - $this->config = $config; - } - - /** - * Create debugger instance - * - * @param int|null $storeId - * @return DebuggerInterface - */ - public function create($storeId = null): DebuggerInterface - { - if (!$this->config->isDebugModeEnabled($storeId)) { - return $this->objectManager->get(BlackHole::class); - } - - return $this->objectManager->get(Log::class); - } -} diff --git a/app/code/Magento/Signifyd/Model/SignifydGateway/Debugger/DebuggerInterface.php b/app/code/Magento/Signifyd/Model/SignifydGateway/Debugger/DebuggerInterface.php deleted file mode 100644 index f4a2f9cc56a8f..0000000000000 --- a/app/code/Magento/Signifyd/Model/SignifydGateway/Debugger/DebuggerInterface.php +++ /dev/null @@ -1,35 +0,0 @@ -<?php -/** - * Copyright © Magento, Inc. All rights reserved. - * See COPYING.txt for license details. - */ -namespace Magento\Signifyd\Model\SignifydGateway\Debugger; - -use Exception; - -/** - * Interface for debugging interaction with Signifyd API - */ -interface DebuggerInterface -{ - /** - * Register debug information about accepted request to Signifyd API - * - * @param string $requestUrl - * @param string $requestData - * @param string $responseStatus - * @param string $responseBody - * @return void - */ - public function success($requestUrl, $requestData, $responseStatus, $responseBody); - - /** - * Register debug information about failed request to Signifyd API - * - * @param string $requestUrl - * @param string $requestData - * @param Exception $exception - * @return mixed - */ - public function failure($requestUrl, $requestData, Exception $exception); -} diff --git a/app/code/Magento/Signifyd/Model/SignifydGateway/Debugger/Log.php b/app/code/Magento/Signifyd/Model/SignifydGateway/Debugger/Log.php deleted file mode 100644 index 4b7258180f53e..0000000000000 --- a/app/code/Magento/Signifyd/Model/SignifydGateway/Debugger/Log.php +++ /dev/null @@ -1,146 +0,0 @@ -<?php -/** - * Copyright © Magento, Inc. All rights reserved. - * See COPYING.txt for license details. - */ -namespace Magento\Signifyd\Model\SignifydGateway\Debugger; - -use Psr\Log\LoggerInterface; -use Exception; - -/** - * Debugger writes information about request, response and possible exception to standard system log. - */ -class Log implements DebuggerInterface -{ - /** - * @var LoggerInterface - */ - private $logger; - - /** - * Log constructor. - * - * @param LoggerInterface $logger - */ - public function __construct(LoggerInterface $logger) - { - $this->logger = $logger; - } - - /** - * {@inheritdoc} - */ - public function success($requestUrl, $requestData, $responseStatus, $responseBody) - { - $requestInfo = $this->buildRequestInfo($requestUrl, $requestData); - $responseInfo = $this->buildResponseInfo($responseStatus, $responseBody); - - $info = $requestInfo - . $responseInfo; - - $this->writeToLog($info); - } - - /** - * {@inheritdoc} - */ - public function failure($requestUrl, $requestData, Exception $exception) - { - $requestInfo = $this->buildRequestInfo($requestUrl, $requestData); - $exceptionInfo = $this->buildExceptionInfo($exception); - - $info = $requestInfo - . $exceptionInfo; - - $this->writeToLog($info); - } - - /** - * Build string with request URL and body - * - * @param string $requestUrl - * @param string $requestData - * @return string - */ - private function buildRequestInfo($requestUrl, $requestData) - { - $infoContent = $this->buildInfoSection('URL', $requestUrl) - . $this->buildInfoSection('Body', $requestData); - - $info = $this->buildInfoSection('Request', $infoContent); - return $info; - } - - /** - * Build string with response status code and body - * - * @param string $responseStatus - * @param string $responseBody - * @return string - */ - private function buildResponseInfo($responseStatus, $responseBody) - { - $infoContent = $this->buildInfoSection('Status', $responseStatus) - . $this->buildInfoSection('Body', $responseBody); - - $info = $this->buildInfoSection('Response', $infoContent); - return $info; - } - - /** - * Build string with exception information - * - * @param Exception $exception - * @return string - */ - private function buildExceptionInfo(Exception $exception) - { - $infoContent = (string)$exception; - $info = $this->buildInfoSection('Exception', $infoContent); - return $info; - } - - /** - * Write debug information to log file (var/log/debug.log by default) - * - * @param string $info - * @return void - */ - private function writeToLog($info) - { - $logMessage = $this->buildInfoSection('Signifyd API integration debug info', $info); - $this->logger->debug($logMessage); - } - - /** - * Build unified debug section string - * - * @param string $title - * @param string $content - * @return string - */ - private function buildInfoSection($title, $content) - { - $formattedInfo = $title . ":\n" - . $this->addIndent($content) . "\n"; - return $formattedInfo; - } - - /** - * Add indent to each line in content - * - * @param string $content - * @param string $indent - * @return string - */ - private function addIndent($content, $indent = ' ') - { - $contentLines = explode("\n", $content); - $contentLinesWithIndent = array_map(function ($line) use ($indent) { - return $indent . $line; - }, $contentLines); - $content = implode("\n", $contentLinesWithIndent); - return $content; - } -} diff --git a/app/code/Magento/Signifyd/Model/SignifydGateway/Gateway.php b/app/code/Magento/Signifyd/Model/SignifydGateway/Gateway.php deleted file mode 100644 index 9f7a053c58724..0000000000000 --- a/app/code/Magento/Signifyd/Model/SignifydGateway/Gateway.php +++ /dev/null @@ -1,225 +0,0 @@ -<?php -/** - * Copyright © Magento, Inc. All rights reserved. - * See COPYING.txt for license details. - */ -namespace Magento\Signifyd\Model\SignifydGateway; - -use Magento\Sales\Api\OrderRepositoryInterface; -use Magento\Signifyd\Api\CaseRepositoryInterface; -use Magento\Signifyd\Model\SignifydGateway\Request\CreateCaseBuilderInterface; - -/** - * Signifyd Gateway. - * - * Encapsulates interaction with Signifyd API. - */ -class Gateway -{ - /**#@+ - * Constants for case available statuses - */ - const STATUS_OPEN = 'OPEN'; - const STATUS_PROCESSING = 'PROCESSING'; - const STATUS_FLAGGED = 'FLAGGED'; - const STATUS_DISMISSED = 'DISMISSED'; - /**#@-*/ - - /**#@+ - * Constants for guarantee available statuses - * @see https://www.signifyd.com/resources/manual/signifyd-guarantee/signifyd-guarantee/ - */ - const GUARANTEE_APPROVED = 'APPROVED'; - const GUARANTEE_DECLINED = 'DECLINED'; - const GUARANTEE_PENDING = 'PENDING'; - const GUARANTEE_CANCELED = 'CANCELED'; - const GUARANTEE_IN_REVIEW = 'IN_REVIEW'; - const GUARANTEE_UNREQUESTED = 'UNREQUESTED'; - /**#@-*/ - - /**#@+ - * Constants for case available review dispositions - */ - const DISPOSITION_GOOD = 'GOOD'; - const DISPOSITION_FRAUDULENT = 'FRAUDULENT'; - const DISPOSITION_UNSET = 'UNSET'; - - /** - * @var CreateCaseBuilderInterface - */ - private $createCaseBuilder; - - /** - * @var ApiClient - */ - private $apiClient; - - /** - * @var OrderRepositoryInterface - */ - private $orderRepository; - - /** - * @var CaseRepositoryInterface - */ - private $caseRepository; - - /** - * Gateway constructor. - * - * @param CreateCaseBuilderInterface $createCaseBuilder - * @param ApiClient $apiClient - * @param OrderRepositoryInterface $orderRepository - * @param CaseRepositoryInterface $caseRepository - */ - public function __construct( - CreateCaseBuilderInterface $createCaseBuilder, - ApiClient $apiClient, - OrderRepositoryInterface $orderRepository, - CaseRepositoryInterface $caseRepository - ) { - $this->createCaseBuilder = $createCaseBuilder; - $this->apiClient = $apiClient; - $this->orderRepository = $orderRepository; - $this->caseRepository = $caseRepository; - } - - /** - * Returns id of created case (investigation) on Signifyd service - * @see https://www.signifyd.com/docs/api/#/reference/cases/create-a-case - * - * @param int $orderId - * @return int Signifyd case (investigation) identifier - * @throws GatewayException - */ - public function createCase($orderId) - { - $caseParams = $this->createCaseBuilder->build($orderId); - $storeId = $this->getStoreIdFromOrder($orderId); - - $caseCreationResult = $this->apiClient->makeApiCall( - '/cases', - 'POST', - $caseParams, - $storeId - ); - - if (!isset($caseCreationResult['investigationId'])) { - throw new GatewayException('Expected field "investigationId" missed.'); - } - - return (int)$caseCreationResult['investigationId']; - } - - /** - * Returns guaranty decision result - * @see https://www.signifyd.com/docs/api/#/reference/guarantees/submit-a-case-for-guarantee - * - * @param int $signifydCaseId - * @return string - * @throws GatewayException - */ - public function submitCaseForGuarantee($signifydCaseId) - { - $storeId = $this->getStoreIdFromCase($signifydCaseId); - $guaranteeCreationResult = $this->apiClient->makeApiCall( - '/guarantees', - 'POST', - [ - 'caseId' => $signifydCaseId, - ], - $storeId - ); - - $disposition = $this->processDispositionResult($guaranteeCreationResult); - return $disposition; - } - - /** - * Sends request to cancel guarantee and returns disposition. - * - * @see https://www.signifyd.com/docs/api/#/reference/guarantees/submit-a-case-for-guarantee/cancel-guarantee - * @param int $caseId - * @return string - * @throws GatewayException - */ - public function cancelGuarantee($caseId) - { - $storeId = $this->getStoreIdFromCase($caseId); - $result = $this->apiClient->makeApiCall( - '/cases/' . $caseId . '/guarantee', - 'PUT', - [ - 'guaranteeDisposition' => self::GUARANTEE_CANCELED - ], - $storeId - ); - - $disposition = $this->processDispositionResult($result); - if ($disposition !== self::GUARANTEE_CANCELED) { - throw new GatewayException("API returned unexpected disposition: $disposition."); - } - - return $disposition; - } - - /** - * Processes result from Signifyd API. - * Throws the GatewayException is result does not contain guarantee disposition in response or - * disposition has unknown status. - * - * @param array $result - * @return string - * @throws GatewayException - */ - private function processDispositionResult(array $result) - { - if (!isset($result['disposition'])) { - throw new GatewayException('Expected field "disposition" missed.'); - } - - $disposition = strtoupper($result['disposition']); - - if (!in_array($disposition, [ - self::GUARANTEE_APPROVED, - self::GUARANTEE_DECLINED, - self::GUARANTEE_PENDING, - self::GUARANTEE_CANCELED, - self::GUARANTEE_IN_REVIEW, - self::GUARANTEE_UNREQUESTED - ])) { - throw new GatewayException( - sprintf('API returns unknown guaranty disposition "%s".', $disposition) - ); - } - - return $disposition; - } - - /** - * Returns store id by case. - * - * @param int $caseId - * @return int|null - */ - private function getStoreIdFromCase(int $caseId) - { - $case = $this->caseRepository->getByCaseId($caseId); - $orderId = $case->getOrderId(); - - return $this->getStoreIdFromOrder($orderId); - } - - /** - * Returns store id from order. - * - * @param int $orderId - * @return int|null - */ - private function getStoreIdFromOrder(int $orderId) - { - $order = $this->orderRepository->get($orderId); - - return $order->getStoreId(); - } -} diff --git a/app/code/Magento/Signifyd/Model/SignifydGateway/GatewayException.php b/app/code/Magento/Signifyd/Model/SignifydGateway/GatewayException.php deleted file mode 100644 index 666217f8ccc85..0000000000000 --- a/app/code/Magento/Signifyd/Model/SignifydGateway/GatewayException.php +++ /dev/null @@ -1,14 +0,0 @@ -<?php -/** - * Copyright © Magento, Inc. All rights reserved. - * See COPYING.txt for license details. - */ -namespace Magento\Signifyd\Model\SignifydGateway; - -/** - * Exception of interaction with Signifyd API - */ -class GatewayException extends \Exception -{ - -} diff --git a/app/code/Magento/Signifyd/Model/SignifydGateway/Request/AddressBuilder.php b/app/code/Magento/Signifyd/Model/SignifydGateway/Request/AddressBuilder.php deleted file mode 100644 index 482f243f6f05d..0000000000000 --- a/app/code/Magento/Signifyd/Model/SignifydGateway/Request/AddressBuilder.php +++ /dev/null @@ -1,46 +0,0 @@ -<?php -/** - * Copyright © Magento, Inc. All rights reserved. - * See COPYING.txt for license details. - */ -namespace Magento\Signifyd\Model\SignifydGateway\Request; - -use Magento\Sales\Api\Data\OrderAddressInterface; - -/** - * Prepare address data - */ -class AddressBuilder -{ - /** - * Returns address data params based on OrderAddressInterface - * - * @param OrderAddressInterface $address - * @return array - */ - public function build(OrderAddressInterface $address) - { - return [ - 'streetAddress' => $this->getStreetLine(1, $address->getStreet()), - 'unit' => $this->getStreetLine(2, $address->getStreet()), - 'city' => $address->getCity(), - 'provinceCode' => $address->getRegionCode(), - 'postalCode' => $address->getPostcode(), - 'countryCode' => $address->getCountryId() - ]; - } - - /** - * Get street line by number - * - * @param int $number - * @param string[]|null $street - * @return string - */ - private function getStreetLine($number, $street) - { - $lines = is_array($street) ? $street : []; - - return $lines[$number - 1] ?? ''; - } -} diff --git a/app/code/Magento/Signifyd/Model/SignifydGateway/Request/CardBuilder.php b/app/code/Magento/Signifyd/Model/SignifydGateway/Request/CardBuilder.php deleted file mode 100644 index 5e3a1a83e7aeb..0000000000000 --- a/app/code/Magento/Signifyd/Model/SignifydGateway/Request/CardBuilder.php +++ /dev/null @@ -1,56 +0,0 @@ -<?php -/** - * Copyright © Magento, Inc. All rights reserved. - * See COPYING.txt for license details. - */ -namespace Magento\Signifyd\Model\SignifydGateway\Request; - -use Magento\Sales\Model\Order; - -/** - * Prepare data related to the card that was used for the purchase and its cardholder. - */ -class CardBuilder -{ - /** - * @var AddressBuilder - */ - private $addressBuilder; - - /** - * @param AddressBuilder $addressBuilder - */ - public function __construct( - AddressBuilder $addressBuilder - ) { - $this->addressBuilder = $addressBuilder; - } - - /** - * Returns card data params based on payment and billing address info - * - * @param Order $order - * @return array - */ - public function build(Order $order) - { - $result = []; - $address = $order->getBillingAddress(); - if ($address === null) { - return $result; - } - - $payment = $order->getPayment(); - $result = [ - 'card' => [ - 'cardHolderName' => $address->getFirstname() . ' ' . $address->getLastname(), - 'last4' => $payment->getCcLast4(), - 'expiryMonth' => $payment->getCcExpMonth(), - 'expiryYear' => $payment->getCcExpYear(), - 'billingAddress' => $this->addressBuilder->build($address) - ] - ]; - - return $result; - } -} diff --git a/app/code/Magento/Signifyd/Model/SignifydGateway/Request/ClientVersionBuilder.php b/app/code/Magento/Signifyd/Model/SignifydGateway/Request/ClientVersionBuilder.php deleted file mode 100644 index 8db06473b96d8..0000000000000 --- a/app/code/Magento/Signifyd/Model/SignifydGateway/Request/ClientVersionBuilder.php +++ /dev/null @@ -1,50 +0,0 @@ -<?php -/** - * Copyright © Magento, Inc. All rights reserved. - * See COPYING.txt for license details. - */ -namespace Magento\Signifyd\Model\SignifydGateway\Request; - -use Magento\Framework\App\ProductMetadataInterface; - -/** - * Provides platform name, edition and version info - */ -class ClientVersionBuilder -{ - /** - * @var string - */ - private static $clientVersion = '1.0'; - - /** - * @var ProductMetadataInterface - */ - private $productMetadata; - - /** - * @param ProductMetadataInterface $productMetadata - */ - public function __construct( - ProductMetadataInterface $productMetadata - ) { - $this->productMetadata = $productMetadata; - } - - /** - * Returns version info - * - * @return array - */ - public function build() - { - return [ - 'platformAndClient' => [ - 'storePlatform' => $this->productMetadata->getName() . ' ' . $this->productMetadata->getEdition(), - 'storePlatformVersion' => $this->productMetadata->getVersion(), - 'signifydClientApp' => $this->productMetadata->getName(), - 'signifydClientAppVersion' => self::$clientVersion, - ] - ]; - } -} diff --git a/app/code/Magento/Signifyd/Model/SignifydGateway/Request/CreateCaseBuilder.php b/app/code/Magento/Signifyd/Model/SignifydGateway/Request/CreateCaseBuilder.php deleted file mode 100644 index 3e41003d47842..0000000000000 --- a/app/code/Magento/Signifyd/Model/SignifydGateway/Request/CreateCaseBuilder.php +++ /dev/null @@ -1,131 +0,0 @@ -<?php -/** - * Copyright © Magento, Inc. All rights reserved. - * See COPYING.txt for license details. - */ -namespace Magento\Signifyd\Model\SignifydGateway\Request; - -use Magento\Sales\Model\OrderFactory; - -/** - * Signifyd case creation request builder. - * - * Handles the conversion from Magento Order to Signifyd Case. - * @SuppressWarnings(PHPMD.CouplingBetweenObjects) - */ -class CreateCaseBuilder implements CreateCaseBuilderInterface -{ - /** - * @var OrderFactory - */ - private $orderFactory; - - /** - * @var PurchaseBuilder - */ - private $purchaseBuilder; - - /** - * @var CardBuilder - */ - private $cardBuilder; - - /** - * @var RecipientBuilder - */ - private $recipientBuilder; - - /** - * @var SellerBuilder - */ - private $sellerBuilder; - - /** - * @var ClientVersionBuilder - */ - private $clientVersionBuilder; - - /** - * @var UserAccountBuilder - */ - private $userAccountBuilder; - - /** - * @param OrderFactory $orderFactory - * @param PurchaseBuilder $purchaseBuilder - * @param CardBuilder $cardBuilder - * @param RecipientBuilder $recipientBuilder - * @param SellerBuilder $sellerBuilder - * @param ClientVersionBuilder $clientVersionBuilder - * @param UserAccountBuilder $userAccountBuilder - */ - public function __construct( - OrderFactory $orderFactory, - PurchaseBuilder $purchaseBuilder, - CardBuilder $cardBuilder, - RecipientBuilder $recipientBuilder, - SellerBuilder $sellerBuilder, - ClientVersionBuilder $clientVersionBuilder, - UserAccountBuilder $userAccountBuilder - ) { - $this->orderFactory = $orderFactory; - $this->purchaseBuilder = $purchaseBuilder; - $this->cardBuilder = $cardBuilder; - $this->recipientBuilder = $recipientBuilder; - $this->sellerBuilder = $sellerBuilder; - $this->clientVersionBuilder = $clientVersionBuilder; - $this->userAccountBuilder = $userAccountBuilder; - } - - /** - * @inheritdoc - */ - public function build($orderId) - { - /* @var $order \Magento\Sales\Model\Order */ - $order = $this->orderFactory->create()->load($orderId); - - return $this->removeEmptyValues( - array_merge( - $this->purchaseBuilder->build($order), - $this->cardBuilder->build($order), - $this->recipientBuilder->build($order), - $this->userAccountBuilder->build($order), - $this->sellerBuilder->build($order), - $this->clientVersionBuilder->build() - ) - ); - } - - /** - * Remove empty and null values. - * - * @param array $data - * @return array - */ - private function removeEmptyValues($data) - { - foreach ($data as $key => $value) { - if (is_array($value)) { - $data[$key] = $this->removeEmptyValues($data[$key]); - } - - if ($this->isEmpty($data[$key])) { - unset($data[$key]); - } - } - - return $data; - } - - /** - * Empty values are null, empty string and empty array. - * - * @param mixed $value - * @return bool - */ - private function isEmpty($value) - { - return $value === null || (is_array($value) && empty($value)); - } -} diff --git a/app/code/Magento/Signifyd/Model/SignifydGateway/Request/CreateCaseBuilderInterface.php b/app/code/Magento/Signifyd/Model/SignifydGateway/Request/CreateCaseBuilderInterface.php deleted file mode 100644 index 56662f69e7c5a..0000000000000 --- a/app/code/Magento/Signifyd/Model/SignifydGateway/Request/CreateCaseBuilderInterface.php +++ /dev/null @@ -1,22 +0,0 @@ -<?php -/** - * Copyright © Magento, Inc. All rights reserved. - * See COPYING.txt for license details. - */ -namespace Magento\Signifyd\Model\SignifydGateway\Request; - -/** - * Collects information about order and build array with parameters required by Signifyd API - * - * @see https://www.signifyd.com/docs/api/#/reference/cases/create-a-case - */ -interface CreateCaseBuilderInterface -{ - /** - * Returns params for Case creation request - * - * @param int $orderId - * @return array - */ - public function build($orderId); -} diff --git a/app/code/Magento/Signifyd/Model/SignifydGateway/Request/PurchaseBuilder.php b/app/code/Magento/Signifyd/Model/SignifydGateway/Request/PurchaseBuilder.php deleted file mode 100644 index 5e544e4b4048e..0000000000000 --- a/app/code/Magento/Signifyd/Model/SignifydGateway/Request/PurchaseBuilder.php +++ /dev/null @@ -1,227 +0,0 @@ -<?php -/** - * Copyright © Magento, Inc. All rights reserved. - * See COPYING.txt for license details. - */ -namespace Magento\Signifyd\Model\SignifydGateway\Request; - -use Magento\Framework\App\Area; -use Magento\Framework\Config\ScopeInterface; -use Magento\Framework\Exception\ConfigurationMismatchException; -use Magento\Framework\Intl\DateTimeFactory; -use Magento\Sales\Api\Data\OrderPaymentInterface; -use Magento\Sales\Model\Order; -use Magento\Signifyd\Model\PaymentMethodMapper\PaymentMethodMapper; -use Magento\Signifyd\Model\PaymentVerificationFactory; -use Magento\Signifyd\Model\SignifydOrderSessionId; - -/** - * Prepare data related to purchase event represented in case creation request. - */ -class PurchaseBuilder -{ - /** - * @var DateTimeFactory - */ - private $dateTimeFactory; - - /** - * @var ScopeInterface - */ - private $scope; - - /** - * @var SignifydOrderSessionId - */ - private $signifydOrderSessionId; - - /** - * @var PaymentVerificationFactory - */ - private $paymentVerificationFactory; - - /** - * @var PaymentMethodMapper - */ - private $paymentMethodMapper; - - /** - * PurchaseBuilder constructor. - * - * @param DateTimeFactory $dateTimeFactory - * @param ScopeInterface $scope - * @param SignifydOrderSessionId $signifydOrderSessionId - * @param PaymentVerificationFactory $paymentVerificationFactory - * @param PaymentMethodMapper $paymentMethodMapper - */ - public function __construct( - DateTimeFactory $dateTimeFactory, - ScopeInterface $scope, - SignifydOrderSessionId $signifydOrderSessionId, - PaymentVerificationFactory $paymentVerificationFactory, - PaymentMethodMapper $paymentMethodMapper - ) { - $this->dateTimeFactory = $dateTimeFactory; - $this->scope = $scope; - $this->signifydOrderSessionId = $signifydOrderSessionId; - $this->paymentVerificationFactory = $paymentVerificationFactory; - $this->paymentMethodMapper = $paymentMethodMapper; - } - - /** - * Returns purchase data params - * - * @param Order $order - * @return array - * @throws ConfigurationMismatchException - */ - public function build(Order $order) - { - $orderPayment = $order->getPayment(); - $createdAt = $this->dateTimeFactory->create( - $order->getCreatedAt(), - new \DateTimeZone('UTC') - ); - - $result = [ - 'purchase' => [ - 'orderSessionId' => $this->signifydOrderSessionId->get($order->getQuoteId()), - 'browserIpAddress' => $order->getRemoteIp(), - 'orderId' => $order->getIncrementId(), - 'createdAt' => $createdAt->format(\DateTime::ATOM), - 'paymentGateway' => $this->getPaymentGateway($orderPayment->getMethod()), - 'transactionId' => $orderPayment->getLastTransId(), - 'currency' => $order->getOrderCurrencyCode(), - 'avsResponseCode' => $this->getAvsCode($orderPayment), - 'cvvResponseCode' => $this->getCvvCode($orderPayment), - 'orderChannel' => $this->getOrderChannel(), - 'totalPrice' => $order->getGrandTotal(), - 'paymentMethod' => $this->paymentMethodMapper - ->getSignifydPaymentMethodCode($orderPayment->getMethod()) - ], - ]; - - $shippingDescription = $order->getShippingDescription(); - if ($shippingDescription !== null) { - $result['purchase']['shipments'] = [ - [ - 'shipper' => $this->getShipper($order->getShippingDescription()), - 'shippingMethod' => $this->getShippingMethod($order->getShippingDescription()), - 'shippingPrice' => $order->getShippingAmount() - ] - ]; - } - - $products = $this->getProducts($order); - if (!empty($products)) { - $result['purchase']['products'] = $products; - } - - return $result; - } - - /** - * Returns the products purchased in the transaction. - * - * @param Order $order - * @return array - */ - private function getProducts(Order $order) - { - $result = []; - foreach ($order->getAllItems() as $orderItem) { - $result[] = [ - 'itemId' => $orderItem->getSku(), - 'itemName' => $orderItem->getName(), - 'itemPrice' => $orderItem->getPrice(), - 'itemQuantity' => (int)$orderItem->getQtyOrdered(), - 'itemUrl' => $orderItem->getProduct()->getProductUrl(), - 'itemWeight' => $orderItem->getProduct()->getWeight() - ]; - } - - return $result; - } - - /** - * Returns the name of the shipper - * - * @param string $shippingDescription - * @return string - */ - private function getShipper($shippingDescription) - { - $result = explode(' - ', $shippingDescription, 2); - - return count($result) == 2 ? $result[0] : ''; - } - - /** - * Returns the type of the shipment method used - * - * @param string $shippingDescription - * @return string - */ - private function getShippingMethod($shippingDescription) - { - $result = explode(' - ', $shippingDescription, 2); - - return count($result) == 2 ? $result[1] : ''; - } - - /** - * Returns the gateway that processed the transaction. For PayPal orders should be paypal_account. - * - * @param string $gatewayCode - * @return string - */ - private function getPaymentGateway($gatewayCode) - { - $payPalCodeList = [ - 'paypal_express', - 'braintree_paypal', - 'payflowpro', - 'payflow_express', - 'payflow_link', - 'payflow_advanced', - 'hosted_pro', - ]; - return in_array($gatewayCode, $payPalCodeList) ? 'paypal_account' : $gatewayCode; - } - - /** - * Returns WEB for web-orders, PHONE for orders created by Admin - * - * @return string - */ - private function getOrderChannel() - { - return $this->scope->getCurrentScope() === Area::AREA_ADMINHTML ? 'PHONE' : 'WEB'; - } - - /** - * Gets AVS code for order payment method. - * - * @param OrderPaymentInterface $orderPayment - * @return string - * @throws ConfigurationMismatchException - */ - private function getAvsCode(OrderPaymentInterface $orderPayment) - { - $avsAdapter = $this->paymentVerificationFactory->createPaymentAvs($orderPayment->getMethod()); - return $avsAdapter->getCode($orderPayment); - } - - /** - * Gets CVV code for order payment method. - * - * @param OrderPaymentInterface $orderPayment - * @return string - * @throws ConfigurationMismatchException - */ - private function getCvvCode(OrderPaymentInterface $orderPayment) - { - $cvvAdapter = $this->paymentVerificationFactory->createPaymentCvv($orderPayment->getMethod()); - return $cvvAdapter->getCode($orderPayment); - } -} diff --git a/app/code/Magento/Signifyd/Model/SignifydGateway/Request/RecipientBuilder.php b/app/code/Magento/Signifyd/Model/SignifydGateway/Request/RecipientBuilder.php deleted file mode 100644 index d9d26c8943b88..0000000000000 --- a/app/code/Magento/Signifyd/Model/SignifydGateway/Request/RecipientBuilder.php +++ /dev/null @@ -1,55 +0,0 @@ -<?php -/** - * Copyright © Magento, Inc. All rights reserved. - * See COPYING.txt for license details. - */ -namespace Magento\Signifyd\Model\SignifydGateway\Request; - -use Magento\Sales\Model\Order; - -/** - * Prepare data related to person or organization receiving the items purchased - */ -class RecipientBuilder -{ - /** - * @var AddressBuilder - */ - private $addressBuilder; - - /** - * @param AddressBuilder $addressBuilder - */ - public function __construct( - AddressBuilder $addressBuilder - ) { - $this->addressBuilder = $addressBuilder; - } - - /** - * Returns recipient data params based on shipping address - * - * @param Order $order - * @return array - */ - public function build(Order $order) - { - $result = []; - $address = $order->getShippingAddress(); - if ($address === null) { - return $result; - } - - $result = [ - 'recipient' => [ - 'fullName' => $address->getName(), - 'confirmationEmail' => $address->getEmail(), - 'confirmationPhone' => $address->getTelephone(), - 'organization' => $address->getCompany(), - 'deliveryAddress' => $this->addressBuilder->build($address) - ] - ]; - - return $result; - } -} diff --git a/app/code/Magento/Signifyd/Model/SignifydGateway/Request/SellerBuilder.php b/app/code/Magento/Signifyd/Model/SignifydGateway/Request/SellerBuilder.php deleted file mode 100644 index b2cf0401b247f..0000000000000 --- a/app/code/Magento/Signifyd/Model/SignifydGateway/Request/SellerBuilder.php +++ /dev/null @@ -1,136 +0,0 @@ -<?php -/** - * Copyright © Magento, Inc. All rights reserved. - * See COPYING.txt for license details. - */ -namespace Magento\Signifyd\Model\SignifydGateway\Request; - -use Magento\Directory\Model\RegionFactory; -use Magento\Framework\App\Config\ScopeConfigInterface; -use Magento\Sales\Model\Order; -use Magento\Sales\Model\Order\Shipment; -use Magento\Store\Api\Data\StoreInterface; -use Magento\Store\Model\Information; -use Magento\Store\Model\ScopeInterface; - -/** - * Prepare data related to the seller of the product. - * - * This information is optional unless you are operating a marketplace, - * listing goods on behalf of multiple sellers who each hold a seller account registered with your site. - */ -class SellerBuilder -{ - /** - * @var ScopeConfigInterface - */ - private $scopeConfig; - - /** - * @var RegionFactory - */ - private $regionFactory; - - /** - * @var array - */ - private $regionCodes = []; - - /** - * @param ScopeConfigInterface $scopeConfig - * @param RegionFactory $regionFactory - */ - public function __construct( - ScopeConfigInterface $scopeConfig, - RegionFactory $regionFactory - ) { - $this->scopeConfig = $scopeConfig; - $this->regionFactory = $regionFactory; - } - - /** - * Returns seller data params - * - * @param Order $order - * @return array - */ - public function build(Order $order) - { - $store = $order->getStore(); - - return [ - 'seller' => [ - 'name' => $this->getConfigValue(Information::XML_PATH_STORE_INFO_NAME, $store), - 'domain' => $this->getPublicDomain($store), - 'shipFromAddress' => [ - 'streetAddress' => $this->getConfigValue(Shipment::XML_PATH_STORE_ADDRESS1, $store), - 'unit' => $this->getConfigValue(Shipment::XML_PATH_STORE_ADDRESS2, $store), - 'city' => $this->getConfigValue(Shipment::XML_PATH_STORE_CITY, $store), - 'provinceCode' => $this->getRegionCodeById( - $this->getConfigValue(Shipment::XML_PATH_STORE_REGION_ID, $store) - ), - 'postalCode' => $this->getConfigValue(Shipment::XML_PATH_STORE_ZIP, $store), - 'countryCode' => $this->getConfigValue(Shipment::XML_PATH_STORE_COUNTRY_ID, $store), - ], - 'corporateAddress' => [ - 'streetAddress' => $this->getConfigValue(Information::XML_PATH_STORE_INFO_STREET_LINE1, $store), - 'unit' => $this->getConfigValue(Information::XML_PATH_STORE_INFO_STREET_LINE2, $store), - 'city' => $this->getConfigValue(Information::XML_PATH_STORE_INFO_CITY, $store), - 'provinceCode' => $this->getRegionCodeById( - $this->getConfigValue(Information::XML_PATH_STORE_INFO_REGION_CODE, $store) - ), - 'postalCode' => $this->getConfigValue(Information::XML_PATH_STORE_INFO_POSTCODE, $store), - 'countryCode' => $this->getConfigValue(Information::XML_PATH_STORE_INFO_COUNTRY_CODE, $store), - ] - ] - ]; - } - - /** - * Returns region code by id - * - * @param int $regionId - * @return string - */ - private function getRegionCodeById($regionId) - { - if (!isset($this->regionCodes[$regionId])) { - $this->regionCodes[$regionId] = $this->regionFactory->create()->load($regionId)->getCode(); - } - - return $this->regionCodes[$regionId]; - } - - /** - * Returns value from config - * - * @param string $value - * @param StoreInterface $store - * @return mixed - */ - private function getConfigValue($value, StoreInterface $store) - { - return $this->scopeConfig->getValue( - $value, - ScopeInterface::SCOPE_STORE, - $store - ); - } - - /** - * Returns public domain name - * - * @param StoreInterface $store - * @return string|null null if no DNS records corresponding to a current host found - */ - private function getPublicDomain(StoreInterface $store) - { - $baseUrl = $store->getBaseUrl(); - $domain = parse_url($baseUrl, PHP_URL_HOST); - if (\function_exists('checkdnsrr') && false === \checkdnsrr($domain)) { - return null; - } - - return $domain; - } -} diff --git a/app/code/Magento/Signifyd/Model/SignifydGateway/Request/UserAccountBuilder.php b/app/code/Magento/Signifyd/Model/SignifydGateway/Request/UserAccountBuilder.php deleted file mode 100644 index 0da49b85da869..0000000000000 --- a/app/code/Magento/Signifyd/Model/SignifydGateway/Request/UserAccountBuilder.php +++ /dev/null @@ -1,100 +0,0 @@ -<?php -/** - * Copyright © Magento, Inc. All rights reserved. - * See COPYING.txt for license details. - */ -namespace Magento\Signifyd\Model\SignifydGateway\Request; - -use Magento\Sales\Model\Order; -use Magento\Signifyd\Model\CustomerOrders; - -/** - * Prepares details based on registered user account info - */ -class UserAccountBuilder -{ - /** - * @var \Magento\Customer\Api\CustomerRepositoryInterface - */ - private $customerRepository; - - /** - * @var \Magento\Framework\Intl\DateTimeFactory - */ - private $dateTimeFactory; - - /** - * @var CustomerOrders - */ - private $customerOrders; - - /** - * @param \Magento\Customer\Api\CustomerRepositoryInterface $customerRepository - * @param CustomerOrders $customerOrders - * @param \Magento\Framework\Intl\DateTimeFactory $dateTimeFactory - */ - public function __construct( - \Magento\Customer\Api\CustomerRepositoryInterface $customerRepository, - CustomerOrders $customerOrders, - \Magento\Framework\Intl\DateTimeFactory $dateTimeFactory - ) { - $this->customerRepository = $customerRepository; - $this->dateTimeFactory = $dateTimeFactory; - $this->customerOrders = $customerOrders; - } - - /** - * Returns user account data params. - * Only for registered customers. - * - * @param Order $order - * @return array - */ - public function build(Order $order) - { - $result = []; - - $customerId = $order->getCustomerId(); - if (null === $customerId) { - return $result; - } - - $customer = $this->customerRepository->getById($customerId); - $result = [ - 'userAccount' => [ - 'email' => $customer->getEmail(), - 'username' => $customer->getEmail(), - 'phone' => $order->getBillingAddress()->getTelephone(), - 'accountNumber' => $customerId, - 'createdDate' => $this->formatDate($customer->getCreatedAt()), - 'lastUpdateDate' => $this->formatDate($customer->getUpdatedAt()) - ] - ]; - - $ordersInfo = $this->customerOrders->getAggregatedOrdersInfo($customerId); - if (isset($ordersInfo['aggregateOrderCount'])) { - $result['userAccount']['aggregateOrderCount'] = $ordersInfo['aggregateOrderCount']; - } - if (isset($ordersInfo['aggregateOrderDollars'])) { - $result['userAccount']['aggregateOrderDollars'] = $ordersInfo['aggregateOrderDollars']; - } - - return $result; - } - - /** - * Returns date formatted according to ISO8601. - * - * @param string $date - * @return string - */ - private function formatDate($date) - { - $result = $this->dateTimeFactory->create( - $date, - new \DateTimeZone('UTC') - ); - - return $result->format(\DateTime::ATOM); - } -} diff --git a/app/code/Magento/Signifyd/Model/SignifydGateway/Response/WebhookMessage.php b/app/code/Magento/Signifyd/Model/SignifydGateway/Response/WebhookMessage.php deleted file mode 100644 index f77db737473c0..0000000000000 --- a/app/code/Magento/Signifyd/Model/SignifydGateway/Response/WebhookMessage.php +++ /dev/null @@ -1,64 +0,0 @@ -<?php -/** - * Copyright © Magento, Inc. All rights reserved. - * See COPYING.txt for license details. - */ -namespace Magento\Signifyd\Model\SignifydGateway\Response; - -/** - * Webhooks are messages sent by SIGNIFYD via HTTP POST to a url you configure on your - * Notifications page in the SIGNIFYD settings. - * - * WebhookMessage messages are sent when certain events occur in the life of an investigation. - * They allow your application to receive pushed updates about a case, rather than poll SIGNIFYD for status changes. - * - * @see https://www.signifyd.com/docs/api/#/reference/webhooks - */ -class WebhookMessage -{ - /** - * Decoded webhook request body. - * - * @var array - */ - private $data; - - /** - * Event topic identifier. - * - * @var string - */ - private $eventTopic; - - /** - * @param array $data - * @param string $eventTopic - */ - public function __construct( - array $data, - $eventTopic - ) { - $this->data = $data; - $this->eventTopic = $eventTopic; - } - - /** - * Returns decoded webhook request body. - * - * @return array - */ - public function getData() - { - return $this->data; - } - - /** - * Returns event topic identifier. - * - * @return string - */ - public function getEventTopic() - { - return $this->eventTopic; - } -} diff --git a/app/code/Magento/Signifyd/Model/SignifydGateway/Response/WebhookMessageReader.php b/app/code/Magento/Signifyd/Model/SignifydGateway/Response/WebhookMessageReader.php deleted file mode 100644 index 50389102359b1..0000000000000 --- a/app/code/Magento/Signifyd/Model/SignifydGateway/Response/WebhookMessageReader.php +++ /dev/null @@ -1,65 +0,0 @@ -<?php -/** - * Copyright © Magento, Inc. All rights reserved. - * See COPYING.txt for license details. - */ -namespace Magento\Signifyd\Model\SignifydGateway\Response; - -use Magento\Framework\Json\DecoderInterface; - -/** - * Reads request and produces webhook message data object based on request params. - */ -class WebhookMessageReader -{ - /** - * @var DecoderInterface - */ - private $dataDecoder; - - /** - * @var WebhookMessageFactory - */ - private $webhookMessageFactory; - - /** - * @param DecoderInterface $decoder - * @param WebhookMessageFactory $webhookMessageFactory - */ - public function __construct( - DecoderInterface $decoder, - WebhookMessageFactory $webhookMessageFactory - ) { - $this->dataDecoder = $decoder; - $this->webhookMessageFactory = $webhookMessageFactory; - } - - /** - * Returns webhook message data object. - * - * @param WebhookRequest $request - * @return WebhookMessage - * @throws \InvalidArgumentException - */ - public function read(WebhookRequest $request) - { - try { - $decodedData = $this->dataDecoder->decode($request->getBody()); - } catch (\Exception $e) { - throw new \InvalidArgumentException( - 'Webhook request body is not valid JSON: ' . $e->getMessage(), - $e->getCode(), - $e - ); - } - - $webhookMessage = $this->webhookMessageFactory->create( - [ - 'data' => $decodedData, - 'eventTopic' => $request->getEventTopic() - ] - ); - - return $webhookMessage; - } -} diff --git a/app/code/Magento/Signifyd/Model/SignifydGateway/Response/WebhookRequest.php b/app/code/Magento/Signifyd/Model/SignifydGateway/Response/WebhookRequest.php deleted file mode 100644 index 214ccf0eeb70f..0000000000000 --- a/app/code/Magento/Signifyd/Model/SignifydGateway/Response/WebhookRequest.php +++ /dev/null @@ -1,58 +0,0 @@ -<?php -/** - * Copyright © Magento, Inc. All rights reserved. - * See COPYING.txt for license details. - */ -namespace Magento\Signifyd\Model\SignifydGateway\Response; - -use Magento\Framework\App\Request\Http; - -/** - * Reads Signifyd webhook request data. - */ -class WebhookRequest -{ - /** - * @var Http - */ - private $request; - - /** - * @param Http $request - */ - public function __construct( - Http $request - ) { - $this->request = $request; - } - - /** - * Returns Base64 encoded output of the HMAC SHA256 encoding of the JSON body of the message. - * - * @return string - */ - public function getHash() - { - return (string)$this->request->getHeader('X-SIGNIFYD-SEC-HMAC-SHA256'); - } - - /** - * Returns event topic identifier. - * - * @return string - */ - public function getEventTopic() - { - return (string)$this->request->getHeader('X-SIGNIFYD-TOPIC'); - } - - /** - * Returns raw data from the request body. - * - * @return string - */ - public function getBody() - { - return (string)$this->request->getContent(); - } -} diff --git a/app/code/Magento/Signifyd/Model/SignifydGateway/Response/WebhookRequestValidator.php b/app/code/Magento/Signifyd/Model/SignifydGateway/Response/WebhookRequestValidator.php deleted file mode 100644 index 274ef2f854684..0000000000000 --- a/app/code/Magento/Signifyd/Model/SignifydGateway/Response/WebhookRequestValidator.php +++ /dev/null @@ -1,113 +0,0 @@ -<?php -/** - * Copyright © Magento, Inc. All rights reserved. - * See COPYING.txt for license details. - */ -namespace Magento\Signifyd\Model\SignifydGateway\Response; - -use Magento\Framework\Json\DecoderInterface; -use Magento\Signifyd\Model\Config; - -/** - * Validates webhook request. - * - */ -class WebhookRequestValidator -{ - /** - * Allowed topic identifiers which will be sent in the X-SIGNIFYD-TOPIC header of the webhook. - * - * @var array - */ - private $allowedTopicValues = [ - 'cases/creation', - 'cases/rescore', - 'cases/review', - 'guarantees/completion', - 'cases/test' - ]; - - /** - * @var Config - */ - private $config; - - /** - * @var DecoderInterface - */ - private $decoder; - - /** - * @param Config $config - * @param DecoderInterface $decoder - */ - public function __construct( - Config $config, - DecoderInterface $decoder - ) { - $this->config = $config; - $this->decoder = $decoder; - } - - /** - * Validates webhook request. - * - * @param WebhookRequest $webhookRequest - * @return bool - */ - public function validate(WebhookRequest $webhookRequest) - { - $body = $webhookRequest->getBody(); - $eventTopic = $webhookRequest->getEventTopic(); - $hash = $webhookRequest->getHash(); - - return $this->isValidTopic($eventTopic) - && $this->isValidBody($body) - && $this->isValidHash($eventTopic, $body, $hash); - } - - /** - * Checks if value of topic identifier is in allowed list - * - * @param string $topic topic identifier. - * @return bool - */ - private function isValidTopic($topic) - { - return in_array($topic, $this->allowedTopicValues); - } - - /** - * Verifies a webhook request body is valid JSON and not empty. - * - * @param string $body - * @return bool - */ - private function isValidBody($body) - { - try { - $decodedBody = $this->decoder->decode($body); - } catch (\Exception $e) { - return false; - } - - return !empty($decodedBody); - } - - /** - * Verifies a webhook request has in fact come from SIGNIFYD. - * - * @param string $eventTopic - * @param string $body - * @param string $hash - * @return bool - */ - private function isValidHash($eventTopic, $body, $hash) - { - // In the case that this is a webhook test, the encoding ABCDE is allowed - $apiKey = $eventTopic == 'cases/test' ? 'ABCDE' : $this->config->getApiKey(); - $actualHash = base64_encode(hash_hmac('sha256', $body, $apiKey, true)); - - return $hash === $actualHash; - } -} diff --git a/app/code/Magento/Signifyd/Model/SignifydOrderSessionId.php b/app/code/Magento/Signifyd/Model/SignifydOrderSessionId.php deleted file mode 100644 index 52746bc4ec6c5..0000000000000 --- a/app/code/Magento/Signifyd/Model/SignifydOrderSessionId.php +++ /dev/null @@ -1,39 +0,0 @@ -<?php -/** - * Copyright © Magento, Inc. All rights reserved. - * See COPYING.txt for license details. - */ -namespace Magento\Signifyd\Model; - -use Magento\Framework\DataObject\IdentityGeneratorInterface; - -/** - * Encapsulates generation of uuid by quote id. - */ -class SignifydOrderSessionId -{ - /** - * @var IdentityGeneratorInterface - */ - private $identityGenerator; - - /** - * @param IdentityGeneratorInterface $identityGenerator - */ - public function __construct( - IdentityGeneratorInterface $identityGenerator - ) { - $this->identityGenerator = $identityGenerator; - } - - /** - * Returns unique identifier through generation uuid by quote id. - * - * @param int $quoteId - * @return string - */ - public function get($quoteId) - { - return $this->identityGenerator->generateIdForData($quoteId); - } -} diff --git a/app/code/Magento/Signifyd/Observer/PlaceOrder.php b/app/code/Magento/Signifyd/Observer/PlaceOrder.php deleted file mode 100644 index 7c451a129cccd..0000000000000 --- a/app/code/Magento/Signifyd/Observer/PlaceOrder.php +++ /dev/null @@ -1,112 +0,0 @@ -<?php -/** - * Copyright © Magento, Inc. All rights reserved. - * See COPYING.txt for license details. - */ -namespace Magento\Signifyd\Observer; - -use Magento\Framework\Event; -use Magento\Framework\Event\Observer; -use Magento\Framework\Event\ObserverInterface; -use Magento\Framework\Exception\AlreadyExistsException; -use Magento\Sales\Api\Data\OrderInterface; -use Magento\Sales\Model\Order; -use Magento\Signifyd\Api\CaseCreationServiceInterface; -use Magento\Signifyd\Model\Config; -use Psr\Log\LoggerInterface; - -/** - * Observer should be triggered when new order is created and placed. - * If Signifyd integration enabled in configuration then new case will be created. - */ -class PlaceOrder implements ObserverInterface -{ - /** - * @var Config - */ - private $signifydIntegrationConfig; - - /** - * @var CaseCreationServiceInterface - */ - private $caseCreationService; - - /** - * @var LoggerInterface - */ - private $logger; - - /** - * @param Config $signifydIntegrationConfig - * @param CaseCreationServiceInterface $caseCreationService - * @param LoggerInterface $logger - */ - public function __construct( - Config $signifydIntegrationConfig, - CaseCreationServiceInterface $caseCreationService, - LoggerInterface $logger - ) { - $this->signifydIntegrationConfig = $signifydIntegrationConfig; - $this->caseCreationService = $caseCreationService; - $this->logger = $logger; - } - - /** - * {@inheritdoc} - */ - public function execute(Observer $observer) - { - $orders = $this->extractOrders( - $observer->getEvent() - ); - - if (null === $orders) { - return; - } - - foreach ($orders as $order) { - $storeId = $order->getStoreId(); - if ($this->signifydIntegrationConfig->isActive($storeId)) { - $this->createCaseForOrder($order); - } - } - } - - /** - * Creates Signifyd case for single order with online payment method. - * - * @param OrderInterface $order - * @return void - */ - private function createCaseForOrder($order) - { - $orderId = $order->getEntityId(); - if (null === $orderId - || $order->getPayment()->getMethodInstance()->isOffline() - || $order->getState() === Order::STATE_PENDING_PAYMENT) { - return; - } - - try { - $this->caseCreationService->createForOrder($orderId); - } catch (AlreadyExistsException $e) { - $this->logger->error($e->getMessage()); - } - } - - /** - * Returns Orders entity list from Event data container - * - * @param Event $event - * @return OrderInterface[]|null - */ - private function extractOrders(Event $event) - { - $order = $event->getData('order'); - if (null !== $order) { - return [$order]; - } - - return $event->getData('orders'); - } -} diff --git a/app/code/Magento/Signifyd/Plugin/OrderPlugin.php b/app/code/Magento/Signifyd/Plugin/OrderPlugin.php deleted file mode 100644 index 663409d0eb824..0000000000000 --- a/app/code/Magento/Signifyd/Plugin/OrderPlugin.php +++ /dev/null @@ -1,52 +0,0 @@ -<?php -/** - * Copyright © Magento, Inc. All rights reserved. - * See COPYING.txt for license details. - */ -namespace Magento\Signifyd\Plugin; - -use Magento\Sales\Api\Data\OrderInterface; -use Magento\Sales\Model\Order; -use Magento\Signifyd\Api\GuaranteeCancelingServiceInterface; - -/** - * Plugin for Magento\Sales\Model\Order. - * - * @see Order - */ -class OrderPlugin -{ - /** - * @var GuaranteeCancelingServiceInterface - */ - private $guaranteeCancelingService; - - /** - * @param GuaranteeCancelingServiceInterface $guaranteeCancelingService - */ - public function __construct( - GuaranteeCancelingServiceInterface $guaranteeCancelingService - ) { - $this->guaranteeCancelingService = $guaranteeCancelingService; - } - - /** - * Performs Signifyd guarantee cancel operation after order canceling - * if cancel order operation was successful. - * - * @see Order::cancel - * @param Order $order - * @param OrderInterface $result - * @return OrderInterface - */ - public function afterCancel(Order $order, $result) - { - if ($order->isCanceled()) { - $this->guaranteeCancelingService->cancelForOrder( - $order->getEntityId() - ); - } - - return $result; - } -} diff --git a/app/code/Magento/Signifyd/Plugin/PaymentPlugin.php b/app/code/Magento/Signifyd/Plugin/PaymentPlugin.php deleted file mode 100644 index 17cf4d7e7dbe9..0000000000000 --- a/app/code/Magento/Signifyd/Plugin/PaymentPlugin.php +++ /dev/null @@ -1,67 +0,0 @@ -<?php -/** - * Copyright © Magento, Inc. All rights reserved. - * See COPYING.txt for license details. - */ -namespace Magento\Signifyd\Plugin; - -use Magento\Payment\Model\InfoInterface; -use Magento\Payment\Model\MethodInterface; -use Magento\Signifyd\Api\GuaranteeCancelingServiceInterface; - -/** - * Plugin for Magento\Payment\Model\MethodInterface. - * - * @see MethodInterface - */ -class PaymentPlugin -{ - /** - * @var GuaranteeCancelingServiceInterface - */ - private $guaranteeCancelingService; - - /** - * @param GuaranteeCancelingServiceInterface $guaranteeCancelingService - */ - public function __construct( - GuaranteeCancelingServiceInterface $guaranteeCancelingService - ) { - $this->guaranteeCancelingService = $guaranteeCancelingService; - } - - /** - * Performs Signifyd guarantee cancel operation after payment denying. - * - * @see MethodInterface::denyPayment - * @SuppressWarnings(PHPMD.UnusedFormalParameter) - * - * @param MethodInterface $subject - * @param MethodInterface|bool $result - * @param InfoInterface $payment - * @return bool|MethodInterface - */ - public function afterDenyPayment(MethodInterface $subject, $result, InfoInterface $payment) - { - if ($this->isPaymentDenied($payment, $result)) { - $this->guaranteeCancelingService->cancelForOrder($payment->getParentId()); - } - - return $result; - } - - /** - * Checks if deny payment operation was successful. - * - * Result not false check for payment methods using AbstractMethod. - * Transaction is closed check for payment methods using Gateway. - * - * @param InfoInterface $payment - * @param MethodInterface $result - * @return bool - */ - private function isPaymentDenied($payment, $result) - { - return $result !== false || $payment->getIsTransactionClosed(); - } -} diff --git a/app/code/Magento/Signifyd/README.md b/app/code/Magento/Signifyd/README.md deleted file mode 100644 index 8048716bed444..0000000000000 --- a/app/code/Magento/Signifyd/README.md +++ /dev/null @@ -1,78 +0,0 @@ -# Magento_Signifyd module - -## Overview - -The Magento_Signifyd module provides integration with the [Signifyd](https://www.signifyd.com/) fraud protection system. The integration is based on the Signifyd API; see the [Signifyd API docs](https://www.signifyd.com/docs/api/#/introduction/) for technical details. - -The module implementation allows to: - - - create a [Signifyd case](https://www.signifyd.com/docs/api/#/reference/cases) for a placed order - - automatically receive a [Signifyd guarantee](https://www.signifyd.com/docs/api/#/reference/guarantees) for a created case - - automatically cancel a guarantee when the order is canceled - -## Extensibility - -The Magento_Signifyd module does not add own Events, Layouts, and UI Components as extension points. - -### Public API - -The following interfaces (marked with the `@api` annotation) provide methods that allow to: - -`Magento\Signifyd\Api\Data\CaseInterface` (common abstraction for the Signifyd case entity): - -- set or retrieve all case data fields - -`Magento\Signifyd\Api\CaseManagementInterface`: - -- create a new case entity -- retrieve the existing case entity for a specified order - -`Magento\Signifyd\Api\CaseCreationServiceInterface`: - -- create a case entity for a specified order -- send a request through the Signifyd API to create a new case - -`Magento\Signifyd\Api\CaseRepositoryInterface`: - -- describe methods to work with a case entity - -`Magento\Signifyd\Api\GuaranteeCreationServiceInterface`: - -- send a request through the Signifyd API to create a new case guarantee - -`Magento\Signifyd\Api\GuaranteeCancelingServiceInterface`: -- send a request through the Signifyd API to cancel the Signifyd case guarantee - -`Magento\Signifyd\Api\Data\CaseSearchResultsInterface`: - -- might be used by `Magento\Signifyd\Api\CaseRepositoryInterface` to retrieve a list of case entities by specific conditions - -For information about a public API in Magento 2, see [Public interfaces & APIs](https://devdocs.magento.com/guides/v2.3/extension-dev-guide/api-concepts.html). - -## Additional information - -### Webhooks - -To update the entity data for a case or guarantee, the Magento_Signifyd module uses the [Signifyd Webhooks](https://www.signifyd.com/docs/api/#/reference/webhooks) mechanism. - -The newly created case entities have the `PENDING` status for a case and a guarantee. After receiving Webhook, both statuses are changed to appropriate Signifyd statuses. - -### Debug mode - -The Debug Mode may be enabled in the module configuration. This logs the communication data between the Magento_Signifyd module and the Signifyd service in this file: - - var/log/debug.log - -### Backward incompatible changes - -The Magento_Signifyd module does not introduce backward incompatible changes. - -You can track [backward incompatible changes in patch releases](https://devdocs.magento.com/guides/v2.3/release-notes/backward-incompatible-changes/reference.html). - -### Processing supplementary payment information - -To improve the accuracy of Signifyd's transaction estimation, you may perform these operations (links lead to the Magento Developer Documentation Portal): - -- [Provide custom AVS/CVV mapping](https://devdocs.magento.com/guides/v2.3/payments-integrations/signifyd/signifyd.html#provide-avscvv-response-codes) - -- [Retrieve payment method for a placed order](https://devdocs.magento.com/guides/v2.3/payments-integrations/signifyd/signifyd.html#retrieve-payment-method-for-a-placed-order) diff --git a/app/code/Magento/Signifyd/Test/Mftf/LICENSE.txt b/app/code/Magento/Signifyd/Test/Mftf/LICENSE.txt deleted file mode 100644 index 49525fd99da9c..0000000000000 --- a/app/code/Magento/Signifyd/Test/Mftf/LICENSE.txt +++ /dev/null @@ -1,48 +0,0 @@ - -Open Software License ("OSL") v. 3.0 - -This Open Software License (the "License") applies to any original work of authorship (the "Original Work") whose owner (the "Licensor") has placed the following licensing notice adjacent to the copyright notice for the Original Work: - -Licensed under the Open Software License version 3.0 - - 1. Grant of Copyright License. Licensor grants You a worldwide, royalty-free, non-exclusive, sublicensable license, for the duration of the copyright, to do the following: - - 1. to reproduce the Original Work in copies, either alone or as part of a collective work; - - 2. to translate, adapt, alter, transform, modify, or arrange the Original Work, thereby creating derivative works ("Derivative Works") based upon the Original Work; - - 3. to distribute or communicate copies of the Original Work and Derivative Works to the public, with the proviso that copies of Original Work or Derivative Works that You distribute or communicate shall be licensed under this Open Software License; - - 4. to perform the Original Work publicly; and - - 5. to display the Original Work publicly. - - 2. Grant of Patent License. Licensor grants You a worldwide, royalty-free, non-exclusive, sublicensable license, under patent claims owned or controlled by the Licensor that are embodied in the Original Work as furnished by the Licensor, for the duration of the patents, to make, use, sell, offer for sale, have made, and import the Original Work and Derivative Works. - - 3. Grant of Source Code License. The term "Source Code" means the preferred form of the Original Work for making modifications to it and all available documentation describing how to modify the Original Work. Licensor agrees to provide a machine-readable copy of the Source Code of the Original Work along with each copy of the Original Work that Licensor distributes. Licensor reserves the right to satisfy this obligation by placing a machine-readable copy of the Source Code in an information repository reasonably calculated to permit inexpensive and convenient access by You for as long as Licensor continues to distribute the Original Work. - - 4. Exclusions From License Grant. Neither the names of Licensor, nor the names of any contributors to the Original Work, nor any of their trademarks or service marks, may be used to endorse or promote products derived from this Original Work without express prior permission of the Licensor. Except as expressly stated herein, nothing in this License grants any license to Licensor's trademarks, copyrights, patents, trade secrets or any other intellectual property. No patent license is granted to make, use, sell, offer for sale, have made, or import embodiments of any patent claims other than the licensed claims defined in Section 2. No license is granted to the trademarks of Licensor even if such marks are included in the Original Work. Nothing in this License shall be interpreted to prohibit Licensor from licensing under terms different from this License any Original Work that Licensor otherwise would have a right to license. - - 5. External Deployment. The term "External Deployment" means the use, distribution, or communication of the Original Work or Derivative Works in any way such that the Original Work or Derivative Works may be used by anyone other than You, whether those works are distributed or communicated to those persons or made available as an application intended for use over a network. As an express condition for the grants of license hereunder, You must treat any External Deployment by You of the Original Work or a Derivative Work as a distribution under section 1(c). - - 6. Attribution Rights. You must retain, in the Source Code of any Derivative Works that You create, all copyright, patent, or trademark notices from the Source Code of the Original Work, as well as any notices of licensing and any descriptive text identified therein as an "Attribution Notice." You must cause the Source Code for any Derivative Works that You create to carry a prominent Attribution Notice reasonably calculated to inform recipients that You have modified the Original Work. - - 7. Warranty of Provenance and Disclaimer of Warranty. Licensor warrants that the copyright in and to the Original Work and the patent rights granted herein by Licensor are owned by the Licensor or are sublicensed to You under the terms of this License with the permission of the contributor(s) of those copyrights and patent rights. Except as expressly stated in the immediately preceding sentence, the Original Work is provided under this License on an "AS IS" BASIS and WITHOUT WARRANTY, either express or implied, including, without limitation, the warranties of non-infringement, merchantability or fitness for a particular purpose. THE ENTIRE RISK AS TO THE QUALITY OF THE ORIGINAL WORK IS WITH YOU. This DISCLAIMER OF WARRANTY constitutes an essential part of this License. No license to the Original Work is granted by this License except under this disclaimer. - - 8. Limitation of Liability. Under no circumstances and under no legal theory, whether in tort (including negligence), contract, or otherwise, shall the Licensor be liable to anyone for any indirect, special, incidental, or consequential damages of any character arising as a result of this License or the use of the Original Work including, without limitation, damages for loss of goodwill, work stoppage, computer failure or malfunction, or any and all other commercial damages or losses. This limitation of liability shall not apply to the extent applicable law prohibits such limitation. - - 9. Acceptance and Termination. If, at any time, You expressly assented to this License, that assent indicates your clear and irrevocable acceptance of this License and all of its terms and conditions. If You distribute or communicate copies of the Original Work or a Derivative Work, You must make a reasonable effort under the circumstances to obtain the express assent of recipients to the terms of this License. This License conditions your rights to undertake the activities listed in Section 1, including your right to create Derivative Works based upon the Original Work, and doing so without honoring these terms and conditions is prohibited by copyright law and international treaty. Nothing in this License is intended to affect copyright exceptions and limitations (including 'fair use' or 'fair dealing'). This License shall terminate immediately and You may no longer exercise any of the rights granted to You by this License upon your failure to honor the conditions in Section 1(c). - - 10. Termination for Patent Action. This License shall terminate automatically and You may no longer exercise any of the rights granted to You by this License as of the date You commence an action, including a cross-claim or counterclaim, against Licensor or any licensee alleging that the Original Work infringes a patent. This termination provision shall not apply for an action alleging patent infringement by combinations of the Original Work with other software or hardware. - - 11. Jurisdiction, Venue and Governing Law. Any action or suit relating to this License may be brought only in the courts of a jurisdiction wherein the Licensor resides or in which Licensor conducts its primary business, and under the laws of that jurisdiction excluding its conflict-of-law provisions. The application of the United Nations Convention on Contracts for the International Sale of Goods is expressly excluded. Any use of the Original Work outside the scope of this License or after its termination shall be subject to the requirements and penalties of copyright or patent law in the appropriate jurisdiction. This section shall survive the termination of this License. - - 12. Attorneys' Fees. In any action to enforce the terms of this License or seeking damages relating thereto, the prevailing party shall be entitled to recover its costs and expenses, including, without limitation, reasonable attorneys' fees and costs incurred in connection with such action, including any appeal of such action. This section shall survive the termination of this License. - - 13. Miscellaneous. If any provision of this License is held to be unenforceable, such provision shall be reformed only to the extent necessary to make it enforceable. - - 14. Definition of "You" in This License. "You" throughout this License, whether in upper or lower case, means an individual or a legal entity exercising rights under, and complying with all of the terms of, this License. For legal entities, "You" includes any entity that controls, is controlled by, or is under common control with you. For purposes of this definition, "control" means (i) the power, direct or indirect, to cause the direction or management of such entity, whether by contract or otherwise, or (ii) ownership of fifty percent (50%) or more of the outstanding shares, or (iii) beneficial ownership of such entity. - - 15. Right to Use. You may use the Original Work in all ways not otherwise restricted or conditioned by this License or by law, and Licensor promises not to interfere with or be responsible for such uses by You. - - 16. Modification of This License. This License is Copyright (C) 2005 Lawrence Rosen. Permission is granted to copy, distribute, or communicate this License without modification. Nothing in this License permits You to modify this License as applied to the Original Work or to Derivative Works. However, You may modify the text of this License and copy, distribute or communicate your modified version (the "Modified License") and apply it to other original works of authorship subject to the following conditions: (i) You may not indicate in any way that your Modified License is the "Open Software License" or "OSL" and you may not use those names in the name of your Modified License; (ii) You must replace the notice specified in the first paragraph above with the notice "Licensed under <insert your license name here>" or with a notice of your own that is not confusingly similar to the notice in this License; and (iii) You may not claim that your original works are open source software unless your Modified License has been approved by Open Source Initiative (OSI) and You comply with its license review and certification process. \ No newline at end of file diff --git a/app/code/Magento/Signifyd/Test/Mftf/LICENSE_AFL.txt b/app/code/Magento/Signifyd/Test/Mftf/LICENSE_AFL.txt deleted file mode 100644 index f39d641b18a19..0000000000000 --- a/app/code/Magento/Signifyd/Test/Mftf/LICENSE_AFL.txt +++ /dev/null @@ -1,48 +0,0 @@ - -Academic Free License ("AFL") v. 3.0 - -This Academic Free License (the "License") applies to any original work of authorship (the "Original Work") whose owner (the "Licensor") has placed the following licensing notice adjacent to the copyright notice for the Original Work: - -Licensed under the Academic Free License version 3.0 - - 1. Grant of Copyright License. Licensor grants You a worldwide, royalty-free, non-exclusive, sublicensable license, for the duration of the copyright, to do the following: - - 1. to reproduce the Original Work in copies, either alone or as part of a collective work; - - 2. to translate, adapt, alter, transform, modify, or arrange the Original Work, thereby creating derivative works ("Derivative Works") based upon the Original Work; - - 3. to distribute or communicate copies of the Original Work and Derivative Works to the public, under any license of your choice that does not contradict the terms and conditions, including Licensor's reserved rights and remedies, in this Academic Free License; - - 4. to perform the Original Work publicly; and - - 5. to display the Original Work publicly. - - 2. Grant of Patent License. Licensor grants You a worldwide, royalty-free, non-exclusive, sublicensable license, under patent claims owned or controlled by the Licensor that are embodied in the Original Work as furnished by the Licensor, for the duration of the patents, to make, use, sell, offer for sale, have made, and import the Original Work and Derivative Works. - - 3. Grant of Source Code License. The term "Source Code" means the preferred form of the Original Work for making modifications to it and all available documentation describing how to modify the Original Work. Licensor agrees to provide a machine-readable copy of the Source Code of the Original Work along with each copy of the Original Work that Licensor distributes. Licensor reserves the right to satisfy this obligation by placing a machine-readable copy of the Source Code in an information repository reasonably calculated to permit inexpensive and convenient access by You for as long as Licensor continues to distribute the Original Work. - - 4. Exclusions From License Grant. Neither the names of Licensor, nor the names of any contributors to the Original Work, nor any of their trademarks or service marks, may be used to endorse or promote products derived from this Original Work without express prior permission of the Licensor. Except as expressly stated herein, nothing in this License grants any license to Licensor's trademarks, copyrights, patents, trade secrets or any other intellectual property. No patent license is granted to make, use, sell, offer for sale, have made, or import embodiments of any patent claims other than the licensed claims defined in Section 2. No license is granted to the trademarks of Licensor even if such marks are included in the Original Work. Nothing in this License shall be interpreted to prohibit Licensor from licensing under terms different from this License any Original Work that Licensor otherwise would have a right to license. - - 5. External Deployment. The term "External Deployment" means the use, distribution, or communication of the Original Work or Derivative Works in any way such that the Original Work or Derivative Works may be used by anyone other than You, whether those works are distributed or communicated to those persons or made available as an application intended for use over a network. As an express condition for the grants of license hereunder, You must treat any External Deployment by You of the Original Work or a Derivative Work as a distribution under section 1(c). - - 6. Attribution Rights. You must retain, in the Source Code of any Derivative Works that You create, all copyright, patent, or trademark notices from the Source Code of the Original Work, as well as any notices of licensing and any descriptive text identified therein as an "Attribution Notice." You must cause the Source Code for any Derivative Works that You create to carry a prominent Attribution Notice reasonably calculated to inform recipients that You have modified the Original Work. - - 7. Warranty of Provenance and Disclaimer of Warranty. Licensor warrants that the copyright in and to the Original Work and the patent rights granted herein by Licensor are owned by the Licensor or are sublicensed to You under the terms of this License with the permission of the contributor(s) of those copyrights and patent rights. Except as expressly stated in the immediately preceding sentence, the Original Work is provided under this License on an "AS IS" BASIS and WITHOUT WARRANTY, either express or implied, including, without limitation, the warranties of non-infringement, merchantability or fitness for a particular purpose. THE ENTIRE RISK AS TO THE QUALITY OF THE ORIGINAL WORK IS WITH YOU. This DISCLAIMER OF WARRANTY constitutes an essential part of this License. No license to the Original Work is granted by this License except under this disclaimer. - - 8. Limitation of Liability. Under no circumstances and under no legal theory, whether in tort (including negligence), contract, or otherwise, shall the Licensor be liable to anyone for any indirect, special, incidental, or consequential damages of any character arising as a result of this License or the use of the Original Work including, without limitation, damages for loss of goodwill, work stoppage, computer failure or malfunction, or any and all other commercial damages or losses. This limitation of liability shall not apply to the extent applicable law prohibits such limitation. - - 9. Acceptance and Termination. If, at any time, You expressly assented to this License, that assent indicates your clear and irrevocable acceptance of this License and all of its terms and conditions. If You distribute or communicate copies of the Original Work or a Derivative Work, You must make a reasonable effort under the circumstances to obtain the express assent of recipients to the terms of this License. This License conditions your rights to undertake the activities listed in Section 1, including your right to create Derivative Works based upon the Original Work, and doing so without honoring these terms and conditions is prohibited by copyright law and international treaty. Nothing in this License is intended to affect copyright exceptions and limitations (including "fair use" or "fair dealing"). This License shall terminate immediately and You may no longer exercise any of the rights granted to You by this License upon your failure to honor the conditions in Section 1(c). - - 10. Termination for Patent Action. This License shall terminate automatically and You may no longer exercise any of the rights granted to You by this License as of the date You commence an action, including a cross-claim or counterclaim, against Licensor or any licensee alleging that the Original Work infringes a patent. This termination provision shall not apply for an action alleging patent infringement by combinations of the Original Work with other software or hardware. - - 11. Jurisdiction, Venue and Governing Law. Any action or suit relating to this License may be brought only in the courts of a jurisdiction wherein the Licensor resides or in which Licensor conducts its primary business, and under the laws of that jurisdiction excluding its conflict-of-law provisions. The application of the United Nations Convention on Contracts for the International Sale of Goods is expressly excluded. Any use of the Original Work outside the scope of this License or after its termination shall be subject to the requirements and penalties of copyright or patent law in the appropriate jurisdiction. This section shall survive the termination of this License. - - 12. Attorneys' Fees. In any action to enforce the terms of this License or seeking damages relating thereto, the prevailing party shall be entitled to recover its costs and expenses, including, without limitation, reasonable attorneys' fees and costs incurred in connection with such action, including any appeal of such action. This section shall survive the termination of this License. - - 13. Miscellaneous. If any provision of this License is held to be unenforceable, such provision shall be reformed only to the extent necessary to make it enforceable. - - 14. Definition of "You" in This License. "You" throughout this License, whether in upper or lower case, means an individual or a legal entity exercising rights under, and complying with all of the terms of, this License. For legal entities, "You" includes any entity that controls, is controlled by, or is under common control with you. For purposes of this definition, "control" means (i) the power, direct or indirect, to cause the direction or management of such entity, whether by contract or otherwise, or (ii) ownership of fifty percent (50%) or more of the outstanding shares, or (iii) beneficial ownership of such entity. - - 15. Right to Use. You may use the Original Work in all ways not otherwise restricted or conditioned by this License or by law, and Licensor promises not to interfere with or be responsible for such uses by You. - - 16. Modification of This License. This License is Copyright © 2005 Lawrence Rosen. Permission is granted to copy, distribute, or communicate this License without modification. Nothing in this License permits You to modify this License as applied to the Original Work or to Derivative Works. However, You may modify the text of this License and copy, distribute or communicate your modified version (the "Modified License") and apply it to other original works of authorship subject to the following conditions: (i) You may not indicate in any way that your Modified License is the "Academic Free License" or "AFL" and you may not use those names in the name of your Modified License; (ii) You must replace the notice specified in the first paragraph above with the notice "Licensed under <insert your license name here>" or with a notice of your own that is not confusingly similar to the notice in this License; and (iii) You may not claim that your original works are open source software unless your Modified License has been approved by Open Source Initiative (OSI) and You comply with its license review and certification process. diff --git a/app/code/Magento/Signifyd/Test/Mftf/Page/AdminFraudProtectionPage.xml b/app/code/Magento/Signifyd/Test/Mftf/Page/AdminFraudProtectionPage.xml deleted file mode 100644 index 07b58b8594843..0000000000000 --- a/app/code/Magento/Signifyd/Test/Mftf/Page/AdminFraudProtectionPage.xml +++ /dev/null @@ -1,14 +0,0 @@ -<?xml version="1.0" encoding="UTF-8"?> -<!-- - /** - * Copyright © Magento, Inc. All rights reserved. - * See COPYING.txt for license details. - */ ---> - -<pages xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" - xsi:noNamespaceSchemaLocation="urn:magento:mftf:Page/etc/PageObject.xsd"> - <page name="AdminFraudProtectionPage" url="admin/system_config/edit/section/fraud_protection/" area="admin" module="Magento_Signifyd"> - <section name="AdminSignifydConfigurationSection"/> - </page> -</pages> diff --git a/app/code/Magento/Signifyd/Test/Mftf/README.md b/app/code/Magento/Signifyd/Test/Mftf/README.md deleted file mode 100644 index 9391d7b314ea5..0000000000000 --- a/app/code/Magento/Signifyd/Test/Mftf/README.md +++ /dev/null @@ -1,3 +0,0 @@ -# Signifyd Functional Tests - -The Functional Test Module for **Magento Signifyd** module. diff --git a/app/code/Magento/Signifyd/Test/Mftf/Section/AdminSignifydConfigurationSection.xml b/app/code/Magento/Signifyd/Test/Mftf/Section/AdminSignifydConfigurationSection.xml deleted file mode 100644 index 618e9d520dd87..0000000000000 --- a/app/code/Magento/Signifyd/Test/Mftf/Section/AdminSignifydConfigurationSection.xml +++ /dev/null @@ -1,16 +0,0 @@ -<?xml version="1.0" encoding="UTF-8"?> -<!-- - /** - * Copyright © Magento, Inc. All rights reserved. - * See COPYING.txt for license details. - */ ---> - -<sections xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" - xsi:noNamespaceSchemaLocation="urn:magento:mftf:Page/etc/SectionObject.xsd"> - <section name="AdminSignifydConfigurationSection"> - <element name="head" type="button" selector="#fraud_protection_signifyd_config-head"/> - <element name="enabled" type="input" selector="#fraud_protection_signifyd_config_active"/> - <element name="url" type="text" selector="#fraud_protection_signifyd_config_api_url"/> - </section> -</sections> diff --git a/app/code/Magento/Signifyd/Test/Mftf/Test/AdminSignifydConfigDependentOnActiveFieldTest.xml b/app/code/Magento/Signifyd/Test/Mftf/Test/AdminSignifydConfigDependentOnActiveFieldTest.xml deleted file mode 100644 index dcae0c4091ba6..0000000000000 --- a/app/code/Magento/Signifyd/Test/Mftf/Test/AdminSignifydConfigDependentOnActiveFieldTest.xml +++ /dev/null @@ -1,33 +0,0 @@ -<?xml version="1.0" encoding="UTF-8"?> -<!-- - /** - * Copyright © Magento, Inc. All rights reserved. - * See COPYING.txt for license details. - */ ---> -<tests xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" - xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/testSchema.xsd"> - <test name="AdminSignifydConfigDependentOnActiveFieldTest"> - <annotations> - <features value="Signifyd"/> - <title value="Signifyd config dependent on active field" /> - <description value="Signifyd system configs dependent by Enable this Solution field."/> - <severity value="MINOR"/> - </annotations> - <before> - <actionGroup ref="LoginAsAdmin" stepKey="loginAsAdmin"/> - <magentoCLI command="config:set fraud_protection/signifyd/active 1" stepKey="enableSignifyd"/> - </before> - - <after> - <actionGroup ref="logout" stepKey="logout"/> - <magentoCLI command="config:set fraud_protection/signifyd/active 0" stepKey="disableSignifyd"/> - </after> - - <amOnPage url="{{AdminFraudProtectionPage.url}}" stepKey="openFraudProtectionPagePage" /> - <conditionalClick dependentSelector="{{AdminSignifydConfigurationSection.enabled}}" visible="false" selector="{{AdminSignifydConfigurationSection.head}}" stepKey="openCollapsibleBlock"/> - <seeInField selector="{{AdminSignifydConfigurationSection.url}}" userInput="https://api.signifyd.com/v2/" stepKey="seeApiUrlField"/> - <selectOption selector="{{AdminSignifydConfigurationSection.enabled}}" userInput="0" stepKey="disableSignifydOption"/> - <dontSeeElement selector="{{AdminSignifydConfigurationSection.url}}" stepKey="dontSeeApiUrlField"/> - </test> -</tests> diff --git a/app/code/Magento/Signifyd/Test/Unit/Block/Adminhtml/CaseInfoTest.php b/app/code/Magento/Signifyd/Test/Unit/Block/Adminhtml/CaseInfoTest.php deleted file mode 100644 index 164cd8018fb69..0000000000000 --- a/app/code/Magento/Signifyd/Test/Unit/Block/Adminhtml/CaseInfoTest.php +++ /dev/null @@ -1,150 +0,0 @@ -<?php -/** - * Copyright © Magento, Inc. All rights reserved. - * See COPYING.txt for license details. - */ -namespace Magento\Signifyd\Test\Unit\Block\Adminhtml; - -use Magento\Framework\App\RequestInterface; -use Magento\Framework\TestFramework\Unit\Helper\ObjectManager; -use Magento\Framework\View\Element\Template\Context; -use Magento\Signifyd\Api\Data\CaseInterface; -use Magento\Signifyd\Block\Adminhtml\CaseInfo; -use Magento\Signifyd\Model\CaseManagement; -use Magento\Signifyd\Model\Config; -use PHPUnit_Framework_MockObject_MockObject as MockObject; - -/** - * Tests for Signifyd block information. - * - * Class CaseInfoTest - */ -class CaseInfoTest extends \PHPUnit\Framework\TestCase -{ - /** - * @var CaseInterface|MockObject - */ - private $caseEntity; - - /** - * @var CaseInfo - */ - private $caseInfo; - - /** - * @var Context|MockObject - */ - private $context; - - /** - * @var Config|MockObject - */ - private $config; - - /** - * @var CaseManagement|MockObject - */ - private $caseManagement; - - /** - * @var RequestInterface|MockObject - */ - private $request; - - /** - * @inheritdoc - */ - protected function setUp() - { - $objectManager = new ObjectManager($this); - - $this->context = $this->getMockBuilder(Context::class) - ->disableOriginalConstructor() - ->getMock(); - - $this->request = $this->getMockBuilder(RequestInterface::class) - ->getMockForAbstractClass(); - - $this->context->expects(self::once()) - ->method('getRequest') - ->willReturn($this->request); - - $this->config = $this->getMockBuilder(Config::class) - ->disableOriginalConstructor() - ->getMock(); - - $this->caseManagement = $this->getMockBuilder(CaseManagement::class) - ->disableOriginalConstructor() - ->getMock(); - - $this->caseEntity = $this->getMockBuilder(CaseInterface::class) - ->disableOriginalConstructor() - ->setMethods(['getScore']) - ->getMockForAbstractClass(); - - $this->caseInfo = $objectManager->getObject(CaseInfo::class, [ - 'context' => $this->context, - 'config' => $this->config, - 'caseManagement' => $this->caseManagement - ]); - } - - /** - * Checks label according to Signifyd Guarantee Disposition. - * - * @param string $guaranteeDisposition - * @param string $expectedLabel - * @covers \Magento\Signifyd\Block\Adminhtml\CaseInfo::getCaseGuaranteeDisposition() - * @dataProvider getGuaranteeLabelDataProvider - */ - public function testGetGuaranteeDisposition($guaranteeDisposition, $expectedLabel) - { - $this->caseManagement->expects(self::once()) - ->method('getByOrderId') - ->willReturn($this->caseEntity); - - $this->caseEntity->expects(self::atLeastOnce()) - ->method('getGuaranteeDisposition') - ->willReturn($guaranteeDisposition); - - self::assertEquals( - $expectedLabel, - $this->caseInfo->getCaseGuaranteeDisposition() - ); - } - - /** - * Case Guarantee Disposition and corresponding label data provider. - * - * @return array - */ - public function getGuaranteeLabelDataProvider() - { - return [ - [CaseInterface::GUARANTEE_APPROVED, __('Approved')], - [CaseInterface::GUARANTEE_DECLINED, __('Declined')], - [CaseInterface::GUARANTEE_PENDING, __('Pending')], - [CaseInterface::GUARANTEE_CANCELED, __('Canceled')], - [CaseInterface::GUARANTEE_IN_REVIEW, __('In Review')], - [CaseInterface::GUARANTEE_UNREQUESTED, __('Unrequested')], - ['Unregistered', ''] - ]; - } - - /** - * Checks case property getter with empty case. - * - * @covers \Magento\Signifyd\Block\Adminhtml\CaseInfo::getCaseProperty - */ - public function testCasePropertyWithEmptyCase() - { - $this->caseManagement->expects(self::once()) - ->method('getByOrderId') - ->willReturn(null); - - self::assertEquals( - '', - $this->caseInfo->getCaseGuaranteeDisposition() - ); - } -} diff --git a/app/code/Magento/Signifyd/Test/Unit/Controller/Webhooks/HandlerTest.php b/app/code/Magento/Signifyd/Test/Unit/Controller/Webhooks/HandlerTest.php deleted file mode 100644 index 8b98be338b973..0000000000000 --- a/app/code/Magento/Signifyd/Test/Unit/Controller/Webhooks/HandlerTest.php +++ /dev/null @@ -1,374 +0,0 @@ -<?php -/** - * Copyright © Magento, Inc. All rights reserved. - * See COPYING.txt for license details. - */ -namespace Magento\Signifyd\Test\Unit\Controller\Webhooks; - -use Magento\Framework\App\Action\Context; -use Magento\Framework\App\Response\Http as ResponseHttp; -use Magento\Framework\App\Response\RedirectInterface; -use Magento\Framework\Exception\LocalizedException; -use Magento\Signifyd\Api\CaseRepositoryInterface; -use Magento\Signifyd\Api\Data\CaseInterface; -use Magento\Signifyd\Controller\Webhooks\Handler; -use Magento\Signifyd\Model\CaseServices\UpdatingService; -use Magento\Signifyd\Model\CaseServices\UpdatingServiceFactory; -use Magento\Signifyd\Model\Config; -use Magento\Signifyd\Model\SignifydGateway\Response\WebhookMessage; -use Magento\Signifyd\Model\SignifydGateway\Response\WebhookMessageReader; -use Magento\Signifyd\Model\SignifydGateway\Response\WebhookRequest; -use Magento\Signifyd\Model\SignifydGateway\Response\WebhookRequestValidator; -use PHPUnit_Framework_MockObject_MockObject as MockObject; -use Psr\Log\LoggerInterface; - -/** - * Class IndexTest - * - * @SuppressWarnings(PHPMD.CouplingBetweenObjects) - */ -class HandlerTest extends \PHPUnit\Framework\TestCase -{ - /** - * @var Handler - */ - private $controller; - - /** - * @var LoggerInterface|MockObject - */ - private $logger; - - /** - * @var RedirectInterface|MockObject - */ - private $redirect; - - /** - * @var ResponseHttp|MockObject - */ - private $response; - - /** - * @var Context|MockObject - */ - private $context; - - /** - * @var WebhookRequest|MockObject - */ - private $webhookRequest; - - /** - * @var WebhookMessageReader|MockObject - */ - private $webhookMessageReader; - - /** - * @var WebhookRequestValidator|MockObject - */ - private $webhookRequestValidator; - - /** - * @var UpdatingServiceFactory|MockObject - */ - private $caseUpdatingServiceFactory; - - /** - * @var CaseRepositoryInterface|MockObject - */ - private $caseRepository; - - /** - * @inheritdoc - */ - protected function setUp() - { - $this->context = $this->getMockBuilder(Context::class) - ->disableOriginalConstructor() - ->getMock(); - - $this->webhookRequest = $this->getMockBuilder(WebhookRequest::class) - ->disableOriginalConstructor() - ->getMock(); - $this->webhookMessageReader = $this->getMockBuilder(WebhookMessageReader::class) - ->disableOriginalConstructor() - ->getMock(); - $this->webhookRequestValidator = $this->getMockBuilder(WebhookRequestValidator::class) - ->disableOriginalConstructor() - ->getMock(); - $this->caseUpdatingServiceFactory = $this->getMockBuilder(UpdatingServiceFactory::class) - ->disableOriginalConstructor() - ->getMock(); - $this->logger = $this->getMockBuilder(LoggerInterface::class) - ->getMockForAbstractClass(); - - $this->response = $this->getMockBuilder(ResponseHttp::class) - ->disableOriginalConstructor() - ->getMock(); - $this->context->expects($this->once()) - ->method('getResponse') - ->willReturn($this->response); - $this->redirect = $this->getMockBuilder(RedirectInterface::class) - ->getMockForAbstractClass(); - $this->context->expects($this->once()) - ->method('getRedirect') - ->willReturn($this->redirect); - $this->caseRepository = $this->getMockBuilder(CaseRepositoryInterface::class) - ->disableOriginalConstructor() - ->setMethods(['getByCaseId']) - ->getMockForAbstractClass(); - - $config = $this->getMockBuilder(Config::class) - ->disableOriginalConstructor() - ->setMethods(['isDebugModeEnabled', 'getByCaseId']) - ->getMock(); - $config->expects(self::any()) - ->method('getByCaseId') - ->willReturn(false); - - $this->controller = new Handler( - $this->context, - $this->webhookRequest, - $this->logger, - $this->webhookMessageReader, - $this->caseUpdatingServiceFactory, - $this->webhookRequestValidator, - $this->caseRepository, - $config - ); - } - - /** - * Successful case - */ - public function testExecuteSuccessfully() - { - $eventTopic = 'cases/creation'; - $caseId = 1; - $data = ['score' => 200, 'caseId' => $caseId]; - - $this->webhookRequestValidator->expects($this->once()) - ->method('validate') - ->willReturn(true); - - $webhookMessage = $this->getMockBuilder(WebhookMessage::class) - ->disableOriginalConstructor() - ->getMock(); - $webhookMessage->expects($this->exactly(2)) - ->method('getEventTopic') - ->willReturn($eventTopic); - $webhookMessage->expects($this->once()) - ->method('getData') - ->willReturn($data); - $this->webhookMessageReader->expects($this->once()) - ->method('read') - ->with($this->webhookRequest) - ->willReturn($webhookMessage); - - $caseEntity = $this->getMockBuilder(CaseInterface::class) - ->disableOriginalConstructor() - ->getMock(); - $this->caseRepository->expects(self::once()) - ->method('getByCaseId') - ->with(self::equalTo($caseId)) - ->willReturn($caseEntity); - - $caseUpdatingService = $this->getMockBuilder(UpdatingService::class) - ->disableOriginalConstructor() - ->getMock(); - $caseUpdatingService->expects($this->once()) - ->method('update') - ->with($caseEntity, $data); - - $this->caseUpdatingServiceFactory->expects($this->once()) - ->method('create') - ->with($eventTopic) - ->willReturn($caseUpdatingService); - - $this->controller->execute(); - } - - /** - * Case when there is exception while updating case - */ - public function testExecuteCaseUpdatingServiceException() - { - $eventTopic = 'cases/creation'; - $caseId = 1; - $data = ['score' => 200, 'caseId' => $caseId]; - - $this->webhookRequestValidator->expects($this->once()) - ->method('validate') - ->willReturn(true); - - $webhookMessage = $this->getMockBuilder(WebhookMessage::class) - ->disableOriginalConstructor() - ->getMock(); - $webhookMessage->expects($this->exactly(2)) - ->method('getEventTopic') - ->willReturn($eventTopic); - $webhookMessage->expects($this->once()) - ->method('getData') - ->willReturn($data); - $this->webhookMessageReader->expects($this->once()) - ->method('read') - ->with($this->webhookRequest) - ->willReturn($webhookMessage); - - $caseEntity = $this->getMockBuilder(CaseInterface::class) - ->disableOriginalConstructor() - ->getMock(); - $this->caseRepository->expects(self::once()) - ->method('getByCaseId') - ->with(self::equalTo($caseId)) - ->willReturn($caseEntity); - - $caseUpdatingService = $this->getMockBuilder(UpdatingService::class) - ->disableOriginalConstructor() - ->getMock(); - $caseUpdatingService->expects($this->once()) - ->method('update') - ->with($caseEntity, $data) - ->willThrowException(new LocalizedException(__('Error'))); - - $this->caseUpdatingServiceFactory->expects($this->once()) - ->method('create') - ->with($eventTopic) - ->willReturn($caseUpdatingService); - - $this->response->expects($this->once()) - ->method('setHttpResponseCode') - ->with(400); - $this->logger->expects($this->once()) - ->method('critical'); - - $this->controller->execute(); - } - - /** - * Case when webhook request validation fails - */ - public function testExecuteRequestValidationFails() - { - $this->webhookRequestValidator->expects($this->once()) - ->method('validate') - ->willReturn(false); - $this->redirect->expects($this->once()) - ->method('redirect') - ->with($this->response, 'noroute', []); - $this->webhookMessageReader->expects($this->never()) - ->method('read'); - $this->caseUpdatingServiceFactory->expects($this->never()) - ->method('create'); - - $this->controller->execute(); - } - - /** - * Case when webhook request has test event topic. - */ - public function testExecuteWithTestEventTopic() - { - $this->webhookRequestValidator->expects($this->once()) - ->method('validate') - ->willReturn(true); - $this->redirect->expects($this->never()) - ->method('redirect'); - - $webhookMessage = $this->getMockBuilder(WebhookMessage::class) - ->disableOriginalConstructor() - ->getMock(); - $webhookMessage->expects($this->once()) - ->method('getEventTopic') - ->willReturn('cases/test'); - $webhookMessage->expects($this->never()) - ->method('getData'); - - $this->webhookMessageReader->expects($this->once()) - ->method('read') - ->with($this->webhookRequest) - ->willReturn($webhookMessage); - - $this->caseUpdatingServiceFactory->expects($this->never()) - ->method('create'); - - $this->controller->execute(); - } - - /** - * Checks a test case when received input data does not contain Signifyd case id. - * - * @covers \Magento\Signifyd\Controller\Webhooks\Handler::execute - */ - public function testExecuteWithMissedCaseId() - { - $this->webhookRequestValidator->expects(self::once()) - ->method('validate') - ->willReturn(true); - - $webhookMessage = $this->getMockBuilder(WebhookMessage::class) - ->disableOriginalConstructor() - ->getMock(); - $webhookMessage->expects($this->once()) - ->method('getEventTopic') - ->willReturn('cases/creation'); - $webhookMessage->expects(self::once()) - ->method('getData') - ->willReturn([ - 'orderId' => '1000101' - ]); - - $this->webhookMessageReader->expects(self::once()) - ->method('read') - ->with($this->webhookRequest) - ->willReturn($webhookMessage); - - $this->redirect->expects(self::once()) - ->method('redirect') - ->with($this->response, 'noroute', []); - - $this->controller->execute(); - } - - /** - * Checks a case when Signifyd case entity not found. - * - * @covers \Magento\Signifyd\Controller\Webhooks\Handler::execute - */ - public function testExecuteWithNotFoundCaseEntity() - { - $caseId = 123; - - $this->webhookRequestValidator->expects(self::once()) - ->method('validate') - ->willReturn(true); - - $webhookMessage = $this->getMockBuilder(WebhookMessage::class) - ->disableOriginalConstructor() - ->setMethods(['getData']) - ->getMock(); - $webhookMessage->expects(self::once()) - ->method('getData') - ->willReturn([ - 'orderId' => '1000101', - 'caseId' => $caseId - ]); - - $this->webhookMessageReader->expects(self::once()) - ->method('read') - ->with($this->webhookRequest) - ->willReturn($webhookMessage); - - $this->caseRepository->expects(self::once()) - ->method('getByCaseId') - ->with(self::equalTo($caseId)) - ->willReturn(null); - - $this->redirect->expects(self::once()) - ->method('redirect') - ->with($this->response, 'noroute', []); - - $this->controller->execute(); - } -} diff --git a/app/code/Magento/Signifyd/Test/Unit/Model/CaseServices/UpdatingServiceFactoryTest.php b/app/code/Magento/Signifyd/Test/Unit/Model/CaseServices/UpdatingServiceFactoryTest.php deleted file mode 100644 index f0184c032b550..0000000000000 --- a/app/code/Magento/Signifyd/Test/Unit/Model/CaseServices/UpdatingServiceFactoryTest.php +++ /dev/null @@ -1,167 +0,0 @@ -<?php -/** - * Copyright © Magento, Inc. All rights reserved. - * See COPYING.txt for license details. - */ -namespace Magento\Signifyd\Test\Unit\Model\CaseServices; - -use Magento\Framework\ObjectManagerInterface; -use Magento\Framework\TestFramework\Unit\Helper\ObjectManager; -use Magento\Signifyd\Model\CaseServices\StubUpdatingService; -use Magento\Signifyd\Model\CaseServices\UpdatingService; -use Magento\Signifyd\Model\CaseServices\UpdatingServiceFactory; -use Magento\Signifyd\Model\Config; -use Magento\Signifyd\Model\MessageGenerators\GeneratorFactory; -use Magento\Signifyd\Model\MessageGenerators\GeneratorInterface; -use PHPUnit_Framework_MockObject_MockObject as MockObject; - -/** - * Contains tests for case updating service factory. - */ -class UpdatingServiceFactoryTest extends \PHPUnit\Framework\TestCase -{ - /** - * @var UpdatingServiceFactory - */ - private $factory; - - /** - * @var Config|MockObject - */ - private $config; - - /** - * @var ObjectManagerInterface|MockObject - */ - private $fakeObjectManager; - - /** - * @var GeneratorFactory|MockObject - */ - private $generatorFactory; - - /** - * @inheritdoc - */ - protected function setUp() - { - $this->config = $this->getMockBuilder(Config::class) - ->disableOriginalConstructor() - ->setMethods(['isActive']) - ->getMock(); - - $this->fakeObjectManager = $this->getMockBuilder(ObjectManagerInterface::class) - ->disableOriginalConstructor() - ->setMethods(['create']) - ->getMockForAbstractClass(); - - $this->generatorFactory = $this->getMockBuilder(GeneratorFactory::class) - ->disableOriginalConstructor() - ->setMethods(['create']) - ->getMock(); - - $objectManager = new ObjectManager($this); - $this->factory = $objectManager->getObject(UpdatingServiceFactory::class, [ - 'objectManager' => $this->fakeObjectManager, - 'generatorFactory' => $this->generatorFactory, - 'config' => $this->config - ]); - } - - /** - * Checks type of instance for updating service if Signifyd is not enabled. - * - * @covers \Magento\Signifyd\Model\CaseServices\UpdatingServiceFactory::create - */ - public function testCreateWithInactiveConfig() - { - $type = 'cases/creation'; - $this->config->expects(self::once()) - ->method('isActive') - ->willReturn(false); - - $this->fakeObjectManager->expects(self::once()) - ->method('create') - ->with(StubUpdatingService::class) - ->willReturn(new StubUpdatingService()); - - $instance = $this->factory->create($type); - static::assertInstanceOf(StubUpdatingService::class, $instance); - } - - /** - * Checks type of instance for updating service if test type is received. - * - * @covers \Magento\Signifyd\Model\CaseServices\UpdatingServiceFactory::create - */ - public function testCreateWithTestType() - { - $type = 'cases/test'; - $this->config->expects(self::once()) - ->method('isActive') - ->willReturn(true); - - $this->fakeObjectManager->expects(self::once()) - ->method('create') - ->with(StubUpdatingService::class) - ->willReturn(new StubUpdatingService()); - - $instance = $this->factory->create($type); - static::assertInstanceOf(StubUpdatingService::class, $instance); - } - - /** - * Checks exception type and message for unknown case type. - * - * @covers \Magento\Signifyd\Model\CaseServices\UpdatingServiceFactory::create - * @expectedException \InvalidArgumentException - * @expectedExceptionMessage Specified message type does not supported. - */ - public function testCreateWithException() - { - $type = 'cases/unknown'; - $this->config->expects(self::once()) - ->method('isActive') - ->willReturn(true); - - $this->generatorFactory->expects(self::once()) - ->method('create') - ->with($type) - ->willThrowException(new \InvalidArgumentException('Specified message type does not supported.')); - - $this->factory->create($type); - } - - /** - * Checks if factory creates correct instance of case updating service. - * - * @covers \Magento\Signifyd\Model\CaseServices\UpdatingServiceFactory::create - */ - public function testCreate() - { - $type = 'case/creation'; - $this->config->expects(self::once()) - ->method('isActive') - ->willReturn(true); - - $messageGenerator = $this->getMockBuilder(GeneratorInterface::class) - ->disableOriginalConstructor() - ->getMock(); - $this->generatorFactory->expects(self::once()) - ->method('create') - ->with($type) - ->willReturn($messageGenerator); - - $service = $this->getMockBuilder(UpdatingService::class) - ->disableOriginalConstructor() - ->getMock(); - - $this->fakeObjectManager->expects(self::once()) - ->method('create') - ->with(UpdatingService::class, ['messageGenerator' => $messageGenerator]) - ->willReturn($service); - - $result = $this->factory->create($type); - static::assertInstanceOf(UpdatingService::class, $result); - } -} diff --git a/app/code/Magento/Signifyd/Test/Unit/Model/CaseServices/UpdatingServiceTest.php b/app/code/Magento/Signifyd/Test/Unit/Model/CaseServices/UpdatingServiceTest.php deleted file mode 100644 index 6eb7e5c37d5fc..0000000000000 --- a/app/code/Magento/Signifyd/Test/Unit/Model/CaseServices/UpdatingServiceTest.php +++ /dev/null @@ -1,316 +0,0 @@ -<?php -/** - * Copyright © Magento, Inc. All rights reserved. - * See COPYING.txt for license details. - */ -namespace Magento\Signifyd\Test\Unit\Model\CaseServices; - -use Magento\Framework\TestFramework\Unit\Helper\ObjectManager; -use Magento\Signifyd\Api\CaseRepositoryInterface; -use Magento\Signifyd\Api\Data\CaseInterface; -use Magento\Signifyd\Model\CaseServices\UpdatingService; -use Magento\Signifyd\Model\CommentsHistoryUpdater; -use Magento\Signifyd\Model\MessageGenerators\GeneratorException; -use Magento\Signifyd\Model\MessageGenerators\GeneratorInterface; -use Magento\Signifyd\Model\OrderStateService; -use Magento\Signifyd\Model\SalesOrderGrid\OrderGridUpdater; -use PHPUnit_Framework_MockObject_MockObject as MockObject; - -/** - * Contains tests with different negative and positive scenarios for case updating service. - */ -class UpdatingServiceTest extends \PHPUnit\Framework\TestCase -{ - /** - * @var UpdatingService - */ - private $service; - - /** - * @var ObjectManager - */ - private $objectManager; - - /** - * @var GeneratorInterface|MockObject - */ - private $messageGenerator; - - /** - * @var CaseRepositoryInterface|MockObject - */ - private $caseRepository; - - /** - * @var CommentsHistoryUpdater|MockObject - */ - private $commentsHistoryUpdater; - - /** - * @var OrderGridUpdater|MockObject - */ - private $orderGridUpdater; - - /** - * @var OrderStateService|MockObject - */ - private $orderStateService; - - /** - * @inheritdoc - */ - protected function setUp() - { - $this->objectManager = new ObjectManager($this); - - $this->messageGenerator = $this->getMockBuilder(GeneratorInterface::class) - ->disableOriginalConstructor() - ->setMethods(['generate']) - ->getMock(); - - $this->caseRepository = $this->getMockBuilder(CaseRepositoryInterface::class) - ->disableOriginalConstructor() - ->setMethods(['getByCaseId']) - ->getMockForAbstractClass(); - - $this->commentsHistoryUpdater = $this->getMockBuilder(CommentsHistoryUpdater::class) - ->disableOriginalConstructor() - ->setMethods(['addComment']) - ->getMock(); - - $this->orderGridUpdater = $this->getMockBuilder(OrderGridUpdater::class) - ->disableOriginalConstructor() - ->getMock(); - - $this->orderStateService = $this->getMockBuilder(OrderStateService::class) - ->disableOriginalConstructor() - ->getMock(); - - $this->service = $this->objectManager->getObject(UpdatingService::class, [ - 'messageGenerator' => $this->messageGenerator, - 'caseRepository' => $this->caseRepository, - 'commentsHistoryUpdater' => $this->commentsHistoryUpdater, - 'orderGridUpdater' => $this->orderGridUpdater, - 'orderStateService' => $this->orderStateService - ]); - } - - /** - * Checks a test case when Signifyd case is empty entity. - * - * @covers \Magento\Signifyd\Model\CaseServices\UpdatingService::update - * @expectedException \Magento\Framework\Exception\LocalizedException - * @expectedExceptionMessage The case entity should not be empty. - */ - public function testUpdateWithEmptyCaseEntity() - { - $data = []; - $caseEntity = $this->withCaseEntity(null, 123, $data); - - $this->service->update($caseEntity, $data); - } - - /** - * Checks a test case when Signifyd case id is not specified for a case entity. - * - * @covers \Magento\Signifyd\Model\CaseServices\UpdatingService::update - * @expectedException \Magento\Framework\Exception\LocalizedException - * @expectedExceptionMessage The case entity should not be empty. - */ - public function testUpdateWithEmptyCaseId() - { - $data = [ - 'caseId' => 123 - ]; - $caseEntity = $this->withCaseEntity(1, null, $data); - - $this->service->update($caseEntity, $data); - } - - /** - * Checks as test case when service cannot save Signifyd case entity - * - * @covers \Magento\Signifyd\Model\CaseServices\UpdatingService::update - * @expectedException \Magento\Framework\Exception\LocalizedException - * @expectedExceptionMessage Cannot update Case entity. - */ - public function testUpdateWithFailedCaseSaving() - { - $caseId = 123; - $data = [ - 'caseId' => $caseId, - 'status' => CaseInterface::STATUS_OPEN, - 'orderId' => '10000012', - 'score' => 500 - ]; - - $caseEntity = $this->withCaseEntity(1, $caseId, $data); - - $this->caseRepository->expects(self::once()) - ->method('save') - ->willThrowException(new \Exception('Something wrong.')); - - $this->service->update($caseEntity, $data); - } - - /** - * Checks as test case when message generator throws an exception - * - * @covers \Magento\Signifyd\Model\CaseServices\UpdatingService::update - * @expectedException \Magento\Framework\Exception\LocalizedException - * @expectedExceptionMessage Cannot update Case entity. - */ - public function testUpdateWithExceptionFromMessageGenerator() - { - $caseId = 123; - $data = [ - 'caseId' => $caseId - ]; - - $caseEntity = $this->withCaseEntity(1, $caseId, $data); - - $this->caseRepository->expects(self::never()) - ->method('save') - ->with($caseEntity) - ->willReturn($caseEntity); - - $this->messageGenerator->expects(self::once()) - ->method('generate') - ->with($data) - ->willThrowException(new GeneratorException(__('Cannot generate message.'))); - - $this->service->update($caseEntity, $data); - } - - /** - * Checks a test case when comments history updater throws an exception. - * - * @covers \Magento\Signifyd\Model\CaseServices\UpdatingService::update - * @expectedException \Magento\Framework\Exception\LocalizedException - * @expectedExceptionMessage Cannot update Case entity. - */ - public function testUpdateWithFailedCommentSaving() - { - $caseId = 123; - $data = [ - 'caseId' => $caseId, - 'orderId' => 1 - ]; - - $caseEntity = $this->withCaseEntity(1, $caseId, $data); - - $this->caseRepository->expects(self::once()) - ->method('save') - ->with($caseEntity) - ->willReturn($caseEntity); - - $this->orderGridUpdater->expects(self::once()) - ->method('update') - ->with($data['orderId']); - - $message = __('Message is generated.'); - $this->messageGenerator->expects(self::once()) - ->method('generate') - ->with($data) - ->willReturn($message); - - $this->commentsHistoryUpdater->expects(self::once()) - ->method('addComment') - ->with($caseEntity, $message) - ->willThrowException(new \Exception('Something wrong')); - - $this->service->update($caseEntity, $data); - } - - /** - * Checks a test case when Signifyd case entity is successfully updated and message stored in comments history. - * - * @covers \Magento\Signifyd\Model\CaseServices\UpdatingService::update - */ - public function testUpdate() - { - $caseId = 123; - $data = [ - 'caseId' => $caseId, - 'orderId' => 1 - ]; - - $caseEntity = $this->withCaseEntity(21, $caseId, $data); - - $caseEntitySaved = clone $caseEntity; - $caseEntitySaved->expects(self::once()) - ->method('getGuaranteeDisposition') - ->willReturn('APPROVED'); - - $this->caseRepository->expects(self::once()) - ->method('save') - ->with($caseEntity) - ->willReturn($caseEntitySaved); - - $message = __('Message is generated.'); - $this->messageGenerator->expects(self::once()) - ->method('generate') - ->with($data) - ->willReturn($message); - - $this->orderGridUpdater->expects(self::once()) - ->method('update') - ->with($data['orderId']); - - $this->commentsHistoryUpdater->expects(self::once()) - ->method('addComment') - ->with($caseEntitySaved, $message); - - $this->orderStateService->expects(self::once()) - ->method('updateByCase') - ->with($caseEntitySaved); - - $this->service->update($caseEntity, $data); - } - - /** - * Create mock for case entity with common scenarios. - * - * @param $caseEntityId - * @param $caseId - * @param array $data - * @return CaseInterface|MockObject - */ - private function withCaseEntity($caseEntityId, $caseId, array $data = []) - { - /** @var CaseInterface|MockObject $caseEntity */ - $caseEntity = $this->getMockBuilder(CaseInterface::class) - ->disableOriginalConstructor() - ->setMethods([ - 'getEntityId', 'getCaseId', 'getOrderId', - 'setCaseId', 'setStatus', 'setOrderId', 'setScore' - ]) - ->getMockForAbstractClass(); - - $caseEntity->expects(self::any()) - ->method('getEntityId') - ->willReturn($caseEntityId); - $caseEntity->expects(self::any()) - ->method('getCaseId') - ->willReturn($caseId); - - foreach ($data as $property => $value) { - $method = 'set' . ucfirst($property); - if ($property === 'orderId') { - $caseEntity->expects(self::never()) - ->method($method); - } - $caseEntity->expects(self::any()) - ->method($method) - ->with(self::equalTo($value)) - ->willReturnSelf(); - - $method = 'get' . ucfirst($property); - $caseEntity->expects(self::any()) - ->method($method) - ->willReturn($value); - } - - return $caseEntity; - } -} diff --git a/app/code/Magento/Signifyd/Test/Unit/Model/CommentsHistoryUpdaterTest.php b/app/code/Magento/Signifyd/Test/Unit/Model/CommentsHistoryUpdaterTest.php deleted file mode 100644 index 5cbb3d8d93cdd..0000000000000 --- a/app/code/Magento/Signifyd/Test/Unit/Model/CommentsHistoryUpdaterTest.php +++ /dev/null @@ -1,176 +0,0 @@ -<?php -/** - * Copyright © Magento, Inc. All rights reserved. - * See COPYING.txt for license details. - */ -namespace Magento\Signifyd\Test\Unit\Model; - -use Magento\Framework\TestFramework\Unit\Helper\ObjectManager; -use Magento\Sales\Api\Data\OrderStatusHistoryInterface; -use Magento\Sales\Model\Order\Status\HistoryFactory; -use Magento\Signifyd\Api\Data\CaseInterface; -use Magento\Signifyd\Model\CommentsHistoryUpdater; -use PHPUnit_Framework_MockObject_MockObject as MockObject; -use Magento\Sales\Api\OrderStatusHistoryRepositoryInterface; - -/** - * Contains tests for comments history updater class. - */ -class CommentsHistoryUpdaterTest extends \PHPUnit\Framework\TestCase -{ - /** - * @var int - */ - private static $orderId = 123; - - /** - * @var string - */ - private static $message = 'Case is created.'; - - /** - * @var string - */ - private static $status = 'On Hold'; - - /** - * @var CommentsHistoryUpdater - */ - private $updater; - - /** - * @var HistoryFactory|MockObject - */ - private $historyFactory; - - /** - * @var CaseInterface|MockObject - */ - private $caseEntity; - - /** - * @var OrderStatusHistoryInterface|MockObject - */ - private $historyEntity; - - /** - * @var OrderStatusHistoryRepositoryInterface|MockObject - */ - private $historyRepository; - - /** - * @inheritdoc - */ - protected function setUp() - { - $objectManager = new ObjectManager($this); - - $this->historyFactory = $this->getMockBuilder(HistoryFactory::class) - ->disableOriginalConstructor() - ->setMethods(['create', 'save']) - ->getMock(); - - $this->historyRepository = $this->getMockBuilder(OrderStatusHistoryRepositoryInterface::class) - ->getMockForAbstractClass(); - - $this->caseEntity = $this->getMockBuilder(CaseInterface::class) - ->disableOriginalConstructor() - ->setMethods(['getOrderId']) - ->getMockForAbstractClass(); - - $this->initCommentMock(); - - $this->updater = $objectManager->getObject(CommentsHistoryUpdater::class, [ - 'historyFactory' => $this->historyFactory, - 'historyRepository' => $this->historyRepository - ]); - } - - /** - * Checks a test case when updater throws an exception while saving history comment. - * - * @covers \Magento\Signifyd\Model\CommentsHistoryUpdater::addComment - * @expectedException \Exception - */ - public function testAddCommentWithException() - { - $this->caseEntity->expects(self::once()) - ->method('getOrderId') - ->willReturn(self::$orderId); - - $this->historyEntity->method('setStatus') - ->with('') - ->willReturnSelf(); - $this->historyRepository->expects(self::once()) - ->method('save') - ->with($this->historyEntity) - ->willThrowException(new \Exception('Cannot save comment message.')); - - $this->updater->addComment($this->caseEntity, __(self::$message)); - } - - /** - * Checks a test case when updater successfully saves history comment. - * - * @covers \Magento\Signifyd\Model\CommentsHistoryUpdater::addComment - */ - public function testAddComment() - { - $this->caseEntity->expects(self::once()) - ->method('getOrderId') - ->willReturn(self::$orderId); - - $this->historyEntity->method('setStatus') - ->with(self::$status) - ->willReturnSelf(); - $this->historyRepository->expects(self::once()) - ->method('save') - ->with($this->historyEntity) - ->willReturnSelf(); - - $this->updater->addComment($this->caseEntity, __(self::$message), self::$status); - } - - /** - * Checks a test when message does not specified. - * - * @covers \Magento\Signifyd\Model\CommentsHistoryUpdater::addComment - */ - public function testAddCommentWithoutMessage() - { - $this->caseEntity->expects(self::never()) - ->method('getOrderId'); - - $this->historyFactory->expects(self::never()) - ->method('save'); - - $phrase = ''; - $this->updater->addComment($this->caseEntity, __($phrase)); - } - - /** - * Creates mock object for history entity. - * - * @return void - */ - private function initCommentMock() - { - $this->historyEntity = $this->getMockBuilder(OrderStatusHistoryInterface::class) - ->disableOriginalConstructor() - ->setMethods(['setParentId', 'setComment', 'setEntityName', 'save']) - ->getMockForAbstractClass(); - - $this->historyFactory->method('create') - ->willReturn($this->historyEntity); - - $this->historyEntity->method('setParentId') - ->with(self::$orderId) - ->willReturnSelf(); - $this->historyEntity->method('setComment') - ->with(self::$message) - ->willReturnSelf(); - $this->historyEntity->method('setEntityName') - ->with('order') - ->willReturnSelf(); - } -} diff --git a/app/code/Magento/Signifyd/Test/Unit/Model/CustomerOrdersTest.php b/app/code/Magento/Signifyd/Test/Unit/Model/CustomerOrdersTest.php deleted file mode 100644 index 02d3b4b9ad7a7..0000000000000 --- a/app/code/Magento/Signifyd/Test/Unit/Model/CustomerOrdersTest.php +++ /dev/null @@ -1,267 +0,0 @@ -<?php -/** - * Copyright © Magento, Inc. All rights reserved. - * See COPYING.txt for license details. - */ -namespace Magento\Signifyd\Test\Unit\Model; - -use Magento\Directory\Model\Currency; -use Magento\Framework\Api\FilterBuilder; -use Magento\Framework\Api\SearchCriteriaBuilder; -use Magento\Framework\TestFramework\Unit\Helper\ObjectManager; -use Magento\Sales\Api\Data\OrderSearchResultInterface; -use Magento\Sales\Model\Order; -use Magento\Signifyd\Model\CustomerOrders; -use PHPUnit_Framework_MockObject_MockObject as MockObject; -use Magento\Sales\Api\OrderRepositoryInterface; -use Psr\Log\LoggerInterface; - -/** - * @SuppressWarnings(PHPMD.CouplingBetweenObjects) - */ -class CustomerOrdersTest extends \PHPUnit\Framework\TestCase -{ - /** - * @var int - */ - private static $customerId = 1; - - /** - * Order amount in EUR - * @var int - */ - private static $eurAmount = 100; - - /** - * Order amount in UAH - * @var int - */ - private static $uahAmount = 270; - - /** - * Order amount in USD - * @var int - */ - private static $usdAmount = 50; - - /** - * @var ObjectManager - */ - private $objectManager; - - /** - * @var FilterBuilder|MockObject - */ - private $filterBuilder; - - /** - * @var Currency|MockObject - */ - private $eurCurrency; - - /** - * @var Currency|MockObject - */ - private $uahCurrency; - - /** - * @var CustomerOrders - */ - private $model; - - /** - * @var OrderRepositoryInterface|MockObject - */ - private $orderRepository; - - /** - * @var SearchCriteriaBuilder|MockObject - */ - private $searchCriteriaBuilder; - - /** - * @var LoggerInterface|MockObject - */ - private $logger; - - /** - * @inheritdoc - */ - protected function setUp() - { - $this->objectManager = new ObjectManager($this); - - $this->orderRepository = $this->getMockBuilder(OrderRepositoryInterface::class) - ->getMockForAbstractClass(); - - $this->logger = $this->getMockBuilder(LoggerInterface::class) - ->getMockForAbstractClass(); - - $this->filterBuilder = $this->getMockBuilder(FilterBuilder::class) - ->disableOriginalConstructor() - ->getMock(); - - $this->searchCriteriaBuilder = $this->getMockBuilder(SearchCriteriaBuilder::class) - ->disableOriginalConstructor() - ->getMock(); - - $this->model = $this->objectManager->getObject(CustomerOrders::class, [ - 'filterBuilder' => $this->filterBuilder, - 'orderRepository' => $this->orderRepository, - 'searchCriteriaBuilder' => $this->searchCriteriaBuilder, - 'logger' => $this->logger - ]); - - $this->initCurrencies(); - $this->initOrderRepository(); - - $this->objectManager->setBackwardCompatibleProperty( - $this->model, - 'currencies', - ['EUR' => $this->eurCurrency, 'UAH' => $this->uahCurrency] - ); - } - - /** - * @covers \Magento\Signifyd\Model\CustomerOrders::getAggregatedOrdersInfo() - */ - public function testGetCountAndTotalAmount() - { - $this->eurCurrency->expects($this->once()) - ->method('convert') - ->with(self::$eurAmount, 'USD') - ->willReturn(109); - - $this->uahCurrency->expects($this->once()) - ->method('convert') - ->with(self::$uahAmount, 'USD') - ->willReturn(10.35); - - $actual = $this->model->getAggregatedOrdersInfo(self::$customerId); - - static::assertEquals(3, $actual['aggregateOrderCount']); - static::assertEquals(169.35, $actual['aggregateOrderDollars']); - } - - /** - * Test case when required currency rate is absent and exception is thrown - * @covers \Magento\Signifyd\Model\CustomerOrders::getAggregatedOrdersInfo() - */ - public function testGetCountAndTotalAmountNegative() - { - $this->eurCurrency->expects($this->once()) - ->method('convert') - ->with(self::$eurAmount, 'USD') - ->willReturn(109); - - $this->uahCurrency->expects($this->once()) - ->method('convert') - ->with(self::$uahAmount, 'USD') - ->willThrowException(new \Exception()); - - $this->logger->expects($this->once()) - ->method('error'); - - $actual = $this->model->getAggregatedOrdersInfo(self::$customerId); - - $this->assertNull($actual['aggregateOrderCount']); - $this->assertNull($actual['aggregateOrderDollars']); - } - - /** - * Populate order repository with mocked orders - */ - private function initOrderRepository() - { - $this->filterBuilder->expects($this->once()) - ->method('setField') - ->willReturnSelf(); - $this->filterBuilder->expects($this->once()) - ->method('setValue') - ->willReturnSelf(); - $filter = $this->getMockBuilder(\Magento\Framework\Api\Filter::class) - ->disableOriginalConstructor() - ->getMock(); - $this->filterBuilder->expects($this->once()) - ->method('create') - ->willReturn($filter); - - $searchCriteria = $this->getMockBuilder(\Magento\Framework\Api\SearchCriteria::class) - ->disableOriginalConstructor() - ->getMock(); - $this->searchCriteriaBuilder->expects($this->once()) - ->method('create') - ->willReturn($searchCriteria); - - $orderSearchResult = $this->getMockBuilder(OrderSearchResultInterface::class) - ->getMockForAbstractClass(); - $orderSearchResult->expects($this->once()) - ->method('getItems') - ->willReturn($this->getOrders()); - $this->orderRepository->expects($this->once()) - ->method('getList') - ->willReturn($orderSearchResult); - } - - /** - * Creates mocks for currencies - * @return void - */ - private function initCurrencies() - { - $this->eurCurrency = $this->getMockBuilder(Currency::class) - ->disableOriginalConstructor() - ->setMethods(['convert']) - ->getMock(); - - $this->uahCurrency = $this->getMockBuilder(Currency::class) - ->disableOriginalConstructor() - ->setMethods(['convert']) - ->getMock(); - } - - /** - * Get list of mocked orders with different currencies - * @return array - */ - private function getOrders() - { - $eurOrder = $this->getMockBuilder(Order::class) - ->disableOriginalConstructor() - ->setMethods(['getBaseGrandTotal', 'getBaseCurrencyCode']) - ->getMock(); - - $eurOrder->expects($this->once()) - ->method('getBaseGrandTotal') - ->willReturn(self::$eurAmount); - $eurOrder->expects($this->once()) - ->method('getBaseCurrencyCode') - ->willReturn('EUR'); - - $uahOrder = $this->getMockBuilder(Order::class) - ->disableOriginalConstructor() - ->setMethods(['getBaseGrandTotal', 'getBaseCurrencyCode']) - ->getMock(); - - $uahOrder->expects($this->once()) - ->method('getBaseGrandTotal') - ->willReturn(self::$uahAmount); - $uahOrder->expects($this->once()) - ->method('getBaseCurrencyCode') - ->willReturn('UAH'); - - $usdOrder = $this->getMockBuilder(Order::class) - ->disableOriginalConstructor() - ->setMethods(['getBaseGrandTotal', 'getBaseCurrencyCode']) - ->getMock(); - - $usdOrder->expects($this->once()) - ->method('getBaseGrandTotal') - ->willReturn(self::$usdAmount); - $usdOrder->expects($this->once()) - ->method('getBaseCurrencyCode') - ->willReturn('USD'); - - return [$usdOrder, $eurOrder, $uahOrder]; - } -} diff --git a/app/code/Magento/Signifyd/Test/Unit/Model/Guarantee/CancelGuaranteeAbilityTest.php b/app/code/Magento/Signifyd/Test/Unit/Model/Guarantee/CancelGuaranteeAbilityTest.php deleted file mode 100644 index f7b4e473a0ec8..0000000000000 --- a/app/code/Magento/Signifyd/Test/Unit/Model/Guarantee/CancelGuaranteeAbilityTest.php +++ /dev/null @@ -1,154 +0,0 @@ -<?php -/** - * Copyright © Magento, Inc. All rights reserved. - * See COPYING.txt for license details. - */ -namespace Magento\Signifyd\Test\Unit\Model\Guarantee; - -use Magento\Framework\Exception\NoSuchEntityException; -use Magento\Sales\Api\Data\OrderInterface; -use Magento\Sales\Api\OrderRepositoryInterface; -use Magento\Signifyd\Api\Data\CaseInterface; -use Magento\Signifyd\Model\CaseEntity; -use Magento\Signifyd\Model\CaseManagement; -use Magento\Signifyd\Model\Guarantee\CancelGuaranteeAbility; - -/** - * @SuppressWarnings(PHPMD.CouplingBetweenObjects) - */ -class CancelGuaranteeAbilityTest extends \PHPUnit\Framework\TestCase -{ - /** - * @var OrderRepositoryInterface|\PHPUnit_Framework_MockObject_MockObject - */ - private $orderRepository; - - /** - * @var CaseManagement|\PHPUnit_Framework_MockObject_MockObject - */ - private $caseManagement; - - /** - * @var CancelGuaranteeAbility - */ - private $cancelGuaranteeAbility; - - /** - * @inheritdoc - */ - protected function setUp() - { - $this->orderRepository = $this->getMockBuilder(OrderRepositoryInterface::class) - ->getMockForAbstractClass(); - - $this->caseManagement = $this->getMockBuilder(CaseManagement::class) - ->disableOriginalConstructor() - ->getMock(); - - $this->cancelGuaranteeAbility = new CancelGuaranteeAbility( - $this->caseManagement, - $this->orderRepository - ); - } - - /** - * Success test for Cancel Guarantee Request button - */ - public function testIsAvailableSuccess() - { - $orderId = 123; - - /** @var CaseInterface|\PHPUnit_Framework_MockObject_MockObject $case */ - $case = $this->getMockBuilder(CaseInterface::class) - ->disableOriginalConstructor() - ->getMock(); - - $case->expects($this->once()) - ->method('getGuaranteeDisposition') - ->willReturn(CaseEntity::GUARANTEE_APPROVED); - - $this->caseManagement->expects($this->once()) - ->method('getByOrderId') - ->with($orderId) - ->willReturn($case); - - /** @var OrderInterface|\PHPUnit_Framework_MockObject_MockObject $order */ - $order = $this->getMockBuilder(OrderInterface::class) - ->getMockForAbstractClass(); - - $this->orderRepository->expects($this->once()) - ->method('get') - ->with($orderId) - ->willReturn($order); - - $this->assertTrue($this->cancelGuaranteeAbility->isAvailable($orderId)); - } - - /** - * Tests case when Case entity doesn't exist for order - */ - public function testIsAvailableWithNullCase() - { - $orderId = 123; - - $this->caseManagement->expects($this->once()) - ->method('getByOrderId') - ->with($orderId) - ->willReturn(null); - - $this->assertFalse($this->cancelGuaranteeAbility->isAvailable($orderId)); - } - - /** - * Tests case when Guarantee Disposition has Canceled states. - */ - public function testIsAvailableWithCanceledGuarantee() - { - $orderId = 123; - - /** @var CaseInterface|\PHPUnit_Framework_MockObject_MockObject $case */ - $case = $this->getMockBuilder(CaseInterface::class) - ->disableOriginalConstructor() - ->getMock(); - - $case->expects($this->once()) - ->method('getGuaranteeDisposition') - ->willReturn(CaseEntity::GUARANTEE_CANCELED); - - $this->caseManagement->expects($this->once()) - ->method('getByOrderId') - ->with($orderId) - ->willReturn($case); - - $this->assertFalse($this->cancelGuaranteeAbility->isAvailable($orderId)); - } - - /** - * Tests case when order does not exist. - */ - public function testIsAvailableWithNullOrder() - { - $orderId = 123; - - /** @var CaseInterface|\PHPUnit_Framework_MockObject_MockObject $case */ - $case = $this->getMockBuilder(CaseInterface::class) - ->disableOriginalConstructor() - ->getMock(); - - $case->expects($this->once()) - ->method('getGuaranteeDisposition') - ->willReturn(CaseEntity::GUARANTEE_APPROVED); - - $this->caseManagement->expects($this->once()) - ->method('getByOrderId') - ->with($orderId) - ->willReturn($case); - - $this->orderRepository->expects($this->once()) - ->method('get') - ->with($orderId) - ->willThrowException(new NoSuchEntityException()); - - $this->assertFalse($this->cancelGuaranteeAbility->isAvailable($orderId)); - } -} diff --git a/app/code/Magento/Signifyd/Test/Unit/Model/Guarantee/CancelingServiceTest.php b/app/code/Magento/Signifyd/Test/Unit/Model/Guarantee/CancelingServiceTest.php deleted file mode 100644 index f8f1d4a4522c9..0000000000000 --- a/app/code/Magento/Signifyd/Test/Unit/Model/Guarantee/CancelingServiceTest.php +++ /dev/null @@ -1,215 +0,0 @@ -<?php -/** - * Copyright © Magento, Inc. All rights reserved. - * See COPYING.txt for license details. - */ -namespace Magento\Signifyd\Test\Unit\Model\Guarantee; - -use Magento\Signifyd\Api\CaseManagementInterface; -use Magento\Signifyd\Api\Data\CaseInterface; -use Magento\Signifyd\Model\CaseServices\StubUpdatingService; -use Magento\Signifyd\Model\CaseServices\UpdatingServiceFactory; -use Magento\Signifyd\Model\Guarantee\CancelGuaranteeAbility; -use Magento\Signifyd\Model\Guarantee\CancelingService; -use Magento\Signifyd\Model\SignifydGateway\Gateway; -use Magento\Signifyd\Model\SignifydGateway\GatewayException; -use PHPUnit_Framework_MockObject_MockObject as MockObject; -use Psr\Log\LoggerInterface; - -/** - * Contains test cases for Signifyd guarantee canceling service. - */ -class CancelingServiceTest extends \PHPUnit\Framework\TestCase -{ - /** - * @var int - */ - private static $orderId = 23; - - /** - * @var int - */ - private static $caseId = 123; - - /** - * @var CancelingService - */ - private $service; - - /** - * @var CaseManagementInterface|MockObject - */ - private $caseManagement; - - /** - * @var UpdatingServiceFactory|MockObject - */ - private $updatingFactory; - - /** - * @var Gateway|MockObject - */ - private $gateway; - - /** - * @var CancelGuaranteeAbility|MockObject - */ - private $guaranteeAbility; - - /** - * @var LoggerInterface|MockObject - */ - private $logger; - - /** - * @inheritdoc - */ - protected function setUp() - { - $this->caseManagement = $this->getMockBuilder(CaseManagementInterface::class) - ->disableOriginalConstructor() - ->setMethods(['getByOrderId']) - ->getMockForAbstractClass(); - - $this->updatingFactory = $this->getMockBuilder(UpdatingServiceFactory::class) - ->disableOriginalConstructor() - ->setMethods(['create']) - ->getMock(); - - $this->gateway = $this->getMockBuilder(Gateway::class) - ->disableOriginalConstructor() - ->setMethods(['cancelGuarantee']) - ->getMock(); - - $this->guaranteeAbility = $this->getMockBuilder(CancelGuaranteeAbility::class) - ->disableOriginalConstructor() - ->setMethods(['isAvailable']) - ->getMock(); - - $this->logger = $this->getMockBuilder(LoggerInterface::class) - ->disableOriginalConstructor() - ->setMethods(['error']) - ->getMockForAbstractClass(); - - $this->service = new CancelingService( - $this->caseManagement, - $this->updatingFactory, - $this->gateway, - $this->guaranteeAbility, - $this->logger - ); - } - - /** - * Checks a test case, when validation for a guarantee is failed. - * - * @covers \Magento\Signifyd\Model\Guarantee\CancelingService::cancelForOrder - */ - public function testCancelForOrderWithUnavailableDisposition() - { - $this->guaranteeAbility->expects(self::once()) - ->method('isAvailable') - ->with(self::equalTo(self::$orderId)) - ->willReturn(false); - - $this->caseManagement->expects(self::never()) - ->method('getByOrderId'); - - $this->gateway->expects(self::never()) - ->method('cancelGuarantee'); - - $this->logger->expects(self::never()) - ->method('error'); - - $this->updatingFactory->expects(self::never()) - ->method('create'); - - $result = $this->service->cancelForOrder(self::$orderId); - self::assertFalse($result); - } - - /** - * Checks a test case, when request to Signifyd API fails. - * - * @covers \Magento\Signifyd\Model\Guarantee\CancelingService::cancelForOrder - */ - public function testCancelForOrderWithFailedRequest() - { - $this->withCaseEntity(); - - $this->gateway->expects(self::once()) - ->method('cancelGuarantee') - ->with(self::equalTo(self::$caseId)) - ->willThrowException(new GatewayException('Something wrong.')); - - $this->logger->expects(self::once()) - ->method('error') - ->with(self::equalTo('Something wrong.')); - - $this->updatingFactory->expects(self::never()) - ->method('create'); - - $result = $this->service->cancelForOrder(self::$orderId); - self::assertFalse($result); - } - - /** - * Checks a test case, when request to Signifyd successfully processed and case entity has been updated. - * - * @covers \Magento\Signifyd\Model\Guarantee\CancelingService::cancelForOrder - */ - public function testCancelForOrder() - { - $case = $this->withCaseEntity(); - - $this->gateway->expects(self::once()) - ->method('cancelGuarantee') - ->with(self::equalTo(self::$caseId)) - ->willReturn(CaseInterface::GUARANTEE_CANCELED); - - $this->logger->expects(self::never()) - ->method('error'); - - $service = $this->getMockBuilder(StubUpdatingService::class) - ->setMethods(['update']) - ->getMock(); - $this->updatingFactory->expects(self::once()) - ->method('create') - ->willReturn($service); - - $service->expects(self::once()) - ->method('update') - ->with(self::equalTo($case), self::equalTo(['guaranteeDisposition' => CaseInterface::GUARANTEE_CANCELED])); - - $result = $this->service->cancelForOrder(self::$orderId); - self::assertTrue($result); - } - - /** - * Gets mock for a case entity. - * - * @return CaseInterface|MockObject - */ - private function withCaseEntity() - { - $this->guaranteeAbility->expects(self::once()) - ->method('isAvailable') - ->with(self::equalTo(self::$orderId)) - ->willReturn(true); - - $caseEntity = $this->getMockBuilder(CaseInterface::class) - ->disableOriginalConstructor() - ->setMethods(['getCaseId']) - ->getMockForAbstractClass(); - - $this->caseManagement->expects(self::once()) - ->method('getByOrderId') - ->with(self::equalTo(self::$orderId)) - ->willReturn($caseEntity); - - $caseEntity->expects(self::once()) - ->method('getCaseId') - ->willReturn(self::$caseId); - return $caseEntity; - } -} diff --git a/app/code/Magento/Signifyd/Test/Unit/Model/Guarantee/CreateGuaranteeAbilityTest.php b/app/code/Magento/Signifyd/Test/Unit/Model/Guarantee/CreateGuaranteeAbilityTest.php deleted file mode 100644 index 6b7a6112a932e..0000000000000 --- a/app/code/Magento/Signifyd/Test/Unit/Model/Guarantee/CreateGuaranteeAbilityTest.php +++ /dev/null @@ -1,260 +0,0 @@ -<?php -/** - * Copyright © Magento, Inc. All rights reserved. - * See COPYING.txt for license details. - */ -namespace Magento\Signifyd\Test\Unit\Model\Guarantee; - -use Magento\Framework\Exception\NoSuchEntityException; -use Magento\Framework\Intl\DateTimeFactory; -use Magento\Sales\Api\Data\OrderInterface; -use Magento\Sales\Api\OrderRepositoryInterface; -use Magento\Sales\Model\Order; -use Magento\Signifyd\Api\Data\CaseInterface; -use Magento\Signifyd\Model\CaseManagement; -use Magento\Signifyd\Model\Guarantee\CreateGuaranteeAbility; - -/** - * @SuppressWarnings(PHPMD.CouplingBetweenObjects) - */ -class CreateGuaranteeAbilityTest extends \PHPUnit\Framework\TestCase -{ - /** - * @var DateTimeFactory - */ - private $dateTimeFactory; - - /** - * @var OrderRepositoryInterface|\PHPUnit_Framework_MockObject_MockObject - */ - private $orderRepository; - - /** - * @var CaseManagement|\PHPUnit_Framework_MockObject_MockObject - */ - private $caseManagement; - - /** - * @var CreateGuaranteeAbility - */ - private $createGuaranteeAbility; - - /** - * @inheritdoc - */ - protected function setUp() - { - $this->dateTimeFactory = new DateTimeFactory(); - $this->orderRepository = $this->getMockBuilder(OrderRepositoryInterface::class) - ->getMockForAbstractClass(); - $this->caseManagement = $this->getMockBuilder(CaseManagement::class) - ->disableOriginalConstructor() - ->getMock(); - - $this->createGuaranteeAbility = new CreateGuaranteeAbility( - $this->caseManagement, - $this->orderRepository, - $this->dateTimeFactory - ); - } - - public function testIsAvailableSuccess() - { - $orderId = 123; - $orderCreatedAt = $this->getDateAgo(6); - - /** @var CaseInterface|\PHPUnit_Framework_MockObject_MockObject $case */ - $case = $this->getMockBuilder(CaseInterface::class) - ->disableOriginalConstructor() - ->getMock(); - $case->expects($this->once()) - ->method('isGuaranteeEligible') - ->willReturn(true); - - $this->caseManagement->expects($this->once()) - ->method('getByOrderId') - ->with($orderId) - ->willReturn($case); - - /** @var OrderInterface|\PHPUnit_Framework_MockObject_MockObject $order */ - $order = $this->getMockBuilder(OrderInterface::class) - ->getMockForAbstractClass(); - $order->expects($this->once()) - ->method('getState') - ->willReturn(Order::STATE_COMPLETE); - $order->expects($this->once()) - ->method('getCreatedAt') - ->willReturn($orderCreatedAt); - - $this->orderRepository->expects($this->once()) - ->method('get') - ->with($orderId) - ->willReturn($order); - - $this->assertTrue($this->createGuaranteeAbility->isAvailable($orderId)); - } - - /** - * Tests case when Case entity doesn't exist for order - */ - public function testIsAvailableWithNullCase() - { - $orderId = 123; - - $this->caseManagement->expects($this->once()) - ->method('getByOrderId') - ->with($orderId) - ->willReturn(null); - - $this->assertFalse($this->createGuaranteeAbility->isAvailable($orderId)); - } - - /** - * Tests case when GuaranteeEligible for Case is false - */ - public function testIsAvailableWithGuarantyEligibleFalse() - { - $orderId = 123; - - /** @var CaseInterface|\PHPUnit_Framework_MockObject_MockObject $case */ - $case = $this->getMockBuilder(CaseInterface::class) - ->disableOriginalConstructor() - ->getMock(); - $case->expects($this->once()) - ->method('isGuaranteeEligible') - ->willReturn(false); - - $this->caseManagement->expects($this->once()) - ->method('getByOrderId') - ->with($orderId) - ->willReturn($case); - - $this->assertFalse($this->createGuaranteeAbility->isAvailable($orderId)); - } - - /** - * Tests case when GuaranteeEligible for Case is false - */ - public function testIsAvailableWithNullOrder() - { - $orderId = 123; - - /** @var CaseInterface|\PHPUnit_Framework_MockObject_MockObject $case */ - $case = $this->getMockBuilder(CaseInterface::class) - ->disableOriginalConstructor() - ->getMock(); - $case->expects($this->once()) - ->method('isGuaranteeEligible') - ->willReturn(true); - - $this->caseManagement->expects($this->once()) - ->method('getByOrderId') - ->with($orderId) - ->willReturn($case); - - $this->orderRepository->expects($this->once()) - ->method('get') - ->with($orderId) - ->willThrowException(new NoSuchEntityException()); - - $this->assertFalse($this->createGuaranteeAbility->isAvailable($orderId)); - } - - /** - * Tests case when order has Canceled Or Closed states. - * - * @param string $state - * @dataProvider isAvailableWithCanceledOrderDataProvider - */ - public function testIsAvailableWithCanceledOrder($state) - { - $orderId = 123; - - /** @var CaseInterface|\PHPUnit_Framework_MockObject_MockObject $case */ - $case = $this->getMockBuilder(CaseInterface::class) - ->disableOriginalConstructor() - ->getMock(); - $case->expects($this->once()) - ->method('isGuaranteeEligible') - ->willReturn(true); - - $this->caseManagement->expects($this->once()) - ->method('getByOrderId') - ->with($orderId) - ->willReturn($case); - - /** @var OrderInterface|\PHPUnit_Framework_MockObject_MockObject $order */ - $order = $this->getMockBuilder(OrderInterface::class) - ->getMockForAbstractClass(); - $order->expects($this->once()) - ->method('getState') - ->willReturn($state); - - $this->orderRepository->expects($this->once()) - ->method('get') - ->with($orderId) - ->willReturn($order); - - $this->assertFalse($this->createGuaranteeAbility->isAvailable($orderId)); - } - - /** - * @return array - */ - public function isAvailableWithCanceledOrderDataProvider() - { - return [ - [Order::STATE_CANCELED], [Order::STATE_CLOSED] - ]; - } - - public function testIsAvailableWithOldOrder() - { - $orderId = 123; - $orderCreatedAt = $this->getDateAgo(8); - - /** @var CaseInterface|\PHPUnit_Framework_MockObject_MockObject $case */ - $case = $this->getMockBuilder(CaseInterface::class) - ->disableOriginalConstructor() - ->getMock(); - $case->expects($this->once()) - ->method('isGuaranteeEligible') - ->willReturn(true); - - $this->caseManagement->expects($this->once()) - ->method('getByOrderId') - ->with($orderId) - ->willReturn($case); - - /** @var OrderInterface|\PHPUnit_Framework_MockObject_MockObject $order */ - $order = $this->getMockBuilder(OrderInterface::class) - ->getMockForAbstractClass(); - $order->expects($this->once()) - ->method('getState') - ->willReturn(Order::STATE_COMPLETE); - $order->expects($this->once()) - ->method('getCreatedAt') - ->willReturn($orderCreatedAt); - - $this->orderRepository->expects($this->once()) - ->method('get') - ->with($orderId) - ->willReturn($order); - - $this->assertFalse($this->createGuaranteeAbility->isAvailable($orderId)); - } - - /** - * Returns date N days ago - * - * @param int $days number of days that will be deducted from the current date - * @return string - */ - private function getDateAgo($days) - { - $createdAtTime = $this->dateTimeFactory->create('now', new \DateTimeZone('UTC')); - $createdAtTime->sub(new \DateInterval('P' . $days . 'D')); - - return $createdAtTime->format('Y-m-d h:i:s'); - } -} diff --git a/app/code/Magento/Signifyd/Test/Unit/Model/Guarantee/CreationServiceTest.php b/app/code/Magento/Signifyd/Test/Unit/Model/Guarantee/CreationServiceTest.php deleted file mode 100644 index db64b38375fe1..0000000000000 --- a/app/code/Magento/Signifyd/Test/Unit/Model/Guarantee/CreationServiceTest.php +++ /dev/null @@ -1,253 +0,0 @@ -<?php -/** - * Copyright © Magento, Inc. All rights reserved. - * See COPYING.txt for license details. - */ -namespace Magento\Signifyd\Test\Unit\Model\Guarantee; - -use Magento\Signifyd\Api\CaseManagementInterface; -use Magento\Signifyd\Api\Data\CaseInterface; -use Magento\Signifyd\Model\CaseServices\UpdatingServiceFactory; -use Magento\Signifyd\Model\CaseServices\UpdatingServiceInterface; -use Magento\Signifyd\Model\Guarantee\CreateGuaranteeAbility; -use Magento\Signifyd\Model\Guarantee\CreationService; -use Magento\Signifyd\Model\SignifydGateway\Gateway; -use Magento\Signifyd\Model\SignifydGateway\GatewayException; -use PHPUnit_Framework_MockObject_MockObject as MockObject; -use \PHPUnit\Framework\TestCase as TestCase; -use Psr\Log\LoggerInterface; - -class CreationServiceTest extends TestCase -{ - /** - * @var CreationService|MockObject - */ - private $service; - - /** - * @var CaseManagementInterface|MockObject - */ - private $caseManagement; - - /** - * @var UpdatingServiceInterface|MockObject - */ - private $caseUpdatingService; - - /** - * @var Gateway|MockObject - */ - private $gateway; - - /** - * @var LoggerInterface|MockObject - */ - private $logger; - - /** - * @var CreateGuaranteeAbility|MockObject - */ - private $createGuaranteeAbility; - - /** - * @inheritdoc - */ - protected function setUp() - { - $this->caseManagement = $this->getMockBuilder(CaseManagementInterface::class) - ->getMockForAbstractClass(); - - $caseUpdatingServiceFactory = $this->getMockBuilder(UpdatingServiceFactory::class) - ->disableOriginalConstructor() - ->getMock(); - $this->caseUpdatingService = $this->getMockBuilder(UpdatingServiceInterface::class) - ->getMockForAbstractClass(); - $caseUpdatingServiceFactory - ->method('create') - ->willReturn($this->caseUpdatingService); - - $this->gateway = $this->getMockBuilder(Gateway::class) - ->disableOriginalConstructor() - ->getMock(); - - $this->createGuaranteeAbility = $this->getMockBuilder(CreateGuaranteeAbility::class) - ->disableOriginalConstructor() - ->setMethods(['isAvailable']) - ->getMock(); - - $this->logger = $this->getMockBuilder(LoggerInterface::class) - ->getMockForAbstractClass(); - - $this->service = new CreationService( - $this->caseManagement, - $caseUpdatingServiceFactory, - $this->gateway, - $this->createGuaranteeAbility, - $this->logger - ); - } - - /** - * Checks a test case, when guarantee ability checker does not allow to submit case for a guarantee. - * - * @covers \Magento\Signifyd\Model\Guarantee\CreationService::createForOrder - */ - public function testCreateForOrderWithNotEligibleCase() - { - $orderId = 1; - - $this->createGuaranteeAbility->expects(self::once()) - ->method('isAvailable') - ->with($orderId) - ->willReturn(false); - - $this->caseManagement->expects(self::never()) - ->method('getByOrderId'); - - $this->gateway->expects(self::never()) - ->method('submitCaseForGuarantee'); - - $result = $this->service->createForOrder($orderId); - self::assertFalse($result); - } - - public function testCreateForOrderWitCase() - { - $dummyOrderId = 1; - $dummyCaseId = 42; - $this->withCaseEntityExistsForOrderId( - $dummyOrderId, - [ - 'caseId' => $dummyCaseId, - ] - ); - - $this->gateway - ->expects($this->once()) - ->method('submitCaseForGuarantee'); - - $this->service->createForOrder($dummyOrderId); - } - - public function testCreateForOrderWithGatewayFailure() - { - $dummyOrderId = 1; - $dummyCaseId = 42; - $dummyGatewayFailureMessage = 'Everything fails sometimes'; - $this->withCaseEntityExistsForOrderId( - $dummyOrderId, - [ - 'caseId' => $dummyCaseId, - ] - ); - $this->withGatewayFailure($dummyGatewayFailureMessage); - - $this->logger - ->expects($this->once()) - ->method('error') - ->with($this->equalTo($dummyGatewayFailureMessage)); - $this->caseUpdatingService - ->expects($this->never()) - ->method('update'); - - $result = $this->service->createForOrder($dummyOrderId); - $this->assertEquals( - false, - $result, - 'Service should return false in case of gateway failure' - ); - } - - public function testCreateForOrderWithGatewaySuccess() - { - $dummyOrderId = 1; - $dummyCaseId = 42; - $dummyGuaranteeDisposition = 'foo'; - $caseEntity = $this->withCaseEntityExistsForOrderId( - $dummyOrderId, - [ - 'caseId' => $dummyCaseId, - ] - ); - $this->withGatewaySuccess($dummyGuaranteeDisposition); - - $this->caseUpdatingService - ->expects($this->once()) - ->method('update') - ->with($caseEntity, $this->equalTo([ - 'caseId' => $dummyCaseId, - 'guaranteeDisposition' => $dummyGuaranteeDisposition, - ])); - - $this->service->createForOrder($dummyOrderId); - } - - public function testCreateForOrderWithCaseUpdate() - { - $dummyOrderId = 1; - $dummyCaseId = 42; - $dummyGuaranteeDisposition = 'foo'; - $this->withCaseEntityExistsForOrderId( - $dummyOrderId, - [ - 'caseId' => $dummyCaseId, - ] - ); - $this->withGatewaySuccess($dummyGuaranteeDisposition); - - $result = $this->service->createForOrder($dummyOrderId); - $this->assertEquals( - true, - $result, - 'Service should return true in case if case update service is called' - ); - } - - /** - * @param $orderId - * @param array $caseData - * @return MockObject - */ - private function withCaseEntityExistsForOrderId($orderId, array $caseData = []) - { - $this->createGuaranteeAbility->expects(self::once()) - ->method('isAvailable') - ->with(self::equalTo($orderId)) - ->willReturn(true); - - $dummyCaseEntity = $this->getMockBuilder(CaseInterface::class) - ->getMockForAbstractClass(); - foreach ($caseData as $caseProperty => $casePropertyValue) { - $dummyCaseEntity - ->method('get' . ucfirst($caseProperty)) - ->willReturn($casePropertyValue); - } - - $this->caseManagement - ->method('getByOrderId') - ->with($this->equalTo($orderId)) - ->willReturn($dummyCaseEntity); - - return $dummyCaseEntity; - } - - /** - * @param $failureMessage - */ - private function withGatewayFailure($failureMessage) - { - $this->gateway - ->method('submitCaseForGuarantee') - ->willThrowException(new GatewayException($failureMessage)); - } - - /** - * @param $gatewayResult - */ - private function withGatewaySuccess($gatewayResult) - { - $this->gateway - ->method('submitCaseForGuarantee') - ->willReturn($gatewayResult); - } -} diff --git a/app/code/Magento/Signifyd/Test/Unit/Model/MessageGenerators/CaseRescoreTest.php b/app/code/Magento/Signifyd/Test/Unit/Model/MessageGenerators/CaseRescoreTest.php deleted file mode 100644 index ba14036cd68d0..0000000000000 --- a/app/code/Magento/Signifyd/Test/Unit/Model/MessageGenerators/CaseRescoreTest.php +++ /dev/null @@ -1,148 +0,0 @@ -<?php -/** - * Copyright © Magento, Inc. All rights reserved. - * See COPYING.txt for license details. - */ -namespace Magento\Signifyd\Test\Unit\Model\MessageGenerators; - -use Magento\Framework\TestFramework\Unit\Helper\ObjectManager; -use Magento\Signifyd\Api\CaseRepositoryInterface; -use Magento\Signifyd\Api\Data\CaseInterface; -use Magento\Signifyd\Model\MessageGenerators\CaseRescore; -use PHPUnit_Framework_MockObject_MockObject as MockObject; - -/** - * Tests for Signifyd CaseRescore message generator. - * - * Class CaseRescoreTest - */ -class CaseRescoreTest extends \PHPUnit\Framework\TestCase -{ - private static $data = [ - 'caseId' => 100, - 'score' => 200 - ]; - - /** - * @var ObjectManager - */ - private $objectManager; - - /** - * @var CaseRepositoryInterface|MockObject - */ - private $caseRepository; - - /** - * @var CaseRescore|MockObject - */ - private $caseRescore; - - /** - * @var CaseInterface|MockObject - */ - private $case; - - /** - * @inheritdoc - */ - protected function setUp() - { - $this->case = $this->getMockBuilder(CaseInterface::class) - ->disableOriginalConstructor() - ->getMock(); - $this->objectManager = new ObjectManager($this); - $this->caseRepository = $this->getMockBuilder(CaseRepositoryInterface::class) - ->disableOriginalConstructor() - ->getMock(); - - $this->caseRescore = $this->objectManager->getObject(CaseRescore::class, [ - 'caseRepository' => $this->caseRepository - ]); - } - - /** - * Data array without required attribute caseId. - * - * @expectedException \Magento\Signifyd\Model\MessageGenerators\GeneratorException - * @expectedExceptionMessage The "caseId" should not be empty - */ - public function testGenerateEmptyCaseIdException() - { - $this->caseRescore->generate([]); - } - - /** - * Case entity was not found in DB. - * - * @expectedException \Magento\Signifyd\Model\MessageGenerators\GeneratorException - * @expectedExceptionMessage Case entity not found. - */ - public function testGenerateNotFoundException() - { - $this->caseRepository->expects($this->once()) - ->method('getByCaseId') - ->with(self::$data['caseId']) - ->willReturn(null); - - $this->caseRescore = $this->objectManager->getObject(CaseRescore::class, [ - 'caseRepository' => $this->caseRepository - ]); - - $this->caseRescore->generate(self::$data); - } - - /** - * Generate case message with not empty previous score. - */ - public function testGenerateWithPreviousScore() - { - $this->case->expects($this->once()) - ->method('getScore') - ->willReturn(self::$data['score']); - - $this->caseRepository->expects($this->once()) - ->method('getByCaseId') - ->with(self::$data['caseId']) - ->willReturn($this->case); - - $this->caseRescore = $this->objectManager->getObject(CaseRescore::class, [ - 'caseRepository' => $this->caseRepository - ]); - - $phrase = __( - 'Case Update: New score for the order is %1. Previous score was %2.', - self::$data['score'], - self::$data['score'] - ); - - $message = $this->caseRescore->generate(self::$data); - - $this->assertEquals($phrase, $message); - } - - /** - * Generate case message with empty previous score. - */ - public function testGenerateWithoutPreviousScore() - { - $this->caseRepository->expects($this->once()) - ->method('getByCaseId') - ->with(self::$data['caseId']) - ->willReturn($this->case); - - $this->caseRescore = $this->objectManager->getObject(CaseRescore::class, [ - 'caseRepository' => $this->caseRepository - ]); - - $phrase = __( - 'Case Update: New score for the order is %1. Previous score was %2.', - self::$data['score'], - null - ); - - $message = $this->caseRescore->generate(self::$data); - - $this->assertEquals($phrase, $message); - } -} diff --git a/app/code/Magento/Signifyd/Test/Unit/Model/MessageGenerators/GeneratorFactoryTest.php b/app/code/Magento/Signifyd/Test/Unit/Model/MessageGenerators/GeneratorFactoryTest.php deleted file mode 100644 index 50f87df3b694f..0000000000000 --- a/app/code/Magento/Signifyd/Test/Unit/Model/MessageGenerators/GeneratorFactoryTest.php +++ /dev/null @@ -1,99 +0,0 @@ -<?php -/** - * Copyright © Magento, Inc. All rights reserved. - * See COPYING.txt for license details. - */ -namespace Magento\Signifyd\Test\Unit\Model\MessageGenerators; - -use Magento\Framework\ObjectManagerInterface; -use Magento\Framework\TestFramework\Unit\Helper\ObjectManager; -use Magento\Signifyd\Model\MessageGenerators\PatternGenerator; -use Magento\Signifyd\Model\MessageGenerators\CaseRescore; -use Magento\Signifyd\Model\MessageGenerators\GeneratorFactory; -use PHPUnit_Framework_MockObject_MockObject as MockObject; - -/** - * Contains tests for messages generators factory. - */ -class GeneratorFactoryTest extends \PHPUnit\Framework\TestCase -{ - /** - * @var GeneratorFactory - */ - private $factory; - - /** - * @var ObjectManagerInterface|MockObject - */ - private $fakeObjectManager; - - /** - * @inheritdoc - */ - protected function setUp() - { - $objectManager = new ObjectManager($this); - - $this->fakeObjectManager = $this->getMockBuilder(ObjectManagerInterface::class) - ->disableOriginalConstructor() - ->setMethods(['create']) - ->getMockForAbstractClass(); - - $this->factory = $objectManager->getObject(GeneratorFactory::class, [ - 'objectManager' => $this->fakeObjectManager - ]); - } - - /** - * Checks if factory returns correct instance of message generator. - * - * @covers \Magento\Signifyd\Model\MessageGenerators\GeneratorFactory::create - * @param string $type - * @param string $className - * @dataProvider typeDataProvider - */ - public function testCreate($type, $className) - { - $generator = $this->getMockBuilder($className) - ->disableOriginalConstructor() - ->getMock(); - - $this->fakeObjectManager->expects(self::once()) - ->method('create') - ->with($className) - ->willReturn($generator); - - $instance = $this->factory->create($type); - self::assertInstanceOf($className, $instance); - } - - /** - * Get list of available messages generators types and equal class names. - * - * @return array - */ - public function typeDataProvider() - { - return [ - ['cases/creation', PatternGenerator::class], - ['cases/review', PatternGenerator::class], - ['cases/rescore', CaseRescore::class], - ['guarantees/completion', PatternGenerator::class], - ['guarantees/creation', PatternGenerator::class], - ['guarantees/cancel', PatternGenerator::class], - ]; - } - - /** - * Checks correct exception message for unknown type of message generator. - * - * @covers \Magento\Signifyd\Model\MessageGenerators\GeneratorFactory::create - * @expectedException \InvalidArgumentException - * @expectedExceptionMessage Specified message type does not supported. - */ - public function testCreateWithException() - { - $type = 'cases/unknown'; - $this->factory->create($type); - } -} diff --git a/app/code/Magento/Signifyd/Test/Unit/Model/MessageGenerators/PatternGeneratorTest.php b/app/code/Magento/Signifyd/Test/Unit/Model/MessageGenerators/PatternGeneratorTest.php deleted file mode 100644 index 9d5f71f657a1e..0000000000000 --- a/app/code/Magento/Signifyd/Test/Unit/Model/MessageGenerators/PatternGeneratorTest.php +++ /dev/null @@ -1,83 +0,0 @@ -<?php -/** - * Copyright © Magento, Inc. All rights reserved. - * See COPYING.txt for license details. - */ -namespace Magento\Signifyd\Test\Unit\Model\MessageGenerators; - -use Magento\Signifyd\Model\MessageGenerators\PatternGenerator; - -/** - * Contains tests for different variations like empty data, wrong required arguments, or bad placeholders. - */ -class PatternGeneratorTest extends \PHPUnit\Framework\TestCase -{ - /** - * Checks an exception if generators does not receives required data. - * - * @covers \Magento\Signifyd\Model\MessageGenerators\PatternGenerator::generate - * @expectedException \Magento\Signifyd\Model\MessageGenerators\GeneratorException - * @expectedExceptionMessage The "caseId" should not be empty. - */ - public function testGenerateThrowsException() - { - $data = []; - $generator = new PatternGenerator('Signifyd Case %1 has been created for order.', ['caseId']); - $generator->generate($data); - } - - /** - * Checks cases with different template placeholders and input data. - * - * @covers \Magento\Signifyd\Model\MessageGenerators\PatternGenerator::generate - * @param string $template - * @param array $requiredFields - * @param string $expected - * @dataProvider messageDataProvider - */ - public function testGenerate($template, array $requiredFields, $expected) - { - $data = [ - 'caseId' => 123, - 'reviewDisposition' => 'Good', - 'guaranteeDisposition' => 'Approved', - 'score' => 500, - 'case_score' => 300 - ]; - - $generator = new PatternGenerator($template, $requiredFields); - $actual = $generator->generate($data); - self::assertEquals($expected, $actual); - } - - /** - * Get list of variations with message templates, required fields and expected generated messages. - * - * @return array - */ - public function messageDataProvider() - { - return [ - [ - 'Signifyd Case %1 has been created for order.', - ['caseId'], - 'Signifyd Case 123 has been created for order.' - ], - [ - 'Case Update: Case Review was completed. Review Deposition is %1.', - ['reviewDisposition'], - 'Case Update: Case Review was completed. Review Deposition is Good.' - ], - [ - 'Case Update: New score for the order is %1. Previous score was %2.', - ['score', 'case_score'], - 'Case Update: New score for the order is 500. Previous score was 300.' - ], - [ - 'Case Update: Case is submitted for guarantee.', - [], - 'Case Update: Case is submitted for guarantee.' - ], - ]; - } -} diff --git a/app/code/Magento/Signifyd/Test/Unit/Model/OrderStateServiceTest.php b/app/code/Magento/Signifyd/Test/Unit/Model/OrderStateServiceTest.php deleted file mode 100644 index 3a567a79891f8..0000000000000 --- a/app/code/Magento/Signifyd/Test/Unit/Model/OrderStateServiceTest.php +++ /dev/null @@ -1,204 +0,0 @@ -<?php -/** - * Copyright © Magento, Inc. All rights reserved. - * See COPYING.txt for license details. - */ -namespace Magento\Signifyd\Test\Unit\Model; - -use Magento\Sales\Api\OrderManagementInterface; -use Magento\Sales\Model\Order; -use Magento\Sales\Model\OrderFactory; -use Magento\Signifyd\Api\Data\CaseInterface; -use Magento\Signifyd\Model\CommentsHistoryUpdater; -use Magento\Signifyd\Model\OrderStateService; -use PHPUnit_Framework_MockObject_MockObject as MockObject; - -class OrderStateServiceTest extends \PHPUnit\Framework\TestCase -{ - /** - * @var int - */ - private static $orderId = 123; - - /** - * @var OrderFactory|MockObject - */ - private $orderFactory; - - /** - * @var OrderManagementInterface|MockObject - */ - private $orderManagement; - - /** - * @var CommentsHistoryUpdater|MockObject - */ - private $commentsHistoryUpdater; - - /** - * @var CaseInterface|MockObject - */ - private $caseEntity; - - /** - * @var Order|MockObject - */ - private $order; - - /** - * @var OrderStateService - */ - private $orderStateService; - - /** - * @inheritdoc - */ - protected function setUp() - { - $this->orderManagement = $this->getMockBuilder(OrderManagementInterface::class) - ->getMockForAbstractClass(); - - $this->commentsHistoryUpdater = $this->getMockBuilder(CommentsHistoryUpdater::class) - ->disableOriginalConstructor() - ->getMock(); - - $this->orderFactory = $this->getMockBuilder(OrderFactory::class) - ->setMethods(['create']) - ->disableOriginalConstructor() - ->getMock(); - - $this->order = $this->getMockBuilder(Order::class) - ->disableOriginalConstructor() - ->getMock(); - $this->order->expects($this->once()) - ->method('load') - ->willReturnSelf(); - - $this->orderFactory->expects($this->once()) - ->method('create') - ->willReturn($this->order); - - $this->caseEntity = $this->getMockBuilder(CaseInterface::class) - ->disableOriginalConstructor() - ->getMockForAbstractClass(); - $this->caseEntity->expects($this->once()) - ->method('getOrderId') - ->willReturn(self::$orderId); - - $this->orderStateService = new OrderStateService( - $this->orderFactory, - $this->orderManagement, - $this->commentsHistoryUpdater - ); - } - - /** - * Tests update order state flow when case guarantee disposition is PENDING. - * - * @param bool $canHold - * @param bool $hold - * @param int $addCommentCall - * @dataProvider updateByCaseWithGuaranteePendingDataProvider - */ - public function testUpdateByCaseWithGuaranteePending($canHold, $hold, $addCommentCall) - { - $this->caseEntity->expects($this->once()) - ->method('getGuaranteeDisposition') - ->willReturn(CaseInterface::GUARANTEE_PENDING); - $this->order->expects($this->any()) - ->method('canHold') - ->willReturn($canHold); - $this->orderManagement->expects($this->any()) - ->method('hold') - ->willReturn($hold); - $this->commentsHistoryUpdater->expects($this->exactly($addCommentCall)) - ->method('addComment') - ->with( - $this->caseEntity, - __('Awaiting the Signifyd guarantee disposition.'), - Order::STATE_HOLDED - ); - - $this->orderStateService->updateByCase($this->caseEntity); - } - - /** - * @return array - */ - public function updateByCaseWithGuaranteePendingDataProvider() - { - return [ - ['canHold' => true, 'hold' => true, 'addCommentCall' => 1], - ['canHold' => false, 'hold' => true, 'addCommentCall' => 0], - ['canHold' => true, 'hold' => false, 'addCommentCall' => 0], - ]; - } - - /** - * Tests update order state flow when case guarantee disposition is APPROVED. - * - * @param bool $canUnhold - * @param int $unholdCall - * @dataProvider updateByCaseWithGuaranteeApprovedDataProvider - */ - public function testUpdateByCaseWithGuaranteeApproved($canUnhold, $unholdCall) - { - $this->caseEntity->expects($this->once()) - ->method('getGuaranteeDisposition') - ->willReturn(CaseInterface::GUARANTEE_APPROVED); - $this->order->expects($this->any()) - ->method('canUnhold') - ->willReturn($canUnhold); - $this->orderManagement->expects($this->exactly($unholdCall)) - ->method('unHold'); - $this->commentsHistoryUpdater->expects($this->never()) - ->method('addComment'); - - $this->orderStateService->updateByCase($this->caseEntity); - } - - /** - * @return array - */ - public function updateByCaseWithGuaranteeApprovedDataProvider() - { - return [ - ['canUnhold' => true, 'unholdCall' => 1], - ['canUnhold' => false, 'unholdCall' => 0] - ]; - } - - /** - * Tests update order state flow when case guarantee disposition is DECLINED. - * - * @param bool $canHold - * @param int $holdCall - * @dataProvider updateByCaseWithGuaranteeDeclinedDataProvider - */ - public function testUpdateByCaseWithGuaranteeDeclined($canHold, $holdCall) - { - $this->caseEntity->expects($this->once()) - ->method('getGuaranteeDisposition') - ->willReturn(CaseInterface::GUARANTEE_DECLINED); - $this->order->expects($this->any()) - ->method('canHold') - ->willReturn($canHold); - $this->orderManagement->expects($this->exactly($holdCall)) - ->method('hold'); - $this->commentsHistoryUpdater->expects($this->never()) - ->method('addComment'); - - $this->orderStateService->updateByCase($this->caseEntity); - } - - /** - * @return array - */ - public function updateByCaseWithGuaranteeDeclinedDataProvider() - { - return [ - ['canHold' => true, 'holdCall' => 1], - ['canHold' => false, 'holdCall' => 0] - ]; - } -} diff --git a/app/code/Magento/Signifyd/Test/Unit/Model/PaymentMethodMapper/XmlToArrayConfigConverterTest.php b/app/code/Magento/Signifyd/Test/Unit/Model/PaymentMethodMapper/XmlToArrayConfigConverterTest.php deleted file mode 100644 index 319229e326c4b..0000000000000 --- a/app/code/Magento/Signifyd/Test/Unit/Model/PaymentMethodMapper/XmlToArrayConfigConverterTest.php +++ /dev/null @@ -1,76 +0,0 @@ -<?php -/** - * Copyright © Magento, Inc. All rights reserved. - * See COPYING.txt for license details. - */ -namespace Magento\Signifyd\Test\Unit\Model\PaymentMethodMapper; - -use Magento\Signifyd\Model\PaymentMethodMapper\XmlToArrayConfigConverter; -use Magento\Framework\TestFramework\Unit\Helper\ObjectManager; -use Magento\Framework\Config\Dom\ValidationSchemaException; - -class XmlToArrayConfigConverterTest extends \PHPUnit\Framework\TestCase -{ - /** - * @var XmlToArrayConfigConverter - */ - private $converter; - - /** - * @var string - */ - private $filePath; - - public function setUp() - { - $this->filePath = realpath(__DIR__) . '/_files/'; - - $objectManagerHelper = new ObjectManager($this); - $this->converter = $objectManagerHelper->getObject( - XmlToArrayConfigConverter::class - ); - } - - public function testConvert() - { - $testDom = $this->filePath . 'signifyd_payment_mapping.xml'; - $dom = new \DOMDocument(); - $dom->load($testDom); - $mapping = $this->converter->convert($dom); - $expectedArray = include $this->filePath . 'expected_array.php'; - - $this->assertEquals($expectedArray, $mapping); - } - - /** - * @expectedException \Magento\Framework\Config\Dom\ValidationSchemaException - * @expectedExceptionMessage Only single entrance of "magento_code" node is required. - */ - public function testConvertEmptyPaymentMethodException() - { - $dom = new \DOMDocument(); - $element = $dom->createElement('payment_method'); - $subelement = $dom->createElement('signifyd_code', 'test'); - $element->appendChild($subelement); - $dom->appendChild($element); - - $this->converter->convert($dom); - } - - /** - * @expectedException \Magento\Framework\Config\Dom\ValidationSchemaException - * @expectedExceptionMessage Not empty value for "signifyd_code" node is required. - */ - public function testConvertEmptySygnifydPaymentMethodException() - { - $dom = new \DOMDocument(); - $element = $dom->createElement('payment_method'); - $subelement = $dom->createElement('magento_code', 'test'); - $subelement2 = $dom->createElement('signifyd_code', ''); - $element->appendChild($subelement); - $element->appendChild($subelement2); - $dom->appendChild($element); - - $this->converter->convert($dom); - } -} diff --git a/app/code/Magento/Signifyd/Test/Unit/Model/PaymentMethodMapper/_files/expected_array.php b/app/code/Magento/Signifyd/Test/Unit/Model/PaymentMethodMapper/_files/expected_array.php deleted file mode 100644 index f5d3436ae6b7b..0000000000000 --- a/app/code/Magento/Signifyd/Test/Unit/Model/PaymentMethodMapper/_files/expected_array.php +++ /dev/null @@ -1,12 +0,0 @@ -<?php -/** - * Copyright © Magento, Inc. All rights reserved. - * See COPYING.txt for license details. - */ -return [ - 'payment_method_1' => 'PAYMENT_CARD', - 'payment_method_2' => 'PAYPAL_ACCOUNT', - 'payment_method_3' => 'CHECK', - 'payment_method_4' => 'CASH', - 'payment_method_5' => 'FREE' -]; diff --git a/app/code/Magento/Signifyd/Test/Unit/Model/PaymentMethodMapper/_files/signifyd_payment_mapping.xml b/app/code/Magento/Signifyd/Test/Unit/Model/PaymentMethodMapper/_files/signifyd_payment_mapping.xml deleted file mode 100644 index f70763e22c418..0000000000000 --- a/app/code/Magento/Signifyd/Test/Unit/Model/PaymentMethodMapper/_files/signifyd_payment_mapping.xml +++ /dev/null @@ -1,32 +0,0 @@ -<?xml version="1.0" ?> -<!-- -/** - * Copyright © Magento, Inc. All rights reserved. - * See COPYING.txt for license details. - */ ---> -<config xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" - xsi:noNamespaceSchemaLocation="urn:magento:module:Magento_Signifyd:etc/payment_mapping.xsd"> - <payment_method_list> - <payment_method> - <magento_code>payment_method_1</magento_code> - <signifyd_code>PAYMENT_CARD</signifyd_code> - </payment_method> - <payment_method> - <magento_code>payment_method_2</magento_code> - <signifyd_code>PAYPAL_ACCOUNT</signifyd_code> - </payment_method> - <payment_method> - <magento_code>payment_method_3</magento_code> - <signifyd_code>CHECK</signifyd_code> - </payment_method> - <payment_method> - <magento_code>payment_method_4</magento_code> - <signifyd_code>CASH</signifyd_code> - </payment_method> - <payment_method> - <magento_code>payment_method_5</magento_code> - <signifyd_code>FREE</signifyd_code> - </payment_method> - </payment_method_list> -</config> diff --git a/app/code/Magento/Signifyd/Test/Unit/Model/PaymentVerificationFactoryTest.php b/app/code/Magento/Signifyd/Test/Unit/Model/PaymentVerificationFactoryTest.php deleted file mode 100644 index b0f9239d43bfa..0000000000000 --- a/app/code/Magento/Signifyd/Test/Unit/Model/PaymentVerificationFactoryTest.php +++ /dev/null @@ -1,222 +0,0 @@ -<?php -/** - * Copyright © Magento, Inc. All rights reserved. - * See COPYING.txt for license details. - */ -namespace Magento\Signifyd\Test\Unit\Model; - -use Magento\Framework\ObjectManagerInterface; -use Magento\Payment\Api\PaymentVerificationInterface; -use Magento\Signifyd\Model\PaymentVerificationFactory; -use Magento\Framework\TestFramework\Unit\Helper\ObjectManager; -use PHPUnit_Framework_MockObject_MockObject as MockObject; -use Magento\Payment\Gateway\ConfigInterface; - -class PaymentVerificationFactoryTest extends \PHPUnit\Framework\TestCase -{ - /** - * @var PaymentVerificationFactory - */ - private $factory; - - /** - * @var ObjectManager - */ - private $objectManager; - - /** - * @var ObjectManagerInterface|MockObject - */ - private $fakeObjectManager; - - /** - * @var ConfigInterface|MockObject - */ - private $config; - - /** - * @var PaymentVerificationInterface|MockObject - */ - private $avsDefaultAdapter; - - /** - * @var PaymentVerificationInterface|MockObject - */ - private $cvvDefaultAdapter; - - /** - * @inheritdoc - */ - protected function setUp() - { - $this->objectManager = new ObjectManager($this); - - $this->fakeObjectManager = $this->getMockBuilder(ObjectManagerInterface::class) - ->disableOriginalConstructor() - ->getMock(); - - $this->config = $this->getMockBuilder(ConfigInterface::class) - ->disableOriginalConstructor() - ->getMock(); - - $this->avsDefaultAdapter = $this->getMockBuilder(PaymentVerificationInterface::class) - ->disableOriginalConstructor() - ->getMock(); - - $this->cvvDefaultAdapter = $this->getMockBuilder(PaymentVerificationInterface::class) - ->disableOriginalConstructor() - ->getMock(); - - $this->factory = $this->objectManager->getObject(PaymentVerificationFactory::class, [ - 'objectManager' => $this->fakeObjectManager, - 'config' => $this->config, - 'avsDefaultAdapter' => $this->avsDefaultAdapter, - 'cvvDefaultAdapter' => $this->cvvDefaultAdapter - ]); - } - - /** - * Checks a test case when factory creates CVV mapper for provided payment method. - * - * @covers \Magento\Signifyd\Model\PaymentVerificationFactory::createPaymentCvv - */ - public function testCreatePaymentCvv() - { - $paymentMethodCode = 'exists_payment'; - - $this->config->expects(self::once()) - ->method('setMethodCode') - ->with(self::equalTo($paymentMethodCode)) - ->willReturnSelf(); - - $this->config->expects(self::once()) - ->method('getValue') - ->with('cvv_ems_adapter') - ->willReturn(PaymentVerificationInterface::class); - - /** @var PaymentVerificationInterface|MockObject $cvvAdapter */ - $cvvAdapter = $this->getMockBuilder(PaymentVerificationInterface::class) - ->disableOriginalConstructor() - ->getMock(); - - $this->fakeObjectManager->expects(self::once()) - ->method('create') - ->with(self::equalTo(PaymentVerificationInterface::class)) - ->willReturn($cvvAdapter); - - $mapper = $this->factory->createPaymentCvv($paymentMethodCode); - self::assertInstanceOf(PaymentVerificationInterface::class, $mapper); - } - - /** - * Checks a test case, when provided payment method does not have cvv mapper. - * - * @covers \Magento\Signifyd\Model\PaymentVerificationFactory::createPaymentCvv - */ - public function testCreateDefaultCvvMapper() - { - $paymentMethodCode = 'non_exists_payment'; - - $this->config->expects(self::once()) - ->method('setMethodCode') - ->with(self::equalTo($paymentMethodCode)) - ->willReturnSelf(); - - $this->config->expects(self::once()) - ->method('getValue') - ->with('cvv_ems_adapter') - ->willReturn(null); - - $this->fakeObjectManager->expects(self::never()) - ->method('create'); - - $mapper = $this->factory->createPaymentCvv($paymentMethodCode); - self::assertSame($this->cvvDefaultAdapter, $mapper); - } - - /** - * Checks a test case, when mapper implementation does not corresponding to PaymentVerificationInterface. - * - * @covers \Magento\Signifyd\Model\PaymentVerificationFactory::createPaymentCvv - * @expectedException \Magento\Framework\Exception\ConfigurationMismatchException - * @expectedExceptionMessage stdClass must implement Magento\Payment\Api\PaymentVerificationInterface - */ - public function testCreateWithUnsupportedImplementation() - { - $paymentMethodCode = 'exists_payment'; - - $this->config->expects(self::once()) - ->method('setMethodCode') - ->with(self::equalTo($paymentMethodCode)) - ->willReturnSelf(); - - $this->config->expects(self::once()) - ->method('getValue') - ->with('cvv_ems_adapter') - ->willReturn(\stdClass::class); - - $cvvAdapter = new \stdClass(); - $this->fakeObjectManager->expects(self::once()) - ->method('create') - ->with(self::equalTo(\stdClass::class)) - ->willReturn($cvvAdapter); - - $this->factory->createPaymentCvv($paymentMethodCode); - } - - /** - * Checks a test case when factory creates AVS mapper for provided payment method. - * - * @covers \Magento\Signifyd\Model\PaymentVerificationFactory::createPaymentAvs - */ - public function testCreatePaymentAvs() - { - $paymentMethodCode = 'exists_payment'; - - $this->config->expects(self::once()) - ->method('setMethodCode') - ->with(self::equalTo($paymentMethodCode)) - ->willReturnSelf(); - - $this->config->expects(self::once()) - ->method('getValue') - ->with('avs_ems_adapter') - ->willReturn(PaymentVerificationInterface::class); - - $avsAdapter = $this->getMockBuilder(PaymentVerificationInterface::class) - ->disableOriginalConstructor() - ->getMock(); - - $this->fakeObjectManager->expects(self::once()) - ->method('create') - ->with(self::equalTo(PaymentVerificationInterface::class)) - ->willReturn($avsAdapter); - - $mapper = $this->factory->createPaymentAvs($paymentMethodCode); - self::assertInstanceOf(PaymentVerificationInterface::class, $mapper); - } - - /** - * Checks a test case when provided payment method does not support - */ - public function testCreateDefaultAvsMapper() - { - $paymentMethodCode = 'non_exists_payment'; - - $this->config->expects(self::once()) - ->method('setMethodCode') - ->with(self::equalTo($paymentMethodCode)) - ->willReturnSelf(); - - $this->config->expects(self::once()) - ->method('getValue') - ->with('avs_ems_adapter') - ->willReturn(null); - - $this->fakeObjectManager->expects(self::never()) - ->method('create'); - - $mapper = $this->factory->createPaymentAvs($paymentMethodCode); - self::assertSame($this->avsDefaultAdapter, $mapper); - } -} diff --git a/app/code/Magento/Signifyd/Test/Unit/Model/SalesOrderGrid/OrderGridUpdaterTest.php b/app/code/Magento/Signifyd/Test/Unit/Model/SalesOrderGrid/OrderGridUpdaterTest.php deleted file mode 100644 index 885c9f018a488..0000000000000 --- a/app/code/Magento/Signifyd/Test/Unit/Model/SalesOrderGrid/OrderGridUpdaterTest.php +++ /dev/null @@ -1,72 +0,0 @@ -<?php -/** - * Copyright © Magento, Inc. All rights reserved. - * See COPYING.txt for license details. - */ -namespace Magento\Signifyd\Test\Unit\Model\SalesOrderGrid; - -use Magento\Framework\App\Config\ScopeConfigInterface; -use Magento\Sales\Model\ResourceModel\GridInterface; -use Magento\Signifyd\Model\SalesOrderGrid\OrderGridUpdater; -use PHPUnit_Framework_MockObject_MockObject as MockObject; - -class OrderGridUpdaterTest extends \PHPUnit\Framework\TestCase -{ - /** - * @var GridInterface|MockObject - */ - private $orderGrid; - - /** - * @var ScopeConfigInterface|MockObject - */ - private $globalConfig; - - /** - * @var OrderGridUpdater - */ - private $model; - - /** - * Sets up testing class and dependency mocks. - */ - protected function setUp() - { - $this->orderGrid = $this->getMockBuilder(GridInterface::class) - ->getMockForAbstractClass(); - $this->globalConfig = $this->getMockBuilder(ScopeConfigInterface::class) - ->getMockForAbstractClass(); - - $this->model = new OrderGridUpdater($this->orderGrid, $this->globalConfig); - } - - public function testUpdateInSyncMode() - { - $orderId = 1; - - $this->globalConfig->expects($this->once()) - ->method('getValue') - ->with('dev/grid/async_indexing', 'default', null) - ->willReturn(false); - $this->orderGrid->expects($this->once()) - ->method('refresh') - ->with($orderId); - - $this->model->update($orderId); - } - - public function testUpdateInAsyncMode() - { - $orderId = 1; - - $this->globalConfig->expects($this->once()) - ->method('getValue') - ->with('dev/grid/async_indexing', 'default', null) - ->willReturn(true); - $this->orderGrid->expects($this->never()) - ->method('refresh') - ->with($orderId); - - $this->model->update($orderId); - } -} diff --git a/app/code/Magento/Signifyd/Test/Unit/Model/SignifydGateway/Client/HttpClientFactoryTest.php b/app/code/Magento/Signifyd/Test/Unit/Model/SignifydGateway/Client/HttpClientFactoryTest.php deleted file mode 100644 index 4aefd63355773..0000000000000 --- a/app/code/Magento/Signifyd/Test/Unit/Model/SignifydGateway/Client/HttpClientFactoryTest.php +++ /dev/null @@ -1,131 +0,0 @@ -<?php -/** - * Copyright © Magento, Inc. All rights reserved. - * See COPYING.txt for license details. - */ -namespace Magento\Signifyd\Test\Unit\Model\SignifydGateway\Client; - -use Magento\Signifyd\Model\SignifydGateway\Client\HttpClientFactory; -use Magento\Framework\TestFramework\Unit\Helper\ObjectManager; -use Magento\Signifyd\Model\Config; -use Magento\Framework\HTTP\ZendClient; -use Magento\Framework\HTTP\ZendClientFactory; -use Magento\Framework\Json\EncoderInterface; -use \PHPUnit_Framework_MockObject_MockObject as MockObject; - -class HttpClientFactoryTest extends \PHPUnit\Framework\TestCase -{ - /** - * @var string - */ - private static $dummy = 'dummy'; - - /** - * @var ObjectManager - */ - private $objectManager; - - /** - * @var Config - */ - private $config; - - /** - * @var ZendClientFactory|MockObject - */ - private $clientFactory; - - /** - * @var ZendClient|MockObject - */ - private $client; - - /** - * @var EncoderInterface|MockObject - */ - private $dataEncoder; - - /** - * @var ZendClient|MockObject - */ - private $httpClient; - - public function setUp() - { - $this->objectManager = new ObjectManager($this); - - $this->config = $this->getMockBuilder(Config::class) - ->disableOriginalConstructor() - ->getMock(); - - $this->client = $this->getMockBuilder(ZendClient::class) - ->disableOriginalConstructor() - ->setMethods(['setHeaders', 'setMethod', 'setUri', 'setRawData']) - ->getMock(); - - $this->clientFactory = $this->getMockBuilder(ZendClientFactory::class) - ->disableOriginalConstructor() - ->setMethods(['create']) - ->getMock(); - - $this->clientFactory->expects($this->once()) - ->method('create') - ->willReturn($this->client); - - $this->dataEncoder = $this->getMockBuilder(EncoderInterface::class) - ->disableOriginalConstructor() - ->getMock(); - - $this->httpClient = $this->objectManager->getObject(HttpClientFactory::class, [ - 'config' => $this->config, - 'clientFactory' => $this->clientFactory, - 'dataEncoder' => $this->dataEncoder - ]); - } - - public function testCreateHttpClient() - { - $this->config->expects($this->once()) - ->method('getApiKey') - ->willReturn('testKey'); - - $this->config->expects($this->once()) - ->method('getApiUrl') - ->willReturn('testUrl'); - - $client = $this->httpClient->create('url', 'method'); - - $this->assertInstanceOf(ZendClient::class, $client); - } - - public function testCreateWithParams() - { - $param = ['id' => 1]; - $storeId = 1; - $json = '{"id":1}'; - - $this->config->expects($this->once()) - ->method('getApiKey') - ->with($storeId) - ->willReturn('testKey'); - - $this->config->expects($this->once()) - ->method('getApiUrl') - ->with($storeId) - ->willReturn(self::$dummy); - - $this->dataEncoder->expects($this->once()) - ->method('encode') - ->with($this->equalTo($param)) - ->willReturn($json); - - $this->client->expects($this->once()) - ->method('setRawData') - ->with($this->equalTo($json), 'application/json') - ->willReturnSelf(); - - $client = $this->httpClient->create('url', 'method', $param, $storeId); - - $this->assertInstanceOf(ZendClient::class, $client); - } -} diff --git a/app/code/Magento/Signifyd/Test/Unit/Model/SignifydGateway/Client/ResponseHandlerTest.php b/app/code/Magento/Signifyd/Test/Unit/Model/SignifydGateway/Client/ResponseHandlerTest.php deleted file mode 100644 index 1ee55d7ad150c..0000000000000 --- a/app/code/Magento/Signifyd/Test/Unit/Model/SignifydGateway/Client/ResponseHandlerTest.php +++ /dev/null @@ -1,182 +0,0 @@ -<?php -/** - * Copyright © Magento, Inc. All rights reserved. - * See COPYING.txt for license details. - */ -namespace Magento\Signifyd\Test\Unit\Model\SignifydGateway\Client; - -use Magento\Signifyd\Model\SignifydGateway\Client\ResponseHandler; -use Magento\Framework\Json\DecoderInterface; -use Magento\Signifyd\Model\SignifydGateway\ApiCallException; -use Magento\Framework\TestFramework\Unit\Helper\ObjectManager; -use \Zend_Http_Response as Response; -use \PHPUnit_Framework_MockObject_MockObject as MockObject; - -class ResponseHandlerTest extends \PHPUnit\Framework\TestCase -{ - /** - * @var string - */ - private static $errorMessage = 'Some error'; - - /** - * @var string - */ - private static $testJson = '{"id": 1}'; - - /** - * @var int - */ - private static $successfulCode = 200; - - /** - * @var int - */ - private static $phpVersionId = 50000; - - /** - * @var ObjectManager - */ - private $objectManager; - - /** - * @var ResponseHandler|MockObject - */ - private $responseHandler; - - /** - * @var DecoderInterface|MockObject - */ - private $dataDecoder; - - /** - * @var Response|MockObject - */ - private $response; - - public function setUp() - { - $this->objectManager = new ObjectManager($this); - - $this->dataDecoder = $this->getMockBuilder(DecoderInterface::class) - ->disableOriginalConstructor() - ->getMock(); - - $this->response = $this->getMockBuilder(Response::class) - ->disableOriginalConstructor() - ->setMethods(['getStatus', 'getBody']) - ->getMock(); - - $this->responseHandler = $this->objectManager->getObject(ResponseHandler::class, [ - 'dataDecoder' => $this->dataDecoder - ]); - } - - /** - * @dataProvider errorsProvider - */ - public function testHandleFailureMessage($code, $message) - { - $this->response->expects($this->any()) - ->method('getStatus') - ->willReturn($code); - - $this->response->expects($this->once()) - ->method('getBody') - ->willReturn(self::$errorMessage); - - try { - $this->responseHandler->handle($this->response); - } catch (ApiCallException $e) { - $this->assertEquals($e->getMessage(), sprintf($message, self::$errorMessage)); - } - } - - /** - * @return array - */ - public function errorsProvider() - { - return [ - [400, 'Bad Request - The request could not be parsed. Response: %s'], - [401, 'Unauthorized - user is not logged in, could not be authenticated. Response: %s'], - [403, 'Forbidden - Cannot access resource. Response: %s'], - [404, 'Not Found - resource does not exist. Response: %s'], - [ - 409, - 'Conflict - with state of the resource on server. Can occur with (too rapid) PUT requests. Response: %s' - ], - [500, 'Server error. Response: %s'] - ]; - } - - /** - * @expectedException \Magento\Signifyd\Model\SignifydGateway\ApiCallException - * @expectedExceptionMessage Response is not valid JSON: Decoding failed: Syntax error - */ - public function testHandleEmptyJsonException() - { - $this->response->expects($this->any()) - ->method('getStatus') - ->willReturn(self::$successfulCode); - - $this->response->expects($this->once()) - ->method('getBody') - ->willReturn(''); - - $r = new \ReflectionObject($this->responseHandler); - $prop = $r->getProperty('phpVersionId'); - $prop->setAccessible(true); - $prop->setValue(self::$phpVersionId); - - $this->responseHandler->handle($this->response); - } - - /** - * @expectedException \Magento\Signifyd\Model\SignifydGateway\ApiCallException - * @expectedExceptionMessage Response is not valid JSON: Some error - */ - public function testHandleInvalidJson() - { - $this->response->expects($this->any()) - ->method('getStatus') - ->willReturn(self::$successfulCode); - - $this->response->expects($this->once()) - ->method('getBody') - ->willReturn('param'); - - $this->dataDecoder = $this->getMockBuilder(DecoderInterface::class) - ->disableOriginalConstructor() - ->getMock(); - - $this->dataDecoder->expects($this->once()) - ->method('decode') - ->with('param') - ->willThrowException(new \Exception(self::$errorMessage, 30)); - - $this->responseHandler = $this->objectManager->getObject(ResponseHandler::class, [ - 'dataDecoder' => $this->dataDecoder - ]); - - $this->responseHandler->handle($this->response); - } - - public function testHandle() - { - $this->response->expects($this->any()) - ->method('getStatus') - ->willReturn(self::$successfulCode); - - $this->response->expects($this->once()) - ->method('getBody') - ->willReturn(self::$testJson); - - $this->dataDecoder->expects($this->once()) - ->method('decode') - ->willReturn(json_decode(self::$testJson, 1)); - - $decodedResponseBody = $this->responseHandler->handle($this->response); - $this->assertEquals($decodedResponseBody, ['id' => 1]); - } -} diff --git a/app/code/Magento/Signifyd/Test/Unit/Model/SignifydGateway/GatewayTest.php b/app/code/Magento/Signifyd/Test/Unit/Model/SignifydGateway/GatewayTest.php deleted file mode 100644 index ba82ff4619ad3..0000000000000 --- a/app/code/Magento/Signifyd/Test/Unit/Model/SignifydGateway/GatewayTest.php +++ /dev/null @@ -1,448 +0,0 @@ -<?php -/** - * Copyright © Magento, Inc. All rights reserved. - * See COPYING.txt for license details. - */ -namespace Magento\Signifyd\Test\Unit\Model\SignifydGateway; - -use Magento\Sales\Api\Data\OrderInterface; -use Magento\Sales\Api\OrderRepositoryInterface; -use Magento\Signifyd\Api\CaseRepositoryInterface; -use Magento\Signifyd\Api\Data\CaseInterface; -use PHPUnit_Framework_MockObject_MockObject as MockObject; -use Magento\Signifyd\Model\SignifydGateway\Gateway; -use Magento\Signifyd\Model\SignifydGateway\GatewayException; -use Magento\Signifyd\Model\SignifydGateway\Request\CreateCaseBuilderInterface; -use Magento\Signifyd\Model\SignifydGateway\ApiClient; -use Magento\Signifyd\Model\SignifydGateway\ApiCallException; - -class GatewayTest extends \PHPUnit\Framework\TestCase -{ - /** - * @var CreateCaseBuilderInterface|MockObject - */ - private $createCaseBuilder; - - /** - * @var ApiClient|MockObject - */ - private $apiClient; - - /** - * @var Gateway - */ - private $gateway; - - /** - * @var OrderRepositoryInterface|MockObject - */ - private $orderRepository; - - /** - * @var CaseRepositoryInterface|MockObject - */ - private $caseRepository; - - public function setUp() - { - $this->createCaseBuilder = $this->getMockBuilder(CreateCaseBuilderInterface::class) - ->getMockForAbstractClass(); - - $this->apiClient = $this->getMockBuilder(ApiClient::class) - ->disableOriginalConstructor() - ->getMock(); - - $this->orderRepository = $this->getMockBuilder(OrderRepositoryInterface::class) - ->getMockForAbstractClass(); - - $this->caseRepository= $this->getMockBuilder(CaseRepositoryInterface::class) - ->getMockForAbstractClass(); - - $this->gateway = new Gateway( - $this->createCaseBuilder, - $this->apiClient, - $this->orderRepository, - $this->caseRepository - ); - } - - public function testCreateCaseForSpecifiedOrder() - { - $dummyOrderId = 1; - $dummyStoreId = 2; - $dummySignifydInvestigationId = 42; - - $this->withOrderEntity($dummyOrderId, $dummyStoreId); - $this->apiClient - ->method('makeApiCall') - ->willReturn([ - 'investigationId' => $dummySignifydInvestigationId - ]); - - $this->createCaseBuilder - ->expects($this->atLeastOnce()) - ->method('build') - ->with($this->equalTo($dummyOrderId)) - ->willReturn([]); - - $result = $this->gateway->createCase($dummyOrderId); - $this->assertEquals(42, $result); - } - - public function testCreateCaseCallsValidApiMethod() - { - $dummyOrderId = 1; - $dummyStoreId = 2; - $dummySignifydInvestigationId = 42; - - $this->withOrderEntity($dummyOrderId, $dummyStoreId); - $this->createCaseBuilder - ->method('build') - ->willReturn([]); - - $this->apiClient - ->expects($this->atLeastOnce()) - ->method('makeApiCall') - ->with( - $this->equalTo('/cases'), - $this->equalTo('POST'), - $this->isType('array'), - $this->equalTo($dummyStoreId) - ) - ->willReturn([ - 'investigationId' => $dummySignifydInvestigationId - ]); - - $result = $this->gateway->createCase($dummyOrderId); - $this->assertEquals(42, $result); - } - - public function testCreateCaseNormalFlow() - { - $dummyOrderId = 1; - $dummyStoreId = 2; - $dummySignifydInvestigationId = 42; - - $this->withOrderEntity($dummyOrderId, $dummyStoreId); - $this->createCaseBuilder - ->method('build') - ->willReturn([]); - $this->apiClient - ->method('makeApiCall') - ->willReturn([ - 'investigationId' => $dummySignifydInvestigationId - ]); - - $returnedInvestigationId = $this->gateway->createCase($dummyOrderId); - $this->assertEquals( - $dummySignifydInvestigationId, - $returnedInvestigationId, - 'Method must return value specified in "investigationId" response parameter' - ); - } - - public function testCreateCaseWithFailedApiCall() - { - $dummyOrderId = 1; - $dummyStoreId = 2; - $apiCallFailureMessage = 'Api call failed'; - - $this->withOrderEntity($dummyOrderId, $dummyStoreId); - $this->createCaseBuilder - ->method('build') - ->willReturn([]); - $this->apiClient - ->method('makeApiCall') - ->willThrowException(new ApiCallException($apiCallFailureMessage)); - - $this->expectException(GatewayException::class); - $this->expectExceptionMessage($apiCallFailureMessage); - $this->gateway->createCase($dummyOrderId); - } - - public function testCreateCaseWithMissedResponseRequiredData() - { - $dummyOrderId = 1; - $dummyStoreId = 2; - - $this->withOrderEntity($dummyOrderId, $dummyStoreId); - $this->createCaseBuilder - ->method('build') - ->willReturn([]); - $this->apiClient - ->method('makeApiCall') - ->willReturn([ - 'someOtherParameter' => 'foo', - ]); - - $this->expectException(GatewayException::class); - $this->gateway->createCase($dummyOrderId); - } - - public function testCreateCaseWithAdditionalResponseData() - { - $dummyOrderId = 1; - $dummyStoreId = 2; - $dummySignifydInvestigationId = 42; - - $this->withOrderEntity($dummyOrderId, $dummyStoreId); - $this->createCaseBuilder - ->method('build') - ->willReturn([]); - $this->apiClient - ->method('makeApiCall') - ->willReturn([ - 'investigationId' => $dummySignifydInvestigationId, - 'someOtherParameter' => 'foo', - ]); - - $returnedInvestigationId = $this->gateway->createCase($dummyOrderId); - $this->assertEquals( - $dummySignifydInvestigationId, - $returnedInvestigationId, - 'Method must return value specified in "investigationId" response parameter and ignore any other parameters' - ); - } - - public function testSubmitCaseForGuaranteeCallsValidApiMethod() - { - $dummySygnifydCaseId = 42; - $dummyStoreId = 1; - $dummyDisposition = 'APPROVED'; - - $this->withCaseEntity($dummySygnifydCaseId, $dummyStoreId); - $this->apiClient - ->expects($this->atLeastOnce()) - ->method('makeApiCall') - ->with( - $this->equalTo('/guarantees'), - $this->equalTo('POST'), - $this->equalTo([ - 'caseId' => $dummySygnifydCaseId - ]), - $this->equalTo($dummyStoreId) - )->willReturn([ - 'disposition' => $dummyDisposition - ]); - - $result = $this->gateway->submitCaseForGuarantee($dummySygnifydCaseId); - $this->assertEquals('APPROVED', $result); - } - - public function testSubmitCaseForGuaranteeWithFailedApiCall() - { - $dummySygnifydCaseId = 42; - $dummyStoreId = 1; - $apiCallFailureMessage = 'Api call failed'; - - $this->withCaseEntity($dummySygnifydCaseId, $dummyStoreId); - $this->apiClient - ->method('makeApiCall') - ->willThrowException(new ApiCallException($apiCallFailureMessage)); - - $this->expectException(GatewayException::class); - $this->expectExceptionMessage($apiCallFailureMessage); - $result = $this->gateway->submitCaseForGuarantee($dummySygnifydCaseId); - $this->assertEquals('Api call failed', $result); - } - - public function testSubmitCaseForGuaranteeReturnsDisposition() - { - $dummySygnifydCaseId = 42; - $dummyStoreId = 1; - $dummyDisposition = 'APPROVED'; - $dummyGuaranteeId = 123; - $dummyRereviewCount = 0; - - $this->withCaseEntity($dummySygnifydCaseId, $dummyStoreId); - $this->apiClient - ->method('makeApiCall') - ->willReturn([ - 'guaranteeId' => $dummyGuaranteeId, - 'disposition' => $dummyDisposition, - 'rereviewCount' => $dummyRereviewCount, - ]); - - $actualDisposition = $this->gateway->submitCaseForGuarantee($dummySygnifydCaseId); - $this->assertEquals( - $dummyDisposition, - $actualDisposition, - 'Method must return guarantee disposition retrieved in Signifyd API response as a result' - ); - } - - public function testSubmitCaseForGuaranteeWithMissedDisposition() - { - $dummySygnifydCaseId = 42; - $dummyStoreId = 1; - $dummyGuaranteeId = 123; - $dummyRereviewCount = 0; - - $this->withCaseEntity($dummySygnifydCaseId, $dummyStoreId); - $this->apiClient - ->method('makeApiCall') - ->willReturn([ - 'guaranteeId' => $dummyGuaranteeId, - 'rereviewCount' => $dummyRereviewCount, - ]); - - $this->expectException(GatewayException::class); - $this->gateway->submitCaseForGuarantee($dummySygnifydCaseId); - } - - public function testSubmitCaseForGuaranteeWithUnexpectedDisposition() - { - $dummySygnifydCaseId = 42; - $dummyStoreId = 1; - $dummyUnexpectedDisposition = 'UNEXPECTED'; - - $this->withCaseEntity($dummySygnifydCaseId, $dummyStoreId); - $this->apiClient - ->method('makeApiCall') - ->willReturn([ - 'disposition' => $dummyUnexpectedDisposition, - ]); - - $this->expectException(GatewayException::class); - $result = $this->gateway->submitCaseForGuarantee($dummySygnifydCaseId); - $this->assertEquals('UNEXPECTED', $result); - } - - /** - * @dataProvider supportedGuaranteeDispositionsProvider - */ - public function testSubmitCaseForGuaranteeWithExpectedDisposition($dummyExpectedDisposition) - { - $dummySygnifydCaseId = 42; - $dummyStoreId = 1; - - $this->withCaseEntity($dummySygnifydCaseId, $dummyStoreId); - $this->apiClient - ->method('makeApiCall') - ->willReturn([ - 'disposition' => $dummyExpectedDisposition, - ]); - - try { - $result = $this->gateway->submitCaseForGuarantee($dummySygnifydCaseId); - $this->assertEquals($dummyExpectedDisposition, $result); - } catch (GatewayException $e) { - $this->fail(sprintf( - 'Expected disposition "%s" was not accepted with message "%s"', - $dummyExpectedDisposition, - $e->getMessage() - )); - } - } - - /** - * Checks a test case when guarantee for a case is successfully canceled - * - * @covers \Magento\Signifyd\Model\SignifydGateway\Gateway::cancelGuarantee - */ - public function testCancelGuarantee() - { - $caseId = 123; - $dummyStoreId = 1; - - $this->withCaseEntity($caseId, $dummyStoreId); - $this->apiClient->expects(self::once()) - ->method('makeApiCall') - ->with( - '/cases/' . $caseId . '/guarantee', - 'PUT', - ['guaranteeDisposition' => Gateway::GUARANTEE_CANCELED], - $dummyStoreId - ) - ->willReturn( - ['disposition' => Gateway::GUARANTEE_CANCELED] - ); - - $result = $this->gateway->cancelGuarantee($caseId); - self::assertEquals(Gateway::GUARANTEE_CANCELED, $result); - } - - /** - * Checks a case when API request returns unexpected guarantee disposition. - * - * @covers \Magento\Signifyd\Model\SignifydGateway\Gateway::cancelGuarantee - * @expectedException \Magento\Signifyd\Model\SignifydGateway\GatewayException - * @expectedExceptionMessage API returned unexpected disposition: DECLINED. - */ - public function testCancelGuaranteeWithUnexpectedDisposition() - { - $caseId = 123; - $dummyStoreId = 1; - - $this->withCaseEntity($caseId, $dummyStoreId); - $this->apiClient->expects(self::once()) - ->method('makeApiCall') - ->with( - '/cases/' . $caseId . '/guarantee', - 'PUT', - ['guaranteeDisposition' => Gateway::GUARANTEE_CANCELED], - $dummyStoreId - ) - ->willReturn(['disposition' => Gateway::GUARANTEE_DECLINED]); - - $result = $this->gateway->cancelGuarantee($caseId); - $this->assertEquals(Gateway::GUARANTEE_CANCELED, $result); - } - - /** - * @return array - */ - public function supportedGuaranteeDispositionsProvider() - { - return [ - 'APPROVED' => ['APPROVED'], - 'DECLINED' => ['DECLINED'], - 'PENDING' => ['PENDING'], - 'CANCELED' => ['CANCELED'], - 'IN_REVIEW' => ['IN_REVIEW'], - 'UNREQUESTED' => ['UNREQUESTED'], - ]; - } - - /** - * Specifies order entity mock execution. - * - * @param int $orderId - * @param int $storeId - * @return void - */ - private function withOrderEntity(int $orderId, int $storeId): void - { - $orderEntity = $this->getMockBuilder(OrderInterface::class) - ->disableOriginalConstructor() - ->getMock(); - $orderEntity->method('getStoreId') - ->willReturn($storeId); - $this->orderRepository->method('get') - ->with($orderId) - ->willReturn($orderEntity); - } - - /** - * Specifies case entity mock execution. - * - * @param int $caseId - * @param int $storeId - * @return void - */ - private function withCaseEntity(int $caseId, int $storeId): void - { - $orderId = 1; - - $caseEntity = $this->getMockBuilder(CaseInterface::class) - ->disableOriginalConstructor() - ->getMock(); - $caseEntity->method('getOrderId') - ->willReturn($orderId); - $this->caseRepository->method('getByCaseId') - ->with($caseId) - ->willReturn($caseEntity); - - $this->withOrderEntity($orderId, $storeId); - } -} diff --git a/app/code/Magento/Signifyd/Test/Unit/Model/SignifydGateway/Response/WebhookMessageReaderTest.php b/app/code/Magento/Signifyd/Test/Unit/Model/SignifydGateway/Response/WebhookMessageReaderTest.php deleted file mode 100644 index 0dfdf4980fb58..0000000000000 --- a/app/code/Magento/Signifyd/Test/Unit/Model/SignifydGateway/Response/WebhookMessageReaderTest.php +++ /dev/null @@ -1,114 +0,0 @@ -<?php -/** - * Copyright © Magento, Inc. All rights reserved. - * See COPYING.txt for license details. - */ -namespace Magento\Signifyd\Test\Unit\Model\SignifydGateway\Response; - -use Magento\Framework\Json\DecoderInterface; -use Magento\Signifyd\Model\SignifydGateway\Response\WebhookRequest; -use Magento\Signifyd\Model\SignifydGateway\Response\WebhookMessageReader; -use Magento\Signifyd\Model\SignifydGateway\Response\WebhookMessage; -use Magento\Signifyd\Model\SignifydGateway\Response\WebhookMessageFactory; - -/** - * Class WebhookMessageReaderTest - */ -class WebhookMessageReaderTest extends \PHPUnit\Framework\TestCase -{ - /** - * @var WebhookMessageReader - */ - private $model; - - /** - * @var DecoderInterface|\PHPUnit_Framework_MockObject_MockObject - */ - private $decoder; - - /** - * @var WebhookMessageFactory|\PHPUnit_Framework_MockObject_MockObject - */ - private $webhookMessageFactory; - - /** - * @var WebhookRequest|\PHPUnit_Framework_MockObject_MockObject - */ - private $webhookRequest; - - /** - * @inheritdoc - */ - protected function setUp() - { - $this->decoder = $this->getMockBuilder(DecoderInterface::class) - ->getMockForAbstractClass(); - - $this->webhookMessageFactory = $this->getMockBuilder(WebhookMessageFactory::class) - ->setMethods(['create']) - ->disableOriginalConstructor() - ->getMock(); - - $this->webhookRequest = $this->getMockBuilder(WebhookRequest::class) - ->disableOriginalConstructor() - ->getMock(); - - $this->model = new WebhookMessageReader( - $this->decoder, - $this->webhookMessageFactory - ); - } - - /** - * Tests successful reading webhook message from request. - * - */ - public function testReadSuccess() - { - $rawBody = 'body'; - $topic = 'topic'; - $decodedData = ['status' => "DISMISSED", 'orderId' => '19418']; - - $this->webhookRequest->expects($this->once()) - ->method('getBody') - ->willReturn($rawBody); - $this->webhookRequest->expects($this->once()) - ->method('getEventTopic') - ->willReturn('topic'); - $this->decoder->expects($this->once()) - ->method('decode') - ->with($rawBody) - ->willReturn($decodedData); - $webhookMessage = $this->getMockBuilder(WebhookMessage::class) - ->disableOriginalConstructor() - ->getMock(); - $this->webhookMessageFactory->expects($this->once()) - ->method('create') - ->with( - [ - 'data' => $decodedData, - 'eventTopic' => $topic - ] - ) - ->willReturn($webhookMessage); - - $this->assertEquals( - $webhookMessage, - $this->model->read($this->webhookRequest) - ); - } - - /** - * Tests reading failure webhook message from request. - * - * @expectedException \InvalidArgumentException - */ - public function testReadFail() - { - $this->decoder->expects($this->once()) - ->method('decode') - ->willThrowException(new \Exception('Error')); - - $this->model->read($this->webhookRequest); - } -} diff --git a/app/code/Magento/Signifyd/Test/Unit/Model/SignifydGateway/Response/WebhookRequestValidatorTest.php b/app/code/Magento/Signifyd/Test/Unit/Model/SignifydGateway/Response/WebhookRequestValidatorTest.php deleted file mode 100644 index 5ae6b95a77548..0000000000000 --- a/app/code/Magento/Signifyd/Test/Unit/Model/SignifydGateway/Response/WebhookRequestValidatorTest.php +++ /dev/null @@ -1,231 +0,0 @@ -<?php -/** - * Copyright © Magento, Inc. All rights reserved. - * See COPYING.txt for license details. - */ -namespace Magento\Signifyd\Test\Unit\Model\SignifydGateway\Response; - -use Magento\Framework\Json\DecoderInterface; -use Magento\Signifyd\Model\Config; -use Magento\Signifyd\Model\SignifydGateway\Response\WebhookRequestValidator; -use Magento\Signifyd\Model\SignifydGateway\Response\WebhookRequest; - -/** - * Class WebhookRequestValidatorTest - */ -class WebhookRequestValidatorTest extends \PHPUnit\Framework\TestCase -{ - /** - * @var WebhookRequestValidator - */ - private $model; - - /** - * @var Config|\PHPUnit_Framework_MockObject_MockObject - */ - private $config; - - /** - * @var DecoderInterface|\PHPUnit_Framework_MockObject_MockObject - */ - private $decoder; - - /** - * @inheritdoc - */ - protected function setUp() - { - $this->config = $this->getMockBuilder(Config::class) - ->disableOriginalConstructor() - ->getMock(); - - $this->decoder = $this->getMockBuilder(DecoderInterface::class) - ->getMockForAbstractClass(); - - $this->model = new WebhookRequestValidator( - $this->config, - $this->decoder - ); - } - - /** - * Tests successful cases. - * - * @param string $body - * @param string $topic - * @param string $hash - * @param int$callConfigCount - * @dataProvider validateSuccessDataProvider - */ - public function testValidateSuccess($body, $topic, $hash, $callConfigCount) - { - $this->config->expects($this->exactly($callConfigCount)) - ->method('getApiKey') - ->willReturn('GpFZZnxGgIxuI8BazSm3v6eGK'); - - $this->decoder->expects($this->once()) - ->method('decode') - ->with($body) - ->willReturn(['status' => "DISMISSED", 'orderId' => '19418']); - - $webhookRequest = $this->createWebhookRequest($body, $topic, $hash); - - $this->assertTrue( - $this->model->validate($webhookRequest) - ); - } - - /** - * @case 1. All data are correct, event topic has real value - * @case 2. All data are correct, event topic has test value - * @return array - */ - public function validateSuccessDataProvider() - { - return [ - 1 => [ - 'body' => '{ status: "DISMISSED", orderId: "19418" }', - 'topic' => 'cases/creation', - 'hash' => 'KWR8Bzu3tinEpDviw1opWSMJGFqfpA79nNGp0TEYM6Q=', - 'callConfigCount' => 1 - ], - 2 => [ - 'body' => '{ status: "DISMISSED", orderId: "19418" }', - 'topic' => 'cases/test', - 'hash' => '6npAahliNbzYo/Qi4+g+JeqPhLFgg19sIbuxDLmvobw=', - 'callConfigCount' => 0 - ] - ]; - } - - /** - * Case with wrong event topic - * - * @param string $topic - * @dataProvider validationTopicFailsDataProvider - */ - public function testValidationTopicFails($topic) - { - $body = '{ status: "DISMISSED", orderId: "19418" }'; - $hash = 'KWR8Bzu3tinEpDviw1opWSMJGFqfpA79nNGp0TEYM6Q='; - - $this->config->expects($this->never()) - ->method('getApiKey'); - - $this->decoder->expects($this->never()) - ->method('decode'); - - $webhookRequest = $this->createWebhookRequest($body, $topic, $hash); - - $this->assertFalse( - $this->model->validate($webhookRequest), - 'Negative webhook event topic value validation fails' - ); - } - - /** - * @return array - */ - public function validationTopicFailsDataProvider() - { - return [ - ['wrong topic' => 'bla-bla-topic'], - ['empty topic' => ''] - ]; - } - - /** - * Case with wrong webhook request body - * - * @param string $body - * @dataProvider validationBodyFailsDataProvider - */ - public function testValidationBodyFails($body) - { - $topic = 'cases/creation'; - $hash = 'KWR8Bzu3tinEpDviw1opWSMJGFqfpA79nNGp0TEYM6Q='; - $webhookRequest = $this->createWebhookRequest($body, $topic, $hash); - - $this->config->expects($this->never()) - ->method('getApiKey'); - - if (empty($body)) { - $this->decoder->expects($this->once()) - ->method('decode') - ->with($body) - ->willReturn(''); - } else { - $this->decoder->expects($this->once()) - ->method('decode') - ->with($body) - ->willThrowException(new \Exception('Error')); - } - - $this->assertFalse( - $this->model->validate($webhookRequest), - 'Negative webhook request body validation fails' - ); - } - - /** - * @return array - */ - public function validationBodyFailsDataProvider() - { - return [ - ['Empty request body' => ''], - ['Bad request body' => '{ bad data}'] - ]; - } - - /** - * Case with wrong hash - */ - public function testValidationHashFails() - { - $topic = 'cases/creation'; - $body = '{ status: "DISMISSED", orderId: "19418" }'; - $hash = 'wrong hash'; - $webhookRequest = $this->createWebhookRequest($body, $topic, $hash); - - $this->config->expects($this->once()) - ->method('getApiKey') - ->willReturn('GpFZZnxGgIxuI8BazSm3v6eGK'); - - $this->decoder->expects($this->once()) - ->method('decode') - ->with($body) - ->willReturn(['status' => "DISMISSED", 'orderId' => '19418']); - - $this->assertFalse( - $this->model->validate($webhookRequest), - 'Negative webhook hash validation fails' - ); - } - - /** - * Returns mocked WebhookRequest - * - * @param string $body - * @param string $topic - * @param string $hash - * @return WebhookRequest|\PHPUnit_Framework_MockObject_MockObject - */ - private function createWebhookRequest($body, $topic, $hash) - { - $webhookRequest = $this->getMockBuilder(WebhookRequest::class) - ->disableOriginalConstructor() - ->getMock(); - $webhookRequest->expects($this->once()) - ->method('getBody') - ->willReturn($body); - $webhookRequest->expects($this->once()) - ->method('getEventTopic') - ->willReturn($topic); - $webhookRequest->expects($this->once()) - ->method('getHash') - ->willReturn($hash); - - return $webhookRequest; - } -} diff --git a/app/code/Magento/Signifyd/Test/Unit/Model/SignifydOrderSessionIdTest.php b/app/code/Magento/Signifyd/Test/Unit/Model/SignifydOrderSessionIdTest.php deleted file mode 100644 index 9d3061f240c21..0000000000000 --- a/app/code/Magento/Signifyd/Test/Unit/Model/SignifydOrderSessionIdTest.php +++ /dev/null @@ -1,60 +0,0 @@ -<?php -/** - * Copyright © Magento, Inc. All rights reserved. - * See COPYING.txt for license details. - */ -namespace Magento\Signifyd\Test\Unit\Model; - -use Magento\Checkout\Model\Session as CheckoutSession; -use Magento\Framework\DataObject\IdentityGeneratorInterface; -use Magento\Signifyd\Model\SignifydOrderSessionId; -use PHPUnit_Framework_MockObject_MockObject as MockObject; - -/** - * Class SignifydOrderSessionIdTest tests that SignifydOrderSessionId class dependencies - * follow the contracts. - */ -class SignifydOrderSessionIdTest extends \PHPUnit\Framework\TestCase -{ - /** - * @var SignifydOrderSessionId - */ - private $signifydOrderSessionId; - - /** - * @var IdentityGeneratorInterface|MockObject - */ - private $identityGenerator; - - /** - * Sets up testing class and dependency mocks. - */ - protected function setUp() - { - $this->identityGenerator = $this->getMockBuilder(IdentityGeneratorInterface::class) - ->getMockForAbstractClass(); - - $this->signifydOrderSessionId = new SignifydOrderSessionId($this->identityGenerator); - } - - /** - * Tests method by passing quoteId parameter - * - * @covers \Magento\Signifyd\Model\SignifydOrderSessionId::get - */ - public function testGetByQuoteId() - { - $quoteId = 1; - $signifydOrderSessionId = 'asdfzxcv'; - - $this->identityGenerator->expects(self::once()) - ->method('generateIdForData') - ->with($quoteId) - ->willReturn($signifydOrderSessionId); - - $this->assertEquals( - $signifydOrderSessionId, - $this->signifydOrderSessionId->get($quoteId) - ); - } -} diff --git a/app/code/Magento/Signifyd/Test/Unit/Observer/PlaceOrderTest.php b/app/code/Magento/Signifyd/Test/Unit/Observer/PlaceOrderTest.php deleted file mode 100644 index d63831b1d4a8e..0000000000000 --- a/app/code/Magento/Signifyd/Test/Unit/Observer/PlaceOrderTest.php +++ /dev/null @@ -1,284 +0,0 @@ -<?php -/** - * Copyright © Magento, Inc. All rights reserved. - * See COPYING.txt for license details. - */ -namespace Magento\Signifyd\Test\Unit\Observer; - -use Magento\Framework\Event; -use Magento\Framework\Event\Observer; -use Magento\Framework\Exception\AlreadyExistsException; -use Magento\Payment\Model\MethodInterface; -use Magento\Sales\Api\Data\OrderInterface; -use Magento\Sales\Model\Order; -use Magento\Sales\Model\Order\Payment; -use Magento\Signifyd\Api\CaseCreationServiceInterface; -use Magento\Signifyd\Model\Config; -use Magento\Signifyd\Observer\PlaceOrder; -use PHPUnit_Framework_MockObject_MockObject as MockObject; -use Psr\Log\LoggerInterface; - -class PlaceOrderTest extends \PHPUnit\Framework\TestCase -{ - /** - * @var Config|MockObject - */ - private $config; - - /** - * @var CaseCreationServiceInterface|MockObject - */ - private $creationService; - - /** - * @var LoggerInterface|MockObject - */ - private $logger; - - /** - * @var Observer|MockObject - */ - private $observer; - - /** - * @var Event|MockObject - */ - private $event; - - /** - * @var OrderInterface|MockObject - */ - private $orderEntity; - - /** - * @var PlaceOrder - */ - private $placeOrder; - - /** - * @inheritdoc - */ - protected function setUp() - { - $this->config = $this->getMockBuilder(Config::class) - ->disableOriginalConstructor() - ->setMethods(['isActive']) - ->getMock(); - - $this->logger = $this->getMockBuilder(LoggerInterface::class) - ->disableOriginalConstructor() - ->getMock(); - - $this->creationService = $this->getMockBuilder(CaseCreationServiceInterface::class) - ->disableOriginalConstructor() - ->setMethods(['createForOrder']) - ->getMock(); - - $this->observer = $this->getMockBuilder(Observer::class) - ->disableOriginalConstructor() - ->setMethods(['getEvent']) - ->getMock(); - - $this->event = $this->getMockBuilder(Event::class) - ->disableOriginalConstructor() - ->setMethods(['getData']) - ->getMock(); - - $this->placeOrder = new PlaceOrder( - $this->config, - $this->creationService, - $this->logger - ); - } - - /** - * Checks a test case when Signifyd module is disabled. - * - * @covers \Magento\Signifyd\Observer\PlaceOrder::execute - */ - public function testExecuteWithDisabledModule() - { - $orderId = 1; - $storeId = 2; - $this->withActiveSignifydIntegration(false, $storeId); - $this->withOrderEntity($orderId, $storeId); - - $this->creationService->expects(self::never()) - ->method('createForOrder'); - - $this->placeOrder->execute($this->observer); - } - - /** - * Checks a test case when the observer event returns empty an order entity. - * - * @covers \Magento\Signifyd\Observer\PlaceOrder::execute - */ - public function testExecuteWithoutOrder() - { - $this->withActiveSignifydIntegration(true); - $this->withOrderEntity(null, null); - - $this->creationService->expects(self::never()) - ->method('createForOrder'); - - $this->placeOrder->execute($this->observer); - } - - /** - * Checks a test case when the order placed with offline payment method. - * - * @covers \Magento\Signifyd\Observer\PlaceOrder::execute - */ - public function testExecuteWithOfflinePayment() - { - $orderId = 1; - $storeId = 2; - $this->withActiveSignifydIntegration(true, $storeId); - $this->withOrderEntity($orderId, $storeId); - $this->withAvailablePaymentMethod(false); - - $this->creationService->expects(self::never()) - ->method('createForOrder'); - - $this->placeOrder->execute($this->observer); - } - - /** - * Checks a test case when case creation service fails. - * - * @covers \Magento\Signifyd\Observer\PlaceOrder::execute - */ - public function testExecuteWithFailedCaseCreation() - { - $orderId = 1; - $storeId = 2; - $exceptionMessage = __('Case with the same order id already exists.'); - - $this->withActiveSignifydIntegration(true, $storeId); - $this->withOrderEntity($orderId, $storeId); - $this->withAvailablePaymentMethod(true); - - $this->creationService->method('createForOrder') - ->with(self::equalTo($orderId)) - ->willThrowException(new AlreadyExistsException($exceptionMessage)); - - $this->logger->method('error') - ->with(self::equalTo($exceptionMessage)); - - $result = $this->placeOrder->execute($this->observer); - $this->assertNull($result); - } - - /** - * Checks a test case when observer successfully calls case creation service. - * - * @covers \Magento\Signifyd\Observer\PlaceOrder::execute - */ - public function testExecute() - { - $orderId = 1; - $storeId = 2; - - $this->withActiveSignifydIntegration(true, $storeId); - $this->withOrderEntity($orderId, $storeId); - $this->withAvailablePaymentMethod(true); - - $this->creationService - ->method('createForOrder') - ->with(self::equalTo($orderId)); - - $this->logger->expects(self::never()) - ->method('error'); - - $this->placeOrder->execute($this->observer); - } - - public function testExecuteWithOrderPendingPayment() - { - $orderId = 1; - $storeId = 2; - - $this->withActiveSignifydIntegration(true, $storeId); - $this->withOrderEntity($orderId, $storeId); - $this->orderEntity->method('getState') - ->willReturn(Order::STATE_PENDING_PAYMENT); - $this->withAvailablePaymentMethod(true); - - $this->creationService->expects(self::never()) - ->method('createForOrder'); - - $this->placeOrder->execute($this->observer); - } - - /** - * Specifies order entity mock execution. - * - * @param int|null $orderId - * @param int|null $storeId - * @return void - */ - private function withOrderEntity($orderId, $storeId): void - { - $this->orderEntity = $this->getMockBuilder(OrderInterface::class) - ->disableOriginalConstructor() - ->getMock(); - - $this->orderEntity->method('getEntityId') - ->willReturn($orderId); - $this->orderEntity->method('getStoreId') - ->willReturn($storeId); - - $this->observer->method('getEvent') - ->willReturn($this->event); - - $this->event->method('getData') - ->with('order') - ->willReturn($this->orderEntity); - } - - /** - * Specifies config mock execution. - * - * @param bool $isActive - * @param int|null $storeId - * @return void - */ - private function withActiveSignifydIntegration(bool $isActive, $storeId = null): void - { - $this->config->method('isActive') - ->with($storeId) - ->willReturn($isActive); - } - - /** - * Specifies payment method mock execution. - * - * @param bool $isAvailable - * @return void - */ - private function withAvailablePaymentMethod($isAvailable) - { - /** @var MethodInterface|MockObject $paymentMethod */ - $paymentMethod = $this->getMockBuilder(MethodInterface::class) - ->disableOriginalConstructor() - ->getMock(); - - /** - * The code depends on implementation but not interface - * because order payment implements two interfaces - */ - /** @var Payment|MockObject $orderPayment */ - $orderPayment = $this->getMockBuilder(Payment::class) - ->disableOriginalConstructor() - ->getMock(); - $this->orderEntity->method('getPayment') - ->willReturn($orderPayment); - - $orderPayment->method('getMethodInstance') - ->willReturn($paymentMethod); - - $paymentMethod->method('isOffline') - ->willReturn(!$isAvailable); - } -} diff --git a/app/code/Magento/Signifyd/Ui/Component/Listing/Column/Guarantee/Options.php b/app/code/Magento/Signifyd/Ui/Component/Listing/Column/Guarantee/Options.php deleted file mode 100644 index 1e6234a8e27a9..0000000000000 --- a/app/code/Magento/Signifyd/Ui/Component/Listing/Column/Guarantee/Options.php +++ /dev/null @@ -1,56 +0,0 @@ -<?php -/** - * Copyright © Magento, Inc. All rights reserved. - * See COPYING.txt for license details. - */ -namespace Magento\Signifyd\Ui\Component\Listing\Column\Guarantee; - -use Magento\Framework\Escaper; -use Magento\Framework\Data\OptionSourceInterface; -use Magento\Signifyd\Api\Data\CaseInterface; - -/** - * Class Options - */ -class Options implements OptionSourceInterface -{ - /** - * @var Escaper - */ - private $escaper; - - /** - * Constructor - * - * @param Escaper $escaper - */ - public function __construct(Escaper $escaper) - { - $this->escaper = $escaper; - } - - /** - * {@inheritdoc} - */ - public function toOptionArray() - { - return [ - [ - 'value' => CaseInterface::GUARANTEE_DECLINED, - 'label' => $this->escaper->escapeHtml(__('Declined')) - ], - [ - 'value' => CaseInterface::GUARANTEE_APPROVED, - 'label' => $this->escaper->escapeHtml(__('Approved')) - ], - [ - 'value' => CaseInterface::GUARANTEE_CANCELED, - 'label' => $this->escaper->escapeHtml(__('Canceled')) - ], - [ - 'value' => CaseInterface::GUARANTEE_PENDING, - 'label' => $this->escaper->escapeHtml(__('Pending')) - ] - ]; - } -} diff --git a/app/code/Magento/Signifyd/composer.json b/app/code/Magento/Signifyd/composer.json deleted file mode 100644 index c0214e9a2813c..0000000000000 --- a/app/code/Magento/Signifyd/composer.json +++ /dev/null @@ -1,35 +0,0 @@ -{ - "name": "magento/module-signifyd", - "description": "Submitting Case Entry to Signifyd on Order Creation", - "config": { - "sort-packages": true - }, - "require": { - "magento/framework": "*", - "magento/module-backend": "*", - "magento/module-checkout": "*", - "magento/module-config": "*", - "magento/module-customer": "*", - "magento/module-directory": "*", - "magento/module-payment": "*", - "magento/module-sales": "*", - "magento/module-store": "*", - "php": "~7.1.3||~7.2.0||~7.3.0" - }, - "suggest": { - "magento/module-config": "*" - }, - "type": "magento2-module", - "license": [ - "OSL-3.0", - "AFL-3.0" - ], - "autoload": { - "files": [ - "registration.php" - ], - "psr-4": { - "Magento\\Signifyd\\": "" - } - } -} diff --git a/app/code/Magento/Signifyd/etc/acl.xml b/app/code/Magento/Signifyd/etc/acl.xml deleted file mode 100644 index 32f0493fbcad9..0000000000000 --- a/app/code/Magento/Signifyd/etc/acl.xml +++ /dev/null @@ -1,22 +0,0 @@ -<?xml version="1.0"?> -<!-- -/** - * Copyright © Magento, Inc. All rights reserved. - * See COPYING.txt for license details. - */ ---> -<config xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="urn:magento:framework:Acl/etc/acl.xsd"> - <acl> - <resources> - <resource id="Magento_Backend::admin"> - <resource id="Magento_Backend::stores"> - <resource id="Magento_Backend::stores_settings"> - <resource id="Magento_Config::config"> - <resource id="Magento_Sales::fraud_protection" title="Fraud Protection Section" translate="title" /> - </resource> - </resource> - </resource> - </resource> - </resources> - </acl> -</config> diff --git a/app/code/Magento/Signifyd/etc/adminhtml/di.xml b/app/code/Magento/Signifyd/etc/adminhtml/di.xml deleted file mode 100644 index c771d67216d43..0000000000000 --- a/app/code/Magento/Signifyd/etc/adminhtml/di.xml +++ /dev/null @@ -1,10 +0,0 @@ -<?xml version="1.0"?> -<!-- -/** - * Copyright © Magento, Inc. All rights reserved. - * See COPYING.txt for license details. - */ ---> -<config xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="urn:magento:framework:ObjectManager/etc/config.xsd"> - <preference for="Magento\Signifyd\Model\QuoteSession\QuoteSessionInterface" type="Magento\Signifyd\Model\QuoteSession\Adminhtml\BackendSession" /> -</config> diff --git a/app/code/Magento/Signifyd/etc/adminhtml/routes.xml b/app/code/Magento/Signifyd/etc/adminhtml/routes.xml deleted file mode 100644 index c078ab3c8c4c1..0000000000000 --- a/app/code/Magento/Signifyd/etc/adminhtml/routes.xml +++ /dev/null @@ -1,14 +0,0 @@ -<?xml version="1.0"?> -<!-- -/** - * Copyright © Magento, Inc. All rights reserved. - * See COPYING.txt for license details. - */ ---> -<config xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="urn:magento:framework:App/etc/routes.xsd"> - <router id="admin"> - <route id="signifyd" frontName="signifyd"> - <module name="Magento_Signifyd" before="Magento_Backend" /> - </route> - </router> -</config> diff --git a/app/code/Magento/Signifyd/etc/adminhtml/system.xml b/app/code/Magento/Signifyd/etc/adminhtml/system.xml deleted file mode 100644 index 272ce78aec2e5..0000000000000 --- a/app/code/Magento/Signifyd/etc/adminhtml/system.xml +++ /dev/null @@ -1,77 +0,0 @@ -<?xml version="1.0"?> -<!-- -/** - * Copyright © Magento, Inc. All rights reserved. - * See COPYING.txt for license details. - */ ---> -<config xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="urn:magento:module:Magento_Config:etc/system_file.xsd"> - <system> - <section id="fraud_protection" translate="label" type="text" sortOrder="410" showInDefault="1" showInWebsite="1" showInStore="1"> - <label>Fraud Protection</label> - <tab>sales</tab> - <resource>Magento_Sales::fraud_protection</resource> - <group id="signifyd" type="text" sortOrder="10" showInDefault="1" showInWebsite="1" showInStore="0"> - <fieldset_css>signifyd-logo-header</fieldset_css> - <group id="about" translate="label comment" sortOrder="15" showInDefault="1" showInWebsite="1" showInStore="0"> - <frontend_model>Magento\Signifyd\Block\Adminhtml\System\Config\Fieldset\Info</frontend_model> - <fieldset_css>signifyd-about-header</fieldset_css> - <label><![CDATA[Protect your store from fraud with Guaranteed Fraud Protection by Signifyd.]]></label> - <comment><![CDATA[Signifyd automatically reviews your orders for fraud, telling you in seconds which orders to ship, and which to reject. - We back our approvals with 100% chargeback protection, reimbursing you the full order amount plus fees should you ever receive a fraudulent chargeback. - <p>Benefits:<ul> - <li>Grow your business without fear of fraud</li> - <li>Accept more orders and maximize your revenue</li> - <li>Automate order review and shift fraud off your plate</li></ul></p>]]> - </comment> - <more_url>https://www.signifyd.com/magento-guaranteed-fraud-protection</more_url> - </group> - <group id="config" translate="label comment" sortOrder="15" showInDefault="1" showInWebsite="1" showInStore="0"> - <fieldset_css>signifyd-about-header</fieldset_css> - <label>Configuration</label> - <comment><![CDATA[<a href="https://www.signifyd.com/resources/manual/magento-2/signifyd-on-magento-integration-guide/" target="_blank">View our setup guide</a> for step-by-step instructions on how to integrate Signifyd with Magento.<br />For support contact <a href="mailto:support@signifyd.com">support@signifyd.com</a>.]]> - </comment> - <field id="active" translate="label" type="select" showInDefault="1" showInWebsite="1" showInStore="0"> - <label>Enable this Solution</label> - <source_model>Magento\Config\Model\Config\Source\Yesno</source_model> - <config_path>fraud_protection/signifyd/active</config_path> - </field> - <field id="api_key" translate="label" type="obscure" sortOrder="20" showInDefault="1" showInWebsite="1" showInStore="0"> - <label>API Key</label> - <comment><![CDATA[Your API key can be found on the <a href="http://signifyd.com/settings" target="_blank">settings page</a> in the Signifyd console.]]></comment> - <config_path>fraud_protection/signifyd/api_key</config_path> - <backend_model>Magento\Config\Model\Config\Backend\Encrypted</backend_model> - <depends> - <field id="active">1</field> - </depends> - </field> - <field id="api_url" translate="label" type="text" sortOrder="30" showInDefault="1" showInWebsite="1" showInStore="0"> - <label>API URL</label> - <config_path>fraud_protection/signifyd/api_url</config_path> - <comment>Don’t change unless asked to do so.</comment> - <depends> - <field id="active">1</field> - </depends> - </field> - <field id="debug" translate="label" type="select" sortOrder="40" showInDefault="1" showInWebsite="1" showInStore="0"> - <label>Debug</label> - <source_model>Magento\Config\Model\Config\Source\Yesno</source_model> - <config_path>fraud_protection/signifyd/debug</config_path> - <depends> - <field id="active">1</field> - </depends> - </field> - <field id="webhook_url" translate="label comment" type="text" sortOrder="50" showInDefault="1" showInWebsite="1" showInStore="0"> - <label>Webhook URL</label> - <comment><![CDATA[Your webhook URL will be used to <a href="https://app.signifyd.com/settings/notifications" target="_blank">configure</a> a guarantee completed webhook in Signifyd. Webhooks are used to sync Signifyd`s guarantee decisions back to Magento.]]></comment> - <attribute type="handler_url">signifyd/webhooks/handler</attribute> - <frontend_model>Magento\Signifyd\Block\Adminhtml\System\Config\Field\WebhookUrl</frontend_model> - <depends> - <field id="active">1</field> - </depends> - </field> - </group> - </group> - </section> - </system> -</config> diff --git a/app/code/Magento/Signifyd/etc/config.xml b/app/code/Magento/Signifyd/etc/config.xml deleted file mode 100644 index 804342a14bb08..0000000000000 --- a/app/code/Magento/Signifyd/etc/config.xml +++ /dev/null @@ -1,19 +0,0 @@ -<?xml version="1.0"?> -<!-- -/** - * Copyright © Magento, Inc. All rights reserved. - * See COPYING.txt for license details. - */ ---> -<config xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="urn:magento:module:Magento_Store:etc/config.xsd"> - <default> - <fraud_protection> - <signifyd> - <active>0</active> - <api_url>https://api.signifyd.com/v2/</api_url> - <api_key backend_model="Magento\Config\Model\Config\Backend\Encrypted" /> - <debug>0</debug> - </signifyd> - </fraud_protection> - </default> -</config> diff --git a/app/code/Magento/Signifyd/etc/db_schema.xml b/app/code/Magento/Signifyd/etc/db_schema.xml deleted file mode 100644 index 8d47321c38b29..0000000000000 --- a/app/code/Magento/Signifyd/etc/db_schema.xml +++ /dev/null @@ -1,43 +0,0 @@ -<?xml version="1.0"?> -<!-- -/** - * Copyright © Magento, Inc. All rights reserved. - * See COPYING.txt for license details. - */ ---> -<schema xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" - xsi:noNamespaceSchemaLocation="urn:magento:framework:Setup/Declaration/Schema/etc/schema.xsd"> - <table name="signifyd_case" resource="sales" engine="innodb" comment="signifyd_case"> - <column xsi:type="int" name="entity_id" padding="10" unsigned="true" nullable="false" identity="true" - comment="Entity_id"/> - <column xsi:type="int" name="order_id" padding="10" unsigned="true" nullable="true" identity="false" - comment="Order_id"/> - <column xsi:type="int" name="case_id" padding="10" unsigned="true" nullable="true" identity="false" - comment="Case_id"/> - <column xsi:type="boolean" name="guarantee_eligible" nullable="true" comment="Guarantee_eligible"/> - <column xsi:type="varchar" name="guarantee_disposition" nullable="true" length="32" default="PENDING" - comment="Guarantee_disposition"/> - <column xsi:type="varchar" name="status" nullable="true" length="32" default="PENDING" comment="Status"/> - <column xsi:type="int" name="score" padding="10" unsigned="true" nullable="true" identity="false" - comment="Score"/> - <column xsi:type="text" name="associated_team" nullable="true" comment="Associated_team"/> - <column xsi:type="varchar" name="review_disposition" nullable="true" length="32" comment="Review_disposition"/> - <column xsi:type="timestamp" name="created_at" on_update="false" nullable="true" comment="Created_at"/> - <column xsi:type="timestamp" name="updated_at" on_update="false" nullable="true" comment="Updated_at"/> - <constraint xsi:type="primary" referenceId="PRIMARY"> - <column name="entity_id"/> - </constraint> - <constraint xsi:type="foreign" referenceId="SIGNIFYD_CASE_ORDER_ID_SALES_ORDER_ENTITY_ID" table="signifyd_case" - column="order_id" referenceTable="sales_order" referenceColumn="entity_id" onDelete="SET NULL"/> - <constraint xsi:type="unique" referenceId="SIGNIFYD_CASE_ORDER_ID"> - <column name="order_id"/> - </constraint> - <constraint xsi:type="unique" referenceId="SIGNIFYD_CASE_CASE_ID"> - <column name="case_id"/> - </constraint> - </table> - <table name="sales_order_grid" resource="sales" comment="Sales Flat Order Grid"> - <column xsi:type="varchar" name="signifyd_guarantee_status" nullable="true" length="32" - comment="Signifyd Guarantee Disposition Status"/> - </table> -</schema> diff --git a/app/code/Magento/Signifyd/etc/db_schema_whitelist.json b/app/code/Magento/Signifyd/etc/db_schema_whitelist.json deleted file mode 100644 index 69d164b23d9e7..0000000000000 --- a/app/code/Magento/Signifyd/etc/db_schema_whitelist.json +++ /dev/null @@ -1,28 +0,0 @@ -{ - "signifyd_case": { - "column": { - "entity_id": true, - "order_id": true, - "case_id": true, - "guarantee_eligible": true, - "guarantee_disposition": true, - "status": true, - "score": true, - "associated_team": true, - "review_disposition": true, - "created_at": true, - "updated_at": true - }, - "constraint": { - "PRIMARY": true, - "SIGNIFYD_CASE_ORDER_ID_SALES_ORDER_ENTITY_ID": true, - "SIGNIFYD_CASE_ORDER_ID": true, - "SIGNIFYD_CASE_CASE_ID": true - } - }, - "sales_order_grid": { - "column": { - "signifyd_guarantee_status": true - } - } -} \ No newline at end of file diff --git a/app/code/Magento/Signifyd/etc/di.xml b/app/code/Magento/Signifyd/etc/di.xml deleted file mode 100644 index e82e8f84b3584..0000000000000 --- a/app/code/Magento/Signifyd/etc/di.xml +++ /dev/null @@ -1,105 +0,0 @@ -<?xml version="1.0"?> -<!-- -/** - * Copyright © Magento, Inc. All rights reserved. - * See COPYING.txt for license details. - */ ---> -<config xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="urn:magento:framework:ObjectManager/etc/config.xsd"> - <preference for="Magento\Signifyd\Api\Data\CaseInterface" type="Magento\Signifyd\Model\CaseEntity" /> - <preference for="Magento\Signifyd\Api\CaseRepositoryInterface" type="Magento\Signifyd\Model\CaseRepository" /> - <preference for="Magento\Signifyd\Api\CaseManagementInterface" type="Magento\Signifyd\Model\CaseManagement" /> - <preference for="Magento\Signifyd\Api\Data\CaseSearchResultsInterface" type="Magento\Signifyd\Model\CaseSearchResults" /> - <preference for="Magento\Signifyd\Api\CaseCreationServiceInterface" type="Magento\Signifyd\Model\CaseServices\CreationService" /> - <preference for="Magento\Signifyd\Api\GuaranteeCreationServiceInterface" type="Magento\Signifyd\Model\Guarantee\CreationService" /> - <preference for="Magento\Signifyd\Api\GuaranteeCancelingServiceInterface" type="Magento\Signifyd\Model\Guarantee\CancelingService" /> - <preference for="Magento\Signifyd\Model\SignifydGateway\Request\CreateCaseBuilderInterface" type="Magento\Signifyd\Model\SignifydGateway\Request\CreateCaseBuilder" /> - - <virtualType name="SignifydAvsDefaultMapper" type="Magento\Signifyd\Model\PredefinedVerificationCode" /> - <virtualType name="SignifydCvvDefaultMapper" type="Magento\Signifyd\Model\PredefinedVerificationCode" /> - - <type name="Magento\Signifyd\Model\PaymentVerificationFactory"> - <arguments> - <argument name="config" xsi:type="object">Magento\Payment\Gateway\Config\Config</argument> - <argument name="avsDefaultAdapter" xsi:type="object">SignifydAvsDefaultMapper</argument> - <argument name="cvvDefaultAdapter" xsi:type="object">SignifydCvvDefaultMapper</argument> - </arguments> - </type> - <type name="Magento\Signifyd\Model\SalesOrderGrid\OrderGridUpdater"> - <arguments> - <argument name="entityGrid" xsi:type="object">Magento\Sales\Model\ResourceModel\Order\Grid</argument> - </arguments> - </type> - <virtualType name="SignifydIdListProvider" type="Magento\Sales\Model\ResourceModel\Provider\NotSyncedDataProvider"> - <arguments> - <argument name="providers" xsi:type="array"> - <item name="signifyd" xsi:type="string">Magento\Signifyd\Model\SalesOrderGrid\NotSyncedOrderIdListProvider</item> - </argument> - </arguments> - </virtualType> - <virtualType name="Magento\Sales\Model\ResourceModel\Order\Grid"> - <arguments> - <argument name="idListProvider" xsi:type="object">SignifydIdListProvider</argument> - <argument name="joins" xsi:type="array"> - <item name="signifyd_case" xsi:type="array"> - <item name="table" xsi:type="string">signifyd_case</item> - <item name="origin_column" xsi:type="string">entity_id</item> - <item name="target_column" xsi:type="string">order_id</item> - </item> - </argument> - <argument name="columns" xsi:type="array"> - <item name="signifyd_guarantee_status" xsi:type="string">signifyd_case.guarantee_disposition</item> - </argument> - </arguments> - </virtualType> - <type name="Magento\Signifyd\Model\ResourceModel\CaseEntity"> - <arguments> - <argument name="connectionName" xsi:type="string">sales</argument> - </arguments> - </type> - <virtualType name="PaymentMapperSchemaLocator" type="Magento\Framework\Config\GenericSchemaLocator"> - <arguments> - <argument name="moduleName" xsi:type="string">Magento_Signifyd</argument> - <argument name="schema" xsi:type="string">signifyd_payment_mapping.xsd</argument> - </arguments> - </virtualType> - <virtualType name="PaymentMapperConfigReader" type="Magento\Framework\Config\Reader\Filesystem"> - <arguments> - <argument name="converter" xsi:type="object">Magento\Signifyd\Model\PaymentMethodMapper\XmlToArrayConfigConverter</argument> - <argument name="schemaLocator" xsi:type="object">PaymentMapperSchemaLocator</argument> - <argument name="fileName" xsi:type="string">signifyd_payment_mapping.xml</argument> - <argument name="idAttributes" xsi:type="array"> - <item name="/config/payment_method_list/payment_method" xsi:type="string">name</item> - </argument> - </arguments> - </virtualType> - <virtualType name="PaymentMethodConfigData" type="Magento\Framework\Config\Data"> - <arguments> - <argument name="reader" xsi:type="object">PaymentMapperConfigReader</argument> - <argument name="cacheId" xsi:type="string">signifyd_payment_list_cache</argument> - </arguments> - </virtualType> - <type name="Magento\Signifyd\Model\PaymentMethodMapper\PaymentMethodMapper"> - <arguments> - <argument name="paymentMapping" xsi:type="object">PaymentMethodConfigData</argument> - </arguments> - </type> - <type name="Magento\Config\Model\Config\TypePool"> - <arguments> - <argument name="sensitive" xsi:type="array"> - <item name="fraud_protection/signifyd/api_key" xsi:type="string">1</item> - <item name="fraud_protection/signifyd/api_url" xsi:type="string">1</item> - </argument> - <argument name="environment" xsi:type="array"> - <item name="fraud_protection/signifyd/api_key" xsi:type="string">1</item> - <item name="fraud_protection/signifyd/api_url" xsi:type="string">1</item> - </argument> - </arguments> - </type> - <type name="Magento\Sales\Model\Order"> - <plugin name="cancelGuaranteeAfterOrderCancel" type="Magento\Signifyd\Plugin\OrderPlugin" /> - </type> - <type name="Magento\Payment\Model\MethodInterface"> - <plugin name="cancelGuaranteeAfterPaymentDeny" type="Magento\Signifyd\Plugin\PaymentPlugin" /> - </type> -</config> diff --git a/app/code/Magento/Signifyd/etc/events.xml b/app/code/Magento/Signifyd/etc/events.xml deleted file mode 100644 index d44665f9fb97b..0000000000000 --- a/app/code/Magento/Signifyd/etc/events.xml +++ /dev/null @@ -1,18 +0,0 @@ -<?xml version="1.0"?> -<!-- -/** - * Copyright © Magento, Inc. All rights reserved. - * See COPYING.txt for license details. - */ ---> -<config xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="urn:magento:framework:Event/etc/events.xsd"> - <event name="checkout_submit_all_after"> - <observer name="signifyd_place_order_observer" instance="Magento\Signifyd\Observer\PlaceOrder" /> - </event> - <event name="paypal_checkout_success"> - <observer name="signifyd_place_order_checkout_success_observer" instance="Magento\Signifyd\Observer\PlaceOrder" /> - </event> - <event name="checkout_onepage_controller_success_action"> - <observer name="signifyd_place_order_checkout_success_observer" instance="Magento\Signifyd\Observer\PlaceOrder" /> - </event> -</config> diff --git a/app/code/Magento/Signifyd/etc/frontend/di.xml b/app/code/Magento/Signifyd/etc/frontend/di.xml deleted file mode 100644 index 08a690d1a9930..0000000000000 --- a/app/code/Magento/Signifyd/etc/frontend/di.xml +++ /dev/null @@ -1,10 +0,0 @@ -<?xml version="1.0"?> -<!-- -/** - * Copyright © Magento, Inc. All rights reserved. - * See COPYING.txt for license details. - */ ---> -<config xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="urn:magento:framework:ObjectManager/etc/config.xsd"> - <preference for="Magento\Signifyd\Model\QuoteSession\QuoteSessionInterface" type="Magento\Signifyd\Model\QuoteSession\FrontendSession" /> -</config> diff --git a/app/code/Magento/Signifyd/etc/frontend/routes.xml b/app/code/Magento/Signifyd/etc/frontend/routes.xml deleted file mode 100644 index 5803f59d8624b..0000000000000 --- a/app/code/Magento/Signifyd/etc/frontend/routes.xml +++ /dev/null @@ -1,14 +0,0 @@ -<?xml version="1.0"?> -<!-- -/** - * Copyright © Magento, Inc. All rights reserved. - * See COPYING.txt for license details. - */ ---> -<config xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="urn:magento:framework:App/etc/routes.xsd"> - <router id="standard"> - <route id="signifyd" frontName="signifyd"> - <module name="Magento_Signifyd" /> - </route> - </router> -</config> diff --git a/app/code/Magento/Signifyd/etc/module.xml b/app/code/Magento/Signifyd/etc/module.xml deleted file mode 100644 index 264f295e8c528..0000000000000 --- a/app/code/Magento/Signifyd/etc/module.xml +++ /dev/null @@ -1,20 +0,0 @@ -<?xml version="1.0"?> -<!-- -/** - * Copyright © Magento, Inc. All rights reserved. - * See COPYING.txt for license details. - */ ---> -<config xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="urn:magento:framework:Module/etc/module.xsd"> - <module name="Magento_Signifyd" > - <sequence> - <module name="Magento_Sales" /> - <module name="Magento_Store" /> - <module name="Magento_Customer" /> - <module name="Magento_Directory" /> - <module name="Magento_Checkout" /> - <module name="Magento_Backend" /> - <module name="Magento_Payment" /> - </sequence> - </module> -</config> diff --git a/app/code/Magento/Signifyd/etc/signifyd_payment_mapping.xml b/app/code/Magento/Signifyd/etc/signifyd_payment_mapping.xml deleted file mode 100644 index 9ff952d04925d..0000000000000 --- a/app/code/Magento/Signifyd/etc/signifyd_payment_mapping.xml +++ /dev/null @@ -1,82 +0,0 @@ -<?xml version="1.0" ?> -<!-- -/** - * Copyright © Magento, Inc. All rights reserved. - * See COPYING.txt for license details. - */ - /** - * Custom payment method might adds a block in payment_method_list e.g. - * <payment_method name="custom_payment_method"> - * <magento_code>custom_payment_method</magento_code> - * <signifyd_code>PAYMENT_CARD</signifyd_code> - * </payment_method> - * Appropriate value for the <signifyd_code> tag from Signifyd documentation: - * @see https://www.signifyd.com/docs/api/#/reference/webhooks/manage-single-webhooks/create-a-case - * Create a Case -> Request -> ATTRIBUTES -> paymentMethod - */ ---> -<config xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" - xsi:noNamespaceSchemaLocation="urn:magento:module:Magento_Signifyd:etc/signifyd_payment_mapping.xsd"> - <payment_method_list> - <payment_method name="braintree"> - <magento_code>braintree</magento_code> - <signifyd_code>PAYMENT_CARD</signifyd_code> - </payment_method> - <payment_method name="braintree_paypal"> - <magento_code>braintree_paypal</magento_code> - <signifyd_code>PAYPAL_ACCOUNT</signifyd_code> - </payment_method> - <payment_method name="paypal_express"> - <magento_code>paypal_express</magento_code> - <signifyd_code>PAYPAL_ACCOUNT</signifyd_code> - </payment_method> - <payment_method name="paypal_express_bml"> - <magento_code>paypal_express_bml</magento_code> - <signifyd_code>PAYPAL_ACCOUNT</signifyd_code> - </payment_method> - <payment_method name="payflow_express"> - <magento_code>payflow_express</magento_code> - <signifyd_code>PAYPAL_ACCOUNT</signifyd_code> - </payment_method> - <payment_method name="payflow_express_bml"> - <magento_code>payflow_express_bml</magento_code> - <signifyd_code>PAYPAL_ACCOUNT</signifyd_code> - </payment_method> - <payment_method name="payflowpro"> - <magento_code>payflowpro</magento_code> - <signifyd_code>PAYMENT_CARD</signifyd_code> - </payment_method> - <payment_method name="payflow_link"> - <magento_code>payflow_link</magento_code> - <signifyd_code>PAYMENT_CARD</signifyd_code> - </payment_method> - <payment_method name="payflow_advanced"> - <magento_code>payflow_advanced</magento_code> - <signifyd_code>PAYMENT_CARD</signifyd_code> - </payment_method> - <payment_method name="hosted_pro"> - <magento_code>hosted_pro</magento_code> - <signifyd_code>PAYMENT_CARD</signifyd_code> - </payment_method> - <payment_method name="authorizenet_directpost"> - <magento_code>authorizenet_directpost</magento_code> - <signifyd_code>PAYMENT_CARD</signifyd_code> - </payment_method> - <payment_method name="worldpay"> - <magento_code>worldpay</magento_code> - <signifyd_code>PAYMENT_CARD</signifyd_code> - </payment_method> - <payment_method name="eway"> - <magento_code>eway</magento_code> - <signifyd_code>PAYMENT_CARD</signifyd_code> - </payment_method> - <payment_method name="cybersource"> - <magento_code>cybersource</magento_code> - <signifyd_code>PAYMENT_CARD</signifyd_code> - </payment_method> - <payment_method name="free"> - <magento_code>free</magento_code> - <signifyd_code>FREE</signifyd_code> - </payment_method> - </payment_method_list> -</config> diff --git a/app/code/Magento/Signifyd/etc/signifyd_payment_mapping.xsd b/app/code/Magento/Signifyd/etc/signifyd_payment_mapping.xsd deleted file mode 100644 index bb3b3036d0c9c..0000000000000 --- a/app/code/Magento/Signifyd/etc/signifyd_payment_mapping.xsd +++ /dev/null @@ -1,34 +0,0 @@ -<?xml version="1.0" ?> -<!-- -/** - * Copyright © Magento, Inc. All rights reserved. - * See COPYING.txt for license details. - */ ---> -<xs:schema xmlns:xs="http://www.w3.org/2001/XMLSchema"> - <xs:element name="config"> - <xs:complexType> - <xs:sequence> - <xs:element minOccurs="1" maxOccurs="unbounded" name="payment_method_list" type="payment_method_list"/> - </xs:sequence> - </xs:complexType> - </xs:element> - <xs:complexType name="payment_method_list"> - <xs:sequence> - <xs:element minOccurs="1" maxOccurs="unbounded" name="payment_method" type="payment_method"/> - </xs:sequence> - </xs:complexType> - <xs:complexType name="payment_method"> - <xs:sequence> - <xs:element minOccurs="1" name="magento_code"/> - <xs:element minOccurs="1" name="signifyd_code"/> - </xs:sequence> - <xs:attribute name="name" type="xs:string" use="optional"> - <xs:annotation> - <xs:documentation> - Element's unique identifier. - </xs:documentation> - </xs:annotation> - </xs:attribute> - </xs:complexType> -</xs:schema> diff --git a/app/code/Magento/Signifyd/i18n/en_US.csv b/app/code/Magento/Signifyd/i18n/en_US.csv deleted file mode 100644 index 40772188dac9e..0000000000000 --- a/app/code/Magento/Signifyd/i18n/en_US.csv +++ /dev/null @@ -1,46 +0,0 @@ -OPEN,Open -PROCESSING,Processing -FLAGGED,Flagged -DISMISSED,Dismissed -HELD,Held -GOOD,Good -FRAUDULENT,Fraudulent -UNSET,Unset -NULL,Unset -APPROVED,Approved -DECLINED,Declined -PENDING,Pending -CANCELED,Canceled -IN_REVIEW,"In review" -Approved,Approved -Declined,Declined -Pending,Pending -Canceled,Canceled -"In Review","In Review" -Unrequested,Unrequested -"Learn more","Learn more" -"This order already has associated case entity","This order already has associated case entity" -"The case entity should not be empty.","The case entity should not be empty." -"Cannot update Case entity.","Cannot update Case entity." -"The ""%1"" should not be empty.","The ""%1"" should not be empty." -"Case entity not found.","Case entity not found." -"Case Update: New score for the order is %1. Previous score was %2.","Case Update: New score for the order is %1. Previous score was %2." -"Awaiting the Signifyd guarantee disposition.","Awaiting the Signifyd guarantee disposition." -"Only single entrance of ""%1"" node is required.","Only single entrance of ""%1"" node is required." -"Not empty value for ""%1"" node is required.","Not empty value for ""%1"" node is required." -"%1 must implement %2","%1 must implement %2" -Error,Error -"Cannot generate message.","Cannot generate message." -"Message is generated.","Message is generated." -"Case with the same order id already exists.","Case with the same order id already exists." -"Fraud Protection Information","Fraud Protection Information" -"Signifyd Guarantee Decision","Signifyd Guarantee Decision" -"Fraud Protection Section","Fraud Protection Section" -"Fraud Protection","Fraud Protection" -"Protect your store from fraud with Guaranteed Fraud Protection by Signifyd.","Protect your store from fraud with Guaranteed Fraud Protection by Signifyd." -Configuration,Configuration -"Enable this Solution","Enable this Solution" -"API Key","API Key" -"API URL","API URL" -Debug,Debug -"Webhook URL","Webhook URL" diff --git a/app/code/Magento/Signifyd/registration.php b/app/code/Magento/Signifyd/registration.php deleted file mode 100644 index 72b11f7eac214..0000000000000 --- a/app/code/Magento/Signifyd/registration.php +++ /dev/null @@ -1,9 +0,0 @@ -<?php -/** - * Copyright © Magento, Inc. All rights reserved. - * See COPYING.txt for license details. - */ - -use \Magento\Framework\Component\ComponentRegistrar; - -ComponentRegistrar::register(ComponentRegistrar::MODULE, 'Magento_Signifyd', __DIR__); diff --git a/app/code/Magento/Signifyd/view/adminhtml/layout/sales_order_view.xml b/app/code/Magento/Signifyd/view/adminhtml/layout/sales_order_view.xml deleted file mode 100644 index ad62b2c6e01cf..0000000000000 --- a/app/code/Magento/Signifyd/view/adminhtml/layout/sales_order_view.xml +++ /dev/null @@ -1,14 +0,0 @@ -<?xml version="1.0"?> -<!-- -/** - * Copyright © Magento, Inc. All rights reserved. - * See COPYING.txt for license details. - */ ---> -<page xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="urn:magento:framework:View/Layout/etc/page_configuration.xsd"> - <body> - <referenceBlock name="order_additional_info"> - <block class="Magento\Signifyd\Block\Adminhtml\CaseInfo" name="order_case_info" template="Magento_Signifyd::case_info.phtml"/> - </referenceBlock> - </body> -</page> diff --git a/app/code/Magento/Signifyd/view/adminhtml/templates/case_info.phtml b/app/code/Magento/Signifyd/view/adminhtml/templates/case_info.phtml deleted file mode 100644 index 07f1b2c2e4ae6..0000000000000 --- a/app/code/Magento/Signifyd/view/adminhtml/templates/case_info.phtml +++ /dev/null @@ -1,31 +0,0 @@ -<?php -/** - * Copyright © Magento, Inc. All rights reserved. - * See COPYING.txt for license details. - */ -?> -<?php /** @var $block Magento\Signifyd\Block\Adminhtml\CaseInfo */ ?> -<?php -if ($block->isEmptyCase()) { - return ''; -} -?> -<section class="admin__page-section order-case-info"> - <div class="admin__page-section-title"> - <span class="title"><?= $block->escapeHtml(__('Fraud Protection Information')) ?></span> - </div> - <div class="admin__page-section-content"> - <div class="admin__page-section-item case-information"> - <div class="admin__table-wrapper"> - <table class="admin__table-secondary order-case-table"> - <tbody> - <tr> - <th class="col-guarantee-disposition"><?= $block->escapeHtml(__('Signifyd Guarantee Decision')) ?></th> - <td class="col-guarantee-disposition"><?= $block->escapeHtml($block->getCaseGuaranteeDisposition()) ?></td> - </tr> - </tbody> - </table> - </div> - </div> - </div> -</section> diff --git a/app/code/Magento/Signifyd/view/adminhtml/ui_component/sales_order_grid.xml b/app/code/Magento/Signifyd/view/adminhtml/ui_component/sales_order_grid.xml deleted file mode 100644 index 91053d617f31f..0000000000000 --- a/app/code/Magento/Signifyd/view/adminhtml/ui_component/sales_order_grid.xml +++ /dev/null @@ -1,20 +0,0 @@ -<?xml version="1.0" encoding="UTF-8"?> -<!-- - ~ /** - ~ * Copyright © Magento, Inc. All rights reserved. - ~ * See COPYING.txt for license details. - ~ */ - --> -<listing xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="urn:magento:module:Magento_Ui:etc/ui_configuration.xsd"> - <columns name="sales_order_columns"> - <column name="signifyd_guarantee_status" component="Magento_Ui/js/grid/columns/select"> - <settings> - <filter>select</filter> - <options class="Magento\Signifyd\Ui\Component\Listing\Column\Guarantee\Options"/> - <visible>true</visible> - <dataType>select</dataType> - <label translate="true">Signifyd Guarantee Decision</label> - </settings> - </column> - </columns> -</listing> diff --git a/app/code/Magento/Signifyd/view/adminhtml/web/images/logo.png b/app/code/Magento/Signifyd/view/adminhtml/web/images/logo.png deleted file mode 100644 index 7c6645e7c6c93..0000000000000 Binary files a/app/code/Magento/Signifyd/view/adminhtml/web/images/logo.png and /dev/null differ diff --git a/app/code/Magento/Signifyd/view/adminhtml/web/js/request-send.js b/app/code/Magento/Signifyd/view/adminhtml/web/js/request-send.js deleted file mode 100644 index f55170336ca03..0000000000000 --- a/app/code/Magento/Signifyd/view/adminhtml/web/js/request-send.js +++ /dev/null @@ -1,24 +0,0 @@ -/** - * Copyright © Magento, Inc. All rights reserved. - * See COPYING.txt for license details. - */ - -define([ - 'mageUtils', - 'Magento_Ui/js/form/components/button' -], function (utils, Button) { - 'use strict'; - - return Button.extend({ - - /** - * Creates and submits form for Guarantee create/cancel - */ - sendRequest: function () { - utils.submit({ - url: this.requestURL, - data: this.data - }); - } - }); -}); diff --git a/app/code/Magento/Signifyd/view/frontend/layout/checkout_cart_index.xml b/app/code/Magento/Signifyd/view/frontend/layout/checkout_cart_index.xml deleted file mode 100644 index 30472ae5e9ae9..0000000000000 --- a/app/code/Magento/Signifyd/view/frontend/layout/checkout_cart_index.xml +++ /dev/null @@ -1,14 +0,0 @@ -<?xml version="1.0"?> -<!-- -/** - * Copyright © Magento, Inc. All rights reserved. - * See COPYING.txt for license details. - */ ---> -<page xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="urn:magento:framework:View/Layout/etc/page_configuration.xsd"> - <body> - <referenceBlock name="head.components"> - <block class="Magento\Signifyd\Block\Fingerprint" name="signifyd.fingerprint" /> - </referenceBlock> - </body> -</page> diff --git a/app/code/Magento/Signifyd/view/frontend/layout/checkout_index_index.xml b/app/code/Magento/Signifyd/view/frontend/layout/checkout_index_index.xml deleted file mode 100644 index 30472ae5e9ae9..0000000000000 --- a/app/code/Magento/Signifyd/view/frontend/layout/checkout_index_index.xml +++ /dev/null @@ -1,14 +0,0 @@ -<?xml version="1.0"?> -<!-- -/** - * Copyright © Magento, Inc. All rights reserved. - * See COPYING.txt for license details. - */ ---> -<page xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="urn:magento:framework:View/Layout/etc/page_configuration.xsd"> - <body> - <referenceBlock name="head.components"> - <block class="Magento\Signifyd\Block\Fingerprint" name="signifyd.fingerprint" /> - </referenceBlock> - </body> -</page> diff --git a/app/code/Magento/Signifyd/view/frontend/templates/fingerprint.phtml b/app/code/Magento/Signifyd/view/frontend/templates/fingerprint.phtml deleted file mode 100644 index 657043b895f60..0000000000000 --- a/app/code/Magento/Signifyd/view/frontend/templates/fingerprint.phtml +++ /dev/null @@ -1,16 +0,0 @@ -<?php -/** - * Copyright © Magento, Inc. All rights reserved. - * See COPYING.txt for license details. - */ - -/** @var $block Magento\Signifyd\Block\Fingerprint */ -?> -<?php if ($block->isModuleActive()) : ?> - <script - async - id="sig-api" - data-order-session-id="<?= $block->escapeHtml($block->getSignifydOrderSessionId()) ?>" - src="https://cdn-scripts.signifyd.com/api/script-tag.js"> - </script> -<?php endif; ?> diff --git a/app/code/Magento/Sitemap/Block/Adminhtml/Grid/Renderer/Link.php b/app/code/Magento/Sitemap/Block/Adminhtml/Grid/Renderer/Link.php index b4e54104bdfb4..5779caf529e33 100644 --- a/app/code/Magento/Sitemap/Block/Adminhtml/Grid/Renderer/Link.php +++ b/app/code/Magento/Sitemap/Block/Adminhtml/Grid/Renderer/Link.php @@ -3,49 +3,55 @@ * Copyright © Magento, Inc. All rights reserved. * See COPYING.txt for license details. */ +declare(strict_types=1); + namespace Magento\Sitemap\Block\Adminhtml\Grid\Renderer; -use Magento\Framework\App\Filesystem\DirectoryList; +use Magento\Backend\Block\Context; +use Magento\Backend\Block\Widget\Grid\Column\Renderer\AbstractRenderer; use Magento\Config\Model\Config\Reader\Source\Deployed\DocumentRoot; -use Magento\Framework\App\ObjectManager; +use Magento\Framework\DataObject; +use Magento\Framework\Filesystem; +use Magento\Sitemap\Model\Sitemap; +use Magento\Sitemap\Model\SitemapFactory; /** * Sitemap grid link column renderer */ -class Link extends \Magento\Backend\Block\Widget\Grid\Column\Renderer\AbstractRenderer +class Link extends AbstractRenderer { /** - * @var \Magento\Framework\Filesystem $filesystem + * @var Filesystem */ - protected $_filesystem; + private $filesystem; /** - * @var \Magento\Sitemap\Model\SitemapFactory + * @var SitemapFactory */ - protected $_sitemapFactory; + private $sitemapFactory; /** * @var DocumentRoot */ - protected $documentRoot; + private $documentRoot; /** - * @param \Magento\Backend\Block\Context $context - * @param \Magento\Sitemap\Model\SitemapFactory $sitemapFactory - * @param \Magento\Framework\Filesystem $filesystem - * @param array $data + * @param Context $context + * @param SitemapFactory $sitemapFactory + * @param Filesystem $filesystem * @param DocumentRoot $documentRoot + * @param array $data */ public function __construct( - \Magento\Backend\Block\Context $context, - \Magento\Sitemap\Model\SitemapFactory $sitemapFactory, - \Magento\Framework\Filesystem $filesystem, - array $data = [], - DocumentRoot $documentRoot = null + Context $context, + SitemapFactory $sitemapFactory, + Filesystem $filesystem, + DocumentRoot $documentRoot, + array $data = [] ) { - $this->_sitemapFactory = $sitemapFactory; - $this->_filesystem = $filesystem; - $this->documentRoot = $documentRoot ?: ObjectManager::getInstance()->get(DocumentRoot::class); + $this->sitemapFactory = $sitemapFactory; + $this->filesystem = $filesystem; + $this->documentRoot = $documentRoot; parent::__construct($context, $data); } @@ -53,19 +59,20 @@ public function __construct( /** * Prepare link to display in grid * - * @param \Magento\Framework\DataObject $row + * @param DataObject $row + * * @return string */ - public function render(\Magento\Framework\DataObject $row) + public function render(DataObject $row) { - /** @var $sitemap \Magento\Sitemap\Model\Sitemap */ - $sitemap = $this->_sitemapFactory->create(); + /** @var $sitemap Sitemap */ + $sitemap = $this->sitemapFactory->create(); $sitemap->setStoreId($row->getStoreId()); - $url = $this->escapeHtml($sitemap->getSitemapUrl($row->getSitemapPath(), $row->getSitemapFilename())); + $url = $this->_escaper->escapeHtml($sitemap->getSitemapUrl($row->getSitemapPath(), $row->getSitemapFilename())); $fileName = preg_replace('/^\//', '', $row->getSitemapPath() . $row->getSitemapFilename()); $documentRootPath = $this->documentRoot->getPath(); - $directory = $this->_filesystem->getDirectoryRead($documentRootPath); + $directory = $this->filesystem->getDirectoryRead($documentRootPath); if ($directory->isFile($fileName)) { return sprintf('<a href="%1$s">%1$s</a>', $url); } diff --git a/app/code/Magento/Sitemap/Controller/Adminhtml/Sitemap/Delete.php b/app/code/Magento/Sitemap/Controller/Adminhtml/Sitemap/Delete.php index 1c807cbfc194e..560797cfc7453 100644 --- a/app/code/Magento/Sitemap/Controller/Adminhtml/Sitemap/Delete.php +++ b/app/code/Magento/Sitemap/Controller/Adminhtml/Sitemap/Delete.php @@ -3,40 +3,45 @@ * Copyright © Magento, Inc. All rights reserved. * See COPYING.txt for license details. */ +declare(strict_types=1); + namespace Magento\Sitemap\Controller\Adminhtml\Sitemap; +use Magento\Backend\App\Action\Context; use Magento\Framework\App\Action\HttpPostActionInterface; use Magento\Framework\App\Filesystem\DirectoryList; -use Magento\Framework\App\ObjectManager; +use Magento\Framework\Filesystem; +use Magento\Sitemap\Controller\Adminhtml\Sitemap; +use Magento\Sitemap\Model\SitemapFactory; /** * Controller class Delete. Represents adminhtml request flow for a sitemap deletion */ -class Delete extends \Magento\Sitemap\Controller\Adminhtml\Sitemap implements HttpPostActionInterface +class Delete extends Sitemap implements HttpPostActionInterface { /** - * @var \Magento\Framework\Filesystem + * @var SitemapFactory */ - private $filesystem; + private $sitemapFactory; /** - * @var \Magento\Sitemap\Model\SitemapFactory + * @var Filesystem */ - private $sitemapFactory; + private $filesystem; /** - * Constructor - * - * @param \Magento\Backend\App\Action\Context $context - * @param \Magento\Sitemap\Model\SitemapFactory|null $sitemapFactory + * @param Context $context + * @param SitemapFactory $sitemapFactory + * @param Filesystem $filesystem */ public function __construct( - \Magento\Backend\App\Action\Context $context, - \Magento\Sitemap\Model\SitemapFactory $sitemapFactory = null + Context $context, + SitemapFactory $sitemapFactory, + Filesystem $filesystem ) { parent::__construct($context); - $this->sitemapFactory = $sitemapFactory ?: ObjectManager::getInstance() - ->get(\Magento\Sitemap\Model\SitemapFactory::class); + $this->sitemapFactory = $sitemapFactory; + $this->filesystem = $filesystem; } /** @@ -46,7 +51,7 @@ public function __construct( */ public function execute() { - $directory = $this->getFilesystem()->getDirectoryWrite(DirectoryList::ROOT); + $directory = $this->filesystem->getDirectoryWrite(DirectoryList::ROOT); // check if we know what should be deleted $id = $this->getRequest()->getParam('sitemap_id'); if ($id) { @@ -86,20 +91,4 @@ public function execute() // go to grid $this->_redirect('adminhtml/*/'); } - - /** - * The getter function to get Filesystem object for real application code - * - * @return \Magento\Framework\Filesystem - * @deprecated 100.2.0 - */ - private function getFilesystem() - { - if (null === $this->filesystem) { - $this->filesystem = \Magento\Framework\App\ObjectManager::getInstance()->get( - \Magento\Framework\Filesystem::class - ); - } - return $this->filesystem; - } } diff --git a/app/code/Magento/Sitemap/Controller/Adminhtml/Sitemap/Generate.php b/app/code/Magento/Sitemap/Controller/Adminhtml/Sitemap/Generate.php index 5cfc7349888f3..7d7ba87940fc7 100644 --- a/app/code/Magento/Sitemap/Controller/Adminhtml/Sitemap/Generate.php +++ b/app/code/Magento/Sitemap/Controller/Adminhtml/Sitemap/Generate.php @@ -3,35 +3,36 @@ * Copyright © Magento, Inc. All rights reserved. * See COPYING.txt for license details. */ +declare(strict_types=1); namespace Magento\Sitemap\Controller\Adminhtml\Sitemap; use Magento\Backend\App\Action; use Magento\Framework\App\Action\HttpGetActionInterface; +use Magento\Framework\App\Area; use Magento\Sitemap\Controller\Adminhtml\Sitemap; use Magento\Store\Model\App\Emulation; -use Magento\Framework\App\ObjectManager; /** * Generate sitemap file */ class Generate extends Sitemap implements HttpGetActionInterface { - /** @var \Magento\Store\Model\App\Emulation $appEmulation */ + /** + * @var Emulation + */ private $appEmulation; /** - * Generate constructor. * @param Action\Context $context - * @param \Magento\Store\Model\App\Emulation|null $appEmulation + * @param Emulation $appEmulation */ public function __construct( Action\Context $context, - Emulation $appEmulation = null + Emulation $appEmulation ) { parent::__construct($context); - $this->appEmulation = $appEmulation ?: ObjectManager::getInstance() - ->get(\Magento\Store\Model\App\Emulation::class); + $this->appEmulation = $appEmulation; } /** @@ -51,7 +52,7 @@ public function execute() try { $this->appEmulation->startEnvironmentEmulation( $sitemap->getStoreId(), - \Magento\Framework\App\Area::AREA_FRONTEND, + Area::AREA_FRONTEND, true ); $sitemap->generateXml(); diff --git a/app/code/Magento/Sitemap/Controller/Adminhtml/Sitemap/Save.php b/app/code/Magento/Sitemap/Controller/Adminhtml/Sitemap/Save.php index 5230de0429778..1543fc8df933c 100644 --- a/app/code/Magento/Sitemap/Controller/Adminhtml/Sitemap/Save.php +++ b/app/code/Magento/Sitemap/Controller/Adminhtml/Sitemap/Save.php @@ -3,20 +3,27 @@ * Copyright © Magento, Inc. All rights reserved. * See COPYING.txt for license details. */ +declare(strict_types=1); + namespace Magento\Sitemap\Controller\Adminhtml\Sitemap; use Magento\Backend\App\Action\Context; +use Magento\Backend\Model\View\Result\Redirect; use Magento\Framework\App\Action\HttpPostActionInterface; use Magento\Framework\App\Filesystem\DirectoryList; use Magento\Framework\Controller; +use Magento\Framework\Controller\ResultInterface; +use Magento\Framework\Filesystem; use Magento\Framework\Validator\StringLength; use Magento\MediaStorage\Model\File\Validator\AvailablePath; +use Magento\Sitemap\Controller\Adminhtml\Sitemap; +use Magento\Sitemap\Helper\Data; use Magento\Sitemap\Model\SitemapFactory; /** * Save sitemap controller. */ -class Save extends \Magento\Sitemap\Controller\Adminhtml\Sitemap implements HttpPostActionInterface +class Save extends Sitemap implements HttpPostActionInterface { /** * Maximum length of sitemap filename @@ -34,12 +41,12 @@ class Save extends \Magento\Sitemap\Controller\Adminhtml\Sitemap implements Http private $pathValidator; /** - * @var \Magento\Sitemap\Helper\Data + * @var Data */ private $sitemapHelper; /** - * @var \Magento\Framework\Filesystem + * @var Filesystem */ private $filesystem; @@ -53,24 +60,24 @@ class Save extends \Magento\Sitemap\Controller\Adminhtml\Sitemap implements Http * @param Context $context * @param StringLength $stringValidator * @param AvailablePath $pathValidator - * @param \Magento\Sitemap\Helper\Data $sitemapHelper - * @param \Magento\Framework\Filesystem $filesystem + * @param Data $sitemapHelper + * @param Filesystem $filesystem * @param SitemapFactory $sitemapFactory */ public function __construct( Context $context, - StringLength $stringValidator = null, - AvailablePath $pathValidator = null, - \Magento\Sitemap\Helper\Data $sitemapHelper = null, - \Magento\Framework\Filesystem $filesystem = null, - SitemapFactory $sitemapFactory = null + StringLength $stringValidator, + AvailablePath $pathValidator, + Data $sitemapHelper, + Filesystem $filesystem, + SitemapFactory $sitemapFactory ) { parent::__construct($context); - $this->stringValidator = $stringValidator ?: $this->_objectManager->get(StringLength::class); - $this->pathValidator = $pathValidator ?: $this->_objectManager->get(AvailablePath::class); - $this->sitemapHelper = $sitemapHelper ?: $this->_objectManager->get(\Magento\Sitemap\Helper\Data::class); - $this->filesystem = $filesystem ?: $this->_objectManager->get(\Magento\Framework\Filesystem::class); - $this->sitemapFactory = $sitemapFactory ?: $this->_objectManager->get(SitemapFactory::class); + $this->stringValidator = $stringValidator; + $this->pathValidator = $pathValidator; + $this->sitemapHelper = $sitemapHelper; + $this->filesystem = $filesystem; + $this->sitemapFactory = $sitemapFactory; } /** @@ -115,11 +122,12 @@ protected function validatePath(array $data) * Clear sitemap * * @param \Magento\Sitemap\Model\Sitemap $model + * * @return void */ protected function clearSiteMap(\Magento\Sitemap\Model\Sitemap $model) { - /** @var \Magento\Framework\Filesystem $directory */ + /** @var Filesystem $directory */ $directory = $this->filesystem->getDirectoryWrite(DirectoryList::ROOT); if ($this->getRequest()->getParam('sitemap_id')) { @@ -169,12 +177,13 @@ protected function saveData($data) * Get result after saving data * * @param string|bool $id - * @return \Magento\Framework\Controller\ResultInterface + * @return ResultInterface */ protected function getResult($id) { - /** @var \Magento\Backend\Model\View\Result\Redirect $resultRedirect */ + /** @var Redirect $resultRedirect */ $resultRedirect = $this->resultFactory->create(Controller\ResultFactory::TYPE_REDIRECT); + if ($id) { // check if 'Save and Continue' if ($this->getRequest()->getParam('back')) { @@ -194,19 +203,20 @@ protected function getResult($id) 'adminhtml/*/edit', ['sitemap_id' => $this->getRequest()->getParam('sitemap_id')] ); + return $resultRedirect; } /** * Save action * - * @return \Magento\Backend\Model\View\Result\Redirect + * @return Redirect */ public function execute() { // check if data sent $data = $this->getRequest()->getPostValue(); - /** @var \Magento\Backend\Model\View\Result\Redirect $resultRedirect */ + /** @var Redirect $resultRedirect */ $resultRedirect = $this->resultFactory->create(Controller\ResultFactory::TYPE_REDIRECT); if ($data) { if (!$this->validatePath($data)) { diff --git a/app/code/Magento/Sitemap/Model/Config/Backend/Robots.php b/app/code/Magento/Sitemap/Model/Config/Backend/Robots.php index 7a6d28259bfed..9b9aa6fb05e5b 100644 --- a/app/code/Magento/Sitemap/Model/Config/Backend/Robots.php +++ b/app/code/Magento/Sitemap/Model/Config/Backend/Robots.php @@ -3,6 +3,8 @@ * Copyright © Magento, Inc. All rights reserved. * See COPYING.txt for license details. */ +declare(strict_types=1); + namespace Magento\Sitemap\Model\Config\Backend; use Magento\Framework\App\Cache\TypeListInterface; @@ -14,7 +16,6 @@ use Magento\Framework\Model\ResourceModel\AbstractResource; use Magento\Framework\Registry; use Magento\Robots\Model\Config\Value as RobotsValue; -use Magento\Store\Model\StoreResolver; use Magento\Store\Model\StoreManagerInterface; /** @@ -41,27 +42,22 @@ class Robots extends Value implements IdentityInterface * @param Registry $registry * @param ScopeConfigInterface $config * @param TypeListInterface $cacheTypeList - * @param StoreResolver $storeResolver - * @param StoreManagerInterface|null $storeManager + * @param StoreManagerInterface $storeManager * @param AbstractResource|null $resource * @param AbstractDb|null $resourceCollection * @param array $data - * - * @SuppressWarnings(PHPMD.UnusedFormalParameter) */ public function __construct( Context $context, Registry $registry, ScopeConfigInterface $config, TypeListInterface $cacheTypeList, - StoreResolver $storeResolver, - StoreManagerInterface $storeManager = null, + StoreManagerInterface $storeManager, AbstractResource $resource = null, AbstractDb $resourceCollection = null, array $data = [] ) { - $this->storeManager = $storeManager ?: \Magento\Framework\App\ObjectManager::getInstance() - ->get(StoreManagerInterface::class); + $this->storeManager = $storeManager; parent::__construct( $context, diff --git a/app/code/Magento/Sitemap/Model/ItemProvider/StoreUrl.php b/app/code/Magento/Sitemap/Model/ItemProvider/StoreUrl.php new file mode 100644 index 0000000000000..11a2d585d8da6 --- /dev/null +++ b/app/code/Magento/Sitemap/Model/ItemProvider/StoreUrl.php @@ -0,0 +1,57 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ + +namespace Magento\Sitemap\Model\ItemProvider; + +use Magento\Sitemap\Model\SitemapItemInterfaceFactory; + +/** + * Class for adding Store Url in sitemap + */ +class StoreUrl implements ItemProviderInterface +{ + /** + * Sitemap item factory + * + * @var SitemapItemInterfaceFactory + */ + private $itemFactory; + + /** + * Config reader + * + * @var ConfigReaderInterface + */ + private $configReader; + + /** + * StoreUrlSitemapItemResolver constructor. + * + * @param ConfigReaderInterface $configReader + * @param SitemapItemInterfaceFactory $itemFactory + */ + public function __construct( + ConfigReaderInterface $configReader, + SitemapItemInterfaceFactory $itemFactory + ) { + $this->itemFactory = $itemFactory; + $this->configReader = $configReader; + } + + /** + * @inheritdoc + */ + public function getItems($storeId) + { + $items[] = $this->itemFactory->create([ + 'url' => '', + 'priority' => $this->configReader->getPriority($storeId), + 'changeFrequency' => $this->configReader->getChangeFrequency($storeId), + ]); + + return $items; + } +} diff --git a/app/code/Magento/Sitemap/Model/ItemProvider/StoreUrlConfigReader.php b/app/code/Magento/Sitemap/Model/ItemProvider/StoreUrlConfigReader.php new file mode 100644 index 0000000000000..b18dd73fb2836 --- /dev/null +++ b/app/code/Magento/Sitemap/Model/ItemProvider/StoreUrlConfigReader.php @@ -0,0 +1,64 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ + +namespace Magento\Sitemap\Model\ItemProvider; + +use Magento\Framework\App\Config\ScopeConfigInterface; +use Magento\Store\Model\ScopeInterface; + +/** + * Class for getting configuration for Store Url + */ +class StoreUrlConfigReader implements ConfigReaderInterface +{ + /**#@+ + * Xpath config settings + */ + const XML_PATH_CHANGE_FREQUENCY = 'sitemap/store/changefreq'; + const XML_PATH_PRIORITY = 'sitemap/store/priority'; + /**#@-*/ + + /** + * Scope config + * + * @var ScopeConfigInterface + */ + private $scopeConfig; + + /** + * CategoryItemResolverConfigReader constructor. + * + * @param ScopeConfigInterface $scopeConfig + */ + public function __construct(ScopeConfigInterface $scopeConfig) + { + $this->scopeConfig = $scopeConfig; + } + + /** + * @inheritdoc + */ + public function getPriority($storeId) + { + return (string)$this->scopeConfig->getValue( + self::XML_PATH_PRIORITY, + ScopeInterface::SCOPE_STORE, + $storeId + ); + } + + /** + * @inheritdoc + */ + public function getChangeFrequency($storeId) + { + return (string)$this->scopeConfig->getValue( + self::XML_PATH_CHANGE_FREQUENCY, + ScopeInterface::SCOPE_STORE, + $storeId + ); + } +} diff --git a/app/code/Magento/Sitemap/Model/ResourceModel/Catalog/Product.php b/app/code/Magento/Sitemap/Model/ResourceModel/Catalog/Product.php index 23a1c0fd09b7b..8b2154e6ee47a 100644 --- a/app/code/Magento/Sitemap/Model/ResourceModel/Catalog/Product.php +++ b/app/code/Magento/Sitemap/Model/ResourceModel/Catalog/Product.php @@ -291,7 +291,7 @@ protected function _getAttribute($attributeCode) } /** - * Get category collection array + * Get product collection array * * @param null|string|bool|int|Store $storeId * diff --git a/app/code/Magento/Sitemap/Test/Mftf/Test/AdminMarketingSiteMapCreateNewTest.xml b/app/code/Magento/Sitemap/Test/Mftf/Test/AdminMarketingSiteMapCreateNewTest.xml index 57d8f8c75d23d..01c8ab5658186 100644 --- a/app/code/Magento/Sitemap/Test/Mftf/Test/AdminMarketingSiteMapCreateNewTest.xml +++ b/app/code/Magento/Sitemap/Test/Mftf/Test/AdminMarketingSiteMapCreateNewTest.xml @@ -18,14 +18,14 @@ <group value="sitemap"/> </annotations> <before> - <actionGroup ref="LoginAsAdmin" stepKey="LoginAsAdmin"/> + <actionGroup ref="AdminLoginActionGroup" stepKey="LoginAsAdmin"/> </before> <after> <actionGroup ref="AdminMarketingSiteDeleteByNameActionGroup" stepKey="deleteSiteMap"> <argument name="filename" value="{{DefaultSiteMap.filename}}" /> </actionGroup> <actionGroup ref="AssertSiteMapDeleteSuccessActionGroup" stepKey="assertDeleteSuccessMessage"/> - <actionGroup ref="logout" stepKey="logout"/> + <actionGroup ref="AdminLogoutActionGroup" stepKey="logout"/> </after> <actionGroup ref="AdminMarketingSiteMapNavigateNewActionGroup" stepKey="navigateNewSiteMap"/> <actionGroup ref="AdminMarketingSiteMapFillFormActionGroup" stepKey="fillSiteMapForm"> diff --git a/app/code/Magento/Sitemap/Test/Mftf/Test/AdminMarketingSiteMapNavigateMenuTest.xml b/app/code/Magento/Sitemap/Test/Mftf/Test/AdminMarketingSiteMapNavigateMenuTest.xml index 54543fab8649d..2a9b17ad49bff 100644 --- a/app/code/Magento/Sitemap/Test/Mftf/Test/AdminMarketingSiteMapNavigateMenuTest.xml +++ b/app/code/Magento/Sitemap/Test/Mftf/Test/AdminMarketingSiteMapNavigateMenuTest.xml @@ -20,10 +20,10 @@ <group value="mtf_migrated"/> </annotations> <before> - <actionGroup ref="LoginAsAdmin" stepKey="LoginAsAdmin"/> + <actionGroup ref="AdminLoginActionGroup" stepKey="LoginAsAdmin"/> </before> <after> - <actionGroup ref="logout" stepKey="logout"/> + <actionGroup ref="AdminLogoutActionGroup" stepKey="logout"/> </after> <actionGroup ref="AdminNavigateMenuActionGroup" stepKey="navigateToMarketingSiteMapPage"> <argument name="menuUiId" value="{{AdminMenuMarketing.dataUiId}}"/> diff --git a/app/code/Magento/Sitemap/Test/Unit/Model/Config/Backend/PriorityTest.php b/app/code/Magento/Sitemap/Test/Unit/Model/Config/Backend/PriorityTest.php new file mode 100644 index 0000000000000..b819dfd343806 --- /dev/null +++ b/app/code/Magento/Sitemap/Test/Unit/Model/Config/Backend/PriorityTest.php @@ -0,0 +1,89 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ + +namespace Magento\Sitemap\Test\Unit\Model\Config\Backend; + +use Magento\Sitemap\Model\Config\Backend\Priority; +use PHPUnit\Framework\MockObject\MockObject; + +/** + * Tests for @see Priority + */ +class PriorityTest extends \PHPUnit\Framework\TestCase +{ + /** + * @var Priority|MockObject + */ + private $priorityMock; + + /** + * @inheritDoc + */ + protected function setUp() + { + $this->priorityMock = $this->getMockBuilder(Priority::class) + ->disableOriginalConstructor() + ->setMethods(['getValue']) + ->getMock(); + } + + /** + * Verify before save in chainable + * + * @param string $value + * @dataProvider dataProviderTestBeforeSaveValueCorrect + */ + public function testBeforeSaveIsChainable($value) + { + $this->priorityMock->expects($this->once()) + ->method('getValue') + ->willReturn($value); + + $this->assertSame($this->priorityMock, $this->priorityMock->beforeSave()); + } + + /** + * Verify before save value out of range + * + * @param string $value + * @dataProvider dataProviderTestBeforeSaveValueOutOfRange + */ + public function testBeforeSaveValueOutOfRange($value) + { + $this->priorityMock->expects($this->once()) + ->method('getValue') + ->willReturn($value); + + $this->expectException(\Exception::class); + $this->expectExceptionMessage('The priority must be between 0 and 1.'); + + $this->priorityMock->beforeSave(); + } + + /** + * Data provider + * + * @return array + */ + public function dataProviderTestBeforeSaveValueCorrect() + { + return [ + ['0'], ['0.0'], ['0.5'], ['1'] + ]; + } + + /** + * Data provider + * + * @return array + */ + public function dataProviderTestBeforeSaveValueOutOfRange() + { + return [ + ['-1'], ['2'], ['nan'] + ]; + } +} diff --git a/app/code/Magento/Sitemap/Test/Unit/Model/Config/Backend/RobotsTest.php b/app/code/Magento/Sitemap/Test/Unit/Model/Config/Backend/RobotsTest.php index cbf353d0a93c7..2ba83aa0ec14f 100644 --- a/app/code/Magento/Sitemap/Test/Unit/Model/Config/Backend/RobotsTest.php +++ b/app/code/Magento/Sitemap/Test/Unit/Model/Config/Backend/RobotsTest.php @@ -3,75 +3,83 @@ * Copyright © Magento, Inc. All rights reserved. * See COPYING.txt for license details. */ +declare(strict_types=1); + namespace Magento\Sitemap\Test\Unit\Model\Config\Backend; -class RobotsTest extends \PHPUnit\Framework\TestCase +use Magento\Framework\App\Cache\TypeListInterface; +use Magento\Framework\App\Config\ScopeConfigInterface; +use Magento\Framework\Model\Context; +use Magento\Framework\Registry; +use Magento\Framework\TestFramework\Unit\Helper\ObjectManager; +use Magento\Robots\Model\Config\Value; +use Magento\Sitemap\Model\Config\Backend\Robots; +use Magento\Store\Api\Data\StoreInterface; +use Magento\Store\Model\StoreManagerInterface; +use PHPUnit\Framework\MockObject\MockObject; +use PHPUnit\Framework\TestCase; + +class RobotsTest extends TestCase { /** - * @var \Magento\Sitemap\Model\Config\Backend\Robots + * @var Robots */ private $model; /** - * @var \Magento\Framework\Model\Context|\PHPUnit_Framework_MockObject_MockObject - */ - private $context; - - /** - * @var \Magento\Framework\Registry|\PHPUnit_Framework_MockObject_MockObject + * @var Context|MockObject */ - private $registry; + private $contextMock; /** - * @var \Magento\Framework\App\Config\ScopeConfigInterface|\PHPUnit_Framework_MockObject_MockObject + * @var Registry|MockObject */ - private $scopeConfig; + private $registryMock; /** - * @var \Magento\Framework\App\Cache\TypeListInterface|\PHPUnit_Framework_MockObject_MockObject + * @var ScopeConfigInterface|MockObject */ - private $typeList; + private $scopeConfigMock; /** - * @var \Magento\Store\Model\StoreResolver|\PHPUnit_Framework_MockObject_MockObject + * @var TypeListInterface|MockObject */ - private $storeResolver; + private $cacheTypeListMock; /** - * @var \Magento\Store\Model\StoreManagerInterface|\PHPUnit_Framework_MockObject_MockObject + * @var StoreManagerInterface|MockObject */ - private $storeManager; + private $storeManagerMock; protected function setUp() { - $this->context = $this->getMockBuilder(\Magento\Framework\Model\Context::class) + $this->contextMock = $this->getMockBuilder(Context::class) ->disableOriginalConstructor() ->getMock(); - $this->registry = $this->getMockBuilder(\Magento\Framework\Registry::class) + $this->registryMock = $this->getMockBuilder(Registry::class) ->disableOriginalConstructor() ->getMock(); - $this->scopeConfig = $this->getMockBuilder(\Magento\Framework\App\Config\ScopeConfigInterface::class) + $this->scopeConfigMock = $this->getMockBuilder(ScopeConfigInterface::class) ->getMockForAbstractClass(); - $this->typeList = $this->getMockBuilder(\Magento\Framework\App\Cache\TypeListInterface::class) + $this->cacheTypeListMock = $this->getMockBuilder(TypeListInterface::class) ->getMockForAbstractClass(); - $this->storeResolver = $this->getMockBuilder(\Magento\Store\Model\StoreResolver::class) - ->disableOriginalConstructor() - ->getMock(); - - $this->storeManager = $this->getMockBuilder(\Magento\Store\Model\StoreManagerInterface::class) + $this->storeManagerMock = $this->getMockBuilder(StoreManagerInterface::class) ->getMockForAbstractClass(); - $this->model = new \Magento\Sitemap\Model\Config\Backend\Robots( - $this->context, - $this->registry, - $this->scopeConfig, - $this->typeList, - $this->storeResolver, - $this->storeManager + $objectManager = new ObjectManager($this); + $this->model = $objectManager->getObject( + Robots::class, + [ + 'context' => $this->contextMock, + 'registry' => $this->registryMock, + 'config' => $this->scopeConfigMock, + 'cacheTypeList' => $this->cacheTypeListMock, + 'storeManager' => $this->storeManagerMock + ] ); } @@ -82,9 +90,9 @@ public function testGetIdentities() { $storeId = 1; - $storeMock = $this->getMockBuilder(\Magento\Store\Api\Data\StoreInterface::class)->getMock(); + $storeMock = $this->getMockBuilder(StoreInterface::class)->getMock(); - $this->storeManager->expects($this->once()) + $this->storeManagerMock->expects($this->once()) ->method('getStore') ->willReturn($storeMock); @@ -93,7 +101,7 @@ public function testGetIdentities() ->willReturn($storeId); $expected = [ - \Magento\Robots\Model\Config\Value::CACHE_TAG . '_' . $storeId, + Value::CACHE_TAG . '_' . $storeId, ]; $this->assertEquals($expected, $this->model->getIdentities()); } diff --git a/app/code/Magento/Sitemap/Test/Unit/Model/ItemProvider/StoreUrlTest.php b/app/code/Magento/Sitemap/Test/Unit/Model/ItemProvider/StoreUrlTest.php new file mode 100644 index 0000000000000..0edfd253f0c1d --- /dev/null +++ b/app/code/Magento/Sitemap/Test/Unit/Model/ItemProvider/StoreUrlTest.php @@ -0,0 +1,72 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ + +namespace Magento\Sitemap\Test\Unit\Model\ItemProvider; + +use Magento\Framework\TestFramework\Unit\Helper\ObjectManager; +use Magento\Sitemap\Model\ItemProvider\ConfigReaderInterface; +use Magento\Sitemap\Model\ItemProvider\StoreUrl as StoreUrlItemResolver; +use Magento\Sitemap\Model\SitemapItem; +use Magento\Sitemap\Model\SitemapItemInterfaceFactory; +use PHPUnit\Framework\MockObject\MockObject; +use PHPUnit\Framework\TestCase; + +class StoreUrlTest extends TestCase +{ + /** + * test for getItems method + */ + public function testGetItems() + { + $configReaderMock = $this->getConfigReaderMock(); + $itemFactoryMock = $this->getItemFactoryMock(); + $resolver = new StoreUrlItemResolver($configReaderMock, $itemFactoryMock); + $items = $resolver->getItems(1); + + $this->assertCount(1, $items); + foreach ($items as $item) { + $this->assertSame('daily', $item->getChangeFrequency()); + $this->assertSame('1.0', $item->getPriority()); + } + } + + /** + * @return SitemapItemInterfaceFactory|MockObject + */ + private function getItemFactoryMock() + { + $itemFactoryMock = $this->getMockBuilder(SitemapItemInterfaceFactory::class) + ->setMethods(['create']) + ->disableOriginalConstructor() + ->getMock(); + + $itemFactoryMock->expects($this->any()) + ->method('create') + ->willReturnCallback(function ($data) { + $helper = new ObjectManager($this); + + return $helper->getObject(SitemapItem::class, $data); + }); + + return $itemFactoryMock; + } + + /** + * @return ConfigReaderInterface|MockObject + */ + private function getConfigReaderMock() + { + $configReaderMock = $this->getMockForAbstractClass(ConfigReaderInterface::class); + $configReaderMock->expects($this->any()) + ->method('getPriority') + ->willReturn('1.0'); + $configReaderMock->expects($this->any()) + ->method('getChangeFrequency') + ->willReturn('daily'); + + return $configReaderMock; + } +} diff --git a/app/code/Magento/Sitemap/etc/adminhtml/system.xml b/app/code/Magento/Sitemap/etc/adminhtml/system.xml index c3c9c85027354..8814d7dcf67bd 100644 --- a/app/code/Magento/Sitemap/etc/adminhtml/system.xml +++ b/app/code/Magento/Sitemap/etc/adminhtml/system.xml @@ -51,7 +51,19 @@ <comment>Valid values range from 0.0 to 1.0.</comment> </field> </group> - <group id="generate" translate="label" type="text" sortOrder="4" showInDefault="1" showInWebsite="0" showInStore="0"> + <group id="store" translate="label" type="text" sortOrder="4" showInDefault="1" showInWebsite="1" showInStore="1"> + <label>Store Url Options</label> + <field id="changefreq" translate="label" type="select" sortOrder="10" showInDefault="1" showInWebsite="1" showInStore="1" canRestore="1"> + <label>Frequency</label> + <source_model>Magento\Sitemap\Model\Config\Source\Frequency</source_model> + </field> + <field id="priority" translate="label comment" type="text" sortOrder="20" showInDefault="1" showInWebsite="1" showInStore="1" canRestore="1"> + <label>Priority</label> + <backend_model>Magento\Sitemap\Model\Config\Backend\Priority</backend_model> + <comment>Valid values range from 0.0 to 1.0.</comment> + </field> + </group> + <group id="generate" translate="label" type="text" sortOrder="5" showInDefault="1"> <label>Generation Settings</label> <field id="enabled" translate="label" type="select" sortOrder="1" showInDefault="1" showInWebsite="1" showInStore="1" canRestore="1"> <label>Enabled</label> @@ -60,26 +72,41 @@ <field id="error_email" translate="label" type="text" sortOrder="5" showInDefault="1" showInWebsite="1" showInStore="1"> <label>Error Email Recipient</label> <validate>validate-email</validate> + <depends> + <field id="enabled">1</field> + </depends> </field> - <field id="error_email_identity" translate="label" type="select" sortOrder="6" showInDefault="1" showInWebsite="1" showInStore="0" canRestore="1"> + <field id="error_email_identity" translate="label" type="select" sortOrder="6" showInDefault="1" showInWebsite="1" canRestore="1"> <label>Error Email Sender</label> <source_model>Magento\Config\Model\Config\Source\Email\Identity</source_model> + <depends> + <field id="enabled">1</field> + </depends> </field> - <field id="error_email_template" translate="label comment" type="select" sortOrder="7" showInDefault="1" showInWebsite="1" showInStore="0" canRestore="1"> + <field id="error_email_template" translate="label comment" type="select" sortOrder="7" showInDefault="1" showInWebsite="1" canRestore="1"> <label>Error Email Template</label> <comment>Email template chosen based on theme fallback when "Default" option is selected.</comment> <source_model>Magento\Config\Model\Config\Source\Email\Template</source_model> + <depends> + <field id="enabled">1</field> + </depends> </field> <field id="frequency" translate="label" type="select" sortOrder="4" showInDefault="1" showInWebsite="1" showInStore="1"> <label>Frequency</label> <source_model>Magento\Cron\Model\Config\Source\Frequency</source_model> <backend_model>Magento\Cron\Model\Config\Backend\Sitemap</backend_model> + <depends> + <field id="enabled">1</field> + </depends> </field> <field id="time" translate="label" type="time" sortOrder="3" showInDefault="1" showInWebsite="1" showInStore="1"> <label>Start Time</label> + <depends> + <field id="enabled">1</field> + </depends> </field> </group> - <group id="limit" translate="label" type="text" sortOrder="5" showInDefault="1" showInWebsite="1" showInStore="1"> + <group id="limit" translate="label" type="text" sortOrder="6" showInDefault="1" showInWebsite="1" showInStore="1"> <label>Sitemap File Limits</label> <field id="max_lines" translate="label" type="text" sortOrder="1" showInDefault="1" showInWebsite="1" showInStore="1" canRestore="1"> <label>Maximum No of URLs Per File</label> @@ -91,7 +118,7 @@ <validate>validate-number validate-greater-than-zero</validate> </field> </group> - <group id="search_engines" translate="label" type="text" sortOrder="6" showInDefault="1" showInWebsite="1" showInStore="1"> + <group id="search_engines" translate="label" type="text" sortOrder="7" showInDefault="1" showInWebsite="1" showInStore="1"> <label>Search Engine Submission Settings</label> <field id="submission_robots" translate="label" type="select" sortOrder="1" showInDefault="1" showInWebsite="1" showInStore="1" canRestore="1"> <label>Enable Submission to Robots.txt</label> diff --git a/app/code/Magento/Sitemap/etc/config.xml b/app/code/Magento/Sitemap/etc/config.xml index 6f14ff728ac4f..36b2cc2207422 100644 --- a/app/code/Magento/Sitemap/etc/config.xml +++ b/app/code/Magento/Sitemap/etc/config.xml @@ -25,6 +25,10 @@ <changefreq>daily</changefreq> <image_include>all</image_include> </product> + <store> + <priority>1</priority> + <changefreq>daily</changefreq> + </store> <generate> <enabled>0</enabled> <error_email /> diff --git a/app/code/Magento/Sitemap/etc/di.xml b/app/code/Magento/Sitemap/etc/di.xml index e627b3b50fff9..4c4a5f98f737a 100644 --- a/app/code/Magento/Sitemap/etc/di.xml +++ b/app/code/Magento/Sitemap/etc/di.xml @@ -25,12 +25,18 @@ <type name="Magento\Sitemap\Model\ItemProvider\Composite"> <arguments> <argument name="itemProviders" xsi:type="array"> + <item name="storeUrlProvider" xsi:type="object">Magento\Sitemap\Model\ItemProvider\StoreUrl</item> <item name="categoryProvider" xsi:type="object">Magento\Sitemap\Model\ItemProvider\Category</item> <item name="cmsPageProvider" xsi:type="object">Magento\Sitemap\Model\ItemProvider\CmsPage</item> <item name="productProvider" xsi:type="object">Magento\Sitemap\Model\ItemProvider\Product</item> </argument> </arguments> </type> + <type name="Magento\Sitemap\Model\ItemProvider\StoreUrl"> + <arguments> + <argument name="configReader" xsi:type="object">Magento\Sitemap\Model\ItemProvider\StoreUrlConfigReader</argument> + </arguments> + </type> <type name="Magento\Sitemap\Model\ItemProvider\Category"> <arguments> <argument name="configReader" xsi:type="object">Magento\Sitemap\Model\ItemProvider\CategoryConfigReader</argument> diff --git a/app/code/Magento/Sitemap/i18n/en_US.csv b/app/code/Magento/Sitemap/i18n/en_US.csv index 29ecd7e22773e..8a7ac86e2fb47 100644 --- a/app/code/Magento/Sitemap/i18n/en_US.csv +++ b/app/code/Magento/Sitemap/i18n/en_US.csv @@ -64,3 +64,4 @@ ID,ID "Link for Google","Link for Google" "Last Generated","Last Generated" Action,Action +"Store Url Options","Store Url Options" diff --git a/app/code/Magento/Sitemap/registration.php b/app/code/Magento/Sitemap/registration.php index 9fc965c854779..b713ddb98f381 100644 --- a/app/code/Magento/Sitemap/registration.php +++ b/app/code/Magento/Sitemap/registration.php @@ -4,6 +4,6 @@ * See COPYING.txt for license details. */ -use \Magento\Framework\Component\ComponentRegistrar; +use Magento\Framework\Component\ComponentRegistrar; ComponentRegistrar::register(ComponentRegistrar::MODULE, 'Magento_Sitemap', __DIR__); diff --git a/app/code/Magento/Store/App/Action/Plugin/StoreCheck.php b/app/code/Magento/Store/App/Action/Plugin/StoreCheck.php index d9cb146c9f12a..d1171865275a8 100644 --- a/app/code/Magento/Store/App/Action/Plugin/StoreCheck.php +++ b/app/code/Magento/Store/App/Action/Plugin/StoreCheck.php @@ -1,40 +1,46 @@ <?php /** - * * Copyright © Magento, Inc. All rights reserved. * See COPYING.txt for license details. */ + namespace Magento\Store\App\Action\Plugin; +use Magento\Framework\App\ActionInterface; +use Magento\Framework\Exception\State\InitException; +use Magento\Store\Model\StoreManagerInterface; + +/** + * Plugin verify Store on before Execute on ActionInterface + */ class StoreCheck { /** - * @var \Magento\Store\Model\StoreManagerInterface + * @var StoreManagerInterface */ protected $_storeManager; /** - * @param \Magento\Store\Model\StoreManagerInterface $storeManager + * @param StoreManagerInterface $storeManager */ public function __construct( - \Magento\Store\Model\StoreManagerInterface $storeManager + StoreManagerInterface $storeManager ) { $this->_storeManager = $storeManager; } /** - * @param \Magento\Framework\App\Action\AbstractAction $subject - * @param \Magento\Framework\App\RequestInterface $request + * Verify before execute + * + * @param ActionInterface $subject * @return void * @SuppressWarnings(PHPMD.UnusedFormalParameter) * @throws \Magento\Framework\Exception\State\InitException */ - public function beforeDispatch( - \Magento\Framework\App\Action\AbstractAction $subject, - \Magento\Framework\App\RequestInterface $request - ) { + public function beforeExecute(ActionInterface $subject) + { if (!$this->_storeManager->getStore()->isActive()) { - throw new \Magento\Framework\Exception\State\InitException( + throw new InitException( __('Current store is not active.') ); } diff --git a/app/code/Magento/Store/App/Config/Source/InitialConfigSource.php b/app/code/Magento/Store/App/Config/Source/InitialConfigSource.php index d6e98c0ddcdaa..4598a70e99397 100644 --- a/app/code/Magento/Store/App/Config/Source/InitialConfigSource.php +++ b/app/code/Magento/Store/App/Config/Source/InitialConfigSource.php @@ -52,6 +52,7 @@ public function __construct( /** * Return whole config data from config file for specified config type. + * * Ignore $path argument due to config source must return all config data * * @param string $path @@ -65,7 +66,7 @@ public function get($path = '') * * @see \Magento\Store\Model\Config\Importer To import store configs */ - if ($this->deploymentConfig->isAvailable()) { + if ($this->deploymentConfig->isAvailable() || $this->deploymentConfig->isDbAvailable()) { return []; } diff --git a/app/code/Magento/Store/App/Response/Redirect.php b/app/code/Magento/Store/App/Response/Redirect.php index 178395ff6eb6a..da0c49aa1bc11 100644 --- a/app/code/Magento/Store/App/Response/Redirect.php +++ b/app/code/Magento/Store/App/Response/Redirect.php @@ -51,7 +51,7 @@ class Redirect implements \Magento\Framework\App\Response\RedirectInterface protected $_urlBuilder; /** - * @var \Zend\Uri\Uri|null + * @var \Laminas\Uri\Uri|null */ private $uri; @@ -64,7 +64,7 @@ class Redirect implements \Magento\Framework\App\Response\RedirectInterface * @param \Magento\Framework\Session\SessionManagerInterface $session * @param \Magento\Framework\Session\SidResolverInterface $sidResolver * @param \Magento\Framework\UrlInterface $urlBuilder - * @param \Zend\Uri\Uri|null $uri + * @param \Laminas\Uri\Uri|null $uri * @param bool $canUseSessionIdInParam */ public function __construct( @@ -74,7 +74,7 @@ public function __construct( \Magento\Framework\Session\SessionManagerInterface $session, \Magento\Framework\Session\SidResolverInterface $sidResolver, \Magento\Framework\UrlInterface $urlBuilder, - \Zend\Uri\Uri $uri = null, + \Laminas\Uri\Uri $uri = null, $canUseSessionIdInParam = true ) { $this->_canUseSessionIdInParam = $canUseSessionIdInParam; @@ -84,7 +84,7 @@ public function __construct( $this->_session = $session; $this->_sidResolver = $sidResolver; $this->_urlBuilder = $urlBuilder; - $this->uri = $uri ?: ObjectManager::getInstance()->get(\Zend\Uri\Uri::class); + $this->uri = $uri ?: ObjectManager::getInstance()->get(\Laminas\Uri\Uri::class); } /** diff --git a/app/code/Magento/Store/Controller/Store/Redirect.php b/app/code/Magento/Store/Controller/Store/Redirect.php index 21692e9d6dd1e..45924b5b0d28a 100644 --- a/app/code/Magento/Store/Controller/Store/Redirect.php +++ b/app/code/Magento/Store/Controller/Store/Redirect.php @@ -7,19 +7,26 @@ namespace Magento\Store\Controller\Store; +use Magento\Framework\App\Action\Action; use Magento\Framework\App\Action\Context; -use Magento\Framework\App\ResponseInterface; +use Magento\Framework\App\Action\HttpGetActionInterface; +use Magento\Framework\App\Action\HttpPostActionInterface; +use Magento\Framework\App\ActionInterface; +use Magento\Framework\App\ObjectManager; use Magento\Framework\Exception\NoSuchEntityException; +use Magento\Framework\Session\Generic; +use Magento\Framework\Session\SidResolverInterface; use Magento\Store\Api\StoreRepositoryInterface; use Magento\Store\Api\StoreResolverInterface; +use Magento\Store\Model\Store; +use Magento\Store\Model\StoreManagerInterface; use Magento\Store\Model\StoreResolver; -use Magento\Framework\Session\SidResolverInterface; -use Magento\Framework\Session\Generic as Session; +use Magento\Store\Model\StoreSwitcher\HashGenerator; /** - * Builds correct url to target store and performs redirect. + * Builds correct url to target store (group) and performs redirect. */ -class Redirect extends \Magento\Framework\App\Action\Action +class Redirect extends Action implements HttpGetActionInterface, HttpPostActionInterface { /** * @var StoreRepositoryInterface @@ -32,43 +39,49 @@ class Redirect extends \Magento\Framework\App\Action\Action private $storeResolver; /** - * @var SidResolverInterface + * @var HashGenerator */ - private $sidResolver; + private $hashGenerator; /** - * @var Session + * @var StoreManagerInterface */ - private $session; + private $storeManager; /** * @param Context $context * @param StoreRepositoryInterface $storeRepository * @param StoreResolverInterface $storeResolver - * @param Session $session + * @param Generic $session * @param SidResolverInterface $sidResolver + * @param HashGenerator $hashGenerator + * @param StoreManagerInterface $storeManager + * @SuppressWarnings(PHPMD.UnusedFormalParameter) */ public function __construct( Context $context, StoreRepositoryInterface $storeRepository, StoreResolverInterface $storeResolver, - Session $session, - SidResolverInterface $sidResolver + Generic $session, + SidResolverInterface $sidResolver, + HashGenerator $hashGenerator, + StoreManagerInterface $storeManager = null ) { parent::__construct($context); $this->storeRepository = $storeRepository; $this->storeResolver = $storeResolver; - $this->session = $session; - $this->sidResolver = $sidResolver; + $this->hashGenerator = $hashGenerator; + $this->storeManager = $storeManager ?: ObjectManager::getInstance()->get(StoreManagerInterface::class); } /** - * @return ResponseInterface|\Magento\Framework\Controller\ResultInterface + * @inheritDoc + * * @throws NoSuchEntityException */ public function execute() { - /** @var \Magento\Store\Model\Store $currentStore */ + /** @var Store $currentStore */ $currentStore = $this->storeRepository->getById($this->storeResolver->getCurrentStoreId()); $targetStoreCode = $this->_request->getParam(StoreResolver::PARAM_NAME); $fromStoreCode = $this->_request->getParam('___from_store'); @@ -79,35 +92,37 @@ public function execute() } try { - /** @var \Magento\Store\Model\Store $targetStore */ + /** @var Store $fromStore */ $fromStore = $this->storeRepository->get($fromStoreCode); + /** @var Store $targetStore */ + $targetStore = $this->storeRepository->get($targetStoreCode); + $this->storeManager->setCurrentStore($targetStore); } catch (NoSuchEntityException $e) { - $error = __('Requested store is not found'); + $error = __("Requested store is not found ({$fromStoreCode})"); } if ($error !== null) { $this->messageManager->addErrorMessage($error); $this->_redirect->redirect($this->_response, $currentStore->getBaseUrl()); } else { - $encodedUrl = $this->_request->getParam(\Magento\Framework\App\ActionInterface::PARAM_NAME_URL_ENCODED); - + $encodedUrl = $this->_request->getParam(ActionInterface::PARAM_NAME_URL_ENCODED); $query = [ '___from_store' => $fromStore->getCode(), StoreResolverInterface::PARAM_NAME => $targetStoreCode, - \Magento\Framework\App\ActionInterface::PARAM_NAME_URL_ENCODED => $encodedUrl, + ActionInterface::PARAM_NAME_URL_ENCODED => $encodedUrl, ]; - if ($this->sidResolver->getUseSessionInUrl()) { - // allow customers to stay logged in during store switching - $sidName = $this->sidResolver->getSessionIdQueryParam($this->session); - $query[$sidName] = $this->session->getSessionId(); - } + $customerHash = $this->hashGenerator->generateHash($fromStore); + $query = array_merge($query, $customerHash); $arguments = [ '_nosid' => true, '_query' => $query ]; + $this->_redirect->redirect($this->_response, 'stores/store/switch', $arguments); } + + return null; } } diff --git a/app/code/Magento/Store/Controller/Store/SwitchAction.php b/app/code/Magento/Store/Controller/Store/SwitchAction.php index d8ac1b308d7ed..41acb1605ec7c 100644 --- a/app/code/Magento/Store/Controller/Store/SwitchAction.php +++ b/app/code/Magento/Store/Controller/Store/SwitchAction.php @@ -11,7 +11,6 @@ use Magento\Framework\App\Action\Action; use Magento\Framework\App\Action\Context as ActionContext; use Magento\Framework\App\Http\Context as HttpContext; -use Magento\Framework\App\ObjectManager; use Magento\Framework\Exception\NoSuchEntityException; use Magento\Store\Api\StoreCookieManagerInterface; use Magento\Store\Api\StoreRepositoryInterface; @@ -21,6 +20,7 @@ use Magento\Store\Model\StoreSwitcherInterface; use Magento\Framework\App\Action\HttpPostActionInterface; use Magento\Framework\App\Action\HttpGetActionInterface; +use Magento\Store\Controller\Store\SwitchAction\CookieManager; /** * Handles store switching url and makes redirect. @@ -56,6 +56,11 @@ class SwitchAction extends Action implements HttpGetActionInterface, HttpPostAct */ private $storeSwitcher; + /** + * @var CookieManager + */ + private $cookieManager; + /** * Initialize dependencies. * @@ -65,6 +70,7 @@ class SwitchAction extends Action implements HttpGetActionInterface, HttpPostAct * @param StoreRepositoryInterface $storeRepository * @param StoreManagerInterface $storeManager * @param StoreSwitcherInterface $storeSwitcher + * @param CookieManager $cookieManager */ public function __construct( ActionContext $context, @@ -72,7 +78,8 @@ public function __construct( HttpContext $httpContext, StoreRepositoryInterface $storeRepository, StoreManagerInterface $storeManager, - StoreSwitcherInterface $storeSwitcher = null + StoreSwitcherInterface $storeSwitcher, + CookieManager $cookieManager ) { parent::__construct($context); $this->storeCookieManager = $storeCookieManager; @@ -80,7 +87,8 @@ public function __construct( $this->storeRepository = $storeRepository; $this->storeManager = $storeManager; $this->messageManager = $context->getMessageManager(); - $this->storeSwitcher = $storeSwitcher ?: ObjectManager::getInstance()->get(StoreSwitcherInterface::class); + $this->storeSwitcher = $storeSwitcher; + $this->cookieManager = $cookieManager; } /** @@ -88,12 +96,13 @@ public function __construct( * * @return void * @throws StoreSwitcher\CannotSwitchStoreException + * @throws \Magento\Framework\Exception\InputException + * @throws \Magento\Framework\Stdlib\Cookie\CookieSizeLimitReachedException + * @throws \Magento\Framework\Stdlib\Cookie\FailureToSendException */ public function execute() { - $targetStoreCode = $this->_request->getParam( - \Magento\Store\Model\StoreManagerInterface::PARAM_NAME - ); + $targetStoreCode = $this->_request->getParam(StoreManagerInterface::PARAM_NAME); $fromStoreCode = $this->_request->getParam( '___from_store', $this->storeCookieManager->getStoreCodeFromCookie() @@ -115,6 +124,7 @@ public function execute() $this->messageManager->addErrorMessage($error); } else { $redirectUrl = $this->storeSwitcher->switch($fromStore, $targetStore, $requestedUrlToRedirect); + $this->cookieManager->setCookieForStore($targetStore); } $this->getResponse()->setRedirect($redirectUrl); diff --git a/app/code/Magento/Store/Controller/Store/SwitchAction/CookieManager.php b/app/code/Magento/Store/Controller/Store/SwitchAction/CookieManager.php new file mode 100644 index 0000000000000..182ae35b0ff61 --- /dev/null +++ b/app/code/Magento/Store/Controller/Store/SwitchAction/CookieManager.php @@ -0,0 +1,65 @@ +<?php +/** + * + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +declare(strict_types=1); + +namespace Magento\Store\Controller\Store\SwitchAction; + +use Magento\Framework\Stdlib\Cookie\CookieMetadataFactory; +use Magento\Framework\Stdlib\CookieManagerInterface; +use Magento\Store\Api\Data\StoreInterface; + +/** + * Handles store switching cookie for the frontend storage clean + * + * @SuppressWarnings(PHPMD.CookieAndSessionMisuse) + */ +class CookieManager +{ + /** + * @var string + */ + const COOKIE_NAME = 'section_data_clean'; + + /** + * @var CookieMetadataFactory + */ + private $cookieMetadataFactory; + + /** + * @var CookieManagerInterface + */ + private $cookieManager; + + /** + * @param CookieMetadataFactory $cookieMetadataFactory + * @param CookieManagerInterface $cookieManager + */ + public function __construct( + CookieMetadataFactory $cookieMetadataFactory, + CookieManagerInterface $cookieManager + ) { + $this->cookieMetadataFactory = $cookieMetadataFactory; + $this->cookieManager = $cookieManager; + } + + /** + * Set cookie for store + * + * @param StoreInterface $targetStore + * @throws \Magento\Framework\Exception\InputException + * @throws \Magento\Framework\Stdlib\Cookie\CookieSizeLimitReachedException + * @throws \Magento\Framework\Stdlib\Cookie\FailureToSendException + */ + public function setCookieForStore(StoreInterface $targetStore) + { + $cookieMetadata = $this->cookieMetadataFactory->createPublicCookieMetadata() + ->setHttpOnly(false) + ->setDuration(15) + ->setPath($targetStore->getStorePath()); + $this->cookieManager->setPublicCookie(self::COOKIE_NAME, $targetStore->getCode(), $cookieMetadata); + } +} diff --git a/app/code/Magento/Store/Model/Config/Importer/Processor/Create.php b/app/code/Magento/Store/Model/Config/Importer/Processor/Create.php index 1fb9f1c224d3e..317e4bf43e42c 100644 --- a/app/code/Magento/Store/Model/Config/Importer/Processor/Create.php +++ b/app/code/Magento/Store/Model/Config/Importer/Processor/Create.php @@ -160,6 +160,7 @@ private function createWebsites(array $items, array $data) * @param array $items Groups to create * @param array $data The all available data * @return void + * @throws NotFoundException */ private function createGroups(array $items, array $data) { @@ -199,6 +200,7 @@ private function createGroups(array $items, array $data) * @param array $items Stores to create * @param array $data The all available data * @return void + * @throws NotFoundException */ private function createStores(array $items, array $data) { diff --git a/app/code/Magento/Store/Model/Config/Processor/Fallback.php b/app/code/Magento/Store/Model/Config/Processor/Fallback.php index 91926cd23f9cf..57ac3459ccb48 100644 --- a/app/code/Magento/Store/Model/Config/Processor/Fallback.php +++ b/app/code/Magento/Store/Model/Config/Processor/Fallback.php @@ -172,7 +172,7 @@ private function getWebsiteConfig(array $websites, $id) foreach ((array)$this->websiteData as $website) { if ($website['website_id'] == $id) { $code = $website['code']; - return isset($websites[$code]) ? $websites[$code] : []; + return $websites[$code] ?? []; } } return []; diff --git a/app/code/Magento/Store/Model/Store.php b/app/code/Magento/Store/Model/Store.php index 5eda6f4a9b57d..5187bb8776632 100644 --- a/app/code/Magento/Store/Model/Store.php +++ b/app/code/Magento/Store/Model/Store.php @@ -18,7 +18,7 @@ use Magento\Framework\Url\ScopeInterface as UrlScopeInterface; use Magento\Framework\UrlInterface; use Magento\Store\Api\Data\StoreInterface; -use Zend\Uri\UriFactory; +use Laminas\Uri\UriFactory; /** * Store model @@ -278,6 +278,7 @@ class Store extends AbstractExtensibleModel implements /** * @var \Magento\Framework\Session\SidResolverInterface + * @deprecated Not used anymore. */ protected $_sidResolver; @@ -1199,7 +1200,6 @@ public function isDefault() */ public function getCurrentUrl($fromStore = true) { - $sidQueryParam = $this->_sidResolver->getSessionIdQueryParam($this->_getSession()); $requestString = $this->_url->escape(ltrim($this->_request->getRequestString(), '/')); $storeUrl = $this->getUrl('', ['_secure' => $this->_storeManager->getStore()->isCurrentlySecure()]); @@ -1218,12 +1218,6 @@ public function getCurrentUrl($fromStore = true) } $currQuery = $this->_request->getQueryValue(); - if (isset($currQuery[$sidQueryParam]) - && !empty($currQuery[$sidQueryParam]) - && $this->_getSession()->getSessionIdForHost($storeUrl) != $currQuery[$sidQueryParam] - ) { - unset($currQuery[$sidQueryParam]); - } foreach ($currQuery as $key => $value) { $storeParsedQuery[$key] = $value; diff --git a/app/code/Magento/Store/Model/StoreSwitcher/HashGenerator.php b/app/code/Magento/Store/Model/StoreSwitcher/HashGenerator.php index 456941bd41c25..d1858939434b7 100644 --- a/app/code/Magento/Store/Model/StoreSwitcher/HashGenerator.php +++ b/app/code/Magento/Store/Model/StoreSwitcher/HashGenerator.php @@ -7,19 +7,18 @@ namespace Magento\Store\Model\StoreSwitcher; +use Magento\Authorization\Model\UserContextInterface; +use Magento\Framework\App\ActionInterface; +use Magento\Framework\App\DeploymentConfig as DeploymentConfig; +use Magento\Framework\Config\ConfigOptionsListConstants; +use Magento\Framework\Url\Helper\Data as UrlHelper; use Magento\Store\Api\Data\StoreInterface; use Magento\Store\Model\StoreSwitcher\HashGenerator\HashData; -use Magento\Store\Model\StoreSwitcherInterface; -use \Magento\Framework\App\DeploymentConfig as DeploymentConfig; -use Magento\Framework\Url\Helper\Data as UrlHelper; -use Magento\Framework\Config\ConfigOptionsListConstants; -use Magento\Authorization\Model\UserContextInterface; -use \Magento\Framework\App\ActionInterface; /** * Generate one time token and build redirect url */ -class HashGenerator implements StoreSwitcherInterface +class HashGenerator { /** * @var \Magento\Framework\App\DeploymentConfig @@ -52,48 +51,40 @@ public function __construct( } /** - * Builds redirect url with token + * Generate hash data for customer * - * @param StoreInterface $fromStore store where we came from - * @param StoreInterface $targetStore store where to go to - * @param string $redirectUrl original url requested for redirect after switching - * @return string redirect url - * @SuppressWarnings(PHPMD.UnusedFormalParameter) + * @param StoreInterface $fromStore + * @return array */ - public function switch(StoreInterface $fromStore, StoreInterface $targetStore, string $redirectUrl): string + public function generateHash(StoreInterface $fromStore): array { - $targetUrl = $redirectUrl; + $key = (string)$this->deploymentConfig->get(ConfigOptionsListConstants::CONFIG_PATH_CRYPT_KEY); + $timeStamp = time(); + $customerId = null; - $encodedUrl = $this->urlHelper->getEncodedUrl($redirectUrl); + $result = []; if ($this->currentUser->getUserType() == UserContextInterface::USER_TYPE_CUSTOMER) { $customerId = $this->currentUser->getUserId(); - } - if ($customerId) { - // phpcs:ignore - $urlParts = parse_url($targetUrl); - $host = $urlParts['host']; - $scheme = $urlParts['scheme']; - $key = (string)$this->deploymentConfig->get(ConfigOptionsListConstants::CONFIG_PATH_CRYPT_KEY); - $timeStamp = time(); - $fromStoreCode = $fromStore->getCode(); - $data = implode(',', [$customerId, $timeStamp, $fromStoreCode]); - $signature = hash_hmac('sha256', $data, $key); - $targetUrl = $scheme . "://" . $host . '/stores/store/switchrequest'; - $targetUrl = $this->urlHelper->addRequestParam( - $targetUrl, - ['customer_id' => $customerId] - ); - $targetUrl = $this->urlHelper->addRequestParam($targetUrl, ['time_stamp' => $timeStamp]); - $targetUrl = $this->urlHelper->addRequestParam($targetUrl, ['signature' => $signature]); - $targetUrl = $this->urlHelper->addRequestParam($targetUrl, ['___from_store' => $fromStoreCode]); - $targetUrl = $this->urlHelper->addRequestParam( - $targetUrl, - [ActionInterface::PARAM_NAME_URL_ENCODED => $encodedUrl] + $data = implode( + ',', + [ + $customerId, + $timeStamp, + $fromStore->getCode() + ] ); + $signature = hash_hmac('sha256', $data, $key); + + $result = [ + 'customer_id' => $customerId, + 'time_stamp' => $timeStamp, + 'signature' => $signature + ]; } - return $targetUrl; + + return $result; } /** diff --git a/app/code/Magento/Store/Model/StoreSwitcher/HashProcessor.php b/app/code/Magento/Store/Model/StoreSwitcher/HashProcessor.php new file mode 100644 index 0000000000000..909fe9f6683f8 --- /dev/null +++ b/app/code/Magento/Store/Model/StoreSwitcher/HashProcessor.php @@ -0,0 +1,127 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +declare(strict_types=1); + +namespace Magento\Store\Model\StoreSwitcher; + +use Magento\Authorization\Model\UserContextInterface; +use Magento\Customer\Api\CustomerRepositoryInterface; +use Magento\Customer\Model\ResourceModel\CustomerRepository; +use Magento\Customer\Model\Session as CustomerSession; +use Magento\Framework\App\DeploymentConfig as DeploymentConfig; +use Magento\Framework\App\RequestInterface; +use Magento\Framework\Exception\LocalizedException; +use Magento\Framework\Exception\NoSuchEntityException; +use Magento\Framework\Message\ManagerInterface; +use Magento\Framework\Url\Helper\Data as UrlHelper; +use Magento\Store\Api\Data\StoreInterface; +use Magento\Store\Model\StoreSwitcher\HashGenerator\HashData; +use Magento\Store\Model\StoreSwitcherInterface; + +/** + * Process one time token and build redirect url + * + * @SuppressWarnings(PHPMD.CookieAndSessionMisuse) + */ +class HashProcessor implements StoreSwitcherInterface +{ + /** + * @var HashGenerator + */ + private $hashGenerator; + + /** + * @var RequestInterface + */ + private $request; + + /** + * @var ManagerInterface + */ + private $messageManager; + + /** + * @var customerSession + */ + private $customerSession; + + /** + * @var CustomerRepositoryInterface + */ + private $customerRepository; + + /** + * @param HashGenerator $hashGenerator + * @param RequestInterface $request + * @param ManagerInterface $messageManager + * @param CustomerRepository $customerRepository + * @param CustomerSession $customerSession + */ + public function __construct( + HashGenerator $hashGenerator, + RequestInterface $request, + ManagerInterface $messageManager, + CustomerRepository $customerRepository, + CustomerSession $customerSession + ) { + $this->hashGenerator = $hashGenerator; + $this->request = $request; + $this->messageManager = $messageManager; + $this->customerSession = $customerSession; + $this->customerRepository = $customerRepository; + } + + /** + * Builds redirect url with token + * + * @param StoreInterface $fromStore store where we came from + * @param StoreInterface $targetStore store where to go to + * @param string $redirectUrl original url requested for redirect after switching + * @return string redirect url + * @SuppressWarnings(PHPMD.UnusedFormalParameter) + */ + public function switch(StoreInterface $fromStore, StoreInterface $targetStore, string $redirectUrl): string + { + $customerId = $this->request->getParam('customer_id'); + + if ($customerId) { + $fromStoreCode = (string)$this->request->getParam('___from_store'); + $timeStamp = (string)$this->request->getParam('time_stamp'); + $signature = (string)$this->request->getParam('signature'); + + $error = null; + + $data = new HashData( + [ + "customer_id" => $customerId, + "time_stamp" => $timeStamp, + "___from_store" => $fromStoreCode + ] + ); + + if ($redirectUrl && $this->hashGenerator->validateHash($signature, $data)) { + try { + $customer = $this->customerRepository->getById($customerId); + if (!$this->customerSession->isLoggedIn()) { + $this->customerSession->setCustomerDataAsLoggedIn($customer); + } + } catch (NoSuchEntityException $e) { + $error = __('The requested customer does not exist.'); + } catch (LocalizedException $e) { + $error = __('There was an error retrieving the customer record.'); + } + } else { + $error = __('The requested store cannot be found. Please check the request and try again.'); + } + + if ($error !== null) { + $this->messageManager->addErrorMessage($error); + } + } + + return $redirectUrl; + } +} diff --git a/app/code/Magento/Store/Test/Mftf/ActionGroup/AdminDeleteStoreViewIfExistsActionGroup.xml b/app/code/Magento/Store/Test/Mftf/ActionGroup/AdminDeleteStoreViewIfExistsActionGroup.xml deleted file mode 100644 index 6ebf72a893c04..0000000000000 --- a/app/code/Magento/Store/Test/Mftf/ActionGroup/AdminDeleteStoreViewIfExistsActionGroup.xml +++ /dev/null @@ -1,34 +0,0 @@ -<?xml version="1.0" encoding="UTF-8"?> -<!-- - /** - * Copyright © Magento, Inc. All rights reserved. - * See COPYING.txt for license details. - */ ---> - -<actionGroups xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" - xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/actionGroupSchema.xsd"> - <actionGroup name="AdminDeleteStoreViewIfExistsActionGroup" extends="AdminSearchStoreViewByNameActionGroup"> - <annotations> - <description>EXTENDS: AdminSearchStoreViewByNameActionGroup. Goes to the Admin Stores grid page. Deletes the provided Store (if exists) without creating a Backup. Validates that the Success Message is present and correct.</description> - </annotations> - - <executeInSelenium function="function($webdriver) use ($I) { - $items = $webdriver->findElements(\Facebook\WebDriver\WebDriverBy::cssSelector('.col-store_title>a')); - if(!empty($items)) { - $I->click('.col-store_title>a'); - $I->waitForPageLoad(10); - $I->click('#delete'); - $I->waitForPageLoad(30); - $I->selectOption('select#store_create_backup', 'No'); - $I->click('#delete'); - $I->waitForPageLoad(30); - $I->waitForElementVisible('aside.confirm .modal-title', 10); - $I->click('aside.confirm .modal-footer button.action-accept'); - $I->waitForPageLoad(60); - $I->waitForElementVisible('#messages div.message-success', 10); - $I->see('You deleted the store view.', '#messages div.message-success'); - } - }" after="clickSearchButton" stepKey="deleteStoreViewIfExists"/> - </actionGroup> -</actionGroups> diff --git a/app/code/Magento/Store/Test/Mftf/ActionGroup/AssertStorefrontStoreNotVisibleInFooterActionGroup.xml b/app/code/Magento/Store/Test/Mftf/ActionGroup/AssertStorefrontStoreNotVisibleInFooterActionGroup.xml index eb8012b7d0635..73f687669497e 100644 --- a/app/code/Magento/Store/Test/Mftf/ActionGroup/AssertStorefrontStoreNotVisibleInFooterActionGroup.xml +++ b/app/code/Magento/Store/Test/Mftf/ActionGroup/AssertStorefrontStoreNotVisibleInFooterActionGroup.xml @@ -15,7 +15,7 @@ <arguments> <argument name="store" type="string"/> </arguments> - + <amOnPage url="{{StorefrontHomePage.url}}" stepKey="goToStorefrontPage"/> <waitForPageLoad stepKey="waitForStorefrontHomePageLoad"/> <dontSee selector="{{StorefrontFooterSection.storeLink(store)}}" stepKey="AssertStoreNotOnStorefront"/> diff --git a/app/code/Magento/Store/Test/Mftf/ActionGroup/AssertStorefrontStoreVisibleInFooterActionGroup.xml b/app/code/Magento/Store/Test/Mftf/ActionGroup/AssertStorefrontStoreVisibleInFooterActionGroup.xml index 3ec1555735e99..f6ef11ad01111 100644 --- a/app/code/Magento/Store/Test/Mftf/ActionGroup/AssertStorefrontStoreVisibleInFooterActionGroup.xml +++ b/app/code/Magento/Store/Test/Mftf/ActionGroup/AssertStorefrontStoreVisibleInFooterActionGroup.xml @@ -15,7 +15,7 @@ <arguments> <argument name="customStore" type="string"/> </arguments> - + <amOnPage url="{{StorefrontHomePage.url}}" stepKey="goToStorefrontPage"/> <waitForPageLoad stepKey="waitForStorefrontHomePageLoad"/> <click selector="{{StorefrontFooterSection.switchStoreButton}}" stepKey="clickSwitchStoreButton"/> diff --git a/app/code/Magento/Store/Test/Mftf/ActionGroup/AssertStorefrontStoreVisibleInHeaderActionGroup.xml b/app/code/Magento/Store/Test/Mftf/ActionGroup/AssertStorefrontStoreVisibleInHeaderActionGroup.xml index f9c10a09c51c2..b7a5e1a76d072 100644 --- a/app/code/Magento/Store/Test/Mftf/ActionGroup/AssertStorefrontStoreVisibleInHeaderActionGroup.xml +++ b/app/code/Magento/Store/Test/Mftf/ActionGroup/AssertStorefrontStoreVisibleInHeaderActionGroup.xml @@ -15,7 +15,7 @@ <arguments> <argument name="store" type="string"/> </arguments> - + <amOnPage url="{{StorefrontHomePage.url}}" stepKey="goToStorefrontPage"/> <waitForPageLoad stepKey="waitForStorefrontHomePageLoad"/> <click selector="{{StorefrontHeaderSection.storeViewSwitcher}}" stepKey="selectStoreSwitcher"/> diff --git a/app/code/Magento/Store/Test/Mftf/Data/StoreConfigData.xml b/app/code/Magento/Store/Test/Mftf/Data/StoreConfigData.xml index 9f4de2ffb5723..6b95faafa5868 100644 --- a/app/code/Magento/Store/Test/Mftf/Data/StoreConfigData.xml +++ b/app/code/Magento/Store/Test/Mftf/Data/StoreConfigData.xml @@ -21,6 +21,18 @@ <data key="label">Yes</data> <data key="value">1</data> </entity> + <entity name="StorefrontSingleStoreModeEnabledConfigData"> + <data key="path">general/single_store_mode/enabled</data> + <data key="scope_id">0</data> + <data key="label">Yes</data> + <data key="value">1</data> + </entity> + <entity name="StorefrontSingleStoreModeDisabledConfigData"> + <data key="path">general/single_store_mode/enabled</data> + <data key="scope_id">0</data> + <data key="label">No</data> + <data key="value">0</data> + </entity> <entity name="MinifyJavaScriptFilesDisableConfigData"> <!-- Default value --> <data key="path">dev/js/minify_files</data> diff --git a/app/code/Magento/Store/Test/Mftf/Data/StorePaymentMethodsData.xml b/app/code/Magento/Store/Test/Mftf/Data/StorePaymentMethodsData.xml index 912399142fa61..27a6150b95798 100644 --- a/app/code/Magento/Store/Test/Mftf/Data/StorePaymentMethodsData.xml +++ b/app/code/Magento/Store/Test/Mftf/Data/StorePaymentMethodsData.xml @@ -5,7 +5,8 @@ * See COPYING.txt for license details. */ --> -<entities xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="../../../../../../../vendor/magento/magento2-functional-testing-framework/src/Magento/FunctionalTestingFramework/DataGenerator/etc/dataProfileSchema.xsd"> +<entities xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:noNamespaceSchemaLocation="urn:magento:mftf:DataGenerator/etc/dataProfileSchema.xsd"> <entity name="PaymentMethodsSettingConfig" type="zero_subtotal_checkout_config_state"> <requiredEntity type="active">active</requiredEntity> <requiredEntity type="order_status">orderStatus</requiredEntity> diff --git a/app/code/Magento/Store/Test/Mftf/Data/StoreShippingMethodsData.xml b/app/code/Magento/Store/Test/Mftf/Data/StoreShippingMethodsData.xml index 11b8931618f70..3b749928308bd 100644 --- a/app/code/Magento/Store/Test/Mftf/Data/StoreShippingMethodsData.xml +++ b/app/code/Magento/Store/Test/Mftf/Data/StoreShippingMethodsData.xml @@ -5,7 +5,8 @@ * See COPYING.txt for license details. */ --> -<entities xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="../../../../../../../vendor/magento/magento2-functional-testing-framework/src/Magento/FunctionalTestingFramework/DataGenerator/etc/dataProfileSchema.xsd"> +<entities xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:noNamespaceSchemaLocation="urn:magento:mftf:DataGenerator/etc/dataProfileSchema.xsd"> <entity name="FreeShippingMethodsSettingConfig" type="free_shipping_config_state"> <requiredEntity type="active">active</requiredEntity> </entity> diff --git a/app/code/Magento/Store/Test/Mftf/Metadata/product_website_link-meta.xml b/app/code/Magento/Store/Test/Mftf/Metadata/ProductWebsiteLinkMeta.xml similarity index 100% rename from app/code/Magento/Store/Test/Mftf/Metadata/product_website_link-meta.xml rename to app/code/Magento/Store/Test/Mftf/Metadata/ProductWebsiteLinkMeta.xml diff --git a/app/code/Magento/Store/Test/Mftf/Metadata/store_group-meta.xml b/app/code/Magento/Store/Test/Mftf/Metadata/StoreGroupMeta.xml similarity index 100% rename from app/code/Magento/Store/Test/Mftf/Metadata/store_group-meta.xml rename to app/code/Magento/Store/Test/Mftf/Metadata/StoreGroupMeta.xml diff --git a/app/code/Magento/Store/Test/Mftf/Metadata/store-meta.xml b/app/code/Magento/Store/Test/Mftf/Metadata/StoreMeta.xml similarity index 100% rename from app/code/Magento/Store/Test/Mftf/Metadata/store-meta.xml rename to app/code/Magento/Store/Test/Mftf/Metadata/StoreMeta.xml diff --git a/app/code/Magento/Store/Test/Mftf/Metadata/StorePaymentMethodsMeta.xml b/app/code/Magento/Store/Test/Mftf/Metadata/StorePaymentMethodsMeta.xml new file mode 100644 index 0000000000000..1995dceb3bf0b --- /dev/null +++ b/app/code/Magento/Store/Test/Mftf/Metadata/StorePaymentMethodsMeta.xml @@ -0,0 +1,46 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + /** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +--> + +<operations xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:noNamespaceSchemaLocation="urn:magento:mftf:DataGenerator/etc/dataOperation.xsd"> + <operation name="EnableZeroSubtotalCheckoutConfigState" dataType="zero_subtotal_checkout_config_state" type="create" auth="adminFormKey" url="/admin/system_config/save/section/payment/" method="POST"> + <object key="groups" dataType="zero_subtotal_checkout_config_state"> + <object key="free" dataType="zero_subtotal_checkout_config_state"> + <object key="fields" dataType="zero_subtotal_checkout_config_state"> + <object key="active" dataType="active"> + <field key="value">string</field> + </object> + <object key="order_status" dataType="order_status"> + <field key="value">string</field> + </object> + </object> + </object> + </object> + </operation> + + <operation name="DisablePaymentMethodsSettingConfig" dataType="use_system_value_config_state" type="create" auth="adminFormKey" url="/admin/system_config/save/section/payment/" method="POST"> + <object key="groups" dataType="use_system_value_config_state"> + <object key="free" dataType="use_system_value_config_state"> + <object key="fields" dataType="use_system_value_config_state"> + <object key="active" dataType="use_system_value_config_state"> + <object key="inherit" dataType="zeroSubEnable"> + <field key="value">integer</field> + </object> + </object> + <object key="order_status" dataType="use_system_value_config_state"> + <object key="inherit" dataType="zeroSubOrderStatus"> + <field key="value">integer</field> + </object> + </object> + </object> + </object> + </object> + </operation> + +</operations> + diff --git a/app/code/Magento/Store/Test/Mftf/Metadata/StoreShippingMethodsMeta.xml b/app/code/Magento/Store/Test/Mftf/Metadata/StoreShippingMethodsMeta.xml new file mode 100644 index 0000000000000..091d0ae673f7a --- /dev/null +++ b/app/code/Magento/Store/Test/Mftf/Metadata/StoreShippingMethodsMeta.xml @@ -0,0 +1,49 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + /** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +--> + +<operations xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:noNamespaceSchemaLocation="urn:magento:mftf:DataGenerator/etc/dataOperation.xsd"> + <operation name="EnableFreeShippingConfigState" dataType="free_shipping_config_state" type="create" auth="adminFormKey" url="/admin/system_config/save/section/carriers/" method="POST"> + <object key="groups" dataType="free_shipping_config_state"> + <object key="freeshipping" dataType="free_shipping_config_state"> + <object key="fields" dataType="free_shipping_config_state"> + <object key="active" dataType="active"> + <field key="value">string</field> + </object> + </object> + </object> + </object> + </operation> + + <operation name="DisableFreeShippingConfigState" dataType="disable_free_shipping_config_state" type="create" auth="adminFormKey" url="/admin/system_config/save/section/carriers/" method="POST"> + <object key="groups" dataType="disable_free_shipping_config_state"> + <object key="freeshipping" dataType="disable_free_shipping_config_state"> + <object key="fields" dataType="disable_free_shipping_config_state"> + <object key="active" dataType="disable_free_shipping_config_state"> + <object key="inherit" dataType="disableFreeShipping"> + <field key="value">integer</field> + </object> + </object> + </object> + </object> + </object> + </operation> + + <operation name="MinimumOrderAmount" dataType="minimum_order_amount" type="create" auth="adminFormKey" url="/admin/system_config/save/section/carriers/" method="POST"> + <object key="groups" dataType="minimum_order_amount"> + <object key="freeshipping" dataType="minimum_order_amount"> + <object key="fields" dataType="minimum_order_amount"> + <object key="free_shipping_subtotal" dataType="free_shipping_subtotal"> + <field key="value">string</field> + </object> + </object> + </object> + </object> + </operation> +</operations> + diff --git a/app/code/Magento/Store/Test/Mftf/Metadata/website-meta.xml b/app/code/Magento/Store/Test/Mftf/Metadata/WebsiteMeta.xml similarity index 100% rename from app/code/Magento/Store/Test/Mftf/Metadata/website-meta.xml rename to app/code/Magento/Store/Test/Mftf/Metadata/WebsiteMeta.xml diff --git a/app/code/Magento/Store/Test/Mftf/Metadata/store_payment_methods-meta.xml b/app/code/Magento/Store/Test/Mftf/Metadata/store_payment_methods-meta.xml deleted file mode 100644 index cbad7265cbbd6..0000000000000 --- a/app/code/Magento/Store/Test/Mftf/Metadata/store_payment_methods-meta.xml +++ /dev/null @@ -1,46 +0,0 @@ -<?xml version="1.0" encoding="UTF-8"?> -<!-- - /** - * Copyright © Magento, Inc. All rights reserved. - * See COPYING.txt for license details. - */ ---> - -<operations xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" - xsi:noNamespaceSchemaLocation="../../../../../../../vendor/magento/magento2-functional-testing-framework/src/Magento/FunctionalTestingFramework/DataGenerator/etc/dataOperation.xsd"> - <operation name="EnableZeroSubtotalCheckoutConfigState" dataType="zero_subtotal_checkout_config_state" type="create" auth="adminFormKey" url="/admin/system_config/save/section/payment/" method="POST"> - <object key="groups" dataType="zero_subtotal_checkout_config_state"> - <object key="free" dataType="zero_subtotal_checkout_config_state"> - <object key="fields" dataType="zero_subtotal_checkout_config_state"> - <object key="active" dataType="active"> - <field key="value">string</field> - </object> - <object key="order_status" dataType="order_status"> - <field key="value">string</field> - </object> - </object> - </object> - </object> - </operation> - - <operation name="DisablePaymentMethodsSettingConfig" dataType="use_system_value_config_state" type="create" auth="adminFormKey" url="/admin/system_config/save/section/payment/" method="POST"> - <object key="groups" dataType="use_system_value_config_state"> - <object key="free" dataType="use_system_value_config_state"> - <object key="fields" dataType="use_system_value_config_state"> - <object key="active" dataType="use_system_value_config_state"> - <object key="inherit" dataType="zeroSubEnable"> - <field key="value">integer</field> - </object> - </object> - <object key="order_status" dataType="use_system_value_config_state"> - <object key="inherit" dataType="zeroSubOrderStatus"> - <field key="value">integer</field> - </object> - </object> - </object> - </object> - </object> - </operation> - -</operations> - diff --git a/app/code/Magento/Store/Test/Mftf/Metadata/store_shipping_methods-meta.xml b/app/code/Magento/Store/Test/Mftf/Metadata/store_shipping_methods-meta.xml deleted file mode 100644 index 6f88bca760204..0000000000000 --- a/app/code/Magento/Store/Test/Mftf/Metadata/store_shipping_methods-meta.xml +++ /dev/null @@ -1,49 +0,0 @@ -<?xml version="1.0" encoding="UTF-8"?> -<!-- - /** - * Copyright © Magento, Inc. All rights reserved. - * See COPYING.txt for license details. - */ ---> - -<operations xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" - xsi:noNamespaceSchemaLocation="../../../../../../../vendor/magento/magento2-functional-testing-framework/src/Magento/FunctionalTestingFramework/DataGenerator/etc/dataOperation.xsd"> - <operation name="EnableFreeShippingConfigState" dataType="free_shipping_config_state" type="create" auth="adminFormKey" url="/admin/system_config/save/section/carriers/" method="POST"> - <object key="groups" dataType="free_shipping_config_state"> - <object key="freeshipping" dataType="free_shipping_config_state"> - <object key="fields" dataType="free_shipping_config_state"> - <object key="active" dataType="active"> - <field key="value">string</field> - </object> - </object> - </object> - </object> - </operation> - - <operation name="DisableFreeShippingConfigState" dataType="disable_free_shipping_config_state" type="create" auth="adminFormKey" url="/admin/system_config/save/section/carriers/" method="POST"> - <object key="groups" dataType="disable_free_shipping_config_state"> - <object key="freeshipping" dataType="disable_free_shipping_config_state"> - <object key="fields" dataType="disable_free_shipping_config_state"> - <object key="active" dataType="disable_free_shipping_config_state"> - <object key="inherit" dataType="disableFreeShipping"> - <field key="value">integer</field> - </object> - </object> - </object> - </object> - </object> - </operation> - - <operation name="MinimumOrderAmount" dataType="minimum_order_amount" type="create" auth="adminFormKey" url="/admin/system_config/save/section/carriers/" method="POST"> - <object key="groups" dataType="minimum_order_amount"> - <object key="freeshipping" dataType="minimum_order_amount"> - <object key="fields" dataType="minimum_order_amount"> - <object key="free_shipping_subtotal" dataType="free_shipping_subtotal"> - <field key="value">string</field> - </object> - </object> - </object> - </object> - </operation> -</operations> - diff --git a/app/code/Magento/Store/Test/Mftf/Section/AdminStoresGridSection.xml b/app/code/Magento/Store/Test/Mftf/Section/AdminStoresGridSection.xml deleted file mode 100644 index 367d239518768..0000000000000 --- a/app/code/Magento/Store/Test/Mftf/Section/AdminStoresGridSection.xml +++ /dev/null @@ -1,30 +0,0 @@ -<?xml version="1.0" encoding="utf-8"?> -<!-- - /** - * Copyright © Magento, Inc. All rights reserved. - * See COPYING.txt for license details. - */ ---> -<sections xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="urn:magento:mftf:Page/etc/SectionObject.xsd"> - <section name="AdminStoresGridControlsSection"> - <element name="createStoreView" type="button" selector="#add_store"/> - <element name="createStore" type="button" selector="#add_group"/> - <element name="createWebsite" type="button" selector="#add"/> - </section> - <section name="AdminStoresGridSection"> - <element name="storeGrpFilterTextField" type="input" selector="#storeGrid_filter_group_title"/> - <element name="websiteFilterTextField" type="input" selector="#storeGrid_filter_website_title"/> - <element name="storeFilterTextField" type="input" selector="#storeGrid_filter_store_title" timeout="90"/> - <element name="searchButton" type="button" selector=".admin__data-grid-header button[title=Search]" timeout="30"/> - <element name="resetButton" type="button" selector="button[title='Reset Filter']" timeout="30"/> - <element name="websiteNameInFirstRow" type="text" selector="tr:nth-of-type(1) > .col-website_title > a"/> - <element name="storeGrpNameInFirstRow" type="text" selector=".col-group_title>a"/> - <element name="storeNameInFirstRow" type="text" selector=".col-store_title>a"/> - <element name="firstRow" type="textarea" selector="(//*[@id='storeGrid_table']/tbody/tr)[1]"/> - <element name="nthRow" type="textarea" selector="(//*[@id='storeGrid_table']/tbody/tr)[{{rownum}}]" parameterized="true"/> - <element name="successMessage" type="text" selector="//div[@class='message message-success success']/div"/> - <element name="emptyText" type="text" selector="//tr[@class='data-grid-tr-no-data even']/td[@class='empty-text']"/> - <element name="websiteName" type="text" selector="//td[@class='a-left col-website_title ']/a[contains(.,'{{websiteName}}')]" parameterized="true"/> - <element name="gridCell" type="text" selector="//table[@class='data-grid']//tr[{{row}}]//td[count(//table[@class='data-grid']//tr//th[contains(., '{{column}}')]/preceding-sibling::th) +1 ]" parameterized="true"/> - </section> -</sections> diff --git a/app/code/Magento/Store/Test/Mftf/Section/AdminStoresGridSection/AdminStoresGridControlsSection.xml b/app/code/Magento/Store/Test/Mftf/Section/AdminStoresGridSection/AdminStoresGridControlsSection.xml new file mode 100644 index 0000000000000..acfe8ff9748f8 --- /dev/null +++ b/app/code/Magento/Store/Test/Mftf/Section/AdminStoresGridSection/AdminStoresGridControlsSection.xml @@ -0,0 +1,15 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + /** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +--> +<sections xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Page/etc/SectionObject.xsd"> + <section name="AdminStoresGridControlsSection"> + <element name="createStoreView" type="button" selector="#add_store"/> + <element name="createStore" type="button" selector="#add_group"/> + <element name="createWebsite" type="button" selector="#add"/> + </section> +</sections> diff --git a/app/code/Magento/Store/Test/Mftf/Section/AdminStoresGridSection/AdminStoresGridSection.xml b/app/code/Magento/Store/Test/Mftf/Section/AdminStoresGridSection/AdminStoresGridSection.xml new file mode 100644 index 0000000000000..e56836c491276 --- /dev/null +++ b/app/code/Magento/Store/Test/Mftf/Section/AdminStoresGridSection/AdminStoresGridSection.xml @@ -0,0 +1,26 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + /** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +--> +<sections xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Page/etc/SectionObject.xsd"> + <section name="AdminStoresGridSection"> + <element name="storeGrpFilterTextField" type="input" selector="#storeGrid_filter_group_title"/> + <element name="websiteFilterTextField" type="input" selector="#storeGrid_filter_website_title"/> + <element name="storeFilterTextField" type="input" selector="#storeGrid_filter_store_title" timeout="90"/> + <element name="searchButton" type="button" selector=".admin__data-grid-header button[title=Search]" timeout="30"/> + <element name="resetButton" type="button" selector="button[title='Reset Filter']" timeout="30"/> + <element name="websiteNameInFirstRow" type="text" selector="tr:nth-of-type(1) > .col-website_title > a"/> + <element name="storeGrpNameInFirstRow" type="text" selector=".col-group_title>a"/> + <element name="storeNameInFirstRow" type="text" selector=".col-store_title>a"/> + <element name="firstRow" type="textarea" selector="(//*[@id='storeGrid_table']/tbody/tr)[1]"/> + <element name="nthRow" type="textarea" selector="(//*[@id='storeGrid_table']/tbody/tr)[{{rownum}}]" parameterized="true"/> + <element name="successMessage" type="text" selector="//div[@class='message message-success success']/div"/> + <element name="emptyText" type="text" selector="//tr[@class='data-grid-tr-no-data even']/td[@class='empty-text']"/> + <element name="websiteName" type="text" selector="//td[@class='a-left col-website_title ']/a[contains(.,'{{websiteName}}')]" parameterized="true"/> + <element name="gridCell" type="text" selector="//table[@class='data-grid']//tr[{{row}}]//td[count(//table[@class='data-grid']//tr//th[contains(., '{{column}}')]/preceding-sibling::th) +1 ]" parameterized="true"/> + </section> +</sections> diff --git a/app/code/Magento/Store/Test/Mftf/Test/AdminCreateCustomStoreViewStatusDisabledVerifyErrorSaveMessageTest.xml b/app/code/Magento/Store/Test/Mftf/Test/AdminCreateCustomStoreViewStatusDisabledVerifyErrorSaveMessageTest.xml index 94e723c9afafb..0a7b7852c38d0 100644 --- a/app/code/Magento/Store/Test/Mftf/Test/AdminCreateCustomStoreViewStatusDisabledVerifyErrorSaveMessageTest.xml +++ b/app/code/Magento/Store/Test/Mftf/Test/AdminCreateCustomStoreViewStatusDisabledVerifyErrorSaveMessageTest.xml @@ -17,7 +17,7 @@ <group value="mtf_migrated"/> </annotations> <before> - <actionGroup ref = "LoginAsAdmin" stepKey="loginAsAdmin"/> + <actionGroup ref="AdminLoginActionGroup" stepKey="loginAsAdmin"/> <!--Create website--> <actionGroup ref="AdminCreateWebsiteActionGroup" stepKey="createWebsite"> <argument name="newWebsiteName" value="{{customWebsite.name}}"/> @@ -34,7 +34,7 @@ <actionGroup ref="AdminDeleteWebsiteActionGroup" stepKey="deleteWebsite"> <argument name="websiteName" value="{{customWebsite.name}}"/> </actionGroup> - <actionGroup ref="logout" stepKey="logout"/> + <actionGroup ref="AdminLogoutActionGroup" stepKey="logout"/> </after> <!--Create store view selecting store created, choose disabled status and verify AssertStoreDisabledErrorSaveMessage--> @@ -44,4 +44,4 @@ <argument name="storeViewStatus" value="{{storeViewDataDisabled.is_active}}"/> </actionGroup> </test> -</tests> \ No newline at end of file +</tests> diff --git a/app/code/Magento/Store/Test/Mftf/Test/AdminCreateCustomStoreViewStatusEnabledVerifyAbsenceOfDeleteButtonTest.xml b/app/code/Magento/Store/Test/Mftf/Test/AdminCreateCustomStoreViewStatusEnabledVerifyAbsenceOfDeleteButtonTest.xml index 5d4ac8de74680..aa39340013142 100644 --- a/app/code/Magento/Store/Test/Mftf/Test/AdminCreateCustomStoreViewStatusEnabledVerifyAbsenceOfDeleteButtonTest.xml +++ b/app/code/Magento/Store/Test/Mftf/Test/AdminCreateCustomStoreViewStatusEnabledVerifyAbsenceOfDeleteButtonTest.xml @@ -17,7 +17,7 @@ <group value="mtf_migrated"/> </annotations> <before> - <actionGroup ref = "LoginAsAdmin" stepKey="loginAsAdmin"/> + <actionGroup ref="AdminLoginActionGroup" stepKey="loginAsAdmin"/> <!-- Create store --> <actionGroup ref="AdminCreateNewStoreGroupActionGroup" stepKey="createFirstStore"> <argument name="website" value="{{_defaultWebsite.name}}"/> @@ -29,7 +29,7 @@ <actionGroup ref="DeleteCustomStoreActionGroup" stepKey="deleteStore"> <argument name="storeGroupName" value="customStore.name"/> </actionGroup> - <actionGroup ref="logout" stepKey="logout"/> + <actionGroup ref="AdminLogoutActionGroup" stepKey="logout"/> </after> <!--Create custom store view--> diff --git a/app/code/Magento/Store/Test/Mftf/Test/AdminCreateCustomStoreViewStatusEnabledVerifyBackendAndFrontendTest.xml b/app/code/Magento/Store/Test/Mftf/Test/AdminCreateCustomStoreViewStatusEnabledVerifyBackendAndFrontendTest.xml index 2ba8c675b3b2a..929b3aade4022 100644 --- a/app/code/Magento/Store/Test/Mftf/Test/AdminCreateCustomStoreViewStatusEnabledVerifyBackendAndFrontendTest.xml +++ b/app/code/Magento/Store/Test/Mftf/Test/AdminCreateCustomStoreViewStatusEnabledVerifyBackendAndFrontendTest.xml @@ -17,7 +17,7 @@ <group value="mtf_migrated"/> </annotations> <before> - <actionGroup ref = "LoginAsAdmin" stepKey="loginAsAdmin"/> + <actionGroup ref="AdminLoginActionGroup" stepKey="loginAsAdmin"/> <!-- Create store --> <actionGroup ref="AdminCreateNewStoreGroupActionGroup" stepKey="createStore"> <argument name="website" value="{{_defaultWebsite.name}}"/> @@ -29,7 +29,7 @@ <actionGroup ref="DeleteCustomStoreActionGroup" stepKey="deleteStore"> <argument name="storeGroupName" value="customStore.name"/> </actionGroup> - <actionGroup ref="logout" stepKey="logout"/> + <actionGroup ref="AdminLogoutActionGroup" stepKey="logout"/> </after> <!--Create custom store view--> diff --git a/app/code/Magento/Store/Test/Mftf/Test/AdminCreateNewLocalizedStoreViewStatusEnabledTest.xml b/app/code/Magento/Store/Test/Mftf/Test/AdminCreateNewLocalizedStoreViewStatusEnabledTest.xml index 6c60445107b28..abb44165abd13 100644 --- a/app/code/Magento/Store/Test/Mftf/Test/AdminCreateNewLocalizedStoreViewStatusEnabledTest.xml +++ b/app/code/Magento/Store/Test/Mftf/Test/AdminCreateNewLocalizedStoreViewStatusEnabledTest.xml @@ -17,13 +17,13 @@ <group value="mtf_migrated"/> </annotations> <before> - <actionGroup ref = "LoginAsAdmin" stepKey="loginAsAdmin"/> + <actionGroup ref="AdminLoginActionGroup" stepKey="loginAsAdmin"/> </before> <after> <actionGroup ref="AdminDeleteStoreViewActionGroup" stepKey="deleteStoreView"> <argument name="customStore" value="storeViewGermany"/> </actionGroup> - <actionGroup ref="logout" stepKey="logout"/> + <actionGroup ref="AdminLogoutActionGroup" stepKey="logout"/> </after> <!--Create new localized store view--> diff --git a/app/code/Magento/Store/Test/Mftf/Test/AdminCreateStoreGroupWithCustomWebsiteAndDefaultCategoryTest.xml b/app/code/Magento/Store/Test/Mftf/Test/AdminCreateStoreGroupWithCustomWebsiteAndDefaultCategoryTest.xml index 4892556929f80..d9cd4c8f9f177 100644 --- a/app/code/Magento/Store/Test/Mftf/Test/AdminCreateStoreGroupWithCustomWebsiteAndDefaultCategoryTest.xml +++ b/app/code/Magento/Store/Test/Mftf/Test/AdminCreateStoreGroupWithCustomWebsiteAndDefaultCategoryTest.xml @@ -19,7 +19,7 @@ </annotations> <before> - <actionGroup ref = "LoginAsAdmin" stepKey="loginAsAdmin"/> + <actionGroup ref="AdminLoginActionGroup" stepKey="loginAsAdmin"/> <!--Create website--> <actionGroup ref="AdminCreateWebsiteActionGroup" stepKey="createWebsite"> <argument name="newWebsiteName" value="{{customWebsite.name}}"/> @@ -30,7 +30,7 @@ <actionGroup ref="AdminDeleteWebsiteActionGroup" stepKey="deleteWebsite"> <argument name="websiteName" value="{{customWebsite.name}}"/> </actionGroup> - <actionGroup ref="logout" stepKey="logout"/> + <actionGroup ref="AdminLogoutActionGroup" stepKey="logout"/> </after> <!--Create custom store group with custom website and default category and verify AssertStoreGroupSuccessSaveMessage--> diff --git a/app/code/Magento/Store/Test/Mftf/Test/AdminCreateStoreGroupWithCustomWebsiteAndRootCategoryTest.xml b/app/code/Magento/Store/Test/Mftf/Test/AdminCreateStoreGroupWithCustomWebsiteAndRootCategoryTest.xml index 27037f45f3835..e541204a636e2 100644 --- a/app/code/Magento/Store/Test/Mftf/Test/AdminCreateStoreGroupWithCustomWebsiteAndRootCategoryTest.xml +++ b/app/code/Magento/Store/Test/Mftf/Test/AdminCreateStoreGroupWithCustomWebsiteAndRootCategoryTest.xml @@ -19,7 +19,7 @@ </annotations> <before> - <actionGroup ref = "LoginAsAdmin" stepKey="loginAsAdmin"/> + <actionGroup ref="AdminLoginActionGroup" stepKey="loginAsAdmin"/> <!--Create root category--> <createData entity="NewRootCategory" stepKey="rootCategory"/> <createData entity="SimpleRootSubCategory" stepKey="category"> @@ -38,7 +38,7 @@ </actionGroup> <!--Delete root category--> <deleteData stepKey="deleteRootCategory" createDataKey="rootCategory"/> - <actionGroup ref="logout" stepKey="logout"/> + <actionGroup ref="AdminLogoutActionGroup" stepKey="logout"/> </after> <!--Create custom store group with custom website and root category and verify AssertStoreGroupSuccessSaveMessage--> diff --git a/app/code/Magento/Store/Test/Mftf/Test/AdminCreateStoreGroupWithDefaultWebsiteAndDefaultCategoryTest.xml b/app/code/Magento/Store/Test/Mftf/Test/AdminCreateStoreGroupWithDefaultWebsiteAndDefaultCategoryTest.xml index 0db65501b4112..809210f3bc436 100644 --- a/app/code/Magento/Store/Test/Mftf/Test/AdminCreateStoreGroupWithDefaultWebsiteAndDefaultCategoryTest.xml +++ b/app/code/Magento/Store/Test/Mftf/Test/AdminCreateStoreGroupWithDefaultWebsiteAndDefaultCategoryTest.xml @@ -19,13 +19,13 @@ </annotations> <before> - <actionGroup ref = "LoginAsAdmin" stepKey="loginAsAdmin"/> + <actionGroup ref="AdminLoginActionGroup" stepKey="loginAsAdmin"/> </before> <after> <actionGroup ref="DeleteCustomStoreActionGroup" stepKey="deleteStoreGroup"> <argument name="storeGroupName" value="SecondStoreGroupUnique.name"/> </actionGroup> - <actionGroup ref="logout" stepKey="logout"/> + <actionGroup ref="AdminLogoutActionGroup" stepKey="logout"/> </after> <!--Create custom store group with default website and default category and verify AssertStoreGroupSuccessSaveMessage--> diff --git a/app/code/Magento/Store/Test/Mftf/Test/AdminCreateStoreViewStatusDisabledVerifyBackendAndFrontendTest.xml b/app/code/Magento/Store/Test/Mftf/Test/AdminCreateStoreViewStatusDisabledVerifyBackendAndFrontendTest.xml index 2d8fbc72263d4..f2bfc7f7cea76 100644 --- a/app/code/Magento/Store/Test/Mftf/Test/AdminCreateStoreViewStatusDisabledVerifyBackendAndFrontendTest.xml +++ b/app/code/Magento/Store/Test/Mftf/Test/AdminCreateStoreViewStatusDisabledVerifyBackendAndFrontendTest.xml @@ -17,13 +17,13 @@ <group value="mtf_migrated"/> </annotations> <before> - <actionGroup ref = "LoginAsAdmin" stepKey="loginAsAdmin"/> + <actionGroup ref="AdminLoginActionGroup" stepKey="loginAsAdmin"/> </before> <after> <actionGroup ref="AdminDeleteStoreViewActionGroup" stepKey="deleteStoreView"> <argument name="customStore" value="storeViewDataDisabled"/> </actionGroup> - <actionGroup ref="logout" stepKey="logout"/> + <actionGroup ref="AdminLogoutActionGroup" stepKey="logout"/> </after> <!--Create store view--> diff --git a/app/code/Magento/Store/Test/Mftf/Test/AdminCreateStoreViewStatusEnabledVerifyBackendAndFrontendTest.xml b/app/code/Magento/Store/Test/Mftf/Test/AdminCreateStoreViewStatusEnabledVerifyBackendAndFrontendTest.xml index 150e1082352cf..5f2472a0c52ee 100644 --- a/app/code/Magento/Store/Test/Mftf/Test/AdminCreateStoreViewStatusEnabledVerifyBackendAndFrontendTest.xml +++ b/app/code/Magento/Store/Test/Mftf/Test/AdminCreateStoreViewStatusEnabledVerifyBackendAndFrontendTest.xml @@ -17,13 +17,13 @@ <group value="mtf_migrated"/> </annotations> <before> - <actionGroup ref = "LoginAsAdmin" stepKey="loginAsAdmin"/> + <actionGroup ref="AdminLoginActionGroup" stepKey="loginAsAdmin"/> </before> <after> <actionGroup ref="AdminDeleteStoreViewActionGroup" stepKey="deleteStoreView"> <argument name="customStore" value="storeViewData"/> </actionGroup> - <actionGroup ref="logout" stepKey="logout"/> + <actionGroup ref="AdminLogoutActionGroup" stepKey="logout"/> </after> <!--Create store view--> diff --git a/app/code/Magento/Store/Test/Mftf/Test/AdminCreateStoreViewTest.xml b/app/code/Magento/Store/Test/Mftf/Test/AdminCreateStoreViewTest.xml index e20eb70ae6f45..4171aa6f08915 100644 --- a/app/code/Magento/Store/Test/Mftf/Test/AdminCreateStoreViewTest.xml +++ b/app/code/Magento/Store/Test/Mftf/Test/AdminCreateStoreViewTest.xml @@ -20,7 +20,7 @@ </annotations> <before> - <actionGroup ref="LoginActionGroup" stepKey="loginAsAdmin"/> + <actionGroup ref="AdminLoginActionGroup" stepKey="loginAsAdmin"/> <actionGroup ref="AdminCreateStoreViewActionGroup" stepKey="createStoreView" /> </before> @@ -29,7 +29,7 @@ <argument name="customStore" value="customStore"/> </actionGroup> <click selector="{{AdminStoresGridSection.resetButton}}" stepKey="resetSearchFilter"/> - <actionGroup ref="logout" stepKey="logout"/> + <actionGroup ref="AdminLogoutActionGroup" stepKey="logout"/> </after> <!--Filter grid and see created store view--> diff --git a/app/code/Magento/Store/Test/Mftf/Test/AdminCreateWebsiteTest.xml b/app/code/Magento/Store/Test/Mftf/Test/AdminCreateWebsiteTest.xml index a8782acb1eb07..d25e7dbb0056a 100644 --- a/app/code/Magento/Store/Test/Mftf/Test/AdminCreateWebsiteTest.xml +++ b/app/code/Magento/Store/Test/Mftf/Test/AdminCreateWebsiteTest.xml @@ -19,13 +19,13 @@ </annotations> <before> - <actionGroup ref="LoginAsAdmin" stepKey="loginAsAdmin"/> + <actionGroup ref="AdminLoginActionGroup" stepKey="loginAsAdmin"/> </before> <after> <actionGroup ref="AdminDeleteWebsiteActionGroup" stepKey="deleteWebsite"> <argument name="websiteName" value="{{customWebsite.name}}"/> </actionGroup> - <actionGroup ref="logout" stepKey="logout"/> + <actionGroup ref="AdminLogoutActionGroup" stepKey="logout"/> </after> <!--Create website and AssertWebsiteSuccessSaveMessage--> diff --git a/app/code/Magento/Store/Test/Mftf/Test/AdminDeleteDefaultStoreViewTest.xml b/app/code/Magento/Store/Test/Mftf/Test/AdminDeleteDefaultStoreViewTest.xml index 85fd8561f90b5..ef4590eaff741 100644 --- a/app/code/Magento/Store/Test/Mftf/Test/AdminDeleteDefaultStoreViewTest.xml +++ b/app/code/Magento/Store/Test/Mftf/Test/AdminDeleteDefaultStoreViewTest.xml @@ -17,10 +17,10 @@ <group value="store"/> </annotations> <before> - <actionGroup ref = "LoginAsAdmin" stepKey="loginAsAdmin"/> + <actionGroup ref="AdminLoginActionGroup" stepKey="loginAsAdmin"/> </before> <after> - <actionGroup ref="logout" stepKey="logout"/> + <actionGroup ref="AdminLogoutActionGroup" stepKey="logout"/> </after> <!--Create custom store view--> <actionGroup ref="AdminCreateStoreViewActionGroup" stepKey="createNewStoreView"> diff --git a/app/code/Magento/Store/Test/Mftf/Test/AdminDeleteStoreGroupTest.xml b/app/code/Magento/Store/Test/Mftf/Test/AdminDeleteStoreGroupTest.xml index 185cf87531d9a..349bcfe8f31e0 100644 --- a/app/code/Magento/Store/Test/Mftf/Test/AdminDeleteStoreGroupTest.xml +++ b/app/code/Magento/Store/Test/Mftf/Test/AdminDeleteStoreGroupTest.xml @@ -20,7 +20,7 @@ <before> <magentoCLI command="config:set system/backup/functionality_enabled 1" stepKey="setEnableBackupToYes"/> - <actionGroup ref = "LoginAsAdmin" stepKey="loginAsAdmin"/> + <actionGroup ref="AdminLoginActionGroup" stepKey="loginAsAdmin"/> <!--Create custom store group--> <actionGroup ref="AdminCreateNewStoreGroupActionGroup" stepKey="createNewCustomStoreGroup"> <argument name="website" value="{{_defaultWebsite.name}}"/> @@ -30,7 +30,7 @@ </before> <after> <magentoCLI command="config:set system/backup/functionality_enabled 0" stepKey="setEnableBackupToNo"/> - <actionGroup ref="logout" stepKey="logout"/> + <actionGroup ref="AdminLogoutActionGroup" stepKey="logout"/> </after> <!--Delete custom store group and verify AssertStoreGroupSuccessDeleteAndBackupMessages--> diff --git a/app/code/Magento/Store/Test/Mftf/Test/AdminDeleteStoreViewTest.xml b/app/code/Magento/Store/Test/Mftf/Test/AdminDeleteStoreViewTest.xml index df6fc391b2972..305b4540668e4 100644 --- a/app/code/Magento/Store/Test/Mftf/Test/AdminDeleteStoreViewTest.xml +++ b/app/code/Magento/Store/Test/Mftf/Test/AdminDeleteStoreViewTest.xml @@ -19,7 +19,7 @@ </annotations> <before> <magentoCLI command="config:set system/backup/functionality_enabled 1" stepKey="setEnableBackupToYes"/> - <actionGroup ref = "LoginAsAdmin" stepKey="loginAsAdmin"/> + <actionGroup ref="AdminLoginActionGroup" stepKey="loginAsAdmin"/> <!--Create custom store view--> <actionGroup ref="AdminCreateStoreViewActionGroup" stepKey="createNewStoreView"> <argument name="StoreGroup" value="_defaultStoreGroup"/> @@ -28,7 +28,7 @@ </before> <after> <magentoCLI command="config:set system/backup/functionality_enabled 0" stepKey="setEnableBackupToNo"/> - <actionGroup ref="logout" stepKey="logout"/> + <actionGroup ref="AdminLogoutActionGroup" stepKey="logout"/> </after> <!--Delete custom store view and verify AssertStoreSuccessDeleteMessage And BackupMessage--> @@ -51,8 +51,7 @@ </actionGroup> <!--Go to storefront and verify AssertStoreNotOnFrontend--> - <amOnPage url="{{StorefrontHomePage.url}}" stepKey="goToStorefrontPage"/> - <waitForPageLoad stepKey="waitForStorefrontHomePageLoad"/> + <actionGroup ref="StorefrontOpenHomePageActionGroup" stepKey="goToStorefrontPage"/> <dontSee selector="{{StorefrontHeaderSection.storeViewList(storeViewData.name)}}" stepKey="dontSeeAssertStoreViewNameOnStorefront"/> </test> </tests> diff --git a/app/code/Magento/Store/Test/Mftf/Test/AdminMoveStoreToOtherGroupSameWebsiteTest.xml b/app/code/Magento/Store/Test/Mftf/Test/AdminMoveStoreToOtherGroupSameWebsiteTest.xml index f1983dca53bf7..812fe6f2d4144 100644 --- a/app/code/Magento/Store/Test/Mftf/Test/AdminMoveStoreToOtherGroupSameWebsiteTest.xml +++ b/app/code/Magento/Store/Test/Mftf/Test/AdminMoveStoreToOtherGroupSameWebsiteTest.xml @@ -19,7 +19,7 @@ </annotations> <before> - <actionGroup ref = "LoginAsAdmin" stepKey="loginAsAdmin"/> + <actionGroup ref="AdminLoginActionGroup" stepKey="loginAsAdmin"/> <!-- Create first store --> <actionGroup ref="AdminCreateNewStoreGroupActionGroup" stepKey="createFirstStore"> <argument name="website" value="{{_defaultWebsite.name}}"/> @@ -50,7 +50,7 @@ <actionGroup ref="DeleteCustomStoreActionGroup" stepKey="deleteSecondStore"> <argument name="storeGroupName" value="customStoreGroup.name"/> </actionGroup> - <actionGroup ref="logout" stepKey="logout"/> + <actionGroup ref="AdminLogoutActionGroup" stepKey="logout"/> </after> <!--Search created second store view in grid--> diff --git a/app/code/Magento/Store/Test/Mftf/Test/AdminUpdateStoreGroupAcceptAlertAndVerifyStoreViewFormTest.xml b/app/code/Magento/Store/Test/Mftf/Test/AdminUpdateStoreGroupAcceptAlertAndVerifyStoreViewFormTest.xml index 16f830224f7f4..09a33d5eb86a6 100644 --- a/app/code/Magento/Store/Test/Mftf/Test/AdminUpdateStoreGroupAcceptAlertAndVerifyStoreViewFormTest.xml +++ b/app/code/Magento/Store/Test/Mftf/Test/AdminUpdateStoreGroupAcceptAlertAndVerifyStoreViewFormTest.xml @@ -19,7 +19,7 @@ </annotations> <before> - <actionGroup ref = "LoginAsAdmin" stepKey="loginAsAdmin"/> + <actionGroup ref="AdminLoginActionGroup" stepKey="loginAsAdmin"/> <!--Create root category--> <createData entity="NewRootCategory" stepKey="rootCategory"/> <createData entity="SimpleRootSubCategory" stepKey="category"> @@ -44,7 +44,7 @@ </actionGroup> <!--Delete root category--> <deleteData stepKey="deleteRootCategory" createDataKey="rootCategory"/> - <actionGroup ref="logout" stepKey="logout"/> + <actionGroup ref="AdminLogoutActionGroup" stepKey="logout"/> </after> <!--Open created Store group in grid--> diff --git a/app/code/Magento/Store/Test/Mftf/Test/AdminUpdateStoreGroupAndVerifyStoreViewFormTest.xml b/app/code/Magento/Store/Test/Mftf/Test/AdminUpdateStoreGroupAndVerifyStoreViewFormTest.xml index ab204560f11c6..1c5d58c13538e 100644 --- a/app/code/Magento/Store/Test/Mftf/Test/AdminUpdateStoreGroupAndVerifyStoreViewFormTest.xml +++ b/app/code/Magento/Store/Test/Mftf/Test/AdminUpdateStoreGroupAndVerifyStoreViewFormTest.xml @@ -19,7 +19,7 @@ </annotations> <before> - <actionGroup ref = "LoginAsAdmin" stepKey="loginAsAdmin"/> + <actionGroup ref="AdminLoginActionGroup" stepKey="loginAsAdmin"/> <!--Create custom store group--> <actionGroup ref="AdminCreateNewStoreGroupActionGroup" stepKey="createNewCustomStoreGroup"> <argument name="website" value="{{_defaultWebsite.name}}"/> @@ -34,7 +34,7 @@ <actionGroup ref="DeleteCustomStoreActionGroup" stepKey="deleteUpdatedStoreGroup"> <argument name="storeGroupName" value="SecondStoreGroupUnique.name"/> </actionGroup> - <actionGroup ref="logout" stepKey="logout"/> + <actionGroup ref="AdminLogoutActionGroup" stepKey="logout"/> </after> <!--Open created Store group in grid--> diff --git a/app/code/Magento/Store/Test/Mftf/Test/AdminUpdateStoreViewTest.xml b/app/code/Magento/Store/Test/Mftf/Test/AdminUpdateStoreViewTest.xml index 26dee6c632928..c7c846c51af4d 100644 --- a/app/code/Magento/Store/Test/Mftf/Test/AdminUpdateStoreViewTest.xml +++ b/app/code/Magento/Store/Test/Mftf/Test/AdminUpdateStoreViewTest.xml @@ -18,7 +18,7 @@ <group value="mtf_migrated"/> </annotations> <before> - <actionGroup ref = "LoginAsAdmin" stepKey="loginAsAdmin"/> + <actionGroup ref="AdminLoginActionGroup" stepKey="loginAsAdmin"/> <!--Create custom store view--> <actionGroup ref="AdminCreateStoreViewActionGroup" stepKey="createNewStoreView"> <argument name="StoreGroup" value="_defaultStoreGroup"/> @@ -32,7 +32,7 @@ <actionGroup ref="AdminDeleteStoreViewActionGroup" stepKey="deleteUpdatedStoreView"> <argument name="customStore" value="SecondStoreUnique"/> </actionGroup> - <actionGroup ref="logout" stepKey="logout"/> + <actionGroup ref="AdminLogoutActionGroup" stepKey="logout"/> </after> <!--Search created store view in grid--> @@ -71,8 +71,7 @@ <see selector="{{AdminConfigSection.defaultConfigDropdown}}" userInput="{{SecondStoreUnique.name}}" stepKey="seeAssertUpdateStoreViewInDefaultConfigDropdown"/> <!--Go to storefront and verify AssertStoreFrontend--> - <amOnPage url="{{StorefrontHomePage.url}}" stepKey="goToStorefrontPage"/> - <waitForPageLoad stepKey="waitForStorefrontHomePageLoad"/> + <actionGroup ref="StorefrontOpenHomePageActionGroup" stepKey="goToStorefrontPage"/> <click selector="{{StorefrontHeaderSection.storeViewSwitcher}}" stepKey="selectStoreSwitcher"/> <waitForPageLoad stepKey="waitForFirstStoreView"/> <see selector="{{StorefrontHeaderSection.storeViewDropdown}}" userInput="{{storeViewData.name}}" stepKey="seeAssertStoreViewOnStorefront"/> diff --git a/app/code/Magento/Store/Test/Mftf/Test/AdminUpdateWebsiteTest.xml b/app/code/Magento/Store/Test/Mftf/Test/AdminUpdateWebsiteTest.xml index 5c4ecb87dda53..1c1c0ae30f6b1 100644 --- a/app/code/Magento/Store/Test/Mftf/Test/AdminUpdateWebsiteTest.xml +++ b/app/code/Magento/Store/Test/Mftf/Test/AdminUpdateWebsiteTest.xml @@ -19,7 +19,7 @@ </annotations> <before> - <actionGroup ref = "LoginAsAdmin" stepKey="loginAsAdmin"/> + <actionGroup ref="AdminLoginActionGroup" stepKey="loginAsAdmin"/> <!--Create website--> <actionGroup ref="AdminCreateWebsiteActionGroup" stepKey="createWebsite"> <argument name="newWebsiteName" value="{{customWebsite.name}}"/> @@ -30,7 +30,7 @@ <actionGroup ref="AdminDeleteWebsiteActionGroup" stepKey="deleteWebsite"> <argument name="websiteName" value="{{updateCustomWebsite.name}}"/> </actionGroup> - <actionGroup ref="logout" stepKey="logout"/> + <actionGroup ref="AdminLogoutActionGroup" stepKey="logout"/> </after> <!--Search created custom website in grid--> diff --git a/app/code/Magento/Store/Test/Unit/App/Action/Plugin/StoreCheckTest.php b/app/code/Magento/Store/Test/Unit/App/Action/Plugin/StoreCheckTest.php index 93bd4f2ccfba4..9ecd640b246af 100644 --- a/app/code/Magento/Store/Test/Unit/App/Action/Plugin/StoreCheckTest.php +++ b/app/code/Magento/Store/Test/Unit/App/Action/Plugin/StoreCheckTest.php @@ -5,37 +5,38 @@ */ namespace Magento\Store\Test\Unit\App\Action\Plugin; +use Magento\Framework\App\Action\AbstractAction; +use Magento\Store\App\Action\Plugin\StoreCheck; +use Magento\Store\Model\Store; +use Magento\Store\Model\StoreManagerInterface; +use PHPUnit\Framework\MockObject\MockObject; + class StoreCheckTest extends \PHPUnit\Framework\TestCase { /** - * @var \Magento\Store\App\Action\Plugin\StoreCheck + * @var StoreCheck */ protected $_plugin; /** - * @var \PHPUnit_Framework_MockObject_MockObject + * @var StoreManagerInterface|MockObject */ protected $_storeManagerMock; /** - * @var \PHPUnit_Framework_MockObject_MockObject + * @var Store|MockObject */ protected $_storeMock; /** - * @var \Magento\Framework\App\Action\AbstractAction|\PHPUnit_Framework_MockObject_MockObject + * @var AbstractAction|MockObject */ protected $subjectMock; - /** - * @var \Magento\Framework\App\RequestInterface|\PHPUnit_Framework_MockObject_MockObject - */ - protected $requestMock; - protected function setUp() { - $this->_storeManagerMock = $this->createMock(\Magento\Store\Model\StoreManagerInterface::class); - $this->_storeMock = $this->createMock(\Magento\Store\Model\Store::class); + $this->_storeManagerMock = $this->createMock(StoreManagerInterface::class); + $this->_storeMock = $this->createMock(Store::class); $this->_storeManagerMock->expects( $this->any() )->method( @@ -43,28 +44,27 @@ protected function setUp() )->will( $this->returnValue($this->_storeMock) ); - $this->requestMock = $this->createMock(\Magento\Framework\App\RequestInterface::class); - $this->subjectMock = $this->getMockBuilder(\Magento\Framework\App\Action\AbstractAction::class) + $this->subjectMock = $this->getMockBuilder(AbstractAction::class) ->disableOriginalConstructor() ->getMockForAbstractClass(); - $this->_plugin = new \Magento\Store\App\Action\Plugin\StoreCheck($this->_storeManagerMock); + $this->_plugin = new StoreCheck($this->_storeManagerMock); } /** * @expectedException \Magento\Framework\Exception\State\InitException * @expectedExceptionMessage Current store is not active. */ - public function testBeforeDispatchWhenStoreNotActive() + public function testBeforeExecuteWhenStoreNotActive() { $this->_storeMock->expects($this->any())->method('isActive')->will($this->returnValue(false)); - $this->_plugin->beforeDispatch($this->subjectMock, $this->requestMock); + $this->_plugin->beforeExecute($this->subjectMock); } - public function testBeforeDispatchWhenStoreIsActive() + public function testBeforeExecuteWhenStoreIsActive() { $this->_storeMock->expects($this->any())->method('isActive')->will($this->returnValue(true)); - $result = $this->_plugin->beforeDispatch($this->subjectMock, $this->requestMock); + $result = $this->_plugin->beforeExecute($this->subjectMock); $this->assertNull($result); } } diff --git a/app/code/Magento/Store/Test/Unit/Controller/Store/RedirectTest.php b/app/code/Magento/Store/Test/Unit/Controller/Store/RedirectTest.php new file mode 100755 index 0000000000000..cb92f14e6227c --- /dev/null +++ b/app/code/Magento/Store/Test/Unit/Controller/Store/RedirectTest.php @@ -0,0 +1,328 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +declare(strict_types=1); + +namespace Magento\Store\Test\Unit\Controller\Store; + +use Magento\Framework\App\Action\Context; +use Magento\Framework\App\ActionInterface; +use Magento\Framework\App\RequestInterface; +use Magento\Framework\App\Response\RedirectInterface; +use Magento\Framework\App\ResponseInterface; +use Magento\Framework\Exception\NoSuchEntityException; +use Magento\Framework\Message\ManagerInterface; +use Magento\Framework\Session\SidResolverInterface; +use Magento\Framework\TestFramework\Unit\Helper\ObjectManager as ObjectManagerHelper; +use Magento\Store\Api\StoreRepositoryInterface; +use Magento\Store\Api\StoreResolverInterface; +use Magento\Store\Controller\Store\Redirect; +use Magento\Store\Model\Store; +use Magento\Store\Model\StoreManagerInterface; +use Magento\Store\Model\StoreResolver; +use Magento\Store\Model\StoreSwitcher\HashGenerator; +use PHPUnit\Framework\MockObject\MockObject; +use PHPUnit\Framework\TestCase; + +/** + * Test class for redirect controller + * @SuppressWarnings(PHPMD.CouplingBetweenObjects) + */ +class RedirectTest extends TestCase +{ + /** + * Stub for default store view code + */ + private const STUB_DEFAULT_STORE_VIEW_CODE = 'default'; + + /** + * Stub for default store code + */ + private const STUB_STORE_CODE = 'sv1'; + + /** + * @var StoreManagerInterface|MockObject + */ + private $storeManagerMock; + + /** + * @var StoreRepositoryInterface|MockObject + */ + private $storeRepositoryMock; + + /** + * @var RequestInterface|MockObject + */ + private $requestMock; + + /** + * @var StoreResolverInterface|MockObject + */ + private $storeResolverMock; + + /** + * @var RedirectInterface|MockObject + */ + private $redirectMock; + + /** + * @var ResponseInterface|MockObject + */ + private $responseMock; + + /** + * @var ManagerInterface|MockObject + */ + private $messageManagerMock; + + /** + * @var Store|MockObject + */ + private $fromStoreMock; + + /** + * @var Store|MockObject + */ + private $targetStoreMock; + + /** + * @var Store|MockObject + */ + private $currentStoreMock; + + /** + * @var SidResolverInterface|MockObject + */ + private $sidResolverMock; + + /** + * @var HashGenerator|MockObject + */ + private $hashGeneratorMock; + + /** + * @var Redirect + */ + private $model; + + /** + * @inheritDoc + */ + protected function setUp() + { + $this->storeManagerMock = $this->createMock(StoreManagerInterface::class); + $this->requestMock = $this->getMockBuilder(RequestInterface::class) + ->disableOriginalConstructor() + ->setMethods(['getParam']) + ->getMockForAbstractClass(); + $this->redirectMock = $this->getMockBuilder(RedirectInterface::class) + ->disableOriginalConstructor() + ->setMethods(['redirect']) + ->getMockForAbstractClass(); + $this->storeResolverMock = $this->getMockBuilder(StoreResolverInterface::class) + ->disableOriginalConstructor() + ->setMethods(['getCurrentStoreId']) + ->getMockForAbstractClass(); + $this->storeRepositoryMock = $this->getMockBuilder(StoreRepositoryInterface::class) + ->disableOriginalConstructor() + ->setMethods(['getById', 'get']) + ->getMockForAbstractClass(); + $this->messageManagerMock = $this->getMockBuilder(ManagerInterface::class) + ->disableOriginalConstructor() + ->setMethods(['addErrorMessage']) + ->getMockForAbstractClass(); + $this->responseMock = $this->getMockBuilder(ResponseInterface::class) + ->disableOriginalConstructor() + ->getMockForAbstractClass(); + $this->fromStoreMock = $this->getMockBuilder(Store::class) + ->disableOriginalConstructor() + ->setMethods(['getCode']) + ->getMockForAbstractClass(); + $this->targetStoreMock = $this->getMockBuilder(Store::class) + ->disableOriginalConstructor() + ->setMethods(['getCode']) + ->getMockForAbstractClass(); + $this->sidResolverMock = $this->getMockBuilder(SidResolverInterface::class) + ->disableOriginalConstructor() + ->setMethods(['getUseSessionInUrl']) + ->getMockForAbstractClass(); + $this->hashGeneratorMock = $this->createMock(HashGenerator::class); + + $this->currentStoreMock = $this->getMockBuilder(Store::class) + ->disableOriginalConstructor() + ->setMethods(['getBaseUrl']) + ->getMock(); + $this->storeRepositoryMock + ->expects($this->once()) + ->method('getById') + ->willReturn($this->currentStoreMock); + $this->storeResolverMock + ->expects($this->once()) + ->method('getCurrentStoreId') + ->willReturnSelf(); + + $objectManager = new ObjectManagerHelper($this); + $context = $objectManager->getObject( + Context::class, + [ + '_request' => $this->requestMock, + '_redirect' => $this->redirectMock, + '_response' => $this->responseMock, + 'messageManager' => $this->messageManagerMock, + ] + ); + $this->model = $objectManager->getObject( + Redirect::class, + [ + 'storeManager' => $this->storeManagerMock, + 'storeRepository' => $this->storeRepositoryMock, + 'storeResolver' => $this->storeResolverMock, + 'sidResolver' => $this->sidResolverMock, + 'hashGenerator' => $this->hashGeneratorMock, + 'context' => $context, + ] + ); + } + + /** + * Verify redirect controller + * + * @param string $defaultStoreViewCode + * @param string $storeCode + * + * @dataProvider getConfigDataProvider + * @return void + */ + public function testRedirect(string $defaultStoreViewCode, string $storeCode): void + { + $this->requestMock + ->expects($this->exactly(3)) + ->method('getParam') + ->withConsecutive( + [StoreResolver::PARAM_NAME], + ['___from_store'], + [ActionInterface::PARAM_NAME_URL_ENCODED] + )->willReturnOnConsecutiveCalls( + $storeCode, + $defaultStoreViewCode, + $defaultStoreViewCode + ); + $this->storeRepositoryMock + ->expects($this->exactly(2)) + ->method('get') + ->willReturnMap([ + [$defaultStoreViewCode, $this->fromStoreMock], + [$storeCode, $this->targetStoreMock], + ]); + $this->fromStoreMock + ->expects($this->once()) + ->method('getCode') + ->willReturn($defaultStoreViewCode); + $this->hashGeneratorMock + ->expects($this->once()) + ->method('generateHash') + ->with($this->fromStoreMock) + ->willReturn([]); + $this->storeManagerMock + ->expects($this->once()) + ->method('setCurrentStore') + ->with($this->targetStoreMock); + $this->redirectMock + ->expects($this->once()) + ->method('redirect') + ->with( + $this->responseMock, + 'stores/store/switch', + ['_nosid' => true, + '_query' => [ + 'uenc' => $defaultStoreViewCode, + '___from_store' => $defaultStoreViewCode, + '___store' => $storeCode + ] + ] + ); + + $this->assertEquals(null, $this->model->execute()); + } + + /** + * Verify execute with exception + * + * @param string $defaultStoreViewCode + * @param string $storeCode + * @return void + * @dataProvider getConfigDataProvider + */ + public function testRedirectWithThrowsException(string $defaultStoreViewCode, string $storeCode): void + { + $this->requestMock + ->expects($this->exactly(2)) + ->method('getParam') + ->withConsecutive( + [StoreResolver::PARAM_NAME], + ['___from_store'] + )->willReturnOnConsecutiveCalls( + $storeCode, + $defaultStoreViewCode + ); + $this->storeRepositoryMock + ->expects($this->once()) + ->method('get') + ->with($defaultStoreViewCode) + ->willThrowException(new NoSuchEntityException()); + $this->messageManagerMock + ->expects($this->once()) + ->method('addErrorMessage') + ->with("Requested store is not found ({$defaultStoreViewCode})") + ->willReturnSelf(); + $this->currentStoreMock + ->expects($this->once()) + ->method('getBaseUrl') + ->willReturnSelf(); + $this->redirectMock + ->expects($this->once()) + ->method('redirect') + ->with($this->responseMock, $this->currentStoreMock) + ->willReturnSelf(); + + $this->assertEquals(null, $this->model->execute()); + } + + /** + * Verify redirect target is null + * + * @return void + */ + public function testRedirectTargetIsNull(): void + { + $this->requestMock + ->expects($this->exactly(2)) + ->method('getParam') + ->withConsecutive( + [StoreResolver::PARAM_NAME], + ['___from_store'] + )->willReturnOnConsecutiveCalls( + null, + null + ); + $this->storeRepositoryMock + ->expects($this->never()) + ->method('get'); + + $this->assertEquals($this->responseMock, $this->model->execute()); + } + + /** + * Data provider + * + * @return array + */ + public function getConfigDataProvider(): array + { + return [ + [self::STUB_DEFAULT_STORE_VIEW_CODE, self::STUB_STORE_CODE] + ]; + } +} diff --git a/app/code/Magento/Store/Test/Unit/Model/InformationTest.php b/app/code/Magento/Store/Test/Unit/Model/InformationTest.php index 0fc2f635f069d..a30446165bb29 100644 --- a/app/code/Magento/Store/Test/Unit/Model/InformationTest.php +++ b/app/code/Magento/Store/Test/Unit/Model/InformationTest.php @@ -63,7 +63,7 @@ protected function setUp() $this->store->expects($this->any()) ->method('getConfig') ->willReturnCallback(function ($path) use ($mockData) { - return isset($mockData[$path]) ? $mockData[$path] : null; + return $mockData[$path] ?? null; }); $this->renderer = $this->getMockBuilder(\Magento\Store\Model\Address\Renderer::class) diff --git a/app/code/Magento/Store/etc/di.xml b/app/code/Magento/Store/etc/di.xml index 3fa9c8314fdd1..5bd8f6e2349fc 100644 --- a/app/code/Magento/Store/etc/di.xml +++ b/app/code/Magento/Store/etc/di.xml @@ -63,9 +63,11 @@ </type> <preference for="Magento\Framework\App\ScopeResolverInterface" type="Magento\Store\Model\Resolver\Store" /> <preference for="Magento\Framework\App\Router\PathConfigInterface" type="Magento\Store\Model\PathConfig" /> - <type name="Magento\Framework\App\Action\AbstractAction"> - <plugin name="storeCheck" type="Magento\Store\App\Action\Plugin\StoreCheck" sortOrder="10"/> - <plugin name="designLoader" type="Magento\Framework\App\Action\Plugin\Design" /> + <type name="Magento\Framework\App\ActionInterface"> + <plugin name="storeCheck" type="Magento\Store\App\Action\Plugin\StoreCheck"/> + <plugin name="designLoader" type="Magento\Framework\App\Action\Plugin\LoadDesignPlugin"/> + <plugin name="eventDispatch" type="Magento\Framework\App\Action\Plugin\EventDispatchPlugin"/> + <plugin name="actionFlagNoDispatch" type="Magento\Framework\App\Action\Plugin\ActionFlagNoDispatchPlugin"/> </type> <type name="Magento\Framework\Url\SecurityInfo"> <plugin name="storeUrlSecurityInfo" type="Magento\Store\Url\Plugin\SecurityInfo"/> @@ -436,7 +438,7 @@ <item name="cleanTargetUrl" xsi:type="object">Magento\Store\Model\StoreSwitcher\CleanTargetUrl</item> <item name="manageStoreCookie" xsi:type="object">Magento\Store\Model\StoreSwitcher\ManageStoreCookie</item> <item name="managePrivateContent" xsi:type="object">Magento\Store\Model\StoreSwitcher\ManagePrivateContent</item> - <item name="hashGenerator" xsi:type="object" sortOrder="1000">Magento\Store\Model\StoreSwitcher\HashGenerator</item> + <item name="hashProcessor" xsi:type="object" sortOrder="1000">Magento\Store\Model\StoreSwitcher\HashProcessor</item> </argument> </arguments> </type> diff --git a/app/code/Magento/Store/etc/frontend/sections.xml b/app/code/Magento/Store/etc/frontend/sections.xml index b7dbfe405263b..85f3627e05c95 100644 --- a/app/code/Magento/Store/etc/frontend/sections.xml +++ b/app/code/Magento/Store/etc/frontend/sections.xml @@ -7,6 +7,10 @@ --> <config xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="urn:magento:module:Magento_Customer:etc/sections.xsd"> - <action name="stores/store/switch"/> - <action name="stores/store/switchrequest"/> + <action name="stores/store/switch"> + <section name="*"/> + </action> + <action name="stores/store/switchrequest"> + <section name="*"/> + </action> </config> diff --git a/app/code/Magento/Store/registration.php b/app/code/Magento/Store/registration.php index 62be25fbdf6ab..64b5f9df52c0c 100644 --- a/app/code/Magento/Store/registration.php +++ b/app/code/Magento/Store/registration.php @@ -4,6 +4,6 @@ * See COPYING.txt for license details. */ -use \Magento\Framework\Component\ComponentRegistrar; +use Magento\Framework\Component\ComponentRegistrar; ComponentRegistrar::register(ComponentRegistrar::MODULE, 'Magento_Store', __DIR__); diff --git a/app/code/Magento/StoreGraphQl/Controller/HttpRequestValidator/StoreValidator.php b/app/code/Magento/StoreGraphQl/Controller/HttpRequestValidator/StoreValidator.php index 144905d728141..8011960915726 100644 --- a/app/code/Magento/StoreGraphQl/Controller/HttpRequestValidator/StoreValidator.php +++ b/app/code/Magento/StoreGraphQl/Controller/HttpRequestValidator/StoreValidator.php @@ -42,16 +42,30 @@ public function validate(HttpRequestInterface $request): void { $headerValue = $request->getHeader('Store'); if (!empty($headerValue)) { - $storeCode = ltrim(rtrim($headerValue)); - $stores = $this->storeManager->getStores(false, true); - if (!isset($stores[$storeCode])) { - if (strtolower($storeCode) !== 'default') { - $this->storeManager->setCurrentStore(null); - throw new GraphQlInputException( - __("Requested store is not found") - ); - } + $storeCode = trim($headerValue); + if (!$this->isStoreActive($storeCode)) { + $this->storeManager->setCurrentStore(null); + throw new GraphQlInputException(__('Requested store is not found ({$storeCode})')); } } } + + /** + * Check if provided store code corresponds to an active store + * + * @param string $storeCode + * @return bool + */ + private function isStoreActive(string $storeCode): bool + { + $stores = $this->storeManager->getStores(false, true); + if (strtolower($storeCode) === 'default') { + return true; + } + if (isset($stores[$storeCode])) { + return (bool)$stores[$storeCode]->getIsActive(); + } + + return false; + } } diff --git a/app/code/Magento/StoreGraphQl/Test/Unit/StoreValidatorTest.php b/app/code/Magento/StoreGraphQl/Test/Unit/StoreValidatorTest.php new file mode 100644 index 0000000000000..7a4ea46990e00 --- /dev/null +++ b/app/code/Magento/StoreGraphQl/Test/Unit/StoreValidatorTest.php @@ -0,0 +1,146 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +declare(strict_types=1); + +namespace Magento\StoreGraphQl\Test\Unit; + +use Magento\Framework\App\HttpRequestInterface; +use Magento\Framework\GraphQl\Exception\GraphQlInputException; +use Magento\Framework\TestFramework\Unit\Helper\ObjectManager as ObjectManagerHelper; +use Magento\Store\Model\StoreManagerInterface; +use Magento\StoreGraphQl\Controller\HttpRequestValidator\StoreValidator; +use PHPUnit\Framework\MockObject\MockObject; +use PHPUnit\Framework\TestCase; + +/** + * Test for StoreValidator class + * @SuppressWarnings(PHPMD.CouplingBetweenObjects) + */ +class StoreValidatorTest extends TestCase +{ + private const DEFAULT_STORE_VIEW_CODE = 'default'; + private const STORE_CODE = 'sv1'; + + /** + * @var StoreManagerInterface|MockObject + */ + private $storeManagerMock; + + /** + * @var HttpRequestInterface|MockObject + */ + private $requestMock; + + /** + * @var StoreValidator + */ + private $storeValidator; + + /** + * @inheritDoc + */ + public function setUp() + { + $this->storeManagerMock = $this->createMock(StoreManagerInterface::class); + $this->requestMock = $this->getMockBuilder(HttpRequestInterface::class) + ->disableOriginalConstructor() + ->setMethods( + [ + 'isPost', + 'isGet', + 'isPatch', + 'isDelete', + 'isPut', + 'isAjax', + 'getHeader' + ] + ) + ->getMock(); + + $objectManager = new ObjectManagerHelper($this); + + $this->storeValidator = $objectManager->getObject( + StoreValidator::class, + [ + 'storeManager' => $this->storeManagerMock + ] + ); + } + + /** + * Verify validate + * + * @param array $config + * + * @dataProvider getConfigDataProvider + * @throws GraphQlInputException + */ + public function testValidate(array $config): void + { + $this->requestMock + ->expects($this->once()) + ->method('getHeader') + ->with('Store') + ->willReturn($config['store']); + $this->storeManagerMock + ->expects($this->once()) + ->method('getStores') + ->with(false, true) + ->willReturn($config['store']); + $this->storeManagerMock + ->expects($this->once()) + ->method('setCurrentStore') + ->with(null) + ->willReturnSelf(); + $this->expectExceptionMessage('Requested store is not found ({$storeCode})'); + $this->storeValidator->validate($this->requestMock); + } + + /** + * Verify validate with active store + * + * @param array $config + * + * @throws GraphQlInputException + * @dataProvider getConfigDataProvider + */ + public function testValidateWithStoreActive(array $config): void + { + $this->requestMock + ->expects($this->once()) + ->method('getHeader') + ->with('Store') + ->willReturn($config['default']); + $this->storeManagerMock + ->expects($this->once()) + ->method('getStores') + ->with(false, true) + ->willReturn($config['default']); + $this->storeManagerMock + ->expects($this->never()) + ->method('setCurrentStore') + ->with(null) + ->willReturnSelf(); + $this->storeValidator->validate($this->requestMock); + } + + /** + * Data provider + * + * @return array + */ + public function getConfigDataProvider(): array + { + return [ + [ + [ + 'default' => self::DEFAULT_STORE_VIEW_CODE, + 'store' => self::STORE_CODE + ] + ] + ]; + } +} diff --git a/app/code/Magento/StoreGraphQl/etc/schema.graphqls b/app/code/Magento/StoreGraphQl/etc/schema.graphqls index aaef3aa13dbaf..919c94684eb21 100644 --- a/app/code/Magento/StoreGraphQl/etc/schema.graphqls +++ b/app/code/Magento/StoreGraphQl/etc/schema.graphqls @@ -4,13 +4,13 @@ type Query { storeConfig : StoreConfig @resolver(class: "Magento\\StoreGraphQl\\Model\\Resolver\\StoreConfigResolver") @doc(description: "The store config query") @cache(cacheable: false) } -type Website @doc(description: "The type contains information about a website") { - id : Int @doc(description: "The ID number assigned to the website") - name : String @doc(description: "The website name. Websites use this name to identify it easier.") - code : String @doc(description: "A code assigned to the website to identify it") - sort_order : Int @doc(description: "The attribute to use for sorting websites") - default_group_id : String @doc(description: "The default group ID that the website has") - is_default : Boolean @doc(description: "Specifies if this is the default website") +type Website @doc(description: "Website is deprecated because it is should not be used on storefront. The type contains information about a website") { + id : Int @deprecated(reason: "The field should not be used on the storefront.") @doc(description: "The ID number assigned to the website") + name : String @deprecated(reason: "The field should not be used on the storefront.") @doc(description: "The website name. Websites use this name to identify it easier.") + code : String @deprecated(reason: "The field should not be used on the storefront.") @doc(description: "A code assigned to the website to identify it") + sort_order : Int @deprecated(reason: "The field should not be used on the storefront.") @doc(description: "The attribute to use for sorting websites") + default_group_id : String @deprecated(reason: "The field should not be used on the storefront.") @doc(description: "The default group ID that the website has") + is_default : Boolean @deprecated(reason: "The field should not be used on the storefront.") @doc(description: "Specifies if this is the default website") } type StoreConfig @doc(description: "The type contains information about a store config") { diff --git a/app/code/Magento/Swagger/Controller/Index/Index.php b/app/code/Magento/Swagger/Controller/Index/Index.php index 162367aaf81f9..c486989c0b1bc 100644 --- a/app/code/Magento/Swagger/Controller/Index/Index.php +++ b/app/code/Magento/Swagger/Controller/Index/Index.php @@ -3,42 +3,38 @@ * Copyright © Magento, Inc. All rights reserved. * See COPYING.txt for license details. */ +declare(strict_types=1); + namespace Magento\Swagger\Controller\Index; -/** - * Class Index - * - * @package Magento\Swagger\Controller\Index - */ -class Index extends \Magento\Framework\App\Action\Action +use Magento\Framework\App\Action\HttpGetActionInterface; +use Magento\Framework\View\Page\Config as PageConfig; +use Magento\Framework\View\Result\PageFactory as PageFactory; + +class Index implements HttpGetActionInterface { /** - * @var \Magento\Framework\View\Page\Config + * @var PageConfig */ private $pageConfig; /** - * @var \Magento\Framework\View\Result\PageFactory + * @var PageFactory */ private $pageFactory; /** - * @param \Magento\Framework\App\Action\Context $context - * @param \Magento\Framework\View\Page\Config $pageConfig - * @param \Magento\Framework\View\Result\PageFactory $pageFactory + * @param PageConfig $pageConfig + * @param PageFactory $pageFactory */ - public function __construct( - \Magento\Framework\App\Action\Context $context, - \Magento\Framework\View\Page\Config $pageConfig, - \Magento\Framework\View\Result\PageFactory $pageFactory - ) { - parent::__construct($context); + public function __construct(PageConfig $pageConfig, PageFactory $pageFactory) + { $this->pageConfig = $pageConfig; $this->pageFactory = $pageFactory; } /** - * @return \Magento\Framework\View\Result\Page + * @inheritDoc */ public function execute() { diff --git a/app/code/Magento/Swagger/Test/Unit/Controller/Index/IndexTest.php b/app/code/Magento/Swagger/Test/Unit/Controller/Index/IndexTest.php index c409cd6ca7504..abfc40d7e5437 100644 --- a/app/code/Magento/Swagger/Test/Unit/Controller/Index/IndexTest.php +++ b/app/code/Magento/Swagger/Test/Unit/Controller/Index/IndexTest.php @@ -8,29 +8,29 @@ namespace Magento\Swagger\Test\Unit\Controller\Index; -class IndexTest extends \PHPUnit\Framework\TestCase +use Magento\Framework\View\Page\Config as PageConfig; +use Magento\Framework\View\Result\PageFactory; +use Magento\Swagger\Controller\Index\Index; +use PHPUnit\Framework\MockObject\MockObject; +use PHPUnit\Framework\TestCase; + +class IndexTest extends TestCase { public function testExecute() { - $objectManager = new \Magento\Framework\TestFramework\Unit\Helper\ObjectManager($this); - - $pageConfigMock = $this->getMockBuilder(\Magento\Framework\View\Page\Config::class) + /** @var MockObject|PageConfig $pageConfigMock */ + $pageConfigMock = $this->getMockBuilder(PageConfig::class) ->disableOriginalConstructor() ->getMock(); - $resultPageFactory = $this->getMockBuilder(\Magento\Framework\View\Result\PageFactory::class) + /** @var MockObject|PageFactory $resultPageFactory */ + $resultPageFactory = $this->getMockBuilder(PageFactory::class) ->disableOriginalConstructor() ->getMock(); $pageConfigMock->expects($this->once())->method('addBodyClass')->with('swagger-section'); $resultPageFactory->expects($this->once())->method('create'); - $model = $objectManager->getObject( - \Magento\Swagger\Controller\Index\Index::class, - [ - 'pageConfig' => $pageConfigMock, - 'pageFactory' => $resultPageFactory - ] - ); - $model->execute(); + $indexAction = new Index($pageConfigMock, $resultPageFactory); + $indexAction->execute(); } } diff --git a/app/code/Magento/Swagger/registration.php b/app/code/Magento/Swagger/registration.php index 7d7b8d2662a1f..562f7d2e5196d 100644 --- a/app/code/Magento/Swagger/registration.php +++ b/app/code/Magento/Swagger/registration.php @@ -4,6 +4,6 @@ * See COPYING.txt for license details. */ -use \Magento\Framework\Component\ComponentRegistrar; +use Magento\Framework\Component\ComponentRegistrar; ComponentRegistrar::register(ComponentRegistrar::MODULE, 'Magento_Swagger', __DIR__); diff --git a/app/code/Magento/SwaggerWebapi/registration.php b/app/code/Magento/SwaggerWebapi/registration.php index 595497df7cfeb..bc551c2ea3cd5 100644 --- a/app/code/Magento/SwaggerWebapi/registration.php +++ b/app/code/Magento/SwaggerWebapi/registration.php @@ -6,6 +6,6 @@ declare(strict_types=1); -use \Magento\Framework\Component\ComponentRegistrar; +use Magento\Framework\Component\ComponentRegistrar; ComponentRegistrar::register(ComponentRegistrar::MODULE, 'Magento_SwaggerWebapi', __DIR__); diff --git a/app/code/Magento/SwaggerWebapiAsync/registration.php b/app/code/Magento/SwaggerWebapiAsync/registration.php index 8805b81ca28a1..6af92fe040fd5 100644 --- a/app/code/Magento/SwaggerWebapiAsync/registration.php +++ b/app/code/Magento/SwaggerWebapiAsync/registration.php @@ -6,6 +6,6 @@ declare(strict_types=1); -use \Magento\Framework\Component\ComponentRegistrar; +use Magento\Framework\Component\ComponentRegistrar; ComponentRegistrar::register(ComponentRegistrar::MODULE, 'Magento_SwaggerWebapiAsync', __DIR__); diff --git a/app/code/Magento/Swatches/Controller/Adminhtml/Product/Attribute/Plugin/Save.php b/app/code/Magento/Swatches/Controller/Adminhtml/Product/Attribute/Plugin/Save.php index 72d27152d639a..c264614b1a0cf 100644 --- a/app/code/Magento/Swatches/Controller/Adminhtml/Product/Attribute/Plugin/Save.php +++ b/app/code/Magento/Swatches/Controller/Adminhtml/Product/Attribute/Plugin/Save.php @@ -3,18 +3,33 @@ * Copyright © Magento, Inc. All rights reserved. * See COPYING.txt for license details. */ +declare(strict_types=1); namespace Magento\Swatches\Controller\Adminhtml\Product\Attribute\Plugin; use Magento\Catalog\Controller\Adminhtml\Product\Attribute; use Magento\Framework\App\RequestInterface; -use Magento\Swatches\Model\Swatch; +use Magento\Swatches\Model\ConvertSwatchAttributeFrontendInput; /** * Plugin for product attribute save controller. */ class Save { + /** + * @var ConvertSwatchAttributeFrontendInput + */ + private $convertSwatchAttributeFrontendInput; + + /** + * @param ConvertSwatchAttributeFrontendInput $convertSwatchAttributeFrontendInput + */ + public function __construct( + ConvertSwatchAttributeFrontendInput $convertSwatchAttributeFrontendInput + ) { + $this->convertSwatchAttributeFrontendInput = $convertSwatchAttributeFrontendInput; + } + /** * Performs the conversion of the frontend input value. * @@ -23,30 +38,12 @@ class Save * @return array * @SuppressWarnings(PHPMD.UnusedFormalParameter) */ - public function beforeDispatch(Attribute\Save $subject, RequestInterface $request) + public function beforeDispatch(Attribute\Save $subject, RequestInterface $request): array { $data = $request->getPostValue(); + $data = $this->convertSwatchAttributeFrontendInput->execute($data); + $request->setPostValue($data); - if (isset($data['frontend_input'])) { - switch ($data['frontend_input']) { - case 'swatch_visual': - $data[Swatch::SWATCH_INPUT_TYPE_KEY] = Swatch::SWATCH_INPUT_TYPE_VISUAL; - $data['frontend_input'] = 'select'; - $request->setPostValue($data); - break; - case 'swatch_text': - $data[Swatch::SWATCH_INPUT_TYPE_KEY] = Swatch::SWATCH_INPUT_TYPE_TEXT; - $data['use_product_image_for_swatch'] = 0; - $data['frontend_input'] = 'select'; - $request->setPostValue($data); - break; - case 'select': - $data[Swatch::SWATCH_INPUT_TYPE_KEY] = Swatch::SWATCH_INPUT_TYPE_DROPDOWN; - $data['frontend_input'] = 'select'; - $request->setPostValue($data); - break; - } - } return [$request]; } } diff --git a/app/code/Magento/Swatches/Helper/Data.php b/app/code/Magento/Swatches/Helper/Data.php index f9a600925b2a9..d2cd1baca894b 100644 --- a/app/code/Magento/Swatches/Helper/Data.php +++ b/app/code/Magento/Swatches/Helper/Data.php @@ -35,7 +35,7 @@ class Data const EMPTY_IMAGE_VALUE = 'no_selection'; /** - * Default store ID + * The int value of the Default store ID */ const DEFAULT_STORE_ID = 0; @@ -471,13 +471,13 @@ public function getSwatchesByOptionsId(array $optionIds) $swatches = []; $fallbackValues = []; $currentStoreId = $this->storeManager->getStore()->getId(); - foreach ($swatchCollection as $item) { + foreach ($swatchCollection->getData() as $item) { if ($item['type'] != Swatch::SWATCH_TYPE_TEXTUAL) { - $swatches[$item['option_id']] = $item->getData(); + $swatches[$item['option_id']] = $item; } elseif ($item['store_id'] == $currentStoreId && $item['value'] != '') { - $fallbackValues[$item['option_id']][$currentStoreId] = $item->getData(); + $fallbackValues[$item['option_id']][$currentStoreId] = $item; } elseif ($item['store_id'] == self::DEFAULT_STORE_ID) { - $fallbackValues[$item['option_id']][self::DEFAULT_STORE_ID] = $item->getData(); + $fallbackValues[$item['option_id']][self::DEFAULT_STORE_ID] = $item; } } diff --git a/app/code/Magento/Swatches/Model/ConvertSwatchAttributeFrontendInput.php b/app/code/Magento/Swatches/Model/ConvertSwatchAttributeFrontendInput.php new file mode 100644 index 0000000000000..698d0fcc4aea2 --- /dev/null +++ b/app/code/Magento/Swatches/Model/ConvertSwatchAttributeFrontendInput.php @@ -0,0 +1,46 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +declare(strict_types=1); + +namespace Magento\Swatches\Model; + +/** + * Performs the conversion of the frontend input value for attribute data + */ +class ConvertSwatchAttributeFrontendInput +{ + /** + * Performs the conversion of the frontend input value for attribute data + * + * @param array|null $data + * + * @return array|null + */ + public function execute(?array $data): ?array + { + if (!isset($data['frontend_input'])) { + return $data; + } + + switch ($data['frontend_input']) { + case 'swatch_visual': + $data[Swatch::SWATCH_INPUT_TYPE_KEY] = Swatch::SWATCH_INPUT_TYPE_VISUAL; + $data['frontend_input'] = 'select'; + break; + case 'swatch_text': + $data[Swatch::SWATCH_INPUT_TYPE_KEY] = Swatch::SWATCH_INPUT_TYPE_TEXT; + $data['use_product_image_for_swatch'] = 0; + $data['frontend_input'] = 'select'; + break; + case 'select': + $data[Swatch::SWATCH_INPUT_TYPE_KEY] = Swatch::SWATCH_INPUT_TYPE_DROPDOWN; + $data['frontend_input'] = 'select'; + break; + } + + return $data; + } +} diff --git a/app/code/Magento/Swatches/Plugin/Catalog/Model/Product/Attribute/RepositoryPlugin.php b/app/code/Magento/Swatches/Plugin/Catalog/Model/Product/Attribute/RepositoryPlugin.php new file mode 100644 index 0000000000000..cbc5ce7889110 --- /dev/null +++ b/app/code/Magento/Swatches/Plugin/Catalog/Model/Product/Attribute/RepositoryPlugin.php @@ -0,0 +1,51 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +declare(strict_types=1); + +namespace Magento\Swatches\Plugin\Catalog\Model\Product\Attribute; + +use Magento\Catalog\Api\Data\ProductAttributeInterface; +use Magento\Catalog\Model\Product\Attribute\Repository as ProductAttributeRepository; +use Magento\Swatches\Model\ConvertSwatchAttributeFrontendInput; + +/** + * Plugin for product attribute repository + */ +class RepositoryPlugin +{ + /** + * @var ConvertSwatchAttributeFrontendInput + */ + private $convertSwatchAttributeFrontendInput; + + /** + * @param ConvertSwatchAttributeFrontendInput $convertSwatchAttributeFrontendInput + */ + public function __construct( + ConvertSwatchAttributeFrontendInput $convertSwatchAttributeFrontendInput + ) { + $this->convertSwatchAttributeFrontendInput = $convertSwatchAttributeFrontendInput; + } + + /** + * Performs the conversion of the frontend input value. + * + * @param ProductAttributeRepository $subject + * @param ProductAttributeInterface $attribute + * @return array + * @SuppressWarnings(PHPMD.UnusedFormalParameter) + */ + public function beforeSave( + ProductAttributeRepository $subject, + ProductAttributeInterface $attribute + ): array { + $data = $attribute->getData(); + $data = $this->convertSwatchAttributeFrontendInput->execute($data); + $attribute->setData($data); + + return [$attribute]; + } +} diff --git a/app/code/Magento/Swatches/Test/Mftf/ActionGroup/AdminUpdateAttributeInputTypeActionGroup.xml b/app/code/Magento/Swatches/Test/Mftf/ActionGroup/AdminUpdateAttributeInputTypeActionGroup.xml new file mode 100644 index 0000000000000..23264601cad94 --- /dev/null +++ b/app/code/Magento/Swatches/Test/Mftf/ActionGroup/AdminUpdateAttributeInputTypeActionGroup.xml @@ -0,0 +1,20 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + /** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +--> + +<actionGroups xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/actionGroupSchema.xsd"> + <actionGroup name="AdminUpdateAttributeInputTypeActionGroup"> + <annotations> + <description>Set value for the "Catalog Input Type for Store Owner" attribute option</description> + </annotations> + <arguments> + <argument name="value" type="string" defaultValue="swatch_visual"/> + </arguments> + <selectOption selector="{{AttributePropertiesSection.InputType}}" userInput="{{value}}" stepKey="setInputType"/> + </actionGroup> +</actionGroups> diff --git a/app/code/Magento/Swatches/Test/Mftf/Data/SwatchAttributeData.xml b/app/code/Magento/Swatches/Test/Mftf/Data/SwatchAttributeData.xml index b05c9cc9e7a9a..97702b9deb9b6 100644 --- a/app/code/Magento/Swatches/Test/Mftf/Data/SwatchAttributeData.xml +++ b/app/code/Magento/Swatches/Test/Mftf/Data/SwatchAttributeData.xml @@ -18,4 +18,35 @@ <data key="default_label" unique="suffix">TextSwatchAttr</data> <data key="attribute_code" unique="suffix">text_swatch_attr</data> </entity> + <entity name="textSwatchProductAttribute" type="ProductAttribute" extends="productDropDownAttribute"> + <data key="frontend_input">swatch_text</data> + </entity> + <entity name="VisualSwatchProductAttribute" type="ProductAttribute"> + <data key="frontend_input">swatch_visual</data> + <data key="attribute_code" unique="suffix">visual_swatch</data> + </entity> + + <entity name="VisualSwatchProductAttributeForm" type="SwatchProductAttributeForm"> + <data key="frontend_label[0]" unique="suffix">VisualSwatchAttribute-</data> + <data key="frontend_input">swatch_visual</data> + <data key="is_required">0</data> + <data key="update_product_preview_image">1</data> + <data key="use_product_image_for_swatch">0</data> + <data key="attribute_code" unique="suffix">visual_swatch_attribute_</data> + <data key="is_global">1</data> + <data key="default_value_yesno">0</data> + <data key="is_unique">0</data> + <data key="is_used_in_grid">1</data> + <data key="is_visible_in_grid">1</data> + <data key="is_filterable_in_grid">1</data> + <data key="is_searchable">1</data> + <data key="is_comparable">1</data> + <data key="is_filterable">1</data> + <data key="is_filterable_in_search">1</data> + <data key="is_used_for_promo_rules">0</data> + <data key="is_html_allowed_on_front">1</data> + <data key="is_visible_on_front">1</data> + <data key="used_in_product_listing">1</data> + <data key="used_for_sort_by">0</data> + </entity> </entities> diff --git a/app/code/Magento/Swatches/Test/Mftf/Data/SwatchAttributeOptionData.xml b/app/code/Magento/Swatches/Test/Mftf/Data/SwatchAttributeOptionData.xml new file mode 100644 index 0000000000000..34f367ea663c0 --- /dev/null +++ b/app/code/Magento/Swatches/Test/Mftf/Data/SwatchAttributeOptionData.xml @@ -0,0 +1,35 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + /** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +--> + +<entities xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:noNamespaceSchemaLocation="urn:magento:mftf:DataGenerator/etc/dataProfileSchema.xsd"> + <entity name="SwatchProductAttributeOption1" type="ProductSwatchAttributeOption"> + <var key="attribute_code" entityKey="attribute_code" entityType="ProductAttribute"/> + <data key="label" unique="suffix">swatch-option1-</data> + <data key="is_default">false</data> + <data key="sort_order">0</data> + <requiredEntity type="StoreLabel">Option1Store0</requiredEntity> + <requiredEntity type="StoreLabel">Option1Store1</requiredEntity> + </entity> + <entity name="SwatchProductAttributeOption2" type="ProductSwatchAttributeOption"> + <var key="attribute_code" entityKey="attribute_code" entityType="ProductAttribute"/> + <data key="label" unique="suffix">swatch-option2-</data> + <data key="is_default">true</data> + <data key="sort_order">1</data> + <requiredEntity type="StoreLabel">Option2Store0</requiredEntity> + <requiredEntity type="StoreLabel">Option2Store1</requiredEntity> + </entity> + <entity name="SwatchProductAttributeOption3" type="ProductSwatchAttributeOption"> + <var key="attribute_code" entityKey="attribute_code" entityType="ProductAttribute"/> + <data key="label" unique="suffix">swatch-option3-</data> + <data key="is_default">true</data> + <data key="sort_order">2</data> + <requiredEntity type="StoreLabel">Option3Store0</requiredEntity> + <requiredEntity type="StoreLabel">Option3Store1</requiredEntity> + </entity> +</entities> diff --git a/app/code/Magento/Swatches/Test/Mftf/Data/SwatchProductAttributeFrontendLabelData.xml b/app/code/Magento/Swatches/Test/Mftf/Data/SwatchProductAttributeFrontendLabelData.xml new file mode 100644 index 0000000000000..095fd65f3a99c --- /dev/null +++ b/app/code/Magento/Swatches/Test/Mftf/Data/SwatchProductAttributeFrontendLabelData.xml @@ -0,0 +1,15 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + /** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +--> + +<entities xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:noNamespaceSchemaLocation="urn:magento:mftf:DataGenerator/etc/dataProfileSchema.xsd"> + <entity name="SwatchProductAttributeFrontendLabel" type="FrontendLabel"> + <data key="store_id">0</data> + <data key="label" unique="suffix">Swatch-Attribute-</data> + </entity> +</entities> diff --git a/app/code/Magento/Swatches/Test/Mftf/Metadata/SwatchProductAttributeMeta.xml b/app/code/Magento/Swatches/Test/Mftf/Metadata/SwatchProductAttributeMeta.xml new file mode 100644 index 0000000000000..795892dbb4d47 --- /dev/null +++ b/app/code/Magento/Swatches/Test/Mftf/Metadata/SwatchProductAttributeMeta.xml @@ -0,0 +1,46 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + /** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +--> + +<operations xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:noNamespaceSchemaLocation="urn:magento:mftf:DataGenerator/etc/dataOperation.xsd"> + <operation name="CreateSwatchProductAttributeForm" dataType="SwatchProductAttributeForm" type="create" + auth="adminFormKey" url="catalog/product_attribute/save" method="POST" successRegex="/messages-message-success/" returnRegex=""> + <contentType>application/x-www-form-urlencoded</contentType> + <field key="frontend_label[0]">string</field> + <field key="frontend_label[1]">string</field> + <field key="frontend_input">string</field> + <field key="is_required">string</field> + <field key="update_product_preview_image">string</field> + <field key="use_product_image_for_swatch">string</field> + <field key="visual_swatch_validation">string</field> + <field key="visual_swatch_validation_unique">string</field> + <field key="text_swatch_validation">string</field> + <field key="text_swatch_validation_unique">string</field> + <field key="dropdown_attribute_validation">string</field> + <field key="dropdown_attribute_validation_unique">string</field> + <field key="attribute_code">string</field> + <field key="is_global">string</field> + <field key="default_value_text">string</field> + <field key="default_value_yesno">string</field> + <field key="default_value_date">string</field> + <field key="default_value_textarea">string</field> + <field key="is_unique">string</field> + <field key="is_used_in_grid">string</field> + <field key="is_visible_in_grid">string</field> + <field key="is_filterable_in_grid">string</field> + <field key="is_searchable">string</field> + <field key="is_comparable">string</field> + <field key="is_filterable">string</field> + <field key="is_filterable_in_search">string</field> + <field key="is_used_for_promo_rules">string</field> + <field key="is_html_allowed_on_front">string</field> + <field key="is_visible_on_front">string</field> + <field key="used_in_product_listing">string</field> + <field key="used_for_sort_by">string</field> + </operation> +</operations> diff --git a/app/code/Magento/Swatches/Test/Mftf/Metadata/SwatchProductAttributeOptionMeta.xml b/app/code/Magento/Swatches/Test/Mftf/Metadata/SwatchProductAttributeOptionMeta.xml new file mode 100644 index 0000000000000..b70550f71400b --- /dev/null +++ b/app/code/Magento/Swatches/Test/Mftf/Metadata/SwatchProductAttributeOptionMeta.xml @@ -0,0 +1,28 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + /** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +--> + +<operations xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:noNamespaceSchemaLocation="urn:magento:mftf:DataGenerator/etc/dataOperation.xsd"> + <operation name="CreateProductSwatchAttributeOption" dataType="ProductSwatchAttributeOption" type="create" auth="adminOauth" url="/V1/products/attributes/{attribute_code}/options" method="POST"> + <contentType>application/json</contentType> + <object dataType="ProductSwatchAttributeOption" key="option"> + <field key="label">string</field> + <field key="sort_order">integer</field> + <field key="is_default">boolean</field> + <array key="store_labels"> + <value>StoreLabel</value> + </array> + </object> + </operation> + <operation name="DeleteProductSwatchAttributeOption" dataType="ProductSwatchAttributeOption" type="delete" auth="adminOauth" url="/V1/products/attributes/{attribute_code}/options/{option_id}" method="DELETE"> + <contentType>application/json</contentType> + </operation> + <operation name="GetProductSwatchAttributeOption" dataType="ProductSwatchAttributeOption" type="get" auth="adminOauth" url="/V1/products/attributes/{attribute_code}/options/" method="GET"> + <contentType>application/json</contentType> + </operation> +</operations> diff --git a/app/code/Magento/Swatches/Test/Mftf/Section/AdminManageSwatchSection.xml b/app/code/Magento/Swatches/Test/Mftf/Section/AdminManageSwatchSection.xml index c3ef0a7324bfd..ab4502f3e9cfb 100644 --- a/app/code/Magento/Swatches/Test/Mftf/Section/AdminManageSwatchSection.xml +++ b/app/code/Magento/Swatches/Test/Mftf/Section/AdminManageSwatchSection.xml @@ -17,8 +17,11 @@ <element name="nthSwatchText" type="input" selector="#swatch-text-options-panel table tbody tr:nth-of-type({{var}}) td:nth-of-type(3) input" parameterized="true"/> <element name="nthIsDefault" type="input" selector="(//input[@name='defaultvisual[]'])[{{var}}]" parameterized="true"/> <element name="nthSwatchAdminDescription" type="input" selector="#swatch-text-options-panel table tbody tr:nth-of-type({{var}}) td:nth-of-type(4) input" parameterized="true"/> + <element name="nthVisualSwatch" type="button" selector="#swatch-visual-options-panel table tbody tr:nth-of-type({{var}}) .swatches-visual-col" parameterized="true"/> + <element name="chooserBlock" type="block" selector="#swatch-visual-options-panel table tbody tr:nth-of-type({{var}}) .swatches-visual-col .swatch_sub-menu_container" parameterized="true"/> <!-- Selector for Admin Description input where the index is zero-based --> <element name="swatchAdminDescriptionByIndex" type="input" selector="input[name='optiontext[value][option_{{index}}][0]']" parameterized="true"/> + <element name="swatchWindow" type="button" selector="#swatch_window_option_option_{{var}}" parameterized="true"/> <element name="nthChooseColor" type="button" selector="#swatch-visual-options-panel table tbody tr:nth-of-type({{var}}) .swatch_row_name.colorpicker_handler" parameterized="true"/> <element name="nthUploadFile" type="button" selector="#swatch-visual-options-panel table tbody tr:nth-of-type({{var}}) .swatch_row_name.btn_choose_file_upload" parameterized="true"/> <element name="nthDelete" type="button" selector="#swatch-visual-options-panel table tbody tr:nth-of-type({{var}}) button.delete-option" parameterized="true"/> diff --git a/app/code/Magento/Swatches/Test/Mftf/Section/StorefrontProductInfoMainSection.xml b/app/code/Magento/Swatches/Test/Mftf/Section/StorefrontProductInfoMainSection.xml index 5b714f01fd46f..a4bff2227ffbb 100644 --- a/app/code/Magento/Swatches/Test/Mftf/Section/StorefrontProductInfoMainSection.xml +++ b/app/code/Magento/Swatches/Test/Mftf/Section/StorefrontProductInfoMainSection.xml @@ -9,13 +9,14 @@ <sections xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="urn:magento:mftf:Page/etc/SectionObject.xsd"> <section name="StorefrontProductInfoMainSection"> - <element name="swatchOptionByLabel" type="button" selector="div.swatch-option[option-label='{{opt}}']" parameterized="true"/> + <element name="swatchOptionByLabel" type="button" selector="div.swatch-option[data-option-label='{{opt}}']" parameterized="true"/> <element name="nthSwatchOption" type="button" selector="div.swatch-option:nth-of-type({{var}})" parameterized="true"/> <element name="selectedSwatchValue" type="text" selector="//div[contains(@class, 'swatch-attribute') and contains(., '{{attr}}')]//span[contains(@class, 'swatch-attribute-selected-option')]" parameterized="true"/> <element name="swatchAttributeOptions" type="text" selector="div.swatch-attribute-options"/> <element name="nthSwatchOptionText" type="button" selector="div.swatch-option.text:nth-of-type({{n}})" parameterized="true"/> <element name="productSwatch" type="button" selector="//div[@class='swatch-option'][@aria-label='{{var1}}']" parameterized="true"/> - <element name="visualSwatchOption" type="button" selector=".swatch-option[option-tooltip-value='#{{visualSwatchOption}}']" parameterized="true"/> + <element name="visualSwatchOption" type="button" selector=".swatch-option[data-option-tooltip-value='#{{visualSwatchOption}}']" parameterized="true"/> <element name="swatchOptionTooltip" type="block" selector="div.swatch-option-tooltip"/> + <element name="swatchAttributeSelectedOption" type="text" selector="#product-options-wrapper .swatch-option.selected"/> </section> </sections> diff --git a/app/code/Magento/Swatches/Test/Mftf/Test/AdminCheckColorUploadChooserVisualSwatchTest.xml b/app/code/Magento/Swatches/Test/Mftf/Test/AdminCheckColorUploadChooserVisualSwatchTest.xml new file mode 100644 index 0000000000000..a4fc0bdcfd1fb --- /dev/null +++ b/app/code/Magento/Swatches/Test/Mftf/Test/AdminCheckColorUploadChooserVisualSwatchTest.xml @@ -0,0 +1,39 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + /** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +--> +<tests xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/testSchema.xsd"> + <test name="AdminCheckColorUploadChooserVisualSwatchTest"> + <annotations> + <features value="Swatches"/> + <stories value="Check correct view of visual swatches"/> + <title value="Correct view of Swatches while choosing color or upload image"/> + <description value="Correct view of Swatches while choosing color or upload image"/> + <severity value="AVERAGE"/> + <group value="Swatches"/> + </annotations> + <before> + <actionGroup ref="AdminLoginActionGroup" stepKey="loginAsAdmin"/> + </before> + <amOnPage url="{{ProductAttributePage.url}}" stepKey="addNewProductAttribute"/> + <selectOption selector="{{AttributePropertiesSection.InputType}}" + userInput="{{visualSwatchAttribute.input_type}}" stepKey="fillInputType"/> + + <click selector="{{AdminManageSwatchSection.addSwatch}}" stepKey="clickAddSwatch1"/> + <click selector="{{AdminManageSwatchSection.addSwatch}}" stepKey="clickAddSwatch2"/> + <click selector="{{AdminManageSwatchSection.addSwatch}}" stepKey="clickAddSwatch3"/> + <click selector="{{AdminManageSwatchSection.nthVisualSwatch('3')}}" stepKey="clickSwatch3"/> + + <click selector="{{AdminManageSwatchSection.nthVisualSwatch('2')}}" stepKey="clickSwatch2"/> + <seeElement selector="{{AdminManageSwatchSection.chooserBlock('2')}}" stepKey="seeSwatch2"/> + <dontSeeElement selector="{{AdminManageSwatchSection.chooserBlock('3')}}" stepKey="dontSeeSwatch3"/> + + <click selector="{{AdminManageSwatchSection.nthVisualSwatch('1')}}" stepKey="clickSwatch1"/> + <seeElement selector="{{AdminManageSwatchSection.chooserBlock('1')}}" stepKey="seeSwatch1"/> + <dontSeeElement selector="{{AdminManageSwatchSection.chooserBlock('2')}}" stepKey="dontSeeSwatch2"/> + </test> +</tests> diff --git a/app/code/Magento/Swatches/Test/Mftf/Test/AdminCheckTextSwatchAttributeAddedViaApiTest.xml b/app/code/Magento/Swatches/Test/Mftf/Test/AdminCheckTextSwatchAttributeAddedViaApiTest.xml new file mode 100644 index 0000000000000..0fcf0c0a35033 --- /dev/null +++ b/app/code/Magento/Swatches/Test/Mftf/Test/AdminCheckTextSwatchAttributeAddedViaApiTest.xml @@ -0,0 +1,44 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + /** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +--> + +<tests xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/testSchema.xsd"> + <test name="AdminCheckTextSwatchAttributeAddedViaApiTest"> + <annotations> + <stories value="Add Swatch Text Product Attribute via API"/> + <title value="Add Swatch Text Product Attribute via API"/> + <description value="Login as admin, create swatch text product attribute.Go to New Product page, + check the created attribute is available on the page."/> + <group value="swatches"/> + </annotations> + <before> + <!-- Login as Admin --> + <actionGroup ref="AdminLoginActionGroup" stepKey="loginToAdminPanel"/> + <!-- Create an attribute with two options to be used in the first child product --> + <createData entity="textSwatchProductAttribute" stepKey="createTextSwatchConfigProductAttribute"/> + <!-- Add the attribute just created to default attribute set --> + <createData entity="AddToDefaultSet" stepKey="createConfigAddToAttributeSet"> + <requiredEntity createDataKey="createTextSwatchConfigProductAttribute"/> + </createData> + </before> + <after> + <!-- Delete Created Data --> + <deleteData createDataKey="createTextSwatchConfigProductAttribute" stepKey="deleteAttribute"/> + <actionGroup ref="AdminLogoutActionGroup" stepKey="logout"/> + <!-- Reindex invalidated indices after product attribute has been created/deleted --> + <magentoCron groups="index" stepKey="reindexInvalidatedIndices"/> + </after> + + <!-- Open the new simple product page --> + <actionGroup ref="AdminOpenNewProductFormPageActionGroup" stepKey="openNewProductPage"/> + <!-- Check created attribute presents on the page --> + <actionGroup ref="AssertAdminProductAttributeByCodeOnProductFormActionGroup" stepKey="checkTextSwatchConfigProductAttributeOnThePage"> + <argument name="productAttributeCode" value="$createTextSwatchConfigProductAttribute.attribute_code$"/> + </actionGroup> + </test> +</tests> diff --git a/app/code/Magento/Swatches/Test/Mftf/Test/AdminCreateImageSwatchTest.xml b/app/code/Magento/Swatches/Test/Mftf/Test/AdminCreateImageSwatchTest.xml index 8a2683af83dc1..a42aadc497a7a 100644 --- a/app/code/Magento/Swatches/Test/Mftf/Test/AdminCreateImageSwatchTest.xml +++ b/app/code/Magento/Swatches/Test/Mftf/Test/AdminCreateImageSwatchTest.xml @@ -20,11 +20,18 @@ </annotations> <before> <createData entity="ApiCategory" stepKey="createCategory"/> - <actionGroup ref="LoginAsAdmin" stepKey="loginAsAdmin"/> + <actionGroup ref="AdminLoginActionGroup" stepKey="loginAsAdmin"/> </before> <after> <deleteData createDataKey="createCategory" stepKey="deleteCategory"/> - <actionGroup ref="logout" stepKey="logout"/> + <actionGroup ref="DeleteProductBySkuActionGroup" stepKey="deleteConfigurableProduct"> + <argument name="sku" value="{{BaseConfigurableProduct.sku}}"/> + </actionGroup> + <actionGroup ref="AdminDeleteProductAttributeByLabelActionGroup" stepKey="deleteProductAttribute"/> + <actionGroup ref="NavigateToAndResetProductAttributeGridToDefaultViewActionGroup" stepKey="resetProductAttributeFilters"/> + <actionGroup ref="AdminLogoutActionGroup" stepKey="logoutFromAdmin"/> + <!-- Reindex invalidated indices after product attribute has been created/deleted --> + <magentoCron groups="index" stepKey="reindexInvalidatedIndices"/> </after> <!-- Begin creating a new product attribute of type "Image Swatch" --> @@ -46,6 +53,7 @@ <click selector="{{AdminManageSwatchSection.nthUploadFile('1')}}" stepKey="clickUploadFile1"/> <attachFile selector="input[name='datafile']" userInput="adobe-thumb.jpg" stepKey="attachFile1"/> <fillField selector="{{AdminManageSwatchSection.adminInputByIndex('0')}}" userInput="adobe-thumb" stepKey="fillAdmin1"/> + <click selector="{{AdminManageSwatchSection.swatchWindow('0')}}" stepKey="clicksWatchWindow1"/> <!-- Set swatch image #2 --> <click selector="{{AdminManageSwatchSection.addSwatch}}" stepKey="clickAddSwatch2"/> @@ -55,6 +63,7 @@ <click selector="{{AdminManageSwatchSection.nthUploadFile('2')}}" stepKey="clickUploadFile2"/> <attachFile selector="input[name='datafile']" userInput="adobe-small.jpg" stepKey="attachFile2"/> <fillField selector="{{AdminManageSwatchSection.adminInputByIndex('1')}}" userInput="adobe-small" stepKey="fillAdmin2"/> + <click selector="{{AdminManageSwatchSection.swatchWindow('1')}}" stepKey="clicksWatchWindow2"/> <!-- Set swatch image #3 --> <click selector="{{AdminManageSwatchSection.addSwatch}}" stepKey="clickAddSwatch3"/> @@ -96,15 +105,11 @@ </assertContains> <!-- Create a configurable product to verify the storefront with --> - <amOnPage url="{{AdminProductIndexPage.url}}" stepKey="amOnProductGridPage"/> - <waitForPageLoad time="30" stepKey="waitForProductGrid"/> - <actionGroup ref="GoToCreateProductPageActionGroup" stepKey="goToCreateConfigurableProduct"> - <argument name="product" value="BaseConfigurableProduct"/> - </actionGroup> + <amOnPage url="{{AdminProductCreatePage.url(BaseConfigurableProduct.attribute_set_id, BaseConfigurableProduct.type_id)}}" stepKey="goToCreateConfigurableProduct"/> <actionGroup ref="FillMainProductFormActionGroup" stepKey="fillProductForm"> <argument name="product" value="BaseConfigurableProduct"/> </actionGroup> - <searchAndMultiSelectOption selector="{{AdminProductFormSection.categoriesDropdown}}" parameterArray="[$$createCategory.name$$]" stepKey="fillCategory"/> + <searchAndMultiSelectOption selector="{{AdminProductFormSection.categoriesDropdown}}" parameterArray="[$createCategory.name$]" stepKey="fillCategory"/> <!-- Create configurations based off the Image Swatch we created earlier --> <click selector="{{AdminProductFormConfigurationsSection.createConfigurations}}" stepKey="clickCreateConfigurations"/> @@ -144,7 +149,7 @@ </assertContains> <!-- Go to the product listing page and see text swatch options --> - <amOnPage url="$$createCategory.name$$.html" stepKey="goToCategoryPageStorefront"/> + <amOnPage url="$createCategory.custom_attributes[url_key]$.html" stepKey="goToCategoryPageStorefront"/> <waitForPageLoad stepKey="waitForProductListingPage"/> <!-- Verify the storefront --> diff --git a/app/code/Magento/Swatches/Test/Mftf/Test/AdminCreateTextSwatchTest.xml b/app/code/Magento/Swatches/Test/Mftf/Test/AdminCreateTextSwatchTest.xml index 4685670fbfdd2..6b2a29d8ec451 100644 --- a/app/code/Magento/Swatches/Test/Mftf/Test/AdminCreateTextSwatchTest.xml +++ b/app/code/Magento/Swatches/Test/Mftf/Test/AdminCreateTextSwatchTest.xml @@ -6,8 +6,7 @@ */ --> -<tests xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" - xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/testSchema.xsd"> +<tests xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/testSchema.xsd"> <test name="AdminCreateTextSwatchTest"> <annotations> <features value="Swatches"/> @@ -19,10 +18,10 @@ <group value="Swatches"/> </annotations> <before> - <actionGroup ref="LoginAsAdmin" stepKey="loginAsAdmin"/> + <actionGroup ref="AdminLoginActionGroup" stepKey="loginAsAdmin"/> </before> <after> - <actionGroup ref="logout" stepKey="adminLogout"/> + <actionGroup ref="AdminLogoutActionGroup" stepKey="adminLogout"/> </after> <!-- Create a new product attribute of type "Text Swatch" --> @@ -83,19 +82,19 @@ <amOnPage url="{{_defaultProduct.urlKey}}.html" stepKey="amOnProductPage"/> <waitForPageLoad stepKey="waitForProductPage"/> <see selector="{{StorefrontProductInfoMainSection.swatchAttributeOptions}}" userInput="red" stepKey="seeRed"/> - <grabAttributeFrom selector="{{StorefrontProductInfoMainSection.nthSwatchOptionText('1')}}" userInput="option-label" stepKey="grabRedLabel"/> + <grabAttributeFrom selector="{{StorefrontProductInfoMainSection.nthSwatchOptionText('1')}}" userInput="data-option-label" stepKey="grabRedLabel"/> <assertEquals stepKey="assertRedLabel"> <expectedResult type="string">Something red.</expectedResult> <actualResult type="string">{$grabRedLabel}</actualResult> </assertEquals> <see selector="{{StorefrontProductInfoMainSection.swatchAttributeOptions}}" userInput="green" stepKey="seeGreen"/> - <grabAttributeFrom selector="{{StorefrontProductInfoMainSection.nthSwatchOptionText('2')}}" userInput="option-label" stepKey="grabGreenLabel"/> + <grabAttributeFrom selector="{{StorefrontProductInfoMainSection.nthSwatchOptionText('2')}}" userInput="data-option-label" stepKey="grabGreenLabel"/> <assertEquals stepKey="assertGreenLabel"> <expectedResult type="string">Something green.</expectedResult> <actualResult type="string">{$grabGreenLabel}</actualResult> </assertEquals> <see selector="{{StorefrontProductInfoMainSection.swatchAttributeOptions}}" userInput="blue" stepKey="seeBlue"/> - <grabAttributeFrom selector="{{StorefrontProductInfoMainSection.nthSwatchOptionText('3')}}" userInput="option-label" stepKey="grabBlueLabel"/> + <grabAttributeFrom selector="{{StorefrontProductInfoMainSection.nthSwatchOptionText('3')}}" userInput="data-option-label" stepKey="grabBlueLabel"/> <assertEquals stepKey="assertBlueLabel"> <expectedResult type="string">Something blue.</expectedResult> <actualResult type="string">{$grabBlueLabel}</actualResult> diff --git a/app/code/Magento/Swatches/Test/Mftf/Test/AdminCreateVisualSwatchTest.xml b/app/code/Magento/Swatches/Test/Mftf/Test/AdminCreateVisualSwatchTest.xml index 1c8e86b3765d4..1a6c0341c0704 100644 --- a/app/code/Magento/Swatches/Test/Mftf/Test/AdminCreateVisualSwatchTest.xml +++ b/app/code/Magento/Swatches/Test/Mftf/Test/AdminCreateVisualSwatchTest.xml @@ -20,7 +20,7 @@ </annotations> <before> <createData entity="ApiCategory" stepKey="createCategory"/> - <actionGroup ref="LoginAsAdmin" stepKey="loginAsAdmin"/> + <actionGroup ref="AdminLoginActionGroup" stepKey="loginAsAdmin"/> </before> <after> <!-- Clean up our modifications to the existing color attribute --> @@ -35,7 +35,7 @@ <waitForPageLoad stepKey="waitToClickSave"/> <click selector="{{AttributePropertiesSection.SaveAndEdit}}" stepKey="clickSaveAndEdit"/> <!-- Logout --> - <actionGroup ref="logout" stepKey="adminLogout"/> + <actionGroup ref="AdminLogoutActionGroup" stepKey="adminLogout"/> </after> <!-- Go to the edit page for the "color" attribute --> diff --git a/app/code/Magento/Swatches/Test/Mftf/Test/AdminCreateVisualSwatchWithNonValidOptionsTest.xml b/app/code/Magento/Swatches/Test/Mftf/Test/AdminCreateVisualSwatchWithNonValidOptionsTest.xml index 8e63a14413f2f..e93a27d377a52 100644 --- a/app/code/Magento/Swatches/Test/Mftf/Test/AdminCreateVisualSwatchWithNonValidOptionsTest.xml +++ b/app/code/Magento/Swatches/Test/Mftf/Test/AdminCreateVisualSwatchWithNonValidOptionsTest.xml @@ -17,14 +17,14 @@ <group value="Swatches"/> </annotations> <before> - <actionGroup ref="LoginAsAdmin" stepKey="login"/> + <actionGroup ref="AdminLoginActionGroup" stepKey="login"/> </before> <after> <!-- Remove attribute --> <actionGroup ref="DeleteProductAttributeActionGroup" stepKey="deleteAttribute"> <argument name="ProductAttribute" value="visualSwatchAttribute"/> </actionGroup> - <actionGroup ref="logout" stepKey="logout"/> + <actionGroup ref="AdminLogoutActionGroup" stepKey="logout"/> </after> <amOnPage url="{{ProductAttributePage.url}}" stepKey="navigateToNewProductAttributePage"/> diff --git a/app/code/Magento/Swatches/Test/Mftf/Test/AdminDisablingSwatchTooltipsTest.xml b/app/code/Magento/Swatches/Test/Mftf/Test/AdminDisablingSwatchTooltipsTest.xml index b44c04d2c1b46..8fd21acbd51d9 100644 --- a/app/code/Magento/Swatches/Test/Mftf/Test/AdminDisablingSwatchTooltipsTest.xml +++ b/app/code/Magento/Swatches/Test/Mftf/Test/AdminDisablingSwatchTooltipsTest.xml @@ -22,7 +22,7 @@ <createData entity="ApiCategory" stepKey="createCategory"/> <!-- Log in --> - <actionGroup ref="LoginAsAdmin" stepKey="loginAsAdmin"/> + <actionGroup ref="AdminLoginActionGroup" stepKey="loginAsAdmin"/> </before> <after> <!-- Clean up our modifications to the existing color attribute --> @@ -37,7 +37,7 @@ <click selector="{{AttributePropertiesSection.SaveAndEdit}}" stepKey="clickSaveAndEdit"/> <!-- Log out --> - <actionGroup ref="logout" stepKey="logOut"/> + <actionGroup ref="AdminLogoutActionGroup" stepKey="logOut"/> <!-- Delete category --> <deleteData stepKey="deleteCategory" createDataKey="createCategory"/> diff --git a/app/code/Magento/Swatches/Test/Mftf/Test/AdminSaveConfigurableProductWithAttributesImagesAndSwatchesTest.xml b/app/code/Magento/Swatches/Test/Mftf/Test/AdminSaveConfigurableProductWithAttributesImagesAndSwatchesTest.xml index d034faeefbdc0..06a51575e68fb 100644 --- a/app/code/Magento/Swatches/Test/Mftf/Test/AdminSaveConfigurableProductWithAttributesImagesAndSwatchesTest.xml +++ b/app/code/Magento/Swatches/Test/Mftf/Test/AdminSaveConfigurableProductWithAttributesImagesAndSwatchesTest.xml @@ -20,7 +20,7 @@ </annotations> <before> <!-- Login as admin --> - <actionGroup ref="LoginAsAdmin" stepKey="loginAsAdmin"/> + <actionGroup ref="AdminLoginActionGroup" stepKey="loginAsAdmin"/> <!-- Create a new product attribute --> <actionGroup ref="AdminOpenProductAttributePageActionGroup" stepKey="openProductAttributePage"/> @@ -71,7 +71,7 @@ <actionGroup ref="AdminOpenProductIndexPageActionGroup" stepKey="openProductIndexPage"/> <actionGroup ref="AdminGridFilterResetActionGroup" stepKey="clearProductsGridFilter"/> <!-- Admin logout --> - <actionGroup ref="logout" stepKey="adminLogout"/> + <actionGroup ref="AdminLogoutActionGroup" stepKey="adminLogout"/> </after> <!-- Add created product attribute to the Default set --> diff --git a/app/code/Magento/Swatches/Test/Mftf/Test/AdminSetUpWatermarkForSwatchImageTest.xml b/app/code/Magento/Swatches/Test/Mftf/Test/AdminSetUpWatermarkForSwatchImageTest.xml index 569952019b29b..3e133dacc66c1 100644 --- a/app/code/Magento/Swatches/Test/Mftf/Test/AdminSetUpWatermarkForSwatchImageTest.xml +++ b/app/code/Magento/Swatches/Test/Mftf/Test/AdminSetUpWatermarkForSwatchImageTest.xml @@ -11,6 +11,7 @@ <test name="AdminSetUpWatermarkForSwatchImageTest"> <annotations> <features value="Swatches"/> + <stories value="Product Swatches Images"/> <title value="Possibility to set up watermark for a swatch image type"/> <description value="Possibility to set up watermark for a swatch image type"/> <severity value="MAJOR"/> @@ -21,12 +22,12 @@ <before> <!-- Login as Admin --> <comment userInput="Login as Admin" stepKey="commentLoginAsAdmin"/> - <actionGroup ref="LoginAsAdmin" stepKey="loginAsAdmin"/> + <actionGroup ref="AdminLoginActionGroup" stepKey="loginAsAdmin"/> </before> <after> <!-- Log out --> <comment userInput="Log out" stepKey="commentLogOut"/> - <actionGroup ref="logout" stepKey="logout"/> + <actionGroup ref="AdminLogoutActionGroup" stepKey="logout"/> </after> <!-- Go to Admin > Content > Configuration page --> <comment userInput="Go to Configuration Page" stepKey="commentOpenConfigurationPage"/> diff --git a/app/code/Magento/Swatches/Test/Mftf/Test/StorefrontConfigurableProductSwatchMinimumPriceTest.xml b/app/code/Magento/Swatches/Test/Mftf/Test/StorefrontConfigurableProductSwatchMinimumPriceTest.xml deleted file mode 100644 index 1cfa7da6f3410..0000000000000 --- a/app/code/Magento/Swatches/Test/Mftf/Test/StorefrontConfigurableProductSwatchMinimumPriceTest.xml +++ /dev/null @@ -1,167 +0,0 @@ -<?xml version="1.0" encoding="UTF-8"?> -<!-- - /** - * Copyright © Magento, Inc. All rights reserved. - * See COPYING.txt for license details. - */ ---> - -<tests xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" - xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/testSchema.xsd"> - <test name="StorefrontConfigurableProductSwatchMinimumPriceProductPageTest"> - <annotations> - <features value="Swatches"/> - <stories value="Configurable product with swatch attribute"/> - <title value="Swatch option should show the lowest price possible on product page"/> - <description value="Swatch option should show the lowest price possible on product page"/> - <severity value="CRITICAL"/> - <testCaseId value="MC-19683"/> - <group value="Swatches"/> - </annotations> - <before> - <!-- Create category --> - <createData entity="ApiCategory" stepKey="createCategory"/> - <!-- Login as Admin --> - <actionGroup ref="LoginAsAdmin" stepKey="loginAsAdmin1"/> - </before> - <after> - <!-- Delete configurable product and all child products --> - <actionGroup ref="DeleteProductsByKeywordActionGroup" stepKey="deleteProductsByKeyword"> - <argument name="keyword" value="{{_defaultProduct.sku}}"/> - </actionGroup> - <!-- Delete category --> - <deleteData createDataKey="createCategory" stepKey="deleteCategoryAttribute"/> - <!-- Delete color attribute --> - <actionGroup ref="DeleteProductAttributeActionGroup" stepKey="deleteColorAttribute"> - <argument name="ProductAttribute" value="ProductColorAttribute"/> - </actionGroup> - <!-- Delete size attribute --> - <actionGroup ref="DeleteProductAttributeActionGroup" stepKey="deleteSizeAttribute"> - <argument name="ProductAttribute" value="ProductSizeAttribute"/> - </actionGroup> - <!-- Logout --> - <actionGroup ref="logout" stepKey="amOnLogoutPage"/> - </after> - <!--Create text swatch attribute with 3 options: Black, White and Blue--> - <actionGroup ref="AddTextSwatchToProductActionGroup" stepKey="addColorAttribute"> - <argument name="attributeName" value="{{ProductColorAttribute.frontend_label}}"/> - <argument name="attributeCode" value="{{ProductColorAttribute.attribute_code}}"/> - <argument name="option1" value="Black"/> - <argument name="option2" value="White"/> - <argument name="option3" value="Blue"/> - <argument name="usedInProductListing" value="Yes"/> - </actionGroup> - <!--Create text swatch attribute with 3 options: Small, Medium and Large--> - <actionGroup ref="AddTextSwatchToProductActionGroup" stepKey="addSizeAttribute"> - <argument name="attributeName" value="{{ProductSizeAttribute.frontend_label}}"/> - <argument name="attributeCode" value="{{ProductSizeAttribute.attribute_code}}"/> - <argument name="option1" value="Small"/> - <argument name="option2" value="Medium"/> - <argument name="option3" value="Large"/> - </actionGroup> - <!--Create configurable product with two attributes: Color and Size--> - <actionGroup ref="CreateConfigurableProductWithTwoAttributesActionGroup" stepKey="createProduct"> - <argument name="product" value="_defaultProduct"/> - <argument name="category" value="$$createCategory$$"/> - <argument name="attribute1" value="ProductColorAttribute"/> - <argument name="attribute2" value="ProductSizeAttribute"/> - </actionGroup> - <!--Set Black-Small product price to 10--> - <actionGroup ref="ChangeConfigurableProductChildProductPriceActionGroup" stepKey="changeBlackSmallPrice"> - <argument name="productAttributes" value="Color: Black, Size: Small"/> - <argument name="productPrice" value="10"/> - </actionGroup> - <!--Set Black-Medium product price to 11--> - <actionGroup ref="ChangeConfigurableProductChildProductPriceActionGroup" stepKey="changeBlackMediumPrice"> - <argument name="productAttributes" value="Color: Black, Size: Medium"/> - <argument name="productPrice" value="11"/> - </actionGroup> - <!--Set Black-Large product price to 12--> - <actionGroup ref="ChangeConfigurableProductChildProductPriceActionGroup" stepKey="changeBlackLargePrice"> - <argument name="productAttributes" value="Color: Black, Size: Large"/> - <argument name="productPrice" value="12"/> - </actionGroup> - <!--Set White-Small product price to 14--> - <actionGroup ref="ChangeConfigurableProductChildProductPriceActionGroup" stepKey="changeWhiteSmallPrice"> - <argument name="productAttributes" value="Color: White, Size: Small"/> - <argument name="productPrice" value="14"/> - </actionGroup> - <!--Set White-Medium product price to 13--> - <actionGroup ref="ChangeConfigurableProductChildProductPriceActionGroup" stepKey="changeWhiteMediumPrice"> - <argument name="productAttributes" value="Color: White, Size: Medium"/> - <argument name="productPrice" value="13"/> - </actionGroup> - <!--Set White-Large product price to 15--> - <actionGroup ref="ChangeConfigurableProductChildProductPriceActionGroup" stepKey="changeWhiteLargePrice"> - <argument name="productAttributes" value="Color: White, Size: Large"/> - <argument name="productPrice" value="15"/> - </actionGroup> - <!--Set Blue-Small product price to 18--> - <actionGroup ref="ChangeConfigurableProductChildProductPriceActionGroup" stepKey="changeBlueSmallPrice"> - <argument name="productAttributes" value="Color: Blue, Size: Small"/> - <argument name="productPrice" value="18"/> - </actionGroup> - <!--Set Blue-Medium product price to 17--> - <actionGroup ref="ChangeConfigurableProductChildProductPriceActionGroup" stepKey="changeBlueMediumPrice"> - <argument name="productAttributes" value="Color: Blue, Size: Medium"/> - <argument name="productPrice" value="17"/> - </actionGroup> - <!--Set Blue-Large product price to 16--> - <actionGroup ref="ChangeConfigurableProductChildProductPriceActionGroup" stepKey="changeBlueLargePrice"> - <argument name="productAttributes" value="Color: Blue, Size: Large"/> - <argument name="productPrice" value="16"/> - </actionGroup> - <!--Save configurable product--> - <actionGroup ref="SaveConfigurableProductActionGroup" stepKey="saveProduct"> - <argument name="product" value="_defaultProduct"/> - </actionGroup> - - <!--Go to product page--> - <amOnPage url="{{StorefrontProductPage.url(_defaultProduct.urlKey)}}" stepKey="amOnConfigurableProductPage"/> - <waitForPageLoad stepKey="waitForConfigurableProductPage"/> - - <!--Verify that the minimum price is 10--> - <actionGroup ref="StorefrontAssertProductPriceOnProductPageActionGroup" stepKey="assertProductPrice"> - <argument name="productPrice" value="10.00"/> - </actionGroup> - - <!--Verify that Black option's minimum price is 16--> - <actionGroup ref="StorefrontAssertSwatchOptionPriceActionGroup" stepKey="assertMinimumPriceForBlackOption"> - <argument name="optionName" value="Black"/> - <argument name="optionPrice" value="10.00"/> - </actionGroup> - - <!--Verify that White option's minimum price is 16--> - <actionGroup ref="StorefrontAssertSwatchOptionPriceActionGroup" stepKey="assertMinimumPriceForWhiteOption"> - <argument name="optionName" value="White"/> - <argument name="optionPrice" value="13.00"/> - </actionGroup> - - <!--Verify that Blue option's minimum price is 16--> - <actionGroup ref="StorefrontAssertSwatchOptionPriceActionGroup" stepKey="assertMinimumPriceForBlueOption"> - <argument name="optionName" value="Blue"/> - <argument name="optionPrice" value="16.00"/> - </actionGroup> - </test> - <test name="StorefrontConfigurableProductSwatchMinimumPriceCategoryPageTest" extends="StorefrontConfigurableProductSwatchMinimumPriceProductPageTest"> - <annotations> - <features value="Swatches"/> - <stories value="Configurable product with swatch attribute"/> - <title value="Swatch option should show the lowest price possible on category page"/> - <description value="Swatch option should show the lowest price possible on category page"/> - <severity value="CRITICAL"/> - <testCaseId value="MC-19683"/> - <group value="Swatches"/> - </annotations> - - <!--Go to category page--> - <amOnPage url="{{StorefrontCategoryPage.url($$createCategory.name$$)}}" stepKey="amOnConfigurableProductPage"/> - <waitForPageLoad stepKey="waitForConfigurableProductPage"/> - - <!--Verify that the minimum price is 10--> - <actionGroup ref="StorefrontAssertProductPriceOnCategoryPageActionGroup" stepKey="assertProductPrice"> - <argument name="productName" value="{{_defaultProduct.name}}"/> - <argument name="productPrice" value="10.00"/> - </actionGroup> - </test> -</tests> diff --git a/app/code/Magento/Swatches/Test/Mftf/Test/StorefrontConfigurableProductSwatchMinimumPriceTest/StorefrontConfigurableProductSwatchMinimumPriceCategoryPageTest.xml b/app/code/Magento/Swatches/Test/Mftf/Test/StorefrontConfigurableProductSwatchMinimumPriceTest/StorefrontConfigurableProductSwatchMinimumPriceCategoryPageTest.xml new file mode 100644 index 0000000000000..576faed573fab --- /dev/null +++ b/app/code/Magento/Swatches/Test/Mftf/Test/StorefrontConfigurableProductSwatchMinimumPriceTest/StorefrontConfigurableProductSwatchMinimumPriceCategoryPageTest.xml @@ -0,0 +1,31 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + /** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +--> +<tests xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/testSchema.xsd"> + <test name="StorefrontConfigurableProductSwatchMinimumPriceCategoryPageTest" extends="StorefrontConfigurableProductSwatchMinimumPriceProductPageTest"> + <annotations> + <features value="Swatches"/> + <stories value="Configurable product with swatch attribute"/> + <title value="Swatch option should show the lowest price possible on category page"/> + <description value="Swatch option should show the lowest price possible on category page"/> + <severity value="CRITICAL"/> + <testCaseId value="MC-19683"/> + <group value="Swatches"/> + </annotations> + + <!--Go to category page--> + <amOnPage url="{{StorefrontCategoryPage.url($$createCategory.name$$)}}" stepKey="amOnConfigurableProductPage"/> + <waitForPageLoad stepKey="waitForConfigurableProductPage"/> + + <!--Verify that the minimum price is 10--> + <actionGroup ref="StorefrontAssertProductPriceOnCategoryPageActionGroup" stepKey="assertProductPrice"> + <argument name="productName" value="{{_defaultProduct.name}}"/> + <argument name="productPrice" value="10.00"/> + </actionGroup> + </test> +</tests> diff --git a/app/code/Magento/Swatches/Test/Mftf/Test/StorefrontConfigurableProductSwatchMinimumPriceTest/StorefrontConfigurableProductSwatchMinimumPriceProductPageTest.xml b/app/code/Magento/Swatches/Test/Mftf/Test/StorefrontConfigurableProductSwatchMinimumPriceTest/StorefrontConfigurableProductSwatchMinimumPriceProductPageTest.xml new file mode 100644 index 0000000000000..62444fd9d30e1 --- /dev/null +++ b/app/code/Magento/Swatches/Test/Mftf/Test/StorefrontConfigurableProductSwatchMinimumPriceTest/StorefrontConfigurableProductSwatchMinimumPriceProductPageTest.xml @@ -0,0 +1,145 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + /** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +--> +<tests xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/testSchema.xsd"> + <test name="StorefrontConfigurableProductSwatchMinimumPriceProductPageTest"> + <annotations> + <features value="Swatches"/> + <stories value="Configurable product with swatch attribute"/> + <title value="Swatch option should show the lowest price possible on product page"/> + <description value="Swatch option should show the lowest price possible on product page"/> + <severity value="CRITICAL"/> + <testCaseId value="MC-19683"/> + <group value="Swatches"/> + </annotations> + <before> + <!-- Create category --> + <createData entity="ApiCategory" stepKey="createCategory"/> + <!-- Login as Admin --> + <actionGroup ref="AdminLoginActionGroup" stepKey="loginAsAdmin1"/> + </before> + <after> + <!-- Delete configurable product and all child products --> + <actionGroup ref="DeleteProductsByKeywordActionGroup" stepKey="deleteProductsByKeyword"> + <argument name="keyword" value="{{_defaultProduct.sku}}"/> + </actionGroup> + <!-- Delete category --> + <deleteData createDataKey="createCategory" stepKey="deleteCategoryAttribute"/> + <!-- Delete color attribute --> + <actionGroup ref="DeleteProductAttributeActionGroup" stepKey="deleteColorAttribute"> + <argument name="ProductAttribute" value="ProductColorAttribute"/> + </actionGroup> + <!-- Delete size attribute --> + <actionGroup ref="DeleteProductAttributeActionGroup" stepKey="deleteSizeAttribute"> + <argument name="ProductAttribute" value="ProductSizeAttribute"/> + </actionGroup> + <!-- Logout --> + <actionGroup ref="AdminLogoutActionGroup" stepKey="amOnLogoutPage"/> + </after> + <!--Create text swatch attribute with 3 options: Black, White and Blue--> + <actionGroup ref="AddTextSwatchToProductActionGroup" stepKey="addColorAttribute"> + <argument name="attributeName" value="{{ProductColorAttribute.frontend_label}}"/> + <argument name="attributeCode" value="{{ProductColorAttribute.attribute_code}}"/> + <argument name="option1" value="Black"/> + <argument name="option2" value="White"/> + <argument name="option3" value="Blue"/> + <argument name="usedInProductListing" value="Yes"/> + </actionGroup> + <!--Create text swatch attribute with 3 options: Small, Medium and Large--> + <actionGroup ref="AddTextSwatchToProductActionGroup" stepKey="addSizeAttribute"> + <argument name="attributeName" value="{{ProductSizeAttribute.frontend_label}}"/> + <argument name="attributeCode" value="{{ProductSizeAttribute.attribute_code}}"/> + <argument name="option1" value="Small"/> + <argument name="option2" value="Medium"/> + <argument name="option3" value="Large"/> + </actionGroup> + <!--Create configurable product with two attributes: Color and Size--> + <actionGroup ref="CreateConfigurableProductWithTwoAttributesActionGroup" stepKey="createProduct"> + <argument name="product" value="_defaultProduct"/> + <argument name="category" value="$$createCategory$$"/> + <argument name="attribute1" value="ProductColorAttribute"/> + <argument name="attribute2" value="ProductSizeAttribute"/> + </actionGroup> + <!--Set Black-Small product price to 10--> + <actionGroup ref="ChangeConfigurableProductChildProductPriceActionGroup" stepKey="changeBlackSmallPrice"> + <argument name="productAttributes" value="Color: Black, Size: Small"/> + <argument name="productPrice" value="10"/> + </actionGroup> + <!--Set Black-Medium product price to 11--> + <actionGroup ref="ChangeConfigurableProductChildProductPriceActionGroup" stepKey="changeBlackMediumPrice"> + <argument name="productAttributes" value="Color: Black, Size: Medium"/> + <argument name="productPrice" value="11"/> + </actionGroup> + <!--Set Black-Large product price to 12--> + <actionGroup ref="ChangeConfigurableProductChildProductPriceActionGroup" stepKey="changeBlackLargePrice"> + <argument name="productAttributes" value="Color: Black, Size: Large"/> + <argument name="productPrice" value="12"/> + </actionGroup> + <!--Set White-Small product price to 14--> + <actionGroup ref="ChangeConfigurableProductChildProductPriceActionGroup" stepKey="changeWhiteSmallPrice"> + <argument name="productAttributes" value="Color: White, Size: Small"/> + <argument name="productPrice" value="14"/> + </actionGroup> + <!--Set White-Medium product price to 13--> + <actionGroup ref="ChangeConfigurableProductChildProductPriceActionGroup" stepKey="changeWhiteMediumPrice"> + <argument name="productAttributes" value="Color: White, Size: Medium"/> + <argument name="productPrice" value="13"/> + </actionGroup> + <!--Set White-Large product price to 15--> + <actionGroup ref="ChangeConfigurableProductChildProductPriceActionGroup" stepKey="changeWhiteLargePrice"> + <argument name="productAttributes" value="Color: White, Size: Large"/> + <argument name="productPrice" value="15"/> + </actionGroup> + <!--Set Blue-Small product price to 18--> + <actionGroup ref="ChangeConfigurableProductChildProductPriceActionGroup" stepKey="changeBlueSmallPrice"> + <argument name="productAttributes" value="Color: Blue, Size: Small"/> + <argument name="productPrice" value="18"/> + </actionGroup> + <!--Set Blue-Medium product price to 17--> + <actionGroup ref="ChangeConfigurableProductChildProductPriceActionGroup" stepKey="changeBlueMediumPrice"> + <argument name="productAttributes" value="Color: Blue, Size: Medium"/> + <argument name="productPrice" value="17"/> + </actionGroup> + <!--Set Blue-Large product price to 16--> + <actionGroup ref="ChangeConfigurableProductChildProductPriceActionGroup" stepKey="changeBlueLargePrice"> + <argument name="productAttributes" value="Color: Blue, Size: Large"/> + <argument name="productPrice" value="16"/> + </actionGroup> + <!--Save configurable product--> + <actionGroup ref="SaveConfigurableProductActionGroup" stepKey="saveProduct"> + <argument name="product" value="_defaultProduct"/> + </actionGroup> + + <!--Go to product page--> + <amOnPage url="{{StorefrontProductPage.url(_defaultProduct.urlKey)}}" stepKey="amOnConfigurableProductPage"/> + <waitForPageLoad stepKey="waitForConfigurableProductPage"/> + + <!--Verify that the minimum price is 10--> + <actionGroup ref="StorefrontAssertProductPriceOnProductPageActionGroup" stepKey="assertProductPrice"> + <argument name="productPrice" value="10.00"/> + </actionGroup> + + <!--Verify that Black option's minimum price is 16--> + <actionGroup ref="StorefrontAssertSwatchOptionPriceActionGroup" stepKey="assertMinimumPriceForBlackOption"> + <argument name="optionName" value="Black"/> + <argument name="optionPrice" value="10.00"/> + </actionGroup> + + <!--Verify that White option's minimum price is 16--> + <actionGroup ref="StorefrontAssertSwatchOptionPriceActionGroup" stepKey="assertMinimumPriceForWhiteOption"> + <argument name="optionName" value="White"/> + <argument name="optionPrice" value="13.00"/> + </actionGroup> + + <!--Verify that Blue option's minimum price is 16--> + <actionGroup ref="StorefrontAssertSwatchOptionPriceActionGroup" stepKey="assertMinimumPriceForBlueOption"> + <argument name="optionName" value="Blue"/> + <argument name="optionPrice" value="16.00"/> + </actionGroup> + </test> +</tests> diff --git a/app/code/Magento/Swatches/Test/Mftf/Test/StorefrontConfigurableSwatchOptionsThumbImagesTest.xml b/app/code/Magento/Swatches/Test/Mftf/Test/StorefrontConfigurableSwatchOptionsThumbImagesTest.xml new file mode 100644 index 0000000000000..8c90d88f51a10 --- /dev/null +++ b/app/code/Magento/Swatches/Test/Mftf/Test/StorefrontConfigurableSwatchOptionsThumbImagesTest.xml @@ -0,0 +1,48 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + /** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +--> + +<tests xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/testSchema.xsd"> + <test name="StorefrontConfigurableSwatchOptionsThumbImagesTest" + extends="StorefrontConfigurableOptionsThumbImagesTest"> + <annotations> + <features value="Swatches"/> + <stories value="Configurable product with swatch attribute"/> + <title value="Check thumbnail images and active image for Configurable Product with swatch attribute"/> + <description value="Login as admin, create attribute with two options, configurable product with two + associated simple products. Add few images for products, check the fotorama thumbnail images + (visible and active) for each selected option for the configurable product"/> + <group value="swatches"/> + </annotations> + <before> + <!-- Go to created attribute (attribute page) --> + <actionGroup ref="NavigateToEditProductAttributeActionGroup" stepKey="navigateToSkuProductAttribute" after="createConfigProductOption"> + <argument name="ProductAttribute" value="$$createConfigProductAttribute.default_frontend_label$$"/> + </actionGroup> + + <!-- Set 'swatch_visual' value for option "Catalog Input Type for Store Owner" --> + <actionGroup ref="AdminUpdateAttributeInputTypeActionGroup" stepKey="selectSwatchVisualInputType" after="navigateToSkuProductAttribute"/> + + <!-- Set 'yes' value for option "Update Product Preview Image" --> + <actionGroup ref="AdminUpdateProductPreviewImageActionGroup" stepKey="setUpdateProductPreviewImage" after="selectSwatchVisualInputType"/> + + <!-- Save Product Attribute --> + <actionGroup ref="SaveProductAttributeActionGroup" stepKey="saveAttribute" after="setUpdateProductPreviewImage"/> + </before> + + <!-- Select first option --> + <actionGroup ref="StorefrontSelectSwatchOptionOnProductPageActionGroup" stepKey="selectFirstOptionValue"> + <argument name="optionName" value="$$getConfigAttributeOption1.label$$"/> + </actionGroup> + + <!-- Select second option --> + <actionGroup ref="StorefrontSelectSwatchOptionOnProductPageActionGroup" stepKey="selectSecondOptionValue"> + <argument name="optionName" value="$$getConfigAttributeOption2.label$$"/> + </actionGroup> + </test> +</tests> diff --git a/app/code/Magento/Swatches/Test/Mftf/Test/StorefrontFilterByImageSwatchTest.xml b/app/code/Magento/Swatches/Test/Mftf/Test/StorefrontFilterByImageSwatchTest.xml index 0c179e92adc8d..47018a1b142b2 100644 --- a/app/code/Magento/Swatches/Test/Mftf/Test/StorefrontFilterByImageSwatchTest.xml +++ b/app/code/Magento/Swatches/Test/Mftf/Test/StorefrontFilterByImageSwatchTest.xml @@ -14,7 +14,7 @@ <stories value="View swatches in product listing"/> <title value="Customers can filter products using image swatches"/> <description value="Customers can filter products using image swatches"/> - <severity value="CRITICAL"/> + <severity value="BLOCKER"/> <testCaseId value="MC-3461"/> <group value="Swatches"/> </annotations> @@ -24,13 +24,13 @@ <createData entity="ApiSimpleProduct" stepKey="createSimpleProduct"> <requiredEntity createDataKey="createCategory"/> </createData> - <actionGroup ref="LoginAsAdmin" stepKey="loginAsAdmin"/> + <actionGroup ref="AdminLoginActionGroup" stepKey="loginAsAdmin"/> </before> <after> <deleteData createDataKey="createCategory" stepKey="deleteCategory"/> <deleteData createDataKey="createSimpleProduct" stepKey="deleteSimpleProduct"/> - <actionGroup ref="logout" stepKey="adminLogout"/> + <actionGroup ref="AdminLogoutActionGroup" stepKey="adminLogout"/> </after> <!-- Begin creating a new product attribute --> @@ -55,6 +55,7 @@ <click selector="{{AdminManageSwatchSection.nthUploadFile('1')}}" stepKey="clickUploadFile1"/> <attachFile selector="input[name='datafile']" userInput="adobe-thumb.jpg" stepKey="attachFile1"/> <fillField selector="{{AdminManageSwatchSection.adminInputByIndex('0')}}" userInput="adobe-thumb" stepKey="fillAdmin1"/> + <click selector="{{AdminManageSwatchSection.swatchWindow('0')}}" stepKey="clicksWatchWindow1"/> <!-- Set swatch #2 image using the file upload --> <click selector="{{AdminManageSwatchSection.addSwatch}}" stepKey="clickAddSwatch2"/> diff --git a/app/code/Magento/Swatches/Test/Mftf/Test/StorefrontFilterByTextSwatchTest.xml b/app/code/Magento/Swatches/Test/Mftf/Test/StorefrontFilterByTextSwatchTest.xml index 0024a95360ef2..82dbff950d62f 100644 --- a/app/code/Magento/Swatches/Test/Mftf/Test/StorefrontFilterByTextSwatchTest.xml +++ b/app/code/Magento/Swatches/Test/Mftf/Test/StorefrontFilterByTextSwatchTest.xml @@ -14,7 +14,7 @@ <stories value="View swatches in product listing"/> <title value="Customers can filter products using text swatches"/> <description value="Customers can filter products using text swatches"/> - <severity value="CRITICAL"/> + <severity value="BLOCKER"/> <testCaseId value="MC-3462"/> <group value="Swatches"/> </annotations> @@ -23,12 +23,12 @@ <createData entity="ApiSimpleProduct" stepKey="createSimpleProduct"> <requiredEntity createDataKey="createCategory"/> </createData> - <actionGroup ref="LoginAsAdmin" stepKey="loginAsAdmin"/> + <actionGroup ref="AdminLoginActionGroup" stepKey="loginAsAdmin"/> </before> <after> <deleteData createDataKey="createCategory" stepKey="deleteCategory"/> <deleteData createDataKey="createSimpleProduct" stepKey="deleteSimpleProduct"/> - <actionGroup ref="logout" stepKey="adminLogout"/> + <actionGroup ref="AdminLogoutActionGroup" stepKey="adminLogout"/> </after> <!-- Begin creating a new product attribute --> diff --git a/app/code/Magento/Swatches/Test/Mftf/Test/StorefrontFilterByVisualSwatchTest.xml b/app/code/Magento/Swatches/Test/Mftf/Test/StorefrontFilterByVisualSwatchTest.xml index 3cd2e13c9d75d..b6b3e58bd7c01 100644 --- a/app/code/Magento/Swatches/Test/Mftf/Test/StorefrontFilterByVisualSwatchTest.xml +++ b/app/code/Magento/Swatches/Test/Mftf/Test/StorefrontFilterByVisualSwatchTest.xml @@ -14,7 +14,7 @@ <stories value="View swatches in product listing"/> <title value="Customers can filter products using visual swatches"/> <description value="Customers can filter products using visual swatches "/> - <severity value="CRITICAL"/> + <severity value="BLOCKER"/> <testCaseId value="MC-3082"/> <group value="Swatches"/> </annotations> @@ -24,13 +24,13 @@ <createData entity="ApiSimpleProduct" stepKey="createSimpleProduct"> <requiredEntity createDataKey="createCategory"/> </createData> - <actionGroup ref="LoginAsAdmin" stepKey="loginAsAdmin"/> + <actionGroup ref="AdminLoginActionGroup" stepKey="loginAsAdmin"/> </before> <after> <deleteData createDataKey="createCategory" stepKey="deleteCategory"/> <deleteData createDataKey="createSimpleProduct" stepKey="deleteSimpleProduct"/> - <actionGroup ref="logout" stepKey="adminLogout"/> + <actionGroup ref="AdminLogoutActionGroup" stepKey="adminLogout"/> </after> <!-- Begin creating a new product attribute --> diff --git a/app/code/Magento/Swatches/Test/Mftf/Test/StorefrontImageColorWhenFilterByColorFilterTest.xml b/app/code/Magento/Swatches/Test/Mftf/Test/StorefrontImageColorWhenFilterByColorFilterTest.xml index 208f5cecc6fa9..03c09c70f3654 100644 --- a/app/code/Magento/Swatches/Test/Mftf/Test/StorefrontImageColorWhenFilterByColorFilterTest.xml +++ b/app/code/Magento/Swatches/Test/Mftf/Test/StorefrontImageColorWhenFilterByColorFilterTest.xml @@ -25,13 +25,13 @@ <createData entity="ApiConfigurableProduct" stepKey="createConfigProduct"> <requiredEntity createDataKey="createCategory"/> </createData> - <actionGroup ref="LoginAsAdmin" stepKey="loginAsAdmin"/> + <actionGroup ref="AdminLoginActionGroup" stepKey="loginAsAdmin"/> </before> <after> <deleteData createDataKey="createCategory" stepKey="deleteCategory"/> <deleteData createDataKey="createConfigProduct" stepKey="deleteConfigProduct"/> - <actionGroup ref="DeleteProductAttributeByLabelActionGroup" stepKey="deleteAttribute"> - <argument name="ProductAttribute" value="visualSwatchAttribute"/> + <actionGroup ref="AdminDeleteProductAttributeByLabelActionGroup" stepKey="deleteAttribute"> + <argument name="productAttributeLabel" value="{{visualSwatchAttribute.default_label}}"/> </actionGroup> <actionGroup ref="AdminOpenProductIndexPageActionGroup" stepKey="openProductIndexPage"/> <actionGroup ref="ClearFiltersAdminDataGridActionGroup" stepKey="clearGridFilter"/> @@ -39,9 +39,11 @@ <argument name="perPage" value="100"/> </actionGroup> <actionGroup ref="DeleteProductsIfTheyExistActionGroup" stepKey="deleteAllProducts"/> - <actionGroup ref="logout" stepKey="logout"/> + <actionGroup ref="AdminLogoutActionGroup" stepKey="logout"/> </after> - <amOnPage url="{{AdminProductEditPage.url($$createConfigProduct.id$$)}}" stepKey="navigateToConfigProductPage"/> + <actionGroup ref="AdminProductPageOpenByIdActionGroup" stepKey="navigateToConfigProductPage"> + <argument name="productId" value="$$createConfigProduct.id$$"/> + </actionGroup> <waitForPageLoad stepKey="waitForProductPageLoad"/> <!--Create visual swatch attribute--> <actionGroup ref="AddVisualSwatchWithProductWithStorefrontPreviewImageConfigActionGroup" stepKey="addSwatchToProduct"> @@ -81,7 +83,10 @@ <waitForElementVisible selector="{{StorefrontCategorySidebarSection.filterOptionByLabel(visualSwatchOption1.default_label)}}" stepKey="waitForOption"/> <click selector="{{StorefrontCategorySidebarSection.filterOptionByLabel(visualSwatchOption1.default_label)}}" stepKey="clickFirstOption"/> <grabAttributeFrom selector="{{StorefrontCategoryMainSection.productImage}}" userInput="src" stepKey="grabFirstOptionImg"/> - <assertContains expectedType="string" expected="{{MagentoLogo.filename}}" actualType="variable" actual="$grabFirstOptionImg" stepKey="assertProductFirstOptionImage"/> + <assertContains stepKey="assertProductFirstOptionImage"> + <actualResult type="variable">$grabFirstOptionImg</actualResult> + <expectedResult type="string">{{MagentoLogo.filename}}</expectedResult> + </assertContains> <click selector="{{StorefrontCategorySidebarSection.removeFilter}}" stepKey="removeSideBarFilter"/> <actionGroup ref="SelectStorefrontSideBarAttributeOption" stepKey="selectStorefrontProductAttributeForSecondOption"> <argument name="categoryName" value="$$createCategory.name$$"/> @@ -90,6 +95,9 @@ <waitForElementVisible selector="{{StorefrontCategorySidebarSection.filterOptionByLabel(visualSwatchOption2.default_label)}}" stepKey="waitForSecondOption"/> <click selector="{{StorefrontCategorySidebarSection.filterOptionByLabel(visualSwatchOption2.default_label)}}" stepKey="clickSecondOption"/> <grabAttributeFrom selector="{{StorefrontCategoryMainSection.productImage}}" userInput="src" stepKey="grabSecondOptionImg"/> - <assertContains expectedType="string" expected="{{TestImageNew.filename}}" actualType="variable" actual="$grabSecondOptionImg" stepKey="assertProductSecondOptionImage"/> + <assertContains stepKey="assertProductSecondOptionImage"> + <actualResult type="variable">$grabSecondOptionImg</actualResult> + <expectedResult type="string">{{TestImageNew.filename}}</expectedResult> + </assertContains> </test> </tests> diff --git a/app/code/Magento/Swatches/Test/Mftf/Test/StorefrontSeeProductImagesMatchingProductSwatchesTest.xml b/app/code/Magento/Swatches/Test/Mftf/Test/StorefrontSeeProductImagesMatchingProductSwatchesTest.xml index d3f6a2e6ccff7..43944ceef33ef 100644 --- a/app/code/Magento/Swatches/Test/Mftf/Test/StorefrontSeeProductImagesMatchingProductSwatchesTest.xml +++ b/app/code/Magento/Swatches/Test/Mftf/Test/StorefrontSeeProductImagesMatchingProductSwatchesTest.xml @@ -21,7 +21,7 @@ <before> <createData entity="SimpleProduct2" stepKey="createSimpleProduct"/> - <actionGroup ref="LoginAsAdmin" stepKey="loginAsAdmin"/> + <actionGroup ref="AdminLoginActionGroup" stepKey="loginAsAdmin"/> </before> <after> @@ -34,7 +34,7 @@ <argument name="product" value="$$createSimpleProduct$$"/> </actionGroup> <actionGroup ref="ClearFiltersAdminDataGridActionGroup" stepKey="clearProductGridFilter"/> - <actionGroup ref="logout" stepKey="logout"/> + <actionGroup ref="AdminLogoutActionGroup" stepKey="logout"/> </after> <!-- Begin creating a new product attribute --> @@ -63,7 +63,7 @@ </actionGroup> <!-- Edit configurable product --> - <actionGroup ref="GoToProductPageViaIDActionGroup" stepKey="openProductEditPage"> + <actionGroup ref="AdminProductPageOpenByIdActionGroup" stepKey="openProductEditPage"> <argument name="productId" value="$$createSimpleProduct.id$$"/> </actionGroup> diff --git a/app/code/Magento/Swatches/Test/Mftf/Test/StorefrontSelectedByQueryParamsConfigurableSwatchOptionsThumbImagesTest.xml b/app/code/Magento/Swatches/Test/Mftf/Test/StorefrontSelectedByQueryParamsConfigurableSwatchOptionsThumbImagesTest.xml new file mode 100644 index 0000000000000..8e4c6c0217b3a --- /dev/null +++ b/app/code/Magento/Swatches/Test/Mftf/Test/StorefrontSelectedByQueryParamsConfigurableSwatchOptionsThumbImagesTest.xml @@ -0,0 +1,54 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + /** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +--> + +<tests xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/testSchema.xsd"> + <test name="StorefrontSelectedByQueryParamsConfigurableSwatchOptionsThumbImagesTest" + extends="StorefrontConfigurableOptionsThumbImagesTest"> + <annotations> + <features value="Swatches"/> + <stories value="Configurable product with swatch attribute"/> + <title value="Check thumbnail images and active image for Configurable Product with swatch attribute + with predefined by query params options"/> + <description value="Login as admin, create swatch attribute with two options, configurable product with two + associated simple products. Add few images for products, check the fotorama thumbnail images + (visible and active) for each option for the configurable product using product URL with params + to selected needed option."/> + <group value="swatches"/> + </annotations> + <before> + <!-- Go to created attribute (attribute page) --> + <actionGroup ref="NavigateToEditProductAttributeActionGroup" stepKey="navigateToSkuProductAttribute" after="createConfigProductOption"> + <argument name="ProductAttribute" value="$$createConfigProductAttribute.default_frontend_label$$"/> + </actionGroup> + + <!-- Set 'swatch_visual' value for option "Catalog Input Type for Store Owner" --> + <actionGroup ref="AdminUpdateAttributeInputTypeActionGroup" stepKey="selectSwatchVisualInputType" after="navigateToSkuProductAttribute"/> + + <!-- Set 'yes' value for option "Update Product Preview Image" --> + <actionGroup ref="AdminUpdateProductPreviewImageActionGroup" stepKey="setUpdateProductPreviewImage" after="selectSwatchVisualInputType"/> + + <!-- Save Product Attribute --> + <actionGroup ref="SaveProductAttributeActionGroup" stepKey="saveAttribute" after="setUpdateProductPreviewImage"/> + </before> + + <!-- Select first option using product query params URL --> + <amOnPage + url="$$createConfigProduct.sku$$.html#$$createConfigProductAttribute.attribute_id$$=$$getConfigAttributeOption1.value$$" + stepKey="selectFirstOptionValue"/> + <reloadPage stepKey="selectFirstOptionValueRefreshPage" after="selectFirstOptionValue"/> + <waitForPageLoad stepKey="waitForProductWithSelectedFirstOptionToLoad" after="selectFirstOptionValueRefreshPage"/> + + <!-- Select second option using product query params URL --> + <amOnPage + url="$$createConfigProduct.sku$$.html#$$createConfigProductAttribute.attribute_id$$=$$getConfigAttributeOption2.value$$" + stepKey="selectSecondOptionValue"/> + <reloadPage stepKey="selectSecondOptionValueRefreshPage" after="selectSecondOptionValue"/> + <waitForPageLoad stepKey="waitForProductWithSelectedSecondOptionToLoad" after="selectSecondOptionValueRefreshPage"/> + </test> +</tests> diff --git a/app/code/Magento/Swatches/Test/Mftf/Test/StorefrontSwatchAttributesDisplayInWidgetCMSTest.xml b/app/code/Magento/Swatches/Test/Mftf/Test/StorefrontSwatchAttributesDisplayInWidgetCMSTest.xml index 119a645252685..2e58aba5963ec 100644 --- a/app/code/Magento/Swatches/Test/Mftf/Test/StorefrontSwatchAttributesDisplayInWidgetCMSTest.xml +++ b/app/code/Magento/Swatches/Test/Mftf/Test/StorefrontSwatchAttributesDisplayInWidgetCMSTest.xml @@ -36,8 +36,8 @@ <amOnPage url="{{AdminProductIndexPage.url}}" stepKey="visitAdminProductPage"/> <waitForPageLoad stepKey="waitForAdminProductGridLoad"/> <conditionalClick selector="{{AdminProductGridFilterSection.clearFilters}}" dependentSelector="{{AdminProductGridFilterSection.clearFilters}}" visible="true" stepKey="clickClearFiltersInitial"/> - <actionGroup ref="DeleteProductAttributeByLabelActionGroup" stepKey="deleteAttribute"> - <argument name="ProductAttribute" value="visualSwatchAttribute"/> + <actionGroup ref="AdminDeleteProductAttributeByLabelActionGroup" stepKey="deleteAttribute"> + <argument name="productAttributeLabel" value="{{visualSwatchAttribute.default_label}}"/> </actionGroup> <!--delete root category--> <amOnPage url="{{AdminCategoryPage.url}}" stepKey="navigateToCategoryPage"/> @@ -51,10 +51,10 @@ <waitForElementVisible selector="{{AdminCategoryMessagesSection.SuccessMessage}}" stepKey="waitForPageReloadAfterDeleteDefaultCategory"/> <magentoCLI command="config:set cms/wysiwyg/enabled enabled" stepKey="enableWYSIWYG"/> <!--logout--> - <actionGroup ref="logout" stepKey="logoutFromAdmin"/> + <actionGroup ref="AdminLogoutActionGroup" stepKey="logoutFromAdmin"/> </after> <!--Login--> - <actionGroup ref="LoginAsAdmin" stepKey="loginToAdmin"/> + <actionGroup ref="AdminLoginActionGroup" stepKey="loginToAdmin"/> <!--Create a configurable swatch product via the UI --> <amOnPage url="{{AdminProductIndexPage.url}}" stepKey="goToProductIndex"/> <waitForPageLoad stepKey="waitForProductPage"/> @@ -77,7 +77,7 @@ <click selector="{{CmsNewPagePageSeoSection.header}}" stepKey="clickToExpandSEOSection"/> <scrollTo selector="{{CmsNewPagePageSeoSection.urlKey}}" stepKey="scrollToUrlKey"/> <grabValueFrom selector="{{CmsNewPagePageSeoSection.urlKey}}" stepKey="grabTextFromUrlKey"/> - <actionGroup ref="logout" stepKey="logout"/> + <actionGroup ref="AdminLogoutActionGroup" stepKey="logout"/> <!--Open Storefront page for the new created page--> <amOnPage url="{{StorefrontHomePage.url}}$grabTextFromUrlKey" stepKey="gotToCreatedCmsPage"/> @@ -86,7 +86,7 @@ <seeElement selector="{{StorefrontProductInfoMainSection.productSwatch(visualSwatchOption2.default_label)}}" stepKey="assertAddedWidgetM"/> <!--Login to delete CMS page--> - <actionGroup ref="LoginAsAdmin" stepKey="loginAsAdmin"/> + <actionGroup ref="AdminLoginActionGroup" stepKey="loginAsAdmin"/> <actionGroup ref="DeletePageByUrlKeyActionGroup" stepKey="deletePage"> <argument name="UrlKey" value="$grabTextFromUrlKey"/> </actionGroup> diff --git a/app/code/Magento/Swatches/Test/Mftf/Test/StorefrontSwatchProductWithFileCustomOptionTest.xml b/app/code/Magento/Swatches/Test/Mftf/Test/StorefrontSwatchProductWithFileCustomOptionTest.xml index 99b38bd3b34d6..e027a41cd9d2a 100644 --- a/app/code/Magento/Swatches/Test/Mftf/Test/StorefrontSwatchProductWithFileCustomOptionTest.xml +++ b/app/code/Magento/Swatches/Test/Mftf/Test/StorefrontSwatchProductWithFileCustomOptionTest.xml @@ -26,10 +26,10 @@ <after> <deleteData createDataKey="createCategory" stepKey="deleteCategory"/> - <actionGroup ref="logout" stepKey="adminLogout"/> + <actionGroup ref="AdminLogoutActionGroup" stepKey="adminLogout"/> </after> - <actionGroup ref="LoginAsAdmin" stepKey="loginAsAdmin"/> + <actionGroup ref="AdminLoginActionGroup" stepKey="loginAsAdmin"/> <!-- Create a configurable swatch product via the UI --> <amOnPage url="{{AdminProductIndexPage.url}}" stepKey="goToProductIndex"/> diff --git a/app/code/Magento/Swatches/Test/Unit/Controller/Adminhtml/Product/Attribute/Plugin/SaveTest.php b/app/code/Magento/Swatches/Test/Unit/Controller/Adminhtml/Product/Attribute/Plugin/SaveTest.php deleted file mode 100644 index c9c826b3a7831..0000000000000 --- a/app/code/Magento/Swatches/Test/Unit/Controller/Adminhtml/Product/Attribute/Plugin/SaveTest.php +++ /dev/null @@ -1,70 +0,0 @@ -<?php -/** - * Copyright © Magento, Inc. All rights reserved. - * See COPYING.txt for license details. - */ - -namespace Magento\Swatches\Test\Unit\Controller\Adminhtml\Product\Attribute\Plugin; - -class SaveTest extends \PHPUnit\Framework\TestCase -{ - /** - * @dataProvider dataRequest - */ - public function testBeforeDispatch($dataRequest, $runTimes) - { - $subject = $this->createMock(\Magento\Catalog\Controller\Adminhtml\Product\Attribute\Save::class); - $request = $this->createPartialMock(\Magento\Framework\App\RequestInterface::class, [ - 'getPostValue', - 'setPostValue', - 'getModuleName', - 'setModuleName', - 'getActionName', - 'setActionName', - 'getParam', - 'setParams', - 'getParams', - 'getCookie', - 'isSecure' - ]); - - $objectManager = new \Magento\Framework\TestFramework\Unit\Helper\ObjectManager($this); - $controller = $objectManager->getObject( - \Magento\Swatches\Controller\Adminhtml\Product\Attribute\Plugin\Save::class - ); - - $request->expects($this->once())->method('getPostValue')->willReturn($dataRequest); - $request->expects($this->exactly($runTimes))->method('setPostValue')->willReturn($this->returnSelf()); - - $controller->beforeDispatch($subject, $request); - } - - /** - * @return array - */ - public function dataRequest() - { - return [ - [ - ['frontend_input' => 'swatch_visual'], - 1 - ], - [ - ['frontend_input' => 'swatch_text'], - 1 - ], - [ - ['frontend_input' => 'select'], - 1 - ], - [ - [], - 0 - ], - [ - null, - 0 - ], - ]; - } -} diff --git a/app/code/Magento/Swatches/Test/Unit/Helper/DataTest.php b/app/code/Magento/Swatches/Test/Unit/Helper/DataTest.php index aa44f1f114037..1b4ab2f4c203b 100644 --- a/app/code/Magento/Swatches/Test/Unit/Helper/DataTest.php +++ b/app/code/Magento/Swatches/Test/Unit/Helper/DataTest.php @@ -669,7 +669,8 @@ public function dataForGettingSwatchAsArray() public function testGetSwatchesByOptionsIdIf1() { - $swatchMock = $this->createMock(\Magento\Swatches\Model\Swatch::class); + //Simulate behaviour of \Magento\Swatches\Model\Swatch as array item + $swatchMock = $this->createMock(\ArrayAccess::class); $optionsData = [ [ @@ -692,22 +693,18 @@ public function testGetSwatchesByOptionsIdIf1() ->willReturn($optionsData[0]['type']); $swatchMock->expects($this->at(1))->method('offsetGet')->with('option_id') ->willReturn($optionsData[0]['option_id']); - $swatchMock->expects($this->at(2))->method('getData')->with('') - ->willReturn($optionsData[0]); - $swatchMock->expects($this->at(3))->method('offsetGet')->with('type') + $swatchMock->expects($this->at(2))->method('offsetGet')->with('type') ->willReturn($optionsData[1]['type']); - $swatchMock->expects($this->at(4))->method('offsetGet')->with('store_id') + $swatchMock->expects($this->at(3))->method('offsetGet')->with('store_id') ->willReturn($optionsData[1]['store_id']); - $swatchMock->expects($this->at(5))->method('offsetGet')->with('store_id') + $swatchMock->expects($this->at(4))->method('offsetGet')->with('store_id') ->willReturn($optionsData[1]['store_id']); - $swatchMock->expects($this->at(6))->method('offsetGet')->with('option_id') + $swatchMock->expects($this->at(5))->method('offsetGet')->with('option_id') ->willReturn($optionsData[1]['option_id']); - $swatchMock->expects($this->at(7))->method('getData')->with('') - ->willReturn($optionsData[1]); - $swatchCollectionMock = $this->objectManager - ->getCollectionMock(Collection::class, [$swatchMock, $swatchMock]); + $swatchCollectionMock = $this->createMock(Collection::class); $swatchCollectionMock->method('addFilterByOptionsIds')->with([35])->will($this->returnSelf()); + $swatchCollectionMock->expects($this->once())->method('getData')->willReturn([$swatchMock, $swatchMock]); $this->swatchCollectionFactoryMock->method('create')->willReturn($swatchCollectionMock); $storeMock = $this->createMock(\Magento\Store\Model\Store::class); @@ -719,7 +716,8 @@ public function testGetSwatchesByOptionsIdIf1() public function testGetSwatchesByOptionsIdIf2() { - $swatchMock = $this->createMock(\Magento\Swatches\Model\Swatch::class); + //Simulate behaviour of \Magento\Swatches\Model\Swatch as array item + $swatchMock = $this->createMock(\ArrayAccess::class); $optionsData = [ [ @@ -737,28 +735,21 @@ public function testGetSwatchesByOptionsIdIf2() 'id' => 488, ] ]; - - $swatchMock->expects($this->at(0))->method('offsetGet')->with('type')->willReturn(0); - $swatchMock->expects($this->at(1))->method('offsetGet')->with('store_id')->willReturn(1); - $swatchMock->expects($this->at(2))->method('offsetGet')->with('value')->willReturn('test'); - $swatchMock->expects($this->at(3))->method('offsetGet')->with('option_id')->willReturn(35); - $swatchMock->expects($this->at(4))->method('getData')->with('')->willReturn($optionsData[0]); - $swatchMock->expects($this->at(5))->method('offsetGet')->with('type')->willReturn(0); - $swatchMock->expects($this->at(6))->method('offsetGet')->with('store_id')->willReturn(1); - $swatchMock->expects($this->at(7))->method('offsetGet')->with('value')->willReturn('test2'); - $swatchMock->expects($this->at(8))->method('offsetGet')->with('option_id')->willReturn(36); - $swatchMock->expects($this->at(9))->method('getData')->with('')->willReturn($optionsData[1]); - - $swatchCollectionMock = $this->objectManager->getCollectionMock( - Collection::class, - [ - $swatchMock, - $swatchMock, - ] - ); + // @codingStandardsIgnoreStart + $swatchMock->expects($this->at(0))->method('offsetGet')->with('type')->willReturn($optionsData[0]['type']); + $swatchMock->expects($this->at(1))->method('offsetGet')->with('store_id')->willReturn($optionsData[0]['store_id']); + $swatchMock->expects($this->at(2))->method('offsetGet')->with('value')->willReturn($optionsData[0]['value']); + $swatchMock->expects($this->at(3))->method('offsetGet')->with('option_id')->willReturn($optionsData[0]['option_id']); + $swatchMock->expects($this->at(4))->method('offsetGet')->with('type')->willReturn($optionsData[1]['type']); + $swatchMock->expects($this->at(5))->method('offsetGet')->with('store_id')->willReturn($optionsData[1]['store_id']); + $swatchMock->expects($this->at(6))->method('offsetGet')->with('value')->willReturn($optionsData[1]['value']); + $swatchMock->expects($this->at(7))->method('offsetGet')->with('option_id')->willReturn($optionsData[1]['option_id']); + // @codingStandardsIgnoreEnd + $swatchCollectionMock = $this->createMock(Collection::class); $this->swatchCollectionFactoryMock->method('create')->willReturn($swatchCollectionMock); $swatchCollectionMock->method('addFilterByOptionsIds')->with([35])->will($this->returnSelf()); + $swatchCollectionMock->expects($this->once())->method('getData')->willReturn([$swatchMock, $swatchMock]); $storeMock = $this->createMock(\Magento\Store\Model\Store::class); $this->storeManagerMock->method('getStore')->willReturn($storeMock); @@ -769,7 +760,8 @@ public function testGetSwatchesByOptionsIdIf2() public function testGetSwatchesByOptionsIdIf3() { - $swatchMock = $this->createMock(\Magento\Swatches\Model\Swatch::class); + //Simulate behaviour of \Magento\Swatches\Model\Swatch as array item + $swatchMock = $this->createMock(\ArrayAccess::class); $optionsData = [ 'type' => 0, @@ -778,22 +770,17 @@ public function testGetSwatchesByOptionsIdIf3() 'option_id' => 35, 'id' => 423, ]; - - $swatchMock->expects($this->at(0))->method('offsetGet')->with('type')->willReturn(0); - $swatchMock->expects($this->at(1))->method('offsetGet')->with('store_id')->willReturn(0); - $swatchMock->expects($this->at(2))->method('offsetGet')->with('store_id')->willReturn(0); - $swatchMock->expects($this->at(3))->method('offsetGet')->with('option_id')->willReturn(35); - $swatchMock->expects($this->at(4))->method('getData')->with('')->willReturn($optionsData); - - $swatchCollectionMock = $this->objectManager->getCollectionMock( - Collection::class, - [ - $swatchMock, - ] - ); + // @codingStandardsIgnoreStart + $swatchMock->expects($this->at(0))->method('offsetGet')->with('type')->willReturn($optionsData['type']); + $swatchMock->expects($this->at(1))->method('offsetGet')->with('store_id')->willReturn($optionsData['store_id']); + $swatchMock->expects($this->at(2))->method('offsetGet')->with('store_id')->willReturn($optionsData['store_id']); + $swatchMock->expects($this->at(3))->method('offsetGet')->with('option_id')->willReturn($optionsData['option_id']); + // @codingStandardsIgnoreEnd + $swatchCollectionMock = $this->createMock(Collection::class); $this->swatchCollectionFactoryMock->method('create')->willReturn($swatchCollectionMock); $swatchCollectionMock->method('addFilterByOptionsIds')->with([35])->will($this->returnSelf()); + $swatchCollectionMock->expects($this->once())->method('getData')->willReturn([$swatchMock]); $storeMock = $this->createMock(\Magento\Store\Model\Store::class); $this->storeManagerMock->method('getStore')->willReturn($storeMock); diff --git a/app/code/Magento/Swatches/Test/Unit/Model/ConvertSwatchAttributeFrontendInputTest.php b/app/code/Magento/Swatches/Test/Unit/Model/ConvertSwatchAttributeFrontendInputTest.php new file mode 100644 index 0000000000000..1d27a33e5c244 --- /dev/null +++ b/app/code/Magento/Swatches/Test/Unit/Model/ConvertSwatchAttributeFrontendInputTest.php @@ -0,0 +1,86 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ + +namespace Magento\Swatches\Test\Unit\Model; + +use Magento\Framework\TestFramework\Unit\Helper\ObjectManager; +use Magento\Swatches\Model\Swatch; +use Magento\Swatches\Model\ConvertSwatchAttributeFrontendInput; + +/** + * Tests for \Magento\Swatches\Model\ConvertSwatchAttributeFrontendInput. + */ +class ConvertSwatchAttributeFrontendInputTest extends \PHPUnit\Framework\TestCase +{ + /** + * @var ConvertSwatchAttributeFrontendInput + */ + private $convertSwatchAttributeFrontendInput; + + /** + * @inheritdoc + */ + protected function setUp() + { + $objectManager = new ObjectManager($this); + $this->convertSwatchAttributeFrontendInput = + $objectManager->getObject(ConvertSwatchAttributeFrontendInput::class); + } + + /** + * @dataProvider attributeData + */ + public function testExecute($inputData, $outputData) + { + $result = $this->convertSwatchAttributeFrontendInput->execute($inputData); + $this->assertEquals($outputData, $result); + } + + /** + * @return array + */ + public function attributeData() + { + return [ + [ + [ + 'frontend_input' => 'swatch_visual' + ], + [ + 'frontend_input' => 'select', + Swatch::SWATCH_INPUT_TYPE_KEY => Swatch::SWATCH_INPUT_TYPE_VISUAL, + ] + ], + [ + [ + 'frontend_input' => 'swatch_text' + ], + [ + 'frontend_input' => 'select', + Swatch::SWATCH_INPUT_TYPE_KEY => Swatch::SWATCH_INPUT_TYPE_TEXT, + 'use_product_image_for_swatch' => 0 + ] + ], + [ + [ + 'frontend_input' => 'select' + ], + [ + 'frontend_input' => 'select', + Swatch::SWATCH_INPUT_TYPE_KEY => Swatch::SWATCH_INPUT_TYPE_DROPDOWN, + ] + ], + [ + [], + [] + ], + [ + null, + null + ], + ]; + } +} diff --git a/app/code/Magento/Swatches/etc/webapi_rest/di.xml b/app/code/Magento/Swatches/etc/webapi_rest/di.xml new file mode 100644 index 0000000000000..f05b9727db28a --- /dev/null +++ b/app/code/Magento/Swatches/etc/webapi_rest/di.xml @@ -0,0 +1,12 @@ +<?xml version="1.0"?> +<!-- +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +--> +<config xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="urn:magento:framework:ObjectManager/etc/config.xsd"> + <type name="Magento\Catalog\Model\Product\Attribute\Repository"> + <plugin name="swatches_product_attribute_repository_plugin" type="Magento\Swatches\Plugin\Catalog\Model\Product\Attribute\RepositoryPlugin"/> + </type> +</config> diff --git a/app/code/Magento/Swatches/registration.php b/app/code/Magento/Swatches/registration.php index 8589c4b71ea47..7f1f20aa18cc2 100644 --- a/app/code/Magento/Swatches/registration.php +++ b/app/code/Magento/Swatches/registration.php @@ -4,6 +4,6 @@ * See COPYING.txt for license details. */ -use \Magento\Framework\Component\ComponentRegistrar; +use Magento\Framework\Component\ComponentRegistrar; ComponentRegistrar::register(ComponentRegistrar::MODULE, 'Magento_Swatches', __DIR__); diff --git a/app/code/Magento/Swatches/view/adminhtml/web/js/text.js b/app/code/Magento/Swatches/view/adminhtml/web/js/text.js index 2bb1672d9e8c8..48a14c14bdf3a 100644 --- a/app/code/Magento/Swatches/view/adminhtml/web/js/text.js +++ b/app/code/Magento/Swatches/view/adminhtml/web/js/text.js @@ -13,7 +13,8 @@ define([ 'mage/template', 'uiRegistry', 'jquery/ui', - 'prototype' + 'prototype', + 'validation' ], function (jQuery, mageTemplate, rg) { 'use strict'; diff --git a/app/code/Magento/Swatches/view/adminhtml/web/js/visual.js b/app/code/Magento/Swatches/view/adminhtml/web/js/visual.js index b91fea59229cd..c27d2e60b2899 100644 --- a/app/code/Magento/Swatches/view/adminhtml/web/js/visual.js +++ b/app/code/Magento/Swatches/view/adminhtml/web/js/visual.js @@ -14,7 +14,8 @@ define([ 'uiRegistry', 'jquery/colorpicker/js/colorpicker', 'prototype', - 'jquery/ui' + 'jquery/ui', + 'validation' ], function (jQuery, mageTemplate, rg) { 'use strict'; @@ -403,8 +404,11 @@ define([ /** * Toggle color upload chooser */ - $(document).on('click', '.swatch_window', function () { - $(this).next('div').toggle(); + $(document).on('click', '.swatches-visual-col', function () { + var currentElement = $(this).find('.swatch_sub-menu_container'); + + jQuery('.swatch_sub-menu_container').not(currentElement).hide(); + currentElement.toggle(); }); }); }; diff --git a/app/code/Magento/Swatches/view/base/web/js/swatch-renderer.js b/app/code/Magento/Swatches/view/base/web/js/swatch-renderer.js index 894a4518f4de8..a45ee933584ba 100644 --- a/app/code/Magento/Swatches/view/base/web/js/swatch-renderer.js +++ b/app/code/Magento/Swatches/view/base/web/js/swatch-renderer.js @@ -63,12 +63,12 @@ define([ /** * Render tooltips by attributes (only to up). * Required element attributes: - * - option-type (integer, 0-3) - * - option-label (string) - * - option-tooltip-thumb - * - option-tooltip-value - * - thumb-width - * - thumb-height + * - data-option-type (integer, 0-3) + * - data-option-label (string) + * - data-option-tooltip-thumb + * - data-option-tooltip-value + * - data-thumb-width + * - data-thumb-height */ $.widget('mage.SwatchRendererTooltip', { options: { @@ -84,12 +84,12 @@ define([ $this = this.element, $element = $('.' + $widget.options.tooltipClass), timer, - type = parseInt($this.attr('option-type'), 10), - label = $this.attr('option-label'), - thumb = $this.attr('option-tooltip-thumb'), - value = $this.attr('option-tooltip-value'), - width = $this.attr('thumb-width'), - height = $this.attr('thumb-height'), + type = parseInt($this.data('option-type'), 10), + label = $this.data('option-label'), + thumb = $this.data('option-tooltip-thumb'), + value = $this.data('option-tooltip-value'), + width = $this.data('thumb-width'), + height = $this.data('thumb-height'), $image, $title, $corner; @@ -429,8 +429,8 @@ define([ // Create new control container.append( '<div class="' + classes.attributeClass + ' ' + item.code + '" ' + - 'attribute-code="' + item.code + '" ' + - 'attribute-id="' + item.id + '">' + + 'data-attribute-code="' + item.code + '" ' + + 'data-attribute-id="' + item.id + '">' + label + '<div aria-activedescendant="" ' + 'tabindex="0" ' + @@ -462,7 +462,8 @@ define([ if (showTooltip === 1) { // Connect Tooltip container - .find('[option-type="1"], [option-type="2"], [option-type="0"], [option-type="3"]') + .find('[data-option-type="1"], [data-option-type="2"],' + + ' [data-option-type="0"], [data-option-type="3"]') .SwatchRendererTooltip(); } @@ -537,21 +538,22 @@ define([ ' aria-checked="false"' + ' aria-describedby="' + controlId + '"' + ' tabindex="0"' + - ' option-type="' + type + '"' + - ' option-id="' + id + '"' + - ' option-label="' + label + '"' + + ' data-option-type="' + type + '"' + + ' data-option-id="' + id + '"' + + ' data-option-label="' + label + '"' + ' aria-label="' + label + '"' + - ' option-tooltip-thumb="' + thumb + '"' + - ' option-tooltip-value="' + value + '"' + ' role="option"' + - ' thumb-width="' + width + '"' + - ' thumb-height="' + height + '"'; + ' data-thumb-width="' + width + '"' + + ' data-thumb-height="' + height + '"'; + + attr += thumb !== '' ? ' data-option-tooltip-thumb="' + thumb + '"' : ''; + attr += value !== '' ? ' data-option-tooltip-value="' + value + '"' : ''; swatchImageWidth = _.has(sizeConfig, 'swatchImage') ? sizeConfig.swatchImage.width : 30; swatchImageHeight = _.has(sizeConfig, 'swatchImage') ? sizeConfig.swatchImage.height : 20; if (!this.hasOwnProperty('products') || this.products.length <= 0) { - attr += ' option-empty="true"'; + attr += ' data-option-empty="true"'; } if (type === 0) { @@ -599,14 +601,14 @@ define([ html = '<select class="' + this.options.classes.selectClass + ' ' + config.code + '">' + - '<option value="0" option-id="0">' + chooseText + '</option>'; + '<option value="0" data-option-id="0">' + chooseText + '</option>'; $.each(config.options, function () { var label = this.label, - attr = ' value="' + this.id + '" option-id="' + this.id + '"'; + attr = ' value="' + this.id + '" data-option-id="' + this.id + '"'; if (!this.hasOwnProperty('products') || this.products.length <= 0) { - attr += ' option-empty="true"'; + attr += ' data-option-empty="true"'; } html += '<option ' + attr + '>' + label + '</option>'; @@ -721,9 +723,11 @@ define([ var $parent = $this.parents('.' + $widget.options.classes.attributeClass), $wrapper = $this.parents('.' + $widget.options.classes.attributeOptionsWrapper), $label = $parent.find('.' + $widget.options.classes.attributeSelectedOptionLabelClass), - attributeId = $parent.attr('attribute-id'), + attributeId = $parent.data('attribute-id'), $input = $parent.find('.' + $widget.options.classes.attributeInput), - checkAdditionalData = JSON.parse(this.options.jsonSwatchConfig[attributeId]['additional_data']); + checkAdditionalData = JSON.parse(this.options.jsonSwatchConfig[attributeId]['additional_data']), + $priceBox = $widget.element.parents($widget.options.selectorProduct) + .find(this.options.selectorProductPrice); if ($widget.inProductList) { $input = $widget.productForm.find( @@ -736,14 +740,14 @@ define([ } if ($this.hasClass('selected')) { - $parent.removeAttr('option-selected').find('.selected').removeClass('selected'); + $parent.removeAttr('data-option-selected').find('.selected').removeClass('selected'); $input.val(''); $label.text(''); $this.attr('aria-checked', false); } else { - $parent.attr('option-selected', $this.attr('option-id')).find('.selected').removeClass('selected'); - $label.text($this.attr('option-label')); - $input.val($this.attr('option-id')); + $parent.attr('data-option-selected', $this.data('option-id')).find('.selected').removeClass('selected'); + $label.text($this.data('option-label')); + $input.val($this.data('option-id')); $input.attr('data-attr-name', this._getAttributeCodeById(attributeId)); $this.addClass('selected'); $widget._toggleCheckedAttributes($this, $wrapper); @@ -751,16 +755,15 @@ define([ $widget._Rebuild(); - if ($widget.element.parents($widget.options.selectorProduct) - .find(this.options.selectorProductPrice).is(':data(mage-priceBox)') - ) { + if ($priceBox.is(':data(mage-priceBox)')) { $widget._UpdatePrice(); } $(document).trigger('updateMsrpPriceBlock', [ - _.findKey($widget.options.jsonConfig.index, $widget.options.jsonConfig.defaultValues), - $widget.options.jsonConfig.optionPrices + this._getSelectedOptionPriceIndex(), + $widget.options.jsonConfig.optionPrices, + $priceBox ]); if (parseInt(checkAdditionalData['update_product_preview_image'], 10) === 1) { @@ -770,6 +773,22 @@ define([ $input.trigger('change'); }, + /** + * Get selected option price index + * + * @return {String|undefined} + * @private + */ + _getSelectedOptionPriceIndex: function () { + var allowedProduct = this._getAllowedProductWithMinPrice(this._CalcProducts()); + + if (_.isEmpty(allowedProduct)) { + return undefined; + } + + return allowedProduct; + }, + /** * Get human readable attribute code (eg. size, color) by it ID from configuration * @@ -805,7 +824,7 @@ define([ */ _OnChange: function ($this, $widget) { var $parent = $this.parents('.' + $widget.options.classes.attributeClass), - attributeId = $parent.attr('attribute-id'), + attributeId = $parent.data('attribute-id'), $input = $parent.find('.' + $widget.options.classes.attributeInput); if ($widget.productForm.length > 0) { @@ -815,10 +834,10 @@ define([ } if ($this.val() > 0) { - $parent.attr('option-selected', $this.val()); + $parent.attr('data-option-selected', $this.val()); $input.val($this.val()); } else { - $parent.removeAttr('option-selected'); + $parent.removeAttr('data-option-selected'); $input.val(''); } @@ -845,8 +864,8 @@ define([ * @private */ _Rewind: function (controls) { - controls.find('div[option-id], option[option-id]').removeClass('disabled').removeAttr('disabled'); - controls.find('div[option-empty], option[option-empty]') + controls.find('div[data-option-id], option[data-option-id]').removeClass('disabled').removeAttr('disabled'); + controls.find('div[data-option-empty], option[data-option-empty]') .attr('disabled', true) .addClass('disabled') .attr('tabindex', '-1'); @@ -859,8 +878,8 @@ define([ */ _Rebuild: function () { var $widget = this, - controls = $widget.element.find('.' + $widget.options.classes.attributeClass + '[attribute-id]'), - selected = controls.filter('[option-selected]'); + controls = $widget.element.find('.' + $widget.options.classes.attributeClass + '[data-attribute-id]'), + selected = controls.filter('[data-option-selected]'); // Enable all options $widget._Rewind(controls); @@ -873,16 +892,16 @@ define([ // Disable not available options controls.each(function () { var $this = $(this), - id = $this.attr('attribute-id'), + id = $this.data('attribute-id'), products = $widget._CalcProducts(id); - if (selected.length === 1 && selected.first().attr('attribute-id') === id) { + if (selected.length === 1 && selected.first().data('attribute-id') === id) { return; } - $this.find('[option-id]').each(function () { + $this.find('[data-option-id]').each(function () { var $element = $(this), - option = $element.attr('option-id'); + option = $element.data('option-id'); if (!$widget.optionsMap.hasOwnProperty(id) || !$widget.optionsMap[id].hasOwnProperty(option) || $element.hasClass('selected') || @@ -905,12 +924,13 @@ define([ */ _CalcProducts: function ($skipAttributeId) { var $widget = this, + selectedOptions = '.' + $widget.options.classes.attributeClass + '[data-option-selected]', products = []; // Generate intersection of products - $widget.element.find('.' + $widget.options.classes.attributeClass + '[option-selected]').each(function () { - var id = $(this).attr('attribute-id'), - option = $(this).attr('option-selected'); + $widget.element.find(selectedOptions).each(function () { + var id = $(this).data('attribute-id'), + option = $(this).attr('data-option-selected'); if ($skipAttributeId !== undefined && $skipAttributeId === id) { return; @@ -952,6 +972,8 @@ define([ isShow = typeof result != 'undefined' && result.oldPrice.amount !== result.finalPrice.amount; + $productPrice.find('span:first').toggleClass('special-price', isShow); + $product.find(this.options.slyOldPriceSelector)[isShow ? 'show' : 'hide'](); if (typeof result != 'undefined' && result.tierPrices && result.tierPrices.length) { @@ -1248,6 +1270,14 @@ define([ isInitial; if (isInProductView) { + if (_.isUndefined(gallery)) { + context.find(this.options.mediaGallerySelector).on('gallery:loaded', function () { + this.updateBaseImage(images, context, isInProductView); + }.bind(this)); + + return; + } + imagesToUpdate = images.length ? this._setImageType($.extend(true, [], images)) : []; isInitial = _.isEqual(imagesToUpdate, initialImages); @@ -1257,32 +1287,36 @@ define([ imagesToUpdate = this._setImageIndex(imagesToUpdate); - if (!_.isUndefined(gallery)) { - gallery.updateData(imagesToUpdate); - } else { - context.find(this.options.mediaGallerySelector).on('gallery:loaded', function (loadedGallery) { - loadedGallery = context.find(this.options.mediaGallerySelector).data('gallery'); - loadedGallery.updateData(imagesToUpdate); - }.bind(this)); - } - - if (isInitial) { - $(this.options.mediaGallerySelector).AddFotoramaVideoEvents(); - } else { - $(this.options.mediaGallerySelector).AddFotoramaVideoEvents({ - selectedOption: this.getProduct(), - dataMergeStrategy: this.options.gallerySwitchStrategy - }); - } - - if (gallery) { - gallery.first(); - } + gallery.updateData(imagesToUpdate); + this._addFotoramaVideoEvents(isInitial); } else if (justAnImage && justAnImage.img) { context.find('.product-image-photo').attr('src', justAnImage.img); } }, + /** + * Add video events + * + * @param {Boolean} isInitial + * @private + */ + _addFotoramaVideoEvents: function (isInitial) { + if (_.isUndefined($.mage.AddFotoramaVideoEvents)) { + return; + } + + if (isInitial) { + $(this.options.mediaGallerySelector).AddFotoramaVideoEvents(); + + return; + } + + $(this.options.mediaGallerySelector).AddFotoramaVideoEvents({ + selectedOption: this.getProduct(), + dataMergeStrategy: this.options.gallerySwitchStrategy + }); + }, + /** * Set correct indexes for image set. * @@ -1322,7 +1356,7 @@ define([ _EmulateSelected: function (selectedAttributes) { $.each(selectedAttributes, $.proxy(function (attributeCode, optionId) { var elem = this.element.find('.' + this.options.classes.attributeClass + - '[attribute-code="' + attributeCode + '"] [option-id="' + optionId + '"]'), + '[data-attribute-code="' + attributeCode + '"] [data-option-id="' + optionId + '"]'), parentInput = elem.parent(); if (elem.hasClass('selected')) { @@ -1346,7 +1380,7 @@ define([ _EmulateSelectedByAttributeId: function (selectedAttributes) { $.each(selectedAttributes, $.proxy(function (attributeId, optionId) { var elem = this.element.find('.' + this.options.classes.attributeClass + - '[attribute-id="' + attributeId + '"] [option-id="' + optionId + '"]'), + '[data-attribute-id="' + attributeId + '"] [data-option-id="' + optionId + '"]'), parentInput = elem.parent(); if (elem.hasClass('selected')) { diff --git a/app/code/Magento/Swatches/view/frontend/layout/catalog_widget_product_list.xml b/app/code/Magento/Swatches/view/frontend/layout/catalog_widget_product_list.xml index ce31f588c6c8c..571155185693a 100644 --- a/app/code/Magento/Swatches/view/frontend/layout/catalog_widget_product_list.xml +++ b/app/code/Magento/Swatches/view/frontend/layout/catalog_widget_product_list.xml @@ -1,8 +1,10 @@ +<?xml version="1.0"?> <!-- - ~ Copyright © Magento, Inc. All rights reserved. - ~ See COPYING.txt for license details. - --> - +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +--> <page xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="urn:magento:framework:View/Layout/etc/page_configuration.xsd"> <body> diff --git a/app/code/Magento/Swatches/view/frontend/templates/product/layered/renderer.phtml b/app/code/Magento/Swatches/view/frontend/templates/product/layered/renderer.phtml index b22429eafc8e8..70eb51651f663 100644 --- a/app/code/Magento/Swatches/view/frontend/templates/product/layered/renderer.phtml +++ b/app/code/Magento/Swatches/view/frontend/templates/product/layered/renderer.phtml @@ -11,60 +11,76 @@ ?> <?php $swatchData = $block->getSwatchData(); ?> <div class="swatch-attribute swatch-layered <?= $block->escapeHtmlAttr($swatchData['attribute_code']) ?>" - attribute-code="<?= $block->escapeHtmlAttr($swatchData['attribute_code']) ?>" - attribute-id="<?= $block->escapeHtmlAttr($swatchData['attribute_id']) ?>"> + data-attribute-code="<?= $block->escapeHtmlAttr($swatchData['attribute_code']) ?>" + data-attribute-id="<?= $block->escapeHtmlAttr($swatchData['attribute_id']) ?>"> <div class="swatch-attribute-options clearfix"> - <?php foreach ($swatchData['options'] as $option => $label) : ?> - <a href="<?= $block->escapeUrl($label['link']) ?>" + <?php foreach ($swatchData['options'] as $option => $label): ?> + <a href="<?= $block->escapeUrl($label['link']) ?>" rel="nofollow" aria-label="<?= $block->escapeHtmlAttr($label['label']) ?>" class="swatch-option-link-layered"> - <?php if (isset($swatchData['swatches'][$option]['type'])) : ?> + <?php if (isset($swatchData['swatches'][$option]['type'])): ?> <?php switch ($swatchData['swatches'][$option]['type']) { case '3': ?> <div class="swatch-option <?= $block->escapeHtmlAttr($label['custom_style']) ?>" tabindex="-1" - option-type="3" - option-id="<?= $block->escapeHtmlAttr($option) ?>" - option-label="<?= $block->escapeHtmlAttr($label['label']) ?>" - option-tooltip-thumb="" - option-tooltip-value="" + data-option-type="3" + data-option-id="<?= $block->escapeHtmlAttr($option) ?>" + data-option-label="<?= $block->escapeHtmlAttr($label['label']) ?>" + data-option-tooltip-thumb="" + data-option-tooltip-value="" ></div> <?php break; case '2': ?> - <?php $swatchThumbPath = $block->getSwatchPath('swatch_thumb', $swatchData['swatches'][$option]['value']); ?> - <?php $swatchImagePath = $block->getSwatchPath('swatch_image', $swatchData['swatches'][$option]['value']); ?> + <?php $swatchThumbPath = $block->getSwatchPath( + 'swatch_thumb', + $swatchData['swatches'][$option]['value'] + ); ?> + <?php $swatchImagePath = $block->getSwatchPath( + 'swatch_image', + $swatchData['swatches'][$option]['value'] + ); + $escapedUrl = $block->escapeUrl($swatchImagePath); + ?> <div class="swatch-option image <?= $block->escapeHtmlAttr($label['custom_style']) ?>" tabindex="-1" - option-type="2" - option-id="<?= $block->escapeHtmlAttr($option) ?>" - option-label="<?= $block->escapeHtmlAttr($label['label']) ?>" - option-tooltip-thumb="<?= $block->escapeUrl($swatchThumbPath) ?>" - option-tooltip-value="" - style="background: url(<?= $block->escapeUrl($swatchImagePath) ?>) no-repeat center; background-size: initial;"></div> + data-option-type="2" + data-option-id="<?= $block->escapeHtmlAttr($option) ?>" + data-option-label="<?= $block->escapeHtmlAttr($label['label']) ?>" + data-option-tooltip-thumb="<?= $block->escapeUrl($swatchThumbPath) ?>" + data-option-tooltip-value="" + style="background: url(<?= + /* @noEscape */ $escapedUrl + ?>) no-repeat center; background-size: initial;"> + </div> <?php break; case '1': ?> <div class="swatch-option color <?= $block->escapeHtmlAttr($label['custom_style']) ?>" tabindex="-1" - option-type="1" - option-id="<?= $block->escapeHtmlAttr($option) ?>" - option-label="<?= $block->escapeHtmlAttr($label['label']) ?>" - option-tooltip-thumb="" - option-tooltip-value="<?= $block->escapeHtmlAttr($swatchData['swatches'][$option]['value']) ?>" - style="background: <?= $block->escapeHtmlAttr($swatchData['swatches'][$option]['value']) ?> no-repeat center; background-size: initial;"></div> + data-option-type="1" + data-option-id="<?= $block->escapeHtmlAttr($option) ?>" + data-option-label="<?= $block->escapeHtmlAttr($label['label']) ?>" + data-option-tooltip-thumb="" + data-option-tooltip-value="<?= $block->escapeHtmlAttr( + $swatchData['swatches'][$option]['value'] + ) ?>" + style="background: <?= $block->escapeHtmlAttr( + $swatchData['swatches'][$option]['value'] + ) ?> no-repeat center; background-size: initial;"> + </div> <?php break; case '0': default: ?> <div class="swatch-option text <?= $block->escapeHtmlAttr($label['custom_style']) ?>" tabindex="-1" - option-type="0" - option-id="<?= $block->escapeHtmlAttr($option) ?>" - option-label="<?= $block->escapeHtmlAttr($label['label']) ?>" - option-tooltip-thumb="" - option-tooltip-value="" + data-option-type="0" + data-option-id="<?= $block->escapeHtmlAttr($option) ?>" + data-option-label="<?= $block->escapeHtmlAttr($label['label']) ?>" + data-option-tooltip-thumb="" + data-option-tooltip-value="" ><?= $block->escapeHtml($swatchData['swatches'][$option]['value']) ?></div> <?php break; } ?> @@ -77,7 +93,7 @@ <script> require(["jquery", "Magento_Swatches/js/swatch-renderer"], function ($) { $('.swatch-layered.<?= $block->escapeJs($swatchData['attribute_code']) ?>') - .find('[option-type="1"], [option-type="2"], [option-type="0"], [option-type="3"]') + .find('[data-option-type="1"], [data-option-type="2"], [data-option-type="0"], [data-option-type="3"]') .SwatchRendererTooltip(); }); </script> diff --git a/app/code/Magento/SwatchesGraphQl/Model/Resolver/Product/Options/DataProvider/SwatchDataProvider.php b/app/code/Magento/SwatchesGraphQl/Model/Resolver/Product/Options/DataProvider/SwatchDataProvider.php new file mode 100644 index 0000000000000..e7cd4c567da7f --- /dev/null +++ b/app/code/Magento/SwatchesGraphQl/Model/Resolver/Product/Options/DataProvider/SwatchDataProvider.php @@ -0,0 +1,66 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +declare(strict_types=1); + +namespace Magento\SwatchesGraphQl\Model\Resolver\Product\Options\DataProvider; + +use Magento\Swatches\Helper\Data as SwatchData; +use Magento\Swatches\Helper\Media as SwatchesMedia; +use Magento\Swatches\Model\Swatch; + +/** + * Data provider for options swatches. + */ +class SwatchDataProvider +{ + /** + * @var SwatchData + */ + private $swatchHelper; + + /** + * @var SwatchesMedia + */ + private $swatchMediaHelper; + + /** + * SwatchDataProvider constructor. + * + * @param SwatchData $swatchHelper + * @param SwatchesMedia $swatchMediaHelper + */ + public function __construct( + SwatchData $swatchHelper, + SwatchesMedia $swatchMediaHelper + ) { + $this->swatchHelper = $swatchHelper; + $this->swatchMediaHelper = $swatchMediaHelper; + } + + /** + * Returns swatch data by option ID. + * + * @param string $optionId + * @return array|null + */ + public function getData(string $optionId): ?array + { + $swatches = $this->swatchHelper->getSwatchesByOptionsId([$optionId]); + if (!isset($swatches[$optionId]['type'], $swatches[$optionId]['value'])) { + return null; + } + $type = (int)$swatches[$optionId]['type']; + $value = $swatches[$optionId]['value']; + $data = ['value' => $value, 'type' => $type]; + if ($type === Swatch::SWATCH_TYPE_VISUAL_IMAGE) { + $data['thumbnail'] = $this->swatchMediaHelper->getSwatchAttributeImage( + Swatch::SWATCH_THUMBNAIL_NAME, + $value + ); + } + return $data; + } +} diff --git a/app/code/Magento/SwatchesGraphQl/Model/Resolver/Product/Options/SwatchData.php b/app/code/Magento/SwatchesGraphQl/Model/Resolver/Product/Options/SwatchData.php new file mode 100644 index 0000000000000..980f779e8e9f6 --- /dev/null +++ b/app/code/Magento/SwatchesGraphQl/Model/Resolver/Product/Options/SwatchData.php @@ -0,0 +1,50 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +declare(strict_types=1); + +namespace Magento\SwatchesGraphQl\Model\Resolver\Product\Options; + +use Magento\Framework\GraphQl\Config\Element\Field; +use Magento\Framework\GraphQl\Query\ResolverInterface; +use Magento\Framework\GraphQl\Schema\Type\ResolveInfo; +use Magento\SwatchesGraphQl\Model\Resolver\Product\Options\DataProvider\SwatchDataProvider; + +/** + * Class SwatchData + * + * Product swatch data resolver, used for GraphQL request processing + */ +class SwatchData implements ResolverInterface +{ + /** + * @var SwatchDataProvider + */ + private $swatchDataProvider; + + /** + * SwatchData constructor. + * + * @param SwatchDataProvider $swatchDataProvider + */ + public function __construct( + SwatchDataProvider $swatchDataProvider + ) { + $this->swatchDataProvider = $swatchDataProvider; + } + + /** + * @inheritdoc + */ + public function resolve( + Field $field, + $context, + ResolveInfo $info, + array $value = null, + array $args = null + ) { + return $this->swatchDataProvider->getData($value['value_index']); + } +} diff --git a/app/code/Magento/SwatchesGraphQl/Model/Resolver/Product/Options/SwatchDataTypeResolver.php b/app/code/Magento/SwatchesGraphQl/Model/Resolver/Product/Options/SwatchDataTypeResolver.php new file mode 100644 index 0000000000000..add6f7123b921 --- /dev/null +++ b/app/code/Magento/SwatchesGraphQl/Model/Resolver/Product/Options/SwatchDataTypeResolver.php @@ -0,0 +1,35 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +declare(strict_types=1); + +namespace Magento\SwatchesGraphQl\Model\Resolver\Product\Options; + +use Magento\Framework\Exception\LocalizedException; +use Magento\Framework\GraphQl\Query\Resolver\TypeResolverInterface; +use Magento\Swatches\Model\Swatch; + +/** + * Resolver for swatch data interface. + */ +class SwatchDataTypeResolver implements TypeResolverInterface +{ + /** + * @inheritdoc + */ + public function resolveType(array $data): string + { + switch ($data['type']) { + case Swatch::SWATCH_TYPE_TEXTUAL: + return 'TextSwatchData'; + case Swatch::SWATCH_TYPE_VISUAL_COLOR: + return 'ColorSwatchData'; + case Swatch::SWATCH_TYPE_VISUAL_IMAGE: + return 'ImageSwatchData'; + default: + throw new LocalizedException(__('Unsupported swatch type')); + } + } +} diff --git a/app/code/Magento/SwatchesGraphQl/etc/graphql/di.xml b/app/code/Magento/SwatchesGraphQl/etc/graphql/di.xml index 34f65d8e30e57..07391aead332b 100644 --- a/app/code/Magento/SwatchesGraphQl/etc/graphql/di.xml +++ b/app/code/Magento/SwatchesGraphQl/etc/graphql/di.xml @@ -16,4 +16,4 @@ </argument> </arguments> </type> -</config> \ No newline at end of file +</config> diff --git a/app/code/Magento/SwatchesGraphQl/etc/schema.graphqls b/app/code/Magento/SwatchesGraphQl/etc/schema.graphqls index bdd2631e7aa10..c51468ccd2856 100644 --- a/app/code/Magento/SwatchesGraphQl/etc/schema.graphqls +++ b/app/code/Magento/SwatchesGraphQl/etc/schema.graphqls @@ -26,4 +26,24 @@ type SwatchLayerFilterItem implements LayerFilterItemInterface, SwatchLayerFilte type SwatchData { type: String @doc(description: "Type of swatch filter item: 1 - text, 2 - image") value: String @doc(description: "Value for swatch item (text or image link)") -} \ No newline at end of file +} + +type ConfigurableProductOptionsValues { + swatch_data: SwatchDataInterface @doc(description: "Swatch data for configurable product option") @resolver(class: "Magento\\SwatchesGraphQl\\Model\\Resolver\\Product\\Options\\SwatchData") +} + +interface SwatchDataInterface @typeResolver(class: "Magento\\SwatchesGraphQl\\Model\\Resolver\\Product\\Options\\SwatchDataTypeResolver") { + value: String @doc(description: "Value of swatch item (HEX color code, image link or textual value)") +} + +type ImageSwatchData implements SwatchDataInterface { + thumbnail: String @doc(description: "Thumbnail swatch image URL") +} + +type TextSwatchData implements SwatchDataInterface { + +} + +type ColorSwatchData implements SwatchDataInterface { + +} diff --git a/app/code/Magento/SwatchesLayeredNavigation/registration.php b/app/code/Magento/SwatchesLayeredNavigation/registration.php index f773212378ce1..8ddcbb0753810 100644 --- a/app/code/Magento/SwatchesLayeredNavigation/registration.php +++ b/app/code/Magento/SwatchesLayeredNavigation/registration.php @@ -4,6 +4,6 @@ * See COPYING.txt for license details. */ -use \Magento\Framework\Component\ComponentRegistrar; +use Magento\Framework\Component\ComponentRegistrar; ComponentRegistrar::register(ComponentRegistrar::MODULE, 'Magento_SwatchesLayeredNavigation', __DIR__); diff --git a/app/code/Magento/Tax/Block/Adminhtml/Rate/Form.php b/app/code/Magento/Tax/Block/Adminhtml/Rate/Form.php index 29ce7e6d4b0aa..1884b247e530a 100644 --- a/app/code/Magento/Tax/Block/Adminhtml/Rate/Form.php +++ b/app/code/Magento/Tax/Block/Adminhtml/Rate/Form.php @@ -211,7 +211,12 @@ protected function _prepareForm() $fieldset->addField( 'zip_is_range', 'checkbox', - ['name' => 'zip_is_range', 'label' => __('Zip/Post is Range'), 'value' => '1'] + [ + 'name' => 'zip_is_range', + 'label' => __('Zip/Post is Range'), + 'value' => '1', + 'class' => 'zip-is-range-checkbox' + ] ); if (!isset($formData['tax_postcode'])) { diff --git a/app/code/Magento/Tax/Model/App/Action/ContextPlugin.php b/app/code/Magento/Tax/Model/App/Action/ContextPlugin.php index 913fa4c46f0ae..d768e2f6092a0 100644 --- a/app/code/Magento/Tax/Model/App/Action/ContextPlugin.php +++ b/app/code/Magento/Tax/Model/App/Action/ContextPlugin.php @@ -6,60 +6,68 @@ namespace Magento\Tax\Model\App\Action; +use Magento\Customer\Model\Session; +use Magento\Framework\App\ActionInterface; +use Magento\Framework\App\Http\Context as HttpContext; +use Magento\Framework\Module\Manager as ModuleManager; +use Magento\PageCache\Model\Config as PageCacheConfig; +use Magento\Tax\Helper\Data as TaxHelper; +use Magento\Tax\Model\Calculation; + /** - * Class ContextPlugin + * Provides Action Context on before executing Controller Action */ class ContextPlugin { /** - * @var \Magento\Customer\Model\Session + * @var Session */ protected $customerSession; /** - * @var \Magento\Framework\App\Http\Context + * @var HttpContext */ protected $httpContext; /** - * @var \Magento\Tax\Helper\Data + * @var TaxHelper */ protected $taxHelper; /** - * @var \Magento\Tax\Model\Calculation\Proxy + * @var Calculation */ protected $taxCalculation; /** * Module manager * - * @var \Magento\Framework\Module\Manager + * @var ModuleManager */ private $moduleManager; /** * Cache config * - * @var \Magento\PageCache\Model\Config + * @var PageCacheConfig */ private $cacheConfig; /** - * @param \Magento\Customer\Model\Session $customerSession - * @param \Magento\Framework\App\Http\Context $httpContext - * @param \Magento\Tax\Model\Calculation\Proxy $calculation - * @param \Magento\Tax\Helper\Data $taxHelper - * @param \Magento\Framework\Module\Manager $moduleManager - * @param \Magento\PageCache\Model\Config $cacheConfig + * @param Session $customerSession + * @param HttpContext $httpContext + * @param Calculation $calculation + * @param TaxHelper $taxHelper + * @param ModuleManager $moduleManager + * @param PageCacheConfig $cacheConfig */ public function __construct( - \Magento\Customer\Model\Session $customerSession, - \Magento\Framework\App\Http\Context $httpContext, - \Magento\Tax\Model\Calculation\Proxy $calculation, //phpcs:ignore Magento2.Classes.DiscouragedDependencies - \Magento\Tax\Helper\Data $taxHelper, - \Magento\Framework\Module\Manager $moduleManager, - \Magento\PageCache\Model\Config $cacheConfig + Session $customerSession, + HttpContext $httpContext, + Calculation $calculation, //phpcs:ignore Magento2.Classes.DiscouragedDependencies + TaxHelper $taxHelper, + ModuleManager $moduleManager, + PageCacheConfig $cacheConfig ) { $this->customerSession = $customerSession; $this->httpContext = $httpContext; @@ -72,15 +80,12 @@ public function __construct( /** * Before dispatch. * - * @param \Magento\Framework\App\ActionInterface $subject - * @param \Magento\Framework\App\RequestInterface $request - * @return mixed + * @param ActionInterface $subject + * @return void * @SuppressWarnings(PHPMD.UnusedFormalParameter) */ - public function beforeDispatch( - \Magento\Framework\App\ActionInterface $subject, - \Magento\Framework\App\RequestInterface $request - ) { + public function beforeExecute(ActionInterface $subject) + { if (!$this->customerSession->isLoggedIn() || !$this->moduleManager->isEnabled('Magento_PageCache') || !$this->cacheConfig->isEnabled() || diff --git a/app/code/Magento/Tax/Model/Layout/DepersonalizePlugin.php b/app/code/Magento/Tax/Model/Layout/DepersonalizePlugin.php index ad17406c34b75..6959f0a0d7c7c 100644 --- a/app/code/Magento/Tax/Model/Layout/DepersonalizePlugin.php +++ b/app/code/Magento/Tax/Model/Layout/DepersonalizePlugin.php @@ -1,85 +1,86 @@ <?php /** - * * Copyright © Magento, Inc. All rights reserved. * See COPYING.txt for license details. */ + namespace Magento\Tax\Model\Layout; +use Magento\Customer\Model\Session as CustomerSession; +use Magento\Framework\View\LayoutInterface; use Magento\PageCache\Model\DepersonalizeChecker; /** - * Class DepersonalizePlugin + * Depersonalize customer data. + * + * @SuppressWarnings(PHPMD.CookieAndSessionMisuse) */ class DepersonalizePlugin { /** * @var DepersonalizeChecker */ - protected $depersonalizeChecker; + private $depersonalizeChecker; /** - * @var \Magento\Customer\Model\Session + * @var CustomerSession */ - protected $customerSession; + private $customerSession; /** * @var array */ - protected $defaultTaxShippingAddress; + private $defaultTaxShippingAddress; /** * @var array */ - protected $defaultTaxBillingAddress; + private $defaultTaxBillingAddress; /** * @var int */ - protected $customerTaxClassId; + private $customerTaxClassId; /** * @param DepersonalizeChecker $depersonalizeChecker - * @param \Magento\Customer\Model\Session $customerSession + * @param CustomerSession $customerSession */ public function __construct( DepersonalizeChecker $depersonalizeChecker, - \Magento\Customer\Model\Session $customerSession + CustomerSession $customerSession ) { - $this->customerSession = $customerSession; $this->depersonalizeChecker = $depersonalizeChecker; + $this->customerSession = $customerSession; } /** - * Before generate Xml + * Resolve sensitive customer data if the depersonalization is needed. * - * @param \Magento\Framework\View\LayoutInterface $subject - * @return array + * @param LayoutInterface $subject + * @return void */ - public function beforeGenerateXml(\Magento\Framework\View\LayoutInterface $subject) + public function beforeGenerateXml(LayoutInterface $subject) { if ($this->depersonalizeChecker->checkIfDepersonalize($subject)) { $this->defaultTaxBillingAddress = $this->customerSession->getDefaultTaxBillingAddress(); $this->defaultTaxShippingAddress = $this->customerSession->getDefaultTaxShippingAddress(); $this->customerTaxClassId = $this->customerSession->getCustomerTaxClassId(); } - return []; } /** - * After generate Xml + * Change sensitive customer data if the depersonalization is needed. * - * @param \Magento\Framework\View\LayoutInterface $subject - * @param \Magento\Framework\View\LayoutInterface $result - * @return \Magento\Framework\View\LayoutInterface + * @param LayoutInterface $subject + * @return void */ - public function afterGenerateXml(\Magento\Framework\View\LayoutInterface $subject, $result) + public function afterGenerateElements(LayoutInterface $subject) { if ($this->depersonalizeChecker->checkIfDepersonalize($subject)) { $this->customerSession->setDefaultTaxBillingAddress($this->defaultTaxBillingAddress); $this->customerSession->setDefaultTaxShippingAddress($this->defaultTaxShippingAddress); $this->customerSession->setCustomerTaxClassId($this->customerTaxClassId); } - return $result; } } diff --git a/app/code/Magento/Tax/Model/ResourceModel/Calculation/Rate/Collection.php b/app/code/Magento/Tax/Model/ResourceModel/Calculation/Rate/Collection.php index 9b344f6e96f65..7863b70f6626a 100644 --- a/app/code/Magento/Tax/Model/ResourceModel/Calculation/Rate/Collection.php +++ b/app/code/Magento/Tax/Model/ResourceModel/Calculation/Rate/Collection.php @@ -7,9 +7,25 @@ /** * Tax rate collection */ + namespace Magento\Tax\Model\ResourceModel\Calculation\Rate; -class Collection extends \Magento\Framework\Model\ResourceModel\Db\Collection\AbstractCollection +use Magento\Framework\Data\Collection\Db\FetchStrategyInterface; +use Magento\Framework\Data\Collection\EntityFactory; +use Magento\Framework\DB\Adapter\AdapterInterface; +use Magento\Framework\Event\ManagerInterface; +use Magento\Framework\Model\ResourceModel\Db\AbstractDb; +use Magento\Framework\Model\ResourceModel\Db\Collection\AbstractCollection; +use Magento\Store\Model\Store; +use Magento\Store\Model\StoreManagerInterface; +use Magento\Tax\Model\Calculation\Rate; +use Magento\Tax\Model\ResourceModel\Calculation\Rate as RateResourceModel; +use Psr\Log\LoggerInterface; + +/** + * Collection of Calculation Rates + */ +class Collection extends AbstractCollection { /** * Value of fetched from DB of rules per cycle @@ -17,27 +33,27 @@ class Collection extends \Magento\Framework\Model\ResourceModel\Db\Collection\Ab const TAX_RULES_CHUNK_SIZE = 1000; /** - * @var \Magento\Store\Model\StoreManagerInterface + * @var StoreManagerInterface */ protected $_storeManager; /** - * @param \Magento\Framework\Data\Collection\EntityFactory $entityFactory - * @param \Psr\Log\LoggerInterface $logger - * @param \Magento\Framework\Data\Collection\Db\FetchStrategyInterface $fetchStrategy - * @param \Magento\Framework\Event\ManagerInterface $eventManager - * @param \Magento\Store\Model\StoreManagerInterface $storeManager + * @param EntityFactory $entityFactory + * @param LoggerInterface $logger + * @param FetchStrategyInterface $fetchStrategy + * @param ManagerInterface $eventManager + * @param StoreManagerInterface $storeManager * @param mixed $connection - * @param \Magento\Framework\Model\ResourceModel\Db\AbstractDb $resource + * @param AbstractDb $resource */ public function __construct( - \Magento\Framework\Data\Collection\EntityFactory $entityFactory, - \Psr\Log\LoggerInterface $logger, - \Magento\Framework\Data\Collection\Db\FetchStrategyInterface $fetchStrategy, - \Magento\Framework\Event\ManagerInterface $eventManager, - \Magento\Store\Model\StoreManagerInterface $storeManager, - \Magento\Framework\DB\Adapter\AdapterInterface $connection = null, - \Magento\Framework\Model\ResourceModel\Db\AbstractDb $resource = null + EntityFactory $entityFactory, + LoggerInterface $logger, + FetchStrategyInterface $fetchStrategy, + ManagerInterface $eventManager, + StoreManagerInterface $storeManager, + AdapterInterface $connection = null, + AbstractDb $resource = null ) { $this->_storeManager = $storeManager; parent::__construct($entityFactory, $logger, $fetchStrategy, $eventManager, $connection, $resource); @@ -50,10 +66,7 @@ public function __construct( */ protected function _construct() { - $this->_init( - \Magento\Tax\Model\Calculation\Rate::class, - \Magento\Tax\Model\ResourceModel\Calculation\Rate::class - ); + $this->_init(Rate::class, RateResourceModel::class); } /** @@ -90,7 +103,7 @@ public function joinRegionTable() /** * Join rate title for specified store * - * @param \Magento\Store\Model\Store|string|int $store + * @param Store|string|int $store * @return $this */ public function joinTitle($store = null) @@ -170,12 +183,10 @@ public function toOptionHash() } /** - * Convert items array to hash for select options - * using fetchItem method - * - * @see fetchItem() + * Convert items array to hash for select options using fetchItem method * * @return array + * @see fetchItem() */ public function toOptionHashOptimized() { @@ -195,7 +206,7 @@ public function getOptionRates() { $size = self::TAX_RULES_CHUNK_SIZE; $page = 1; - $rates = []; + $rates = [[]]; do { $offset = $size * ($page - 1); $this->getSelect()->reset(); @@ -206,11 +217,11 @@ public function getOptionRates() ) ->limit($size, $offset); - $rates = array_merge($rates, $this->toOptionArray()); + $rates[] = $this->toOptionArray(); $this->clear(); $page++; } while ($this->getSize() > $offset); - return $rates; + return array_merge(...$rates); } } diff --git a/app/code/Magento/Tax/Model/Sales/Total/Quote/CommonTaxCollector.php b/app/code/Magento/Tax/Model/Sales/Total/Quote/CommonTaxCollector.php index c70c715d32c1b..55a56bd857a58 100644 --- a/app/code/Magento/Tax/Model/Sales/Total/Quote/CommonTaxCollector.php +++ b/app/code/Magento/Tax/Model/Sales/Total/Quote/CommonTaxCollector.php @@ -593,8 +593,11 @@ protected function processProductItems( $total->setSubtotalInclTax($subtotalInclTax); $total->setBaseSubtotalTotalInclTax($baseSubtotalInclTax); $total->setBaseSubtotalInclTax($baseSubtotalInclTax); - $shippingAssignment->getShipping()->getAddress()->setBaseSubtotalTotalInclTax($baseSubtotalInclTax); - $shippingAssignment->getShipping()->getAddress()->setBaseTaxAmount($baseTax); + $address = $shippingAssignment->getShipping()->getAddress(); + $address->setBaseTaxAmount($baseTax); + $address->setBaseSubtotalTotalInclTax($baseSubtotalInclTax); + $address->setSubtotal($total->getSubtotal()); + $address->setBaseSubtotal($total->getBaseSubtotal()); return $this; } diff --git a/app/code/Magento/Tax/Test/Mftf/ActionGroup/AdminCreateTaxRuleCustomProductTaxClassActionGroup.xml b/app/code/Magento/Tax/Test/Mftf/ActionGroup/AdminCreateTaxRuleCustomProductTaxClassActionGroup.xml new file mode 100644 index 0000000000000..a1a488c2e4e37 --- /dev/null +++ b/app/code/Magento/Tax/Test/Mftf/ActionGroup/AdminCreateTaxRuleCustomProductTaxClassActionGroup.xml @@ -0,0 +1,31 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + /** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +--> + +<actionGroups xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/actionGroupSchema.xsd"> + <actionGroup name="AdminCreateTaxRuleCustomProductTaxClassActionGroup"> + <annotations> + <description>Admin create tax rule with custom tax rate and product tax class</description> + </annotations> + <arguments> + <argument name="taxRuleCode" type="string" defaultValue="{{defaultTaxRule.code}}"/> + <argument name="taxRateCode" type="string" defaultValue="{{US_NY_Rate_1.code}}"/> + <argument name="productTaxClassName" type="string" defaultValue="NewTaxClass"/> + </arguments> + <amOnPage url="{{AdminNewTaxRulePage.url}}" stepKey="goNewTaxRulePage"/> + <waitForPageLoad stepKey="waitForNewTaxRulePageLoaded"/> + <fillField selector="{{AdminTaxRulesSection.ruleName}}" userInput="{{taxRuleCode}}" stepKey="fillRuleName"/> + <click selector="{{AdminTaxRulesSection.selectTaxRate(taxRateCode)}}" stepKey="selectTaxRate"/> + <click selector="{{AdminTaxRuleFormSection.additionalSettings}}" stepKey="clickAdditionalSettings"/> + <click selector="{{AdminTaxRulesSection.selectProductTaxClass(taxableGoodsTaxClass.class_name)}}" stepKey="unSelectTaxClass"/> + <click selector="{{AdminTaxRulesSection.selectProductTaxClass(productTaxClassName)}}" stepKey="selectProductTaxClass"/> + <click selector="{{AdminMainActionsSection.save}}" stepKey="clickSave"/> + <waitForElementVisible selector="{{AdminMessagesSection.success}}" stepKey="waitForSuccessMessage"/> + <see selector="{{AdminMessagesSection.success}}" userInput="You saved the tax rule." stepKey="verifyRuleSaved"/> + </actionGroup> +</actionGroups> diff --git a/app/code/Magento/Tax/Test/Mftf/ActionGroup/AdminSelectProductTaxClassActionGroup.xml b/app/code/Magento/Tax/Test/Mftf/ActionGroup/AdminSelectProductTaxClassActionGroup.xml new file mode 100644 index 0000000000000..c59c710a7500d --- /dev/null +++ b/app/code/Magento/Tax/Test/Mftf/ActionGroup/AdminSelectProductTaxClassActionGroup.xml @@ -0,0 +1,23 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + /** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +--> + +<actionGroups xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/actionGroupSchema.xsd"> + <actionGroup name="AdminSelectProductTaxClassActionGroup"> + <annotations> + <description>Select "Product Tax Class" in tax rule edit form.</description> + </annotations> + <arguments> + <argument name="taxClass" type="string" defaultValue="{{productTaxClass.class_name}}"/> + </arguments> + + <conditionalClick selector="{{AdminTaxRuleFormSection.additionalSettings}}" dependentSelector="{{AdminTaxRuleFormSection.additionalSettingsOpened}}" visible="false" stepKey="openAdditionalSettings"/> + <waitForElementVisible selector="{{AdminTaxRuleFormSection.productTaxClassOption(taxClass)}}" stepKey="waitForVisibleTaxClass"/> + <conditionalClick selector="{{AdminTaxRuleFormSection.productTaxClassOption(taxClass)}}" dependentSelector="{{AdminTaxRuleFormSection.productTaxClassSelected(taxClass)}}" visible="false" stepKey="assignProdTaxClass"/> + </actionGroup> +</actionGroups> diff --git a/app/code/Magento/Tax/Test/Mftf/ActionGroup/AdminSelectTaxRateActionGroup.xml b/app/code/Magento/Tax/Test/Mftf/ActionGroup/AdminSelectTaxRateActionGroup.xml new file mode 100644 index 0000000000000..6fc6ecc6dbcdf --- /dev/null +++ b/app/code/Magento/Tax/Test/Mftf/ActionGroup/AdminSelectTaxRateActionGroup.xml @@ -0,0 +1,24 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + /** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +--> + +<actionGroups xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/actionGroupSchema.xsd"> + <actionGroup name="AdminSelectTaxRateActionGroup"> + <annotations> + <description>Select "Tax Rate" in tax rule edit form.</description> + </annotations> + <arguments> + <argument name="taxRate" type="string" defaultValue="{{TaxRateTexas.code}}"/> + </arguments> + + <fillField selector="{{AdminTaxRuleFormSection.taxRateSearch}}" userInput="{{taxRate}}" stepKey="searchTaxRate"/> + <waitForPageLoad time="30" stepKey="waitForAjaxLoad"/> + <waitForElementVisible selector="{{AdminTaxRuleFormSection.taxRateOption(taxRate)}}" time="30" stepKey="waitForVisibleTaxRate" /> + <click selector="{{AdminTaxRuleFormSection.taxRateOption(taxRate)}}" stepKey="clickTaxRate"/> + </actionGroup> +</actionGroups> diff --git a/app/code/Magento/Tax/Test/Mftf/ActionGroup/AdminUnassignProductTaxClassActionGroup.xml b/app/code/Magento/Tax/Test/Mftf/ActionGroup/AdminUnassignProductTaxClassActionGroup.xml new file mode 100644 index 0000000000000..bda604ffa5da0 --- /dev/null +++ b/app/code/Magento/Tax/Test/Mftf/ActionGroup/AdminUnassignProductTaxClassActionGroup.xml @@ -0,0 +1,23 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + /** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +--> + +<actionGroups xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/actionGroupSchema.xsd"> + <actionGroup name="AdminUnassignProductTaxClassActionGroup"> + <annotations> + <description>Admin unassign "Product Tax Class" in tax rule edit form</description> + </annotations> + <arguments> + <argument name="taxClass" type="string" defaultValue="{{productTaxClass.class_name}}"/> + </arguments> + + <conditionalClick selector="{{AdminTaxRuleFormSection.additionalSettings}}" dependentSelector="{{AdminTaxRuleFormSection.additionalSettingsOpened}}" visible="false" stepKey="openAdditionalSettings"/> + <waitForElementVisible selector="{{AdminProductTaxClassSection.productTaxClass}}" stepKey="waitForAddProductTaxClassButton"/> + <conditionalClick selector="{{AdminTaxRuleFormSection.productTaxClassOption(taxClass)}}" dependentSelector="{{AdminTaxRuleFormSection.productTaxClassSelected(taxClass)}}" visible="true" stepKey="unSelectTaxClass"/> + </actionGroup> +</actionGroups> diff --git a/app/code/Magento/Tax/Test/Mftf/ActionGroup/AssertStorefrontCartItemExcludingTaxActionGroup.xml b/app/code/Magento/Tax/Test/Mftf/ActionGroup/AssertStorefrontCartItemExcludingTaxActionGroup.xml new file mode 100644 index 0000000000000..cb8e40c3b962c --- /dev/null +++ b/app/code/Magento/Tax/Test/Mftf/ActionGroup/AssertStorefrontCartItemExcludingTaxActionGroup.xml @@ -0,0 +1,24 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + /** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +--> + +<actionGroups xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/actionGroupSchema.xsd"> + <actionGroup name="AssertStorefrontCartItemExcludingTaxActionGroup"> + <annotations> + <description>Validates that the provided product price and subtotal excluding tax are present and correct in the Shopping Cart.</description> + </annotations> + <arguments> + <argument name="productName" defaultValue="{{_defaultProduct.name}}" type="string"/> + <argument name="productPriceExcludingTax" defaultValue="150" type="string"/> + <argument name="productSubtotalExcludingTax" defaultValue="150" type="string"/> + </arguments> + + <see userInput="{{productPriceExcludingTax}}" selector="{{StorefrontCheckoutCartItemTaxSection.productPriceExcludingTaxByName(productName)}}" stepKey="assertProductPriceExcludingTax"/> + <see userInput="{{productSubtotalExcludingTax}}" selector="{{StorefrontCheckoutCartItemTaxSection.productSubtotalExcludingTaxByName(productName)}}" stepKey="assertProductSubtotalExcludingTax"/> + </actionGroup> +</actionGroups> diff --git a/app/code/Magento/Tax/Test/Mftf/ActionGroup/AssertStorefrontCheckoutCartTaxAmountActionGroup.xml b/app/code/Magento/Tax/Test/Mftf/ActionGroup/AssertStorefrontCheckoutCartTaxAmountActionGroup.xml new file mode 100644 index 0000000000000..9a1f60e7433d9 --- /dev/null +++ b/app/code/Magento/Tax/Test/Mftf/ActionGroup/AssertStorefrontCheckoutCartTaxAmountActionGroup.xml @@ -0,0 +1,25 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + /** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +--> + +<actionGroups xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/actionGroupSchema.xsd"> + <actionGroup name="AssertStorefrontCheckoutCartTaxAmountActionGroup"> + <annotations> + <description>Check tax summary data in Summary section of shopping cart on storefront</description> + </annotations> + <arguments> + <argument name="taxAmount" type="string" defaultValue="$0.83"/> + <argument name="rate" type="string" defaultValue="US-CA-*-Rate 1 (8.25%)"/> + </arguments> + + <see selector="{{CheckoutCartSummarySection.taxAmount}}" userInput="{{taxAmount}}" stepKey="checkTaxAmount" /> + <scrollTo selector="{{CheckoutCartSummarySection.taxSummary}}" stepKey="scrollToTaxSummary" /> + <conditionalClick selector="{{CheckoutCartSummarySection.taxSummary}}" dependentSelector="{{CheckoutCartSummarySection.rate}}" visible="false" stepKey="expandTaxSummary"/> + <see selector="{{CheckoutCartSummarySection.rate}}" userInput="{{rate}}" stepKey="checkRate" /> + </actionGroup> +</actionGroups> diff --git a/app/code/Magento/Tax/Test/Mftf/ActionGroup/AssertStorefrontShoppingCartSummaryItemsExcludingAndIncludingTaxActionGroup.xml b/app/code/Magento/Tax/Test/Mftf/ActionGroup/AssertStorefrontShoppingCartSummaryItemsExcludingAndIncludingTaxActionGroup.xml new file mode 100644 index 0000000000000..9497f41ec0d57 --- /dev/null +++ b/app/code/Magento/Tax/Test/Mftf/ActionGroup/AssertStorefrontShoppingCartSummaryItemsExcludingAndIncludingTaxActionGroup.xml @@ -0,0 +1,32 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + /** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +--> + +<actionGroups xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/actionGroupSchema.xsd"> + <actionGroup name="AssertStorefrontShoppingCartSummaryItemsExcludingAndIncludingTaxActionGroup"> + <annotations> + <description>Validates that the provided Subtotal/Total excluding and including tax are present and correct on shopping cart page.</description> + </annotations> + <arguments> + <argument name="subtotalExcludingTax" defaultValue="100" type="string"/> + <argument name="subtotalIncludingTax" defaultValue="108.25" type="string"/> + <argument name="tax" defaultValue="8.25" type="string"/> + <argument name="total" defaultValue="113.25" type="string"/> + </arguments> + + <seeInCurrentUrl url="{{CheckoutCartPage.url}}" stepKey="assertUrl"/> + <waitForElementVisible selector="{{CheckoutCartSummarySection.subtotalExcludingTax}}" stepKey="waitForSubtotalVisible"/> + <see selector="{{CheckoutCartSummarySection.subtotalExcludingTax}}" userInput="{{subtotalExcludingTax}}" stepKey="assertSubtotalExcludingTax"/> + <see selector="{{CheckoutCartSummarySection.subtotalIncludingTax}}" userInput="{{subtotalIncludingTax}}" stepKey="assertSubtotalIncludingTax"/> + <see selector="{{CheckoutCartSummarySection.taxAmount}}" userInput="{{tax}}" stepKey="assertTaxAmount"/> + <waitForElementVisible selector="{{CheckoutCartSummarySection.total}}" stepKey="waitForTotalVisible"/> + <waitForElementVisible selector="{{CheckoutCartSummarySection.totalAmount(total)}}" stepKey="waitForTotalAmountVisible"/> + <see selector="{{CheckoutCartSummarySection.total}}" userInput="{{total}}" stepKey="assertTotal"/> + <seeElement selector="{{CheckoutCartSummarySection.proceedToCheckout}}" stepKey="seeProceedToCheckoutButton"/> + </actionGroup> +</actionGroups> diff --git a/app/code/Magento/Tax/Test/Mftf/Data/TaxConfigData.xml b/app/code/Magento/Tax/Test/Mftf/Data/TaxConfigData.xml index 4edf005c2fc2b..d1f77a69f69f4 100644 --- a/app/code/Magento/Tax/Test/Mftf/Data/TaxConfigData.xml +++ b/app/code/Magento/Tax/Test/Mftf/Data/TaxConfigData.xml @@ -16,6 +16,12 @@ <data key="value">*</data> </entity> <!-- Shopping Cart Display Settings --> + <entity name="IncludeAndExcludeTaxInCartPrice" type="priceCart"> + <data key="value">3</data> + </entity> + <entity name="IncludeAndExcludeTaxInCartSubtotal" type="subtotalCart"> + <data key="value">3</data> + </entity> <entity name="IncludeTaxInOrderTotalCart" type="grandtotalCart"> <data key="value">1</data> </entity> @@ -57,4 +63,26 @@ <entity name="EmptyField" type="taxPostCodeEmpty"> <data key="value"/> </entity> + <entity name="TaxConfigExcludeAndIncludeTaxInCart" type="tax_config_state"> + <!-- Shopping Cart Display Settings --> + <requiredEntity type="priceCart">IncludeAndExcludeTaxInCartPrice</requiredEntity> + <requiredEntity type="subtotalCart">IncludeAndExcludeTaxInCartSubtotal</requiredEntity> + </entity> + <!-- Orders, Invoices, Credit Memos Display Settings--> + <entity name="IncludeAndExcludeTaxInSalesPrice" type="priceSales"> + <data key="value">3</data> + </entity> + <entity name="IncludeAndExcludeTaxInSalesSubtotal" type="subtotalSales"> + <data key="value">3</data> + </entity> + <entity name="TaxConfigExcludeAndIncludeTaxInSales" type="tax_config_state"> + <requiredEntity type="priceSales">IncludeAndExcludeTaxInSalesPrice</requiredEntity> + <requiredEntity type="subtotalSales">IncludeAndExcludeTaxInSalesSubtotal</requiredEntity> + </entity> + <entity name="DefaultProductTaxClass"> + <!-- Default value --> + <data key="path">tax/classes/default_product_tax_class</data> + <data key="label">Yes</data> + <data key="value">2</data> + </entity> </entities> diff --git a/app/code/Magento/Tax/Test/Mftf/Data/TaxRateData.xml b/app/code/Magento/Tax/Test/Mftf/Data/TaxRateData.xml index 20d05e1f572c2..80b158d116aa7 100644 --- a/app/code/Magento/Tax/Test/Mftf/Data/TaxRateData.xml +++ b/app/code/Magento/Tax/Test/Mftf/Data/TaxRateData.xml @@ -127,4 +127,15 @@ <data key="zip_is_range">0</data> <data key="rate">100.0000</data> </entity> + <entity name="TaxRateTexas" type="taxRate"> + <data key="code" unique="suffix">Tax Rate </data> + <data key="tax_region">Texas</data> + <data key="tax_country_id">US</data> + <data key="tax_country">United States</data> + <data key="tax_postcode">78729</data> + <data key="rate">7.25</data> + </entity> + <entity name="SecondTaxRateTexas" extends="TaxRateTexas"> + <data key="rate">0.125</data> + </entity> </entities> diff --git a/app/code/Magento/Tax/Test/Mftf/Metadata/tax_class-meta.xml b/app/code/Magento/Tax/Test/Mftf/Metadata/TaxClassMeta.xml similarity index 100% rename from app/code/Magento/Tax/Test/Mftf/Metadata/tax_class-meta.xml rename to app/code/Magento/Tax/Test/Mftf/Metadata/TaxClassMeta.xml diff --git a/app/code/Magento/Tax/Test/Mftf/Metadata/TaxConfigMeta.xml b/app/code/Magento/Tax/Test/Mftf/Metadata/TaxConfigMeta.xml new file mode 100644 index 0000000000000..848c5de375275 --- /dev/null +++ b/app/code/Magento/Tax/Test/Mftf/Metadata/TaxConfigMeta.xml @@ -0,0 +1,207 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + /** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +--> +<operations xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:noNamespaceSchemaLocation="urn:magento:mftf:DataGenerator/etc/dataOperation.xsd"> + <operation name="CreateTaxConfigDefaultsTaxDestination" dataType="tax_config_state" type="create" auth="adminFormKey" url="/admin/system_config/save/section/tax/" method="POST"> + <object key="groups" dataType="tax_config_state"> + <object key="defaults" dataType="tax_config_state"> + <object key="fields" dataType="tax_config_state"> + <object key="country" dataType="country"> + <field key="value">string</field> + </object> + <object key="region" dataType="region"> + <field key="value">string</field> + </object> + <object key="postcode" dataType="postcode"> + <field key="value">string</field> + </object> + </object> + </object> + <object key="cart_display" dataType="tax_config_state"> + <object key="fields" dataType="tax_config_state"> + <object key="price" dataType="priceCart"> + <field key="value">string</field> + </object> + <object key="subtotal" dataType="subtotalCart"> + <field key="value">string</field> + </object> + <object key="grandtotal" dataType="grandtotalCart"> + <field key="value">string</field> + </object> + <object key="full_summary" dataType="full_summaryCart"> + <field key="value">string</field> + </object> + <object key="zero_tax" dataType="zero_taxCart"> + <field key="value">string</field> + </object> + </object> + </object> + <object key="sales_display" dataType="tax_config_state"> + <object key="fields" dataType="tax_config_state"> + <object key="price" dataType="priceSales"> + <field key="value">string</field> + </object> + <object key="subtotal" dataType="subtotalSales"> + <field key="value">string</field> + </object> + </object> + </object> + </object> + </operation> + <operation name="TaxConfigDefaultsTaxDestination" dataType="tax_config_default" type="create" auth="adminFormKey" url="/admin/system_config/save/section/tax/" method="POST"> + <object key="groups" dataType="tax_config_default"> + <object key="calculation" dataType="tax_config_default"> + <object key="fields" dataType="tax_config_default"> + <object key="algorithm" dataType="tax_config_default"> + <object key="inherit" dataType="taxTotalFlagZero"> + <field key="value">integer</field> + </object> + </object> + <object key="based_on" dataType="tax_config_default"> + <object key="inherit" dataType="taxTotalFlagZero"> + <field key="value">integer</field> + </object> + </object> + <object key="price_includes_tax" dataType="tax_config_default"> + <object key="inherit" dataType="taxTotalFlagZero"> + <field key="value">integer</field> + </object> + </object> + <object key="shipping_includes_tax" dataType="tax_config_default"> + <object key="inherit" dataType="taxTotalFlagZero"> + <field key="value">integer</field> + </object> + </object> + <object key="apply_after_discount" dataType="tax_config_default"> + <object key="inherit" dataType="taxTotalFlagZero"> + <field key="value">integer</field> + </object> + </object> + <object key="discount_tax" dataType="tax_config_default"> + <object key="inherit" dataType="taxTotalFlagZero"> + <field key="value">integer</field> + </object> + </object> + <object key="apply_tax_on" dataType="tax_config_default"> + <object key="inherit" dataType="taxTotalFlagZero"> + <field key="value">integer</field> + </object> + </object> + <object key="cross_border_trade_enabled" dataType="taxTotalFlagZero"> + <field key="value">integer</field> + </object> + </object> + </object> + <object key="defaults" dataType="tax_config_default"> + <object key="fields" dataType="tax_config_default"> + <object key="country" dataType="tax_config_default"> + <object key="inherit" dataType="taxTotalFlagZero"> + <field key="value">integer</field> + </object> + </object> + <object key="region" dataType="tax_config_default"> + <object key="inherit" dataType="taxTotalFlagZero"> + <field key="value">integer</field> + </object> + </object> + <object key="postcode" dataType="taxPostCodeEmpty"> + <field key="value">string</field> + </object> + </object> + </object> + <object key="cart_display" dataType="tax_config_default"> + <object key="fields" dataType="tax_config_default"> + <object key="price" dataType="tax_config_default"> + <object key="inherit" dataType="taxTotalFlagZero"> + <field key="value">integer</field> + </object> + </object> + <object key="subtotal" dataType="tax_config_default"> + <object key="inherit" dataType="taxTotalFlagZero"> + <field key="value">integer</field> + </object> + </object> + <object key="shipping" dataType="tax_config_default"> + <object key="inherit" dataType="taxTotalFlagZero"> + <field key="value">integer</field> + </object> + </object> + <object key="gift_wrapping" dataType="tax_config_default"> + <object key="inherit" dataType="taxTotalFlagZero"> + <field key="value">integer</field> + </object> + </object> + <object key="printed_card" dataType="tax_config_default"> + <object key="inherit" dataType="taxTotalFlagZero"> + <field key="value">integer</field> + </object> + </object> + <object key="grandtotal" dataType="tax_config_default"> + <object key="inherit" dataType="taxTotalFlagZero"> + <field key="value">integer</field> + </object> + </object> + <object key="full_summary" dataType="tax_config_default"> + <object key="inherit" dataType="taxTotalFlagZero"> + <field key="value">integer</field> + </object> + </object> + <object key="zero_tax" dataType="tax_config_default"> + <object key="inherit" dataType="taxTotalFlagZero"> + <field key="value">integer</field> + </object> + </object> + </object> + </object> + <object key="sales_display" dataType="tax_config_default"> + <object key="fields" dataType="tax_config_default"> + <object key="price" dataType="tax_config_default"> + <object key="inherit" dataType="taxTotalFlagZero"> + <field key="value">integer</field> + </object> + </object> + <object key="subtotal" dataType="tax_config_default"> + <object key="inherit" dataType="taxTotalFlagZero"> + <field key="value">integer</field> + </object> + </object> + <object key="shipping" dataType="tax_config_default"> + <object key="inherit" dataType="taxTotalFlagZero"> + <field key="value">integer</field> + </object> + </object> + <object key="gift_wrapping" dataType="tax_config_default"> + <object key="inherit" dataType="taxTotalFlagZero"> + <field key="value">integer</field> + </object> + </object> + <object key="printed_card" dataType="tax_config_default"> + <object key="inherit" dataType="taxTotalFlagZero"> + <field key="value">integer</field> + </object> + </object> + <object key="grandtotal" dataType="tax_config_default"> + <object key="inherit" dataType="taxTotalFlagZero"> + <field key="value">integer</field> + </object> + </object> + <object key="full_summary" dataType="tax_config_default"> + <object key="inherit" dataType="taxTotalFlagZero"> + <field key="value">integer</field> + </object> + </object> + <object key="zero_tax" dataType="tax_config_default"> + <object key="inherit" dataType="taxTotalFlagZero"> + <field key="value">integer</field> + </object> + </object> + </object> + </object> + </object> + </operation> +</operations> diff --git a/app/code/Magento/Tax/Test/Mftf/Metadata/TaxRateMeta.xml b/app/code/Magento/Tax/Test/Mftf/Metadata/TaxRateMeta.xml new file mode 100644 index 0000000000000..6236a6d6c627e --- /dev/null +++ b/app/code/Magento/Tax/Test/Mftf/Metadata/TaxRateMeta.xml @@ -0,0 +1,27 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + /** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +--> +<operations xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:noNamespaceSchemaLocation="urn:magento:mftf:DataGenerator/etc/dataOperation.xsd"> + <operation name="CreateTaxRate" dataType="taxRate" type="create" auth="adminOauth" url="/V1/taxRates" method="POST"> + <contentType>application/json</contentType> + <object key="taxRate" dataType="taxRate"> + <field key="id">integer</field> + <field key="tax_country_id">string</field> + <field key="tax_region_id">integer</field> + <field key="tax_postcode">string</field> + <field key="zip_is_range">integer</field> + <field key="zip_from">integer</field> + <field key="zip_to">integer</field> + <field key="rate">number</field> + <field key="code">string</field> + </object> + </operation> + <operation name="DeleteTaxRate" dataType="taxRate" type="delete" auth="adminOauth" url="/V1/taxRates/{id}" method="DELETE"> + <contentType>application/json</contentType> + </operation> +</operations> diff --git a/app/code/Magento/Tax/Test/Mftf/Metadata/tax_rule-meta.xml b/app/code/Magento/Tax/Test/Mftf/Metadata/TaxRuleMeta.xml similarity index 100% rename from app/code/Magento/Tax/Test/Mftf/Metadata/tax_rule-meta.xml rename to app/code/Magento/Tax/Test/Mftf/Metadata/TaxRuleMeta.xml diff --git a/app/code/Magento/Tax/Test/Mftf/Metadata/tax_config-meta.xml b/app/code/Magento/Tax/Test/Mftf/Metadata/tax_config-meta.xml deleted file mode 100644 index 7383e9c580283..0000000000000 --- a/app/code/Magento/Tax/Test/Mftf/Metadata/tax_config-meta.xml +++ /dev/null @@ -1,183 +0,0 @@ -<?xml version="1.0" encoding="UTF-8"?> -<!-- - /** - * Copyright © Magento, Inc. All rights reserved. - * See COPYING.txt for license details. - */ ---> -<operations xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" - xsi:noNamespaceSchemaLocation="urn:magento:mftf:DataGenerator/etc/dataOperation.xsd"> - <operation name="CreateTaxConfigDefaultsTaxDestination" dataType="tax_config_state" type="create" auth="adminFormKey" url="/admin/system_config/save/section/tax/" method="POST"> - <object key="groups" dataType="tax_config_state"> - <object key="defaults" dataType="tax_config_state"> - <object key="fields" dataType="tax_config_state"> - <object key="country" dataType="country"> - <field key="value">string</field> - </object> - <object key="region" dataType="region"> - <field key="value">string</field> - </object> - <object key="postcode" dataType="postcode"> - <field key="value">string</field> - </object> - </object> - </object> - <object key="cart_display" dataType="tax_config_state"> - <object key="fields" dataType="tax_config_state"> - <object key="grandtotal" dataType="grandtotalCart"> - <field key="value">string</field> - </object> - <object key="full_summary" dataType="full_summaryCart"> - <field key="value">string</field> - </object> - <object key="zero_tax" dataType="zero_taxCart"> - <field key="value">string</field> - </object> - </object> - </object> - </object> - </operation> - <operation name="TaxConfigDefaultsTaxDestination" dataType="tax_config_default" type="create" auth="adminFormKey" url="/admin/system_config/save/section/tax/" method="POST"> - <object key="groups" dataType="tax_config_default"> - <object key="calculation" dataType="tax_config_default"> - <object key="fields" dataType="tax_config_default"> - <object key="algorithm" dataType="tax_config_default"> - <object key="inherit" dataType="taxTotalFlagZero"> - <field key="value">integer</field> - </object> - </object> - <object key="based_on" dataType="tax_config_default"> - <object key="inherit" dataType="taxTotalFlagZero"> - <field key="value">integer</field> - </object> - </object> - <object key="price_includes_tax" dataType="tax_config_default"> - <object key="inherit" dataType="taxTotalFlagZero"> - <field key="value">integer</field> - </object> - </object> - <object key="shipping_includes_tax" dataType="tax_config_default"> - <object key="inherit" dataType="taxTotalFlagZero"> - <field key="value">integer</field> - </object> - </object> - <object key="apply_after_discount" dataType="tax_config_default"> - <object key="inherit" dataType="taxTotalFlagZero"> - <field key="value">integer</field> - </object> - </object> - <object key="discount_tax" dataType="tax_config_default"> - <object key="inherit" dataType="taxTotalFlagZero"> - <field key="value">integer</field> - </object> - </object> - <object key="apply_tax_on" dataType="tax_config_default"> - <object key="inherit" dataType="taxTotalFlagZero"> - <field key="value">integer</field> - </object> - </object> - <object key="cross_border_trade_enabled" dataType="taxTotalFlagZero"> - <field key="value">integer</field> - </object> - </object> - </object> - <object key="defaults" dataType="tax_config_default"> - <object key="fields" dataType="tax_config_default"> - <object key="country" dataType="tax_config_default"> - <object key="inherit" dataType="taxTotalFlagZero"> - <field key="value">integer</field> - </object> - </object> - <object key="region" dataType="tax_config_default"> - <object key="inherit" dataType="taxTotalFlagZero"> - <field key="value">integer</field> - </object> - </object> - <object key="postcode" dataType="taxPostCodeEmpty"> - <field key="value">string</field> - </object> - </object> - </object> - <object key="cart_display" dataType="tax_config_default"> - <object key="fields" dataType="tax_config_default"> - <object key="price" dataType="tax_config_default"> - <object key="inherit" dataType="taxTotalFlagZero"> - <field key="value">integer</field> - </object> - </object> - <object key="subtotal" dataType="tax_config_default"> - <object key="inherit" dataType="taxTotalFlagZero"> - <field key="value">integer</field> - </object> - </object> - <object key="shipping" dataType="tax_config_default"> - <object key="inherit" dataType="taxTotalFlagZero"> - <field key="value">integer</field> - </object> - </object> - <object key="gift_wrapping" dataType="taxTotalFlagZero"> - <field key="value">integer</field> - </object> - <object key="printed_card" dataType="taxTotalFlagZero"> - <field key="value">integer</field> - </object> - <object key="grandtotal" dataType="tax_config_default"> - <object key="inherit" dataType="taxTotalFlagZero"> - <field key="value">integer</field> - </object> - </object> - <object key="full_summary" dataType="tax_config_default"> - <object key="inherit" dataType="taxTotalFlagZero"> - <field key="value">integer</field> - </object> - </object> - <object key="zero_tax" dataType="tax_config_default"> - <object key="inherit" dataType="taxTotalFlagZero"> - <field key="value">integer</field> - </object> - </object> - </object> - </object> - <object key="sales_display" dataType="tax_config_default"> - <object key="fields" dataType="tax_config_default"> - <object key="price" dataType="tax_config_default"> - <object key="inherit" dataType="taxTotalFlagZero"> - <field key="value">integer</field> - </object> - </object> - <object key="subtotal" dataType="tax_config_default"> - <object key="inherit" dataType="taxTotalFlagZero"> - <field key="value">integer</field> - </object> - </object> - <object key="shipping" dataType="tax_config_default"> - <object key="inherit" dataType="taxTotalFlagZero"> - <field key="value">integer</field> - </object> - </object> - <object key="gift_wrapping" dataType="taxTotalFlagZero"> - <field key="value">integer</field> - </object> - <object key="printed_card" dataType="taxTotalFlagZero"> - <field key="value">integer</field> - </object> - <object key="grandtotal" dataType="tax_config_default"> - <object key="inherit" dataType="taxTotalFlagZero"> - <field key="value">integer</field> - </object> - </object> - <object key="full_summary" dataType="tax_config_default"> - <object key="inherit" dataType="taxTotalFlagZero"> - <field key="value">integer</field> - </object> - </object> - <object key="zero_tax" dataType="tax_config_default"> - <object key="inherit" dataType="taxTotalFlagZero"> - <field key="value">integer</field> - </object> - </object> - </object> - </object> - </object> - </operation> -</operations> diff --git a/app/code/Magento/Tax/Test/Mftf/Metadata/tax_rate-meta.xml b/app/code/Magento/Tax/Test/Mftf/Metadata/tax_rate-meta.xml deleted file mode 100644 index 3f192920c5cc3..0000000000000 --- a/app/code/Magento/Tax/Test/Mftf/Metadata/tax_rate-meta.xml +++ /dev/null @@ -1,27 +0,0 @@ -<?xml version="1.0" encoding="UTF-8"?> -<!-- - /** - * Copyright © Magento, Inc. All rights reserved. - * See COPYING.txt for license details. - */ ---> -<operations xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" - xsi:noNamespaceSchemaLocation="urn:magento:mftf:DataGenerator/etc/dataOperation.xsd"> - <operation name="CreateTaxRate" dataType="taxRate" type="create" auth="adminOauth" url="/V1/taxRates" method="POST"> - <contentType>application/json</contentType> - <object key="taxRate" dataType="taxRate"> - <field key="id">integer</field> - <field key="tax_country_id">string</field> - <field key="tax_region_id">integer</field> - <field key="tax_postcode">string</field> - <field key="zip_is_range">integer</field> - <field key="zip_from">integer</field> - <field key="zip_to">integer</field> - <field key="rate">integer</field> - <field key="code">string</field> - </object> - </operation> - <operation name="DeleteTaxRate" dataType="taxRate" type="delete" auth="adminOauth" url="/V1/taxRates/{id}" method="DELETE"> - <contentType>application/json</contentType> - </operation> -</operations> \ No newline at end of file diff --git a/app/code/Magento/Tax/Test/Mftf/Page/CheckoutCartPage.xml b/app/code/Magento/Tax/Test/Mftf/Page/CheckoutCartPage.xml new file mode 100644 index 0000000000000..9b0b4fc537e1e --- /dev/null +++ b/app/code/Magento/Tax/Test/Mftf/Page/CheckoutCartPage.xml @@ -0,0 +1,14 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + /** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +--> + +<pages xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Page/etc/PageObject.xsd"> + <page name="CheckoutCartPage" url="/checkout/cart" module="Magento_Checkout" area="storefront"> + <section name="StorefrontCheckoutCartItemTaxSection"/> + </page> +</pages> diff --git a/app/code/Magento/Tax/Test/Mftf/Section/AdminOrderFormTotalSection.xml b/app/code/Magento/Tax/Test/Mftf/Section/AdminOrderFormTotalSection.xml new file mode 100644 index 0000000000000..9b033bb375eb2 --- /dev/null +++ b/app/code/Magento/Tax/Test/Mftf/Section/AdminOrderFormTotalSection.xml @@ -0,0 +1,14 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + /** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +--> + +<sections xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Page/etc/SectionObject.xsd"> + <section name="AdminOrderFormTotalSection"> + <element name="totalTax" type="text" selector="//table[contains(@class, 'order-subtotal-table')]/tbody/tr/td/div[contains(text(), 'Tax')]/ancestor::tr/td/span[contains(@class, 'price')]"/> + </section> +</sections> diff --git a/app/code/Magento/Tax/Test/Mftf/Section/AdminOrderItemsOrderedSection.xml b/app/code/Magento/Tax/Test/Mftf/Section/AdminOrderItemsOrderedSection.xml new file mode 100644 index 0000000000000..969b59af2a3c3 --- /dev/null +++ b/app/code/Magento/Tax/Test/Mftf/Section/AdminOrderItemsOrderedSection.xml @@ -0,0 +1,14 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + /** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +--> + +<sections xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Page/etc/SectionObject.xsd"> + <section name="AdminOrderItemsOrderedSection"> + <element name="itemTaxAmountByProductName" type="text" selector="//table[contains(@class,'edit-order-table')]//div[contains(text(),'{{productName}}')]/ancestor::tr//td[contains(@class, 'col-tax-amount')]//span" parameterized="true"/> + </section> +</sections> diff --git a/app/code/Magento/Tax/Test/Mftf/Section/AdminTaxReportsSection.xml b/app/code/Magento/Tax/Test/Mftf/Section/AdminTaxReportsSection.xml index 80101687e173e..3cfa8206a089c 100644 --- a/app/code/Magento/Tax/Test/Mftf/Section/AdminTaxReportsSection.xml +++ b/app/code/Magento/Tax/Test/Mftf/Section/AdminTaxReportsSection.xml @@ -7,10 +7,11 @@ --> <sections xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" - xsi:noNamespaceSchemaLocation="../../../../../../../vendor/magento/magento2-functional-testing-framework/src/Magento/FunctionalTestingFramework/Page/etc/SectionObject.xsd"> + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Page/etc/SectionObject.xsd"> <section name="AdminTaxReportsSection"> <element name="refreshStatistics" type="button" selector="//a[contains(text(),'here')]"/> <element name="fromDate" type="input" selector="#sales_report_from"/> + <element name="toDateInput" type="input" selector="#sales_report_to"/> <element name="toDate" type="input" selector="//*[@id='sales_report_to']/following-sibling::button"/> <element name="goTodayButton" type="input" selector="//button[contains(text(),'Go Today')]"/> <element name="showReportButton" type="button" selector="#filter_form_submit"/> diff --git a/app/code/Magento/Tax/Test/Mftf/Section/AdminTaxRuleFormSection.xml b/app/code/Magento/Tax/Test/Mftf/Section/AdminTaxRuleFormSection.xml index c77d3ad0d9444..a7e5826454ac6 100644 --- a/app/code/Magento/Tax/Test/Mftf/Section/AdminTaxRuleFormSection.xml +++ b/app/code/Magento/Tax/Test/Mftf/Section/AdminTaxRuleFormSection.xml @@ -19,6 +19,7 @@ <element name="deleteRule" type="button" selector="#delete" timeout="30"/> <element name="ok" type="button" selector="button.action-primary.action-accept" timeout="30"/> <element name="additionalSettings" type="button" selector="#details-summarybase_fieldset" timeout="30"/> + <element name="additionalSettingsOpened" type="button" selector="#details-summarybase_fieldset[aria-expanded=true]"/> <element name="customerTaxClassOption" type="checkbox" selector="//*[@id='tax_customer_class']/..//span[.='{{taxCustomerClass}}']" parameterized="true"/> <element name="productTaxClassOption" type="checkbox" selector="//*[@id='tax_product_class']/..//span[.='{{taxProductClass}}']" parameterized="true"/> <element name="customerTaxClassSelected" type="checkbox" selector="//*[@id='tax_customer_class']/..//span[.='{{taxCustomerClass}}' and preceding-sibling::input[contains(@class, 'mselect-checked')]]" parameterized="true"/> diff --git a/app/code/Magento/Tax/Test/Mftf/Section/AdminTaxRulesSection.xml b/app/code/Magento/Tax/Test/Mftf/Section/AdminTaxRulesSection.xml index 7f721d4079c27..c69d555d5177a 100644 --- a/app/code/Magento/Tax/Test/Mftf/Section/AdminTaxRulesSection.xml +++ b/app/code/Magento/Tax/Test/Mftf/Section/AdminTaxRulesSection.xml @@ -35,5 +35,6 @@ <element name="taxRateMultiSelectItems" type="block" selector=".mselect-list-item"/> <element name="taxRateNumber" type="button" selector="//div[@data-ui-id='tax-rate-form-fieldset-element-form-field-tax-rate']//div[@class='mselect-items-wrapper']//label[{{var}}]" parameterized="true"/> <element name="selectTaxRate" type="input" selector="//span[text()='{{taxCode}}']" parameterized="true"/> + <element name="selectProductTaxClass" type="input" selector="//div[contains(@class, 'field-tax_product_class')]//*[contains(@class, 'mselect-list-item') and contains(.,'{{taxClass}}')]" parameterized="true"/> </section> </sections> diff --git a/app/code/Magento/Tax/Test/Mftf/Section/CheckoutCartSummarySection.xml b/app/code/Magento/Tax/Test/Mftf/Section/CheckoutCartSummarySection.xml index b89a77b8ad2ca..da6528215887b 100644 --- a/app/code/Magento/Tax/Test/Mftf/Section/CheckoutCartSummarySection.xml +++ b/app/code/Magento/Tax/Test/Mftf/Section/CheckoutCartSummarySection.xml @@ -12,5 +12,7 @@ <element name="taxAmount" type="text" selector="[data-th='Tax']>span"/> <element name="taxSummary" type="text" selector=".totals-tax-summary"/> <element name="rate" type="text" selector=" tr.totals-tax-details.shown th.mark"/> + <element name="subtotalExcludingTax" type="text" selector="#cart-totals .totals.sub.excl span.price"/> + <element name="subtotalIncludingTax" type="text" selector="#cart-totals .totals.sub.incl span.price"/> </section> </sections> diff --git a/app/code/Magento/Tax/Test/Mftf/Section/StorefrontCheckoutCartItemTaxSection.xml b/app/code/Magento/Tax/Test/Mftf/Section/StorefrontCheckoutCartItemTaxSection.xml new file mode 100644 index 0000000000000..c91a1fe4fb08b --- /dev/null +++ b/app/code/Magento/Tax/Test/Mftf/Section/StorefrontCheckoutCartItemTaxSection.xml @@ -0,0 +1,15 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + /** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +--> + +<sections xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Page/etc/SectionObject.xsd"> + <section name="StorefrontCheckoutCartItemTaxSection"> + <element name="productPriceExcludingTaxByName" type="text" selector="//a[contains(text(),'{{productName}}')]/ancestor::tr//td[contains(@class, 'price')]//span[contains(@class,'price-excluding-tax')]//span[contains(@class,'cart-tax-total')]" parameterized="true"/> + <element name="productSubtotalExcludingTaxByName" type="text" selector="//a[contains(text(),'{{productName}}')]/ancestor::tr//td[contains(@class, 'subtotal')]//span[contains(@class,'price-excluding-tax')]//span[contains(@class,'cart-tax-total')]" parameterized="true"/> + </section> +</sections> diff --git a/app/code/Magento/Tax/Test/Mftf/Test/AdminCheckCreditMemoTotalsTest.xml b/app/code/Magento/Tax/Test/Mftf/Test/AdminCheckCreditMemoTotalsTest.xml new file mode 100644 index 0000000000000..fc4b6dd8b84c5 --- /dev/null +++ b/app/code/Magento/Tax/Test/Mftf/Test/AdminCheckCreditMemoTotalsTest.xml @@ -0,0 +1,89 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + /** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +--> + +<tests xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/testSchema.xsd"> + <test name="AdminCheckCreditMemoTotalsTest"> + <annotations> + <features value="Tax"/> + <stories value="Credit memo entity"/> + <title value="Checking Credit memo Totals"/> + <description value="Checking Credit memo Totals"/> + <severity value="MAJOR"/> + <testCaseId value="MC-25752"/> + <group value="tax"/> + <group value="sales"/> + </annotations> + <before> + <!-- Create productTaxClass --> + <createData entity="productTaxClass" stepKey="createProductTaxClass"/> + <!--Set configs--> + <magentoCLI command="config:set {{DefaultProductTaxClass.path}} $createProductTaxClass.return$" stepKey="setDefaultProductTaxClass"/> + <!--Create category and product--> + <createData entity="_defaultCategory" stepKey="createCategory"/> + <createData entity="_defaultProduct" stepKey="createSimpleProduct"> + <requiredEntity createDataKey="createCategory"/> + <field key="productTaxClass">$createProductTaxClass.taxClass[class_name]$</field> + </createData> + <!--Create customer--> + <createData entity="Simple_US_Customer_NY" stepKey="createCustomer"/> + <!--Login as admin--> + <actionGroup ref="AdminLoginActionGroup" stepKey="loginAsAdmin"/> + <!--Create tax rule--> + <actionGroup ref="AdminCreateTaxRuleCustomProductTaxClassActionGroup" stepKey="createTaxRuleCustomProductTaxClass"> + <argument name="productTaxClassName" value="$createProductTaxClass.taxClass[class_name]$"/> + </actionGroup> + </before> + <after> + <!--Set configs--> + <magentoCLI command="config:set {{DefaultProductTaxClass.path}} {{DefaultProductTaxClass.value}}" stepKey="setDefaultProductTaxClass"/> + <!--Delete category and product--> + <deleteData createDataKey="createCategory" stepKey="deleteCategory"/> + <deleteData createDataKey="createSimpleProduct" stepKey="deleteSimpleProduct"/> + <!--Delete customer--> + <deleteData createDataKey="createCustomer" stepKey="deleteCustomer"/> + <!-- Reset admin order filter --> + <actionGroup ref="AdminOrdersGridClearFiltersActionGroup" stepKey="clearOrderFilters"/> + <!-- Go to the tax rule page and delete the row we created--> + <amOnPage url="{{AdminTaxRuleGridPage.url}}" stepKey="goToTaxRulesPage"/> + <actionGroup ref="deleteEntitySecondaryGrid" stepKey="deleteRule"> + <argument name="name" value="{{defaultTaxRule.code}}"/> + <argument name="searchInput" value="{{AdminSecondaryGridSection.taxIdentifierSearch}}"/> + </actionGroup> + <actionGroup ref="ClearFiltersAdminDataGridActionGroup" stepKey="clearTaxRuleFilter"/> + <!-- Delete Tax Class --> + <deleteData createDataKey="createProductTaxClass" stepKey="deleteProductTaxClass"/> + <!--Logout--> + <actionGroup ref="AdminLogoutActionGroup" stepKey="logoutFromAdmin"/> + </after> + <!--Create new order--> + <actionGroup ref="NavigateToNewOrderPageExistingCustomerActionGroup" stepKey="createNewOrder"> + <argument name="customer" value="Simple_US_Customer_NY"/> + </actionGroup> + <!--Add product to order--> + <actionGroup ref="AddSimpleProductToOrderActionGroup" stepKey="addSimpleProductToOrder"> + <argument name="product" value="$createSimpleProduct$"/> + </actionGroup> + <!--Set shipping method--> + <actionGroup ref="OrderSelectFlatRateShippingActionGroup" stepKey="orderSelectFlatRateShipping"/> + <!--Submit order--> + <actionGroup ref="AdminSubmitOrderActionGroup" stepKey="submitOrder"/> + <!--Create order invoice--> + <actionGroup ref="StartCreateInvoiceFromOrderPageActionGroup" stepKey="startCreateInvoice"/> + <actionGroup ref="SubmitInvoiceActionGroup" stepKey="submitInvoice"/> + <!--Create Credit Memo--> + <actionGroup ref="AdminStartToCreateCreditMemoFromOrderPageActionGroup" stepKey="startCreatingCreditMemo"/> + <fillField selector="{{AdminCreditMemoTotalSection.refundShipping}}" userInput="0" stepKey="setRefundShipping"/> + <actionGroup ref="UpdateCreditMemoTotalsActionGroup" stepKey="updateTotals"/> + <actionGroup ref="SubmitCreditMemoActionGroup" stepKey="submitCreditMemo"/> + <actionGroup ref="AdminOpenCreditMemoFromOrderPageActionGroup" stepKey="openCreditMemoFromOrderPageActionGroup"/> + <actionGroup ref="AssertAdminCreditMemoGrandTotalActionGroup" stepKey="assertGrandTotal"> + <argument name="expectedGrandTotal" value="$133.30"/> + </actionGroup> + </test> +</tests> diff --git a/app/code/Magento/Tax/Test/Mftf/Test/AdminCheckingTaxReportGridTest.xml b/app/code/Magento/Tax/Test/Mftf/Test/AdminCheckingTaxReportGridTest.xml new file mode 100644 index 0000000000000..01e1677ec8d8a --- /dev/null +++ b/app/code/Magento/Tax/Test/Mftf/Test/AdminCheckingTaxReportGridTest.xml @@ -0,0 +1,207 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + /** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +--> + +<tests xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/testSchema.xsd"> + <test name="AdminCheckingTaxReportGridTest"> + <annotations> + <features value="Tax"/> + <stories value="Tax Report Grid"/> + <title value="Checking Tax Report grid"/> + <description value="Tax Report Grid displays Tax amount in rows 'Total' and 'Subtotal' is a sum of all tax amounts"/> + <severity value="MAJOR"/> + <testCaseId value="MC-25815"/> + <useCaseId value="MAGETWO-91521"/> + <group value="Tax"/> + </annotations> + <before> + <!-- Create category and product --> + <createData entity="_defaultCategory" stepKey="createCategory"/> + <createData entity="_defaultProduct" stepKey="createFirstProduct"> + <requiredEntity createDataKey="createCategory"/> + </createData> + <createData entity="_defaultProduct" stepKey="createSecondProduct"> + <requiredEntity createDataKey="createCategory"/> + </createData> + + <!-- Create Tax Rule and Tax Rate --> + <createData entity="SimpleTaxRule" stepKey="createTaxRule"/> + <createData entity="SimpleTaxRule" stepKey="createSecondTaxRule"/> + <createData entity="TaxRateTexas" stepKey="createTaxRate"/> + <createData entity="SecondTaxRateTexas" stepKey="createSecondTaxRate"/> + + <!-- Create product tax class --> + <createData entity="productTaxClass" stepKey="createProductTaxClass"/> + <getData entity="productTaxClass" stepKey="productTaxClass"> + <requiredEntity createDataKey="createProductTaxClass"/> + </getData> + <createData entity="productTaxClass" stepKey="createSecondProductTaxClass"/> + <getData entity="productTaxClass" stepKey="productSecondTaxClass"> + <requiredEntity createDataKey="createSecondProductTaxClass"/> + </getData> + + <!-- Login to Admin --> + <actionGroup ref="AdminLoginActionGroup" stepKey="loginAsAdmin"/> + + <!-- Go to Tax Rule page, add Tax Rate, unassign Default Tax Rate --> + <amOnPage url="{{AdminEditTaxRulePage.url($createTaxRule.id$)}}" stepKey="goToTaxRulePage"/> + <waitForPageLoad stepKey="waitForTaxRulePage"/> + <actionGroup ref="AdminSelectTaxRateActionGroup" stepKey="assignTaxRate"> + <argument name="taxRate" value="$createTaxRate.code$"/> + </actionGroup> + + <!-- Assign Product Tax Class and Unassign Default Product Tax Class --> + <actionGroup ref="AdminSelectProductTaxClassActionGroup" stepKey="assignProductTaxClass"> + <argument name="taxClass" value="$productTaxClass.class_name$"/> + </actionGroup> + <actionGroup ref="AdminUnassignProductTaxClassActionGroup" stepKey="unSelectTaxRuleDefaultProductTax"> + <argument name="taxClass" value="{{taxableGoodsTaxClass.class_name}}"/> + </actionGroup> + + <!-- Save Tax Rule --> + <actionGroup ref="ClickSaveButtonActionGroup" stepKey="saveTaxRule"> + <argument name="message" value="You saved the tax rule."/> + </actionGroup> + + <!-- Go to Tax Rule page to create second Tax Rule, add Tax Rate, unassign Default Tax Rate --> + <amOnPage url="{{AdminEditTaxRulePage.url($createSecondTaxRule.id$)}}" stepKey="goToSecondTaxRulePage"/> + <waitForPageLoad stepKey="waitForSecondTaxRatePage"/> + <actionGroup ref="AdminSelectTaxRateActionGroup" stepKey="assignSecondTaxRate"> + <argument name="taxRate" value="$createSecondTaxRate.code$"/> + </actionGroup> + <!-- Assign Product Tax Class and Unassign Default Product Tax Class --> + <actionGroup ref="AdminSelectProductTaxClassActionGroup" stepKey="assignSecondProductTaxClass"> + <argument name="taxClass" value="$productSecondTaxClass.class_name$"/> + </actionGroup> + <actionGroup ref="AdminUnassignProductTaxClassActionGroup" stepKey="unaSelectTaxRuleDefaultSecondProductTaxClass"> + <argument name="taxClass" value="{{taxableGoodsTaxClass.class_name}}"/> + </actionGroup> + + <!-- Save Tax Rule --> + <actionGroup ref="ClickSaveButtonActionGroup" stepKey="saveSecondTaxRule"> + <argument name="message" value="You saved the tax rule."/> + </actionGroup> + </before> + <after> + <!-- Delete product and category --> + <deleteData createDataKey="createFirstProduct" stepKey="deleteFirstProduct"/> + <deleteData createDataKey="createSecondProduct" stepKey="deleteSecondProduct"/> + <deleteData createDataKey="createCategory" stepKey="deleteCategory"/> + + <!--Delete Tax Rule --> + <deleteData createDataKey="createTaxRule" stepKey="deleteRule"/> + <deleteData createDataKey="createSecondTaxRule" stepKey="deleteSecondRule"/> + + <!-- Delete Tax Rate --> + <deleteData createDataKey="createTaxRate" stepKey="deleteTaxRate"/> + <deleteData createDataKey="createSecondTaxRate" stepKey="deleteSecondTaxRate"/> + + <!-- Delete Product Tax Class --> + <deleteData createDataKey="createProductTaxClass" stepKey="deleteProductTaxClass"/> + <deleteData createDataKey="createSecondProductTaxClass" stepKey="deleteSecondProductTaxClass"/> + + <!-- Clear filter Product --> + <amOnPage url="{{AdminProductIndexPage.url}}" stepKey="amOnProductGridPage"/> + <actionGroup ref="ClearFiltersAdminDataGridActionGroup" stepKey="clearFilterProduct"/> + + <!-- Delete Customer and clear filter --> + <actionGroup ref="DeleteCustomerByEmailActionGroup" stepKey="deleteCustomer"> + <argument name="email" value="{{Simple_US_Customer.email}}"/> + </actionGroup> + <actionGroup ref="AssertMessageInAdminPanelActionGroup" stepKey="assertSuccessMessage"> + <argument name="message" value="A total of 1 record(s) were deleted."/> + </actionGroup> + <actionGroup ref="ClearFiltersAdminDataGridActionGroup" stepKey="clearFilterCustomer"/> + + <!-- Logout Admin --> + <actionGroup ref="AdminLogoutActionGroup" stepKey="logoutFromAdmin"/> + </after> + + <!--Open Created product. In Tax Class select new created Product Tax class.--> + <actionGroup ref="AdminProductPageOpenByIdActionGroup" stepKey="openProductForEdit"> + <argument name="productId" value="$createFirstProduct.id$"/> + </actionGroup> + <selectOption selector="{{AdminProductFormSection.productTaxClass}}" userInput="$productTaxClass.class_name$" stepKey="selectTexClassForProduct"/> + <!-- Save the second product --> + <actionGroup ref="SaveProductFormActionGroup" stepKey="saveProduct"/> + + <!--Open Created Second Product. In Tax Class select new created Product Tax class.--> + <actionGroup ref="AdminProductPageOpenByIdActionGroup" stepKey="openSecondProductForEdit"> + <argument name="productId" value="$createSecondProduct.id$"/> + </actionGroup> + + <selectOption selector="{{AdminProductFormSection.productTaxClass}}" userInput="$productSecondTaxClass.class_name$" stepKey="selectTexClassForSecondProduct"/> + + <!-- Save the second product --> + <actionGroup ref="SaveProductFormActionGroup" stepKey="saveSecondProduct"/> + + <!--Create an order with these 2 products in that zip code.--> + <actionGroup ref="NavigateToNewOrderPageNewCustomerActionGroup" stepKey="navigateToNewOrder"/> + <!--Check if order can be submitted without the required fields including email address--> + <scrollToTopOfPage stepKey="scrollToTopOfOrderFormPage"/> + <waitForElementVisible selector="{{AdminOrderFormItemsSection.addProducts}}" stepKey="waitForAddProductButton"/> + <actionGroup ref="AddSimpleProductToOrderActionGroup" stepKey="addFirstProductToOrder"> + <argument name="product" value="$createFirstProduct$"/> + </actionGroup> + <waitForElementVisible selector="{{AdminOrderFormItemsSection.addProducts}}" stepKey="waitForAddProductButtonAfterOneProductIsAdded"/> + <actionGroup ref="AddSimpleProductToOrderActionGroup" stepKey="addSecondProductToOrder"> + <argument name="product" value="$createSecondProduct$"/> + </actionGroup> + + <!--Fill customer group and customer email--> + <selectOption selector="{{AdminOrderFormAccountSection.group}}" userInput="{{GeneralCustomerGroup.code}}" stepKey="selectCustomerGroup"/> + <fillField selector="{{AdminOrderFormAccountSection.email}}" userInput="{{Simple_US_Customer.email}}" stepKey="fillCustomerEmail"/> + + <!--Fill customer address information--> + <actionGroup ref="FillOrderCustomerInformationActionGroup" stepKey="fillCustomerAddress"> + <argument name="customer" value="Simple_US_Customer"/> + <argument name="address" value="US_Address_TX"/> + </actionGroup> + + <!-- Select shipping --> + <actionGroup ref="OrderSelectFlatRateShippingActionGroup" stepKey="selectFlatRateShipping"/> + <!-- Checkout select Check/Money Order payment --> + <actionGroup ref="SelectCheckMoneyPaymentMethodActionGroup" stepKey="selectCheckMoneyPayment"/> + <!--Submit Order and verify information--> + <actionGroup ref="AdminSubmitOrderActionGroup" stepKey="submitOrder"/> + + <!-- Grab tax amounts --> + <!-- need check selector --> + <grabTextFrom selector="{{AdminOrderItemsOrderedSection.itemTaxAmountByProductName($createFirstProduct.name$)}}" stepKey="amountOfTaxOnFirstProduct"/> + <grabTextFrom selector="{{AdminOrderItemsOrderedSection.itemTaxAmountByProductName($createSecondProduct.name$)}}" stepKey="amountOfTaxOnSecondProduct"/> + <grabTextFrom selector="{{AdminOrderFormTotalSection.totalTax}}" stepKey="amountOfTotalTax"/> + + <!--Create Invoice and Shipment for this Order.--> + <actionGroup ref="StartCreateInvoiceFromOrderPageActionGroup" stepKey="startCreatingInvoice"/> + + <actionGroup ref="SubmitInvoiceActionGroup" stepKey="clickSubmitInvoice"/> + + <actionGroup ref="GoToShipmentIntoOrderActionGroup" stepKey="seeShipmentOrderPage"/> + <!--Submit Shipment--> + <actionGroup ref="SubmitShipmentIntoOrderActionGroup" stepKey="clickSubmitShipment"/> + <!--Go to "Reports" -> "Sales" -> "Tax"--> + <amOnPage url="{{AdminSalesTaxReportPage.url}}" stepKey="navigateToReportsTaxPage"/> + <waitForPageLoad stepKey="waitForReportsTaxPageLoad"/> + + <!--click "here" to refresh last day's statistics --> + <click selector="{{AdminTaxReportsSection.refreshStatistics}}" stepKey="clickRefreshStatistics"/> + <waitForPageLoad time="30" stepKey="waitForRefresh"/> + + <!--Select Dates--> + <generateDate date="+0 day" format="m/d/Y" stepKey="today"/> + <fillField selector="{{AdminTaxReportsSection.fromDate}}" userInput="{$today}" stepKey="fillDateFrom"/> + <fillField selector="{{AdminTaxReportsSection.toDateInput}}" userInput="{$today}" stepKey="fillDateTo"/> + <!--Click "Show report" in the upper right corner.--> + <click selector="{{AdminTaxReportsSection.showReportButton}}" stepKey="clickShowReportButton"/> + <waitForPageLoad time="60" stepKey="waitForReload"/> + <!--Tax Report Grid displays Tax amount in rows. "Total" and "Subtotal" is a sum of all tax amounts--> + <see selector="{{AdminTaxReportsSection.taxRuleAmount(TaxRateTexas.code)}}" userInput="$amountOfTaxOnFirstProduct" stepKey="assertSubtotalFirstField"/> + <see selector="{{AdminTaxReportsSection.taxRuleAmount(SecondTaxRateTexas.code)}}" userInput="$amountOfTaxOnSecondProduct" stepKey="assertSubtotalSecondField"/> + <see selector="{{AdminTaxReportsSection.taxRuleAmount('Subtotal')}}" userInput="$amountOfTotalTax" stepKey="assertSubtotalField"/> + </test> +</tests> diff --git a/app/code/Magento/Tax/Test/Mftf/Test/AdminCreateDefaultsTaxRuleTest.xml b/app/code/Magento/Tax/Test/Mftf/Test/AdminCreateDefaultsTaxRuleTest.xml index e632f6265f438..3a5f905d89dd5 100644 --- a/app/code/Magento/Tax/Test/Mftf/Test/AdminCreateDefaultsTaxRuleTest.xml +++ b/app/code/Magento/Tax/Test/Mftf/Test/AdminCreateDefaultsTaxRuleTest.xml @@ -20,7 +20,7 @@ </annotations> <before> <createData entity="defaultTaxRate" stepKey="initialTaxRate"/> - <actionGroup ref="LoginAsAdmin" stepKey="loginAsAdmin"/> + <actionGroup ref="AdminLoginActionGroup" stepKey="loginAsAdmin"/> </before> <after> <actionGroup ref="AdminDeleteTaxRule" stepKey="deleteTaxRule"> diff --git a/app/code/Magento/Tax/Test/Mftf/Test/AdminCreateTaxRateAllPostCodesTest.xml b/app/code/Magento/Tax/Test/Mftf/Test/AdminCreateTaxRateAllPostCodesTest.xml index b63ca1953157f..06160eb09b787 100644 --- a/app/code/Magento/Tax/Test/Mftf/Test/AdminCreateTaxRateAllPostCodesTest.xml +++ b/app/code/Magento/Tax/Test/Mftf/Test/AdminCreateTaxRateAllPostCodesTest.xml @@ -19,7 +19,7 @@ <group value="mtf_migrated"/> </annotations> <before> - <actionGroup ref="LoginAsAdmin" stepKey="loginAsAdmin"/> + <actionGroup ref="AdminLoginActionGroup" stepKey="loginAsAdmin"/> </before> <after> <amOnPage url="{{AdminTaxRateGridPage.url}}" stepKey="goToTaxRateIndex"/> diff --git a/app/code/Magento/Tax/Test/Mftf/Test/AdminCreateTaxRateInvalidPostcodeTestLength.xml b/app/code/Magento/Tax/Test/Mftf/Test/AdminCreateTaxRateInvalidPostcodeTestLength.xml deleted file mode 100644 index a98de31b42f81..0000000000000 --- a/app/code/Magento/Tax/Test/Mftf/Test/AdminCreateTaxRateInvalidPostcodeTestLength.xml +++ /dev/null @@ -1,37 +0,0 @@ -<?xml version="1.0" encoding="UTF-8"?> -<!-- - /** - * Copyright © Magento, Inc. All rights reserved. - * See COPYING.txt for license details. - */ ---> - -<tests xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" - xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/testSchema.xsd"> - <test name="AdminCreateTaxRateInvalidPostcodeTestLength"> - <annotations> - <stories value="Create tax rate"/> - <title value="Create tax rate, invalid post code length"/> - <description value="Test log in to Create Tax Rate and Create Tax Rate with invalid post code length"/> - <testCaseId value="MC-18817"/> - <severity value="AVERAGE"/> - <group value="tax"/> - <group value="mtf_migrated"/> - </annotations> - <before> - <actionGroup ref="LoginAsAdmin" stepKey="loginAsAdmin"/> - </before> - - <amOnPage url="{{AdminTaxRateGridPage.url}}" stepKey="goToTaxRateIndex1"/> - <waitForPageLoad stepKey="waitForTaxRateIndex1"/> - <!-- Create a tax rate for large postcodes --> - <click selector="{{AdminTaxRateGridSection.add}}" stepKey="clickAddNewTaxRateButton"/> - <fillField selector="{{AdminTaxRateFormSection.taxIdentifier}}" userInput="{{taxRateWithInvalidPostCodeLength.code}}" stepKey="fillRuleName"/> - <fillField selector="{{AdminTaxRateFormSection.zipCode}}" userInput="{{taxRateWithInvalidPostCodeLength.tax_postcode}}" stepKey="fillPostCode"/> - <selectOption selector="{{AdminTaxRateFormSection.country}}" userInput="{{taxRateWithInvalidPostCodeLength.tax_country}}" stepKey="selectCountry1"/> - <selectOption selector="{{AdminTaxRateFormSection.state}}" userInput="{{taxRateWithInvalidPostCodeLength.tax_region}}" stepKey="selectState" /> - <fillField selector="{{AdminTaxRateFormSection.rate}}" userInput="{{taxRateWithInvalidPostCodeLength.rate}}" stepKey="fillRate"/> - <click selector="{{AdminTaxRateFormSection.save}}" stepKey="clickSave"/> - <see selector="{{AdminTaxRateFormSection.fieldError('tax_postcode')}}" userInput="Please enter less or equal than 10 symbols." stepKey="seeErrorMessage"/> - </test> -</tests> diff --git a/app/code/Magento/Tax/Test/Mftf/Test/AdminCreateTaxRateInvalidPostcodeTestLengthTest.xml b/app/code/Magento/Tax/Test/Mftf/Test/AdminCreateTaxRateInvalidPostcodeTestLengthTest.xml new file mode 100644 index 0000000000000..3a6e4dfef5bac --- /dev/null +++ b/app/code/Magento/Tax/Test/Mftf/Test/AdminCreateTaxRateInvalidPostcodeTestLengthTest.xml @@ -0,0 +1,37 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + /** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +--> + +<tests xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/testSchema.xsd"> + <test name="AdminCreateTaxRateInvalidPostcodeTestLengthTest"> + <annotations> + <stories value="Create tax rate"/> + <title value="Create tax rate, invalid post code length"/> + <description value="Test log in to Create Tax Rate and Create Tax Rate with invalid post code length"/> + <testCaseId value="MC-18817"/> + <severity value="AVERAGE"/> + <group value="tax"/> + <group value="mtf_migrated"/> + </annotations> + <before> + <actionGroup ref="AdminLoginActionGroup" stepKey="loginAsAdmin"/> + </before> + + <amOnPage url="{{AdminTaxRateGridPage.url}}" stepKey="goToTaxRateIndex1"/> + <waitForPageLoad stepKey="waitForTaxRateIndex1"/> + <!-- Create a tax rate for large postcodes --> + <click selector="{{AdminTaxRateGridSection.add}}" stepKey="clickAddNewTaxRateButton"/> + <fillField selector="{{AdminTaxRateFormSection.taxIdentifier}}" userInput="{{taxRateWithInvalidPostCodeLength.code}}" stepKey="fillRuleName"/> + <fillField selector="{{AdminTaxRateFormSection.zipCode}}" userInput="{{taxRateWithInvalidPostCodeLength.tax_postcode}}" stepKey="fillPostCode"/> + <selectOption selector="{{AdminTaxRateFormSection.country}}" userInput="{{taxRateWithInvalidPostCodeLength.tax_country}}" stepKey="selectCountry1"/> + <selectOption selector="{{AdminTaxRateFormSection.state}}" userInput="{{taxRateWithInvalidPostCodeLength.tax_region}}" stepKey="selectState" /> + <fillField selector="{{AdminTaxRateFormSection.rate}}" userInput="{{taxRateWithInvalidPostCodeLength.rate}}" stepKey="fillRate"/> + <click selector="{{AdminTaxRateFormSection.save}}" stepKey="clickSave"/> + <see selector="{{AdminTaxRateFormSection.fieldError('tax_postcode')}}" userInput="Please enter less or equal than 10 symbols." stepKey="seeErrorMessage"/> + </test> +</tests> diff --git a/app/code/Magento/Tax/Test/Mftf/Test/AdminCreateTaxRateLargeRateTest.xml b/app/code/Magento/Tax/Test/Mftf/Test/AdminCreateTaxRateLargeRateTest.xml index cb79de19ce23a..b55ec35fa9eb1 100644 --- a/app/code/Magento/Tax/Test/Mftf/Test/AdminCreateTaxRateLargeRateTest.xml +++ b/app/code/Magento/Tax/Test/Mftf/Test/AdminCreateTaxRateLargeRateTest.xml @@ -19,7 +19,7 @@ <group value="mtf_migrated"/> </annotations> <before> - <actionGroup ref="LoginAsAdmin" stepKey="loginAsAdmin"/> + <actionGroup ref="AdminLoginActionGroup" stepKey="loginAsAdmin"/> </before> <after> <amOnPage url="{{AdminTaxRateGridPage.url}}" stepKey="goToTaxRateIndex"/> diff --git a/app/code/Magento/Tax/Test/Mftf/Test/AdminCreateTaxRateSpecificPostcodeTest.xml b/app/code/Magento/Tax/Test/Mftf/Test/AdminCreateTaxRateSpecificPostcodeTest.xml index 696d6c4c87763..379164d134448 100644 --- a/app/code/Magento/Tax/Test/Mftf/Test/AdminCreateTaxRateSpecificPostcodeTest.xml +++ b/app/code/Magento/Tax/Test/Mftf/Test/AdminCreateTaxRateSpecificPostcodeTest.xml @@ -19,7 +19,7 @@ <group value="mtf_migrated"/> </annotations> <before> - <actionGroup ref="LoginAsAdmin" stepKey="loginAsAdmin"/> + <actionGroup ref="AdminLoginActionGroup" stepKey="loginAsAdmin"/> </before> <after> <amOnPage url="{{AdminTaxRateGridPage.url}}" stepKey="goToTaxRateIndex"/> diff --git a/app/code/Magento/Tax/Test/Mftf/Test/AdminCreateTaxRateWiderZipCodeRangeTest.xml b/app/code/Magento/Tax/Test/Mftf/Test/AdminCreateTaxRateWiderZipCodeRangeTest.xml index c6c5e318cc791..d276a6a276b2c 100644 --- a/app/code/Magento/Tax/Test/Mftf/Test/AdminCreateTaxRateWiderZipCodeRangeTest.xml +++ b/app/code/Magento/Tax/Test/Mftf/Test/AdminCreateTaxRateWiderZipCodeRangeTest.xml @@ -19,7 +19,7 @@ <group value="mtf_migrated"/> </annotations> <before> - <actionGroup ref="LoginAsAdmin" stepKey="loginAsAdmin"/> + <actionGroup ref="AdminLoginActionGroup" stepKey="loginAsAdmin"/> </before> <after> <amOnPage url="{{AdminTaxRateGridPage.url}}" stepKey="goToTaxRateIndex"/> diff --git a/app/code/Magento/Tax/Test/Mftf/Test/AdminCreateTaxRateZipCodeRangeTest.xml b/app/code/Magento/Tax/Test/Mftf/Test/AdminCreateTaxRateZipCodeRangeTest.xml index f75fa716e9d30..998c948d869d0 100644 --- a/app/code/Magento/Tax/Test/Mftf/Test/AdminCreateTaxRateZipCodeRangeTest.xml +++ b/app/code/Magento/Tax/Test/Mftf/Test/AdminCreateTaxRateZipCodeRangeTest.xml @@ -19,7 +19,7 @@ <group value="mtf_migrated"/> </annotations> <before> - <actionGroup ref="LoginAsAdmin" stepKey="loginAsAdmin"/> + <actionGroup ref="AdminLoginActionGroup" stepKey="loginAsAdmin"/> </before> <after> <amOnPage url="{{AdminTaxRateGridPage.url}}" stepKey="goToTaxRateIndex"/> diff --git a/app/code/Magento/Tax/Test/Mftf/Test/AdminCreateTaxRuleWithCustomerAndProductTaxClassTest.xml b/app/code/Magento/Tax/Test/Mftf/Test/AdminCreateTaxRuleWithCustomerAndProductTaxClassTest.xml index 03b12c8f28098..6e2ca794379f6 100644 --- a/app/code/Magento/Tax/Test/Mftf/Test/AdminCreateTaxRuleWithCustomerAndProductTaxClassTest.xml +++ b/app/code/Magento/Tax/Test/Mftf/Test/AdminCreateTaxRuleWithCustomerAndProductTaxClassTest.xml @@ -28,7 +28,7 @@ <getData entity="productTaxClass" stepKey="productTaxClass"> <requiredEntity createDataKey="createProductTaxClass"/> </getData> - <actionGroup ref="LoginAsAdmin" stepKey="LoginAsAdmin"/> + <actionGroup ref="AdminLoginActionGroup" stepKey="LoginAsAdmin"/> </before> <after> <actionGroup ref="AdminDeleteTaxRule" stepKey="deleteTaxRule"> diff --git a/app/code/Magento/Tax/Test/Mftf/Test/AdminCreateTaxRuleWithNewAndExistingTaxRateAndCustomerAndProductTaxClassTest.xml b/app/code/Magento/Tax/Test/Mftf/Test/AdminCreateTaxRuleWithNewAndExistingTaxRateAndCustomerAndProductTaxClassTest.xml index abff31803a165..895bb920973c8 100644 --- a/app/code/Magento/Tax/Test/Mftf/Test/AdminCreateTaxRuleWithNewAndExistingTaxRateAndCustomerAndProductTaxClassTest.xml +++ b/app/code/Magento/Tax/Test/Mftf/Test/AdminCreateTaxRuleWithNewAndExistingTaxRateAndCustomerAndProductTaxClassTest.xml @@ -29,7 +29,7 @@ <getData entity="productTaxClass" stepKey="productTaxClass"> <requiredEntity createDataKey="createProductTaxClass"/> </getData> - <actionGroup ref="LoginAsAdmin" stepKey="LoginAsAdmin"/> + <actionGroup ref="AdminLoginActionGroup" stepKey="LoginAsAdmin"/> </before> <after> <actionGroup ref="AdminDeleteTaxRule" stepKey="deleteTaxRule"> diff --git a/app/code/Magento/Tax/Test/Mftf/Test/AdminCreateTaxRuleWithNewTaxClassesAndTaxRateTest.xml b/app/code/Magento/Tax/Test/Mftf/Test/AdminCreateTaxRuleWithNewTaxClassesAndTaxRateTest.xml index 6e9b81743ddf0..43ce4059ad84e 100644 --- a/app/code/Magento/Tax/Test/Mftf/Test/AdminCreateTaxRuleWithNewTaxClassesAndTaxRateTest.xml +++ b/app/code/Magento/Tax/Test/Mftf/Test/AdminCreateTaxRuleWithNewTaxClassesAndTaxRateTest.xml @@ -29,7 +29,7 @@ <getData entity="productTaxClass" stepKey="productTaxClass"> <requiredEntity createDataKey="createProductTaxClass"/> </getData> - <actionGroup ref="LoginAsAdmin" stepKey="LoginAsAdmin"/> + <actionGroup ref="AdminLoginActionGroup" stepKey="LoginAsAdmin"/> </before> <after> <actionGroup ref="AdminDeleteTaxRule" stepKey="deleteTaxRule"> diff --git a/app/code/Magento/Tax/Test/Mftf/Test/AdminCreateTaxRuleWithZipRangeTest.xml b/app/code/Magento/Tax/Test/Mftf/Test/AdminCreateTaxRuleWithZipRangeTest.xml index 6a9bf30811ff5..0293e04293daf 100644 --- a/app/code/Magento/Tax/Test/Mftf/Test/AdminCreateTaxRuleWithZipRangeTest.xml +++ b/app/code/Magento/Tax/Test/Mftf/Test/AdminCreateTaxRuleWithZipRangeTest.xml @@ -29,7 +29,7 @@ <getData entity="productTaxClass" stepKey="productTaxClass"> <requiredEntity createDataKey="createProductTaxClass"/> </getData> - <actionGroup ref="LoginAsAdmin" stepKey="LoginAsAdmin"/> + <actionGroup ref="AdminLoginActionGroup" stepKey="LoginAsAdmin"/> </before> <after> <actionGroup ref="AdminDeleteTaxRule" stepKey="deleteTaxRule"> diff --git a/app/code/Magento/Tax/Test/Mftf/Test/AdminDeleteTaxRuleTest.xml b/app/code/Magento/Tax/Test/Mftf/Test/AdminDeleteTaxRuleTest.xml index 72adf7b0dae1e..770cdd1e3b2c4 100644 --- a/app/code/Magento/Tax/Test/Mftf/Test/AdminDeleteTaxRuleTest.xml +++ b/app/code/Magento/Tax/Test/Mftf/Test/AdminDeleteTaxRuleTest.xml @@ -23,7 +23,7 @@ <createData entity="defaultTaxRule" stepKey="initialTaxRule"/> <createData entity="ApiSimplePrice100Qty100v2" stepKey="simpleProduct"/> <createData entity="Simple_US_Utah_Customer" stepKey="customer" /> - <actionGroup ref="LoginAsAdmin" stepKey="loginAsAdmin"/> + <actionGroup ref="AdminLoginActionGroup" stepKey="loginAsAdmin"/> </before> <after> <deleteData stepKey="deleteSimpleProduct" createDataKey="simpleProduct" /> diff --git a/app/code/Magento/Tax/Test/Mftf/Test/AdminStoresTaxRulesNavigateMenuTest.xml b/app/code/Magento/Tax/Test/Mftf/Test/AdminStoresTaxRulesNavigateMenuTest.xml index 1277d6e5f9fd2..fd5b07a88b52f 100644 --- a/app/code/Magento/Tax/Test/Mftf/Test/AdminStoresTaxRulesNavigateMenuTest.xml +++ b/app/code/Magento/Tax/Test/Mftf/Test/AdminStoresTaxRulesNavigateMenuTest.xml @@ -20,10 +20,10 @@ <group value="mtf_migrated"/> </annotations> <before> - <actionGroup ref="LoginAsAdmin" stepKey="LoginAsAdmin"/> + <actionGroup ref="AdminLoginActionGroup" stepKey="LoginAsAdmin"/> </before> <after> - <actionGroup ref="logout" stepKey="logout"/> + <actionGroup ref="AdminLogoutActionGroup" stepKey="logout"/> </after> <actionGroup ref="AdminNavigateMenuActionGroup" stepKey="navigateToStoresTaxRulesPage"> <argument name="menuUiId" value="{{AdminMenuStores.dataUiId}}"/> diff --git a/app/code/Magento/Tax/Test/Mftf/Test/AdminStoresTaxZonesAndRatesNavigateMenuTest.xml b/app/code/Magento/Tax/Test/Mftf/Test/AdminStoresTaxZonesAndRatesNavigateMenuTest.xml index e0a4d5d9a4016..824d6d1630162 100644 --- a/app/code/Magento/Tax/Test/Mftf/Test/AdminStoresTaxZonesAndRatesNavigateMenuTest.xml +++ b/app/code/Magento/Tax/Test/Mftf/Test/AdminStoresTaxZonesAndRatesNavigateMenuTest.xml @@ -20,10 +20,10 @@ <group value="mtf_migrated"/> </annotations> <before> - <actionGroup ref="LoginAsAdmin" stepKey="LoginAsAdmin"/> + <actionGroup ref="AdminLoginActionGroup" stepKey="LoginAsAdmin"/> </before> <after> - <actionGroup ref="logout" stepKey="logout"/> + <actionGroup ref="AdminLogoutActionGroup" stepKey="logout"/> </after> <actionGroup ref="AdminNavigateMenuActionGroup" stepKey="navigateToStoresTaxZonesAndRatesPage"> <argument name="menuUiId" value="{{AdminMenuStores.dataUiId}}"/> diff --git a/app/code/Magento/Tax/Test/Mftf/Test/AdminSystemImportExportTaxRatesNavigateMenuTest.xml b/app/code/Magento/Tax/Test/Mftf/Test/AdminSystemImportExportTaxRatesNavigateMenuTest.xml index a84ae61d66305..a6939bf14c4a1 100644 --- a/app/code/Magento/Tax/Test/Mftf/Test/AdminSystemImportExportTaxRatesNavigateMenuTest.xml +++ b/app/code/Magento/Tax/Test/Mftf/Test/AdminSystemImportExportTaxRatesNavigateMenuTest.xml @@ -20,10 +20,10 @@ <group value="mtf_migrated"/> </annotations> <before> - <actionGroup ref="LoginAsAdmin" stepKey="LoginAsAdmin"/> + <actionGroup ref="AdminLoginActionGroup" stepKey="LoginAsAdmin"/> </before> <after> - <actionGroup ref="logout" stepKey="logout"/> + <actionGroup ref="AdminLogoutActionGroup" stepKey="logout"/> </after> <actionGroup ref="AdminNavigateMenuActionGroup" stepKey="navigateToSystemImportExportTaxRatesPage"> <argument name="menuUiId" value="{{AdminMenuSystem.dataUiId}}"/> diff --git a/app/code/Magento/Tax/Test/Mftf/Test/AdminTaxCalcWithApplyTaxOnSettingTest.xml b/app/code/Magento/Tax/Test/Mftf/Test/AdminTaxCalcWithApplyTaxOnSettingTest.xml index fbf7873d37438..8cd85ee0ca969 100644 --- a/app/code/Magento/Tax/Test/Mftf/Test/AdminTaxCalcWithApplyTaxOnSettingTest.xml +++ b/app/code/Magento/Tax/Test/Mftf/Test/AdminTaxCalcWithApplyTaxOnSettingTest.xml @@ -27,7 +27,7 @@ <createData entity="ApiSimpleProductWithCustomPrice" stepKey="createProduct"> <requiredEntity createDataKey="createCategory"/> </createData> - <actionGroup ref="LoginActionGroup" stepKey="loginAsAdmin"/> + <actionGroup ref="AdminLoginActionGroup" stepKey="loginAsAdmin"/> <actionGroup ref="SetTaxClassForShippingActionGroup" stepKey="setTaxClass"/> <actionGroup ref="SetTaxApplyOnSettingActionGroup" stepKey="setApplyTaxOnSetting"> <argument name="userInput" value="Original price only"/> @@ -49,7 +49,7 @@ <argument name="userInput" value="Custom price if available"/> </actionGroup> <actionGroup ref="ResetTaxClassForShippingActionGroup" stepKey="resetTaxClassForShipping"/> - <actionGroup ref="logout" stepKey="logout"/> + <actionGroup ref="AdminLogoutActionGroup" stepKey="logout"/> </after> <actionGroup ref="NavigateToNewOrderPageNewCustomerSingleStoreActionGroup" stepKey="gotoNewOrderCreationPage"/> @@ -69,12 +69,18 @@ <click selector="{{AdminOrderFormPaymentSection.getShippingMethods}}" stepKey="getShippingMethods"/> <waitForPageLoad stepKey="waitForApplyingShippingMethods"/> <grabTextFrom selector="{{AdminOrderFormTotalSection.subtotalRow('3')}}" stepKey="grabTaxCost"/> - <assertEquals expected='$6.00' expectedType="string" actual="($grabTaxCost)" stepKey="assertTax"/> + <assertEquals stepKey="assertTax"> + <actualResult type="const">($grabTaxCost)</actualResult> + <expectedResult type="string">$6.00</expectedResult> + </assertEquals> <scrollTo selector="{{AdminOrderFormItemsSection.addProducts}}" stepKey="scrollToSubmitButton"/> <waitForElementVisible selector="{{AdminOrderFormItemsSection.addProducts}}" stepKey="waitElementToBeVisble"/> <click selector="{{AdminOrderFormItemsSection.customPriceCheckbox}}" stepKey="clickOnCustomPriceCheckbox"/> <fillField selector="{{AdminOrderFormItemsSection.customPriceField}}" userInput="{{SimpleProductNameWithDoubleQuote.price}}" stepKey="changePrice"/> <click selector="{{AdminOrderFormItemsSection.updateItemsAndQuantities}}" stepKey="updateItemsAndQunatities"/> - <assertEquals expected='$6.00' expectedType="string" actual="($grabTaxCost)" stepKey="assertTaxAfterCustomPrice"/> + <assertEquals stepKey="assertTaxAfterCustomPrice"> + <actualResult type="const">($grabTaxCost)</actualResult> + <expectedResult type="string">$6.00</expectedResult> + </assertEquals> </test> </tests> diff --git a/app/code/Magento/Tax/Test/Mftf/Test/AdminTaxReportGridTest.xml b/app/code/Magento/Tax/Test/Mftf/Test/AdminTaxReportGridTest.xml deleted file mode 100644 index e90602441261d..0000000000000 --- a/app/code/Magento/Tax/Test/Mftf/Test/AdminTaxReportGridTest.xml +++ /dev/null @@ -1,225 +0,0 @@ -<?xml version="1.0" encoding="UTF-8"?> -<!-- - /** - * Copyright © Magento, Inc. All rights reserved. - * See COPYING.txt for license details. - */ ---> - -<tests xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" - xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/testSchema.xsd"> - <test name="AdminTaxReportGridTest"> - <annotations> - <features value="Tax"/> - <stories value="MAGETWO-91521: Reports / Sales / Tax report show incorrect amount"/> - <title value="Checking Tax Report grid"/> - <description value="Tax Report Grid displays Tax amount in rows 'Total' and 'Subtotal' is a sum of all tax amounts"/> - <severity value="MAJOR"/> - <testCaseId value="MAGETWO-94338"/> - <group value="Tax"/> - <skip> - <issueId value="MAGETWO-96193"/> - </skip> - </annotations> - <before> - <actionGroup ref="LoginAsAdmin" stepKey="loginAsAdmin"/> - - <!-- Go to tax rule page --> - <actionGroup ref="AddNewTaxRuleActionGroup" stepKey="addFirstTaxRuleActionGroup"/> - <fillField stepKey="fillRuleName" selector="{{AdminTaxRulesSection.ruleName}}" userInput="TaxRule1"/> - - <!-- Add NY and CA tax rules --> - <actionGroup ref="AddNewTaxRateNoZipActionGroup" stepKey="addNYTaxRate"> - <argument name="taxCode" value="SimpleTaxWithZipCode"/> - </actionGroup> - - <actionGroup ref="addProductTaxClass" stepKey="addProductTaxClass"> - <argument name="prodTaxClassName" value="TaxClasses1"/> - </actionGroup> - - <click stepKey="disableDefaultProdTaxClass" selector="{{AdminTaxRulesSection.defaultTaxClass}}"/> - <waitForPageLoad stepKey="waitForTaxRulePage"/> - <click stepKey="clickSave" selector="{{AdminTaxRulesSection.saveRule}}"/> - <waitForPageLoad stepKey="waitForNewTaxRuleCreated"/> - - <!-- Go to tax rule page to create second Tax Rule--> - <actionGroup ref="AddNewTaxRuleActionGroup" stepKey="addSecondTaxRuleActionGroup"/> - <fillField stepKey="fillSecondRuleName" selector="{{AdminTaxRulesSection.ruleName}}" userInput="TaxRule2"/> - - <actionGroup ref="AddNewTaxRateNoZipActionGroup" stepKey="addCATaxRate"> - <argument name="taxCode" value="SimpleSecondTaxWithZipCode"/> - </actionGroup> - - <actionGroup ref="addProductTaxClass" stepKey="addSecondProductTaxClass"> - <argument name="prodTaxClassName" value="TaxClasses2"/> - </actionGroup> - - <click stepKey="disableSecondProdTaxClass" selector="{{AdminTaxRulesSection.defaultTaxClass}}"/> - <waitForPageLoad stepKey="waitForTaxRulePage2"/> - <click stepKey="clickSaveBtn" selector="{{AdminTaxRulesSection.saveRule}}"/> - <waitForPageLoad stepKey="waitForSecondTaxRuleCreated"/> - - <createData entity="_defaultCategory" stepKey="createCategory"/> - <createData entity="_defaultProduct" stepKey="firstProduct"> - <requiredEntity createDataKey="createCategory"/> - </createData> - - <createData entity="_defaultProduct" stepKey="secondProduct"> - <requiredEntity createDataKey="createCategory"/> - </createData> - </before> - - <!--Open Created products. In Tax Class select new created Product Tax classes.--> - <amOnPage url="{{AdminProductIndexPage.url}}" stepKey="goToProductIndex"/> - <waitForPageLoad stepKey="wait1"/> - <actionGroup ref="ResetProductGridToDefaultViewActionGroup" stepKey="resetProductGrid"/> - <actionGroup ref="FilterProductGridBySkuActionGroup" stepKey="filterProductGridBySku"> - <argument name="product" value="$$firstProduct$$"/> - </actionGroup> - <actionGroup ref="OpenProductForEditByClickingRowXColumnYInProductGridActionGroup" stepKey="openFirstProductForEdit"/> - <selectOption selector="{{AdminProductFormSection.productTaxClass}}" stepKey="selectTexClassForFirstProduct" userInput="TaxClasses1"/> - <!-- Save the second product --> - <click selector="{{AdminProductFormActionSection.saveButton}}" stepKey="saveFirstProduct"/> - <waitForPageLoad stepKey="waitForFirstProductSaved"/> - - <amOnPage url="{{AdminProductIndexPage.url}}" stepKey="againGoToProductIndex"/> - <waitForPageLoad stepKey="wait2"/> - <actionGroup ref="ResetProductGridToDefaultViewActionGroup" stepKey="resetSecondProductGrid"/> - <actionGroup ref="FilterProductGridBySkuActionGroup" stepKey="filterSecondProductGridBySku"> - <argument name="product" value="$$secondProduct$$"/> - </actionGroup> - <actionGroup ref="OpenProductForEditByClickingRowXColumnYInProductGridActionGroup" stepKey="openSecondProductForEdit"/> - <selectOption selector="{{AdminProductFormSection.productTaxClass}}" stepKey="selectTexClassForSecondProduct" userInput="TaxClasses2"/> - <!-- Save the second product --> - <click selector="{{AdminProductFormActionSection.saveButton}}" stepKey="saveSecondProduct"/> - <waitForPageLoad stepKey="waitForSecondProductSaved"/> - - <!--Create an order with these 2 products in that zip code.--> - <amOnPage url="{{AdminOrdersPage.url}}" stepKey="navigateToOrderIndexPage"/> - <waitForPageLoad stepKey="waitForIndexPageLoad"/> - <see selector="{{AdminHeaderSection.pageTitle}}" userInput="Orders" stepKey="seeIndexPageTitle"/> - <click selector="{{AdminOrdersGridSection.createNewOrder}}" stepKey="clickCreateNewOrder"/> - <click selector="{{AdminOrderFormActionSection.CreateNewCustomer}}" stepKey="clickCreateCustomer"/> - <see selector="{{AdminHeaderSection.pageTitle}}" userInput="Create New Order" stepKey="seeNewOrderPageTitle"/> - <waitForPageLoad stepKey="waitForPage" time="60"/> - <!--Check if order can be submitted without the required fields including email address--> - <scrollToTopOfPage stepKey="scrollToTopOfOrderFormPage" after="seeNewOrderPageTitle"/> - <actionGroup ref="AddSimpleProductToOrderActionGroup" stepKey="addFirstProductToOrder" after="scrollToTopOfOrderFormPage"> - <argument name="product" value="$$firstProduct$$"/> - </actionGroup> - <actionGroup ref="AddSimpleProductToOrderActionGroup" stepKey="addSecondProductToOrder" after="addFirstProductToOrder"> - <argument name="product" value="$$secondProduct$$"/> - </actionGroup> - - <!--Fill customer group and customer email--> - <selectOption selector="{{AdminOrderFormAccountSection.group}}" userInput="{{GeneralCustomerGroup.code}}" stepKey="selectCustomerGroup" after="addSecondProductToOrder"/> - <fillField selector="{{AdminOrderFormAccountSection.email}}" userInput="{{Simple_US_Customer.email}}" stepKey="fillCustomerEmail" after="selectCustomerGroup"/> - - <!--Fill customer address information--> - <actionGroup ref="FillOrderCustomerInformationActionGroup" stepKey="fillCustomerAddress" after="fillCustomerEmail"> - <argument name="customer" value="Simple_US_Customer"/> - <argument name="address" value="US_Address_TX"/> - </actionGroup> - - <!-- Select shipping --> - <actionGroup ref="OrderSelectFlatRateShippingActionGroup" stepKey="selectFlatRateShipping" after="fillCustomerAddress"/> - <!-- Checkout select Check/Money Order payment --> - <actionGroup ref="SelectCheckMoneyPaymentMethodActionGroup" after="selectFlatRateShipping" stepKey="selectCheckMoneyPayment"/> - <!--Submit Order and verify information--> - <click selector="{{AdminOrderFormActionSection.SubmitOrder}}" after="selectCheckMoneyPayment" stepKey="clickSubmitOrder"/> - <seeInCurrentUrl url="{{AdminOrderDetailsPage.url}}" stepKey="seeViewOrderPage" after="clickSubmitOrder"/> - <see selector="{{AdminOrderDetailsMessagesSection.successMessage}}" userInput="You created the order." stepKey="seeSuccessMessage" after="seeViewOrderPage"/> - - <!--Create Invoice and Shipment for this Order.--> - <click selector="{{AdminOrderDetailsMainActionsSection.invoice}}" stepKey="clickInvoiceButton"/> - <see selector="{{AdminHeaderSection.pageTitle}}" userInput="New Invoice" stepKey="seeNewInvoiceInPageTitle" after="clickInvoiceButton"/> - <waitForPageLoad stepKey="waitForInvoicePageOpened"/> - - <click selector="{{AdminInvoiceMainActionsSection.submitInvoice}}" stepKey="clickSubmitInvoice"/> - - <click selector="{{AdminOrderDetailsMainActionsSection.ship}}" stepKey="clickShipAction"/> - <seeInCurrentUrl url="{{AdminShipmentNewPage.url}}" stepKey="seeOrderShipmentUrl" after="clickShipAction"/> - <!--Submit Shipment--> - <click selector="{{AdminShipmentMainActionsSection.submitShipment}}" stepKey="clickSubmitShipment" after="seeOrderShipmentUrl"/> - <waitForPageLoad stepKey="waitForShipmentSaved"/> - - <!--Go to "Reports" -> "Sales" -> "Tax"--> - <amOnPage url="/admin/reports/report_sales/tax/" stepKey="navigateToReportsTaxPage"/> - <waitForPageLoad stepKey="waitForReportsTaxPageLoad"/> - - <!--click "here" to refresh last day's statistics --> - <click stepKey="clickRefrashStatisticsHere" selector="{{AdminTaxReportsSection.refreshStatistics}}"/> - <waitForPageLoad stepKey="waitForRefresh"/> - - <!--Select Dates--> - <fillField selector="{{AdminTaxReportsSection.fromDate}}" userInput="05/16/2018" stepKey="fillDateFrom"/> - <click selector="{{AdminTaxReportsSection.toDate}}" stepKey="clickDateTo"/> - <click selector="{{AdminTaxReportsSection.goTodayButton}}" stepKey="clickGoTodayDate"/> - <!--Click "Show report" in the upper right corner.--> - <click selector="{{AdminTaxReportsSection.showReportButton}}" stepKey="clickShowReportButton"/> - <waitForPageLoad time="60" stepKey="waitForReload"/> - <!--Tax Report Grid displays Tax amount in rows. "Total" and "Subtotal" is a sum of all tax amounts--> - <grabTextFrom selector="{{AdminTaxReportsSection.taxRuleAmount('Texas-0.125')}}" stepKey="amountOfFirstTaxRate"/> - <grabTextFrom selector="{{AdminTaxReportsSection.taxRuleAmount('Texas-7.25')}}" stepKey="amountOfSecondTaxRate"/> - <grabTextFrom selector="{{AdminTaxReportsSection.taxRuleAmount('Subtotal')}}" stepKey="amountOfSubtotalTaxRate"/> - <assertEquals stepKey="assertSubtotalFirstField"> - <expectedResult type="string">$0.15</expectedResult> - <actualResult type="variable">amountOfFirstTaxRate</actualResult> - </assertEquals> - - <assertEquals stepKey="assertSubtotalSecondField"> - <expectedResult type="string">$8.92</expectedResult> - <actualResult type="variable">amountOfSecondTaxRate</actualResult> - </assertEquals> - - <assertEquals stepKey="assertSubtotalField"> - <expectedResult type="string">$9.07</expectedResult> - <actualResult type="variable">amountOfSubtotalTaxRate</actualResult> - </assertEquals> - - <after> - <!-- Go to the tax rule page and delete the row we created--> - <amOnPage url="{{AdminTaxRuleGridPage.url}}" stepKey="goToTaxRulesPage"/> - <waitForPageLoad stepKey="waitForRulesPage"/> - - <actionGroup ref="deleteEntitySecondaryGrid" stepKey="deleteRule"> - <argument name="name" value="TaxRule1"/> - <argument name="searchInput" value="{{AdminSecondaryGridSection.taxIdentifierSearch}}"/> - </actionGroup> - - <actionGroup ref="deleteEntitySecondaryGrid" stepKey="deleteSecondRule"> - <argument name="name" value="TaxRule2"/> - <argument name="searchInput" value="{{AdminSecondaryGridSection.taxIdentifierSearch}}"/> - </actionGroup> - - <!-- Go to the tax rate page --> - <amOnPage url="{{AdminTaxRateGridPage.url}}" stepKey="goToTaxRatesPage"/> - <waitForPageLoad stepKey="waitForRatesPage"/> - - <!-- Delete the two tax rates that were created --> - <actionGroup ref="deleteEntitySecondaryGrid" stepKey="deleteNYRate"> - <argument name="name" value="{{SimpleTaxWithZipCode.state}}-{{SimpleTaxWithZipCode.rate}}"/> - <argument name="searchInput" value="{{AdminSecondaryGridSection.taxIdentifierSearch}}"/> - </actionGroup> - - <actionGroup ref="deleteEntitySecondaryGrid" stepKey="deleteCARate"> - <argument name="name" value="{{SimpleSecondTaxWithZipCode.state}}-{{SimpleSecondTaxWithZipCode.rate}}"/> - <argument name="searchInput" value="{{AdminSecondaryGridSection.taxIdentifierSearch}}"/> - </actionGroup> - - <deleteData createDataKey="firstProduct" stepKey="deleteFirstProduct"/> - <deleteData createDataKey="secondProduct" stepKey="deleteSecondProduct"/> - <deleteData createDataKey="createCategory" stepKey="deleteCategory"/> - - <actionGroup ref="DeleteProductTaxClassActionGroup" stepKey="deleteFirstProductTaxClass"> - <argument name="taxClassName" value="TaxClasses1"/> - </actionGroup> - - <actionGroup ref="DeleteProductTaxClassActionGroup" stepKey="deleteSecondProductTaxClass"> - <argument name="taxClassName" value="TaxClasses2"/> - </actionGroup> - - <actionGroup ref="logout" stepKey="logout"/> - </after> - </test> -</tests> diff --git a/app/code/Magento/Tax/Test/Mftf/Test/AdminUpdateDefaultTaxRuleTest.xml b/app/code/Magento/Tax/Test/Mftf/Test/AdminUpdateDefaultTaxRuleTest.xml index 0390542544060..61b09eabe7d35 100644 --- a/app/code/Magento/Tax/Test/Mftf/Test/AdminUpdateDefaultTaxRuleTest.xml +++ b/app/code/Magento/Tax/Test/Mftf/Test/AdminUpdateDefaultTaxRuleTest.xml @@ -29,7 +29,7 @@ <getData entity="productTaxClass" stepKey="productTaxClass"> <requiredEntity createDataKey="createProductTaxClass"/> </getData> - <actionGroup ref="LoginAsAdmin" stepKey="loginAsAdmin"/> + <actionGroup ref="AdminLoginActionGroup" stepKey="loginAsAdmin"/> </before> <after> <deleteData stepKey="deleteTaxRule" createDataKey="initialTaxRule" /> diff --git a/app/code/Magento/Tax/Test/Mftf/Test/AdminUpdateTaxRuleWithCustomClassesTest.xml b/app/code/Magento/Tax/Test/Mftf/Test/AdminUpdateTaxRuleWithCustomClassesTest.xml index 0e87f268f8825..b7ffe05ebf5c2 100644 --- a/app/code/Magento/Tax/Test/Mftf/Test/AdminUpdateTaxRuleWithCustomClassesTest.xml +++ b/app/code/Magento/Tax/Test/Mftf/Test/AdminUpdateTaxRuleWithCustomClassesTest.xml @@ -30,7 +30,7 @@ <getData entity="productTaxClass" stepKey="productTaxClass"> <requiredEntity createDataKey="createProductTaxClass"/> </getData> - <actionGroup ref="LoginAsAdmin" stepKey="loginAsAdmin"/> + <actionGroup ref="AdminLoginActionGroup" stepKey="loginAsAdmin"/> </before> <after> <deleteData stepKey="deleteTaxRule" createDataKey="initialTaxRule" /> diff --git a/app/code/Magento/Tax/Test/Mftf/Test/AdminUpdateTaxRuleWithFixedZipUtahTest.xml b/app/code/Magento/Tax/Test/Mftf/Test/AdminUpdateTaxRuleWithFixedZipUtahTest.xml index a96a57cbfec55..14df3f8987f5e 100644 --- a/app/code/Magento/Tax/Test/Mftf/Test/AdminUpdateTaxRuleWithFixedZipUtahTest.xml +++ b/app/code/Magento/Tax/Test/Mftf/Test/AdminUpdateTaxRuleWithFixedZipUtahTest.xml @@ -32,7 +32,7 @@ </getData> <createData entity="ApiSimplePrice100Qty100v2" stepKey="simpleProduct"/> <createData entity="Simple_US_Utah_Customer" stepKey="customer" /> - <actionGroup ref="LoginAsAdmin" stepKey="loginAsAdmin"/> + <actionGroup ref="AdminLoginActionGroup" stepKey="loginAsAdmin"/> </before> <after> <deleteData stepKey="deleteTaxRule" createDataKey="initialTaxRule" /> diff --git a/app/code/Magento/Tax/Test/Mftf/Test/CheckCreditMemoTotalsTest.xml b/app/code/Magento/Tax/Test/Mftf/Test/CheckCreditMemoTotalsTest.xml deleted file mode 100644 index e7964a2dd29eb..0000000000000 --- a/app/code/Magento/Tax/Test/Mftf/Test/CheckCreditMemoTotalsTest.xml +++ /dev/null @@ -1,148 +0,0 @@ -<?xml version="1.0" encoding="UTF-8"?> -<!-- - /** - * Copyright © Magento, Inc. All rights reserved. - * See COPYING.txt for license details. - */ ---> - -<tests xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" - xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/testSchema.xsd"> - <test name="CheckCreditMemoTotalsTest"> - <annotations> - <features value="Tax"/> - <stories value="MAGETWO-91769 - Credit Memo - Wrong tax calculation! #10982"/> - <title value="Checking Credit memo Totals"/> - <description value="Checking Credit memo Totals"/> - <severity value="MAJOR"/> - <testCaseId value="MAGETWO-95175"/> - <group value="creditMemo"/> - <group value="tax"/> - <skip> - <issueId value="MC-17140"/> - </skip> - </annotations> - <before> - <!--Create category and product--> - <createData entity="_defaultCategory" stepKey="createCategory"/> - <createData entity="_defaultProduct" stepKey="createSimpleProduct"> - <requiredEntity createDataKey="createCategory"/> - </createData> - <!--Create customer--> - <createData entity="Simple_US_Customer_NY" stepKey="createCustomer"/> - <!--Login as admin--> - <actionGroup ref="LoginAsAdmin" stepKey="LoginAsAdmin"/> - <!--Create tax rule and tax rate--> - <amOnPage url="{{AdminTaxRuleGridPage.url}}" stepKey="goToTaxRulePage"/> - <waitForPageLoad stepKey="waitForTaxRatePage"/> - <click stepKey="addNewTaxRate" selector="{{AdminGridMainControls.add}}"/> - <fillField stepKey="fillRuleName" selector="{{AdminTaxRulesSection.ruleName}}" userInput="SampleRule"/> - <actionGroup ref="addCustomTaxRate" stepKey="addCustomTaxRate"> - <argument name="taxCode" value="SimpleTaxNY"/> - </actionGroup> - <click stepKey="expandAdditionalSettings" selector="{{AdminProductTaxClassSection.additionalSettings}}"/> - <waitForPageLoad stepKey="waitForPageLoad"/> - <click selector="{{AdminProductTaxClassSection.productTaxClass}}" stepKey="ClickToAddTaxClass"/> - <fillField selector="{{AdminProductTaxClassSection.TaxClassName}}" userInput="NewTaxClass" stepKey="setName"/> - <click selector="{{AdminProductTaxClassSection.confirm}}" stepKey="ClickToDone"/> - <click stepKey="clickSave" selector="{{AdminStoresMainActionsSection.saveButton}}"/> - <waitForPageLoad stepKey="waitForTaxRatePage1"/> - <see userInput="You saved the tax rule." stepKey="VerifyRuleSaved"/> - <!--Search and edit product to add new created tax rule--> - <actionGroup ref="SearchForProductOnBackendActionGroup" stepKey="searchForSimpleProduct"> - <argument name="product" value="$$createSimpleProduct$$"/> - </actionGroup> - <actionGroup ref="OpenEditProductOnBackendActionGroup" stepKey="OpenEditProductOnBackend"> - <argument name="product" value="$$createSimpleProduct$$"/> - </actionGroup> - <waitForPageLoad stepKey="waitForPageLoad2"/> - <selectOption selector="{{AdminProductFormActionSection.selectTaxClass}}" userInput="NewTaxClass" stepKey="SetNewTaxClass" /> - <click selector="{{AdminProductFormActionSection.saveButton}}" stepKey="saveProduct"/> - <!--Set configs--> - <amOnPage url="{{AdminTaxConfigurationPage.url}}" stepKey="goToTaxConfigPage"/> - <conditionalClick stepKey="clickOrdersInvoicesCreditSales" selector="{{AdminConfigureTaxSection.taxClasses}}" dependentSelector="{{AdminConfigureTaxSection.taxClassesCondition}}" visible="false"/> - <click selector="{{AdminConfigureTaxSection.useSystemValue}}" stepKey="UncheckUseSystemValue"/> - <selectOption selector="{{AdminConfigureTaxSection.productTaxClass}}" userInput="NewTaxClass" stepKey="selectClass"/> - <click selector="{{AdminConfigureTaxSection.save}}" stepKey="saveConfig"/> - <!--flash cache--> - <magentoCLI command="cache:flush" stepKey="flushCache"/> - </before> - <after> - <!--Delete category and product--> - <deleteData createDataKey="createCategory" stepKey="deleteCategory"/> - <deleteData createDataKey="createSimpleProduct" stepKey="deleteSimpleProduct"/> - <!--Delete customer--> - <deleteData createDataKey="createCustomer" stepKey="deleteCustomer"/> - <!-- Reset admin order filter --> - <actionGroup ref="AdminOrdersGridClearFiltersActionGroup" stepKey="clearOrderFilters"/> - <!--Roll Back configuration--> - <amOnPage url="{{AdminTaxConfigurationPage.url}}" stepKey="goToTaxConfigPage"/> - <conditionalClick stepKey="clickOrdersInvoicesCreditSales" selector="{{AdminConfigureTaxSection.taxClasses}}" dependentSelector="{{AdminConfigureTaxSection.taxClassesCondition}}" visible="false"/> - <selectOption selector="{{AdminConfigureTaxSection.productTaxClass}}" userInput="Taxable Goods" stepKey="selectClass"/> - <click selector="{{AdminConfigureTaxSection.save}}" stepKey="saveConfig"/> - <!-- Go to the tax rule page and delete the row we created--> - <amOnPage url="{{AdminTaxRuleGridPage.url}}" stepKey="goToTaxRulesPage"/> - <waitForPageLoad stepKey="waitForRulesPage"/> - <actionGroup ref="deleteEntitySecondaryGrid" stepKey="deleteRule"> - <argument name="name" value="SampleRule"/> - <argument name="searchInput" value="{{AdminSecondaryGridSection.taxIdentifierSearch}}"/> - </actionGroup> - <!-- Go to the tax rate page --> - <amOnPage url="{{AdminTaxRateGridPage.url}}" stepKey="goToTaxRatesPage"/> - <waitForPageLoad stepKey="waitForRatesPage"/> - <!-- Delete tax rate that were created --> - <actionGroup ref="deleteEntitySecondaryGrid" stepKey="deleteNYRate"> - <argument name="name" value="{{SimpleTaxNY.state}}"/> - <argument name="searchInput" value="{{AdminSecondaryGridSection.taxIdentifierSearch}}"/> - </actionGroup> - <actionGroup ref="DeleteProductTaxClassActionGroup" stepKey="deleteFirstProductTaxClass"> - <argument name="taxClassName" value="NewTaxClass"/> - </actionGroup> - <!--logout--> - <actionGroup ref="logout" stepKey="logout"/> - </after> - <!--Create new order--> - <actionGroup stepKey="CreateNewOrder" ref="NavigateToNewOrderPageExistingCustomerActionGroup"> - <argument name="customer" value="Simple_US_Customer_NY"/> - </actionGroup> - <!--Add product to order--> - <click stepKey="clickToAddProduct" selector="{{AdminOrderFormItemsSection.addProducts}}"/> - <waitForPageLoad stepKey="waitForProductsOpened"/> - <checkOption selector="{{AdminOrderFormItemsSection.rowCheck('1')}}" stepKey="selectProduct"/> - <click selector="{{AdminOrderFormItemsSection.addSelected}}" stepKey="clickAddSelectedProducts"/> - <waitForPageLoad stepKey="waitForPageLoad3"/> - <!--Set shipping method--> - <actionGroup stepKey="OrderSelectFlatRateShippingActionGroup" ref="OrderSelectFlatRateShippingActionGroup"/> - <!--Submit order--> - <click stepKey="SubmitOrder" selector="{{AdminOrderFormActionSection.SubmitOrder}}"/> - <waitForPageLoad stepKey="waitForPageLoad4"/> - <grabTextFrom stepKey="getOrderId" selector="|Order # (\d+)|"/> - <!--Open new created order--> - <amOnPage url="{{AdminOrdersPage.url}}" stepKey="goToOrders"/> - <waitForPageLoad stepKey="waitForPageLoad5"/> - <actionGroup ref="FilterOrderGridByIdActionGroup" stepKey="filterOrderGridById"> - <argument name="orderId" value="$getOrderId"/> - </actionGroup> - <!--Create order invoice--> - <click selector="{{AdminDataGridTableSection.rowViewAction('1')}}" stepKey="clickCreatedOrderInGrid"/> - <click selector="{{AdminOrderDetailsMainActionsSection.invoice}}" stepKey="clickInvoiceAction"/> - <see selector="{{AdminHeaderSection.pageTitle}}" userInput="New Invoice" stepKey="seePageNameNewInvoicePage"/> - <click selector="{{AdminInvoiceMainActionsSection.submitInvoice}}" stepKey="clickSubmitInvoice"/> - <see selector="{{AdminOrderDetailsMessagesSection.successMessage}}" userInput="The invoice has been created." stepKey="seeInvoiceCreateSuccess"/> - <see selector="{{AdminInvoiceOrderInformationSection.orderStatus}}" userInput="Processing" stepKey="seeOrderProcessing"/> - <!--Create Credit Memo--> - <click selector="{{AdminOrderDetailsMainActionsSection.creditMemo}}" stepKey="clickCreditMemoAction"/> - <fillField selector="{{AdminCreditMemoTotalSection.refundShipping}}" userInput="0" stepKey="setRefundShipping"/> - <see selector="{{AdminHeaderSection.pageTitle}}" userInput="New Memo" stepKey="seeNewMemoInPageTitle"/> - <click selector="{{AdminCreditMemoTotalSection.submitRefundOffline}}" stepKey="clickRefundOffline"/> - <see selector="{{AdminOrderDetailsMessagesSection.successMessage}}" userInput="You created the credit memo." stepKey="seeCreditMemoSuccess"/> - <click selector="{{AdminCreditMemoTotalSection.creditMemoItem}}" stepKey="goToCreatedCreditMemo"/> - <waitForPageLoad stepKey="waitForPageLoad6"/> - <!--View created memo and verify tax for product--> - <click selector="{{AdminCreditMemoTotalSection.viewMemo}}" stepKey="ViewMemo"/> - <waitForPageLoad stepKey="waitForPageLoad7"/> - <grabTextFrom selector="{{AdminCreditMemoTotalSection.grandTotal}}" stepKey="getGrandTotal"/> - <assertEquals expected='$123.00' expectedType="string" actual="($getGrandTotal)" stepKey="assertGrandTotalValue"/> - - </test> -</tests> diff --git a/app/code/Magento/Tax/Test/Mftf/Test/DeleteTaxRateEntityTest.xml b/app/code/Magento/Tax/Test/Mftf/Test/DeleteTaxRateEntityTest.xml index 5f855f5bb750d..4b34121b10829 100644 --- a/app/code/Magento/Tax/Test/Mftf/Test/DeleteTaxRateEntityTest.xml +++ b/app/code/Magento/Tax/Test/Mftf/Test/DeleteTaxRateEntityTest.xml @@ -20,7 +20,7 @@ </annotations> <before> <createData entity="defaultTaxRate" stepKey="initialTaxRate"/> - <actionGroup ref="LoginAsAdmin" stepKey="loginAsAdmin"/> + <actionGroup ref="AdminLoginActionGroup" stepKey="loginAsAdmin"/> </before> <!-- Search the tax rate on tax grid page --> diff --git a/app/code/Magento/Tax/Test/Mftf/Test/StorefrontTaxInformationInShoppingCartForCustomerPhysicalQuoteTest.xml b/app/code/Magento/Tax/Test/Mftf/Test/StorefrontTaxInformationInShoppingCartForCustomerPhysicalQuoteTest.xml deleted file mode 100644 index d81ea25de87a4..0000000000000 --- a/app/code/Magento/Tax/Test/Mftf/Test/StorefrontTaxInformationInShoppingCartForCustomerPhysicalQuoteTest.xml +++ /dev/null @@ -1,125 +0,0 @@ -<?xml version="1.0" encoding="UTF-8"?> -<!-- - /** - * Copyright © Magento, Inc. All rights reserved. - * See COPYING.txt for license details. - */ ---> - -<tests xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" - xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/testSchema.xsd"> - <test name="StorefrontTaxInformationInShoppingCartForCustomerPhysicalQuoteTest"> - <annotations> - <features value="Tax"/> - <stories value="Shopping cart taxes"/> - <title value="Tax information are updating/recalculating on fly in shopping cart for Customer with default addresses (physical quote)"/> - <description value="Tax information are updating/recalculating on fly in shopping cart for Customer with default addresses (physical quote)"/> - <severity value="CRITICAL"/> - <testCaseId value="MAGETWO-41932"/> - <group value="checkout"/> - <group value="tax"/> - </annotations> - <before> - <!-- Preconditions --> - <!-- Tax Rule is created based on default tax rates (Stores>Tax Rule) US-CA-*-Rate 1 = 8.2500 US-NY-*-Rate 1 = 8.3750 --> - <createData entity="SimpleTaxRule" stepKey="createTaxRule"/> - <!-- Fixed Product Tax attribute is created and added to default attribute set --> - <createData entity="productFPTAttribute" stepKey="createProductFPTAttribute"/> - <createData entity="AddToDefaultSet" stepKey="addFPTToAttributeSet"> - <requiredEntity createDataKey="createProductFPTAttribute"/> - </createData> - <!-- Tax configuration (Store>Configuration; Sales>Tax) With FPT Enable --> - <createData entity="Tax_Config_NY" stepKey="taxConfigurationNYWithFPTEnable"/> - <!-- Store>Configuration; Sales>Tax FPT Enable --> - <createData entity="WeeeConfigEnable" stepKey="enableFPT"/> - <!-- Simple product is created Price = 10; FPT United States/California/10,United States/New York/20 --> - <createData entity="_defaultCategory" stepKey="createCategory"/> - <createData entity="SimpleProduct" stepKey="createSimpleProduct"> - <field key="price">10.00</field> - <requiredEntity createDataKey="createCategory"/> - </createData> - <!-- Customer is created with default addresses: --> - <createData entity="Simple_US_Customer_CA" stepKey="createCustomer"/> - <actionGroup ref="LoginAsAdmin" stepKey="loginAsAdmin"/> - <actionGroup ref="SearchForProductOnBackendActionGroup" stepKey="searchForSimpleProduct"> - <argument name="product" value="$$createSimpleProduct$$"/> - </actionGroup> - <actionGroup ref="OpenEditProductOnBackendActionGroup" stepKey="openEditProduct"> - <argument name="product" value="$$createSimpleProduct$$"/> - </actionGroup> - <actionGroup ref="AdminProductAddFPTValueActionGroup" stepKey="addFPTValue1"> - <argument name="FPTAttributeCode" value="$$createProductFPTAttribute.attribute_code$$"/> - <argument name="stateForFPT" value="California"/> - <argument name="valueForFPT" value="10"/> - </actionGroup> - <actionGroup ref="AdminProductAddFPTValueActionGroup" stepKey="addFPTValue2"> - <argument name="FPTAttributeCode" value="$$createProductFPTAttribute.attribute_code$$"/> - <argument name="stateForFPT" value="New York"/> - <argument name="valueForFPT" value="20"/> - </actionGroup> - <actionGroup ref="SaveProductFormActionGroup" stepKey="saveProduct"/> - <amOnPage url="{{AdminProductIndexPage.url}}" stepKey="navigateToProductIndex"/> - <waitForPageLoad stepKey="waitForProductIndexPageLoad"/> - <actionGroup ref="ResetProductGridToDefaultViewActionGroup" stepKey="resetGridToDefaultKeywordSearch"/> - </before> - <after> - <actionGroup ref="StorefrontCustomerLogoutActionGroup" stepKey="customerLogout"/> - <deleteData createDataKey="createTaxRule" stepKey="deleteTaxRule"/> - <deleteData createDataKey="createProductFPTAttribute" stepKey="deleteProductFPTAttribute"/> - <createData entity="DefaultTaxConfig" stepKey="defaultTaxConfiguration"/> - <createData entity="WeeeConfigDisable" stepKey="disableFPT"/> - <deleteData createDataKey="createSimpleProduct" stepKey="deleteSimpleProduct"/> - <deleteData createDataKey="createCategory" stepKey="deleteCategory"/> - <deleteData createDataKey="createCustomer" stepKey="deleteCustomer"/> - <actionGroup ref="logout" stepKey="logout"/> - </after> - <!-- Test Steps --> - <!-- Step 1: Go to Storefront as logged in Customer --> - <actionGroup ref="LoginToStorefrontActionGroup" stepKey="customerLogin"> - <argument name="Customer" value="$$createCustomer$$" /> - </actionGroup> - <!-- Step 2: Add simple product to shopping cart --> - <amOnPage url="{{StorefrontProductPage.url($$createSimpleProduct.name$$)}}" stepKey="amOnSimpleProductPage"/> - <waitForPageLoad stepKey="waitForPageLoad"/> - <actionGroup ref="StorefrontAddProductToCartActionGroup" stepKey="cartAddSimpleProductToCart"> - <argument name="product" value="$$createSimpleProduct$$"/> - <argument name="productCount" value="1"/> - </actionGroup> - <!-- Step 3: Go to Shopping Cart --> - <actionGroup ref="ClickViewAndEditCartFromMiniCartActionGroup" stepKey="goToShoppingCartFromMinicart"/> - <!-- Step 4: Open Estimate Shipping and Tax section --> - <conditionalClick selector="{{CheckoutCartSummarySection.estimateShippingAndTax}}" dependentSelector="{{CheckoutCartSummarySection.country}}" visible="false" stepKey="expandEstimateShippingandTax" /> - <seeOptionIsSelected selector="{{CheckoutCartSummarySection.country}}" userInput="{{US_Address_CA.country}}" stepKey="checkCustomerCountry" /> - <seeOptionIsSelected selector="{{CheckoutCartSummarySection.stateProvince}}" userInput="{{US_Address_CA.state}}" stepKey="checkCustomerRegion" /> - <grabValueFrom selector="{{CheckoutCartSummarySection.postcode}}" stepKey="grabTextPostCode"/> - <assertEquals message="Customer postcode is invalid" stepKey="checkCustomerPostcode"> - <expectedResult type="string">{{US_Address_CA.postcode}}</expectedResult> - <actualResult type="variable">grabTextPostCode</actualResult> - </assertEquals> - <see selector="{{CheckoutCartSummarySection.amountFPT}}" userInput="$10" stepKey="checkFPTAmountCA" /> - <see selector="{{CheckoutCartSummarySection.taxAmount}}" userInput="$0.83" stepKey="checkTaxAmountCA" /> - <scrollTo selector="{{CheckoutCartSummarySection.taxSummary}}" stepKey="scrollToTaxSummary" /> - <click selector="{{CheckoutCartSummarySection.taxSummary}}" stepKey="taxSummary"/> - <see selector="{{CheckoutCartSummarySection.rate}}" userInput="US-CA-*-Rate 1 (8.25%)" stepKey="checkRateCA" /> - <!-- Step 5: Change Data --> - <selectOption selector="{{CheckoutCartSummarySection.country}}" userInput="Switzerland" stepKey="selectSwitzerlandCountry"/> - <selectOption selector="{{CheckoutCartSummarySection.stateProvince}}" userInput="Aargau" stepKey="selectAargauRegion"/> - <fillField selector="{{CheckoutCartSummarySection.postcode}}" userInput="1234" stepKey="inputPostCode"/> - <!-- Step 6: Select shipping rate again(it need for get new totals request - performance reason) --> - <click selector="{{CheckoutCartSummarySection.flatRateShippingMethod}}" stepKey="selectflatRateShippingMethodShippingMethod"/> - <scrollTo selector="{{CheckoutCartSummarySection.taxAmount}}" stepKey="scrollToTaxSummary2" /> - <see selector="{{CheckoutCartSummarySection.taxAmount}}" userInput="$0.00" stepKey="checkTaxAmount" /> - <dontSeeElement selector="{{CheckoutCartSummarySection.amountFPT}}" stepKey="checkFPTIsNotDisplayed" /> - <!-- Step 7: Change Data --> - <selectOption selector="{{CheckoutCartSummarySection.country}}" userInput="United States" stepKey="selectUnitedStatesCountry"/> - <selectOption selector="{{CheckoutCartSummarySection.stateProvince}}" userInput="New York" stepKey="selectNewYorkRegion"/> - <fillField selector="{{CheckoutCartSummarySection.postcode}}" userInput="12345" stepKey="inputPostCode2"/> - <!-- Step 8: Select shipping rate again(it need for get new totals request - performance reason) --> - <click selector="{{CheckoutCartSummarySection.flatRateShippingMethod}}" stepKey="selectflatRateShippingMethodShippingMethod2"/> - <scrollTo selector="{{CheckoutCartSummarySection.taxSummary}}" stepKey="scrollToTaxSummary3" /> - <click selector="{{CheckoutCartSummarySection.taxSummary}}" stepKey="taxSummary3"/> - <see selector="{{CheckoutCartSummarySection.taxAmount}}" userInput="$0.84" stepKey="checkTaxAmountNY" /> - <see selector="{{CheckoutCartSummarySection.rate}}" userInput="US-NY-*-Rate 1 (8.375%)" stepKey="checkRateNY" /> - <see selector="{{CheckoutCartSummarySection.amountFPT}}" userInput="$20" stepKey="checkFPTAmountNY" /> - </test> -</tests> diff --git a/app/code/Magento/Tax/Test/Mftf/Test/StorefrontTaxInformationInShoppingCartForCustomerVirtualQuoteTest.xml b/app/code/Magento/Tax/Test/Mftf/Test/StorefrontTaxInformationInShoppingCartForCustomerVirtualQuoteTest.xml deleted file mode 100644 index c62fc59a52e68..0000000000000 --- a/app/code/Magento/Tax/Test/Mftf/Test/StorefrontTaxInformationInShoppingCartForCustomerVirtualQuoteTest.xml +++ /dev/null @@ -1,89 +0,0 @@ -<?xml version="1.0" encoding="UTF-8"?> -<!-- - /** - * Copyright © Magento, Inc. All rights reserved. - * See COPYING.txt for license details. - */ ---> - -<tests xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" - xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/testSchema.xsd"> - <test name="StorefrontTaxInformationInShoppingCartForCustomerVirtualQuoteTest"> - <annotations> - <features value="Tax"/> - <stories value="Shopping cart taxes"/> - <title value="Tax information are updating/recalculating on fly in shopping cart for Customer with default addresses (virtual quote)"/> - <description value="Tax information are updating/recalculating on fly in shopping cart for Customer with default addresses (virtual quote)"/> - <severity value="CRITICAL"/> - <testCaseId value="MAGETWO-41933"/> - <group value="checkout"/> - <group value="tax"/> - </annotations> - <before> - <!-- Preconditions --> - <!-- Tax Rule is created based on default tax rates (Stores>Tax Rule) US-CA-*-Rate 1 = 8.2500 US-NY-*-Rate 1 = 8.3750 --> - <createData entity="SimpleTaxRule" stepKey="createTaxRule"/> - <!-- Fixed Product Tax attribute is created and added to default attribute set --> - <createData entity="productFPTAttribute" stepKey="createProductFPTAttribute"/> - <createData entity="AddToDefaultSet" stepKey="addFPTToAttributeSet"> - <requiredEntity createDataKey="createProductFPTAttribute"/> - </createData> - <!-- Tax configuration (Store>Configuration; Sales>Tax) --> - <createData entity="Tax_Config_CA" stepKey="taxConfigurationCA"/> - <!-- Virtual product is created: --> - <createData entity="VirtualProduct" stepKey="createVirtualProduct"> - <field key="price">40.00</field> - </createData> - <!-- Customer is created with default addresses: --> - <createData entity="Simple_US_Customer_NY" stepKey="createCustomer"/> - </before> - <after> - <actionGroup ref="StorefrontCustomerLogoutActionGroup" stepKey="customerLogout"/> - <deleteData createDataKey="createTaxRule" stepKey="deleteTaxRule"/> - <deleteData createDataKey="createProductFPTAttribute" stepKey="deleteProductFPTAttribute"/> - <createData entity="DefaultTaxConfig" stepKey="defaultTaxConfiguration"/> - <deleteData createDataKey="createVirtualProduct" stepKey="deleteVirtualProduct"/> - <deleteData createDataKey="createCustomer" stepKey="deleteCustomer"/> - </after> - <!-- Test Steps --> - <!-- Step 1: Go to Storefront as logged in Customer --> - <actionGroup ref="LoginToStorefrontActionGroup" stepKey="customerLogin"> - <argument name="Customer" value="$$createCustomer$$" /> - </actionGroup> - <!-- Step 2: Add virtual product to shopping cart --> - <amOnPage url="{{StorefrontProductPage.url($$createVirtualProduct.name$$)}}" stepKey="amOnStorefrontVirtualProductPage"/> - <waitForPageLoad stepKey="waitForPageLoad"/> - <actionGroup ref="StorefrontAddProductToCartActionGroup" stepKey="cartAddVirtualProductToCart"> - <argument name="product" value="$$createVirtualProduct$$"/> - <argument name="productCount" value="1"/> - </actionGroup> - <!-- Step 3: Go to Shopping Cart --> - <actionGroup ref="ClickViewAndEditCartFromMiniCartActionGroup" stepKey="goToShoppingCartFromMinicart"/> - <!-- Step 4: Open Estimate Shipping and Tax section --> - <conditionalClick selector="{{CheckoutCartSummarySection.estimateShippingAndTax}}" dependentSelector="{{CheckoutCartSummarySection.country}}" visible="false" stepKey="expandEstimateShippingandTax" /> - <seeOptionIsSelected selector="{{CheckoutCartSummarySection.country}}" userInput="{{US_Address_NY.country}}" stepKey="checkCustomerCountry" /> - <seeOptionIsSelected selector="{{CheckoutCartSummarySection.stateProvince}}" userInput="{{US_Address_NY.state}}" stepKey="checkCustomerRegion" /> - <grabValueFrom selector="{{CheckoutCartSummarySection.postcode}}" stepKey="grabTextPostCode"/> - <assertEquals message="Customer postcode is invalid" stepKey="checkCustomerPostcode"> - <expectedResult type="string">{{US_Address_NY.postcode}}</expectedResult> - <actualResult type="variable">grabTextPostCode</actualResult> - </assertEquals> - <scrollTo selector="{{CheckoutCartSummarySection.taxSummary}}" stepKey="scrollToTaxSummary" /> - <click selector="{{CheckoutCartSummarySection.taxSummary}}" stepKey="expandTaxSummary"/> - <see selector="{{CheckoutCartSummarySection.rate}}" userInput="US-NY-*-Rate 1 (8.375%)" stepKey="checkRateNY" /> - <!-- Step 5: Change Data --> - <selectOption selector="{{CheckoutCartSummarySection.country}}" userInput="Switzerland" stepKey="selectSwitzerlandCountry"/> - <selectOption selector="{{CheckoutCartSummarySection.stateProvince}}" userInput="Aargau" stepKey="selectAargauRegion"/> - <fillField selector="{{CheckoutCartSummarySection.postcode}}" userInput="1234" stepKey="inputPostCode"/> - <scrollTo selector="{{CheckoutCartSummarySection.taxAmount}}" stepKey="scrollToTaxSummary2" /> - <see selector="{{CheckoutCartSummarySection.taxAmount}}" userInput="$0.00" stepKey="checkTaxAmount" /> - <!-- Step 6: Change Data --> - <selectOption selector="{{CheckoutCartSummarySection.country}}" userInput="United States" stepKey="selectUnitedStatesCountry"/> - <selectOption selector="{{CheckoutCartSummarySection.stateProvince}}" userInput="California" stepKey="selectCaliforniaRegion"/> - <fillField selector="{{CheckoutCartSummarySection.postcode}}" userInput="90230" stepKey="inputPostCode2"/> - <scrollTo selector="{{CheckoutCartSummarySection.taxSummary}}" stepKey="scrollToTaxSummary3" /> - <click selector="{{CheckoutCartSummarySection.taxSummary}}" stepKey="taxSummary2"/> - <see selector="{{CheckoutCartSummarySection.taxAmount}}" userInput="$3.30" stepKey="checkTaxAmount2" /> - <see selector="{{CheckoutCartSummarySection.rate}}" userInput="US-CA-*-Rate 1 (8.25%)" stepKey="checkRateCA" /> - </test> -</tests> diff --git a/app/code/Magento/Tax/Test/Mftf/Test/StorefrontTaxInformationInShoppingCartForGuestPhysicalQuoteTest.xml b/app/code/Magento/Tax/Test/Mftf/Test/StorefrontTaxInformationInShoppingCartForGuestPhysicalQuoteTest.xml deleted file mode 100644 index e8c2f1a66f0c3..0000000000000 --- a/app/code/Magento/Tax/Test/Mftf/Test/StorefrontTaxInformationInShoppingCartForGuestPhysicalQuoteTest.xml +++ /dev/null @@ -1,115 +0,0 @@ -<?xml version="1.0" encoding="UTF-8"?> -<!-- - /** - * Copyright © Magento, Inc. All rights reserved. - * See COPYING.txt for license details. - */ ---> - -<tests xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" - xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/testSchema.xsd"> - <test name="StorefrontTaxInformationInShoppingCartForGuestPhysicalQuoteTest"> - <annotations> - <features value="Tax"/> - <stories value="Shopping cart taxes"/> - <title value="Tax information are updating/recalculating on fly in shopping cart for Guest (physical quote)"/> - <description value="Tax information are updating/recalculating on fly in shopping cart for Guest (physical quote)"/> - <severity value="CRITICAL"/> - <testCaseId value="MAGETWO-41930"/> - <group value="checkout"/> - <group value="tax"/> - </annotations> - <before> - <!-- Preconditions --> - <!-- Tax Rule is created based on default tax rates (Stores>Tax Rule) US-CA-*-Rate 1 = 8.2500 US-NY-*-Rate 1 = 8.3750 --> - <createData entity="SimpleTaxRule" stepKey="createTaxRule"/> - <!-- Fixed Product Tax attribute is created and added to default attribute set --> - <createData entity="productFPTAttribute" stepKey="createProductFPTAttribute"/> - <createData entity="AddToDefaultSet" stepKey="addFPTToAttributeSet"> - <requiredEntity createDataKey="createProductFPTAttribute"/> - </createData> - <!-- Tax configuration (Store>Configuration; Sales>Tax) --> - <createData entity="Tax_Config_CA" stepKey="taxConfigurationForCA"/> - <!-- Store>Configuration; Sales>Tax FPT Enable --> - <createData entity="WeeeConfigEnable" stepKey="enableFPT"/> - <!-- Simple product is created Price = 10; FPT United States/California/10,United States/New York/20 --> - <createData entity="_defaultCategory" stepKey="createCategory"/> - <createData entity="SimpleProduct" stepKey="createSimpleProduct"> - <field key="price">10.00</field> - <requiredEntity createDataKey="createCategory"/> - </createData> - <actionGroup ref="LoginAsAdmin" stepKey="loginAsAdmin"/> - <actionGroup ref="SearchForProductOnBackendActionGroup" stepKey="searchForSimpleProduct"> - <argument name="product" value="$$createSimpleProduct$$"/> - </actionGroup> - <actionGroup ref="OpenEditProductOnBackendActionGroup" stepKey="openEditProduct"> - <argument name="product" value="$$createSimpleProduct$$"/> - </actionGroup> - <actionGroup ref="AdminProductAddFPTValueActionGroup" stepKey="addFPTValue1"> - <argument name="FPTAttributeCode" value="$$createProductFPTAttribute.attribute_code$$"/> - <argument name="stateForFPT" value="California"/> - <argument name="valueForFPT" value="10"/> - </actionGroup> - <actionGroup ref="AdminProductAddFPTValueActionGroup" stepKey="addFPTValue2"> - <argument name="FPTAttributeCode" value="$$createProductFPTAttribute.attribute_code$$"/> - <argument name="stateForFPT" value="New York"/> - <argument name="valueForFPT" value="20"/> - </actionGroup> - <actionGroup ref="SaveProductFormActionGroup" stepKey="saveProduct"/> - <amOnPage url="{{AdminProductIndexPage.url}}" stepKey="navigateToProductIndex"/> - <waitForPageLoad stepKey="waitForProductIndexPageLoad"/> - <actionGroup ref="ResetProductGridToDefaultViewActionGroup" stepKey="resetGridToDefaultKeywordSearch"/> - </before> - <after> - <deleteData createDataKey="createTaxRule" stepKey="deleteTaxRule"/> - <deleteData createDataKey="createProductFPTAttribute" stepKey="deleteProductFPTAttribute"/> - <createData entity="DefaultTaxConfig" stepKey="defaultTaxConfiguration"/> - <createData entity="WeeeConfigDisable" stepKey="disableFPT"/> - <deleteData createDataKey="createCategory" stepKey="deleteCategory"/> - <deleteData createDataKey="createSimpleProduct" stepKey="deleteSimpleProduct"/> - <actionGroup ref="logout" stepKey="logout"/> - </after> - <!-- Test Steps --> - <!-- Step 1: Go to Storefront as Guest --> - <amOnPage url="{{StorefrontHomePage.url}}" stepKey="amOnStorefrontPage"/> - <waitForPageLoad stepKey="waitForPageLoad"/> - <!-- Step 2: Add simple product to shopping cart --> - <amOnPage url="{{StorefrontProductPage.url($$createSimpleProduct.name$$)}}" stepKey="amOnSimpleProductPage"/> - <waitForPageLoad stepKey="waitForPageLoad2"/> - <actionGroup ref="StorefrontAddProductToCartActionGroup" stepKey="cartAddSimpleProductToCart"> - <argument name="product" value="$$createSimpleProduct$$"/> - <argument name="productCount" value="1"/> - </actionGroup> - <!-- Step 3: Go to Shopping Cart --> - <actionGroup ref="ClickViewAndEditCartFromMiniCartActionGroup" stepKey="goToShoppingCartFromMinicart"/> - <!-- Step 4: Open Estimate Shipping and Tax section --> - <conditionalClick selector="{{CheckoutCartSummarySection.estimateShippingAndTax}}" dependentSelector="{{CheckoutCartSummarySection.country}}" visible="false" stepKey="expandEstimateShippingandTax" /> - <selectOption selector="{{CheckoutCartSummarySection.country}}" userInput="United States" stepKey="selectUSCountry"/> - <selectOption selector="{{CheckoutCartSummarySection.stateProvince}}" userInput="California" stepKey="selectCaliforniaRegion"/> - <see selector="{{CheckoutCartSummarySection.amountFPT}}" userInput="$10" stepKey="checkFPTAmountCA" /> - <see selector="{{CheckoutCartSummarySection.taxAmount}}" userInput="$0.83" stepKey="checkTaxAmountCA" /> - <scrollTo selector="{{CheckoutCartSummarySection.taxSummary}}" stepKey="scrollToTaxSummary" /> - <click selector="{{CheckoutCartSummarySection.taxSummary}}" stepKey="taxSummary"/> - <see selector="{{CheckoutCartSummarySection.rate}}" userInput="US-CA-*-Rate 1 (8.25%)" stepKey="checkRateCA" /> - <!-- Step 5: Change Data --> - <selectOption selector="{{CheckoutCartSummarySection.country}}" userInput="Switzerland" stepKey="selectSwitzerlandCountry"/> - <selectOption selector="{{CheckoutCartSummarySection.stateProvince}}" userInput="Aargau" stepKey="selectAargauRegion"/> - <fillField selector="{{CheckoutCartSummarySection.postcode}}" userInput="1234" stepKey="inputPostCode"/> - <!-- Step 6: Select shipping rate again(it need for get new totals request - performance reason) --> - <click selector="{{CheckoutCartSummarySection.flatRateShippingMethod}}" stepKey="selectflatRateShippingMethodShippingMethod"/> - <scrollTo selector="{{CheckoutCartSummarySection.taxAmount}}" stepKey="scrollToTaxSummary2" /> - <see selector="{{CheckoutCartSummarySection.taxAmount}}" userInput="$0.00" stepKey="checkTaxAmount" /> - <dontSeeElement selector="{{CheckoutCartSummarySection.amountFPT}}" stepKey="checkFPTIsNotDisplayed" /> - <!-- Step 7: Change Data --> - <selectOption selector="{{CheckoutCartSummarySection.country}}" userInput="United States" stepKey="selectUnitedStatesCountry"/> - <selectOption selector="{{CheckoutCartSummarySection.stateProvince}}" userInput="New York" stepKey="selectNewYorkRegion"/> - <fillField selector="{{CheckoutCartSummarySection.postcode}}" userInput="12345" stepKey="inputPostCode2"/> - <!-- Step 8: Select shipping rate again(it need for get new totals request - performance reason) --> - <click selector="{{CheckoutCartSummarySection.flatRateShippingMethod}}" stepKey="selectflatRateShippingMethodShippingMethod2"/> - <scrollTo selector="{{CheckoutCartSummarySection.taxSummary}}" stepKey="scrollToTaxSummary3" /> - <click selector="{{CheckoutCartSummarySection.taxSummary}}" stepKey="taxSummary2"/> - <see selector="{{CheckoutCartSummarySection.taxAmount}}" userInput="$0.84" stepKey="checkTaxAmountNY" /> - <see selector="{{CheckoutCartSummarySection.rate}}" userInput="US-NY-*-Rate 1 (8.375%)" stepKey="checkRateNY" /> - <see selector="{{CheckoutCartSummarySection.amountFPT}}" userInput="$20" stepKey="checkFPTAmountNY" /> - </test> -</tests> diff --git a/app/code/Magento/Tax/Test/Mftf/Test/StorefrontTaxInformationInShoppingCartForGuestVirtualQuoteTest.xml b/app/code/Magento/Tax/Test/Mftf/Test/StorefrontTaxInformationInShoppingCartForGuestVirtualQuoteTest.xml deleted file mode 100644 index 4b911cd6db32f..0000000000000 --- a/app/code/Magento/Tax/Test/Mftf/Test/StorefrontTaxInformationInShoppingCartForGuestVirtualQuoteTest.xml +++ /dev/null @@ -1,79 +0,0 @@ -<?xml version="1.0" encoding="UTF-8"?> -<!-- - /** - * Copyright © Magento, Inc. All rights reserved. - * See COPYING.txt for license details. - */ ---> - -<tests xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" - xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/testSchema.xsd"> - <test name="StorefrontTaxInformationInShoppingCartForGuestVirtualQuoteTest"> - <annotations> - <features value="Tax"/> - <stories value="Shopping cart taxes"/> - <title value="Tax information are updating/recalculating on fly in shopping cart for Guest (virtual quote)"/> - <description value="Tax information are updating/recalculating on fly in shopping cart for Guest (virtual quote)"/> - <severity value="CRITICAL"/> - <testCaseId value="MAGETWO-41931"/> - <group value="checkout"/> - <group value="tax"/> - </annotations> - <before> - <!-- Preconditions --> - <!-- Tax Rule is created based on default tax rates (Stores>Tax Rule) US-CA-*-Rate 1 = 8.2500 US-NY-*-Rate 1 = 8.3750 --> - <createData entity="SimpleTaxRule" stepKey="createTaxRule"/> - <!-- Fixed Product Tax attribute is created and added to default attribute set --> - <createData entity="productFPTAttribute" stepKey="createProductFPTAttribute"/> - <createData entity="AddToDefaultSet" stepKey="addFPTToAttributeSet"> - <requiredEntity createDataKey="createProductFPTAttribute"/> - </createData> - <!-- Tax configuration (Store>Configuration; Sales>Tax) --> - <createData entity="Tax_Config_CA" stepKey="taxConfigurationCA"/> - <!-- Virtual product is created: Price = 10 --> - <createData entity="VirtualProduct" stepKey="createVirtualProduct"> - <field key="price">40.00</field> - </createData> - </before> - <after> - <deleteData createDataKey="createTaxRule" stepKey="deleteTaxRule"/> - <deleteData createDataKey="createProductFPTAttribute" stepKey="deleteProductFPTAttribute"/> - <createData entity="DefaultTaxConfig" stepKey="defaultTaxConfiguration"/> - <deleteData createDataKey="createVirtualProduct" stepKey="deleteVirtualProduct"/> - </after> - <!-- Test Steps --> - <!-- Step 1: Go to Storefront as Guest --> - <amOnPage url="{{StorefrontHomePage.url}}" stepKey="amOnStorefrontPage"/> - <waitForPageLoad stepKey="waitForPageLoad"/> - <!-- Step 2: Add virtual product to shopping cart --> - <amOnPage url="{{StorefrontProductPage.url($$createVirtualProduct.name$$)}}" stepKey="amOnStorefrontVirtualProductPage"/> - <waitForPageLoad stepKey="waitForPageLoad2"/> - <actionGroup ref="StorefrontAddProductToCartActionGroup" stepKey="cartAddVirtualProductToCart"> - <argument name="product" value="$$createVirtualProduct$$"/> - <argument name="productCount" value="1"/> - </actionGroup> - <!-- Step 3: Go to Shopping Cart --> - <actionGroup ref="ClickViewAndEditCartFromMiniCartActionGroup" stepKey="goToShoppingCartFromMinicart"/> - <!-- Step 4: Open Estimate Shipping and Tax section --> - <conditionalClick selector="{{CheckoutCartSummarySection.estimateShippingAndTax}}" dependentSelector="{{CheckoutCartSummarySection.country}}" visible="false" stepKey="expandEstimateShippingandTax" /> - <selectOption selector="{{CheckoutCartSummarySection.country}}" userInput="United States" stepKey="selectUSCountry"/> - <selectOption selector="{{CheckoutCartSummarySection.stateProvince}}" userInput="California" stepKey="selectCaliforniaRegion"/> - <scrollTo selector="{{CheckoutCartSummarySection.taxSummary}}" stepKey="scrollToTaxSummary" /> - <see selector="{{CheckoutCartSummarySection.taxAmount}}" userInput="$3.30" stepKey="checkTaxAmountCA" /> - <click selector="{{CheckoutCartSummarySection.taxSummary}}" stepKey="taxSummary"/> - <see selector="{{CheckoutCartSummarySection.rate}}" userInput="US-CA-*-Rate 1 (8.25%)" stepKey="checkRateCA" /> - <!-- Step 5: Change Data --> - <selectOption selector="{{CheckoutCartSummarySection.country}}" userInput="Switzerland" stepKey="selectSwitzerlandCountry"/> - <selectOption selector="{{CheckoutCartSummarySection.stateProvince}}" userInput="Aargau" stepKey="selectAargauRegion"/> - <fillField selector="{{CheckoutCartSummarySection.postcode}}" userInput="1234" stepKey="inputPostCode"/> - <see selector="{{CheckoutCartSummarySection.taxAmount}}" userInput="$0.00" stepKey="checkTaxAmount" /> - <!-- Step 6: Change Data --> - <selectOption selector="{{CheckoutCartSummarySection.country}}" userInput="United States" stepKey="selectUnitedStatesCountry"/> - <selectOption selector="{{CheckoutCartSummarySection.stateProvince}}" userInput="New York" stepKey="selectNewYorkRegion"/> - <fillField selector="{{CheckoutCartSummarySection.postcode}}" userInput="12345" stepKey="inputPostCode2"/> - <scrollTo selector="{{CheckoutCartSummarySection.taxSummary}}" stepKey="scrollToTaxSummary2" /> - <click selector="{{CheckoutCartSummarySection.taxSummary}}" stepKey="taxSummary2"/> - <see selector="{{CheckoutCartSummarySection.taxAmount}}" userInput="$3.35" stepKey="checkTaxAmountNY" /> - <see selector="{{CheckoutCartSummarySection.rate}}" userInput="US-NY-*-Rate 1 (8.375%)" stepKey="checkRateNY" /> - </test> -</tests> diff --git a/app/code/Magento/Tax/Test/Mftf/Test/StorefrontTaxQuoteCartTest.xml b/app/code/Magento/Tax/Test/Mftf/Test/StorefrontTaxQuoteCartTest.xml deleted file mode 100644 index f0fac38a6c05c..0000000000000 --- a/app/code/Magento/Tax/Test/Mftf/Test/StorefrontTaxQuoteCartTest.xml +++ /dev/null @@ -1,465 +0,0 @@ -<?xml version="1.0" encoding="UTF-8"?> -<!-- - /** - * Copyright © Magento, Inc. All rights reserved. - * See COPYING.txt for license details. - */ ---> - -<tests xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" - xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/testSchema.xsd"> - <test name="StorefrontTaxQuoteCartLoggedInSimple"> - <annotations> - <features value="Tax"/> - <stories value="Tax Calculation in Shopping Cart"/> - <title value="Tax for Simple Product Quote should be recalculated in Shopping Cart for Logged in Customer with Default Address"/> - <description value="Tax for Simple Product Quote should be recalculated in Shopping Cart for Logged in Customer with Default Address"/> - <severity value="CRITICAL"/> - <testCaseId value="MC-295"/> - <group value="Tax"/> - </annotations> - <before> - <actionGroup ref="LoginAsAdmin" stepKey="loginAsAdmin"/> - <createData entity="SimpleProduct2" stepKey="simpleProduct1"/> - - <!-- Fill in rules to display tax in the cart --> - <actionGroup ref="EditTaxConfigurationByUIActionGroup" stepKey="fillDefaultTaxForms"/> - - <!-- Go to tax rule page --> - <amOnPage url="{{AdminTaxRuleGridPage.url}}" stepKey="goToTaxRulePage"/> - <waitForPageLoad stepKey="waitForTaxRatePage"/> - <click stepKey="addNewTaxRate" selector="{{AdminGridMainControls.add}}"/> - <fillField stepKey="fillRuleName" selector="{{AdminTaxRulesSection.ruleName}}" userInput="SampleRule"/> - - <!-- Add NY and CA tax rules --> - <actionGroup ref="AddNewTaxRateNoZipActionGroup" stepKey="addNYTaxRate"> - <argument name="taxCode" value="SimpleTaxNY"/> - </actionGroup> - - <actionGroup ref="AddNewTaxRateNoZipActionGroup" stepKey="addCATaxRate"> - <argument name="taxCode" value="SimpleTaxCA"/> - </actionGroup> - - <click stepKey="clickSave" selector="{{AdminStoresMainActionsSection.saveButton}}"/> - - <!-- Fill out form for a new user with address --> - <actionGroup ref="SignUpNewUserFromStorefrontActionGroup" stepKey="SignUpNewUser"> - <argument name="Customer" value="Simple_US_Customer_NY"/> - </actionGroup> - - <actionGroup ref="EnterCustomerAddressInfo" stepKey="enterAddressInfo"> - <argument name="Address" value="US_Address_NY"/> - </actionGroup> - </before> - <after> - <!-- Go to the tax rule page and delete the row we created--> - <amOnPage url="{{AdminTaxRuleGridPage.url}}" stepKey="goToTaxRulesPage"/> - <waitForPageLoad stepKey="waitForRulesPage"/> - <actionGroup ref="deleteEntitySecondaryGrid" stepKey="deleteRule"> - <argument name="name" value="SampleRule"/> - <argument name="searchInput" value="{{AdminSecondaryGridSection.taxIdentifierSearch}}"/> - </actionGroup> - - <!-- Go to the tax rate page --> - <amOnPage url="{{AdminTaxRateGridPage.url}}" stepKey="goToTaxRatesPage"/> - <waitForPageLoad stepKey="waitForRatesPage"/> - - <!-- Delete the two tax rates that were created --> - <actionGroup ref="deleteEntitySecondaryGrid" stepKey="deleteNYRate"> - <argument name="name" value="{{SimpleTaxNY.state}}-{{SimpleTaxNY.rate}}"/> - <argument name="searchInput" value="{{AdminSecondaryGridSection.taxIdentifierSearch}}"/> - </actionGroup> - - <actionGroup ref="deleteEntitySecondaryGrid" stepKey="deleteCARate"> - <argument name="name" value="{{SimpleTaxCA.state}}-{{SimpleTaxCA.rate}}"/> - <argument name="searchInput" value="{{AdminSecondaryGridSection.taxIdentifierSearch}}"/> - </actionGroup> - - <!-- Ensure tax won't be shown in the cart --> - <actionGroup ref="ChangeToDefaultTaxConfigurationUIActionGroup" stepKey="changeToDefaultTaxConfiguration"/> - - <amOnPage url="{{AdminLogoutPage.url}}" stepKey="amOnLogoutPage"/> - <deleteData createDataKey="simpleProduct1" stepKey="deleteSimpleProduct1"/> - </after> - - <!-- Go to the created product page and add it to the cart --> - <amOnPage url="$$simpleProduct1.sku$$.html" stepKey="goToSimpleProductPage"/> - <waitForPageLoad stepKey="waitForSimpleProductPage"/> - <click stepKey="addSimpleProductToCart" selector="{{StorefrontProductActionSection.addToCart}}"/> - <waitForPageLoad stepKey="waitForProductAdded"/> - <see stepKey="seeSuccess" selector="{{AdminCategoryMessagesSection.SuccessMessage}}" userInput="You added"/> - - <!-- Assert that taxes are applied correctly for NY --> - <amOnPage url="{{CheckoutCartPage.url}}" stepKey="goToCheckout"/> - <waitForPageLoad stepKey="waitForCart"/> - <waitForLoadingMaskToDisappear stepKey="waitForLoadingMask"/> - - <waitForElementVisible stepKey="waitForOverviewVisible" selector="{{CheckoutPaymentSection.tax}}"/> - <waitForText userInput="$5.00" selector="{{CheckoutPaymentSection.orderSummaryShippingTotal}}" time="30" stepKey="waitForCorrectShippingAmount"/> - <see stepKey="seeTax" selector="{{CheckoutPaymentSection.tax}}" userInput="$10.30"/> - <click stepKey="expandTax" selector="{{CheckoutPaymentSection.tax}}"/> - <see stepKey="seeTaxPercent" selector="{{CheckoutPaymentSection.taxPercentage}}" userInput="({{SimpleTaxNY.rate}}%)"/> - <see stepKey="seeTotalIncl" selector="{{CheckoutPaymentSection.orderSummaryTotalIncluding}}" userInput="$138.30"/> - <see stepKey="seeTotalExcl" selector="{{CheckoutPaymentSection.orderSummaryTotalExcluding}}" userInput="$128.00"/> - - <!-- Change the address --> - <actionGroup ref="ChangeSummaryQuoteAddressActionGroup" stepKey="changeAddress"> - <argument name="taxCode" value="SimpleTaxSwiss"/> - </actionGroup> - - <!-- Assert that taxes are applied correctly for Switzerland --> - <see stepKey="seeTax2" selector="{{CheckoutPaymentSection.tax}}" userInput="$0.00"/> - <see stepKey="seeTotalIncl2" selector="{{CheckoutPaymentSection.orderSummaryTotalIncluding}}" userInput="$128.00"/> - <see stepKey="seeTotalExcl2" selector="{{CheckoutPaymentSection.orderSummaryTotalExcluding}}" userInput="$128.00"/> - - <!-- Change the address --> - <actionGroup ref="ChangeSummaryQuoteAddressActionGroup" stepKey="changeAddress2"> - <argument name="taxCode" value="SimpleTaxCA"/> - </actionGroup> - - <!-- Assert that taxes are applied correctly for CA --> - <see stepKey="seeTax3" selector="{{CheckoutPaymentSection.tax}}" userInput="$10.15"/> - <click stepKey="expandTax2" selector="{{CheckoutPaymentSection.tax}}"/> - <see stepKey="seeTaxPercent2" selector="{{CheckoutPaymentSection.taxPercentage}}" userInput="({{SimpleTaxCA.rate}}%)"/> - <see stepKey="seeTotalIncl3" selector="{{CheckoutPaymentSection.orderSummaryTotalIncluding}}" userInput="$138.15"/> - <see stepKey="seeTotalExcl3" selector="{{CheckoutPaymentSection.orderSummaryTotalExcluding}}" userInput="$128.00"/> - </test> - - - <test name="StorefrontTaxQuoteCartLoggedInVirtual"> - <annotations> - <features value="Tax"/> - <stories value="Tax Calculation in Shopping Cart"/> - <title value="Tax for Virtual Product Quote should be recalculated in Shopping Cart for Logged in Customer with Default Address"/> - <description value="Tax for Virtual Product Quote should be recalculated in Shopping Cart for Logged in Customer with Default Address"/> - <severity value="CRITICAL"/> - <testCaseId value="MC-296"/> - <group value="Tax"/> - </annotations> - <before> - <actionGroup ref="LoginAsAdmin" stepKey="loginAsAdmin"/> - <createData entity="VirtualProduct" stepKey="virtualProduct1"/> - - <!-- Fill in rules to display tax in the cart --> - <actionGroup ref="EditTaxConfigurationByUIActionGroup" stepKey="fillDefaultTaxForms"/> - - <!-- Go to tax rule page --> - <amOnPage url="{{AdminTaxRuleGridPage.url}}" stepKey="goToTaxRulePage"/> - <waitForPageLoad stepKey="waitForTaxRatePage"/> - <click stepKey="addNewTaxRate" selector="{{AdminGridMainControls.add}}"/> - <fillField stepKey="fillRuleName" selector="{{AdminTaxRulesSection.ruleName}}" userInput="SampleRule"/> - - <!-- Add NY and CA tax rules --> - <actionGroup ref="AddNewTaxRateNoZipActionGroup" stepKey="addNYTaxRate"> - <argument name="taxCode" value="SimpleTaxNY"/> - </actionGroup> - - <actionGroup ref="AddNewTaxRateNoZipActionGroup" stepKey="addCATaxRate"> - <argument name="taxCode" value="SimpleTaxCA"/> - </actionGroup> - - <click stepKey="clickSave" selector="{{AdminStoresMainActionsSection.saveButton}}"/> - - <!-- Fill out form for a new user with address --> - <actionGroup ref="SignUpNewUserFromStorefrontActionGroup" stepKey="SignUpNewUser"> - <argument name="Customer" value="Simple_US_Customer_NY"/> - </actionGroup> - - <actionGroup ref="EnterCustomerAddressInfo" stepKey="enterAddressInfo"> - <argument name="Address" value="US_Address_NY"/> - </actionGroup> - </before> - <after> - <!-- Go to the tax rule page and delete the row we created--> - <amOnPage url="{{AdminTaxRuleGridPage.url}}" stepKey="goToTaxRulesPage"/> - <waitForPageLoad stepKey="waitForRulesPage"/> - <actionGroup ref="deleteEntitySecondaryGrid" stepKey="deleteRule"> - <argument name="name" value="SampleRule"/> - <argument name="searchInput" value="{{AdminSecondaryGridSection.taxIdentifierSearch}}"/> - </actionGroup> - - <!-- Go to the tax rate page --> - <amOnPage url="{{AdminTaxRateGridPage.url}}" stepKey="goToTaxRatesPage"/> - <waitForPageLoad stepKey="waitForRatesPage"/> - - <!-- Delete the two tax rates that were created --> - <actionGroup ref="deleteEntitySecondaryGrid" stepKey="deleteNYRate"> - <argument name="name" value="{{SimpleTaxNY.state}}-{{SimpleTaxNY.rate}}"/> - <argument name="searchInput" value="{{AdminSecondaryGridSection.taxIdentifierSearch}}"/> - </actionGroup> - - <actionGroup ref="deleteEntitySecondaryGrid" stepKey="deleteCARate"> - <argument name="name" value="{{SimpleTaxCA.state}}-{{SimpleTaxCA.rate}}"/> - <argument name="searchInput" value="{{AdminSecondaryGridSection.taxIdentifierSearch}}"/> - </actionGroup> - - <!-- Ensure tax won't be shown in the cart --> - <actionGroup ref="ChangeToDefaultTaxConfigurationUIActionGroup" stepKey="changeToDefaultTaxConfiguration"/> - - <amOnPage url="{{AdminLogoutPage.url}}" stepKey="amOnLogoutPage"/> - <deleteData createDataKey="virtualProduct1" stepKey="deleteVirtualProduct1"/> - </after> - - <!-- Go to the created product page and add it to the cart --> - <amOnPage url="$$virtualProduct1.sku$$.html" stepKey="goToVirtualProductPage"/> - <waitForPageLoad stepKey="waitForVirtualProductPage"/> - <click stepKey="addVirtualProductToCart" selector="{{StorefrontProductActionSection.addToCart}}"/> - <waitForPageLoad stepKey="waitForProductAdded"/> - <see stepKey="seeSuccess" selector="{{AdminCategoryMessagesSection.SuccessMessage}}" userInput="You added"/> - - <!-- Assert that taxes are applied correctly for NY --> - <amOnPage url="{{CheckoutCartPage.url}}" stepKey="goToCheckout"/> - <waitForPageLoad stepKey="waitForCart"/> - <waitForLoadingMaskToDisappear stepKey="waitForLoadingMask"/> - - <waitForElementVisible stepKey="waitForOverviewVisible" selector="{{CheckoutPaymentSection.tax}}"/> - <see stepKey="seeTax" selector="{{CheckoutPaymentSection.tax}}" userInput="$8.37"/> - <click stepKey="expandTax" selector="{{CheckoutPaymentSection.tax}}"/> - <see stepKey="seeTaxPercent" selector="{{CheckoutPaymentSection.taxPercentage}}" userInput="({{SimpleTaxNY.rate}}%)"/> - <see stepKey="seeTotalIncl" selector="{{CheckoutPaymentSection.orderSummaryTotalIncluding}}" userInput="$108.36"/> - <see stepKey="seeTotalExcl" selector="{{CheckoutPaymentSection.orderSummaryTotalExcluding}}" userInput="$$virtualProduct1.price$$"/> - - <!-- Change the address --> - <actionGroup ref="ChangeSummaryQuoteAddressActionGroup" stepKey="changeAddress"> - <argument name="taxCode" value="SimpleTaxSwiss"/> - </actionGroup> - - <!-- Assert that taxes are applied correctly for Switzerland --> - <see stepKey="seeTax2" selector="{{CheckoutPaymentSection.tax}}" userInput="$0.00"/> - <see stepKey="seeTotalIncl2" selector="{{CheckoutPaymentSection.orderSummaryTotalIncluding}}" userInput="$$$virtualProduct1.price$$"/> - <see stepKey="seeTotalExcl2" selector="{{CheckoutPaymentSection.orderSummaryTotalExcluding}}" userInput="$$$virtualProduct1.price$$"/> - - <!-- Change the address --> - <actionGroup ref="ChangeSummaryQuoteAddressActionGroup" stepKey="changeAddress2"> - <argument name="taxCode" value="SimpleTaxCA"/> - </actionGroup> - - <!-- Assert that taxes are applied correctly for CA --> - <see stepKey="seeTax3" selector="{{CheckoutPaymentSection.tax}}" userInput="$8.25"/> - <click stepKey="expandTax2" selector="{{CheckoutPaymentSection.tax}}"/> - <see stepKey="seeTaxPercent2" selector="{{CheckoutPaymentSection.taxPercentage}}" userInput="({{SimpleTaxCA.rate}}%)"/> - <see stepKey="seeTotalIncl3" selector="{{CheckoutPaymentSection.orderSummaryTotalIncluding}}" userInput="$108.24"/> - <see stepKey="seeTotalExcl3" selector="{{CheckoutPaymentSection.orderSummaryTotalExcluding}}" userInput="$$virtualProduct1.price$$"/> - </test> - - <test name="StorefrontTaxQuoteCartGuestSimple"> - <annotations> - <features value="Tax"/> - <stories value="Tax Calculation in Shopping Cart"/> - <title value="Tax for Simple Product Quote should be recalculated in Shopping Cart for Guest Customer"/> - <description value="Tax for Simple Product Quote should be recalculated in Shopping Cart for Guest Customer#anch"/> - <severity value="CRITICAL"/> - <testCaseId value="MC-297"/> - <group value="Tax"/> - </annotations> - <before> - <actionGroup ref="LoginAsAdmin" stepKey="loginAsAdmin"/> - <createData entity="SimpleProduct2" stepKey="simpleProduct1"/> - - <!-- Fill in rules to display tax in the cart --> - <actionGroup ref="EditTaxConfigurationByUIActionGroup" stepKey="fillDefaultTaxForms"/> - - <!-- Go to tax rule page --> - <amOnPage url="{{AdminTaxRuleGridPage.url}}" stepKey="goToTaxRulePage"/> - <waitForPageLoad stepKey="waitForTaxRatePage"/> - <click stepKey="addNewTaxRate" selector="{{AdminGridMainControls.add}}"/> - <fillField stepKey="fillRuleName" selector="{{AdminTaxRulesSection.ruleName}}" userInput="SampleRule"/> - - <!-- Add NY and CA tax rules --> - <actionGroup ref="AddNewTaxRateNoZipActionGroup" stepKey="addNYTaxRate"> - <argument name="taxCode" value="SimpleTaxNY"/> - </actionGroup> - - <actionGroup ref="AddNewTaxRateNoZipActionGroup" stepKey="addCATaxRate"> - <argument name="taxCode" value="SimpleTaxCA"/> - </actionGroup> - - <click stepKey="clickSave" selector="{{AdminStoresMainActionsSection.saveButton}}"/> - </before> - <after> - <!-- Go to the tax rule page and delete the row we created--> - <amOnPage url="{{AdminTaxRuleGridPage.url}}" stepKey="goToTaxRulesPage"/> - <waitForPageLoad stepKey="waitForRulesPage"/> - <actionGroup ref="deleteEntitySecondaryGrid" stepKey="deleteRule"> - <argument name="name" value="SampleRule"/> - <argument name="searchInput" value="{{AdminSecondaryGridSection.taxIdentifierSearch}}"/> - </actionGroup> - - <!-- Go to the tax rate page --> - <amOnPage url="{{AdminTaxRateGridPage.url}}" stepKey="goToTaxRatesPage"/> - <waitForPageLoad stepKey="waitForRatesPage"/> - - <!-- Delete the two tax rates that were created --> - <actionGroup ref="deleteEntitySecondaryGrid" stepKey="deleteNYRate"> - <argument name="name" value="{{SimpleTaxNY.state}}-{{SimpleTaxNY.rate}}"/> - <argument name="searchInput" value="{{AdminSecondaryGridSection.taxIdentifierSearch}}"/> - </actionGroup> - - <actionGroup ref="deleteEntitySecondaryGrid" stepKey="deleteCARate"> - <argument name="name" value="{{SimpleTaxCA.state}}-{{SimpleTaxCA.rate}}"/> - <argument name="searchInput" value="{{AdminSecondaryGridSection.taxIdentifierSearch}}"/> - </actionGroup> - - <!-- Ensure tax won't be shown in the cart --> - <actionGroup ref="ChangeToDefaultTaxConfigurationUIActionGroup" stepKey="changeToDefaultTaxConfiguration"/> - - <amOnPage url="{{AdminLogoutPage.url}}" stepKey="amOnLogoutPage"/> - <deleteData createDataKey="simpleProduct1" stepKey="deleteSimpleProduct1"/> - </after> - - <!-- Go to the created product page and add it to the cart --> - <amOnPage url="$$simpleProduct1.sku$$.html" stepKey="goToSimpleProductPage"/> - <waitForPageLoad stepKey="waitForSimpleProductPage"/> - <click stepKey="addSimpleProductToCart" selector="{{StorefrontProductActionSection.addToCart}}"/> - <waitForPageLoad stepKey="waitForProductAdded"/> - <see stepKey="seeSuccess" selector="{{AdminCategoryMessagesSection.SuccessMessage}}" userInput="You added"/> - - <!-- Assert that taxes are applied correctly for CA --> - <amOnPage url="{{CheckoutCartPage.url}}" stepKey="goToCheckout"/> - <waitForPageLoad stepKey="waitForCart"/> - <waitForLoadingMaskToDisappear stepKey="waitForLoadingMask"/> - - <waitForElementVisible stepKey="waitForOverviewVisible" selector="{{CheckoutPaymentSection.tax}}"/> - <conditionalClick selector="{{CheckoutCartSummarySection.estimateShippingAndTax}}" dependentSelector="{{CheckoutCartSummarySection.country}}" visible="false" stepKey="expandEstimateShippingandTax" /> - <selectOption selector="{{CheckoutCartSummarySection.country}}" userInput="United States" stepKey="selectUSCountry"/> - <selectOption selector="{{CheckoutCartSummarySection.stateProvince}}" userInput="California" stepKey="selectCaliforniaRegion"/> - <see stepKey="seeTax3" selector="{{CheckoutPaymentSection.tax}}" userInput="$10.15"/> - <click stepKey="expandTax2" selector="{{CheckoutPaymentSection.tax}}"/> - <see stepKey="seeTaxPercent2" selector="{{CheckoutPaymentSection.taxPercentage}}" userInput="({{SimpleTaxCA.rate}}%)"/> - <see stepKey="seeTotalIncl3" selector="{{CheckoutPaymentSection.orderSummaryTotalIncluding}}" userInput="$138.15"/> - <see stepKey="seeTotalExcl3" selector="{{CheckoutPaymentSection.orderSummaryTotalExcluding}}" userInput="$128.00"/> - - <!-- Change the address --> - <actionGroup ref="ChangeSummaryQuoteAddressActionGroup" stepKey="changeAddress"> - <argument name="taxCode" value="SimpleTaxSwiss"/> - </actionGroup> - - <!-- Assert that taxes are applied correctly for Switzerland --> - <see stepKey="seeTax2" selector="{{CheckoutPaymentSection.tax}}" userInput="$0.00"/> - <see stepKey="seeTotalIncl2" selector="{{CheckoutPaymentSection.orderSummaryTotalIncluding}}" userInput="$128.00"/> - <see stepKey="seeTotalExcl2" selector="{{CheckoutPaymentSection.orderSummaryTotalExcluding}}" userInput="$128.00"/> - - <!-- Change the address --> - <actionGroup ref="ChangeSummaryQuoteAddressActionGroup" stepKey="changeAddress2"> - <argument name="taxCode" value="SimpleTaxNY"/> - </actionGroup> - - <!-- Assert that taxes are applied correctly for NY --> - <see stepKey="seeTax" selector="{{CheckoutPaymentSection.tax}}" userInput="$10.30"/> - <click stepKey="expandTax" selector="{{CheckoutPaymentSection.tax}}"/> - <see stepKey="seeTaxPercent" selector="{{CheckoutPaymentSection.taxPercentage}}" userInput="({{SimpleTaxNY.rate}}%)"/> - <see stepKey="seeTotalIncl" selector="{{CheckoutPaymentSection.orderSummaryTotalIncluding}}" userInput="$138.30"/> - <see stepKey="seeTotalExcl" selector="{{CheckoutPaymentSection.orderSummaryTotalExcluding}}" userInput="$128.00"/> - </test> - - <test name="StorefrontTaxQuoteCartGuestVirtual"> - <annotations> - <features value="Tax"/> - <stories value="Tax Calculation in Shopping Cart"/> - <title value="Tax for Virtual Product Quote should be recalculated in Shopping Cart for Guest Customer"/> - <description value="Tax for Virtual Product Quote should be recalculated in Shopping Cart for Guest Customer"/> - <severity value="CRITICAL"/> - <testCaseId value="MC-298"/> - <group value="Tax"/> - </annotations> - <before> - <actionGroup ref="LoginAsAdmin" stepKey="loginAsAdmin"/> - <createData entity="VirtualProduct" stepKey="virtualProduct1"/> - - <!-- Fill in rules to display tax in the cart --> - <actionGroup ref="EditTaxConfigurationByUIActionGroup" stepKey="fillDefaultTaxForms"/> - - <!-- Go to tax rule page --> - <amOnPage url="{{AdminTaxRuleGridPage.url}}" stepKey="goToTaxRulePage"/> - <waitForPageLoad stepKey="waitForTaxRatePage"/> - <click stepKey="addNewTaxRate" selector="{{AdminGridMainControls.add}}"/> - <fillField stepKey="fillRuleName" selector="{{AdminTaxRulesSection.ruleName}}" userInput="SampleRule"/> - - <!-- Add NY and CA tax rules --> - <actionGroup ref="AddNewTaxRateNoZipActionGroup" stepKey="addNYTaxRate"> - <argument name="taxCode" value="SimpleTaxNY"/> - </actionGroup> - - <actionGroup ref="AddNewTaxRateNoZipActionGroup" stepKey="addCATaxRate"> - <argument name="taxCode" value="SimpleTaxCA"/> - </actionGroup> - - <click stepKey="clickSave" selector="{{AdminStoresMainActionsSection.saveButton}}"/> - </before> - <after> - <!-- Go to the tax rule page and delete the row we created--> - <amOnPage url="{{AdminTaxRuleGridPage.url}}" stepKey="goToTaxRulesPage"/> - <waitForPageLoad stepKey="waitForRulesPage"/> - <actionGroup ref="deleteEntitySecondaryGrid" stepKey="deleteRule"> - <argument name="name" value="SampleRule"/> - <argument name="searchInput" value="{{AdminSecondaryGridSection.taxIdentifierSearch}}"/> - </actionGroup> - - <!-- Go to the tax rate page --> - <amOnPage url="{{AdminTaxRateGridPage.url}}" stepKey="goToTaxRatesPage"/> - <waitForPageLoad stepKey="waitForRatesPage"/> - - <!-- Delete the two tax rates that were created --> - <actionGroup ref="deleteEntitySecondaryGrid" stepKey="deleteNYRate"> - <argument name="name" value="{{SimpleTaxNY.state}}-{{SimpleTaxNY.rate}}"/> - <argument name="searchInput" value="{{AdminSecondaryGridSection.taxIdentifierSearch}}"/> - </actionGroup> - - <actionGroup ref="deleteEntitySecondaryGrid" stepKey="deleteCARate"> - <argument name="name" value="{{SimpleTaxCA.state}}-{{SimpleTaxCA.rate}}"/> - <argument name="searchInput" value="{{AdminSecondaryGridSection.taxIdentifierSearch}}"/> - </actionGroup> - - <!-- Ensure tax won't be shown in the cart --> - <actionGroup ref="ChangeToDefaultTaxConfigurationUIActionGroup" stepKey="changeToDefaultTaxConfiguration"/> - - <amOnPage url="{{AdminLogoutPage.url}}" stepKey="amOnLogoutPage"/> - <deleteData createDataKey="virtualProduct1" stepKey="deleteVirtualProduct1"/> - </after> - - <!-- Go to the created product page and add it to the cart --> - <amOnPage url="$$virtualProduct1.sku$$.html" stepKey="goToVirtualProductPage"/> - <waitForPageLoad stepKey="waitForVirtualProductPage"/> - <click stepKey="addVirtualProductToCart" selector="{{StorefrontProductActionSection.addToCart}}"/> - <waitForPageLoad stepKey="waitForProductAdded"/> - <see stepKey="seeSuccess" selector="{{AdminCategoryMessagesSection.SuccessMessage}}" userInput="You added"/> - - <!-- Assert that taxes are applied correctly for NY --> - <amOnPage url="{{CheckoutCartPage.url}}" stepKey="goToCheckout"/> - <waitForPageLoad stepKey="waitForCart"/> - <waitForLoadingMaskToDisappear stepKey="waitForLoadingMask"/> - - <!-- Assert that taxes are applied correctly for CA --> - <waitForElementVisible stepKey="waitForOverviewVisible" selector="{{CheckoutPaymentSection.tax}}"/> - <conditionalClick selector="{{CheckoutCartSummarySection.estimateShippingAndTax}}" dependentSelector="{{CheckoutCartSummarySection.country}}" visible="false" stepKey="expandEstimateShippingandTax" /> - <selectOption selector="{{CheckoutCartSummarySection.country}}" userInput="United States" stepKey="selectUSCountry"/> - <selectOption selector="{{CheckoutCartSummarySection.stateProvince}}" userInput="California" stepKey="selectCaliforniaRegion"/> - <see stepKey="seeTax3" selector="{{CheckoutPaymentSection.tax}}" userInput="$8.25"/> - <click stepKey="expandTax2" selector="{{CheckoutPaymentSection.tax}}"/> - <see stepKey="seeTaxPercent2" selector="{{CheckoutPaymentSection.taxPercentage}}" userInput="({{SimpleTaxCA.rate}}%)"/> - <see stepKey="seeTotalIncl3" selector="{{CheckoutPaymentSection.orderSummaryTotalIncluding}}" userInput="$108.24"/> - <see stepKey="seeTotalExcl3" selector="{{CheckoutPaymentSection.orderSummaryTotalExcluding}}" userInput="$$virtualProduct1.price$$"/> - - <!-- Change the address --> - <actionGroup ref="ChangeSummaryQuoteAddressActionGroup" stepKey="changeAddress"> - <argument name="taxCode" value="SimpleTaxSwiss"/> - </actionGroup> - - <!-- Assert that taxes are applied correctly for Switzerland --> - <see stepKey="seeTax2" selector="{{CheckoutPaymentSection.tax}}" userInput="$0.00"/> - <see stepKey="seeTotalIncl2" selector="{{CheckoutPaymentSection.orderSummaryTotalIncluding}}" userInput="$$$virtualProduct1.price$$"/> - <see stepKey="seeTotalExcl2" selector="{{CheckoutPaymentSection.orderSummaryTotalExcluding}}" userInput="$$$virtualProduct1.price$$"/> - - <!-- Change the address --> - <actionGroup ref="ChangeSummaryQuoteAddressActionGroup" stepKey="changeAddress2"> - <argument name="taxCode" value="SimpleTaxNY"/> - </actionGroup> - - <!-- Assert that taxes are applied correctly for NY --> - <see stepKey="seeTax" selector="{{CheckoutPaymentSection.tax}}" userInput="$8.37"/> - <click stepKey="expandTax" selector="{{CheckoutPaymentSection.tax}}"/> - <see stepKey="seeTaxPercent" selector="{{CheckoutPaymentSection.taxPercentage}}" userInput="({{SimpleTaxNY.rate}}%)"/> - <see stepKey="seeTotalIncl" selector="{{CheckoutPaymentSection.orderSummaryTotalIncluding}}" userInput="$108.36"/> - <see stepKey="seeTotalExcl" selector="{{CheckoutPaymentSection.orderSummaryTotalExcluding}}" userInput="$$virtualProduct1.price$$"/> - </test> -</tests> diff --git a/app/code/Magento/Tax/Test/Mftf/Test/StorefrontTaxQuoteCartTest/StorefrontTaxQuoteCartGuestSimpleTest.xml b/app/code/Magento/Tax/Test/Mftf/Test/StorefrontTaxQuoteCartTest/StorefrontTaxQuoteCartGuestSimpleTest.xml new file mode 100644 index 0000000000000..959aa323308be --- /dev/null +++ b/app/code/Magento/Tax/Test/Mftf/Test/StorefrontTaxQuoteCartTest/StorefrontTaxQuoteCartGuestSimpleTest.xml @@ -0,0 +1,121 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + /** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +--> +<tests xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/testSchema.xsd"> + <test name="StorefrontTaxQuoteCartGuestSimpleTest"> + <annotations> + <features value="Tax"/> + <stories value="Tax Calculation in Shopping Cart"/> + <title value="Tax for Simple Product Quote should be recalculated in Shopping Cart for Guest Customer"/> + <description value="Tax for Simple Product Quote should be recalculated in Shopping Cart for Guest Customer#anch"/> + <severity value="CRITICAL"/> + <testCaseId value="MC-297"/> + <group value="Tax"/> + </annotations> + <before> + <actionGroup ref="AdminLoginActionGroup" stepKey="loginAsAdmin"/> + <createData entity="SimpleProduct2" stepKey="simpleProduct1"/> + + <!-- Fill in rules to display tax in the cart --> + <actionGroup ref="EditTaxConfigurationByUIActionGroup" stepKey="fillDefaultTaxForms"/> + + <!-- Go to tax rule page --> + <amOnPage url="{{AdminTaxRuleGridPage.url}}" stepKey="goToTaxRulePage"/> + <waitForPageLoad stepKey="waitForTaxRatePage"/> + <click stepKey="addNewTaxRate" selector="{{AdminGridMainControls.add}}"/> + <fillField stepKey="fillRuleName" selector="{{AdminTaxRulesSection.ruleName}}" userInput="SampleRule"/> + + <!-- Add NY and CA tax rules --> + <actionGroup ref="AddNewTaxRateNoZipActionGroup" stepKey="addNYTaxRate"> + <argument name="taxCode" value="SimpleTaxNY"/> + </actionGroup> + + <actionGroup ref="AddNewTaxRateNoZipActionGroup" stepKey="addCATaxRate"> + <argument name="taxCode" value="SimpleTaxCA"/> + </actionGroup> + + <click stepKey="clickSave" selector="{{AdminStoresMainActionsSection.saveButton}}"/> + + <magentoCLI command="cron:run --group=index" stepKey="runCronIndexer"/> + </before> + <after> + <!-- Go to the tax rule page and delete the row we created--> + <amOnPage url="{{AdminTaxRuleGridPage.url}}" stepKey="goToTaxRulesPage"/> + <waitForPageLoad stepKey="waitForRulesPage"/> + <actionGroup ref="deleteEntitySecondaryGrid" stepKey="deleteRule"> + <argument name="name" value="SampleRule"/> + <argument name="searchInput" value="{{AdminSecondaryGridSection.taxIdentifierSearch}}"/> + </actionGroup> + + <!-- Go to the tax rate page --> + <amOnPage url="{{AdminTaxRateGridPage.url}}" stepKey="goToTaxRatesPage"/> + <waitForPageLoad stepKey="waitForRatesPage"/> + + <!-- Delete the two tax rates that were created --> + <actionGroup ref="deleteEntitySecondaryGrid" stepKey="deleteNYRate"> + <argument name="name" value="{{SimpleTaxNY.state}}-{{SimpleTaxNY.rate}}"/> + <argument name="searchInput" value="{{AdminSecondaryGridSection.taxIdentifierSearch}}"/> + </actionGroup> + + <actionGroup ref="deleteEntitySecondaryGrid" stepKey="deleteCARate"> + <argument name="name" value="{{SimpleTaxCA.state}}-{{SimpleTaxCA.rate}}"/> + <argument name="searchInput" value="{{AdminSecondaryGridSection.taxIdentifierSearch}}"/> + </actionGroup> + + <!-- Ensure tax won't be shown in the cart --> + <actionGroup ref="ChangeToDefaultTaxConfigurationUIActionGroup" stepKey="changeToDefaultTaxConfiguration"/> + + <amOnPage url="{{AdminLogoutPage.url}}" stepKey="amOnLogoutPage"/> + <deleteData createDataKey="simpleProduct1" stepKey="deleteSimpleProduct1"/> + </after> + + <!-- Go to the created product page and add it to the cart --> + <amOnPage url="$$simpleProduct1.sku$$.html" stepKey="goToSimpleProductPage"/> + <waitForPageLoad stepKey="waitForSimpleProductPage"/> + <click stepKey="addSimpleProductToCart" selector="{{StorefrontProductActionSection.addToCart}}"/> + <waitForPageLoad stepKey="waitForProductAdded"/> + <see stepKey="seeSuccess" selector="{{AdminCategoryMessagesSection.SuccessMessage}}" userInput="You added"/> + + <!-- Assert that taxes are applied correctly for CA --> + <amOnPage url="{{CheckoutCartPage.url}}" stepKey="goToCheckout"/> + <waitForPageLoad stepKey="waitForCart"/> + <waitForLoadingMaskToDisappear stepKey="waitForLoadingMask"/> + + <waitForElementVisible stepKey="waitForOverviewVisible" selector="{{CheckoutPaymentSection.tax}}"/> + <conditionalClick selector="{{CheckoutCartSummarySection.estimateShippingAndTax}}" dependentSelector="{{CheckoutCartSummarySection.country}}" visible="false" stepKey="expandEstimateShippingandTax"/> + <selectOption selector="{{CheckoutCartSummarySection.country}}" userInput="United States" stepKey="selectUSCountry"/> + <selectOption selector="{{CheckoutCartSummarySection.stateProvince}}" userInput="California" stepKey="selectCaliforniaRegion"/> + <see stepKey="seeTax3" selector="{{CheckoutPaymentSection.tax}}" userInput="$10.15"/> + <click stepKey="expandTax2" selector="{{CheckoutPaymentSection.tax}}"/> + <see stepKey="seeTaxPercent2" selector="{{CheckoutPaymentSection.taxPercentage}}" userInput="({{SimpleTaxCA.rate}}%)"/> + <see stepKey="seeTotalIncl3" selector="{{CheckoutPaymentSection.orderSummaryTotalIncluding}}" userInput="$138.15"/> + <see stepKey="seeTotalExcl3" selector="{{CheckoutPaymentSection.orderSummaryTotalExcluding}}" userInput="$128.00"/> + + <!-- Change the address --> + <actionGroup ref="ChangeSummaryQuoteAddressActionGroup" stepKey="changeAddress"> + <argument name="taxCode" value="SimpleTaxSwiss"/> + </actionGroup> + + <!-- Assert that taxes are applied correctly for Switzerland --> + <see stepKey="seeTax2" selector="{{CheckoutPaymentSection.tax}}" userInput="$0.00"/> + <see stepKey="seeTotalIncl2" selector="{{CheckoutPaymentSection.orderSummaryTotalIncluding}}" userInput="$128.00"/> + <see stepKey="seeTotalExcl2" selector="{{CheckoutPaymentSection.orderSummaryTotalExcluding}}" userInput="$128.00"/> + + <!-- Change the address --> + <actionGroup ref="ChangeSummaryQuoteAddressActionGroup" stepKey="changeAddress2"> + <argument name="taxCode" value="SimpleTaxNY"/> + </actionGroup> + + <!-- Assert that taxes are applied correctly for NY --> + <see stepKey="seeTax" selector="{{CheckoutPaymentSection.tax}}" userInput="$10.30"/> + <click stepKey="expandTax" selector="{{CheckoutPaymentSection.tax}}"/> + <see stepKey="seeTaxPercent" selector="{{CheckoutPaymentSection.taxPercentage}}" userInput="({{SimpleTaxNY.rate}}%)"/> + <see stepKey="seeTotalIncl" selector="{{CheckoutPaymentSection.orderSummaryTotalIncluding}}" userInput="$138.30"/> + <see stepKey="seeTotalExcl" selector="{{CheckoutPaymentSection.orderSummaryTotalExcluding}}" userInput="$128.00"/> + </test> +</tests> diff --git a/app/code/Magento/Tax/Test/Mftf/Test/StorefrontTaxQuoteCartTest/StorefrontTaxQuoteCartGuestVirtualTest.xml b/app/code/Magento/Tax/Test/Mftf/Test/StorefrontTaxQuoteCartTest/StorefrontTaxQuoteCartGuestVirtualTest.xml new file mode 100644 index 0000000000000..c1268638c7104 --- /dev/null +++ b/app/code/Magento/Tax/Test/Mftf/Test/StorefrontTaxQuoteCartTest/StorefrontTaxQuoteCartGuestVirtualTest.xml @@ -0,0 +1,122 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + /** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +--> +<tests xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/testSchema.xsd"> + <test name="StorefrontTaxQuoteCartGuestVirtualTest"> + <annotations> + <features value="Tax"/> + <stories value="Tax Calculation in Shopping Cart"/> + <title value="Tax for Virtual Product Quote should be recalculated in Shopping Cart for Guest Customer"/> + <description value="Tax for Virtual Product Quote should be recalculated in Shopping Cart for Guest Customer"/> + <severity value="CRITICAL"/> + <testCaseId value="MC-298"/> + <group value="Tax"/> + </annotations> + <before> + <actionGroup ref="AdminLoginActionGroup" stepKey="loginAsAdmin"/> + <createData entity="VirtualProduct" stepKey="virtualProduct1"/> + + <!-- Fill in rules to display tax in the cart --> + <actionGroup ref="EditTaxConfigurationByUIActionGroup" stepKey="fillDefaultTaxForms"/> + + <!-- Go to tax rule page --> + <amOnPage url="{{AdminTaxRuleGridPage.url}}" stepKey="goToTaxRulePage"/> + <waitForPageLoad stepKey="waitForTaxRatePage"/> + <click stepKey="addNewTaxRate" selector="{{AdminGridMainControls.add}}"/> + <fillField stepKey="fillRuleName" selector="{{AdminTaxRulesSection.ruleName}}" userInput="SampleRule"/> + + <!-- Add NY and CA tax rules --> + <actionGroup ref="AddNewTaxRateNoZipActionGroup" stepKey="addNYTaxRate"> + <argument name="taxCode" value="SimpleTaxNY"/> + </actionGroup> + + <actionGroup ref="AddNewTaxRateNoZipActionGroup" stepKey="addCATaxRate"> + <argument name="taxCode" value="SimpleTaxCA"/> + </actionGroup> + + <click stepKey="clickSave" selector="{{AdminStoresMainActionsSection.saveButton}}"/> + + <magentoCLI command="cron:run --group=index" stepKey="runCronIndexer"/> + </before> + <after> + <!-- Go to the tax rule page and delete the row we created--> + <amOnPage url="{{AdminTaxRuleGridPage.url}}" stepKey="goToTaxRulesPage"/> + <waitForPageLoad stepKey="waitForRulesPage"/> + <actionGroup ref="deleteEntitySecondaryGrid" stepKey="deleteRule"> + <argument name="name" value="SampleRule"/> + <argument name="searchInput" value="{{AdminSecondaryGridSection.taxIdentifierSearch}}"/> + </actionGroup> + + <!-- Go to the tax rate page --> + <amOnPage url="{{AdminTaxRateGridPage.url}}" stepKey="goToTaxRatesPage"/> + <waitForPageLoad stepKey="waitForRatesPage"/> + + <!-- Delete the two tax rates that were created --> + <actionGroup ref="deleteEntitySecondaryGrid" stepKey="deleteNYRate"> + <argument name="name" value="{{SimpleTaxNY.state}}-{{SimpleTaxNY.rate}}"/> + <argument name="searchInput" value="{{AdminSecondaryGridSection.taxIdentifierSearch}}"/> + </actionGroup> + + <actionGroup ref="deleteEntitySecondaryGrid" stepKey="deleteCARate"> + <argument name="name" value="{{SimpleTaxCA.state}}-{{SimpleTaxCA.rate}}"/> + <argument name="searchInput" value="{{AdminSecondaryGridSection.taxIdentifierSearch}}"/> + </actionGroup> + + <!-- Ensure tax won't be shown in the cart --> + <actionGroup ref="ChangeToDefaultTaxConfigurationUIActionGroup" stepKey="changeToDefaultTaxConfiguration"/> + + <amOnPage url="{{AdminLogoutPage.url}}" stepKey="amOnLogoutPage"/> + <deleteData createDataKey="virtualProduct1" stepKey="deleteVirtualProduct1"/> + </after> + + <!-- Go to the created product page and add it to the cart --> + <amOnPage url="$$virtualProduct1.sku$$.html" stepKey="goToVirtualProductPage"/> + <waitForPageLoad stepKey="waitForVirtualProductPage"/> + <click stepKey="addVirtualProductToCart" selector="{{StorefrontProductActionSection.addToCart}}"/> + <waitForPageLoad stepKey="waitForProductAdded"/> + <see stepKey="seeSuccess" selector="{{AdminCategoryMessagesSection.SuccessMessage}}" userInput="You added"/> + + <!-- Assert that taxes are applied correctly for NY --> + <amOnPage url="{{CheckoutCartPage.url}}" stepKey="goToCheckout"/> + <waitForPageLoad stepKey="waitForCart"/> + <waitForLoadingMaskToDisappear stepKey="waitForLoadingMask"/> + + <!-- Assert that taxes are applied correctly for CA --> + <waitForElementVisible stepKey="waitForOverviewVisible" selector="{{CheckoutPaymentSection.tax}}"/> + <conditionalClick selector="{{CheckoutCartSummarySection.estimateShippingAndTax}}" dependentSelector="{{CheckoutCartSummarySection.country}}" visible="false" stepKey="expandEstimateShippingandTax"/> + <selectOption selector="{{CheckoutCartSummarySection.country}}" userInput="United States" stepKey="selectUSCountry"/> + <selectOption selector="{{CheckoutCartSummarySection.stateProvince}}" userInput="California" stepKey="selectCaliforniaRegion"/> + <see stepKey="seeTax3" selector="{{CheckoutPaymentSection.tax}}" userInput="$8.25"/> + <click stepKey="expandTax2" selector="{{CheckoutPaymentSection.tax}}"/> + <see stepKey="seeTaxPercent2" selector="{{CheckoutPaymentSection.taxPercentage}}" userInput="({{SimpleTaxCA.rate}}%)"/> + <see stepKey="seeTotalIncl3" selector="{{CheckoutPaymentSection.orderSummaryTotalIncluding}}" userInput="$108.24"/> + <see stepKey="seeTotalExcl3" selector="{{CheckoutPaymentSection.orderSummaryTotalExcluding}}" userInput="$$virtualProduct1.price$$"/> + + <!-- Change the address --> + <actionGroup ref="ChangeSummaryQuoteAddressActionGroup" stepKey="changeAddress"> + <argument name="taxCode" value="SimpleTaxSwiss"/> + </actionGroup> + + <!-- Assert that taxes are applied correctly for Switzerland --> + <see stepKey="seeTax2" selector="{{CheckoutPaymentSection.tax}}" userInput="$0.00"/> + <see stepKey="seeTotalIncl2" selector="{{CheckoutPaymentSection.orderSummaryTotalIncluding}}" userInput="$$$virtualProduct1.price$$"/> + <see stepKey="seeTotalExcl2" selector="{{CheckoutPaymentSection.orderSummaryTotalExcluding}}" userInput="$$$virtualProduct1.price$$"/> + + <!-- Change the address --> + <actionGroup ref="ChangeSummaryQuoteAddressActionGroup" stepKey="changeAddress2"> + <argument name="taxCode" value="SimpleTaxNY"/> + </actionGroup> + + <!-- Assert that taxes are applied correctly for NY --> + <see stepKey="seeTax" selector="{{CheckoutPaymentSection.tax}}" userInput="$8.37"/> + <click stepKey="expandTax" selector="{{CheckoutPaymentSection.tax}}"/> + <see stepKey="seeTaxPercent" selector="{{CheckoutPaymentSection.taxPercentage}}" userInput="({{SimpleTaxNY.rate}}%)"/> + <see stepKey="seeTotalIncl" selector="{{CheckoutPaymentSection.orderSummaryTotalIncluding}}" userInput="$108.36"/> + <see stepKey="seeTotalExcl" selector="{{CheckoutPaymentSection.orderSummaryTotalExcluding}}" userInput="$$virtualProduct1.price$$"/> + </test> +</tests> diff --git a/app/code/Magento/Tax/Test/Mftf/Test/StorefrontTaxQuoteCartTest/StorefrontTaxQuoteCartLoggedInSimpleTest.xml b/app/code/Magento/Tax/Test/Mftf/Test/StorefrontTaxQuoteCartTest/StorefrontTaxQuoteCartLoggedInSimpleTest.xml new file mode 100644 index 0000000000000..e43511318f137 --- /dev/null +++ b/app/code/Magento/Tax/Test/Mftf/Test/StorefrontTaxQuoteCartTest/StorefrontTaxQuoteCartLoggedInSimpleTest.xml @@ -0,0 +1,133 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + /** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +--> +<tests xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/testSchema.xsd"> + <test name="StorefrontTaxQuoteCartLoggedInSimpleTest"> + <annotations> + <features value="Tax"/> + <stories value="Tax Calculation in Shopping Cart"/> + <title value="Tax for Simple Product Quote should be recalculated in Shopping Cart for Logged in Customer with Default Address"/> + <description value="Tax for Simple Product Quote should be recalculated in Shopping Cart for Logged in Customer with Default Address"/> + <severity value="CRITICAL"/> + <testCaseId value="MC-295"/> + <group value="Tax"/> + </annotations> + <before> + <actionGroup ref="AdminLoginActionGroup" stepKey="loginAsAdmin"/> + <createData entity="SimpleProduct2" stepKey="simpleProduct1"/> + + <!-- Fill in rules to display tax in the cart --> + <actionGroup ref="EditTaxConfigurationByUIActionGroup" stepKey="fillDefaultTaxForms"/> + + <!-- Go to tax rule page --> + <amOnPage url="{{AdminTaxRuleGridPage.url}}" stepKey="goToTaxRulePage"/> + <waitForPageLoad stepKey="waitForTaxRatePage"/> + <click stepKey="addNewTaxRate" selector="{{AdminGridMainControls.add}}"/> + <fillField stepKey="fillRuleName" selector="{{AdminTaxRulesSection.ruleName}}" userInput="SampleRule"/> + + <!-- Add NY and CA tax rules --> + <actionGroup ref="AddNewTaxRateNoZipActionGroup" stepKey="addNYTaxRate"> + <argument name="taxCode" value="SimpleTaxNY"/> + </actionGroup> + + <actionGroup ref="AddNewTaxRateNoZipActionGroup" stepKey="addCATaxRate"> + <argument name="taxCode" value="SimpleTaxCA"/> + </actionGroup> + + <click stepKey="clickSave" selector="{{AdminStoresMainActionsSection.saveButton}}"/> + <magentoCLI command="cron:run --group=index" stepKey="runCronIndexer"/> + + <!-- Fill out form for a new user with address --> + <actionGroup ref="StorefrontOpenCustomerAccountCreatePageActionGroup" stepKey="openCreateAccountPage"/> + <actionGroup ref="StorefrontFillCustomerAccountCreationFormActionGroup" stepKey="fillCreateAccountForm"> + <argument name="customer" value="Simple_US_Customer_NY"/> + </actionGroup> + <actionGroup ref="StorefrontClickCreateAnAccountCustomerAccountCreationFormActionGroup" stepKey="submitCreateAccountForm"/> + <actionGroup ref="AssertMessageCustomerCreateAccountActionGroup" stepKey="seeSuccessMessage"> + <argument name="messageType" value="success"/> + <argument name="message" value="Thank you for registering with Main Website Store."/> + </actionGroup> + + <actionGroup ref="EnterCustomerAddressInfoActionGroup" stepKey="enterAddressInfo"> + <argument name="Address" value="US_Address_NY"/> + </actionGroup> + </before> + <after> + <!-- Go to the tax rule page and delete the row we created--> + <amOnPage url="{{AdminTaxRuleGridPage.url}}" stepKey="goToTaxRulesPage"/> + <waitForPageLoad stepKey="waitForRulesPage"/> + <actionGroup ref="deleteEntitySecondaryGrid" stepKey="deleteRule"> + <argument name="name" value="SampleRule"/> + <argument name="searchInput" value="{{AdminSecondaryGridSection.taxIdentifierSearch}}"/> + </actionGroup> + + <!-- Go to the tax rate page --> + <amOnPage url="{{AdminTaxRateGridPage.url}}" stepKey="goToTaxRatesPage"/> + <waitForPageLoad stepKey="waitForRatesPage"/> + + <!-- Delete the two tax rates that were created --> + <actionGroup ref="deleteEntitySecondaryGrid" stepKey="deleteNYRate"> + <argument name="name" value="{{SimpleTaxNY.state}}-{{SimpleTaxNY.rate}}"/> + <argument name="searchInput" value="{{AdminSecondaryGridSection.taxIdentifierSearch}}"/> + </actionGroup> + + <actionGroup ref="deleteEntitySecondaryGrid" stepKey="deleteCARate"> + <argument name="name" value="{{SimpleTaxCA.state}}-{{SimpleTaxCA.rate}}"/> + <argument name="searchInput" value="{{AdminSecondaryGridSection.taxIdentifierSearch}}"/> + </actionGroup> + + <!-- Ensure tax won't be shown in the cart --> + <actionGroup ref="ChangeToDefaultTaxConfigurationUIActionGroup" stepKey="changeToDefaultTaxConfiguration"/> + + <amOnPage url="{{AdminLogoutPage.url}}" stepKey="amOnLogoutPage"/> + <deleteData createDataKey="simpleProduct1" stepKey="deleteSimpleProduct1"/> + </after> + + <!-- Go to the created product page and add it to the cart --> + <amOnPage url="$$simpleProduct1.sku$$.html" stepKey="goToSimpleProductPage"/> + <waitForPageLoad stepKey="waitForSimpleProductPage"/> + <click stepKey="addSimpleProductToCart" selector="{{StorefrontProductActionSection.addToCart}}"/> + <waitForPageLoad stepKey="waitForProductAdded"/> + <see stepKey="seeSuccess" selector="{{AdminCategoryMessagesSection.SuccessMessage}}" userInput="You added"/> + + <!-- Assert that taxes are applied correctly for NY --> + <amOnPage url="{{CheckoutCartPage.url}}" stepKey="goToCheckout"/> + <waitForPageLoad stepKey="waitForCart"/> + <waitForLoadingMaskToDisappear stepKey="waitForLoadingMask"/> + + <waitForElementVisible stepKey="waitForOverviewVisible" selector="{{CheckoutPaymentSection.tax}}"/> + <waitForText userInput="$5.00" selector="{{CheckoutPaymentSection.orderSummaryShippingTotal}}" time="30" stepKey="waitForCorrectShippingAmount"/> + <see stepKey="seeTax" selector="{{CheckoutPaymentSection.tax}}" userInput="$10.30"/> + <click stepKey="expandTax" selector="{{CheckoutPaymentSection.tax}}"/> + <see stepKey="seeTaxPercent" selector="{{CheckoutPaymentSection.taxPercentage}}" userInput="({{SimpleTaxNY.rate}}%)"/> + <see stepKey="seeTotalIncl" selector="{{CheckoutPaymentSection.orderSummaryTotalIncluding}}" userInput="$138.30"/> + <see stepKey="seeTotalExcl" selector="{{CheckoutPaymentSection.orderSummaryTotalExcluding}}" userInput="$128.00"/> + + <!-- Change the address --> + <actionGroup ref="ChangeSummaryQuoteAddressActionGroup" stepKey="changeAddress"> + <argument name="taxCode" value="SimpleTaxSwiss"/> + </actionGroup> + + <!-- Assert that taxes are applied correctly for Switzerland --> + <see stepKey="seeTax2" selector="{{CheckoutPaymentSection.tax}}" userInput="$0.00"/> + <see stepKey="seeTotalIncl2" selector="{{CheckoutPaymentSection.orderSummaryTotalIncluding}}" userInput="$128.00"/> + <see stepKey="seeTotalExcl2" selector="{{CheckoutPaymentSection.orderSummaryTotalExcluding}}" userInput="$128.00"/> + + <!-- Change the address --> + <actionGroup ref="ChangeSummaryQuoteAddressActionGroup" stepKey="changeAddress2"> + <argument name="taxCode" value="SimpleTaxCA"/> + </actionGroup> + + <!-- Assert that taxes are applied correctly for CA --> + <see stepKey="seeTax3" selector="{{CheckoutPaymentSection.tax}}" userInput="$10.15"/> + <click stepKey="expandTax2" selector="{{CheckoutPaymentSection.tax}}"/> + <see stepKey="seeTaxPercent2" selector="{{CheckoutPaymentSection.taxPercentage}}" userInput="({{SimpleTaxCA.rate}}%)"/> + <see stepKey="seeTotalIncl3" selector="{{CheckoutPaymentSection.orderSummaryTotalIncluding}}" userInput="$138.15"/> + <see stepKey="seeTotalExcl3" selector="{{CheckoutPaymentSection.orderSummaryTotalExcluding}}" userInput="$128.00"/> + </test> +</tests> diff --git a/app/code/Magento/Tax/Test/Mftf/Test/StorefrontTaxQuoteCartTest/StorefrontTaxQuoteCartLoggedInVirtualTest.xml b/app/code/Magento/Tax/Test/Mftf/Test/StorefrontTaxQuoteCartTest/StorefrontTaxQuoteCartLoggedInVirtualTest.xml new file mode 100644 index 0000000000000..3b92ab3498442 --- /dev/null +++ b/app/code/Magento/Tax/Test/Mftf/Test/StorefrontTaxQuoteCartTest/StorefrontTaxQuoteCartLoggedInVirtualTest.xml @@ -0,0 +1,131 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + /** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +--> +<tests xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/testSchema.xsd"> + <test name="StorefrontTaxQuoteCartLoggedInVirtualTest"> + <annotations> + <features value="Tax"/> + <stories value="Tax Calculation in Shopping Cart"/> + <title value="Tax for Virtual Product Quote should be recalculated in Shopping Cart for Logged in Customer with Default Address"/> + <description value="Tax for Virtual Product Quote should be recalculated in Shopping Cart for Logged in Customer with Default Address"/> + <severity value="CRITICAL"/> + <testCaseId value="MC-296"/> + <group value="Tax"/> + </annotations> + <before> + <actionGroup ref="AdminLoginActionGroup" stepKey="loginAsAdmin"/> + <createData entity="VirtualProduct" stepKey="virtualProduct1"/> + + <!-- Fill in rules to display tax in the cart --> + <actionGroup ref="EditTaxConfigurationByUIActionGroup" stepKey="fillDefaultTaxForms"/> + + <!-- Go to tax rule page --> + <amOnPage url="{{AdminTaxRuleGridPage.url}}" stepKey="goToTaxRulePage"/> + <waitForPageLoad stepKey="waitForTaxRatePage"/> + <click stepKey="addNewTaxRate" selector="{{AdminGridMainControls.add}}"/> + <fillField stepKey="fillRuleName" selector="{{AdminTaxRulesSection.ruleName}}" userInput="SampleRule"/> + + <!-- Add NY and CA tax rules --> + <actionGroup ref="AddNewTaxRateNoZipActionGroup" stepKey="addNYTaxRate"> + <argument name="taxCode" value="SimpleTaxNY"/> + </actionGroup> + + <actionGroup ref="AddNewTaxRateNoZipActionGroup" stepKey="addCATaxRate"> + <argument name="taxCode" value="SimpleTaxCA"/> + </actionGroup> + + <click stepKey="clickSave" selector="{{AdminStoresMainActionsSection.saveButton}}"/> + + <!-- Fill out form for a new user with address --> + <actionGroup ref="StorefrontOpenCustomerAccountCreatePageActionGroup" stepKey="openCreateAccountPage"/> + <actionGroup ref="StorefrontFillCustomerAccountCreationFormActionGroup" stepKey="fillCreateAccountForm"> + <argument name="customer" value="Simple_US_Customer_NY"/> + </actionGroup> + <actionGroup ref="StorefrontClickCreateAnAccountCustomerAccountCreationFormActionGroup" stepKey="submitCreateAccountForm"/> + <actionGroup ref="AssertMessageCustomerCreateAccountActionGroup" stepKey="seeSuccessMessage"> + <argument name="messageType" value="success"/> + <argument name="message" value="Thank you for registering with Main Website Store."/> + </actionGroup> + + <actionGroup ref="EnterCustomerAddressInfoActionGroup" stepKey="enterAddressInfo"> + <argument name="Address" value="US_Address_NY"/> + </actionGroup> + </before> + <after> + <!-- Go to the tax rule page and delete the row we created--> + <amOnPage url="{{AdminTaxRuleGridPage.url}}" stepKey="goToTaxRulesPage"/> + <waitForPageLoad stepKey="waitForRulesPage"/> + <actionGroup ref="deleteEntitySecondaryGrid" stepKey="deleteRule"> + <argument name="name" value="SampleRule"/> + <argument name="searchInput" value="{{AdminSecondaryGridSection.taxIdentifierSearch}}"/> + </actionGroup> + + <!-- Go to the tax rate page --> + <amOnPage url="{{AdminTaxRateGridPage.url}}" stepKey="goToTaxRatesPage"/> + <waitForPageLoad stepKey="waitForRatesPage"/> + + <!-- Delete the two tax rates that were created --> + <actionGroup ref="deleteEntitySecondaryGrid" stepKey="deleteNYRate"> + <argument name="name" value="{{SimpleTaxNY.state}}-{{SimpleTaxNY.rate}}"/> + <argument name="searchInput" value="{{AdminSecondaryGridSection.taxIdentifierSearch}}"/> + </actionGroup> + + <actionGroup ref="deleteEntitySecondaryGrid" stepKey="deleteCARate"> + <argument name="name" value="{{SimpleTaxCA.state}}-{{SimpleTaxCA.rate}}"/> + <argument name="searchInput" value="{{AdminSecondaryGridSection.taxIdentifierSearch}}"/> + </actionGroup> + + <!-- Ensure tax won't be shown in the cart --> + <actionGroup ref="ChangeToDefaultTaxConfigurationUIActionGroup" stepKey="changeToDefaultTaxConfiguration"/> + + <amOnPage url="{{AdminLogoutPage.url}}" stepKey="amOnLogoutPage"/> + <deleteData createDataKey="virtualProduct1" stepKey="deleteVirtualProduct1"/> + </after> + + <!-- Go to the created product page and add it to the cart --> + <amOnPage url="$$virtualProduct1.sku$$.html" stepKey="goToVirtualProductPage"/> + <waitForPageLoad stepKey="waitForVirtualProductPage"/> + <click stepKey="addVirtualProductToCart" selector="{{StorefrontProductActionSection.addToCart}}"/> + <waitForPageLoad stepKey="waitForProductAdded"/> + <see stepKey="seeSuccess" selector="{{AdminCategoryMessagesSection.SuccessMessage}}" userInput="You added"/> + + <!-- Assert that taxes are applied correctly for NY --> + <amOnPage url="{{CheckoutCartPage.url}}" stepKey="goToCheckout"/> + <waitForPageLoad stepKey="waitForCart"/> + <waitForLoadingMaskToDisappear stepKey="waitForLoadingMask"/> + + <waitForElementVisible stepKey="waitForOverviewVisible" selector="{{CheckoutPaymentSection.tax}}"/> + <see stepKey="seeTax" selector="{{CheckoutPaymentSection.tax}}" userInput="$8.37"/> + <click stepKey="expandTax" selector="{{CheckoutPaymentSection.tax}}"/> + <see stepKey="seeTaxPercent" selector="{{CheckoutPaymentSection.taxPercentage}}" userInput="({{SimpleTaxNY.rate}}%)"/> + <see stepKey="seeTotalIncl" selector="{{CheckoutPaymentSection.orderSummaryTotalIncluding}}" userInput="$108.36"/> + <see stepKey="seeTotalExcl" selector="{{CheckoutPaymentSection.orderSummaryTotalExcluding}}" userInput="$$virtualProduct1.price$$"/> + + <!-- Change the address --> + <actionGroup ref="ChangeSummaryQuoteAddressActionGroup" stepKey="changeAddress"> + <argument name="taxCode" value="SimpleTaxSwiss"/> + </actionGroup> + + <!-- Assert that taxes are applied correctly for Switzerland --> + <see stepKey="seeTax2" selector="{{CheckoutPaymentSection.tax}}" userInput="$0.00"/> + <see stepKey="seeTotalIncl2" selector="{{CheckoutPaymentSection.orderSummaryTotalIncluding}}" userInput="$$$virtualProduct1.price$$"/> + <see stepKey="seeTotalExcl2" selector="{{CheckoutPaymentSection.orderSummaryTotalExcluding}}" userInput="$$$virtualProduct1.price$$"/> + + <!-- Change the address --> + <actionGroup ref="ChangeSummaryQuoteAddressActionGroup" stepKey="changeAddress2"> + <argument name="taxCode" value="SimpleTaxCA"/> + </actionGroup> + + <!-- Assert that taxes are applied correctly for CA --> + <see stepKey="seeTax3" selector="{{CheckoutPaymentSection.tax}}" userInput="$8.25"/> + <click stepKey="expandTax2" selector="{{CheckoutPaymentSection.tax}}"/> + <see stepKey="seeTaxPercent2" selector="{{CheckoutPaymentSection.taxPercentage}}" userInput="({{SimpleTaxCA.rate}}%)"/> + <see stepKey="seeTotalIncl3" selector="{{CheckoutPaymentSection.orderSummaryTotalIncluding}}" userInput="$108.24"/> + <see stepKey="seeTotalExcl3" selector="{{CheckoutPaymentSection.orderSummaryTotalExcluding}}" userInput="$$virtualProduct1.price$$"/> + </test> +</tests> diff --git a/app/code/Magento/Tax/Test/Mftf/Test/StorefrontTaxQuoteCheckoutTest.xml b/app/code/Magento/Tax/Test/Mftf/Test/StorefrontTaxQuoteCheckoutTest.xml deleted file mode 100644 index e0e411a04d484..0000000000000 --- a/app/code/Magento/Tax/Test/Mftf/Test/StorefrontTaxQuoteCheckoutTest.xml +++ /dev/null @@ -1,469 +0,0 @@ -<?xml version="1.0" encoding="UTF-8"?> -<!-- - /** - * Copyright © Magento, Inc. All rights reserved. - * See COPYING.txt for license details. - */ ---> - -<tests xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" - xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/testSchema.xsd"> - <test name="StorefrontTaxQuoteCheckoutGuestVirtual"> - <annotations> - <features value="Tax"/> - <stories value="Tax Calculation in One Page Checkout"/> - <title value="Tax for Virtual Product Quote should be recalculated according to inputted data on Checkout flow for Guest Customer"/> - <description value="Tax for Virtual Product Quote should be recalculated according to inputted data on Checkout flow for Guest Customer"/> - <severity value="CRITICAL"/> - <testCaseId value="MC-255"/> - <group value="Tax"/> - </annotations> - <before> - <actionGroup ref="LoginAsAdmin" stepKey="loginAsAdmin"/> - <createData entity="VirtualProduct" stepKey="virtualProduct1"/> - - <!-- Fill in rules to display tax in the cart --> - <actionGroup ref="EditTaxConfigurationByUIActionGroup" stepKey="fillDefaultTaxForms"/> - - <!-- Go to tax rule page --> - <amOnPage url="{{AdminTaxRuleGridPage.url}}" stepKey="goToTaxRulePage"/> - <waitForPageLoad stepKey="waitForTaxRatePage"/> - <click stepKey="addNewTaxRate" selector="{{AdminGridMainControls.add}}"/> - <fillField stepKey="fillRuleName" selector="{{AdminTaxRulesSection.ruleName}}" userInput="SampleRule"/> - - <!-- Add NY and CA tax rules --> - <actionGroup ref="AddNewTaxRateNoZipActionGroup" stepKey="addNYTaxRate"> - <argument name="taxCode" value="SimpleTaxNY"/> - </actionGroup> - - <actionGroup ref="AddNewTaxRateNoZipActionGroup" stepKey="addCATaxRate"> - <argument name="taxCode" value="SimpleTaxCA"/> - </actionGroup> - - <click stepKey="clickSave" selector="{{AdminStoresMainActionsSection.saveButton}}"/> - </before> - <after> - <!-- Go to the tax rule page and delete the row we created--> - <amOnPage url="{{AdminTaxRuleGridPage.url}}" stepKey="goToTaxRulesPage"/> - <waitForPageLoad stepKey="waitForRulesPage"/> - <actionGroup ref="deleteEntitySecondaryGrid" stepKey="deleteRule"> - <argument name="name" value="SampleRule"/> - <argument name="searchInput" value="{{AdminSecondaryGridSection.taxIdentifierSearch}}"/> - </actionGroup> - - <!-- Go to the tax rate page --> - <amOnPage url="{{AdminTaxRateGridPage.url}}" stepKey="goToTaxRatesPage"/> - <waitForPageLoad stepKey="waitForRatesPage"/> - - <!-- Delete the two tax rates that were created --> - <actionGroup ref="deleteEntitySecondaryGrid" stepKey="deleteNYRate"> - <argument name="name" value="{{SimpleTaxNY.state}}-{{SimpleTaxNY.rate}}"/> - <argument name="searchInput" value="{{AdminSecondaryGridSection.taxIdentifierSearch}}"/> - </actionGroup> - - <actionGroup ref="deleteEntitySecondaryGrid" stepKey="deleteCARate"> - <argument name="name" value="{{SimpleTaxCA.state}}-{{SimpleTaxCA.rate}}"/> - <argument name="searchInput" value="{{AdminSecondaryGridSection.taxIdentifierSearch}}"/> - </actionGroup> - - <!-- Ensure tax won't be shown in the cart --> - <actionGroup ref="ChangeToDefaultTaxConfigurationUIActionGroup" stepKey="changeToDefaultTaxConfiguration"/> - - <amOnPage url="{{AdminLogoutPage.url}}" stepKey="amOnLogoutPage"/> - <deleteData createDataKey="virtualProduct1" stepKey="deleteVirtualProduct1"/> - </after> - - <!-- Go to the created product page and add it to the cart --> - <amOnPage url="$$virtualProduct1.sku$$.html" stepKey="goToVirtualProductPage"/> - <waitForPageLoad stepKey="waitForVirtualProductPage"/> - <click stepKey="addVirtualProductToCart" selector="{{StorefrontProductActionSection.addToCart}}"/> - <waitForPageLoad stepKey="waitForProductAdded"/> - <see stepKey="seeSuccess" selector="{{AdminCategoryMessagesSection.SuccessMessage}}" userInput="You added"/> - - <!-- Assert that taxes are applied correctly for CA --> - <amOnPage url="{{CheckoutPage.url}}" stepKey="goToCheckout"/> - <waitForPageLoad stepKey="waitForShippingSection"/> - - <waitForElementVisible stepKey="waitForOverviewVisible" selector="{{CheckoutPaymentSection.tax}}"/> - <see stepKey="seeTax2" selector="{{CheckoutPaymentSection.tax}}" userInput="$8.25"/> - <click stepKey="expandTax2" selector="{{CheckoutPaymentSection.tax}}"/> - <see stepKey="seeTaxPercent2" selector="{{CheckoutPaymentSection.taxPercentage}}" userInput="({{SimpleTaxCA.rate}}%)"/> - <see stepKey="seeTotalIncl2" selector="{{CheckoutPaymentSection.orderSummaryTotalIncluding}}" userInput="$108.24"/> - <see stepKey="seeTotalExcl2" selector="{{CheckoutPaymentSection.orderSummaryTotalExcluding}}" userInput="$$virtualProduct1.price$$"/> - - <!-- Change the address --> - <actionGroup ref="GuestCheckoutSelectPaymentAndFillNewBillingAddressActionGroup" stepKey="changeAddress"> - <argument name="customerVar" value="Simple_US_Customer_NY"/> - <argument name="customerAddressVar" value="US_Address_NY"/> - <argument name="paymentMethod" value="Check / Money order"/> - </actionGroup> - <click stepKey="saveAddress" selector="{{CheckoutShippingSection.updateAddress}}"/> - - <waitForPageLoad stepKey="waitForAddressSaved"/> - <see stepKey="seeEditButton" selector="{{CheckoutShippingSection.editAddressButton}}" userInput="Edit"/> - <see stepKey="seeAddress2" selector="{{CheckoutShippingSection.defaultShipping}}" userInput="{{SimpleTaxNY.state}}"/> - - <!-- Assert that taxes are applied correctly for NY --> - <waitForElementVisible stepKey="waitForOverviewVisible2" selector="{{CheckoutPaymentSection.tax}}"/> - <see stepKey="seeTax" selector="{{CheckoutPaymentSection.tax}}" userInput="$8.37"/> - <click stepKey="expandTax" selector="{{CheckoutPaymentSection.tax}}"/> - <see stepKey="seeTaxPercent" selector="{{CheckoutPaymentSection.taxPercentage}}" userInput="({{SimpleTaxNY.rate}}%)"/> - <see stepKey="seeTotalIncl" selector="{{CheckoutPaymentSection.orderSummaryTotalIncluding}}" userInput="$108.36"/> - <see stepKey="seeTotalExcl" selector="{{CheckoutPaymentSection.orderSummaryTotalExcluding}}" userInput="$$virtualProduct1.price$$"/> - </test> - - <test name="StorefrontTaxQuoteCheckoutLoggedInSimple"> - <annotations> - <features value="Tax"/> - <stories value="Tax Calculation in One Page Checkout"/> - <title value="Tax for Simple Product Quote should be recalculated according to inputted data on Checkout flow for Logged in Customer"/> - <description value="Tax for Simple Product Quote should be recalculated according to inputted data on Checkout flow for Logged in Customer"/> - <severity value="CRITICAL"/> - <testCaseId value="MC-256"/> - <group value="Tax"/> - </annotations> - <before> - <actionGroup ref="LoginAsAdmin" stepKey="loginAsAdmin"/> - <createData entity="SimpleProduct2" stepKey="simpleProduct1"/> - - <!-- Fill in rules to display tax in the cart --> - <actionGroup ref="EditTaxConfigurationByUIActionGroup" stepKey="fillDefaultTaxForms"/> - - <!-- Go to tax rule page --> - <amOnPage url="{{AdminTaxRuleGridPage.url}}" stepKey="goToTaxRulePage"/> - <waitForPageLoad stepKey="waitForTaxRatePage"/> - <click stepKey="addNewTaxRate" selector="{{AdminGridMainControls.add}}"/> - <fillField stepKey="fillRuleName" selector="{{AdminTaxRulesSection.ruleName}}" userInput="SampleRule"/> - - <!-- Add NY and CA tax rules --> - <actionGroup ref="AddNewTaxRateNoZipActionGroup" stepKey="addNYTaxRate"> - <argument name="taxCode" value="SimpleTaxNY"/> - </actionGroup> - - <actionGroup ref="AddNewTaxRateNoZipActionGroup" stepKey="addCATaxRate"> - <argument name="taxCode" value="SimpleTaxCA"/> - </actionGroup> - - <click stepKey="clickSave" selector="{{AdminStoresMainActionsSection.saveButton}}"/> - - <!-- Fill out form for a new user with address --> - <actionGroup ref="SignUpNewUserFromStorefrontActionGroup" stepKey="SignUpNewUser"> - <argument name="Customer" value="Simple_US_Customer_NY"/> - </actionGroup> - - <actionGroup ref="EnterCustomerAddressInfo" stepKey="enterAddressInfo"> - <argument name="Address" value="US_Address_NY"/> - </actionGroup> - </before> - <after> - <!-- Go to the tax rule page and delete the row we created--> - <amOnPage url="{{AdminTaxRuleGridPage.url}}" stepKey="goToTaxRulesPage"/> - <waitForPageLoad stepKey="waitForRulesPage"/> - <actionGroup ref="deleteEntitySecondaryGrid" stepKey="deleteRule"> - <argument name="name" value="SampleRule"/> - <argument name="searchInput" value="{{AdminSecondaryGridSection.taxIdentifierSearch}}"/> - </actionGroup> - - <!-- Go to the tax rate page --> - <amOnPage url="{{AdminTaxRateGridPage.url}}" stepKey="goToTaxRatesPage"/> - <waitForPageLoad stepKey="waitForRatesPage"/> - - <!-- Delete the two tax rates that were created --> - <actionGroup ref="deleteEntitySecondaryGrid" stepKey="deleteNYRate"> - <argument name="name" value="{{SimpleTaxNY.state}}-{{SimpleTaxNY.rate}}"/> - <argument name="searchInput" value="{{AdminSecondaryGridSection.taxIdentifierSearch}}"/> - </actionGroup> - - <actionGroup ref="deleteEntitySecondaryGrid" stepKey="deleteCARate"> - <argument name="name" value="{{SimpleTaxCA.state}}-{{SimpleTaxCA.rate}}"/> - <argument name="searchInput" value="{{AdminSecondaryGridSection.taxIdentifierSearch}}"/> - </actionGroup> - - <!-- Ensure tax won't be shown in the cart --> - <actionGroup ref="ChangeToDefaultTaxConfigurationUIActionGroup" stepKey="changeToDefaultTaxConfiguration"/> - - <amOnPage url="{{AdminLogoutPage.url}}" stepKey="amOnLogoutPage"/> - <deleteData createDataKey="simpleProduct1" stepKey="deleteSimpleProduct1"/> - </after> - - <!-- Go to the created product page and add it to the cart --> - <amOnPage url="$$simpleProduct1.sku$$.html" stepKey="goToSimpleProductPage"/> - <waitForPageLoad stepKey="waitForSimpleProductPage"/> - <click stepKey="addSimpleProductToCart" selector="{{StorefrontProductActionSection.addToCart}}"/> - <waitForPageLoad stepKey="waitForProductAdded"/> - <see stepKey="seeSuccess" selector="{{AdminCategoryMessagesSection.SuccessMessage}}" userInput="You added"/> - - <!-- Assert that taxes are applied correctly for NY --> - <amOnPage url="{{CheckoutPage.url}}" stepKey="goToCheckout"/> - <waitForPageLoad stepKey="waitForShippingSection"/> - <see stepKey="seeAddress" selector="{{CheckoutShippingSection.selectedShippingAddress}}" userInput="{{SimpleTaxNY.state}}"/> - <click stepKey="clickNext" selector="{{CheckoutShippingSection.next}}"/> - <waitForPageLoad stepKey="waitForReviewAndPayments"/> - - <waitForElementVisible stepKey="waitForOverviewVisible" selector="{{CheckoutPaymentSection.tax}}"/> - <see stepKey="seeTax" selector="{{CheckoutPaymentSection.tax}}" userInput="$10.30"/> - <click stepKey="expandTax" selector="{{CheckoutPaymentSection.tax}}"/> - <see stepKey="seeTaxPercent" selector="{{CheckoutPaymentSection.taxPercentage}}" userInput="({{SimpleTaxNY.rate}}%)"/> - <see stepKey="seeTotalIncl" selector="{{CheckoutPaymentSection.orderSummaryTotalIncluding}}" userInput="$138.30"/> - <see stepKey="seeTotalExcl" selector="{{CheckoutPaymentSection.orderSummaryTotalExcluding}}" userInput="$128.00"/> - - <!-- Go back to the shipping page and change the address --> - <click stepKey="goBackToShipping" selector="{{CheckoutShippingSection.shippingTab}}"/> - <click stepKey="addNewAddress" selector="{{CheckoutShippingSection.newAddressButton}}"/> - <waitForPageLoad stepKey="waitForAddressPopUp"/> - - <actionGroup ref="LoggedInCheckoutFillNewBillingAddressActionGroup" stepKey="changeAddress"> - <argument name="Address" value="US_Address_CA"/> - <argument name="classPrefix" value="._show"/> - </actionGroup> - <click stepKey="saveAddress" selector="{{CheckoutShippingSection.saveAddress}}"/> - - <waitForPageLoad stepKey="waitForAddressSaved"/> - <see stepKey="seeAddress2" selector="{{CheckoutShippingSection.selectedShippingAddress}}" userInput="{{SimpleTaxCA.state}}"/> - <click stepKey="clickNext2" selector="{{CheckoutShippingSection.next}}"/> - <waitForPageLoad stepKey="waitForReviewAndPayments2"/> - - <!-- Assert that taxes are applied correctly for CA --> - <waitForElementVisible stepKey="waitForOverviewVisible2" selector="{{CheckoutPaymentSection.tax}}"/> - <see stepKey="seeTax2" selector="{{CheckoutPaymentSection.tax}}" userInput="$10.15"/> - <click stepKey="expandTax2" selector="{{CheckoutPaymentSection.tax}}"/> - <see stepKey="seeTaxPercent2" selector="{{CheckoutPaymentSection.taxPercentage}}" userInput="({{SimpleTaxCA.rate}}%)"/> - <see stepKey="seeTotalIncl2" selector="{{CheckoutPaymentSection.orderSummaryTotalIncluding}}" userInput="$138.15"/> - <see stepKey="seeTotalExcl2" selector="{{CheckoutPaymentSection.orderSummaryTotalExcluding}}" userInput="$128.00"/> - </test> - - <test name="StorefrontTaxQuoteCheckoutGuestSimple"> - <annotations> - <features value="Tax"/> - <stories value="Tax Calculation in One Page Checkout"/> - <title value="Tax for Simple Product Quote should be recalculated according to inputted data on Checkout flow for Guest Customer"/> - <description value="Tax for Simple Product Quote should be recalculated according to inputted data on Checkout flow for Guest Customer"/> - <severity value="CRITICAL"/> - <testCaseId value="MC-258"/> - <group value="Tax"/> - </annotations> - <before> - <actionGroup ref="LoginAsAdmin" stepKey="loginAsAdmin"/> - <createData entity="SimpleProduct2" stepKey="simpleProduct1"/> - - <!-- Fill in rules to display tax in the cart --> - <actionGroup ref="EditTaxConfigurationByUIActionGroup" stepKey="fillDefaultTaxForms"/> - - <!-- Go to tax rule page --> - <amOnPage url="{{AdminTaxRuleGridPage.url}}" stepKey="goToTaxRulePage"/> - <waitForPageLoad stepKey="waitForTaxRatePage"/> - <click stepKey="addNewTaxRate" selector="{{AdminGridMainControls.add}}"/> - <fillField stepKey="fillRuleName" selector="{{AdminTaxRulesSection.ruleName}}" userInput="SampleRule"/> - - <!-- Add NY and CA tax rules --> - <actionGroup ref="AddNewTaxRateNoZipActionGroup" stepKey="addNYTaxRate"> - <argument name="taxCode" value="SimpleTaxNY"/> - </actionGroup> - - <actionGroup ref="AddNewTaxRateNoZipActionGroup" stepKey="addCATaxRate"> - <argument name="taxCode" value="SimpleTaxCA"/> - </actionGroup> - - <click stepKey="clickSave" selector="{{AdminStoresMainActionsSection.saveButton}}"/> - </before> - <after> - <!-- Go to the tax rule page and delete the row we created--> - <amOnPage url="{{AdminTaxRuleGridPage.url}}" stepKey="goToTaxRulesPage"/> - <waitForPageLoad stepKey="waitForRulesPage"/> - <actionGroup ref="deleteEntitySecondaryGrid" stepKey="deleteRule"> - <argument name="name" value="SampleRule"/> - <argument name="searchInput" value="{{AdminSecondaryGridSection.taxIdentifierSearch}}"/> - </actionGroup> - - <!-- Go to the tax rate page --> - <amOnPage url="{{AdminTaxRateGridPage.url}}" stepKey="goToTaxRatesPage"/> - <waitForPageLoad stepKey="waitForRatesPage"/> - - <!-- Delete the two tax rates that were created --> - <actionGroup ref="deleteEntitySecondaryGrid" stepKey="deleteNYRate"> - <argument name="name" value="{{SimpleTaxNY.state}}-{{SimpleTaxNY.rate}}"/> - <argument name="searchInput" value="{{AdminSecondaryGridSection.taxIdentifierSearch}}"/> - </actionGroup> - - <actionGroup ref="deleteEntitySecondaryGrid" stepKey="deleteCARate"> - <argument name="name" value="{{SimpleTaxCA.state}}-{{SimpleTaxCA.rate}}"/> - <argument name="searchInput" value="{{AdminSecondaryGridSection.taxIdentifierSearch}}"/> - </actionGroup> - - <!-- Ensure tax won't be shown in the cart --> - <actionGroup ref="ChangeToDefaultTaxConfigurationUIActionGroup" stepKey="changeToDefaultTaxConfiguration"/> - - <amOnPage url="{{AdminLogoutPage.url}}" stepKey="amOnLogoutPage"/> - <deleteData createDataKey="simpleProduct1" stepKey="deleteSimpleProduct1"/> - </after> - - <!-- Go to the created product page and add it to the cart --> - <amOnPage url="$$simpleProduct1.sku$$.html" stepKey="goToSimpleProductPage"/> - <waitForPageLoad stepKey="waitForSimpleProductPage"/> - <click stepKey="addSimpleProductToCart" selector="{{StorefrontProductActionSection.addToCart}}"/> - <waitForPageLoad stepKey="waitForProductAdded"/> - <see stepKey="seeSuccess" selector="{{AdminCategoryMessagesSection.SuccessMessage}}" userInput="You added"/> - - <!-- Fill in address for CA --> - <amOnPage url="{{CheckoutPage.url}}" stepKey="goToCheckout"/> - <waitForPageLoad stepKey="waitForShippingSection"/> - <fillField selector="{{CheckoutShippingSection.email}}" userInput="{{Simple_US_Customer_CA.email}}" stepKey="enterEmail"/> - <waitForLoadingMaskToDisappear stepKey="waitEmailLoad" /> - <actionGroup ref="LoggedInCheckoutFillNewBillingAddressActionGroup" stepKey="changeAddress"> - <argument name="Address" value="US_Address_CA"/> - </actionGroup> - <click stepKey="clickNext" selector="{{CheckoutShippingSection.next}}"/> - <waitForPageLoad stepKey="waitForAddressToLoad"/> - <!-- Checkout select Check/Money Order payment --> - <actionGroup ref="CheckoutSelectCheckMoneyOrderPaymentActionGroup" stepKey="selectCheckMoneyPayment"/> - <see stepKey="seeAddress" selector="{{CheckoutShippingSection.defaultShipping}}" userInput="{{SimpleTaxCA.state}}"/> - <see stepKey="seeShipTo" selector="{{CheckoutPaymentSection.shipToInformation}}" userInput="{{SimpleTaxCA.state}}"/> - - <!-- Assert that taxes are applied correctly for CA --> - <waitForElementVisible stepKey="waitForOverviewVisible" selector="{{CheckoutPaymentSection.tax}}"/> - <see stepKey="seeTax2" selector="{{CheckoutPaymentSection.tax}}" userInput="$10.15"/> - <click stepKey="expandTax2" selector="{{CheckoutPaymentSection.tax}}"/> - <see stepKey="seeTaxPercent2" selector="{{CheckoutPaymentSection.taxPercentage}}" userInput="({{SimpleTaxCA.rate}}%)"/> - <see stepKey="seeTotalIncl2" selector="{{CheckoutPaymentSection.orderSummaryTotalIncluding}}" userInput="$138.15"/> - <see stepKey="seeTotalExcl2" selector="{{CheckoutPaymentSection.orderSummaryTotalExcluding}}" userInput="$128.00"/> - - <!-- Go back to the shipping page and change the address --> - <click stepKey="goBackToShipping" selector="{{CheckoutShippingSection.shippingTab}}"/> - <actionGroup ref="LoggedInCheckoutFillNewBillingAddressActionGroup" stepKey="changeAddress2"> - <argument name="Address" value="US_Address_NY"/> - </actionGroup> - <click stepKey="clickNext2" selector="{{CheckoutShippingSection.next}}"/> - <waitForPageLoad stepKey="waitForShippingToLoad"/> - <!-- Checkout select Check/Money Order payment --> - <actionGroup ref="CheckoutSelectCheckMoneyOrderPaymentActionGroup" stepKey="selectCheckMoneyPayment2"/> - <see stepKey="seeShipTo2" selector="{{CheckoutPaymentSection.shipToInformation}}" userInput="{{SimpleTaxNY.state}}"/> - - <!-- Assert that taxes are applied correctly for NY --> - <waitForElementVisible stepKey="waitForOverviewVisible2" selector="{{CheckoutPaymentSection.tax}}"/> - <see stepKey="seeTax" selector="{{CheckoutPaymentSection.tax}}" userInput="$10.30"/> - <click stepKey="expandTax" selector="{{CheckoutPaymentSection.tax}}"/> - <see stepKey="seeTaxPercent" selector="{{CheckoutPaymentSection.taxPercentage}}" userInput="({{SimpleTaxNY.rate}}%)"/> - <see stepKey="seeTotalIncl" selector="{{CheckoutPaymentSection.orderSummaryTotalIncluding}}" userInput="$138.30"/> - <see stepKey="seeTotalExcl" selector="{{CheckoutPaymentSection.orderSummaryTotalExcluding}}" userInput="$128.00"/> - </test> - - <test name="StorefrontTaxQuoteCheckoutLoggedInVirtual"> - <annotations> - <features value="Tax"/> - <stories value="Tax Calculation in One Page Checkout"/> - <title value="Tax for Virtual Product Quote should be recalculated according to inputted data on Checkout flow for Logged in Customer"/> - <description value="Tax for Virtual Product Quote should be recalculated according to inputted data on Checkout flow for Logged in Customer"/> - <severity value="CRITICAL"/> - <testCaseId value="MC-259"/> - <group value="Tax"/> - </annotations> - <before> - <actionGroup ref="LoginAsAdmin" stepKey="loginAsAdmin"/> - <createData entity="VirtualProduct" stepKey="virtualProduct1"/> - - <!-- Fill in rules to display tax in the cart --> - <actionGroup ref="EditTaxConfigurationByUIActionGroup" stepKey="fillDefaultTaxForms"/> - - <!-- Go to tax rule page --> - <amOnPage url="{{AdminTaxRuleGridPage.url}}" stepKey="goToTaxRulePage"/> - <waitForPageLoad stepKey="waitForTaxRatePage"/> - <click stepKey="addNewTaxRate" selector="{{AdminGridMainControls.add}}"/> - <fillField stepKey="fillRuleName" selector="{{AdminTaxRulesSection.ruleName}}" userInput="SampleRule"/> - - <!-- Add NY and CA tax rules --> - <actionGroup ref="AddNewTaxRateNoZipActionGroup" stepKey="addNYTaxRate"> - <argument name="taxCode" value="SimpleTaxNY"/> - </actionGroup> - - <actionGroup ref="AddNewTaxRateNoZipActionGroup" stepKey="addCATaxRate"> - <argument name="taxCode" value="SimpleTaxCA"/> - </actionGroup> - - <click stepKey="clickSave" selector="{{AdminStoresMainActionsSection.saveButton}}"/> - - <!-- Fill out form for a new user with address --> - <actionGroup ref="SignUpNewUserFromStorefrontActionGroup" stepKey="SignUpNewUser"> - <argument name="Customer" value="Simple_US_Customer_NY"/> - </actionGroup> - - <actionGroup ref="EnterCustomerAddressInfo" stepKey="enterAddressInfo"> - <argument name="Address" value="US_Address_NY"/> - </actionGroup> - </before> - <after> - <!-- Go to the tax rule page and delete the row we created--> - <amOnPage url="{{AdminTaxRuleGridPage.url}}" stepKey="goToTaxRulesPage"/> - <waitForPageLoad stepKey="waitForRulesPage"/> - <actionGroup ref="deleteEntitySecondaryGrid" stepKey="deleteRule"> - <argument name="name" value="SampleRule"/> - <argument name="searchInput" value="{{AdminSecondaryGridSection.taxIdentifierSearch}}"/> - </actionGroup> - - <!-- Go to the tax rate page --> - <amOnPage url="{{AdminTaxRateGridPage.url}}" stepKey="goToTaxRatesPage"/> - <waitForPageLoad stepKey="waitForRatesPage"/> - - <!-- Delete the two tax rates that were created --> - <actionGroup ref="deleteEntitySecondaryGrid" stepKey="deleteNYRate"> - <argument name="name" value="{{SimpleTaxNY.state}}-{{SimpleTaxNY.rate}}"/> - <argument name="searchInput" value="{{AdminSecondaryGridSection.taxIdentifierSearch}}"/> - </actionGroup> - - <actionGroup ref="deleteEntitySecondaryGrid" stepKey="deleteCARate"> - <argument name="name" value="{{SimpleTaxCA.state}}-{{SimpleTaxCA.rate}}"/> - <argument name="searchInput" value="{{AdminSecondaryGridSection.taxIdentifierSearch}}"/> - </actionGroup> - - <!-- Ensure tax won't be shown in the cart --> - <actionGroup ref="ChangeToDefaultTaxConfigurationUIActionGroup" stepKey="changeToDefaultTaxConfiguration"/> - - <amOnPage url="{{AdminLogoutPage.url}}" stepKey="amOnLogoutPage"/> - <deleteData createDataKey="virtualProduct1" stepKey="deleteVirtualProduct1"/> - </after> - - <!-- Go to the created product page and add it to the cart --> - <amOnPage url="$$virtualProduct1.sku$$.html" stepKey="goToVirtualProductPage"/> - <waitForPageLoad stepKey="waitForVirtualProductPage"/> - <click stepKey="addVirtualProductToCart" selector="{{StorefrontProductActionSection.addToCart}}"/> - <waitForPageLoad stepKey="waitForProductAdded"/> - <see stepKey="seeSuccess" selector="{{AdminCategoryMessagesSection.SuccessMessage}}" userInput="You added"/> - - <!-- Assert that taxes are applied correctly for NY --> - <amOnPage url="{{CheckoutPage.url}}" stepKey="goToCheckout"/> - <waitForPageLoad stepKey="waitForShippingSection"/> - <!-- Checkout select Check/Money Order payment --> - <actionGroup ref="CheckoutSelectCheckMoneyOrderPaymentActionGroup" stepKey="selectCheckMoneyPayment"/> - <see stepKey="seeAddress" selector="{{CheckoutShippingSection.defaultShipping}}" userInput="{{SimpleTaxNY.state}}"/> - - <waitForElementVisible stepKey="waitForOverviewVisible" selector="{{CheckoutPaymentSection.tax}}"/> - <see stepKey="seeTax" selector="{{CheckoutPaymentSection.tax}}" userInput="$8.37"/> - <click stepKey="expandTax" selector="{{CheckoutPaymentSection.tax}}"/> - <see stepKey="seeTaxPercent" selector="{{CheckoutPaymentSection.taxPercentage}}" userInput="({{SimpleTaxNY.rate}}%)"/> - <see stepKey="seeTotalIncl" selector="{{CheckoutPaymentSection.orderSummaryTotalIncluding}}" userInput="$108.36"/> - <see stepKey="seeTotalExcl" selector="{{CheckoutPaymentSection.orderSummaryTotalExcluding}}" userInput="$$virtualProduct1.price$$"/> - - <!-- Change the address --> - <click stepKey="editAddress" selector="{{CheckoutShippingSection.editAddressButton}}"/> - <selectOption stepKey="addNewAddress" selector="{{CheckoutShippingSection.addressDropdown}}" userInput="New Address"/> - <waitForPageLoad stepKey="waitForNewAddressForm"/> - - <actionGroup ref="LoggedInCheckoutFillNewBillingAddressActionGroup" stepKey="changeAddress"> - <argument name="Address" value="US_Address_CA"/> - <argument name="classPrefix" value="[aria-hidden=false]"/> - </actionGroup> - <click stepKey="saveAddress" selector="{{CheckoutShippingSection.updateAddress}}"/> - - <waitForPageLoad stepKey="waitForAddressSaved"/> - <!-- Checkout select Check/Money Order payment --> - <actionGroup ref="CheckoutSelectCheckMoneyOrderPaymentActionGroup" stepKey="selectCheckMoneyPayment2"/> - <see stepKey="seeAddress2" selector="{{CheckoutShippingSection.defaultShipping}}" userInput="{{SimpleTaxCA.state}}"/> - - <!-- Assert that taxes are applied correctly for CA --> - <waitForElementVisible stepKey="waitForOverviewVisible2" selector="{{CheckoutPaymentSection.tax}}"/> - <see stepKey="seeTax2" selector="{{CheckoutPaymentSection.tax}}" userInput="$8.25"/> - <click stepKey="expandTax2" selector="{{CheckoutPaymentSection.tax}}"/> - <see stepKey="seeTaxPercent2" selector="{{CheckoutPaymentSection.taxPercentage}}" userInput="({{SimpleTaxCA.rate}}%)"/> - <see stepKey="seeTotalIncl2" selector="{{CheckoutPaymentSection.orderSummaryTotalIncluding}}" userInput="$108.24"/> - <see stepKey="seeTotalExcl2" selector="{{CheckoutPaymentSection.orderSummaryTotalExcluding}}" userInput="$$virtualProduct1.price$$"/> - </test> -</tests> diff --git a/app/code/Magento/Tax/Test/Mftf/Test/StorefrontTaxQuoteCheckoutTest/StorefrontTaxQuoteCheckoutGuestSimpleTest.xml b/app/code/Magento/Tax/Test/Mftf/Test/StorefrontTaxQuoteCheckoutTest/StorefrontTaxQuoteCheckoutGuestSimpleTest.xml new file mode 100644 index 0000000000000..d005f4b657448 --- /dev/null +++ b/app/code/Magento/Tax/Test/Mftf/Test/StorefrontTaxQuoteCheckoutTest/StorefrontTaxQuoteCheckoutGuestSimpleTest.xml @@ -0,0 +1,126 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + /** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +--> +<tests xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/testSchema.xsd"> + <test name="StorefrontTaxQuoteCheckoutGuestSimpleTest"> + <annotations> + <features value="Tax"/> + <stories value="Tax Calculation in One Page Checkout"/> + <title value="Tax for Simple Product Quote should be recalculated according to inputted data on Checkout flow for Guest Customer"/> + <description value="Tax for Simple Product Quote should be recalculated according to inputted data on Checkout flow for Guest Customer"/> + <severity value="CRITICAL"/> + <testCaseId value="MC-258"/> + <group value="Tax"/> + </annotations> + <before> + <actionGroup ref="AdminLoginActionGroup" stepKey="loginAsAdmin"/> + <createData entity="SimpleProduct2" stepKey="simpleProduct1"/> + + <!-- Fill in rules to display tax in the cart --> + <actionGroup ref="EditTaxConfigurationByUIActionGroup" stepKey="fillDefaultTaxForms"/> + + <!-- Go to tax rule page --> + <amOnPage url="{{AdminTaxRuleGridPage.url}}" stepKey="goToTaxRulePage"/> + <waitForPageLoad stepKey="waitForTaxRatePage"/> + <click stepKey="addNewTaxRate" selector="{{AdminGridMainControls.add}}"/> + <fillField stepKey="fillRuleName" selector="{{AdminTaxRulesSection.ruleName}}" userInput="SampleRule"/> + + <!-- Add NY and CA tax rules --> + <actionGroup ref="AddNewTaxRateNoZipActionGroup" stepKey="addNYTaxRate"> + <argument name="taxCode" value="SimpleTaxNY"/> + </actionGroup> + + <actionGroup ref="AddNewTaxRateNoZipActionGroup" stepKey="addCATaxRate"> + <argument name="taxCode" value="SimpleTaxCA"/> + </actionGroup> + + <click stepKey="clickSave" selector="{{AdminStoresMainActionsSection.saveButton}}"/> + + <magentoCLI command="cron:run --group=index" stepKey="runCronIndexer"/> + </before> + <after> + <!-- Go to the tax rule page and delete the row we created--> + <amOnPage url="{{AdminTaxRuleGridPage.url}}" stepKey="goToTaxRulesPage"/> + <waitForPageLoad stepKey="waitForRulesPage"/> + <actionGroup ref="deleteEntitySecondaryGrid" stepKey="deleteRule"> + <argument name="name" value="SampleRule"/> + <argument name="searchInput" value="{{AdminSecondaryGridSection.taxIdentifierSearch}}"/> + </actionGroup> + + <!-- Go to the tax rate page --> + <amOnPage url="{{AdminTaxRateGridPage.url}}" stepKey="goToTaxRatesPage"/> + <waitForPageLoad stepKey="waitForRatesPage"/> + + <!-- Delete the two tax rates that were created --> + <actionGroup ref="deleteEntitySecondaryGrid" stepKey="deleteNYRate"> + <argument name="name" value="{{SimpleTaxNY.state}}-{{SimpleTaxNY.rate}}"/> + <argument name="searchInput" value="{{AdminSecondaryGridSection.taxIdentifierSearch}}"/> + </actionGroup> + + <actionGroup ref="deleteEntitySecondaryGrid" stepKey="deleteCARate"> + <argument name="name" value="{{SimpleTaxCA.state}}-{{SimpleTaxCA.rate}}"/> + <argument name="searchInput" value="{{AdminSecondaryGridSection.taxIdentifierSearch}}"/> + </actionGroup> + + <!-- Ensure tax won't be shown in the cart --> + <actionGroup ref="ChangeToDefaultTaxConfigurationUIActionGroup" stepKey="changeToDefaultTaxConfiguration"/> + + <amOnPage url="{{AdminLogoutPage.url}}" stepKey="amOnLogoutPage"/> + <deleteData createDataKey="simpleProduct1" stepKey="deleteSimpleProduct1"/> + </after> + + <!-- Go to the created product page and add it to the cart --> + <amOnPage url="$$simpleProduct1.sku$$.html" stepKey="goToSimpleProductPage"/> + <waitForPageLoad stepKey="waitForSimpleProductPage"/> + <click stepKey="addSimpleProductToCart" selector="{{StorefrontProductActionSection.addToCart}}"/> + <waitForPageLoad stepKey="waitForProductAdded"/> + <see stepKey="seeSuccess" selector="{{AdminCategoryMessagesSection.SuccessMessage}}" userInput="You added"/> + + <!-- Fill in address for CA --> + <amOnPage url="{{CheckoutPage.url}}" stepKey="goToCheckout"/> + <waitForPageLoad stepKey="waitForShippingSection"/> + <fillField selector="{{CheckoutShippingSection.email}}" userInput="{{Simple_US_Customer_CA.email}}" stepKey="enterEmail"/> + <waitForLoadingMaskToDisappear stepKey="waitEmailLoad"/> + <actionGroup ref="LoggedInCheckoutFillNewBillingAddressActionGroup" stepKey="changeAddress"> + <argument name="Address" value="US_Address_CA"/> + </actionGroup> + <click stepKey="clickNext" selector="{{CheckoutShippingSection.next}}"/> + <waitForPageLoad stepKey="waitForAddressToLoad"/> + <!-- Checkout select Check/Money Order payment --> + <actionGroup ref="CheckoutSelectCheckMoneyOrderPaymentActionGroup" stepKey="selectCheckMoneyPayment"/> + <see stepKey="seeAddress" selector="{{CheckoutShippingSection.defaultShipping}}" userInput="{{SimpleTaxCA.state}}"/> + <see stepKey="seeShipTo" selector="{{CheckoutPaymentSection.shipToInformation}}" userInput="{{SimpleTaxCA.state}}"/> + + <!-- Assert that taxes are applied correctly for CA --> + <waitForElementVisible stepKey="waitForOverviewVisible" selector="{{CheckoutPaymentSection.tax}}"/> + <see stepKey="seeTax2" selector="{{CheckoutPaymentSection.tax}}" userInput="$10.15"/> + <click stepKey="expandTax2" selector="{{CheckoutPaymentSection.tax}}"/> + <see stepKey="seeTaxPercent2" selector="{{CheckoutPaymentSection.taxPercentage}}" userInput="({{SimpleTaxCA.rate}}%)"/> + <see stepKey="seeTotalIncl2" selector="{{CheckoutPaymentSection.orderSummaryTotalIncluding}}" userInput="$138.15"/> + <see stepKey="seeTotalExcl2" selector="{{CheckoutPaymentSection.orderSummaryTotalExcluding}}" userInput="$128.00"/> + + <!-- Go back to the shipping page and change the address --> + <click stepKey="goBackToShipping" selector="{{CheckoutShippingSection.shippingTab}}"/> + <actionGroup ref="LoggedInCheckoutFillNewBillingAddressActionGroup" stepKey="changeAddress2"> + <argument name="Address" value="US_Address_NY"/> + </actionGroup> + <click stepKey="clickNext2" selector="{{CheckoutShippingSection.next}}"/> + <waitForPageLoad stepKey="waitForShippingToLoad"/> + <!-- Checkout select Check/Money Order payment --> + <actionGroup ref="CheckoutSelectCheckMoneyOrderPaymentActionGroup" stepKey="selectCheckMoneyPayment2"/> + <see stepKey="seeShipTo2" selector="{{CheckoutPaymentSection.shipToInformation}}" userInput="{{SimpleTaxNY.state}}"/> + + <!-- Assert that taxes are applied correctly for NY --> + <waitForElementVisible stepKey="waitForOverviewVisible2" selector="{{CheckoutPaymentSection.tax}}"/> + <see stepKey="seeTax" selector="{{CheckoutPaymentSection.tax}}" userInput="$10.30"/> + <click stepKey="expandTax" selector="{{CheckoutPaymentSection.tax}}"/> + <see stepKey="seeTaxPercent" selector="{{CheckoutPaymentSection.taxPercentage}}" userInput="({{SimpleTaxNY.rate}}%)"/> + <see stepKey="seeTotalIncl" selector="{{CheckoutPaymentSection.orderSummaryTotalIncluding}}" userInput="$138.30"/> + <see stepKey="seeTotalExcl" selector="{{CheckoutPaymentSection.orderSummaryTotalExcluding}}" userInput="$128.00"/> + </test> +</tests> diff --git a/app/code/Magento/Tax/Test/Mftf/Test/StorefrontTaxQuoteCheckoutTest/StorefrontTaxQuoteCheckoutGuestVirtualTest.xml b/app/code/Magento/Tax/Test/Mftf/Test/StorefrontTaxQuoteCheckoutTest/StorefrontTaxQuoteCheckoutGuestVirtualTest.xml new file mode 100644 index 0000000000000..d1fc0654fc496 --- /dev/null +++ b/app/code/Magento/Tax/Test/Mftf/Test/StorefrontTaxQuoteCheckoutTest/StorefrontTaxQuoteCheckoutGuestVirtualTest.xml @@ -0,0 +1,115 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + /** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +--> +<tests xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/testSchema.xsd"> + <test name="StorefrontTaxQuoteCheckoutGuestVirtualTest"> + <annotations> + <features value="Tax"/> + <stories value="Tax Calculation in One Page Checkout"/> + <title value="Tax for Virtual Product Quote should be recalculated according to inputted data on Checkout flow for Guest Customer"/> + <description value="Tax for Virtual Product Quote should be recalculated according to inputted data on Checkout flow for Guest Customer"/> + <severity value="CRITICAL"/> + <testCaseId value="MC-255"/> + <group value="Tax"/> + </annotations> + <before> + <actionGroup ref="AdminLoginActionGroup" stepKey="loginAsAdmin"/> + <createData entity="VirtualProduct" stepKey="virtualProduct1"/> + + <!-- Fill in rules to display tax in the cart --> + <actionGroup ref="EditTaxConfigurationByUIActionGroup" stepKey="fillDefaultTaxForms"/> + + <!-- Go to tax rule page --> + <amOnPage url="{{AdminTaxRuleGridPage.url}}" stepKey="goToTaxRulePage"/> + <waitForPageLoad stepKey="waitForTaxRatePage"/> + <click stepKey="addNewTaxRate" selector="{{AdminGridMainControls.add}}"/> + <fillField stepKey="fillRuleName" selector="{{AdminTaxRulesSection.ruleName}}" userInput="SampleRule"/> + + <!-- Add NY and CA tax rules --> + <actionGroup ref="AddNewTaxRateNoZipActionGroup" stepKey="addNYTaxRate"> + <argument name="taxCode" value="SimpleTaxNY"/> + </actionGroup> + + <actionGroup ref="AddNewTaxRateNoZipActionGroup" stepKey="addCATaxRate"> + <argument name="taxCode" value="SimpleTaxCA"/> + </actionGroup> + + <click stepKey="clickSave" selector="{{AdminStoresMainActionsSection.saveButton}}"/> + + <magentoCLI command="cron:run --group=index" stepKey="runCronIndexer"/> + </before> + <after> + <!-- Go to the tax rule page and delete the row we created--> + <amOnPage url="{{AdminTaxRuleGridPage.url}}" stepKey="goToTaxRulesPage"/> + <waitForPageLoad stepKey="waitForRulesPage"/> + <actionGroup ref="deleteEntitySecondaryGrid" stepKey="deleteRule"> + <argument name="name" value="SampleRule"/> + <argument name="searchInput" value="{{AdminSecondaryGridSection.taxIdentifierSearch}}"/> + </actionGroup> + + <!-- Go to the tax rate page --> + <amOnPage url="{{AdminTaxRateGridPage.url}}" stepKey="goToTaxRatesPage"/> + <waitForPageLoad stepKey="waitForRatesPage"/> + + <!-- Delete the two tax rates that were created --> + <actionGroup ref="deleteEntitySecondaryGrid" stepKey="deleteNYRate"> + <argument name="name" value="{{SimpleTaxNY.state}}-{{SimpleTaxNY.rate}}"/> + <argument name="searchInput" value="{{AdminSecondaryGridSection.taxIdentifierSearch}}"/> + </actionGroup> + + <actionGroup ref="deleteEntitySecondaryGrid" stepKey="deleteCARate"> + <argument name="name" value="{{SimpleTaxCA.state}}-{{SimpleTaxCA.rate}}"/> + <argument name="searchInput" value="{{AdminSecondaryGridSection.taxIdentifierSearch}}"/> + </actionGroup> + + <!-- Ensure tax won't be shown in the cart --> + <actionGroup ref="ChangeToDefaultTaxConfigurationUIActionGroup" stepKey="changeToDefaultTaxConfiguration"/> + + <amOnPage url="{{AdminLogoutPage.url}}" stepKey="amOnLogoutPage"/> + <deleteData createDataKey="virtualProduct1" stepKey="deleteVirtualProduct1"/> + </after> + + <!-- Go to the created product page and add it to the cart --> + <amOnPage url="$$virtualProduct1.sku$$.html" stepKey="goToVirtualProductPage"/> + <waitForPageLoad stepKey="waitForVirtualProductPage"/> + <click stepKey="addVirtualProductToCart" selector="{{StorefrontProductActionSection.addToCart}}"/> + <waitForPageLoad stepKey="waitForProductAdded"/> + <see stepKey="seeSuccess" selector="{{AdminCategoryMessagesSection.SuccessMessage}}" userInput="You added"/> + + <!-- Assert that taxes are applied correctly for CA --> + <amOnPage url="{{CheckoutPage.url}}" stepKey="goToCheckout"/> + <waitForPageLoad stepKey="waitForShippingSection"/> + + <waitForElementVisible stepKey="waitForOverviewVisible" selector="{{CheckoutPaymentSection.tax}}"/> + <see stepKey="seeTax2" selector="{{CheckoutPaymentSection.tax}}" userInput="$8.25"/> + <click stepKey="expandTax2" selector="{{CheckoutPaymentSection.tax}}"/> + <see stepKey="seeTaxPercent2" selector="{{CheckoutPaymentSection.taxPercentage}}" userInput="({{SimpleTaxCA.rate}}%)"/> + <see stepKey="seeTotalIncl2" selector="{{CheckoutPaymentSection.orderSummaryTotalIncluding}}" userInput="$108.24"/> + <see stepKey="seeTotalExcl2" selector="{{CheckoutPaymentSection.orderSummaryTotalExcluding}}" userInput="$$virtualProduct1.price$$"/> + + <!-- Change the address --> + <actionGroup ref="GuestCheckoutSelectPaymentAndFillNewBillingAddressActionGroup" stepKey="changeAddress"> + <argument name="customerVar" value="Simple_US_Customer_NY"/> + <argument name="customerAddressVar" value="US_Address_NY"/> + <argument name="paymentMethod" value="Check / Money order"/> + </actionGroup> + <click stepKey="saveAddress" selector="{{CheckoutShippingSection.updateAddress}}"/> + + <waitForPageLoad stepKey="waitForAddressSaved"/> + <see stepKey="seeEditButton" selector="{{CheckoutShippingSection.editAddressButton}}" userInput="Edit"/> + <see stepKey="seeAddress2" selector="{{CheckoutShippingSection.defaultShipping}}" userInput="{{SimpleTaxNY.state}}"/> + + <!-- Assert that taxes are applied correctly for NY --> + <waitForElementVisible stepKey="waitForOverviewVisible2" selector="{{CheckoutPaymentSection.tax}}"/> + <see stepKey="seeTax" selector="{{CheckoutPaymentSection.tax}}" userInput="$8.37"/> + <click stepKey="expandTax" selector="{{CheckoutPaymentSection.tax}}"/> + <see stepKey="seeTaxPercent" selector="{{CheckoutPaymentSection.taxPercentage}}" userInput="({{SimpleTaxNY.rate}}%)"/> + <see stepKey="seeTotalIncl" selector="{{CheckoutPaymentSection.orderSummaryTotalIncluding}}" userInput="$108.36"/> + <see stepKey="seeTotalExcl" selector="{{CheckoutPaymentSection.orderSummaryTotalExcluding}}" userInput="$$virtualProduct1.price$$"/> + </test> +</tests> diff --git a/app/code/Magento/Tax/Test/Mftf/Test/StorefrontTaxQuoteCheckoutTest/StorefrontTaxQuoteCheckoutLoggedInSimpleTest.xml b/app/code/Magento/Tax/Test/Mftf/Test/StorefrontTaxQuoteCheckoutTest/StorefrontTaxQuoteCheckoutLoggedInSimpleTest.xml new file mode 100644 index 0000000000000..18a1a11d35fd2 --- /dev/null +++ b/app/code/Magento/Tax/Test/Mftf/Test/StorefrontTaxQuoteCheckoutTest/StorefrontTaxQuoteCheckoutLoggedInSimpleTest.xml @@ -0,0 +1,137 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + /** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +--> +<tests xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/testSchema.xsd"> + <test name="StorefrontTaxQuoteCheckoutLoggedInSimpleTest"> + <annotations> + <features value="Tax"/> + <stories value="Tax Calculation in One Page Checkout"/> + <title value="Tax for Simple Product Quote should be recalculated according to inputted data on Checkout flow for Logged in Customer"/> + <description value="Tax for Simple Product Quote should be recalculated according to inputted data on Checkout flow for Logged in Customer"/> + <severity value="CRITICAL"/> + <testCaseId value="MC-256"/> + <group value="Tax"/> + </annotations> + <before> + <actionGroup ref="AdminLoginActionGroup" stepKey="loginAsAdmin"/> + <createData entity="SimpleProduct2" stepKey="simpleProduct1"/> + + <!-- Fill in rules to display tax in the cart --> + <actionGroup ref="EditTaxConfigurationByUIActionGroup" stepKey="fillDefaultTaxForms"/> + + <!-- Go to tax rule page --> + <amOnPage url="{{AdminTaxRuleGridPage.url}}" stepKey="goToTaxRulePage"/> + <waitForPageLoad stepKey="waitForTaxRatePage"/> + <click stepKey="addNewTaxRate" selector="{{AdminGridMainControls.add}}"/> + <fillField stepKey="fillRuleName" selector="{{AdminTaxRulesSection.ruleName}}" userInput="SampleRule"/> + + <!-- Add NY and CA tax rules --> + <actionGroup ref="AddNewTaxRateNoZipActionGroup" stepKey="addNYTaxRate"> + <argument name="taxCode" value="SimpleTaxNY"/> + </actionGroup> + + <actionGroup ref="AddNewTaxRateNoZipActionGroup" stepKey="addCATaxRate"> + <argument name="taxCode" value="SimpleTaxCA"/> + </actionGroup> + + <click stepKey="clickSave" selector="{{AdminStoresMainActionsSection.saveButton}}"/> + + <magentoCLI command="cron:run --group=index" stepKey="runCronIndexer"/> + </before> + <after> + <!-- Go to the tax rule page and delete the row we created--> + <amOnPage url="{{AdminTaxRuleGridPage.url}}" stepKey="goToTaxRulesPage"/> + <waitForPageLoad stepKey="waitForRulesPage"/> + <actionGroup ref="deleteEntitySecondaryGrid" stepKey="deleteRule"> + <argument name="name" value="SampleRule"/> + <argument name="searchInput" value="{{AdminSecondaryGridSection.taxIdentifierSearch}}"/> + </actionGroup> + + <!-- Go to the tax rate page --> + <amOnPage url="{{AdminTaxRateGridPage.url}}" stepKey="goToTaxRatesPage"/> + <waitForPageLoad stepKey="waitForRatesPage"/> + + <!-- Delete the two tax rates that were created --> + <actionGroup ref="deleteEntitySecondaryGrid" stepKey="deleteNYRate"> + <argument name="name" value="{{SimpleTaxNY.state}}-{{SimpleTaxNY.rate}}"/> + <argument name="searchInput" value="{{AdminSecondaryGridSection.taxIdentifierSearch}}"/> + </actionGroup> + + <actionGroup ref="deleteEntitySecondaryGrid" stepKey="deleteCARate"> + <argument name="name" value="{{SimpleTaxCA.state}}-{{SimpleTaxCA.rate}}"/> + <argument name="searchInput" value="{{AdminSecondaryGridSection.taxIdentifierSearch}}"/> + </actionGroup> + + <!-- Ensure tax won't be shown in the cart --> + <actionGroup ref="ChangeToDefaultTaxConfigurationUIActionGroup" stepKey="changeToDefaultTaxConfiguration"/> + + <amOnPage url="{{AdminLogoutPage.url}}" stepKey="amOnLogoutPage"/> + <deleteData createDataKey="simpleProduct1" stepKey="deleteSimpleProduct1"/> + </after> + + <!-- Fill out form for a new user with address --> + <actionGroup ref="StorefrontOpenCustomerAccountCreatePageActionGroup" stepKey="openCreateAccountPage"/> + <actionGroup ref="StorefrontFillCustomerAccountCreationFormActionGroup" stepKey="fillCreateAccountForm"> + <argument name="customer" value="Simple_US_Customer_NY"/> + </actionGroup> + <actionGroup ref="StorefrontClickCreateAnAccountCustomerAccountCreationFormActionGroup" stepKey="submitCreateAccountForm"/> + <actionGroup ref="AssertMessageCustomerCreateAccountActionGroup" stepKey="seeSuccessMessage"> + <argument name="messageType" value="success"/> + <argument name="message" value="Thank you for registering with Main Website Store."/> + </actionGroup> + + <actionGroup ref="EnterCustomerAddressInfoActionGroup" stepKey="enterAddressInfo"> + <argument name="Address" value="US_Address_NY"/> + </actionGroup> + + <!-- Go to the created product page and add it to the cart --> + <amOnPage url="$$simpleProduct1.sku$$.html" stepKey="goToSimpleProductPage"/> + <waitForPageLoad stepKey="waitForSimpleProductPage"/> + <click stepKey="addSimpleProductToCart" selector="{{StorefrontProductActionSection.addToCart}}"/> + <waitForPageLoad stepKey="waitForProductAdded"/> + <see stepKey="seeSuccess" selector="{{AdminCategoryMessagesSection.SuccessMessage}}" userInput="You added"/> + + <!-- Assert that taxes are applied correctly for NY --> + <amOnPage url="{{CheckoutPage.url}}" stepKey="goToCheckout"/> + <waitForPageLoad stepKey="waitForShippingSection"/> + <see stepKey="seeAddress" selector="{{CheckoutShippingSection.selectedShippingAddress}}" userInput="{{SimpleTaxNY.state}}"/> + <click stepKey="clickNext" selector="{{CheckoutShippingSection.next}}"/> + <waitForPageLoad stepKey="waitForReviewAndPayments"/> + + <waitForElementVisible stepKey="waitForOverviewVisible" selector="{{CheckoutPaymentSection.tax}}"/> + <see stepKey="seeTax" selector="{{CheckoutPaymentSection.tax}}" userInput="$10.30"/> + <click stepKey="expandTax" selector="{{CheckoutPaymentSection.tax}}"/> + <see stepKey="seeTaxPercent" selector="{{CheckoutPaymentSection.taxPercentage}}" userInput="({{SimpleTaxNY.rate}}%)"/> + <see stepKey="seeTotalIncl" selector="{{CheckoutPaymentSection.orderSummaryTotalIncluding}}" userInput="$138.30"/> + <see stepKey="seeTotalExcl" selector="{{CheckoutPaymentSection.orderSummaryTotalExcluding}}" userInput="$128.00"/> + + <!-- Go back to the shipping page and change the address --> + <click stepKey="goBackToShipping" selector="{{CheckoutShippingSection.shippingTab}}"/> + <click stepKey="addNewAddress" selector="{{CheckoutShippingSection.newAddressButton}}"/> + <waitForPageLoad stepKey="waitForAddressPopUp"/> + + <actionGroup ref="LoggedInCheckoutFillNewBillingAddressActionGroup" stepKey="changeAddress"> + <argument name="Address" value="US_Address_CA"/> + <argument name="classPrefix" value="._show"/> + </actionGroup> + <click stepKey="saveAddress" selector="{{CheckoutShippingSection.saveAddress}}"/> + + <waitForPageLoad stepKey="waitForAddressSaved"/> + <see stepKey="seeAddress2" selector="{{CheckoutShippingSection.selectedShippingAddress}}" userInput="{{SimpleTaxCA.state}}"/> + <click stepKey="clickNext2" selector="{{CheckoutShippingSection.next}}"/> + <waitForPageLoad stepKey="waitForReviewAndPayments2"/> + + <!-- Assert that taxes are applied correctly for CA --> + <waitForElementVisible stepKey="waitForOverviewVisible2" selector="{{CheckoutPaymentSection.tax}}"/> + <see stepKey="seeTax2" selector="{{CheckoutPaymentSection.tax}}" userInput="$10.15"/> + <click stepKey="expandTax2" selector="{{CheckoutPaymentSection.tax}}"/> + <see stepKey="seeTaxPercent2" selector="{{CheckoutPaymentSection.taxPercentage}}" userInput="({{SimpleTaxCA.rate}}%)"/> + <see stepKey="seeTotalIncl2" selector="{{CheckoutPaymentSection.orderSummaryTotalIncluding}}" userInput="$138.15"/> + <see stepKey="seeTotalExcl2" selector="{{CheckoutPaymentSection.orderSummaryTotalExcluding}}" userInput="$128.00"/> + </test> +</tests> diff --git a/app/code/Magento/Tax/Test/Mftf/Test/StorefrontTaxQuoteCheckoutTest/StorefrontTaxQuoteCheckoutLoggedInVirtualTest.xml b/app/code/Magento/Tax/Test/Mftf/Test/StorefrontTaxQuoteCheckoutTest/StorefrontTaxQuoteCheckoutLoggedInVirtualTest.xml new file mode 100644 index 0000000000000..35a483da7f690 --- /dev/null +++ b/app/code/Magento/Tax/Test/Mftf/Test/StorefrontTaxQuoteCheckoutTest/StorefrontTaxQuoteCheckoutLoggedInVirtualTest.xml @@ -0,0 +1,137 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + /** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +--> +<tests xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/testSchema.xsd"> + <test name="StorefrontTaxQuoteCheckoutLoggedInVirtualTest"> + <annotations> + <features value="Tax"/> + <stories value="Tax Calculation in One Page Checkout"/> + <title value="Tax for Virtual Product Quote should be recalculated according to inputted data on Checkout flow for Logged in Customer"/> + <description value="Tax for Virtual Product Quote should be recalculated according to inputted data on Checkout flow for Logged in Customer"/> + <severity value="CRITICAL"/> + <testCaseId value="MC-259"/> + <group value="Tax"/> + </annotations> + <before> + <actionGroup ref="AdminLoginActionGroup" stepKey="loginAsAdmin"/> + <createData entity="VirtualProduct" stepKey="virtualProduct1"/> + + <!-- Fill in rules to display tax in the cart --> + <actionGroup ref="EditTaxConfigurationByUIActionGroup" stepKey="fillDefaultTaxForms"/> + + <!-- Go to tax rule page --> + <amOnPage url="{{AdminTaxRuleGridPage.url}}" stepKey="goToTaxRulePage"/> + <waitForPageLoad stepKey="waitForTaxRatePage"/> + <click stepKey="addNewTaxRate" selector="{{AdminGridMainControls.add}}"/> + <fillField stepKey="fillRuleName" selector="{{AdminTaxRulesSection.ruleName}}" userInput="SampleRule"/> + + <!-- Add NY and CA tax rules --> + <actionGroup ref="AddNewTaxRateNoZipActionGroup" stepKey="addNYTaxRate"> + <argument name="taxCode" value="SimpleTaxNY"/> + </actionGroup> + + <actionGroup ref="AddNewTaxRateNoZipActionGroup" stepKey="addCATaxRate"> + <argument name="taxCode" value="SimpleTaxCA"/> + </actionGroup> + + <click stepKey="clickSave" selector="{{AdminStoresMainActionsSection.saveButton}}"/> + + <magentoCLI command="cron:run --group=index" stepKey="runCronIndexer"/> + </before> + <after> + <!-- Go to the tax rule page and delete the row we created--> + <amOnPage url="{{AdminTaxRuleGridPage.url}}" stepKey="goToTaxRulesPage"/> + <waitForPageLoad stepKey="waitForRulesPage"/> + <actionGroup ref="deleteEntitySecondaryGrid" stepKey="deleteRule"> + <argument name="name" value="SampleRule"/> + <argument name="searchInput" value="{{AdminSecondaryGridSection.taxIdentifierSearch}}"/> + </actionGroup> + + <!-- Go to the tax rate page --> + <amOnPage url="{{AdminTaxRateGridPage.url}}" stepKey="goToTaxRatesPage"/> + <waitForPageLoad stepKey="waitForRatesPage"/> + + <!-- Delete the two tax rates that were created --> + <actionGroup ref="deleteEntitySecondaryGrid" stepKey="deleteNYRate"> + <argument name="name" value="{{SimpleTaxNY.state}}-{{SimpleTaxNY.rate}}"/> + <argument name="searchInput" value="{{AdminSecondaryGridSection.taxIdentifierSearch}}"/> + </actionGroup> + + <actionGroup ref="deleteEntitySecondaryGrid" stepKey="deleteCARate"> + <argument name="name" value="{{SimpleTaxCA.state}}-{{SimpleTaxCA.rate}}"/> + <argument name="searchInput" value="{{AdminSecondaryGridSection.taxIdentifierSearch}}"/> + </actionGroup> + + <!-- Ensure tax won't be shown in the cart --> + <actionGroup ref="ChangeToDefaultTaxConfigurationUIActionGroup" stepKey="changeToDefaultTaxConfiguration"/> + + <amOnPage url="{{AdminLogoutPage.url}}" stepKey="amOnLogoutPage"/> + <deleteData createDataKey="virtualProduct1" stepKey="deleteVirtualProduct1"/> + </after> + + <!-- Fill out form for a new user with address --> + <actionGroup ref="StorefrontOpenCustomerAccountCreatePageActionGroup" stepKey="openCreateAccountPage"/> + <actionGroup ref="StorefrontFillCustomerAccountCreationFormActionGroup" stepKey="fillCreateAccountForm"> + <argument name="customer" value="Simple_US_Customer_NY"/> + </actionGroup> + <actionGroup ref="StorefrontClickCreateAnAccountCustomerAccountCreationFormActionGroup" stepKey="submitCreateAccountForm"/> + <actionGroup ref="AssertMessageCustomerCreateAccountActionGroup" stepKey="seeSuccessMessage"> + <argument name="messageType" value="success"/> + <argument name="message" value="Thank you for registering with Main Website Store."/> + </actionGroup> + + <actionGroup ref="EnterCustomerAddressInfoActionGroup" stepKey="enterAddressInfo"> + <argument name="Address" value="US_Address_NY"/> + </actionGroup> + + <!-- Go to the created product page and add it to the cart --> + <amOnPage url="$$virtualProduct1.sku$$.html" stepKey="goToVirtualProductPage"/> + <waitForPageLoad stepKey="waitForVirtualProductPage"/> + <click stepKey="addVirtualProductToCart" selector="{{StorefrontProductActionSection.addToCart}}"/> + <waitForPageLoad stepKey="waitForProductAdded"/> + <see stepKey="seeSuccess" selector="{{AdminCategoryMessagesSection.SuccessMessage}}" userInput="You added"/> + + <!-- Assert that taxes are applied correctly for NY --> + <amOnPage url="{{CheckoutPage.url}}" stepKey="goToCheckout"/> + <waitForPageLoad stepKey="waitForShippingSection"/> + <!-- Checkout select Check/Money Order payment --> + <actionGroup ref="CheckoutSelectCheckMoneyOrderPaymentActionGroup" stepKey="selectCheckMoneyPayment"/> + <see stepKey="seeAddress" selector="{{CheckoutShippingSection.defaultShipping}}" userInput="{{SimpleTaxNY.state}}"/> + + <waitForElementVisible stepKey="waitForOverviewVisible" selector="{{CheckoutPaymentSection.tax}}"/> + <see stepKey="seeTax" selector="{{CheckoutPaymentSection.tax}}" userInput="$8.37"/> + <click stepKey="expandTax" selector="{{CheckoutPaymentSection.tax}}"/> + <see stepKey="seeTaxPercent" selector="{{CheckoutPaymentSection.taxPercentage}}" userInput="({{SimpleTaxNY.rate}}%)"/> + <see stepKey="seeTotalIncl" selector="{{CheckoutPaymentSection.orderSummaryTotalIncluding}}" userInput="$108.36"/> + <see stepKey="seeTotalExcl" selector="{{CheckoutPaymentSection.orderSummaryTotalExcluding}}" userInput="$$virtualProduct1.price$$"/> + + <!-- Change the address --> + <click stepKey="editAddress" selector="{{CheckoutShippingSection.editAddressButton}}"/> + <selectOption stepKey="addNewAddress" selector="{{CheckoutShippingSection.addressDropdown}}" userInput="New Address"/> + <waitForPageLoad stepKey="waitForNewAddressForm"/> + + <actionGroup ref="LoggedInCheckoutFillNewBillingAddressActionGroup" stepKey="changeAddress"> + <argument name="Address" value="US_Address_CA"/> + <argument name="classPrefix" value="[aria-hidden=false]"/> + </actionGroup> + <click stepKey="saveAddress" selector="{{CheckoutShippingSection.updateAddress}}"/> + + <waitForPageLoad stepKey="waitForAddressSaved"/> + <!-- Checkout select Check/Money Order payment --> + <actionGroup ref="CheckoutSelectCheckMoneyOrderPaymentActionGroup" stepKey="selectCheckMoneyPayment2"/> + <see stepKey="seeAddress2" selector="{{CheckoutShippingSection.defaultShipping}}" userInput="{{SimpleTaxCA.state}}"/> + + <!-- Assert that taxes are applied correctly for CA --> + <waitForElementVisible stepKey="waitForOverviewVisible2" selector="{{CheckoutPaymentSection.tax}}"/> + <see stepKey="seeTax2" selector="{{CheckoutPaymentSection.tax}}" userInput="$8.25"/> + <click stepKey="expandTax2" selector="{{CheckoutPaymentSection.tax}}"/> + <see stepKey="seeTaxPercent2" selector="{{CheckoutPaymentSection.taxPercentage}}" userInput="({{SimpleTaxCA.rate}}%)"/> + <see stepKey="seeTotalIncl2" selector="{{CheckoutPaymentSection.orderSummaryTotalIncluding}}" userInput="$108.24"/> + <see stepKey="seeTotalExcl2" selector="{{CheckoutPaymentSection.orderSummaryTotalExcluding}}" userInput="$$virtualProduct1.price$$"/> + </test> +</tests> diff --git a/app/code/Magento/Tax/Test/Mftf/Test/Update01TaxRateEntityTest.xml b/app/code/Magento/Tax/Test/Mftf/Test/Update01TaxRateEntityTest.xml index 2ed31c2e20488..306216422adea 100644 --- a/app/code/Magento/Tax/Test/Mftf/Test/Update01TaxRateEntityTest.xml +++ b/app/code/Magento/Tax/Test/Mftf/Test/Update01TaxRateEntityTest.xml @@ -20,7 +20,7 @@ </annotations> <before> <createData entity="defaultTaxRate" stepKey="initialTaxRate"/> - <actionGroup ref="LoginAsAdmin" stepKey="loginAsAdmin"/> + <actionGroup ref="AdminLoginActionGroup" stepKey="loginAsAdmin"/> </before> <after> <deleteData stepKey="deleteTaxRate" createDataKey="initialTaxRate" /> diff --git a/app/code/Magento/Tax/Test/Mftf/Test/Update100TaxRateEntityTest.xml b/app/code/Magento/Tax/Test/Mftf/Test/Update100TaxRateEntityTest.xml index 27a7f2c51724e..c22bab774de29 100644 --- a/app/code/Magento/Tax/Test/Mftf/Test/Update100TaxRateEntityTest.xml +++ b/app/code/Magento/Tax/Test/Mftf/Test/Update100TaxRateEntityTest.xml @@ -20,7 +20,7 @@ </annotations> <before> <createData entity="defaultTaxRate" stepKey="initialTaxRate"/> - <actionGroup ref="LoginAsAdmin" stepKey="loginAsAdmin"/> + <actionGroup ref="AdminLoginActionGroup" stepKey="loginAsAdmin"/> </before> <after> <deleteData stepKey="deleteTaxRate" createDataKey="initialTaxRate" /> diff --git a/app/code/Magento/Tax/Test/Mftf/Test/Update1299TaxRateEntityTest.xml b/app/code/Magento/Tax/Test/Mftf/Test/Update1299TaxRateEntityTest.xml index aa46a5fdae0e1..6f93d07b76eed 100644 --- a/app/code/Magento/Tax/Test/Mftf/Test/Update1299TaxRateEntityTest.xml +++ b/app/code/Magento/Tax/Test/Mftf/Test/Update1299TaxRateEntityTest.xml @@ -20,7 +20,7 @@ </annotations> <before> <createData entity="defaultTaxRate" stepKey="initialTaxRate"/> - <actionGroup ref="LoginAsAdmin" stepKey="loginAsAdmin"/> + <actionGroup ref="AdminLoginActionGroup" stepKey="loginAsAdmin"/> </before> <after> <deleteData stepKey="deleteTaxRate" createDataKey="initialTaxRate" /> diff --git a/app/code/Magento/Tax/Test/Mftf/Test/UpdateAnyRegionTaxRateEntityTest.xml b/app/code/Magento/Tax/Test/Mftf/Test/UpdateAnyRegionTaxRateEntityTest.xml index d7b25da6c14ae..c3986e6a8d0cc 100644 --- a/app/code/Magento/Tax/Test/Mftf/Test/UpdateAnyRegionTaxRateEntityTest.xml +++ b/app/code/Magento/Tax/Test/Mftf/Test/UpdateAnyRegionTaxRateEntityTest.xml @@ -20,7 +20,7 @@ </annotations> <before> <createData entity="defaultTaxRate" stepKey="initialTaxRate"/> - <actionGroup ref="LoginAsAdmin" stepKey="loginAsAdmin"/> + <actionGroup ref="AdminLoginActionGroup" stepKey="loginAsAdmin"/> </before> <after> <deleteData stepKey="deleteTaxRate" createDataKey="initialTaxRate" /> diff --git a/app/code/Magento/Tax/Test/Mftf/Test/UpdateDecimalTaxRateEntityTest.xml b/app/code/Magento/Tax/Test/Mftf/Test/UpdateDecimalTaxRateEntityTest.xml index 3002452196904..fb1eff1d74067 100644 --- a/app/code/Magento/Tax/Test/Mftf/Test/UpdateDecimalTaxRateEntityTest.xml +++ b/app/code/Magento/Tax/Test/Mftf/Test/UpdateDecimalTaxRateEntityTest.xml @@ -20,7 +20,7 @@ </annotations> <before> <createData entity="defaultTaxRate" stepKey="initialTaxRate"/> - <actionGroup ref="LoginAsAdmin" stepKey="loginAsAdmin"/> + <actionGroup ref="AdminLoginActionGroup" stepKey="loginAsAdmin"/> </before> <after> <deleteData stepKey="deleteTaxRate" createDataKey="initialTaxRate" /> diff --git a/app/code/Magento/Tax/Test/Mftf/Test/UpdateLargeTaxRateEntityTest.xml b/app/code/Magento/Tax/Test/Mftf/Test/UpdateLargeTaxRateEntityTest.xml index 6c81a6aeb3f11..1f0406244a926 100644 --- a/app/code/Magento/Tax/Test/Mftf/Test/UpdateLargeTaxRateEntityTest.xml +++ b/app/code/Magento/Tax/Test/Mftf/Test/UpdateLargeTaxRateEntityTest.xml @@ -20,7 +20,7 @@ </annotations> <before> <createData entity="defaultTaxRate" stepKey="initialTaxRate"/> - <actionGroup ref="LoginAsAdmin" stepKey="loginAsAdmin"/> + <actionGroup ref="AdminLoginActionGroup" stepKey="loginAsAdmin"/> </before> <after> <deleteData stepKey="deleteTaxRate" createDataKey="initialTaxRate" /> diff --git a/app/code/Magento/Tax/Test/Unit/App/Action/ContextPluginTest.php b/app/code/Magento/Tax/Test/Unit/App/Action/ContextPluginTest.php index a6c7e9bb8685a..8601bdc175ae8 100644 --- a/app/code/Magento/Tax/Test/Unit/App/Action/ContextPluginTest.php +++ b/app/code/Magento/Tax/Test/Unit/App/Action/ContextPluginTest.php @@ -5,81 +5,105 @@ */ namespace Magento\Tax\Test\Unit\App\Action; +use Magento\Customer\Model\Session; +use Magento\Framework\App\Http\Context as HttpContext; +use Magento\Framework\App\Test\Unit\Action\Stub\ActionStub; +use Magento\Framework\Module\Manager as ModuleManager; +use Magento\Framework\TestFramework\Unit\Helper\ObjectManager as ObjectManagerHelper; +use Magento\PageCache\Model\Config as PageCacheConfig; +use Magento\Tax\Helper\Data as TaxHelper; +use Magento\Tax\Model\App\Action\ContextPlugin as TaxContextPlugin; +use Magento\Tax\Model\Calculation; +use Magento\Weee\Helper\Data as WeeeHelper; +use Magento\Weee\Model\Tax; +use PHPUnit\Framework\MockObject\MockObject; +use PHPUnit\Framework\TestCase; + /** - * Context plugin test + * Unit Tests to cover ContextPlugin */ -class ContextPluginTest extends \PHPUnit\Framework\TestCase +class ContextPluginTest extends TestCase { /** - * @var \Magento\Tax\Helper\Data + * @var ObjectManagerHelper + */ + private $objectManager; + + /** + * @var TaxHelper|MockObject */ protected $taxHelperMock; /** - * @var \Magento\Weee\Helper\Data + * @var WeeeHelper|MockObject */ protected $weeeHelperMock; /** - * @var \Magento\Weee\Model\Tax + * @var Tax|MockObject */ protected $weeeTaxMock; /** - * @var \Magento\Framework\App\Http\Context + * @var HttpContext|MockObject */ protected $httpContextMock; /** - * @var \Magento\Tax\Model\Calculation\Proxy + * @var Calculation|MockObject */ protected $taxCalculationMock; /** * Module manager * - * @var \Magento\Framework\Module\Manager + * @var ModuleManager|MockObject */ private $moduleManagerMock; /** * Cache config * - * @var \Magento\PageCache\Model\Config + * @var PageCacheConfig|MockObject */ private $cacheConfigMock; /** - * @var \Magento\Tax\Model\App\Action\ContextPlugin + * @var Session|MockObject + */ + private $customerSessionMock; + + /** + * @var TaxContextPlugin */ protected $contextPlugin; protected function setUp() { - $this->objectManager = new \Magento\Framework\TestFramework\Unit\Helper\ObjectManager($this); + $this->objectManager = new ObjectManagerHelper($this); - $this->taxHelperMock = $this->getMockBuilder(\Magento\Tax\Helper\Data::class) + $this->taxHelperMock = $this->getMockBuilder(TaxHelper::class) ->disableOriginalConstructor() ->getMock(); - $this->weeeHelperMock = $this->getMockBuilder(\Magento\Weee\Helper\Data::class) + $this->weeeHelperMock = $this->getMockBuilder(WeeeHelper::class) ->disableOriginalConstructor() ->getMock(); - $this->weeeTaxMock = $this->getMockBuilder(\Magento\Weee\Model\Tax::class) + $this->weeeTaxMock = $this->getMockBuilder(Tax::class) ->disableOriginalConstructor() ->getMock(); - $this->httpContextMock = $this->getMockBuilder(\Magento\Framework\App\Http\Context::class) + $this->httpContextMock = $this->getMockBuilder(HttpContext::class) ->disableOriginalConstructor() ->getMock(); - $this->taxCalculationMock = $this->getMockBuilder(\Magento\Tax\Model\Calculation\Proxy::class) + $this->taxCalculationMock = $this->getMockBuilder(Calculation::class) ->disableOriginalConstructor() ->setMethods(['getTaxRates']) ->getMock(); - $this->customerSessionMock = $this->getMockBuilder(\Magento\Customer\Model\Session::class) + $this->customerSessionMock = $this->getMockBuilder(Session::class) ->disableOriginalConstructor() ->setMethods( [ @@ -89,16 +113,16 @@ protected function setUp() ) ->getMock(); - $this->moduleManagerMock = $this->getMockBuilder(\Magento\Framework\Module\Manager::class) + $this->moduleManagerMock = $this->getMockBuilder(ModuleManager::class) ->disableOriginalConstructor() ->getMock(); - $this->cacheConfigMock = $this->getMockBuilder(\Magento\PageCache\Model\Config::class) + $this->cacheConfigMock = $this->getMockBuilder(PageCacheConfig::class) ->disableOriginalConstructor() ->getMock(); $this->contextPlugin = $this->objectManager->getObject( - \Magento\Tax\Model\App\Action\ContextPlugin::class, + TaxContextPlugin::class, [ 'customerSession' => $this->customerSessionMock, 'httpContext' => $this->httpContextMock, @@ -116,9 +140,9 @@ protected function setUp() * @param bool $cache * @param bool $taxEnabled * @param bool $loggedIn - * @dataProvider beforeDispatchDataProvider + * @dataProvider beforeExecuteDataProvider */ - public function testBeforeDispatch($cache, $taxEnabled, $loggedIn) + public function testBeforeExecute($cache, $taxEnabled, $loggedIn) { $this->customerSessionMock->expects($this->any()) ->method('isLoggedIn') @@ -163,9 +187,8 @@ public function testBeforeDispatch($cache, $taxEnabled, $loggedIn) ->with('tax_rates', [], 0); } - $action = $this->objectManager->getObject(\Magento\Framework\App\Test\Unit\Action\Stub\ActionStub::class); - $request = $this->createPartialMock(\Magento\Framework\App\Request\Http::class, ['getActionName']); - $result = $this->contextPlugin->beforeDispatch($action, $request); + $action = $this->objectManager->getObject(ActionStub::class); + $result = $this->contextPlugin->beforeExecute($action); $this->assertNull($result); } else { $this->assertFalse($loggedIn); @@ -175,7 +198,7 @@ public function testBeforeDispatch($cache, $taxEnabled, $loggedIn) /** * @return array */ - public function beforeDispatchDataProvider() + public function beforeExecuteDataProvider() { return [ [false, false, false], diff --git a/app/code/Magento/Tax/Test/Unit/Model/Layout/DepersonalizePluginTest.php b/app/code/Magento/Tax/Test/Unit/Model/Layout/DepersonalizePluginTest.php new file mode 100644 index 0000000000000..26ceef5f6ac3b --- /dev/null +++ b/app/code/Magento/Tax/Test/Unit/Model/Layout/DepersonalizePluginTest.php @@ -0,0 +1,166 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +declare(strict_types=1); + +namespace Magento\Tax\Test\Unit\Model\Layout; + +use Magento\Customer\Model\Session as CustomerSession; +use Magento\Framework\TestFramework\Unit\Helper\ObjectManager as ObjectManagerHelper; +use Magento\Framework\View\LayoutInterface; +use Magento\PageCache\Model\DepersonalizeChecker; +use Magento\Tax\Model\Layout\DepersonalizePlugin; +use PHPUnit\Framework\MockObject\MockObject; +use PHPUnit\Framework\TestCase; + +/** + * Unit tests for \Magento\Tax\Model\Layout\DepersonalizePlugin class. + */ +class DepersonalizePluginTest extends TestCase +{ + /** + * @var CustomerSession|MockObject + */ + private $customerSessionMock; + + /** + * @var DepersonalizePlugin + */ + private $plugin; + + /** + * @var DepersonalizeChecker|MockObject + */ + private $depersonalizeCheckerMock; + + /** + * @var LayoutInterface|MockObject + */ + private $layoutMock; + + /** + * @inheritdoc + */ + protected function setUp() + { + $this->customerSessionMock = $this->createPartialMock( + CustomerSession::class, + [ + 'getDefaultTaxBillingAddress', + 'getDefaultTaxShippingAddress', + 'getCustomerTaxClassId', + 'setDefaultTaxBillingAddress', + 'setDefaultTaxShippingAddress', + 'setCustomerTaxClassId', + ] + ); + $this->depersonalizeCheckerMock = $this->createMock(DepersonalizeChecker::class); + $this->layoutMock = $this->getMockForAbstractClass(LayoutInterface::class); + + $this->plugin = (new ObjectManagerHelper($this))->getObject( + DepersonalizePlugin::class, + [ + 'customerSession' => $this->customerSessionMock, + 'depersonalizeChecker' => $this->depersonalizeCheckerMock, + ] + ); + } + + /** + * Test beforeGenerateXml method when depersonalization is needed. + * + * @return void + */ + public function testBeforeGenerateXml(): void + { + $this->depersonalizeCheckerMock->expects($this->once())->method('checkIfDepersonalize')->willReturn(true); + $this->customerSessionMock->expects($this->once())->method('getDefaultTaxBillingAddress'); + $this->customerSessionMock->expects($this->once())->method('getDefaultTaxShippingAddress'); + $this->customerSessionMock->expects($this->once())->method('getCustomerTaxClassId'); + $this->plugin->beforeGenerateXml($this->layoutMock); + } + + /** + * Test beforeGenerateXml method when depersonalization is not needed. + * + * @return void + */ + public function testBeforeGenerateXmlNoDepersonalize(): void + { + $this->depersonalizeCheckerMock->expects($this->once())->method('checkIfDepersonalize')->willReturn(false); + $this->customerSessionMock->expects($this->never())->method('getDefaultTaxBillingAddress'); + $this->customerSessionMock->expects($this->never())->method('getDefaultTaxShippingAddress'); + $this->customerSessionMock->expects($this->never())->method('getCustomerTaxClassId'); + $this->plugin->beforeGenerateXml($this->layoutMock); + } + + /** + * Test afterGenerateElements method when depersonalization is needed. + * + * @return void + */ + public function testAfterGenerateElements(): void + { + $this->depersonalizeCheckerMock->expects($this->once())->method('checkIfDepersonalize')->willReturn(true); + $this->customerSessionMock->expects($this->once())->method('setDefaultTaxBillingAddress'); + $this->customerSessionMock->expects($this->once())->method('setDefaultTaxShippingAddress'); + $this->customerSessionMock->expects($this->once())->method('setCustomerTaxClassId'); + $this->assertEmpty($this->plugin->afterGenerateElements($this->layoutMock)); + } + + /** + * Test afterGenerateElements method when depersonalization is not needed. + * + * @return void + */ + public function testAfterGenerateElementsNoDepersonalize(): void + { + $this->depersonalizeCheckerMock->expects($this->once())->method('checkIfDepersonalize')->willReturn(false); + $this->customerSessionMock->expects($this->never())->method('setDefaultTaxBillingAddress'); + $this->customerSessionMock->expects($this->never())->method('setDefaultTaxShippingAddress'); + $this->customerSessionMock->expects($this->never())->method('setCustomerTaxClassId'); + $this->assertEmpty($this->plugin->afterGenerateElements($this->layoutMock)); + } + + /** + * Test beforeGenerateXml and afterGenerateElements methods. + * + * @return void + */ + public function testBeforeAndAfter(): void + { + $defaultTaxBillingAddress = []; + $defaultTaxShippingAddress = []; + $customerTaxClassId = 1; + + $this->depersonalizeCheckerMock->expects($this->exactly(2)) + ->method('checkIfDepersonalize') + ->willReturn(true); + + $this->customerSessionMock->expects($this->once()) + ->method('getDefaultTaxBillingAddress') + ->willReturn($defaultTaxBillingAddress); + $this->customerSessionMock->expects($this->once()) + ->method('getDefaultTaxShippingAddress') + ->willReturn($defaultTaxShippingAddress); + $this->customerSessionMock->expects($this->once()) + ->method('getCustomerTaxClassId') + ->willReturn($customerTaxClassId); + + $this->plugin->beforeGenerateXml($this->layoutMock); + + $this->customerSessionMock->expects($this->once()) + ->method('setDefaultTaxBillingAddress') + ->with($defaultTaxBillingAddress); + $this->customerSessionMock->expects($this->once()) + ->method('setDefaultTaxShippingAddress') + ->with($defaultTaxShippingAddress); + $this->customerSessionMock->expects($this->once()) + ->method('setCustomerTaxClassId') + ->with($customerTaxClassId); + + $this->assertEmpty($this->plugin->afterGenerateElements($this->layoutMock)); + } +} diff --git a/app/code/Magento/Tax/etc/adminhtml/system.xml b/app/code/Magento/Tax/etc/adminhtml/system.xml index 7fc1744b8e27e..c1e1286041ce6 100644 --- a/app/code/Magento/Tax/etc/adminhtml/system.xml +++ b/app/code/Magento/Tax/etc/adminhtml/system.xml @@ -14,59 +14,59 @@ <resource>Magento_Tax::config_tax</resource> <group id="classes" translate="label" sortOrder="10" showInDefault="1" showInWebsite="1" showInStore="1"> <label>Tax Classes</label> - <field id="shipping_tax_class" translate="label" type="select" sortOrder="10" showInDefault="1" showInWebsite="1" showInStore="0" canRestore="1"> + <field id="shipping_tax_class" translate="label" type="select" sortOrder="10" showInDefault="1" showInWebsite="1" canRestore="1"> <label>Tax Class for Shipping</label> <source_model>Magento\Tax\Model\TaxClass\Source\Product</source_model> </field> - <field id="default_product_tax_class" translate="label" type="select" sortOrder="20" showInDefault="1" showInWebsite="0" showInStore="0" canRestore="1"> + <field id="default_product_tax_class" translate="label" type="select" sortOrder="20" showInDefault="1" canRestore="1"> <label>Default Tax Class for Product</label> <source_model>Magento\Tax\Model\TaxClass\Source\Product</source_model> <backend_model>Magento\Tax\Model\Config\TaxClass</backend_model> </field> - <field id="default_customer_tax_class" translate="label" type="select" sortOrder="30" showInDefault="1" showInWebsite="0" showInStore="0" canRestore="1"> + <field id="default_customer_tax_class" translate="label" type="select" sortOrder="30" showInDefault="1" canRestore="1"> <label>Default Tax Class for Customer</label> <source_model>Magento\Tax\Model\TaxClass\Source\Customer</source_model> </field> </group> <group id="calculation" translate="label" sortOrder="20" showInDefault="1" showInWebsite="1" showInStore="1"> <label>Calculation Settings</label> - <field id="algorithm" translate="label" type="select" sortOrder="1" showInDefault="1" showInWebsite="1" showInStore="0" canRestore="1"> + <field id="algorithm" translate="label" type="select" sortOrder="1" showInDefault="1" showInWebsite="1" canRestore="1"> <label>Tax Calculation Method Based On</label> <source_model>Magento\Tax\Model\System\Config\Source\Algorithm</source_model> </field> - <field id="based_on" translate="label" type="select" sortOrder="10" showInDefault="1" showInWebsite="1" showInStore="0" canRestore="1"> + <field id="based_on" translate="label" type="select" sortOrder="10" showInDefault="1" showInWebsite="1" canRestore="1"> <label>Tax Calculation Based On</label> <source_model>Magento\Tax\Model\Config\Source\Basedon</source_model> <backend_model>Magento\Tax\Model\Config\Notification</backend_model> </field> - <field id="price_includes_tax" translate="label comment" type="select" sortOrder="20" showInDefault="1" showInWebsite="1" showInStore="0" canRestore="1"> + <field id="price_includes_tax" translate="label comment" type="select" sortOrder="20" showInDefault="1" showInWebsite="1" canRestore="1"> <label>Catalog Prices</label> <comment>This sets whether catalog prices entered from Magento Admin include tax.</comment> <backend_model>Magento\Tax\Model\Config\Price\IncludePrice</backend_model> <source_model>Magento\Tax\Model\System\Config\Source\PriceType</source_model> </field> - <field id="shipping_includes_tax" translate="label comment" type="select" sortOrder="30" showInDefault="1" showInWebsite="1" showInStore="0" canRestore="1"> + <field id="shipping_includes_tax" translate="label comment" type="select" sortOrder="30" showInDefault="1" showInWebsite="1" canRestore="1"> <label>Shipping Prices</label> <comment>This sets whether shipping amounts entered from Magento Admin or obtained from gateways include tax.</comment> <backend_model>Magento\Tax\Model\Config\Price\IncludePrice</backend_model> <source_model>Magento\Tax\Model\System\Config\Source\PriceType</source_model> </field> - <field id="apply_after_discount" translate="label comment" type="select" sortOrder="40" showInDefault="1" showInWebsite="1" showInStore="0" canRestore="1"> + <field id="apply_after_discount" translate="label comment" type="select" sortOrder="40" showInDefault="1" showInWebsite="1" canRestore="1"> <label>Apply Customer Tax</label> <source_model>Magento\Tax\Model\System\Config\Source\Apply</source_model> <backend_model>Magento\Tax\Model\Config\Notification</backend_model> </field> - <field id="discount_tax" translate="label comment" type="select" sortOrder="50" showInDefault="1" showInWebsite="1" showInStore="0" canRestore="1"> + <field id="discount_tax" translate="label comment" type="select" sortOrder="50" showInDefault="1" showInWebsite="1" canRestore="1"> <label>Apply Discount On Prices</label> <source_model>Magento\Tax\Model\System\Config\Source\PriceType</source_model> <backend_model>Magento\Tax\Model\Config\Notification</backend_model> <comment>Warning: To apply the discount on prices including tax and apply the tax after discount, set Catalog Prices to “Including Tax”.</comment> </field> - <field id="apply_tax_on" translate="label" type="select" sortOrder="60" showInDefault="1" showInWebsite="1" showInStore="0" canRestore="1"> + <field id="apply_tax_on" translate="label" type="select" sortOrder="60" showInDefault="1" showInWebsite="1" canRestore="1"> <label>Apply Tax On</label> <source_model>Magento\Tax\Model\Config\Source\Apply\On</source_model> </field> - <field id="cross_border_trade_enabled" translate="label comment" type="select" sortOrder="70" showInDefault="1" showInWebsite="1" showInStore="0"> + <field id="cross_border_trade_enabled" translate="label comment" type="select" sortOrder="70" showInDefault="1" showInWebsite="1"> <label>Enable Cross Border Trade</label> <source_model>Magento\Config\Model\Config\Source\Yesno</source_model> <comment>When catalog price includes tax, enable this setting to fix the price no matter what the customer's tax rate.</comment> diff --git a/app/code/Magento/Tax/etc/frontend/di.xml b/app/code/Magento/Tax/etc/frontend/di.xml index 4db3f54b0edd9..d1b8d93e96935 100644 --- a/app/code/Magento/Tax/etc/frontend/di.xml +++ b/app/code/Magento/Tax/etc/frontend/di.xml @@ -35,7 +35,7 @@ <plugin name="tax-session-depersonalize" type="Magento\Tax\Model\Layout\DepersonalizePlugin" sortOrder="20"/> </type> - <type name="Magento\Framework\App\Action\AbstractAction"> + <type name="Magento\Framework\App\ActionInterface"> <plugin name="tax-app-action-dispatchController-context-plugin" type="Magento\Tax\Model\App\Action\ContextPlugin"/> </type> diff --git a/app/code/Magento/Tax/registration.php b/app/code/Magento/Tax/registration.php index 3568ba311e9f2..2d36e0cc4c992 100644 --- a/app/code/Magento/Tax/registration.php +++ b/app/code/Magento/Tax/registration.php @@ -4,6 +4,6 @@ * See COPYING.txt for license details. */ -use \Magento\Framework\Component\ComponentRegistrar; +use Magento\Framework\Component\ComponentRegistrar; ComponentRegistrar::register(ComponentRegistrar::MODULE, 'Magento_Tax', __DIR__); diff --git a/app/code/Magento/TaxImportExport/registration.php b/app/code/Magento/TaxImportExport/registration.php index 63093dc25aa6d..67b67b55cbeee 100644 --- a/app/code/Magento/TaxImportExport/registration.php +++ b/app/code/Magento/TaxImportExport/registration.php @@ -4,6 +4,6 @@ * See COPYING.txt for license details. */ -use \Magento\Framework\Component\ComponentRegistrar; +use Magento\Framework\Component\ComponentRegistrar; ComponentRegistrar::register(ComponentRegistrar::MODULE, 'Magento_TaxImportExport', __DIR__); diff --git a/app/code/Magento/Theme/Block/Html/Pager.php b/app/code/Magento/Theme/Block/Html/Pager.php index d26c383241f66..5798b94e31a70 100644 --- a/app/code/Magento/Theme/Block/Html/Pager.php +++ b/app/code/Magento/Theme/Block/Html/Pager.php @@ -450,7 +450,11 @@ public function getLastPageUrl() */ public function getPageUrl($page) { - return $this->getPagerUrl([$this->getPageVarName() => $page]); + return $this->getPagerUrl( + [ + $this->getPageVarName() => $page > 1 ? $page : null, + ] + ); } /** diff --git a/app/code/Magento/Theme/Block/Html/Title.php b/app/code/Magento/Theme/Block/Html/Title.php index ea0feb403180c..9059afe19ab05 100644 --- a/app/code/Magento/Theme/Block/Html/Title.php +++ b/app/code/Magento/Theme/Block/Html/Title.php @@ -5,7 +5,9 @@ */ namespace Magento\Theme\Block\Html; +use Magento\Framework\App\Config\ScopeConfigInterface; use Magento\Framework\View\Element\Template; +use Magento\Store\Model\ScopeInterface; /** * Html page title block @@ -19,6 +21,16 @@ */ class Title extends Template { + /** + * Config path to 'Translate Title' header settings + */ + private const XML_PATH_HEADER_TRANSLATE_TITLE = 'design/header/translate_title'; + + /** + * @var ScopeConfigInterface + */ + private $scopeConfig; + /** * Own page title to display on the page * @@ -26,6 +38,22 @@ class Title extends Template */ protected $pageTitle; + /** + * Constructor + * + * @param Template\Context $context + * @param ScopeConfigInterface $scopeConfig + * @param array $data + */ + public function __construct( + Template\Context $context, + ScopeConfigInterface $scopeConfig, + array $data = [] + ) { + parent::__construct($context, $data); + $this->scopeConfig = $scopeConfig; + } + /** * Provide own page title or pick it from Head Block * @@ -36,7 +64,10 @@ public function getPageTitle() if (!empty($this->pageTitle)) { return $this->pageTitle; } - return __($this->pageConfig->getTitle()->getShort()); + + $pageTitle = $this->pageConfig->getTitle()->getShort(); + + return $this->shouldTranslateTitle() ? __($pageTitle) : $pageTitle; } /** @@ -46,10 +77,9 @@ public function getPageTitle() */ public function getPageHeading() { - if (!empty($this->pageTitle)) { - return __($this->pageTitle); - } - return __($this->pageConfig->getTitle()->getShortHeading()); + $pageTitle = !empty($this->pageTitle) ? $this->pageTitle : $this->pageConfig->getTitle()->getShortHeading(); + + return $this->shouldTranslateTitle() ? __($pageTitle) : $pageTitle; } /** @@ -62,4 +92,17 @@ public function setPageTitle($pageTitle) { $this->pageTitle = $pageTitle; } + + /** + * Check if page title should be translated + * + * @return bool + */ + private function shouldTranslateTitle(): bool + { + return $this->scopeConfig->isSetFlag( + static::XML_PATH_HEADER_TRANSLATE_TITLE, + ScopeInterface::SCOPE_STORE + ); + } } diff --git a/app/code/Magento/Theme/Controller/Result/AsyncCssPlugin.php b/app/code/Magento/Theme/Controller/Result/AsyncCssPlugin.php index a3f054d7c8dc7..70ea478004b9d 100644 --- a/app/code/Magento/Theme/Controller/Result/AsyncCssPlugin.php +++ b/app/code/Magento/Theme/Controller/Result/AsyncCssPlugin.php @@ -58,9 +58,9 @@ function ($matches) use (&$cssMatches) { } $media = $media ?? 'all'; $loadCssAsync = sprintf( - '<link rel="preload" as="style" media="%s" . - onload="this.onload=null;this.rel=\'stylesheet\'"' . - 'href="%s">', + '<link rel="preload" as="style" media="%s"' . + ' onload="this.onload=null;this.rel=\'stylesheet\'"' . + ' href="%s" />', $media, $href ); diff --git a/app/code/Magento/Theme/Controller/Result/JsFooterPlugin.php b/app/code/Magento/Theme/Controller/Result/JsFooterPlugin.php index 317ab39d307cd..6a80dec460660 100644 --- a/app/code/Magento/Theme/Controller/Result/JsFooterPlugin.php +++ b/app/code/Magento/Theme/Controller/Result/JsFooterPlugin.php @@ -41,7 +41,7 @@ public function beforeSendResponse(Http $subject) { $content = $subject->getContent(); $script = []; - if (strpos($content, '</body') !== false) { + if (is_string($content) && strpos($content, '</body') !== false) { if ($this->scopeConfig->isSetFlag( self::XML_PATH_DEV_MOVE_JS_TO_BOTTOM, ScopeInterface::SCOPE_STORE diff --git a/app/code/Magento/Theme/Model/Theme/Plugin/Registration.php b/app/code/Magento/Theme/Model/Theme/Plugin/Registration.php index 05030c56c2ee0..13102a722eddf 100644 --- a/app/code/Magento/Theme/Model/Theme/Plugin/Registration.php +++ b/app/code/Magento/Theme/Model/Theme/Plugin/Registration.php @@ -5,8 +5,7 @@ */ namespace Magento\Theme\Model\Theme\Plugin; -use Magento\Backend\App\AbstractAction; -use Magento\Framework\App\RequestInterface; +use Magento\Framework\App\ActionInterface; use Magento\Theme\Model\Theme\Registration as ThemeRegistration; use Magento\Framework\Exception\LocalizedException; use Psr\Log\LoggerInterface; @@ -16,32 +15,34 @@ use Magento\Framework\Config\Theme; /** + * Plugin for Theme Registration + * * @SuppressWarnings(PHPMD.CouplingBetweenObjects) */ class Registration { /** - * @var \Magento\Theme\Model\Theme\Registration + * @var ThemeRegistration */ protected $themeRegistration; /** - * @var \Magento\Theme\Model\Theme\Collection + * @var ThemeCollection */ protected $themeCollection; /** - * @var \Magento\Theme\Model\ResourceModel\Theme\Collection + * @var ThemeLoader */ protected $themeLoader; /** - * @var \Psr\Log\LoggerInterface + * @var LoggerInterface */ protected $logger; /** - * @var \Magento\Framework\App\State + * @var AppState */ protected $appState; @@ -69,15 +70,13 @@ public function __construct( /** * Add new theme from filesystem and update existing * - * @param AbstractAction $subject - * @param RequestInterface $request + * @param ActionInterface $subject * * @return void * @SuppressWarnings(PHPMD.UnusedFormalParameter) */ - public function beforeDispatch( - AbstractAction $subject, - RequestInterface $request + public function beforeExecute( + ActionInterface $subject ) { try { if ($this->appState->getMode() != AppState::MODE_PRODUCTION) { diff --git a/app/code/Magento/Theme/Model/Theme/ThemePackageInfo.php b/app/code/Magento/Theme/Model/Theme/ThemePackageInfo.php index 1a5da09a7a4e7..f98fe3d76a35e 100644 --- a/app/code/Magento/Theme/Model/Theme/ThemePackageInfo.php +++ b/app/code/Magento/Theme/Model/Theme/ThemePackageInfo.php @@ -67,7 +67,7 @@ public function getPackageName($themePath) if ($themeFile) { $rawData = $this->serializer->unserialize($themeFile); } - return isset($rawData['name']) ? $rawData['name'] : ''; + return $rawData['name'] ?? ''; } return ''; } @@ -83,8 +83,7 @@ public function getFullThemePath($packageName) if (empty($this->packageNameToFullPathMap)) { $this->initializeMap(); } - return isset($this->packageNameToFullPathMap[$packageName]) - ? $this->packageNameToFullPathMap[$packageName] : ''; + return $this->packageNameToFullPathMap[$packageName] ?? ''; } /** diff --git a/app/code/Magento/Theme/Test/Mftf/ActionGroup/StorefrontCheckElementColorActionGroup.xml b/app/code/Magento/Theme/Test/Mftf/ActionGroup/StorefrontCheckElementColorActionGroup.xml index 66e98d5e41527..c4c0f01441f0b 100644 --- a/app/code/Magento/Theme/Test/Mftf/ActionGroup/StorefrontCheckElementColorActionGroup.xml +++ b/app/code/Magento/Theme/Test/Mftf/ActionGroup/StorefrontCheckElementColorActionGroup.xml @@ -19,6 +19,9 @@ </arguments> <executeJS function="return window.getComputedStyle(document.querySelector('{{selector}}')).getPropertyValue('{{property}}')" stepKey="getElementColor"/> - <assertEquals expected="{{color}}" expectedType="string" actualType="variable" actual="getElementColor" message="pass" stepKey="assertElementColor"/> + <assertEquals message="pass" stepKey="assertElementColor"> + <actualResult type="variable">getElementColor</actualResult> + <expectedResult type="string">{{color}}</expectedResult> + </assertEquals> </actionGroup> </actionGroups> diff --git a/app/code/Magento/Theme/Test/Mftf/Section/AdminDesignConfigSection.xml b/app/code/Magento/Theme/Test/Mftf/Section/AdminDesignConfigSection.xml index 762537ba426f2..820917661b2ce 100644 --- a/app/code/Magento/Theme/Test/Mftf/Section/AdminDesignConfigSection.xml +++ b/app/code/Magento/Theme/Test/Mftf/Section/AdminDesignConfigSection.xml @@ -26,6 +26,9 @@ <element name="logoUpload" type ="input" selector="[name='email_logo']" /> <element name="logoWrapperOpen" type ="text" selector="[data-index='email'] [data-state-collapsible ='closed']"/> <element name="logoPreview" type ="text" selector="[alt ='magento-logo.png']"/> + <element name="logoImageAlt" type ="text" selector="[name='email_logo_alt']"/> + <element name="logoImageWidth" type ="text" selector="[name='email_logo_width']"/> + <element name="logoImageHeight" type ="text" selector="[name='email_logo_height']"/> <element name="faviconArrow" type="button" selector="#ZmF2aWNvbg-- > .jstree-icon" /> <element name="checkIfFaviconArrowExpand" type="button" selector="//li[@id='ZmF2aWNvbg--' and contains(@class,'jstree-closed')]" /> <element name="storesArrow" type="button" selector="#ZmF2aWNvbi9zdG9yZXM- > .jstree-icon" /> @@ -34,5 +37,7 @@ <element name="imageWatermarkType" type="text" selector="//div[contains(@class, 'fieldset-wrapper-title')]//span[contains(text(), '{{watermarkType}}')]" parameterized="true"/> <element name="appliedTheme" type="select" selector="select[name='theme_theme_id']"/> <element name="scopeEditLinkByName" type="button" selector="//tr//td[count(//div[@data-role='grid-wrapper']//tr//th[normalize-space(.)= '{{scope}}']/preceding-sibling::th)+1][contains(.,'{{scopeName}}')]/..//a[contains(@class, 'action-menu-item')]" timeout="30" parameterized="true"/> + <element name="transactionalEmailSectionHeader" type="button" selector="[data-index='email'] .fieldset-wrapper-title" timeout="30"/> + <element name="transactionalEmailSectionBody" type="block" selector="[data-index='email'] .admin__fieldset-wrapper-content" timeout="30"/> </section> </sections> diff --git a/app/code/Magento/Theme/Test/Mftf/Section/StorefrontNavigationMenuSection.xml b/app/code/Magento/Theme/Test/Mftf/Section/StorefrontNavigationMenuSection.xml index 5741b50f877f6..d6432be2ef2ee 100644 --- a/app/code/Magento/Theme/Test/Mftf/Section/StorefrontNavigationMenuSection.xml +++ b/app/code/Magento/Theme/Test/Mftf/Section/StorefrontNavigationMenuSection.xml @@ -15,7 +15,6 @@ <element name="subItemByLevel" type="text" selector="li.{{itemLevel}}.parent ul.{{itemLevel}}" parameterized="true"/> <element name="itemActiveState" type="text" selector=".navigation .level0.active>.level-top"/> <element name="subItemActiveState" type="text" selector=".navigation .level0 .submenu .active>a"/> - <element name="submenuLeftDirection" type="text" selector="ul.{{itemLevel}}.submenu-reverse" parameterized="true"/> - <element name="submenuRightDirection" type="text" selector="ul.{{itemLevel}}:not(.submenu-reverse)" parameterized="true"/> + <element name="submenu" type="text" selector="ul.{{itemLevel}}" parameterized="true"/> </section> </sections> diff --git a/app/code/Magento/Theme/Test/Mftf/Test/AdminContentThemeSortTest.xml b/app/code/Magento/Theme/Test/Mftf/Test/AdminContentThemeSortTest.xml index 9facab57e9a09..056b4c3f914fe 100644 --- a/app/code/Magento/Theme/Test/Mftf/Test/AdminContentThemeSortTest.xml +++ b/app/code/Magento/Theme/Test/Mftf/Test/AdminContentThemeSortTest.xml @@ -19,10 +19,10 @@ <group value="menu"/> </annotations> <before> - <actionGroup ref="LoginAsAdmin" stepKey="LoginAsAdmin"/> + <actionGroup ref="AdminLoginActionGroup" stepKey="LoginAsAdmin"/> </before> <after> - <actionGroup ref="logout" stepKey="logout"/> + <actionGroup ref="AdminLogoutActionGroup" stepKey="logout"/> </after> <actionGroup ref="AdminNavigateMenuActionGroup" stepKey="navigateToContentThemesPage"> <argument name="menuUiId" value="{{AdminMenuContent.dataUiId}}"/> diff --git a/app/code/Magento/Theme/Test/Mftf/Test/AdminContentThemesNavigateMenuTest.xml b/app/code/Magento/Theme/Test/Mftf/Test/AdminContentThemesNavigateMenuTest.xml index 8e7bfd71b07d3..167191ee69a79 100644 --- a/app/code/Magento/Theme/Test/Mftf/Test/AdminContentThemesNavigateMenuTest.xml +++ b/app/code/Magento/Theme/Test/Mftf/Test/AdminContentThemesNavigateMenuTest.xml @@ -20,10 +20,10 @@ <group value="mtf_migrated"/> </annotations> <before> - <actionGroup ref="LoginAsAdmin" stepKey="LoginAsAdmin"/> + <actionGroup ref="AdminLoginActionGroup" stepKey="LoginAsAdmin"/> </before> <after> - <actionGroup ref="logout" stepKey="logout"/> + <actionGroup ref="AdminLogoutActionGroup" stepKey="logout"/> </after> <actionGroup ref="AdminNavigateMenuActionGroup" stepKey="navigateToContentThemesPage"> <argument name="menuUiId" value="{{AdminMenuContent.dataUiId}}"/> diff --git a/app/code/Magento/Theme/Test/Mftf/Test/AdminDesignConfigMediaGalleryImageUploadTest.xml b/app/code/Magento/Theme/Test/Mftf/Test/AdminDesignConfigMediaGalleryImageUploadTest.xml index 8291ddc12b3b1..07db3a5a18b73 100644 --- a/app/code/Magento/Theme/Test/Mftf/Test/AdminDesignConfigMediaGalleryImageUploadTest.xml +++ b/app/code/Magento/Theme/Test/Mftf/Test/AdminDesignConfigMediaGalleryImageUploadTest.xml @@ -19,10 +19,10 @@ <group value="Content"/> </annotations> <before> - <actionGroup ref="LoginAsAdmin" stepKey="loginToAdminArea"/> + <actionGroup ref="AdminLoginActionGroup" stepKey="loginToAdminArea"/> </before> <after> - <actionGroup ref="logout" stepKey="logoutOfAdmin"/> + <actionGroup ref="AdminLogoutActionGroup" stepKey="logoutOfAdmin"/> </after> <!--Edit Store View--> <comment userInput="Edit Store View" stepKey="editStoreViewComment"/> diff --git a/app/code/Magento/Theme/Test/Mftf/Test/AdminWatermarkUploadTest.xml b/app/code/Magento/Theme/Test/Mftf/Test/AdminWatermarkUploadTest.xml index afe70243f602c..fbe1d5ac936d7 100644 --- a/app/code/Magento/Theme/Test/Mftf/Test/AdminWatermarkUploadTest.xml +++ b/app/code/Magento/Theme/Test/Mftf/Test/AdminWatermarkUploadTest.xml @@ -22,10 +22,10 @@ <group value="Watermark"/> </annotations> <before> - <actionGroup ref="LoginAsAdmin" stepKey="loginToAdminArea"/> + <actionGroup ref="AdminLoginActionGroup" stepKey="loginToAdminArea"/> </before> <after> - <actionGroup ref="logout" stepKey="logoutOfAdmin"/> + <actionGroup ref="AdminLogoutActionGroup" stepKey="logoutOfAdmin"/> </after> <amOnPage url="{{DesignConfigPage.url}}" stepKey="navigateToDesignConfigPage" /> <waitForPageLoad stepKey="waitForPageload1"/> diff --git a/app/code/Magento/Theme/Test/Mftf/Test/ThemeTest.xml b/app/code/Magento/Theme/Test/Mftf/Test/ThemeTest.xml index 67213934a6c41..0f9ff05af3d89 100644 --- a/app/code/Magento/Theme/Test/Mftf/Test/ThemeTest.xml +++ b/app/code/Magento/Theme/Test/Mftf/Test/ThemeTest.xml @@ -19,10 +19,10 @@ <group value="Theme"/> </annotations> <after> - <actionGroup ref="logout" stepKey="logoutOfAdmin"/> + <actionGroup ref="AdminLogoutActionGroup" stepKey="logoutOfAdmin"/> </after> <!--Login to Admin Area--> - <actionGroup ref="LoginAsAdmin" stepKey="loginToAdminArea"/> + <actionGroup ref="AdminLoginActionGroup" stepKey="loginToAdminArea"/> <!--Navigate to Themes page--> <amOnPage url="{{ThemesPageIndex.url}}" stepKey="navigateToThemesIndexPage" /> <waitForPageLoad stepKey="wait1"/> diff --git a/app/code/Magento/Theme/Test/Unit/Block/Html/PagerTest.php b/app/code/Magento/Theme/Test/Unit/Block/Html/PagerTest.php new file mode 100644 index 0000000000000..2fa1c637f1838 --- /dev/null +++ b/app/code/Magento/Theme/Test/Unit/Block/Html/PagerTest.php @@ -0,0 +1,104 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +namespace Magento\Theme\Test\Unit\Block\Html; + +use Magento\Framework\App\Config; +use Magento\Framework\Data\Collection; +use Magento\Framework\TestFramework\Unit\Helper\ObjectManager; +use Magento\Framework\UrlInterface; +use Magento\Framework\View\Element\Template\Context; +use Magento\Theme\Block\Html\Pager; +use PHPUnit\Framework\TestCase; + +/** + * Test For Page class + */ +class PagerTest extends TestCase +{ + + /** + * @var Pager $pager + */ + private $pager; + + /** + * @var Context $context + */ + private $context; + + /** + * @var Config $scopeConfig + */ + private $scopeConfig; + + /** + * @var UrlInterface $urlBuilderMock + */ + private $urlBuilderMock; + + /** + * @inheritdoc + */ + protected function setUp() + { + $this->context = $this->createMock(Context::class); + $this->urlBuilderMock = $this->createMock(UrlInterface::class); + $this->context->expects($this->any()) + ->method('getUrlBuilder') + ->willReturn($this->urlBuilderMock); + $this->scopeConfig = $this->createMock(Config::class); + $this->pager = (new ObjectManager($this))->getObject( + Pager::class, + ['context' => $this->context] + ); + } + + /** + * Verify current page Url + * + * @return void + */ + public function testGetPageUrl(): void + { + $expectedPageUrl = 'page-url'; + $this->urlBuilderMock->expects($this->once()) + ->method('getUrl') + ->willReturn($expectedPageUrl); + $this->assertEquals($expectedPageUrl, $this->pager->getPageUrl(0)); + } + + /** + * Verify get pages method. + * + * @return void + */ + public function testGetPages(): void + { + $expectedPages = range(1, 5); + $collectionMock = $this->createMock(Collection::class); + $collectionMock->expects($this->exactly(2)) + ->method('getCurPage') + ->willReturn(2); + $collectionMock->expects($this->any()) + ->method('getLastPageNumber') + ->willReturn(10); + $this->setCollectionProperty($collectionMock); + $this->assertEquals($expectedPages, $this->pager->getPages()); + } + + /** + * Set Collection + * + * @return void + */ + private function setCollectionProperty($collection): void + { + $reflection = new \ReflectionClass($this->pager); + $reflection_property = $reflection->getProperty('_collection'); + $reflection_property->setAccessible(true); + $reflection_property->setValue($this->pager, $collection); + } +} diff --git a/app/code/Magento/Theme/Test/Unit/Block/Html/TitleTest.php b/app/code/Magento/Theme/Test/Unit/Block/Html/TitleTest.php index a4d56748eee6b..d6357d8d61995 100644 --- a/app/code/Magento/Theme/Test/Unit/Block/Html/TitleTest.php +++ b/app/code/Magento/Theme/Test/Unit/Block/Html/TitleTest.php @@ -5,29 +5,51 @@ */ namespace Magento\Theme\Test\Unit\Block\Html; +use Magento\Framework\App\Config\ScopeConfigInterface; +use Magento\Framework\Phrase; use Magento\Framework\TestFramework\Unit\Helper\ObjectManager as ObjectManagerHelper; +use Magento\Framework\View\Element\Template\Context; +use Magento\Framework\View\Page\Config; +use Magento\Framework\View\Page\Title as PageTitle; +use Magento\Store\Model\ScopeInterface; +use Magento\Theme\Block\Html\Title; +use PHPUnit\Framework\MockObject\MockObject; +use PHPUnit\Framework\TestCase; -class TitleTest extends \PHPUnit\Framework\TestCase +/** + * Test class for \Magento\Theme\Block\Html\Title + */ +class TitleTest extends TestCase { + /** + * Config path to 'Translate Title' header settings + */ + private const XML_PATH_HEADER_TRANSLATE_TITLE = 'design/header/translate_title'; + /** * @var ObjectManagerHelper */ - protected $objectManagerHelper; + private $objectManagerHelper; + + /** + * @var Config|MockObject + */ + private $pageConfigMock; /** - * @var \Magento\Framework\View\Page\Config|\PHPUnit_Framework_MockObject_MockObject + * @var PageTitle|MockObject */ - protected $pageConfigMock; + private $pageTitleMock; /** - * @var \Magento\Framework\View\Page\Title|\PHPUnit_Framework_MockObject_MockObject + * @var ScopeConfigInterface|MockObject */ - protected $pageTitleMock; + private $scopeConfigMock; /** - * @var \Magento\Theme\Block\Html\Title + * @var Title */ - protected $htmlTitle; + private $htmlTitle; /** * @return void @@ -35,17 +57,22 @@ class TitleTest extends \PHPUnit\Framework\TestCase protected function setUp() { $this->objectManagerHelper = new ObjectManagerHelper($this); - $this->pageConfigMock = $this->createMock(\Magento\Framework\View\Page\Config::class); - $this->pageTitleMock = $this->createMock(\Magento\Framework\View\Page\Title::class); + $this->pageConfigMock = $this->createMock(Config::class); + $this->pageTitleMock = $this->createMock(PageTitle::class); $context = $this->objectManagerHelper->getObject( - \Magento\Framework\View\Element\Template\Context::class, + Context::class, ['pageConfig' => $this->pageConfigMock] ); + $this->scopeConfigMock = $this->getMockForAbstractClass(ScopeConfigInterface::class); + $this->htmlTitle = $this->objectManagerHelper->getObject( - \Magento\Theme\Block\Html\Title::class, - ['context' => $context] + Title::class, + [ + 'context' => $context, + 'scopeConfig' => $this->scopeConfigMock + ] ); } @@ -64,10 +91,16 @@ public function testGetPageTitleWithSetPageTitle() } /** + * @param bool $shouldTranslateTitle + * * @return void + * @dataProvider dataProviderShouldTranslateTitle */ - public function testGetPageTitle() + public function testGetPageTitle($shouldTranslateTitle) { + $this->scopeConfigMock->method('isSetFlag') + ->with(static::XML_PATH_HEADER_TRANSLATE_TITLE, ScopeInterface::SCOPE_STORE) + ->willReturn($shouldTranslateTitle); $title = 'some title'; $this->pageTitleMock->expects($this->once()) @@ -77,28 +110,58 @@ public function testGetPageTitle() ->method('getTitle') ->willReturn($this->pageTitleMock); - $this->assertEquals($title, $this->htmlTitle->getPageTitle()); + $result = $this->htmlTitle->getPageTitle(); + + if ($shouldTranslateTitle) { + $this->assertInstanceOf(Phrase::class, $result); + } else { + $this->assertInternalType('string', $result); + } + + $this->assertEquals($title, $result); } /** + * @param bool $shouldTranslateTitle + * * @return void + * @dataProvider dataProviderShouldTranslateTitle */ - public function testGetPageHeadingWithSetPageTitle() + public function testGetPageHeadingWithSetPageTitle($shouldTranslateTitle) { + $this->scopeConfigMock->method('isSetFlag') + ->with(static::XML_PATH_HEADER_TRANSLATE_TITLE, ScopeInterface::SCOPE_STORE) + ->willReturn($shouldTranslateTitle); + $title = 'some title'; $this->htmlTitle->setPageTitle($title); $this->pageConfigMock->expects($this->never()) ->method('getTitle'); - $this->assertEquals($title, $this->htmlTitle->getPageHeading()); + $result = $this->htmlTitle->getPageHeading(); + + if ($shouldTranslateTitle) { + $this->assertInstanceOf(Phrase::class, $result); + } else { + $this->assertInternalType('string', $result); + } + + $this->assertEquals($title, $result); } /** + * @param bool $shouldTranslateTitle + * * @return void + * @dataProvider dataProviderShouldTranslateTitle */ - public function testGetPageHeading() + public function testGetPageHeading($shouldTranslateTitle) { + $this->scopeConfigMock->method('isSetFlag') + ->with(static::XML_PATH_HEADER_TRANSLATE_TITLE, ScopeInterface::SCOPE_STORE) + ->willReturn($shouldTranslateTitle); + $title = 'some title'; $this->pageTitleMock->expects($this->once()) @@ -108,6 +171,29 @@ public function testGetPageHeading() ->method('getTitle') ->willReturn($this->pageTitleMock); - $this->assertEquals($title, $this->htmlTitle->getPageHeading()); + $result = $this->htmlTitle->getPageHeading(); + + if ($shouldTranslateTitle) { + $this->assertInstanceOf(Phrase::class, $result); + } else { + $this->assertInternalType('string', $result); + } + + $this->assertEquals($title, $result); + } + + /** + * @return array + */ + public function dataProviderShouldTranslateTitle(): array + { + return [ + [ + true + ], + [ + false + ] + ]; } } diff --git a/app/code/Magento/Theme/Test/Unit/Controller/Adminhtml/Design/Config/SaveTest.php b/app/code/Magento/Theme/Test/Unit/Controller/Adminhtml/Design/Config/SaveTest.php index a193604a0d6da..21989a0e6c58e 100644 --- a/app/code/Magento/Theme/Test/Unit/Controller/Adminhtml/Design/Config/SaveTest.php +++ b/app/code/Magento/Theme/Test/Unit/Controller/Adminhtml/Design/Config/SaveTest.php @@ -37,7 +37,7 @@ class SaveTest extends \PHPUnit\Framework\TestCase /** @var \Magento\Backend\App\Action\Context|\PHPUnit_Framework_MockObject_MockObject */ protected $context; - /** @var \Zend\Stdlib\Parameters|\PHPUnit_Framework_MockObject_MockObject */ + /** @var \Laminas\Stdlib\Parameters|\PHPUnit_Framework_MockObject_MockObject */ protected $fileParams; /** @var \Magento\Theme\Api\Data\DesignConfigInterface|\PHPUnit_Framework_MockObject_MockObject */ @@ -83,7 +83,7 @@ public function setUp() '', false ); - $this->fileParams = $this->createMock(\Zend\Stdlib\Parameters::class); + $this->fileParams = $this->createMock(\Laminas\Stdlib\Parameters::class); $this->dataPersistor = $this->getMockBuilder(\Magento\Framework\App\Request\DataPersistorInterface::class) ->getMockForAbstractClass(); $this->controller = new Save( diff --git a/app/code/Magento/Theme/Test/Unit/Controller/Result/AsyncCssPluginTest.php b/app/code/Magento/Theme/Test/Unit/Controller/Result/AsyncCssPluginTest.php new file mode 100644 index 0000000000000..b1303e991a680 --- /dev/null +++ b/app/code/Magento/Theme/Test/Unit/Controller/Result/AsyncCssPluginTest.php @@ -0,0 +1,145 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +declare(strict_types=1); + +namespace Magento\Theme\Test\Unit\Controller\Result; + +use Magento\Theme\Controller\Result\AsyncCssPlugin; +use Magento\Framework\App\Response\Http; +use PHPUnit\Framework\TestCase; +use PHPUnit\Framework\MockObject\MockObject; +use Magento\Framework\App\Config\ScopeConfigInterface; +use Magento\Store\Model\ScopeInterface; +use Magento\Framework\TestFramework\Unit\Helper\ObjectManager as ObjectManagerHelper; + +/** + * Unit test for Magento\Theme\Test\Unit\Controller\Result\AsyncCssPlugin. + */ +class AsyncCssPluginTest extends TestCase +{ + const STUB_XML_PATH_USE_CSS_CRITICAL_PATH = 'dev/css/use_css_critical_path'; + + /** + * @var AsyncCssPlugin + */ + private $plugin; + + /** + * @var ScopeConfigInterface|MockObject + */ + private $scopeConfigMock; + + /** + * @var Http|MockObject + */ + private $httpMock; + + /** + * @inheritdoc + */ + protected function setUp(): void + { + $this->scopeConfigMock = $this->getMockBuilder(ScopeConfigInterface::class) + ->setMethods(['isSetFlag']) + ->disableOriginalConstructor() + ->getMockForAbstractClass(); + + $this->httpMock = $this->createMock(Http::class); + + $objectManager = new ObjectManagerHelper($this); + $this->plugin = $objectManager->getObject( + AsyncCssPlugin::class, + [ + 'scopeConfig' => $this->scopeConfigMock + ] + ); + } + + /** + * Data Provider for before send response + * + * @return array + */ + public function sendResponseDataProvider(): array + { + return [ + [ + "content" => "<body><h1>Test Title</h1>" . + "<link rel=\"stylesheet\" href=\"css/critical.css\" />" . + "<p>Test Content</p></body>", + "flag" => true, + "result" => "<body><h1>Test Title</h1>" . + "<link rel=\"preload\" as=\"style\" media=\"all\"" . + " onload=\"this.onload=null;this.rel='stylesheet'\" href=\"css/critical.css\" />" . + "<p>Test Content</p>" . + "<link rel=\"stylesheet\" href=\"css/critical.css\" />" . + "\n</body>" + ], + [ + "content" => "<body><p>Test Content</p></body>", + "flag" => false, + "result" => "<body><p>Test Content</p></body>" + ], + [ + "content" => "<body><p>Test Content</p></body>", + "flag" => true, + "result" => "<body><p>Test Content</p></body>" + ] + ]; + } + + /** + * Test beforeSendResponse + * + * @param string $content + * @param bool $isSetFlag + * @param string $result + * @return void + * @dataProvider sendResponseDataProvider + */ + public function testBeforeSendResponse($content, $isSetFlag, $result): void + { + $this->httpMock->expects($this->once()) + ->method('getContent') + ->willReturn($content); + + $this->scopeConfigMock->expects($this->once()) + ->method('isSetFlag') + ->with( + self::STUB_XML_PATH_USE_CSS_CRITICAL_PATH, + ScopeInterface::SCOPE_STORE + ) + ->willReturn($isSetFlag); + + $this->httpMock->expects($this->any()) + ->method('setContent') + ->with($result); + + $this->plugin->beforeSendResponse($this->httpMock); + } + + /** + * Test BeforeSendResponse if content is not a string + * + * @return void + */ + public function testIfGetContentIsNotAString(): void + { + $this->httpMock->expects($this->once()) + ->method('getContent') + ->willReturn([]); + + $this->scopeConfigMock->expects($this->any()) + ->method('isSetFlag') + ->with( + self::STUB_XML_PATH_USE_CSS_CRITICAL_PATH, + ScopeInterface::SCOPE_STORE + ) + ->willReturn(false); + + $this->plugin->beforeSendResponse($this->httpMock); + } +} diff --git a/app/code/Magento/Theme/Test/Unit/Controller/Result/JsFooterPluginTest.php b/app/code/Magento/Theme/Test/Unit/Controller/Result/JsFooterPluginTest.php new file mode 100644 index 0000000000000..8b696251d4e73 --- /dev/null +++ b/app/code/Magento/Theme/Test/Unit/Controller/Result/JsFooterPluginTest.php @@ -0,0 +1,164 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +declare(strict_types=1); + +namespace Magento\Theme\Test\Unit\Controller\Result; + +use Magento\Theme\Controller\Result\JsFooterPlugin; +use Magento\Framework\App\Response\Http; +use PHPUnit\Framework\TestCase; +use PHPUnit\Framework\MockObject\MockObject; +use Magento\Framework\App\Config\ScopeConfigInterface; +use Magento\Store\Model\ScopeInterface; +use Magento\Framework\TestFramework\Unit\Helper\ObjectManager as ObjectManagerHelper; + +/** + * Unit test for Magento\Theme\Test\Unit\Controller\Result\JsFooterPlugin. + */ +class JsFooterPluginTest extends TestCase +{ + const STUB_XML_PATH_DEV_MOVE_JS_TO_BOTTOM = 'dev/js/move_script_to_bottom'; + + /** + * @var JsFooterPlugin + */ + private $plugin; + + /** + * @var ScopeConfigInterface|MockObject + */ + private $scopeConfigMock; + + /** + * @var Http|MockObject + */ + private $httpMock; + + /** + * @inheritdoc + */ + protected function setUp(): void + { + $this->scopeConfigMock = $this->getMockBuilder(ScopeConfigInterface::class) + ->setMethods(['isSetFlag']) + ->disableOriginalConstructor() + ->getMockForAbstractClass(); + + $this->httpMock = $this->createMock(Http::class); + + $objectManager = new ObjectManagerHelper($this); + $this->plugin = $objectManager->getObject( + JsFooterPlugin::class, + [ + 'scopeConfig' => $this->scopeConfigMock + ] + ); + } + + /** + * Data Provider for testBeforeSendResponse() + * + * @return array + */ + public function sendResponseDataProvider(): array + { + return [ + 'content_with_script_tag' => [ + "content" => "<body><h1>Test Title</h1>" . + "<script type=\"text/x-magento-init\">test</script>" . + "<script type=\"text/x-magento-template\">test</script>" . + "<p>Test Content</p></body>", + "flag" => true, + "result" => "<body><h1>Test Title</h1>" . + "<script type=\"text/x-magento-template\">test</script>" . + "<p>Test Content</p>" . + "<script type=\"text/x-magento-init\">test</script>" . + "\n</body>" + ], + 'content_with_config_disable' => [ + "content" => "<body><p>Test Content</p></body>", + "flag" => false, + "result" => "<body><p>Test Content</p></body>" + ], + 'content_without_script_tag' => [ + "content" => "<body><p>Test Content</p></body>", + "flag" => true, + "result" => "<body><p>Test Content</p>\n</body>" + ] + ]; + } + + /** + * Test beforeSendResponse + * + * @param string $content + * @param bool $isSetFlag + * @param string $result + * @return void + * @dataProvider sendResponseDataProvider + */ + public function testBeforeSendResponse($content, $isSetFlag, $result): void + { + $this->httpMock->expects($this->once()) + ->method('getContent') + ->willReturn($content); + + $this->scopeConfigMock->expects($this->once()) + ->method('isSetFlag') + ->with( + self::STUB_XML_PATH_DEV_MOVE_JS_TO_BOTTOM, + ScopeInterface::SCOPE_STORE + ) + ->willReturn($isSetFlag); + + $this->httpMock->expects($this->any()) + ->method('setContent') + ->with($result); + + $this->plugin->beforeSendResponse($this->httpMock); + } + + /** + * Data Provider for testBeforeSendResponseIfGetContentIsNotAString() + * + * @return array + */ + public function ifGetContentIsNotAStringDataProvider(): array + { + return [ + 'empty_array' => [ + 'content' => [] + ], + 'null' => [ + 'content' => null + ] + ]; + } + + /** + * Test BeforeSendResponse if content is not a string + * + * @param string $content + * @return void + * @dataProvider ifGetContentIsNotAStringDataProvider + */ + public function testBeforeSendResponseIfGetContentIsNotAString($content): void + { + $this->httpMock->expects($this->once()) + ->method('getContent') + ->willReturn($content); + + $this->scopeConfigMock->expects($this->never()) + ->method('isSetFlag') + ->with( + self::STUB_XML_PATH_DEV_MOVE_JS_TO_BOTTOM, + ScopeInterface::SCOPE_STORE + ) + ->willReturn(false); + + $this->plugin->beforeSendResponse($this->httpMock); + } +} diff --git a/app/code/Magento/Theme/Test/Unit/Model/Theme/Plugin/RegistrationTest.php b/app/code/Magento/Theme/Test/Unit/Model/Theme/Plugin/RegistrationTest.php index 190a6edf55900..fb320de97dcc8 100644 --- a/app/code/Magento/Theme/Test/Unit/Model/Theme/Plugin/RegistrationTest.php +++ b/app/code/Magento/Theme/Test/Unit/Model/Theme/Plugin/RegistrationTest.php @@ -3,73 +3,89 @@ * Copyright © Magento, Inc. All rights reserved. * See COPYING.txt for license details. */ + namespace Magento\Theme\Test\Unit\Model\Theme\Plugin; -use Magento\Theme\Model\Theme\Plugin\Registration; +use Magento\Framework\App\ActionInterface; +use Magento\Framework\App\State; +use Magento\Framework\TestFramework\Unit\Helper\ObjectManager; +use Magento\Theme\Model\ResourceModel\Theme\Collection as ThemeCollectionResourceModel; +use Magento\Theme\Model\Theme; +use Magento\Theme\Model\Theme\Collection as ThemeCollection; +use Magento\Theme\Model\Theme\Plugin\Registration as RegistrationPlugin; use Magento\Framework\Exception\LocalizedException; use Magento\Framework\Phrase; +use Magento\Theme\Model\Theme\Registration as ThemeRegistration; +use PHPUnit\Framework\MockObject\MockObject; +use Psr\Log\LoggerInterface; class RegistrationTest extends \PHPUnit\Framework\TestCase { - /** @var \Magento\Theme\Model\Theme\Registration|\PHPUnit_Framework_MockObject_MockObject */ - protected $themeRegistration; - - /** @var \Psr\Log\LoggerInterface|\PHPUnit_Framework_MockObject_MockObject */ - protected $logger; + /** + * @var ThemeRegistration|MockObject + */ + protected $themeRegistrationMock; - /** @var \Magento\Backend\App\AbstractAction|\PHPUnit_Framework_MockObject_MockObject */ - protected $abstractAction; + /** + * @var LoggerInterface|MockObject + */ + protected $loggerMock; - /** @var \Magento\Framework\App\RequestInterface|\PHPUnit_Framework_MockObject_MockObject */ - protected $request; + /** + * @var ActionInterface|MockObject + */ + protected $actionMock; - /** @var \Magento\Framework\App\State|\PHPUnit_Framework_MockObject_MockObject */ - protected $appState; + /** + * @var State|MockObject + */ + protected $appStateMock; - /** @var \Magento\Theme\Model\Theme\Collection|\PHPUnit_Framework_MockObject_MockObject */ - protected $themeCollection; + /** + * @var ThemeCollection|MockObject + */ + protected $themeCollectionMock; - /** @var \Magento\Theme\Model\ResourceModel\Theme\Collection|\PHPUnit_Framework_MockObject_MockObject */ - protected $themeLoader; + /** + * @var ThemeCollectionResourceModel|MockObject + */ + protected $themeLoaderMock; - /** @var Registration */ + /** + * @var RegistrationPlugin + */ protected $plugin; protected function setUp() { - $this->themeRegistration = $this->createMock(\Magento\Theme\Model\Theme\Registration::class); - $this->logger = $this->getMockForAbstractClass(\Psr\Log\LoggerInterface::class, [], '', false); - $this->abstractAction = $this->getMockForAbstractClass( - \Magento\Backend\App\AbstractAction::class, - [], - '', - false - ); - $this->request = $this->getMockForAbstractClass(\Magento\Framework\App\RequestInterface::class, [], '', false); - $this->appState = $this->createMock(\Magento\Framework\App\State::class); - $this->themeCollection = $this->createMock(\Magento\Theme\Model\Theme\Collection::class); - $this->themeLoader = $this->createMock(\Magento\Theme\Model\ResourceModel\Theme\Collection::class); - $this->plugin = new Registration( - $this->themeRegistration, - $this->themeCollection, - $this->themeLoader, - $this->logger, - $this->appState - ); + $this->themeRegistrationMock = $this->createMock(ThemeRegistration::class); + $this->loggerMock = $this->getMockForAbstractClass(LoggerInterface::class, [], '', false); + $this->actionMock = $this->createMock(ActionInterface::class); + $this->appStateMock = $this->createMock(State::class); + $this->themeCollectionMock = $this->createMock(ThemeCollection::class); + $this->themeLoaderMock = $this->createMock(ThemeCollectionResourceModel::class); + + $objectManager = new ObjectManager($this); + $this->plugin = $objectManager->getObject(RegistrationPlugin::class, [ + 'themeRegistration' => $this->themeRegistrationMock, + 'themeCollection' => $this->themeCollectionMock, + 'themeLoader' => $this->themeLoaderMock, + 'logger' => $this->loggerMock, + 'appState' => $this->appStateMock + ]); } /** * @param bool $hasParentTheme - * @dataProvider dataProviderBeforeDispatch + * @dataProvider dataProviderBeforeExecute * @SuppressWarnings(PHPMD.NPathComplexity) */ - public function testBeforeDispatch( - $hasParentTheme - ) { + public function testBeforeExecute($hasParentTheme) + { $themeId = 1; $themeTitle = 'Theme title'; - $themeFromConfigMock = $this->getMockBuilder(\Magento\Theme\Model\Theme::class) + $themeFromConfigMock = $this->getMockBuilder(Theme::class) ->disableOriginalConstructor() ->setMethods([ 'getArea', @@ -79,7 +95,7 @@ public function testBeforeDispatch( ]) ->getMock(); - $themeFromDbMock = $this->getMockBuilder(\Magento\Theme\Model\Theme::class) + $themeFromDbMock = $this->getMockBuilder(Theme::class) ->disableOriginalConstructor() ->setMethods([ 'setParentId', @@ -88,26 +104,26 @@ public function testBeforeDispatch( ]) ->getMock(); - $parentThemeFromDbMock = $this->getMockBuilder(\Magento\Theme\Model\Theme::class) + $parentThemeFromDbMock = $this->getMockBuilder(Theme::class) ->disableOriginalConstructor() ->getMock(); - $parentThemeFromConfigMock = $this->getMockBuilder(\Magento\Theme\Model\Theme::class) + $parentThemeFromConfigMock = $this->getMockBuilder(Theme::class) ->disableOriginalConstructor() ->getMock(); - $this->appState->expects($this->once()) + $this->appStateMock->expects($this->once()) ->method('getMode') ->willReturn('default'); - $this->themeRegistration->expects($this->once()) + $this->themeRegistrationMock->expects($this->once()) ->method('register'); - $this->themeCollection->expects($this->once()) + $this->themeCollectionMock->expects($this->once()) ->method('loadData') ->willReturn([$themeFromConfigMock]); - $this->themeLoader->expects($hasParentTheme ? $this->exactly(2) : $this->once()) + $this->themeLoaderMock->expects($hasParentTheme ? $this->exactly(2) : $this->once()) ->method('getThemeByFullPath') ->willReturnMap([ ['frontend/Magento/blank', $parentThemeFromDbMock], @@ -147,13 +163,13 @@ public function testBeforeDispatch( ->method('save') ->willReturnSelf(); - $this->plugin->beforeDispatch($this->abstractAction, $this->request); + $this->plugin->beforeExecute($this->actionMock); } /** * @return array */ - public function dataProviderBeforeDispatch() + public function dataProviderBeforeExecute() { return [ [true], @@ -163,16 +179,16 @@ public function dataProviderBeforeDispatch() public function testBeforeDispatchWithProductionMode() { - $this->appState->expects($this->once())->method('getMode')->willReturn('production'); - $this->plugin->beforeDispatch($this->abstractAction, $this->request); + $this->appStateMock->expects($this->once())->method('getMode')->willReturn('production'); + $this->plugin->beforeExecute($this->actionMock); } public function testBeforeDispatchWithException() { $exception = new LocalizedException(new Phrase('Phrase')); - $this->themeRegistration->expects($this->once())->method('register')->willThrowException($exception); - $this->logger->expects($this->once())->method('critical'); + $this->themeRegistrationMock->expects($this->once())->method('register')->willThrowException($exception); + $this->loggerMock->expects($this->once())->method('critical'); - $this->plugin->beforeDispatch($this->abstractAction, $this->request); + $this->plugin->beforeExecute($this->actionMock); } } diff --git a/app/code/Magento/Theme/etc/config.xml b/app/code/Magento/Theme/etc/config.xml index 1515c357e094e..c733f2a9503ee 100644 --- a/app/code/Magento/Theme/etc/config.xml +++ b/app/code/Magento/Theme/etc/config.xml @@ -43,6 +43,7 @@ Disallow: /*SID= </search_engine_robots> <header translate="welcome"> <welcome>Default welcome msg!</welcome> + <translate_title>1</translate_title> </header> <footer translate="copyright"> <copyright>Copyright © 2013-present Magento, Inc. All rights reserved.</copyright> diff --git a/app/code/Magento/Theme/etc/di.xml b/app/code/Magento/Theme/etc/di.xml index 62f51e74b6007..921e6bfc6ecf1 100644 --- a/app/code/Magento/Theme/etc/di.xml +++ b/app/code/Magento/Theme/etc/di.xml @@ -206,6 +206,10 @@ <item name="path" xsi:type="string">design/header/welcome</item> <item name="fieldset" xsi:type="string">other_settings/header</item> </item> + <item name="header_translate_title" xsi:type="array"> + <item name="path" xsi:type="string">design/header/translate_title</item> + <item name="fieldset" xsi:type="string">other_settings/header</item> + </item> <item name="footer_copyright" xsi:type="array"> <item name="path" xsi:type="string">design/footer/copyright</item> <item name="fieldset" xsi:type="string">other_settings/footer</item> @@ -285,4 +289,24 @@ <argument name="identifierName" xsi:type="string">theme_id</argument> </arguments> </type> + <virtualType name="configured_design_cache" type="Magento\Framework\App\Cache"> + <arguments> + <argument name="cacheIdentifier" xsi:type="string">layout</argument> + </arguments> + </virtualType> + <virtualType name="design_context" type="Magento\Framework\Model\Context"> + <arguments> + <argument name="cacheManager" xsi:type="object">configured_design_cache</argument> + </arguments> + </virtualType> + <type name="Magento\Theme\Model\Design"> + <arguments> + <argument name="context" xsi:type="object">design_context</argument> + </arguments> + </type> + <type name="Magento\Theme\Model\Theme\ThemeProvider"> + <arguments> + <argument name="cache" xsi:type="object">configured_design_cache</argument> + </arguments> + </type> </config> diff --git a/app/code/Magento/Theme/registration.php b/app/code/Magento/Theme/registration.php index 8f2b360da9d33..c00f6e4ba2136 100644 --- a/app/code/Magento/Theme/registration.php +++ b/app/code/Magento/Theme/registration.php @@ -4,6 +4,6 @@ * See COPYING.txt for license details. */ -use \Magento\Framework\Component\ComponentRegistrar; +use Magento\Framework\Component\ComponentRegistrar; ComponentRegistrar::register(ComponentRegistrar::MODULE, 'Magento_Theme', __DIR__); diff --git a/app/code/Magento/Theme/view/adminhtml/ui_component/design_config_form.xml b/app/code/Magento/Theme/view/adminhtml/ui_component/design_config_form.xml index bc1f36222dd60..dfe11f3120cd8 100644 --- a/app/code/Magento/Theme/view/adminhtml/ui_component/design_config_form.xml +++ b/app/code/Magento/Theme/view/adminhtml/ui_component/design_config_form.xml @@ -208,6 +208,20 @@ <dataScope>header_logo_alt</dataScope> </settings> </field> + <field name="header_translate_title" formElement="select"> + <settings> + <dataType>text</dataType> + <label translate="true">Translate Title</label> + <dataScope>header_translate_title</dataScope> + </settings> + <formElements> + <select> + <settings> + <options class="Magento\Config\Model\Config\Source\Yesno"/> + </settings> + </select> + </formElements> + </field> </fieldset> <fieldset name="footer"> <settings> diff --git a/app/code/Magento/Theme/view/frontend/layout/default_head_blocks.xml b/app/code/Magento/Theme/view/frontend/layout/default_head_blocks.xml index ab4dabfa6d1a0..a4a10ef3f6ee9 100644 --- a/app/code/Magento/Theme/view/frontend/layout/default_head_blocks.xml +++ b/app/code/Magento/Theme/view/frontend/layout/default_head_blocks.xml @@ -23,6 +23,7 @@ </referenceBlock> <referenceContainer name="after.body.start"> <block class="Magento\Framework\View\Element\Js\Components" name="head.components" as="components" template="Magento_Theme::js/components.phtml" before="-"/> + <block class="Magento\Framework\View\Element\Template" name="cookie-status-check" as="cookie-status" template="Magento_Theme::js/cookie_status.phtml" /> </referenceContainer> </body> </page> diff --git a/app/code/Magento/Theme/view/frontend/requirejs-config.js b/app/code/Magento/Theme/view/frontend/requirejs-config.js index c41a0602ef3e8..e14c93d329a07 100644 --- a/app/code/Magento/Theme/view/frontend/requirejs-config.js +++ b/app/code/Magento/Theme/view/frontend/requirejs-config.js @@ -27,10 +27,9 @@ var config = { 'menu': 'mage/menu', 'popupWindow': 'mage/popup-window', 'validation': 'mage/validation/validation', - 'welcome': 'Magento_Theme/js/view/welcome', 'breadcrumbs': 'Magento_Theme/js/view/breadcrumbs', - 'criticalCssLoader': 'Magento_Theme/js/view/critical-css-loader', - 'jquery/ui': 'jquery/compat' + 'jquery/ui': 'jquery/compat', + 'cookieStatus': 'Magento_Theme/js/cookie-status' } }, deps: [ diff --git a/app/code/Magento/Theme/view/frontend/templates/html/pager.phtml b/app/code/Magento/Theme/view/frontend/templates/html/pager.phtml index bd50fa39d4099..6b28dbd4521a0 100644 --- a/app/code/Magento/Theme/view/frontend/templates/html/pager.phtml +++ b/app/code/Magento/Theme/view/frontend/templates/html/pager.phtml @@ -10,31 +10,33 @@ * @see \Magento\Theme\Block\Html\Pager */ ?> -<?php if ($block->getCollection()->getSize()) : ?> +<?php if ($block->getCollection()->getSize()): ?> - <?php if ($block->getUseContainer()) : ?> + <?php if ($block->getUseContainer()): ?> <div class="pager"> <?php endif ?> - <?php if ($block->getShowAmounts()) : ?> + <?php if ($block->getShowAmounts()): ?> <p class="toolbar-amount"> <span class="toolbar-number"> - <?php if ($block->getLastPageNum()>1) : ?> - <?= $block->escapeHtml(__('Items %1 to %2 of %3 total', $block->getFirstNum(), $block->getLastNum(), $block->getTotalNum())) ?> - <?php elseif ($block->getTotalNum() == 1) : ?> + <?php if ($block->getLastPageNum()>1): ?> + <?= $block->escapeHtml( + __('Items %1 to %2 of %3 total', $block->getFirstNum(), $block->getLastNum(), $block->getTotalNum()) + ) ?> + <?php elseif ($block->getTotalNum() == 1): ?> <?= $block->escapeHtml(__('%1 Item', $block->getTotalNum())) ?> - <?php else : ?> + <?php else: ?> <?= $block->escapeHtml(__('%1 Item(s)', $block->getTotalNum())) ?> <?php endif; ?> </span> </p> <?php endif ?> - <?php if ($block->getLastPageNum()>1) : ?> + <?php if ($block->getLastPageNum()>1): ?> <div class="pages"> <strong class="label pages-label" id="paging-label"><?= $block->escapeHtml(__('Page')) ?></strong> <ul class="items pages-items" aria-labelledby="paging-label"> - <?php if (!$block->isFirstPage()) : ?> + <?php if (!$block->isFirstPage()): ?> <li class="item pages-item-previous"> <?php $text = $block->getAnchorTextForPrevious() ? $block->getAnchorTextForPrevious() : '';?> <a class="<?= $block->escapeHtmlAttr($text ? 'link ' : 'action ') ?> previous" @@ -46,7 +48,7 @@ </li> <?php endif;?> - <?php if ($block->canShowFirst()) : ?> + <?php if ($block->canShowFirst()): ?> <li class="item"> <a class="page first" href="<?= $block->escapeUrl($block->getFirstPageUrl()) ?>"> <span class="label"><?= $block->escapeHtml(__('Page')) ?></span> @@ -55,7 +57,7 @@ </li> <?php endif;?> - <?php if ($block->canShowPreviousJump()) : ?> + <?php if ($block->canShowPreviousJump()): ?> <li class="item"> <a class="page previous jump" title="" @@ -65,15 +67,15 @@ </li> <?php endif;?> - <?php foreach ($block->getFramePages() as $_page) : ?> - <?php if ($block->isPageCurrent($_page)) : ?> + <?php foreach ($block->getFramePages() as $_page): ?> + <?php if ($block->isPageCurrent($_page)): ?> <li class="item current"> <strong class="page"> <span class="label"><?= $block->escapeHtml(__('You\'re currently reading page')) ?></span> <span><?= $block->escapeHtml($_page) ?></span> </strong> </li> - <?php else : ?> + <?php else: ?> <li class="item"> <a href="<?= $block->escapeUrl($block->getPageUrl($_page)) ?>" class="page"> <span class="label"><?= $block->escapeHtml(__('Page')) ?></span> @@ -83,7 +85,7 @@ <?php endif;?> <?php endforeach;?> - <?php if ($block->canShowNextJump()) : ?> + <?php if ($block->canShowNextJump()): ?> <li class="item"> <a class="page next jump" title="" href="<?= $block->escapeUrl($block->getNextJumpUrl()) ?>"> <span>...</span> @@ -91,7 +93,7 @@ </li> <?php endif;?> - <?php if ($block->canShowLast()) : ?> + <?php if ($block->canShowLast()): ?> <li class="item"> <a class="page last" href="<?= $block->escapeUrl($block->getLastPageUrl()) ?>"> <span class="label"><?= $block->escapeHtml(__('Page')) ?></span> @@ -100,7 +102,7 @@ </li> <?php endif;?> - <?php if (!$block->isLastPage()) : ?> + <?php if (!$block->isLastPage()): ?> <li class="item pages-item-next"> <?php $text = $block->getAnchorTextForNext() ? $block->getAnchorTextForNext() : '';?> <a class="<?= /* @noEscape */ $text ? 'link ' : 'action ' ?> next" @@ -115,13 +117,13 @@ </div> <?php endif; ?> - <?php if ($block->isShowPerPage()) : ?> + <?php if ($block->isShowPerPage()): ?> <div class="limiter"> <strong class="limiter-label"><?= $block->escapeHtml(__('Show')) ?></strong> <select id="limiter" data-mage-init='{"redirectUrl": {"event":"change"}}' class="limiter-options"> - <?php foreach ($block->getAvailableLimit() as $_key => $_limit) : ?> - <option value="<?= $block->escapeHtmlAttr($block->getLimitUrl($_key)) ?>" - <?php if ($block->isLimitCurrent($_key)) : ?> + <?php foreach ($block->getAvailableLimit() as $_key => $_limit): ?> + <option value="<?= $block->escapeUrl($block->getLimitUrl($_key)) ?>" + <?php if ($block->isLimitCurrent($_key)): ?> selected="selected"<?php endif ?>> <?= $block->escapeHtml($_limit) ?> </option> @@ -131,7 +133,7 @@ </div> <?php endif ?> - <?php if ($block->getUseContainer()) : ?> + <?php if ($block->getUseContainer()): ?> </div> <?php endif ?> diff --git a/app/code/Magento/Theme/view/frontend/templates/js/cookie_status.phtml b/app/code/Magento/Theme/view/frontend/templates/js/cookie_status.phtml new file mode 100644 index 0000000000000..2da71c90b5657 --- /dev/null +++ b/app/code/Magento/Theme/view/frontend/templates/js/cookie_status.phtml @@ -0,0 +1,20 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +?> + +<div id="cookie-status" style="display: none"> + <?= $block->escapeHtml(__('The store will not work correctly in the case when cookies are disabled.')); ?> +</div> + +<script type="text/x-magento-init"> + { + "*": { + "cookieStatus": {} + } + } +</script> + + diff --git a/app/code/Magento/Theme/view/frontend/web/js/cookie-status.js b/app/code/Magento/Theme/view/frontend/web/js/cookie-status.js new file mode 100644 index 0000000000000..3726996a862c6 --- /dev/null +++ b/app/code/Magento/Theme/view/frontend/web/js/cookie-status.js @@ -0,0 +1,40 @@ +define([ + 'jquery', + 'Magento_Ui/js/modal/modal', + 'mage/translate' +], function ($, modal) { + 'use strict'; + + $.widget('mage.cookieStatus', { + options: { + type: 'popup', + responsive: true, + innerScroll: true, + autoOpen: true, + buttons: [{ + text: $.mage.__('Close'), + class: 'cookie-status', + + /** + * Callback for click event + */ + click: function () { + this.closeModal(); + } + }] + }, + + /** + * Init object + * @private + */ + _init: function () { + + if (!navigator.cookieEnabled) { + modal(this.options, $('#cookie-status')); + } + } + }); + + return $.mage.cookieStatus; +}); diff --git a/app/code/Magento/Tinymce3/Test/Mftf/Section/AdminTinymce3FileldsSection.xml b/app/code/Magento/Tinymce3/Test/Mftf/Section/AdminTinymce3FileldsSection.xml deleted file mode 100644 index 76c0a9e1fe797..0000000000000 --- a/app/code/Magento/Tinymce3/Test/Mftf/Section/AdminTinymce3FileldsSection.xml +++ /dev/null @@ -1,21 +0,0 @@ -<?xml version="1.0" encoding="UTF-8"?> -<!-- - /** - * Copyright © Magento, Inc. All rights reserved. - * See COPYING.txt for license details. - */ ---> - -<sections xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" - xsi:noNamespaceSchemaLocation="urn:magento:mftf:Page/etc/SectionObject.xsd"> - <section name="ProductWYSIWYGSection"> - <element name="Tinymce3MSG" type="button" selector=".admin__field-error"/> - </section> - <section name="TinyMCESection"> - <element name="TinyMCE3" type="text" selector="#cms_page_form_content_tbl"/> - <element name="InsertImageBtnTinyMCE3" type="button" selector="#cms_page_form_content_image"/> - </section> - <section name="NewsletterWYSIWYGSection"> - <element name="TinyMCE3" type="text" selector="#cms_page_form_content_tbl"/> - </section> -</sections> diff --git a/app/code/Magento/Tinymce3/Test/Mftf/Section/AdminTinymce3FileldsSection/NewsletterWYSIWYGSection.xml b/app/code/Magento/Tinymce3/Test/Mftf/Section/AdminTinymce3FileldsSection/NewsletterWYSIWYGSection.xml new file mode 100644 index 0000000000000..14002028d9da4 --- /dev/null +++ b/app/code/Magento/Tinymce3/Test/Mftf/Section/AdminTinymce3FileldsSection/NewsletterWYSIWYGSection.xml @@ -0,0 +1,13 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + /** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +--> +<sections xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Page/etc/SectionObject.xsd"> + <section name="NewsletterWYSIWYGSection"> + <element name="TinyMCE3" type="text" selector="#cms_page_form_content_tbl"/> + </section> +</sections> diff --git a/app/code/Magento/Tinymce3/Test/Mftf/Section/AdminTinymce3FileldsSection/ProductWYSIWYGSection.xml b/app/code/Magento/Tinymce3/Test/Mftf/Section/AdminTinymce3FileldsSection/ProductWYSIWYGSection.xml new file mode 100644 index 0000000000000..9ce4e067169ec --- /dev/null +++ b/app/code/Magento/Tinymce3/Test/Mftf/Section/AdminTinymce3FileldsSection/ProductWYSIWYGSection.xml @@ -0,0 +1,13 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + /** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +--> +<sections xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Page/etc/SectionObject.xsd"> + <section name="ProductWYSIWYGSection"> + <element name="Tinymce3MSG" type="button" selector=".admin__field-error"/> + </section> +</sections> diff --git a/app/code/Magento/Tinymce3/Test/Mftf/Section/AdminTinymce3FileldsSection/TinyMCESection.xml b/app/code/Magento/Tinymce3/Test/Mftf/Section/AdminTinymce3FileldsSection/TinyMCESection.xml new file mode 100644 index 0000000000000..cb46bed781e5a --- /dev/null +++ b/app/code/Magento/Tinymce3/Test/Mftf/Section/AdminTinymce3FileldsSection/TinyMCESection.xml @@ -0,0 +1,14 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + /** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +--> +<sections xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Page/etc/SectionObject.xsd"> + <section name="TinyMCESection"> + <element name="TinyMCE3" type="text" selector="#cms_page_form_content_tbl"/> + <element name="InsertImageBtnTinyMCE3" type="button" selector="#cms_page_form_content_image"/> + </section> +</sections> diff --git a/app/code/Magento/Tinymce3/Test/Mftf/Test/AdminSwitchWYSIWYGOptionsTest.xml b/app/code/Magento/Tinymce3/Test/Mftf/Test/AdminSwitchWYSIWYGOptionsTest.xml index deb94a7f30d34..9c4e7bf3a646a 100644 --- a/app/code/Magento/Tinymce3/Test/Mftf/Test/AdminSwitchWYSIWYGOptionsTest.xml +++ b/app/code/Magento/Tinymce3/Test/Mftf/Test/AdminSwitchWYSIWYGOptionsTest.xml @@ -19,7 +19,7 @@ <testCaseId value="MC-6114"/> </annotations> <before> - <actionGroup ref="LoginActionGroup" stepKey="loginGetFromGeneralFile"/> + <actionGroup ref="AdminLoginActionGroup" stepKey="loginGetFromGeneralFile"/> <actionGroup ref="EnabledWYSIWYGActionGroup" stepKey="enableWYSIWYG"/> </before> <amOnPage url="{{ConfigurationStoresPage.url}}" stepKey="navigateToWYSIWYGConfigPage1"/> @@ -81,7 +81,7 @@ <actionGroup ref="SwitchToVersion4ActionGroup" stepKey="switchToTinyMCE4" /> <after> <actionGroup ref="DisabledWYSIWYGActionGroup" stepKey="disableWYSIWYG"/> - <actionGroup ref="logout" stepKey="logout"/> + <actionGroup ref="AdminLogoutActionGroup" stepKey="logout"/> </after> </test> </tests> diff --git a/app/code/Magento/Tinymce3/registration.php b/app/code/Magento/Tinymce3/registration.php index fd57434a6280f..1e561dd1ae36a 100644 --- a/app/code/Magento/Tinymce3/registration.php +++ b/app/code/Magento/Tinymce3/registration.php @@ -3,6 +3,6 @@ * Copyright © Magento, Inc. All rights reserved. * See COPYING.txt for license details. */ -use \Magento\Framework\Component\ComponentRegistrar; +use Magento\Framework\Component\ComponentRegistrar; ComponentRegistrar::register(ComponentRegistrar::MODULE, 'Magento_Tinymce3', __DIR__); diff --git a/app/code/Magento/Tinymce3/view/base/web/tiny_mce/plugins/table/editor_plugin_src.js b/app/code/Magento/Tinymce3/view/base/web/tiny_mce/plugins/table/editor_plugin_src.js index 8170e4ed44eea..512f3520f2ef0 100644 --- a/app/code/Magento/Tinymce3/view/base/web/tiny_mce/plugins/table/editor_plugin_src.js +++ b/app/code/Magento/Tinymce3/view/base/web/tiny_mce/plugins/table/editor_plugin_src.js @@ -1222,7 +1222,7 @@ var last; // Skip empty text nodes form the end - for (last = ed.getBody().lastChild; last && last.nodeType == 3 && !last.nodeValue.length; last = last.previousSibling) ; + for (last = ed.getBody().lastChild; last && last.nodeType == 3 && !last.nodeValue.length; last = last.previousSibling); if (last && last.nodeName == 'TABLE') ed.dom.add(ed.getBody(), 'p', null, '<br mce_bogus="1" />'); diff --git a/app/code/Magento/Translation/Model/FileManager.php b/app/code/Magento/Translation/Model/FileManager.php index 387173f6de0ba..95fb3f2a5d4e9 100644 --- a/app/code/Magento/Translation/Model/FileManager.php +++ b/app/code/Magento/Translation/Model/FileManager.php @@ -3,11 +3,13 @@ * Copyright © Magento, Inc. All rights reserved. * See COPYING.txt for license details. */ +declare(strict_types=1); + namespace Magento\Translation\Model; use Magento\Framework\App\Filesystem\DirectoryList; -use Magento\Framework\App\ObjectManager; -use Magento\Translation\Model\Inline\File as TranslationFile; +use Magento\Framework\Filesystem\Driver\File; +use Magento\Framework\View\Asset\Repository; /** * A service for handling Translation config files @@ -20,41 +22,33 @@ class FileManager const TRANSLATION_CONFIG_FILE_NAME = 'Magento_Translation/js/i18n-config.js'; /** - * @var \Magento\Framework\View\Asset\Repository + * @var Repository */ private $assetRepo; /** - * @var \Magento\Framework\App\Filesystem\DirectoryList + * @var DirectoryList */ private $directoryList; /** - * @var \Magento\Framework\Filesystem\Driver\File + * @var File */ private $driverFile; /** - * @var TranslationFile - */ - private $translationFile; - - /** - * @param \Magento\Framework\View\Asset\Repository $assetRepo - * @param \Magento\Framework\App\Filesystem\DirectoryList $directoryList - * @param \Magento\Framework\Filesystem\Driver\File $driverFile - * @param TranslationFile $translationFile + * @param Repository $assetRepo + * @param DirectoryList $directoryList + * @param File $driverFile */ public function __construct( - \Magento\Framework\View\Asset\Repository $assetRepo, - \Magento\Framework\App\Filesystem\DirectoryList $directoryList, - \Magento\Framework\Filesystem\Driver\File $driverFile, - \Magento\Translation\Model\Inline\File $translationFile = null + Repository $assetRepo, + DirectoryList $directoryList, + File $driverFile ) { $this->assetRepo = $assetRepo; $this->directoryList = $directoryList; $this->driverFile = $driverFile; - $this->translationFile = $translationFile ?: ObjectManager::getInstance()->get(TranslationFile::class); } /** @@ -71,7 +65,7 @@ public function createTranslateConfigAsset() } /** - * gets current js-translation.json timestamp + * Gets current js-translation.json timestamp * * @return string|void */ @@ -87,18 +81,22 @@ public function getTranslationFileTimestamp() } /** + * Retrieve full path for translation file + * * @return string */ protected function getTranslationFileFullPath() { return $this->directoryList->getPath(DirectoryList::STATIC_VIEW) . - \DIRECTORY_SEPARATOR . - $this->assetRepo->getStaticViewFileContext()->getPath() . - \DIRECTORY_SEPARATOR . - Js\Config::DICTIONARY_FILE_NAME; + \DIRECTORY_SEPARATOR . + $this->assetRepo->getStaticViewFileContext()->getPath() . + \DIRECTORY_SEPARATOR . + Js\Config::DICTIONARY_FILE_NAME; } /** + * Retrieve path for translation file + * * @return string */ public function getTranslationFilePath() @@ -107,7 +105,10 @@ public function getTranslationFilePath() } /** + * Update translation file with content + * * @param string $content + * * @return void */ public function updateTranslationFileContent($content) @@ -115,9 +116,11 @@ public function updateTranslationFileContent($content) $translationDir = $this->directoryList->getPath(DirectoryList::STATIC_VIEW) . \DIRECTORY_SEPARATOR . $this->assetRepo->getStaticViewFileContext()->getPath(); + if (!$this->driverFile->isExists($this->getTranslationFileFullPath())) { $this->driverFile->createDirectory($translationDir); } + $this->driverFile->filePutContents($this->getTranslationFileFullPath(), $content); } diff --git a/app/code/Magento/Translation/Model/Inline/Parser.php b/app/code/Magento/Translation/Model/Inline/Parser.php index ca66d288d6f60..9c0c8dc696c1b 100644 --- a/app/code/Magento/Translation/Model/Inline/Parser.php +++ b/app/code/Magento/Translation/Model/Inline/Parser.php @@ -3,24 +3,27 @@ * Copyright © Magento, Inc. All rights reserved. * See COPYING.txt for license details. */ +declare(strict_types=1); namespace Magento\Translation\Model\Inline; +use Magento\Backend\App\Area\FrontNameResolver; +use Magento\Framework\Translate\Inline\ParserInterface; +use Magento\Translation\Model\ResourceModel\StringFactory; +use Magento\Translation\Model\ResourceModel\StringUtils; use Magento\Translation\Model\ResourceModel\StringUtilsFactory; use Magento\Store\Model\StoreManagerInterface; use Magento\Framework\App\State; use Magento\Framework\App\Cache\TypeListInterface; use Magento\Framework\Translate\InlineInterface; use Magento\Framework\Escaper; -use Magento\Framework\App\ObjectManager; -use Magento\Translation\Model\Inline\CacheManager; /** * Parses content and applies necessary html element wrapping and client scripts for inline translation. * * @SuppressWarnings(PHPMD.CouplingBetweenObjects) */ -class Parser implements \Magento\Framework\Translate\Inline\ParserInterface +class Parser implements ParserInterface { /** * data-translate html element attribute name @@ -105,7 +108,7 @@ class Parser implements \Magento\Framework\Translate\Inline\ParserInterface ]; /** - * @var \Magento\Translation\Model\ResourceModel\StringFactory + * @var StringFactory */ protected $_resourceFactory; @@ -144,23 +147,6 @@ class Parser implements \Magento\Framework\Translate\Inline\ParserInterface */ private $relatedCacheTypes; - /** - * Return cache manager - * - * @return CacheManager - * - * @deprecated 100.1.0 - */ - private function getCacheManger() - { - if (!$this->cacheManager instanceof CacheManager) { - $this->cacheManager = ObjectManager::getInstance()->get( - CacheManager::class - ); - } - return $this->cacheManager; - } - /** * Initialize base inline translation model * @@ -170,8 +156,9 @@ private function getCacheManger() * @param State $appState * @param TypeListInterface $appCache * @param InlineInterface $translateInline + * @param Escaper $escaper + * @param CacheManager $cacheManager * @param array $relatedCacheTypes - * @param Escaper|null $escaper */ public function __construct( StringUtilsFactory $resource, @@ -180,8 +167,9 @@ public function __construct( State $appState, TypeListInterface $appCache, InlineInterface $translateInline, - array $relatedCacheTypes = [], - Escaper $escaper = null + Escaper $escaper, + CacheManager $cacheManager, + array $relatedCacheTypes = [] ) { $this->_resourceFactory = $resource; $this->_storeManager = $storeManager; @@ -189,16 +177,16 @@ public function __construct( $this->_appState = $appState; $this->_appCache = $appCache; $this->_translateInline = $translateInline; + $this->escaper = $escaper; + $this->cacheManager = $cacheManager; $this->relatedCacheTypes = $relatedCacheTypes; - $this->escaper = $escaper ?? ObjectManager::getInstance()->get( - Escaper::class - ); } /** * Parse and save edited translation * * @param array $translateParams + * * @return array */ public function processAjaxPost(array $translateParams) @@ -206,6 +194,7 @@ public function processAjaxPost(array $translateParams) if (!$this->_translateInline->isAllowed()) { return ['inline' => 'not allowed']; } + if (!empty($this->relatedCacheTypes)) { $this->_appCache->invalidate($this->relatedCacheTypes); } @@ -216,18 +205,16 @@ public function processAjaxPost(array $translateParams) /** @var $validStoreId int */ $validStoreId = $this->_storeManager->getStore()->getId(); - /** @var $resource \Magento\Translation\Model\ResourceModel\StringUtils */ + /** @var $resource StringUtils */ $resource = $this->_resourceFactory->create(); foreach ($translateParams as $param) { - if ($this->_appState->getAreaCode() == \Magento\Backend\App\Area\FrontNameResolver::AREA_CODE) { + if ($this->_appState->getAreaCode() == FrontNameResolver::AREA_CODE) { + $storeId = 0; + } elseif (empty($param['perstore'])) { + $resource->deleteTranslate($param['original'], null, false); $storeId = 0; } else { - if (empty($param['perstore'])) { - $resource->deleteTranslate($param['original'], null, false); - $storeId = 0; - } else { - $storeId = $validStoreId; - } + $storeId = $validStoreId; } $resource->saveTranslate( $param['original'], @@ -237,13 +224,14 @@ public function processAjaxPost(array $translateParams) ); } - return $this->getCacheManger()->updateAndGetTranslations(); + return $this->cacheManager->updateAndGetTranslations(); } /** * Validate the structure of translation parameters * * @param array $translateParams + * * @return void * @throws \InvalidArgumentException */ @@ -263,6 +251,7 @@ protected function _validateTranslationParams(array $translateParams) * * @param array $translateParams * @param array $fieldNames Names of fields values of which are to be filtered + * * @return void */ protected function _filterTranslationParams(array &$translateParams, array $fieldNames) @@ -278,6 +267,7 @@ protected function _filterTranslationParams(array &$translateParams, array $fiel * Replace html body with translation wrapping. * * @param string $body + * * @return string */ public function processResponseBodyString($body) @@ -305,6 +295,7 @@ public function getContent() * Sets the body content that is being parsed passed upon the passed in string. * * @param string $content + * * @return void */ public function setContent($content) @@ -316,6 +307,7 @@ public function setContent($content) * Set flag about parsed content is Json * * @param bool $flag + * * @return $this */ public function setIsJson($flag) @@ -329,7 +321,9 @@ public function setIsJson($flag) * * @param array $matches * @param array $options + * * @return string + * * @SuppressWarnings(PHPMD.UnusedFormalParameter) */ protected function _getAttributeLocation($matches, $options) @@ -343,26 +337,25 @@ protected function _getAttributeLocation($matches, $options) * * @param array $matches * @param array $options + * * @return string + * * @SuppressWarnings(PHPMD.UnusedFormalParameter) */ protected function _getTagLocation($matches, $options) { $tagName = strtolower($options['tagName']); - if (isset($options['tagList'][$tagName])) { - return $options['tagList'][$tagName]; - } - - return ucfirst($tagName) . ' Text'; + return $options['tagList'][$tagName] ?? (ucfirst($tagName) . ' Text'); } /** - * Format translation for special tags. Adding translate mode attribute for vde requests. + * Format translation for special tags. Adding translate mode attribute for vde requests. * * @param string $tagHtml * @param string $tagName * @param array $trArr + * * @return string */ protected function _applySpecialTagsFormat($tagHtml, $tagName, $trArr) @@ -378,6 +371,7 @@ protected function _applySpecialTagsFormat($tagHtml, $tagName, $trArr) $specialTags .= '>' . strtoupper($tagName); } $specialTags .= '</span>'; + return $specialTags; } @@ -387,6 +381,7 @@ protected function _applySpecialTagsFormat($tagHtml, $tagName, $trArr) * @param string $tagHtml * @param string $tagName * @param array $trArr + * * @return string */ protected function _applySimpleTagsFormat($tagHtml, $tagName, $trArr) @@ -404,6 +399,7 @@ protected function _applySimpleTagsFormat($tagHtml, $tagName, $trArr) $simpleTags .= ' ' . $additionalAttr; } $simpleTags .= substr($tagHtml, strlen($tagName) + 1); + return $simpleTags; } @@ -414,6 +410,7 @@ protected function _applySimpleTagsFormat($tagHtml, $tagName, $trArr) * @param string $text * @param callable $locationCallback * @param array $options + * * @return array */ private function _getTranslateData(string $regexp, string &$text, callable $locationCallback, array $options = []) @@ -449,6 +446,7 @@ private function _tagAttributes() * Prepare tags inline translates for the content * * @param string &$content + * * @return void */ private function _prepareTagAttributesForContent(&$content) @@ -494,6 +492,7 @@ private function _prepareTagAttributesForContent(&$content) * * @param string $name * @param string $value + * * @return string */ private function _getHtmlAttribute($name, $value) @@ -505,6 +504,7 @@ private function _getHtmlAttribute($name, $value) * Add data-translate-mode attribute * * @param string $trAttr + * * @return string */ private function _addTranslateAttribute($trAttr) @@ -514,6 +514,7 @@ private function _addTranslateAttribute($trAttr) if ($additionalAttr !== null) { $translateAttr .= ' ' . $additionalAttr . ' '; } + return $translateAttr; } @@ -526,9 +527,9 @@ private function _getHtmlQuote() { if ($this->_isJson) { return '\"'; - } else { - return '"'; } + + return '"'; } /** @@ -560,6 +561,7 @@ function ($tagHtml, $tagName, $trArr) { * @param string $content * @param array $tagsList * @param callable $formatCallback + * * @return void */ private function _translateTags(string &$content, array $tagsList, callable $formatCallback) @@ -642,6 +644,7 @@ private function _translateTags(string &$content, array $tagsList, callable $for * @param string $body * @param string $tagName * @param int $from + * * @return bool|int return false if end of tag is not found */ private function _findEndOfTag($body, $tagName, $from) @@ -658,11 +661,11 @@ private function _findEndOfTag($body, $tagName, $from) } $length = $end - $from + $tagLength + 3; } - if (preg_match('#<\\\\?\/' . $tagName . '\s*?>#i', $body, $tagMatch, null, $end)) { + if (preg_match('#<\\\\?\/' . $tagName . '\s*?>#i', $body, $tagMatch, 0, $end)) { return $end + strlen($tagMatch[0]); - } else { - return false; } + + return false; } /** @@ -700,6 +703,7 @@ private function _otherText() * * @param string $data * @param string $text + * * @return string */ protected function _getDataTranslateSpan($data, $text) @@ -717,6 +721,7 @@ protected function _getDataTranslateSpan($data, $text) * Add an additional html attribute if needed. * * @param mixed $tagName + * * @return string */ protected function _getAdditionalHtmlAttribute($tagName = null) diff --git a/app/code/Magento/Translation/Model/Json/PreProcessor.php b/app/code/Magento/Translation/Model/Json/PreProcessor.php index f19d6a8fc80c4..a5ea52f8df581 100644 --- a/app/code/Magento/Translation/Model/Json/PreProcessor.php +++ b/app/code/Magento/Translation/Model/Json/PreProcessor.php @@ -3,12 +3,12 @@ * Copyright © Magento, Inc. All rights reserved. * See COPYING.txt for license details. */ +declare(strict_types=1); namespace Magento\Translation\Model\Json; use Magento\Framework\App\Area; use Magento\Framework\App\AreaList; -use Magento\Framework\App\ObjectManager; use Magento\Framework\TranslateInterface; use Magento\Framework\View\Asset\File\FallbackContext; use Magento\Framework\View\Asset\PreProcessor\Chain; @@ -57,26 +57,27 @@ class PreProcessor implements PreProcessorInterface * @param DataProviderInterface $dataProvider * @param AreaList $areaList * @param TranslateInterface $translate - * @param DesignInterface|null $viewDesign + * @param DesignInterface $viewDesign */ public function __construct( Config $config, DataProviderInterface $dataProvider, AreaList $areaList, TranslateInterface $translate, - DesignInterface $viewDesign = null + DesignInterface $viewDesign ) { $this->config = $config; $this->dataProvider = $dataProvider; $this->areaList = $areaList; $this->translate = $translate; - $this->viewDesign = $viewDesign ?? ObjectManager::getInstance()->get(DesignInterface::class); + $this->viewDesign = $viewDesign; } /** * Transform content and/or content type for the specified preprocessing chain object * * @param Chain $chain + * * @return void */ public function process(Chain $chain) @@ -110,6 +111,7 @@ public function process(Chain $chain) * Is provided path the path to translation dictionary * * @param string $path + * * @return bool */ protected function isDictionaryPath($path) diff --git a/app/code/Magento/Translation/Model/ResourceModel/StringUtils.php b/app/code/Magento/Translation/Model/ResourceModel/StringUtils.php index be7656fbf61a7..4cefdd58609a6 100644 --- a/app/code/Magento/Translation/Model/ResourceModel/StringUtils.php +++ b/app/code/Magento/Translation/Model/ResourceModel/StringUtils.php @@ -3,15 +3,23 @@ * Copyright © Magento, Inc. All rights reserved. * See COPYING.txt for license details. */ +declare(strict_types=1); + namespace Magento\Translation\Model\ResourceModel; +use Magento\Framework\App\ScopeResolverInterface; +use Magento\Framework\DB\Select; use Magento\Framework\Escaper; -use Magento\Framework\App\ObjectManager; +use Magento\Framework\Locale\ResolverInterface; +use Magento\Framework\Model\AbstractModel; +use Magento\Framework\Model\ResourceModel\Db\AbstractDb; +use Magento\Framework\Model\ResourceModel\Db\Context; +use Magento\Store\Model\Store; /** * String translation utilities */ -class StringUtils extends \Magento\Framework\Model\ResourceModel\Db\AbstractDb +class StringUtils extends AbstractDb { /** * @var Escaper @@ -19,12 +27,12 @@ class StringUtils extends \Magento\Framework\Model\ResourceModel\Db\AbstractDb private $escaper; /** - * @var \Magento\Framework\Locale\ResolverInterface + * @var ResolverInterface */ protected $_localeResolver; /** - * @var \Magento\Framework\App\ScopeResolverInterface + * @var ScopeResolverInterface */ protected $scopeResolver; @@ -34,27 +42,25 @@ class StringUtils extends \Magento\Framework\Model\ResourceModel\Db\AbstractDb protected $scope; /** - * @param \Magento\Framework\Model\ResourceModel\Db\Context $context - * @param \Magento\Framework\Locale\ResolverInterface $localeResolver - * @param \Magento\Framework\App\ScopeResolverInterface $scopeResolver + * @param Context $context + * @param ResolverInterface $localeResolver + * @param ScopeResolverInterface $scopeResolver + * @param Escaper $escaper * @param string $connectionName * @param string|null $scope - * @param Escaper|null $escaper */ public function __construct( - \Magento\Framework\Model\ResourceModel\Db\Context $context, - \Magento\Framework\Locale\ResolverInterface $localeResolver, - \Magento\Framework\App\ScopeResolverInterface $scopeResolver, + Context $context, + ResolverInterface $localeResolver, + ScopeResolverInterface $scopeResolver, + Escaper $escaper, $connectionName = null, - $scope = null, - Escaper $escaper = null + $scope = null ) { $this->_localeResolver = $localeResolver; $this->scopeResolver = $scopeResolver; + $this->escaper = $escaper; $this->scope = $scope; - $this->escaper = $escaper ?? ObjectManager::getInstance()->get( - Escaper::class - ); parent::__construct($context, $connectionName); } @@ -69,14 +75,15 @@ protected function _construct() } /** - * Load + * Load an object * - * @param \Magento\Framework\Model\AbstractModel $object + * @param AbstractModel $object * @param String $value * @param String $field + * * @return array|$this */ - public function load(\Magento\Framework\Model\AbstractModel $object, $value, $field = null) + public function load(AbstractModel $object, $value, $field = null) { if (is_string($value)) { $select = $this->getConnection()->select()->from( @@ -88,9 +95,9 @@ public function load(\Magento\Framework\Model\AbstractModel $object, $value, $fi $object->setData($result); $this->_afterLoad($object); return $result; - } else { - return parent::load($object, $value, $field); } + + return parent::load($object, $value, $field); } /** @@ -98,23 +105,25 @@ public function load(\Magento\Framework\Model\AbstractModel $object, $value, $fi * * @param String $field * @param String $value - * @param \Magento\Framework\Model\AbstractModel $object - * @return \Magento\Framework\DB\Select + * @param AbstractModel $object + * + * @return Select */ protected function _getLoadSelect($field, $value, $object) { $select = parent::_getLoadSelect($field, $value, $object); - $select->where('store_id = ?', \Magento\Store\Model\Store::DEFAULT_STORE_ID); + $select->where('store_id = ?', Store::DEFAULT_STORE_ID); return $select; } /** * After translation loading * - * @param \Magento\Framework\Model\AbstractModel $object + * @param AbstractModel $object + * * @return $this */ - public function _afterLoad(\Magento\Framework\Model\AbstractModel $object) + public function _afterLoad(AbstractModel $object) { $connection = $this->getConnection(); $select = $connection->select()->from( @@ -131,10 +140,11 @@ public function _afterLoad(\Magento\Framework\Model\AbstractModel $object) /** * Before save * - * @param \Magento\Framework\Model\AbstractModel $object + * @param AbstractModel $object + * * @return $this */ - protected function _beforeSave(\Magento\Framework\Model\AbstractModel $object) + protected function _beforeSave(AbstractModel $object) { $connection = $this->getConnection(); $select = $connection->select() @@ -142,7 +152,7 @@ protected function _beforeSave(\Magento\Framework\Model\AbstractModel $object) ->where('string = :string') ->where('store_id = :store_id'); - $bind = ['string' => $object->getString(), 'store_id' => \Magento\Store\Model\Store::DEFAULT_STORE_ID]; + $bind = ['string' => $object->getString(), 'store_id' => Store::DEFAULT_STORE_ID]; $object->setId($connection->fetchOne($select, $bind)); return parent::_beforeSave($object); @@ -151,10 +161,11 @@ protected function _beforeSave(\Magento\Framework\Model\AbstractModel $object) /** * After save * - * @param \Magento\Framework\Model\AbstractModel $object + * @param AbstractModel $object + * * @return $this */ - protected function _afterSave(\Magento\Framework\Model\AbstractModel $object) + protected function _afterSave(AbstractModel $object) { $connection = $this->getConnection(); $select = $connection->select()->from( @@ -192,6 +203,7 @@ protected function _afterSave(\Magento\Framework\Model\AbstractModel $object) * @param string $string * @param string $locale * @param int|null $storeId + * * @return $this */ public function deleteTranslate($string, $locale = null, $storeId = null) @@ -203,7 +215,7 @@ public function deleteTranslate($string, $locale = null, $storeId = null) $where = ['locale = ?' => $locale, 'string = ?' => $string]; if ($storeId === false) { - $where['store_id > ?'] = \Magento\Store\Model\Store::DEFAULT_STORE_ID; + $where['store_id > ?'] = Store::DEFAULT_STORE_ID; } elseif ($storeId !== null) { $where['store_id = ?'] = $storeId; } @@ -220,6 +232,7 @@ public function deleteTranslate($string, $locale = null, $storeId = null) * @param String $translate * @param String $locale * @param int|null $storeId + * * @return $this */ public function saveTranslate($string, $translate, $locale = null, $storeId = null) diff --git a/app/code/Magento/Translation/Model/ResourceModel/Translate.php b/app/code/Magento/Translation/Model/ResourceModel/Translate.php index cf379254d4320..ef14758f9f917 100644 --- a/app/code/Magento/Translation/Model/ResourceModel/Translate.php +++ b/app/code/Magento/Translation/Model/ResourceModel/Translate.php @@ -3,26 +3,29 @@ * Copyright © Magento, Inc. All rights reserved. * See COPYING.txt for license details. */ +declare(strict_types=1); + namespace Magento\Translation\Model\ResourceModel; -use Magento\Framework\App\ObjectManager; use Magento\Framework\App\DeploymentConfig; use Magento\Framework\App\Config; +use Magento\Framework\App\ObjectManager; +use Magento\Framework\App\ScopeResolverInterface; +use Magento\Framework\Model\ResourceModel\Db\AbstractDb; +use Magento\Framework\Model\ResourceModel\Db\Context; +use Magento\Framework\Translate\ResourceInterface; use Magento\Translation\App\Config\Type\Translation; -class Translate extends \Magento\Framework\Model\ResourceModel\Db\AbstractDb implements - \Magento\Framework\Translate\ResourceInterface +/** + * Translate data resource model + */ +class Translate extends AbstractDb implements ResourceInterface { /** - * @var \Magento\Framework\App\ScopeResolverInterface + * @var ScopeResolverInterface */ protected $scopeResolver; - /** - * @var null|string - */ - protected $scope; - /** * @var Config */ @@ -34,19 +37,30 @@ class Translate extends \Magento\Framework\Model\ResourceModel\Db\AbstractDb imp private $deployedConfig; /** - * @param \Magento\Framework\Model\ResourceModel\Db\Context $context - * @param \Magento\Framework\App\ScopeResolverInterface $scopeResolver + * @var null|string + */ + protected $scope; + + /** + * @param Context $context + * @param ScopeResolverInterface $scopeResolver * @param string $connectionName * @param null|string $scope + * @param Config|null $appConfig + * @param DeploymentConfig|null $deployedConfig */ public function __construct( - \Magento\Framework\Model\ResourceModel\Db\Context $context, - \Magento\Framework\App\ScopeResolverInterface $scopeResolver, + Context $context, + ScopeResolverInterface $scopeResolver, $connectionName = null, - $scope = null + $scope = null, + ?Config $appConfig = null, + ?DeploymentConfig $deployedConfig = null ) { $this->scopeResolver = $scopeResolver; $this->scope = $scope; + $this->appConfig = $appConfig ?? ObjectManager::getInstance()->get(Config::class); + $this->deployedConfig = $deployedConfig ?? ObjectManager::getInstance()->get(DeploymentConfig::class); parent::__construct($context, $connectionName); } @@ -65,6 +79,7 @@ protected function _construct() * * @param int $storeId * @param string $locale + * * @return array */ public function getTranslationArray($storeId = null, $locale = null) @@ -74,7 +89,7 @@ public function getTranslationArray($storeId = null, $locale = null) } $locale = (string) $locale; - $data = $this->getAppConfig()->get( + $data = $this->appConfig->get( Translation::CONFIG_TYPE, $locale . '/' . $this->getStoreCode($storeId), [] @@ -98,6 +113,7 @@ public function getTranslationArray($storeId = null, $locale = null) * * @param array $strings * @param int|null $storeId + * * @return array */ public function getTranslationArrayByStrings(array $strings, $storeId = null) @@ -141,7 +157,7 @@ public function getMainChecksum() */ public function getConnection() { - if (!$this->getDeployedConfig()->isDbAvailable()) { + if (!$this->deployedConfig->isDbAvailable()) { return false; } return parent::getConnection(); @@ -161,34 +177,11 @@ protected function getStoreId() * Retrieve store code by store id * * @param int $storeId + * * @return string */ private function getStoreCode($storeId) { return $this->scopeResolver->getScope($storeId)->getCode(); } - - /** - * @deprecated 100.1.2 - * @return DeploymentConfig - */ - private function getDeployedConfig() - { - if ($this->deployedConfig === null) { - $this->deployedConfig = ObjectManager::getInstance()->get(DeploymentConfig::class); - } - return $this->deployedConfig; - } - - /** - * @deprecated 100.1.2 - * @return Config - */ - private function getAppConfig() - { - if ($this->appConfig === null) { - $this->appConfig = ObjectManager::getInstance()->get(Config::class); - } - return $this->appConfig; - } } diff --git a/app/code/Magento/Translation/Test/Mftf/ActionGroup/AssertElementInTranslateInlineModeActionGroup.xml b/app/code/Magento/Translation/Test/Mftf/ActionGroup/AssertElementInTranslateInlineModeActionGroup.xml index 2bc783f128c33..6128aeeb8918c 100644 --- a/app/code/Magento/Translation/Test/Mftf/ActionGroup/AssertElementInTranslateInlineModeActionGroup.xml +++ b/app/code/Magento/Translation/Test/Mftf/ActionGroup/AssertElementInTranslateInlineModeActionGroup.xml @@ -17,9 +17,18 @@ <executeJS function="return window.getComputedStyle(document.querySelector('{{elementSelector}}')).getPropertyValue('outline-style')" stepKey="getBorderType"/> <executeJS function="return window.getComputedStyle(document.querySelector('{{elementSelector}}')).getPropertyValue('outline-width')" stepKey="getBorderWidth"/> - <assertContains actual="$getBorderColor" expected="{{TranslateInlineModeStyleData.borderColor}}" expectedType="string" stepKey="assertBorderColor"/> - <assertContains actual="$getBorderType" expected="{{TranslateInlineModeStyleData.borderType}}" expectedType="string" stepKey="assertBorderType"/> - <assertContains actual="$getBorderWidth" expected="{{TranslateInlineModeStyleData.borderWidth}}" expectedType="string" stepKey="assertBorderWidth"/> + <assertContains stepKey="assertBorderColor"> + <actualResult type="const">$getBorderColor</actualResult> + <expectedResult type="string">{{TranslateInlineModeStyleData.borderColor}}</expectedResult> + </assertContains> + <assertContains stepKey="assertBorderType"> + <actualResult type="const">$getBorderType</actualResult> + <expectedResult type="string">{{TranslateInlineModeStyleData.borderType}}</expectedResult> + </assertContains> + <assertContains stepKey="assertBorderWidth"> + <actualResult type="const">$getBorderWidth</actualResult> + <expectedResult type="string">{{TranslateInlineModeStyleData.borderWidth}}</expectedResult> + </assertContains> </actionGroup> </actionGroups> \ No newline at end of file diff --git a/app/code/Magento/Translation/Test/Mftf/Test/AdminInlineTranslationOnCheckoutTest.xml b/app/code/Magento/Translation/Test/Mftf/Test/AdminInlineTranslationOnCheckoutTest.xml index 08448f7735f7c..ad0cf1a5a1bc2 100644 --- a/app/code/Magento/Translation/Test/Mftf/Test/AdminInlineTranslationOnCheckoutTest.xml +++ b/app/code/Magento/Translation/Test/Mftf/Test/AdminInlineTranslationOnCheckoutTest.xml @@ -17,9 +17,12 @@ <testCaseId value="MC-11505"/> <group value="translation"/> <group value="checkout"/> + <skip> + <issueId value="MC-31663"/> + </skip> </annotations> <before> - <actionGroup ref="LoginAsAdmin" stepKey="LoginAsAdmin"/> + <actionGroup ref="AdminLoginActionGroup" stepKey="LoginAsAdmin"/> <!--Product and a customer is created --> <createData entity="Simple_US_Customer_Multiple_Addresses" stepKey="createCustomer"/> @@ -43,7 +46,7 @@ <deleteData createDataKey="createCustomer3" stepKey="deleteCustomer3"/> <deleteData createDataKey="createCategory" stepKey="deleteCategory"/> <deleteData createDataKey="createProduct" stepKey="deleteProduct"/> - <actionGroup ref="logout" stepKey="logout"/> + <actionGroup ref="AdminLogoutActionGroup" stepKey="logout"/> <!-- Set production mode --> <!-- <magentoCLI command="cache:flush" stepKey="flushCache"/>--> diff --git a/app/code/Magento/Translation/Test/Unit/App/Config/Type/TranslationTest.php b/app/code/Magento/Translation/Test/Unit/App/Config/Type/TranslationTest.php index 6b262b9e9f93c..8759c9048fe0f 100644 --- a/app/code/Magento/Translation/Test/Unit/App/Config/Type/TranslationTest.php +++ b/app/code/Magento/Translation/Test/Unit/App/Config/Type/TranslationTest.php @@ -5,7 +5,6 @@ */ namespace Magento\Translation\Test\Unit\App\Config\Type; -use Magento\Authorizenet\Helper\Backend\Data; use Magento\Framework\App\Cache\Type\Translate; use Magento\Framework\App\Config\ConfigSourceInterface; use Magento\Framework\Cache\FrontendInterface; diff --git a/app/code/Magento/Translation/Test/Unit/Model/Inline/ParserTest.php b/app/code/Magento/Translation/Test/Unit/Model/Inline/ParserTest.php index e11618831552f..552f3118748f8 100644 --- a/app/code/Magento/Translation/Test/Unit/Model/Inline/ParserTest.php +++ b/app/code/Magento/Translation/Test/Unit/Model/Inline/ParserTest.php @@ -3,22 +3,27 @@ * Copyright © Magento, Inc. All rights reserved. * See COPYING.txt for license details. */ +declare(strict_types=1); + namespace Magento\Translation\Test\Unit\Model\Inline; -use Magento\Translation\Model\Inline\Parser; +use Magento\Framework\App\Cache\TypeListInterface; +use Magento\Framework\App\State; use Magento\Framework\TestFramework\Unit\Helper\ObjectManager; use Magento\Framework\Translate\InlineInterface; -use Magento\Framework\App\Cache\TypeListInterface; -use Magento\Store\Model\StoreManagerInterface; use Magento\Store\Api\Data\StoreInterface; -use Magento\Translation\Model\ResourceModel\StringUtilsFactory; -use Magento\Translation\Model\ResourceModel\StringUtils; +use Magento\Store\Model\StoreManagerInterface; use Magento\Translation\Model\Inline\CacheManager; +use Magento\Translation\Model\Inline\Parser; +use Magento\Translation\Model\ResourceModel\StringUtils; +use Magento\Translation\Model\ResourceModel\StringUtilsFactory; +use PHPUnit\Framework\MockObject\MockObject; +use PHPUnit\Framework\TestCase; /** * Class ParserTest to test \Magento\Translation\Model\Inline\Parser */ -class ParserTest extends \PHPUnit\Framework\TestCase +class ParserTest extends TestCase { /** * @var Parser @@ -31,47 +36,47 @@ class ParserTest extends \PHPUnit\Framework\TestCase private $objectManager; /** - * @var InlineInterface|\PHPUnit_Framework_MockObject_MockObject + * @var InlineInterface|MockObject */ private $translateInlineMock; /** - * @var TypeListInterface|\PHPUnit_Framework_MockObject_MockObject + * @var TypeListInterface|MockObject */ private $appCacheMock; /** - * @var StoreManagerInterface|\PHPUnit_Framework_MockObject_MockObject + * @var StoreManagerInterface|MockObject */ private $storeManagerMock; /** - * @var StoreInterface|\PHPUnit_Framework_MockObject_MockObject + * @var StoreInterface|MockObject */ private $storeMock; /** - * @var \Zend_Filter_Interface|\PHPUnit_Framework_MockObject_MockObject + * @var \Zend_Filter_Interface|MockObject */ private $inputFilterMock; /** - * @var StringUtilsFactory|\PHPUnit_Framework_MockObject_MockObject + * @var StringUtilsFactory|MockObject */ private $resourceFactoryMock; /** - * @var \Magento\Framework\App\State|\PHPUnit_Framework_MockObject_MockObject + * @var State|MockObject */ private $appStateMock; /** - * @var StringUtils|\PHPUnit_Framework_MockObject_MockObject + * @var StringUtils|MockObject */ private $resourceMock; /** - * @var CacheManager|\PHPUnit_Framework_MockObject_MockObject + * @var CacheManager|MockObject */ private $cacheManagerMock; @@ -79,37 +84,49 @@ protected function setUp() { $this->objectManager = new ObjectManager($this); $this->translateInlineMock = - $this->getMockForAbstractClass(\Magento\Framework\Translate\InlineInterface::class); - $this->appCacheMock = $this->getMockForAbstractClass(\Magento\Framework\App\Cache\TypeListInterface::class); - $this->storeManagerMock = $this->getMockForAbstractClass(\Magento\Store\Model\StoreManagerInterface::class); - $this->storeMock = $this->getMockForAbstractClass(\Magento\Store\Api\Data\StoreInterface::class); - $this->storeManagerMock->expects($this->any()) - ->method('getStore') + $this->getMockForAbstractClass(InlineInterface::class); + $this->appCacheMock = $this->getMockForAbstractClass(TypeListInterface::class); + $this->storeManagerMock = $this->getMockForAbstractClass(StoreManagerInterface::class); + $this->storeMock = $this->getMockForAbstractClass(StoreInterface::class); + $this->storeManagerMock->method('getStore') ->willReturn($this->storeMock); $this->resourceFactoryMock = $this->getMockBuilder( - \Magento\Translation\Model\ResourceModel\StringUtilsFactory::class + StringUtilsFactory::class ) ->disableOriginalConstructor() + ->setMethods(['create']) ->getMock(); - $this->resourceMock = $this->getMockBuilder(\Magento\Translation\Model\ResourceModel\StringUtils::class) + $this->resourceMock = $this->getMockBuilder(StringUtils::class) ->disableOriginalConstructor() ->setMethods([]) ->getMock(); - $this->inputFilterMock = $this->getMockBuilder('Zend_Filter_Interface'); + $this->inputFilterMock = $this->getMockForAbstractClass('Zend_Filter_Interface'); - $this->resourceFactoryMock->expects($this->any()) - ->method('create') + $this->resourceFactoryMock->method('create') ->willReturn($this->resourceMock); - $this->cacheManagerMock = $this->getMockBuilder(\Magento\Translation\Model\Inline\CacheManager::class) + $this->cacheManagerMock = $this->getMockBuilder(CacheManager::class) ->disableOriginalConstructor() ->setMethods([]) ->getMock(); - $this->appStateMock = $this->getMockBuilder(\Magento\Framework\App\State::class) + $this->appStateMock = $this->getMockBuilder(State::class) ->disableOriginalConstructor() ->setMethods([]) ->getMock(); + + $this->model = $this->objectManager->getObject( + Parser::class, + [ + 'resource' => $this->resourceFactoryMock, + 'storeManager' => $this->storeManagerMock, + 'inputFilter' => $this->inputFilterMock, + 'appState' => $this->appStateMock, + 'appCache' => $this->appCacheMock, + 'translateInline' => $this->translateInlineMock, + 'cacheManager' => $this->cacheManagerMock, + ] + ); } public function testProcessAjaxPostNotAllowed() @@ -118,10 +135,6 @@ public function testProcessAjaxPostNotAllowed() $this->translateInlineMock->expects($this->once()) ->method('isAllowed') ->willReturn(false); - $this->model = $this->objectManager->getObject( - Parser::class, - ['translateInline' => $this->translateInlineMock] - ); $this->assertEquals($expected, $this->model->processAjaxPost([])); } @@ -130,15 +143,6 @@ public function testProcessAjaxPost() $this->translateInlineMock->expects($this->once()) ->method('isAllowed') ->willReturn(true); - $this->model = $this->objectManager->getObject( - Parser::class, - [ - 'cacheManager' => $this->cacheManagerMock, - 'resource' => $this->resourceFactoryMock, - 'storeManager' => $this->storeManagerMock, - 'translateInline' => $this->translateInlineMock - ] - ); $this->model->processAjaxPost([]); } @@ -153,26 +157,11 @@ public function testProcessResponseBodyStringProcessingAttributesCorrectly() "data-translate=\"[{'shown':'Password','translated':'Password','original':'Password'," . "'location':'Tag attribute (ALT, TITLE, etc.)'}]\"" ]; - $this->translateInlineMock->expects($this->any())->method('getAdditionalHtmlAttribute')->willReturn(null); - - $this->model = $this->objectManager->getObject( - Parser::class, - [ - 'cacheManager' => $this->cacheManagerMock, - 'resource' => $this->resourceFactoryMock, - 'storeManager' => $this->storeManagerMock, - 'translateInline' => $this->translateInlineMock, - '_resourceFactory' => $this->resourceMock, - '_inputFilter' => $this->inputFilterMock, - '_appState' => $this->appStateMock, - '_appCache' => $this->appCacheMock, - '_translateInline' => $this->translateInlineMock - ] - ); + $this->translateInlineMock->method('getAdditionalHtmlAttribute')->willReturn(null); $processedContent = $this->model->processResponseBodyString($testContent); foreach ($processedAttributes as $attribute) { - $this->assertContains($attribute, $processedContent, "data-translate attribute not processed correctly"); + $this->assertContains($attribute, $processedContent, 'data-translate attribute not processed correctly'); } } } diff --git a/app/code/Magento/Translation/Test/Unit/Model/Js/DataProviderTest.php b/app/code/Magento/Translation/Test/Unit/Model/Js/DataProviderTest.php index b5bfbbc29a603..be52c6a744c50 100644 --- a/app/code/Magento/Translation/Test/Unit/Model/Js/DataProviderTest.php +++ b/app/code/Magento/Translation/Test/Unit/Model/Js/DataProviderTest.php @@ -3,72 +3,79 @@ * Copyright © Magento, Inc. All rights reserved. * See COPYING.txt for license details. */ +declare(strict_types=1); namespace Magento\Translation\Test\Unit\Model\Js; use Magento\Framework\App\State; use Magento\Framework\App\Utility\Files; -use Magento\Framework\Filesystem; +use Magento\Framework\Component\ComponentRegistrar; +use Magento\Framework\Component\DirSearch; +use Magento\Framework\Filesystem\File\Read; +use Magento\Framework\Filesystem\File\ReadFactory; use Magento\Framework\Filesystem\File\ReadInterface; -use Magento\Translation\Model\Js\DataProvider; -use Magento\Translation\Model\Js\Config; use Magento\Framework\Phrase\Renderer\Translate; +use Magento\Framework\TestFramework\Unit\Helper\ObjectManager; +use Magento\Translation\Model\Js\Config; +use Magento\Translation\Model\Js\DataProvider as ModelDataProvider; +use PHPUnit\Framework\TestCase; +use PHPUnit_Framework_MockObject_MockObject as MockObject; /** - * Class DataProviderTest + * Verify data provider translation * * @SuppressWarnings(PHPMD.CouplingBetweenObjects) */ -class DataProviderTest extends \PHPUnit\Framework\TestCase +class DataProviderTest extends TestCase { /** - * @var DataProvider + * @var ModelDataProvider */ - protected $model; + private $model; /** - * @var State|\PHPUnit_Framework_MockObject_MockObject + * @var State|MockObject */ - protected $appStateMock; + private $appStateMock; /** - * @var Config|\PHPUnit_Framework_MockObject_MockObject + * @var Config|MockObject */ - protected $configMock; + private $configMock; /** - * @var Files|\PHPUnit_Framework_MockObject_MockObject + * @var Files|MockObject */ - protected $filesUtilityMock; + private $filesUtilityMock; /** - * @var ReadInterface|\PHPUnit_Framework_MockObject_MockObject + * @var ReadInterface|MockObject */ - protected $fileReadMock; + private $fileReadMock; /** - * @var Translate|\PHPUnit_Framework_MockObject_MockObject + * @var Translate|MockObject */ - protected $translateMock; + private $translateMock; /** - * @return void + * @inheritDoc */ protected function setUp() { - $this->appStateMock = $this->createMock(\Magento\Framework\App\State::class); - $this->configMock = $this->createMock(\Magento\Translation\Model\Js\Config::class); - $this->filesUtilityMock = $this->createMock(\Magento\Framework\App\Utility\Files::class); - $fileReadFactory = $this->createMock(\Magento\Framework\Filesystem\File\ReadFactory::class); - $this->fileReadMock = $this->createMock(\Magento\Framework\Filesystem\File\Read::class); - $this->translateMock = $this->createMock(\Magento\Framework\Phrase\Renderer\Translate::class); + $this->appStateMock = $this->createMock(State::class); + $this->configMock = $this->createMock(Config::class); + $this->filesUtilityMock = $this->createMock(Files::class); + $fileReadFactory = $this->createMock(ReadFactory::class); + $this->fileReadMock = $this->createMock(Read::class); + $this->translateMock = $this->createMock(Translate::class); $fileReadFactory->expects($this->atLeastOnce()) ->method('create') ->willReturn($this->fileReadMock); - $dirSearch = $this->createMock(\Magento\Framework\Component\DirSearch::class); - $objectManager = new \Magento\Framework\TestFramework\Unit\Helper\ObjectManager($this); + $dirSearch = $this->createMock(DirSearch::class); + $objectManager = new ObjectManager($this); $this->model = $objectManager->getObject( - \Magento\Translation\Model\Js\DataProvider::class, + ModelDataProvider::class, [ 'appState' => $this->appStateMock, 'config' => $this->configMock, @@ -76,16 +83,19 @@ protected function setUp() 'translate' => $this->translateMock, 'dirSearch' => $dirSearch, 'filesUtility' => $this->filesUtilityMock, - 'componentRegistrar' => - $this->createMock(\Magento\Framework\Component\ComponentRegistrar::class) + 'componentRegistrar' => $this->createMock(ComponentRegistrar::class) ] ); } /** + * Verify data translate. + * + * @param array $config * @return void + * @dataProvider configDataProvider */ - public function testGetData() + public function testGetData(array $config): void { $themePath = 'blank'; $areaCode = 'adminhtml'; @@ -101,29 +111,6 @@ public function testGetData() [$areaCode, $themePath, '*', '*', [$filePaths[3]]] ]; - $expectedResult = [ - 'hello1' => 'hello1translated', - 'hello2' => 'hello2translated', - 'hello3' => 'hello3translated', - 'hello4' => 'hello4translated' - ]; - - $contentsMap = [ - 'content1$.mage.__("hello1")content1', - 'content2$.mage.__("hello2")content2', - 'content2$.mage.__("hello4")content4', // this value should be last after running data provider - 'content2$.mage.__("hello3")content3', - ]; - - $translateMap = [ - [['hello1'], [], 'hello1translated'], - [['hello2'], [], 'hello2translated'], - [['hello3'], [], 'hello3translated'], - [['hello4'], [], 'hello4translated'] - ]; - - $patterns = ['~\$\.mage\.__\(([\'"])(.+?)\1\)~']; - $this->appStateMock->expects($this->once()) ->method('getAreaCode') ->willReturn($areaCode); @@ -134,7 +121,7 @@ public function testGetData() ->method('getStaticHtmlFiles') ->willReturnMap($staticFilesMap); - foreach ($contentsMap as $index => $content) { + foreach ($config['contentsMap'] as $index => $content) { $this->fileReadMock->expects($this->at($index)) ->method('readAll') ->willReturn($content); @@ -142,34 +129,37 @@ public function testGetData() $this->configMock->expects($this->any()) ->method('getPatterns') - ->willReturn($patterns); + ->willReturn($config['patterns']); $this->translateMock->expects($this->any()) ->method('render') - ->willReturnMap($translateMap); + ->willReturnMap($config['translateMap']); $actualResult = $this->model->getData($themePath); - $this->assertEquals($expectedResult, $actualResult); + $this->assertEquals($config['expectedResult'], $actualResult); $this->assertEquals( - json_encode($expectedResult), + json_encode($config['expectedResult']), json_encode($actualResult), "Translations should be sorted by key" ); } /** + * Verify get data throwing exception. + * + * @param array $config + * @return void + * @dataProvider configDataProvider * @expectedException \Magento\Framework\Exception\LocalizedException */ - public function testGetDataThrowingException() + public function testGetDataThrowingException(array $config): void { $themePath = 'blank'; $areaCode = 'adminhtml'; - - $patterns = ['~\$\.mage\.__\(([\'"])(.+?)\1\)~']; + $patterns = $config['patterns']; $this->fileReadMock->expects($this->once()) ->method('readAll') ->willReturn('content1$.mage.__("hello1")content1'); - $this->appStateMock->expects($this->once()) ->method('getAreaCode') ->willReturn($areaCode); @@ -179,15 +169,57 @@ public function testGetDataThrowingException() $this->filesUtilityMock->expects($this->any()) ->method('getStaticHtmlFiles') ->willReturn(['test']); - $this->configMock->expects($this->any()) ->method('getPatterns') ->willReturn($patterns); - $this->translateMock->expects($this->once()) ->method('render') ->willThrowException(new \Exception('Test exception')); $this->model->getData($themePath); } + + /** + * Config data provider. + * + * @return array + */ + public function configDataProvider(): array + { + return [ + [ + [ + 'patterns' => [ + '~\$\.mage\.__\(([\'"])(.+?)\1\)~', + '~(?:i18n\:|_\.i18n\()\s*(["\'])(.*?)(?<!\\\\)\1~', + '~translate\=("\')([^\'].*?)\'\"~', + '~(?s)\$t\(\s*([\'"])(\?\<translate\>.+?)(?<!\\\)\1\s*(*SKIP)\)(?s)~', + '~translate args\=("|\'|"\'|\\\"\')([^\'].*?)(\'\\\"|\'"|\'|")~', + ], + 'expectedResult' => [ + 'hello1' => 'hello1translated', + 'hello2' => 'hello2translated', + 'hello3' => 'hello3translated', + 'hello4' => 'hello4translated', + 'ko i18' => 'ko i18 translated', + 'underscore i18' => 'underscore i18 translated', + ], + 'contentsMap' => [ + 'content1$.mage.__("hello1")content1', + 'content2$.mage.__("hello2")content2', + 'content2$.mage.__("hello4")content4 <!-- ko i18n: "ko i18" --><!-- /ko -->', + 'content2$.mage.__("hello3")content3 <% _.i18n("underscore i18") %>', + ], + 'translateMap' => [ + [['hello1'], [], 'hello1translated'], + [['hello2'], [], 'hello2translated'], + [['hello3'], [], 'hello3translated'], + [['hello4'], [], 'hello4translated'], + [['ko i18'], [], 'ko i18 translated'], + [['underscore i18'], [], 'underscore i18 translated'], + ] + ], + ] + ]; + } } diff --git a/app/code/Magento/Translation/etc/adminhtml/system.xml b/app/code/Magento/Translation/etc/adminhtml/system.xml index ab854f8a4db52..b19ac5d1bfb09 100644 --- a/app/code/Magento/Translation/etc/adminhtml/system.xml +++ b/app/code/Magento/Translation/etc/adminhtml/system.xml @@ -9,7 +9,7 @@ <system> <section id="dev"> <group id="js"> - <field id="translate_strategy" translate="label comment" type="select" sortOrder="30" showInDefault="1" showInWebsite="0" showInStore="0" canRestore="1"> + <field id="translate_strategy" translate="label comment" type="select" sortOrder="30" showInDefault="1" canRestore="1"> <label>Translation Strategy</label> <source_model>Magento\Translation\Model\Js\Config\Source\Strategy</source_model> <comment>Please put your store into maintenance mode and redeploy static files after changing strategy</comment> diff --git a/app/code/Magento/Translation/etc/di.xml b/app/code/Magento/Translation/etc/di.xml index d17dac23933ee..783384fe64fa9 100644 --- a/app/code/Magento/Translation/etc/di.xml +++ b/app/code/Magento/Translation/etc/di.xml @@ -64,11 +64,11 @@ <type name="Magento\Translation\Model\Js\Config"> <arguments> <argument name="patterns" xsi:type="array"> - <item name="i18n_translation" xsi:type="string"><![CDATA[~i18n\:\s*(["'])(.*?)(?<!\\)\1~]]></item> + <item name="i18n_translation" xsi:type="string"><![CDATA[~(?:i18n\:|_\.i18n\()\s*(["'])(.*?)(?<!\\)\1~]]></item> <item name="translate_wrapping" xsi:type="string"><![CDATA[~translate\=("')([^\'].*?)\'\"~]]></item> <item name="mage_translation_widget" xsi:type="string"><![CDATA[~(?s)(?:\$|jQuery)\.mage\.__\(\s*(['"])(?<translate>.+?)(?<!\\)\1\s*(*SKIP)\)\s*(?s)~]]></item> <item name="mage_translation_static" xsi:type="string"><![CDATA[~(?s)\$t\(\s*(['"])(?<translate>.+?)(?<!\\)\1\s*(*SKIP)\)(?s)~]]></item> - <item name="translate_args" xsi:type="string"><![CDATA[~translate args\=("|'|"')([^\'].*?)('"|'|")~]]></item> + <item name="translate_args" xsi:type="string"><![CDATA[~translate args\=("|'|"'|\\"')([^\'].*?)('\\"|'"|'|")~]]></item> </argument> </arguments> </type> diff --git a/app/code/Magento/Translation/etc/module.xml b/app/code/Magento/Translation/etc/module.xml index 23ebfe1b0751d..5bf40ec19b6e2 100644 --- a/app/code/Magento/Translation/etc/module.xml +++ b/app/code/Magento/Translation/etc/module.xml @@ -6,6 +6,5 @@ */ --> <config xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="urn:magento:framework:Module/etc/module.xsd"> - <module name="Magento_Translation" > - </module> + <module name="Magento_Translation"/> </config> diff --git a/app/code/Magento/Translation/registration.php b/app/code/Magento/Translation/registration.php index 2e3adaf378181..7c6266026c5b0 100644 --- a/app/code/Magento/Translation/registration.php +++ b/app/code/Magento/Translation/registration.php @@ -4,6 +4,6 @@ * See COPYING.txt for license details. */ -use \Magento\Framework\Component\ComponentRegistrar; +use Magento\Framework\Component\ComponentRegistrar; ComponentRegistrar::register(ComponentRegistrar::MODULE, 'Magento_Translation', __DIR__); diff --git a/app/code/Magento/Ui/Component/AbstractComponent.php b/app/code/Magento/Ui/Component/AbstractComponent.php index 772bdceeb4957..9ce6d7d7fd334 100644 --- a/app/code/Magento/Ui/Component/AbstractComponent.php +++ b/app/code/Magento/Ui/Component/AbstractComponent.php @@ -5,15 +5,14 @@ */ namespace Magento\Ui\Component; +use Magento\Framework\Data\ValueSourceInterface; use Magento\Framework\DataObject; -use Magento\Framework\Exception\LocalizedException; -use Magento\Framework\View\Element\UiComponentFactory; -use Magento\Framework\View\Element\UiComponentInterface; use Magento\Framework\View\Element\UiComponent\ContextInterface; use Magento\Framework\View\Element\UiComponent\DataSourceInterface; use Magento\Framework\View\Element\UiComponent\ObserverInterface; -use Magento\Framework\Data\ValueSourceInterface; +use Magento\Framework\View\Element\UiComponentInterface; +//phpcs:disable Magento2.Classes.AbstractApi /** * Abstract class AbstractComponent * @@ -146,7 +145,7 @@ protected function prepareChildComponent(UiComponentInterface $component) */ public function toHtml() { - $this->render(); + return $this->render(); } /** @@ -173,12 +172,14 @@ public function addComponent($name, UiComponentInterface $component) } /** + * Return component + * * @param string $name * @return UiComponentInterface */ public function getComponent($name) { - return isset($this->components[$name]) ? $this->components[$name] : null; + return $this->components[$name] ?? null; } /** @@ -242,6 +243,7 @@ public function getJsConfig(UiComponentInterface $component) return $jsConfig; } + // phpcs:disable Generic.CodeAnalysis.UselessOverridingMethod /** * Component data setter * @@ -254,6 +256,7 @@ public function setData($key, $value = null) parent::setData($key, $value); } + // phpcs:disable Generic.CodeAnalysis.UselessOverridingMethod /** * Component data getter * @@ -278,7 +281,7 @@ public function prepareDataSource(array $dataSource) } /** - * {@inheritdoc} + * @inheritdoc */ public function getDataSourceData() { diff --git a/app/code/Magento/Ui/Component/MassAction/Filter.php b/app/code/Magento/Ui/Component/MassAction/Filter.php index c512c82d694bc..a06592bbd6758 100644 --- a/app/code/Magento/Ui/Component/MassAction/Filter.php +++ b/app/code/Magento/Ui/Component/MassAction/Filter.php @@ -3,6 +3,7 @@ * Copyright © Magento, Inc. All rights reserved. * See COPYING.txt for license details. */ +declare(strict_types=1); namespace Magento\Ui\Component\MassAction; @@ -107,7 +108,7 @@ public function getCollection(AbstractDb $collection) $filterIds = array_unique(array_merge($filterIds, $selected)); } $collection->addFieldToFilter( - $collection->getIdFieldName(), + $collection->getResource()->getIdFieldName(), ['in' => $filterIds] ); @@ -163,9 +164,9 @@ protected function applySelection(AbstractDb $collection) try { if (is_array($excluded) && !empty($excluded)) { - $collection->addFieldToFilter($collection->getIdFieldName(), ['nin' => $excluded]); + $collection->addFieldToFilter($collection->getResource()->getIdFieldName(), ['nin' => $excluded]); } elseif (is_array($selected) && !empty($selected)) { - $collection->addFieldToFilter($collection->getIdFieldName(), ['in' => $selected]); + $collection->addFieldToFilter($collection->getResource()->getIdFieldName(), ['in' => $selected]); } else { throw new LocalizedException(__('An item needs to be selected. Select and try again.')); } diff --git a/app/code/Magento/Ui/Config/Reader/Definition/Data.php b/app/code/Magento/Ui/Config/Reader/Definition/Data.php index 62bb07a6486f5..44b2c87e544a2 100644 --- a/app/code/Magento/Ui/Config/Reader/Definition/Data.php +++ b/app/code/Magento/Ui/Config/Reader/Definition/Data.php @@ -17,14 +17,8 @@ */ class Data implements \Magento\Framework\Config\DataInterface { - /** - * ID in the storage cache - */ const CACHE_ID = 'ui_component_configuration_definition_data'; - /** - * Search pattern - */ const SEARCH_PATTERN = '%s.xml'; /** @@ -123,7 +117,7 @@ public function merge(array $config) */ public function get($key, $default = null) { - return isset($this->data[$key]) ? $this->data[$key] : $default; + return $this->data[$key] ?? $default; } /** diff --git a/app/code/Magento/Ui/Controller/Adminhtml/Index/Render.php b/app/code/Magento/Ui/Controller/Adminhtml/Index/Render.php index b06c655939b1c..a1502b0650e2b 100644 --- a/app/code/Magento/Ui/Controller/Adminhtml/Index/Render.php +++ b/app/code/Magento/Ui/Controller/Adminhtml/Index/Render.php @@ -13,11 +13,15 @@ use Psr\Log\LoggerInterface; use Magento\Framework\Escaper; use Magento\Framework\Controller\Result\JsonFactory; +use Magento\Framework\App\ResponseInterface; +use Magento\Framework\Controller\Result\Json; +use Magento\Framework\Controller\ResultInterface; /** * Render a component. * * @SuppressWarnings(PHPMD.AllPurposeAction) + * @SuppressWarnings(PHPMD.CouplingBetweenObjects) */ class Render extends AbstractAction { @@ -68,7 +72,9 @@ public function __construct( } /** - * @inheritdoc + * Render a component + * + * @return ResponseInterface|Json|ResultInterface|void */ public function execute() { @@ -90,8 +96,8 @@ public function execute() /** @var \Magento\Framework\Controller\Result\Json $resultJson */ $resultJson = $this->resultJsonFactory->create(); $resultJson->setStatusHeader( - \Zend\Http\Response::STATUS_CODE_403, - \Zend\Http\AbstractMessage::VERSION_11, + \Laminas\Http\Response::STATUS_CODE_403, + \Laminas\Http\AbstractMessage::VERSION_11, 'Forbidden' ); return $resultJson->setData([ @@ -108,8 +114,8 @@ public function execute() /** @var \Magento\Framework\Controller\Result\Json $resultJson */ $resultJson = $this->resultJsonFactory->create(); $resultJson->setStatusHeader( - \Zend\Http\Response::STATUS_CODE_400, - \Zend\Http\AbstractMessage::VERSION_11, + \Laminas\Http\Response::STATUS_CODE_400, + \Laminas\Http\AbstractMessage::VERSION_11, 'Bad Request' ); @@ -123,8 +129,8 @@ public function execute() /** @var \Magento\Framework\Controller\Result\Json $resultJson */ $resultJson = $this->resultJsonFactory->create(); $resultJson->setStatusHeader( - \Zend\Http\Response::STATUS_CODE_400, - \Zend\Http\AbstractMessage::VERSION_11, + \Laminas\Http\Response::STATUS_CODE_400, + \Laminas\Http\AbstractMessage::VERSION_11, 'Bad Request' ); diff --git a/app/code/Magento/Ui/Controller/Index/Render.php b/app/code/Magento/Ui/Controller/Index/Render.php index faab203547064..42818686840aa 100644 --- a/app/code/Magento/Ui/Controller/Index/Render.php +++ b/app/code/Magento/Ui/Controller/Index/Render.php @@ -14,6 +14,9 @@ use Magento\Framework\Controller\Result\JsonFactory; use Psr\Log\LoggerInterface; use Magento\Framework\AuthorizationInterface; +use Magento\Framework\App\ResponseInterface; +use Magento\Framework\Controller\Result\Json; +use Magento\Framework\Controller\ResultInterface; /** * Is responsible for providing ui components information on store front. @@ -87,7 +90,9 @@ public function __construct( } /** - * @inheritdoc + * Provides ui component + * + * @return ResponseInterface|Json|ResultInterface|void */ public function execute() { @@ -109,8 +114,8 @@ public function execute() /** @var \Magento\Framework\Controller\Result\Json $resultJson */ $resultJson = $this->resultJsonFactory->create(); $resultJson->setStatusHeader( - \Zend\Http\Response::STATUS_CODE_403, - \Zend\Http\AbstractMessage::VERSION_11, + \Laminas\Http\Response::STATUS_CODE_403, + \Laminas\Http\AbstractMessage::VERSION_11, 'Forbidden' ); return $resultJson->setData( @@ -129,8 +134,8 @@ public function execute() /** @var \Magento\Framework\Controller\Result\Json $resultJson */ $resultJson = $this->resultJsonFactory->create(); $resultJson->setStatusHeader( - \Zend\Http\Response::STATUS_CODE_400, - \Zend\Http\AbstractMessage::VERSION_11, + \Laminas\Http\Response::STATUS_CODE_400, + \Laminas\Http\AbstractMessage::VERSION_11, 'Bad Request' ); @@ -144,8 +149,8 @@ public function execute() /** @var \Magento\Framework\Controller\Result\Json $resultJson */ $resultJson = $this->resultJsonFactory->create(); $resultJson->setStatusHeader( - \Zend\Http\Response::STATUS_CODE_400, - \Zend\Http\AbstractMessage::VERSION_11, + \Laminas\Http\Response::STATUS_CODE_400, + \Laminas\Http\AbstractMessage::VERSION_11, 'Bad Request' ); diff --git a/app/code/Magento/Ui/DataProvider/AbstractDataProvider.php b/app/code/Magento/Ui/DataProvider/AbstractDataProvider.php index 578ebe4d441d9..abbc79859a038 100644 --- a/app/code/Magento/Ui/DataProvider/AbstractDataProvider.php +++ b/app/code/Magento/Ui/DataProvider/AbstractDataProvider.php @@ -8,7 +8,10 @@ use Magento\Framework\Model\ResourceModel\Db\Collection\AbstractCollection; use Magento\Framework\View\Element\UiComponent\DataProvider\DataProviderInterface; + // phpcs:disable Magento2.Classes.AbstractApi /** + * @inheritdoc + * * @api * @since 100.0.2 */ @@ -74,6 +77,8 @@ public function __construct( } /** + * Return collection + * * @return AbstractCollection */ public function getCollection() @@ -112,6 +117,8 @@ public function getRequestFieldName() } /** + * Return Meta + * * @return array */ public function getMeta() @@ -127,28 +134,30 @@ public function getMeta() */ public function getFieldSetMetaInfo($fieldSetName) { - return isset($this->meta[$fieldSetName]) ? $this->meta[$fieldSetName] : []; + return $this->meta[$fieldSetName] ?? []; } /** + * Return fields meta info + * * @param string $fieldSetName * @return array */ public function getFieldsMetaInfo($fieldSetName) { - return isset($this->meta[$fieldSetName]['children']) ? $this->meta[$fieldSetName]['children'] : []; + return $this->meta[$fieldSetName]['children'] ?? []; } /** + * Return field meta info + * * @param string $fieldSetName * @param string $fieldName * @return array */ public function getFieldMetaInfo($fieldSetName, $fieldName) { - return isset($this->meta[$fieldSetName]['children'][$fieldName]) - ? $this->meta[$fieldSetName]['children'][$fieldName] - : []; + return $this->meta[$fieldSetName]['children'][$fieldName] ?? []; } /** @@ -197,7 +206,7 @@ public function addField($field, $alias = null) } /** - * self::setOrder() alias + * Alias for self::setOrder() * * @param string $field * @param string $direction @@ -270,7 +279,7 @@ public function count() */ public function getConfigData() { - return isset($this->data['config']) ? $this->data['config'] : []; + return $this->data['config'] ?? []; } /** diff --git a/app/code/Magento/Ui/Model/Export/MetadataProvider.php b/app/code/Magento/Ui/Model/Export/MetadataProvider.php index 3f6685e37fcc4..59f3af18de451 100755 --- a/app/code/Magento/Ui/Model/Export/MetadataProvider.php +++ b/app/code/Magento/Ui/Model/Export/MetadataProvider.php @@ -3,19 +3,28 @@ * Copyright © Magento, Inc. All rights reserved. * See COPYING.txt for license details. */ +declare(strict_types=1); + namespace Magento\Ui\Model\Export; +use DateTime; +use DateTimeZone; +use Exception; use Magento\Framework\Api\Search\DocumentInterface; +use Magento\Framework\Data\OptionSourceInterface; +use Magento\Framework\Exception\LocalizedException; +use Magento\Framework\Locale\ResolverInterface; +use Magento\Framework\Stdlib\DateTime\TimezoneInterface; use Magento\Framework\View\Element\UiComponentInterface; use Magento\Ui\Component\Filters; use Magento\Ui\Component\Filters\Type\Select; use Magento\Ui\Component\Listing\Columns; +use Magento\Ui\Component\Listing\Columns\Column; use Magento\Ui\Component\MassAction\Filter; -use Magento\Framework\Locale\ResolverInterface; -use Magento\Framework\Stdlib\DateTime\TimezoneInterface; /** - * Metadata Provider + * Metadata Provider for grid listing export. + * * @SuppressWarnings(PHPMD.CouplingBetweenObjects) */ class MetadataProvider @@ -75,26 +84,29 @@ public function __construct( * Returns Columns component * * @param UiComponentInterface $component + * * @return UiComponentInterface - * @throws \Exception + * @throws Exception */ - protected function getColumnsComponent(UiComponentInterface $component) + protected function getColumnsComponent(UiComponentInterface $component): UiComponentInterface { foreach ($component->getChildComponents() as $childComponent) { if ($childComponent instanceof Columns) { return $childComponent; } } - throw new \Exception('No columns found'); // @codingStandardsIgnoreLine + throw new Exception('No columns found'); // @codingStandardsIgnoreLine } /** * Returns columns list * * @param UiComponentInterface $component + * * @return UiComponentInterface[] + * @throws Exception */ - protected function getColumns(UiComponentInterface $component) + protected function getColumns(UiComponentInterface $component): array { if (!isset($this->columns[$component->getName()])) { $columns = $this->getColumnsComponent($component); @@ -104,6 +116,7 @@ protected function getColumns(UiComponentInterface $component) } } } + return $this->columns[$component->getName()]; } @@ -111,9 +124,11 @@ protected function getColumns(UiComponentInterface $component) * Retrieve Headers row array for Export * * @param UiComponentInterface $component + * * @return string[] + * @throws Exception */ - public function getHeaders(UiComponentInterface $component) + public function getHeaders(UiComponentInterface $component): array { $row = []; foreach ($this->getColumns($component) as $column) { @@ -127,14 +142,17 @@ public function getHeaders(UiComponentInterface $component) * Returns DB fields list * * @param UiComponentInterface $component - * @return array + * + * @return string[] + * @throws Exception */ - public function getFields(UiComponentInterface $component) + public function getFields(UiComponentInterface $component): array { $row = []; foreach ($this->getColumns($component) as $column) { $row[] = $column->getName(); } + return $row; } @@ -144,9 +162,10 @@ public function getFields(UiComponentInterface $component) * @param DocumentInterface $document * @param array $fields * @param array $options - * @return array + * + * @return string[] */ - public function getRowData(DocumentInterface $document, $fields, $options) + public function getRowData(DocumentInterface $document, $fields, $options): array { $row = []; foreach ($fields as $column) { @@ -155,12 +174,13 @@ public function getRowData(DocumentInterface $document, $fields, $options) if (isset($options[$column][$key])) { $row[] = $options[$column][$key]; } else { - $row[] = ''; + $row[] = $key; } } else { $row[] = $document->getCustomAttribute($column)->getValue(); } } + return $row; } @@ -170,9 +190,10 @@ public function getRowData(DocumentInterface $document, $fields, $options) * @param array $list * @param string $label * @param array $output + * * @return void */ - protected function getComplexLabel($list, $label, &$output) + protected function getComplexLabel($list, $label, &$output): void { foreach ($list as $item) { if (!is_array($item['value'])) { @@ -184,34 +205,75 @@ protected function getComplexLabel($list, $label, &$output) } /** - * Returns array of Select options + * Prepare array of options. + * + * @param array $options * - * @param Select $filter * @return array */ - protected function getFilterOptions(Select $filter) + protected function getOptionsArray(array $options): array { - $options = []; - foreach ($filter->getData('config/options') as $option) { + $preparedOptions = []; + foreach ($options as $option) { if (!is_array($option['value'])) { - $options[$option['value']] = $option['label']; + $preparedOptions[$option['value']] = $option['label']; } else { $this->getComplexLabel( $option['value'], $option['label'], - $options + $preparedOptions ); } } - return $options; + + return $preparedOptions; } /** * Returns Filters with options * * @return array + * @throws LocalizedException */ - public function getOptions() + public function getOptions(): array + { + return array_merge( + $this->getColumnOptions(), + $this->getFilterOptions() + ); + } + + /** + * Get options from columns. + * + * @return array + * @throws LocalizedException + * @throws Exception + */ + protected function getColumnOptions(): array + { + $options = []; + $component = $this->filter->getComponent(); + /** @var Column $columnComponent */ + foreach ($this->getColumns($component) as $columnComponent) { + if ($columnComponent->hasData('options')) { + $optionSource = $columnComponent->getData('options'); + $optionsArray = $optionSource instanceof OptionSourceInterface ? + $optionSource->toOptionArray() : $optionSource; + $options[$columnComponent->getName()] = $this->getOptionsArray($optionsArray ?: []); + } + } + + return $options; + } + + /** + * Get options from column filters. + * + * @return array + * @throws LocalizedException + */ + protected function getFilterOptions(): array { $options = []; $component = $this->filter->getComponent(); @@ -221,22 +283,25 @@ public function getOptions() if ($child instanceof Filters) { foreach ($child->getChildComponents() as $filter) { if ($filter instanceof Select) { - $options[$filter->getName()] = $this->getFilterOptions($filter); + $options[$filter->getName()] = $this->getOptionsArray($filter->getData('config/options')); } } } } + return $options; } /** * Convert document date(UTC) fields to default scope specified * - * @param \Magento\Framework\Api\Search\DocumentInterface $document + * @param DocumentInterface $document * @param string $componentName + * * @return void + * @throws Exception */ - public function convertDate($document, $componentName) + public function convertDate($document, $componentName): void { if (!isset($this->data[$componentName])) { return; @@ -247,7 +312,7 @@ public function convertDate($document, $componentName) continue; } $convertedDate = $this->localeDate->date( - new \DateTime($fieldValue, new \DateTimeZone('UTC')), + new DateTime($fieldValue, new DateTimeZone('UTC')), $this->locale, true ); diff --git a/app/code/Magento/Ui/Model/Manager.php b/app/code/Magento/Ui/Model/Manager.php index e3c56418c8425..1bacdc80a5c5e 100644 --- a/app/code/Magento/Ui/Model/Manager.php +++ b/app/code/Magento/Ui/Model/Manager.php @@ -7,9 +7,11 @@ namespace Magento\Ui\Model; use ArrayObject; +use Magento\Framework\App\ObjectManager; +use Magento\Framework\Config\CacheInterface; use Magento\Framework\Data\Argument\InterpreterInterface; use Magento\Framework\Exception\LocalizedException; -use Magento\Framework\Config\CacheInterface; +use Magento\Framework\Serialize\SerializerInterface; use Magento\Framework\View\Element\UiComponent\ArrayObjectFactory; use Magento\Framework\View\Element\UiComponent\Config\Converter; use Magento\Framework\View\Element\UiComponent\Config\DomMergerInterface; @@ -18,11 +20,10 @@ use Magento\Framework\View\Element\UiComponent\Config\Provider\Component\Definition as ComponentDefinition; use Magento\Framework\View\Element\UiComponent\Config\ReaderFactory; use Magento\Framework\View\Element\UiComponent\Config\UiReaderInterface; -use Magento\Framework\Serialize\SerializerInterface; -use Magento\Framework\App\ObjectManager; /** - * Class Manager + * @inheritdoc + * * @deprecated 100.2.0 * @SuppressWarnings(PHPMD.CouplingBetweenObjects) */ @@ -313,6 +314,7 @@ protected function createDataForComponent($name, array $componentsPool) // Create inner components foreach ($component as $subComponentName => $subComponent) { if (is_array($subComponent)) { + // phpcs:ignore Magento2.Performance.ForeachArrayMerge $resultConfiguration[ManagerInterface::CHILDREN_KEY] = array_merge( $resultConfiguration[ManagerInterface::CHILDREN_KEY], $this->createDataForComponent($subComponentName, $subComponent) @@ -386,8 +388,7 @@ protected function mergeAttributes(array $componentData, array $rootComponentDat */ protected function createName(array $componentData, $key, $componentName) { - return isset($componentData[Converter::DATA_ATTRIBUTES_KEY][Converter::NAME_ATTRIBUTE_KEY]) - ? $componentData[Converter::DATA_ATTRIBUTES_KEY][Converter::NAME_ATTRIBUTE_KEY] - : sprintf(ManagerInterface::ANONYMOUS_TEMPLATE, $componentName, $key); + return $componentData[Converter::DATA_ATTRIBUTES_KEY][Converter::NAME_ATTRIBUTE_KEY] + ?? sprintf(ManagerInterface::ANONYMOUS_TEMPLATE, $componentName, $key); } } diff --git a/app/code/Magento/Ui/TemplateEngine/Xhtml/Result.php b/app/code/Magento/Ui/TemplateEngine/Xhtml/Result.php index d16b1eaad7f37..d15f94bd6f2dd 100644 --- a/app/code/Magento/Ui/TemplateEngine/Xhtml/Result.php +++ b/app/code/Magento/Ui/TemplateEngine/Xhtml/Result.php @@ -5,17 +5,17 @@ */ namespace Magento\Ui\TemplateEngine\Xhtml; -use Magento\Framework\App\ObjectManager; +use Magento\Framework\App\State; use Magento\Framework\Serialize\Serializer\JsonHexTag; -use Magento\Framework\View\Layout\Generator\Structure; use Magento\Framework\View\Element\UiComponentInterface; -use Magento\Framework\View\TemplateEngine\Xhtml\Template; -use Magento\Framework\View\TemplateEngine\Xhtml\ResultInterface; +use Magento\Framework\View\Layout\Generator\Structure; use Magento\Framework\View\TemplateEngine\Xhtml\CompilerInterface; +use Magento\Framework\View\TemplateEngine\Xhtml\ResultInterface; +use Magento\Framework\View\TemplateEngine\Xhtml\Template; use Psr\Log\LoggerInterface; /** - * Class Result + * Convert DOMElement to string representation */ class Result implements ResultInterface { @@ -49,6 +49,11 @@ class Result implements ResultInterface */ private $jsonSerializer; + /** + * @var State + */ + private $state; + /** * @param Template $template * @param CompilerInterface $compiler @@ -56,6 +61,7 @@ class Result implements ResultInterface * @param Structure $structure * @param LoggerInterface $logger * @param JsonHexTag $jsonSerializer + * @param State $state */ public function __construct( Template $template, @@ -63,14 +69,16 @@ public function __construct( UiComponentInterface $component, Structure $structure, LoggerInterface $logger, - JsonHexTag $jsonSerializer = null + JsonHexTag $jsonSerializer, + State $state ) { $this->template = $template; $this->compiler = $compiler; $this->component = $component; $this->structure = $structure; $this->logger = $logger; - $this->jsonSerializer = $jsonSerializer ?? ObjectManager::getInstance()->get(JsonHexTag::class); + $this->jsonSerializer = $jsonSerializer; + $this->state = $state; } /** @@ -116,8 +124,11 @@ public function __toString() $this->appendLayoutConfiguration(); $result = $this->compiler->postprocessing($this->template->__toString()); } catch (\Throwable $e) { - $this->logger->critical($e->getMessage()); + $this->logger->critical($e); $result = $e->getMessage(); + if ($this->state->getMode() === State::MODE_DEVELOPER) { + $result .= "<pre><code>Exception in {$e->getFile()}:{$e->getLine()}</code></pre>"; + } } return $result; } diff --git a/app/code/Magento/Ui/Test/Mftf/ActionGroup/AdminAssertGridNotEmptyActionGroup.xml b/app/code/Magento/Ui/Test/Mftf/ActionGroup/AdminAssertGridNotEmptyActionGroup.xml new file mode 100644 index 0000000000000..7c951db99ef5e --- /dev/null +++ b/app/code/Magento/Ui/Test/Mftf/ActionGroup/AdminAssertGridNotEmptyActionGroup.xml @@ -0,0 +1,14 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + /** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +--> +<actionGroups xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/actionGroupSchema.xsd"> + <actionGroup name="AdminAssertGridNotEmptyActionGroup"> + <waitForLoadingMaskToDisappear stepKey="waitForGridLoaded"/> + <dontSee selector="{{AdminDataGridTableSection.dataGridEmpty}}" userInput="We couldn't find any records." stepKey="expectNotEmptyGrid"/> + </actionGroup> +</actionGroups> diff --git a/app/code/Magento/Ui/Test/Mftf/ActionGroup/AdminFormSaveAndCloseActionGroup.xml b/app/code/Magento/Ui/Test/Mftf/ActionGroup/AdminFormSaveAndCloseActionGroup.xml new file mode 100644 index 0000000000000..a1b8c8c7609e1 --- /dev/null +++ b/app/code/Magento/Ui/Test/Mftf/ActionGroup/AdminFormSaveAndCloseActionGroup.xml @@ -0,0 +1,20 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + /** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +--> + +<actionGroups xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/actionGroupSchema.xsd"> + <actionGroup name="AdminFormSaveAndCloseActionGroup"> + <annotations> + <description>Clicks on 'Save and Close'. Validates that the Success Message is present.</description> + </annotations> + + <click selector="{{AdminProductFormActionSection.saveArrow}}" stepKey="openSaveDropDown"/> + <click selector="{{AdminProductFormActionSection.saveAndClose}}" stepKey="clickOnSaveAndClose"/> + <seeElement selector="{{AdminProductMessagesSection.successMessage}}" stepKey="assertSaveMessageSuccess"/> + </actionGroup> +</actionGroups> diff --git a/app/code/Magento/Ui/Test/Mftf/ActionGroup/AdminFormSaveAndDuplicateActionGroup.xml b/app/code/Magento/Ui/Test/Mftf/ActionGroup/AdminFormSaveAndDuplicateActionGroup.xml new file mode 100644 index 0000000000000..322fbffd90427 --- /dev/null +++ b/app/code/Magento/Ui/Test/Mftf/ActionGroup/AdminFormSaveAndDuplicateActionGroup.xml @@ -0,0 +1,21 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + /** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +--> + +<actionGroups xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/actionGroupSchema.xsd"> + <actionGroup name="AdminFormSaveAndDuplicateActionGroup"> + <annotations> + <description>Clicks on 'Save and Duplicate'. Validates that the Success Message is present and correct.</description> + </annotations> + + <click selector="{{AdminProductFormActionSection.saveArrow}}" stepKey="openSaveDropDown"/> + <click selector="{{AdminProductFormActionSection.saveAndDuplicate}}" stepKey="clickOnSaveAndDuplicate"/> + <see selector="{{AdminProductMessagesSection.successMessage}}" stepKey="assertSaveSuccess" userInput="You saved the product."/> + <see selector="{{AdminProductMessagesSection.successMessage}}" stepKey="assertDuplicateSuccess" userInput="You duplicated the product."/> + </actionGroup> +</actionGroups> diff --git a/app/code/Magento/Ui/Test/Mftf/ActionGroup/AdminGridFilterSearchResultsBySelectActionGroup.xml b/app/code/Magento/Ui/Test/Mftf/ActionGroup/AdminGridFilterSearchResultsBySelectActionGroup.xml new file mode 100644 index 0000000000000..5ae764ce100aa --- /dev/null +++ b/app/code/Magento/Ui/Test/Mftf/ActionGroup/AdminGridFilterSearchResultsBySelectActionGroup.xml @@ -0,0 +1,26 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + /** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +--> + +<actionGroups xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/actionGroupSchema.xsd"> + <actionGroup name="AdminGridFilterSearchResultsBySelectActionGroup"> + <annotations> + <description>Filters an Admin Grid page using the provided Filter Selector and Search Value.</description> + </annotations> + <arguments> + <argument name="attributeSelector" type="string" defaultValue="store_id"/> + <argument name="attributeValue" type="string" defaultValue="{{_defaultStore.name}}"/> + </arguments> + + <conditionalClick selector="{{AdminGridFilterControls.clearAll}}" dependentSelector="{{AdminGridFilterControls.clearAll}}" visible="true" stepKey="clearTheFiltersIfPresent"/> + <waitForPageLoad time="30" stepKey="waitForFilterApplied"/> + <click selector="{{AdminGridFilterControls.filters}}" stepKey="clickOnFilters"/> + <selectOption selector="{{AdminDataGridFilterSection.filterSelectFieldByName(attributeSelector)}}" userInput="{{attributeValue}}" stepKey="setAttributeValue"/> + <click selector="{{AdminGridFilterControls.applyFilters}}" stepKey="clickOnApplyFilters"/> + </actionGroup> +</actionGroups> diff --git a/app/code/Magento/Ui/Test/Mftf/ActionGroup/AdminSaveAndCloseActionGroup.xml b/app/code/Magento/Ui/Test/Mftf/ActionGroup/AdminSaveAndCloseActionGroup.xml deleted file mode 100644 index d4e1c7e3ba873..0000000000000 --- a/app/code/Magento/Ui/Test/Mftf/ActionGroup/AdminSaveAndCloseActionGroup.xml +++ /dev/null @@ -1,31 +0,0 @@ -<?xml version="1.0" encoding="UTF-8"?> -<!-- - /** - * Copyright © Magento, Inc. All rights reserved. - * See COPYING.txt for license details. - */ ---> - -<actionGroups xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" - xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/actionGroupSchema.xsd"> - <actionGroup name="AdminFormSaveAndClose"> - <annotations> - <description>Clicks on 'Save and Close'. Validates that the Success Message is present.</description> - </annotations> - - <click selector="{{AdminProductFormActionSection.saveArrow}}" stepKey="openSaveDropDown"/> - <click selector="{{AdminProductFormActionSection.saveAndClose}}" stepKey="clickOnSaveAndClose"/> - <seeElement selector="{{AdminProductMessagesSection.successMessage}}" stepKey="assertSaveMessageSuccess"/> - </actionGroup> - - <actionGroup name="AdminFormSaveAndDuplicate"> - <annotations> - <description>Clicks on 'Save and Duplicate'. Validates that the Success Message is present and correct.</description> - </annotations> - - <click selector="{{AdminProductFormActionSection.saveArrow}}" stepKey="openSaveDropDown"/> - <click selector="{{AdminProductFormActionSection.saveAndDuplicate}}" stepKey="clickOnSaveAndDuplicate"/> - <see selector="{{AdminProductMessagesSection.successMessage}}" stepKey="assertSaveSuccess" userInput="You saved the product."/> - <see selector="{{AdminProductMessagesSection.successMessage}}" stepKey="assertDuplicateSuccess" userInput="You duplicated the product."/> - </actionGroup> -</actionGroups> diff --git a/app/code/Magento/Ui/Test/Mftf/ActionGroup/AdminUiGridSelectFilterOptionActionGroup.xml b/app/code/Magento/Ui/Test/Mftf/ActionGroup/AdminUiGridSelectFilterOptionActionGroup.xml new file mode 100644 index 0000000000000..1c5a27ab326c3 --- /dev/null +++ b/app/code/Magento/Ui/Test/Mftf/ActionGroup/AdminUiGridSelectFilterOptionActionGroup.xml @@ -0,0 +1,18 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + /** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +--> + +<actionGroups xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/actionGroupSchema.xsd"> + <actionGroup name="AdminUiGridSelectFilterOptionActionGroup"> + <arguments> + <argument name="filterName" type="string"/> + <argument name="filterValue" type="string"/> + </arguments> + <selectOption selector="{{AdminDataGridFilterSection.selectByNameAttrInGrid(filterName)}}" userInput="{{filterValue}}" stepKey="selectOption"/> + </actionGroup> +</actionGroups> diff --git a/app/code/Magento/Ui/Test/Mftf/Section/AdminDataGridFilterSection.xml b/app/code/Magento/Ui/Test/Mftf/Section/AdminDataGridFilterSection.xml index ad8182c62ae68..ec4554be21510 100644 --- a/app/code/Magento/Ui/Test/Mftf/Section/AdminDataGridFilterSection.xml +++ b/app/code/Magento/Ui/Test/Mftf/Section/AdminDataGridFilterSection.xml @@ -13,6 +13,8 @@ <element name="filterExpand" type="button" selector="//div[@class='admin__data-grid-header'][(not(ancestor::*[@class='sticky-header']) and not(contains(@style,'visibility: hidden'))) or (ancestor::*[@class='sticky-header' and not(contains(@style,'display: none'))])]//button[@data-action='grid-filter-expand']" /> <element name="inputFieldByNameAttr" type="input" selector="//*[@data-part='filter-form']//input[@name='{{inputNameAttr}}']" parameterized="true" /> <element name="inputFieldByNameAttrInGrid" type="input" selector="//*[@data-role='filter-form']//input[@name='{{inputNameAttr}}']" parameterized="true"/> + <element name="selectByNameAttrInGrid" type="input" selector="//*[@data-role='filter-form']//select[@name='{{inputNameAttr}}']" parameterized="true"/> + <element name="filterSelectFieldByName" type="select" selector="//*[@data-part='filter-form']//select[@name='{{fieldName}}']" parameterized="true"/> <element name="apply" type="button" selector="//*[@data-part='filter-form']//button[@data-action='grid-filter-apply']" /> <element name="clear" type="button" selector=".admin__data-grid-header [data-action='grid-filter-reset']" /> </section> diff --git a/app/code/Magento/Ui/Test/Mftf/Section/AdminGridControlsSection.xml b/app/code/Magento/Ui/Test/Mftf/Section/AdminGridControlsSection.xml deleted file mode 100644 index 978a09db82b16..0000000000000 --- a/app/code/Magento/Ui/Test/Mftf/Section/AdminGridControlsSection.xml +++ /dev/null @@ -1,81 +0,0 @@ -<?xml version="1.0" encoding="UTF-8"?> -<!-- - /** - * Copyright © Magento, Inc. All rights reserved. - * See COPYING.txt for license details. - */ ---> - -<sections xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" - xsi:noNamespaceSchemaLocation="urn:magento:mftf:Page/etc/SectionObject.xsd"> - <!-- TODO: Search, Notifications, Admin Menu --> - <section name="AdminGridMainControls"> - <element name="add" type="button" selector="#add" timeout="30"/> - <element name="back" type="button" selector="#back" timeout="30"/> - <element name="reset" type="button" selector="#reset" timeout="30"/> - <element name="save" type="button" selector="#save-button" timeout="30"/> - <element name="saveAndContinue" type="button" selector="#save-button" timeout="30"/> - <element name="saveArrow" type="button" selector="button[data-ui-id='save-button-dropdown']" timeout="5"/> - <element name="saveAndClose" type="button" selector="span[title='Save & Close']" timeout="30"/> - <element name="saveAndNew" type="button" selector="span[title='Save & New']" timeout="30"/> - </section> - <section name="AdminGridSearchBox"> - <element name="searchByKeyword" type="input" selector=".admin__data-grid-header[data-bind='afterRender: \$data.setToolbarNode'] input[placeholder='Search by keyword']"/> - <element name="search" type="button" selector=".admin__data-grid-header[data-bind='afterRender: \$data.setToolbarNode'] input[placeholder='Search by keyword'] + .action-submit" timeout="30"/> - </section> - <section name="AdminGridFilterControls"> - <element name="filters" type="button" selector=".admin__data-grid-header[data-bind='afterRender: \$data.setToolbarNode'] button[data-action='grid-filter-expand']" timeout="5"/> - <element name="applyFilters" type="button" selector="button[data-action='grid-filter-apply']" timeout="30"/> - <element name="cancel" type="button" selector="button[data-action='grid-filter-cancel']" timeout="30"/> - <element name="clearAll" type="button" selector="(//*[contains(@class, 'admin__data-grid-header')][contains(@data-bind, 'afterRender: \$data.setToolbarNode')]//button[contains(@data-action, 'reset')])[1]" timeout="5"/> - </section> - <section name="AdminGridDefaultViewControls"> - <element name="defaultView" type="button" selector=".admin__data-grid-header[data-bind='afterRender: \$data.setToolbarNode'] .admin__data-grid-action-bookmarks" timeout="5"/> - <element name="viewByName" type="button" selector="//a[@class='action-dropdown-menu-link'][contains(text(), '{{var1}}')]" parameterized="true" timeout="5"/> - <element name="saveViewAs" type="button" selector=".admin__data-grid-header[data-bind='afterRender: \$data.setToolbarNode'] .action-dropdown-menu-item-last a" timeout="5"/> - <element name="viewName" type="input" selector=".admin__data-grid-header[data-bind='afterRender: \$data.setToolbarNode'] ._edit input"/> - <element name="save" type="button" selector=".admin__data-grid-header[data-bind='afterRender: \$data.setToolbarNode'] ._edit button" timeout="30"/> - </section> - <section name="AdminGridColumnsControls"> - <element name="columns" type="button" selector=".admin__data-grid-header[data-bind='afterRender: \$data.setToolbarNode'] .admin__data-grid-action-columns" timeout="5"/> - - <element name="columnName" type="button" selector="//label[contains(text(), '{{var1}}')]" parameterized="true" timeout="5"/> - - <element name="reset" type="button" selector="//div[@class='admin__action-dropdown-menu-footer']/div/button[contains(text(), 'Reset')]" timeout="5"/> - <element name="cancel" type="button" selector="//div[@class='admin__action-dropdown-menu-footer']/div/button[contains(text(), 'Cancel')]" timeout="5"/> - </section> - <section name="AdminGridActionsMenu"> - <element name="dropDown" type="select" selector=".admin__data-grid-header[data-bind='afterRender: \$data.setToolbarNode'] .action-select" timeout="30"/> - <element name="delete" type="select" selector=".admin__data-grid-header[data-bind='afterRender: \$data.setToolbarNode'] .action-menu-items li" timeout="5"/> - </section> - <section name="AdminGridRowsPerPage"> - <element name="count" type="select" selector=".admin__data-grid-header[data-bind='afterRender: \$data.setToolbarNode'] .selectmenu-value input" timeout="30"/> - </section> - <!-- TODO: Pagination controls --> - <section name="AdminGridHeaders"> - <element name="title" type="text" selector=".page-title-wrapper h1"/> - <element name="headerByName" type="text" selector="//div[@data-role='grid-wrapper']//span[@class='data-grid-cell-content' and contains(text(), '{{var1}}')]/parent::*" parameterized="true"/> - <element name="columnsNames" type="text" selector="[data-role='grid-wrapper'] .data-grid-th > span"/> - </section> - <section name="AdminGridRow"> - <element name="rowOne" type="text" selector="tr[data-repeat-index='0']"/> - <element name="rowByIndex" type="text" selector="tr[data-repeat-index='{{var1}}']" parameterized="true"/> - - <element name="editByValue" type="button" selector="//a[ancestor::tr[contains(., '{{var1}}')]]" parameterized="true"/> - - <element name="checkboxByValue" type="checkbox" selector="//input[ancestor::tr[contains(., '{{var1}}')]]" parameterized="true"/> - <element name="checkboxByIndex" type="checkbox" selector=".data-row[data-repeat-index='{{var1}}'] .admin__control-checkbox" parameterized="true"/> - </section> - <section name="AdminGridSelectRows"> - <element name="multicheckDropdown" type="button" selector="div[data-role='grid-wrapper'] th.data-grid-multicheck-cell button.action-multicheck-toggle"/> - <element name="multicheckOption" type="button" selector="//div[@data-role='grid-wrapper']//th[contains(@class, data-grid-multicheck-cell)]//li//span[text() = '{{label}}']" parameterized="true"/> - <element name="bulkActionDropdown" type="button" selector="div.admin__data-grid-header-row.row div.action-select-wrap button.action-select"/> - <element name="bulkActionOption" type="button" selector="//div[contains(@class,'admin__data-grid-header-row') and contains(@class, 'row')]//div[contains(@class, 'action-select-wrap')]//ul/li/span[text() = '{{label}}']" parameterized="true"/> - </section> - <section name="AdminGridConfirmActionSection"> - <element name="title" type="text" selector=".modal-popup.confirm h1.modal-title"/> - <element name="message" type="text" selector=".modal-popup.confirm div.modal-content"/> - <element name="cancel" type="button" selector=".modal-popup.confirm button.action-dismiss"/> - <element name="ok" type="button" selector=".modal-popup.confirm button.action-accept" timeout="60"/> - </section> -</sections> diff --git a/app/code/Magento/Ui/Test/Mftf/Section/AdminGridControlsSection/AdminGridActionsMenuSection.xml b/app/code/Magento/Ui/Test/Mftf/Section/AdminGridControlsSection/AdminGridActionsMenuSection.xml new file mode 100644 index 0000000000000..fc9e83f9693c0 --- /dev/null +++ b/app/code/Magento/Ui/Test/Mftf/Section/AdminGridControlsSection/AdminGridActionsMenuSection.xml @@ -0,0 +1,14 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + /** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +--> +<sections xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Page/etc/SectionObject.xsd"> + <section name="AdminGridActionsMenu"> + <element name="dropDown" type="select" selector=".admin__data-grid-header[data-bind='afterRender: \$data.setToolbarNode'] .action-select" timeout="30"/> + <element name="delete" type="select" selector=".admin__data-grid-header[data-bind='afterRender: \$data.setToolbarNode'] .action-menu-items li" timeout="5"/> + </section> +</sections> diff --git a/app/code/Magento/Ui/Test/Mftf/Section/AdminGridControlsSection/AdminGridColumnsControlsSection.xml b/app/code/Magento/Ui/Test/Mftf/Section/AdminGridControlsSection/AdminGridColumnsControlsSection.xml new file mode 100644 index 0000000000000..30edbe4aade18 --- /dev/null +++ b/app/code/Magento/Ui/Test/Mftf/Section/AdminGridControlsSection/AdminGridColumnsControlsSection.xml @@ -0,0 +1,18 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + /** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +--> +<sections xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Page/etc/SectionObject.xsd"> + <section name="AdminGridColumnsControls"> + <element name="columns" type="button" selector=".admin__data-grid-header[data-bind='afterRender: \$data.setToolbarNode'] .admin__data-grid-action-columns" timeout="5"/> + + <element name="columnName" type="button" selector="//label[contains(text(), '{{var1}}')]" parameterized="true" timeout="5"/> + + <element name="reset" type="button" selector="//div[@class='admin__action-dropdown-menu-footer']/div/button[contains(text(), 'Reset')]" timeout="5"/> + <element name="cancel" type="button" selector="//div[@class='admin__action-dropdown-menu-footer']/div/button[contains(text(), 'Cancel')]" timeout="5"/> + </section> +</sections> diff --git a/app/code/Magento/Ui/Test/Mftf/Section/AdminGridControlsSection/AdminGridConfirmActionSection.xml b/app/code/Magento/Ui/Test/Mftf/Section/AdminGridControlsSection/AdminGridConfirmActionSection.xml new file mode 100644 index 0000000000000..d2d17b1afd055 --- /dev/null +++ b/app/code/Magento/Ui/Test/Mftf/Section/AdminGridControlsSection/AdminGridConfirmActionSection.xml @@ -0,0 +1,16 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + /** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +--> +<sections xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Page/etc/SectionObject.xsd"> + <section name="AdminGridConfirmActionSection"> + <element name="title" type="text" selector=".modal-popup.confirm h1.modal-title"/> + <element name="message" type="text" selector=".modal-popup.confirm div.modal-content"/> + <element name="cancel" type="button" selector=".modal-popup.confirm button.action-dismiss"/> + <element name="ok" type="button" selector=".modal-popup.confirm button.action-accept" timeout="60"/> + </section> +</sections> diff --git a/app/code/Magento/Ui/Test/Mftf/Section/AdminGridControlsSection/AdminGridDefaultViewControlsSection.xml b/app/code/Magento/Ui/Test/Mftf/Section/AdminGridControlsSection/AdminGridDefaultViewControlsSection.xml new file mode 100644 index 0000000000000..b3900f5a641e3 --- /dev/null +++ b/app/code/Magento/Ui/Test/Mftf/Section/AdminGridControlsSection/AdminGridDefaultViewControlsSection.xml @@ -0,0 +1,17 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + /** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +--> +<sections xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Page/etc/SectionObject.xsd"> + <section name="AdminGridDefaultViewControls"> + <element name="defaultView" type="button" selector=".admin__data-grid-header[data-bind='afterRender: \$data.setToolbarNode'] .admin__data-grid-action-bookmarks" timeout="5"/> + <element name="viewByName" type="button" selector="//a[@class='action-dropdown-menu-link'][contains(text(), '{{var1}}')]" parameterized="true" timeout="5"/> + <element name="saveViewAs" type="button" selector=".admin__data-grid-header[data-bind='afterRender: \$data.setToolbarNode'] .action-dropdown-menu-item-last a" timeout="5"/> + <element name="viewName" type="input" selector=".admin__data-grid-header[data-bind='afterRender: \$data.setToolbarNode'] ._edit input"/> + <element name="save" type="button" selector=".admin__data-grid-header[data-bind='afterRender: \$data.setToolbarNode'] ._edit button" timeout="30"/> + </section> +</sections> diff --git a/app/code/Magento/Ui/Test/Mftf/Section/AdminGridControlsSection/AdminGridFilterControlsSection.xml b/app/code/Magento/Ui/Test/Mftf/Section/AdminGridControlsSection/AdminGridFilterControlsSection.xml new file mode 100644 index 0000000000000..db3f5de3a97ee --- /dev/null +++ b/app/code/Magento/Ui/Test/Mftf/Section/AdminGridControlsSection/AdminGridFilterControlsSection.xml @@ -0,0 +1,16 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + /** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +--> +<sections xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Page/etc/SectionObject.xsd"> + <section name="AdminGridFilterControls"> + <element name="filters" type="button" selector=".admin__data-grid-header[data-bind='afterRender: \$data.setToolbarNode'] button[data-action='grid-filter-expand']" timeout="5"/> + <element name="applyFilters" type="button" selector="button[data-action='grid-filter-apply']" timeout="30"/> + <element name="cancel" type="button" selector="button[data-action='grid-filter-cancel']" timeout="30"/> + <element name="clearAll" type="button" selector="(//*[contains(@class, 'admin__data-grid-header')][contains(@data-bind, 'afterRender: \$data.setToolbarNode')]//button[contains(@data-action, 'reset')])[1]" timeout="5"/> + </section> +</sections> diff --git a/app/code/Magento/Ui/Test/Mftf/Section/AdminGridControlsSection/AdminGridHeadersSection.xml b/app/code/Magento/Ui/Test/Mftf/Section/AdminGridControlsSection/AdminGridHeadersSection.xml new file mode 100644 index 0000000000000..89831359657bf --- /dev/null +++ b/app/code/Magento/Ui/Test/Mftf/Section/AdminGridControlsSection/AdminGridHeadersSection.xml @@ -0,0 +1,15 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + /** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +--> +<sections xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Page/etc/SectionObject.xsd"> + <section name="AdminGridHeaders"> + <element name="title" type="text" selector=".page-title-wrapper h1"/> + <element name="headerByName" type="text" selector="//div[@data-role='grid-wrapper']//span[@class='data-grid-cell-content' and contains(text(), '{{var1}}')]/parent::*" parameterized="true"/> + <element name="columnsNames" type="text" selector="[data-role='grid-wrapper'] .data-grid-th > span"/> + </section> +</sections> diff --git a/app/code/Magento/Ui/Test/Mftf/Section/AdminGridControlsSection/AdminGridMainControlsSection.xml b/app/code/Magento/Ui/Test/Mftf/Section/AdminGridControlsSection/AdminGridMainControlsSection.xml new file mode 100644 index 0000000000000..1ccc3e82b15ce --- /dev/null +++ b/app/code/Magento/Ui/Test/Mftf/Section/AdminGridControlsSection/AdminGridMainControlsSection.xml @@ -0,0 +1,20 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + /** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +--> +<sections xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Page/etc/SectionObject.xsd"> + <section name="AdminGridMainControls"> + <element name="add" type="button" selector="#add" timeout="30"/> + <element name="back" type="button" selector="#back" timeout="30"/> + <element name="reset" type="button" selector="#reset" timeout="30"/> + <element name="save" type="button" selector="#save-button" timeout="30"/> + <element name="saveAndContinue" type="button" selector="#save-button" timeout="30"/> + <element name="saveArrow" type="button" selector="button[data-ui-id='save-button-dropdown']" timeout="5"/> + <element name="saveAndClose" type="button" selector="span[title='Save & Close']" timeout="30"/> + <element name="saveAndNew" type="button" selector="span[title='Save & New']" timeout="30"/> + </section> +</sections> diff --git a/app/code/Magento/Ui/Test/Mftf/Section/AdminGridControlsSection/AdminGridRowSection.xml b/app/code/Magento/Ui/Test/Mftf/Section/AdminGridControlsSection/AdminGridRowSection.xml new file mode 100644 index 0000000000000..eb7a690e8fed1 --- /dev/null +++ b/app/code/Magento/Ui/Test/Mftf/Section/AdminGridControlsSection/AdminGridRowSection.xml @@ -0,0 +1,19 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + /** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +--> +<sections xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Page/etc/SectionObject.xsd"> + <section name="AdminGridRow"> + <element name="rowOne" type="text" selector="tr[data-repeat-index='0']"/> + <element name="rowByIndex" type="text" selector="tr[data-repeat-index='{{var1}}']" parameterized="true"/> + + <element name="editByValue" type="button" selector="//a[ancestor::tr[contains(., '{{var1}}')]]" parameterized="true"/> + + <element name="checkboxByValue" type="checkbox" selector="//input[ancestor::tr[contains(., '{{var1}}')]]" parameterized="true"/> + <element name="checkboxByIndex" type="checkbox" selector=".data-row[data-repeat-index='{{var1}}'] .admin__control-checkbox" parameterized="true"/> + </section> +</sections> diff --git a/app/code/Magento/Ui/Test/Mftf/Section/AdminGridControlsSection/AdminGridRowsPerPageSection.xml b/app/code/Magento/Ui/Test/Mftf/Section/AdminGridControlsSection/AdminGridRowsPerPageSection.xml new file mode 100644 index 0000000000000..de87f2290bf88 --- /dev/null +++ b/app/code/Magento/Ui/Test/Mftf/Section/AdminGridControlsSection/AdminGridRowsPerPageSection.xml @@ -0,0 +1,13 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + /** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +--> +<sections xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Page/etc/SectionObject.xsd"> + <section name="AdminGridRowsPerPage"> + <element name="count" type="select" selector=".admin__data-grid-header[data-bind='afterRender: \$data.setToolbarNode'] .selectmenu-value input" timeout="30"/> + </section> +</sections> diff --git a/app/code/Magento/Ui/Test/Mftf/Section/AdminGridControlsSection/AdminGridSearchBoxSection.xml b/app/code/Magento/Ui/Test/Mftf/Section/AdminGridControlsSection/AdminGridSearchBoxSection.xml new file mode 100644 index 0000000000000..a46151184c158 --- /dev/null +++ b/app/code/Magento/Ui/Test/Mftf/Section/AdminGridControlsSection/AdminGridSearchBoxSection.xml @@ -0,0 +1,14 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + /** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +--> +<sections xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Page/etc/SectionObject.xsd"> + <section name="AdminGridSearchBox"> + <element name="searchByKeyword" type="input" selector=".admin__data-grid-header[data-bind='afterRender: \$data.setToolbarNode'] input[placeholder='Search by keyword']"/> + <element name="search" type="button" selector=".admin__data-grid-header[data-bind='afterRender: \$data.setToolbarNode'] input[placeholder='Search by keyword'] + .action-submit" timeout="30"/> + </section> +</sections> diff --git a/app/code/Magento/Ui/Test/Mftf/Section/AdminGridControlsSection/AdminGridSelectRowsSection.xml b/app/code/Magento/Ui/Test/Mftf/Section/AdminGridControlsSection/AdminGridSelectRowsSection.xml new file mode 100644 index 0000000000000..15e700799817a --- /dev/null +++ b/app/code/Magento/Ui/Test/Mftf/Section/AdminGridControlsSection/AdminGridSelectRowsSection.xml @@ -0,0 +1,16 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + /** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +--> +<sections xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Page/etc/SectionObject.xsd"> + <section name="AdminGridSelectRows"> + <element name="multicheckDropdown" type="button" selector="div[data-role='grid-wrapper'] th.data-grid-multicheck-cell button.action-multicheck-toggle"/> + <element name="multicheckOption" type="button" selector="//div[@data-role='grid-wrapper']//th[contains(@class, data-grid-multicheck-cell)]//li//span[text() = '{{label}}']" parameterized="true"/> + <element name="bulkActionDropdown" type="button" selector="div.admin__data-grid-header-row.row div.action-select-wrap button.action-select"/> + <element name="bulkActionOption" type="button" selector="//div[contains(@class,'admin__data-grid-header-row') and contains(@class, 'row')]//div[contains(@class, 'action-select-wrap')]//ul/li/span[text() = '{{label}}']" parameterized="true"/> + </section> +</sections> diff --git a/app/code/Magento/Ui/Test/Mftf/Section/ModalConfirmationSection.xml b/app/code/Magento/Ui/Test/Mftf/Section/ModalConfirmationSection.xml index 4bf84d9ee63da..07c4498efdafc 100644 --- a/app/code/Magento/Ui/Test/Mftf/Section/ModalConfirmationSection.xml +++ b/app/code/Magento/Ui/Test/Mftf/Section/ModalConfirmationSection.xml @@ -11,6 +11,6 @@ <section name="ModalConfirmationSection"> <element name="modalContent" type="text" selector="aside.confirm div.modal-content"/> <element name="CancelButton" type="button" selector="//footer[@class='modal-footer']/button[contains(@class, 'action-dismiss')]"/> - <element name="OkButton" type="button" selector="//footer[@class='modal-footer']/button[contains(@class, 'action-accept')]"/> + <element name="OkButton" type="button" selector="//footer[@class='modal-footer']/button[contains(@class, 'action-accept')]" timeout="30"/> </section> </sections> diff --git a/app/code/Magento/Ui/Test/Mftf/Test/AdminGridFilterDeleteAndVerifyErrorMessageTest.xml b/app/code/Magento/Ui/Test/Mftf/Test/AdminGridFilterDeleteAndVerifyErrorMessageTest.xml index 674d1d2216793..d38e065914617 100644 --- a/app/code/Magento/Ui/Test/Mftf/Test/AdminGridFilterDeleteAndVerifyErrorMessageTest.xml +++ b/app/code/Magento/Ui/Test/Mftf/Test/AdminGridFilterDeleteAndVerifyErrorMessageTest.xml @@ -20,7 +20,7 @@ <before> <magentoCLI command="config:set system/backup/functionality_enabled 1" stepKey="setEnableBackupToYes"/> - <actionGroup ref = "LoginAsAdmin" stepKey="loginAsAdmin"/> + <actionGroup ref="AdminLoginActionGroup" stepKey="loginAsAdmin"/> <createData entity="NewRootCategory" stepKey="rootCategory"/> <createData entity="defaultSimpleProduct" stepKey="createProduct"> <requiredEntity createDataKey="rootCategory" /> @@ -47,7 +47,7 @@ <magentoCLI command="config:set system/backup/functionality_enabled 0" stepKey="setEnableBackupToNo"/> <deleteData stepKey="deleteRootCategory" createDataKey="rootCategory"/> <deleteData stepKey="deleteProduct" createDataKey="createProduct"/> - <actionGroup ref="logout" stepKey="logout"/> + <actionGroup ref="AdminLogoutActionGroup" stepKey="logout"/> </after> <!--Filter created simple product in grid and add category and website created in create data--> diff --git a/app/code/Magento/Ui/Test/Unit/Component/MassAction/FilterTest.php b/app/code/Magento/Ui/Test/Unit/Component/MassAction/FilterTest.php index bee5cec1f0630..665e2e636cf91 100644 --- a/app/code/Magento/Ui/Test/Unit/Component/MassAction/FilterTest.php +++ b/app/code/Magento/Ui/Test/Unit/Component/MassAction/FilterTest.php @@ -3,6 +3,7 @@ * Copyright © Magento, Inc. All rights reserved. * See COPYING.txt for license details. */ + namespace Magento\Ui\Test\Unit\Component\MassAction; use Magento\Framework\Api\Filter as ApiFilter; @@ -10,68 +11,77 @@ use Magento\Framework\Api\Search\SearchResultInterface; use Magento\Framework\App\RequestInterface; use Magento\Framework\Data\Collection\AbstractDb; +use Magento\Framework\Model\ResourceModel\Db\AbstractDb as ResourceAbstractDb; use Magento\Framework\TestFramework\Unit\Helper\ObjectManager; use Magento\Framework\View\Element\UiComponent\ContextInterface; use Magento\Framework\View\Element\UiComponent\DataProvider\DataProviderInterface; use Magento\Framework\View\Element\UiComponentFactory; use Magento\Framework\View\Element\UiComponentInterface; use Magento\Ui\Component\MassAction\Filter; +use PHPUnit\Framework\MockObject\MockObject; /** - * Class FilterTest + * Ui component massaction filter tests * * @SuppressWarnings(PHPMD.CouplingBetweenObjects) */ class FilterTest extends \PHPUnit\Framework\TestCase { /** - * \PHPUnit_Framework_MockObject_MockObject + * MockObject */ private $requestMock; /** - * \PHPUnit_Framework_MockObject_MockObject + * MockObject */ private $uiComponentFactoryMock; /** - * \PHPUnit_Framework_MockObject_MockObject + * MockObject */ private $filterBuilderMock; - /** @var \Magento\Ui\Component\MassAction\Filter */ + /** + * @var Filter + */ private $filter; /** - * @var \Magento\Framework\TestFramework\Unit\Helper\ObjectManager + * @var ObjectManager */ private $objectManager; /** - * \PHPUnit_Framework_MockObject_MockObject + * MockObject */ private $dataProviderMock; /** - * \PHPUnit_Framework_MockObject_MockObject + * MockObject */ private $abstractDbMock; /** - * \PHPUnit_Framework_MockObject_MockObject + * MockObject */ private $searchResultMock; /** - * \PHPUnit_Framework_MockObject_MockObject + * MockObject */ private $uiComponentMock; /** - * \PHPUnit_Framework_MockObject_MockObject + * MockObject */ private $contextMock; + /** + * @var MockObject + */ + private $resourceAbstractDbMock; + /** * Set up */ @@ -86,7 +96,11 @@ protected function setUp() $this->requestMock = $this->createMock(RequestInterface::class); $this->dataProviderMock = $this->createMock(DataProviderInterface::class); $this->uiComponentMock = $this->createMock(UiComponentInterface::class); - $this->abstractDbMock = $this->createMock(AbstractDb::class); + $this->abstractDbMock = $this->createPartialMock( + AbstractDb::class, + ['getResource', 'addFieldToFilter'] + ); + $this->resourceAbstractDbMock = $this->createMock(ResourceAbstractDb::class); $this->contextMock = $this->createMock(ContextInterface::class); $this->searchResultMock = $this->createMock(SearchResultInterface::class); $uiComponentMockTwo = $this->createMock(UiComponentInterface::class); @@ -119,6 +133,7 @@ protected function setUp() * @param int[]|bool $excludedIds * @param int $filterExpected * @param string $conditionExpected + * @throws \Magento\Framework\Exception\LocalizedException * @dataProvider applySelectionOnTargetProviderDataProvider */ public function testApplySelectionOnTargetProvider($selectedIds, $excludedIds, $filterExpected, $conditionExpected) @@ -133,10 +148,10 @@ public function testApplySelectionOnTargetProvider($selectedIds, $excludedIds, $ public function applySelectionOnTargetProviderDataProvider() { return [ - [[1, 2, 3], 'false' , 0, 'in'], - [[1, 2, 3], [1, 2, 3] , 1, 'nin'], - ['false', [1, 2, 3] , 1, 'nin'], - ['false', 'false' , 0, ''] + [[1, 2, 3], 'false', 0, 'in'], + [[1, 2, 3], [1, 2, 3], 1, 'nin'], + ['false', [1, 2, 3], 1, 'nin'], + ['false', 'false', 0, ''] ]; } @@ -190,6 +205,7 @@ public function testApplySelectionOnTargetProviderException() * @param int[]|bool $excludedIds * @param int $filterExpected * @param string $conditionExpected + * @throws \Magento\Framework\Exception\LocalizedException * @dataProvider applySelectionOnTargetProviderDataProvider */ public function testGetCollection($selectedIds, $excludedIds, $filterExpected, $conditionExpected) @@ -207,6 +223,12 @@ public function testGetCollection($selectedIds, $excludedIds, $filterExpected, $ ->method('getParam') ->with(Filter::EXCLUDED_PARAM) ->willReturn($excludedIds); + $this->abstractDbMock->expects($this->once()) + ->method('getResource') + ->willReturn($this->resourceAbstractDbMock); + $this->abstractDbMock->expects($this->once()) + ->method('addFieldToFilter') + ->willReturnSelf(); $this->assertEquals($this->abstractDbMock, $this->filter->getCollection($this->abstractDbMock)); } @@ -217,6 +239,7 @@ public function testGetCollection($selectedIds, $excludedIds, $filterExpected, $ * @param int[]|bool $excludedIds * @param int $filterExpected * @param string $conditionExpected + * @throws \Magento\Framework\Exception\LocalizedException * @dataProvider applySelectionOnTargetProviderDataProvider */ public function testGetCollectionWithCollection($selectedIds, $excludedIds, $filterExpected, $conditionExpected) @@ -233,11 +256,20 @@ public function testGetCollectionWithCollection($selectedIds, $excludedIds, $fil $this->requestMock->expects($this->any()) ->method('getParam') - ->willReturnMap([ - ['namespace', null, ''], - [Filter::SELECTED_PARAM, null, $selectedIds], - [Filter::EXCLUDED_PARAM, null, $excludedIds], - ]); + ->willReturnMap( + [ + ['namespace', null, ''], + [Filter::SELECTED_PARAM, null, $selectedIds], + [Filter::EXCLUDED_PARAM, null, $excludedIds], + ] + ); + + $this->abstractDbMock->expects($this->once()) + ->method('getResource') + ->willReturn($this->resourceAbstractDbMock); + $this->abstractDbMock->expects($this->once()) + ->method('addFieldToFilter') + ->willReturnSelf(); $this->assertEquals($this->abstractDbMock, $this->filter->getCollection($this->abstractDbMock)); } diff --git a/app/code/Magento/Ui/Test/Unit/Controller/Adminhtml/Index/RenderTest.php b/app/code/Magento/Ui/Test/Unit/Controller/Adminhtml/Index/RenderTest.php index 2bba8686490b6..7c4c373abad3b 100644 --- a/app/code/Magento/Ui/Test/Unit/Controller/Adminhtml/Index/RenderTest.php +++ b/app/code/Magento/Ui/Test/Unit/Controller/Adminhtml/Index/RenderTest.php @@ -12,8 +12,8 @@ use Magento\Framework\View\Element\UiComponent\ContextInterface; use Magento\Ui\Controller\Adminhtml\Index\Render; use Magento\Ui\Model\UiComponentTypeResolver; -use Zend\Http\AbstractMessage; -use Zend\Http\Response; +use Laminas\Http\AbstractMessage; +use Laminas\Http\Response; /** * @SuppressWarnings(PHPMD.CouplingBetweenObjects) diff --git a/app/code/Magento/Ui/Test/Unit/Controller/Index/RenderTest.php b/app/code/Magento/Ui/Test/Unit/Controller/Index/RenderTest.php index 646cea81212f9..894ff354a96fe 100644 --- a/app/code/Magento/Ui/Test/Unit/Controller/Index/RenderTest.php +++ b/app/code/Magento/Ui/Test/Unit/Controller/Index/RenderTest.php @@ -12,8 +12,8 @@ use Magento\Framework\View\Element\UiComponent\ContextInterface; use Magento\Ui\Controller\Index\Render; use Magento\Ui\Model\UiComponentTypeResolver; -use Zend\Http\AbstractMessage; -use Zend\Http\Response; +use Laminas\Http\AbstractMessage; +use Laminas\Http\Response; /** * @SuppressWarnings(PHPMD.CouplingBetweenObjects) diff --git a/app/code/Magento/Ui/Test/Unit/Model/Export/MetadataProviderTest.php b/app/code/Magento/Ui/Test/Unit/Model/Export/MetadataProviderTest.php index 50bce70e08feb..1b86bc14c7df7 100755 --- a/app/code/Magento/Ui/Test/Unit/Model/Export/MetadataProviderTest.php +++ b/app/code/Magento/Ui/Test/Unit/Model/Export/MetadataProviderTest.php @@ -3,22 +3,30 @@ * Copyright © Magento, Inc. All rights reserved. * See COPYING.txt for license details. */ + namespace Magento\Ui\Test\Unit\Model\Export; +use Magento\Framework\Api\AttributeInterface; use Magento\Framework\Api\Search\DocumentInterface; +use Magento\Framework\Data\OptionSourceInterface; +use Magento\Framework\DataObject; +use Magento\Framework\Exception\LocalizedException; use Magento\Framework\Locale\ResolverInterface; use Magento\Framework\Stdlib\DateTime\TimezoneInterface; +use Magento\Framework\TestFramework\Unit\Helper\ObjectManager; use Magento\Framework\View\Element\UiComponentInterface; +use Magento\Ui\Component\Filters; +use Magento\Ui\Component\Filters\Type\Select; use Magento\Ui\Component\Listing\Columns; use Magento\Ui\Component\Listing\Columns\Column; use Magento\Ui\Component\MassAction\Filter; use Magento\Ui\Model\Export\MetadataProvider; -use Magento\Framework\TestFramework\Unit\Helper\ObjectManager; +use PHPUnit\Framework\TestCase; /** * @SuppressWarnings(PHPMD.CouplingBetweenObjects) */ -class MetadataProviderTest extends \PHPUnit\Framework\TestCase +class MetadataProviderTest extends TestCase { /** * @var MetadataProvider @@ -45,25 +53,25 @@ class MetadataProviderTest extends \PHPUnit\Framework\TestCase */ protected function setUp() { - $this->filter = $this->getMockBuilder(\Magento\Ui\Component\MassAction\Filter::class) - ->disableOriginalConstructor() - ->getMock(); + $this->filter = $this->getMockBuilder(Filter::class) + ->disableOriginalConstructor() + ->getMock(); - $this->localeDate = $this->getMockBuilder(\Magento\Framework\Stdlib\DateTime\TimezoneInterface::class) - ->disableOriginalConstructor() - ->getMock(); + $this->localeDate = $this->getMockBuilder(TimezoneInterface::class) + ->disableOriginalConstructor() + ->getMock(); - $this->localeResolver = $this->getMockBuilder(\Magento\Framework\Locale\ResolverInterface::class) - ->disableOriginalConstructor() - ->getMock(); + $this->localeResolver = $this->getMockBuilder(ResolverInterface::class) + ->disableOriginalConstructor() + ->getMock(); $this->localeResolver->expects($this->any()) - ->method('getLocale') - ->willReturn(null); + ->method('getLocale') + ->willReturn(null); $objectManager = new ObjectManager($this); $this->model = $objectManager->getObject( - \Magento\Ui\Model\Export\MetadataProvider::class, + MetadataProvider::class, [ 'filter' => $this->filter, 'localeDate' => $this->localeDate, @@ -76,8 +84,10 @@ protected function setUp() /** * @param array $columnLabels * @param array $expected + * * @return void * @dataProvider getColumnsDataProvider + * @throws \Exception */ public function testGetHeaders(array $columnLabels, array $expected): void { @@ -97,12 +107,12 @@ public function testGetHeaders(array $columnLabels, array $expected): void public function getColumnsDataProvider(): array { return [ - [['ID'],['ID']], - [['Name'],['Name']], - [['Id'],['Id']], - [['id'],['id']], - [['IDTEST'],['IDTEST']], - [['ID TEST'],['ID TEST']], + [['ID'], ['ID']], + [['Name'], ['Name']], + [['Id'], ['Id']], + [['id'], ['id']], + [['IDTEST'], ['IDTEST']], + [['ID TEST'], ['ID TEST']], ]; } @@ -126,6 +136,7 @@ public function testGetFields() * @param string $columnLabel * @param string $columnActionsName * @param string $columnActionsLabel + * * @return UiComponentInterface|\PHPUnit_Framework_MockObject_MockObject */ protected function prepareColumns( @@ -136,57 +147,57 @@ protected function prepareColumns( $columnActionsLabel = 'actions_label' ) { /** @var UiComponentInterface|\PHPUnit_Framework_MockObject_MockObject $component */ - $component = $this->getMockBuilder(\Magento\Framework\View\Element\UiComponentInterface::class) - ->getMockForAbstractClass(); + $component = $this->getMockBuilder(UiComponentInterface::class) + ->getMockForAbstractClass(); /** @var Columns|\PHPUnit_Framework_MockObject_MockObject $columns */ - $columns = $this->getMockBuilder(\Magento\Ui\Component\Listing\Columns::class) - ->disableOriginalConstructor() - ->getMock(); + $columns = $this->getMockBuilder(Columns::class) + ->disableOriginalConstructor() + ->getMock(); /** @var Column|\PHPUnit_Framework_MockObject_MockObject $column */ - $column = $this->getMockBuilder(\Magento\Ui\Component\Listing\Columns\Column::class) - ->disableOriginalConstructor() - ->getMock(); + $column = $this->getMockBuilder(Column::class) + ->disableOriginalConstructor() + ->getMock(); /** @var Column|\PHPUnit_Framework_MockObject_MockObject $columnActions */ - $columnActions = $this->getMockBuilder(\Magento\Ui\Component\Listing\Columns\Column::class) - ->disableOriginalConstructor() - ->getMock(); + $columnActions = $this->getMockBuilder(Column::class) + ->disableOriginalConstructor() + ->getMock(); $component->expects($this->any()) - ->method('getName') - ->willReturn($componentName); - $component->expects($this->once()) - ->method('getChildComponents') - ->willReturn([$columns]); + ->method('getName') + ->willReturn($componentName); + $component->expects($this->atLeastOnce()) + ->method('getChildComponents') + ->willReturn([$columns]); - $columns->expects($this->once()) - ->method('getChildComponents') - ->willReturn([$column, $columnActions]); + $columns->expects($this->atLeastOnce()) + ->method('getChildComponents') + ->willReturn([$column, $columnActions]); $column->expects($this->any()) - ->method('getName') - ->willReturn($columnName); + ->method('getName') + ->willReturn($columnName); $column->expects($this->any()) - ->method('getData') - ->willReturnMap( - [ - ['config/label', null, $columnLabel], - ['config/dataType', null, 'data_type'], - ] - ); + ->method('getData') + ->willReturnMap( + [ + ['config/label', null, $columnLabel], + ['config/dataType', null, 'data_type'], + ] + ); $columnActions->expects($this->any()) - ->method('getName') - ->willReturn($columnActionsName); + ->method('getName') + ->willReturn($columnActionsName); $columnActions->expects($this->any()) - ->method('getData') - ->willReturnMap( - [ - ['config/label', null, $columnActionsLabel], - ['config/dataType', null, 'actions'], - ] - ); + ->method('getData') + ->willReturnMap( + [ + ['config/label', null, $columnActionsLabel], + ['config/dataType', null, 'actions'], + ] + ); return $component; } @@ -196,25 +207,26 @@ protected function prepareColumns( * @param array $fields * @param array $options * @param array $expected + * * @dataProvider getRowDataProvider */ public function testGetRowData($key, $fields, $options, $expected) { /** @var DocumentInterface|\PHPUnit_Framework_MockObject_MockObject $document */ - $document = $this->getMockBuilder(\Magento\Framework\Api\Search\DocumentInterface::class) - ->getMockForAbstractClass(); + $document = $this->getMockBuilder(DocumentInterface::class) + ->getMockForAbstractClass(); - $attribute = $this->getMockBuilder(\Magento\Framework\Api\AttributeInterface::class) - ->getMockForAbstractClass(); + $attribute = $this->getMockBuilder(AttributeInterface::class) + ->getMockForAbstractClass(); $document->expects($this->once()) - ->method('getCustomAttribute') - ->with($fields[0]) - ->willReturn($attribute); + ->method('getCustomAttribute') + ->with($fields[0]) + ->willReturn($attribute); $attribute->expects($this->once()) - ->method('getValue') - ->willReturn($key); + ->method('getValue') + ->willReturn($key); $result = $this->model->getRowData($document, $fields, $options); $this->assertTrue(is_array($result)); @@ -249,7 +261,7 @@ public function getRowDataProvider() ], ], 'expected' => [ - '', + 'key_2', ], ], [ @@ -265,54 +277,131 @@ public function getRowDataProvider() /** * @param string $filter - * @param array $options + * @param array $filterOptions + * @param array $columnsOptions * @param array $expected + * + * @throws LocalizedException * @dataProvider getOptionsDataProvider */ - public function testGetOptions($filter, $options, $expected) + public function testGetOptions(string $filter, array $filterOptions, array $columnsOptions, array $expected) { - $component = $this->getMockBuilder(\Magento\Framework\View\Element\UiComponentInterface::class) - ->getMockForAbstractClass(); + $component = $this->prepareColumnsWithOptions($filter, $filterOptions, $columnsOptions); - $childComponent = $this->getMockBuilder(\Magento\Framework\View\Element\UiComponentInterface::class) - ->getMockForAbstractClass(); + $this->filter->expects($this->exactly(2)) + ->method('getComponent') + ->willReturn($component); - $filters = $this->getMockBuilder(\Magento\Ui\Component\Filters::class) - ->disableOriginalConstructor() - ->getMock(); + $result = $this->model->getOptions(); + $this->assertTrue(is_array($result)); + $this->assertCount(2, $result); + $this->assertEquals($expected, $result); + } - $select = $this->getMockBuilder(\Magento\Ui\Component\Filters\Type\Select::class) - ->disableOriginalConstructor() - ->getMock(); + /** + * @param string $filter + * @param array $filterOptions + * + * @param array $columnsOptions + * + * @return UiComponentInterface|\PHPUnit_Framework_MockObject_MockObject + */ + protected function prepareColumnsWithOptions(string $filter, array $filterOptions, array $columnsOptions) + { + /** @var UiComponentInterface|\PHPUnit_Framework_MockObject_MockObject $component */ + $component = $this->getMockBuilder(UiComponentInterface::class) + ->getMockForAbstractClass(); + + $listingTopComponent = $this->getMockBuilder(UiComponentInterface::class) + ->getMockForAbstractClass(); + + $filters = $this->getMockBuilder(Filters::class) + ->disableOriginalConstructor() + ->getMock(); + + /** @var Columns|\PHPUnit_Framework_MockObject_MockObject $columns */ + $columns = $this->getMockBuilder(Columns::class) + ->disableOriginalConstructor() + ->getMock(); + + /** @var Column|\PHPUnit_Framework_MockObject_MockObject $column */ + $column = $this->getMockBuilder(Column::class) + ->disableOriginalConstructor() + ->getMock(); + /** @var Column|\PHPUnit_Framework_MockObject_MockObject $columnActions */ + $columnActions = $this->getMockBuilder(Column::class) + ->disableOriginalConstructor() + ->getMock(); - $this->filter->expects($this->once()) - ->method('getComponent') - ->willReturn($component); + $component->expects($this->any()) + ->method('getName') + ->willReturn('columns_component_name'); + $component->expects($this->atLeastOnce()) + ->method('getChildComponents') + ->willReturn(['columns' => $columns, 'listing_top' => $listingTopComponent]); - $component->expects($this->once()) - ->method('getChildComponents') - ->willReturn(['listing_top' => $childComponent]); + $listingTopComponent->expects($this->once()) + ->method('getChildComponents') + ->willReturn([$filters]); - $childComponent->expects($this->once()) - ->method('getChildComponents') - ->willReturn([$filters]); + $select = $this->getMockBuilder(Select::class) + ->disableOriginalConstructor() + ->getMock(); $filters->expects($this->once()) - ->method('getChildComponents') - ->willReturn([$select]); + ->method('getChildComponents') + ->willReturn([$select]); $select->expects($this->any()) - ->method('getName') - ->willReturn($filter); + ->method('getName') + ->willReturn($filter); $select->expects($this->any()) - ->method('getData') - ->with('config/options') - ->willReturn($options); + ->method('getData') + ->with('config/options') + ->willReturn($filterOptions); - $result = $this->model->getOptions(); - $this->assertTrue(is_array($result)); - $this->assertCount(1, $result); - $this->assertEquals($expected, $result); + $columns->expects($this->atLeastOnce()) + ->method('getChildComponents') + ->willReturn([$column, $columnActions]); + + $column->expects($this->any()) + ->method('getName') + ->willReturn('column_name'); + + $optionSource = $this->getMockBuilder(OptionSourceInterface::class) + ->getMockForAbstractClass(); + $optionSource->expects($this->once()) + ->method('toOptionArray') + ->willReturn($columnsOptions); + + $column->expects($this->any()) + ->method('getData') + ->willReturnMap( + [ + ['config/label', null, 'column_label'], + ['config/dataType', null, 'data_type'], + ['options', null, $optionSource], + ] + ); + + $column->expects($this->once()) + ->method('hasData') + ->willReturn(true) + ->with('options'); + + $columnActions->expects($this->any()) + ->method('getName') + ->willReturn('column_actions_name'); + $columnActions->expects($this->any()) + ->method('getData') + ->willReturnMap( + [ + ['config/label', null, 'column_actions_label'], + ['config/dataType', null, 'actions'], + ] + ); + + return $component; } /** @@ -323,7 +412,13 @@ public function getOptionsDataProvider() return [ [ 'filter' => 'filter_name', - 'options' => [ + 'filterOptions' => [ + [ + 'value' => 'value_1', + 'label' => 'label_1', + ] + ], + 'columnsOptions' => [ [ 'value' => 'value_1', 'label' => 'label_1', @@ -333,11 +428,25 @@ public function getOptionsDataProvider() 'filter_name' => [ 'value_1' => 'label_1', ], + 'column_name' => [ + 'value_1' => 'label_1', + ] ], ], [ 'filter' => 'filter_name', - 'options' => [ + 'filterOptions' => [ + [ + 'value' => [ + [ + 'value' => 'value_2', + 'label' => 'label_2', + ], + ], + 'label' => 'label_1', + ] + ], + 'columnsOptions' => [ [ 'value' => [ [ @@ -352,11 +461,14 @@ public function getOptionsDataProvider() 'filter_name' => [ 'value_2' => 'label_1label_2', ], + 'column_name' => [ + 'value_2' => 'label_1label_2', + ] ], ], [ 'filter' => 'filter_name', - 'options' => [ + 'filterOptions' => [ [ 'value' => [ [ @@ -372,10 +484,12 @@ public function getOptionsDataProvider() 'label' => 'label_1', ] ], + 'columnsOptions' => [], 'expected' => [ 'filter_name' => [ 'value_3' => 'label_1label_2label_3', ], + 'column_name' => [] ], ], ]; @@ -386,29 +500,31 @@ public function getOptionsDataProvider() * * @param string $fieldValue * @param string $expected + * * @dataProvider convertDateProvider * @covers \Magento\Ui\Model\Export\MetadataProvider::convertDate() + * @throws \Exception */ public function testConvertDate($fieldValue, $expected) { $componentName = 'component_name'; /** @var DocumentInterface|\PHPUnit_Framework_MockObject_MockObject $document */ - $document = $this->getMockBuilder(\Magento\Framework\DataObject::class) - ->disableOriginalConstructor() - ->getMock(); + $document = $this->getMockBuilder(DataObject::class) + ->disableOriginalConstructor() + ->getMock(); $document->expects($this->once()) - ->method('getData') - ->with('field') - ->willReturn($fieldValue); + ->method('getData') + ->with('field') + ->willReturn($fieldValue); $this->localeDate->expects($this->once()) - ->method('date') - ->willReturn(new \DateTime($fieldValue, new \DateTimeZone('UTC'))); + ->method('date') + ->willReturn(new \DateTime($fieldValue, new \DateTimeZone('UTC'))); $document->expects($this->once()) - ->method('setData') - ->with('field', $expected); + ->method('setData') + ->with('field', $expected); $this->model->convertDate($document, $componentName); } diff --git a/app/code/Magento/Ui/Test/Unit/TemplateEngine/Xhtml/ResultTest.php b/app/code/Magento/Ui/Test/Unit/TemplateEngine/Xhtml/ResultTest.php new file mode 100644 index 0000000000000..0890a3c095c40 --- /dev/null +++ b/app/code/Magento/Ui/Test/Unit/TemplateEngine/Xhtml/ResultTest.php @@ -0,0 +1,144 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ + +declare(strict_types=1); + +namespace Magento\Ui\Test\Unit\TemplateEngine\Xhtml; + +use Magento\Framework\App\State; +use Magento\Framework\TestFramework\Unit\Helper\ObjectManager as ObjectManagerHelper; +use Magento\Framework\View\Element\UiComponentInterface; +use Magento\Framework\View\TemplateEngine\Xhtml\CompilerInterface; +use Magento\Framework\View\TemplateEngine\Xhtml\Template; +use Magento\Ui\Component\Listing; +use Magento\Ui\TemplateEngine\Xhtml\Result; +use PHPUnit\Framework\MockObject\MockObject; +use PHPUnit\Framework\TestCase; +use Psr\Log\LoggerInterface; + +/** + * Test for \Magento\Ui\TemplateEngine\Xhtml\Result. + */ +class ResultTest extends TestCase +{ + /** + * Stub simple html element + */ + private const STUB_HTML_ELEMENT = '<div id="id"></div>'; + + /** + * @var Result + */ + private $model; + + /** + * @var ObjectManagerHelper|MockObject + */ + private $objectManagerHelper; + + /** + * @var Template|MockObject + */ + private $templateMock; + + /** + * @var CompilerInterface|MockObject + */ + private $compilerMock; + + /** + * @var UiComponentInterface|MockObject + */ + private $componentMock; + + /** + * @var LoggerInterface|MockObject + */ + private $loggerMock; + + /** + * @var State|MockObject + */ + private $stateMock; + + /** + * @inheritdoc + */ + protected function setUp() + { + $this->templateMock = $this->createMock(Template::class); + $this->compilerMock = $this->createMock(CompilerInterface::class); + $this->componentMock = $this->createMock(Listing::class); + $this->loggerMock = $this->createMock(LoggerInterface::class); + $this->stateMock = $this->createMock(State::class); + + $this->objectManagerHelper = new ObjectManagerHelper($this); + $this->model = $this->objectManagerHelper->getObject( + Result::class, + [ + 'template' => $this->templateMock, + 'compiler' => $this->compilerMock, + 'component' => $this->componentMock, + 'logger' => $this->loggerMock, + 'state' => $this->stateMock, + ] + ); + } + + /** + * To string method with exception message + * + * @return void + */ + public function testToStringWithException(): void + { + $e = new \Exception(); + + $this->templateMock->expects($this->once()) + ->method('getDocumentElement') + ->willThrowException($e); + $this->stateMock->expects($this->once()) + ->method('getMode') + ->willReturn(State::MODE_DEVELOPER); + + $this->loggerMock->expects($this->once()) + ->method('critical') + ->with($e); + $this->assertEquals( + '<pre><code>Exception in ' . $e->getFile() . ':' . $e->getLine() . '</code></pre>', + $this->model->__toString() + ); + } + + /** + * To string method + * + * @return void + */ + public function testToString(): void + { + $domElementMock = $this->getMockBuilder(\DOMElement::class) + ->setConstructorArgs(['arg']) + ->getMock(); + + $this->templateMock->expects($this->once()) + ->method('getDocumentElement') + ->willReturn($domElementMock); + $this->compilerMock->expects($this->once()) + ->method('compile') + ->with( + $this->isInstanceOf(\DOMElement::class), + $this->componentMock, + $this->componentMock + ); + $this->templateMock->expects($this->once())->method('__toString'); + $this->compilerMock->expects($this->once()) + ->method('postprocessing') + ->willReturn(self::STUB_HTML_ELEMENT); + + $this->assertEquals(self::STUB_HTML_ELEMENT, $this->model->__toString()); + } +} diff --git a/app/code/Magento/Ui/etc/adminhtml/system.xml b/app/code/Magento/Ui/etc/adminhtml/system.xml index ab4272f8d2a34..77af492c70b36 100644 --- a/app/code/Magento/Ui/etc/adminhtml/system.xml +++ b/app/code/Magento/Ui/etc/adminhtml/system.xml @@ -9,12 +9,12 @@ <system> <section id="dev"> <group id="js"> - <field id="session_storage_logging" translate="label comment" type="select" sortOrder="100" showInDefault="1" showInWebsite="0" showInStore="0" canRestore="1"> + <field id="session_storage_logging" translate="label comment" type="select" sortOrder="100" showInDefault="1" canRestore="1"> <label>Log JS Errors to Session Storage</label> <source_model>Magento\Config\Model\Config\Source\Yesno</source_model> <comment>If enabled, can be used by functional tests for extended reporting</comment> </field> - <field id="session_storage_key" translate="label comment" type="text" sortOrder="110" showInDefault="1" showInWebsite="0" showInStore="0" canRestore="1"> + <field id="session_storage_key" translate="label comment" type="text" sortOrder="110" showInDefault="1" canRestore="1"> <label>Log JS Errors to Session Storage Key</label> <comment>Use this key to retrieve collected js errors</comment> </field> diff --git a/app/code/Magento/Ui/etc/di.xml b/app/code/Magento/Ui/etc/di.xml index 05ace9d556fa0..b0cef3b90d431 100644 --- a/app/code/Magento/Ui/etc/di.xml +++ b/app/code/Magento/Ui/etc/di.xml @@ -194,13 +194,11 @@ <argument name="uiReader" xsi:type="object">uiDefinitionReader</argument> </arguments> </type> - <type name="Magento\Ui\Component\Filter\FilterPool"> + <type name="Magento\Framework\View\Element\UiComponent\DataProvider\FilterPool"> <arguments> - <argument name="filters" xsi:type="array"> - <item name="filter_input" xsi:type="string">Magento\Ui\Component\Filter\Type\Input</item> - <item name="filter_select" xsi:type="string">Magento\Ui\Component\Filter\Type\Select</item> - <item name="filter_range" xsi:type="string">Magento\Ui\Component\Filter\Type\Range</item> - <item name="filter_store" xsi:type="string">Magento\Ui\Component\Filter\Type\Store</item> + <argument name="appliers" xsi:type="array"> + <item name="regular" xsi:type="object">Magento\Framework\View\Element\UiComponent\DataProvider\RegularFilter</item> + <item name="fulltext" xsi:type="object">Magento\Framework\View\Element\UiComponent\DataProvider\FulltextFilter</item> </argument> </arguments> </type> diff --git a/app/code/Magento/Ui/registration.php b/app/code/Magento/Ui/registration.php index 28d4d44e82d9b..d6d80766703fe 100644 --- a/app/code/Magento/Ui/registration.php +++ b/app/code/Magento/Ui/registration.php @@ -4,6 +4,6 @@ * See COPYING.txt for license details. */ -use \Magento\Framework\Component\ComponentRegistrar; +use Magento\Framework\Component\ComponentRegistrar; ComponentRegistrar::register(ComponentRegistrar::MODULE, 'Magento_Ui', __DIR__); diff --git a/app/code/Magento/Ui/view/base/requirejs-config.js b/app/code/Magento/Ui/view/base/requirejs-config.js index cdbbd03c93ae1..5e76600673254 100644 --- a/app/code/Magento/Ui/view/base/requirejs-config.js +++ b/app/code/Magento/Ui/view/base/requirejs-config.js @@ -5,6 +5,7 @@ var config = { shim: { + 'chartjs/Chart.min': ['moment'], 'tiny_mce_4/tinymce.min': { exports: 'tinyMCE' } @@ -23,6 +24,7 @@ var config = { consoleLogger: 'Magento_Ui/js/lib/logger/console-logger', uiLayout: 'Magento_Ui/js/core/renderer/layout', buttonAdapter: 'Magento_Ui/js/form/button-adapter', + chartJs: 'chartjs/Chart.min', tinymce4: 'tiny_mce_4/tinymce.min', wysiwygAdapter: 'mage/adminhtml/wysiwyg/tiny_mce/tinymce4Adapter' } diff --git a/app/code/Magento/Ui/view/base/web/js/grid/columns/image-preview.js b/app/code/Magento/Ui/view/base/web/js/grid/columns/image-preview.js index 2549fa93a834f..0464788d155e2 100644 --- a/app/code/Magento/Ui/view/base/web/js/grid/columns/image-preview.js +++ b/app/code/Magento/Ui/view/base/web/js/grid/columns/image-preview.js @@ -239,7 +239,7 @@ define([ handleKeyDown: function (e) { var key = keyCodes[e.keyCode]; - if (this.visibleRecord() !== null) { + if (this.visibleRecord() !== null && document.activeElement.tagName !== 'INPUT') { if (key === 'pageLeftKey') { this.prev(this.displayedRecord()); } else if (key === 'pageRightKey') { diff --git a/app/code/Magento/Ui/view/base/web/js/grid/columns/image.js b/app/code/Magento/Ui/view/base/web/js/grid/columns/image.js index cec0955b835e0..e8e1cf3246c76 100644 --- a/app/code/Magento/Ui/view/base/web/js/grid/columns/image.js +++ b/app/code/Magento/Ui/view/base/web/js/grid/columns/image.js @@ -80,6 +80,17 @@ define([ return record.css || {}; }, + /** + * Get is active record + * + * @param {Object} record - Data to be preprocessed. + * + * @returns {Boolean} + */ + getIsActive: function (record) { + return this.previewComponent().visibleRecord() === record._rowIndex || false; + }, + /** * Expand image preview */ diff --git a/app/code/Magento/Ui/view/base/web/js/grid/data-storage.js b/app/code/Magento/Ui/view/base/web/js/grid/data-storage.js index 547cdab16cdf1..aa9989251b656 100644 --- a/app/code/Magento/Ui/view/base/web/js/grid/data-storage.js +++ b/app/code/Magento/Ui/view/base/web/js/grid/data-storage.js @@ -188,7 +188,8 @@ define([ result = { items: this.getByIds(request.ids), - totalRecords: request.totalRecords + totalRecords: request.totalRecords, + errorMessage: request.errorMessage }; delay ? @@ -216,7 +217,8 @@ define([ this._requests.push({ ids: this.getIds(data.items), params: params, - totalRecords: data.totalRecords + totalRecords: data.totalRecords, + errorMessage: data.errorMessage }); return this; diff --git a/app/code/Magento/Ui/view/base/web/js/grid/sortBy.js b/app/code/Magento/Ui/view/base/web/js/grid/sortBy.js new file mode 100644 index 0000000000000..50cbb6881a6f1 --- /dev/null +++ b/app/code/Magento/Ui/view/base/web/js/grid/sortBy.js @@ -0,0 +1,78 @@ +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +define([ + 'uiElement' +], function (Element) { + 'use strict'; + + return Element.extend({ + defaults: { + template: 'ui/grid/sortBy', + options: [], + applied: {}, + sorting: 'asc', + columnsProvider: 'ns = ${ $.ns }, componentType = columns', + selectedOption: '', + isVisible: true, + listens: { + 'selectedOption': 'applyChanges' + }, + statefull: { + selectedOption: true, + applied: true + }, + exports: { + applied: '${ $.provider }:params.sorting' + }, + imports: { + preparedOptions: '${ $.columnsProvider }:elems' + }, + modules: { + columns: '${ $.columnsProvider }' + } + }, + + /** + * @inheritdoc + */ + initObservable: function () { + return this._super() + .observe([ + 'applied', + 'selectedOption', + 'isVisible' + ]); + }, + + /** + * Prepared sort order options + */ + preparedOptions: function (columns) { + if (columns && columns.length > 0) { + columns.map(function (column) { + if (column.sortable === true) { + this.options.push({ + value: column.index, + label: column.label + }); + this.isVisible(true); + } else { + this.isVisible(false); + } + }.bind(this)); + } + }, + + /** + * Apply changes + */ + applyChanges: function () { + this.applied({ + field: this.selectedOption(), + direction: this.sorting + }); + } + }); +}); diff --git a/app/code/Magento/Ui/view/base/web/js/lib/core/collection.js b/app/code/Magento/Ui/view/base/web/js/lib/core/collection.js index 0491390d2b6c2..33acba6103b10 100644 --- a/app/code/Magento/Ui/view/base/web/js/lib/core/collection.js +++ b/app/code/Magento/Ui/view/base/web/js/lib/core/collection.js @@ -214,6 +214,19 @@ define([ return regions[name]; }, + /** + * Checks if the specified region has any elements + * associated with it. + * + * @param {String} name + * @returns {Boolean} + */ + regionHasElements: function (name) { + var region = this.getRegion(name); + + return region().length > 0; + }, + /** * Replaces specified regions' data with a provided one. * Creates region if it was not created yet. diff --git a/app/code/Magento/Ui/view/base/web/js/lib/knockout/bindings/optgroup.js b/app/code/Magento/Ui/view/base/web/js/lib/knockout/bindings/optgroup.js index 6ff7c1f673213..ab806e89385b6 100644 --- a/app/code/Magento/Ui/view/base/web/js/lib/knockout/bindings/optgroup.js +++ b/app/code/Magento/Ui/view/base/web/js/lib/knockout/bindings/optgroup.js @@ -250,7 +250,7 @@ define([ // IE6 doesn't like us to assign selection to OPTION nodes before they're added to the document. // That's why we first added them without selection. Now it's time to set the selection. - if (previousSelectedValues.length) { + if (previousSelectedValues.length && newOptions.value) { isSelected = ko.utils.arrayIndexOf( previousSelectedValues, ko.selectExtensions.readValue(newOptions.value) diff --git a/app/code/Magento/Ui/view/base/web/js/lib/validation/rules.js b/app/code/Magento/Ui/view/base/web/js/lib/validation/rules.js index 08f67955976c4..f9778ad8d688c 100644 --- a/app/code/Magento/Ui/view/base/web/js/lib/validation/rules.js +++ b/app/code/Magento/Ui/view/base/web/js/lib/validation/rules.js @@ -830,7 +830,7 @@ define([ ], 'validate-state': [ function (value) { - return value !== 0 || value === ''; + return value !== 0; }, $.mage.__('Please select State/Province.') ], @@ -1069,12 +1069,12 @@ define([ $.mage.__('This link is not allowed.') ], 'validate-dob': [ - function (value) { + function (value, param, params) { if (value === '') { return true; } - return moment(value).isBefore(moment()); + return moment.utc(value, params.dateFormat).isSameOrBefore(moment.utc()); }, $.mage.__('The Date of Birth should not be greater than today.') ] diff --git a/app/code/Magento/Ui/view/base/web/js/modal/modal.js b/app/code/Magento/Ui/view/base/web/js/modal/modal.js index bcbb2f3c31dbd..f5c284165d8d2 100644 --- a/app/code/Magento/Ui/view/base/web/js/modal/modal.js +++ b/app/code/Magento/Ui/view/base/web/js/modal/modal.js @@ -192,7 +192,7 @@ define([ * @param {String} title */ setTitle: function (title) { - var $title = $(this.options.modalTitle), + var $title = this.modal.find(this.options.modalTitle), $subTitle = this.modal.find(this.options.modalSubTitle); $title.text(title); diff --git a/app/code/Magento/Ui/view/base/web/js/modal/prompt.js b/app/code/Magento/Ui/view/base/web/js/modal/prompt.js index 13b4d55ea2787..84e9494a8e781 100644 --- a/app/code/Magento/Ui/view/base/web/js/modal/prompt.js +++ b/app/code/Magento/Ui/view/base/web/js/modal/prompt.js @@ -27,6 +27,44 @@ define([ value: '', validation: false, validationRules: [], + keyEventHandlers: { + + /** + * Enter key press handler, + * submit result and close modal window + * @param {Object} event - event + */ + enterKey: function (event) { + if (this.options.isOpen && this.modal.find(document.activeElement).length || + this.options.isOpen && this.modal[0] === document.activeElement) { + this.closeModal(true); + event.preventDefault(); + } + }, + + /** + * Tab key press handler, + * set focus to elements + */ + tabKey: function () { + if (document.activeElement === this.modal[0]) { + this._setFocus('start'); + } + }, + + /** + * Escape key press handler, + * cancel and close modal window + * @param {Object} event - event + */ + escapeKey: function (event) { + if (this.options.isOpen && this.modal.find(document.activeElement).length || + this.options.isOpen && this.modal[0] === document.activeElement) { + this.closeModal(); + event.preventDefault(); + } + } + }, actions: { /** @@ -73,6 +111,7 @@ define([ _create: function () { this.options.focus = this.options.promptField; this.options.validation = this.options.validation && this.options.validationRules.length; + this.options.outerClickHandler = this.options.outerClickHandler || _.bind(this.closeModal, this, false); this._super(); this.modal.find(this.options.modalContent).append(this.getFormTemplate()); this.modal.find(this.options.modalCloseBtn).off().on('click', _.bind(this.closeModal, this, false)); diff --git a/app/code/Magento/Ui/view/base/web/templates/grid/columns/image.html b/app/code/Magento/Ui/view/base/web/templates/grid/columns/image.html index c513ddeff9895..fa0074ad72283 100644 --- a/app/code/Magento/Ui/view/base/web/templates/grid/columns/image.html +++ b/app/code/Magento/Ui/view/base/web/templates/grid/columns/image.html @@ -4,6 +4,6 @@ * See COPYING.txt for license details. */ --> -<div class="masonry-image-block" ko-style="$col.getStyles($row())" attr="'data-id': $col.getId($row())"> +<div class="masonry-image-block" ko-style="$col.getStyles($row())" css="{'active': $col.getIsActive($row())}" attr="'data-id': $col.getId($row())"> <img attr="src: $col.getUrl($row())" css="$col.getClasses($row())" click="function(){ expandPreview($row()) }" data-role="thumbnail"/> </div> diff --git a/app/code/Magento/Ui/view/base/web/templates/grid/controls/bookmarks/bookmarks.html b/app/code/Magento/Ui/view/base/web/templates/grid/controls/bookmarks/bookmarks.html index 36a3232c3e61a..6d50ed7e5bd03 100644 --- a/app/code/Magento/Ui/view/base/web/templates/grid/controls/bookmarks/bookmarks.html +++ b/app/code/Magento/Ui/view/base/web/templates/grid/controls/bookmarks/bookmarks.html @@ -9,9 +9,9 @@ <span class="admin__action-dropdown-text" translate="activeView.label"/> </button> <ul class="admin__action-dropdown-menu"> - <repeat args="foreach: viewsArray, item: '$view'"> - <li css="_edit: isEditing($view().index)" outerClick="endEdit.bind($data, $view().index)" template="viewTmpl"/> - </repeat> + <!-- ko foreach: { data: viewsArray, as: '$view'} --> + <li css="_edit: $parent.isEditing($view.index)" outerClick="$parent.endEdit.bind($parent, $view.index)" template="$parent.viewTmpl"/> + <!-- /ko --> <li visible="hasChanges" outerClick="hideCustom.bind($data)" css=" _edit: customVisible, diff --git a/app/code/Magento/Ui/view/base/web/templates/grid/controls/bookmarks/view.html b/app/code/Magento/Ui/view/base/web/templates/grid/controls/bookmarks/view.html index 521ce9fc806ac..1262fce544599 100644 --- a/app/code/Magento/Ui/view/base/web/templates/grid/controls/bookmarks/view.html +++ b/app/code/Magento/Ui/view/base/web/templates/grid/controls/bookmarks/view.html @@ -4,36 +4,36 @@ * See COPYING.txt for license details. */ --> -<div class="action-dropdown-menu-item-edit" if="$view().editable"> +<div class="action-dropdown-menu-item-edit" if="$view.editable"> <input class="admin__control-text" data-bind=" - value: $view().value, - hasFocus: isEditing($view().index), + value: $view.value, + hasFocus: $parent.isEditing($view.index), autoselect, attr: { - placeholder: $view().label + placeholder: $view.label }, keyboard: { - 13: updateAndSave.bind($data, $view().index), - 27: endEdit.bind($data, $view().index) + 13: $parent.updateAndSave.bind($parent, $view.index), + 27: $parent.endEdit.bind($parent, $view.index) }" type="text"> - <button class="action-submit" type="button" attr="title: $t('Save all changes')" click="updateAndSave.bind($data, $view().index)"> + <button class="action-submit" type="button" attr="title: $t('Save all changes')" click="$parent.updateAndSave.bind($parent, $view.index)"> <span translate="'Submit'"/> </button> <div class="action-dropdown-menu-item-actions"> - <button class="action-delete" type="button" attr="title: $t('Delete bookmark')" click="removeView.bind($data, $view().index)"> + <button class="action-delete" type="button" attr="title: $t('Delete bookmark')" click="$parent.removeView.bind($parent, $view.index)"> <span translate="'Delete'"/> </button> </div> </div> <div class="action-dropdown-menu-item"> - <a href="" class="action-dropdown-menu-link" translate="$view().label" click="applyView.bind($data, $view().index)" closeCollapsible/> + <a href="" class="action-dropdown-menu-link" translate="$view.label" click="$parent.applyView.bind($parent, $view.index)" closeCollapsible/> - <div class="action-dropdown-menu-item-actions" if="$view().editable"> - <button class="action-edit" type="button" attr="title: $t('Edit bookmark')" click="editView.bind($data, $view().index)"> + <div class="action-dropdown-menu-item-actions" if="$view.editable"> + <button class="action-edit" type="button" attr="title: $t('Edit bookmark')" click="$parent.editView.bind($parent, $view.index)"> <span translate="'Edit'"/> </button> </div> diff --git a/app/code/Magento/Ui/view/base/web/templates/grid/sortBy.html b/app/code/Magento/Ui/view/base/web/templates/grid/sortBy.html new file mode 100644 index 0000000000000..104c92bf784f0 --- /dev/null +++ b/app/code/Magento/Ui/view/base/web/templates/grid/sortBy.html @@ -0,0 +1,15 @@ +<!-- +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +--> +<div if="isVisible" class="masonry-image-sortby"> + <b><!-- ko i18n: 'Sort by' --><!-- /ko -->:</b> + <select class="admin__control-select" data-bind=" + options: options, + optionsValue: 'value', + optionsText: 'label', + value: selectedOption + "></select> +</div> diff --git a/app/code/Magento/Ui/view/base/web/templates/modal/modal-custom.html b/app/code/Magento/Ui/view/base/web/templates/modal/modal-custom.html index 5e7fd11dbf693..7d7d4b6ae88cb 100644 --- a/app/code/Magento/Ui/view/base/web/templates/modal/modal-custom.html +++ b/app/code/Magento/Ui/view/base/web/templates/modal/modal-custom.html @@ -27,7 +27,7 @@ <h1 id="modal-title-<%- data.id %>" class="modal-title" <% if(data.subTitle){ %> <span class="modal-subtitle" - data-role="subtitle"> + data-role="subTitle"> <%= data.subTitle %> </span> <% } %> diff --git a/app/code/Magento/Ui/view/base/web/templates/modal/modal-popup.html b/app/code/Magento/Ui/view/base/web/templates/modal/modal-popup.html index 53661ed8df87f..08376964eee63 100644 --- a/app/code/Magento/Ui/view/base/web/templates/modal/modal-popup.html +++ b/app/code/Magento/Ui/view/base/web/templates/modal/modal-popup.html @@ -27,7 +27,7 @@ <h1 id="modal-title-<%- data.id %>" class="modal-title" <% if(data.subTitle){ %> <span class="modal-subtitle" - data-role="subtitle"> + data-role="subTitle"> <%= data.subTitle %> </span> <% } %> diff --git a/app/code/Magento/Ui/view/frontend/web/js/model/messages.js b/app/code/Magento/Ui/view/frontend/web/js/model/messages.js index fb9a20c054da2..f0680fe6eaae5 100644 --- a/app/code/Magento/Ui/view/frontend/web/js/model/messages.js +++ b/app/code/Magento/Ui/view/frontend/web/js/model/messages.js @@ -48,6 +48,10 @@ define([ message = messageObj.message.replace(expr, function (varName) { varName = varName.substr(1); + if (!isNaN(varName)) { + varName--; + } + if (messageObj.parameters.hasOwnProperty(varName)) { return messageObj.parameters[varName]; } diff --git a/app/code/Magento/Ui/view/frontend/web/templates/form/field.html b/app/code/Magento/Ui/view/frontend/web/templates/form/field.html index cbd3242b451b4..8ef49e307747b 100644 --- a/app/code/Magento/Ui/view/frontend/web/templates/form/field.html +++ b/app/code/Magento/Ui/view/frontend/web/templates/form/field.html @@ -6,11 +6,7 @@ --> <div class="field" data-bind="visible: visible, attr: {'name': element.dataScope}, css: additionalClasses"> - <label class="label" data-bind="attr: { for: element.uid }"> - <!-- ko if: element.label --> - <span translate="element.label"></span> - <!-- /ko --> - </label> + <label class="label" data-bind="attr: { for: element.uid }"><!-- ko if: element.label --><span translate="element.label"></span><!-- /ko --></label> <div class="control" data-bind="css: {'_with-tooltip': element.tooltip}"> <!-- ko ifnot: element.hasAddons() --> diff --git a/app/code/Magento/Ups/Model/Carrier.php b/app/code/Magento/Ups/Model/Carrier.php index 9e33b86ea8215..5b333e2dc2d5c 100644 --- a/app/code/Magento/Ups/Model/Carrier.php +++ b/app/code/Magento/Ups/Model/Carrier.php @@ -7,6 +7,12 @@ namespace Magento\Ups\Model; +use Magento\CatalogInventory\Api\StockRegistryInterface; +use Magento\Directory\Helper\Data; +use Magento\Directory\Model\CountryFactory; +use Magento\Directory\Model\CurrencyFactory; +use Magento\Directory\Model\RegionFactory; +use Magento\Framework\App\Config\ScopeConfigInterface; use Magento\Framework\App\ObjectManager; use Magento\Framework\Async\CallbackDeferred; use Magento\Framework\DataObject; @@ -15,19 +21,34 @@ use Magento\Framework\HTTP\AsyncClient\Request; use Magento\Framework\HTTP\AsyncClientInterface; use Magento\Framework\HTTP\ClientFactory; +use Magento\Framework\Locale\FormatInterface; use Magento\Framework\Xml\Security; use Magento\Quote\Model\Quote\Address\RateRequest; use Magento\Quote\Model\Quote\Address\RateResult\Error; +use Magento\Quote\Model\Quote\Address\RateResult\ErrorFactory as RateErrorFactory; +use Magento\Quote\Model\Quote\Address\RateResult\MethodFactory as RateMethodFactory; +use Magento\Sales\Model\Order\Shipment as OrderShipment; use Magento\Shipping\Model\Carrier\AbstractCarrierOnline; use Magento\Shipping\Model\Carrier\CarrierInterface; use Magento\Shipping\Model\Rate\Result; use Magento\Shipping\Model\Rate\Result\ProxyDeferredFactory; +use Magento\Shipping\Model\Rate\ResultFactory as RateFactory; use Magento\Shipping\Model\Simplexml\Element; +use Magento\Shipping\Model\Simplexml\ElementFactory; +use Magento\Shipping\Model\Tracking\Result\ErrorFactory as TrackErrorFactory; +use Magento\Shipping\Model\Tracking\Result\StatusFactory as TrackStatusFactory; +use Magento\Shipping\Model\Tracking\ResultFactory as TrackFactory; +use Magento\Store\Model\ScopeInterface; use Magento\Ups\Helper\Config; use Magento\Shipping\Model\Shipment\Request as Shipment; +use Psr\Log\LoggerInterface; +use RuntimeException; +use Throwable; +use Zend_Http_Client; /** - * UPS shipping implementation + * UPS shipping implementation. + * * @SuppressWarnings(PHPMD.ExcessiveClassComplexity) * @SuppressWarnings(PHPMD.CouplingBetweenObjects) */ @@ -117,12 +138,12 @@ class Carrier extends AbstractCarrierOnline implements CarrierInterface protected $_customizableContainerTypes = ['CP', 'CSP']; /** - * @var \Magento\Framework\Locale\FormatInterface + * @var FormatInterface */ protected $_localeFormat; /** - * @var \Psr\Log\LoggerInterface + * @var LoggerInterface */ protected $_logger; @@ -135,7 +156,9 @@ class Carrier extends AbstractCarrierOnline implements CarrierInterface * @inheritdoc */ protected $_debugReplacePrivateDataKeys = [ - 'UserId', 'Password', 'AccessLicenseNumber' + 'UserId', + 'Password', + 'AccessLicenseNumber', ]; /** @@ -149,22 +172,22 @@ class Carrier extends AbstractCarrierOnline implements CarrierInterface private $deferredProxyFactory; /** - * @param \Magento\Framework\App\Config\ScopeConfigInterface $scopeConfig - * @param \Magento\Quote\Model\Quote\Address\RateResult\ErrorFactory $rateErrorFactory - * @param \Psr\Log\LoggerInterface $logger + * @param ScopeConfigInterface $scopeConfig + * @param RateErrorFactory $rateErrorFactory + * @param LoggerInterface $logger * @param Security $xmlSecurity - * @param \Magento\Shipping\Model\Simplexml\ElementFactory $xmlElFactory - * @param \Magento\Shipping\Model\Rate\ResultFactory $rateFactory - * @param \Magento\Quote\Model\Quote\Address\RateResult\MethodFactory $rateMethodFactory - * @param \Magento\Shipping\Model\Tracking\ResultFactory $trackFactory - * @param \Magento\Shipping\Model\Tracking\Result\ErrorFactory $trackErrorFactory - * @param \Magento\Shipping\Model\Tracking\Result\StatusFactory $trackStatusFactory - * @param \Magento\Directory\Model\RegionFactory $regionFactory - * @param \Magento\Directory\Model\CountryFactory $countryFactory - * @param \Magento\Directory\Model\CurrencyFactory $currencyFactory - * @param \Magento\Directory\Helper\Data $directoryData - * @param \Magento\CatalogInventory\Api\StockRegistryInterface $stockRegistry - * @param \Magento\Framework\Locale\FormatInterface $localeFormat + * @param ElementFactory $xmlElFactory + * @param RateFactory $rateFactory + * @param RateMethodFactory $rateMethodFactory + * @param TrackFactory $trackFactory + * @param TrackErrorFactory $trackErrorFactory + * @param TrackStatusFactory $trackStatusFactory + * @param RegionFactory $regionFactory + * @param CountryFactory $countryFactory + * @param CurrencyFactory $currencyFactory + * @param Data $directoryData + * @param StockRegistryInterface $stockRegistry + * @param FormatInterface $localeFormat * @param Config $configHelper * @param ClientFactory $httpClientFactory * @param array $data @@ -175,27 +198,27 @@ class Carrier extends AbstractCarrierOnline implements CarrierInterface * @SuppressWarnings(PHPMD.UnusedFormalParameter) */ public function __construct( - \Magento\Framework\App\Config\ScopeConfigInterface $scopeConfig, - \Magento\Quote\Model\Quote\Address\RateResult\ErrorFactory $rateErrorFactory, - \Psr\Log\LoggerInterface $logger, + ScopeConfigInterface $scopeConfig, + RateErrorFactory $rateErrorFactory, + LoggerInterface $logger, Security $xmlSecurity, - \Magento\Shipping\Model\Simplexml\ElementFactory $xmlElFactory, - \Magento\Shipping\Model\Rate\ResultFactory $rateFactory, - \Magento\Quote\Model\Quote\Address\RateResult\MethodFactory $rateMethodFactory, - \Magento\Shipping\Model\Tracking\ResultFactory $trackFactory, - \Magento\Shipping\Model\Tracking\Result\ErrorFactory $trackErrorFactory, - \Magento\Shipping\Model\Tracking\Result\StatusFactory $trackStatusFactory, - \Magento\Directory\Model\RegionFactory $regionFactory, - \Magento\Directory\Model\CountryFactory $countryFactory, - \Magento\Directory\Model\CurrencyFactory $currencyFactory, - \Magento\Directory\Helper\Data $directoryData, - \Magento\CatalogInventory\Api\StockRegistryInterface $stockRegistry, - \Magento\Framework\Locale\FormatInterface $localeFormat, + ElementFactory $xmlElFactory, + RateFactory $rateFactory, + RateMethodFactory $rateMethodFactory, + TrackFactory $trackFactory, + TrackErrorFactory $trackErrorFactory, + TrackStatusFactory $trackStatusFactory, + RegionFactory $regionFactory, + CountryFactory $countryFactory, + CurrencyFactory $currencyFactory, + Data $directoryData, + StockRegistryInterface $stockRegistry, + FormatInterface $localeFormat, Config $configHelper, ClientFactory $httpClientFactory, array $data = [], ?AsyncClientInterface $asyncHttpClient = null, - ?ProxyDeferredFactory $proxyDeferredFactory + ?ProxyDeferredFactory $proxyDeferredFactory = null ) { parent::__construct( $scopeConfig, @@ -265,7 +288,7 @@ public function setRequest(RateRequest $request) { $this->_request = $request; - $rowRequest = new \Magento\Framework\DataObject(); + $rowRequest = new DataObject(); if ($request->getLimitMethod()) { $rowRequest->setAction($this->configHelper->getCode('action', 'single')); @@ -300,8 +323,8 @@ public function setRequest(RateRequest $request) $origCountry = $request->getOrigCountry(); } else { $origCountry = $this->_scopeConfig->getValue( - \Magento\Sales\Model\Order\Shipment::XML_PATH_STORE_COUNTRY_ID, - \Magento\Store\Model\ScopeInterface::SCOPE_STORE, + OrderShipment::XML_PATH_STORE_COUNTRY_ID, + ScopeInterface::SCOPE_STORE, $request->getStoreId() ); } @@ -312,8 +335,8 @@ public function setRequest(RateRequest $request) $origRegionCode = $request->getOrigRegionCode(); } else { $origRegionCode = $this->_scopeConfig->getValue( - \Magento\Sales\Model\Order\Shipment::XML_PATH_STORE_REGION_ID, - \Magento\Store\Model\ScopeInterface::SCOPE_STORE, + OrderShipment::XML_PATH_STORE_REGION_ID, + ScopeInterface::SCOPE_STORE, $request->getStoreId() ); } @@ -327,8 +350,8 @@ public function setRequest(RateRequest $request) } else { $rowRequest->setOrigPostal( $this->_scopeConfig->getValue( - \Magento\Sales\Model\Order\Shipment::XML_PATH_STORE_ZIP, - \Magento\Store\Model\ScopeInterface::SCOPE_STORE, + OrderShipment::XML_PATH_STORE_ZIP, + ScopeInterface::SCOPE_STORE, $request->getStoreId() ) ); @@ -339,8 +362,8 @@ public function setRequest(RateRequest $request) } else { $rowRequest->setOrigCity( $this->_scopeConfig->getValue( - \Magento\Sales\Model\Order\Shipment::XML_PATH_STORE_CITY, - \Magento\Store\Model\ScopeInterface::SCOPE_STORE, + OrderShipment::XML_PATH_STORE_CITY, + ScopeInterface::SCOPE_STORE, $request->getStoreId() ) ); @@ -352,9 +375,10 @@ public function setRequest(RateRequest $request) $destCountry = self::USA_COUNTRY_ID; } - //for UPS, puero rico state for US will assume as puerto rico country - if ($destCountry == self::USA_COUNTRY_ID && ($request->getDestPostcode() == '00912' || - $request->getDestRegionCode() == self::PUERTORICO_COUNTRY_ID) + //for UPS, puerto rico state for US will assume as puerto rico country + if ($destCountry == self::USA_COUNTRY_ID + && ($request->getDestPostcode() == '00912' + || $request->getDestRegionCode() == self::PUERTORICO_COUNTRY_ID) ) { $destCountry = self::PUERTORICO_COUNTRY_ID; } @@ -516,7 +540,7 @@ protected function _getCgiQuotes() if (!$url) { $url = $this->_defaultCgiGatewayUrl; } - $client = new \Zend_Http_Client(); + $client = new Zend_Http_Client(); $client->setUri($url); $client->setConfig(['maxredirects' => 0, 'timeout' => 30]); $client->setParameterGet($params); @@ -525,7 +549,7 @@ protected function _getCgiQuotes() $debugData['result'] = $responseBody; $this->_setCachedQuotes($params, $responseBody); - } catch (\Throwable $e) { + } catch (Throwable $e) { $debugData['result'] = ['error' => $e->getMessage(), 'code' => $e->getCode()]; $responseBody = ''; } @@ -727,7 +751,7 @@ protected function _getXmlQuotes() <StateProvinceCode>{$shipperStateProvince}</StateProvinceCode> </Address> </Shipper> - + <ShipTo> <Address> <PostalCode>{$params['19_destPostal']}</PostalCode> @@ -743,7 +767,7 @@ protected function _getXmlQuotes() $xmlParams .= <<<XMLRequest </Address> </ShipTo> - + <ShipFrom> <Address> <PostalCode>{$params['15_origPostal']}</PostalCode> @@ -832,7 +856,7 @@ private function mapCurrencyCode($code) 'CNH' => 'CNY' ]; - return isset($currencyMapping[$code]) ? $currencyMapping[$code] : $code; + return $currencyMapping[$code] ?? $code; } /** @@ -1056,7 +1080,7 @@ protected function setXMLAccessRequest() * Get cgi tracking * * @param string[] $trackings - * @return \Magento\Shipping\Model\Tracking\ResultFactory + * @return TrackFactory */ protected function _getCgiTracking($trackings) { @@ -1302,32 +1326,40 @@ public function getResponse() } /** - * Get allowed shipping methods + * Get allowed shipping methods. * * @return array */ public function getAllowedMethods() { - $allowed = explode(',', $this->getConfigData('allowed_methods')); - $arr = []; - $isByCode = $this->getConfigData('type') == 'UPS_XML'; - foreach ($allowed as $code) { - $arr[$code] = $isByCode ? $this->getShipmentByCode($code) : $this->configHelper->getCode('method', $code); + $allowedMethods = explode(',', (string)$this->getConfigData('allowed_methods')); + $isUpsXml = $this->getConfigData('type') === 'UPS_XML'; + $origin = $this->getConfigData('origin_shipment'); + + $availableByTypeMethods = $isUpsXml + ? $this->configHelper->getCode('originShipment', $origin) + : $this->configHelper->getCode('method'); + + $methods = []; + foreach ($availableByTypeMethods as $methodCode => $methodData) { + if (in_array($methodCode, $allowedMethods)) { + $methods[$methodCode] = $methodData->getText(); + } } - return $arr; + return $methods; } /** * Form XML for shipment request * - * @param \Magento\Framework\DataObject $request + * @param DataObject $request * @return string * @SuppressWarnings(PHPMD.CyclomaticComplexity) * @SuppressWarnings(PHPMD.NPathComplexity) * @SuppressWarnings(PHPMD.ExcessiveMethodLength) */ - protected function _formShipmentRequest(\Magento\Framework\DataObject $request) + protected function _formShipmentRequest(DataObject $request) { $packageParams = $request->getPackageParams(); $height = $packageParams->getHeight(); @@ -1339,7 +1371,7 @@ protected function _formShipmentRequest(\Magento\Framework\DataObject $request) $itemsDesc = []; $itemsShipment = $request->getPackageItems(); foreach ($itemsShipment as $itemShipment) { - $item = new \Magento\Framework\DataObject(); + $item = new DataObject(); $item->setData($itemShipment); $itemsDesc[] = $item->getName(); } @@ -1533,7 +1565,7 @@ protected function _formShipmentRequest(\Magento\Framework\DataObject $request) * Send and process shipment accept request * * @param Element $shipmentConfirmResponse - * @return \Magento\Framework\DataObject + * @return DataObject * @deprecated New asynchronous methods introduced. * @see requestToShipment */ @@ -1559,18 +1591,18 @@ protected function _sendShipmentAcceptRequest(Element $shipmentConfirmResponse) $xmlResponse = $deferredResponse->get()->getBody(); $debugData['result'] = $xmlResponse; $this->_setCachedQuotes($xmlRequest, $xmlResponse); - } catch (\Throwable $e) { + } catch (Throwable $e) { $debugData['result'] = ['error' => $e->getMessage(), 'code' => $e->getCode()]; $xmlResponse = ''; } try { $response = $this->_xmlElFactory->create(['data' => $xmlResponse]); - } catch (\Throwable $e) { + } catch (Throwable $e) { $debugData['result'] = ['error' => $e->getMessage(), 'code' => $e->getCode()]; } - $result = new \Magento\Framework\DataObject(); + $result = new DataObject(); if (isset($response->Error)) { $result->setErrors((string)$response->Error->ErrorDescription); } else { @@ -1609,7 +1641,7 @@ public function getShipAcceptUrl() * @param DataObject[] $packages * @return string[] Quote IDs. * @throws LocalizedException - * @throws \RuntimeException + * @throws RuntimeException */ private function requestQuotes(array $packages): array { @@ -1640,13 +1672,13 @@ private function requestQuotes(array $packages): array try { /** @var Element $response */ $response = $this->_xmlElFactory->create(['data' => $httpResponse->getBody()]); - } catch (\Throwable $e) { - throw new \RuntimeException($e->getMessage()); + } catch (Throwable $e) { + throw new RuntimeException($e->getMessage()); } if (isset($response->Response->Error) && in_array($response->Response->Error->ErrorSeverity, ['Hard', 'Transient']) ) { - throw new \RuntimeException((string)$response->Response->Error->ErrorDescription); + throw new RuntimeException((string)$response->Response->Error->ErrorDescription); } $ids[] = $response->ShipmentDigest; @@ -1661,7 +1693,7 @@ private function requestQuotes(array $packages): array * @param string[] $quoteIds * @return DataObject[] * @throws LocalizedException - * @throws \RuntimeException + * @throws RuntimeException */ private function requestShipments(array $quoteIds): array { @@ -1697,11 +1729,11 @@ private function requestShipments(array $quoteIds): array try { /** @var Element $response */ $response = $this->_xmlElFactory->create(['data' => $httpResponse->getBody()]); - } catch (\Throwable $e) { - throw new \RuntimeException($e->getMessage()); + } catch (Throwable $e) { + throw new RuntimeException($e->getMessage()); } if (isset($response->Error)) { - throw new \RuntimeException((string)$response->Error->ErrorDescription); + throw new RuntimeException((string)$response->Error->ErrorDescription); } else { $shippingLabelContent = (string)$response->ShipmentResults->PackageResults->LabelImage->GraphicImage; $trackingNumber = (string)$response->ShipmentResults->PackageResults->TrackingNumber; @@ -1726,7 +1758,7 @@ private function requestShipments(array $quoteIds): array protected function _doShipmentRequest(DataObject $request) { $this->_prepareShipmentRequest($request); - $result = new \Magento\Framework\DataObject(); + $result = new DataObject(); $rawXmlRequest = $this->_formShipmentRequest($request); $this->setXMLAccessRequest(); $xmlRequest = $this->_xmlAccessRequest . $rawXmlRequest; @@ -1747,14 +1779,14 @@ protected function _doShipmentRequest(DataObject $request) $xmlResponse = $deferredResponse->get()->getBody(); $debugData['result'] = $xmlResponse; $this->_setCachedQuotes($xmlRequest, $xmlResponse); - } catch (\Throwable $e) { + } catch (Throwable $e) { $debugData['result'] = ['code' => $e->getCode(), 'error' => $e->getMessage()]; } } try { $response = $this->_xmlElFactory->create(['data' => $xmlResponse]); - } catch (\Throwable $e) { + } catch (Throwable $e) { $debugData['result'] = ['error' => $e->getMessage(), 'code' => $e->getCode()]; $result->setErrors($e->getMessage()); } @@ -1827,7 +1859,7 @@ public function requestToShipment($request) $labels = $this->requestShipments($quoteIds); } catch (LocalizedException $exception) { return new DataObject(['errors' => [$exception->getMessage()]]); - } catch (\RuntimeException $exception) { + } catch (RuntimeException $exception) { return new DataObject(['errors' => __('Failed to send items')]); } // phpcs:enable @@ -1848,11 +1880,11 @@ public function returnOfShipment($request) /** * Return container types of carrier * - * @param \Magento\Framework\DataObject|null $params + * @param DataObject|null $params * @return array|bool * @SuppressWarnings(PHPMD.CyclomaticComplexity) */ - public function getContainerTypes(\Magento\Framework\DataObject $params = null) + public function getContainerTypes(DataObject $params = null) { if ($params === null) { return $this->_getAllowedContainers($params); @@ -1876,20 +1908,18 @@ public function getContainerTypes(\Magento\Framework\DataObject $params = null) ]; } $containerTypes = $containerTypes + [ - '03' => __('UPS Tube'), - '04' => __('PAK'), - '2a' => __('Small Express Box'), - '2b' => __('Medium Express Box'), - '2c' => __('Large Express Box'), - ]; + '03' => __('UPS Tube'), + '04' => __('PAK'), + '2a' => __('Small Express Box'), + '2b' => __('Medium Express Box'), + '2c' => __('Large Express Box'), + ]; } return ['00' => __('Customer Packaging')] + $containerTypes; - } elseif ($countryShipper == self::USA_COUNTRY_ID && - $countryRecipient == self::PUERTORICO_COUNTRY_ID && - ($method == '03' || - $method == '02' || - $method == '01') + } elseif ($countryShipper == self::USA_COUNTRY_ID + && $countryRecipient == self::PUERTORICO_COUNTRY_ID + && in_array($method, ['01', '02', '03']) ) { // Container types should be the same as for domestic $params->setCountryRecipient(self::USA_COUNTRY_ID); @@ -1932,10 +1962,10 @@ public function getContainerTypesFilter() /** * Return delivery confirmation types of carrier * - * @param \Magento\Framework\DataObject|null $params + * @param DataObject|null $params * @return array|bool */ - public function getDeliveryConfirmationTypes(\Magento\Framework\DataObject $params = null) + public function getDeliveryConfirmationTypes(DataObject $params = null) { $countryRecipient = $params != null ? $params->getCountryRecipient() : null; $deliveryConfirmationTypes = []; diff --git a/app/code/Magento/Ups/Test/Mftf/Metadata/shipping_methods_ups_type_config-meta.xml b/app/code/Magento/Ups/Test/Mftf/Metadata/ShippingMethodsUpsTypeConfigMeta.xml similarity index 100% rename from app/code/Magento/Ups/Test/Mftf/Metadata/shipping_methods_ups_type_config-meta.xml rename to app/code/Magento/Ups/Test/Mftf/Metadata/ShippingMethodsUpsTypeConfigMeta.xml diff --git a/app/code/Magento/Ups/Test/Mftf/Test/AdminCheckInputFieldsDisabledAfterAppConfigDumpTest.xml b/app/code/Magento/Ups/Test/Mftf/Test/AdminCheckInputFieldsDisabledAfterAppConfigDumpTest.xml index 126586669afd2..f339d0a5b7028 100644 --- a/app/code/Magento/Ups/Test/Mftf/Test/AdminCheckInputFieldsDisabledAfterAppConfigDumpTest.xml +++ b/app/code/Magento/Ups/Test/Mftf/Test/AdminCheckInputFieldsDisabledAfterAppConfigDumpTest.xml @@ -15,52 +15,124 @@ <conditionalClick selector="{{AdminShippingMethodsUpsSection.carriersUpsTab}}" dependentSelector="{{AdminShippingMethodsUpsSection.carriersUPSActive}}" visible="false" stepKey="expandUPSTab"/> <waitForElementVisible selector="{{AdminShippingMethodsUpsSection.carriersUPSActive}}" stepKey="waitUPSTabOpen"/> <grabAttributeFrom selector="{{AdminShippingMethodsUpsSection.carriersUPSActive}}" userInput="disabled" stepKey="grabUPSActiveDisabled"/> - <assertEquals expected='true' expectedType="string" actual="$grabUPSActiveDisabled" stepKey="assertUPSActiveDisabled"/> + <assertEquals stepKey="assertUPSActiveDisabled"> + <actualResult type="const">$grabUPSActiveDisabled</actualResult> + <expectedResult type="string">true</expectedResult> + </assertEquals> <grabAttributeFrom selector="{{AdminShippingMethodsUpsSection.carriersUPSTypeSystem}}" userInput="disabled" stepKey="grabUPSTypeDisabled"/> - <assertEquals expected='true' expectedType="string" actual="$grabUPSTypeDisabled" stepKey="assertUPSTypeDisabled"/> + <assertEquals stepKey="assertUPSTypeDisabled"> + <actualResult type="const">$grabUPSTypeDisabled</actualResult> + <expectedResult type="string">true</expectedResult> + </assertEquals> <grabAttributeFrom selector="{{AdminShippingMethodsUpsSection.carriersUPSAccountLive}}" userInput="disabled" stepKey="grabUPSAccountLiveDisabled"/> - <assertEquals expected='true' expectedType="string" actual="$grabUPSAccountLiveDisabled" stepKey="assertUPSAccountLiveDisabled"/> + <assertEquals stepKey="assertUPSAccountLiveDisabled"> + <actualResult type="const">$grabUPSAccountLiveDisabled</actualResult> + <expectedResult type="string">true</expectedResult> + </assertEquals> <grabAttributeFrom selector="{{AdminShippingMethodsUpsSection.carriersUPSGatewayXMLUrl}}" userInput="disabled" stepKey="grabUPSGatewayXMLUrlDisabled"/> - <assertEquals expected='true' expectedType="string" actual="$grabUPSGatewayXMLUrlDisabled" stepKey="assertUPSGatewayXMLUrlDisabled"/> + <assertEquals stepKey="assertUPSGatewayXMLUrlDisabled"> + <actualResult type="const">$grabUPSGatewayXMLUrlDisabled</actualResult> + <expectedResult type="string">true</expectedResult> + </assertEquals> <grabAttributeFrom selector="{{AdminShippingMethodsUpsSection.carriersUPSModeXML}}" userInput="disabled" stepKey="grabUPSModeXMLDisabled"/> - <assertEquals expected='true' expectedType="string" actual="$grabUPSModeXMLDisabled" stepKey="assertUPSModeXMLDisabled"/> + <assertEquals stepKey="assertUPSModeXMLDisabled"> + <actualResult type="const">$grabUPSModeXMLDisabled</actualResult> + <expectedResult type="string">true</expectedResult> + </assertEquals> <grabAttributeFrom selector="{{AdminShippingMethodsUpsSection.carriersUPSOriginShipment}}" userInput="disabled" stepKey="grabUPSOriginShipmentDisabled"/> - <assertEquals expected='true' expectedType="string" actual="$grabUPSOriginShipmentDisabled" stepKey="assertUPSOriginShipmentDisabled"/> + <assertEquals stepKey="assertUPSOriginShipmentDisabled"> + <actualResult type="const">$grabUPSOriginShipmentDisabled</actualResult> + <expectedResult type="string">true</expectedResult> + </assertEquals> <grabAttributeFrom selector="{{AdminShippingMethodsUpsSection.carriersUPSTitle}}" userInput="disabled" stepKey="grabUPSTitleDisabled"/> - <assertEquals expected='true' expectedType="string" actual="$grabUPSTitleDisabled" stepKey="assertUPSTitleDisabled"/> + <assertEquals stepKey="assertUPSTitleDisabled"> + <actualResult type="const">$grabUPSTitleDisabled</actualResult> + <expectedResult type="string">true</expectedResult> + </assertEquals> <grabAttributeFrom selector="{{AdminShippingMethodsUpsSection.carriersUPSNegotiatedActive}}" userInput="disabled" stepKey="grabUPSNegotiatedActiveDisabled"/> - <assertEquals expected='true' expectedType="string" actual="$grabUPSNegotiatedActiveDisabled" stepKey="assertUPSNegotiatedActiveDisabled"/> + <assertEquals stepKey="assertUPSNegotiatedActiveDisabled"> + <actualResult type="const">$grabUPSNegotiatedActiveDisabled</actualResult> + <expectedResult type="string">true</expectedResult> + </assertEquals> <grabAttributeFrom selector="{{AdminShippingMethodsUpsSection.carriersUPSIncludeTaxes}}" userInput="disabled" stepKey="grabUPSIncludeTaxesDisabled"/> - <assertEquals expected='true' expectedType="string" actual="$grabUPSIncludeTaxesDisabled" stepKey="assertUPSIncludeTaxesDisabled"/> + <assertEquals stepKey="assertUPSIncludeTaxesDisabled"> + <actualResult type="const">$grabUPSIncludeTaxesDisabled</actualResult> + <expectedResult type="string">true</expectedResult> + </assertEquals> <grabAttributeFrom selector="{{AdminShippingMethodsUpsSection.carriersUPSShipmentRequestType}}" userInput="disabled" stepKey="grabUPSShipmentRequestTypeDisabled"/> - <assertEquals expected='true' expectedType="string" actual="$grabUPSShipmentRequestTypeDisabled" stepKey="assertUPSShipmentRequestTypeDisabled"/> + <assertEquals stepKey="assertUPSShipmentRequestTypeDisabled"> + <actualResult type="const">$grabUPSShipmentRequestTypeDisabled</actualResult> + <expectedResult type="string">true</expectedResult> + </assertEquals> <grabAttributeFrom selector="{{AdminShippingMethodsUpsSection.carriersUPSContainer}}" userInput="disabled" stepKey="grabUPSContainerDisabled"/> - <assertEquals expected='true' expectedType="string" actual="$grabUPSContainerDisabled" stepKey="assertUPSContainerDisabled"/> + <assertEquals stepKey="assertUPSContainerDisabled"> + <actualResult type="const">$grabUPSContainerDisabled</actualResult> + <expectedResult type="string">true</expectedResult> + </assertEquals> <grabAttributeFrom selector="{{AdminShippingMethodsUpsSection.carriersUPSDestType}}" userInput="disabled" stepKey="grabUPSDestTypeDisabled"/> - <assertEquals expected='true' expectedType="string" actual="$grabUPSDestTypeDisabled" stepKey="assertUPSDestTypeDisabled"/> + <assertEquals stepKey="assertUPSDestTypeDisabled"> + <actualResult type="const">$grabUPSDestTypeDisabled</actualResult> + <expectedResult type="string">true</expectedResult> + </assertEquals> <grabAttributeFrom selector="{{AdminShippingMethodsUpsSection.carriersUPSTrackingXmlUrl}}" userInput="disabled" stepKey="grabUPSTrackingXmlUrlDisabled"/> - <assertEquals expected='true' expectedType="string" actual="$grabUPSTrackingXmlUrlDisabled" stepKey="assertUPSTrackingXmlUrlDisabled"/> + <assertEquals stepKey="assertUPSTrackingXmlUrlDisabled"> + <actualResult type="const">$grabUPSTrackingXmlUrlDisabled</actualResult> + <expectedResult type="string">true</expectedResult> + </assertEquals> <grabAttributeFrom selector="{{AdminShippingMethodsUpsSection.carriersUPSUnitOfMeasure}}" userInput="disabled" stepKey="grabUPSUnitOfMeasureDisabled"/> - <assertEquals expected='true' expectedType="string" actual="$grabUPSUnitOfMeasureDisabled" stepKey="assertUPSUnitOfMeasureDisabled"/> + <assertEquals stepKey="assertUPSUnitOfMeasureDisabled"> + <actualResult type="const">$grabUPSUnitOfMeasureDisabled</actualResult> + <expectedResult type="string">true</expectedResult> + </assertEquals> <grabAttributeFrom selector="{{AdminShippingMethodsUpsSection.carriersUPSMaxPackageWeight}}" userInput="disabled" stepKey="grabUPSMaxPackageWeightDisabled"/> - <assertEquals expected='true' expectedType="string" actual="$grabUPSMaxPackageWeightDisabled" stepKey="assertUPSMaxPackageWeightDisabled"/> + <assertEquals stepKey="assertUPSMaxPackageWeightDisabled"> + <actualResult type="const">$grabUPSMaxPackageWeightDisabled</actualResult> + <expectedResult type="string">true</expectedResult> + </assertEquals> <grabAttributeFrom selector="{{AdminShippingMethodsUpsSection.carriersUPSPickup}}" userInput="disabled" stepKey="grabUPSPickupDisabled"/> - <assertEquals expected='true' expectedType="string" actual="$grabUPSPickupDisabled" stepKey="assertUPSPickupDisabled"/> + <assertEquals stepKey="assertUPSPickupDisabled"> + <actualResult type="const">$grabUPSPickupDisabled</actualResult> + <expectedResult type="string">true</expectedResult> + </assertEquals> <grabAttributeFrom selector="{{AdminShippingMethodsUpsSection.carriersUPSMinPackageWeight}}" userInput="disabled" stepKey="grabUPSMinPackageWeightDisabled"/> - <assertEquals expected='true' expectedType="string" actual="$grabUPSMinPackageWeightDisabled" stepKey="assertUPSMinPackageWeightDisabled"/> + <assertEquals stepKey="assertUPSMinPackageWeightDisabled"> + <actualResult type="const">$grabUPSMinPackageWeightDisabled</actualResult> + <expectedResult type="string">true</expectedResult> + </assertEquals> <grabAttributeFrom selector="{{AdminShippingMethodsUpsSection.carriersUPSHandlingType}}" userInput="disabled" stepKey="grabUPSHandlingTypeDisabled"/> - <assertEquals expected='true' expectedType="string" actual="$grabUPSHandlingTypeDisabled" stepKey="assertUPSHandlingTypeDisabled"/> + <assertEquals stepKey="assertUPSHandlingTypeDisabled"> + <actualResult type="const">$grabUPSHandlingTypeDisabled</actualResult> + <expectedResult type="string">true</expectedResult> + </assertEquals> <grabAttributeFrom selector="{{AdminShippingMethodsUpsSection.carriersUPSHandlingAction}}" userInput="disabled" stepKey="grabUPSHandlingActionDisabled"/> - <assertEquals expected='true' expectedType="string" actual="$grabUPSHandlingActionDisabled" stepKey="assertUPSHandlingActionDisabled"/> + <assertEquals stepKey="assertUPSHandlingActionDisabled"> + <actualResult type="const">$grabUPSHandlingActionDisabled</actualResult> + <expectedResult type="string">true</expectedResult> + </assertEquals> <grabAttributeFrom selector="{{AdminShippingMethodsUpsSection.carriersUPSAllowedMethods}}" userInput="disabled" stepKey="grabUPSAllowedMethodsDisabled"/> - <assertEquals expected='true' expectedType="string" actual="$grabUPSAllowedMethodsDisabled" stepKey="assertUPSAllowedMethodsDisabled"/> + <assertEquals stepKey="assertUPSAllowedMethodsDisabled"> + <actualResult type="const">$grabUPSAllowedMethodsDisabled</actualResult> + <expectedResult type="string">true</expectedResult> + </assertEquals> <grabAttributeFrom selector="{{AdminShippingMethodsUpsSection.carriersUPSFreeMethod}}" userInput="disabled" stepKey="grabUPSFreeMethodDisabled"/> - <assertEquals expected='true' expectedType="string" actual="$grabUPSFreeMethodDisabled" stepKey="assertUPSFreeMethodDisabled"/> + <assertEquals stepKey="assertUPSFreeMethodDisabled"> + <actualResult type="const">$grabUPSFreeMethodDisabled</actualResult> + <expectedResult type="string">true</expectedResult> + </assertEquals> <grabAttributeFrom selector="{{AdminShippingMethodsUpsSection.carriersUPSSpecificErrMsg}}" userInput="disabled" stepKey="grabUPSSpecificErrMsgDisabled"/> - <assertEquals expected='true' expectedType="string" actual="$grabUPSSpecificErrMsgDisabled" stepKey="assertUPSSpecificErrMsgDisabled"/> + <assertEquals stepKey="assertUPSSpecificErrMsgDisabled"> + <actualResult type="const">$grabUPSSpecificErrMsgDisabled</actualResult> + <expectedResult type="string">true</expectedResult> + </assertEquals> <grabAttributeFrom selector="{{AdminShippingMethodsUpsSection.carriersUPSAllowSpecific}}" userInput="disabled" stepKey="grabUPSAllowSpecificDisabled"/> - <assertEquals expected='true' expectedType="string" actual="$grabUPSAllowSpecificDisabled" stepKey="assertUPSAllowSpecificDisabled"/> + <assertEquals stepKey="assertUPSAllowSpecificDisabled"> + <actualResult type="const">$grabUPSAllowSpecificDisabled</actualResult> + <expectedResult type="string">true</expectedResult> + </assertEquals> <grabAttributeFrom selector="{{AdminShippingMethodsUpsSection.carriersUPSSpecificCountry}}" userInput="disabled" stepKey="grabUPSSpecificCountryDisabled"/> - <assertEquals expected='true' expectedType="string" actual="$grabUPSSpecificCountryDisabled" stepKey="assertUPSSpecificCountryDisabled"/> + <assertEquals stepKey="assertUPSSpecificCountryDisabled"> + <actualResult type="const">$grabUPSSpecificCountryDisabled</actualResult> + <expectedResult type="string">true</expectedResult> + </assertEquals> </test> </tests> diff --git a/app/code/Magento/Ups/Test/Mftf/Test/DefaultConfigForUPSTypeTest.xml b/app/code/Magento/Ups/Test/Mftf/Test/DefaultConfigForUPSTypeTest.xml index 9caa32609c487..58ac4ef53861c 100644 --- a/app/code/Magento/Ups/Test/Mftf/Test/DefaultConfigForUPSTypeTest.xml +++ b/app/code/Magento/Ups/Test/Mftf/Test/DefaultConfigForUPSTypeTest.xml @@ -20,13 +20,13 @@ <group value="ups"/> </annotations> <before> - <actionGroup ref="LoginAsAdmin" stepKey="loginAsAdmin"/> + <actionGroup ref="AdminLoginActionGroup" stepKey="loginAsAdmin"/> </before> <after> <!--Collapse UPS tab and logout--> <comment userInput="Collapse UPS tab and logout" stepKey="collapseTabAndLogout"/> <click selector="{{AdminShippingMethodsUpsSection.carriersUpsTab}}" stepKey="collapseTab"/> - <actionGroup ref="logout" stepKey="logout"/> + <actionGroup ref="AdminLogoutActionGroup" stepKey="logout"/> </after> <!-- Set shipping methods UPS type to default --> <comment userInput="Set shipping methods UPS type to default" stepKey="setToDefaultShippingMethodsUpsType"/> @@ -42,6 +42,9 @@ <!-- Assert that selected UPS type by default is 'United Parcel Service XML' --> <comment userInput="Check that selected UPS type by default is 'United Parcel Service XML'" stepKey="assertDefUpsType"/> <grabTextFrom selector="{{AdminShippingMethodsUpsSection.selectedUpsType}}" stepKey="grabSelectedOptionText"/> - <assertEquals expected='United Parcel Service XML' expectedType="string" actual="($grabSelectedOptionText)" stepKey="assertDefaultUpsType"/> + <assertEquals stepKey="assertDefaultUpsType"> + <actualResult type="const">($grabSelectedOptionText)</actualResult> + <expectedResult type="string">United Parcel Service XML</expectedResult> + </assertEquals> </test> </tests> diff --git a/app/code/Magento/Ups/Test/Unit/Model/CarrierTest.php b/app/code/Magento/Ups/Test/Unit/Model/CarrierTest.php index 0d33087a0f3bc..e0580629dd638 100644 --- a/app/code/Magento/Ups/Test/Unit/Model/CarrierTest.php +++ b/app/code/Magento/Ups/Test/Unit/Model/CarrierTest.php @@ -3,15 +3,16 @@ * Copyright © Magento, Inc. All rights reserved. * See COPYING.txt for license details. */ + namespace Magento\Ups\Test\Unit\Model; use Magento\Directory\Model\Country; use Magento\Directory\Model\CountryFactory; use Magento\Framework\App\Config\ScopeConfigInterface; -use Magento\Framework\DataObject; use Magento\Framework\HTTP\ClientFactory; use Magento\Framework\HTTP\ClientInterface; use Magento\Framework\Model\AbstractModel; +use Magento\Framework\Phrase; use Magento\Framework\TestFramework\Unit\Helper\ObjectManager; use Magento\Quote\Model\Quote\Address\RateRequest; use Magento\Quote\Model\Quote\Address\RateResult\Error; @@ -20,11 +21,15 @@ use Magento\Shipping\Model\Rate\ResultFactory; use Magento\Shipping\Model\Simplexml\Element; use Magento\Shipping\Model\Simplexml\ElementFactory; +use Magento\Store\Model\ScopeInterface; +use Magento\Ups\Helper\Config; use Magento\Ups\Model\Carrier; use PHPUnit_Framework_MockObject_MockObject as MockObject; use Psr\Log\LoggerInterface; /** + * Unit tests for \Magento\Ups\Model\Carrier class. + * * @SuppressWarnings(PHPMD.CouplingBetweenObjects) */ class CarrierTest extends \PHPUnit\Framework\TestCase @@ -92,17 +97,23 @@ class CarrierTest extends \PHPUnit\Framework\TestCase */ private $logger; + /** + * @var Config|MockObject + */ + private $configHelper; + + /** + * @inheritdoc + */ protected function setUp() { $this->helper = new ObjectManager($this); $this->scope = $this->getMockBuilder(ScopeConfigInterface::class) ->disableOriginalConstructor() + ->setMethods(['getValue', 'isSetFlag']) ->getMock(); - $this->scope->method('getValue') - ->willReturnCallback([$this, 'scopeConfigGetValue']); - $this->error = $this->getMockBuilder(Error::class) ->setMethods(['setCarrier', 'setCarrierTitle', 'setErrorMessage']) ->getMock(); @@ -143,6 +154,11 @@ protected function setUp() $this->logger = $this->getMockForAbstractClass(LoggerInterface::class); + $this->configHelper = $this->getMockBuilder(Config::class) + ->disableOriginalConstructor() + ->setMethods(['getCode']) + ->getMock(); + $this->model = $this->helper->getObject( Carrier::class, [ @@ -153,6 +169,7 @@ protected function setUp() 'xmlElFactory' => $xmlFactory, 'logger' => $this->logger, 'httpClientFactory' => $httpClientFactory, + 'configHelper' => $this->configHelper, ] ); } @@ -179,7 +196,7 @@ public function scopeConfigGetValue(string $path) 'carriers/ups/access_license_number' => 'acn', ]; - return isset($pathMap[$path]) ? $pathMap[$path] : null; + return $pathMap[$path] ?? null; } /** @@ -189,14 +206,17 @@ public function scopeConfigGetValue(string $path) * @param bool $freeShippingEnabled * @param int $requestSubtotal * @param int $expectedPrice + * @return void */ public function testGetMethodPrice( - $cost, - $shippingMethod, - $freeShippingEnabled, - $requestSubtotal, - $expectedPrice - ) { + int $cost, + string $shippingMethod, + bool $freeShippingEnabled, + int $requestSubtotal, + int $expectedPrice + ): void { + $this->scope->method('getValue') + ->willReturnCallback([$this, 'scopeConfigGetValue']); $path = 'carriers/' . $this->model->getCarrierCode() . '/'; $this->scope->method('isSetFlag') ->with($path . 'free_shipping_enable') @@ -244,8 +264,13 @@ public function getMethodPriceProvider() ]; } - public function testCollectRatesErrorMessage() + /** + * @return void + */ + public function testCollectRatesErrorMessage(): void { + $this->scope->method('getValue') + ->willReturnCallback([$this, 'scopeConfigGetValue']); $this->scope->method('isSetFlag') ->willReturn(false); @@ -373,6 +398,87 @@ public function countryDataProvider() ]; } + /** + * @dataProvider allowedMethodsDataProvider + * @param string $carrierType + * @param string $methodType + * @param string $methodCode + * @param string $methodTitle + * @param string $allowedMethods + * @param array $expectedMethods + * @return void + */ + public function testGetAllowedMethods( + string $carrierType, + string $methodType, + string $methodCode, + string $methodTitle, + string $allowedMethods, + array $expectedMethods + ): void { + $this->scope->method('getValue') + ->willReturnMap( + [ + [ + 'carriers/ups/allowed_methods', + ScopeInterface::SCOPE_STORE, + null, + $allowedMethods, + ], + [ + 'carriers/ups/type', + ScopeInterface::SCOPE_STORE, + null, + $carrierType, + ], + [ + 'carriers/ups/origin_shipment', + ScopeInterface::SCOPE_STORE, + null, + 'Shipments Originating in United States', + ], + ] + ); + $this->configHelper->method('getCode') + ->with($methodType) + ->willReturn([$methodCode => new Phrase($methodTitle)]); + $actualMethods = $this->model->getAllowedMethods(); + $this->assertEquals($expectedMethods, $actualMethods); + } + + /** + * @return array + */ + public function allowedMethodsDataProvider(): array + { + return [ + [ + 'UPS', + 'method', + '1DM', + 'Next Day Air Early AM', + '', + [], + ], + [ + 'UPS', + 'method', + '1DM', + 'Next Day Air Early AM', + '1DM,1DML,1DA', + ['1DM' => 'Next Day Air Early AM'], + ], + [ + 'UPS_XML', + 'originShipment', + '01', + 'UPS Next Day Air', + '01,02,03', + ['01' => 'UPS Next Day Air'], + ], + ]; + } + /** * Creates mock for XML factory. * diff --git a/app/code/Magento/Ups/etc/adminhtml/system.xml b/app/code/Magento/Ups/etc/adminhtml/system.xml index c6a2516e96170..3a1676d221977 100644 --- a/app/code/Magento/Ups/etc/adminhtml/system.xml +++ b/app/code/Magento/Ups/etc/adminhtml/system.xml @@ -10,147 +10,147 @@ <section id="carriers"> <group id="ups" translate="label" type="text" sortOrder="100" showInDefault="1" showInWebsite="1" showInStore="1"> <label>UPS</label> - <field id="access_license_number" translate="label" type="obscure" sortOrder="30" showInDefault="1" showInWebsite="1" showInStore="0"> + <field id="access_license_number" translate="label" type="obscure" sortOrder="30" showInDefault="1" showInWebsite="1"> <label>Access License Number</label> <backend_model>Magento\Config\Model\Config\Backend\Encrypted</backend_model> <depends> <field id="carriers/ups/active">1</field> </depends> </field> - <field id="active" translate="label" type="select" sortOrder="10" showInDefault="1" showInWebsite="1" showInStore="0" canRestore="1"> + <field id="active" translate="label" type="select" sortOrder="10" showInDefault="1" showInWebsite="1" canRestore="1"> <label>Enabled for Checkout</label> <source_model>Magento\Config\Model\Config\Source\Yesno</source_model> </field> - <field id="allowed_methods" translate="label" type="multiselect" sortOrder="170" showInDefault="1" showInWebsite="1" showInStore="0" canRestore="1"> + <field id="allowed_methods" translate="label" type="multiselect" sortOrder="170" showInDefault="1" showInWebsite="1" canRestore="1"> <label>Allowed Methods</label> <source_model>Magento\Ups\Model\Config\Source\Method</source_model> <can_be_empty>1</can_be_empty> </field> - <field id="shipment_requesttype" translate="label" type="select" sortOrder="47" showInDefault="1" showInWebsite="1" showInStore="0" canRestore="1"> + <field id="shipment_requesttype" translate="label" type="select" sortOrder="47" showInDefault="1" showInWebsite="1" canRestore="1"> <label>Packages Request Type</label> <source_model>Magento\Shipping\Model\Config\Source\Online\Requesttype</source_model> </field> - <field id="container" translate="label" type="select" sortOrder="50" showInDefault="1" showInWebsite="1" showInStore="0" canRestore="1"> + <field id="container" translate="label" type="select" sortOrder="50" showInDefault="1" showInWebsite="1" canRestore="1"> <label>Container</label> <source_model>Magento\Ups\Model\Config\Source\Container</source_model> </field> - <field id="free_shipping_enable" translate="label" type="select" sortOrder="210" showInDefault="1" showInWebsite="1" showInStore="0"> + <field id="free_shipping_enable" translate="label" type="select" sortOrder="210" showInDefault="1" showInWebsite="1"> <label>Enable Free Shipping Threshold</label> <source_model>Magento\Config\Model\Config\Source\Enabledisable</source_model> </field> - <field id="free_shipping_subtotal" translate="label" type="text" sortOrder="220" showInDefault="1" showInWebsite="1" showInStore="0"> + <field id="free_shipping_subtotal" translate="label" type="text" sortOrder="220" showInDefault="1" showInWebsite="1"> <label>Free Shipping Amount Threshold</label> <validate>validate-number validate-zero-or-greater</validate> <depends> <field id="free_shipping_enable">1</field> </depends> </field> - <field id="dest_type" translate="label" type="select" sortOrder="60" showInDefault="1" showInWebsite="1" showInStore="0" canRestore="1"> + <field id="dest_type" translate="label" type="select" sortOrder="60" showInDefault="1" showInWebsite="1" canRestore="1"> <label>Destination Type</label> <source_model>Magento\Ups\Model\Config\Source\DestType</source_model> </field> - <field id="free_method" translate="label" type="select" sortOrder="200" showInDefault="1" showInWebsite="1" showInStore="0" canRestore="1"> + <field id="free_method" translate="label" type="select" sortOrder="200" showInDefault="1" showInWebsite="1" canRestore="1"> <label>Free Method</label> <frontend_class>free-method</frontend_class> <source_model>Magento\Ups\Model\Config\Source\Freemethod</source_model> </field> - <field id="gateway_url" translate="label" type="text" sortOrder="40" showInDefault="1" showInWebsite="1" showInStore="0" canRestore="1"> + <field id="gateway_url" translate="label" type="text" sortOrder="40" showInDefault="1" showInWebsite="1" canRestore="1"> <label>Gateway URL</label> <backend_model>Magento\Ups\Model\Config\Backend\UpsUrl</backend_model> </field> - <field id="gateway_xml_url" translate="label" type="text" sortOrder="30" showInDefault="1" showInWebsite="1" showInStore="0" canRestore="1"> + <field id="gateway_xml_url" translate="label" type="text" sortOrder="30" showInDefault="1" showInWebsite="1" canRestore="1"> <label>Gateway XML URL</label> <backend_model>Magento\Ups\Model\Config\Backend\UpsUrl</backend_model> </field> - <field id="handling_type" translate="label" type="select" sortOrder="110" showInDefault="1" showInWebsite="1" showInStore="0" canRestore="1"> + <field id="handling_type" translate="label" type="select" sortOrder="110" showInDefault="1" showInWebsite="1" canRestore="1"> <label>Calculate Handling Fee</label> <source_model>Magento\Shipping\Model\Source\HandlingType</source_model> </field> - <field id="handling_action" translate="label" type="select" sortOrder="120" showInDefault="1" showInWebsite="1" showInStore="0" canRestore="1"> + <field id="handling_action" translate="label" type="select" sortOrder="120" showInDefault="1" showInWebsite="1" canRestore="1"> <label>Handling Applied</label> <source_model>Magento\Shipping\Model\Source\HandlingAction</source_model> </field> - <field id="handling_fee" translate="label" type="text" sortOrder="130" showInDefault="1" showInWebsite="1" showInStore="0"> + <field id="handling_fee" translate="label" type="text" sortOrder="130" showInDefault="1" showInWebsite="1"> <label>Handling Fee</label> <validate>validate-number validate-zero-or-greater</validate> </field> - <field id="max_package_weight" translate="label" type="text" sortOrder="80" showInDefault="1" showInWebsite="1" showInStore="0" canRestore="1"> + <field id="max_package_weight" translate="label" type="text" sortOrder="80" showInDefault="1" showInWebsite="1" canRestore="1"> <label>Maximum Package Weight (Please consult your shipping carrier for maximum supported shipping weight)</label> <validate>validate-number validate-zero-or-greater</validate> </field> - <field id="min_package_weight" translate="label" type="text" sortOrder="90" showInDefault="1" showInWebsite="1" showInStore="0" canRestore="1"> + <field id="min_package_weight" translate="label" type="text" sortOrder="90" showInDefault="1" showInWebsite="1" canRestore="1"> <label>Minimum Package Weight (Please consult your shipping carrier for minimum supported shipping weight)</label> <validate>validate-number validate-zero-or-greater</validate> </field> - <field id="origin_shipment" translate="label" type="select" sortOrder="30" showInDefault="1" showInWebsite="1" showInStore="0" canRestore="1"> + <field id="origin_shipment" translate="label" type="select" sortOrder="30" showInDefault="1" showInWebsite="1" canRestore="1"> <label>Origin of the Shipment</label> <source_model>Magento\Ups\Model\Config\Source\OriginShipment</source_model> </field> - <field id="password" translate="label" type="obscure" sortOrder="30" showInDefault="1" showInWebsite="1" showInStore="0"> + <field id="password" translate="label" type="obscure" sortOrder="30" showInDefault="1" showInWebsite="1"> <label>Password</label> <backend_model>Magento\Config\Model\Config\Backend\Encrypted</backend_model> <depends> <field id="carriers/ups/active">1</field> </depends> </field> - <field id="pickup" translate="label" type="select" sortOrder="80" showInDefault="1" showInWebsite="1" showInStore="0" canRestore="1"> + <field id="pickup" translate="label" type="select" sortOrder="80" showInDefault="1" showInWebsite="1" canRestore="1"> <label>Pickup Method</label> <source_model>Magento\Ups\Model\Config\Source\Pickup</source_model> </field> - <field id="sort_order" translate="label" type="text" sortOrder="1000" showInDefault="1" showInWebsite="1" showInStore="0"> + <field id="sort_order" translate="label" type="text" sortOrder="1000" showInDefault="1" showInWebsite="1"> <label>Sort Order</label> <validate>validate-number validate-zero-or-greater</validate> </field> <field id="title" translate="label" type="text" sortOrder="40" showInDefault="1" showInWebsite="1" showInStore="1" canRestore="1"> <label>Title</label> </field> - <field id="tracking_xml_url" translate="label" type="text" sortOrder="60" showInDefault="1" showInWebsite="1" showInStore="0" canRestore="1"> + <field id="tracking_xml_url" translate="label" type="text" sortOrder="60" showInDefault="1" showInWebsite="1" canRestore="1"> <label>Tracking XML URL</label> <backend_model>Magento\Ups\Model\Config\Backend\UpsUrl</backend_model> </field> - <field id="type" translate="label" type="select" sortOrder="20" showInDefault="1" showInWebsite="1" showInStore="0" canRestore="1"> + <field id="type" translate="label" type="select" sortOrder="20" showInDefault="1" showInWebsite="1" canRestore="1"> <label>UPS Type</label> <source_model>Magento\Ups\Model\Config\Source\Type</source_model> </field> - <field id="is_account_live" translate="label" type="select" sortOrder="25" showInDefault="1" showInWebsite="1" showInStore="0" canRestore="1"> + <field id="is_account_live" translate="label" type="select" sortOrder="25" showInDefault="1" showInWebsite="1" canRestore="1"> <label>Live Account</label> <source_model>Magento\Config\Model\Config\Source\Yesno</source_model> </field> - <field id="unit_of_measure" translate="label" type="select" sortOrder="60" showInDefault="1" showInWebsite="1" showInStore="0" canRestore="1"> + <field id="unit_of_measure" translate="label" type="select" sortOrder="60" showInDefault="1" showInWebsite="1" canRestore="1"> <label>Weight Unit</label> <source_model>Magento\Ups\Model\Config\Source\Unitofmeasure</source_model> </field> - <field id="username" translate="label" type="obscure" sortOrder="30" showInDefault="1" showInWebsite="1" showInStore="0"> + <field id="username" translate="label" type="obscure" sortOrder="30" showInDefault="1" showInWebsite="1"> <label>User ID</label> <backend_model>Magento\Config\Model\Config\Backend\Encrypted</backend_model> <depends> <field id="carriers/ups/active">1</field> </depends> </field> - <field id="negotiated_active" translate="label" type="select" sortOrder="40" showInDefault="1" showInWebsite="1" showInStore="0" canRestore="1"> + <field id="negotiated_active" translate="label" type="select" sortOrder="40" showInDefault="1" showInWebsite="1" canRestore="1"> <label>Enable Negotiated Rates</label> <source_model>Magento\Config\Model\Config\Source\Yesno</source_model> </field> - <field id="include_taxes" translate="label" type="select" sortOrder="45" showInDefault="1" showInWebsite="1" showInStore="0" canRestore="1"> + <field id="include_taxes" translate="label" type="select" sortOrder="45" showInDefault="1" showInWebsite="1" canRestore="1"> <label>Request Tax-Inclusive Rate</label> <source_model>Magento\Config\Model\Config\Source\Yesno</source_model> <comment>When applicable, taxes (sales tax, VAT etc.) are included in the rate.</comment> </field> - <field id="shipper_number" translate="label comment" type="text" sortOrder="50" showInDefault="1" showInWebsite="1" showInStore="0"> + <field id="shipper_number" translate="label comment" type="text" sortOrder="50" showInDefault="1" showInWebsite="1"> <label>Shipper Number</label> <comment>Required for negotiated rates; 6-character UPS</comment> </field> - <field id="sallowspecific" translate="label" type="select" sortOrder="900" showInDefault="1" showInWebsite="1" showInStore="0" canRestore="1"> + <field id="sallowspecific" translate="label" type="select" sortOrder="900" showInDefault="1" showInWebsite="1" canRestore="1"> <label>Ship to Applicable Countries</label> <frontend_class>shipping-applicable-country</frontend_class> <source_model>Magento\Shipping\Model\Config\Source\Allspecificcountries</source_model> </field> - <field id="specificcountry" translate="label" type="multiselect" sortOrder="910" showInDefault="1" showInWebsite="1" showInStore="0"> + <field id="specificcountry" translate="label" type="multiselect" sortOrder="910" showInDefault="1" showInWebsite="1"> <label>Ship to Specific Countries</label> <source_model>Magento\Directory\Model\Config\Source\Country</source_model> <can_be_empty>1</can_be_empty> </field> - <field id="showmethod" translate="label" type="select" sortOrder="920" showInDefault="1" showInWebsite="1" showInStore="0"> + <field id="showmethod" translate="label" type="select" sortOrder="920" showInDefault="1" showInWebsite="1"> <label>Show Method if Not Applicable</label> <frontend_class>shipping-skip-hide</frontend_class> <source_model>Magento\Config\Model\Config\Source\Yesno</source_model> @@ -158,12 +158,12 @@ <field id="specificerrmsg" translate="label" type="textarea" sortOrder="800" showInDefault="1" showInWebsite="1" showInStore="1" canRestore="1"> <label>Displayed Error Message</label> </field> - <field id="mode_xml" translate="label comment" type="select" sortOrder="30" showInDefault="1" showInWebsite="1" showInStore="0" canRestore="1"> + <field id="mode_xml" translate="label comment" type="select" sortOrder="30" showInDefault="1" showInWebsite="1" canRestore="1"> <label>Mode</label> <comment>This enables or disables SSL verification of the Magento server by UPS.</comment> <source_model>Magento\Shipping\Model\Config\Source\Online\Mode</source_model> </field> - <field id="debug" translate="label" type="select" sortOrder="920" showInDefault="1" showInWebsite="1" showInStore="0"> + <field id="debug" translate="label" type="select" sortOrder="920" showInDefault="1" showInWebsite="1"> <label>Debug</label> <source_model>Magento\Config\Model\Config\Source\Yesno</source_model> </field> diff --git a/app/code/Magento/Ups/etc/module.xml b/app/code/Magento/Ups/etc/module.xml index cc4599627ffb9..1c449f6b1cdb5 100644 --- a/app/code/Magento/Ups/etc/module.xml +++ b/app/code/Magento/Ups/etc/module.xml @@ -6,6 +6,5 @@ */ --> <config xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="urn:magento:framework:Module/etc/module.xsd"> - <module name="Magento_Ups" > - </module> + <module name="Magento_Ups"/> </config> diff --git a/app/code/Magento/Ups/registration.php b/app/code/Magento/Ups/registration.php index 4c87e21b00ac7..c9d7f2b58c86f 100644 --- a/app/code/Magento/Ups/registration.php +++ b/app/code/Magento/Ups/registration.php @@ -4,6 +4,6 @@ * See COPYING.txt for license details. */ -use \Magento\Framework\Component\ComponentRegistrar; +use Magento\Framework\Component\ComponentRegistrar; ComponentRegistrar::register(ComponentRegistrar::MODULE, 'Magento_Ups', __DIR__); diff --git a/app/code/Magento/UrlRewrite/Block/Edit/Form.php b/app/code/Magento/UrlRewrite/Block/Edit/Form.php index 65312c9cc01b8..2bc92ecf78ae1 100644 --- a/app/code/Magento/UrlRewrite/Block/Edit/Form.php +++ b/app/code/Magento/UrlRewrite/Block/Edit/Form.php @@ -3,6 +3,8 @@ * Copyright © Magento, Inc. All rights reserved. * See COPYING.txt for license details. */ +declare(strict_types=1); + namespace Magento\UrlRewrite\Block\Edit; /** @@ -95,6 +97,7 @@ protected function _construct() /** * Initialize form values + * * Set form data either from model values or from session * * @return $this @@ -194,7 +197,7 @@ protected function _prepareForm() 'label' => __('Redirect Type'), 'title' => __('Redirect Type'), 'name' => 'redirect_type', - 'options' => $this->optionProvider->toOptionArray(), + 'options' => $this->optionProvider->getOptions(), 'value' => $this->_formValues['redirect_type'] ] ); diff --git a/app/code/Magento/UrlRewrite/Block/GridContainer.php b/app/code/Magento/UrlRewrite/Block/GridContainer.php index b95b724fa05cc..1a3a6e89fa854 100644 --- a/app/code/Magento/UrlRewrite/Block/GridContainer.php +++ b/app/code/Magento/UrlRewrite/Block/GridContainer.php @@ -3,10 +3,14 @@ * Copyright © Magento, Inc. All rights reserved. * See COPYING.txt for license details. */ + namespace Magento\UrlRewrite\Block; /** + * Url rewrite grid container class + * * @api + * @deprecated Moved to UI component implementation * @since 100.0.2 */ class GridContainer extends \Magento\Backend\Block\Widget\Grid\Container diff --git a/app/code/Magento/UrlRewrite/Controller/Adminhtml/Url/Rewrite/InlineEdit.php b/app/code/Magento/UrlRewrite/Controller/Adminhtml/Url/Rewrite/InlineEdit.php new file mode 100644 index 0000000000000..589127792a478 --- /dev/null +++ b/app/code/Magento/UrlRewrite/Controller/Adminhtml/Url/Rewrite/InlineEdit.php @@ -0,0 +1,125 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +declare(strict_types=1); + +namespace Magento\UrlRewrite\Controller\Adminhtml\Url\Rewrite; + +use Magento\Backend\App\Action\Context; +use Magento\Framework\App\Action\HttpPostActionInterface; +use Magento\Framework\Controller\Result\Json; +use Magento\Framework\Controller\Result\JsonFactory; +use Magento\UrlRewrite\Controller\Adminhtml\Url\Rewrite; +use Magento\UrlRewrite\Model\ResourceModel\UrlRewriteFactory as UrlRewriteFactoryAlias; +use Magento\UrlRewrite\Model\UrlRewrite; +use Magento\UrlRewrite\Model\UrlRewriteFactory; + +/** + * Inline edit action class + * + * @SuppressWarnings(PHPMD.CouplingBetweenObjects) + */ +class InlineEdit extends Rewrite implements HttpPostActionInterface +{ + /** + * @var JsonFactory + */ + private $jsonFactory; + + /** + * @var UrlRewriteFactory + */ + private $urlRewriteFactory; + + /** + * @var UrlRewriteFactoryAlias + */ + private $urlRewriteResourceFactory; + + /** + * @param Context $context + * @param UrlRewriteFactory $urlRewriteFactory + * @param UrlRewriteFactoryAlias $urlRewriteResourceFactory + * @param JsonFactory $jsonFactory + */ + public function __construct( + Context $context, + UrlRewriteFactory $urlRewriteFactory, + UrlRewriteFactoryAlias $urlRewriteResourceFactory, + JsonFactory $jsonFactory + ) { + parent::__construct($context); + $this->urlRewriteFactory = $urlRewriteFactory; + $this->urlRewriteResourceFactory = $urlRewriteResourceFactory; + $this->jsonFactory = $jsonFactory; + } + + /** + * Inline edit save action + * + * @return Json + */ + public function execute(): Json + { + $resultJson = $this->jsonFactory->create(); + $error = false; + $messages = []; + + $postItems = $this->getRequest()->getParam( + 'items', + [] + ); + if (!($this->getRequest()->getParam('isAjax') && count($postItems))) { + return $resultJson->setData( + [ + 'messages' => [__('Please correct the data sent.')], + 'error' => true, + ] + ); + } + + $urlRewriteResource = $this->urlRewriteResourceFactory->create(); + foreach (array_keys($postItems) as $urlRewriteId) { + $urlRewrite = $this->urlRewriteFactory->create(); + $urlRewriteResource->load($urlRewrite, $urlRewriteId); + + try { + $urlRewrite->addData($postItems[$urlRewriteId]); + $urlRewriteResource->save($urlRewrite); + } catch (\RuntimeException $e) { + $messages[] = $this->getErrorWithUrlRewriteId( + $urlRewrite, + $e->getMessage() + ); + $error = true; + } catch (\Exception $e) { + $messages[] = $this->getErrorWithUrlRewriteId( + $urlRewrite, + __('Something went wrong while saving the url rewrite.') + ); + $error = true; + } + } + + return $resultJson->setData( + [ + 'messages' => $messages, + 'error' => $error + ] + ); + } + + /** + * Get error message for url rewrite + * + * @param UrlRewrite $urlRewrite + * @param string $errorText + * @return string + */ + private function getErrorWithUrlRewriteId(UrlRewrite $urlRewrite, $errorText): string + { + return '[Url rewrite ID: ' . $urlRewrite->getId() . '] ' . $errorText; + } +} diff --git a/app/code/Magento/UrlRewrite/Controller/Adminhtml/Url/Rewrite/MassDelete.php b/app/code/Magento/UrlRewrite/Controller/Adminhtml/Url/Rewrite/MassDelete.php new file mode 100644 index 0000000000000..51f7ddc4b53f9 --- /dev/null +++ b/app/code/Magento/UrlRewrite/Controller/Adminhtml/Url/Rewrite/MassDelete.php @@ -0,0 +1,85 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +declare(strict_types=1); + +namespace Magento\UrlRewrite\Controller\Adminhtml\Url\Rewrite; + +use Exception; +use Magento\Backend\App\Action\Context; +use Magento\Framework\App\Action\HttpPostActionInterface; +use Magento\Framework\Controller\Result\Redirect; +use Magento\Framework\Exception\LocalizedException; +use Magento\Ui\Component\MassAction\Filter; +use Magento\UrlRewrite\Controller\Adminhtml\Url\Rewrite; +use Magento\UrlRewrite\Model\ResourceModel\UrlRewriteCollectionFactory as CollectionFactory; + +/** + * Mass delete action class + * + * @SuppressWarnings(PHPMD.CouplingBetweenObjects) + */ +class MassDelete extends Rewrite implements HttpPostActionInterface +{ + /** + * @var Filter + */ + private $filter; + + /** + * @var CollectionFactory + */ + private $collectionFactory; + + /** + * @param Context $context + * @param Filter $filter + * @param CollectionFactory $collectionFactory + */ + public function __construct( + Context $context, + Filter $filter, + CollectionFactory $collectionFactory + ) { + $this->filter = $filter; + $this->collectionFactory = $collectionFactory; + parent::__construct($context); + } + + /** + * Mass delete action + * + * @return Redirect + * @throws LocalizedException + */ + public function execute(): Redirect + { + $collection = $this->filter->getCollection($this->collectionFactory->create()); + + $notProcessedCount = $processedCount = 0; + try { + foreach ($collection as $urlRewrite) { + $urlRewrite->delete(); + $processedCount++; + } + } catch (Exception $e) { + $this->getMessageManager()->addExceptionMessage($e); + $notProcessedCount++; + } + + $this->getMessageManager()->addSuccessMessage( + __('A total of %1 record(s) have been deleted.', $processedCount) + ); + + if ($notProcessedCount > 0) { + $this->getMessageManager()->addErrorMessage( + __('A total of %1 record(s) haven\'t been deleted.', $notProcessedCount) + ); + } + + $resultRedirect = $this->resultRedirectFactory->create(); + return $resultRedirect->setPath('*/*/'); + } +} diff --git a/app/code/Magento/UrlRewrite/Controller/Router.php b/app/code/Magento/UrlRewrite/Controller/Router.php index dd26f49b8efa4..040aa6fc85702 100644 --- a/app/code/Magento/UrlRewrite/Controller/Router.php +++ b/app/code/Magento/UrlRewrite/Controller/Router.php @@ -3,15 +3,22 @@ * Copyright © Magento, Inc. All rights reserved. * See COPYING.txt for license details. */ +declare(strict_types=1); + namespace Magento\UrlRewrite\Controller; +use Magento\Framework\App\Action\Forward; use Magento\Framework\App\Action\Redirect; +use Magento\Framework\App\ActionFactory; use Magento\Framework\App\ActionInterface; use Magento\Framework\App\Request\Http as HttpRequest; use Magento\Framework\App\RequestInterface; use Magento\Framework\App\Response\Http as HttpResponse; +use Magento\Framework\App\ResponseInterface; +use Magento\Framework\App\RouterInterface; use Magento\Framework\Exception\NoSuchEntityException; use Magento\Framework\UrlInterface; +use Magento\Store\Model\StoreManagerInterface; use Magento\UrlRewrite\Controller\Adminhtml\Url\Rewrite; use Magento\UrlRewrite\Model\UrlFinderInterface; use Magento\UrlRewrite\Service\V1\Data\UrlRewrite; @@ -21,10 +28,10 @@ * * @SuppressWarnings(PHPMD.CouplingBetweenObjects) */ -class Router implements \Magento\Framework\App\RouterInterface +class Router implements RouterInterface { /** - * @var \Magento\Framework\App\ActionFactory + * @var ActionFactory */ protected $actionFactory; @@ -34,7 +41,7 @@ class Router implements \Magento\Framework\App\RouterInterface protected $url; /** - * @var \Magento\Store\Model\StoreManagerInterface + * @var StoreManagerInterface */ protected $storeManager; @@ -49,17 +56,17 @@ class Router implements \Magento\Framework\App\RouterInterface protected $urlFinder; /** - * @param \Magento\Framework\App\ActionFactory $actionFactory + * @param ActionFactory $actionFactory * @param UrlInterface $url - * @param \Magento\Store\Model\StoreManagerInterface $storeManager - * @param \Magento\Framework\App\ResponseInterface $response + * @param StoreManagerInterface $storeManager + * @param ResponseInterface $response * @param UrlFinderInterface $urlFinder */ public function __construct( - \Magento\Framework\App\ActionFactory $actionFactory, + ActionFactory $actionFactory, UrlInterface $url, - \Magento\Store\Model\StoreManagerInterface $storeManager, - \Magento\Framework\App\ResponseInterface $response, + StoreManagerInterface $storeManager, + ResponseInterface $response, UrlFinderInterface $urlFinder ) { $this->actionFactory = $actionFactory; @@ -84,23 +91,41 @@ public function match(RequestInterface $request) ); if ($rewrite === null) { - //No rewrite rule matching current URl found, continuing with - //processing of this URL. + // No rewrite rule matching current URl found, continuing with + // processing of this URL. return null; } + + $requestStringTrimmed = ltrim($request->getRequestString(), '/'); + $rewriteRequestPath = $rewrite->getRequestPath(); + $rewriteTargetPath = $rewrite->getTargetPath(); + $rewriteTargetPathTrimmed = ltrim($rewriteTargetPath, '/'); + + if (preg_replace('/\?.*/', '', $rewriteRequestPath) === preg_replace('/\?.*/', '', $rewriteTargetPath) && + ( + !$requestStringTrimmed || + !$rewriteTargetPathTrimmed || + strpos($requestStringTrimmed, $rewriteTargetPathTrimmed) === 0 + ) + ) { + // Request and target paths of rewrite found without query params are equal and current request string + // starts with request target path, continuing with processing of this URL. + return null; + } + if ($rewrite->getRedirectType()) { - //Rule requires the request to be redirected to another URL - //and cannot be processed further. + // Rule requires the request to be redirected to another URL + // and cannot be processed further. return $this->processRedirect($request, $rewrite); } - //Rule provides actual URL that can be processed by a controller. + // Rule provides actual URL that can be processed by a controller. $request->setAlias( UrlInterface::REWRITE_REQUEST_PATH_ALIAS, - $rewrite->getRequestPath() + $rewriteRequestPath ); - $request->setPathInfo('/' . $rewrite->getTargetPath()); + $request->setPathInfo('/' . $rewriteTargetPath); return $this->actionFactory->create( - \Magento\Framework\App\Action\Forward::class + Forward::class ); } @@ -118,7 +143,7 @@ protected function processRedirect($request, $rewrite) if ($rewrite->getEntityType() !== Rewrite::ENTITY_TYPE_CUSTOM || ($prefix = substr($target, 0, 6)) !== 'http:/' && $prefix !== 'https:' ) { - $target = $this->url->getUrl('', ['_direct' => $target]); + $target = $this->url->getUrl('', ['_direct' => $target, '_query' => $request->getParams()]); } return $this->redirect($request, $target, $rewrite->getRedirectType()); } diff --git a/app/code/Magento/UrlRewrite/Model/OptionProvider.php b/app/code/Magento/UrlRewrite/Model/OptionProvider.php index ec70dad90e7cc..ddcd436691791 100644 --- a/app/code/Magento/UrlRewrite/Model/OptionProvider.php +++ b/app/code/Magento/UrlRewrite/Model/OptionProvider.php @@ -5,14 +5,16 @@ * Copyright © Magento, Inc. All rights reserved. * See COPYING.txt for license details. */ +declare(strict_types=1); + namespace Magento\UrlRewrite\Model; -use Magento\Framework\Option\ArrayInterface; +use Magento\Framework\Data\OptionSourceInterface; /** - * @codeCoverageIgnore + * Redirect type OptionProvider class */ -class OptionProvider implements ArrayInterface +class OptionProvider implements OptionSourceInterface { /** * Permanent redirect code @@ -20,19 +22,38 @@ class OptionProvider implements ArrayInterface const PERMANENT = 301; /** - * Redirect code + * Temporary redirect code */ const TEMPORARY = 302; /** - * {@inheritdoc} + * Retrieve redirect type options + * + * @return array + */ + public function toOptionArray(): array + { + $options = []; + foreach ($this->getOptions() as $value => $label) { + $options[] = [ + 'label' => $label, + 'value' => $value + ]; + } + return $options; + } + + /** + * Retrieve options for edit form + * + * @return array */ - public function toOptionArray() + public function getOptions(): array { return [ 0 => __('No'), self::TEMPORARY => __('Temporary (302)'), - self::PERMANENT => __('Permanent (301)'), + self::PERMANENT => __('Permanent (301)') ]; } } diff --git a/app/code/Magento/UrlRewrite/Model/Storage/DbStorage.php b/app/code/Magento/UrlRewrite/Model/Storage/DbStorage.php index f0e94e8379ad2..f187408d45a9d 100644 --- a/app/code/Magento/UrlRewrite/Model/Storage/DbStorage.php +++ b/app/code/Magento/UrlRewrite/Model/Storage/DbStorage.php @@ -138,17 +138,22 @@ private function extractMostRelevantUrlRewrite(string $requestPath, array $urlRe { $prioritizedUrlRewrites = []; foreach ($urlRewrites as $urlRewrite) { + $urlRewriteRequestPath = $urlRewrite[UrlRewrite::REQUEST_PATH]; + $urlRewriteTargetPath = $urlRewrite[UrlRewrite::TARGET_PATH]; switch (true) { - case $urlRewrite[UrlRewrite::REQUEST_PATH] === $requestPath: + case rtrim($urlRewriteRequestPath, '/') === rtrim($urlRewriteTargetPath, '/'): + $priority = 99; + break; + case $urlRewriteRequestPath === $requestPath: $priority = 1; break; - case $urlRewrite[UrlRewrite::REQUEST_PATH] === urldecode($requestPath): + case $urlRewriteRequestPath === urldecode($requestPath): $priority = 2; break; - case rtrim($urlRewrite[UrlRewrite::REQUEST_PATH], '/') === rtrim($requestPath, '/'): + case rtrim($urlRewriteRequestPath, '/') === rtrim($requestPath, '/'): $priority = 3; break; - case rtrim($urlRewrite[UrlRewrite::REQUEST_PATH], '/') === rtrim(urldecode($requestPath), '/'): + case rtrim($urlRewriteRequestPath, '/') === rtrim(urldecode($requestPath), '/'): $priority = 4; break; default: diff --git a/app/code/Magento/UrlRewrite/Model/UrlRewrite.php b/app/code/Magento/UrlRewrite/Model/UrlRewrite.php index 83d9e7c5cf335..5f29008621c1a 100644 --- a/app/code/Magento/UrlRewrite/Model/UrlRewrite.php +++ b/app/code/Magento/UrlRewrite/Model/UrlRewrite.php @@ -3,12 +3,16 @@ * Copyright © Magento, Inc. All rights reserved. * See COPYING.txt for license details. */ +declare(strict_types=1); + namespace Magento\UrlRewrite\Model; use Magento\Framework\App\ObjectManager; use Magento\Framework\Serialize\Serializer\Json; /** + * UrlRewrite model class + * * @method int getEntityId() * @method string getEntityType() * @method int getRedirectType() @@ -32,6 +36,7 @@ class UrlRewrite extends \Magento\Framework\Model\AbstractModel /** * UrlRewrite constructor. + * * @param \Magento\Framework\Model\Context $context * @param \Magento\Framework\Registry $registry * @param \Magento\Framework\Model\ResourceModel\AbstractResource|null $resource @@ -63,6 +68,8 @@ protected function _construct() } /** + * Get metadata + * * @return array * @api */ @@ -76,7 +83,6 @@ public function getMetadata() * Overwrite Metadata in the object. * * @param array|string $metadata - * * @return $this */ public function setMetadata($metadata) diff --git a/app/code/Magento/UrlRewrite/Test/Mftf/ActionGroup/AdminAddCustomUrlRewriteActionGroup.xml b/app/code/Magento/UrlRewrite/Test/Mftf/ActionGroup/AdminAddCustomUrlRewriteActionGroup.xml index d4bcb5bbb414f..dbc5e4bd48e84 100644 --- a/app/code/Magento/UrlRewrite/Test/Mftf/ActionGroup/AdminAddCustomUrlRewriteActionGroup.xml +++ b/app/code/Magento/UrlRewrite/Test/Mftf/ActionGroup/AdminAddCustomUrlRewriteActionGroup.xml @@ -21,7 +21,7 @@ <argument name="description" type="string"/> </arguments> - <amOnPage url="{{AdminUrlRewriteEditPage.url}}" stepKey="openUrlRewriteEditPage"/> + <amOnPage url="{{AdminUrlRewriteEditPage.url('')}}" stepKey="openUrlRewriteEditPage"/> <waitForPageLoad stepKey="waitForUrlRewriteEditPageToLoad" after="openUrlRewriteEditPage"/> <click selector="{{AdminUrlRewriteEditSection.createCustomUrlRewrite}}" stepKey="clickOnCustonUrlRewrite"/> <click selector="{{AdminUrlRewriteEditSection.createCustomUrlRewriteValue('customUrlRewriteValue')}}" stepKey="selectCustom"/> @@ -33,6 +33,6 @@ <click selector="{{AdminUrlRewriteEditSection.redirectTypeValue('redirectTypeValue')}}" stepKey="selectRedirectTypeValue"/> <fillField selector="{{AdminUrlRewriteEditSection.description}}" userInput="{{description}}" stepKey="fillDescription"/> <click selector="{{AdminUrlRewriteEditSection.saveButton}}" stepKey="clickOnSaveButton"/> - <seeElement selector="{{AdminUrlRewriteIndexSection.successMessage}}" stepKey="seeSuccessSaveMessage"/> + <seeElement selector="{{AdminMessagesSection.success}}" stepKey="seeSuccessSaveMessage"/> </actionGroup> </actionGroups> diff --git a/app/code/Magento/UrlRewrite/Test/Mftf/ActionGroup/AdminAddUrlRewriteActionGroup.xml b/app/code/Magento/UrlRewrite/Test/Mftf/ActionGroup/AdminAddUrlRewriteActionGroup.xml index 55f18ae5a0187..b0335607d5455 100644 --- a/app/code/Magento/UrlRewrite/Test/Mftf/ActionGroup/AdminAddUrlRewriteActionGroup.xml +++ b/app/code/Magento/UrlRewrite/Test/Mftf/ActionGroup/AdminAddUrlRewriteActionGroup.xml @@ -21,18 +21,20 @@ <argument name="description" type="string"/> </arguments> - <amOnPage url="{{AdminUrlRewriteEditPage.url}}" stepKey="openUrlRewriteEditPage"/> - <waitForElementVisible selector="{{AdminUrlRewriteEditSection.createCustomUrlRewrite}}" stepKey="waitForCreateUrlRewriteVisible"/> - <selectOption selector="{{AdminUrlRewriteEditSection.createCustomUrlRewrite}}" userInput="{{customUrlRewriteValue}}" stepKey="selectUrlRewriteTypeOption"/> - <waitForElementVisible selector="{{AdminUrlRewriteEditSection.categoryInTree(category)}}" stepKey="waitForCategoryInTreeVisible"/> - <click selector="{{AdminUrlRewriteEditSection.categoryInTree(category)}}" stepKey="clickOnCategoryInTree"/> - <waitForElementVisible selector="{{AdminUrlRewriteEditSection.store}}" stepKey="waitForStoreSelectVisible"/> - <selectOption selector="{{AdminUrlRewriteEditSection.store}}" userInput="{{storeValue}}" stepKey="selectStoreOption"/> - <fillField selector="{{AdminUrlRewriteEditSection.requestPath}}" userInput="{{requestPath}}" stepKey="fillRequestPathField"/> - <selectOption selector="{{AdminUrlRewriteEditSection.redirectType}}" userInput="{{redirectTypeValue}}" stepKey="selectRedirectType"/> - <fillField selector="{{AdminUrlRewriteEditSection.description}}" userInput="{{description}}" stepKey="fillDescriptionField"/> - <click selector="{{AdminMainActionsSection.save}}" stepKey="clickSaveButton"/> - <waitForElementVisible selector="{{AdminMessagesSection.success}}" stepKey="waitForSuccessMessage"/> - <see selector="{{AdminMessagesSection.success}}" userInput="The URL Rewrite has been saved." stepKey="seeSuccessMessage"/> + <amOnPage url="{{AdminUrlRewriteEditPage.url('')}}" stepKey="openUrlRewriteEditPage"/> + <waitForPageLoad stepKey="waitForUrlRewriteEditPageToLoad"/> + <click selector="{{AdminUrlRewriteEditSection.createCustomUrlRewrite}}" stepKey="clickOnCustonUrlRewrite"/> + <click selector="{{AdminUrlRewriteEditSection.createCustomUrlRewriteValue('customUrlRewriteValue')}}" stepKey="selectForCategory"/> + <waitForPageLoad stepKey="waitForCategoryEditSectionToLoad"/> + <click selector="{{AdminUrlRewriteEditSection.categoryInTree(category)}}" stepKey="selectCategoryInTree"/> + <waitForPageLoad stepKey="waitForPageToLoad"/> + <click selector="{{AdminUrlRewriteEditSection.store}}" stepKey="clickOnStore"/> + <click selector="{{AdminUrlRewriteEditSection.storeValue('storeValue')}}" stepKey="clickOnStoreValue"/> + <fillField selector="{{AdminUrlRewriteEditSection.requestPath}}" userInput="{{requestPath}}" stepKey="fillRequestPath"/> + <click selector="{{AdminUrlRewriteEditSection.redirectType}}" stepKey="selectRedirectType"/> + <click selector="{{AdminUrlRewriteEditSection.redirectTypeValue('redirectTypeValue')}}" stepKey="clickOnRedirectTypeValue"/> + <fillField selector="{{AdminUrlRewriteEditSection.description}}" userInput="{{description}}" stepKey="fillDescription"/> + <click selector="{{AdminUrlRewriteEditSection.saveButton}}" stepKey="clickOnSaveButton"/> + <seeElement selector="{{AdminMessagesSection.success}}" stepKey="seeSuccessSaveMessage"/> </actionGroup> </actionGroups> diff --git a/app/code/Magento/UrlRewrite/Test/Mftf/ActionGroup/AdminAddUrlRewriteForProductActionGroup.xml b/app/code/Magento/UrlRewrite/Test/Mftf/ActionGroup/AdminAddUrlRewriteForProductActionGroup.xml index 51436fc5de584..f1dcf175455f3 100644 --- a/app/code/Magento/UrlRewrite/Test/Mftf/ActionGroup/AdminAddUrlRewriteForProductActionGroup.xml +++ b/app/code/Magento/UrlRewrite/Test/Mftf/ActionGroup/AdminAddUrlRewriteForProductActionGroup.xml @@ -29,6 +29,6 @@ <click selector="{{AdminUrlRewriteEditSection.redirectTypeValue('redirectTypeValue')}}" stepKey="clickOnRedirectTypeValue"/> <fillField selector="{{AdminUrlRewriteEditSection.description}}" userInput="{{description}}" stepKey="fillDescription"/> <click selector="{{AdminUrlRewriteEditSection.saveButton}}" stepKey="clickOnSaveButton"/> - <seeElement selector="{{AdminUrlRewriteIndexSection.successMessage}}" stepKey="seeSuccessSaveMessage"/> + <seeElement selector="{{AdminMessagesSection.success}}" stepKey="seeSuccessSaveMessage"/> </actionGroup> </actionGroups> diff --git a/app/code/Magento/UrlRewrite/Test/Mftf/ActionGroup/AdminDeleteUrlRewriteActionGroup.xml b/app/code/Magento/UrlRewrite/Test/Mftf/ActionGroup/AdminDeleteUrlRewriteActionGroup.xml index bc3953c4dedd4..4d26f60cd48eb 100644 --- a/app/code/Magento/UrlRewrite/Test/Mftf/ActionGroup/AdminDeleteUrlRewriteActionGroup.xml +++ b/app/code/Magento/UrlRewrite/Test/Mftf/ActionGroup/AdminDeleteUrlRewriteActionGroup.xml @@ -18,18 +18,20 @@ <amOnPage url="{{AdminUrlRewriteIndexPage.url}}" stepKey="openUrlRewriteEditPage"/> <waitForPageLoad stepKey="waitForUrlRewriteEditPageToLoad"/> - <click selector="{{AdminUrlRewriteIndexSection.resetButton}}" stepKey="clickOnResetButton"/> + <conditionalClick selector="{{AdminDataGridHeaderSection.clearFilters}}" dependentSelector="{{AdminDataGridHeaderSection.clearFilters}}" visible="true" stepKey="clickClearFilters"/> <waitForPageLoad stepKey="waitForPageToLoad"/> - <fillField selector="{{AdminUrlRewriteIndexSection.requestPathFilter}}" userInput="{{requestPath}}" stepKey="fillRedirectPathFilter"/> - <click selector="{{AdminUrlRewriteIndexSection.searchButton}}" stepKey="clickOnSearchButton"/> + <click selector="{{AdminDataGridHeaderSection.filters}}" stepKey="openUrlRewriteGridFilters"/> + <fillField selector="{{AdminDataGridHeaderSection.filterFieldInput('request_path')}}" userInput="{{requestPath}}" stepKey="fillRedirectPathFilter"/> + <click selector="{{AdminDataGridHeaderSection.applyFilters}}" stepKey="clickOrderApplyFilters"/> <waitForPageLoad stepKey="waitForPageToLoad1"/> - <click selector="{{AdminUrlRewriteIndexSection.editButton('1')}}" stepKey="clickOnEditButton"/> + <click selector="{{AdminUrlRewriteIndexSection.rowSelectAction('1')}}" stepKey="clickOnRowSelectButton"/> + <click selector="{{AdminUrlRewriteIndexSection.rowEditAction('1')}}" stepKey="clickOnEditButton"/> <waitForPageLoad stepKey="waitForEditPageToLoad"/> <click selector="{{AdminUrlRewriteEditSection.deleteButton}}" stepKey="clickOnDeleteButton"/> <waitForPageLoad stepKey="waitForPageToLoad2"/> <waitForElementVisible selector="{{AdminUrlRewriteEditSection.okButton}}" stepKey="waitForOkButtonToVisible"/> <click selector="{{AdminUrlRewriteEditSection.okButton}}" stepKey="clickOnOkButton"/> <waitForPageLoad stepKey="waitForPageToLoad3"/> - <see selector="{{AdminUrlRewriteIndexSection.successMessage}}" userInput="You deleted the URL rewrite." stepKey="seeSuccessMessage"/> + <see selector="{{AdminMessagesSection.success}}" userInput="You deleted the URL rewrite." stepKey="seeSuccessMessage"/> </actionGroup> </actionGroups> diff --git a/app/code/Magento/UrlRewrite/Test/Mftf/ActionGroup/AdminGoToAddNewUrlRewritePageActionGroup.xml b/app/code/Magento/UrlRewrite/Test/Mftf/ActionGroup/AdminGoToAddNewUrlRewritePageActionGroup.xml index e0e8df47852d6..9b247af64eef1 100644 --- a/app/code/Magento/UrlRewrite/Test/Mftf/ActionGroup/AdminGoToAddNewUrlRewritePageActionGroup.xml +++ b/app/code/Magento/UrlRewrite/Test/Mftf/ActionGroup/AdminGoToAddNewUrlRewritePageActionGroup.xml @@ -13,7 +13,7 @@ <description>Goes to the Admin Add URL Rewrite edit page</description> </annotations> - <amOnPage url="{{AdminUrlRewriteEditPage.url}}" stepKey="openUrlRewriteEditPage"/> + <amOnPage url="{{AdminUrlRewriteEditPage.url('')}}" stepKey="openUrlRewriteEditPage"/> <waitForPageLoad stepKey="waitForUrlRewriteEditPageToLoad"/> </actionGroup> </actionGroups> diff --git a/app/code/Magento/UrlRewrite/Test/Mftf/ActionGroup/AdminSearchAndSelectUrlRewriteInGridActionGroup.xml b/app/code/Magento/UrlRewrite/Test/Mftf/ActionGroup/AdminSearchAndSelectUrlRewriteInGridActionGroup.xml index f2f114f01cc9e..6d3ddb69b2b58 100644 --- a/app/code/Magento/UrlRewrite/Test/Mftf/ActionGroup/AdminSearchAndSelectUrlRewriteInGridActionGroup.xml +++ b/app/code/Magento/UrlRewrite/Test/Mftf/ActionGroup/AdminSearchAndSelectUrlRewriteInGridActionGroup.xml @@ -18,12 +18,14 @@ <amOnPage url="{{AdminUrlRewriteIndexPage.url}}" stepKey="openUrlRewriteEditPage"/> <waitForPageLoad stepKey="waitForUrlRewriteEditPageToLoad"/> - <click selector="{{AdminUrlRewriteIndexSection.resetButton}}" stepKey="clickOnResetButton"/> + <conditionalClick selector="{{AdminDataGridHeaderSection.clearFilters}}" dependentSelector="{{AdminDataGridHeaderSection.clearFilters}}" visible="true" stepKey="clickClearFilters"/> <waitForPageLoad stepKey="waitForPageToLoad"/> - <fillField selector="{{AdminUrlRewriteIndexSection.requestPathFilter}}" userInput="{{requestPath}}" stepKey="fillRedirectPathFilter"/> - <click selector="{{AdminUrlRewriteIndexSection.searchButton}}" stepKey="clickOnSearchButton"/> + <click selector="{{AdminDataGridHeaderSection.filters}}" stepKey="openUrlRewriteGridFilters"/> + <fillField selector="{{AdminDataGridHeaderSection.filterFieldInput('request_path')}}" userInput="{{requestPath}}" stepKey="fillRequestPathFilter"/> + <click selector="{{AdminDataGridHeaderSection.applyFilters}}" stepKey="clickOrderApplyFilters"/> <waitForPageLoad stepKey="waitForPageToLoad1"/> - <click selector="{{AdminUrlRewriteIndexSection.editButton('1')}}" stepKey="clickOnEditButton"/> + <click selector="{{AdminUrlRewriteIndexSection.rowSelectAction('1')}}" stepKey="clickOnRowSelectButton"/> + <click selector="{{AdminUrlRewriteIndexSection.rowEditAction('1')}}" stepKey="clickOnEditButton"/> <waitForPageLoad stepKey="waitForEditPageToLoad"/> </actionGroup> </actionGroups> diff --git a/app/code/Magento/UrlRewrite/Test/Mftf/ActionGroup/AdminSearchByRequestPathActionGroup.xml b/app/code/Magento/UrlRewrite/Test/Mftf/ActionGroup/AdminSearchByRequestPathActionGroup.xml index 99ac4ea160f82..7661ed8c04992 100644 --- a/app/code/Magento/UrlRewrite/Test/Mftf/ActionGroup/AdminSearchByRequestPathActionGroup.xml +++ b/app/code/Magento/UrlRewrite/Test/Mftf/ActionGroup/AdminSearchByRequestPathActionGroup.xml @@ -20,13 +20,14 @@ <amOnPage url="{{AdminUrlRewriteIndexPage.url}}" stepKey="openUrlRewriteEditPage"/> <waitForPageLoad stepKey="waitForUrlRewriteEditPageToLoad"/> - <click selector="{{AdminUrlRewriteIndexSection.resetButton}}" stepKey="clickOnResetButton"/> + <conditionalClick selector="{{AdminDataGridHeaderSection.clearFilters}}" dependentSelector="{{AdminDataGridHeaderSection.clearFilters}}" visible="true" stepKey="clickClearFilters"/> <waitForPageLoad stepKey="waitForPageToLoad"/> - <fillField selector="{{AdminUrlRewriteIndexSection.requestPathFilter}}" userInput="{{redirectPath}}" stepKey="fillRedirectPathFilter"/> - <click selector="{{AdminUrlRewriteIndexSection.searchButton}}" stepKey="clickOnSearchButton"/> + <click selector="{{AdminDataGridHeaderSection.filters}}" stepKey="openUrlRewriteGridFilters"/> + <fillField selector="{{AdminDataGridHeaderSection.filterFieldInput('request_path')}}" userInput="{{redirectPath}}" stepKey="fillRedirectPathFilter"/> + <click selector="{{AdminDataGridHeaderSection.applyFilters}}" stepKey="clickOrderApplyFilters"/> <waitForPageLoad stepKey="waitForPageToLoad1"/> - <see selector="{{AdminUrlRewriteIndexSection.requestPathColumn('1')}}" userInput="{{redirectPath}}" stepKey="seeTheRedirectPathForOldUrl"/> - <see selector="{{AdminUrlRewriteIndexSection.targetPathColumn('1')}}" userInput="{{targetPath}}" stepKey="seeTheTargetPath"/> - <see selector="{{AdminUrlRewriteIndexSection.redirectTypeColumn('1')}}" userInput="{{redirectType}}" stepKey="seeTheRedirectTypeForOldUrl"/> + <see selector="{{AdminUrlRewriteIndexSection.gridCellByColumnRowNumber('1', 'Request Path')}}" userInput="{{redirectPath}}" stepKey="seeTheRedirectPathForOldUrl"/> + <see selector="{{AdminUrlRewriteIndexSection.gridCellByColumnRowNumber('1', 'Target Path')}}" userInput="{{targetPath}}" stepKey="seeTheTargetPath"/> + <see selector="{{AdminUrlRewriteIndexSection.gridCellByColumnRowNumber('1', 'Redirect Type')}}" userInput="{{redirectType}}" stepKey="seeTheRedirectTypeForOldUrl"/> </actionGroup> </actionGroups> diff --git a/app/code/Magento/UrlRewrite/Test/Mftf/ActionGroup/AdminSearchDeletedUrlRewriteActionGroup.xml b/app/code/Magento/UrlRewrite/Test/Mftf/ActionGroup/AdminSearchDeletedUrlRewriteActionGroup.xml index 2d7178f72f407..8f0a2c1731c3d 100644 --- a/app/code/Magento/UrlRewrite/Test/Mftf/ActionGroup/AdminSearchDeletedUrlRewriteActionGroup.xml +++ b/app/code/Magento/UrlRewrite/Test/Mftf/ActionGroup/AdminSearchDeletedUrlRewriteActionGroup.xml @@ -18,11 +18,12 @@ <amOnPage url="{{AdminUrlRewriteIndexPage.url}}" stepKey="openUrlRewriteEditPage"/> <waitForPageLoad stepKey="waitForUrlRewriteEditPageToLoad"/> - <click selector="{{AdminUrlRewriteIndexSection.resetButton}}" stepKey="clickOnResetButton"/> + <conditionalClick selector="{{AdminDataGridHeaderSection.clearFilters}}" dependentSelector="{{AdminDataGridHeaderSection.clearFilters}}" visible="true" stepKey="clickClearFilters"/> <waitForPageLoad stepKey="waitForPageToLoad"/> - <fillField selector="{{AdminUrlRewriteIndexSection.requestPathFilter}}" userInput="{{requestPath}}" stepKey="fillRedirectPathFilter"/> - <click selector="{{AdminUrlRewriteIndexSection.searchButton}}" stepKey="clickOnSearchButton"/> + <click selector="{{AdminDataGridHeaderSection.filters}}" stepKey="openUrlRewriteGridFilters"/> + <fillField selector="{{AdminDataGridHeaderSection.filterFieldInput('request_path')}}" userInput="{{requestPath}}" stepKey="fillRedirectPathFilter"/> + <click selector="{{AdminDataGridHeaderSection.applyFilters}}" stepKey="clickOrderApplyFilters"/> <waitForPageLoad stepKey="waitForPageToLoad1"/> - <see selector="{{AdminUrlRewriteIndexSection.emptyRecords}}" userInput="We couldn't find any records." stepKey="seeEmptyRecordMessage"/> + <see selector="{{AdminDataGridTableSection.dataGridEmpty}}" userInput="We couldn't find any records." stepKey="seeEmptyRecordMessage"/> </actionGroup> </actionGroups> diff --git a/app/code/Magento/UrlRewrite/Test/Mftf/ActionGroup/AdminUpdateCustomUrlRewriteActionGroup.xml b/app/code/Magento/UrlRewrite/Test/Mftf/ActionGroup/AdminUpdateCustomUrlRewriteActionGroup.xml index 0d0c754fe076e..ed60081160ace 100644 --- a/app/code/Magento/UrlRewrite/Test/Mftf/ActionGroup/AdminUpdateCustomUrlRewriteActionGroup.xml +++ b/app/code/Magento/UrlRewrite/Test/Mftf/ActionGroup/AdminUpdateCustomUrlRewriteActionGroup.xml @@ -27,6 +27,6 @@ <selectOption selector="{{AdminUrlRewriteEditSection.redirectType}}" userInput="{{redirectTypeValue}}" stepKey="selectRedirectTypeValue"/> <fillField selector="{{AdminUrlRewriteEditSection.description}}" userInput="{{description}}" stepKey="fillDescription"/> <click selector="{{AdminUrlRewriteEditSection.saveButton}}" stepKey="clickOnSaveButton"/> - <seeElement selector="{{AdminUrlRewriteIndexSection.successMessage}}" stepKey="seeSuccessSaveMessage"/> + <seeElement selector="{{AdminMessagesSection.success}}" stepKey="seeSuccessSaveMessage"/> </actionGroup> </actionGroups> diff --git a/app/code/Magento/UrlRewrite/Test/Mftf/ActionGroup/AdminUpdateUrlRewriteActionGroup.xml b/app/code/Magento/UrlRewrite/Test/Mftf/ActionGroup/AdminUpdateUrlRewriteActionGroup.xml index 4b884448a7653..c3e539db34108 100644 --- a/app/code/Magento/UrlRewrite/Test/Mftf/ActionGroup/AdminUpdateUrlRewriteActionGroup.xml +++ b/app/code/Magento/UrlRewrite/Test/Mftf/ActionGroup/AdminUpdateUrlRewriteActionGroup.xml @@ -26,6 +26,6 @@ <click selector="{{AdminUrlRewriteEditSection.redirectTypeValue(redirectTypeValue)}}" stepKey="selectRedirectTypeValue"/> <fillField selector="{{AdminUrlRewriteEditSection.description}}" userInput="{{description}}" stepKey="fillDescription"/> <click selector="{{AdminUrlRewriteEditSection.saveButton}}" stepKey="clickOnSaveButton"/> - <seeElement selector="{{AdminUrlRewriteIndexSection.successMessage}}" stepKey="seeSuccessSaveMessage"/> + <seeElement selector="{{AdminMessagesSection.success}}" stepKey="seeSuccessSaveMessage"/> </actionGroup> </actionGroups> diff --git a/app/code/Magento/UrlRewrite/Test/Mftf/Metadata/url_rewrite-meta.xml b/app/code/Magento/UrlRewrite/Test/Mftf/Metadata/UrlRewriteMeta.xml similarity index 100% rename from app/code/Magento/UrlRewrite/Test/Mftf/Metadata/url_rewrite-meta.xml rename to app/code/Magento/UrlRewrite/Test/Mftf/Metadata/UrlRewriteMeta.xml diff --git a/app/code/Magento/UrlRewrite/Test/Mftf/Page/AdminUrlRewriteEditPage.xml b/app/code/Magento/UrlRewrite/Test/Mftf/Page/AdminUrlRewriteEditPage.xml index b43e0e05ad55d..754381007221f 100644 --- a/app/code/Magento/UrlRewrite/Test/Mftf/Page/AdminUrlRewriteEditPage.xml +++ b/app/code/Magento/UrlRewrite/Test/Mftf/Page/AdminUrlRewriteEditPage.xml @@ -8,7 +8,7 @@ <pages xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="urn:magento:mftf:Page/etc/PageObject.xsd"> - <page name="AdminUrlRewriteEditPage" url="admin/url_rewrite/edit/id/{{url_rewrite_id}}/" area="admin" module="Magento_UrlRewrite"> + <page name="AdminUrlRewriteEditPage" url="admin/url_rewrite/edit/id/{{url_rewrite_id}}" parameterized="true" area="admin" module="Magento_UrlRewrite"> <section name="AdminUrlRewriteEditSection"/> </page> </pages> diff --git a/app/code/Magento/UrlRewrite/Test/Mftf/Section/AdminUrlRewriteIndexSection.xml b/app/code/Magento/UrlRewrite/Test/Mftf/Section/AdminUrlRewriteIndexSection.xml index 7b789845fe249..b6a155c9db6fb 100644 --- a/app/code/Magento/UrlRewrite/Test/Mftf/Section/AdminUrlRewriteIndexSection.xml +++ b/app/code/Magento/UrlRewrite/Test/Mftf/Section/AdminUrlRewriteIndexSection.xml @@ -9,18 +9,11 @@ <sections xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="urn:magento:mftf:Page/etc/SectionObject.xsd"> <section name="AdminUrlRewriteIndexSection"> - <element name="requestPathFilter" type="input" selector="#urlrewriteGrid_filter_request_path"/> - <element name="requestPathColumnValue" type="text" selector="//*[@id='urlrewriteGrid']//tbody//td[@data-column='request_path' and normalize-space(.)='{{columnValue}}']" parameterized="true"/> - <element name="targetPathColumnValue" type="text" selector="//*[@id='urlrewriteGrid']//tbody//td[@data-column='target_path' and normalize-space(.)='{{columnValue}}']" parameterized="true"/> - <element name="searchButton" type="button" selector="//button[@data-ui-id='widget-button-1']" timeout="30"/> - <element name="resetButton" type="button" selector="button[data-ui-id='widget-button-0']" timeout="30"/> - <element name="emptyRecordMessage" type="text" selector="//*[@class='empty-text']"/> - <element name="targetPathColumn" type="text" selector="//tr[@data-role='row'][{{var1}}]/td[@data-column='target_path']" parameterized="true"/> - <element name="redirectTypeColumn" type="text" selector="//tr[@data-role='row'][{{var1}}]/td[@data-column='redirect_type']" parameterized="true"/> - <element name="requestPathColumn" type="text" selector="//tr[@data-role='row'][{{var1}}]/td[@data-column='request_path']" parameterized="true"/> - <element name="emptyRecords" type="text" selector="//td[@class='empty-text']"/> - <element name="successMessage" type="text" selector="#messages"/> - <element name="editButton" type="text" selector="//tr[@data-role='row'][{{rowNumber}}]/td/a[contains(.,'Edit')]" parameterized="true"/> - <element name="storeView" type="text" selector="//tr[@data-role='row'][{{var1}}]/td[@data-column='store_id']" parameterized="true"/> + <element name="rowSelectAction" type="button" selector="//*[@data-role='grid']//tbody//tr[{{row}}+1]//td[count(//*[@data-role='grid']//th[contains(., 'Action')]/preceding-sibling::th)+1]//button[contains(@class, 'action-select')]" parameterized="true" timeout="30"/> + <element name="rowEditAction" type="button" selector="//*[@data-role='grid']//tbody//tr[{{row}}+1]//td[count(//*[@data-role='grid']//th[contains(., 'Action')]/preceding-sibling::th)+1]//a[contains(., 'Edit')]" parameterized="true" timeout="30"/> + <element name="gridCellByColumnRowNumber" type="text" selector="//*[@data-role='grid']//tbody//tr[{{row}}+1]//td[count(//*[@data-role='grid']//th[contains(., '{{column}}')]/preceding-sibling::th)+1]" parameterized="true"/> + <element name="gridCellByColumnValue" type="text" selector="//*[@data-role='grid']//tbody//td[count(//*[@data-role='grid']//th[contains(., '{{column}}')]/preceding-sibling::th)+1][normalize-space(.)='{{columnValue}}']" parameterized="true"/> + <element name="select" type="button" selector="//*[@data-role='grid']//tbody//tr[{{row}}+1]//button[@class='action-select']" timeout="30" parameterized="true"/> + <element name="activeEdit" type="button" selector="//*[@data-role='grid']//tbody//ul[@class='action-menu _active']//a[@data-action='item-edit']" timeout="30"/> </section> </sections> diff --git a/app/code/Magento/UrlRewrite/Test/Mftf/Test/AdminAutoUpdateURLRewriteWhenCategoryIsDeletedTest.xml b/app/code/Magento/UrlRewrite/Test/Mftf/Test/AdminAutoUpdateURLRewriteWhenCategoryIsDeletedTest.xml index 022d28ed692c1..d43a6b69165a5 100644 --- a/app/code/Magento/UrlRewrite/Test/Mftf/Test/AdminAutoUpdateURLRewriteWhenCategoryIsDeletedTest.xml +++ b/app/code/Magento/UrlRewrite/Test/Mftf/Test/AdminAutoUpdateURLRewriteWhenCategoryIsDeletedTest.xml @@ -18,7 +18,7 @@ </annotations> <before> - <actionGroup ref="LoginAsAdmin" stepKey="login"/> + <actionGroup ref="AdminLoginActionGroup" stepKey="login"/> <createData entity="SimpleSubCategory" stepKey="createCategory"/> <createData entity="SimpleProduct" stepKey="createSimpleProduct"> <requiredEntity createDataKey="createCategory"/> @@ -26,7 +26,7 @@ </before> <after> <deleteData createDataKey="createSimpleProduct" stepKey="deleteProduct"/> - <actionGroup ref="logout" stepKey="logout"/> + <actionGroup ref="AdminLogoutActionGroup" stepKey="logout"/> </after> <!--Filter and Select the created Product --> diff --git a/app/code/Magento/UrlRewrite/Test/Mftf/Test/AdminCheckUrlRewritesCorrectlyGeneratedForMultipleStoreviewsDuringProductImportTest.xml b/app/code/Magento/UrlRewrite/Test/Mftf/Test/AdminCheckUrlRewritesCorrectlyGeneratedForMultipleStoreviewsDuringProductImportTest.xml deleted file mode 100644 index 77fb2b5285ac3..0000000000000 --- a/app/code/Magento/UrlRewrite/Test/Mftf/Test/AdminCheckUrlRewritesCorrectlyGeneratedForMultipleStoreviewsDuringProductImportTest.xml +++ /dev/null @@ -1,267 +0,0 @@ -<?xml version="1.0" encoding="UTF-8"?> -<!-- - /** - * Copyright © Magento, Inc. All rights reserved. - * See COPYING.txt for license details. - */ ---> - -<tests xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/testSchema.xsd"> - <test name="AdminCheckUrlRewritesCorrectlyGeneratedForMultipleStoreviewsDuringProductImportTest"> - <annotations> - <features value="Url Rewrite"/> - <stories value="Url Rewrites for Multiple Storeviews"/> - <title value="Url Rewrites Correctly Generated for Multiple Storeviews During Product Import"/> - <description value="Check Url Rewrites Correctly Generated for Multiple Storeviews During Product Import."/> - <severity value="CRITICAL"/> - <testCaseId value="MC-12656"/> - <group value="urlRewrite"/> - </annotations> - <before> - <createData entity="ApiCategory" stepKey="createCategory"> - <field key="name">category-admin</field> - </createData> - <actionGroup ref="LoginAsAdmin" stepKey="loginAsAdmin"/> - <actionGroup ref="AdminDeleteStoreViewIfExistsActionGroup" stepKey="deleteENStoreViewIfExists"> - <argument name="storeViewName" value="{{customStoreENNotUnique.name}}"/> - </actionGroup> - <actionGroup ref="AdminDeleteStoreViewIfExistsActionGroup" stepKey="deleteNLStoreViewIfExists"> - <argument name="storeViewName" value="{{customStoreNLNotUnique.name}}"/> - </actionGroup> - <!-- Create Store View EN --> - <actionGroup ref="AdminCreateStoreViewActionGroup" stepKey="createStoreViewEn"> - <argument name="customStore" value="customStoreENNotUnique"/> - </actionGroup> - <!-- Create Store View NL --> - <actionGroup ref="AdminCreateStoreViewActionGroup" stepKey="createStoreViewNl"> - <argument name="customStore" value="customStoreNLNotUnique"/> - </actionGroup> - </before> - <after> - <deleteData createDataKey="createCategory" stepKey="deleteCategory"/> - <actionGroup ref="AdminDeleteStoreViewIfExistsActionGroup" stepKey="deleteStoreViewEn"> - <argument name="storeViewName" value="{{customStoreENNotUnique.name}}"/> - </actionGroup> - <actionGroup ref="AdminDeleteStoreViewIfExistsActionGroup" stepKey="deleteStoreViewNl"> - <argument name="storeViewName" value="{{customStoreNLNotUnique.name}}"/> - </actionGroup> - <actionGroup ref="ClearFiltersAdminDataGridActionGroup" stepKey="clearStoreFilters"/> - <actionGroup ref="DeleteProductByNameActionGroup" stepKey="deleteImportedProduct"> - <argument name="sku" value="productformagetwo68980"/> - <argument name="name" value="productformagetwo68980"/> - </actionGroup> - <actionGroup ref="ClearFiltersAdminDataGridActionGroup" stepKey="clearFiltersIfSet"/> - <actionGroup ref="logout" stepKey="logout"/> - </after> - <actionGroup ref="SwitchCategoryStoreViewActionGroup" stepKey="switchToStoreViewEn"> - <argument name="Store" value="customStoreENNotUnique.name"/> - <argument name="CatName" value="$$createCategory.name$$"/> - </actionGroup> - <uncheckOption selector="{{AdminCategoryBasicFieldSection.categoryNameUseDefault}}" stepKey="uncheckUseDefaultValueENStoreView"/> - <fillField selector="{{AdminCategoryBasicFieldSection.CategoryNameInput}}" userInput="category-english" stepKey="changeNameField"/> - <click selector="{{AdminCategorySEOSection.SectionHeader}}" stepKey="clickOnSectionHeader"/> - <actionGroup ref="ChangeSeoUrlKeyForSubCategoryActionGroup" stepKey="changeSeoUrlKeyENStoreView"> - <argument name="value" value="category-english"/> - </actionGroup> - <actionGroup ref="SwitchCategoryStoreViewActionGroup" stepKey="switchToStoreViewNl"> - <argument name="Store" value="customStoreNLNotUnique.name"/> - <argument name="CatName" value="$$createCategory.name$$"/> - </actionGroup> - <uncheckOption selector="{{AdminCategoryBasicFieldSection.categoryNameUseDefault}}" stepKey="uncheckUseDefaultValue1"/> - <fillField selector="{{AdminCategoryBasicFieldSection.CategoryNameInput}}" userInput="category-dutch" stepKey="changeNameFieldNLStoreView"/> - <click selector="{{AdminCategorySEOSection.SectionHeader}}" stepKey="clickOnSectionHeader2"/> - <actionGroup ref="ChangeSeoUrlKeyForSubCategoryActionGroup" stepKey="changeSeoUrlKeyNLStoreView"> - <argument name="value" value="category-dutch"/> - </actionGroup> - <amOnPage url="{{AdminImportIndexPage.url}}" stepKey="navigateToSystemImport"/> - <selectOption selector="{{AdminImportMainSection.entityType}}" userInput="Products" stepKey="selectProductsOption"/> - <waitForElementVisible selector="{{AdminImportMainSection.importBehavior}}" stepKey="waitForImportBehaviorElementVisible"/> - <selectOption selector="{{AdminImportMainSection.importBehavior}}" userInput="Add/Update" stepKey="selectAddUpdateOption"/> - <attachFile selector="{{AdminImportMainSection.selectFileToImport}}" userInput="import_updated.csv" stepKey="attachFileForImport"/> - <click selector="{{AdminImportHeaderSection.checkDataButton}}" stepKey="clickCheckDataButton"/> - <see selector="{{AdminImportValidationMessagesSection.notice}}" userInput="Checked rows: 3, checked entities: 1, invalid rows: 0, total errors: 0" stepKey="assertNotice"/> - <see selector="{{AdminImportValidationMessagesSection.success}}" userInput="File is valid! To start import process press "Import" button" stepKey="assertSuccessMessage"/> - <click selector="{{AdminImportMainSection.importButton}}" stepKey="clickImportButton"/> - <see selector="{{AdminImportValidationMessagesSection.success}}" userInput="Import successfully done" stepKey="assertSuccessMessage1"/> - <see selector="{{AdminImportValidationMessagesSection.notice}}" userInput="Created: 1, Updated: 0, Deleted: 0" stepKey="assertNotice1"/> - <actionGroup ref="SearchForProductOnBackendByNameActionGroup" stepKey="searchForProductOnBackend"> - <argument name="productName" value="productformagetwo68980"/> - </actionGroup> - <click selector="{{AdminProductGridSection.productRowBySku('productformagetwo68980')}}" stepKey="clickOnProductRow"/> - <grabFromCurrentUrl regex="~/id/(\d+)/~" stepKey="grabProductIdFromUrl"/> - <amOnPage url="{{AdminUrlRewriteIndexPage.url}}" stepKey="goToUrlRewritesIndexPage"/> - - <fillField selector="{{AdminUrlRewriteIndexSection.requestPathFilter}}" userInput="category-english.html" stepKey="inputCategoryUrlForENStoreView"/> - <click selector="{{AdminDataGridHeaderSection.applyFilters}}" stepKey="clickSearchButton"/> - <seeElement selector="{{AdminUrlRewriteIndexSection.requestPathColumnValue('category-english.html')}}" stepKey="seeUrlInRequestPathColumn"/> - <seeElement selector="{{AdminUrlRewriteIndexSection.targetPathColumnValue(catalog/category/view/id/$$createCategory.id$$)}}" stepKey="seeUrlInTargetPathColumn"/> - - <fillField selector="{{AdminUrlRewriteIndexSection.requestPathFilter}}" userInput="category-dutch.html" stepKey="inputCategoryUrlForNLStoreView"/> - <click selector="{{AdminDataGridHeaderSection.applyFilters}}" stepKey="clickSearchButton1"/> - <seeElement selector="{{AdminUrlRewriteIndexSection.requestPathColumnValue('category-dutch.html')}}" stepKey="seeUrlInRequestPathColumn1"/> - <seeElement selector="{{AdminUrlRewriteIndexSection.targetPathColumnValue(catalog/category/view/id/$$createCategory.id$$)}}" stepKey="seeUrlInTargetPathColumn1"/> - - <fillField selector="{{AdminUrlRewriteIndexSection.requestPathFilter}}" userInput="productformagetwo68980-english.html" stepKey="inputProductUrlForENStoreView"/> - <click selector="{{AdminDataGridHeaderSection.applyFilters}}" stepKey="clickSearchButton2"/> - <seeElement selector="{{AdminUrlRewriteIndexSection.requestPathColumnValue('productformagetwo68980-english.html')}}" stepKey="seeUrlInRequestPathColumn2"/> - <seeElement selector="{{AdminUrlRewriteIndexSection.targetPathColumnValue('catalog/product/view/id/$grabProductIdFromUrl')}}" stepKey="seeUrlInTargetPathColumn2"/> - - <fillField selector="{{AdminUrlRewriteIndexSection.requestPathFilter}}" userInput="productformagetwo68980-dutch.html" stepKey="inputProductUrlForENStoreView1"/> - <click selector="{{AdminDataGridHeaderSection.applyFilters}}" stepKey="clickSearchButton3"/> - <seeElement selector="{{AdminUrlRewriteIndexSection.requestPathColumnValue('productformagetwo68980-dutch.html')}}" stepKey="seeUrlInRequestPathColumn3"/> - <seeElement selector="{{AdminUrlRewriteIndexSection.targetPathColumnValue('catalog/product/view/id/$grabProductIdFromUrl')}}" stepKey="seeUrlInTargetPathColumn3"/> - - <fillField selector="{{AdminUrlRewriteIndexSection.requestPathFilter}}" userInput="category-english/productformagetwo68980-english.html" stepKey="inputProductUrlForENStoreView2"/> - <click selector="{{AdminDataGridHeaderSection.applyFilters}}" stepKey="clickSearchButton4"/> - <seeElement selector="{{AdminUrlRewriteIndexSection.requestPathColumnValue('category-english/productformagetwo68980-english.html')}}" stepKey="seeUrlInRequestPathColumn4"/> - <seeElement selector="{{AdminUrlRewriteIndexSection.targetPathColumnValue(catalog/product/view/id/$grabProductIdFromUrl/category/$$createCategory.id$$)}}" stepKey="seeUrlInTargetPathColumn4"/> - - <fillField selector="{{AdminUrlRewriteIndexSection.requestPathFilter}}" userInput="category-dutch/productformagetwo68980-dutch.html" stepKey="inputProductUrlForENStoreView3"/> - <click selector="{{AdminDataGridHeaderSection.applyFilters}}" stepKey="clickSearchButton5"/> - <seeElement selector="{{AdminUrlRewriteIndexSection.requestPathColumnValue('category-dutch/productformagetwo68980-dutch.html')}}" stepKey="seeUrlInRequestPathColumn5"/> - <seeElement selector="{{AdminUrlRewriteIndexSection.targetPathColumnValue(catalog/product/view/id/$grabProductIdFromUrl/category/$$createCategory.id$$)}}" stepKey="seeUrlInTargetPathColumn5"/> - </test> - <test name="AdminCheckUrlRewritesCorrectlyGeneratedForMultipleStoreviewsDuringProductImportTestWithConfigurationTurnedOff"> - <annotations> - <features value="Url Rewrite"/> - <stories value="Url Rewrites for Multiple Storeviews"/> - <title value="Url Rewrites Correctly Generated for Multiple Storeviews During Product Import With Configuration Turned Off"/> - <description value="Check Url Rewrites Correctly Generated for Multiple Storeviews During Product Import."/> - <severity value="CRITICAL"/> - <testCaseId value="MC-6802"/> - <group value="urlRewrite"/> - </annotations> - <before> - <!-- Set the configuration for Generate "category/product" URL Rewrites to Yes (default)--> - <comment userInput="Enable SEO configuration setting to generate category/product URL Rewrites" stepKey="commentEnableUrlRewriteConfig" /> - <magentoCLI command="config:set catalog/seo/generate_category_product_rewrites 1" stepKey="enableGenerateUrlRewrite"/> - <!--Flush cache--> - <magentoCLI command="cache:flush" stepKey="cleanCache1"/> - <createData entity="ApiCategory" stepKey="createCategory"> - <field key="name">category-admin</field> - </createData> - <actionGroup ref="LoginAsAdmin" stepKey="loginAsAdmin"/> - <actionGroup ref="AdminDeleteStoreViewIfExistsActionGroup" stepKey="deleteENStoreViewIfExists"> - <argument name="storeViewName" value="{{customStoreENNotUnique.name}}"/> - </actionGroup> - <actionGroup ref="AdminDeleteStoreViewIfExistsActionGroup" stepKey="deleteNLStoreViewIfExists"> - <argument name="storeViewName" value="{{customStoreNLNotUnique.name}}"/> - </actionGroup> - <!-- Create Store View EN --> - <actionGroup ref="AdminCreateStoreViewActionGroup" stepKey="createStoreViewEn"> - <argument name="customStore" value="customStoreENNotUnique"/> - </actionGroup> - <!-- Create Store View NL --> - <actionGroup ref="AdminCreateStoreViewActionGroup" stepKey="createStoreViewNl"> - <argument name="customStore" value="customStoreNLNotUnique"/> - </actionGroup> - <!-- Set the configuration for Generate "category/product" URL Rewrites to No--> - <comment userInput="Disable SEO configuration setting to generate category/product URL Rewrites" stepKey="commentDisableUrlRewriteConfig" /> - <magentoCLI command="config:set catalog/seo/generate_category_product_rewrites 0" stepKey="disableGenerateUrlRewrite"/> - <!--Flush cache--> - <magentoCLI command="cache:flush" stepKey="cleanCache"/> - </before> - <after> - <deleteData createDataKey="createCategory" stepKey="deleteCategory"/> - <actionGroup ref="AdminDeleteStoreViewIfExistsActionGroup" stepKey="deleteStoreViewEn"> - <argument name="storeViewName" value="{{customStoreENNotUnique.name}}"/> - </actionGroup> - <actionGroup ref="AdminDeleteStoreViewIfExistsActionGroup" stepKey="deleteStoreViewNl"> - <argument name="storeViewName" value="{{customStoreNLNotUnique.name}}"/> - </actionGroup> - <actionGroup ref="ClearFiltersAdminDataGridActionGroup" stepKey="clearStoreGridFilters"/> - <actionGroup ref="DeleteProductByNameActionGroup" stepKey="deleteImportedProduct"> - <argument name="sku" value="productformagetwo68980"/> - <argument name="name" value="productformagetwo68980"/> - </actionGroup> - <actionGroup ref="ClearFiltersAdminDataGridActionGroup" stepKey="clearFiltersIfSet"/> - <actionGroup ref="logout" stepKey="logout"/> - <magentoCLI command="config:set catalog/seo/generate_category_product_rewrites 1" stepKey="resetConfigurationSetting"/> - <!--Flush cache--> - <magentoCLI command="cache:flush" stepKey="cleanCache2"/> - </after> - <actionGroup ref="SwitchCategoryStoreViewActionGroup" stepKey="switchToStoreViewEn"> - <argument name="Store" value="customStoreENNotUnique.name"/> - <argument name="CatName" value="$$createCategory.name$$"/> - </actionGroup> - <uncheckOption selector="{{AdminCategoryBasicFieldSection.categoryNameUseDefault}}" stepKey="uncheckUseDefaultValueENStoreView"/> - <fillField selector="{{AdminCategoryBasicFieldSection.CategoryNameInput}}" userInput="category-english" stepKey="changeNameField"/> - <click selector="{{AdminCategorySEOSection.SectionHeader}}" stepKey="clickOnSectionHeader"/> - <actionGroup ref="ChangeSeoUrlKeyForSubCategoryActionGroup" stepKey="changeSeoUrlKeyENStoreView"> - <argument name="value" value="category-english"/> - </actionGroup> - <actionGroup ref="SwitchCategoryStoreViewActionGroup" stepKey="switchToStoreViewNl"> - <argument name="Store" value="customStoreNLNotUnique.name"/> - <argument name="CatName" value="$$createCategory.name$$"/> - </actionGroup> - <uncheckOption selector="{{AdminCategoryBasicFieldSection.categoryNameUseDefault}}" stepKey="uncheckUseDefaultValue1"/> - <fillField selector="{{AdminCategoryBasicFieldSection.CategoryNameInput}}" userInput="category-dutch" stepKey="changeNameFieldNLStoreView"/> - <click selector="{{AdminCategorySEOSection.SectionHeader}}" stepKey="clickOnSectionHeader2"/> - <actionGroup ref="ChangeSeoUrlKeyForSubCategoryActionGroup" stepKey="changeSeoUrlKeyNLStoreView"> - <argument name="value" value="category-dutch"/> - </actionGroup> - <amOnPage url="{{AdminImportIndexPage.url}}" stepKey="navigateToSystemImport"/> - <selectOption selector="{{AdminImportMainSection.entityType}}" userInput="Products" stepKey="selectProductsOption"/> - <waitForElementVisible selector="{{AdminImportMainSection.importBehavior}}" stepKey="waitForImportBehaviorElementVisible"/> - <selectOption selector="{{AdminImportMainSection.importBehavior}}" userInput="Add/Update" stepKey="selectAddUpdateOption"/> - <attachFile selector="{{AdminImportMainSection.selectFileToImport}}" userInput="import_updated.csv" stepKey="attachFileForImport"/> - <click selector="{{AdminImportHeaderSection.checkDataButton}}" stepKey="clickCheckDataButton"/> - <see selector="{{AdminImportValidationMessagesSection.notice}}" userInput="Checked rows: 3, checked entities: 1, invalid rows: 0, total errors: 0" stepKey="assertNotice"/> - <see selector="{{AdminImportValidationMessagesSection.success}}" userInput="File is valid! To start import process press "Import" button" stepKey="assertSuccessMessage"/> - <click selector="{{AdminImportMainSection.importButton}}" stepKey="clickImportButton"/> - <see selector="{{AdminImportValidationMessagesSection.success}}" userInput="Import successfully done" stepKey="assertSuccessMessage1"/> - <see selector="{{AdminImportValidationMessagesSection.notice}}" userInput="Created: 1, Updated: 0, Deleted: 0" stepKey="assertNotice1"/> - <actionGroup ref="SearchForProductOnBackendByNameActionGroup" stepKey="searchForProductOnBackend"> - <argument name="productName" value="productformagetwo68980"/> - </actionGroup> - <click selector="{{AdminProductGridSection.productRowBySku('productformagetwo68980')}}" stepKey="clickOnProductRow"/> - <grabFromCurrentUrl regex="~/id/(\d+)/~" stepKey="grabProductIdFromUrl"/> - <amOnPage url="{{AdminUrlRewriteIndexPage.url}}" stepKey="goToUrlRewritesIndexPage"/> - - <fillField selector="{{AdminUrlRewriteIndexSection.requestPathFilter}}" userInput="category-english.html" stepKey="inputCategoryUrlForENStoreView"/> - <click selector="{{AdminDataGridHeaderSection.applyFilters}}" stepKey="clickSearchButton"/> - <seeElement selector="{{AdminUrlRewriteIndexSection.requestPathColumnValue('category-english.html')}}" stepKey="seeUrlInRequestPathColumn"/> - <seeElement selector="{{AdminUrlRewriteIndexSection.targetPathColumnValue(catalog/category/view/id/$$createCategory.id$$)}}" stepKey="seeUrlInTargetPathColumn"/> - - <fillField selector="{{AdminUrlRewriteIndexSection.requestPathFilter}}" userInput="category-dutch.html" stepKey="inputCategoryUrlForNLStoreView"/> - <click selector="{{AdminDataGridHeaderSection.applyFilters}}" stepKey="clickSearchButton1"/> - <seeElement selector="{{AdminUrlRewriteIndexSection.requestPathColumnValue('category-dutch.html')}}" stepKey="seeUrlInRequestPathColumn1"/> - <seeElement selector="{{AdminUrlRewriteIndexSection.targetPathColumnValue(catalog/category/view/id/$$createCategory.id$$)}}" stepKey="seeUrlInTargetPathColumn1"/> - - <fillField selector="{{AdminUrlRewriteIndexSection.requestPathFilter}}" userInput="productformagetwo68980-english.html" stepKey="inputProductUrlForENStoreView"/> - <click selector="{{AdminDataGridHeaderSection.applyFilters}}" stepKey="clickSearchButton2"/> - <seeElement selector="{{AdminUrlRewriteIndexSection.requestPathColumnValue('productformagetwo68980-english.html')}}" stepKey="seeUrlInRequestPathColumn2"/> - <seeElement selector="{{AdminUrlRewriteIndexSection.targetPathColumnValue('catalog/product/view/id/$grabProductIdFromUrl')}}" stepKey="seeUrlInTargetPathColumn2"/> - <dontSeeElement selector="{{AdminUrlRewriteIndexSection.requestPathColumnValue('category-english/productformagetwo68980-english.html')}}" stepKey="seeUrlInRequestPathColumn4"/> - <dontSeeElement selector="{{AdminUrlRewriteIndexSection.targetPathColumnValue(catalog/product/view/id/$grabProductIdFromUrl/category/$$createCategory.id$$)}}" stepKey="seeUrlInTargetPathColumn4"/> - - <fillField selector="{{AdminUrlRewriteIndexSection.requestPathFilter}}" userInput="productformagetwo68980-dutch.html" stepKey="inputProductUrlForENStoreView1"/> - <click selector="{{AdminDataGridHeaderSection.applyFilters}}" stepKey="clickSearchButton3"/> - <seeElement selector="{{AdminUrlRewriteIndexSection.requestPathColumnValue('productformagetwo68980-dutch.html')}}" stepKey="seeUrlInRequestPathColumn3"/> - <seeElement selector="{{AdminUrlRewriteIndexSection.targetPathColumnValue('catalog/product/view/id/$grabProductIdFromUrl')}}" stepKey="seeUrlInTargetPathColumn3"/> - <dontSeeElement selector="{{AdminUrlRewriteIndexSection.requestPathColumnValue('category-dutch/productformagetwo68980-dutch.html')}}" stepKey="seeUrlInRequestPathColumn5"/> - <dontSeeElement selector="{{AdminUrlRewriteIndexSection.targetPathColumnValue(catalog/product/view/id/$grabProductIdFromUrl/category/$$createCategory.id$$)}}" stepKey="seeUrlInTargetPathColumn5"/> - - <!-- Switch StoreView --> - <amOnPage url="{{StorefrontHomePage.url}}" stepKey="amOnProduct4Page"/> - <actionGroup ref="StorefrontSwitchStoreViewActionGroup" stepKey="switchToCustomStoreView"> - <argument name="storeView" value="customStoreENNotUnique"/> - </actionGroup> - - <amOnPage url="/productformagetwo68980-english.html" stepKey="navigateToProductPage"/> - <see selector="{{StorefrontProductInfoMainSection.productName}}" userInput="productformagetwo68980-english" stepKey="seeProductName"/> - <amOnPage url="/category-english/productformagetwo68980-english.html" stepKey="navigateToProductPage2"/> - <see selector="{{StorefrontProductInfoMainSection.productName}}" userInput="productformagetwo68980-english" stepKey="seeProductName2"/> - - <!-- Switch StoreView --> - <amOnPage url="{{StorefrontHomePage.url}}" stepKey="amOnProduct4Page2"/> - <actionGroup ref="StorefrontSwitchStoreViewActionGroup" stepKey="switchToCustomStoreView2"> - <argument name="storeView" value="customStoreNLNotUnique"/> - </actionGroup> - - <amOnPage url="/productformagetwo68980-dutch.html" stepKey="navigateToProductPage3"/> - <see selector="{{StorefrontProductInfoMainSection.productName}}" userInput="productformagetwo68980-dutch" stepKey="seeProductName3"/> - <amOnPage url="/category-dutch/productformagetwo68980-dutch.html" stepKey="navigateToProductPage4"/> - <see selector="{{StorefrontProductInfoMainSection.productName}}" userInput="productformagetwo68980-dutch" stepKey="seeProductName4"/> - </test> -</tests> diff --git a/app/code/Magento/UrlRewrite/Test/Mftf/Test/AdminCheckUrlRewritesCorrectlyGeneratedForMultipleStoreviewsDuringProductImportTest/AdminCheckUrlRewritesCorrectlyGeneratedForMultipleStoreviewsDuringProductImportTest.xml b/app/code/Magento/UrlRewrite/Test/Mftf/Test/AdminCheckUrlRewritesCorrectlyGeneratedForMultipleStoreviewsDuringProductImportTest/AdminCheckUrlRewritesCorrectlyGeneratedForMultipleStoreviewsDuringProductImportTest.xml new file mode 100644 index 0000000000000..b9f726aec668e --- /dev/null +++ b/app/code/Magento/UrlRewrite/Test/Mftf/Test/AdminCheckUrlRewritesCorrectlyGeneratedForMultipleStoreviewsDuringProductImportTest/AdminCheckUrlRewritesCorrectlyGeneratedForMultipleStoreviewsDuringProductImportTest.xml @@ -0,0 +1,130 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + /** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +--> +<tests xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/testSchema.xsd"> + <test name="AdminCheckUrlRewritesCorrectlyGeneratedForMultipleStoreviewsDuringProductImportTest"> + <annotations> + <features value="Url Rewrite"/> + <stories value="Url Rewrites for Multiple Storeviews"/> + <title value="Url Rewrites Correctly Generated for Multiple Storeviews During Product Import"/> + <description value="Check Url Rewrites Correctly Generated for Multiple Storeviews During Product Import."/> + <severity value="CRITICAL"/> + <testCaseId value="MC-12656"/> + <group value="urlRewrite"/> + </annotations> + <before> + <createData entity="ApiCategory" stepKey="createCategory"> + <field key="name">category-admin</field> + </createData> + <actionGroup ref="AdminLoginActionGroup" stepKey="loginAsAdmin"/> + <!-- Create Store View EN --> + <actionGroup ref="AdminCreateStoreViewActionGroup" stepKey="createStoreViewEn"> + <argument name="customStore" value="customStoreENNotUnique"/> + </actionGroup> + <!-- Create Store View NL --> + <actionGroup ref="AdminCreateStoreViewActionGroup" stepKey="createStoreViewNl"> + <argument name="customStore" value="customStoreNLNotUnique"/> + </actionGroup> + </before> + <after> + <deleteData createDataKey="createCategory" stepKey="deleteCategory"/> + <actionGroup ref="AdminDeleteStoreViewActionGroup" stepKey="deleteStoreViewEn"> + <argument name="customStore" value="customStoreENNotUnique"/> + </actionGroup> + <actionGroup ref="AdminDeleteStoreViewActionGroup" stepKey="deleteStoreViewNl"> + <argument name="customStore" value="customStoreNLNotUnique"/> + </actionGroup> + <actionGroup ref="ClearFiltersAdminDataGridActionGroup" stepKey="clearStoreFilters"/> + <actionGroup ref="DeleteProductByNameActionGroup" stepKey="deleteImportedProduct"> + <argument name="sku" value="productformagetwo68980"/> + <argument name="name" value="productformagetwo68980"/> + </actionGroup> + <actionGroup ref="ClearFiltersAdminDataGridActionGroup" stepKey="clearFiltersIfSet"/> + <actionGroup ref="AdminLogoutActionGroup" stepKey="logout"/> + </after> + <actionGroup ref="SwitchCategoryStoreViewActionGroup" stepKey="switchToStoreViewEn"> + <argument name="Store" value="customStoreENNotUnique.name"/> + <argument name="CatName" value="$$createCategory.name$$"/> + </actionGroup> + <uncheckOption selector="{{AdminCategoryBasicFieldSection.categoryNameUseDefault}}" stepKey="uncheckUseDefaultValueENStoreView"/> + <fillField selector="{{AdminCategoryBasicFieldSection.CategoryNameInput}}" userInput="category-english" stepKey="changeNameField"/> + <click selector="{{AdminCategorySEOSection.SectionHeader}}" stepKey="clickOnSectionHeader"/> + <actionGroup ref="ChangeSeoUrlKeyForSubCategoryActionGroup" stepKey="changeSeoUrlKeyENStoreView"> + <argument name="value" value="category-english"/> + </actionGroup> + <actionGroup ref="SwitchCategoryStoreViewActionGroup" stepKey="switchToStoreViewNl"> + <argument name="Store" value="customStoreNLNotUnique.name"/> + <argument name="CatName" value="$$createCategory.name$$"/> + </actionGroup> + <uncheckOption selector="{{AdminCategoryBasicFieldSection.categoryNameUseDefault}}" stepKey="uncheckUseDefaultValue1"/> + <fillField selector="{{AdminCategoryBasicFieldSection.CategoryNameInput}}" userInput="category-dutch" stepKey="changeNameFieldNLStoreView"/> + <click selector="{{AdminCategorySEOSection.SectionHeader}}" stepKey="clickOnSectionHeader2"/> + <actionGroup ref="ChangeSeoUrlKeyForSubCategoryActionGroup" stepKey="changeSeoUrlKeyNLStoreView"> + <argument name="value" value="category-dutch"/> + </actionGroup> + <amOnPage url="{{AdminImportIndexPage.url}}" stepKey="navigateToSystemImport"/> + <selectOption selector="{{AdminImportMainSection.entityType}}" userInput="Products" stepKey="selectProductsOption"/> + <waitForElementVisible selector="{{AdminImportMainSection.importBehavior}}" stepKey="waitForImportBehaviorElementVisible"/> + <selectOption selector="{{AdminImportMainSection.importBehavior}}" userInput="Add/Update" stepKey="selectAddUpdateOption"/> + <attachFile selector="{{AdminImportMainSection.selectFileToImport}}" userInput="import_updated.csv" stepKey="attachFileForImport"/> + <click selector="{{AdminImportHeaderSection.checkDataButton}}" stepKey="clickCheckDataButton"/> + <see selector="{{AdminImportValidationMessagesSection.notice}}" userInput="Checked rows: 3, checked entities: 1, invalid rows: 0, total errors: 0" stepKey="assertNotice"/> + <see selector="{{AdminImportValidationMessagesSection.success}}" userInput="File is valid! To start import process press "Import" button" stepKey="assertSuccessMessage"/> + <click selector="{{AdminImportMainSection.importButton}}" stepKey="clickImportButton"/> + <see selector="{{AdminImportValidationMessagesSection.success}}" userInput="Import successfully done" stepKey="assertSuccessMessage1"/> + <see selector="{{AdminImportValidationMessagesSection.notice}}" userInput="Created: 1, Updated: 0, Deleted: 0" stepKey="assertNotice1"/> + <actionGroup ref="SearchForProductOnBackendByNameActionGroup" stepKey="searchForProductOnBackend"> + <argument name="productName" value="productformagetwo68980"/> + </actionGroup> + <click selector="{{AdminProductGridSection.productRowBySku('productformagetwo68980')}}" stepKey="clickOnProductRow"/> + <grabFromCurrentUrl regex="~/id/(\d+)/~" stepKey="grabProductIdFromUrl"/> + <amOnPage url="{{AdminUrlRewriteIndexPage.url}}" stepKey="goToUrlRewritesIndexPage"/> + + <click selector="{{AdminDataGridHeaderSection.filters}}" stepKey="openUrlRewriteGridFilters"/> + <fillField selector="{{AdminDataGridHeaderSection.filterFieldInput('request_path')}}" userInput="category-english.html" stepKey="inputCategoryUrlForENStoreView"/> + <click selector="{{AdminDataGridHeaderSection.applyFilters}}" stepKey="clickOrderApplyFilters"/> + <waitForPageLoad stepKey="waitForPageToLoad"/> + <seeElement selector="{{AdminUrlRewriteIndexSection.gridCellByColumnValue('Request Path', 'category-english.html')}}" stepKey="seeUrlInRequestPathColumn"/> + <seeElement selector="{{AdminUrlRewriteIndexSection.gridCellByColumnValue('Target Path', catalog/category/view/id/$$createCategory.id$$)}}" stepKey="seeUrlInTargetPathColumn"/> + + <click selector="{{AdminDataGridHeaderSection.filters}}" stepKey="openUrlRewriteGridFilters1"/> + <fillField selector="{{AdminDataGridHeaderSection.filterFieldInput('request_path')}}" userInput="category-dutch.html" stepKey="inputCategoryUrlForNLStoreView"/> + <click selector="{{AdminDataGridHeaderSection.applyFilters}}" stepKey="clickOrderApplyFilters1"/> + <waitForPageLoad stepKey="waitForPageToLoad1"/> + <seeElement selector="{{AdminUrlRewriteIndexSection.gridCellByColumnValue('Request Path', 'category-dutch.html')}}" stepKey="seeUrlInRequestPathColumn1"/> + <seeElement selector="{{AdminUrlRewriteIndexSection.gridCellByColumnValue('Target Path', catalog/category/view/id/$$createCategory.id$$)}}" stepKey="seeUrlInTargetPathColumn1"/> + + <click selector="{{AdminDataGridHeaderSection.filters}}" stepKey="openUrlRewriteGridFilters2"/> + <fillField selector="{{AdminDataGridHeaderSection.filterFieldInput('request_path')}}" userInput="productformagetwo68980-english.html" stepKey="inputProductUrlForENStoreView"/> + <click selector="{{AdminDataGridHeaderSection.applyFilters}}" stepKey="clickOrderApplyFilters2"/> + <waitForPageLoad stepKey="waitForPageToLoad2"/> + <seeElement selector="{{AdminUrlRewriteIndexSection.gridCellByColumnValue('Request Path', 'productformagetwo68980-english.html')}}" stepKey="seeUrlInRequestPathColumn2"/> + <seeElement selector="{{AdminUrlRewriteIndexSection.gridCellByColumnValue('Target Path', 'catalog/product/view/id/$grabProductIdFromUrl')}}" stepKey="seeUrlInTargetPathColumn2"/> + + <click selector="{{AdminDataGridHeaderSection.filters}}" stepKey="openUrlRewriteGridFilters3"/> + <fillField selector="{{AdminDataGridHeaderSection.filterFieldInput('request_path')}}" userInput="productformagetwo68980-dutch.html" stepKey="inputProductUrlForENStoreView1"/> + <click selector="{{AdminDataGridHeaderSection.applyFilters}}" stepKey="clickOrderApplyFilters3"/> + <waitForPageLoad stepKey="waitForPageToLoad3"/> + <seeElement selector="{{AdminUrlRewriteIndexSection.gridCellByColumnValue('Request Path', 'productformagetwo68980-dutch.html')}}" stepKey="seeUrlInRequestPathColumn3"/> + <seeElement selector="{{AdminUrlRewriteIndexSection.gridCellByColumnValue('Target Path', 'catalog/product/view/id/$grabProductIdFromUrl')}}" stepKey="seeUrlInTargetPathColumn3"/> + + <click selector="{{AdminDataGridHeaderSection.filters}}" stepKey="openUrlRewriteGridFilters4"/> + <fillField selector="{{AdminDataGridHeaderSection.filterFieldInput('request_path')}}" userInput="category-english/productformagetwo68980-english.html" stepKey="inputProductUrlForENStoreView2"/> + <click selector="{{AdminDataGridHeaderSection.applyFilters}}" stepKey="clickOrderApplyFilters4"/> + <waitForPageLoad stepKey="waitForPageToLoad4"/> + <seeElement selector="{{AdminUrlRewriteIndexSection.gridCellByColumnValue('Request Path', 'category-english/productformagetwo68980-english.html')}}" stepKey="seeUrlInRequestPathColumn4"/> + <seeElement selector="{{AdminUrlRewriteIndexSection.gridCellByColumnValue('Target Path', catalog/product/view/id/$grabProductIdFromUrl/category/$$createCategory.id$$)}}" stepKey="seeUrlInTargetPathColumn4"/> + + <click selector="{{AdminDataGridHeaderSection.filters}}" stepKey="openUrlRewriteGridFilters5"/> + <fillField selector="{{AdminDataGridHeaderSection.filterFieldInput('request_path')}}" userInput="category-dutch/productformagetwo68980-dutch.html" stepKey="inputProductUrlForENStoreView3"/> + <click selector="{{AdminDataGridHeaderSection.applyFilters}}" stepKey="clickOrderApplyFilters5"/> + <waitForPageLoad stepKey="waitForPageToLoad5"/> + <seeElement selector="{{AdminUrlRewriteIndexSection.gridCellByColumnValue('Request Path', 'category-dutch/productformagetwo68980-dutch.html')}}" stepKey="seeUrlInRequestPathColumn5"/> + <seeElement selector="{{AdminUrlRewriteIndexSection.gridCellByColumnValue('Target Path', catalog/product/view/id/$grabProductIdFromUrl/category/$$createCategory.id$$)}}" stepKey="seeUrlInTargetPathColumn5"/> + </test> +</tests> diff --git a/app/code/Magento/UrlRewrite/Test/Mftf/Test/AdminCheckUrlRewritesCorrectlyGeneratedForMultipleStoreviewsDuringProductImportTest/AdminUrlRewriteGeneratedForMultipleStoreviewsDuringProductImportWithConfigTurnedOffTest.xml b/app/code/Magento/UrlRewrite/Test/Mftf/Test/AdminCheckUrlRewritesCorrectlyGeneratedForMultipleStoreviewsDuringProductImportTest/AdminUrlRewriteGeneratedForMultipleStoreviewsDuringProductImportWithConfigTurnedOffTest.xml new file mode 100644 index 0000000000000..f9003ce8c0897 --- /dev/null +++ b/app/code/Magento/UrlRewrite/Test/Mftf/Test/AdminCheckUrlRewritesCorrectlyGeneratedForMultipleStoreviewsDuringProductImportTest/AdminUrlRewriteGeneratedForMultipleStoreviewsDuringProductImportWithConfigTurnedOffTest.xml @@ -0,0 +1,155 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + /** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +--> +<tests xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/testSchema.xsd"> + <test name="AdminUrlRewriteGeneratedForMultipleStoreviewsDuringProductImportWithConfigTurnedOffTest"> + <annotations> + <features value="Url Rewrite"/> + <stories value="Url Rewrites for Multiple Storeviews"/> + <title value="Url Rewrites Correctly Generated for Multiple Storeviews During Product Import With Configuration Turned Off"/> + <description value="Check Url Rewrites Correctly Generated for Multiple Storeviews During Product Import."/> + <severity value="CRITICAL"/> + <testCaseId value="MC-6802"/> + <group value="urlRewrite"/> + </annotations> + <before> + <!-- Set the configuration for Generate "category/product" URL Rewrites to Yes (default)--> + <comment userInput="Enable SEO configuration setting to generate category/product URL Rewrites" stepKey="commentEnableUrlRewriteConfig"/> + <magentoCLI command="config:set catalog/seo/generate_category_product_rewrites 1" stepKey="enableGenerateUrlRewrite"/> + <!--Flush cache--> + <magentoCLI command="cache:flush" stepKey="cleanCache1"/> + <createData entity="ApiCategory" stepKey="createCategory"> + <field key="name">category-admin</field> + </createData> + <actionGroup ref="AdminLoginActionGroup" stepKey="loginAsAdmin"/> + <!-- Create Store View EN --> + <actionGroup ref="AdminCreateStoreViewActionGroup" stepKey="createStoreViewEn"> + <argument name="customStore" value="customStoreENNotUnique"/> + </actionGroup> + <!-- Create Store View NL --> + <actionGroup ref="AdminCreateStoreViewActionGroup" stepKey="createStoreViewNl"> + <argument name="customStore" value="customStoreNLNotUnique"/> + </actionGroup> + <!-- Set the configuration for Generate "category/product" URL Rewrites to No--> + <comment userInput="Disable SEO configuration setting to generate category/product URL Rewrites" stepKey="commentDisableUrlRewriteConfig"/> + <magentoCLI command="config:set catalog/seo/generate_category_product_rewrites 0" stepKey="disableGenerateUrlRewrite"/> + <!--Flush cache--> + <magentoCLI command="cache:flush" stepKey="cleanCache"/> + </before> + <after> + <deleteData createDataKey="createCategory" stepKey="deleteCategory"/> + <actionGroup ref="AdminDeleteStoreViewActionGroup" stepKey="deleteStoreViewEn"> + <argument name="customStore" value="customStoreENNotUnique"/> + </actionGroup> + <actionGroup ref="AdminDeleteStoreViewActionGroup" stepKey="deleteStoreViewNl"> + <argument name="customStore" value="customStoreNLNotUnique"/> + </actionGroup> + <actionGroup ref="ClearFiltersAdminDataGridActionGroup" stepKey="clearStoreGridFilters"/> + <actionGroup ref="DeleteProductByNameActionGroup" stepKey="deleteImportedProduct"> + <argument name="sku" value="productformagetwo68980"/> + <argument name="name" value="productformagetwo68980"/> + </actionGroup> + <actionGroup ref="ClearFiltersAdminDataGridActionGroup" stepKey="clearFiltersIfSet"/> + <actionGroup ref="AdminLogoutActionGroup" stepKey="logout"/> + <magentoCLI command="config:set catalog/seo/generate_category_product_rewrites 1" stepKey="resetConfigurationSetting"/> + <!--Flush cache--> + <magentoCLI command="cache:flush" stepKey="cleanCache2"/> + </after> + <actionGroup ref="SwitchCategoryStoreViewActionGroup" stepKey="switchToStoreViewEn"> + <argument name="Store" value="customStoreENNotUnique.name"/> + <argument name="CatName" value="$$createCategory.name$$"/> + </actionGroup> + <uncheckOption selector="{{AdminCategoryBasicFieldSection.categoryNameUseDefault}}" stepKey="uncheckUseDefaultValueENStoreView"/> + <fillField selector="{{AdminCategoryBasicFieldSection.CategoryNameInput}}" userInput="category-english" stepKey="changeNameField"/> + <click selector="{{AdminCategorySEOSection.SectionHeader}}" stepKey="clickOnSectionHeader"/> + <actionGroup ref="ChangeSeoUrlKeyForSubCategoryActionGroup" stepKey="changeSeoUrlKeyENStoreView"> + <argument name="value" value="category-english"/> + </actionGroup> + <actionGroup ref="SwitchCategoryStoreViewActionGroup" stepKey="switchToStoreViewNl"> + <argument name="Store" value="customStoreNLNotUnique.name"/> + <argument name="CatName" value="$$createCategory.name$$"/> + </actionGroup> + <uncheckOption selector="{{AdminCategoryBasicFieldSection.categoryNameUseDefault}}" stepKey="uncheckUseDefaultValue1"/> + <fillField selector="{{AdminCategoryBasicFieldSection.CategoryNameInput}}" userInput="category-dutch" stepKey="changeNameFieldNLStoreView"/> + <click selector="{{AdminCategorySEOSection.SectionHeader}}" stepKey="clickOnSectionHeader2"/> + <actionGroup ref="ChangeSeoUrlKeyForSubCategoryActionGroup" stepKey="changeSeoUrlKeyNLStoreView"> + <argument name="value" value="category-dutch"/> + </actionGroup> + <amOnPage url="{{AdminImportIndexPage.url}}" stepKey="navigateToSystemImport"/> + <selectOption selector="{{AdminImportMainSection.entityType}}" userInput="Products" stepKey="selectProductsOption"/> + <waitForElementVisible selector="{{AdminImportMainSection.importBehavior}}" stepKey="waitForImportBehaviorElementVisible"/> + <selectOption selector="{{AdminImportMainSection.importBehavior}}" userInput="Add/Update" stepKey="selectAddUpdateOption"/> + <attachFile selector="{{AdminImportMainSection.selectFileToImport}}" userInput="import_updated.csv" stepKey="attachFileForImport"/> + <click selector="{{AdminImportHeaderSection.checkDataButton}}" stepKey="clickCheckDataButton"/> + <see selector="{{AdminImportValidationMessagesSection.notice}}" userInput="Checked rows: 3, checked entities: 1, invalid rows: 0, total errors: 0" stepKey="assertNotice"/> + <see selector="{{AdminImportValidationMessagesSection.success}}" userInput="File is valid! To start import process press "Import" button" stepKey="assertSuccessMessage"/> + <click selector="{{AdminImportMainSection.importButton}}" stepKey="clickImportButton"/> + <see selector="{{AdminImportValidationMessagesSection.success}}" userInput="Import successfully done" stepKey="assertSuccessMessage1"/> + <see selector="{{AdminImportValidationMessagesSection.notice}}" userInput="Created: 1, Updated: 0, Deleted: 0" stepKey="assertNotice1"/> + <actionGroup ref="SearchForProductOnBackendByNameActionGroup" stepKey="searchForProductOnBackend"> + <argument name="productName" value="productformagetwo68980"/> + </actionGroup> + <click selector="{{AdminProductGridSection.productRowBySku('productformagetwo68980')}}" stepKey="clickOnProductRow"/> + <grabFromCurrentUrl regex="~/id/(\d+)/~" stepKey="grabProductIdFromUrl"/> + <amOnPage url="{{AdminUrlRewriteIndexPage.url}}" stepKey="goToUrlRewritesIndexPage"/> + + <click selector="{{AdminDataGridHeaderSection.filters}}" stepKey="openUrlRewriteGridFilters"/> + <fillField selector="{{AdminDataGridHeaderSection.filterFieldInput('request_path')}}" userInput="category-english.html" stepKey="inputCategoryUrlForENStoreView"/> + <click selector="{{AdminDataGridHeaderSection.applyFilters}}" stepKey="clickOrderApplyFilters"/> + <waitForPageLoad stepKey="waitForPageToLoad"/> + <seeElement selector="{{AdminUrlRewriteIndexSection.gridCellByColumnValue('Request Path', 'category-english.html')}}" stepKey="seeUrlInRequestPathColumn"/> + <seeElement selector="{{AdminUrlRewriteIndexSection.gridCellByColumnValue('Target Path', catalog/category/view/id/$$createCategory.id$$)}}" stepKey="seeUrlInTargetPathColumn"/> + + <click selector="{{AdminDataGridHeaderSection.filters}}" stepKey="openUrlRewriteGridFilters1"/> + <fillField selector="{{AdminDataGridHeaderSection.filterFieldInput('request_path')}}" userInput="category-dutch.html" stepKey="inputCategoryUrlForNLStoreView"/> + <click selector="{{AdminDataGridHeaderSection.applyFilters}}" stepKey="clickOrderApplyFilters1"/> + <waitForPageLoad stepKey="waitForPageToLoad1"/> + <seeElement selector="{{AdminUrlRewriteIndexSection.gridCellByColumnValue('Request Path', 'category-dutch.html')}}" stepKey="seeUrlInRequestPathColumn1"/> + <seeElement selector="{{AdminUrlRewriteIndexSection.gridCellByColumnValue('Target Path', catalog/category/view/id/$$createCategory.id$$)}}" stepKey="seeUrlInTargetPathColumn1"/> + + <click selector="{{AdminDataGridHeaderSection.filters}}" stepKey="openUrlRewriteGridFilters2"/> + <fillField selector="{{AdminDataGridHeaderSection.filterFieldInput('request_path')}}" userInput="productformagetwo68980-english.html" stepKey="inputProductUrlForENStoreView"/> + <click selector="{{AdminDataGridHeaderSection.applyFilters}}" stepKey="clickOrderApplyFilters2"/> + <waitForPageLoad stepKey="waitForPageToLoad2"/> + <seeElement selector="{{AdminUrlRewriteIndexSection.gridCellByColumnValue('Request Path', 'productformagetwo68980-english.html')}}" stepKey="seeUrlInRequestPathColumn2"/> + <seeElement selector="{{AdminUrlRewriteIndexSection.gridCellByColumnValue('Target Path', 'catalog/product/view/id/$grabProductIdFromUrl')}}" stepKey="seeUrlInTargetPathColumn2"/> + <dontSeeElement selector="{{AdminUrlRewriteIndexSection.gridCellByColumnValue('Request Path', 'category-english/productformagetwo68980-english.html')}}" stepKey="seeUrlInRequestPathColumn4"/> + <dontSeeElement selector="{{AdminUrlRewriteIndexSection.gridCellByColumnValue('Target Path', catalog/product/view/id/$grabProductIdFromUrl/category/$$createCategory.id$$)}}" stepKey="seeUrlInTargetPathColumn4"/> + + <click selector="{{AdminDataGridHeaderSection.filters}}" stepKey="openUrlRewriteGridFilters3"/> + <fillField selector="{{AdminDataGridHeaderSection.filterFieldInput('request_path')}}" userInput="productformagetwo68980-dutch.html" stepKey="inputProductUrlForENStoreView1"/> + <click selector="{{AdminDataGridHeaderSection.applyFilters}}" stepKey="clickOrderApplyFilters3"/> + <waitForPageLoad stepKey="waitForPageToLoad3"/> + <seeElement selector="{{AdminUrlRewriteIndexSection.gridCellByColumnValue('Request Path', 'productformagetwo68980-dutch.html')}}" stepKey="seeUrlInRequestPathColumn3"/> + <seeElement selector="{{AdminUrlRewriteIndexSection.gridCellByColumnValue('Target Path', 'catalog/product/view/id/$grabProductIdFromUrl')}}" stepKey="seeUrlInTargetPathColumn3"/> + <dontSeeElement selector="{{AdminUrlRewriteIndexSection.gridCellByColumnValue('Request Path', 'category-dutch/productformagetwo68980-dutch.html')}}" stepKey="seeUrlInRequestPathColumn5"/> + <dontSeeElement selector="{{AdminUrlRewriteIndexSection.gridCellByColumnValue('Target Path', catalog/product/view/id/$grabProductIdFromUrl/category/$$createCategory.id$$)}}" stepKey="seeUrlInTargetPathColumn5"/> + + <!-- Switch StoreView --> + <amOnPage url="{{StorefrontHomePage.url}}" stepKey="amOnProduct4Page"/> + <actionGroup ref="StorefrontSwitchStoreViewActionGroup" stepKey="switchToCustomStoreView"> + <argument name="storeView" value="customStoreENNotUnique"/> + </actionGroup> + + <amOnPage url="/productformagetwo68980-english.html" stepKey="navigateToProductPage"/> + <see selector="{{StorefrontProductInfoMainSection.productName}}" userInput="productformagetwo68980-english" stepKey="seeProductName"/> + <amOnPage url="/category-english/productformagetwo68980-english.html" stepKey="navigateToProductPage2"/> + <see selector="{{StorefrontProductInfoMainSection.productName}}" userInput="productformagetwo68980-english" stepKey="seeProductName2"/> + + <!-- Switch StoreView --> + <amOnPage url="{{StorefrontHomePage.url}}" stepKey="amOnProduct4Page2"/> + <actionGroup ref="StorefrontSwitchStoreViewActionGroup" stepKey="switchToCustomStoreView2"> + <argument name="storeView" value="customStoreNLNotUnique"/> + </actionGroup> + + <amOnPage url="/productformagetwo68980-dutch.html" stepKey="navigateToProductPage3"/> + <see selector="{{StorefrontProductInfoMainSection.productName}}" userInput="productformagetwo68980-dutch" stepKey="seeProductName3"/> + <amOnPage url="/category-dutch/productformagetwo68980-dutch.html" stepKey="navigateToProductPage4"/> + <see selector="{{StorefrontProductInfoMainSection.productName}}" userInput="productformagetwo68980-dutch" stepKey="seeProductName4"/> + </test> +</tests> diff --git a/app/code/Magento/UrlRewrite/Test/Mftf/Test/AdminCheckUrlRewritesInCatalogCategoriesAfterChangingUrlKeyForStoreViewAndMovingCategory2Test.xml b/app/code/Magento/UrlRewrite/Test/Mftf/Test/AdminCheckUrlRewritesInCatalogCategoriesAfterChangingUrlKeyForStoreViewAndMovingCategory2Test.xml new file mode 100644 index 0000000000000..20e6392091998 --- /dev/null +++ b/app/code/Magento/UrlRewrite/Test/Mftf/Test/AdminCheckUrlRewritesInCatalogCategoriesAfterChangingUrlKeyForStoreViewAndMovingCategory2Test.xml @@ -0,0 +1,87 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + /** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +--> + +<tests xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/testSchema.xsd"> + <test name="AdminCheckUrlRewritesInCatalogCategoriesAfterChangingUrlKeyForStoreViewAndMovingCategory2Test"> + <annotations> + <features value="UrlRewrite"/> + <stories value="Update url rewrites"/> + <title value="Check url rewrites in catalog categories after changing url key"/> + <description value="Check url rewrites in catalog categories after changing url key for store view and moving category"/> + <severity value="CRITICAL"/> + <testCaseId value="MC-25622"/> + <group value="catalog"/> + <group value="url_rewrite"/> + <group value="mtf_migrated"/> + </annotations> + <before> + <!-- Create two sub-categories in default category with simple products --> + <createData entity="_defaultCategory" stepKey="createFirstCategory"/> + <createData entity="_defaultProduct" stepKey="createFirstSimpleProduct"> + <requiredEntity createDataKey="createFirstCategory"/> + </createData> + <createData entity="_defaultCategory" stepKey="createSecondCategory"/> + <createData entity="_defaultProduct" stepKey="createSecondSimpleProduct"> + <requiredEntity createDataKey="createSecondCategory"/> + </createData> + + <!-- Log in to backend --> + <actionGroup ref="AdminLoginActionGroup" stepKey="loginAsAdmin"/> + + <!--Create additional Store View in Main Website Store --> + <actionGroup ref="AdminCreateStoreViewActionGroup" stepKey="createStoreView"/> + <magentoCLI command="indexer:reindex" stepKey="reindexAll"/> + </before> + + <after> + <deleteData createDataKey="createFirstCategory" stepKey="deleteFirstCategory"/> + <deleteData createDataKey="createSecondCategory" stepKey="deleteSecondCategory"/> + <deleteData createDataKey="createFirstSimpleProduct" stepKey="deleteFirstSimpleProduct"/> + <deleteData createDataKey="createSecondSimpleProduct" stepKey="deleteSecondSimpleProduct"/> + <actionGroup ref="AdminDeleteStoreViewActionGroup" stepKey="deleteStoreView"/> + <actionGroup ref="ClearFiltersAdminDataGridActionGroup" stepKey="clearWebsitesGridFilters"/> + <actionGroup ref="AdminLogoutActionGroup" stepKey="logoutFromAdmin"/> + </after> + + <!-- On the categories editing page change store view to created additional view --> + <actionGroup ref="SwitchCategoryStoreViewActionGroup" stepKey="openFirstCategoryAndSwitchToCustomStoreView"> + <argument name="Store" value="customStore.name"/> + <argument name="CatName" value="$createFirstCategory.name$"/> + </actionGroup> + + <!-- Change url key for category for first category; save --> + <actionGroup ref="ChangeSeoUrlKeyForSubCategoryActionGroup" stepKey="changeFirstCategoryUrlKey"> + <argument name="value" value="{{SimpleRootSubCategory.url_key}}"/> + </actionGroup> + + <!-- Change store view to "All store views" for first category --> + <actionGroup ref="SwitchCategoryToAllStoreViewActionGroup" stepKey="switchToAllStoreViews"> + <argument name="CatName" value="$createFirstCategory.name$"/> + </actionGroup> + + <!-- Move first category inside second category --> + <actionGroup ref="MoveCategoryActionGroup" stepKey="moveFirstCategoryInsideSecondCategory"> + <argument name="childCategory" value="$createFirstCategory.name$"/> + <argument name="parentCategory" value="$createSecondCategory.name$"/> + </actionGroup> + + <!-- Open first category storefront page --> + <amOnPage url="$createSecondCategory.custom_attributes[url_key]$/$createFirstCategory.custom_attributes[url_key]$.html" stepKey="openFirstCategoryStorefrontPage"/> + <waitForPageLoad stepKey="waitForFirstCategoryStorefrontPageLoad"/> + <see userInput="$createFirstSimpleProduct.name$" selector="{{StorefrontCategoryMainSection.productsList}}" stepKey="seeFirstProductInCategory"/> + + <!-- Switch to custom store view--> + <actionGroup ref="StorefrontSwitchStoreViewActionGroup" stepKey="switchToCustomStoreView"> + <argument name="storeView" value="customStore"/> + </actionGroup> + + <!-- Assert category url with custom store view --> + <seeInCurrentUrl url="{{SimpleRootSubCategory.url_key}}.html" stepKey="seeUpdatedUrlKey"/> + <see userInput="$createFirstSimpleProduct.name$" selector="{{StorefrontCategoryMainSection.productsList}}" stepKey="seeFirstProductInCategoryAgain"/> + </test> +</tests> diff --git a/app/code/Magento/UrlRewrite/Test/Mftf/Test/AdminCheckUrlRewritesInCatalogCategoriesAfterChangingUrlKeyForStoreViewAndMovingCategoryTest.xml b/app/code/Magento/UrlRewrite/Test/Mftf/Test/AdminCheckUrlRewritesInCatalogCategoriesAfterChangingUrlKeyForStoreViewAndMovingCategoryTest.xml deleted file mode 100644 index badda06b827ea..0000000000000 --- a/app/code/Magento/UrlRewrite/Test/Mftf/Test/AdminCheckUrlRewritesInCatalogCategoriesAfterChangingUrlKeyForStoreViewAndMovingCategoryTest.xml +++ /dev/null @@ -1,85 +0,0 @@ -<?xml version="1.0" encoding="UTF-8"?> -<!-- - /** - * Copyright © Magento, Inc. All rights reserved. - * See COPYING.txt for license details. - */ ---> - -<tests xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/testSchema.xsd"> - <test name="AdminCheckUrlRewritesInCatalogCategoriesAfterChangingUrlKeyForStoreViewAndMovingCategoryTest"> - <annotations> - <features value="Url Rewrite"/> - <stories value="Update url rewrites"/> - <title value="Check url rewrites in catalog categories after changing url key"/> - <description value="Check url rewrites in catalog categories after changing url key for store view and moving category"/> - <severity value="CRITICAL"/> - <testCaseId value="MC-5352"/> - <group value="url_rewrite"/> - <group value="mtf_migrated"/> - </annotations> - <before> - <!-- Create two sub-categories in default category with simple products --> - <createData entity="_defaultCategory" stepKey="createFirstCategory"/> - <createData entity="_defaultProduct" stepKey="createFirstSimpleProduct"> - <requiredEntity createDataKey="createFirstCategory"/> - </createData> - <createData entity="_defaultCategory" stepKey="createSecondCategory"/> - <createData entity="_defaultProduct" stepKey="createSecondSimpleProduct"> - <requiredEntity createDataKey="createSecondCategory"/> - </createData> - - <!-- Log in to backend --> - <actionGroup ref="LoginAsAdmin" stepKey="loginAsAdmin"/> - - <!--Create additional Store View in Main Website Store --> - <actionGroup ref="AdminCreateStoreViewActionGroup" stepKey="createStoreView"/> - - <!-- Reindex and flush the cache --> - <magentoCLI command="indexer:reindex" stepKey="runReindex"/> - <magentoCLI command="cache:flush" stepKey="cleanCache"/> - </before> - <after> - <deleteData createDataKey="createFirstCategory" stepKey="deleteFirstCategory"/> - <deleteData createDataKey="createSecondCategory" stepKey="deleteSecondCategory"/> - <deleteData createDataKey="createFirstSimpleProduct" stepKey="deleteFirstSimpleProduct"/> - <deleteData createDataKey="createSecondSimpleProduct" stepKey="deleteSecondSimpleProduct"/> - <actionGroup ref="AdminDeleteStoreViewActionGroup" stepKey="deleteStoreView"/> - <actionGroup ref="logout" stepKey="logout"/> - </after> - - <!-- On the categories editing page change store view to created additional view --> - <actionGroup ref="SwitchCategoryStoreViewActionGroup" stepKey="switchStoreView"> - <argument name="Store" value="customStore.name"/> - <argument name="CatName" value="$$createFirstCategory.name$$"/> - </actionGroup> - - <!-- Change url key for category for first category; save --> - <actionGroup ref="ChangeSeoUrlKeyForSubCategoryActionGroup" stepKey="changeUrlKey"> - <argument name="value" value="{{SimpleRootSubCategory.url_key}}"/> - </actionGroup> - - <!-- Change store view to "All store views" for first category --> - <actionGroup ref="SwitchCategoryToAllStoreViewActionGroup" stepKey="switchToAllStoreViewProduct"> - <argument name="CatName" value="$$createFirstCategory.name$$"/> - </actionGroup> - - <!-- Move first category inside second category --> - <actionGroup ref="MoveCategoryActionGroup" stepKey="moveFirstCategoryToSecondCategory"> - <argument name="childCategory" value="$$createFirstCategory.name$$"/> - <argument name="parentCategory" value="$$createSecondCategory.name$$"/> - </actionGroup> - - <!-- Switch default store view on store view created below for first category --> - <amOnPage url="{{StorefrontHomePage.url}}" stepKey="amOnStorefrontPage"/> - <waitForPageLoad stepKey="waitForStorefrontPageLoad"/> - <actionGroup ref="StorefrontSwitchStoreViewActionGroup" stepKey="storefrontSwitchStoreView"> - <argument name="storeView" value="customStore"/> - </actionGroup> - - <!-- Assert category url with custom store view --> - <amOnPage url="{{StorefrontHomePage.url}}$$createSecondCategory.name$$/{{SimpleRootSubCategory.url_key}}.html" stepKey="amOnCategoryPage"/> - <waitForPageLoad stepKey="waitForCategoryPageLoad"/> - <see userInput="$$createFirstSimpleProduct.name$$" selector="{{StorefrontCategoryMainSection.productsList}}" stepKey="seeProductInCategory"/> - </test> -</tests> diff --git a/app/code/Magento/UrlRewrite/Test/Mftf/Test/AdminCreateCategoryUrlRewriteAndAddNoRedirectTest.xml b/app/code/Magento/UrlRewrite/Test/Mftf/Test/AdminCreateCategoryUrlRewriteAndAddNoRedirectTest.xml index 732fc22aaf84a..7f82cbd506f20 100644 --- a/app/code/Magento/UrlRewrite/Test/Mftf/Test/AdminCreateCategoryUrlRewriteAndAddNoRedirectTest.xml +++ b/app/code/Magento/UrlRewrite/Test/Mftf/Test/AdminCreateCategoryUrlRewriteAndAddNoRedirectTest.xml @@ -18,12 +18,12 @@ </annotations> <before> - <actionGroup ref="LoginAsAdmin" stepKey="loginToAdminPanel"/> + <actionGroup ref="AdminLoginActionGroup" stepKey="loginToAdminPanel"/> <createData entity="_defaultCategory" stepKey="category"/> </before> <after> <deleteData createDataKey="category" stepKey="deleteRootCategory"/> - <actionGroup ref="logout" stepKey="logout"/> + <actionGroup ref="AdminLogoutActionGroup" stepKey="logout"/> </after> <!--Open Url Rewrite Index Page and update the Custom Url Rewrite, Store, Request Path, Redirect Type and Description --> diff --git a/app/code/Magento/UrlRewrite/Test/Mftf/Test/AdminCreateCategoryUrlRewriteAndAddPermanentRedirectTest.xml b/app/code/Magento/UrlRewrite/Test/Mftf/Test/AdminCreateCategoryUrlRewriteAndAddPermanentRedirectTest.xml index 867b3ee54161c..f0ac774756958 100644 --- a/app/code/Magento/UrlRewrite/Test/Mftf/Test/AdminCreateCategoryUrlRewriteAndAddPermanentRedirectTest.xml +++ b/app/code/Magento/UrlRewrite/Test/Mftf/Test/AdminCreateCategoryUrlRewriteAndAddPermanentRedirectTest.xml @@ -18,12 +18,12 @@ </annotations> <before> - <actionGroup ref="LoginAsAdmin" stepKey="loginToAdminPanel"/> + <actionGroup ref="AdminLoginActionGroup" stepKey="loginToAdminPanel"/> <createData entity="_defaultCategory" stepKey="category"/> </before> <after> <deleteData createDataKey="category" stepKey="deleteRootCategory"/> - <actionGroup ref="logout" stepKey="logout"/> + <actionGroup ref="AdminLogoutActionGroup" stepKey="logout"/> </after> <!--Open Url Rewrite Index Page and update the Custom Url Rewrite, Store, Request Path, Redirect Type and Description --> diff --git a/app/code/Magento/UrlRewrite/Test/Mftf/Test/AdminCreateCategoryUrlRewriteAndAddTemporaryRedirectTest.xml b/app/code/Magento/UrlRewrite/Test/Mftf/Test/AdminCreateCategoryUrlRewriteAndAddTemporaryRedirectTest.xml index ab18add56aeb9..5409a669211df 100644 --- a/app/code/Magento/UrlRewrite/Test/Mftf/Test/AdminCreateCategoryUrlRewriteAndAddTemporaryRedirectTest.xml +++ b/app/code/Magento/UrlRewrite/Test/Mftf/Test/AdminCreateCategoryUrlRewriteAndAddTemporaryRedirectTest.xml @@ -18,12 +18,12 @@ </annotations> <before> - <actionGroup ref="LoginAsAdmin" stepKey="loginToAdminPanel"/> + <actionGroup ref="AdminLoginActionGroup" stepKey="loginToAdminPanel"/> <createData entity="_defaultCategory" stepKey="category"/> </before> <after> <deleteData createDataKey="category" stepKey="deleteRootCategory"/> - <actionGroup ref="logout" stepKey="logout"/> + <actionGroup ref="AdminLogoutActionGroup" stepKey="logout"/> </after> <!--Open Url Rewrite Index Page and update the Custom Url Rewrite, Store, Request Path, Redirect Type and Description --> diff --git a/app/code/Magento/UrlRewrite/Test/Mftf/Test/AdminCreateCustomCMSPageUrlRewriteAndAddPermanentRedirectTest.xml b/app/code/Magento/UrlRewrite/Test/Mftf/Test/AdminCreateCustomCMSPageUrlRewriteAndAddPermanentRedirectTest.xml index bd4f7d7a32165..ec1a7586722ff 100644 --- a/app/code/Magento/UrlRewrite/Test/Mftf/Test/AdminCreateCustomCMSPageUrlRewriteAndAddPermanentRedirectTest.xml +++ b/app/code/Magento/UrlRewrite/Test/Mftf/Test/AdminCreateCustomCMSPageUrlRewriteAndAddPermanentRedirectTest.xml @@ -19,14 +19,14 @@ <before> <createData entity="simpleCmsPage" stepKey="createCMSPage"/> - <actionGroup ref="LoginAsAdmin" stepKey="login"/> + <actionGroup ref="AdminLoginActionGroup" stepKey="login"/> </before> <after> <deleteData createDataKey="createCMSPage" stepKey="deleteCMSPage"/> <actionGroup ref="AdminDeleteUrlRewriteActionGroup" stepKey="deleteCustomUrlRewrite"> <argument name="requestPath" value="{{defaultCmsPage.title}}"/> </actionGroup> - <actionGroup ref="logout" stepKey="logout"/> + <actionGroup ref="AdminLogoutActionGroup" stepKey="logout"/> </after> <!--Open CMS Edit Page and Get the CMS ID --> diff --git a/app/code/Magento/UrlRewrite/Test/Mftf/Test/AdminCreateCustomCMSPageUrlRewriteAndAddTemporaryRedirectTest.xml b/app/code/Magento/UrlRewrite/Test/Mftf/Test/AdminCreateCustomCMSPageUrlRewriteAndAddTemporaryRedirectTest.xml index 074140845c8a6..d3728cc58b775 100644 --- a/app/code/Magento/UrlRewrite/Test/Mftf/Test/AdminCreateCustomCMSPageUrlRewriteAndAddTemporaryRedirectTest.xml +++ b/app/code/Magento/UrlRewrite/Test/Mftf/Test/AdminCreateCustomCMSPageUrlRewriteAndAddTemporaryRedirectTest.xml @@ -19,14 +19,14 @@ <before> <createData entity="simpleCmsPage" stepKey="createCMSPage"/> - <actionGroup ref="LoginAsAdmin" stepKey="login"/> + <actionGroup ref="AdminLoginActionGroup" stepKey="login"/> </before> <after> <deleteData createDataKey="createCMSPage" stepKey="deleteCMSPage"/> <actionGroup ref="AdminDeleteUrlRewriteActionGroup" stepKey="deleteCustomUrlRewrite"> <argument name="requestPath" value="{{defaultCmsPage.title}}"/> </actionGroup> - <actionGroup ref="logout" stepKey="logout"/> + <actionGroup ref="AdminLogoutActionGroup" stepKey="logout"/> </after> <!--Open CMS Edit Page and Get the CMS ID --> diff --git a/app/code/Magento/UrlRewrite/Test/Mftf/Test/AdminCreateCustomCategoryUrlRewriteAndAddPermanentRedirectTest.xml b/app/code/Magento/UrlRewrite/Test/Mftf/Test/AdminCreateCustomCategoryUrlRewriteAndAddPermanentRedirectTest.xml index cdd7c334e35cd..a3ca2c84ceffb 100644 --- a/app/code/Magento/UrlRewrite/Test/Mftf/Test/AdminCreateCustomCategoryUrlRewriteAndAddPermanentRedirectTest.xml +++ b/app/code/Magento/UrlRewrite/Test/Mftf/Test/AdminCreateCustomCategoryUrlRewriteAndAddPermanentRedirectTest.xml @@ -13,20 +13,20 @@ <title value="Create custom URL rewrite, permanent"/> <description value="Login as Admin and create custom UrlRewrite and add redirect type permenent"/> <testCaseId value="MC-5343"/> - <severity value="CRITICAL"/> + <severity value="BLOCKER"/> <group value="mtf_migrated"/> </annotations> <before> <createData entity="SimpleSubCategory" stepKey="category"/> - <actionGroup ref="LoginAsAdmin" stepKey="login"/> + <actionGroup ref="AdminLoginActionGroup" stepKey="login"/> </before> <after> <deleteData createDataKey="category" stepKey="deleteCategory"/> <actionGroup ref="AdminDeleteUrlRewriteActionGroup" stepKey="deleteCustomUrlRewrite"> <argument name="requestPath" value="{{FirstLevelSubCat.name}}.html"/> </actionGroup> - <actionGroup ref="logout" stepKey="logout"/> + <actionGroup ref="AdminLogoutActionGroup" stepKey="logout"/> </after> <!-- Open Category Page and Get Category ID --> diff --git a/app/code/Magento/UrlRewrite/Test/Mftf/Test/AdminCreateCustomProductUrlRewriteAndAddTemporaryRedirectTest.xml b/app/code/Magento/UrlRewrite/Test/Mftf/Test/AdminCreateCustomProductUrlRewriteAndAddTemporaryRedirectTest.xml index 593c4282fc516..c007399faa654 100644 --- a/app/code/Magento/UrlRewrite/Test/Mftf/Test/AdminCreateCustomProductUrlRewriteAndAddTemporaryRedirectTest.xml +++ b/app/code/Magento/UrlRewrite/Test/Mftf/Test/AdminCreateCustomProductUrlRewriteAndAddTemporaryRedirectTest.xml @@ -19,14 +19,14 @@ <before> <createData entity="defaultSimpleProduct" stepKey="createProduct"/> - <actionGroup ref="LoginAsAdmin" stepKey="login"/> + <actionGroup ref="AdminLoginActionGroup" stepKey="login"/> </before> <after> <deleteData createDataKey="createProduct" stepKey="deleteProduct"/> <actionGroup ref="AdminDeleteUrlRewriteActionGroup" stepKey="deleteCustomUrlRewrite"> <argument name="requestPath" value="{{_defaultProduct.name}}.html"/> </actionGroup> - <actionGroup ref="logout" stepKey="logout"/> + <actionGroup ref="AdminLogoutActionGroup" stepKey="logout"/> </after> <!--Filter Product in product page and get the Product ID --> diff --git a/app/code/Magento/UrlRewrite/Test/Mftf/Test/AdminCreateProductURLRewriteAndAddNoRedirectTest.xml b/app/code/Magento/UrlRewrite/Test/Mftf/Test/AdminCreateProductURLRewriteAndAddNoRedirectTest.xml index c51030315b287..75fb4572bc336 100644 --- a/app/code/Magento/UrlRewrite/Test/Mftf/Test/AdminCreateProductURLRewriteAndAddNoRedirectTest.xml +++ b/app/code/Magento/UrlRewrite/Test/Mftf/Test/AdminCreateProductURLRewriteAndAddNoRedirectTest.xml @@ -18,12 +18,12 @@ </annotations> <before> - <actionGroup ref="LoginAsAdmin" stepKey="login"/> + <actionGroup ref="AdminLoginActionGroup" stepKey="login"/> <createData entity="defaultSimpleProduct" stepKey="createSimpleProduct"/> </before> <after> <deleteData createDataKey="createSimpleProduct" stepKey="deleteProduct"/> - <actionGroup ref="logout" stepKey="logout"/> + <actionGroup ref="AdminLogoutActionGroup" stepKey="logout"/> </after> <!--Filter and Select the created Product --> diff --git a/app/code/Magento/UrlRewrite/Test/Mftf/Test/AdminCreateProductURLRewriteWithCategoryAndAddTemporaryRedirectTest.xml b/app/code/Magento/UrlRewrite/Test/Mftf/Test/AdminCreateProductURLRewriteWithCategoryAndAddTemporaryRedirectTest.xml index d433aa7557094..1cd4a4747aa4a 100644 --- a/app/code/Magento/UrlRewrite/Test/Mftf/Test/AdminCreateProductURLRewriteWithCategoryAndAddTemporaryRedirectTest.xml +++ b/app/code/Magento/UrlRewrite/Test/Mftf/Test/AdminCreateProductURLRewriteWithCategoryAndAddTemporaryRedirectTest.xml @@ -18,7 +18,7 @@ </annotations> <before> - <actionGroup ref="LoginAsAdmin" stepKey="login"/> + <actionGroup ref="AdminLoginActionGroup" stepKey="login"/> <createData entity="SimpleSubCategory" stepKey="createCategory"/> <createData entity="SimpleProduct" stepKey="createSimpleProduct"> <requiredEntity createDataKey="createCategory"/> @@ -27,7 +27,7 @@ <after> <deleteData createDataKey="createCategory" stepKey="deleteCategory"/> <deleteData createDataKey="createSimpleProduct" stepKey="deleteProduct"/> - <actionGroup ref="logout" stepKey="logout"/> + <actionGroup ref="AdminLogoutActionGroup" stepKey="logout"/> </after> <!--Filter and Select the created Product --> diff --git a/app/code/Magento/UrlRewrite/Test/Mftf/Test/AdminCreateProductUrLRewriteAndAddPermanentRedirectTest.xml b/app/code/Magento/UrlRewrite/Test/Mftf/Test/AdminCreateProductUrLRewriteAndAddPermanentRedirectTest.xml index e1ff4f598943e..1a47130b14858 100644 --- a/app/code/Magento/UrlRewrite/Test/Mftf/Test/AdminCreateProductUrLRewriteAndAddPermanentRedirectTest.xml +++ b/app/code/Magento/UrlRewrite/Test/Mftf/Test/AdminCreateProductUrLRewriteAndAddPermanentRedirectTest.xml @@ -13,16 +13,16 @@ <title value="Create product URL rewrite, with permanent redirect"/> <description value="Login as admin, create product UrlRewrite and add Permanent redirect"/> <testCaseId value="MC-5341"/> - <severity value="CRITICAL"/> + <severity value="BLOCKER"/> <group value="mtf_migrated"/> </annotations> <before> - <actionGroup ref="LoginAsAdmin" stepKey="login"/> + <actionGroup ref="AdminLoginActionGroup" stepKey="login"/> <createData entity="defaultSimpleProduct" stepKey="createSimpleProduct"/> </before> <after> <deleteData createDataKey="createSimpleProduct" stepKey="deleteProduct"/> - <actionGroup ref="logout" stepKey="logout"/> + <actionGroup ref="AdminLogoutActionGroup" stepKey="logout"/> </after> <!--Filter and Select the created Product --> diff --git a/app/code/Magento/UrlRewrite/Test/Mftf/Test/AdminCreateProductUrLRewriteAndAddTemporaryRedirectTest.xml b/app/code/Magento/UrlRewrite/Test/Mftf/Test/AdminCreateProductUrLRewriteAndAddTemporaryRedirectTest.xml index 3fa4e6b7bad90..b6f3c8691e563 100644 --- a/app/code/Magento/UrlRewrite/Test/Mftf/Test/AdminCreateProductUrLRewriteAndAddTemporaryRedirectTest.xml +++ b/app/code/Magento/UrlRewrite/Test/Mftf/Test/AdminCreateProductUrLRewriteAndAddTemporaryRedirectTest.xml @@ -13,17 +13,17 @@ <title value="Create product URL rewrite, with temporary redirect"/> <description value="Login as admin, create product UrlRewrite and add Temporary redirect"/> <testCaseId value="MC-5340"/> - <severity value="CRITICAL"/> + <severity value="BLOCKER"/> <group value="mtf_migrated"/> </annotations> <before> - <actionGroup ref="LoginAsAdmin" stepKey="login"/> + <actionGroup ref="AdminLoginActionGroup" stepKey="login"/> <createData entity="defaultSimpleProduct" stepKey="createSimpleProduct"/> </before> <after> <deleteData createDataKey="createSimpleProduct" stepKey="deleteProduct"/> - <actionGroup ref="logout" stepKey="logout"/> + <actionGroup ref="AdminLogoutActionGroup" stepKey="logout"/> </after> <!--Filter and Select the created Product --> diff --git a/app/code/Magento/UrlRewrite/Test/Mftf/Test/AdminCreateProductWithSeveralWebsitesAndCheckURLRewritesTest.xml b/app/code/Magento/UrlRewrite/Test/Mftf/Test/AdminCreateProductWithSeveralWebsitesAndCheckURLRewritesTest.xml index 38a84f6a75f30..036d35d9c3258 100644 --- a/app/code/Magento/UrlRewrite/Test/Mftf/Test/AdminCreateProductWithSeveralWebsitesAndCheckURLRewritesTest.xml +++ b/app/code/Magento/UrlRewrite/Test/Mftf/Test/AdminCreateProductWithSeveralWebsitesAndCheckURLRewritesTest.xml @@ -24,7 +24,7 @@ <requiredEntity createDataKey="rootCategory"/> </createData> <createData entity="defaultSimpleProduct" stepKey="createProduct"/> - <actionGroup ref = "LoginAsAdmin" stepKey="loginAsAdmin"/> + <actionGroup ref="AdminLoginActionGroup" stepKey="loginAsAdmin"/> </before> <after> <actionGroup ref="DeleteCustomStoreActionGroup" stepKey="deleteStore1"> @@ -35,7 +35,7 @@ </actionGroup> <deleteData stepKey="deleteRootCategory" createDataKey="rootCategory"/> <deleteData stepKey="deleteProduct" createDataKey="createProduct"/> - <actionGroup ref="logout" stepKey="logout"/> + <actionGroup ref="AdminLogoutActionGroup" stepKey="logout"/> </after> <!-- Create first store --> @@ -95,8 +95,8 @@ <argument name="redirectType" value="No"/> <argument name="targetPath" value="catalog/category/view/id/{$categoryId}"/> </actionGroup> - <see selector="{{AdminUrlRewriteIndexSection.storeView('1')}}" userInput="{{customStoreGroup.name}}" stepKey="seeStoreValueForCategoryId"/> - <see selector="{{AdminUrlRewriteIndexSection.storeView('1')}}" userInput="{{customStoreEN.name}}" stepKey="seeStoreViewValueForCategoryId"/> + <see selector="{{AdminUrlRewriteIndexSection.gridCellByColumnRowNumber('1', 'Store View')}}" userInput="{{customStoreGroup.name}}" stepKey="seeStoreValueForCategoryId"/> + <see selector="{{AdminUrlRewriteIndexSection.gridCellByColumnRowNumber('1', 'Store View')}}" userInput="{{customStoreEN.name}}" stepKey="seeStoreViewValueForCategoryId"/> <!-- Grab product Id --> <actionGroup ref="FilterAndSelectProductActionGroup" stepKey="grabProductId"> @@ -109,7 +109,7 @@ <argument name="redirectType" value="No"/> <argument name="targetPath" value="catalog/product/view/id/{$productId}"/> </actionGroup> - <see selector="{{AdminUrlRewriteIndexSection.storeView('1')}}" userInput="{{customStore.name}}" stepKey="seeStoreValueForProductId"/> - <see selector="{{AdminUrlRewriteIndexSection.storeView('1')}}" userInput="{{storeViewData.name}}" stepKey="seeStoreViewValueForProductId"/> + <see selector="{{AdminUrlRewriteIndexSection.gridCellByColumnRowNumber('1', 'Store View')}}" userInput="{{customStore.name}}" stepKey="seeStoreValueForProductId"/> + <see selector="{{AdminUrlRewriteIndexSection.gridCellByColumnRowNumber('1', 'Store View')}}" userInput="{{storeViewData.name}}" stepKey="seeStoreViewValueForProductId"/> </test> </tests> diff --git a/app/code/Magento/UrlRewrite/Test/Mftf/Test/AdminDeleteCMSPageNoRedirectUrlRewriteTest.xml b/app/code/Magento/UrlRewrite/Test/Mftf/Test/AdminDeleteCMSPageNoRedirectUrlRewriteTest.xml new file mode 100644 index 0000000000000..19eaa0029c39a --- /dev/null +++ b/app/code/Magento/UrlRewrite/Test/Mftf/Test/AdminDeleteCMSPageNoRedirectUrlRewriteTest.xml @@ -0,0 +1,57 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + /** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +--> +<tests xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/testSchema.xsd"> + <test name="AdminDeleteCMSPageNoRedirectUrlRewriteTest"> + <annotations> + <stories value="Delete CMS Page No Redirect URL rewrite"/> + <title value="Delete CMS Page No Redirect URL rewrite"/> + <description value="Login as Admin and remove CMS Page Url Rewrite"/> + <testCaseId value=""/> + <severity value="CRITICAL"/> + <group value="mtf_migrated"/> + </annotations> + + <before> + <actionGroup ref="AdminLoginActionGroup" stepKey="login"/> + <createData entity="simpleCmsPage" stepKey="createCMSPage"/> + </before> + <after> + <deleteData createDataKey="createCMSPage" stepKey="deleteCMSPage"/> + <actionGroup ref="AdminLogoutActionGroup" stepKey="logout"/> + </after> + + <!--Open CMS Edit Page and Get the CMS ID --> + <actionGroup ref="NavigateToCreatedCMSPageActionGroup" stepKey="navigateToCreatedCMSPage"> + <argument name="CMSPage" value="$$createCMSPage$$"/> + </actionGroup> + <grabFromCurrentUrl stepKey="cmsId" regex="#\/([0-9]*)?\/$#"/> + + <!-- Assert initial CMS page Url Rewrite in Grid--> + <actionGroup ref="AdminSearchByRequestPathActionGroup" stepKey="searchByRequestPath1"> + <argument name="redirectPath" value="$$createCMSPage.identifier$$"/> + <argument name="redirectType" value="No"/> + <argument name="targetPath" value="cms/page/view/page_id/{$cmsId}"/> + </actionGroup> + + <!-- Assert initial request path directs to the CMS Page on Store Front --> + <actionGroup ref="NavigateToStorefrontForCreatedPageActionGroup" stepKey="navigateToTheStoreFront1"> + <argument name="page" value="$$createCMSPage.identifier$$"/> + </actionGroup> + + <!--Delete created cms page url rewrite and verify AssertUrlRewriteDeletedMessage--> + <actionGroup ref="AdminDeleteUrlRewriteActionGroup" stepKey="deleteUrlRewrite"> + <argument name="requestPath" value="$$createCMSPage.identifier$$"/> + </actionGroup> + + <!--Search and verify AssertUrlRewriteNotInGrid--> + <actionGroup ref="AdminSearchDeletedUrlRewriteActionGroup" stepKey="searchDeletedUrlRewriteInGrid"> + <argument name="requestPath" value="$$createCMSPage.identifier$$"/> + </actionGroup> + </test> +</tests> diff --git a/app/code/Magento/UrlRewrite/Test/Mftf/Test/AdminDeleteCMSPagePermanentRedirectUrlRewriteTest.xml b/app/code/Magento/UrlRewrite/Test/Mftf/Test/AdminDeleteCMSPagePermanentRedirectUrlRewriteTest.xml new file mode 100644 index 0000000000000..8bf9516720f90 --- /dev/null +++ b/app/code/Magento/UrlRewrite/Test/Mftf/Test/AdminDeleteCMSPagePermanentRedirectUrlRewriteTest.xml @@ -0,0 +1,98 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + /** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +--> +<tests xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/testSchema.xsd"> + <test name="AdminDeleteCMSPagePermanentRedirectUrlRewriteTest"> + <annotations> + <stories value="Delete CMS Page Permanent Redirect URL rewrite"/> + <title value="Delete CMS Page Permanent Redirect URL rewrite"/> + <description value="Login as Admin and create custom CMS page UrlRewrite and add Permanent redirect type and delete Permanent redirect URL rewrite"/> + <testCaseId value=""/> + <severity value="CRITICAL"/> + <group value="mtf_migrated"/> + </annotations> + + <before> + <actionGroup ref="AdminLoginActionGroup" stepKey="login"/> + <createData entity="simpleCmsPage" stepKey="createCMSPage"/> + </before> + <after> + <deleteData createDataKey="createCMSPage" stepKey="deleteCMSPage"/> + <actionGroup ref="AdminLogoutActionGroup" stepKey="logout"/> + </after> + + <!--Open CMS Edit Page and Get the CMS ID --> + <actionGroup ref="NavigateToCreatedCMSPageActionGroup" stepKey="navigateToCreatedCMSPage"> + <argument name="CMSPage" value="$$createCMSPage$$"/> + </actionGroup> + <grabFromCurrentUrl stepKey="cmsId" regex="#\/([0-9]*)?\/$#"/> + + <!-- Open UrlRewrite Edit page and update the fields and fill the created CMS Page Target Path --> + <actionGroup ref="AdminAddCustomUrlRewriteActionGroup" stepKey="addCustomUrlRewrite"> + <argument name="customUrlRewriteValue" value="Custom"/> + <argument name="storeValue" value="Default Store View"/> + <argument name="requestPath" value="{{defaultCmsPage.title}}"/> + <argument name="redirectTypeValue" value="Permanent (301)"/> + <argument name="targetPath" value="cms/page/view/page_id/{$cmsId}"/> + <argument name="description" value="Created New CMS Page."/> + </actionGroup> + + <!-- Assert updated CMS page Url Rewrite in Grid --> + <actionGroup ref="AdminSearchByRequestPathActionGroup" stepKey="searchByRequestPath"> + <argument name="redirectPath" value="{{defaultCmsPage.title}}"/> + <argument name="redirectType" value="Permanent (301)"/> + <argument name="targetPath" value="cms/page/view/page_id/{$cmsId}"/> + </actionGroup> + + <!-- Assert initial CMS page Url Rewrite in Grid--> + <actionGroup ref="AdminSearchByRequestPathActionGroup" stepKey="searchByRequestPath1"> + <argument name="redirectPath" value="$$createCMSPage.identifier$$"/> + <argument name="redirectType" value="No"/> + <argument name="targetPath" value="cms/page/view/page_id/{$cmsId}"/> + </actionGroup> + + <!-- Assert added Request Path redirects to the CMS Page on Store Front --> + <actionGroup ref="NavigateToStorefrontForCreatedPageActionGroup" stepKey="navigateToTheStoreFront"> + <argument name="page" value="{{defaultCmsPage.title}}"/> + </actionGroup> + + <!-- Assert added CMS redirect in Store Front --> + <actionGroup ref="AssertStoreFrontCMSPageActionGroup" stepKey="assertCMSPage"> + <argument name="cmsTitle" value="$$createCMSPage.title$$"/> + <argument name="cmsContent" value="$$createCMSPage.content$$"/> + <argument name="cmsContentHeading" value="$$createCMSPage.content_heading$$"/> + </actionGroup> + + <!-- Assert initial request path directs to the CMS Page on Store Front --> + <actionGroup ref="NavigateToStorefrontForCreatedPageActionGroup" stepKey="navigateToTheStoreFront1"> + <argument name="page" value="$$createCMSPage.identifier$$"/> + </actionGroup> + + <!-- Assert initial CMS redirect in Store Front --> + <actionGroup ref="AssertStoreFrontCMSPageActionGroup" stepKey="assertCMSPage1"> + <argument name="cmsTitle" value="$$createCMSPage.title$$"/> + <argument name="cmsContent" value="$$createCMSPage.content$$"/> + <argument name="cmsContentHeading" value="$$createCMSPage.content_heading$$"/> + </actionGroup> + + <!--Delete added CMS Page Url Rewrite and verify AssertUrlRewriteDeletedMessage--> + <actionGroup ref="AdminDeleteUrlRewriteActionGroup" stepKey="deleteUrlRewrite"> + <argument name="requestPath" value="{{defaultCmsPage.title}}"/> + </actionGroup> + + <!--Search and verify AssertUrlRewriteNotInGrid--> + <actionGroup ref="AdminSearchDeletedUrlRewriteActionGroup" stepKey="searchDeletedUrlRewriteInGrid"> + <argument name="requestPath" value="{{defaultCmsPage.title}}"/> + </actionGroup> + + <!--Verify AssertPageByUrlRewriteIsNotFound--> + <actionGroup ref="AssertPageByUrlRewriteIsNotFoundActionGroup" stepKey="amOnPage"> + <argument name="requestPath" value="{{defaultCmsPage.title}}"/> + </actionGroup> + </test> +</tests> diff --git a/app/code/Magento/UrlRewrite/Test/Mftf/Test/AdminDeleteCMSPageTemporaryRedirectUrlRewriteTest.xml b/app/code/Magento/UrlRewrite/Test/Mftf/Test/AdminDeleteCMSPageTemporaryRedirectUrlRewriteTest.xml new file mode 100644 index 0000000000000..91bdd4a22059c --- /dev/null +++ b/app/code/Magento/UrlRewrite/Test/Mftf/Test/AdminDeleteCMSPageTemporaryRedirectUrlRewriteTest.xml @@ -0,0 +1,98 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + /** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +--> +<tests xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/testSchema.xsd"> + <test name="AdminDeleteCMSPageTemporaryRedirectUrlRewriteTest"> + <annotations> + <stories value="Delete CMS Page Temporary Redirect URL rewrite"/> + <title value="Delete CMS Page Temporary Redirect URL rewrite"/> + <description value="Login as Admin and create custom CMS page UrlRewrite and add Temporary redirect type and delete Temporary redirect URL rewrite"/> + <testCaseId value=""/> + <severity value="CRITICAL"/> + <group value="mtf_migrated"/> + </annotations> + + <before> + <actionGroup ref="AdminLoginActionGroup" stepKey="login"/> + <createData entity="simpleCmsPage" stepKey="createCMSPage"/> + </before> + <after> + <deleteData createDataKey="createCMSPage" stepKey="deleteCMSPage"/> + <actionGroup ref="AdminLogoutActionGroup" stepKey="logout"/> + </after> + + <!--Open CMS Edit Page and Get the CMS ID --> + <actionGroup ref="NavigateToCreatedCMSPageActionGroup" stepKey="navigateToCreatedCMSPage"> + <argument name="CMSPage" value="$$createCMSPage$$"/> + </actionGroup> + <grabFromCurrentUrl stepKey="cmsId" regex="#\/([0-9]*)?\/$#"/> + + <!-- Open UrlRewrite Edit page and update the fields and fill the created CMS Page Target Path --> + <actionGroup ref="AdminAddCustomUrlRewriteActionGroup" stepKey="addCustomUrlRewrite"> + <argument name="customUrlRewriteValue" value="Custom"/> + <argument name="storeValue" value="Default Store View"/> + <argument name="requestPath" value="{{defaultCmsPage.title}}"/> + <argument name="redirectTypeValue" value="Temporary (302)"/> + <argument name="targetPath" value="cms/page/view/page_id/{$cmsId}"/> + <argument name="description" value="Created New CMS Page."/> + </actionGroup> + + <!-- Assert updated CMS page Url Rewrite in Grid --> + <actionGroup ref="AdminSearchByRequestPathActionGroup" stepKey="searchByRequestPath"> + <argument name="redirectPath" value="{{defaultCmsPage.title}}"/> + <argument name="redirectType" value="Temporary (302)"/> + <argument name="targetPath" value="cms/page/view/page_id/{$cmsId}"/> + </actionGroup> + + <!-- Assert initial CMS page Url Rewrite in Grid--> + <actionGroup ref="AdminSearchByRequestPathActionGroup" stepKey="searchByRequestPath1"> + <argument name="redirectPath" value="$$createCMSPage.identifier$$"/> + <argument name="redirectType" value="No"/> + <argument name="targetPath" value="cms/page/view/page_id/{$cmsId}"/> + </actionGroup> + + <!-- Assert Updated Request Path redirects to the CMS Page on Store Front --> + <actionGroup ref="NavigateToStorefrontForCreatedPageActionGroup" stepKey="navigateToTheStoreFront"> + <argument name="page" value="{{defaultCmsPage.title}}"/> + </actionGroup> + + <!-- Assert updated CMS redirect in Store Front --> + <actionGroup ref="AssertStoreFrontCMSPageActionGroup" stepKey="assertCMSPage"> + <argument name="cmsTitle" value="$$createCMSPage.title$$"/> + <argument name="cmsContent" value="$$createCMSPage.content$$"/> + <argument name="cmsContentHeading" value="$$createCMSPage.content_heading$$"/> + </actionGroup> + + <!-- Assert initial request path directs to the CMS Page on Store Front --> + <actionGroup ref="NavigateToStorefrontForCreatedPageActionGroup" stepKey="navigateToTheStoreFront1"> + <argument name="page" value="$$createCMSPage.identifier$$"/> + </actionGroup> + + <!-- Assert initial CMS redirect in Store Front --> + <actionGroup ref="AssertStoreFrontCMSPageActionGroup" stepKey="assertCMSPage1"> + <argument name="cmsTitle" value="$$createCMSPage.title$$"/> + <argument name="cmsContent" value="$$createCMSPage.content$$"/> + <argument name="cmsContentHeading" value="$$createCMSPage.content_heading$$"/> + </actionGroup> + + <!--Delete added CMS Page Url Rewrite and verify AssertUrlRewriteDeletedMessage--> + <actionGroup ref="AdminDeleteUrlRewriteActionGroup" stepKey="deleteUrlRewrite"> + <argument name="requestPath" value="{{defaultCmsPage.title}}"/> + </actionGroup> + + <!--Search and verify AssertUrlRewriteNotInGrid--> + <actionGroup ref="AdminSearchDeletedUrlRewriteActionGroup" stepKey="searchDeletedUrlRewriteInGrid"> + <argument name="requestPath" value="{{defaultCmsPage.title}}"/> + </actionGroup> + + <!--Verify AssertPageByUrlRewriteIsNotFound--> + <actionGroup ref="AssertPageByUrlRewriteIsNotFoundActionGroup" stepKey="amOnPage"> + <argument name="requestPath" value="{{defaultCmsPage.title}}"/> + </actionGroup> + </test> +</tests> diff --git a/app/code/Magento/UrlRewrite/Test/Mftf/Test/AdminDeleteCategoryUrlRewriteHypenAsRequestPathTest.xml b/app/code/Magento/UrlRewrite/Test/Mftf/Test/AdminDeleteCategoryUrlRewriteHypenAsRequestPathTest.xml index 4b9f37f628f34..6dddc0f333ca5 100644 --- a/app/code/Magento/UrlRewrite/Test/Mftf/Test/AdminDeleteCategoryUrlRewriteHypenAsRequestPathTest.xml +++ b/app/code/Magento/UrlRewrite/Test/Mftf/Test/AdminDeleteCategoryUrlRewriteHypenAsRequestPathTest.xml @@ -14,17 +14,18 @@ <title value="Delete category URL rewrite, hyphen as request path"/> <description value="Delete category URL rewrite, hyphen as request path"/> <testCaseId value="MC-5348" /> + <severity value="MAJOR"/> <group value="urlRewrite"/> <group value="mtf_migrated"/> </annotations> <before> - <actionGroup ref="LoginAsAdmin" stepKey="loginToAdminPanel"/> + <actionGroup ref="AdminLoginActionGroup" stepKey="loginToAdminPanel"/> <createData entity="_defaultCategory" stepKey="category"/> </before> <after> <deleteData createDataKey="category" stepKey="deleteCategory"/> - <actionGroup ref="logout" stepKey="logout"/> + <actionGroup ref="AdminLogoutActionGroup" stepKey="logout"/> </after> <!--Create the Category Url Rewrite--> diff --git a/app/code/Magento/UrlRewrite/Test/Mftf/Test/AdminDeleteCategoryUrlRewriteTest.xml b/app/code/Magento/UrlRewrite/Test/Mftf/Test/AdminDeleteCategoryUrlRewriteTest.xml new file mode 100644 index 0000000000000..6596d2b8fdac4 --- /dev/null +++ b/app/code/Magento/UrlRewrite/Test/Mftf/Test/AdminDeleteCategoryUrlRewriteTest.xml @@ -0,0 +1,56 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + /** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +--> +<tests xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/testSchema.xsd"> + <test name="AdminDeleteCategoryUrlRewriteTest"> + <annotations> + <stories value="Delete Category URL rewrite"/> + <title value="Delete Category URL rewrite"/> + <description value="Test log in to URL rewrite and Delete Category URL rewrite"/> + <testCaseId value=""/> + <severity value="CRITICAL"/> + <group value="mtf_migrated"/> + </annotations> + <before> + <actionGroup ref="AdminLoginActionGroup" stepKey="loginToAdminPanel"/> + <createData entity="_defaultCategory" stepKey="category"/> + </before> + <after> + <deleteData createDataKey="category" stepKey="deleteCategory"/> + <actionGroup ref="AdminLogoutActionGroup" stepKey="logout"/> + </after> + + <!--Open Url Rewrite Index Page and update the Category Url Rewrite, Store, Request Path, Redirect Type and Description --> + <actionGroup ref="AdminAddUrlRewriteActionGroup" stepKey="addUrlRewrite"> + <argument name="category" value="$$category.name$$"/> + <argument name="customUrlRewriteValue" value="For Category'"/> + <argument name="storeValue" value="Default Store View"/> + <argument name="requestPath" value="newcategoryrequestpath.html"/> + <argument name="redirectTypeValue" value="No"/> + <argument name="description" value="End To End Test"/> + </actionGroup> + + <!-- Get Category ID --> + <actionGroup ref="OpenCategoryFromCategoryTreeActionGroup" stepKey="getCategoryId"> + <argument name="category" value="$$category.name$$"/> + </actionGroup> + <grabFromCurrentUrl stepKey="categoryId" regex="#\/([0-9]*)?\/$#"/> + + <!--Delete created product url rewrite and verify AssertUrlRewriteDeletedMessage--> + <actionGroup ref="AdminDeleteUrlRewriteActionGroup" stepKey="deleteUrlRewrite"> + <argument name="requestPath" value="newcategoryrequestpath.html"/> + </actionGroup> + <!--Search and verify AssertUrlRewriteNotInGrid--> + <actionGroup ref="AdminSearchDeletedUrlRewriteActionGroup" stepKey="searchDeletedUrlRewriteInGrid"> + <argument name="requestPath" value="newcategoryrequestpath.html"/> + </actionGroup> + <!--Verify AssertPageByUrlRewriteIsNotFound--> + <actionGroup ref="AssertPageByUrlRewriteIsNotFoundActionGroup" stepKey="amOnPage"> + <argument name="requestPath" value="newcategoryrequestpath.html"/> + </actionGroup> + </test> +</tests> diff --git a/app/code/Magento/UrlRewrite/Test/Mftf/Test/AdminDeleteCategoryUrlRewriteWithRequestPathTest.xml b/app/code/Magento/UrlRewrite/Test/Mftf/Test/AdminDeleteCategoryUrlRewriteWithRequestPathTest.xml index 7c4023c6d0f75..8113d4511d8da 100644 --- a/app/code/Magento/UrlRewrite/Test/Mftf/Test/AdminDeleteCategoryUrlRewriteWithRequestPathTest.xml +++ b/app/code/Magento/UrlRewrite/Test/Mftf/Test/AdminDeleteCategoryUrlRewriteWithRequestPathTest.xml @@ -14,17 +14,18 @@ <title value="Delete category URL rewrite, with request path"/> <description value="Delete category URL rewrite, with request path"/> <testCaseId value="MC-5349" /> + <severity value="MAJOR"/> <group value="urlRewrite"/> <group value="mtf_migrated"/> </annotations> <before> - <actionGroup ref="LoginAsAdmin" stepKey="loginToAdminPanel"/> + <actionGroup ref="AdminLoginActionGroup" stepKey="loginToAdminPanel"/> <createData entity="_defaultCategory" stepKey="category"/> </before> <after> <deleteData createDataKey="category" stepKey="deleteCategory"/> - <actionGroup ref="logout" stepKey="logout"/> + <actionGroup ref="AdminLogoutActionGroup" stepKey="logout"/> </after> <!--Create the Category Url Rewrite--> diff --git a/app/code/Magento/UrlRewrite/Test/Mftf/Test/AdminDeleteCmsPageUrlRewriteWithNoRedirectsTest.xml b/app/code/Magento/UrlRewrite/Test/Mftf/Test/AdminDeleteCmsPageUrlRewriteWithNoRedirectsTest.xml index c40dd3256114e..1d582582684fa 100644 --- a/app/code/Magento/UrlRewrite/Test/Mftf/Test/AdminDeleteCmsPageUrlRewriteWithNoRedirectsTest.xml +++ b/app/code/Magento/UrlRewrite/Test/Mftf/Test/AdminDeleteCmsPageUrlRewriteWithNoRedirectsTest.xml @@ -13,16 +13,17 @@ <stories value="Delete CMS Page URL rewrite with No Redirects"/> <title value="Delete CMS Page URL rewrite with No Redirects"/> <description value="Log in to admin and delete CMS Page URL rewrite with No Redirects"/> + <severity value="CRITICAL"/> <testCaseId value="MC-14648"/> <group value="mtf_migrated"/> </annotations> <before> <createData entity="simpleCmsPage" stepKey="createCMSPage"/> - <actionGroup ref="LoginAsAdmin" stepKey="loginToAdminPanel"/> + <actionGroup ref="AdminLoginActionGroup" stepKey="loginToAdminPanel"/> </before> <after> <deleteData createDataKey="createCMSPage" stepKey="deleteCMSPage"/> - <actionGroup ref="logout" stepKey="logout"/> + <actionGroup ref="AdminLogoutActionGroup" stepKey="logout"/> </after> <actionGroup ref="AdminGoToAddNewUrlRewritePageActionGroup" stepKey="openUrlRewriteEditPage"/> diff --git a/app/code/Magento/UrlRewrite/Test/Mftf/Test/AdminDeleteCmsPageUrlRewriteWithPermanentRedirectTest.xml b/app/code/Magento/UrlRewrite/Test/Mftf/Test/AdminDeleteCmsPageUrlRewriteWithPermanentRedirectTest.xml index 741be6985d517..bd51fc9ce81d1 100644 --- a/app/code/Magento/UrlRewrite/Test/Mftf/Test/AdminDeleteCmsPageUrlRewriteWithPermanentRedirectTest.xml +++ b/app/code/Magento/UrlRewrite/Test/Mftf/Test/AdminDeleteCmsPageUrlRewriteWithPermanentRedirectTest.xml @@ -14,15 +14,16 @@ <title value="Delete CMS Page URL rewrite with Permanent Redirect"/> <description value="Log in to admin and delete CMS Page URL rewrite with Permanent Redirect"/> <testCaseId value="MC-14649"/> + <severity value="CRITICAL"/> <group value="mtf_migrated"/> </annotations> <before> <createData entity="simpleCmsPage" stepKey="createCMSPage"/> - <actionGroup ref="LoginAsAdmin" stepKey="loginToAdminPanel"/> + <actionGroup ref="AdminLoginActionGroup" stepKey="loginToAdminPanel"/> </before> <after> <deleteData createDataKey="createCMSPage" stepKey="deleteCMSPage"/> - <actionGroup ref="logout" stepKey="logout"/> + <actionGroup ref="AdminLogoutActionGroup" stepKey="logout"/> </after> <actionGroup ref="AdminGoToAddNewUrlRewritePageActionGroup" stepKey="openUrlRewriteEditPage"/> diff --git a/app/code/Magento/UrlRewrite/Test/Mftf/Test/AdminDeleteCmsPageUrlRewriteWithTemporaryRedirectTest.xml b/app/code/Magento/UrlRewrite/Test/Mftf/Test/AdminDeleteCmsPageUrlRewriteWithTemporaryRedirectTest.xml index 43de4123f35a8..c1deef78fcd7a 100644 --- a/app/code/Magento/UrlRewrite/Test/Mftf/Test/AdminDeleteCmsPageUrlRewriteWithTemporaryRedirectTest.xml +++ b/app/code/Magento/UrlRewrite/Test/Mftf/Test/AdminDeleteCmsPageUrlRewriteWithTemporaryRedirectTest.xml @@ -14,15 +14,16 @@ <title value="Delete CMS Page URL rewrite with Temporary Redirect"/> <description value="Log in to admin and delete CMS Page URL rewrite with Temporary Redirect"/> <testCaseId value="MC-14650"/> + <severity value="CRITICAL"/> <group value="mtf_migrated"/> </annotations> <before> <createData entity="simpleCmsPage" stepKey="createCMSPage"/> - <actionGroup ref="LoginAsAdmin" stepKey="loginToAdminPanel"/> + <actionGroup ref="AdminLoginActionGroup" stepKey="loginToAdminPanel"/> </before> <after> <deleteData createDataKey="createCMSPage" stepKey="deleteCMSPage"/> - <actionGroup ref="logout" stepKey="logout"/> + <actionGroup ref="AdminLogoutActionGroup" stepKey="logout"/> </after> <actionGroup ref="AdminGoToAddNewUrlRewritePageActionGroup" stepKey="openUrlRewriteEditPage"/> diff --git a/app/code/Magento/UrlRewrite/Test/Mftf/Test/AdminDeleteCustomUrlRewriteTest.xml b/app/code/Magento/UrlRewrite/Test/Mftf/Test/AdminDeleteCustomUrlRewriteTest.xml index 288c32102c606..293c412570742 100644 --- a/app/code/Magento/UrlRewrite/Test/Mftf/Test/AdminDeleteCustomUrlRewriteTest.xml +++ b/app/code/Magento/UrlRewrite/Test/Mftf/Test/AdminDeleteCustomUrlRewriteTest.xml @@ -20,10 +20,10 @@ <before> <createData entity="defaultUrlRewrite" stepKey="urlRewrite" /> - <actionGroup ref="LoginAsAdmin" stepKey="loginToAdminPanel"/> + <actionGroup ref="AdminLoginActionGroup" stepKey="loginToAdminPanel"/> </before> <after> - <actionGroup ref="logout" stepKey="logout"/> + <actionGroup ref="AdminLogoutActionGroup" stepKey="logout"/> </after> <!--Delete created custom url rewrite and verify AssertUrlRewriteDeletedMessage--> diff --git a/app/code/Magento/UrlRewrite/Test/Mftf/Test/AdminDeleteProductURLRewriteEntityTest.xml b/app/code/Magento/UrlRewrite/Test/Mftf/Test/AdminDeleteProductURLRewriteEntityTest.xml new file mode 100644 index 0000000000000..4ad6e99b0b424 --- /dev/null +++ b/app/code/Magento/UrlRewrite/Test/Mftf/Test/AdminDeleteProductURLRewriteEntityTest.xml @@ -0,0 +1,60 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + /** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +--> +<tests xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/testSchema.xsd"> + <test name="AdminDeleteProductURLRewriteEntityTest"> + <annotations> + <stories value="Delete Product UrlRewrite"/> + <title value="Delete created product URL rewrite"/> + <description value="Login as admin, create product with category and UrlRewrite, delete created URL rewrite"/> + <testCaseId value="MC-5347"/> + <severity value="MAJOR"/> + <group value="urlRewrite"/> + <group value="mtf_migrated"/> + </annotations> + + <before> + <actionGroup ref="AdminLoginActionGroup" stepKey="login"/> + <createData entity="SimpleSubCategory" stepKey="createCategory"/> + <createData entity="SimpleProduct" stepKey="createSimpleProduct"> + <requiredEntity createDataKey="createCategory"/> + </createData> + </before> + <after> + <deleteData createDataKey="createCategory" stepKey="deleteCategory"/> + <deleteData createDataKey="createSimpleProduct" stepKey="deleteProduct"/> + <actionGroup ref="AdminLogoutActionGroup" stepKey="logout"/> + </after> + + <!--Filter and Select the created Product --> + <actionGroup ref="AdminSearchUrlRewriteProductBySkuActionGroup" stepKey="searchProduct"> + <argument name="productSku" value="$$createSimpleProduct.sku$$"/> + </actionGroup> + + <!-- Update the Store, RequestPath, RedirectType and Description --> + <actionGroup ref="AdminAddUrlRewriteForProductActionGroup" stepKey="addUrlRewrite"> + <argument name="storeValue" value="Default Store View"/> + <argument name="requestPath" value="{{FirstLevelSubCat.name_lwr}}/{{_defaultProduct.urlKey}}.html"/> + <argument name="redirectTypeValue" value="Temporary (302)"/> + <argument name="description" value="End To End Test"/> + </actionGroup> + + <!--Delete Created Rewrite, Assert Success Message --> + <actionGroup ref="AdminDeleteUrlRewriteActionGroup" stepKey="deleteCreatedRewrite"> + <argument name="requestPath" value="{{FirstLevelSubCat.name_lwr}}/{{_defaultProduct.urlKey}}.html"/> + </actionGroup> + <!-- Assert Deleted Rewrite is Not in Grid --> + <actionGroup ref="AdminSearchDeletedUrlRewriteActionGroup" stepKey="searchDeletedURLRewriteInGrid"> + <argument name="requestPath" value="{{FirstLevelSubCat.name_lwr}}/{{_defaultProduct.urlKey}}.html"/> + </actionGroup> + <!-- Assert Page By Url Rewrite is Not Found --> + <actionGroup ref="AssertPageByUrlRewriteIsNotFoundActionGroup" stepKey="amOnPage"> + <argument name="requestPath" value="{{FirstLevelSubCat.name_lwr}}/{{_defaultProduct.urlKey}}.html"/> + </actionGroup> + </test> +</tests> diff --git a/app/code/Magento/UrlRewrite/Test/Mftf/Test/AdminDeleteProductUrlRewriteTest.xml b/app/code/Magento/UrlRewrite/Test/Mftf/Test/AdminDeleteProductUrlRewriteTest.xml new file mode 100644 index 0000000000000..4654c5a6a5daf --- /dev/null +++ b/app/code/Magento/UrlRewrite/Test/Mftf/Test/AdminDeleteProductUrlRewriteTest.xml @@ -0,0 +1,42 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + /** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +--> +<tests xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/testSchema.xsd"> + <test name="AdminDeleteProductUrlRewriteTest"> + <annotations> + <stories value="Delete Product URL rewrite"/> + <title value="Delete Product URL rewrite"/> + <description value="Test log in to URL rewrite and Delete Product URL rewrite"/> + <testCaseId value=""/> + <severity value="CRITICAL"/> + <group value="mtf_migrated"/> + </annotations> + <before> + <actionGroup ref="AdminLoginActionGroup" stepKey="login"/> + <createData entity="defaultSimpleProduct" stepKey="simpleProduct"/> + </before> + <after> + <deleteData createDataKey="simpleProduct" stepKey="deleteProduct"/> + <actionGroup ref="AdminLogoutActionGroup" stepKey="logout"/> + </after> + + <!--Delete created product url rewrite and verify AssertUrlRewriteDeletedMessage--> + <actionGroup ref="AdminDeleteUrlRewriteActionGroup" stepKey="deleteUrlRewrite"> + <argument name="requestPath" value="$$simpleProduct.custom_attributes[url_key]$$.html"/> + </actionGroup> + + <!--Search and verify AssertUrlRewriteNotInGrid--> + <actionGroup ref="AdminSearchDeletedUrlRewriteActionGroup" stepKey="searchDeletedUrlRewriteInGrid"> + <argument name="requestPath" value="$$simpleProduct.custom_attributes[url_key]$$.html"/> + </actionGroup> + + <!--Verify AssertPageByUrlRewriteIsNotFound--> + <actionGroup ref="AssertPageByUrlRewriteIsNotFoundActionGroup" stepKey="amOnPage"> + <argument name="requestPath" value="$$simpleProduct.custom_attributes[url_key]$$.html"/> + </actionGroup> + </test> +</tests> diff --git a/app/code/Magento/UrlRewrite/Test/Mftf/Test/AdminGenerateUrlRewritesForProductInCategoriesSwitchOffTest.xml b/app/code/Magento/UrlRewrite/Test/Mftf/Test/AdminGenerateUrlRewritesForProductInCategoriesSwitchOffTest.xml index 884c7778786a3..9d6b267055f70 100644 --- a/app/code/Magento/UrlRewrite/Test/Mftf/Test/AdminGenerateUrlRewritesForProductInCategoriesSwitchOffTest.xml +++ b/app/code/Magento/UrlRewrite/Test/Mftf/Test/AdminGenerateUrlRewritesForProductInCategoriesSwitchOffTest.xml @@ -24,7 +24,7 @@ <!--Flush cache--> <magentoCLI command="cache:flush" stepKey="cleanCache1"/> - <actionGroup ref="LoginAsAdmin" stepKey="login"/> + <actionGroup ref="AdminLoginActionGroup" stepKey="login"/> <createData entity="SimpleSubCategory" stepKey="createCategory"/> <createData entity="SimpleProduct" stepKey="createSimpleProduct"> <requiredEntity createDataKey="createCategory"/> @@ -40,15 +40,17 @@ <!--Flush cache--> <magentoCLI command="cache:flush" stepKey="cleanCache1"/> - <actionGroup ref="logout" stepKey="logout"/> + <actionGroup ref="AdminLogoutActionGroup" stepKey="logout"/> </after> <!-- 1. Open Marketing - SEO & Search - URL Rewrites --> <amOnPage url="{{AdminUrlRewriteIndexPage.url}}" stepKey="amOnUrlRewriteIndexPage"/> - <fillField selector="{{AdminUrlRewriteIndexSection.requestPathFilter}}" userInput="$createSimpleProduct.custom_attributes[url_key]$.html" stepKey="inputProductName"/> - <click selector="{{AdminDataGridHeaderSection.applyFilters}}" stepKey="clickSearchButton"/> - <seeElement selector="{{AdminUrlRewriteIndexSection.requestPathColumnValue($createSimpleProduct.custom_attributes[url_key]$.html)}}" stepKey="seeValue1"/> - <seeElement selector="{{AdminUrlRewriteIndexSection.requestPathColumnValue($createCategory.custom_attributes[url_key]$/$createSimpleProduct.custom_attributes[url_key]$.html)}}" stepKey="seeValue2"/> + <click selector="{{AdminDataGridHeaderSection.filters}}" stepKey="openUrlRewriteGridFilters"/> + <fillField selector="{{AdminDataGridHeaderSection.filterFieldInput('request_path')}}" userInput="$createSimpleProduct.custom_attributes[url_key]$.html" stepKey="inputProductName"/> + <click selector="{{AdminDataGridHeaderSection.applyFilters}}" stepKey="clickOrderApplyFilters"/> + <waitForPageLoad stepKey="waitForPageToLoad"/> + <seeElement selector="{{AdminUrlRewriteIndexSection.gridCellByColumnValue('Request Path', $createSimpleProduct.custom_attributes[url_key]$.html)}}" stepKey="seeValue1"/> + <seeElement selector="{{AdminUrlRewriteIndexSection.gridCellByColumnValue('Request Path', $createCategory.custom_attributes[url_key]$/$createSimpleProduct.custom_attributes[url_key]$.html)}}" stepKey="seeValue2"/> <!-- 2. Set the configuration for Generate "category/product" URL Rewrites to No--> <amOnPage url="{{CatalogConfigPage.url}}" stepKey="amOnCatalogConfigPage"/> @@ -65,9 +67,11 @@ <!-- 4. Open Marketing - SEO & Search - URL Rewrites --> <amOnPage url="{{AdminUrlRewriteIndexPage.url}}" stepKey="amOnUrlRewriteIndexPage2"/> - <fillField selector="{{AdminUrlRewriteIndexSection.requestPathFilter}}" userInput="$createSimpleProduct.custom_attributes[url_key]$.html" stepKey="inputProductName2"/> - <click selector="{{AdminDataGridHeaderSection.applyFilters}}" stepKey="clickSearchButton2"/> - <seeElement selector="{{AdminUrlRewriteIndexSection.requestPathColumnValue($createSimpleProduct.custom_attributes[url_key]$.html)}}" stepKey="seeInListValue1"/> - <dontSeeElement selector="{{AdminUrlRewriteIndexSection.requestPathColumnValue($createCategory.custom_attributes[url_key]$/$createSimpleProduct.custom_attributes[url_key]$.html)}}" stepKey="dontSeeValue2"/> + <click selector="{{AdminDataGridHeaderSection.filters}}" stepKey="openUrlRewriteGridFilters1"/> + <fillField selector="{{AdminDataGridHeaderSection.filterFieldInput('request_path')}}" userInput="$createSimpleProduct.custom_attributes[url_key]$.html" stepKey="inputProductName2"/> + <click selector="{{AdminDataGridHeaderSection.applyFilters}}" stepKey="clickOrderApplyFilters1"/> + <waitForPageLoad stepKey="waitForPageToLoad1"/> + <seeElement selector="{{AdminUrlRewriteIndexSection.gridCellByColumnValue('Request Path', $createSimpleProduct.custom_attributes[url_key]$.html)}}" stepKey="seeInListValue1"/> + <dontSeeElement selector="{{AdminUrlRewriteIndexSection.gridCellByColumnValue('Request Path', $createCategory.custom_attributes[url_key]$/$createSimpleProduct.custom_attributes[url_key]$.html)}}" stepKey="dontSeeValue2"/> </test> </tests> diff --git a/app/code/Magento/UrlRewrite/Test/Mftf/Test/AdminMarketingUrlRewritesNavigateMenuTest.xml b/app/code/Magento/UrlRewrite/Test/Mftf/Test/AdminMarketingUrlRewritesNavigateMenuTest.xml index 443307b427b42..e6ac90a32d0c1 100644 --- a/app/code/Magento/UrlRewrite/Test/Mftf/Test/AdminMarketingUrlRewritesNavigateMenuTest.xml +++ b/app/code/Magento/UrlRewrite/Test/Mftf/Test/AdminMarketingUrlRewritesNavigateMenuTest.xml @@ -20,10 +20,10 @@ <group value="mtf_migrated"/> </annotations> <before> - <actionGroup ref="LoginAsAdmin" stepKey="LoginAsAdmin"/> + <actionGroup ref="AdminLoginActionGroup" stepKey="LoginAsAdmin"/> </before> <after> - <actionGroup ref="logout" stepKey="logout"/> + <actionGroup ref="AdminLogoutActionGroup" stepKey="logout"/> </after> <actionGroup ref="AdminNavigateMenuActionGroup" stepKey="navigateToMarketingURLRewritesPage"> <argument name="menuUiId" value="{{AdminMenuMarketing.dataUiId}}"/> diff --git a/app/code/Magento/UrlRewrite/Test/Mftf/Test/AdminProductCreateUrlRewriteForCustomStoreViewTest.xml b/app/code/Magento/UrlRewrite/Test/Mftf/Test/AdminProductCreateUrlRewriteForCustomStoreViewTest.xml index 0ad68dae4e4ce..f03d9ae1bad67 100644 --- a/app/code/Magento/UrlRewrite/Test/Mftf/Test/AdminProductCreateUrlRewriteForCustomStoreViewTest.xml +++ b/app/code/Magento/UrlRewrite/Test/Mftf/Test/AdminProductCreateUrlRewriteForCustomStoreViewTest.xml @@ -26,7 +26,7 @@ <createData entity="SimpleProduct" stepKey="createProductForUrlRewrite"> <requiredEntity createDataKey="createCategory" /> </createData> - <actionGroup ref="LoginAsAdmin" stepKey="loginAsAdmin"/> + <actionGroup ref="AdminLoginActionGroup" stepKey="loginAsAdmin"/> <!-- Create second store view --> <actionGroup ref="AdminCreateStoreViewActionGroup" stepKey="createCustomStoreView"> <argument name="customStore" value="customStore"/> @@ -41,7 +41,7 @@ <argument name="customStore" value="customStore"/> </actionGroup> <actionGroup ref="AdminGridFilterResetActionGroup" stepKey="clearFilterForStores"/> - <actionGroup ref="logout" stepKey="logoutFromAdmin"/> + <actionGroup ref="AdminLogoutActionGroup" stepKey="logoutFromAdmin"/> </after> <!--Step 1. Navigate as Admin on Product Page for edit product`s Url Key--> <actionGroup ref="NavigateToCreatedProductEditPageActionGroup" stepKey="goToProductForUrlRewrite"> diff --git a/app/code/Magento/UrlRewrite/Test/Mftf/Test/AdminUpdateCategoryUrlRewriteAndAddAspxRequestPathTest.xml b/app/code/Magento/UrlRewrite/Test/Mftf/Test/AdminUpdateCategoryUrlRewriteAndAddAspxRequestPathTest.xml index fb8f200741c8a..ef43020191c4c 100644 --- a/app/code/Magento/UrlRewrite/Test/Mftf/Test/AdminUpdateCategoryUrlRewriteAndAddAspxRequestPathTest.xml +++ b/app/code/Magento/UrlRewrite/Test/Mftf/Test/AdminUpdateCategoryUrlRewriteAndAddAspxRequestPathTest.xml @@ -19,11 +19,11 @@ <before> <createData entity="SimpleSubCategory" stepKey="category"/> - <actionGroup ref="LoginAsAdmin" stepKey="login"/> + <actionGroup ref="AdminLoginActionGroup" stepKey="login"/> </before> <after> <deleteData createDataKey="category" stepKey="deleteCategory"/> - <actionGroup ref="logout" stepKey="logout"/> + <actionGroup ref="AdminLogoutActionGroup" stepKey="logout"/> </after> <!-- Search and Select Edit option for created category in grid --> diff --git a/app/code/Magento/UrlRewrite/Test/Mftf/Test/AdminUpdateCategoryUrlRewriteAndAddNoRedirectTest.xml b/app/code/Magento/UrlRewrite/Test/Mftf/Test/AdminUpdateCategoryUrlRewriteAndAddNoRedirectTest.xml index 72180105b38f8..ab911e95dfbc3 100644 --- a/app/code/Magento/UrlRewrite/Test/Mftf/Test/AdminUpdateCategoryUrlRewriteAndAddNoRedirectTest.xml +++ b/app/code/Magento/UrlRewrite/Test/Mftf/Test/AdminUpdateCategoryUrlRewriteAndAddNoRedirectTest.xml @@ -19,11 +19,11 @@ <before> <createData entity="SimpleSubCategory" stepKey="category"/> - <actionGroup ref="LoginAsAdmin" stepKey="login"/> + <actionGroup ref="AdminLoginActionGroup" stepKey="login"/> </before> <after> <deleteData createDataKey="category" stepKey="deleteCategory"/> - <actionGroup ref="logout" stepKey="logout"/> + <actionGroup ref="AdminLogoutActionGroup" stepKey="logout"/> </after> <!-- Search and Select Edit option for created category in grid --> diff --git a/app/code/Magento/UrlRewrite/Test/Mftf/Test/AdminUpdateCategoryUrlRewriteAndAddPermanentRedirectTest.xml b/app/code/Magento/UrlRewrite/Test/Mftf/Test/AdminUpdateCategoryUrlRewriteAndAddPermanentRedirectTest.xml index 3fb8e5da39caf..2222267db0e9f 100644 --- a/app/code/Magento/UrlRewrite/Test/Mftf/Test/AdminUpdateCategoryUrlRewriteAndAddPermanentRedirectTest.xml +++ b/app/code/Magento/UrlRewrite/Test/Mftf/Test/AdminUpdateCategoryUrlRewriteAndAddPermanentRedirectTest.xml @@ -13,17 +13,17 @@ <title value="Update Category URL Rewrites, permanent"/> <description value="Login as Admin and update category UrlRewrite and add Permanent redirect type"/> <testCaseId value="MC-5357"/> - <severity value="CRITICAL"/> + <severity value="BLOCKER"/> <group value="mtf_migrated"/> </annotations> <before> <createData entity="SimpleSubCategory" stepKey="category"/> - <actionGroup ref="LoginAsAdmin" stepKey="login"/> + <actionGroup ref="AdminLoginActionGroup" stepKey="login"/> </before> <after> <deleteData createDataKey="category" stepKey="deleteCategory"/> - <actionGroup ref="logout" stepKey="logout"/> + <actionGroup ref="AdminLogoutActionGroup" stepKey="logout"/> </after> <!-- Search and Select Edit option for created category in grid --> diff --git a/app/code/Magento/UrlRewrite/Test/Mftf/Test/AdminUpdateCategoryUrlRewriteAndAddTemporaryRedirectTest.xml b/app/code/Magento/UrlRewrite/Test/Mftf/Test/AdminUpdateCategoryUrlRewriteAndAddTemporaryRedirectTest.xml index bea5c44461a70..9cc8530959559 100644 --- a/app/code/Magento/UrlRewrite/Test/Mftf/Test/AdminUpdateCategoryUrlRewriteAndAddTemporaryRedirectTest.xml +++ b/app/code/Magento/UrlRewrite/Test/Mftf/Test/AdminUpdateCategoryUrlRewriteAndAddTemporaryRedirectTest.xml @@ -13,17 +13,17 @@ <title value="Update Category URL Rewrites, Temporary redirect type"/> <description value="Login as Admin and update category UrlRewrite and add Temporary redirect type"/> <testCaseId value="MC-5356"/> - <severity value="CRITICAL"/> + <severity value="BLOCKER"/> <group value="mtf_migrated"/> </annotations> <before> <createData entity="SimpleSubCategory" stepKey="category"/> - <actionGroup ref="LoginAsAdmin" stepKey="login"/> + <actionGroup ref="AdminLoginActionGroup" stepKey="login"/> </before> <after> <deleteData createDataKey="category" stepKey="deleteCategory"/> - <actionGroup ref="logout" stepKey="logout"/> + <actionGroup ref="AdminLogoutActionGroup" stepKey="logout"/> </after> <!-- Search and Select Edit option for created category in grid --> diff --git a/app/code/Magento/UrlRewrite/Test/Mftf/Test/AdminUpdateCmsPageRewriteEntityWithNoRedirectTest.xml b/app/code/Magento/UrlRewrite/Test/Mftf/Test/AdminUpdateCmsPageRewriteEntityWithNoRedirectTest.xml index 6467a5051631d..228478ea581b9 100644 --- a/app/code/Magento/UrlRewrite/Test/Mftf/Test/AdminUpdateCmsPageRewriteEntityWithNoRedirectTest.xml +++ b/app/code/Magento/UrlRewrite/Test/Mftf/Test/AdminUpdateCmsPageRewriteEntityWithNoRedirectTest.xml @@ -12,17 +12,18 @@ <stories value="Update CMS Page URL Redirect With No Redirect"/> <title value="Update CMS Page URL Redirect With No Redirect"/> <description value="Login as Admin and tried to update the created URL Rewrite for CMS page"/> + <severity value="MINOR"/> <group value="cMSContent"/> <group value="mtf_migrated"/> </annotations> <before> <createData entity="simpleCmsPage" stepKey="createCMSPage"/> - <actionGroup ref="LoginAsAdmin" stepKey="login"/> + <actionGroup ref="AdminLoginActionGroup" stepKey="login"/> </before> <after> <deleteData createDataKey="createCMSPage" stepKey="deleteCMSPage"/> - <actionGroup ref="logout" stepKey="logout"/> + <actionGroup ref="AdminLogoutActionGroup" stepKey="logout"/> </after> <!--Create Custom Store View--> diff --git a/app/code/Magento/UrlRewrite/Test/Mftf/Test/AdminUpdateCmsPageRewriteEntityWithPermanentReirectTest.xml b/app/code/Magento/UrlRewrite/Test/Mftf/Test/AdminUpdateCmsPageRewriteEntityWithPermanentReirectTest.xml index 3bf278db8410a..fab01ba6a6e35 100644 --- a/app/code/Magento/UrlRewrite/Test/Mftf/Test/AdminUpdateCmsPageRewriteEntityWithPermanentReirectTest.xml +++ b/app/code/Magento/UrlRewrite/Test/Mftf/Test/AdminUpdateCmsPageRewriteEntityWithPermanentReirectTest.xml @@ -12,17 +12,18 @@ <stories value="Update CMS Page URL Redirect With Permanent Redirect"/> <title value="Update CMS Page URL Redirect With Permanent Redirect"/> <description value="Login as Admin and tried to update the created URL Rewrite for CMS page"/> + <severity value="CRITICAL"/> <group value="cMSContent"/> <group value="mtf_migrated"/> </annotations> <before> <createData entity="simpleCmsPage" stepKey="createCMSPage"/> - <actionGroup ref="LoginAsAdmin" stepKey="login"/> + <actionGroup ref="AdminLoginActionGroup" stepKey="login"/> </before> <after> <deleteData createDataKey="createCMSPage" stepKey="deleteCMSPage"/> - <actionGroup ref="logout" stepKey="logout"/> + <actionGroup ref="AdminLogoutActionGroup" stepKey="logout"/> </after> <!--Create Custom Store View--> diff --git a/app/code/Magento/UrlRewrite/Test/Mftf/Test/AdminUpdateCmsPageRewriteEntityWithTemporaryRedirectTest.xml b/app/code/Magento/UrlRewrite/Test/Mftf/Test/AdminUpdateCmsPageRewriteEntityWithTemporaryRedirectTest.xml index a7cadcdf753c3..2623b238ad2eb 100644 --- a/app/code/Magento/UrlRewrite/Test/Mftf/Test/AdminUpdateCmsPageRewriteEntityWithTemporaryRedirectTest.xml +++ b/app/code/Magento/UrlRewrite/Test/Mftf/Test/AdminUpdateCmsPageRewriteEntityWithTemporaryRedirectTest.xml @@ -12,17 +12,18 @@ <stories value="Update CMS Page URL Redirect With Temporary Redirect"/> <title value="Update CMS Page URL Redirect With Temporary Redirect"/> <description value="Login as Admin and tried to update the created URL Rewrite for CMS page"/> + <severity value="MINOR"/> <group value="cMSContent"/> <group value="mtf_migrated"/> </annotations> <before> <createData entity="simpleCmsPage" stepKey="createCMSPage"/> - <actionGroup ref="LoginAsAdmin" stepKey="login"/> + <actionGroup ref="AdminLoginActionGroup" stepKey="login"/> </before> <after> <deleteData createDataKey="createCMSPage" stepKey="deleteCMSPage"/> - <actionGroup ref="logout" stepKey="logout"/> + <actionGroup ref="AdminLogoutActionGroup" stepKey="logout"/> </after> <!--Create Custom Store View--> diff --git a/app/code/Magento/UrlRewrite/Test/Mftf/Test/AdminUpdateCmsPageUrlRewriteAndAddNoRedirectTest.xml b/app/code/Magento/UrlRewrite/Test/Mftf/Test/AdminUpdateCmsPageUrlRewriteAndAddNoRedirectTest.xml new file mode 100644 index 0000000000000..03a4e1fc7d82f --- /dev/null +++ b/app/code/Magento/UrlRewrite/Test/Mftf/Test/AdminUpdateCmsPageUrlRewriteAndAddNoRedirectTest.xml @@ -0,0 +1,66 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + /** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +--> +<tests xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/testSchema.xsd"> + <test name="AdminUpdateCmsPageUrlRewriteAndAddNoRedirectTest"> + <annotations> + <stories value="Update URL rewrite"/> + <title value="Update Cms Page URL Rewrites, no redirect type"/> + <description value="Login as Admin and update Cms Page Url Rewrite and add redirect type No"/> + <testCaseId value=""/> + <severity value="CRITICAL"/> + <group value="mtf_migrated"/> + </annotations> + <before> + <actionGroup ref="AdminLoginActionGroup" stepKey="login"/> + <createData entity="simpleCmsPage" stepKey="createCMSPage"/> + </before> + <after> + <deleteData createDataKey="createCMSPage" stepKey="deleteCMSPage"/> + <actionGroup ref="AdminLogoutActionGroup" stepKey="logout"/> + </after> + + <!--Open CMS Edit Page and Get the CMS ID --> + <actionGroup ref="NavigateToCreatedCMSPageActionGroup" stepKey="navigateToCreatedCMSPage"> + <argument name="CMSPage" value="$$createCMSPage$$"/> + </actionGroup> + <grabFromCurrentUrl stepKey="cmsId" regex="#\/([0-9]*)?\/$#"/> + + <!-- Search and Select Edit option for created cms page in grid --> + <actionGroup ref="AdminSearchAndSelectUrlRewriteInGridActionGroup" stepKey="editUrlRewrite"> + <argument name="requestPath" value="$$createCMSPage.identifier$$"/> + </actionGroup> + + <!-- Open UrlRewrite Edit page and update the fields --> + <actionGroup ref="AdminUpdateUrlRewriteActionGroup" stepKey="updateCmsPageUrlRewrite"> + <argument name="storeValue" value="Default Store View"/> + <argument name="requestPath" value="{{defaultCmsPage.title}}"/> + <argument name="redirectTypeValue" value="No"/> + <argument name="description" value="Update Cms Page Url Rewrite"/> + </actionGroup> + + <!-- Assert Cms Page Url Rewrite in grid --> + <actionGroup ref="AdminSearchByRequestPathActionGroup" stepKey="searchByNewRequestPath"> + <argument name="redirectPath" value="{{defaultCmsPage.title}}"/> + <argument name="redirectType" value="No"/> + <argument name="targetPath" value="cms/page/view/page_id/{$cmsId}"/> + </actionGroup> + + <!-- Assert Updated Request Path directs to the CMS Page on Store Front --> + <actionGroup ref="NavigateToStorefrontForCreatedPageActionGroup" stepKey="navigateToTheStoreFront"> + <argument name="page" value="{{defaultCmsPage.title}}"/> + </actionGroup> + + <!-- Assert updated CMS redirect in Store Front --> + <actionGroup ref="AssertStoreFrontCMSPageActionGroup" stepKey="assertCMSPage"> + <argument name="cmsTitle" value="$$createCMSPage.title$$"/> + <argument name="cmsContent" value="$$createCMSPage.content$$"/> + <argument name="cmsContentHeading" value="$$createCMSPage.content_heading$$"/> + </actionGroup> + </test> +</tests> diff --git a/app/code/Magento/UrlRewrite/Test/Mftf/Test/AdminUpdateCmsPageUrlRewriteAndAddPermanentRedirectTest.xml b/app/code/Magento/UrlRewrite/Test/Mftf/Test/AdminUpdateCmsPageUrlRewriteAndAddPermanentRedirectTest.xml new file mode 100644 index 0000000000000..527ba1bfea065 --- /dev/null +++ b/app/code/Magento/UrlRewrite/Test/Mftf/Test/AdminUpdateCmsPageUrlRewriteAndAddPermanentRedirectTest.xml @@ -0,0 +1,66 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + /** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +--> +<tests xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/testSchema.xsd"> + <test name="AdminUpdateCmsPageUrlRewriteAndAddPermanentRedirectTest"> + <annotations> + <stories value="Update URL rewrite"/> + <title value="Update Cms Page URL Rewrites, Permanent redirect type"/> + <description value="Login as Admin and update Cms Page Url Rewrite and add Permanent redirect type"/> + <testCaseId value=""/> + <severity value="CRITICAL"/> + <group value="mtf_migrated"/> + </annotations> + <before> + <actionGroup ref="AdminLoginActionGroup" stepKey="login"/> + <createData entity="simpleCmsPage" stepKey="createCMSPage"/> + </before> + <after> + <deleteData createDataKey="createCMSPage" stepKey="deleteCMSPage"/> + <actionGroup ref="AdminLogoutActionGroup" stepKey="logout"/> + </after> + + <!--Open CMS Edit Page and Get the CMS ID --> + <actionGroup ref="NavigateToCreatedCMSPageActionGroup" stepKey="navigateToCreatedCMSPage"> + <argument name="CMSPage" value="$$createCMSPage$$"/> + </actionGroup> + <grabFromCurrentUrl stepKey="cmsId" regex="#\/([0-9]*)?\/$#"/> + + <!-- Search and Select Edit option for created cms page in grid --> + <actionGroup ref="AdminSearchAndSelectUrlRewriteInGridActionGroup" stepKey="editUrlRewrite"> + <argument name="requestPath" value="$$createCMSPage.identifier$$"/> + </actionGroup> + + <!-- Open UrlRewrite Edit page and update the fields --> + <actionGroup ref="AdminUpdateUrlRewriteActionGroup" stepKey="updateCmsPageUrlRewrite"> + <argument name="storeValue" value="Default Store View"/> + <argument name="requestPath" value="{{defaultCmsPage.title}}"/> + <argument name="redirectTypeValue" value="Permanent (301)"/> + <argument name="description" value="Update Cms Page Url Rewrite"/> + </actionGroup> + + <!-- Assert Cms Page Url Rewrite in grid --> + <actionGroup ref="AdminSearchByRequestPathActionGroup" stepKey="searchByNewRequestPath"> + <argument name="redirectPath" value="{{defaultCmsPage.title}}"/> + <argument name="redirectType" value="Permanent (301)"/> + <argument name="targetPath" value="cms/page/view/page_id/{$cmsId}"/> + </actionGroup> + + <!-- Assert Updated Request Path directs to the CMS Page on Store Front --> + <actionGroup ref="NavigateToStorefrontForCreatedPageActionGroup" stepKey="navigateToTheStoreFront"> + <argument name="page" value="{{defaultCmsPage.title}}"/> + </actionGroup> + + <!-- Assert updated CMS redirect in Store Front --> + <actionGroup ref="AssertStoreFrontCMSPageActionGroup" stepKey="assertCMSPage"> + <argument name="cmsTitle" value="$$createCMSPage.title$$"/> + <argument name="cmsContent" value="$$createCMSPage.content$$"/> + <argument name="cmsContentHeading" value="$$createCMSPage.content_heading$$"/> + </actionGroup> + </test> +</tests> diff --git a/app/code/Magento/UrlRewrite/Test/Mftf/Test/AdminUpdateCmsPageUrlRewriteAndAddTemporaryRedirectTest.xml b/app/code/Magento/UrlRewrite/Test/Mftf/Test/AdminUpdateCmsPageUrlRewriteAndAddTemporaryRedirectTest.xml new file mode 100644 index 0000000000000..bd3766a8c51a1 --- /dev/null +++ b/app/code/Magento/UrlRewrite/Test/Mftf/Test/AdminUpdateCmsPageUrlRewriteAndAddTemporaryRedirectTest.xml @@ -0,0 +1,66 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + /** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +--> +<tests xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/testSchema.xsd"> + <test name="AdminUpdateCmsPageUrlRewriteAndAddTemporaryRedirectTest"> + <annotations> + <stories value="Update URL rewrite"/> + <title value="Update Cms Page URL Rewrites, Temporary redirect type"/> + <description value="Login as Admin and update Cms Page Url Rewrite and add Temporary redirect type"/> + <testCaseId value=""/> + <severity value="CRITICAL"/> + <group value="mtf_migrated"/> + </annotations> + <before> + <actionGroup ref="AdminLoginActionGroup" stepKey="login"/> + <createData entity="simpleCmsPage" stepKey="createCMSPage"/> + </before> + <after> + <deleteData createDataKey="createCMSPage" stepKey="deleteCMSPage"/> + <actionGroup ref="AdminLogoutActionGroup" stepKey="logout"/> + </after> + + <!--Open CMS Edit Page and Get the CMS ID --> + <actionGroup ref="NavigateToCreatedCMSPageActionGroup" stepKey="navigateToCreatedCMSPage"> + <argument name="CMSPage" value="$$createCMSPage$$"/> + </actionGroup> + <grabFromCurrentUrl stepKey="cmsId" regex="#\/([0-9]*)?\/$#"/> + + <!-- Search and Select Edit option for created cms page in grid --> + <actionGroup ref="AdminSearchAndSelectUrlRewriteInGridActionGroup" stepKey="editUrlRewrite"> + <argument name="requestPath" value="$$createCMSPage.identifier$$"/> + </actionGroup> + + <!-- Open UrlRewrite Edit page and update the fields --> + <actionGroup ref="AdminUpdateUrlRewriteActionGroup" stepKey="updateCmsPageUrlRewrite"> + <argument name="storeValue" value="Default Store View"/> + <argument name="requestPath" value="{{defaultCmsPage.title}}"/> + <argument name="redirectTypeValue" value="Temporary (302)"/> + <argument name="description" value="Update Cms Page Url Rewrite"/> + </actionGroup> + + <!-- Assert Cms Page Url Rewrite in grid --> + <actionGroup ref="AdminSearchByRequestPathActionGroup" stepKey="searchByNewRequestPath"> + <argument name="redirectPath" value="{{defaultCmsPage.title}}"/> + <argument name="redirectType" value="Temporary (302)"/> + <argument name="targetPath" value="cms/page/view/page_id/{$cmsId}"/> + </actionGroup> + + <!-- Assert Updated Request Path directs to the CMS Page on Store Front --> + <actionGroup ref="NavigateToStorefrontForCreatedPageActionGroup" stepKey="navigateToTheStoreFront"> + <argument name="page" value="{{defaultCmsPage.title}}"/> + </actionGroup> + + <!-- Assert updated CMS redirect in Store Front --> + <actionGroup ref="AssertStoreFrontCMSPageActionGroup" stepKey="assertCMSPage"> + <argument name="cmsTitle" value="$$createCMSPage.title$$"/> + <argument name="cmsContent" value="$$createCMSPage.content$$"/> + <argument name="cmsContentHeading" value="$$createCMSPage.content_heading$$"/> + </actionGroup> + </test> +</tests> diff --git a/app/code/Magento/UrlRewrite/Test/Mftf/Test/AdminUpdateCustomURLRewritesPermanentTest.xml b/app/code/Magento/UrlRewrite/Test/Mftf/Test/AdminUpdateCustomURLRewritesPermanentTest.xml index a07f7b8c0fe60..9b739b157cddc 100644 --- a/app/code/Magento/UrlRewrite/Test/Mftf/Test/AdminUpdateCustomURLRewritesPermanentTest.xml +++ b/app/code/Magento/UrlRewrite/Test/Mftf/Test/AdminUpdateCustomURLRewritesPermanentTest.xml @@ -20,13 +20,13 @@ <before> <createData entity="defaultUrlRewrite" stepKey="urlRewrite"/> - <actionGroup ref="LoginAsAdmin" stepKey="loginToAdminPanel"/> + <actionGroup ref="AdminLoginActionGroup" stepKey="loginToAdminPanel"/> </before> <after> <actionGroup ref="AdminDeleteUrlRewriteActionGroup" stepKey="deleteCustomUrlRewrite"> <argument name="requestPath" value="{{customPermanentUrlRewrite.request_path}}"/> </actionGroup> - <actionGroup ref="logout" stepKey="logout"/> + <actionGroup ref="AdminLogoutActionGroup" stepKey="logout"/> </after> <!--Search default custom url rewrite in grid--> diff --git a/app/code/Magento/UrlRewrite/Test/Mftf/Test/AdminUpdateCustomURLRewritesTemporaryTest.xml b/app/code/Magento/UrlRewrite/Test/Mftf/Test/AdminUpdateCustomURLRewritesTemporaryTest.xml index dcf923a34081b..f450d7640d360 100644 --- a/app/code/Magento/UrlRewrite/Test/Mftf/Test/AdminUpdateCustomURLRewritesTemporaryTest.xml +++ b/app/code/Magento/UrlRewrite/Test/Mftf/Test/AdminUpdateCustomURLRewritesTemporaryTest.xml @@ -21,14 +21,14 @@ <before> <createData entity="defaultUrlRewrite" stepKey="urlRewrite"/> <createData entity="defaultSimpleProduct" stepKey="createProduct"/> - <actionGroup ref="LoginAsAdmin" stepKey="loginToAdminPanel"/> + <actionGroup ref="AdminLoginActionGroup" stepKey="loginToAdminPanel"/> </before> <after> <deleteData createDataKey="createProduct" stepKey="deleteProduct"/> <actionGroup ref="AdminDeleteUrlRewriteActionGroup" stepKey="deleteCustomUrlRewrite"> <argument name="requestPath" value="{{customTemporaryUrlRewrite.request_path}}"/> </actionGroup> - <actionGroup ref="logout" stepKey="logout"/> + <actionGroup ref="AdminLogoutActionGroup" stepKey="logout"/> </after> <!--Filter Product in product page and get the Product ID --> diff --git a/app/code/Magento/UrlRewrite/Test/Mftf/Test/AdminUpdateProductUrlRewriteAndAddTemporaryRedirectTest.xml b/app/code/Magento/UrlRewrite/Test/Mftf/Test/AdminUpdateProductUrlRewriteAndAddTemporaryRedirectTest.xml index 74f3a60f35cea..ea97175d8823c 100644 --- a/app/code/Magento/UrlRewrite/Test/Mftf/Test/AdminUpdateProductUrlRewriteAndAddTemporaryRedirectTest.xml +++ b/app/code/Magento/UrlRewrite/Test/Mftf/Test/AdminUpdateProductUrlRewriteAndAddTemporaryRedirectTest.xml @@ -19,11 +19,11 @@ <before> <createData entity="defaultSimpleProduct" stepKey="createProduct"/> - <actionGroup ref="LoginAsAdmin" stepKey="login"/> + <actionGroup ref="AdminLoginActionGroup" stepKey="login"/> </before> <after> <deleteData createDataKey="createProduct" stepKey="deleteProduct"/> - <actionGroup ref="logout" stepKey="logout"/> + <actionGroup ref="AdminLogoutActionGroup" stepKey="logout"/> </after> <!-- Search and Select Edit option for created product in grid --> diff --git a/app/code/Magento/UrlRewrite/Test/Mftf/Test/AdminUrlRewritesForProductAfterImportTest.xml b/app/code/Magento/UrlRewrite/Test/Mftf/Test/AdminUrlRewritesForProductAfterImportTest.xml index a3d3b897ef75d..a70065dc1d307 100644 --- a/app/code/Magento/UrlRewrite/Test/Mftf/Test/AdminUrlRewritesForProductAfterImportTest.xml +++ b/app/code/Magento/UrlRewrite/Test/Mftf/Test/AdminUrlRewritesForProductAfterImportTest.xml @@ -42,21 +42,22 @@ <deleteData createDataKey="simpleSubCategory2" stepKey="deleteSimpleSubCategory2"/> <deleteData createDataKey="simpleSubCategory1" stepKey="deleteSimpleSubCategory1"/> <comment userInput="Disable config to generate category/product URL Rewrites " stepKey="commentDisableConfig" /> - <magentoCLI command="config:set catalog/seo/generate_category_product_rewrites 0" stepKey="disableGenerateUrlRewrite"/> + <magentoCLI command="config:set catalog/seo/generate_category_product_rewrites 1" stepKey="disableGenerateUrlRewrite"/> <amOnPage url="{{AdminLogoutPage.url}}" stepKey="amOnLogoutPage"/> </after> <comment userInput="1. Log in to Admin " stepKey="commentAdminLogin" /> - <actionGroup ref="LoginAsAdmin" stepKey="loginAsAdmin"/> + <actionGroup ref="AdminLoginActionGroup" stepKey="loginAsAdmin"/> <comment userInput="2. Open Marketing - SEO and Search - URL Rewrites " stepKey="commentVerifyUrlRewrite" /> <amOnPage url="{{AdminUrlRewriteIndexPage.url}}" stepKey="amOnUrlRewriteIndexPage"/> - <fillField selector="{{AdminUrlRewriteIndexSection.requestPathFilter}}" userInput="$createSimpleProduct.custom_attributes[url_key]$.html" stepKey="inputProductName"/> + <click selector="{{AdminDataGridHeaderSection.filters}}" stepKey="openUrlRewriteGridFilters"/> + <fillField selector="{{AdminDataGridHeaderSection.filterFieldInput('request_path')}}" userInput="$createSimpleProduct.custom_attributes[url_key]$.html" stepKey="inputProductName"/> <click selector="{{AdminDataGridHeaderSection.applyFilters}}" stepKey="clickSearchButton"/> - <seeElement selector="{{AdminUrlRewriteIndexSection.requestPathColumnValue($createSimpleProduct.custom_attributes[url_key]$.html)}}" stepKey="seeValue1"/> - <seeElement selector="{{AdminUrlRewriteIndexSection.requestPathColumnValue($simpleSubCategory1.custom_attributes[url_key]$/$createSimpleProduct.custom_attributes[url_key]$.html)}}" stepKey="seeValue2"/> - <seeElement selector="{{AdminUrlRewriteIndexSection.requestPathColumnValue($simpleSubCategory1.custom_attributes[url_key]$/$simpleSubCategory2.custom_attributes[url_key]$/$createSimpleProduct.custom_attributes[url_key]$.html)}}" stepKey="seeValue3"/> - <seeElement selector="{{AdminUrlRewriteIndexSection.requestPathColumnValue($simpleSubCategory1.custom_attributes[url_key]$/$simpleSubCategory2.custom_attributes[url_key]$/$simpleSubCategory3.custom_attributes[url_key]$/$createSimpleProduct.custom_attributes[url_key]$.html)}}" stepKey="seeValue4"/> + <seeElement selector="{{AdminUrlRewriteIndexSection.gridCellByColumnValue('Request Path', $createSimpleProduct.custom_attributes[url_key]$.html)}}" stepKey="seeValue1"/> + <seeElement selector="{{AdminUrlRewriteIndexSection.gridCellByColumnValue('Request Path', $simpleSubCategory1.custom_attributes[url_key]$/$createSimpleProduct.custom_attributes[url_key]$.html)}}" stepKey="seeValue2"/> + <seeElement selector="{{AdminUrlRewriteIndexSection.gridCellByColumnValue('Request Path', $simpleSubCategory1.custom_attributes[url_key]$/$simpleSubCategory2.custom_attributes[url_key]$/$createSimpleProduct.custom_attributes[url_key]$.html)}}" stepKey="seeValue3"/> + <seeElement selector="{{AdminUrlRewriteIndexSection.gridCellByColumnValue('Request Path', $simpleSubCategory1.custom_attributes[url_key]$/$simpleSubCategory2.custom_attributes[url_key]$/$simpleSubCategory3.custom_attributes[url_key]$/$createSimpleProduct.custom_attributes[url_key]$.html)}}" stepKey="seeValue4"/> <comment userInput="3. Import products with add/update behavior " stepKey="commentProductImport" /> <actionGroup ref="AdminImportProductsActionGroup" stepKey="adminImportProducts"> @@ -72,14 +73,15 @@ <comment userInput="5. Open Marketing - SEO and Search - URL Rewrites" stepKey="commentVerifyURLAfterImport" /> <amOnPage url="{{AdminUrlRewriteIndexPage.url}}" stepKey="amOnUrlRewriteIndexPage2"/> - <fillField selector="{{AdminUrlRewriteIndexSection.requestPathFilter}}" userInput="$createSimpleProduct.custom_attributes[url_key]$-new.html" stepKey="inputProductName2"/> + <click selector="{{AdminDataGridHeaderSection.filters}}" stepKey="openUrlRewriteGridFilters2"/> + <fillField selector="{{AdminDataGridHeaderSection.filterFieldInput('request_path')}}" userInput="$createSimpleProduct.custom_attributes[url_key]$-new.html" stepKey="inputProductName2"/> <click selector="{{AdminDataGridHeaderSection.applyFilters}}" stepKey="clickSearchButton2"/> - <seeElement selector="{{AdminUrlRewriteIndexSection.requestPathColumnValue($createSimpleProduct.custom_attributes[url_key]$-new.html)}}" stepKey="seeInListValue1"/> - <seeElement selector="{{AdminUrlRewriteIndexSection.requestPathColumnValue($simpleSubCategory1.custom_attributes[url_key]$/$createSimpleProduct.custom_attributes[url_key]$-new.html)}}" stepKey="seeInListValue2"/> - <seeElement selector="{{AdminUrlRewriteIndexSection.requestPathColumnValue($simpleSubCategory1.custom_attributes[url_key]$/$simpleSubCategory2.custom_attributes[url_key]$/$createSimpleProduct.custom_attributes[url_key]$-new.html)}}" stepKey="seeInListValue3"/> - <seeElement selector="{{AdminUrlRewriteIndexSection.requestPathColumnValue($simpleSubCategory1.custom_attributes[url_key]$/$simpleSubCategory2.custom_attributes[url_key]$/$simpleSubCategory3.custom_attributes[url_key]$/$createSimpleProduct.custom_attributes[url_key]$-new.html)}}" stepKey="seeInListValue4"/> - <seeElement selector="{{AdminUrlRewriteIndexSection.requestPathColumnValue($simpleSubCategory1.custom_attributes[url_key]$/$createSimpleProduct.custom_attributes[url_key]$-new.html)}}" stepKey="seeInListValue5"/> - <seeElement selector="{{AdminUrlRewriteIndexSection.requestPathColumnValue($simpleSubCategory1.custom_attributes[url_key]$/$simpleSubCategory2.custom_attributes[url_key]$/$createSimpleProduct.custom_attributes[url_key]$-new.html)}}" stepKey="seeInListValue6"/> - <seeElement selector="{{AdminUrlRewriteIndexSection.requestPathColumnValue($simpleSubCategory1.custom_attributes[url_key]$/$simpleSubCategory2.custom_attributes[url_key]$/$simpleSubCategory3.custom_attributes[url_key]$/$createSimpleProduct.custom_attributes[url_key]$-new.html)}}" stepKey="seeInListValue7"/> + <seeElement selector="{{AdminUrlRewriteIndexSection.gridCellByColumnValue('Request Path', $createSimpleProduct.custom_attributes[url_key]$-new.html)}}" stepKey="seeInListValue1"/> + <seeElement selector="{{AdminUrlRewriteIndexSection.gridCellByColumnValue('Request Path', $simpleSubCategory1.custom_attributes[url_key]$/$createSimpleProduct.custom_attributes[url_key]$-new.html)}}" stepKey="seeInListValue2"/> + <seeElement selector="{{AdminUrlRewriteIndexSection.gridCellByColumnValue('Request Path', $simpleSubCategory1.custom_attributes[url_key]$/$simpleSubCategory2.custom_attributes[url_key]$/$createSimpleProduct.custom_attributes[url_key]$-new.html)}}" stepKey="seeInListValue3"/> + <seeElement selector="{{AdminUrlRewriteIndexSection.gridCellByColumnValue('Request Path', $simpleSubCategory1.custom_attributes[url_key]$/$simpleSubCategory2.custom_attributes[url_key]$/$simpleSubCategory3.custom_attributes[url_key]$/$createSimpleProduct.custom_attributes[url_key]$-new.html)}}" stepKey="seeInListValue4"/> + <seeElement selector="{{AdminUrlRewriteIndexSection.gridCellByColumnValue('Request Path', $simpleSubCategory1.custom_attributes[url_key]$/$createSimpleProduct.custom_attributes[url_key]$-new.html)}}" stepKey="seeInListValue5"/> + <seeElement selector="{{AdminUrlRewriteIndexSection.gridCellByColumnValue('Request Path', $simpleSubCategory1.custom_attributes[url_key]$/$simpleSubCategory2.custom_attributes[url_key]$/$createSimpleProduct.custom_attributes[url_key]$-new.html)}}" stepKey="seeInListValue6"/> + <seeElement selector="{{AdminUrlRewriteIndexSection.gridCellByColumnValue('Request Path', $simpleSubCategory1.custom_attributes[url_key]$/$simpleSubCategory2.custom_attributes[url_key]$/$simpleSubCategory3.custom_attributes[url_key]$/$createSimpleProduct.custom_attributes[url_key]$-new.html)}}" stepKey="seeInListValue7"/> </test> </tests> diff --git a/app/code/Magento/UrlRewrite/Test/Mftf/Test/AdminUrlRewritesForProductInAnchorCategoriesTest.xml b/app/code/Magento/UrlRewrite/Test/Mftf/Test/AdminUrlRewritesForProductInAnchorCategoriesTest.xml deleted file mode 100644 index dc82a3e4ab24f..0000000000000 --- a/app/code/Magento/UrlRewrite/Test/Mftf/Test/AdminUrlRewritesForProductInAnchorCategoriesTest.xml +++ /dev/null @@ -1,281 +0,0 @@ -<?xml version="1.0" encoding="UTF-8"?> -<!-- - /** - * Copyright © Magento, Inc. All rights reserved. - * See COPYING.txt for license details. - */ ---> - -<tests xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/testSchema.xsd"> - <test name="AdminUrlRewritesForProductInAnchorCategoriesTest"> - <annotations> - <features value="Url Rewrite"/> - <stories value="Url-rewrites for product in anchor categories"/> - <title value="Url-rewrites for product in anchor categories"/> - <description value="For a product with category that has parent anchor categories, the rewrites is created when the category/product is saved."/> - <severity value="CRITICAL"/> - <testCaseId value="MC-16568"/> - <group value="urlRewrite"/> - </annotations> - - <!-- Preconditions--> - <!-- Create 3 categories --> - <before> - <createData entity="SimpleSubCategory" stepKey="simpleSubCategory1"/> - <createData entity="SubCategoryWithParent" stepKey="simpleSubCategory2"> - <requiredEntity createDataKey="simpleSubCategory1"/> - </createData> - <createData entity="SubCategoryWithParent" stepKey="simpleSubCategory3"> - <requiredEntity createDataKey="simpleSubCategory2"/> - </createData> - <!-- Create Simple product 1 and assign it to Category 3 --> - <createData entity="ApiSimpleProduct" stepKey="createSimpleProduct"> - <requiredEntity createDataKey="simpleSubCategory3"/> - </createData> - <!-- Set the configuration for Generate "category/product" URL Rewrites--> - <comment userInput="Enable config to generate category/product URL Rewrites " stepKey="commentEnableConfig" /> - <magentoCLI command="config:set catalog/seo/generate_category_product_rewrites 1" stepKey="enableGenerateUrlRewrite"/> - </before> - <after> - <deleteData createDataKey="createSimpleProduct" stepKey="deleteSimpleProduct"/> - <deleteData createDataKey="simpleSubCategory1" stepKey="deletesimpleSubCategory1"/> - <amOnPage url="{{AdminLogoutPage.url}}" stepKey="amOnLogoutPage"/> - </after> - <!-- Steps --> - <!-- 1. Log in to Admin --> - <actionGroup ref="LoginAsAdmin" stepKey="loginAsAdmin"/> - <!-- 2. Open Marketing - SEO & Search - URL Rewrites --> - <amOnPage url="{{AdminUrlRewriteIndexPage.url}}" stepKey="amOnUrlRewriteIndexPage"/> - <fillField selector="{{AdminUrlRewriteIndexSection.requestPathFilter}}" userInput="$createSimpleProduct.custom_attributes[url_key]$.html" stepKey="inputProductName"/> - <click selector="{{AdminDataGridHeaderSection.applyFilters}}" stepKey="clickSearchButton"/> - <seeElement selector="{{AdminUrlRewriteIndexSection.requestPathColumnValue($createSimpleProduct.custom_attributes[url_key]$.html)}}" stepKey="seeValue1"/> - <seeElement selector="{{AdminUrlRewriteIndexSection.requestPathColumnValue($simpleSubCategory1.custom_attributes[url_key]$/$createSimpleProduct.custom_attributes[url_key]$.html)}}" stepKey="seeValue2"/> - <seeElement selector="{{AdminUrlRewriteIndexSection.requestPathColumnValue($simpleSubCategory1.custom_attributes[url_key]$/$simpleSubCategory2.custom_attributes[url_key]$/$createSimpleProduct.custom_attributes[url_key]$.html)}}" stepKey="seeValue3"/> - <seeElement selector="{{AdminUrlRewriteIndexSection.requestPathColumnValue($simpleSubCategory1.custom_attributes[url_key]$/$simpleSubCategory2.custom_attributes[url_key]$/$simpleSubCategory3.custom_attributes[url_key]$/$createSimpleProduct.custom_attributes[url_key]$.html)}}" stepKey="seeValue4"/> - - <!-- 3. Edit Category 1 for DEFAULT Store View: --> - <actionGroup ref="SwitchCategoryStoreViewActionGroup" stepKey="switchStoreView"> - <argument name="Store" value="_defaultStore.name"/> - <argument name="CatName" value="$$simpleSubCategory1.name$$"/> - </actionGroup> - <click selector="{{AdminCategorySEOSection.SectionHeader}}" stepKey="openSeoSection2"/> - <uncheckOption selector="{{AdminCategorySEOSection.UrlKeyDefaultValueCheckbox}}" stepKey="uncheckRedirect2"/> - <fillField selector="{{AdminCategorySEOSection.UrlKeyInput}}" userInput="$simpleSubCategory1.custom_attributes[url_key]$-new" stepKey="changeURLKey"/> - <checkOption selector="{{AdminCategorySEOSection.UrlKeyRedirectCheckbox}}" stepKey="checkUrlKeyRedirect"/> - <!-- 4. Save Category 1 --> - <click selector="{{AdminCategoryMainActionsSection.SaveButton}}" stepKey="saveCategory"/> - <seeElement selector="{{AdminCategoryMessagesSection.SuccessMessage}}" stepKey="assertSuccessMessageAfterSaved"/> - - <!-- 5. Open Marketing - SEO & Search - URL Rewrites --> - <amOnPage url="{{AdminUrlRewriteIndexPage.url}}" stepKey="amOnUrlRewriteIndexPage2"/> - <fillField selector="{{AdminUrlRewriteIndexSection.requestPathFilter}}" userInput="$createSimpleProduct.custom_attributes[url_key]$.html" stepKey="inputProductName2"/> - <click selector="{{AdminDataGridHeaderSection.applyFilters}}" stepKey="clickSearchButton2"/> - <seeElement selector="{{AdminUrlRewriteIndexSection.requestPathColumnValue($createSimpleProduct.custom_attributes[url_key]$.html)}}" stepKey="seeInListValue1"/> - <seeElement selector="{{AdminUrlRewriteIndexSection.requestPathColumnValue($simpleSubCategory1.custom_attributes[url_key]$/$createSimpleProduct.custom_attributes[url_key]$.html)}}" stepKey="seeInListValue2"/> - <seeElement selector="{{AdminUrlRewriteIndexSection.requestPathColumnValue($simpleSubCategory1.custom_attributes[url_key]$/$simpleSubCategory2.custom_attributes[url_key]$/$createSimpleProduct.custom_attributes[url_key]$.html)}}" stepKey="seeInListValue3"/> - <seeElement selector="{{AdminUrlRewriteIndexSection.requestPathColumnValue($simpleSubCategory1.custom_attributes[url_key]$/$simpleSubCategory2.custom_attributes[url_key]$/$simpleSubCategory3.custom_attributes[url_key]$/$createSimpleProduct.custom_attributes[url_key]$.html)}}" stepKey="seeInListValue4"/> - <seeElement selector="{{AdminUrlRewriteIndexSection.requestPathColumnValue($simpleSubCategory1.custom_attributes[url_key]$-new/$createSimpleProduct.custom_attributes[url_key]$.html)}}" stepKey="seeInListValue5"/> - <seeElement selector="{{AdminUrlRewriteIndexSection.requestPathColumnValue($simpleSubCategory1.custom_attributes[url_key]$-new/$simpleSubCategory2.custom_attributes[url_key]$/$createSimpleProduct.custom_attributes[url_key]$.html)}}" stepKey="seeInListValue6"/> - <seeElement selector="{{AdminUrlRewriteIndexSection.requestPathColumnValue($simpleSubCategory1.custom_attributes[url_key]$-new/$simpleSubCategory2.custom_attributes[url_key]$/$simpleSubCategory3.custom_attributes[url_key]$/$createSimpleProduct.custom_attributes[url_key]$.html)}}" stepKey="seeInListValue7"/> - </test> - - <test name="AdminUrlRewritesForProductInAnchorCategoriesTestWithConfigurationTurnedOff"> - <annotations> - <features value="Url Rewrite"/> - <stories value="Url-rewrites for product in anchor categories"/> - <title value="Url-rewrites for product in anchor categories with configuration turned off"/> - <description value="For a product with category that has parent anchor categories, the rewrites is created when the category/product is saved."/> - <severity value="CRITICAL"/> - <testCaseId value="MC-6844"/> - <group value="urlRewrite"/> - </annotations> - - <!-- Preconditions--> - <!-- Create 3 categories --> - <before> - <!-- Set the configuration for Generate "category/product" URL Rewrites to Yes (default)--> - <comment userInput="Enable SEO configuration setting to generate category/product URL Rewrites" stepKey="commentEnableUrlRewriteConfig" /> - <magentoCLI command="config:set catalog/seo/generate_category_product_rewrites 1" stepKey="enableGenerateUrlRewrite"/> - <!--Flush cache--> - <magentoCLI command="cache:flush" stepKey="cleanCache1"/> - - <createData entity="SimpleSubCategory" stepKey="simpleSubCategory1"/> - <createData entity="SubCategoryWithParent" stepKey="simpleSubCategory2"> - <requiredEntity createDataKey="simpleSubCategory1"/> - </createData> - <createData entity="SubCategoryWithParent" stepKey="simpleSubCategory3"> - <requiredEntity createDataKey="simpleSubCategory2"/> - </createData> - <!-- Create Simple product 1 and assign it to Category 3 --> - <createData entity="ApiSimpleProduct" stepKey="createSimpleProduct"> - <requiredEntity createDataKey="simpleSubCategory3"/> - </createData> - <!-- Set the configuration for Generate "category/product" URL Rewrites to No--> - <comment userInput="Disable SEO configuration setting to generate category/product URL Rewrites" stepKey="commentDisableUrlRewriteConfig" /> - <magentoCLI command="config:set catalog/seo/generate_category_product_rewrites 0" stepKey="disableGenerateUrlRewrite"/> - <!--Flush cache--> - <magentoCLI command="cache:flush" stepKey="cleanCache"/> - </before> - <after> - <deleteData createDataKey="createSimpleProduct" stepKey="deleteSimpleProduct"/> - <deleteData createDataKey="simpleSubCategory1" stepKey="deletesimpleSubCategory1"/> - <amOnPage url="{{AdminLogoutPage.url}}" stepKey="amOnLogoutPage"/> - <magentoCLI command="config:set catalog/seo/generate_category_product_rewrites 1" stepKey="resetConfigurationSetting"/> - <!--Flush cache--> - <magentoCLI command="cache:flush" stepKey="cleanCache2"/> - </after> - <!-- Steps --> - <!-- 1. Log in to Admin --> - <actionGroup ref="LoginAsAdmin" stepKey="loginAsAdmin"/> - <!-- 2. Open Marketing - SEO & Search - URL Rewrites --> - <amOnPage url="{{AdminUrlRewriteIndexPage.url}}" stepKey="amOnUrlRewriteIndexPage"/> - <fillField selector="{{AdminUrlRewriteIndexSection.requestPathFilter}}" userInput="$createSimpleProduct.custom_attributes[url_key]$.html" stepKey="inputProductName"/> - <click selector="{{AdminDataGridHeaderSection.applyFilters}}" stepKey="clickSearchButton"/> - <seeElement selector="{{AdminUrlRewriteIndexSection.requestPathColumnValue($createSimpleProduct.custom_attributes[url_key]$.html)}}" stepKey="seeValue1"/> - <dontSeeElement selector="{{AdminUrlRewriteIndexSection.requestPathColumnValue($simpleSubCategory1.custom_attributes[url_key]$/$createSimpleProduct.custom_attributes[url_key]$.html)}}" stepKey="seeValue2"/> - <dontSeeElement selector="{{AdminUrlRewriteIndexSection.requestPathColumnValue($simpleSubCategory1.custom_attributes[url_key]$/$simpleSubCategory2.custom_attributes[url_key]$/$createSimpleProduct.custom_attributes[url_key]$.html)}}" stepKey="seeValue3"/> - <dontSeeElement selector="{{AdminUrlRewriteIndexSection.requestPathColumnValue($simpleSubCategory1.custom_attributes[url_key]$/$simpleSubCategory2.custom_attributes[url_key]$/$simpleSubCategory3.custom_attributes[url_key]$/$createSimpleProduct.custom_attributes[url_key]$.html)}}" stepKey="seeValue4"/> - - <!-- 3. Edit Category 1 for DEFAULT Store View: --> - <actionGroup ref="SwitchCategoryStoreViewActionGroup" stepKey="switchStoreView"> - <argument name="Store" value="_defaultStore.name"/> - <argument name="CatName" value="$$simpleSubCategory1.name$$"/> - </actionGroup> - <click selector="{{AdminCategorySEOSection.SectionHeader}}" stepKey="openSeoSection2"/> - <uncheckOption selector="{{AdminCategorySEOSection.UrlKeyDefaultValueCheckbox}}" stepKey="uncheckRedirect2"/> - <fillField selector="{{AdminCategorySEOSection.UrlKeyInput}}" userInput="$simpleSubCategory1.custom_attributes[url_key]$-new" stepKey="changeURLKey"/> - <checkOption selector="{{AdminCategorySEOSection.UrlKeyRedirectCheckbox}}" stepKey="checkUrlKeyRedirect"/> - <!-- 4. Save Category 1 --> - <click selector="{{AdminCategoryMainActionsSection.SaveButton}}" stepKey="saveCategory"/> - <seeElement selector="{{AdminCategoryMessagesSection.SuccessMessage}}" stepKey="assertSuccessMessageAfterSaved"/> - - <!-- 5. Open Marketing - SEO & Search - URL Rewrites --> - <amOnPage url="{{AdminUrlRewriteIndexPage.url}}" stepKey="amOnUrlRewriteIndexPage2"/> - <fillField selector="{{AdminUrlRewriteIndexSection.requestPathFilter}}" userInput="$createSimpleProduct.custom_attributes[url_key]$.html" stepKey="inputProductName2"/> - <click selector="{{AdminDataGridHeaderSection.applyFilters}}" stepKey="clickSearchButton2"/> - <seeElement selector="{{AdminUrlRewriteIndexSection.requestPathColumnValue($createSimpleProduct.custom_attributes[url_key]$.html)}}" stepKey="seeInListValue1"/> - <dontSeeElement selector="{{AdminUrlRewriteIndexSection.requestPathColumnValue($simpleSubCategory1.custom_attributes[url_key]$/$createSimpleProduct.custom_attributes[url_key]$.html)}}" stepKey="seeInListValue2"/> - <dontSeeElement selector="{{AdminUrlRewriteIndexSection.requestPathColumnValue($simpleSubCategory1.custom_attributes[url_key]$/$simpleSubCategory2.custom_attributes[url_key]$/$createSimpleProduct.custom_attributes[url_key]$.html)}}" stepKey="seeInListValue3"/> - <dontSeeElement selector="{{AdminUrlRewriteIndexSection.requestPathColumnValue($simpleSubCategory1.custom_attributes[url_key]$/$simpleSubCategory2.custom_attributes[url_key]$/$simpleSubCategory3.custom_attributes[url_key]$/$createSimpleProduct.custom_attributes[url_key]$.html)}}" stepKey="seeInListValue4"/> - <dontSeeElement selector="{{AdminUrlRewriteIndexSection.requestPathColumnValue($simpleSubCategory1.custom_attributes[url_key]$-new/$createSimpleProduct.custom_attributes[url_key]$.html)}}" stepKey="seeInListValue5"/> - <dontSeeElement selector="{{AdminUrlRewriteIndexSection.requestPathColumnValue($simpleSubCategory1.custom_attributes[url_key]$-new/$simpleSubCategory2.custom_attributes[url_key]$/$createSimpleProduct.custom_attributes[url_key]$.html)}}" stepKey="seeInListValue6"/> - <dontSeeElement selector="{{AdminUrlRewriteIndexSection.requestPathColumnValue($simpleSubCategory1.custom_attributes[url_key]$-new/$simpleSubCategory2.custom_attributes[url_key]$/$simpleSubCategory3.custom_attributes[url_key]$/$createSimpleProduct.custom_attributes[url_key]$.html)}}" stepKey="seeInListValue7"/> - - <amOnPage url="/$createSimpleProduct.custom_attributes[url_key]$.html" stepKey="navigateToProductPage"/> - <see selector="{{StorefrontProductInfoMainSection.productName}}" userInput="$$createSimpleProduct.name$$" stepKey="seeProductName"/> - - <amOnPage url="/$simpleSubCategory1.custom_attributes[url_key]$/$createSimpleProduct.custom_attributes[url_key]$.html" stepKey="navigateToProductPage2"/> - <see selector="{{StorefrontProductInfoMainSection.productName}}" userInput="$$createSimpleProduct.name$$" stepKey="seeProductName2"/> - <amOnPage url="/$simpleSubCategory1.custom_attributes[url_key]$/$simpleSubCategory2.custom_attributes[url_key]$/$createSimpleProduct.custom_attributes[url_key]$.html" stepKey="navigateToProductPage3"/> - <see selector="{{StorefrontProductInfoMainSection.productName}}" userInput="$$createSimpleProduct.name$$" stepKey="seeProductName3"/> - <amOnPage url="/$simpleSubCategory1.custom_attributes[url_key]$/$simpleSubCategory2.custom_attributes[url_key]$/$simpleSubCategory3.custom_attributes[url_key]$/$createSimpleProduct.custom_attributes[url_key]$.html" stepKey="navigateToProductPage4"/> - <see selector="{{StorefrontProductInfoMainSection.productName}}" userInput="$$createSimpleProduct.name$$" stepKey="seeProductName4"/> - - <amOnPage url="/$simpleSubCategory1.custom_attributes[url_key]$-new/$createSimpleProduct.custom_attributes[url_key]$.html" stepKey="navigateToProductPage5"/> - <see selector="{{StorefrontProductInfoMainSection.productName}}" userInput="$$createSimpleProduct.name$$" stepKey="seeProductName5"/> - <amOnPage url="/$simpleSubCategory1.custom_attributes[url_key]$-new/$simpleSubCategory2.custom_attributes[url_key]$/$createSimpleProduct.custom_attributes[url_key]$.html" stepKey="navigateToProductPage6"/> - <see selector="{{StorefrontProductInfoMainSection.productName}}" userInput="$$createSimpleProduct.name$$" stepKey="seeProductName6"/> - <amOnPage url="/$simpleSubCategory1.custom_attributes[url_key]$-new/$simpleSubCategory2.custom_attributes[url_key]$/$simpleSubCategory3.custom_attributes[url_key]$/$createSimpleProduct.custom_attributes[url_key]$.html" stepKey="navigateToProductPage7"/> - <see selector="{{StorefrontProductInfoMainSection.productName}}" userInput="$$createSimpleProduct.name$$" stepKey="seeProductName7"/> - </test> - - <test name="AdminUrlRewritesForProductInAnchorCategoriesTestAllStoreView" extends="AdminUrlRewritesForProductInAnchorCategoriesTest"> - <annotations> - <features value="Url Rewrite"/> - <stories value="Url-rewrites for product in anchor categories for all store views"/> - <title value="Url-rewrites for product in anchor categories for all store views"/> - <description value="Verify that Saving category do not delete UrlRewrites for subcategories and all products in them."/> - <severity value="CRITICAL"/> - <testCaseId value="MC-16681"/> - <group value="urlRewrite"/> - </annotations> - <before> - <remove keyForRemoval="createSimpleProduct"/> - <!-- Create Simple product 1 and assign it to all the threee categories above --> - <createData entity="ApiSimpleProduct" stepKey="createSimpleProduct" after="simpleSubCategory3"> - <requiredEntity createDataKey="simpleSubCategory1"/> - <requiredEntity createDataKey="simpleSubCategory2"/> - <requiredEntity createDataKey="simpleSubCategory3"/> - </createData> - </before> - <remove keyForRemoval="switchStoreView"/> - <!-- 3. Edit Category 1 for All store view: --> - <actionGroup ref="NavigateToCreatedCategoryActionGroup" stepKey="goToCategoryPage" after="seeValue4"> - <argument name="Category" value="$$simpleSubCategory1$$"/> - </actionGroup> - <remove keyForRemoval="uncheckRedirect2"/> - </test> - - <test name="AdminUrlRewritesForProductInAnchorCategoriesTestAllStoreViewWithConfigurationTurnedOff" extends="AdminUrlRewritesForProductInAnchorCategoriesTestWithConfigurationTurnedOff"> - <annotations> - <features value="Url Rewrite"/> - <stories value="Url-rewrites for product in anchor categories for all store views"/> - <title value="Url-rewrites for product in anchor categories for all store views with configuration turned off"/> - <description value="Url-rewrites for product in anchor categories for all store views with configuration turned off"/> - <severity value="CRITICAL"/> - <testCaseId value="MC-6964"/> - <group value="urlRewrite"/> - </annotations> - <before> - <remove keyForRemoval="createSimpleProduct"/> - <!-- Create Simple product 1 and assign it to all the threee categories above --> - <createData entity="ApiSimpleProduct" stepKey="createSimpleProduct" after="simpleSubCategory3"> - <requiredEntity createDataKey="simpleSubCategory1"/> - <requiredEntity createDataKey="simpleSubCategory2"/> - <requiredEntity createDataKey="simpleSubCategory3"/> - </createData> - </before> - <remove keyForRemoval="switchStoreView"/> - <!-- 3. Edit Category 1 for All store view: --> - <actionGroup ref="NavigateToCreatedCategoryActionGroup" stepKey="goToCategoryPage" after="seeValue4"> - <argument name="Category" value="$$simpleSubCategory1$$"/> - </actionGroup> - <remove keyForRemoval="uncheckRedirect2"/> - </test> - - <test name="AdminUrlRewritesForProductsWithConfigurationTurnedOff"> - <annotations> - <features value="Url Rewrite"/> - <stories value="No Url-rewrites for product if configuration to generate url rewrite for Generate 'category/product' URL Rewrites is enabled "/> - <title value="No auto generated of request path for simple product when assigned to subCategory"/> - <description value="No auto generated of request path when SEO configuration to Generate url rewrite for Generate 'category/product' URL Rewrites is set to No"/> - <severity value="CRITICAL"/> - <testCaseId value="MAGETWO-94803"/> - <group value="urlRewrite"/> - </annotations> - <before> - <!-- Set the configuration for Generate "category/product" URL Rewrites to Yes (default)--> - <comment userInput="Enable SEO configuration setting to generate category/product URL Rewrites" stepKey="commentEnableUrlRewriteConfig" /> - <magentoCLI command="config:set catalog/seo/generate_category_product_rewrites 1" stepKey="enableGenerateUrlRewrite"/> - <!--Flush cache--> - <magentoCLI command="cache:flush" stepKey="cleanCache1"/> - <createData entity="SimpleSubCategory" stepKey="simpleSubCategory1"/> - <!-- Create Simple product 1 and assign it to Category 1 --> - <createData entity="ApiSimpleProduct" stepKey="createSimpleProduct"> - <requiredEntity createDataKey="simpleSubCategory1"/> - </createData> - <!-- Set the configuration for Generate "category/product" URL Rewrites to No--> - <comment userInput="Disable SEO configuration setting to generate category/product URL Rewrites" stepKey="commentDisableUrlRewriteConfig" /> - <magentoCLI command="config:set catalog/seo/generate_category_product_rewrites 0" stepKey="disableGenerateUrlRewrite"/> - <!--Flush cache--> - <magentoCLI command="cache:flush" stepKey="cleanCache"/> - </before> - <after> - <deleteData createDataKey="createSimpleProduct" stepKey="deleteSimpleProduct"/> - <deleteData createDataKey="simpleSubCategory1" stepKey="deletesimpleSubCategory1"/> - <magentoCLI command="config:set catalog/seo/generate_category_product_rewrites 1" stepKey="resetConfigurationSetting"/> - <!--Flush cache--> - <magentoCLI command="cache:flush" stepKey="cleanCache2"/> - </after> - <!-- 1. Log in to Admin --> - <actionGroup ref="LoginAsAdmin" stepKey="loginAsAdmin"/> - <!-- 2. Open Marketing - SEO & Search - URL Rewrites --> - <amOnPage url="{{AdminUrlRewriteIndexPage.url}}" stepKey="amOnUrlRewriteIndexPage"/> - <fillField selector="{{AdminUrlRewriteIndexSection.requestPathFilter}}" userInput="$createSimpleProduct.custom_attributes[url_key]$.html" stepKey="inputProductName5"/> - <click selector="{{AdminDataGridHeaderSection.applyFilters}}" stepKey="clickSearchButton5"/> - <seeElement selector="{{AdminUrlRewriteIndexSection.requestPathColumnValue($createSimpleProduct.custom_attributes[url_key]$.html)}}" stepKey="seeProducturl"/> - <dontSeeElement selector="{{AdminUrlRewriteIndexSection.requestPathColumnValue($simpleSubCategory1.custom_attributes[url_key]$/$createSimpleProduct.custom_attributes[url_key]$.html)}}" stepKey="dontSeeCategoryProducturlKey"/> - <amOnPage url="/$simpleSubCategory1.custom_attributes[url_key]$/$createSimpleProduct.custom_attributes[url_key]$.html" stepKey="navigateToProductPage"/> - <see selector="{{StorefrontProductInfoMainSection.productName}}" userInput="$$createSimpleProduct.name$$" stepKey="seeProductName"/> - </test> -</tests> diff --git a/app/code/Magento/UrlRewrite/Test/Mftf/Test/AdminUrlRewritesForProductInAnchorCategoriesTest/AdminUrlRewritesForProductInAnchorCategoriesTest.xml b/app/code/Magento/UrlRewrite/Test/Mftf/Test/AdminUrlRewritesForProductInAnchorCategoriesTest/AdminUrlRewritesForProductInAnchorCategoriesTest.xml new file mode 100644 index 0000000000000..cce0cd11e0199 --- /dev/null +++ b/app/code/Magento/UrlRewrite/Test/Mftf/Test/AdminUrlRewritesForProductInAnchorCategoriesTest/AdminUrlRewritesForProductInAnchorCategoriesTest.xml @@ -0,0 +1,85 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + /** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +--> +<tests xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/testSchema.xsd"> + <test name="AdminUrlRewritesForProductInAnchorCategoriesTest"> + <annotations> + <features value="Url Rewrite"/> + <stories value="Url-rewrites for product in anchor categories"/> + <title value="Url-rewrites for product in anchor categories"/> + <description value="For a product with category that has parent anchor categories, the rewrites is created when the category/product is saved."/> + <severity value="BLOCKER"/> + <testCaseId value="MC-16568"/> + <group value="urlRewrite"/> + </annotations> + + <!-- Preconditions--> + <!-- Create 3 categories --> + <before> + <createData entity="SimpleSubCategory" stepKey="simpleSubCategory1"/> + <createData entity="SubCategoryWithParent" stepKey="simpleSubCategory2"> + <requiredEntity createDataKey="simpleSubCategory1"/> + </createData> + <createData entity="SubCategoryWithParent" stepKey="simpleSubCategory3"> + <requiredEntity createDataKey="simpleSubCategory2"/> + </createData> + <!-- Create Simple product 1 and assign it to Category 3 --> + <createData entity="ApiSimpleProduct" stepKey="createSimpleProduct"> + <requiredEntity createDataKey="simpleSubCategory3"/> + </createData> + <!-- Set the configuration for Generate "category/product" URL Rewrites--> + <comment userInput="Enable config to generate category/product URL Rewrites " stepKey="commentEnableConfig"/> + <magentoCLI command="config:set catalog/seo/generate_category_product_rewrites 1" stepKey="enableGenerateUrlRewrite"/> + </before> + <after> + <deleteData createDataKey="createSimpleProduct" stepKey="deleteSimpleProduct"/> + <deleteData createDataKey="simpleSubCategory1" stepKey="deletesimpleSubCategory1"/> + <amOnPage url="{{AdminLogoutPage.url}}" stepKey="amOnLogoutPage"/> + </after> + <!-- Steps --> + <!-- 1. Log in to Admin --> + <actionGroup ref="AdminLoginActionGroup" stepKey="loginAsAdmin"/> + <!-- 2. Open Marketing - SEO & Search - URL Rewrites --> + <amOnPage url="{{AdminUrlRewriteIndexPage.url}}" stepKey="amOnUrlRewriteIndexPage"/> + <click selector="{{AdminDataGridHeaderSection.filters}}" stepKey="openUrlRewriteGridFilters"/> + <fillField selector="{{AdminDataGridHeaderSection.filterFieldInput('request_path')}}" userInput="$createSimpleProduct.custom_attributes[url_key]$.html" stepKey="inputProductName"/> + <click selector="{{AdminDataGridHeaderSection.applyFilters}}" stepKey="clickOrderApplyFilters"/> + <waitForPageLoad stepKey="waitForPageToLoad"/> + <seeElement selector="{{AdminUrlRewriteIndexSection.gridCellByColumnValue('Request Path', $createSimpleProduct.custom_attributes[url_key]$.html)}}" stepKey="seeValue1"/> + <seeElement selector="{{AdminUrlRewriteIndexSection.gridCellByColumnValue('Request Path', $simpleSubCategory1.custom_attributes[url_key]$/$createSimpleProduct.custom_attributes[url_key]$.html)}}" stepKey="seeValue2"/> + <seeElement selector="{{AdminUrlRewriteIndexSection.gridCellByColumnValue('Request Path', $simpleSubCategory1.custom_attributes[url_key]$/$simpleSubCategory2.custom_attributes[url_key]$/$createSimpleProduct.custom_attributes[url_key]$.html)}}" stepKey="seeValue3"/> + <seeElement selector="{{AdminUrlRewriteIndexSection.gridCellByColumnValue('Request Path', $simpleSubCategory1.custom_attributes[url_key]$/$simpleSubCategory2.custom_attributes[url_key]$/$simpleSubCategory3.custom_attributes[url_key]$/$createSimpleProduct.custom_attributes[url_key]$.html)}}" stepKey="seeValue4"/> + + <!-- 3. Edit Category 1 for DEFAULT Store View: --> + <actionGroup ref="SwitchCategoryStoreViewActionGroup" stepKey="switchStoreView"> + <argument name="Store" value="_defaultStore.name"/> + <argument name="CatName" value="$$simpleSubCategory1.name$$"/> + </actionGroup> + <click selector="{{AdminCategorySEOSection.SectionHeader}}" stepKey="openSeoSection2"/> + <uncheckOption selector="{{AdminCategorySEOSection.UrlKeyDefaultValueCheckbox}}" stepKey="uncheckRedirect2"/> + <fillField selector="{{AdminCategorySEOSection.UrlKeyInput}}" userInput="$simpleSubCategory1.custom_attributes[url_key]$-new" stepKey="changeURLKey"/> + <checkOption selector="{{AdminCategorySEOSection.UrlKeyRedirectCheckbox}}" stepKey="checkUrlKeyRedirect"/> + <!-- 4. Save Category 1 --> + <click selector="{{AdminCategoryMainActionsSection.SaveButton}}" stepKey="saveCategory"/> + <seeElement selector="{{AdminCategoryMessagesSection.SuccessMessage}}" stepKey="assertSuccessMessageAfterSaved"/> + + <!-- 5. Open Marketing - SEO & Search - URL Rewrites --> + <amOnPage url="{{AdminUrlRewriteIndexPage.url}}" stepKey="amOnUrlRewriteIndexPage2"/> + <click selector="{{AdminDataGridHeaderSection.filters}}" stepKey="openUrlRewriteGridFilters1"/> + <fillField selector="{{AdminDataGridHeaderSection.filterFieldInput('request_path')}}" userInput="$createSimpleProduct.custom_attributes[url_key]$.html" stepKey="inputProductName2"/> + <click selector="{{AdminDataGridHeaderSection.applyFilters}}" stepKey="clickOrderApplyFilters1"/> + <waitForPageLoad stepKey="waitForPageToLoad1"/> + <seeElement selector="{{AdminUrlRewriteIndexSection.gridCellByColumnValue('Request Path', $createSimpleProduct.custom_attributes[url_key]$.html)}}" stepKey="seeInListValue1"/> + <seeElement selector="{{AdminUrlRewriteIndexSection.gridCellByColumnValue('Request Path', $simpleSubCategory1.custom_attributes[url_key]$/$createSimpleProduct.custom_attributes[url_key]$.html)}}" stepKey="seeInListValue2"/> + <seeElement selector="{{AdminUrlRewriteIndexSection.gridCellByColumnValue('Request Path', $simpleSubCategory1.custom_attributes[url_key]$/$simpleSubCategory2.custom_attributes[url_key]$/$createSimpleProduct.custom_attributes[url_key]$.html)}}" stepKey="seeInListValue3"/> + <seeElement selector="{{AdminUrlRewriteIndexSection.gridCellByColumnValue('Request Path', $simpleSubCategory1.custom_attributes[url_key]$/$simpleSubCategory2.custom_attributes[url_key]$/$simpleSubCategory3.custom_attributes[url_key]$/$createSimpleProduct.custom_attributes[url_key]$.html)}}" stepKey="seeInListValue4"/> + <seeElement selector="{{AdminUrlRewriteIndexSection.gridCellByColumnValue('Request Path', $simpleSubCategory1.custom_attributes[url_key]$-new/$createSimpleProduct.custom_attributes[url_key]$.html)}}" stepKey="seeInListValue5"/> + <seeElement selector="{{AdminUrlRewriteIndexSection.gridCellByColumnValue('Request Path', $simpleSubCategory1.custom_attributes[url_key]$-new/$simpleSubCategory2.custom_attributes[url_key]$/$createSimpleProduct.custom_attributes[url_key]$.html)}}" stepKey="seeInListValue6"/> + <seeElement selector="{{AdminUrlRewriteIndexSection.gridCellByColumnValue('Request Path', $simpleSubCategory1.custom_attributes[url_key]$-new/$simpleSubCategory2.custom_attributes[url_key]$/$simpleSubCategory3.custom_attributes[url_key]$/$createSimpleProduct.custom_attributes[url_key]$.html)}}" stepKey="seeInListValue7"/> + </test> +</tests> diff --git a/app/code/Magento/UrlRewrite/Test/Mftf/Test/AdminUrlRewritesForProductInAnchorCategoriesTest/AdminUrlRewritesForProductInAnchorCategoriesTestAllStoreViewTest.xml b/app/code/Magento/UrlRewrite/Test/Mftf/Test/AdminUrlRewritesForProductInAnchorCategoriesTest/AdminUrlRewritesForProductInAnchorCategoriesTestAllStoreViewTest.xml new file mode 100644 index 0000000000000..1876b001eb5bc --- /dev/null +++ b/app/code/Magento/UrlRewrite/Test/Mftf/Test/AdminUrlRewritesForProductInAnchorCategoriesTest/AdminUrlRewritesForProductInAnchorCategoriesTestAllStoreViewTest.xml @@ -0,0 +1,36 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + /** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +--> +<tests xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/testSchema.xsd"> + <test name="AdminUrlRewritesForProductInAnchorCategoriesTestAllStoreViewTest" extends="AdminUrlRewritesForProductInAnchorCategoriesTest"> + <annotations> + <features value="Url Rewrite"/> + <stories value="Url-rewrites for product in anchor categories for all store views"/> + <title value="Url-rewrites for product in anchor categories for all store views"/> + <description value="Verify that Saving category do not delete UrlRewrites for subcategories and all products in them."/> + <severity value="CRITICAL"/> + <testCaseId value="MC-16681"/> + <group value="urlRewrite"/> + </annotations> + <before> + <remove keyForRemoval="createSimpleProduct"/> + <!-- Create Simple product 1 and assign it to all the threee categories above --> + <createData entity="ApiSimpleProduct" stepKey="createSimpleProduct" after="simpleSubCategory3"> + <requiredEntity createDataKey="simpleSubCategory1"/> + <requiredEntity createDataKey="simpleSubCategory2"/> + <requiredEntity createDataKey="simpleSubCategory3"/> + </createData> + </before> + <remove keyForRemoval="switchStoreView"/> + <!-- 3. Edit Category 1 for All store view: --> + <actionGroup ref="NavigateToCreatedCategoryActionGroup" stepKey="goToCategoryPage" after="seeValue4"> + <argument name="Category" value="$$simpleSubCategory1$$"/> + </actionGroup> + <remove keyForRemoval="uncheckRedirect2"/> + </test> +</tests> diff --git a/app/code/Magento/UrlRewrite/Test/Mftf/Test/AdminUrlRewritesForProductInAnchorCategoriesTest/AdminUrlRewritesForProductInAnchorCategoriesTestAllStoreViewWithConfigurationTurnedOffTest.xml b/app/code/Magento/UrlRewrite/Test/Mftf/Test/AdminUrlRewritesForProductInAnchorCategoriesTest/AdminUrlRewritesForProductInAnchorCategoriesTestAllStoreViewWithConfigurationTurnedOffTest.xml new file mode 100644 index 0000000000000..14f7c9fb7cbe3 --- /dev/null +++ b/app/code/Magento/UrlRewrite/Test/Mftf/Test/AdminUrlRewritesForProductInAnchorCategoriesTest/AdminUrlRewritesForProductInAnchorCategoriesTestAllStoreViewWithConfigurationTurnedOffTest.xml @@ -0,0 +1,36 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + /** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +--> +<tests xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/testSchema.xsd"> + <test name="AdminUrlRewritesForProductInAnchorCategoriesTestAllStoreViewWithConfigurationTurnedOffTest" extends="AdminUrlRewritesForProductInAnchorCategoriesTestWithConfigurationTurnedOffTest"> + <annotations> + <features value="Url Rewrite"/> + <stories value="Url-rewrites for product in anchor categories for all store views"/> + <title value="Url-rewrites for product in anchor categories for all store views with configuration turned off"/> + <description value="Url-rewrites for product in anchor categories for all store views with configuration turned off"/> + <severity value="CRITICAL"/> + <testCaseId value="MC-6964"/> + <group value="urlRewrite"/> + </annotations> + <before> + <remove keyForRemoval="createSimpleProduct"/> + <!-- Create Simple product 1 and assign it to all the threee categories above --> + <createData entity="ApiSimpleProduct" stepKey="createSimpleProduct" after="simpleSubCategory3"> + <requiredEntity createDataKey="simpleSubCategory1"/> + <requiredEntity createDataKey="simpleSubCategory2"/> + <requiredEntity createDataKey="simpleSubCategory3"/> + </createData> + </before> + <remove keyForRemoval="switchStoreView"/> + <!-- 3. Edit Category 1 for All store view: --> + <actionGroup ref="NavigateToCreatedCategoryActionGroup" stepKey="goToCategoryPage" after="seeValue4"> + <argument name="Category" value="$$simpleSubCategory1$$"/> + </actionGroup> + <remove keyForRemoval="uncheckRedirect2"/> + </test> +</tests> diff --git a/app/code/Magento/UrlRewrite/Test/Mftf/Test/AdminUrlRewritesForProductInAnchorCategoriesTest/AdminUrlRewritesForProductInAnchorCategoriesTestWithConfigurationTurnedOffTest.xml b/app/code/Magento/UrlRewrite/Test/Mftf/Test/AdminUrlRewritesForProductInAnchorCategoriesTest/AdminUrlRewritesForProductInAnchorCategoriesTestWithConfigurationTurnedOffTest.xml new file mode 100644 index 0000000000000..639cd2c57f7d1 --- /dev/null +++ b/app/code/Magento/UrlRewrite/Test/Mftf/Test/AdminUrlRewritesForProductInAnchorCategoriesTest/AdminUrlRewritesForProductInAnchorCategoriesTestWithConfigurationTurnedOffTest.xml @@ -0,0 +1,113 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + /** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +--> +<tests xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/testSchema.xsd"> + <test name="AdminUrlRewritesForProductInAnchorCategoriesTestWithConfigurationTurnedOffTest"> + <annotations> + <features value="Url Rewrite"/> + <stories value="Url-rewrites for product in anchor categories"/> + <title value="Url-rewrites for product in anchor categories with configuration turned off"/> + <description value="For a product with category that has parent anchor categories, the rewrites is created when the category/product is saved."/> + <severity value="CRITICAL"/> + <testCaseId value="MC-6844"/> + <group value="urlRewrite"/> + </annotations> + + <!-- Preconditions--> + <!-- Create 3 categories --> + <before> + <!-- Set the configuration for Generate "category/product" URL Rewrites to Yes (default)--> + <comment userInput="Enable SEO configuration setting to generate category/product URL Rewrites" stepKey="commentEnableUrlRewriteConfig"/> + <magentoCLI command="config:set catalog/seo/generate_category_product_rewrites 1" stepKey="enableGenerateUrlRewrite"/> + <!--Flush cache--> + <magentoCLI command="cache:flush" stepKey="cleanCache1"/> + + <createData entity="SimpleSubCategory" stepKey="simpleSubCategory1"/> + <createData entity="SubCategoryWithParent" stepKey="simpleSubCategory2"> + <requiredEntity createDataKey="simpleSubCategory1"/> + </createData> + <createData entity="SubCategoryWithParent" stepKey="simpleSubCategory3"> + <requiredEntity createDataKey="simpleSubCategory2"/> + </createData> + <!-- Create Simple product 1 and assign it to Category 3 --> + <createData entity="ApiSimpleProduct" stepKey="createSimpleProduct"> + <requiredEntity createDataKey="simpleSubCategory3"/> + </createData> + <!-- Set the configuration for Generate "category/product" URL Rewrites to No--> + <comment userInput="Disable SEO configuration setting to generate category/product URL Rewrites" stepKey="commentDisableUrlRewriteConfig"/> + <magentoCLI command="config:set catalog/seo/generate_category_product_rewrites 0" stepKey="disableGenerateUrlRewrite"/> + <!--Flush cache--> + <magentoCLI command="cache:flush" stepKey="cleanCache"/> + </before> + <after> + <deleteData createDataKey="createSimpleProduct" stepKey="deleteSimpleProduct"/> + <deleteData createDataKey="simpleSubCategory1" stepKey="deletesimpleSubCategory1"/> + <amOnPage url="{{AdminLogoutPage.url}}" stepKey="amOnLogoutPage"/> + <magentoCLI command="config:set catalog/seo/generate_category_product_rewrites 1" stepKey="resetConfigurationSetting"/> + <!--Flush cache--> + <magentoCLI command="cache:flush" stepKey="cleanCache2"/> + </after> + <!-- Steps --> + <!-- 1. Log in to Admin --> + <actionGroup ref="AdminLoginActionGroup" stepKey="loginAsAdmin"/> + <!-- 2. Open Marketing - SEO & Search - URL Rewrites --> + <amOnPage url="{{AdminUrlRewriteIndexPage.url}}" stepKey="amOnUrlRewriteIndexPage"/> + <click selector="{{AdminDataGridHeaderSection.filters}}" stepKey="openUrlRewriteGridFilters"/> + <fillField selector="{{AdminDataGridHeaderSection.filterFieldInput('request_path')}}" userInput="$createSimpleProduct.custom_attributes[url_key]$.html" stepKey="inputProductName"/> + <click selector="{{AdminDataGridHeaderSection.applyFilters}}" stepKey="clickOrderApplyFilters"/> + <waitForPageLoad stepKey="waitForPageToLoad"/> + <seeElement selector="{{AdminUrlRewriteIndexSection.gridCellByColumnValue('Request Path', $createSimpleProduct.custom_attributes[url_key]$.html)}}" stepKey="seeValue1"/> + <dontSeeElement selector="{{AdminUrlRewriteIndexSection.gridCellByColumnValue('Request Path', $simpleSubCategory1.custom_attributes[url_key]$/$createSimpleProduct.custom_attributes[url_key]$.html)}}" stepKey="seeValue2"/> + <dontSeeElement selector="{{AdminUrlRewriteIndexSection.gridCellByColumnValue('Request Path', $simpleSubCategory1.custom_attributes[url_key]$/$simpleSubCategory2.custom_attributes[url_key]$/$createSimpleProduct.custom_attributes[url_key]$.html)}}" stepKey="seeValue3"/> + <dontSeeElement selector="{{AdminUrlRewriteIndexSection.gridCellByColumnValue('Request Path', $simpleSubCategory1.custom_attributes[url_key]$/$simpleSubCategory2.custom_attributes[url_key]$/$simpleSubCategory3.custom_attributes[url_key]$/$createSimpleProduct.custom_attributes[url_key]$.html)}}" stepKey="seeValue4"/> + + <!-- 3. Edit Category 1 for DEFAULT Store View: --> + <actionGroup ref="SwitchCategoryStoreViewActionGroup" stepKey="switchStoreView"> + <argument name="Store" value="_defaultStore.name"/> + <argument name="CatName" value="$$simpleSubCategory1.name$$"/> + </actionGroup> + <click selector="{{AdminCategorySEOSection.SectionHeader}}" stepKey="openSeoSection2"/> + <uncheckOption selector="{{AdminCategorySEOSection.UrlKeyDefaultValueCheckbox}}" stepKey="uncheckRedirect2"/> + <fillField selector="{{AdminCategorySEOSection.UrlKeyInput}}" userInput="$simpleSubCategory1.custom_attributes[url_key]$-new" stepKey="changeURLKey"/> + <checkOption selector="{{AdminCategorySEOSection.UrlKeyRedirectCheckbox}}" stepKey="checkUrlKeyRedirect"/> + <!-- 4. Save Category 1 --> + <click selector="{{AdminCategoryMainActionsSection.SaveButton}}" stepKey="saveCategory"/> + <seeElement selector="{{AdminCategoryMessagesSection.SuccessMessage}}" stepKey="assertSuccessMessageAfterSaved"/> + + <!-- 5. Open Marketing - SEO & Search - URL Rewrites --> + <amOnPage url="{{AdminUrlRewriteIndexPage.url}}" stepKey="amOnUrlRewriteIndexPage2"/> + <click selector="{{AdminDataGridHeaderSection.filters}}" stepKey="openUrlRewriteGridFilters1"/> + <fillField selector="{{AdminDataGridHeaderSection.filterFieldInput('request_path')}}" userInput="$createSimpleProduct.custom_attributes[url_key]$.html" stepKey="inputProductName1"/> + <click selector="{{AdminDataGridHeaderSection.applyFilters}}" stepKey="clickOrderApplyFilters1"/> + <waitForPageLoad stepKey="waitForPageToLoad1"/> + <seeElement selector="{{AdminUrlRewriteIndexSection.gridCellByColumnValue('Request Path', $createSimpleProduct.custom_attributes[url_key]$.html)}}" stepKey="seeInListValue1"/> + <dontSeeElement selector="{{AdminUrlRewriteIndexSection.gridCellByColumnValue('Request Path', $simpleSubCategory1.custom_attributes[url_key]$/$createSimpleProduct.custom_attributes[url_key]$.html)}}" stepKey="seeInListValue2"/> + <dontSeeElement selector="{{AdminUrlRewriteIndexSection.gridCellByColumnValue('Request Path', $simpleSubCategory1.custom_attributes[url_key]$/$simpleSubCategory2.custom_attributes[url_key]$/$createSimpleProduct.custom_attributes[url_key]$.html)}}" stepKey="seeInListValue3"/> + <dontSeeElement selector="{{AdminUrlRewriteIndexSection.gridCellByColumnValue('Request Path', $simpleSubCategory1.custom_attributes[url_key]$/$simpleSubCategory2.custom_attributes[url_key]$/$simpleSubCategory3.custom_attributes[url_key]$/$createSimpleProduct.custom_attributes[url_key]$.html)}}" stepKey="seeInListValue4"/> + <dontSeeElement selector="{{AdminUrlRewriteIndexSection.gridCellByColumnValue('Request Path', $simpleSubCategory1.custom_attributes[url_key]$-new/$createSimpleProduct.custom_attributes[url_key]$.html)}}" stepKey="seeInListValue5"/> + <dontSeeElement selector="{{AdminUrlRewriteIndexSection.gridCellByColumnValue('Request Path', $simpleSubCategory1.custom_attributes[url_key]$-new/$simpleSubCategory2.custom_attributes[url_key]$/$createSimpleProduct.custom_attributes[url_key]$.html)}}" stepKey="seeInListValue6"/> + <dontSeeElement selector="{{AdminUrlRewriteIndexSection.gridCellByColumnValue('Request Path', $simpleSubCategory1.custom_attributes[url_key]$-new/$simpleSubCategory2.custom_attributes[url_key]$/$simpleSubCategory3.custom_attributes[url_key]$/$createSimpleProduct.custom_attributes[url_key]$.html)}}" stepKey="seeInListValue7"/> + + <amOnPage url="/$createSimpleProduct.custom_attributes[url_key]$.html" stepKey="navigateToProductPage"/> + <see selector="{{StorefrontProductInfoMainSection.productName}}" userInput="$$createSimpleProduct.name$$" stepKey="seeProductName"/> + + <amOnPage url="/$simpleSubCategory1.custom_attributes[url_key]$/$createSimpleProduct.custom_attributes[url_key]$.html" stepKey="navigateToProductPage2"/> + <see selector="{{StorefrontProductInfoMainSection.productName}}" userInput="$$createSimpleProduct.name$$" stepKey="seeProductName2"/> + <amOnPage url="/$simpleSubCategory1.custom_attributes[url_key]$/$simpleSubCategory2.custom_attributes[url_key]$/$createSimpleProduct.custom_attributes[url_key]$.html" stepKey="navigateToProductPage3"/> + <see selector="{{StorefrontProductInfoMainSection.productName}}" userInput="$$createSimpleProduct.name$$" stepKey="seeProductName3"/> + <amOnPage url="/$simpleSubCategory1.custom_attributes[url_key]$/$simpleSubCategory2.custom_attributes[url_key]$/$simpleSubCategory3.custom_attributes[url_key]$/$createSimpleProduct.custom_attributes[url_key]$.html" stepKey="navigateToProductPage4"/> + <see selector="{{StorefrontProductInfoMainSection.productName}}" userInput="$$createSimpleProduct.name$$" stepKey="seeProductName4"/> + + <amOnPage url="/$simpleSubCategory1.custom_attributes[url_key]$-new/$createSimpleProduct.custom_attributes[url_key]$.html" stepKey="navigateToProductPage5"/> + <see selector="{{StorefrontProductInfoMainSection.productName}}" userInput="$$createSimpleProduct.name$$" stepKey="seeProductName5"/> + <amOnPage url="/$simpleSubCategory1.custom_attributes[url_key]$-new/$simpleSubCategory2.custom_attributes[url_key]$/$createSimpleProduct.custom_attributes[url_key]$.html" stepKey="navigateToProductPage6"/> + <see selector="{{StorefrontProductInfoMainSection.productName}}" userInput="$$createSimpleProduct.name$$" stepKey="seeProductName6"/> + <amOnPage url="/$simpleSubCategory1.custom_attributes[url_key]$-new/$simpleSubCategory2.custom_attributes[url_key]$/$simpleSubCategory3.custom_attributes[url_key]$/$createSimpleProduct.custom_attributes[url_key]$.html" stepKey="navigateToProductPage7"/> + <see selector="{{StorefrontProductInfoMainSection.productName}}" userInput="$$createSimpleProduct.name$$" stepKey="seeProductName7"/> + </test> +</tests> diff --git a/app/code/Magento/UrlRewrite/Test/Mftf/Test/AdminUrlRewritesForProductInAnchorCategoriesTest/AdminUrlRewritesForProductsWithConfigurationTurnedOffTest.xml b/app/code/Magento/UrlRewrite/Test/Mftf/Test/AdminUrlRewritesForProductInAnchorCategoriesTest/AdminUrlRewritesForProductsWithConfigurationTurnedOffTest.xml new file mode 100644 index 0000000000000..1d460b9b668a0 --- /dev/null +++ b/app/code/Magento/UrlRewrite/Test/Mftf/Test/AdminUrlRewritesForProductInAnchorCategoriesTest/AdminUrlRewritesForProductsWithConfigurationTurnedOffTest.xml @@ -0,0 +1,57 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + /** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +--> +<tests xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/testSchema.xsd"> + <test name="AdminUrlRewritesForProductsWithConfigurationTurnedOffTest"> + <annotations> + <features value="Url Rewrite"/> + <stories value="No Url-rewrites for product if configuration to generate url rewrite for Generate 'category/product' URL Rewrites is enabled "/> + <title value="No auto generated of request path for simple product when assigned to subCategory"/> + <description value="No auto generated of request path when SEO configuration to Generate url rewrite for Generate 'category/product' URL Rewrites is set to No"/> + <severity value="CRITICAL"/> + <testCaseId value="MAGETWO-94803"/> + <group value="urlRewrite"/> + </annotations> + <before> + <!-- Set the configuration for Generate "category/product" URL Rewrites to Yes (default)--> + <comment userInput="Enable SEO configuration setting to generate category/product URL Rewrites" stepKey="commentEnableUrlRewriteConfig"/> + <magentoCLI command="config:set catalog/seo/generate_category_product_rewrites 1" stepKey="enableGenerateUrlRewrite"/> + <!--Flush cache--> + <magentoCLI command="cache:flush" stepKey="cleanCache1"/> + <createData entity="SimpleSubCategory" stepKey="simpleSubCategory1"/> + <!-- Create Simple product 1 and assign it to Category 1 --> + <createData entity="ApiSimpleProduct" stepKey="createSimpleProduct"> + <requiredEntity createDataKey="simpleSubCategory1"/> + </createData> + <!-- Set the configuration for Generate "category/product" URL Rewrites to No--> + <comment userInput="Disable SEO configuration setting to generate category/product URL Rewrites" stepKey="commentDisableUrlRewriteConfig"/> + <magentoCLI command="config:set catalog/seo/generate_category_product_rewrites 0" stepKey="disableGenerateUrlRewrite"/> + <!--Flush cache--> + <magentoCLI command="cache:flush" stepKey="cleanCache"/> + </before> + <after> + <deleteData createDataKey="createSimpleProduct" stepKey="deleteSimpleProduct"/> + <deleteData createDataKey="simpleSubCategory1" stepKey="deletesimpleSubCategory1"/> + <magentoCLI command="config:set catalog/seo/generate_category_product_rewrites 1" stepKey="resetConfigurationSetting"/> + <!--Flush cache--> + <magentoCLI command="cache:flush" stepKey="cleanCache2"/> + </after> + <!-- 1. Log in to Admin --> + <actionGroup ref="AdminLoginActionGroup" stepKey="loginAsAdmin"/> + <!-- 2. Open Marketing - SEO & Search - URL Rewrites --> + <amOnPage url="{{AdminUrlRewriteIndexPage.url}}" stepKey="amOnUrlRewriteIndexPage"/> + <click selector="{{AdminDataGridHeaderSection.filters}}" stepKey="openUrlRewriteGridFilters"/> + <fillField selector="{{AdminDataGridHeaderSection.filterFieldInput('request_path')}}" userInput="$createSimpleProduct.custom_attributes[url_key]$.html" stepKey="inputProductName"/> + <click selector="{{AdminDataGridHeaderSection.applyFilters}}" stepKey="clickOrderApplyFilters"/> + <waitForPageLoad stepKey="waitForPageToLoad"/> + <seeElement selector="{{AdminUrlRewriteIndexSection.gridCellByColumnValue('Request Path', $createSimpleProduct.custom_attributes[url_key]$.html)}}" stepKey="seeProducturl"/> + <dontSeeElement selector="{{AdminUrlRewriteIndexSection.gridCellByColumnValue('Request Path', $simpleSubCategory1.custom_attributes[url_key]$/$createSimpleProduct.custom_attributes[url_key]$.html)}}" stepKey="dontSeeCategoryProducturlKey"/> + <amOnPage url="/$simpleSubCategory1.custom_attributes[url_key]$/$createSimpleProduct.custom_attributes[url_key]$.html" stepKey="navigateToProductPage"/> + <see selector="{{StorefrontProductInfoMainSection.productName}}" userInput="$$createSimpleProduct.name$$" stepKey="seeProductName"/> + </test> +</tests> diff --git a/app/code/Magento/UrlRewrite/Test/Unit/Controller/RouterTest.php b/app/code/Magento/UrlRewrite/Test/Unit/Controller/RouterTest.php index c935b3c7ec4cb..a41d9d38bdcb3 100644 --- a/app/code/Magento/UrlRewrite/Test/Unit/Controller/RouterTest.php +++ b/app/code/Magento/UrlRewrite/Test/Unit/Controller/RouterTest.php @@ -6,27 +6,35 @@ namespace Magento\UrlRewrite\Test\Unit\Controller; use Magento\Framework\App\Action\Forward; +use Magento\Framework\App\Action\Redirect; +use Magento\Framework\App\ActionFactory; +use Magento\Framework\App\RequestInterface; +use Magento\Framework\App\ResponseInterface; use Magento\Framework\TestFramework\Unit\Helper\ObjectManager; use Magento\Framework\UrlInterface; +use Magento\Store\Model\StoreManagerInterface; +use Magento\UrlRewrite\Controller\Router; +use Magento\UrlRewrite\Model\UrlFinderInterface; use Magento\UrlRewrite\Service\V1\Data\UrlRewrite; use Magento\Store\Model\Store; +use Laminas\Stdlib\ParametersInterface; use PHPUnit\Framework\MockObject\MockObject; -use Zend\Stdlib\ParametersInterface; +use PHPUnit\Framework\TestCase; /** * Test class for UrlRewrite Controller Router * * @SuppressWarnings(PHPMD.CouplingBetweenObjects) */ -class RouterTest extends \PHPUnit\Framework\TestCase +class RouterTest extends TestCase { /** - * @var \Magento\UrlRewrite\Controller\Router + * @var Router */ private $router; /** - * @var \Magento\Framework\App\ActionFactory|MockObject + * @var ActionFactory|MockObject */ private $actionFactory; @@ -36,7 +44,7 @@ class RouterTest extends \PHPUnit\Framework\TestCase private $url; /** - * @var \Magento\Store\Model\StoreManagerInterface|MockObject + * @var StoreManagerInterface|MockObject */ private $storeManager; @@ -46,12 +54,12 @@ class RouterTest extends \PHPUnit\Framework\TestCase private $store; /** - * @var \Magento\Framework\App\ResponseInterface|MockObject + * @var ResponseInterface|MockObject */ private $response; /** - * @var \Magento\Framework\App\RequestInterface|MockObject + * @var RequestInterface|MockObject */ private $request; @@ -61,7 +69,7 @@ class RouterTest extends \PHPUnit\Framework\TestCase private $requestQuery; /** - * @var \Magento\UrlRewrite\Model\UrlFinderInterface|MockObject + * @var UrlFinderInterface|MockObject */ private $urlFinder; @@ -71,24 +79,24 @@ class RouterTest extends \PHPUnit\Framework\TestCase protected function setUp() { $objectManager = new ObjectManager($this); - $this->actionFactory = $this->createMock(\Magento\Framework\App\ActionFactory::class); + $this->actionFactory = $this->createMock(ActionFactory::class); $this->url = $this->createMock(UrlInterface::class); - $this->storeManager = $this->createMock(\Magento\Store\Model\StoreManagerInterface::class); + $this->storeManager = $this->createMock(StoreManagerInterface::class); $this->response = $this->createPartialMock( - \Magento\Framework\App\ResponseInterface::class, + ResponseInterface::class, ['setRedirect', 'sendResponse'] ); $this->requestQuery = $this->createMock(ParametersInterface::class); $this->request = $this->getMockBuilder(\Magento\Framework\App\Request\Http::class) ->disableOriginalConstructor()->getMock(); $this->request->method('getQuery')->willReturn($this->requestQuery); - $this->urlFinder = $this->createMock(\Magento\UrlRewrite\Model\UrlFinderInterface::class); + $this->urlFinder = $this->createMock(UrlFinderInterface::class); $this->store = $this->getMockBuilder( Store::class )->disableOriginalConstructor()->getMock(); $this->router = $objectManager->getObject( - \Magento\UrlRewrite\Controller\Router::class, + Router::class, [ 'actionFactory' => $this->actionFactory, 'url' => $this->url, @@ -104,9 +112,16 @@ protected function setUp() */ public function testNoRewriteExist() { - $this->urlFinder->expects($this->any())->method('findOneByData')->will($this->returnValue(null)); - $this->storeManager->expects($this->any())->method('getStore')->will($this->returnValue($this->store)); - $this->store->expects($this->any())->method('getId')->will($this->returnValue('current-store-id')); + $this->request->method('getPathInfo') + ->willReturn(''); + $this->request->method('getRequestString') + ->willReturn(''); + $this->urlFinder->method('findOneByData') + ->willReturn(null); + $this->storeManager->method('getStore') + ->willReturn($this->store); + $this->store->method('getId') + ->willReturn(1); $this->assertNull($this->router->match($this->request)); } @@ -118,55 +133,45 @@ public function testRewriteAfterStoreSwitcher() { $initialRequestPath = 'request-path'; $newRequestPath = 'new-request-path'; + $newTargetPath = 'new-target-path'; $oldStoreAlias = 'old-store'; $oldStoreId = 'old-store-id'; $currentStoreId = 'current-store-id'; $rewriteEntityType = 'entity-type'; $rewriteEntityId = 42; - $this->request - ->expects($this->any()) - ->method('getParam') + $this->request->method('getParam') ->with('___from_store') ->willReturn($oldStoreAlias); - $this->request - ->expects($this->any()) - ->method('getPathInfo') + $this->request->method('getPathInfo') + ->willReturn($initialRequestPath); + $this->request->method('getRequestString') ->willReturn($initialRequestPath); $oldStore = $this->getMockBuilder(Store::class) ->disableOriginalConstructor() ->getMock(); - $oldStore->expects($this->any()) - ->method('getId') + $oldStore->method('getId') ->willReturn($oldStoreId); - $this->store - ->expects($this->any()) - ->method('getId') + $this->store->method('getId') ->willReturn($currentStoreId); - $this->storeManager - ->expects($this->any()) - ->method('getStore') + $this->storeManager->method('getStore') ->willReturnMap([[$oldStoreAlias, $oldStore], [null, $this->store]]); $oldUrlRewrite = $this->getMockBuilder(UrlRewrite::class) ->disableOriginalConstructor() ->getMock(); - $oldUrlRewrite->expects($this->any()) - ->method('getEntityType') + $oldUrlRewrite->method('getEntityType') ->willReturn($rewriteEntityType); - $oldUrlRewrite->expects($this->any()) - ->method('getEntityId') + $oldUrlRewrite->method('getEntityId') ->willReturn($rewriteEntityId); - $oldUrlRewrite->expects($this->any()) - ->method('getRedirectType') + $oldUrlRewrite->method('getRedirectType') ->willReturn(0); $urlRewrite = $this->getMockBuilder(UrlRewrite::class) ->disableOriginalConstructor() ->getMock(); - $urlRewrite->expects($this->any()) - ->method('getRequestPath') + $urlRewrite->method('getRequestPath') ->willReturn($newRequestPath); - $this->urlFinder - ->expects($this->any()) - ->method('findOneByData') + $urlRewrite->method('getTargetPath') + ->willReturn($newTargetPath); + $this->urlFinder->method('findOneByData') ->willReturnMap( [ [ @@ -190,22 +195,23 @@ public function testRewriteAfterStoreSwitcher() */ public function testNoRewriteAfterStoreSwitcherWhenNoOldRewrite() { - $this->request->expects($this->any())->method('getPathInfo')->will($this->returnValue('request-path')); - $this->request->expects($this->any())->method('getParam')->with('___from_store') - ->will($this->returnValue('old-store')); + $this->request->method('getPathInfo')->willReturn('request-path'); + $this->request->method('getRequestString')->willReturn('request-path'); + $this->request->method('getParam')->with('___from_store') + ->willReturn('old-store'); $oldStore = $this->getMockBuilder(Store::class)->disableOriginalConstructor()->getMock(); - $this->storeManager->expects($this->any())->method('getStore') - ->will($this->returnValueMap([['old-store', $oldStore], [null, $this->store]])); - $oldStore->expects($this->any())->method('getId')->will($this->returnValue('old-store-id')); - $this->store->expects($this->any())->method('getId')->will($this->returnValue('current-store-id')); + $this->storeManager->method('getStore') + ->willReturnMap([['old-store', $oldStore], [null, $this->store]]); + $oldStore->method('getId')->willReturn('old-store-id'); + $this->store->method('getId')->willReturn('current-store-id'); $oldUrlRewrite = $this->getMockBuilder(UrlRewrite::class) ->disableOriginalConstructor()->getMock(); - $oldUrlRewrite->expects($this->any())->method('getEntityType')->will($this->returnValue('entity-type')); - $oldUrlRewrite->expects($this->any())->method('getEntityId')->will($this->returnValue('entity-id')); - $oldUrlRewrite->expects($this->any())->method('getRequestPath')->will($this->returnValue('request-path')); + $oldUrlRewrite->method('getEntityType')->willReturn('entity-type'); + $oldUrlRewrite->method('getEntityId')->willReturn('entity-id'); + $oldUrlRewrite->method('getRequestPath')->willReturn('request-path'); $urlRewrite = $this->getMockBuilder(UrlRewrite::class) ->disableOriginalConstructor()->getMock(); - $urlRewrite->expects($this->any())->method('getRequestPath')->will($this->returnValue('request-path')); + $urlRewrite->method('getRequestPath')->willReturn('request-path'); $this->assertNull($this->router->match($this->request)); } @@ -215,41 +221,40 @@ public function testNoRewriteAfterStoreSwitcherWhenNoOldRewrite() */ public function testNoRewriteAfterStoreSwitcherWhenOldRewriteEqualsToNewOne() { - $this->request->expects($this->any())->method('getPathInfo')->will($this->returnValue('request-path')); - $this->request->expects($this->any())->method('getParam')->with('___from_store') - ->will($this->returnValue('old-store')); + $this->request->method('getPathInfo')->willReturn('request-path'); + $this->request->method('getRequestString')->willReturn('request-path'); + $this->request->method('getParam')->with('___from_store') + ->willReturn('old-store'); $oldStore = $this->getMockBuilder(Store::class)->disableOriginalConstructor()->getMock(); - $this->storeManager->expects($this->any())->method('getStore') - ->will($this->returnValueMap([['old-store', $oldStore], [null, $this->store]])); - $oldStore->expects($this->any())->method('getId')->will($this->returnValue('old-store-id')); - $this->store->expects($this->any())->method('getId')->will($this->returnValue('current-store-id')); + $this->storeManager->method('getStore') + ->willReturnMap([['old-store', $oldStore], [null, $this->store]]); + $oldStore->method('getId')->willReturn('old-store-id'); + $this->store->method('getId')->willReturn('current-store-id'); $oldUrlRewrite = $this->getMockBuilder(UrlRewrite::class) ->disableOriginalConstructor()->getMock(); - $oldUrlRewrite->expects($this->any())->method('getEntityType')->will($this->returnValue('entity-type')); - $oldUrlRewrite->expects($this->any())->method('getEntityId')->will($this->returnValue('entity-id')); - $oldUrlRewrite->expects($this->any())->method('getRequestPath')->will($this->returnValue('old-request-path')); + $oldUrlRewrite->method('getEntityType')->willReturn('entity-type'); + $oldUrlRewrite->method('getEntityId')->willReturn('entity-id'); + $oldUrlRewrite->method('getRequestPath')->willReturn('old-request-path'); $urlRewrite = $this->getMockBuilder(UrlRewrite::class) ->disableOriginalConstructor()->getMock(); - $urlRewrite->expects($this->any())->method('getRequestPath')->will($this->returnValue('old-request-path')); + $urlRewrite->method('getRequestPath')->willReturn('old-request-path'); - $this->urlFinder->expects($this->any())->method('findOneByData')->will( - $this->returnValueMap( + $this->urlFinder->method('findOneByData')->willReturnMap( + [ + [ + [UrlRewrite::REQUEST_PATH => 'request-path', UrlRewrite::STORE_ID => 'old-store-id'], + $oldUrlRewrite, + ], [ [ - [UrlRewrite::REQUEST_PATH => 'request-path', UrlRewrite::STORE_ID => 'old-store-id'], - $oldUrlRewrite, + UrlRewrite::ENTITY_TYPE => 'entity-type', + UrlRewrite::ENTITY_ID => 'entity-id', + UrlRewrite::STORE_ID => 'current-store-id', + UrlRewrite::IS_AUTOGENERATED => 1, ], - [ - [ - UrlRewrite::ENTITY_TYPE => 'entity-type', - UrlRewrite::ENTITY_ID => 'entity-id', - UrlRewrite::STORE_ID => 'current-store-id', - UrlRewrite::IS_AUTOGENERATED => 1, - ], - $urlRewrite - ], - ] - ) + $urlRewrite + ], + ] ); $this->assertNull($this->router->match($this->request)); @@ -260,42 +265,108 @@ public function testNoRewriteAfterStoreSwitcherWhenOldRewriteEqualsToNewOne() */ public function testMatchWithRedirect() { - $this->storeManager->expects($this->any())->method('getStore')->will($this->returnValue($this->store)); + $queryParams = []; + $redirectType = 'redirect-code'; + $requestPath = 'request-path'; + $targetPath = 'target-path'; + $newTargetPath = 'new-target-path'; + $this->storeManager->method('getStore') + ->willReturn($this->store); + $this->request->method('getPathInfo') + ->willReturn($requestPath); + $this->request->method('getRequestString') + ->willReturn($requestPath); $urlRewrite = $this->getMockBuilder(UrlRewrite::class) - ->disableOriginalConstructor()->getMock(); - $urlRewrite->expects($this->any())->method('getRedirectType')->will($this->returnValue('redirect-code')); - $urlRewrite->expects($this->any())->method('getTargetPath')->will($this->returnValue('target-path')); - $this->urlFinder->expects($this->any())->method('findOneByData')->will($this->returnValue($urlRewrite)); - $this->response->expects($this->once())->method('setRedirect') - ->with('new-target-path', 'redirect-code'); - $this->url->expects($this->once())->method('getUrl')->with('', ['_direct' => 'target-path']) - ->will($this->returnValue('new-target-path')); - $this->request->expects($this->once())->method('setDispatched')->with(true); - $this->actionFactory->expects($this->once())->method('create') - ->with(\Magento\Framework\App\Action\Redirect::class); + ->disableOriginalConstructor() + ->getMock(); + $urlRewrite->method('getRedirectType')->willReturn($redirectType); + $urlRewrite->method('getRequestPath')->willReturn($requestPath); + $urlRewrite->method('getTargetPath')->willReturn($targetPath); + $this->urlFinder->method('findOneByData')->willReturn($urlRewrite); + $this->response->expects($this->once()) + ->method('setRedirect') + ->with($newTargetPath, $redirectType); + $this->request->expects($this->once()) + ->method('getParams') + ->willReturn($queryParams); + $this->url->expects($this->once()) + ->method('getUrl') + ->with( + '', + ['_direct' => $targetPath, '_query' => $queryParams] + ) + ->willReturn($newTargetPath); + $this->request->expects($this->once()) + ->method('setDispatched') + ->with(true); + $this->actionFactory->expects($this->once()) + ->method('create') + ->with(Redirect::class); $this->router->match($this->request); } /** - * @return void + * @param string $requestPath + * @param string $targetPath + * @param bool $shouldRedirect + * @dataProvider customInternalRedirectDataProvider */ - public function testMatchWithCustomInternalRedirect() + public function testMatchWithCustomInternalRedirect($requestPath, $targetPath, $shouldRedirect) { - $this->storeManager->expects($this->any())->method('getStore')->will($this->returnValue($this->store)); + $queryParams = []; + $redirectType = 'redirect-code'; + $this->storeManager->method('getStore') + ->willReturn($this->store); + $this->request->method('getPathInfo') + ->willReturn($requestPath); + $this->request->method('getRequestString') + ->willReturn($requestPath); $urlRewrite = $this->getMockBuilder(UrlRewrite::class) - ->disableOriginalConstructor()->getMock(); - $urlRewrite->expects($this->any())->method('getEntityType')->will($this->returnValue('custom')); - $urlRewrite->expects($this->any())->method('getRedirectType')->will($this->returnValue('redirect-code')); - $urlRewrite->expects($this->any())->method('getTargetPath')->will($this->returnValue('target-path')); - $this->urlFinder->expects($this->any())->method('findOneByData')->will($this->returnValue($urlRewrite)); - $this->response->expects($this->once())->method('setRedirect')->with('a', 'redirect-code'); - $this->url->expects($this->once())->method('getUrl')->with('', ['_direct' => 'target-path'])->willReturn('a'); - $this->request->expects($this->once())->method('setDispatched')->with(true); - $this->actionFactory->expects($this->once())->method('create') - ->with(\Magento\Framework\App\Action\Redirect::class); + ->disableOriginalConstructor() + ->getMock(); + $urlRewrite->method('getEntityType')->willReturn('custom'); + $urlRewrite->method('getRedirectType')->willReturn($redirectType); + $urlRewrite->method('getRequestPath')->willReturn($requestPath); + $urlRewrite->method('getTargetPath')->willReturn($targetPath); + $this->urlFinder->method('findOneByData')->willReturn($urlRewrite); - $this->router->match($this->request); + if ($shouldRedirect) { + $this->request->method('getParams')->willReturn($queryParams); + $this->response->expects($this->once()) + ->method('setRedirect') + ->with('a', $redirectType); + $this->url->expects($this->once()) + ->method('getUrl') + ->with( + '', + ['_direct' => $targetPath, '_query' => $queryParams] + ) + ->willReturn('a'); + $this->request->expects($this->once()) + ->method('setDispatched') + ->with(true); + $this->actionFactory->expects($this->once()) + ->method('create') + ->with(Redirect::class); + } + + $routerResult = $this->router->match($this->request); + + if (!$shouldRedirect) { + $this->assertNull($routerResult); + } + } + + /** + * @return array + */ + public function customInternalRedirectDataProvider() + { + return [ + ['request-path', 'target-path', true], + ['/', '/', false], + ]; } /** @@ -304,18 +375,27 @@ public function testMatchWithCustomInternalRedirect() */ public function testMatchWithCustomExternalRedirect($targetPath) { - $this->storeManager->expects($this->any())->method('getStore')->will($this->returnValue($this->store)); + $requestPath = 'request-path'; + $this->storeManager->method('getStore')->willReturn($this->store); + $this->request->method('getPathInfo') + ->willReturn($requestPath); + $this->request->method('getRequestString') + ->willReturn($requestPath); $urlRewrite = $this->getMockBuilder(UrlRewrite::class) ->disableOriginalConstructor()->getMock(); - $urlRewrite->expects($this->any())->method('getEntityType')->will($this->returnValue('custom')); - $urlRewrite->expects($this->any())->method('getRedirectType')->will($this->returnValue('redirect-code')); - $urlRewrite->expects($this->any())->method('getTargetPath')->will($this->returnValue($targetPath)); - $this->urlFinder->expects($this->any())->method('findOneByData')->will($this->returnValue($urlRewrite)); - $this->response->expects($this->once())->method('setRedirect')->with($targetPath, 'redirect-code'); + $urlRewrite->method('getEntityType')->willReturn('custom'); + $urlRewrite->method('getRedirectType')->willReturn('redirect-code'); + $urlRewrite->method('getRequestPath')->willReturn($requestPath); + $urlRewrite->method('getTargetPath')->willReturn($targetPath); + $this->urlFinder->method('findOneByData')->willReturn($urlRewrite); + $this->response->expects($this->once()) + ->method('setRedirect') + ->with($targetPath, 'redirect-code'); + $this->request->expects($this->never())->method('getParams'); $this->url->expects($this->never())->method('getUrl'); $this->request->expects($this->once())->method('setDispatched')->with(true); $this->actionFactory->expects($this->once())->method('create') - ->with(\Magento\Framework\App\Action\Redirect::class); + ->with(Redirect::class); $this->router->match($this->request); } @@ -336,18 +416,23 @@ public function externalRedirectTargetPathDataProvider() */ public function testMatch() { - $this->storeManager->expects($this->any())->method('getStore')->will($this->returnValue($this->store)); + $requestPath = 'request-path'; + $this->storeManager->method('getStore')->willReturn($this->store); + $this->request->method('getPathInfo') + ->willReturn($requestPath); + $this->request->method('getRequestString') + ->willReturn($requestPath); $urlRewrite = $this->getMockBuilder(UrlRewrite::class) ->disableOriginalConstructor()->getMock(); - $urlRewrite->expects($this->any())->method('getRedirectType')->will($this->returnValue(0)); - $urlRewrite->expects($this->any())->method('getTargetPath')->will($this->returnValue('target-path')); - $urlRewrite->expects($this->any())->method('getRequestPath')->will($this->returnValue('request-path')); - $this->urlFinder->expects($this->any())->method('findOneByData')->will($this->returnValue($urlRewrite)); + $urlRewrite->method('getRedirectType')->willReturn(0); + $urlRewrite->method('getRequestPath')->willReturn($requestPath); + $urlRewrite->method('getTargetPath')->willReturn('target-path'); + $this->urlFinder->method('findOneByData')->willReturn($urlRewrite); $this->request->expects($this->once())->method('setPathInfo')->with('/target-path'); $this->request->expects($this->once())->method('setAlias') ->with(UrlInterface::REWRITE_REQUEST_PATH_ALIAS, 'request-path'); $this->actionFactory->expects($this->once())->method('create') - ->with(\Magento\Framework\App\Action\Forward::class); + ->with(Forward::class); $this->router->match($this->request); } diff --git a/app/code/Magento/UrlRewrite/Test/Unit/Controller/Url/Rewrite/InlineEditTest.php b/app/code/Magento/UrlRewrite/Test/Unit/Controller/Url/Rewrite/InlineEditTest.php new file mode 100644 index 0000000000000..9fd4a022061cb --- /dev/null +++ b/app/code/Magento/UrlRewrite/Test/Unit/Controller/Url/Rewrite/InlineEditTest.php @@ -0,0 +1,302 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +declare(strict_types=1); + +namespace Magento\UrlRewrite\Test\Unit\Controller\Url\Rewrite; + +use Magento\Backend\App\Action\Context; +use Magento\Framework\App\RequestInterface; +use Magento\Framework\Controller\Result\Json; +use Magento\Framework\Controller\Result\JsonFactory; +use Magento\Framework\TestFramework\Unit\Helper\ObjectManager; +use Magento\UrlRewrite\Controller\Adminhtml\Url\Rewrite\InlineEdit; +use Magento\UrlRewrite\Model\ResourceModel\UrlRewrite as UrlRewriteResource; +use Magento\UrlRewrite\Model\ResourceModel\UrlRewriteFactory as UrlRewriteResourceFactory; +use Magento\UrlRewrite\Model\UrlRewrite; +use Magento\UrlRewrite\Model\UrlRewriteFactory; +use PHPUnit\Framework\MockObject\MockObject; +use PHPUnit\Framework\TestCase; + +/** + * Url rewrite inline edit action unit test class + * + * @SuppressWarnings(PHPMD.CouplingBetweenObjects) + */ +class InlineEditTest extends TestCase +{ + /** + * @var ObjectManager + */ + private $objectManager; + + /** + * @var RequestInterface|MockObject + */ + private $request; + + /** + * @var Context|MockObject + */ + private $contextMock; + + /** + * @var JsonFactory|MockObject + */ + private $jsonFactory; + + /** + * @var Json|MockObject + */ + private $resultJson; + + /** + * @var UrlRewrite|MockObject + */ + private $urlRewrite; + + /** + * @var UrlRewriteFactory|MockObject + */ + private $urlRewriteFactory; + + /** + * @var UrlRewriteResource|MockObject + */ + private $urlRewriteResource; + + /** + * @var urlRewriteResourceFactory|MockObject + */ + private $urlRewriteResourceFactory; + + /** + * @var InlineEdit|MockObject + */ + private $inlineEditController; + + /** + * SetUp method + */ + protected function setUp(): void + { + $this->objectManager = new ObjectManager($this); + + $this->request = $this->getMockForAbstractClass( + RequestInterface::class, + [], + '', + false + ); + + $this->contextMock = $this->objectManager->getObject( + Context::class, + [ + 'request' => $this->request + ] + ); + + $this->resultJson = $this->createMock(Json::class); + $this->jsonFactory = $this->createPartialMock( + JsonFactory::class, + ['create'] + ); + + $this->urlRewrite = $this->createPartialMock( + UrlRewrite::class, + ['addData', 'getId'] + ); + $this->urlRewriteFactory = $this->createPartialMock( + UrlRewriteFactory::class, + ['create'] + ); + + $this->urlRewriteResource = $this->createPartialMock( + UrlRewriteResource::class, + ['load', 'save'] + ); + $this->urlRewriteResourceFactory = $this->createPartialMock( + urlRewriteResourceFactory::class, + ['create'] + ); + + $this->inlineEditController = $this->objectManager->getObject( + InlineEdit::class, + [ + 'context' => $this->contextMock, + 'urlRewriteFactory' => $this->urlRewriteFactory, + 'urlRewriteResourceFactory' => $this->urlRewriteResourceFactory, + 'jsonFactory' => $this->jsonFactory + ] + ); + } + + /** + * Prepare mocks for tests + * + * @return void + */ + private function prepareMocksWithParamsForTestExecute(): void + { + $postData = [ + 1 => [ + 'request_path' => 'category-1.html', + 'redirect_type' => 0, + 'url_rewrite_id' => 1 + ] + ]; + $this->request->expects($this->any()) + ->method('getParam') + ->willReturnMap( + [ + ['isAjax', null, true], + ['items', [], $postData] + ] + ); + + $this->jsonFactory->expects($this->once()) + ->method('create') + ->willReturn($this->resultJson); + + $this->urlRewriteResourceFactory->expects($this->once()) + ->method('create') + ->willReturn($this->urlRewriteResource); + + $this->urlRewriteResource->expects($this->once()) + ->method('load') + ->with($this->urlRewrite, 1) + ->willReturn($this->urlRewrite); + + $this->urlRewriteFactory->expects($this->once()) + ->method('create') + ->willReturn($this->urlRewrite); + + $this->urlRewrite->expects($this->once()) + ->method('addData') + ->with(array_shift($postData)) + ->willReturnSelf(); + } + + /** + * Execute test with runtime exception + * + * @return void + */ + public function testExecuteWithRuntimeException(): void + { + $this->prepareMocksWithParamsForTestExecute(); + $this->urlRewrite->expects($this->once()) + ->method('getId') + ->willReturn(1); + $this->urlRewriteResource->expects($this->once()) + ->method('save') + ->with($this->urlRewrite) + ->willThrowException(new \RuntimeException('RuntimeException')); + $this->resultJson->expects($this->once()) + ->method('setData') + ->with( + [ + 'messages' => [ + '[Url rewrite ID: 1] RuntimeException' + ], + 'error' => true + ] + ) + ->willReturnSelf(); + + $this->assertSame($this->resultJson, $this->inlineEditController->execute()); + } + + /** + * Execute test with exception + * + * @return void + */ + public function testExecuteWithException(): void + { + $this->prepareMocksWithParamsForTestExecute(); + $this->urlRewrite->expects($this->once()) + ->method('getId') + ->willReturn(1); + $this->urlRewriteResource->expects($this->once()) + ->method('save') + ->with($this->urlRewrite) + ->willThrowException(new \Exception('Exception')); + $this->resultJson->expects($this->once()) + ->method('setData') + ->with( + [ + 'messages' => [ + '[Url rewrite ID: 1] Something went wrong while saving the url rewrite.' + ], + 'error' => true + ] + ) + ->willReturnSelf(); + + $this->assertSame($this->resultJson, $this->inlineEditController->execute()); + } + + /** + * Execute test without post data + * + * @return void + */ + public function testExecuteWithoutData(): void + { + $this->jsonFactory->expects($this->once()) + ->method('create') + ->willReturn($this->resultJson); + $this->request->expects($this->any()) + ->method('getParam') + ->willReturnMap( + [ + ['isAjax', null, true], + ['items', [], []] + ] + ); + $this->resultJson->expects($this->once()) + ->method('setData') + ->with( + [ + 'messages' => [ + __('Please correct the data sent.') + ], + 'error' => true + ] + ) + ->willReturnSelf(); + + $this->assertSame($this->resultJson, $this->inlineEditController->execute()); + } + + /** + * Execute test without exceptions + * + * @return void + */ + public function testExecuteAction(): void + { + $this->prepareMocksWithParamsForTestExecute(); + $this->urlRewriteResource->expects($this->once()) + ->method('save') + ->with($this->urlRewrite) + ->willReturnSelf(); + $this->resultJson->expects($this->once()) + ->method('setData') + ->with( + [ + 'messages' => [], + 'error' => false + ] + ) + ->willReturnSelf(); + + $this->assertSame( + $this->resultJson, + $this->inlineEditController->execute() + ); + } +} diff --git a/app/code/Magento/UrlRewrite/Test/Unit/Controller/Url/Rewrite/MassDeleteTest.php b/app/code/Magento/UrlRewrite/Test/Unit/Controller/Url/Rewrite/MassDeleteTest.php new file mode 100644 index 0000000000000..bf58fe3d7067a --- /dev/null +++ b/app/code/Magento/UrlRewrite/Test/Unit/Controller/Url/Rewrite/MassDeleteTest.php @@ -0,0 +1,186 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +declare(strict_types=1); + +namespace Magento\UrlRewrite\Test\Unit\Controller\Url\Rewrite; + +use Magento\Backend\App\Action\Context; +use Magento\Backend\Model\View\Result\Redirect; +use Magento\Framework\Controller\Result\RedirectFactory; +use Magento\Framework\Exception\LocalizedException; +use Magento\Framework\Message\ManagerInterface; +use Magento\Framework\TestFramework\Unit\Helper\ObjectManager; +use Magento\Ui\Component\MassAction\Filter; +use Magento\UrlRewrite\Controller\Adminhtml\Url\Rewrite\MassDelete; +use Magento\UrlRewrite\Model\ResourceModel\UrlRewriteCollection as Collection; +use Magento\UrlRewrite\Model\ResourceModel\UrlRewriteCollectionFactory as CollectionFactory; +use Magento\UrlRewrite\Model\UrlRewrite; +use PHPUnit\Framework\MockObject\MockObject; +use PHPUnit\Framework\TestCase; + +/** + * Url rewrite mass delete action unit test class + * + * @SuppressWarnings(PHPMD.CouplingBetweenObjects) + */ +class MassDeleteTest extends TestCase +{ + /** + * @var ObjectManager + */ + private $objectManager; + + /** + * @var ManagerInterface|MockObject + */ + private $messageManagerMock; + + /** + * @var RedirectFactory|MockObject + */ + private $resultRedirectMock; + + /** + * @var Context|MockObject + */ + private $contextMock; + + /** + * @var Filter|MockObject + */ + private $filterMock; + + /** + * @var Collection|MockObject + */ + private $urlRewriteCollectionMock; + + /** + * @var CollectionFactory|MockObject + */ + private $collectionFactoryMock; + + /** + * @var MassDelete|object + */ + private $massDeleteController; + + /** + * @var RedirectFactory|MockObject + */ + private $resultRedirectFactoryMock; + + /** + * SetUp method + */ + protected function setUp(): void + { + $this->objectManager = new ObjectManager($this); + + $this->messageManagerMock = $this->createMock(ManagerInterface::class); + + $this->resultRedirectFactoryMock = $this->createPartialMock( + RedirectFactory::class, + ['create'] + ); + $this->resultRedirectMock = $this->createMock(Redirect::class); + + $this->contextMock = $this->createMock(Context::class); + + $this->filterMock = $this->getMockBuilder(Filter::class) + ->disableOriginalConstructor() + ->getMock(); + + $this->contextMock->expects($this->any()) + ->method('getMessageManager') + ->willReturn($this->messageManagerMock); + $this->contextMock->expects($this->any()) + ->method('getResultRedirectFactory') + ->willReturn($this->resultRedirectFactoryMock); + + $this->collectionFactoryMock = $this->createPartialMock( + CollectionFactory::class, + ['create'] + ); + $this->urlRewriteCollectionMock = + $this->createMock(Collection::class); + + $this->massDeleteController = $this->objectManager->getObject( + MassDelete::class, + [ + 'context' => $this->contextMock, + 'filter' => $this->filterMock, + 'collectionFactory' => $this->collectionFactoryMock + ] + ); + } + + /** + * Test mass delete action + * + * @throws LocalizedException + */ + public function testMassDeleteAction(): void + { + $collection = [ + $this->getUrlRewriteMock(), + $this->getUrlRewriteMock(), + $this->getUrlRewriteMock(true) + ]; + + $this->collectionFactoryMock->expects($this->once()) + ->method('create') + ->willReturn($this->urlRewriteCollectionMock); + + $this->filterMock->expects($this->once()) + ->method('getCollection') + ->with($this->urlRewriteCollectionMock) + ->willReturn($this->urlRewriteCollectionMock); + + $this->urlRewriteCollectionMock->expects($this->once()) + ->method('getIterator') + ->willReturn(new \ArrayIterator($collection)); + + $this->messageManagerMock->expects($this->once()) + ->method('addSuccessMessage') + ->with(__('A total of %1 record(s) have been deleted.', 2)); + $this->messageManagerMock->expects($this->once()) + ->method('addErrorMessage') + ->with(__('A total of %1 record(s) haven\'t been deleted.', 1)); + + $this->resultRedirectFactoryMock->expects($this->once()) + ->method('create') + ->willReturn($this->resultRedirectMock); + $this->resultRedirectMock->expects($this->once()) + ->method('setPath') + ->with('*/*/') + ->willReturnSelf(); + + $this->assertSame($this->resultRedirectMock, $this->massDeleteController->execute()); + } + + /** + * Create url rewrite model mock + * + * @param bool $exception + * @return MockObject + */ + private function getUrlRewriteMock($exception = false): MockObject + { + $urlRewrite = $this->createPartialMock(UrlRewrite::class, ['delete']); + $urlRewriteInvocationMocker = $urlRewrite->expects($this->once()) + ->method('delete'); + if ($exception) { + $urlRewriteInvocationMocker->willThrowException( + new \Exception('Test delete exception') + ); + } else { + $urlRewriteInvocationMocker->willReturn(true); + } + + return $urlRewrite; + } +} diff --git a/app/code/Magento/UrlRewrite/Test/Unit/Model/Storage/DbStorageTest.php b/app/code/Magento/UrlRewrite/Test/Unit/Model/Storage/DbStorageTest.php index 697ce33be0fa7..946f30f609290 100644 --- a/app/code/Magento/UrlRewrite/Test/Unit/Model/Storage/DbStorageTest.php +++ b/app/code/Magento/UrlRewrite/Test/Unit/Model/Storage/DbStorageTest.php @@ -6,64 +6,68 @@ namespace Magento\UrlRewrite\Test\Unit\Model\Storage; +use Magento\Framework\Api\DataObjectHelper; +use Magento\Framework\App\ResourceConnection; +use Magento\Framework\DB\Adapter\AdapterInterface; use Magento\Framework\DB\Select; use Magento\Framework\TestFramework\Unit\Helper\ObjectManager; use Magento\UrlRewrite\Model\Storage\DbStorage; use Magento\UrlRewrite\Service\V1\Data\UrlRewrite; +use Magento\UrlRewrite\Service\V1\Data\UrlRewriteFactory; +use PHPUnit\Framework\MockObject\MockObject; +use PHPUnit\Framework\TestCase; -class DbStorageTest extends \PHPUnit\Framework\TestCase +class DbStorageTest extends TestCase { /** - * @var \Magento\UrlRewrite\Service\V1\Data\UrlRewriteFactory|\PHPUnit_Framework_MockObject_MockObject + * @var UrlRewriteFactory|MockObject */ - protected $urlRewriteFactory; + private $urlRewriteFactory; /** - * @var \Magento\Framework\Api\DataObjectHelper|\PHPUnit_Framework_MockObject_MockObject + * @var DataObjectHelper|MockObject */ - protected $dataObjectHelper; + private $dataObjectHelper; /** - * @var \Magento\Framework\DB\Adapter\AdapterInterface|\PHPUnit_Framework_MockObject_MockObject + * @var AdapterInterface|MockObject */ - protected $connectionMock; + private $connectionMock; /** - * @var \Magento\Framework\DB\Select|\PHPUnit_Framework_MockObject_MockObject + * @var \Magento\Framework\DB\Select|MockObject */ - protected $select; + private $select; /** - * @var \Magento\Framework\App\ResourceConnection|\PHPUnit_Framework_MockObject_MockObject + * @var ResourceConnection|MockObject */ - protected $resource; + private $resource; /** - * @var \Magento\UrlRewrite\Model\Storage\DbStorage + * @var DbStorage */ - protected $storage; + private $storage; protected function setUp() { - $this->urlRewriteFactory = $this->getMockBuilder(\Magento\UrlRewrite\Service\V1\Data\UrlRewriteFactory::class) + $this->urlRewriteFactory = $this->getMockBuilder(UrlRewriteFactory::class) ->setMethods(['create']) ->disableOriginalConstructor()->getMock(); - $this->dataObjectHelper = $this->createMock(\Magento\Framework\Api\DataObjectHelper::class); - $this->connectionMock = $this->createMock(\Magento\Framework\DB\Adapter\AdapterInterface::class); + $this->dataObjectHelper = $this->createMock(DataObjectHelper::class); + $this->connectionMock = $this->createMock(AdapterInterface::class); $this->select = $this->getMockBuilder(Select::class) ->disableOriginalConstructor() ->getMock(); - $this->resource = $this->createMock(\Magento\Framework\App\ResourceConnection::class); + $this->resource = $this->createMock(ResourceConnection::class); - $this->resource->expects($this->any()) - ->method('getConnection') - ->will($this->returnValue($this->connectionMock)); - $this->connectionMock->expects($this->any()) - ->method('select') - ->will($this->returnValue($this->select)); + $this->resource->method('getConnection') + ->willReturn($this->connectionMock); + $this->connectionMock->method('select') + ->willReturn($this->select); $this->storage = (new ObjectManager($this))->getObject( - \Magento\UrlRewrite\Model\Storage\DbStorage::class, + DbStorage::class, [ 'urlRewriteFactory' => $this->urlRewriteFactory, 'dataObjectHelper' => $this->dataObjectHelper, @@ -84,32 +88,32 @@ public function testFindAllByData() ->method('where') ->with('col2 IN (?)', 'val2'); - $this->connectionMock->expects($this->any()) + $this->connectionMock ->method('quoteIdentifier') ->will($this->returnArgument(0)); $this->connectionMock->expects($this->once()) ->method('fetchAll') ->with($this->select) - ->will($this->returnValue([['row1'], ['row2']])); + ->willReturn([['row1'], ['row2']]); $this->dataObjectHelper->expects($this->at(0)) ->method('populateWithArray') - ->with(['urlRewrite1'], ['row1'], \Magento\UrlRewrite\Service\V1\Data\UrlRewrite::class) + ->with(['urlRewrite1'], ['row1'], UrlRewrite::class) ->will($this->returnSelf()); $this->urlRewriteFactory->expects($this->at(0)) ->method('create') - ->will($this->returnValue(['urlRewrite1'])); + ->willReturn(['urlRewrite1']); $this->dataObjectHelper->expects($this->at(1)) ->method('populateWithArray') - ->with(['urlRewrite2'], ['row2'], \Magento\UrlRewrite\Service\V1\Data\UrlRewrite::class) + ->with(['urlRewrite2'], ['row2'], UrlRewrite::class) ->will($this->returnSelf()); $this->urlRewriteFactory->expects($this->at(1)) ->method('create') - ->will($this->returnValue(['urlRewrite2'])); + ->willReturn(['urlRewrite2']); $this->assertEquals([['urlRewrite1'], ['urlRewrite2']], $this->storage->findAllByData($data)); } @@ -126,25 +130,24 @@ public function testFindOneByData() ->method('where') ->with('col2 IN (?)', 'val2'); - $this->connectionMock->expects($this->any()) - ->method('quoteIdentifier') + $this->connectionMock->method('quoteIdentifier') ->will($this->returnArgument(0)); $this->connectionMock->expects($this->once()) ->method('fetchRow') ->with($this->select) - ->will($this->returnValue(['row1'])); + ->willReturn(['row1']); $this->connectionMock->expects($this->never())->method('fetchAll'); $this->dataObjectHelper->expects($this->at(0)) ->method('populateWithArray') - ->with(['urlRewrite1'], ['row1'], \Magento\UrlRewrite\Service\V1\Data\UrlRewrite::class) + ->with(['urlRewrite1'], ['row1'], UrlRewrite::class) ->will($this->returnSelf()); $this->urlRewriteFactory->expects($this->at(0)) ->method('create') - ->will($this->returnValue(['urlRewrite1'])); + ->willReturn(['urlRewrite1']); $this->assertEquals(['urlRewrite1'], $this->storage->findOneByData($data)); } @@ -153,8 +156,8 @@ public function testFindOneByDataWithRequestPath() { $origRequestPath = 'page-one'; $data = [ - 'col1' => 'val1', - 'col2' => 'val2', + 'col1' => 'val1', + 'col2' => 'val2', UrlRewrite::REQUEST_PATH => $origRequestPath, ]; @@ -170,31 +173,31 @@ public function testFindOneByDataWithRequestPath() ->method('where') ->with('request_path IN (?)', [$origRequestPath, $origRequestPath . '/']); - $this->connectionMock->expects($this->any()) - ->method('quoteIdentifier') + $this->connectionMock->method('quoteIdentifier') ->will($this->returnArgument(0)); $this->connectionMock->expects($this->never()) ->method('fetchRow'); $urlRewriteRowInDb = [ - UrlRewrite::REQUEST_PATH => $origRequestPath, + UrlRewrite::REQUEST_PATH => $origRequestPath, + UrlRewrite::TARGET_PATH => $origRequestPath, UrlRewrite::REDIRECT_TYPE => 0, ]; $this->connectionMock->expects($this->once()) ->method('fetchAll') ->with($this->select) - ->will($this->returnValue([$urlRewriteRowInDb])); + ->willReturn([$urlRewriteRowInDb]); $this->dataObjectHelper->expects($this->at(0)) ->method('populateWithArray') - ->with(['urlRewrite1'], $urlRewriteRowInDb, \Magento\UrlRewrite\Service\V1\Data\UrlRewrite::class) - ->will($this->returnSelf()); + ->with(['urlRewrite1'], $urlRewriteRowInDb, UrlRewrite::class) + ->willReturnSelf(); $this->urlRewriteFactory->expects($this->at(0)) ->method('create') - ->will($this->returnValue(['urlRewrite1'])); + ->willReturn(['urlRewrite1']); $this->assertEquals(['urlRewrite1'], $this->storage->findOneByData($data)); } @@ -203,8 +206,8 @@ public function testFindOneByDataWithRequestPathIsDifferent() { $origRequestPath = 'page-one'; $data = [ - 'col1' => 'val1', - 'col2' => 'val2', + 'col1' => 'val1', + 'col2' => 'val2', UrlRewrite::REQUEST_PATH => $origRequestPath, ]; @@ -220,44 +223,44 @@ public function testFindOneByDataWithRequestPathIsDifferent() ->method('where') ->with('request_path IN (?)', [$origRequestPath, $origRequestPath . '/']); - $this->connectionMock->expects($this->any()) - ->method('quoteIdentifier') + $this->connectionMock->method('quoteIdentifier') ->will($this->returnArgument(0)); $this->connectionMock->expects($this->never()) ->method('fetchRow'); $urlRewriteRowInDb = [ - UrlRewrite::REQUEST_PATH => $origRequestPath . '/', + UrlRewrite::REQUEST_PATH => $origRequestPath . '/', + UrlRewrite::TARGET_PATH => $origRequestPath . '/', UrlRewrite::REDIRECT_TYPE => 0, - UrlRewrite::STORE_ID => 1, + UrlRewrite::STORE_ID => 1, ]; $this->connectionMock->expects($this->once()) ->method('fetchAll') ->with($this->select) - ->will($this->returnValue([$urlRewriteRowInDb])); + ->willReturn([$urlRewriteRowInDb]); $urlRewriteRedirect = [ - 'request_path' => $origRequestPath, - 'redirect_type' => 301, - 'store_id' => 1, - 'entity_type' => 'custom', - 'entity_id' => '0', - 'target_path' => $origRequestPath . '/', - 'description' => null, + 'request_path' => $origRequestPath, + 'redirect_type' => 301, + 'store_id' => 1, + 'entity_type' => 'custom', + 'entity_id' => '0', + 'target_path' => $origRequestPath . '/', + 'description' => null, 'is_autogenerated' => '0', - 'metadata' => null, + 'metadata' => null, ]; $this->dataObjectHelper->expects($this->at(0)) ->method('populateWithArray') - ->with(['urlRewrite1'], $urlRewriteRedirect, \Magento\UrlRewrite\Service\V1\Data\UrlRewrite::class) + ->with(['urlRewrite1'], $urlRewriteRedirect, UrlRewrite::class) ->will($this->returnSelf()); $this->urlRewriteFactory->expects($this->at(0)) ->method('create') - ->will($this->returnValue(['urlRewrite1'])); + ->willReturn(['urlRewrite1']); $this->assertEquals(['urlRewrite1'], $this->storage->findOneByData($data)); } @@ -266,8 +269,8 @@ public function testFindOneByDataWithRequestPathIsDifferent2() { $origRequestPath = 'page-one/'; $data = [ - 'col1' => 'val1', - 'col2' => 'val2', + 'col1' => 'val1', + 'col2' => 'val2', UrlRewrite::REQUEST_PATH => $origRequestPath, ]; @@ -283,7 +286,7 @@ public function testFindOneByDataWithRequestPathIsDifferent2() ->method('where') ->with('request_path IN (?)', [rtrim($origRequestPath, '/'), rtrim($origRequestPath, '/') . '/']); - $this->connectionMock->expects($this->any()) + $this->connectionMock ->method('quoteIdentifier') ->will($this->returnArgument(0)); @@ -291,36 +294,37 @@ public function testFindOneByDataWithRequestPathIsDifferent2() ->method('fetchRow'); $urlRewriteRowInDb = [ - UrlRewrite::REQUEST_PATH => rtrim($origRequestPath, '/'), + UrlRewrite::REQUEST_PATH => rtrim($origRequestPath, '/'), + UrlRewrite::TARGET_PATH => rtrim($origRequestPath, '/'), UrlRewrite::REDIRECT_TYPE => 0, - UrlRewrite::STORE_ID => 1, + UrlRewrite::STORE_ID => 1, ]; $this->connectionMock->expects($this->once()) ->method('fetchAll') ->with($this->select) - ->will($this->returnValue([$urlRewriteRowInDb])); + ->willReturn([$urlRewriteRowInDb]); $urlRewriteRedirect = [ - 'request_path' => $origRequestPath, - 'redirect_type' => 301, - 'store_id' => 1, - 'entity_type' => 'custom', - 'entity_id' => '0', - 'target_path' => rtrim($origRequestPath, '/'), - 'description' => null, + 'request_path' => $origRequestPath, + 'redirect_type' => 301, + 'store_id' => 1, + 'entity_type' => 'custom', + 'entity_id' => '0', + 'target_path' => rtrim($origRequestPath, '/'), + 'description' => null, 'is_autogenerated' => '0', - 'metadata' => null, + 'metadata' => null, ]; $this->dataObjectHelper->expects($this->at(0)) ->method('populateWithArray') - ->with(['urlRewrite1'], $urlRewriteRedirect, \Magento\UrlRewrite\Service\V1\Data\UrlRewrite::class) + ->with(['urlRewrite1'], $urlRewriteRedirect, UrlRewrite::class) ->will($this->returnSelf()); $this->urlRewriteFactory->expects($this->at(0)) ->method('create') - ->will($this->returnValue(['urlRewrite1'])); + ->willReturn(['urlRewrite1']); $this->assertEquals(['urlRewrite1'], $this->storage->findOneByData($data)); } @@ -329,8 +333,8 @@ public function testFindOneByDataWithRequestPathIsRedirect() { $origRequestPath = 'page-one'; $data = [ - 'col1' => 'val1', - 'col2' => 'val2', + 'col1' => 'val1', + 'col2' => 'val2', UrlRewrite::REQUEST_PATH => $origRequestPath, ]; @@ -346,33 +350,32 @@ public function testFindOneByDataWithRequestPathIsRedirect() ->method('where') ->with('request_path IN (?)', [$origRequestPath, $origRequestPath . '/']); - $this->connectionMock->expects($this->any()) - ->method('quoteIdentifier') + $this->connectionMock->method('quoteIdentifier') ->will($this->returnArgument(0)); $this->connectionMock->expects($this->never()) ->method('fetchRow'); $urlRewriteRowInDb = [ - UrlRewrite::REQUEST_PATH => $origRequestPath . '/', - UrlRewrite::TARGET_PATH => 'page-A/', + UrlRewrite::REQUEST_PATH => $origRequestPath . '/', + UrlRewrite::TARGET_PATH => 'page-A/', UrlRewrite::REDIRECT_TYPE => 301, - UrlRewrite::STORE_ID => 1, + UrlRewrite::STORE_ID => 1, ]; $this->connectionMock->expects($this->once()) ->method('fetchAll') ->with($this->select) - ->will($this->returnValue([$urlRewriteRowInDb])); + ->willReturn([$urlRewriteRowInDb]); $this->dataObjectHelper->expects($this->at(0)) ->method('populateWithArray') - ->with(['urlRewrite1'], $urlRewriteRowInDb, \Magento\UrlRewrite\Service\V1\Data\UrlRewrite::class) - ->will($this->returnSelf()); + ->with(['urlRewrite1'], $urlRewriteRowInDb, UrlRewrite::class) + ->willReturnSelf(); $this->urlRewriteFactory->expects($this->at(0)) ->method('create') - ->will($this->returnValue(['urlRewrite1'])); + ->willReturn(['urlRewrite1']); $this->assertEquals(['urlRewrite1'], $this->storage->findOneByData($data)); } @@ -398,40 +401,39 @@ public function testFindOneByDataWithRequestPathTwoResults() ->method('where') ->with('request_path IN (?)', [$origRequestPath, $origRequestPath . '/']); - $this->connectionMock->expects($this->any()) - ->method('quoteIdentifier') + $this->connectionMock->method('quoteIdentifier') ->will($this->returnArgument(0)); $this->connectionMock->expects($this->never()) ->method('fetchRow'); $urlRewriteRowInDb = [ - UrlRewrite::REQUEST_PATH => $origRequestPath . '/', - UrlRewrite::TARGET_PATH => 'page-A/', + UrlRewrite::REQUEST_PATH => $origRequestPath . '/', + UrlRewrite::TARGET_PATH => 'page-A/', UrlRewrite::REDIRECT_TYPE => 301, - UrlRewrite::STORE_ID => 1, + UrlRewrite::STORE_ID => 1, ]; $urlRewriteRowInDb2 = [ - UrlRewrite::REQUEST_PATH => $origRequestPath, - UrlRewrite::TARGET_PATH => 'page-B/', + UrlRewrite::REQUEST_PATH => $origRequestPath, + UrlRewrite::TARGET_PATH => 'page-B/', UrlRewrite::REDIRECT_TYPE => 301, - UrlRewrite::STORE_ID => 1, + UrlRewrite::STORE_ID => 1, ]; $this->connectionMock->expects($this->once()) ->method('fetchAll') ->with($this->select) - ->will($this->returnValue([$urlRewriteRowInDb, $urlRewriteRowInDb2])); + ->willReturn([$urlRewriteRowInDb, $urlRewriteRowInDb2]); $this->dataObjectHelper->expects($this->at(0)) ->method('populateWithArray') - ->with(['urlRewrite1'], $urlRewriteRowInDb2, \Magento\UrlRewrite\Service\V1\Data\UrlRewrite::class) - ->will($this->returnSelf()); + ->with(['urlRewrite1'], $urlRewriteRowInDb2, UrlRewrite::class) + ->willReturnSelf(); $this->urlRewriteFactory->expects($this->at(0)) ->method('create') - ->will($this->returnValue(['urlRewrite1'])); + ->willReturn(['urlRewrite1']); $this->assertEquals(['urlRewrite1'], $this->storage->findOneByData($data)); } @@ -441,58 +443,48 @@ public function testFindOneByDataWithRequestPathTwoResults() */ public function testReplace() { - $urlFirst = $this->createMock(\Magento\UrlRewrite\Service\V1\Data\UrlRewrite::class); - $urlSecond = $this->createMock(\Magento\UrlRewrite\Service\V1\Data\UrlRewrite::class); + $urlFirst = $this->createMock(UrlRewrite::class); + $urlSecond = $this->createMock(UrlRewrite::class); // delete - $urlFirst->expects($this->any()) - ->method('getEntityType') + $urlFirst->method('getEntityType') ->willReturn('product'); - $urlFirst->expects($this->any()) - ->method('getEntityId') + $urlFirst->method('getEntityId') ->willReturn('entity_1'); - $urlFirst->expects($this->any()) - ->method('getStoreId') + $urlFirst->method('getStoreId') ->willReturn('store_id_1'); - $urlSecond->expects($this->any()) - ->method('getEntityType') + $urlSecond->method('getEntityType') ->willReturn('category'); - $urlSecond->expects($this->any()) - ->method('getEntityId') + $urlSecond->method('getEntityId') ->willReturn('entity_2'); - $urlSecond->expects($this->any()) - ->method('getStoreId') + $urlSecond->method('getStoreId') ->willReturn('store_id_2'); - $this->connectionMock->expects($this->any()) - ->method('quoteIdentifier') - ->will($this->returnArgument(0)); + $this->connectionMock->method('quoteIdentifier') + ->willReturnArgument(0); - $this->select->expects($this->any()) - ->method($this->anything()) + $this->select->method($this->anything()) ->willReturnSelf(); - $this->resource->expects($this->any()) - ->method('getTableName') + $this->resource->method('getTableName') ->with(DbStorage::TABLE_NAME) - ->will($this->returnValue('table_name')); + ->willReturn('table_name'); // insert - $urlFirst->expects($this->any()) - ->method('toArray') - ->will($this->returnValue(['row1'])); - $urlSecond->expects($this->any()) - ->method('toArray') - ->will($this->returnValue(['row2'])); + $urlFirst->method('toArray') + ->willReturn(['row1']); + $urlSecond->method('toArray') + ->willReturn(['row2']); - $this->resource->expects($this->any()) - ->method('getTableName') + $this->resource->method('getTableName') ->with(DbStorage::TABLE_NAME) - ->will($this->returnValue('table_name')); + ->willReturn('table_name'); + + $urls = [$urlFirst, $urlSecond]; - $this->storage->replace([$urlFirst, $urlSecond]); + $this->assertEquals($urls, $this->storage->replace($urls)); } /** @@ -500,23 +492,20 @@ public function testReplace() */ public function testReplaceIfThrewExceptionOnDuplicateUrl() { - $url = $this->createMock(\Magento\UrlRewrite\Service\V1\Data\UrlRewrite::class); + $url = $this->createMock(UrlRewrite::class); - $url->expects($this->any()) - ->method('toArray') - ->will($this->returnValue(['row1'])); + $url->method('toArray') + ->willReturn(['row1']); $this->connectionMock->expects($this->once()) ->method('insertMultiple') - ->will( - $this->throwException( - new \Exception('SQLSTATE[23000]: test: 1062 test', DbStorage::ERROR_CODE_DUPLICATE_ENTRY) - ) + ->willThrowException( + new \Exception('SQLSTATE[23000]: test: 1062 test', DbStorage::ERROR_CODE_DUPLICATE_ENTRY) ); $conflictingUrl = [ UrlRewrite::URL_REWRITE_ID => 'conflicting-url' ]; - $this->connectionMock->expects($this->any()) + $this->connectionMock ->method('fetchRow') ->willReturn($conflictingUrl); @@ -533,18 +522,15 @@ public function testReplaceIfThrewExceptionOnDuplicateUrl() */ public function testReplaceIfThrewExceptionOnDuplicateEntry() { - $url = $this->createMock(\Magento\UrlRewrite\Service\V1\Data\UrlRewrite::class); + $url = $this->createMock(UrlRewrite::class); - $url->expects($this->any()) - ->method('toArray') - ->will($this->returnValue(['row1'])); + $url->method('toArray') + ->willReturn(['row1']); $this->connectionMock->expects($this->once()) ->method('insertMultiple') - ->will( - $this->throwException( - new \Exception('SQLSTATE[23000]: test: 1062 test', DbStorage::ERROR_CODE_DUPLICATE_ENTRY) - ) + ->willThrowException( + new \Exception('SQLSTATE[23000]: test: 1062 test', DbStorage::ERROR_CODE_DUPLICATE_ENTRY) ); $this->storage->replace([$url]); @@ -555,15 +541,14 @@ public function testReplaceIfThrewExceptionOnDuplicateEntry() */ public function testReplaceIfThrewCustomException() { - $url = $this->createMock(\Magento\UrlRewrite\Service\V1\Data\UrlRewrite::class); + $url = $this->createMock(UrlRewrite::class); - $url->expects($this->any()) - ->method('toArray') - ->will($this->returnValue(['row1'])); + $url->method('toArray') + ->willReturn(['row1']); $this->connectionMock->expects($this->once()) ->method('insertMultiple') - ->will($this->throwException(new \RuntimeException())); + ->willThrowException(new \RuntimeException()); $this->storage->replace([$url]); } @@ -572,8 +557,7 @@ public function testDeleteByData() { $data = ['col1' => 'val1', 'col2' => 'val2']; - $this->connectionMock->expects($this->any()) - ->method('quoteIdentifier') + $this->connectionMock->method('quoteIdentifier') ->will($this->returnArgument(0)); $this->select->expects($this->at(1)) @@ -587,12 +571,11 @@ public function testDeleteByData() $this->select->expects($this->at(3)) ->method('deleteFromSelect') ->with('table_name') - ->will($this->returnValue('sql delete query')); + ->willReturn('sql delete query'); - $this->resource->expects($this->any()) - ->method('getTableName') + $this->resource->method('getTableName') ->with(DbStorage::TABLE_NAME) - ->will($this->returnValue('table_name')); + ->willReturn('table_name'); $this->connectionMock->expects($this->once()) ->method('query') diff --git a/app/code/Magento/UrlRewrite/Ui/Component/Listing/Column/BlockActions.php b/app/code/Magento/UrlRewrite/Ui/Component/Listing/Column/BlockActions.php new file mode 100644 index 0000000000000..1e6dcde123129 --- /dev/null +++ b/app/code/Magento/UrlRewrite/Ui/Component/Listing/Column/BlockActions.php @@ -0,0 +1,91 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +declare(strict_types=1); + +namespace Magento\UrlRewrite\Ui\Component\Listing\Column; + +use Magento\Framework\UrlInterface; +use Magento\Framework\View\Element\UiComponent\ContextInterface; +use Magento\Framework\View\Element\UiComponentFactory; +use Magento\Ui\Component\Listing\Columns\Column; + +/** + * Class EditAction + */ +class BlockActions extends Column +{ + /** + * @var UrlInterface + */ + private $urlBuilder; + + /** + * Constructor + * + * @param ContextInterface $context + * @param UiComponentFactory $uiComponentFactory + * @param UrlInterface $urlBuilder + * @param array $components + * @param array $data + */ + public function __construct( + ContextInterface $context, + UiComponentFactory $uiComponentFactory, + UrlInterface $urlBuilder, + array $components = [], + array $data = [] + ) { + $this->urlBuilder = $urlBuilder; + parent::__construct($context, $uiComponentFactory, $components, $data); + } + + /** + * Prepare Data Source + * + * @param array $dataSource + * @return array + */ + public function prepareDataSource(array $dataSource): array + { + if (isset($dataSource['data']['items'])) { + foreach ($dataSource['data']['items'] as & $item) { + if (isset($item['url_rewrite_id'])) { + $editUrlPath = $this->getData('config/editUrlPath') ?: '#'; + $deleteUrlPath = $this->getData('config/deleteUrlPath') ?: '#'; + $urlEntityParamName = $this->getData('config/urlEntityParamName') ?: 'url_rewrite_id'; + $title = $item['url_rewrite_id']; + $item[$this->getData('name')] = [ + 'edit' => [ + 'href' => $this->urlBuilder->getUrl( + $editUrlPath, + [ + $urlEntityParamName => $item['url_rewrite_id'] + ] + ), + 'label' => __('Edit') + ], + 'delete' => [ + 'href' => $this->urlBuilder->getUrl( + $deleteUrlPath, + [ + $urlEntityParamName => $item['url_rewrite_id'] + ] + ), + 'label' => __('Delete'), + 'confirm' => [ + 'title' => __('Delete record #%1', $title), + 'message' => __('Are you sure you want to delete a record #%1?', $title) + ], + 'post' => true + ] + ]; + } + } + } + + return $dataSource; + } +} diff --git a/app/code/Magento/UrlRewrite/composer.json b/app/code/Magento/UrlRewrite/composer.json index 90f11accfe503..083a32f43bd49 100644 --- a/app/code/Magento/UrlRewrite/composer.json +++ b/app/code/Magento/UrlRewrite/composer.json @@ -12,7 +12,8 @@ "magento/module-catalog-url-rewrite": "*", "magento/module-cms": "*", "magento/module-cms-url-rewrite": "*", - "magento/module-store": "*" + "magento/module-store": "*", + "magento/module-ui": "*" }, "type": "magento2-module", "license": [ diff --git a/app/code/Magento/UrlRewrite/etc/di.xml b/app/code/Magento/UrlRewrite/etc/di.xml index e09c48ff89141..e56ef7b6c467d 100644 --- a/app/code/Magento/UrlRewrite/etc/di.xml +++ b/app/code/Magento/UrlRewrite/etc/di.xml @@ -26,4 +26,18 @@ </argument> </arguments> </type> + <type name="Magento\Framework\View\Element\UiComponent\DataProvider\CollectionFactory"> + <arguments> + <argument name="collections" xsi:type="array"> + <item name="url_rewrite_listing_data_source" xsi:type="string">Magento\UrlRewrite\Ui\Component\UrlRewrite\DataProvider\SearchResult</item> + </argument> + </arguments> + </type> + <virtualType name="Magento\UrlRewrite\Ui\Component\UrlRewrite\DataProvider\SearchResult" type="Magento\Framework\View\Element\UiComponent\DataProvider\SearchResult"> + <arguments> + <argument name="mainTable" xsi:type="string">url_rewrite</argument> + <argument name="resourceModel" xsi:type="string">Magento\UrlRewrite\Model\ResourceModel\UrlRewrite</argument> + <argument name="identifierName" xsi:type="string">url_rewrite_id</argument> + </arguments> + </virtualType> </config> diff --git a/app/code/Magento/UrlRewrite/etc/module.xml b/app/code/Magento/UrlRewrite/etc/module.xml index eb314b71bc377..310497892747e 100644 --- a/app/code/Magento/UrlRewrite/etc/module.xml +++ b/app/code/Magento/UrlRewrite/etc/module.xml @@ -6,6 +6,5 @@ */ --> <config xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="urn:magento:framework:Module/etc/module.xsd"> - <module name="Magento_UrlRewrite" > - </module> + <module name="Magento_UrlRewrite"/> </config> diff --git a/app/code/Magento/UrlRewrite/registration.php b/app/code/Magento/UrlRewrite/registration.php index 225a51f517569..87a28d518fab3 100644 --- a/app/code/Magento/UrlRewrite/registration.php +++ b/app/code/Magento/UrlRewrite/registration.php @@ -4,6 +4,6 @@ * See COPYING.txt for license details. */ -use \Magento\Framework\Component\ComponentRegistrar; +use Magento\Framework\Component\ComponentRegistrar; ComponentRegistrar::register(ComponentRegistrar::MODULE, 'Magento_UrlRewrite', __DIR__); diff --git a/app/code/Magento/UrlRewrite/view/adminhtml/layout/adminhtml_url_rewrite_index.xml b/app/code/Magento/UrlRewrite/view/adminhtml/layout/adminhtml_url_rewrite_index.xml index 012409a8b6aca..e495f714ee78c 100644 --- a/app/code/Magento/UrlRewrite/view/adminhtml/layout/adminhtml_url_rewrite_index.xml +++ b/app/code/Magento/UrlRewrite/view/adminhtml/layout/adminhtml_url_rewrite_index.xml @@ -8,90 +8,7 @@ <page xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="urn:magento:framework:View/Layout/etc/page_configuration.xsd"> <body> <referenceContainer name="content"> - <block class="Magento\UrlRewrite\Block\GridContainer" name="adminhtml.block.url_rewrite.grid.container"> - <block class="Magento\Backend\Block\Widget\Grid" name="adminhtml.block.url_rewrite.grid" as="grid"> - <arguments> - <argument name="id" xsi:type="string">urlrewriteGrid</argument> - <argument name="dataSource" xsi:type="object" shared="false">Magento\UrlRewrite\Model\ResourceModel\UrlRewriteCollection</argument> - <argument name="default_sort" xsi:type="string">url_rewrite_id</argument> - <!-- Add below argument to save session parameter in URL rewrite grid --> - <argument name="save_parameters_in_session" xsi:type="string">1</argument> - </arguments> - <block class="Magento\Backend\Block\Widget\Grid\ColumnSet" as="grid.columnSet" name="adminhtml.url_rewrite.grid.columnSet"> - <arguments> - <argument name="rowUrl" xsi:type="array"> - <item name="path" xsi:type="string">adminhtml/*/edit</item> - <item name="extraParamsTemplate" xsi:type="array"> - <item name="id" xsi:type="string">getId</item> - </item> - </argument> - </arguments> - <block class="Magento\Backend\Block\Widget\Grid\Column" name="adminhtml.url_rewrite.grid.columnSet.url_rewrite_id" as="url_rewrite_id"> - <arguments> - <argument name="header" xsi:type="string" translate="true">ID</argument> - <argument name="type" xsi:type="string">text</argument> - <argument name="id" xsi:type="string">url_rewrite_id</argument> - <argument name="index" xsi:type="string">url_rewrite_id</argument> - <argument name="column_css_class" xsi:type="string">col-id</argument> - <argument name="header_css_class" xsi:type="string">col-id</argument> - </arguments> - </block> - <block class="Magento\Backend\Block\Widget\Grid\Column\Multistore" name="adminhtml.url_rewrite.grid.columnSet.store_id" as="store_id"> - <arguments> - <argument name="header" xsi:type="string" translate="true">Store View</argument> - <argument name="type" xsi:type="string">store</argument> - <argument name="id" xsi:type="string">store_id</argument> - <argument name="index" xsi:type="string">store_id</argument> - <argument name="store_view" xsi:type="string">true</argument> - </arguments> - </block> - <block class="Magento\Backend\Block\Widget\Grid\Column" name="adminhtml.url_rewrite.grid.columnSet.request_path" as="request_path"> - <arguments> - <argument name="header" xsi:type="string" translate="true">Request Path</argument> - <argument name="type" xsi:type="string">text</argument> - <argument name="id" xsi:type="string">request_path</argument> - <argument name="index" xsi:type="string">request_path</argument> - </arguments> - </block> - <block class="Magento\Backend\Block\Widget\Grid\Column" name="adminhtml.url_rewrite.grid.columnSet.target_path" as="target_path"> - <arguments> - <argument name="header" xsi:type="string" translate="true">Target Path</argument> - <argument name="type" xsi:type="string">text</argument> - <argument name="id" xsi:type="string">target_path</argument> - <argument name="index" xsi:type="string">target_path</argument> - </arguments> - </block> - <block class="Magento\Backend\Block\Widget\Grid\Column" name="adminhtml.url_rewrite.grid.columnSet.redirect_type" as="redirect_type"> - <arguments> - <argument name="header" xsi:type="string" translate="true">Redirect Type</argument> - <argument name="type" xsi:type="string">options</argument> - <argument name="id" xsi:type="string">redirect_type</argument> - <argument name="index" xsi:type="string">redirect_type</argument> - <argument name="options" xsi:type="options" model="Magento\UrlRewrite\Model\OptionProvider"/> - </arguments> - </block> - <block class="Magento\Backend\Block\Widget\Grid\Column" name="adminhtml.url_rewrite.grid.columnSet.actions" as="actions"> - <arguments> - <argument name="header" xsi:type="string" translate="true">Action</argument> - <argument name="sortable" xsi:type="string">0</argument> - <argument name="filter" xsi:type="string">0</argument> - <argument name="type" xsi:type="string">action</argument> - <argument name="id" xsi:type="string">actions</argument> - <argument name="index" xsi:type="string">url_rewrite_id</argument> - <argument name="actions" xsi:type="array"> - <item name="view_action" xsi:type="array"> - <item name="caption" xsi:type="string" translate="true">Edit</item> - <item name="url" xsi:type="array"> - <item name="base" xsi:type="string">adminhtml/*/edit</item> - </item> - <item name="field" xsi:type="string">id</item> - </item> - </argument> - </arguments> - </block> - </block> - </block> - </block> + <uiComponent name="url_rewrite_listing"/> </referenceContainer> </body> </page> diff --git a/app/code/Magento/UrlRewrite/view/adminhtml/ui_component/url_rewrite_listing.xml b/app/code/Magento/UrlRewrite/view/adminhtml/ui_component/url_rewrite_listing.xml new file mode 100644 index 0000000000000..39400f1092cbb --- /dev/null +++ b/app/code/Magento/UrlRewrite/view/adminhtml/ui_component/url_rewrite_listing.xml @@ -0,0 +1,158 @@ +<?xml version="1.0"?> +<!-- +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +--> +<listing xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:noNamespaceSchemaLocation="urn:magento:module:Magento_Ui:etc/ui_configuration.xsd"> + <argument name="data" xsi:type="array"> + <item name="js_config" xsi:type="array"> + <item name="provider" xsi:type="string">url_rewrite_listing.url_rewrite_listing_data_source</item> + </item> + </argument> + <settings> + <buttons> + <button name="add"> + <url path="*/*/edit"/> + <class>primary</class> + <label translate="true">Add URL Rewrite</label> + </button> + </buttons> + <spinner>url_rewrite_listing_columns</spinner> + <deps> + <dep>url_rewrite_listing.url_rewrite_listing_data_source</dep> + </deps> + </settings> + <dataSource name="url_rewrite_listing_data_source" component="Magento_Ui/js/grid/provider"> + <settings> + <storageConfig> + <param name="indexField" xsi:type="string">url_rewrite_id</param> + </storageConfig> + <updateUrl path="mui/index/render"/> + </settings> + <aclResource>Magento_UrlRewrite::urlrewrite</aclResource> + <dataProvider class="Magento\Framework\View\Element\UiComponent\DataProvider\DataProvider" + name="url_rewrite_listing_data_source"> + <settings> + <requestFieldName>id</requestFieldName> + <primaryFieldName>url_rewrite_id</primaryFieldName> + </settings> + </dataProvider> + </dataSource> + <listingToolbar name="listing_top"> + <settings> + <sticky>true</sticky> + </settings> + <bookmark name="bookmarks"/> + <columnsControls name="columns_controls"/> + <filters name="listing_filters"> + <filterSelect name="store_id" provider="${ $.parentName }"> + <settings> + <options class="Magento\Store\Ui\Component\Listing\Column\Store\Options"/> + <caption translate="true">All Store Views</caption> + <label translate="true">Store View</label> + <dataScope>store_id</dataScope> + </settings> + </filterSelect> + </filters> + <massaction name="listing_massaction"> + <action name="delete"> + <settings> + <confirm> + <message translate="true">Are you sure you want to delete selected items?</message> + <title translate="true">Delete items + + + delete + + + + + + + + + + + + false + + url_rewrite_id + true + url_rewrite_listing.url_rewrite_listing.url_rewrite_listing_columns.ids + + + + url_rewrite_listing.url_rewrite_listing.url_rewrite_listing_columns_editor + startEdit + + ${ $.$data.rowIndex } + true + + + + + + + url_rewrite_id + + + + + textRange + + desc + + + + + + ui/grid/cells/html + false + + + + + text + + + + true + + text + + + + + + text + + + + + + + select + + select + + select + + + + + + + id + adminhtml/url_rewrite/edit + adminhtml/url_rewrite/delete + + + + url_rewrite_id + + + + diff --git a/app/code/Magento/User/Block/Role/Tab/Edit.php b/app/code/Magento/User/Block/Role/Tab/Edit.php index 5fe6a1b2a2e88..5ba3facfc2d45 100644 --- a/app/code/Magento/User/Block/Role/Tab/Edit.php +++ b/app/code/Magento/User/Block/Role/Tab/Edit.php @@ -63,10 +63,12 @@ class Edit extends \Magento\Backend\Block\Widget\Form implements \Magento\Backen protected $coreRegistry = null; /** + * Constructor + * * @param \Magento\Backend\Block\Template\Context $context + * @param \Magento\Authorization\Model\Acl\AclRetriever $aclRetriever * @param \Magento\Framework\Acl\RootResource $rootResource * @param \Magento\Authorization\Model\ResourceModel\Rules\CollectionFactory $rulesCollectionFactory - * @param \Magento\Authorization\Model\Acl\AclRetriever $aclRetriever * @param \Magento\Framework\Acl\AclResource\ProviderInterface $aclResourceProvider * @param \Magento\Integration\Helper\Data $integrationData * @param array $data @@ -222,6 +224,6 @@ function ($node) { } ); $configResource = reset($configResource); - return isset($configResource['children']) ? $configResource['children'] : []; + return $configResource['children'] ?? []; } } diff --git a/app/code/Magento/User/Model/ResourceModel/User.php b/app/code/Magento/User/Model/ResourceModel/User.php index 4eaf6116056dd..d9bc555b8e391 100644 --- a/app/code/Magento/User/Model/ResourceModel/User.php +++ b/app/code/Magento/User/Model/ResourceModel/User.php @@ -13,6 +13,7 @@ use Magento\Authorization\Model\UserContextInterface; use Magento\Framework\Acl\Data\CacheInterface; use Magento\Framework\App\ObjectManager; +use Magento\Framework\Exception\LocalizedException; use Magento\User\Model\Backend\Config\ObserverConfig; use Magento\User\Model\User as ModelUser; @@ -249,14 +250,17 @@ protected function _afterLoad(\Magento\Framework\Model\AbstractModel $user) * * @param \Magento\Framework\Model\AbstractModel $user * @return bool - * @throws \Magento\Framework\Exception\LocalizedException + * @throws LocalizedException */ public function delete(\Magento\Framework\Model\AbstractModel $user) { + $uid = $user->getId(); + if (!$uid) { + return false; + } + $this->_beforeDelete($user); $connection = $this->getConnection(); - - $uid = $user->getId(); $connection->beginTransaction(); try { $connection->delete($this->getMainTable(), ['user_id = ?' => $uid]); @@ -264,12 +268,15 @@ public function delete(\Magento\Framework\Model\AbstractModel $user) $this->getTable('authorization_role'), ['user_id = ?' => $uid, 'user_type = ?' => UserContextInterface::USER_TYPE_ADMIN] ); - } catch (\Magento\Framework\Exception\LocalizedException $e) { + } catch (LocalizedException $e) { $connection->rollBack(); + return false; } + $connection->commit(); $this->_afterDelete($user); + return true; } diff --git a/app/code/Magento/User/Test/Mftf/ActionGroup/AdminCreateUserWithRoleAndIsInactiveActionGroup.xml b/app/code/Magento/User/Test/Mftf/ActionGroup/AdminCreateUserWithRoleAndIsInactiveActionGroup.xml new file mode 100644 index 0000000000000..3c7db628e1378 --- /dev/null +++ b/app/code/Magento/User/Test/Mftf/ActionGroup/AdminCreateUserWithRoleAndIsInactiveActionGroup.xml @@ -0,0 +1,14 @@ + + + + + + + + diff --git a/app/code/Magento/User/Test/Mftf/ActionGroup/AdminDeleteUserActionGroup.xml b/app/code/Magento/User/Test/Mftf/ActionGroup/AdminDeleteUserActionGroup.xml index 67075eb52a678..82a3a37cdd724 100644 --- a/app/code/Magento/User/Test/Mftf/ActionGroup/AdminDeleteUserActionGroup.xml +++ b/app/code/Magento/User/Test/Mftf/ActionGroup/AdminDeleteUserActionGroup.xml @@ -18,7 +18,7 @@ - + diff --git a/app/code/Magento/User/Test/Mftf/ActionGroup/AdminFillNewUserFormRequiredFieldsActionGroup.xml b/app/code/Magento/User/Test/Mftf/ActionGroup/AdminFillNewUserFormRequiredFieldsActionGroup.xml index eb3ef37056b2f..15cb4e5319904 100644 --- a/app/code/Magento/User/Test/Mftf/ActionGroup/AdminFillNewUserFormRequiredFieldsActionGroup.xml +++ b/app/code/Magento/User/Test/Mftf/ActionGroup/AdminFillNewUserFormRequiredFieldsActionGroup.xml @@ -13,7 +13,7 @@ Fills in the provided User details on the New User creation page. - + diff --git a/app/code/Magento/User/Test/Mftf/ActionGroup/AdminFillUserRoleFormActionGroup.xml b/app/code/Magento/User/Test/Mftf/ActionGroup/AdminFillUserRoleFormActionGroup.xml index 7b913382651ae..2facfbac04820 100644 --- a/app/code/Magento/User/Test/Mftf/ActionGroup/AdminFillUserRoleFormActionGroup.xml +++ b/app/code/Magento/User/Test/Mftf/ActionGroup/AdminFillUserRoleFormActionGroup.xml @@ -24,6 +24,7 @@ - + + diff --git a/app/code/Magento/User/Test/Mftf/ActionGroup/AdminUserClickRoleResourceTabActionGroup.xml b/app/code/Magento/User/Test/Mftf/ActionGroup/AdminUserClickRoleResourceTabActionGroup.xml new file mode 100644 index 0000000000000..3e20eaf973674 --- /dev/null +++ b/app/code/Magento/User/Test/Mftf/ActionGroup/AdminUserClickRoleResourceTabActionGroup.xml @@ -0,0 +1,16 @@ + + + + + + Switch to role resource tab. + + + + diff --git a/app/code/Magento/User/Test/Mftf/ActionGroup/AdminUserOpenAdminRolesPageActionGroup.xml b/app/code/Magento/User/Test/Mftf/ActionGroup/AdminUserOpenAdminRolesPageActionGroup.xml new file mode 100644 index 0000000000000..71be9117e5caf --- /dev/null +++ b/app/code/Magento/User/Test/Mftf/ActionGroup/AdminUserOpenAdminRolesPageActionGroup.xml @@ -0,0 +1,17 @@ + + + + + + Navigate to User Role Grid + + + + + diff --git a/app/code/Magento/User/Test/Mftf/ActionGroup/AdminUserSaveRoleActionGroup.xml b/app/code/Magento/User/Test/Mftf/ActionGroup/AdminUserSaveRoleActionGroup.xml new file mode 100644 index 0000000000000..824e9407125f5 --- /dev/null +++ b/app/code/Magento/User/Test/Mftf/ActionGroup/AdminUserSaveRoleActionGroup.xml @@ -0,0 +1,17 @@ + + + + + + Click to Save Role + + + + + diff --git a/app/code/Magento/User/Test/Mftf/ActionGroup/AssertAdminImpossibleDeleteYourOwnAccountActionGroup.xml b/app/code/Magento/User/Test/Mftf/ActionGroup/AssertAdminImpossibleDeleteYourOwnAccountActionGroup.xml new file mode 100644 index 0000000000000..54fe82831ef02 --- /dev/null +++ b/app/code/Magento/User/Test/Mftf/ActionGroup/AssertAdminImpossibleDeleteYourOwnAccountActionGroup.xml @@ -0,0 +1,21 @@ + + + + + + + + + + + + + + + diff --git a/app/code/Magento/User/Test/Mftf/ActionGroup/AssertAdminRoleInGridActionGroup.xml b/app/code/Magento/User/Test/Mftf/ActionGroup/AssertAdminRoleInGridActionGroup.xml new file mode 100644 index 0000000000000..93ff97e8bbf8c --- /dev/null +++ b/app/code/Magento/User/Test/Mftf/ActionGroup/AssertAdminRoleInGridActionGroup.xml @@ -0,0 +1,20 @@ + + + + + + + + + + + + + + diff --git a/app/code/Magento/User/Test/Mftf/ActionGroup/AssertAdminRoleNotInGridActionGroup.xml b/app/code/Magento/User/Test/Mftf/ActionGroup/AssertAdminRoleNotInGridActionGroup.xml new file mode 100644 index 0000000000000..bb6af04e0e7b6 --- /dev/null +++ b/app/code/Magento/User/Test/Mftf/ActionGroup/AssertAdminRoleNotInGridActionGroup.xml @@ -0,0 +1,20 @@ + + + + + + + + + + + + + + diff --git a/app/code/Magento/User/Test/Mftf/ActionGroup/AssertAdminUserInGridActionGroup.xml b/app/code/Magento/User/Test/Mftf/ActionGroup/AssertAdminUserInGridActionGroup.xml new file mode 100644 index 0000000000000..b0ade7b3a8ce8 --- /dev/null +++ b/app/code/Magento/User/Test/Mftf/ActionGroup/AssertAdminUserInGridActionGroup.xml @@ -0,0 +1,22 @@ + + + + + + + + + + + + + + + + diff --git a/app/code/Magento/User/Test/Mftf/ActionGroup/AssertAdminUserNotInGridActionGroup.xml b/app/code/Magento/User/Test/Mftf/ActionGroup/AssertAdminUserNotInGridActionGroup.xml new file mode 100644 index 0000000000000..ed5c82aba84db --- /dev/null +++ b/app/code/Magento/User/Test/Mftf/ActionGroup/AssertAdminUserNotInGridActionGroup.xml @@ -0,0 +1,22 @@ + + + + + + + + + + + + + + + + diff --git a/app/code/Magento/User/Test/Mftf/Data/UserData.xml b/app/code/Magento/User/Test/Mftf/Data/UserData.xml index 7947c8ee3c161..cf15c77810791 100644 --- a/app/code/Magento/User/Test/Mftf/Data/UserData.xml +++ b/app/code/Magento/User/Test/Mftf/Data/UserData.xml @@ -145,6 +145,38 @@ admin_user_with_correct_password 123123123q + + AdminUser + FirstName + LastName + admin@example.com + 123123q + 123123q + en_US + English (United States) + {{_ENV.MAGENTO_ADMIN_PASSWORD}} + Administrators + 1 + + 1 + + + + AdminUser + FirstName + LastName + admin@example.com + 123123q + 123123q + en_US + English (United States) + {{_ENV.MAGENTO_ADMIN_PASSWORD}} + Administrators + 0 + + 1 + + - -
- - - - - - -
- -
- - - - - -
-
diff --git a/app/code/Magento/User/Test/Mftf/Section/AdminRoleGridSection/AdminDeleteRoleSection.xml b/app/code/Magento/User/Test/Mftf/Section/AdminRoleGridSection/AdminDeleteRoleSection.xml new file mode 100644 index 0000000000000..e369d037d28f6 --- /dev/null +++ b/app/code/Magento/User/Test/Mftf/Section/AdminRoleGridSection/AdminDeleteRoleSection.xml @@ -0,0 +1,17 @@ + + + +
+ + + + + +
+
diff --git a/app/code/Magento/User/Test/Mftf/Section/AdminRoleGridSection/AdminRoleGridSection.xml b/app/code/Magento/User/Test/Mftf/Section/AdminRoleGridSection/AdminRoleGridSection.xml new file mode 100644 index 0000000000000..f1826d360fb88 --- /dev/null +++ b/app/code/Magento/User/Test/Mftf/Section/AdminRoleGridSection/AdminRoleGridSection.xml @@ -0,0 +1,19 @@ + + + +
+ + + + + + + +
+
diff --git a/app/code/Magento/User/Test/Mftf/Section/AdminUserGridSection.xml b/app/code/Magento/User/Test/Mftf/Section/AdminUserGridSection.xml deleted file mode 100644 index caff47114687e..0000000000000 --- a/app/code/Magento/User/Test/Mftf/Section/AdminUserGridSection.xml +++ /dev/null @@ -1,25 +0,0 @@ - - - -
- - - - - - -
- -
- - - - - -
-
diff --git a/app/code/Magento/User/Test/Mftf/Section/AdminUserGridSection/AdminDeleteUserSection.xml b/app/code/Magento/User/Test/Mftf/Section/AdminUserGridSection/AdminDeleteUserSection.xml new file mode 100644 index 0000000000000..d4718ca43d6cf --- /dev/null +++ b/app/code/Magento/User/Test/Mftf/Section/AdminUserGridSection/AdminDeleteUserSection.xml @@ -0,0 +1,17 @@ + + + +
+ + + + + +
+
diff --git a/app/code/Magento/User/Test/Mftf/Section/AdminUserGridSection/AdminUserGridSection.xml b/app/code/Magento/User/Test/Mftf/Section/AdminUserGridSection/AdminUserGridSection.xml new file mode 100644 index 0000000000000..8e6b5f612a7b1 --- /dev/null +++ b/app/code/Magento/User/Test/Mftf/Section/AdminUserGridSection/AdminUserGridSection.xml @@ -0,0 +1,19 @@ + + + +
+ + + + + + + +
+
diff --git a/app/code/Magento/User/Test/Mftf/Test/AdminBulkOperationsLogIsNotAccessibleForAdminUserWithLimitedAccessTest.xml b/app/code/Magento/User/Test/Mftf/Test/AdminBulkOperationsLogIsNotAccessibleForAdminUserWithLimitedAccessTest.xml index 4aa9e7b7fb4cd..e192bea3a25e0 100644 --- a/app/code/Magento/User/Test/Mftf/Test/AdminBulkOperationsLogIsNotAccessibleForAdminUserWithLimitedAccessTest.xml +++ b/app/code/Magento/User/Test/Mftf/Test/AdminBulkOperationsLogIsNotAccessibleForAdminUserWithLimitedAccessTest.xml @@ -20,11 +20,11 @@ - + - + @@ -35,7 +35,7 @@ - + @@ -44,11 +44,12 @@ - + - - + + + @@ -63,9 +64,9 @@ - + - + diff --git a/app/code/Magento/User/Test/Mftf/Test/AdminCreateActiveUserEntityTest.xml b/app/code/Magento/User/Test/Mftf/Test/AdminCreateActiveUserEntityTest.xml index 753ab02f84053..668ae550f1b3d 100644 --- a/app/code/Magento/User/Test/Mftf/Test/AdminCreateActiveUserEntityTest.xml +++ b/app/code/Magento/User/Test/Mftf/Test/AdminCreateActiveUserEntityTest.xml @@ -14,36 +14,34 @@ <description value="Admin user should be able to create active admin user"/> - <testCaseId value=""/> + <testCaseId value="MC-33044"/> <severity value="CRITICAL"/> <group value="user"/> <group value="mtf_migrated"/> </annotations> - <before> - <actionGroup ref="LoginAsAdmin" stepKey="LoginAsAdmin"/> - </before> <after> - <actionGroup ref="LoginAsAdmin" stepKey="LoginAsAdmin"/> + <actionGroup ref="AdminLoginActionGroup" stepKey="adminLogin"/> <actionGroup ref="AdminDeleteUserActionGroup" stepKey="deleteUser"> <argument name="user" value="activeAdmin"/> </actionGroup> + <actionGroup ref="AdminLogoutActionGroup" stepKey="adminLogout"/> </after> - <actionGroup ref="AdminCreateUserWithRoleAndIsActiveActionGroup" stepKey="createAdminUser"> - <argument name="role" value="genericAdminRole"/> + <actionGroup ref="AdminLoginActionGroup" stepKey="adminLogin"/> + <actionGroup ref="AdminCreateUserWithRoleActionGroup" stepKey="createAdminUser"> <argument name="user" value="activeAdmin"/> + <argument name="role" value="roleDefaultAdministrator"/> + </actionGroup> + <actionGroup ref="AdminLogoutActionGroup" stepKey="logoutMasterAdmin"/> + + <actionGroup ref="AdminLoginActionGroup" stepKey="loginToNewAdmin"> + <argument name="username" value="{{activeAdmin.username}}"/> + <argument name="password" value="{{activeAdmin.password}}"/> </actionGroup> - <actionGroup ref="logout" stepKey="logoutMasterAdmin"/> - <amOnPage url="{{AdminLoginPage.url}}" stepKey="navigateToAdmin"/> - <fillField selector="{{AdminLoginFormSection.username}}" userInput="{{activeAdmin.username}}" stepKey="fillUsername"/> - <fillField selector="{{AdminLoginFormSection.password}}" userInput="{{activeAdmin.password}}" stepKey="fillPassword"/> - <click selector="{{AdminLoginFormSection.signIn}}" stepKey="clickLogin"/> - <closeAdminNotification stepKey="closeAdminNotification"/> <amOnPage url="{{AdminUsersPage.url}}" stepKey="navigateToAdminUsersGrid"/> - <fillField selector="{{AdminUserGridSection.usernameFilterTextField}}" userInput="{{activeAdmin.username}}" stepKey="fillUsernameSearch"/> - <click selector="{{AdminUserGridSection.searchButton}}" stepKey="clickSearchButton"/> - <waitForPageLoad time="10" stepKey="wait1"/> - <see selector="{{AdminUserGridSection.usernameInFirstRow}}" userInput="{{activeAdmin.username}}" stepKey="seeFoundUsername"/> - <actionGroup ref="logout" stepKey="logoutCreatedUser"/> + <actionGroup ref="AssertAdminUserIsInGridActionGroup" stepKey="assertAdminIsInGrid"> + <argument name="user" value="activeAdmin"/> + </actionGroup> + <actionGroup ref="AdminLogoutActionGroup" stepKey="logoutCreatedUser"/> </test> </tests> diff --git a/app/code/Magento/User/Test/Mftf/Test/AdminCreateInactiveUserEntityTest.xml b/app/code/Magento/User/Test/Mftf/Test/AdminCreateInactiveUserEntityTest.xml index d0fdd72ebbdcc..23a30246bd999 100644 --- a/app/code/Magento/User/Test/Mftf/Test/AdminCreateInactiveUserEntityTest.xml +++ b/app/code/Magento/User/Test/Mftf/Test/AdminCreateInactiveUserEntityTest.xml @@ -14,35 +14,28 @@ <stories value="Create Admin User"/> <title value="Admin user should be able to create inactive admin user"/> <description value="Admin user should be able to create inactive admin user"/> - <testCaseId value=""/> + <testCaseId value="MC-33045"/> <severity value="CRITICAL"/> <group value="user"/> <group value="mtf_migrated"/> </annotations> - <before> - <actionGroup ref="LoginAsAdmin" stepKey="LoginAsAdmin"/> - </before> - <after> - <actionGroup ref="LoginAsAdmin" stepKey="LoginAsAdmin"/> - <actionGroup ref="AdminDeleteUserActionGroup" stepKey="deleteUser"> - <argument name="user" value="inactiveAdmin"/> - </actionGroup> - </after> + <actionGroup ref="AdminLoginActionGroup" stepKey="adminMasterLogin"/> <actionGroup ref="AdminCreateUserWithRoleAndIsActiveActionGroup" stepKey="createAdminUser"> - <argument name="role" value="genericAdminRole"/> <argument name="user" value="inactiveAdmin"/> + <argument name="role" value="roleDefaultAdministrator"/> </actionGroup> <amOnPage url="{{AdminUsersPage.url}}" stepKey="navigateToAdminUsersGrid"/> - <fillField selector="{{AdminUserGridSection.usernameFilterTextField}}" userInput="{{inactiveAdmin.username}}" stepKey="fillUsernameSearch"/> - <click selector="{{AdminUserGridSection.searchButton}}" stepKey="clickSearchButton"/> - <waitForPageLoad time="10" stepKey="wait1"/> - <see selector="{{AdminUserGridSection.usernameInFirstRow}}" userInput="{{inactiveAdmin.username}}" stepKey="seeFoundUsername"/> - <actionGroup ref="logout" stepKey="logoutMasterAdmin"/> - <amOnPage url="{{AdminLoginPage.url}}" stepKey="navigateToAdmin"/> - <fillField selector="{{AdminLoginFormSection.username}}" userInput="{{inactiveAdmin.username}}" stepKey="fillUsername"/> - <fillField selector="{{AdminLoginFormSection.password}}" userInput="{{inactiveAdmin.password}}" stepKey="fillPassword"/> - <click selector="{{AdminLoginFormSection.signIn}}" stepKey="clickLogin"/> + <actionGroup ref="AssertAdminUserIsInGridActionGroup" stepKey="assertAdminIsInGrid"> + <argument name="user" value="inactiveAdmin"/> + </actionGroup> + <actionGroup ref="AdminLogoutActionGroup" stepKey="adminMasterLogout"/> + + <actionGroup ref="AdminLoginActionGroup" stepKey="adminNewLogin"> + <argument name="username" value="{{inactiveAdmin.username}}"/> + <argument name="password" value="{{inactiveAdmin.password}}"/> + </actionGroup> <actionGroup ref="AssertMessageOnAdminLoginActionGroup" stepKey="seeUserErrorMessage" /> + <actionGroup ref="AdminLogoutActionGroup" stepKey="adminNewLogout"/> </test> </tests> diff --git a/app/code/Magento/User/Test/Mftf/Test/AdminCreateUserRoleEntityTest.xml b/app/code/Magento/User/Test/Mftf/Test/AdminCreateUserRoleEntityTest.xml new file mode 100644 index 0000000000000..d373bc6676deb --- /dev/null +++ b/app/code/Magento/User/Test/Mftf/Test/AdminCreateUserRoleEntityTest.xml @@ -0,0 +1,79 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + /** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +--> + +<tests xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/testSchema.xsd"> + <test name="AdminCreateUserRoleEntityTest"> + <annotations> + <features value="User"/> + <stories value="Create User Role"/> + <title value="Creating a new role with different data sets"/> + <description value="Creating a new role with different data sets"/> + <testCaseId value="MC-14258"/> + <group value="user"/> + <group value="mtf_migrated"/> + </annotations> + + <before> + <actionGroup ref="AdminLoginActionGroup" stepKey="logIn"/> + </before> + <after> + <actionGroup ref="AdminLogoutActionGroup" stepKey="logOut"/> + </after> + + <!--Create a new role with custom access--> + <actionGroup ref="AdminOpenCreateRolePageActionGroup" stepKey="goToNewRolePage"/> + <actionGroup ref="AdminFillUserRoleFormActionGroup" stepKey="fillNewRoleForm"> + <argument name="role" value="roleSales"/> + </actionGroup> + <actionGroup ref="AdminClickSaveButtonOnUserRoleFormActionGroup" stepKey="saveNewRole"/> + <actionGroup ref="AssertMessageInAdminPanelActionGroup" stepKey="assertSuccessMessage"> + <argument name="message" value="You saved the role."/> + </actionGroup> + <actionGroup ref="AssertAdminRoleInGridActionGroup" stepKey="assertRoleInGrid"> + <argument name="roleName" value="{{roleSales.name}}"/> + </actionGroup> + <!--Delete the new role with custom access--> + <actionGroup ref="AdminDeleteUserRoleActionGroup" stepKey="deleteSaleRole"> + <argument name="roleName" value="{{roleSales.name}}"/> + </actionGroup> + + <!--Create a new role with full access--> + <actionGroup ref="AdminOpenCreateRolePageActionGroup" stepKey="goToNewRolePageSecondTime"/> + <actionGroup ref="AdminFillUserRoleFormActionGroup" stepKey="fillNewRoleFormSecondTime"> + <argument name="role" value="roleAdministrator"/> + </actionGroup> + <actionGroup ref="AdminClickSaveButtonOnUserRoleFormActionGroup" stepKey="saveNewRoleSecondTime"/> + <actionGroup ref="AssertMessageInAdminPanelActionGroup" stepKey="assertSuccessMessageSecondTime"> + <argument name="message" value="You saved the role."/> + </actionGroup> + <actionGroup ref="AssertAdminRoleInGridActionGroup" stepKey="assertRoleInGridSecondTime"> + <argument name="roleName" value="{{roleAdministrator.name}}"/> + </actionGroup> + <!--Delete the new role with full access--> + <actionGroup ref="AdminDeleteUserRoleActionGroup" stepKey="deleteAdministratorRole"> + <argument name="roleName" value="{{roleAdministrator.name}}"/> + </actionGroup> + + <!--Create a new role using incorrect current_password--> + <actionGroup ref="AdminOpenCreateRolePageActionGroup" stepKey="goToNewRolePageThirdTime"/> + <actionGroup ref="AdminFillUserRoleFormActionGroup" stepKey="fillNewRoleFormThirdTime"> + <argument name="role" value="roleAdministrator"/> + <argument name="currentAdminPassword" value="{{_ENV.MAGENTO_ADMIN_PASSWORD}}INVALID"/> + </actionGroup> + <actionGroup ref="AdminClickSaveButtonOnUserRoleFormActionGroup" stepKey="saveNewRoleThirdTime"/> + <actionGroup ref="AssertMessageInAdminPanelActionGroup" stepKey="assertErrorMessage"> + <argument name="messageType" value="error"/> + <argument name="message" value="The password entered for the current user is invalid. Verify the password and try again."/> + </actionGroup> + <actionGroup ref="AssertAdminRoleNotInGridActionGroup" stepKey="assertRoleNotInGrid"> + <argument name="roleName" value="{{roleAdministrator.name}}"/> + </actionGroup> + + </test> +</tests> diff --git a/app/code/Magento/User/Test/Mftf/Test/AdminDeleteAdminUserEntityTest.xml b/app/code/Magento/User/Test/Mftf/Test/AdminDeleteAdminUserEntityTest.xml new file mode 100644 index 0000000000000..60f6089e56a41 --- /dev/null +++ b/app/code/Magento/User/Test/Mftf/Test/AdminDeleteAdminUserEntityTest.xml @@ -0,0 +1,46 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + /** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +--> + +<tests xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/testSchema.xsd"> + <test name="AdminDeleteAdminUserEntityTest"> + <annotations> + <features value="User"/> + <stories value="Delete Admin User"/> + <title value="Admin user is able to delete a user account"/> + <description value="Admin user is able to delete a user account"/> + <testCaseId value="MC-14270"/> + <group value="user"/> + <group value="mtf_migrated"/> + </annotations> + + <before> + <!--Create New Admin User--> + <createData entity="NewAdminUser" stepKey="user"> + <field key="current_password">{{DefaultAdminUser.password}}</field> + </createData> + + <actionGroup ref="AdminLoginActionGroup" stepKey="logIn"/> + </before> + + <after> + <actionGroup ref="AdminLogoutActionGroup" stepKey="logOut"/> + </after> + + <!--Delete New Admin User--> + <actionGroup ref="AdminDeleteCustomUserActionGroup" stepKey="deleteNewUser"> + <argument name="user" value="$$user$$"/> + </actionGroup> + <actionGroup ref="AssertMessageInAdminPanelActionGroup" stepKey="assertSuccessMessage"> + <argument name="message" value="You deleted the user."/> + </actionGroup> + <actionGroup ref="AssertAdminUserNotInGridActionGroup" stepKey="assertUserNotInGrid"> + <argument name="userName" value="$$user.username$$"/> + </actionGroup> + </test> +</tests> diff --git a/app/code/Magento/User/Test/Mftf/Test/AdminDeleteOwnAdminUserAccountTest.xml b/app/code/Magento/User/Test/Mftf/Test/AdminDeleteOwnAdminUserAccountTest.xml new file mode 100644 index 0000000000000..1019d58edcaf2 --- /dev/null +++ b/app/code/Magento/User/Test/Mftf/Test/AdminDeleteOwnAdminUserAccountTest.xml @@ -0,0 +1,54 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + /** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +--> + +<tests xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/testSchema.xsd"> + <test name="AdminDeleteOwnAdminUserAccountTest"> + <annotations> + <features value="User"/> + <stories value="Delete Admin User"/> + <title value="Admin user is not able to delete the own account"/> + <description value="Admin user is not able to delete the own account"/> + <testCaseId value="MC-14271"/> + <group value="user"/> + <group value="mtf_migrated"/> + </annotations> + + <before> + <!--Create New Admin User--> + <executeJS function="return '{{DefaultAdminUser.password}}'" stepKey="adminPassword" /> + <createData entity="NewAdminUser" stepKey="user"> + <field key="current_password">{$adminPassword}</field> + </createData> + <actionGroup ref="AdminLoginActionGroup" stepKey="loginAsNewUser"> + <argument name="username" value="$$user.username$$"/> + <argument name="password" value="$$user.password$$"/> + </actionGroup> + </before> + <after> + <!-- Delete New Admin User --> + <actionGroup ref="AdminLoginActionGroup" stepKey="LoginAdmin"/> + <actionGroup ref="AdminDeleteUserViaCurlActionGroup" stepKey="deleteUser"> + <argument name="user" value="$$user$$" /> + </actionGroup> + <actionGroup ref="AdminLogoutActionGroup" stepKey="logOut"/> + </after> + + <!--Assert Impossible Delete Your Own Account--> + <actionGroup ref="AdminOpenUserEditPageActionGroup" stepKey="openUserEditPageForDeleting"> + <argument name="user" value="$$user$$"/> + </actionGroup> + <actionGroup ref="AssertAdminImpossibleDeleteYourOwnAccountActionGroup" stepKey="assertErrorMessage"> + <argument name="currentAdminPassword" value="$$user.password$$"/> + </actionGroup> + <actionGroup ref="AssertAdminUserInGridActionGroup" stepKey="assertUserInGrid"> + <argument name="userName" value="$$user.username$$"/> + </actionGroup> + <actionGroup ref="AdminLogoutActionGroup" stepKey="logOutAsNewUser"/> + </test> +</tests> diff --git a/app/code/Magento/User/Test/Mftf/Test/AdminLockAdminUserEntityTest.xml b/app/code/Magento/User/Test/Mftf/Test/AdminLockAdminUserEntityTest.xml index 3bd55a454c3b0..850fa04549e84 100644 --- a/app/code/Magento/User/Test/Mftf/Test/AdminLockAdminUserEntityTest.xml +++ b/app/code/Magento/User/Test/Mftf/Test/AdminLockAdminUserEntityTest.xml @@ -19,16 +19,17 @@ <group value="user"/> <group value="mtf_migrated"/> </annotations> - <before> <magentoCLI command="config:set admin/captcha/enable 0" stepKey="disableAdminCaptcha"/> + <magentoCLI command="config:set admin/security/lockout_failures 2" stepKey="setDefaultMaximumLoginFailures"/> <magentoCLI command="cache:clean config full_page" stepKey="cleanInvalidatedCaches1"/> - <actionGroup ref="LoginAsAdmin" stepKey="logIn"/> + <actionGroup ref="AdminLoginActionGroup" stepKey="adminLogin"/> </before> <after> <magentoCLI command="config:set admin/captcha/enable 1" stepKey="enableAdminCaptcha"/> + <magentoCLI command="config:set admin/security/lockout_failures 6" stepKey="setDefaultMaximumLoginFailures"/> <magentoCLI command="cache:clean config full_page" stepKey="cleanInvalidatedCaches"/> - <actionGroup ref="logout" stepKey="logOut"/> + <actionGroup ref="AdminLogoutActionGroup" stepKey="adminLogout"/> </after> <!--Create New User--> @@ -38,33 +39,28 @@ </actionGroup> <actionGroup ref="AdminClickSaveButtonOnUserFormActionGroup" stepKey="saveNewUser"/> - <!--Set 'Maximum Login Failures to Lockout Account'--> - <actionGroup ref="AdminOpenConfigAdminPageActionGroup" stepKey="goToConfigAdminSectionPage"/> - <actionGroup ref="AdminExpandSecurityTabActionGroup" stepKey="openSecurityTab"/> - <actionGroup ref="AdminSetMaximumLoginFailuresToLockoutAccountActionGroup" stepKey="setMaximumLoginFailures"> - <argument name="qty" value="2"/> - </actionGroup> - <actionGroup ref="AdminSaveConfigActionGroup" stepKey="saveChanges"/> - <!-- Log in to Admin Panel with incorrect password specified number of times--> - <actionGroup ref="logout" stepKey="logoutAsDefaultUser"/> - <actionGroup ref="LoginAsAdmin" stepKey="loginAsNewUserFirstAttempt"> - <argument name="adminUser" value="adminUserIncorrectPassword"/> + <actionGroup ref="AdminLogoutActionGroup" stepKey="logoutAsDefaultUser"/> + <actionGroup ref="AdminLoginActionGroup" stepKey="loginAsNewUserFirstAttempt"> + <argument name="username" value="{{adminUserIncorrectPassword.username}}"/> + <argument name="password" value="{{adminUserIncorrectPassword.password}}"/> </actionGroup> <actionGroup ref="AssertMessageOnAdminLoginActionGroup" stepKey="checkLoginErrorFirstAttempt"/> - <actionGroup ref="LoginAsAdmin" stepKey="loginAsNewUserSecondAttempt"> - <argument name="adminUser" value="adminUserIncorrectPassword"/> + <actionGroup ref="AdminLoginActionGroup" stepKey="loginAsNewUserSecondAttempt"> + <argument name="username" value="{{adminUserIncorrectPassword.username}}"/> + <argument name="password" value="{{adminUserIncorrectPassword.password}}"/> </actionGroup> <actionGroup ref="AssertMessageOnAdminLoginActionGroup" stepKey="checkLoginErrorSecondAttempt"/> <!-- Log in to Admin Panel with correct password--> - <actionGroup ref="LoginAsAdmin" stepKey="loginAsNewUserThirdAttempt"> - <argument name="adminUser" value="adminUserCorrectPassword"/> + <actionGroup ref="AdminLoginActionGroup" stepKey="loginAsNewUserThirdAttempt"> + <argument name="username" value="{{adminUserCorrectPassword.username}}"/> + <argument name="password" value="{{adminUserCorrectPassword.password}}"/> </actionGroup> <actionGroup ref="AssertMessageOnAdminLoginActionGroup" stepKey="checkLoginErrorThirdAttempt"/> <!--Login as default admin user--> - <actionGroup ref="LoginAsAdmin" stepKey="loginAsDefaultAdminUser"/> + <actionGroup ref="AdminLoginActionGroup" stepKey="loginAsDefaultAdminUser"/> <!--Delete new User--> <actionGroup ref="AdminDeleteCustomUserActionGroup" stepKey="deleteNewUser"> diff --git a/app/code/Magento/User/Test/Mftf/Test/AdminSystemAllUsersNavigateMenuTest.xml b/app/code/Magento/User/Test/Mftf/Test/AdminSystemAllUsersNavigateMenuTest.xml index b899320403d71..9793387b02289 100644 --- a/app/code/Magento/User/Test/Mftf/Test/AdminSystemAllUsersNavigateMenuTest.xml +++ b/app/code/Magento/User/Test/Mftf/Test/AdminSystemAllUsersNavigateMenuTest.xml @@ -20,10 +20,10 @@ <group value="mtf_migrated"/> </annotations> <before> - <actionGroup ref="LoginAsAdmin" stepKey="LoginAsAdmin"/> + <actionGroup ref="AdminLoginActionGroup" stepKey="LoginAsAdmin"/> </before> <after> - <actionGroup ref="logout" stepKey="logout"/> + <actionGroup ref="AdminLogoutActionGroup" stepKey="logout"/> </after> <actionGroup ref="AdminNavigateMenuActionGroup" stepKey="navigateToSystemAllUsersPage"> <argument name="menuUiId" value="{{AdminMenuSystem.dataUiId}}"/> diff --git a/app/code/Magento/User/Test/Mftf/Test/AdminSystemLockedUsersNavigateMenuTest.xml b/app/code/Magento/User/Test/Mftf/Test/AdminSystemLockedUsersNavigateMenuTest.xml index aea46f3273157..e32f0736f880b 100644 --- a/app/code/Magento/User/Test/Mftf/Test/AdminSystemLockedUsersNavigateMenuTest.xml +++ b/app/code/Magento/User/Test/Mftf/Test/AdminSystemLockedUsersNavigateMenuTest.xml @@ -20,10 +20,10 @@ <group value="mtf_migrated"/> </annotations> <before> - <actionGroup ref="LoginAsAdmin" stepKey="LoginAsAdmin"/> + <actionGroup ref="AdminLoginActionGroup" stepKey="LoginAsAdmin"/> </before> <after> - <actionGroup ref="logout" stepKey="logout"/> + <actionGroup ref="AdminLogoutActionGroup" stepKey="logout"/> </after> <actionGroup ref="AdminNavigateMenuActionGroup" stepKey="navigateToSystemLockedUsersPage"> <argument name="menuUiId" value="{{AdminMenuSystem.dataUiId}}"/> diff --git a/app/code/Magento/User/Test/Mftf/Test/AdminSystemManageEncryptionKeyNavigateMenuTest.xml b/app/code/Magento/User/Test/Mftf/Test/AdminSystemManageEncryptionKeyNavigateMenuTest.xml index f8013a54058c3..8c7bdfc9360b5 100644 --- a/app/code/Magento/User/Test/Mftf/Test/AdminSystemManageEncryptionKeyNavigateMenuTest.xml +++ b/app/code/Magento/User/Test/Mftf/Test/AdminSystemManageEncryptionKeyNavigateMenuTest.xml @@ -20,10 +20,10 @@ <group value="mtf_migrated"/> </annotations> <before> - <actionGroup ref="LoginAsAdmin" stepKey="LoginAsAdmin"/> + <actionGroup ref="AdminLoginActionGroup" stepKey="LoginAsAdmin"/> </before> <after> - <actionGroup ref="logout" stepKey="logout"/> + <actionGroup ref="AdminLogoutActionGroup" stepKey="logout"/> </after> <actionGroup ref="AdminNavigateMenuActionGroup" stepKey="navigateToManageEncryptionKeyPage"> <argument name="menuUiId" value="{{AdminMenuSystem.dataUiId}}"/> diff --git a/app/code/Magento/User/Test/Mftf/Test/AdminSystemUserRolesNavigateMenuTest.xml b/app/code/Magento/User/Test/Mftf/Test/AdminSystemUserRolesNavigateMenuTest.xml index c4052a7f4219c..5abf119f18d4a 100644 --- a/app/code/Magento/User/Test/Mftf/Test/AdminSystemUserRolesNavigateMenuTest.xml +++ b/app/code/Magento/User/Test/Mftf/Test/AdminSystemUserRolesNavigateMenuTest.xml @@ -20,10 +20,10 @@ <group value="mtf_migrated"/> </annotations> <before> - <actionGroup ref="LoginAsAdmin" stepKey="LoginAsAdmin"/> + <actionGroup ref="AdminLoginActionGroup" stepKey="LoginAsAdmin"/> </before> <after> - <actionGroup ref="logout" stepKey="logout"/> + <actionGroup ref="AdminLogoutActionGroup" stepKey="logout"/> </after> <actionGroup ref="AdminNavigateMenuActionGroup" stepKey="navigateToSystemUserRolesPage"> <argument name="menuUiId" value="{{AdminMenuSystem.dataUiId}}"/> diff --git a/app/code/Magento/User/Test/Mftf/Test/AdminUpdateUserTest.xml b/app/code/Magento/User/Test/Mftf/Test/AdminUpdateUserTest.xml index dfadee8ee6807..10f81c9254b40 100644 --- a/app/code/Magento/User/Test/Mftf/Test/AdminUpdateUserTest.xml +++ b/app/code/Magento/User/Test/Mftf/Test/AdminUpdateUserTest.xml @@ -18,10 +18,12 @@ <description value="Change full access role for admin user to custom one with restricted permission (Sales)"/> <group value="user"/> <group value="mtf_migrated"/> + <!-- skip due to MQE-1964 --> + <group value="skip"/> </annotations> <before> - <actionGroup ref="LoginAsAdmin" stepKey="logIn"/> + <actionGroup ref="AdminLoginActionGroup" stepKey="logIn"/> <!--Create New User--> <actionGroup ref="AdminOpenNewUserPageActionGroup" stepKey="goToNewUserPage"/> @@ -39,8 +41,8 @@ </before> <after> <!--Delete new User--> - <actionGroup ref="logout" stepKey="logoutAsSaleRoleUser"/> - <actionGroup ref="LoginAsAdmin" stepKey="loginAsDefaultAdminUser"/> + <actionGroup ref="AdminLogoutActionGroup" stepKey="logoutAsSaleRoleUser"/> + <actionGroup ref="AdminLoginActionGroup" stepKey="loginAsDefaultAdminUser"/> <actionGroup ref="AdminDeleteCustomUserActionGroup" stepKey="deleteNewUser"> <argument name="user" value="AdminUserWithUpdatedUserRoleToSales"/> </actionGroup> @@ -49,7 +51,7 @@ <actionGroup ref="AdminDeleteUserRoleActionGroup" stepKey="deleteCustomRole"> <argument name="roleName" value="{{roleSales.rolename}}"/> </actionGroup> - <actionGroup ref="logout" stepKey="logOut"/> + <actionGroup ref="AdminLogoutActionGroup" stepKey="logOut"/> </after> @@ -68,9 +70,10 @@ <actionGroup ref="AssertAdminUserIsInGridActionGroup" stepKey="seeUserInGrid"> <argument name="user" value="AdminUserWithUpdatedUserRoleToSales"/> </actionGroup> - <actionGroup ref="logout" stepKey="logOutFromAdminPanel"/> - <actionGroup ref="LoginAsAdmin" stepKey="loginAsSaleRoleUser"> - <argument name="adminUser" value="AdminUserWithUpdatedUserRoleToSales"/> + <actionGroup ref="AdminLogoutActionGroup" stepKey="logOutFromAdminPanel"/> + <actionGroup ref="AdminLoginActionGroup" stepKey="loginAsSaleRoleUser"> + <argument name="username" value="{{AdminUserWithUpdatedUserRoleToSales.username}}"/> + <argument name="password" value="{{AdminUserWithUpdatedUserRoleToSales.password}}"/> </actionGroup> <actionGroup ref="AssertAdminSuccessLoginActionGroup" stepKey="seeSuccessloginMessage"/> <actionGroup ref="AdminOpenAdminUsersPageActionGroup" stepKey="navigateToAdminUsersPage"/> diff --git a/app/code/Magento/User/etc/adminhtml/system.xml b/app/code/Magento/User/etc/adminhtml/system.xml index 6c57b268968c3..584b40a023c93 100644 --- a/app/code/Magento/User/etc/adminhtml/system.xml +++ b/app/code/Magento/User/etc/adminhtml/system.xml @@ -9,7 +9,7 @@ <system> <section id="admin"> <group id="emails"> - <field id="user_notification_template" translate="label comment" type="select" sortOrder="40" showInDefault="1" showInWebsite="0" showInStore="0" canRestore="1"> + <field id="user_notification_template" translate="label comment" type="select" sortOrder="40" showInDefault="1" canRestore="1"> <label>User Notification Template</label> <comment>Email template chosen based on theme fallback when "Default" option is selected.</comment> <source_model>Magento\Config\Model\Config\Source\Email\Template</source_model> diff --git a/app/code/Magento/User/etc/config.xml b/app/code/Magento/User/etc/config.xml index c1f51bcbecef4..57631df18bb85 100644 --- a/app/code/Magento/User/etc/config.xml +++ b/app/code/Magento/User/etc/config.xml @@ -16,6 +16,7 @@ </emails> <security> <password_reset_link_expiration_period>2</password_reset_link_expiration_period> + <use_case_sensitive_login>0</use_case_sensitive_login> <lockout_failures>6</lockout_failures> <lockout_threshold>30</lockout_threshold> <password_lifetime>90</password_lifetime> diff --git a/app/code/Magento/User/registration.php b/app/code/Magento/User/registration.php index c00bc0a633803..9a81d456f5f57 100644 --- a/app/code/Magento/User/registration.php +++ b/app/code/Magento/User/registration.php @@ -4,6 +4,6 @@ * See COPYING.txt for license details. */ -use \Magento\Framework\Component\ComponentRegistrar; +use Magento\Framework\Component\ComponentRegistrar; ComponentRegistrar::register(ComponentRegistrar::MODULE, 'Magento_User', __DIR__); diff --git a/app/code/Magento/Usps/Test/Mftf/Test/AdminCheckInputFieldsDisabledAfterAppConfigDumpTest.xml b/app/code/Magento/Usps/Test/Mftf/Test/AdminCheckInputFieldsDisabledAfterAppConfigDumpTest.xml index cd77861fccd58..4d4f84c41e1d4 100644 --- a/app/code/Magento/Usps/Test/Mftf/Test/AdminCheckInputFieldsDisabledAfterAppConfigDumpTest.xml +++ b/app/code/Magento/Usps/Test/Mftf/Test/AdminCheckInputFieldsDisabledAfterAppConfigDumpTest.xml @@ -15,42 +15,99 @@ <conditionalClick selector="{{AdminShippingMethodUSPSSection.carriersUSPSTab}}" dependentSelector="{{AdminShippingMethodUSPSSection.carriersUSPSActive}}" visible="false" stepKey="expandUSPSTab"/> <waitForElementVisible selector="{{AdminShippingMethodUSPSSection.carriersUSPSActive}}" stepKey="waitUSPSTabOpen"/> <grabAttributeFrom selector="{{AdminShippingMethodUSPSSection.carriersUSPSActive}}" userInput="disabled" stepKey="grabUSPSActiveDisabled"/> - <assertEquals expected='true' expectedType="string" actual="$grabUSPSActiveDisabled" stepKey="assertUSPSActiveDisabled"/> + <assertEquals stepKey="assertUSPSActiveDisabled"> + <actualResult type="const">$grabUSPSActiveDisabled</actualResult> + <expectedResult type="string">true</expectedResult> + </assertEquals> <grabAttributeFrom selector="{{AdminShippingMethodUSPSSection.carriersUSPSGatewayXMLUrl}}" userInput="disabled" stepKey="grabUSPSGatewayXMLUrlDisabled"/> - <assertEquals expected='true' expectedType="string" actual="$grabUSPSGatewayXMLUrlDisabled" stepKey="assertUSPSGatewayXMLUrlDisabled"/> + <assertEquals stepKey="assertUSPSGatewayXMLUrlDisabled"> + <actualResult type="const">$grabUSPSGatewayXMLUrlDisabled</actualResult> + <expectedResult type="string">true</expectedResult> + </assertEquals> <grabAttributeFrom selector="{{AdminShippingMethodUSPSSection.carriersUSPSGatewaySecureUrl}}" userInput="disabled" stepKey="grabUSPSGatewaySecureUrlDisabled"/> - <assertEquals expected='true' expectedType="string" actual="$grabUSPSGatewaySecureUrlDisabled" stepKey="assertUSPSGatewaySecureUrlDisabled"/> + <assertEquals stepKey="assertUSPSGatewaySecureUrlDisabled"> + <actualResult type="const">$grabUSPSGatewaySecureUrlDisabled</actualResult> + <expectedResult type="string">true</expectedResult> + </assertEquals> <grabAttributeFrom selector="{{AdminShippingMethodUSPSSection.carriersUSPSTitle}}" userInput="disabled" stepKey="grabUSPSTitleDisabled"/> - <assertEquals expected='true' expectedType="string" actual="$grabUSPSTitleDisabled" stepKey="assertUSPSTitleDisabled"/> + <assertEquals stepKey="assertUSPSTitleDisabled"> + <actualResult type="const">$grabUSPSTitleDisabled</actualResult> + <expectedResult type="string">true</expectedResult> + </assertEquals> <grabAttributeFrom selector="{{AdminShippingMethodUSPSSection.carriersUSPSUserId}}" userInput="disabled" stepKey="grabUSPSUserIdDisabled"/> - <assertEquals expected='true' expectedType="string" actual="$grabUSPSUserIdDisabled" stepKey="assertUSPSUserIdDisabled"/> + <assertEquals stepKey="assertUSPSUserIdDisabled"> + <actualResult type="const">$grabUSPSUserIdDisabled</actualResult> + <expectedResult type="string">true</expectedResult> + </assertEquals> <grabAttributeFrom selector="{{AdminShippingMethodUSPSSection.carriersUSPSPassword}}" userInput="disabled" stepKey="grabUSPSPasswordDisabled"/> - <assertEquals expected='true' expectedType="string" actual="$grabUSPSPasswordDisabled" stepKey="assertUSPSPasswordDisabled"/> + <assertEquals stepKey="assertUSPSPasswordDisabled"> + <actualResult type="const">$grabUSPSPasswordDisabled</actualResult> + <expectedResult type="string">true</expectedResult> + </assertEquals> <grabAttributeFrom selector="{{AdminShippingMethodUSPSSection.carriersUSPSShipmentRequestType}}" userInput="disabled" stepKey="grabUSPSShipmentRequestTypeDisabled"/> - <assertEquals expected='true' expectedType="string" actual="$grabUSPSShipmentRequestTypeDisabled" stepKey="assertUSPSShipmentRequestTypeDisabled"/> + <assertEquals stepKey="assertUSPSShipmentRequestTypeDisabled"> + <actualResult type="const">$grabUSPSShipmentRequestTypeDisabled</actualResult> + <expectedResult type="string">true</expectedResult> + </assertEquals> <grabAttributeFrom selector="{{AdminShippingMethodUSPSSection.carriersUSPSContainer}}" userInput="disabled" stepKey="grabUSPSContainerDisabled"/> - <assertEquals expected='true' expectedType="string" actual="$grabUSPSContainerDisabled" stepKey="assertUSPSContainerDisabled"/> + <assertEquals stepKey="assertUSPSContainerDisabled"> + <actualResult type="const">$grabUSPSContainerDisabled</actualResult> + <expectedResult type="string">true</expectedResult> + </assertEquals> <grabAttributeFrom selector="{{AdminShippingMethodUSPSSection.carriersUSPSSize}}" userInput="disabled" stepKey="grabUSPSSizeDisabled"/> - <assertEquals expected='true' expectedType="string" actual="$grabUSPSSizeDisabled" stepKey="assertUSPSSizeDisabled"/> + <assertEquals stepKey="assertUSPSSizeDisabled"> + <actualResult type="const">$grabUSPSSizeDisabled</actualResult> + <expectedResult type="string">true</expectedResult> + </assertEquals> <grabAttributeFrom selector="{{AdminShippingMethodUSPSSection.carriersUSPSDestType}}" userInput="disabled" stepKey="grabUSPSDestTypeDisabled"/> - <assertEquals expected='true' expectedType="string" actual="$grabUSPSDestTypeDisabled" stepKey="assertUSPSDestTypeDisabled"/> + <assertEquals stepKey="assertUSPSDestTypeDisabled"> + <actualResult type="const">$grabUSPSDestTypeDisabled</actualResult> + <expectedResult type="string">true</expectedResult> + </assertEquals> <grabAttributeFrom selector="{{AdminShippingMethodUSPSSection.carriersUSPSMachinable}}" userInput="disabled" stepKey="grabUSPSMachinableDisabled"/> - <assertEquals expected='true' expectedType="string" actual="$grabUSPSMachinableDisabled" stepKey="assertUSPSMachinableDisabled"/> + <assertEquals stepKey="assertUSPSMachinableDisabled"> + <actualResult type="const">$grabUSPSMachinableDisabled</actualResult> + <expectedResult type="string">true</expectedResult> + </assertEquals> <grabAttributeFrom selector="{{AdminShippingMethodUSPSSection.carriersUSPSMaxPackageWeight}}" userInput="disabled" stepKey="grabUSPSMaxPackageWeightDisabled"/> - <assertEquals expected='true' expectedType="string" actual="$grabUSPSMaxPackageWeightDisabled" stepKey="assertUSPSMaxPackageWeightDisabled"/> + <assertEquals stepKey="assertUSPSMaxPackageWeightDisabled"> + <actualResult type="const">$grabUSPSMaxPackageWeightDisabled</actualResult> + <expectedResult type="string">true</expectedResult> + </assertEquals> <grabAttributeFrom selector="{{AdminShippingMethodUSPSSection.carriersUSPSHandlingType}}" userInput="disabled" stepKey="grabUSPSHandlingTypeDisabled"/> - <assertEquals expected='true' expectedType="string" actual="$grabUSPSHandlingTypeDisabled" stepKey="assertUSPSHandlingTypeDisabled"/> + <assertEquals stepKey="assertUSPSHandlingTypeDisabled"> + <actualResult type="const">$grabUSPSHandlingTypeDisabled</actualResult> + <expectedResult type="string">true</expectedResult> + </assertEquals> <grabAttributeFrom selector="{{AdminShippingMethodUSPSSection.carriersUSPSHandlingAction}}" userInput="disabled" stepKey="grabUSPSHandlingActionDisabled"/> - <assertEquals expected='true' expectedType="string" actual="$grabUSPSHandlingActionDisabled" stepKey="assertUSPSHandlingActionDisabled"/> + <assertEquals stepKey="assertUSPSHandlingActionDisabled"> + <actualResult type="const">$grabUSPSHandlingActionDisabled</actualResult> + <expectedResult type="string">true</expectedResult> + </assertEquals> <grabAttributeFrom selector="{{AdminShippingMethodUSPSSection.carriersUSPSAllowedMethods}}" userInput="disabled" stepKey="grabUSPSAllowedMethodsDisabled"/> - <assertEquals expected='true' expectedType="string" actual="$grabUSPSAllowedMethodsDisabled" stepKey="assertUSPSAllowedMethodsDisabled"/> + <assertEquals stepKey="assertUSPSAllowedMethodsDisabled"> + <actualResult type="const">$grabUSPSAllowedMethodsDisabled</actualResult> + <expectedResult type="string">true</expectedResult> + </assertEquals> <grabAttributeFrom selector="{{AdminShippingMethodUSPSSection.carriersUSPSFreeMethod}}" userInput="disabled" stepKey="grabUSPSFreeMethodDisabled"/> - <assertEquals expected='true' expectedType="string" actual="$grabUSPSFreeMethodDisabled" stepKey="assertUSPSFreeMethodDisabled"/> + <assertEquals stepKey="assertUSPSFreeMethodDisabled"> + <actualResult type="const">$grabUSPSFreeMethodDisabled</actualResult> + <expectedResult type="string">true</expectedResult> + </assertEquals> <grabAttributeFrom selector="{{AdminShippingMethodUSPSSection.carriersUSPSSpecificErrMsg}}" userInput="disabled" stepKey="grabUSPSSpecificErrMsgDisabled"/> - <assertEquals expected='true' expectedType="string" actual="$grabUSPSSpecificErrMsgDisabled" stepKey="assertUSPSSpecificErrMsgDisabled"/> + <assertEquals stepKey="assertUSPSSpecificErrMsgDisabled"> + <actualResult type="const">$grabUSPSSpecificErrMsgDisabled</actualResult> + <expectedResult type="string">true</expectedResult> + </assertEquals> <grabAttributeFrom selector="{{AdminShippingMethodUSPSSection.carriersUSPSAllowSpecific}}" userInput="disabled" stepKey="grabUSPSAllowSpecificDisabled"/> - <assertEquals expected='true' expectedType="string" actual="$grabUSPSAllowSpecificDisabled" stepKey="assertUSPSAllowSpecificDisabled"/> + <assertEquals stepKey="assertUSPSAllowSpecificDisabled"> + <actualResult type="const">$grabUSPSAllowSpecificDisabled</actualResult> + <expectedResult type="string">true</expectedResult> + </assertEquals> <grabAttributeFrom selector="{{AdminShippingMethodUSPSSection.carriersUSPSSpecificCountry}}" userInput="disabled" stepKey="grabUSPSSpecificCountryDisabled"/> - <assertEquals expected='true' expectedType="string" actual="$grabUSPSSpecificCountryDisabled" stepKey="assertUSPSSpecificCountryDisabled"/> + <assertEquals stepKey="assertUSPSSpecificCountryDisabled"> + <actualResult type="const">$grabUSPSSpecificCountryDisabled</actualResult> + <expectedResult type="string">true</expectedResult> + </assertEquals> </test> </tests> diff --git a/app/code/Magento/Usps/Test/Unit/Model/CarrierTest.php b/app/code/Magento/Usps/Test/Unit/Model/CarrierTest.php index cc29c5cffadf1..1f7b149ca07de 100644 --- a/app/code/Magento/Usps/Test/Unit/Model/CarrierTest.php +++ b/app/code/Magento/Usps/Test/Unit/Model/CarrierTest.php @@ -205,7 +205,7 @@ public function scopeConfigGetValue($path) 'carriers/usps/mode' => 0, ]; - return isset($pathMap[$path]) ? $pathMap[$path] : null; + return $pathMap[$path] ?? null; } /** diff --git a/app/code/Magento/Usps/etc/adminhtml/system.xml b/app/code/Magento/Usps/etc/adminhtml/system.xml index 0849572e7eb1c..b01f7be9a19f9 100644 --- a/app/code/Magento/Usps/etc/adminhtml/system.xml +++ b/app/code/Magento/Usps/etc/adminhtml/system.xml @@ -10,102 +10,102 @@ <section id="carriers"> <group id="usps" translate="label" type="text" sortOrder="110" showInDefault="1" showInWebsite="1" showInStore="1"> <label>USPS</label> - <field id="active" translate="label" type="select" sortOrder="10" showInDefault="1" showInWebsite="1" showInStore="0" canRestore="1"> + <field id="active" translate="label" type="select" sortOrder="10" showInDefault="1" showInWebsite="1" canRestore="1"> <label>Enabled for Checkout</label> <source_model>Magento\Config\Model\Config\Source\Yesno</source_model> </field> - <field id="gateway_url" translate="label" type="text" sortOrder="20" showInDefault="1" showInWebsite="1" showInStore="0" canRestore="1"> + <field id="gateway_url" translate="label" type="text" sortOrder="20" showInDefault="1" showInWebsite="1" canRestore="1"> <label>Gateway URL</label> </field> - <field id="gateway_secure_url" translate="label" type="text" sortOrder="30" showInDefault="1" showInWebsite="1" showInStore="0" canRestore="1"> + <field id="gateway_secure_url" translate="label" type="text" sortOrder="30" showInDefault="1" showInWebsite="1" canRestore="1"> <label>Secure Gateway URL</label> </field> <field id="title" translate="label" type="text" sortOrder="40" showInDefault="1" showInWebsite="1" showInStore="1" canRestore="1"> <label>Title</label> </field> - <field id="userid" translate="label" type="obscure" sortOrder="50" showInDefault="1" showInWebsite="1" showInStore="0"> + <field id="userid" translate="label" type="obscure" sortOrder="50" showInDefault="1" showInWebsite="1"> <label>User ID</label> <backend_model>Magento\Config\Model\Config\Backend\Encrypted</backend_model> </field> - <field id="password" translate="label" type="obscure" sortOrder="53" showInDefault="1" showInWebsite="1" showInStore="0"> + <field id="password" translate="label" type="obscure" sortOrder="53" showInDefault="1" showInWebsite="1"> <label>Password</label> <backend_model>Magento\Config\Model\Config\Backend\Encrypted</backend_model> </field> - <field id="mode" translate="label" type="select" sortOrder="54" showInDefault="1" showInWebsite="1" showInStore="0" canRestore="1"> + <field id="mode" translate="label" type="select" sortOrder="54" showInDefault="1" showInWebsite="1" canRestore="1"> <label>Mode</label> <source_model>Magento\Shipping\Model\Config\Source\Online\Mode</source_model> </field> - <field id="shipment_requesttype" translate="label" type="select" sortOrder="55" showInDefault="1" showInWebsite="1" showInStore="0" canRestore="1"> + <field id="shipment_requesttype" translate="label" type="select" sortOrder="55" showInDefault="1" showInWebsite="1" canRestore="1"> <label>Packages Request Type</label> <source_model>Magento\Shipping\Model\Config\Source\Online\Requesttype</source_model> </field> - <field id="container" translate="label" type="select" sortOrder="60" showInDefault="1" showInWebsite="1" showInStore="0" canRestore="1"> + <field id="container" translate="label" type="select" sortOrder="60" showInDefault="1" showInWebsite="1" canRestore="1"> <label>Container</label> <source_model>Magento\Usps\Model\Source\Container</source_model> </field> - <field id="size" translate="label" type="select" sortOrder="70" showInDefault="1" showInWebsite="1" showInStore="0" canRestore="1"> + <field id="size" translate="label" type="select" sortOrder="70" showInDefault="1" showInWebsite="1" canRestore="1"> <label>Size</label> <source_model>Magento\Usps\Model\Source\Size</source_model> </field> - <field id="width" translate="label" type="text" sortOrder="73" showInDefault="1" showInWebsite="1" showInStore="0"> + <field id="width" translate="label" type="text" sortOrder="73" showInDefault="1" showInWebsite="1"> <label>Width</label> <depends> <field id="size">LARGE</field> </depends> </field> - <field id="length" translate="label" type="text" sortOrder="72" showInDefault="1" showInWebsite="1" showInStore="0"> + <field id="length" translate="label" type="text" sortOrder="72" showInDefault="1" showInWebsite="1"> <label>Length</label> <depends> <field id="size">LARGE</field> </depends> </field> - <field id="height" translate="label" type="text" sortOrder="74" showInDefault="1" showInWebsite="1" showInStore="0"> + <field id="height" translate="label" type="text" sortOrder="74" showInDefault="1" showInWebsite="1"> <label>Height</label> <depends> <field id="size">LARGE</field> </depends> </field> - <field id="girth" translate="label" type="text" sortOrder="76" showInDefault="1" showInWebsite="1" showInStore="0"> + <field id="girth" translate="label" type="text" sortOrder="76" showInDefault="1" showInWebsite="1"> <label>Girth</label> <depends> <field id="size">LARGE</field> </depends> </field> - <field id="machinable" translate="label" type="select" sortOrder="80" showInDefault="1" showInWebsite="1" showInStore="0" canRestore="1"> + <field id="machinable" translate="label" type="select" sortOrder="80" showInDefault="1" showInWebsite="1" canRestore="1"> <label>Machinable</label> <source_model>Magento\Usps\Model\Source\Machinable</source_model> </field> - <field id="max_package_weight" translate="label" type="text" sortOrder="90" showInDefault="1" showInWebsite="1" showInStore="0" canRestore="1"> + <field id="max_package_weight" translate="label" type="text" sortOrder="90" showInDefault="1" showInWebsite="1" canRestore="1"> <label>Maximum Package Weight (Please consult your shipping carrier for maximum supported shipping weight)</label> <validate>validate-number validate-zero-or-greater</validate> </field> - <field id="handling_type" translate="label" type="select" sortOrder="100" showInDefault="1" showInWebsite="1" showInStore="0" canRestore="1"> + <field id="handling_type" translate="label" type="select" sortOrder="100" showInDefault="1" showInWebsite="1" canRestore="1"> <label>Calculate Handling Fee</label> <source_model>Magento\Shipping\Model\Source\HandlingType</source_model> </field> - <field id="handling_action" translate="label" type="select" sortOrder="110" showInDefault="1" showInWebsite="1" showInStore="0" canRestore="1"> + <field id="handling_action" translate="label" type="select" sortOrder="110" showInDefault="1" showInWebsite="1" canRestore="1"> <label>Handling Applied</label> <source_model>Magento\Shipping\Model\Source\HandlingAction</source_model> </field> - <field id="handling_fee" translate="label" type="text" sortOrder="120" showInDefault="1" showInWebsite="1" showInStore="0"> + <field id="handling_fee" translate="label" type="text" sortOrder="120" showInDefault="1" showInWebsite="1"> <label>Handling Fee</label> <validate>validate-number validate-zero-or-greater</validate> </field> - <field id="allowed_methods" translate="label" type="multiselect" sortOrder="130" showInDefault="1" showInWebsite="1" showInStore="0" canRestore="1"> + <field id="allowed_methods" translate="label" type="multiselect" sortOrder="130" showInDefault="1" showInWebsite="1" canRestore="1"> <label>Allowed Methods</label> <source_model>Magento\Usps\Model\Source\Method</source_model> <can_be_empty>1</can_be_empty> </field> - <field id="free_method" translate="label" type="select" sortOrder="140" showInDefault="1" showInWebsite="1" showInStore="0" canRestore="1"> + <field id="free_method" translate="label" type="select" sortOrder="140" showInDefault="1" showInWebsite="1" canRestore="1"> <label>Free Method</label> <frontend_class>free-method</frontend_class> <source_model>Magento\Usps\Model\Source\Freemethod</source_model> </field> - <field id="free_shipping_enable" translate="label" type="select" sortOrder="160" showInDefault="1" showInWebsite="1" showInStore="0"> + <field id="free_shipping_enable" translate="label" type="select" sortOrder="160" showInDefault="1" showInWebsite="1"> <label>Enable Free Shipping Threshold</label> <source_model>Magento\Config\Model\Config\Source\Enabledisable</source_model> </field> - <field id="free_shipping_subtotal" translate="label" type="text" sortOrder="165" showInDefault="1" showInWebsite="1" showInStore="0"> + <field id="free_shipping_subtotal" translate="label" type="text" sortOrder="165" showInDefault="1" showInWebsite="1"> <label>Free Shipping Amount Threshold</label> <validate>validate-number validate-zero-or-greater</validate> <depends> @@ -115,26 +115,26 @@ <field id="specificerrmsg" translate="label" type="textarea" sortOrder="170" showInDefault="1" showInWebsite="1" showInStore="1" canRestore="1"> <label>Displayed Error Message</label> </field> - <field id="sallowspecific" translate="label" type="select" sortOrder="180" showInDefault="1" showInWebsite="1" showInStore="0" canRestore="1"> + <field id="sallowspecific" translate="label" type="select" sortOrder="180" showInDefault="1" showInWebsite="1" canRestore="1"> <label>Ship to Applicable Countries</label> <frontend_class>shipping-applicable-country</frontend_class> <source_model>Magento\Shipping\Model\Config\Source\Allspecificcountries</source_model> </field> - <field id="specificcountry" translate="label" type="multiselect" sortOrder="190" showInDefault="1" showInWebsite="1" showInStore="0"> + <field id="specificcountry" translate="label" type="multiselect" sortOrder="190" showInDefault="1" showInWebsite="1"> <label>Ship to Specific Countries</label> <source_model>Magento\Directory\Model\Config\Source\Country</source_model> <can_be_empty>1</can_be_empty> </field> - <field id="debug" translate="label" type="select" sortOrder="200" showInDefault="1" showInWebsite="1" showInStore="0"> + <field id="debug" translate="label" type="select" sortOrder="200" showInDefault="1" showInWebsite="1"> <label>Debug</label> <source_model>Magento\Config\Model\Config\Source\Yesno</source_model> </field> - <field id="showmethod" translate="label" type="select" sortOrder="210" showInDefault="1" showInWebsite="1" showInStore="0"> + <field id="showmethod" translate="label" type="select" sortOrder="210" showInDefault="1" showInWebsite="1"> <label>Show Method if Not Applicable</label> <frontend_class>shipping-skip-hide</frontend_class> <source_model>Magento\Config\Model\Config\Source\Yesno</source_model> </field> - <field id="sort_order" translate="label" type="text" sortOrder="220" showInDefault="1" showInWebsite="1" showInStore="0"> + <field id="sort_order" translate="label" type="text" sortOrder="220" showInDefault="1" showInWebsite="1"> <label>Sort Order</label> <validate>validate-number validate-zero-or-greater</validate> </field> diff --git a/app/code/Magento/Usps/etc/config.xml b/app/code/Magento/Usps/etc/config.xml index 68c1b31fcd515..7bfe3dc86a06e 100644 --- a/app/code/Magento/Usps/etc/config.xml +++ b/app/code/Magento/Usps/etc/config.xml @@ -11,7 +11,7 @@ <usps> <active>0</active> <sallowspecific>0</sallowspecific> - <allowed_methods>0_FCLE,0_FCL,0_FCP,1,2,3,4,6,7,13,16,17,22,23,25,27,28,33,34,35,36,37,42,43,53,55,56,57,61,INT_1,INT_2,INT_4,INT_6,INT_7,INT_8,INT_9,INT_10,INT_11,INT_12,INT_13,INT_14,INT_15,INT_16,INT_20,INT_26</allowed_methods> + <allowed_methods>0_FCLE,0_FCL,0_FCP,1,2,3,4,6,7,13,16,17,22,23,25,27,28,33,34,35,36,37,42,43,53,57,61,INT_1,INT_2,INT_4,INT_6,INT_7,INT_8,INT_9,INT_10,INT_11,INT_12,INT_13,INT_14,INT_15,INT_16,INT_20</allowed_methods> <container>VARIABLE</container> <cutoff_cost /> <free_method /> diff --git a/app/code/Magento/Usps/registration.php b/app/code/Magento/Usps/registration.php index 988089ccda954..32fe2a6a49fbd 100644 --- a/app/code/Magento/Usps/registration.php +++ b/app/code/Magento/Usps/registration.php @@ -4,6 +4,6 @@ * See COPYING.txt for license details. */ -use \Magento\Framework\Component\ComponentRegistrar; +use Magento\Framework\Component\ComponentRegistrar; ComponentRegistrar::register(ComponentRegistrar::MODULE, 'Magento_Usps', __DIR__); diff --git a/app/code/Magento/Variable/Test/Mftf/Test/AdminCreateCustomVariableEntityTest.xml b/app/code/Magento/Variable/Test/Mftf/Test/AdminCreateCustomVariableEntityTest.xml index 3185640e3783a..1c6e222a242d8 100644 --- a/app/code/Magento/Variable/Test/Mftf/Test/AdminCreateCustomVariableEntityTest.xml +++ b/app/code/Magento/Variable/Test/Mftf/Test/AdminCreateCustomVariableEntityTest.xml @@ -20,10 +20,10 @@ <group value="mtf_migrated"/> </annotations> <before> - <actionGroup ref="LoginAsAdmin" stepKey="loginAsAdmin"/> + <actionGroup ref="AdminLoginActionGroup" stepKey="loginAsAdmin"/> </before> <after> - <actionGroup ref="logout" stepKey="logout"/> + <actionGroup ref="AdminLogoutActionGroup" stepKey="logout"/> </after> <actionGroup ref="AdminOpenNewVariablePageActionGroup" stepKey="goToNewVariableAdminPage"/> <actionGroup ref="AdminFillVariableFormActionGroup" stepKey="fillInCustomVariableData"> diff --git a/app/code/Magento/Variable/Test/Mftf/Test/AdminSystemCustomVariablesNavigateMenuTest.xml b/app/code/Magento/Variable/Test/Mftf/Test/AdminSystemCustomVariablesNavigateMenuTest.xml index 74446cf601348..8197f9a5b5752 100644 --- a/app/code/Magento/Variable/Test/Mftf/Test/AdminSystemCustomVariablesNavigateMenuTest.xml +++ b/app/code/Magento/Variable/Test/Mftf/Test/AdminSystemCustomVariablesNavigateMenuTest.xml @@ -20,10 +20,10 @@ <group value="mtf_migrated"/> </annotations> <before> - <actionGroup ref="LoginAsAdmin" stepKey="LoginAsAdmin"/> + <actionGroup ref="AdminLoginActionGroup" stepKey="LoginAsAdmin"/> </before> <after> - <actionGroup ref="logout" stepKey="logout"/> + <actionGroup ref="AdminLogoutActionGroup" stepKey="logout"/> </after> <actionGroup ref="AdminNavigateMenuActionGroup" stepKey="navigateToSystemCustomVariablesPage"> <argument name="menuUiId" value="{{AdminMenuSystem.dataUiId}}"/> diff --git a/app/code/Magento/Variable/registration.php b/app/code/Magento/Variable/registration.php index a35a0da8b2c79..c68fbb63c2e64 100644 --- a/app/code/Magento/Variable/registration.php +++ b/app/code/Magento/Variable/registration.php @@ -4,6 +4,6 @@ * See COPYING.txt for license details. */ -use \Magento\Framework\Component\ComponentRegistrar; +use Magento\Framework\Component\ComponentRegistrar; ComponentRegistrar::register(ComponentRegistrar::MODULE, 'Magento_Variable', __DIR__); diff --git a/app/code/Magento/Vault/Test/Mftf/Page/StorefrontOnePageCheckoutPage.xml b/app/code/Magento/Vault/Test/Mftf/Page/StorefrontOnePageCheckoutPage.xml new file mode 100644 index 0000000000000..f767dcedc13b6 --- /dev/null +++ b/app/code/Magento/Vault/Test/Mftf/Page/StorefrontOnePageCheckoutPage.xml @@ -0,0 +1,14 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + /** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +--> + +<pages xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Page/etc/PageObject.xsd"> + <page name="CheckoutPage" url="/checkout" area="storefront" module="Magento_Checkout"> + <section name="StorefrontOnePageCheckoutPaymentSection"/> + </page> +</pages> diff --git a/app/code/Magento/Vault/Test/Mftf/Section/StorefrontOnePageCheckoutSection.xml b/app/code/Magento/Vault/Test/Mftf/Section/StorefrontOnePageCheckoutSection.xml new file mode 100644 index 0000000000000..53ad03bd01ef8 --- /dev/null +++ b/app/code/Magento/Vault/Test/Mftf/Section/StorefrontOnePageCheckoutSection.xml @@ -0,0 +1,14 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + /** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +--> + +<sections xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Page/etc/SectionObject.xsd"> + <section name="StorefrontOnePageCheckoutPaymentSection"> + <element name="saveForLaterUse" type="checkbox" selector="fieldset.payment > div.choice > input[name='vault[is_enabled]']"/> + </section> +</sections> diff --git a/app/code/Magento/Vault/Test/Mftf/Test/StorefrontVerifySecureURLRedirectVaultTest.xml b/app/code/Magento/Vault/Test/Mftf/Test/StorefrontVerifySecureURLRedirectVaultTest.xml index c9d4cb3391cfd..f496e500a4d9b 100644 --- a/app/code/Magento/Vault/Test/Mftf/Test/StorefrontVerifySecureURLRedirectVaultTest.xml +++ b/app/code/Magento/Vault/Test/Mftf/Test/StorefrontVerifySecureURLRedirectVaultTest.xml @@ -8,7 +8,7 @@ <tests xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/testSchema.xsd"> - <test name="StorefrontVerifySecureURLRedirectVault"> + <test name="StorefrontVerifySecureURLRedirectVaultTest"> <annotations> <features value="Vault"/> <stories value="Storefront Secure URLs"/> diff --git a/app/code/Magento/Vault/Test/Unit/Plugin/PaymentVaultAttributesLoadTest.php b/app/code/Magento/Vault/Test/Unit/Plugin/PaymentVaultAttributesLoadTest.php new file mode 100644 index 0000000000000..5a945c5462979 --- /dev/null +++ b/app/code/Magento/Vault/Test/Unit/Plugin/PaymentVaultAttributesLoadTest.php @@ -0,0 +1,134 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ + +declare(strict_types=1); + +namespace Magento\Vault\Test\Unit\Plugin; + +use Magento\Framework\TestFramework\Unit\Helper\ObjectManager as ObjectManagerHelper; +use Magento\Sales\Api\Data\OrderPaymentExtensionFactory; +use Magento\Sales\Api\Data\OrderPaymentExtensionInterface; +use Magento\Sales\Api\Data\OrderPaymentInterface; +use Magento\Vault\Api\Data\PaymentTokenInterface; +use Magento\Vault\Api\PaymentTokenManagementInterface; +use Magento\Vault\Plugin\PaymentVaultAttributesLoad; +use PHPUnit\Framework\MockObject\MockObject; +use PHPUnit\Framework\TestCase; + +/** + * Unit Test for \Magento\Vault\Plugin\PaymentVaultAttributesLoad + */ +class PaymentVaultAttributesLoadTest extends TestCase +{ + /** + * @var OrderPaymentExtensionFactory|MockObject + */ + private $paymentExtensionFactoryMock; + + /** + * @var PaymentTokenManagementInterface|MockObject + */ + private $paymentTokenManagementMock; + + /** + * @var OrderPaymentInterface|MockObject + */ + private $paymentMock; + + /** + * @var OrderPaymentExtensionInterface|MockObject + */ + private $paymentExtensionMock; + + /** + * @var PaymentVaultAttributesLoad + */ + private $plugin; + + /** + * @inheritDoc + */ + protected function setUp(): void + { + $this->paymentMock = $this->getMockBuilder(OrderPaymentInterface::class) + ->disableOriginalConstructor() + ->setMethods(['getEntityId', 'setExtensionAttributes']) + ->getMockForAbstractClass(); + $this->paymentExtensionMock = $this->getMockBuilder(OrderPaymentExtensionInterface::class) + ->disableOriginalConstructor() + ->setMethods(['getVaultPaymentToken', 'setVaultPaymentToken']) + ->getMockForAbstractClass(); + + $this->paymentExtensionFactoryMock = $this->createMock(OrderPaymentExtensionFactory::class); + $this->paymentTokenManagementMock = $this->getMockBuilder(PaymentTokenManagementInterface::class) + ->disableOriginalConstructor() + ->setMethods(['getByPaymentId']) + ->getMockForAbstractClass(); + + $this->plugin = (new ObjectManagerHelper($this))->getObject( + PaymentVaultAttributesLoad::class, + [ + 'paymentExtensionFactory' => $this->paymentExtensionFactoryMock, + 'paymentTokenManagement' => $this->paymentTokenManagementMock + ] + ); + } + + /** + * Test case when paymentExtension param was not provided. + */ + public function testAfterGetExtensionAttributesCallsFactoryIfPaymentExtensionIsNull(): void + { + $this->paymentExtensionFactoryMock->expects($this->once()) + ->method('create') + ->willReturn($this->paymentExtensionMock); + + $this->assertSame( + $this->paymentExtensionMock, + $this->plugin->afterGetExtensionAttributes($this->paymentMock, null) + ); + } + + /** + * Test case when payment token was already set. + */ + public function testAfterGetExtensionAttributesWhenPaymentTokenIsNotNull(): void + { + $this->paymentExtensionMock->expects($this->once()) + ->method('getVaultPaymentToken') + ->willReturn($this->createMock(PaymentTokenInterface::class)); + $this->paymentTokenManagementMock->expects($this->never())->method('getByPaymentId'); + $this->paymentMock->expects($this->never())->method('setExtensionAttributes'); + $this->assertSame( + $this->paymentExtensionMock, + $this->plugin->afterGetExtensionAttributes($this->paymentMock, $this->paymentExtensionMock) + ); + } + + /** + * Test case when payment token is null and extension attributes must be set. + */ + public function testAfterGetExtensionAttributesWhenPaymentTokenIsNull(): void + { + $this->paymentExtensionMock->expects($this->once())->method('getVaultPaymentToken')->willReturn(null); + + $paymentTokenMock = $this->createMock(PaymentTokenInterface::class); + $this->paymentTokenManagementMock->expects($this->once()) + ->method('getByPaymentId') + ->willReturn($paymentTokenMock); + $this->paymentExtensionMock->expects($this->once()) + ->method('setVaultPaymentToken') + ->with($paymentTokenMock); + $this->paymentMock->expects($this->once()) + ->method('setExtensionAttributes') + ->with($this->paymentExtensionMock); + + $this->assertSame( + $this->paymentExtensionMock, + $this->plugin->afterGetExtensionAttributes($this->paymentMock, $this->paymentExtensionMock) + ); + } +} diff --git a/app/code/Magento/Vault/registration.php b/app/code/Magento/Vault/registration.php index 704dd359d139c..4959a7f61cd7c 100644 --- a/app/code/Magento/Vault/registration.php +++ b/app/code/Magento/Vault/registration.php @@ -4,6 +4,6 @@ * See COPYING.txt for license details. */ -use \Magento\Framework\Component\ComponentRegistrar; +use Magento\Framework\Component\ComponentRegistrar; ComponentRegistrar::register(ComponentRegistrar::MODULE, 'Magento_Vault', __DIR__); diff --git a/app/code/Magento/Vault/view/frontend/web/js/view/payment/method-renderer/vault.js b/app/code/Magento/Vault/view/frontend/web/js/view/payment/method-renderer/vault.js index 732bd4961f4fc..9a2e75f6c07d1 100644 --- a/app/code/Magento/Vault/view/frontend/web/js/view/payment/method-renderer/vault.js +++ b/app/code/Magento/Vault/view/frontend/web/js/view/payment/method-renderer/vault.js @@ -104,6 +104,24 @@ define( : false; }, + /** + * Return state of place order button. + * + * @return {Boolean} + */ + isButtonActive: function () { + return this.isActive() && this.isPlaceOrderActionAllowed(); + }, + + /** + * Check if payment is active. + * + * @return {Boolean} + */ + isActive: function () { + return this.isChecked() === this.getId(); + }, + /** * @returns {*} */ diff --git a/app/code/Magento/Vault/view/frontend/web/template/payment/form.html b/app/code/Magento/Vault/view/frontend/web/template/payment/form.html index 5f32281686a65..98c0fbfb00177 100644 --- a/app/code/Magento/Vault/view/frontend/web/template/payment/form.html +++ b/app/code/Magento/Vault/view/frontend/web/template/payment/form.html @@ -49,7 +49,10 @@ type="submit" data-bind=" click: placeOrder, - attr: {title: $t('Place Order')}"> + attr: {title: $t('Place Order')}, + enable: isButtonActive() + " + disabled> <span translate="'Place Order'"></span> </button> </div> diff --git a/app/code/Magento/VaultGraphQl/Model/Resolver/DeletePaymentToken.php b/app/code/Magento/VaultGraphQl/Model/Resolver/DeletePaymentToken.php index 8dc42cebe8dfc..146215059b365 100644 --- a/app/code/Magento/VaultGraphQl/Model/Resolver/DeletePaymentToken.php +++ b/app/code/Magento/VaultGraphQl/Model/Resolver/DeletePaymentToken.php @@ -9,7 +9,6 @@ use Magento\Framework\GraphQl\Config\Element\Field; use Magento\Framework\GraphQl\Exception\GraphQlAuthorizationException; -use Magento\Framework\GraphQl\Exception\GraphQlInputException; use Magento\Framework\GraphQl\Exception\GraphQlNoSuchEntityException; use Magento\Framework\GraphQl\Query\ResolverInterface; use Magento\Framework\GraphQl\Schema\Type\ResolveInfo; @@ -59,10 +58,6 @@ public function resolve( throw new GraphQlAuthorizationException(__('The current customer isn\'t authorized.')); } - if (!isset($args['public_hash'])) { - throw new GraphQlInputException(__('Specify the "public_hash" value.')); - } - $token = $this->paymentTokenManagement->getByPublicHash($args['public_hash'], $context->getUserId()); if (!$token) { throw new GraphQlNoSuchEntityException( diff --git a/app/code/Magento/VaultGraphQl/Test/Unit/Model/Resolver/DeletePaymentTokenTest.php b/app/code/Magento/VaultGraphQl/Test/Unit/Model/Resolver/DeletePaymentTokenTest.php new file mode 100644 index 0000000000000..0ec1a8b3907e7 --- /dev/null +++ b/app/code/Magento/VaultGraphQl/Test/Unit/Model/Resolver/DeletePaymentTokenTest.php @@ -0,0 +1,230 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ + +namespace Magento\VaultGraphQl\Test\Unit\Model\Resolver; + +use Magento\Framework\TestFramework\Unit\Helper\ObjectManager; +use Magento\Framework\GraphQl\Config\Element\Field; +use Magento\Framework\GraphQl\Exception\GraphQlNoSuchEntityException; +use Magento\Framework\GraphQl\Schema\Type\ResolveInfo; +use Magento\GraphQl\Model\Query\ContextInterface; +use Magento\GraphQl\Model\Query\ContextExtensionInterface; +use Magento\Vault\Api\PaymentTokenManagementInterface; +use Magento\Vault\Api\PaymentTokenRepositoryInterface; +use Magento\Vault\Api\Data\PaymentTokenInterface; +use Magento\VaultGraphQl\Model\Resolver\DeletePaymentToken; +use PHPUnit\Framework\MockObject\MockObject; +use PHPUnit\Framework\TestCase; + +/** + * Test class for \Magento\VaultGraphQl\Model\Resolver\DeletePaymentToken + */ +class DeletePaymentTokenTest extends TestCase +{ + /** + * Object Manager Instance + * + * @var ObjectManager + */ + private $objectManager; + + /** + * Testable Object + * + * @var DeletePaymentToken + */ + private $resolver; + + /** + * @var ContextInterface|MockObject + */ + private $contextMock; + + /** + * @var ContextExtensionInterface|MockObject + */ + private $contextExtensionMock; + + /** + * @var Field|MockObject + */ + private $fieldMock; + + /** + * @var PaymentTokenManagementInterface|MockObject + */ + private $paymentTokenManagementMock; + + /** + * @var PaymentTokenRepositoryInterface|MockObject + */ + private $paymentTokenRepositoryMock; + + /** + * @var PaymentTokenInterface|MockObject + */ + private $paymentTokenMock; + + /** + * @var ResolveInfo|MockObject + */ + private $resolveInfoMock; + + /** + * @inheritdoc + */ + protected function setUp() : void + { + $this->objectManager = new ObjectManager($this); + + $this->contextMock = $this->getMockBuilder(ContextInterface::class) + ->disableOriginalConstructor() + ->setMethods( + [ + 'getExtensionAttributes', + 'getUserId', + 'getUserType', + ] + ) + ->getMock(); + + $this->contextExtensionMock = $this->getMockBuilder(ContextExtensionInterface::class) + ->setMethods( + [ + 'getIsCustomer', + 'getStore', + 'setStore', + 'setIsCustomer', + ] + ) + ->getMock(); + + $this->fieldMock = $this->getMockBuilder(Field::class) + ->disableOriginalConstructor() + ->getMock(); + + $this->paymentTokenManagementMock = $this->getMockBuilder(PaymentTokenManagementInterface::class) + ->getMockForAbstractClass(); + + $this->paymentTokenRepositoryMock = $this->getMockBuilder(PaymentTokenRepositoryInterface::class) + ->getMockForAbstractClass(); + + $this->paymentTokenMock = $this->getMockBuilder(PaymentTokenInterface::class) + ->getMockForAbstractClass(); + + $this->resolveInfoMock = $this->getMockBuilder(ResolveInfo::class) + ->disableOriginalConstructor() + ->getMock(); + + $this->resolver = $this->objectManager->getObject( + DeletePaymentToken::class, + [ + 'paymentTokenManagement' => $this->paymentTokenManagementMock, + 'paymentTokenRepository' => $this->paymentTokenRepositoryMock, + ] + ); + } + + /** + * Test delete customer payment token + */ + public function testDeleteCustomerPaymentToken() + { + $isCustomer = true; + $paymentTokenResult = true; + + $this->contextMock + ->expects($this->once()) + ->method('getExtensionAttributes') + ->willReturn($this->contextExtensionMock); + + $this->contextExtensionMock + ->expects($this->once()) + ->method('getIsCustomer') + ->willReturn($isCustomer); + + $this->paymentTokenManagementMock + ->expects($this->once()) + ->method('getByPublicHash') + ->willReturn($this->paymentTokenMock); + + $this->paymentTokenRepositoryMock + ->expects($this->once()) + ->method('delete') + ->with($this->paymentTokenMock) + ->willReturn($paymentTokenResult); + + $this->assertEquals( + [ + 'result' => true + ], + $this->resolver->resolve( + $this->fieldMock, + $this->contextMock, + $this->resolveInfoMock + ) + ); + } + + /** + * Test mutation when customer isn't authorized. + * + * @expectedException \Magento\Framework\GraphQl\Exception\GraphQlAuthorizationException + * @expectedExceptionMessage The current customer isn't authorized. + */ + public function testCustomerNotAuthorized() + { + $isCustomer = false; + + $this->contextMock + ->expects($this->once()) + ->method('getExtensionAttributes') + ->willReturn($this->contextExtensionMock); + + $this->contextExtensionMock + ->expects($this->once()) + ->method('getIsCustomer') + ->willReturn($isCustomer); + + $this->resolver->resolve( + $this->fieldMock, + $this->contextMock, + $this->resolveInfoMock + ); + } + + /** + * Test mutation when provided token ID does not exist + */ + public function testCustomerPaymentTokenNotExists() + { + $isCustomer = true; + $token = false; + + $this->contextMock + ->expects($this->once()) + ->method('getExtensionAttributes') + ->willReturn($this->contextExtensionMock); + + $this->contextExtensionMock + ->expects($this->once()) + ->method('getIsCustomer') + ->willReturn($isCustomer); + + $this->paymentTokenManagementMock + ->expects($this->once()) + ->method('getByPublicHash') + ->willReturn($token); + + $this->expectException(GraphQlNoSuchEntityException::class); + + $this->resolver->resolve( + $this->fieldMock, + $this->contextMock, + $this->resolveInfoMock + ); + } +} diff --git a/app/code/Magento/Version/Controller/Index/Index.php b/app/code/Magento/Version/Controller/Index/Index.php index 0db9b5f80d483..2c874c601ea08 100644 --- a/app/code/Magento/Version/Controller/Index/Index.php +++ b/app/code/Magento/Version/Controller/Index/Index.php @@ -1,57 +1,61 @@ <?php /** - * * Copyright © Magento, Inc. All rights reserved. * See COPYING.txt for license details. */ +declare(strict_types=1); + namespace Magento\Version\Controller\Index; -use Magento\Framework\App\Action\HttpGetActionInterface as HttpGetActionInterface; -use Magento\Framework\App\Action\Action; -use Magento\Framework\App\Action\Context; +use Magento\Framework\App\Action\HttpGetActionInterface; use Magento\Framework\App\ProductMetadataInterface; +use Magento\Framework\Controller\Result\RawFactory as RawResponseFactory; /** - * Magento Version controller + * Magento Version controller: Sets the response body to ProductName/Major.MinorVersion (Edition). */ -class Index extends Action implements HttpGetActionInterface +class Index implements HttpGetActionInterface { + const DEV_PREFIX = 'dev-'; + /** * @var ProductMetadataInterface */ - protected $productMetadata; + private $productMetadata; + + /** + * @var RawResponseFactory + */ + private $rawFactory; /** - * @param Context $context + * @param RawResponseFactory $rawFactory * @param ProductMetadataInterface $productMetadata */ - public function __construct(Context $context, ProductMetadataInterface $productMetadata) + public function __construct(RawResponseFactory $rawFactory, ProductMetadataInterface $productMetadata) { + $this->rawFactory = $rawFactory; $this->productMetadata = $productMetadata; - parent::__construct($context); } /** - * Sets the response body to ProductName/Major.MinorVersion (Edition). E.g.: Magento/0.42 (Community). Omits patch - * version from response - * - * @return void + * @inheritDoc */ public function execute() { + $rawResponse = $this->rawFactory->create(); + $version = $this->productMetadata->getVersion(); $versionParts = explode('.', $version); - if ((!isset($versionParts[0]) || !isset($versionParts[1])) - || $this->isGitBasedInstallation($version) - ) { - return; + if (!$this->isGitBasedInstallation($version) && $this->isCorrectVersion($versionParts)) { + $rawResponse->setContents( + $this->productMetadata->getName() . '/' . + $this->getMajorMinorVersion($versionParts) . + ' (' . $this->productMetadata->getEdition() . ')' + ); } - $majorMinorVersion = $versionParts[0] . '.' . $versionParts[1]; - $this->getResponse()->setBody( - $this->productMetadata->getName() . '/' . - $majorMinorVersion . ' (' . - $this->productMetadata->getEdition() . ')' - ); + + return $rawResponse; } /** @@ -60,9 +64,30 @@ public function execute() * @param string $fullVersion * @return bool */ - private function isGitBasedInstallation($fullVersion) + private function isGitBasedInstallation($fullVersion): bool + { + return 0 === strpos($fullVersion, self::DEV_PREFIX); + } + + /** + * Verifies if the Magento version is correct + * + * @param array $versionParts + * @return bool + */ + private function isCorrectVersion(array $versionParts): bool + { + return isset($versionParts[0]) && isset($versionParts[1]); + } + + /** + * Returns string only with Major and Minor version number + * + * @param array $versionParts + * @return string + */ + private function getMajorMinorVersion(array $versionParts): string { - $versionParts = explode('-', $fullVersion); - return (isset($versionParts[0]) && $versionParts[0] == 'dev'); + return $versionParts[0] . '.' . $versionParts[1]; } } diff --git a/app/code/Magento/Version/Test/Unit/Controller/Index/IndexTest.php b/app/code/Magento/Version/Test/Unit/Controller/Index/IndexTest.php index 6f8daa9d8008d..69aed915bd513 100644 --- a/app/code/Magento/Version/Test/Unit/Controller/Index/IndexTest.php +++ b/app/code/Magento/Version/Test/Unit/Controller/Index/IndexTest.php @@ -7,91 +7,73 @@ namespace Magento\Version\Test\Unit\Controller\Index; -use Magento\Version\Controller\Index\Index as VersionIndex; -use Magento\Framework\App\Action\Context; use Magento\Framework\App\ProductMetadataInterface; use Magento\Framework\App\ResponseInterface; -use Magento\Framework\TestFramework\Unit\Helper\ObjectManager; +use Magento\Framework\Controller\Result\Raw; +use Magento\Framework\Controller\Result\RawFactory; +use Magento\Version\Controller\Index\Index as VersionIndex; +use PHPUnit\Framework\MockObject\MockObject; +use PHPUnit\Framework\TestCase; -/** - * Class \Magento\Version\Test\Unit\Controller\Index\IndexTest - */ -class IndexTest extends \PHPUnit\Framework\TestCase +class IndexTest extends TestCase { - /** - * @var VersionIndex - */ - private $model; + /** @var VersionIndex */ + private $versionController; - /** - * @var Context - */ - private $context; + /** @var MockObject|ProductMetadataInterface */ + private $productMetadataMock; - /** - * @var ProductMetadataInterface - */ - private $productMetadata; + /** @var MockObject|RawFactory */ + private $rawResponseFactoryMock; - /** - * @var ResponseInterface - */ - private $response; + /** @var MockObject|Raw */ + private $rawResponseMock; /** * Prepare test preconditions */ protected function setUp() { - $this->context = $this->getMockBuilder(Context::class) - ->disableOriginalConstructor() - ->getMock(); - - $this->productMetadata = $this->getMockBuilder(ProductMetadataInterface::class) + $this->productMetadataMock = $this->getMockBuilder(ProductMetadataInterface::class) ->disableOriginalConstructor() ->setMethods(['getName', 'getEdition', 'getVersion']) ->getMock(); - $this->response = $this->getMockBuilder(ResponseInterface::class) - ->disableOriginalConstructor() - ->setMethods(['setBody', 'sendResponse']) - ->getMock(); - - $this->context->expects($this->any()) - ->method('getResponse') - ->willReturn($this->response); + $this->rawResponseFactoryMock = $this->createPartialMock(RawFactory::class, ['create']); + $this->rawResponseMock = $this->createPartialMock(Raw::class, ['setContents']); + $this->rawResponseFactoryMock->method('create')->willReturn($this->rawResponseMock); - $helper = new ObjectManager($this); - - $this->model = $helper->getObject( - 'Magento\Version\Controller\Index\Index', - [ - 'context' => $this->context, - 'productMetadata' => $this->productMetadata - ] - ); + $this->versionController = new VersionIndex($this->rawResponseFactoryMock, $this->productMetadataMock); } /** - * Test with Git Base version + * Git Base version does not return information about version */ - public function testExecuteWithGitBase() + public function testGitBasedInstallationDoesNotReturnVersion(): void { - $this->productMetadata->expects($this->any())->method('getVersion')->willReturn('dev-2.3'); - $this->assertNull($this->model->execute()); + $this->productMetadataMock->expects($this->any()) + ->method('getVersion') + ->willReturn('dev-2.3'); + + $this->rawResponseMock->expects($this->never()) + ->method('setContents'); + + $this->versionController->execute(); } /** - * Test with Community Version + * Magento Community returns information about major and minor version of product */ - public function testExecuteWithCommunityVersion() + public function testCommunityVersionDisplaysMajorMinorVersionAndEditionName(): void { - $this->productMetadata->expects($this->any())->method('getVersion')->willReturn('2.3.3'); - $this->productMetadata->expects($this->any())->method('getEdition')->willReturn('Community'); - $this->productMetadata->expects($this->any())->method('getName')->willReturn('Magento'); - $this->response->expects($this->once())->method('setBody') + $this->productMetadataMock->expects($this->any())->method('getVersion')->willReturn('2.3.3'); + $this->productMetadataMock->expects($this->any())->method('getEdition')->willReturn('Community'); + $this->productMetadataMock->expects($this->any())->method('getName')->willReturn('Magento'); + + $this->rawResponseMock->expects($this->once())->method('setContents') ->with('Magento/2.3 (Community)') ->will($this->returnSelf()); - $this->model->execute(); + + $this->versionController->execute(); } } diff --git a/app/code/Magento/Version/etc/frontend/routes.xml b/app/code/Magento/Version/etc/frontend/routes.xml index a3988030478ff..3550a0c6a15f5 100644 --- a/app/code/Magento/Version/etc/frontend/routes.xml +++ b/app/code/Magento/Version/etc/frontend/routes.xml @@ -8,7 +8,7 @@ <config xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="urn:magento:framework:App/etc/routes.xsd"> <router id="standard"> <route id="magento_version" frontName="magento_version"> - <module name="Magento_Version" /> + <module name="Magento_Version"/> </route> </router> </config> diff --git a/app/code/Magento/Version/etc/module.xml b/app/code/Magento/Version/etc/module.xml index fe8ace51ea07d..b21ef687e164d 100644 --- a/app/code/Magento/Version/etc/module.xml +++ b/app/code/Magento/Version/etc/module.xml @@ -6,6 +6,6 @@ */ --> <config xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="urn:magento:framework:Module/etc/module.xsd"> - <module name="Magento_Version" > + <module name="Magento_Version"> </module> </config> diff --git a/app/code/Magento/Version/registration.php b/app/code/Magento/Version/registration.php index 24daee14ec3da..e2a8d758ae15b 100644 --- a/app/code/Magento/Version/registration.php +++ b/app/code/Magento/Version/registration.php @@ -4,6 +4,6 @@ * See COPYING.txt for license details. */ -use \Magento\Framework\Component\ComponentRegistrar; +use Magento\Framework\Component\ComponentRegistrar; ComponentRegistrar::register(ComponentRegistrar::MODULE, 'Magento_Version', __DIR__); diff --git a/app/code/Magento/Webapi/Controller/Rest/InputParamsResolver.php b/app/code/Magento/Webapi/Controller/Rest/InputParamsResolver.php index 07d1b4e07fe9d..723e274d1e5fa 100644 --- a/app/code/Magento/Webapi/Controller/Rest/InputParamsResolver.php +++ b/app/code/Magento/Webapi/Controller/Rest/InputParamsResolver.php @@ -8,7 +8,6 @@ use Magento\Framework\Webapi\ServiceInputProcessor; use Magento\Framework\Webapi\Rest\Request as RestRequest; -use Magento\Webapi\Controller\Rest\Router; use Magento\Webapi\Controller\Rest\Router\Route; /** @@ -81,7 +80,20 @@ public function resolve() $route = $this->getRoute(); $serviceMethodName = $route->getServiceMethod(); $serviceClassName = $route->getServiceClass(); + $inputData = $this->getInputData(); + return $this->serviceInputProcessor->process($serviceClassName, $serviceMethodName, $inputData); + } + /** + * Get API input data + * + * @return array + */ + public function getInputData() + { + $route = $this->getRoute(); + $serviceMethodName = $route->getServiceMethod(); + $serviceClassName = $route->getServiceClass(); /* * Valid only for updates using PUT when passing id value both in URL and body */ @@ -97,9 +109,7 @@ public function resolve() $inputData = $this->request->getRequestData(); } - $inputData = $this->paramsOverrider->override($inputData, $route->getParameters()); - $inputParams = $this->serviceInputProcessor->process($serviceClassName, $serviceMethodName, $inputData); - return $inputParams; + return $this->paramsOverrider->override($inputData, $route->getParameters()); } /** diff --git a/app/code/Magento/Webapi/Model/Config/ClassReflector.php b/app/code/Magento/Webapi/Model/Config/ClassReflector.php index b73e4e0afb585..be880a0767bba 100644 --- a/app/code/Magento/Webapi/Model/Config/ClassReflector.php +++ b/app/code/Magento/Webapi/Model/Config/ClassReflector.php @@ -5,24 +5,24 @@ */ namespace Magento\Webapi\Model\Config; -use Zend\Code\Reflection\MethodReflection; +use Laminas\Code\Reflection\ClassReflection; +use Laminas\Code\Reflection\MethodReflection; +use Magento\Framework\Reflection\TypeProcessor; /** - * Class reflector. + * Config class reflector */ class ClassReflector { /** - * @var \Magento\Framework\Reflection\TypeProcessor + * @var TypeProcessor */ protected $_typeProcessor; /** - * Construct reflector. - * - * @param \Magento\Framework\Reflection\TypeProcessor $typeProcessor + * @param TypeProcessor $typeProcessor */ - public function __construct(\Magento\Framework\Reflection\TypeProcessor $typeProcessor) + public function __construct(TypeProcessor $typeProcessor) { $this->_typeProcessor = $typeProcessor; } @@ -60,12 +60,13 @@ public function __construct(\Magento\Framework\Reflection\TypeProcessor $typePro * ), * ... * )</pre> + * @throws \ReflectionException */ public function reflectClassMethods($className, $methods) { $data = []; - $classReflection = new \Zend\Code\Reflection\ClassReflection($className); - /** @var \Zend\Code\Reflection\MethodReflection $methodReflection */ + $classReflection = new ClassReflection($className); + /** @var MethodReflection $methodReflection */ foreach ($classReflection->getMethods() as $methodReflection) { $methodName = $methodReflection->getName(); if (in_array($methodName, $methods) || array_key_exists($methodName, $methods)) { @@ -78,14 +79,15 @@ public function reflectClassMethods($className, $methods) /** * Retrieve method interface and documentation description. * - * @param \Zend\Code\Reflection\MethodReflection $method + * @param MethodReflection $method * @return array * @throws \InvalidArgumentException + * @throws \ReflectionException */ - public function extractMethodData(\Zend\Code\Reflection\MethodReflection $method) + public function extractMethodData(MethodReflection $method) { $methodData = ['documentation' => $this->extractMethodDescription($method), 'interface' => []]; - /** @var \Zend\Code\Reflection\ParameterReflection $parameter */ + /** @var \Laminas\Code\Reflection\ParameterReflection $parameter */ foreach ($method->getParameters() as $parameter) { $parameterData = [ 'type' => $this->_typeProcessor->register($this->_typeProcessor->getParamType($parameter)), @@ -116,10 +118,11 @@ public function extractMethodData(\Zend\Code\Reflection\MethodReflection $method /** * Retrieve method full documentation description. * - * @param \Zend\Code\Reflection\MethodReflection $method + * @param MethodReflection $method * @return string + * @throws \ReflectionException */ - protected function extractMethodDescription(\Zend\Code\Reflection\MethodReflection $method) + protected function extractMethodDescription(MethodReflection $method) { $methodReflection = new MethodReflection( $method->getDeclaringClass()->getName(), @@ -141,10 +144,11 @@ protected function extractMethodDescription(\Zend\Code\Reflection\MethodReflecti * * @param string $className * @return string + * @throws \ReflectionException */ public function extractClassDescription($className) { - $classReflection = new \Zend\Code\Reflection\ClassReflection($className); + $classReflection = new ClassReflection($className); $docBlock = $classReflection->getDocBlock(); if (!$docBlock) { return ''; diff --git a/app/code/Magento/Webapi/Model/Soap/Wsdl.php b/app/code/Magento/Webapi/Model/Soap/Wsdl.php index 2d0b310995215..14e9990d25c3e 100644 --- a/app/code/Magento/Webapi/Model/Soap/Wsdl.php +++ b/app/code/Magento/Webapi/Model/Soap/Wsdl.php @@ -11,15 +11,15 @@ /** * Magento-specific WSDL builder. */ -class Wsdl extends \Zend\Soap\Wsdl +class Wsdl extends \Laminas\Soap\Wsdl { /** - * Constructor. * Save URI for targetNamespace generation. * * @param string $name - * @param string|\Zend\Uri\Uri $uri + * @param string|\Laminas\Uri\Uri $uri * @param ComplexTypeStrategy $strategy + * phpcs:disable Generic.CodeAnalysis.UselessOverridingMethod */ public function __construct($name, $uri, ComplexTypeStrategy $strategy) { diff --git a/app/code/Magento/Webapi/Model/Soap/Wsdl/ComplexTypeStrategy.php b/app/code/Magento/Webapi/Model/Soap/Wsdl/ComplexTypeStrategy.php index 3884a0ef026e1..93a0cf9d835c7 100644 --- a/app/code/Magento/Webapi/Model/Soap/Wsdl/ComplexTypeStrategy.php +++ b/app/code/Magento/Webapi/Model/Soap/Wsdl/ComplexTypeStrategy.php @@ -5,8 +5,8 @@ */ namespace Magento\Webapi\Model\Soap\Wsdl; -use Zend\Soap\Wsdl; -use Zend\Soap\Wsdl\ComplexTypeStrategy\AbstractComplexTypeStrategy; +use Laminas\Soap\Wsdl; +use Laminas\Soap\Wsdl\ComplexTypeStrategy\AbstractComplexTypeStrategy; /** * Magento-specific Complex type strategy for WSDL auto discovery. @@ -184,7 +184,7 @@ protected function _processArrayParameter($type, $callInfo = []) * Revert required call info data if needed. * * @param bool $isRequired - * @param array &$callInfo + * @param array $callInfo * @return void */ protected function _revertRequiredCallInfo($isRequired, &$callInfo) @@ -236,19 +236,7 @@ public function addAnnotation(\DOMElement $element, $documentation, $default = n $tagValue = $matches[2][$i]; switch ($tagName) { case 'callInfo': - $callInfoRegExp = '/([a-z].+):(returned|requiredInput):(yes|no|always|conditionally)/i'; - if (preg_match($callInfoRegExp, $tagValue)) { - list($callName, $direction, $condition) = explode(':', $tagValue); - $condition = strtolower($condition); - if (preg_match('/allCallsExcept\(([a-zA-Z].+)\)/', $callName, $calls)) { - $callInfo[$direction][$condition] = [ - 'allCallsExcept' => $calls[1], - ]; - } elseif (!isset($callInfo[$direction][$condition]['allCallsExcept'])) { - $this->_overrideCallInfoName($callInfo, $callName); - $callInfo[$direction][$condition]['calls'][] = $callName; - } - } + $this->processCallInfo($callInfo, $tagValue); break; case 'seeLink': $this->_processSeeLink($appInfoNode, $tagValue); @@ -451,4 +439,27 @@ protected function _overrideCallInfoName(&$callInfo, $callName) } } } + + /** + * Process CallInfo data + * + * @param array $callInfo + * @param string $tagValue + */ + private function processCallInfo(array &$callInfo, string $tagValue): void + { + $callInfoRegExp = '/([a-z].+):(returned|requiredInput):(yes|no|always|conditionally)/i'; + if (preg_match($callInfoRegExp, $tagValue)) { + list($callName, $direction, $condition) = explode(':', $tagValue); + $condition = strtolower($condition); + if (preg_match('/allCallsExcept\(([a-zA-Z].+)\)/', $callName, $calls)) { + $callInfo[$direction][$condition] = [ + 'allCallsExcept' => $calls[1], + ]; + } elseif (!isset($callInfo[$direction][$condition]['allCallsExcept'])) { + $this->_overrideCallInfoName($callInfo, $callName); + $callInfo[$direction][$condition]['calls'][] = $callName; + } + } + } } diff --git a/app/code/Magento/Webapi/Test/Unit/Controller/SoapTest.php b/app/code/Magento/Webapi/Test/Unit/Controller/SoapTest.php index 9b42d3c9c3e3a..576353f3636a4 100644 --- a/app/code/Magento/Webapi/Test/Unit/Controller/SoapTest.php +++ b/app/code/Magento/Webapi/Test/Unit/Controller/SoapTest.php @@ -100,7 +100,7 @@ protected function setUp() $this->_responseMock ->expects($this->any()) ->method('getHeaders') - ->will($this->returnValue(new \Zend\Http\Headers())); + ->will($this->returnValue(new \Laminas\Http\Headers())); $appconfig = $this->createMock(\Magento\Framework\App\Config::class); $objectManagerHelper->setBackwardCompatibleProperty( diff --git a/app/code/Magento/Webapi/Test/Unit/Model/Config/ClassReflectorTest.php b/app/code/Magento/Webapi/Test/Unit/Model/Config/ClassReflectorTest.php index b597b838a3512..8c77c65135a61 100644 --- a/app/code/Magento/Webapi/Test/Unit/Model/Config/ClassReflectorTest.php +++ b/app/code/Magento/Webapi/Test/Unit/Model/Config/ClassReflectorTest.php @@ -46,10 +46,10 @@ public function testReflectClassMethods() public function testExtractMethodData() { - $classReflection = new \Zend\Code\Reflection\ClassReflection( + $classReflection = new \Laminas\Code\Reflection\ClassReflection( \Magento\Webapi\Test\Unit\Model\Config\TestServiceForClassReflector::class ); - /** @var $methodReflection \Zend\Code\Reflection\MethodReflection */ + /** @var $methodReflection \Laminas\Code\Reflection\MethodReflection */ $methodReflection = $classReflection->getMethods()[0]; $methodData = $this->_classReflector->extractMethodData($methodReflection); $expectedResponse = $this->_getSampleReflectionData(); diff --git a/app/code/Magento/Webapi/Test/Unit/Model/Soap/Wsdl/ComplexTypeStrategyTest.php b/app/code/Magento/Webapi/Test/Unit/Model/Soap/Wsdl/ComplexTypeStrategyTest.php index 93d65d545408d..4ff37b3c15b0d 100644 --- a/app/code/Magento/Webapi/Test/Unit/Model/Soap/Wsdl/ComplexTypeStrategyTest.php +++ b/app/code/Magento/Webapi/Test/Unit/Model/Soap/Wsdl/ComplexTypeStrategyTest.php @@ -7,7 +7,7 @@ use \Magento\Webapi\Model\Soap\Wsdl\ComplexTypeStrategy; -use Zend\Soap\Wsdl; +use Laminas\Soap\Wsdl; /** * Complex type strategy tests. diff --git a/app/code/Magento/Webapi/registration.php b/app/code/Magento/Webapi/registration.php index 1ce2579e4e7c8..1836a29955e86 100644 --- a/app/code/Magento/Webapi/registration.php +++ b/app/code/Magento/Webapi/registration.php @@ -4,6 +4,6 @@ * See COPYING.txt for license details. */ -use \Magento\Framework\Component\ComponentRegistrar; +use Magento\Framework\Component\ComponentRegistrar; ComponentRegistrar::register(ComponentRegistrar::MODULE, 'Magento_Webapi', __DIR__); diff --git a/app/code/Magento/WebapiAsync/Controller/Rest/Asynchronous/InputParamsResolver.php b/app/code/Magento/WebapiAsync/Controller/Rest/Asynchronous/InputParamsResolver.php index 93bddd09faef8..064bd99b9b6bf 100644 --- a/app/code/Magento/WebapiAsync/Controller/Rest/Asynchronous/InputParamsResolver.php +++ b/app/code/Magento/WebapiAsync/Controller/Rest/Asynchronous/InputParamsResolver.php @@ -8,12 +8,12 @@ namespace Magento\WebapiAsync\Controller\Rest\Asynchronous; -use Magento\Framework\Webapi\ServiceInputProcessor; use Magento\Framework\Webapi\Rest\Request as RestRequest; -use Magento\Webapi\Controller\Rest\Router; +use Magento\Framework\Webapi\ServiceInputProcessor; +use Magento\Webapi\Controller\Rest\InputParamsResolver as WebapiInputParamsResolver; use Magento\Webapi\Controller\Rest\ParamsOverrider; use Magento\Webapi\Controller\Rest\RequestValidator; -use Magento\Webapi\Controller\Rest\InputParamsResolver as WebapiInputParamsResolver; +use Magento\Webapi\Controller\Rest\Router; /** * This class is responsible for retrieving resolved input data @@ -96,6 +96,22 @@ public function resolve() } $this->requestValidator->validate(); $webapiResolvedParams = []; + foreach ($this->getInputData() as $key => $singleEntityParams) { + $webapiResolvedParams[$key] = $this->resolveBulkItemParams($singleEntityParams); + } + return $webapiResolvedParams; + } + + /** + * Get API input data + * + * @return array + */ + public function getInputData() + { + if ($this->isBulk === false) { + return [$this->inputParamsResolver->getInputData()]; + } $inputData = $this->request->getRequestData(); $httpMethod = $this->request->getHttpMethod(); @@ -103,12 +119,7 @@ public function resolve() $requestBodyParams = $this->request->getBodyParams(); $inputData = array_merge($requestBodyParams, $inputData); } - - foreach ($inputData as $key => $singleEntityParams) { - $webapiResolvedParams[$key] = $this->resolveBulkItemParams($singleEntityParams); - } - - return $webapiResolvedParams; + return $inputData; } /** diff --git a/app/code/Magento/WebapiAsync/Model/Config.php b/app/code/Magento/WebapiAsync/Model/Config.php index 16c24643ba355..7329862ca528c 100644 --- a/app/code/Magento/WebapiAsync/Model/Config.php +++ b/app/code/Magento/WebapiAsync/Model/Config.php @@ -3,35 +3,34 @@ * Copyright © Magento, Inc. All rights reserved. * See COPYING.txt for license details. */ - declare(strict_types=1); namespace Magento\WebapiAsync\Model; +use Magento\AsynchronousOperations\Model\ConfigInterface; +use Magento\Framework\Exception\LocalizedException; +use Magento\Framework\Serialize\SerializerInterface; use Magento\Webapi\Model\Cache\Type\Webapi as WebapiCache; use Magento\Webapi\Model\Config as WebapiConfig; -use Magento\Framework\App\ObjectManager; -use Magento\Framework\Serialize\SerializerInterface; -use Magento\Framework\Exception\LocalizedException; use Magento\Webapi\Model\Config\Converter; /** * Class for accessing to Webapi_Async configuration. */ -class Config implements \Magento\AsynchronousOperations\Model\ConfigInterface +class Config implements ConfigInterface { /** - * @var \Magento\Webapi\Model\Cache\Type\Webapi + * @var WebapiCache */ private $cache; /** - * @var \Magento\Webapi\Model\Config + * @var WebapiConfig */ private $webApiConfig; /** - * @var \Magento\Framework\Serialize\SerializerInterface + * @var SerializerInterface */ private $serializer; @@ -43,18 +42,18 @@ class Config implements \Magento\AsynchronousOperations\Model\ConfigInterface /** * Initialize dependencies. * - * @param \Magento\Webapi\Model\Cache\Type\Webapi $cache - * @param \Magento\Webapi\Model\Config $webApiConfig - * @param \Magento\Framework\Serialize\SerializerInterface|null $serializer + * @param WebapiCache $cache + * @param WebapiConfig $webApiConfig + * @param SerializerInterface $serializer */ public function __construct( WebapiCache $cache, WebapiConfig $webApiConfig, - SerializerInterface $serializer = null + SerializerInterface $serializer ) { $this->cache = $cache; $this->webApiConfig = $webApiConfig; - $this->serializer = $serializer ? : ObjectManager::getInstance()->get(SerializerInterface::class); + $this->serializer = $serializer; } /** @@ -179,7 +178,7 @@ private function generateTopicNameFromService($serviceInterface, $serviceMethod, */ private function generateKey($typeName, $methodName, $delimiter = '\\', $lcfirst = true) { - $parts = explode($delimiter, ltrim($typeName, $delimiter)); + $parts = explode($delimiter, trim($typeName, $delimiter)); foreach ($parts as &$part) { $part = ltrim($part, ':'); if ($lcfirst === true) { diff --git a/app/code/Magento/WebapiAsync/Model/OperationRepository.php b/app/code/Magento/WebapiAsync/Model/OperationRepository.php new file mode 100644 index 0000000000000..7af8ff877ebbc --- /dev/null +++ b/app/code/Magento/WebapiAsync/Model/OperationRepository.php @@ -0,0 +1,102 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +declare(strict_types=1); + +namespace Magento\WebapiAsync\Model; + +use Magento\AsynchronousOperations\Api\Data\OperationInterface; +use Magento\AsynchronousOperations\Api\Data\OperationInterfaceFactory; +use Magento\AsynchronousOperations\Model\OperationRepositoryInterface; +use Magento\Framework\MessageQueue\MessageValidator; +use Magento\Framework\Serialize\Serializer\Json; +use Magento\Framework\EntityManager\EntityManager; +use Magento\WebapiAsync\Controller\Rest\Asynchronous\InputParamsResolver; + +/** + * Repository class to create operation + */ +class OperationRepository implements OperationRepositoryInterface +{ + /** + * @var OperationInterfaceFactory + */ + private $operationFactory; + + /** + * @var Json + */ + private $jsonSerializer; + + /** + * @var EntityManager + */ + private $entityManager; + + /** + * @var MessageValidator + */ + private $messageValidator; + /** + * @var InputParamsResolver + */ + private $inputParamsResolver; + + /** + * Initialize dependencies. + * + * @param OperationInterfaceFactory $operationFactory + * @param EntityManager $entityManager + * @param MessageValidator $messageValidator + * @param Json $jsonSerializer + * @param InputParamsResolver $inputParamsResolver + */ + public function __construct( + OperationInterfaceFactory $operationFactory, + EntityManager $entityManager, + MessageValidator $messageValidator, + Json $jsonSerializer, + InputParamsResolver $inputParamsResolver + ) { + $this->operationFactory = $operationFactory; + $this->jsonSerializer = $jsonSerializer; + $this->messageValidator = $messageValidator; + $this->entityManager = $entityManager; + $this->inputParamsResolver = $inputParamsResolver; + } + + /** + * @inheritDoc + */ + public function create($topicName, $entityParams, $groupId, $operationId): OperationInterface + { + $this->messageValidator->validate($topicName, $entityParams); + $requestData = $this->inputParamsResolver->getInputData(); + if ($operationId === null || !isset($requestData[$operationId])) { + throw new \InvalidArgumentException( + 'Parameter "$operationId" must not be NULL and must exist in input data' + ); + } + $encodedMessage = $this->jsonSerializer->serialize($requestData[$operationId]); + + $serializedData = [ + 'entity_id' => null, + 'entity_link' => '', + 'meta_information' => $encodedMessage, + ]; + $data = [ + 'data' => [ + OperationInterface::BULK_ID => $groupId, + OperationInterface::TOPIC_NAME => $topicName, + OperationInterface::SERIALIZED_DATA => $this->jsonSerializer->serialize($serializedData), + OperationInterface::STATUS => OperationInterface::STATUS_TYPE_OPEN, + ], + ]; + + /** @var OperationInterface $operation */ + $operation = $this->operationFactory->create($data); + return $operation; + } +} diff --git a/app/code/Magento/WebapiAsync/etc/di.xml b/app/code/Magento/WebapiAsync/etc/di.xml index 7411ec0561d24..cfe1a5dbae53f 100644 --- a/app/code/Magento/WebapiAsync/etc/di.xml +++ b/app/code/Magento/WebapiAsync/etc/di.xml @@ -34,10 +34,31 @@ <argument name="isBulk" xsi:type="boolean">true</argument> </arguments> </virtualType> + <virtualType name="Magento\WebapiAsync\Model\Bulk\OperationRepository" type="Magento\WebapiAsync\Model\OperationRepository"> + <arguments> + <argument name="inputParamsResolver" xsi:type="object">Magento\WebapiAsync\Controller\VirtualType\InputParamsResolver</argument> + </arguments> + </virtualType> + <virtualType name="Magento\WebapiAsync\Model\MassSchedule" type="Magento\AsynchronousOperations\Model\MassSchedule"> + <arguments> + <argument name="operationRepository" xsi:type="object">Magento\WebapiAsync\Model\OperationRepository</argument> + </arguments> + </virtualType> + <virtualType name="Magento\WebapiAsync\Model\Bulk\MassSchedule" type="Magento\AsynchronousOperations\Model\MassSchedule"> + <arguments> + <argument name="operationRepository" xsi:type="object">Magento\WebapiAsync\Model\Bulk\OperationRepository</argument> + </arguments> + </virtualType> + <type name="Magento\WebapiAsync\Controller\Rest\AsynchronousRequestProcessor"> + <arguments> + <argument name="asyncBulkPublisher" xsi:type="object">Magento\WebapiAsync\Model\MassSchedule</argument> + </arguments> + </type> <virtualType name="Magento\WebapiAsync\Controller\Rest\VirtualType\AsynchronousBulkRequestProcessor" type="Magento\WebapiAsync\Controller\Rest\AsynchronousRequestProcessor"> <arguments> <argument name="inputParamsResolver" xsi:type="object">Magento\WebapiAsync\Controller\VirtualType\InputParamsResolver</argument> <argument name="processorPath" xsi:type="const">Magento\WebapiAsync\Controller\Rest\AsynchronousRequestProcessor::BULK_PROCESSOR_PATH</argument> + <argument name="asyncBulkPublisher" xsi:type="object">Magento\WebapiAsync\Model\Bulk\MassSchedule</argument> </arguments> </virtualType> <virtualType name="Magento\WebapiAsync\Controller\Rest\VirtualType\AsynchronousBulkSchemaRequestProcessor" type="Magento\WebapiAsync\Controller\Rest\AsynchronousSchemaRequestProcessor"> diff --git a/app/code/Magento/WebapiSecurity/etc/adminhtml/system.xml b/app/code/Magento/WebapiSecurity/etc/adminhtml/system.xml index d6f40f5ac2023..ea60d34f78b0f 100644 --- a/app/code/Magento/WebapiSecurity/etc/adminhtml/system.xml +++ b/app/code/Magento/WebapiSecurity/etc/adminhtml/system.xml @@ -6,9 +6,9 @@ <config xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="urn:magento:module:Magento_Config:etc/system_file.xsd"> <system> <section id="webapi" type="text" sortOrder="102" showInDefault="1" showInWebsite="1" showInStore="1"> - <group id="webapisecurity" translate="label" type="text" sortOrder="250" showInDefault="1" showInWebsite="0" showInStore="0"> + <group id="webapisecurity" translate="label" type="text" sortOrder="250" showInDefault="1"> <label>Web API Security</label> - <field id="allow_insecure" translate="label comment" type="select" sortOrder="1" showInDefault="1" showInWebsite="0" showInStore="0"> + <field id="allow_insecure" translate="label comment" type="select" sortOrder="1" showInDefault="1"> <label>Allow Anonymous Guest Access</label> <source_model>Magento\Config\Model\Config\Source\Yesno</source_model> <comment>This feature applies only to CMS, Catalog and Store APIs. Please consult your developers for details on potential security risks.</comment> diff --git a/app/code/Magento/WebapiSecurity/registration.php b/app/code/Magento/WebapiSecurity/registration.php index a25eea9deb660..fc6d8e76c9754 100644 --- a/app/code/Magento/WebapiSecurity/registration.php +++ b/app/code/Magento/WebapiSecurity/registration.php @@ -4,6 +4,6 @@ * See COPYING.txt for license details. */ -use \Magento\Framework\Component\ComponentRegistrar; +use Magento\Framework\Component\ComponentRegistrar; ComponentRegistrar::register(ComponentRegistrar::MODULE, 'Magento_WebapiSecurity', __DIR__); diff --git a/app/code/Magento/Weee/Model/App/Action/ContextPlugin.php b/app/code/Magento/Weee/Model/App/Action/ContextPlugin.php index aae6f769eb500..66485d0410872 100644 --- a/app/code/Magento/Weee/Model/App/Action/ContextPlugin.php +++ b/app/code/Magento/Weee/Model/App/Action/ContextPlugin.php @@ -6,78 +6,92 @@ namespace Magento\Weee\Model\App\Action; +use Magento\Customer\Model\Session as CustomerSession; +use Magento\Framework\App\ActionInterface; +use Magento\Framework\App\Config\ScopeConfigInterface; +use Magento\Framework\App\Http\Context as HttpContext; +use Magento\Framework\Module\Manager as ModuleManager; +use Magento\PageCache\Model\Config as PageCacheConfig; +use Magento\Store\Model\ScopeInterface; +use Magento\Store\Model\StoreManagerInterface; +use Magento\Tax\Helper\Data as TaxHelper; +use Magento\Tax\Model\Config as TaxConfig; +use Magento\Weee\Helper\Data as WeeeHelper; +use Magento\Weee\Model\Tax; + /** - * Class ContextPlugin + * Plugin to provide Context information to Weee Action + * * @SuppressWarnings(PHPMD.CouplingBetweenObjects) */ class ContextPlugin { /** - * @var \Magento\Customer\Model\Session + * @var CustomerSession */ protected $customerSession; /** - * @var \Magento\Framework\App\Http\Context + * @var HttpContext */ protected $httpContext; /** - * @var \Magento\Tax\Helper\Data + * @var TaxHelper */ protected $taxHelper; /** - * @var \Magento\Weee\Helper\Data + * @var WeeeHelper */ protected $weeeHelper; /** - * @var \Magento\Framework\Module\Manager + * @var ModuleManager */ protected $moduleManager; /** - * @var \Magento\Weee\Model\Tax + * @var Tax */ protected $weeeTax; /** - * @var \Magento\PageCache\Model\Config + * @var PageCacheConfig */ protected $cacheConfig; /** - * @var \Magento\Store\Model\StoreManagerInterface + * @var StoreManagerInterface */ protected $storeManager; /** - * @var \Magento\Framework\App\Config\ScopeConfigInterface + * @var ScopeConfigInterface */ protected $scopeConfig; /** - * @param \Magento\Customer\Model\Session $customerSession - * @param \Magento\Framework\App\Http\Context $httpContext - * @param \Magento\Weee\Model\Tax $weeeTax - * @param \Magento\Tax\Helper\Data $taxHelper - * @param \Magento\Weee\Helper\Data $weeeHelper - * @param \Magento\Framework\Module\Manager $moduleManager - * @param \Magento\PageCache\Model\Config $cacheConfig - * @param \Magento\Store\Model\StoreManagerInterface $storeManager - * @param \Magento\Framework\App\Config\ScopeConfigInterface $scopeConfig + * @param CustomerSession $customerSession + * @param HttpContext $httpContext + * @param Tax $weeeTax + * @param TaxHelper $taxHelper + * @param WeeeHelper $weeeHelper + * @param ModuleManager $moduleManager + * @param PageCacheConfig $cacheConfig + * @param StoreManagerInterface $storeManager + * @param ScopeConfigInterface $scopeConfig */ public function __construct( - \Magento\Customer\Model\Session $customerSession, - \Magento\Framework\App\Http\Context $httpContext, - \Magento\Weee\Model\Tax $weeeTax, - \Magento\Tax\Helper\Data $taxHelper, - \Magento\Weee\Helper\Data $weeeHelper, - \Magento\Framework\Module\Manager $moduleManager, - \Magento\PageCache\Model\Config $cacheConfig, - \Magento\Store\Model\StoreManagerInterface $storeManager, - \Magento\Framework\App\Config\ScopeConfigInterface $scopeConfig + CustomerSession $customerSession, + HttpContext $httpContext, + Tax $weeeTax, + TaxHelper $taxHelper, + WeeeHelper $weeeHelper, + ModuleManager $moduleManager, + PageCacheConfig $cacheConfig, + StoreManagerInterface $storeManager, + ScopeConfigInterface $scopeConfig ) { $this->customerSession = $customerSession; $this->httpContext = $httpContext; @@ -93,18 +107,15 @@ public function __construct( /** * Before dispatch. * - * @param \Magento\Framework\App\ActionInterface $subject - * @param \Magento\Framework\App\RequestInterface $request + * @param ActionInterface $subject * @return void * @SuppressWarnings(PHPMD.UnusedFormalParameter) * @SuppressWarnings(PHPMD.CyclomaticComplexity) * @SuppressWarnings(PHPMD.NPathComplexity) * @SuppressWarnings(PHPMD.ExcessiveMethodLength) */ - public function beforeDispatch( - \Magento\Framework\App\ActionInterface $subject, - \Magento\Framework\App\RequestInterface $request - ) { + public function beforeExecute(ActionInterface $subject) + { if (!$this->weeeHelper->isEnabled() || !$this->customerSession->isLoggedIn() || !$this->moduleManager->isEnabled('Magento_PageCache') || @@ -128,26 +139,14 @@ public function beforeDispatch( } elseif ($countryId && !$regionId) { // country exist and region does not exist $regionId = 0; - $exist = $this->weeeTax->isWeeeInLocation( - $countryId, - $regionId, - $websiteId - ); + $exist = $this->weeeTax->isWeeeInLocation($countryId, $regionId, $websiteId); } else { // country and region exist - $exist = $this->weeeTax->isWeeeInLocation( - $countryId, - $regionId, - $websiteId - ); + $exist = $this->weeeTax->isWeeeInLocation($countryId, $regionId, $websiteId); if (!$exist) { // just check the country for weee $regionId = 0; - $exist = $this->weeeTax->isWeeeInLocation( - $countryId, - $regionId, - $websiteId - ); + $exist = $this->weeeTax->isWeeeInLocation($countryId, $regionId, $websiteId); } } @@ -171,13 +170,13 @@ protected function getWeeeTaxRegion($basedOn) $countryId = null; $regionId = null; $defaultCountryId = $this->scopeConfig->getValue( - \Magento\Tax\Model\Config::CONFIG_XML_PATH_DEFAULT_COUNTRY, - \Magento\Store\Model\ScopeInterface::SCOPE_STORE, + TaxConfig::CONFIG_XML_PATH_DEFAULT_COUNTRY, + ScopeInterface::SCOPE_STORE, null ); $defaultRegionId = $this->scopeConfig->getValue( - \Magento\Tax\Model\Config::CONFIG_XML_PATH_DEFAULT_REGION, - \Magento\Store\Model\ScopeInterface::SCOPE_STORE, + TaxConfig::CONFIG_XML_PATH_DEFAULT_REGION, + ScopeInterface::SCOPE_STORE, null ); diff --git a/app/code/Magento/Weee/Model/Total/Quote/Weee.php b/app/code/Magento/Weee/Model/Total/Quote/Weee.php index 96a40c9533f8f..449c6cd688668 100644 --- a/app/code/Magento/Weee/Model/Total/Quote/Weee.php +++ b/app/code/Magento/Weee/Model/Total/Quote/Weee.php @@ -7,10 +7,19 @@ namespace Magento\Weee\Model\Total\Quote; use Magento\Framework\Pricing\PriceCurrencyInterface; +use Magento\Quote\Api\Data\ShippingAssignmentInterface; +use Magento\Quote\Model\Quote; +use Magento\Quote\Model\Quote\Address; +use Magento\Quote\Model\Quote\Address\Total; use Magento\Quote\Model\Quote\Address\Total\AbstractTotal; +use Magento\Quote\Model\Quote\Item\AbstractItem; use Magento\Store\Model\Store; use Magento\Tax\Model\Sales\Total\Quote\CommonTaxCollector; +use Magento\Weee\Helper\Data as WeeHelper; +/** + * Calculate totals for Quote + */ class Weee extends AbstractTotal { /** @@ -23,7 +32,7 @@ class Weee extends AbstractTotal const ITEM_TYPE = 'weee'; /** - * @var \Magento\Weee\Helper\Data + * @var WeeHelper */ protected $weeeData; @@ -66,11 +75,11 @@ class Weee extends AbstractTotal protected $priceCurrency; /** - * @param \Magento\Weee\Helper\Data $weeeData + * @param WeeHelper $weeeData * @param PriceCurrencyInterface $priceCurrency */ public function __construct( - \Magento\Weee\Helper\Data $weeeData, + WeeHelper $weeeData, PriceCurrencyInterface $priceCurrency ) { $this->priceCurrency = $priceCurrency; @@ -82,15 +91,15 @@ public function __construct( /** * Collect Weee amounts for the quote / order * - * @param \Magento\Quote\Model\Quote $quote - * @param \Magento\Quote\Api\Data\ShippingAssignmentInterface $shippingAssignment - * @param \Magento\Quote\Model\Quote\Address\Total $total + * @param Quote $quote + * @param ShippingAssignmentInterface $shippingAssignment + * @param Total $total * @return $this */ public function collect( - \Magento\Quote\Model\Quote $quote, - \Magento\Quote\Api\Data\ShippingAssignmentInterface $shippingAssignment, - \Magento\Quote\Model\Quote\Address\Total $total + Quote $quote, + ShippingAssignmentInterface $shippingAssignment, + Total $total ) { AbstractTotal::collect($quote, $shippingAssignment, $total); $this->_store = $quote->getStore(); @@ -130,16 +139,16 @@ public function collect( /** * Calculate item fixed tax and prepare information for discount and regular taxation * - * @param \Magento\Quote\Model\Quote\Address $address - * @param \Magento\Quote\Model\Quote\Address\Total $total - * @param \Magento\Quote\Model\Quote\Item\AbstractItem $item - * @return void|$this + * @param Address $address + * @param Total $total + * @param AbstractItem $item + * @return void|$this * @SuppressWarnings(PHPMD.ExcessiveMethodLength) * @SuppressWarnings(PHPMD.UnusedLocalVariable) */ protected function process( - \Magento\Quote\Model\Quote\Address $address, - \Magento\Quote\Model\Quote\Address\Total $total, + Address $address, + Total $total, $item ) { $attributes = $this->weeeData->getProductWeeeAttributes( @@ -248,12 +257,12 @@ protected function process( /** * Process row amount based on FPT total amount configuration setting * - * @param \Magento\Quote\Model\Quote\Address\Total $total - * @param float $rowValueExclTax - * @param float $baseRowValueExclTax - * @param float $rowValueInclTax - * @param float $baseRowValueInclTax - * @return $this + * @param Total $total + * @param float $rowValueExclTax + * @param float $baseRowValueExclTax + * @param float $rowValueInclTax + * @param float $baseRowValueInclTax + * @return $this */ protected function processTotalAmount( $total, @@ -279,8 +288,7 @@ protected function processTotalAmount( } /** - * Increment and return counter. This function is intended to be used to generate temporary - * id for an item. + * Increment and return counter. This function is intended to be used to generate temporary id for an item. * * @return int */ @@ -292,25 +300,26 @@ protected function getNextIncrement() /** * Recalculate parent item amounts based on children results * - * @param \Magento\Quote\Model\Quote\Item\AbstractItem $item - * @return void - * + * @param AbstractItem $item + * @return void * @SuppressWarnings(PHPMD.UnusedFormalParameter) */ - protected function recalculateParent(\Magento\Quote\Model\Quote\Item\AbstractItem $item) + protected function recalculateParent(AbstractItem $item) { - $associatedTaxables = []; + $associatedTaxables = [[]]; foreach ($item->getChildren() as $child) { - $associatedTaxables = array_merge($associatedTaxables, $child->getAssociatedTaxables()); + $associatedTaxables[] = $child->getAssociatedTaxables(); } - $item->setAssociatedTaxables($associatedTaxables); + $item->setAssociatedTaxables( + array_unique(array_merge(...$associatedTaxables)) + ); } /** * Reset information about Tax and Wee on FPT for shopping cart item * - * @param \Magento\Quote\Model\Quote\Item\AbstractItem $item - * @return void + * @param AbstractItem $item + * @return void */ protected function resetItemData($item) { @@ -332,24 +341,24 @@ protected function resetItemData($item) } /** - * @param \Magento\Quote\Model\Quote $quote - * @param \Magento\Quote\Model\Quote\Address\Total $total + * Return `null` instead of original empty array + * + * @param Quote $quote + * @param Total $total * @return array|null * @SuppressWarnings(PHPMD.UnusedFormalParameter) */ - public function fetch(\Magento\Quote\Model\Quote $quote, \Magento\Quote\Model\Quote\Address\Total $total) + public function fetch(Quote $quote, Total $total) { return null; } /** - * Process model configuration array. - * This method can be used for changing totals collect sort order - * - * @param array $config - * @param Store $store - * @return array + * Process model configuration array. This method can be used for changing totals collect sort order * + * @param array $config + * @param Store $store + * @return array * @SuppressWarnings(PHPMD.UnusedFormalParameter) */ public function processConfigArray($config, $store) @@ -360,7 +369,7 @@ public function processConfigArray($config, $store) /** * No aggregated label for fixed product tax * - * TODO: fix + * @TODO: fix * @return string */ public function getLabel() diff --git a/app/code/Magento/Weee/Test/Mftf/ActionGroup/AssertStorefrontCartItemExcludingTaxWithWeeeAttributeActionGroup.xml b/app/code/Magento/Weee/Test/Mftf/ActionGroup/AssertStorefrontCartItemExcludingTaxWithWeeeAttributeActionGroup.xml new file mode 100644 index 0000000000000..ff3f6eb91e04a --- /dev/null +++ b/app/code/Magento/Weee/Test/Mftf/ActionGroup/AssertStorefrontCartItemExcludingTaxWithWeeeAttributeActionGroup.xml @@ -0,0 +1,25 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + /** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +--> + +<actionGroups xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/actionGroupSchema.xsd"> + <actionGroup name="AssertStorefrontCartItemExcludingTaxWithWeeeAttributeActionGroup" extends="AssertStorefrontCartItemExcludingTaxActionGroup"> + <annotations> + <description>Validates that the provided product price, subtotal, weee attribute value excluding tax are present and correct in the Shopping Cart.</description> + </annotations> + <arguments> + <argument name="weeePrice" defaultValue="50" type="string"/> + <argument name="weeeSubtotal" defaultValue="50" type="string"/> + </arguments> + + <click selector="{{StorefrontCheckoutCartItemTaxSection.productPriceExcludingTaxByName(productName)}}" after="assertProductSubtotalExcludingTax" stepKey="clickOnItemPriceExcludingTax"/> + <see userInput="{{weeePrice}}" selector="{{StorefrontCheckoutCartItemWeeeSection.productWeeePriceExcludingTaxByName(productName)}}" after="clickOnItemPriceExcludingTax" stepKey="assertProductWeeeExcludingTax"/> + <click selector="{{StorefrontCheckoutCartItemTaxSection.productSubtotalExcludingTaxByName(productName)}}" after="assertProductWeeeExcludingTax" stepKey="clickOnItemSubtotalExcludingTax"/> + <see userInput="{{weeeSubtotal}}" selector="{{StorefrontCheckoutCartItemWeeeSection.productWeeeSubtotalExcludingTaxByName(productName)}}" after="clickOnItemSubtotalExcludingTax" stepKey="assertProductWeeeSubtotalExcludingTax"/> + </actionGroup> +</actionGroups> diff --git a/app/code/Magento/Weee/Test/Mftf/ActionGroup/AssertStorefrontCartItemWithWeeeAttributeActionGroup.xml b/app/code/Magento/Weee/Test/Mftf/ActionGroup/AssertStorefrontCartItemWithWeeeAttributeActionGroup.xml new file mode 100644 index 0000000000000..6819afa9a0ad2 --- /dev/null +++ b/app/code/Magento/Weee/Test/Mftf/ActionGroup/AssertStorefrontCartItemWithWeeeAttributeActionGroup.xml @@ -0,0 +1,25 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + /** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +--> + +<actionGroups xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/actionGroupSchema.xsd"> + <actionGroup name="AssertStorefrontCartItemWithWeeeAttributeActionGroup" extends="AssertStorefrontCheckoutCartItemsActionGroup"> + <annotations> + <description>Validates that the provided Product details (Name and Price), Product Quantity, Weee Attribute data are present and correct in the Shopping Cart.</description> + </annotations> + <arguments> + <argument name="weeePrice" defaultValue="50" type="string"/> + <argument name="weeeSubtotal" defaultValue="50" type="string"/> + </arguments> + + <click selector="{{CheckoutCartProductSection.ProductPriceByName(productName)}}" after="seeProductQuantity" stepKey="clickOnItemPrice"/> + <see userInput="{{weeePrice}}" selector="{{StorefrontCheckoutCartItemWeeeSection.productWeeePriceByName(productName)}}" after="clickOnItemPrice" stepKey="assertProductWeee"/> + <click selector="{{CheckoutCartProductSection.productSubtotalByName(productName)}}" after="assertProductWeee" stepKey="clickOnItemSubtotal"/> + <see userInput="{{weeeSubtotal}}" selector="{{StorefrontCheckoutCartItemWeeeSection.productWeeeSubtotalByName(productName)}}" after="clickOnItemSubtotal" stepKey="assertProductWeeeSubtotal"/> + </actionGroup> +</actionGroups> diff --git a/app/code/Magento/Weee/Test/Mftf/ActionGroup/AssertStorefrontCheckoutCartTaxAmountFPTActionGroup.xml b/app/code/Magento/Weee/Test/Mftf/ActionGroup/AssertStorefrontCheckoutCartTaxAmountFPTActionGroup.xml new file mode 100644 index 0000000000000..794484b280151 --- /dev/null +++ b/app/code/Magento/Weee/Test/Mftf/ActionGroup/AssertStorefrontCheckoutCartTaxAmountFPTActionGroup.xml @@ -0,0 +1,21 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + /** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +--> + +<actionGroups xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/actionGroupSchema.xsd"> + <actionGroup name="AssertStorefrontCheckoutCartTaxAmountFPTActionGroup" extends="AssertStorefrontCheckoutCartTaxAmountActionGroup"> + <annotations> + <description>EXTENDS: AssertStorefrontCheckoutCartTaxAmountActionGroup. Add check FPT tax summary in Summary section of shopping cart on storefront</description> + </annotations> + <arguments> + <argument name="FPTAmount" type="string" defaultValue="$10"/> + </arguments> + + <see selector="{{CheckoutCartSummarySection.amountFPT}}" userInput="{{FPTAmount}}" before="checkTaxAmount" stepKey="checkFPTAmount"/> + </actionGroup> +</actionGroups> diff --git a/app/code/Magento/Weee/Test/Mftf/ActionGroup/AssertStorefrontShoppingCartSummaryItemsExcludingAndIncludingTaxWithWeeeAttributeActionGroup.xml b/app/code/Magento/Weee/Test/Mftf/ActionGroup/AssertStorefrontShoppingCartSummaryItemsExcludingAndIncludingTaxWithWeeeAttributeActionGroup.xml new file mode 100644 index 0000000000000..24b25420c1bd6 --- /dev/null +++ b/app/code/Magento/Weee/Test/Mftf/ActionGroup/AssertStorefrontShoppingCartSummaryItemsExcludingAndIncludingTaxWithWeeeAttributeActionGroup.xml @@ -0,0 +1,21 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + /** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +--> + +<actionGroups xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/actionGroupSchema.xsd"> + <actionGroup name="AssertStorefrontShoppingCartSummaryItemsExcludingAndIncludingTaxWithWeeeAttributeActionGroup" extends="AssertStorefrontShoppingCartSummaryItemsExcludingAndIncludingTaxActionGroup"> + <annotations> + <description>Goes to the Storefront Shopping Cart page. Validates that the provided Subtotal/Total excluding and including tax, weee attribute total are present and correct.</description> + </annotations> + <arguments> + <argument name="weeeSubtotal" defaultValue="50" type="string"/> + </arguments> + + <see userInput="{{weeeSubtotal}}" selector="{{CheckoutCartSummarySection.amountFPT}}" after="seeProceedToCheckoutButton" stepKey="assertWeeeSubtotal"/> + </actionGroup> +</actionGroups> diff --git a/app/code/Magento/Weee/Test/Mftf/ActionGroup/AssertStorefrontShoppingCartSummaryWithWeeeEnabledActionGroup.xml b/app/code/Magento/Weee/Test/Mftf/ActionGroup/AssertStorefrontShoppingCartSummaryWithWeeeEnabledActionGroup.xml new file mode 100644 index 0000000000000..259afa53f5c32 --- /dev/null +++ b/app/code/Magento/Weee/Test/Mftf/ActionGroup/AssertStorefrontShoppingCartSummaryWithWeeeEnabledActionGroup.xml @@ -0,0 +1,21 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + /** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +--> + +<actionGroups xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/actionGroupSchema.xsd"> + <actionGroup name="AssertStorefrontShoppingCartSummaryWithWeeeEnabledActionGroup" extends="AssertStorefrontShoppingCartSummaryItemsActionGroup"> + <annotations> + <description>Validates that the provided Subtotal/Total, Weee total are present and correct.</description> + </annotations> + <arguments> + <argument name="weeeSubtotal" defaultValue="50" type="string"/> + </arguments> + + <see userInput="{{weeeSubtotal}}" selector="{{CheckoutCartSummarySection.amountFPT}}" after="assertTotal" stepKey="assertWeeeSubtotal"/> + </actionGroup> +</actionGroups> diff --git a/app/code/Magento/Weee/Test/Mftf/Data/WeeeConfigData.xml b/app/code/Magento/Weee/Test/Mftf/Data/WeeeConfigData.xml index e44c1bb51e41b..5d55728606a52 100644 --- a/app/code/Magento/Weee/Test/Mftf/Data/WeeeConfigData.xml +++ b/app/code/Magento/Weee/Test/Mftf/Data/WeeeConfigData.xml @@ -22,4 +22,18 @@ <entity name="DisableFPT" type="disableFPT"> <data key="value">0</data> </entity> + <!-- Apply Tax To FPT Enable --> + <entity name="WeeeConfigApplyTaxToFptEnable" type="weee_config"> + <requiredEntity type="applyVat">ApplyVat</requiredEntity> + </entity> + <entity name="ApplyVat" type="applyVat"> + <data key="value">1</data> + </entity> + <!-- Apply Tax To FPT Disable--> + <entity name="WeeeConfigApplyTaxToFptDisable" type="weee_config_default"> + <requiredEntity type="disableVat">DisableVat</requiredEntity> + </entity> + <entity name="DisableVat" type="disableVat"> + <data key="value">0</data> + </entity> </entities> diff --git a/app/code/Magento/Weee/Test/Mftf/Metadata/WeeeConfigMeta.xml b/app/code/Magento/Weee/Test/Mftf/Metadata/WeeeConfigMeta.xml new file mode 100644 index 0000000000000..a7009d32eee20 --- /dev/null +++ b/app/code/Magento/Weee/Test/Mftf/Metadata/WeeeConfigMeta.xml @@ -0,0 +1,42 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + /** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +--> +<operations xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:noNamespaceSchemaLocation="urn:magento:mftf:DataGenerator/etc/dataOperation.xsd"> + <operation name="WeeeConfigEnable" dataType="weee_config" type="create" auth="adminFormKey" url="/admin/system_config/save/section/tax/" method="POST"> + <object key="groups" dataType="weee_config"> + <object key="weee" dataType="weee_config"> + <object key="fields" dataType="weee_config"> + <object key="enable" dataType="enableFPT"> + <field key="value">string</field> + </object> + <object key="apply_vat" dataType="applyVat"> + <field key="value">string</field> + </object> + </object> + </object> + </object> + </operation> + <operation name="WeeeConfigDefault" dataType="weee_config_default" type="create" auth="adminFormKey" url="/admin/system_config/save/section/tax/" method="POST"> + <object key="groups" dataType="weee_config_default"> + <object key="weee" dataType="weee_config_default"> + <object key="fields" dataType="weee_config_default"> + <object key="enable" dataType="weee_config_default"> + <object key="inherit" dataType="disableFPT"> + <field key="value">integer</field> + </object> + </object> + <object key="apply_vat" dataType="weee_config_default"> + <object key="inherit" dataType="disableVat"> + <field key="value">integer</field> + </object> + </object> + </object> + </object> + </object> + </operation> +</operations> diff --git a/app/code/Magento/Weee/Test/Mftf/Metadata/weee_config-meta.xml b/app/code/Magento/Weee/Test/Mftf/Metadata/weee_config-meta.xml deleted file mode 100644 index 56153067658d2..0000000000000 --- a/app/code/Magento/Weee/Test/Mftf/Metadata/weee_config-meta.xml +++ /dev/null @@ -1,34 +0,0 @@ -<?xml version="1.0" encoding="UTF-8"?> -<!-- - /** - * Copyright © Magento, Inc. All rights reserved. - * See COPYING.txt for license details. - */ ---> -<operations xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" - xsi:noNamespaceSchemaLocation="urn:magento:mftf:DataGenerator/etc/dataOperation.xsd"> - <operation name="WeeeConfigEnable" dataType="weee_config" type="create" auth="adminFormKey" url="/admin/system_config/save/section/tax/" method="POST"> - <object key="groups" dataType="weee_config"> - <object key="weee" dataType="weee_config"> - <object key="fields" dataType="weee_config"> - <object key="enable" dataType="enableFPT"> - <field key="value">string</field> - </object> - </object> - </object> - </object> - </operation> - <operation name="WeeeConfigDefault" dataType="weee_config_default" type="create" auth="adminFormKey" url="/admin/system_config/save/section/tax/" method="POST"> - <object key="groups" dataType="weee_config_default"> - <object key="weee" dataType="weee_config_default"> - <object key="fields" dataType="weee_config_default"> - <object key="enable" dataType="weee_config_default"> - <object key="inherit" dataType="disableFPT"> - <field key="value">integer</field> - </object> - </object> - </object> - </object> - </object> - </operation> -</operations> diff --git a/app/code/Magento/Weee/Test/Mftf/Page/AdminProductEditPage.xml b/app/code/Magento/Weee/Test/Mftf/Page/AdminProductEditPage.xml index 793b763f0fc15..fd4d38aeb9bd1 100644 --- a/app/code/Magento/Weee/Test/Mftf/Page/AdminProductEditPage.xml +++ b/app/code/Magento/Weee/Test/Mftf/Page/AdminProductEditPage.xml @@ -8,7 +8,7 @@ <pages xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="urn:magento:mftf:Page/etc/PageObject.xsd"> - <page name="AdminProductEditPage" url="catalog/product/edit/id/{{product_id}}/" area="admin" module="Magento_Catalog"> + <page name="AdminProductEditPage" url="catalog/product/edit/id/{{product_id}}" parameterized="true" area="admin" module="Magento_Catalog"> <section name="AdminProductAddFPTValueSection"/> </page> </pages> diff --git a/app/code/Magento/Weee/Test/Mftf/Page/CheckoutCartPage.xml b/app/code/Magento/Weee/Test/Mftf/Page/CheckoutCartPage.xml new file mode 100644 index 0000000000000..424db8eee8c45 --- /dev/null +++ b/app/code/Magento/Weee/Test/Mftf/Page/CheckoutCartPage.xml @@ -0,0 +1,14 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + /** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +--> + +<pages xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Page/etc/PageObject.xsd"> + <page name="CheckoutCartPage" url="/checkout/cart" module="Magento_Checkout" area="storefront"> + <section name="StorefrontCheckoutCartItemWeeeSection"/> + </page> +</pages> diff --git a/app/code/Magento/Weee/Test/Mftf/Section/AdminProductAddFPTValueSection.xml b/app/code/Magento/Weee/Test/Mftf/Section/AdminProductAddFPTValueSection.xml index 8a9e9ff6a7169..13383c9df121f 100644 --- a/app/code/Magento/Weee/Test/Mftf/Section/AdminProductAddFPTValueSection.xml +++ b/app/code/Magento/Weee/Test/Mftf/Section/AdminProductAddFPTValueSection.xml @@ -11,10 +11,10 @@ <section name="AdminProductAddFPTValueSection"> <element name="addFPT" type="button" selector="[data-index='{{FPTAttributeCode}}'] [data-action='add_new_row']" parameterized="true"/> <element name="removeRowByIndex" type="button" selector="[data-index='{{FPTAttributeCode}}'] [data-action='remove_row']:nth-of-type({{rowIndex}})" parameterized="true"/> - <element name="selectCountryForFPT" type="select" selector="(//select[contains(@name, 'product[{{FPTAttributeCode}}]') and contains(@name, '[country]')])[last()]" parameterized="true"/> - <element name="selectStateForFPT" type="select" selector="(//select[contains(@name, 'product[{{FPTAttributeCode}}]') and contains(@name, '[state]')])[last()]" parameterized="true"/> - <element name="setTaxValueForFPT" type="text" selector="(//input[contains(@name, 'product[{{FPTAttributeCode}}]') and contains(@name, '[value]')])[last()]" parameterized="true"/> - <element name="setWebSiteForFPT" type="text" selector="(//select[contains(@name, 'product[{{FPTAttributeCode}}]') and contains(@name, '[website_id]')])[last()]" parameterized="true"/> + <element name="selectCountryForFPT" type="select" selector="div.admin__field-control[data-index='{{FPTAttributeCode}}'] table tbody tr.data-row:last-child select[name*='[country]']" parameterized="true"/> + <element name="selectStateForFPT" type="select" selector="div.admin__field-control[data-index='{{FPTAttributeCode}}'] table tbody tr.data-row:last-child select[name*='[state]']" parameterized="true"/> + <element name="setTaxValueForFPT" type="text" selector="div.admin__field-control[data-index='{{FPTAttributeCode}}'] table tbody tr.data-row:last-child input[name*='[value]']" parameterized="true"/> + <element name="setWebSiteForFPT" type="text" selector="div.admin__field-control[data-index='{{FPTAttributeCode}}'] table tbody tr.data-row:last-child select[name*='[website_id]']" parameterized="true"/> <element name="setWebSiteForFPTOption" type="text" selector="(//select[contains(@name, 'product[{{FPTAttributeCode}}]') and contains(@name, '[website_id]')])/option[contains(text(), '{{website}}')]" parameterized="true"/> </section> </sections> diff --git a/app/code/Magento/Weee/Test/Mftf/Section/StorefrontCheckoutCartItemWeeeSection.xml b/app/code/Magento/Weee/Test/Mftf/Section/StorefrontCheckoutCartItemWeeeSection.xml new file mode 100644 index 0000000000000..8c656af42cbf5 --- /dev/null +++ b/app/code/Magento/Weee/Test/Mftf/Section/StorefrontCheckoutCartItemWeeeSection.xml @@ -0,0 +1,17 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + /** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +--> + +<sections xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Page/etc/SectionObject.xsd"> + <section name="StorefrontCheckoutCartItemWeeeSection"> + <element name="productWeeePriceByName" type="text" selector="//a[contains(text(),'{{productName}}')]/ancestor::tr//td[contains(@class, 'price')]//span[contains(@class,'weee')]" parameterized="true"/> + <element name="productWeeeSubtotalByName" type="text" selector="//a[contains(text(),'{{productName}}')]/ancestor::tr//td[contains(@class, 'subtotal')]//span[contains(@class,'weee')]" parameterized="true"/> + <element name="productWeeePriceExcludingTaxByName" type="text" selector="//a[contains(text(),'{{productName}}')]/ancestor::tr//td[contains(@class, 'price')]//span[contains(@class,'price-excluding-tax')]//span[contains(@class,'weee')]" parameterized="true"/> + <element name="productWeeeSubtotalExcludingTaxByName" type="text" selector="//a[contains(text(),'{{productName}}')]/ancestor::tr//td[contains(@class, 'subtotal')]//span[contains(@class,'price-excluding-tax')]//span[contains(@class,'weee')]" parameterized="true"/> + </section> +</sections> diff --git a/app/code/Magento/Weee/Test/Mftf/Test/AdminFixedTaxValSavedForSpecificWebsiteTest.xml b/app/code/Magento/Weee/Test/Mftf/Test/AdminFixedTaxValSavedForSpecificWebsiteTest.xml index 6cbdbe0db267d..3e9635e2a1492 100644 --- a/app/code/Magento/Weee/Test/Mftf/Test/AdminFixedTaxValSavedForSpecificWebsiteTest.xml +++ b/app/code/Magento/Weee/Test/Mftf/Test/AdminFixedTaxValSavedForSpecificWebsiteTest.xml @@ -32,7 +32,7 @@ <createData entity="ApiSimpleProduct" stepKey="createProduct"> <requiredEntity createDataKey="createCategory"/> </createData> - <actionGroup ref="LoginAsAdmin" stepKey="loginAsAdmin"/> + <actionGroup ref="AdminLoginActionGroup" stepKey="loginAsAdmin"/> <!-- Create website, store and store view --> <comment userInput="Create website, store and store view" stepKey="createWebsite"/> <actionGroup ref="AdminCreateWebsiteActionGroup" stepKey="createSecondWebsite"> @@ -66,11 +66,13 @@ <actionGroup ref="AdminDeleteWebsiteActionGroup" stepKey="deleteCustomWebsite"> <argument name="websiteName" value="{{NewWebSiteData.name}}"/> </actionGroup> - <actionGroup ref="logout" stepKey="logoutOfAdmin"/> + <actionGroup ref="AdminLogoutActionGroup" stepKey="logoutOfAdmin"/> </after> <!-- Go to product edit page and assign it to created website --> <comment userInput="Go to product edit page and assign it to created website" stepKey="assignProductToCreatedWebsite"/> - <amOnPage url="{{AdminProductEditPage.url($$createProduct.id$$)}}" stepKey="navigateToProductPage"/> + <actionGroup ref="AdminProductPageOpenByIdActionGroup" stepKey="navigateToProductPage"> + <argument name="productId" value="$$createProduct.id$$"/> + </actionGroup> <waitForPageLoad stepKey="waitForProductPageLoad"/> <actionGroup ref="SelectProductInWebsitesActionGroup" stepKey="selectWebsiteInProduct"> <argument name="website" value="{{NewWebSiteData.name}}"/> @@ -99,7 +101,9 @@ <magentoCLI command="cache:flush" stepKey="flushCache"/> <!--See available websites only 'All Websites'--> <comment userInput="See available websites 'All Websites', 'Main Website' and Second website" stepKey="commentCheckWebsitesInProductPage"/> - <amOnPage url="{{AdminProductEditPage.url($$createProduct.id$$)}}" stepKey="goToProductPageSecondTime"/> + <actionGroup ref="AdminProductPageOpenByIdActionGroup" stepKey="goToProductPageSecondTime"> + <argument name="productId" value="$$createProduct.id$$"/> + </actionGroup> <waitForPageLoad stepKey="waitForProductPageLoadSecondTime"/> <seeElement selector="{{AdminProductAddFPTValueSection.setWebSiteForFPTOption($$createProductFPTAttribute.attribute_code$$, 'All Websites')}}" stepKey="checkAllWebsitesInDropDown"/> <seeElement selector="{{AdminProductAddFPTValueSection.setWebSiteForFPTOption($$createProductFPTAttribute.attribute_code$$, 'Main Website')}}" stepKey="checkMainWebsiteInDropDown"/> diff --git a/app/code/Magento/Weee/Test/Mftf/Test/AdminRemoveProductWeeeAttributeOptionTest.xml b/app/code/Magento/Weee/Test/Mftf/Test/AdminRemoveProductWeeeAttributeOptionTest.xml index 6164f037048ba..60c39dd5058b5 100644 --- a/app/code/Magento/Weee/Test/Mftf/Test/AdminRemoveProductWeeeAttributeOptionTest.xml +++ b/app/code/Magento/Weee/Test/Mftf/Test/AdminRemoveProductWeeeAttributeOptionTest.xml @@ -23,7 +23,7 @@ <requiredEntity createDataKey="createProductFPTAttribute"/> </createData> <createData entity="SimpleProduct2" stepKey="createSimpleProduct"/> - <actionGroup ref="LoginAsAdmin" stepKey="loginAsAdmin"/> + <actionGroup ref="AdminLoginActionGroup" stepKey="loginAsAdmin"/> <actionGroup ref="SearchForProductOnBackendActionGroup" stepKey="searchForSimpleProductInitial"> <argument name="product" value="$$createSimpleProduct$$"/> </actionGroup> @@ -41,7 +41,7 @@ <amOnPage url="{{AdminProductIndexPage.url}}" stepKey="navigateToProductListing"/> <waitForPageLoad stepKey="waitForProductListingPageLoad"/> <actionGroup ref="ResetProductGridToDefaultViewActionGroup" stepKey="resetGridToDefaultKeywordSearch"/> - <actionGroup ref="logout" stepKey="logout"/> + <actionGroup ref="AdminLogoutActionGroup" stepKey="logout"/> <deleteData createDataKey="createProductFPTAttribute" stepKey="deleteProductFPTAttribute"/> <deleteData createDataKey="createSimpleProduct" stepKey="deleteSimpleProduct"/> </after> diff --git a/app/code/Magento/Weee/Test/Mftf/Test/StorefrontFPTTaxInformationInShoppingCartForCustomerPhysicalQuoteTest.xml b/app/code/Magento/Weee/Test/Mftf/Test/StorefrontFPTTaxInformationInShoppingCartForCustomerPhysicalQuoteTest.xml new file mode 100644 index 0000000000000..74d6c2a97b089 --- /dev/null +++ b/app/code/Magento/Weee/Test/Mftf/Test/StorefrontFPTTaxInformationInShoppingCartForCustomerPhysicalQuoteTest.xml @@ -0,0 +1,122 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + /** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +--> + +<tests xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/testSchema.xsd"> + <test name="StorefrontFPTTaxInformationInShoppingCartForCustomerPhysicalQuoteTest"> + <annotations> + <features value="Tax"/> + <stories value="Shopping cart taxes"/> + <title value="Tax information are updating/recalculating on fly in shopping cart for Customer with default addresses (physical quote)"/> + <description value="Tax information are updating/recalculating on fly in shopping cart for Customer with default addresses (physical quote)"/> + <severity value="CRITICAL"/> + <useCaseId value="MC-294"/> + <testCaseId value="MC-28586"/> + <group value="checkout"/> + <group value="tax"/> + <group value="weee"/> + </annotations> + + <before> + <!-- Preconditions --> + <!-- Tax Rule is created based on default tax rates (Stores>Tax Rule) US-CA-*-Rate 1 = 8.2500 US-NY-*-Rate 1 = 8.3750 --> + <createData entity="SimpleTaxRule" stepKey="createTaxRule"/> + <!-- Fixed Product Tax attribute is created and added to default attribute set --> + <createData entity="productFPTAttribute" stepKey="createProductFPTAttribute"/> + <createData entity="AddToDefaultSet" stepKey="addFPTToAttributeSet"> + <requiredEntity createDataKey="createProductFPTAttribute"/> + </createData> + <!-- Tax configuration (Store>Configuration; Sales>Tax) With FPT Enable --> + <createData entity="Tax_Config_NY" stepKey="taxConfigurationNYWithFPTEnable"/> + <!-- Store>Configuration; Sales>Tax FPT Enable --> + <createData entity="WeeeConfigEnable" stepKey="enableFPT"/> + <!-- Simple product is created Price = 10; FPT United States/California/10,United States/New York/20 --> + <createData entity="SimpleProduct2" stepKey="createSimpleProduct"> + <field key="price">10.00</field> + </createData> + <!-- Customer is created with default addresses: --> + <createData entity="Simple_US_Customer_CA" stepKey="createCustomer"/> + <actionGroup ref="AdminLoginActionGroup" stepKey="loginAsAdmin"/> + <actionGroup ref="AdminProductPageOpenByIdActionGroup" stepKey="openProductEditPage"> + <argument name="productId" value="$createSimpleProduct.id$"/> + </actionGroup> + <actionGroup ref="AdminProductAddFPTValueActionGroup" stepKey="addFPTValue1"> + <argument name="FPTAttributeCode" value="$createProductFPTAttribute.attribute_code$"/> + <argument name="stateForFPT" value="California"/> + <argument name="valueForFPT" value="10"/> + </actionGroup> + <actionGroup ref="AdminProductAddFPTValueActionGroup" stepKey="addFPTValue2"> + <argument name="FPTAttributeCode" value="$createProductFPTAttribute.attribute_code$"/> + <argument name="stateForFPT" value="New York"/> + <argument name="valueForFPT" value="20"/> + </actionGroup> + <actionGroup ref="SaveProductFormActionGroup" stepKey="saveProduct"/> + <magentoCron groups="index" stepKey="reindexBrokenIndices"/> + </before> + + <after> + <!-- Need to logout first because otherwise selenium fail with timeout --> + <actionGroup ref="StorefrontCustomerLogoutActionGroup" stepKey="customerLogout"/> + <deleteData createDataKey="createTaxRule" stepKey="deleteTaxRule"/> + <deleteData createDataKey="createProductFPTAttribute" stepKey="deleteProductFPTAttribute"/> + <createData entity="DefaultTaxConfig" stepKey="defaultTaxConfiguration"/> + <createData entity="WeeeConfigDisable" stepKey="disableFPT"/> + <deleteData createDataKey="createSimpleProduct" stepKey="deleteSimpleProduct"/> + <deleteData createDataKey="createCustomer" stepKey="deleteCustomer"/> + <amOnPage url="{{AdminProductIndexPage.url}}" stepKey="navigateToProductIndex"/> + <actionGroup ref="ClearFiltersAdminDataGridActionGroup" stepKey="clearProductsGridFilters"/> + <actionGroup ref="AdminLogoutActionGroup" stepKey="logoutFromAdmin"/> + <magentoCron groups="index" stepKey="reindexBrokenIndices"/> + </after> + + <!-- Test Steps --> + <!-- Step 1: Go to Storefront as logged in Customer --> + <actionGroup ref="LoginToStorefrontActionGroup" stepKey="customerLogin"> + <argument name="Customer" value="$createCustomer$" /> + </actionGroup> + <!-- Step 2: Add simple product to shopping cart --> + <amOnPage url="{{StorefrontProductPage.url($createSimpleProduct.custom_attributes[url_key]$)}}" stepKey="amOnSimpleProductPage"/> + <waitForPageLoad stepKey="waitForPageLoad"/> + <actionGroup ref="AddToCartFromStorefrontProductPageActionGroup" stepKey="cartAddSimpleProductToCart"> + <argument name="productName" value="$createSimpleProduct.name$"/> + </actionGroup> + <!-- Step 3: Go to Shopping Cart --> + <actionGroup ref="ClickViewAndEditCartFromMiniCartActionGroup" stepKey="goToShoppingCartFromMinicart"/> + <!-- Step 4: Open Estimate Shipping and Tax section --> + <actionGroup ref="AssertStorefrontCheckoutCartEstimateShippingAndTaxAddressActionGroup" stepKey="checkAddress"> + <argument name="country" value="{{US_Address_CA.country}}"/> + <argument name="state" value="{{US_Address_CA.state}}"/> + <argument name="postcode" value="{{US_Address_CA.postcode}}"/> + </actionGroup> + <actionGroup ref="AssertStorefrontCheckoutCartTaxAmountFPTActionGroup" stepKey="checkTaxAmountCA"/> + <!-- Step 5: Change Data --> + <actionGroup ref="StorefrontCheckoutCartFillEstimateShippingAndTaxActionGroup" stepKey="setEstimateShippingAndTaxAddressToSwitzerland"> + <argument name="country" value="Switzerland"/> + <argument name="state" value="Aargau"/> + <argument name="postcode" value="1234"/> + </actionGroup> + <!-- Step 6: Select shipping rate again(it need for get new totals request - performance reason) --> + <click selector="{{CheckoutCartSummarySection.flatRateShippingMethod}}" stepKey="selectFlatRateShippingMethod"/> + <scrollTo selector="{{CheckoutCartSummarySection.taxAmount}}" stepKey="scrollToTaxSummary" /> + <see selector="{{CheckoutCartSummarySection.taxAmount}}" userInput="$0.00" stepKey="checkTaxAmountZero" /> + <dontSeeElement selector="{{CheckoutCartSummarySection.amountFPT}}" stepKey="checkFPTIsNotDisplayed" /> + <!-- Step 7: Change Data --> + <actionGroup ref="StorefrontCheckoutCartFillEstimateShippingAndTaxActionGroup" stepKey="setEstimateShippingAndTaxAddressToUnitedStates"> + <argument name="country" value="United States"/> + <argument name="state" value="New York"/> + <argument name="postcode" value="12345"/> + </actionGroup> + <!-- Step 8: Select shipping rate again(it need for get new totals request - performance reason) --> + <click selector="{{CheckoutCartSummarySection.flatRateShippingMethod}}" stepKey="selectFlatRateShippingMethodAgain"/> + <actionGroup ref="AssertStorefrontCheckoutCartTaxAmountFPTActionGroup" stepKey="checkTaxAmountNY"> + <argument name="FPTAmount" value="$20"/> + <argument name="taxAmount" value="$0.84"/> + <argument name="rate" value="US-NY-*-Rate 1 (8.375%)"/> + </actionGroup> + </test> +</tests> diff --git a/app/code/Magento/Weee/Test/Mftf/Test/StorefrontFPTTaxInformationInShoppingCartForCustomerVirtualQuoteTest.xml b/app/code/Magento/Weee/Test/Mftf/Test/StorefrontFPTTaxInformationInShoppingCartForCustomerVirtualQuoteTest.xml new file mode 100644 index 0000000000000..dda125835110a --- /dev/null +++ b/app/code/Magento/Weee/Test/Mftf/Test/StorefrontFPTTaxInformationInShoppingCartForCustomerVirtualQuoteTest.xml @@ -0,0 +1,101 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + /** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +--> + +<tests xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/testSchema.xsd"> + <test name="StorefrontFPTTaxInformationInShoppingCartForCustomerVirtualQuoteTest"> + <annotations> + <features value="Tax"/> + <stories value="Shopping cart taxes"/> + <title value="Tax information are updating/recalculating on fly in shopping cart for Customer with default addresses (virtual quote)"/> + <description value="Tax information are updating/recalculating on fly in shopping cart for Customer with default addresses (virtual quote)"/> + <severity value="CRITICAL"/> + <useCaseId value="MC-294"/> + <testCaseId value="MC-28585"/> + <group value="checkout"/> + <group value="tax"/> + <group value="weee"/> + </annotations> + + <before> + <!-- Preconditions --> + <!-- Tax Rule is created based on default tax rates (Stores>Tax Rule) US-CA-*-Rate 1 = 8.2500 US-NY-*-Rate 1 = 8.3750 --> + <createData entity="SimpleTaxRule" stepKey="createTaxRule"/> + <!-- Fixed Product Tax attribute is created and added to default attribute set --> + <createData entity="productFPTAttribute" stepKey="createProductFPTAttribute"/> + <createData entity="AddToDefaultSet" stepKey="addFPTToAttributeSet"> + <requiredEntity createDataKey="createProductFPTAttribute"/> + </createData> + <!-- Tax configuration (Store>Configuration; Sales>Tax) --> + <createData entity="Tax_Config_CA" stepKey="taxConfigurationCA"/> + <!-- Virtual product is created: --> + <createData entity="VirtualProduct" stepKey="createVirtualProduct"> + <field key="price">40.00</field> + </createData> + <!-- Customer is created with default addresses: --> + <createData entity="Simple_US_Customer_NY" stepKey="createCustomer"/> + + <actionGroup ref="AdminLoginActionGroup" stepKey="loginAsAdmin"/> + <magentoCron groups="index" stepKey="reindexBrokenIndices"/> + </before> + + <after> + <!-- Need to logout first because otherwise selenium fail with timeout --> + <actionGroup ref="StorefrontCustomerLogoutActionGroup" stepKey="customerLogout"/> + <deleteData createDataKey="createTaxRule" stepKey="deleteTaxRule"/> + <deleteData createDataKey="createProductFPTAttribute" stepKey="deleteProductFPTAttribute"/> + <createData entity="DefaultTaxConfig" stepKey="defaultTaxConfiguration"/> + <deleteData createDataKey="createVirtualProduct" stepKey="deleteVirtualProduct"/> + <deleteData createDataKey="createCustomer" stepKey="deleteCustomer"/> + <actionGroup ref="AdminLogoutActionGroup" stepKey="logoutFromAdmin"/> + <magentoCron groups="index" stepKey="reindexBrokenIndices"/> + </after> + + <!-- Test Steps --> + <!-- Step 1: Go to Storefront as logged in Customer --> + <actionGroup ref="LoginToStorefrontActionGroup" stepKey="customerLogin"> + <argument name="Customer" value="$createCustomer$" /> + </actionGroup> + <!-- Step 2: Add virtual product to shopping cart --> + <amOnPage url="{{StorefrontProductPage.url($createVirtualProduct.custom_attributes[url_key]$)}}" stepKey="amOnStorefrontVirtualProductPage"/> + <waitForPageLoad stepKey="waitForPageLoad"/> + <actionGroup ref="AddToCartFromStorefrontProductPageActionGroup" stepKey="cartAddVirtualProductToCart"> + <argument name="productName" value="$createVirtualProduct.name$"/> + </actionGroup> + <!-- Step 3: Go to Shopping Cart --> + <actionGroup ref="ClickViewAndEditCartFromMiniCartActionGroup" stepKey="goToShoppingCartFromMinicart"/> + <!-- Step 4: Open Estimate Shipping and Tax section --> + <actionGroup ref="AssertStorefrontCheckoutCartEstimateShippingAndTaxAddressActionGroup" stepKey="checkAddress"> + <argument name="country" value="{{US_Address_NY.country}}"/> + <argument name="state" value="{{US_Address_NY.state}}"/> + <argument name="postcode" value="{{US_Address_NY.postcode}}"/> + </actionGroup> + <actionGroup ref="AssertStorefrontCheckoutCartTaxAmountActionGroup" stepKey="checkTaxAmountNY"> + <argument name="taxAmount" value="$3.35"/> + <argument name="rate" value="US-NY-*-Rate 1 (8.375%)"/> + </actionGroup> + <!-- Step 5: Change Data --> + <actionGroup ref="StorefrontCheckoutCartFillEstimateShippingAndTaxActionGroup" stepKey="setEstimateShippingAndTaxAddressToSwitzerland"> + <argument name="country" value="Switzerland"/> + <argument name="state" value="Aargau"/> + <argument name="postcode" value="1234"/> + </actionGroup> + <scrollTo selector="{{CheckoutCartSummarySection.taxAmount}}" stepKey="scrollToTaxSummary" /> + <see selector="{{CheckoutCartSummarySection.taxAmount}}" userInput="$0.00" stepKey="checkTaxAmountZero" /> + <!-- Step 6: Change Data --> + <actionGroup ref="StorefrontCheckoutCartFillEstimateShippingAndTaxActionGroup" stepKey="setEstimateShippingAndTaxAddressToUnitedStates"> + <argument name="country" value="United States"/> + <argument name="state" value="California"/> + <argument name="postcode" value="90230"/> + </actionGroup> + <actionGroup ref="AssertStorefrontCheckoutCartTaxAmountActionGroup" stepKey="checkTaxAmountCA"> + <argument name="taxAmount" value="$3.30"/> + <argument name="rate" value="US-CA-*-Rate 1 (8.25%)"/> + </actionGroup> + </test> +</tests> diff --git a/app/code/Magento/Weee/Test/Mftf/Test/StorefrontFPTTaxInformationInShoppingCartForGuestPhysicalQuoteTest.xml b/app/code/Magento/Weee/Test/Mftf/Test/StorefrontFPTTaxInformationInShoppingCartForGuestPhysicalQuoteTest.xml new file mode 100644 index 0000000000000..92f526c79e926 --- /dev/null +++ b/app/code/Magento/Weee/Test/Mftf/Test/StorefrontFPTTaxInformationInShoppingCartForGuestPhysicalQuoteTest.xml @@ -0,0 +1,114 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + /** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +--> + +<tests xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/testSchema.xsd"> + <test name="StorefrontFPTTaxInformationInShoppingCartForGuestPhysicalQuoteTest"> + <annotations> + <features value="Tax"/> + <stories value="Shopping cart taxes"/> + <title value="Tax information are updating/recalculating on fly in shopping cart for Guest (physical quote)"/> + <description value="Tax information are updating/recalculating on fly in shopping cart for Guest (physical quote)"/> + <severity value="CRITICAL"/> + <useCaseId value="MC-294"/> + <testCaseId value="MC-28592"/> + <group value="checkout"/> + <group value="tax"/> + <group value="weee"/> + </annotations> + + <before> + <!-- Preconditions --> + <!-- Tax Rule is created based on default tax rates (Stores>Tax Rule) US-CA-*-Rate 1 = 8.2500 US-NY-*-Rate 1 = 8.3750 --> + <createData entity="SimpleTaxRule" stepKey="createTaxRule"/> + <!-- Fixed Product Tax attribute is created and added to default attribute set --> + <createData entity="productFPTAttribute" stepKey="createProductFPTAttribute"/> + <createData entity="AddToDefaultSet" stepKey="addFPTToAttributeSet"> + <requiredEntity createDataKey="createProductFPTAttribute"/> + </createData> + <!-- Tax configuration (Store>Configuration; Sales>Tax) --> + <createData entity="Tax_Config_CA" stepKey="taxConfigurationForCA"/> + <!-- Store>Configuration; Sales>Tax FPT Enable --> + <createData entity="WeeeConfigEnable" stepKey="enableFPT"/> + <!-- Simple product is created Price = 10; FPT United States/California/10,United States/New York/20 --> + <createData entity="SimpleProduct2" stepKey="createSimpleProduct"> + <field key="price">10.00</field> + </createData> + <actionGroup ref="AdminLoginActionGroup" stepKey="loginAsAdmin"/> + <actionGroup ref="AdminProductPageOpenByIdActionGroup" stepKey="openProductEditPage"> + <argument name="productId" value="$createSimpleProduct.id$"/> + </actionGroup> + <actionGroup ref="AdminProductAddFPTValueActionGroup" stepKey="addFPTValue1"> + <argument name="FPTAttributeCode" value="$createProductFPTAttribute.attribute_code$"/> + <argument name="stateForFPT" value="California"/> + <argument name="valueForFPT" value="10"/> + </actionGroup> + <actionGroup ref="AdminProductAddFPTValueActionGroup" stepKey="addFPTValue2"> + <argument name="FPTAttributeCode" value="$createProductFPTAttribute.attribute_code$"/> + <argument name="stateForFPT" value="New York"/> + <argument name="valueForFPT" value="20"/> + </actionGroup> + <actionGroup ref="SaveProductFormActionGroup" stepKey="saveProduct"/> + <magentoCron groups="index" stepKey="reindexBrokenIndices"/> + </before> + + <after> + <deleteData createDataKey="createTaxRule" stepKey="deleteTaxRule"/> + <deleteData createDataKey="createProductFPTAttribute" stepKey="deleteProductFPTAttribute"/> + <createData entity="DefaultTaxConfig" stepKey="defaultTaxConfiguration"/> + <createData entity="WeeeConfigDisable" stepKey="disableFPT"/> + <deleteData createDataKey="createSimpleProduct" stepKey="deleteSimpleProduct"/> + <amOnPage url="{{AdminProductIndexPage.url}}" stepKey="navigateToProductIndex"/> + <actionGroup ref="ClearFiltersAdminDataGridActionGroup" stepKey="clearProductsGridFilters"/> + <actionGroup ref="AdminLogoutActionGroup" stepKey="logoutFromAdmin"/> + <magentoCron groups="index" stepKey="reindexBrokenIndices"/> + </after> + + <!-- Test Steps --> + <!-- Step 1: Go to Storefront as Guest --> + <!-- Step 2: Add simple product to shopping cart --> + <amOnPage url="{{StorefrontProductPage.url($createSimpleProduct.custom_attributes[url_key]$)}}" stepKey="amOnSimpleProductPage"/> + <waitForPageLoad stepKey="waitForPageLoad2"/> + <actionGroup ref="AddToCartFromStorefrontProductPageActionGroup" stepKey="cartAddSimpleProductToCart"> + <argument name="productName" value="$createSimpleProduct.name$"/> + </actionGroup> + <!-- Step 3: Go to Shopping Cart --> + <actionGroup ref="ClickViewAndEditCartFromMiniCartActionGroup" stepKey="goToShoppingCartFromMinicart"/> + <!-- Step 4: Open Estimate Shipping and Tax section --> + <actionGroup ref="StorefrontCheckoutCartFillEstimateShippingAndTaxActionGroup" stepKey="setEstimateShippingAndTaxInitialAddressToUnitedStates"> + <argument name="country" value="United States"/> + <argument name="state" value="California"/> + <argument name="postcode" value="*"/> + </actionGroup> + <actionGroup ref="AssertStorefrontCheckoutCartTaxAmountFPTActionGroup" stepKey="checkTaxAmountCA"/> + <!-- Step 5: Change Data --> + <actionGroup ref="StorefrontCheckoutCartFillEstimateShippingAndTaxActionGroup" stepKey="setEstimateShippingAndTaxAddressToSwitzerland"> + <argument name="country" value="Switzerland"/> + <argument name="state" value="Aargau"/> + <argument name="postcode" value="1234"/> + </actionGroup> + <!-- Step 6: Select shipping rate again(it need for get new totals request - performance reason) --> + <click selector="{{CheckoutCartSummarySection.flatRateShippingMethod}}" stepKey="selectFlatRateShippingMethod"/> + <scrollTo selector="{{CheckoutCartSummarySection.taxAmount}}" stepKey="scrollToTaxSummary" /> + <see selector="{{CheckoutCartSummarySection.taxAmount}}" userInput="$0.00" stepKey="checkTaxAmountZero" /> + <dontSeeElement selector="{{CheckoutCartSummarySection.amountFPT}}" stepKey="checkFPTIsNotDisplayed" /> + <!-- Step 7: Change Data --> + <actionGroup ref="StorefrontCheckoutCartFillEstimateShippingAndTaxActionGroup" stepKey="setEstimateShippingAndTaxAddressToUnitedStates"> + <argument name="country" value="United States"/> + <argument name="state" value="New York"/> + <argument name="postcode" value="12345"/> + </actionGroup> + <!-- Step 8: Select shipping rate again(it need for get new totals request - performance reason) --> + <click selector="{{CheckoutCartSummarySection.flatRateShippingMethod}}" stepKey="selectFlatRateShippingMethodAgain"/> + <actionGroup ref="AssertStorefrontCheckoutCartTaxAmountFPTActionGroup" stepKey="checkTaxAmountNY"> + <argument name="FPTAmount" value="$20"/> + <argument name="taxAmount" value="$0.84"/> + <argument name="rate" value="US-NY-*-Rate 1 (8.375%)"/> + </actionGroup> + </test> +</tests> diff --git a/app/code/Magento/Weee/Test/Mftf/Test/StorefrontFPTTaxInformationInShoppingCartForGuestVirtualQuoteTest.xml b/app/code/Magento/Weee/Test/Mftf/Test/StorefrontFPTTaxInformationInShoppingCartForGuestVirtualQuoteTest.xml new file mode 100644 index 0000000000000..495b9a990a465 --- /dev/null +++ b/app/code/Magento/Weee/Test/Mftf/Test/StorefrontFPTTaxInformationInShoppingCartForGuestVirtualQuoteTest.xml @@ -0,0 +1,94 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + /** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +--> + +<tests xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/testSchema.xsd"> + <test name="StorefrontFPTTaxInformationInShoppingCartForGuestVirtualQuoteTest"> + <annotations> + <features value="Tax"/> + <stories value="Shopping cart taxes"/> + <title value="Tax information are updating/recalculating on fly in shopping cart for Guest (virtual quote)"/> + <description value="Tax information are updating/recalculating on fly in shopping cart for Guest (virtual quote)"/> + <severity value="CRITICAL"/> + <useCaseId value="MC-294"/> + <testCaseId value="MC-26557"/> + <group value="checkout"/> + <group value="tax"/> + <group value="weee"/> + </annotations> + + <before> + <!-- Preconditions --> + <!-- Tax Rule is created based on default tax rates (Stores>Tax Rule) US-CA-*-Rate 1 = 8.2500 US-NY-*-Rate 1 = 8.3750 --> + <createData entity="SimpleTaxRule" stepKey="createTaxRule"/> + <!-- Fixed Product Tax attribute is created and added to default attribute set --> + <createData entity="productFPTAttribute" stepKey="createProductFPTAttribute"/> + <createData entity="AddToDefaultSet" stepKey="addFPTToAttributeSet"> + <requiredEntity createDataKey="createProductFPTAttribute"/> + </createData> + <!-- Tax configuration (Store>Configuration; Sales>Tax) --> + <createData entity="Tax_Config_CA" stepKey="taxConfigurationCA"/> + <!-- Virtual product is created: Price = 10 --> + <createData entity="VirtualProduct" stepKey="createVirtualProduct"> + <field key="price">40.00</field> + </createData> + + <actionGroup ref="AdminLoginActionGroup" stepKey="loginAsAdmin"/> + <magentoCron groups="index" stepKey="reindexBrokenIndices"/> + </before> + + <after> + <deleteData createDataKey="createTaxRule" stepKey="deleteTaxRule"/> + <deleteData createDataKey="createProductFPTAttribute" stepKey="deleteProductFPTAttribute"/> + <createData entity="DefaultTaxConfig" stepKey="defaultTaxConfiguration"/> + <deleteData createDataKey="createVirtualProduct" stepKey="deleteVirtualProduct"/> + <actionGroup ref="AdminLogoutActionGroup" stepKey="logoutFromAdmin"/> + <magentoCron groups="index" stepKey="reindexBrokenIndices"/> + </after> + + <!-- Test Steps --> + <!-- Step 1: Go to Storefront as Guest --> + <!-- Step 2: Add virtual product to shopping cart --> + <amOnPage url="{{StorefrontProductPage.url($createVirtualProduct.custom_attributes[url_key]$)}}" stepKey="amOnStorefrontVirtualProductPage"/> + <waitForPageLoad stepKey="waitForPageLoad2"/> + <actionGroup ref="AddToCartFromStorefrontProductPageActionGroup" stepKey="cartAddVirtualProductToCart"> + <argument name="productName" value="$createVirtualProduct.name$"/> + </actionGroup> + <!-- Step 3: Go to Shopping Cart --> + <actionGroup ref="ClickViewAndEditCartFromMiniCartActionGroup" stepKey="goToShoppingCartFromMinicart"/> + <!-- Step 4: Open Estimate Shipping and Tax section --> + <actionGroup ref="StorefrontCheckoutCartFillEstimateShippingAndTaxActionGroup" stepKey="setEstimateShippingAndTaxInitialAddressToUnitedStates"> + <argument name="country" value="United States"/> + <argument name="state" value="California"/> + <argument name="postcode" value="*"/> + </actionGroup> + + <actionGroup ref="AssertStorefrontCheckoutCartTaxAmountActionGroup" stepKey="checkTaxAmountCA"> + <argument name="taxAmount" value="$3.30"/> + <argument name="rate" value="US-CA-*-Rate 1 (8.25%)"/> + </actionGroup> + <!-- Step 5: Change Data --> + <actionGroup ref="StorefrontCheckoutCartFillEstimateShippingAndTaxActionGroup" stepKey="setEstimateShippingAndTaxAddressToSwitzerland"> + <argument name="country" value="Switzerland"/> + <argument name="state" value="Aargau"/> + <argument name="postcode" value="1234"/> + </actionGroup> + <scrollTo selector="{{CheckoutCartSummarySection.taxAmount}}" stepKey="scrollToTaxSummary" /> + <see selector="{{CheckoutCartSummarySection.taxAmount}}" userInput="$0.00" stepKey="checkTaxAmountZero" /> + <!-- Step 6: Change Data --> + <actionGroup ref="StorefrontCheckoutCartFillEstimateShippingAndTaxActionGroup" stepKey="setEstimateShippingAndTaxAddressToUnitedStates"> + <argument name="country" value="United States"/> + <argument name="state" value="New York"/> + <argument name="postcode" value="12345"/> + </actionGroup> + <actionGroup ref="AssertStorefrontCheckoutCartTaxAmountActionGroup" stepKey="checkTaxAmountNY"> + <argument name="taxAmount" value="$3.35"/> + <argument name="rate" value="US-NY-*-Rate 1 (8.375%)"/> + </actionGroup> + </test> +</tests> diff --git a/app/code/Magento/Weee/Test/Unit/App/Action/ContextPluginTest.php b/app/code/Magento/Weee/Test/Unit/App/Action/ContextPluginTest.php index c829b524527a6..b28a223ef2dce 100644 --- a/app/code/Magento/Weee/Test/Unit/App/Action/ContextPluginTest.php +++ b/app/code/Magento/Weee/Test/Unit/App/Action/ContextPluginTest.php @@ -3,114 +3,146 @@ * Copyright © Magento, Inc. All rights reserved. * See COPYING.txt for license details. */ + namespace Magento\Weee\Test\Unit\App\Action; +use Magento\Customer\Model\Session as CustomerSession; +use Magento\Framework\App\Config; +use Magento\Framework\App\Http\Context as HttpContext; +use Magento\Framework\App\Test\Unit\Action\Stub\ActionStub; +use Magento\Framework\Module\Manager as ModuleManager; +use Magento\Framework\TestFramework\Unit\Helper\ObjectManager; +use Magento\PageCache\Model\Config as PageCacheConfig; +use Magento\Store\Model\ScopeInterface; +use Magento\Store\Model\Store; +use Magento\Store\Model\StoreManager; +use Magento\Tax\Helper\Data as TaxHelper; +use Magento\Tax\Model\Calculation\Proxy as TaxCalculation; +use Magento\Tax\Model\Config as TaxConfig; +use Magento\Weee\Helper\Data as WeeeHelper; +use Magento\Weee\Model\App\Action\ContextPlugin; +use Magento\Weee\Model\Tax; +use PHPUnit\Framework\MockObject\MockObject; +use PHPUnit\Framework\TestCase; + /** - * Class ContextPluginTest + * Unit Tests to cover Context Plugin * - * @package Magento\Weee\Test\Unit\App\Action * @SuppressWarnings(PHPMD.CouplingBetweenObjects) */ -class ContextPluginTest extends \PHPUnit\Framework\TestCase +class ContextPluginTest extends TestCase { /** - * @var \Magento\Tax\Helper\Data + * @var TaxHelper|MockObject */ protected $taxHelperMock; /** - * @var \Magento\Weee\Helper\Data + * @var WeeeHelper|MockObject */ protected $weeeHelperMock; /** - * @var \Magento\Weee\Model\Tax + * @var Tax|MockObject */ protected $weeeTaxMock; /** - * @var \Magento\Framework\App\Http\Context + * @var HttpContext|MockObject */ protected $httpContextMock; /** - * @var \Magento\Tax\Model\Calculation\Proxy + * @var TaxCalculation|MockObject */ protected $taxCalculationMock; /** - * @var \Magento\Framework\Module\Manager + * @var ModuleManager|MockObject */ protected $moduleManagerMock; /** - * @var \Magento\PageCache\Model\Config + * @var PageCacheConfig|MockObject */ protected $cacheConfigMock; /** - * @var \Magento\Store\Model\StoreManager + * @var StoreManager|MockObject */ - protected $storeManageMock; + protected $storeManagerMock; /** - * @var \Magento\Framework\App\Config\ScopeConfig + * @var Config|MockObject */ protected $scopeConfigMock; /** - * @var \Magento\Tax\Model\App\Action\ContextPlugin + * @var CustomerSession|MockObject + */ + private $customerSessionMock; + + /** + * @var ObjectManager + */ + private $objectManager; + + /** + * @var ContextPlugin */ protected $contextPlugin; protected function setUp() { - $this->objectManager = new \Magento\Framework\TestFramework\Unit\Helper\ObjectManager($this); + $this->objectManager = new ObjectManager($this); - $this->taxHelperMock = $this->getMockBuilder(\Magento\Tax\Helper\Data::class) + $this->taxHelperMock = $this->getMockBuilder(TaxHelper::class) ->disableOriginalConstructor() ->getMock(); - $this->weeeHelperMock = $this->getMockBuilder(\Magento\Weee\Helper\Data::class) + $this->weeeHelperMock = $this->getMockBuilder(WeeeHelper::class) ->disableOriginalConstructor() ->getMock(); - $this->weeeTaxMock = $this->getMockBuilder(\Magento\Weee\Model\Tax::class) + $this->weeeTaxMock = $this->getMockBuilder(Tax::class) ->disableOriginalConstructor() ->getMock(); - $this->httpContextMock = $this->getMockBuilder(\Magento\Framework\App\Http\Context::class) + $this->httpContextMock = $this->getMockBuilder(HttpContext::class) ->disableOriginalConstructor() ->getMock(); - $this->customerSessionMock = $this->getMockBuilder(\Magento\Customer\Model\Session::class) + $this->customerSessionMock = $this->getMockBuilder(CustomerSession::class) ->disableOriginalConstructor() ->setMethods( [ - 'getDefaultTaxBillingAddress', 'getDefaultTaxShippingAddress', 'getCustomerTaxClassId', - 'getWebsiteId', 'isLoggedIn' + 'getDefaultTaxBillingAddress', + 'getDefaultTaxShippingAddress', + 'getCustomerTaxClassId', + 'getWebsiteId', + 'isLoggedIn' ] ) ->getMock(); - $this->moduleManagerMock = $this->getMockBuilder(\Magento\Framework\Module\Manager::class) + $this->moduleManagerMock = $this->getMockBuilder(ModuleManager::class) ->disableOriginalConstructor() ->getMock(); - $this->cacheConfigMock = $this->getMockBuilder(\Magento\PageCache\Model\Config::class) + $this->cacheConfigMock = $this->getMockBuilder(PageCacheConfig::class) ->disableOriginalConstructor() ->getMock(); - $this->storeManagerMock = $this->getMockBuilder(\Magento\Store\Model\StoreManager::class) + $this->storeManagerMock = $this->getMockBuilder(StoreManager::class) ->disableOriginalConstructor() ->getMock(); - $this->scopeConfigMock = $this->getMockBuilder(\Magento\Framework\App\Config::class) + $this->scopeConfigMock = $this->getMockBuilder(Config::class) ->disableOriginalConstructor() ->getMock(); $this->contextPlugin = $this->objectManager->getObject( - \Magento\Weee\Model\App\Action\ContextPlugin::class, + ContextPlugin::class, [ 'customerSession' => $this->customerSessionMock, 'httpContext' => $this->httpContextMock, @@ -125,7 +157,7 @@ protected function setUp() ); } - public function testBeforeDispatchBasedOnDefault() + public function testBeforeExecuteBasedOnDefault() { $this->customerSessionMock->expects($this->once()) ->method('isLoggedIn') @@ -148,7 +180,7 @@ public function testBeforeDispatchBasedOnDefault() ->method('getTaxBasedOn') ->willReturn('billing'); - $storeMock = $this->getMockBuilder(\Magento\Store\Model\Store::class) + $storeMock = $this->getMockBuilder(Store::class) ->disableOriginalConstructor() ->getMock(); @@ -163,8 +195,8 @@ public function testBeforeDispatchBasedOnDefault() $this->scopeConfigMock->expects($this->at(0)) ->method('getValue') ->with( - \Magento\Tax\Model\Config::CONFIG_XML_PATH_DEFAULT_COUNTRY, - \Magento\Store\Model\ScopeInterface::SCOPE_STORE, + TaxConfig::CONFIG_XML_PATH_DEFAULT_COUNTRY, + ScopeInterface::SCOPE_STORE, null ) ->willReturn('US'); @@ -172,8 +204,8 @@ public function testBeforeDispatchBasedOnDefault() $this->scopeConfigMock->expects($this->at(1)) ->method('getValue') ->with( - \Magento\Tax\Model\Config::CONFIG_XML_PATH_DEFAULT_REGION, - \Magento\Store\Model\ScopeInterface::SCOPE_STORE, + TaxConfig::CONFIG_XML_PATH_DEFAULT_REGION, + ScopeInterface::SCOPE_STORE, null ) ->willReturn(0); @@ -187,13 +219,13 @@ public function testBeforeDispatchBasedOnDefault() ->method('setValue') ->with('weee_tax_region', ['countryId' => 'US', 'regionId' => 0], 0); - $action = $this->objectManager->getObject(\Magento\Framework\App\Test\Unit\Action\Stub\ActionStub::class); - $request = $this->createPartialMock(\Magento\Framework\App\Request\Http::class, ['getActionName']); + /** @var ActionStub $action */ + $action = $this->objectManager->getObject(ActionStub::class); - $this->contextPlugin->beforeDispatch($action, $request); + $this->contextPlugin->beforeExecute($action); } - public function testBeforeDispatchBasedOnOrigin() + public function testBeforeExecuteBasedOnOrigin() { $this->customerSessionMock->expects($this->once()) ->method('isLoggedIn') @@ -216,13 +248,13 @@ public function testBeforeDispatchBasedOnOrigin() ->method('getTaxBasedOn') ->willReturn('origin'); - $action = $this->objectManager->getObject(\Magento\Framework\App\Test\Unit\Action\Stub\ActionStub::class); - $request = $this->createPartialMock(\Magento\Framework\App\Request\Http::class, ['getActionName']); + /** @var ActionStub $action */ + $action = $this->objectManager->getObject(ActionStub::class); - $this->contextPlugin->beforeDispatch($action, $request); + $this->contextPlugin->beforeExecute($action); } - public function testBeforeDispatchBasedOnBilling() + public function testBeforeExecuteBasedOnBilling() { $this->customerSessionMock->expects($this->once()) ->method('isLoggedIn') @@ -245,7 +277,7 @@ public function testBeforeDispatchBasedOnBilling() ->method('getTaxBasedOn') ->willReturn('billing'); - $storeMock = $this->getMockBuilder(\Magento\Store\Model\Store::class) + $storeMock = $this->getMockBuilder(Store::class) ->disableOriginalConstructor() ->getMock(); @@ -260,8 +292,8 @@ public function testBeforeDispatchBasedOnBilling() $this->scopeConfigMock->expects($this->at(0)) ->method('getValue') ->with( - \Magento\Tax\Model\Config::CONFIG_XML_PATH_DEFAULT_COUNTRY, - \Magento\Store\Model\ScopeInterface::SCOPE_STORE, + TaxConfig::CONFIG_XML_PATH_DEFAULT_COUNTRY, + ScopeInterface::SCOPE_STORE, null ) ->willReturn('US'); @@ -269,8 +301,8 @@ public function testBeforeDispatchBasedOnBilling() $this->scopeConfigMock->expects($this->at(1)) ->method('getValue') ->with( - \Magento\Tax\Model\Config::CONFIG_XML_PATH_DEFAULT_REGION, - \Magento\Store\Model\ScopeInterface::SCOPE_STORE, + TaxConfig::CONFIG_XML_PATH_DEFAULT_REGION, + ScopeInterface::SCOPE_STORE, null ) ->willReturn(0); @@ -288,13 +320,13 @@ public function testBeforeDispatchBasedOnBilling() ->method('setValue') ->with('weee_tax_region', ['countryId' => 'US', 'regionId' => 1], 0); - $action = $this->objectManager->getObject(\Magento\Framework\App\Test\Unit\Action\Stub\ActionStub::class); - $request = $this->createPartialMock(\Magento\Framework\App\Request\Http::class, ['getActionName']); + /** @var ActionStub $action */ + $action = $this->objectManager->getObject(ActionStub::class); - $this->contextPlugin->beforeDispatch($action, $request); + $this->contextPlugin->beforeExecute($action); } - public function testBeforeDispatchBasedOnShipping() + public function testBeforeExecuterBasedOnShipping() { $this->customerSessionMock->expects($this->once()) ->method('isLoggedIn') @@ -317,7 +349,7 @@ public function testBeforeDispatchBasedOnShipping() ->method('getTaxBasedOn') ->willReturn('shipping'); - $storeMock = $this->getMockBuilder(\Magento\Store\Model\Store::class) + $storeMock = $this->getMockBuilder(Store::class) ->disableOriginalConstructor() ->getMock(); @@ -332,8 +364,8 @@ public function testBeforeDispatchBasedOnShipping() $this->scopeConfigMock->expects($this->at(0)) ->method('getValue') ->with( - \Magento\Tax\Model\Config::CONFIG_XML_PATH_DEFAULT_COUNTRY, - \Magento\Store\Model\ScopeInterface::SCOPE_STORE, + TaxConfig::CONFIG_XML_PATH_DEFAULT_COUNTRY, + ScopeInterface::SCOPE_STORE, null ) ->willReturn('US'); @@ -341,8 +373,8 @@ public function testBeforeDispatchBasedOnShipping() $this->scopeConfigMock->expects($this->at(1)) ->method('getValue') ->with( - \Magento\Tax\Model\Config::CONFIG_XML_PATH_DEFAULT_REGION, - \Magento\Store\Model\ScopeInterface::SCOPE_STORE, + TaxConfig::CONFIG_XML_PATH_DEFAULT_REGION, + ScopeInterface::SCOPE_STORE, null ) ->willReturn(0); @@ -360,9 +392,9 @@ public function testBeforeDispatchBasedOnShipping() ->method('setValue') ->with('weee_tax_region', ['countryId' => 'US', 'regionId' => 1], 0); - $action = $this->objectManager->getObject(\Magento\Framework\App\Test\Unit\Action\Stub\ActionStub::class); - $request = $this->createPartialMock(\Magento\Framework\App\Request\Http::class, ['getActionName']); + /** @var ActionStub $action */ + $action = $this->objectManager->getObject(ActionStub::class); - $this->contextPlugin->beforeDispatch($action, $request); + $this->contextPlugin->beforeExecute($action); } } diff --git a/app/code/Magento/Weee/Test/Unit/Observer/UpdateElementTypesObserverTest.php b/app/code/Magento/Weee/Test/Unit/Observer/UpdateElementTypesObserverTest.php new file mode 100644 index 0000000000000..bcdbfe1d6f03f --- /dev/null +++ b/app/code/Magento/Weee/Test/Unit/Observer/UpdateElementTypesObserverTest.php @@ -0,0 +1,105 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ + +declare(strict_types=1); + +namespace Magento\Weee\Test\Unit\Observer; + +use Magento\Framework\DataObject; +use Magento\Framework\Event\Observer; +use Magento\Framework\TestFramework\Unit\Helper\ObjectManager; +use Magento\Reports\Model\Event; +use Magento\Weee\Block\Element\Weee\Tax; +use Magento\Weee\Observer\UpdateElementTypesObserver; +use PHPUnit\Framework\MockObject\MockObject; +use PHPUnit\Framework\TestCase; + +/** + * Unit test for Magento\Weee\Observer\UpdateElementTypesObserver + */ +class UpdateElementTypesObserverTest extends TestCase +{ + /* + * Stub response type + */ + const STUB_RESPONSE_TYPE = []; + + /** + * Testable Object + * + * @var UpdateElementTypesObserver + */ + private $observer; + + /** + * @var ObjectManager + */ + private $objectManager; + + /** + * @var Observer|MockObject + */ + private $observerMock; + + /** + * @var Event|MockObject + */ + private $eventMock; + + /** + * @var DataObject|MockObject + */ + private $responseMock; + + /** + * @inheritDoc + */ + protected function setUp(): void + { + $this->objectManager = new ObjectManager($this); + $this->observerMock = $this->createMock(Observer::class); + + $this->eventMock = $this->getMockBuilder(Event::class) + ->disableOriginalConstructor() + ->setMethods(['getResponse']) + ->getMock(); + + $this->responseMock = $this->getMockBuilder(DataObject::class) + ->disableOriginalConstructor() + ->setMethods(['getTypes', 'setTypes']) + ->getMock(); + + $this->observer = $this->objectManager->getObject(UpdateElementTypesObserver::class); + } + + /** + * Test for execute(), covers test case to adding custom element type for attributes form + */ + public function testRemoveProductUrlsFromStorage(): void + { + $this->observerMock + ->expects($this->once()) + ->method('getEvent') + ->willReturn($this->eventMock); + + $this->eventMock + ->expects($this->once()) + ->method('getResponse') + ->willReturn($this->responseMock); + + $this->responseMock + ->expects($this->once()) + ->method('getTypes') + ->willReturn(self::STUB_RESPONSE_TYPE); + + $this->responseMock + ->expects($this->once()) + ->method('setTypes') + ->with(['weee' => Tax::class]); + + $this->observer->execute($this->observerMock); + } +} diff --git a/app/code/Magento/Weee/Test/Unit/Plugin/Catalog/Controller/Adminhtml/Product/Initialization/Helper/ProcessTaxAttributeTest.php b/app/code/Magento/Weee/Test/Unit/Plugin/Catalog/Controller/Adminhtml/Product/Initialization/Helper/ProcessTaxAttributeTest.php new file mode 100644 index 0000000000000..2c88d3e0f0251 --- /dev/null +++ b/app/code/Magento/Weee/Test/Unit/Plugin/Catalog/Controller/Adminhtml/Product/Initialization/Helper/ProcessTaxAttributeTest.php @@ -0,0 +1,157 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +declare(strict_types=1); + +namespace Magento\Weee\Test\Unit\Plugin\Catalog\Controller\Adminhtml\Product\Initialization\Helper; + +use Magento\Catalog\Controller\Adminhtml\Product\Initialization\Helper; +use Magento\Catalog\Model\Product; +use Magento\Eav\Model\Entity\Attribute\AbstractAttribute; +use Magento\Framework\TestFramework\Unit\Helper\ObjectManager; +use Magento\Weee\Plugin\Catalog\Controller\Adminhtml\Product\Initialization\Helper\ProcessTaxAttribute; +use PHPUnit\Framework\MockObject\Matcher\InvokedCount as InvokedCountMatcher; +use PHPUnit\Framework\MockObject\MockObject; +use PHPUnit\Framework\TestCase; + +class ProcessTaxAttributeTest extends TestCase +{ + /** + * Weee frontend input + */ + private const WEEE_FRONTEND_INPUT = 'weee'; + + /** + * Text frontend input + */ + private const TEXT_FRONTEND_INPUT = 'text'; + + /** + * Stub weee attribute code + */ + private const STUB_WEEE_ATTRIBUTE_CODE = 'weee_1'; + + /** + * Stub weee attribute value + */ + private const STUB_WEEE_ATTRIBUTE_VALUE = 1122; + + /** + * @var ProcessTaxAttribute + */ + private $plugin; + + /** + * @var Helper|MockObject + */ + private $subjectMock; + + /** + * @var Product|MockObject + */ + private $productMock; + + /** + * @var Product|MockObject + */ + private $resultMock; + + /** + * Prepare environment for test + */ + protected function setUp() + { + $this->subjectMock = $this->createMock(Helper::class); + $this->resultMock = $this->createMock(Product::class); + $this->productMock = $this->createMock(Product::class); + + $objectManager = new ObjectManager($this); + $this->plugin = $objectManager->getObject(ProcessTaxAttribute::class); + } + + /** + * Test afterInitializeFromData when attributes are empty + */ + public function testAfterInitializeFromDataWhenAttributesAreEmpty() + { + $this->resultMock->expects($this->any())->method('getAttributes') + ->willReturn([]); + + $this->resultMock->expects($this->never())->method('setData'); + + $this->plugin->afterInitializeFromData($this->subjectMock, $this->resultMock, $this->productMock, []); + } + + /** + * Test afterInitializeFromData when attributes do not include weee frontend input + */ + public function testAfterInitializeFromDataWhenAttributesDoNotIncludeWeee() + { + /** @var AbstractAttribute|MockObject $attributeMock */ + $attributeMock = $this->createMock(AbstractAttribute::class); + + $attributeMock->expects($this->any())->method('getFrontendInput') + ->willReturn(self::TEXT_FRONTEND_INPUT); + + $this->resultMock->expects($this->any())->method('getAttributes') + ->willReturn([$attributeMock]); + + $this->resultMock->expects($this->never())->method('setData'); + + $this->plugin->afterInitializeFromData($this->subjectMock, $this->resultMock, $this->productMock, []); + } + + /** + * Test afterInitializeFromData when attributes include weee + * + * @param array $productData + * @param InvokedCountMatcher $expected + * @dataProvider afterInitializeFromDataWhenAttributesIncludeWeeeDataProvider + */ + public function testAfterInitializeFromDataWhenAttributesIncludeWeee($productData, $expected) + { + /** @var AbstractAttribute|MockObject $attributeMock */ + $attributeMock = $this->createMock(AbstractAttribute::class); + + $attributeMock->expects($this->any())->method('getFrontendInput') + ->willReturn(self::WEEE_FRONTEND_INPUT); + $attributeMock->expects($this->any())->method('getAttributeCode') + ->willReturn(self::STUB_WEEE_ATTRIBUTE_CODE); + $this->resultMock->expects($this->any())->method('getAttributes') + ->willReturn([$attributeMock]); + + $this->resultMock->expects($expected)->method('setData') + ->with(self::STUB_WEEE_ATTRIBUTE_CODE, []) + ->willReturnSelf(); + + $this->plugin->afterInitializeFromData( + $this->subjectMock, + $this->resultMock, + $this->productMock, + $productData + ); + } + + /** + * ProductData data provider for testAfterInitializeFromDataWhenAttributesIncludeWeee + * + * @return array + */ + public function afterInitializeFromDataWhenAttributesIncludeWeeeDataProvider() + { + return [ + 'Product data includes wee' => [ + [ + self::STUB_WEEE_ATTRIBUTE_CODE => self::STUB_WEEE_ATTRIBUTE_VALUE + ], + $this->never() + ], + 'Product data does not include wee' => [ + [], + $this->once() + ] + ]; + } +} diff --git a/app/code/Magento/Weee/Test/Unit/Plugin/Ui/DataProvider/WeeeSettingsTest.php b/app/code/Magento/Weee/Test/Unit/Plugin/Ui/DataProvider/WeeeSettingsTest.php new file mode 100644 index 0000000000000..0ac9c55313538 --- /dev/null +++ b/app/code/Magento/Weee/Test/Unit/Plugin/Ui/DataProvider/WeeeSettingsTest.php @@ -0,0 +1,74 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ + +declare(strict_types=1); + +namespace Magento\Weee\Test\Unit\Plugin\Ui\DataProvider; + +use Magento\Catalog\Ui\DataProvider\Product\Listing\DataProvider; +use Magento\Framework\App\Config; +use Magento\Framework\TestFramework\Unit\Helper\ObjectManager; +use Magento\Weee\Model\Config as WeeeConfig; +use Magento\Weee\Plugin\Ui\DataProvider\WeeeSettings; +use PHPUnit\Framework\MockObject\MockObject; +use PHPUnit\Framework\TestCase; + +class WeeeSettingsTest extends TestCase +{ + /** + * Stub settings fpt display product list + */ + private const STUB_FPT_DISPLAY_PRODUCT_LIST = '1'; + + /** + * @var WeeeSettings + */ + private $plugin; + + /** + * @var DataProvider|MockObject + */ + protected $subjectMock; + + /** + * @var Config|MockObject + */ + private $configMock; + + /** + * Prepare environment for test + */ + protected function setUp() + { + $this->configMock = $this->createMock(Config::class); + $this->subjectMock = $this->createMock(DataProvider::class); + + $objectManager = new ObjectManager($this); + $this->plugin = $objectManager->getObject( + WeeeSettings::class, + [ + 'config' => $this->configMock + ] + ); + } + + /** + * Test plugin afterGetData + */ + public function testAfterGetDataWhenConfigIsYesResultIsEmpty() + { + $this->configMock->expects($this->any())->method('getValue') + ->with(WeeeConfig::XML_PATH_FPT_DISPLAY_PRODUCT_LIST) + ->willReturn(self::STUB_FPT_DISPLAY_PRODUCT_LIST); + + $this->assertEquals( + [ + 'displayWeee' => self::STUB_FPT_DISPLAY_PRODUCT_LIST + ], + $this->plugin->afterGetData($this->subjectMock, []) + ); + } +} diff --git a/app/code/Magento/Weee/etc/adminhtml/system.xml b/app/code/Magento/Weee/etc/adminhtml/system.xml index d3e9efb8f0b46..68ceae482daaa 100644 --- a/app/code/Magento/Weee/etc/adminhtml/system.xml +++ b/app/code/Magento/Weee/etc/adminhtml/system.xml @@ -10,39 +10,57 @@ <section id="tax"> <group id="weee" translate="label" sortOrder="100" showInDefault="1" showInWebsite="1" showInStore="1"> <label>Fixed Product Taxes</label> - <field id="enable" translate="label" type="select" sortOrder="1" showInDefault="1" showInWebsite="1" showInStore="0" canRestore="1"> + <field id="enable" translate="label" type="select" sortOrder="1" showInDefault="1" showInWebsite="1" canRestore="1"> <label>Enable FPT</label> <source_model>Magento\Config\Model\Config\Source\Yesno</source_model> </field> - <field id="display_list" translate="label" type="select" sortOrder="10" showInDefault="1" showInWebsite="1" showInStore="0" canRestore="1"> + <field id="display_list" translate="label" type="select" sortOrder="10" showInDefault="1" showInWebsite="1" canRestore="1"> <label>Display Prices In Product Lists</label> <source_model>Magento\Weee\Model\Config\Source\Display</source_model> + <depends> + <field id="enable">1</field> + </depends> </field> - <field id="display" translate="label" type="select" sortOrder="20" showInDefault="1" showInWebsite="1" showInStore="0" canRestore="1"> + <field id="display" translate="label" type="select" sortOrder="20" showInDefault="1" showInWebsite="1" canRestore="1"> <label>Display Prices On Product View Page</label> <source_model>Magento\Weee\Model\Config\Source\Display</source_model> + <depends> + <field id="enable">1</field> + </depends> </field> - <field id="display_sales" translate="label" type="select" sortOrder="30" showInDefault="1" showInWebsite="1" showInStore="0" canRestore="1"> + <field id="display_sales" translate="label" type="select" sortOrder="30" showInDefault="1" showInWebsite="1" canRestore="1"> <label>Display Prices In Sales Modules</label> <source_model>Magento\Weee\Model\Config\Source\Display</source_model> + <depends> + <field id="enable">1</field> + </depends> </field> - <field id="display_email" translate="label" type="select" sortOrder="40" showInDefault="1" showInWebsite="1" showInStore="0" canRestore="1"> + <field id="display_email" translate="label" type="select" sortOrder="40" showInDefault="1" showInWebsite="1" canRestore="1"> <label>Display Prices In Emails</label> <source_model>Magento\Weee\Model\Config\Source\Display</source_model> + <depends> + <field id="enable">1</field> + </depends> </field> - <field id="apply_vat" translate="label" type="select" sortOrder="60" showInDefault="1" showInWebsite="1" showInStore="0" canRestore="1"> + <field id="apply_vat" translate="label" type="select" sortOrder="60" showInDefault="1" showInWebsite="1" canRestore="1"> <label>Apply Tax To FPT</label> <source_model>Magento\Config\Model\Config\Source\Yesno</source_model> + <depends> + <field id="enable">1</field> + </depends> </field> - <field id="include_in_subtotal" translate="label" type="select" sortOrder="70" showInDefault="1" showInWebsite="1" showInStore="0" canRestore="1"> + <field id="include_in_subtotal" translate="label" type="select" sortOrder="70" showInDefault="1" showInWebsite="1" canRestore="1"> <label>Include FPT In Subtotal</label> <source_model>Magento\Config\Model\Config\Source\Yesno</source_model> + <depends> + <field id="enable">1</field> + </depends> </field> </group> </section> <section id="sales"> <group id="totals_sort"> - <field id="weee" translate="label" type="text" sortOrder="4" showInDefault="1" showInWebsite="1" showInStore="0" canRestore="1"> + <field id="weee" translate="label" type="text" sortOrder="4" showInDefault="1" showInWebsite="1" canRestore="1"> <label>Fixed Product Tax</label> <validate>required-number validate-number</validate> </field> diff --git a/app/code/Magento/Weee/etc/frontend/di.xml b/app/code/Magento/Weee/etc/frontend/di.xml index 1ce09205b6e77..f6417fd86a3e1 100644 --- a/app/code/Magento/Weee/etc/frontend/di.xml +++ b/app/code/Magento/Weee/etc/frontend/di.xml @@ -13,7 +13,7 @@ </argument> </arguments> </type> - <type name="Magento\Framework\App\Action\AbstractAction"> + <type name="Magento\Framework\App\ActionInterface"> <plugin name="weee-app-action-dispatchController-context-plugin" type="Magento\Weee\Model\App\Action\ContextPlugin"/> </type> diff --git a/app/code/Magento/Weee/registration.php b/app/code/Magento/Weee/registration.php index 874705ab5c6b1..73623ce882acb 100644 --- a/app/code/Magento/Weee/registration.php +++ b/app/code/Magento/Weee/registration.php @@ -4,6 +4,6 @@ * See COPYING.txt for license details. */ -use \Magento\Framework\Component\ComponentRegistrar; +use Magento\Framework\Component\ComponentRegistrar; ComponentRegistrar::register(ComponentRegistrar::MODULE, 'Magento_Weee', __DIR__); diff --git a/app/code/Magento/WeeeGraphQl/Test/Unit/FixedProductTaxTest.php b/app/code/Magento/WeeeGraphQl/Test/Unit/FixedProductTaxTest.php new file mode 100644 index 0000000000000..b289c7d1dac55 --- /dev/null +++ b/app/code/Magento/WeeeGraphQl/Test/Unit/FixedProductTaxTest.php @@ -0,0 +1,150 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +declare(strict_types=1); + +namespace Magento\WeeeGraphQl\Test\Unit; + +use Magento\Framework\DataObject; +use Magento\Framework\Exception\LocalizedException; +use Magento\Framework\GraphQl\Query\Resolver\ContextInterface; +use Magento\Framework\GraphQl\Config\Element\Field; +use Magento\Framework\GraphQl\Schema\Type\ResolveInfo; +use Magento\Framework\TestFramework\Unit\Helper\ObjectManager; +use Magento\GraphQl\Model\Query\ContextExtensionInterface; +use Magento\Weee\Helper\Data as WeeeHelper; +use Magento\WeeeGraphQl\Model\Resolver\FixedProductTax; +use PHPUnit\Framework\TestCase; +use PHPUnit_Framework_MockObject_MockObject as MockObject; + +class FixedProductTaxTest extends TestCase +{ + const STUB_STORE_ID = 1; + + /** + * @var MockObject|ContextInterface + */ + private $contextMock; + + /** + * @var MockObject|ContextExtensionInterface + */ + private $extensionAttributesMock; + + /** + * @var FixedProductTax + */ + private $resolver; + + /** + * @var MockObject|WeeeHelper + */ + private $weeeHelperMock; + + /** + * @var MockObject|DataObject + */ + private $productMock; + + /** + * Build the Testing Environment + */ + protected function setUp() + { + $this->contextMock = $this->getMockBuilder(ContextInterface::class) + ->setMethods(['getExtensionAttributes']) + ->getMock(); + + $this->extensionAttributesMock = $this->getMockBuilder(ContextExtensionInterface::class) + ->setMethods(['getStore', 'setStore', 'getIsCustomer', 'setIsCustomer']) + ->getMock(); + + $this->contextMock->method('getExtensionAttributes') + ->willReturn($this->extensionAttributesMock); + + $this->productMock = $this->getMockBuilder(DataObject::class) + ->disableOriginalConstructor() + ->getMock(); + + $this->weeeHelperMock = $this->getMockBuilder(WeeeHelper::class) + ->disableOriginalConstructor() + ->setMethods(['isEnabled', 'getProductWeeeAttributesForDisplay']) + ->getMock(); + + $objectManager = new ObjectManager($this); + $this->resolver = $objectManager->getObject(FixedProductTax::class, [ + 'weeeHelper' => $this->weeeHelperMock + ]); + } + + /** + * Verifies if the Exception is being thrown when no Product Model passed to resolver + */ + public function testExceptionWhenNoModelSpecified(): void + { + $this->expectException(LocalizedException::class); + $this->expectExceptionMessageRegExp('/value should be specified/'); + + $this->resolver->resolve( + $this->getFieldStub(), + null, + $this->getResolveInfoStub() + ); + } + + /** + * Verifies that Attributes for display are not being fetched if feature not enabled in store + */ + public function testNotGettingAttributesWhenWeeeDisabledForStore(): void + { + // Given + $this->extensionAttributesMock->method('getStore') + ->willreturn(self::STUB_STORE_ID); + + // When + $this->weeeHelperMock->method('isEnabled') + ->with(self::STUB_STORE_ID) + ->willReturn(false); + + // Then + $this->weeeHelperMock->expects($this->never()) + ->method('getProductWeeeAttributesForDisplay'); + + $this->resolver->resolve( + $this->getFieldStub(), + $this->contextMock, + $this->getResolveInfoStub(), + ['model' => $this->productMock] + ); + } + + /** + * Returns stub for Field + * + * @return MockObject|Field + */ + private function getFieldStub(): Field + { + /** @var MockObject|Field $fieldMock */ + $fieldMock = $this->getMockBuilder(Field::class) + ->disableOriginalConstructor() + ->getMock(); + return $fieldMock; + } + + /** + * Returns stub for ResolveInfo + * + * @return MockObject|ResolveInfo + */ + private function getResolveInfoStub(): ResolveInfo + { + /** @var MockObject|ResolveInfo $resolveInfoMock */ + $resolveInfoMock = $this->getMockBuilder(ResolveInfo::class) + ->disableOriginalConstructor() + ->getMock(); + return $resolveInfoMock; + } +} diff --git a/app/code/Magento/Widget/Test/Mftf/ActionGroup/AdminMassDeleteWidgetActionGroup.xml b/app/code/Magento/Widget/Test/Mftf/ActionGroup/AdminMassDeleteWidgetActionGroup.xml new file mode 100644 index 0000000000000..e93f99fd475fd --- /dev/null +++ b/app/code/Magento/Widget/Test/Mftf/ActionGroup/AdminMassDeleteWidgetActionGroup.xml @@ -0,0 +1,49 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + /** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +--> + +<actionGroups xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/actionGroupSchema.xsd"> + <actionGroup name="AdminMassDeleteWidgetActionGroup"> + <annotations> + <description>Goes to the Admin Widgets list page. Mass delete widgets.</description> + </annotations> + <arguments> + <argument name="row" type="string"/> + </arguments> + + <amOnPage url="{{AdminWidgetsPage.url}}" stepKey="visitAdminWidetPage"/> + <conditionalClick selector="{{AdminDataGridHeaderSection.clearFilters}}" + dependentSelector="{{AdminDataGridHeaderSection.clearFilters}}" + visible="true" stepKey="clickClearFilters"/> + + <waitForLoadingMaskToDisappear stepKey="waitForLoadingMaskToDisappear1"/> + <waitForPageLoad stepKey="waitForPageLoad"/> + <click selector="{{AdminWidgetsSection.massActionSelect}}" stepKey="massActionSelectClick"/> + <click selector="{{AdminWidgetsSection.massActionSelectOptionAll}}" stepKey="massActionSelectOptionAllClick"/> + <waitForLoadingMaskToDisappear stepKey="waitForLoadingMaskToDisappear2"/> + <click selector="{{AdminWidgetsSection.massActionSelectAction}}" stepKey="massActionSelectActionClick"/> + <click selector="{{AdminWidgetsSection.massActionSelectActionDelete}}" stepKey="massActionSelectActionDeleteClick"/> + <waitForLoadingMaskToDisappear stepKey="waitForLoadingMaskToDisappear3"/> + <click selector="{{AdminWidgetsSection.massActionSelectActionDeleteSubmit}}" stepKey="massActionSelectActionDeleteSubmitClick1"/> + <waitForLoadingMaskToDisappear stepKey="waitForLoadingMaskToDisappear4"/> + <seeElement selector="{{AdminWidgetsSection.WidgetViewModalDismiss}}" stepKey="widgetViewModalDismissSeeElement"/> + <waitForPageLoad stepKey="waitForPageLoad3"/> + <seeElement selector="{{AdminWidgetsSection.WidgetViewModalClose}}" stepKey="widgetViewModalCloseSeeElement"/> + <click selector="{{AdminWidgetsSection.WidgetViewModalDismiss}}" stepKey="widgetViewModalDismissClick"/> + <waitForElementNotVisible selector="{{AdminWidgetsSection.WidgetViewModalDismiss}}" stepKey="waitForModalClosed"/> + <seeElement selector="{{AdminWidgetsSection.WidgetViewGridInstanceId(row)}}" stepKey="widgetViewGridRowSeeElement"/> + <waitForPageLoad stepKey="waitForPageLoad4"/> + <waitForElementVisible selector="{{AdminWidgetsSection.WidgetViewGridInstanceId(row)}}" stepKey="widgetViewGridInstanceIdWaitForElementVisible"/> + <click selector="{{AdminWidgetsSection.massActionSelectActionDeleteSubmit}}" stepKey="massActionSelectActionDeleteSubmitClick2"/> + <waitForElementVisible selector="{{AdminWidgetsSection.WidgetViewModalAccept}}" stepKey="waitForModalVisible"/> + <click selector="{{AdminWidgetsSection.WidgetViewModalAccept}}" stepKey="widgetViewModalAcceptClick"/> + <waitForPageLoad stepKey="waitForPageLoad5" /> + <waitForElementVisible selector="{{AdminWidgetsSection.WidgetViewGridInstanceRow(row)}}" stepKey="widgetViewGridInstanceRowWaitForElementVisible"/> + <dontSeeElement selector="{{AdminWidgetsSection.WidgetViewGridInstanceId(row)}}" stepKey="widgetViewGridRowDontSeeElement"/> + </actionGroup> +</actionGroups> diff --git a/app/code/Magento/Widget/Test/Mftf/Data/WidgetData.xml b/app/code/Magento/Widget/Test/Mftf/Data/WidgetData.xml index 4c6e98aafd765..9beb5d1da1b1c 100644 --- a/app/code/Magento/Widget/Test/Mftf/Data/WidgetData.xml +++ b/app/code/Magento/Widget/Test/Mftf/Data/WidgetData.xml @@ -11,9 +11,14 @@ <entity name="WidgetWithBlock" type="widget"> <data key="type">CMS Static Block</data> <data key="designTheme">Magento Luma</data> + <data key="design_theme">Magento Luma</data> <data key="name" unique="suffix">testName</data> <data key="store_id">All Store Views</data> + <array key="store_ids"> + <item>All Store Views</item> + </array> <data key="display">All Pages</data> <data key="container">Page Top</data> + <data key="display_on">All Pages</data> </entity> </entities> diff --git a/app/code/Magento/Widget/Test/Mftf/Section/AdminWidgetsSection.xml b/app/code/Magento/Widget/Test/Mftf/Section/AdminWidgetsSection.xml index 162f4e9c41064..f6ca1b25a90f2 100644 --- a/app/code/Magento/Widget/Test/Mftf/Section/AdminWidgetsSection.xml +++ b/app/code/Magento/Widget/Test/Mftf/Section/AdminWidgetsSection.xml @@ -13,5 +13,16 @@ <element name="searchButton" type="button" selector=".action-default.scalable.action-secondary"/> <element name="searchResult" type="text" selector="#widgetInstanceGrid_table>tbody>tr:nth-child(1)"/> <element name="resetFilter" type="button" selector="button[data-action='grid-filter-reset']"/> + <element name="massActionSelect" type="select" selector="#widgetInstanceGrid_massaction-mass-select"/> + <element name="massActionSelectOptionAll" type="select" selector="//*[@id='widgetInstanceGrid_massaction-mass-select']//option[@value='selectAll']"/> + <element name="massActionSelectAction" type="multiselect" selector="//*[@id='widgetInstanceGrid_massaction-select']//option[contains(., 'Action')]" /> + <element name="massActionSelectActionDelete" type="multiselect" selector="//*[@id='widgetInstanceGrid_massaction-select']//option[@value='delete']" /> + <element name="massActionSelectActionDeleteSubmit" type="button" selector="#widgetInstanceGrid_massaction-form button.action-default"/> + <element name="WidgetViewModalAccept" type="button" selector=".modal-popup.confirm._show .action-accept"/> + <element name="WidgetViewModalDismiss" type="button" selector=".modal-popup.confirm._show .action-dismiss"/> + <element name="WidgetViewModalClose" type="button" selector=".modal-popup.confirm._show .action-close"/> + <element name="WidgetViewGridRow" type="text" selector="table.data-grid tbody tr[data-role=row]:nth-of-type({{row}})" parameterized="true"/> + <element name="WidgetViewGridInstanceRow" type="text" selector="table.data-grid tbody tr[data-role=row]:nth-of-type({{row}})" parameterized="true"/> + <element name="WidgetViewGridInstanceId" type="text" selector="table.data-grid tbody tr[data-role=row]:nth-of-type({{row}}) td[data-column=instance_id]" parameterized="true"/> </section> </sections> diff --git a/app/code/Magento/Widget/Test/Mftf/Test/AdminContentWidgetsMassDeletesTest.xml b/app/code/Magento/Widget/Test/Mftf/Test/AdminContentWidgetsMassDeletesTest.xml new file mode 100644 index 0000000000000..6e83687207341 --- /dev/null +++ b/app/code/Magento/Widget/Test/Mftf/Test/AdminContentWidgetsMassDeletesTest.xml @@ -0,0 +1,55 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + /** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +--> + +<tests xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/testSchema.xsd"> + <test name="AdminContentWidgetsMassDeletesTest"> + <annotations> + <features value="Widget"/> + <stories value="Widget mass delete"/> + <title value="Admin mass delete widgets in grid"/> + <description value="Admin select widgets in grid and try to mass delete action, will show pop-up with confirm action"/> + <severity value="MAJOR"/> + <group value="widget"/> + </annotations> + <before> + <actionGroup ref="AdminLoginActionGroup" stepKey="LoginAsAdmin"/> + </before> + <after> + <actionGroup ref="AdminLogoutActionGroup" stepKey="logout"/> + </after> + <actionGroup ref="AdminNavigateMenuActionGroup" stepKey="navigateToContentWidgetsPageFirst"> + <argument name="menuUiId" value="{{AdminMenuContent.dataUiId}}"/> + <argument name="submenuUiId" value="{{AdminMenuContentElementsWidgets.dataUiId}}"/> + </actionGroup> + <actionGroup ref="AdminAssertPageTitleActionGroup" stepKey="seePageTitleFirst"> + <argument name="title" value="{{AdminMenuContentElementsWidgets.pageTitle}}"/> + </actionGroup> + <waitForLoadingMaskToDisappear stepKey="waitForLoadingMaskToDisappear1"/> + <actionGroup ref="AdminCreateAndSaveWidgetActionGroup" stepKey="addWidgetForTest1"> + <argument name="widget" value="ProductsListWidget"/> + </actionGroup> + <waitForLoadingMaskToDisappear stepKey="waitForLoadingMaskToDisappear2"/> + <actionGroup ref="AdminCreateAndSaveWidgetActionGroup" stepKey="addWidgetForTest2"> + <argument name="widget" value="ProductsListWidget"/> + </actionGroup> + <waitForLoadingMaskToDisappear stepKey="waitForLoadingMaskToDisappear4"/> + <actionGroup ref="AdminNavigateMenuActionGroup" stepKey="navigateToContentWidgetsPageSecond"> + <argument name="menuUiId" value="{{AdminMenuContent.dataUiId}}"/> + <argument name="submenuUiId" value="{{AdminMenuContentElementsWidgets.dataUiId}}"/> + </actionGroup> + <actionGroup ref="AdminAssertPageTitleActionGroup" stepKey="seePageTitleSecond"> + <argument name="title" value="{{AdminMenuContentElementsWidgets.pageTitle}}"/> + </actionGroup> + <waitForPageLoad stepKey="waitForPageLoad1"/> + <actionGroup ref="AdminMassDeleteWidgetActionGroup" stepKey="massWidgetDelete"> + <argument name="row" value="1"/> + </actionGroup> + </test> +</tests> + diff --git a/app/code/Magento/Widget/Test/Mftf/Test/AdminContentWidgetsNavigateMenuTest.xml b/app/code/Magento/Widget/Test/Mftf/Test/AdminContentWidgetsNavigateMenuTest.xml index f5af024ec1d51..b599feeef76db 100644 --- a/app/code/Magento/Widget/Test/Mftf/Test/AdminContentWidgetsNavigateMenuTest.xml +++ b/app/code/Magento/Widget/Test/Mftf/Test/AdminContentWidgetsNavigateMenuTest.xml @@ -20,10 +20,10 @@ <group value="mtf_migrated"/> </annotations> <before> - <actionGroup ref="LoginAsAdmin" stepKey="LoginAsAdmin"/> + <actionGroup ref="AdminLoginActionGroup" stepKey="LoginAsAdmin"/> </before> <after> - <actionGroup ref="logout" stepKey="logout"/> + <actionGroup ref="AdminLogoutActionGroup" stepKey="logout"/> </after> <actionGroup ref="AdminNavigateMenuActionGroup" stepKey="navigateToContentWidgetsPage"> <argument name="menuUiId" value="{{AdminMenuContent.dataUiId}}"/> diff --git a/app/code/Magento/Widget/Test/Mftf/Test/NewProductsListWidgetTest.xml b/app/code/Magento/Widget/Test/Mftf/Test/NewProductsListWidgetTest.xml index e2fb9cb50f88a..04218c76a4b7b 100644 --- a/app/code/Magento/Widget/Test/Mftf/Test/NewProductsListWidgetTest.xml +++ b/app/code/Magento/Widget/Test/Mftf/Test/NewProductsListWidgetTest.xml @@ -17,17 +17,17 @@ <stories value="New products list widget"/> <title value="Admin should be able to set products as new so that they show up in the Catalog New Products List Widget"/> <description value="Admin should be able to set products as new so that they show up in the Catalog New Products List Widget"/> - <severity value="MAJOR"/> + <severity value="BLOCKER"/> <group value="Widget"/> </annotations> <before> - <actionGroup ref="LoginAsAdmin" stepKey="loginAsAdmin"/> + <actionGroup ref="AdminLoginActionGroup" stepKey="loginAsAdmin"/> <actionGroup ref="DisabledWYSIWYGActionGroup" stepKey="disableWYSIWYG"/> </before> <after> - <actionGroup ref="logout" stepKey="logout"/> + <actionGroup ref="AdminLogoutActionGroup" stepKey="logout"/> </after> <!-- Create a CMS page containing the New Products widget --> diff --git a/app/code/Magento/Widget/Test/Mftf/Test/ProductsListWidgetTest.xml b/app/code/Magento/Widget/Test/Mftf/Test/ProductsListWidgetTest.xml index 4407991ff5a93..30b4f46c791e8 100644 --- a/app/code/Magento/Widget/Test/Mftf/Test/ProductsListWidgetTest.xml +++ b/app/code/Magento/Widget/Test/Mftf/Test/ProductsListWidgetTest.xml @@ -6,15 +6,14 @@ */ --> -<tests xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" - xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/testSchema.xsd"> +<tests xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/testSchema.xsd"> <test name="ProductsListWidgetTest"> <annotations> <features value="Widget"/> <stories value="Products list widget"/> <title value="Admin should be able to set Products List Widget"/> <description value="Admin should be able to set Products List Widget"/> - <severity value="CRITICAL"/> + <severity value="BLOCKER"/> <testCaseId value="MAGETWO-97041"/> <group value="Widget"/> <group value="WYSIWYGDisabled"/> @@ -24,11 +23,11 @@ <createData entity="ApiSimpleProduct" stepKey="createSimpleProduct"> <requiredEntity createDataKey="createCategory"/> </createData> - <actionGroup ref="LoginAsAdmin" stepKey="loginAsAdmin"/> + <actionGroup ref="AdminLoginActionGroup" stepKey="loginAsAdmin"/> </before> <after> <conditionalClick selector="{{CmsPagesPageActionsSection.clearAllButton}}" dependentSelector="{{CmsPagesPageActionsSection.activeFilters}}" stepKey="clickToResetFilter" visible="true"/> - <actionGroup ref="logout" stepKey="logout"/> + <actionGroup ref="AdminLogoutActionGroup" stepKey="logout"/> <deleteData createDataKey="createCategory" stepKey="deleteCategory"/> <deleteData createDataKey="createSimpleProduct" stepKey="deleteSimpleProduct"/> </after> @@ -61,8 +60,10 @@ <click selector="{{CmsPagesPageActionsSection.select(_newDefaultCmsPage.title)}}" stepKey="clickSelect" /> <waitForElementVisible selector="{{CmsPagesPageActionsSection.edit(_newDefaultCmsPage.title)}}" stepKey="waitForEditLink" /> <click selector="{{CmsPagesPageActionsSection.preview(_newDefaultCmsPage.title)}}" stepKey="clickEdit" /> + <switchToNextTab stepKey="switchToNextTab"/> <waitForPageLoad stepKey="waitForCMSPage"/> <seeInTitle userInput="{{_newDefaultCmsPage.title}}" stepKey="seePageTitle"/> <see userInput="{{_newDefaultCmsPage.title}}" stepKey="seeProduct"/> + <closeTab stepKey="closeCurrentTab"/> </test> -</tests> \ No newline at end of file +</tests> diff --git a/app/code/Magento/Widget/Test/Unit/Block/Adminhtml/Widget/Catalog/Category/ChooserTest.php b/app/code/Magento/Widget/Test/Unit/Block/Adminhtml/Widget/Catalog/Category/ChooserTest.php index 87260305a3d0f..53b0044d82f04 100644 --- a/app/code/Magento/Widget/Test/Unit/Block/Adminhtml/Widget/Catalog/Category/ChooserTest.php +++ b/app/code/Magento/Widget/Test/Unit/Block/Adminhtml/Widget/Catalog/Category/ChooserTest.php @@ -9,52 +9,52 @@ class ChooserTest extends \PHPUnit\Framework\TestCase { /** - * @var \Magento\Catalog\Model\ResourceModel\Category\Collection|\PHPUnit_Framework_MockObject_MockObject + * @var \Magento\Catalog\Model\ResourceModel\Category\Collection|\PHPUnit\Framework\MockObject\MockObject */ protected $collection; /** - * @var \Magento\Framework\Data\Tree\Node|\PHPUnit_Framework_MockObject_MockObject + * @var \Magento\Framework\Data\Tree\Node|\PHPUnit\Framework\MockObject\MockObject */ protected $childNode; /** - * @var \Magento\Framework\Data\Tree\Node|\PHPUnit_Framework_MockObject_MockObject + * @var \Magento\Framework\Data\Tree\Node|\PHPUnit\Framework\MockObject\MockObject */ protected $rootNode; /** - * @var \Magento\Catalog\Model\ResourceModel\Category\Tree|\PHPUnit_Framework_MockObject_MockObject + * @var \Magento\Catalog\Model\ResourceModel\Category\Tree|\PHPUnit\Framework\MockObject\MockObject */ protected $categoryTree; /** - * @var \Magento\Store\Model\Store|\PHPUnit_Framework_MockObject_MockObject + * @var \Magento\Store\Model\Store|\PHPUnit\Framework\MockObject\MockObject */ protected $store; /** - * @var \Magento\Store\Model\StoreManagerInterface|\PHPUnit_Framework_MockObject_MockObject + * @var \Magento\Store\Model\StoreManagerInterface|\PHPUnit\Framework\MockObject\MockObject */ protected $storeManager; /** - * @var \Magento\Framework\App\RequestInterface|\PHPUnit_Framework_MockObject_MockObject + * @var \Magento\Framework\App\RequestInterface|\PHPUnit\Framework\MockObject\MockObject */ protected $request; /** - * @var \Magento\Framework\Escaper|\PHPUnit_Framework_MockObject_MockObject + * @var \Magento\Framework\Escaper|\PHPUnit\Framework\MockObject\MockObject */ protected $escaper; /** - * @var \Magento\Framework\Event\ManagerInterface|\PHPUnit_Framework_MockObject_MockObject + * @var \Magento\Framework\Event\ManagerInterface|\PHPUnit\Framework\MockObject\MockObject */ protected $eventManager; /** - * @var \Magento\Backend\Block\Template\Context|\PHPUnit_Framework_MockObject_MockObject + * @var \Magento\Backend\Block\Template\Context|\PHPUnit\Framework\MockObject\MockObject */ protected $context; diff --git a/app/code/Magento/Widget/Test/Unit/Block/Adminhtml/Widget/Instance/Edit/Chooser/AbstractContainerTest.php b/app/code/Magento/Widget/Test/Unit/Block/Adminhtml/Widget/Instance/Edit/Chooser/AbstractContainerTest.php index c84e56d5869bb..ab1c73596201c 100644 --- a/app/code/Magento/Widget/Test/Unit/Block/Adminhtml/Widget/Instance/Edit/Chooser/AbstractContainerTest.php +++ b/app/code/Magento/Widget/Test/Unit/Block/Adminhtml/Widget/Instance/Edit/Chooser/AbstractContainerTest.php @@ -10,47 +10,47 @@ abstract class AbstractContainerTest extends \PHPUnit\Framework\TestCase { /** - * @var \Magento\Framework\Event\Manager|\PHPUnit_Framework_MockObject_MockObject + * @var \Magento\Framework\Event\Manager|\PHPUnit\Framework\MockObject\MockObject */ protected $eventManagerMock; /** - * @var \Magento\Framework\App\Config\ScopeConfigInterface|\PHPUnit_Framework_MockObject_MockObject + * @var \Magento\Framework\App\Config\ScopeConfigInterface|\PHPUnit\Framework\MockObject\MockObject */ protected $scopeConfigMock; /** - * @var \Magento\Backend\Block\Context|\PHPUnit_Framework_MockObject_MockObject + * @var \Magento\Backend\Block\Context|\PHPUnit\Framework\MockObject\MockObject */ protected $contextMock; /** - * @var \Magento\Theme\Model\ResourceModel\Theme\Collection|\PHPUnit_Framework_MockObject_MockObject + * @var \Magento\Theme\Model\ResourceModel\Theme\Collection|\PHPUnit\Framework\MockObject\MockObject */ protected $themeCollectionMock; /** - * @var \Magento\Theme\Model\ResourceModel\Theme\CollectionFactory|\PHPUnit_Framework_MockObject_MockObject + * @var \Magento\Theme\Model\ResourceModel\Theme\CollectionFactory|\PHPUnit\Framework\MockObject\MockObject */ protected $themeCollectionFactoryMock; /** - * @var \Magento\Theme\Model\Theme|\PHPUnit_Framework_MockObject_MockObject + * @var \Magento\Theme\Model\Theme|\PHPUnit\Framework\MockObject\MockObject */ protected $themeMock; /** - * @var \Magento\Framework\View\Layout\ProcessorFactory|\PHPUnit_Framework_MockObject_MockObject + * @var \Magento\Framework\View\Layout\ProcessorFactory|\PHPUnit\Framework\MockObject\MockObject */ protected $layoutProcessorFactoryMock; /** - * @var \Magento\Framework\View\Model\Layout\Merge|\PHPUnit_Framework_MockObject_MockObject + * @var \Magento\Framework\View\Model\Layout\Merge|\PHPUnit\Framework\MockObject\MockObject */ protected $layoutMergeMock; /** - * @var \Magento\Framework\Escaper|\PHPUnit_Framework_MockObject_MockObject + * @var \Magento\Framework\Escaper|\PHPUnit\Framework\MockObject\MockObject */ protected $escaperMock; diff --git a/app/code/Magento/Widget/Test/Unit/Block/Adminhtml/Widget/Instance/Edit/Tab/PropertiesTest.php b/app/code/Magento/Widget/Test/Unit/Block/Adminhtml/Widget/Instance/Edit/Tab/PropertiesTest.php index b83be871de34e..471947e15091f 100644 --- a/app/code/Magento/Widget/Test/Unit/Block/Adminhtml/Widget/Instance/Edit/Tab/PropertiesTest.php +++ b/app/code/Magento/Widget/Test/Unit/Block/Adminhtml/Widget/Instance/Edit/Tab/PropertiesTest.php @@ -8,12 +8,12 @@ class PropertiesTest extends \PHPUnit\Framework\TestCase { /** - * @var \PHPUnit_Framework_MockObject_MockObject + * @var \PHPUnit\Framework\MockObject\MockObject */ protected $widget; /** - * @var \PHPUnit_Framework_MockObject_MockObject + * @var \PHPUnit\Framework\MockObject\MockObject */ protected $registry; diff --git a/app/code/Magento/Widget/Test/Unit/Controller/Adminhtml/Widget/Instance/CategoriesTest.php b/app/code/Magento/Widget/Test/Unit/Controller/Adminhtml/Widget/Instance/CategoriesTest.php index 5e88a6e356076..5e3600767b302 100644 --- a/app/code/Magento/Widget/Test/Unit/Controller/Adminhtml/Widget/Instance/CategoriesTest.php +++ b/app/code/Magento/Widget/Test/Unit/Controller/Adminhtml/Widget/Instance/CategoriesTest.php @@ -9,17 +9,17 @@ class CategoriesTest extends \PHPUnit\Framework\TestCase { /** - * @var \Magento\Framework\App\RequestInterface|\PHPUnit_Framework_MockObject_MockObject + * @var \Magento\Framework\App\RequestInterface|\PHPUnit\Framework\MockObject\MockObject */ protected $request; /** - * @var \Magento\Framework\Math\Random|\PHPUnit_Framework_MockObject_MockObject + * @var \Magento\Framework\Math\Random|\PHPUnit\Framework\MockObject\MockObject */ protected $mathRandom; /** - * @var \Magento\Framework\View\Layout|\PHPUnit_Framework_MockObject_MockObject + * @var \Magento\Framework\View\Layout|\PHPUnit\Framework\MockObject\MockObject */ protected $chooser; @@ -29,22 +29,22 @@ class CategoriesTest extends \PHPUnit\Framework\TestCase protected $blockClass = \Magento\Widget\Block\Adminhtml\Widget\Catalog\Category\Chooser::class; /** - * @var \Magento\Framework\View\Layout|\PHPUnit_Framework_MockObject_MockObject + * @var \Magento\Framework\View\Layout|\PHPUnit\Framework\MockObject\MockObject */ protected $layout; /** - * @var \Magento\Framework\Controller\Result\Raw|\PHPUnit_Framework_MockObject_MockObject + * @var \Magento\Framework\Controller\Result\Raw|\PHPUnit\Framework\MockObject\MockObject */ protected $resultRaw; /** - * @var \Magento\Framework\Controller\ResultFactory|\PHPUnit_Framework_MockObject_MockObject + * @var \Magento\Framework\Controller\ResultFactory|\PHPUnit\Framework\MockObject\MockObject */ protected $resultFactory; /** - * @var \Magento\Backend\App\Action\Context|\PHPUnit_Framework_MockObject_MockObject + * @var \Magento\Backend\App\Action\Context|\PHPUnit\Framework\MockObject\MockObject */ protected $context; diff --git a/app/code/Magento/Widget/Test/Unit/Controller/Adminhtml/Widget/LoadOptionsTest.php b/app/code/Magento/Widget/Test/Unit/Controller/Adminhtml/Widget/LoadOptionsTest.php index e3be77193be01..79f75a1807ce9 100644 --- a/app/code/Magento/Widget/Test/Unit/Controller/Adminhtml/Widget/LoadOptionsTest.php +++ b/app/code/Magento/Widget/Test/Unit/Controller/Adminhtml/Widget/LoadOptionsTest.php @@ -26,32 +26,32 @@ class LoadOptionsTest extends \PHPUnit\Framework\TestCase private $objectManagerHelper; /** - * @var Context|\PHPUnit_Framework_MockObject_MockObject + * @var Context|\PHPUnit\Framework\MockObject\MockObject */ private $contextMock; /** - * @var ViewInterface|\PHPUnit_Framework_MockObject_MockObject + * @var ViewInterface|\PHPUnit\Framework\MockObject\MockObject */ private $viewMock; /** - * @var ConditionsHelper|\PHPUnit_Framework_MockObject_MockObject + * @var ConditionsHelper|\PHPUnit\Framework\MockObject\MockObject */ private $conditionsHelperMock; /** - * @var ResponseInterface|\PHPUnit_Framework_MockObject_MockObject + * @var ResponseInterface|\PHPUnit\Framework\MockObject\MockObject */ private $responseMock; /** - * @var ObjectManagerInterface|\PHPUnit_Framework_MockObject_MockObject + * @var ObjectManagerInterface|\PHPUnit\Framework\MockObject\MockObject */ private $objectManagerMock; /** - * @var RequestInterface|\PHPUnit_Framework_MockObject_MockObject + * @var RequestInterface|\PHPUnit\Framework\MockObject\MockObject */ private $requestMock; @@ -110,7 +110,7 @@ public function dtestExecuteWithException() $jsonResult = '{"error":true,"message":"Some error"}'; $errorMessage = 'Some error'; - /** @var \Magento\Framework\Json\Helper\Data|\PHPUnit_Framework_MockObject_MockObject $jsonDataHelperMock */ + /** @var \Magento\Framework\Json\Helper\Data|\PHPUnit\Framework\MockObject\MockObject $jsonDataHelperMock */ $jsonDataHelperMock = $this->getMockBuilder(\Magento\Framework\Json\Helper\Data::class) ->disableOriginalConstructor() ->getMock(); @@ -163,7 +163,7 @@ public function testExecute() ], ]; - /** @var \Magento\Framework\Json\Helper\Data|\PHPUnit_Framework_MockObject_MockObject $jsonDataHelperMock */ + /** @var \Magento\Framework\Json\Helper\Data|\PHPUnit\Framework\MockObject\MockObject $jsonDataHelperMock */ $jsonDataHelperMock = $this->getMockBuilder(\Magento\Framework\Json\Helper\Data::class) ->disableOriginalConstructor() ->getMock(); @@ -183,7 +183,7 @@ public function testExecute() ->with(\Magento\Framework\Json\Helper\Data::class) ->willReturn($jsonDataHelperMock); - /** @var \Magento\Framework\View\Element\BlockInterface|\PHPUnit_Framework_MockObject_MockObject $blockMock */ + /** @var \Magento\Framework\View\Element\BlockInterface|\PHPUnit\Framework\MockObject\MockObject $blockMock */ $blockMock = $this->getMockBuilder(\Magento\Framework\View\Element\BlockInterface::class) ->setMethods(['setWidgetType', 'setWidgetValues']) ->getMockForAbstractClass(); @@ -196,7 +196,7 @@ public function testExecute() ->with($resultWidgetArrayParams['values']) ->willReturnSelf(); - /** @var \Magento\Framework\View\LayoutInterface|\PHPUnit_Framework_MockObject_MockObject $layoutMock */ + /** @var \Magento\Framework\View\LayoutInterface|\PHPUnit\Framework\MockObject\MockObject $layoutMock */ $layoutMock = $this->getMockForAbstractClass(\Magento\Framework\View\LayoutInterface::class); $layoutMock->expects($this->once()) ->method('getBlock') diff --git a/app/code/Magento/Widget/Test/Unit/Helper/ConditionsTest.php b/app/code/Magento/Widget/Test/Unit/Helper/ConditionsTest.php index f943a5688b56c..e0865432aedb0 100644 --- a/app/code/Magento/Widget/Test/Unit/Helper/ConditionsTest.php +++ b/app/code/Magento/Widget/Test/Unit/Helper/ConditionsTest.php @@ -11,6 +11,8 @@ /** * Class ConditionsTest + * + * PHPUnit test case for \Magento\Widget\Helper\Conditions */ class ConditionsTest extends \PHPUnit\Framework\TestCase { @@ -20,12 +22,12 @@ class ConditionsTest extends \PHPUnit\Framework\TestCase protected $conditions; /** - * @var \Magento\Framework\Serialize\Serializer\Json|\PHPUnit_Framework_MockObject_MockObject + * @var \Magento\Framework\Serialize\Serializer\Json|\PHPUnit\Framework\MockObject\MockObject */ private $serializer; /** - * @var Normalizer|\PHPUnit_Framework_MockObject_MockObject + * @var Normalizer|\PHPUnit\Framework\MockObject\MockObject */ private $normalizer; diff --git a/app/code/Magento/Widget/Test/Unit/Model/Config/FileResolverTest.php b/app/code/Magento/Widget/Test/Unit/Model/Config/FileResolverTest.php index 301869a50a713..730a9adfb8b93 100644 --- a/app/code/Magento/Widget/Test/Unit/Model/Config/FileResolverTest.php +++ b/app/code/Magento/Widget/Test/Unit/Model/Config/FileResolverTest.php @@ -17,17 +17,17 @@ class FileResolverTest extends \PHPUnit\Framework\TestCase private $object; /** - * @var \Magento\Framework\Module\Dir\Reader|\PHPUnit_Framework_MockObject_MockObject + * @var \Magento\Framework\Module\Dir\Reader|\PHPUnit\Framework\MockObject\MockObject */ private $moduleReader; /** - * @var \Magento\Framework\Config\FileIteratorFactory|\PHPUnit_Framework_MockObject_MockObject + * @var \Magento\Framework\Config\FileIteratorFactory|\PHPUnit\Framework\MockObject\MockObject */ private $factory; /** - * @var \Magento\Framework\Component\DirSearch|\PHPUnit_Framework_MockObject_MockObject + * @var \Magento\Framework\Component\DirSearch|\PHPUnit\Framework\MockObject\MockObject */ private $componentDirSearch; diff --git a/app/code/Magento/Widget/Test/Unit/Model/NamespaceResolverTest.php b/app/code/Magento/Widget/Test/Unit/Model/NamespaceResolverTest.php index 0bf2717a54286..e3b61fe9e0bfc 100644 --- a/app/code/Magento/Widget/Test/Unit/Model/NamespaceResolverTest.php +++ b/app/code/Magento/Widget/Test/Unit/Model/NamespaceResolverTest.php @@ -13,7 +13,7 @@ class NamespaceResolverTest extends \PHPUnit\Framework\TestCase protected $namespaceResolver; /** - * @var \Magento\Framework\Module\ModuleListInterface|\PHPUnit_Framework_MockObject_MockObject + * @var \Magento\Framework\Module\ModuleListInterface|\PHPUnit\Framework\MockObject\MockObject */ protected $moduleListMock; diff --git a/app/code/Magento/Widget/Test/Unit/Model/ResourceModel/Layout/AbstractTestCase.php b/app/code/Magento/Widget/Test/Unit/Model/ResourceModel/Layout/AbstractTestCase.php index 32c4d72c8bccd..4ccb436ca83f4 100644 --- a/app/code/Magento/Widget/Test/Unit/Model/ResourceModel/Layout/AbstractTestCase.php +++ b/app/code/Magento/Widget/Test/Unit/Model/ResourceModel/Layout/AbstractTestCase.php @@ -51,7 +51,7 @@ protected function setUp() * Retrieve resource model instance * * @param \Magento\Framework\DB\Select $select - * @return \PHPUnit_Framework_MockObject_MockObject + * @return \PHPUnit\Framework\MockObject\MockObject */ protected function _getResource(\Magento\Framework\DB\Select $select) { @@ -88,7 +88,7 @@ public function testAddUpdatedDaysBeforeFilter() $collection = $this->_getCollection($select); - /** @var $connection \PHPUnit_Framework_MockObject_MockObject */ + /** @var $connection \PHPUnit\Framework\MockObject\MockObject */ $connection = $collection->getResource()->getConnection(); $connection->expects( $this->any() diff --git a/app/code/Magento/Widget/Test/Unit/Model/ResourceModel/Layout/Link/CollectionTest.php b/app/code/Magento/Widget/Test/Unit/Model/ResourceModel/Layout/Link/CollectionTest.php index 825d2ca50feda..29e35c9524beb 100644 --- a/app/code/Magento/Widget/Test/Unit/Model/ResourceModel/Layout/Link/CollectionTest.php +++ b/app/code/Magento/Widget/Test/Unit/Model/ResourceModel/Layout/Link/CollectionTest.php @@ -52,7 +52,7 @@ public function testAddTemporaryFilter($flag) $collection = $this->_getCollection($select); - /** @var $connection \PHPUnit_Framework_MockObject_MockObject */ + /** @var $connection \PHPUnit\Framework\MockObject\MockObject */ $connection = $collection->getResource()->getConnection(); $connection->expects( $this->any() @@ -97,7 +97,7 @@ public function testJoinWithUpdate() $collection = $this->_getCollection($select); - /** @var $resource \PHPUnit_Framework_MockObject_MockObject */ + /** @var $resource \PHPUnit\Framework\MockObject\MockObject */ $resource = $collection->getResource(); $resource->expects( $this->once() diff --git a/app/code/Magento/Widget/Test/Unit/Model/ResourceModel/Layout/Update/CollectionTest.php b/app/code/Magento/Widget/Test/Unit/Model/ResourceModel/Layout/Update/CollectionTest.php index 3467378eaeb9c..2af3dd4476e3f 100644 --- a/app/code/Magento/Widget/Test/Unit/Model/ResourceModel/Layout/Update/CollectionTest.php +++ b/app/code/Magento/Widget/Test/Unit/Model/ResourceModel/Layout/Update/CollectionTest.php @@ -85,7 +85,7 @@ public function testAddNoLinksFilter() $collection = $this->_getCollection($select); - /** @var $connection \PHPUnit_Framework_MockObject_MockObject */ + /** @var $connection \PHPUnit\Framework\MockObject\MockObject */ $connection = $collection->getResource()->getConnection(); $connection->expects( $this->once() diff --git a/app/code/Magento/Widget/Test/Unit/Model/ResourceModel/Widget/Instance/Options/ThemesTest.php b/app/code/Magento/Widget/Test/Unit/Model/ResourceModel/Widget/Instance/Options/ThemesTest.php index cc87b98b28ed5..6744227b3d188 100644 --- a/app/code/Magento/Widget/Test/Unit/Model/ResourceModel/Widget/Instance/Options/ThemesTest.php +++ b/app/code/Magento/Widget/Test/Unit/Model/ResourceModel/Widget/Instance/Options/ThemesTest.php @@ -20,12 +20,12 @@ class ThemesTest extends \PHPUnit\Framework\TestCase private $model; /** - * @var \PHPUnit_Framework_MockObject_MockObject + * @var \PHPUnit\Framework\MockObject\MockObject */ private $themeCollectionFactoryMock; /** - * @var \PHPUnit_Framework_MockObject_MockObject + * @var \PHPUnit\Framework\MockObject\MockObject */ private $themeCollectionMock; diff --git a/app/code/Magento/Widget/Test/Unit/Model/Template/FilterEmulateTest.php b/app/code/Magento/Widget/Test/Unit/Model/Template/FilterEmulateTest.php index f49ee97818618..4be86a25403e3 100644 --- a/app/code/Magento/Widget/Test/Unit/Model/Template/FilterEmulateTest.php +++ b/app/code/Magento/Widget/Test/Unit/Model/Template/FilterEmulateTest.php @@ -20,7 +20,7 @@ class FilterEmulateTest extends \PHPUnit\Framework\TestCase protected $filterEmulate; /** - * @var \Magento\Framework\App\State|\PHPUnit_Framework_MockObject_MockObject + * @var \Magento\Framework\App\State|\PHPUnit\Framework\MockObject\MockObject */ protected $appStateMock; diff --git a/app/code/Magento/Widget/Test/Unit/Model/Template/FilterTest.php b/app/code/Magento/Widget/Test/Unit/Model/Template/FilterTest.php index db2f468da663c..52995e7168153 100644 --- a/app/code/Magento/Widget/Test/Unit/Model/Template/FilterTest.php +++ b/app/code/Magento/Widget/Test/Unit/Model/Template/FilterTest.php @@ -21,27 +21,27 @@ class FilterTest extends \PHPUnit\Framework\TestCase protected $objectManagerHelper; /** - * @var \Magento\Store\Model\Store|\PHPUnit_Framework_MockObject_MockObject + * @var \Magento\Store\Model\Store|\PHPUnit\Framework\MockObject\MockObject */ protected $storeMock; /** - * @var StoreManagerInterface|\PHPUnit_Framework_MockObject_MockObject + * @var StoreManagerInterface|\PHPUnit\Framework\MockObject\MockObject */ protected $storeManagerMock; /** - * @var \Magento\Widget\Model\ResourceModel\Widget|\PHPUnit_Framework_MockObject_MockObject + * @var \Magento\Widget\Model\ResourceModel\Widget|\PHPUnit\Framework\MockObject\MockObject */ protected $widgetResourceMock; /** - * @var \Magento\Widget\Model\Widget|\PHPUnit_Framework_MockObject_MockObject + * @var \Magento\Widget\Model\Widget|\PHPUnit\Framework\MockObject\MockObject */ protected $widgetMock; /** - * @var \Magento\Framework\View\LayoutInterface|\PHPUnit_Framework_MockObject_MockObject + * @var \Magento\Framework\View\LayoutInterface|\PHPUnit\Framework\MockObject\MockObject */ protected $layoutMock; @@ -235,11 +235,11 @@ protected function generalForGenerateWidget( /** * @param string $returnedResult - * @return \Magento\Widget\Block\BlockInterface|\PHPUnit_Framework_MockObject_MockObject + * @return \Magento\Widget\Block\BlockInterface|\PHPUnit\Framework\MockObject\MockObject */ protected function getBlockMock($returnedResult = '') { - /** @var \Magento\Widget\Block\BlockInterface|\PHPUnit_Framework_MockObject_MockObject $blockMock */ + /** @var \Magento\Widget\Block\BlockInterface|\PHPUnit\Framework\MockObject\MockObject $blockMock */ $blockMock = $this->getMockBuilder(\Magento\Widget\Block\BlockInterface::class) ->setMethods(['toHtml']) ->getMockForAbstractClass(); diff --git a/app/code/Magento/Widget/Test/Unit/Model/Widget/InstanceTest.php b/app/code/Magento/Widget/Test/Unit/Model/Widget/InstanceTest.php index a2a9a88bff774..9baf599134912 100644 --- a/app/code/Magento/Widget/Test/Unit/Model/Widget/InstanceTest.php +++ b/app/code/Magento/Widget/Test/Unit/Model/Widget/InstanceTest.php @@ -15,16 +15,16 @@ class InstanceTest extends \PHPUnit\Framework\TestCase { /** - * @var \Magento\Widget\Model\Config\Data|PHPUnit_Framework_MockObject_MockObject + * @var \Magento\Widget\Model\Config\Data|\PHPUnit\Framework\MockObject\MockObject */ protected $_widgetModelMock; /** - * @var \Magento\Framework\View\FileSystem|PHPUnit_Framework_MockObject_MockObject + * @var \Magento\Framework\View\FileSystem|\PHPUnit\Framework\MockObject\MockObject */ protected $_viewFileSystemMock; - /** @var \Magento\Widget\Model\NamespaceResolver |PHPUnit_Framework_MockObject_MockObject */ + /** @var \Magento\Widget\Model\NamespaceResolver|\PHPUnit\Framework\MockObject\MockObject */ protected $_namespaceResolver; /** @@ -36,16 +36,16 @@ class InstanceTest extends \PHPUnit\Framework\TestCase protected $_readerMock; /** - * @var \PHPUnit_Framework_MockObject_MockObject + * @var \PHPUnit\Framework\MockObject\MockObject */ protected $_cacheTypesListMock; /** - * @var \PHPUnit_Framework_MockObject_MockObject + * @var \PHPUnit\Framework\MockObject\MockObject */ protected $_directoryMock; - /** @var \Magento\Framework\Serialize\Serializer\Json | \PHPUnit_Framework_MockObject_MockObject */ + /** @var \Magento\Framework\Serialize\Serializer\Json|\PHPUnit\Framework\MockObject\MockObject */ private $serializer; protected function setUp() diff --git a/app/code/Magento/Widget/Test/Unit/Model/WidgetTest.php b/app/code/Magento/Widget/Test/Unit/Model/WidgetTest.php index 850a3fbe83211..2228177836695 100644 --- a/app/code/Magento/Widget/Test/Unit/Model/WidgetTest.php +++ b/app/code/Magento/Widget/Test/Unit/Model/WidgetTest.php @@ -13,12 +13,12 @@ class WidgetTest extends \PHPUnit\Framework\TestCase { /** - * @var \Magento\Widget\Model\Config\Data|\PHPUnit_Framework_MockObject_MockObject + * @var \Magento\Widget\Model\Config\Data|\PHPUnit\Framework\MockObject\MockObject */ protected $dataStorageMock; /** - * @var \Magento\Framework\Escaper|\PHPUnit_Framework_MockObject_MockObject + * @var \Magento\Framework\Escaper|\PHPUnit\Framework\MockObject\MockObject */ private $escaperMock; diff --git a/app/code/Magento/Widget/i18n/en_US.csv b/app/code/Magento/Widget/i18n/en_US.csv index 4156b94a1f988..3592cbe9a2042 100644 --- a/app/code/Magento/Widget/i18n/en_US.csv +++ b/app/code/Magento/Widget/i18n/en_US.csv @@ -71,3 +71,4 @@ Product,Product Conditions,Conditions "Widget ID","Widget ID" "Inserting a widget does not create a widget instance.","Inserting a widget does not create a widget instance." +"Are you sure you want to delete the selected widget(s)?","Are you sure you want to delete the selected widget(s)?" diff --git a/app/code/Magento/Widget/registration.php b/app/code/Magento/Widget/registration.php index 27e924eba367e..c7821664fdaca 100644 --- a/app/code/Magento/Widget/registration.php +++ b/app/code/Magento/Widget/registration.php @@ -4,6 +4,6 @@ * See COPYING.txt for license details. */ -use \Magento\Framework\Component\ComponentRegistrar; +use Magento\Framework\Component\ComponentRegistrar; ComponentRegistrar::register(ComponentRegistrar::MODULE, 'Magento_Widget', __DIR__); diff --git a/app/code/Magento/Widget/view/adminhtml/layout/adminhtml_widget_instance_block.xml b/app/code/Magento/Widget/view/adminhtml/layout/adminhtml_widget_instance_block.xml index c78f9ec225be4..1604ac5ba1122 100644 --- a/app/code/Magento/Widget/view/adminhtml/layout/adminhtml_widget_instance_block.xml +++ b/app/code/Magento/Widget/view/adminhtml/layout/adminhtml_widget_instance_block.xml @@ -25,6 +25,7 @@ <item name="label" xsi:type="string" translate="true">Delete</item> <item name="url" xsi:type="string">*/*/massDelete</item> <item name="selected" xsi:type="string">0</item> + <item name="confirm" xsi:type="string" translate="true">Are you sure you want to delete the selected widget(s)?</item> </item> </argument> </arguments> diff --git a/app/code/Magento/Wishlist/Block/Share/Email/Items.php b/app/code/Magento/Wishlist/Block/Share/Email/Items.php index d4e6587fd6519..130c7cb136afb 100644 --- a/app/code/Magento/Wishlist/Block/Share/Email/Items.php +++ b/app/code/Magento/Wishlist/Block/Share/Email/Items.php @@ -4,29 +4,73 @@ * See COPYING.txt for license details. */ -/** - * Wishlist block customer items - * - * @author Magento Core Team <core@magentocommerce.com> - */ namespace Magento\Wishlist\Block\Share\Email; +use Magento\Catalog\Model\Product; +use Magento\Catalog\Model\Product\Configuration\Item\ItemResolverInterface; +use Magento\Catalog\Model\Product\Image\UrlBuilder; +use Magento\Framework\App\ObjectManager; +use Magento\Framework\View\ConfigInterface; +use Magento\Wishlist\Model\Item; + /** + * Wishlist share items + * * @api * @since 100.0.2 */ class Items extends \Magento\Wishlist\Block\AbstractBlock { + /** + * @var ItemResolverInterface + */ + private $itemResolver; + /** * @var string */ protected $_template = 'Magento_Wishlist::email/items.phtml'; + /** + * Items constructor. + * + * @param \Magento\Catalog\Block\Product\Context $context + * @param \Magento\Framework\App\Http\Context $httpContext + * @param array $data + * @param ConfigInterface|null $config + * @param UrlBuilder|null $urlBuilder + * @param ItemResolverInterface|null $itemResolver + */ + public function __construct( + \Magento\Catalog\Block\Product\Context $context, + \Magento\Framework\App\Http\Context $httpContext, + array $data = [], + ConfigInterface $config = null, + UrlBuilder $urlBuilder = null, + ItemResolverInterface $itemResolver = null + ) { + parent::__construct($context, $httpContext, $data, $config, $urlBuilder); + $this->itemResolver = $itemResolver ?? ObjectManager::getInstance()->get(ItemResolverInterface::class); + } + + /** + * Identify the product from which thumbnail should be taken. + * + * @param Item $item + * + * @return Product + */ + public function getProductForThumbnail(Item $item): Product + { + return $this->itemResolver->getFinalProduct($item); + } + /** * Retrieve Product View URL * * @param \Magento\Catalog\Model\Product $product * @param array $additional + * * @return string */ public function getProductUrl($product, $additional = []) @@ -40,6 +84,7 @@ public function getProductUrl($product, $additional = []) * * @param \Magento\Catalog\Model\Product $product * @param array $additional + * * @return string */ public function getAddToCartUrl($product, $additional = []) @@ -53,6 +98,7 @@ public function getAddToCartUrl($product, $additional = []) * Check whether wishlist item has description * * @param \Magento\Wishlist\Model\Item $item + * * @return bool */ public function hasDescription($item) diff --git a/app/code/Magento/Wishlist/Controller/Index/Plugin.php b/app/code/Magento/Wishlist/Controller/Index/Plugin.php index 150e4de72b40d..63ae419759250 100644 --- a/app/code/Magento/Wishlist/Controller/Index/Plugin.php +++ b/app/code/Magento/Wishlist/Controller/Index/Plugin.php @@ -10,6 +10,7 @@ use Magento\Framework\App\Config\ScopeConfigInterface; use Magento\Framework\App\RequestInterface; use Magento\Framework\App\Response\RedirectInterface; +use Magento\Store\Model\ScopeInterface; /** * Wishlist plugin before dispatch @@ -89,7 +90,7 @@ public function beforeDispatch(\Magento\Framework\App\ActionInterface $subject, $this->messageManager->addErrorMessage(__('You must login or register to add items to your wishlist.')); } } - if (!$this->config->isSetFlag('wishlist/general/active')) { + if (!$this->config->isSetFlag('wishlist/general/active', ScopeInterface::SCOPE_STORES)) { throw new NotFoundException(__('Page not found.')); } } diff --git a/app/code/Magento/Wishlist/Controller/Index/Send.php b/app/code/Magento/Wishlist/Controller/Index/Send.php index 54aa53d829db5..283400a6f94ea 100644 --- a/app/code/Magento/Wishlist/Controller/Index/Send.php +++ b/app/code/Magento/Wishlist/Controller/Index/Send.php @@ -25,9 +25,8 @@ use Magento\Customer\Model\Customer; /** - * Class Send + * Class Send Email Wishlist Controller * - * @package Magento\Wishlist\Controller\Index * @SuppressWarnings(PHPMD.CouplingBetweenObjects) */ class Send extends \Magento\Wishlist\Controller\AbstractIndex implements Action\HttpPostActionInterface @@ -204,7 +203,7 @@ public function execute() $error = __('Please enter an email address.'); } else { if (count($emails) > $emailsLeft) { - $error = __('This wish list can be shared %1 more times.', $emailsLeft); + $error = __('Maximum of %1 emails can be sent.', $emailsLeft); } else { foreach ($emails as $index => $email) { $email = trim($email); @@ -319,7 +318,6 @@ protected function addLayoutHandles(ResultLayout $resultLayout) * * @param int $wishlistId * @param \Magento\Framework\View\Result\Layout $resultLayout - * @return mixed */ protected function getRssLink($wishlistId, ResultLayout $resultLayout) { diff --git a/app/code/Magento/Wishlist/Controller/Index/Update.php b/app/code/Magento/Wishlist/Controller/Index/Update.php index e5fbd4b93f82e..ceb001a61c405 100644 --- a/app/code/Magento/Wishlist/Controller/Index/Update.php +++ b/app/code/Magento/Wishlist/Controller/Index/Update.php @@ -11,7 +11,7 @@ use Magento\Framework\Controller\ResultFactory; /** - * Class Update + * Controller for updating wishlists */ class Update extends \Magento\Wishlist\Controller\AbstractIndex implements HttpPostActionInterface { @@ -70,7 +70,12 @@ public function execute() } $post = $this->getRequest()->getPostValue(); - if ($post && isset($post['description']) && is_array($post['description'])) { + $resultRedirect->setPath('*', ['wishlist_id' => $wishlist->getId()]); + if (!$post) { + return $resultRedirect; + } + + if (isset($post['description']) && is_array($post['description'])) { $updatedItems = 0; foreach ($post['description'] as $itemId => $description) { @@ -136,13 +141,12 @@ public function execute() $this->messageManager->addErrorMessage(__('Can\'t update wish list')); } } + } - if (isset($post['save_and_share'])) { - $resultRedirect->setPath('*/*/share', ['wishlist_id' => $wishlist->getId()]); - return $resultRedirect; - } + if (isset($post['save_and_share'])) { + $resultRedirect->setPath('*/*/share', ['wishlist_id' => $wishlist->getId()]); } - $resultRedirect->setPath('*', ['wishlist_id' => $wishlist->getId()]); + return $resultRedirect; } } diff --git a/app/code/Magento/Wishlist/Test/Mftf/ActionGroup/AssertStorefrontAddToWishListIconIsClickableForGuestUserActionGroup.xml b/app/code/Magento/Wishlist/Test/Mftf/ActionGroup/AssertStorefrontAddToWishListIconIsClickableForGuestUserActionGroup.xml new file mode 100644 index 0000000000000..690b6b8bc2b59 --- /dev/null +++ b/app/code/Magento/Wishlist/Test/Mftf/ActionGroup/AssertStorefrontAddToWishListIconIsClickableForGuestUserActionGroup.xml @@ -0,0 +1,22 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + /** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +--> + +<actionGroups xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/actionGroupSchema.xsd"> + <actionGroup name="AssertStorefrontAddToWishListIconIsClickableForGuestUserActionGroup"> + <annotations> + <description>Assert "Add to Wish List" icon is clickable in category product listing</description> + </annotations> + + <moveMouseOver selector="{{StorefrontCategoryMainSection.ProductItemInfo}}" stepKey="hoverProduct"/> + <click selector="{{StorefrontCategoryMainSection.addToWishListIconProductInfoHover}}" stepKey="clickOnAddToWishListIcon"/> + <waitForElementVisible selector="{{StorefrontCustomerLoginMessagesSection.errorMessage}}" stepKey="waitForErrorMessageIsVisible"/> + <see selector="{{StorefrontCustomerLoginMessagesSection.errorMessage}}" userInput="You must login or register to add items to your wishlist." stepKey="assertErrorMessage"/> + <seeInCurrentUrl url="{{StorefrontCustomerSignInPage.url}}" stepKey="assertCustomerLoginPageUrl"/> + </actionGroup> +</actionGroups> diff --git a/app/code/Magento/Wishlist/Test/Mftf/Metadata/wishlist-meta.xml b/app/code/Magento/Wishlist/Test/Mftf/Metadata/WishlistMeta.xml similarity index 100% rename from app/code/Magento/Wishlist/Test/Mftf/Metadata/wishlist-meta.xml rename to app/code/Magento/Wishlist/Test/Mftf/Metadata/WishlistMeta.xml diff --git a/app/code/Magento/Wishlist/Test/Mftf/Page/StorefrontCustomerWishlistSharePage.xml b/app/code/Magento/Wishlist/Test/Mftf/Page/StorefrontCustomerWishlistSharePage.xml index 6d6151648c5ee..f2fa689546ef2 100644 --- a/app/code/Magento/Wishlist/Test/Mftf/Page/StorefrontCustomerWishlistSharePage.xml +++ b/app/code/Magento/Wishlist/Test/Mftf/Page/StorefrontCustomerWishlistSharePage.xml @@ -8,7 +8,7 @@ <pages xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="urn:magento:mftf:Page/etc/PageObject.xsd"> - <page name="StorefrontCustomerWishlistSharePage" url="/wishlist/index/share/wishlist_id/{{wishlistId}}/" area="storefront" module="Magento_Wishlist"> + <page name="StorefrontCustomerWishlistSharePage" url="/wishlist/index/share/wishlist_id/{{wishlistId}}/" parameterized="true" area="storefront" module="Magento_Wishlist"> <section name="StorefrontCustomerWishlistShareSection"/> </page> </pages> diff --git a/app/code/Magento/Wishlist/Test/Mftf/Test/AdminCustomerWishListShareOptionsInputValidationTest.xml b/app/code/Magento/Wishlist/Test/Mftf/Test/AdminCustomerWishListShareOptionsInputValidationTest.xml index da51bdf917e37..aaf7980e46aac 100755 --- a/app/code/Magento/Wishlist/Test/Mftf/Test/AdminCustomerWishListShareOptionsInputValidationTest.xml +++ b/app/code/Magento/Wishlist/Test/Mftf/Test/AdminCustomerWishListShareOptionsInputValidationTest.xml @@ -19,14 +19,14 @@ <testCaseId value="N/a"/> </annotations> <before> - <actionGroup ref="LoginAsAdmin" stepKey="loginAsAdmin1"/> + <actionGroup ref="AdminLoginActionGroup" stepKey="loginAsAdmin1"/> </before> <after> <actionGroup ref="setEmailTextLengthLimitActionGroup" stepKey="rollbackEmailTextLengthLimit"> <argument name="emailTextLengthLimit" value="{{Wishlist.default_email_text_length_limit}}"/> </actionGroup> <checkOption selector="{{WishListShareOptionsSection.useSystemValueForWishListEmailTextLimit}}" stepKey="checkUseSystemValueForWishListEmailTextLimit"/> - <amOnPage url="admin/admin/auth/logout/" stepKey="amOnLogoutPage"/> + <actionGroup ref="AdminLogoutActionGroup" stepKey="adminLogout"/> </after> <actionGroup ref="setEmailTextLengthLimitActionGroup" stepKey="setEmailTextLengthLimitToMin"> diff --git a/app/code/Magento/Wishlist/Test/Mftf/Test/ConfProdAddToCartWishListWithUnselectedAttrTest.xml b/app/code/Magento/Wishlist/Test/Mftf/Test/ConfProdAddToCartWishListWithUnselectedAttrTest.xml index d1d9a439c335d..fbda4b97f6e5c 100644 --- a/app/code/Magento/Wishlist/Test/Mftf/Test/ConfProdAddToCartWishListWithUnselectedAttrTest.xml +++ b/app/code/Magento/Wishlist/Test/Mftf/Test/ConfProdAddToCartWishListWithUnselectedAttrTest.xml @@ -20,7 +20,7 @@ </annotations> <before> - <actionGroup ref="LoginAsAdmin" stepKey="loginAsAdmin1"/> + <actionGroup ref="AdminLoginActionGroup" stepKey="loginAsAdmin1"/> <createData entity="ApiCategory" stepKey="createCategory"/> <!--Create Configurable product--> <actionGroup ref="CreateConfigurableProductActionGroup" stepKey="createProduct"> @@ -38,7 +38,7 @@ </actionGroup> <conditionalClick selector="{{AdminProductGridFilterSection.clearFilters}}" dependentSelector="{{AdminProductGridFilterSection.clearFilters}}" visible="true" stepKey="clickClearFilters"/> - <actionGroup ref="logout" stepKey="logout"/> + <actionGroup ref="AdminLogoutActionGroup" stepKey="logout"/> </after> <!--Login as customer --> diff --git a/app/code/Magento/Wishlist/Test/Mftf/Test/ConfigurableProductChildImageShouldBeShownOnWishListTest.xml b/app/code/Magento/Wishlist/Test/Mftf/Test/ConfigurableProductChildImageShouldBeShownOnWishListTest.xml index c9c539d215165..738bbc6bda35c 100644 --- a/app/code/Magento/Wishlist/Test/Mftf/Test/ConfigurableProductChildImageShouldBeShownOnWishListTest.xml +++ b/app/code/Magento/Wishlist/Test/Mftf/Test/ConfigurableProductChildImageShouldBeShownOnWishListTest.xml @@ -21,7 +21,7 @@ <before> <magentoCLI command="config:set checkout/cart/configurable_product_image 0" stepKey="setProductImageSettingUnderCofigurationSalesCheckout"/> <createData entity="ApiCategory" stepKey="createCategory"/> - <actionGroup ref="LoginAsAdmin" stepKey="loginAsAdmin1"/> + <actionGroup ref="AdminLoginActionGroup" stepKey="loginAsAdmin1"/> <actionGroup ref="CreateConfigurableProductActionGroup" stepKey="createProduct"> <argument name="product" value="_defaultProduct"/> @@ -32,7 +32,7 @@ <after> <deleteData createDataKey="createCategory" stepKey="deleteCategory"/> <deleteData createDataKey="customer" stepKey="deleteCustomer"/> - <actionGroup ref="logout" stepKey="adminLogout"/> + <actionGroup ref="AdminLogoutActionGroup" stepKey="adminLogout"/> </after> <amOnPage url="{{AdminProductIndexPage.url}}" stepKey="navigateToProductIndex"/> <waitForPageLoad stepKey="waitForProductIndexPageLoad"/> diff --git a/app/code/Magento/Wishlist/Test/Mftf/Test/StorefrontAddMultipleStoreProductsToWishlistTest.xml b/app/code/Magento/Wishlist/Test/Mftf/Test/StorefrontAddMultipleStoreProductsToWishlistTest.xml index 82021e75e0983..eed4dc8d4767e 100644 --- a/app/code/Magento/Wishlist/Test/Mftf/Test/StorefrontAddMultipleStoreProductsToWishlistTest.xml +++ b/app/code/Magento/Wishlist/Test/Mftf/Test/StorefrontAddMultipleStoreProductsToWishlistTest.xml @@ -29,7 +29,7 @@ </createData> <createData entity="Simple_US_Customer" stepKey="customer"/> <!-- Create new store view and assign it to non default store --> - <actionGroup ref="LoginAsAdmin" stepKey="loginAsAdmin1"/> + <actionGroup ref="AdminLoginActionGroup" stepKey="loginAsAdmin1"/> <actionGroup ref="CreateCustomStoreViewActionGroup" stepKey="createCustomStoreView"> <argument name="storeGroupName" value="$$storeGroup.group[name]$$"/> </actionGroup> @@ -48,7 +48,7 @@ <amOnPage url="{{AdminProductIndexPage.url}}" stepKey="navigateToProductIndex"/> <actionGroup ref="ClearFiltersAdminDataGridActionGroup" stepKey="clearProductsFilters"/> <!--Logout everywhere--> - <actionGroup ref="logout" stepKey="adminLogout"/> + <actionGroup ref="AdminLogoutActionGroup" stepKey="adminLogout"/> <actionGroup ref="StorefrontCustomerLogoutActionGroup" stepKey="customerLogout"/> </after> diff --git a/app/code/Magento/Wishlist/Test/Mftf/Test/StorefrontDeleteBundleDynamicProductFromWishlistTest.xml b/app/code/Magento/Wishlist/Test/Mftf/Test/StorefrontDeleteBundleDynamicProductFromWishlistTest.xml index f5d7eb4c6ed02..8ba45035e7b70 100644 --- a/app/code/Magento/Wishlist/Test/Mftf/Test/StorefrontDeleteBundleDynamicProductFromWishlistTest.xml +++ b/app/code/Magento/Wishlist/Test/Mftf/Test/StorefrontDeleteBundleDynamicProductFromWishlistTest.xml @@ -47,8 +47,8 @@ <requiredEntity createDataKey="simpleProduct2"/> </createData> - <actionGroup ref="LoginAsAdmin" stepKey="LoginAsAdmin"/> - <actionGroup ref="GoToProductPageViaIDActionGroup" stepKey="goToProduct"> + <actionGroup ref="AdminLoginActionGroup" stepKey="LoginAsAdmin"/> + <actionGroup ref="AdminProductPageOpenByIdActionGroup" stepKey="goToProduct"> <argument name="productId" value="$$createBundleProduct.id$$"/> </actionGroup> <scrollTo selector="{{AdminProductFormBundleSection.contentDropDown}}" stepKey="scrollToBundleSection"/> @@ -62,7 +62,7 @@ <deleteData createDataKey="createBundleProduct" stepKey="deleteBundleProduct"/> <deleteData createDataKey="simpleProduct1" stepKey="deleteProduct1"/> <deleteData createDataKey="simpleProduct2" stepKey="deleteProduct2"/> - <actionGroup ref="logout" stepKey="logout"/> + <actionGroup ref="AdminLogoutActionGroup" stepKey="logout"/> </after> <!-- Login as a customer --> diff --git a/app/code/Magento/Wishlist/Test/Mftf/Test/StorefrontDeleteBundleFixedProductFromWishlistTest.xml b/app/code/Magento/Wishlist/Test/Mftf/Test/StorefrontDeleteBundleFixedProductFromWishlistTest.xml index cdd86bfecccc8..a23788d2c508f 100644 --- a/app/code/Magento/Wishlist/Test/Mftf/Test/StorefrontDeleteBundleFixedProductFromWishlistTest.xml +++ b/app/code/Magento/Wishlist/Test/Mftf/Test/StorefrontDeleteBundleFixedProductFromWishlistTest.xml @@ -55,7 +55,7 @@ <deleteData createDataKey="createBundleProduct" stepKey="deleteBundleProduct"/> <deleteData createDataKey="simpleProduct1" stepKey="deleteProduct1"/> <deleteData createDataKey="simpleProduct2" stepKey="deleteProduct2"/> - <actionGroup ref="logout" stepKey="logout"/> + <actionGroup ref="AdminLogoutActionGroup" stepKey="logout"/> </after> <!-- Login as a customer --> diff --git a/app/code/Magento/Wishlist/Test/Mftf/Test/StorefrontDeleteConfigurableProductFromWishlistTest.xml b/app/code/Magento/Wishlist/Test/Mftf/Test/StorefrontDeleteConfigurableProductFromWishlistTest.xml index 6a718ebdfcf0f..4ad87095ecd30 100644 --- a/app/code/Magento/Wishlist/Test/Mftf/Test/StorefrontDeleteConfigurableProductFromWishlistTest.xml +++ b/app/code/Magento/Wishlist/Test/Mftf/Test/StorefrontDeleteConfigurableProductFromWishlistTest.xml @@ -117,7 +117,10 @@ <deleteData createDataKey="createConfigProduct" stepKey="deleteProduct"/> <deleteData createDataKey="createCategory" stepKey="deleteCategory"/> <deleteData createDataKey="createConfigProductAttribute" stepKey="deleteProductAttribute"/> - <actionGroup ref="logout" stepKey="logout"/> + <actionGroup ref="AdminLogoutActionGroup" stepKey="logout"/> + + <!-- Reindex invalidated indices after product attribute has been created/deleted --> + <magentoCron groups="index" stepKey="reindexInvalidatedIndices"/> </after> <!-- 1. Login as a customer --> diff --git a/app/code/Magento/Wishlist/Test/Mftf/Test/StorefrontDeletePersistedWishlistTest.xml b/app/code/Magento/Wishlist/Test/Mftf/Test/StorefrontDeletePersistedWishlistTest.xml index f1659baaa4e09..0ead68a6b9144 100644 --- a/app/code/Magento/Wishlist/Test/Mftf/Test/StorefrontDeletePersistedWishlistTest.xml +++ b/app/code/Magento/Wishlist/Test/Mftf/Test/StorefrontDeletePersistedWishlistTest.xml @@ -32,7 +32,7 @@ <deleteData stepKey="deleteCategory" createDataKey="category"/> <deleteData stepKey="deleteProduct" createDataKey="product"/> <deleteData stepKey="deleteCustomer" createDataKey="customer"/> - <actionGroup ref="logout" stepKey="adminLogout"/> + <actionGroup ref="AdminLogoutActionGroup" stepKey="adminLogout"/> </after> <amOnPage stepKey="amOnSignInPage" url="{{StorefrontCustomerSignInPage.url}}"/> diff --git a/app/code/Magento/Wishlist/Test/Mftf/Test/StorefrontMoveConfigurableProductFromShoppingCartToWishlistTest.xml b/app/code/Magento/Wishlist/Test/Mftf/Test/StorefrontMoveConfigurableProductFromShoppingCartToWishlistTest.xml index 0f94a08328c95..a5081ca2ad338 100644 --- a/app/code/Magento/Wishlist/Test/Mftf/Test/StorefrontMoveConfigurableProductFromShoppingCartToWishlistTest.xml +++ b/app/code/Magento/Wishlist/Test/Mftf/Test/StorefrontMoveConfigurableProductFromShoppingCartToWishlistTest.xml @@ -119,7 +119,7 @@ <deleteData createDataKey="createConfigProduct" stepKey="deleteProduct"/> <deleteData createDataKey="createCategory" stepKey="deleteCategory"/> <deleteData createDataKey="createConfigProductAttribute" stepKey="deleteProductAttribute"/> - <actionGroup ref="logout" stepKey="logout"/> + <actionGroup ref="AdminLogoutActionGroup" stepKey="logout"/> </after> <!-- 1. Login as a customer --> diff --git a/app/code/Magento/Wishlist/Test/Mftf/Test/StorefrontMoveDynamicBundleProductFromShoppingCartToWishlistTest.xml b/app/code/Magento/Wishlist/Test/Mftf/Test/StorefrontMoveDynamicBundleProductFromShoppingCartToWishlistTest.xml index 3a823efbcdf61..6932d56b2f56a 100644 --- a/app/code/Magento/Wishlist/Test/Mftf/Test/StorefrontMoveDynamicBundleProductFromShoppingCartToWishlistTest.xml +++ b/app/code/Magento/Wishlist/Test/Mftf/Test/StorefrontMoveDynamicBundleProductFromShoppingCartToWishlistTest.xml @@ -45,15 +45,15 @@ <requiredEntity createDataKey="createBundleOption1_1"/> <requiredEntity createDataKey="simpleProduct2"/> </createData> - <actionGroup ref="LoginAsAdmin" stepKey="LoginAsAdmin"/> - <actionGroup ref="GoToProductPageViaIDActionGroup" stepKey="goToProduct"> + <actionGroup ref="AdminLoginActionGroup" stepKey="LoginAsAdmin"/> + <actionGroup ref="AdminProductPageOpenByIdActionGroup" stepKey="goToProduct"> <argument name="productId" value="$$createBundleProduct.id$$"/> </actionGroup> <scrollTo selector="{{AdminProductFormBundleSection.contentDropDown}}" stepKey="scrollToBundleSection"/> <selectOption userInput="Separately" selector="{{AdminProductFormBundleSection.shipmentType}}" stepKey="selectSeparately"/> <actionGroup ref="SaveProductFormActionGroup" stepKey="saveProduct"/> - <magentoCLI stepKey="reindex" command="indexer:reindex"/> - <magentoCLI stepKey="flushCache" command="cache:flush"/> + + <magentoCLI stepKey="runCronIndex" command="cron:run --group=index"/> </before> <after> <!-- Delete data --> @@ -62,7 +62,7 @@ <deleteData createDataKey="createBundleProduct" stepKey="deleteBundleProduct"/> <deleteData createDataKey="simpleProduct1" stepKey="deleteProduct1"/> <deleteData createDataKey="simpleProduct2" stepKey="deleteProduct2"/> - <actionGroup ref="logout" stepKey="logout"/> + <actionGroup ref="AdminLogoutActionGroup" stepKey="logout"/> </after> <!-- 1. Login as a customer --> diff --git a/app/code/Magento/Wishlist/Test/Mftf/Test/StorefrontMoveFixedBundleProductFromShoppingCartToWishlistTest.xml b/app/code/Magento/Wishlist/Test/Mftf/Test/StorefrontMoveFixedBundleProductFromShoppingCartToWishlistTest.xml index 2d83043f81318..8b94c42106a6a 100644 --- a/app/code/Magento/Wishlist/Test/Mftf/Test/StorefrontMoveFixedBundleProductFromShoppingCartToWishlistTest.xml +++ b/app/code/Magento/Wishlist/Test/Mftf/Test/StorefrontMoveFixedBundleProductFromShoppingCartToWishlistTest.xml @@ -43,8 +43,8 @@ <requiredEntity createDataKey="createBundleOption1_1"/> <requiredEntity createDataKey="simpleProduct2"/> </createData> - <magentoCLI stepKey="reindex" command="indexer:reindex"/> - <magentoCLI stepKey="flushCache" command="cache:flush"/> + + <magentoCLI stepKey="runCronIndex" command="cron:run --group=index"/> </before> <after> <!-- Delete data --> @@ -53,7 +53,7 @@ <deleteData createDataKey="createBundleProduct" stepKey="deleteBundleProduct"/> <deleteData createDataKey="simpleProduct1" stepKey="deleteProduct1"/> <deleteData createDataKey="simpleProduct2" stepKey="deleteProduct2"/> - <actionGroup ref="logout" stepKey="logout"/> + <actionGroup ref="AdminLogoutActionGroup" stepKey="logout"/> </after> <!-- 1. Login as a customer --> diff --git a/app/code/Magento/Wishlist/Test/Mftf/Test/StorefrontMoveVirtualProductFromShoppingCartToWishlistTest.xml b/app/code/Magento/Wishlist/Test/Mftf/Test/StorefrontMoveVirtualProductFromShoppingCartToWishlistTest.xml index ad10b8d01bbbd..5c2762f39dde6 100644 --- a/app/code/Magento/Wishlist/Test/Mftf/Test/StorefrontMoveVirtualProductFromShoppingCartToWishlistTest.xml +++ b/app/code/Magento/Wishlist/Test/Mftf/Test/StorefrontMoveVirtualProductFromShoppingCartToWishlistTest.xml @@ -32,7 +32,7 @@ <deleteData createDataKey="createCustomer" stepKey="deleteCustomer"/> <deleteData createDataKey="createCategory" stepKey="deleteCategory"/> <deleteData createDataKey="createProduct" stepKey="deleteProduct"/> - <actionGroup ref="logout" stepKey="logout"/> + <actionGroup ref="AdminLogoutActionGroup" stepKey="logout"/> </after> <!-- 1. Login as a customer --> diff --git a/app/code/Magento/Wishlist/Test/Mftf/Test/StorefrontShareWishlistEntityTest.xml b/app/code/Magento/Wishlist/Test/Mftf/Test/StorefrontShareWishlistEntityTest.xml index 87c5ed950949f..329978462c107 100644 --- a/app/code/Magento/Wishlist/Test/Mftf/Test/StorefrontShareWishlistEntityTest.xml +++ b/app/code/Magento/Wishlist/Test/Mftf/Test/StorefrontShareWishlistEntityTest.xml @@ -15,40 +15,39 @@ <title value="Customer should be able to share a persistent wishlist"/> <description value="Customer should be able to share a persistent wishlist"/> <severity value="AVERAGE"/> - <group value="wishlist"/> - <testCaseId value="MC-13976"/> + <testCaseId value="MC-27621"/> <group value="wishlist"/> <group value="mtf_migrated"/> </annotations> <before> - <createData entity="SimpleSubCategory" stepKey="category"/> - <createData entity="SimpleProduct" stepKey="product"> - <requiredEntity createDataKey="category"/> + <createData entity="SimpleSubCategory" stepKey="createCategory"/> + <createData entity="SimpleProduct" stepKey="createProduct"> + <requiredEntity createDataKey="createCategory"/> </createData> - <createData entity="Simple_US_Customer" stepKey="customer"/> + <createData entity="Simple_US_Customer" stepKey="createCustomer"/> </before> <after> - <deleteData createDataKey="category" stepKey="deleteCategory"/> - <deleteData createDataKey="product" stepKey="deleteProduct"/> - <deleteData createDataKey="customer" stepKey="deleteCustomer"/> + <deleteData createDataKey="createCategory" stepKey="deleteCategory"/> + <deleteData createDataKey="createProduct" stepKey="deleteProduct"/> + <!-- Customer logout --> + <actionGroup ref="StorefrontCustomerLogoutActionGroup" stepKey="logoutCustomer"/> + <deleteData createDataKey="createCustomer" stepKey="deleteCustomer"/> </after> <!-- Sign in as customer --> <actionGroup ref="LoginToStorefrontActionGroup" stepKey="loginToStorefrontAccount"> - <argument name="Customer" value="$$customer$$"/> + <argument name="Customer" value="$createCustomer$"/> </actionGroup> <actionGroup ref="OpenProductFromCategoryPageActionGroup" stepKey="openProductFromCategory"> - <argument name="category" value="$$category$$"/> - <argument name="product" value="$$product$$"/> + <argument name="category" value="$createCategory$"/> + <argument name="product" value="$createProduct$"/> </actionGroup> <actionGroup ref="StorefrontCustomerAddProductToWishlistActionGroup" stepKey="addToWishlistProduct"> - <argument name="productVar" value="$$product$$"/> + <argument name="productVar" value="$createProduct$"/> </actionGroup> <actionGroup ref="StorefrontCustomerShareWishlistActionGroup" stepKey="shareWishlist"/> - - <actionGroup ref="StorefrontCustomerLogoutActionGroup" stepKey="logoutCustomer"/> </test> </tests> diff --git a/app/code/Magento/Wishlist/Test/Mftf/Test/StorefrontVerifySecureURLRedirectWishlistTest.xml b/app/code/Magento/Wishlist/Test/Mftf/Test/StorefrontVerifySecureURLRedirectWishlistTest.xml index 21fa334a43196..72f5bab1e6af5 100644 --- a/app/code/Magento/Wishlist/Test/Mftf/Test/StorefrontVerifySecureURLRedirectWishlistTest.xml +++ b/app/code/Magento/Wishlist/Test/Mftf/Test/StorefrontVerifySecureURLRedirectWishlistTest.xml @@ -8,7 +8,7 @@ <tests xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/testSchema.xsd"> - <test name="StorefrontVerifySecureURLRedirectWishlist"> + <test name="StorefrontVerifySecureURLRedirectWishlistTest"> <annotations> <features value="Wishlist"/> <stories value="Storefront Secure URLs"/> diff --git a/app/code/Magento/Wishlist/Test/Mftf/Test/WishListWithDisabledProductTest.xml b/app/code/Magento/Wishlist/Test/Mftf/Test/WishListWithDisabledProductTest.xml index 3aabb9e94c364..689b76e42e6f1 100644 --- a/app/code/Magento/Wishlist/Test/Mftf/Test/WishListWithDisabledProductTest.xml +++ b/app/code/Magento/Wishlist/Test/Mftf/Test/WishListWithDisabledProductTest.xml @@ -26,7 +26,7 @@ <deleteData createDataKey="createProduct" stepKey="deleteProduct"/> <deleteData createDataKey="createCustomer" stepKey="deleteCustomer"/> <actionGroup ref="StorefrontCustomerLogoutActionGroup" stepKey="customerLogout"/> - <actionGroup ref="logout" stepKey="logout"/> + <actionGroup ref="AdminLogoutActionGroup" stepKey="logout"/> </after> <actionGroup ref="LoginToStorefrontActionGroup" stepKey="loginToStorefrontAccount"> <argument name="Customer" value="$$createCustomer$$"/> @@ -39,8 +39,10 @@ <argument name="productVar" value="$$createProduct$$"/> </actionGroup> <openNewTab stepKey="openNewTab"/> - <actionGroup ref="LoginAsAdmin" stepKey="loginToAdminArea"/> - <amOnPage url="{{AdminProductEditPage.url($$createProduct.id$$)}}" stepKey="goToProductEditPage"/> + <actionGroup ref="AdminLoginActionGroup" stepKey="loginToAdminArea"/> + <actionGroup ref="AdminProductPageOpenByIdActionGroup" stepKey="goToProductEditPage"> + <argument name="productId" value="$$createProduct.id$$"/> + </actionGroup> <actionGroup ref="AdminSetProductDisabledActionGroup" stepKey="disableProduct"/> <actionGroup ref="SaveProductFormActionGroup" stepKey="saveProduct"/> <closeTab stepKey="closeSecondTab"/> diff --git a/app/code/Magento/Wishlist/Test/Unit/Block/Adminhtml/Widget/Grid/Column/Filter/TextTest.php b/app/code/Magento/Wishlist/Test/Unit/Block/Adminhtml/Widget/Grid/Column/Filter/TextTest.php index 69283cd701119..2454a2e84e5af 100644 --- a/app/code/Magento/Wishlist/Test/Unit/Block/Adminhtml/Widget/Grid/Column/Filter/TextTest.php +++ b/app/code/Magento/Wishlist/Test/Unit/Block/Adminhtml/Widget/Grid/Column/Filter/TextTest.php @@ -10,7 +10,7 @@ class TextTest extends \PHPUnit\Framework\TestCase { - /** @var Text | \PHPUnit_Framework_MockObject_MockObject */ + /** @var Text|\PHPUnit\Framework\MockObject\MockObject */ private $textFilterBlock; protected function setUp() diff --git a/app/code/Magento/Wishlist/Test/Unit/Block/Cart/Item/Renderer/Actions/MoveToWishlistTest.php b/app/code/Magento/Wishlist/Test/Unit/Block/Cart/Item/Renderer/Actions/MoveToWishlistTest.php index cd4fe2d04cbbe..f454b62024680 100644 --- a/app/code/Magento/Wishlist/Test/Unit/Block/Cart/Item/Renderer/Actions/MoveToWishlistTest.php +++ b/app/code/Magento/Wishlist/Test/Unit/Block/Cart/Item/Renderer/Actions/MoveToWishlistTest.php @@ -16,7 +16,7 @@ class MoveToWishlistTest extends \PHPUnit\Framework\TestCase */ protected $model; - /** @var Data|\PHPUnit_Framework_MockObject_MockObject */ + /** @var Data|\PHPUnit\Framework\MockObject\MockObject */ protected $wishlistHelperMock; protected function setUp() @@ -50,7 +50,7 @@ public function testGetMoveFromCartParams() $json = '{json;}'; /** - * @var Item|\PHPUnit_Framework_MockObject_MockObject $itemMock + * @var Item|\PHPUnit\Framework\MockObject\MockObject $itemMock */ $itemMock = $this->getMockBuilder(\Magento\Quote\Model\Quote\Item::class) ->disableOriginalConstructor() diff --git a/app/code/Magento/Wishlist/Test/Unit/Block/Customer/SidebarTest.php b/app/code/Magento/Wishlist/Test/Unit/Block/Customer/SidebarTest.php index 92c36470d7f7f..546ca009a6b1a 100644 --- a/app/code/Magento/Wishlist/Test/Unit/Block/Customer/SidebarTest.php +++ b/app/code/Magento/Wishlist/Test/Unit/Block/Customer/SidebarTest.php @@ -11,12 +11,12 @@ class SidebarTest extends \PHPUnit\Framework\TestCase { /** - * @var \Magento\Catalog\Block\Product\Context|\PHPUnit_Framework_MockObject_MockObject + * @var \Magento\Catalog\Block\Product\Context|\PHPUnit\Framework\MockObject\MockObject */ private $productContext; /** - * @var \Magento\Framework\App\Http\Context|\PHPUnit_Framework_MockObject_MockObject + * @var \Magento\Framework\App\Http\Context|\PHPUnit\Framework\MockObject\MockObject */ private $httpContext; @@ -26,7 +26,7 @@ class SidebarTest extends \PHPUnit\Framework\TestCase private $block; /** - * @var \Magento\Framework\View\LayoutInterface|\PHPUnit_Framework_MockObject_MockObject + * @var \Magento\Framework\View\LayoutInterface|\PHPUnit\Framework\MockObject\MockObject */ private $layout; diff --git a/app/code/Magento/Wishlist/Test/Unit/Block/Customer/Wishlist/Item/OptionsTest.php b/app/code/Magento/Wishlist/Test/Unit/Block/Customer/Wishlist/Item/OptionsTest.php index 36c51547c5a42..9067f042445d9 100644 --- a/app/code/Magento/Wishlist/Test/Unit/Block/Customer/Wishlist/Item/OptionsTest.php +++ b/app/code/Magento/Wishlist/Test/Unit/Block/Customer/Wishlist/Item/OptionsTest.php @@ -13,12 +13,12 @@ class OptionsTest extends \PHPUnit\Framework\TestCase const TEST_HELPER_CLASS_NAME = 'testHelperClass'; /** - * @var \Magento\Framework\Escaper|\PHPUnit_Framework_MockObject_MockObject + * @var \Magento\Framework\Escaper|\PHPUnit\Framework\MockObject\MockObject */ private $escaperMock; /** - * @var \Magento\Framework\App\Http\Context|\PHPUnit_Framework_MockObject_MockObject + * @var \Magento\Framework\App\Http\Context|\PHPUnit\Framework\MockObject\MockObject */ private $httpContextMock; @@ -28,12 +28,12 @@ class OptionsTest extends \PHPUnit\Framework\TestCase private $block; /** - * @var \Magento\Catalog\Helper\Product\ConfigurationPool|\PHPUnit_Framework_MockObject_MockObject + * @var \Magento\Catalog\Helper\Product\ConfigurationPool|\PHPUnit\Framework\MockObject\MockObject */ private $helperPoolMock; /** - * @var \Magento\Wishlist\Model\Item|\PHPUnit_Framework_MockObject_MockObject + * @var \Magento\Wishlist\Model\Item|\PHPUnit\Framework\MockObject\MockObject */ private $itemMock; diff --git a/app/code/Magento/Wishlist/Test/Unit/Block/Item/ConfigureTest.php b/app/code/Magento/Wishlist/Test/Unit/Block/Item/ConfigureTest.php index 6aa60aa3be571..9847fff4135f6 100644 --- a/app/code/Magento/Wishlist/Test/Unit/Block/Item/ConfigureTest.php +++ b/app/code/Magento/Wishlist/Test/Unit/Block/Item/ConfigureTest.php @@ -15,17 +15,17 @@ class ConfigureTest extends \PHPUnit\Framework\TestCase protected $model; /** - * @var \PHPUnit_Framework_MockObject_MockObject + * @var \PHPUnit\Framework\MockObject\MockObject */ protected $registryMock; /** - * @var \PHPUnit_Framework_MockObject_MockObject + * @var \PHPUnit\Framework\MockObject\MockObject */ protected $contextMock; /** - * @var \PHPUnit_Framework_MockObject_MockObject + * @var \PHPUnit\Framework\MockObject\MockObject */ protected $wishlistDataMock; diff --git a/app/code/Magento/Wishlist/Test/Unit/Block/Rss/EmailLinkTest.php b/app/code/Magento/Wishlist/Test/Unit/Block/Rss/EmailLinkTest.php index 030669202f44d..cf73945a9feef 100644 --- a/app/code/Magento/Wishlist/Test/Unit/Block/Rss/EmailLinkTest.php +++ b/app/code/Magento/Wishlist/Test/Unit/Block/Rss/EmailLinkTest.php @@ -16,14 +16,14 @@ class EmailLinkTest extends \PHPUnit\Framework\TestCase /** @var ObjectManagerHelper */ protected $objectManagerHelper; - /** @var \Magento\Wishlist\Helper\Data|\PHPUnit_Framework_MockObject_MockObject */ + /** @var \Magento\Wishlist\Helper\Data|\PHPUnit\Framework\MockObject\MockObject */ protected $wishlistHelper; - /** @var \Magento\Framework\App\Rss\UrlBuilderInterface|\PHPUnit_Framework_MockObject_MockObject */ + /** @var \Magento\Framework\App\Rss\UrlBuilderInterface|\PHPUnit\Framework\MockObject\MockObject */ protected $urlBuilder; /** - * @var \Magento\Framework\Url\EncoderInterface|\PHPUnit_Framework_MockObject_MockObject + * @var \Magento\Framework\Url\EncoderInterface|\PHPUnit\Framework\MockObject\MockObject */ protected $urlEncoder; diff --git a/app/code/Magento/Wishlist/Test/Unit/Block/Rss/LinkTest.php b/app/code/Magento/Wishlist/Test/Unit/Block/Rss/LinkTest.php index df63367a23cba..6eb5bf0ad0cd5 100644 --- a/app/code/Magento/Wishlist/Test/Unit/Block/Rss/LinkTest.php +++ b/app/code/Magento/Wishlist/Test/Unit/Block/Rss/LinkTest.php @@ -16,17 +16,17 @@ class LinkTest extends \PHPUnit\Framework\TestCase /** @var ObjectManagerHelper */ protected $objectManagerHelper; - /** @var \Magento\Wishlist\Helper\Data|\PHPUnit_Framework_MockObject_MockObject */ + /** @var \Magento\Wishlist\Helper\Data|\PHPUnit\Framework\MockObject\MockObject */ protected $wishlistHelper; - /** @var \Magento\Framework\App\Rss\UrlBuilderInterface|\PHPUnit_Framework_MockObject_MockObject */ + /** @var \Magento\Framework\App\Rss\UrlBuilderInterface|\PHPUnit\Framework\MockObject\MockObject */ protected $urlBuilder; - /** @var \Magento\Framework\App\Config\ScopeConfigInterface|\PHPUnit_Framework_MockObject_MockObject */ + /** @var \Magento\Framework\App\Config\ScopeConfigInterface|\PHPUnit\Framework\MockObject\MockObject */ protected $scopeConfig; /** - * @var \Magento\Framework\Url\EncoderInterface|\PHPUnit_Framework_MockObject_MockObject + * @var \Magento\Framework\Url\EncoderInterface|\PHPUnit\Framework\MockObject\MockObject */ protected $urlEncoder; diff --git a/app/code/Magento/Wishlist/Test/Unit/Controller/Index/AllcartTest.php b/app/code/Magento/Wishlist/Test/Unit/Controller/Index/AllcartTest.php index 2df44a912a09c..341ca72cc2ef2 100644 --- a/app/code/Magento/Wishlist/Test/Unit/Controller/Index/AllcartTest.php +++ b/app/code/Magento/Wishlist/Test/Unit/Controller/Index/AllcartTest.php @@ -13,47 +13,47 @@ class AllcartTest extends \PHPUnit\Framework\TestCase { /** - * @var \Magento\Wishlist\Controller\WishlistProviderInterface|\PHPUnit_Framework_MockObject_MockObject + * @var \Magento\Wishlist\Controller\WishlistProviderInterface|\PHPUnit\Framework\MockObject\MockObject */ protected $wishlistProvider; /** - * @var \Magento\Wishlist\Model\ItemCarrier|\PHPUnit_Framework_MockObject_MockObject + * @var \Magento\Wishlist\Model\ItemCarrier|\PHPUnit\Framework\MockObject\MockObject */ protected $itemCarrier; /** - * @var \Magento\Framework\Data\Form\FormKey\Validator|\PHPUnit_Framework_MockObject_MockObject + * @var \Magento\Framework\Data\Form\FormKey\Validator|\PHPUnit\Framework\MockObject\MockObject */ protected $formKeyValidator; /** - * @var \Magento\Framework\App\Action\Context|\PHPUnit_Framework_MockObject_MockObject + * @var \Magento\Framework\App\Action\Context|\PHPUnit\Framework\MockObject\MockObject */ protected $context; /** - * @var \Magento\Framework\App\Request\Http|\PHPUnit_Framework_MockObject_MockObject + * @var \Magento\Framework\App\Request\Http|\PHPUnit\Framework\MockObject\MockObject */ protected $request; /** - * @var \Magento\Framework\App\Response\Http|\PHPUnit_Framework_MockObject_MockObject + * @var \Magento\Framework\App\Response\Http|\PHPUnit\Framework\MockObject\MockObject */ protected $response; /** - * @var \Magento\Framework\Controller\ResultFactory|\PHPUnit_Framework_MockObject_MockObject + * @var \Magento\Framework\Controller\ResultFactory|\PHPUnit\Framework\MockObject\MockObject */ protected $resultFactoryMock; /** - * @var \Magento\Framework\Controller\Result\Redirect|\PHPUnit_Framework_MockObject_MockObject + * @var \Magento\Framework\Controller\Result\Redirect|\PHPUnit\Framework\MockObject\MockObject */ protected $resultRedirectMock; /** - * @var \Magento\Framework\Controller\Result\Forward|\PHPUnit_Framework_MockObject_MockObject + * @var \Magento\Framework\Controller\Result\Forward|\PHPUnit\Framework\MockObject\MockObject */ protected $resultForwardMock; @@ -189,7 +189,7 @@ public function testExecutePassed() { $url = 'http://redirect-url.com'; $wishlist = $this->createMock(\Magento\Wishlist\Model\Wishlist::class); - + $this->formKeyValidator->expects($this->once()) ->method('validate') ->with($this->request) diff --git a/app/code/Magento/Wishlist/Test/Unit/Controller/Index/CartTest.php b/app/code/Magento/Wishlist/Test/Unit/Controller/Index/CartTest.php index c1f1378c22da6..093f0d8d6b8f8 100644 --- a/app/code/Magento/Wishlist/Test/Unit/Controller/Index/CartTest.php +++ b/app/code/Magento/Wishlist/Test/Unit/Controller/Index/CartTest.php @@ -21,97 +21,97 @@ class CartTest extends \PHPUnit\Framework\TestCase protected $model; /** - * @var \Magento\Framework\App\Action\Context|\PHPUnit_Framework_MockObject_MockObject + * @var \Magento\Framework\App\Action\Context|\PHPUnit\Framework\MockObject\MockObject */ protected $contextMock; /** - * @var \Magento\Wishlist\Controller\WishlistProviderInterface|\PHPUnit_Framework_MockObject_MockObject + * @var \Magento\Wishlist\Controller\WishlistProviderInterface|\PHPUnit\Framework\MockObject\MockObject */ protected $wishlistProviderMock; /** - * @var \Magento\Wishlist\Model\LocaleQuantityProcessor|\PHPUnit_Framework_MockObject_MockObject + * @var \Magento\Wishlist\Model\LocaleQuantityProcessor|\PHPUnit\Framework\MockObject\MockObject */ protected $quantityProcessorMock; /** - * @var \Magento\Wishlist\Model\ItemFactory|\PHPUnit_Framework_MockObject_MockObject + * @var \Magento\Wishlist\Model\ItemFactory|\PHPUnit\Framework\MockObject\MockObject */ protected $itemFactoryMock; /** - * @var \Magento\Checkout\Model\Cart|\PHPUnit_Framework_MockObject_MockObject + * @var \Magento\Checkout\Model\Cart|\PHPUnit\Framework\MockObject\MockObject */ protected $checkoutCartMock; /** - * @var \Magento\Wishlist\Model\Item\OptionFactory|\PHPUnit_Framework_MockObject_MockObject + * @var \Magento\Wishlist\Model\Item\OptionFactory|\PHPUnit\Framework\MockObject\MockObject */ protected $optionFactoryMock; /** - * @var \Magento\Catalog\Helper\Product|\PHPUnit_Framework_MockObject_MockObject + * @var \Magento\Catalog\Helper\Product|\PHPUnit\Framework\MockObject\MockObject */ protected $productHelperMock; /** - * @var \Magento\Framework\Escaper|\PHPUnit_Framework_MockObject_MockObject + * @var \Magento\Framework\Escaper|\PHPUnit\Framework\MockObject\MockObject */ protected $escaperMock; /** - * @var \Magento\Wishlist\Helper\Data|\PHPUnit_Framework_MockObject_MockObject + * @var \Magento\Wishlist\Helper\Data|\PHPUnit\Framework\MockObject\MockObject */ protected $helperMock; /** - * @var \Magento\Framework\App\RequestInterface|\PHPUnit_Framework_MockObject_MockObject + * @var \Magento\Framework\App\RequestInterface|\PHPUnit\Framework\MockObject\MockObject */ protected $requestMock; /** - * @var \Magento\Framework\App\Response\RedirectInterface|\PHPUnit_Framework_MockObject_MockObject + * @var \Magento\Framework\App\Response\RedirectInterface|\PHPUnit\Framework\MockObject\MockObject */ protected $redirectMock; /** - * @var \Magento\Framework\ObjectManagerInterface|\PHPUnit_Framework_MockObject_MockObject + * @var \Magento\Framework\ObjectManagerInterface|\PHPUnit\Framework\MockObject\MockObject */ protected $objectManagerMock; /** - * @var \Magento\Framework\Message\ManagerInterface|\PHPUnit_Framework_MockObject_MockObject + * @var \Magento\Framework\Message\ManagerInterface|\PHPUnit\Framework\MockObject\MockObject */ protected $messageManagerMock; /** - * @var \Magento\Framework\UrlInterface|\PHPUnit_Framework_MockObject_MockObject + * @var \Magento\Framework\UrlInterface|\PHPUnit\Framework\MockObject\MockObject */ protected $urlMock; /** - * @var \Magento\Checkout\Helper\Cart|\PHPUnit_Framework_MockObject_MockObject + * @var \Magento\Checkout\Helper\Cart|\PHPUnit\Framework\MockObject\MockObject */ protected $cartHelperMock; /** - * @var \Magento\Framework\Controller\ResultFactory|\PHPUnit_Framework_MockObject_MockObject + * @var \Magento\Framework\Controller\ResultFactory|\PHPUnit\Framework\MockObject\MockObject */ protected $resultFactoryMock; /** - * @var \Magento\Framework\Controller\Result\Redirect|\PHPUnit_Framework_MockObject_MockObject + * @var \Magento\Framework\Controller\Result\Redirect|\PHPUnit\Framework\MockObject\MockObject */ protected $resultRedirectMock; /** - * @var \Magento\Framework\Controller\Result\Json|\PHPUnit_Framework_MockObject_MockObject + * @var \Magento\Framework\Controller\Result\Json|\PHPUnit\Framework\MockObject\MockObject */ protected $resultJsonMock; /** - * @var \Magento\Framework\Data\Form\FormKey\Validator|\PHPUnit_Framework_MockObject_MockObject + * @var \Magento\Framework\Data\Form\FormKey\Validator|\PHPUnit\Framework\MockObject\MockObject */ protected $formKeyValidator; diff --git a/app/code/Magento/Wishlist/Test/Unit/Controller/Index/FromcartTest.php b/app/code/Magento/Wishlist/Test/Unit/Controller/Index/FromcartTest.php index 52af38dee0ed3..82a5559ceedf4 100644 --- a/app/code/Magento/Wishlist/Test/Unit/Controller/Index/FromcartTest.php +++ b/app/code/Magento/Wishlist/Test/Unit/Controller/Index/FromcartTest.php @@ -31,57 +31,57 @@ class FromcartTest extends \PHPUnit\Framework\TestCase protected $controller; /** - * @var Context | \PHPUnit_Framework_MockObject_MockObject + * @var Context|\PHPUnit\Framework\MockObject\MockObject */ protected $context; /** - * @var WishlistProviderInterface | \PHPUnit_Framework_MockObject_MockObject + * @var WishlistProviderInterface|\PHPUnit\Framework\MockObject\MockObject */ protected $wishlistProvider; /** - * @var WishlistHelper | \PHPUnit_Framework_MockObject_MockObject + * @var WishlistHelper|\PHPUnit\Framework\MockObject\MockObject */ protected $wishlistHelper; /** - * @var CheckoutCart | \PHPUnit_Framework_MockObject_MockObject + * @var CheckoutCart|\PHPUnit\Framework\MockObject\MockObject */ protected $cart; /** - * @var CartHelper | \PHPUnit_Framework_MockObject_MockObject + * @var CartHelper|\PHPUnit\Framework\MockObject\MockObject */ protected $cartHelper; /** - * @var Escaper | \PHPUnit_Framework_MockObject_MockObject + * @var Escaper|\PHPUnit\Framework\MockObject\MockObject */ protected $escaper; /** - * @var Http | \PHPUnit_Framework_MockObject_MockObject + * @var Http|\PHPUnit\Framework\MockObject\MockObject */ protected $request; /** - * @var MessageManager | \PHPUnit_Framework_MockObject_MockObject + * @var MessageManager|\PHPUnit\Framework\MockObject\MockObject */ protected $messageManager; /** - * @var ResultFactory | \PHPUnit_Framework_MockObject_MockObject + * @var ResultFactory|\PHPUnit\Framework\MockObject\MockObject */ protected $resultFactory; /** - * @var ResultRedirect | \PHPUnit_Framework_MockObject_MockObject + * @var ResultRedirect|\PHPUnit\Framework\MockObject\MockObject */ protected $resultRedirect; /** - * @var Validator|\PHPUnit_Framework_MockObject_MockObject + * @var Validator|\PHPUnit\Framework\MockObject\MockObject */ protected $formKeyValidator; @@ -363,7 +363,7 @@ protected function prepareContext() * @param string $productName * @param DataObject $dataObjectMock * @param int $itemId - * @return \PHPUnit_Framework_MockObject_MockObject + * @return \PHPUnit\Framework\MockObject\MockObject */ protected function createQuoteMock($productId, $productName, $dataObjectMock, $itemId) { diff --git a/app/code/Magento/Wishlist/Test/Unit/Controller/Index/IndexTest.php b/app/code/Magento/Wishlist/Test/Unit/Controller/Index/IndexTest.php index 7c6ba740aa18d..f850154f086a9 100644 --- a/app/code/Magento/Wishlist/Test/Unit/Controller/Index/IndexTest.php +++ b/app/code/Magento/Wishlist/Test/Unit/Controller/Index/IndexTest.php @@ -13,42 +13,42 @@ class IndexTest extends \PHPUnit\Framework\TestCase { /** - * @var \Magento\Framework\App\Action\Context|\PHPUnit_Framework_MockObject_MockObject + * @var \Magento\Framework\App\Action\Context|\PHPUnit\Framework\MockObject\MockObject */ protected $context; /** - * @var \Magento\Framework\App\Request\Http|\PHPUnit_Framework_MockObject_MockObject + * @var \Magento\Framework\App\Request\Http|\PHPUnit\Framework\MockObject\MockObject */ protected $request; /** - * @var \Magento\Framework\App\Response\Http|\PHPUnit_Framework_MockObject_MockObject + * @var \Magento\Framework\App\Response\Http|\PHPUnit\Framework\MockObject\MockObject */ protected $response; /** - * @var \Magento\Wishlist\Controller\WishlistProvider|\PHPUnit_Framework_MockObject_MockObject + * @var \Magento\Wishlist\Controller\WishlistProvider|\PHPUnit\Framework\MockObject\MockObject */ protected $wishlistProvider; /** - * @var \Magento\Store\App\Response\Redirect|\PHPUnit_Framework_MockObject_MockObject + * @var \Magento\Store\App\Response\Redirect|\PHPUnit\Framework\MockObject\MockObject */ protected $redirect; /** - * @var \Magento\Framework\Controller\ResultFactory|\PHPUnit_Framework_MockObject_MockObject + * @var \Magento\Framework\Controller\ResultFactory|\PHPUnit\Framework\MockObject\MockObject */ protected $resultFactoryMock; /** - * @var \Magento\Framework\View\Result\Page|\PHPUnit_Framework_MockObject_MockObject + * @var \Magento\Framework\View\Result\Page|\PHPUnit\Framework\MockObject\MockObject */ protected $resultPageMock; /** - * @var \Magento\Framework\View\Layout|\PHPUnit_Framework_MockObject_MockObject + * @var \Magento\Framework\View\Layout|\PHPUnit\Framework\MockObject\MockObject */ protected $layoutMock; diff --git a/app/code/Magento/Wishlist/Test/Unit/Controller/Index/PluginTest.php b/app/code/Magento/Wishlist/Test/Unit/Controller/Index/PluginTest.php index 2b583f9101516..fb58777690bd1 100644 --- a/app/code/Magento/Wishlist/Test/Unit/Controller/Index/PluginTest.php +++ b/app/code/Magento/Wishlist/Test/Unit/Controller/Index/PluginTest.php @@ -6,38 +6,42 @@ namespace Magento\Wishlist\Test\Unit\Controller\Index; +use Magento\Store\Model\ScopeInterface; + /** * Test for wishlist plugin before dispatch + * + * @SuppressWarnings(PHPMD.CouplingBetweenObjects) */ class PluginTest extends \PHPUnit\Framework\TestCase { /** - * @var \Magento\Customer\Model\Session|\PHPUnit_Framework_MockObject_MockObject + * @var \Magento\Customer\Model\Session|\PHPUnit\Framework\MockObject\MockObject */ protected $customerSession; /** - * @var \Magento\Wishlist\Model\AuthenticationStateInterface|\PHPUnit_Framework_MockObject_MockObject + * @var \Magento\Wishlist\Model\AuthenticationStateInterface|\PHPUnit\Framework\MockObject\MockObject */ protected $authenticationState; /** - * @var \Magento\Framework\App\Config\ScopeConfigInterface|\PHPUnit_Framework_MockObject_MockObject + * @var \Magento\Framework\App\Config\ScopeConfigInterface|\PHPUnit\Framework\MockObject\MockObject */ protected $config; /** - * @var \Magento\Framework\App\Response\RedirectInterface|\PHPUnit_Framework_MockObject_MockObject + * @var \Magento\Framework\App\Response\RedirectInterface|\PHPUnit\Framework\MockObject\MockObject */ protected $redirector; /** - * @var \Magento\Framework\Message\ManagerInterface|\PHPUnit_Framework_MockObject_MockObject + * @var \Magento\Framework\Message\ManagerInterface|\PHPUnit\Framework\MockObject\MockObject */ protected $messageManager; /** - * @var \Magento\Framework\App\Request\Http|\PHPUnit_Framework_MockObject_MockObject + * @var \Magento\Framework\App\Request\Http|\PHPUnit\Framework\MockObject\MockObject */ protected $request; @@ -175,7 +179,7 @@ public function testBeforeDispatch() $this->config ->expects($this->once()) ->method('isSetFlag') - ->with('wishlist/general/active') + ->with('wishlist/general/active', ScopeInterface::SCOPE_STORES) ->willReturn(false); $this->getPlugin()->beforeDispatch($indexController, $this->request); diff --git a/app/code/Magento/Wishlist/Test/Unit/Controller/Index/RemoveTest.php b/app/code/Magento/Wishlist/Test/Unit/Controller/Index/RemoveTest.php index 2f4d0e6ba48ab..9029ae2bb82bf 100644 --- a/app/code/Magento/Wishlist/Test/Unit/Controller/Index/RemoveTest.php +++ b/app/code/Magento/Wishlist/Test/Unit/Controller/Index/RemoveTest.php @@ -13,52 +13,52 @@ class RemoveTest extends \PHPUnit\Framework\TestCase { /** - * @var \Magento\Wishlist\Controller\WishlistProvider|\PHPUnit_Framework_MockObject_MockObject + * @var \Magento\Wishlist\Controller\WishlistProvider|\PHPUnit\Framework\MockObject\MockObject */ protected $wishlistProvider; /** - * @var \Magento\Framework\App\Action\Context|\PHPUnit_Framework_MockObject_MockObject + * @var \Magento\Framework\App\Action\Context|\PHPUnit\Framework\MockObject\MockObject */ protected $context; /** - * @var \Magento\Framework\App\Request\Http|\PHPUnit_Framework_MockObject_MockObject + * @var \Magento\Framework\App\Request\Http|\PHPUnit\Framework\MockObject\MockObject */ protected $request; /** - * @var \Magento\Store\App\Response\Redirect|\PHPUnit_Framework_MockObject_MockObject + * @var \Magento\Store\App\Response\Redirect|\PHPUnit\Framework\MockObject\MockObject */ protected $redirect; /** - * @var \Magento\Framework\App\ObjectManager|\PHPUnit_Framework_MockObject_MockObject + * @var \Magento\Framework\App\ObjectManager|\PHPUnit\Framework\MockObject\MockObject */ protected $om; /** - * @var \Magento\Framework\Message\Manager|\PHPUnit_Framework_MockObject_MockObject + * @var \Magento\Framework\Message\Manager|\PHPUnit\Framework\MockObject\MockObject */ protected $messageManager; /** - * @var \Magento\Framework\Url|\PHPUnit_Framework_MockObject_MockObject + * @var \Magento\Framework\Url|\PHPUnit\Framework\MockObject\MockObject */ protected $url; /** - * @var \Magento\Framework\Controller\ResultFactory|\PHPUnit_Framework_MockObject_MockObject + * @var \Magento\Framework\Controller\ResultFactory|\PHPUnit\Framework\MockObject\MockObject */ protected $resultFactoryMock; /** - * @var \Magento\Framework\Controller\Result\Redirect|\PHPUnit_Framework_MockObject_MockObject + * @var \Magento\Framework\Controller\Result\Redirect|\PHPUnit\Framework\MockObject\MockObject */ protected $resultRedirectMock; /** - * @var \Magento\Framework\Data\Form\FormKey\Validator|\PHPUnit_Framework_MockObject_MockObject + * @var \Magento\Framework\Data\Form\FormKey\Validator|\PHPUnit\Framework\MockObject\MockObject */ protected $formKeyValidator; diff --git a/app/code/Magento/Wishlist/Test/Unit/Controller/Index/SendTest.php b/app/code/Magento/Wishlist/Test/Unit/Controller/Index/SendTest.php index 47148f7878134..d3efd1f192f27 100644 --- a/app/code/Magento/Wishlist/Test/Unit/Controller/Index/SendTest.php +++ b/app/code/Magento/Wishlist/Test/Unit/Controller/Index/SendTest.php @@ -5,7 +5,10 @@ */ namespace Magento\Wishlist\Test\Unit\Controller\Index; +use Magento\Captcha\Helper\Data as CaptchaHelper; +use Magento\Captcha\Model\DefaultModel as CaptchaModel; use Magento\Customer\Model\Data\Customer as CustomerData; +use Magento\Customer\Model\Session; use Magento\Framework\App\Action\Context as ActionContext; use Magento\Framework\App\RequestInterface; use Magento\Framework\Controller\Result\Redirect as ResultRedirect; @@ -14,15 +17,13 @@ use Magento\Framework\Event\ManagerInterface as EventManagerInterface; use Magento\Framework\Mail\TransportInterface; use Magento\Framework\Message\ManagerInterface; +use Magento\Framework\Phrase; +use Magento\Framework\TestFramework\Unit\Helper\ObjectManager; use Magento\Framework\UrlInterface; use Magento\Framework\View\Result\Layout as ResultLayout; use Magento\Store\Model\Store; use Magento\Wishlist\Controller\Index\Send; use Magento\Wishlist\Controller\WishlistProviderInterface; -use Magento\Framework\TestFramework\Unit\Helper\ObjectManager; -use Magento\Captcha\Helper\Data as CaptchaHelper; -use Magento\Captcha\Model\DefaultModel as CaptchaModel; -use Magento\Customer\Model\Session; /** * @SuppressWarnings(PHPMD.TooManyFields) @@ -30,55 +31,55 @@ */ class SendTest extends \PHPUnit\Framework\TestCase { - /** @var Send |\PHPUnit_Framework_MockObject_MockObject */ + /** @var Send|\PHPUnit\Framework\MockObject\MockObject */ protected $model; - /** @var ActionContext |\PHPUnit_Framework_MockObject_MockObject */ + /** @var ActionContext|\PHPUnit\Framework\MockObject\MockObject */ protected $context; - /** @var FormKeyValidator |\PHPUnit_Framework_MockObject_MockObject */ + /** @var FormKeyValidator|\PHPUnit\Framework\MockObject\MockObject */ protected $formKeyValidator; - /** @var WishlistProviderInterface |\PHPUnit_Framework_MockObject_MockObject */ + /** @var WishlistProviderInterface|\PHPUnit\Framework\MockObject\MockObject */ protected $wishlistProvider; - /** @var Store |\PHPUnit_Framework_MockObject_MockObject */ + /** @var Store|\PHPUnit\Framework\MockObject\MockObject */ protected $store; - /** @var ResultFactory |\PHPUnit_Framework_MockObject_MockObject */ + /** @var ResultFactory|\PHPUnit\Framework\MockObject\MockObject */ protected $resultFactory; - /** @var ResultRedirect |\PHPUnit_Framework_MockObject_MockObject */ + /** @var ResultRedirect|\PHPUnit\Framework\MockObject\MockObject */ protected $resultRedirect; - /** @var ResultLayout |\PHPUnit_Framework_MockObject_MockObject */ + /** @var ResultLayout|\PHPUnit\Framework\MockObject\MockObject */ protected $resultLayout; - /** @var RequestInterface |\PHPUnit_Framework_MockObject_MockObject */ + /** @var RequestInterface|\PHPUnit\Framework\MockObject\MockObject */ protected $request; - /** @var ManagerInterface |\PHPUnit_Framework_MockObject_MockObject */ + /** @var ManagerInterface|\PHPUnit\Framework\MockObject\MockObject */ protected $messageManager; - /** @var CustomerData |\PHPUnit_Framework_MockObject_MockObject */ + /** @var CustomerData|\PHPUnit\Framework\MockObject\MockObject */ protected $customerData; - /** @var UrlInterface |\PHPUnit_Framework_MockObject_MockObject */ + /** @var UrlInterface|\PHPUnit\Framework\MockObject\MockObject */ protected $url; - /** @var TransportInterface |\PHPUnit_Framework_MockObject_MockObject */ + /** @var TransportInterface|\PHPUnit\Framework\MockObject\MockObject */ protected $transport; - /** @var EventManagerInterface |\PHPUnit_Framework_MockObject_MockObject */ + /** @var EventManagerInterface|\PHPUnit\Framework\MockObject\MockObject */ protected $eventManager; - /** @var CaptchaHelper |\PHPUnit_Framework_MockObject_MockObject */ + /** @var CaptchaHelper|\PHPUnit\Framework\MockObject\MockObject */ protected $captchaHelper; - /** @var CaptchaModel |\PHPUnit_Framework_MockObject_MockObject */ + /** @var CaptchaModel|\PHPUnit\Framework\MockObject\MockObject */ protected $captchaModel; - /** @var Session |\PHPUnit_Framework_MockObject_MockObject */ + /** @var Session|\PHPUnit\Framework\MockObject\MockObject */ protected $customerSession; /** @@ -212,7 +213,12 @@ protected function setUp() ); } - public function testExecuteNoFormKeyValidated() + /** + * Verify execute method without Form Key validated + * + * @return void + */ + public function testExecuteNoFormKeyValidated(): void { $this->formKeyValidator->expects($this->once()) ->method('validate') @@ -228,8 +234,43 @@ public function testExecuteNoFormKeyValidated() } /** - * @expectedException \Magento\Framework\Exception\NotFoundException - * @expectedExceptionMessage Page not found. + * Verify execute with no emails left + * + * @return void + */ + public function testExecuteWithNoEmailLeft(): void + { + $expectedMessage = new Phrase('Maximum of %1 emails can be sent.', [0]); + + $this->formKeyValidator->expects($this->once()) + ->method('validate') + ->with($this->request) + ->willReturn(true); + + $this->request->expects($this->at(0)) + ->method('getPost') + ->with('emails') + ->willReturn('some.Email@gmail.com', 'some.email2@gmail.com'); + $this->request->expects($this->at(1)) + ->method('getPost') + ->with('message'); + $wishlist = $this->createMock(\Magento\Wishlist\Model\Wishlist::class); + $this->wishlistProvider->expects($this->once()) + ->method('getWishlist') + ->willReturn($wishlist); + $this->resultRedirect->expects($this->once()) + ->method('setPath') + ->with('*/*/share') + ->willReturnSelf(); + $this->messageManager->expects($this->once()) + ->method('addErrorMessage') + ->with($expectedMessage); + + $this->assertEquals($this->resultRedirect, $this->model->execute()); + } + + /** + * Execute method with no wishlist available */ public function testExecuteNoWishlistAvailable() { @@ -241,6 +282,8 @@ public function testExecuteNoWishlistAvailable() $this->wishlistProvider->expects($this->once()) ->method('getWishlist') ->willReturn(null); + $this->expectException(\Magento\Framework\Exception\NotFoundException::class); + $this->expectExceptionMessage('Page not found'); $this->model->execute(); } diff --git a/app/code/Magento/Wishlist/Test/Unit/Controller/Index/ShareTest.php b/app/code/Magento/Wishlist/Test/Unit/Controller/Index/ShareTest.php index cf97489f1fbc0..6d7746a1f8e76 100644 --- a/app/code/Magento/Wishlist/Test/Unit/Controller/Index/ShareTest.php +++ b/app/code/Magento/Wishlist/Test/Unit/Controller/Index/ShareTest.php @@ -16,17 +16,17 @@ class ShareTest extends \PHPUnit\Framework\TestCase protected $model; /** - * @var \PHPUnit_Framework_MockObject_MockObject + * @var \PHPUnit\Framework\MockObject\MockObject */ protected $customerSessionMock; /** - * @var \PHPUnit_Framework_MockObject_MockObject + * @var \PHPUnit\Framework\MockObject\MockObject */ protected $contextMock; /** - * @var \PHPUnit_Framework_MockObject_MockObject + * @var \PHPUnit\Framework\MockObject\MockObject */ protected $resultFactoryMock; diff --git a/app/code/Magento/Wishlist/Test/Unit/Controller/Index/UpdateItemOptionsTest.php b/app/code/Magento/Wishlist/Test/Unit/Controller/Index/UpdateItemOptionsTest.php index b6fd509214897..815e6bbe0adc6 100644 --- a/app/code/Magento/Wishlist/Test/Unit/Controller/Index/UpdateItemOptionsTest.php +++ b/app/code/Magento/Wishlist/Test/Unit/Controller/Index/UpdateItemOptionsTest.php @@ -13,62 +13,62 @@ class UpdateItemOptionsTest extends \PHPUnit\Framework\TestCase { /** - * @var \Magento\Catalog\Model\ProductRepository|\PHPUnit_Framework_MockObject_MockObject + * @var \Magento\Catalog\Model\ProductRepository|\PHPUnit\Framework\MockObject\MockObject */ protected $productRepository; /** - * @var \Magento\Wishlist\Controller\WishlistProvider|\PHPUnit_Framework_MockObject_MockObject + * @var \Magento\Wishlist\Controller\WishlistProvider|\PHPUnit\Framework\MockObject\MockObject */ protected $wishlistProvider; /** - * @var \Magento\Framework\App\Action\Context|\PHPUnit_Framework_MockObject_MockObject + * @var \Magento\Framework\App\Action\Context|\PHPUnit\Framework\MockObject\MockObject */ protected $context; /** - * @var \Magento\Framework\App\Request\Http|\PHPUnit_Framework_MockObject_MockObject + * @var \Magento\Framework\App\Request\Http|\PHPUnit\Framework\MockObject\MockObject */ protected $request; /** - * @var \Magento\Framework\App\ObjectManager|\PHPUnit_Framework_MockObject_MockObject + * @var \Magento\Framework\App\ObjectManager|\PHPUnit\Framework\MockObject\MockObject */ protected $om; /** - * @var \Magento\Framework\Message\Manager|\PHPUnit_Framework_MockObject_MockObject + * @var \Magento\Framework\Message\Manager|\PHPUnit\Framework\MockObject\MockObject */ protected $messageManager; /** - * @var \Magento\Framework\Url|\PHPUnit_Framework_MockObject_MockObject + * @var \Magento\Framework\Url|\PHPUnit\Framework\MockObject\MockObject */ protected $url; /** - * @var \Magento\Customer\Model\Session|\PHPUnit_Framework_MockObject_MockObject + * @var \Magento\Customer\Model\Session|\PHPUnit\Framework\MockObject\MockObject */ protected $customerSession; /** - * @var \Magento\Framework\Event\Manager|\PHPUnit_Framework_MockObject_MockObject + * @var \Magento\Framework\Event\Manager|\PHPUnit\Framework\MockObject\MockObject */ protected $eventManager; /** - * @var \Magento\Framework\Controller\ResultFactory|\PHPUnit_Framework_MockObject_MockObject + * @var \Magento\Framework\Controller\ResultFactory|\PHPUnit\Framework\MockObject\MockObject */ protected $resultFactoryMock; /** - * @var \Magento\Framework\Controller\Result\Redirect|\PHPUnit_Framework_MockObject_MockObject + * @var \Magento\Framework\Controller\Result\Redirect|\PHPUnit\Framework\MockObject\MockObject */ protected $resultRedirectMock; /** - * @var \Magento\Framework\Data\Form\FormKey\Validator|\PHPUnit_Framework_MockObject_MockObject + * @var \Magento\Framework\Data\Form\FormKey\Validator|\PHPUnit\Framework\MockObject\MockObject */ protected $formKeyValidator; diff --git a/app/code/Magento/Wishlist/Test/Unit/Controller/Index/UpdateTest.php b/app/code/Magento/Wishlist/Test/Unit/Controller/Index/UpdateTest.php new file mode 100644 index 0000000000000..738131b5c2930 --- /dev/null +++ b/app/code/Magento/Wishlist/Test/Unit/Controller/Index/UpdateTest.php @@ -0,0 +1,290 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +namespace Magento\Wishlist\Test\Unit\Controller\Index; + +use Magento\Backend\Model\View\Result\Redirect; +use Magento\Framework\App\Action\Context; +use Magento\Framework\App\RequestInterface; +use Magento\Framework\Controller\ResultFactory; +use Magento\Framework\Data\Form\FormKey\Validator; +use Magento\Framework\Exception\NotFoundException; +use Magento\Framework\Message\ManagerInterface; +use Magento\Framework\ObjectManagerInterface; +use Magento\Framework\TestFramework\Unit\Helper\ObjectManager as ObjectManagerHelper; +use Magento\Wishlist\Controller\Index\Update; +use Magento\Wishlist\Controller\WishlistProviderInterface; +use Magento\Wishlist\Helper\Data; +use Magento\Wishlist\Model\Item; +use Magento\Wishlist\Model\LocaleQuantityProcessor; +use Magento\Wishlist\Model\Wishlist; +use PHPUnit\Framework\TestCase; +use PHPUnit\Framework\MockObject\MockObject; + +/** + * Test for upate controller wishlist + * @SuppressWarnings(PHPMD.CouplingBetweenObjects) + */ +class UpdateTest extends TestCase +{ + private const STUB_ITEM_ID = 1; + + private const STUB_WISHLIST_PRODUCT_QTY = 21; + + /** + * @var MockObject|Validator $formKeyValidatorMock + */ + private $formKeyValidatorMock; + + /** + * @var MockObject|WishlistProviderInterface $wishlistProviderMock + */ + private $wishlistProviderMock; + + /** + * @var MockObject|LocaleQuantityProcessor $quantityProcessorMock + */ + private $quantityProcessorMock; + + /** + * @var Update $updateController + */ + private $updateController; + + /** + * @var MockObject|Context$contextMock + */ + private $contextMock; + + /** + * @var MockObject|Redirect $resultRedirectMock + */ + private $resultRedirectMock; + + /** + * @var MockObject|ResultFactory $resultFatoryMock + */ + private $resultFactoryMock; + + /** + * @var MockObject|RequestInterface $requestMock + */ + private $requestMock; + + /** + * @var MockObject|ObjectManagerInterface $objectManagerMock + */ + private $objectManagerMock; + + /** + * @var MockObject|ManagerInterface $messageManagerMock + */ + private $messageManagerMock; + + /** + * @inheritdoc + */ + protected function setUp() + { + $this->formKeyValidatorMock = $this->createMock(Validator::class); + $this->wishlistProviderMock = $this->createMock(WishlistProviderInterface::class); + $this->quantityProcessorMock = $this->createMock(LocaleQuantityProcessor::class); + $this->contextMock = $this->createMock(Context::class); + $this->resultRedirectMock = $this->createMock(Redirect::class); + $this->resultFactoryMock = $this->createPartialMock(ResultFactory::class, ['create']); + $this->messageManagerMock = $this->createMock(ManagerInterface::class); + $this->objectManagerMock = $this->createMock(ObjectManagerInterface::class); + $this->requestMock = $this->getMockBuilder(RequestInterface::class) + ->setMethods(['getPostValue']) + ->getMockForAbstractClass(); + + $this->resultFactoryMock->expects($this->any()) + ->method('create') + ->willReturn($this->resultRedirectMock); + $this->contextMock->expects($this->once()) + ->method('getResultFactory') + ->willReturn($this->resultFactoryMock); + $this->contextMock->expects($this->once()) + ->method('getObjectManager') + ->willReturn($this->objectManagerMock); + $this->contextMock->expects($this->any()) + ->method('getRequest') + ->willReturn($this->requestMock); + $this->contextMock->expects($this->any()) + ->method('getMessageManager') + ->willReturn($this->messageManagerMock); + + $objectManager = new ObjectManagerHelper($this); + + $this->updateController = $objectManager->getObject( + Update::class, + [ + 'context' => $this->contextMock, + '_formKeyValidator' => $this->formKeyValidatorMock, + 'wishlistProvider' => $this->wishlistProviderMock, + 'quantityProcessor' => $this->quantityProcessorMock + ] + ); + } + + /** + * Test for update method Wishlist controller. + * + * @dataProvider getWishlistDataProvider + * @param array $wishlistDataProvider + * @param array $postData + * @return void + */ + public function testUpdate(array $wishlistDataProvider, array $postData): void + { + $wishlist = $this->createMock(Wishlist::class); + $itemMock = $this->getMockBuilder(Item::class) + ->disableOriginalConstructor() + ->setMethods( + [ + 'load', + 'getId', + 'getWishlistId', + 'setQty', + 'save', + 'getDescription', + 'setDescription', + 'getProduct', + 'getName' + ] + )->getMock(); + $dataMock = $this->createMock(Data::class); + $productMock = $this->getMockBuilder(\Magento\Catalog\Model\Product::class) + ->disableOriginalConstructor() + ->getMock(); + + $this->formKeyValidatorMock->expects($this->once()) + ->method('validate') + ->with($this->requestMock) + ->willReturn(true); + $this->wishlistProviderMock->expects($this->once()) + ->method('getWishlist') + ->willReturn($wishlist); + $wishlist->expects($this->exactly(2)) + ->method('getId') + ->willReturn($wishlistDataProvider['id']); + $this->requestMock->expects($this->once()) + ->method('getPostValue') + ->willReturn($postData); + $this->resultRedirectMock->expects($this->once()) + ->method('setPath') + ->with('*', ['wishlist_id' => $wishlistDataProvider['id']]); + $this->objectManagerMock->expects($this->once()) + ->method('create') + ->with(Item::class) + ->willReturn($itemMock); + $itemMock->expects($this->once()) + ->method('load') + ->with(1) + ->willReturnSelf(); + $itemMock->expects($this->once()) + ->method('getWishLIstId') + ->willReturn($wishlistDataProvider['id']); + $itemMock->expects($this->once()) + ->method('getDescription') + ->willReturn(''); + $itemMock->expects($this->once()) + ->method('setDescription') + ->willReturnSelf(); + $itemMock->expects($this->once()) + ->method('setQty') + ->willReturnSelf(); + $this->objectManagerMock->expects($this->exactly(2)) + ->method('get') + ->with(Data::class) + ->willReturn($dataMock); + $dataMock->expects($this->once()) + ->method('defaultCommentString') + ->willReturn(''); + $dataMock->expects($this->once()) + ->method('calculate'); + $this->quantityProcessorMock->expects($this->once()) + ->method('process') + ->willReturn($postData['qty']); + $itemMock->expects($this->once()) + ->method('getProduct') + ->willReturn($productMock); + $productMock->expects($this->once()) + ->method('getName') + ->willReturn('product'); + $this->messageManagerMock->expects($this->once()) + ->method('addSuccessMessage'); + + $this->assertEquals($this->resultRedirectMock, $this->updateController->execute()); + } + + /** + * Verify update method if post data not available + * + * @dataProvider getWishlistDataProvider + * @param array $wishlistDataProvider + * @return void + */ + public function testUpdateRedirectWhenNoPostData(array $wishlistDataProvider): void + { + $wishlist = $this->createMock(Wishlist::class); + + $this->formKeyValidatorMock->expects($this->once()) + ->method('validate') + ->willReturn(true); + $this->wishlistProviderMock->expects($this->once()) + ->method('getWishlist') + ->willReturn($wishlist); + $wishlist->expects($this->exactly(1)) + ->method('getId') + ->willReturn($wishlistDataProvider['id']); + $this->resultRedirectMock->expects($this->once()) + ->method('setPath') + ->with('*', ['wishlist_id' => $wishlistDataProvider['id']]); + $this->requestMock->expects($this->once()) + ->method('getPostValue') + ->willReturn(null); + + $this->assertEquals($this->resultRedirectMock, $this->updateController->execute()); + } + + /** + * Check if wishlist not availbale, and exception is shown + * + * @return void + */ + public function testUpdateThrowsNotFoundExceptionWhenWishlistDoNotExist(): void + { + $this->formKeyValidatorMock->expects($this->once()) + ->method('validate') + ->willReturn(true); + $this->wishlistProviderMock->expects($this->once()) + ->method('getWishlist') + ->willReturn(null); + $this->expectException(NotFoundException::class); + $this->updateController->execute(); + } + + /** + * Dataprovider for Update test + * + * @return array + */ + public function getWishlistDataProvider(): array + { + return + [ + [ + [ + 'id' => self::STUB_ITEM_ID + ], + [ + 'qty' => [self::STUB_ITEM_ID => self::STUB_WISHLIST_PRODUCT_QTY], + 'description' => [self::STUB_ITEM_ID => 'Description for item_id 1'] + ] + ] + ]; + } +} diff --git a/app/code/Magento/Wishlist/Test/Unit/Controller/Shared/AllcartTest.php b/app/code/Magento/Wishlist/Test/Unit/Controller/Shared/AllcartTest.php index 24438dbc71d99..d5ac5e9485424 100644 --- a/app/code/Magento/Wishlist/Test/Unit/Controller/Shared/AllcartTest.php +++ b/app/code/Magento/Wishlist/Test/Unit/Controller/Shared/AllcartTest.php @@ -26,37 +26,37 @@ class AllcartTest extends \PHPUnit\Framework\TestCase protected $context; /** - * @var \Magento\Wishlist\Controller\Shared\WishlistProvider|\PHPUnit_Framework_MockObject_MockObject + * @var \Magento\Wishlist\Controller\Shared\WishlistProvider|\PHPUnit\Framework\MockObject\MockObject */ protected $wishlistProviderMock; /** - * @var \Magento\Wishlist\Model\ItemCarrier|\PHPUnit_Framework_MockObject_MockObject + * @var \Magento\Wishlist\Model\ItemCarrier|\PHPUnit\Framework\MockObject\MockObject */ protected $itemCarrierMock; /** - * @var \Magento\Wishlist\Model\Wishlist|\PHPUnit_Framework_MockObject_MockObject + * @var \Magento\Wishlist\Model\Wishlist|\PHPUnit\Framework\MockObject\MockObject */ protected $wishlistMock; /** - * @var \Magento\Framework\App\Request\Http|\PHPUnit_Framework_MockObject_MockObject + * @var \Magento\Framework\App\Request\Http|\PHPUnit\Framework\MockObject\MockObject */ protected $requestMock; /** - * @var \Magento\Framework\Controller\ResultFactory|\PHPUnit_Framework_MockObject_MockObject + * @var \Magento\Framework\Controller\ResultFactory|\PHPUnit\Framework\MockObject\MockObject */ protected $resultFactoryMock; /** - * @var \Magento\Framework\Controller\Result\Redirect|\PHPUnit_Framework_MockObject_MockObject + * @var \Magento\Framework\Controller\Result\Redirect|\PHPUnit\Framework\MockObject\MockObject */ protected $resultRedirectMock; /** - * @var \Magento\Framework\Controller\Result\Forward|\PHPUnit_Framework_MockObject_MockObject + * @var \Magento\Framework\Controller\Result\Forward|\PHPUnit\Framework\MockObject\MockObject */ protected $resultForwardMock; diff --git a/app/code/Magento/Wishlist/Test/Unit/Controller/Shared/CartTest.php b/app/code/Magento/Wishlist/Test/Unit/Controller/Shared/CartTest.php index c65f166957c5f..eba5666114139 100644 --- a/app/code/Magento/Wishlist/Test/Unit/Controller/Shared/CartTest.php +++ b/app/code/Magento/Wishlist/Test/Unit/Controller/Shared/CartTest.php @@ -30,55 +30,55 @@ */ class CartTest extends \PHPUnit\Framework\TestCase { - /** @var SharedCart |\PHPUnit_Framework_MockObject_MockObject */ + /** @var SharedCart|\PHPUnit\Framework\MockObject\MockObject */ protected $model; - /** @var RequestInterface |\PHPUnit_Framework_MockObject_MockObject */ + /** @var RequestInterface|\PHPUnit\Framework\MockObject\MockObject */ protected $request; - /** @var ManagerInterface |\PHPUnit_Framework_MockObject_MockObject */ + /** @var ManagerInterface|\PHPUnit\Framework\MockObject\MockObject */ protected $messageManager; - /** @var ActionContext |\PHPUnit_Framework_MockObject_MockObject */ + /** @var ActionContext|\PHPUnit\Framework\MockObject\MockObject */ protected $context; - /** @var Cart |\PHPUnit_Framework_MockObject_MockObject */ + /** @var Cart|\PHPUnit\Framework\MockObject\MockObject */ protected $cart; - /** @var CartHelper |\PHPUnit_Framework_MockObject_MockObject */ + /** @var CartHelper|\PHPUnit\Framework\MockObject\MockObject */ protected $cartHelper; - /** @var Quote | \PHPUnit_Framework_MockObject_MockObject */ + /** @var Quote|\PHPUnit\Framework\MockObject\MockObject */ protected $quote; - /** @var OptionCollection |\PHPUnit_Framework_MockObject_MockObject */ + /** @var OptionCollection|\PHPUnit\Framework\MockObject\MockObject */ protected $optionCollection; - /** @var OptionFactory |\PHPUnit_Framework_MockObject_MockObject */ + /** @var OptionFactory|\PHPUnit\Framework\MockObject\MockObject */ protected $optionFactory; - /** @var Option |\PHPUnit_Framework_MockObject_MockObject */ + /** @var Option|\PHPUnit\Framework\MockObject\MockObject */ protected $option; - /** @var ItemFactory |\PHPUnit_Framework_MockObject_MockObject */ + /** @var ItemFactory|\PHPUnit\Framework\MockObject\MockObject */ protected $itemFactory; - /** @var Item |\PHPUnit_Framework_MockObject_MockObject */ + /** @var Item|\PHPUnit\Framework\MockObject\MockObject */ protected $item; - /** @var Escaper |\PHPUnit_Framework_MockObject_MockObject */ + /** @var Escaper|\PHPUnit\Framework\MockObject\MockObject */ protected $escaper; - /** @var RedirectInterface |\PHPUnit_Framework_MockObject_MockObject */ + /** @var RedirectInterface|\PHPUnit\Framework\MockObject\MockObject */ protected $redirect; - /** @var ResultFactory |\PHPUnit_Framework_MockObject_MockObject */ + /** @var ResultFactory|\PHPUnit\Framework\MockObject\MockObject */ protected $resultFactory; - /** @var Redirect |\PHPUnit_Framework_MockObject_MockObject */ + /** @var Redirect|\PHPUnit\Framework\MockObject\MockObject */ protected $resultRedirect; - /** @var Product |\PHPUnit_Framework_MockObject_MockObject */ + /** @var Product|\PHPUnit\Framework\MockObject\MockObject */ protected $product; protected function setUp() diff --git a/app/code/Magento/Wishlist/Test/Unit/CustomerData/WishlistTest.php b/app/code/Magento/Wishlist/Test/Unit/CustomerData/WishlistTest.php index 4954712e5ff3b..325922f0bc4e3 100644 --- a/app/code/Magento/Wishlist/Test/Unit/CustomerData/WishlistTest.php +++ b/app/code/Magento/Wishlist/Test/Unit/CustomerData/WishlistTest.php @@ -26,19 +26,19 @@ class WishlistTest extends \PHPUnit\Framework\TestCase /** @var Wishlist */ private $model; - /** @var Data|\PHPUnit_Framework_MockObject_MockObject */ + /** @var Data|\PHPUnit\Framework\MockObject\MockObject */ private $wishlistHelperMock; - /** @var Sidebar|\PHPUnit_Framework_MockObject_MockObject */ + /** @var Sidebar|\PHPUnit\Framework\MockObject\MockObject */ private $sidebarMock; - /** @var Image|\PHPUnit_Framework_MockObject_MockObject */ + /** @var Image|\PHPUnit\Framework\MockObject\MockObject */ private $catalogImageHelperMock; - /** @var ViewInterface|\PHPUnit_Framework_MockObject_MockObject */ + /** @var ViewInterface|\PHPUnit\Framework\MockObject\MockObject */ private $viewMock; - /** @var \Magento\Catalog\Block\Product\ImageBuilder|\PHPUnit_Framework_MockObject_MockObject */ + /** @var \Magento\Catalog\Block\Product\ImageBuilder|\PHPUnit\Framework\MockObject\MockObject */ private $itemResolver; protected function setUp() @@ -120,7 +120,7 @@ public function testGetSectionData() ], ]; - /** @var Item|\PHPUnit_Framework_MockObject_MockObject $itemMock */ + /** @var Item|\PHPUnit\Framework\MockObject\MockObject $itemMock */ $itemMock = $this->getMockBuilder(\Magento\Wishlist\Model\Item::class) ->disableOriginalConstructor() ->getMock(); @@ -133,7 +133,7 @@ public function testGetSectionData() $this->viewMock->expects($this->once()) ->method('loadLayout'); - /** @var Collection|\PHPUnit_Framework_MockObject_MockObject $itemCollectionMock */ + /** @var Collection|\PHPUnit\Framework\MockObject\MockObject $itemCollectionMock */ $itemCollectionMock = $this->getMockBuilder(\Magento\Wishlist\Model\ResourceModel\Item\Collection::class) ->disableOriginalConstructor() ->getMock(); @@ -161,7 +161,7 @@ public function testGetSectionData() ->method('getIterator') ->willReturn(new \ArrayIterator($items)); - /** @var Product|\PHPUnit_Framework_MockObject_MockObject $productMock */ + /** @var Product|\PHPUnit\Framework\MockObject\MockObject $productMock */ $productMock = $this->getMockBuilder(\Magento\Catalog\Model\Product::class) ->disableOriginalConstructor() ->getMock(); @@ -232,7 +232,7 @@ public function testGetSectionData() ->method('isVisibleInSiteVisibility') ->willReturn($productIsVisible); - /** @var AbstractType|\PHPUnit_Framework_MockObject_MockObject $productTypeMock */ + /** @var AbstractType|\PHPUnit\Framework\MockObject\MockObject $productTypeMock */ $productTypeMock = $this->getMockBuilder(\Magento\Catalog\Model\Product\Type\AbstractType::class) ->disableOriginalConstructor() ->setMethods(['hasRequiredOptions']) @@ -279,7 +279,7 @@ public function testGetSectionDataWithTwoItems() $itemAddParams = ['add_params']; $itemRemoveParams = ['remove_params']; - /** @var Item|\PHPUnit_Framework_MockObject_MockObject $itemMock */ + /** @var Item|\PHPUnit\Framework\MockObject\MockObject $itemMock */ $itemMock = $this->getMockBuilder(\Magento\Wishlist\Model\Item::class) ->disableOriginalConstructor() ->getMock(); @@ -334,7 +334,7 @@ public function testGetSectionDataWithTwoItems() $this->viewMock->expects($this->once()) ->method('loadLayout'); - /** @var Collection|\PHPUnit_Framework_MockObject_MockObject $itemCollectionMock */ + /** @var Collection|\PHPUnit\Framework\MockObject\MockObject $itemCollectionMock */ $itemCollectionMock = $this->getMockBuilder(\Magento\Wishlist\Model\ResourceModel\Item\Collection::class) ->disableOriginalConstructor() ->getMock(); @@ -362,7 +362,7 @@ public function testGetSectionDataWithTwoItems() ->method('getIterator') ->willReturn(new \ArrayIterator($items)); - /** @var Product|\PHPUnit_Framework_MockObject_MockObject $productMock */ + /** @var Product|\PHPUnit\Framework\MockObject\MockObject $productMock */ $productMock = $this->getMockBuilder(\Magento\Catalog\Model\Product::class) ->disableOriginalConstructor() ->getMock(); @@ -434,7 +434,7 @@ public function testGetSectionDataWithTwoItems() $productMock->expects($this->never()) ->method('isVisibleInSiteVisibility'); - /** @var AbstractType|\PHPUnit_Framework_MockObject_MockObject $productTypeMock */ + /** @var AbstractType|\PHPUnit\Framework\MockObject\MockObject $productTypeMock */ $productTypeMock = $this->getMockBuilder(\Magento\Catalog\Model\Product\Type\AbstractType::class) ->disableOriginalConstructor() ->setMethods(['hasRequiredOptions']) diff --git a/app/code/Magento/Wishlist/Test/Unit/DataProvider/Product/Collector/ButtonTest.php b/app/code/Magento/Wishlist/Test/Unit/DataProvider/Product/Collector/ButtonTest.php index 8fd514796d949..89114fe34dc62 100644 --- a/app/code/Magento/Wishlist/Test/Unit/DataProvider/Product/Collector/ButtonTest.php +++ b/app/code/Magento/Wishlist/Test/Unit/DataProvider/Product/Collector/ButtonTest.php @@ -23,13 +23,13 @@ class ButtonTest extends \PHPUnit\Framework\TestCase /** @var \Magento\Wishlist\Ui\DataProvider\Product\Collector\Button */ private $button; - /** @var ProductRenderExtensionFactory|\PHPUnit_Framework_MockObject_MockObject */ + /** @var ProductRenderExtensionFactory|\PHPUnit\Framework\MockObject\MockObject */ private $productRenderExtensionFactoryMock; - /** @var Data|\PHPUnit_Framework_MockObject_MockObject */ + /** @var Data|\PHPUnit\Framework\MockObject\MockObject */ private $wishlistHelperMock; - /** @var ButtonInterfaceFactory|\PHPUnit_Framework_MockObject_MockObject */ + /** @var ButtonInterfaceFactory|\PHPUnit\Framework\MockObject\MockObject */ private $buttonInterfaceFactoryMock; protected function setUp() diff --git a/app/code/Magento/Wishlist/Test/Unit/Helper/DataTest.php b/app/code/Magento/Wishlist/Test/Unit/Helper/DataTest.php index 263f3c5d1688e..3a43705120e9e 100644 --- a/app/code/Magento/Wishlist/Test/Unit/Helper/DataTest.php +++ b/app/code/Magento/Wishlist/Test/Unit/Helper/DataTest.php @@ -28,43 +28,43 @@ class DataTest extends \PHPUnit\Framework\TestCase /** @var \Magento\Wishlist\Helper\Data */ protected $model; - /** @var WishlistProviderInterface |\PHPUnit_Framework_MockObject_MockObject */ + /** @var WishlistProviderInterface|\PHPUnit\Framework\MockObject\MockObject */ protected $wishlistProvider; - /** @var Registry |\PHPUnit_Framework_MockObject_MockObject */ + /** @var Registry|\PHPUnit\Framework\MockObject\MockObject */ protected $coreRegistry; - /** @var PostHelper |\PHPUnit_Framework_MockObject_MockObject */ + /** @var PostHelper|\PHPUnit\Framework\MockObject\MockObject */ protected $postDataHelper; - /** @var WishlistItem |\PHPUnit_Framework_MockObject_MockObject */ + /** @var WishlistItem|\PHPUnit\Framework\MockObject\MockObject */ protected $wishlistItem; - /** @var Product |\PHPUnit_Framework_MockObject_MockObject */ + /** @var Product|\PHPUnit\Framework\MockObject\MockObject */ protected $product; - /** @var StoreManagerInterface |\PHPUnit_Framework_MockObject_MockObject */ + /** @var StoreManagerInterface|\PHPUnit\Framework\MockObject\MockObject */ protected $storeManager; - /** @var Store |\PHPUnit_Framework_MockObject_MockObject */ + /** @var Store|\PHPUnit\Framework\MockObject\MockObject */ protected $store; - /** @var UrlInterface |\PHPUnit_Framework_MockObject_MockObject */ + /** @var UrlInterface|\PHPUnit\Framework\MockObject\MockObject */ protected $urlBuilder; - /** @var Wishlist |\PHPUnit_Framework_MockObject_MockObject */ + /** @var Wishlist|\PHPUnit\Framework\MockObject\MockObject */ protected $wishlist; - /** @var EncoderInterface|\PHPUnit_Framework_MockObject_MockObject */ + /** @var EncoderInterface|\PHPUnit\Framework\MockObject\MockObject */ protected $urlEncoderMock; - /** @var RequestInterface|\PHPUnit_Framework_MockObject_MockObject */ + /** @var RequestInterface|\PHPUnit\Framework\MockObject\MockObject */ protected $requestMock; - /** @var Context |\PHPUnit_Framework_MockObject_MockObject */ + /** @var Context|\PHPUnit\Framework\MockObject\MockObject */ protected $context; - /** @var Session |\PHPUnit_Framework_MockObject_MockObject */ + /** @var Session|\PHPUnit\Framework\MockObject\MockObject */ protected $customerSession; /** @@ -180,7 +180,7 @@ public function testGetConfigureUrl() { $url = 'http://magento2ce/wishlist/index/configure/id/4/product_id/30/'; - /** @var \Magento\Wishlist\Model\Item|\PHPUnit_Framework_MockObject_MockObject $wishlistItem */ + /** @var \Magento\Wishlist\Model\Item|\PHPUnit\Framework\MockObject\MockObject $wishlistItem */ $wishlistItem = $this->createPartialMock( \Magento\Wishlist\Model\Item::class, ['getWishlistItemId', 'getProductId'] diff --git a/app/code/Magento/Wishlist/Test/Unit/Helper/RssTest.php b/app/code/Magento/Wishlist/Test/Unit/Helper/RssTest.php index d0397be83fac7..622c151b2f79a 100644 --- a/app/code/Magento/Wishlist/Test/Unit/Helper/RssTest.php +++ b/app/code/Magento/Wishlist/Test/Unit/Helper/RssTest.php @@ -16,42 +16,42 @@ class RssTest extends \PHPUnit\Framework\TestCase protected $model; /** - * @var \Magento\Wishlist\Model\WishlistFactory|\PHPUnit_Framework_MockObject_MockObject + * @var \Magento\Wishlist\Model\WishlistFactory|\PHPUnit\Framework\MockObject\MockObject */ protected $wishlistFactoryMock; /** - * @var \Magento\Framework\App\RequestInterface|\PHPUnit_Framework_MockObject_MockObject + * @var \Magento\Framework\App\RequestInterface|\PHPUnit\Framework\MockObject\MockObject */ protected $requestMock; /** - * @var \Magento\Framework\Url\DecoderInterface|\PHPUnit_Framework_MockObject_MockObject + * @var \Magento\Framework\Url\DecoderInterface|\PHPUnit\Framework\MockObject\MockObject */ protected $urlDecoderMock; /** - * @var \Magento\Customer\Api\Data\CustomerInterfaceFactory|\PHPUnit_Framework_MockObject_MockObject + * @var \Magento\Customer\Api\Data\CustomerInterfaceFactory|\PHPUnit\Framework\MockObject\MockObject */ protected $customerFactoryMock; /** - * @var \Magento\Customer\Model\Session|\PHPUnit_Framework_MockObject_MockObject + * @var \Magento\Customer\Model\Session|\PHPUnit\Framework\MockObject\MockObject */ protected $customerSessionMock; /** - * @var \Magento\Customer\Api\CustomerRepositoryInterface|\PHPUnit_Framework_MockObject_MockObject + * @var \Magento\Customer\Api\CustomerRepositoryInterface|\PHPUnit\Framework\MockObject\MockObject */ protected $customerRepositoryMock; /** - * @var \Magento\Framework\Module\Manager|\PHPUnit_Framework_MockObject_MockObject + * @var \Magento\Framework\Module\Manager|\PHPUnit\Framework\MockObject\MockObject */ protected $moduleManagerMock; /** - * @var \Magento\Framework\App\Config\ScopeConfigInterface|\PHPUnit_Framework_MockObject_MockObject + * @var \Magento\Framework\App\Config\ScopeConfigInterface|\PHPUnit\Framework\MockObject\MockObject */ protected $scopeConfigMock; diff --git a/app/code/Magento/Wishlist/Test/Unit/Model/ConfigTest.php b/app/code/Magento/Wishlist/Test/Unit/Model/ConfigTest.php index 914d9aca65602..fc9efb38747f9 100644 --- a/app/code/Magento/Wishlist/Test/Unit/Model/ConfigTest.php +++ b/app/code/Magento/Wishlist/Test/Unit/Model/ConfigTest.php @@ -15,17 +15,17 @@ class ConfigTest extends \PHPUnit\Framework\TestCase protected $model; /** - * @var \Magento\Framework\App\Config\ScopeConfigInterface|\PHPUnit_Framework_MockObject_MockObject + * @var \Magento\Framework\App\Config\ScopeConfigInterface|\PHPUnit\Framework\MockObject\MockObject */ protected $_scopeConfig; /** - * @var \Magento\Catalog\Model\Config|\PHPUnit_Framework_MockObject_MockObject + * @var \Magento\Catalog\Model\Config|\PHPUnit\Framework\MockObject\MockObject */ protected $_catalogConfig; /** - * @var \Magento\Catalog\Model\Attribute\Config|\PHPUnit_Framework_MockObject_MockObject + * @var \Magento\Catalog\Model\Attribute\Config|\PHPUnit\Framework\MockObject\MockObject */ protected $_attributeConfig; diff --git a/app/code/Magento/Wishlist/Test/Unit/Model/ItemCarrierTest.php b/app/code/Magento/Wishlist/Test/Unit/Model/ItemCarrierTest.php index 71ae2d182d0e4..3b9e734c89598 100644 --- a/app/code/Magento/Wishlist/Test/Unit/Model/ItemCarrierTest.php +++ b/app/code/Magento/Wishlist/Test/Unit/Model/ItemCarrierTest.php @@ -15,31 +15,31 @@ class ItemCarrierTest extends \PHPUnit\Framework\TestCase /** @var \Magento\Wishlist\Model\ItemCarrier */ protected $model; - /** @var \Magento\Customer\Model\Session|\PHPUnit_Framework_MockObject_MockObject */ + /** @var \Magento\Customer\Model\Session|\PHPUnit\Framework\MockObject\MockObject */ protected $sessionMock; - /** @var \Magento\Wishlist\Model\LocaleQuantityProcessor|\PHPUnit_Framework_MockObject_MockObject */ + /** @var \Magento\Wishlist\Model\LocaleQuantityProcessor|\PHPUnit\Framework\MockObject\MockObject */ protected $quantityProcessorMock; - /** @var \Magento\Checkout\Model\Cart|\PHPUnit_Framework_MockObject_MockObject */ + /** @var \Magento\Checkout\Model\Cart|\PHPUnit\Framework\MockObject\MockObject */ protected $cartMock; - /** @var \Psr\Log\LoggerInterface|\PHPUnit_Framework_MockObject_MockObject */ + /** @var \Psr\Log\LoggerInterface|\PHPUnit\Framework\MockObject\MockObject */ protected $loggerMock; - /** @var \Magento\Wishlist\Helper\Data|\PHPUnit_Framework_MockObject_MockObject */ + /** @var \Magento\Wishlist\Helper\Data|\PHPUnit\Framework\MockObject\MockObject */ protected $wishlistHelperMock; - /** @var \Magento\Checkout\Helper\Cart|\PHPUnit_Framework_MockObject_MockObject */ + /** @var \Magento\Checkout\Helper\Cart|\PHPUnit\Framework\MockObject\MockObject */ protected $cartHelperMock; - /** @var \Magento\Framework\UrlInterface|\PHPUnit_Framework_MockObject_MockObject */ + /** @var \Magento\Framework\UrlInterface|\PHPUnit\Framework\MockObject\MockObject */ protected $urlBuilderMock; - /** @var \Magento\Framework\Message\ManagerInterface|\PHPUnit_Framework_MockObject_MockObject */ + /** @var \Magento\Framework\Message\ManagerInterface|\PHPUnit\Framework\MockObject\MockObject */ protected $managerMock; - /** @var \Magento\Framework\App\Response\RedirectInterface|\PHPUnit_Framework_MockObject_MockObject */ + /** @var \Magento\Framework\App\Response\RedirectInterface|\PHPUnit\Framework\MockObject\MockObject */ protected $redirectMock; protected function setUp() @@ -97,23 +97,23 @@ public function testMoveAllToCart() $indexUrl = 'index_url'; $redirectUrl = 'redirect_url'; - /** @var \Magento\Wishlist\Model\Item|\PHPUnit_Framework_MockObject_MockObject $itemOneMock */ + /** @var \Magento\Wishlist\Model\Item|\PHPUnit\Framework\MockObject\MockObject $itemOneMock */ $itemOneMock = $this->getMockBuilder(\Magento\Wishlist\Model\Item::class) ->setMethods(['getProduct', 'unsProduct', 'getId', 'setQty', 'addToCart', 'delete', 'getProductUrl']) ->disableOriginalConstructor() ->getMock(); - /** @var \Magento\Wishlist\Model\Item|\PHPUnit_Framework_MockObject_MockObject $itemTwoMock */ + /** @var \Magento\Wishlist\Model\Item|\PHPUnit\Framework\MockObject\MockObject $itemTwoMock */ $itemTwoMock = $this->getMockBuilder(\Magento\Wishlist\Model\Item::class) ->setMethods(['getProduct', 'unsProduct', 'getId', 'setQty', 'addToCart', 'delete', 'getProductUrl']) ->disableOriginalConstructor() ->getMock(); - /** @var \Magento\Catalog\Model\Product|\PHPUnit_Framework_MockObject_MockObject $productOneMock */ + /** @var \Magento\Catalog\Model\Product|\PHPUnit\Framework\MockObject\MockObject $productOneMock */ $productOneMock = $this->getMockBuilder(\Magento\Catalog\Model\Product::class) ->setMethods(['getDisableAddToCart', 'setDisableAddToCart', 'getName']) ->disableOriginalConstructor() ->getMock(); - /** @var \Magento\Catalog\Model\Product|\PHPUnit_Framework_MockObject_MockObject $productTwoMock */ + /** @var \Magento\Catalog\Model\Product|\PHPUnit\Framework\MockObject\MockObject $productTwoMock */ $productTwoMock = $this->getMockBuilder(\Magento\Catalog\Model\Product::class) ->setMethods(['getDisableAddToCart', 'setDisableAddToCart', 'getName']) ->disableOriginalConstructor() @@ -128,7 +128,7 @@ public function testMoveAllToCart() $collection = [$itemOneMock, $itemTwoMock]; - /** @var \Magento\Wishlist\Model\Wishlist|\PHPUnit_Framework_MockObject_MockObject $wishlistMock */ + /** @var \Magento\Wishlist\Model\Wishlist|\PHPUnit\Framework\MockObject\MockObject $wishlistMock */ $wishlistMock = $this->getMockBuilder(\Magento\Wishlist\Model\Wishlist::class) ->disableOriginalConstructor() ->getMock(); @@ -145,7 +145,7 @@ public function testMoveAllToCart() ->method('getId') ->willReturn($wishlistId); - /** @var Collection|\PHPUnit_Framework_MockObject_MockObject $collectionMock */ + /** @var Collection|\PHPUnit\Framework\MockObject\MockObject $collectionMock */ $collectionMock = $this->getMockBuilder(\Magento\Wishlist\Model\ResourceModel\Item\Collection::class) ->disableOriginalConstructor() ->getMock(); @@ -236,7 +236,7 @@ public function testMoveAllToCart() ->method('save') ->willReturnSelf(); - /** @var \Magento\Quote\Model\Quote|\PHPUnit_Framework_MockObject_MockObject $collectionMock */ + /** @var \Magento\Quote\Model\Quote|\PHPUnit\Framework\MockObject\MockObject $collectionMock */ $quoteMock = $this->getMockBuilder(\Magento\Quote\Model\Quote::class) ->disableOriginalConstructor() ->getMock(); @@ -272,23 +272,23 @@ public function testMoveAllToCartWithNotSalableAndOptions() $redirectUrl = 'redirect_url'; $sharingCode = 'sharingcode'; - /** @var \Magento\Wishlist\Model\Item|\PHPUnit_Framework_MockObject_MockObject $itemOneMock */ + /** @var \Magento\Wishlist\Model\Item|\PHPUnit\Framework\MockObject\MockObject $itemOneMock */ $itemOneMock = $this->getMockBuilder(\Magento\Wishlist\Model\Item::class) ->setMethods(['getProduct', 'unsProduct', 'getId', 'setQty', 'addToCart', 'delete', 'getProductUrl']) ->disableOriginalConstructor() ->getMock(); - /** @var \Magento\Wishlist\Model\Item|\PHPUnit_Framework_MockObject_MockObject $itemTwoMock */ + /** @var \Magento\Wishlist\Model\Item|\PHPUnit\Framework\MockObject\MockObject $itemTwoMock */ $itemTwoMock = $this->getMockBuilder(\Magento\Wishlist\Model\Item::class) ->setMethods(['getProduct', 'unsProduct', 'getId', 'setQty', 'addToCart', 'delete', 'getProductUrl']) ->disableOriginalConstructor() ->getMock(); - /** @var \Magento\Catalog\Model\Product|\PHPUnit_Framework_MockObject_MockObject $productOneMock */ + /** @var \Magento\Catalog\Model\Product|\PHPUnit\Framework\MockObject\MockObject $productOneMock */ $productOneMock = $this->getMockBuilder(\Magento\Catalog\Model\Product::class) ->setMethods(['getDisableAddToCart', 'setDisableAddToCart', 'getName']) ->disableOriginalConstructor() ->getMock(); - /** @var \Magento\Catalog\Model\Product|\PHPUnit_Framework_MockObject_MockObject $productTwoMock */ + /** @var \Magento\Catalog\Model\Product|\PHPUnit\Framework\MockObject\MockObject $productTwoMock */ $productTwoMock = $this->getMockBuilder(\Magento\Catalog\Model\Product::class) ->setMethods(['getDisableAddToCart', 'setDisableAddToCart', 'getName']) ->disableOriginalConstructor() @@ -303,7 +303,7 @@ public function testMoveAllToCartWithNotSalableAndOptions() $collection = [$itemOneMock, $itemTwoMock]; - /** @var \Magento\Wishlist\Model\Wishlist|\PHPUnit_Framework_MockObject_MockObject $wishlistMock */ + /** @var \Magento\Wishlist\Model\Wishlist|\PHPUnit\Framework\MockObject\MockObject $wishlistMock */ $wishlistMock = $this->getMockBuilder(\Magento\Wishlist\Model\Wishlist::class) ->setMethods(['isOwner', 'getItemCollection', 'getId', 'getSharingCode', 'save']) ->disableOriginalConstructor() @@ -318,7 +318,7 @@ public function testMoveAllToCartWithNotSalableAndOptions() ->with($sessionCustomerId) ->willReturn($isOwner); - /** @var Collection|\PHPUnit_Framework_MockObject_MockObject $collectionMock */ + /** @var Collection|\PHPUnit\Framework\MockObject\MockObject $collectionMock */ $collectionMock = $this->getMockBuilder(\Magento\Wishlist\Model\ResourceModel\Item\Collection::class) ->disableOriginalConstructor() ->getMock(); @@ -382,7 +382,7 @@ public function testMoveAllToCartWithNotSalableAndOptions() ->with($this->cartMock, $isOwner) ->willThrowException(new \Magento\Framework\Exception\LocalizedException(__('Localized Exception.'))); - /** @var \Magento\Quote\Model\Quote|\PHPUnit_Framework_MockObject_MockObject $collectionMock */ + /** @var \Magento\Quote\Model\Quote|\PHPUnit\Framework\MockObject\MockObject $collectionMock */ $quoteMock = $this->getMockBuilder(\Magento\Quote\Model\Quote::class) ->disableOriginalConstructor() ->getMock(); @@ -391,7 +391,7 @@ public function testMoveAllToCartWithNotSalableAndOptions() ->method('getQuote') ->willReturn($quoteMock); - /** @var \Magento\Quote\Model\Quote\Item|\PHPUnit_Framework_MockObject_MockObject $collectionMock */ + /** @var \Magento\Quote\Model\Quote\Item|\PHPUnit\Framework\MockObject\MockObject $collectionMock */ $itemMock = $this->getMockBuilder(\Magento\Quote\Model\Quote\Item::class) ->disableOriginalConstructor() ->getMock(); @@ -467,23 +467,23 @@ public function testMoveAllToCartWithException() $isOwner = true; $indexUrl = 'index_url'; - /** @var \Magento\Wishlist\Model\Item|\PHPUnit_Framework_MockObject_MockObject $itemOneMock */ + /** @var \Magento\Wishlist\Model\Item|\PHPUnit\Framework\MockObject\MockObject $itemOneMock */ $itemOneMock = $this->getMockBuilder(\Magento\Wishlist\Model\Item::class) ->setMethods(['getProduct', 'unsProduct', 'getId', 'setQty', 'addToCart', 'delete', 'getProductUrl']) ->disableOriginalConstructor() ->getMock(); - /** @var \Magento\Wishlist\Model\Item|\PHPUnit_Framework_MockObject_MockObject $itemTwoMock */ + /** @var \Magento\Wishlist\Model\Item|\PHPUnit\Framework\MockObject\MockObject $itemTwoMock */ $itemTwoMock = $this->getMockBuilder(\Magento\Wishlist\Model\Item::class) ->setMethods(['getProduct', 'unsProduct', 'getId', 'setQty', 'addToCart', 'delete', 'getProductUrl']) ->disableOriginalConstructor() ->getMock(); - /** @var \Magento\Catalog\Model\Product|\PHPUnit_Framework_MockObject_MockObject $productOneMock */ + /** @var \Magento\Catalog\Model\Product|\PHPUnit\Framework\MockObject\MockObject $productOneMock */ $productOneMock = $this->getMockBuilder(\Magento\Catalog\Model\Product::class) ->setMethods(['getDisableAddToCart', 'setDisableAddToCart', 'getName']) ->disableOriginalConstructor() ->getMock(); - /** @var \Magento\Catalog\Model\Product|\PHPUnit_Framework_MockObject_MockObject $productTwoMock */ + /** @var \Magento\Catalog\Model\Product|\PHPUnit\Framework\MockObject\MockObject $productTwoMock */ $productTwoMock = $this->getMockBuilder(\Magento\Catalog\Model\Product::class) ->setMethods(['getDisableAddToCart', 'setDisableAddToCart', 'getName']) ->disableOriginalConstructor() @@ -498,7 +498,7 @@ public function testMoveAllToCartWithException() $collection = [$itemOneMock, $itemTwoMock]; - /** @var \Magento\Wishlist\Model\Wishlist|\PHPUnit_Framework_MockObject_MockObject $wishlistMock */ + /** @var \Magento\Wishlist\Model\Wishlist|\PHPUnit\Framework\MockObject\MockObject $wishlistMock */ $wishlistMock = $this->getMockBuilder(\Magento\Wishlist\Model\Wishlist::class) ->disableOriginalConstructor() ->getMock(); @@ -515,7 +515,7 @@ public function testMoveAllToCartWithException() ->method('getId') ->willReturn($wishlistId); - /** @var Collection|\PHPUnit_Framework_MockObject_MockObject $collectionMock */ + /** @var Collection|\PHPUnit\Framework\MockObject\MockObject $collectionMock */ $collectionMock = $this->getMockBuilder(\Magento\Wishlist\Model\ResourceModel\Item\Collection::class) ->disableOriginalConstructor() ->getMock(); @@ -623,7 +623,7 @@ public function testMoveAllToCartWithException() ->method('save') ->willReturnSelf(); - /** @var \Magento\Quote\Model\Quote|\PHPUnit_Framework_MockObject_MockObject $collectionMock */ + /** @var \Magento\Quote\Model\Quote|\PHPUnit\Framework\MockObject\MockObject $collectionMock */ $quoteMock = $this->getMockBuilder(\Magento\Quote\Model\Quote::class) ->disableOriginalConstructor() ->getMock(); diff --git a/app/code/Magento/Wishlist/Test/Unit/Model/ItemTest.php b/app/code/Magento/Wishlist/Test/Unit/Model/ItemTest.php index 9876b3f6bb75e..29f4fbf4e4741 100644 --- a/app/code/Magento/Wishlist/Test/Unit/Model/ItemTest.php +++ b/app/code/Magento/Wishlist/Test/Unit/Model/ItemTest.php @@ -15,52 +15,52 @@ class ItemTest extends \PHPUnit\Framework\TestCase { /** - * @var \Magento\Framework\Registry|\PHPUnit_Framework_MockObject_MockObject + * @var \Magento\Framework\Registry|\PHPUnit\Framework\MockObject\MockObject */ protected $registry; /** - * @var \Magento\Catalog\Model\ResourceModel\Url|\PHPUnit_Framework_MockObject_MockObject + * @var \Magento\Catalog\Model\ResourceModel\Url|\PHPUnit\Framework\MockObject\MockObject */ protected $catalogUrl; /** - * @var \Magento\Catalog\Model\ProductTypes\ConfigInterface|\PHPUnit_Framework_MockObject_MockObject + * @var \Magento\Catalog\Model\ProductTypes\ConfigInterface|\PHPUnit\Framework\MockObject\MockObject */ protected $productTypeConfig; /** - * @var \Magento\Wishlist\Model\ResourceModel\Item|\PHPUnit_Framework_MockObject_MockObject + * @var \Magento\Wishlist\Model\ResourceModel\Item|\PHPUnit\Framework\MockObject\MockObject */ protected $resource; /** - * @var \Magento\Wishlist\Model\ResourceModel\Item\Collection|\PHPUnit_Framework_MockObject_MockObject + * @var \Magento\Wishlist\Model\ResourceModel\Item\Collection|\PHPUnit\Framework\MockObject\MockObject */ protected $collection; /** - * @var \Magento\Store\Model\StoreManagerInterface|\PHPUnit_Framework_MockObject_MockObject + * @var \Magento\Store\Model\StoreManagerInterface|\PHPUnit\Framework\MockObject\MockObject */ protected $storeManager; /** - * @var \Magento\Framework\Stdlib\DateTime\DateTime|\PHPUnit_Framework_MockObject_MockObject + * @var \Magento\Framework\Stdlib\DateTime\DateTime|\PHPUnit\Framework\MockObject\MockObject */ protected $date; /** - * @var \Magento\Wishlist\Model\Item\OptionFactory|\PHPUnit_Framework_MockObject_MockObject + * @var \Magento\Wishlist\Model\Item\OptionFactory|\PHPUnit\Framework\MockObject\MockObject */ protected $optionFactory; /** - * @var \Magento\Wishlist\Model\ResourceModel\Item\Option\CollectionFactory|\PHPUnit_Framework_MockObject_MockObject + * @var \Magento\Wishlist\Model\ResourceModel\Item\Option\CollectionFactory|\PHPUnit\Framework\MockObject\MockObject */ protected $itemOptFactory; /** - * @var \Magento\Catalog\Api\ProductRepositoryInterface|\PHPUnit_Framework_MockObject_MockObject + * @var \Magento\Catalog\Api\ProductRepositoryInterface|\PHPUnit\Framework\MockObject\MockObject */ protected $productRepository; diff --git a/app/code/Magento/Wishlist/Test/Unit/Model/LocaleQuantityProcessorTest.php b/app/code/Magento/Wishlist/Test/Unit/Model/LocaleQuantityProcessorTest.php index 6cac17aa8c3da..f262f4419f366 100644 --- a/app/code/Magento/Wishlist/Test/Unit/Model/LocaleQuantityProcessorTest.php +++ b/app/code/Magento/Wishlist/Test/Unit/Model/LocaleQuantityProcessorTest.php @@ -15,12 +15,12 @@ class LocaleQuantityProcessorTest extends \PHPUnit\Framework\TestCase protected $processor; /** - * @var \Magento\Framework\Locale\ResolverInterface|\PHPUnit_Framework_MockObject_MockObject + * @var \Magento\Framework\Locale\ResolverInterface|\PHPUnit\Framework\MockObject\MockObject */ protected $resolver; /** - * @var \Magento\Framework\Filter\LocalizedToNormalized|\PHPUnit_Framework_MockObject_MockObject + * @var \Magento\Framework\Filter\LocalizedToNormalized|\PHPUnit\Framework\MockObject\MockObject */ protected $filter; diff --git a/app/code/Magento/Wishlist/Test/Unit/Model/Product/AttributeValueProviderTest.php b/app/code/Magento/Wishlist/Test/Unit/Model/Product/AttributeValueProviderTest.php index fb0113eb6ae75..62eb2605b3b64 100644 --- a/app/code/Magento/Wishlist/Test/Unit/Model/Product/AttributeValueProviderTest.php +++ b/app/code/Magento/Wishlist/Test/Unit/Model/Product/AttributeValueProviderTest.php @@ -13,30 +13,32 @@ use Magento\Framework\DB\Adapter\AdapterInterface; use Magento\Wishlist\Model\Product\AttributeValueProvider; use PHPUnit\Framework\TestCase; -use PHPUnit_Framework_MockObject_MockObject; +use PHPUnit\Framework\MockObject\MockObject; /** - * AttributeValueProviderTest + * Class AttributeValueProviderTest + * + * PHPUnit test case for \Magento\Wishlist\Model\Product\AttributeValueProvider */ class AttributeValueProviderTest extends TestCase { /** - * @var AttributeValueProvider|PHPUnit_Framework_MockObject_MockObject + * @var AttributeValueProvider|MockObject */ private $attributeValueProvider; /** - * @var CollectionFactory|PHPUnit_Framework_MockObject_MockObject + * @var CollectionFactory|MockObject */ private $productCollectionFactoryMock; /** - * @var Product|PHPUnit_Framework_MockObject_MockObject + * @var Product|MockObject */ private $productMock; /** - * @var AdapterInterface|PHPUnit_Framework_MockObject_MockObject + * @var AdapterInterface|MockObject */ private $connectionMock; diff --git a/app/code/Magento/Wishlist/Test/Unit/Model/ResourceModel/Item/CollectionTest.php b/app/code/Magento/Wishlist/Test/Unit/Model/ResourceModel/Item/CollectionTest.php index 197edda298019..92e4a67519090 100644 --- a/app/code/Magento/Wishlist/Test/Unit/Model/ResourceModel/Item/CollectionTest.php +++ b/app/code/Magento/Wishlist/Test/Unit/Model/ResourceModel/Item/CollectionTest.php @@ -44,7 +44,7 @@ class CollectionTest extends \PHPUnit\Framework\TestCase WHERE (INSTR(product_name_table.value, 'TestProductName'))"; /** - * @var MetadataPool|\PHPUnit_Framework_MockObject_MockObject + * @var MetadataPool|\PHPUnit\Framework\MockObject\MockObject */ protected $metadataPool; diff --git a/app/code/Magento/Wishlist/Test/Unit/Model/Rss/WishlistTest.php b/app/code/Magento/Wishlist/Test/Unit/Model/Rss/WishlistTest.php index fc43baa0a67de..b62734b93cbbd 100644 --- a/app/code/Magento/Wishlist/Test/Unit/Model/Rss/WishlistTest.php +++ b/app/code/Magento/Wishlist/Test/Unit/Model/Rss/WishlistTest.php @@ -54,7 +54,7 @@ class WishlistTest extends \PHPUnit\Framework\TestCase protected $catalogOutputMock; /** - * @var \Magento\Catalog\Helper\Output|\PHPUnit_Framework_MockObject_MockObject + * @var \Magento\Catalog\Helper\Output|\PHPUnit\Framework\MockObject\MockObject */ protected $layoutMock; diff --git a/app/code/Magento/Wishlist/Test/Unit/Model/WishlistTest.php b/app/code/Magento/Wishlist/Test/Unit/Model/WishlistTest.php index eb788efc0d622..be1c11076750a 100644 --- a/app/code/Magento/Wishlist/Test/Unit/Model/WishlistTest.php +++ b/app/code/Magento/Wishlist/Test/Unit/Model/WishlistTest.php @@ -33,7 +33,7 @@ use Magento\Wishlist\Model\ResourceModel\Wishlist\Collection as WishlistCollection; use Magento\Wishlist\Model\Wishlist; use PHPUnit\Framework\TestCase; -use PHPUnit_Framework_MockObject_MockObject; +use PHPUnit\Framework\MockObject\MockObject; /** * @SuppressWarnings(PHPMD.CouplingBetweenObjects) @@ -42,67 +42,67 @@ class WishlistTest extends TestCase { /** - * @var Registry|PHPUnit_Framework_MockObject_MockObject + * @var Registry|MockObject */ protected $registry; /** - * @var HelperProduct|PHPUnit_Framework_MockObject_MockObject + * @var HelperProduct|MockObject */ protected $productHelper; /** - * @var Data|PHPUnit_Framework_MockObject_MockObject + * @var Data|MockObject */ protected $helper; /** - * @var WishlistResource|PHPUnit_Framework_MockObject_MockObject + * @var WishlistResource|MockObject */ protected $resource; /** - * @var WishlistCollection|PHPUnit_Framework_MockObject_MockObject + * @var WishlistCollection|MockObject */ protected $collection; /** - * @var StoreManagerInterface|PHPUnit_Framework_MockObject_MockObject + * @var StoreManagerInterface|MockObject */ protected $storeManager; /** - * @var DateTime\DateTime|PHPUnit_Framework_MockObject_MockObject + * @var DateTime\DateTime|MockObject */ protected $date; /** - * @var ItemFactory|PHPUnit_Framework_MockObject_MockObject + * @var ItemFactory|MockObject */ protected $itemFactory; /** - * @var CollectionFactory|PHPUnit_Framework_MockObject_MockObject + * @var CollectionFactory|MockObject */ protected $itemsFactory; /** - * @var ProductFactory|PHPUnit_Framework_MockObject_MockObject + * @var ProductFactory|MockObject */ protected $productFactory; /** - * @var Random|PHPUnit_Framework_MockObject_MockObject + * @var Random|MockObject */ protected $mathRandom; /** - * @var DateTime|PHPUnit_Framework_MockObject_MockObject + * @var DateTime|MockObject */ protected $dateTime; /** - * @var ManagerInterface|PHPUnit_Framework_MockObject_MockObject + * @var ManagerInterface|MockObject */ protected $eventDispatcher; @@ -112,22 +112,22 @@ class WishlistTest extends TestCase protected $wishlist; /** - * @var ProductRepositoryInterface|PHPUnit_Framework_MockObject_MockObject + * @var ProductRepositoryInterface|MockObject */ protected $productRepository; /** - * @var Json|PHPUnit_Framework_MockObject_MockObject + * @var Json|MockObject */ protected $serializer; /** - * @var StockItemRepository|PHPUnit_Framework_MockObject_MockObject + * @var StockItemRepository|MockObject */ private $scopeConfig; /** - * @var StockRegistryInterface|PHPUnit_Framework_MockObject_MockObject + * @var StockRegistryInterface|MockObject */ private $stockRegistry; @@ -240,7 +240,7 @@ public function testLoadByCustomerId() } /** - * @param int|Item|PHPUnit_Framework_MockObject_MockObject $itemId + * @param int|Item|MockObject $itemId * @param DataObject $buyRequest * @param null|array|DataObject $param * @throws LocalizedException diff --git a/app/code/Magento/Wishlist/Test/Unit/Observer/AddToCartTest.php b/app/code/Magento/Wishlist/Test/Unit/Observer/AddToCartTest.php index e6e14a452a96d..a9d05a7a99eec 100644 --- a/app/code/Magento/Wishlist/Test/Unit/Observer/AddToCartTest.php +++ b/app/code/Magento/Wishlist/Test/Unit/Observer/AddToCartTest.php @@ -19,32 +19,32 @@ class AddToCartTest extends \PHPUnit\Framework\TestCase protected $observer; /** - * @var \Magento\Wishlist\Helper\Data|\PHPUnit_Framework_MockObject_MockObject + * @var \Magento\Wishlist\Helper\Data|\PHPUnit\Framework\MockObject\MockObject */ protected $helper; /** - * @var \Magento\Checkout\Model\Session|\PHPUnit_Framework_MockObject_MockObject + * @var \Magento\Checkout\Model\Session|\PHPUnit\Framework\MockObject\MockObject */ protected $checkoutSession; /** - * @var \Magento\Customer\Model\Session|\PHPUnit_Framework_MockObject_MockObject + * @var \Magento\Customer\Model\Session|\PHPUnit\Framework\MockObject\MockObject */ protected $customerSession; /** - * @var \Magento\Wishlist\Model\WishlistFactory|\PHPUnit_Framework_MockObject_MockObject + * @var \Magento\Wishlist\Model\WishlistFactory|\PHPUnit\Framework\MockObject\MockObject */ protected $wishlistFactory; /** - * @var \Magento\Wishlist\Model\Wishlist|\PHPUnit_Framework_MockObject_MockObject + * @var \Magento\Wishlist\Model\Wishlist|\PHPUnit\Framework\MockObject\MockObject */ protected $wishlist; /** - * @var \Magento\Framework\Message\ManagerInterface|\PHPUnit_Framework_MockObject_MockObject + * @var \Magento\Framework\Message\ManagerInterface|\PHPUnit\Framework\MockObject\MockObject */ protected $messageManager; @@ -113,7 +113,7 @@ public function testExecute() $wishlists = $this->getMockBuilder(\Magento\Wishlist\Model\ResourceModel\Wishlist\Collection::class) ->disableOriginalConstructor() ->getMock(); - $loadedWishlist = $this->getMockBuilder(\Magento\Wishlist\Model\Wishlist\Item::class) + $loadedWishlist = $this->getMockBuilder(\Magento\Wishlist\Model\Wishlist::class) ->setMethods(['getId', 'delete']) ->disableOriginalConstructor() ->getMock(); diff --git a/app/code/Magento/Wishlist/Test/Unit/Observer/CartUpdateBeforeTest.php b/app/code/Magento/Wishlist/Test/Unit/Observer/CartUpdateBeforeTest.php index 3ca87234b60f9..08614fde9b290 100644 --- a/app/code/Magento/Wishlist/Test/Unit/Observer/CartUpdateBeforeTest.php +++ b/app/code/Magento/Wishlist/Test/Unit/Observer/CartUpdateBeforeTest.php @@ -19,32 +19,32 @@ class CartUpdateBeforeTest extends \PHPUnit\Framework\TestCase protected $observer; /** - * @var \Magento\Wishlist\Helper\Data|\PHPUnit_Framework_MockObject_MockObject + * @var \Magento\Wishlist\Helper\Data|\PHPUnit\Framework\MockObject\MockObject */ protected $helper; /** - * @var \Magento\Checkout\Model\Session|\PHPUnit_Framework_MockObject_MockObject + * @var \Magento\Checkout\Model\Session|\PHPUnit\Framework\MockObject\MockObject */ protected $checkoutSession; /** - * @var \Magento\Customer\Model\Session|\PHPUnit_Framework_MockObject_MockObject + * @var \Magento\Customer\Model\Session|\PHPUnit\Framework\MockObject\MockObject */ protected $customerSession; /** - * @var \Magento\Wishlist\Model\WishlistFactory|\PHPUnit_Framework_MockObject_MockObject + * @var \Magento\Wishlist\Model\WishlistFactory|\PHPUnit\Framework\MockObject\MockObject */ protected $wishlistFactory; /** - * @var \Magento\Wishlist\Model\Wishlist|\PHPUnit_Framework_MockObject_MockObject + * @var \Magento\Wishlist\Model\Wishlist|\PHPUnit\Framework\MockObject\MockObject */ protected $wishlist; /** - * @var \Magento\Framework\Message\ManagerInterface|\PHPUnit_Framework_MockObject_MockObject + * @var \Magento\Framework\Message\ManagerInterface|\PHPUnit\Framework\MockObject\MockObject */ protected $messageManager; diff --git a/app/code/Magento/Wishlist/Test/Unit/Observer/CustomerLoginTest.php b/app/code/Magento/Wishlist/Test/Unit/Observer/CustomerLoginTest.php index e94f0a8793d57..489fcb48da352 100644 --- a/app/code/Magento/Wishlist/Test/Unit/Observer/CustomerLoginTest.php +++ b/app/code/Magento/Wishlist/Test/Unit/Observer/CustomerLoginTest.php @@ -16,7 +16,7 @@ class CustomerLoginTest extends \PHPUnit\Framework\TestCase protected $observer; /** - * @var \Magento\Wishlist\Helper\Data|\PHPUnit_Framework_MockObject_MockObject + * @var \Magento\Wishlist\Helper\Data|\PHPUnit\Framework\MockObject\MockObject */ protected $helper; diff --git a/app/code/Magento/Wishlist/Test/Unit/Observer/CustomerLogoutTest.php b/app/code/Magento/Wishlist/Test/Unit/Observer/CustomerLogoutTest.php index 0933ff2772214..da5ab765a2287 100644 --- a/app/code/Magento/Wishlist/Test/Unit/Observer/CustomerLogoutTest.php +++ b/app/code/Magento/Wishlist/Test/Unit/Observer/CustomerLogoutTest.php @@ -15,7 +15,7 @@ class CustomerLogoutTest extends \PHPUnit\Framework\TestCase protected $observer; /** - * @var \Magento\Customer\Model\Session|\PHPUnit_Framework_MockObject_MockObject + * @var \Magento\Customer\Model\Session|\PHPUnit\Framework\MockObject\MockObject */ protected $customerSession; diff --git a/app/code/Magento/Wishlist/Test/Unit/Plugin/Ui/DataProvider/WishlistSettingsTest.php b/app/code/Magento/Wishlist/Test/Unit/Plugin/Ui/DataProvider/WishlistSettingsTest.php index aa3b956e12153..c498ec270dea1 100644 --- a/app/code/Magento/Wishlist/Test/Unit/Plugin/Ui/DataProvider/WishlistSettingsTest.php +++ b/app/code/Magento/Wishlist/Test/Unit/Plugin/Ui/DataProvider/WishlistSettingsTest.php @@ -24,7 +24,7 @@ class WishlistSettingsTest extends \PHPUnit\Framework\TestCase private $wishlistSettings; /** - * @var Data|\PHPUnit_Framework_MockObject_MockObject + * @var Data|\PHPUnit\Framework\MockObject\MockObject */ private $helperMock; @@ -46,7 +46,7 @@ protected function setUp() */ public function testAfterGetData() { - /** @var DataProvider|\PHPUnit_Framework_MockObject_MockObject $subjectMock */ + /** @var DataProvider|\PHPUnit\Framework\MockObject\MockObject $subjectMock */ $subjectMock = $this->createMock(DataProvider::class); $result = []; $isAllow = true; diff --git a/app/code/Magento/Wishlist/Test/Unit/Pricing/ConfiguredPrice/ConfigurableProductTest.php b/app/code/Magento/Wishlist/Test/Unit/Pricing/ConfiguredPrice/ConfigurableProductTest.php index 23a93bc350203..695ecded081c7 100644 --- a/app/code/Magento/Wishlist/Test/Unit/Pricing/ConfiguredPrice/ConfigurableProductTest.php +++ b/app/code/Magento/Wishlist/Test/Unit/Pricing/ConfiguredPrice/ConfigurableProductTest.php @@ -8,17 +8,17 @@ class ConfigurableProductTest extends \PHPUnit\Framework\TestCase { /** - * @var \Magento\Framework\Pricing\SaleableInterface|\PHPUnit_Framework_MockObject_MockObject + * @var \Magento\Framework\Pricing\SaleableInterface|\PHPUnit\Framework\MockObject\MockObject */ private $saleableItem; /** - * @var \Magento\Framework\Pricing\Adjustment\CalculatorInterface|\PHPUnit_Framework_MockObject_MockObject + * @var \Magento\Framework\Pricing\Adjustment\CalculatorInterface|\PHPUnit\Framework\MockObject\MockObject */ private $calculator; /** - * @var \Magento\Framework\Pricing\PriceCurrencyInterface|\PHPUnit_Framework_MockObject_MockObject + * @var \Magento\Framework\Pricing\PriceCurrencyInterface|\PHPUnit\Framework\MockObject\MockObject */ private $priceCurrency; @@ -28,7 +28,7 @@ class ConfigurableProductTest extends \PHPUnit\Framework\TestCase private $model; /** - * @var \Magento\Framework\Pricing\PriceInfoInterface|\PHPUnit_Framework_MockObject_MockObject + * @var \Magento\Framework\Pricing\PriceInfoInterface|\PHPUnit\Framework\MockObject\MockObject */ private $priceInfoMock; diff --git a/app/code/Magento/Wishlist/Test/Unit/Pricing/ConfiguredPrice/DownloadableTest.php b/app/code/Magento/Wishlist/Test/Unit/Pricing/ConfiguredPrice/DownloadableTest.php index 0623eeabc6554..ed2318274d1c0 100644 --- a/app/code/Magento/Wishlist/Test/Unit/Pricing/ConfiguredPrice/DownloadableTest.php +++ b/app/code/Magento/Wishlist/Test/Unit/Pricing/ConfiguredPrice/DownloadableTest.php @@ -15,17 +15,17 @@ class DownloadableTest extends \PHPUnit\Framework\TestCase { /** - * @var SaleableInterface|\PHPUnit_Framework_MockObject_MockObject + * @var SaleableInterface|\PHPUnit\Framework\MockObject\MockObject */ private $saleableItem; /** - * @var CalculatorInterface|\PHPUnit_Framework_MockObject_MockObject + * @var CalculatorInterface|\PHPUnit\Framework\MockObject\MockObject */ private $calculator; /** - * @var PriceCurrencyInterface|\PHPUnit_Framework_MockObject_MockObject + * @var PriceCurrencyInterface|\PHPUnit\Framework\MockObject\MockObject */ private $priceCurrency; @@ -35,7 +35,7 @@ class DownloadableTest extends \PHPUnit\Framework\TestCase private $model; /** - * @var PriceInfoInterface|\PHPUnit_Framework_MockObject_MockObject + * @var PriceInfoInterface|\PHPUnit\Framework\MockObject\MockObject */ private $priceInfoMock; diff --git a/app/code/Magento/Wishlist/Test/Unit/Pricing/Render/ConfiguredPriceBoxTest.php b/app/code/Magento/Wishlist/Test/Unit/Pricing/Render/ConfiguredPriceBoxTest.php index 40291f2420b2e..1ec47db91f194 100644 --- a/app/code/Magento/Wishlist/Test/Unit/Pricing/Render/ConfiguredPriceBoxTest.php +++ b/app/code/Magento/Wishlist/Test/Unit/Pricing/Render/ConfiguredPriceBoxTest.php @@ -10,22 +10,22 @@ class ConfiguredPriceBoxTest extends \PHPUnit\Framework\TestCase { /** - * @var \Magento\Framework\View\Element\Template\Context|\PHPUnit_Framework_MockObject_MockObject + * @var \Magento\Framework\View\Element\Template\Context|\PHPUnit\Framework\MockObject\MockObject */ private $templateContext; /** - * @var \Magento\Framework\Pricing\SaleableInterface|\PHPUnit_Framework_MockObject_MockObject + * @var \Magento\Framework\Pricing\SaleableInterface|\PHPUnit\Framework\MockObject\MockObject */ private $saleableItem; /** - * @var \Magento\Framework\Pricing\Price\PriceInterface|\PHPUnit_Framework_MockObject_MockObject + * @var \Magento\Framework\Pricing\Price\PriceInterface|\PHPUnit\Framework\MockObject\MockObject */ private $price; /** - * @var \Magento\Framework\Pricing\Render\RendererPool|\PHPUnit_Framework_MockObject_MockObject + * @var \Magento\Framework\Pricing\Render\RendererPool|\PHPUnit\Framework\MockObject\MockObject */ private $rendererPool; @@ -35,7 +35,7 @@ class ConfiguredPriceBoxTest extends \PHPUnit\Framework\TestCase private $model; /** - * @var \Magento\Catalog\Model\Product\Configuration\Item\ItemInterface|\PHPUnit_Framework_MockObject_MockObject + * @var \Magento\Catalog\Model\Product\Configuration\Item\ItemInterface|\PHPUnit\Framework\MockObject\MockObject */ private $item; diff --git a/app/code/Magento/Wishlist/etc/adminhtml/system.xml b/app/code/Magento/Wishlist/etc/adminhtml/system.xml index e61c07abca993..efadd6c4d58ba 100644 --- a/app/code/Magento/Wishlist/etc/adminhtml/system.xml +++ b/app/code/Magento/Wishlist/etc/adminhtml/system.xml @@ -13,6 +13,9 @@ <resource>Magento_Wishlist::config_wishlist</resource> <group id="email" translate="label" type="text" sortOrder="2" showInDefault="1" showInWebsite="1" showInStore="1"> <label>Share Options</label> + <depends> + <field id="*/general/active">1</field> + </depends> <field id="email_identity" translate="label" type="select" sortOrder="1" showInDefault="1" showInWebsite="1" showInStore="1" canRestore="1"> <label>Email Sender</label> <source_model>Magento\Config\Model\Config\Source\Email\Identity</source_model> @@ -42,11 +45,17 @@ <field id="show_in_sidebar" translate="label" type="select" sortOrder="5" showInDefault="1" showInWebsite="1" showInStore="1" canRestore="1"> <label>Show in Sidebar</label> <source_model>Magento\Config\Model\Config\Source\Yesno</source_model> + <depends> + <field id="active">1</field> + </depends> </field> </group> - <group id="wishlist_link" translate="label" sortOrder="3" showInDefault="1" showInWebsite="1" showInStore="0"> + <group id="wishlist_link" translate="label" sortOrder="3" showInDefault="1" showInWebsite="1"> <label>My Wish List Link</label> - <field id="use_qty" translate="label" type="select" sortOrder="1" showInDefault="1" showInWebsite="1" showInStore="0"> + <depends> + <field id="*/general/active">1</field> + </depends> + <field id="use_qty" translate="label" type="select" sortOrder="1" showInDefault="1" showInWebsite="1" canRestore="1"> <label>Display Wish List Summary</label> <source_model>Magento\Wishlist\Model\Config\Source\Summary</source_model> </field> diff --git a/app/code/Magento/Wishlist/etc/config.xml b/app/code/Magento/Wishlist/etc/config.xml index dd88e63bc90ad..780d6b904fedc 100644 --- a/app/code/Magento/Wishlist/etc/config.xml +++ b/app/code/Magento/Wishlist/etc/config.xml @@ -18,6 +18,9 @@ <number_limit>10</number_limit> <text_limit>255</text_limit> </email> + <wishlist_link> + <use_qty>0</use_qty> + </wishlist_link> </wishlist> <captcha translate="label"> <frontend> diff --git a/app/code/Magento/Wishlist/registration.php b/app/code/Magento/Wishlist/registration.php index ea46a972ef449..b4c9f523ab95f 100644 --- a/app/code/Magento/Wishlist/registration.php +++ b/app/code/Magento/Wishlist/registration.php @@ -4,6 +4,6 @@ * See COPYING.txt for license details. */ -use \Magento\Framework\Component\ComponentRegistrar; +use Magento\Framework\Component\ComponentRegistrar; ComponentRegistrar::register(ComponentRegistrar::MODULE, 'Magento_Wishlist', __DIR__); diff --git a/app/code/Magento/Wishlist/view/frontend/templates/email/items.phtml b/app/code/Magento/Wishlist/view/frontend/templates/email/items.phtml index 449d4c43c5aa0..782b7d4892e62 100644 --- a/app/code/Magento/Wishlist/view/frontend/templates/email/items.phtml +++ b/app/code/Magento/Wishlist/view/frontend/templates/email/items.phtml @@ -11,42 +11,43 @@ <table> <tr> <?php $i = 0; - foreach ($block->getWishlistItems() as $item) : $i++ ?> - <?php /* @var $item \Magento\Wishlist\Model\Item */ ?> - <?php /* @var $_product \Magento\Catalog\Model\Product */ ?> + foreach ($block->getWishlistItems() as $item): $i++ ?> + <?php /* @var \Magento\Wishlist\Model\Item $item */ ?> + <?php /* @var \Magento\Catalog\Model\Product $_product */ ?> <?php $_product = $item->getProduct(); ?> <td class="col product"> <p> - <a href="<?= $block->escapeUrl($block->getProductUrl($_product)) ?>"> - <?= /* @noEscape */ $block->getImage($_product, 'product_small_image')->toHtml() ?> + <a href="<?= $block->escapeUrl($block->getProductUrl($item)) ?>"> + <?php $productThumbnail = $block->getProductForThumbnail($item) ?> + <?= /* @noEscape */ $block->getImage($productThumbnail, 'product_small_image')->toHtml() ?> </a> </p> <p> - <a href="<?= $block->escapeUrl($block->getProductUrl($_product)) ?>"> + <a href="<?= $block->escapeUrl($block->getProductUrl($item)) ?>"> <strong><?= $block->escapeHtml($_product->getName()) ?></strong> </a> </p> - <?php if ($block->hasDescription($item)) : ?> + <?php if ($block->hasDescription($item)): ?> <p> <strong><?= $block->escapeHtml(__('Comment')) ?>:</strong> <br/><?= /* @noEscape */ $block->getEscapedDescription($item) ?> </p> <?php endif; ?> <p> - <a href="<?= $block->escapeUrl($block->getProductUrl($_product)) ?>"> + <a href="<?= $block->escapeUrl($block->getProductUrl($item)) ?>"> <?= $block->escapeHtml(__('View Product')) ?> </a> </p> </td> - <?php if ($i % 3 != 0) : ?> + <?php if ($i % 3 != 0): ?> <td></td> - <?php else : ?> + <?php else: ?> </tr> <tr> <td colspan="5"> </td> </tr> - <?php if ($i < $l) : ?> + <?php if ($i < $l): ?> <tr> <?php endif ?> <?php endif ?> diff --git a/app/code/Magento/Wishlist/view/frontend/web/js/add-to-wishlist.js b/app/code/Magento/Wishlist/view/frontend/web/js/add-to-wishlist.js index aca843872af65..55cd77b196be5 100644 --- a/app/code/Magento/Wishlist/view/frontend/web/js/add-to-wishlist.js +++ b/app/code/Magento/Wishlist/view/frontend/web/js/add-to-wishlist.js @@ -16,7 +16,8 @@ define([ groupedInfo: '#super-product-table input', downloadableInfo: '#downloadable-links-list input', customOptionsInfo: '.product-custom-option', - qtyInfo: '#qty' + qtyInfo: '#qty', + actionElement: '[data-action="add-to-wishlist"]' }, /** @inheritdoc */ @@ -30,8 +31,10 @@ define([ _bind: function () { var options = this.options, dataUpdateFunc = '_updateWishlistData', + validateProductQty = '_validateWishlistQty', changeCustomOption = 'change ' + options.customOptionsInfo, changeQty = 'change ' + options.qtyInfo, + updateWishlist = 'click ' + options.actionElement, events = {}, key; @@ -45,6 +48,7 @@ define([ events[changeCustomOption] = dataUpdateFunc; events[changeQty] = dataUpdateFunc; + events[updateWishlist] = validateProductQty; for (key in options.productType) { if (options.productType.hasOwnProperty(key) && options.productType[key] + 'Info' in options) { @@ -220,6 +224,23 @@ define([ $(form).attr('action', action).submit(); }); + }, + + /** + * Validate product quantity before updating Wish List + * + * @param {jQuery.Event} event + * @private + */ + _validateWishlistQty: function (event) { + var element = $(this.options.qtyInfo); + + if (!(element.validation() && element.validation('isValid'))) { + event.preventDefault(); + event.stopPropagation(); + + return; + } } }); diff --git a/app/code/Magento/WishlistGraphQl/Model/Resolver/CustomerWishlistResolver.php b/app/code/Magento/WishlistGraphQl/Model/Resolver/CustomerWishlistResolver.php index e866b9cead03c..a84ce0e965b6d 100644 --- a/app/code/Magento/WishlistGraphQl/Model/Resolver/CustomerWishlistResolver.php +++ b/app/code/Magento/WishlistGraphQl/Model/Resolver/CustomerWishlistResolver.php @@ -8,10 +8,11 @@ namespace Magento\WishlistGraphQl\Model\Resolver; use Magento\Framework\GraphQl\Config\Element\Field; +use Magento\Framework\GraphQl\Exception\GraphQlAuthorizationException; use Magento\Framework\GraphQl\Query\ResolverInterface; use Magento\Framework\GraphQl\Schema\Type\ResolveInfo; +use Magento\Wishlist\Model\Wishlist; use Magento\Wishlist\Model\WishlistFactory; -use Magento\Framework\GraphQl\Exception\GraphQlAuthorizationException; /** * Fetches customer wishlist data @@ -44,9 +45,13 @@ public function resolve( if (false === $context->getExtensionAttributes()->getIsCustomer()) { throw new GraphQlAuthorizationException(__('The current customer isn\'t authorized.')); } - $wishlist = $this->wishlistFactory->create()->loadByCustomerId($context->getUserId(), true); + + /** @var Wishlist $wishlist */ + $wishlist = $this->wishlistFactory->create(); + $wishlist->loadByCustomerId($context->getUserId(), true); + return [ - 'id' => (string) $wishlist->getId(), + 'id' => (string)$wishlist->getId(), 'sharing_code' => $wishlist->getSharingCode(), 'updated_at' => $wishlist->getUpdatedAt(), 'items_count' => $wishlist->getItemsCount(), diff --git a/app/code/Magento/WishlistGraphQl/Test/Unit/CustomerWishlistResolverTest.php b/app/code/Magento/WishlistGraphQl/Test/Unit/CustomerWishlistResolverTest.php new file mode 100644 index 0000000000000..67c38561c7e41 --- /dev/null +++ b/app/code/Magento/WishlistGraphQl/Test/Unit/CustomerWishlistResolverTest.php @@ -0,0 +1,164 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +declare(strict_types=1); + +namespace Magento\WishlistGraphQl\Test\Unit; + +use Magento\Framework\GraphQl\Config\Element\Field; +use Magento\Framework\GraphQl\Exception\GraphQlAuthorizationException; +use Magento\Framework\GraphQl\Query\Resolver\ContextInterface; +use Magento\Framework\GraphQl\Schema\Type\ResolveInfo; +use Magento\Framework\TestFramework\Unit\Helper\ObjectManager; +use Magento\GraphQl\Model\Query\ContextExtensionInterface; +use Magento\Wishlist\Model\Wishlist; +use Magento\Wishlist\Model\WishlistFactory; +use Magento\WishlistGraphQl\Model\Resolver\CustomerWishlistResolver; +use PHPUnit\Framework\MockObject\MockObject; +use PHPUnit\Framework\TestCase; + +class CustomerWishlistResolverTest extends TestCase +{ + private const STUB_CUSTOMER_ID = 1; + + /** + * @var MockObject|ContextInterface + */ + private $contextMock; + + /** + * @var MockObject|ContextExtensionInterface + */ + private $extensionAttributesMock; + + /** + * @var MockObject|WishlistFactory + */ + private $wishlistFactoryMock; + + /** + * @var MockObject|Wishlist + */ + private $wishlistMock; + + /** + * @var CustomerWishlistResolver + */ + private $resolver; + + /** + * Build the Testing Environment + */ + protected function setUp() + { + $this->contextMock = $this->getMockBuilder(ContextInterface::class) + ->setMethods(['getExtensionAttributes', 'getUserId']) + ->getMock(); + + $this->extensionAttributesMock = $this->getMockBuilder(ContextExtensionInterface::class) + ->setMethods(['getStore', 'setStore', 'getIsCustomer', 'setIsCustomer']) + ->getMock(); + + $this->contextMock->method('getExtensionAttributes') + ->willReturn($this->extensionAttributesMock); + + $this->wishlistFactoryMock = $this->getMockBuilder(WishlistFactory::class) + ->disableOriginalConstructor() + ->setMethods(['create']) + ->getMock(); + + $this->wishlistMock = $this->getMockBuilder(Wishlist::class) + ->disableOriginalConstructor() + ->setMethods(['loadByCustomerId', 'getId', 'getSharingCode', 'getUpdatedAt', 'getItemsCount']) + ->getMock(); + + $objectManager = new ObjectManager($this); + $this->resolver = $objectManager->getObject(CustomerWishlistResolver::class, [ + 'wishlistFactory' => $this->wishlistFactoryMock + ]); + } + + /** + * Verify if Authorization exception is being thrown when User not logged in + */ + public function testThrowExceptionWhenUserNotAuthorized(): void + { + // Given + $this->extensionAttributesMock->method('getIsCustomer') + ->willReturn(false); + + // Then + $this->expectException(GraphQlAuthorizationException::class); + $this->wishlistFactoryMock->expects($this->never()) + ->method('create'); + + // When + $this->resolver->resolve( + $this->getFieldStub(), + $this->contextMock, + $this->getResolveInfoStub() + ); + } + + /** + * Verify if Wishlist instance is created for currently Authorized user + */ + public function testFactoryCreatesWishlistByAuthorizedCustomerId(): void + { + // Given + $this->extensionAttributesMock->method('getIsCustomer') + ->willReturn(true); + + $this->contextMock->method('getUserId') + ->willReturn(self::STUB_CUSTOMER_ID); + + // Then + $this->wishlistMock->expects($this->once()) + ->method('loadByCustomerId') + ->with(self::STUB_CUSTOMER_ID) + ->willReturnSelf(); + + $this->wishlistFactoryMock->expects($this->once()) + ->method('create') + ->willReturn($this->wishlistMock); + + // When + $this->resolver->resolve( + $this->getFieldStub(), + $this->contextMock, + $this->getResolveInfoStub() + ); + } + + /** + * Returns stub for Field + * + * @return MockObject|Field + */ + private function getFieldStub(): Field + { + /** @var MockObject|Field $fieldMock */ + $fieldMock = $this->getMockBuilder(Field::class) + ->disableOriginalConstructor() + ->getMock(); + + return $fieldMock; + } + + /** + * Returns stub for ResolveInfo + * + * @return MockObject|ResolveInfo + */ + private function getResolveInfoStub(): ResolveInfo + { + /** @var MockObject|ResolveInfo $resolveInfoMock */ + $resolveInfoMock = $this->getMockBuilder(ResolveInfo::class) + ->disableOriginalConstructor() + ->getMock(); + + return $resolveInfoMock; + } +} diff --git a/app/design/adminhtml/Magento/backend/Magento_Backend/web/css/source/module/pages/_dashboard.less b/app/design/adminhtml/Magento/backend/Magento_Backend/web/css/source/module/pages/_dashboard.less index 7ef304932a649..3c50fc02a05c5 100644 --- a/app/design/adminhtml/Magento/backend/Magento_Backend/web/css/source/module/pages/_dashboard.less +++ b/app/design/adminhtml/Magento/backend/Magento_Backend/web/css/source/module/pages/_dashboard.less @@ -126,16 +126,19 @@ // Range switcher .dashboard-diagram-switcher { - margin-bottom: 2rem; + border-top: 1px solid @color-gray68; + margin-top: -1px; + padding: 2rem 2rem 0; .label { &:extend(.abs-visually-hidden all); } -} -// Chart -.dashboard-diagram-image { - max-width: 100%; + + .dashboard-diagram-tab-content { + > .ui-tabs-panel { + border-top: 0 none; + } + } } // diff --git a/app/design/adminhtml/Magento/backend/Magento_Catalog/web/css/source/_module-old.less b/app/design/adminhtml/Magento/backend/Magento_Catalog/web/css/source/_module-old.less index 2074106e719db..027561423892f 100644 --- a/app/design/adminhtml/Magento/backend/Magento_Catalog/web/css/source/_module-old.less +++ b/app/design/adminhtml/Magento/backend/Magento_Catalog/web/css/source/_module-old.less @@ -25,11 +25,13 @@ // Attributes .attributes-edit-form { .field { + .addon { + display: block; + position: relative; + } + &:not(.field-weight) { .addon { - display: block; - position: relative; - input[type="text"] { border-width: 1px; } @@ -46,6 +48,14 @@ } } } + + &.field-weight { + .addon { + .addafter { + border-width: 0; + } + } + } } .with-addon .textarea { diff --git a/app/design/adminhtml/Magento/backend/Magento_Catalog/web/css/source/_module.less b/app/design/adminhtml/Magento/backend/Magento_Catalog/web/css/source/_module.less index ffbbaeb084162..650fe6177d04b 100644 --- a/app/design/adminhtml/Magento/backend/Magento_Catalog/web/css/source/_module.less +++ b/app/design/adminhtml/Magento/backend/Magento_Catalog/web/css/source/_module.less @@ -20,7 +20,7 @@ .admin__grid-control-value { display: none; } - } + } } .product-composite-configure-inner { @@ -102,3 +102,15 @@ } } } + +// +// Catalog Product Edit Action Attribute +// --------------------------------------------- + +.admin__field-control { + .attribute-change-checkbox { + input[type='checkbox'].checkbox { + margin-top: 0; + } + } +} diff --git a/app/design/adminhtml/Magento/backend/Magento_Cms/web/css/source/_module.less b/app/design/adminhtml/Magento/backend/Magento_Cms/web/css/source/_module.less index 715ad40dc475f..fda9a1ebdd083 100644 --- a/app/design/adminhtml/Magento/backend/Magento_Cms/web/css/source/_module.less +++ b/app/design/adminhtml/Magento/backend/Magento_Cms/web/css/source/_module.less @@ -6,6 +6,10 @@ .media-gallery-modal { .page-main-actions { margin-bottom: 3rem; + + .page-action-buttons { + text-align: right; + } } .new_folder { diff --git a/app/design/adminhtml/Magento/backend/Magento_Customer/web/css/source/_module.less b/app/design/adminhtml/Magento/backend/Magento_Customer/web/css/source/_module.less index 0c7dd7e7cb94c..e526ae4857469 100644 --- a/app/design/adminhtml/Magento/backend/Magento_Customer/web/css/source/_module.less +++ b/app/design/adminhtml/Magento/backend/Magento_Customer/web/css/source/_module.less @@ -101,10 +101,4 @@ } } } - - > .admin__field > .admin__field-control { - input[type='checkbox'] { - margin: 8px 0 0 0; - } - } } diff --git a/app/design/adminhtml/Magento/backend/Magento_Signifyd/web/css/source/_module.less b/app/design/adminhtml/Magento/backend/Magento_Signifyd/web/css/source/_module.less deleted file mode 100644 index 3f0875da552a3..0000000000000 --- a/app/design/adminhtml/Magento/backend/Magento_Signifyd/web/css/source/_module.less +++ /dev/null @@ -1,7 +0,0 @@ -// /** -// * Copyright © Magento, Inc. All rights reserved. -// * See COPYING.txt for license details. -// */ - -@import 'module/_order.less'; -@import 'module/_config.less'; diff --git a/app/design/adminhtml/Magento/backend/Magento_Signifyd/web/css/source/module/_config.less b/app/design/adminhtml/Magento/backend/Magento_Signifyd/web/css/source/module/_config.less deleted file mode 100644 index b66c9c753aa83..0000000000000 --- a/app/design/adminhtml/Magento/backend/Magento_Signifyd/web/css/source/module/_config.less +++ /dev/null @@ -1,25 +0,0 @@ -// /** -// * Copyright © Magento, Inc. All rights reserved. -// * See COPYING.txt for license details. -// */ - -@image-signifyd-logo-path: '../Magento_Signifyd/images/logo.png'; - -// -// Outer slider of configuration -// -.signifyd-logo-header { - > .entry-edit-head { - > a:after { - content: url(@image-signifyd-logo-path); - } - } - - ul { - margin-left: 5em; - } - - .webhook-url { - word-break: break-all; - } -} diff --git a/app/design/adminhtml/Magento/backend/Magento_Signifyd/web/css/source/module/_order.less b/app/design/adminhtml/Magento/backend/Magento_Signifyd/web/css/source/module/_order.less deleted file mode 100644 index d0ae877b3f18c..0000000000000 --- a/app/design/adminhtml/Magento/backend/Magento_Signifyd/web/css/source/module/_order.less +++ /dev/null @@ -1,23 +0,0 @@ -// /** -// * Copyright © Magento, Inc. All rights reserved. -// * See COPYING.txt for license details. -// */ - -// -// Order Case Info -// --------------------------------------------- - -.order-case-table { - &:extend(.abs-order-tables all); - &:extend(.abs-order-tbody-border all); -} - -// -// Layout -// --------------------------------------------- -.media-width(@extremum, @break) when (@extremum = 'min') and (@break = @screen__m) { - .case-information { - float: left; - #mix-grid .width(6,12); - } -} diff --git a/app/design/adminhtml/Magento/backend/Magento_Tax/web/css/source/_module.less b/app/design/adminhtml/Magento/backend/Magento_Tax/web/css/source/_module.less index f7ae4cb821fff..49459bc11cfb2 100644 --- a/app/design/adminhtml/Magento/backend/Magento_Tax/web/css/source/_module.less +++ b/app/design/adminhtml/Magento/backend/Magento_Tax/web/css/source/_module.less @@ -14,7 +14,7 @@ .block.mselect-list.paginated { .admin__action-multiselect-search-wrap { - border:1px solid @color-gray80; + border: 1px solid @color-gray80; border-bottom: none; border-radius: 3px; margin: 0; diff --git a/app/design/adminhtml/Magento/backend/Magento_Ui/web/css/source/_module-old.less b/app/design/adminhtml/Magento/backend/Magento_Ui/web/css/source/_module-old.less index 3756fe678a3c9..5708c5ef72518 100644 --- a/app/design/adminhtml/Magento/backend/Magento_Ui/web/css/source/_module-old.less +++ b/app/design/adminhtml/Magento/backend/Magento_Ui/web/css/source/_module-old.less @@ -340,11 +340,24 @@ } .weight-switcher { - margin-bottom: 18px; + .lib-vendor-prefix-flex-grow(1); + margin-top: 18px; - .addafter { - margin-top: 30px; - position: absolute; + .admin__control-switcher { + .label { + display: block; + float: none; + text-align: left; + width: auto; + } + + .admin__field-option { + position: relative; + } + + .admin__control-radio { + position: absolute; + } } .mage-error { diff --git a/app/design/adminhtml/Magento/backend/Magento_Ui/web/css/source/module/_masonry-grid.less b/app/design/adminhtml/Magento/backend/Magento_Ui/web/css/source/module/_masonry-grid.less index c17c19e259478..237395db8cb43 100644 --- a/app/design/adminhtml/Magento/backend/Magento_Ui/web/css/source/module/_masonry-grid.less +++ b/app/design/adminhtml/Magento/backend/Magento_Ui/web/css/source/module/_masonry-grid.less @@ -5,6 +5,7 @@ @admin__masonry_grid_image__space: 20px; @admin__masonry_grid_background_color: #fff; @admin__masonry_overlay_background_color: #507dc8; +@admin__masonry_grid_active_image_border_color: #558dd6; & when (@media-common = true) { .masonry-image { @@ -27,6 +28,15 @@ margin: @admin__masonry_grid_image__space/2; overflow: hidden; + .masonry-image-block { + &.active { + img { + border: 2px @admin__masonry_grid_active_image_border_color solid; + padding: 1px; + } + } + } + img { cursor: pointer; height: 100%; @@ -104,5 +114,16 @@ } } } + + &-sortby { + select { + background-image: url(../images/arrows-bg.svg); + border: none; + padding-right: 3.2rem; + &:active { + background-image+: url('../images/arrows-bg.svg'); + } + } + } } } diff --git a/app/design/adminhtml/Magento/backend/registration.php b/app/design/adminhtml/Magento/backend/registration.php index 039f3a141e382..e15cc9c18604b 100644 --- a/app/design/adminhtml/Magento/backend/registration.php +++ b/app/design/adminhtml/Magento/backend/registration.php @@ -4,6 +4,6 @@ * See COPYING.txt for license details. */ -use \Magento\Framework\Component\ComponentRegistrar; +use Magento\Framework\Component\ComponentRegistrar; ComponentRegistrar::register(ComponentRegistrar::THEME, 'adminhtml/Magento/backend', __DIR__); diff --git a/app/design/adminhtml/Magento/backend/web/css/source/components/_calendar-temp.less b/app/design/adminhtml/Magento/backend/web/css/source/components/_calendar-temp.less index 11b187db3d1e4..3ed4ac91d32a4 100644 --- a/app/design/adminhtml/Magento/backend/web/css/source/components/_calendar-temp.less +++ b/app/design/adminhtml/Magento/backend/web/css/source/components/_calendar-temp.less @@ -47,10 +47,6 @@ vertical-align: top; z-index: 1; - &:active { - .scale(.975); - } - + .admin__control-support-text, + .admin__control-label { margin-left: @action__height + .5rem; diff --git a/app/design/adminhtml/Magento/backend/web/css/source/forms/_fields.less b/app/design/adminhtml/Magento/backend/web/css/source/forms/_fields.less index bfb515c700b33..256ac453578df 100644 --- a/app/design/adminhtml/Magento/backend/web/css/source/forms/_fields.less +++ b/app/design/adminhtml/Magento/backend/web/css/source/forms/_fields.less @@ -122,7 +122,7 @@ > .admin__field-control { #mix-grid .column(@field-control-grid__column, @field-grid__columns); input[type="checkbox"] { - margin-top: 0; + margin-top: @indent__s; } } diff --git a/app/design/frontend/Magento/blank/Magento_Catalog/web/css/source/_module.less b/app/design/frontend/Magento/blank/Magento_Catalog/web/css/source/_module.less index 44e93087399a1..f57420deb621d 100644 --- a/app/design/frontend/Magento/blank/Magento_Catalog/web/css/source/_module.less +++ b/app/design/frontend/Magento/blank/Magento_Catalog/web/css/source/_module.less @@ -544,8 +544,7 @@ .compare, .product-addto-links .action.tocompare, - .product-item-actions .actions-secondary > .action.tocompare, - [class*='block-compare'] { + .product-item-actions .actions-secondary > .action.tocompare { display: none; } } diff --git a/app/design/frontend/Magento/blank/Magento_Checkout/web/css/source/module/_minicart.less b/app/design/frontend/Magento/blank/Magento_Checkout/web/css/source/module/_minicart.less index 133dd0fe721bb..9ed7e3a1ba839 100644 --- a/app/design/frontend/Magento/blank/Magento_Checkout/web/css/source/module/_minicart.less +++ b/app/design/frontend/Magento/blank/Magento_Checkout/web/css/source/module/_minicart.less @@ -135,7 +135,7 @@ .product { .actions { float: right; - margin: -24px 0 0; + margin: -28px 0 0; text-align: right; > .primary, @@ -347,6 +347,7 @@ .update-cart-item { .lib-font-size(11); + margin-left: 5px; vertical-align: top; } @@ -399,6 +400,7 @@ } .update-cart-item { float: right; + margin-left: 0; } } } diff --git a/app/design/frontend/Magento/blank/Magento_Customer/web/css/source/_module.less b/app/design/frontend/Magento/blank/Magento_Customer/web/css/source/_module.less index 0c2b1b4db83e6..adfcb117cc9d0 100644 --- a/app/design/frontend/Magento/blank/Magento_Customer/web/css/source/_module.less +++ b/app/design/frontend/Magento/blank/Magento_Customer/web/css/source/_module.less @@ -412,8 +412,10 @@ .block { &:extend(.abs-blocks-2columns all); - &.login { + .login { .actions-toolbar { + margin-left: 0; + > .primary { margin-bottom: 0; margin-right: @indent__l; @@ -428,6 +430,7 @@ .fieldset { &:after { + margin-left: 0; &:extend(.abs-margin-for-forms-desktop all); } @@ -435,6 +438,10 @@ > .control { width: 80%; } + + .label { + text-align: left; + } } } } diff --git a/app/design/frontend/Magento/blank/Magento_Theme/layout/default.xml b/app/design/frontend/Magento/blank/Magento_Theme/layout/default.xml new file mode 100644 index 0000000000000..76d9d319f955c --- /dev/null +++ b/app/design/frontend/Magento/blank/Magento_Theme/layout/default.xml @@ -0,0 +1,16 @@ +<?xml version="1.0"?> +<!-- +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +--> +<page xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="urn:magento:framework:View/Layout/etc/page_configuration.xsd"> + <body> + <referenceBlock name="logo"> + <arguments> + <argument name="logo_width" xsi:type="number">170</argument> + </arguments> + </referenceBlock> + </body> +</page> diff --git a/app/design/frontend/Magento/blank/Magento_Theme/web/css/source/_module.less b/app/design/frontend/Magento/blank/Magento_Theme/web/css/source/_module.less index 3faa8ca965410..8518b5bf76735 100644 --- a/app/design/frontend/Magento/blank/Magento_Theme/web/css/source/_module.less +++ b/app/design/frontend/Magento/blank/Magento_Theme/web/css/source/_module.less @@ -200,7 +200,7 @@ } } - .page-header, + .page-header .panel.wrapper, .page-footer { .switcher { margin-right: 10px; @@ -368,7 +368,7 @@ } .logo { - margin: -8px auto 25px 0; + margin: 0 auto 25px 0; img { max-height: inherit; diff --git a/app/design/frontend/Magento/blank/Magento_Wishlist/web/css/source/_module.less b/app/design/frontend/Magento/blank/Magento_Wishlist/web/css/source/_module.less index 9cd0439c13956..20a026ac2af9c 100644 --- a/app/design/frontend/Magento/blank/Magento_Wishlist/web/css/source/_module.less +++ b/app/design/frontend/Magento/blank/Magento_Wishlist/web/css/source/_module.less @@ -128,6 +128,13 @@ .products-grid.wishlist { .product { &-item { + margin-left: 2%; + width: calc(~'(100% - 4%) / 3'); + + &:nth-child(3n + 1) { + margin-left: 0; + } + &-photo { display: block; margin-bottom: @indent__s; @@ -185,6 +192,9 @@ .products-grid.wishlist { .product-item { border-bottom: 1px solid @secondary__color; + margin: 0; + width: 100%; + &:first-child { border-top: 1px solid @secondary__color; } diff --git a/app/design/frontend/Magento/blank/etc/view.xml b/app/design/frontend/Magento/blank/etc/view.xml index 5884699af15cd..dbadb958d0471 100644 --- a/app/design/frontend/Magento/blank/etc/view.xml +++ b/app/design/frontend/Magento/blank/etc/view.xml @@ -49,8 +49,8 @@ <height>100</height> </image> <image id="mini_cart_product_thumbnail" type="thumbnail"> - <width>78</width> - <height>78</height> + <width>156</width> + <height>156</height> </image> <image id="new_products_content_widget_grid" type="small_image"> <width>240</width> @@ -259,6 +259,7 @@ <var name="bundle_size">1MB</var> </vars> <exclude> + <item type="file">Lib::chartjs/Chart.min.js</item> <item type="file">Lib::jquery/jquery.min.js</item> <item type="file">Lib::jquery/jquery-ui-1.9.2.js</item> <item type="file">Lib::jquery/jquery.details.js</item> diff --git a/app/design/frontend/Magento/blank/registration.php b/app/design/frontend/Magento/blank/registration.php index 211f3fca131cc..297ae6d98ee41 100644 --- a/app/design/frontend/Magento/blank/registration.php +++ b/app/design/frontend/Magento/blank/registration.php @@ -4,6 +4,6 @@ * See COPYING.txt for license details. */ -use \Magento\Framework\Component\ComponentRegistrar; +use Magento\Framework\Component\ComponentRegistrar; ComponentRegistrar::register(ComponentRegistrar::THEME, 'frontend/Magento/blank', __DIR__); diff --git a/app/design/frontend/Magento/blank/web/css/source/_navigation.less b/app/design/frontend/Magento/blank/web/css/source/_navigation.less index 21b7315779764..fad906a089400 100644 --- a/app/design/frontend/Magento/blank/web/css/source/_navigation.less +++ b/app/design/frontend/Magento/blank/web/css/source/_navigation.less @@ -142,7 +142,7 @@ display: block; } } - } + } .header.links { .lib-list-reset-styles(); border-bottom: 1px solid @color-gray82; @@ -154,7 +154,7 @@ &.greet.welcome { border-top: 1px solid @color-gray82; font-weight: @font-weight__bold; - padding: .8rem @indent__base; + padding: .8rem @submenu__padding-left; } > a { @@ -168,7 +168,7 @@ .lib-css(text-decoration, @navigation-level0-item__text-decoration); display: block; font-weight: @font-weight__bold; - padding: .8rem @indent__base; + padding: .8rem @submenu__padding-left; } .header.links { diff --git a/app/design/frontend/Magento/luma/Magento_AdvancedCheckout/web/css/source/_module.less b/app/design/frontend/Magento/luma/Magento_AdvancedCheckout/web/css/source/_module.less index 8538a481903c7..4f2cf789d3a0e 100644 --- a/app/design/frontend/Magento/luma/Magento_AdvancedCheckout/web/css/source/_module.less +++ b/app/design/frontend/Magento/luma/Magento_AdvancedCheckout/web/css/source/_module.less @@ -48,12 +48,6 @@ } .block-content { - &:extend(.abs-add-clearfix-desktop all); - - .box { - &:extend(.abs-blocks-2columns all); - } - .actions-toolbar { clear: both; .lib-actions-toolbar( @@ -188,4 +182,16 @@ &:extend(.abs-add-clearfix-desktop all); } } + + .column { + .block-addbysku { + .block-content { + &:extend(.abs-add-clearfix-desktop all); + + .box { + &:extend(.abs-blocks-2columns all); + } + } + } + } } diff --git a/app/design/frontend/Magento/luma/Magento_Catalog/web/css/source/_module.less b/app/design/frontend/Magento/luma/Magento_Catalog/web/css/source/_module.less index 9b6986249b009..d0b7aa1523ad6 100644 --- a/app/design/frontend/Magento/luma/Magento_Catalog/web/css/source/_module.less +++ b/app/design/frontend/Magento/luma/Magento_Catalog/web/css/source/_module.less @@ -174,9 +174,9 @@ width: 100%; .price-box { - display: table-cell; + display: inline-block; vertical-align: top; - width: 1px; + width: auto; .price-container { > span { @@ -228,7 +228,8 @@ } .product-info-stock-sku { - display: table-cell; + display: inline-block; + float: right; padding-bottom: @indent__s; padding-left: 10%; text-align: right; @@ -622,11 +623,6 @@ // _____________________________________________ .media-width(@extremum, @break) when (@extremum = 'max') and (@break = @screen__s) { - .product-social-links { - .action.tocompare { - display: none; - } - } .product-info-price { margin: 0 -@indent__s 0; @@ -1006,8 +1002,7 @@ } .media-width(@extremum, @break) when (@extremum = 'max') and (@break = @screen__m) { - .compare.wrapper, - [class*='block-compare'] { + .compare.wrapper { display: none; } .catalog-product_compare-index { diff --git a/app/design/frontend/Magento/luma/Magento_Catalog/web/css/source/module/_listings.less b/app/design/frontend/Magento/luma/Magento_Catalog/web/css/source/module/_listings.less index 7745900f1766c..d106fa8886c05 100644 --- a/app/design/frontend/Magento/luma/Magento_Catalog/web/css/source/module/_listings.less +++ b/app/design/frontend/Magento/luma/Magento_Catalog/web/css/source/module/_listings.less @@ -108,6 +108,13 @@ .actions-primary { display: inline-block; vertical-align: middle; + + > .stock.unavailable { + line-height: 1; + padding-bottom: @indent__s; + padding-right: 24px; + padding-top: @indent__s; + } } } @@ -279,6 +286,12 @@ } } } + + .product-item-actions { + .actions-primary { + display: block; + } + } } .media-width(@extremum, @break) when (@extremum = 'min') and (@break = @screen__s) { diff --git a/app/design/frontend/Magento/luma/Magento_Catalog/web/css/source/module/_toolbar.less b/app/design/frontend/Magento/luma/Magento_Catalog/web/css/source/module/_toolbar.less index b587c169f67eb..9d30f787a7431 100644 --- a/app/design/frontend/Magento/luma/Magento_Catalog/web/css/source/module/_toolbar.less +++ b/app/design/frontend/Magento/luma/Magento_Catalog/web/css/source/module/_toolbar.less @@ -243,3 +243,13 @@ } } } + +// +// Mobile +// _____________________________________________ + +.media-width(@extremum, @break) when (@extremum = 'max') and (@break = @screen__xs) { + .sorter-options { + margin: 0 2px; + } +} diff --git a/app/design/frontend/Magento/luma/Magento_CatalogSearch/web/css/source/_module.less b/app/design/frontend/Magento/luma/Magento_CatalogSearch/web/css/source/_module.less index 31859a46d3efe..2176fcd241916 100644 --- a/app/design/frontend/Magento/luma/Magento_CatalogSearch/web/css/source/_module.less +++ b/app/design/frontend/Magento/luma/Magento_CatalogSearch/web/css/source/_module.less @@ -18,7 +18,7 @@ // _____________________________________________ & when (@media-common = true) { - + .search { .fieldset { .control { @@ -31,7 +31,7 @@ } } } - + .block-search { margin-bottom: 0; @@ -136,8 +136,6 @@ } .form.search.advanced { - &:extend(.abs-forms-general-desktop all); - .fields.range { .field { &:first-child { @@ -275,4 +273,8 @@ .search-autocomplete { margin-top: 0; } + + .form.search.advanced { + &:extend(.abs-forms-general-desktop all); + } } diff --git a/app/design/frontend/Magento/luma/Magento_Checkout/web/css/source/module/_cart.less b/app/design/frontend/Magento/luma/Magento_Checkout/web/css/source/module/_cart.less index cbf1d185a5a08..87990c3e48280 100644 --- a/app/design/frontend/Magento/luma/Magento_Checkout/web/css/source/module/_cart.less +++ b/app/design/frontend/Magento/luma/Magento_Checkout/web/css/source/module/_cart.less @@ -267,6 +267,10 @@ .lib-icon-font-symbol( @_icon-font-content: @icon-trash ); + + &:hover { + .lib-css(text-decoration, @link__text-decoration); + } } } diff --git a/app/design/frontend/Magento/luma/Magento_Checkout/web/css/source/module/_minicart.less b/app/design/frontend/Magento/luma/Magento_Checkout/web/css/source/module/_minicart.less index 43ccee738a45e..a97cc041b1c42 100644 --- a/app/design/frontend/Magento/luma/Magento_Checkout/web/css/source/module/_minicart.less +++ b/app/design/frontend/Magento/luma/Magento_Checkout/web/css/source/module/_minicart.less @@ -145,7 +145,7 @@ .product { .actions { float: right; - margin: -24px 0 0; + margin: -28px 0 0; > .primary, > .secondary { @@ -376,6 +376,7 @@ .update-cart-item { .lib-font-size(11); + margin-left: 5px; vertical-align: top; } @@ -428,6 +429,7 @@ } .update-cart-item { float: right; + margin-left: 0; } } } diff --git a/app/design/frontend/Magento/luma/Magento_Checkout/web/css/source/module/checkout/_estimated-total.less b/app/design/frontend/Magento/luma/Magento_Checkout/web/css/source/module/checkout/_estimated-total.less index 530c700cbc395..e89ad3aaf137f 100644 --- a/app/design/frontend/Magento/luma/Magento_Checkout/web/css/source/module/checkout/_estimated-total.less +++ b/app/design/frontend/Magento/luma/Magento_Checkout/web/css/source/module/checkout/_estimated-total.less @@ -14,7 +14,6 @@ // --------------------------------------------- .opc-estimated-wrapper { - &:extend(.abs-no-display-desktop all); &:extend(.abs-add-clearfix all); .lib-css(background, @checkout-step-content-mobile__background); .lib-css(border-bottom, @checkout-step-title__border); @@ -54,6 +53,6 @@ .media-width(@extremum, @break) when (@extremum = 'min') and (@break = @screen__m) { .opc-estimated-wrapper { - display: none; + &:extend(.abs-no-display-desktop all); } } diff --git a/app/design/frontend/Magento/luma/Magento_Checkout/web/css/source/module/checkout/_payments.less b/app/design/frontend/Magento/luma/Magento_Checkout/web/css/source/module/checkout/_payments.less index eb9c069053661..494483ff60dda 100644 --- a/app/design/frontend/Magento/luma/Magento_Checkout/web/css/source/module/checkout/_payments.less +++ b/app/design/frontend/Magento/luma/Magento_Checkout/web/css/source/module/checkout/_payments.less @@ -133,6 +133,10 @@ .lib-css(line-height, @checkout-billing-address-details__line-height); .lib-css(padding, @checkout-billing-address-details__padding); } + + input[type="checkbox"] { + vertical-align: top; + } } .payment-method-note { diff --git a/app/design/frontend/Magento/luma/Magento_Checkout/web/css/source/module/checkout/_shipping.less b/app/design/frontend/Magento/luma/Magento_Checkout/web/css/source/module/checkout/_shipping.less index d0edf245dc5b8..8f2092713b718 100644 --- a/app/design/frontend/Magento/luma/Magento_Checkout/web/css/source/module/checkout/_shipping.less +++ b/app/design/frontend/Magento/luma/Magento_Checkout/web/css/source/module/checkout/_shipping.less @@ -184,6 +184,10 @@ padding-right: 0; width: 20px; } + + input[type='radio'] { + margin: 4px 5px 0 0; + } } tr { @@ -292,6 +296,9 @@ .action { &.primary { margin: 0; + &.button { + margin-top: 2px; + } } } } diff --git a/app/design/frontend/Magento/luma/Magento_Checkout/web/css/source/module/checkout/fields/_file-uploader.less b/app/design/frontend/Magento/luma/Magento_Checkout/web/css/source/module/checkout/fields/_file-uploader.less index 7b06186ef9ad3..fbd9701d44be9 100644 --- a/app/design/frontend/Magento/luma/Magento_Checkout/web/css/source/module/checkout/fields/_file-uploader.less +++ b/app/design/frontend/Magento/luma/Magento_Checkout/web/css/source/module/checkout/fields/_file-uploader.less @@ -129,7 +129,6 @@ .file-uploader-preview { .action-remove { - &:extend(.abs-action-reset all); .lib-icon-font ( @icon-delete__content, @_icon-font: @icons__font-name, diff --git a/app/design/frontend/Magento/luma/Magento_Customer/web/css/source/_module.less b/app/design/frontend/Magento/luma/Magento_Customer/web/css/source/_module.less index 6354cc35d32ed..a0a36f55574fe 100644 --- a/app/design/frontend/Magento/luma/Magento_Customer/web/css/source/_module.less +++ b/app/design/frontend/Magento/luma/Magento_Customer/web/css/source/_module.less @@ -8,7 +8,7 @@ // _____________________________________________ @account-title-border-color: @color-gray-middle2; -@account-table-border-bottom-color: @color-gray-middle1; +@account-table-border-bottom-color: @color-gray_light; @account-table-action-delete: @color-red12; @_password-default: @color-gray-light01; @@ -348,6 +348,12 @@ position: relative; } } + + .additional-addresses { + table > thead > tr > th { + white-space: nowrap; + } + } } // diff --git a/app/design/frontend/Magento/luma/Magento_Downloadable/web/css/source/_module.less b/app/design/frontend/Magento/luma/Magento_Downloadable/web/css/source/_module.less index 7d911220d9dca..40b8cb8740810 100644 --- a/app/design/frontend/Magento/luma/Magento_Downloadable/web/css/source/_module.less +++ b/app/design/frontend/Magento/luma/Magento_Downloadable/web/css/source/_module.less @@ -92,7 +92,6 @@ } .field.choice { - &:extend(.clearer all); border-bottom: 1px solid @color-gray92; box-sizing: border-box; margin-bottom: @indent__s; @@ -178,10 +177,6 @@ .media-width(@extremum, @break) when (@extremum = 'min') and (@break = @screen__m) { .page-product-downloadable { - .product-add-form { - &:extend(.clearer all); - } - .product-options-wrapper { float: left; width: 55%; diff --git a/app/design/frontend/Magento/luma/Magento_GiftMessage/web/css/source/_module.less b/app/design/frontend/Magento/luma/Magento_GiftMessage/web/css/source/_module.less index 0f2a0db56a355..0453937471a28 100644 --- a/app/design/frontend/Magento/luma/Magento_GiftMessage/web/css/source/_module.less +++ b/app/design/frontend/Magento/luma/Magento_GiftMessage/web/css/source/_module.less @@ -27,7 +27,6 @@ & when (@media-common = true) { .gift-message { .field { - &:extend(.abs-clearfix all); margin-bottom: @indent__base; .label { diff --git a/app/design/frontend/Magento/luma/Magento_Newsletter/web/css/source/_module.less b/app/design/frontend/Magento/luma/Magento_Newsletter/web/css/source/_module.less index 5d44a32b9391b..a72f31d72ce48 100644 --- a/app/design/frontend/Magento/luma/Magento_Newsletter/web/css/source/_module.less +++ b/app/design/frontend/Magento/luma/Magento_Newsletter/web/css/source/_module.less @@ -81,6 +81,13 @@ .block.newsletter { max-width: 44%; width: max-content; + + .form.subscribe { + > .field, + > .actions { + float: left; + } + } } } diff --git a/app/design/frontend/Magento/luma/Magento_Sales/email/creditmemo_new.html b/app/design/frontend/Magento/luma/Magento_Sales/email/creditmemo_new.html index 86e3cf01e965e..f8e192bafdf29 100644 --- a/app/design/frontend/Magento/luma/Magento_Sales/email/creditmemo_new.html +++ b/app/design/frontend/Magento/luma/Magento_Sales/email/creditmemo_new.html @@ -20,6 +20,8 @@ "var store_email":"Store Email", "var creditmemo":"Credit Memo", "var order":"Order", +"var order_id": "Order DB Id", +"var creditmemo_id": "Credit Memo DB Id", "var order_data.is_not_virtual":"Order Type" } @--> {{template config_path="design/email/header_template"}} @@ -79,7 +81,7 @@ <h3>{{trans "Shipping Method"}}</h3> {{/depend}} </tr> </table> - {{layout handle="sales_email_order_creditmemo_items" creditmemo=$creditmemo order=$order}} + {{layout handle="sales_email_order_creditmemo_items" creditmemo_id=$creditmemo_id order_id=$order_id}} </td> </tr> </table> diff --git a/app/design/frontend/Magento/luma/Magento_Sales/email/creditmemo_new_guest.html b/app/design/frontend/Magento/luma/Magento_Sales/email/creditmemo_new_guest.html index d0310a8e2c7b6..4442c172a08e5 100644 --- a/app/design/frontend/Magento/luma/Magento_Sales/email/creditmemo_new_guest.html +++ b/app/design/frontend/Magento/luma/Magento_Sales/email/creditmemo_new_guest.html @@ -19,6 +19,8 @@ "var store_email":"Store Email", "var creditmemo":"Credit Memo", "var order":"Order", +"var order_id": "Order DB Id", +"var creditmemo_id": "Credit Memo DB Id", "var order_data.is_not_virtual":"Order Type" } @--> {{template config_path="design/email/header_template"}} @@ -77,7 +79,7 @@ <h3>{{trans "Shipping Method"}}</h3> {{/depend}} </tr> </table> - {{layout handle="sales_email_order_creditmemo_items" creditmemo=$creditmemo order=$order}} + {{layout handle="sales_email_order_creditmemo_items" creditmemo_id=$creditmemo_id order_id=$order_id}} </td> </tr> </table> diff --git a/app/design/frontend/Magento/luma/Magento_Sales/email/invoice_new.html b/app/design/frontend/Magento/luma/Magento_Sales/email/invoice_new.html index 636fa9ac5f425..c46f0b03a53f7 100644 --- a/app/design/frontend/Magento/luma/Magento_Sales/email/invoice_new.html +++ b/app/design/frontend/Magento/luma/Magento_Sales/email/invoice_new.html @@ -20,6 +20,8 @@ "var store_email":"Store Email", "var invoice": "Invoice", "var order": "Order", +"var order_id": "Order DB Id", +"var invoice_id": "Invoice DB Id", "var order_data.is_not_virtual": "Order Type" } @--> {{template config_path="design/email/header_template"}} @@ -79,7 +81,7 @@ <h3>{{trans "Shipping Method"}}</h3> {{/depend}} </tr> </table> - {{layout area="frontend" handle="sales_email_order_invoice_items" invoice=$invoice order=$order}} + {{layout area="frontend" handle="sales_email_order_invoice_items" invoice_id=$invoice_id order_id=$order_id}} </td> </tr> </table> diff --git a/app/design/frontend/Magento/luma/Magento_Sales/email/invoice_new_guest.html b/app/design/frontend/Magento/luma/Magento_Sales/email/invoice_new_guest.html index 7df5ffe5f4ab8..6e35fd2609dff 100644 --- a/app/design/frontend/Magento/luma/Magento_Sales/email/invoice_new_guest.html +++ b/app/design/frontend/Magento/luma/Magento_Sales/email/invoice_new_guest.html @@ -19,6 +19,8 @@ "var store_email":"Store Email", "var invoice": "Invoice", "var order": "Order", +"var order_id": "Order DB Id", +"var invoice_id": "Invoice DB Id", "var order_data.is_not_virtual": "Order Type" } @--> {{template config_path="design/email/header_template"}} @@ -77,7 +79,7 @@ <h3>{{trans "Shipping Method"}}</h3> {{/depend}} </tr> </table> - {{layout handle="sales_email_order_invoice_items" invoice=$invoice order=$order}} + {{layout handle="sales_email_order_invoice_items" invoice_id=$invoice_id order_id=$order_id}} </td> </tr> </table> diff --git a/app/design/frontend/Magento/luma/Magento_Sales/email/order_new.html b/app/design/frontend/Magento/luma/Magento_Sales/email/order_new.html index 745bf5c9c2eff..373db99d87d99 100644 --- a/app/design/frontend/Magento/luma/Magento_Sales/email/order_new.html +++ b/app/design/frontend/Magento/luma/Magento_Sales/email/order_new.html @@ -20,6 +20,7 @@ "var order":"Order", "var order_data.is_not_virtual":"Order Type", "var order_data.customer_name":"Customer Name", +"var order_id": "Order DB Id", "var this.getUrl($store,'customer/account/',[_nosid:1])":"Customer Account URL" } @--> @@ -85,7 +86,7 @@ <h3>{{trans "Shipping Method"}}</h3> {{/depend}} </tr> </table> - {{layout handle="sales_email_order_items" order=$order area="frontend"}} + {{layout handle="sales_email_order_items" order_id=$order_id area="frontend"}} </td> </tr> </table> diff --git a/app/design/frontend/Magento/luma/Magento_Sales/email/order_new_guest.html b/app/design/frontend/Magento/luma/Magento_Sales/email/order_new_guest.html index 907be4d45a6c5..024f6daf76ace 100644 --- a/app/design/frontend/Magento/luma/Magento_Sales/email/order_new_guest.html +++ b/app/design/frontend/Magento/luma/Magento_Sales/email/order_new_guest.html @@ -19,6 +19,7 @@ "var store.frontend_name":"Store Frontend Name", "var store_email":"Store Email", "var order":"Order", +"var order_id": "Order DB Id", "var order_data.is_not_virtual":"Order Type" } @--> {{template config_path="design/email/header_template"}} @@ -82,7 +83,7 @@ <h3>{{trans "Shipping Method"}}</h3> {{/depend}} </tr> </table> - {{layout handle="sales_email_order_items" order=$order}} + {{layout handle="sales_email_order_items" order_id=$order_id}} </td> </tr> </table> diff --git a/app/design/frontend/Magento/luma/Magento_Sales/email/shipment_new.html b/app/design/frontend/Magento/luma/Magento_Sales/email/shipment_new.html index 4ff9da3a31b27..d1b1e1e33763c 100644 --- a/app/design/frontend/Magento/luma/Magento_Sales/email/shipment_new.html +++ b/app/design/frontend/Magento/luma/Magento_Sales/email/shipment_new.html @@ -21,7 +21,9 @@ "var store_email":"Store Email", "var order_data.is_not_virtual": "Order Type", "var shipment": "Shipment", -"var order": "Order" +"var order": "Order", +"var order_id": "Order DB Id", +"var shipment_id": "Shipment DB Id" } @--> {{template config_path="design/email/header_template"}} @@ -55,7 +57,7 @@ <h1>{{trans "Your Shipment #%shipment_id for Order #%order_id" shipment_id=$ship </tr> </table> {{/depend}} - {{layout handle="sales_email_order_shipment_track" shipment=$shipment order=$order}} + {{layout handle="sales_email_order_shipment_track" shipment_id=$shipment_id order_id=$order_id}} <table class="order-details"> <tr> <td class="address-details"> @@ -82,7 +84,7 @@ <h3>{{trans "Shipping Method"}}</h3> {{/depend}} </tr> </table> - {{layout handle="sales_email_order_shipment_items" shipment=$shipment order=$order}} + {{layout handle="sales_email_order_shipment_items" shipment_id=$shipment_id order_id=$order_id}} </td> </tr> </table> diff --git a/app/design/frontend/Magento/luma/Magento_Sales/email/shipment_new_guest.html b/app/design/frontend/Magento/luma/Magento_Sales/email/shipment_new_guest.html index ac7eaae6b7ff7..18684fb052b4e 100644 --- a/app/design/frontend/Magento/luma/Magento_Sales/email/shipment_new_guest.html +++ b/app/design/frontend/Magento/luma/Magento_Sales/email/shipment_new_guest.html @@ -21,6 +21,8 @@ "var order_data.is_not_virtual": "Order Type", "var shipment": "Shipment", "var order": "Order", +"var order_id": "Order DB Id", +"var shipment_id": "Shipment DB Id", "var this.getUrl($store,'customer/account/',[_nosid:1])":"Customer Account URL" } @--> {{template config_path="design/email/header_template"}} @@ -54,7 +56,7 @@ <h1>{{trans "Your Shipment #%shipment_id for Order #%order_id" shipment_id=$ship </tr> </table> {{/depend}} - {{layout handle="sales_email_order_shipment_track" shipment=$shipment order=$order}} + {{layout handle="sales_email_order_shipment_track" shipment_id=$shipment_id order_id=$order_id}} <table class="order-details"> <tr> <td class="address-details"> @@ -81,7 +83,7 @@ <h3>{{trans "Shipping Method"}}</h3> {{/depend}} </tr> </table> - {{layout handle="sales_email_order_shipment_items" shipment=$shipment order=$order}} + {{layout handle="sales_email_order_shipment_items" shipment_id=$shipment_id order_id=$order_id}} </td> </tr> </table> diff --git a/app/design/frontend/Magento/luma/Magento_Sales/web/css/source/_module.less b/app/design/frontend/Magento/luma/Magento_Sales/web/css/source/_module.less index 599218f970907..bab8a2abb9b93 100644 --- a/app/design/frontend/Magento/luma/Magento_Sales/web/css/source/_module.less +++ b/app/design/frontend/Magento/luma/Magento_Sales/web/css/source/_module.less @@ -311,7 +311,6 @@ margin-right: 30px; &.print { - &:extend(.abs-action-print all); display: none; margin: 0; } @@ -589,6 +588,7 @@ .order-actions-toolbar { .action.print { + &:extend(.abs-action-print all); display: block; float: right; } @@ -709,7 +709,7 @@ } } -.media-width(@extremum, @break) when (@extremum = 'min') and (@break = @screen__l) { +.media-width(@extremum, @break) when (@extremum = 'min') and (@break = @screen__l) { .order-links { .item { margin: 0 @tab-control__margin-right 0 0; diff --git a/app/design/frontend/Magento/luma/Magento_Theme/web/css/source/_module.less b/app/design/frontend/Magento/luma/Magento_Theme/web/css/source/_module.less index 438fb55d32e5c..ac2a2f249ac11 100644 --- a/app/design/frontend/Magento/luma/Magento_Theme/web/css/source/_module.less +++ b/app/design/frontend/Magento/luma/Magento_Theme/web/css/source/_module.less @@ -289,7 +289,7 @@ text-align: center; } - .page-header, + .page-header .panel.wrapper, .page-footer { .switcher { .options { @@ -331,7 +331,7 @@ } } } - .page-header { + .page-header .panel.wrapper { .switcher { .options { ul.dropdown { diff --git a/app/design/frontend/Magento/luma/Magento_Wishlist/web/css/source/_module.less b/app/design/frontend/Magento/luma/Magento_Wishlist/web/css/source/_module.less index 85e8aeb0b515c..69342821e9b17 100644 --- a/app/design/frontend/Magento/luma/Magento_Wishlist/web/css/source/_module.less +++ b/app/design/frontend/Magento/luma/Magento_Wishlist/web/css/source/_module.less @@ -135,6 +135,13 @@ .products-grid.wishlist { .product { &-item { + margin-left: 2%; + width: calc(~'(100% - 4%) / 3'); + + &:nth-child(3n + 1) { + margin-left: 0; + } + &-photo { display: block; margin-bottom: @indent__s; @@ -183,7 +190,7 @@ } } .products-grid.wishlist { - .product-item-actions { + .product-item-actions { .action { &.edit, &.delete { @@ -216,6 +223,8 @@ .products-grid.wishlist { .product-item { border-bottom: 1px solid @secondary__color; + margin: 0; + width: 100%; &:first-child { border-top: 1px solid @secondary__color; @@ -253,7 +262,7 @@ &:last-child { margin-right: 0; } - + &.edit, &.delete { margin-top: 7px; diff --git a/app/design/frontend/Magento/luma/etc/view.xml b/app/design/frontend/Magento/luma/etc/view.xml index a2802b7e374f3..b02f221784494 100644 --- a/app/design/frontend/Magento/luma/etc/view.xml +++ b/app/design/frontend/Magento/luma/etc/view.xml @@ -53,8 +53,8 @@ <height>100</height> </image> <image id="mini_cart_product_thumbnail" type="thumbnail"> - <width>75</width> - <height>75</height> + <width>150</width> + <height>150</height> </image> <image id="new_products_content_widget_grid" type="small_image"> <width>240</width> @@ -270,6 +270,7 @@ <var name="bundle_size">1MB</var> </vars> <exclude> + <item type="file">Lib::chartjs/Chart.min.js</item> <item type="file">Lib::jquery/jquery.min.js</item> <item type="file">Lib::jquery/jquery-ui-1.9.2.js</item> <item type="file">Lib::jquery/jquery.details.js</item> diff --git a/app/design/frontend/Magento/luma/registration.php b/app/design/frontend/Magento/luma/registration.php index 41921c06fd040..f8a2c3bdb8b2e 100644 --- a/app/design/frontend/Magento/luma/registration.php +++ b/app/design/frontend/Magento/luma/registration.php @@ -4,6 +4,6 @@ * See COPYING.txt for license details. */ -use \Magento\Framework\Component\ComponentRegistrar; +use Magento\Framework\Component\ComponentRegistrar; ComponentRegistrar::register(ComponentRegistrar::THEME, 'frontend/Magento/luma', __DIR__); diff --git a/app/etc/di.xml b/app/etc/di.xml index dfbf4f2373ac5..dbc4a1832adf6 100644 --- a/app/etc/di.xml +++ b/app/etc/di.xml @@ -167,6 +167,7 @@ <preference for="Magento\Framework\EntityManager\MapperInterface" type="Magento\Framework\EntityManager\CompositeMapper"/> <preference for="Magento\Framework\Console\CommandListInterface" type="Magento\Framework\Console\CommandList"/> <preference for="Magento\Framework\DataObject\IdentityGeneratorInterface" type="Magento\Framework\DataObject\IdentityService" /> + <preference for="Magento\Framework\DataObject\IdentityValidatorInterface" type="Magento\Framework\DataObject\IdentityValidator" /> <preference for="Magento\Framework\Serialize\SerializerInterface" type="Magento\Framework\Serialize\Serializer\Json" /> <preference for="Magento\Framework\App\Scope\ValidatorInterface" type="Magento\Framework\App\Scope\Validator"/> <preference for="Magento\Framework\App\ScopeResolverInterface" type="Magento\Framework\App\ScopeResolver" /> @@ -284,6 +285,7 @@ <type name="Magento\Framework\App\Request\Http"> <arguments> <argument name="pathInfoProcessor" xsi:type="object">Magento\Backend\App\Request\PathInfoProcessor\Proxy</argument> + <argument name="routeConfig" xsi:type="object">Magento\Framework\App\Route\ConfigInterface\Proxy</argument> </arguments> </type> <type name="Magento\Framework\App\Response\Http"> @@ -1444,6 +1446,7 @@ <item name="mediumtext" xsi:type="object">\Magento\Framework\Setup\Declaration\Schema\Dto\Factories\MediumText</item> <item name="text" xsi:type="object">\Magento\Framework\Setup\Declaration\Schema\Dto\Factories\Text</item> <item name="varchar" xsi:type="object">\Magento\Framework\Setup\Declaration\Schema\Dto\Factories\StringBinary</item> + <item name="char" xsi:type="object">\Magento\Framework\Setup\Declaration\Schema\Dto\Factories\StringBinary</item> <item name="varbinary" xsi:type="object">\Magento\Framework\Setup\Declaration\Schema\Dto\Factories\StringBinary</item> <item name="blob" xsi:type="object">\Magento\Framework\Setup\Declaration\Schema\Dto\Factories\Blob</item> <item name="mediumblob" xsi:type="object">\Magento\Framework\Setup\Declaration\Schema\Dto\Factories\MediumBlob</item> @@ -1453,6 +1456,7 @@ <item name="primary" xsi:type="object">\Magento\Framework\Setup\Declaration\Schema\Dto\Factories\Primary</item> <item name="foreign" xsi:type="object">\Magento\Framework\Setup\Declaration\Schema\Dto\Factories\Foreign</item> <item name="index" xsi:type="object">\Magento\Framework\Setup\Declaration\Schema\Dto\Factories\Index</item> + <item name="json" xsi:type="object">\Magento\Framework\Setup\Declaration\Schema\Dto\Factories\Json</item> </argument> </arguments> </type> @@ -1480,6 +1484,7 @@ <item name="varchar" xsi:type="object">\Magento\Framework\Setup\Declaration\Schema\Db\MySQL\Definition\Columns\StringBinary</item> <item name="binary" xsi:type="object">\Magento\Framework\Setup\Declaration\Schema\Db\MySQL\Definition\Columns\StringBinary</item> <item name="varbinary" xsi:type="object">\Magento\Framework\Setup\Declaration\Schema\Db\MySQL\Definition\Columns\StringBinary</item> + <item name="json" xsi:type="object">\Magento\Framework\Setup\Declaration\Schema\Db\MySQL\Definition\Columns\Json</item> <item name="index" xsi:type="object">\Magento\Framework\Setup\Declaration\Schema\Db\MySQL\Definition\Index</item> <item name="unique" xsi:type="object">\Magento\Framework\Setup\Declaration\Schema\Db\MySQL\Definition\Constraints\Internal</item> <item name="primary" xsi:type="object">\Magento\Framework\Setup\Declaration\Schema\Db\MySQL\Definition\Constraints\Internal</item> @@ -1589,10 +1594,12 @@ <item name="longblog" xsi:type="object">Magento\Framework\Setup\SchemaListenerDefinition\TextBlobDefinition</item> <item name="varbinary" xsi:type="object">Magento\Framework\Setup\SchemaListenerDefinition\TextBlobDefinition</item> <item name="varchar" xsi:type="object">Magento\Framework\Setup\SchemaListenerDefinition\TextBlobDefinition</item> + <item name="char" xsi:type="object">Magento\Framework\Setup\SchemaListenerDefinition\CharDefinition</item> <item name="timestamp" xsi:type="object">Magento\Framework\Setup\SchemaListenerDefinition\TimestampDefinition</item> <item name="datetime" xsi:type="object">Magento\Framework\Setup\SchemaListenerDefinition\TimestampDefinition</item> <item name="date" xsi:type="object">Magento\Framework\Setup\SchemaListenerDefinition\DateDefinition</item> <item name="boolean" xsi:type="object">Magento\Framework\Setup\SchemaListenerDefinition\BooleanDefinition</item> + <item name="json" xsi:type="object">Magento\Framework\Setup\SchemaListenerDefinition\JsonDefinition</item> </argument> </arguments> </type> @@ -1800,4 +1807,14 @@ </type> <preference for="Magento\Framework\GraphQl\Query\ErrorHandlerInterface" type="Magento\Framework\GraphQl\Query\ErrorHandler"/> <preference for="Magento\Framework\Filter\VariableResolverInterface" type="Magento\Framework\Filter\VariableResolver\StrategyResolver"/> + <virtualType name="configured_block_cache" type="Magento\Framework\App\Cache"> + <arguments> + <argument name="cacheIdentifier" xsi:type="string">block_html</argument> + </arguments> + </virtualType> + <type name="Magento\Framework\View\Element\Context"> + <arguments> + <argument name="cache" xsi:type="object">configured_block_cache</argument> + </arguments> + </type> </config> diff --git a/app/functions.php b/app/functions.php deleted file mode 100644 index 6b3dae71c42c6..0000000000000 --- a/app/functions.php +++ /dev/null @@ -1,30 +0,0 @@ -<?php -/** - * Copyright © Magento, Inc. All rights reserved. - * See COPYING.txt for license details. - */ - -/** - * Create value-object \Magento\Framework\Phrase - * @deprecated The global function __() is now loaded via Magento Framework, the below require is only - * for backwards compatibility reasons and this file will be removed in a future version - * @see Magento\Framework\Phrase\__.php - * @SuppressWarnings(PHPMD.ShortMethodName) - * @return \Magento\Framework\Phrase - */ -if (!function_exists('__')) { - /** - * @return \Magento\Framework\Phrase - */ - function __() - { - $argc = func_get_args(); - - $text = array_shift($argc); - if (!empty($argc) && is_array($argc[0])) { - $argc = $argc[0]; - } - - return new \Magento\Framework\Phrase($text, $argc); - } -} diff --git a/app/i18n/Magento/de_DE/registration.php b/app/i18n/Magento/de_DE/registration.php index 12f479d30b1e1..fae5a938af236 100644 --- a/app/i18n/Magento/de_DE/registration.php +++ b/app/i18n/Magento/de_DE/registration.php @@ -4,6 +4,6 @@ * See COPYING.txt for license details. */ -use \Magento\Framework\Component\ComponentRegistrar; +use Magento\Framework\Component\ComponentRegistrar; ComponentRegistrar::register(ComponentRegistrar::LANGUAGE, 'magento_de_de', __DIR__); diff --git a/app/i18n/Magento/en_US/registration.php b/app/i18n/Magento/en_US/registration.php index a8fd609af0592..25c6ce2b53eda 100644 --- a/app/i18n/Magento/en_US/registration.php +++ b/app/i18n/Magento/en_US/registration.php @@ -4,6 +4,6 @@ * See COPYING.txt for license details. */ -use \Magento\Framework\Component\ComponentRegistrar; +use Magento\Framework\Component\ComponentRegistrar; ComponentRegistrar::register(ComponentRegistrar::LANGUAGE, 'magento_en_us', __DIR__); diff --git a/app/i18n/Magento/es_ES/registration.php b/app/i18n/Magento/es_ES/registration.php index b22457a5be4d9..6109afe6b3aa2 100644 --- a/app/i18n/Magento/es_ES/registration.php +++ b/app/i18n/Magento/es_ES/registration.php @@ -4,6 +4,6 @@ * See COPYING.txt for license details. */ -use \Magento\Framework\Component\ComponentRegistrar; +use Magento\Framework\Component\ComponentRegistrar; ComponentRegistrar::register(ComponentRegistrar::LANGUAGE, 'magento_es_es', __DIR__); diff --git a/app/i18n/Magento/fr_FR/registration.php b/app/i18n/Magento/fr_FR/registration.php index ed866adf28cd1..bca5547092867 100644 --- a/app/i18n/Magento/fr_FR/registration.php +++ b/app/i18n/Magento/fr_FR/registration.php @@ -4,6 +4,6 @@ * See COPYING.txt for license details. */ -use \Magento\Framework\Component\ComponentRegistrar; +use Magento\Framework\Component\ComponentRegistrar; ComponentRegistrar::register(ComponentRegistrar::LANGUAGE, 'magento_fr_fr', __DIR__); diff --git a/app/i18n/Magento/nl_NL/registration.php b/app/i18n/Magento/nl_NL/registration.php index b86c41cd8c76d..27a93b13df5eb 100644 --- a/app/i18n/Magento/nl_NL/registration.php +++ b/app/i18n/Magento/nl_NL/registration.php @@ -4,6 +4,6 @@ * See COPYING.txt for license details. */ -use \Magento\Framework\Component\ComponentRegistrar; +use Magento\Framework\Component\ComponentRegistrar; ComponentRegistrar::register(ComponentRegistrar::LANGUAGE, 'magento_nl_nl', __DIR__); diff --git a/app/i18n/Magento/pt_BR/registration.php b/app/i18n/Magento/pt_BR/registration.php index eec9dc7895f29..edfbb40f0a78e 100644 --- a/app/i18n/Magento/pt_BR/registration.php +++ b/app/i18n/Magento/pt_BR/registration.php @@ -4,6 +4,6 @@ * See COPYING.txt for license details. */ -use \Magento\Framework\Component\ComponentRegistrar; +use Magento\Framework\Component\ComponentRegistrar; ComponentRegistrar::register(ComponentRegistrar::LANGUAGE, 'magento_pt_br', __DIR__); diff --git a/app/i18n/Magento/zh_Hans_CN/registration.php b/app/i18n/Magento/zh_Hans_CN/registration.php index 675778b201e41..da291a0d1b98b 100644 --- a/app/i18n/Magento/zh_Hans_CN/registration.php +++ b/app/i18n/Magento/zh_Hans_CN/registration.php @@ -4,6 +4,6 @@ * See COPYING.txt for license details. */ -use \Magento\Framework\Component\ComponentRegistrar; +use Magento\Framework\Component\ComponentRegistrar; ComponentRegistrar::register(ComponentRegistrar::LANGUAGE, 'magento_zh_hans_cn', __DIR__); diff --git a/composer.json b/composer.json index ab767fdac286d..5223fa2a0aca4 100644 --- a/composer.json +++ b/composer.json @@ -30,57 +30,58 @@ "lib-libxml": "*", "braintree/braintree_php": "3.35.0", "colinmollenhour/cache-backend-file": "~1.4.1", - "colinmollenhour/cache-backend-redis": "1.10.6", + "colinmollenhour/cache-backend-redis": "1.11.0", "colinmollenhour/credis": "1.10.0", "colinmollenhour/php-redis-session-abstract": "~1.4.0", "composer/composer": "^1.6", - "elasticsearch/elasticsearch": "~2.0||~5.1||~6.1", - "magento/composer": "~1.5.0", + "elasticsearch/elasticsearch": "~7.6", + "guzzlehttp/guzzle": "^6.3.3", + "laminas/laminas-captcha": "^2.7.1", + "laminas/laminas-code": "~3.3.0", + "laminas/laminas-config": "^2.6.0", + "laminas/laminas-console": "^2.6.0", + "laminas/laminas-crypt": "^2.6.0", + "laminas/laminas-db": "^2.8.2", + "laminas/laminas-dependency-plugin": "^1.0", + "laminas/laminas-di": "^2.6.1", + "laminas/laminas-eventmanager": "^3.0.0", + "laminas/laminas-feed": "^2.9.0", + "laminas/laminas-form": "^2.10.0", + "laminas/laminas-http": "^2.6.0", + "laminas/laminas-i18n": "^2.7.3", + "laminas/laminas-json": "^2.6.1", + "laminas/laminas-log": "^2.9.1", + "laminas/laminas-mail": "^2.9.0", + "laminas/laminas-mime": "^2.5.0", + "laminas/laminas-modulemanager": "^2.7", + "laminas/laminas-mvc": "~2.7.0", + "laminas/laminas-serializer": "^2.7.2", + "laminas/laminas-server": "^2.6.1", + "laminas/laminas-servicemanager": "^2.7.8", + "laminas/laminas-session": "^2.7.3", + "laminas/laminas-soap": "^2.7.0", + "laminas/laminas-stdlib": "^3.2.1", + "laminas/laminas-text": "^2.6.0", + "laminas/laminas-uri": "^2.5.1", + "laminas/laminas-validator": "^2.6.0", + "laminas/laminas-view": "~2.11.2", + "magento/composer": "1.6.x-dev", "magento/magento-composer-installer": ">=0.1.11", "magento/zendframework1": "~1.14.2", "monolog/monolog": "^1.17", - "wikimedia/less.php": "~1.8.0", "paragonie/sodium_compat": "^1.6", "pelago/emogrifier": "^2.0.0", "php-amqplib/php-amqplib": "~2.7.0||~2.10.0", "phpseclib/mcrypt_compat": "1.0.8", "phpseclib/phpseclib": "2.0.*", "ramsey/uuid": "~3.8.0", - "symfony/console": "~4.1.0||~4.2.0||~4.3.0", - "symfony/event-dispatcher": "~4.1.0||~4.2.0||~4.3.0", - "symfony/process": "~4.1.0||~4.2.0||~4.3.0", + "symfony/console": "~4.4.0", + "symfony/event-dispatcher": "~4.4.0", + "symfony/process": "~4.4.0", "tedivm/jshrink": "~1.3.0", "tubalmartin/cssmin": "4.1.1", "webonyx/graphql-php": "^0.13.8", - "zendframework/zend-captcha": "^2.7.1", - "zendframework/zend-code": "~3.3.0", - "zendframework/zend-config": "^2.6.0", - "zendframework/zend-console": "^2.6.0", - "zendframework/zend-crypt": "^2.6.0", - "zendframework/zend-db": "^2.8.2", - "zendframework/zend-di": "^2.6.1", - "zendframework/zend-eventmanager": "^3.0.0", - "zendframework/zend-feed": "^2.9.0", - "zendframework/zend-form": "^2.10.0", - "zendframework/zend-http": "^2.6.0", - "zendframework/zend-i18n": "^2.7.3", - "zendframework/zend-json": "^2.6.1", - "zendframework/zend-log": "^2.9.1", - "zendframework/zend-mail": "^2.9.0", - "zendframework/zend-mime": "^2.5.0", - "zendframework/zend-modulemanager": "^2.7", - "zendframework/zend-mvc": "~2.7.0", - "zendframework/zend-serializer": "^2.7.2", - "zendframework/zend-server": "^2.6.1", - "zendframework/zend-servicemanager": "^2.7.8", - "zendframework/zend-session": "^2.7.3", - "zendframework/zend-soap": "^2.7.0", - "zendframework/zend-stdlib": "^3.2.1", - "zendframework/zend-text": "^2.6.0", - "zendframework/zend-uri": "^2.5.1", - "zendframework/zend-validator": "^2.6.0", - "zendframework/zend-view": "~2.11.2", - "guzzlehttp/guzzle": "^6.3.3" + "wikimedia/less.php": "~1.8.0" }, "require-dev": { "allure-framework/allure-phpunit": "~1.2.0", @@ -88,7 +89,7 @@ "friendsofphp/php-cs-fixer": "~2.14.0", "lusitanian/oauth": "~0.8.10", "magento/magento-coding-standard": "*", - "magento/magento2-functional-testing-framework": "2.5.4", + "magento/magento2-functional-testing-framework": "dev-3.0.0-RC1", "pdepend/pdepend": "2.5.2", "phpcompatibility/php-compatibility": "^9.3", "phpmd/phpmd": "@stable", @@ -110,10 +111,6 @@ "magento/module-analytics": "*", "magento/module-asynchronous-operations": "*", "magento/module-authorization": "*", - "magento/module-authorizenet": "*", - "magento/module-authorizenet-acceptjs": "*", - "magento/module-authorizenet-cardinal": "*", - "magento/module-authorizenet-graph-ql": "*", "magento/module-advanced-search": "*", "magento/module-backend": "*", "magento/module-backup": "*", @@ -132,6 +129,7 @@ "magento/module-catalog-inventory": "*", "magento/module-catalog-inventory-graph-ql": "*", "magento/module-catalog-rule": "*", + "magento/module-catalog-rule-graph-ql": "*", "magento/module-catalog-rule-configurable": "*", "magento/module-catalog-search": "*", "magento/module-catalog-url-rewrite": "*", @@ -164,6 +162,7 @@ "magento/module-eav": "*", "magento/module-elasticsearch": "*", "magento/module-elasticsearch-6": "*", + "magento/module-elasticsearch-7": "*", "magento/module-email": "*", "magento/module-encryption-key": "*", "magento/module-fedex": "*", @@ -239,7 +238,6 @@ "magento/module-send-friend": "*", "magento/module-send-friend-graph-ql": "*", "magento/module-shipping": "*", - "magento/module-signifyd": "*", "magento/module-sitemap": "*", "magento/module-store": "*", "magento/module-store-graph-ql": "*", diff --git a/composer.lock b/composer.lock index b6d834610059a..37849c7c21a29 100644 --- a/composer.lock +++ b/composer.lock @@ -4,7 +4,7 @@ "Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies", "This file is @generated automatically" ], - "content-hash": "8d8e6b87c1f6ac98b3b7331eba9473f3", + "content-hash": "1f11bed01d000a3d3eeda8b462e29f75", "packages": [ { "name": "braintree/braintree_php", @@ -88,16 +88,16 @@ }, { "name": "colinmollenhour/cache-backend-redis", - "version": "1.10.6", + "version": "1.11.0", "source": { "type": "git", "url": "https://github.com/colinmollenhour/Cm_Cache_Backend_Redis.git", - "reference": "cc941a5f4cc017e11d3eab9061811ba9583ed6bf" + "reference": "389fb68de15660e39b055d149d31f3708b5d6cbc" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/colinmollenhour/Cm_Cache_Backend_Redis/zipball/cc941a5f4cc017e11d3eab9061811ba9583ed6bf", - "reference": "cc941a5f4cc017e11d3eab9061811ba9583ed6bf", + "url": "https://api.github.com/repos/colinmollenhour/Cm_Cache_Backend_Redis/zipball/389fb68de15660e39b055d149d31f3708b5d6cbc", + "reference": "389fb68de15660e39b055d149d31f3708b5d6cbc", "shasum": "" }, "require": { @@ -120,7 +120,7 @@ ], "description": "Zend_Cache backend using Redis with full support for tags.", "homepage": "https://github.com/colinmollenhour/Cm_Cache_Backend_Redis", - "time": "2018-09-24T16:02:07+00:00" + "time": "2019-03-03T04:04:49+00:00" }, { "name": "colinmollenhour/credis", @@ -164,16 +164,16 @@ }, { "name": "colinmollenhour/php-redis-session-abstract", - "version": "v1.4.1", + "version": "v1.4.2", "source": { "type": "git", "url": "https://github.com/colinmollenhour/php-redis-session-abstract.git", - "reference": "4949ca28b86037abb44984c77bab9d0a4e075643" + "reference": "669521218794f125c7b668252f4f576eda65e1e4" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/colinmollenhour/php-redis-session-abstract/zipball/4949ca28b86037abb44984c77bab9d0a4e075643", - "reference": "4949ca28b86037abb44984c77bab9d0a4e075643", + "url": "https://api.github.com/repos/colinmollenhour/php-redis-session-abstract/zipball/669521218794f125c7b668252f4f576eda65e1e4", + "reference": "669521218794f125c7b668252f4f576eda65e1e4", "shasum": "" }, "require": { @@ -197,20 +197,20 @@ ], "description": "A Redis-based session handler with optimistic locking", "homepage": "https://github.com/colinmollenhour/php-redis-session-abstract", - "time": "2019-03-18T14:43:14+00:00" + "time": "2020-01-08T17:41:01+00:00" }, { "name": "composer/ca-bundle", - "version": "1.2.4", + "version": "1.2.6", "source": { "type": "git", "url": "https://github.com/composer/ca-bundle.git", - "reference": "10bb96592168a0f8e8f6dcde3532d9fa50b0b527" + "reference": "47fe531de31fca4a1b997f87308e7d7804348f7e" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/composer/ca-bundle/zipball/10bb96592168a0f8e8f6dcde3532d9fa50b0b527", - "reference": "10bb96592168a0f8e8f6dcde3532d9fa50b0b527", + "url": "https://api.github.com/repos/composer/ca-bundle/zipball/47fe531de31fca4a1b997f87308e7d7804348f7e", + "reference": "47fe531de31fca4a1b997f87308e7d7804348f7e", "shasum": "" }, "require": { @@ -221,7 +221,7 @@ "require-dev": { "phpunit/phpunit": "^4.8.35 || ^5.7 || 6.5 - 8", "psr/log": "^1.0", - "symfony/process": "^2.5 || ^3.0 || ^4.0" + "symfony/process": "^2.5 || ^3.0 || ^4.0 || ^5.0" }, "type": "library", "extra": { @@ -253,20 +253,20 @@ "ssl", "tls" ], - "time": "2019-08-30T08:44:50+00:00" + "time": "2020-01-13T10:02:55+00:00" }, { "name": "composer/composer", - "version": "1.9.1", + "version": "1.10.1", "source": { "type": "git", "url": "https://github.com/composer/composer.git", - "reference": "bb01f2180df87ce7992b8331a68904f80439dd2f" + "reference": "b912a45da3e2b22f5cb5a23e441b697a295ba011" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/composer/composer/zipball/bb01f2180df87ce7992b8331a68904f80439dd2f", - "reference": "bb01f2180df87ce7992b8331a68904f80439dd2f", + "url": "https://api.github.com/repos/composer/composer/zipball/b912a45da3e2b22f5cb5a23e441b697a295ba011", + "reference": "b912a45da3e2b22f5cb5a23e441b697a295ba011", "shasum": "" }, "require": { @@ -279,17 +279,17 @@ "psr/log": "^1.0", "seld/jsonlint": "^1.4", "seld/phar-utils": "^1.0", - "symfony/console": "^2.7 || ^3.0 || ^4.0", - "symfony/filesystem": "^2.7 || ^3.0 || ^4.0", - "symfony/finder": "^2.7 || ^3.0 || ^4.0", - "symfony/process": "^2.7 || ^3.0 || ^4.0" + "symfony/console": "^2.7 || ^3.0 || ^4.0 || ^5.0", + "symfony/filesystem": "^2.7 || ^3.0 || ^4.0 || ^5.0", + "symfony/finder": "^2.7 || ^3.0 || ^4.0 || ^5.0", + "symfony/process": "^2.7 || ^3.0 || ^4.0 || ^5.0" }, "conflict": { "symfony/console": "2.8.38" }, "require-dev": { - "phpunit/phpunit": "^4.8.35 || ^5.7", - "phpunit/phpunit-mock-objects": "^2.3 || ^3.0" + "phpspec/prophecy": "^1.10", + "symfony/phpunit-bridge": "^3.4" }, "suggest": { "ext-openssl": "Enabling the openssl extension allows you to access https URLs for repositories and packages", @@ -302,7 +302,7 @@ "type": "library", "extra": { "branch-alias": { - "dev-master": "1.9-dev" + "dev-master": "1.10-dev" } }, "autoload": { @@ -333,28 +333,27 @@ "dependency", "package" ], - "time": "2019-11-01T16:20:17+00:00" + "time": "2020-03-13T19:34:27+00:00" }, { "name": "composer/semver", - "version": "1.5.0", + "version": "1.5.1", "source": { "type": "git", "url": "https://github.com/composer/semver.git", - "reference": "46d9139568ccb8d9e7cdd4539cab7347568a5e2e" + "reference": "c6bea70230ef4dd483e6bbcab6005f682ed3a8de" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/composer/semver/zipball/46d9139568ccb8d9e7cdd4539cab7347568a5e2e", - "reference": "46d9139568ccb8d9e7cdd4539cab7347568a5e2e", + "url": "https://api.github.com/repos/composer/semver/zipball/c6bea70230ef4dd483e6bbcab6005f682ed3a8de", + "reference": "c6bea70230ef4dd483e6bbcab6005f682ed3a8de", "shasum": "" }, "require": { "php": "^5.3.2 || ^7.0" }, "require-dev": { - "phpunit/phpunit": "^4.5 || ^5.0.5", - "phpunit/phpunit-mock-objects": "2.3.0 || ^3.0" + "phpunit/phpunit": "^4.5 || ^5.0.5" }, "type": "library", "extra": { @@ -395,20 +394,20 @@ "validation", "versioning" ], - "time": "2019-03-19T17:25:45+00:00" + "time": "2020-01-13T12:06:48+00:00" }, { "name": "composer/spdx-licenses", - "version": "1.5.2", + "version": "1.5.3", "source": { "type": "git", "url": "https://github.com/composer/spdx-licenses.git", - "reference": "7ac1e6aec371357df067f8a688c3d6974df68fa5" + "reference": "0c3e51e1880ca149682332770e25977c70cf9dae" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/composer/spdx-licenses/zipball/7ac1e6aec371357df067f8a688c3d6974df68fa5", - "reference": "7ac1e6aec371357df067f8a688c3d6974df68fa5", + "url": "https://api.github.com/repos/composer/spdx-licenses/zipball/0c3e51e1880ca149682332770e25977c70cf9dae", + "reference": "0c3e51e1880ca149682332770e25977c70cf9dae", "shasum": "" }, "require": { @@ -455,20 +454,20 @@ "spdx", "validator" ], - "time": "2019-07-29T10:31:59+00:00" + "time": "2020-02-14T07:44:31+00:00" }, { "name": "composer/xdebug-handler", - "version": "1.4.0", + "version": "1.4.1", "source": { "type": "git", "url": "https://github.com/composer/xdebug-handler.git", - "reference": "cbe23383749496fe0f373345208b79568e4bc248" + "reference": "1ab9842d69e64fb3a01be6b656501032d1b78cb7" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/composer/xdebug-handler/zipball/cbe23383749496fe0f373345208b79568e4bc248", - "reference": "cbe23383749496fe0f373345208b79568e4bc248", + "url": "https://api.github.com/repos/composer/xdebug-handler/zipball/1ab9842d69e64fb3a01be6b656501032d1b78cb7", + "reference": "1ab9842d69e64fb3a01be6b656501032d1b78cb7", "shasum": "" }, "require": { @@ -499,7 +498,7 @@ "Xdebug", "performance" ], - "time": "2019-11-06T16:40:04+00:00" + "time": "2020-03-01T12:26:26+00:00" }, { "name": "container-interop/container-interop", @@ -535,33 +534,33 @@ }, { "name": "elasticsearch/elasticsearch", - "version": "v6.7.2", + "version": "v7.6.1", "source": { "type": "git", "url": "https://github.com/elastic/elasticsearch-php.git", - "reference": "9ba89f905ebf699e72dacffa410331c7fecc8255" + "reference": "d4f24bc43c2af60aece3df20eb689d322f9c8acf" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/elastic/elasticsearch-php/zipball/9ba89f905ebf699e72dacffa410331c7fecc8255", - "reference": "9ba89f905ebf699e72dacffa410331c7fecc8255", + "url": "https://api.github.com/repos/elastic/elasticsearch-php/zipball/d4f24bc43c2af60aece3df20eb689d322f9c8acf", + "reference": "d4f24bc43c2af60aece3df20eb689d322f9c8acf", "shasum": "" }, "require": { "ext-json": ">=1.3.7", - "guzzlehttp/ringphp": "~1.0", - "php": "^7.0", + "ezimuel/ringphp": "^1.1.2", + "php": "^7.1", "psr/log": "~1.0" }, "require-dev": { - "cpliakas/git-wrapper": "^1.7 || ^2.1", - "doctrine/inflector": "^1.1", + "cpliakas/git-wrapper": "~2.0", + "doctrine/inflector": "^1.3", "mockery/mockery": "^1.2", - "phpstan/phpstan-shim": "^0.9 || ^0.11", - "phpunit/phpunit": "^5.7 || ^6.5", + "phpstan/phpstan": "^0.12", + "phpunit/phpunit": "^7.5", "squizlabs/php_codesniffer": "^3.4", - "symfony/finder": "^2.8", - "symfony/yaml": "^2.8" + "symfony/finder": "~4.0", + "symfony/yaml": "~4.0" }, "suggest": { "ext-curl": "*", @@ -569,6 +568,9 @@ }, "type": "library", "autoload": { + "files": [ + "src/autoload.php" + ], "psr-4": { "Elasticsearch\\": "src/Elasticsearch/" } @@ -591,20 +593,121 @@ "elasticsearch", "search" ], - "time": "2019-07-19T14:48:24+00:00" + "time": "2020-02-15T00:09:00+00:00" + }, + { + "name": "ezimuel/guzzlestreams", + "version": "3.0.1", + "source": { + "type": "git", + "url": "https://github.com/ezimuel/guzzlestreams.git", + "reference": "abe3791d231167f14eb80d413420d1eab91163a8" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/ezimuel/guzzlestreams/zipball/abe3791d231167f14eb80d413420d1eab91163a8", + "reference": "abe3791d231167f14eb80d413420d1eab91163a8", + "shasum": "" + }, + "require": { + "php": ">=5.4.0" + }, + "require-dev": { + "phpunit/phpunit": "~4.0" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "3.0-dev" + } + }, + "autoload": { + "psr-4": { + "GuzzleHttp\\Stream\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Michael Dowling", + "email": "mtdowling@gmail.com", + "homepage": "https://github.com/mtdowling" + } + ], + "description": "Fork of guzzle/streams (abandoned) to be used with elasticsearch-php", + "homepage": "http://guzzlephp.org/", + "keywords": [ + "Guzzle", + "stream" + ], + "time": "2020-02-14T23:11:50+00:00" + }, + { + "name": "ezimuel/ringphp", + "version": "1.1.2", + "source": { + "type": "git", + "url": "https://github.com/ezimuel/ringphp.git", + "reference": "0b78f89d8e0bb9e380046c31adfa40347e9f663b" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/ezimuel/ringphp/zipball/0b78f89d8e0bb9e380046c31adfa40347e9f663b", + "reference": "0b78f89d8e0bb9e380046c31adfa40347e9f663b", + "shasum": "" + }, + "require": { + "ezimuel/guzzlestreams": "^3.0.1", + "php": ">=5.4.0", + "react/promise": "~2.0" + }, + "require-dev": { + "ext-curl": "*", + "phpunit/phpunit": "~4.0" + }, + "suggest": { + "ext-curl": "Guzzle will use specific adapters if cURL is present" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "1.1-dev" + } + }, + "autoload": { + "psr-4": { + "GuzzleHttp\\Ring\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Michael Dowling", + "email": "mtdowling@gmail.com", + "homepage": "https://github.com/mtdowling" + } + ], + "description": "Fork of guzzle/RingPHP (abandoned) to be used with elasticsearch-php", + "time": "2020-02-14T23:51:21+00:00" }, { "name": "guzzlehttp/guzzle", - "version": "6.4.1", + "version": "6.5.2", "source": { "type": "git", "url": "https://github.com/guzzle/guzzle.git", - "reference": "0895c932405407fd3a7368b6910c09a24d26db11" + "reference": "43ece0e75098b7ecd8d13918293029e555a50f82" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/guzzle/guzzle/zipball/0895c932405407fd3a7368b6910c09a24d26db11", - "reference": "0895c932405407fd3a7368b6910c09a24d26db11", + "url": "https://api.github.com/repos/guzzle/guzzle/zipball/43ece0e75098b7ecd8d13918293029e555a50f82", + "reference": "43ece0e75098b7ecd8d13918293029e555a50f82", "shasum": "" }, "require": { @@ -619,12 +722,13 @@ "psr/log": "^1.1" }, "suggest": { + "ext-intl": "Required for Internationalized Domain Name (IDN) support", "psr/log": "Required for using the Log middleware" }, "type": "library", "extra": { "branch-alias": { - "dev-master": "6.3-dev" + "dev-master": "6.5-dev" } }, "autoload": { @@ -657,7 +761,7 @@ "rest", "web service" ], - "time": "2019-10-23T15:58:00+00:00" + "time": "2019-12-23T11:57:10+00:00" }, { "name": "guzzlehttp/promises", @@ -782,40 +886,39 @@ "time": "2019-07-01T23:21:34+00:00" }, { - "name": "guzzlehttp/ringphp", - "version": "1.1.1", + "name": "justinrainbow/json-schema", + "version": "5.2.9", "source": { "type": "git", - "url": "https://github.com/guzzle/RingPHP.git", - "reference": "5e2a174052995663dd68e6b5ad838afd47dd615b" + "url": "https://github.com/justinrainbow/json-schema.git", + "reference": "44c6787311242a979fa15c704327c20e7221a0e4" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/guzzle/RingPHP/zipball/5e2a174052995663dd68e6b5ad838afd47dd615b", - "reference": "5e2a174052995663dd68e6b5ad838afd47dd615b", + "url": "https://api.github.com/repos/justinrainbow/json-schema/zipball/44c6787311242a979fa15c704327c20e7221a0e4", + "reference": "44c6787311242a979fa15c704327c20e7221a0e4", "shasum": "" }, "require": { - "guzzlehttp/streams": "~3.0", - "php": ">=5.4.0", - "react/promise": "~2.0" + "php": ">=5.3.3" }, "require-dev": { - "ext-curl": "*", - "phpunit/phpunit": "~4.0" - }, - "suggest": { - "ext-curl": "Guzzle will use specific adapters if cURL is present" + "friendsofphp/php-cs-fixer": "~2.2.20||~2.15.1", + "json-schema/json-schema-test-suite": "1.2.0", + "phpunit/phpunit": "^4.8.35" }, + "bin": [ + "bin/validate-json" + ], "type": "library", "extra": { "branch-alias": { - "dev-master": "1.1-dev" + "dev-master": "5.0.x-dev" } }, "autoload": { "psr-4": { - "GuzzleHttp\\Ring\\": "src/" + "JsonSchema\\": "src/JsonSchema/" } }, "notification-url": "https://packagist.org/downloads/", @@ -824,4060 +927,4334 @@ ], "authors": [ { - "name": "Michael Dowling", - "email": "mtdowling@gmail.com", - "homepage": "https://github.com/mtdowling" + "name": "Bruno Prieto Reis", + "email": "bruno.p.reis@gmail.com" + }, + { + "name": "Justin Rainbow", + "email": "justin.rainbow@gmail.com" + }, + { + "name": "Igor Wiedler", + "email": "igor@wiedler.ch" + }, + { + "name": "Robert Schönthal", + "email": "seroscho@googlemail.com" } ], - "description": "Provides a simple API and specification that abstracts away the details of HTTP into a single PHP function.", - "time": "2018-07-31T13:22:33+00:00" + "description": "A library to validate a json schema.", + "homepage": "https://github.com/justinrainbow/json-schema", + "keywords": [ + "json", + "schema" + ], + "time": "2019-09-25T14:49:45+00:00" }, { - "name": "guzzlehttp/streams", - "version": "3.0.0", + "name": "laminas/laminas-captcha", + "version": "2.9.0", "source": { "type": "git", - "url": "https://github.com/guzzle/streams.git", - "reference": "47aaa48e27dae43d39fc1cea0ccf0d84ac1a2ba5" + "url": "https://github.com/laminas/laminas-captcha.git", + "reference": "b88f650f3adf2d902ef56f6377cceb5cd87b9876" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/guzzle/streams/zipball/47aaa48e27dae43d39fc1cea0ccf0d84ac1a2ba5", - "reference": "47aaa48e27dae43d39fc1cea0ccf0d84ac1a2ba5", + "url": "https://api.github.com/repos/laminas/laminas-captcha/zipball/b88f650f3adf2d902ef56f6377cceb5cd87b9876", + "reference": "b88f650f3adf2d902ef56f6377cceb5cd87b9876", "shasum": "" }, "require": { - "php": ">=5.4.0" + "laminas/laminas-math": "^2.7 || ^3.0", + "laminas/laminas-stdlib": "^3.2.1", + "laminas/laminas-zendframework-bridge": "^1.0", + "php": "^5.6 || ^7.0" + }, + "replace": { + "zendframework/zend-captcha": "self.version" }, "require-dev": { - "phpunit/phpunit": "~4.0" + "laminas/laminas-coding-standard": "~1.0.0", + "laminas/laminas-recaptcha": "^3.0", + "laminas/laminas-session": "^2.8", + "laminas/laminas-text": "^2.6", + "laminas/laminas-validator": "^2.10.1", + "phpunit/phpunit": "^5.7.27 || ^6.5.8 || ^7.1.2" + }, + "suggest": { + "laminas/laminas-i18n-resources": "Translations of captcha messages", + "laminas/laminas-recaptcha": "Laminas\\ReCaptcha component", + "laminas/laminas-session": "Laminas\\Session component", + "laminas/laminas-text": "Laminas\\Text component", + "laminas/laminas-validator": "Laminas\\Validator component" }, "type": "library", "extra": { "branch-alias": { - "dev-master": "3.0-dev" + "dev-master": "2.9.x-dev", + "dev-develop": "2.10.x-dev" } }, "autoload": { "psr-4": { - "GuzzleHttp\\Stream\\": "src/" + "Laminas\\Captcha\\": "src/" } }, "notification-url": "https://packagist.org/downloads/", "license": [ - "MIT" - ], - "authors": [ - { - "name": "Michael Dowling", - "email": "mtdowling@gmail.com", - "homepage": "https://github.com/mtdowling" - } + "BSD-3-Clause" ], - "description": "Provides a simple abstraction over streams of data", - "homepage": "http://guzzlephp.org/", + "description": "Generate and validate CAPTCHAs using Figlets, images, ReCaptcha, and more", + "homepage": "https://laminas.dev", "keywords": [ - "Guzzle", - "stream" + "captcha", + "laminas" ], - "time": "2014-10-12T19:18:40+00:00" + "time": "2019-12-31T16:24:14+00:00" }, { - "name": "justinrainbow/json-schema", - "version": "5.2.9", + "name": "laminas/laminas-code", + "version": "3.3.2", "source": { "type": "git", - "url": "https://github.com/justinrainbow/json-schema.git", - "reference": "44c6787311242a979fa15c704327c20e7221a0e4" + "url": "https://github.com/laminas/laminas-code.git", + "reference": "128784abc7a0d9e1fcc30c446533aa6f1db1f999" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/justinrainbow/json-schema/zipball/44c6787311242a979fa15c704327c20e7221a0e4", - "reference": "44c6787311242a979fa15c704327c20e7221a0e4", + "url": "https://api.github.com/repos/laminas/laminas-code/zipball/128784abc7a0d9e1fcc30c446533aa6f1db1f999", + "reference": "128784abc7a0d9e1fcc30c446533aa6f1db1f999", "shasum": "" }, "require": { - "php": ">=5.3.3" + "laminas/laminas-eventmanager": "^2.6 || ^3.0", + "laminas/laminas-zendframework-bridge": "^1.0", + "php": "^7.1" + }, + "replace": { + "zendframework/zend-code": "self.version" }, "require-dev": { - "friendsofphp/php-cs-fixer": "~2.2.20||~2.15.1", - "json-schema/json-schema-test-suite": "1.2.0", - "phpunit/phpunit": "^4.8.35" + "doctrine/annotations": "^1.0", + "ext-phar": "*", + "laminas/laminas-coding-standard": "^1.0", + "laminas/laminas-stdlib": "^2.7 || ^3.0", + "phpunit/phpunit": "^7.5.15" + }, + "suggest": { + "doctrine/annotations": "Doctrine\\Common\\Annotations >=1.0 for annotation features", + "laminas/laminas-stdlib": "Laminas\\Stdlib component" }, - "bin": [ - "bin/validate-json" - ], "type": "library", "extra": { "branch-alias": { - "dev-master": "5.0.x-dev" + "dev-master": "3.3.x-dev", + "dev-develop": "3.4.x-dev" } }, "autoload": { "psr-4": { - "JsonSchema\\": "src/JsonSchema/" + "Laminas\\Code\\": "src/" } }, "notification-url": "https://packagist.org/downloads/", "license": [ - "MIT" - ], - "authors": [ - { - "name": "Bruno Prieto Reis", - "email": "bruno.p.reis@gmail.com" - }, - { - "name": "Justin Rainbow", - "email": "justin.rainbow@gmail.com" - }, - { - "name": "Igor Wiedler", - "email": "igor@wiedler.ch" - }, - { - "name": "Robert Schönthal", - "email": "seroscho@googlemail.com" - } + "BSD-3-Clause" ], - "description": "A library to validate a json schema.", - "homepage": "https://github.com/justinrainbow/json-schema", + "description": "Extensions to the PHP Reflection API, static code scanning, and code generation", + "homepage": "https://laminas.dev", "keywords": [ - "json", - "schema" + "code", + "laminas" ], - "time": "2019-09-25T14:49:45+00:00" + "time": "2019-12-31T16:28:14+00:00" }, { - "name": "magento/composer", - "version": "1.5.0", + "name": "laminas/laminas-config", + "version": "2.6.0", "source": { "type": "git", - "url": "https://github.com/magento/composer.git", - "reference": "ea12b95be5c0833b3d9497aaefa08816c19e5dcd" + "url": "https://github.com/laminas/laminas-config.git", + "reference": "71ba6d5dd703196ce66b25abc4d772edb094dae1" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/magento/composer/zipball/ea12b95be5c0833b3d9497aaefa08816c19e5dcd", - "reference": "ea12b95be5c0833b3d9497aaefa08816c19e5dcd", + "url": "https://api.github.com/repos/laminas/laminas-config/zipball/71ba6d5dd703196ce66b25abc4d772edb094dae1", + "reference": "71ba6d5dd703196ce66b25abc4d772edb094dae1", "shasum": "" }, "require": { - "composer/composer": "^1.6", - "php": "~7.1.3||~7.2.0||~7.3.0", - "symfony/console": "~4.0.0 || ~4.1.0" + "laminas/laminas-stdlib": "^2.7 || ^3.0", + "laminas/laminas-zendframework-bridge": "^1.0", + "php": "^5.5 || ^7.0" + }, + "replace": { + "zendframework/zend-config": "self.version" }, "require-dev": { - "phpunit/phpunit": "~7.0.0" + "fabpot/php-cs-fixer": "1.7.*", + "laminas/laminas-filter": "^2.6", + "laminas/laminas-i18n": "^2.5", + "laminas/laminas-json": "^2.6.1", + "laminas/laminas-servicemanager": "^2.7.5 || ^3.0.3", + "phpunit/phpunit": "~4.0" + }, + "suggest": { + "laminas/laminas-filter": "Laminas\\Filter component", + "laminas/laminas-i18n": "Laminas\\I18n component", + "laminas/laminas-json": "Laminas\\Json to use the Json reader or writer classes", + "laminas/laminas-servicemanager": "Laminas\\ServiceManager for use with the Config Factory to retrieve reader and writer instances" }, "type": "library", + "extra": { + "branch-alias": { + "dev-master": "2.6-dev", + "dev-develop": "2.7-dev" + } + }, "autoload": { "psr-4": { - "Magento\\Composer\\": "src" + "Laminas\\Config\\": "src/" } }, "notification-url": "https://packagist.org/downloads/", "license": [ - "OSL-3.0", - "AFL-3.0" + "BSD-3-Clause" ], - "description": "Magento composer library helps to instantiate Composer application and run composer commands.", - "time": "2019-07-29T19:52:05+00:00" + "description": "provides a nested object property based user interface for accessing this configuration data within application code", + "homepage": "https://laminas.dev", + "keywords": [ + "config", + "laminas" + ], + "time": "2019-12-31T16:30:04+00:00" }, { - "name": "magento/magento-composer-installer", - "version": "0.1.13", + "name": "laminas/laminas-console", + "version": "2.8.0", "source": { "type": "git", - "url": "https://github.com/magento/magento-composer-installer.git", - "reference": "8b6c32f53b4944a5d6656e86344cd0f9784709a1" + "url": "https://github.com/laminas/laminas-console.git", + "reference": "478a6ceac3e31fb38d6314088abda8b239ee23a5" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/magento/magento-composer-installer/zipball/8b6c32f53b4944a5d6656e86344cd0f9784709a1", - "reference": "8b6c32f53b4944a5d6656e86344cd0f9784709a1", + "url": "https://api.github.com/repos/laminas/laminas-console/zipball/478a6ceac3e31fb38d6314088abda8b239ee23a5", + "reference": "478a6ceac3e31fb38d6314088abda8b239ee23a5", "shasum": "" }, "require": { - "composer-plugin-api": "^1.0" + "laminas/laminas-stdlib": "^3.2.1", + "laminas/laminas-zendframework-bridge": "^1.0", + "php": "^5.6 || ^7.0" }, "replace": { - "magento-hackathon/magento-composer-installer": "*" + "zendframework/zend-console": "self.version" }, "require-dev": { - "composer/composer": "*@dev", - "firegento/phpcs": "dev-patch-1", - "mikey179/vfsstream": "*", - "phpunit/phpunit": "*", - "phpunit/phpunit-mock-objects": "dev-master", - "squizlabs/php_codesniffer": "1.4.7", - "symfony/process": "*" + "laminas/laminas-coding-standard": "~1.0.0", + "laminas/laminas-filter": "^2.7.2", + "laminas/laminas-json": "^2.6 || ^3.0", + "laminas/laminas-validator": "^2.10.1", + "phpunit/phpunit": "^5.7.23 || ^6.4.3" }, - "type": "composer-plugin", + "suggest": { + "laminas/laminas-filter": "To support DefaultRouteMatcher usage", + "laminas/laminas-validator": "To support DefaultRouteMatcher usage" + }, + "type": "library", "extra": { - "composer-command-registry": [ - "MagentoHackathon\\Composer\\Magento\\Command\\DeployCommand" - ], - "class": "MagentoHackathon\\Composer\\Magento\\Plugin" + "branch-alias": { + "dev-master": "2.8.x-dev", + "dev-develop": "2.9.x-dev" + } }, "autoload": { - "psr-0": { - "MagentoHackathon\\Composer\\Magento": "src/" + "psr-4": { + "Laminas\\Console\\": "src/" } }, "notification-url": "https://packagist.org/downloads/", "license": [ - "OSL-3.0" - ], - "authors": [ - { - "name": "Vinai Kopp", - "email": "vinai@netzarbeiter.com" - }, - { - "name": "Daniel Fahlke aka Flyingmana", - "email": "flyingmana@googlemail.com" - }, - { - "name": "Jörg Weller", - "email": "weller@flagbit.de" - }, - { - "name": "Karl Spies", - "email": "karl.spies@gmx.net" - }, - { - "name": "Tobias Vogt", - "email": "tobi@webguys.de" - }, - { - "name": "David Fuhr", - "email": "fuhr@flagbit.de" - } + "BSD-3-Clause" ], - "description": "Composer installer for Magento modules", - "homepage": "https://github.com/magento/magento-composer-installer", + "description": "Build console applications using getopt syntax or routing, complete with prompts", + "homepage": "https://laminas.dev", "keywords": [ - "composer-installer", - "magento" + "console", + "laminas" ], - "time": "2017-12-29T16:45:24+00:00" + "time": "2019-12-31T16:31:45+00:00" }, { - "name": "magento/zendframework1", - "version": "1.14.2", + "name": "laminas/laminas-crypt", + "version": "2.6.0", "source": { "type": "git", - "url": "https://github.com/magento/zf1.git", - "reference": "8221062d42a198e431d183bbe672e5e1a2f98c5f" + "url": "https://github.com/laminas/laminas-crypt.git", + "reference": "6f291fe90c84c74d737c9dc9b8f0ad2b55dc0567" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/magento/zf1/zipball/8221062d42a198e431d183bbe672e5e1a2f98c5f", - "reference": "8221062d42a198e431d183bbe672e5e1a2f98c5f", + "url": "https://api.github.com/repos/laminas/laminas-crypt/zipball/6f291fe90c84c74d737c9dc9b8f0ad2b55dc0567", + "reference": "6f291fe90c84c74d737c9dc9b8f0ad2b55dc0567", "shasum": "" }, "require": { - "php": ">=5.2.11" + "container-interop/container-interop": "~1.0", + "laminas/laminas-math": "^2.6", + "laminas/laminas-stdlib": "^2.7 || ^3.0", + "laminas/laminas-zendframework-bridge": "^1.0", + "php": "^5.5 || ^7.0" + }, + "replace": { + "zendframework/zend-crypt": "self.version" }, "require-dev": { - "phpunit/dbunit": "1.3.*", - "phpunit/phpunit": "3.7.*" + "fabpot/php-cs-fixer": "1.7.*", + "phpunit/phpunit": "~4.0" + }, + "suggest": { + "ext-mcrypt": "Required for most features of Laminas\\Crypt" }, "type": "library", "extra": { "branch-alias": { - "dev-master": "1.12.x-dev" + "dev-master": "2.6-dev", + "dev-develop": "2.7-dev" } }, "autoload": { - "psr-0": { - "Zend_": "library/" + "psr-4": { + "Laminas\\Crypt\\": "src/" } }, "notification-url": "https://packagist.org/downloads/", - "include-path": [ - "library/" - ], "license": [ "BSD-3-Clause" ], - "description": "Magento Zend Framework 1", - "homepage": "http://framework.zend.com/", + "homepage": "https://laminas.dev", "keywords": [ - "ZF1", - "framework" + "crypt", + "laminas" ], - "time": "2019-07-26T16:43:11+00:00" + "time": "2019-12-31T16:33:11+00:00" }, { - "name": "monolog/monolog", - "version": "1.25.2", + "name": "laminas/laminas-db", + "version": "2.11.2", "source": { "type": "git", - "url": "https://github.com/Seldaek/monolog.git", - "reference": "d5e2fb341cb44f7e2ab639d12a1e5901091ec287" + "url": "https://github.com/laminas/laminas-db.git", + "reference": "76f9527da996c2fef32ef1f3a939e18ca5e9d962" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/Seldaek/monolog/zipball/d5e2fb341cb44f7e2ab639d12a1e5901091ec287", - "reference": "d5e2fb341cb44f7e2ab639d12a1e5901091ec287", + "url": "https://api.github.com/repos/laminas/laminas-db/zipball/76f9527da996c2fef32ef1f3a939e18ca5e9d962", + "reference": "76f9527da996c2fef32ef1f3a939e18ca5e9d962", "shasum": "" }, "require": { - "php": ">=5.3.0", - "psr/log": "~1.0" + "laminas/laminas-stdlib": "^2.7 || ^3.0", + "laminas/laminas-zendframework-bridge": "^1.0", + "php": "^5.6 || ^7.0" }, - "provide": { - "psr/log-implementation": "1.0.0" + "replace": { + "zendframework/zend-db": "self.version" }, "require-dev": { - "aws/aws-sdk-php": "^2.4.9 || ^3.0", - "doctrine/couchdb": "~1.0@dev", - "graylog2/gelf-php": "~1.0", - "jakub-onderka/php-parallel-lint": "0.9", - "php-amqplib/php-amqplib": "~2.4", - "php-console/php-console": "^3.1.3", - "phpunit/phpunit": "~4.5", - "phpunit/phpunit-mock-objects": "2.3.0", - "ruflin/elastica": ">=0.90 <3.0", - "sentry/sentry": "^0.13", - "swiftmailer/swiftmailer": "^5.3|^6.0" + "laminas/laminas-coding-standard": "~1.0.0", + "laminas/laminas-eventmanager": "^2.6.2 || ^3.0", + "laminas/laminas-hydrator": "^1.1 || ^2.1 || ^3.0", + "laminas/laminas-servicemanager": "^2.7.5 || ^3.0.3", + "phpunit/phpunit": "^5.7.27 || ^6.5.14" }, "suggest": { - "aws/aws-sdk-php": "Allow sending log messages to AWS services like DynamoDB", - "doctrine/couchdb": "Allow sending log messages to a CouchDB server", - "ext-amqp": "Allow sending log messages to an AMQP server (1.0+ required)", - "ext-mongo": "Allow sending log messages to a MongoDB server", - "graylog2/gelf-php": "Allow sending log messages to a GrayLog2 server", - "mongodb/mongodb": "Allow sending log messages to a MongoDB server via PHP Driver", - "php-amqplib/php-amqplib": "Allow sending log messages to an AMQP server using php-amqplib", - "php-console/php-console": "Allow sending log messages to Google Chrome", - "rollbar/rollbar": "Allow sending log messages to Rollbar", - "ruflin/elastica": "Allow sending log messages to an Elastic Search server", - "sentry/sentry": "Allow sending log messages to a Sentry server" + "laminas/laminas-eventmanager": "Laminas\\EventManager component", + "laminas/laminas-hydrator": "Laminas\\Hydrator component for using HydratingResultSets", + "laminas/laminas-servicemanager": "Laminas\\ServiceManager component" }, "type": "library", "extra": { "branch-alias": { - "dev-master": "2.0.x-dev" + "dev-master": "2.11.x-dev", + "dev-develop": "2.12.x-dev" + }, + "laminas": { + "component": "Laminas\\Db", + "config-provider": "Laminas\\Db\\ConfigProvider" } }, "autoload": { "psr-4": { - "Monolog\\": "src/Monolog" + "Laminas\\Db\\": "src/" } }, "notification-url": "https://packagist.org/downloads/", "license": [ - "MIT" - ], - "authors": [ - { - "name": "Jordi Boggiano", - "email": "j.boggiano@seld.be", - "homepage": "http://seld.be" - } + "BSD-3-Clause" ], - "description": "Sends your logs to files, sockets, inboxes, databases and various web services", - "homepage": "http://github.com/Seldaek/monolog", + "description": "Database abstraction layer, SQL abstraction, result set abstraction, and RowDataGateway and TableDataGateway implementations", + "homepage": "https://laminas.dev", "keywords": [ - "log", - "logging", - "psr-3" + "db", + "laminas" ], - "time": "2019-11-13T10:00:05+00:00" + "time": "2020-01-14T13:07:26+00:00" }, { - "name": "paragonie/random_compat", - "version": "v9.99.99", + "name": "laminas/laminas-dependency-plugin", + "version": "1.0.3", "source": { "type": "git", - "url": "https://github.com/paragonie/random_compat.git", - "reference": "84b4dfb120c6f9b4ff7b3685f9b8f1aa365a0c95" + "url": "https://github.com/laminas/laminas-dependency-plugin.git", + "reference": "f269716dc584cd7b69e7f6e8ac1092d645ab56d5" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/paragonie/random_compat/zipball/84b4dfb120c6f9b4ff7b3685f9b8f1aa365a0c95", - "reference": "84b4dfb120c6f9b4ff7b3685f9b8f1aa365a0c95", + "url": "https://api.github.com/repos/laminas/laminas-dependency-plugin/zipball/f269716dc584cd7b69e7f6e8ac1092d645ab56d5", + "reference": "f269716dc584cd7b69e7f6e8ac1092d645ab56d5", "shasum": "" }, "require": { - "php": "^7" + "composer-plugin-api": "^1.1", + "php": "^5.6 || ^7.0" }, "require-dev": { - "phpunit/phpunit": "4.*|5.*", - "vimeo/psalm": "^1" + "composer/composer": "^1.9", + "dealerdirect/phpcodesniffer-composer-installer": "^0.5.0", + "phpcompatibility/php-compatibility": "^9.3", + "phpunit/phpunit": "^8.4", + "roave/security-advisories": "dev-master", + "webimpress/coding-standard": "^1.0" }, - "suggest": { - "ext-libsodium": "Provides a modern crypto API that can be used to generate random bytes." + "type": "composer-plugin", + "extra": { + "branch-alias": { + "dev-master": "1.0.x-dev", + "dev-develop": "1.1.x-dev" + }, + "class": "Laminas\\DependencyPlugin\\DependencyRewriterPlugin" + }, + "autoload": { + "psr-4": { + "Laminas\\DependencyPlugin\\": "src/" + } }, - "type": "library", "notification-url": "https://packagist.org/downloads/", "license": [ - "MIT" - ], - "authors": [ - { - "name": "Paragon Initiative Enterprises", - "email": "security@paragonie.com", - "homepage": "https://paragonie.com" - } - ], - "description": "PHP 5.x polyfill for random_bytes() and random_int() from PHP 7", - "keywords": [ - "csprng", - "polyfill", - "pseudorandom", - "random" + "BSD-3-Clause" ], - "time": "2018-07-02T15:55:56+00:00" + "description": "Replace zendframework and zfcampus packages with their Laminas Project equivalents.", + "time": "2020-01-14T19:36:52+00:00" }, { - "name": "paragonie/sodium_compat", - "version": "v1.12.1", + "name": "laminas/laminas-di", + "version": "2.6.1", "source": { "type": "git", - "url": "https://github.com/paragonie/sodium_compat.git", - "reference": "063cae9b3a7323579063e7037720f5b52b56c178" + "url": "https://github.com/laminas/laminas-di.git", + "reference": "239b22408a1f8eacda6fc2b838b5065c4cf1d88e" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/paragonie/sodium_compat/zipball/063cae9b3a7323579063e7037720f5b52b56c178", - "reference": "063cae9b3a7323579063e7037720f5b52b56c178", + "url": "https://api.github.com/repos/laminas/laminas-di/zipball/239b22408a1f8eacda6fc2b838b5065c4cf1d88e", + "reference": "239b22408a1f8eacda6fc2b838b5065c4cf1d88e", "shasum": "" }, "require": { - "paragonie/random_compat": ">=1", - "php": "^5.2.4|^5.3|^5.4|^5.5|^5.6|^7|^8" + "container-interop/container-interop": "^1.1", + "laminas/laminas-code": "^2.6 || ^3.0", + "laminas/laminas-stdlib": "^2.7 || ^3.0", + "laminas/laminas-zendframework-bridge": "^0.4.5 || ^1.0", + "php": "^5.5 || ^7.0" }, - "require-dev": { - "phpunit/phpunit": "^3|^4|^5|^6|^7" + "replace": { + "zendframework/zend-di": "self.version" }, - "suggest": { - "ext-libsodium": "PHP < 7.0: Better performance, password hashing (Argon2i), secure memory management (memzero), and better security.", - "ext-sodium": "PHP >= 7.0: Better performance, password hashing (Argon2i), secure memory management (memzero), and better security." + "require-dev": { + "fabpot/php-cs-fixer": "1.7.*", + "phpunit/phpunit": "~4.0" }, "type": "library", + "extra": { + "branch-alias": { + "dev-master": "2.6-dev", + "dev-develop": "2.7-dev" + } + }, "autoload": { - "files": [ - "autoload.php" - ] + "psr-4": { + "Laminas\\Di\\": "src/" + } }, "notification-url": "https://packagist.org/downloads/", "license": [ - "ISC" - ], - "authors": [ - { - "name": "Paragon Initiative Enterprises", - "email": "security@paragonie.com" - }, - { - "name": "Frank Denis", - "email": "jedisct1@pureftpd.org" - } + "BSD-3-Clause" ], - "description": "Pure PHP implementation of libsodium; uses the PHP extension if it exists", + "homepage": "https://laminas.dev", "keywords": [ - "Authentication", - "BLAKE2b", - "ChaCha20", - "ChaCha20-Poly1305", - "Chapoly", - "Curve25519", - "Ed25519", - "EdDSA", - "Edwards-curve Digital Signature Algorithm", - "Elliptic Curve Diffie-Hellman", - "Poly1305", - "Pure-PHP cryptography", - "RFC 7748", - "RFC 8032", - "Salpoly", - "Salsa20", - "X25519", - "XChaCha20-Poly1305", - "XSalsa20-Poly1305", - "Xchacha20", - "Xsalsa20", - "aead", - "cryptography", - "ecdh", - "elliptic curve", - "elliptic curve cryptography", - "encryption", - "libsodium", - "php", - "public-key cryptography", - "secret-key cryptography", - "side-channel resistant" + "di", + "laminas" ], - "time": "2019-11-07T17:07:24+00:00" + "time": "2019-12-31T15:17:33+00:00" }, { - "name": "pelago/emogrifier", - "version": "v2.2.0", + "name": "laminas/laminas-diactoros", + "version": "1.8.7p1", "source": { "type": "git", - "url": "https://github.com/MyIntervals/emogrifier.git", - "reference": "2472bc1c3a2dee8915ecc2256139c6100024332f" + "url": "https://github.com/laminas/laminas-diactoros.git", + "reference": "56a9aca1f89231763d24d2ae13531b97fa5f4029" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/MyIntervals/emogrifier/zipball/2472bc1c3a2dee8915ecc2256139c6100024332f", - "reference": "2472bc1c3a2dee8915ecc2256139c6100024332f", + "url": "https://api.github.com/repos/laminas/laminas-diactoros/zipball/56a9aca1f89231763d24d2ae13531b97fa5f4029", + "reference": "56a9aca1f89231763d24d2ae13531b97fa5f4029", "shasum": "" }, "require": { - "ext-dom": "*", - "ext-libxml": "*", - "php": "^5.5.0 || ~7.0.0 || ~7.1.0 || ~7.2.0 || ~7.3.0", - "symfony/css-selector": "^3.4.0 || ^4.0.0" + "laminas/laminas-zendframework-bridge": "^1.0", + "php": "^5.6 || ^7.0", + "psr/http-message": "^1.0" + }, + "provide": { + "psr/http-message-implementation": "1.0" + }, + "replace": { + "zendframework/zend-diactoros": "self.version" }, "require-dev": { - "friendsofphp/php-cs-fixer": "^2.2.0", - "phpmd/phpmd": "^2.6.0", - "phpunit/phpunit": "^4.8.0", - "squizlabs/php_codesniffer": "^3.3.2" + "ext-dom": "*", + "ext-libxml": "*", + "laminas/laminas-coding-standard": "~1.0", + "php-http/psr7-integration-tests": "dev-master", + "phpunit/phpunit": "^5.7.16 || ^6.0.8 || ^7.2.7" }, "type": "library", "extra": { "branch-alias": { - "dev-master": "3.0.x-dev" + "dev-release-1.8": "1.8.x-dev" } }, "autoload": { + "files": [ + "src/functions/create_uploaded_file.php", + "src/functions/marshal_headers_from_sapi.php", + "src/functions/marshal_method_from_sapi.php", + "src/functions/marshal_protocol_version_from_sapi.php", + "src/functions/marshal_uri_from_sapi.php", + "src/functions/normalize_server.php", + "src/functions/normalize_uploaded_files.php", + "src/functions/parse_cookie_header.php", + "src/functions/create_uploaded_file.legacy.php", + "src/functions/marshal_headers_from_sapi.legacy.php", + "src/functions/marshal_method_from_sapi.legacy.php", + "src/functions/marshal_protocol_version_from_sapi.legacy.php", + "src/functions/marshal_uri_from_sapi.legacy.php", + "src/functions/normalize_server.legacy.php", + "src/functions/normalize_uploaded_files.legacy.php", + "src/functions/parse_cookie_header.legacy.php" + ], "psr-4": { - "Pelago\\": "src/" + "Laminas\\Diactoros\\": "src/" } }, "notification-url": "https://packagist.org/downloads/", "license": [ - "MIT" - ], - "authors": [ - { - "name": "Oliver Klee", - "email": "github@oliverklee.de" - }, - { - "name": "Zoli Szabó", - "email": "zoli.szabo+github@gmail.com" - }, - { - "name": "John Reeve", - "email": "jreeve@pelagodesign.com" - }, - { - "name": "Jake Hotson", - "email": "jake@qzdesign.co.uk" - }, - { - "name": "Cameron Brooks" - }, - { - "name": "Jaime Prado" - } + "BSD-3-Clause" ], - "description": "Converts CSS styles into inline style attributes in your HTML code", - "homepage": "https://www.myintervals.com/emogrifier.php", + "description": "PSR HTTP Message implementations", + "homepage": "https://laminas.dev", "keywords": [ - "css", - "email", - "pre-processing" + "http", + "laminas", + "psr", + "psr-7" ], - "time": "2019-09-04T16:07:59+00:00" + "time": "2020-01-07T19:25:17+00:00" }, { - "name": "php-amqplib/php-amqplib", - "version": "v2.10.1", + "name": "laminas/laminas-escaper", + "version": "2.6.1", "source": { "type": "git", - "url": "https://github.com/php-amqplib/php-amqplib.git", - "reference": "6e2b2501e021e994fb64429e5a78118f83b5c200" + "url": "https://github.com/laminas/laminas-escaper.git", + "reference": "25f2a053eadfa92ddacb609dcbbc39362610da70" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/php-amqplib/php-amqplib/zipball/6e2b2501e021e994fb64429e5a78118f83b5c200", - "reference": "6e2b2501e021e994fb64429e5a78118f83b5c200", + "url": "https://api.github.com/repos/laminas/laminas-escaper/zipball/25f2a053eadfa92ddacb609dcbbc39362610da70", + "reference": "25f2a053eadfa92ddacb609dcbbc39362610da70", "shasum": "" }, "require": { - "ext-bcmath": "*", - "ext-sockets": "*", - "php": ">=5.6" + "laminas/laminas-zendframework-bridge": "^1.0", + "php": "^5.6 || ^7.0" }, "replace": { - "videlalvaro/php-amqplib": "self.version" + "zendframework/zend-escaper": "self.version" }, "require-dev": { - "ext-curl": "*", - "nategood/httpful": "^0.2.20", - "phpunit/phpunit": "^5.7|^6.5|^7.0", - "squizlabs/php_codesniffer": "^2.5" + "laminas/laminas-coding-standard": "~1.0.0", + "phpunit/phpunit": "^5.7.27 || ^6.5.8 || ^7.1.2" }, "type": "library", "extra": { "branch-alias": { - "dev-master": "2.10-dev" + "dev-master": "2.6.x-dev", + "dev-develop": "2.7.x-dev" } }, "autoload": { "psr-4": { - "PhpAmqpLib\\": "PhpAmqpLib/" + "Laminas\\Escaper\\": "src/" } }, "notification-url": "https://packagist.org/downloads/", "license": [ - "LGPL-2.1-or-later" - ], - "authors": [ - { - "name": "Alvaro Videla", - "role": "Original Maintainer" - }, - { - "name": "John Kelly", - "email": "johnmkelly86@gmail.com", - "role": "Maintainer" - }, - { - "name": "Raúl Araya", - "email": "nubeiro@gmail.com", - "role": "Maintainer" - }, - { - "name": "Luke Bakken", - "email": "luke@bakken.io", - "role": "Maintainer" - } + "BSD-3-Clause" ], - "description": "Formerly videlalvaro/php-amqplib. This library is a pure PHP implementation of the AMQP protocol. It's been tested against RabbitMQ.", - "homepage": "https://github.com/php-amqplib/php-amqplib/", + "description": "Securely and safely escape HTML, HTML attributes, JavaScript, CSS, and URLs", + "homepage": "https://laminas.dev", "keywords": [ - "message", - "queue", - "rabbitmq" + "escaper", + "laminas" ], - "time": "2019-10-10T13:23:40+00:00" + "time": "2019-12-31T16:43:30+00:00" }, { - "name": "phpseclib/mcrypt_compat", - "version": "1.0.8", + "name": "laminas/laminas-eventmanager", + "version": "3.2.1", "source": { "type": "git", - "url": "https://github.com/phpseclib/mcrypt_compat.git", - "reference": "f74c7b1897b62f08f268184b8bb98d9d9ab723b0" + "url": "https://github.com/laminas/laminas-eventmanager.git", + "reference": "ce4dc0bdf3b14b7f9815775af9dfee80a63b4748" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/phpseclib/mcrypt_compat/zipball/f74c7b1897b62f08f268184b8bb98d9d9ab723b0", - "reference": "f74c7b1897b62f08f268184b8bb98d9d9ab723b0", + "url": "https://api.github.com/repos/laminas/laminas-eventmanager/zipball/ce4dc0bdf3b14b7f9815775af9dfee80a63b4748", + "reference": "ce4dc0bdf3b14b7f9815775af9dfee80a63b4748", "shasum": "" }, "require": { - "php": ">=5.3.3", - "phpseclib/phpseclib": ">=2.0.11 <3.0.0" + "laminas/laminas-zendframework-bridge": "^1.0", + "php": "^5.6 || ^7.0" + }, + "replace": { + "zendframework/zend-eventmanager": "self.version" }, "require-dev": { - "phpunit/phpunit": "^4.8.35|^5.7|^6.0" + "athletic/athletic": "^0.1", + "container-interop/container-interop": "^1.1.0", + "laminas/laminas-coding-standard": "~1.0.0", + "laminas/laminas-stdlib": "^2.7.3 || ^3.0", + "phpunit/phpunit": "^5.7.27 || ^6.5.8 || ^7.1.2" }, "suggest": { - "ext-openssl": "Will enable faster cryptographic operations" + "container-interop/container-interop": "^1.1.0, to use the lazy listeners feature", + "laminas/laminas-stdlib": "^2.7.3 || ^3.0, to use the FilterChain feature" }, "type": "library", + "extra": { + "branch-alias": { + "dev-master": "3.2-dev", + "dev-develop": "3.3-dev" + } + }, "autoload": { - "files": [ - "lib/mcrypt.php" - ] + "psr-4": { + "Laminas\\EventManager\\": "src/" + } }, "notification-url": "https://packagist.org/downloads/", "license": [ - "MIT" - ], - "authors": [ - { - "name": "Jim Wigginton", - "email": "terrafrost@php.net", - "homepage": "http://phpseclib.sourceforge.net" - } + "BSD-3-Clause" ], - "description": "PHP 7.1 polyfill for the mcrypt extension from PHP <= 7.0", + "description": "Trigger and listen to events within a PHP application", + "homepage": "https://laminas.dev", "keywords": [ - "cryptograpy", - "encryption", - "mcrypt" + "event", + "eventmanager", + "events", + "laminas" ], - "time": "2018-08-22T03:11:43+00:00" + "time": "2019-12-31T16:44:52+00:00" }, { - "name": "phpseclib/phpseclib", - "version": "2.0.23", + "name": "laminas/laminas-feed", + "version": "2.12.1", "source": { "type": "git", - "url": "https://github.com/phpseclib/phpseclib.git", - "reference": "c78eb5058d5bb1a183133c36d4ba5b6675dfa099" + "url": "https://github.com/laminas/laminas-feed.git", + "reference": "c9356994eb80d0f6b46d7e12ba048d450bf0cd72" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/phpseclib/phpseclib/zipball/c78eb5058d5bb1a183133c36d4ba5b6675dfa099", - "reference": "c78eb5058d5bb1a183133c36d4ba5b6675dfa099", + "url": "https://api.github.com/repos/laminas/laminas-feed/zipball/c9356994eb80d0f6b46d7e12ba048d450bf0cd72", + "reference": "c9356994eb80d0f6b46d7e12ba048d450bf0cd72", "shasum": "" }, "require": { - "php": ">=5.3.3" + "ext-dom": "*", + "ext-libxml": "*", + "laminas/laminas-escaper": "^2.5.2", + "laminas/laminas-stdlib": "^3.2.1", + "laminas/laminas-zendframework-bridge": "^1.0", + "php": "^5.6 || ^7.0" + }, + "replace": { + "zendframework/zend-feed": "self.version" }, "require-dev": { - "phing/phing": "~2.7", - "phpunit/phpunit": "^4.8.35|^5.7|^6.0", - "sami/sami": "~2.0", - "squizlabs/php_codesniffer": "~2.0" + "laminas/laminas-cache": "^2.7.2", + "laminas/laminas-coding-standard": "~1.0.0", + "laminas/laminas-db": "^2.8.2", + "laminas/laminas-http": "^2.7", + "laminas/laminas-servicemanager": "^2.7.8 || ^3.3", + "laminas/laminas-validator": "^2.10.1", + "phpunit/phpunit": "^5.7.27 || ^6.5.14 || ^7.5.20", + "psr/http-message": "^1.0.1" }, "suggest": { - "ext-gmp": "Install the GMP (GNU Multiple Precision) extension in order to speed up arbitrary precision integer arithmetic operations.", - "ext-libsodium": "SSH2/SFTP can make use of some algorithms provided by the libsodium-php extension.", - "ext-mcrypt": "Install the Mcrypt extension in order to speed up a few other cryptographic operations.", - "ext-openssl": "Install the OpenSSL extension in order to speed up a wide variety of cryptographic operations." + "laminas/laminas-cache": "Laminas\\Cache component, for optionally caching feeds between requests", + "laminas/laminas-db": "Laminas\\Db component, for use with PubSubHubbub", + "laminas/laminas-http": "Laminas\\Http for PubSubHubbub, and optionally for use with Laminas\\Feed\\Reader", + "laminas/laminas-servicemanager": "Laminas\\ServiceManager component, for easily extending ExtensionManager implementations", + "laminas/laminas-validator": "Laminas\\Validator component, for validating email addresses used in Atom feeds and entries when using the Writer subcomponent", + "psr/http-message": "PSR-7 ^1.0.1, if you wish to use Laminas\\Feed\\Reader\\Http\\Psr7ResponseDecorator" }, "type": "library", + "extra": { + "branch-alias": { + "dev-master": "2.12.x-dev", + "dev-develop": "2.13.x-dev" + } + }, "autoload": { - "files": [ - "phpseclib/bootstrap.php" - ], "psr-4": { - "phpseclib\\": "phpseclib/" + "Laminas\\Feed\\": "src/" } }, "notification-url": "https://packagist.org/downloads/", "license": [ - "MIT" - ], - "authors": [ - { - "name": "Jim Wigginton", - "email": "terrafrost@php.net", - "role": "Lead Developer" - }, - { - "name": "Patrick Monnerat", - "email": "pm@datasphere.ch", - "role": "Developer" - }, - { - "name": "Andreas Fischer", - "email": "bantu@phpbb.com", - "role": "Developer" - }, - { - "name": "Hans-Jürgen Petrich", - "email": "petrich@tronic-media.com", - "role": "Developer" - }, - { - "name": "Graham Campbell", - "email": "graham@alt-three.com", - "role": "Developer" - } + "BSD-3-Clause" ], - "description": "PHP Secure Communications Library - Pure-PHP implementations of RSA, AES, SSH2, SFTP, X.509 etc.", - "homepage": "http://phpseclib.sourceforge.net", + "description": "provides functionality for consuming RSS and Atom feeds", + "homepage": "https://laminas.dev", "keywords": [ - "BigInteger", - "aes", - "asn.1", - "asn1", - "blowfish", - "crypto", - "cryptography", - "encryption", - "rsa", - "security", - "sftp", - "signature", - "signing", - "ssh", - "twofish", - "x.509", - "x509" + "feed", + "laminas" ], - "time": "2019-09-17T03:41:22+00:00" + "time": "2020-03-23T10:40:31+00:00" }, { - "name": "psr/container", - "version": "1.0.0", + "name": "laminas/laminas-filter", + "version": "2.9.3", "source": { "type": "git", - "url": "https://github.com/php-fig/container.git", - "reference": "b7ce3b176482dbbc1245ebf52b181af44c2cf55f" + "url": "https://github.com/laminas/laminas-filter.git", + "reference": "52b5cdbef8902280996e687e7352a648a8e22f31" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/php-fig/container/zipball/b7ce3b176482dbbc1245ebf52b181af44c2cf55f", - "reference": "b7ce3b176482dbbc1245ebf52b181af44c2cf55f", + "url": "https://api.github.com/repos/laminas/laminas-filter/zipball/52b5cdbef8902280996e687e7352a648a8e22f31", + "reference": "52b5cdbef8902280996e687e7352a648a8e22f31", "shasum": "" }, "require": { - "php": ">=5.3.0" + "laminas/laminas-stdlib": "^2.7.7 || ^3.1", + "laminas/laminas-zendframework-bridge": "^1.0", + "php": "^5.6 || ^7.0" + }, + "conflict": { + "laminas/laminas-validator": "<2.10.1" + }, + "replace": { + "zendframework/zend-filter": "self.version" + }, + "require-dev": { + "laminas/laminas-coding-standard": "~1.0.0", + "laminas/laminas-crypt": "^3.2.1", + "laminas/laminas-servicemanager": "^2.7.8 || ^3.3", + "laminas/laminas-uri": "^2.6", + "pear/archive_tar": "^1.4.3", + "phpunit/phpunit": "^5.7.23 || ^6.4.3", + "psr/http-factory": "^1.0" + }, + "suggest": { + "laminas/laminas-crypt": "Laminas\\Crypt component, for encryption filters", + "laminas/laminas-i18n": "Laminas\\I18n component for filters depending on i18n functionality", + "laminas/laminas-servicemanager": "Laminas\\ServiceManager component, for using the filter chain functionality", + "laminas/laminas-uri": "Laminas\\Uri component, for the UriNormalize filter", + "psr/http-factory-implementation": "psr/http-factory-implementation, for creating file upload instances when consuming PSR-7 in file upload filters" }, "type": "library", "extra": { "branch-alias": { - "dev-master": "1.0.x-dev" + "dev-master": "2.9.x-dev", + "dev-develop": "2.10.x-dev" + }, + "laminas": { + "component": "Laminas\\Filter", + "config-provider": "Laminas\\Filter\\ConfigProvider" } }, "autoload": { "psr-4": { - "Psr\\Container\\": "src/" + "Laminas\\Filter\\": "src/" } }, "notification-url": "https://packagist.org/downloads/", "license": [ - "MIT" - ], - "authors": [ - { - "name": "PHP-FIG", - "homepage": "http://www.php-fig.org/" - } + "BSD-3-Clause" ], - "description": "Common Container Interface (PHP FIG PSR-11)", - "homepage": "https://github.com/php-fig/container", + "description": "Programmatically filter and normalize data and files", + "homepage": "https://laminas.dev", "keywords": [ - "PSR-11", - "container", - "container-interface", - "container-interop", - "psr" + "filter", + "laminas" ], - "time": "2017-02-14T16:28:37+00:00" + "time": "2020-01-07T20:43:53+00:00" }, { - "name": "psr/http-message", - "version": "1.0.1", + "name": "laminas/laminas-form", + "version": "2.14.4", "source": { "type": "git", - "url": "https://github.com/php-fig/http-message.git", - "reference": "f6561bf28d520154e4b0ec72be95418abe6d9363" + "url": "https://github.com/laminas/laminas-form.git", + "reference": "8b985f74bfe32910edb4ba9503877c4310228cd2" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/php-fig/http-message/zipball/f6561bf28d520154e4b0ec72be95418abe6d9363", - "reference": "f6561bf28d520154e4b0ec72be95418abe6d9363", + "url": "https://api.github.com/repos/laminas/laminas-form/zipball/8b985f74bfe32910edb4ba9503877c4310228cd2", + "reference": "8b985f74bfe32910edb4ba9503877c4310228cd2", "shasum": "" }, "require": { - "php": ">=5.3.0" + "laminas/laminas-hydrator": "^1.1 || ^2.1 || ^3.0", + "laminas/laminas-inputfilter": "^2.8", + "laminas/laminas-stdlib": "^3.2.1", + "laminas/laminas-zendframework-bridge": "^1.0", + "php": "^5.6 || ^7.0" + }, + "replace": { + "zendframework/zend-form": "self.version" + }, + "require-dev": { + "doctrine/annotations": "~1.0", + "laminas/laminas-cache": "^2.6.1", + "laminas/laminas-captcha": "^2.7.1", + "laminas/laminas-code": "^2.6 || ^3.0", + "laminas/laminas-coding-standard": "~1.0.0", + "laminas/laminas-escaper": "^2.5", + "laminas/laminas-eventmanager": "^2.6.2 || ^3.0", + "laminas/laminas-filter": "^2.6", + "laminas/laminas-i18n": "^2.6", + "laminas/laminas-recaptcha": "^3.0.0", + "laminas/laminas-servicemanager": "^2.7.5 || ^3.0.3", + "laminas/laminas-session": "^2.8.1", + "laminas/laminas-text": "^2.6", + "laminas/laminas-validator": "^2.6", + "laminas/laminas-view": "^2.6.2", + "phpunit/phpunit": "^5.7.27 || ^6.5.14 || ^7.5.20" + }, + "suggest": { + "laminas/laminas-captcha": "^2.7.1, required for using CAPTCHA form elements", + "laminas/laminas-code": "^2.6 || ^3.0, required to use laminas-form annotations support", + "laminas/laminas-eventmanager": "^2.6.2 || ^3.0, reuired for laminas-form annotations support", + "laminas/laminas-i18n": "^2.6, required when using laminas-form view helpers", + "laminas/laminas-recaptcha": "in order to use the ReCaptcha form element", + "laminas/laminas-servicemanager": "^2.7.5 || ^3.0.3, required to use the form factories or provide services", + "laminas/laminas-view": "^2.6.2, required for using the laminas-form view helpers" }, "type": "library", "extra": { "branch-alias": { - "dev-master": "1.0.x-dev" + "dev-master": "2.14.x-dev", + "dev-develop": "2.15.x-dev" + }, + "laminas": { + "component": "Laminas\\Form", + "config-provider": "Laminas\\Form\\ConfigProvider" } }, "autoload": { "psr-4": { - "Psr\\Http\\Message\\": "src/" - } + "Laminas\\Form\\": "src/" + }, + "files": [ + "autoload/formElementManagerPolyfill.php" + ] }, "notification-url": "https://packagist.org/downloads/", "license": [ - "MIT" - ], - "authors": [ - { - "name": "PHP-FIG", - "homepage": "http://www.php-fig.org/" - } + "BSD-3-Clause" ], - "description": "Common interface for HTTP messages", - "homepage": "https://github.com/php-fig/http-message", + "description": "Validate and display simple and complex forms, casting forms to business objects and vice versa", + "homepage": "https://laminas.dev", "keywords": [ - "http", - "http-message", - "psr", - "psr-7", - "request", - "response" + "form", + "laminas" ], - "time": "2016-08-06T14:39:51+00:00" + "time": "2020-03-18T22:38:54+00:00" }, { - "name": "psr/log", - "version": "1.1.2", + "name": "laminas/laminas-http", + "version": "2.11.2", "source": { "type": "git", - "url": "https://github.com/php-fig/log.git", - "reference": "446d54b4cb6bf489fc9d75f55843658e6f25d801" + "url": "https://github.com/laminas/laminas-http.git", + "reference": "8c66963b933c80da59433da56a44dfa979f3ec88" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/php-fig/log/zipball/446d54b4cb6bf489fc9d75f55843658e6f25d801", - "reference": "446d54b4cb6bf489fc9d75f55843658e6f25d801", + "url": "https://api.github.com/repos/laminas/laminas-http/zipball/8c66963b933c80da59433da56a44dfa979f3ec88", + "reference": "8c66963b933c80da59433da56a44dfa979f3ec88", "shasum": "" }, "require": { - "php": ">=5.3.0" + "laminas/laminas-loader": "^2.5.1", + "laminas/laminas-stdlib": "^3.2.1", + "laminas/laminas-uri": "^2.5.2", + "laminas/laminas-validator": "^2.10.1", + "laminas/laminas-zendframework-bridge": "^1.0", + "php": "^5.6 || ^7.0" + }, + "replace": { + "zendframework/zend-http": "self.version" + }, + "require-dev": { + "laminas/laminas-coding-standard": "~1.0.0", + "laminas/laminas-config": "^3.1 || ^2.6", + "phpunit/phpunit": "^5.7.27 || ^6.5.8 || ^7.1.3" + }, + "suggest": { + "paragonie/certainty": "For automated management of cacert.pem" }, "type": "library", "extra": { "branch-alias": { - "dev-master": "1.1.x-dev" + "dev-master": "2.11.x-dev", + "dev-develop": "2.12.x-dev" } }, "autoload": { "psr-4": { - "Psr\\Log\\": "Psr/Log/" + "Laminas\\Http\\": "src/" } }, "notification-url": "https://packagist.org/downloads/", "license": [ - "MIT" - ], - "authors": [ - { - "name": "PHP-FIG", - "homepage": "http://www.php-fig.org/" - } + "BSD-3-Clause" ], - "description": "Common interface for logging libraries", - "homepage": "https://github.com/php-fig/log", + "description": "Provides an easy interface for performing Hyper-Text Transfer Protocol (HTTP) requests", + "homepage": "https://laminas.dev", "keywords": [ - "log", - "psr", - "psr-3" + "http", + "http client", + "laminas" ], - "time": "2019-11-01T11:05:21+00:00" + "time": "2019-12-31T17:02:36+00:00" }, { - "name": "ralouphie/getallheaders", - "version": "3.0.3", + "name": "laminas/laminas-hydrator", + "version": "2.4.2", "source": { "type": "git", - "url": "https://github.com/ralouphie/getallheaders.git", - "reference": "120b605dfeb996808c31b6477290a714d356e822" + "url": "https://github.com/laminas/laminas-hydrator.git", + "reference": "4a0e81cf05f32edcace817f1f48cb4055f689d85" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/ralouphie/getallheaders/zipball/120b605dfeb996808c31b6477290a714d356e822", - "reference": "120b605dfeb996808c31b6477290a714d356e822", + "url": "https://api.github.com/repos/laminas/laminas-hydrator/zipball/4a0e81cf05f32edcace817f1f48cb4055f689d85", + "reference": "4a0e81cf05f32edcace817f1f48cb4055f689d85", "shasum": "" }, "require": { - "php": ">=5.6" + "laminas/laminas-stdlib": "^3.0", + "laminas/laminas-zendframework-bridge": "^1.0", + "php": "^5.6 || ^7.0" + }, + "replace": { + "zendframework/zend-hydrator": "self.version" }, "require-dev": { - "php-coveralls/php-coveralls": "^2.1", - "phpunit/phpunit": "^5 || ^6.5" + "laminas/laminas-coding-standard": "~1.0.0", + "laminas/laminas-eventmanager": "^2.6.2 || ^3.0", + "laminas/laminas-filter": "^2.6", + "laminas/laminas-inputfilter": "^2.6", + "laminas/laminas-serializer": "^2.6.1", + "laminas/laminas-servicemanager": "^2.7.5 || ^3.0.3", + "phpunit/phpunit": "^5.7.27 || ^6.5.8 || ^7.1.2" + }, + "suggest": { + "laminas/laminas-eventmanager": "^2.6.2 || ^3.0, to support aggregate hydrator usage", + "laminas/laminas-filter": "^2.6, to support naming strategy hydrator usage", + "laminas/laminas-serializer": "^2.6.1, to use the SerializableStrategy", + "laminas/laminas-servicemanager": "^2.7.5 || ^3.0.3, to support hydrator plugin manager usage" }, "type": "library", + "extra": { + "branch-alias": { + "dev-release-2.4": "2.4.x-dev" + }, + "laminas": { + "component": "Laminas\\Hydrator", + "config-provider": "Laminas\\Hydrator\\ConfigProvider" + } + }, "autoload": { - "files": [ - "src/getallheaders.php" - ] + "psr-4": { + "Laminas\\Hydrator\\": "src/" + } }, "notification-url": "https://packagist.org/downloads/", "license": [ - "MIT" + "BSD-3-Clause" ], - "authors": [ - { - "name": "Ralph Khattar", - "email": "ralph.khattar@gmail.com" - } + "description": "Serialize objects to arrays, and vice versa", + "homepage": "https://laminas.dev", + "keywords": [ + "hydrator", + "laminas" ], - "description": "A polyfill for getallheaders.", - "time": "2019-03-08T08:55:37+00:00" + "time": "2019-12-31T17:06:38+00:00" }, { - "name": "ramsey/uuid", - "version": "3.8.0", + "name": "laminas/laminas-i18n", + "version": "2.10.2", "source": { "type": "git", - "url": "https://github.com/ramsey/uuid.git", - "reference": "d09ea80159c1929d75b3f9c60504d613aeb4a1e3" + "url": "https://github.com/laminas/laminas-i18n.git", + "reference": "9699c98d97d2f519def3da8d4128dfe6e6ad11bf" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/ramsey/uuid/zipball/d09ea80159c1929d75b3f9c60504d613aeb4a1e3", - "reference": "d09ea80159c1929d75b3f9c60504d613aeb4a1e3", + "url": "https://api.github.com/repos/laminas/laminas-i18n/zipball/9699c98d97d2f519def3da8d4128dfe6e6ad11bf", + "reference": "9699c98d97d2f519def3da8d4128dfe6e6ad11bf", "shasum": "" }, "require": { - "paragonie/random_compat": "^1.0|^2.0|9.99.99", - "php": "^5.4 || ^7.0", - "symfony/polyfill-ctype": "^1.8" + "ext-intl": "*", + "laminas/laminas-stdlib": "^2.7 || ^3.0", + "laminas/laminas-zendframework-bridge": "^1.0", + "php": "^5.6 || ^7.0" + }, + "conflict": { + "phpspec/prophecy": "<1.9.0" }, "replace": { - "rhumsaa/uuid": "self.version" + "zendframework/zend-i18n": "self.version" }, "require-dev": { - "codeception/aspect-mock": "^1.0 | ~2.0.0", - "doctrine/annotations": "~1.2.0", - "goaop/framework": "1.0.0-alpha.2 | ^1.0 | ~2.1.0", - "ircmaxell/random-lib": "^1.1", - "jakub-onderka/php-parallel-lint": "^0.9.0", - "mockery/mockery": "^0.9.9", - "moontoast/math": "^1.1", - "php-mock/php-mock-phpunit": "^0.3|^1.1", - "phpunit/phpunit": "^4.7|^5.0|^6.5", - "squizlabs/php_codesniffer": "^2.3" + "laminas/laminas-cache": "^2.6.1", + "laminas/laminas-coding-standard": "~1.0.0", + "laminas/laminas-config": "^2.6", + "laminas/laminas-eventmanager": "^2.6.2 || ^3.0", + "laminas/laminas-filter": "^2.6.1", + "laminas/laminas-servicemanager": "^2.7.5 || ^3.0.3", + "laminas/laminas-validator": "^2.6", + "laminas/laminas-view": "^2.6.3", + "phpunit/phpunit": "^5.7.27 || ^6.5.14 || ^7.5.16" }, "suggest": { - "ext-ctype": "Provides support for PHP Ctype functions", - "ext-libsodium": "Provides the PECL libsodium extension for use with the SodiumRandomGenerator", - "ext-uuid": "Provides the PECL UUID extension for use with the PeclUuidTimeGenerator and PeclUuidRandomGenerator", - "ircmaxell/random-lib": "Provides RandomLib for use with the RandomLibAdapter", - "moontoast/math": "Provides support for converting UUID to 128-bit integer (in string form).", - "ramsey/uuid-console": "A console application for generating UUIDs with ramsey/uuid", - "ramsey/uuid-doctrine": "Allows the use of Ramsey\\Uuid\\Uuid as Doctrine field type." + "laminas/laminas-cache": "Laminas\\Cache component", + "laminas/laminas-config": "Laminas\\Config component", + "laminas/laminas-eventmanager": "You should install this package to use the events in the translator", + "laminas/laminas-filter": "You should install this package to use the provided filters", + "laminas/laminas-i18n-resources": "Translation resources", + "laminas/laminas-servicemanager": "Laminas\\ServiceManager component", + "laminas/laminas-validator": "You should install this package to use the provided validators", + "laminas/laminas-view": "You should install this package to use the provided view helpers" }, "type": "library", "extra": { "branch-alias": { - "dev-master": "3.x-dev" + "dev-master": "2.10.x-dev", + "dev-develop": "2.11.x-dev" + }, + "laminas": { + "component": "Laminas\\I18n", + "config-provider": "Laminas\\I18n\\ConfigProvider" } }, "autoload": { "psr-4": { - "Ramsey\\Uuid\\": "src/" + "Laminas\\I18n\\": "src/" } }, "notification-url": "https://packagist.org/downloads/", "license": [ - "MIT" - ], - "authors": [ - { - "name": "Marijn Huizendveld", - "email": "marijn.huizendveld@gmail.com" - }, - { - "name": "Thibaud Fabre", - "email": "thibaud@aztech.io" - }, - { - "name": "Ben Ramsey", - "email": "ben@benramsey.com", - "homepage": "https://benramsey.com" - } + "BSD-3-Clause" ], - "description": "Formerly rhumsaa/uuid. A PHP 5.4+ library for generating RFC 4122 version 1, 3, 4, and 5 universally unique identifiers (UUID).", - "homepage": "https://github.com/ramsey/uuid", + "description": "Provide translations for your application, and filter and validate internationalized values", + "homepage": "https://laminas.dev", "keywords": [ - "guid", - "identifier", - "uuid" + "i18n", + "laminas" ], - "time": "2018-07-19T23:38:55+00:00" + "time": "2020-03-20T11:57:14+00:00" }, { - "name": "react/promise", - "version": "v2.7.1", + "name": "laminas/laminas-inputfilter", + "version": "2.10.1", "source": { "type": "git", - "url": "https://github.com/reactphp/promise.git", - "reference": "31ffa96f8d2ed0341a57848cbb84d88b89dd664d" + "url": "git@github.com:laminas/laminas-inputfilter.git", + "reference": "b29ce8f512c966468eee37ea4873ae5fb545d00a" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/reactphp/promise/zipball/31ffa96f8d2ed0341a57848cbb84d88b89dd664d", - "reference": "31ffa96f8d2ed0341a57848cbb84d88b89dd664d", + "url": "https://api.github.com/repos/laminas/laminas-inputfilter/zipball/b29ce8f512c966468eee37ea4873ae5fb545d00a", + "reference": "b29ce8f512c966468eee37ea4873ae5fb545d00a", "shasum": "" }, "require": { - "php": ">=5.4.0" + "laminas/laminas-filter": "^2.9.1", + "laminas/laminas-servicemanager": "^2.7.10 || ^3.3.1", + "laminas/laminas-stdlib": "^2.7 || ^3.0", + "laminas/laminas-validator": "^2.11", + "laminas/laminas-zendframework-bridge": "^1.0", + "php": "^5.6 || ^7.0" + }, + "replace": { + "zendframework/zend-inputfilter": "self.version" }, "require-dev": { - "phpunit/phpunit": "~4.8" + "laminas/laminas-coding-standard": "~1.0.0", + "phpunit/phpunit": "^5.7.27 || ^6.5.14 || ^7.5.15", + "psr/http-message": "^1.0" + }, + "suggest": { + "psr/http-message-implementation": "PSR-7 is required if you wish to validate PSR-7 UploadedFileInterface payloads" }, "type": "library", + "extra": { + "branch-alias": { + "dev-master": "2.10.x-dev", + "dev-develop": "2.11.x-dev" + }, + "laminas": { + "component": "Laminas\\InputFilter", + "config-provider": "Laminas\\InputFilter\\ConfigProvider" + } + }, "autoload": { "psr-4": { - "React\\Promise\\": "src/" - }, - "files": [ - "src/functions_include.php" - ] + "Laminas\\InputFilter\\": "src/" + } }, "notification-url": "https://packagist.org/downloads/", "license": [ - "MIT" - ], - "authors": [ - { - "name": "Jan Sorgalla", - "email": "jsorgalla@gmail.com" - } + "BSD-3-Clause" ], - "description": "A lightweight implementation of CommonJS Promises/A for PHP", + "description": "Normalize and validate input sets from the web, APIs, the CLI, and more, including files", + "homepage": "https://laminas.dev", "keywords": [ - "promise", - "promises" + "inputfilter", + "laminas" ], - "time": "2019-01-07T21:25:54+00:00" + "time": "2019-12-31T17:11:54+00:00" }, { - "name": "seld/jsonlint", - "version": "1.7.2", + "name": "laminas/laminas-json", + "version": "2.6.1", "source": { "type": "git", - "url": "https://github.com/Seldaek/jsonlint.git", - "reference": "e2e5d290e4d2a4f0eb449f510071392e00e10d19" + "url": "https://github.com/laminas/laminas-json.git", + "reference": "db58425b7f0eba44a7539450cc926af80915951a" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/Seldaek/jsonlint/zipball/e2e5d290e4d2a4f0eb449f510071392e00e10d19", - "reference": "e2e5d290e4d2a4f0eb449f510071392e00e10d19", + "url": "https://api.github.com/repos/laminas/laminas-json/zipball/db58425b7f0eba44a7539450cc926af80915951a", + "reference": "db58425b7f0eba44a7539450cc926af80915951a", "shasum": "" }, "require": { - "php": "^5.3 || ^7.0" + "laminas/laminas-zendframework-bridge": "^1.0", + "php": "^5.5 || ^7.0" + }, + "replace": { + "zendframework/zend-json": "self.version" }, "require-dev": { - "phpunit/phpunit": "^4.8.35 || ^5.7 || ^6.0" + "fabpot/php-cs-fixer": "1.7.*", + "laminas/laminas-http": "^2.5.4", + "laminas/laminas-server": "^2.6.1", + "laminas/laminas-stdlib": "^2.5 || ^3.0", + "laminas/laminas-xml": "^1.0.2", + "phpunit/phpunit": "~4.0" + }, + "suggest": { + "laminas/laminas-http": "Laminas\\Http component, required to use Laminas\\Json\\Server", + "laminas/laminas-server": "Laminas\\Server component, required to use Laminas\\Json\\Server", + "laminas/laminas-stdlib": "Laminas\\Stdlib component, for use with caching Laminas\\Json\\Server responses", + "laminas/laminas-xml": "To support Laminas\\Json\\Json::fromXml() usage" }, - "bin": [ - "bin/jsonlint" - ], "type": "library", + "extra": { + "branch-alias": { + "dev-master": "2.6-dev", + "dev-develop": "2.7-dev" + } + }, "autoload": { "psr-4": { - "Seld\\JsonLint\\": "src/Seld/JsonLint/" + "Laminas\\Json\\": "src/" } }, "notification-url": "https://packagist.org/downloads/", "license": [ - "MIT" - ], - "authors": [ - { - "name": "Jordi Boggiano", - "email": "j.boggiano@seld.be", - "homepage": "http://seld.be" - } + "BSD-3-Clause" ], - "description": "JSON Linter", + "description": "provides convenience methods for serializing native PHP to JSON and decoding JSON to native PHP", + "homepage": "https://laminas.dev", "keywords": [ "json", - "linter", - "parser", - "validator" + "laminas" ], - "time": "2019-10-24T14:27:39+00:00" + "time": "2019-12-31T17:15:00+00:00" }, { - "name": "seld/phar-utils", - "version": "1.0.1", + "name": "laminas/laminas-loader", + "version": "2.6.1", "source": { "type": "git", - "url": "https://github.com/Seldaek/phar-utils.git", - "reference": "7009b5139491975ef6486545a39f3e6dad5ac30a" + "url": "https://github.com/laminas/laminas-loader.git", + "reference": "5d01c2c237ae9e68bec262f339947e2ea18979bc" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/Seldaek/phar-utils/zipball/7009b5139491975ef6486545a39f3e6dad5ac30a", - "reference": "7009b5139491975ef6486545a39f3e6dad5ac30a", + "url": "https://api.github.com/repos/laminas/laminas-loader/zipball/5d01c2c237ae9e68bec262f339947e2ea18979bc", + "reference": "5d01c2c237ae9e68bec262f339947e2ea18979bc", "shasum": "" }, "require": { - "php": ">=5.3" + "laminas/laminas-zendframework-bridge": "^1.0", + "php": "^5.6 || ^7.0" + }, + "replace": { + "zendframework/zend-loader": "self.version" + }, + "require-dev": { + "laminas/laminas-coding-standard": "~1.0.0", + "phpunit/phpunit": "^5.7.27 || ^6.5.8 || ^7.1.4" }, "type": "library", "extra": { "branch-alias": { - "dev-master": "1.x-dev" + "dev-master": "2.6.x-dev", + "dev-develop": "2.7.x-dev" } }, "autoload": { "psr-4": { - "Seld\\PharUtils\\": "src/" + "Laminas\\Loader\\": "src/" } }, "notification-url": "https://packagist.org/downloads/", "license": [ - "MIT" - ], - "authors": [ - { - "name": "Jordi Boggiano", - "email": "j.boggiano@seld.be" - } + "BSD-3-Clause" ], - "description": "PHAR file format utilities, for when PHP phars you up", + "description": "Autoloading and plugin loading strategies", + "homepage": "https://laminas.dev", "keywords": [ - "phra" + "laminas", + "loader" ], - "time": "2015-10-13T18:44:15+00:00" + "time": "2019-12-31T17:18:27+00:00" }, { - "name": "symfony/console", - "version": "v4.1.12", + "name": "laminas/laminas-log", + "version": "2.12.0", "source": { "type": "git", - "url": "https://github.com/symfony/console.git", - "reference": "9e87c798f67dc9fceeb4f3d57847b52d945d1a02" + "url": "https://github.com/laminas/laminas-log.git", + "reference": "4e92d841b48868714a070b10866e94be80fc92ff" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/console/zipball/9e87c798f67dc9fceeb4f3d57847b52d945d1a02", - "reference": "9e87c798f67dc9fceeb4f3d57847b52d945d1a02", + "url": "https://api.github.com/repos/laminas/laminas-log/zipball/4e92d841b48868714a070b10866e94be80fc92ff", + "reference": "4e92d841b48868714a070b10866e94be80fc92ff", "shasum": "" }, "require": { - "php": "^7.1.3", - "symfony/polyfill-mbstring": "~1.0" - }, - "conflict": { - "symfony/dependency-injection": "<3.4", - "symfony/process": "<3.3" + "laminas/laminas-servicemanager": "^2.7.5 || ^3.0.3", + "laminas/laminas-stdlib": "^2.7 || ^3.0", + "laminas/laminas-zendframework-bridge": "^1.0", + "php": "^5.6 || ^7.0", + "psr/log": "^1.1.2" }, "provide": { - "psr/log-implementation": "1.0" + "psr/log-implementation": "1.0.0" + }, + "replace": { + "zendframework/zend-log": "self.version" }, "require-dev": { - "psr/log": "~1.0", - "symfony/config": "~3.4|~4.0", - "symfony/dependency-injection": "~3.4|~4.0", - "symfony/event-dispatcher": "~3.4|~4.0", - "symfony/lock": "~3.4|~4.0", - "symfony/process": "~3.4|~4.0" + "laminas/laminas-coding-standard": "~1.0.0", + "laminas/laminas-db": "^2.6", + "laminas/laminas-escaper": "^2.5", + "laminas/laminas-filter": "^2.5", + "laminas/laminas-mail": "^2.6.1", + "laminas/laminas-validator": "^2.10.1", + "mikey179/vfsstream": "^1.6.7", + "phpunit/phpunit": "^5.7.27 || ^6.5.14 || ^7.5.15" }, "suggest": { - "psr/log": "For using the console logger", - "symfony/event-dispatcher": "", - "symfony/lock": "", - "symfony/process": "" + "ext-mongo": "mongo extension to use Mongo writer", + "ext-mongodb": "mongodb extension to use MongoDB writer", + "laminas/laminas-db": "Laminas\\Db component to use the database log writer", + "laminas/laminas-escaper": "Laminas\\Escaper component, for use in the XML log formatter", + "laminas/laminas-mail": "Laminas\\Mail component to use the email log writer", + "laminas/laminas-validator": "Laminas\\Validator component to block invalid log messages" }, "type": "library", "extra": { "branch-alias": { - "dev-master": "4.1-dev" + "dev-master": "2.12.x-dev", + "dev-develop": "2.13.x-dev" + }, + "laminas": { + "component": "Laminas\\Log", + "config-provider": "Laminas\\Log\\ConfigProvider" } }, "autoload": { "psr-4": { - "Symfony\\Component\\Console\\": "" - }, - "exclude-from-classmap": [ - "/Tests/" - ] + "Laminas\\Log\\": "src/" + } }, "notification-url": "https://packagist.org/downloads/", "license": [ - "MIT" + "BSD-3-Clause" ], - "authors": [ - { - "name": "Fabien Potencier", - "email": "fabien@symfony.com" - }, - { - "name": "Symfony Community", - "homepage": "https://symfony.com/contributors" - } + "description": "Robust, composite logger with filtering, formatting, and PSR-3 support", + "homepage": "https://laminas.dev", + "keywords": [ + "laminas", + "log", + "logging" ], - "description": "Symfony Console Component", - "homepage": "https://symfony.com", - "time": "2019-01-25T14:34:37+00:00" + "time": "2019-12-31T17:18:59+00:00" }, { - "name": "symfony/css-selector", - "version": "v4.3.8", + "name": "laminas/laminas-mail", + "version": "2.10.0", "source": { "type": "git", - "url": "https://github.com/symfony/css-selector.git", - "reference": "f4b3ff6a549d9ed28b2b0ecd1781bf67cf220ee9" + "url": "https://github.com/laminas/laminas-mail.git", + "reference": "019fb670c1dff6be7fc91d3b88942bd0a5f68792" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/css-selector/zipball/f4b3ff6a549d9ed28b2b0ecd1781bf67cf220ee9", - "reference": "f4b3ff6a549d9ed28b2b0ecd1781bf67cf220ee9", + "url": "https://api.github.com/repos/laminas/laminas-mail/zipball/019fb670c1dff6be7fc91d3b88942bd0a5f68792", + "reference": "019fb670c1dff6be7fc91d3b88942bd0a5f68792", "shasum": "" }, "require": { - "php": "^7.1.3" + "ext-iconv": "*", + "laminas/laminas-loader": "^2.5", + "laminas/laminas-mime": "^2.5", + "laminas/laminas-stdlib": "^2.7 || ^3.0", + "laminas/laminas-validator": "^2.10.2", + "laminas/laminas-zendframework-bridge": "^1.0", + "php": "^5.6 || ^7.0", + "true/punycode": "^2.1" + }, + "replace": { + "zendframework/zend-mail": "self.version" + }, + "require-dev": { + "laminas/laminas-coding-standard": "~1.0.0", + "laminas/laminas-config": "^2.6", + "laminas/laminas-crypt": "^2.6 || ^3.0", + "laminas/laminas-servicemanager": "^2.7.10 || ^3.3.1", + "phpunit/phpunit": "^5.7.25 || ^6.4.4 || ^7.1.4" + }, + "suggest": { + "laminas/laminas-crypt": "Crammd5 support in SMTP Auth", + "laminas/laminas-servicemanager": "^2.7.10 || ^3.3.1 when using SMTP to deliver messages" }, "type": "library", "extra": { "branch-alias": { - "dev-master": "4.3-dev" + "dev-master": "2.10.x-dev", + "dev-develop": "2.11.x-dev" + }, + "laminas": { + "component": "Laminas\\Mail", + "config-provider": "Laminas\\Mail\\ConfigProvider" } }, "autoload": { "psr-4": { - "Symfony\\Component\\CssSelector\\": "" - }, - "exclude-from-classmap": [ - "/Tests/" - ] + "Laminas\\Mail\\": "src/" + } }, "notification-url": "https://packagist.org/downloads/", "license": [ - "MIT" + "BSD-3-Clause" ], - "authors": [ - { - "name": "Fabien Potencier", - "email": "fabien@symfony.com" - }, - { - "name": "Jean-François Simon", - "email": "jeanfrancois.simon@sensiolabs.com" - }, - { - "name": "Symfony Community", - "homepage": "https://symfony.com/contributors" - } + "description": "Provides generalized functionality to compose and send both text and MIME-compliant multipart e-mail messages", + "homepage": "https://laminas.dev", + "keywords": [ + "laminas", + "mail" ], - "description": "Symfony CssSelector Component", - "homepage": "https://symfony.com", - "time": "2019-10-02T08:36:26+00:00" + "time": "2019-12-31T17:21:22+00:00" }, { - "name": "symfony/event-dispatcher", - "version": "v4.3.8", + "name": "laminas/laminas-math", + "version": "2.7.1", "source": { "type": "git", - "url": "https://github.com/symfony/event-dispatcher.git", - "reference": "0df002fd4f500392eabd243c2947061a50937287" + "url": "https://github.com/laminas/laminas-math.git", + "reference": "8027b37e00accc43f28605c7d8fd081baed1f475" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/event-dispatcher/zipball/0df002fd4f500392eabd243c2947061a50937287", - "reference": "0df002fd4f500392eabd243c2947061a50937287", + "url": "https://api.github.com/repos/laminas/laminas-math/zipball/8027b37e00accc43f28605c7d8fd081baed1f475", + "reference": "8027b37e00accc43f28605c7d8fd081baed1f475", "shasum": "" }, "require": { - "php": "^7.1.3", - "symfony/event-dispatcher-contracts": "^1.1" - }, - "conflict": { - "symfony/dependency-injection": "<3.4" + "laminas/laminas-zendframework-bridge": "^1.0", + "php": "^5.5 || ^7.0" }, - "provide": { - "psr/event-dispatcher-implementation": "1.0", - "symfony/event-dispatcher-implementation": "1.1" + "replace": { + "zendframework/zend-math": "self.version" }, "require-dev": { - "psr/log": "~1.0", - "symfony/config": "~3.4|~4.0", - "symfony/dependency-injection": "~3.4|~4.0", - "symfony/expression-language": "~3.4|~4.0", - "symfony/http-foundation": "^3.4|^4.0", - "symfony/service-contracts": "^1.1", - "symfony/stopwatch": "~3.4|~4.0" + "fabpot/php-cs-fixer": "1.7.*", + "ircmaxell/random-lib": "~1.1", + "phpunit/phpunit": "~4.0" }, "suggest": { - "symfony/dependency-injection": "", - "symfony/http-kernel": "" + "ext-bcmath": "If using the bcmath functionality", + "ext-gmp": "If using the gmp functionality", + "ircmaxell/random-lib": "Fallback random byte generator for Laminas\\Math\\Rand if Mcrypt extensions is unavailable" }, "type": "library", "extra": { "branch-alias": { - "dev-master": "4.3-dev" + "dev-master": "2.7-dev", + "dev-develop": "2.8-dev" } }, "autoload": { "psr-4": { - "Symfony\\Component\\EventDispatcher\\": "" - }, - "exclude-from-classmap": [ - "/Tests/" - ] + "Laminas\\Math\\": "src/" + } }, "notification-url": "https://packagist.org/downloads/", "license": [ - "MIT" + "BSD-3-Clause" ], - "authors": [ - { - "name": "Fabien Potencier", - "email": "fabien@symfony.com" - }, - { - "name": "Symfony Community", - "homepage": "https://symfony.com/contributors" - } + "homepage": "https://laminas.dev", + "keywords": [ + "laminas", + "math" ], - "description": "Symfony EventDispatcher Component", - "homepage": "https://symfony.com", - "time": "2019-11-03T09:04:05+00:00" + "time": "2019-12-31T17:24:15+00:00" }, { - "name": "symfony/event-dispatcher-contracts", - "version": "v1.1.7", + "name": "laminas/laminas-mime", + "version": "2.7.3", "source": { "type": "git", - "url": "https://github.com/symfony/event-dispatcher-contracts.git", - "reference": "c43ab685673fb6c8d84220c77897b1d6cdbe1d18" + "url": "https://github.com/laminas/laminas-mime.git", + "reference": "e844abb02e868fae154207929190292ad25057cc" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/event-dispatcher-contracts/zipball/c43ab685673fb6c8d84220c77897b1d6cdbe1d18", - "reference": "c43ab685673fb6c8d84220c77897b1d6cdbe1d18", + "url": "https://api.github.com/repos/laminas/laminas-mime/zipball/e844abb02e868fae154207929190292ad25057cc", + "reference": "e844abb02e868fae154207929190292ad25057cc", "shasum": "" }, "require": { - "php": "^7.1.3" + "laminas/laminas-stdlib": "^2.7 || ^3.0", + "laminas/laminas-zendframework-bridge": "^1.0", + "php": "^5.6 || ^7.0" + }, + "replace": { + "zendframework/zend-mime": "self.version" + }, + "require-dev": { + "laminas/laminas-coding-standard": "~1.0.0", + "laminas/laminas-mail": "^2.6", + "phpunit/phpunit": "^5.7.27 || ^6.5.14 || ^7.5.20" }, "suggest": { - "psr/event-dispatcher": "", - "symfony/event-dispatcher-implementation": "" + "laminas/laminas-mail": "Laminas\\Mail component" }, "type": "library", "extra": { "branch-alias": { - "dev-master": "1.1-dev" + "dev-master": "2.7.x-dev", + "dev-develop": "2.8.x-dev" } }, "autoload": { "psr-4": { - "Symfony\\Contracts\\EventDispatcher\\": "" + "Laminas\\Mime\\": "src/" } }, "notification-url": "https://packagist.org/downloads/", "license": [ - "MIT" - ], - "authors": [ - { - "name": "Nicolas Grekas", - "email": "p@tchwork.com" - }, - { - "name": "Symfony Community", - "homepage": "https://symfony.com/contributors" - } + "BSD-3-Clause" ], - "description": "Generic abstractions related to dispatching event", - "homepage": "https://symfony.com", + "description": "Create and parse MIME messages and parts", + "homepage": "https://laminas.dev", "keywords": [ - "abstractions", - "contracts", - "decoupling", - "interfaces", - "interoperability", - "standards" + "laminas", + "mime" ], - "time": "2019-09-17T09:54:03+00:00" + "time": "2020-03-06T08:38:03+00:00" }, { - "name": "symfony/filesystem", - "version": "v4.3.8", + "name": "laminas/laminas-modulemanager", + "version": "2.8.4", "source": { "type": "git", - "url": "https://github.com/symfony/filesystem.git", - "reference": "9abbb7ef96a51f4d7e69627bc6f63307994e4263" + "url": "https://github.com/laminas/laminas-modulemanager.git", + "reference": "92b1cde1aab5aef687b863face6dd5d9c6751c78" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/filesystem/zipball/9abbb7ef96a51f4d7e69627bc6f63307994e4263", - "reference": "9abbb7ef96a51f4d7e69627bc6f63307994e4263", + "url": "https://api.github.com/repos/laminas/laminas-modulemanager/zipball/92b1cde1aab5aef687b863face6dd5d9c6751c78", + "reference": "92b1cde1aab5aef687b863face6dd5d9c6751c78", "shasum": "" }, "require": { - "php": "^7.1.3", - "symfony/polyfill-ctype": "~1.8" + "laminas/laminas-config": "^3.1 || ^2.6", + "laminas/laminas-eventmanager": "^3.2 || ^2.6.3", + "laminas/laminas-stdlib": "^3.1 || ^2.7", + "laminas/laminas-zendframework-bridge": "^1.0", + "php": "^5.6 || ^7.0" + }, + "replace": { + "zendframework/zend-modulemanager": "self.version" + }, + "require-dev": { + "laminas/laminas-coding-standard": "~1.0.0", + "laminas/laminas-console": "^2.6", + "laminas/laminas-di": "^2.6", + "laminas/laminas-loader": "^2.5", + "laminas/laminas-mvc": "^3.0 || ^2.7", + "laminas/laminas-servicemanager": "^3.0.3 || ^2.7.5", + "phpunit/phpunit": "^5.7.27 || ^6.5.14 || ^7.5.16" + }, + "suggest": { + "laminas/laminas-console": "Laminas\\Console component", + "laminas/laminas-loader": "Laminas\\Loader component if you are not using Composer autoloading for your modules", + "laminas/laminas-mvc": "Laminas\\Mvc component", + "laminas/laminas-servicemanager": "Laminas\\ServiceManager component" }, "type": "library", "extra": { "branch-alias": { - "dev-master": "4.3-dev" + "dev-master": "2.8.x-dev", + "dev-develop": "2.9.x-dev" } }, "autoload": { "psr-4": { - "Symfony\\Component\\Filesystem\\": "" - }, - "exclude-from-classmap": [ - "/Tests/" - ] + "Laminas\\ModuleManager\\": "src/" + } }, "notification-url": "https://packagist.org/downloads/", "license": [ - "MIT" + "BSD-3-Clause" ], - "authors": [ - { - "name": "Fabien Potencier", - "email": "fabien@symfony.com" - }, - { - "name": "Symfony Community", - "homepage": "https://symfony.com/contributors" - } + "description": "Modular application system for laminas-mvc applications", + "homepage": "https://laminas.dev", + "keywords": [ + "laminas", + "modulemanager" ], - "description": "Symfony Filesystem Component", - "homepage": "https://symfony.com", - "time": "2019-08-20T14:07:54+00:00" + "time": "2019-12-31T17:26:56+00:00" }, { - "name": "symfony/finder", - "version": "v4.3.8", + "name": "laminas/laminas-mvc", + "version": "2.7.15", "source": { "type": "git", - "url": "https://github.com/symfony/finder.git", - "reference": "72a068f77e317ae77c0a0495236ad292cfb5ce6f" + "url": "https://github.com/laminas/laminas-mvc.git", + "reference": "7e7198b03556a57fb5fd3ed919d9e1cf71500642" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/finder/zipball/72a068f77e317ae77c0a0495236ad292cfb5ce6f", - "reference": "72a068f77e317ae77c0a0495236ad292cfb5ce6f", + "url": "https://api.github.com/repos/laminas/laminas-mvc/zipball/7e7198b03556a57fb5fd3ed919d9e1cf71500642", + "reference": "7e7198b03556a57fb5fd3ed919d9e1cf71500642", "shasum": "" }, "require": { - "php": "^7.1.3" + "container-interop/container-interop": "^1.1", + "laminas/laminas-console": "^2.7", + "laminas/laminas-eventmanager": "^2.6.4 || ^3.0", + "laminas/laminas-form": "^2.11", + "laminas/laminas-hydrator": "^1.1 || ^2.4", + "laminas/laminas-psr7bridge": "^0.2", + "laminas/laminas-servicemanager": "^2.7.10 || ^3.0.3", + "laminas/laminas-stdlib": "^2.7.5 || ^3.0", + "laminas/laminas-zendframework-bridge": "^1.0", + "php": "^5.5 || ^7.0" + }, + "replace": { + "zendframework/zend-mvc": "self.version" + }, + "require-dev": { + "friendsofphp/php-cs-fixer": "1.7.*", + "laminas/laminas-authentication": "^2.6", + "laminas/laminas-cache": "^2.8", + "laminas/laminas-di": "^2.6", + "laminas/laminas-filter": "^2.8", + "laminas/laminas-http": "^2.8", + "laminas/laminas-i18n": "^2.8", + "laminas/laminas-inputfilter": "^2.8", + "laminas/laminas-json": "^2.6.1", + "laminas/laminas-log": "^2.9.3", + "laminas/laminas-modulemanager": "^2.8", + "laminas/laminas-serializer": "^2.8", + "laminas/laminas-session": "^2.8.1", + "laminas/laminas-text": "^2.7", + "laminas/laminas-uri": "^2.6", + "laminas/laminas-validator": "^2.10", + "laminas/laminas-view": "^2.9", + "phpunit/phpunit": "^4.8.36", + "sebastian/comparator": "^1.2.4", + "sebastian/version": "^1.0.4" + }, + "suggest": { + "laminas/laminas-authentication": "Laminas\\Authentication component for Identity plugin", + "laminas/laminas-config": "Laminas\\Config component", + "laminas/laminas-di": "Laminas\\Di component", + "laminas/laminas-filter": "Laminas\\Filter component", + "laminas/laminas-http": "Laminas\\Http component", + "laminas/laminas-i18n": "Laminas\\I18n component for translatable segments", + "laminas/laminas-inputfilter": "Laminas\\Inputfilter component", + "laminas/laminas-json": "Laminas\\Json component", + "laminas/laminas-log": "Laminas\\Log component", + "laminas/laminas-modulemanager": "Laminas\\ModuleManager component", + "laminas/laminas-serializer": "Laminas\\Serializer component", + "laminas/laminas-servicemanager-di": "^1.0.1, if using laminas-servicemanager v3 and requiring the laminas-di integration", + "laminas/laminas-session": "Laminas\\Session component for FlashMessenger, PRG, and FPRG plugins", + "laminas/laminas-text": "Laminas\\Text component", + "laminas/laminas-uri": "Laminas\\Uri component", + "laminas/laminas-validator": "Laminas\\Validator component", + "laminas/laminas-view": "Laminas\\View component" }, "type": "library", "extra": { "branch-alias": { - "dev-master": "4.3-dev" + "dev-master": "2.7-dev", + "dev-develop": "3.0-dev" } }, "autoload": { + "files": [ + "src/autoload.php" + ], "psr-4": { - "Symfony\\Component\\Finder\\": "" - }, - "exclude-from-classmap": [ - "/Tests/" - ] + "Laminas\\Mvc\\": "src/" + } }, "notification-url": "https://packagist.org/downloads/", "license": [ - "MIT" + "BSD-3-Clause" ], - "authors": [ - { - "name": "Fabien Potencier", - "email": "fabien@symfony.com" - }, - { - "name": "Symfony Community", - "homepage": "https://symfony.com/contributors" - } + "homepage": "https://laminas.dev", + "keywords": [ + "laminas", + "mvc" ], - "description": "Symfony Finder Component", - "homepage": "https://symfony.com", - "time": "2019-10-30T12:53:54+00:00" + "time": "2019-12-31T17:32:15+00:00" }, { - "name": "symfony/polyfill-ctype", - "version": "v1.12.0", + "name": "laminas/laminas-psr7bridge", + "version": "0.2.2", "source": { "type": "git", - "url": "https://github.com/symfony/polyfill-ctype.git", - "reference": "550ebaac289296ce228a706d0867afc34687e3f4" + "url": "https://github.com/laminas/laminas-psr7bridge.git", + "reference": "14780ef1d40effd59d77ab29c6d439b2af42cdfa" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/polyfill-ctype/zipball/550ebaac289296ce228a706d0867afc34687e3f4", - "reference": "550ebaac289296ce228a706d0867afc34687e3f4", + "url": "https://api.github.com/repos/laminas/laminas-psr7bridge/zipball/14780ef1d40effd59d77ab29c6d439b2af42cdfa", + "reference": "14780ef1d40effd59d77ab29c6d439b2af42cdfa", "shasum": "" }, "require": { - "php": ">=5.3.3" + "laminas/laminas-diactoros": "^1.1", + "laminas/laminas-http": "^2.5", + "laminas/laminas-zendframework-bridge": "^1.0", + "php": ">=5.5", + "psr/http-message": "^1.0" }, - "suggest": { - "ext-ctype": "For best performance" + "replace": { + "zendframework/zend-psr7bridge": "self.version" + }, + "require-dev": { + "phpunit/phpunit": "^4.7", + "squizlabs/php_codesniffer": "^2.3" }, "type": "library", "extra": { "branch-alias": { - "dev-master": "1.12-dev" + "dev-master": "1.0-dev", + "dev-develop": "1.1-dev" } }, "autoload": { "psr-4": { - "Symfony\\Polyfill\\Ctype\\": "" - }, - "files": [ - "bootstrap.php" - ] + "Laminas\\Psr7Bridge\\": "src/" + } }, "notification-url": "https://packagist.org/downloads/", "license": [ - "MIT" - ], - "authors": [ - { - "name": "Gert de Pagter", - "email": "BackEndTea@gmail.com" - }, - { - "name": "Symfony Community", - "homepage": "https://symfony.com/contributors" - } + "BSD-3-Clause" ], - "description": "Symfony polyfill for ctype functions", - "homepage": "https://symfony.com", + "description": "PSR-7 <-> Laminas\\Http bridge", + "homepage": "https://laminas.dev", "keywords": [ - "compatibility", - "ctype", - "polyfill", - "portable" + "http", + "laminas", + "psr", + "psr-7" ], - "time": "2019-08-06T08:03:45+00:00" + "time": "2019-12-31T17:38:47+00:00" }, { - "name": "symfony/polyfill-mbstring", - "version": "v1.12.0", + "name": "laminas/laminas-serializer", + "version": "2.9.1", "source": { "type": "git", - "url": "https://github.com/symfony/polyfill-mbstring.git", - "reference": "b42a2f66e8f1b15ccf25652c3424265923eb4f17" + "url": "https://github.com/laminas/laminas-serializer.git", + "reference": "c1c9361f114271b0736db74e0083a919081af5e0" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/polyfill-mbstring/zipball/b42a2f66e8f1b15ccf25652c3424265923eb4f17", - "reference": "b42a2f66e8f1b15ccf25652c3424265923eb4f17", + "url": "https://api.github.com/repos/laminas/laminas-serializer/zipball/c1c9361f114271b0736db74e0083a919081af5e0", + "reference": "c1c9361f114271b0736db74e0083a919081af5e0", "shasum": "" }, "require": { - "php": ">=5.3.3" + "laminas/laminas-json": "^2.5 || ^3.0", + "laminas/laminas-stdlib": "^2.7 || ^3.0", + "laminas/laminas-zendframework-bridge": "^1.0", + "php": "^5.6 || ^7.0" + }, + "replace": { + "zendframework/zend-serializer": "self.version" + }, + "require-dev": { + "laminas/laminas-coding-standard": "~1.0.0", + "laminas/laminas-math": "^2.6 || ^3.0", + "laminas/laminas-servicemanager": "^2.7.5 || ^3.0.3", + "phpunit/phpunit": "^5.7.27 || ^6.5.14 || ^7.5.16" }, "suggest": { - "ext-mbstring": "For best performance" + "laminas/laminas-math": "(^2.6 || ^3.0) To support Python Pickle serialization", + "laminas/laminas-servicemanager": "(^2.7.5 || ^3.0.3) To support plugin manager support" }, "type": "library", "extra": { "branch-alias": { - "dev-master": "1.12-dev" + "dev-master": "2.9.x-dev", + "dev-develop": "2.10.x-dev" + }, + "laminas": { + "component": "Laminas\\Serializer", + "config-provider": "Laminas\\Serializer\\ConfigProvider" } }, "autoload": { "psr-4": { - "Symfony\\Polyfill\\Mbstring\\": "" - }, - "files": [ - "bootstrap.php" - ] + "Laminas\\Serializer\\": "src/" + } }, "notification-url": "https://packagist.org/downloads/", "license": [ - "MIT" - ], - "authors": [ - { - "name": "Nicolas Grekas", - "email": "p@tchwork.com" - }, - { - "name": "Symfony Community", - "homepage": "https://symfony.com/contributors" - } + "BSD-3-Clause" ], - "description": "Symfony polyfill for the Mbstring extension", - "homepage": "https://symfony.com", + "description": "Serialize and deserialize PHP structures to a variety of representations", + "homepage": "https://laminas.dev", "keywords": [ - "compatibility", - "mbstring", - "polyfill", - "portable", - "shim" + "laminas", + "serializer" ], - "time": "2019-08-06T08:03:45+00:00" + "time": "2019-12-31T17:42:11+00:00" }, { - "name": "symfony/process", - "version": "v4.3.8", + "name": "laminas/laminas-server", + "version": "2.8.1", "source": { "type": "git", - "url": "https://github.com/symfony/process.git", - "reference": "3b2e0cb029afbb0395034509291f21191d1a4db0" + "url": "https://github.com/laminas/laminas-server.git", + "reference": "4aaca9174c40a2fab2e2aa77999da99f71bdd88e" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/process/zipball/3b2e0cb029afbb0395034509291f21191d1a4db0", - "reference": "3b2e0cb029afbb0395034509291f21191d1a4db0", + "url": "https://api.github.com/repos/laminas/laminas-server/zipball/4aaca9174c40a2fab2e2aa77999da99f71bdd88e", + "reference": "4aaca9174c40a2fab2e2aa77999da99f71bdd88e", "shasum": "" }, "require": { - "php": "^7.1.3" + "laminas/laminas-code": "^2.5 || ^3.0", + "laminas/laminas-stdlib": "^2.5 || ^3.0", + "laminas/laminas-zendframework-bridge": "^1.0", + "php": "^5.6 || ^7.0" + }, + "replace": { + "zendframework/zend-server": "self.version" + }, + "require-dev": { + "laminas/laminas-coding-standard": "~1.0.0", + "phpunit/phpunit": "^5.7.27 || ^6.5.8 || ^7.1.4" }, "type": "library", "extra": { "branch-alias": { - "dev-master": "4.3-dev" + "dev-master": "2.8.x-dev", + "dev-develop": "2.9.x-dev" } }, "autoload": { "psr-4": { - "Symfony\\Component\\Process\\": "" - }, - "exclude-from-classmap": [ - "/Tests/" - ] + "Laminas\\Server\\": "src/" + } }, "notification-url": "https://packagist.org/downloads/", "license": [ - "MIT" + "BSD-3-Clause" ], - "authors": [ - { - "name": "Fabien Potencier", - "email": "fabien@symfony.com" - }, - { - "name": "Symfony Community", - "homepage": "https://symfony.com/contributors" - } + "description": "Create Reflection-based RPC servers", + "homepage": "https://laminas.dev", + "keywords": [ + "laminas", + "server" ], - "description": "Symfony Process Component", - "homepage": "https://symfony.com", - "time": "2019-10-28T17:07:32+00:00" + "time": "2019-12-31T17:43:03+00:00" }, { - "name": "tedivm/jshrink", - "version": "v1.3.3", + "name": "laminas/laminas-servicemanager", + "version": "2.7.11", "source": { "type": "git", - "url": "https://github.com/tedious/JShrink.git", - "reference": "566e0c731ba4e372be2de429ef7d54f4faf4477a" + "url": "https://github.com/laminas/laminas-servicemanager.git", + "reference": "841abb656c6018afebeec1f355be438426d6a3dd" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/tedious/JShrink/zipball/566e0c731ba4e372be2de429ef7d54f4faf4477a", - "reference": "566e0c731ba4e372be2de429ef7d54f4faf4477a", + "url": "https://api.github.com/repos/laminas/laminas-servicemanager/zipball/841abb656c6018afebeec1f355be438426d6a3dd", + "reference": "841abb656c6018afebeec1f355be438426d6a3dd", "shasum": "" }, "require": { - "php": "^5.6|^7.0" + "container-interop/container-interop": "~1.0", + "laminas/laminas-zendframework-bridge": "^1.0", + "php": "^5.5 || ^7.0" + }, + "replace": { + "zendframework/zend-servicemanager": "self.version" }, "require-dev": { - "friendsofphp/php-cs-fixer": "^2.8", - "php-coveralls/php-coveralls": "^1.1.0", - "phpunit/phpunit": "^6" + "athletic/athletic": "dev-master", + "fabpot/php-cs-fixer": "1.7.*", + "laminas/laminas-di": "~2.5", + "laminas/laminas-mvc": "~2.5", + "phpunit/phpunit": "~4.0" + }, + "suggest": { + "laminas/laminas-di": "Laminas\\Di component", + "ocramius/proxy-manager": "ProxyManager 0.5.* to handle lazy initialization of services" }, "type": "library", + "extra": { + "branch-alias": { + "dev-master": "2.7-dev", + "dev-develop": "3.0-dev" + } + }, "autoload": { - "psr-0": { - "JShrink": "src/" + "psr-4": { + "Laminas\\ServiceManager\\": "src/" } }, "notification-url": "https://packagist.org/downloads/", "license": [ "BSD-3-Clause" ], - "authors": [ - { - "name": "Robert Hafner", - "email": "tedivm@tedivm.com" - } - ], - "description": "Javascript Minifier built in PHP", - "homepage": "http://github.com/tedious/JShrink", + "homepage": "https://laminas.dev", "keywords": [ - "javascript", - "minifier" + "laminas", + "servicemanager" ], - "time": "2019-06-28T18:11:46+00:00" + "time": "2019-12-31T17:44:16+00:00" }, { - "name": "true/punycode", - "version": "v2.1.1", + "name": "laminas/laminas-session", + "version": "2.9.2", "source": { "type": "git", - "url": "https://github.com/true/php-punycode.git", - "reference": "a4d0c11a36dd7f4e7cd7096076cab6d3378a071e" + "url": "https://github.com/laminas/laminas-session.git", + "reference": "fdba34c1b257235dba2fff6ed4df1844390f85f6" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/true/php-punycode/zipball/a4d0c11a36dd7f4e7cd7096076cab6d3378a071e", - "reference": "a4d0c11a36dd7f4e7cd7096076cab6d3378a071e", + "url": "https://api.github.com/repos/laminas/laminas-session/zipball/fdba34c1b257235dba2fff6ed4df1844390f85f6", + "reference": "fdba34c1b257235dba2fff6ed4df1844390f85f6", "shasum": "" }, "require": { - "php": ">=5.3.0", - "symfony/polyfill-mbstring": "^1.3" + "laminas/laminas-eventmanager": "^2.6.2 || ^3.0", + "laminas/laminas-stdlib": "^3.2.1", + "laminas/laminas-zendframework-bridge": "^1.0", + "php": "^5.6 || ^7.0" + }, + "replace": { + "zendframework/zend-session": "self.version" }, "require-dev": { - "phpunit/phpunit": "~4.7", - "squizlabs/php_codesniffer": "~2.0" + "container-interop/container-interop": "^1.1", + "laminas/laminas-cache": "^2.6.1", + "laminas/laminas-coding-standard": "~1.0.0", + "laminas/laminas-db": "^2.7", + "laminas/laminas-http": "^2.5.4", + "laminas/laminas-servicemanager": "^2.7.5 || ^3.0.3", + "laminas/laminas-validator": "^2.6", + "mongodb/mongodb": "^1.0.1", + "php-mock/php-mock-phpunit": "^1.1.2 || ^2.0", + "phpunit/phpunit": "^5.7.27 || ^6.5.14 || ^7.5.20" + }, + "suggest": { + "laminas/laminas-cache": "Laminas\\Cache component", + "laminas/laminas-db": "Laminas\\Db component", + "laminas/laminas-http": "Laminas\\Http component", + "laminas/laminas-servicemanager": "Laminas\\ServiceManager component", + "laminas/laminas-validator": "Laminas\\Validator component", + "mongodb/mongodb": "If you want to use the MongoDB session save handler" }, "type": "library", + "extra": { + "branch-alias": { + "dev-master": "2.9.x-dev", + "dev-develop": "2.10.x-dev" + }, + "laminas": { + "component": "Laminas\\Session", + "config-provider": "Laminas\\Session\\ConfigProvider" + } + }, "autoload": { "psr-4": { - "TrueBV\\": "src/" + "Laminas\\Session\\": "src/" } }, "notification-url": "https://packagist.org/downloads/", "license": [ - "MIT" - ], - "authors": [ - { - "name": "Renan Gonçalves", - "email": "renan.saddam@gmail.com" - } + "BSD-3-Clause" ], - "description": "A Bootstring encoding of Unicode for Internationalized Domain Names in Applications (IDNA)", - "homepage": "https://github.com/true/php-punycode", + "description": "Object-oriented interface to PHP sessions and storage", + "homepage": "https://laminas.dev", "keywords": [ - "idna", - "punycode" + "laminas", + "session" ], - "time": "2016-11-16T10:37:54+00:00" + "time": "2020-03-06T09:44:45+00:00" }, { - "name": "tubalmartin/cssmin", - "version": "v4.1.1", + "name": "laminas/laminas-soap", + "version": "2.8.0", "source": { "type": "git", - "url": "https://github.com/tubalmartin/YUI-CSS-compressor-PHP-port.git", - "reference": "3cbf557f4079d83a06f9c3ff9b957c022d7805cf" + "url": "https://github.com/laminas/laminas-soap.git", + "reference": "34f91d5c4c0a78bc5689cca2d1eaf829b27edd72" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/tubalmartin/YUI-CSS-compressor-PHP-port/zipball/3cbf557f4079d83a06f9c3ff9b957c022d7805cf", - "reference": "3cbf557f4079d83a06f9c3ff9b957c022d7805cf", + "url": "https://api.github.com/repos/laminas/laminas-soap/zipball/34f91d5c4c0a78bc5689cca2d1eaf829b27edd72", + "reference": "34f91d5c4c0a78bc5689cca2d1eaf829b27edd72", "shasum": "" }, "require": { - "ext-pcre": "*", - "php": ">=5.3.2" + "ext-soap": "*", + "laminas/laminas-server": "^2.6.1", + "laminas/laminas-stdlib": "^2.7 || ^3.0", + "laminas/laminas-uri": "^2.5.2", + "laminas/laminas-zendframework-bridge": "^1.0", + "php": "^5.6 || ^7.0" + }, + "replace": { + "zendframework/zend-soap": "self.version" }, "require-dev": { - "cogpowered/finediff": "0.3.*", - "phpunit/phpunit": "4.8.*" + "laminas/laminas-coding-standard": "~1.0.0", + "laminas/laminas-config": "^2.6", + "laminas/laminas-http": "^2.5.4", + "phpunit/phpunit": "^5.7.21 || ^6.3" + }, + "suggest": { + "laminas/laminas-http": "Laminas\\Http component" }, - "bin": [ - "cssmin" - ], "type": "library", - "autoload": { - "psr-4": { - "tubalmartin\\CssMin\\": "src" - } - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "BSD-3-Clause" - ], - "authors": [ - { - "name": "Túbal Martín", - "homepage": "http://tubalmartin.me/" + "extra": { + "branch-alias": { + "dev-master": "2.7.x-dev", + "dev-develop": "2.8.x-dev" } - ], - "description": "A PHP port of the YUI CSS compressor", - "homepage": "https://github.com/tubalmartin/YUI-CSS-compressor-PHP-port", - "keywords": [ - "compress", - "compressor", - "css", - "cssmin", - "minify", - "yui" - ], - "time": "2018-01-15T15:26:51+00:00" - }, - { - "name": "webonyx/graphql-php", - "version": "v0.13.8", - "source": { - "type": "git", - "url": "https://github.com/webonyx/graphql-php.git", - "reference": "6829ae58f4c59121df1f86915fb9917a2ec595e8" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/webonyx/graphql-php/zipball/6829ae58f4c59121df1f86915fb9917a2ec595e8", - "reference": "6829ae58f4c59121df1f86915fb9917a2ec595e8", - "shasum": "" - }, - "require": { - "ext-json": "*", - "ext-mbstring": "*", - "php": "^7.1||^8.0" - }, - "require-dev": { - "doctrine/coding-standard": "^6.0", - "phpbench/phpbench": "^0.14.0", - "phpstan/phpstan": "^0.11.4", - "phpstan/phpstan-phpunit": "^0.11.0", - "phpstan/phpstan-strict-rules": "^0.11.0", - "phpunit/phpcov": "^5.0", - "phpunit/phpunit": "^7.2", - "psr/http-message": "^1.0", - "react/promise": "2.*" }, - "suggest": { - "psr/http-message": "To use standard GraphQL server", - "react/promise": "To leverage async resolving on React PHP platform" - }, - "type": "library", "autoload": { "psr-4": { - "GraphQL\\": "src/" + "Laminas\\Soap\\": "src/" } }, "notification-url": "https://packagist.org/downloads/", "license": [ - "MIT" + "BSD-3-Clause" ], - "description": "A PHP port of GraphQL reference implementation", - "homepage": "https://github.com/webonyx/graphql-php", + "homepage": "https://laminas.dev", "keywords": [ - "api", - "graphql" + "laminas", + "soap" ], - "time": "2019-08-25T10:32:47+00:00" + "time": "2019-12-31T17:48:49+00:00" }, { - "name": "wikimedia/less.php", - "version": "1.8.2", + "name": "laminas/laminas-stdlib", + "version": "3.2.1", "source": { "type": "git", - "url": "https://github.com/wikimedia/less.php.git", - "reference": "e238ad228d74b6ffd38209c799b34e9826909266" + "url": "https://github.com/laminas/laminas-stdlib.git", + "reference": "2b18347625a2f06a1a485acfbc870f699dbe51c6" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/wikimedia/less.php/zipball/e238ad228d74b6ffd38209c799b34e9826909266", - "reference": "e238ad228d74b6ffd38209c799b34e9826909266", + "url": "https://api.github.com/repos/laminas/laminas-stdlib/zipball/2b18347625a2f06a1a485acfbc870f699dbe51c6", + "reference": "2b18347625a2f06a1a485acfbc870f699dbe51c6", "shasum": "" }, "require": { - "php": ">=7.2.9" - }, - "require-dev": { - "phpunit/phpunit": "7.5.14" - }, - "bin": [ - "bin/lessc" - ], - "type": "library", - "autoload": { - "psr-0": { - "Less": "lib/" - }, - "classmap": [ - "lessc.inc.php" - ] - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "Apache-2.0" - ], - "authors": [ - { - "name": "Josh Schmidt", - "homepage": "https://github.com/oyejorge" - }, - { - "name": "Matt Agar", - "homepage": "https://github.com/agar" - }, - { - "name": "Martin Jantošovič", - "homepage": "https://github.com/Mordred" - } - ], - "description": "PHP port of the Javascript version of LESS http://lesscss.org (Originally maintained by Josh Schmidt)", - "keywords": [ - "css", - "less", - "less.js", - "lesscss", - "php", - "stylesheet" - ], - "time": "2019-11-06T18:30:11+00:00" - }, - { - "name": "zendframework/zend-captcha", - "version": "2.9.0", - "source": { - "type": "git", - "url": "https://github.com/zendframework/zend-captcha.git", - "reference": "4272f3d0cde0a1fa9135d0cbc4a629fb655391d3" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/zendframework/zend-captcha/zipball/4272f3d0cde0a1fa9135d0cbc4a629fb655391d3", - "reference": "4272f3d0cde0a1fa9135d0cbc4a629fb655391d3", - "shasum": "" + "laminas/laminas-zendframework-bridge": "^1.0", + "php": "^5.6 || ^7.0" }, - "require": { - "php": "^5.6 || ^7.0", - "zendframework/zend-math": "^2.7 || ^3.0", - "zendframework/zend-stdlib": "^3.2.1" + "replace": { + "zendframework/zend-stdlib": "self.version" }, "require-dev": { - "phpunit/phpunit": "^5.7.27 || ^6.5.8 || ^7.1.2", - "zendframework/zend-coding-standard": "~1.0.0", - "zendframework/zend-session": "^2.8", - "zendframework/zend-text": "^2.6", - "zendframework/zend-validator": "^2.10.1", - "zendframework/zendservice-recaptcha": "^3.0" - }, - "suggest": { - "zendframework/zend-i18n-resources": "Translations of captcha messages", - "zendframework/zend-session": "Zend\\Session component", - "zendframework/zend-text": "Zend\\Text component", - "zendframework/zend-validator": "Zend\\Validator component", - "zendframework/zendservice-recaptcha": "ZendService\\ReCaptcha component" + "laminas/laminas-coding-standard": "~1.0.0", + "phpbench/phpbench": "^0.13", + "phpunit/phpunit": "^5.7.27 || ^6.5.8 || ^7.1.2" }, "type": "library", "extra": { "branch-alias": { - "dev-master": "2.9.x-dev", - "dev-develop": "2.10.x-dev" + "dev-master": "3.2.x-dev", + "dev-develop": "3.3.x-dev" } }, "autoload": { "psr-4": { - "Zend\\Captcha\\": "src/" + "Laminas\\Stdlib\\": "src/" } }, "notification-url": "https://packagist.org/downloads/", "license": [ "BSD-3-Clause" ], - "description": "Generate and validate CAPTCHAs using Figlets, images, ReCaptcha, and more", + "description": "SPL extensions, array utilities, error handlers, and more", + "homepage": "https://laminas.dev", "keywords": [ - "ZendFramework", - "captcha", - "zf" + "laminas", + "stdlib" ], - "time": "2019-06-18T09:32:52+00:00" + "time": "2019-12-31T17:51:15+00:00" }, { - "name": "zendframework/zend-code", - "version": "3.3.2", + "name": "laminas/laminas-text", + "version": "2.7.1", "source": { "type": "git", - "url": "https://github.com/zendframework/zend-code.git", - "reference": "936fa7ad4d53897ea3e3eb41b5b760828246a20b" + "url": "https://github.com/laminas/laminas-text.git", + "reference": "3601b5eacb06ed0a12f658df860cc0f9613cf4db" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/zendframework/zend-code/zipball/936fa7ad4d53897ea3e3eb41b5b760828246a20b", - "reference": "936fa7ad4d53897ea3e3eb41b5b760828246a20b", + "url": "https://api.github.com/repos/laminas/laminas-text/zipball/3601b5eacb06ed0a12f658df860cc0f9613cf4db", + "reference": "3601b5eacb06ed0a12f658df860cc0f9613cf4db", "shasum": "" }, "require": { - "php": "^7.1", - "zendframework/zend-eventmanager": "^2.6 || ^3.0" + "laminas/laminas-servicemanager": "^2.7.5 || ^3.0.3", + "laminas/laminas-stdlib": "^2.7 || ^3.0", + "laminas/laminas-zendframework-bridge": "^1.0", + "php": "^5.6 || ^7.0" }, - "require-dev": { - "doctrine/annotations": "^1.0", - "ext-phar": "*", - "phpunit/phpunit": "^7.5.15", - "zendframework/zend-coding-standard": "^1.0", - "zendframework/zend-stdlib": "^2.7 || ^3.0" + "replace": { + "zendframework/zend-text": "self.version" }, - "suggest": { - "doctrine/annotations": "Doctrine\\Common\\Annotations >=1.0 for annotation features", - "zendframework/zend-stdlib": "Zend\\Stdlib component" + "require-dev": { + "laminas/laminas-coding-standard": "~1.0.0", + "laminas/laminas-config": "^2.6", + "phpunit/phpunit": "^5.7.27 || ^6.5.8 || ^7.1.4" }, "type": "library", "extra": { "branch-alias": { - "dev-master": "3.3.x-dev", - "dev-develop": "3.4.x-dev" + "dev-master": "2.7.x-dev", + "dev-develop": "2.8.x-dev" } }, "autoload": { "psr-4": { - "Zend\\Code\\": "src/" + "Laminas\\Text\\": "src/" } }, "notification-url": "https://packagist.org/downloads/", "license": [ "BSD-3-Clause" ], - "description": "Extensions to the PHP Reflection API, static code scanning, and code generation", + "description": "Create FIGlets and text-based tables", + "homepage": "https://laminas.dev", "keywords": [ - "ZendFramework", - "code", - "zf" + "laminas", + "text" ], - "time": "2019-08-31T14:14:34+00:00" + "time": "2019-12-31T17:54:52+00:00" }, { - "name": "zendframework/zend-config", - "version": "2.6.0", + "name": "laminas/laminas-uri", + "version": "2.7.1", "source": { "type": "git", - "url": "https://github.com/zendframework/zend-config.git", - "reference": "2920e877a9f6dca9fa8f6bd3b1ffc2e19bb1e30d" + "url": "https://github.com/laminas/laminas-uri.git", + "reference": "6be8ce19622f359b048ce4faebf1aa1bca73a7ff" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/zendframework/zend-config/zipball/2920e877a9f6dca9fa8f6bd3b1ffc2e19bb1e30d", - "reference": "2920e877a9f6dca9fa8f6bd3b1ffc2e19bb1e30d", + "url": "https://api.github.com/repos/laminas/laminas-uri/zipball/6be8ce19622f359b048ce4faebf1aa1bca73a7ff", + "reference": "6be8ce19622f359b048ce4faebf1aa1bca73a7ff", "shasum": "" }, "require": { - "php": "^5.5 || ^7.0", - "zendframework/zend-stdlib": "^2.7 || ^3.0" + "laminas/laminas-escaper": "^2.5", + "laminas/laminas-validator": "^2.10", + "laminas/laminas-zendframework-bridge": "^1.0", + "php": "^5.6 || ^7.0" }, - "require-dev": { - "fabpot/php-cs-fixer": "1.7.*", - "phpunit/phpunit": "~4.0", - "zendframework/zend-filter": "^2.6", - "zendframework/zend-i18n": "^2.5", - "zendframework/zend-json": "^2.6.1", - "zendframework/zend-servicemanager": "^2.7.5 || ^3.0.3" + "replace": { + "zendframework/zend-uri": "self.version" }, - "suggest": { - "zendframework/zend-filter": "Zend\\Filter component", - "zendframework/zend-i18n": "Zend\\I18n component", - "zendframework/zend-json": "Zend\\Json to use the Json reader or writer classes", - "zendframework/zend-servicemanager": "Zend\\ServiceManager for use with the Config Factory to retrieve reader and writer instances" + "require-dev": { + "laminas/laminas-coding-standard": "~1.0.0", + "phpunit/phpunit": "^5.7.27 || ^6.5.8 || ^7.1.4" }, "type": "library", "extra": { "branch-alias": { - "dev-master": "2.6-dev", - "dev-develop": "2.7-dev" + "dev-master": "2.7.x-dev", + "dev-develop": "2.8.x-dev" } }, "autoload": { "psr-4": { - "Zend\\Config\\": "src/" + "Laminas\\Uri\\": "src/" } }, "notification-url": "https://packagist.org/downloads/", "license": [ "BSD-3-Clause" ], - "description": "provides a nested object property based user interface for accessing this configuration data within application code", - "homepage": "https://github.com/zendframework/zend-config", + "description": "A component that aids in manipulating and validating » Uniform Resource Identifiers (URIs)", + "homepage": "https://laminas.dev", "keywords": [ - "config", - "zf2" + "laminas", + "uri" ], - "time": "2016-02-04T23:01:10+00:00" + "time": "2019-12-31T17:56:00+00:00" }, { - "name": "zendframework/zend-console", - "version": "2.8.0", + "name": "laminas/laminas-validator", + "version": "2.13.2", "source": { "type": "git", - "url": "https://github.com/zendframework/zend-console.git", - "reference": "95817ae78f73c48026972e350a2ecc31c6d9f9ae" + "url": "https://github.com/laminas/laminas-validator.git", + "reference": "e7bf6a2eedc0508ebde0ebc66662efeb0abbcb2c" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/zendframework/zend-console/zipball/95817ae78f73c48026972e350a2ecc31c6d9f9ae", - "reference": "95817ae78f73c48026972e350a2ecc31c6d9f9ae", + "url": "https://api.github.com/repos/laminas/laminas-validator/zipball/e7bf6a2eedc0508ebde0ebc66662efeb0abbcb2c", + "reference": "e7bf6a2eedc0508ebde0ebc66662efeb0abbcb2c", "shasum": "" }, "require": { - "php": "^5.6 || ^7.0", - "zendframework/zend-stdlib": "^3.2.1" + "container-interop/container-interop": "^1.1", + "laminas/laminas-stdlib": "^3.2.1", + "laminas/laminas-zendframework-bridge": "^1.0", + "php": "^7.1" }, - "require-dev": { - "phpunit/phpunit": "^5.7.23 || ^6.4.3", - "zendframework/zend-coding-standard": "~1.0.0", - "zendframework/zend-filter": "^2.7.2", - "zendframework/zend-json": "^2.6 || ^3.0", - "zendframework/zend-validator": "^2.10.1" + "replace": { + "zendframework/zend-validator": "self.version" + }, + "require-dev": { + "laminas/laminas-cache": "^2.6.1", + "laminas/laminas-coding-standard": "~1.0.0", + "laminas/laminas-config": "^2.6", + "laminas/laminas-db": "^2.7", + "laminas/laminas-filter": "^2.6", + "laminas/laminas-http": "^2.5.4", + "laminas/laminas-i18n": "^2.6", + "laminas/laminas-math": "^2.6", + "laminas/laminas-servicemanager": "^2.7.5 || ^3.0.3", + "laminas/laminas-session": "^2.8", + "laminas/laminas-uri": "^2.5", + "phpunit/phpunit": "^7.5.20 || ^8.5.2", + "psr/http-client": "^1.0", + "psr/http-factory": "^1.0", + "psr/http-message": "^1.0" }, "suggest": { - "zendframework/zend-filter": "To support DefaultRouteMatcher usage", - "zendframework/zend-validator": "To support DefaultRouteMatcher usage" + "laminas/laminas-db": "Laminas\\Db component, required by the (No)RecordExists validator", + "laminas/laminas-filter": "Laminas\\Filter component, required by the Digits validator", + "laminas/laminas-i18n": "Laminas\\I18n component to allow translation of validation error messages", + "laminas/laminas-i18n-resources": "Translations of validator messages", + "laminas/laminas-math": "Laminas\\Math component, required by the Csrf validator", + "laminas/laminas-servicemanager": "Laminas\\ServiceManager component to allow using the ValidatorPluginManager and validator chains", + "laminas/laminas-session": "Laminas\\Session component, ^2.8; required by the Csrf validator", + "laminas/laminas-uri": "Laminas\\Uri component, required by the Uri and Sitemap\\Loc validators", + "psr/http-message": "psr/http-message, required when validating PSR-7 UploadedFileInterface instances via the Upload and UploadFile validators" }, "type": "library", "extra": { "branch-alias": { - "dev-master": "2.8.x-dev", - "dev-develop": "2.9.x-dev" + "dev-master": "2.13.x-dev", + "dev-develop": "2.14.x-dev" + }, + "laminas": { + "component": "Laminas\\Validator", + "config-provider": "Laminas\\Validator\\ConfigProvider" } }, "autoload": { "psr-4": { - "Zend\\Console\\": "src/" + "Laminas\\Validator\\": "src/" } }, "notification-url": "https://packagist.org/downloads/", "license": [ "BSD-3-Clause" ], - "description": "Build console applications using getopt syntax or routing, complete with prompts", + "description": "Validation classes for a wide range of domains, and the ability to chain validators to create complex validation criteria", + "homepage": "https://laminas.dev", "keywords": [ - "ZendFramework", - "console", - "zf" + "laminas", + "validator" ], - "time": "2019-02-04T19:48:22+00:00" + "time": "2020-03-16T11:38:27+00:00" }, { - "name": "zendframework/zend-crypt", - "version": "2.6.0", + "name": "laminas/laminas-view", + "version": "2.11.4", "source": { "type": "git", - "url": "https://github.com/zendframework/zend-crypt.git", - "reference": "1b2f5600bf6262904167116fa67b58ab1457036d" + "url": "https://github.com/laminas/laminas-view.git", + "reference": "3bbb2e94287383604c898284a18d2d06cf17301e" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/zendframework/zend-crypt/zipball/1b2f5600bf6262904167116fa67b58ab1457036d", - "reference": "1b2f5600bf6262904167116fa67b58ab1457036d", + "url": "https://api.github.com/repos/laminas/laminas-view/zipball/3bbb2e94287383604c898284a18d2d06cf17301e", + "reference": "3bbb2e94287383604c898284a18d2d06cf17301e", "shasum": "" }, "require": { - "container-interop/container-interop": "~1.0", - "php": "^5.5 || ^7.0", - "zendframework/zend-math": "^2.6", - "zendframework/zend-stdlib": "^2.7 || ^3.0" + "laminas/laminas-eventmanager": "^2.6.2 || ^3.0", + "laminas/laminas-json": "^2.6.1 || ^3.0", + "laminas/laminas-loader": "^2.5", + "laminas/laminas-stdlib": "^2.7 || ^3.0", + "laminas/laminas-zendframework-bridge": "^1.0", + "php": "^5.6 || ^7.0" }, - "require-dev": { - "fabpot/php-cs-fixer": "1.7.*", - "phpunit/phpunit": "~4.0" + "replace": { + "zendframework/zend-view": "self.version" + }, + "require-dev": { + "laminas/laminas-authentication": "^2.5", + "laminas/laminas-cache": "^2.6.1", + "laminas/laminas-coding-standard": "~1.0.0", + "laminas/laminas-config": "^2.6", + "laminas/laminas-console": "^2.6", + "laminas/laminas-escaper": "^2.5", + "laminas/laminas-feed": "^2.7", + "laminas/laminas-filter": "^2.6.1", + "laminas/laminas-http": "^2.5.4", + "laminas/laminas-i18n": "^2.6", + "laminas/laminas-log": "^2.7", + "laminas/laminas-modulemanager": "^2.7.1", + "laminas/laminas-mvc": "^2.7.14 || ^3.0", + "laminas/laminas-navigation": "^2.5", + "laminas/laminas-paginator": "^2.5", + "laminas/laminas-permissions-acl": "^2.6", + "laminas/laminas-router": "^3.0.1", + "laminas/laminas-serializer": "^2.6.1", + "laminas/laminas-servicemanager": "^2.7.5 || ^3.0.3", + "laminas/laminas-session": "^2.8.1", + "laminas/laminas-uri": "^2.5", + "phpunit/phpunit": "^5.7.15 || ^6.0.8" }, "suggest": { - "ext-mcrypt": "Required for most features of Zend\\Crypt" + "laminas/laminas-authentication": "Laminas\\Authentication component", + "laminas/laminas-escaper": "Laminas\\Escaper component", + "laminas/laminas-feed": "Laminas\\Feed component", + "laminas/laminas-filter": "Laminas\\Filter component", + "laminas/laminas-http": "Laminas\\Http component", + "laminas/laminas-i18n": "Laminas\\I18n component", + "laminas/laminas-mvc": "Laminas\\Mvc component", + "laminas/laminas-mvc-plugin-flashmessenger": "laminas-mvc-plugin-flashmessenger component, if you want to use the FlashMessenger view helper with laminas-mvc versions 3 and up", + "laminas/laminas-navigation": "Laminas\\Navigation component", + "laminas/laminas-paginator": "Laminas\\Paginator component", + "laminas/laminas-permissions-acl": "Laminas\\Permissions\\Acl component", + "laminas/laminas-servicemanager": "Laminas\\ServiceManager component", + "laminas/laminas-uri": "Laminas\\Uri component" }, + "bin": [ + "bin/templatemap_generator.php" + ], "type": "library", "extra": { "branch-alias": { - "dev-master": "2.6-dev", - "dev-develop": "2.7-dev" + "dev-master": "2.11.x-dev", + "dev-develop": "2.12.x-dev" } }, "autoload": { "psr-4": { - "Zend\\Crypt\\": "src/" + "Laminas\\View\\": "src/" } }, "notification-url": "https://packagist.org/downloads/", "license": [ "BSD-3-Clause" ], - "homepage": "https://github.com/zendframework/zend-crypt", + "description": "Flexible view layer supporting and providing multiple view layers, helpers, and more", + "homepage": "https://laminas.dev", "keywords": [ - "crypt", - "zf2" + "laminas", + "view" ], - "time": "2016-02-03T23:46:30+00:00" + "time": "2019-12-31T18:03:30+00:00" }, { - "name": "zendframework/zend-db", - "version": "2.10.0", + "name": "laminas/laminas-zendframework-bridge", + "version": "1.0.2", "source": { "type": "git", - "url": "https://github.com/zendframework/zend-db.git", - "reference": "77022f06f6ffd384fa86d22ab8d8bbdb925a1e8e" + "url": "https://github.com/laminas/laminas-zendframework-bridge.git", + "reference": "faf68f6109ceeff24241226033ab59640c7eb63b" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/zendframework/zend-db/zipball/77022f06f6ffd384fa86d22ab8d8bbdb925a1e8e", - "reference": "77022f06f6ffd384fa86d22ab8d8bbdb925a1e8e", + "url": "https://api.github.com/repos/laminas/laminas-zendframework-bridge/zipball/faf68f6109ceeff24241226033ab59640c7eb63b", + "reference": "faf68f6109ceeff24241226033ab59640c7eb63b", "shasum": "" }, "require": { - "php": "^5.6 || ^7.0", - "zendframework/zend-stdlib": "^2.7 || ^3.0" + "php": "^5.6 || ^7.0" }, "require-dev": { - "phpunit/phpunit": "^5.7.25 || ^6.4.4", - "zendframework/zend-coding-standard": "~1.0.0", - "zendframework/zend-eventmanager": "^2.6.2 || ^3.0", - "zendframework/zend-hydrator": "^1.1 || ^2.1 || ^3.0", - "zendframework/zend-servicemanager": "^2.7.5 || ^3.0.3" - }, - "suggest": { - "zendframework/zend-eventmanager": "Zend\\EventManager component", - "zendframework/zend-hydrator": "Zend\\Hydrator component for using HydratingResultSets", - "zendframework/zend-servicemanager": "Zend\\ServiceManager component" + "phpunit/phpunit": "^5.7 || ^6.5 || ^7.5 || ^8.1", + "squizlabs/php_codesniffer": "^3.5" }, "type": "library", "extra": { "branch-alias": { - "dev-master": "2.9-dev", - "dev-develop": "2.10-dev" + "dev-master": "1.0.x-dev", + "dev-develop": "1.1.x-dev" }, - "zf": { - "component": "Zend\\Db", - "config-provider": "Zend\\Db\\ConfigProvider" + "laminas": { + "module": "Laminas\\ZendFrameworkBridge" } }, "autoload": { + "files": [ + "src/autoload.php" + ], "psr-4": { - "Zend\\Db\\": "src/" + "Laminas\\ZendFrameworkBridge\\": "src//" } }, "notification-url": "https://packagist.org/downloads/", "license": [ "BSD-3-Clause" ], - "description": "Database abstraction layer, SQL abstraction, result set abstraction, and RowDataGateway and TableDataGateway implementations", + "description": "Alias legacy ZF class names to Laminas Project equivalents.", "keywords": [ "ZendFramework", - "db", + "autoloading", + "laminas", "zf" ], - "time": "2019-02-25T11:37:45+00:00" + "time": "2020-03-26T16:07:12+00:00" }, { - "name": "zendframework/zend-di", - "version": "2.6.1", + "name": "magento/composer", + "version": "1.6.x-dev", "source": { "type": "git", - "url": "https://github.com/zendframework/zend-di.git", - "reference": "1fd1ba85660b5a2718741b38639dc7c4c3194b37" + "url": "https://github.com/magento/composer.git", + "reference": "fe738ac9155f550b669b260b3cfa6422eacb53fa" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/zendframework/zend-di/zipball/1fd1ba85660b5a2718741b38639dc7c4c3194b37", - "reference": "1fd1ba85660b5a2718741b38639dc7c4c3194b37", + "url": "https://api.github.com/repos/magento/composer/zipball/fe738ac9155f550b669b260b3cfa6422eacb53fa", + "reference": "fe738ac9155f550b669b260b3cfa6422eacb53fa", "shasum": "" }, "require": { - "container-interop/container-interop": "^1.1", - "php": "^5.5 || ^7.0", - "zendframework/zend-code": "^2.6 || ^3.0", - "zendframework/zend-stdlib": "^2.7 || ^3.0" + "composer/composer": "^1.6", + "php": "~7.1.3||~7.2.0||~7.3.0", + "symfony/console": "~4.4.0" }, "require-dev": { - "fabpot/php-cs-fixer": "1.7.*", - "phpunit/phpunit": "~4.0" + "phpunit/phpunit": "~7.0.0" }, "type": "library", - "extra": { - "branch-alias": { - "dev-master": "2.6-dev", - "dev-develop": "2.7-dev" - } - }, "autoload": { "psr-4": { - "Zend\\Di\\": "src/" + "Magento\\Composer\\": "src" } }, "notification-url": "https://packagist.org/downloads/", "license": [ - "BSD-3-Clause" - ], - "homepage": "https://github.com/zendframework/zend-di", - "keywords": [ - "di", - "zf2" + "OSL-3.0", + "AFL-3.0" ], - "time": "2016-04-25T20:58:11+00:00" + "description": "Magento composer library helps to instantiate Composer application and run composer commands.", + "time": "2020-01-17T16:43:51+00:00" }, { - "name": "zendframework/zend-diactoros", - "version": "1.8.7", + "name": "magento/magento-composer-installer", + "version": "0.1.13", "source": { "type": "git", - "url": "https://github.com/zendframework/zend-diactoros.git", - "reference": "a85e67b86e9b8520d07e6415fcbcb8391b44a75b" + "url": "https://github.com/magento/magento-composer-installer.git", + "reference": "8b6c32f53b4944a5d6656e86344cd0f9784709a1" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/zendframework/zend-diactoros/zipball/a85e67b86e9b8520d07e6415fcbcb8391b44a75b", - "reference": "a85e67b86e9b8520d07e6415fcbcb8391b44a75b", + "url": "https://api.github.com/repos/magento/magento-composer-installer/zipball/8b6c32f53b4944a5d6656e86344cd0f9784709a1", + "reference": "8b6c32f53b4944a5d6656e86344cd0f9784709a1", "shasum": "" }, "require": { - "php": "^5.6 || ^7.0", - "psr/http-message": "^1.0" + "composer-plugin-api": "^1.0" }, - "provide": { - "psr/http-message-implementation": "1.0" + "replace": { + "magento-hackathon/magento-composer-installer": "*" }, "require-dev": { - "ext-dom": "*", - "ext-libxml": "*", - "php-http/psr7-integration-tests": "dev-master", - "phpunit/phpunit": "^5.7.16 || ^6.0.8 || ^7.2.7", - "zendframework/zend-coding-standard": "~1.0" + "composer/composer": "*@dev", + "firegento/phpcs": "dev-patch-1", + "mikey179/vfsstream": "*", + "phpunit/phpunit": "*", + "phpunit/phpunit-mock-objects": "dev-master", + "squizlabs/php_codesniffer": "1.4.7", + "symfony/process": "*" }, - "type": "library", + "type": "composer-plugin", "extra": { - "branch-alias": { - "dev-release-1.8": "1.8.x-dev" - } + "composer-command-registry": [ + "MagentoHackathon\\Composer\\Magento\\Command\\DeployCommand" + ], + "class": "MagentoHackathon\\Composer\\Magento\\Plugin" }, "autoload": { - "files": [ - "src/functions/create_uploaded_file.php", - "src/functions/marshal_headers_from_sapi.php", - "src/functions/marshal_method_from_sapi.php", - "src/functions/marshal_protocol_version_from_sapi.php", - "src/functions/marshal_uri_from_sapi.php", - "src/functions/normalize_server.php", - "src/functions/normalize_uploaded_files.php", - "src/functions/parse_cookie_header.php" - ], - "psr-4": { - "Zend\\Diactoros\\": "src/" + "psr-0": { + "MagentoHackathon\\Composer\\Magento": "src/" } }, "notification-url": "https://packagist.org/downloads/", "license": [ - "BSD-2-Clause" + "OSL-3.0" ], - "description": "PSR HTTP Message implementations", - "homepage": "https://github.com/zendframework/zend-diactoros", + "authors": [ + { + "name": "Vinai Kopp", + "email": "vinai@netzarbeiter.com" + }, + { + "name": "Daniel Fahlke aka Flyingmana", + "email": "flyingmana@googlemail.com" + }, + { + "name": "Jörg Weller", + "email": "weller@flagbit.de" + }, + { + "name": "Karl Spies", + "email": "karl.spies@gmx.net" + }, + { + "name": "Tobias Vogt", + "email": "tobi@webguys.de" + }, + { + "name": "David Fuhr", + "email": "fuhr@flagbit.de" + } + ], + "description": "Composer installer for Magento modules", + "homepage": "https://github.com/magento/magento-composer-installer", "keywords": [ - "http", - "psr", - "psr-7" + "composer-installer", + "magento" ], - "time": "2019-08-06T17:53:53+00:00" + "time": "2017-12-29T16:45:24+00:00" }, { - "name": "zendframework/zend-escaper", - "version": "2.6.1", + "name": "magento/zendframework1", + "version": "1.14.3", "source": { "type": "git", - "url": "https://github.com/zendframework/zend-escaper.git", - "reference": "3801caa21b0ca6aca57fa1c42b08d35c395ebd5f" + "url": "https://github.com/magento/zf1.git", + "reference": "726855dfb080089dc7bc7b016624129f8e7bc4e5" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/zendframework/zend-escaper/zipball/3801caa21b0ca6aca57fa1c42b08d35c395ebd5f", - "reference": "3801caa21b0ca6aca57fa1c42b08d35c395ebd5f", + "url": "https://api.github.com/repos/magento/zf1/zipball/726855dfb080089dc7bc7b016624129f8e7bc4e5", + "reference": "726855dfb080089dc7bc7b016624129f8e7bc4e5", "shasum": "" }, "require": { - "php": "^5.6 || ^7.0" + "php": ">=5.2.11" }, "require-dev": { - "phpunit/phpunit": "^5.7.27 || ^6.5.8 || ^7.1.2", - "zendframework/zend-coding-standard": "~1.0.0" + "phpunit/dbunit": "1.3.*", + "phpunit/phpunit": "3.7.*" }, "type": "library", "extra": { "branch-alias": { - "dev-master": "2.6.x-dev", - "dev-develop": "2.7.x-dev" + "dev-master": "1.12.x-dev" } }, "autoload": { - "psr-4": { - "Zend\\Escaper\\": "src/" + "psr-0": { + "Zend_": "library/" } }, "notification-url": "https://packagist.org/downloads/", + "include-path": [ + "library/" + ], "license": [ "BSD-3-Clause" ], - "description": "Securely and safely escape HTML, HTML attributes, JavaScript, CSS, and URLs", + "description": "Magento Zend Framework 1", + "homepage": "http://framework.zend.com/", "keywords": [ - "ZendFramework", - "escaper", - "zf" + "ZF1", + "framework" ], - "time": "2019-09-05T20:03:20+00:00" + "time": "2019-11-26T15:09:40+00:00" }, { - "name": "zendframework/zend-eventmanager", - "version": "3.2.1", + "name": "monolog/monolog", + "version": "1.25.3", "source": { "type": "git", - "url": "https://github.com/zendframework/zend-eventmanager.git", - "reference": "a5e2583a211f73604691586b8406ff7296a946dd" + "url": "https://github.com/Seldaek/monolog.git", + "reference": "fa82921994db851a8becaf3787a9e73c5976b6f1" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/zendframework/zend-eventmanager/zipball/a5e2583a211f73604691586b8406ff7296a946dd", - "reference": "a5e2583a211f73604691586b8406ff7296a946dd", + "url": "https://api.github.com/repos/Seldaek/monolog/zipball/fa82921994db851a8becaf3787a9e73c5976b6f1", + "reference": "fa82921994db851a8becaf3787a9e73c5976b6f1", "shasum": "" }, "require": { - "php": "^5.6 || ^7.0" + "php": ">=5.3.0", + "psr/log": "~1.0" + }, + "provide": { + "psr/log-implementation": "1.0.0" }, "require-dev": { - "athletic/athletic": "^0.1", - "container-interop/container-interop": "^1.1.0", - "phpunit/phpunit": "^5.7.27 || ^6.5.8 || ^7.1.2", - "zendframework/zend-coding-standard": "~1.0.0", - "zendframework/zend-stdlib": "^2.7.3 || ^3.0" + "aws/aws-sdk-php": "^2.4.9 || ^3.0", + "doctrine/couchdb": "~1.0@dev", + "graylog2/gelf-php": "~1.0", + "jakub-onderka/php-parallel-lint": "0.9", + "php-amqplib/php-amqplib": "~2.4", + "php-console/php-console": "^3.1.3", + "phpunit/phpunit": "~4.5", + "phpunit/phpunit-mock-objects": "2.3.0", + "ruflin/elastica": ">=0.90 <3.0", + "sentry/sentry": "^0.13", + "swiftmailer/swiftmailer": "^5.3|^6.0" }, "suggest": { - "container-interop/container-interop": "^1.1.0, to use the lazy listeners feature", - "zendframework/zend-stdlib": "^2.7.3 || ^3.0, to use the FilterChain feature" + "aws/aws-sdk-php": "Allow sending log messages to AWS services like DynamoDB", + "doctrine/couchdb": "Allow sending log messages to a CouchDB server", + "ext-amqp": "Allow sending log messages to an AMQP server (1.0+ required)", + "ext-mongo": "Allow sending log messages to a MongoDB server", + "graylog2/gelf-php": "Allow sending log messages to a GrayLog2 server", + "mongodb/mongodb": "Allow sending log messages to a MongoDB server via PHP Driver", + "php-amqplib/php-amqplib": "Allow sending log messages to an AMQP server using php-amqplib", + "php-console/php-console": "Allow sending log messages to Google Chrome", + "rollbar/rollbar": "Allow sending log messages to Rollbar", + "ruflin/elastica": "Allow sending log messages to an Elastic Search server", + "sentry/sentry": "Allow sending log messages to a Sentry server" }, "type": "library", "extra": { "branch-alias": { - "dev-master": "3.2-dev", - "dev-develop": "3.3-dev" + "dev-master": "2.0.x-dev" } }, "autoload": { "psr-4": { - "Zend\\EventManager\\": "src/" + "Monolog\\": "src/Monolog" } }, "notification-url": "https://packagist.org/downloads/", "license": [ - "BSD-3-Clause" + "MIT" ], - "description": "Trigger and listen to events within a PHP application", - "homepage": "https://github.com/zendframework/zend-eventmanager", + "authors": [ + { + "name": "Jordi Boggiano", + "email": "j.boggiano@seld.be", + "homepage": "http://seld.be" + } + ], + "description": "Sends your logs to files, sockets, inboxes, databases and various web services", + "homepage": "http://github.com/Seldaek/monolog", "keywords": [ - "event", - "eventmanager", - "events", - "zf2" + "log", + "logging", + "psr-3" ], - "time": "2018-04-25T15:33:34+00:00" + "time": "2019-12-20T14:15:16+00:00" }, { - "name": "zendframework/zend-feed", - "version": "2.12.0", + "name": "paragonie/random_compat", + "version": "v9.99.99", "source": { "type": "git", - "url": "https://github.com/zendframework/zend-feed.git", - "reference": "d926c5af34b93a0121d5e2641af34ddb1533d733" + "url": "https://github.com/paragonie/random_compat.git", + "reference": "84b4dfb120c6f9b4ff7b3685f9b8f1aa365a0c95" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/zendframework/zend-feed/zipball/d926c5af34b93a0121d5e2641af34ddb1533d733", - "reference": "d926c5af34b93a0121d5e2641af34ddb1533d733", + "url": "https://api.github.com/repos/paragonie/random_compat/zipball/84b4dfb120c6f9b4ff7b3685f9b8f1aa365a0c95", + "reference": "84b4dfb120c6f9b4ff7b3685f9b8f1aa365a0c95", "shasum": "" }, "require": { - "ext-dom": "*", - "ext-libxml": "*", - "php": "^5.6 || ^7.0", - "zendframework/zend-escaper": "^2.5.2", - "zendframework/zend-stdlib": "^3.2.1" + "php": "^7" }, "require-dev": { - "phpunit/phpunit": "^5.7.23 || ^6.4.3", - "psr/http-message": "^1.0.1", - "zendframework/zend-cache": "^2.7.2", - "zendframework/zend-coding-standard": "~1.0.0", - "zendframework/zend-db": "^2.8.2", - "zendframework/zend-http": "^2.7", - "zendframework/zend-servicemanager": "^2.7.8 || ^3.3", - "zendframework/zend-validator": "^2.10.1" + "phpunit/phpunit": "4.*|5.*", + "vimeo/psalm": "^1" + }, + "suggest": { + "ext-libsodium": "Provides a modern crypto API that can be used to generate random bytes." + }, + "type": "library", + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Paragon Initiative Enterprises", + "email": "security@paragonie.com", + "homepage": "https://paragonie.com" + } + ], + "description": "PHP 5.x polyfill for random_bytes() and random_int() from PHP 7", + "keywords": [ + "csprng", + "polyfill", + "pseudorandom", + "random" + ], + "time": "2018-07-02T15:55:56+00:00" + }, + { + "name": "paragonie/sodium_compat", + "version": "v1.13.0", + "source": { + "type": "git", + "url": "https://github.com/paragonie/sodium_compat.git", + "reference": "bbade402cbe84c69b718120911506a3aa2bae653" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/paragonie/sodium_compat/zipball/bbade402cbe84c69b718120911506a3aa2bae653", + "reference": "bbade402cbe84c69b718120911506a3aa2bae653", + "shasum": "" + }, + "require": { + "paragonie/random_compat": ">=1", + "php": "^5.2.4|^5.3|^5.4|^5.5|^5.6|^7|^8" + }, + "require-dev": { + "phpunit/phpunit": "^3|^4|^5|^6|^7" }, "suggest": { - "psr/http-message": "PSR-7 ^1.0.1, if you wish to use Zend\\Feed\\Reader\\Http\\Psr7ResponseDecorator", - "zendframework/zend-cache": "Zend\\Cache component, for optionally caching feeds between requests", - "zendframework/zend-db": "Zend\\Db component, for use with PubSubHubbub", - "zendframework/zend-http": "Zend\\Http for PubSubHubbub, and optionally for use with Zend\\Feed\\Reader", - "zendframework/zend-servicemanager": "Zend\\ServiceManager component, for easily extending ExtensionManager implementations", - "zendframework/zend-validator": "Zend\\Validator component, for validating email addresses used in Atom feeds and entries when using the Writer subcomponent" + "ext-libsodium": "PHP < 7.0: Better performance, password hashing (Argon2i), secure memory management (memzero), and better security.", + "ext-sodium": "PHP >= 7.0: Better performance, password hashing (Argon2i), secure memory management (memzero), and better security." + }, + "type": "library", + "autoload": { + "files": [ + "autoload.php" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "ISC" + ], + "authors": [ + { + "name": "Paragon Initiative Enterprises", + "email": "security@paragonie.com" + }, + { + "name": "Frank Denis", + "email": "jedisct1@pureftpd.org" + } + ], + "description": "Pure PHP implementation of libsodium; uses the PHP extension if it exists", + "keywords": [ + "Authentication", + "BLAKE2b", + "ChaCha20", + "ChaCha20-Poly1305", + "Chapoly", + "Curve25519", + "Ed25519", + "EdDSA", + "Edwards-curve Digital Signature Algorithm", + "Elliptic Curve Diffie-Hellman", + "Poly1305", + "Pure-PHP cryptography", + "RFC 7748", + "RFC 8032", + "Salpoly", + "Salsa20", + "X25519", + "XChaCha20-Poly1305", + "XSalsa20-Poly1305", + "Xchacha20", + "Xsalsa20", + "aead", + "cryptography", + "ecdh", + "elliptic curve", + "elliptic curve cryptography", + "encryption", + "libsodium", + "php", + "public-key cryptography", + "secret-key cryptography", + "side-channel resistant" + ], + "time": "2020-03-20T21:48:09+00:00" + }, + { + "name": "pelago/emogrifier", + "version": "v2.2.0", + "source": { + "type": "git", + "url": "https://github.com/MyIntervals/emogrifier.git", + "reference": "2472bc1c3a2dee8915ecc2256139c6100024332f" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/MyIntervals/emogrifier/zipball/2472bc1c3a2dee8915ecc2256139c6100024332f", + "reference": "2472bc1c3a2dee8915ecc2256139c6100024332f", + "shasum": "" + }, + "require": { + "ext-dom": "*", + "ext-libxml": "*", + "php": "^5.5.0 || ~7.0.0 || ~7.1.0 || ~7.2.0 || ~7.3.0", + "symfony/css-selector": "^3.4.0 || ^4.0.0" + }, + "require-dev": { + "friendsofphp/php-cs-fixer": "^2.2.0", + "phpmd/phpmd": "^2.6.0", + "phpunit/phpunit": "^4.8.0", + "squizlabs/php_codesniffer": "^3.3.2" }, "type": "library", "extra": { "branch-alias": { - "dev-master": "2.12.x-dev", - "dev-develop": "2.13.x-dev" + "dev-master": "3.0.x-dev" } }, "autoload": { "psr-4": { - "Zend\\Feed\\": "src/" + "Pelago\\": "src/" } }, "notification-url": "https://packagist.org/downloads/", "license": [ - "BSD-3-Clause" + "MIT" ], - "description": "provides functionality for consuming RSS and Atom feeds", + "authors": [ + { + "name": "Oliver Klee", + "email": "github@oliverklee.de" + }, + { + "name": "Zoli Szabó", + "email": "zoli.szabo+github@gmail.com" + }, + { + "name": "John Reeve", + "email": "jreeve@pelagodesign.com" + }, + { + "name": "Jake Hotson", + "email": "jake@qzdesign.co.uk" + }, + { + "name": "Cameron Brooks" + }, + { + "name": "Jaime Prado" + } + ], + "description": "Converts CSS styles into inline style attributes in your HTML code", + "homepage": "https://www.myintervals.com/emogrifier.php", "keywords": [ - "ZendFramework", - "feed", - "zf" + "css", + "email", + "pre-processing" ], - "time": "2019-03-05T20:08:49+00:00" + "time": "2019-09-04T16:07:59+00:00" }, { - "name": "zendframework/zend-filter", - "version": "2.9.2", + "name": "php-amqplib/php-amqplib", + "version": "v2.10.1", "source": { "type": "git", - "url": "https://github.com/zendframework/zend-filter.git", - "reference": "d78f2cdde1c31975e18b2a0753381ed7b61118ef" + "url": "https://github.com/php-amqplib/php-amqplib.git", + "reference": "6e2b2501e021e994fb64429e5a78118f83b5c200" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/zendframework/zend-filter/zipball/d78f2cdde1c31975e18b2a0753381ed7b61118ef", - "reference": "d78f2cdde1c31975e18b2a0753381ed7b61118ef", + "url": "https://api.github.com/repos/php-amqplib/php-amqplib/zipball/6e2b2501e021e994fb64429e5a78118f83b5c200", + "reference": "6e2b2501e021e994fb64429e5a78118f83b5c200", "shasum": "" }, "require": { - "php": "^5.6 || ^7.0", - "zendframework/zend-stdlib": "^2.7.7 || ^3.1" + "ext-bcmath": "*", + "ext-sockets": "*", + "php": ">=5.6" }, - "conflict": { - "zendframework/zend-validator": "<2.10.1" + "replace": { + "videlalvaro/php-amqplib": "self.version" }, "require-dev": { - "pear/archive_tar": "^1.4.3", - "phpunit/phpunit": "^5.7.23 || ^6.4.3", - "psr/http-factory": "^1.0", - "zendframework/zend-coding-standard": "~1.0.0", - "zendframework/zend-crypt": "^3.2.1", - "zendframework/zend-servicemanager": "^2.7.8 || ^3.3", - "zendframework/zend-uri": "^2.6" - }, - "suggest": { - "psr/http-factory-implementation": "psr/http-factory-implementation, for creating file upload instances when consuming PSR-7 in file upload filters", - "zendframework/zend-crypt": "Zend\\Crypt component, for encryption filters", - "zendframework/zend-i18n": "Zend\\I18n component for filters depending on i18n functionality", - "zendframework/zend-servicemanager": "Zend\\ServiceManager component, for using the filter chain functionality", - "zendframework/zend-uri": "Zend\\Uri component, for the UriNormalize filter" + "ext-curl": "*", + "nategood/httpful": "^0.2.20", + "phpunit/phpunit": "^5.7|^6.5|^7.0", + "squizlabs/php_codesniffer": "^2.5" }, "type": "library", "extra": { "branch-alias": { - "dev-master": "2.9.x-dev", - "dev-develop": "2.10.x-dev" + "dev-master": "2.10-dev" + } + }, + "autoload": { + "psr-4": { + "PhpAmqpLib\\": "PhpAmqpLib/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "LGPL-2.1-or-later" + ], + "authors": [ + { + "name": "Alvaro Videla", + "role": "Original Maintainer" + }, + { + "name": "John Kelly", + "email": "johnmkelly86@gmail.com", + "role": "Maintainer" + }, + { + "name": "Raúl Araya", + "email": "nubeiro@gmail.com", + "role": "Maintainer" }, - "zf": { - "component": "Zend\\Filter", - "config-provider": "Zend\\Filter\\ConfigProvider" + { + "name": "Luke Bakken", + "email": "luke@bakken.io", + "role": "Maintainer" } + ], + "description": "Formerly videlalvaro/php-amqplib. This library is a pure PHP implementation of the AMQP protocol. It's been tested against RabbitMQ.", + "homepage": "https://github.com/php-amqplib/php-amqplib/", + "keywords": [ + "message", + "queue", + "rabbitmq" + ], + "time": "2019-10-10T13:23:40+00:00" + }, + { + "name": "phpseclib/mcrypt_compat", + "version": "1.0.8", + "source": { + "type": "git", + "url": "https://github.com/phpseclib/mcrypt_compat.git", + "reference": "f74c7b1897b62f08f268184b8bb98d9d9ab723b0" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/phpseclib/mcrypt_compat/zipball/f74c7b1897b62f08f268184b8bb98d9d9ab723b0", + "reference": "f74c7b1897b62f08f268184b8bb98d9d9ab723b0", + "shasum": "" + }, + "require": { + "php": ">=5.3.3", + "phpseclib/phpseclib": ">=2.0.11 <3.0.0" + }, + "require-dev": { + "phpunit/phpunit": "^4.8.35|^5.7|^6.0" }, + "suggest": { + "ext-openssl": "Will enable faster cryptographic operations" + }, + "type": "library", "autoload": { + "files": [ + "lib/mcrypt.php" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Jim Wigginton", + "email": "terrafrost@php.net", + "homepage": "http://phpseclib.sourceforge.net" + } + ], + "description": "PHP 7.1 polyfill for the mcrypt extension from PHP <= 7.0", + "keywords": [ + "cryptograpy", + "encryption", + "mcrypt" + ], + "time": "2018-08-22T03:11:43+00:00" + }, + { + "name": "phpseclib/phpseclib", + "version": "2.0.26", + "source": { + "type": "git", + "url": "https://github.com/phpseclib/phpseclib.git", + "reference": "09655fcc1f8bab65727be036b28f6f20311c126c" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/phpseclib/phpseclib/zipball/09655fcc1f8bab65727be036b28f6f20311c126c", + "reference": "09655fcc1f8bab65727be036b28f6f20311c126c", + "shasum": "" + }, + "require": { + "php": ">=5.3.3" + }, + "require-dev": { + "phing/phing": "~2.7", + "phpunit/phpunit": "^4.8.35|^5.7|^6.0", + "sami/sami": "~2.0", + "squizlabs/php_codesniffer": "~2.0" + }, + "suggest": { + "ext-gmp": "Install the GMP (GNU Multiple Precision) extension in order to speed up arbitrary precision integer arithmetic operations.", + "ext-libsodium": "SSH2/SFTP can make use of some algorithms provided by the libsodium-php extension.", + "ext-mcrypt": "Install the Mcrypt extension in order to speed up a few other cryptographic operations.", + "ext-openssl": "Install the OpenSSL extension in order to speed up a wide variety of cryptographic operations." + }, + "type": "library", + "autoload": { + "files": [ + "phpseclib/bootstrap.php" + ], "psr-4": { - "Zend\\Filter\\": "src/" + "phpseclib\\": "phpseclib/" } }, "notification-url": "https://packagist.org/downloads/", "license": [ - "BSD-3-Clause" + "MIT" ], - "description": "Programmatically filter and normalize data and files", + "authors": [ + { + "name": "Jim Wigginton", + "email": "terrafrost@php.net", + "role": "Lead Developer" + }, + { + "name": "Patrick Monnerat", + "email": "pm@datasphere.ch", + "role": "Developer" + }, + { + "name": "Andreas Fischer", + "email": "bantu@phpbb.com", + "role": "Developer" + }, + { + "name": "Hans-Jürgen Petrich", + "email": "petrich@tronic-media.com", + "role": "Developer" + }, + { + "name": "Graham Campbell", + "email": "graham@alt-three.com", + "role": "Developer" + } + ], + "description": "PHP Secure Communications Library - Pure-PHP implementations of RSA, AES, SSH2, SFTP, X.509 etc.", + "homepage": "http://phpseclib.sourceforge.net", "keywords": [ - "ZendFramework", - "filter", - "zf" + "BigInteger", + "aes", + "asn.1", + "asn1", + "blowfish", + "crypto", + "cryptography", + "encryption", + "rsa", + "security", + "sftp", + "signature", + "signing", + "ssh", + "twofish", + "x.509", + "x509" ], - "time": "2019-08-19T07:08:04+00:00" + "time": "2020-03-13T04:15:39+00:00" }, { - "name": "zendframework/zend-form", - "version": "2.14.3", + "name": "psr/container", + "version": "1.0.0", "source": { "type": "git", - "url": "https://github.com/zendframework/zend-form.git", - "reference": "0b1616c59b1f3df194284e26f98c81ad0c377871" + "url": "https://github.com/php-fig/container.git", + "reference": "b7ce3b176482dbbc1245ebf52b181af44c2cf55f" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/zendframework/zend-form/zipball/0b1616c59b1f3df194284e26f98c81ad0c377871", - "reference": "0b1616c59b1f3df194284e26f98c81ad0c377871", + "url": "https://api.github.com/repos/php-fig/container/zipball/b7ce3b176482dbbc1245ebf52b181af44c2cf55f", + "reference": "b7ce3b176482dbbc1245ebf52b181af44c2cf55f", "shasum": "" }, "require": { - "php": "^5.6 || ^7.0", - "zendframework/zend-hydrator": "^1.1 || ^2.1 || ^3.0", - "zendframework/zend-inputfilter": "^2.8", - "zendframework/zend-stdlib": "^3.2.1" - }, - "require-dev": { - "doctrine/annotations": "~1.0", - "phpunit/phpunit": "^5.7.23 || ^6.5.3", - "zendframework/zend-cache": "^2.6.1", - "zendframework/zend-captcha": "^2.7.1", - "zendframework/zend-code": "^2.6 || ^3.0", - "zendframework/zend-coding-standard": "~1.0.0", - "zendframework/zend-escaper": "^2.5", - "zendframework/zend-eventmanager": "^2.6.2 || ^3.0", - "zendframework/zend-filter": "^2.6", - "zendframework/zend-i18n": "^2.6", - "zendframework/zend-servicemanager": "^2.7.5 || ^3.0.3", - "zendframework/zend-session": "^2.8.1", - "zendframework/zend-text": "^2.6", - "zendframework/zend-validator": "^2.6", - "zendframework/zend-view": "^2.6.2", - "zendframework/zendservice-recaptcha": "^3.0.0" - }, - "suggest": { - "zendframework/zend-captcha": "^2.7.1, required for using CAPTCHA form elements", - "zendframework/zend-code": "^2.6 || ^3.0, required to use zend-form annotations support", - "zendframework/zend-eventmanager": "^2.6.2 || ^3.0, reuired for zend-form annotations support", - "zendframework/zend-i18n": "^2.6, required when using zend-form view helpers", - "zendframework/zend-servicemanager": "^2.7.5 || ^3.0.3, required to use the form factories or provide services", - "zendframework/zend-view": "^2.6.2, required for using the zend-form view helpers", - "zendframework/zendservice-recaptcha": "in order to use the ReCaptcha form element" + "php": ">=5.3.0" }, "type": "library", "extra": { "branch-alias": { - "dev-master": "2.14.x-dev", - "dev-develop": "2.15.x-dev" - }, - "zf": { - "component": "Zend\\Form", - "config-provider": "Zend\\Form\\ConfigProvider" + "dev-master": "1.0.x-dev" } }, "autoload": { "psr-4": { - "Zend\\Form\\": "src/" - }, - "files": [ - "autoload/formElementManagerPolyfill.php" - ] + "Psr\\Container\\": "src/" + } }, "notification-url": "https://packagist.org/downloads/", "license": [ - "BSD-3-Clause" + "MIT" ], - "description": "Validate and display simple and complex forms, casting forms to business objects and vice versa", + "authors": [ + { + "name": "PHP-FIG", + "homepage": "http://www.php-fig.org/" + } + ], + "description": "Common Container Interface (PHP FIG PSR-11)", + "homepage": "https://github.com/php-fig/container", "keywords": [ - "ZendFramework", - "form", - "zf" + "PSR-11", + "container", + "container-interface", + "container-interop", + "psr" ], - "time": "2019-10-04T10:46:36+00:00" + "time": "2017-02-14T16:28:37+00:00" }, { - "name": "zendframework/zend-http", - "version": "2.10.0", + "name": "psr/http-message", + "version": "1.0.1", "source": { "type": "git", - "url": "https://github.com/zendframework/zend-http.git", - "reference": "4b4983178693a8fdda53b0bbee58552e2d2b1ac0" + "url": "https://github.com/php-fig/http-message.git", + "reference": "f6561bf28d520154e4b0ec72be95418abe6d9363" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/zendframework/zend-http/zipball/4b4983178693a8fdda53b0bbee58552e2d2b1ac0", - "reference": "4b4983178693a8fdda53b0bbee58552e2d2b1ac0", + "url": "https://api.github.com/repos/php-fig/http-message/zipball/f6561bf28d520154e4b0ec72be95418abe6d9363", + "reference": "f6561bf28d520154e4b0ec72be95418abe6d9363", "shasum": "" }, "require": { - "php": "^5.6 || ^7.0", - "zendframework/zend-loader": "^2.5.1", - "zendframework/zend-stdlib": "^3.2.1", - "zendframework/zend-uri": "^2.5.2", - "zendframework/zend-validator": "^2.10.1" - }, - "require-dev": { - "phpunit/phpunit": "^5.7.27 || ^6.5.8 || ^7.1.3", - "zendframework/zend-coding-standard": "~1.0.0", - "zendframework/zend-config": "^3.1 || ^2.6" - }, - "suggest": { - "paragonie/certainty": "For automated management of cacert.pem" + "php": ">=5.3.0" }, "type": "library", "extra": { "branch-alias": { - "dev-master": "2.10.x-dev", - "dev-develop": "2.11.x-dev" + "dev-master": "1.0.x-dev" } }, "autoload": { "psr-4": { - "Zend\\Http\\": "src/" + "Psr\\Http\\Message\\": "src/" } }, "notification-url": "https://packagist.org/downloads/", "license": [ - "BSD-3-Clause" + "MIT" ], - "description": "Provides an easy interface for performing Hyper-Text Transfer Protocol (HTTP) requests", + "authors": [ + { + "name": "PHP-FIG", + "homepage": "http://www.php-fig.org/" + } + ], + "description": "Common interface for HTTP messages", + "homepage": "https://github.com/php-fig/http-message", "keywords": [ - "ZendFramework", "http", - "http client", - "zend", - "zf" + "http-message", + "psr", + "psr-7", + "request", + "response" ], - "time": "2019-02-19T18:58:14+00:00" + "time": "2016-08-06T14:39:51+00:00" }, { - "name": "zendframework/zend-hydrator", - "version": "2.4.2", + "name": "psr/log", + "version": "1.1.3", "source": { "type": "git", - "url": "https://github.com/zendframework/zend-hydrator.git", - "reference": "2bfc6845019e7b6d38b0ab5e55190244dc510285" + "url": "https://github.com/php-fig/log.git", + "reference": "0f73288fd15629204f9d42b7055f72dacbe811fc" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/zendframework/zend-hydrator/zipball/2bfc6845019e7b6d38b0ab5e55190244dc510285", - "reference": "2bfc6845019e7b6d38b0ab5e55190244dc510285", + "url": "https://api.github.com/repos/php-fig/log/zipball/0f73288fd15629204f9d42b7055f72dacbe811fc", + "reference": "0f73288fd15629204f9d42b7055f72dacbe811fc", "shasum": "" }, "require": { - "php": "^5.6 || ^7.0", - "zendframework/zend-stdlib": "^3.0" - }, - "require-dev": { - "phpunit/phpunit": "^5.7.27 || ^6.5.8 || ^7.1.2", - "zendframework/zend-coding-standard": "~1.0.0", - "zendframework/zend-eventmanager": "^2.6.2 || ^3.0", - "zendframework/zend-filter": "^2.6", - "zendframework/zend-inputfilter": "^2.6", - "zendframework/zend-serializer": "^2.6.1", - "zendframework/zend-servicemanager": "^2.7.5 || ^3.0.3" - }, - "suggest": { - "zendframework/zend-eventmanager": "^2.6.2 || ^3.0, to support aggregate hydrator usage", - "zendframework/zend-filter": "^2.6, to support naming strategy hydrator usage", - "zendframework/zend-serializer": "^2.6.1, to use the SerializableStrategy", - "zendframework/zend-servicemanager": "^2.7.5 || ^3.0.3, to support hydrator plugin manager usage" + "php": ">=5.3.0" }, "type": "library", "extra": { "branch-alias": { - "dev-release-2.4": "2.4.x-dev" - }, - "zf": { - "component": "Zend\\Hydrator", - "config-provider": "Zend\\Hydrator\\ConfigProvider" + "dev-master": "1.1.x-dev" } }, "autoload": { "psr-4": { - "Zend\\Hydrator\\": "src/" + "Psr\\Log\\": "Psr/Log/" } }, "notification-url": "https://packagist.org/downloads/", "license": [ - "BSD-3-Clause" + "MIT" ], - "description": "Serialize objects to arrays, and vice versa", + "authors": [ + { + "name": "PHP-FIG", + "homepage": "http://www.php-fig.org/" + } + ], + "description": "Common interface for logging libraries", + "homepage": "https://github.com/php-fig/log", "keywords": [ - "ZendFramework", - "hydrator", - "zf" + "log", + "psr", + "psr-3" ], - "time": "2019-10-04T11:17:36+00:00" + "time": "2020-03-23T09:12:05+00:00" }, { - "name": "zendframework/zend-i18n", - "version": "2.9.2", + "name": "ralouphie/getallheaders", + "version": "3.0.3", "source": { "type": "git", - "url": "https://github.com/zendframework/zend-i18n.git", - "reference": "e17a54b3aee333ab156958f570cde630acee8b07" + "url": "https://github.com/ralouphie/getallheaders.git", + "reference": "120b605dfeb996808c31b6477290a714d356e822" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/zendframework/zend-i18n/zipball/e17a54b3aee333ab156958f570cde630acee8b07", - "reference": "e17a54b3aee333ab156958f570cde630acee8b07", + "url": "https://api.github.com/repos/ralouphie/getallheaders/zipball/120b605dfeb996808c31b6477290a714d356e822", + "reference": "120b605dfeb996808c31b6477290a714d356e822", "shasum": "" }, "require": { - "php": "^5.6 || ^7.0", - "zendframework/zend-stdlib": "^2.7 || ^3.0" + "php": ">=5.6" }, "require-dev": { - "phpunit/phpunit": "^5.7.27 || ^6.5.14 || ^7.5.16", - "zendframework/zend-cache": "^2.6.1", - "zendframework/zend-coding-standard": "~1.0.0", - "zendframework/zend-config": "^2.6", - "zendframework/zend-eventmanager": "^2.6.2 || ^3.0", - "zendframework/zend-filter": "^2.6.1", - "zendframework/zend-servicemanager": "^2.7.5 || ^3.0.3", - "zendframework/zend-validator": "^2.6", - "zendframework/zend-view": "^2.6.3" - }, - "suggest": { - "ext-intl": "Required for most features of Zend\\I18n; included in default builds of PHP", - "zendframework/zend-cache": "Zend\\Cache component", - "zendframework/zend-config": "Zend\\Config component", - "zendframework/zend-eventmanager": "You should install this package to use the events in the translator", - "zendframework/zend-filter": "You should install this package to use the provided filters", - "zendframework/zend-i18n-resources": "Translation resources", - "zendframework/zend-servicemanager": "Zend\\ServiceManager component", - "zendframework/zend-validator": "You should install this package to use the provided validators", - "zendframework/zend-view": "You should install this package to use the provided view helpers" + "php-coveralls/php-coveralls": "^2.1", + "phpunit/phpunit": "^5 || ^6.5" }, "type": "library", - "extra": { - "branch-alias": { - "dev-master": "2.9.x-dev", - "dev-develop": "2.10.x-dev" - }, - "zf": { - "component": "Zend\\I18n", - "config-provider": "Zend\\I18n\\ConfigProvider" - } - }, "autoload": { - "psr-4": { - "Zend\\I18n\\": "src/" - } + "files": [ + "src/getallheaders.php" + ] }, "notification-url": "https://packagist.org/downloads/", "license": [ - "BSD-3-Clause" + "MIT" ], - "description": "Provide translations for your application, and filter and validate internationalized values", - "keywords": [ - "ZendFramework", - "i18n", - "zf" + "authors": [ + { + "name": "Ralph Khattar", + "email": "ralph.khattar@gmail.com" + } ], - "time": "2019-09-30T12:04:37+00:00" + "description": "A polyfill for getallheaders.", + "time": "2019-03-08T08:55:37+00:00" }, { - "name": "zendframework/zend-inputfilter", - "version": "2.10.1", + "name": "ramsey/uuid", + "version": "3.8.0", "source": { "type": "git", - "url": "https://github.com/zendframework/zend-inputfilter.git", - "reference": "1f44a2e9bc394a71638b43bc7024b572fa65410e" + "url": "https://github.com/ramsey/uuid.git", + "reference": "d09ea80159c1929d75b3f9c60504d613aeb4a1e3" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/zendframework/zend-inputfilter/zipball/1f44a2e9bc394a71638b43bc7024b572fa65410e", - "reference": "1f44a2e9bc394a71638b43bc7024b572fa65410e", + "url": "https://api.github.com/repos/ramsey/uuid/zipball/d09ea80159c1929d75b3f9c60504d613aeb4a1e3", + "reference": "d09ea80159c1929d75b3f9c60504d613aeb4a1e3", "shasum": "" }, "require": { - "php": "^5.6 || ^7.0", - "zendframework/zend-filter": "^2.9.1", - "zendframework/zend-servicemanager": "^2.7.10 || ^3.3.1", - "zendframework/zend-stdlib": "^2.7 || ^3.0", - "zendframework/zend-validator": "^2.11" + "paragonie/random_compat": "^1.0|^2.0|9.99.99", + "php": "^5.4 || ^7.0", + "symfony/polyfill-ctype": "^1.8" + }, + "replace": { + "rhumsaa/uuid": "self.version" }, "require-dev": { - "phpunit/phpunit": "^5.7.27 || ^6.5.14 || ^7.5.15", - "psr/http-message": "^1.0", - "zendframework/zend-coding-standard": "~1.0.0" + "codeception/aspect-mock": "^1.0 | ~2.0.0", + "doctrine/annotations": "~1.2.0", + "goaop/framework": "1.0.0-alpha.2 | ^1.0 | ~2.1.0", + "ircmaxell/random-lib": "^1.1", + "jakub-onderka/php-parallel-lint": "^0.9.0", + "mockery/mockery": "^0.9.9", + "moontoast/math": "^1.1", + "php-mock/php-mock-phpunit": "^0.3|^1.1", + "phpunit/phpunit": "^4.7|^5.0|^6.5", + "squizlabs/php_codesniffer": "^2.3" }, "suggest": { - "psr/http-message-implementation": "PSR-7 is required if you wish to validate PSR-7 UploadedFileInterface payloads" + "ext-ctype": "Provides support for PHP Ctype functions", + "ext-libsodium": "Provides the PECL libsodium extension for use with the SodiumRandomGenerator", + "ext-uuid": "Provides the PECL UUID extension for use with the PeclUuidTimeGenerator and PeclUuidRandomGenerator", + "ircmaxell/random-lib": "Provides RandomLib for use with the RandomLibAdapter", + "moontoast/math": "Provides support for converting UUID to 128-bit integer (in string form).", + "ramsey/uuid-console": "A console application for generating UUIDs with ramsey/uuid", + "ramsey/uuid-doctrine": "Allows the use of Ramsey\\Uuid\\Uuid as Doctrine field type." }, "type": "library", "extra": { "branch-alias": { - "dev-master": "2.10.x-dev", - "dev-develop": "2.11.x-dev" - }, - "zf": { - "component": "Zend\\InputFilter", - "config-provider": "Zend\\InputFilter\\ConfigProvider" + "dev-master": "3.x-dev" } }, "autoload": { "psr-4": { - "Zend\\InputFilter\\": "src/" + "Ramsey\\Uuid\\": "src/" } }, "notification-url": "https://packagist.org/downloads/", "license": [ - "BSD-3-Clause" + "MIT" ], - "description": "Normalize and validate input sets from the web, APIs, the CLI, and more, including files", + "authors": [ + { + "name": "Ben Ramsey", + "email": "ben@benramsey.com", + "homepage": "https://benramsey.com" + }, + { + "name": "Marijn Huizendveld", + "email": "marijn.huizendveld@gmail.com" + }, + { + "name": "Thibaud Fabre", + "email": "thibaud@aztech.io" + } + ], + "description": "Formerly rhumsaa/uuid. A PHP 5.4+ library for generating RFC 4122 version 1, 3, 4, and 5 universally unique identifiers (UUID).", + "homepage": "https://github.com/ramsey/uuid", "keywords": [ - "ZendFramework", - "inputfilter", - "zf" + "guid", + "identifier", + "uuid" ], - "time": "2019-08-28T19:45:32+00:00" + "time": "2018-07-19T23:38:55+00:00" }, { - "name": "zendframework/zend-json", - "version": "2.6.1", + "name": "react/promise", + "version": "v2.7.1", "source": { "type": "git", - "url": "https://github.com/zendframework/zend-json.git", - "reference": "4c8705dbe4ad7d7e51b2876c5b9eea0ef916ba28" + "url": "https://github.com/reactphp/promise.git", + "reference": "31ffa96f8d2ed0341a57848cbb84d88b89dd664d" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/zendframework/zend-json/zipball/4c8705dbe4ad7d7e51b2876c5b9eea0ef916ba28", - "reference": "4c8705dbe4ad7d7e51b2876c5b9eea0ef916ba28", + "url": "https://api.github.com/repos/reactphp/promise/zipball/31ffa96f8d2ed0341a57848cbb84d88b89dd664d", + "reference": "31ffa96f8d2ed0341a57848cbb84d88b89dd664d", "shasum": "" }, "require": { - "php": "^5.5 || ^7.0" + "php": ">=5.4.0" }, "require-dev": { - "fabpot/php-cs-fixer": "1.7.*", - "phpunit/phpunit": "~4.0", - "zendframework/zend-http": "^2.5.4", - "zendframework/zend-server": "^2.6.1", - "zendframework/zend-stdlib": "^2.5 || ^3.0", - "zendframework/zendxml": "^1.0.2" - }, - "suggest": { - "zendframework/zend-http": "Zend\\Http component, required to use Zend\\Json\\Server", - "zendframework/zend-server": "Zend\\Server component, required to use Zend\\Json\\Server", - "zendframework/zend-stdlib": "Zend\\Stdlib component, for use with caching Zend\\Json\\Server responses", - "zendframework/zendxml": "To support Zend\\Json\\Json::fromXml() usage" + "phpunit/phpunit": "~4.8" }, "type": "library", - "extra": { - "branch-alias": { - "dev-master": "2.6-dev", - "dev-develop": "2.7-dev" - } - }, "autoload": { "psr-4": { - "Zend\\Json\\": "src/" - } + "React\\Promise\\": "src/" + }, + "files": [ + "src/functions_include.php" + ] }, "notification-url": "https://packagist.org/downloads/", "license": [ - "BSD-3-Clause" + "MIT" ], - "description": "provides convenience methods for serializing native PHP to JSON and decoding JSON to native PHP", - "homepage": "https://github.com/zendframework/zend-json", + "authors": [ + { + "name": "Jan Sorgalla", + "email": "jsorgalla@gmail.com" + } + ], + "description": "A lightweight implementation of CommonJS Promises/A for PHP", "keywords": [ - "json", - "zf2" + "promise", + "promises" ], - "time": "2016-02-04T21:20:26+00:00" + "time": "2019-01-07T21:25:54+00:00" }, { - "name": "zendframework/zend-loader", - "version": "2.6.1", + "name": "seld/jsonlint", + "version": "1.7.2", "source": { "type": "git", - "url": "https://github.com/zendframework/zend-loader.git", - "reference": "91da574d29b58547385b2298c020b257310898c6" + "url": "https://github.com/Seldaek/jsonlint.git", + "reference": "e2e5d290e4d2a4f0eb449f510071392e00e10d19" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/zendframework/zend-loader/zipball/91da574d29b58547385b2298c020b257310898c6", - "reference": "91da574d29b58547385b2298c020b257310898c6", + "url": "https://api.github.com/repos/Seldaek/jsonlint/zipball/e2e5d290e4d2a4f0eb449f510071392e00e10d19", + "reference": "e2e5d290e4d2a4f0eb449f510071392e00e10d19", "shasum": "" }, "require": { - "php": "^5.6 || ^7.0" + "php": "^5.3 || ^7.0" }, "require-dev": { - "phpunit/phpunit": "^5.7.27 || ^6.5.8 || ^7.1.4", - "zendframework/zend-coding-standard": "~1.0.0" + "phpunit/phpunit": "^4.8.35 || ^5.7 || ^6.0" }, + "bin": [ + "bin/jsonlint" + ], "type": "library", - "extra": { - "branch-alias": { - "dev-master": "2.6.x-dev", - "dev-develop": "2.7.x-dev" - } - }, "autoload": { "psr-4": { - "Zend\\Loader\\": "src/" + "Seld\\JsonLint\\": "src/Seld/JsonLint/" } }, "notification-url": "https://packagist.org/downloads/", "license": [ - "BSD-3-Clause" + "MIT" ], - "description": "Autoloading and plugin loading strategies", + "authors": [ + { + "name": "Jordi Boggiano", + "email": "j.boggiano@seld.be", + "homepage": "http://seld.be" + } + ], + "description": "JSON Linter", "keywords": [ - "ZendFramework", - "loader", - "zf" + "json", + "linter", + "parser", + "validator" ], - "time": "2019-09-04T19:38:14+00:00" + "time": "2019-10-24T14:27:39+00:00" }, { - "name": "zendframework/zend-log", - "version": "2.11.0", + "name": "seld/phar-utils", + "version": "1.1.0", "source": { "type": "git", - "url": "https://github.com/zendframework/zend-log.git", - "reference": "cb278772afdacb1924342248a069330977625ae6" + "url": "https://github.com/Seldaek/phar-utils.git", + "reference": "8800503d56b9867d43d9c303b9cbcc26016e82f0" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/zendframework/zend-log/zipball/cb278772afdacb1924342248a069330977625ae6", - "reference": "cb278772afdacb1924342248a069330977625ae6", + "url": "https://api.github.com/repos/Seldaek/phar-utils/zipball/8800503d56b9867d43d9c303b9cbcc26016e82f0", + "reference": "8800503d56b9867d43d9c303b9cbcc26016e82f0", "shasum": "" }, "require": { - "php": "^5.6 || ^7.0", - "psr/log": "^1.0", - "zendframework/zend-servicemanager": "^2.7.5 || ^3.0.3", - "zendframework/zend-stdlib": "^2.7 || ^3.0" - }, - "provide": { - "psr/log-implementation": "1.0.0" - }, - "require-dev": { - "mikey179/vfsstream": "^1.6.7", - "phpunit/phpunit": "^5.7.27 || ^6.5.14 || ^7.5.15", - "zendframework/zend-coding-standard": "~1.0.0", - "zendframework/zend-db": "^2.6", - "zendframework/zend-escaper": "^2.5", - "zendframework/zend-filter": "^2.5", - "zendframework/zend-mail": "^2.6.1", - "zendframework/zend-validator": "^2.10.1" - }, - "suggest": { - "ext-mongo": "mongo extension to use Mongo writer", - "ext-mongodb": "mongodb extension to use MongoDB writer", - "zendframework/zend-db": "Zend\\Db component to use the database log writer", - "zendframework/zend-escaper": "Zend\\Escaper component, for use in the XML log formatter", - "zendframework/zend-mail": "Zend\\Mail component to use the email log writer", - "zendframework/zend-validator": "Zend\\Validator component to block invalid log messages" + "php": ">=5.3" }, "type": "library", "extra": { "branch-alias": { - "dev-master": "2.11.x-dev", - "dev-develop": "2.12.x-dev" - }, - "zf": { - "component": "Zend\\Log", - "config-provider": "Zend\\Log\\ConfigProvider" + "dev-master": "1.x-dev" } }, "autoload": { "psr-4": { - "Zend\\Log\\": "src/" + "Seld\\PharUtils\\": "src/" } }, "notification-url": "https://packagist.org/downloads/", "license": [ - "BSD-3-Clause" + "MIT" ], - "description": "Robust, composite logger with filtering, formatting, and PSR-3 support", + "authors": [ + { + "name": "Jordi Boggiano", + "email": "j.boggiano@seld.be" + } + ], + "description": "PHAR file format utilities, for when PHP phars you up", "keywords": [ - "ZendFramework", - "log", - "logging", - "zf" + "phar" ], - "time": "2019-08-23T21:28:18+00:00" + "time": "2020-02-14T15:25:33+00:00" }, { - "name": "zendframework/zend-mail", - "version": "2.10.0", + "name": "symfony/console", + "version": "v4.4.5", "source": { "type": "git", - "url": "https://github.com/zendframework/zend-mail.git", - "reference": "d7beb63d5f7144a21ac100072c453e63860cdab8" + "url": "https://github.com/symfony/console.git", + "reference": "4fa15ae7be74e53f6ec8c83ed403b97e23b665e9" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/zendframework/zend-mail/zipball/d7beb63d5f7144a21ac100072c453e63860cdab8", - "reference": "d7beb63d5f7144a21ac100072c453e63860cdab8", + "url": "https://api.github.com/repos/symfony/console/zipball/4fa15ae7be74e53f6ec8c83ed403b97e23b665e9", + "reference": "4fa15ae7be74e53f6ec8c83ed403b97e23b665e9", "shasum": "" }, "require": { - "ext-iconv": "*", - "php": "^5.6 || ^7.0", - "true/punycode": "^2.1", - "zendframework/zend-loader": "^2.5", - "zendframework/zend-mime": "^2.5", - "zendframework/zend-stdlib": "^2.7 || ^3.0", - "zendframework/zend-validator": "^2.10.2" + "php": "^7.1.3", + "symfony/polyfill-mbstring": "~1.0", + "symfony/polyfill-php73": "^1.8", + "symfony/service-contracts": "^1.1|^2" + }, + "conflict": { + "symfony/dependency-injection": "<3.4", + "symfony/event-dispatcher": "<4.3|>=5", + "symfony/lock": "<4.4", + "symfony/process": "<3.3" + }, + "provide": { + "psr/log-implementation": "1.0" }, "require-dev": { - "phpunit/phpunit": "^5.7.25 || ^6.4.4 || ^7.1.4", - "zendframework/zend-coding-standard": "~1.0.0", - "zendframework/zend-config": "^2.6", - "zendframework/zend-crypt": "^2.6 || ^3.0", - "zendframework/zend-servicemanager": "^2.7.10 || ^3.3.1" + "psr/log": "~1.0", + "symfony/config": "^3.4|^4.0|^5.0", + "symfony/dependency-injection": "^3.4|^4.0|^5.0", + "symfony/event-dispatcher": "^4.3", + "symfony/lock": "^4.4|^5.0", + "symfony/process": "^3.4|^4.0|^5.0", + "symfony/var-dumper": "^4.3|^5.0" }, "suggest": { - "zendframework/zend-crypt": "Crammd5 support in SMTP Auth", - "zendframework/zend-servicemanager": "^2.7.10 || ^3.3.1 when using SMTP to deliver messages" + "psr/log": "For using the console logger", + "symfony/event-dispatcher": "", + "symfony/lock": "", + "symfony/process": "" }, "type": "library", "extra": { "branch-alias": { - "dev-master": "2.10.x-dev", - "dev-develop": "2.11.x-dev" - }, - "zf": { - "component": "Zend\\Mail", - "config-provider": "Zend\\Mail\\ConfigProvider" + "dev-master": "4.4-dev" } }, "autoload": { "psr-4": { - "Zend\\Mail\\": "src/" - } + "Symfony\\Component\\Console\\": "" + }, + "exclude-from-classmap": [ + "/Tests/" + ] }, "notification-url": "https://packagist.org/downloads/", "license": [ - "BSD-3-Clause" + "MIT" ], - "description": "Provides generalized functionality to compose and send both text and MIME-compliant multipart e-mail messages", - "keywords": [ - "ZendFramework", - "mail", - "zf" + "authors": [ + { + "name": "Fabien Potencier", + "email": "fabien@symfony.com" + }, + { + "name": "Symfony Community", + "homepage": "https://symfony.com/contributors" + } ], - "time": "2018-06-07T13:37:07+00:00" + "description": "Symfony Console Component", + "homepage": "https://symfony.com", + "time": "2020-02-24T13:10:00+00:00" }, { - "name": "zendframework/zend-math", - "version": "2.7.1", + "name": "symfony/css-selector", + "version": "v4.4.5", "source": { "type": "git", - "url": "https://github.com/zendframework/zend-math.git", - "reference": "1abce074004dacac1a32cd54de94ad47ef960d38" + "url": "https://github.com/symfony/css-selector.git", + "reference": "d0a6dd288fa8848dcc3d1f58b94de6a7cc5d2d22" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/zendframework/zend-math/zipball/1abce074004dacac1a32cd54de94ad47ef960d38", - "reference": "1abce074004dacac1a32cd54de94ad47ef960d38", + "url": "https://api.github.com/repos/symfony/css-selector/zipball/d0a6dd288fa8848dcc3d1f58b94de6a7cc5d2d22", + "reference": "d0a6dd288fa8848dcc3d1f58b94de6a7cc5d2d22", "shasum": "" }, "require": { - "php": "^5.5 || ^7.0" - }, - "require-dev": { - "fabpot/php-cs-fixer": "1.7.*", - "ircmaxell/random-lib": "~1.1", - "phpunit/phpunit": "~4.0" - }, - "suggest": { - "ext-bcmath": "If using the bcmath functionality", - "ext-gmp": "If using the gmp functionality", - "ircmaxell/random-lib": "Fallback random byte generator for Zend\\Math\\Rand if Mcrypt extensions is unavailable" + "php": "^7.1.3" }, "type": "library", "extra": { "branch-alias": { - "dev-master": "2.7-dev", - "dev-develop": "2.8-dev" + "dev-master": "4.4-dev" } }, "autoload": { "psr-4": { - "Zend\\Math\\": "src/" - } + "Symfony\\Component\\CssSelector\\": "" + }, + "exclude-from-classmap": [ + "/Tests/" + ] }, "notification-url": "https://packagist.org/downloads/", "license": [ - "BSD-3-Clause" + "MIT" ], - "homepage": "https://github.com/zendframework/zend-math", - "keywords": [ - "math", - "zf2" + "authors": [ + { + "name": "Fabien Potencier", + "email": "fabien@symfony.com" + }, + { + "name": "Jean-François Simon", + "email": "jeanfrancois.simon@sensiolabs.com" + }, + { + "name": "Symfony Community", + "homepage": "https://symfony.com/contributors" + } ], - "time": "2018-12-04T15:34:17+00:00" + "description": "Symfony CssSelector Component", + "homepage": "https://symfony.com", + "time": "2020-02-04T09:01:01+00:00" }, { - "name": "zendframework/zend-mime", - "version": "2.7.2", + "name": "symfony/event-dispatcher", + "version": "v4.4.5", "source": { "type": "git", - "url": "https://github.com/zendframework/zend-mime.git", - "reference": "c91e0350be53cc9d29be15563445eec3b269d7c1" + "url": "https://github.com/symfony/event-dispatcher.git", + "reference": "4ad8e149799d3128621a3a1f70e92b9897a8930d" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/zendframework/zend-mime/zipball/c91e0350be53cc9d29be15563445eec3b269d7c1", - "reference": "c91e0350be53cc9d29be15563445eec3b269d7c1", + "url": "https://api.github.com/repos/symfony/event-dispatcher/zipball/4ad8e149799d3128621a3a1f70e92b9897a8930d", + "reference": "4ad8e149799d3128621a3a1f70e92b9897a8930d", "shasum": "" }, "require": { - "php": "^5.6 || ^7.0", - "zendframework/zend-stdlib": "^2.7 || ^3.0" + "php": "^7.1.3", + "symfony/event-dispatcher-contracts": "^1.1" + }, + "conflict": { + "symfony/dependency-injection": "<3.4" + }, + "provide": { + "psr/event-dispatcher-implementation": "1.0", + "symfony/event-dispatcher-implementation": "1.1" }, "require-dev": { - "phpunit/phpunit": "^5.7.21 || ^6.3", - "zendframework/zend-coding-standard": "~1.0.0", - "zendframework/zend-mail": "^2.6" + "psr/log": "~1.0", + "symfony/config": "^3.4|^4.0|^5.0", + "symfony/dependency-injection": "^3.4|^4.0|^5.0", + "symfony/expression-language": "^3.4|^4.0|^5.0", + "symfony/http-foundation": "^3.4|^4.0|^5.0", + "symfony/service-contracts": "^1.1|^2", + "symfony/stopwatch": "^3.4|^4.0|^5.0" }, "suggest": { - "zendframework/zend-mail": "Zend\\Mail component" + "symfony/dependency-injection": "", + "symfony/http-kernel": "" }, "type": "library", "extra": { "branch-alias": { - "dev-master": "2.7.x-dev", - "dev-develop": "2.8.x-dev" + "dev-master": "4.4-dev" } }, "autoload": { "psr-4": { - "Zend\\Mime\\": "src/" - } + "Symfony\\Component\\EventDispatcher\\": "" + }, + "exclude-from-classmap": [ + "/Tests/" + ] }, "notification-url": "https://packagist.org/downloads/", "license": [ - "BSD-3-Clause" + "MIT" ], - "description": "Create and parse MIME messages and parts", - "keywords": [ - "ZendFramework", - "mime", - "zf" + "authors": [ + { + "name": "Fabien Potencier", + "email": "fabien@symfony.com" + }, + { + "name": "Symfony Community", + "homepage": "https://symfony.com/contributors" + } ], - "time": "2019-10-16T19:30:37+00:00" + "description": "Symfony EventDispatcher Component", + "homepage": "https://symfony.com", + "time": "2020-02-04T09:32:40+00:00" }, { - "name": "zendframework/zend-modulemanager", - "version": "2.8.4", + "name": "symfony/event-dispatcher-contracts", + "version": "v1.1.7", "source": { "type": "git", - "url": "https://github.com/zendframework/zend-modulemanager.git", - "reference": "b2596d24b9a4e36a3cd114d35d3ad0918db9a243" + "url": "https://github.com/symfony/event-dispatcher-contracts.git", + "reference": "c43ab685673fb6c8d84220c77897b1d6cdbe1d18" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/zendframework/zend-modulemanager/zipball/b2596d24b9a4e36a3cd114d35d3ad0918db9a243", - "reference": "b2596d24b9a4e36a3cd114d35d3ad0918db9a243", + "url": "https://api.github.com/repos/symfony/event-dispatcher-contracts/zipball/c43ab685673fb6c8d84220c77897b1d6cdbe1d18", + "reference": "c43ab685673fb6c8d84220c77897b1d6cdbe1d18", "shasum": "" }, "require": { - "php": "^5.6 || ^7.0", - "zendframework/zend-config": "^3.1 || ^2.6", - "zendframework/zend-eventmanager": "^3.2 || ^2.6.3", - "zendframework/zend-stdlib": "^3.1 || ^2.7" - }, - "require-dev": { - "phpunit/phpunit": "^5.7.27 || ^6.5.14 || ^7.5.16", - "zendframework/zend-coding-standard": "~1.0.0", - "zendframework/zend-console": "^2.6", - "zendframework/zend-di": "^2.6", - "zendframework/zend-loader": "^2.5", - "zendframework/zend-mvc": "^3.0 || ^2.7", - "zendframework/zend-servicemanager": "^3.0.3 || ^2.7.5" + "php": "^7.1.3" }, "suggest": { - "zendframework/zend-console": "Zend\\Console component", - "zendframework/zend-loader": "Zend\\Loader component if you are not using Composer autoloading for your modules", - "zendframework/zend-mvc": "Zend\\Mvc component", - "zendframework/zend-servicemanager": "Zend\\ServiceManager component" + "psr/event-dispatcher": "", + "symfony/event-dispatcher-implementation": "" }, "type": "library", "extra": { "branch-alias": { - "dev-master": "2.8.x-dev", - "dev-develop": "2.9.x-dev" + "dev-master": "1.1-dev" } }, "autoload": { "psr-4": { - "Zend\\ModuleManager\\": "src/" + "Symfony\\Contracts\\EventDispatcher\\": "" } }, "notification-url": "https://packagist.org/downloads/", "license": [ - "BSD-3-Clause" + "MIT" + ], + "authors": [ + { + "name": "Nicolas Grekas", + "email": "p@tchwork.com" + }, + { + "name": "Symfony Community", + "homepage": "https://symfony.com/contributors" + } ], - "description": "Modular application system for zend-mvc applications", + "description": "Generic abstractions related to dispatching event", + "homepage": "https://symfony.com", "keywords": [ - "ZendFramework", - "modulemanager", - "zf" + "abstractions", + "contracts", + "decoupling", + "interfaces", + "interoperability", + "standards" ], - "time": "2019-10-28T13:29:38+00:00" + "time": "2019-09-17T09:54:03+00:00" }, { - "name": "zendframework/zend-mvc", - "version": "2.7.15", + "name": "symfony/filesystem", + "version": "v4.4.5", "source": { "type": "git", - "url": "https://github.com/zendframework/zend-mvc.git", - "reference": "a8d45689d37a9e4ff4b75ea0b7478fa3d4f9c089" + "url": "https://github.com/symfony/filesystem.git", + "reference": "266c9540b475f26122b61ef8b23dd9198f5d1cfd" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/zendframework/zend-mvc/zipball/a8d45689d37a9e4ff4b75ea0b7478fa3d4f9c089", - "reference": "a8d45689d37a9e4ff4b75ea0b7478fa3d4f9c089", + "url": "https://api.github.com/repos/symfony/filesystem/zipball/266c9540b475f26122b61ef8b23dd9198f5d1cfd", + "reference": "266c9540b475f26122b61ef8b23dd9198f5d1cfd", "shasum": "" }, "require": { - "container-interop/container-interop": "^1.1", - "php": "^5.5 || ^7.0", - "zendframework/zend-console": "^2.7", - "zendframework/zend-eventmanager": "^2.6.4 || ^3.0", - "zendframework/zend-form": "^2.11", - "zendframework/zend-hydrator": "^1.1 || ^2.4", - "zendframework/zend-psr7bridge": "^0.2", - "zendframework/zend-servicemanager": "^2.7.10 || ^3.0.3", - "zendframework/zend-stdlib": "^2.7.5 || ^3.0" - }, - "replace": { - "zendframework/zend-router": "^2.0" - }, - "require-dev": { - "friendsofphp/php-cs-fixer": "1.7.*", - "phpunit/phpunit": "^4.8.36", - "sebastian/comparator": "^1.2.4", - "sebastian/version": "^1.0.4", - "zendframework/zend-authentication": "^2.6", - "zendframework/zend-cache": "^2.8", - "zendframework/zend-di": "^2.6", - "zendframework/zend-filter": "^2.8", - "zendframework/zend-http": "^2.8", - "zendframework/zend-i18n": "^2.8", - "zendframework/zend-inputfilter": "^2.8", - "zendframework/zend-json": "^2.6.1", - "zendframework/zend-log": "^2.9.3", - "zendframework/zend-modulemanager": "^2.8", - "zendframework/zend-serializer": "^2.8", - "zendframework/zend-session": "^2.8.1", - "zendframework/zend-text": "^2.7", - "zendframework/zend-uri": "^2.6", - "zendframework/zend-validator": "^2.10", - "zendframework/zend-view": "^2.9" - }, - "suggest": { - "zendframework/zend-authentication": "Zend\\Authentication component for Identity plugin", - "zendframework/zend-config": "Zend\\Config component", - "zendframework/zend-di": "Zend\\Di component", - "zendframework/zend-filter": "Zend\\Filter component", - "zendframework/zend-http": "Zend\\Http component", - "zendframework/zend-i18n": "Zend\\I18n component for translatable segments", - "zendframework/zend-inputfilter": "Zend\\Inputfilter component", - "zendframework/zend-json": "Zend\\Json component", - "zendframework/zend-log": "Zend\\Log component", - "zendframework/zend-modulemanager": "Zend\\ModuleManager component", - "zendframework/zend-serializer": "Zend\\Serializer component", - "zendframework/zend-servicemanager-di": "^1.0.1, if using zend-servicemanager v3 and requiring the zend-di integration", - "zendframework/zend-session": "Zend\\Session component for FlashMessenger, PRG, and FPRG plugins", - "zendframework/zend-text": "Zend\\Text component", - "zendframework/zend-uri": "Zend\\Uri component", - "zendframework/zend-validator": "Zend\\Validator component", - "zendframework/zend-view": "Zend\\View component" + "php": "^7.1.3", + "symfony/polyfill-ctype": "~1.8" }, "type": "library", "extra": { "branch-alias": { - "dev-master": "2.7-dev", - "dev-develop": "3.0-dev" + "dev-master": "4.4-dev" } }, "autoload": { - "files": [ - "src/autoload.php" - ], "psr-4": { - "Zend\\Mvc\\": "src/" - } + "Symfony\\Component\\Filesystem\\": "" + }, + "exclude-from-classmap": [ + "/Tests/" + ] }, "notification-url": "https://packagist.org/downloads/", "license": [ - "BSD-3-Clause" + "MIT" ], - "homepage": "https://github.com/zendframework/zend-mvc", - "keywords": [ - "mvc", - "zf2" + "authors": [ + { + "name": "Fabien Potencier", + "email": "fabien@symfony.com" + }, + { + "name": "Symfony Community", + "homepage": "https://symfony.com/contributors" + } ], - "time": "2018-05-03T13:13:41+00:00" + "description": "Symfony Filesystem Component", + "homepage": "https://symfony.com", + "time": "2020-01-21T08:20:44+00:00" }, { - "name": "zendframework/zend-psr7bridge", - "version": "0.2.2", + "name": "symfony/finder", + "version": "v4.4.5", "source": { "type": "git", - "url": "https://github.com/zendframework/zend-psr7bridge.git", - "reference": "86c0b53b0c6381391c4add4a93a56e51d5c74605" + "url": "https://github.com/symfony/finder.git", + "reference": "ea69c129aed9fdeca781d4b77eb20b62cf5d5357" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/zendframework/zend-psr7bridge/zipball/86c0b53b0c6381391c4add4a93a56e51d5c74605", - "reference": "86c0b53b0c6381391c4add4a93a56e51d5c74605", + "url": "https://api.github.com/repos/symfony/finder/zipball/ea69c129aed9fdeca781d4b77eb20b62cf5d5357", + "reference": "ea69c129aed9fdeca781d4b77eb20b62cf5d5357", "shasum": "" }, "require": { - "php": ">=5.5", - "psr/http-message": "^1.0", - "zendframework/zend-diactoros": "^1.1", - "zendframework/zend-http": "^2.5" - }, - "require-dev": { - "phpunit/phpunit": "^4.7", - "squizlabs/php_codesniffer": "^2.3" + "php": "^7.1.3" }, "type": "library", "extra": { "branch-alias": { - "dev-master": "1.0-dev", - "dev-develop": "1.1-dev" + "dev-master": "4.4-dev" } }, "autoload": { "psr-4": { - "Zend\\Psr7Bridge\\": "src/" - } + "Symfony\\Component\\Finder\\": "" + }, + "exclude-from-classmap": [ + "/Tests/" + ] }, "notification-url": "https://packagist.org/downloads/", "license": [ - "BSD-3-Clause" + "MIT" ], - "description": "PSR-7 <-> Zend\\Http bridge", - "homepage": "https://github.com/zendframework/zend-psr7bridge", - "keywords": [ - "http", - "psr", - "psr-7" + "authors": [ + { + "name": "Fabien Potencier", + "email": "fabien@symfony.com" + }, + { + "name": "Symfony Community", + "homepage": "https://symfony.com/contributors" + } ], - "time": "2016-05-10T21:44:39+00:00" + "description": "Symfony Finder Component", + "homepage": "https://symfony.com", + "time": "2020-02-14T07:42:58+00:00" }, { - "name": "zendframework/zend-serializer", - "version": "2.9.1", + "name": "symfony/polyfill-ctype", + "version": "v1.15.0", "source": { "type": "git", - "url": "https://github.com/zendframework/zend-serializer.git", - "reference": "6fb7ae016cfdf0cfcdfa2b989e6a65f351170e21" + "url": "https://github.com/symfony/polyfill-ctype.git", + "reference": "4719fa9c18b0464d399f1a63bf624b42b6fa8d14" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/zendframework/zend-serializer/zipball/6fb7ae016cfdf0cfcdfa2b989e6a65f351170e21", - "reference": "6fb7ae016cfdf0cfcdfa2b989e6a65f351170e21", + "url": "https://api.github.com/repos/symfony/polyfill-ctype/zipball/4719fa9c18b0464d399f1a63bf624b42b6fa8d14", + "reference": "4719fa9c18b0464d399f1a63bf624b42b6fa8d14", "shasum": "" }, "require": { - "php": "^5.6 || ^7.0", - "zendframework/zend-json": "^2.5 || ^3.0", - "zendframework/zend-stdlib": "^2.7 || ^3.0" - }, - "require-dev": { - "phpunit/phpunit": "^5.7.27 || ^6.5.14 || ^7.5.16", - "zendframework/zend-coding-standard": "~1.0.0", - "zendframework/zend-math": "^2.6 || ^3.0", - "zendframework/zend-servicemanager": "^2.7.5 || ^3.0.3" + "php": ">=5.3.3" }, "suggest": { - "zendframework/zend-math": "(^2.6 || ^3.0) To support Python Pickle serialization", - "zendframework/zend-servicemanager": "(^2.7.5 || ^3.0.3) To support plugin manager support" + "ext-ctype": "For best performance" }, "type": "library", "extra": { "branch-alias": { - "dev-master": "2.9.x-dev", - "dev-develop": "2.10.x-dev" - }, - "zf": { - "component": "Zend\\Serializer", - "config-provider": "Zend\\Serializer\\ConfigProvider" + "dev-master": "1.15-dev" } }, "autoload": { "psr-4": { - "Zend\\Serializer\\": "src/" - } + "Symfony\\Polyfill\\Ctype\\": "" + }, + "files": [ + "bootstrap.php" + ] }, "notification-url": "https://packagist.org/downloads/", "license": [ - "BSD-3-Clause" + "MIT" ], - "description": "Serialize and deserialize PHP structures to a variety of representations", + "authors": [ + { + "name": "Gert de Pagter", + "email": "BackEndTea@gmail.com" + }, + { + "name": "Symfony Community", + "homepage": "https://symfony.com/contributors" + } + ], + "description": "Symfony polyfill for ctype functions", + "homepage": "https://symfony.com", "keywords": [ - "ZendFramework", - "serializer", - "zf" + "compatibility", + "ctype", + "polyfill", + "portable" ], - "time": "2019-10-19T08:06:30+00:00" + "time": "2020-02-27T09:26:54+00:00" }, { - "name": "zendframework/zend-server", - "version": "2.8.1", + "name": "symfony/polyfill-mbstring", + "version": "v1.15.0", "source": { "type": "git", - "url": "https://github.com/zendframework/zend-server.git", - "reference": "d80c44700ebb92191dd9a3005316a6ab6637c0d1" + "url": "https://github.com/symfony/polyfill-mbstring.git", + "reference": "81ffd3a9c6d707be22e3012b827de1c9775fc5ac" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/zendframework/zend-server/zipball/d80c44700ebb92191dd9a3005316a6ab6637c0d1", - "reference": "d80c44700ebb92191dd9a3005316a6ab6637c0d1", + "url": "https://api.github.com/repos/symfony/polyfill-mbstring/zipball/81ffd3a9c6d707be22e3012b827de1c9775fc5ac", + "reference": "81ffd3a9c6d707be22e3012b827de1c9775fc5ac", "shasum": "" }, "require": { - "php": "^5.6 || ^7.0", - "zendframework/zend-code": "^2.5 || ^3.0", - "zendframework/zend-stdlib": "^2.5 || ^3.0" + "php": ">=5.3.3" }, - "require-dev": { - "phpunit/phpunit": "^5.7.27 || ^6.5.8 || ^7.1.4", - "zendframework/zend-coding-standard": "~1.0.0" + "suggest": { + "ext-mbstring": "For best performance" }, "type": "library", "extra": { "branch-alias": { - "dev-master": "2.8.x-dev", - "dev-develop": "2.9.x-dev" + "dev-master": "1.15-dev" } }, "autoload": { "psr-4": { - "Zend\\Server\\": "src/" - } + "Symfony\\Polyfill\\Mbstring\\": "" + }, + "files": [ + "bootstrap.php" + ] }, "notification-url": "https://packagist.org/downloads/", "license": [ - "BSD-3-Clause" + "MIT" ], - "description": "Create Reflection-based RPC servers", + "authors": [ + { + "name": "Nicolas Grekas", + "email": "p@tchwork.com" + }, + { + "name": "Symfony Community", + "homepage": "https://symfony.com/contributors" + } + ], + "description": "Symfony polyfill for the Mbstring extension", + "homepage": "https://symfony.com", "keywords": [ - "ZendFramework", - "server", - "zf" + "compatibility", + "mbstring", + "polyfill", + "portable", + "shim" ], - "time": "2019-10-16T18:27:05+00:00" + "time": "2020-03-09T19:04:49+00:00" }, { - "name": "zendframework/zend-servicemanager", - "version": "2.7.11", + "name": "symfony/polyfill-php73", + "version": "v1.15.0", "source": { "type": "git", - "url": "https://github.com/zendframework/zend-servicemanager.git", - "reference": "99ec9ed5d0f15aed9876433c74c2709eb933d4c7" + "url": "https://github.com/symfony/polyfill-php73.git", + "reference": "0f27e9f464ea3da33cbe7ca3bdf4eb66def9d0f7" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/zendframework/zend-servicemanager/zipball/99ec9ed5d0f15aed9876433c74c2709eb933d4c7", - "reference": "99ec9ed5d0f15aed9876433c74c2709eb933d4c7", + "url": "https://api.github.com/repos/symfony/polyfill-php73/zipball/0f27e9f464ea3da33cbe7ca3bdf4eb66def9d0f7", + "reference": "0f27e9f464ea3da33cbe7ca3bdf4eb66def9d0f7", "shasum": "" }, "require": { - "container-interop/container-interop": "~1.0", - "php": "^5.5 || ^7.0" - }, - "require-dev": { - "athletic/athletic": "dev-master", - "fabpot/php-cs-fixer": "1.7.*", - "phpunit/phpunit": "~4.0", - "zendframework/zend-di": "~2.5", - "zendframework/zend-mvc": "~2.5" - }, - "suggest": { - "ocramius/proxy-manager": "ProxyManager 0.5.* to handle lazy initialization of services", - "zendframework/zend-di": "Zend\\Di component" + "php": ">=5.3.3" }, "type": "library", "extra": { "branch-alias": { - "dev-master": "2.7-dev", - "dev-develop": "3.0-dev" + "dev-master": "1.15-dev" } }, "autoload": { "psr-4": { - "Zend\\ServiceManager\\": "src/" - } + "Symfony\\Polyfill\\Php73\\": "" + }, + "files": [ + "bootstrap.php" + ], + "classmap": [ + "Resources/stubs" + ] }, "notification-url": "https://packagist.org/downloads/", "license": [ - "BSD-3-Clause" + "MIT" + ], + "authors": [ + { + "name": "Nicolas Grekas", + "email": "p@tchwork.com" + }, + { + "name": "Symfony Community", + "homepage": "https://symfony.com/contributors" + } ], - "homepage": "https://github.com/zendframework/zend-servicemanager", + "description": "Symfony polyfill backporting some PHP 7.3+ features to lower PHP versions", + "homepage": "https://symfony.com", "keywords": [ - "servicemanager", - "zf2" + "compatibility", + "polyfill", + "portable", + "shim" ], - "time": "2018-06-22T14:49:54+00:00" + "time": "2020-02-27T09:26:54+00:00" }, { - "name": "zendframework/zend-session", - "version": "2.9.1", + "name": "symfony/process", + "version": "v4.4.5", "source": { "type": "git", - "url": "https://github.com/zendframework/zend-session.git", - "reference": "c289c4d733ec23a389e25c7c451f4d062088511f" + "url": "https://github.com/symfony/process.git", + "reference": "bf9166bac906c9e69fb7a11d94875e7ced97bcd7" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/zendframework/zend-session/zipball/c289c4d733ec23a389e25c7c451f4d062088511f", - "reference": "c289c4d733ec23a389e25c7c451f4d062088511f", + "url": "https://api.github.com/repos/symfony/process/zipball/bf9166bac906c9e69fb7a11d94875e7ced97bcd7", + "reference": "bf9166bac906c9e69fb7a11d94875e7ced97bcd7", "shasum": "" }, "require": { - "php": "^5.6 || ^7.0", - "zendframework/zend-eventmanager": "^2.6.2 || ^3.0", - "zendframework/zend-stdlib": "^3.2.1" - }, - "require-dev": { - "container-interop/container-interop": "^1.1", - "mongodb/mongodb": "^1.0.1", - "php-mock/php-mock-phpunit": "^1.1.2 || ^2.0", - "phpunit/phpunit": "^5.7.27 || ^6.5.14 || ^7.5.16", - "zendframework/zend-cache": "^2.6.1", - "zendframework/zend-coding-standard": "~1.0.0", - "zendframework/zend-db": "^2.7", - "zendframework/zend-http": "^2.5.4", - "zendframework/zend-servicemanager": "^2.7.5 || ^3.0.3", - "zendframework/zend-validator": "^2.6" - }, - "suggest": { - "mongodb/mongodb": "If you want to use the MongoDB session save handler", - "zendframework/zend-cache": "Zend\\Cache component", - "zendframework/zend-db": "Zend\\Db component", - "zendframework/zend-http": "Zend\\Http component", - "zendframework/zend-servicemanager": "Zend\\ServiceManager component", - "zendframework/zend-validator": "Zend\\Validator component" + "php": "^7.1.3" }, "type": "library", "extra": { "branch-alias": { - "dev-master": "2.9.x-dev", - "dev-develop": "2.10.x-dev" - }, - "zf": { - "component": "Zend\\Session", - "config-provider": "Zend\\Session\\ConfigProvider" + "dev-master": "4.4-dev" } }, "autoload": { "psr-4": { - "Zend\\Session\\": "src/" - } + "Symfony\\Component\\Process\\": "" + }, + "exclude-from-classmap": [ + "/Tests/" + ] }, "notification-url": "https://packagist.org/downloads/", "license": [ - "BSD-3-Clause" + "MIT" ], - "description": "Object-oriented interface to PHP sessions and storage", - "keywords": [ - "ZendFramework", - "session", - "zf" + "authors": [ + { + "name": "Fabien Potencier", + "email": "fabien@symfony.com" + }, + { + "name": "Symfony Community", + "homepage": "https://symfony.com/contributors" + } ], - "time": "2019-10-28T19:40:43+00:00" + "description": "Symfony Process Component", + "homepage": "https://symfony.com", + "time": "2020-02-07T20:06:44+00:00" }, { - "name": "zendframework/zend-soap", - "version": "2.8.0", + "name": "symfony/service-contracts", + "version": "v2.0.1", "source": { "type": "git", - "url": "https://github.com/zendframework/zend-soap.git", - "reference": "8762d79efa220d82529c43ce08d70554146be645" + "url": "https://github.com/symfony/service-contracts.git", + "reference": "144c5e51266b281231e947b51223ba14acf1a749" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/zendframework/zend-soap/zipball/8762d79efa220d82529c43ce08d70554146be645", - "reference": "8762d79efa220d82529c43ce08d70554146be645", + "url": "https://api.github.com/repos/symfony/service-contracts/zipball/144c5e51266b281231e947b51223ba14acf1a749", + "reference": "144c5e51266b281231e947b51223ba14acf1a749", "shasum": "" }, "require": { - "ext-soap": "*", - "php": "^5.6 || ^7.0", - "zendframework/zend-server": "^2.6.1", - "zendframework/zend-stdlib": "^2.7 || ^3.0", - "zendframework/zend-uri": "^2.5.2" - }, - "require-dev": { - "phpunit/phpunit": "^5.7.21 || ^6.3", - "zendframework/zend-coding-standard": "~1.0.0", - "zendframework/zend-config": "^2.6", - "zendframework/zend-http": "^2.5.4" + "php": "^7.2.5", + "psr/container": "^1.0" }, "suggest": { - "zendframework/zend-http": "Zend\\Http component" + "symfony/service-implementation": "" }, "type": "library", "extra": { "branch-alias": { - "dev-master": "2.7.x-dev", - "dev-develop": "2.8.x-dev" + "dev-master": "2.0-dev" } }, "autoload": { "psr-4": { - "Zend\\Soap\\": "src/" + "Symfony\\Contracts\\Service\\": "" } }, "notification-url": "https://packagist.org/downloads/", "license": [ - "BSD-3-Clause" + "MIT" + ], + "authors": [ + { + "name": "Nicolas Grekas", + "email": "p@tchwork.com" + }, + { + "name": "Symfony Community", + "homepage": "https://symfony.com/contributors" + } ], - "homepage": "https://github.com/zendframework/zend-soap", + "description": "Generic abstractions related to writing services", + "homepage": "https://symfony.com", "keywords": [ - "soap", - "zf2" + "abstractions", + "contracts", + "decoupling", + "interfaces", + "interoperability", + "standards" ], - "time": "2019-04-30T16:45:35+00:00" + "time": "2019-11-18T17:27:11+00:00" }, { - "name": "zendframework/zend-stdlib", - "version": "3.2.1", + "name": "tedivm/jshrink", + "version": "v1.3.3", "source": { "type": "git", - "url": "https://github.com/zendframework/zend-stdlib.git", - "reference": "66536006722aff9e62d1b331025089b7ec71c065" + "url": "https://github.com/tedious/JShrink.git", + "reference": "566e0c731ba4e372be2de429ef7d54f4faf4477a" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/zendframework/zend-stdlib/zipball/66536006722aff9e62d1b331025089b7ec71c065", - "reference": "66536006722aff9e62d1b331025089b7ec71c065", + "url": "https://api.github.com/repos/tedious/JShrink/zipball/566e0c731ba4e372be2de429ef7d54f4faf4477a", + "reference": "566e0c731ba4e372be2de429ef7d54f4faf4477a", "shasum": "" }, "require": { - "php": "^5.6 || ^7.0" + "php": "^5.6|^7.0" }, "require-dev": { - "phpbench/phpbench": "^0.13", - "phpunit/phpunit": "^5.7.27 || ^6.5.8 || ^7.1.2", - "zendframework/zend-coding-standard": "~1.0.0" + "friendsofphp/php-cs-fixer": "^2.8", + "php-coveralls/php-coveralls": "^1.1.0", + "phpunit/phpunit": "^6" }, "type": "library", - "extra": { - "branch-alias": { - "dev-master": "3.2.x-dev", - "dev-develop": "3.3.x-dev" - } - }, "autoload": { - "psr-4": { - "Zend\\Stdlib\\": "src/" + "psr-0": { + "JShrink": "src/" } }, "notification-url": "https://packagist.org/downloads/", "license": [ "BSD-3-Clause" ], - "description": "SPL extensions, array utilities, error handlers, and more", + "authors": [ + { + "name": "Robert Hafner", + "email": "tedivm@tedivm.com" + } + ], + "description": "Javascript Minifier built in PHP", + "homepage": "http://github.com/tedious/JShrink", "keywords": [ - "ZendFramework", - "stdlib", - "zf" + "javascript", + "minifier" ], - "time": "2018-08-28T21:34:05+00:00" + "time": "2019-06-28T18:11:46+00:00" }, { - "name": "zendframework/zend-text", - "version": "2.7.1", + "name": "true/punycode", + "version": "v2.1.1", "source": { "type": "git", - "url": "https://github.com/zendframework/zend-text.git", - "reference": "41e32dafa4015e160e2f95a7039554385c71624d" + "url": "https://github.com/true/php-punycode.git", + "reference": "a4d0c11a36dd7f4e7cd7096076cab6d3378a071e" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/zendframework/zend-text/zipball/41e32dafa4015e160e2f95a7039554385c71624d", - "reference": "41e32dafa4015e160e2f95a7039554385c71624d", + "url": "https://api.github.com/repos/true/php-punycode/zipball/a4d0c11a36dd7f4e7cd7096076cab6d3378a071e", + "reference": "a4d0c11a36dd7f4e7cd7096076cab6d3378a071e", "shasum": "" }, "require": { - "php": "^5.6 || ^7.0", - "zendframework/zend-servicemanager": "^2.7.5 || ^3.0.3", - "zendframework/zend-stdlib": "^2.7 || ^3.0" + "php": ">=5.3.0", + "symfony/polyfill-mbstring": "^1.3" }, "require-dev": { - "phpunit/phpunit": "^5.7.27 || ^6.5.8 || ^7.1.4", - "zendframework/zend-coding-standard": "~1.0.0", - "zendframework/zend-config": "^2.6" + "phpunit/phpunit": "~4.7", + "squizlabs/php_codesniffer": "~2.0" }, "type": "library", - "extra": { - "branch-alias": { - "dev-master": "2.7.x-dev", - "dev-develop": "2.8.x-dev" - } - }, "autoload": { "psr-4": { - "Zend\\Text\\": "src/" + "TrueBV\\": "src/" } }, "notification-url": "https://packagist.org/downloads/", "license": [ - "BSD-3-Clause" + "MIT" ], - "description": "Create FIGlets and text-based tables", + "authors": [ + { + "name": "Renan Gonçalves", + "email": "renan.saddam@gmail.com" + } + ], + "description": "A Bootstring encoding of Unicode for Internationalized Domain Names in Applications (IDNA)", + "homepage": "https://github.com/true/php-punycode", "keywords": [ - "ZendFramework", - "text", - "zf" + "idna", + "punycode" ], - "time": "2019-10-16T20:36:27+00:00" + "time": "2016-11-16T10:37:54+00:00" }, { - "name": "zendframework/zend-uri", - "version": "2.7.1", + "name": "tubalmartin/cssmin", + "version": "v4.1.1", "source": { "type": "git", - "url": "https://github.com/zendframework/zend-uri.git", - "reference": "bfc4a5b9a309711e968d7c72afae4ac50c650083" + "url": "https://github.com/tubalmartin/YUI-CSS-compressor-PHP-port.git", + "reference": "3cbf557f4079d83a06f9c3ff9b957c022d7805cf" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/zendframework/zend-uri/zipball/bfc4a5b9a309711e968d7c72afae4ac50c650083", - "reference": "bfc4a5b9a309711e968d7c72afae4ac50c650083", + "url": "https://api.github.com/repos/tubalmartin/YUI-CSS-compressor-PHP-port/zipball/3cbf557f4079d83a06f9c3ff9b957c022d7805cf", + "reference": "3cbf557f4079d83a06f9c3ff9b957c022d7805cf", "shasum": "" }, "require": { - "php": "^5.6 || ^7.0", - "zendframework/zend-escaper": "^2.5", - "zendframework/zend-validator": "^2.10" + "ext-pcre": "*", + "php": ">=5.3.2" }, "require-dev": { - "phpunit/phpunit": "^5.7.27 || ^6.5.8 || ^7.1.4", - "zendframework/zend-coding-standard": "~1.0.0" + "cogpowered/finediff": "0.3.*", + "phpunit/phpunit": "4.8.*" }, + "bin": [ + "cssmin" + ], "type": "library", - "extra": { - "branch-alias": { - "dev-master": "2.7.x-dev", - "dev-develop": "2.8.x-dev" - } - }, "autoload": { "psr-4": { - "Zend\\Uri\\": "src/" + "tubalmartin\\CssMin\\": "src" } }, "notification-url": "https://packagist.org/downloads/", "license": [ "BSD-3-Clause" ], - "description": "A component that aids in manipulating and validating » Uniform Resource Identifiers (URIs)", + "authors": [ + { + "name": "Túbal Martín", + "homepage": "http://tubalmartin.me/" + } + ], + "description": "A PHP port of the YUI CSS compressor", + "homepage": "https://github.com/tubalmartin/YUI-CSS-compressor-PHP-port", "keywords": [ - "ZendFramework", - "uri", - "zf" + "compress", + "compressor", + "css", + "cssmin", + "minify", + "yui" ], - "time": "2019-10-07T13:35:33+00:00" + "time": "2018-01-15T15:26:51+00:00" }, { - "name": "zendframework/zend-validator", - "version": "2.12.2", + "name": "webonyx/graphql-php", + "version": "v0.13.8", "source": { "type": "git", - "url": "https://github.com/zendframework/zend-validator.git", - "reference": "fd24920c2afcf2a70d11f67c3457f8f509453a62" + "url": "https://github.com/webonyx/graphql-php.git", + "reference": "6829ae58f4c59121df1f86915fb9917a2ec595e8" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/zendframework/zend-validator/zipball/fd24920c2afcf2a70d11f67c3457f8f509453a62", - "reference": "fd24920c2afcf2a70d11f67c3457f8f509453a62", + "url": "https://api.github.com/repos/webonyx/graphql-php/zipball/6829ae58f4c59121df1f86915fb9917a2ec595e8", + "reference": "6829ae58f4c59121df1f86915fb9917a2ec595e8", "shasum": "" }, "require": { - "container-interop/container-interop": "^1.1", - "php": "^5.6 || ^7.0", - "zendframework/zend-stdlib": "^3.2.1" + "ext-json": "*", + "ext-mbstring": "*", + "php": "^7.1||^8.0" }, "require-dev": { - "phpunit/phpunit": "^6.0.8 || ^5.7.15", + "doctrine/coding-standard": "^6.0", + "phpbench/phpbench": "^0.14.0", + "phpstan/phpstan": "^0.11.4", + "phpstan/phpstan-phpunit": "^0.11.0", + "phpstan/phpstan-strict-rules": "^0.11.0", + "phpunit/phpcov": "^5.0", + "phpunit/phpunit": "^7.2", "psr/http-message": "^1.0", - "zendframework/zend-cache": "^2.6.1", - "zendframework/zend-coding-standard": "~1.0.0", - "zendframework/zend-config": "^2.6", - "zendframework/zend-db": "^2.7", - "zendframework/zend-filter": "^2.6", - "zendframework/zend-http": "^2.5.4", - "zendframework/zend-i18n": "^2.6", - "zendframework/zend-math": "^2.6", - "zendframework/zend-servicemanager": "^2.7.5 || ^3.0.3", - "zendframework/zend-session": "^2.8", - "zendframework/zend-uri": "^2.5" + "react/promise": "2.*" }, "suggest": { - "psr/http-message": "psr/http-message, required when validating PSR-7 UploadedFileInterface instances via the Upload and UploadFile validators", - "zendframework/zend-db": "Zend\\Db component, required by the (No)RecordExists validator", - "zendframework/zend-filter": "Zend\\Filter component, required by the Digits validator", - "zendframework/zend-i18n": "Zend\\I18n component to allow translation of validation error messages", - "zendframework/zend-i18n-resources": "Translations of validator messages", - "zendframework/zend-math": "Zend\\Math component, required by the Csrf validator", - "zendframework/zend-servicemanager": "Zend\\ServiceManager component to allow using the ValidatorPluginManager and validator chains", - "zendframework/zend-session": "Zend\\Session component, ^2.8; required by the Csrf validator", - "zendframework/zend-uri": "Zend\\Uri component, required by the Uri and Sitemap\\Loc validators" + "psr/http-message": "To use standard GraphQL server", + "react/promise": "To leverage async resolving on React PHP platform" }, "type": "library", - "extra": { - "branch-alias": { - "dev-master": "2.12.x-dev", - "dev-develop": "2.13.x-dev" - }, - "zf": { - "component": "Zend\\Validator", - "config-provider": "Zend\\Validator\\ConfigProvider" - } - }, "autoload": { "psr-4": { - "Zend\\Validator\\": "src/" + "GraphQL\\": "src/" } }, "notification-url": "https://packagist.org/downloads/", "license": [ - "BSD-3-Clause" + "MIT" ], - "description": "Validation classes for a wide range of domains, and the ability to chain validators to create complex validation criteria", + "description": "A PHP port of GraphQL reference implementation", + "homepage": "https://github.com/webonyx/graphql-php", "keywords": [ - "ZendFramework", - "validator", - "zf" + "api", + "graphql" ], - "time": "2019-10-29T08:33:25+00:00" + "time": "2019-08-25T10:32:47+00:00" }, { - "name": "zendframework/zend-view", - "version": "2.11.3", + "name": "wikimedia/less.php", + "version": "1.8.2", "source": { "type": "git", - "url": "https://github.com/zendframework/zend-view.git", - "reference": "e766457bd6ce13c5354e443bb949511b6904d7f5" + "url": "https://github.com/wikimedia/less.php.git", + "reference": "e238ad228d74b6ffd38209c799b34e9826909266" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/zendframework/zend-view/zipball/e766457bd6ce13c5354e443bb949511b6904d7f5", - "reference": "e766457bd6ce13c5354e443bb949511b6904d7f5", + "url": "https://api.github.com/repos/wikimedia/less.php/zipball/e238ad228d74b6ffd38209c799b34e9826909266", + "reference": "e238ad228d74b6ffd38209c799b34e9826909266", "shasum": "" }, "require": { - "php": "^5.6 || ^7.0", - "zendframework/zend-eventmanager": "^2.6.2 || ^3.0", - "zendframework/zend-json": "^2.6.1 || ^3.0", - "zendframework/zend-loader": "^2.5", - "zendframework/zend-stdlib": "^2.7 || ^3.0" - }, - "require-dev": { - "phpunit/phpunit": "^5.7.15 || ^6.0.8", - "zendframework/zend-authentication": "^2.5", - "zendframework/zend-cache": "^2.6.1", - "zendframework/zend-coding-standard": "~1.0.0", - "zendframework/zend-config": "^2.6", - "zendframework/zend-console": "^2.6", - "zendframework/zend-escaper": "^2.5", - "zendframework/zend-feed": "^2.7", - "zendframework/zend-filter": "^2.6.1", - "zendframework/zend-http": "^2.5.4", - "zendframework/zend-i18n": "^2.6", - "zendframework/zend-log": "^2.7", - "zendframework/zend-modulemanager": "^2.7.1", - "zendframework/zend-mvc": "^2.7.14 || ^3.0", - "zendframework/zend-navigation": "^2.5", - "zendframework/zend-paginator": "^2.5", - "zendframework/zend-permissions-acl": "^2.6", - "zendframework/zend-router": "^3.0.1", - "zendframework/zend-serializer": "^2.6.1", - "zendframework/zend-servicemanager": "^2.7.5 || ^3.0.3", - "zendframework/zend-session": "^2.8.1", - "zendframework/zend-uri": "^2.5" + "php": ">=7.2.9" }, - "suggest": { - "zendframework/zend-authentication": "Zend\\Authentication component", - "zendframework/zend-escaper": "Zend\\Escaper component", - "zendframework/zend-feed": "Zend\\Feed component", - "zendframework/zend-filter": "Zend\\Filter component", - "zendframework/zend-http": "Zend\\Http component", - "zendframework/zend-i18n": "Zend\\I18n component", - "zendframework/zend-mvc": "Zend\\Mvc component", - "zendframework/zend-mvc-plugin-flashmessenger": "zend-mvc-plugin-flashmessenger component, if you want to use the FlashMessenger view helper with zend-mvc versions 3 and up", - "zendframework/zend-navigation": "Zend\\Navigation component", - "zendframework/zend-paginator": "Zend\\Paginator component", - "zendframework/zend-permissions-acl": "Zend\\Permissions\\Acl component", - "zendframework/zend-servicemanager": "Zend\\ServiceManager component", - "zendframework/zend-uri": "Zend\\Uri component" + "require-dev": { + "phpunit/phpunit": "7.5.14" }, "bin": [ - "bin/templatemap_generator.php" + "bin/lessc" ], "type": "library", - "extra": { - "branch-alias": { - "dev-master": "2.11.x-dev", - "dev-develop": "2.12.x-dev" - } - }, "autoload": { - "psr-4": { - "Zend\\View\\": "src/" - } + "psr-0": { + "Less": "lib/" + }, + "classmap": [ + "lessc.inc.php" + ] }, "notification-url": "https://packagist.org/downloads/", "license": [ - "BSD-3-Clause" + "Apache-2.0" ], - "description": "Flexible view layer supporting and providing multiple view layers, helpers, and more", + "authors": [ + { + "name": "Josh Schmidt", + "homepage": "https://github.com/oyejorge" + }, + { + "name": "Matt Agar", + "homepage": "https://github.com/agar" + }, + { + "name": "Martin Jantošovič", + "homepage": "https://github.com/Mordred" + } + ], + "description": "PHP port of the Javascript version of LESS http://lesscss.org (Originally maintained by Josh Schmidt)", "keywords": [ - "ZendFramework", - "view", - "zf" + "css", + "less", + "less.js", + "lesscss", + "php", + "stylesheet" ], - "time": "2019-10-11T21:10:04+00:00" + "time": "2019-11-06T18:30:11+00:00" } ], "packages-dev": [ @@ -4934,23 +5311,23 @@ }, { "name": "allure-framework/allure-php-api", - "version": "1.1.5", + "version": "1.1.8", "source": { "type": "git", "url": "https://github.com/allure-framework/allure-php-commons.git", - "reference": "c7a675823ad75b8e02ddc364baae21668e7c4e88" + "reference": "5ae2deac1c7e1b992cfa572167370de45bdd346d" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/allure-framework/allure-php-commons/zipball/c7a675823ad75b8e02ddc364baae21668e7c4e88", - "reference": "c7a675823ad75b8e02ddc364baae21668e7c4e88", + "url": "https://api.github.com/repos/allure-framework/allure-php-commons/zipball/5ae2deac1c7e1b992cfa572167370de45bdd346d", + "reference": "5ae2deac1c7e1b992cfa572167370de45bdd346d", "shasum": "" }, "require": { - "jms/serializer": "^0.16.0", + "jms/serializer": "^0.16 || ^1.0", "php": ">=5.4.0", - "ramsey/uuid": "^3.0.0", - "symfony/http-foundation": "^2.0" + "ramsey/uuid": "^3.0", + "symfony/http-foundation": "^2.0 || ^3.0 || ^4.0 || ^5.0" }, "require-dev": { "phpunit/phpunit": "^4.0.0" @@ -4983,7 +5360,7 @@ "php", "report" ], - "time": "2018-05-25T14:02:11+00:00" + "time": "2020-03-13T10:47:35+00:00" }, { "name": "allure-framework/allure-phpunit", @@ -5033,20 +5410,104 @@ "steps", "testing" ], - "time": "2017-11-03T13:08:21+00:00" + "time": "2017-11-03T13:08:21+00:00" + }, + { + "name": "aws/aws-sdk-php", + "version": "3.133.45", + "source": { + "type": "git", + "url": "https://github.com/aws/aws-sdk-php.git", + "reference": "928a23e2ee7e195a66f93d0758895e26958c3b7d" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/aws/aws-sdk-php/zipball/928a23e2ee7e195a66f93d0758895e26958c3b7d", + "reference": "928a23e2ee7e195a66f93d0758895e26958c3b7d", + "shasum": "" + }, + "require": { + "ext-json": "*", + "ext-pcre": "*", + "ext-simplexml": "*", + "guzzlehttp/guzzle": "^5.3.3|^6.2.1|^7.0", + "guzzlehttp/promises": "^1.0", + "guzzlehttp/psr7": "^1.4.1", + "mtdowling/jmespath.php": "^2.5", + "php": ">=5.5" + }, + "require-dev": { + "andrewsville/php-token-reflection": "^1.4", + "aws/aws-php-sns-message-validator": "~1.0", + "behat/behat": "~3.0", + "doctrine/cache": "~1.4", + "ext-dom": "*", + "ext-openssl": "*", + "ext-pcntl": "*", + "ext-sockets": "*", + "nette/neon": "^2.3", + "phpunit/phpunit": "^4.8.35|^5.4.3", + "psr/cache": "^1.0", + "psr/simple-cache": "^1.0", + "sebastian/comparator": "^1.2.3" + }, + "suggest": { + "aws/aws-php-sns-message-validator": "To validate incoming SNS notifications", + "doctrine/cache": "To use the DoctrineCacheAdapter", + "ext-curl": "To send requests using cURL", + "ext-openssl": "Allows working with CloudFront private distributions and verifying received SNS messages", + "ext-sockets": "To use client-side monitoring" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "3.0-dev" + } + }, + "autoload": { + "psr-4": { + "Aws\\": "src/" + }, + "files": [ + "src/functions.php" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "Apache-2.0" + ], + "authors": [ + { + "name": "Amazon Web Services", + "homepage": "http://aws.amazon.com" + } + ], + "description": "AWS SDK for PHP - Use Amazon Web Services in your PHP project", + "homepage": "http://aws.amazon.com/sdkforphp", + "keywords": [ + "amazon", + "aws", + "cloud", + "dynamodb", + "ec2", + "glacier", + "s3", + "sdk" + ], + "time": "2020-03-26T18:12:15+00:00" }, { "name": "behat/gherkin", - "version": "v4.6.0", + "version": "v4.6.2", "source": { "type": "git", "url": "https://github.com/Behat/Gherkin.git", - "reference": "ab0a02ea14893860bca00f225f5621d351a3ad07" + "reference": "51ac4500c4dc30cbaaabcd2f25694299df666a31" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/Behat/Gherkin/zipball/ab0a02ea14893860bca00f225f5621d351a3ad07", - "reference": "ab0a02ea14893860bca00f225f5621d351a3ad07", + "url": "https://api.github.com/repos/Behat/Gherkin/zipball/51ac4500c4dc30cbaaabcd2f25694299df666a31", + "reference": "51ac4500c4dc30cbaaabcd2f25694299df666a31", "shasum": "" }, "require": { @@ -5092,7 +5553,7 @@ "gherkin", "parser" ], - "time": "2019-01-16T14:22:17+00:00" + "time": "2020-03-17T14:03:26+00:00" }, { "name": "cache/cache", @@ -5181,532 +5642,79 @@ ], "description": "Library of all the php-cache adapters", "homepage": "http://www.php-cache.com/en/latest/", - "keywords": [ - "cache", - "psr6" - ], - "time": "2017-03-28T16:08:48+00:00" - }, - { - "name": "codeception/codeception", - "version": "2.4.5", - "source": { - "type": "git", - "url": "https://github.com/Codeception/Codeception.git", - "reference": "5fee32d5c82791548931cbc34806b4de6aa1abfc" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/Codeception/Codeception/zipball/5fee32d5c82791548931cbc34806b4de6aa1abfc", - "reference": "5fee32d5c82791548931cbc34806b4de6aa1abfc", - "shasum": "" - }, - "require": { - "behat/gherkin": "^4.4.0", - "codeception/phpunit-wrapper": "^6.0.9|^7.0.6", - "codeception/stub": "^2.0", - "ext-json": "*", - "ext-mbstring": "*", - "facebook/webdriver": ">=1.1.3 <2.0", - "guzzlehttp/guzzle": ">=4.1.4 <7.0", - "guzzlehttp/psr7": "~1.0", - "php": ">=5.6.0 <8.0", - "symfony/browser-kit": ">=2.7 <5.0", - "symfony/console": ">=2.7 <5.0", - "symfony/css-selector": ">=2.7 <5.0", - "symfony/dom-crawler": ">=2.7 <5.0", - "symfony/event-dispatcher": ">=2.7 <5.0", - "symfony/finder": ">=2.7 <5.0", - "symfony/yaml": ">=2.7 <5.0" - }, - "require-dev": { - "codeception/specify": "~0.3", - "facebook/graph-sdk": "~5.3", - "flow/jsonpath": "~0.2", - "monolog/monolog": "~1.8", - "pda/pheanstalk": "~3.0", - "php-amqplib/php-amqplib": "~2.4", - "predis/predis": "^1.0", - "squizlabs/php_codesniffer": "~2.0", - "symfony/process": ">=2.7 <5.0", - "vlucas/phpdotenv": "^2.4.0" - }, - "suggest": { - "aws/aws-sdk-php": "For using AWS Auth in REST module and Queue module", - "codeception/phpbuiltinserver": "Start and stop PHP built-in web server for your tests", - "codeception/specify": "BDD-style code blocks", - "codeception/verify": "BDD-style assertions", - "flow/jsonpath": "For using JSONPath in REST module", - "league/factory-muffin": "For DataFactory module", - "league/factory-muffin-faker": "For Faker support in DataFactory module", - "phpseclib/phpseclib": "for SFTP option in FTP Module", - "stecman/symfony-console-completion": "For BASH autocompletion", - "symfony/phpunit-bridge": "For phpunit-bridge support" - }, - "bin": [ - "codecept" - ], - "type": "library", - "extra": { - "branch-alias": [] - }, - "autoload": { - "psr-4": { - "Codeception\\": "src\\Codeception", - "Codeception\\Extension\\": "ext" - } - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "authors": [ - { - "name": "Michael Bodnarchuk", - "email": "davert@mail.ua", - "homepage": "http://codegyre.com" - } - ], - "description": "BDD-style testing framework", - "homepage": "http://codeception.com/", - "keywords": [ - "BDD", - "TDD", - "acceptance testing", - "functional testing", - "unit testing" - ], - "time": "2018-08-01T07:21:49+00:00" - }, - { - "name": "codeception/phpunit-wrapper", - "version": "6.7.0", - "source": { - "type": "git", - "url": "https://github.com/Codeception/phpunit-wrapper.git", - "reference": "93f59e028826464eac086052fa226e58967f6907" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/Codeception/phpunit-wrapper/zipball/93f59e028826464eac086052fa226e58967f6907", - "reference": "93f59e028826464eac086052fa226e58967f6907", - "shasum": "" - }, - "require": { - "phpunit/php-code-coverage": ">=4.0.4 <6.0", - "phpunit/phpunit": ">=6.5.13 <7.0", - "sebastian/comparator": ">=1.2.4 <3.0", - "sebastian/diff": ">=1.4 <4.0" - }, - "replace": { - "codeception/phpunit-wrapper": "*" - }, - "require-dev": { - "codeception/specify": "*", - "vlucas/phpdotenv": "^3.0" - }, - "type": "library", - "autoload": { - "psr-4": { - "Codeception\\PHPUnit\\": "src\\" - } - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "authors": [ - { - "name": "Davert", - "email": "davert.php@resend.cc" - } - ], - "description": "PHPUnit classes used by Codeception", - "time": "2019-08-18T15:43:35+00:00" - }, - { - "name": "codeception/stub", - "version": "2.1.0", - "source": { - "type": "git", - "url": "https://github.com/Codeception/Stub.git", - "reference": "853657f988942f7afb69becf3fd0059f192c705a" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/Codeception/Stub/zipball/853657f988942f7afb69becf3fd0059f192c705a", - "reference": "853657f988942f7afb69becf3fd0059f192c705a", - "shasum": "" - }, - "require": { - "codeception/phpunit-wrapper": ">6.0.15 <6.1.0 | ^6.6.1 | ^7.7.1 | ^8.0.3" - }, - "type": "library", - "autoload": { - "psr-4": { - "Codeception\\": "src/" - } - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "description": "Flexible Stub wrapper for PHPUnit's Mock Builder", - "time": "2019-03-02T15:35:10+00:00" - }, - { - "name": "consolidation/annotated-command", - "version": "2.12.0", - "source": { - "type": "git", - "url": "https://github.com/consolidation/annotated-command.git", - "reference": "512a2e54c98f3af377589de76c43b24652bcb789" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/consolidation/annotated-command/zipball/512a2e54c98f3af377589de76c43b24652bcb789", - "reference": "512a2e54c98f3af377589de76c43b24652bcb789", - "shasum": "" - }, - "require": { - "consolidation/output-formatters": "^3.4", - "php": ">=5.4.5", - "psr/log": "^1", - "symfony/console": "^2.8|^3|^4", - "symfony/event-dispatcher": "^2.5|^3|^4", - "symfony/finder": "^2.5|^3|^4" - }, - "require-dev": { - "g1a/composer-test-scenarios": "^3", - "php-coveralls/php-coveralls": "^1", - "phpunit/phpunit": "^6", - "squizlabs/php_codesniffer": "^2.7" - }, - "type": "library", - "extra": { - "scenarios": { - "symfony4": { - "require": { - "symfony/console": "^4.0" - }, - "config": { - "platform": { - "php": "7.1.3" - } - } - }, - "symfony2": { - "require": { - "symfony/console": "^2.8" - }, - "require-dev": { - "phpunit/phpunit": "^4.8.36" - }, - "remove": [ - "php-coveralls/php-coveralls" - ], - "config": { - "platform": { - "php": "5.4.8" - } - }, - "scenario-options": { - "create-lockfile": "false" - } - }, - "phpunit4": { - "require-dev": { - "phpunit/phpunit": "^4.8.36" - }, - "remove": [ - "php-coveralls/php-coveralls" - ], - "config": { - "platform": { - "php": "5.4.8" - } - } - } - }, - "branch-alias": { - "dev-master": "2.x-dev" - } - }, - "autoload": { - "psr-4": { - "Consolidation\\AnnotatedCommand\\": "src" - } - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "authors": [ - { - "name": "Greg Anderson", - "email": "greg.1.anderson@greenknowe.org" - } - ], - "description": "Initialize Symfony Console commands from annotated command class methods.", - "time": "2019-03-08T16:55:03+00:00" - }, - { - "name": "consolidation/config", - "version": "1.2.1", - "source": { - "type": "git", - "url": "https://github.com/consolidation/config.git", - "reference": "cac1279bae7efb5c7fb2ca4c3ba4b8eb741a96c1" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/consolidation/config/zipball/cac1279bae7efb5c7fb2ca4c3ba4b8eb741a96c1", - "reference": "cac1279bae7efb5c7fb2ca4c3ba4b8eb741a96c1", - "shasum": "" - }, - "require": { - "dflydev/dot-access-data": "^1.1.0", - "grasmash/expander": "^1", - "php": ">=5.4.0" - }, - "require-dev": { - "g1a/composer-test-scenarios": "^3", - "php-coveralls/php-coveralls": "^1", - "phpunit/phpunit": "^5", - "squizlabs/php_codesniffer": "2.*", - "symfony/console": "^2.5|^3|^4", - "symfony/yaml": "^2.8.11|^3|^4" - }, - "suggest": { - "symfony/yaml": "Required to use Consolidation\\Config\\Loader\\YamlConfigLoader" - }, - "type": "library", - "extra": { - "scenarios": { - "symfony4": { - "require-dev": { - "symfony/console": "^4.0" - }, - "config": { - "platform": { - "php": "7.1.3" - } - } - }, - "symfony2": { - "require-dev": { - "symfony/console": "^2.8", - "symfony/event-dispatcher": "^2.8", - "phpunit/phpunit": "^4.8.36" - }, - "remove": [ - "php-coveralls/php-coveralls" - ], - "config": { - "platform": { - "php": "5.4.8" - } - } - } - }, - "branch-alias": { - "dev-master": "1.x-dev" - } - }, - "autoload": { - "psr-4": { - "Consolidation\\Config\\": "src" - } - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "authors": [ - { - "name": "Greg Anderson", - "email": "greg.1.anderson@greenknowe.org" - } - ], - "description": "Provide configuration services for a commandline tool.", - "time": "2019-03-03T19:37:04+00:00" - }, - { - "name": "consolidation/log", - "version": "1.1.1", - "source": { - "type": "git", - "url": "https://github.com/consolidation/log.git", - "reference": "b2e887325ee90abc96b0a8b7b474cd9e7c896e3a" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/consolidation/log/zipball/b2e887325ee90abc96b0a8b7b474cd9e7c896e3a", - "reference": "b2e887325ee90abc96b0a8b7b474cd9e7c896e3a", - "shasum": "" - }, - "require": { - "php": ">=5.4.5", - "psr/log": "^1.0", - "symfony/console": "^2.8|^3|^4" - }, - "require-dev": { - "g1a/composer-test-scenarios": "^3", - "php-coveralls/php-coveralls": "^1", - "phpunit/phpunit": "^6", - "squizlabs/php_codesniffer": "^2" - }, - "type": "library", - "extra": { - "scenarios": { - "symfony4": { - "require": { - "symfony/console": "^4.0" - }, - "config": { - "platform": { - "php": "7.1.3" - } - } - }, - "symfony2": { - "require": { - "symfony/console": "^2.8" - }, - "require-dev": { - "phpunit/phpunit": "^4.8.36" - }, - "remove": [ - "php-coveralls/php-coveralls" - ], - "config": { - "platform": { - "php": "5.4.8" - } - } - }, - "phpunit4": { - "require-dev": { - "phpunit/phpunit": "^4.8.36" - }, - "remove": [ - "php-coveralls/php-coveralls" - ], - "config": { - "platform": { - "php": "5.4.8" - } - } - } - }, - "branch-alias": { - "dev-master": "1.x-dev" - } - }, - "autoload": { - "psr-4": { - "Consolidation\\Log\\": "src" - } - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "authors": [ - { - "name": "Greg Anderson", - "email": "greg.1.anderson@greenknowe.org" - } + "keywords": [ + "cache", + "psr6" ], - "description": "Improved Psr-3 / Psr\\Log logger based on Symfony Console components.", - "time": "2019-01-01T17:30:51+00:00" + "time": "2017-03-28T16:08:48+00:00" }, { - "name": "consolidation/output-formatters", - "version": "3.5.0", + "name": "codeception/codeception", + "version": "2.4.5", "source": { "type": "git", - "url": "https://github.com/consolidation/output-formatters.git", - "reference": "99ec998ffb697e0eada5aacf81feebfb13023605" + "url": "https://github.com/Codeception/Codeception.git", + "reference": "5fee32d5c82791548931cbc34806b4de6aa1abfc" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/consolidation/output-formatters/zipball/99ec998ffb697e0eada5aacf81feebfb13023605", - "reference": "99ec998ffb697e0eada5aacf81feebfb13023605", + "url": "https://api.github.com/repos/Codeception/Codeception/zipball/5fee32d5c82791548931cbc34806b4de6aa1abfc", + "reference": "5fee32d5c82791548931cbc34806b4de6aa1abfc", "shasum": "" }, "require": { - "dflydev/dot-access-data": "^1.1.0", - "php": ">=5.4.0", - "symfony/console": "^2.8|^3|^4", - "symfony/finder": "^2.5|^3|^4" + "behat/gherkin": "^4.4.0", + "codeception/phpunit-wrapper": "^6.0.9|^7.0.6", + "codeception/stub": "^2.0", + "ext-json": "*", + "ext-mbstring": "*", + "facebook/webdriver": ">=1.1.3 <2.0", + "guzzlehttp/guzzle": ">=4.1.4 <7.0", + "guzzlehttp/psr7": "~1.0", + "php": ">=5.6.0 <8.0", + "symfony/browser-kit": ">=2.7 <5.0", + "symfony/console": ">=2.7 <5.0", + "symfony/css-selector": ">=2.7 <5.0", + "symfony/dom-crawler": ">=2.7 <5.0", + "symfony/event-dispatcher": ">=2.7 <5.0", + "symfony/finder": ">=2.7 <5.0", + "symfony/yaml": ">=2.7 <5.0" }, "require-dev": { - "g1a/composer-test-scenarios": "^3", - "php-coveralls/php-coveralls": "^1", - "phpunit/phpunit": "^5.7.27", - "squizlabs/php_codesniffer": "^2.7", - "symfony/var-dumper": "^2.8|^3|^4", - "victorjonsson/markdowndocs": "^1.3" + "codeception/specify": "~0.3", + "facebook/graph-sdk": "~5.3", + "flow/jsonpath": "~0.2", + "monolog/monolog": "~1.8", + "pda/pheanstalk": "~3.0", + "php-amqplib/php-amqplib": "~2.4", + "predis/predis": "^1.0", + "squizlabs/php_codesniffer": "~2.0", + "symfony/process": ">=2.7 <5.0", + "vlucas/phpdotenv": "^2.4.0" }, "suggest": { - "symfony/var-dumper": "For using the var_dump formatter" - }, - "type": "library", - "extra": { - "scenarios": { - "symfony4": { - "require": { - "symfony/console": "^4.0" - }, - "require-dev": { - "phpunit/phpunit": "^6" - }, - "config": { - "platform": { - "php": "7.1.3" - } - } - }, - "symfony3": { - "require": { - "symfony/console": "^3.4", - "symfony/finder": "^3.4", - "symfony/var-dumper": "^3.4" - }, - "config": { - "platform": { - "php": "5.6.32" - } - } - }, - "symfony2": { - "require": { - "symfony/console": "^2.8" - }, - "require-dev": { - "phpunit/phpunit": "^4.8.36" - }, - "remove": [ - "php-coveralls/php-coveralls" - ], - "config": { - "platform": { - "php": "5.4.8" - } - }, - "scenario-options": { - "create-lockfile": "false" - } - } - }, - "branch-alias": { - "dev-master": "3.x-dev" - } + "aws/aws-sdk-php": "For using AWS Auth in REST module and Queue module", + "codeception/phpbuiltinserver": "Start and stop PHP built-in web server for your tests", + "codeception/specify": "BDD-style code blocks", + "codeception/verify": "BDD-style assertions", + "flow/jsonpath": "For using JSONPath in REST module", + "league/factory-muffin": "For DataFactory module", + "league/factory-muffin-faker": "For Faker support in DataFactory module", + "phpseclib/phpseclib": "for SFTP option in FTP Module", + "stecman/symfony-console-completion": "For BASH autocompletion", + "symfony/phpunit-bridge": "For phpunit-bridge support" + }, + "bin": [ + "codecept" + ], + "type": "library", + "extra": { + "branch-alias": [] }, "autoload": { "psr-4": { - "Consolidation\\OutputFormatters\\": "src" + "Codeception\\": "src\\Codeception", + "Codeception\\Extension\\": "ext" } }, "notification-url": "https://packagist.org/downloads/", @@ -5715,107 +5723,53 @@ ], "authors": [ { - "name": "Greg Anderson", - "email": "greg.1.anderson@greenknowe.org" + "name": "Michael Bodnarchuk", + "email": "davert@mail.ua", + "homepage": "http://codegyre.com" } ], - "description": "Format text by applying transformations provided by plug-in formatters.", - "time": "2019-05-30T23:16:01+00:00" + "description": "BDD-style testing framework", + "homepage": "http://codeception.com/", + "keywords": [ + "BDD", + "TDD", + "acceptance testing", + "functional testing", + "unit testing" + ], + "time": "2018-08-01T07:21:49+00:00" }, { - "name": "consolidation/robo", - "version": "1.4.11", + "name": "codeception/phpunit-wrapper", + "version": "6.8.1", "source": { "type": "git", - "url": "https://github.com/consolidation/Robo.git", - "reference": "5fa1d901776a628167a325baa9db95d8edf13a80" + "url": "https://github.com/Codeception/phpunit-wrapper.git", + "reference": "ca6e94c6dadc19db05698d4e0d84214e570ce045" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/consolidation/Robo/zipball/5fa1d901776a628167a325baa9db95d8edf13a80", - "reference": "5fa1d901776a628167a325baa9db95d8edf13a80", + "url": "https://api.github.com/repos/Codeception/phpunit-wrapper/zipball/ca6e94c6dadc19db05698d4e0d84214e570ce045", + "reference": "ca6e94c6dadc19db05698d4e0d84214e570ce045", "shasum": "" }, "require": { - "consolidation/annotated-command": "^2.11.0", - "consolidation/config": "^1.2", - "consolidation/log": "~1", - "consolidation/output-formatters": "^3.1.13", - "consolidation/self-update": "^1", - "grasmash/yaml-expander": "^1.3", - "league/container": "^2.2", - "php": ">=5.5.0", - "symfony/console": "^2.8|^3|^4", - "symfony/event-dispatcher": "^2.5|^3|^4", - "symfony/filesystem": "^2.5|^3|^4", - "symfony/finder": "^2.5|^3|^4", - "symfony/process": "^2.5|^3|^4" + "phpunit/php-code-coverage": ">=4.0.4 <6.0", + "phpunit/phpunit": ">=6.5.13 <7.0", + "sebastian/comparator": ">=1.2.4 <3.0", + "sebastian/diff": ">=1.4 <4.0" }, "replace": { - "codegyre/robo": "< 1.0" - }, - "require-dev": { - "codeception/aspect-mock": "^1|^2.1.1", - "codeception/base": "^2.3.7", - "codeception/verify": "^0.3.2", - "g1a/composer-test-scenarios": "^3", - "goaop/framework": "~2.1.2", - "goaop/parser-reflection": "^1.1.0", - "natxet/cssmin": "3.0.4", - "nikic/php-parser": "^3.1.5", - "patchwork/jsqueeze": "~2", - "pear/archive_tar": "^1.4.4", - "php-coveralls/php-coveralls": "^1", - "phpunit/php-code-coverage": "~2|~4", - "sebastian/comparator": "^1.2.4", - "squizlabs/php_codesniffer": "^2.8" - }, - "suggest": { - "henrikbjorn/lurker": "For monitoring filesystem changes in taskWatch", - "natxet/CssMin": "For minifying CSS files in taskMinify", - "patchwork/jsqueeze": "For minifying JS files in taskMinify", - "pear/archive_tar": "Allows tar archives to be created and extracted in taskPack and taskExtract, respectively." + "codeception/phpunit-wrapper": "*" }, - "bin": [ - "robo" - ], - "type": "library", - "extra": { - "scenarios": { - "symfony4": { - "require": { - "symfony/console": "^4" - }, - "config": { - "platform": { - "php": "7.1.3" - } - } - }, - "symfony2": { - "require": { - "symfony/console": "^2.8" - }, - "remove": [ - "goaop/framework" - ], - "config": { - "platform": { - "php": "5.5.9" - } - }, - "scenario-options": { - "create-lockfile": "false" - } - } - }, - "branch-alias": { - "dev-master": "2.x-dev" - } + "require-dev": { + "codeception/specify": "*", + "vlucas/phpdotenv": "^3.0" }, + "type": "library", "autoload": { "psr-4": { - "Robo\\": "src" + "Codeception\\PHPUnit\\": "src/" } }, "notification-url": "https://packagist.org/downloads/", @@ -5828,58 +5782,38 @@ "email": "davert.php@resend.cc" } ], - "description": "Modern task runner", - "time": "2019-10-29T15:50:02+00:00" + "description": "PHPUnit classes used by Codeception", + "time": "2020-03-20T08:05:05+00:00" }, { - "name": "consolidation/self-update", - "version": "1.1.5", + "name": "codeception/stub", + "version": "2.1.0", "source": { "type": "git", - "url": "https://github.com/consolidation/self-update.git", - "reference": "a1c273b14ce334789825a09d06d4c87c0a02ad54" + "url": "https://github.com/Codeception/Stub.git", + "reference": "853657f988942f7afb69becf3fd0059f192c705a" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/consolidation/self-update/zipball/a1c273b14ce334789825a09d06d4c87c0a02ad54", - "reference": "a1c273b14ce334789825a09d06d4c87c0a02ad54", + "url": "https://api.github.com/repos/Codeception/Stub/zipball/853657f988942f7afb69becf3fd0059f192c705a", + "reference": "853657f988942f7afb69becf3fd0059f192c705a", "shasum": "" }, "require": { - "php": ">=5.5.0", - "symfony/console": "^2.8|^3|^4", - "symfony/filesystem": "^2.5|^3|^4" + "codeception/phpunit-wrapper": ">6.0.15 <6.1.0 | ^6.6.1 | ^7.7.1 | ^8.0.3" }, - "bin": [ - "scripts/release" - ], "type": "library", - "extra": { - "branch-alias": { - "dev-master": "1.x-dev" - } - }, "autoload": { "psr-4": { - "SelfUpdate\\": "src" + "Codeception\\": "src/" } }, "notification-url": "https://packagist.org/downloads/", "license": [ "MIT" ], - "authors": [ - { - "name": "Greg Anderson", - "email": "greg.1.anderson@greenknowe.org" - }, - { - "name": "Alexander Menk", - "email": "menk@mestrona.net" - } - ], - "description": "Provides a self:update command for Symfony Console applications.", - "time": "2018-10-28T01:52:03+00:00" + "description": "Flexible Stub wrapper for PHPUnit's Mock Builder", + "time": "2019-03-02T15:35:10+00:00" }, { "name": "csharpru/vault-php", @@ -6033,65 +5967,6 @@ ], "time": "2018-10-26T13:21:45+00:00" }, - { - "name": "dflydev/dot-access-data", - "version": "v1.1.0", - "source": { - "type": "git", - "url": "https://github.com/dflydev/dflydev-dot-access-data.git", - "reference": "3fbd874921ab2c041e899d044585a2ab9795df8a" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/dflydev/dflydev-dot-access-data/zipball/3fbd874921ab2c041e899d044585a2ab9795df8a", - "reference": "3fbd874921ab2c041e899d044585a2ab9795df8a", - "shasum": "" - }, - "require": { - "php": ">=5.3.2" - }, - "type": "library", - "extra": { - "branch-alias": { - "dev-master": "1.0-dev" - } - }, - "autoload": { - "psr-0": { - "Dflydev\\DotAccessData": "src" - } - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "authors": [ - { - "name": "Dragonfly Development Inc.", - "email": "info@dflydev.com", - "homepage": "http://dflydev.com" - }, - { - "name": "Beau Simensen", - "email": "beau@dflydev.com", - "homepage": "http://beausimensen.com" - }, - { - "name": "Carlos Frutos", - "email": "carlos@kiwing.it", - "homepage": "https://github.com/cfrutos" - } - ], - "description": "Given a deep data structure, access data by dot notation.", - "homepage": "https://github.com/dflydev/dflydev-dot-access-data", - "keywords": [ - "access", - "data", - "dot", - "notation" - ], - "time": "2017-01-20T21:14:22+00:00" - }, { "name": "doctrine/annotations", "version": "v1.8.0", @@ -6162,16 +6037,16 @@ }, { "name": "doctrine/cache", - "version": "1.9.1", + "version": "1.10.0", "source": { "type": "git", "url": "https://github.com/doctrine/cache.git", - "reference": "89a5c76c39c292f7798f964ab3c836c3f8192a55" + "reference": "382e7f4db9a12dc6c19431743a2b096041bcdd62" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/doctrine/cache/zipball/89a5c76c39c292f7798f964ab3c836c3f8192a55", - "reference": "89a5c76c39c292f7798f964ab3c836c3f8192a55", + "url": "https://api.github.com/repos/doctrine/cache/zipball/382e7f4db9a12dc6c19431743a2b096041bcdd62", + "reference": "382e7f4db9a12dc6c19431743a2b096041bcdd62", "shasum": "" }, "require": { @@ -6238,10 +6113,9 @@ "memcached", "php", "redis", - "riak", "xcache" ], - "time": "2019-11-15T14:31:57+00:00" + "time": "2019-11-29T15:36:20+00:00" }, { "name": "doctrine/inflector", @@ -6385,135 +6259,18 @@ }, "require-dev": { "doctrine/coding-standard": "^6.0", - "phpstan/phpstan": "^0.11.8", - "phpunit/phpunit": "^8.2" - }, - "type": "library", - "extra": { - "branch-alias": { - "dev-master": "1.2.x-dev" - } - }, - "autoload": { - "psr-4": { - "Doctrine\\Common\\Lexer\\": "lib/Doctrine/Common/Lexer" - } - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "authors": [ - { - "name": "Guilherme Blanco", - "email": "guilhermeblanco@gmail.com" - }, - { - "name": "Roman Borschel", - "email": "roman@code-factory.org" - }, - { - "name": "Johannes Schmitt", - "email": "schmittjoh@gmail.com" - } - ], - "description": "PHP Doctrine Lexer parser library that can be used in Top-Down, Recursive Descent Parsers.", - "homepage": "https://www.doctrine-project.org/projects/lexer.html", - "keywords": [ - "annotations", - "docblock", - "lexer", - "parser", - "php" - ], - "time": "2019-10-30T14:39:59+00:00" - }, - { - "name": "facebook/webdriver", - "version": "1.7.1", - "source": { - "type": "git", - "url": "https://github.com/facebook/php-webdriver.git", - "reference": "e43de70f3c7166169d0f14a374505392734160e5" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/facebook/php-webdriver/zipball/e43de70f3c7166169d0f14a374505392734160e5", - "reference": "e43de70f3c7166169d0f14a374505392734160e5", - "shasum": "" - }, - "require": { - "ext-curl": "*", - "ext-json": "*", - "ext-mbstring": "*", - "ext-zip": "*", - "php": "^5.6 || ~7.0", - "symfony/process": "^2.8 || ^3.1 || ^4.0" - }, - "require-dev": { - "friendsofphp/php-cs-fixer": "^2.0", - "jakub-onderka/php-parallel-lint": "^0.9.2", - "php-coveralls/php-coveralls": "^2.0", - "php-mock/php-mock-phpunit": "^1.1", - "phpunit/phpunit": "^5.7", - "sebastian/environment": "^1.3.4 || ^2.0 || ^3.0", - "squizlabs/php_codesniffer": "^2.6", - "symfony/var-dumper": "^3.3 || ^4.0" - }, - "suggest": { - "ext-SimpleXML": "For Firefox profile creation" - }, - "type": "library", - "extra": { - "branch-alias": { - "dev-community": "1.5-dev" - } - }, - "autoload": { - "psr-4": { - "Facebook\\WebDriver\\": "lib/" - } - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "Apache-2.0" - ], - "description": "A PHP client for Selenium WebDriver", - "homepage": "https://github.com/facebook/php-webdriver", - "keywords": [ - "facebook", - "php", - "selenium", - "webdriver" - ], - "time": "2019-06-13T08:02:18+00:00" - }, - { - "name": "flow/jsonpath", - "version": "0.4.0", - "source": { - "type": "git", - "url": "https://github.com/FlowCommunications/JSONPath.git", - "reference": "f0222818d5c938e4ab668ab2e2c079bd51a27112" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/FlowCommunications/JSONPath/zipball/f0222818d5c938e4ab668ab2e2c079bd51a27112", - "reference": "f0222818d5c938e4ab668ab2e2c079bd51a27112", - "shasum": "" - }, - "require": { - "php": ">=5.4.0" - }, - "require-dev": { - "peekmo/jsonpath": "dev-master", - "phpunit/phpunit": "^4.0" + "phpstan/phpstan": "^0.11.8", + "phpunit/phpunit": "^8.2" }, "type": "library", + "extra": { + "branch-alias": { + "dev-master": "1.2.x-dev" + } + }, "autoload": { - "psr-0": { - "Flow\\JSONPath": "src/", - "Flow\\JSONPath\\Test": "tests/" + "psr-4": { + "Doctrine\\Common\\Lexer\\": "lib/Doctrine/Common/Lexer" } }, "notification-url": "https://packagist.org/downloads/", @@ -6522,12 +6279,28 @@ ], "authors": [ { - "name": "Stephen Frank", - "email": "stephen@flowsa.com" + "name": "Guilherme Blanco", + "email": "guilhermeblanco@gmail.com" + }, + { + "name": "Roman Borschel", + "email": "roman@code-factory.org" + }, + { + "name": "Johannes Schmitt", + "email": "schmittjoh@gmail.com" } ], - "description": "JSONPath implementation for parsing, searching and flattening arrays", - "time": "2018-03-04T16:39:47+00:00" + "description": "PHP Doctrine Lexer parser library that can be used in Top-Down, Recursive Descent Parsers.", + "homepage": "https://www.doctrine-project.org/projects/lexer.html", + "keywords": [ + "annotations", + "docblock", + "lexer", + "parser", + "php" + ], + "time": "2019-10-30T14:39:59+00:00" }, { "name": "friendsofphp/php-cs-fixer", @@ -6618,191 +6391,6 @@ "description": "A tool to automatically fix PHP code style", "time": "2019-08-31T12:47:52+00:00" }, - { - "name": "fzaninotto/faker", - "version": "v1.9.0", - "source": { - "type": "git", - "url": "https://github.com/fzaninotto/Faker.git", - "reference": "27a216cbe72327b2d6369fab721a5843be71e57d" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/fzaninotto/Faker/zipball/27a216cbe72327b2d6369fab721a5843be71e57d", - "reference": "27a216cbe72327b2d6369fab721a5843be71e57d", - "shasum": "" - }, - "require": { - "php": "^5.3.3 || ^7.0" - }, - "require-dev": { - "ext-intl": "*", - "phpunit/phpunit": "^4.8.35 || ^5.7", - "squizlabs/php_codesniffer": "^2.9.2" - }, - "type": "library", - "extra": { - "branch-alias": [] - }, - "autoload": { - "psr-4": { - "Faker\\": "src/Faker/" - } - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "authors": [ - { - "name": "François Zaninotto" - } - ], - "description": "Faker is a PHP library that generates fake data for you.", - "keywords": [ - "data", - "faker", - "fixtures" - ], - "time": "2019-11-14T13:13:06+00:00" - }, - { - "name": "grasmash/expander", - "version": "1.0.0", - "source": { - "type": "git", - "url": "https://github.com/grasmash/expander.git", - "reference": "95d6037344a4be1dd5f8e0b0b2571a28c397578f" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/grasmash/expander/zipball/95d6037344a4be1dd5f8e0b0b2571a28c397578f", - "reference": "95d6037344a4be1dd5f8e0b0b2571a28c397578f", - "shasum": "" - }, - "require": { - "dflydev/dot-access-data": "^1.1.0", - "php": ">=5.4" - }, - "require-dev": { - "greg-1-anderson/composer-test-scenarios": "^1", - "phpunit/phpunit": "^4|^5.5.4", - "satooshi/php-coveralls": "^1.0.2|dev-master", - "squizlabs/php_codesniffer": "^2.7" - }, - "type": "library", - "extra": { - "branch-alias": { - "dev-master": "1.x-dev" - } - }, - "autoload": { - "psr-4": { - "Grasmash\\Expander\\": "src/" - } - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "authors": [ - { - "name": "Matthew Grasmick" - } - ], - "description": "Expands internal property references in PHP arrays file.", - "time": "2017-12-21T22:14:55+00:00" - }, - { - "name": "grasmash/yaml-expander", - "version": "1.4.0", - "source": { - "type": "git", - "url": "https://github.com/grasmash/yaml-expander.git", - "reference": "3f0f6001ae707a24f4d9733958d77d92bf9693b1" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/grasmash/yaml-expander/zipball/3f0f6001ae707a24f4d9733958d77d92bf9693b1", - "reference": "3f0f6001ae707a24f4d9733958d77d92bf9693b1", - "shasum": "" - }, - "require": { - "dflydev/dot-access-data": "^1.1.0", - "php": ">=5.4", - "symfony/yaml": "^2.8.11|^3|^4" - }, - "require-dev": { - "greg-1-anderson/composer-test-scenarios": "^1", - "phpunit/phpunit": "^4.8|^5.5.4", - "satooshi/php-coveralls": "^1.0.2|dev-master", - "squizlabs/php_codesniffer": "^2.7" - }, - "type": "library", - "extra": { - "branch-alias": { - "dev-master": "1.x-dev" - } - }, - "autoload": { - "psr-4": { - "Grasmash\\YamlExpander\\": "src/" - } - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "authors": [ - { - "name": "Matthew Grasmick" - } - ], - "description": "Expands internal property references in a yaml file.", - "time": "2017-12-16T16:06:03+00:00" - }, - { - "name": "ircmaxell/password-compat", - "version": "v1.0.4", - "source": { - "type": "git", - "url": "https://github.com/ircmaxell/password_compat.git", - "reference": "5c5cde8822a69545767f7c7f3058cb15ff84614c" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/ircmaxell/password_compat/zipball/5c5cde8822a69545767f7c7f3058cb15ff84614c", - "reference": "5c5cde8822a69545767f7c7f3058cb15ff84614c", - "shasum": "" - }, - "require-dev": { - "phpunit/phpunit": "4.*" - }, - "type": "library", - "autoload": { - "files": [ - "lib/password.php" - ] - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "authors": [ - { - "name": "Anthony Ferrara", - "email": "ircmaxell@php.net", - "homepage": "http://blog.ircmaxell.com" - } - ], - "description": "A compatibility library for the proposed simplified password hashing algorithm: https://wiki.php.net/rfc/password_hash", - "homepage": "https://github.com/ircmaxell/password_compat", - "keywords": [ - "hashing", - "password" - ], - "time": "2014-11-20T16:49:30+00:00" - }, { "name": "jms/metadata", "version": "1.7.0", @@ -6895,44 +6483,56 @@ }, { "name": "jms/serializer", - "version": "0.16.0", + "version": "1.14.1", "source": { "type": "git", "url": "https://github.com/schmittjoh/serializer.git", - "reference": "c8a171357ca92b6706e395c757f334902d430ea9" + "reference": "ba908d278fff27ec01fb4349f372634ffcd697c0" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/schmittjoh/serializer/zipball/c8a171357ca92b6706e395c757f334902d430ea9", - "reference": "c8a171357ca92b6706e395c757f334902d430ea9", + "url": "https://api.github.com/repos/schmittjoh/serializer/zipball/ba908d278fff27ec01fb4349f372634ffcd697c0", + "reference": "ba908d278fff27ec01fb4349f372634ffcd697c0", "shasum": "" }, "require": { - "doctrine/annotations": "1.*", - "jms/metadata": "~1.1", + "doctrine/annotations": "^1.0", + "doctrine/instantiator": "^1.0.3", + "jms/metadata": "^1.3", "jms/parser-lib": "1.*", - "php": ">=5.3.2", - "phpcollection/phpcollection": "~0.1" + "php": "^5.5|^7.0", + "phpcollection/phpcollection": "~0.1", + "phpoption/phpoption": "^1.1" + }, + "conflict": { + "twig/twig": "<1.12" }, "require-dev": { "doctrine/orm": "~2.1", - "doctrine/phpcr-odm": "~1.0.1", - "jackalope/jackalope-doctrine-dbal": "1.0.*", + "doctrine/phpcr-odm": "^1.3|^2.0", + "ext-pdo_sqlite": "*", + "jackalope/jackalope-doctrine-dbal": "^1.1.5", + "phpunit/phpunit": "^4.8|^5.0", "propel/propel1": "~1.7", - "symfony/filesystem": "2.*", - "symfony/form": "~2.1", - "symfony/translation": "~2.0", - "symfony/validator": "~2.0", - "symfony/yaml": "2.*", - "twig/twig": ">=1.8,<2.0-dev" + "psr/container": "^1.0", + "symfony/dependency-injection": "^2.7|^3.3|^4.0", + "symfony/expression-language": "^2.6|^3.0", + "symfony/filesystem": "^2.1", + "symfony/form": "~2.1|^3.0", + "symfony/translation": "^2.1|^3.0", + "symfony/validator": "^2.2|^3.0", + "symfony/yaml": "^2.1|^3.0", + "twig/twig": "~1.12|~2.0" }, "suggest": { + "doctrine/cache": "Required if you like to use cache functionality.", + "doctrine/collections": "Required if you like to use doctrine collection types as ArrayCollection.", "symfony/yaml": "Required if you'd like to serialize data to YAML format." }, "type": "library", "extra": { "branch-alias": { - "dev-master": "0.15-dev" + "dev-1.x": "1.14-dev" } }, "autoload": { @@ -6942,14 +6542,16 @@ }, "notification-url": "https://packagist.org/downloads/", "license": [ - "Apache2" + "MIT" ], "authors": [ { - "name": "Johannes Schmitt", - "email": "schmittjoh@gmail.com", - "homepage": "https://github.com/schmittjoh", - "role": "Developer of wrapped JMSSerializerBundle" + "name": "Johannes M. Schmitt", + "email": "schmittjoh@gmail.com" + }, + { + "name": "Asmir Mustafic", + "email": "goetas@gmail.com" } ], "description": "Library for (de-)serializing data of any complexity; supports XML, JSON, and YAML.", @@ -6961,85 +6563,20 @@ "serialization", "xml" ], - "time": "2014-03-18T08:39:00+00:00" - }, - { - "name": "league/container", - "version": "2.4.1", - "source": { - "type": "git", - "url": "https://github.com/thephpleague/container.git", - "reference": "43f35abd03a12977a60ffd7095efd6a7808488c0" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/thephpleague/container/zipball/43f35abd03a12977a60ffd7095efd6a7808488c0", - "reference": "43f35abd03a12977a60ffd7095efd6a7808488c0", - "shasum": "" - }, - "require": { - "container-interop/container-interop": "^1.2", - "php": "^5.4.0 || ^7.0" - }, - "provide": { - "container-interop/container-interop-implementation": "^1.2", - "psr/container-implementation": "^1.0" - }, - "replace": { - "orno/di": "~2.0" - }, - "require-dev": { - "phpunit/phpunit": "4.*" - }, - "type": "library", - "extra": { - "branch-alias": { - "dev-2.x": "2.x-dev", - "dev-1.x": "1.x-dev" - } - }, - "autoload": { - "psr-4": { - "League\\Container\\": "src" - } - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "authors": [ - { - "name": "Phil Bennett", - "email": "philipobenito@gmail.com", - "homepage": "http://www.philipobenito.com", - "role": "Developer" - } - ], - "description": "A fast and intuitive dependency injection container.", - "homepage": "https://github.com/thephpleague/container", - "keywords": [ - "container", - "dependency", - "di", - "injection", - "league", - "provider", - "service" - ], - "time": "2017-05-10T09:20:27+00:00" + "time": "2020-02-22T20:59:37+00:00" }, { "name": "league/flysystem", - "version": "1.0.57", + "version": "1.0.66", "source": { "type": "git", "url": "https://github.com/thephpleague/flysystem.git", - "reference": "0e9db7f0b96b9f12dcf6f65bc34b72b1a30ea55a" + "reference": "021569195e15f8209b1c4bebb78bd66aa4f08c21" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/thephpleague/flysystem/zipball/0e9db7f0b96b9f12dcf6f65bc34b72b1a30ea55a", - "reference": "0e9db7f0b96b9f12dcf6f65bc34b72b1a30ea55a", + "url": "https://api.github.com/repos/thephpleague/flysystem/zipball/021569195e15f8209b1c4bebb78bd66aa4f08c21", + "reference": "021569195e15f8209b1c4bebb78bd66aa4f08c21", "shasum": "" }, "require": { @@ -7051,7 +6588,7 @@ }, "require-dev": { "phpspec/phpspec": "^3.4", - "phpunit/phpunit": "^5.7.10" + "phpunit/phpunit": "^5.7.26" }, "suggest": { "ext-fileinfo": "Required for MimeType", @@ -7110,7 +6647,7 @@ "sftp", "storage" ], - "time": "2019-10-16T21:01:05+00:00" + "time": "2020-03-17T18:58:12+00:00" }, { "name": "lusitanian/oauth", @@ -7220,34 +6757,42 @@ }, { "name": "magento/magento2-functional-testing-framework", - "version": "2.5.4", + "version": "dev-3.0.0-RC1", "source": { "type": "git", "url": "https://github.com/magento/magento2-functional-testing-framework.git", - "reference": "4f482ce22a755a812b76f81020ae71d502f9d043" + "reference": "5a3d146d082f8073a328d4e961da1c9f56a2c572" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/magento/magento2-functional-testing-framework/zipball/4f482ce22a755a812b76f81020ae71d502f9d043", - "reference": "4f482ce22a755a812b76f81020ae71d502f9d043", + "url": "https://api.github.com/repos/magento/magento2-functional-testing-framework/zipball/5a3d146d082f8073a328d4e961da1c9f56a2c572", + "reference": "5a3d146d082f8073a328d4e961da1c9f56a2c572", "shasum": "" }, "require": { "allure-framework/allure-codeception": "~1.3.0", + "aws/aws-sdk-php": "^3.132", "codeception/codeception": "~2.4.5", - "composer/composer": "^1.4", - "consolidation/robo": "^1.0.0", + "composer/composer": "^1.6", "csharpru/vault-php": "~3.5.3", "csharpru/vault-php-guzzle6-transport": "^2.0", "ext-curl": "*", - "flow/jsonpath": ">0.2", - "fzaninotto/faker": "^1.6", + "ext-dom": "*", + "ext-json": "*", + "ext-openssl": "*", "monolog/monolog": "^1.0", "mustache/mustache": "~2.5", - "php": "7.0.2||7.0.4||~7.0.6||~7.1.0||~7.2.0||~7.3.0", - "symfony/process": "^2.8 || ^3.1 || ^4.0", + "php": "~7.2.0||~7.3.0", + "php-webdriver/webdriver": "^1.8.0", + "symfony/console": "^4.4", + "symfony/finder": "^4.4", + "symfony/mime": "^5.0", + "symfony/process": "^4.4", "vlucas/phpdotenv": "^2.4" }, + "replace": { + "facebook/webdriver": "^1.7.1" + }, "require-dev": { "brainmaestro/composer-git-hooks": "^2.3.1", "codacy/coverage": "^1.4", @@ -7280,7 +6825,7 @@ ], "psr-4": { "Magento\\FunctionalTestingFramework\\": "src/Magento/FunctionalTestingFramework", - "MFTF\\": "dev/tests/functional/MFTF" + "MFTF\\": "dev/tests/functional/tests/MFTF" } }, "notification-url": "https://packagist.org/downloads/", @@ -7294,66 +6839,123 @@ "magento", "testing" ], - "time": "2019-12-12T20:14:00+00:00" + "time": "2020-03-24T22:00:41+00:00" + }, + { + "name": "mikey179/vfsstream", + "version": "v1.6.8", + "source": { + "type": "git", + "url": "https://github.com/bovigo/vfsStream.git", + "reference": "231c73783ebb7dd9ec77916c10037eff5a2b6efe" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/bovigo/vfsStream/zipball/231c73783ebb7dd9ec77916c10037eff5a2b6efe", + "reference": "231c73783ebb7dd9ec77916c10037eff5a2b6efe", + "shasum": "" + }, + "require": { + "php": ">=5.3.0" + }, + "require-dev": { + "phpunit/phpunit": "^4.5|^5.0" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "1.6.x-dev" + } + }, + "autoload": { + "psr-0": { + "org\\bovigo\\vfs\\": "src/main/php" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Frank Kleine", + "homepage": "http://frankkleine.de/", + "role": "Developer" + } + ], + "description": "Virtual file system to mock the real file system in unit tests.", + "homepage": "http://vfs.bovigo.org/", + "time": "2019-10-30T15:31:00+00:00" }, { - "name": "mikey179/vfsstream", - "version": "v1.6.8", + "name": "mtdowling/jmespath.php", + "version": "2.5.0", "source": { "type": "git", - "url": "https://github.com/bovigo/vfsStream.git", - "reference": "231c73783ebb7dd9ec77916c10037eff5a2b6efe" + "url": "https://github.com/jmespath/jmespath.php.git", + "reference": "52168cb9472de06979613d365c7f1ab8798be895" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/bovigo/vfsStream/zipball/231c73783ebb7dd9ec77916c10037eff5a2b6efe", - "reference": "231c73783ebb7dd9ec77916c10037eff5a2b6efe", + "url": "https://api.github.com/repos/jmespath/jmespath.php/zipball/52168cb9472de06979613d365c7f1ab8798be895", + "reference": "52168cb9472de06979613d365c7f1ab8798be895", "shasum": "" }, "require": { - "php": ">=5.3.0" + "php": ">=5.4.0", + "symfony/polyfill-mbstring": "^1.4" }, "require-dev": { - "phpunit/phpunit": "^4.5|^5.0" + "composer/xdebug-handler": "^1.2", + "phpunit/phpunit": "^4.8.36|^7.5.15" }, + "bin": [ + "bin/jp.php" + ], "type": "library", "extra": { "branch-alias": { - "dev-master": "1.6.x-dev" + "dev-master": "2.5-dev" } }, "autoload": { - "psr-0": { - "org\\bovigo\\vfs\\": "src/main/php" - } + "psr-4": { + "JmesPath\\": "src/" + }, + "files": [ + "src/JmesPath.php" + ] }, "notification-url": "https://packagist.org/downloads/", "license": [ - "BSD-3-Clause" + "MIT" ], "authors": [ { - "name": "Frank Kleine", - "homepage": "http://frankkleine.de/", - "role": "Developer" + "name": "Michael Dowling", + "email": "mtdowling@gmail.com", + "homepage": "https://github.com/mtdowling" } ], - "description": "Virtual file system to mock the real file system in unit tests.", - "homepage": "http://vfs.bovigo.org/", - "time": "2019-10-30T15:31:00+00:00" + "description": "Declaratively specify how to extract elements from a JSON document", + "keywords": [ + "json", + "jsonpath" + ], + "time": "2019-12-30T18:03:34+00:00" }, { "name": "mustache/mustache", - "version": "v2.12.0", + "version": "v2.13.0", "source": { "type": "git", "url": "https://github.com/bobthecow/mustache.php.git", - "reference": "fe8fe72e9d580591854de404cc59a1b83ca4d19e" + "reference": "e95c5a008c23d3151d59ea72484d4f72049ab7f4" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/bobthecow/mustache.php/zipball/fe8fe72e9d580591854de404cc59a1b83ca4d19e", - "reference": "fe8fe72e9d580591854de404cc59a1b83ca4d19e", + "url": "https://api.github.com/repos/bobthecow/mustache.php/zipball/e95c5a008c23d3151d59ea72484d4f72049ab7f4", + "reference": "e95c5a008c23d3151d59ea72484d4f72049ab7f4", "shasum": "" }, "require": { @@ -7386,20 +6988,20 @@ "mustache", "templating" ], - "time": "2017-07-11T12:54:05+00:00" + "time": "2019-11-23T21:40:31+00:00" }, { "name": "myclabs/deep-copy", - "version": "1.9.3", + "version": "1.9.5", "source": { "type": "git", "url": "https://github.com/myclabs/DeepCopy.git", - "reference": "007c053ae6f31bba39dfa19a7726f56e9763bbea" + "reference": "b2c28789e80a97badd14145fda39b545d83ca3ef" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/myclabs/DeepCopy/zipball/007c053ae6f31bba39dfa19a7726f56e9763bbea", - "reference": "007c053ae6f31bba39dfa19a7726f56e9763bbea", + "url": "https://api.github.com/repos/myclabs/DeepCopy/zipball/b2c28789e80a97badd14145fda39b545d83ca3ef", + "reference": "b2c28789e80a97badd14145fda39b545d83ca3ef", "shasum": "" }, "require": { @@ -7434,59 +7036,7 @@ "object", "object graph" ], - "time": "2019-08-09T12:45:53+00:00" - }, - { - "name": "nikic/php-parser", - "version": "v4.3.0", - "source": { - "type": "git", - "url": "https://github.com/nikic/PHP-Parser.git", - "reference": "9a9981c347c5c49d6dfe5cf826bb882b824080dc" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/nikic/PHP-Parser/zipball/9a9981c347c5c49d6dfe5cf826bb882b824080dc", - "reference": "9a9981c347c5c49d6dfe5cf826bb882b824080dc", - "shasum": "" - }, - "require": { - "ext-tokenizer": "*", - "php": ">=7.0" - }, - "require-dev": { - "ircmaxell/php-yacc": "0.0.5", - "phpunit/phpunit": "^6.5 || ^7.0 || ^8.0" - }, - "bin": [ - "bin/php-parse" - ], - "type": "library", - "extra": { - "branch-alias": { - "dev-master": "4.3-dev" - } - }, - "autoload": { - "psr-4": { - "PhpParser\\": "lib/PhpParser" - } - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "BSD-3-Clause" - ], - "authors": [ - { - "name": "Nikita Popov" - } - ], - "description": "A PHP parser written in PHP", - "keywords": [ - "parser", - "php" - ], - "time": "2019-11-08T13:50:10+00:00" + "time": "2020-01-17T21:11:47+00:00" }, { "name": "pdepend/pdepend", @@ -7681,6 +7231,71 @@ ], "time": "2018-02-15T16:58:55+00:00" }, + { + "name": "php-webdriver/webdriver", + "version": "1.8.2", + "source": { + "type": "git", + "url": "https://github.com/php-webdriver/php-webdriver.git", + "reference": "3308a70be084d6d7fd1ee5787b4c2e6eb4b70aab" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/php-webdriver/php-webdriver/zipball/3308a70be084d6d7fd1ee5787b4c2e6eb4b70aab", + "reference": "3308a70be084d6d7fd1ee5787b4c2e6eb4b70aab", + "shasum": "" + }, + "require": { + "ext-curl": "*", + "ext-json": "*", + "ext-zip": "*", + "php": "^5.6 || ~7.0", + "symfony/polyfill-mbstring": "^1.12", + "symfony/process": "^2.8 || ^3.1 || ^4.0 || ^5.0" + }, + "require-dev": { + "friendsofphp/php-cs-fixer": "^2.0", + "jakub-onderka/php-parallel-lint": "^1.0", + "php-coveralls/php-coveralls": "^2.0", + "php-mock/php-mock-phpunit": "^1.1", + "phpunit/phpunit": "^5.7", + "sebastian/environment": "^1.3.4 || ^2.0 || ^3.0", + "sminnee/phpunit-mock-objects": "^3.4", + "squizlabs/php_codesniffer": "^3.5", + "symfony/var-dumper": "^3.3 || ^4.0 || ^5.0" + }, + "suggest": { + "ext-SimpleXML": "For Firefox profile creation" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "1.8.x-dev" + } + }, + "autoload": { + "psr-4": { + "Facebook\\WebDriver\\": "lib/" + }, + "files": [ + "lib/Exception/TimeoutException.php" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "description": "A PHP client for Selenium WebDriver. Previously facebook/webdriver.", + "homepage": "https://github.com/php-webdriver/php-webdriver", + "keywords": [ + "Chromedriver", + "geckodriver", + "php", + "selenium", + "webdriver" + ], + "time": "2020-03-04T14:40:12+00:00" + }, { "name": "phpcollection/phpcollection", "version": "0.5.0", @@ -7731,16 +7346,16 @@ }, { "name": "phpcompatibility/php-compatibility", - "version": "9.3.4", + "version": "9.3.5", "source": { "type": "git", "url": "https://github.com/PHPCompatibility/PHPCompatibility.git", - "reference": "1f37659196e4f3113ea506a7efba201c52303bf1" + "reference": "9fb324479acf6f39452e0655d2429cc0d3914243" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/PHPCompatibility/PHPCompatibility/zipball/1f37659196e4f3113ea506a7efba201c52303bf1", - "reference": "1f37659196e4f3113ea506a7efba201c52303bf1", + "url": "https://api.github.com/repos/PHPCompatibility/PHPCompatibility/zipball/9fb324479acf6f39452e0655d2429cc0d3914243", + "reference": "9fb324479acf6f39452e0655d2429cc0d3914243", "shasum": "" }, "require": { @@ -7785,7 +7400,7 @@ "phpcs", "standards" ], - "time": "2019-11-15T04:12:02+00:00" + "time": "2019-12-27T09:44:58+00:00" }, { "name": "phpdocumentor/reflection-common", @@ -7841,40 +7456,38 @@ }, { "name": "phpdocumentor/reflection-docblock", - "version": "4.3.2", + "version": "5.1.0", "source": { "type": "git", "url": "https://github.com/phpDocumentor/ReflectionDocBlock.git", - "reference": "b83ff7cfcfee7827e1e78b637a5904fe6a96698e" + "reference": "cd72d394ca794d3466a3b2fc09d5a6c1dc86b47e" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/phpDocumentor/ReflectionDocBlock/zipball/b83ff7cfcfee7827e1e78b637a5904fe6a96698e", - "reference": "b83ff7cfcfee7827e1e78b637a5904fe6a96698e", + "url": "https://api.github.com/repos/phpDocumentor/ReflectionDocBlock/zipball/cd72d394ca794d3466a3b2fc09d5a6c1dc86b47e", + "reference": "cd72d394ca794d3466a3b2fc09d5a6c1dc86b47e", "shasum": "" }, "require": { - "php": "^7.0", - "phpdocumentor/reflection-common": "^1.0.0 || ^2.0.0", - "phpdocumentor/type-resolver": "~0.4 || ^1.0.0", - "webmozart/assert": "^1.0" + "ext-filter": "^7.1", + "php": "^7.2", + "phpdocumentor/reflection-common": "^2.0", + "phpdocumentor/type-resolver": "^1.0", + "webmozart/assert": "^1" }, "require-dev": { - "doctrine/instantiator": "^1.0.5", - "mockery/mockery": "^1.0", - "phpunit/phpunit": "^6.4" + "doctrine/instantiator": "^1", + "mockery/mockery": "^1" }, "type": "library", "extra": { "branch-alias": { - "dev-master": "4.x-dev" + "dev-master": "5.x-dev" } }, "autoload": { "psr-4": { - "phpDocumentor\\Reflection\\": [ - "src/" - ] + "phpDocumentor\\Reflection\\": "src" } }, "notification-url": "https://packagist.org/downloads/", @@ -7885,33 +7498,36 @@ { "name": "Mike van Riel", "email": "me@mikevanriel.com" + }, + { + "name": "Jaap van Otterdijk", + "email": "account@ijaap.nl" } ], "description": "With this component, a library can provide support for annotations via DocBlocks or otherwise retrieve information that is embedded in a DocBlock.", - "time": "2019-09-12T14:27:41+00:00" + "time": "2020-02-22T12:28:44+00:00" }, { "name": "phpdocumentor/type-resolver", - "version": "1.0.1", + "version": "1.1.0", "source": { "type": "git", "url": "https://github.com/phpDocumentor/TypeResolver.git", - "reference": "2e32a6d48972b2c1976ed5d8967145b6cec4a4a9" + "reference": "7462d5f123dfc080dfdf26897032a6513644fc95" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/phpDocumentor/TypeResolver/zipball/2e32a6d48972b2c1976ed5d8967145b6cec4a4a9", - "reference": "2e32a6d48972b2c1976ed5d8967145b6cec4a4a9", + "url": "https://api.github.com/repos/phpDocumentor/TypeResolver/zipball/7462d5f123dfc080dfdf26897032a6513644fc95", + "reference": "7462d5f123dfc080dfdf26897032a6513644fc95", "shasum": "" }, "require": { - "php": "^7.1", + "php": "^7.2", "phpdocumentor/reflection-common": "^2.0" }, "require-dev": { - "ext-tokenizer": "^7.1", - "mockery/mockery": "~1", - "phpunit/phpunit": "^7.0" + "ext-tokenizer": "^7.2", + "mockery/mockery": "~1" }, "type": "library", "extra": { @@ -7935,7 +7551,7 @@ } ], "description": "A PSR-5 based resolver of Class names, Types and Structural Element Names", - "time": "2019-08-22T18:11:29+00:00" + "time": "2020-02-18T18:59:58+00:00" }, { "name": "phpmd/phpmd", @@ -8007,33 +7623,34 @@ }, { "name": "phpoption/phpoption", - "version": "1.5.2", + "version": "1.7.3", "source": { "type": "git", "url": "https://github.com/schmittjoh/php-option.git", - "reference": "2ba2586380f8d2b44ad1b9feb61c371020b27793" + "reference": "4acfd6a4b33a509d8c88f50e5222f734b6aeebae" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/schmittjoh/php-option/zipball/2ba2586380f8d2b44ad1b9feb61c371020b27793", - "reference": "2ba2586380f8d2b44ad1b9feb61c371020b27793", + "url": "https://api.github.com/repos/schmittjoh/php-option/zipball/4acfd6a4b33a509d8c88f50e5222f734b6aeebae", + "reference": "4acfd6a4b33a509d8c88f50e5222f734b6aeebae", "shasum": "" }, "require": { - "php": ">=5.3.0" + "php": "^5.5.9 || ^7.0 || ^8.0" }, "require-dev": { - "phpunit/phpunit": "^4.7|^5.0" + "bamarni/composer-bin-plugin": "^1.3", + "phpunit/phpunit": "^4.8.35 || ^5.0 || ^6.0 || ^7.0" }, "type": "library", "extra": { "branch-alias": { - "dev-master": "1.5-dev" + "dev-master": "1.7-dev" } }, "autoload": { - "psr-0": { - "PhpOption\\": "src/" + "psr-4": { + "PhpOption\\": "src/PhpOption/" } }, "notification-url": "https://packagist.org/downloads/", @@ -8044,6 +7661,10 @@ { "name": "Johannes M. Schmitt", "email": "schmittjoh@gmail.com" + }, + { + "name": "Graham Campbell", + "email": "graham@alt-three.com" } ], "description": "Option Type for PHP", @@ -8053,37 +7674,37 @@ "php", "type" ], - "time": "2019-11-06T22:27:00+00:00" + "time": "2020-03-21T18:07:53+00:00" }, { "name": "phpspec/prophecy", - "version": "1.9.0", + "version": "v1.10.3", "source": { "type": "git", "url": "https://github.com/phpspec/prophecy.git", - "reference": "f6811d96d97bdf400077a0cc100ae56aa32b9203" + "reference": "451c3cd1418cf640de218914901e51b064abb093" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/phpspec/prophecy/zipball/f6811d96d97bdf400077a0cc100ae56aa32b9203", - "reference": "f6811d96d97bdf400077a0cc100ae56aa32b9203", + "url": "https://api.github.com/repos/phpspec/prophecy/zipball/451c3cd1418cf640de218914901e51b064abb093", + "reference": "451c3cd1418cf640de218914901e51b064abb093", "shasum": "" }, "require": { "doctrine/instantiator": "^1.0.2", "php": "^5.3|^7.0", "phpdocumentor/reflection-docblock": "^2.0|^3.0.2|^4.0|^5.0", - "sebastian/comparator": "^1.1|^2.0|^3.0", - "sebastian/recursion-context": "^1.0|^2.0|^3.0" + "sebastian/comparator": "^1.2.3|^2.0|^3.0|^4.0", + "sebastian/recursion-context": "^1.0|^2.0|^3.0|^4.0" }, "require-dev": { - "phpspec/phpspec": "^2.5|^3.2", + "phpspec/phpspec": "^2.5 || ^3.2", "phpunit/phpunit": "^4.8.35 || ^5.7 || ^6.5 || ^7.1" }, "type": "library", "extra": { "branch-alias": { - "dev-master": "1.8.x-dev" + "dev-master": "1.10.x-dev" } }, "autoload": { @@ -8116,24 +7737,23 @@ "spy", "stub" ], - "time": "2019-10-03T11:07:50+00:00" + "time": "2020-03-05T15:02:03+00:00" }, { "name": "phpstan/phpstan", - "version": "0.12.3", + "version": "0.12.18", "source": { "type": "git", "url": "https://github.com/phpstan/phpstan.git", - "reference": "c15a6ea55da71d8133399306f560cfe4d30301b7" + "reference": "1ce27fe29c8660a27926127d350d53d80c4d4286" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/phpstan/phpstan/zipball/c15a6ea55da71d8133399306f560cfe4d30301b7", - "reference": "c15a6ea55da71d8133399306f560cfe4d30301b7", + "url": "https://api.github.com/repos/phpstan/phpstan/zipball/1ce27fe29c8660a27926127d350d53d80c4d4286", + "reference": "1ce27fe29c8660a27926127d350d53d80c4d4286", "shasum": "" }, "require": { - "nikic/php-parser": "^4.3.0", "php": "^7.1" }, "bin": [ @@ -8156,7 +7776,7 @@ "MIT" ], "description": "PHPStan - PHP Static Analysis Tool", - "time": "2019-12-14T13:41:17+00:00" + "time": "2020-03-22T16:51:47+00:00" }, { "name": "phpunit/php-code-coverage", @@ -8925,23 +8545,27 @@ }, { "name": "sebastian/finder-facade", - "version": "1.2.2", + "version": "1.2.3", "source": { "type": "git", "url": "https://github.com/sebastianbergmann/finder-facade.git", - "reference": "4a3174709c2dc565fe5fb26fcf827f6a1fc7b09f" + "reference": "167c45d131f7fc3d159f56f191a0a22228765e16" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/finder-facade/zipball/4a3174709c2dc565fe5fb26fcf827f6a1fc7b09f", - "reference": "4a3174709c2dc565fe5fb26fcf827f6a1fc7b09f", + "url": "https://api.github.com/repos/sebastianbergmann/finder-facade/zipball/167c45d131f7fc3d159f56f191a0a22228765e16", + "reference": "167c45d131f7fc3d159f56f191a0a22228765e16", "shasum": "" }, "require": { - "symfony/finder": "~2.3|~3.0|~4.0", - "theseer/fdomdocument": "~1.3" + "php": "^7.1", + "symfony/finder": "^2.3|^3.0|^4.0|^5.0", + "theseer/fdomdocument": "^1.6" }, "type": "library", + "extra": { + "branch-alias": [] + }, "autoload": { "classmap": [ "src/" @@ -8960,7 +8584,7 @@ ], "description": "FinderFacade is a convenience wrapper for Symfony's Finder component.", "homepage": "https://github.com/sebastianbergmann/finder-facade", - "time": "2017-11-18T17:31:49+00:00" + "time": "2020-01-16T08:08:45+00:00" }, { "name": "sebastian/global-state", @@ -9346,27 +8970,27 @@ }, { "name": "symfony/browser-kit", - "version": "v4.3.8", + "version": "v4.4.5", "source": { "type": "git", "url": "https://github.com/symfony/browser-kit.git", - "reference": "b14fa08508afd152257d5dcc7adb5f278654d972" + "reference": "090ce406505149d6852a7c03b0346dec3b8cf612" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/browser-kit/zipball/b14fa08508afd152257d5dcc7adb5f278654d972", - "reference": "b14fa08508afd152257d5dcc7adb5f278654d972", + "url": "https://api.github.com/repos/symfony/browser-kit/zipball/090ce406505149d6852a7c03b0346dec3b8cf612", + "reference": "090ce406505149d6852a7c03b0346dec3b8cf612", "shasum": "" }, "require": { "php": "^7.1.3", - "symfony/dom-crawler": "~3.4|~4.0" + "symfony/dom-crawler": "^3.4|^4.0|^5.0" }, "require-dev": { - "symfony/css-selector": "~3.4|~4.0", - "symfony/http-client": "^4.3", - "symfony/mime": "^4.3", - "symfony/process": "~3.4|~4.0" + "symfony/css-selector": "^3.4|^4.0|^5.0", + "symfony/http-client": "^4.3|^5.0", + "symfony/mime": "^4.3|^5.0", + "symfony/process": "^3.4|^4.0|^5.0" }, "suggest": { "symfony/process": "" @@ -9374,7 +8998,7 @@ "type": "library", "extra": { "branch-alias": { - "dev-master": "4.3-dev" + "dev-master": "4.4-dev" } }, "autoload": { @@ -9401,36 +9025,36 @@ ], "description": "Symfony BrowserKit Component", "homepage": "https://symfony.com", - "time": "2019-10-28T17:07:32+00:00" + "time": "2020-02-23T10:00:59+00:00" }, { "name": "symfony/config", - "version": "v4.3.8", + "version": "v4.4.5", "source": { "type": "git", "url": "https://github.com/symfony/config.git", - "reference": "8267214841c44d315a55242ea867684eb43c42ce" + "reference": "cbfef5ae91ccd3b06621c18d58cd355c68c87ae9" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/config/zipball/8267214841c44d315a55242ea867684eb43c42ce", - "reference": "8267214841c44d315a55242ea867684eb43c42ce", + "url": "https://api.github.com/repos/symfony/config/zipball/cbfef5ae91ccd3b06621c18d58cd355c68c87ae9", + "reference": "cbfef5ae91ccd3b06621c18d58cd355c68c87ae9", "shasum": "" }, "require": { "php": "^7.1.3", - "symfony/filesystem": "~3.4|~4.0", + "symfony/filesystem": "^3.4|^4.0|^5.0", "symfony/polyfill-ctype": "~1.8" }, "conflict": { "symfony/finder": "<3.4" }, "require-dev": { - "symfony/dependency-injection": "~3.4|~4.0", - "symfony/event-dispatcher": "~3.4|~4.0", - "symfony/finder": "~3.4|~4.0", - "symfony/messenger": "~4.1", - "symfony/yaml": "~3.4|~4.0" + "symfony/event-dispatcher": "^3.4|^4.0|^5.0", + "symfony/finder": "^3.4|^4.0|^5.0", + "symfony/messenger": "^4.1|^5.0", + "symfony/service-contracts": "^1.1|^2", + "symfony/yaml": "^3.4|^4.0|^5.0" }, "suggest": { "symfony/yaml": "To use the yaml reference dumper" @@ -9438,7 +9062,7 @@ "type": "library", "extra": { "branch-alias": { - "dev-master": "4.3-dev" + "dev-master": "4.4-dev" } }, "autoload": { @@ -9465,29 +9089,29 @@ ], "description": "Symfony Config Component", "homepage": "https://symfony.com", - "time": "2019-11-08T08:31:27+00:00" + "time": "2020-02-04T09:32:40+00:00" }, { "name": "symfony/dependency-injection", - "version": "v4.3.8", + "version": "v4.4.5", "source": { "type": "git", "url": "https://github.com/symfony/dependency-injection.git", - "reference": "80c6d9e19467dfbba14f830ed478eb592ce51b64" + "reference": "ebb2e882e8c9e2eb990aa61ddcd389848466e342" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/dependency-injection/zipball/80c6d9e19467dfbba14f830ed478eb592ce51b64", - "reference": "80c6d9e19467dfbba14f830ed478eb592ce51b64", + "url": "https://api.github.com/repos/symfony/dependency-injection/zipball/ebb2e882e8c9e2eb990aa61ddcd389848466e342", + "reference": "ebb2e882e8c9e2eb990aa61ddcd389848466e342", "shasum": "" }, "require": { "php": "^7.1.3", "psr/container": "^1.0", - "symfony/service-contracts": "^1.1.6" + "symfony/service-contracts": "^1.1.6|^2" }, "conflict": { - "symfony/config": "<4.3", + "symfony/config": "<4.3|>=5.0", "symfony/finder": "<3.4", "symfony/proxy-manager-bridge": "<3.4", "symfony/yaml": "<3.4" @@ -9498,8 +9122,8 @@ }, "require-dev": { "symfony/config": "^4.3", - "symfony/expression-language": "~3.4|~4.0", - "symfony/yaml": "~3.4|~4.0" + "symfony/expression-language": "^3.4|^4.0|^5.0", + "symfony/yaml": "^3.4|^4.0|^5.0" }, "suggest": { "symfony/config": "", @@ -9511,7 +9135,7 @@ "type": "library", "extra": { "branch-alias": { - "dev-master": "4.3-dev" + "dev-master": "4.4-dev" } }, "autoload": { @@ -9538,20 +9162,20 @@ ], "description": "Symfony DependencyInjection Component", "homepage": "https://symfony.com", - "time": "2019-11-08T16:22:27+00:00" + "time": "2020-02-29T09:50:10+00:00" }, { "name": "symfony/dom-crawler", - "version": "v4.3.8", + "version": "v4.4.5", "source": { "type": "git", "url": "https://github.com/symfony/dom-crawler.git", - "reference": "4b9efd5708c3a38593e19b6a33e40867f4f89d72" + "reference": "11dcf08f12f29981bf770f097a5d64d65bce5929" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/dom-crawler/zipball/4b9efd5708c3a38593e19b6a33e40867f4f89d72", - "reference": "4b9efd5708c3a38593e19b6a33e40867f4f89d72", + "url": "https://api.github.com/repos/symfony/dom-crawler/zipball/11dcf08f12f29981bf770f097a5d64d65bce5929", + "reference": "11dcf08f12f29981bf770f097a5d64d65bce5929", "shasum": "" }, "require": { @@ -9564,7 +9188,7 @@ }, "require-dev": { "masterminds/html5": "^2.6", - "symfony/css-selector": "~3.4|~4.0" + "symfony/css-selector": "^3.4|^4.0|^5.0" }, "suggest": { "symfony/css-selector": "" @@ -9572,7 +9196,7 @@ "type": "library", "extra": { "branch-alias": { - "dev-master": "4.3-dev" + "dev-master": "4.4-dev" } }, "autoload": { @@ -9599,35 +9223,35 @@ ], "description": "Symfony DomCrawler Component", "homepage": "https://symfony.com", - "time": "2019-10-28T17:07:32+00:00" + "time": "2020-02-29T10:05:28+00:00" }, { "name": "symfony/http-foundation", - "version": "v2.8.52", + "version": "v5.0.5", "source": { "type": "git", "url": "https://github.com/symfony/http-foundation.git", - "reference": "3929d9fe8148d17819ad0178c748b8d339420709" + "reference": "6f9c2ba72f4295d7ce6cf9f79dbb18036291d335" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/http-foundation/zipball/3929d9fe8148d17819ad0178c748b8d339420709", - "reference": "3929d9fe8148d17819ad0178c748b8d339420709", + "url": "https://api.github.com/repos/symfony/http-foundation/zipball/6f9c2ba72f4295d7ce6cf9f79dbb18036291d335", + "reference": "6f9c2ba72f4295d7ce6cf9f79dbb18036291d335", "shasum": "" }, "require": { - "php": ">=5.3.9", - "symfony/polyfill-mbstring": "~1.1", - "symfony/polyfill-php54": "~1.0", - "symfony/polyfill-php55": "~1.0" + "php": "^7.2.5", + "symfony/mime": "^4.4|^5.0", + "symfony/polyfill-mbstring": "~1.1" }, "require-dev": { - "symfony/expression-language": "~2.4|~3.0.0" + "predis/predis": "~1.0", + "symfony/expression-language": "^4.4|^5.0" }, "type": "library", "extra": { "branch-alias": { - "dev-master": "2.8-dev" + "dev-master": "5.0-dev" } }, "autoload": { @@ -9654,34 +9278,43 @@ ], "description": "Symfony HttpFoundation Component", "homepage": "https://symfony.com", - "time": "2019-11-12T12:34:41+00:00" + "time": "2020-02-14T07:43:07+00:00" }, { - "name": "symfony/options-resolver", - "version": "v4.3.8", + "name": "symfony/mime", + "version": "v5.0.5", "source": { "type": "git", - "url": "https://github.com/symfony/options-resolver.git", - "reference": "f46c7fc8e207bd8a2188f54f8738f232533765a4" + "url": "https://github.com/symfony/mime.git", + "reference": "9b3e5b5e58c56bbd76628c952d2b78556d305f3c" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/options-resolver/zipball/f46c7fc8e207bd8a2188f54f8738f232533765a4", - "reference": "f46c7fc8e207bd8a2188f54f8738f232533765a4", + "url": "https://api.github.com/repos/symfony/mime/zipball/9b3e5b5e58c56bbd76628c952d2b78556d305f3c", + "reference": "9b3e5b5e58c56bbd76628c952d2b78556d305f3c", "shasum": "" }, "require": { - "php": "^7.1.3" + "php": "^7.2.5", + "symfony/polyfill-intl-idn": "^1.10", + "symfony/polyfill-mbstring": "^1.0" + }, + "conflict": { + "symfony/mailer": "<4.4" + }, + "require-dev": { + "egulias/email-validator": "^2.1.10", + "symfony/dependency-injection": "^4.4|^5.0" }, "type": "library", "extra": { "branch-alias": { - "dev-master": "4.3-dev" + "dev-master": "5.0-dev" } }, "autoload": { "psr-4": { - "Symfony\\Component\\OptionsResolver\\": "" + "Symfony\\Component\\Mime\\": "" }, "exclude-from-classmap": [ "/Tests/" @@ -9701,47 +9334,43 @@ "homepage": "https://symfony.com/contributors" } ], - "description": "Symfony OptionsResolver Component", + "description": "A library to manipulate MIME messages", "homepage": "https://symfony.com", "keywords": [ - "config", - "configuration", - "options" + "mime", + "mime-type" ], - "time": "2019-10-28T20:59:01+00:00" + "time": "2020-02-04T09:41:09+00:00" }, { - "name": "symfony/polyfill-php54", - "version": "v1.12.0", + "name": "symfony/options-resolver", + "version": "v4.4.5", "source": { "type": "git", - "url": "https://github.com/symfony/polyfill-php54.git", - "reference": "a043bcced870214922fbb4bf22679d431ec0296a" + "url": "https://github.com/symfony/options-resolver.git", + "reference": "9a02d6662660fe7bfadad63b5f0b0718d4c8b6b0" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/polyfill-php54/zipball/a043bcced870214922fbb4bf22679d431ec0296a", - "reference": "a043bcced870214922fbb4bf22679d431ec0296a", + "url": "https://api.github.com/repos/symfony/options-resolver/zipball/9a02d6662660fe7bfadad63b5f0b0718d4c8b6b0", + "reference": "9a02d6662660fe7bfadad63b5f0b0718d4c8b6b0", "shasum": "" }, "require": { - "php": ">=5.3.3" + "php": "^7.1.3" }, "type": "library", "extra": { "branch-alias": { - "dev-master": "1.12-dev" + "dev-master": "4.4-dev" } }, "autoload": { "psr-4": { - "Symfony\\Polyfill\\Php54\\": "" + "Symfony\\Component\\OptionsResolver\\": "" }, - "files": [ - "bootstrap.php" - ], - "classmap": [ - "Resources/stubs" + "exclude-from-classmap": [ + "/Tests/" ] }, "notification-url": "https://packagist.org/downloads/", @@ -9750,51 +9379,54 @@ ], "authors": [ { - "name": "Nicolas Grekas", - "email": "p@tchwork.com" + "name": "Fabien Potencier", + "email": "fabien@symfony.com" }, { "name": "Symfony Community", "homepage": "https://symfony.com/contributors" } ], - "description": "Symfony polyfill backporting some PHP 5.4+ features to lower PHP versions", + "description": "Symfony OptionsResolver Component", "homepage": "https://symfony.com", "keywords": [ - "compatibility", - "polyfill", - "portable", - "shim" + "config", + "configuration", + "options" ], - "time": "2019-08-06T08:03:45+00:00" + "time": "2020-01-04T13:00:46+00:00" }, { - "name": "symfony/polyfill-php55", - "version": "v1.12.0", + "name": "symfony/polyfill-intl-idn", + "version": "v1.15.0", "source": { "type": "git", - "url": "https://github.com/symfony/polyfill-php55.git", - "reference": "548bb39407e78e54f785b4e18c7e0d5d9e493265" + "url": "https://github.com/symfony/polyfill-intl-idn.git", + "reference": "47bd6aa45beb1cd7c6a16b7d1810133b728bdfcf" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/polyfill-php55/zipball/548bb39407e78e54f785b4e18c7e0d5d9e493265", - "reference": "548bb39407e78e54f785b4e18c7e0d5d9e493265", + "url": "https://api.github.com/repos/symfony/polyfill-intl-idn/zipball/47bd6aa45beb1cd7c6a16b7d1810133b728bdfcf", + "reference": "47bd6aa45beb1cd7c6a16b7d1810133b728bdfcf", "shasum": "" }, "require": { - "ircmaxell/password-compat": "~1.0", - "php": ">=5.3.3" + "php": ">=5.3.3", + "symfony/polyfill-mbstring": "^1.3", + "symfony/polyfill-php72": "^1.10" + }, + "suggest": { + "ext-intl": "For best performance" }, "type": "library", "extra": { "branch-alias": { - "dev-master": "1.12-dev" + "dev-master": "1.15-dev" } }, "autoload": { "psr-4": { - "Symfony\\Polyfill\\Php55\\": "" + "Symfony\\Polyfill\\Intl\\Idn\\": "" }, "files": [ "bootstrap.php" @@ -9806,36 +9438,38 @@ ], "authors": [ { - "name": "Nicolas Grekas", - "email": "p@tchwork.com" + "name": "Laurent Bassin", + "email": "laurent@bassin.info" }, { "name": "Symfony Community", "homepage": "https://symfony.com/contributors" } ], - "description": "Symfony polyfill backporting some PHP 5.5+ features to lower PHP versions", + "description": "Symfony polyfill for intl's idn_to_ascii and idn_to_utf8 functions", "homepage": "https://symfony.com", "keywords": [ "compatibility", + "idn", + "intl", "polyfill", "portable", "shim" ], - "time": "2019-08-06T08:03:45+00:00" + "time": "2020-03-09T19:04:49+00:00" }, { "name": "symfony/polyfill-php70", - "version": "v1.12.0", + "version": "v1.15.0", "source": { "type": "git", "url": "https://github.com/symfony/polyfill-php70.git", - "reference": "54b4c428a0054e254223797d2713c31e08610831" + "reference": "2a18e37a489803559284416df58c71ccebe50bf0" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/polyfill-php70/zipball/54b4c428a0054e254223797d2713c31e08610831", - "reference": "54b4c428a0054e254223797d2713c31e08610831", + "url": "https://api.github.com/repos/symfony/polyfill-php70/zipball/2a18e37a489803559284416df58c71ccebe50bf0", + "reference": "2a18e37a489803559284416df58c71ccebe50bf0", "shasum": "" }, "require": { @@ -9845,7 +9479,7 @@ "type": "library", "extra": { "branch-alias": { - "dev-master": "1.12-dev" + "dev-master": "1.15-dev" } }, "autoload": { @@ -9881,20 +9515,20 @@ "portable", "shim" ], - "time": "2019-08-06T08:03:45+00:00" + "time": "2020-02-27T09:26:54+00:00" }, { "name": "symfony/polyfill-php72", - "version": "v1.12.0", + "version": "v1.15.0", "source": { "type": "git", "url": "https://github.com/symfony/polyfill-php72.git", - "reference": "04ce3335667451138df4307d6a9b61565560199e" + "reference": "37b0976c78b94856543260ce09b460a7bc852747" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/polyfill-php72/zipball/04ce3335667451138df4307d6a9b61565560199e", - "reference": "04ce3335667451138df4307d6a9b61565560199e", + "url": "https://api.github.com/repos/symfony/polyfill-php72/zipball/37b0976c78b94856543260ce09b460a7bc852747", + "reference": "37b0976c78b94856543260ce09b460a7bc852747", "shasum": "" }, "require": { @@ -9903,7 +9537,7 @@ "type": "library", "extra": { "branch-alias": { - "dev-master": "1.12-dev" + "dev-master": "1.15-dev" } }, "autoload": { @@ -9936,88 +9570,30 @@ "portable", "shim" ], - "time": "2019-08-06T08:03:45+00:00" - }, - { - "name": "symfony/service-contracts", - "version": "v1.1.8", - "source": { - "type": "git", - "url": "https://github.com/symfony/service-contracts.git", - "reference": "ffc7f5692092df31515df2a5ecf3b7302b3ddacf" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/symfony/service-contracts/zipball/ffc7f5692092df31515df2a5ecf3b7302b3ddacf", - "reference": "ffc7f5692092df31515df2a5ecf3b7302b3ddacf", - "shasum": "" - }, - "require": { - "php": "^7.1.3", - "psr/container": "^1.0" - }, - "suggest": { - "symfony/service-implementation": "" - }, - "type": "library", - "extra": { - "branch-alias": { - "dev-master": "1.1-dev" - } - }, - "autoload": { - "psr-4": { - "Symfony\\Contracts\\Service\\": "" - } - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "authors": [ - { - "name": "Nicolas Grekas", - "email": "p@tchwork.com" - }, - { - "name": "Symfony Community", - "homepage": "https://symfony.com/contributors" - } - ], - "description": "Generic abstractions related to writing services", - "homepage": "https://symfony.com", - "keywords": [ - "abstractions", - "contracts", - "decoupling", - "interfaces", - "interoperability", - "standards" - ], - "time": "2019-10-14T12:27:06+00:00" + "time": "2020-02-27T09:26:54+00:00" }, { "name": "symfony/stopwatch", - "version": "v4.3.8", + "version": "v4.4.5", "source": { "type": "git", "url": "https://github.com/symfony/stopwatch.git", - "reference": "e96c259de6abcd0cead71f0bf4d730d53ee464d0" + "reference": "abc08d7c48987829bac301347faa10f7e8bbf4fb" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/stopwatch/zipball/e96c259de6abcd0cead71f0bf4d730d53ee464d0", - "reference": "e96c259de6abcd0cead71f0bf4d730d53ee464d0", + "url": "https://api.github.com/repos/symfony/stopwatch/zipball/abc08d7c48987829bac301347faa10f7e8bbf4fb", + "reference": "abc08d7c48987829bac301347faa10f7e8bbf4fb", "shasum": "" }, "require": { "php": "^7.1.3", - "symfony/service-contracts": "^1.0" + "symfony/service-contracts": "^1.0|^2" }, "type": "library", "extra": { "branch-alias": { - "dev-master": "4.3-dev" + "dev-master": "4.4-dev" } }, "autoload": { @@ -10044,20 +9620,20 @@ ], "description": "Symfony Stopwatch Component", "homepage": "https://symfony.com", - "time": "2019-11-05T14:48:09+00:00" + "time": "2020-01-04T13:00:46+00:00" }, { "name": "symfony/yaml", - "version": "v4.3.8", + "version": "v4.4.5", "source": { "type": "git", "url": "https://github.com/symfony/yaml.git", - "reference": "324cf4b19c345465fad14f3602050519e09e361d" + "reference": "94d005c176db2080e98825d98e01e8b311a97a88" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/yaml/zipball/324cf4b19c345465fad14f3602050519e09e361d", - "reference": "324cf4b19c345465fad14f3602050519e09e361d", + "url": "https://api.github.com/repos/symfony/yaml/zipball/94d005c176db2080e98825d98e01e8b311a97a88", + "reference": "94d005c176db2080e98825d98e01e8b311a97a88", "shasum": "" }, "require": { @@ -10068,7 +9644,7 @@ "symfony/console": "<3.4" }, "require-dev": { - "symfony/console": "~3.4|~4.0" + "symfony/console": "^3.4|^4.0|^5.0" }, "suggest": { "symfony/console": "For validating YAML files using the lint command" @@ -10076,7 +9652,7 @@ "type": "library", "extra": { "branch-alias": { - "dev-master": "4.3-dev" + "dev-master": "4.4-dev" } }, "autoload": { @@ -10103,7 +9679,7 @@ ], "description": "Symfony Yaml Component", "homepage": "https://symfony.com", - "time": "2019-10-30T12:58:49+00:00" + "time": "2020-02-03T10:46:43+00:00" }, { "name": "theseer/fdomdocument", @@ -10238,31 +9814,29 @@ }, { "name": "webmozart/assert", - "version": "1.5.0", + "version": "1.7.0", "source": { "type": "git", "url": "https://github.com/webmozart/assert.git", - "reference": "88e6d84706d09a236046d686bbea96f07b3a34f4" + "reference": "aed98a490f9a8f78468232db345ab9cf606cf598" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/webmozart/assert/zipball/88e6d84706d09a236046d686bbea96f07b3a34f4", - "reference": "88e6d84706d09a236046d686bbea96f07b3a34f4", + "url": "https://api.github.com/repos/webmozart/assert/zipball/aed98a490f9a8f78468232db345ab9cf606cf598", + "reference": "aed98a490f9a8f78468232db345ab9cf606cf598", "shasum": "" }, "require": { "php": "^5.3.3 || ^7.0", "symfony/polyfill-ctype": "^1.8" }, + "conflict": { + "vimeo/psalm": "<3.6.0" + }, "require-dev": { "phpunit/phpunit": "^4.8.36 || ^7.5.13" }, "type": "library", - "extra": { - "branch-alias": { - "dev-master": "1.3-dev" - } - }, "autoload": { "psr-4": { "Webmozart\\Assert\\": "src/" @@ -10284,7 +9858,7 @@ "check", "validate" ], - "time": "2019-08-24T08:43:50+00:00" + "time": "2020-02-14T12:15:55+00:00" }, { "name": "weew/helpers-array", @@ -10327,6 +9901,8 @@ "aliases": [], "minimum-stability": "stable", "stability-flags": { + "magento/composer": 20, + "magento/magento2-functional-testing-framework": 20, "phpmd/phpmd": 0 }, "prefer-stable": true, diff --git a/dev/tests/acceptance/tests/_data/catalog_product_import_bundle.csv b/dev/tests/acceptance/tests/_data/catalog_product_import_bundle.csv new file mode 100644 index 0000000000000..6804675940a02 --- /dev/null +++ b/dev/tests/acceptance/tests/_data/catalog_product_import_bundle.csv @@ -0,0 +1,3 @@ +sku,store_view_code,attribute_set_code,product_type,categories,product_websites,name,description,short_description,weight,product_online,tax_class_name,visibility,price,special_price,special_price_from_date,special_price_to_date,url_key,meta_title,meta_keywords,meta_description,base_image,base_image_label,small_image,small_image_label,thumbnail_image,thumbnail_image_label,swatch_image,swatch_image_label,created_at,updated_at,new_from_date,new_to_date,display_product_options_in,map_price,msrp_price,map_enabled,gift_message_available,custom_design,custom_design_from,custom_design_to,custom_layout_update,page_layout,product_options_container,msrp_display_actual_price_type,country_of_manufacture,additional_attributes,qty,out_of_stock_qty,use_config_min_qty,is_qty_decimal,allow_backorders,use_config_backorders,min_cart_qty,use_config_min_sale_qty,max_cart_qty,use_config_max_sale_qty,is_in_stock,notify_on_stock_below,use_config_notify_stock_qty,manage_stock,use_config_manage_stock,use_config_qty_increments,qty_increments,use_config_enable_qty_inc,enable_qty_increments,is_decimal_divided,website_id,related_skus,related_position,crosssell_skus,crosssell_position,upsell_skus,upsell_position,additional_images,additional_image_labels,hide_from_product_page,custom_options,bundle_price_type,bundle_sku_type,bundle_price_view,bundle_weight_type,bundle_values,bundle_shipment_type,associated_skus,downloadable_links,downloadable_samples,configurable_variations,configurable_variation_labels +Simple,,Default,simple,"Default Category/New",base,Simple,,,1.000000,1,"Taxable Goods","Catalog, Search",100.000000,,,,simple,Simple,Simple,"Simple ",,,,,,,,,"3/18/20, 6:56 AM","3/18/20, 6:56 AM",,,"Block after Info Column",,,,"Use config",,,,,,,"Use config",,,1000.0000,0.0000,1,0,0,1,1.0000,1,10000.0000,1,1,1.0000,1,1,1,1,1.0000,1,0,0,0,,,,,,,,,,,,,,,,,,,,, +Bundle,,Default,bundle,"Default Category/New",base,Bundle,,,,1,"Taxable Goods","Catalog, Search",,,,,bundle,Bundle,Bundle,"Bundle ",,,,,,,,,"3/18/20, 6:57 AM","3/18/20, 6:57 AM",,,"Block after Info Column",,,,"Use config",,,,,,,"Use config",,,0.0000,0.0000,1,0,0,1,1.0000,1,10000.0000,1,1,1.0000,1,1,1,1,1.0000,1,0,0,0,,,,,,,,,,,dynamic,dynamic,"Price range",dynamic,"name=Test Option,type=select,required=1,sku=Simple,price=0.0000,default=1,default_qty=1.0000,price_type=fixed,can_change_qty=0",together,,,,, diff --git a/dev/tests/acceptance/tests/_suite/WYSIWYGDisabledSuite.xml b/dev/tests/acceptance/tests/_suite/WYSIWYGDisabledSuite.xml index 65c2bb7004503..924c712404dfe 100644 --- a/dev/tests/acceptance/tests/_suite/WYSIWYGDisabledSuite.xml +++ b/dev/tests/acceptance/tests/_suite/WYSIWYGDisabledSuite.xml @@ -5,7 +5,7 @@ * See COPYING.txt for license details. */ --> -<suites xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="../../../../../vendor/magento/magento2-functional-testing-framework/src/Magento/FunctionalTestingFramework/Suite/etc/suiteSchema.xsd"> +<suites xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="urn:magento:mftf:Suite/etc/suiteSchema.xsd"> <suite name="WYSIWYGDisabledSuite"> <before> <magentoCLI stepKey="disableWYSYWYG" command="config:set cms/wysiwyg/enabled disabled" /> @@ -20,4 +20,4 @@ <magentoCLI stepKey="disableWYSYWYG" command="config:set cms/wysiwyg/enabled enabled" /> </after> </suite> -</suites> \ No newline at end of file +</suites> diff --git a/dev/tests/acceptance/tests/_suite/suite.xml.sample b/dev/tests/acceptance/tests/_suite/suite.xml.sample index 4c6a78afd40d0..30cf2eef05682 100644 --- a/dev/tests/acceptance/tests/_suite/suite.xml.sample +++ b/dev/tests/acceptance/tests/_suite/suite.xml.sample @@ -5,7 +5,7 @@ * See COPYING.txt for license details. */ --> -<suites xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="../../vendor/magento/magento2-functional-testing-framework/src/Magento/FunctionalTestingFramework/Suite/etc/suiteSchema.xsd"> +<suites xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="urn:magento:mftf:Suite/etc/suiteSchema.xsd"> <suite name="sample"> <before> <createData entity="defaultCategory" stepKey="createCategory"/> diff --git a/app/code/Magento/Authorizenet/Test/Mftf/LICENSE.txt b/dev/tests/acceptance/tests/functional/Magento/ConfigurableProductCatalogSearch/LICENSE.txt similarity index 100% rename from app/code/Magento/Authorizenet/Test/Mftf/LICENSE.txt rename to dev/tests/acceptance/tests/functional/Magento/ConfigurableProductCatalogSearch/LICENSE.txt diff --git a/app/code/Magento/Authorizenet/Test/Mftf/LICENSE_AFL.txt b/dev/tests/acceptance/tests/functional/Magento/ConfigurableProductCatalogSearch/LICENSE_AFL.txt similarity index 100% rename from app/code/Magento/Authorizenet/Test/Mftf/LICENSE_AFL.txt rename to dev/tests/acceptance/tests/functional/Magento/ConfigurableProductCatalogSearch/LICENSE_AFL.txt diff --git a/dev/tests/acceptance/tests/functional/Magento/FunctionalTest/ConfigurableProductCatalogSearch/README.md b/dev/tests/acceptance/tests/functional/Magento/ConfigurableProductCatalogSearch/README.md similarity index 100% rename from dev/tests/acceptance/tests/functional/Magento/FunctionalTest/ConfigurableProductCatalogSearch/README.md rename to dev/tests/acceptance/tests/functional/Magento/ConfigurableProductCatalogSearch/README.md diff --git a/dev/tests/acceptance/tests/functional/Magento/ConfigurableProductCatalogSearch/Test/EndToEndB2CGuestUserTest/EndToEndB2CGuestUserMysqlTest.xml b/dev/tests/acceptance/tests/functional/Magento/ConfigurableProductCatalogSearch/Test/EndToEndB2CGuestUserTest/EndToEndB2CGuestUserMysqlTest.xml new file mode 100644 index 0000000000000..77040cbbd3a03 --- /dev/null +++ b/dev/tests/acceptance/tests/functional/Magento/ConfigurableProductCatalogSearch/Test/EndToEndB2CGuestUserTest/EndToEndB2CGuestUserMysqlTest.xml @@ -0,0 +1,35 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + /** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +--> +<tests xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/testSchema.xsd"> + <test name="EndToEndB2CGuestUserMysqlTest"> + <!-- Search configurable product --> + <comment userInput="Search configurable product" stepKey="commentSearchConfigurableProduct" after="searchAssertSimpleProduct2ImageNotDefault"/> + <actionGroup ref="StorefrontCheckCategoryConfigurableProductActionGroup" stepKey="searchAssertFilterCategoryConfigProduct" after="commentSearchConfigurableProduct"> + <argument name="product" value="$$createConfigProduct$$"/> + <argument name="optionProduct" value="$$createConfigChildProduct1$$"/> + </actionGroup> + <!-- @TODO: Move Image check to action group after MQE-697 is fixed --> + <grabAttributeFrom selector="{{StorefrontCategoryProductSection.ProductImageByName($$createConfigProduct.name$$)}}" userInput="src" stepKey="searchGrabConfigProductImageSrc" after="searchAssertFilterCategoryConfigProduct"/> + <assertNotRegExp stepKey="searchAssertConfigProductImageNotDefault" after="searchGrabConfigProductImageSrc"> + <actualResult type="const">$searchGrabConfigProductImageSrc</actualResult> + <expectedResult type="const">'/placeholder\/small_image\.jpg/'</expectedResult> + </assertNotRegExp> + <click selector="{{StorefrontCategoryProductSection.ProductTitleByName($$createConfigProduct.name$$)}}" stepKey="searchClickConfigProductView" after="searchAssertConfigProductImageNotDefault"/> + <actionGroup ref="StorefrontCheckConfigurableProductActionGroup" stepKey="searchAssertConfigProductPage" after="searchClickConfigProductView"> + <argument name="product" value="$$createConfigProduct$$"/> + <argument name="optionProduct" value="$$createConfigChildProduct1$$"/> + </actionGroup> + <!-- @TODO: Move Image check to action group after MQE-697 is fixed --> + <grabAttributeFrom selector="{{StorefrontProductInfoMainSection.productImage}}" userInput="src" stepKey="searchGrabConfigProductPageImageSrc" after="searchAssertConfigProductPage"/> + <assertNotRegExp stepKey="searchAssertConfigProductPageImageNotDefault" after="searchGrabConfigProductPageImageSrc"> + <actualResult type="const">$searchGrabConfigProductPageImageSrc</actualResult> + <expectedResult type="const">'/placeholder\/image\.jpg/'</expectedResult> + </assertNotRegExp> + </test> +</tests> diff --git a/dev/tests/acceptance/tests/functional/Magento/ConfigurableProductCatalogSearch/Test/EndToEndB2CGuestUserTest/EndToEndB2CGuestUserTest.xml b/dev/tests/acceptance/tests/functional/Magento/ConfigurableProductCatalogSearch/Test/EndToEndB2CGuestUserTest/EndToEndB2CGuestUserTest.xml new file mode 100644 index 0000000000000..531c3601da587 --- /dev/null +++ b/dev/tests/acceptance/tests/functional/Magento/ConfigurableProductCatalogSearch/Test/EndToEndB2CGuestUserTest/EndToEndB2CGuestUserTest.xml @@ -0,0 +1,35 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + /** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +--> +<tests xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/testSchema.xsd"> + <test name="EndToEndB2CGuestUserTest"> + <!-- Search configurable product --> + <comment userInput="Search configurable product" stepKey="commentSearchConfigurableProduct" after="searchAssertSimpleProduct2ImageNotDefault"/> + <actionGroup ref="StorefrontCheckCategoryConfigurableProductActionGroup" stepKey="searchAssertFilterCategoryConfigProduct" after="commentSearchConfigurableProduct"> + <argument name="product" value="$$createConfigProduct$$"/> + <argument name="optionProduct" value="$$createConfigChildProduct1$$"/> + </actionGroup> + <!-- @TODO: Move Image check to action group after MQE-697 is fixed --> + <grabAttributeFrom selector="{{StorefrontCategoryProductSection.ProductImageByName($$createConfigProduct.name$$)}}" userInput="src" stepKey="searchGrabConfigProductImageSrc" after="searchAssertFilterCategoryConfigProduct"/> + <assertNotRegExp stepKey="searchAssertConfigProductImageNotDefault" after="searchGrabConfigProductImageSrc"> + <actualResult type="const">$searchGrabConfigProductImageSrc</actualResult> + <expectedResult type="const">'/placeholder\/small_image\.jpg/'</expectedResult> + </assertNotRegExp> + <click selector="{{StorefrontCategoryProductSection.ProductTitleByName($$createConfigProduct.name$$)}}" stepKey="searchClickConfigProductView" after="searchAssertConfigProductImageNotDefault"/> + <actionGroup ref="StorefrontCheckConfigurableProductActionGroup" stepKey="searchAssertConfigProductPage" after="searchClickConfigProductView"> + <argument name="product" value="$$createConfigProduct$$"/> + <argument name="optionProduct" value="$$createConfigChildProduct1$$"/> + </actionGroup> + <!-- @TODO: Move Image check to action group after MQE-697 is fixed --> + <grabAttributeFrom selector="{{StorefrontProductInfoMainSection.productImage}}" userInput="src" stepKey="searchGrabConfigProductPageImageSrc" after="searchAssertConfigProductPage"/> + <assertNotRegExp stepKey="searchAssertConfigProductPageImageNotDefault" after="searchGrabConfigProductPageImageSrc"> + <actualResult type="const">$searchGrabConfigProductPageImageSrc</actualResult> + <expectedResult type="const">'/placeholder\/image\.jpg/'</expectedResult> + </assertNotRegExp> + </test> +</tests> diff --git a/dev/tests/acceptance/tests/functional/Magento/ConfigurableProductCatalogSearch/Test/EndToEndB2CLoggedInUserTest.xml b/dev/tests/acceptance/tests/functional/Magento/ConfigurableProductCatalogSearch/Test/EndToEndB2CLoggedInUserTest.xml new file mode 100644 index 0000000000000..e25d3e0d728f6 --- /dev/null +++ b/dev/tests/acceptance/tests/functional/Magento/ConfigurableProductCatalogSearch/Test/EndToEndB2CLoggedInUserTest.xml @@ -0,0 +1,36 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + /** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +--> + +<tests xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/testSchema.xsd"> + <test name="EndToEndB2CLoggedInUserTest"> + <!-- Search configurable product --> + <comment userInput="Search configurable product" stepKey="commentSearchConfigurableProduct" after="searchAssertSimpleProduct2ImageNotDefault" /> + <actionGroup ref="StorefrontCheckCategoryConfigurableProductActionGroup" stepKey="searchAssertFilterCategoryConfigProduct" after="commentSearchConfigurableProduct"> + <argument name="product" value="$$createConfigProduct$$"/> + <argument name="optionProduct" value="$$createConfigChildProduct1$$"/> + </actionGroup> + <!-- @TODO: Move Image check to action group after MQE-697 is fixed --> + <grabAttributeFrom selector="{{StorefrontCategoryProductSection.ProductImageByName($$createConfigProduct.name$$)}}" userInput="src" stepKey="searchGrabConfigProductImageSrc" after="searchAssertFilterCategoryConfigProduct"/> + <assertNotRegExp stepKey="searchAssertConfigProductImageNotDefault" after="searchGrabConfigProductImageSrc"> + <actualResult type="const">$searchGrabConfigProductImageSrc</actualResult> + <expectedResult type="const">'/placeholder\/small_image\.jpg/'</expectedResult> + </assertNotRegExp> + <click selector="{{StorefrontCategoryProductSection.ProductTitleByName($$createConfigProduct.name$$)}}" stepKey="searchClickConfigProductView" after="searchAssertConfigProductImageNotDefault"/> + <actionGroup ref="StorefrontCheckConfigurableProductActionGroup" stepKey="searchAssertConfigProductPage" after="searchClickConfigProductView"> + <argument name="product" value="$$createConfigProduct$$"/> + <argument name="optionProduct" value="$$createConfigChildProduct1$$"/> + </actionGroup> + <!-- @TODO: Move Image check to action group after MQE-697 is fixed --> + <grabAttributeFrom selector="{{StorefrontProductInfoMainSection.productImage}}" userInput="src" stepKey="searchGrabConfigProductPageImageSrc" after="searchAssertConfigProductPage"/> + <assertNotRegExp stepKey="searchAssertConfigProductPageImageNotDefault" after="searchGrabConfigProductPageImageSrc"> + <actualResult type="const">$searchGrabConfigProductPageImageSrc</actualResult> + <expectedResult type="const">'/placeholder\/image\.jpg/'</expectedResult> + </assertNotRegExp> + </test> +</tests> diff --git a/dev/tests/acceptance/tests/functional/Magento/ConfigurableProductCatalogSearch/composer.json b/dev/tests/acceptance/tests/functional/Magento/ConfigurableProductCatalogSearch/composer.json new file mode 100644 index 0000000000000..e2e16acaa00d4 --- /dev/null +++ b/dev/tests/acceptance/tests/functional/Magento/ConfigurableProductCatalogSearch/composer.json @@ -0,0 +1,19 @@ +{ + "name": "magento/module-configurable-product-catalog-search-functional-test", + "description": "MFTF test module for Magento_ConfigurableProduct and Magento_CatalogSearch", + "type": "magento2-functional-test-module", + "config": { + "sort-packages": true + }, + "require": { + "magento/magento2-functional-testing-framework": ">=2.5.2" + }, + "suggest": { + "magento/module-configurable-product": "type: magento2-module, name: Magento_ConfigurableProduct, version: *", + "magento/module-catalog-search": "type: magento2-module, name: Magento_CatalogSearch, version: *" + }, + "license": [ + "OSL-3.0", + "AFL-3.0" + ] +} diff --git a/dev/tests/acceptance/tests/functional/Magento/ConfigurableProductWishlist/ActionGroup/StorefrontCustomerCheckConfigurableProductInWishlistActionGroup.xml b/dev/tests/acceptance/tests/functional/Magento/ConfigurableProductWishlist/ActionGroup/StorefrontCustomerCheckConfigurableProductInWishlistActionGroup.xml new file mode 100644 index 0000000000000..8ea33d82f278a --- /dev/null +++ b/dev/tests/acceptance/tests/functional/Magento/ConfigurableProductWishlist/ActionGroup/StorefrontCustomerCheckConfigurableProductInWishlistActionGroup.xml @@ -0,0 +1,27 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + /** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +--> + +<actionGroups xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/actionGroupSchema.xsd"> + <!-- Check configurable product in wishlist --> + <actionGroup name="StorefrontCustomerCheckConfigurableProductInWishlistActionGroup"> + <annotations> + <description>Validates that the provided Configurable Product and Product Option is present in the Storefront Wish List.</description> + </annotations> + <arguments> + <argument name="productVar"/> + <argument name="optionProductVar"/> + </arguments> + + <waitForElement selector="{{StorefrontCustomerWishlistProductSection.ProductTitleByName(productVar.name)}}" time="30" stepKey="assertWishlistProductName"/> + <see userInput="${{optionProductVar.price}}.00" selector="{{StorefrontCustomerWishlistProductSection.ProductPriceByName(productVar.name)}}" stepKey="AssertWishlistProductPrice"/> + <moveMouseOver selector="{{StorefrontCustomerWishlistProductSection.ProductInfoByName(productVar.name)}}" stepKey="wishlistMoveMouseOverProduct"/> + <seeElement selector="{{StorefrontCustomerWishlistProductSection.ProductAddToCartByName(productVar.name)}}" stepKey="AssertWishlistAddToCart"/> + <seeElement selector="{{StorefrontCustomerWishlistProductSection.ProductImageByName(productVar.name)}}" stepKey="AssertWishlistProductImage"/> + </actionGroup> +</actionGroups> diff --git a/dev/tests/acceptance/tests/functional/Magento/ConfigurableProductWishlist/ActionGroup/StorefrontCustomerCheckConfigurableProductInWishlistSidebarActionGroup.xml b/dev/tests/acceptance/tests/functional/Magento/ConfigurableProductWishlist/ActionGroup/StorefrontCustomerCheckConfigurableProductInWishlistSidebarActionGroup.xml new file mode 100644 index 0000000000000..604e97bf0a515 --- /dev/null +++ b/dev/tests/acceptance/tests/functional/Magento/ConfigurableProductWishlist/ActionGroup/StorefrontCustomerCheckConfigurableProductInWishlistSidebarActionGroup.xml @@ -0,0 +1,26 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + /** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +--> + +<actionGroups xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/actionGroupSchema.xsd"> + <!-- Check configurable product in wishlist sidebar --> + <actionGroup name="StorefrontCustomerCheckConfigurableProductInWishlistSidebarActionGroup"> + <annotations> + <description>Validates that the provided Configurable Product and Product Option is present in the Storefront Wish List sidebar.</description> + </annotations> + <arguments> + <argument name="productVar"/> + <argument name="optionProductVar"/> + </arguments> + + <waitForElement selector="{{StorefrontCustomerWishlistSidebarSection.ProductTitleByName(productVar.name)}}" time="30" stepKey="assertWishlistSidebarProductName"/> + <see userInput="${{optionProductVar.price}}.00" selector="{{StorefrontCustomerWishlistSidebarSection.ProductPriceByName(productVar.name)}}" stepKey="AssertWishlistSidebarProductPrice"/> + <seeElement selector="{{StorefrontCustomerWishlistSidebarSection.ProductAddToCartByName(productVar.name)}}" stepKey="AssertWishlistSidebarAddToCart"/> + <seeElement selector="{{StorefrontCustomerWishlistSidebarSection.ProductImageByName(productVar.name)}}" stepKey="AssertWishlistSidebarProductImage"/> + </actionGroup> +</actionGroups> diff --git a/app/code/Magento/AuthorizenetAcceptjs/LICENSE.txt b/dev/tests/acceptance/tests/functional/Magento/ConfigurableProductWishlist/LICENSE.txt similarity index 100% rename from app/code/Magento/AuthorizenetAcceptjs/LICENSE.txt rename to dev/tests/acceptance/tests/functional/Magento/ConfigurableProductWishlist/LICENSE.txt diff --git a/app/code/Magento/AuthorizenetAcceptjs/LICENSE_AFL.txt b/dev/tests/acceptance/tests/functional/Magento/ConfigurableProductWishlist/LICENSE_AFL.txt similarity index 100% rename from app/code/Magento/AuthorizenetAcceptjs/LICENSE_AFL.txt rename to dev/tests/acceptance/tests/functional/Magento/ConfigurableProductWishlist/LICENSE_AFL.txt diff --git a/dev/tests/acceptance/tests/functional/Magento/FunctionalTest/ConfigurableProductWishlist/README.md b/dev/tests/acceptance/tests/functional/Magento/ConfigurableProductWishlist/README.md similarity index 100% rename from dev/tests/acceptance/tests/functional/Magento/FunctionalTest/ConfigurableProductWishlist/README.md rename to dev/tests/acceptance/tests/functional/Magento/ConfigurableProductWishlist/README.md diff --git a/dev/tests/acceptance/tests/functional/Magento/ConfigurableProductWishlist/Test/EndToEndB2CLoggedInUserTest.xml b/dev/tests/acceptance/tests/functional/Magento/ConfigurableProductWishlist/Test/EndToEndB2CLoggedInUserTest.xml new file mode 100644 index 0000000000000..ec6edcea3b357 --- /dev/null +++ b/dev/tests/acceptance/tests/functional/Magento/ConfigurableProductWishlist/Test/EndToEndB2CLoggedInUserTest.xml @@ -0,0 +1,29 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + /** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +--> + +<tests xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/testSchema.xsd"> + <test name="EndToEndB2CLoggedInUserTest"> + <!-- Step 5: Add products to wishlist --> + <!-- Add Configurable Product to wishlist --> + <comment userInput="Add Configurable Product to wishlist" stepKey="commentAddConfigurableProductToWishlist" before="endOfAddingProductsToWishlist" /> + <click selector="{{StorefrontHeaderSection.NavigationCategoryByName($$createCategory.name$$)}}" after="commentAddConfigurableProductToWishlist" stepKey="wishlistGotoCategory3"/> + <click selector="{{StorefrontCategoryProductSection.ProductTitleByName($$createConfigProduct.name$$)}}" after="wishlistGotoCategory3" stepKey="wishlistClickConfigurableProduct"/> + <actionGroup ref="StorefrontCustomerAddProductToWishlistActionGroup" after="wishlistClickConfigurableProduct" stepKey="wishlistAddConfigurableProductToWishlist"> + <argument name="productVar" value="$$createConfigProduct$$"/> + </actionGroup> + <actionGroup ref="StorefrontCustomerCheckConfigurableProductInWishlistActionGroup" after="wishlistAddConfigurableProductToWishlist" stepKey="wishlistCheckConfigurableProductInWishlist"> + <argument name="productVar" value="$$createConfigProduct$$"/> + <argument name="optionProductVar" value="$$createConfigChildProduct1$$"/> + </actionGroup> + <actionGroup ref="StorefrontCustomerCheckConfigurableProductInWishlistSidebarActionGroup" after="wishlistCheckConfigurableProductInWishlist" stepKey="wishlistCheckConfigurableProductInWishlistSidebar"> + <argument name="productVar" value="$$createConfigProduct$$"/> + <argument name="optionProductVar" value="$$createConfigChildProduct1$$"/> + </actionGroup> + </test> +</tests> diff --git a/dev/tests/acceptance/tests/functional/Magento/ConfigurableProductWishlist/composer.json b/dev/tests/acceptance/tests/functional/Magento/ConfigurableProductWishlist/composer.json new file mode 100644 index 0000000000000..ad5922bccb338 --- /dev/null +++ b/dev/tests/acceptance/tests/functional/Magento/ConfigurableProductWishlist/composer.json @@ -0,0 +1,19 @@ +{ + "name": "magento/module-configurable-product-wishlist-functional-test", + "description": "MFTF test module for Magento_ConfigurableProduct and Magento_Wishlist", + "type": "magento2-functional-test-module", + "config": { + "sort-packages": true + }, + "require": { + "magento/magento2-functional-testing-framework": ">=2.5.2" + }, + "suggest": { + "magento/module-configurable-product": "type: magento2-module, name: Magento_ConfigurableProduct, version: *", + "magento/module-wishlist": "type: magento2-module, name: Magento_Wishlist, version: *" + }, + "license": [ + "OSL-3.0", + "AFL-3.0" + ] +} diff --git a/dev/tests/acceptance/tests/functional/Magento/FunctionalTest/ConfigurableProductCatalogSearch/LICENSE.txt b/dev/tests/acceptance/tests/functional/Magento/FunctionalTest/ConfigurableProductCatalogSearch/LICENSE.txt deleted file mode 100644 index 49525fd99da9c..0000000000000 --- a/dev/tests/acceptance/tests/functional/Magento/FunctionalTest/ConfigurableProductCatalogSearch/LICENSE.txt +++ /dev/null @@ -1,48 +0,0 @@ - -Open Software License ("OSL") v. 3.0 - -This Open Software License (the "License") applies to any original work of authorship (the "Original Work") whose owner (the "Licensor") has placed the following licensing notice adjacent to the copyright notice for the Original Work: - -Licensed under the Open Software License version 3.0 - - 1. Grant of Copyright License. Licensor grants You a worldwide, royalty-free, non-exclusive, sublicensable license, for the duration of the copyright, to do the following: - - 1. to reproduce the Original Work in copies, either alone or as part of a collective work; - - 2. to translate, adapt, alter, transform, modify, or arrange the Original Work, thereby creating derivative works ("Derivative Works") based upon the Original Work; - - 3. to distribute or communicate copies of the Original Work and Derivative Works to the public, with the proviso that copies of Original Work or Derivative Works that You distribute or communicate shall be licensed under this Open Software License; - - 4. to perform the Original Work publicly; and - - 5. to display the Original Work publicly. - - 2. Grant of Patent License. Licensor grants You a worldwide, royalty-free, non-exclusive, sublicensable license, under patent claims owned or controlled by the Licensor that are embodied in the Original Work as furnished by the Licensor, for the duration of the patents, to make, use, sell, offer for sale, have made, and import the Original Work and Derivative Works. - - 3. Grant of Source Code License. The term "Source Code" means the preferred form of the Original Work for making modifications to it and all available documentation describing how to modify the Original Work. Licensor agrees to provide a machine-readable copy of the Source Code of the Original Work along with each copy of the Original Work that Licensor distributes. Licensor reserves the right to satisfy this obligation by placing a machine-readable copy of the Source Code in an information repository reasonably calculated to permit inexpensive and convenient access by You for as long as Licensor continues to distribute the Original Work. - - 4. Exclusions From License Grant. Neither the names of Licensor, nor the names of any contributors to the Original Work, nor any of their trademarks or service marks, may be used to endorse or promote products derived from this Original Work without express prior permission of the Licensor. Except as expressly stated herein, nothing in this License grants any license to Licensor's trademarks, copyrights, patents, trade secrets or any other intellectual property. No patent license is granted to make, use, sell, offer for sale, have made, or import embodiments of any patent claims other than the licensed claims defined in Section 2. No license is granted to the trademarks of Licensor even if such marks are included in the Original Work. Nothing in this License shall be interpreted to prohibit Licensor from licensing under terms different from this License any Original Work that Licensor otherwise would have a right to license. - - 5. External Deployment. The term "External Deployment" means the use, distribution, or communication of the Original Work or Derivative Works in any way such that the Original Work or Derivative Works may be used by anyone other than You, whether those works are distributed or communicated to those persons or made available as an application intended for use over a network. As an express condition for the grants of license hereunder, You must treat any External Deployment by You of the Original Work or a Derivative Work as a distribution under section 1(c). - - 6. Attribution Rights. You must retain, in the Source Code of any Derivative Works that You create, all copyright, patent, or trademark notices from the Source Code of the Original Work, as well as any notices of licensing and any descriptive text identified therein as an "Attribution Notice." You must cause the Source Code for any Derivative Works that You create to carry a prominent Attribution Notice reasonably calculated to inform recipients that You have modified the Original Work. - - 7. Warranty of Provenance and Disclaimer of Warranty. Licensor warrants that the copyright in and to the Original Work and the patent rights granted herein by Licensor are owned by the Licensor or are sublicensed to You under the terms of this License with the permission of the contributor(s) of those copyrights and patent rights. Except as expressly stated in the immediately preceding sentence, the Original Work is provided under this License on an "AS IS" BASIS and WITHOUT WARRANTY, either express or implied, including, without limitation, the warranties of non-infringement, merchantability or fitness for a particular purpose. THE ENTIRE RISK AS TO THE QUALITY OF THE ORIGINAL WORK IS WITH YOU. This DISCLAIMER OF WARRANTY constitutes an essential part of this License. No license to the Original Work is granted by this License except under this disclaimer. - - 8. Limitation of Liability. Under no circumstances and under no legal theory, whether in tort (including negligence), contract, or otherwise, shall the Licensor be liable to anyone for any indirect, special, incidental, or consequential damages of any character arising as a result of this License or the use of the Original Work including, without limitation, damages for loss of goodwill, work stoppage, computer failure or malfunction, or any and all other commercial damages or losses. This limitation of liability shall not apply to the extent applicable law prohibits such limitation. - - 9. Acceptance and Termination. If, at any time, You expressly assented to this License, that assent indicates your clear and irrevocable acceptance of this License and all of its terms and conditions. If You distribute or communicate copies of the Original Work or a Derivative Work, You must make a reasonable effort under the circumstances to obtain the express assent of recipients to the terms of this License. This License conditions your rights to undertake the activities listed in Section 1, including your right to create Derivative Works based upon the Original Work, and doing so without honoring these terms and conditions is prohibited by copyright law and international treaty. Nothing in this License is intended to affect copyright exceptions and limitations (including 'fair use' or 'fair dealing'). This License shall terminate immediately and You may no longer exercise any of the rights granted to You by this License upon your failure to honor the conditions in Section 1(c). - - 10. Termination for Patent Action. This License shall terminate automatically and You may no longer exercise any of the rights granted to You by this License as of the date You commence an action, including a cross-claim or counterclaim, against Licensor or any licensee alleging that the Original Work infringes a patent. This termination provision shall not apply for an action alleging patent infringement by combinations of the Original Work with other software or hardware. - - 11. Jurisdiction, Venue and Governing Law. Any action or suit relating to this License may be brought only in the courts of a jurisdiction wherein the Licensor resides or in which Licensor conducts its primary business, and under the laws of that jurisdiction excluding its conflict-of-law provisions. The application of the United Nations Convention on Contracts for the International Sale of Goods is expressly excluded. Any use of the Original Work outside the scope of this License or after its termination shall be subject to the requirements and penalties of copyright or patent law in the appropriate jurisdiction. This section shall survive the termination of this License. - - 12. Attorneys' Fees. In any action to enforce the terms of this License or seeking damages relating thereto, the prevailing party shall be entitled to recover its costs and expenses, including, without limitation, reasonable attorneys' fees and costs incurred in connection with such action, including any appeal of such action. This section shall survive the termination of this License. - - 13. Miscellaneous. If any provision of this License is held to be unenforceable, such provision shall be reformed only to the extent necessary to make it enforceable. - - 14. Definition of "You" in This License. "You" throughout this License, whether in upper or lower case, means an individual or a legal entity exercising rights under, and complying with all of the terms of, this License. For legal entities, "You" includes any entity that controls, is controlled by, or is under common control with you. For purposes of this definition, "control" means (i) the power, direct or indirect, to cause the direction or management of such entity, whether by contract or otherwise, or (ii) ownership of fifty percent (50%) or more of the outstanding shares, or (iii) beneficial ownership of such entity. - - 15. Right to Use. You may use the Original Work in all ways not otherwise restricted or conditioned by this License or by law, and Licensor promises not to interfere with or be responsible for such uses by You. - - 16. Modification of This License. This License is Copyright (C) 2005 Lawrence Rosen. Permission is granted to copy, distribute, or communicate this License without modification. Nothing in this License permits You to modify this License as applied to the Original Work or to Derivative Works. However, You may modify the text of this License and copy, distribute or communicate your modified version (the "Modified License") and apply it to other original works of authorship subject to the following conditions: (i) You may not indicate in any way that your Modified License is the "Open Software License" or "OSL" and you may not use those names in the name of your Modified License; (ii) You must replace the notice specified in the first paragraph above with the notice "Licensed under <insert your license name here>" or with a notice of your own that is not confusingly similar to the notice in this License; and (iii) You may not claim that your original works are open source software unless your Modified License has been approved by Open Source Initiative (OSI) and You comply with its license review and certification process. \ No newline at end of file diff --git a/dev/tests/acceptance/tests/functional/Magento/FunctionalTest/ConfigurableProductCatalogSearch/LICENSE_AFL.txt b/dev/tests/acceptance/tests/functional/Magento/FunctionalTest/ConfigurableProductCatalogSearch/LICENSE_AFL.txt deleted file mode 100644 index f39d641b18a19..0000000000000 --- a/dev/tests/acceptance/tests/functional/Magento/FunctionalTest/ConfigurableProductCatalogSearch/LICENSE_AFL.txt +++ /dev/null @@ -1,48 +0,0 @@ - -Academic Free License ("AFL") v. 3.0 - -This Academic Free License (the "License") applies to any original work of authorship (the "Original Work") whose owner (the "Licensor") has placed the following licensing notice adjacent to the copyright notice for the Original Work: - -Licensed under the Academic Free License version 3.0 - - 1. Grant of Copyright License. Licensor grants You a worldwide, royalty-free, non-exclusive, sublicensable license, for the duration of the copyright, to do the following: - - 1. to reproduce the Original Work in copies, either alone or as part of a collective work; - - 2. to translate, adapt, alter, transform, modify, or arrange the Original Work, thereby creating derivative works ("Derivative Works") based upon the Original Work; - - 3. to distribute or communicate copies of the Original Work and Derivative Works to the public, under any license of your choice that does not contradict the terms and conditions, including Licensor's reserved rights and remedies, in this Academic Free License; - - 4. to perform the Original Work publicly; and - - 5. to display the Original Work publicly. - - 2. Grant of Patent License. Licensor grants You a worldwide, royalty-free, non-exclusive, sublicensable license, under patent claims owned or controlled by the Licensor that are embodied in the Original Work as furnished by the Licensor, for the duration of the patents, to make, use, sell, offer for sale, have made, and import the Original Work and Derivative Works. - - 3. Grant of Source Code License. The term "Source Code" means the preferred form of the Original Work for making modifications to it and all available documentation describing how to modify the Original Work. Licensor agrees to provide a machine-readable copy of the Source Code of the Original Work along with each copy of the Original Work that Licensor distributes. Licensor reserves the right to satisfy this obligation by placing a machine-readable copy of the Source Code in an information repository reasonably calculated to permit inexpensive and convenient access by You for as long as Licensor continues to distribute the Original Work. - - 4. Exclusions From License Grant. Neither the names of Licensor, nor the names of any contributors to the Original Work, nor any of their trademarks or service marks, may be used to endorse or promote products derived from this Original Work without express prior permission of the Licensor. Except as expressly stated herein, nothing in this License grants any license to Licensor's trademarks, copyrights, patents, trade secrets or any other intellectual property. No patent license is granted to make, use, sell, offer for sale, have made, or import embodiments of any patent claims other than the licensed claims defined in Section 2. No license is granted to the trademarks of Licensor even if such marks are included in the Original Work. Nothing in this License shall be interpreted to prohibit Licensor from licensing under terms different from this License any Original Work that Licensor otherwise would have a right to license. - - 5. External Deployment. The term "External Deployment" means the use, distribution, or communication of the Original Work or Derivative Works in any way such that the Original Work or Derivative Works may be used by anyone other than You, whether those works are distributed or communicated to those persons or made available as an application intended for use over a network. As an express condition for the grants of license hereunder, You must treat any External Deployment by You of the Original Work or a Derivative Work as a distribution under section 1(c). - - 6. Attribution Rights. You must retain, in the Source Code of any Derivative Works that You create, all copyright, patent, or trademark notices from the Source Code of the Original Work, as well as any notices of licensing and any descriptive text identified therein as an "Attribution Notice." You must cause the Source Code for any Derivative Works that You create to carry a prominent Attribution Notice reasonably calculated to inform recipients that You have modified the Original Work. - - 7. Warranty of Provenance and Disclaimer of Warranty. Licensor warrants that the copyright in and to the Original Work and the patent rights granted herein by Licensor are owned by the Licensor or are sublicensed to You under the terms of this License with the permission of the contributor(s) of those copyrights and patent rights. Except as expressly stated in the immediately preceding sentence, the Original Work is provided under this License on an "AS IS" BASIS and WITHOUT WARRANTY, either express or implied, including, without limitation, the warranties of non-infringement, merchantability or fitness for a particular purpose. THE ENTIRE RISK AS TO THE QUALITY OF THE ORIGINAL WORK IS WITH YOU. This DISCLAIMER OF WARRANTY constitutes an essential part of this License. No license to the Original Work is granted by this License except under this disclaimer. - - 8. Limitation of Liability. Under no circumstances and under no legal theory, whether in tort (including negligence), contract, or otherwise, shall the Licensor be liable to anyone for any indirect, special, incidental, or consequential damages of any character arising as a result of this License or the use of the Original Work including, without limitation, damages for loss of goodwill, work stoppage, computer failure or malfunction, or any and all other commercial damages or losses. This limitation of liability shall not apply to the extent applicable law prohibits such limitation. - - 9. Acceptance and Termination. If, at any time, You expressly assented to this License, that assent indicates your clear and irrevocable acceptance of this License and all of its terms and conditions. If You distribute or communicate copies of the Original Work or a Derivative Work, You must make a reasonable effort under the circumstances to obtain the express assent of recipients to the terms of this License. This License conditions your rights to undertake the activities listed in Section 1, including your right to create Derivative Works based upon the Original Work, and doing so without honoring these terms and conditions is prohibited by copyright law and international treaty. Nothing in this License is intended to affect copyright exceptions and limitations (including "fair use" or "fair dealing"). This License shall terminate immediately and You may no longer exercise any of the rights granted to You by this License upon your failure to honor the conditions in Section 1(c). - - 10. Termination for Patent Action. This License shall terminate automatically and You may no longer exercise any of the rights granted to You by this License as of the date You commence an action, including a cross-claim or counterclaim, against Licensor or any licensee alleging that the Original Work infringes a patent. This termination provision shall not apply for an action alleging patent infringement by combinations of the Original Work with other software or hardware. - - 11. Jurisdiction, Venue and Governing Law. Any action or suit relating to this License may be brought only in the courts of a jurisdiction wherein the Licensor resides or in which Licensor conducts its primary business, and under the laws of that jurisdiction excluding its conflict-of-law provisions. The application of the United Nations Convention on Contracts for the International Sale of Goods is expressly excluded. Any use of the Original Work outside the scope of this License or after its termination shall be subject to the requirements and penalties of copyright or patent law in the appropriate jurisdiction. This section shall survive the termination of this License. - - 12. Attorneys' Fees. In any action to enforce the terms of this License or seeking damages relating thereto, the prevailing party shall be entitled to recover its costs and expenses, including, without limitation, reasonable attorneys' fees and costs incurred in connection with such action, including any appeal of such action. This section shall survive the termination of this License. - - 13. Miscellaneous. If any provision of this License is held to be unenforceable, such provision shall be reformed only to the extent necessary to make it enforceable. - - 14. Definition of "You" in This License. "You" throughout this License, whether in upper or lower case, means an individual or a legal entity exercising rights under, and complying with all of the terms of, this License. For legal entities, "You" includes any entity that controls, is controlled by, or is under common control with you. For purposes of this definition, "control" means (i) the power, direct or indirect, to cause the direction or management of such entity, whether by contract or otherwise, or (ii) ownership of fifty percent (50%) or more of the outstanding shares, or (iii) beneficial ownership of such entity. - - 15. Right to Use. You may use the Original Work in all ways not otherwise restricted or conditioned by this License or by law, and Licensor promises not to interfere with or be responsible for such uses by You. - - 16. Modification of This License. This License is Copyright © 2005 Lawrence Rosen. Permission is granted to copy, distribute, or communicate this License without modification. Nothing in this License permits You to modify this License as applied to the Original Work or to Derivative Works. However, You may modify the text of this License and copy, distribute or communicate your modified version (the "Modified License") and apply it to other original works of authorship subject to the following conditions: (i) You may not indicate in any way that your Modified License is the "Academic Free License" or "AFL" and you may not use those names in the name of your Modified License; (ii) You must replace the notice specified in the first paragraph above with the notice "Licensed under <insert your license name here>" or with a notice of your own that is not confusingly similar to the notice in this License; and (iii) You may not claim that your original works are open source software unless your Modified License has been approved by Open Source Initiative (OSI) and You comply with its license review and certification process. diff --git a/dev/tests/acceptance/tests/functional/Magento/FunctionalTest/ConfigurableProductCatalogSearch/Test/EndToEndB2CGuestUserTest.xml b/dev/tests/acceptance/tests/functional/Magento/FunctionalTest/ConfigurableProductCatalogSearch/Test/EndToEndB2CGuestUserTest.xml deleted file mode 100644 index d38bf90a3a16d..0000000000000 --- a/dev/tests/acceptance/tests/functional/Magento/FunctionalTest/ConfigurableProductCatalogSearch/Test/EndToEndB2CGuestUserTest.xml +++ /dev/null @@ -1,49 +0,0 @@ -<?xml version="1.0" encoding="UTF-8"?> -<!-- - /** - * Copyright © Magento, Inc. All rights reserved. - * See COPYING.txt for license details. - */ ---> - -<tests xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" - xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/testSchema.xsd"> - <test name="EndToEndB2CGuestUserTest"> - <!-- Search configurable product --> - <comment userInput="Search configurable product" stepKey="commentSearchConfigurableProduct" after="searchAssertSimpleProduct2ImageNotDefault" /> - <actionGroup ref="StorefrontCheckCategoryConfigurableProductActionGroup" stepKey="searchAssertFilterCategoryConfigProduct" after="commentSearchConfigurableProduct"> - <argument name="product" value="$$createConfigProduct$$"/> - <argument name="optionProduct" value="$$createConfigChildProduct1$$"/> - </actionGroup> - <!-- @TODO: Move Image check to action group after MQE-697 is fixed --> - <grabAttributeFrom selector="{{StorefrontCategoryProductSection.ProductImageByName($$createConfigProduct.name$$)}}" userInput="src" stepKey="searchGrabConfigProductImageSrc" after="searchAssertFilterCategoryConfigProduct"/> - <assertNotRegExp expected="'/placeholder\/small_image\.jpg/'" actual="$searchGrabConfigProductImageSrc" stepKey="searchAssertConfigProductImageNotDefault" after="searchGrabConfigProductImageSrc"/> - <click selector="{{StorefrontCategoryProductSection.ProductTitleByName($$createConfigProduct.name$$)}}" stepKey="searchClickConfigProductView" after="searchAssertConfigProductImageNotDefault"/> - <actionGroup ref="StorefrontCheckConfigurableProductActionGroup" stepKey="searchAssertConfigProductPage" after="searchClickConfigProductView"> - <argument name="product" value="$$createConfigProduct$$"/> - <argument name="optionProduct" value="$$createConfigChildProduct1$$"/> - </actionGroup> - <!-- @TODO: Move Image check to action group after MQE-697 is fixed --> - <grabAttributeFrom selector="{{StorefrontProductInfoMainSection.productImage}}" userInput="src" stepKey="searchGrabConfigProductPageImageSrc" after="searchAssertConfigProductPage"/> - <assertNotRegExp expected="'/placeholder\/image\.jpg/'" actual="$searchGrabConfigProductPageImageSrc" stepKey="searchAssertConfigProductPageImageNotDefault" after="searchGrabConfigProductPageImageSrc"/> - </test> - <test name="EndToEndB2CGuestUserMysqlTest"> - <!-- Search configurable product --> - <comment userInput="Search configurable product" stepKey="commentSearchConfigurableProduct" after="searchAssertSimpleProduct2ImageNotDefault" /> - <actionGroup ref="StorefrontCheckCategoryConfigurableProductActionGroup" stepKey="searchAssertFilterCategoryConfigProduct" after="commentSearchConfigurableProduct"> - <argument name="product" value="$$createConfigProduct$$"/> - <argument name="optionProduct" value="$$createConfigChildProduct1$$"/> - </actionGroup> - <!-- @TODO: Move Image check to action group after MQE-697 is fixed --> - <grabAttributeFrom selector="{{StorefrontCategoryProductSection.ProductImageByName($$createConfigProduct.name$$)}}" userInput="src" stepKey="searchGrabConfigProductImageSrc" after="searchAssertFilterCategoryConfigProduct"/> - <assertNotRegExp expected="'/placeholder\/small_image\.jpg/'" actual="$searchGrabConfigProductImageSrc" stepKey="searchAssertConfigProductImageNotDefault" after="searchGrabConfigProductImageSrc"/> - <click selector="{{StorefrontCategoryProductSection.ProductTitleByName($$createConfigProduct.name$$)}}" stepKey="searchClickConfigProductView" after="searchAssertConfigProductImageNotDefault"/> - <actionGroup ref="StorefrontCheckConfigurableProductActionGroup" stepKey="searchAssertConfigProductPage" after="searchClickConfigProductView"> - <argument name="product" value="$$createConfigProduct$$"/> - <argument name="optionProduct" value="$$createConfigChildProduct1$$"/> - </actionGroup> - <!-- @TODO: Move Image check to action group after MQE-697 is fixed --> - <grabAttributeFrom selector="{{StorefrontProductInfoMainSection.productImage}}" userInput="src" stepKey="searchGrabConfigProductPageImageSrc" after="searchAssertConfigProductPage"/> - <assertNotRegExp expected="'/placeholder\/image\.jpg/'" actual="$searchGrabConfigProductPageImageSrc" stepKey="searchAssertConfigProductPageImageNotDefault" after="searchGrabConfigProductPageImageSrc"/> - </test> -</tests> diff --git a/dev/tests/acceptance/tests/functional/Magento/FunctionalTest/ConfigurableProductCatalogSearch/Test/EndToEndB2CLoggedInUserTest.xml b/dev/tests/acceptance/tests/functional/Magento/FunctionalTest/ConfigurableProductCatalogSearch/Test/EndToEndB2CLoggedInUserTest.xml deleted file mode 100644 index f616e46d6f692..0000000000000 --- a/dev/tests/acceptance/tests/functional/Magento/FunctionalTest/ConfigurableProductCatalogSearch/Test/EndToEndB2CLoggedInUserTest.xml +++ /dev/null @@ -1,30 +0,0 @@ -<?xml version="1.0" encoding="UTF-8"?> -<!-- - /** - * Copyright © Magento, Inc. All rights reserved. - * See COPYING.txt for license details. - */ ---> - -<tests xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" - xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/testSchema.xsd"> - <test name="EndToEndB2CLoggedInUserTest"> - <!-- Search configurable product --> - <comment userInput="Search configurable product" stepKey="commentSearchConfigurableProduct" after="searchAssertSimpleProduct2ImageNotDefault" /> - <actionGroup ref="StorefrontCheckCategoryConfigurableProductActionGroup" stepKey="searchAssertFilterCategoryConfigProduct" after="commentSearchConfigurableProduct"> - <argument name="product" value="$$createConfigProduct$$"/> - <argument name="optionProduct" value="$$createConfigChildProduct1$$"/> - </actionGroup> - <!-- @TODO: Move Image check to action group after MQE-697 is fixed --> - <grabAttributeFrom selector="{{StorefrontCategoryProductSection.ProductImageByName($$createConfigProduct.name$$)}}" userInput="src" stepKey="searchGrabConfigProductImageSrc" after="searchAssertFilterCategoryConfigProduct"/> - <assertNotRegExp expected="'/placeholder\/small_image\.jpg/'" actual="$searchGrabConfigProductImageSrc" stepKey="searchAssertConfigProductImageNotDefault" after="searchGrabConfigProductImageSrc"/> - <click selector="{{StorefrontCategoryProductSection.ProductTitleByName($$createConfigProduct.name$$)}}" stepKey="searchClickConfigProductView" after="searchAssertConfigProductImageNotDefault"/> - <actionGroup ref="StorefrontCheckConfigurableProductActionGroup" stepKey="searchAssertConfigProductPage" after="searchClickConfigProductView"> - <argument name="product" value="$$createConfigProduct$$"/> - <argument name="optionProduct" value="$$createConfigChildProduct1$$"/> - </actionGroup> - <!-- @TODO: Move Image check to action group after MQE-697 is fixed --> - <grabAttributeFrom selector="{{StorefrontProductInfoMainSection.productImage}}" userInput="src" stepKey="searchGrabConfigProductPageImageSrc" after="searchAssertConfigProductPage"/> - <assertNotRegExp expected="'/placeholder\/image\.jpg/'" actual="$searchGrabConfigProductPageImageSrc" stepKey="searchAssertConfigProductPageImageNotDefault" after="searchGrabConfigProductPageImageSrc"/> - </test> -</tests> diff --git a/dev/tests/acceptance/tests/functional/Magento/FunctionalTest/ConfigurableProductWishlist/ActionGroup/StorefrontCustomerWishlistActionGroup.xml b/dev/tests/acceptance/tests/functional/Magento/FunctionalTest/ConfigurableProductWishlist/ActionGroup/StorefrontCustomerWishlistActionGroup.xml deleted file mode 100644 index 598ff8deabcc0..0000000000000 --- a/dev/tests/acceptance/tests/functional/Magento/FunctionalTest/ConfigurableProductWishlist/ActionGroup/StorefrontCustomerWishlistActionGroup.xml +++ /dev/null @@ -1,43 +0,0 @@ -<?xml version="1.0" encoding="UTF-8"?> -<!-- - /** - * Copyright © Magento, Inc. All rights reserved. - * See COPYING.txt for license details. - */ ---> - -<actionGroups xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" - xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/actionGroupSchema.xsd"> - <!-- Check configurable product in wishlist --> - <actionGroup name="StorefrontCustomerCheckConfigurableProductInWishlist"> - <annotations> - <description>Validates that the provided Configurable Product and Product Option is present in the Storefront Wish List.</description> - </annotations> - <arguments> - <argument name="productVar"/> - <argument name="optionProductVar"/> - </arguments> - - <waitForElement selector="{{StorefrontCustomerWishlistProductSection.ProductTitleByName(productVar.name)}}" time="30" stepKey="assertWishlistProductName"/> - <see userInput="${{optionProductVar.price}}.00" selector="{{StorefrontCustomerWishlistProductSection.ProductPriceByName(productVar.name)}}" stepKey="AssertWishlistProductPrice"/> - <moveMouseOver selector="{{StorefrontCustomerWishlistProductSection.ProductInfoByName(productVar.name)}}" stepKey="wishlistMoveMouseOverProduct"/> - <seeElement selector="{{StorefrontCustomerWishlistProductSection.ProductAddToCartByName(productVar.name)}}" stepKey="AssertWishlistAddToCart"/> - <seeElement selector="{{StorefrontCustomerWishlistProductSection.ProductImageByName(productVar.name)}}" stepKey="AssertWishlistProductImage"/> - </actionGroup> - - <!-- Check configurable product in wishlist sidebar --> - <actionGroup name="StorefrontCustomerCheckConfigurableProductInWishlistSidebar"> - <annotations> - <description>Validates that the provided Configurable Product and Product Option is present in the Storefront Wish List sidebar.</description> - </annotations> - <arguments> - <argument name="productVar"/> - <argument name="optionProductVar"/> - </arguments> - - <waitForElement selector="{{StorefrontCustomerWishlistSidebarSection.ProductTitleByName(productVar.name)}}" time="30" stepKey="assertWishlistSidebarProductName"/> - <see userInput="${{optionProductVar.price}}.00" selector="{{StorefrontCustomerWishlistSidebarSection.ProductPriceByName(productVar.name)}}" stepKey="AssertWishlistSidebarProductPrice"/> - <seeElement selector="{{StorefrontCustomerWishlistSidebarSection.ProductAddToCartByName(productVar.name)}}" stepKey="AssertWishlistSidebarAddToCart"/> - <seeElement selector="{{StorefrontCustomerWishlistSidebarSection.ProductImageByName(productVar.name)}}" stepKey="AssertWishlistSidebarProductImage"/> - </actionGroup> -</actionGroups> diff --git a/dev/tests/acceptance/tests/functional/Magento/FunctionalTest/ConfigurableProductWishlist/LICENSE.txt b/dev/tests/acceptance/tests/functional/Magento/FunctionalTest/ConfigurableProductWishlist/LICENSE.txt deleted file mode 100644 index 49525fd99da9c..0000000000000 --- a/dev/tests/acceptance/tests/functional/Magento/FunctionalTest/ConfigurableProductWishlist/LICENSE.txt +++ /dev/null @@ -1,48 +0,0 @@ - -Open Software License ("OSL") v. 3.0 - -This Open Software License (the "License") applies to any original work of authorship (the "Original Work") whose owner (the "Licensor") has placed the following licensing notice adjacent to the copyright notice for the Original Work: - -Licensed under the Open Software License version 3.0 - - 1. Grant of Copyright License. Licensor grants You a worldwide, royalty-free, non-exclusive, sublicensable license, for the duration of the copyright, to do the following: - - 1. to reproduce the Original Work in copies, either alone or as part of a collective work; - - 2. to translate, adapt, alter, transform, modify, or arrange the Original Work, thereby creating derivative works ("Derivative Works") based upon the Original Work; - - 3. to distribute or communicate copies of the Original Work and Derivative Works to the public, with the proviso that copies of Original Work or Derivative Works that You distribute or communicate shall be licensed under this Open Software License; - - 4. to perform the Original Work publicly; and - - 5. to display the Original Work publicly. - - 2. Grant of Patent License. Licensor grants You a worldwide, royalty-free, non-exclusive, sublicensable license, under patent claims owned or controlled by the Licensor that are embodied in the Original Work as furnished by the Licensor, for the duration of the patents, to make, use, sell, offer for sale, have made, and import the Original Work and Derivative Works. - - 3. Grant of Source Code License. The term "Source Code" means the preferred form of the Original Work for making modifications to it and all available documentation describing how to modify the Original Work. Licensor agrees to provide a machine-readable copy of the Source Code of the Original Work along with each copy of the Original Work that Licensor distributes. Licensor reserves the right to satisfy this obligation by placing a machine-readable copy of the Source Code in an information repository reasonably calculated to permit inexpensive and convenient access by You for as long as Licensor continues to distribute the Original Work. - - 4. Exclusions From License Grant. Neither the names of Licensor, nor the names of any contributors to the Original Work, nor any of their trademarks or service marks, may be used to endorse or promote products derived from this Original Work without express prior permission of the Licensor. Except as expressly stated herein, nothing in this License grants any license to Licensor's trademarks, copyrights, patents, trade secrets or any other intellectual property. No patent license is granted to make, use, sell, offer for sale, have made, or import embodiments of any patent claims other than the licensed claims defined in Section 2. No license is granted to the trademarks of Licensor even if such marks are included in the Original Work. Nothing in this License shall be interpreted to prohibit Licensor from licensing under terms different from this License any Original Work that Licensor otherwise would have a right to license. - - 5. External Deployment. The term "External Deployment" means the use, distribution, or communication of the Original Work or Derivative Works in any way such that the Original Work or Derivative Works may be used by anyone other than You, whether those works are distributed or communicated to those persons or made available as an application intended for use over a network. As an express condition for the grants of license hereunder, You must treat any External Deployment by You of the Original Work or a Derivative Work as a distribution under section 1(c). - - 6. Attribution Rights. You must retain, in the Source Code of any Derivative Works that You create, all copyright, patent, or trademark notices from the Source Code of the Original Work, as well as any notices of licensing and any descriptive text identified therein as an "Attribution Notice." You must cause the Source Code for any Derivative Works that You create to carry a prominent Attribution Notice reasonably calculated to inform recipients that You have modified the Original Work. - - 7. Warranty of Provenance and Disclaimer of Warranty. Licensor warrants that the copyright in and to the Original Work and the patent rights granted herein by Licensor are owned by the Licensor or are sublicensed to You under the terms of this License with the permission of the contributor(s) of those copyrights and patent rights. Except as expressly stated in the immediately preceding sentence, the Original Work is provided under this License on an "AS IS" BASIS and WITHOUT WARRANTY, either express or implied, including, without limitation, the warranties of non-infringement, merchantability or fitness for a particular purpose. THE ENTIRE RISK AS TO THE QUALITY OF THE ORIGINAL WORK IS WITH YOU. This DISCLAIMER OF WARRANTY constitutes an essential part of this License. No license to the Original Work is granted by this License except under this disclaimer. - - 8. Limitation of Liability. Under no circumstances and under no legal theory, whether in tort (including negligence), contract, or otherwise, shall the Licensor be liable to anyone for any indirect, special, incidental, or consequential damages of any character arising as a result of this License or the use of the Original Work including, without limitation, damages for loss of goodwill, work stoppage, computer failure or malfunction, or any and all other commercial damages or losses. This limitation of liability shall not apply to the extent applicable law prohibits such limitation. - - 9. Acceptance and Termination. If, at any time, You expressly assented to this License, that assent indicates your clear and irrevocable acceptance of this License and all of its terms and conditions. If You distribute or communicate copies of the Original Work or a Derivative Work, You must make a reasonable effort under the circumstances to obtain the express assent of recipients to the terms of this License. This License conditions your rights to undertake the activities listed in Section 1, including your right to create Derivative Works based upon the Original Work, and doing so without honoring these terms and conditions is prohibited by copyright law and international treaty. Nothing in this License is intended to affect copyright exceptions and limitations (including 'fair use' or 'fair dealing'). This License shall terminate immediately and You may no longer exercise any of the rights granted to You by this License upon your failure to honor the conditions in Section 1(c). - - 10. Termination for Patent Action. This License shall terminate automatically and You may no longer exercise any of the rights granted to You by this License as of the date You commence an action, including a cross-claim or counterclaim, against Licensor or any licensee alleging that the Original Work infringes a patent. This termination provision shall not apply for an action alleging patent infringement by combinations of the Original Work with other software or hardware. - - 11. Jurisdiction, Venue and Governing Law. Any action or suit relating to this License may be brought only in the courts of a jurisdiction wherein the Licensor resides or in which Licensor conducts its primary business, and under the laws of that jurisdiction excluding its conflict-of-law provisions. The application of the United Nations Convention on Contracts for the International Sale of Goods is expressly excluded. Any use of the Original Work outside the scope of this License or after its termination shall be subject to the requirements and penalties of copyright or patent law in the appropriate jurisdiction. This section shall survive the termination of this License. - - 12. Attorneys' Fees. In any action to enforce the terms of this License or seeking damages relating thereto, the prevailing party shall be entitled to recover its costs and expenses, including, without limitation, reasonable attorneys' fees and costs incurred in connection with such action, including any appeal of such action. This section shall survive the termination of this License. - - 13. Miscellaneous. If any provision of this License is held to be unenforceable, such provision shall be reformed only to the extent necessary to make it enforceable. - - 14. Definition of "You" in This License. "You" throughout this License, whether in upper or lower case, means an individual or a legal entity exercising rights under, and complying with all of the terms of, this License. For legal entities, "You" includes any entity that controls, is controlled by, or is under common control with you. For purposes of this definition, "control" means (i) the power, direct or indirect, to cause the direction or management of such entity, whether by contract or otherwise, or (ii) ownership of fifty percent (50%) or more of the outstanding shares, or (iii) beneficial ownership of such entity. - - 15. Right to Use. You may use the Original Work in all ways not otherwise restricted or conditioned by this License or by law, and Licensor promises not to interfere with or be responsible for such uses by You. - - 16. Modification of This License. This License is Copyright (C) 2005 Lawrence Rosen. Permission is granted to copy, distribute, or communicate this License without modification. Nothing in this License permits You to modify this License as applied to the Original Work or to Derivative Works. However, You may modify the text of this License and copy, distribute or communicate your modified version (the "Modified License") and apply it to other original works of authorship subject to the following conditions: (i) You may not indicate in any way that your Modified License is the "Open Software License" or "OSL" and you may not use those names in the name of your Modified License; (ii) You must replace the notice specified in the first paragraph above with the notice "Licensed under <insert your license name here>" or with a notice of your own that is not confusingly similar to the notice in this License; and (iii) You may not claim that your original works are open source software unless your Modified License has been approved by Open Source Initiative (OSI) and You comply with its license review and certification process. \ No newline at end of file diff --git a/dev/tests/acceptance/tests/functional/Magento/FunctionalTest/ConfigurableProductWishlist/LICENSE_AFL.txt b/dev/tests/acceptance/tests/functional/Magento/FunctionalTest/ConfigurableProductWishlist/LICENSE_AFL.txt deleted file mode 100644 index f39d641b18a19..0000000000000 --- a/dev/tests/acceptance/tests/functional/Magento/FunctionalTest/ConfigurableProductWishlist/LICENSE_AFL.txt +++ /dev/null @@ -1,48 +0,0 @@ - -Academic Free License ("AFL") v. 3.0 - -This Academic Free License (the "License") applies to any original work of authorship (the "Original Work") whose owner (the "Licensor") has placed the following licensing notice adjacent to the copyright notice for the Original Work: - -Licensed under the Academic Free License version 3.0 - - 1. Grant of Copyright License. Licensor grants You a worldwide, royalty-free, non-exclusive, sublicensable license, for the duration of the copyright, to do the following: - - 1. to reproduce the Original Work in copies, either alone or as part of a collective work; - - 2. to translate, adapt, alter, transform, modify, or arrange the Original Work, thereby creating derivative works ("Derivative Works") based upon the Original Work; - - 3. to distribute or communicate copies of the Original Work and Derivative Works to the public, under any license of your choice that does not contradict the terms and conditions, including Licensor's reserved rights and remedies, in this Academic Free License; - - 4. to perform the Original Work publicly; and - - 5. to display the Original Work publicly. - - 2. Grant of Patent License. Licensor grants You a worldwide, royalty-free, non-exclusive, sublicensable license, under patent claims owned or controlled by the Licensor that are embodied in the Original Work as furnished by the Licensor, for the duration of the patents, to make, use, sell, offer for sale, have made, and import the Original Work and Derivative Works. - - 3. Grant of Source Code License. The term "Source Code" means the preferred form of the Original Work for making modifications to it and all available documentation describing how to modify the Original Work. Licensor agrees to provide a machine-readable copy of the Source Code of the Original Work along with each copy of the Original Work that Licensor distributes. Licensor reserves the right to satisfy this obligation by placing a machine-readable copy of the Source Code in an information repository reasonably calculated to permit inexpensive and convenient access by You for as long as Licensor continues to distribute the Original Work. - - 4. Exclusions From License Grant. Neither the names of Licensor, nor the names of any contributors to the Original Work, nor any of their trademarks or service marks, may be used to endorse or promote products derived from this Original Work without express prior permission of the Licensor. Except as expressly stated herein, nothing in this License grants any license to Licensor's trademarks, copyrights, patents, trade secrets or any other intellectual property. No patent license is granted to make, use, sell, offer for sale, have made, or import embodiments of any patent claims other than the licensed claims defined in Section 2. No license is granted to the trademarks of Licensor even if such marks are included in the Original Work. Nothing in this License shall be interpreted to prohibit Licensor from licensing under terms different from this License any Original Work that Licensor otherwise would have a right to license. - - 5. External Deployment. The term "External Deployment" means the use, distribution, or communication of the Original Work or Derivative Works in any way such that the Original Work or Derivative Works may be used by anyone other than You, whether those works are distributed or communicated to those persons or made available as an application intended for use over a network. As an express condition for the grants of license hereunder, You must treat any External Deployment by You of the Original Work or a Derivative Work as a distribution under section 1(c). - - 6. Attribution Rights. You must retain, in the Source Code of any Derivative Works that You create, all copyright, patent, or trademark notices from the Source Code of the Original Work, as well as any notices of licensing and any descriptive text identified therein as an "Attribution Notice." You must cause the Source Code for any Derivative Works that You create to carry a prominent Attribution Notice reasonably calculated to inform recipients that You have modified the Original Work. - - 7. Warranty of Provenance and Disclaimer of Warranty. Licensor warrants that the copyright in and to the Original Work and the patent rights granted herein by Licensor are owned by the Licensor or are sublicensed to You under the terms of this License with the permission of the contributor(s) of those copyrights and patent rights. Except as expressly stated in the immediately preceding sentence, the Original Work is provided under this License on an "AS IS" BASIS and WITHOUT WARRANTY, either express or implied, including, without limitation, the warranties of non-infringement, merchantability or fitness for a particular purpose. THE ENTIRE RISK AS TO THE QUALITY OF THE ORIGINAL WORK IS WITH YOU. This DISCLAIMER OF WARRANTY constitutes an essential part of this License. No license to the Original Work is granted by this License except under this disclaimer. - - 8. Limitation of Liability. Under no circumstances and under no legal theory, whether in tort (including negligence), contract, or otherwise, shall the Licensor be liable to anyone for any indirect, special, incidental, or consequential damages of any character arising as a result of this License or the use of the Original Work including, without limitation, damages for loss of goodwill, work stoppage, computer failure or malfunction, or any and all other commercial damages or losses. This limitation of liability shall not apply to the extent applicable law prohibits such limitation. - - 9. Acceptance and Termination. If, at any time, You expressly assented to this License, that assent indicates your clear and irrevocable acceptance of this License and all of its terms and conditions. If You distribute or communicate copies of the Original Work or a Derivative Work, You must make a reasonable effort under the circumstances to obtain the express assent of recipients to the terms of this License. This License conditions your rights to undertake the activities listed in Section 1, including your right to create Derivative Works based upon the Original Work, and doing so without honoring these terms and conditions is prohibited by copyright law and international treaty. Nothing in this License is intended to affect copyright exceptions and limitations (including "fair use" or "fair dealing"). This License shall terminate immediately and You may no longer exercise any of the rights granted to You by this License upon your failure to honor the conditions in Section 1(c). - - 10. Termination for Patent Action. This License shall terminate automatically and You may no longer exercise any of the rights granted to You by this License as of the date You commence an action, including a cross-claim or counterclaim, against Licensor or any licensee alleging that the Original Work infringes a patent. This termination provision shall not apply for an action alleging patent infringement by combinations of the Original Work with other software or hardware. - - 11. Jurisdiction, Venue and Governing Law. Any action or suit relating to this License may be brought only in the courts of a jurisdiction wherein the Licensor resides or in which Licensor conducts its primary business, and under the laws of that jurisdiction excluding its conflict-of-law provisions. The application of the United Nations Convention on Contracts for the International Sale of Goods is expressly excluded. Any use of the Original Work outside the scope of this License or after its termination shall be subject to the requirements and penalties of copyright or patent law in the appropriate jurisdiction. This section shall survive the termination of this License. - - 12. Attorneys' Fees. In any action to enforce the terms of this License or seeking damages relating thereto, the prevailing party shall be entitled to recover its costs and expenses, including, without limitation, reasonable attorneys' fees and costs incurred in connection with such action, including any appeal of such action. This section shall survive the termination of this License. - - 13. Miscellaneous. If any provision of this License is held to be unenforceable, such provision shall be reformed only to the extent necessary to make it enforceable. - - 14. Definition of "You" in This License. "You" throughout this License, whether in upper or lower case, means an individual or a legal entity exercising rights under, and complying with all of the terms of, this License. For legal entities, "You" includes any entity that controls, is controlled by, or is under common control with you. For purposes of this definition, "control" means (i) the power, direct or indirect, to cause the direction or management of such entity, whether by contract or otherwise, or (ii) ownership of fifty percent (50%) or more of the outstanding shares, or (iii) beneficial ownership of such entity. - - 15. Right to Use. You may use the Original Work in all ways not otherwise restricted or conditioned by this License or by law, and Licensor promises not to interfere with or be responsible for such uses by You. - - 16. Modification of This License. This License is Copyright © 2005 Lawrence Rosen. Permission is granted to copy, distribute, or communicate this License without modification. Nothing in this License permits You to modify this License as applied to the Original Work or to Derivative Works. However, You may modify the text of this License and copy, distribute or communicate your modified version (the "Modified License") and apply it to other original works of authorship subject to the following conditions: (i) You may not indicate in any way that your Modified License is the "Academic Free License" or "AFL" and you may not use those names in the name of your Modified License; (ii) You must replace the notice specified in the first paragraph above with the notice "Licensed under <insert your license name here>" or with a notice of your own that is not confusingly similar to the notice in this License; and (iii) You may not claim that your original works are open source software unless your Modified License has been approved by Open Source Initiative (OSI) and You comply with its license review and certification process. diff --git a/dev/tests/acceptance/tests/functional/Magento/FunctionalTest/ConfigurableProductWishlist/Test/EndToEndB2CLoggedInUserTest.xml b/dev/tests/acceptance/tests/functional/Magento/FunctionalTest/ConfigurableProductWishlist/Test/EndToEndB2CLoggedInUserTest.xml deleted file mode 100644 index cb3d9edbc1cbb..0000000000000 --- a/dev/tests/acceptance/tests/functional/Magento/FunctionalTest/ConfigurableProductWishlist/Test/EndToEndB2CLoggedInUserTest.xml +++ /dev/null @@ -1,29 +0,0 @@ -<?xml version="1.0" encoding="UTF-8"?> -<!-- - /** - * Copyright © Magento, Inc. All rights reserved. - * See COPYING.txt for license details. - */ ---> - -<tests xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" - xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/testSchema.xsd"> - <test name="EndToEndB2CLoggedInUserTest"> - <!-- Step 5: Add products to wishlist --> - <!-- Add Configurable Product to wishlist --> - <comment userInput="Add Configurable Product to wishlist" stepKey="commentAddConfigurableProductToWishlist" before="endOfAddingProductsToWishlist" /> - <click selector="{{StorefrontHeaderSection.NavigationCategoryByName($$createCategory.name$$)}}" after="commentAddConfigurableProductToWishlist" stepKey="wishlistGotoCategory3"/> - <click selector="{{StorefrontCategoryProductSection.ProductTitleByName($$createConfigProduct.name$$)}}" after="wishlistGotoCategory3" stepKey="wishlistClickConfigurableProduct"/> - <actionGroup ref="StorefrontCustomerAddProductToWishlistActionGroup" after="wishlistClickConfigurableProduct" stepKey="wishlistAddConfigurableProductToWishlist"> - <argument name="productVar" value="$$createConfigProduct$$"/> - </actionGroup> - <actionGroup ref="StorefrontCustomerCheckConfigurableProductInWishlist" after="wishlistAddConfigurableProductToWishlist" stepKey="wishlistCheckConfigurableProductInWishlist"> - <argument name="productVar" value="$$createConfigProduct$$"/> - <argument name="optionProductVar" value="$$createConfigChildProduct1$$"/> - </actionGroup> - <actionGroup ref="StorefrontCustomerCheckConfigurableProductInWishlistSidebar" after="wishlistCheckConfigurableProductInWishlist" stepKey="wishlistCheckConfigurableProductInWishlistSidebar"> - <argument name="productVar" value="$$createConfigProduct$$"/> - <argument name="optionProductVar" value="$$createConfigChildProduct1$$"/> - </actionGroup> - </test> -</tests> diff --git a/dev/tests/api-functional/_files/Magento/TestModuleAuthorizenetAcceptjs/Gateway/Http/MockClient.php b/dev/tests/api-functional/_files/Magento/TestModuleAuthorizenetAcceptjs/Gateway/Http/MockClient.php deleted file mode 100644 index 3c82d4eba740f..0000000000000 --- a/dev/tests/api-functional/_files/Magento/TestModuleAuthorizenetAcceptjs/Gateway/Http/MockClient.php +++ /dev/null @@ -1,135 +0,0 @@ -<?php -/** - * Copyright © Magento, Inc. All rights reserved. - * See COPYING.txt for license details. - */ -declare(strict_types=1); - -namespace Magento\TestModuleAuthorizenetAcceptjs\Gateway\Http; - -use Magento\Framework\Math\Random; -use Magento\Framework\Stdlib\ArrayManager; -use Magento\Payment\Gateway\Http\ClientInterface; -use Magento\Payment\Gateway\Http\TransferInterface; - -/** - * A client for mocking communicate with the Authorize.net API - */ -class MockClient implements ClientInterface -{ - /** - * @var Random - */ - private $random; - - /** - * @var ArrayManager - */ - private $arrayManager; - - /** - * @param Random $random - * @param ArrayManager $arrayManager - */ - public function __construct( - Random $random, - ArrayManager $arrayManager - ) { - $this->random = $random; - $this->arrayManager = $arrayManager; - } - - /** - * Places request to gateway. Returns result as ENV array - * - * @param TransferInterface $transferObject - * @return array - */ - public function placeRequest(TransferInterface $transferObject): array - { - $request = $transferObject->getBody(); - $nonce = $this->arrayManager->get('transactionRequest/payment/opaqueData/dataValue', $request); - $descriptor = $this->arrayManager->get('transactionRequest/payment/opaqueData/dataDescriptor', $request); - - $approve = true; - if ($nonce !== 'fake-nonce' || $descriptor !== 'COMMON.ACCEPT.INAPP.PAYMENT') { - $approve = false; - } - - return $this->createResponse($approve); - } - - /** - * Create mock response body - * - * @param bool $approve - * @return array - * @throws \Magento\Framework\Exception\LocalizedException - */ - private function createResponse(bool $approve): array - { - return [ - 'transactionResponse' => [ - 'responseCode' => $approve ? '1' : '2', - 'authCode' => strtoupper($this->random->getRandomString(6)), - 'avsResultCode' => 'Y', - 'cvvResultCode' => 'P', - 'cavvResultCode' => '2', - 'transId' => random_int(10000000000, 99999999999), - 'refTransId' => '', - 'transHash' => '', - 'testRequest' => '0', - 'accountNumber' => 'XXXX1111', - 'accountType' => 'Visa', - 'messages' => $approve ? $this->getApprovalMessage() : $this->getDeclineMessage(), - 'userFields' => [ - [ - 'name' => 'transactionType', - 'value' => 'authOnlyTransaction', - ], - ], - 'transHashSha2' => 'fake-hash', - 'SupplementalDataQualificationIndicator' => '0', - ], - 'messages' => [ - 'resultCode' => 'Ok', - 'message' => [ - [ - 'code' => 'I00001', - 'text' => 'Successful.', - ], - ], - ], - ]; - } - - /** - * Provide approval message for response - * - * @return array - */ - private function getApprovalMessage(): array - { - return [ - [ - 'code' => '1', - 'description' => 'This transaction has been approved.', - ], - ]; - } - - /** - * Provide decline message for response - * - * @return array - */ - private function getDeclineMessage(): array - { - return [ - [ - 'code' => '2', - 'description' => 'This transaction has been declined.', - ], - ]; - } -} diff --git a/dev/tests/api-functional/_files/Magento/TestModuleAuthorizenetAcceptjs/Gateway/Validator/TransactionHashValidator.php b/dev/tests/api-functional/_files/Magento/TestModuleAuthorizenetAcceptjs/Gateway/Validator/TransactionHashValidator.php deleted file mode 100644 index b0e281e9faa5c..0000000000000 --- a/dev/tests/api-functional/_files/Magento/TestModuleAuthorizenetAcceptjs/Gateway/Validator/TransactionHashValidator.php +++ /dev/null @@ -1,29 +0,0 @@ -<?php -/** - * Copyright © Magento, Inc. All rights reserved. - * See COPYING.txt for license details. - */ -declare(strict_types=1); - -namespace Magento\TestModuleAuthorizenetAcceptjs\Gateway\Validator; - -use Magento\Payment\Gateway\Validator\AbstractValidator; -use Magento\Payment\Gateway\Validator\ResultInterface; - -/** - * Force validation of the transaction hash - */ -class TransactionHashValidator extends AbstractValidator -{ - /** - * Skip validation of transaction hash in mock response - * - * @param array $validationSubject - * @return ResultInterface - * @SuppressWarnings(PHPMD.UnusedFormalParameter) - */ - public function validate(array $validationSubject): ResultInterface - { - return $this->createResult(true); - } -} diff --git a/dev/tests/api-functional/_files/Magento/TestModuleAuthorizenetAcceptjs/etc/di.xml b/dev/tests/api-functional/_files/Magento/TestModuleAuthorizenetAcceptjs/etc/di.xml deleted file mode 100644 index 9f19743cfc205..0000000000000 --- a/dev/tests/api-functional/_files/Magento/TestModuleAuthorizenetAcceptjs/etc/di.xml +++ /dev/null @@ -1,11 +0,0 @@ -<?xml version="1.0"?> -<!-- -/** - * Copyright © Magento, Inc. All rights reserved. - * See COPYING.txt for license details. - */ ---> -<config xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="urn:magento:framework:ObjectManager/etc/config.xsd"> - <preference for="Magento\AuthorizenetAcceptjs\Gateway\Http\Client" type="Magento\TestModuleAuthorizenetAcceptjs\Gateway\Http\MockClient" /> - <preference for="Magento\AuthorizenetAcceptjs\Gateway\Validator\TransactionHashValidator" type="Magento\TestModuleAuthorizenetAcceptjs\Gateway\Validator\TransactionHashValidator" /> -</config> diff --git a/dev/tests/api-functional/_files/Magento/TestModuleAuthorizenetAcceptjs/etc/module.xml b/dev/tests/api-functional/_files/Magento/TestModuleAuthorizenetAcceptjs/etc/module.xml deleted file mode 100644 index 378b61946ef3a..0000000000000 --- a/dev/tests/api-functional/_files/Magento/TestModuleAuthorizenetAcceptjs/etc/module.xml +++ /dev/null @@ -1,10 +0,0 @@ -<?xml version="1.0"?> -<!-- -/** - * Copyright © Magento, Inc. All rights reserved. - * See COPYING.txt for license details. - */ ---> -<config xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="urn:magento:framework:Module/etc/module.xsd"> - <module name="Magento_TestModuleAuthorizenetAcceptjs" /> -</config> \ No newline at end of file diff --git a/dev/tests/api-functional/_files/Magento/TestModuleAuthorizenetAcceptjs/registration.php b/dev/tests/api-functional/_files/Magento/TestModuleAuthorizenetAcceptjs/registration.php deleted file mode 100644 index 28c5861ed5fb8..0000000000000 --- a/dev/tests/api-functional/_files/Magento/TestModuleAuthorizenetAcceptjs/registration.php +++ /dev/null @@ -1,13 +0,0 @@ -<?php -/** - * Copyright © Magento, Inc. All rights reserved. - * See COPYING.txt for license details. - */ -declare(strict_types=1); - -use Magento\Framework\Component\ComponentRegistrar; - -$registrar = new ComponentRegistrar(); -if ($registrar->getPath(ComponentRegistrar::MODULE, 'Magento_TestModuleAuthorizenetAcceptjs') === null) { - ComponentRegistrar::register(ComponentRegistrar::MODULE, 'Magento_TestModuleAuthorizenetAcceptjs', __DIR__); -} diff --git a/dev/tests/api-functional/framework/Magento/TestFramework/Authentication/OauthHelper.php b/dev/tests/api-functional/framework/Magento/TestFramework/Authentication/OauthHelper.php index 60dd6b57bda86..eb5cedf9e477c 100644 --- a/dev/tests/api-functional/framework/Magento/TestFramework/Authentication/OauthHelper.php +++ b/dev/tests/api-functional/framework/Magento/TestFramework/Authentication/OauthHelper.php @@ -7,12 +7,18 @@ */ namespace Magento\TestFramework\Authentication; +use Magento\Framework\Exception\IntegrationException; +use Magento\Framework\Exception\LocalizedException; +use Magento\Framework\Oauth\Exception; use Magento\TestFramework\Authentication\Rest\OauthClient; use Magento\TestFramework\Helper\Bootstrap; use OAuth\Common\Consumer\Credentials; -use Zend\Stdlib\Exception\LogicException; +use Laminas\Stdlib\Exception\LogicException; use Magento\Integration\Model\Integration; +/** + * Authentication Oauth helper + */ class OauthHelper { /** @var array */ @@ -20,6 +26,7 @@ class OauthHelper /** * Generate authentication credentials + * * @param string $date consumer creation date * @return array * <pre> @@ -31,6 +38,8 @@ class OauthHelper * 'token' => $token // retrieved token Model * ); * </pre> + * @throws LocalizedException + * @throws Exception */ public static function getConsumerCredentials($date = null) { @@ -69,6 +78,9 @@ public static function getConsumerCredentials($date = null) * 'oauth_client' => $oauthClient // OauthClient instance used to fetch the access token * ); * </pre> + * @throws LocalizedException + * @throws Exception + * @throws \OAuth\Common\Http\Exception\TokenResponseException */ public static function getAccessToken() { @@ -104,7 +116,8 @@ public static function getAccessToken() * 'integration' => $integration // Integration instance associated with access token * ); * </pre> - * @throws LogicException + * @throws LocalizedException + * @throws Exception */ public static function getApiAccessCredentials($resources = null, Integration $integrationModel = null) { @@ -170,7 +183,8 @@ protected static function _rmRecursive($dir, $doSaveRoot = false) * * @param array $resources * @return \Magento\Integration\Model\Integration - * @throws \Zend\Stdlib\Exception\LogicException + * @throws LogicException + * @throws IntegrationException */ protected static function _createIntegration($resources) { diff --git a/dev/tests/api-functional/framework/Magento/TestFramework/TestCase/Webapi/Adapter/Soap.php b/dev/tests/api-functional/framework/Magento/TestFramework/TestCase/Webapi/Adapter/Soap.php index 8453edb071b3e..01c48c8410f5a 100644 --- a/dev/tests/api-functional/framework/Magento/TestFramework/TestCase/Webapi/Adapter/Soap.php +++ b/dev/tests/api-functional/framework/Magento/TestFramework/TestCase/Webapi/Adapter/Soap.php @@ -19,7 +19,7 @@ class Soap implements \Magento\TestFramework\TestCase\Webapi\AdapterInterface /** * SOAP client initialized with different WSDLs. * - * @var \Zend\Soap\Client[] + * @var \Laminas\Soap\Client[] */ protected $_soapClients = ['custom' => [], 'default' => []]; @@ -67,7 +67,7 @@ public function call($serviceInfo, $arguments = [], $storeCode = null, $integrat * * @param string $serviceInfo PHP service interface name, should include version if present * @param string|null $storeCode - * @return \Zend\Soap\Client + * @return \Laminas\Soap\Client */ protected function _getSoapClient($serviceInfo, $storeCode = null) { @@ -75,7 +75,7 @@ protected function _getSoapClient($serviceInfo, $storeCode = null) [$this->_getSoapServiceName($serviceInfo) . $this->_getSoapServiceVersion($serviceInfo)], $storeCode ); - /** @var \Zend\Soap\Client $soapClient */ + /** @var \Laminas\Soap\Client $soapClient */ $soapClient = null; if (isset($serviceInfo['soap']['token'])) { $token = $serviceInfo['soap']['token']; @@ -104,7 +104,7 @@ protected function _getSoapClient($serviceInfo, $storeCode = null) * * @param string $wsdlUrl * @param string $token Authentication token - * @return \Zend\Soap\Client + * @return \Laminas\Soap\Client */ public function instantiateSoapClient($wsdlUrl, $token = null) { @@ -113,7 +113,7 @@ public function instantiateSoapClient($wsdlUrl, $token = null) : \Magento\TestFramework\Authentication\OauthHelper::getApiAccessCredentials()['key']; $opts = ['http' => ['header' => "Authorization: Bearer " . $accessCredentials]]; $context = stream_context_create($opts); - $soapClient = new \Zend\Soap\Client($wsdlUrl); + $soapClient = new \Laminas\Soap\Client($wsdlUrl); $soapClient->setSoapVersion(SOAP_1_2); $soapClient->setStreamContext($context); if (TESTS_XDEBUG_ENABLED) { diff --git a/dev/tests/api-functional/testsuite/Magento/Catalog/Api/ProductRepositoryInterfaceTest.php b/dev/tests/api-functional/testsuite/Magento/Catalog/Api/ProductRepositoryInterfaceTest.php index 76107ebc6a13a..2512de3537f28 100644 --- a/dev/tests/api-functional/testsuite/Magento/Catalog/Api/ProductRepositoryInterfaceTest.php +++ b/dev/tests/api-functional/testsuite/Magento/Catalog/Api/ProductRepositoryInterfaceTest.php @@ -8,19 +8,13 @@ namespace Magento\Catalog\Api; use Magento\Authorization\Model\Role; -use Magento\Authorization\Model\Rules; use Magento\Authorization\Model\RoleFactory; +use Magento\Authorization\Model\Rules; use Magento\Authorization\Model\RulesFactory; use Magento\Catalog\Api\Data\ProductInterface; use Magento\CatalogInventory\Api\Data\StockItemInterface; use Magento\Downloadable\Api\DomainManagerInterface; use Magento\Downloadable\Model\Link; -use Magento\Integration\Api\AdminTokenServiceInterface; -use Magento\Store\Model\Store; -use Magento\Store\Model\Website; -use Magento\Store\Model\WebsiteRepository; -use Magento\TestFramework\Helper\Bootstrap; -use Magento\TestFramework\TestCase\WebapiAbstract; use Magento\Framework\Api\ExtensibleDataInterface; use Magento\Framework\Api\FilterBuilder; use Magento\Framework\Api\SearchCriteriaBuilder; @@ -28,6 +22,12 @@ use Magento\Framework\Api\SortOrderBuilder; use Magento\Framework\Exception\NoSuchEntityException; use Magento\Framework\Webapi\Exception as HTTPExceptionCodes; +use Magento\Integration\Api\AdminTokenServiceInterface; +use Magento\Store\Model\Store; +use Magento\Store\Model\Website; +use Magento\Store\Model\WebsiteRepository; +use Magento\TestFramework\Helper\Bootstrap; +use Magento\TestFramework\TestCase\WebapiAbstract; /** * Test for \Magento\Catalog\Api\ProductRepositoryInterface @@ -280,7 +280,7 @@ public function testCreateWithMultipleWebsites() $websitesData = [ 'website_ids' => [ 1, - (int) $website->getId(), + (int)$website->getId(), ] ]; $productBuilder[ProductInterface::EXTENSION_ATTRIBUTES_KEY] = $websitesData; @@ -491,7 +491,6 @@ public function testProductLinks() ProductInterface::TYPE_ID => 'simple', ProductInterface::PRICE => 100, ProductInterface::STATUS => 1, - ProductInterface::TYPE_ID => 'simple', ProductInterface::ATTRIBUTE_SET_ID => 4, ProductInterface::EXTENSION_ATTRIBUTES_KEY => [ 'stock_item' => $this->getStockItemData() @@ -1096,6 +1095,86 @@ public function testGetListWithFilteringByStoreDataProvider() ]; } + /** + * Test getList() method with pagination + * + * @param int $pageSize + * @param int $currentPage + * @param int $expectedCount + * + * @magentoAppIsolation enabled + * @magentoApiDataFixture Magento/Catalog/_files/products_for_search.php + * @dataProvider productPaginationDataProvider + */ + public function testGetListPagination(int $pageSize, int $currentPage, int $expectedCount) + { + $fixtureProducts = 5; + + /** @var FilterBuilder $filterBuilder */ + $filterBuilder = Bootstrap::getObjectManager()->create(FilterBuilder::class); + + $categoryFilter = $filterBuilder->setField('category_id') + ->setValue(333) + ->create(); + + /** @var SearchCriteriaBuilder $searchCriteriaBuilder */ + $searchCriteriaBuilder = Bootstrap::getObjectManager()->create(SearchCriteriaBuilder::class); + + $searchCriteriaBuilder->addFilters([$categoryFilter]); + $searchCriteriaBuilder->setPageSize($pageSize); + $searchCriteriaBuilder->setCurrentPage($currentPage); + + $searchData = $searchCriteriaBuilder->create()->__toArray(); + $requestData = ['searchCriteria' => $searchData]; + $serviceInfo = [ + 'rest' => [ + 'resourcePath' => self::RESOURCE_PATH . '?' . http_build_query($requestData), + 'httpMethod' => \Magento\Framework\Webapi\Rest\Request::HTTP_METHOD_GET, + ], + 'soap' => [ + 'service' => self::SERVICE_NAME, + 'serviceVersion' => self::SERVICE_VERSION, + 'operation' => self::SERVICE_NAME . 'GetList', + ], + ]; + + $searchResult = $this->_webApiCall($serviceInfo, $requestData); + + $this->assertEquals($fixtureProducts, $searchResult['total_count']); + $this->assertCount($expectedCount, $searchResult['items']); + } + + /** + * Keep in mind: Fixture contains 5 products + * + * @return array + */ + public function productPaginationDataProvider() + { + return [ + 'expect-all-items' => [ + 'pageSize' => 10, + 'currentPage' => 1, + 'expectedCount' => 5 + ], + 'expect-page=size-items' => [ + 'pageSize' => 2, + 'currentPage' => 1, + 'expectedCount' => 2 + ], + 'expect-less-than-pagesize-elements' => [ + 'pageSize' => 3, + 'currentPage' => 2, + 'expectedCount' => 2 + ], + 'expect-no-items' => [ + 'pageSize' => 100, + 'currentPage' => 99, + 'expectedCount' => 0 + ] + ]; + } + /** * Test getList() method with multiple filter groups and sorting and pagination * @@ -1133,7 +1212,7 @@ public function testGetListWithMultipleFilterGroupsAndSortingAndPagination() $sortOrder = $sortOrderBuilder->setField('meta_title')->setDirection(SortOrder::SORT_DESC)->create(); /** @var SearchCriteriaBuilder $searchCriteriaBuilder */ - $searchCriteriaBuilder = Bootstrap::getObjectManager()->create(SearchCriteriaBuilder::class); + $searchCriteriaBuilder = Bootstrap::getObjectManager()->create(SearchCriteriaBuilder::class); $searchCriteriaBuilder->addFilters([$filter1, $filter2, $filter3, $filter4]); $searchCriteriaBuilder->addFilters([$filter5]); @@ -1393,8 +1472,6 @@ public function testTierPrices() $response = $this->getProduct($productData[ProductInterface::SKU]); $response[self::KEY_TIER_PRICES] = []; $response = $this->updateProduct($response); - $this->assertArrayHasKey(self::KEY_TIER_PRICES, $response, "expected to have the 'tier_prices' key"); - $this->assertEmpty($response[self::KEY_TIER_PRICES], "expected to have an empty array of 'tier_prices'"); // delete the product with tier prices; expect that all goes well $response = $this->deleteProduct($productData[ProductInterface::SKU]); @@ -1618,7 +1695,6 @@ public function testUpdateStatus() ProductInterface::TYPE_ID => 'simple', ProductInterface::PRICE => 100, ProductInterface::STATUS => 0, - ProductInterface::TYPE_ID => 'simple', ProductInterface::ATTRIBUTE_SET_ID => 4, ]; @@ -1728,8 +1804,8 @@ private function assertMultiselectValue($productSku, $multiselectAttributeCode, * Test design settings authorization * * @magentoApiDataFixture Magento/User/_files/user_with_custom_role.php - * @throws \Throwable * @return void + * @throws \Throwable */ public function testSaveDesign(): void { diff --git a/dev/tests/api-functional/testsuite/Magento/Cms/Api/PageRepositoryTest.php b/dev/tests/api-functional/testsuite/Magento/Cms/Api/PageRepositoryTest.php index 015eb067e4c8e..bff2ba3ce7aac 100644 --- a/dev/tests/api-functional/testsuite/Magento/Cms/Api/PageRepositoryTest.php +++ b/dev/tests/api-functional/testsuite/Magento/Cms/Api/PageRepositoryTest.php @@ -6,14 +6,18 @@ namespace Magento\Cms\Api; use Magento\Authorization\Model\Role; -use Magento\Authorization\Model\Rules; use Magento\Authorization\Model\RoleFactory; +use Magento\Authorization\Model\Rules; use Magento\Authorization\Model\RulesFactory; use Magento\Cms\Api\Data\PageInterface; +use Magento\Cms\Api\Data\PageInterfaceFactory; +use Magento\Framework\Api\DataObjectHelper; use Magento\Framework\Api\FilterBuilder; use Magento\Framework\Api\SearchCriteriaBuilder; use Magento\Framework\Api\SortOrder; use Magento\Framework\Api\SortOrderBuilder; +use Magento\Framework\Reflection\DataObjectProcessor; +use Magento\Framework\Webapi\Rest\Request; use Magento\Integration\Api\AdminTokenServiceInterface; use Magento\TestFramework\Helper\Bootstrap; use Magento\TestFramework\TestCase\WebapiAbstract; @@ -25,34 +29,39 @@ */ class PageRepositoryTest extends WebapiAbstract { - const SERVICE_NAME = 'cmsPageRepositoryV1'; - const SERVICE_VERSION = 'V1'; - const RESOURCE_PATH = '/V1/cmsPage'; + private const PAGE_TITLE = 'Page title'; + private const PAGE_TITLE_NEW = 'Page title new'; + private const PAGE_CONTENT = '<h1>Some content</h1>'; + private const PAGE_IDENTIFIER_PREFIX = 'page-'; + + private const SERVICE_NAME = 'cmsPageRepositoryV1'; + private const SERVICE_VERSION = 'V1'; + private const RESOURCE_PATH = '/V1/cmsPage'; /** - * @var \Magento\Cms\Api\Data\PageInterfaceFactory + * @var PageInterfaceFactory */ - protected $pageFactory; + private $pageFactory; /** - * @var \Magento\Cms\Api\PageRepositoryInterface + * @var PageRepositoryInterface */ - protected $pageRepository; + private $pageRepository; /** - * @var \Magento\Framework\Api\DataObjectHelper + * @var DataObjectHelper */ - protected $dataObjectHelper; + private $dataObjectHelper; /** - * @var \Magento\Framework\Reflection\DataObjectProcessor + * @var DataObjectProcessor */ - protected $dataObjectProcessor; + private $dataObjectProcessor; /** - * @var \Magento\Cms\Api\Data\PageInterface|null + * @var PageInterface|null */ - protected $currentPage; + private $currentPage; /** * @var RoleFactory @@ -70,39 +79,49 @@ class PageRepositoryTest extends WebapiAbstract private $adminTokens; /** - * Execute per test initialization. + * @var array + */ + private $createdPages = []; + + /** + * @inheritdoc */ public function setUp() { - $this->pageFactory = Bootstrap::getObjectManager()->create(\Magento\Cms\Api\Data\PageInterfaceFactory::class); - $this->pageRepository = Bootstrap::getObjectManager()->create(\Magento\Cms\Api\PageRepositoryInterface::class); - $this->dataObjectHelper = Bootstrap::getObjectManager()->create(\Magento\Framework\Api\DataObjectHelper::class); - $this->dataObjectProcessor = Bootstrap::getObjectManager() - ->create(\Magento\Framework\Reflection\DataObjectProcessor::class); + $this->pageFactory = Bootstrap::getObjectManager()->create(PageInterfaceFactory::class); + $this->pageRepository = Bootstrap::getObjectManager()->create(PageRepositoryInterface::class); + $this->dataObjectHelper = Bootstrap::getObjectManager()->create(DataObjectHelper::class); + $this->dataObjectProcessor = Bootstrap::getObjectManager()->create(DataObjectProcessor::class); $this->roleFactory = Bootstrap::getObjectManager()->get(RoleFactory::class); $this->rulesFactory = Bootstrap::getObjectManager()->get(RulesFactory::class); $this->adminTokens = Bootstrap::getObjectManager()->get(AdminTokenServiceInterface::class); } /** - * Clear temporary data + * @inheritdoc */ - public function tearDown() + protected function tearDown(): void { if ($this->currentPage) { $this->pageRepository->delete($this->currentPage); $this->currentPage = null; } + + foreach ($this->createdPages as $page) { + $this->pageRepository->delete($page); + } } /** - * Test get \Magento\Cms\Api\Data\PageInterface + * Test get page + * + * @return void */ - public function testGet() + public function testGet(): void { - $pageTitle = 'Page title'; - $pageIdentifier = 'page-title' . uniqid(); - /** @var \Magento\Cms\Api\Data\PageInterface $pageDataObject */ + $pageTitle = self::PAGE_TITLE; + $pageIdentifier = self::PAGE_IDENTIFIER_PREFIX . uniqid(); + /** @var PageInterface $pageDataObject */ $pageDataObject = $this->pageFactory->create(); $pageDataObject->setTitle($pageTitle) ->setIdentifier($pageIdentifier); @@ -111,7 +130,7 @@ public function testGet() $serviceInfo = [ 'rest' => [ 'resourcePath' => self::RESOURCE_PATH . '/' . $this->currentPage->getId(), - 'httpMethod' => \Magento\Framework\Webapi\Rest\Request::HTTP_METHOD_GET, + 'httpMethod' => Request::HTTP_METHOD_GET, ], 'soap' => [ 'service' => self::SERVICE_NAME, @@ -129,13 +148,15 @@ public function testGet() } /** - * Test create \Magento\Cms\Api\Data\PageInterface + * Test create page + * + * @return void */ - public function testCreate() + public function testCreate(): void { - $pageTitle = 'Page title'; - $pageIdentifier = 'page-title' . uniqid(); - /** @var \Magento\Cms\Api\Data\PageInterface $pageDataObject */ + $pageTitle = self::PAGE_TITLE; + $pageIdentifier = self::PAGE_IDENTIFIER_PREFIX . uniqid(); + /** @var PageInterface $pageDataObject */ $pageDataObject = $this->pageFactory->create(); $pageDataObject->setTitle($pageTitle) ->setIdentifier($pageIdentifier); @@ -143,7 +164,7 @@ public function testCreate() $serviceInfo = [ 'rest' => [ 'resourcePath' => self::RESOURCE_PATH, - 'httpMethod' => \Magento\Framework\Webapi\Rest\Request::HTTP_METHOD_POST, + 'httpMethod' => Request::HTTP_METHOD_POST, ], 'soap' => [ 'service' => self::SERVICE_NAME, @@ -152,7 +173,8 @@ public function testCreate() ], ]; - $requestData = ['page' => [ + $requestData = [ + 'page' => [ PageInterface::IDENTIFIER => $pageDataObject->getIdentifier(), PageInterface::TITLE => $pageDataObject->getTitle(), ], @@ -170,10 +192,10 @@ public function testCreate() */ public function testUpdate() { - $pageTitle = 'Page title'; - $newPageTitle = 'New Page title'; - $pageIdentifier = 'page-title' . uniqid(); - /** @var \Magento\Cms\Api\Data\PageInterface $pageDataObject */ + $pageTitle = self::PAGE_TITLE; + $newPageTitle = self::PAGE_TITLE_NEW; + $pageIdentifier = self::PAGE_IDENTIFIER_PREFIX . uniqid(); + /** @var PageInterface $pageDataObject */ $pageDataObject = $this->pageFactory->create(); $pageDataObject->setTitle($pageTitle) ->setIdentifier($pageIdentifier); @@ -181,17 +203,17 @@ public function testUpdate() $this->dataObjectHelper->populateWithArray( $this->currentPage, [PageInterface::TITLE => $newPageTitle], - \Magento\Cms\Api\Data\PageInterface::class + PageInterface::class ); $pageData = $this->dataObjectProcessor->buildOutputDataArray( $this->currentPage, - \Magento\Cms\Api\Data\PageInterface::class + PageInterface::class ); $serviceInfo = [ 'rest' => [ 'resourcePath' => self::RESOURCE_PATH, - 'httpMethod' => \Magento\Framework\Webapi\Rest\Request::HTTP_METHOD_POST, + 'httpMethod' => Request::HTTP_METHOD_POST, ], 'soap' => [ 'service' => self::SERVICE_NAME, @@ -207,15 +229,69 @@ public function testUpdate() $this->assertEquals($pageData->getTitle(), $newPageTitle); } + /** + * Test update page one field + * + * @return void + */ + public function testUpdateOneField(): void + { + $pageTitle = self::PAGE_TITLE; + $content = self::PAGE_CONTENT; + $newPageTitle = self::PAGE_TITLE_NEW; + $pageIdentifier = self::PAGE_IDENTIFIER_PREFIX . uniqid(); + + /** @var PageInterface $pageDataObject */ + $pageDataObject = $this->pageFactory->create(); + $pageDataObject->setTitle($pageTitle) + ->setIdentifier($pageIdentifier) + ->setContent($content); + $this->currentPage = $this->pageRepository->save($pageDataObject); + $pageId = $this->currentPage->getId(); + + $serviceInfo = [ + 'rest' => [ + 'resourcePath' => self::RESOURCE_PATH . '/' . $pageId, + 'httpMethod' => Request::HTTP_METHOD_PUT, + ], + 'soap' => [ + 'service' => self::SERVICE_NAME, + 'serviceVersion' => self::SERVICE_VERSION, + 'operation' => self::SERVICE_NAME . 'Save', + ], + ]; + + $data = [ + 'page' => [ + 'title' => $newPageTitle, + ], + ]; + + if (TESTS_WEB_API_ADAPTER === self::ADAPTER_SOAP) { + $data['page'] += [ + 'id' => $pageId, + 'identifier' => $pageIdentifier, + ]; + } + + $page = $this->_webApiCall($serviceInfo, $data); + + $this->assertArrayHasKey('title', $page); + $this->assertEquals($page['title'], $newPageTitle); + + $this->assertArrayHasKey('content', $page); + $this->assertEquals($page['content'], $content); + } + /** * Test delete \Magento\Cms\Api\Data\PageInterface * @expectedException \Magento\Framework\Exception\NoSuchEntityException */ public function testDelete() { - $pageTitle = 'Page title'; - $pageIdentifier = 'page-title' . uniqid(); - /** @var \Magento\Cms\Api\Data\PageInterface $pageDataObject */ + $pageTitle = self::PAGE_TITLE; + $pageIdentifier = self::PAGE_IDENTIFIER_PREFIX . uniqid(); + /** @var PageInterface $pageDataObject */ $pageDataObject = $this->pageFactory->create(); $pageDataObject->setTitle($pageTitle) ->setIdentifier($pageIdentifier); @@ -224,7 +300,7 @@ public function testDelete() $serviceInfo = [ 'rest' => [ 'resourcePath' => self::RESOURCE_PATH . '/' . $this->currentPage->getId(), - 'httpMethod' => \Magento\Framework\Webapi\Rest\Request::HTTP_METHOD_DELETE, + 'httpMethod' => Request::HTTP_METHOD_DELETE, ], 'soap' => [ 'service' => self::SERVICE_NAME, @@ -291,7 +367,7 @@ public function testSearch() $serviceInfo = [ 'rest' => [ 'resourcePath' => self::RESOURCE_PATH . "/search" . '?' . http_build_query($requestData), - 'httpMethod' => \Magento\Framework\Webapi\Rest\Request::HTTP_METHOD_GET, + 'httpMethod' => Request::HTTP_METHOD_GET, ], 'soap' => [ 'service' => self::SERVICE_NAME, @@ -314,11 +390,12 @@ public function testSearch() */ public function testCreateSamePage() { - $pageIdentifier = 'page-' . uniqid(); + $pageIdentifier = self::PAGE_IDENTIFIER_PREFIX . uniqid(); $pageId = $this->createPageWithIdentifier($pageIdentifier); $this->deletePageByIdentifier($pageId); - $this->createPageWithIdentifier($pageIdentifier); + $id = $this->createPageWithIdentifier($pageIdentifier); + $this->currentPage = $this->pageRepository->getById($id); } /** @@ -341,14 +418,14 @@ private function prepareCmsPages() $pagesData['third'][PageInterface::IS_ACTIVE] = true; foreach ($pagesData as $key => $pageData) { - /** @var \Magento\Cms\Api\Data\PageInterface $pageDataObject */ + /** @var PageInterface $pageDataObject */ $pageDataObject = $this->pageFactory->create(); $this->dataObjectHelper->populateWithArray( $pageDataObject, $pageData, - \Magento\Cms\Api\Data\PageInterface::class + PageInterface::class ); - $result[$key] = $this->pageRepository->save($pageDataObject); + $this->createdPages[] = $result[$key] = $this->pageRepository->save($pageDataObject); } return $result; @@ -364,7 +441,7 @@ private function createPageWithIdentifier($identifier) $serviceInfo = [ 'rest' => [ 'resourcePath' => self::RESOURCE_PATH, - 'httpMethod' => \Magento\Framework\Webapi\Rest\Request::HTTP_METHOD_POST, + 'httpMethod' => Request::HTTP_METHOD_POST, ], 'soap' => [ 'service' => self::SERVICE_NAME, @@ -372,10 +449,10 @@ private function createPageWithIdentifier($identifier) 'operation' => self::SERVICE_NAME . 'Save', ], ]; - $requestData = ['page' => - [ + $requestData = [ + 'page' => [ PageInterface::IDENTIFIER => $identifier, - PageInterface::TITLE => 'Page title', + PageInterface::TITLE => self::PAGE_TITLE, ], ]; @@ -393,7 +470,7 @@ private function deletePageByIdentifier($pageId) $serviceInfo = [ 'rest' => [ 'resourcePath' => self::RESOURCE_PATH . '/' . $pageId, - 'httpMethod' => \Magento\Framework\Webapi\Rest\Request::HTTP_METHOD_DELETE, + 'httpMethod' => Request::HTTP_METHOD_DELETE, ], 'soap' => [ 'service' => self::SERVICE_NAME, @@ -433,7 +510,7 @@ public function testSaveDesign(): void $serviceInfo = [ 'rest' => [ 'resourcePath' => self::RESOURCE_PATH, - 'httpMethod' => \Magento\Framework\Webapi\Rest\Request::HTTP_METHOD_POST, + 'httpMethod' => Request::HTTP_METHOD_POST, 'token' => $token, ], 'soap' => [ @@ -446,7 +523,7 @@ public function testSaveDesign(): void $requestData = [ 'page' => [ PageInterface::IDENTIFIER => $id, - PageInterface::TITLE => 'Page title', + PageInterface::TITLE => self::PAGE_TITLE, PageInterface::CUSTOM_THEME => 1 ], ]; diff --git a/dev/tests/api-functional/testsuite/Magento/ConfigurableProduct/Api/ProductRepositoryTest.php b/dev/tests/api-functional/testsuite/Magento/ConfigurableProduct/Api/ProductRepositoryTest.php index 1dc7ca1ad44a6..c8ecab9ce54d8 100644 --- a/dev/tests/api-functional/testsuite/Magento/ConfigurableProduct/Api/ProductRepositoryTest.php +++ b/dev/tests/api-functional/testsuite/Magento/ConfigurableProduct/Api/ProductRepositoryTest.php @@ -3,11 +3,15 @@ * Copyright © Magento, Inc. All rights reserved. * See COPYING.txt for license details. */ - namespace Magento\ConfigurableProduct\Api; use Magento\Catalog\Api\Data\ProductInterface; +use Magento\Catalog\Model\Entity\Attribute; +use Magento\Eav\Model\Config; +use Magento\Eav\Model\ResourceModel\Entity\Attribute\Option\Collection; use Magento\Framework\Api\ExtensibleDataInterface; +use Magento\Framework\ObjectManagerInterface; +use Magento\Framework\Webapi\Rest\Request; use Magento\TestFramework\Helper\Bootstrap; use Magento\TestFramework\TestCase\WebapiAbstract; @@ -22,31 +26,31 @@ class ProductRepositoryTest extends WebapiAbstract const CONFIGURABLE_PRODUCT_SKU = 'configurable-product-sku'; /** - * @var \Magento\Eav\Model\Config + * @var Config */ protected $eavConfig; /** - * @var \Magento\Framework\ObjectManagerInterface + * @var ObjectManagerInterface */ protected $objectManager; /** - * @var \Magento\Catalog\Model\Entity\Attribute + * @var Attribute */ protected $configurableAttribute; /** - * Execute per test initialization + * @inheritdoc */ public function setUp() { $this->objectManager = Bootstrap::getObjectManager(); - $this->eavConfig = $this->objectManager->get(\Magento\Eav\Model\Config::class); + $this->eavConfig = $this->objectManager->get(Config::class); } /** - * Execute per test cleanup + * @inheritdoc */ public function tearDown() { @@ -54,16 +58,26 @@ public function tearDown() parent::tearDown(); } + /** + * Retrieve configurable attribute options + * + * @return array + */ protected function getConfigurableAttributeOptions() { - /** @var \Magento\Eav\Model\ResourceModel\Entity\Attribute\Option\Collection $optionCollection */ + /** @var Collection $optionCollection */ $optionCollection = $this->objectManager->create( - \Magento\Eav\Model\ResourceModel\Entity\Attribute\Option\Collection::class + Collection::class ); $options = $optionCollection->setAttributeFilter($this->configurableAttribute->getId())->getData(); return $options; } + /** + * Create configurable product by web api + * + * @return array + */ protected function createConfigurableProduct() { $productId1 = 10; @@ -254,7 +268,6 @@ public function testUpdateConfigurableProductLinks() $this->assertEquals([$productId1], $resultConfigurableProductLinks); //adding back the product links, the option value should be restored - unset($response[ExtensibleDataInterface::EXTENSION_ATTRIBUTES_KEY]['configurable_product_options']); $response[ExtensibleDataInterface::EXTENSION_ATTRIBUTES_KEY]['configurable_product_links'] = [$productId1, $productId2]; //set the value for required attribute @@ -286,7 +299,7 @@ public function testUpdateConfigurableProductLinksWithNonExistingProduct() $productId1, $nonExistingId ]; - $expectedMessage = 'The product was unable to be saved. Please try again.'; + $expectedMessage = 'The product that was requested doesn\'t exist. Verify the product and try again.'; try { $this->saveProduct($response); $this->fail("Expected exception"); @@ -362,7 +375,7 @@ public function testUpdateConfigurableProductLinksWithWithoutVariationAttributes $productId1, $productId2 ]; - $expectedMessage = 'The product was unable to be saved. Please try again.'; + $expectedMessage = 'The product that was requested doesn\'t exist. Verify the product and try again.'; try { $this->saveProduct($response); $this->fail("Expected exception"); @@ -389,7 +402,7 @@ protected function getProduct($productSku) $serviceInfo = [ 'rest' => [ 'resourcePath' => self::RESOURCE_PATH . '/' . $productSku, - 'httpMethod' => \Magento\Framework\Webapi\Rest\Request::HTTP_METHOD_GET, + 'httpMethod' => Request::HTTP_METHOD_GET, ], 'soap' => [ 'service' => self::SERVICE_NAME, @@ -415,7 +428,7 @@ protected function createProduct($product) $serviceInfo = [ 'rest' => [ 'resourcePath' => self::RESOURCE_PATH, - 'httpMethod' => \Magento\Framework\Webapi\Rest\Request::HTTP_METHOD_POST + 'httpMethod' => Request::HTTP_METHOD_POST ], 'soap' => [ 'service' => self::SERVICE_NAME, @@ -440,7 +453,7 @@ protected function deleteProductBySku($productSku) $serviceInfo = [ 'rest' => [ 'resourcePath' => $resourcePath, - 'httpMethod' => \Magento\Framework\Webapi\Rest\Request::HTTP_METHOD_DELETE + 'httpMethod' => Request::HTTP_METHOD_DELETE ], 'soap' => [ 'service' => self::SERVICE_NAME, @@ -475,7 +488,7 @@ protected function saveProduct($product) $serviceInfo = [ 'rest' => [ 'resourcePath' => $resourcePath, - 'httpMethod' => \Magento\Framework\Webapi\Rest\Request::HTTP_METHOD_PUT + 'httpMethod' => Request::HTTP_METHOD_PUT ], 'soap' => [ 'service' => self::SERVICE_NAME, diff --git a/dev/tests/api-functional/testsuite/Magento/Customer/Api/CustomerRepositoryTest.php b/dev/tests/api-functional/testsuite/Magento/Customer/Api/CustomerRepositoryTest.php index 7a02e2f843719..8ee23a1efea44 100644 --- a/dev/tests/api-functional/testsuite/Magento/Customer/Api/CustomerRepositoryTest.php +++ b/dev/tests/api-functional/testsuite/Magento/Customer/Api/CustomerRepositoryTest.php @@ -8,6 +8,8 @@ use Magento\Customer\Api\Data\CustomerInterface as Customer; use Magento\Customer\Api\Data\AddressInterface as Address; +use Magento\Framework\Api\FilterBuilder; +use Magento\Framework\Api\SearchCriteriaInterface; use Magento\Framework\Api\SortOrder; use Magento\Framework\Exception\InputException; use Magento\Framework\Exception\LocalizedException; @@ -482,11 +484,17 @@ public function testCreateCustomerWithoutAddressRequiresException() /** * Test with a single filter + * + * @param bool $subscribeStatus + * @return void + * + * @dataProvider subscriptionDataProvider */ - public function testSearchCustomers() + public function testSearchCustomers(bool $subscribeStatus): void { - $builder = Bootstrap::getObjectManager()->create(\Magento\Framework\Api\FilterBuilder::class); - $customerData = $this->_createCustomer(); + $builder = Bootstrap::getObjectManager()->create(FilterBuilder::class); + $subscribeData = $this->buildSubscriptionData($subscribeStatus); + $customerData = $this->_createCustomer($subscribeData); $filter = $builder ->setField(Customer::EMAIL) ->setValue($customerData[Customer::EMAIL]) @@ -494,13 +502,13 @@ public function testSearchCustomers() $this->searchCriteriaBuilder->addFilters([$filter]); $searchData = $this->dataObjectProcessor->buildOutputDataArray( $this->searchCriteriaBuilder->create(), - \Magento\Framework\Api\SearchCriteriaInterface::class + SearchCriteriaInterface::class ); $requestData = ['searchCriteria' => $searchData]; $serviceInfo = [ 'rest' => [ 'resourcePath' => self::RESOURCE_PATH . '/search' . '?' . http_build_query($requestData), - 'httpMethod' => \Magento\Framework\Webapi\Rest\Request::HTTP_METHOD_GET, + 'httpMethod' => Request::HTTP_METHOD_GET, ], 'soap' => [ 'service' => self::SERVICE_NAME, @@ -511,6 +519,35 @@ public function testSearchCustomers() $searchResults = $this->_webApiCall($serviceInfo, $requestData); $this->assertEquals(1, $searchResults['total_count']); $this->assertEquals($customerData[Customer::ID], $searchResults['items'][0][Customer::ID]); + $this->assertEquals($subscribeStatus, $searchResults['items'][0]['extension_attributes']['is_subscribed']); + } + + /** + * Build subscription extension attributes data + * + * @param bool $status + * @return array + */ + private function buildSubscriptionData(bool $status): array + { + return [ + 'extension_attributes' => [ + 'is_subscribed' => $status, + ], + ]; + } + + /** + * Subscription customer data provider + * + * @return array + */ + public function subscriptionDataProvider(): array + { + return [ + 'subscribed user' => [true], + 'not subscribed user' => [false], + ]; } /** @@ -857,11 +894,12 @@ protected function _getCustomerData($customerId) } /** + * @param array|null $additionalData * @return array|bool|float|int|string */ - protected function _createCustomer() + protected function _createCustomer(?array $additionalData = []) { - $customerData = $this->customerHelper->createSampleCustomer(); + $customerData = $this->customerHelper->createSampleCustomer($additionalData); $this->currentCustomerId[] = $customerData['id']; return $customerData; } diff --git a/dev/tests/api-functional/testsuite/Magento/GiftMessage/Api/OrderGetRepositoryTest.php b/dev/tests/api-functional/testsuite/Magento/GiftMessage/Api/OrderGetRepositoryTest.php new file mode 100644 index 0000000000000..91d1954581da7 --- /dev/null +++ b/dev/tests/api-functional/testsuite/Magento/GiftMessage/Api/OrderGetRepositoryTest.php @@ -0,0 +1,54 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +declare(strict_types=1); + +namespace Magento\GiftMessage\Api; + +use Magento\TestFramework\TestCase\WebapiAbstract; + +class OrderGetRepositoryTest extends WebapiAbstract +{ + const SERVICE_VERSION = 'V1'; + const SERVICE_NAME = 'salesOrderRepositoryV1'; + const RESOURCE_PATH = '/V1/orders/'; + + /** + * @magentoDataFixture Magento/GiftMessage/_files/order_with_message.php + * @magentoConfigFixture default_store sales/gift_options/allow_order 1 + * @magentoAppIsolation enabled + * @magentoDbIsolation disabled + */ + public function testGet() + { + $objectManager = \Magento\TestFramework\Helper\Bootstrap::getObjectManager(); + /** @var \Magento\Sales\Model\Order $order */ + $order = $objectManager->create(\Magento\Sales\Model\Order::class)->loadByIncrementId('100000001'); + $orderId = $order->getId(); + $serviceInfo = [ + 'rest' => [ + 'resourcePath' => self::RESOURCE_PATH . $orderId, + 'httpMethod' => \Magento\Framework\Webapi\Rest\Request::HTTP_METHOD_GET, + ], + 'soap' => [ + 'service' => self::SERVICE_NAME, + 'serviceVersion' => self::SERVICE_VERSION, + 'operation' => self::SERVICE_NAME . 'Get', + ], + ]; + $expectedMessage = [ + 'recipient' => 'Mercutio', + 'sender' => 'Romeo', + 'message' => 'I thought all for the best.', + ]; + $requestData = ['id' => $orderId]; + $result = $this->_webApiCall($serviceInfo, $requestData); + $resultMessage = $result['extension_attributes']['gift_message']; + static::assertCount(5, $resultMessage); + unset($resultMessage['gift_message_id']); + unset($resultMessage['customer_id']); + static::assertEquals($expectedMessage, $resultMessage); + } +} diff --git a/dev/tests/api-functional/testsuite/Magento/GiftMessage/Api/OrderItemGetRepositoryTest.php b/dev/tests/api-functional/testsuite/Magento/GiftMessage/Api/OrderItemGetRepositoryTest.php new file mode 100644 index 0000000000000..074133835f6da --- /dev/null +++ b/dev/tests/api-functional/testsuite/Magento/GiftMessage/Api/OrderItemGetRepositoryTest.php @@ -0,0 +1,57 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +declare(strict_types=1); + +namespace Magento\GiftMessage\Api; + +use Magento\TestFramework\TestCase\WebapiAbstract; + +class OrderItemGetRepositoryTest extends WebapiAbstract +{ + const SERVICE_VERSION = 'V1'; + const SERVICE_NAME = 'salesOrderItemRepositoryV1'; + const RESOURCE_PATH = '/V1/orders/items/'; + + /** + * @magentoDataFixture Magento/GiftMessage/_files/order_with_message.php + * @magentoConfigFixture default_store sales/gift_options/allow_items 1 + * @magentoAppIsolation enabled + * @magentoDbIsolation disabled + */ + public function testGet() + { + $objectManager = \Magento\TestFramework\Helper\Bootstrap::getObjectManager(); + /** @var \Magento\Sales\Model\Order $order */ + $order = $objectManager->create(\Magento\Sales\Model\Order::class)->loadByIncrementId('100000001'); + $items = $order->getItems(); + /** @var \Magento\Sales\Api\Data\OrderItemInterface $orderItem */ + $orderItem = array_shift($items); + $itemId = $orderItem->getItemId(); + $serviceInfo = [ + 'rest' => [ + 'resourcePath' => self::RESOURCE_PATH . $itemId, + 'httpMethod' => \Magento\Framework\Webapi\Rest\Request::HTTP_METHOD_GET, + ], + 'soap' => [ + 'service' => self::SERVICE_NAME, + 'serviceVersion' => self::SERVICE_VERSION, + 'operation' => self::SERVICE_NAME . 'Get', + ], + ]; + $expectedMessage = [ + 'recipient' => 'Mercutio', + 'sender' => 'Romeo', + 'message' => 'I thought all for the best.', + ]; + $requestData = ['id' => $itemId]; + $result = $this->_webApiCall($serviceInfo, $requestData); + $resultMessage = $result['extension_attributes']['gift_message']; + static::assertCount(5, $resultMessage); + unset($resultMessage['gift_message_id']); + unset($resultMessage['customer_id']); + static::assertEquals($expectedMessage, $resultMessage); + } +} diff --git a/dev/tests/api-functional/testsuite/Magento/GraphQl/AuthorizenetAcceptjs/Customer/SetPaymentMethodTest.php b/dev/tests/api-functional/testsuite/Magento/GraphQl/AuthorizenetAcceptjs/Customer/SetPaymentMethodTest.php deleted file mode 100644 index e9ab4456fae81..0000000000000 --- a/dev/tests/api-functional/testsuite/Magento/GraphQl/AuthorizenetAcceptjs/Customer/SetPaymentMethodTest.php +++ /dev/null @@ -1,409 +0,0 @@ -<?php -/** - * Copyright © Magento, Inc. All rights reserved. - * See COPYING.txt for license details. - */ -declare(strict_types=1); - -namespace Magento\GraphQl\AuthorizenetAcceptjs\Customer; - -use Magento\Framework\Registry; -use Magento\GraphQl\Quote\GetMaskedQuoteIdByReservedOrderId; -use Magento\Integration\Api\CustomerTokenServiceInterface; -use Magento\Sales\Api\OrderRepositoryInterface; -use Magento\Sales\Model\ResourceModel\Order\CollectionFactory; -use Magento\TestFramework\Helper\Bootstrap; -use Magento\TestFramework\TestCase\GraphQlAbstract; - -/** - * Test setting payment method and placing order with AuthorizenetAcceptjs - */ -class SetPaymentMethodTest extends GraphQlAbstract -{ - private const VALID_DESCRIPTOR = 'COMMON.ACCEPT.INAPP.PAYMENT'; - private const VALID_NONCE = 'fake-nonce'; - - /** - * @var CustomerTokenServiceInterface - */ - private $customerTokenService; - - /** - * @var GetMaskedQuoteIdByReservedOrderId - */ - private $getMaskedQuoteIdByReservedOrderId; - - /** - * @var CollectionFactory - */ - private $orderCollectionFactory; - - /** - * @var OrderRepositoryInterface - */ - private $orderRepository; - - /** - * @var Registry - */ - private $registry; - - /** - * @inheritdoc - */ - protected function setUp() - { - $objectManager = Bootstrap::getObjectManager(); - $this->getMaskedQuoteIdByReservedOrderId = $objectManager->get(GetMaskedQuoteIdByReservedOrderId::class); - $this->customerTokenService = $objectManager->get(CustomerTokenServiceInterface::class); - $this->orderCollectionFactory = $objectManager->get(CollectionFactory::class); - $this->orderRepository = $objectManager->get(OrderRepositoryInterface::class); - $this->registry = Bootstrap::getObjectManager()->get(Registry::class); - } - - /** - * @magentoApiDataFixture Magento/Customer/_files/customer.php - * @magentoApiDataFixture Magento/GraphQl/Catalog/_files/simple_product.php - * @magentoApiDataFixture Magento/GraphQl/Quote/_files/customer/create_empty_cart.php - * @magentoApiDataFixture Magento/GraphQl/Quote/_files/add_simple_product.php - * @magentoApiDataFixture Magento/GraphQl/Quote/_files/set_new_shipping_address.php - * @magentoApiDataFixture Magento/GraphQl/Quote/_files/set_new_billing_address.php - * @magentoApiDataFixture Magento/GraphQl/Quote/_files/set_flatrate_shipping_method.php - * @magentoApiDataFixture Magento/GraphQl/AuthorizenetAcceptjs/_files/enable_authorizenetacceptjs.php - * @magentoConfigFixture default_store carriers/flatrate/active 1 - * @magentoConfigFixture default_store carriers/tablerate/active 1 - * @magentoConfigFixture default_store carriers/freeshipping/active 1 - * @param string $nonce - * @param string $descriptor - * @param bool $expectSuccess - * @dataProvider dataProviderTestPlaceOrder - */ - public function testPlaceOrder(string $nonce, string $descriptor, bool $expectSuccess) - { - $reservedOrderId = 'test_quote'; - $maskedQuoteId = $this->getMaskedQuoteIdByReservedOrderId->execute($reservedOrderId); - - $setPaymentMutation = $this->getSetPaymentMutation($maskedQuoteId, $descriptor, $nonce); - $setPaymentResponse = $this->graphQlMutation($setPaymentMutation, [], '', $this->getHeaderMap()); - - $this->assertSetPaymentMethodResponse($setPaymentResponse, 'authorizenet_acceptjs'); - - $placeOrderQuery = $this->getPlaceOrderMutation($maskedQuoteId); - - if (!$expectSuccess) { - $this->expectException(\Exception::class); - $this->expectExceptionMessage('Transaction has been declined. Please try again later.'); - } - - $placeOrderResponse = $this->graphQlMutation($placeOrderQuery, [], '', $this->getHeaderMap()); - - $this->assertPlaceOrderResponse($placeOrderResponse, $reservedOrderId); - } - - public function dataProviderTestPlaceOrder(): array - { - return [ - [static::VALID_NONCE, static::VALID_DESCRIPTOR, true], - ['nonce', static::VALID_DESCRIPTOR, false], - [static::VALID_NONCE, 'descriptor', false], - ]; - } - - /** - * @magentoConfigFixture default_store carriers/flatrate/active 1 - * @magentoConfigFixture default_store payment/authorizenet_acceptjs/active 1 - * @magentoConfigFixture default_store payment/authorizenet_acceptjs/environment sandbox - * @magentoConfigFixture default_store payment/authorizenet_acceptjs/login def_login - * @magentoConfigFixture default_store payment/authorizenet_acceptjs/trans_key def_trans_key - * @magentoConfigFixture default_store payment/authorizenet_acceptjs/public_client_key def_public_client_key - * @magentoConfigFixture default_store payment/authorizenet_acceptjs/trans_signature_key def_trans_signature_key - * @magentoApiDataFixture Magento/Customer/_files/customer.php - * @magentoApiDataFixture Magento/GraphQl/Catalog/_files/simple_product.php - * @magentoApiDataFixture Magento/GraphQl/Quote/_files/customer/create_empty_cart.php - * @magentoApiDataFixture Magento/GraphQl/Quote/_files/add_simple_product.php - * @magentoApiDataFixture Magento/GraphQl/Quote/_files/set_new_shipping_address.php - * @magentoApiDataFixture Magento/GraphQl/Quote/_files/set_new_billing_address.php - * @magentoApiDataFixture Magento/GraphQl/Quote/_files/set_flatrate_shipping_method.php - * @dataProvider dataProviderSetPaymentInvalidInput - * @param \Closure $getMutationClosure - * @param array $expectedMessages - * @expectedException \Exception - */ - public function testSetPaymentInvalidInput(\Closure $getMutationClosure, array $expectedMessages) - { - $reservedOrderId = 'test_quote'; - $maskedQuoteId = $this->getMaskedQuoteIdByReservedOrderId->execute($reservedOrderId); - - $setPaymentMutation = $getMutationClosure($maskedQuoteId); - - foreach ($expectedMessages as $expectedMessage) { - $this->expectExceptionMessage($expectedMessage); - } - $this->graphQlMutation($setPaymentMutation, [], '', $this->getHeaderMap()); - } - - /** - * Data provider for testSetPaymentInvalidInput - * - * @return array - */ - public function dataProviderSetPaymentInvalidInput(): array - { - return [ - [ - function (string $maskedQuoteId) { - return $this->getInvalidSetPaymentMutation($maskedQuoteId); - }, - [ - 'Required parameter "authorizenet_acceptjs" for "payment_method" is missing.' - ] - ], - [ - function (string $maskedQuoteId) { - return $this->getEmptyAcceptJsInput($maskedQuoteId); - }, - [ - 'Field AuthorizenetInput.cc_last_4 of required type Int! was not provided.', - 'Field AuthorizenetInput.opaque_data_descriptor of required type String! was not provided.', - 'Field AuthorizenetInput.opaque_data_value of required type String! was not provided.' - ] - - ], - [ - function (string $maskedQuoteId) { - return $this->getMissingCcLastFourAcceptJsInput( - $maskedQuoteId, - static::VALID_DESCRIPTOR, - static::VALID_NONCE - ); - }, - [ - 'Field AuthorizenetInput.cc_last_4 of required type Int! was not provided', - ] - ], - [ - function (string $maskedQuoteId) { - return $this->getMissingOpaqueDataValueAcceptJsInput($maskedQuoteId, static::VALID_DESCRIPTOR); - }, - [ - 'Field AuthorizenetInput.opaque_data_value of required type String! was not provided', - ] - ], - ]; - } - - /** - * Get setPaymentMethodOnCart missing additional data property - * - * @param string $maskedQuoteId - * @return string - */ - private function getInvalidSetPaymentMutation(string $maskedQuoteId): string - { - return <<<QUERY -mutation { - setPaymentMethodOnCart(input:{ - cart_id:"{$maskedQuoteId}" - payment_method:{ - code:"authorizenet_acceptjs" - } - }) { - cart { - selected_payment_method { - code - } - } - } -} -QUERY; - } - - /** - * Get setPaymentMethodOnCart missing required additional data properties - * - * @param string $maskedQuoteId - * @return string - */ - private function getEmptyAcceptJsInput(string $maskedQuoteId): string - { - return <<<QUERY -mutation { - setPaymentMethodOnCart(input:{ - cart_id:"{$maskedQuoteId}" - payment_method:{ - code:"authorizenet_acceptjs" - authorizenet_acceptjs: {} - } - }) { - cart { - selected_payment_method { - code - } - } - } -} -QUERY; - } - - /** - * Get setPaymentMethodOnCart missing required additional data properties - * - * @param string $maskedQuoteId - * @return string - */ - private function getMissingCcLastFourAcceptJsInput(string $maskedQuoteId, string $descriptor, string $nonce): string - { - return <<<QUERY -mutation { - setPaymentMethodOnCart(input:{ - cart_id:"{$maskedQuoteId}" - payment_method:{ - code:"authorizenet_acceptjs" - authorizenet_acceptjs:{ - opaque_data_descriptor: "{$descriptor}" - opaque_data_value: "{$nonce}" - } - } - }) { - cart { - selected_payment_method { - code - } - } - } -} -QUERY; - } - - /** - * Get setPaymentMethodOnCart missing required additional data properties - * - * @param string $maskedQuoteId - * @return string - */ - private function getMissingOpaqueDataValueAcceptJsInput(string $maskedQuoteId, string $descriptor): string - { - return <<<QUERY -mutation { - setPaymentMethodOnCart(input:{ - cart_id:"{$maskedQuoteId}" - payment_method:{ - code:"authorizenet_acceptjs" - authorizenet_acceptjs:{ - opaque_data_descriptor: "{$descriptor}" - cc_last_4: 1111 - } - } - }) { - cart { - selected_payment_method { - code - } - } - } -} -QUERY; - } - - private function assertPlaceOrderResponse(array $response, string $reservedOrderId): void - { - self::assertArrayHasKey('placeOrder', $response); - self::assertArrayHasKey('order', $response['placeOrder']); - self::assertArrayHasKey('order_number', $response['placeOrder']['order']); - self::assertEquals($reservedOrderId, $response['placeOrder']['order']['order_number']); - } - - private function assertSetPaymentMethodResponse(array $response, string $methodCode): void - { - self::assertArrayHasKey('setPaymentMethodOnCart', $response); - self::assertArrayHasKey('cart', $response['setPaymentMethodOnCart']); - self::assertArrayHasKey('selected_payment_method', $response['setPaymentMethodOnCart']['cart']); - self::assertArrayHasKey('code', $response['setPaymentMethodOnCart']['cart']['selected_payment_method']); - self::assertEquals($methodCode, $response['setPaymentMethodOnCart']['cart']['selected_payment_method']['code']); - } - - /** - * Create setPaymentMethodOnCart mutation - * - * @param string $maskedQuoteId - * @param string $descriptor - * @param string $nonce - * @return string - */ - private function getSetPaymentMutation(string $maskedQuoteId, string $descriptor, string $nonce): string - { - return <<<QUERY -mutation { - setPaymentMethodOnCart(input:{ - cart_id:"{$maskedQuoteId}" - payment_method:{ - code:"authorizenet_acceptjs" - authorizenet_acceptjs:{ - opaque_data_descriptor: "{$descriptor}" - opaque_data_value: "{$nonce}" - cc_last_4: 1111 - } - } - }) { - cart { - selected_payment_method { - code - } - } - } -} -QUERY; - } - - /** - * Create placeOrder mutation - * - * @param string $maskedQuoteId - * @return string - */ - private function getPlaceOrderMutation(string $maskedQuoteId): string - { - return <<<QUERY -mutation { - placeOrder(input: {cart_id: "{$maskedQuoteId}"}) { - order { - order_number - } - } -} -QUERY; - } - - /** - * Get authorization headers for requests - * - * @param string $username - * @param string $password - * @return array - * @throws \Magento\Framework\Exception\AuthenticationException - */ - private function getHeaderMap(string $username = 'customer@example.com', string $password = 'password'): array - { - $customerToken = $this->customerTokenService->createCustomerAccessToken($username, $password); - $headerMap = ['Authorization' => 'Bearer ' . $customerToken]; - return $headerMap; - } - - /** - * @inheritdoc - */ - public function tearDown() - { - $this->registry->unregister('isSecureArea'); - $this->registry->register('isSecureArea', true); - - $orderCollection = $this->orderCollectionFactory->create(); - foreach ($orderCollection as $order) { - $this->orderRepository->delete($order); - } - $this->registry->unregister('isSecureArea'); - $this->registry->register('isSecureArea', false); - - parent::tearDown(); - } -} diff --git a/dev/tests/api-functional/testsuite/Magento/GraphQl/AuthorizenetAcceptjs/Guest/SetPaymentMethodTest.php b/dev/tests/api-functional/testsuite/Magento/GraphQl/AuthorizenetAcceptjs/Guest/SetPaymentMethodTest.php deleted file mode 100644 index 322d984f5fa75..0000000000000 --- a/dev/tests/api-functional/testsuite/Magento/GraphQl/AuthorizenetAcceptjs/Guest/SetPaymentMethodTest.php +++ /dev/null @@ -1,198 +0,0 @@ -<?php -/** - * Copyright © Magento, Inc. All rights reserved. - * See COPYING.txt for license details. - */ -declare(strict_types=1); - -namespace Magento\GraphQl\AuthorizenetAcceptjs\Guest; - -use Magento\Framework\Registry; -use Magento\GraphQl\Quote\GetMaskedQuoteIdByReservedOrderId; -use Magento\Integration\Api\CustomerTokenServiceInterface; -use Magento\Sales\Api\OrderRepositoryInterface; -use Magento\Sales\Model\ResourceModel\Order\CollectionFactory; -use Magento\TestFramework\Helper\Bootstrap; -use Magento\TestFramework\TestCase\GraphQlAbstract; - -/** - * Test setting payment method and placing order with AuthorizenetAcceptjs - */ -class SetPaymentMethodTest extends GraphQlAbstract -{ - private const VALID_DESCRIPTOR = 'COMMON.ACCEPT.INAPP.PAYMENT'; - private const VALID_NONCE = 'fake-nonce'; - - /** - * @var CustomerTokenServiceInterface - */ - private $customerTokenService; - - /** - * @var GetMaskedQuoteIdByReservedOrderId - */ - private $getMaskedQuoteIdByReservedOrderId; - - /** - * @var CollectionFactory - */ - private $orderCollectionFactory; - - /** - * @var OrderRepositoryInterface - */ - private $orderRepository; - - /** - * @var Registry - */ - private $registry; - - /** - * @inheritdoc - */ - protected function setUp() - { - $objectManager = Bootstrap::getObjectManager(); - $this->getMaskedQuoteIdByReservedOrderId = $objectManager->get(GetMaskedQuoteIdByReservedOrderId::class); - $this->customerTokenService = $objectManager->get(CustomerTokenServiceInterface::class); - $this->orderCollectionFactory = $objectManager->get(CollectionFactory::class); - $this->orderRepository = $objectManager->get(OrderRepositoryInterface::class); - $this->registry = Bootstrap::getObjectManager()->get(Registry::class); - } - - /** - * @magentoApiDataFixture Magento/GraphQl/Catalog/_files/simple_product.php - * @magentoApiDataFixture Magento/GraphQl/Quote/_files/guest/create_empty_cart.php - * @magentoApiDataFixture Magento/GraphQl/Quote/_files/guest/set_guest_email.php - * @magentoApiDataFixture Magento/GraphQl/Quote/_files/add_simple_product.php - * @magentoApiDataFixture Magento/GraphQl/Quote/_files/set_new_shipping_address.php - * @magentoApiDataFixture Magento/GraphQl/Quote/_files/set_new_billing_address.php - * @magentoApiDataFixture Magento/GraphQl/Quote/_files/set_flatrate_shipping_method.php - * @magentoApiDataFixture Magento/GraphQl/AuthorizenetAcceptjs/_files/enable_authorizenetacceptjs.php - * @magentoConfigFixture default_store carriers/flatrate/active 1 - * @magentoConfigFixture default_store carriers/tablerate/active 1 - * @magentoConfigFixture default_store carriers/freeshipping/active 1 - * @param string $nonce - * @param string $descriptor - * @param bool $expectSuccess - * @dataProvider dataProviderTestPlaceOrder - */ - public function testPlaceOrder(string $nonce, string $descriptor, bool $expectSuccess) - { - $reservedOrderId = 'test_quote'; - $maskedQuoteId = $this->getMaskedQuoteIdByReservedOrderId->execute($reservedOrderId); - - $setPaymentMutation = $this->getSetPaymentMutation($maskedQuoteId, $descriptor, $nonce); - $setPaymentResponse = $this->graphQlMutation($setPaymentMutation); - - $this->assertSetPaymentMethodResponse($setPaymentResponse, 'authorizenet_acceptjs'); - - $placeOrderQuery = $this->getPlaceOrderMutation($maskedQuoteId); - - if (!$expectSuccess) { - $this->expectException(\Exception::class); - $this->expectExceptionMessage('Transaction has been declined. Please try again later.'); - } - - $placeOrderResponse = $this->graphQlMutation($placeOrderQuery); - - $this->assertPlaceOrderResponse($placeOrderResponse, $reservedOrderId); - } - - public function dataProviderTestPlaceOrder(): array - { - return [ - [static::VALID_NONCE, static::VALID_DESCRIPTOR, true], - ['nonce', static::VALID_DESCRIPTOR, false], - [static::VALID_NONCE, 'descriptor', false], - ]; - } - - private function assertPlaceOrderResponse(array $response, string $reservedOrderId): void - { - self::assertArrayHasKey('placeOrder', $response); - self::assertArrayHasKey('order', $response['placeOrder']); - self::assertArrayHasKey('order_number', $response['placeOrder']['order']); - self::assertEquals($reservedOrderId, $response['placeOrder']['order']['order_number']); - } - - private function assertSetPaymentMethodResponse(array $response, string $methodCode): void - { - self::assertArrayHasKey('setPaymentMethodOnCart', $response); - self::assertArrayHasKey('cart', $response['setPaymentMethodOnCart']); - self::assertArrayHasKey('selected_payment_method', $response['setPaymentMethodOnCart']['cart']); - self::assertArrayHasKey('code', $response['setPaymentMethodOnCart']['cart']['selected_payment_method']); - self::assertEquals($methodCode, $response['setPaymentMethodOnCart']['cart']['selected_payment_method']['code']); - } - - /** - * Create setPaymentMethodOnCart mutation - * - * @param string $maskedQuoteId - * @param string $descriptor - * @param string $nonce - * @return string - */ - private function getSetPaymentMutation(string $maskedQuoteId, string $descriptor, string $nonce): string - { - return <<<QUERY -mutation { - setPaymentMethodOnCart(input:{ - cart_id:"{$maskedQuoteId}" - payment_method:{ - code:"authorizenet_acceptjs" - authorizenet_acceptjs:{ - opaque_data_descriptor: "{$descriptor}" - opaque_data_value: "{$nonce}" - cc_last_4: 1111 - } - } - }) { - cart { - selected_payment_method { - code - } - } - } -} -QUERY; - } - - /** - * Create placeOrder mutation - * - * @param string $maskedQuoteId - * @return string - */ - private function getPlaceOrderMutation(string $maskedQuoteId): string - { - return <<<QUERY -mutation { - placeOrder(input: {cart_id: "{$maskedQuoteId}"}) { - order { - order_number - } - } -} -QUERY; - } - - /** - * @inheritdoc - */ - public function tearDown() - { - $this->registry->unregister('isSecureArea'); - $this->registry->register('isSecureArea', true); - - $orderCollection = $this->orderCollectionFactory->create(); - foreach ($orderCollection as $order) { - $this->orderRepository->delete($order); - } - $this->registry->unregister('isSecureArea'); - $this->registry->register('isSecureArea', false); - - parent::tearDown(); - } -} diff --git a/dev/tests/api-functional/testsuite/Magento/GraphQl/Braintree/Customer/SetPaymentMethodTest.php b/dev/tests/api-functional/testsuite/Magento/GraphQl/Braintree/Customer/SetPaymentMethodTest.php index a282b295c2974..a36a4f5d38223 100644 --- a/dev/tests/api-functional/testsuite/Magento/GraphQl/Braintree/Customer/SetPaymentMethodTest.php +++ b/dev/tests/api-functional/testsuite/Magento/GraphQl/Braintree/Customer/SetPaymentMethodTest.php @@ -270,35 +270,6 @@ public function testSetPaymentMethodInvalidMethodInput(string $methodCode) $this->graphQlMutation($setPaymentQuery, [], '', $this->getHeaderMap()); } - /** - * @magentoConfigFixture default_store carriers/flatrate/active 1 - * @magentoConfigFixture default_store payment/braintree/active 1 - * @magentoConfigFixture default_store payment/braintree_cc_vault/active 1 - * @magentoConfigFixture default_store payment/braintree/environment sandbox - * @magentoConfigFixture default_store payment/braintree/merchant_id def_merchant_id - * @magentoConfigFixture default_store payment/braintree/public_key def_public_key - * @magentoConfigFixture default_store payment/braintree/private_key def_private_key - * @magentoApiDataFixture Magento/Customer/_files/customer.php - * @magentoApiDataFixture Magento/GraphQl/Catalog/_files/simple_product.php - * @magentoApiDataFixture Magento/GraphQl/Quote/_files/customer/create_empty_cart.php - * @magentoApiDataFixture Magento/GraphQl/Quote/_files/add_simple_product.php - * @magentoApiDataFixture Magento/GraphQl/Quote/_files/set_new_shipping_address.php - * @magentoApiDataFixture Magento/GraphQl/Quote/_files/set_new_billing_address.php - * @magentoApiDataFixture Magento/GraphQl/Quote/_files/set_flatrate_shipping_method.php - * @expectedException \Exception - */ - public function testSetPaymentMethodWithoutRequiredPaymentMethodInput() - { - $reservedOrderId = 'test_quote'; - $maskedQuoteId = $this->getMaskedQuoteIdByReservedOrderId->execute($reservedOrderId); - - $setPaymentQuery = $this->getSetPaymentBraintreeQueryInvalidPaymentMethodInput($maskedQuoteId); - $this->expectExceptionMessage( - 'Field BraintreeInput.is_active_payment_token_enabler of required type Boolean! was not provided.' - ); - $this->graphQlMutation($setPaymentQuery, [], '', $this->getHeaderMap()); - } - public function dataProviderTestSetPaymentMethodInvalidInput(): array { return [ @@ -409,33 +380,6 @@ private function getSetPaymentBraintreeQueryInvalidInput(string $maskedQuoteId, QUERY; } - /** - * @param string $maskedQuoteId - * @return string - */ - private function getSetPaymentBraintreeQueryInvalidPaymentMethodInput(string $maskedQuoteId): string - { - return <<<QUERY -mutation { - setPaymentMethodOnCart(input:{ - cart_id:"{$maskedQuoteId}" - payment_method:{ - code:"braintree" - braintree:{ - payment_method_nonce:"fake-valid-nonce" - } - } - }) { - cart { - selected_payment_method { - code - } - } - } -} -QUERY; - } - /** * @param string $maskedQuoteId * @param string $methodCode diff --git a/dev/tests/api-functional/testsuite/Magento/GraphQl/Braintree/Guest/SetPaymentMethodTest.php b/dev/tests/api-functional/testsuite/Magento/GraphQl/Braintree/Guest/SetPaymentMethodTest.php index c0a7491cbc1bf..5376634c05146 100644 --- a/dev/tests/api-functional/testsuite/Magento/GraphQl/Braintree/Guest/SetPaymentMethodTest.php +++ b/dev/tests/api-functional/testsuite/Magento/GraphQl/Braintree/Guest/SetPaymentMethodTest.php @@ -129,39 +129,6 @@ public function testSetPaymentMethodInvalidInput() $this->graphQlMutation($setPaymentQuery); } - /** - * @magentoConfigFixture default_store carriers/flatrate/active 1 - * @magentoConfigFixture default_store payment/braintree/active 1 - * @magentoConfigFixture default_store payment/braintree/environment sandbox - * @magentoConfigFixture default_store payment/braintree/merchant_id def_merchant_id - * @magentoConfigFixture default_store payment/braintree/public_key def_public_key - * @magentoConfigFixture default_store payment/braintree/private_key def_private_key - * @magentoApiDataFixture Magento/GraphQl/Catalog/_files/simple_product.php - * @magentoApiDataFixture Magento/GraphQl/Quote/_files/guest/create_empty_cart.php - * @magentoApiDataFixture Magento/GraphQl/Quote/_files/guest/set_guest_email.php - * @magentoApiDataFixture Magento/GraphQl/Quote/_files/add_simple_product.php - * @magentoApiDataFixture Magento/GraphQl/Quote/_files/set_new_shipping_address.php - * @magentoApiDataFixture Magento/GraphQl/Quote/_files/set_new_billing_address.php - * @magentoApiDataFixture Magento/GraphQl/Quote/_files/set_flatrate_shipping_method.php - * @expectedException \Exception - */ - public function testSetPaymentMethodInvalidMethodInput() - { - $reservedOrderId = 'test_quote'; - $maskedQuoteId = $this->getMaskedQuoteIdByReservedOrderId->execute($reservedOrderId); - - $setPaymentQuery = $this->getSetPaymentBraintreeQueryInvalidMethodInput($maskedQuoteId); - - $this->expectExceptionMessage( - 'Field BraintreeInput.is_active_payment_token_enabler of required type Boolean! was not provided' - ); - $this->expectExceptionMessage( - 'Field BraintreeInput.payment_method_nonce of required type String! was not provided.' - ); - - $this->graphQlMutation($setPaymentQuery); - } - private function assertPlaceOrderResponse(array $response, string $reservedOrderId): void { self::assertArrayHasKey('placeOrder', $response); @@ -231,31 +198,6 @@ private function getSetPaymentBraintreeQueryInvalidInput(string $maskedQuoteId): QUERY; } - /** - * @param string $maskedQuoteId - * @return string - */ - private function getSetPaymentBraintreeQueryInvalidMethodInput(string $maskedQuoteId): string - { - return <<<QUERY -mutation { - setPaymentMethodOnCart(input:{ - cart_id:"{$maskedQuoteId}" - payment_method:{ - code:"braintree" - braintree: {} - } - }) { - cart { - selected_payment_method { - code - } - } - } -} -QUERY; - } - /** * @param string $maskedQuoteId * @return string diff --git a/dev/tests/api-functional/testsuite/Magento/GraphQl/Bundle/BundleProductViewTest.php b/dev/tests/api-functional/testsuite/Magento/GraphQl/Bundle/BundleProductViewTest.php index 5d08078cf7646..4c58241590540 100644 --- a/dev/tests/api-functional/testsuite/Magento/GraphQl/Bundle/BundleProductViewTest.php +++ b/dev/tests/api-functional/testsuite/Magento/GraphQl/Bundle/BundleProductViewTest.php @@ -10,13 +10,12 @@ use Magento\Bundle\Model\Product\OptionList; use Magento\Catalog\Api\Data\ProductInterface; use Magento\Catalog\Api\ProductRepositoryInterface; -use Magento\Framework\EntityManager\MetadataPool; use Magento\Framework\App\Config\ScopeConfigInterface; use Magento\TestFramework\ObjectManager; use Magento\TestFramework\TestCase\GraphQlAbstract; /** - * Bundle product view test + * Test querying Bundle products */ class BundleProductViewTest extends GraphQlAbstract { @@ -83,12 +82,7 @@ public function testAllFieldsBundleProducts() /** @var ProductRepositoryInterface $productRepository */ $productRepository = ObjectManager::getInstance()->get(ProductRepositoryInterface::class); - /** @var MetadataPool $metadataPool */ - $metadataPool = ObjectManager::getInstance()->get(MetadataPool::class); $bundleProduct = $productRepository->get($productSku, false, null, true); - $bundleProduct->setId( - $bundleProduct->getData($metadataPool->getMetadata(ProductInterface::class)->getLinkField()) - ); if ((bool)$bundleProduct->getShipmentType()) { $this->assertEquals('SEPARATELY', $response['products']['items'][0]['ship_bundle_items']); } else { @@ -182,12 +176,7 @@ public function testBundleProductWithNotVisibleChildren() /** @var ProductRepositoryInterface $productRepository */ $productRepository = ObjectManager::getInstance()->get(ProductRepositoryInterface::class); - /** @var MetadataPool $metadataPool */ - $metadataPool = ObjectManager::getInstance()->get(MetadataPool::class); $bundleProduct = $productRepository->get($productSku, false, null, true); - $bundleProduct->setId( - $bundleProduct->getData($metadataPool->getMetadata(ProductInterface::class)->getLinkField()) - ); if ((bool)$bundleProduct->getShipmentType()) { $this->assertEquals('SEPARATELY', $response['products']['items'][0]['ship_bundle_items']); } else { @@ -238,7 +227,6 @@ private function assertBundleProductOptions($product, $actualResponse) $actualResponse['items'], "Precondition failed: 'bundle product items' must not be empty" ); - $metadataPool = ObjectManager::getInstance()->get(MetadataPool::class); /** @var OptionList $optionList */ $optionList = ObjectManager::getInstance()->get(\Magento\Bundle\Model\Product\OptionList::class); $options = $optionList->getItems($product); @@ -249,10 +237,6 @@ private function assertBundleProductOptions($product, $actualResponse) $childProductSku = $bundleProductLink->getSku(); $productRepository = ObjectManager::getInstance()->get(ProductRepositoryInterface::class); $childProduct = $productRepository->get($childProductSku); - /** @var MetadataPool $metadataPool */ - $childProduct->setId( - $childProduct->getData($metadataPool->getMetadata(ProductInterface::class)->getLinkField()) - ); $this->assertEquals(1, count($options)); $this->assertResponseFields( $actualResponse['items'][0], diff --git a/dev/tests/api-functional/testsuite/Magento/GraphQl/Catalog/CategoriesQuery/CategoriesFilterTest.php b/dev/tests/api-functional/testsuite/Magento/GraphQl/Catalog/CategoriesQuery/CategoriesFilterTest.php new file mode 100644 index 0000000000000..2bea5832126e8 --- /dev/null +++ b/dev/tests/api-functional/testsuite/Magento/GraphQl/Catalog/CategoriesQuery/CategoriesFilterTest.php @@ -0,0 +1,735 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +declare(strict_types=1); + +namespace Magento\GraphQl\Catalog\CategoriesQuery; + +use Magento\Catalog\Model\ResourceModel\Category\Collection as CategoryCollection; +use Magento\Store\Model\StoreManagerInterface; +use Magento\TestFramework\Helper\Bootstrap; +use Magento\TestFramework\TestCase\GraphQlAbstract; + +/** + * Test categories query filtering works as expected + */ +class CategoriesFilterTest extends GraphQlAbstract +{ + /** + * @magentoApiDataFixture Magento/Catalog/_files/categories.php + * @dataProvider filterSingleCategoryDataProvider + * @param string $field + * @param string $condition + * @param string $value + * @param array $expectedResult + */ + public function testFilterSingleCategoryByField($field, $condition, $value, $expectedResult) + { + $query = <<<QUERY +{ + categories(filters: { $field : { $condition : "$value" } }){ + items{ + id + name + url_key + url_path + children_count + path + position + } + } +} +QUERY; + $result = $this->graphQlQuery($query); + $this->assertArrayNotHasKey('errors', $result); + $this->assertCount(1, $result['categories']['items']); + $this->assertResponseFields($result['categories']['items'][0], $expectedResult); + } + + /** + * @magentoApiDataFixture Magento/Catalog/_files/categories.php + * @dataProvider filterMultipleCategoriesDataProvider + * @param $field + * @param $condition + * @param $value + * @param $expectedResult + */ + public function testFilterMultipleCategoriesByField($field, $condition, $value, $expectedResult) + { + $query = <<<QUERY +{ + categories(filters: { $field : { $condition : $value } }){ + items{ + id + name + url_key + url_path + children_count + path + position + } + } +} +QUERY; + $result = $this->graphQlQuery($query); + $this->assertArrayNotHasKey('errors', $result); + $this->assertCount(count($expectedResult), $result['categories']['items']); + foreach ($expectedResult as $i => $expected) { + $this->assertResponseFields($result['categories']['items'][$i], $expected); + } + } + + /** + * @magentoApiDataFixture Magento/Catalog/_files/categories.php + */ + public function testFilterCategoryByMultipleFields() + { + $query = <<<QUERY +{ + categories(filters: {ids: {in: ["6","7","8","9","10"]}, name: {match: "Movable"}}){ + total_count + items{ + id + name + url_key + url_path + children_count + path + position + } + } +} +QUERY; + $result = $this->graphQlQuery($query); + $this->assertArrayNotHasKey('errors', $result); + $this->assertCount(3, $result['categories']['items']); + $this->assertEquals(3, $result['categories']['total_count']); + + $expectedCategories = [7 => 'Movable', 9 => 'Movable Position 1', 10 => 'Movable Position 2']; + $actualCategories = array_column($result['categories']['items'], 'name', 'id'); + $this->assertEquals($expectedCategories, $actualCategories); + } + + /** + * @magentoApiDataFixture Magento/Catalog/_files/categories.php + */ + public function testFilterWithInactiveCategory() + { + $query = <<<QUERY +{ + categories(filters: {url_key: {in: ["inactive", "category-2"]}}){ + items{ + id + name + url_key + url_path + } + } +} +QUERY; + $result = $this->graphQlQuery($query); + $this->assertArrayNotHasKey('errors', $result); + $this->assertCount(1, $result['categories']['items']); + $actualCategories = array_column($result['categories']['items'], 'url_key', 'id'); + $this->assertContains('category-2', $actualCategories); + $this->assertNotContains('inactive', $actualCategories); + } + + /** + * @magentoApiDataFixture Magento/Catalog/_files/categories.php + */ + public function testQueryChildCategoriesWithProducts() + { + $query = <<<QUERY +{ + categories(filters: {ids: {in: ["3"]}}){ + items{ + id + name + url_key + url_path + description + products{ + total_count + items{ + name + sku + } + } + children{ + name + url_key + description + products{ + total_count + items{ + name + sku + } + } + children{ + name + } + } + } + } +} +QUERY; + $result = $this->graphQlQuery($query); + + $this->assertArrayNotHasKey('errors', $result); + $this->assertCount(1, $result['categories']['items']); + $baseCategory = $result['categories']['items'][0]; + + $this->assertEquals('Category 1', $baseCategory['name']); + $this->assertArrayHasKey('products', $baseCategory); + //Check base category products + $expectedBaseCategoryProducts = [ + ['sku' => 'simple', 'name' => 'Simple Product'], + ['sku' => 'simple-4', 'name' => 'Simple Product Three'], + ['sku' => '12345', 'name' => 'Simple Product Two'] + ]; + $this->assertCategoryProducts($baseCategory, $expectedBaseCategoryProducts); + //Check base category children + $expectedBaseCategoryChildren = [ + ['name' => 'Category 1.1', 'description' => 'Category 1.1 description.'], + ['name' => 'Category 1.2', 'description' => 'Its a description of Test Category 1.2'] + ]; + $this->assertCategoryChildren($baseCategory, $expectedBaseCategoryChildren); + + //Check first child category + $firstChildCategory = $baseCategory['children'][0]; + $this->assertEquals('Category 1.1', $firstChildCategory['name']); + $this->assertEquals('Category 1.1 description.', $firstChildCategory['description']); + $firstChildCategoryExpectedProducts = [ + ['sku' => '12345', 'name' => 'Simple Product Two'], + ['sku' => 'simple', 'name' => 'Simple Product'] + ]; + $this->assertCategoryProducts($firstChildCategory, $firstChildCategoryExpectedProducts); + $firstChildCategoryChildren = [['name' =>'Category 1.1.1']]; + $this->assertCategoryChildren($firstChildCategory, $firstChildCategoryChildren); + //Check second child category + $secondChildCategory = $baseCategory['children'][1]; + $this->assertEquals('Category 1.2', $secondChildCategory['name']); + $this->assertEquals('Its a description of Test Category 1.2', $secondChildCategory['description']); + $firstChildCategoryExpectedProducts = [ + ['sku' => 'simple-4', 'name' => 'Simple Product Three'], + ['sku' => 'simple', 'name' => 'Simple Product'] + ]; + $this->assertCategoryProducts($secondChildCategory, $firstChildCategoryExpectedProducts); + $firstChildCategoryChildren = []; + $this->assertCategoryChildren($secondChildCategory, $firstChildCategoryChildren); + } + + /** + * @magentoApiDataFixture Magento/Catalog/_files/categories_disabled.php + */ + public function testQueryCategoryWithDisabledChildren() + { + $query = <<<QUERY +{ + categories(filters: {ids: {in: ["3"]}}){ + items{ + id + name + image + url_key + url_path + description + products{ + total_count + items{ + name + sku + } + } + children{ + name + image + url_key + description + products{ + total_count + items{ + name + sku + } + } + children{ + name + image + children{ + name + image + } + } + } + } + } +} +QUERY; + $result = $this->graphQlQuery($query); + + $this->assertArrayNotHasKey('errors', $result); + $this->assertCount(1, $result['categories']['items']); + $baseCategory = $result['categories']['items'][0]; + + $this->assertEquals('Category 1', $baseCategory['name']); + $this->assertArrayHasKey('products', $baseCategory); + //Check base category products + $expectedBaseCategoryProducts = [ + ['sku' => 'simple', 'name' => 'Simple Product'], + ['sku' => 'simple-4', 'name' => 'Simple Product Three'], + ['sku' => '12345', 'name' => 'Simple Product Two'] + ]; + $this->assertCategoryProducts($baseCategory, $expectedBaseCategoryProducts); + //Check base category children + $expectedBaseCategoryChildren = [ + ['name' => 'Category 1.2', 'description' => 'Its a description of Test Category 1.2'] + ]; + $this->assertCategoryChildren($baseCategory, $expectedBaseCategoryChildren); + + //Check first child category + $firstChildCategory = $baseCategory['children'][0]; + $this->assertEquals('Category 1.2', $firstChildCategory['name']); + $this->assertEquals('Its a description of Test Category 1.2', $firstChildCategory['description']); + + $firstChildCategoryExpectedProducts = [ + ['sku' => 'simple-4', 'name' => 'Simple Product Three'], + ['sku' => 'simple', 'name' => 'Simple Product'] + ]; + $this->assertCategoryProducts($firstChildCategory, $firstChildCategoryExpectedProducts); + $firstChildCategoryChildren = []; + $this->assertCategoryChildren($firstChildCategory, $firstChildCategoryChildren); + } + + /** + * @magentoApiDataFixture Magento/Catalog/_files/categories.php + */ + public function testNoResultsFound() + { + $query = <<<QUERY +{ + categories(filters: {url_key: {in: ["inactive", "does-not-exist"]}}){ + items{ + id + name + url_key + url_path + children_count + path + position + } + } +} +QUERY; + $result = $this->graphQlQuery($query); + $this->assertArrayNotHasKey('errors', $result); + $this->assertArrayHasKey('categories', $result); + $this->assertEquals([], $result['categories']['items']); + } + + /** + * When no filters are supplied, the root category is returned + * + * @magentoApiDataFixture Magento/Catalog/_files/categories.php + */ + public function testEmptyFiltersReturnRootCategory() + { + $query = <<<QUERY +{ + categories{ + items{ + id + name + url_key + url_path + children_count + path + position + } + } +} +QUERY; + $storeManager = Bootstrap::getObjectManager()->get(StoreManagerInterface::class); + $storeRootCategoryId = $storeManager->getStore()->getRootCategoryId(); + + $result = $this->graphQlQuery($query); + $this->assertArrayNotHasKey('errors', $result); + $this->assertArrayHasKey('categories', $result); + $this->assertEquals('Default Category', $result['categories']['items'][0]['name']); + $this->assertEquals($storeRootCategoryId, $result['categories']['items'][0]['id']); + } + + /** + * Filtering with match value less than minimum query should return empty result + * + * @magentoApiDataFixture Magento/Catalog/_files/categories.php + * @expectedException \Exception + * @expectedExceptionMessage Invalid match filter. Minimum length is 3. + */ + public function testMinimumMatchQueryLength() + { + $query = <<<QUERY +{ + categories(filters: {name: {match: "mo"}}){ + items{ + id + name + url_key + url_path + children_count + path + position + } + } +} +QUERY; + $this->graphQlQuery($query); + } + + /** + * Test category image full name is returned + * + * @magentoApiDataFixture Magento/Catalog/_files/catalog_category_with_long_image_name.php + */ + public function testCategoryImageName() + { + /** @var CategoryCollection $categoryCollection */ + $categoryCollection = Bootstrap::getObjectManager()->get(CategoryCollection::class); + $categoryModel = $categoryCollection + ->addAttributeToSelect('image') + ->addAttributeToFilter('name', ['eq' => 'Parent Image Category']) + ->getFirstItem(); + $categoryId = $categoryModel->getId(); + + $query = <<<QUERY +{ + categories(filters: {ids: {in: ["$categoryId"]}}) { + items{ + id + name + image + } + } +} +QUERY; + $storeManager = Bootstrap::getObjectManager()->get(StoreManagerInterface::class); + $storeBaseUrl = $storeManager->getStore()->getBaseUrl('media'); + + $expected = "catalog/category/magento_long_image_name_magento_long_image_name_magento_long_image_name.jpg"; + $expectedImageUrl = rtrim($storeBaseUrl, '/') . '/' . $expected; + + $response = $this->graphQlQuery($query); + $categories = $response['categories']; + $this->assertArrayNotHasKey('errors', $response); + $this->assertNotEmpty($response['categories']['items']); + $expectedImageUrl = str_replace('index.php/', '', $expectedImageUrl); + $categories['items'][0]['image'] = str_replace('index.php/', '', $categories['items'][0]['image']); + $this->assertEquals('Parent Image Category', $categories['items'][0]['name']); + $this->assertEquals($expectedImageUrl, $categories['items'][0]['image']); + } + + /** + * @magentoApiDataFixture Magento/Catalog/_files/categories.php + */ + public function testFilterByUrlPathTopLevelCategory() + { + $urlPath = 'category-1'; + $query = <<<QUERY +{ + categories(filters: {url_path: {eq: "$urlPath"}}){ + items{ + id + name + url_key + url_path + path + position + } + } +} +QUERY; + + $response = $this->graphQlQuery($query); + $this->assertArrayNotHasKey('errors', $response); + $categories = $response['categories']; + $this->assertCount(1, $categories); + $this->assertEquals($urlPath, $categories['items'][0]['url_path']); + $this->assertEquals('Category 1', $categories['items'][0]['name']); + } + + /** + * @magentoApiDataFixture Magento/Catalog/_files/categories.php + */ + public function testFilterByUrlPathNestedCategory() + { + $urlPath = 'category-1/category-1-1/category-1-1-1'; + $query = <<<QUERY +{ + categories(filters: {url_path: {eq: "$urlPath"}}){ + items{ + id + name + url_key + url_path + path + position + } + } +} +QUERY; + + $response = $this->graphQlQuery($query); + $this->assertArrayNotHasKey('errors', $response); + $categories = $response['categories']; + $this->assertCount(1, $categories); + $this->assertEquals($urlPath, $categories['items'][0]['url_path']); + $this->assertEquals('Category 1.1.1', $categories['items'][0]['name']); + } + + /** + * @magentoApiDataFixture Magento/Catalog/_files/categories.php + */ + public function testFilterByUrlPathMultipleCategories() + { + $urlPaths = ['category-1/category-1-1', 'inactive', 'movable-position-2']; + $urlPathsString = '"' . implode('", "', $urlPaths) . '"'; + $query = <<<QUERY +{ + categories(filters: {url_path: {in: [$urlPathsString]}}){ + items{ + id + name + url_key + url_path + path + position + } + } +} +QUERY; + + $response = $this->graphQlQuery($query); + $this->assertArrayNotHasKey('errors', $response); + $categories = $response['categories']; + $this->assertCount(2, $categories['items']); + $this->assertEquals($urlPaths[0], $categories['items'][0]['url_path']); + $this->assertEquals('Category 1.1', $categories['items'][0]['name']); + $this->assertEquals($urlPaths[2], $categories['items'][1]['url_path']); + $this->assertEquals('Movable Position 2', $categories['items'][1]['name']); + } + + /** + * @magentoApiDataFixture Magento/Catalog/_files/categories.php + */ + public function testFilterByUrlPathNoResults() + { + $query = <<<QUERY +{ + categories(filters: {url_path: {in: ["not-a-category url path"]}}){ + items{ + id + name + url_key + url_path + path + position + } + } +} +QUERY; + + $response = $this->graphQlQuery($query); + $this->assertArrayNotHasKey('errors', $response); + $categories = $response['categories']; + $this->assertCount(0, $categories['items']); + } + + /** + * @return array + */ + public function filterSingleCategoryDataProvider(): array + { + return [ + [ + 'ids', + 'eq', + '4', + [ + 'id' => '4', + 'name' => 'Category 1.1', + 'url_key' => 'category-1-1', + 'url_path' => 'category-1/category-1-1', + 'children_count' => '0', + 'path' => '1/2/3/4', + 'position' => '1' + ] + ], + [ + 'name', + 'match', + 'Movable Position 2', + [ + 'id' => '10', + 'name' => 'Movable Position 2', + 'url_key' => 'movable-position-2', + 'url_path' => 'movable-position-2', + 'children_count' => '0', + 'path' => '1/2/10', + 'position' => '6' + ] + ], + [ + 'url_key', + 'eq', + 'category-1-1-1', + [ + 'id' => '5', + 'name' => 'Category 1.1.1', + 'url_key' => 'category-1-1-1', + 'url_path' => 'category-1/category-1-1/category-1-1-1', + 'children_count' => '0', + 'path' => '1/2/3/4/5', + 'position' => '1' + ] + ], + ]; + } + + /** + * @SuppressWarnings(PHPMD.ExcessiveMethodLength) + * @return array + */ + public function filterMultipleCategoriesDataProvider(): array + { + return[ + //Filter by multiple IDs + [ + 'ids', + 'in', + '["4", "9", "10"]', + [ + [ + 'id' => '4', + 'name' => 'Category 1.1', + 'url_key' => 'category-1-1', + 'url_path' => 'category-1/category-1-1', + 'children_count' => '0', + 'path' => '1/2/3/4', + 'position' => '1' + ], + [ + 'id' => '9', + 'name' => 'Movable Position 1', + 'url_key' => 'movable-position-1', + 'url_path' => 'movable-position-1', + 'children_count' => '0', + 'path' => '1/2/9', + 'position' => '5' + ], + [ + 'id' => '10', + 'name' => 'Movable Position 2', + 'url_key' => 'movable-position-2', + 'url_path' => 'movable-position-2', + 'children_count' => '0', + 'path' => '1/2/10', + 'position' => '6' + ] + ] + ], + //Filter by multiple url keys + [ + 'url_key', + 'in', + '["category-1-2", "movable"]', + [ + [ + 'id' => '7', + 'name' => 'Movable', + 'url_key' => 'movable', + 'url_path' => 'movable', + 'children_count' => '0', + 'path' => '1/2/7', + 'position' => '3' + ], + [ + 'id' => '13', + 'name' => 'Category 1.2', + 'url_key' => 'category-1-2', + 'url_path' => 'category-1/category-1-2', + 'children_count' => '0', + 'path' => '1/2/3/13', + 'position' => '2' + ] + ] + ], + //Filter by matching multiple names + [ + 'name', + 'match', + '"Position"', + [ + [ + 'id' => '9', + 'name' => 'Movable Position 1', + 'url_key' => 'movable-position-1', + 'url_path' => 'movable-position-1', + 'children_count' => '0', + 'path' => '1/2/9', + 'position' => '5' + ], + [ + 'id' => '10', + 'name' => 'Movable Position 2', + 'url_key' => 'movable-position-2', + 'url_path' => 'movable-position-2', + 'children_count' => '0', + 'path' => '1/2/10', + 'position' => '6' + ], + [ + 'id' => '11', + 'name' => 'Movable Position 3', + 'url_key' => 'movable-position-3', + 'url_path' => 'movable-position-3', + 'children_count' => '0', + 'path' => '1/2/11', + 'position' => '7' + ] + ] + ] + ]; + } + + /** + * Check category products + * + * @param array $category + * @param array $expectedProducts + */ + private function assertCategoryProducts(array $category, array $expectedProducts) + { + $this->assertEquals(count($expectedProducts), $category['products']['total_count']); + $this->assertCount(count($expectedProducts), $category['products']['items']); + $this->assertResponseFields($category['products']['items'], $expectedProducts); + } + + /** + * Check category child categories + * + * @param array $category + * @param array $expectedChildren + */ + private function assertCategoryChildren(array $category, array $expectedChildren) + { + $this->assertArrayHasKey('children', $category); + $this->assertCount(count($expectedChildren), $category['children']); + foreach ($expectedChildren as $i => $expectedChild) { + $this->assertResponseFields($category['children'][$i], $expectedChild); + } + } +} diff --git a/dev/tests/api-functional/testsuite/Magento/GraphQl/Catalog/CategoriesQuery/CategoriesPaginationTest.php b/dev/tests/api-functional/testsuite/Magento/GraphQl/Catalog/CategoriesQuery/CategoriesPaginationTest.php new file mode 100644 index 0000000000000..3d6982757b932 --- /dev/null +++ b/dev/tests/api-functional/testsuite/Magento/GraphQl/Catalog/CategoriesQuery/CategoriesPaginationTest.php @@ -0,0 +1,243 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +declare(strict_types=1); + +namespace Magento\GraphQl\Catalog\CategoriesQuery; + +use Magento\TestFramework\TestCase\GraphQlAbstract; + +/** + * Test pagination for the categories query + */ +class CategoriesPaginationTest extends GraphQlAbstract +{ + /** + * @magentoApiDataFixture Magento/Catalog/_files/categories.php + */ + public function testDefaultPagination() + { + $query = <<<QUERY +{ + categories(filters: {ids: {in: ["3", "4", "5", "6", "7", "8", "9"]}}) { + total_count + page_info { + current_page + page_size + total_pages + } + items { + name + } + } +} +QUERY; + + $response = $this->graphQlQuery($query); + $categories = $response['categories']['items']; + $this->assertCount(6, $categories); + $this->assertEquals(count($categories), $response['categories']['total_count']); + $this->assertArrayHasKey('page_info', $response['categories']); + $pageInfo = $response['categories']['page_info']; + $this->assertEquals(1, $pageInfo['current_page']); + $this->assertEquals(20, $pageInfo['page_size']); + $this->assertEquals(1, $pageInfo['total_pages']); + } + + /** + * @magentoApiDataFixture Magento/Catalog/_files/categories.php + */ + public function testPageSize() + { + $query = <<<QUERY +{ + categories( + filters: {ids: {in: ["3", "4", "5", "6", "7", "8", "9"]}} + pageSize: 2 + ) { + total_count + page_info { + current_page + page_size + total_pages + } + items { + name + } + } +} +QUERY; + + $response = $this->graphQlQuery($query); + $categories = $response['categories']['items']; + $this->assertCount(2, $categories); + $this->assertNotEquals(count($categories), $response['categories']['total_count']); + $this->assertEquals(6, $response['categories']['total_count']); + $pageInfo = $response['categories']['page_info']; + $this->assertEquals(1, $pageInfo['current_page']); + $this->assertEquals(2, $pageInfo['page_size']); + $this->assertEquals(3, $pageInfo['total_pages']); + } + + /** + * @magentoApiDataFixture Magento/Catalog/_files/categories.php + */ + public function testCurrentPage() + { + $query = <<<QUERY +{ + categories( + filters: {name: {match: "Category"}} + pageSize: 3 + currentPage: 3 + ) { + total_count + page_info { + current_page + page_size + total_pages + } + items { + name + } + } +} +QUERY; + + $response = $this->graphQlQuery($query); + $categories = $response['categories']['items']; + $this->assertCount(1, $categories); + $this->assertEquals(7, $response['categories']['total_count']); + $pageInfo = $response['categories']['page_info']; + $this->assertEquals(3, $pageInfo['current_page']); + $this->assertEquals(3, $pageInfo['page_size']); + $this->assertEquals(3, $pageInfo['total_pages']); + } + + /** + * @magentoApiDataFixture Magento/Catalog/_files/categories.php + */ + public function testPaging() + { + $baseQuery = <<<QUERY +{ + categories( + filters: {name: {match: "Category"}} + pageSize: 2 + currentPage: %s + ) { + total_count + page_info { + current_page + page_size + total_pages + } + items { + name + } + } +} +QUERY; + + $page1Query = sprintf($baseQuery, 1); + $page1Result = $this->graphQlQuery($page1Query); + $this->assertEquals('Default Category', $page1Result['categories']['items'][0]['name']); + $this->assertEquals('Category 1', $page1Result['categories']['items'][1]['name']); + $this->assertEquals(7, $page1Result['categories']['total_count']); + + $page2Query = sprintf($baseQuery, 2); + $page2Result = $this->graphQlQuery($page2Query); + $this->assertEquals('Category 1.1', $page2Result['categories']['items'][0]['name']); + $this->assertEquals('Category 1.1.1', $page2Result['categories']['items'][1]['name']); + + $lastPageQuery = sprintf($baseQuery, $page1Result['categories']['page_info']['total_pages']); + $lastPageResult = $this->graphQlQuery($lastPageQuery); + $this->assertCount(1, $lastPageResult['categories']['items']); + $this->assertEquals('Category 1.2', $lastPageResult['categories']['items'][0]['name']); + } + + /** + * @expectedException \Exception + * @expectedExceptionMessage currentPage value must be greater than 0. + */ + public function testCurrentPageZero() + { + $query = <<<QUERY +{ + categories( + filters: {name: {match: "Category"}} + currentPage: 0 + ) { + total_count + page_info { + current_page + page_size + total_pages + } + items { + name + } + } +} +QUERY; + $this->graphQlQuery($query); + } + + /** + * @expectedException \Exception + * @expectedExceptionMessage pageSize value must be greater than 0. + */ + public function testPageSizeZero() + { + $query = <<<QUERY +{ + categories( + filters: {name: {match: "Category"}} + pageSize: 0 + ) { + total_count + page_info { + current_page + page_size + total_pages + } + items { + name + } + } +} +QUERY; + $this->graphQlQuery($query); + } + + /** + * @magentoApiDataFixture Magento/Catalog/_files/categories.php + * @expectedException \Exception + * @expectedExceptionMessage currentPage value 6 specified is greater than the 2 page(s) available. + */ + public function testCurrentPageTooLarge() + { + $query = <<<QUERY +{ + categories( + filters: {url_key: {in: ["category-1", "category-1-1", "category-1-1-1"]}} + pageSize: 2 + currentPage: 6 + ) { + total_count + page_info { + current_page + page_size + total_pages + } + items { + name + } + } +} +QUERY; + $this->graphQlQuery($query); + } +} diff --git a/dev/tests/api-functional/testsuite/Magento/GraphQl/Catalog/CategoriesQuery/CategoryTreeTest.php b/dev/tests/api-functional/testsuite/Magento/GraphQl/Catalog/CategoriesQuery/CategoryTreeTest.php new file mode 100644 index 0000000000000..96b303c689683 --- /dev/null +++ b/dev/tests/api-functional/testsuite/Magento/GraphQl/Catalog/CategoriesQuery/CategoryTreeTest.php @@ -0,0 +1,741 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +declare(strict_types=1); + +namespace Magento\GraphQl\Catalog\CategoriesQuery; + +use Magento\Catalog\Api\Data\ProductInterface; +use Magento\Catalog\Api\ProductRepositoryInterface; +use Magento\Catalog\Model\CategoryRepository; +use Magento\Catalog\Model\ResourceModel\Category\Collection as CategoryCollection; +use Magento\Framework\EntityManager\MetadataPool; +use Magento\Store\Model\Store; +use Magento\Store\Model\StoreManagerInterface; +use Magento\TestFramework\ObjectManager; +use Magento\TestFramework\TestCase\GraphQlAbstract; + +/** + * Test category tree data is returned correctly from "categories" query + */ +class CategoryTreeTest extends GraphQlAbstract +{ + /** + * @var ObjectManager + */ + private $objectManager; + + /** + * @var CategoryRepository + */ + private $categoryRepository; + + /** + * @var Store + */ + private $store; + + /** + * @var MetadataPool + */ + private $metadataPool; + + protected function setUp() + { + $this->objectManager = \Magento\TestFramework\Helper\Bootstrap::getObjectManager(); + $this->categoryRepository = $this->objectManager->get(CategoryRepository::class); + $this->store = $this->objectManager->get(Store::class); + $this->metadataPool = $this->objectManager->get(MetadataPool::class); + } + + /** + * @magentoApiDataFixture Magento/Catalog/_files/categories.php + * @SuppressWarnings(PHPMD.ExcessiveMethodLength) + */ + public function testCategoriesTree() + { + $rootCategoryId = 2; + $query = <<<QUERY +{ + categories(filters: {ids: {eq: "{$rootCategoryId}"}}) { + items{ + id + level + description + path + path_in_store + product_count + url_key + url_path + children { + id + description + available_sort_by + default_sort_by + image + level + children { + id + filter_price_range + description + image + meta_keywords + level + is_anchor + children { + level + id + } + } + } + } + } +} +QUERY; + $response = $this->graphQlQuery($query); + $this->assertArrayNotHasKey('errors', $response); + $this->assertArrayHasKey('categories', $response); + $baseCategory = $response['categories']['items'][0]; + $this->assertEquals( + 'Its a description of Test Category 1.2', + $baseCategory['children'][0]['children'][1]['description'] + ); + $this->assertEquals('default-category', $baseCategory['url_key']); + $this->assertEquals([], $baseCategory['children'][0]['available_sort_by']); + $this->assertEquals('name', $baseCategory['children'][0]['default_sort_by']); + $this->assertCount(7, $baseCategory['children']); + $this->assertCount(2, $baseCategory['children'][0]['children']); + $this->assertEquals(13, $baseCategory['children'][0]['children'][1]['id']); + } + + /** + * @magentoApiDataFixture Magento/Catalog/_files/categories.php + * @SuppressWarnings(PHPMD.ExcessiveMethodLength) + */ + public function testRootCategoryTree() + { + $query = <<<QUERY +{ + categories { + items{ + id + level + description + path + path_in_store + product_count + url_key + url_path + children { + id + description + available_sort_by + default_sort_by + image + level + children { + id + filter_price_range + description + image + meta_keywords + level + is_anchor + children { + level + id + } + } + } + } + } +} +QUERY; + $response = $this->graphQlQuery($query); + $this->assertArrayNotHasKey('errors', $response); + $this->assertArrayHasKey('categories', $response); + $baseCategory = $response['categories']['items'][0]; + $this->assertEquals( + 'Its a description of Test Category 1.2', + $baseCategory['children'][0]['children'][1]['description'] + ); + $this->assertEquals('default-category', $baseCategory['url_key']); + $this->assertEquals([], $baseCategory['children'][0]['available_sort_by']); + $this->assertEquals('name', $baseCategory['children'][0]['default_sort_by']); + $this->assertCount(7, $baseCategory['children']); + $this->assertCount(2, $baseCategory['children'][0]['children']); + $this->assertEquals(13, $baseCategory['children'][0]['children'][1]['id']); + } + + /** + * @magentoApiDataFixture Magento/Catalog/_files/categories.php + */ + public function testCategoriesTreeWithDisabledCategory() + { + $category = $this->categoryRepository->get(3); + $category->setIsActive(false); + $this->categoryRepository->save($category); + + $rootCategoryId = 2; + $query = <<<QUERY +{ + categories(filters: {ids: {eq: "{$rootCategoryId}"}}) { + items{ + id + name + level + description + children { + id + name + productImagePreview: products(pageSize: 1) { + items { + id + } + } + } + } + } +} +QUERY; + $response = $this->graphQlQuery($query); + + $this->assertArrayHasKey('categories', $response); + $this->assertArrayHasKey('children', $response['categories']['items'][0]); + $this->assertSame(6, count($response['categories']['items'][0]['children'])); + } + + /** + * @magentoApiDataFixture Magento/Catalog/_files/categories.php + */ + public function testGetCategoryById() + { + $categoryId = 13; + $query = <<<QUERY +{ + categories(filters: {ids: {eq: "{$categoryId}"}}) { + items{ + id + name + } + } +} +QUERY; + $response = $this->graphQlQuery($query); + $this->assertEquals('Category 1.2', $response['categories']['items'][0]['name']); + $this->assertEquals(13, $response['categories']['items'][0]['id']); + } + + /** + * @magentoApiDataFixture Magento/Catalog/_files/categories.php + */ + public function testGetDisabledCategory() + { + $categoryId = 8; + $query = <<<QUERY +{ + categories(filters: {ids: {eq: "{$categoryId}"}}) { + items{ + id + name + } + } +} +QUERY; + $response = $this->graphQlQuery($query); + $this->assertArrayHasKey('items', $response['categories']); + $this->assertEquals([], $response['categories']['items']); + } + + /** + * @magentoApiDataFixture Magento/Catalog/_files/categories.php + */ + public function testGetCategoryIdZero() + { + $query = <<<QUERY +{ + categories(filters: {ids: {eq: "0"}}) { + items{ + id + name + } + } +} +QUERY; + $response = $this->graphQlQuery($query); + $this->assertArrayHasKey('items', $response['categories']); + $this->assertEquals([], $response['categories']['items']); + } + + public function testNonExistentCategoryWithProductCount() + { + $query = <<<QUERY +{ + categories(filters: {ids: {eq: "99"}}) { + total_count + items{ + product_count + } + } +} +QUERY; + + $response = $this->graphQlQuery($query); + $this->assertArrayHasKey('items', $response['categories']); + $this->assertEquals([], $response['categories']['items']); + $this->assertEquals(0, $response['categories']['total_count']); + } + + /** + * @magentoApiDataFixture Magento/Catalog/_files/categories.php + * @SuppressWarnings(PHPMD.ExcessiveMethodLength) + */ + public function testCategoryProducts() + { + $categoryId = 2; + $query = <<<QUERY +{ + categories(filters: {ids: {eq: "{$categoryId}"}}) { + items{ + products { + total_count + page_info { + current_page + page_size + } + items { + attribute_set_id + country_of_manufacture + created_at + description { + html + } + gift_message_available + id + categories { + name + url_path + available_sort_by + level + } + image { url, label } + meta_description + meta_keyword + meta_title + media_gallery_entries { + disabled + file + id + label + media_type + position + types + content { + base64_encoded_data + type + name + } + video_content { + media_type + video_description + video_metadata + video_provider + video_title + video_url + } + } + name + new_from_date + new_to_date + options_container + price { + minimalPrice { + amount { + value + currency + } + adjustments { + amount { + value + currency + } + code + description + } + } + maximalPrice { + amount { + value + currency + } + adjustments { + amount { + value + currency + } + code + description + } + } + regularPrice { + amount { + value + currency + } + adjustments { + amount { + value + currency + } + code + description + } + } + } + product_links { + link_type + linked_product_sku + linked_product_type + position + sku + } + short_description { + html + } + sku + small_image { url, label } + thumbnail { url, label } + special_from_date + special_price + special_to_date + swatch_image + tier_price + tier_prices { + customer_group_id + percentage_value + qty + value + website_id + } + type_id + updated_at + url_key + url_path + websites { + id + name + code + sort_order + default_group_id + is_default + } + } + } + } + } +} +QUERY; + $response = $this->graphQlQuery($query); + $this->assertArrayHasKey('products', $response['categories']['items'][0]); + $baseCategory = $response['categories']['items'][0]; + $this->assertArrayHasKey('total_count', $baseCategory['products']); + $this->assertGreaterThanOrEqual(1, $baseCategory['products']['total_count']); + $this->assertEquals(1, $baseCategory['products']['page_info']['current_page']); + $this->assertEquals(20, $baseCategory['products']['page_info']['page_size']); + $this->assertArrayHasKey('sku', $baseCategory['products']['items'][0]); + $firstProduct = $baseCategory['products']['items'][0]; + /** @var ProductRepositoryInterface $productRepository */ + $productRepository = ObjectManager::getInstance()->get(ProductRepositoryInterface::class); + $firstProductModel = $productRepository->get($firstProduct['sku'], false, null, true); + $this->assertBaseFields($firstProductModel, $firstProduct); + $this->assertAttributes($firstProduct); + $this->assertWebsites($firstProductModel, $firstProduct['websites']); + $this->assertEquals('Category 1', $firstProduct['categories'][0]['name']); + $this->assertEquals('category-1/category-1-1', $firstProduct['categories'][1]['url_path']); + $this->assertCount(3, $firstProduct['categories']); + } + + /** + * @magentoApiDataFixture Magento/Catalog/_files/categories.php + */ + public function testAnchorCategory() + { + $query = <<<QUERY +{ + categories(filters: {url_key: {eq: "category-1"}}) { + items { + name + products(sort: {name: DESC}) { + total_count + items { + sku + } + } + } + } +} +QUERY; + $response = $this->graphQlQuery($query); + $expectedResponse = [ + 'categories' => [ + 'items' => [ + 0 => [ + 'name' => 'Category 1', + 'products' => [ + 'total_count' => 3, + 'items' => [ + ['sku' => '12345'], + ['sku' => 'simple-4'], + ['sku' => 'simple'] + ] + ] + ] + ] + ] + ]; + $this->assertEquals($expectedResponse, $response); + } + + /** + * @magentoApiDataFixture Magento/Catalog/_files/categories.php + */ + public function testBreadCrumbs() + { + $query = <<<QUERY +{ + categories(filters: {url_key: {eq: "category-1-1-1"}}) { + items{ + name + breadcrumbs { + category_id + category_name + category_level + category_url_key + category_url_path + } + } + } +} +QUERY; + $response = $this->graphQlQuery($query); + $expectedResponse = [ + 'categories' => [ + 'items' => [ + 0 => [ + 'name' => 'Category 1.1.1', + 'breadcrumbs' => [ + [ + 'category_id' => 3, + 'category_name' => "Category 1", + 'category_level' => 2, + 'category_url_key' => "category-1", + 'category_url_path' => "category-1" + ], + [ + 'category_id' => 4, + 'category_name' => "Category 1.1", + 'category_level' => 3, + 'category_url_key' => "category-1-1", + 'category_url_path' => "category-1/category-1-1" + ], + ] + ] + ] + ] + ]; + $this->assertEquals($expectedResponse, $response); + } + + /** + * Test category image is returned as full url (not relative path) + * + * @param string $imagePrefix + * @magentoApiDataFixture Magento/Catalog/_files/catalog_category_with_image.php + * @dataProvider categoryImageDataProvider + */ + public function testCategoryImage(?string $imagePrefix) + { + /** @var CategoryCollection $categoryCollection */ + $categoryCollection = $this->objectManager->get(CategoryCollection::class); + $categoryModel = $categoryCollection + ->addAttributeToSelect('image') + ->addAttributeToFilter('name', ['eq' => 'Parent Image Category']) + ->getFirstItem(); + $categoryId = $categoryModel->getId(); + + if ($imagePrefix !== null) { + // update image to account for different stored image formats + $connection = $categoryCollection->getConnection(); + $productLinkField = $this->metadataPool + ->getMetadata(\Magento\Catalog\Api\Data\ProductInterface::class) + ->getLinkField(); + + $defaultStoreId = $this->store->getId(); + + $imageAttributeValue = $imagePrefix . basename($categoryModel->getImage()); + + if (!empty($imageAttributeValue)) { + $query = sprintf( + 'UPDATE %s SET `value` = "%s" ' . + 'WHERE `%s` = %d ' . + 'AND `store_id`= %d ' . + 'AND `attribute_id` = ' . + '(SELECT `ea`.`attribute_id` FROM %s ea WHERE `ea`.`attribute_code` = "image" LIMIT 1)', + $connection->getTableName('catalog_category_entity_varchar'), + $imageAttributeValue, + $productLinkField, + $categoryModel->getData($productLinkField), + $defaultStoreId, + $connection->getTableName('eav_attribute') + ); + $connection->query($query); + } + } + + $query = <<<QUERY +{ +categories(filters: {ids: {in: ["$categoryId"]}}) { + items { + id + name + url_key + image + children { + id + name + url_key + image + } + } +} +} +QUERY; + + $response = $this->graphQlQuery($query); + $this->assertArrayNotHasKey('errors', $response); + $this->assertNotEmpty($response['categories']); + $categories = $response['categories']['items']; + $storeBaseUrl = $this->objectManager->get(StoreManagerInterface::class)->getStore()->getBaseUrl('media'); + $expectedImageUrl = rtrim($storeBaseUrl, '/') . '/' . ltrim($categoryModel->getImage(), '/'); + $expectedImageUrl = str_replace('index.php/', '', $expectedImageUrl); + + $this->assertEquals($categoryId, $categories[0]['id']); + $this->assertEquals('Parent Image Category', $categories[0]['name']); + $categories[0]['image'] = str_replace('index.php/', '', $categories[0]['image']); + $this->assertEquals($expectedImageUrl, $categories[0]['image']); + + $childCategory = $categories[0]['children'][0]; + $this->assertEquals('Child Image Category', $childCategory['name']); + $childCategory['image'] = str_replace('index.php/', '', $childCategory['image']); + $this->assertEquals($expectedImageUrl, $childCategory['image']); + } + + /** + * @return array + */ + public function categoryImageDataProvider(): array + { + return [ + 'default_filename_strategy' => [ + 'image_prefix' => null + ], + 'just_filename_strategy' => [ + 'image_prefix' => '' + ], + 'with_pub_media_strategy' => [ + 'image_prefix' => '/pub/media/catalog/category/' + ], + 'catalog_category_strategy' => [ + 'image_prefix' => 'catalog/category/' + ], + ]; + } + + /** + * @param ProductInterface $product + * @param array $actualResponse + */ + private function assertBaseFields($product, $actualResponse) + { + $assertionMap = [ + ['response_field' => 'attribute_set_id', 'expected_value' => $product->getAttributeSetId()], + ['response_field' => 'created_at', 'expected_value' => $product->getCreatedAt()], + ['response_field' => 'name', 'expected_value' => $product->getName()], + ['response_field' => 'price', 'expected_value' => [ + 'minimalPrice' => [ + 'amount' => [ + 'value' => $product->getPrice(), + 'currency' => 'USD' + ], + 'adjustments' => [] + ], + 'regularPrice' => [ + 'amount' => [ + 'value' => $product->getPrice(), + 'currency' => 'USD' + ], + 'adjustments' => [] + ], + 'maximalPrice' => [ + 'amount' => [ + 'value' => $product->getPrice(), + 'currency' => 'USD' + ], + 'adjustments' => [] + ], + ] + ], + ['response_field' => 'sku', 'expected_value' => $product->getSku()], + ['response_field' => 'type_id', 'expected_value' => $product->getTypeId()], + ['response_field' => 'updated_at', 'expected_value' => $product->getUpdatedAt()], + ]; + $this->assertResponseFields($actualResponse, $assertionMap); + } + + /** + * @param ProductInterface $product + * @param array $actualResponse + */ + private function assertWebsites($product, $actualResponse) + { + $assertionMap = [ + [ + 'id' => current($product->getExtensionAttributes()->getWebsiteIds()), + 'name' => 'Main Website', + 'code' => 'base', + 'sort_order' => 0, + 'default_group_id' => '1', + 'is_default' => true, + ] + ]; + $this->assertEquals($actualResponse, $assertionMap); + } + + /** + * @param array $actualResponse + */ + private function assertAttributes($actualResponse) + { + $eavAttributes = [ + 'url_key', + 'description', + 'meta_description', + 'meta_keyword', + 'meta_title', + 'short_description', + 'country_of_manufacture', + 'gift_message_available', + 'new_from_date', + 'new_to_date', + 'options_container', + 'special_price', + 'special_from_date', + 'special_to_date', + ]; + foreach ($eavAttributes as $eavAttribute) { + $this->assertArrayHasKey($eavAttribute, $actualResponse); + } + } +} diff --git a/dev/tests/api-functional/testsuite/Magento/GraphQl/Catalog/CategoryListTest.php b/dev/tests/api-functional/testsuite/Magento/GraphQl/Catalog/CategoryListTest.php index 96e8ae79b612e..82a606dad7dec 100644 --- a/dev/tests/api-functional/testsuite/Magento/GraphQl/Catalog/CategoryListTest.php +++ b/dev/tests/api-functional/testsuite/Magento/GraphQl/Catalog/CategoryListTest.php @@ -7,6 +7,8 @@ namespace Magento\GraphQl\Catalog; +use Magento\Catalog\Model\ResourceModel\Category\Collection as CategoryCollection; +use Magento\Framework\ObjectManagerInterface; use Magento\Store\Model\StoreManagerInterface; use Magento\TestFramework\Helper\Bootstrap; use Magento\TestFramework\TestCase\GraphQlAbstract; @@ -16,12 +18,23 @@ */ class CategoryListTest extends GraphQlAbstract { + /** + * @var ObjectManagerInterface + */ + private $objectManager; + + protected function setUp() + { + $this->objectManager = Bootstrap::getObjectManager(); + } + /** * @magentoApiDataFixture Magento/Catalog/_files/categories.php * @dataProvider filterSingleCategoryDataProvider - * @param $field - * @param $condition - * @param $value + * @param string $field + * @param string $condition + * @param string $value + * @param array $expectedResult */ public function testFilterSingleCategoryByField($field, $condition, $value, $expectedResult) { @@ -174,8 +187,8 @@ public function testQueryChildCategoriesWithProducts() //Check base category products $expectedBaseCategoryProducts = [ ['sku' => 'simple', 'name' => 'Simple Product'], - ['sku' => '12345', 'name' => 'Simple Product Two'], - ['sku' => 'simple-4', 'name' => 'Simple Product Three'] + ['sku' => 'simple-4', 'name' => 'Simple Product Three'], + ['sku' => '12345', 'name' => 'Simple Product Two'] ]; $this->assertCategoryProducts($baseCategory, $expectedBaseCategoryProducts); //Check base category children @@ -190,8 +203,8 @@ public function testQueryChildCategoriesWithProducts() $this->assertEquals('Category 1.1', $firstChildCategory['name']); $this->assertEquals('Category 1.1 description.', $firstChildCategory['description']); $firstChildCategoryExpectedProducts = [ - ['sku' => 'simple', 'name' => 'Simple Product'], ['sku' => '12345', 'name' => 'Simple Product Two'], + ['sku' => 'simple', 'name' => 'Simple Product'], ]; $this->assertCategoryProducts($firstChildCategory, $firstChildCategoryExpectedProducts); $firstChildCategoryChildren = [['name' =>'Category 1.1.1']]; @@ -201,8 +214,8 @@ public function testQueryChildCategoriesWithProducts() $this->assertEquals('Category 1.2', $secondChildCategory['name']); $this->assertEquals('Its a description of Test Category 1.2', $secondChildCategory['description']); $firstChildCategoryExpectedProducts = [ + ['sku' => 'simple-4', 'name' => 'Simple Product Three'], ['sku' => 'simple', 'name' => 'Simple Product'], - ['sku' => 'simple-4', 'name' => 'Simple Product Three'] ]; $this->assertCategoryProducts($secondChildCategory, $firstChildCategoryExpectedProducts); $firstChildCategoryChildren = []; @@ -265,8 +278,8 @@ public function testQueryCategoryWithDisabledChildren() //Check base category products $expectedBaseCategoryProducts = [ ['sku' => 'simple', 'name' => 'Simple Product'], - ['sku' => '12345', 'name' => 'Simple Product Two'], - ['sku' => 'simple-4', 'name' => 'Simple Product Three'] + ['sku' => 'simple-4', 'name' => 'Simple Product Three'], + ['sku' => '12345', 'name' => 'Simple Product Two'] ]; $this->assertCategoryProducts($baseCategory, $expectedBaseCategoryProducts); //Check base category children @@ -281,8 +294,8 @@ public function testQueryCategoryWithDisabledChildren() $this->assertEquals('Its a description of Test Category 1.2', $firstChildCategory['description']); $firstChildCategoryExpectedProducts = [ - ['sku' => 'simple', 'name' => 'Simple Product'], - ['sku' => 'simple-4', 'name' => 'Simple Product Three'] + ['sku' => 'simple-4', 'name' => 'Simple Product Three'], + ['sku' => 'simple', 'name' => 'Simple Product'] ]; $this->assertCategoryProducts($firstChildCategory, $firstChildCategoryExpectedProducts); $firstChildCategoryChildren = []; @@ -333,7 +346,7 @@ public function testEmptyFiltersReturnRootCategory() } } QUERY; - $storeManager = Bootstrap::getObjectManager()->get(StoreManagerInterface::class); + $storeManager = $this->objectManager->get(StoreManagerInterface::class); $storeRootCategoryId = $storeManager->getStore()->getRootCategoryId(); $result = $this->graphQlQuery($query); @@ -347,6 +360,8 @@ public function testEmptyFiltersReturnRootCategory() * Filtering with match value less than minimum query should return empty result * * @magentoApiDataFixture Magento/Catalog/_files/categories.php + * @expectedException \Exception + * @expectedExceptionMessage Invalid match filter. Minimum length is 3. */ public function testMinimumMatchQueryLength() { @@ -363,10 +378,155 @@ public function testMinimumMatchQueryLength() } } QUERY; - $result = $this->graphQlQuery($query); - $this->assertArrayNotHasKey('errors', $result); - $this->assertArrayHasKey('categoryList', $result); - $this->assertEquals([], $result['categoryList']); + $this->graphQlQuery($query); + } + + /** + * Test category image full name is returned + * + * @magentoApiDataFixture Magento/Catalog/_files/catalog_category_with_long_image_name.php + */ + public function testCategoryImageName() + { + /** @var CategoryCollection $categoryCollection */ + $categoryCollection = $this->objectManager->get(CategoryCollection::class); + $categoryModel = $categoryCollection + ->addAttributeToSelect('image') + ->addAttributeToFilter('name', ['eq' => 'Parent Image Category']) + ->getFirstItem(); + $categoryId = $categoryModel->getId(); + + $query = <<<QUERY + { +categoryList(filters: {ids: {in: ["$categoryId"]}}) { + id + name + image + } +} +QUERY; + $storeManager = $this->objectManager->get(StoreManagerInterface::class); + $storeBaseUrl = $storeManager->getStore()->getBaseUrl('media'); + + $expected = "catalog/category/magento_long_image_name_magento_long_image_name_magento_long_image_name.jpg"; + $expectedImageUrl = rtrim($storeBaseUrl, '/') . '/' . $expected; + + $response = $this->graphQlQuery($query); + $categoryList = $response['categoryList']; + $this->assertArrayNotHasKey('errors', $response); + $this->assertNotEmpty($response['categoryList']); + $expectedImageUrl = str_replace('index.php/', '', $expectedImageUrl); + $categoryList[0]['image'] = str_replace('index.php/', '', $categoryList[0]['image']); + $this->assertEquals('Parent Image Category', $categoryList[0]['name']); + $this->assertEquals($expectedImageUrl, $categoryList[0]['image']); + } + + /** + * @magentoApiDataFixture Magento/Catalog/_files/categories.php + */ + public function testFilterByUrlPathTopLevelCategory() + { + $urlPath = 'category-1'; + $query = <<<QUERY +{ + categoryList(filters: {url_path: {eq: "$urlPath"}}){ + id + name + url_key + url_path + path + position + } +} +QUERY; + + $response = $this->graphQlQuery($query); + $this->assertArrayNotHasKey('errors', $response); + $categoryList = $response['categoryList']; + $this->assertCount(1, $categoryList); + $this->assertEquals($urlPath, $categoryList[0]['url_path']); + $this->assertEquals('Category 1', $categoryList[0]['name']); + } + + /** + * @magentoApiDataFixture Magento/Catalog/_files/categories.php + */ + public function testFilterByUrlPathNestedCategory() + { + $urlPath = 'category-1/category-1-1/category-1-1-1'; + $query = <<<QUERY +{ + categoryList(filters: {url_path: {eq: "$urlPath"}}){ + id + name + url_key + url_path + path + position + } +} +QUERY; + + $response = $this->graphQlQuery($query); + $this->assertArrayNotHasKey('errors', $response); + $categoryList = $response['categoryList']; + $this->assertCount(1, $categoryList); + $this->assertEquals($urlPath, $categoryList[0]['url_path']); + $this->assertEquals('Category 1.1.1', $categoryList[0]['name']); + } + + /** + * @magentoApiDataFixture Magento/Catalog/_files/categories.php + */ + public function testFilterByUrlPathMultipleCategories() + { + $urlPaths = ['category-1/category-1-1', 'inactive', 'movable-position-2']; + $urlPathsString = '"' . implode('", "', $urlPaths) . '"'; + $query = <<<QUERY +{ + categoryList(filters: {url_path: {in: [$urlPathsString]}}){ + id + name + url_key + url_path + path + position + } +} +QUERY; + + $response = $this->graphQlQuery($query); + $this->assertArrayNotHasKey('errors', $response); + $categoryList = $response['categoryList']; + $this->assertCount(2, $categoryList); + $this->assertEquals($urlPaths[0], $categoryList[0]['url_path']); + $this->assertEquals('Category 1.1', $categoryList[0]['name']); + $this->assertEquals($urlPaths[2], $categoryList[1]['url_path']); + $this->assertEquals('Movable Position 2', $categoryList[1]['name']); + } + + /** + * @magentoApiDataFixture Magento/Catalog/_files/categories.php + */ + public function testFilterByUrlPathNoResults() + { + $query = <<<QUERY +{ + categoryList(filters: {url_path: {in: ["not-a-category url path"]}}){ + id + name + url_key + url_path + path + position + } +} +QUERY; + + $response = $this->graphQlQuery($query); + $this->assertArrayNotHasKey('errors', $response); + $categoryList = $response['categoryList']; + $this->assertCount(0, $categoryList); } /** diff --git a/dev/tests/api-functional/testsuite/Magento/GraphQl/Catalog/CategoryTest.php b/dev/tests/api-functional/testsuite/Magento/GraphQl/Catalog/CategoryTest.php index 480388db98d2f..da6a3604f1c67 100644 --- a/dev/tests/api-functional/testsuite/Magento/GraphQl/Catalog/CategoryTest.php +++ b/dev/tests/api-functional/testsuite/Magento/GraphQl/Catalog/CategoryTest.php @@ -13,6 +13,8 @@ use Magento\Catalog\Model\CategoryRepository; use Magento\Catalog\Model\ResourceModel\Category\Collection as CategoryCollection; use Magento\Framework\DataObject; +use Magento\Framework\EntityManager\MetadataPool; +use Magento\Store\Model\Store; use Magento\Store\Model\StoreManagerInterface; use Magento\TestFramework\ObjectManager; use Magento\TestFramework\TestCase\GraphQl\ResponseContainsErrorsException; @@ -33,10 +35,22 @@ class CategoryTest extends GraphQlAbstract */ private $categoryRepository; + /** + * @var Store + */ + private $store; + + /** + * @var MetadataPool + */ + private $metadataPool; + protected function setUp() { $this->objectManager = \Magento\TestFramework\Helper\Bootstrap::getObjectManager(); $this->categoryRepository = $this->objectManager->get(CategoryRepository::class); + $this->store = $this->objectManager->get(Store::class); + $this->metadataPool = $this->objectManager->get(MetadataPool::class); } /** @@ -211,7 +225,7 @@ public function testCategoriesTreeWithDisabledCategory() productImagePreview: products(pageSize: 1) { items { id - } + } } } } @@ -557,10 +571,13 @@ public function testBreadCrumbs() /** * Test category image is returned as full url (not relative path) * + * @param string $imagePrefix * @magentoApiDataFixture Magento/Catalog/_files/catalog_category_with_image.php + * @dataProvider categoryImageDataProvider */ - public function testCategoryImage() + public function testCategoryImage(?string $imagePrefix) { + /** @var CategoryCollection $categoryCollection */ $categoryCollection = $this->objectManager->get(CategoryCollection::class); $categoryModel = $categoryCollection ->addAttributeToSelect('image') @@ -568,6 +585,35 @@ public function testCategoryImage() ->getFirstItem(); $categoryId = $categoryModel->getId(); + if ($imagePrefix !== null) { + // update image to account for different stored image formats + $connection = $categoryCollection->getConnection(); + $productLinkField = $this->metadataPool + ->getMetadata(\Magento\Catalog\Api\Data\ProductInterface::class) + ->getLinkField(); + + $defaultStoreId = $this->store->getId(); + + $imageAttributeValue = $imagePrefix . basename($categoryModel->getImage()); + + if (!empty($imageAttributeValue)) { + $query = sprintf( + 'UPDATE %s SET `value` = "%s" ' . + 'WHERE `%s` = %d ' . + 'AND `store_id`= %d ' . + 'AND `attribute_id` = ' . + '(SELECT `ea`.`attribute_id` FROM %s ea WHERE `ea`.`attribute_code` = "image" LIMIT 1)', + $connection->getTableName('catalog_category_entity_varchar'), + $imageAttributeValue, + $productLinkField, + $categoryModel->getData($productLinkField), + $defaultStoreId, + $connection->getTableName('eav_attribute') + ); + $connection->query($query); + } + } + $query = <<<QUERY { categoryList(filters: {ids: {in: ["$categoryId"]}}) { @@ -591,17 +637,41 @@ public function testCategoryImage() $this->assertNotEmpty($response['categoryList']); $categoryList = $response['categoryList']; $storeBaseUrl = $this->objectManager->get(StoreManagerInterface::class)->getStore()->getBaseUrl('media'); - $expectedImageUrl = rtrim($storeBaseUrl, '/'). '/' . ltrim($categoryModel->getImage(), '/'); + $expectedImageUrl = rtrim($storeBaseUrl, '/') . '/' . ltrim($categoryModel->getImage(), '/'); + $expectedImageUrl = str_replace('index.php/', '', $expectedImageUrl); $this->assertEquals($categoryId, $categoryList[0]['id']); $this->assertEquals('Parent Image Category', $categoryList[0]['name']); + $categoryList[0]['image'] = str_replace('index.php/', '', $categoryList[0]['image']); $this->assertEquals($expectedImageUrl, $categoryList[0]['image']); $childCategory = $categoryList[0]['children'][0]; $this->assertEquals('Child Image Category', $childCategory['name']); + $childCategory['image'] = str_replace('index.php/', '', $childCategory['image']); $this->assertEquals($expectedImageUrl, $childCategory['image']); } + /** + * @return array + */ + public function categoryImageDataProvider(): array + { + return [ + 'default_filename_strategy' => [ + 'image_prefix' => null + ], + 'just_filename_strategy' => [ + 'image_prefix' => '' + ], + 'with_pub_media_strategy' => [ + 'image_prefix' => '/pub/media/catalog/category/' + ], + 'catalog_category_strategy' => [ + 'image_prefix' => 'catalog/category/' + ], + ]; + } + /** * @param ProductInterface $product * @param array $actualResponse diff --git a/dev/tests/api-functional/testsuite/Magento/GraphQl/Catalog/MediaGalleryTest.php b/dev/tests/api-functional/testsuite/Magento/GraphQl/Catalog/MediaGalleryTest.php index b6687b4e171d3..615438d52e764 100644 --- a/dev/tests/api-functional/testsuite/Magento/GraphQl/Catalog/MediaGalleryTest.php +++ b/dev/tests/api-functional/testsuite/Magento/GraphQl/Catalog/MediaGalleryTest.php @@ -14,16 +14,6 @@ */ class MediaGalleryTest extends GraphQlAbstract { - /** - * @var \Magento\TestFramework\ObjectManager - */ - private $objectManager; - - protected function setUp() - { - $this->objectManager = \Magento\TestFramework\Helper\Bootstrap::getObjectManager(); - } - /** * @magentoApiDataFixture Magento/Catalog/_files/product_with_image.php */ @@ -38,7 +28,7 @@ public function testProductSmallImageUrlWithExistingImage() url } } - } + } } QUERY; $response = $this->graphQlQuery($query); @@ -48,6 +38,33 @@ public function testProductSmallImageUrlWithExistingImage() self::assertTrue($this->checkImageExists($response['products']['items'][0]['small_image']['url'])); } + /** + * Test for get product image placeholder + * + * @magentoApiDataFixture Magento/Catalog/_files/product_simple.php + */ + public function testProductSmallImageUrlPlaceholder() + { + $productSku = 'simple'; + $query = <<<QUERY +{ + products(filter: {sku: {eq: "{$productSku}"}}) { + items { + small_image { + url + } + } + } +} +QUERY; + $response = $this->graphQlQuery($query); + $responseImage = $response['products']['items'][0]['small_image']; + + self::assertArrayHasKey('url', $responseImage); + self::assertContains('placeholder/small_image.jpg', $responseImage['url']); + self::assertTrue($this->checkImageExists($responseImage['url'])); + } + /** * @magentoApiDataFixture Magento/Catalog/_files/product_with_multiple_images.php */ @@ -57,7 +74,7 @@ public function testMediaGalleryTypesAreCorrect() $query = <<<QUERY { products(filter: {sku: {eq: "{$productSku}"}}) { - items { + items { media_gallery_entries { label media_type @@ -65,7 +82,7 @@ public function testMediaGalleryTypesAreCorrect() types } } - } + } } QUERY; $response = $this->graphQlQuery($query); @@ -91,13 +108,15 @@ public function testMediaGallery() $query = <<<QUERY { products(filter: {sku: {eq: "{$productSku}"}}) { - items { + items { media_gallery { label url + position + disabled } } - } + } } QUERY; $response = $this->graphQlQuery($query); @@ -105,9 +124,13 @@ public function testMediaGallery() $mediaGallery = $response['products']['items'][0]['media_gallery']; $this->assertCount(2, $mediaGallery); $this->assertEquals('Image Alt Text', $mediaGallery[0]['label']); - self::assertTrue($this->checkImageExists($mediaGallery[0]['url'])); + $this->assertEquals(1, $mediaGallery[0]['position']); + $this->assertFalse($mediaGallery[0]['disabled']); + $this->assertTrue($this->checkImageExists($mediaGallery[0]['url'])); $this->assertEquals('Thumbnail Image', $mediaGallery[1]['label']); - self::assertTrue($this->checkImageExists($mediaGallery[1]['url'])); + $this->assertEquals(2, $mediaGallery[1]['position']); + $this->assertFalse($mediaGallery[1]['disabled']); + $this->assertTrue($this->checkImageExists($mediaGallery[1]['url'])); } /** @@ -119,10 +142,12 @@ public function testMediaGalleryForProductVideos() $query = <<<QUERY { products(filter: {sku: {eq: "{$productSku}"}}) { - items { + items { media_gallery { label url + position + disabled ... on ProductVideo { video_content { media_type @@ -135,7 +160,7 @@ public function testMediaGalleryForProductVideos() } } } - } + } } QUERY; $response = $this->graphQlQuery($query); @@ -143,7 +168,9 @@ public function testMediaGalleryForProductVideos() $mediaGallery = $response['products']['items'][0]['media_gallery']; $this->assertCount(1, $mediaGallery); $this->assertEquals('Video Label', $mediaGallery[0]['label']); - self::assertTrue($this->checkImageExists($mediaGallery[0]['url'])); + $this->assertTrue($this->checkImageExists($mediaGallery[0]['url'])); + $this->assertFalse($mediaGallery[0]['disabled']); + $this->assertEquals(2, $mediaGallery[0]['position']); $this->assertNotEmpty($mediaGallery[0]['video_content']); $video_content = $mediaGallery[0]['video_content']; $this->assertEquals('external-video', $video_content['media_type']); diff --git a/dev/tests/api-functional/testsuite/Magento/GraphQl/Catalog/ProductFragmentTest.php b/dev/tests/api-functional/testsuite/Magento/GraphQl/Catalog/ProductFragmentTest.php new file mode 100644 index 0000000000000..32a2f8f763572 --- /dev/null +++ b/dev/tests/api-functional/testsuite/Magento/GraphQl/Catalog/ProductFragmentTest.php @@ -0,0 +1,54 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +declare(strict_types=1); + +namespace Magento\GraphQl\Catalog; + +use Magento\TestFramework\TestCase\GraphQlAbstract; + +/** + * Test for simple product fragment. + */ +class ProductFragmentTest extends GraphQlAbstract +{ + /** + * @magentoApiDataFixture Magento/Catalog/_files/product_simple.php + */ + public function testSimpleProductFragment() + { + $sku = 'simple'; + $name = 'Simple Product'; + $price = 10; + + $query = <<<QUERY +query GetProduct { + products(filter: { sku: { eq: "$sku" } }) { + items { + sku + ...BasicProductInformation + } + } +} + +fragment BasicProductInformation on ProductInterface { + sku + name + price { + regularPrice { + amount { + value + } + } + } +} +QUERY; + $result = $this->graphQlQuery($query); + $actualProductData = $result['products']['items'][0]; + $this->assertNotEmpty($actualProductData); + $this->assertEquals($name, $actualProductData['name']); + $this->assertEquals($price, $actualProductData['price']['regularPrice']['amount']['value']); + } +} diff --git a/dev/tests/api-functional/testsuite/Magento/GraphQl/Catalog/ProductSearchTest.php b/dev/tests/api-functional/testsuite/Magento/GraphQl/Catalog/ProductSearchTest.php index 9ee3b3baa5fc2..d7f7a3601b7e9 100644 --- a/dev/tests/api-functional/testsuite/Magento/GraphQl/Catalog/ProductSearchTest.php +++ b/dev/tests/api-functional/testsuite/Magento/GraphQl/Catalog/ProductSearchTest.php @@ -13,6 +13,7 @@ use Magento\Catalog\Api\ProductRepositoryInterface; use Magento\Catalog\Model\Category; use Magento\Catalog\Model\CategoryLinkManagement; +use Magento\Catalog\Model\ResourceModel\Category\Collection; use Magento\Eav\Model\Config; use Magento\TestFramework\ObjectManager; use Magento\TestFramework\TestCase\GraphQlAbstract; @@ -42,7 +43,7 @@ public function testFilterForNonExistingCategory() { products(filter: {category_id: {eq: "99999999"}}) { filters { - name + name } } } @@ -156,7 +157,7 @@ public function testLayeredNavigationForConfigurableProducts() CacheCleaner::cleanAll(); $attributeCode = 'test_configurable'; - /** @var \Magento\Eav\Model\Config $eavConfig */ + /** @var Config $eavConfig */ $eavConfig = \Magento\TestFramework\Helper\Bootstrap::getObjectManager()->get(Config::class); $attribute = $eavConfig->getAttribute('catalog_product', $attributeCode); /** @var AttributeOptionInterface[] $options */ @@ -204,7 +205,7 @@ private function getQueryProductsWithArrayOfCustomAttributes($attributeCode, $fi { return <<<QUERY { - products(filter:{ + products(filter:{ $attributeCode: {in:["{$firstOption}", "{$secondOption}"]} } pageSize: 3 @@ -212,7 +213,7 @@ private function getQueryProductsWithArrayOfCustomAttributes($attributeCode, $fi ) { total_count - items + items { name sku @@ -225,14 +226,14 @@ private function getQueryProductsWithArrayOfCustomAttributes($attributeCode, $fi filters{ name request_var - filter_items_count + filter_items_count filter_items{ label items_count value_string __typename } - } + } aggregations{ attribute_code count @@ -243,8 +244,8 @@ private function getQueryProductsWithArrayOfCustomAttributes($attributeCode, $fi count } } - - } + + } } QUERY; } @@ -262,16 +263,14 @@ public function testFilterProductsByDropDownCustomAttribute() $optionValue = $this->getDefaultAttributeOptionValue($attributeCode); $query = <<<QUERY { - products(filter:{ - $attributeCode: {eq: "{$optionValue}"} - } - - pageSize: 3 - currentPage: 1 - ) + products( + filter:{ $attributeCode: {eq: "{$optionValue}"} } + pageSize: 3 + currentPage: 1 + ) { - total_count - items + total_count + items { name sku @@ -284,14 +283,13 @@ public function testFilterProductsByDropDownCustomAttribute() filters{ name request_var - filter_items_count + filter_items_count filter_items{ label items_count value_string __typename } - } aggregations{ attribute_code @@ -304,8 +302,8 @@ public function testFilterProductsByDropDownCustomAttribute() value } } - - } + + } } QUERY; @@ -335,7 +333,7 @@ public function testFilterProductsByDropDownCustomAttribute() ); } - /** @var \Magento\Eav\Model\Config $eavConfig */ + /** @var Config $eavConfig */ $eavConfig = $objectManager->get(Config::class); $attribute = $eavConfig->getAttribute('catalog_product', 'second_test_configurable'); // Validate custom attribute filter layer data from aggregations @@ -365,7 +363,9 @@ private function reIndexAndCleanCache() : void $appDir = dirname(Bootstrap::getInstance()->getAppTempDir()); $out = ''; // phpcs:ignore Magento2.Security.InsecureFunction - exec("php -f {$appDir}/bin/magento indexer:reindex", $out); + exec("php -f {$appDir}/bin/magento indexer:reindex catalog_category_product", $out); + // phpcs:ignore Magento2.Security.InsecureFunction + exec("php -f {$appDir}/bin/magento indexer:reindex catalogsearch_fulltext", $out); CacheCleaner::cleanAll(); } @@ -380,8 +380,8 @@ public function testFilterProductsByMultiSelectCustomAttributes() $objectManager = Bootstrap::getObjectManager(); $this->reIndexAndCleanCache(); $attributeCode = 'multiselect_attribute'; - /** @var \Magento\Eav\Model\Config $eavConfig */ - $eavConfig = $objectManager->get(\Magento\Eav\Model\Config::class); + /** @var Config $eavConfig */ + $eavConfig = $objectManager->get(Config::class); $attribute = $eavConfig->getAttribute('catalog_product', $attributeCode); /** @var AttributeOptionInterface[] $options */ $options = $attribute->getOptions(); @@ -393,15 +393,15 @@ public function testFilterProductsByMultiSelectCustomAttributes() } $query = <<<QUERY { - products(filter:{ - $attributeCode: {in:["{$optionValues[0]}", "{$optionValues[1]}", "{$optionValues[2]}"]} + products(filter:{ + $attributeCode: {in:["{$optionValues[0]}", "{$optionValues[1]}", "{$optionValues[2]}"]} } pageSize: 3 currentPage: 1 ) { total_count - items + items { name sku @@ -414,14 +414,14 @@ public function testFilterProductsByMultiSelectCustomAttributes() filters{ name request_var - filter_items_count + filter_items_count filter_items{ label items_count value_string __typename } - } + } aggregations{ attribute_code count @@ -430,15 +430,16 @@ public function testFilterProductsByMultiSelectCustomAttributes() { label value - + } } - - } + + } } QUERY; $response = $this->graphQlQuery($query); + $this->assertArrayNotHasKey('errors', $response, 'Response has errors.'); $this->assertEquals(3, $response['products']['total_count']); $this->assertNotEmpty($response['products']['filters']); $this->assertNotEmpty($response['products']['aggregations']); @@ -452,8 +453,8 @@ public function testFilterProductsByMultiSelectCustomAttributes() */ private function getDefaultAttributeOptionValue(string $attributeCode) : string { - /** @var \Magento\Eav\Model\Config $eavConfig */ - $eavConfig = \Magento\TestFramework\Helper\Bootstrap::getObjectManager()->get(\Magento\Eav\Model\Config::class); + /** @var Config $eavConfig */ + $eavConfig = \Magento\TestFramework\Helper\Bootstrap::getObjectManager()->get(Config::class); $attribute = $eavConfig->getAttribute('catalog_product', $attributeCode); /** @var AttributeOptionInterface[] $options */ $options = $attribute->getOptions(); @@ -463,7 +464,7 @@ private function getDefaultAttributeOptionValue(string $attributeCode) : string } /** - * Full text search for Products and then filter the results by custom attribute ( sort is by defaulty by relevance) + * Full text search for Products and then filter the results by custom attribute (default sort is relevance) * * @magentoApiDataFixture Magento/Catalog/_files/products_with_layered_navigation_custom_attribute.php * @SuppressWarnings(PHPMD.ExcessiveMethodLength) @@ -485,7 +486,7 @@ public function testSearchAndFilterByCustomAttribute() ) { total_count - items + items { name sku @@ -498,15 +499,15 @@ public function testSearchAndFilterByCustomAttribute() filters{ name request_var - filter_items_count + filter_items_count filter_items{ label items_count value_string __typename } - - } + + } aggregations { attribute_code @@ -518,10 +519,8 @@ public function testSearchAndFilterByCustomAttribute() label value } - } - } - + } } QUERY; $response = $this->graphQlQuery($query); @@ -555,7 +554,7 @@ public function testSearchAndFilterByCustomAttribute() ); } - // Validate the price layer of aggregations from the response + // Validate the price layer of aggregations from the response $this->assertResponseFields( $response['products']['aggregations'][0], [ @@ -631,7 +630,7 @@ public function testFilterByCategoryIdAndCustomAttribute() ) { total_count - items + items { name sku @@ -644,7 +643,7 @@ public function testFilterByCategoryIdAndCustomAttribute() filters{ name request_var - filter_items_count + filter_items_count filter_items{ label items_count @@ -664,7 +663,7 @@ public function testFilterByCategoryIdAndCustomAttribute() value } } - } + } } QUERY; $response = $this->graphQlQuery($query); @@ -673,7 +672,7 @@ public function testFilterByCategoryIdAndCustomAttribute() $productRepository = ObjectManager::getInstance()->get(ProductRepositoryInterface::class); $product1 = $productRepository->get('simple'); $product2 = $productRepository->get('simple-4'); - $filteredProducts = [$product1, $product2]; + $filteredProducts = [$product2, $product1]; $productItemsInResponse = array_map(null, $response['products']['items'], $filteredProducts); //phpcs:ignore Generic.CodeAnalysis.ForLoopWithTestFunctionCall for ($itemIndex = 0; $itemIndex < count($filteredProducts); $itemIndex++) { @@ -681,7 +680,8 @@ public function testFilterByCategoryIdAndCustomAttribute() //validate that correct products are returned $this->assertResponseFields( $productItemsInResponse[$itemIndex][0], - [ 'name' => $filteredProducts[$itemIndex]->getName(), + [ + 'name' => $filteredProducts[$itemIndex]->getName(), 'sku' => $filteredProducts[$itemIndex]->getSku() ] ); @@ -695,7 +695,7 @@ public function testFilterByCategoryIdAndCustomAttribute() //Validate the number of categories/sub-categories that contain the products with the custom attribute $this->assertCount(6, $actualCategoriesFromResponse); - $expectedCategoryInAggregrations = + $expectedCategoryInAggregations = [ [ 'count' => 2, @@ -732,9 +732,9 @@ public function testFilterByCategoryIdAndCustomAttribute() ], ]; // presort expected and actual results as different search engines have different orders - usort($expectedCategoryInAggregrations, [$this, 'compareLabels']); + usort($expectedCategoryInAggregations, [$this, 'compareLabels']); usort($actualCategoriesFromResponse, [$this, 'compareLabels']); - $categoryInAggregations = array_map(null, $expectedCategoryInAggregrations, $actualCategoriesFromResponse); + $categoryInAggregations = array_map(null, $expectedCategoryInAggregations, $actualCategoriesFromResponse); //Validate the categories and sub-categories data in the filter layer foreach ($categoryInAggregations as $index => $categoryAggregationsData) { @@ -788,7 +788,7 @@ public function testFilterBySingleProductUrlKey() ) { total_count - items + items { name sku @@ -802,7 +802,7 @@ public function testFilterBySingleProductUrlKey() filters{ name request_var - filter_items_count + filter_items_count filter_items{ label items_count @@ -822,7 +822,7 @@ public function testFilterBySingleProductUrlKey() value } } - } + } } QUERY; $response = $this->graphQlQuery($query); @@ -851,17 +851,17 @@ public function testFilterBySingleProductUrlKey() ) { total_count - items + items { name sku url_key } - + filters{ name request_var - filter_items_count + filter_items_count } aggregations { @@ -875,7 +875,7 @@ public function testFilterBySingleProductUrlKey() value } } - } + } } QUERY; $response = $this->graphQlQuery($query2); @@ -914,7 +914,7 @@ public function testFilterByMultipleProductUrlKeys() ) { total_count - items + items { name sku @@ -923,12 +923,12 @@ public function testFilterByMultipleProductUrlKeys() page_info{ current_page page_size - + } filters{ name request_var - filter_items_count + filter_items_count } aggregations { @@ -942,7 +942,7 @@ public function testFilterByMultipleProductUrlKeys() value } } - } + } } QUERY; $response = $this->graphQlQuery($query); @@ -971,8 +971,8 @@ public function testFilterByMultipleProductUrlKeys() */ private function getExpectedFiltersDataSet() { - /** @var \Magento\Eav\Model\Config $eavConfig */ - $eavConfig = \Magento\TestFramework\Helper\Bootstrap::getObjectManager()->get(\Magento\Eav\Model\Config::class); + /** @var Config $eavConfig */ + $eavConfig = \Magento\TestFramework\Helper\Bootstrap::getObjectManager()->get(Config::class); $attribute = $eavConfig->getAttribute('catalog_product', 'test_configurable'); /** @var \Magento\Eav\Api\Data\AttributeOptionInterface[] $options */ $options = $attribute->getOptions(); @@ -1118,6 +1118,85 @@ public function testFilterWithinSpecificPriceRangeSortedByNameDesc() $this->assertEquals(4, $response['products']['page_info']['page_size']); } + /** + * @magentoApiDataFixture Magento/Catalog/_files/category_with_three_products.php + * @SuppressWarnings(PHPMD.UnusedLocalVariable) + */ + public function testSortByPosition() + { + $this->reIndexAndCleanCache(); + // Get category ID for filtering + /** @var Collection $categoryCollection */ + $categoryCollection = Bootstrap::getObjectManager()->get(Collection::class); + $category = $categoryCollection->addFieldToFilter('name', 'Category 999')->getFirstItem(); + $categoryId = $category->getId(); + + $queryAsc = <<<QUERY +{ + products(filter: {category_id: {eq: "$categoryId"}}, sort: {position: ASC}) { + total_count + items { + sku + name + } + } +} +QUERY; + $resultAsc = $this->graphQlQuery($queryAsc); + $this->assertArrayNotHasKey('errors', $resultAsc); + $productsAsc = array_column($resultAsc['products']['items'], 'sku'); + $expectedProductsAsc = ['simple1002', 'simple1001', 'simple1000']; + // position equal and secondary sort by entity_id DESC + $this->assertEquals($expectedProductsAsc, $productsAsc); + + $queryDesc = <<<QUERY +{ + products(filter: {category_id: {eq: "$categoryId"}}, sort: {position: DESC}) { + total_count + items { + sku + name + } + } +} +QUERY; + $resultDesc = $this->graphQlQuery($queryDesc); + $this->assertArrayNotHasKey('errors', $resultDesc); + $productsDesc = array_column($resultDesc['products']['items'], 'sku'); + // position equal and secondary sort by entity_id DESC + $this->assertEquals($expectedProductsAsc, $productsDesc); + + //revert position + $productPositions = $category->getProductsPosition(); + $count = 1; + foreach ($productPositions as $productId => $position) { + $productPositions[$productId] = $count; + $count++; + } + ksort($productPositions); + + $category->setPostedProducts($productPositions); + $category->save(); + $this->reIndexAndCleanCache(); + + $queryDesc = <<<QUERY +{ + products(filter: {category_id: {eq: "$categoryId"}}, sort: {position: ASC}) { + total_count + items { + sku + name + } + } +} +QUERY; + $resultDesc = $this->graphQlQuery($queryDesc); + $this->assertArrayNotHasKey('errors', $resultDesc); + $productsDesc = array_column($resultDesc['products']['items'], 'sku'); + // position NOT equal and oldest entity first + $this->assertEquals(array_reverse($expectedProductsAsc), $productsDesc); + } + /** * pageSize = total_count and current page = 2 * expected - error is thrown @@ -1198,10 +1277,10 @@ public function testFilterByMultipleFilterFieldsSortedByMultipleSortFields() products( filter: { - price:{to :"50"} + price:{to :"50"} sku:{in:["simple1", "simple2"]} name:{match:"Simple"} - + } pageSize:4 currentPage:1 @@ -1236,10 +1315,10 @@ public function testFilterByMultipleFilterFieldsSortedByMultipleSortFields() page_size current_page } - sort_fields + sort_fields { default - options + options { value label @@ -1382,7 +1461,7 @@ public function testFilteringForProductsFromMultipleCategories() $query = <<<QUERY { - products(filter:{ + products(filter:{ category_id :{in:["4","5","12"]} }) { @@ -1429,7 +1508,7 @@ public function testFilterProductsBySingleCategoryId() category_id:{eq:"{$queryCategoryId}"} } pageSize:2 - + ) { items @@ -1446,7 +1525,7 @@ public function testFilterProductsBySingleCategoryId() } } total_count - + } } @@ -1460,6 +1539,7 @@ public function testFilterProductsBySingleCategoryId() $categoryRepository = ObjectManager::getInstance()->get(CategoryRepositoryInterface::class); $links = $productLinks->getAssignedProducts($queryCategoryId); + $links = array_reverse($links); foreach ($response['products']['items'] as $itemIndex => $itemData) { $this->assertNotEmpty($itemData); $this->assertEquals($response['products']['items'][$itemIndex]['sku'], $links[$itemIndex]->getSku()); @@ -1520,7 +1600,7 @@ public function testSearchAndSortByRelevance() ) { total_count - items + items { name sku @@ -1533,14 +1613,14 @@ public function testSearchAndSortByRelevance() filters{ name request_var - filter_items_count + filter_items_count filter_items{ label items_count value_string __typename } - } + } aggregations{ attribute_code count @@ -1550,9 +1630,9 @@ public function testSearchAndSortByRelevance() value count } - } + } } - + } QUERY; $response = $this->graphQlQuery($query); @@ -1679,7 +1759,7 @@ public function testProductBasicFullTextSearchQuery() items_count label value_string - } + } } aggregations{ attribute_code @@ -1838,11 +1918,11 @@ public function testQueryFilterNoMatchingItems() { products( filter: - { + { price:{from:"50"} - + description:{match:"Description"} - + } pageSize:2 currentPage:1 @@ -1995,7 +2075,7 @@ public function testFilterProductsThatAreOutOfStockWithConfigSettings() sku:{eq:"simple_visible_in_stock"} } pageSize:20 - + ) { items @@ -2004,7 +2084,7 @@ public function testFilterProductsThatAreOutOfStockWithConfigSettings() name } total_count - + } } QUERY; diff --git a/dev/tests/api-functional/testsuite/Magento/GraphQl/Catalog/ProductViewTest.php b/dev/tests/api-functional/testsuite/Magento/GraphQl/Catalog/ProductViewTest.php index 3ade1a0ef17d0..9d6a5e6d414e0 100644 --- a/dev/tests/api-functional/testsuite/Magento/GraphQl/Catalog/ProductViewTest.php +++ b/dev/tests/api-functional/testsuite/Magento/GraphQl/Catalog/ProductViewTest.php @@ -13,7 +13,6 @@ use Magento\Catalog\Api\ProductRepositoryInterface; use Magento\Catalog\Model\Category; use Magento\Framework\DataObject; -use Magento\Framework\EntityManager\MetadataPool; use Magento\TestFramework\ObjectManager; use Magento\TestFramework\TestCase\GraphQlAbstract; @@ -270,11 +269,6 @@ public function testQueryAllFieldsSimpleProduct() /** @var ProductRepositoryInterface $productRepository */ $productRepository = ObjectManager::getInstance()->get(ProductRepositoryInterface::class); $product = $productRepository->get($productSku, false, null, true); - /** @var MetadataPool $metadataPool */ - $metadataPool = ObjectManager::getInstance()->get(MetadataPool::class); - $product->setId( - $product->getData($metadataPool->getMetadata(ProductInterface::class)->getLinkField()) - ); $this->assertArrayHasKey('products', $response); $this->assertArrayHasKey('items', $response['products']); $this->assertEquals(1, count($response['products']['items'])); @@ -656,15 +650,7 @@ public function testProductPrices() */ $productRepository = ObjectManager::getInstance()->get(ProductRepositoryInterface::class); $firstProduct = $productRepository->get($firstProductSku, false, null, true); - /** @var MetadataPool $metadataPool */ - $metadataPool = ObjectManager::getInstance()->get(MetadataPool::class); - $firstProduct->setId( - $firstProduct->getData($metadataPool->getMetadata(ProductInterface::class)->getLinkField()) - ); $secondProduct = $productRepository->get($secondProductSku, false, null, true); - $secondProduct->setId( - $secondProduct->getData($metadataPool->getMetadata(ProductInterface::class)->getLinkField()) - ); self::assertNotNull($response['products']['items'][0]['price'], "price must be not null"); self::assertCount(2, $response['products']['items']); $this->assertBaseFields($firstProduct, $response['products']['items'][0]); diff --git a/dev/tests/api-functional/testsuite/Magento/GraphQl/Catalog/VirtualProductViewTest.php b/dev/tests/api-functional/testsuite/Magento/GraphQl/Catalog/VirtualProductViewTest.php index 58b6d4f0e4ea2..80206b232585f 100644 --- a/dev/tests/api-functional/testsuite/Magento/GraphQl/Catalog/VirtualProductViewTest.php +++ b/dev/tests/api-functional/testsuite/Magento/GraphQl/Catalog/VirtualProductViewTest.php @@ -9,7 +9,6 @@ use Magento\Catalog\Api\Data\ProductInterface; use Magento\Catalog\Api\ProductRepositoryInterface; -use Magento\Framework\EntityManager\MetadataPool; use Magento\TestFramework\ObjectManager; use Magento\TestFramework\TestCase\GraphQlAbstract; @@ -57,11 +56,6 @@ public function testQueryAllFieldsVirtualProduct() /** @var ProductRepositoryInterface $productRepository */ $productRepository = ObjectManager::getInstance()->get(ProductRepositoryInterface::class); $product = $productRepository->get($productSku, false, null, true); - /** @var MetadataPool $metadataPool */ - $metadataPool = ObjectManager::getInstance()->get(MetadataPool::class); - $product->setId( - $product->getData($metadataPool->getMetadata(ProductInterface::class)->getLinkField()) - ); $this->assertArrayHasKey('products', $response); $this->assertArrayHasKey('items', $response['products']); $this->assertEquals(1, count($response['products']['items'])); diff --git a/dev/tests/api-functional/testsuite/Magento/GraphQl/CatalogCms/CategoryBlockTest.php b/dev/tests/api-functional/testsuite/Magento/GraphQl/CatalogCms/CategoryBlockTest.php index 52985422c3355..5788313c4704e 100644 --- a/dev/tests/api-functional/testsuite/Magento/GraphQl/CatalogCms/CategoryBlockTest.php +++ b/dev/tests/api-functional/testsuite/Magento/GraphQl/CatalogCms/CategoryBlockTest.php @@ -29,7 +29,7 @@ public function testCategoryCmsBlock() $blockRepository = Bootstrap::getObjectManager()->get(BlockRepositoryInterface::class); $block = $blockRepository->getById($blockId); $filter = Bootstrap::getObjectManager()->get(FilterEmulate::class); - $renderedContent = $filter->setUseSessionInUrl(false)->filter($block->getContent()); + $renderedContent = $filter->filter($block->getContent()); /** @var CategoryRepositoryInterface $categoryRepository */ $categoryRepository = Bootstrap::getObjectManager()->get(CategoryRepositoryInterface::class); diff --git a/dev/tests/api-functional/testsuite/Magento/GraphQl/Cms/CmsBlockTest.php b/dev/tests/api-functional/testsuite/Magento/GraphQl/Cms/CmsBlockTest.php index d598a463a48a7..f0b7ab1b924b6 100644 --- a/dev/tests/api-functional/testsuite/Magento/GraphQl/Cms/CmsBlockTest.php +++ b/dev/tests/api-functional/testsuite/Magento/GraphQl/Cms/CmsBlockTest.php @@ -43,7 +43,7 @@ public function testGetCmsBlock() { $cmsBlock = $this->blockRepository->getById('enabled_block'); $cmsBlockData = $cmsBlock->getData(); - $renderedContent = $this->filterEmulate->setUseSessionInUrl(false)->filter($cmsBlock->getContent()); + $renderedContent = $this->filterEmulate->filter($cmsBlock->getContent()); $query = <<<QUERY @@ -77,7 +77,7 @@ public function testGetCmsBlockByBlockId() $cmsBlock = $this->blockRepository->getById('enabled_block'); $cmsBlockData = $cmsBlock->getData(); $blockId = $cmsBlockData['block_id']; - $renderedContent = $this->filterEmulate->setUseSessionInUrl(false)->filter($cmsBlock->getContent()); + $renderedContent = $this->filterEmulate->filter($cmsBlock->getContent()); $query = <<<QUERY diff --git a/dev/tests/api-functional/testsuite/Magento/GraphQl/ConfigurableProduct/AddConfigurableProductToCartTest.php b/dev/tests/api-functional/testsuite/Magento/GraphQl/ConfigurableProduct/AddConfigurableProductToCartTest.php index b1858e843bf0f..8e6400a9a3b93 100644 --- a/dev/tests/api-functional/testsuite/Magento/GraphQl/ConfigurableProduct/AddConfigurableProductToCartTest.php +++ b/dev/tests/api-functional/testsuite/Magento/GraphQl/ConfigurableProduct/AddConfigurableProductToCartTest.php @@ -199,7 +199,7 @@ public function testAddVariationFromAnotherConfigurableProductWithDifferentSuper /** * @magentoApiDataFixture Magento/ConfigurableProduct/_files/product_configurable_sku.php * @magentoApiDataFixture Magento/Checkout/_files/active_quote.php - * @expectedException \Exception + * @expectedException Exception * @expectedExceptionMessage The requested qty is not available */ public function testAddProductIfQuantityIsNotAvailable() @@ -224,7 +224,7 @@ public function testAddProductIfQuantityIsNotAvailable() /** * @magentoApiDataFixture Magento/ConfigurableProduct/_files/product_configurable_sku.php * @magentoApiDataFixture Magento/Checkout/_files/active_quote.php - * @expectedException \Exception + * @expectedException Exception * @expectedExceptionMessage Could not find a product with SKU "configurable_no_exist" */ public function testAddNonExistentConfigurableProductParentToCart() @@ -263,7 +263,7 @@ public function testAddNonExistentConfigurableProductVariationToCart() 2000 ); - $this->expectException(\Exception::class); + $this->expectException(Exception::class); $this->expectExceptionMessage( 'Could not add the product with SKU configurable to the shopping cart: The product that was requested ' . 'doesn\'t exist. Verify the product and try again.' @@ -272,6 +272,58 @@ public function testAddNonExistentConfigurableProductVariationToCart() $this->graphQlMutation($query); } + /** + * @magentoApiDataFixture Magento/ConfigurableProduct/_files/product_configurable_disable_first_child.php + * @magentoApiDataFixture Magento/Checkout/_files/active_quote.php + */ + public function testAddDisabledVariationToCart() + { + $searchResponse = $this->graphQlQuery($this->getFetchProductQuery('configurable')); + $product = current($searchResponse['products']['items']); + $maskedQuoteId = $this->getMaskedQuoteIdByReservedOrderId->execute('test_order_1'); + $parentSku = $product['sku']; + $sku = 'simple_10'; + $query = $this->getQuery( + $maskedQuoteId, + $parentSku, + $sku, + 1 + ); + + $this->expectException(Exception::class); + $this->expectExceptionMessage( + 'Could not add the product with SKU configurable to the shopping cart' + ); + + $this->graphQlMutation($query); + } + + /** + * @magentoApiDataFixture Magento/ConfigurableProduct/_files/product_configurable_zero_qty_first_child.php + * @magentoApiDataFixture Magento/Checkout/_files/active_quote.php + */ + public function testOutOfStockVariationToCart() + { + $searchResponse = $this->graphQlQuery($this->getFetchProductQuery('configurable')); + $product = current($searchResponse['products']['items']); + $maskedQuoteId = $this->getMaskedQuoteIdByReservedOrderId->execute('test_order_1'); + $parentSku = $product['sku']; + $sku = 'simple_10'; + $query = $this->getQuery( + $maskedQuoteId, + $parentSku, + $sku, + 1 + ); + + $this->expectException(Exception::class); + $this->expectExceptionMessage( + 'Could not add the product with SKU configurable to the shopping cart' + ); + + $this->graphQlMutation($query); + } + /** * @param string $maskedQuoteId * @param string $parentSku diff --git a/dev/tests/api-functional/testsuite/Magento/GraphQl/ConfigurableProduct/ConfigurableProductViewTest.php b/dev/tests/api-functional/testsuite/Magento/GraphQl/ConfigurableProduct/ConfigurableProductViewTest.php index 4729cae92717d..4837e2c6ec98a 100644 --- a/dev/tests/api-functional/testsuite/Magento/GraphQl/ConfigurableProduct/ConfigurableProductViewTest.php +++ b/dev/tests/api-functional/testsuite/Magento/GraphQl/ConfigurableProduct/ConfigurableProductViewTest.php @@ -243,7 +243,7 @@ private function assertBaseFields($product, $actualResponse) 'expected_value' => $product->getData( $metadataPool->getMetadata( ProductInterface::class - )->getLinkField() + )->getIdentifierField() ) ], ['response_field' => 'name', 'expected_value' => $product->getName()], diff --git a/dev/tests/api-functional/testsuite/Magento/GraphQl/Customer/CreateCustomerTest.php b/dev/tests/api-functional/testsuite/Magento/GraphQl/Customer/CreateCustomerTest.php index a6455a9728fec..3da51088f0af6 100644 --- a/dev/tests/api-functional/testsuite/Magento/GraphQl/Customer/CreateCustomerTest.php +++ b/dev/tests/api-functional/testsuite/Magento/GraphQl/Customer/CreateCustomerTest.php @@ -172,24 +172,25 @@ public function testCreateCustomerIfEmailMissed() } /** - * @expectedException \Exception - * @expectedExceptionMessage "Email" is not a valid email address. + * @dataProvider invalidEmailAddressDataProvider + * + * @param string $email + * @throws \Exception */ - public function testCreateCustomerIfEmailIsNotValid() + public function testCreateCustomerIfEmailIsNotValid(string $email) { - $newFirstname = 'Richard'; - $newLastname = 'Rowe'; - $currentPassword = 'test123#'; - $newEmail = 'email'; + $firstname = 'Richard'; + $lastname = 'Rowe'; + $password = 'test123#'; $query = <<<QUERY mutation { createCustomer( input: { - firstname: "{$newFirstname}" - lastname: "{$newLastname}" - email: "{$newEmail}" - password: "{$currentPassword}" + firstname: "{$firstname}" + lastname: "{$lastname}" + email: "{$email}" + password: "{$password}" is_subscribed: true } ) { @@ -203,9 +204,29 @@ public function testCreateCustomerIfEmailIsNotValid() } } QUERY; + $this->expectExceptionMessage('"' . $email . '" is not a valid email address.'); $this->graphQlMutation($query); } + /** + * @return array + */ + public function invalidEmailAddressDataProvider(): array + { + return [ + ['plainaddress'], + ['jØrgen@somedomain.com'], + ['#@%^%#$@#$@#.com'], + ['@example.com'], + ['Joe Smith <email@example.com>'], + ['email.example.com'], + ['email@example@example.com'], + ['email@example.com (Joe Smith)'], + ['email@example'], + ['“email”@example.com'], + ]; + } + /** * @expectedException \Exception * @expectedExceptionMessage Field "test123" is not defined by type CustomerInput. diff --git a/dev/tests/api-functional/testsuite/Magento/GraphQl/Quote/Customer/AddSimpleProductToCartTest.php b/dev/tests/api-functional/testsuite/Magento/GraphQl/Quote/Customer/AddSimpleProductToCartTest.php index 0c676d86a33da..aca98e946054c 100644 --- a/dev/tests/api-functional/testsuite/Magento/GraphQl/Quote/Customer/AddSimpleProductToCartTest.php +++ b/dev/tests/api-functional/testsuite/Magento/GraphQl/Quote/Customer/AddSimpleProductToCartTest.php @@ -82,32 +82,6 @@ public function testAddSimpleProductToCart() self::assertEquals('USD', $rowTotalIncludingTax['currency']); } - /** - * @magentoApiDataFixture Magento/Customer/_files/customer.php - * @expectedException Exception - * @expectedExceptionMessage Field AddSimpleProductsToCartInput.cart_id of required type String! was not provided. - */ - public function testAddSimpleProductToCartIfCartIdIsMissed() - { - $query = <<<QUERY -mutation { - addSimpleProductsToCart( - input: { - cart_items: [] - } - ) { - cart { - items { - id - } - } - } -} -QUERY; - - $this->graphQlMutation($query, [], '', $this->getHeaderMap()); - } - /** * @magentoApiDataFixture Magento/Customer/_files/customer.php * @expectedException Exception @@ -135,36 +109,6 @@ public function testAddSimpleProductToCartIfCartIdIsEmpty() $this->graphQlMutation($query, [], '', $this->getHeaderMap()); } - /** - * @magentoApiDataFixture Magento/Customer/_files/customer.php - * @expectedException Exception - */ - public function testAddSimpleProductToCartIfCartItemsAreMissed() - { - $query = <<<QUERY -mutation { - addSimpleProductsToCart( - input: { - cart_id: "cart_id" - } - ) { - cart { - items { - id - } - } - } -} -QUERY; - - $this->expectExceptionMessage( - 'Field AddSimpleProductsToCartInput.cart_items of required type' - . ' [SimpleProductCartItemInput]! was not provided.' - ); - - $this->graphQlMutation($query, [], '', $this->getHeaderMap()); - } - /** * @magentoApiDataFixture Magento/Customer/_files/customer.php * @expectedException Exception diff --git a/dev/tests/api-functional/testsuite/Magento/GraphQl/Quote/Customer/AddVirtualProductToCartTest.php b/dev/tests/api-functional/testsuite/Magento/GraphQl/Quote/Customer/AddVirtualProductToCartTest.php index a7a3028f2a369..4805721de625a 100644 --- a/dev/tests/api-functional/testsuite/Magento/GraphQl/Quote/Customer/AddVirtualProductToCartTest.php +++ b/dev/tests/api-functional/testsuite/Magento/GraphQl/Quote/Customer/AddVirtualProductToCartTest.php @@ -54,32 +54,6 @@ public function testAddVirtualProductToCart() self::assertEquals($sku, $response['addVirtualProductsToCart']['cart']['items'][0]['product']['sku']); } - /** - * @magentoApiDataFixture Magento/Customer/_files/customer.php - * @expectedException Exception - * @expectedExceptionMessage Field AddSimpleProductsToCartInput.cart_id of required type String! was not provided. - */ - public function testAddVirtualProductToCartIfCartIdIsMissed() - { - $query = <<<QUERY -mutation { - addSimpleProductsToCart( - input: { - cart_items: [] - } - ) { - cart { - items { - id - } - } - } -} -QUERY; - - $this->graphQlMutation($query, [], '', $this->getHeaderMap()); - } - /** * @magentoApiDataFixture Magento/Customer/_files/customer.php * @expectedException Exception @@ -107,35 +81,6 @@ public function testAddVirtualProductToCartIfCartIdIsEmpty() $this->graphQlMutation($query, [], '', $this->getHeaderMap()); } - /** - * @magentoApiDataFixture Magento/Customer/_files/customer.php - */ - public function testAddVirtualProductToCartIfCartItemsAreMissed() - { - $query = <<<QUERY -mutation { - addSimpleProductsToCart( - input: { - cart_id: "cart_id" - } - ) { - cart { - items { - id - } - } - } -} -QUERY; - $this->expectException(\Exception::class); - $this->expectExceptionMessage( - 'Field AddSimpleProductsToCartInput.cart_items of required type [SimpleProductCartItemInput]!' - . ' was not provided.' - ); - - $this->graphQlMutation($query, [], '', $this->getHeaderMap()); - } - /** * @magentoApiDataFixture Magento/Customer/_files/customer.php * @expectedException Exception diff --git a/dev/tests/api-functional/testsuite/Magento/GraphQl/Quote/Customer/ApplyCouponToCartTest.php b/dev/tests/api-functional/testsuite/Magento/GraphQl/Quote/Customer/ApplyCouponToCartTest.php index fa96443eaee1e..d96bf77f2ef0e 100644 --- a/dev/tests/api-functional/testsuite/Magento/GraphQl/Quote/Customer/ApplyCouponToCartTest.php +++ b/dev/tests/api-functional/testsuite/Magento/GraphQl/Quote/Customer/ApplyCouponToCartTest.php @@ -183,49 +183,6 @@ public function testApplyCouponWhichIsNotApplicable() $this->graphQlMutation($query, [], '', $this->getHeaderMap()); } - /** - * @param string $input - * @param string $message - * @dataProvider dataProviderUpdateWithMissedRequiredParameters - * @magentoApiDataFixture Magento/Customer/_files/customer.php - * @magentoApiDataFixture Magento/GraphQl/Quote/_files/customer/create_empty_cart.php - * @expectedException \Exception - */ - public function testApplyCouponWithMissedRequiredParameters(string $input, string $message) - { - $query = <<<QUERY -mutation { - applyCouponToCart(input: {{$input}}) { - cart { - applied_coupon { - code - } - } - } -} -QUERY; - - $this->expectExceptionMessage($message); - $this->graphQlMutation($query, [], '', $this->getHeaderMap()); - } - - /** - * @return array - */ - public function dataProviderUpdateWithMissedRequiredParameters(): array - { - return [ - 'missed_cart_id' => [ - 'coupon_code: "test"', - 'Field ApplyCouponToCartInput.cart_id of required type String! was not provided.' - ], - 'missed_coupon_code' => [ - 'cart_id: "test_quote"', - 'Field ApplyCouponToCartInput.coupon_code of required type String! was not provided.' - ], - ]; - } - /** * Retrieve customer authorization headers * diff --git a/dev/tests/api-functional/testsuite/Magento/GraphQl/Quote/Customer/GetAvailableShippingMethodsTest.php b/dev/tests/api-functional/testsuite/Magento/GraphQl/Quote/Customer/GetAvailableShippingMethodsTest.php index f950d35f54658..ded3caa0d812c 100644 --- a/dev/tests/api-functional/testsuite/Magento/GraphQl/Quote/Customer/GetAvailableShippingMethodsTest.php +++ b/dev/tests/api-functional/testsuite/Magento/GraphQl/Quote/Customer/GetAvailableShippingMethodsTest.php @@ -82,6 +82,51 @@ public function testGetAvailableShippingMethods() ); } + /** + * Test case: get available shipping methods from current customer quote with configurable product + * + * @magentoApiDataFixture Magento/Customer/_files/customer.php + * @magentoApiDataFixture Magento/GraphQl/Quote/_files/customer/create_empty_cart.php + * @magentoApiDataFixture Magento/CatalogRule/_files/configurable_product.php + * @magentoApiDataFixture Magento/GraphQl/Quote/_files/add_configurable_product.php + * @magentoApiDataFixture Magento/GraphQl/Quote/_files/set_new_shipping_address.php + */ + public function testGetAvailableShippingMethodsWithConfigurableProduct() + { + $maskedQuoteId = $this->getMaskedQuoteIdByReservedOrderId->execute('test_quote'); + $response = $this->graphQlQuery($this->getQuery($maskedQuoteId), [], '', $this->getHeaderMap()); + + self::assertArrayHasKey('cart', $response); + self::assertArrayHasKey('shipping_addresses', $response['cart']); + self::assertCount(1, $response['cart']['shipping_addresses']); + self::assertArrayHasKey('available_shipping_methods', $response['cart']['shipping_addresses'][0]); + self::assertCount(1, $response['cart']['shipping_addresses'][0]['available_shipping_methods']); + + $expectedAddressData = [ + 'amount' => [ + 'value' => 5, + 'currency' => 'USD', + ], + 'carrier_code' => 'flatrate', + 'carrier_title' => 'Flat Rate', + 'error_message' => '', + 'method_code' => 'flatrate', + 'method_title' => 'Fixed', + 'price_incl_tax' => [ + 'value' => 5, + 'currency' => 'USD', + ], + 'price_excl_tax' => [ + 'value' => 5, + 'currency' => 'USD', + ], + ]; + self::assertEquals( + $expectedAddressData, + $response['cart']['shipping_addresses'][0]['available_shipping_methods'][0] + ); + } + /** * _security * @magentoApiDataFixture Magento/Customer/_files/customer.php diff --git a/dev/tests/api-functional/testsuite/Magento/GraphQl/Quote/Customer/GetCartTest.php b/dev/tests/api-functional/testsuite/Magento/GraphQl/Quote/Customer/GetCartTest.php index 7ffce2a7f541d..90ee6caec6797 100644 --- a/dev/tests/api-functional/testsuite/Magento/GraphQl/Quote/Customer/GetCartTest.php +++ b/dev/tests/api-functional/testsuite/Magento/GraphQl/Quote/Customer/GetCartTest.php @@ -162,7 +162,7 @@ public function testGetNonExistentCart() * @magentoApiDataFixture Magento/GraphQl/Quote/_files/make_cart_inactive.php * * @expectedException Exception - * @expectedExceptionMessage Current user does not have an active cart. + * @expectedExceptionMessage The cart isn't active. */ public function testGetInactiveCart() { diff --git a/dev/tests/api-functional/testsuite/Magento/GraphQl/Quote/Customer/GetCustomerCartTest.php b/dev/tests/api-functional/testsuite/Magento/GraphQl/Quote/Customer/GetCustomerCartTest.php index 8100bce4ac718..6ee9bcc516172 100644 --- a/dev/tests/api-functional/testsuite/Magento/GraphQl/Quote/Customer/GetCustomerCartTest.php +++ b/dev/tests/api-functional/testsuite/Magento/GraphQl/Quote/Customer/GetCustomerCartTest.php @@ -11,6 +11,8 @@ use Magento\GraphQl\Quote\GetMaskedQuoteIdByReservedOrderId; use Magento\Integration\Api\CustomerTokenServiceInterface; use Magento\TestFramework\Helper\Bootstrap; +use Magento\Quote\Model\ResourceModel\Quote\Collection; +use Magento\Framework\ObjectManagerInterface; use Magento\TestFramework\TestCase\GraphQlAbstract; /** @@ -28,11 +30,29 @@ class GetCustomerCartTest extends GraphQlAbstract */ private $customerTokenService; + /** + * @var ObjectManagerInterface + */ + private $objectManager; + protected function setUp() { - $objectManager = Bootstrap::getObjectManager(); - $this->getMaskedQuoteIdByReservedOrderId = $objectManager->get(GetMaskedQuoteIdByReservedOrderId::class); - $this->customerTokenService = $objectManager->get(CustomerTokenServiceInterface::class); + $this->objectManager = Bootstrap::getObjectManager(); + $this->getMaskedQuoteIdByReservedOrderId = $this->objectManager->get(GetMaskedQuoteIdByReservedOrderId::class); + $this->customerTokenService = $this->objectManager->get(CustomerTokenServiceInterface::class); + } + + /** + * @inheritdoc + */ + protected function tearDown() + { + /** @var \Magento\Quote\Model\Quote $quote */ + $quoteCollection = $this->objectManager->create(Collection::class); + foreach ($quoteCollection as $quote) { + $quote->delete(); + } + parent::tearDown(); } /** @@ -177,9 +197,8 @@ public function testGetInactiveCustomerCart() */ public function testGetCustomerCartSecondStore() { - $maskedQuoteIdSecondStore = $this->getMaskedQuoteIdByReservedOrderId->execute('test_order_1_not_default_store'); $customerCartQuery = $this->getCustomerCartQuery(); - + $maskedQuoteIdSecondStore = $this->getMaskedQuoteIdByReservedOrderId->execute('test_order_1_not_default_store'); $headerMap = $this->getHeaderMap(); $headerMap['Store'] = 'fixture_second_store'; $responseSecondStore = $this->graphQlQuery($customerCartQuery, [], '', $headerMap); diff --git a/dev/tests/api-functional/testsuite/Magento/GraphQl/Quote/Customer/GetSelectedPaymentMethodTest.php b/dev/tests/api-functional/testsuite/Magento/GraphQl/Quote/Customer/GetSelectedPaymentMethodTest.php index 74a311932991e..cd96e3af6f012 100644 --- a/dev/tests/api-functional/testsuite/Magento/GraphQl/Quote/Customer/GetSelectedPaymentMethodTest.php +++ b/dev/tests/api-functional/testsuite/Magento/GraphQl/Quote/Customer/GetSelectedPaymentMethodTest.php @@ -34,7 +34,6 @@ class GetSelectedPaymentMethodTest extends GraphQlAbstract * @magentoConfigFixture default_store payment/cashondelivery/active 1 * @magentoConfigFixture default_store payment/checkmo/active 1 * @magentoConfigFixture default_store payment/purchaseorder/active 1 - * @magentoConfigFixture default_store payment/authorizenet_acceptjs/active 1 * @magentoApiDataFixture Magento/GraphQl/Quote/_files/customer/create_empty_cart.php * @magentoApiDataFixture Magento/GraphQl/Quote/_files/add_simple_product.php * @magentoApiDataFixture Magento/GraphQl/Quote/_files/set_new_shipping_address.php @@ -60,7 +59,6 @@ public function testGetSelectedPaymentMethod() * @magentoConfigFixture default_store payment/cashondelivery/active 1 * @magentoConfigFixture default_store payment/checkmo/active 1 * @magentoConfigFixture default_store payment/purchaseorder/active 1 - * @magentoConfigFixture default_store payment/authorizenet_acceptjs/active 1 * @magentoApiDataFixture Magento/GraphQl/Quote/_files/customer/create_empty_cart.php * @magentoApiDataFixture Magento/GraphQl/Quote/_files/add_simple_product.php * @magentoApiDataFixture Magento/GraphQl/Quote/_files/set_new_shipping_address.php @@ -103,7 +101,6 @@ public function testGetSelectedPaymentMethodFromNonExistentCart() * @magentoConfigFixture default_store payment/cashondelivery/active 1 * @magentoConfigFixture default_store payment/checkmo/active 1 * @magentoConfigFixture default_store payment/purchaseorder/active 1 - * @magentoConfigFixture default_store payment/authorizenet_acceptjs/active 1 * @magentoApiDataFixture Magento/GraphQl/Quote/_files/add_simple_product.php * @magentoApiDataFixture Magento/GraphQl/Quote/_files/set_new_shipping_address.php * @magentoApiDataFixture Magento/GraphQl/Quote/_files/set_checkmo_payment_method.php @@ -152,7 +149,7 @@ private function getQuery(string $maskedQuoteId): string { return <<<QUERY { - cart(cart_id:"$maskedQuoteId") { + cart(cart_id:"$maskedQuoteId") { selected_payment_method { code } diff --git a/dev/tests/api-functional/testsuite/Magento/GraphQl/Quote/Customer/MergeCartsTest.php b/dev/tests/api-functional/testsuite/Magento/GraphQl/Quote/Customer/MergeCartsTest.php index b78c8894970b5..695857f781b23 100644 --- a/dev/tests/api-functional/testsuite/Magento/GraphQl/Quote/Customer/MergeCartsTest.php +++ b/dev/tests/api-functional/testsuite/Magento/GraphQl/Quote/Customer/MergeCartsTest.php @@ -108,7 +108,7 @@ public function testMergeGuestWithCustomerCart() * @magentoApiDataFixture Magento/GraphQl/Quote/_files/customer/create_empty_cart.php * @magentoApiDataFixture Magento/GraphQl/Quote/_files/add_simple_product.php * @expectedException \Exception - * @expectedExceptionMessage Current user does not have an active cart. + * @expectedExceptionMessage The cart isn't active. */ public function testGuestCartExpiryAfterMerge() { @@ -163,6 +163,48 @@ public function testMergeTwoCustomerCarts() $this->graphQlMutation($query, [], '', $this->getHeaderMap()); } + /** + * @magentoApiDataFixture Magento/Customer/_files/customer.php + * @magentoApiDataFixture Magento/GraphQl/Catalog/_files/simple_product.php + * @magentoApiDataFixture Magento/GraphQl/Quote/_files/customer/create_empty_cart.php + * @magentoApiDataFixture Magento/GraphQl/Quote/_files/add_simple_product.php + * @expectedException \Exception + * @expectedExceptionMessage Required parameter "source_cart_id" is missing + */ + public function testMergeCartsWithEmptySourceCartId() + { + $customerQuote = $this->quoteFactory->create(); + $this->quoteResource->load($customerQuote, 'test_quote', 'reserved_order_id'); + + $customerQuoteMaskedId = $this->quoteIdToMaskedId->execute((int)$customerQuote->getId()); + $guestQuoteMaskedId = ""; + + $query = $this->getCartMergeMutation($guestQuoteMaskedId, $customerQuoteMaskedId); + $this->graphQlMutation($query, [], '', $this->getHeaderMap()); + } + + /** + * @magentoApiDataFixture Magento/Checkout/_files/quote_with_virtual_product_saved.php + * @magentoApiDataFixture Magento/Customer/_files/customer.php + * @expectedException \Exception + * @expectedExceptionMessage Required parameter "destination_cart_id" is missing + */ + public function testMergeCartsWithEmptyDestinationCartId() + { + $guestQuote = $this->quoteFactory->create(); + $this->quoteResource->load( + $guestQuote, + 'test_order_with_virtual_product_without_address', + 'reserved_order_id' + ); + + $customerQuoteMaskedId = ""; + $guestQuoteMaskedId = $this->quoteIdToMaskedId->execute((int)$guestQuote->getId()); + + $query = $this->getCartMergeMutation($guestQuoteMaskedId, $customerQuoteMaskedId); + $this->graphQlMutation($query, [], '', $this->getHeaderMap()); + } + /** * Add simple product to cart * diff --git a/dev/tests/api-functional/testsuite/Magento/GraphQl/Quote/Customer/PlaceOrderTest.php b/dev/tests/api-functional/testsuite/Magento/GraphQl/Quote/Customer/PlaceOrderTest.php index 189d5ceab838d..11dc10beb72e2 100644 --- a/dev/tests/api-functional/testsuite/Magento/GraphQl/Quote/Customer/PlaceOrderTest.php +++ b/dev/tests/api-functional/testsuite/Magento/GraphQl/Quote/Customer/PlaceOrderTest.php @@ -69,7 +69,6 @@ protected function setUp() * @magentoConfigFixture default_store payment/cashondelivery/active 1 * @magentoConfigFixture default_store payment/checkmo/active 1 * @magentoConfigFixture default_store payment/purchaseorder/active 1 - * @magentoConfigFixture default_store payment/authorizenet_acceptjs/active 1 * @magentoApiDataFixture Magento/GraphQl/Quote/_files/customer/create_empty_cart.php * @magentoApiDataFixture Magento/GraphQl/Quote/_files/add_simple_product.php * @magentoApiDataFixture Magento/GraphQl/Quote/_files/set_new_shipping_address.php @@ -103,26 +102,6 @@ public function testPlaceOrderIfCartIdIsEmpty() $this->graphQlMutation($query, [], '', $this->getHeaderMap()); } - /** - * @magentoApiDataFixture Magento/Customer/_files/customer.php - * @expectedException Exception - * @expectedExceptionMessage Field PlaceOrderInput.cart_id of required type String! was not provided. - */ - public function testPlaceOrderIfCartIdIsMissed() - { - $query = <<<QUERY -mutation { - placeOrder(input: {}) { - order { - order_number - } - } -} -QUERY; - - $this->graphQlMutation($query, [], '', $this->getHeaderMap()); - } - /** * @magentoApiDataFixture Magento/Customer/_files/customer.php * @magentoApiDataFixture Magento/GraphQl/Quote/_files/customer/create_empty_cart.php @@ -256,7 +235,6 @@ public function testPlaceOrderWithOutOfStockProduct() * @magentoConfigFixture default_store payment/cashondelivery/active 1 * @magentoConfigFixture default_store payment/checkmo/active 1 * @magentoConfigFixture default_store payment/purchaseorder/active 1 - * @magentoConfigFixture default_store payment/authorizenet_acceptjs/active 1 * @magentoApiDataFixture Magento/GraphQl/Quote/_files/guest/create_empty_cart.php * @magentoApiDataFixture Magento/GraphQl/Quote/_files/add_simple_product.php * @magentoApiDataFixture Magento/GraphQl/Quote/_files/set_new_shipping_address.php @@ -285,7 +263,6 @@ public function testPlaceOrderOfGuestCart() * @magentoConfigFixture default_store payment/cashondelivery/active 1 * @magentoConfigFixture default_store payment/checkmo/active 1 * @magentoConfigFixture default_store payment/purchaseorder/active 1 - * @magentoConfigFixture default_store payment/authorizenet_acceptjs/active 1 * @magentoApiDataFixture Magento/GraphQl/Quote/_files/customer/create_empty_cart.php * @magentoApiDataFixture Magento/GraphQl/Quote/_files/add_simple_product.php * @magentoApiDataFixture Magento/GraphQl/Quote/_files/set_new_shipping_address.php diff --git a/dev/tests/api-functional/testsuite/Magento/GraphQl/Quote/Customer/RemoveCouponFromCartTest.php b/dev/tests/api-functional/testsuite/Magento/GraphQl/Quote/Customer/RemoveCouponFromCartTest.php index d4390e902a3f9..1b5a308b5a9a8 100644 --- a/dev/tests/api-functional/testsuite/Magento/GraphQl/Quote/Customer/RemoveCouponFromCartTest.php +++ b/dev/tests/api-functional/testsuite/Magento/GraphQl/Quote/Customer/RemoveCouponFromCartTest.php @@ -70,29 +70,6 @@ public function testRemoveCouponFromCartIfCartIdIsEmpty() $this->graphQlMutation($query, [], '', $this->getHeaderMap()); } - /** - * @magentoApiDataFixture Magento/Customer/_files/customer.php - * @expectedException Exception - * @expectedExceptionMessage Field RemoveCouponFromCartInput.cart_id of required type String! was not provided. - */ - public function testRemoveCouponFromCartIfCartIdIsMissed() - { - $query = <<<QUERY -mutation { - removeCouponFromCart(input: {}) { - cart { - applied_coupon { - code - } - } - } -} - -QUERY; - - $this->graphQlMutation($query, [], '', $this->getHeaderMap()); - } - /** * @magentoApiDataFixture Magento/Customer/_files/customer.php * @expectedException Exception diff --git a/dev/tests/api-functional/testsuite/Magento/GraphQl/Quote/Customer/RemoveItemFromCartTest.php b/dev/tests/api-functional/testsuite/Magento/GraphQl/Quote/Customer/RemoveItemFromCartTest.php index a14aacc974af6..806cd08415ac1 100644 --- a/dev/tests/api-functional/testsuite/Magento/GraphQl/Quote/Customer/RemoveItemFromCartTest.php +++ b/dev/tests/api-functional/testsuite/Magento/GraphQl/Quote/Customer/RemoveItemFromCartTest.php @@ -84,56 +84,12 @@ public function testRemoveNonExistentItem() $maskedQuoteId = $this->getMaskedQuoteIdByReservedOrderId->execute('test_quote'); $notExistentItemId = 999; - $this->expectExceptionMessage("Cart doesn't contain the {$notExistentItemId} item."); + $this->expectExceptionMessage("The cart doesn't contain the item"); $query = $this->getQuery($maskedQuoteId, $notExistentItemId); $this->graphQlMutation($query, [], '', $this->getHeaderMap()); } - /** - * @magentoApiDataFixture Magento/Customer/_files/customer.php - * @param string $input - * @param string $message - * @dataProvider dataProviderUpdateWithMissedRequiredParameters - */ - public function testUpdateWithMissedItemRequiredParameters(string $input, string $message) - { - $query = <<<QUERY -mutation { - removeItemFromCart( - input: { - {$input} - } - ) { - cart { - items { - quantity - } - } - } -} -QUERY; - $this->expectExceptionMessage($message); - $this->graphQlMutation($query, [], '', $this->getHeaderMap()); - } - - /** - * @return array - */ - public function dataProviderUpdateWithMissedRequiredParameters(): array - { - return [ - 'missed_cart_id' => [ - 'cart_item_id: 1', - 'Field RemoveItemFromCartInput.cart_id of required type String! was not provided.' - ], - 'missed_cart_item_id' => [ - 'cart_id: "test_quote"', - 'Field RemoveItemFromCartInput.cart_item_id of required type Int! was not provided.' - ], - ]; - } - /** * _security * @magentoApiDataFixture Magento/Customer/_files/customer.php @@ -150,7 +106,7 @@ public function testRemoveItemIfItemIsNotBelongToCart() 'virtual-product' ); - $this->expectExceptionMessage("Cart doesn't contain the {$secondQuoteItemId} item."); + $this->expectExceptionMessage("The cart doesn't contain the item"); $query = $this->getQuery($firstQuoteMaskedId, $secondQuoteItemId); $this->graphQlMutation($query, [], '', $this->getHeaderMap()); @@ -199,6 +155,40 @@ public function testRemoveItemFromAnotherCustomerCart() $this->graphQlMutation($query, [], '', $this->getHeaderMap('customer2@search.example.com')); } + /** + * @magentoApiDataFixture Magento/Customer/_files/customer.php + * @magentoApiDataFixture Magento/GraphQl/Catalog/_files/simple_product.php + * @magentoApiDataFixture Magento/GraphQl/Quote/_files/customer/create_empty_cart.php + * @magentoApiDataFixture Magento/GraphQl/Quote/_files/add_simple_product.php + */ + public function testRemoveItemWithEmptyCartId() + { + $cartId = ""; + $cartItemId = $this->getQuoteItemIdByReservedQuoteIdAndSku->execute('test_quote', 'simple_product'); + + $this->expectExceptionMessage("Required parameter \"cart_id\" is missing."); + + $query = $this->getQuery($cartId, $cartItemId); + $this->graphQlMutation($query, [], '', $this->getHeaderMap()); + } + + /** + * @magentoApiDataFixture Magento/Customer/_files/customer.php + * @magentoApiDataFixture Magento/GraphQl/Catalog/_files/simple_product.php + * @magentoApiDataFixture Magento/GraphQl/Quote/_files/customer/create_empty_cart.php + * @magentoApiDataFixture Magento/GraphQl/Quote/_files/add_simple_product.php + */ + public function testRemoveItemWithZeroCartItemId() + { + $cartId = $this->getMaskedQuoteIdByReservedOrderId->execute('test_quote'); + $cartItemId = 0; + + $this->expectExceptionMessage("Required parameter \"cart_item_id\" is missing."); + + $query = $this->getQuery($cartId, $cartItemId); + $this->graphQlMutation($query, [], '', $this->getHeaderMap()); + } + /** * @param string $maskedQuoteId * @param int $itemId diff --git a/dev/tests/api-functional/testsuite/Magento/GraphQl/Quote/Customer/SetBillingAddressOnCartTest.php b/dev/tests/api-functional/testsuite/Magento/GraphQl/Quote/Customer/SetBillingAddressOnCartTest.php index 19a10d9466a32..b7376e91f705e 100644 --- a/dev/tests/api-functional/testsuite/Magento/GraphQl/Quote/Customer/SetBillingAddressOnCartTest.php +++ b/dev/tests/api-functional/testsuite/Magento/GraphQl/Quote/Customer/SetBillingAddressOnCartTest.php @@ -653,15 +653,6 @@ public function testSetBillingAddressWithoutRequiredParameters(string $input, st public function dataProviderSetWithoutRequiredParameters(): array { return [ - 'missed_billing_address' => [ - 'cart_id: "cart_id_value"', - 'Field SetBillingAddressOnCartInput.billing_address of required type BillingAddressInput!' - . ' was not provided.', - ], - 'missed_cart_id' => [ - 'billing_address: {}', - 'Field SetBillingAddressOnCartInput.cart_id of required type String! was not provided.' - ], 'missed_region' => [ 'cart_id: "cart_id_value" billing_address: { @@ -867,7 +858,7 @@ public function testSetNewBillingAddressWithSaveInAddressBook() $searchCriteria = $this->searchCriteriaBuilder->addFilter('parent_id', $customer->getId())->create(); $addresses = $this->customerAddressRepository->getList($searchCriteria)->getItems(); - self::assertCount(1, $addresses); + self::assertCount(0, $addresses); self::assertArrayHasKey('cart', $response['setBillingAddressOnCart']); $cartResponse = $response['setBillingAddressOnCart']['cart']; @@ -880,6 +871,171 @@ public function testSetNewBillingAddressWithSaveInAddressBook() } } + /** + * @magentoApiDataFixture Magento/Customer/_files/customer.php + * @magentoApiDataFixture Magento/GraphQl/Catalog/_files/simple_product.php + * @magentoApiDataFixture Magento/GraphQl/Quote/_files/customer/create_empty_cart.php + * @magentoApiDataFixture Magento/GraphQl/Quote/_files/add_simple_product.php + */ + public function testSetBillingAddressAndPlaceOrder() + { + $maskedQuoteId = $this->getMaskedQuoteIdByReservedOrderId->execute('test_quote'); + $query = <<<QUERY +mutation { + setBillingAddressOnCart( + input: { + cart_id: "$maskedQuoteId" + billing_address: { + same_as_shipping: true + address: { + firstname: "test firstname" + lastname: "test lastname" + company: "test company" + street: ["test street 1", "test street 2"] + city: "test city" + region: "AZ" + postcode: "88776" + country_code: "US" + telephone: "88776655" + save_in_address_book: true + } + } + } + ) { + cart { + billing_address { + firstname + lastname + company + street + city + postcode + telephone + country { + code + label + } + __typename + } + } + } +} +QUERY; + $response = $this->graphQlMutation($query, [], '', $this->getHeaderMap()); + $this->graphQlMutation( + $this->getSetShippingMethodsQuery($maskedQuoteId, 'flatrate', 'flatrate'), + [], + '', + $this->getHeaderMap() + ); + $this->graphQlMutation( + $this->getSetPaymentMethodQuery( + $maskedQuoteId, + 'checkmo' + ), + [], + '', + $this->getHeaderMap() + ); + $this->graphQlMutation( + $this->getPlaceOrderQuery($maskedQuoteId), + [], + '', + $this->getHeaderMap() + ); + $customer = $this->customerRepository->get('customer@example.com'); + $searchCriteria = $this->searchCriteriaBuilder->addFilter('parent_id', $customer->getId())->create(); + $addresses = $this->customerAddressRepository->getList($searchCriteria)->getItems(); + + self::assertCount(1, $addresses); + self::assertArrayHasKey('cart', $response['setBillingAddressOnCart']); + foreach ($addresses as $address) { + $this->customerAddressRepository->delete($address); + } + } + + /** + * @magentoApiDataFixture Magento/Customer/_files/customer.php + * @magentoApiDataFixture Magento/GraphQl/Catalog/_files/simple_product.php + * @magentoApiDataFixture Magento/GraphQl/Quote/_files/customer/create_empty_cart.php + * @magentoApiDataFixture Magento/GraphQl/Quote/_files/add_simple_product.php + */ + public function testSetBillingAddressWithDefaultValueOfSaveInAddressBookAndPlaceOrder() + { + $maskedQuoteId = $this->getMaskedQuoteIdByReservedOrderId->execute('test_quote'); + $query = <<<QUERY +mutation { + setBillingAddressOnCart( + input: { + cart_id: "$maskedQuoteId" + billing_address: { + same_as_shipping: true + address: { + firstname: "test firstname" + lastname: "test lastname" + company: "test company" + street: ["test street 1", "test street 2"] + city: "test city" + region: "AZ" + postcode: "88776" + country_code: "US" + telephone: "88776655" + } + } + } + ) { + cart { + billing_address { + firstname + lastname + company + street + city + postcode + telephone + country { + code + label + } + __typename + } + } + } +} +QUERY; + $response = $this->graphQlMutation($query, [], '', $this->getHeaderMap()); + $this->graphQlMutation( + $this->getSetShippingMethodsQuery($maskedQuoteId, 'flatrate', 'flatrate'), + [], + '', + $this->getHeaderMap() + ); + $this->graphQlMutation( + $this->getSetPaymentMethodQuery( + $maskedQuoteId, + 'checkmo' + ), + [], + '', + $this->getHeaderMap() + ); + $this->graphQlMutation( + $this->getPlaceOrderQuery($maskedQuoteId), + [], + '', + $this->getHeaderMap() + ); + $customer = $this->customerRepository->get('customer@example.com'); + $searchCriteria = $this->searchCriteriaBuilder->addFilter('parent_id', $customer->getId())->create(); + $addresses = $this->customerAddressRepository->getList($searchCriteria)->getItems(); + + $this->assertCount(1, $addresses); + $this->assertArrayHasKey('cart', $response['setBillingAddressOnCart']); + foreach ($addresses as $address) { + $this->customerAddressRepository->delete($address); + } + } + /** * @magentoApiDataFixture Magento/Customer/_files/customer.php * @magentoApiDataFixture Magento/GraphQl/Catalog/_files/simple_product.php @@ -1120,4 +1276,88 @@ private function assignQuoteToCustomer( $this->quoteResource->save($quote); return $this->quoteIdToMaskedId->execute((int)$quote->getId()); } + + /** + * @param string $maskedQuoteId + * @param string $shippingMethodCode + * @param string $shippingCarrierCode + * @return string + */ + private function getSetShippingMethodsQuery( + string $maskedQuoteId, + string $shippingMethodCode, + string $shippingCarrierCode + ): string { + return <<<QUERY +mutation { + setShippingMethodsOnCart(input: + { + cart_id: "$maskedQuoteId", + shipping_methods: [{ + carrier_code: "$shippingCarrierCode" + method_code: "$shippingMethodCode" + }] + }) { + cart { + shipping_addresses { + selected_shipping_method { + carrier_code + method_code + carrier_title + method_title + amount { + value + currency + } + } + } + } + } +} +QUERY; + } + + /** + * @param string $maskedQuoteId + * @param string $methodCode + * @return string + */ + private function getSetPaymentMethodQuery( + string $maskedQuoteId, + string $methodCode + ) : string { + return <<<QUERY +mutation { + setPaymentMethodOnCart(input: { + cart_id: "$maskedQuoteId" + payment_method: { + code: "$methodCode" + } + }) { + cart { + selected_payment_method { + code + } + } + } +} +QUERY; + } + + /** + * @param string $maskedQuoteId + * @return string + */ + private function getPlaceOrderQuery(string $maskedQuoteId): string + { + return <<<QUERY +mutation { + placeOrder(input: {cart_id: "{$maskedQuoteId}"}) { + order { + order_number + } + } +} +QUERY; + } } diff --git a/dev/tests/api-functional/testsuite/Magento/GraphQl/Quote/Customer/SetOfflinePaymentMethodsOnCartTest.php b/dev/tests/api-functional/testsuite/Magento/GraphQl/Quote/Customer/SetOfflinePaymentMethodsOnCartTest.php index 138d27ef26483..dbd6bb90f9a03 100644 --- a/dev/tests/api-functional/testsuite/Magento/GraphQl/Quote/Customer/SetOfflinePaymentMethodsOnCartTest.php +++ b/dev/tests/api-functional/testsuite/Magento/GraphQl/Quote/Customer/SetOfflinePaymentMethodsOnCartTest.php @@ -51,7 +51,6 @@ protected function setUp() * @magentoConfigFixture default_store payment/cashondelivery/active 1 * @magentoConfigFixture default_store payment/checkmo/active 1 * @magentoConfigFixture default_store payment/purchaseorder/active 1 - * @magentoConfigFixture default_store payment/authorizenet_acceptjs/active 1 * * @param string $methodCode * @param string $methodTitle @@ -98,7 +97,6 @@ public function offlinePaymentMethodDataProvider(): array * @magentoConfigFixture default_store payment/cashondelivery/active 1 * @magentoConfigFixture default_store payment/checkmo/active 1 * @magentoConfigFixture default_store payment/purchaseorder/active 1 - * @magentoConfigFixture default_store payment/authorizenet_acceptjs/active 1 */ public function testSetPurchaseOrderPaymentMethod() { @@ -110,7 +108,7 @@ public function testSetPurchaseOrderPaymentMethod() $query = <<<QUERY mutation { setPaymentMethodOnCart(input: { - cart_id: "{$maskedQuoteId}", + cart_id: "{$maskedQuoteId}", payment_method: { code: "{$methodCode}" purchase_order_number: "{$poNumber}" @@ -155,7 +153,7 @@ private function getQuery( return <<<QUERY mutation { setPaymentMethodOnCart(input: { - cart_id: "{$maskedQuoteId}", + cart_id: "{$maskedQuoteId}", payment_method: { code: "{$methodCode}" } diff --git a/dev/tests/api-functional/testsuite/Magento/GraphQl/Quote/Customer/SetPaymentMethodAndPlaceOrderTest.php b/dev/tests/api-functional/testsuite/Magento/GraphQl/Quote/Customer/SetPaymentMethodAndPlaceOrderTest.php index 543ce6fe9c8e7..b31ce8a7302a9 100644 --- a/dev/tests/api-functional/testsuite/Magento/GraphQl/Quote/Customer/SetPaymentMethodAndPlaceOrderTest.php +++ b/dev/tests/api-functional/testsuite/Magento/GraphQl/Quote/Customer/SetPaymentMethodAndPlaceOrderTest.php @@ -132,17 +132,6 @@ public function testSetPaymentOnCartWithException(string $input, string $message public function dataProviderSetPaymentOnCartWithException(): array { return [ - 'missed_cart_id' => [ - 'payment_method: { - code: "' . Checkmo::PAYMENT_METHOD_CHECKMO_CODE . '" - }', - 'Field SetPaymentMethodAndPlaceOrderInput.cart_id of required type String! was not provided.', - ], - 'missed_payment_method' => [ - 'cart_id: "cart_id_value"', - 'Field SetPaymentMethodAndPlaceOrderInput.payment_method of required type PaymentMethodInput!' - . ' was not provided.', - ], 'place_order_with_out_of_stock_products' => [ 'cart_id: "cart_id_value" payment_method: { diff --git a/dev/tests/api-functional/testsuite/Magento/GraphQl/Quote/Customer/SetPaymentMethodOnCartTest.php b/dev/tests/api-functional/testsuite/Magento/GraphQl/Quote/Customer/SetPaymentMethodOnCartTest.php index da190be333600..fa3cbb5a9b457 100644 --- a/dev/tests/api-functional/testsuite/Magento/GraphQl/Quote/Customer/SetPaymentMethodOnCartTest.php +++ b/dev/tests/api-functional/testsuite/Magento/GraphQl/Quote/Customer/SetPaymentMethodOnCartTest.php @@ -234,12 +234,8 @@ public function dataProviderSetPaymentMethodWithoutRequiredParameters(): array { return [ 'missed_cart_id' => [ - 'payment_method: {code: "' . Checkmo::PAYMENT_METHOD_CHECKMO_CODE . '"}', - 'Field SetPaymentMethodOnCartInput.cart_id of required type String! was not provided.' - ], - 'missed_payment_method' => [ - 'cart_id: "cart_id_value"', - 'Field SetPaymentMethodOnCartInput.payment_method of required type PaymentMethodInput! was not provided' + 'cart_id: "", payment_method: {code: "' . Checkmo::PAYMENT_METHOD_CHECKMO_CODE . '"}', + 'Required parameter "cart_id" is missing.' ], 'missed_payment_method_code' => [ 'cart_id: "cart_id_value", payment_method: {code: ""}', @@ -255,7 +251,6 @@ public function dataProviderSetPaymentMethodWithoutRequiredParameters(): array * @magentoConfigFixture default_store payment/cashondelivery/active 1 * @magentoConfigFixture default_store payment/checkmo/active 1 * @magentoConfigFixture default_store payment/purchaseorder/active 1 - * @magentoConfigFixture default_store payment/authorizenet_acceptjs/active 1 * @magentoApiDataFixture Magento/GraphQl/Quote/_files/customer/create_empty_cart.php * @magentoApiDataFixture Magento/GraphQl/Quote/_files/add_simple_product.php * @magentoApiDataFixture Magento/GraphQl/Quote/_files/set_new_shipping_address.php @@ -292,7 +287,7 @@ private function getQuery( payment_method: { code: "$methodCode" } - }) { + }) { cart { selected_payment_method { code diff --git a/dev/tests/api-functional/testsuite/Magento/GraphQl/Quote/Customer/SetPurchaseOrderPaymentMethodOnCartTest.php b/dev/tests/api-functional/testsuite/Magento/GraphQl/Quote/Customer/SetPurchaseOrderPaymentMethodOnCartTest.php index 6b9e46fd153eb..1e64679b4abd8 100644 --- a/dev/tests/api-functional/testsuite/Magento/GraphQl/Quote/Customer/SetPurchaseOrderPaymentMethodOnCartTest.php +++ b/dev/tests/api-functional/testsuite/Magento/GraphQl/Quote/Customer/SetPurchaseOrderPaymentMethodOnCartTest.php @@ -49,7 +49,6 @@ protected function setUp() * @magentoConfigFixture default_store payment/cashondelivery/active 1 * @magentoConfigFixture default_store payment/checkmo/active 1 * @magentoConfigFixture default_store payment/purchaseorder/active 1 - * @magentoConfigFixture default_store payment/authorizenet_acceptjs/active 1 */ public function testSetPurchaseOrderPaymentMethodOnCartWithSimpleProduct() { @@ -65,7 +64,7 @@ public function testSetPurchaseOrderPaymentMethodOnCartWithSimpleProduct() code: "$methodCode" purchase_order_number: "$purchaseOrderNumber" } - }) { + }) { cart { selected_payment_method { code @@ -97,7 +96,6 @@ public function testSetPurchaseOrderPaymentMethodOnCartWithSimpleProduct() * @magentoConfigFixture default_store payment/cashondelivery/active 1 * @magentoConfigFixture default_store payment/checkmo/active 1 * @magentoConfigFixture default_store payment/purchaseorder/active 1 - * @magentoConfigFixture default_store payment/authorizenet_acceptjs/active 1 * * @expectedException Exception * @expectedExceptionMessage Purchase order number is a required field. @@ -114,7 +112,7 @@ public function testSetPurchaseOrderPaymentMethodOnCartWithoutPurchaseOrderNumbe payment_method: { code: "$methodCode" } - }) { + }) { cart { selected_payment_method { code @@ -150,7 +148,7 @@ public function testSetDisabledPurchaseOrderPaymentMethodOnCart() code: "$methodCode" purchase_order_number: "$purchaseOrderNumber" } - }) { + }) { cart { selected_payment_method { code diff --git a/dev/tests/api-functional/testsuite/Magento/GraphQl/Quote/Customer/SetShippingAddressOnCartTest.php b/dev/tests/api-functional/testsuite/Magento/GraphQl/Quote/Customer/SetShippingAddressOnCartTest.php index 47a3a13f05221..9256c1c41b49f 100644 --- a/dev/tests/api-functional/testsuite/Magento/GraphQl/Quote/Customer/SetShippingAddressOnCartTest.php +++ b/dev/tests/api-functional/testsuite/Magento/GraphQl/Quote/Customer/SetShippingAddressOnCartTest.php @@ -511,19 +511,6 @@ public function testSetNewShippingAddressWithMissedRequiredStreetParameters() public function dataProviderUpdateWithMissedRequiredParameters(): array { return [ - 'missed_shipping_addresses' => [ - 'cart_id: "cart_id_value"', - 'Field SetShippingAddressesOnCartInput.shipping_addresses of required type [ShippingAddressInput]! ' . - 'was not provided.', - ], - 'missed_city' => [ - 'shipping_addresses: [ { address: { save_in_address_book: false } } ]', - 'Field CartAddressInput.city of required type String! was not provided' - ], - 'missed_cart_id' => [ - 'shipping_addresses: {}', - 'Field SetShippingAddressesOnCartInput.cart_id of required type String! was not provided.' - ], 'missed_region' => [ 'cart_id: "cart_id_value" shipping_addresses: [{ @@ -953,6 +940,99 @@ public function testSetNewShippingAddressWithSaveInAddressBook() $searchCriteria = $this->searchCriteriaBuilder->addFilter('parent_id', $customer->getId())->create(); $addresses = $this->customerAddressRepository->getList($searchCriteria)->getItems(); + self::assertCount(0, $addresses); + self::assertArrayHasKey('cart', $response['setShippingAddressesOnCart']); + + $cartResponse = $response['setShippingAddressesOnCart']['cart']; + self::assertArrayHasKey('shipping_addresses', $cartResponse); + $shippingAddressResponse = current($cartResponse['shipping_addresses']); + $this->assertNewShippingAddressFields($shippingAddressResponse); + + foreach ($addresses as $address) { + $this->customerAddressRepository->delete($address); + } + } + + /** + * @magentoApiDataFixture Magento/Customer/_files/customer.php + * @magentoApiDataFixture Magento/GraphQl/Catalog/_files/simple_product.php + * @magentoApiDataFixture Magento/GraphQl/Quote/_files/customer/create_empty_cart.php + * @magentoApiDataFixture Magento/GraphQl/Quote/_files/add_simple_product.php + * @magentoApiDataFixture Magento/GraphQl/Quote/_files/set_new_billing_address.php + */ + public function testSetNewShippingAddressAndPlaceOrder() + { + $maskedQuoteId = $this->getMaskedQuoteIdByReservedOrderId->execute('test_quote'); + $query = <<<QUERY +mutation { + setShippingAddressesOnCart( + input: { + cart_id: "$maskedQuoteId" + shipping_addresses: [ + { + address: { + firstname: "test firstname" + lastname: "test lastname" + company: "test company" + street: ["test street 1", "test street 2"] + city: "test city" + region: "AZ" + postcode: "887766" + country_code: "US" + telephone: "88776655" + save_in_address_book: true + } + customer_notes: "Test note" + } + ] + } + ) { + cart { + shipping_addresses { + firstname + lastname + company + street + city + postcode + telephone + country { + code + label + } + __typename + customer_notes + } + } + } +} +QUERY; + $response = $this->graphQlMutation($query, [], '', $this->getHeaderMap()); + $this->graphQlMutation( + $this->getSetShippingMethodsQuery($maskedQuoteId, 'flatrate', 'flatrate'), + [], + '', + $this->getHeaderMap() + ); + $this->graphQlMutation( + $this->getSetPaymentMethodQuery( + $maskedQuoteId, + 'checkmo' + ), + [], + '', + $this->getHeaderMap() + ); + $this->graphQlMutation( + $this->getPlaceOrderQuery($maskedQuoteId), + [], + '', + $this->getHeaderMap() + ); + $customer = $this->customerRepository->get('customer@example.com'); + $searchCriteria = $this->searchCriteriaBuilder->addFilter('parent_id', $customer->getId())->create(); + $addresses = $this->customerAddressRepository->getList($searchCriteria)->getItems(); + self::assertCount(1, $addresses); self::assertArrayHasKey('cart', $response['setShippingAddressesOnCart']); @@ -966,6 +1046,98 @@ public function testSetNewShippingAddressWithSaveInAddressBook() } } + /** + * @magentoApiDataFixture Magento/Customer/_files/customer.php + * @magentoApiDataFixture Magento/GraphQl/Catalog/_files/simple_product.php + * @magentoApiDataFixture Magento/GraphQl/Quote/_files/customer/create_empty_cart.php + * @magentoApiDataFixture Magento/GraphQl/Quote/_files/add_simple_product.php + * @magentoApiDataFixture Magento/GraphQl/Quote/_files/set_new_billing_address.php + */ + public function testSetNewShippingAddressWithDefaultValueOfSaveInAddressBookAndPlaceOrder() + { + $maskedQuoteId = $this->getMaskedQuoteIdByReservedOrderId->execute('test_quote'); + $query = <<<QUERY +mutation { + setShippingAddressesOnCart( + input: { + cart_id: "$maskedQuoteId" + shipping_addresses: [ + { + address: { + firstname: "test firstname" + lastname: "test lastname" + company: "test company" + street: ["test street 1", "test street 2"] + city: "test city" + region: "AZ" + postcode: "887766" + country_code: "US" + telephone: "88776655" + } + customer_notes: "Test note" + } + ] + } + ) { + cart { + shipping_addresses { + firstname + lastname + company + street + city + postcode + telephone + country { + code + label + } + __typename + customer_notes + } + } + } +} +QUERY; + $response = $this->graphQlMutation($query, [], '', $this->getHeaderMap()); + $this->graphQlMutation( + $this->getSetShippingMethodsQuery($maskedQuoteId, 'flatrate', 'flatrate'), + [], + '', + $this->getHeaderMap() + ); + $this->graphQlMutation( + $this->getSetPaymentMethodQuery( + $maskedQuoteId, + 'checkmo' + ), + [], + '', + $this->getHeaderMap() + ); + $this->graphQlMutation( + $this->getPlaceOrderQuery($maskedQuoteId), + [], + '', + $this->getHeaderMap() + ); + $customer = $this->customerRepository->get('customer@example.com'); + $searchCriteria = $this->searchCriteriaBuilder->addFilter('parent_id', $customer->getId())->create(); + $addresses = $this->customerAddressRepository->getList($searchCriteria)->getItems(); + + $this->assertCount(1, $addresses); + $this->assertArrayHasKey('cart', $response['setShippingAddressesOnCart']); + + $cartResponse = $response['setShippingAddressesOnCart']['cart']; + $this->assertArrayHasKey('shipping_addresses', $cartResponse); + $shippingAddressResponse = current($cartResponse['shipping_addresses']); + $this->assertNewShippingAddressFields($shippingAddressResponse); + + foreach ($addresses as $address) { + $this->customerAddressRepository->delete($address); + } + } + /** * @magentoApiDataFixture Magento/Customer/_files/customer.php * @magentoApiDataFixture Magento/GraphQl/Catalog/_files/simple_product.php @@ -1107,4 +1279,88 @@ private function assignQuoteToCustomer( $this->quoteResource->save($quote); return $this->quoteIdToMaskedId->execute((int)$quote->getId()); } + + /** + * @param string $maskedQuoteId + * @param string $shippingMethodCode + * @param string $shippingCarrierCode + * @return string + */ + private function getSetShippingMethodsQuery( + string $maskedQuoteId, + string $shippingMethodCode, + string $shippingCarrierCode + ): string { + return <<<QUERY +mutation { + setShippingMethodsOnCart(input: + { + cart_id: "$maskedQuoteId", + shipping_methods: [{ + carrier_code: "$shippingCarrierCode" + method_code: "$shippingMethodCode" + }] + }) { + cart { + shipping_addresses { + selected_shipping_method { + carrier_code + method_code + carrier_title + method_title + amount { + value + currency + } + } + } + } + } +} +QUERY; + } + + /** + * @param string $maskedQuoteId + * @param string $methodCode + * @return string + */ + private function getSetPaymentMethodQuery( + string $maskedQuoteId, + string $methodCode + ) : string { + return <<<QUERY +mutation { + setPaymentMethodOnCart(input: { + cart_id: "$maskedQuoteId" + payment_method: { + code: "$methodCode" + } + }) { + cart { + selected_payment_method { + code + } + } + } +} +QUERY; + } + + /** + * @param string $maskedQuoteId + * @return string + */ + private function getPlaceOrderQuery(string $maskedQuoteId): string + { + return <<<QUERY +mutation { + placeOrder(input: {cart_id: "{$maskedQuoteId}"}) { + order { + order_number + } + } +} +QUERY; + } } diff --git a/dev/tests/api-functional/testsuite/Magento/GraphQl/Quote/Customer/SetShippingMethodsOnCartTest.php b/dev/tests/api-functional/testsuite/Magento/GraphQl/Quote/Customer/SetShippingMethodsOnCartTest.php index 149a2fbb1da32..860785dd31dd2 100644 --- a/dev/tests/api-functional/testsuite/Magento/GraphQl/Quote/Customer/SetShippingMethodsOnCartTest.php +++ b/dev/tests/api-functional/testsuite/Magento/GraphQl/Quote/Customer/SetShippingMethodsOnCartTest.php @@ -39,6 +39,7 @@ protected function setUp() } /** + * @magentoConfigFixture default_store carriers/freeshipping/active 1 * @magentoApiDataFixture Magento/Customer/_files/customer.php * @magentoApiDataFixture Magento/GraphQl/Catalog/_files/simple_product.php * @magentoApiDataFixture Magento/GraphQl/Quote/_files/customer/create_empty_cart.php @@ -64,7 +65,17 @@ public function testSetShippingMethodOnCartWithSimpleProduct() self::assertCount(1, $response['setShippingMethodsOnCart']['cart']['shipping_addresses']); $shippingAddress = current($response['setShippingMethodsOnCart']['cart']['shipping_addresses']); + $availableShippingMethods = $shippingAddress['available_shipping_methods']; + self::assertArrayHasKey('selected_shipping_method', $shippingAddress); + self::assertArrayHasKey('available_shipping_methods', $shippingAddress); + + self::assertCount(2, $availableShippingMethods); + self::assertEquals('freeshipping', $availableShippingMethods[0]['carrier_code']); + self::assertEquals($carrierCode, $availableShippingMethods[1]['carrier_code']); + + self::assertEquals($availableShippingMethods[0]['amount']['value'], 0); + self::assertEquals($availableShippingMethods[1]['amount']['value'], 10); self::assertArrayHasKey('carrier_code', $shippingAddress['selected_shipping_method']); self::assertEquals('flatrate', $shippingAddress['selected_shipping_method']['carrier_code']); @@ -146,7 +157,7 @@ public function testSetShippingMethodWithWrongParameters(string $input, string $ $query = <<<QUERY mutation { setShippingMethodsOnCart(input: { - {$input} + {$input} }) { cart { shipping_addresses { @@ -169,28 +180,10 @@ public function testSetShippingMethodWithWrongParameters(string $input, string $ public function dataProviderSetShippingMethodWithWrongParameters(): array { return [ - 'missed_cart_id' => [ - 'shipping_methods: [{ - carrier_code: "flatrate" - method_code: "flatrate" - }]', - 'Field SetShippingMethodsOnCartInput.cart_id of required type String! was not provided.' - ], - 'missed_shipping_methods' => [ - 'cart_id: "cart_id_value"', - 'Field SetShippingMethodsOnCartInput.shipping_methods of required type [ShippingMethodInput]!' - . ' was not provided.' - ], 'shipping_methods_are_empty' => [ 'cart_id: "cart_id_value" shipping_methods: []', 'Required parameter "shipping_methods" is missing' ], - 'missed_carrier_code' => [ - 'cart_id: "cart_id_value", shipping_methods: [{ - method_code: "flatrate" - }]', - 'Field ShippingMethodInput.carrier_code of required type String! was not provided.' - ], 'empty_carrier_code' => [ 'cart_id: "cart_id_value", shipping_methods: [{ carrier_code: "" @@ -205,12 +198,6 @@ public function dataProviderSetShippingMethodWithWrongParameters(): array }]', 'Carrier with such method not found: wrong-carrier-code, flatrate' ], - 'missed_method_code' => [ - 'cart_id: "cart_id_value", shipping_methods: [{ - carrier_code: "flatrate" - }]', - 'Field ShippingMethodInput.method_code of required type String! was not provided.' - ], 'empty_method_code' => [ 'cart_id: "cart_id_value", shipping_methods: [{ carrier_code: "flatrate" @@ -261,7 +248,7 @@ public function testSetMultipleShippingMethods() $query = <<<QUERY mutation { setShippingMethodsOnCart(input: { - cart_id: "{$maskedQuoteId}", + cart_id: "{$maskedQuoteId}", shipping_methods: [ { carrier_code: "flatrate" @@ -353,9 +340,9 @@ private function getQuery( ): string { return <<<QUERY mutation { - setShippingMethodsOnCart(input: + setShippingMethodsOnCart(input: { - cart_id: "$maskedQuoteId", + cart_id: "$maskedQuoteId", shipping_methods: [{ carrier_code: "$shippingCarrierCode" method_code: "$shippingMethodCode" @@ -373,6 +360,12 @@ private function getQuery( currency } } + available_shipping_methods { + amount{ + value + } + carrier_code + } } } } diff --git a/dev/tests/api-functional/testsuite/Magento/GraphQl/Quote/Customer/UpdateCartItemsTest.php b/dev/tests/api-functional/testsuite/Magento/GraphQl/Quote/Customer/UpdateCartItemsTest.php index 1ec2de36a0bc2..b351872a69bc7 100644 --- a/dev/tests/api-functional/testsuite/Magento/GraphQl/Quote/Customer/UpdateCartItemsTest.php +++ b/dev/tests/api-functional/testsuite/Magento/GraphQl/Quote/Customer/UpdateCartItemsTest.php @@ -209,35 +209,6 @@ public function testUpdateItemInAnotherCustomerCart() $this->graphQlMutation($query, [], '', $this->getHeaderMap()); } - /** - * @magentoApiDataFixture Magento/Customer/_files/customer.php - * @expectedException \Exception - * @expectedExceptionMessage Field UpdateCartItemsInput.cart_id of required type String! was not provided. - */ - public function testUpdateWithMissedCartItemId() - { - $query = <<<QUERY -mutation { - updateCartItems(input: { - cart_items: [ - { - cart_item_id: 1 - quantity: 2 - } - ] - }) { - cart { - items { - id - quantity - } - } - } -} -QUERY; - $this->graphQlMutation($query, [], '', $this->getHeaderMap()); - } - /** * @param string $input * @param string $message @@ -275,14 +246,6 @@ public function testUpdateWithMissedItemRequiredParameters(string $input, string public function dataProviderUpdateWithMissedRequiredParameters(): array { return [ - 'missed_cart_items' => [ - '', - 'Field UpdateCartItemsInput.cart_items of required type [CartItemUpdateInput]! was not provided.' - ], - 'missed_cart_item_id' => [ - 'cart_items: [{ quantity: 2 }]', - 'Field CartItemUpdateInput.cart_item_id of required type Int! was not provided.' - ], 'missed_cart_item_qty' => [ 'cart_items: [{ cart_item_id: 1 }]', 'Required parameter "quantity" for "cart_items" is missing.' diff --git a/dev/tests/api-functional/testsuite/Magento/GraphQl/Quote/Guest/AddSimpleProductToCartTest.php b/dev/tests/api-functional/testsuite/Magento/GraphQl/Quote/Guest/AddSimpleProductToCartTest.php index 59f11be6d5b45..3ee27acfa2418 100644 --- a/dev/tests/api-functional/testsuite/Magento/GraphQl/Quote/Guest/AddSimpleProductToCartTest.php +++ b/dev/tests/api-functional/testsuite/Magento/GraphQl/Quote/Guest/AddSimpleProductToCartTest.php @@ -8,8 +8,10 @@ namespace Magento\GraphQl\Quote\Guest; use Exception; +use Magento\Framework\Exception\NoSuchEntityException; use Magento\GraphQl\Quote\GetMaskedQuoteIdByReservedOrderId; use Magento\TestFramework\Helper\Bootstrap; +use Magento\TestFramework\TestCase\GraphQl\ResponseContainsErrorsException; use Magento\TestFramework\TestCase\GraphQlAbstract; /** @@ -80,26 +82,52 @@ public function testAddSimpleProductToCart() } /** - * @expectedException Exception - * @expectedExceptionMessage Field AddSimpleProductsToCartInput.cart_id of required type String! was not provided. + * Add disabled product to cart + * + * @magentoApiDataFixture Magento/Catalog/_files/multiple_products.php + * @magentoApiDataFixture Magento/GraphQl/Quote/_files/guest/create_empty_cart.php + * @return void */ - public function testAddSimpleProductToCartIfCartIdIsMissed() + public function testAddDisabledProductToCart(): void { - $query = <<<QUERY -mutation { - addSimpleProductsToCart( - input: { - cart_items: [] - } - ) { - cart { - items { - id - } + $sku = 'simple3'; + $quantity = 2; + + $maskedQuoteId = $this->getMaskedQuoteIdByReservedOrderId->execute('test_quote'); + $query = $this->getQuery($maskedQuoteId, $sku, $quantity); + + $this->expectException(ResponseContainsErrorsException::class); + $this->expectExceptionMessage( + 'Could not add the product with SKU ' . $sku . ' to the shopping cart: ' . + 'Product that you are trying to add is not available.' + ); + + $this->graphQlMutation($query); } - } -} -QUERY; + + /** + * Add out of stock product to cart + * + * @magentoApiDataFixture Magento/GraphQl/Catalog/_files/simple_product.php + * @magentoApiDataFixture Magento/Catalog/_files/multiple_products.php + * @magentoApiDataFixture Magento/GraphQl/Quote/_files/guest/create_empty_cart.php + * @magentoApiDataFixture Magento/GraphQl/Quote/_files/add_simple_product.php + * @magentoApiDataFixture Magento/GraphQl/Catalog/_files/set_simple_product_out_of_stock.php + * @return void + * @throws NoSuchEntityException + */ + public function testAddOutOfStockProductToCart(): void + { + $sku = 'simple1'; + $quantity = 1; + + $maskedQuoteId = $this->getMaskedQuoteIdByReservedOrderId->execute('test_quote'); + $query = $this->getQuery($maskedQuoteId, $sku, $quantity); + + $this->expectException(ResponseContainsErrorsException::class); + $this->expectExceptionMessage( + 'Some of the products are out of stock.' + ); $this->graphQlMutation($query); } @@ -130,32 +158,6 @@ public function testAddSimpleProductToCartIfCartIdIsEmpty() $this->graphQlMutation($query); } - public function testAddSimpleProductToCartIfCartItemsAreMissed() - { - $query = <<<QUERY -mutation { - addSimpleProductsToCart( - input: { - cart_id: "cart_id" - } - ) { - cart { - items { - id - } - } - } -} -QUERY; - $this->expectException(\Exception::class); - $this->expectExceptionMessage( - 'Field AddSimpleProductsToCartInput.cart_items of required type [SimpleProductCartItemInput]!' - . ' was not provided.' - ); - - $this->graphQlMutation($query); - } - /** * @expectedException Exception * @expectedExceptionMessage Required parameter "cart_items" is missing @@ -242,7 +244,7 @@ public function testAddSimpleProductToCustomerCart() private function getQuery(string $maskedQuoteId, string $sku, float $quantity): string { return <<<QUERY -mutation { +mutation { addSimpleProductsToCart( input: { cart_id: "{$maskedQuoteId}" diff --git a/dev/tests/api-functional/testsuite/Magento/GraphQl/Quote/Guest/AddVirtualProductToCartTest.php b/dev/tests/api-functional/testsuite/Magento/GraphQl/Quote/Guest/AddVirtualProductToCartTest.php index c5723d137d070..3811a60ffc522 100644 --- a/dev/tests/api-functional/testsuite/Magento/GraphQl/Quote/Guest/AddVirtualProductToCartTest.php +++ b/dev/tests/api-functional/testsuite/Magento/GraphQl/Quote/Guest/AddVirtualProductToCartTest.php @@ -51,31 +51,6 @@ public function testAddVirtualProductToCart() self::assertEquals($sku, $response['addVirtualProductsToCart']['cart']['items'][0]['product']['sku']); } - /** - * @expectedException Exception - * @expectedExceptionMessage Field AddSimpleProductsToCartInput.cart_id of required type String! was not provided. - */ - public function testAddVirtualProductToCartIfCartIdIsMissed() - { - $query = <<<QUERY -mutation { - addSimpleProductsToCart( - input: { - cart_items: [] - } - ) { - cart { - items { - id - } - } - } -} -QUERY; - - $this->graphQlMutation($query); - } - /** * @expectedException Exception * @expectedExceptionMessage Required parameter "cart_id" is missing @@ -102,33 +77,6 @@ public function testAddVirtualProductToCartIfCartIdIsEmpty() $this->graphQlMutation($query); } - public function testAddVirtualProductToCartIfCartItemsAreMissed() - { - $query = <<<QUERY -mutation { - addSimpleProductsToCart( - input: { - cart_id: "cart_id" - } - ) { - cart { - items { - id - } - } - } -} -QUERY; - - $this->expectException(\Exception::class); - $this->expectExceptionMessage( - 'Field AddSimpleProductsToCartInput.cart_items of required type [SimpleProductCartItemInput]!' - . ' was not provided.' - ); - - $this->graphQlMutation($query); - } - /** * @expectedException Exception * @expectedExceptionMessage Required parameter "cart_items" is missing diff --git a/dev/tests/api-functional/testsuite/Magento/GraphQl/Quote/Guest/AllowGuestCheckoutOptionTest.php b/dev/tests/api-functional/testsuite/Magento/GraphQl/Quote/Guest/AllowGuestCheckoutOptionTest.php index 60c3cc2e8b24e..f67638015988b 100644 --- a/dev/tests/api-functional/testsuite/Magento/GraphQl/Quote/Guest/AllowGuestCheckoutOptionTest.php +++ b/dev/tests/api-functional/testsuite/Magento/GraphQl/Quote/Guest/AllowGuestCheckoutOptionTest.php @@ -17,7 +17,7 @@ use Magento\TestFramework\TestCase\GraphQlAbstract; /** - * Allow guest checkout option test + * Test for guest checkout. */ class AllowGuestCheckoutOptionTest extends GraphQlAbstract { @@ -169,7 +169,7 @@ public function testSetPaymentOnCartWithGuestCheckoutDisabled() $query = <<<QUERY mutation { setPaymentMethodOnCart(input: { - cart_id: "{$maskedQuoteId}", + cart_id: "{$maskedQuoteId}", payment_method: { code: "{$methodCode}" } @@ -254,9 +254,9 @@ public function testSetShippingMethodOnCartWithGuestCheckoutDisabled() $query = <<<QUERY mutation { - setShippingMethodsOnCart(input: + setShippingMethodsOnCart(input: { - cart_id: "$maskedQuoteId", + cart_id: "$maskedQuoteId", shipping_methods: [{ carrier_code: "$carrierCode" method_code: "$methodCode" @@ -284,7 +284,6 @@ public function testSetShippingMethodOnCartWithGuestCheckoutDisabled() * @magentoConfigFixture default_store payment/cashondelivery/active 1 * @magentoConfigFixture default_store payment/checkmo/active 1 * @magentoConfigFixture default_store payment/purchaseorder/active 1 - * @magentoConfigFixture default_store payment/authorizenet_acceptjs/active 1 * @magentoApiDataFixture Magento/GraphQl/Quote/_files/guest/create_empty_cart.php * @magentoApiDataFixture Magento/GraphQl/Quote/_files/guest/set_guest_email.php * @magentoApiDataFixture Magento/GraphQl/Quote/_files/add_simple_product.php diff --git a/dev/tests/api-functional/testsuite/Magento/GraphQl/Quote/Guest/ApplyCouponToCartTest.php b/dev/tests/api-functional/testsuite/Magento/GraphQl/Quote/Guest/ApplyCouponToCartTest.php index 865837e6bd629..454f01b5cde19 100644 --- a/dev/tests/api-functional/testsuite/Magento/GraphQl/Quote/Guest/ApplyCouponToCartTest.php +++ b/dev/tests/api-functional/testsuite/Magento/GraphQl/Quote/Guest/ApplyCouponToCartTest.php @@ -150,49 +150,6 @@ public function testApplyCouponWhichIsNotApplicable() $this->graphQlMutation($query); } - /** - * @param string $input - * @param string $message - * @magentoApiDataFixture Magento/GraphQl/Catalog/_files/simple_product.php - * @magentoApiDataFixture Magento/GraphQl/Quote/_files/guest/create_empty_cart.php - * @dataProvider dataProviderUpdateWithMissedRequiredParameters - * @expectedException \Exception - */ - public function testApplyCouponWithMissedRequiredParameters(string $input, string $message) - { - $query = <<<QUERY -mutation { - applyCouponToCart(input: {{$input}}) { - cart { - applied_coupon { - code - } - } - } -} -QUERY; - - $this->expectExceptionMessage($message); - $this->graphQlMutation($query); - } - - /** - * @return array - */ - public function dataProviderUpdateWithMissedRequiredParameters(): array - { - return [ - 'missed_cart_id' => [ - 'coupon_code: "test"', - 'Field ApplyCouponToCartInput.cart_id of required type String! was not provided.' - ], - 'missed_coupon_code' => [ - 'cart_id: "test_quote"', - 'Field ApplyCouponToCartInput.coupon_code of required type String! was not provided.' - ], - ]; - } - /** * @param string $maskedQuoteId * @param string $couponCode diff --git a/dev/tests/api-functional/testsuite/Magento/GraphQl/Quote/Guest/ApplyCouponsToCartTest.php b/dev/tests/api-functional/testsuite/Magento/GraphQl/Quote/Guest/ApplyCouponsToCartTest.php index 7d5e21cd25b8a..0344e274d6fbc 100644 --- a/dev/tests/api-functional/testsuite/Magento/GraphQl/Quote/Guest/ApplyCouponsToCartTest.php +++ b/dev/tests/api-functional/testsuite/Magento/GraphQl/Quote/Guest/ApplyCouponsToCartTest.php @@ -127,49 +127,6 @@ public function testApplyCouponsWhichIsNotApplicable() $this->graphQlMutation($query); } - /** - * @param string $input - * @param string $message - * @magentoApiDataFixture Magento/GraphQl/Catalog/_files/simple_product.php - * @magentoApiDataFixture Magento/GraphQl/Quote/_files/guest/create_empty_cart.php - * @dataProvider dataProviderUpdateWithMissedRequiredParameters - * @expectedException \Exception - */ - public function testApplyCouponsWithMissedRequiredParameters(string $input, string $message) - { - $query = <<<QUERY -mutation { - applyCouponToCart(input: {{$input}}) { - cart { - applied_coupons { - code - } - } - } -} -QUERY; - - $this->expectExceptionMessage($message); - $this->graphQlMutation($query); - } - - /** - * @return array - */ - public function dataProviderUpdateWithMissedRequiredParameters(): array - { - return [ - 'missed_cart_id' => [ - 'coupon_code: "test"', - 'Field ApplyCouponToCartInput.cart_id of required type String! was not provided.' - ], - 'missed_coupon_code' => [ - 'cart_id: "test_quote"', - 'Field ApplyCouponToCartInput.coupon_code of required type String! was not provided.' - ], - ]; - } - /** * @param string $maskedQuoteId * @param string $couponCode diff --git a/dev/tests/api-functional/testsuite/Magento/GraphQl/Quote/Guest/CartTotalsTest.php b/dev/tests/api-functional/testsuite/Magento/GraphQl/Quote/Guest/CartTotalsTest.php index 20022d48e83be..135b61849c29a 100644 --- a/dev/tests/api-functional/testsuite/Magento/GraphQl/Quote/Guest/CartTotalsTest.php +++ b/dev/tests/api-functional/testsuite/Magento/GraphQl/Quote/Guest/CartTotalsTest.php @@ -64,6 +64,94 @@ public function testGetCartTotalsWithTaxApplied() self::assertEquals('USD', $appliedTaxesResponse[0]['amount']['currency']); } + /** + * @magentoApiDataFixture Magento/GraphQl/Catalog/_files/simple_product.php + * @magentoApiDataFixture Magento/CatalogRule/_files/catalog_rule_10_off_not_logged.php + * @magentoApiDataFixture Magento/GraphQl/Quote/_files/guest/create_empty_cart.php + * @magentoApiDataFixture Magento/GraphQl/Quote/_files/add_simple_product.php + * @magentoApiDataFixture Magento/GraphQl/Quote/_files/set_new_shipping_address.php + * @magentoApiDataFixture Magento/GraphQl/Quote/_files/set_new_billing_address.php + */ + public function testGetCartTotalsWithCatalogRuleApplied() + { + $maskedQuoteId = $this->getMaskedQuoteIdByReservedOrderId->execute('test_quote'); + $query = $this->getQuery($maskedQuoteId); + $response = $this->graphQlQuery($query); + + $cartItem = $response['cart']['items'][0]; + self::assertEquals(9, $cartItem['prices']['price']['value']); + self::assertEquals(18, $cartItem['prices']['row_total']['value']); + self::assertEquals(18, $cartItem['prices']['row_total_including_tax']['value']); + + self::assertArrayHasKey('prices', $response['cart']); + $pricesResponse = $response['cart']['prices']; + self::assertEquals(18, $pricesResponse['grand_total']['value']); + self::assertEquals(18, $pricesResponse['subtotal_including_tax']['value']); + self::assertEquals(18, $pricesResponse['subtotal_excluding_tax']['value']); + self::assertEquals(18, $pricesResponse['subtotal_with_discount_excluding_tax']['value']); + } + + /** + * @magentoApiDataFixture Magento/GraphQl/Tax/_files/tax_rule_for_region_1.php + * @magentoApiDataFixture Magento/GraphQl/Catalog/_files/simple_product.php + * @magentoApiDataFixture Magento/CatalogRule/_files/catalog_rule_10_off_not_logged.php + * @magentoApiDataFixture Magento/GraphQl/Quote/_files/guest/create_empty_cart.php + * @magentoApiDataFixture Magento/GraphQl/Quote/_files/add_simple_product.php + * @magentoApiDataFixture Magento/GraphQl/Quote/_files/set_new_shipping_address.php + * @magentoApiDataFixture Magento/GraphQl/Quote/_files/set_new_billing_address.php + */ + public function testGetCartTotalsWithCatalogRuleAndTaxApplied() + { + $maskedQuoteId = $this->getMaskedQuoteIdByReservedOrderId->execute('test_quote'); + $query = $this->getQuery($maskedQuoteId); + $response = $this->graphQlQuery($query); + + $cartItem = $response['cart']['items'][0]; + self::assertEquals(9, $cartItem['prices']['price']['value']); + self::assertEquals(18, $cartItem['prices']['row_total']['value']); + self::assertEquals(19.35, $cartItem['prices']['row_total_including_tax']['value']); + + self::assertArrayHasKey('prices', $response['cart']); + $pricesResponse = $response['cart']['prices']; + self::assertEquals(19.35, $pricesResponse['grand_total']['value']); + self::assertEquals(19.35, $pricesResponse['subtotal_including_tax']['value']); + self::assertEquals(18, $pricesResponse['subtotal_excluding_tax']['value']); + self::assertEquals(18, $pricesResponse['subtotal_with_discount_excluding_tax']['value']); + } + + /** + * @magentoApiDataFixture Magento/GraphQl/Quote/_files/cart_rule_discount_no_coupon.php + * @magentoApiDataFixture Magento/GraphQl/Catalog/_files/simple_product.php + * @magentoApiDataFixture Magento/CatalogRule/_files/catalog_rule_10_off_not_logged.php + * @magentoApiDataFixture Magento/GraphQl/Quote/_files/guest/create_empty_cart.php + * @magentoApiDataFixture Magento/GraphQl/Quote/_files/add_simple_product.php + * @magentoApiDataFixture Magento/GraphQl/Quote/_files/set_new_shipping_address.php + * @magentoApiDataFixture Magento/GraphQl/Quote/_files/set_new_billing_address.php + */ + public function testGetCartTotalsWithCatalogRuleAndCartRuleApplied() + { + $maskedQuoteId = $this->getMaskedQuoteIdByReservedOrderId->execute('test_quote'); + $query = $this->getQuery($maskedQuoteId); + $response = $this->graphQlQuery($query); + + $cartItem = $response['cart']['items'][0]; + self::assertEquals(9, $cartItem['prices']['price']['value']); + self::assertEquals(18, $cartItem['prices']['row_total']['value']); + self::assertEquals(18, $cartItem['prices']['row_total_including_tax']['value']); + self::assertEquals(9, $cartItem['prices']['total_item_discount']['value']); + + $discount = $cartItem['prices']['discounts'][0]; + self::assertEquals("50% Off for all orders", $discount['label']); + self::assertEquals(9, $discount['amount']['value']); + + self::assertArrayHasKey('prices', $response['cart']); + $pricesResponse = $response['cart']['prices']; + self::assertEquals(9, $pricesResponse['grand_total']['value']); + self::assertEquals(18, $pricesResponse['subtotal_including_tax']['value']); + self::assertEquals(18, $pricesResponse['subtotal_excluding_tax']['value']); + self::assertEquals(9, $pricesResponse['subtotal_with_discount_excluding_tax']['value']); + } + /** * @magentoApiDataFixture Magento/GraphQl/Tax/_files/tax_rule_for_region_1.php * @magentoApiDataFixture Magento/GraphQl/Catalog/_files/simple_product.php @@ -190,6 +278,15 @@ private function getQuery(string $maskedQuoteId): string value currency } + total_item_discount { + value + } + discounts { + label + amount { + value + } + } } } prices { diff --git a/dev/tests/api-functional/testsuite/Magento/GraphQl/Quote/Guest/GetAvailableShippingMethodsTest.php b/dev/tests/api-functional/testsuite/Magento/GraphQl/Quote/Guest/GetAvailableShippingMethodsTest.php index 867aaab7b3a58..9a1eea82686e5 100644 --- a/dev/tests/api-functional/testsuite/Magento/GraphQl/Quote/Guest/GetAvailableShippingMethodsTest.php +++ b/dev/tests/api-functional/testsuite/Magento/GraphQl/Quote/Guest/GetAvailableShippingMethodsTest.php @@ -81,6 +81,57 @@ public function testGetAvailableShippingMethods() ); } + /** + * Test case: get available shipping methods from current customer quote with configurable product + * + * @magentoApiDataFixture Magento/GraphQl/Quote/_files/guest/create_empty_cart.php + * @magentoApiDataFixture Magento/CatalogRule/_files/configurable_product.php + * @magentoApiDataFixture Magento/GraphQl/Quote/_files/add_configurable_product.php + * @magentoApiDataFixture Magento/GraphQl/Quote/_files/set_new_shipping_address.php + */ + public function testGetAvailableShippingMethodsWithConfigurableProduct() + { + $maskedQuoteId = $this->getMaskedQuoteIdByReservedOrderId->execute('test_quote'); + $response = $this->graphQlQuery($this->getQuery($maskedQuoteId)); + + self::assertArrayHasKey('cart', $response); + self::assertArrayHasKey('shipping_addresses', $response['cart']); + self::assertCount(1, $response['cart']['shipping_addresses']); + self::assertArrayHasKey('available_shipping_methods', $response['cart']['shipping_addresses'][0]); + self::assertCount(1, $response['cart']['shipping_addresses'][0]['available_shipping_methods']); + + $expectedAddressData = [ + 'amount' => [ + 'value' => 5, + 'currency' => 'USD', + ], + 'carrier_code' => 'flatrate', + 'carrier_title' => 'Flat Rate', + 'error_message' => '', + 'method_code' => 'flatrate', + 'method_title' => 'Fixed', + 'price_incl_tax' => [ + 'value' => 5, + 'currency' => 'USD', + ], + 'price_excl_tax' => [ + 'value' => 5, + 'currency' => 'USD', + ], + 'base_amount' => null, + ]; + self::assertEquals( + $expectedAddressData, + $response['cart']['shipping_addresses'][0]['available_shipping_methods'][0] + ); + self::assertCount(2, $response['cart']['shipping_addresses'][0]['cart_items']); + self::assertCount(2, $response['cart']['shipping_addresses'][0]['cart_items_v2']); + self::assertEquals( + 'configurable', + $response['cart']['shipping_addresses'][0]['cart_items_v2'][0]['product']['sku'] + ); + } + /** * _security * @magentoApiDataFixture Magento/Customer/_files/customer.php diff --git a/dev/tests/api-functional/testsuite/Magento/GraphQl/Quote/Guest/GetCartTest.php b/dev/tests/api-functional/testsuite/Magento/GraphQl/Quote/Guest/GetCartTest.php index ae9b7b32b2dab..1b54e2f57017f 100644 --- a/dev/tests/api-functional/testsuite/Magento/GraphQl/Quote/Guest/GetCartTest.php +++ b/dev/tests/api-functional/testsuite/Magento/GraphQl/Quote/Guest/GetCartTest.php @@ -119,7 +119,7 @@ public function testGetNonExistentCart() * @magentoApiDataFixture Magento/GraphQl/Quote/_files/make_cart_inactive.php * * @expectedException Exception - * @expectedExceptionMessage Current user does not have an active cart. + * @expectedExceptionMessage The cart isn't active. */ public function testGetInactiveCart() { diff --git a/dev/tests/api-functional/testsuite/Magento/GraphQl/Quote/Guest/GetSelectedPaymentMethodTest.php b/dev/tests/api-functional/testsuite/Magento/GraphQl/Quote/Guest/GetSelectedPaymentMethodTest.php index 9536f078402a4..7619212942812 100644 --- a/dev/tests/api-functional/testsuite/Magento/GraphQl/Quote/Guest/GetSelectedPaymentMethodTest.php +++ b/dev/tests/api-functional/testsuite/Magento/GraphQl/Quote/Guest/GetSelectedPaymentMethodTest.php @@ -34,7 +34,6 @@ protected function setUp() * @magentoConfigFixture default_store payment/cashondelivery/active 1 * @magentoConfigFixture default_store payment/checkmo/active 1 * @magentoConfigFixture default_store payment/purchaseorder/active 1 - * @magentoConfigFixture default_store payment/authorizenet_acceptjs/active 1 * @magentoApiDataFixture Magento/GraphQl/Quote/_files/add_simple_product.php * @magentoApiDataFixture Magento/GraphQl/Quote/_files/set_new_shipping_address.php * @magentoApiDataFixture Magento/GraphQl/Quote/_files/set_checkmo_payment_method.php @@ -60,7 +59,6 @@ public function testGetSelectedPaymentMethod() * @magentoConfigFixture default_store payment/cashondelivery/active 1 * @magentoConfigFixture default_store payment/checkmo/active 1 * @magentoConfigFixture default_store payment/purchaseorder/active 1 - * @magentoConfigFixture default_store payment/authorizenet_acceptjs/active 1 * @magentoApiDataFixture Magento/GraphQl/Quote/_files/add_simple_product.php * @magentoApiDataFixture Magento/GraphQl/Quote/_files/set_new_shipping_address.php */ @@ -100,7 +98,6 @@ public function testGetSelectedPaymentMethodFromNonExistentCart() * @magentoConfigFixture default_store payment/cashondelivery/active 1 * @magentoConfigFixture default_store payment/checkmo/active 1 * @magentoConfigFixture default_store payment/purchaseorder/active 1 - * @magentoConfigFixture default_store payment/authorizenet_acceptjs/active 1 * @magentoApiDataFixture Magento/GraphQl/Quote/_files/customer/create_empty_cart.php * @magentoApiDataFixture Magento/GraphQl/Quote/_files/add_simple_product.php * @magentoApiDataFixture Magento/GraphQl/Quote/_files/set_new_shipping_address.php @@ -126,7 +123,7 @@ private function getQuery(string $maskedQuoteId): string { return <<<QUERY { - cart(cart_id:"$maskedQuoteId") { + cart(cart_id:"$maskedQuoteId") { selected_payment_method { code } diff --git a/dev/tests/api-functional/testsuite/Magento/GraphQl/Quote/Guest/PlaceOrderTest.php b/dev/tests/api-functional/testsuite/Magento/GraphQl/Quote/Guest/PlaceOrderTest.php index c6c1d3be99c59..4879ff39b709e 100644 --- a/dev/tests/api-functional/testsuite/Magento/GraphQl/Quote/Guest/PlaceOrderTest.php +++ b/dev/tests/api-functional/testsuite/Magento/GraphQl/Quote/Guest/PlaceOrderTest.php @@ -61,7 +61,6 @@ protected function setUp() * @magentoConfigFixture default_store payment/cashondelivery/active 1 * @magentoConfigFixture default_store payment/checkmo/active 1 * @magentoConfigFixture default_store payment/purchaseorder/active 1 - * @magentoConfigFixture default_store payment/authorizenet_acceptjs/active 1 * @magentoApiDataFixture Magento/GraphQl/Quote/_files/guest/create_empty_cart.php * @magentoApiDataFixture Magento/GraphQl/Quote/_files/guest/set_guest_email.php * @magentoApiDataFixture Magento/GraphQl/Quote/_files/add_simple_product.php @@ -95,25 +94,6 @@ public function testPlaceOrderIfCartIdIsEmpty() $this->graphQlMutation($query); } - /** - * @expectedException Exception - * @expectedExceptionMessage Field PlaceOrderInput.cart_id of required type String! was not provided. - */ - public function testPlaceOrderIfCartIdIsMissed() - { - $query = <<<QUERY -mutation { - placeOrder(input: {}) { - order { - order_number - } - } -} -QUERY; - - $this->graphQlMutation($query); - } - /** * @magentoApiDataFixture Magento/GraphQl/Catalog/_files/simple_product.php * @magentoConfigFixture default_store carriers/flatrate/active 1 @@ -123,7 +103,6 @@ public function testPlaceOrderIfCartIdIsMissed() * @magentoConfigFixture default_store payment/cashondelivery/active 1 * @magentoConfigFixture default_store payment/checkmo/active 1 * @magentoConfigFixture default_store payment/purchaseorder/active 1 - * @magentoConfigFixture default_store payment/authorizenet_acceptjs/active 1 * @magentoApiDataFixture Magento/GraphQl/Quote/_files/guest/create_empty_cart.php * @magentoApiDataFixture Magento/GraphQl/Quote/_files/add_simple_product.php * @magentoApiDataFixture Magento/GraphQl/Quote/_files/set_new_shipping_address.php @@ -276,7 +255,6 @@ public function testPlaceOrderWithOutOfStockProduct() * @magentoConfigFixture default_store payment/cashondelivery/active 1 * @magentoConfigFixture default_store payment/checkmo/active 1 * @magentoConfigFixture default_store payment/purchaseorder/active 1 - * @magentoConfigFixture default_store payment/authorizenet_acceptjs/active 1 * @magentoApiDataFixture Magento/GraphQl/Quote/_files/customer/create_empty_cart.php * @magentoApiDataFixture Magento/GraphQl/Quote/_files/add_simple_product.php * @magentoApiDataFixture Magento/GraphQl/Quote/_files/set_new_shipping_address.php diff --git a/dev/tests/api-functional/testsuite/Magento/GraphQl/Quote/Guest/RemoveCouponFromCartTest.php b/dev/tests/api-functional/testsuite/Magento/GraphQl/Quote/Guest/RemoveCouponFromCartTest.php index 12c3918fcd0ac..e94a70cbd929f 100644 --- a/dev/tests/api-functional/testsuite/Magento/GraphQl/Quote/Guest/RemoveCouponFromCartTest.php +++ b/dev/tests/api-functional/testsuite/Magento/GraphQl/Quote/Guest/RemoveCouponFromCartTest.php @@ -61,28 +61,6 @@ public function testRemoveCouponFromCartIfCartIdIsEmpty() $this->graphQlMutation($query); } - /** - * @expectedException Exception - * @expectedExceptionMessage Field RemoveCouponFromCartInput.cart_id of required type String! was not provided. - */ - public function testRemoveCouponFromCartIfCartIdIsMissed() - { - $query = <<<QUERY -mutation { - removeCouponFromCart(input: {}) { - cart { - applied_coupon { - code - } - } - } -} - -QUERY; - - $this->graphQlMutation($query); - } - /** * @expectedException Exception * @expectedExceptionMessage Could not find a cart with ID "non_existent_masked_id" diff --git a/dev/tests/api-functional/testsuite/Magento/GraphQl/Quote/Guest/RemoveItemFromCartTest.php b/dev/tests/api-functional/testsuite/Magento/GraphQl/Quote/Guest/RemoveItemFromCartTest.php index c3a66291251c7..7970e4b15bc32 100644 --- a/dev/tests/api-functional/testsuite/Magento/GraphQl/Quote/Guest/RemoveItemFromCartTest.php +++ b/dev/tests/api-functional/testsuite/Magento/GraphQl/Quote/Guest/RemoveItemFromCartTest.php @@ -74,55 +74,12 @@ public function testRemoveNonExistentItem() $maskedQuoteId = $this->getMaskedQuoteIdByReservedOrderId->execute('test_quote'); $notExistentItemId = 999; - $this->expectExceptionMessage("Cart doesn't contain the {$notExistentItemId} item."); + $this->expectExceptionMessage("The cart doesn't contain the item"); $query = $this->getQuery($maskedQuoteId, $notExistentItemId); $this->graphQlMutation($query); } - /** - * @param string $input - * @param string $message - * @dataProvider dataProviderUpdateWithMissedRequiredParameters - */ - public function testUpdateWithMissedItemRequiredParameters(string $input, string $message) - { - $query = <<<QUERY -mutation { - removeItemFromCart( - input: { - {$input} - } - ) { - cart { - items { - quantity - } - } - } -} -QUERY; - $this->expectExceptionMessage($message); - $this->graphQlMutation($query); - } - - /** - * @return array - */ - public function dataProviderUpdateWithMissedRequiredParameters(): array - { - return [ - 'missed_cart_id' => [ - 'cart_item_id: 1', - 'Field RemoveItemFromCartInput.cart_id of required type String! was not provided.' - ], - 'missed_cart_item_id' => [ - 'cart_id: "test_quote"', - 'Field RemoveItemFromCartInput.cart_item_id of required type Int! was not provided.' - ], - ]; - } - /** * _security * @magentoApiDataFixture Magento/GraphQl/Catalog/_files/simple_product.php @@ -138,7 +95,7 @@ public function testRemoveItemIfItemIsNotBelongToCart() 'virtual-product' ); - $this->expectExceptionMessage("Cart doesn't contain the {$secondQuoteItemId} item."); + $this->expectExceptionMessage("The cart doesn't contain the item"); $query = $this->getQuery($firstQuoteMaskedId, $secondQuoteItemId); $this->graphQlMutation($query); @@ -162,6 +119,38 @@ public function testRemoveItemFromCustomerCart() $this->graphQlMutation($query); } + /** + * @magentoApiDataFixture Magento/GraphQl/Catalog/_files/simple_product.php + * @magentoApiDataFixture Magento/GraphQl/Quote/_files/guest/create_empty_cart.php + * @magentoApiDataFixture Magento/GraphQl/Quote/_files/add_simple_product.php + */ + public function testRemoveItemWithEmptyCartId() + { + $cartId = ""; + $cartItemId = $this->getQuoteItemIdByReservedQuoteIdAndSku->execute('test_quote', 'simple_product'); + + $this->expectExceptionMessage("Required parameter \"cart_id\" is missing."); + + $query = $this->getQuery($cartId, $cartItemId); + $this->graphQlMutation($query); + } + + /** + * @magentoApiDataFixture Magento/GraphQl/Catalog/_files/simple_product.php + * @magentoApiDataFixture Magento/GraphQl/Quote/_files/guest/create_empty_cart.php + * @magentoApiDataFixture Magento/GraphQl/Quote/_files/add_simple_product.php + */ + public function testRemoveItemWithZeroCartItemId() + { + $cartId = $this->getMaskedQuoteIdByReservedOrderId->execute('test_quote'); + $cartItemId = 0; + + $this->expectExceptionMessage("Required parameter \"cart_item_id\" is missing."); + + $query = $this->getQuery($cartId, $cartItemId); + $this->graphQlMutation($query); + } + /** * @param string $maskedQuoteId * @param int $itemId diff --git a/dev/tests/api-functional/testsuite/Magento/GraphQl/Quote/Guest/SetBillingAddressOnCartTest.php b/dev/tests/api-functional/testsuite/Magento/GraphQl/Quote/Guest/SetBillingAddressOnCartTest.php index 87335bd5c96dd..ea77ad35d2693 100644 --- a/dev/tests/api-functional/testsuite/Magento/GraphQl/Quote/Guest/SetBillingAddressOnCartTest.php +++ b/dev/tests/api-functional/testsuite/Magento/GraphQl/Quote/Guest/SetBillingAddressOnCartTest.php @@ -296,58 +296,6 @@ public function testSetBillingAddressOnNonExistentCart() $this->graphQlMutation($query); } - /** - * @magentoApiDataFixture Magento/GraphQl/Catalog/_files/simple_product.php - * @magentoApiDataFixture Magento/GraphQl/Quote/_files/guest/create_empty_cart.php - * @magentoApiDataFixture Magento/GraphQl/Quote/_files/add_simple_product.php - * - * @dataProvider dataProviderSetWithoutRequiredParameters - * @param string $input - * @param string $message - * @throws \Exception - */ - public function testSetBillingAddressWithoutRequiredParameters(string $input, string $message) - { - $maskedQuoteId = $this->getMaskedQuoteIdByReservedOrderId->execute('test_quote'); - $input = str_replace('cart_id_value', $maskedQuoteId, $input); - - $query = <<<QUERY -mutation { - setBillingAddressOnCart( - input: { - {$input} - } - ) { - cart { - billing_address { - city - } - } - } -} -QUERY; - $this->expectExceptionMessage($message); - $this->graphQlMutation($query); - } - - /** - * @return array - */ - public function dataProviderSetWithoutRequiredParameters(): array - { - return [ - 'missed_billing_address' => [ - 'cart_id: "cart_id_value"', - 'Field SetBillingAddressOnCartInput.billing_address of required type BillingAddressInput!' - . ' was not provided.', - ], - 'missed_cart_id' => [ - 'billing_address: {}', - 'Field SetBillingAddressOnCartInput.cart_id of required type String! was not provided.' - ] - ]; - } - /** * @magentoApiDataFixture Magento/GraphQl/Catalog/_files/simple_product.php * @magentoApiDataFixture Magento/GraphQl/Quote/_files/guest/create_empty_cart.php diff --git a/dev/tests/api-functional/testsuite/Magento/GraphQl/Quote/Guest/SetOfflinePaymentMethodsOnCartTest.php b/dev/tests/api-functional/testsuite/Magento/GraphQl/Quote/Guest/SetOfflinePaymentMethodsOnCartTest.php index 6c45227c47742..7a92ef8df201d 100644 --- a/dev/tests/api-functional/testsuite/Magento/GraphQl/Quote/Guest/SetOfflinePaymentMethodsOnCartTest.php +++ b/dev/tests/api-functional/testsuite/Magento/GraphQl/Quote/Guest/SetOfflinePaymentMethodsOnCartTest.php @@ -43,7 +43,6 @@ protected function setUp() * @magentoConfigFixture default_store payment/cashondelivery/active 1 * @magentoConfigFixture default_store payment/checkmo/active 1 * @magentoConfigFixture default_store payment/purchaseorder/active 1 - * @magentoConfigFixture default_store payment/authorizenet_acceptjs/active 1 * * @param string $methodCode * @param string $methodTitle @@ -89,7 +88,6 @@ public function offlinePaymentMethodDataProvider(): array * @magentoConfigFixture default_store payment/cashondelivery/active 1 * @magentoConfigFixture default_store payment/checkmo/active 1 * @magentoConfigFixture default_store payment/purchaseorder/active 1 - * @magentoConfigFixture default_store payment/authorizenet_acceptjs/active 1 */ public function testSetPurchaseOrderPaymentMethod() { @@ -101,7 +99,7 @@ public function testSetPurchaseOrderPaymentMethod() $query = <<<QUERY mutation { setPaymentMethodOnCart(input: { - cart_id: "{$maskedQuoteId}", + cart_id: "{$maskedQuoteId}", payment_method: { code: "{$methodCode}" purchase_order_number: "{$poNumber}" @@ -147,7 +145,7 @@ private function getQuery( return <<<QUERY mutation { setPaymentMethodOnCart(input: { - cart_id: "{$maskedQuoteId}", + cart_id: "{$maskedQuoteId}", payment_method: { code: "{$methodCode}" } diff --git a/dev/tests/api-functional/testsuite/Magento/GraphQl/Quote/Guest/SetPaymentMethodAndPlaceOrderTest.php b/dev/tests/api-functional/testsuite/Magento/GraphQl/Quote/Guest/SetPaymentMethodAndPlaceOrderTest.php index e38ccf78d420b..e506c7c784f3f 100644 --- a/dev/tests/api-functional/testsuite/Magento/GraphQl/Quote/Guest/SetPaymentMethodAndPlaceOrderTest.php +++ b/dev/tests/api-functional/testsuite/Magento/GraphQl/Quote/Guest/SetPaymentMethodAndPlaceOrderTest.php @@ -155,7 +155,6 @@ public function testSetPaymentOnNonExistentCart() * @magentoConfigFixture default_store payment/cashondelivery/active 1 * @magentoConfigFixture default_store payment/checkmo/active 1 * @magentoConfigFixture default_store payment/purchaseorder/active 1 - * @magentoConfigFixture default_store payment/authorizenet_acceptjs/active 1 * @magentoApiDataFixture Magento/GraphQl/Quote/_files/guest/create_empty_cart.php * @magentoApiDataFixture Magento/GraphQl/Quote/_files/add_simple_product.php * @magentoApiDataFixture Magento/GraphQl/Quote/_files/set_new_shipping_address.php @@ -230,7 +229,7 @@ private function getQuery( payment_method: { code: "$methodCode" } - }) { + }) { order { order_number } diff --git a/dev/tests/api-functional/testsuite/Magento/GraphQl/Quote/Guest/SetPaymentMethodOnCartTest.php b/dev/tests/api-functional/testsuite/Magento/GraphQl/Quote/Guest/SetPaymentMethodOnCartTest.php index 24ba3e78f9b4e..7c02589261a4a 100644 --- a/dev/tests/api-functional/testsuite/Magento/GraphQl/Quote/Guest/SetPaymentMethodOnCartTest.php +++ b/dev/tests/api-functional/testsuite/Magento/GraphQl/Quote/Guest/SetPaymentMethodOnCartTest.php @@ -184,12 +184,8 @@ public function dataProviderSetPaymentMethodWithoutRequiredParameters(): array { return [ 'missed_cart_id' => [ - 'payment_method: {code: "' . Checkmo::PAYMENT_METHOD_CHECKMO_CODE . '"}', - 'Field SetPaymentMethodOnCartInput.cart_id of required type String! was not provided.' - ], - 'missed_payment_method' => [ - 'cart_id: "cart_id_value"', - 'Field SetPaymentMethodOnCartInput.payment_method of required type PaymentMethodInput! was not provided' + 'cart_id: "", payment_method: {code: "' . Checkmo::PAYMENT_METHOD_CHECKMO_CODE . '"}', + 'Required parameter "cart_id" is missing.' ], 'missed_payment_method_code' => [ 'cart_id: "cart_id_value", payment_method: {code: ""}', @@ -204,7 +200,6 @@ public function dataProviderSetPaymentMethodWithoutRequiredParameters(): array * @magentoConfigFixture default_store payment/cashondelivery/active 1 * @magentoConfigFixture default_store payment/checkmo/active 1 * @magentoConfigFixture default_store payment/purchaseorder/active 1 - * @magentoConfigFixture default_store payment/authorizenet_acceptjs/active 1 * @magentoApiDataFixture Magento/GraphQl/Quote/_files/guest/create_empty_cart.php * @magentoApiDataFixture Magento/GraphQl/Quote/_files/add_simple_product.php * @magentoApiDataFixture Magento/GraphQl/Quote/_files/set_new_shipping_address.php @@ -254,7 +249,7 @@ private function getQuery( return <<<QUERY mutation { setPaymentMethodOnCart(input: { - cart_id: "{$maskedQuoteId}", + cart_id: "{$maskedQuoteId}", payment_method: { code: "{$methodCode}" } diff --git a/dev/tests/api-functional/testsuite/Magento/GraphQl/Quote/Guest/SetPurchaseOrderPaymentMethodOnCartTest.php b/dev/tests/api-functional/testsuite/Magento/GraphQl/Quote/Guest/SetPurchaseOrderPaymentMethodOnCartTest.php index 8b6317bd76c36..067c65fe85b6c 100644 --- a/dev/tests/api-functional/testsuite/Magento/GraphQl/Quote/Guest/SetPurchaseOrderPaymentMethodOnCartTest.php +++ b/dev/tests/api-functional/testsuite/Magento/GraphQl/Quote/Guest/SetPurchaseOrderPaymentMethodOnCartTest.php @@ -41,7 +41,6 @@ protected function setUp() * @magentoConfigFixture default_store payment/cashondelivery/active 1 * @magentoConfigFixture default_store payment/checkmo/active 1 * @magentoConfigFixture default_store payment/purchaseorder/active 1 - * @magentoConfigFixture default_store payment/authorizenet_acceptjs/active 1 */ public function testSetPurchaseOrderPaymentMethodOnCartWithSimpleProduct() { @@ -57,7 +56,7 @@ public function testSetPurchaseOrderPaymentMethodOnCartWithSimpleProduct() code: "$methodCode" purchase_order_number: "$purchaseOrderNumber" } - }) { + }) { cart { selected_payment_method { code @@ -88,7 +87,6 @@ public function testSetPurchaseOrderPaymentMethodOnCartWithSimpleProduct() * @magentoConfigFixture default_store payment/cashondelivery/active 1 * @magentoConfigFixture default_store payment/checkmo/active 1 * @magentoConfigFixture default_store payment/purchaseorder/active 1 - * @magentoConfigFixture default_store payment/authorizenet_acceptjs/active 1 * * @expectedException Exception * @expectedExceptionMessage Purchase order number is a required field. @@ -105,7 +103,7 @@ public function testSetPurchaseOrderPaymentMethodOnCartWithoutPurchaseOrderNumbe payment_method: { code: "$methodCode" } - }) { + }) { cart { selected_payment_method { code @@ -140,7 +138,7 @@ public function testSetDisabledPurchaseOrderPaymentMethodOnCart() code: "$methodCode" purchase_order_number: "$purchaseOrderNumber" } - }) { + }) { cart { selected_payment_method { code diff --git a/dev/tests/api-functional/testsuite/Magento/GraphQl/Quote/Guest/SetShippingAddressOnCartTest.php b/dev/tests/api-functional/testsuite/Magento/GraphQl/Quote/Guest/SetShippingAddressOnCartTest.php index b142de71e89a3..53a20b775530b 100644 --- a/dev/tests/api-functional/testsuite/Magento/GraphQl/Quote/Guest/SetShippingAddressOnCartTest.php +++ b/dev/tests/api-functional/testsuite/Magento/GraphQl/Quote/Guest/SetShippingAddressOnCartTest.php @@ -208,40 +208,6 @@ public function testSetShippingAddressToCustomerCart() $this->graphQlMutation($query); } - /** - * @magentoApiDataFixture Magento/GraphQl/Catalog/_files/simple_product.php - * @magentoApiDataFixture Magento/GraphQl/Quote/_files/guest/create_empty_cart.php - * @magentoApiDataFixture Magento/GraphQl/Quote/_files/add_simple_product.php - * - * @dataProvider dataProviderUpdateWithMissedRequiredParameters - * @param string $input - * @param string $message - * @throws \Exception - */ - public function testSetNewShippingAddressWithMissedRequiredParameters(string $input, string $message) - { - $maskedQuoteId = $this->getMaskedQuoteIdByReservedOrderId->execute('test_quote'); - $input = str_replace('cart_id_value', $maskedQuoteId, $input); - - $query = <<<QUERY -mutation { - setShippingAddressesOnCart( - input: { - {$input} - } - ) { - cart { - shipping_addresses { - city - } - } - } -} -QUERY; - $this->expectExceptionMessage($message); - $this->graphQlMutation($query); - } - /** * @magentoApiDataFixture Magento/GraphQl/Catalog/_files/simple_product.php * @magentoApiDataFixture Magento/GraphQl/Quote/_files/guest/create_empty_cart.php @@ -285,28 +251,6 @@ public function testSetNewShippingAddressOnCartWithRedundantStreetLine() $this->graphQlMutation($query); } - /** - * @return array - */ - public function dataProviderUpdateWithMissedRequiredParameters(): array - { - return [ - 'missed_shipping_addresses' => [ - 'cart_id: "cart_id_value"', - 'Field SetShippingAddressesOnCartInput.shipping_addresses of required type [ShippingAddressInput]! ' . - 'was not provided.', - ], - 'missed_city' => [ - 'shipping_addresses: [ { address: { save_in_address_book: false } } ]', - 'Field CartAddressInput.city of required type String! was not provided' - ], - 'missed_cart_id' => [ - 'shipping_addresses: {}', - 'Field SetShippingAddressesOnCartInput.cart_id of required type String! was not provided.' - ] - ]; - } - /** * @magentoApiDataFixture Magento/GraphQl/Catalog/_files/simple_product.php * @magentoApiDataFixture Magento/GraphQl/Quote/_files/guest/create_empty_cart.php diff --git a/dev/tests/api-functional/testsuite/Magento/GraphQl/Quote/Guest/SetShippingMethodsOnCartTest.php b/dev/tests/api-functional/testsuite/Magento/GraphQl/Quote/Guest/SetShippingMethodsOnCartTest.php index 007ada1ce57cf..0a49136421e9a 100644 --- a/dev/tests/api-functional/testsuite/Magento/GraphQl/Quote/Guest/SetShippingMethodsOnCartTest.php +++ b/dev/tests/api-functional/testsuite/Magento/GraphQl/Quote/Guest/SetShippingMethodsOnCartTest.php @@ -186,28 +186,10 @@ public function testSetShippingMethodWithWrongParameters(string $input, string $ public function dataProviderSetShippingMethodWithWrongParameters(): array { return [ - 'missed_cart_id' => [ - 'shipping_methods: [{ - carrier_code: "flatrate" - method_code: "flatrate" - }]', - 'Field SetShippingMethodsOnCartInput.cart_id of required type String! was not provided.' - ], - 'missed_shipping_methods' => [ - 'cart_id: "cart_id_value"', - 'Field SetShippingMethodsOnCartInput.shipping_methods of required type [ShippingMethodInput]!' - . ' was not provided.' - ], 'shipping_methods_are_empty' => [ 'cart_id: "cart_id_value" shipping_methods: []', 'Required parameter "shipping_methods" is missing' ], - 'missed_carrier_code' => [ - 'cart_id: "cart_id_value", shipping_methods: [{ - method_code: "flatrate" - }]', - 'Field ShippingMethodInput.carrier_code of required type String! was not provided.' - ], 'empty_carrier_code' => [ 'cart_id: "cart_id_value", shipping_methods: [{ carrier_code: "" @@ -222,12 +204,6 @@ public function dataProviderSetShippingMethodWithWrongParameters(): array }]', 'Carrier with such method not found: wrong-carrier-code, flatrate' ], - 'missed_method_code' => [ - 'cart_id: "cart_id_value", shipping_methods: [{ - carrier_code: "flatrate" - }]', - 'Field ShippingMethodInput.method_code of required type String! was not provided.' - ], 'empty_method_code' => [ 'cart_id: "cart_id_value", shipping_methods: [{ carrier_code: "flatrate" diff --git a/dev/tests/api-functional/testsuite/Magento/GraphQl/Quote/Guest/UpdateCartItemsTest.php b/dev/tests/api-functional/testsuite/Magento/GraphQl/Quote/Guest/UpdateCartItemsTest.php index 48d58a0dd8f17..761993d983db8 100644 --- a/dev/tests/api-functional/testsuite/Magento/GraphQl/Quote/Guest/UpdateCartItemsTest.php +++ b/dev/tests/api-functional/testsuite/Magento/GraphQl/Quote/Guest/UpdateCartItemsTest.php @@ -181,34 +181,6 @@ public function testUpdateItemFromCustomerCart() $this->graphQlMutation($query); } - /** - * @expectedException \Exception - * @expectedExceptionMessage Field UpdateCartItemsInput.cart_id of required type String! was not provided. - */ - public function testUpdateWithMissedCartItemId() - { - $query = <<<QUERY -mutation { - updateCartItems(input: { - cart_items: [ - { - cart_item_id: 1 - quantity: 2 - } - ] - }) { - cart { - items { - id - quantity - } - } - } -} -QUERY; - $this->graphQlMutation($query); - } - /** * @param string $input * @param string $message @@ -246,14 +218,6 @@ public function testUpdateWithMissedItemRequiredParameters(string $input, string public function dataProviderUpdateWithMissedRequiredParameters(): array { return [ - 'missed_cart_items' => [ - '', - 'Field UpdateCartItemsInput.cart_items of required type [CartItemUpdateInput]! was not provided.' - ], - 'missed_cart_item_id' => [ - 'cart_items: [{ quantity: 2 }]', - 'Field CartItemUpdateInput.cart_item_id of required type Int! was not provided.' - ], 'missed_cart_item_qty' => [ 'cart_items: [{ cart_item_id: 1 }]', 'Required parameter "quantity" for "cart_items" is missing.' diff --git a/dev/tests/api-functional/testsuite/Magento/GraphQl/Sales/ReorderConfigurableWithVariationsTest.php b/dev/tests/api-functional/testsuite/Magento/GraphQl/Sales/ReorderConfigurableWithVariationsTest.php new file mode 100644 index 0000000000000..42f19517c077d --- /dev/null +++ b/dev/tests/api-functional/testsuite/Magento/GraphQl/Sales/ReorderConfigurableWithVariationsTest.php @@ -0,0 +1,333 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +declare(strict_types=1); + +namespace Magento\GraphQl\Sales; + +use Magento\Framework\Exception\NoSuchEntityException; +use Magento\Integration\Api\CustomerTokenServiceInterface; +use Magento\Quote\Api\CartRepositoryInterface; +use Magento\TestFramework\Helper\Bootstrap; +use Magento\TestFramework\TestCase\GraphQlAbstract; + +/** + * @SuppressWarnings(PHPMD.CouplingBetweenObjects) + * Test Reorder with and without products overlay in shopping cart. + */ +class ReorderConfigurableWithVariationsTest extends GraphQlAbstract +{ + /** + * Customer Id + */ + private const CUSTOMER_ID = 1; + + /** + * Order Number + */ + private const ORDER_NUMBER = '100000001'; + + /** + * Customer email + */ + private const CUSTOMER_EMAIL = 'customer@example.com'; + + /** + * @var CustomerTokenServiceInterface + */ + private $customerTokenService; + + /** + * @var CartRepositoryInterface + */ + private $cartRepository; + + /** + * @inheritDoc + */ + protected function setUp() + { + parent::setUp(); + $this->customerTokenService = Bootstrap::getObjectManager()->get(CustomerTokenServiceInterface::class); + + // be sure previous tests didn't left customer quote + /** @var CartRepositoryInterface $cartRepository */ + $this->cartRepository = Bootstrap::getObjectManager()->get(CartRepositoryInterface::class); + try { + $quote = $this->cartRepository->getForCustomer(self::CUSTOMER_ID); + $this->cartRepository->delete($quote); + } catch (NoSuchEntityException $e) { + } + } + + /** + * @magentoApiDataFixture Magento/Sales/_files/order_with_two_configurable_variations.php + */ + public function testVariations() + { + /** @var \Magento\Catalog\Api\ProductRepositoryInterface $repository */ + $productRepository = Bootstrap::getObjectManager() + ->create(\Magento\Catalog\Api\ProductRepositoryInterface::class); + $productSku = 'simple_20'; + /** @var \Magento\Catalog\Api\Data\ProductInterface $product */ + $product = $productRepository->get($productSku); + + $this->assertValidVariations(); + $this->assertWithOutOfStockVariation($productRepository, $product); + } + + /** + * Assert 2 variations of configurable product. + * + * @return void + * @throws NoSuchEntityException + */ + private function assertValidVariations(): void + { + $response = $this->makeReorderForDefaultCustomer(self::ORDER_NUMBER); + + $expectedResponse = [ + 'userInputErrors' => [], + 'cart' => [ + 'email' => 'customer@example.com', + 'total_quantity' => 2, + 'items' => [ + [ + 'quantity' => 1, + 'product' => [ + 'sku' => 'configurable', + ], + 'configurable_options' => [ + [ + 'option_label' => 'Test Configurable', + 'value_label' => 'Option 1', + ] + ], + ], + [ + 'quantity' => 1, + 'product' => [ + 'sku' => 'configurable', + ], + 'configurable_options' => [ + [ + 'option_label' => 'Test Configurable', + 'value_label' => 'Option 2', + ], + ], + ], + ], + ], + ]; + $this->assertResponseFields($response['reorderItems'] ?? [], $expectedResponse); + $this->cartRepository->delete($this->cartRepository->getForCustomer(self::CUSTOMER_ID)); + } + + /** + * Assert reorder with out of stock variation. + * + * @param \Magento\Catalog\Api\ProductRepositoryInterface $productRepository + * @param \Magento\Catalog\Api\Data\ProductInterface $product + * @return void + * @throws NoSuchEntityException + * @throws \Magento\Framework\Exception\CouldNotSaveException + * @throws \Magento\Framework\Exception\InputException + * @throws \Magento\Framework\Exception\StateException + * @throws NoSuchEntityException + */ + private function assertWithOutOfStockVariation( + \Magento\Catalog\Api\ProductRepositoryInterface $productRepository, + \Magento\Catalog\Api\Data\ProductInterface $product + ): void { + // make product available in stock but disable and make reorder + $product->setStockData( + [ + 'use_config_manage_stock' => 1, + 'qty' => 0, + 'is_qty_decimal' => 0, + 'is_in_stock' => 0, + ] + ); + $productRepository->save($product); + $response = $this->makeReorderForDefaultCustomer(self::ORDER_NUMBER); + $this->assetProductNotSalable($response); + $this->cartRepository->delete($this->cartRepository->getForCustomer(self::CUSTOMER_ID)); + } + + /** + * Assert reorder with "out of stock" variation. + * + * @magentoApiDataFixture Magento/Sales/_files/order_with_two_configurable_variations.php + * @return void + * @throws \Magento\Framework\Exception\StateException + * @throws NoSuchEntityException + */ + public function testWithDeletedVariation(): void + { + /** @var \Magento\Catalog\Api\ProductRepositoryInterface $repository */ + $productRepository = Bootstrap::getObjectManager() + ->create(\Magento\Catalog\Api\ProductRepositoryInterface::class); + $productSku = 'simple_20'; + /** @var \Magento\Catalog\Api\Data\ProductInterface $product */ + $product = $productRepository->get($productSku); + // delete a product and make reorder + /** @var \Magento\Framework\Registry $registry */ + $registry = Bootstrap::getObjectManager() + ->get(\Magento\Framework\Registry::class); + $registry->unregister('isSecureArea'); + $registry->register('isSecureArea', true); + $productRepository->delete($product); + $registry->unregister('isSecureArea'); + $registry->register('isSecureArea', false); + + $response = $this->makeReorderForDefaultCustomer(self::ORDER_NUMBER); + $this->assetProductUndefined($response); + $this->cartRepository->delete($this->cartRepository->getForCustomer(self::CUSTOMER_ID)); + } + + /** + * Assert that variation is not salable. + * + * @param array $response + * @return void + */ + private function assetProductNotSalable(array $response) + { + $expectedResponse = [ + 'userInputErrors' => [ + [ + 'path' => [ + 'orderNumber', + ], + 'code' => 'NOT_SALABLE', + ], + ], + 'cart' => [ + 'email' => 'customer@example.com', + 'total_quantity' => 1, + 'items' => [ + [ + 'quantity' => 1, + 'product' => [ + 'sku' => 'configurable', + ], + 'configurable_options' => [ + [ + 'option_label' => 'Test Configurable', + 'value_label' => 'Option 1', + ], + ], + ], + ], + ], + ]; + $this->assertResponseFields($response['reorderItems'] ?? [], $expectedResponse); + } + + /** + * Assert condition that variation is undefined. + * + * @param array $response + * @return void + */ + private function assetProductUndefined(array $response): void + { + $expectedResponse = [ + 'userInputErrors' => [ + [ + 'path' => [ + 'orderNumber', + ], + 'code' => 'UNDEFINED', + ], + ], + 'cart' => [ + 'email' => 'customer@example.com', + 'total_quantity' => 1, + 'items' => [ + [ + 'quantity' => 1, + 'product' => [ + 'sku' => 'configurable', + ], + 'configurable_options' => [ + [ + 'option_label' => 'Test Configurable', + 'value_label' => 'Option 1', + ], + ], + ], + ], + ], + ]; + $this->assertResponseFields($response['reorderItems'] ?? [], $expectedResponse); + } + + /** + * @param string $email + * @param string $password + * @return array + * @throws \Magento\Framework\Exception\AuthenticationException + */ + private function getCustomerAuthHeaders(string $email, string $password): array + { + $customerToken = $this->customerTokenService->createCustomerAccessToken($email, $password); + return ['Authorization' => 'Bearer ' . $customerToken]; + } + + /** + * Execute GraphQL Mutation for default customer (make reorder) + * + * @param string $orderId + * @return array|bool|float|int|string + * @throws \Exception + */ + private function makeReorderForDefaultCustomer(string $orderId = self::ORDER_NUMBER) + { + $query = $this->getQuery($orderId); + $currentPassword = 'password'; + return $this->graphQlMutation( + $query, + [], + '', + $this->getCustomerAuthHeaders(self::CUSTOMER_EMAIL, $currentPassword) + ); + } + + /** + * @param string $orderNumber + * @return string + */ + protected function getQuery($orderNumber): string + { + return + <<<MUTATION +mutation { + reorderItems(orderNumber: "{$orderNumber}") { + userInputErrors { + path + code + } + cart { + email + total_quantity + items { + quantity + product { + sku + } + ... on ConfigurableCartItem { + configurable_options { + option_label + value_label + } + } + } + } + } +} +MUTATION; + } +} diff --git a/dev/tests/api-functional/testsuite/Magento/GraphQl/Sales/ReorderMultipleProductsTest.php b/dev/tests/api-functional/testsuite/Magento/GraphQl/Sales/ReorderMultipleProductsTest.php new file mode 100644 index 0000000000000..0dec95ed44c51 --- /dev/null +++ b/dev/tests/api-functional/testsuite/Magento/GraphQl/Sales/ReorderMultipleProductsTest.php @@ -0,0 +1,214 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +declare(strict_types=1); + +namespace Magento\GraphQl\Sales; + +use Magento\Framework\Exception\NoSuchEntityException; +use Magento\Integration\Api\CustomerTokenServiceInterface; +use Magento\Quote\Api\CartRepositoryInterface; +use Magento\TestFramework\Helper\Bootstrap; +use Magento\TestFramework\TestCase\GraphQlAbstract; + +/** + * Test Reorder with different types of product. + */ +class ReorderMultipleProductsTest extends GraphQlAbstract +{ + /** + * Customer Id + */ + private const CUSTOMER_ID = 1; + + /** + * Order Number + */ + private const ORDER_NUMBER = '100000001'; + + /** + * Customer email + */ + private const CUSTOMER_EMAIL = 'customer@example.com'; + + /** + * @var CustomerTokenServiceInterface + */ + private $customerTokenService; + + /** + * @inheritDoc + */ + protected function setUp() + { + parent::setUp(); + $this->customerTokenService = Bootstrap::getObjectManager()->get(CustomerTokenServiceInterface::class); + + // be sure previous tests didn't left customer quote + /** @var CartRepositoryInterface $cartRepository */ + $cartRepository = Bootstrap::getObjectManager()->get(CartRepositoryInterface::class); + try { + $quote = $cartRepository->getForCustomer(self::CUSTOMER_ID); + $cartRepository->delete($quote); + } catch (NoSuchEntityException $e) { + } + } + + /** + * @magentoApiDataFixture Magento/Sales/_files/order_with_different_types_of_product.php + */ + public function testMutplipleProducts() + { + $response = $this->makeReorderForDefaultCustomer(self::ORDER_NUMBER); + + $expectedResponse = [ + 'userInputErrors' => [], + 'cart' => [ + 'email' => 'customer@example.com', + 'total_quantity' => 5, + 'items' => [ + [ + 'quantity' => 1, + 'product' => [ + 'sku' => 'simple-2', + ], + ], + [ + 'quantity' => 1, + 'product' => [ + 'sku' => 'configurable-1', + ], + 'configurable_options' => [ + [ + 'option_label' => 'Test Configurable', + 'value_label' => 'Option 1', + ], + ], + ], + [ + 'quantity' => 1, + 'product' => + [ + 'sku' => 'virtual_product', + ], + ], + [ + 'quantity' => 1, + 'product' => [ + 'sku' => 'bundle-product-radio-required-option', + ], + 'bundle_options' => [ + [ + 'label' => 'Radio Options', + 'type' => 'radio', + 'values' => [ + [ + 'label' => 'Simple Product', + 'price' => 10, + 'quantity' => 1, + ], + ], + ], + ], + ], + [ + 'quantity' => 1, + 'product' => [ + 'sku' => 'downloadable-product', + ], + 'links' => [ + [ + 'title' => 'Downloadable Product Link', + ], + ], + ], + ], + ], + ]; + $this->assertResponseFields($response['reorderItems'] ?? [], $expectedResponse); + } + + /** + * @param string $email + * @param string $password + * @return array + * @throws \Magento\Framework\Exception\AuthenticationException + */ + private function getCustomerAuthHeaders(string $email, string $password): array + { + $customerToken = $this->customerTokenService->createCustomerAccessToken($email, $password); + return ['Authorization' => 'Bearer ' . $customerToken]; + } + + /** + * Execute GraphQL Mutation for default customer (make reorder) + * + * @param string $orderId + * @return array|bool|float|int|string + * @throws \Exception + */ + private function makeReorderForDefaultCustomer(string $orderId = self::ORDER_NUMBER) + { + $query = $this->getQuery($orderId); + $currentPassword = 'password'; + return $this->graphQlMutation( + $query, + [], + '', + $this->getCustomerAuthHeaders(self::CUSTOMER_EMAIL, $currentPassword) + ); + } + + /** + * @param string $orderNumber + * @return string + */ + protected function getQuery($orderNumber): string + { + return + <<<MUTATION +mutation { + reorderItems(orderNumber: "{$orderNumber}") { + userInputErrors { + path + code + } + cart { + email + total_quantity + items { + quantity + product { + sku + } + ... on ConfigurableCartItem { + configurable_options { + option_label + value_label + } + } + ... on BundleCartItem { + bundle_options { + label + type + values { + label + price + quantity + } + } + } + ... on DownloadableCartItem { + links { + title + } + } + } + } + } +} +MUTATION; + } +} diff --git a/dev/tests/api-functional/testsuite/Magento/GraphQl/Sales/ReorderOverlayProductTest.php b/dev/tests/api-functional/testsuite/Magento/GraphQl/Sales/ReorderOverlayProductTest.php new file mode 100644 index 0000000000000..53c6bd4cfb212 --- /dev/null +++ b/dev/tests/api-functional/testsuite/Magento/GraphQl/Sales/ReorderOverlayProductTest.php @@ -0,0 +1,208 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +declare(strict_types=1); + +namespace Magento\GraphQl\Sales; + +use Magento\Framework\Exception\NoSuchEntityException; +use Magento\Integration\Api\CustomerTokenServiceInterface; +use Magento\Quote\Api\CartRepositoryInterface; +use Magento\TestFramework\Helper\Bootstrap; +use Magento\TestFramework\TestCase\GraphQlAbstract; + +/** + * Test Reorder with and without products overlay in shopping cart. + */ +class ReorderOverlayProductTest extends GraphQlAbstract +{ + /** + * Customer Id + */ + private const CUSTOMER_ID = 1; + + /** + * Order Number + */ + private const ORDER_NUMBER = '100000001'; + + /** + * Customer email + */ + private const CUSTOMER_EMAIL = 'customer@example.com'; + + /** + * @var CustomerTokenServiceInterface + */ + private $customerTokenService; + + /** + * @inheritDoc + */ + protected function setUp() + { + parent::setUp(); + $this->customerTokenService = Bootstrap::getObjectManager()->get(CustomerTokenServiceInterface::class); + + // be sure previous tests didn't left customer quote + /** @var CartRepositoryInterface $cartRepository */ + $cartRepository = Bootstrap::getObjectManager()->get(CartRepositoryInterface::class); + try { + $quote = $cartRepository->getForCustomer(self::CUSTOMER_ID); + $cartRepository->delete($quote); + } catch (NoSuchEntityException $e) { + } + } + + /** + * @magentoApiDataFixture Magento/Sales/_files/customer_order_item_with_product_and_custom_options.php + * @magentoApiDataFixture Magento/Catalog/_files/product_simple_without_custom_options.php + * @throws NoSuchEntityException + */ + public function testWithoutOverlay() + { + $response = $this->addProductToCartAndReorder(); + + $expectedResponse = [ + 'userInputErrors' => [], + 'cart' => [ + 'email' => 'customer@example.com', + 'total_quantity' => 2, + 'items' => [ + [ + 'quantity' => 1, + 'product' => [ + 'sku' => 'simple-2', + ], + ], + [ + 'quantity' => 1, + 'product' => [ + 'sku' => 'simple', + ], + ], + ], + ], + ]; + $this->assertResponseFields($response['reorderItems'] ?? [], $expectedResponse); + } + + /** + * @magentoApiDataFixture Magento/Sales/_files/order_with_two_simple_products.php + * @throws NoSuchEntityException + */ + public function testWithOverlay() + { + $response = $this->addProductToCartAndReorder(); + + $expectedResponse = [ + 'userInputErrors' => [], + 'cart' => [ + 'email' => 'customer@example.com', + 'total_quantity' => 3, + 'items' => [ + [ + 'quantity' => 2, + 'product' => [ + 'sku' => 'simple-2', + ], + ], + [ + 'quantity' => 1, + 'product' => [ + 'sku' => 'simple', + ], + ], + ], + ], + ]; + $this->assertResponseFields($response['reorderItems'] ?? [], $expectedResponse); + } + + /** + * @return array|bool|float|int|string + * @throws NoSuchEntityException + * @throws \Exception + */ + private function addProductToCartAndReorder() + { + /** @var \Magento\Catalog\Api\ProductRepositoryInterface $productRepository */ + $productRepository = Bootstrap::getObjectManager() + ->create(\Magento\Catalog\Api\ProductRepositoryInterface::class); + $product = $productRepository->get('simple-2'); + /** @var CartRepositoryInterface $cartRepository */ + $cartRepository = Bootstrap::getObjectManager()->get(CartRepositoryInterface::class); + + /** @var \Magento\QuoteGraphQl\Model\Cart\CreateEmptyCartForCustomer $emptyCartForCustomer */ + $emptyCartForCustomer = Bootstrap::getObjectManager() + ->create(\Magento\QuoteGraphQl\Model\Cart\CreateEmptyCartForCustomer::class); + $emptyCartForCustomer->execute(self::CUSTOMER_ID); + $quote = $cartRepository->getForCustomer(self::CUSTOMER_ID); + $quote->addProduct($product, 1); + $cartRepository->save($quote); + + return $this->makeReorderForDefaultCustomer(self::ORDER_NUMBER); + } + + /** + * @param string $email + * @param string $password + * @return array + * @throws \Magento\Framework\Exception\AuthenticationException + */ + private function getCustomerAuthHeaders(string $email, string $password): array + { + $customerToken = $this->customerTokenService->createCustomerAccessToken($email, $password); + return ['Authorization' => 'Bearer ' . $customerToken]; + } + + /** + * Execute GraphQL Mutation for default customer (make reorder) + * + * @param string $orderId + * @return array|bool|float|int|string + * @throws \Exception + */ + private function makeReorderForDefaultCustomer(string $orderId = self::ORDER_NUMBER) + { + $query = $this->getQuery($orderId); + $currentPassword = 'password'; + return $this->graphQlMutation( + $query, + [], + '', + $this->getCustomerAuthHeaders(self::CUSTOMER_EMAIL, $currentPassword) + ); + } + + /** + * @param string $orderNumber + * @return string + */ + protected function getQuery($orderNumber): string + { + return + <<<MUTATION +mutation { + reorderItems(orderNumber: "{$orderNumber}") { + userInputErrors { + path + code + } + cart { + email + total_quantity + items { + quantity + product { + sku + } + } + } + } +} +MUTATION; + } +} diff --git a/dev/tests/api-functional/testsuite/Magento/GraphQl/Sales/ReorderTest.php b/dev/tests/api-functional/testsuite/Magento/GraphQl/Sales/ReorderTest.php new file mode 100644 index 0000000000000..1a2fa55126267 --- /dev/null +++ b/dev/tests/api-functional/testsuite/Magento/GraphQl/Sales/ReorderTest.php @@ -0,0 +1,338 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +declare(strict_types=1); + +namespace Magento\GraphQl\Sales; + +use Magento\Framework\Exception\NoSuchEntityException; +use Magento\Integration\Api\CustomerTokenServiceInterface; +use Magento\Quote\Api\CartRepositoryInterface; +use Magento\TestFramework\Helper\Bootstrap; +use Magento\TestFramework\TestCase\GraphQlAbstract; + +/** + * Test Reorder + * @SuppressWarnings(PHPMD.CouplingBetweenObjects) + */ +class ReorderTest extends GraphQlAbstract +{ + /** + * Customer Id + */ + private const CUSTOMER_ID = 1; + + /** + * Order Number + */ + private const ORDER_NUMBER = '100000001'; + + /** + * Customer email + */ + private const CUSTOMER_EMAIL = 'customer@example.com'; + + /** + * @var CustomerTokenServiceInterface + */ + private $customerTokenService; + + /** + * @inheritDoc + */ + protected function setUp() + { + parent::setUp(); + $this->customerTokenService = Bootstrap::getObjectManager()->get(CustomerTokenServiceInterface::class); + + // be sure previous tests didn't left customer quote + /** @var CartRepositoryInterface $cartRepository */ + $cartRepository = Bootstrap::getObjectManager()->get(\Magento\Quote\Api\CartRepositoryInterface::class); + try { + $quote = $cartRepository->getForCustomer(self::CUSTOMER_ID); + $cartRepository->delete($quote); + } catch (NoSuchEntityException $e) { + } + } + + /** + * @magentoApiDataFixture Magento/Sales/_files/customer_order_item_with_product_and_custom_options.php + */ + public function testReorderMutation() + { + $response = $this->makeReorderForDefaultCustomer(); + $this->assertResponseFields( + $response['reorderItems'] ?? [], + [ + 'cart' => [ + 'email' => self::CUSTOMER_EMAIL, + 'total_quantity' => 1, + 'items' => [ + [ + 'quantity' => 1, + 'product' => [ + 'sku' => 'simple', + ], + ] + ], + ], + 'userInputErrors' => [] + ] + ); + } + + /** + * @magentoApiDataFixture Magento/Sales/_files/customer_order_item_with_product_and_custom_options.php + * @expectedException \Exception + * @expectedExceptionMessage The current customer isn't authorized. + */ + public function testReorderWithoutAuthorisedCustomer() + { + $query = $this->getQuery(self::ORDER_NUMBER); + $this->graphQlMutation($query); + } + + /** + * Test reorder when simple product is out of stock/disabled/deleted + * + * @magentoApiDataFixture Magento/Sales/_files/order_with_product_out_of_stock.php + * @throws \Magento\Framework\Exception\CouldNotSaveException + * @throws \Magento\Framework\Exception\InputException + * @throws \Magento\Framework\Exception\StateException + */ + public function testSimpleProductOutOfStock() + { + /** @var \Magento\Catalog\Api\ProductRepositoryInterface $repository */ + $productRepository = Bootstrap::getObjectManager() + ->create(\Magento\Catalog\Api\ProductRepositoryInterface::class); + $productSku = 'simple'; + /** @var \Magento\Catalog\Api\Data\ProductInterface $product */ + $product = $productRepository->get($productSku); + + $this->assertProductNotAvailable(); + $this->assertDisabledProduct($productRepository, $product); + $this->assertWithDeletedProduct($productRepository, $product); + } + + /** + * Test reorder when simple product qty is 0, with allowed backorders configured to below 0 + * + * @magentoApiDataFixture Magento/Sales/_files/order_with_zero_qty_product.php + * @throws \Exception + */ + public function testBackorderWithOutOfStock() + { + $response = $this->makeReorderForDefaultCustomer(); + $expectedResponse = [ + 'userInputErrors' => [], + 'cart' => [ + 'email' => 'customer@example.com', + 'total_quantity' => 2, + 'items' => [ + [ + 'quantity' => 1, + 'product' => [ + 'sku' => 'simple' + ] + ], + [ + 'quantity' => 1, + 'product' => [ + 'sku' => 'simple-2' + ] + ] + ], + ], + ]; + $this->assertResponseFields($response['reorderItems'], $expectedResponse); + } + + /** + * Test reorder with low stock for simple product + * + * @magentoApiDataFixture Magento/Sales/_files/order_with_1_qty_product.php + * @throws \Exception + */ + public function testReorderWithLowStock() + { + $response = $this->makeReorderForDefaultCustomer(); + $expectedResponse = [ + 'userInputErrors' => [ + [ + 'path' => ['orderNumber'], + 'code' => 'INSUFFICIENT_STOCK', + ], + ], + 'cart' => [ + 'email' => 'customer@example.com', + 'total_quantity' => 10, + 'items' => [ + [ + 'quantity' => 10, + 'product' => [ + 'sku' => 'simple-2' + ] + ] + ], + ], + ]; + $this->assertResponseFields($response['reorderItems'], $expectedResponse); + $response = $this->makeReorderForDefaultCustomer(); + $expectedResponse['cart']['total_quantity'] = 20; + $expectedResponse['cart']['items'][0]['quantity'] = 20; + + $this->assertResponseFields($response['reorderItems'], $expectedResponse); + } + + /** + * Assert that simple product is not available. + */ + private function assertProductNotAvailable() + { + $response = $this->makeReorderForDefaultCustomer(); + $expectedResponse = [ + 'userInputErrors' => [ + [ + 'path' => ['orderNumber'], + 'code' => 'NOT_SALABLE', + ], + ], + 'cart' => [ + 'email' => 'customer@example.com', + 'total_quantity' => 0, + 'items' => [], + ], + ]; + $this->assertResponseFields($response['reorderItems'] ?? [], $expectedResponse); + } + + /** + * Assert reorder with disabled product. + * + * @param \Magento\Catalog\Api\ProductRepositoryInterface $productRepository + * @param \Magento\Catalog\Api\Data\ProductInterface $product + * @throws \Magento\Framework\Exception\CouldNotSaveException + * @throws \Magento\Framework\Exception\InputException + * @throws \Magento\Framework\Exception\StateException + * @return void + */ + private function assertDisabledProduct( + \Magento\Catalog\Api\ProductRepositoryInterface $productRepository, + \Magento\Catalog\Api\Data\ProductInterface $product + ): void { + // make product available in stock but disable and make reorder + $product->setStockData( + [ + 'use_config_manage_stock' => 1, + 'qty' => 100, + 'is_qty_decimal' => 0, + 'is_in_stock' => 1, + ] + ) + ->setStatus(\Magento\Catalog\Model\Product\Attribute\Source\Status::STATUS_DISABLED); + $productRepository->save($product); + $this->assertProductNotAvailable(); + } + + /** + * Assert reorder with deleted product. + * + * @param \Magento\Catalog\Api\ProductRepositoryInterface $productRepository + * @param \Magento\Catalog\Api\Data\ProductInterface $product + * @throws \Magento\Framework\Exception\StateException + * @return void + */ + private function assertWithDeletedProduct( + \Magento\Catalog\Api\ProductRepositoryInterface $productRepository, + \Magento\Catalog\Api\Data\ProductInterface $product + ): void { + // delete a product and make reorder + /** @var \Magento\Framework\Registry $registry */ + $registry = \Magento\TestFramework\Helper\Bootstrap::getObjectManager() + ->get(\Magento\Framework\Registry::class); + $registry->unregister('isSecureArea'); + $registry->register('isSecureArea', true); + $productRepository->delete($product); + $registry->unregister('isSecureArea'); + $registry->register('isSecureArea', false); + + $expectedResponse = [ + 'userInputErrors' => [ + [ + 'path' => ['orderNumber'], + 'code' => 'PRODUCT_NOT_FOUND', + ], + ], + 'cart' => [ + 'email' => 'customer@example.com', + 'total_quantity' => 0, + 'items' => [], + ], + ]; + $response = $this->makeReorderForDefaultCustomer(); + $this->assertResponseFields($response['reorderItems'] ?? [], $expectedResponse); + } + + /** + * @param string $email + * @param string $password + * @return array + * @throws \Magento\Framework\Exception\AuthenticationException + */ + private function getCustomerAuthHeaders(string $email, string $password): array + { + $customerToken = $this->customerTokenService->createCustomerAccessToken($email, $password); + return ['Authorization' => 'Bearer ' . $customerToken]; + } + + /** + * Execute GraphQL Mutation for default customer (make reorder) + * + * @param string $orderId + * @return array|bool|float|int|string + * @throws \Exception + */ + private function makeReorderForDefaultCustomer(string $orderId = self::ORDER_NUMBER) + { + $query = $this->getQuery($orderId); + $currentPassword = 'password'; + return $this->graphQlMutation( + $query, + [], + '', + $this->getCustomerAuthHeaders(self::CUSTOMER_EMAIL, $currentPassword) + ); + } + + /** + * @param string $orderNumber + * @return string + */ + protected function getQuery($orderNumber): string + { + $query = + <<<MUTATION +mutation { + reorderItems(orderNumber: "{$orderNumber}") { + userInputErrors { + path + code + } + cart { + email + total_quantity + items { + quantity + product { + sku + } + } + } + } +} +MUTATION; + return $query; + } +} diff --git a/dev/tests/api-functional/testsuite/Magento/GraphQl/SendFriend/SendFriendTest.php b/dev/tests/api-functional/testsuite/Magento/GraphQl/SendFriend/SendFriendTest.php index e01e074900519..93001dd396cdc 100644 --- a/dev/tests/api-functional/testsuite/Magento/GraphQl/SendFriend/SendFriendTest.php +++ b/dev/tests/api-functional/testsuite/Magento/GraphQl/SendFriend/SendFriendTest.php @@ -45,6 +45,7 @@ protected function setUp() /** * @magentoApiDataFixture Magento/GraphQl/Catalog/_files/simple_product.php + * @magentoConfigFixture default_store sendfriend/email/enabled 1 * @magentoConfigFixture default_store sendfriend/email/allow_guest 1 */ public function testSendFriendGuestEnable() @@ -66,6 +67,7 @@ public function testSendFriendGuestEnable() /** * @magentoApiDataFixture Magento/GraphQl/Catalog/_files/simple_product.php + * @magentoConfigFixture default_store sendfriend/email/enabled 1 * @magentoConfigFixture default_store sendfriend/email/allow_guest 0 * @expectedException \Exception * @expectedExceptionMessage The current customer isn't authorized. @@ -90,9 +92,11 @@ public function testSendFriendGuestDisableAsGuest() /** * @magentoApiDataFixture Magento/Customer/_files/customer.php * @magentoApiDataFixture Magento/GraphQl/Catalog/_files/simple_product.php - * @magentoConfigFixture default_store sendfriend/email/allow_guest 0 + * @magentoConfigFixture default_store sendfriend/email/enabled 0 + * @expectedException \Exception + * @expectedExceptionMessage "Email to a Friend" is not enabled. */ - public function testSendFriendGuestDisableAsCustomer() + public function testSendFriendDisableAsCustomer() { $productId = (int)$this->productRepository->get('simple_product')->getId(); $recipients = '{ @@ -111,6 +115,9 @@ public function testSendFriendGuestDisableAsCustomer() /** * @magentoApiDataFixture Magento/Customer/_files/customer.php + * @magentoConfigFixture default_store sendfriend/email/enabled 1 + * @expectedException \Exception + * @expectedExceptionMessage The product that was requested doesn't exist. Verify the product and try again. */ public function testSendWithoutExistProduct() { @@ -125,15 +132,13 @@ public function testSendWithoutExistProduct() }'; $query = $this->getQuery($productId, $recipients); - $this->expectExceptionMessage( - 'The product that was requested doesn\'t exist. Verify the product and try again.' - ); $this->graphQlMutation($query, [], '', $this->getHeaderMap()); } /** * @magentoApiDataFixture Magento/Customer/_files/customer.php * @magentoApiDataFixture Magento/GraphQl/Catalog/_files/simple_product.php + * @magentoConfigFixture default_store sendfriend/email/enabled 1 */ public function testMaxSendEmailToFriend() { @@ -176,6 +181,7 @@ public function testMaxSendEmailToFriend() /** * @magentoApiDataFixture Magento/Customer/_files/customer.php * @magentoApiDataFixture Magento/Catalog/_files/product_simple.php + * @magentoConfigFixture default_store sendfriend/email/enabled 1 * @dataProvider sendFriendsErrorsDataProvider * @param string $input * @param string $errorMessage @@ -188,7 +194,7 @@ public function testErrors(string $input, string $errorMessage) sendEmailToFriend( input: { $input - } + } ) { sender { name @@ -210,6 +216,7 @@ public function testErrors(string $input, string $errorMessage) /** * @magentoApiDataFixture Magento/Customer/_files/customer.php * @magentoApiDataFixture Magento/GraphQl/Catalog/_files/simple_product.php + * @magentoConfigFixture default_store sendfriend/email/enabled 1 * @magentoConfigFixture default_store sendfriend/email/max_per_hour 1 * @magentoApiDataFixture Magento/SendFriend/Fixtures/sendfriend_configuration.php */ @@ -238,6 +245,7 @@ public function testLimitMessagesPerHour() /** * @magentoApiDataFixture Magento/Customer/_files/customer.php * @magentoApiDataFixture Magento/GraphQl/Catalog/_files/simple_product.php + * @magentoConfigFixture default_store sendfriend/email/enabled 1 */ public function testSendProductWithoutSenderEmail() { @@ -256,6 +264,7 @@ public function testSendProductWithoutSenderEmail() /** * @magentoApiDataFixture Magento/Customer/_files/customer.php * @magentoApiDataFixture Magento/GraphQl/Catalog/_files/simple_product_without_visibility.php + * @magentoConfigFixture default_store sendfriend/email/enabled 1 */ public function testSendProductWithoutVisibility() { @@ -282,12 +291,12 @@ public function sendFriendsErrorsDataProvider() { return [ [ - 'product_id: 1 + 'product_id: 1 sender: { name: "Name" email: "e@mail.com" message: "Lorem Ipsum" - } + } recipients: [ { name: "" @@ -300,12 +309,12 @@ public function sendFriendsErrorsDataProvider() ]', 'Please provide Name for all of recipients.' ], [ - 'product_id: 1 + 'product_id: 1 sender: { name: "Name" email: "e@mail.com" message: "Lorem Ipsum" - } + } recipients: [ { name: "Recipient Name 1" @@ -318,12 +327,12 @@ public function sendFriendsErrorsDataProvider() ]', 'Please provide Email for all of recipients.' ], [ - 'product_id: 1 + 'product_id: 1 sender: { name: "" email: "e@mail.com" message: "Lorem Ipsum" - } + } recipients: [ { name: "Recipient Name 1" @@ -336,12 +345,12 @@ public function sendFriendsErrorsDataProvider() ]', 'Please provide Name of sender.' ], [ - 'product_id: 1 + 'product_id: 1 sender: { name: "Name" email: "e@mail.com" message: "" - } + } recipients: [ { name: "Recipient Name 1" @@ -403,9 +412,9 @@ private function getQuery(int $productId, string $recipients): string name: "Name" email: "e@mail.com" message: "Lorem Ipsum" - } + } recipients: [{$recipients}] - } + } ) { sender { name diff --git a/dev/tests/api-functional/testsuite/Magento/GraphQl/SendFriend/StoreConfigTest.php b/dev/tests/api-functional/testsuite/Magento/GraphQl/SendFriend/StoreConfigTest.php new file mode 100644 index 0000000000000..9d87afb2ddec9 --- /dev/null +++ b/dev/tests/api-functional/testsuite/Magento/GraphQl/SendFriend/StoreConfigTest.php @@ -0,0 +1,124 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +declare(strict_types=1); + +namespace Magento\GraphQl\SendFriend; + +use Magento\TestFramework\TestCase\GraphQlAbstract; + +/** + * Test SendFriend configuration resolves correctly in StoreConfig + */ +class StoreConfigTest extends GraphQlAbstract +{ + public function testSendFriendFieldsAreReturnedWithoutError() + { + $query = $this->getStoreConfigQuery(); + + $response = $this->graphQlQuery($query); + $this->assertArrayNotHasKey('errors', $response); + $this->assertArrayHasKey('send_friend', $response['storeConfig']); + $this->assertArrayHasKey('enabled_for_customers', $response['storeConfig']['send_friend']); + $this->assertArrayHasKey('enabled_for_guests', $response['storeConfig']['send_friend']); + $this->assertNotNull($response['storeConfig']['send_friend']['enabled_for_customers']); + $this->assertNotNull($response['storeConfig']['send_friend']['enabled_for_guests']); + } + + /** + * @magentoConfigFixture default_store sendfriend/email/enabled 0 + */ + public function testSendFriendDisabled() + { + $response = $this->graphQlQuery($this->getStoreConfigQuery()); + + $this->assertResponse( + ['enabled_for_customers' => false, 'enabled_for_guests' => false], + $response + ); + } + + /** + * @magentoConfigFixture default_store sendfriend/email/enabled 1 + * @magentoConfigFixture default_store sendfriend/email/allow_guest 0 + */ + public function testSendFriendEnabledGuestDisabled() + { + $response = $this->graphQlQuery($this->getStoreConfigQuery()); + + $this->assertResponse( + ['enabled_for_customers' => true, 'enabled_for_guests' => false], + $response + ); + } + + /** + * @magentoConfigFixture default_store sendfriend/email/enabled 1 + * @magentoConfigFixture default_store sendfriend/email/allow_guest 1 + */ + public function testSendFriendEnabledGuestEnabled() + { + $response = $this->graphQlQuery($this->getStoreConfigQuery()); + + $this->assertResponse( + ['enabled_for_customers' => true, 'enabled_for_guests' => true], + $response + ); + } + + /** + * Assert response matches expected output + * + * @param array $expectedValues + * @param array $response + */ + private function assertResponse(array $expectedValues, array $response) + { + $this->assertArrayNotHasKey( + 'errors', + $response + ); + $this->assertArrayHasKey( + 'send_friend', + $response['storeConfig'] + ); + $this->assertArrayHasKey( + 'enabled_for_customers', + $response['storeConfig']['send_friend'] + ); + $this->assertArrayHasKey( + 'enabled_for_guests', + $response['storeConfig']['send_friend'] + ); + $this->assertEquals( + $expectedValues['enabled_for_customers'], + $response['storeConfig']['send_friend']['enabled_for_customers'] + ); + $this->assertEquals( + $expectedValues['enabled_for_guests'], + $response['storeConfig']['send_friend']['enabled_for_guests'] + ); + } + + /** + * Return simple storeConfig query to get sendFriend configuration + * + * @return string + */ + private function getStoreConfigQuery() + { + return <<<QUERY +{ + storeConfig{ + id + send_friend { + enabled_for_customers + enabled_for_guests + } + } +} +QUERY; + } +} diff --git a/dev/tests/api-functional/testsuite/Magento/GraphQl/Store/StoreValidatorTest.php b/dev/tests/api-functional/testsuite/Magento/GraphQl/Store/StoreValidatorTest.php new file mode 100644 index 0000000000000..3e5f868a21da5 --- /dev/null +++ b/dev/tests/api-functional/testsuite/Magento/GraphQl/Store/StoreValidatorTest.php @@ -0,0 +1,56 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +declare(strict_types=1); + +namespace Magento\GraphQl\Store; + +use Magento\TestFramework\TestCase\GraphQlAbstract; + +/** + * Test the GraphQL `Store` header validation + */ +class StoreValidatorTest extends GraphQlAbstract +{ + /** + * @param string $storeCode + * @param string $errorMessage + * + * @dataProvider dataProviderInvalidStore + * @magentoApiDataFixture Magento/Store/_files/inactive_store.php + */ + public function testInvalidStoreHeader(string $storeCode, string $errorMessage) + { + $query + = <<<QUERY +{ + storeConfig{ + code + } +} +QUERY; + $this->expectExceptionMessage($errorMessage); + $this->graphQlMutation($query, [], '', ['Store' => $storeCode]); + } + + /** + * Data provider with invalid store codes and expected error messages + * + * @return array + */ + public function dataProviderInvalidStore(): array + { + return [ + 'non_existing' => [ + 'non_existing', + 'Requested store is not found' + ], + 'inactive_store' => [ + 'inactive_store', + 'Requested store is not found' + ] + ]; + } +} diff --git a/dev/tests/api-functional/testsuite/Magento/GraphQl/Swatches/ProductSwatchDataTest.php b/dev/tests/api-functional/testsuite/Magento/GraphQl/Swatches/ProductSwatchDataTest.php new file mode 100644 index 0000000000000..c356012c71f47 --- /dev/null +++ b/dev/tests/api-functional/testsuite/Magento/GraphQl/Swatches/ProductSwatchDataTest.php @@ -0,0 +1,127 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +declare(strict_types=1); + +namespace Magento\GraphQl\Swatches; + +use Magento\Swatches\Helper\Media as SwatchesMedia; +use Magento\Swatches\Model\Swatch; +use Magento\TestFramework\Helper\Bootstrap; +use Magento\TestFramework\TestCase\GraphQlAbstract; + +/** + * Test for configurable product option swatch data + */ +class ProductSwatchDataTest extends GraphQlAbstract +{ + /** + * @var SwatchesMedia + */ + private $swatchMediaHelper; + + /** + * @inheritdoc + */ + protected function setUp() + { + $objectManager = Bootstrap::getObjectManager(); + $this->swatchMediaHelper = $objectManager->get(SwatchesMedia::class); + } + + /** + * @magentoApiDataFixture Magento/Swatches/_files/text_swatch_attribute.php + * @magentoApiDataFixture Magento/ConfigurableProduct/_files/configurable_products.php + */ + public function testTextSwatchDataValues() + { + $productSku = 'configurable'; + $query = <<<QUERY +{ + products(filter: {sku: {eq: "$productSku"}}) { + items { + ... on ConfigurableProduct{ + configurable_options{ + values { + swatch_data{ + value + } + } + } + } + } + } +} +QUERY; + $response = $this->graphQlQuery($query); + + $this->assertArrayHasKey('products', $response); + $this->assertArrayHasKey('items', $response['products']); + $this->assertArrayHasKey(0, $response['products']['items']); + + $product = $response['products']['items'][0]; + $this->assertArrayHasKey('configurable_options', $product); + $this->assertArrayHasKey(0, $product['configurable_options']); + + $option = $product['configurable_options'][0]; + $this->assertArrayHasKey('values', $option); + $length = count($option['values']); + for ($i = 0; $i < $length; $i++) { + $this->assertEquals('option ' . ($i + 1), $option['values'][$i]['swatch_data']['value']); + } + } + + /** + * @magentoApiDataFixture Magento/Swatches/_files/visual_swatch_attribute_with_different_options_type.php + * @magentoApiDataFixture Magento/ConfigurableProduct/_files/configurable_products.php + */ + public function testVisualSwatchDataValues() + { + $productSku = 'configurable'; + $imageName = '/visual_swatch_attribute_option_type_image.jpg'; + $color = '#000000'; + $query = <<<QUERY +{ + products(filter: {sku: {eq: "$productSku"}}) { + items { + ... on ConfigurableProduct{ + configurable_options{ + values { + swatch_data{ + value + ... on ImageSwatchData { + thumbnail + } + } + } + } + } + } + } +} +QUERY; + $response = $this->graphQlQuery($query); + + $this->assertArrayHasKey('products', $response); + $this->assertArrayHasKey('items', $response['products']); + $this->assertArrayHasKey(0, $response['products']['items']); + + $product = $response['products']['items'][0]; + $this->assertArrayHasKey('configurable_options', $product); + $this->assertArrayHasKey(0, $product['configurable_options']); + + $option = $product['configurable_options'][0]; + $this->assertArrayHasKey('values', $option); + $this->assertEquals($color, $option['values'][0]['swatch_data']['value']); + $this->assertContains( + $option['values'][1]['swatch_data']['value'], + $this->swatchMediaHelper->getSwatchAttributeImage(Swatch::SWATCH_IMAGE_NAME, $imageName) + ); + $this->assertEquals( + $option['values'][1]['swatch_data']['thumbnail'], + $this->swatchMediaHelper->getSwatchAttributeImage(Swatch::SWATCH_THUMBNAIL_NAME, $imageName) + ); + } +} diff --git a/dev/tests/api-functional/testsuite/Magento/GraphQl/Tax/ProductViewTest.php b/dev/tests/api-functional/testsuite/Magento/GraphQl/Tax/ProductViewTest.php index 1dc5a813de2b8..461b5673235dd 100644 --- a/dev/tests/api-functional/testsuite/Magento/GraphQl/Tax/ProductViewTest.php +++ b/dev/tests/api-functional/testsuite/Magento/GraphQl/Tax/ProductViewTest.php @@ -9,7 +9,6 @@ use Magento\Catalog\Api\Data\ProductInterface; use Magento\Catalog\Api\ProductRepositoryInterface; -use Magento\Framework\EntityManager\MetadataPool; use Magento\TestFramework\ObjectManager; use Magento\TestFramework\TestCase\GraphQlAbstract; use Magento\Store\Model\StoreManagerInterface; @@ -208,11 +207,6 @@ public function testQueryAllFieldsSimpleProduct() /** @var \Magento\Catalog\Model\Product $product */ $product = $this->productRepository->get($productSku, false, null, true); - /** @var MetadataPool $metadataPool */ - $metadataPool = ObjectManager::getInstance()->get(MetadataPool::class); - $product->setId( - $product->getData($metadataPool->getMetadata(ProductInterface::class)->getLinkField()) - ); $this->assertArrayHasKey('products', $response); $this->assertArrayHasKey('items', $response['products']); $this->assertEquals(1, count($response['products']['items'])); diff --git a/dev/tests/api-functional/testsuite/Magento/GroupedProduct/Api/ProductRepositoryInterfaceTest.php b/dev/tests/api-functional/testsuite/Magento/GroupedProduct/Api/ProductRepositoryInterfaceTest.php index afd347cb08e9b..8cccfbe1905a5 100644 --- a/dev/tests/api-functional/testsuite/Magento/GroupedProduct/Api/ProductRepositoryInterfaceTest.php +++ b/dev/tests/api-functional/testsuite/Magento/GroupedProduct/Api/ProductRepositoryInterfaceTest.php @@ -125,7 +125,6 @@ public function testProductLinks() ProductInterface::TYPE_ID => 'simple', ProductInterface::PRICE => 100, ProductInterface::STATUS => 1, - ProductInterface::TYPE_ID => 'simple', ProductInterface::ATTRIBUTE_SET_ID => 4, ProductInterface::EXTENSION_ATTRIBUTES_KEY => [ 'stock_item' => $this->getStockItemData() diff --git a/dev/tests/api-functional/testsuite/Magento/ProductVideo/ProductVideoExternalSourceTest.php b/dev/tests/api-functional/testsuite/Magento/ProductVideo/ProductVideoExternalSourceTest.php new file mode 100644 index 0000000000000..7c38cf15545c6 --- /dev/null +++ b/dev/tests/api-functional/testsuite/Magento/ProductVideo/ProductVideoExternalSourceTest.php @@ -0,0 +1,178 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +declare(strict_types=1); + +namespace Magento\ProductVideo; + +use Magento\Catalog\Api\Data\ProductInterface; +use Magento\TestFramework\TestCase\WebapiAbstract; + +/** + * Test for \Magento\ProductVideo feature + * + * @magentoAppIsolation enabled + * @SuppressWarnings(PHPMD.CouplingBetweenObjects) + */ +class ProductVideoExternalSourceTest extends WebapiAbstract +{ + const SERVICE_NAME = 'catalogProductRepositoryV1'; + const SERVICE_VERSION = 'V1'; + const RESOURCE_PATH = '/V1/products'; + + /** + * Media gallery entries with external videos + * + * @return array + */ + public function externalVideoDataProvider(): array + { + return [ + 'youtube-external-video' => [ + [ + 'media_type' => 'external-video', + 'disabled' => false, + 'label' => 'Test Video Created', + 'types' => [], + 'position' => 1, + 'content' => $this->getVideoThumbnailStub(), + 'extension_attributes' => [ + 'video_content' => [ + 'media_type' => 'external-video', + 'video_provider' => 'youtube', + 'video_url' => 'https://www.youtube.com/', + 'video_title' => 'Video title', + 'video_description' => 'Video description', + 'video_metadata' => 'Video meta', + ], + ], + ] + ], + 'vimeo-external-video' => [ + [ + 'media_type' => 'external-video', + 'disabled' => false, + 'label' => 'Test Video Updated', + 'types' => [], + 'position' => 1, + 'content' => $this->getVideoThumbnailStub(), + 'extension_attributes' => [ + 'video_content' => [ + 'media_type' => 'external-video', + 'video_provider' => 'vimeo', + 'video_url' => 'https://www.vimeo.com/', + 'video_title' => 'Video title', + 'video_description' => 'Video description', + 'video_metadata' => 'Video meta', + ], + ], + ] + ] + ]; + } + + /** + * Returns the array of data for Video thumbnail + * + * @return array|string[] + */ + private function getVideoThumbnailStub(): array + { + return [ + 'type' => 'image/png', + 'name' => 'thumbnail.png', + 'base64_encoded_data' => 'iVBORw0KGgoAAAANSUhEUgAAAP8AAADGCAMAAAAqo6adAAAAA1BMVEUAAP79f' + . '+LBAAAASElEQVR4nO3BMQEAAADCoPVPbQwfoAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA' + . 'AAAAAAAAAAAAAAAAAAAAAAAAAAAAA+BsYAAAF7hZJ0AAAAAElFTkSuQmCC', + ]; + } + + /** + * Test create/ update product with external video media gallery entry + * + * @dataProvider externalVideoDataProvider + * @param array $mediaGalleryData + */ + public function testCreateWithExternalVideo(array $mediaGalleryData) + { + $simpleProductBaseData = $this->getSimpleProductData( + [ + ProductInterface::NAME => 'Product With Ext. Video', + ProductInterface::SKU => 'prod-with-ext-video' + ] + ); + + $simpleProductBaseData['media_gallery_entries'] = [$mediaGalleryData]; + + $response = $this->saveProduct($simpleProductBaseData); + $this->assertEquals( + $simpleProductBaseData['media_gallery_entries'][0]['extension_attributes'], + $response["media_gallery_entries"][0]["extension_attributes"] + ); + } + + /** + * Get Simple Product Data + * + * @param array $productData + * @return array + */ + protected function getSimpleProductData($productData = []) + { + return [ + ProductInterface::SKU => isset($productData[ProductInterface::SKU]) + ? $productData[ProductInterface::SKU] : uniqid('sku-', true), + ProductInterface::NAME => isset($productData[ProductInterface::NAME]) + ? $productData[ProductInterface::NAME] : uniqid('sku-', true), + ProductInterface::VISIBILITY => 4, + ProductInterface::TYPE_ID => 'simple', + ProductInterface::PRICE => 3.62, + ProductInterface::STATUS => 1, + ProductInterface::ATTRIBUTE_SET_ID => 4, + 'custom_attributes' => [ + ['attribute_code' => 'cost', 'value' => ''], + ['attribute_code' => 'description', 'value' => 'Description'], + ] + ]; + } + + /** + * Save Product + * + * @param $product + * @param string|null $storeCode + * @param string|null $token + * @return mixed + */ + protected function saveProduct($product, $storeCode = null, ?string $token = null) + { + if (isset($product['custom_attributes'])) { + foreach ($product['custom_attributes'] as &$attribute) { + if ($attribute['attribute_code'] == 'category_ids' + && !is_array($attribute['value']) + ) { + $attribute['value'] = [""]; + } + } + } + $serviceInfo = [ + 'rest' => [ + 'resourcePath' => self::RESOURCE_PATH, + 'httpMethod' => \Magento\Framework\Webapi\Rest\Request::HTTP_METHOD_POST, + ], + 'soap' => [ + 'service' => self::SERVICE_NAME, + 'serviceVersion' => self::SERVICE_VERSION, + 'operation' => self::SERVICE_NAME . 'Save', + ], + ]; + if ($token) { + $serviceInfo['rest']['token'] = $serviceInfo['soap']['token'] = $token; + } + $requestData = ['product' => $product]; + + return $this->_webApiCall($serviceInfo, $requestData, null, $storeCode); + } +} diff --git a/dev/tests/api-functional/testsuite/Magento/Quote/Api/GuestCartItemRepositoryTest.php b/dev/tests/api-functional/testsuite/Magento/Quote/Api/GuestCartItemRepositoryTest.php index 00c8bb85d9be7..ddd986bdafc60 100644 --- a/dev/tests/api-functional/testsuite/Magento/Quote/Api/GuestCartItemRepositoryTest.php +++ b/dev/tests/api-functional/testsuite/Magento/Quote/Api/GuestCartItemRepositoryTest.php @@ -1,30 +1,41 @@ <?php /** - * * Copyright © Magento, Inc. All rights reserved. * See COPYING.txt for license details. */ namespace Magento\Quote\Api; +use Magento\CatalogInventory\Api\StockRegistryInterface; +use Magento\CatalogInventory\Model\Stock; +use Magento\TestFramework\Helper\Bootstrap; +use Magento\TestFramework\ObjectManager; use Magento\TestFramework\TestCase\WebapiAbstract; +/** + * Test for Magento\Quote\Api\GuestCartItemRepositoryInterface. + */ class GuestCartItemRepositoryTest extends WebapiAbstract { - const SERVICE_VERSION = 'V1'; - const SERVICE_NAME = 'quoteGuestCartItemRepositoryV1'; - const RESOURCE_PATH = '/V1/guest-carts/'; + public const SERVICE_NAME = 'quoteGuestCartItemRepositoryV1'; + private const SERVICE_VERSION = 'V1'; + private const RESOURCE_PATH = '/V1/guest-carts/'; /** - * @var \Magento\TestFramework\ObjectManager + * @var ObjectManager */ - protected $objectManager; + private $objectManager; + /** + * @inheritdoc + */ protected function setUp() { - $this->objectManager = \Magento\TestFramework\Helper\Bootstrap::getObjectManager(); + $this->objectManager = Bootstrap::getObjectManager(); } /** + * Test quote items + * * @magentoApiDataFixture Magento/Checkout/_files/quote_with_items_saved.php */ public function testGetList() @@ -110,12 +121,16 @@ public function testAddItem() ]; $requestData = [ - "cartItem" => [ - "sku" => $productSku, - "qty" => 7, - "quote_id" => $cartId, + 'cartItem' => [ + 'sku' => $productSku, + 'qty' => 7, ], ]; + + if (TESTS_WEB_API_ADAPTER === self::ADAPTER_SOAP) { + $requestData['cartItem']['quote_id'] = $cartId; + } + $this->_webApiCall($serviceInfo, $requestData); $this->assertTrue($quote->hasProductId(2)); $this->assertEquals(7, $quote->getItemByProduct($product)->getQty()); @@ -167,9 +182,13 @@ public function testRemoveItem() /** * @magentoApiDataFixture Magento/Checkout/_files/quote_with_items_saved.php + * @param array $stockData + * @param string|null $errorMessage + * @dataProvider updateItemDataProvider */ - public function testUpdateItem() + public function testUpdateItem(array $stockData, string $errorMessage = null) { + $this->updateStockData('simple_one', $stockData); /** @var \Magento\Quote\Model\Quote $quote */ $quote = $this->objectManager->create(\Magento\Quote\Model\Quote::class); $quote->load('test_order_item_with_items', 'reserved_order_id'); @@ -199,22 +218,16 @@ public function testUpdateItem() ], ]; - if (TESTS_WEB_API_ADAPTER == self::ADAPTER_SOAP) { - $requestData = [ - "cartItem" => [ - "qty" => 5, - "quote_id" => $cartId, - "itemId" => $itemId, - ], - ]; - } else { - $requestData = [ - "cartItem" => [ - "qty" => 5, - "quote_id" => $cartId, - ], + $requestData['cartItem']['qty'] = 5; + if (TESTS_WEB_API_ADAPTER === self::ADAPTER_SOAP) { + $requestData['cartItem'] += [ + 'quote_id' => $cartId, + 'itemId' => $itemId, ]; } + if ($errorMessage) { + $this->expectExceptionMessage($errorMessage); + } $this->_webApiCall($serviceInfo, $requestData); $quote = $this->objectManager->create(\Magento\Quote\Model\Quote::class); $quote->load('test_order_item_with_items', 'reserved_order_id'); @@ -223,4 +236,66 @@ public function testUpdateItem() $this->assertEquals(5, $item->getQty()); $this->assertEquals($itemId, $item->getItemId()); } + + /** + * @return array + */ + public function updateItemDataProvider(): array + { + return [ + [ + [] + ], + [ + [ + 'qty' => 0, + 'is_in_stock' => 1, + 'use_config_manage_stock' => 0, + 'manage_stock' => 1, + 'use_config_backorders' => 0, + 'backorders' => Stock::BACKORDERS_YES_NOTIFY, + ] + ], + [ + [ + 'qty' => 0, + 'is_in_stock' => 1, + 'use_config_manage_stock' => 0, + 'manage_stock' => 1, + 'use_config_backorders' => 0, + 'backorders' => Stock::BACKORDERS_NO, + ], + 'This product is out of stock.' + ], + [ + [ + 'qty' => 2, + 'is_in_stock' => 1, + 'use_config_manage_stock' => 0, + 'manage_stock' => 1, + 'use_config_backorders' => 0, + 'backorders' => Stock::BACKORDERS_NO, + ], + 'The requested qty is not available' + ] + ]; + } + + /** + * Update product stock + * + * @param string $sku + * @param array $stockData + * @return void + */ + private function updateStockData(string $sku, array $stockData): void + { + if ($stockData) { + /** @var $stockRegistry StockRegistryInterface */ + $stockRegistry = $this->objectManager->create(StockRegistryInterface::class); + $stockItem = $stockRegistry->getStockItemBySku($sku); + $stockItem->addData($stockData); + $stockRegistry->updateStockItemBySku($sku, $stockItem); + } + } } diff --git a/dev/tests/api-functional/testsuite/Magento/SalesRule/Api/GuestTotalsInformationManagement.php b/dev/tests/api-functional/testsuite/Magento/SalesRule/Api/GuestTotalsInformationManagement.php new file mode 100644 index 0000000000000..2057a97087fff --- /dev/null +++ b/dev/tests/api-functional/testsuite/Magento/SalesRule/Api/GuestTotalsInformationManagement.php @@ -0,0 +1,71 @@ +<?php +/** + * + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ + +namespace Magento\SalesRule\Api; + +use Magento\Framework\Webapi\Rest\Request; +use Magento\TestFramework\Helper\Bootstrap; +use Magento\TestFramework\TestCase\WebapiAbstract; + +/** + * Tests disabled cart rules for guest's cart + */ +class GuestTotalsInformationManagement extends WebapiAbstract +{ + private const SERVICE_OPERATION = 'calculate'; + private const SERVICE_NAME = 'checkoutGuestTotalsInformationManagementV1'; + private const SERVICE_VERSION = 'V1'; + private const RESOURCE_PATH = '/V1/guest-carts/:cartId/totals-information'; + private const QUOTE_RESERVED_ORDER_ID = 'test01'; + private const SALES_RULE_ID = 'Magento/SalesRule/_files/cart_rule_50_percent_off_no_condition/salesRuleId'; + + /** + * Test sales rule changes should be persisted in the database + * + * @magentoApiDataFixture Magento/SalesRule/_files/cart_rule_50_percent_off_no_condition.php + * @magentoApiDataFixture Magento/Sales/_files/quote.php + */ + public function testCalculate() + { + /** @var \Magento\Quote\Model\Quote $quote */ + /** @var \Magento\Quote\Model\QuoteIdMask $quoteIdMask */ + /** @var \Magento\SalesRule\Model\Rule $salesRule */ + /** @var \Magento\Framework\Registry $registry */ + $registry = Bootstrap::getObjectManager()->get(\Magento\Framework\Registry::class); + $quote = Bootstrap::getObjectManager()->get(\Magento\Quote\Model\QuoteFactory::class)->create(); + $quote->load(self::QUOTE_RESERVED_ORDER_ID, 'reserved_order_id'); + $quoteIdMask = Bootstrap::getObjectManager()->get(\Magento\Quote\Model\QuoteIdMaskFactory::class)->create(); + $quoteIdMask->load($quote->getId(), 'quote_id'); + $salesRuleId = $registry->registry(self::SALES_RULE_ID); + $salesRule = Bootstrap::getObjectManager()->create(\Magento\SalesRule\Model\RuleFactory::class)->create(); + $salesRule->load($salesRuleId); + $this->assertContains($salesRule->getRuleId(), str_getcsv($quote->getAppliedRuleIds())); + $salesRule->setIsActive(0); + $salesRule->save(); + $response = $this->_webApiCall( + [ + 'rest' => [ + 'resourcePath' => str_replace(':cartId', $quoteIdMask->getMaskedId(), self::RESOURCE_PATH), + 'httpMethod' => Request::HTTP_METHOD_POST, + ], + 'soap' => [ + 'service' => self::SERVICE_NAME, + 'serviceVersion' => self::SERVICE_VERSION, + 'operation' => self::SERVICE_NAME . self::SERVICE_OPERATION, + ], + ], + [ + 'addressInformation' => [ + 'address' => [] + ] + ] + ); + $this->assertNotEmpty($response); + $quote->load(self::QUOTE_RESERVED_ORDER_ID, 'reserved_order_id'); + $this->assertNotContains($salesRule->getId(), str_getcsv($quote->getAppliedRuleIds())); + } +} diff --git a/dev/tests/api-functional/testsuite/Magento/SalesRule/Api/TotalsInformationManagement.php b/dev/tests/api-functional/testsuite/Magento/SalesRule/Api/TotalsInformationManagement.php new file mode 100644 index 0000000000000..6bde30b9acab4 --- /dev/null +++ b/dev/tests/api-functional/testsuite/Magento/SalesRule/Api/TotalsInformationManagement.php @@ -0,0 +1,82 @@ +<?php +/** + * + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ + +namespace Magento\SalesRule\Api; + +use Magento\Framework\Webapi\Rest\Request; +use Magento\TestFramework\Helper\Bootstrap; +use Magento\TestFramework\TestCase\WebapiAbstract; + +/** + * Tests disabled cart rules for customer's cart + */ +class TotalsInformationManagement extends WebapiAbstract +{ + private const SERVICE_OPERATION = 'calculate'; + private const SERVICE_NAME = 'checkoutTotalsInformationManagementV1'; + private const SERVICE_VERSION = 'V1'; + private const RESOURCE_PATH = '/V1/carts/mine/totals-information'; + private const QUOTE_RESERVED_ORDER_ID = 'test01'; + private const SALES_RULE_ID = 'Magento/SalesRule/_files/cart_rule_50_percent_off_no_condition/salesRuleId'; + private const CUSTOMER_EMAIL = 'customer@example.com'; + private const CUSTOMER_PASSWORD = 'password'; + + /** + * Test sales rule changes should be persisted in the database + * + * @magentoApiDataFixture Magento/SalesRule/_files/cart_rule_50_percent_off_no_condition.php + * @magentoApiDataFixture Magento/Sales/_files/quote_with_customer.php + */ + public function testCalculate() + { + /** @var \Magento\Quote\Model\Quote $quote */ + /** @var \Magento\Quote\Model\QuoteIdMask $quoteIdMask */ + /** @var \Magento\SalesRule\Model\Rule $salesRule */ + /** @var \Magento\Framework\Registry $registry */ + $registry = Bootstrap::getObjectManager()->get(\Magento\Framework\Registry::class); + $quote = Bootstrap::getObjectManager()->get(\Magento\Quote\Model\QuoteFactory::class)->create(); + $quote->load(self::QUOTE_RESERVED_ORDER_ID, 'reserved_order_id'); + $quoteIdMask = Bootstrap::getObjectManager()->get(\Magento\Quote\Model\QuoteIdMaskFactory::class)->create(); + $quoteIdMask->load($quote->getId(), 'quote_id'); + $salesRuleId = $registry->registry(self::SALES_RULE_ID); + $salesRule = Bootstrap::getObjectManager()->create(\Magento\SalesRule\Model\RuleFactory::class)->create(); + $salesRule->load($salesRuleId); + $this->assertContains($salesRule->getRuleId(), str_getcsv($quote->getAppliedRuleIds())); + $salesRule->setIsActive(0); + $salesRule->save(); + $response = $this->_webApiCall( + [ + 'rest' => [ + 'resourcePath' => self::RESOURCE_PATH, + 'httpMethod' => Request::HTTP_METHOD_POST, + 'token' => Bootstrap::getObjectManager() + ->create( + \Magento\Integration\Api\CustomerTokenServiceInterface::class + ) + ->createCustomerAccessToken( + self::CUSTOMER_EMAIL, + self::CUSTOMER_PASSWORD + ) + ], + 'soap' => [ + 'service' => self::SERVICE_NAME, + 'serviceVersion' => self::SERVICE_VERSION, + 'operation' => self::SERVICE_NAME . self::SERVICE_OPERATION, + ], + ], + [ + 'cartId' => $quote->getId(), + 'addressInformation' => [ + 'address' => [] + ] + ] + ); + $this->assertNotEmpty($response); + $quote->load(self::QUOTE_RESERVED_ORDER_ID, 'reserved_order_id'); + $this->assertNotContains($salesRule->getId(), str_getcsv($quote->getAppliedRuleIds())); + } +} diff --git a/dev/tests/api-functional/testsuite/Magento/WebapiAsync/Model/OrderRepositoryInterfaceTest.php b/dev/tests/api-functional/testsuite/Magento/WebapiAsync/Model/OrderRepositoryInterfaceTest.php new file mode 100644 index 0000000000000..bc7940ca35f35 --- /dev/null +++ b/dev/tests/api-functional/testsuite/Magento/WebapiAsync/Model/OrderRepositoryInterfaceTest.php @@ -0,0 +1,174 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +declare(strict_types=1); + +namespace Magento\WebapiAsync\Model; + +use Magento\Framework\ObjectManagerInterface; +use Magento\Framework\Webapi\Rest\Request; +use Magento\Sales\Api\Data\OrderInterface; +use Magento\Sales\Model\Order; +use Magento\TestFramework\Helper\Bootstrap; +use Magento\TestFramework\MessageQueue\EnvironmentPreconditionException; +use Magento\TestFramework\MessageQueue\PreconditionFailedException; +use Magento\TestFramework\MessageQueue\PublisherConsumerController; +use Magento\TestFramework\TestCase\WebapiAbstract; + +/** + * Test order repository interface via async webapi + */ +class OrderRepositoryInterfaceTest extends WebapiAbstract +{ + private const ASYNC_BULK_SAVE_ORDER = '/async/bulk/V1/orders'; + private const ASYNC_SAVE_ORDER = '/async/V1/orders'; + /** + * @var ObjectManagerInterface + */ + private $objectManager; + /** + * @var PublisherConsumerController + */ + private $publisherConsumerController; + + /** + * @inheritDoc + */ + protected function setUp() + { + parent::setUp(); + $this->objectManager = Bootstrap::getObjectManager(); + + $params = array_merge_recursive( + Bootstrap::getInstance()->getAppInitParams(), + ['MAGE_DIRS' => ['cache' => ['path' => TESTS_TEMP_DIR . '/cache']]] + ); + + /** @var PublisherConsumerController publisherConsumerController */ + $this->publisherConsumerController = $this->objectManager->create( + PublisherConsumerController::class, + [ + 'consumers' => ['async.operations.all'], + 'logFilePath' => TESTS_TEMP_DIR . "/MessageQueueTestLog.txt", + 'appInitParams' => $params, + ] + ); + + try { + $this->publisherConsumerController->initialize(); + } catch (EnvironmentPreconditionException $e) { + $this->markTestSkipped($e->getMessage()); + } catch (PreconditionFailedException $e) { + $this->fail( + $e->getMessage() + ); + } + } + + /** + * @inheritDoc + */ + public function tearDown() + { + $this->publisherConsumerController->stopConsumers(); + parent::tearDown(); + } + + /** + * Check that order is updated successfuly via async webapi + * + * @magentoApiDataFixture Magento/Sales/_files/order.php + * @dataProvider saveDataProvider + * @param array $data + * @param bool $isBulk + * @return void + */ + public function testSave(array $data, bool $isBulk = true): void + { + $this->_markTestAsRestOnly(); + /** @var Order $beforeUpdateOrder */ + $beforeUpdateOrder = $this->objectManager->get(Order::class)->loadByIncrementId('100000001'); + $requestData = [ + 'entity' => array_merge($data, [OrderInterface::ENTITY_ID => $beforeUpdateOrder->getEntityId()]) + ]; + if ($isBulk) { + $requestData = [$requestData]; + } + $serviceInfo = [ + 'rest' => [ + 'resourcePath' => $isBulk ? self::ASYNC_BULK_SAVE_ORDER : self::ASYNC_SAVE_ORDER, + 'httpMethod' => Request::HTTP_METHOD_POST, + ] + ]; + $this->makeAsyncRequest($serviceInfo, $requestData); + try { + $this->publisherConsumerController->waitForAsynchronousResult( + function (Order $beforeUpdateOrder, array $data) { + /** @var Order $afterUpdateOrder */ + $afterUpdateOrder = $this->objectManager->get(Order::class)->load($beforeUpdateOrder->getId()); + foreach ($data as $attribute => $value) { + $getter = 'get' . str_replace(' ', '', ucwords(str_replace('_', ' ', $attribute))); + if ($value !== $afterUpdateOrder->$getter()) { + return false; + } + } + //check that base_grand_total and grand_total are not overwritten + $this->assertEquals( + $beforeUpdateOrder->getBaseGrandTotal(), + $afterUpdateOrder->getBaseGrandTotal() + ); + $this->assertEquals( + $beforeUpdateOrder->getGrandTotal(), + $afterUpdateOrder->getGrandTotal() + ); + return true; + }, + [$beforeUpdateOrder, $data] + ); + } catch (PreconditionFailedException $e) { + $this->fail("Order update via async webapi failed"); + } + } + + /** + * Data provider for tesSave() + * + * @return array + */ + public function saveDataProvider(): array + { + return [ + 'update order in bulk mode' => [ + [ + OrderInterface::CUSTOMER_EMAIL => 'customer.email.modified@magento.test' + ], + true + ], + 'update order in single mode' => [ + [ + OrderInterface::CUSTOMER_EMAIL => 'customer.email.modified@magento.test' + ], + false + ] + ]; + } + + /** + * Make async webapi request + * + * @param array $serviceInfo + * @param array $requestData + * @return void + */ + private function makeAsyncRequest(array $serviceInfo, array $requestData): void + { + $response = $this->_webApiCall($serviceInfo, $requestData); + $this->assertNotEmpty($response['request_items']); + foreach ($response['request_items'] as $requestItem) { + $this->assertEquals('accepted', $requestItem['status']); + } + $this->assertFalse($response['errors']); + } +} diff --git a/dev/tests/functional/credentials.xml.dist b/dev/tests/functional/credentials.xml.dist index 01e3a35be9a2d..97c854dcb7132 100644 --- a/dev/tests/functional/credentials.xml.dist +++ b/dev/tests/functional/credentials.xml.dist @@ -28,14 +28,6 @@ <field replace="carriers_dhl_password_eu" value="" /> <field replace="carriers_dhl_account_eu" value="" /> - <field replace="payment_authorizenet_login" value="" /> - <field replace="payment_authorizenet_trans_key" value="" /> - <field replace="payment_authorizenet_trans_md5" value="" /> - - <field replace="authorizenet_fraud_review_login" value="" /> - <field replace="authorizenet_fraud_review_trans_key" value="" /> - <field replace="authorizenet_fraud_review_md5" value="" /> - <field replace="braintree_enabled_fraud_merchant_account_id" value="" /> <field replace="braintree_enabled_fraud_merchant_id" value="" /> <field replace="braintree_enabled_fraud_public_key" value="" /> @@ -79,6 +71,4 @@ <field path="payment/paypal_alternative_payment_methods/express_checkout_us/express_checkout_required/express_checkout_required_express_checkout/api_username" value="" /> <field path="payment/paypal_alternative_payment_methods/express_checkout_us/express_checkout_required/express_checkout_required_express_checkout/api_password" value="" /> <field path="payment/paypal_alternative_payment_methods/express_checkout_us/express_checkout_required/express_checkout_required_express_checkout/api_signature" value="" /> - - <field path="fraud_protection/signifyd/api_key" value="" /> </replace> diff --git a/dev/tests/functional/etc/repository_replacer_payments.xml b/dev/tests/functional/etc/repository_replacer_payments.xml index a0ecca61eb372..5141280399e42 100644 --- a/dev/tests/functional/etc/repository_replacer_payments.xml +++ b/dev/tests/functional/etc/repository_replacer_payments.xml @@ -14,18 +14,4 @@ <field name="privateKey" xsi:type="string">BRAINTREE_PRIVATE_KEY</field> </dataset> </repository> - - <repository class="Magento\Authorizenet\Test\Repository\AuthorizenetSandboxCustomer"> - <dataset name="sandbox_fraud_hold_review"> - <field name="login_id" xsi:type="string">AUTHORIZENET_LOGIN_ID</field> - <field name="password" xsi:type="string">AUTHORIZENET_PASSWORD</field> - </dataset> - </repository> - - <repository class="Magento\Signifyd\Test\Repository\SignifydAccount"> - <dataset name="sandbox_default"> - <field name="email" xsi:type="string">SIGNIFYD_EMAIL</field> - <field name="password" xsi:type="string">SIGNIFYD_PASSWORD</field> - </dataset> - </repository> </config> diff --git a/dev/tests/functional/tests/app/Magento/Braintree/Test/Repository/CreditCard.xml b/dev/tests/functional/tests/app/Magento/Braintree/Test/Repository/CreditCard.xml index dc02e2424b827..83ab138dacf55 100644 --- a/dev/tests/functional/tests/app/Magento/Braintree/Test/Repository/CreditCard.xml +++ b/dev/tests/functional/tests/app/Magento/Braintree/Test/Repository/CreditCard.xml @@ -11,7 +11,7 @@ <field name="payment_code" xsi:type="string">braintree</field> <field name="cc_number" xsi:type="string">4000000000000002</field> <field name="cc_exp_month" xsi:type="string">01</field> - <field name="cc_exp_year" xsi:type="string">20</field> + <field name="cc_exp_year" xsi:type="string">24</field> <field name="cc_cid" xsi:type="string">123</field> </dataset> @@ -19,7 +19,7 @@ <field name="payment_code" xsi:type="string">braintree</field> <field name="cc_number" xsi:type="string">4000000000000028</field> <field name="cc_exp_month" xsi:type="string">01</field> - <field name="cc_exp_year" xsi:type="string">2020</field> + <field name="cc_exp_year" xsi:type="string">2024</field> <field name="cc_cid" xsi:type="string">123</field> </dataset> @@ -27,7 +27,7 @@ <field name="payment_code" xsi:type="string">braintree</field> <field name="cc_number" xsi:type="string">4000111111111511</field> <field name="cc_exp_month" xsi:type="string">01</field> - <field name="cc_exp_year" xsi:type="string">2020</field> + <field name="cc_exp_year" xsi:type="string">2024</field> <field name="cc_cid" xsi:type="string">123</field> </dataset> @@ -35,7 +35,7 @@ <field name="payment_code" xsi:type="string">braintree</field> <field name="cc_number" xsi:type="string">4111111111111111</field> <field name="cc_exp_month" xsi:type="string">01 - January</field> - <field name="cc_exp_year" xsi:type="string">2020</field> + <field name="cc_exp_year" xsi:type="string">2024</field> </dataset> </repository> </config> diff --git a/dev/tests/functional/tests/app/Magento/Catalog/Test/TestCase/Category/CreateCategoryEntityTest.xml b/dev/tests/functional/tests/app/Magento/Catalog/Test/TestCase/Category/CreateCategoryEntityTest.xml index 5ea1b692e3eb9..3630a642534fb 100644 --- a/dev/tests/functional/tests/app/Magento/Catalog/Test/TestCase/Category/CreateCategoryEntityTest.xml +++ b/dev/tests/functional/tests/app/Magento/Catalog/Test/TestCase/Category/CreateCategoryEntityTest.xml @@ -56,6 +56,7 @@ <constraint name="Magento\Catalog\Test\Constraint\AssertCategoryPage" /> </variation> <variation name="CreateCategoryEntityTestVariation4_Subcategory_AllFields"> + <data name="tag" xsi:type="string">mftf_migrated:yes</data> <data name="description" xsi:type="string">Create not anchor subcategory specifying all fields</data> <data name="addCategory" xsi:type="string">addSubcategory</data> <data name="category/data/parent_id/dataset" xsi:type="string">default_category</data> diff --git a/dev/tests/functional/tests/app/Magento/Catalog/Test/TestCase/Product/CreateSimpleProductEntityPartTwoTest.xml b/dev/tests/functional/tests/app/Magento/Catalog/Test/TestCase/Product/CreateSimpleProductEntityPartTwoTest.xml index d6b75e5c3a6e4..ce1e473836ed8 100644 --- a/dev/tests/functional/tests/app/Magento/Catalog/Test/TestCase/Product/CreateSimpleProductEntityPartTwoTest.xml +++ b/dev/tests/functional/tests/app/Magento/Catalog/Test/TestCase/Product/CreateSimpleProductEntityPartTwoTest.xml @@ -159,6 +159,7 @@ <constraint name="Magento\Catalog\Test\Constraint\AssertCanSaveProduct" /> </variation> <variation name="CreateSimpleProductNotVisibleIndividually" summary="Check that url rewrite is not generated for product if visibility is 'Not Visible Individually'" ticketId="MAGETWO-53788"> + <data name="tag" xsi:type="string">mftf_migrated:yes</data> <data name="product/data/url_key" xsi:type="string">simple-product-%isolation%</data> <data name="product/data/name" xsi:type="string">Simple Product %isolation%</data> <data name="product/data/sku" xsi:type="string">simple_sku_%isolation%</data> diff --git a/dev/tests/functional/tests/app/Magento/CheckoutAgreements/Test/TestCase/DeleteTermEntityTest.xml b/dev/tests/functional/tests/app/Magento/CheckoutAgreements/Test/TestCase/DeleteTermEntityTest.xml index e16d5881e2ee4..b8298ffcee3c9 100644 --- a/dev/tests/functional/tests/app/Magento/CheckoutAgreements/Test/TestCase/DeleteTermEntityTest.xml +++ b/dev/tests/functional/tests/app/Magento/CheckoutAgreements/Test/TestCase/DeleteTermEntityTest.xml @@ -8,7 +8,7 @@ <config xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="../../../../../../vendor/magento/mtf/etc/variations.xsd"> <testCase name="Magento\CheckoutAgreements\Test\TestCase\DeleteTermEntityTest" summary="Delete Terms And Conditions" ticketId="MAGETWO-29687"> <variation name="DeleteTermEntityTestVariation1"> - <data name="tag" xsi:type="string">severity:S3</data> + <data name="tag" xsi:type="string">severity:S3, mftf_migrated:yes</data> <data name="configData" xsi:type="string">checkout_term_condition</data> <data name="products/0" xsi:type="string">catalogProductSimple::default</data> <data name="agreement/dataset" xsi:type="string">term_enabled_text</data> diff --git a/dev/tests/functional/tests/app/Magento/Cms/Test/TestCase/CreateCmsBlockEntityTest.xml b/dev/tests/functional/tests/app/Magento/Cms/Test/TestCase/CreateCmsBlockEntityTest.xml index 5f537baf9c979..bd9db3f34bdd7 100644 --- a/dev/tests/functional/tests/app/Magento/Cms/Test/TestCase/CreateCmsBlockEntityTest.xml +++ b/dev/tests/functional/tests/app/Magento/Cms/Test/TestCase/CreateCmsBlockEntityTest.xml @@ -8,7 +8,7 @@ <config xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="../../../../../../vendor/magento/mtf/etc/variations.xsd"> <testCase name="Magento\Cms\Test\TestCase\CreateCmsBlockEntityTest" summary="Create CMS Block" ticketId="MAGETWO-25578"> <variation name="CreateCmsBlockEntityTestVariation1"> - <data name="tag" xsi:type="string">test_type:extended_acceptance_test, severity:S1</data> + <data name="tag" xsi:type="string">test_type:extended_acceptance_test, severity:S1, mftf_migrated:yes</data> <data name="cmsBlock/data/title" xsi:type="string">block_%isolation%</data> <data name="cmsBlock/data/identifier" xsi:type="string">identifier_%isolation%</data> <data name="cmsBlock/data/stores/dataset/option_0" xsi:type="string">All Store Views</data> @@ -19,7 +19,7 @@ <constraint name="Magento\Cms\Test\Constraint\AssertCmsBlockOnCategoryPage" /> </variation> <variation name="CreateCmsBlockEntityTestVariation2"> - <data name="tag" xsi:type="string">severity:S2</data> + <data name="tag" xsi:type="string">severity:S2, mftf_migrated:yes</data> <data name="cmsBlock/data/title" xsi:type="string">block_%isolation%</data> <data name="cmsBlock/data/identifier" xsi:type="string">identifier_%isolation%</data> <data name="cmsBlock/data/stores/dataset/option_0" xsi:type="string">default</data> diff --git a/dev/tests/functional/tests/app/Magento/Cms/Test/TestCase/CreateCmsPageEntityTest.xml b/dev/tests/functional/tests/app/Magento/Cms/Test/TestCase/CreateCmsPageEntityTest.xml index 88434bbc009ce..bf477e1215486 100644 --- a/dev/tests/functional/tests/app/Magento/Cms/Test/TestCase/CreateCmsPageEntityTest.xml +++ b/dev/tests/functional/tests/app/Magento/Cms/Test/TestCase/CreateCmsPageEntityTest.xml @@ -8,7 +8,7 @@ <config xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="../../../../../../vendor/magento/mtf/etc/variations.xsd"> <testCase name="Magento\Cms\Test\TestCase\CreateCmsPageEntityTest" summary="Create Cms Page" ticketId="MAGETWO-25580"> <variation name="CreateCmsPageEntityTestVariation1" summary="Create CMS Content Page" ticketId="MAGETWO-12399"> - <data name="tag" xsi:type="string">test_type:acceptance_test, test_type:extended_acceptance_test, severity:S1</data> + <data name="tag" xsi:type="string">test_type:acceptance_test, test_type:extended_acceptance_test, severity:S1, mftf_migrated:yes</data> <data name="fixtureType" xsi:type="string">cmsPage</data> <data name="data/is_active" xsi:type="string">Yes</data> <data name="data/title" xsi:type="string">NewCmsPage%isolation%</data> @@ -20,7 +20,7 @@ <constraint name="Magento\Cms\Test\Constraint\AssertCmsPagePreview" /> </variation> <variation name="CreateCmsPageEntityTestVariation2" summary="Create page for default store view"> - <data name="tag" xsi:type="string">severity:S1</data> + <data name="tag" xsi:type="string">severity:S1, mftf_migrated:yes</data> <data name="fixtureType" xsi:type="string">cmsPage</data> <data name="data/is_active" xsi:type="string">Yes</data> <data name="data/title" xsi:type="string">NewCmsPage%isolation%</data> @@ -31,7 +31,7 @@ <constraint name="Magento\Cms\Test\Constraint\AssertCmsPageForm" /> </variation> <variation name="CreateCmsPageEntityTestVariation4" summary="Create disabled page"> - <data name="tag" xsi:type="string">severity:S3</data> + <data name="tag" xsi:type="string">severity:S3, mftf_migrated:yes</data> <data name="fixtureType" xsi:type="string">cmsPage</data> <data name="data/title" xsi:type="string">NewCmsPage%isolation%</data> <data name="data/identifier" xsi:type="string">identifier-%isolation%</data> @@ -42,7 +42,7 @@ <constraint name="Magento\Cms\Test\Constraint\AssertCmsPageDisabledOnFrontend" /> </variation> <variation name="CreateCmsPageEntityTestVariation5" summary="Block Cache Exploit" ticketId="MAGETWO-48017"> - <data name="tag" xsi:type="string">severity:S2</data> + <data name="tag" xsi:type="string">severity:S2, mftf_migrated:yes</data> <data name="fixtureType" xsi:type="string">cmsPage</data> <data name="data/title" xsi:type="string">NewCmsPage%isolation%</data> <data name="data/identifier" xsi:type="string">identifier-%isolation%</data> @@ -55,7 +55,7 @@ <constraint name="Magento\Cms\Test\Constraint\AssertCmsPageOnFrontend" /> </variation> <variation name="CreateCmsPageEntityTestVariation6" summary="Create CMS page with single store mode" ticketId="MAGETWO-59654"> - <data name="tag" xsi:type="string">test_type:acceptance_test, test_type:extended_acceptance_test</data> + <data name="tag" xsi:type="string">test_type:acceptance_test, test_type:extended_acceptance_test, mftf_migrated:yes</data> <data name="configData" xsi:type="string">enable_single_store_mode</data> <data name="fixtureType" xsi:type="string">cmsPage</data> <data name="data/is_active" xsi:type="string">Yes</data> diff --git a/dev/tests/functional/tests/app/Magento/Cms/Test/TestCase/DeleteCmsPageUrlRewriteEntityTest.xml b/dev/tests/functional/tests/app/Magento/Cms/Test/TestCase/DeleteCmsPageUrlRewriteEntityTest.xml index ea9d49d662bdd..9a04e3552a137 100644 --- a/dev/tests/functional/tests/app/Magento/Cms/Test/TestCase/DeleteCmsPageUrlRewriteEntityTest.xml +++ b/dev/tests/functional/tests/app/Magento/Cms/Test/TestCase/DeleteCmsPageUrlRewriteEntityTest.xml @@ -8,20 +8,20 @@ <config xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="../../../../../../vendor/magento/mtf/etc/variations.xsd"> <testCase name="Magento\Cms\Test\TestCase\DeleteCmsPageUrlRewriteEntityTest" summary="Delete Cms Page URL Rewrites" ticketId="MAGETWO-25915"> <variation name="DeleteCmsPageUrlRewriteEntityTestVariation1"> - <data name="tag" xsi:type="string">severity:S2,mftf_migrated:yes</data> + <data name="tag" xsi:type="string">severity:S2, mftf_migrated:yes</data> <data name="urlRewrite/dataset" xsi:type="string">cms_default_no_redirect</data> <constraint name="Magento\UrlRewrite\Test\Constraint\AssertUrlRewriteDeletedMessage" /> <constraint name="Magento\UrlRewrite\Test\Constraint\AssertUrlRewriteNotInGrid" /> <constraint name="Magento\UrlRewrite\Test\Constraint\AssertPageByUrlRewriteIsNotFound" /> </variation> <variation name="DeleteCmsPageUrlRewriteEntityTestVariation2"> - <data name="tag" xsi:type="string">severity:S2,mftf_migrated:yes</data> + <data name="tag" xsi:type="string">severity:S2, mftf_migrated:yes</data> <data name="urlRewrite/dataset" xsi:type="string">cms_default_permanent_redirect</data> <constraint name="Magento\UrlRewrite\Test\Constraint\AssertUrlRewriteDeletedMessage" /> <constraint name="Magento\UrlRewrite\Test\Constraint\AssertPageByUrlRewriteIsNotFound" /> </variation> <variation name="DeleteCmsPageUrlRewriteEntityTestVariation3"> - <data name="tag" xsi:type="string">severity:S2,mftf_migrated:yes</data> + <data name="tag" xsi:type="string">severity:S2, mftf_migrated:yes</data> <data name="urlRewrite/dataset" xsi:type="string">cms_default_temporary_redirect</data> <constraint name="Magento\UrlRewrite\Test\Constraint\AssertUrlRewriteDeletedMessage" /> <constraint name="Magento\UrlRewrite\Test\Constraint\AssertPageByUrlRewriteIsNotFound" /> diff --git a/dev/tests/functional/tests/app/Magento/Cms/Test/TestCase/UpdateCmsPageEntityTest.xml b/dev/tests/functional/tests/app/Magento/Cms/Test/TestCase/UpdateCmsPageEntityTest.xml index 4bee113add071..61ca945adcad9 100644 --- a/dev/tests/functional/tests/app/Magento/Cms/Test/TestCase/UpdateCmsPageEntityTest.xml +++ b/dev/tests/functional/tests/app/Magento/Cms/Test/TestCase/UpdateCmsPageEntityTest.xml @@ -8,7 +8,7 @@ <config xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="../../../../../../vendor/magento/mtf/etc/variations.xsd"> <testCase name="Magento\Cms\Test\TestCase\UpdateCmsPageEntityTest" summary="Update Cms Page" ticketId="MAGETWO-25186"> <variation name="UpdateCmsPageEntityTestVariation1"> - <data name="tag" xsi:type="string">severity:S3</data> + <data name="tag" xsi:type="string">severity:S3, mftf_migrated:yes</data> <data name="cms/data/title" xsi:type="string">CmsPageEdited%isolation%</data> <data name="cms/data/is_active" xsi:type="string">No</data> <data name="cms/data/content/content" xsi:type="string">cms_page_text_content_after_edit</data> @@ -16,7 +16,7 @@ <constraint name="Magento\Cms\Test\Constraint\AssertCmsPageDisabledOnFrontend" /> </variation> <variation name="UpdateCmsPageEntityTestVariation2"> - <data name="tag" xsi:type="string">severity:S1</data> + <data name="tag" xsi:type="string">severity:S1, mftf_migrated:yes</data> <data name="cms/data/title" xsi:type="string">CmsPageEdited%isolation%</data> <data name="cms/data/identifier" xsi:type="string">cms_page_url_edited_%isolation%</data> <data name="cms/data/content_heading" xsi:type="string">Content Heading TextEdited</data> diff --git a/dev/tests/functional/tests/app/Magento/Cms/Test/TestCase/UpdateCmsPageRewriteEntityTest.xml b/dev/tests/functional/tests/app/Magento/Cms/Test/TestCase/UpdateCmsPageRewriteEntityTest.xml index 1125ce8d916c1..872a719d54fe9 100644 --- a/dev/tests/functional/tests/app/Magento/Cms/Test/TestCase/UpdateCmsPageRewriteEntityTest.xml +++ b/dev/tests/functional/tests/app/Magento/Cms/Test/TestCase/UpdateCmsPageRewriteEntityTest.xml @@ -8,7 +8,7 @@ <config xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="../../../../../../vendor/magento/mtf/etc/variations.xsd"> <testCase name="Magento\Cms\Test\TestCase\UpdateCmsPageRewriteEntityTest" summary="Update Cms Page URL Rewrites " ticketId="MAGETWO-26173"> <variation name="UpdateCmsPageRewriteEntityTestVariation1"> - <data name="tag" xsi:type="string">severity:S2,mftf_migrated:yes</data> + <data name="tag" xsi:type="string">severity:S2, mftf_migrated:yes</data> <data name="cmsPageRewrite/dataset" xsi:type="string">cms_default_no_redirect</data> <data name="urlRewrite/data/store_id" xsi:type="string">Main Website/Main Website Store/%default%</data> <data name="urlRewrite/data/request_path" xsi:type="string">request_path%isolation%</data> @@ -18,7 +18,7 @@ <constraint name="Magento\Cms\Test\Constraint\AssertUrlRewriteCmsPageRedirect" /> </variation> <variation name="UpdateCmsPageRewriteEntityTestVariation2"> - <data name="tag" xsi:type="string">severity:S2,mftf_migrated:yes</data> + <data name="tag" xsi:type="string">severity:S2, mftf_migrated:yes</data> <data name="cmsPageRewrite/dataset" xsi:type="string">cms_default_temporary_redirect</data> <data name="urlRewrite/data/store_id" xsi:type="string">Main Website/Main Website Store/Default Store View</data> <data name="urlRewrite/data/request_path" xsi:type="string">request_path%isolation%.html</data> @@ -28,7 +28,7 @@ <constraint name="Magento\Cms\Test\Constraint\AssertUrlRewriteCmsPageRedirect" /> </variation> <variation name="UpdateCmsPageRewriteEntityTestVariation3"> - <data name="tag" xsi:type="string">severity:S2,mftf_migrated:yes</data> + <data name="tag" xsi:type="string">severity:S2, mftf_migrated:yes</data> <data name="cmsPageRewrite/dataset" xsi:type="string">cms_default_permanent_redirect</data> <data name="urlRewrite/data/store_id" xsi:type="string">Main Website/Main Website Store/Default Store View</data> <data name="urlRewrite/data/request_path" xsi:type="string">request_path%isolation%.htm</data> diff --git a/dev/tests/functional/tests/app/Magento/Customer/Test/TestCase/DeleteSystemCustomerGroupTest.xml b/dev/tests/functional/tests/app/Magento/Customer/Test/TestCase/DeleteSystemCustomerGroupTest.xml index c8cf44a674b28..822cbcfbcb7ee 100644 --- a/dev/tests/functional/tests/app/Magento/Customer/Test/TestCase/DeleteSystemCustomerGroupTest.xml +++ b/dev/tests/functional/tests/app/Magento/Customer/Test/TestCase/DeleteSystemCustomerGroupTest.xml @@ -10,10 +10,12 @@ <variation name="DeleteSystemCustomerGroup1" summary="Checks that NOT LOGGED IN Customer Group cannot be deleted"> <data name="customerGroup/dataset" xsi:type="string">NOT_LOGGED_IN</data> <constraint name="Magento\Customer\Test\Constraint\AssertNoDeleteForSystemCustomerGroup" /> + <data name="tag" xsi:type="string">mftf_migrated:yes</data> </variation> <variation name="DeleteSystemCustomerGroup2" summary="Checks that General Customer Group cannot be deleted"> <data name="customerGroup/dataset" xsi:type="string">General</data> <constraint name="Magento\Customer\Test\Constraint\AssertNoDeleteForSystemCustomerGroup" /> + <data name="tag" xsi:type="string">mftf_migrated:yes</data> </variation> </testCase> </config> diff --git a/dev/tests/functional/tests/app/Magento/Integration/Test/TestCase/CreateIntegrationWithDuplicatedNameTest.xml b/dev/tests/functional/tests/app/Magento/Integration/Test/TestCase/CreateIntegrationWithDuplicatedNameTest.xml index 0a3fff5b3e510..2cd14d9de05a1 100644 --- a/dev/tests/functional/tests/app/Magento/Integration/Test/TestCase/CreateIntegrationWithDuplicatedNameTest.xml +++ b/dev/tests/functional/tests/app/Magento/Integration/Test/TestCase/CreateIntegrationWithDuplicatedNameTest.xml @@ -8,6 +8,7 @@ <config xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="../../../../../../vendor/magento/mtf/etc/variations.xsd"> <testCase name="Magento\Integration\Test\TestCase\CreateIntegrationWithDuplicatedNameTest" summary="Create Integration with existing name" ticketId="MAGETWO-16756"> <variation name="CreateIntegrationWithDuplicatedNameTestVariation1"> + <data name="tag" xsi:type="string">mftf_migrated:yes</data> <data name="integration/dataset" xsi:type="string">default</data> <constraint name="Magento\Integration\Test\Constraint\AssertIntegrationNameDuplicationErrorMessage" /> </variation> diff --git a/dev/tests/functional/tests/app/Magento/Payment/Test/Repository/CreditCard.xml b/dev/tests/functional/tests/app/Magento/Payment/Test/Repository/CreditCard.xml index 7e3266cadf0a3..b2c866f9cdce1 100644 --- a/dev/tests/functional/tests/app/Magento/Payment/Test/Repository/CreditCard.xml +++ b/dev/tests/functional/tests/app/Magento/Payment/Test/Repository/CreditCard.xml @@ -10,7 +10,7 @@ <dataset name="visa_default"> <field name="cc_number" xsi:type="string">4111111111111111</field> <field name="cc_exp_month" xsi:type="string">01 - January</field> - <field name="cc_exp_year" xsi:type="string">2020</field> + <field name="cc_exp_year" xsi:type="string">2025</field> <field name="cc_cid" xsi:type="string">123</field> </dataset> @@ -18,28 +18,28 @@ <field name="cc_type" xsi:type="string">Visa</field> <field name="cc_number" xsi:type="string">4111111111111111</field> <field name="cc_exp_month" xsi:type="string">01 - January</field> - <field name="cc_exp_year" xsi:type="string">2020</field> + <field name="cc_exp_year" xsi:type="string">2025</field> <field name="cc_cid" xsi:type="string">123</field> </dataset> <dataset name="visa_alt"> <field name="cc_number" xsi:type="string">4012888888881881</field> <field name="cc_exp_month" xsi:type="string">02 - February</field> - <field name="cc_exp_year" xsi:type="string">2021</field> + <field name="cc_exp_year" xsi:type="string">2025</field> <field name="cc_cid" xsi:type="string">123</field> </dataset> <dataset name="amex_default"> <field name="cc_number" xsi:type="string">378282246310005</field> <field name="cc_exp_month" xsi:type="string">02 - February</field> - <field name="cc_exp_year" xsi:type="string">2021</field> + <field name="cc_exp_year" xsi:type="string">2025</field> <field name="cc_cid" xsi:type="string">1234</field> </dataset> <dataset name="visa_direct"> <field name="cc_number" xsi:type="string">4617747819866651</field> <field name="cc_exp_month" xsi:type="string">01 - January</field> - <field name="cc_exp_year" xsi:type="string">2020</field> + <field name="cc_exp_year" xsi:type="string">2025</field> <field name="cc_cid" xsi:type="string">123</field> </dataset> @@ -54,14 +54,14 @@ <dataset name="visa_cvv_mismatch"> <field name="cc_number" xsi:type="string">4111111111111111</field> <field name="cc_exp_month" xsi:type="string">01 - January</field> - <field name="cc_exp_year" xsi:type="string">2020</field> + <field name="cc_exp_year" xsi:type="string">2025</field> <field name="cc_cid" xsi:type="string">306</field> </dataset> <dataset name="mastercard_default"> <field name="cc_number" xsi:type="string">5555555555554444</field> <field name="cc_exp_month" xsi:type="string">01 - January</field> - <field name="cc_exp_year" xsi:type="string">2020</field> + <field name="cc_exp_year" xsi:type="string">2025</field> <field name="cc_cid" xsi:type="string">123</field> </dataset> </repository> diff --git a/dev/tests/functional/tests/app/Magento/ProductVideo/Test/TestCase/ConfigurableProductVideoTest.xml b/dev/tests/functional/tests/app/Magento/ProductVideo/Test/TestCase/ConfigurableProductVideoTest.xml index 25fe2b75dfe48..f9c1019290a63 100644 --- a/dev/tests/functional/tests/app/Magento/ProductVideo/Test/TestCase/ConfigurableProductVideoTest.xml +++ b/dev/tests/functional/tests/app/Magento/ProductVideo/Test/TestCase/ConfigurableProductVideoTest.xml @@ -8,6 +8,7 @@ <config xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="../../../../../../vendor/magento/mtf/etc/variations.xsd"> <testCase name="Magento\ProductVideo\Test\TestCase\ConfigurableProductVideoTest" summary="Video displaying for simple variation of configurable on product page" ticketId="MAGETWO-69381"> <variation name="ConfigurableProductVideoTestVariation1"> + <data name="tag" xsi:type="string">stable:no</data> <data name="product/dataset" xsi:type="string">configurable_with_video</data> <data name="simpleProductVideo/data/media_gallery/images/0/video_url" xsi:type="string">https://vimeo.com/16342611</data> <data name="variation" xsi:type="string">attribute_key_0:option_key_0 attribute_key_1:option_key_0</data> diff --git a/dev/tests/functional/tests/app/Magento/ProductVideo/Test/TestCase/UpdateProductVideoTest.xml b/dev/tests/functional/tests/app/Magento/ProductVideo/Test/TestCase/UpdateProductVideoTest.xml index 9d5dd49eadc6f..a6ad2c6e621f4 100644 --- a/dev/tests/functional/tests/app/Magento/ProductVideo/Test/TestCase/UpdateProductVideoTest.xml +++ b/dev/tests/functional/tests/app/Magento/ProductVideo/Test/TestCase/UpdateProductVideoTest.xml @@ -8,6 +8,7 @@ <config xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="../../../../../../vendor/magento/mtf/etc/variations.xsd"> <testCase name="Magento\ProductVideo\Test\TestCase\UpdateProductVideoTest" summary="Add Video to PCF"> <variation name="UpdateProductVideoTestVariation1" summary="Edit Youtube URL" ticketId="MAGETWO-43664"> + <data name="tag" xsi:type="string">stable:no</data> <data name="productVideo/dataset" xsi:type="string">product_with_video_youtube</data> <data name="product/data/sku" xsi:type="string">simple_product_with_category_%isolation%</data> <data name="product/data/media_gallery/images/0/video_url" xsi:type="string">https://vimeo.com/16342611</data> @@ -39,6 +40,7 @@ <constraint name="Magento\Catalog\Test\Constraint\AssertProductForm" /> </variation> <variation name="GetVideoInfoTestVariation1" summary="Validate Youtube video info" ticketId="MAGETWO-43663"> + <data name="tag" xsi:type="string">stable:no</data> <data name="productVideo/dataset" xsi:type="string">product_with_video_youtube</data> <data name="product/data/sku" xsi:type="string">simple_product_with_category_%isolation%</data> <data name="product/data/media_gallery/images/0/video_url" xsi:type="string">https://youtu.be/WMp2PvU2qi8</data> diff --git a/dev/tests/functional/tests/app/Magento/Security/Test/TestCase/SecureChangingCustomerEmailTest.xml b/dev/tests/functional/tests/app/Magento/Security/Test/TestCase/SecureChangingCustomerEmailTest.xml index bd8a417d48bf3..8c7f35add2a8d 100644 --- a/dev/tests/functional/tests/app/Magento/Security/Test/TestCase/SecureChangingCustomerEmailTest.xml +++ b/dev/tests/functional/tests/app/Magento/Security/Test/TestCase/SecureChangingCustomerEmailTest.xml @@ -8,7 +8,7 @@ <config xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="../../../../../../vendor/magento/mtf/etc/variations.xsd"> <testCase name="Magento\Security\Test\TestCase\SecureChangingCustomerEmailTest" summary="Secure Changing Customer Email" ticketId="MAGETWO-49041"> <variation name="SecureChangingEmailV1" summary="Change Email checkbox is checked, enter new email, correct password"> - <data name="tag" xsi:type="string">severity:S1</data> + <data name="tag" xsi:type="string">severity:S1, mftf_migrated:yes</data> <data name="initialCustomer/dataset" xsi:type="string">default</data> <data name="customer/data/email" xsi:type="string">JaneDoe_%isolation%@example.com</data> <data name="customer/data/current_password" xsi:type="string">123123^q</data> @@ -16,7 +16,7 @@ <constraint name="Magento\Security\Test\Constraint\AssertCustomerEmailChanged" /> </variation> <variation name="SecureChangingEmailV2" summary="Change Email checkbox is checked, enter new email, wrong password"> - <data name="tag" xsi:type="string">severity:S2</data> + <data name="tag" xsi:type="string">severity:S2, mftf_migrated:yes</data> <data name="initialCustomer/dataset" xsi:type="string">default</data> <data name="customer/data/email" xsi:type="string">JaneDoe_%isolation%@example.com</data> <data name="customer/data/current_password" xsi:type="string">123123123</data> diff --git a/dev/tests/functional/tests/app/Magento/Security/Test/TestCase/SecureChangingCustomerPasswordTest.xml b/dev/tests/functional/tests/app/Magento/Security/Test/TestCase/SecureChangingCustomerPasswordTest.xml index da5e312cf0bc9..1c5807401ca7b 100644 --- a/dev/tests/functional/tests/app/Magento/Security/Test/TestCase/SecureChangingCustomerPasswordTest.xml +++ b/dev/tests/functional/tests/app/Magento/Security/Test/TestCase/SecureChangingCustomerPasswordTest.xml @@ -8,7 +8,7 @@ <config xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="../../../../../../vendor/magento/mtf/etc/variations.xsd"> <testCase name="Magento\Security\Test\TestCase\SecureChangingCustomerPasswordTest" summary="Secure Changing Customer Password" ticketId="MAGETWO-49043"> <variation name="SecureChangingPasswordV1" summary="Change Password checkbox is checked, enter correct password"> - <data name="tag" xsi:type="string">severity:S1</data> + <data name="tag" xsi:type="string">severity:S1, mftf_migrated:yes</data> <data name="initialCustomer/dataset" xsi:type="string">default</data> <data name="customer/data/current_password" xsi:type="string">123123^q</data> <data name="customer/data/password" xsi:type="string">123123Qa</data> @@ -18,7 +18,7 @@ <constraint name="Magento\Customer\Test\Constraint\AssertCustomerPasswordChanged" /> </variation> <variation name="SecureChangingPasswordV2" summary="Change Password checkbox is checked, enter wrong password"> - <data name="tag" xsi:type="string">severity:S2</data> + <data name="tag" xsi:type="string">severity:S2, mftf_migrated:yes</data> <data name="initialCustomer/dataset" xsi:type="string">default</data> <data name="customer/data/current_password" xsi:type="string">123123123</data> <data name="customer/data/password" xsi:type="string">123123Qa</data> @@ -27,7 +27,7 @@ <constraint name="Magento\Customer\Test\Constraint\AssertChangePasswordFailMessage" /> </variation> <variation name="SecureChangingPasswordV3" summary="Change Password checkbox is unchecked"> - <data name="tag" xsi:type="string">severity:S3</data> + <data name="tag" xsi:type="string">severity:S3, mftf_migrated:yes</data> <data name="initialCustomer/dataset" xsi:type="string">default</data> <data name="check" xsi:type="string">0</data> <constraint name="Magento\Security\Test\Constraint\AssertDefaultAccountInformation" /> diff --git a/dev/tests/functional/tests/app/Magento/Signifyd/Test/Block/Adminhtml/Order/Grid.php b/dev/tests/functional/tests/app/Magento/Signifyd/Test/Block/Adminhtml/Order/Grid.php deleted file mode 100644 index 54f580f48dcf4..0000000000000 --- a/dev/tests/functional/tests/app/Magento/Signifyd/Test/Block/Adminhtml/Order/Grid.php +++ /dev/null @@ -1,33 +0,0 @@ -<?php -/** - * Copyright © Magento, Inc. All rights reserved. - * See COPYING.txt for license details. - */ -namespace Magento\Signifyd\Test\Block\Adminhtml\Order; - -use Magento\Ui\Test\Block\Adminhtml\DataGrid; - -/** - * Admin Data Grid for managing "Sales Order" entities. - */ -class Grid extends DataGrid -{ - /** - * Filters array mapping. - * - * @var array - */ - protected $filters = [ - 'id' => [ - 'selector' => '[name="increment_id"]', - ], - 'status' => [ - 'selector' => '[name="status"]', - 'input' => 'select', - ], - 'signifyd_guarantee_status' => [ - 'selector' => '[name="signifyd_guarantee_status"]', - 'input' => 'select' - ] - ]; -} diff --git a/dev/tests/functional/tests/app/Magento/Signifyd/Test/Block/Adminhtml/Order/View/FraudProtection.php b/dev/tests/functional/tests/app/Magento/Signifyd/Test/Block/Adminhtml/Order/View/FraudProtection.php deleted file mode 100644 index e2b4e6f748977..0000000000000 --- a/dev/tests/functional/tests/app/Magento/Signifyd/Test/Block/Adminhtml/Order/View/FraudProtection.php +++ /dev/null @@ -1,31 +0,0 @@ -<?php -/** - * Copyright © Magento, Inc. All rights reserved. - * See COPYING.txt for license details. - */ -namespace Magento\Signifyd\Test\Block\Adminhtml\Order\View; - -use Magento\Mtf\Block\Block; - -/** - * Information about fraud protection on order page. - */ -class FraudProtection extends Block -{ - /** - * Case Guarantee Disposition. - * - * @var string - */ - private $caseGuaranteeDisposition = 'td.col-guarantee-disposition'; - - /** - * Get Case Guarantee Disposition status. - * - * @return string - */ - public function getCaseGuaranteeDisposition() - { - return $this->_rootElement->find($this->caseGuaranteeDisposition)->getText(); - } -} diff --git a/dev/tests/functional/tests/app/Magento/Signifyd/Test/Block/SignifydConsole/CaseInfo.php b/dev/tests/functional/tests/app/Magento/Signifyd/Test/Block/SignifydConsole/CaseInfo.php deleted file mode 100644 index 1529755fdc3c9..0000000000000 --- a/dev/tests/functional/tests/app/Magento/Signifyd/Test/Block/SignifydConsole/CaseInfo.php +++ /dev/null @@ -1,245 +0,0 @@ -<?php -/** - * Copyright © Magento, Inc. All rights reserved. - * See COPYING.txt for license details. - */ -namespace Magento\Signifyd\Test\Block\SignifydConsole; - -use Magento\Mtf\Block\Block; - -/** - * Case information block. - */ -class CaseInfo extends Block -{ - /** - * Css selector of "Flag Case As Good" button. - * - * @var string - */ - private $flagCaseAsGoodButton = '[class*="flag-case-good"]'; - - /** - * Css selector of "Flag Case As Bad" button. - * - * @var string - */ - private $flagCaseAsBadButton = '[class*="flag-case-bad"]'; - - /** - * Css selector of guarantee status. - * - * @var string - */ - private $guaranteeDisposition = '[class*="guarantee-status"]'; - - /** - * Css selector of CVV response description. - * - * @var string - */ - private $cvvResponseDescription = '[ng-bind="caseOrderSummary.cvvResponseDescription"]'; - - /** - * Css selector of CVV response code. - * - * @var string - */ - private $cvvResponseCode = '[ng-bind="caseOrderSummary.cvvResponseCode"]'; - - /** - * Css selector of AVS response description. - * - * @var string - */ - private $avsResponseDescription = '[ng-bind="caseOrderSummary.avsResponseDescription"]'; - - /** - * Css selector of AVS response code. - * - * @var string - */ - private $avsResponseCode = '[ng-bind="caseOrderSummary.avsResponseCode"]'; - - /** - * Css selector of displayed case order id. - * - * @var string - */ - private $orderId = '[ng-bind="currentCase.caseIdDisplay"]'; - - /** - * Css selector of displayed order amount. - * - * @var string - */ - private $orderAmount = '[ng-bind*="currentCase.orderTotalAmount"]'; - - /** - * Locator value for order amount currency. - * - * @var string - */ - private $orderAmountCurrency = '[ng-bind*="currentCase.currency"]'; - - /** - * Css selector of displayed card holder name. - * - * @var string - */ - private $cardHolder = '[data-dropdown="peopleLinks0_card_holders"]'; - - /** - * Css selector of displayed billing address. - * - * @var string - */ - private $billingAddress = '[data-dropdown="streetLinks0"]'; - - /** - * Locator value for "No analysis available" block in "Device" container. - * - * @var string - */ - private $noDeviceAnalysisAvailable = '[ng-hide^="caseAnalysis.deviceAnalysis.details.length"]'; - - /** - * Locator value for "Shipping Price" block. - * - * @var string - */ - private $shippingPrice = '[ng-if="shipment.shippingPrice"]'; - - /** - * Check if device data are present. - * - * @return bool - */ - public function isAvailableDeviceData() - { - return !$this->_rootElement->find($this->noDeviceAnalysisAvailable)->isVisible(); - } - - /** - * Returns shipping price. - * - * @return string - */ - public function getShippingPrice() - { - return $this->_rootElement->find($this->shippingPrice)->getText(); - } - - /** - * Flags case as good or bad. - * - * @param string $flagType - * @return void - */ - public function flagCase($flagType) - { - $flagSelector = ($flagType === 'Good') - ? $this->flagCaseAsGoodButton - : $this->flagCaseAsBadButton; - - $this->_rootElement->find($flagSelector)->click(); - } - - /** - * Flags case as bad. - * - * @return void - */ - public function flagCaseAsBad() - { - $this->_rootElement->find($this->flagCaseAsBadButton)->click(); - } - - /** - * Gets guarantee disposition. - * - * @return string - */ - public function getGuaranteeDisposition() - { - return $this->_rootElement->find($this->guaranteeDisposition)->getText(); - } - - /** - * Gets CVV response. - * - * @return string - */ - public function getCvvResponse() - { - return sprintf( - '%s (%s)', - $this->_rootElement->find($this->cvvResponseDescription)->getText(), - $this->_rootElement->find($this->cvvResponseCode)->getText() - ); - } - - /** - * Gets AVS response. - * - * @return string - */ - public function getAvsResponse() - { - return sprintf( - '%s (%s)', - $this->_rootElement->find($this->avsResponseDescription)->getText(), - $this->_rootElement->find($this->avsResponseCode)->getText() - ); - } - - /** - * Gets displayed order id. - * - * @return string - */ - public function getOrderId() - { - return $this->_rootElement->find($this->orderId)->getText(); - } - - /** - * Gets displayed order amount. - * - * @return string - */ - public function getOrderAmount() - { - return $this->_rootElement->find($this->orderAmount)->getText(); - } - - /** - * Returns displayed order amount currency. - * - * @return string - */ - public function getOrderAmountCurrency() - { - return $this->_rootElement->find($this->orderAmountCurrency)->getText(); - } - - /** - * Gets displayed card holder name. - * - * @return string - */ - public function getCardHolder() - { - return $this->_rootElement->find($this->cardHolder)->getText(); - } - - /** - * Gets displayed billing address. - * - * @return string - */ - public function getBillingAddress() - { - return $this->_rootElement->find($this->billingAddress)->getText(); - } -} diff --git a/dev/tests/functional/tests/app/Magento/Signifyd/Test/Block/SignifydConsole/CaseSearch.php b/dev/tests/functional/tests/app/Magento/Signifyd/Test/Block/SignifydConsole/CaseSearch.php deleted file mode 100644 index 16621f9a0cd48..0000000000000 --- a/dev/tests/functional/tests/app/Magento/Signifyd/Test/Block/SignifydConsole/CaseSearch.php +++ /dev/null @@ -1,119 +0,0 @@ -<?php -/** - * Copyright © Magento, Inc. All rights reserved. - * See COPYING.txt for license details. - */ -namespace Magento\Signifyd\Test\Block\SignifydConsole; - -use Magento\Mtf\Block\Form; - -/** - * Side block with case search and cases list. - */ -class CaseSearch extends Form -{ - /** - * Css selector of search input. - * - * @var string - */ - private $searchBar = '[id=queueSearchBar]'; - - /** - * Css selector of search submit button. - * - * @var string - */ - private $submitButton = '[type=submit]'; - - /** - * Css selector of searched element in cases list. - * - * @var string - */ - private $selectCaseLink = 'ul[case-list=cases] li[case-list-case=case] a'; - - /** - * Locator for resolving applied filters list. - * - * @var string - */ - private $appliedFilters = '.app-taglist > ul > li > a'; - - /** - * Locator for loading spinner. - * - * @var string - */ - private $spinner = '.cases-loading-spinner'; - - /** - * Fill search input with customer name and submit. - * - * @param string $customerName - * @return void - */ - public function searchCaseByCustomerName($customerName) - { - $this->resetFilters(); - $this->_rootElement->find($this->searchBar)->setValue($customerName); - $this->_rootElement->find($this->submitButton)->click(); - $this->waitLoadingSpinner(); - } - - /** - * Reset applied filters. - * - * @return void - */ - private function resetFilters(): void - { - $filters = $this->_rootElement->getElements($this->appliedFilters); - if (!empty($filters)) { - foreach ($filters as $filter) { - $filter->click(); - $this->waitLoadingSpinner(); - } - } - } - - /** - * Wait until loading spinner disappeared. - * - * @return void - */ - private function waitLoadingSpinner(): void - { - $this->waitForElementNotVisible($this->spinner); - } - - /** - * Checks if any case is visible. - * - * @return bool - */ - public function isAnyCaseVisible(): bool - { - return $this->_rootElement->find($this->selectCaseLink)->isVisible(); - } - - /** - * Select searched case. - * - * @return void - */ - public function selectCase() - { - $this->_rootElement->find($this->selectCaseLink)->click(); - } - - /** - * Waiting of case page loading. - * - * @return void - */ - public function waitForLoading() - { - $this->waitForElementVisible($this->searchBar); - } -} diff --git a/dev/tests/functional/tests/app/Magento/Signifyd/Test/Block/SignifydConsole/SignifydLogin.php b/dev/tests/functional/tests/app/Magento/Signifyd/Test/Block/SignifydConsole/SignifydLogin.php deleted file mode 100644 index 7705a67360e55..0000000000000 --- a/dev/tests/functional/tests/app/Magento/Signifyd/Test/Block/SignifydConsole/SignifydLogin.php +++ /dev/null @@ -1,64 +0,0 @@ -<?php -/** - * Copyright © Magento, Inc. All rights reserved. - * See COPYING.txt for license details. - */ -namespace Magento\Signifyd\Test\Block\SignifydConsole; - -use Magento\Mtf\Block\Form; -use Magento\Mtf\Client\Element\SimpleElement; -use Magento\Mtf\Fixture\FixtureInterface; - -/** - * Signifyd login block. - */ -class SignifydLogin extends Form -{ - /** - * Css selector of Signifyd login button. - * - * @var string - */ - private $loginButton = '[type=submit]'; - - /** - * Locator for admin form notification window. - * - * @var string - */ - private $notificationCloseButton = '.wm-close-button'; - - /** - * @inheritdoc - */ - public function fill(FixtureInterface $fixture, SimpleElement $element = null) - { - $this->closeNotification(); - - return parent::fill($fixture, $element); - } - - /** - * Login to Signifyd. - * - * @return void - */ - public function login() - { - $this->closeNotification(); - $this->_rootElement->find($this->loginButton)->click(); - } - - /** - * Close notification popup. - * - * @return void - */ - private function closeNotification(): void - { - $notification = $this->browser->find($this->notificationCloseButton); - if ($notification->isVisible()) { - $notification->click(); - } - } -} diff --git a/dev/tests/functional/tests/app/Magento/Signifyd/Test/Block/SignifydConsole/Webhooks.php b/dev/tests/functional/tests/app/Magento/Signifyd/Test/Block/SignifydConsole/Webhooks.php deleted file mode 100644 index 424d5681c07c6..0000000000000 --- a/dev/tests/functional/tests/app/Magento/Signifyd/Test/Block/SignifydConsole/Webhooks.php +++ /dev/null @@ -1,216 +0,0 @@ -<?php -/** - * Copyright © Magento, Inc. All rights reserved. - * See COPYING.txt for license details. - */ -namespace Magento\Signifyd\Test\Block\SignifydConsole; - -use Magento\Mtf\Block\Block; -use Magento\Mtf\Client\ElementInterface; -use Magento\Mtf\Client\Locator; - -/** - * Signifyd webhook addition block. - */ -class Webhooks extends Block -{ - /** - * Map of webhook event select option values on names of events in webhook grid. - * - * @var array - */ - private $webhookEventOptionsMap = [ - 'CASE_CREATION' => 'Case Creation', - 'CASE_REVIEW' => 'Case Review', - 'GUARANTEE_COMPLETION' => 'Guarantee Completion' - ]; - - /** - * XPath selector of webhook element added into grid. - * - * @var string - */ - private $webhookAddedElement = '//table[@id="webhooks"]//tr[./td/span/text()="%s" and ./td/span/text()="%s"]'; - - /** - * Css selector of webhook url input. - * - * @var string - */ - private $webhookUrl = '[id="webhookUrl"]'; - - /** - * XPath selector of test team select option. - * - * @var string - */ - private $webhookTeamOption = './/select[@id="webhookTeams"]//option[text()="%s"]'; - - /** - * Css selector of webhook event select option. - * - * @var string - */ - private $webhookEventOption = 'select[id="webhookEvent"] option[value="%s"]'; - - /** - * Css selector of webhook addition button. - * - * @var string - */ - private $webhookAddButton = '[id="addWebhook"] [type=submit]'; - - /** - * Css selector of delete button in element of webhook grid. - * - * @var string - */ - private $webhookDeleteButton = '[class*="webhook-delete"]'; - - /** - * Css selector of confirming button for deleting webhook. - * - * @var string - */ - private $webhookDeleteConfirmButton = '[class="appriseOuter"] button[value="ok"]'; - - /** - * Creates new set of webhooks, if it not exists. - * - * @param string $team - * @return void - */ - public function create($team) - { - $handlerUrl = $this->getHandlerUrl(); - - foreach ($this->webhookEventOptionsMap as $webhookEventCode => $webhookEventName) { - if ($this->getWebhook($team, $webhookEventName)) { - continue; - } - - $this->addWebhook($handlerUrl, $webhookEventCode, $team); - } - } - - /** - * Deletes set of webhooks. - * - * @param string $team - * @return void - */ - public function cleanup($team) - { - foreach ($this->webhookEventOptionsMap as $webhookEventName) { - if ($webhook = $this->getWebhook($team, $webhookEventName)) { - $this->deleteWebhook($webhook); - } - } - } - - /** - * Gets webhook if exists. - * - * @param string $team - * @param string $webhookEventName - * @return ElementInterface|null - */ - private function getWebhook($team, $webhookEventName) - { - $webhook = $this->_rootElement->find( - sprintf($this->webhookAddedElement, $team, $webhookEventName), - Locator::SELECTOR_XPATH - ); - - return $webhook->isPresent() ? $webhook : null; - } - - /** - * Delete webhook element with confirmation popup. - * - * @param ElementInterface $webhook - * @return void - */ - private function deleteWebhook(ElementInterface $webhook) - { - $webhook->find($this->webhookDeleteButton)->click(); - $this->_rootElement->find($this->webhookDeleteConfirmButton)->click(); - } - - /** - * Sets webhook data and add it. - * - * @param string $handlerUrl - * @param string $webhookEventCode - * @param string $team - * @return void - */ - private function addWebhook( - $handlerUrl, - $webhookEventCode, - $team - ) { - $this->setEvent($webhookEventCode); - $this->setTeam($team); - $this->setUrl($handlerUrl); - $this->submit(); - } - - /** - * Sets appropriate webhook event select option by code. - * - * @param string $webhookEventCode - * @return void - */ - private function setEvent($webhookEventCode) - { - $this->_rootElement->find( - sprintf($this->webhookEventOption, $webhookEventCode) - )->click(); - } - - /** - * Sets test team select option. - * - * @param string $team - * @return void - */ - private function setTeam($team) - { - $this->_rootElement->find( - sprintf($this->webhookTeamOption, $team), - Locator::SELECTOR_XPATH - )->click(); - } - - /** - * Sets webhook handler url input value. - * - * @param string $handlerUrl - * @return void - */ - private function setUrl($handlerUrl) - { - $this->_rootElement->find($this->webhookUrl)->setValue($handlerUrl); - } - - /** - * Add webhook element. - * - * @return void - */ - private function submit() - { - $this->_rootElement->find($this->webhookAddButton)->click(); - } - - /** - * Gets webhook handler url. - * - * @return string - */ - private function getHandlerUrl() - { - return $_ENV['app_frontend_url'] . 'signifyd/webhooks/handler'; - } -} diff --git a/dev/tests/functional/tests/app/Magento/Signifyd/Test/Constraint/AssertAwaitingSignifydGuaranteeInCommentsHistory.php b/dev/tests/functional/tests/app/Magento/Signifyd/Test/Constraint/AssertAwaitingSignifydGuaranteeInCommentsHistory.php deleted file mode 100644 index f85e77a5c421b..0000000000000 --- a/dev/tests/functional/tests/app/Magento/Signifyd/Test/Constraint/AssertAwaitingSignifydGuaranteeInCommentsHistory.php +++ /dev/null @@ -1,73 +0,0 @@ -<?php -/** - * Copyright © Magento, Inc. All rights reserved. - * See COPYING.txt for license details. - */ -namespace Magento\Signifyd\Test\Constraint; - -use Magento\Mtf\Constraint\AbstractConstraint; -use Magento\Sales\Test\Page\Adminhtml\OrderIndex; -use Magento\Sales\Test\Page\Adminhtml\SalesOrderView; - -/** - * Assert that comment about awaiting the Signifyd guarantee disposition - * exists in Comments History section on order page in Admin. - */ -class AssertAwaitingSignifydGuaranteeInCommentsHistory extends AbstractConstraint -{ - /** - * Expected history comment. - */ - private $historyComment = 'Awaiting the Signifyd guarantee disposition.'; - - /** - * Expected history status. - */ - private $historyCommentStatus = 'On Hold'; - - /** - * @param SalesOrderView $salesOrderView - * @param OrderIndex $salesOrder - * @param string $orderId - * @return void - */ - public function processAssert( - SalesOrderView $salesOrderView, - OrderIndex $salesOrder, - $orderId - ) { - $salesOrder->open(); - $salesOrder->getSalesOrderGrid()->searchAndOpen(['id' => $orderId]); - - /** @var \Magento\Sales\Test\Block\Adminhtml\Order\View\Tab\Info $infoTab */ - $infoTab = $salesOrderView->getOrderForm()->openTab('info')->getTab('info'); - $orderComments = $infoTab->getCommentsHistoryBlock()->getComments(); - - $key = array_search( - $this->historyComment, - array_column($orderComments, 'comment') - ); - - \PHPUnit\Framework\Assert::assertNotFalse( - $key, - 'There is no message about awaiting the Signifyd guarantee disposition' . - ' in Comments History section for the order #' . $orderId - ); - - \PHPUnit\Framework\Assert::assertEquals( - $this->historyCommentStatus, - $orderComments[$key]['status'], - 'Message about awaiting the Signifyd guarantee disposition' . - ' doesn\'t have status "'. $this->historyCommentStatus.'"' . - ' in Comments History section for the order #' . $orderId - ); - } - - /** - * @inheritdoc - */ - public function toString() - { - return "Message about awaiting the Signifyd guarantee disposition is available in Comments History section."; - } -} diff --git a/dev/tests/functional/tests/app/Magento/Signifyd/Test/Constraint/AssertCaseInfoOnAdmin.php b/dev/tests/functional/tests/app/Magento/Signifyd/Test/Constraint/AssertCaseInfoOnAdmin.php deleted file mode 100644 index 298b957131395..0000000000000 --- a/dev/tests/functional/tests/app/Magento/Signifyd/Test/Constraint/AssertCaseInfoOnAdmin.php +++ /dev/null @@ -1,85 +0,0 @@ -<?php -/** - * Copyright © Magento, Inc. All rights reserved. - * See COPYING.txt for license details. - */ -namespace Magento\Signifyd\Test\Constraint; - -use Magento\Sales\Test\Page\Adminhtml\OrderIndex; -use Magento\Sales\Test\Page\Adminhtml\SalesOrderView; -use Magento\Mtf\Constraint\AbstractConstraint; -use Magento\Signifyd\Test\Fixture\SignifydData; - -/** - * Assert that Order Case Entity is correct on order page in Admin. - */ -class AssertCaseInfoOnAdmin extends AbstractConstraint -{ - /** - * Customized order view page. - * - * @var SalesOrderView - */ - private $orderView; - - /** - * Signifyd data fixture. - * - * @var SignifydData - */ - private $signifydData; - - /** - * Order id. - * - * @var string - */ - private $orderId; - - /** - * Assert that Signifyd Case information is correct in Admin. - * - * @param SalesOrderView $orderView - * @param OrderIndex $salesOrder - * @param SignifydData $signifydData - * @param string $orderId - * @return void - */ - public function processAssert( - SalesOrderView $orderView, - OrderIndex $salesOrder, - SignifydData $signifydData, - $orderId - ) { - $salesOrder->open(); - $salesOrder->getSalesOrderGrid()->searchAndOpen(['id' => $orderId]); - - $this->orderView = $orderView; - $this->signifydData = $signifydData; - $this->orderId = $orderId; - - $this->checkCaseGuaranteeDisposition(); - } - - /** - * Checks case guarantee disposition is correct. - * - * @return void - */ - private function checkCaseGuaranteeDisposition() - { - \PHPUnit\Framework\Assert::assertEquals( - $this->signifydData->getGuaranteeDisposition(), - $this->orderView->getFraudProtectionBlock()->getCaseGuaranteeDisposition(), - 'Case Guarantee Disposition status is wrong for order #' . $this->orderId - ); - } - - /** - * @inheritdoc - */ - public function toString() - { - return 'Signifyd Case information is correct in Admin.'; - } -} diff --git a/dev/tests/functional/tests/app/Magento/Signifyd/Test/Constraint/AssertCaseInfoOnSignifydConsole.php b/dev/tests/functional/tests/app/Magento/Signifyd/Test/Constraint/AssertCaseInfoOnSignifydConsole.php deleted file mode 100644 index a8a1f735d004c..0000000000000 --- a/dev/tests/functional/tests/app/Magento/Signifyd/Test/Constraint/AssertCaseInfoOnSignifydConsole.php +++ /dev/null @@ -1,214 +0,0 @@ -<?php -/** - * Copyright © Magento, Inc. All rights reserved. - * See COPYING.txt for license details. - */ -namespace Magento\Signifyd\Test\Constraint; - -use Magento\Mtf\Constraint\AbstractConstraint; -use Magento\Signifyd\Test\Fixture\SignifydAddress; -use Magento\Signifyd\Test\Fixture\SignifydData; -use Magento\Signifyd\Test\Page\SignifydConsole\SignifydCases; - -/** - * Assert that order information is correct on Signifyd case info page. - */ -class AssertCaseInfoOnSignifydConsole extends AbstractConstraint -{ - /** - * Signifyd cases page. - * - * @var SignifydCases - */ - private $signifydCases; - - /** - * @param SignifydCases $signifydCases - * @param SignifydAddress $billingAddress - * @param SignifydData $signifydData - * @param array $prices - * @param string $orderId - * @param string $customerFullName - * @return void - */ - public function processAssert( - SignifydCases $signifydCases, - SignifydAddress $billingAddress, - SignifydData $signifydData, - array $prices, - $orderId, - $customerFullName - ) { - $this->signifydCases = $signifydCases; - - $this->checkDeviceData(); - $this->checkShippingPrice($signifydData->getShippingPrice()); - $this->checkGuaranteeDisposition($signifydData->getGuaranteeDisposition()); - $cvvResponse = $signifydData->getCvvResponse(); - if (isset($cvvResponse)) { - $this->checkCvvResponse($cvvResponse); - } - $this->checkAvsResponse($signifydData->getAvsResponse()); - $this->checkOrderId($orderId); - $this->checkOrderAmount($prices['grandTotal']); - $this->checkOrderAmountCurrency($prices['grandTotalCurrency']); - $this->checkCardHolder($customerFullName); - $this->checkBillingAddress($billingAddress); - } - - /** - * Checks device data are present. - * - * @return void - */ - private function checkDeviceData() - { - \PHPUnit\Framework\Assert::assertTrue( - $this->signifydCases->getCaseInfoBlock()->isAvailableDeviceData(), - 'Device data are not available on case page in Signifyd console.' - ); - } - - /** - * Checks shipping price is correct. - * - * @param string $shippingPrice - * @return void - */ - private function checkShippingPrice($shippingPrice) - { - \PHPUnit\Framework\Assert::assertContains( - $shippingPrice, - $this->signifydCases->getCaseInfoBlock()->getShippingPrice(), - 'Shipping price is incorrect on case page in Signifyd console.' - ); - } - - /** - * Checks guarantee disposition is correct. - * - * @param string $guaranteeDisposition - * @return void - */ - private function checkGuaranteeDisposition($guaranteeDisposition) - { - \PHPUnit\Framework\Assert::assertEquals( - $guaranteeDisposition, - $this->signifydCases->getCaseInfoBlock()->getGuaranteeDisposition(), - 'Guarantee disposition is incorrect on case page in Signifyd console.' - ); - } - - /** - * Checks CVV response is correct. - * - * @param string $cvvResponse - * @return void - */ - private function checkCvvResponse($cvvResponse) - { - \PHPUnit\Framework\Assert::assertEquals( - $cvvResponse, - $this->signifydCases->getCaseInfoBlock()->getCvvResponse(), - 'CVV response is incorrect on case page in Signifyd console.' - ); - } - - /** - * Checks AVS response is correct. - * - * @param string $avsResponse - * @return void - */ - private function checkAvsResponse($avsResponse) - { - \PHPUnit\Framework\Assert::assertEquals( - $avsResponse, - $this->signifydCases->getCaseInfoBlock()->getAvsResponse(), - 'AVS response is incorrect on case page in Signifyd console.' - ); - } - - /** - * Checks order id is correct. - * - * @param string $orderId - * @return void - */ - private function checkOrderId($orderId) - { - \PHPUnit\Framework\Assert::assertEquals( - $orderId, - $this->signifydCases->getCaseInfoBlock()->getOrderId(), - 'Order id is incorrect on case page in Signifyd console.' - ); - } - - /** - * Checks order amount is correct. - * - * @param string $amount - * @return void - */ - private function checkOrderAmount($amount) - { - \PHPUnit\Framework\Assert::assertEquals( - number_format($amount, 2), - $this->signifydCases->getCaseInfoBlock()->getOrderAmount(), - 'Order amount is incorrect on case page in Signifyd console.' - ); - } - - /** - * Checks order amount currency is correct. - * - * @param string $currency - * @return void - */ - private function checkOrderAmountCurrency($currency) - { - \PHPUnit\Framework\Assert::assertEquals( - $currency, - $this->signifydCases->getCaseInfoBlock()->getOrderAmountCurrency(), - 'Order amount currency is incorrect on case page in Signifyd console.' - ); - } - - /** - * Checks card holder is correct. - * - * @param string $customerFullName - * @return void - */ - private function checkCardHolder($customerFullName) - { - \PHPUnit\Framework\Assert::assertEquals( - $customerFullName, - $this->signifydCases->getCaseInfoBlock()->getCardHolder(), - 'Card holder name is incorrect on case page in Signifyd console.' - ); - } - - /** - * Checks billing address is correct. - * - * @param SignifydAddress $billingAddress - * @return void - */ - private function checkBillingAddress(SignifydAddress $billingAddress) - { - \PHPUnit\Framework\Assert::assertContains( - $billingAddress->getStreet(), - $this->signifydCases->getCaseInfoBlock()->getBillingAddress(), - 'Billing address is incorrect on case page in Signifyd console.' - ); - } - - /** - * @inheritdoc - */ - public function toString() - { - return 'Case information is correct on case page in Signifyd console.'; - } -} diff --git a/dev/tests/functional/tests/app/Magento/Signifyd/Test/Constraint/AssertSignifydCaseInCommentsHistory.php b/dev/tests/functional/tests/app/Magento/Signifyd/Test/Constraint/AssertSignifydCaseInCommentsHistory.php deleted file mode 100644 index ea919f9410d4d..0000000000000 --- a/dev/tests/functional/tests/app/Magento/Signifyd/Test/Constraint/AssertSignifydCaseInCommentsHistory.php +++ /dev/null @@ -1,56 +0,0 @@ -<?php -/** - * Copyright © Magento, Inc. All rights reserved. - * See COPYING.txt for license details. - */ -namespace Magento\Signifyd\Test\Constraint; - -use Magento\Sales\Test\Page\Adminhtml\OrderIndex; -use Magento\Sales\Test\Page\Adminhtml\SalesOrderView; -use Magento\Mtf\Constraint\AbstractConstraint; - -/** - * Assert that comment about created Signifyd Case - * exists in Comments History section on order page in Admin. - */ -class AssertSignifydCaseInCommentsHistory extends AbstractConstraint -{ - /** - * Pattern of message about created Signifyd Case in order. - */ - const CASE_CREATED_PATTERN = '/Signifyd Case (\d)+ has been created for order\./'; - - /** - * @param SalesOrderView $salesOrderView - * @param OrderIndex $salesOrder - * @param string $orderId - * @return void - */ - public function processAssert( - SalesOrderView $salesOrderView, - OrderIndex $salesOrder, - $orderId - ) { - $salesOrder->open(); - $salesOrder->getSalesOrderGrid()->searchAndOpen(['id' => $orderId]); - - /** @var \Magento\Sales\Test\Block\Adminhtml\Order\View\Tab\Info $infoTab */ - $infoTab = $salesOrderView->getOrderForm()->openTab('info')->getTab('info'); - $orderComments = $infoTab->getCommentsHistoryBlock()->getComments(); - $commentsMessages = array_column($orderComments, 'comment'); - - \PHPUnit\Framework\Assert::assertRegExp( - self::CASE_CREATED_PATTERN, - implode('. ', $commentsMessages), - 'Signifyd case is not created for the order #' . $orderId - ); - } - - /** - * @inheritdoc - */ - public function toString() - { - return "Message about Signifyd Case is available in Comments History section."; - } -} diff --git a/dev/tests/functional/tests/app/Magento/Signifyd/Test/Constraint/AssertSignifydCaseInOrdersGrid.php b/dev/tests/functional/tests/app/Magento/Signifyd/Test/Constraint/AssertSignifydCaseInOrdersGrid.php deleted file mode 100644 index bdf3598d4f46a..0000000000000 --- a/dev/tests/functional/tests/app/Magento/Signifyd/Test/Constraint/AssertSignifydCaseInOrdersGrid.php +++ /dev/null @@ -1,50 +0,0 @@ -<?php -/** - * Copyright © Magento, Inc. All rights reserved. - * See COPYING.txt for license details. - */ -namespace Magento\Signifyd\Test\Constraint; - -use Magento\Signifyd\Test\Fixture\SignifydData; -use Magento\Signifyd\Test\Page\Adminhtml\OrdersGrid; -use Magento\Mtf\Constraint\AbstractConstraint; - -/** - * Assert that Signifyd Guarantee Status is present in Orders grid. - */ -class AssertSignifydCaseInOrdersGrid extends AbstractConstraint -{ - /** - * @param string $orderId - * @param OrdersGrid $ordersGrid - * @param SignifydData $signifydData - * @return void - */ - public function processAssert( - $orderId, - OrdersGrid $ordersGrid, - SignifydData $signifydData - ) { - $filter = [ - 'id' => $orderId, - 'signifyd_guarantee_status' => $signifydData->getGuaranteeDisposition() - ]; - - $errorMessage = implode(', ', $filter); - - $ordersGrid->open(); - - \PHPUnit\Framework\Assert::assertTrue( - $ordersGrid->getSignifydOrdersGrid()->isRowVisible(array_filter($filter)), - 'Order with following data \'' . $errorMessage . '\' is absent in orders grid.' - ); - } - - /** - * @inheritdoc - */ - public function toString() - { - return 'Signifyd guarantee status is displayed in sales orders grid.'; - } -} diff --git a/dev/tests/functional/tests/app/Magento/Signifyd/Test/Constraint/AssertSignifydGuaranteeCancelInCommentsHistory.php b/dev/tests/functional/tests/app/Magento/Signifyd/Test/Constraint/AssertSignifydGuaranteeCancelInCommentsHistory.php deleted file mode 100644 index 618d77885736c..0000000000000 --- a/dev/tests/functional/tests/app/Magento/Signifyd/Test/Constraint/AssertSignifydGuaranteeCancelInCommentsHistory.php +++ /dev/null @@ -1,57 +0,0 @@ -<?php -/** - * Copyright © Magento, Inc. All rights reserved. - * See COPYING.txt for license details. - */ -namespace Magento\Signifyd\Test\Constraint; - -use Magento\Mtf\Constraint\AbstractConstraint; -use Magento\Sales\Test\Page\Adminhtml\OrderIndex; -use Magento\Sales\Test\Page\Adminhtml\SalesOrderView; - -/** - * Assert that comment about created Signifyd Case guarantee - * has been cancelled exists in Comments History section on order page in Admin. - */ -class AssertSignifydGuaranteeCancelInCommentsHistory extends AbstractConstraint -{ - /** - * Signifyd case guarantee cancel message in order view. - */ - private $guaranteeCancelMessage = 'Case Update: Case guarantee has been cancelled.'; - - /** - * @param SalesOrderView $salesOrderView - * @param OrderIndex $salesOrder - * @param string $orderId - * @return void - */ - public function processAssert( - SalesOrderView $salesOrderView, - OrderIndex $salesOrder, - $orderId - ) { - $salesOrder->open(); - $salesOrder->getSalesOrderGrid()->searchAndOpen(['id' => $orderId]); - - /** @var \Magento\Sales\Test\Block\Adminhtml\Order\View\Tab\Info $infoTab */ - $infoTab = $salesOrderView->getOrderForm()->openTab('info')->getTab('info'); - $orderComments = $infoTab->getCommentsHistoryBlock()->getComments(); - $commentsMessages = array_column($orderComments, 'comment'); - - \PHPUnit\Framework\Assert::assertContains( - $this->guaranteeCancelMessage, - implode('. ', $commentsMessages), - 'There is no message regarding Signifyd guarantee cancel in Comments History section for the order #' - . $orderId - ); - } - - /** - * @inheritdoc - */ - public function toString() - { - return "Message about Signifyd guarantee cancel is available in Comments History section."; - } -} diff --git a/dev/tests/functional/tests/app/Magento/Signifyd/Test/Fixture/SignifydAccount.xml b/dev/tests/functional/tests/app/Magento/Signifyd/Test/Fixture/SignifydAccount.xml deleted file mode 100644 index 8b1455811d003..0000000000000 --- a/dev/tests/functional/tests/app/Magento/Signifyd/Test/Fixture/SignifydAccount.xml +++ /dev/null @@ -1,17 +0,0 @@ -<?xml version="1.0" encoding="utf-8"?> -<!-- -/** - * Copyright © Magento, Inc. All rights reserved. - * See COPYING.txt for license details. - */ - --> -<config xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="../../../../../../vendor/magento/mtf/etc/fixture.xsd"> - <fixture name="signifyd_account" - module="Magento_Signifyd" - type="virtual" - repository_class="Magento\Signifyd\Test\Repository\SignifydAccount" - class="Magento\Signifyd\Test\Fixture\SignifydAccount"> - <field name="email" /> - <field name="password" /> - </fixture> -</config> diff --git a/dev/tests/functional/tests/app/Magento/Signifyd/Test/Fixture/SignifydAddress.xml b/dev/tests/functional/tests/app/Magento/Signifyd/Test/Fixture/SignifydAddress.xml deleted file mode 100644 index 02b5cc8211d89..0000000000000 --- a/dev/tests/functional/tests/app/Magento/Signifyd/Test/Fixture/SignifydAddress.xml +++ /dev/null @@ -1,15 +0,0 @@ -<?xml version="1.0" encoding="utf-8"?> -<!-- -/** - * Copyright © Magento, Inc. All rights reserved. - * See COPYING.txt for license details. - */ - --> -<config xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="../../../../../../vendor/magento/mtf/etc/fixture.xsd"> - <fixture name="signifydAddress" - module="Magento_Signifyd" - class="Magento\Signifyd\Test\Fixture\SignifydAddress" - extends="\Magento\Customer\Test\Fixture\Address"> - <field name="firstname" source="Magento\Signifyd\Test\Fixture\SignifydAddress\Firstname" is_required="1" /> - </fixture> -</config> diff --git a/dev/tests/functional/tests/app/Magento/Signifyd/Test/Fixture/SignifydAddress/Firstname.php b/dev/tests/functional/tests/app/Magento/Signifyd/Test/Fixture/SignifydAddress/Firstname.php deleted file mode 100644 index 9e4930484eef0..0000000000000 --- a/dev/tests/functional/tests/app/Magento/Signifyd/Test/Fixture/SignifydAddress/Firstname.php +++ /dev/null @@ -1,46 +0,0 @@ -<?php -/** - * Copyright © Magento, Inc. All rights reserved. - * See COPYING.txt for license details. - */ -namespace Magento\Signifyd\Test\Fixture\SignifydAddress; - -use Magento\Mtf\Fixture\DataSource; - -/** - * Source handler for `firstname` field in shipping address fixture. - */ -class Firstname extends DataSource -{ - /** - * @param string $data - */ - public function __construct($data = '') - { - $this->data = $data; - } - - /** - * Add isolation for `firstname` field. - * - * @param null $key - * @return string - */ - public function getData($key = null) - { - $this->data = str_replace('%signifyd_isolation%', $this->generateIsolation(), $this->data); - - return parent::getData($key); - } - - /** - * Generates character isolation. - * - * @param int $length - * @return string - */ - private function generateIsolation($length = 10) - { - return substr(str_shuffle(str_repeat("abcdefghijklmnopqrstuvwxyz", $length)), 0, $length); - } -} diff --git a/dev/tests/functional/tests/app/Magento/Signifyd/Test/Fixture/SignifydData.xml b/dev/tests/functional/tests/app/Magento/Signifyd/Test/Fixture/SignifydData.xml deleted file mode 100644 index 23ee2cddf434a..0000000000000 --- a/dev/tests/functional/tests/app/Magento/Signifyd/Test/Fixture/SignifydData.xml +++ /dev/null @@ -1,21 +0,0 @@ -<?xml version="1.0" encoding="utf-8"?> -<!-- -/** - * Copyright © Magento, Inc. All rights reserved. - * See COPYING.txt for license details. - */ - --> -<config xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="../../../../../../vendor/magento/mtf/etc/fixture.xsd"> - <fixture name="signifyd_data" - module="Magento_Signifyd" - type="virtual" - repository_class="Magento\Signifyd\Test\Repository\SignifydData" - class="Magento\Signifyd\Test\Fixture\SignifydData"> - <field name="team" /> - <field name="caseFlag" /> - <field name="guaranteeDisposition" /> - <field name="cvvResponse" /> - <field name="avsResponse" /> - <field name="shippingPrice" /> - </fixture> -</config> diff --git a/dev/tests/functional/tests/app/Magento/Signifyd/Test/Page/Adminhtml/OrdersGrid.xml b/dev/tests/functional/tests/app/Magento/Signifyd/Test/Page/Adminhtml/OrdersGrid.xml deleted file mode 100644 index 749d91c3ed395..0000000000000 --- a/dev/tests/functional/tests/app/Magento/Signifyd/Test/Page/Adminhtml/OrdersGrid.xml +++ /dev/null @@ -1,12 +0,0 @@ -<?xml version="1.0" encoding="utf-8"?> -<!-- -/** - * Copyright © Magento, Inc. All rights reserved. - * See COPYING.txt for license details. - */ - --> -<config xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="../../../../../../../vendor/magento/mtf/etc/pages.xsd"> - <page name="ordersGrid" area="Adminhtml" mca="sales/order" module="Magento_Signifyd"> - <block name="signifydOrdersGrid" class="Magento\Signifyd\Test\Block\Adminhtml\Order\Grid" locator="//div[contains(@data-bind, 'sales_order_grid')]" strategy="xpath" /> - </page> -</config> diff --git a/dev/tests/functional/tests/app/Magento/Signifyd/Test/Page/Adminhtml/SalesOrderView.xml b/dev/tests/functional/tests/app/Magento/Signifyd/Test/Page/Adminhtml/SalesOrderView.xml deleted file mode 100644 index 36d77723dfcee..0000000000000 --- a/dev/tests/functional/tests/app/Magento/Signifyd/Test/Page/Adminhtml/SalesOrderView.xml +++ /dev/null @@ -1,12 +0,0 @@ -<?xml version="1.0" encoding="utf-8"?> -<!-- -/** - * Copyright © Magento, Inc. All rights reserved. - * See COPYING.txt for license details. - */ - --> -<config xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="../../../../../../../vendor/magento/mtf/etc/pages.xsd"> - <page name="SalesOrderView" area="Adminhtml" mca="sales/order/view"> - <block name="fraudProtectionBlock" class="Magento\Signifyd\Test\Block\Adminhtml\Order\View\FraudProtection" locator=".admin__page-section.order-case-info" strategy="css selector" /> - </page> -</config> diff --git a/dev/tests/functional/tests/app/Magento/Signifyd/Test/Page/SignifydConsole/SignifydCases.xml b/dev/tests/functional/tests/app/Magento/Signifyd/Test/Page/SignifydConsole/SignifydCases.xml deleted file mode 100644 index cfe889bb8de44..0000000000000 --- a/dev/tests/functional/tests/app/Magento/Signifyd/Test/Page/SignifydConsole/SignifydCases.xml +++ /dev/null @@ -1,13 +0,0 @@ -<?xml version="1.0" encoding="utf-8"?> -<!-- -/** - * Copyright © Magento, Inc. All rights reserved. - * See COPYING.txt for license details. - */ - --> -<config xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="../../../../../../../vendor/magento/mtf/etc/pages.xsd"> - <page name="SignifydCases" area="SignifydConsole" mca="https://app.signifyd.com/cases" module="Magento_Signifyd"> - <block name="caseSearchBlock" class="Magento\Signifyd\Test\Block\SignifydConsole\CaseSearch" locator="[class$=app-sidebar]" strategy="css selector" /> - <block name="caseInfoBlock" class="Magento\Signifyd\Test\Block\SignifydConsole\CaseInfo" locator="[class*=app-wrapper]" strategy="css selector" /> - </page> -</config> diff --git a/dev/tests/functional/tests/app/Magento/Signifyd/Test/Page/SignifydConsole/SignifydLogin.xml b/dev/tests/functional/tests/app/Magento/Signifyd/Test/Page/SignifydConsole/SignifydLogin.xml deleted file mode 100644 index 5fe5da6d28013..0000000000000 --- a/dev/tests/functional/tests/app/Magento/Signifyd/Test/Page/SignifydConsole/SignifydLogin.xml +++ /dev/null @@ -1,12 +0,0 @@ -<?xml version="1.0" encoding="utf-8"?> -<!-- -/** - * Copyright © Magento, Inc. All rights reserved. - * See COPYING.txt for license details. - */ - --> -<config xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="../../../../../../../vendor/magento/mtf/etc/pages.xsd"> - <page name="SignifydLogin" area="SignifydConsole" mca="https://app.signifyd.com/login" module="Magento_Signifyd"> - <block name="loginBlock" class="Magento\Signifyd\Test\Block\SignifydConsole\SignifydLogin" locator="[id=loginForm]" strategy="css selector" /> - </page> -</config> diff --git a/dev/tests/functional/tests/app/Magento/Signifyd/Test/Page/SignifydConsole/SignifydNotifications.xml b/dev/tests/functional/tests/app/Magento/Signifyd/Test/Page/SignifydConsole/SignifydNotifications.xml deleted file mode 100644 index dd12f14c28800..0000000000000 --- a/dev/tests/functional/tests/app/Magento/Signifyd/Test/Page/SignifydConsole/SignifydNotifications.xml +++ /dev/null @@ -1,12 +0,0 @@ -<?xml version="1.0" encoding="utf-8"?> -<!-- -/** - * Copyright © Magento, Inc. All rights reserved. - * See COPYING.txt for license details. - */ - --> -<config xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="../../../../../../../vendor/magento/mtf/etc/pages.xsd"> - <page name="SignifydNotifications" area="SignifydConsole" mca="https://app.signifyd.com/settings/notifications" module="Magento_Signifyd"> - <block name="webhooksBlock" class="Magento\Signifyd\Test\Block\SignifydConsole\Webhooks" locator="[id=body]" strategy="css selector" /> - </page> -</config> diff --git a/dev/tests/functional/tests/app/Magento/Signifyd/Test/Repository/Address.xml b/dev/tests/functional/tests/app/Magento/Signifyd/Test/Repository/Address.xml deleted file mode 100644 index a534cbc6107fe..0000000000000 --- a/dev/tests/functional/tests/app/Magento/Signifyd/Test/Repository/Address.xml +++ /dev/null @@ -1,22 +0,0 @@ -<?xml version="1.0" ?> -<!-- -/** - * Copyright © Magento, Inc. All rights reserved. - * See COPYING.txt for license details. - */ ---> -<config xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="../../../../../../vendor/magento/mtf/Magento/Mtf/Repository/etc/repository.xsd"> - <repository class="Magento\Customer\Test\Repository\Address"> - <dataset name="signifyd_us_shipping_address"> - <field name="firstname" xsi:type="string">John%signifyd_isolation%</field> - <field name="lastname" xsi:type="string">Doe</field> - <field name="company" xsi:type="string">Magento</field> - <field name="city" xsi:type="string">Culver City</field> - <field name="street" xsi:type="string">6161 West Centinela Avenue</field> - <field name="telephone" xsi:type="string">555-55-555-55</field> - <field name="country_id" xsi:type="string">United States</field> - <field name="region_id" xsi:type="string">California</field> - <field name="postcode" xsi:type="string">90230</field> - </dataset> - </repository> -</config> diff --git a/dev/tests/functional/tests/app/Magento/Signifyd/Test/Repository/ConfigData.xml b/dev/tests/functional/tests/app/Magento/Signifyd/Test/Repository/ConfigData.xml deleted file mode 100644 index 2af46f077b304..0000000000000 --- a/dev/tests/functional/tests/app/Magento/Signifyd/Test/Repository/ConfigData.xml +++ /dev/null @@ -1,45 +0,0 @@ -<?xml version="1.0"?> -<!-- -/** - * Copyright © Magento, Inc. All rights reserved. - * See COPYING.txt for license details. - */ ---> -<config xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="../../../../../../vendor/magento/mtf/Magento/Mtf/Repository/etc/repository.xsd"> - <repository class="Magento\Config\Test\Repository\ConfigData"> - <dataset name="signifyd"> - <field name="fraud_protection/signifyd/active" xsi:type="array"> - <item name="scope" xsi:type="string">fraud_protection</item> - <item name="scope_id" xsi:type="number">1</item> - <item name="label" xsi:type="string">Yes</item> - <item name="value" xsi:type="number">1</item> - </field> - <field name="fraud_protection/signifyd/config/api_key" xsi:type="array"> - <item name="scope" xsi:type="string">fraud_protection</item> - <item name="scope_id" xsi:type="number">1</item> - <item name="label" xsi:type="string"/> - <item name="value" xsi:type="string">SIGNIFYD_CONFIG_API_KEY</item> - </field> - <field name="fraud_protection/signifyd/api_url" xsi:type="array"> - <item name="scope" xsi:type="string">fraud_protection</item> - <item name="scope_id" xsi:type="number">1</item> - <item name="label" xsi:type="string"/> - <item name="value" xsi:type="string">https://api.signifyd.com/v2/</item> - </field> - <field name="fraud_protection/signifyd/debug" xsi:type="array"> - <item name="scope" xsi:type="string">fraud_protection</item> - <item name="scope_id" xsi:type="number">1</item> - <item name="label" xsi:type="string">Yes</item> - <item name="value" xsi:type="number">1</item> - </field> - </dataset> - <dataset name="signifyd_rollback"> - <field name="fraud_protection/signifyd/active" xsi:type="array"> - <item name="scope" xsi:type="string">fraud_protection</item> - <item name="scope_id" xsi:type="number">1</item> - <item name="label" xsi:type="string">Yes</item> - <item name="value" xsi:type="number">0</item> - </field> - </dataset> - </repository> -</config> diff --git a/dev/tests/functional/tests/app/Magento/Signifyd/Test/Repository/Customer.xml b/dev/tests/functional/tests/app/Magento/Signifyd/Test/Repository/Customer.xml deleted file mode 100644 index b45e3d01b8ec8..0000000000000 --- a/dev/tests/functional/tests/app/Magento/Signifyd/Test/Repository/Customer.xml +++ /dev/null @@ -1,25 +0,0 @@ -<?xml version="1.0" ?> -<!-- -/** - * Copyright © Magento, Inc. All rights reserved. - * See COPYING.txt for license details. - */ ---> -<config xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="../../../../../../vendor/magento/mtf/Magento/Mtf/Repository/etc/repository.xsd"> - <repository class="Magento\Customer\Test\Repository\Customer"> - <dataset name="signifyd_approve_us_customer"> - <field name="firstname" xsi:type="string">John</field> - <field name="lastname" xsi:type="string">Doe</field> - <field name="email" xsi:type="string">testapprove@magento.com</field> - <field name="password" xsi:type="string">123123^q</field> - <field name="password_confirmation" xsi:type="string">123123^q</field> - </dataset> - <dataset name="signifyd_decline_us_customer"> - <field name="firstname" xsi:type="string">John</field> - <field name="lastname" xsi:type="string">Doe</field> - <field name="email" xsi:type="string">testdecline@magento.com</field> - <field name="password" xsi:type="string">123123^q</field> - <field name="password_confirmation" xsi:type="string">123123^q</field> - </dataset> - </repository> -</config> diff --git a/dev/tests/functional/tests/app/Magento/Signifyd/Test/Repository/SignifydAccount.xml b/dev/tests/functional/tests/app/Magento/Signifyd/Test/Repository/SignifydAccount.xml deleted file mode 100644 index 4d3dd5229eae3..0000000000000 --- a/dev/tests/functional/tests/app/Magento/Signifyd/Test/Repository/SignifydAccount.xml +++ /dev/null @@ -1,15 +0,0 @@ -<?xml version="1.0" ?> -<!-- -/** - * Copyright © Magento, Inc. All rights reserved. - * See COPYING.txt for license details. - */ ---> -<config xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="../../../../../../vendor/magento/mtf/Magento/Mtf/Repository/etc/repository.xsd"> - <repository class="Magento\Signifyd\Test\Repository\SignifydAccount"> - <dataset name="sandbox_default"> - <field name="email" xsi:type="string">SIGNIFYD_EMAIL</field> - <field name="password" xsi:type="string">SIGNIFYD_PASSWORD</field> - </dataset> - </repository> -</config> diff --git a/dev/tests/functional/tests/app/Magento/Signifyd/Test/Repository/SignifydData.xml b/dev/tests/functional/tests/app/Magento/Signifyd/Test/Repository/SignifydData.xml deleted file mode 100644 index 02037534b0149..0000000000000 --- a/dev/tests/functional/tests/app/Magento/Signifyd/Test/Repository/SignifydData.xml +++ /dev/null @@ -1,33 +0,0 @@ -<?xml version="1.0" ?> -<!-- -/** - * Copyright © Magento, Inc. All rights reserved. - * See COPYING.txt for license details. - */ ---> -<config xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="../../../../../../vendor/magento/mtf/Magento/Mtf/Repository/etc/repository.xsd"> - <repository class="Magento\Signifyd\Test\Repository\SignifydData"> - <dataset name="signifyd_guarantee_approve"> - <field name="team" xsi:type="string">autotest</field> - <field name="caseFlag" xsi:type="string">Good</field> - <field name="guaranteeDisposition" xsi:type="string">Approved</field> - <field name="cvvResponse" xsi:type="string">CVV2 Match (M)</field> - <field name="avsResponse" xsi:type="string">Full match (Y)</field> - <field name="shippingPrice" xsi:type="string">USD 5.00</field> - </dataset> - <dataset name="signifyd_guarantee_decline"> - <field name="team" xsi:type="string">autotest</field> - <field name="caseFlag" xsi:type="string">Bad</field> - <field name="guaranteeDisposition" xsi:type="string">Declined</field> - <field name="cvvResponse" xsi:type="string">CVV2 Match (M)</field> - <field name="avsResponse" xsi:type="string">Full match (Y)</field> - <field name="shippingPrice" xsi:type="string">USD 5.00</field> - </dataset> - <dataset name="signifyd_guarantee_fraudulent"> - <field name="team" xsi:type="string">autotest</field> - <field name="caseFlag" xsi:type="string">Bad</field> - <field name="guaranteeDisposition" xsi:type="string">Declined</field> - <field name="shippingPrice" xsi:type="string">GBP 10.00</field> - </dataset> - </repository> -</config> diff --git a/dev/tests/functional/tests/app/Magento/Signifyd/Test/TestCase/AcceptPaymentWithSignifydGuaranteeDeclinedTest.php b/dev/tests/functional/tests/app/Magento/Signifyd/Test/TestCase/AcceptPaymentWithSignifydGuaranteeDeclinedTest.php deleted file mode 100644 index 1dd742c9f7096..0000000000000 --- a/dev/tests/functional/tests/app/Magento/Signifyd/Test/TestCase/AcceptPaymentWithSignifydGuaranteeDeclinedTest.php +++ /dev/null @@ -1,63 +0,0 @@ -<?php -/** - * Copyright © Magento, Inc. All rights reserved. - * See COPYING.txt for license details. - */ -namespace Magento\Signifyd\Test\TestCase; - -use Magento\Mtf\TestCase\Scenario; - -/** - * * Preconditions: - * 1. Configure shipping method. - * 2. Configure payment method. - * 3. Configure Signifyd fraud protection tool - * 4. Create products. - * 5. Create and setup customer. - * - * Steps: - * 1. Log in to Signifyd account. - * 2. Remove all existing webhooks by test team. - * 3. Add new webhook set. - * 4. Log in Storefront. - * 5. Add products to the Shopping Cart. - * 6. Click the 'Proceed to Checkout' button. - * 7. Fill shipping information. - * 8. Select shipping method. - * 9. Select Hosted Pro method. - * 10. Click 'Continue' button. - * 11. Specify credit card data in Paypal iframe. - * 12. Click 'Pay Now' button. - * 13. Log in to Signifyd account and search for created case. - * 14. Open created case. - * 15. Click "Flag case as bad" button. - * 16. Perform case info assertions. - * 17. Log in to Admin. - * 18. Proceed to order grid. - * 19. Perform Signifyd guarantee status assertions. - * 20. Proceed to order view. - * 21. Perform order status and case info assertions. - * 22. Click Accept Payment button. - * 23. Perform remaining assertions. - * - * @group Signifyd - * @ZephyrId MAGETWO-65333 - */ -class AcceptPaymentWithSignifydGuaranteeDeclinedTest extends Scenario -{ - /* tags */ - const MVP = 'yes'; - const TEST_TYPE = '3rd_party_test_single_flow'; - const SEVERITY = 'S2'; - /* end tags */ - - /** - * Runs one page checkout test. - * - * @return void - */ - public function test() - { - $this->executeScenario(); - } -} diff --git a/dev/tests/functional/tests/app/Magento/Signifyd/Test/TestCase/AcceptPaymentWithSignifydGuaranteeDeclinedTest.xml b/dev/tests/functional/tests/app/Magento/Signifyd/Test/TestCase/AcceptPaymentWithSignifydGuaranteeDeclinedTest.xml deleted file mode 100644 index 6391081e5e161..0000000000000 --- a/dev/tests/functional/tests/app/Magento/Signifyd/Test/TestCase/AcceptPaymentWithSignifydGuaranteeDeclinedTest.xml +++ /dev/null @@ -1,38 +0,0 @@ -<?xml version="1.0" encoding="utf-8"?> -<!-- -/** - * Copyright © Magento, Inc. All rights reserved. - * See COPYING.txt for license details. - */ - --> -<config xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="../../../../../../vendor/magento/mtf/etc/variations.xsd"> - <testCase name="Magento\Signifyd\Test\TestCase\AcceptPaymentWithSignifydGuaranteeDeclinedTest" - summary="One Page Checkout with Signifyd, PayPal Payments Pro Hosted Solution with Fraud filters triggered."> - <variation name="AcceptPaymentWithSignifydGuaranteeDeclinedWithHostedProVariation1" summary="Accept order placed within PayPal Payments Pro Hosted Solution with Fraud filters triggered and Signifyd Guarantee Declined" ticketId="MAGETWO-65333"> - <data name="products/0" xsi:type="string">catalogProductSimple::product_100_dollar</data> - <data name="customer/dataset" xsi:type="string">signifyd_decline_us_customer</data> - <data name="checkoutMethod" xsi:type="string">login</data> - <data name="shipping/shipping_service" xsi:type="string">Flat Rate</data> - <data name="shipping/shipping_method" xsi:type="string">Fixed</data> - <data name="payment/method" xsi:type="string">hosted_pro</data> - <data name="prices" xsi:type="array"> - <item name="grandTotal" xsi:type="string">210.00</item> - <item name="grandTotalCurrency" xsi:type="string">GBP</item> - </data> - <data name="creditCardClass" xsi:type="string">credit_card_hostedpro</data> - <data name="creditCard/dataset" xsi:type="string">visa_hosted_pro</data> - <data name="isVaultPresent" xsi:type="boolean">false</data> - <data name="configData" xsi:type="string">merchant_country_gb, hosted_pro, config_base_currency_gb, signifyd</data> - <data name="placeOrderStatus" xsi:type="string">Suspected Fraud</data> - <data name="signifydAddress/dataset" xsi:type="string">signifyd_us_shipping_address</data> - <data name="signifydAccount/dataset" xsi:type="string">sandbox_default</data> - <data name="signifydData/dataset" xsi:type="string">signifyd_guarantee_fraudulent</data> - <data name="status" xsi:type="string">Processing</data> - <data name="tag" xsi:type="string">test_type:3rd_party_test_single_flow, severity:S2</data> - <constraint name="Magento\Sales\Test\Constraint\AssertOrderGrandTotal" /> - <constraint name="Magento\Signifyd\Test\Constraint\AssertSignifydCaseInCommentsHistory" /> - <constraint name="Magento\Sales\Test\Constraint\AssertAcceptPaymentMessageInCommentsHistory" /> - <constraint name="Magento\Sales\Test\Constraint\AssertOrderStatusIsCorrect" /> - </variation> - </testCase> -</config> diff --git a/dev/tests/functional/tests/app/Magento/Signifyd/Test/TestCase/CreateSignifydGuaranteeAndCancelOrderTest.php b/dev/tests/functional/tests/app/Magento/Signifyd/Test/TestCase/CreateSignifydGuaranteeAndCancelOrderTest.php deleted file mode 100644 index 5ca76ff70479f..0000000000000 --- a/dev/tests/functional/tests/app/Magento/Signifyd/Test/TestCase/CreateSignifydGuaranteeAndCancelOrderTest.php +++ /dev/null @@ -1,62 +0,0 @@ -<?php -/** - * Copyright © Magento, Inc. All rights reserved. - * See COPYING.txt for license details. - */ -namespace Magento\Signifyd\Test\TestCase; - -use Magento\Mtf\TestCase\Scenario; - -/** - * Preconditions: - * 1. Configure shipping method. - * 2. Configure payment method. - * 3. Configure Signifyd fraud protection tool - * 4. Create products. - * 5. Create and setup customer. - * - * Steps: - * 1. Log in to Signifyd account. - * 2. Remove all existing webhooks by test team. - * 3. Add new webhook set. - * 4. Log in Storefront. - * 5. Add products to the Shopping Cart. - * 6. Click the 'Proceed to Checkout' button. - * 7. Fill shipping information. - * 8. Select shipping method. - * 9. Select payment method. - * 10. Specify credit card data. - * 11. Click 'Place order' button. - * 12. Search for created case. - * 13. Open created case. - * 14. Click "Flag case as good" or "Flag case as bad" button. - * 15. Perform case info assertions. - * 16. Log in to Admin. - * 17. Proceed to order grid. - * 18. Perform Signifyd guarantee status assertions. - * 19. Proceed to order view. - * 20. Perform order status and case info assertions. - * 21. Click Cancel button. - * 22. Perform remaining assertions. - * - * @group Signifyd - * @ZephyrId MAGETWO-62120, MAGETWO-63221, MAGETWO-64305, MAGETWO-65253 - */ -class CreateSignifydGuaranteeAndCancelOrderTest extends Scenario -{ - /* tags */ - const MVP = 'yes'; - const TEST_TYPE = '3rd_party_test_single_flow'; - const SEVERITY = 'S1'; - /* end tags */ - - /** - * Runs one page checkout test. - * - * @return void - */ - public function test() - { - $this->executeScenario(); - } -} diff --git a/dev/tests/functional/tests/app/Magento/Signifyd/Test/TestCase/CreateSignifydGuaranteeAndCancelOrderTest.xml b/dev/tests/functional/tests/app/Magento/Signifyd/Test/TestCase/CreateSignifydGuaranteeAndCancelOrderTest.xml deleted file mode 100644 index 082627fe5821d..0000000000000 --- a/dev/tests/functional/tests/app/Magento/Signifyd/Test/TestCase/CreateSignifydGuaranteeAndCancelOrderTest.xml +++ /dev/null @@ -1,67 +0,0 @@ -<?xml version="1.0" encoding="utf-8"?> -<!-- -/** - * Copyright © Magento, Inc. All rights reserved. - * See COPYING.txt for license details. - */ - --> -<config xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="../../../../../../vendor/magento/mtf/etc/variations.xsd"> - <testCase name="Magento\Signifyd\Test\TestCase\CreateSignifydGuaranteeAndCancelOrderTest" - summary="One Page Checkout with Signifyd and cancel order."> - <variation name="CreateSignifydGuaranteeAndCancelOrderWithBraintreeVariation1" - summary="Cancel order placed within Braintree credit card with Signifyd approved guarantee." - ticketId="MAGETWO-62120,MAGETWO-63221"> - <data name="products/0" xsi:type="string">catalogProductSimple::product_10_dollar</data> - <data name="checkoutMethod" xsi:type="string">login</data> - <data name="customer/dataset" xsi:type="string">signifyd_approve_us_customer</data> - <data name="shipping/shipping_service" xsi:type="string">Flat Rate</data> - <data name="shipping/shipping_method" xsi:type="string">Fixed</data> - <data name="payment/method" xsi:type="string">braintree</data> - <data name="paymentForm" xsi:type="string">braintree</data> - <data name="prices/grandTotal" xsi:type="string">15.00</data> - <data name="prices/grandTotalCurrency" xsi:type="string">USD</data> - <data name="creditCard/dataset" xsi:type="string">visa_default</data> - <data name="creditCard/data/payment_code" xsi:type="string">braintree</data> - <data name="configData" xsi:type="string">braintree,signifyd</data> - <data name="placeOrderStatus" xsi:type="string">Processing</data> - <data name="signifydAddress/dataset" xsi:type="string">signifyd_us_shipping_address</data> - <data name="signifydAccount/dataset" xsi:type="string">sandbox_default</data> - <data name="signifydData/dataset" xsi:type="string">signifyd_guarantee_approve</data> - <data name="tag" xsi:type="string">test_type:3rd_party_test_single_flow, severity:S1</data> - <constraint name="Magento\Sales\Test\Constraint\AssertAuthorizationInCommentsHistory" /> - <constraint name="Magento\Sales\Test\Constraint\AssertOrderGrandTotal" /> - <constraint name="Magento\Sales\Test\Constraint\AssertOrderStatusIsCanceled" /> - <constraint name="Magento\Sales\Test\Constraint\AssertCancelInCommentsHistory" /> - <constraint name="Magento\Signifyd\Test\Constraint\AssertSignifydCaseInCommentsHistory" /> - <constraint name="Magento\Signifyd\Test\Constraint\AssertAwaitingSignifydGuaranteeInCommentsHistory" /> - <constraint name="Magento\Signifyd\Test\Constraint\AssertSignifydGuaranteeCancelInCommentsHistory" /> - </variation> - <variation name="CreateSignifydGuaranteeAndCancelOrderWithBraintreeVariation2" - summary="Cancel order placed within Braintree credit card with Signifyd declined guarantee." - ticketId="MAGETWO-64305, MAGETWO-65253"> - <data name="products/0" xsi:type="string">catalogProductSimple::product_10_dollar</data> - <data name="checkoutMethod" xsi:type="string">login</data> - <data name="customer/dataset" xsi:type="string">signifyd_decline_us_customer</data> - <data name="shipping/shipping_service" xsi:type="string">Flat Rate</data> - <data name="shipping/shipping_method" xsi:type="string">Fixed</data> - <data name="payment/method" xsi:type="string">braintree</data> - <data name="paymentForm" xsi:type="string">braintree</data> - <data name="prices/grandTotal" xsi:type="string">15.00</data> - <data name="prices/grandTotalCurrency" xsi:type="string">USD</data> - <data name="creditCard/dataset" xsi:type="string">visa_default</data> - <data name="creditCard/data/payment_code" xsi:type="string">braintree</data> - <data name="configData" xsi:type="string">braintree,signifyd</data> - <data name="placeOrderStatus" xsi:type="string">On Hold</data> - <data name="signifydAddress/dataset" xsi:type="string">signifyd_us_shipping_address</data> - <data name="signifydAccount/dataset" xsi:type="string">sandbox_default</data> - <data name="signifydData/dataset" xsi:type="string">signifyd_guarantee_decline</data> - <data name="tag" xsi:type="string">test_type:3rd_party_test_single_flow, severity:S1</data> - <constraint name="Magento\Sales\Test\Constraint\AssertAuthorizationInCommentsHistory" /> - <constraint name="Magento\Sales\Test\Constraint\AssertOrderGrandTotal" /> - <constraint name="Magento\Sales\Test\Constraint\AssertOrderStatusIsCanceled" /> - <constraint name="Magento\Sales\Test\Constraint\AssertCancelInCommentsHistory" /> - <constraint name="Magento\Signifyd\Test\Constraint\AssertSignifydCaseInCommentsHistory" /> - <constraint name="Magento\Signifyd\Test\Constraint\AssertAwaitingSignifydGuaranteeInCommentsHistory" /> - </variation> - </testCase> -</config> diff --git a/dev/tests/functional/tests/app/Magento/Signifyd/Test/TestCase/DenyPaymentWithSignifydGuaranteeDeclinedTest.php b/dev/tests/functional/tests/app/Magento/Signifyd/Test/TestCase/DenyPaymentWithSignifydGuaranteeDeclinedTest.php deleted file mode 100644 index 698861da26018..0000000000000 --- a/dev/tests/functional/tests/app/Magento/Signifyd/Test/TestCase/DenyPaymentWithSignifydGuaranteeDeclinedTest.php +++ /dev/null @@ -1,63 +0,0 @@ -<?php -/** - * Copyright © Magento, Inc. All rights reserved. - * See COPYING.txt for license details. - */ -namespace Magento\Signifyd\Test\TestCase; - -use Magento\Mtf\TestCase\Scenario; - -/** - * * Preconditions: - * 1. Configure shipping method. - * 2. Configure payment method. - * 3. Configure Signifyd fraud protection tool - * 4. Create products. - * 5. Create and setup customer. - * - * Steps: - * 1. Log in to Signifyd account. - * 2. Remove all existing webhooks by test team. - * 3. Add new webhook set. - * 4. Log in Storefront. - * 5. Add products to the Shopping Cart. - * 6. Click the 'Proceed to Checkout' button. - * 7. Fill shipping information. - * 8. Select shipping method. - * 9. Select Hosted Pro method. - * 10. Click 'Continue' button. - * 11. Specify credit card data in Paypal iframe. - * 12. Click 'Pay Now' button. - * 13. Log in to Signifyd account and search for created case. - * 14. Open created case. - * 15. Click "Flag case as bad" button. - * 16. Perform case info assertions. - * 17. Log in to Admin. - * 18. Proceed to order grid. - * 19. Perform Signifyd guarantee status assertions. - * 20. Proceed to order view. - * 21. Perform order status and case info assertions. - * 22. Click Deny Payment button. - * 23. Perform remaining assertions. - * - * @group Signifyd - * @ZephyrId MAGETWO-65332 - */ -class DenyPaymentWithSignifydGuaranteeDeclinedTest extends Scenario -{ - /* tags */ - const MVP = 'yes'; - const TEST_TYPE = '3rd_party_test_single_flow'; - const SEVERITY = 'S2'; - /* end tags */ - - /** - * Runs one page checkout test. - * - * @return void - */ - public function test() - { - $this->executeScenario(); - } -} diff --git a/dev/tests/functional/tests/app/Magento/Signifyd/Test/TestCase/DenyPaymentWithSignifydGuaranteeDeclinedTest.xml b/dev/tests/functional/tests/app/Magento/Signifyd/Test/TestCase/DenyPaymentWithSignifydGuaranteeDeclinedTest.xml deleted file mode 100644 index 5496619fe1bf9..0000000000000 --- a/dev/tests/functional/tests/app/Magento/Signifyd/Test/TestCase/DenyPaymentWithSignifydGuaranteeDeclinedTest.xml +++ /dev/null @@ -1,39 +0,0 @@ -<?xml version="1.0" encoding="utf-8"?> -<!-- -/** - * Copyright © Magento, Inc. All rights reserved. - * See COPYING.txt for license details. - */ - --> -<config xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="../../../../../../vendor/magento/mtf/etc/variations.xsd"> - <testCase name="Magento\Signifyd\Test\TestCase\DenyPaymentWithSignifydGuaranteeDeclinedTest" - summary="One Page Checkout with Signifyd, PayPal Payments Pro Hosted Solution with Fraud filters triggered."> - <variation name="DenyPaymentWithSignifydGuaranteeDeclinedWithHostedProVariation1" summary="Deny order placed within PayPal Payments Pro Hosted Solution with Fraud filters triggered and Signifyd Guarantee Declined" ticketId="MAGETWO-65332"> - <data name="products/0" xsi:type="string">catalogProductSimple::product_100_dollar</data> - <data name="customer/dataset" xsi:type="string">signifyd_decline_us_customer</data> - <data name="checkoutMethod" xsi:type="string">login</data> - <data name="shipping/shipping_service" xsi:type="string">Flat Rate</data> - <data name="shipping/shipping_method" xsi:type="string">Fixed</data> - <data name="payment/method" xsi:type="string">hosted_pro</data> - <data name="prices" xsi:type="array"> - <item name="grandTotal" xsi:type="string">210.00</item> - <item name="grandTotalCurrency" xsi:type="string">GBP</item> - </data> - <data name="creditCardClass" xsi:type="string">credit_card_hostedpro</data> - <data name="creditCard/dataset" xsi:type="string">visa_hosted_pro</data> - <data name="isVaultPresent" xsi:type="boolean">false</data> - <data name="configData" xsi:type="string">merchant_country_gb, hosted_pro, config_base_currency_gb, signifyd</data> - <data name="placeOrderStatus" xsi:type="string">Suspected Fraud</data> - <data name="status" xsi:type="string">Canceled</data> - <data name="signifydAddress/dataset" xsi:type="string">signifyd_us_shipping_address</data> - <data name="signifydAccount/dataset" xsi:type="string">sandbox_default</data> - <data name="signifydData/dataset" xsi:type="string">signifyd_guarantee_fraudulent</data> - <data name="tag" xsi:type="string">test_type:3rd_party_test_single_flow, severity:S2</data> - <constraint name="Magento\Sales\Test\Constraint\AssertOrderGrandTotal" /> - <constraint name="Magento\Signifyd\Test\Constraint\AssertSignifydCaseInCommentsHistory" /> - <constraint name="Magento\Sales\Test\Constraint\AssertDenyPaymentMessageInCommentsHistory" /> - <constraint name="Magento\Sales\Test\Constraint\AssertOrderStatusIsCorrect" /> - <constraint name="Magento\Signifyd\Test\Constraint\AssertSignifydGuaranteeCancelInCommentsHistory" /> - </variation> - </testCase> -</config> diff --git a/dev/tests/functional/tests/app/Magento/Signifyd/Test/TestStep/OpenOrderGridStep.php b/dev/tests/functional/tests/app/Magento/Signifyd/Test/TestStep/OpenOrderGridStep.php deleted file mode 100644 index 409dffc8340b7..0000000000000 --- a/dev/tests/functional/tests/app/Magento/Signifyd/Test/TestStep/OpenOrderGridStep.php +++ /dev/null @@ -1,173 +0,0 @@ -<?php -/** - * Copyright © Magento, Inc. All rights reserved. - * See COPYING.txt for license details. - */ -namespace Magento\Signifyd\Test\TestStep; - -use Magento\Mtf\TestStep\TestStepInterface; -use Magento\Sales\Test\Page\Adminhtml\OrderIndex; -use Magento\Sales\Test\Page\Adminhtml\SalesOrderView; -use Magento\Sales\Test\Constraint\AssertOrderStatusIsCorrect as AssertOrderStatus; -use Magento\Signifyd\Test\Constraint\AssertSignifydCaseInOrdersGrid as AssertOrdersGrid; -use Magento\Signifyd\Test\Constraint\AssertCaseInfoOnAdmin; -use Magento\Signifyd\Test\Fixture\SignifydData; -use Magento\Signifyd\Test\Page\Adminhtml\OrdersGrid; - -/** - * Open order grid step. - */ -class OpenOrderGridStep implements TestStepInterface -{ - /** - * Magento order status assertion. - * - * @var AssertOrderStatus - */ - private $assertOrderStatus; - - /** - * Case information on Magento Admin assertion. - * - * @var AssertCaseInfoOnAdmin - */ - private $assertCaseInfo; - - /** - * Case information on Magento order grid assertion. - * - * @var AssertOrdersGrid - */ - private $assertOrdersGrid; - - /** - * Magento order status. - * - * @var string - */ - private $placeOrderStatus; - - /** - * Magento order id. - * - * @var int - */ - private $orderId; - - /** - * Order View Page. - * - * @var SalesOrderView - */ - private $salesOrderView; - - /** - * Orders grid page. - * - * @var OrdersGrid - */ - private $ordersGrid; - - /** - * Signifyd data fixture. - * - * @var array - */ - private $signifydData; - - /** - * Orders Page. - * - * @var OrderIndex - */ - private $orderIndex; - - /** - * @param string $placeOrderStatus - * @param int $orderId - * @param OrderIndex $orderIndex - * @param SalesOrderView $salesOrderView - * @param OrdersGrid $ordersGrid - * @param AssertOrderStatus $assertOrderStatus - * @param AssertCaseInfoOnAdmin $assertCaseInfo - * @param AssertOrdersGrid $assertOrdersGrid - * @param SignifydData $signifydData - */ - public function __construct( - $placeOrderStatus, - $orderId, - OrderIndex $orderIndex, - SalesOrderView $salesOrderView, - OrdersGrid $ordersGrid, - AssertOrderStatus $assertOrderStatus, - AssertCaseInfoOnAdmin $assertCaseInfo, - AssertOrdersGrid $assertOrdersGrid, - SignifydData $signifydData - ) { - $this->placeOrderStatus = $placeOrderStatus; - $this->orderId = $orderId; - $this->orderIndex = $orderIndex; - $this->salesOrderView = $salesOrderView; - $this->ordersGrid = $ordersGrid; - $this->assertOrderStatus = $assertOrderStatus; - $this->assertCaseInfo = $assertCaseInfo; - $this->assertOrdersGrid = $assertOrdersGrid; - $this->signifydData = $signifydData; - } - - /** - * Open order. - * - * @return void - */ - public function run() - { - $this->checkOrdersGrid(); - $this->checkCaseInfo(); - $this->checkOrderStatus(); - } - - /** - * Run assert to check Signifyd Case Disposition status in orders grid. - * - * @return void - */ - private function checkOrdersGrid() - { - $this->assertOrdersGrid->processAssert( - $this->orderId, - $this->ordersGrid, - $this->signifydData - ); - } - - /** - * Run assert to check order status is valid. - * - * @return void - */ - private function checkOrderStatus() - { - $this->assertOrderStatus->processAssert( - $this->placeOrderStatus, - $this->orderId, - $this->orderIndex, - $this->salesOrderView - ); - } - - /** - * Run assert to check Signifyd Case information is correct in Admin. - * - * @return void - */ - private function checkCaseInfo() - { - $this->assertCaseInfo->processAssert( - $this->salesOrderView, - $this->orderIndex, - $this->signifydData, - $this->orderId - ); - } -} diff --git a/dev/tests/functional/tests/app/Magento/Signifyd/Test/TestStep/SignifydCancelOrderStep.php b/dev/tests/functional/tests/app/Magento/Signifyd/Test/TestStep/SignifydCancelOrderStep.php deleted file mode 100644 index 54f4dd75223e0..0000000000000 --- a/dev/tests/functional/tests/app/Magento/Signifyd/Test/TestStep/SignifydCancelOrderStep.php +++ /dev/null @@ -1,105 +0,0 @@ -<?php -/** - * Copyright © Magento, Inc. All rights reserved. - * See COPYING.txt for license details. - */ -namespace Magento\Signifyd\Test\TestStep; - -use Magento\Mtf\TestStep\TestStepFactory; -use Magento\Mtf\TestStep\TestStepInterface; -use Magento\Sales\Test\Fixture\OrderInjectable; -use Magento\Sales\Test\Page\Adminhtml\OrderIndex; -use Magento\Sales\Test\Page\Adminhtml\SalesOrderView; -use Magento\Sales\Test\TestStep\CancelOrderStep; -use Magento\Sales\Test\TestStep\DenyPaymentStep; -use Magento\Sales\Test\TestStep\UnholdOrderStep; - -/** - * Rollback step for Signifyd scenarios. - */ -class SignifydCancelOrderStep implements TestStepInterface -{ - /** - * Order index page. - * - * @var OrderIndex - */ - private $orderIndex; - - /** - * Order fixture. - * - * @var OrderInjectable - */ - private $order; - - /** - * Order View page. - * - * @var SalesOrderView - */ - private $salesOrderView; - - /** - * Test step factory. - * - * @var TestStepFactory - */ - private $testStepFactory; - - /** - * @param OrderIndex $orderIndex - * @param OrderInjectable $order - * @param SalesOrderView $salesOrderView - * @param TestStepFactory $testStepFactory - */ - public function __construct( - OrderIndex $orderIndex, - OrderInjectable $order, - SalesOrderView $salesOrderView, - TestStepFactory $testStepFactory - ) { - $this->orderIndex = $orderIndex; - $this->order = $order; - $this->salesOrderView = $salesOrderView; - $this->testStepFactory = $testStepFactory; - } - - /** - * @inheritdoc - */ - public function run() - { - $this->orderIndex->open(); - $this->orderIndex->getSalesOrderGrid() - ->searchAndOpen(['id' => $this->order->getId()]); - - switch ($this->salesOrderView->getOrderInfoBlock()->getOrderStatus()) { - case 'Suspected Fraud': - $this->getStepInstance(DenyPaymentStep::class)->run(); - break; - case 'On Hold': - $this->getStepInstance(UnholdOrderStep::class)->run(); - $this->getStepInstance(CancelOrderStep::class)->run(); - break; - case 'Canceled': - break; - default: - $this->getStepInstance(CancelOrderStep::class)->run(); - } - } - - /** - * Creates test step instance with preset params. - * - * @param string $class - * @return TestStepInterface - */ - private function getStepInstance($class) - { - return $this->testStepFactory->create( - $class, - ['order' => $this->order] - ); - } -} diff --git a/dev/tests/functional/tests/app/Magento/Signifyd/Test/TestStep/SignifydCreateCustomerStep.php b/dev/tests/functional/tests/app/Magento/Signifyd/Test/TestStep/SignifydCreateCustomerStep.php deleted file mode 100644 index 5fec5988d2c9f..0000000000000 --- a/dev/tests/functional/tests/app/Magento/Signifyd/Test/TestStep/SignifydCreateCustomerStep.php +++ /dev/null @@ -1,77 +0,0 @@ -<?php -/** - * Copyright © Magento, Inc. All rights reserved. - * See COPYING.txt for license details. - */ -namespace Magento\Signifyd\Test\TestStep; - -use Magento\Customer\Test\Fixture\Customer; -use Magento\Customer\Test\TestStep\CreateCustomerStep; -use Magento\Customer\Test\TestStep\DeleteCustomerStep; -use Magento\Mtf\TestStep\TestStepFactory; -use Magento\Mtf\TestStep\TestStepInterface; - -/** - * Customized create customer step with remove cleanup. - */ -class SignifydCreateCustomerStep implements TestStepInterface -{ - /** - * Customer fixture. - * - * @var Customer - */ - private $customer; - - /** - * Test step factory. - * - * @var TestStepFactory - */ - private $testStepFactory; - - /** - * @param Customer $customer - * @param TestStepFactory $testStepFactory - */ - public function __construct( - Customer $customer, - TestStepFactory $testStepFactory - ) { - $this->customer = $customer; - $this->testStepFactory = $testStepFactory; - } - - /** - * Run step flow. - * - * @return void - */ - public function run() - { - $this->getStepInstance(CreateCustomerStep::class)->run(); - } - - /** - * @return void - */ - public function cleanup() - { - $this->getStepInstance(CreateCustomerStep::class)->cleanup(); - $this->getStepInstance(DeleteCustomerStep::class)->run(); - } - - /** - * Creates test step instance with preset params. - * - * @param string $class - * @return TestStepInterface - */ - private function getStepInstance($class) - { - return $this->testStepFactory->create( - $class, - ['customer' => $this->customer] - ); - } -} diff --git a/dev/tests/functional/tests/app/Magento/Signifyd/Test/TestStep/SignifydFillShippingAddressStep.php b/dev/tests/functional/tests/app/Magento/Signifyd/Test/TestStep/SignifydFillShippingAddressStep.php deleted file mode 100644 index 0b1dda1d0a46a..0000000000000 --- a/dev/tests/functional/tests/app/Magento/Signifyd/Test/TestStep/SignifydFillShippingAddressStep.php +++ /dev/null @@ -1,70 +0,0 @@ -<?php -/** - * Copyright © Magento, Inc. All rights reserved. - * See COPYING.txt for license details. - */ -namespace Magento\Signifyd\Test\TestStep; - -use Magento\Checkout\Test\TestStep\FillShippingAddressStep; -use Magento\Checkout\Test\Page\CheckoutOnepage; -use Magento\Mtf\Fixture\FixtureFactory; -use Magento\Customer\Test\Fixture\Customer; -use Magento\Mtf\ObjectManager; -use Magento\Signifyd\Test\Fixture\SignifydAddress; - -/** - * Class SignifydFillShippingAddressStep only overrides type of $shippingAddress. - * - * We cannot configure Checkout_FillShippingAddressStep this via di.xml, - * because 'source' handler Firstname, specified in SignifydAddress - * fixture did not apply when the step was called from testcase.xml scenario. - * - * Note. When fixture was called directly in the class constructor, - * source handlers applied correctly. - */ -class SignifydFillShippingAddressStep extends FillShippingAddressStep -{ - /** - * @var SignifydAddress|null - */ - private $signifydAddress; - - /** - * @param CheckoutOnepage $checkoutOnepage - * @param Customer $customer - * @param ObjectManager $objectManager - * @param FixtureFactory $fixtureFactory - * @param SignifydAddress|null $signifydAddress - * @param null $shippingAddressCustomer - */ - public function __construct( - CheckoutOnepage $checkoutOnepage, - Customer $customer, - ObjectManager $objectManager, - FixtureFactory $fixtureFactory, - SignifydAddress $signifydAddress = null, - $shippingAddressCustomer = null - ) { - parent::__construct( - $checkoutOnepage, - $customer, - $objectManager, - $fixtureFactory, - $signifydAddress, - $shippingAddressCustomer - ); - $this->signifydAddress = $signifydAddress; - } - - /** - * @inheritdoc - */ - public function run() - { - parent::run(); - - return [ - 'signifydAddress' => $this->signifydAddress, - ]; - } -} diff --git a/dev/tests/functional/tests/app/Magento/Signifyd/Test/TestStep/SignifydLoginStep.php b/dev/tests/functional/tests/app/Magento/Signifyd/Test/TestStep/SignifydLoginStep.php deleted file mode 100644 index 3dd1b94d6b505..0000000000000 --- a/dev/tests/functional/tests/app/Magento/Signifyd/Test/TestStep/SignifydLoginStep.php +++ /dev/null @@ -1,68 +0,0 @@ -<?php -/** - * Copyright © Magento, Inc. All rights reserved. - * See COPYING.txt for license details. - */ -namespace Magento\Signifyd\Test\TestStep; - -use Magento\Mtf\TestStep\TestStepInterface; -use Magento\Signifyd\Test\Fixture\SignifydAccount; -use Magento\Signifyd\Test\Page\SignifydConsole\SignifydCases; -use Magento\Signifyd\Test\Page\SignifydConsole\SignifydLogin; - -/** - * Login into Signifyd console step. - */ -class SignifydLoginStep implements TestStepInterface -{ - /** - * Signifyd account fixture. - * - * @var SignifydAccount - */ - private $signifydAccount; - - /** - * Signifyd login page. - * - * @var SignifydLogin - */ - private $signifydLogin; - - /** - * Signifyd cases page. - * - * @var SignifydCases - */ - private $signifydCases; - - /** - * @param SignifydAccount $signifydAccount - * @param SignifydLogin $signifydLogin - * @param SignifydCases $signifydCases - */ - public function __construct( - SignifydAccount $signifydAccount, - SignifydLogin $signifydLogin, - SignifydCases $signifydCases - ) { - $this->signifydAccount = $signifydAccount; - $this->signifydLogin = $signifydLogin; - $this->signifydCases = $signifydCases; - } - - /** - * @inheritdoc - */ - public function run() - { - $this->signifydLogin->open(); - - if ($this->signifydLogin->getLoginBlock()->isVisible()) { - $this->signifydLogin->getLoginBlock()->fill($this->signifydAccount); - $this->signifydLogin->getLoginBlock()->login(); - } - - $this->signifydCases->getCaseSearchBlock()->waitForLoading(); - } -} diff --git a/dev/tests/functional/tests/app/Magento/Signifyd/Test/TestStep/SignifydObserveCaseStep.php b/dev/tests/functional/tests/app/Magento/Signifyd/Test/TestStep/SignifydObserveCaseStep.php deleted file mode 100644 index c00c81fa237e0..0000000000000 --- a/dev/tests/functional/tests/app/Magento/Signifyd/Test/TestStep/SignifydObserveCaseStep.php +++ /dev/null @@ -1,165 +0,0 @@ -<?php -/** - * Copyright © Magento, Inc. All rights reserved. - * See COPYING.txt for license details. - */ -namespace Magento\Signifyd\Test\TestStep; - -use Magento\Mtf\TestStep\TestStepFactory; -use Magento\Mtf\TestStep\TestStepInterface; -use Magento\Sales\Test\Fixture\OrderInjectable; -use Magento\Signifyd\Test\Constraint\AssertCaseInfoOnSignifydConsole; -use Magento\Signifyd\Test\Fixture\SignifydAddress; -use Magento\Signifyd\Test\Fixture\SignifydData; -use Magento\Signifyd\Test\Page\SignifydConsole\SignifydCases; -use Magento\Signifyd\Test\Page\SignifydConsole\SignifydNotifications; - -/** - * Observe case information in Signifyd console step. - */ -class SignifydObserveCaseStep implements TestStepInterface -{ - /** - * Case information on Signifyd console assertion. - * - * @var AssertCaseInfoOnSignifydConsole - */ - private $assertCaseInfo; - - /** - * Billing address fixture. - * - * @var SignifydAddress - */ - private $signifydAddress; - - /** - * Signifyd cases page. - * - * @var SignifydCases - */ - private $signifydCases; - - /** - * Signifyd notifications page. - * - * @var SignifydNotifications - */ - private $signifydNotifications; - - /** - * Signifyd data fixture. - * - * @var array - */ - private $signifydData; - - /** - * Prices list. - * - * @var array - */ - private $prices; - - /** - * Order fixture. - * - * @var string - */ - private $order; - - /** - * Test step factory. - * - * @var TestStepFactory - */ - private $testStepFactory; - - /** - * @var int - */ - private $searchAttempts = 10; - - /** - * @param AssertCaseInfoOnSignifydConsole $assertCaseInfoOnSignifydConsole - * @param SignifydAddress $signifydAddress - * @param SignifydCases $signifydCases - * @param SignifydNotifications $signifydNotifications - * @param SignifydData $signifydData - * @param OrderInjectable $order - * @param TestStepFactory $testStepFactory - * @param array $prices - */ - public function __construct( - AssertCaseInfoOnSignifydConsole $assertCaseInfoOnSignifydConsole, - SignifydAddress $signifydAddress, - SignifydCases $signifydCases, - SignifydNotifications $signifydNotifications, - SignifydData $signifydData, - OrderInjectable $order, - TestStepFactory $testStepFactory, - array $prices - ) { - $this->assertCaseInfo = $assertCaseInfoOnSignifydConsole; - $this->signifydAddress = $signifydAddress; - $this->signifydCases = $signifydCases; - $this->signifydNotifications = $signifydNotifications; - $this->signifydData = $signifydData; - $this->order = $order; - $this->testStepFactory = $testStepFactory; - $this->prices = $prices; - } - - /** - * @inheritdoc - */ - public function run() - { - $this->signifydCases->open(); - // Search case few times because it can appear with delay. - for ($attempts = $this->searchAttempts; $attempts > 0; $attempts--) { - $this->signifydCases->getCaseSearchBlock() - ->searchCaseByCustomerName($this->signifydAddress->getFirstname()); - if ($this->signifydCases->getCaseSearchBlock()->isAnyCaseVisible()) { - break; - } - sleep(3); - } - - $this->signifydCases->getCaseSearchBlock()->selectCase(); - $this->signifydCases->getCaseInfoBlock()->flagCase($this->signifydData->getCaseFlag()); - - $this->assertCaseInfo->processAssert( - $this->signifydCases, - $this->signifydAddress, - $this->signifydData, - $this->prices, - $this->order->getId(), - $this->getCustomerFullName($this->signifydAddress) - ); - } - - /** - * Cancel order if test fails, or in the end of variation. - * - * @return void - */ - public function cleanup() - { - $this->testStepFactory->create( - SignifydCancelOrderStep::class, - ['order' => $this->order] - )->run(); - } - - /** - * Gets customer full name. - * - * @param SignifydAddress $billingAddress - * @return string - */ - private function getCustomerFullName(SignifydAddress $billingAddress) - { - return sprintf('%s %s', $billingAddress->getFirstname(), $billingAddress->getLastname()); - } -} diff --git a/dev/tests/functional/tests/app/Magento/Signifyd/Test/TestStep/SignifydSetWebhookHandlersStep.php b/dev/tests/functional/tests/app/Magento/Signifyd/Test/TestStep/SignifydSetWebhookHandlersStep.php deleted file mode 100644 index 2b630b8c1dac8..0000000000000 --- a/dev/tests/functional/tests/app/Magento/Signifyd/Test/TestStep/SignifydSetWebhookHandlersStep.php +++ /dev/null @@ -1,64 +0,0 @@ -<?php -/** - * Copyright © Magento, Inc. All rights reserved. - * See COPYING.txt for license details. - */ -namespace Magento\Signifyd\Test\TestStep; - -use Magento\Mtf\TestStep\TestStepInterface; -use Magento\Signifyd\Test\Fixture\SignifydData; -use Magento\Signifyd\Test\Page\SignifydConsole\SignifydNotifications; - -/** - * Set webhook handlers in Signifyd console step. - */ -class SignifydSetWebhookHandlersStep implements TestStepInterface -{ - /** - * Signifyd Notifications page. - * - * @var SignifydNotifications - */ - private $signifydNotifications; - - /** - * Signifyd data fixture. - * - * @var array - */ - private $signifydData; - - /** - * @param SignifydNotifications $signifydNotifications - * @param SignifydData $signifydData - */ - public function __construct( - SignifydNotifications $signifydNotifications, - SignifydData $signifydData - ) { - $this->signifydNotifications = $signifydNotifications; - $this->signifydData = $signifydData; - } - - /** - * @inheritdoc - */ - public function run() - { - $this->signifydNotifications->open(); - $this->signifydNotifications->getWebhooksBlock() - ->create($this->signifydData->getTeam()); - } - - /** - * Removes webhooks if test fails, or in the end of variation execution. - * - * @return void - */ - public function cleanup() - { - $this->signifydNotifications->open(); - $this->signifydNotifications->getWebhooksBlock() - ->cleanup($this->signifydData->getTeam()); - } -} diff --git a/dev/tests/functional/tests/app/Magento/Signifyd/Test/TestStep/UnholdAndCancelOrderStep.php b/dev/tests/functional/tests/app/Magento/Signifyd/Test/TestStep/UnholdAndCancelOrderStep.php deleted file mode 100644 index 0c90b1b76937b..0000000000000 --- a/dev/tests/functional/tests/app/Magento/Signifyd/Test/TestStep/UnholdAndCancelOrderStep.php +++ /dev/null @@ -1,84 +0,0 @@ -<?php -/** - * Copyright © Magento, Inc. All rights reserved. - * See COPYING.txt for license details. - */ -namespace Magento\Signifyd\Test\TestStep; - -use Magento\Mtf\TestStep\TestStepFactory; -use Magento\Mtf\TestStep\TestStepInterface; -use Magento\Sales\Test\Fixture\OrderInjectable; -use Magento\Sales\Test\TestStep\CancelOrderStep; -use Magento\Sales\Test\TestStep\UnholdOrderStep; - -/** - * Unhold and cancel order. - */ -class UnholdAndCancelOrderStep implements TestStepInterface -{ - /** - * Magento order status. - * - * @var string - */ - private $placeOrderStatus; - - /** - * Order fixture. - * - * @var OrderInjectable - */ - private $order; - - /** - * Test step factory. - * - * @var TestStepFactory - */ - private $testStepFactory; - - /** - * @param string $placeOrderStatus - * @param OrderInjectable $order - * @param TestStepFactory $testStepFactory - */ - public function __construct( - $placeOrderStatus, - OrderInjectable $order, - TestStepFactory $testStepFactory - ) { - $this->placeOrderStatus = $placeOrderStatus; - $this->order = $order; - $this->testStepFactory = $testStepFactory; - } - - /** - * Cancel order step. - * - * If order was held - unhold and then cancel the order. - * - * @return void - */ - public function run() - { - if ($this->placeOrderStatus === 'On Hold') { - $this->getStepInstance(UnholdOrderStep::class)->run(); - } - - $this->getStepInstance(CancelOrderStep::class)->run(); - } - - /** - * Creates test step instance with preset params. - * - * @param string $class - * @return TestStepInterface - */ - private function getStepInstance($class) - { - return $this->testStepFactory->create( - $class, - ['order' => $this->order] - ); - } -} diff --git a/dev/tests/functional/tests/app/Magento/Signifyd/Test/etc/di.xml b/dev/tests/functional/tests/app/Magento/Signifyd/Test/etc/di.xml deleted file mode 100644 index 2ba4b6c97d33f..0000000000000 --- a/dev/tests/functional/tests/app/Magento/Signifyd/Test/etc/di.xml +++ /dev/null @@ -1,34 +0,0 @@ -<?xml version="1.0" encoding="utf-8"?> -<!-- -/** - * Copyright © Magento, Inc. All rights reserved. - * See COPYING.txt for license details. - */ - --> -<config xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="urn:magento:framework:ObjectManager/etc/config.xsd"> - <type name="Magento\Signifyd\Test\Constraint\AssertCaseInfoOnAdmin"> - <arguments> - <argument name="severity" xsi:type="string">S1</argument> - </arguments> - </type> - <type name="Magento\Signifyd\Test\Constraint\AssertCaseInfoOnSignifydConsole"> - <arguments> - <argument name="severity" xsi:type="string">S1</argument> - </arguments> - </type> - <type name="Magento\Signifyd\Test\Constraint\AssertSignifydCaseInCommentsHistory"> - <arguments> - <argument name="severity" xsi:type="string">S1</argument> - </arguments> - </type> - <type name="Magento\Signifyd\Test\Constraint\AssertSignifydCaseInOrdersGrid"> - <arguments> - <argument name="severity" xsi:type="string">S1</argument> - </arguments> - </type> - <type name="Magento\Signifyd\Test\Constraint\AssertSignifydGuaranteeCancelInCommentsHistory"> - <arguments> - <argument name="severity" xsi:type="string">S2</argument> - </arguments> - </type> -</config> diff --git a/dev/tests/functional/tests/app/Magento/Signifyd/Test/etc/testcase.xml b/dev/tests/functional/tests/app/Magento/Signifyd/Test/etc/testcase.xml deleted file mode 100644 index cfbd2e6ace2b4..0000000000000 --- a/dev/tests/functional/tests/app/Magento/Signifyd/Test/etc/testcase.xml +++ /dev/null @@ -1,58 +0,0 @@ -<?xml version="1.0"?> -<!-- -/** - * Copyright © Magento, Inc. All rights reserved. - * See COPYING.txt for license details. - */ ---> -<config xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="../../../../../../vendor/magento/mtf/Magento/Mtf/TestCase/etc/testcase.xsd"> - <scenario name="CreateSignifydGuaranteeAndCancelOrderTest" firstStep="signifydLogin"> - <step name="signifydLogin" module="Magento_Signifyd" next="signifydSetWebhookHandlers" /> - <step name="signifydSetWebhookHandlers" module="Magento_Signifyd" next="setupConfiguration" /> - <step name="setupConfiguration" module="Magento_Config" next="createProducts" /> - <step name="createProducts" module="Magento_Catalog" next="addProductsToTheCart" /> - <step name="addProductsToTheCart" module="Magento_Checkout" next="proceedToCheckoutFromMiniShoppingCart" /> - <step name="proceedToCheckoutFromMiniShoppingCart" module="Magento_Checkout" next="signifydCreateCustomer" /> - <step name="signifydCreateCustomer" module="Magento_Signifyd" next="selectCheckoutMethod" /> - <step name="selectCheckoutMethod" module="Magento_Checkout" next="signifydFillShippingAddress" /> - <step name="signifydFillShippingAddress" module="Magento_Signifyd" next="fillShippingMethod" /> - <step name="fillShippingMethod" module="Magento_Checkout" next="selectPaymentMethod" /> - <step name="selectPaymentMethod" module="Magento_Checkout" next="placeOrder" /> - <step name="placeOrder" module="Magento_Checkout" next="signifydObserveCase" /> - <step name="signifydObserveCase" module="Magento_Signifyd" next="openOrderGrid" /> - <step name="openOrderGrid" module="Magento_Signifyd" next="unholdAndCancelOrder" /> - <step name="unholdAndCancelOrder" module="Magento_Signifyd" /> - </scenario> - <scenario name="AcceptPaymentWithSignifydGuaranteeDeclinedTest" firstStep="signifydLogin"> - <step name="signifydLogin" module="Magento_Signifyd" next="signifydSetWebhookHandlers" /> - <step name="signifydSetWebhookHandlers" module="Magento_Signifyd" next="setupConfiguration" /> - <step name="setupConfiguration" module="Magento_Config" next="createProducts" /> - <step name="createProducts" module="Magento_Catalog" next="addProductsToTheCart" /> - <step name="addProductsToTheCart" module="Magento_Checkout" next="proceedToCheckoutFromMiniShoppingCart" /> - <step name="proceedToCheckoutFromMiniShoppingCart" module="Magento_Checkout" next="signifydCreateCustomer" /> - <step name="signifydCreateCustomer" module="Magento_Signifyd" next="selectCheckoutMethod" /> - <step name="selectCheckoutMethod" module="Magento_Checkout" next="signifydFillShippingAddress" /> - <step name="signifydFillShippingAddress" module="Magento_Signifyd" next="fillShippingMethod" /> - <step name="fillShippingMethod" module="Magento_Checkout" next="placeOrderWithHostedPro" /> - <step name="placeOrderWithHostedPro" module="Magento_Paypal" next="signifydObserveCase" /> - <step name="signifydObserveCase" module="Magento_Signifyd" next="openOrderGrid" /> - <step name="openOrderGrid" module="Magento_Signifyd" next="acceptPayment" /> - <step name="acceptPayment" module="Magento_Sales" /> - </scenario> - <scenario name="DenyPaymentWithSignifydGuaranteeDeclinedTest" firstStep="signifydLogin"> - <step name="signifydLogin" module="Magento_Signifyd" next="signifydSetWebhookHandlers" /> - <step name="signifydSetWebhookHandlers" module="Magento_Signifyd" next="setupConfiguration" /> - <step name="setupConfiguration" module="Magento_Config" next="createProducts" /> - <step name="createProducts" module="Magento_Catalog" next="addProductsToTheCart" /> - <step name="addProductsToTheCart" module="Magento_Checkout" next="proceedToCheckoutFromMiniShoppingCart" /> - <step name="proceedToCheckoutFromMiniShoppingCart" module="Magento_Checkout" next="signifydCreateCustomer" /> - <step name="signifydCreateCustomer" module="Magento_Signifyd" next="selectCheckoutMethod" /> - <step name="selectCheckoutMethod" module="Magento_Checkout" next="signifydFillShippingAddress" /> - <step name="signifydFillShippingAddress" module="Magento_Signifyd" next="fillShippingMethod" /> - <step name="fillShippingMethod" module="Magento_Checkout" next="placeOrderWithHostedPro" /> - <step name="placeOrderWithHostedPro" module="Magento_Paypal" next="signifydObserveCase" /> - <step name="signifydObserveCase" module="Magento_Signifyd" next="openOrderGrid" /> - <step name="openOrderGrid" module="Magento_Signifyd" next="denyPayment" /> - <step name="denyPayment" module="Magento_Sales" /> - </scenario> -</config> diff --git a/dev/tests/functional/tests/app/Magento/Swatches/Test/Block/Product/ProductList/ProductItem.php b/dev/tests/functional/tests/app/Magento/Swatches/Test/Block/Product/ProductList/ProductItem.php index 11d290a05cca7..395257a0b74a4 100644 --- a/dev/tests/functional/tests/app/Magento/Swatches/Test/Block/Product/ProductList/ProductItem.php +++ b/dev/tests/functional/tests/app/Magento/Swatches/Test/Block/Product/ProductList/ProductItem.php @@ -19,7 +19,7 @@ class ProductItem extends CatalogProductItem * * @var string */ - protected $swatchSelector = 'div[option-id="%s"]'; + protected $swatchSelector = 'div[data-option-id="%s"]'; /** * Selector for the swatches of the product. diff --git a/dev/tests/functional/tests/app/Magento/Swatches/Test/Block/Product/ViewWithSwatches.php b/dev/tests/functional/tests/app/Magento/Swatches/Test/Block/Product/ViewWithSwatches.php index d30aabd7ff370..b908c95b884bf 100644 --- a/dev/tests/functional/tests/app/Magento/Swatches/Test/Block/Product/ViewWithSwatches.php +++ b/dev/tests/functional/tests/app/Magento/Swatches/Test/Block/Product/ViewWithSwatches.php @@ -55,10 +55,10 @@ public function getSwatchAttributesData() $swatchAttributesData = []; $swatchAttributes = $this->_rootElement->getElements($this->swatchAttributesSelector); foreach ($swatchAttributes as $swatchAttribute) { - $attributeCode = $swatchAttribute->getAttribute('attribute-code'); + $attributeCode = $swatchAttribute->getAttribute('data-attribute-code'); $swatchAttributesData[$attributeCode] = [ 'attribute_code' => $attributeCode, - 'attribute_id' => $swatchAttribute->getAttribute('attribute-id'), + 'attribute_id' => $swatchAttribute->getAttribute('data-attribute-id'), 'label' => $swatchAttribute->find($this->swatchAttributesLabelSelector)->getText(), 'options' => $this->getSwatchAttributeOptionsData($swatchAttribute), ]; @@ -77,7 +77,7 @@ private function getSwatchAttributeOptionsData(ElementInterface $swatchAttribute $optionsData = []; $options = $swatchAttribute->getElements($this->swatchAttributeOptionsSelector); foreach ($options as $option) { - $optionId = $option->getAttribute('option-id'); + $optionId = $option->getAttribute('data-option-id'); $optionsData[$optionId] = [ 'option_id' => $optionId, 'label' => $option->getText(), diff --git a/dev/tests/functional/tests/app/Magento/UrlRewrite/Test/TestCase/CreateCategoryRewriteEntityTest.xml b/dev/tests/functional/tests/app/Magento/UrlRewrite/Test/TestCase/CreateCategoryRewriteEntityTest.xml index 1293c6e89b69a..b0f2a9e293295 100644 --- a/dev/tests/functional/tests/app/Magento/UrlRewrite/Test/TestCase/CreateCategoryRewriteEntityTest.xml +++ b/dev/tests/functional/tests/app/Magento/UrlRewrite/Test/TestCase/CreateCategoryRewriteEntityTest.xml @@ -39,6 +39,7 @@ <constraint name="Magento\UrlRewrite\Test\Constraint\AssertUrlRewriteCategoryRedirect" /> </variation> <variation name="CreateCategoryRewriteEntityTestVariation4" summary="Create Category URL Rewrites with Permanent redirect"> + <data name="tag" xsi:type="string">mftf_migrated:yes</data> <data name="urlRewrite/data/entity_type" xsi:type="string">For Category</data> <data name="urlRewrite/data/store_id" xsi:type="string">Main Website/Main Website Store/Default Store View</data> <data name="urlRewrite/data/request_path" xsi:type="string">request_path%isolation%</data> diff --git a/dev/tests/functional/tests/app/Magento/UrlRewrite/Test/TestCase/DeleteProductUrlRewriteEntityTest.xml b/dev/tests/functional/tests/app/Magento/UrlRewrite/Test/TestCase/DeleteProductUrlRewriteEntityTest.xml index 7a98f463072cb..7e8f8729716b0 100644 --- a/dev/tests/functional/tests/app/Magento/UrlRewrite/Test/TestCase/DeleteProductUrlRewriteEntityTest.xml +++ b/dev/tests/functional/tests/app/Magento/UrlRewrite/Test/TestCase/DeleteProductUrlRewriteEntityTest.xml @@ -8,6 +8,7 @@ <config xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="../../../../../../vendor/magento/mtf/etc/variations.xsd"> <testCase name="Magento\UrlRewrite\Test\TestCase\DeleteProductUrlRewriteEntityTest" summary="Delete Product URL Rewrites" ticketId="MAGETWO-23287"> <variation name="DeleteProductUrlRewriteEntityTestVariation1"> + <data name="tag" xsi:type="string">mftf_migrated:yes</data> <data name="productRedirect/dataset" xsi:type="string">default_without_target</data> <data name="productRedirect/data/target_path/entity" xsi:type="string">product/%catalogProductSimple::product_100_dollar%</data> <constraint name="Magento\UrlRewrite\Test\Constraint\AssertUrlRewriteDeletedMessage" /> diff --git a/dev/tests/functional/tests/app/Magento/User/Test/TestCase/CreateAdminUserRoleEntityTest.xml b/dev/tests/functional/tests/app/Magento/User/Test/TestCase/CreateAdminUserRoleEntityTest.xml index 19b7406139584..4ae4c476a959b 100644 --- a/dev/tests/functional/tests/app/Magento/User/Test/TestCase/CreateAdminUserRoleEntityTest.xml +++ b/dev/tests/functional/tests/app/Magento/User/Test/TestCase/CreateAdminUserRoleEntityTest.xml @@ -8,7 +8,7 @@ <config xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="../../../../../../vendor/magento/mtf/etc/variations.xsd"> <testCase name="Magento\User\Test\TestCase\CreateAdminUserRoleEntityTest" summary="Create Admin User Role" ticketId="MAGETWO-23413"> <variation name="CreateAdminUserRoleEntityTestVariation1"> - <data name="tag" xsi:type="string">test_type:extended_acceptance_test</data> + <data name="tag" xsi:type="string">test_type:extended_acceptance_test, mftf_migrated:yes</data> <data name="role/data/rolename" xsi:type="string">AdminRole%isolation%</data> <data name="role/data/current_password" xsi:type="string">%current_password%</data> <data name="role/data/resource_access" xsi:type="string">Custom</data> @@ -22,6 +22,7 @@ <data name="role/data/resource_access" xsi:type="string">All</data> <constraint name="Magento\User\Test\Constraint\AssertRoleSuccessSaveMessage" /> <constraint name="Magento\User\Test\Constraint\AssertRoleInGrid" /> + <data name="tag" xsi:type="string">mftf_migrated:yes</data> </variation> <variation name="CreateAdminUserRoleEntityTestVariation3"> <data name="role/data/rolename" xsi:type="string">AdminRole%isolation%</data> @@ -29,6 +30,7 @@ <data name="role/data/resource_access" xsi:type="string">All</data> <constraint name="Magento\User\Test\Constraint\AssertIncorrectUserPassword" /> <constraint name="Magento\User\Test\Constraint\AssertRoleNotInGrid" /> + <data name="tag" xsi:type="string">mftf_migrated:yes</data> </variation> </testCase> </config> diff --git a/dev/tests/functional/tests/app/Magento/User/Test/TestCase/DeleteAdminUserEntityTest.xml b/dev/tests/functional/tests/app/Magento/User/Test/TestCase/DeleteAdminUserEntityTest.xml index 2d44f86a17fe1..58e3fc4e76b30 100644 --- a/dev/tests/functional/tests/app/Magento/User/Test/TestCase/DeleteAdminUserEntityTest.xml +++ b/dev/tests/functional/tests/app/Magento/User/Test/TestCase/DeleteAdminUserEntityTest.xml @@ -12,12 +12,14 @@ <data name="systemAdmin/dataset" xsi:type="string">system_admin</data> <constraint name="Magento\User\Test\Constraint\AssertImpossibleDeleteYourOwnAccount" /> <constraint name="Magento\User\Test\Constraint\AssertUserInGrid" /> + <data name="tag" xsi:type="string">mftf_migrated:yes</data> </variation> <variation name="DeleteAdminUserEntityTestVariation2"> <data name="isDefaultUser" xsi:type="string">1</data> <data name="systemAdmin/dataset" xsi:type="string">system_admin</data> <constraint name="Magento\User\Test\Constraint\AssertUserSuccessDeleteMessage" /> <constraint name="Magento\User\Test\Constraint\AssertUserNotInGrid" /> + <data name="tag" xsi:type="string">mftf_migrated:yes</data> </variation> </testCase> </config> diff --git a/dev/tests/integration/_files/Magento/TestModuleCatalogSearch/Model/ElasticsearchVersionChecker.php b/dev/tests/integration/_files/Magento/TestModuleCatalogSearch/Model/ElasticsearchVersionChecker.php new file mode 100644 index 0000000000000..5e006a0aa1197 --- /dev/null +++ b/dev/tests/integration/_files/Magento/TestModuleCatalogSearch/Model/ElasticsearchVersionChecker.php @@ -0,0 +1,39 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +declare(strict_types=1); + +namespace Magento\TestModuleCatalogSearch\Model; + +use Magento\TestFramework\Helper\Curl; + +/** + * Retrieve elasticsearch version by curl request + */ +class ElasticsearchVersionChecker +{ + /** + * @var int + */ + private $version; + + /** + * @return int + */ + public function getVersion() : int + { + if (!$this->version) { + $curl = new Curl(); + $url = 'http://localhost:9200'; + $curl->get($url); + $curl->addHeader('content-type', 'application/json'); + $data = $curl->getBody(); + $versionData = explode('.', json_decode($data, true)['version']['number']); + $this->version = (int)array_shift($versionData); + } + + return $this->version; + } +} diff --git a/dev/tests/integration/framework/Magento/TestFramework/Application.php b/dev/tests/integration/framework/Magento/TestFramework/Application.php index 1bfc928f2916a..f0ce2e24545eb 100644 --- a/dev/tests/integration/framework/Magento/TestFramework/Application.php +++ b/dev/tests/integration/framework/Magento/TestFramework/Application.php @@ -558,7 +558,7 @@ private function copyAppConfigFiles() } } } - + /** * Copies global configuration file from the tests folder (see TESTS_GLOBAL_CONFIG_FILE) * diff --git a/dev/tests/integration/framework/Magento/TestFramework/Bundle/Model/PrepareBundleLinks.php b/dev/tests/integration/framework/Magento/TestFramework/Bundle/Model/PrepareBundleLinks.php new file mode 100644 index 0000000000000..6a7d034d5892f --- /dev/null +++ b/dev/tests/integration/framework/Magento/TestFramework/Bundle/Model/PrepareBundleLinks.php @@ -0,0 +1,89 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +declare(strict_types=1); + +namespace Magento\TestFramework\Bundle\Model; + +use Magento\Bundle\Api\Data\LinkInterfaceFactory; +use Magento\Bundle\Api\Data\OptionInterfaceFactory; +use Magento\Bundle\Model\Product\Price; +use Magento\Catalog\Api\Data\ProductExtensionFactory; +use Magento\Catalog\Api\Data\ProductInterface; + +/** + * Prepare bundle product links + */ +class PrepareBundleLinks +{ + /** @var LinkInterfaceFactory */ + private $linkFactory; + + /** @var OptionInterfaceFactory */ + private $optionLinkFactory; + + /** @var ProductExtensionFactory */ + private $extensionAttributesFactory; + + /** + * @param LinkInterfaceFactory $linkFactory + * @param OptionInterfaceFactory $optionLinkFactory + * @param ProductExtensionFactory $extensionAttributesFactory + */ + public function __construct( + LinkInterfaceFactory $linkFactory, + OptionInterfaceFactory $optionLinkFactory, + ProductExtensionFactory $extensionAttributesFactory + ) { + $this->linkFactory = $linkFactory; + $this->optionLinkFactory = $optionLinkFactory; + $this->extensionAttributesFactory = $extensionAttributesFactory; + } + + /** + * Prepare bundle product links + * + * @param ProductInterface $product + * @param array $bundleOptionsData + * @param array $bundleSelectionsData + * @return ProductInterface + */ + public function execute( + ProductInterface $product, + array $bundleOptionsData, + array $bundleSelectionsData + ): ProductInterface { + $product->setBundleOptionsData($bundleOptionsData) + ->setBundleSelectionsData($bundleSelectionsData); + $options = []; + foreach ($product->getBundleOptionsData() as $key => $optionData) { + $option = $this->optionLinkFactory->create(['data' => $optionData]); + $option->setSku($product->getSku()); + $option->setOptionId(null); + $links = []; + $bundleLinks = $product->getBundleSelectionsData(); + foreach ($bundleLinks[$key] as $linkData) { + $link = $this->linkFactory->create(['data' => $linkData]); + $link->setQty($linkData['selection_qty']); + $priceType = $price = null; + if ($product->getPriceType() === Price::PRICE_TYPE_FIXED) { + $priceType = $linkData['selection_price_type'] ?? null; + $price = $linkData['selection_price_value'] ?? null; + } + $link->setPriceType($priceType); + $link->setPrice($price); + $links[] = $link; + } + $option->setProductLinks($links); + $options[] = $option; + } + /** @var ProductExtensionFactory $extensionAttributesFactory */ + $extensionAttributes = $product->getExtensionAttributes() ?? $this->extensionAttributesFactory->create(); + $extensionAttributes->setBundleProductOptions($options); + $product->setExtensionAttributes($extensionAttributes); + + return $product; + } +} diff --git a/dev/tests/integration/framework/Magento/TestFramework/Catalog/Block/Product/View/Options/DateGroupDataProvider.php b/dev/tests/integration/framework/Magento/TestFramework/Catalog/Block/Product/View/Options/DateGroupDataProvider.php new file mode 100644 index 0000000000000..7e83b3f349d4d --- /dev/null +++ b/dev/tests/integration/framework/Magento/TestFramework/Catalog/Block/Product/View/Options/DateGroupDataProvider.php @@ -0,0 +1,211 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +declare(strict_types=1); + +namespace Magento\TestFramework\Catalog\Block\Product\View\Options; + +use Magento\Catalog\Api\Data\ProductCustomOptionInterface; +use Magento\Catalog\Model\Config\Source\ProductPriceOptionsInterface; +use Magento\Catalog\Model\Product\Option; + +/** + * Data provider with product custom options from date group(date, date & time, time). + */ +class DateGroupDataProvider +{ + /** + * Return options data. + * + * @SuppressWarnings(PHPMD.ExcessiveMethodLength) + * + * @return array + */ + public function getData(): array + { + return [ + 'type_date_required' => [ + [ + Option::KEY_TITLE => 'Test option date title 1', + Option::KEY_TYPE => ProductCustomOptionInterface::OPTION_TYPE_DATE, + Option::KEY_IS_REQUIRE => 1, + Option::KEY_PRICE => 10, + Option::KEY_PRICE_TYPE => ProductPriceOptionsInterface::VALUE_FIXED, + Option::KEY_SKU => 'test-option-date-title-1', + ], + [ + 'block_with_required_class' => '<div class="field date required"', + 'title' => '<span>Test option date title 1</span>', + 'price' => 'data-price-amount="10"', + ], + ], + 'type_date_not_required' => [ + [ + Option::KEY_TITLE => 'Test option date title 2', + Option::KEY_TYPE => ProductCustomOptionInterface::OPTION_TYPE_DATE, + Option::KEY_IS_REQUIRE => 0, + Option::KEY_PRICE => 10, + Option::KEY_PRICE_TYPE => ProductPriceOptionsInterface::VALUE_FIXED, + Option::KEY_SKU => 'test-option-date-title-2', + ], + [ + 'block_with_required_class' => '<div class="field date"', + 'title' => '<span>Test option date title 2</span>', + 'price' => 'data-price-amount="10"', + ], + ], + 'type_date_fixed_price' => [ + [ + Option::KEY_TITLE => 'Test option date title 3', + Option::KEY_TYPE => ProductCustomOptionInterface::OPTION_TYPE_DATE, + Option::KEY_IS_REQUIRE => 0, + Option::KEY_PRICE => 50, + Option::KEY_PRICE_TYPE => ProductPriceOptionsInterface::VALUE_FIXED, + Option::KEY_SKU => 'test-option-date-title-3', + ], + [ + 'block_with_required_class' => '<div class="field date"', + 'title' => '<span>Test option date title 3</span>', + 'price' => 'data-price-amount="50"', + ], + ], + 'type_date_percent_price' => [ + [ + Option::KEY_TITLE => 'Test option date title 4', + Option::KEY_TYPE => ProductCustomOptionInterface::OPTION_TYPE_DATE, + Option::KEY_IS_REQUIRE => 0, + Option::KEY_PRICE => 50, + Option::KEY_PRICE_TYPE => ProductPriceOptionsInterface::VALUE_PERCENT, + Option::KEY_SKU => 'test-option-date-title-4', + ], + [ + 'block_with_required_class' => '<div class="field date"', + 'title' => '<span>Test option date title 4</span>', + 'price' => 'data-price-amount="5"', + ], + ], + 'type_date_and_time_required' => [ + [ + Option::KEY_TITLE => 'Test option date and time title 1', + Option::KEY_TYPE => ProductCustomOptionInterface::OPTION_TYPE_DATE_TIME, + Option::KEY_IS_REQUIRE => 1, + Option::KEY_PRICE => 10, + Option::KEY_PRICE_TYPE => ProductPriceOptionsInterface::VALUE_FIXED, + Option::KEY_SKU => 'test-option-date-and-time-title-1', + ], + [ + 'block_with_required_class' => '<div class="field date required"', + 'title' => '<span>Test option date and time title 1</span>', + 'price' => 'data-price-amount="10"', + ], + ], + 'type_date_and_time_not_required' => [ + [ + Option::KEY_TITLE => 'Test option date and time title 2', + Option::KEY_TYPE => ProductCustomOptionInterface::OPTION_TYPE_DATE_TIME, + Option::KEY_IS_REQUIRE => 0, + Option::KEY_PRICE => 10, + Option::KEY_PRICE_TYPE => ProductPriceOptionsInterface::VALUE_FIXED, + Option::KEY_SKU => 'test-option-date-and-time-title-2', + ], + [ + 'block_with_required_class' => '<div class="field date"', + 'title' => '<span>Test option date and time title 2</span>', + 'price' => 'data-price-amount="10"', + ], + ], + 'type_date_and_time_fixed_price' => [ + [ + Option::KEY_TITLE => 'Test option date and time title 3', + Option::KEY_TYPE => ProductCustomOptionInterface::OPTION_TYPE_DATE_TIME, + Option::KEY_IS_REQUIRE => 0, + Option::KEY_PRICE => 50, + Option::KEY_PRICE_TYPE => ProductPriceOptionsInterface::VALUE_FIXED, + Option::KEY_SKU => 'test-option-date-and-time-title-3', + ], + [ + 'block_with_required_class' => '<div class="field date"', + 'title' => '<span>Test option date and time title 3</span>', + 'price' => 'data-price-amount="50"', + ], + ], + 'type_date_and_time_percent_price' => [ + [ + Option::KEY_TITLE => 'Test option date and time title 4', + Option::KEY_TYPE => ProductCustomOptionInterface::OPTION_TYPE_DATE_TIME, + Option::KEY_IS_REQUIRE => 0, + Option::KEY_PRICE => 50, + Option::KEY_PRICE_TYPE => ProductPriceOptionsInterface::VALUE_PERCENT, + Option::KEY_SKU => 'test-option-date-and-time-title-4', + ], + [ + 'block_with_required_class' => '<div class="field date"', + 'title' => '<span>Test option date and time title 4</span>', + 'price' => 'data-price-amount="5"', + ], + ], + 'type_time_required' => [ + [ + Option::KEY_TITLE => 'Test option time title 1', + Option::KEY_TYPE => ProductCustomOptionInterface::OPTION_TYPE_TIME, + Option::KEY_IS_REQUIRE => 1, + Option::KEY_PRICE => 10, + Option::KEY_PRICE_TYPE => ProductPriceOptionsInterface::VALUE_FIXED, + Option::KEY_SKU => 'test-option-time-title-1', + ], + [ + 'block_with_required_class' => '<div class="field date required"', + 'title' => '<span>Test option time title 1</span>', + 'price' => 'data-price-amount="10"', + ], + ], + 'type_time_not_required' => [ + [ + Option::KEY_TITLE => 'Test option time title 2', + Option::KEY_TYPE => ProductCustomOptionInterface::OPTION_TYPE_TIME, + Option::KEY_IS_REQUIRE => 0, + Option::KEY_PRICE => 10, + Option::KEY_PRICE_TYPE => ProductPriceOptionsInterface::VALUE_FIXED, + Option::KEY_SKU => 'test-option-time-title-2', + ], + [ + 'block_with_required_class' => '<div class="field date"', + 'title' => '<span>Test option time title 2</span>', + 'price' => 'data-price-amount="10"', + ], + ], + 'type_time_fixed_price' => [ + [ + Option::KEY_TITLE => 'Test option time title 3', + Option::KEY_TYPE => ProductCustomOptionInterface::OPTION_TYPE_TIME, + Option::KEY_IS_REQUIRE => 0, + Option::KEY_PRICE => 50, + Option::KEY_PRICE_TYPE => ProductPriceOptionsInterface::VALUE_FIXED, + Option::KEY_SKU => 'test-option-time-title-3', + ], + [ + 'block_with_required_class' => '<div class="field date"', + 'title' => '<span>Test option time title 3</span>', + 'price' => 'data-price-amount="50"', + ], + ], + 'type_time_percent_price' => [ + [ + Option::KEY_TITLE => 'Test option time title 4', + Option::KEY_TYPE => ProductCustomOptionInterface::OPTION_TYPE_TIME, + Option::KEY_IS_REQUIRE => 0, + Option::KEY_PRICE => 50, + Option::KEY_PRICE_TYPE => ProductPriceOptionsInterface::VALUE_PERCENT, + Option::KEY_SKU => 'test-option-time-title-4', + ], + [ + 'block_with_required_class' => '<div class="field date"', + 'title' => '<span>Test option time title 4</span>', + 'price' => 'data-price-amount="5"', + ], + ], + ]; + } +} diff --git a/dev/tests/integration/framework/Magento/TestFramework/Catalog/Block/Product/View/Options/FileGroupDataProvider.php b/dev/tests/integration/framework/Magento/TestFramework/Catalog/Block/Product/View/Options/FileGroupDataProvider.php new file mode 100644 index 0000000000000..1817509539eec --- /dev/null +++ b/dev/tests/integration/framework/Magento/TestFramework/Catalog/Block/Product/View/Options/FileGroupDataProvider.php @@ -0,0 +1,135 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +declare(strict_types=1); + +namespace Magento\TestFramework\Catalog\Block\Product\View\Options; + +use Magento\Catalog\Api\Data\ProductCustomOptionInterface; +use Magento\Catalog\Model\Config\Source\ProductPriceOptionsInterface; +use Magento\Catalog\Model\Product\Option; + +/** + * Data provider with product custom options from file group(file). + */ +class FileGroupDataProvider +{ + /** + * Return options data. + * + * @SuppressWarnings(PHPMD.ExcessiveMethodLength) + * + * @return array + */ + public function getData(): array + { + return [ + 'type_file_required' => [ + [ + Option::KEY_TITLE => 'Test option file title 1', + Option::KEY_TYPE => ProductCustomOptionInterface::OPTION_TYPE_FILE, + Option::KEY_IS_REQUIRE => 1, + Option::KEY_PRICE => 10, + Option::KEY_PRICE_TYPE => ProductPriceOptionsInterface::VALUE_FIXED, + Option::KEY_SKU => 'test-option-file-title-1', + Option::KEY_SORT_ORDER => 1, + Option::KEY_FILE_EXTENSION => 'png, jpg', + ], + [ + 'block_with_required_class' => '<div class="field file required">', + 'label_for_created_option' => '<label class="label" for="options_%s_file"', + 'title' => '<span>Test option file title 1</span>', + 'price' => 'data-price-amount="10"', + 'required_element' => '/<input type="file"/', + 'file_extension' => '<strong>png, jpg</strong>', + ], + ], + 'type_file_not_required' => [ + [ + Option::KEY_TITLE => 'Test option file title 2', + Option::KEY_TYPE => ProductCustomOptionInterface::OPTION_TYPE_FILE, + Option::KEY_IS_REQUIRE => 0, + Option::KEY_PRICE => 10, + Option::KEY_PRICE_TYPE => ProductPriceOptionsInterface::VALUE_FIXED, + Option::KEY_SKU => 'test-option-file-title-2', + Option::KEY_SORT_ORDER => 1, + Option::KEY_FILE_EXTENSION => 'png, jpg', + ], + [ + 'block_with_required_class' => '<div class="field file">', + 'label_for_created_option' => '<label class="label" for="options_%s_file"', + 'title' => '<span>Test option file title 2</span>', + 'price' => 'data-price-amount="10"', + 'required_element' => '/<input type="file"/', + 'file_extension' => '<strong>png, jpg</strong>', + ], + ], + 'type_file_fixed_price' => [ + [ + Option::KEY_TITLE => 'Test option file title 3', + Option::KEY_TYPE => ProductCustomOptionInterface::OPTION_TYPE_FILE, + Option::KEY_IS_REQUIRE => 0, + Option::KEY_PRICE => 50, + Option::KEY_PRICE_TYPE => ProductPriceOptionsInterface::VALUE_FIXED, + Option::KEY_SKU => 'test-option-file-title-3', + Option::KEY_SORT_ORDER => 1, + Option::KEY_FILE_EXTENSION => 'png, jpg', + ], + [ + 'block_with_required_class' => '<div class="field file">', + 'label_for_created_option' => '<label class="label" for="options_%s_file"', + 'title' => '<span>Test option file title 3</span>', + 'price' => 'data-price-amount="50"', + 'required_element' => '/<input type="file"/', + 'file_extension' => '<strong>png, jpg</strong>', + ], + ], + 'type_file_percent_price' => [ + [ + Option::KEY_TITLE => 'Test option file title 4', + Option::KEY_TYPE => ProductCustomOptionInterface::OPTION_TYPE_FILE, + Option::KEY_IS_REQUIRE => 0, + Option::KEY_PRICE => 50, + Option::KEY_PRICE_TYPE => ProductPriceOptionsInterface::VALUE_PERCENT, + Option::KEY_SKU => 'test-option-file-title-4', + Option::KEY_SORT_ORDER => 1, + Option::KEY_FILE_EXTENSION => 'png, jpg', + ], + [ + 'block_with_required_class' => '<div class="field file">', + 'label_for_created_option' => '<label class="label" for="options_%s_file"', + 'title' => '<span>Test option file title 4</span>', + 'price' => 'data-price-amount="5"', + 'required_element' => '/<input type="file"/', + 'file_extension' => '<strong>png, jpg</strong>', + ], + ], + 'type_file_with_width_and_height' => [ + [ + Option::KEY_TITLE => 'Test option file title 5', + Option::KEY_TYPE => ProductCustomOptionInterface::OPTION_TYPE_FILE, + Option::KEY_IS_REQUIRE => 0, + Option::KEY_PRICE => 50, + Option::KEY_PRICE_TYPE => ProductPriceOptionsInterface::VALUE_FIXED, + Option::KEY_SKU => 'test-option-file-title-5', + Option::KEY_SORT_ORDER => 1, + Option::KEY_FILE_EXTENSION => 'png, jpg', + Option::KEY_IMAGE_SIZE_X => 10, + Option::KEY_IMAGE_SIZE_Y => 81, + ], + [ + 'block_with_required_class' => '<div class="field file">', + 'label_for_created_option' => '<label class="label" for="options_%s_file"', + 'title' => '<span>Test option file title 5</span>', + 'price' => 'data-price-amount="50"', + 'required_element' => '/<input type="file"/', + 'file_extension' => '<strong>png, jpg</strong>', + 'file_width' => '/%s:.*<strong>10 px.<\/strong>/', + 'file_height' => '/%s:.*<strong>81 px.<\/strong>/', + ], + ], + ]; + } +} diff --git a/dev/tests/integration/framework/Magento/TestFramework/Catalog/Block/Product/View/Options/SelectGroupDataProvider.php b/dev/tests/integration/framework/Magento/TestFramework/Catalog/Block/Product/View/Options/SelectGroupDataProvider.php new file mode 100644 index 0000000000000..ad9ffb40f5762 --- /dev/null +++ b/dev/tests/integration/framework/Magento/TestFramework/Catalog/Block/Product/View/Options/SelectGroupDataProvider.php @@ -0,0 +1,364 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +declare(strict_types=1); + +namespace Magento\TestFramework\Catalog\Block\Product\View\Options; + +use Magento\Catalog\Api\Data\ProductCustomOptionInterface; +use Magento\Catalog\Model\Config\Source\ProductPriceOptionsInterface; +use Magento\Catalog\Model\Product\Option; +use Magento\Catalog\Model\Product\Option\Value; + +/** + * Data provider with product custom options from select group(drop-down, radio buttons, checkbox, multiple select). + */ +class SelectGroupDataProvider +{ + /** + * Return options data. + * + * @SuppressWarnings(PHPMD.ExcessiveMethodLength) + * + * @return array + */ + public function getData(): array + { + return [ + 'type_drop_down_required' => [ + [ + Option::KEY_TITLE => 'Test option drop-down title 1', + Option::KEY_TYPE => ProductCustomOptionInterface::OPTION_TYPE_DROP_DOWN, + Option::KEY_IS_REQUIRE => 1, + ], + [ + Value::KEY_TITLE => 'Test option drop-down title 1 value 1', + Value::KEY_PRICE => 10, + Value::KEY_PRICE_TYPE => ProductPriceOptionsInterface::VALUE_FIXED, + Value::KEY_SKU => 'test-option-drop-down-title-1-value-1', + ], + [ + 'block_with_required_class' => '<div class="field required">', + 'label_for_created_option' => '<label class="label" for="select_%s">', + 'title' => '<span>Test option drop-down title 1</span>', + 'required_element' => '/<select/', + 'option_value_item' => '/<option value="%s" price="10" >%s \+\s{11}\$10.00.*/', + 'not_contain_arr' => [ + '/<select.*multiple="multiple"/', + ], + ], + ], + 'type_drop_down_not_required' => [ + [ + Option::KEY_TITLE => 'Test option drop-down title 2', + Option::KEY_TYPE => ProductCustomOptionInterface::OPTION_TYPE_DROP_DOWN, + Option::KEY_IS_REQUIRE => 0, + ], + [ + Value::KEY_TITLE => 'Test option drop-down title 2 value 1', + Value::KEY_PRICE => 10, + Value::KEY_PRICE_TYPE => ProductPriceOptionsInterface::VALUE_FIXED, + Value::KEY_SKU => 'test-option-drop-down-title-2-value-1', + ], + [ + 'block_with_required_class' => '<div class="field">', + 'label_for_created_option' => '<label class="label" for="select_%s">', + 'title' => '<span>Test option drop-down title 2</span>', + 'required_element' => '/<select/', + 'option_value_item' => '/<option value="%s" price="10" >%s \+\s{11}\$10.00.*/', + 'not_contain_arr' => [ + '/<select.*multiple="multiple"/', + ], + ], + ], + 'type_drop_down_value_fixed_price' => [ + [ + Option::KEY_TITLE => 'Test option drop-down title 3', + Option::KEY_TYPE => ProductCustomOptionInterface::OPTION_TYPE_DROP_DOWN, + Option::KEY_IS_REQUIRE => 0, + ], + [ + Value::KEY_TITLE => 'Test option drop-down title 3 value 1', + Value::KEY_PRICE => 50, + Value::KEY_PRICE_TYPE => ProductPriceOptionsInterface::VALUE_FIXED, + Value::KEY_SKU => 'test-option-drop-down-title-3-value-1', + ], + [ + 'block_with_required_class' => '<div class="field">', + 'label_for_created_option' => '<label class="label" for="select_%s">', + 'title' => '<span>Test option drop-down title 3</span>', + 'required_element' => '/<select/', + 'option_value_item' => '/<option value="%s" price="50" >%s \+\s{11}\$50.00.*/', + 'not_contain_arr' => [ + '/<select.*multiple="multiple"/', + ], + ], + ], + 'type_drop_down_value_percent_price' => [ + [ + Option::KEY_TITLE => 'Test option drop-down title 4', + Option::KEY_TYPE => ProductCustomOptionInterface::OPTION_TYPE_DROP_DOWN, + Option::KEY_IS_REQUIRE => 0, + ], + [ + Value::KEY_TITLE => 'Test option drop-down title 4 value 1', + Value::KEY_PRICE => 50, + Value::KEY_PRICE_TYPE => ProductPriceOptionsInterface::VALUE_PERCENT, + Value::KEY_SKU => 'test-option-drop-down-title-4-value-1', + ], + [ + 'block_with_required_class' => '<div class="field">', + 'label_for_created_option' => '<label class="label" for="select_%s">', + 'title' => '<span>Test option drop-down title 4</span>', + 'required_element' => '/<select/', + 'option_value_item' => '/<option value="%s" price="5" >%s \+\s{11}\$5.00.*/', + 'not_contain_arr' => [ + '/<select.*multiple="multiple"/', + ], + ], + ], + 'type_radio_button_required' => [ + [ + Option::KEY_TITLE => 'Test option radio-button title 1', + Option::KEY_TYPE => ProductCustomOptionInterface::OPTION_TYPE_RADIO, + Option::KEY_IS_REQUIRE => 1, + ], + [ + Value::KEY_TITLE => 'Test option radio-button title 1 value 1', + Value::KEY_PRICE => 10, + Value::KEY_PRICE_TYPE => ProductPriceOptionsInterface::VALUE_FIXED, + Value::KEY_SKU => 'test-option-radio-button-title-1-value-1', + ], + [ + 'block_with_required_class' => '<div class="field required">', + 'label_for_created_option' => '<label class="label" for="select_%s">', + 'title' => '<span>Test option radio-button title 1</span>', + 'required_element' => '/<input type="radio"/', + 'price' => 'data-price-amount="10"', + ], + ], + 'type_radio_button_not_required' => [ + [ + Option::KEY_TITLE => 'Test option radio-button title 2', + Option::KEY_TYPE => ProductCustomOptionInterface::OPTION_TYPE_RADIO, + Option::KEY_IS_REQUIRE => 0, + ], + [ + Value::KEY_TITLE => 'Test option radio-button title 2 value 1', + Value::KEY_PRICE => 10, + Value::KEY_PRICE_TYPE => ProductPriceOptionsInterface::VALUE_FIXED, + Value::KEY_SKU => 'test-option-radio-button-title-2-value-1', + ], + [ + 'block_with_required_class' => '<div class="field">', + 'label_for_created_option' => '<label class="label" for="select_%s">', + 'title' => '<span>Test option radio-button title 2</span>', + 'required_element' => '/<input type="radio"/', + 'price' => 'data-price-amount="10"', + ], + ], + 'type_radio_button_value_fixed_price' => [ + [ + Option::KEY_TITLE => 'Test option radio-button title 3', + Option::KEY_TYPE => ProductCustomOptionInterface::OPTION_TYPE_RADIO, + Option::KEY_IS_REQUIRE => 0, + ], + [ + Value::KEY_TITLE => 'Test option radio-button title 3 value 1', + Value::KEY_PRICE => 50, + Value::KEY_PRICE_TYPE => ProductPriceOptionsInterface::VALUE_FIXED, + Value::KEY_SKU => 'test-option-radio-button-title-3-value-1', + ], + [ + 'block_with_required_class' => '<div class="field">', + 'label_for_created_option' => '<label class="label" for="select_%s">', + 'title' => '<span>Test option radio-button title 3</span>', + 'required_element' => '/<input type="radio"/', + 'price' => 'data-price-amount="50"', + ], + ], + 'type_radio_button_value_percent_price' => [ + [ + Option::KEY_TITLE => 'Test option radio-button title 4', + Option::KEY_TYPE => ProductCustomOptionInterface::OPTION_TYPE_RADIO, + Option::KEY_IS_REQUIRE => 0, + ], + [ + Value::KEY_TITLE => 'Test option radio-button title 4 value 1', + Value::KEY_PRICE => 50, + Value::KEY_PRICE_TYPE => ProductPriceOptionsInterface::VALUE_PERCENT, + Value::KEY_SKU => 'test-option-radio-button-title-4-value-1', + ], + [ + 'block_with_required_class' => '<div class="field">', + 'label_for_created_option' => '<label class="label" for="select_%s">', + 'title' => '<span>Test option radio-button title 4</span>', + 'required_element' => '/<input type="radio"/', + 'price' => 'data-price-amount="5"', + ], + ], + 'type_checkbox_required' => [ + [ + Option::KEY_TITLE => 'Test option checkbox title 1', + Option::KEY_TYPE => ProductCustomOptionInterface::OPTION_TYPE_CHECKBOX, + Option::KEY_IS_REQUIRE => 1, + ], + [ + Value::KEY_TITLE => 'Test option checkbox title 1 value 1', + Value::KEY_PRICE => 10, + Value::KEY_PRICE_TYPE => ProductPriceOptionsInterface::VALUE_FIXED, + Value::KEY_SKU => 'test-option-checkbox-title-1-value-1', + ], + [ + 'block_with_required_class' => '<div class="field required">', + 'label_for_created_option' => '<label class="label" for="select_%s">', + 'title' => '<span>Test option checkbox title 1</span>', + 'required_element' => '/<input type="checkbox"/', + 'price' => 'data-price-amount="10"', + ], + ], + 'type_checkbox_not_required' => [ + [ + Option::KEY_TITLE => 'Test option checkbox title 2', + Option::KEY_TYPE => ProductCustomOptionInterface::OPTION_TYPE_CHECKBOX, + Option::KEY_IS_REQUIRE => 0, + ], + [ + Value::KEY_TITLE => 'Test option checkbox title 2 value 1', + Value::KEY_PRICE => 10, + Value::KEY_PRICE_TYPE => ProductPriceOptionsInterface::VALUE_FIXED, + Value::KEY_SKU => 'test-option-checkbox-title-2-value-1', + ], + [ + 'block_with_required_class' => '<div class="field">', + 'label_for_created_option' => '<label class="label" for="select_%s">', + 'title' => '<span>Test option checkbox title 2</span>', + 'required_element' => '/<input type="checkbox"/', + 'price' => 'data-price-amount="10"', + ], + ], + 'type_checkbox_value_fixed_price' => [ + [ + Option::KEY_TITLE => 'Test option checkbox title 3', + Option::KEY_TYPE => ProductCustomOptionInterface::OPTION_TYPE_CHECKBOX, + Option::KEY_IS_REQUIRE => 0, + ], + [ + Value::KEY_TITLE => 'Test option checkbox title 3 value 1', + Value::KEY_PRICE => 50, + Value::KEY_PRICE_TYPE => ProductPriceOptionsInterface::VALUE_FIXED, + Value::KEY_SKU => 'test-option-checkbox-title-3-value-1', + ], + [ + 'block_with_required_class' => '<div class="field">', + 'label_for_created_option' => '<label class="label" for="select_%s">', + 'title' => '<span>Test option checkbox title 3</span>', + 'required_element' => '/<input type="checkbox"/', + 'price' => 'data-price-amount="50"', + ], + ], + 'type_checkbox_value_percent_price' => [ + [ + Option::KEY_TITLE => 'Test option checkbox title 4', + Option::KEY_TYPE => ProductCustomOptionInterface::OPTION_TYPE_CHECKBOX, + Option::KEY_IS_REQUIRE => 0, + ], + [ + Value::KEY_TITLE => 'Test option checkbox title 4 value 1', + Value::KEY_PRICE => 50, + Value::KEY_PRICE_TYPE => ProductPriceOptionsInterface::VALUE_PERCENT, + Value::KEY_SKU => 'test-option-checkbox-title-4-value-1', + ], + [ + 'block_with_required_class' => '<div class="field">', + 'label_for_created_option' => '<label class="label" for="select_%s">', + 'title' => '<span>Test option checkbox title 4</span>', + 'required_element' => '/<input type="checkbox"/', + 'price' => 'data-price-amount="5"', + ], + ], + 'type_multiselect_required' => [ + [ + Option::KEY_TITLE => 'Test option multiselect title 1', + Option::KEY_TYPE => ProductCustomOptionInterface::OPTION_TYPE_MULTIPLE, + Option::KEY_IS_REQUIRE => 1, + ], + [ + Value::KEY_TITLE => 'Test option multiselect title 1 value 1', + Value::KEY_PRICE => 10, + Value::KEY_PRICE_TYPE => ProductPriceOptionsInterface::VALUE_FIXED, + Value::KEY_SKU => 'test-option-multiselect-title-1-value-1', + ], + [ + 'block_with_required_class' => '<div class="field required">', + 'label_for_created_option' => '<label class="label" for="select_%s">', + 'title' => '<span>Test option multiselect title 1</span>', + 'required_element' => '/<select.*multiple="multiple"/', + 'option_value_item' => '/<option value="%s" price="10" >%s \+\s{11}\$10.00.*/', + ], + ], + 'type_multiselect_not_required' => [ + [ + Option::KEY_TITLE => 'Test option multiselect title 2', + Option::KEY_TYPE => ProductCustomOptionInterface::OPTION_TYPE_MULTIPLE, + Option::KEY_IS_REQUIRE => 0, + ], + [ + Value::KEY_TITLE => 'Test option multiselect title 2 value 1', + Value::KEY_PRICE => 10, + Value::KEY_PRICE_TYPE => ProductPriceOptionsInterface::VALUE_FIXED, + Value::KEY_SKU => 'test-option-multiselect-title-2-value-1', + ], + [ + 'block_with_required_class' => '<div class="field">', + 'label_for_created_option' => '<label class="label" for="select_%s">', + 'title' => '<span>Test option multiselect title 2</span>', + 'required_element' => '/<select.*multiple="multiple"/', + 'option_value_item' => '/<option value="%s" price="10" >%s \+\s{11}\$10.00.*/', + ], + ], + 'type_multiselect_value_fixed_price' => [ + [ + Option::KEY_TITLE => 'Test option multiselect title 3', + Option::KEY_TYPE => ProductCustomOptionInterface::OPTION_TYPE_MULTIPLE, + Option::KEY_IS_REQUIRE => 0, + ], + [ + Value::KEY_TITLE => 'Test option multiselect title 3 value 1', + Value::KEY_PRICE => 50, + Value::KEY_PRICE_TYPE => ProductPriceOptionsInterface::VALUE_FIXED, + Value::KEY_SKU => 'test-option-multiselect-title-3-value-1', + ], + [ + 'block_with_required_class' => '<div class="field">', + 'label_for_created_option' => '<label class="label" for="select_%s">', + 'title' => '<span>Test option multiselect title 3</span>', + 'required_element' => '/<select.*multiple="multiple"/', + 'option_value_item' => '/<option value="%s" price="50" >%s \+\s{11}\$50.00.*/', + ], + ], + 'type_multiselect_value_percent_price' => [ + [ + Option::KEY_TITLE => 'Test option multiselect title 4', + Option::KEY_TYPE => ProductCustomOptionInterface::OPTION_TYPE_MULTIPLE, + Option::KEY_IS_REQUIRE => 0, + ], + [ + Value::KEY_TITLE => 'Test option multiselect title 4 value 1', + Value::KEY_PRICE => 50, + Value::KEY_PRICE_TYPE => ProductPriceOptionsInterface::VALUE_PERCENT, + Value::KEY_SKU => 'test-option-multiselect-title-4-value-1', + ], + [ + 'block_with_required_class' => '<div class="field">', + 'label_for_created_option' => '<label class="label" for="select_%s">', + 'title' => '<span>Test option multiselect title 4</span>', + 'required_element' => '/<select.*multiple="multiple"/', + 'option_value_item' => '/<option value="%s" price="5" >%s \+\s{11}\$5.00.*/', + ], + ], + ]; + } +} diff --git a/dev/tests/integration/framework/Magento/TestFramework/Catalog/Block/Product/View/Options/TextGroupDataProvider.php b/dev/tests/integration/framework/Magento/TestFramework/Catalog/Block/Product/View/Options/TextGroupDataProvider.php new file mode 100644 index 0000000000000..b0d22a222dd85 --- /dev/null +++ b/dev/tests/integration/framework/Magento/TestFramework/Catalog/Block/Product/View/Options/TextGroupDataProvider.php @@ -0,0 +1,213 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +declare(strict_types=1); + +namespace Magento\TestFramework\Catalog\Block\Product\View\Options; + +use Magento\Catalog\Api\Data\ProductCustomOptionInterface; +use Magento\Catalog\Model\Config\Source\ProductPriceOptionsInterface; +use Magento\Catalog\Model\Product\Option; + +/** + * Data provider with product custom options from text group(field, area). + */ +class TextGroupDataProvider +{ + /** + * Return options data. + * + * @SuppressWarnings(PHPMD.ExcessiveMethodLength) + * + * @return array + */ + public function getData(): array + { + return [ + 'type_field_required' => [ + [ + Option::KEY_TITLE => 'Test option field title 1', + Option::KEY_TYPE => ProductCustomOptionInterface::OPTION_TYPE_FIELD, + Option::KEY_IS_REQUIRE => 1, + Option::KEY_PRICE => 10, + Option::KEY_PRICE_TYPE => ProductPriceOptionsInterface::VALUE_FIXED, + Option::KEY_SKU => 'test-option-field-title-1', + Option::KEY_MAX_CHARACTERS => 0, + ], + [ + 'block_with_required_class' => '<div class="field required">', + 'label_for_created_option' => '<label class="label" for="options_%s_text">', + 'title' => '<span>Test option field title 1</span>', + 'price' => 'data-price-amount="10"', + 'required_element' => '/<input type="text"/', + ], + ], + 'type_field_not_required' => [ + [ + Option::KEY_TITLE => 'Test option field title 2', + Option::KEY_TYPE => ProductCustomOptionInterface::OPTION_TYPE_FIELD, + Option::KEY_IS_REQUIRE => 0, + Option::KEY_PRICE => 10, + Option::KEY_PRICE_TYPE => ProductPriceOptionsInterface::VALUE_FIXED, + Option::KEY_SKU => 'test-option-field-title-2', + Option::KEY_MAX_CHARACTERS => 0, + ], + [ + 'block_with_required_class' => '<div class="field">', + 'label_for_created_option' => '<label class="label" for="options_%s_text">', + 'title' => '<span>Test option field title 2</span>', + 'price' => 'data-price-amount="10"', + 'required_element' => '/<input type="text"/', + ], + ], + 'type_field_fixed_price' => [ + [ + Option::KEY_TITLE => 'Test option field title 3', + Option::KEY_TYPE => ProductCustomOptionInterface::OPTION_TYPE_FIELD, + Option::KEY_IS_REQUIRE => 0, + Option::KEY_PRICE => 50, + Option::KEY_PRICE_TYPE => ProductPriceOptionsInterface::VALUE_FIXED, + Option::KEY_SKU => 'test-option-field-title-3', + Option::KEY_MAX_CHARACTERS => 0, + ], + [ + 'block_with_required_class' => '<div class="field">', + 'label_for_created_option' => '<label class="label" for="options_%s_text">', + 'title' => '<span>Test option field title 3</span>', + 'price' => 'data-price-amount="50"', + 'required_element' => '/<input type="text"/', + ], + ], + 'type_field_percent_price' => [ + [ + Option::KEY_TITLE => 'Test option field title 4', + Option::KEY_TYPE => ProductCustomOptionInterface::OPTION_TYPE_FIELD, + Option::KEY_IS_REQUIRE => 0, + Option::KEY_PRICE => 50, + Option::KEY_PRICE_TYPE => ProductPriceOptionsInterface::VALUE_PERCENT, + Option::KEY_SKU => 'test-option-field-title-4', + Option::KEY_MAX_CHARACTERS => 0, + ], + [ + 'block_with_required_class' => '<div class="field">', + 'label_for_created_option' => '<label class="label" for="options_%s_text">', + 'title' => '<span>Test option field title 4</span>', + 'price' => 'data-price-amount="5"', + 'required_element' => '/<input type="text"/', + ], + ], + 'type_field_max_characters' => [ + [ + Option::KEY_TITLE => 'Test option field title 5', + Option::KEY_TYPE => ProductCustomOptionInterface::OPTION_TYPE_FIELD, + Option::KEY_IS_REQUIRE => 0, + Option::KEY_PRICE => 10, + Option::KEY_PRICE_TYPE => ProductPriceOptionsInterface::VALUE_FIXED, + Option::KEY_SKU => 'test-option-field-title-5', + Option::KEY_MAX_CHARACTERS => 99, + ], + [ + 'block_with_required_class' => '<div class="field">', + 'label_for_created_option' => '<label class="label" for="options_%s_text">', + 'title' => '<span>Test option field title 5</span>', + 'price' => 'data-price-amount="10"', + 'required_element' => '/<input type="text"/', + 'max_characters' => 'Maximum 99 characters', + ], + ], + 'type_area_required' => [ + [ + Option::KEY_TITLE => 'Test option area title 1', + Option::KEY_TYPE => ProductCustomOptionInterface::OPTION_TYPE_AREA, + Option::KEY_IS_REQUIRE => 1, + Option::KEY_PRICE => 10, + Option::KEY_PRICE_TYPE => ProductPriceOptionsInterface::VALUE_FIXED, + Option::KEY_SKU => 'test-option-area-title-1', + Option::KEY_MAX_CHARACTERS => 0, + ], + [ + 'block_with_required_class' => '<div class="field textarea required">', + 'label_for_created_option' => '<label class="label" for="options_%s_text">', + 'title' => '<span>Test option area title 1</span>', + 'price' => 'data-price-amount="10"', + 'required_element' => '/<textarea/', + ], + ], + 'type_area_not_required' => [ + [ + Option::KEY_TITLE => 'Test option area title 2', + Option::KEY_TYPE => ProductCustomOptionInterface::OPTION_TYPE_AREA, + Option::KEY_IS_REQUIRE => 0, + Option::KEY_PRICE => 10, + Option::KEY_PRICE_TYPE => ProductPriceOptionsInterface::VALUE_FIXED, + Option::KEY_SKU => 'test-option-area-title-2', + Option::KEY_MAX_CHARACTERS => 0, + ], + [ + 'block_with_required_class' => '<div class="field textarea">', + 'label_for_created_option' => '<label class="label" for="options_%s_text">', + 'title' => '<span>Test option area title 2</span>', + 'price' => 'data-price-amount="10"', + 'required_element' => '/<textarea/', + ], + ], + 'type_area_fixed_price' => [ + [ + Option::KEY_TITLE => 'Test option area title 3', + Option::KEY_TYPE => ProductCustomOptionInterface::OPTION_TYPE_AREA, + Option::KEY_IS_REQUIRE => 0, + Option::KEY_PRICE => 50, + Option::KEY_PRICE_TYPE => ProductPriceOptionsInterface::VALUE_FIXED, + Option::KEY_SKU => 'test-option-area-title-3', + Option::KEY_MAX_CHARACTERS => 0, + ], + [ + 'block_with_required_class' => '<div class="field textarea">', + 'label_for_created_option' => '<label class="label" for="options_%s_text">', + 'title' => '<span>Test option area title 3</span>', + 'price' => 'data-price-amount="50"', + 'required_element' => '/<textarea/', + ], + ], + 'type_area_percent_price' => [ + [ + Option::KEY_TITLE => 'Test option area title 4', + Option::KEY_TYPE => ProductCustomOptionInterface::OPTION_TYPE_AREA, + Option::KEY_IS_REQUIRE => 0, + Option::KEY_PRICE => 50, + Option::KEY_PRICE_TYPE => ProductPriceOptionsInterface::VALUE_PERCENT, + Option::KEY_SKU => 'test-option-area-title-4', + Option::KEY_MAX_CHARACTERS => 0, + ], + [ + 'block_with_required_class' => '<div class="field textarea">', + 'label_for_created_option' => '<label class="label" for="options_%s_text">', + 'title' => '<span>Test option area title 4</span>', + 'price' => 'data-price-amount="5"', + 'required_element' => '/<textarea/', + ], + ], + 'type_area_max_characters' => [ + [ + Option::KEY_TITLE => 'Test option area title 5', + Option::KEY_TYPE => ProductCustomOptionInterface::OPTION_TYPE_AREA, + Option::KEY_IS_REQUIRE => 0, + Option::KEY_PRICE => 10, + Option::KEY_PRICE_TYPE => ProductPriceOptionsInterface::VALUE_FIXED, + Option::KEY_SKU => 'test-option-area-title-5', + Option::KEY_MAX_CHARACTERS => 99, + ], + [ + 'block_with_required_class' => '<div class="field textarea">', + 'label_for_created_option' => '<label class="label" for="options_%s_text">', + 'title' => '<span>Test option area title 5</span>', + 'price' => 'data-price-amount="10"', + 'required_element' => '/<textarea/', + 'max_characters' => 'Maximum 99 characters', + ], + ], + ]; + } +} diff --git a/dev/tests/integration/framework/Magento/TestFramework/Catalog/Model/GetCategoryByName.php b/dev/tests/integration/framework/Magento/TestFramework/Catalog/Model/GetCategoryByName.php new file mode 100644 index 0000000000000..75b123e2dc906 --- /dev/null +++ b/dev/tests/integration/framework/Magento/TestFramework/Catalog/Model/GetCategoryByName.php @@ -0,0 +1,43 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +declare(strict_types=1); + +namespace Magento\TestFramework\Catalog\Model; + +use Magento\Catalog\Api\Data\CategoryInterface; +use Magento\Catalog\Model\ResourceModel\Category\CollectionFactory; + +/** + * Load category by category name + */ +class GetCategoryByName +{ + /** @var CollectionFactory */ + private $categoryCollectionFactory; + + /** + * @param CollectionFactory $categoryCollectionFactory + */ + public function __construct(CollectionFactory $categoryCollectionFactory) + { + $this->categoryCollectionFactory = $categoryCollectionFactory; + } + + /** + * Load category by name. + * + * @param string $categoryName + * @return CategoryInterface + */ + public function execute(string $categoryName): CategoryInterface + { + $categoryCollection = $this->categoryCollectionFactory->create(); + + return $categoryCollection->addAttributeToFilter(CategoryInterface::KEY_NAME, $categoryName) + ->setPageSize(1) + ->getFirstItem(); + } +} diff --git a/dev/tests/integration/framework/Magento/TestFramework/Catalog/Model/Layer/QuickSearchByQuery.php b/dev/tests/integration/framework/Magento/TestFramework/Catalog/Model/Layer/QuickSearchByQuery.php new file mode 100644 index 0000000000000..506aba9f6bd84 --- /dev/null +++ b/dev/tests/integration/framework/Magento/TestFramework/Catalog/Model/Layer/QuickSearchByQuery.php @@ -0,0 +1,51 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +declare(strict_types=1); + +namespace Magento\TestFramework\Catalog\Model\Layer; + +use Magento\Catalog\Model\Layer\SearchFactory; +use Magento\Catalog\Model\ResourceModel\Product\Collection; + +/** + * Quick search products by query. + */ +class QuickSearchByQuery +{ + /** + * @var SearchFactory + */ + private $searchFactory; + + /** + * @param SearchFactory $searchFactory + */ + public function __construct( + SearchFactory $searchFactory + ) { + $this->searchFactory = $searchFactory; + } + + /** + * Flush search instances cache and find products by search query. + * + * @param string $query + * @param string $sortedField + * @param string $sortOrder + * @return Collection + */ + public function execute( + string $query, + string $sortedField = 'relevance', + string $sortOrder = 'desc' + ): Collection { + $productCollection = $this->searchFactory->create()->getProductCollection(); + $productCollection->addSearchFilter($query); + $productCollection->setOrder($sortedField, $sortOrder); + + return $productCollection; + } +} diff --git a/dev/tests/integration/framework/Magento/TestFramework/Catalog/Model/Product/Attribute/DataProvider/Decimal.php b/dev/tests/integration/framework/Magento/TestFramework/Catalog/Model/Product/Attribute/DataProvider/Decimal.php new file mode 100644 index 0000000000000..ad72f2d197794 --- /dev/null +++ b/dev/tests/integration/framework/Magento/TestFramework/Catalog/Model/Product/Attribute/DataProvider/Decimal.php @@ -0,0 +1,134 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +declare(strict_types=1); + +namespace Magento\TestFramework\Catalog\Model\Product\Attribute\DataProvider; + +use Magento\Eav\Model\Entity\Attribute\ScopedAttributeInterface; +use Magento\TestFramework\Eav\Model\Attribute\DataProvider\AbstractBaseAttributeData; +use Magento\Store\Model\Store; +use Magento\Catalog\Model\Product\Attribute\Backend\Price as BackendPrice; + +/** + * Product attribute data for attribute with input type weee. + */ +class Decimal extends AbstractBaseAttributeData +{ + /** + * @inheritdoc + */ + public function __construct() + { + parent::__construct(); + $this->defaultAttributePostData['is_filterable'] = '0'; + $this->defaultAttributePostData['is_filterable_in_search'] = '0'; + $this->defaultAttributePostData['used_for_sort_by'] = '0'; + } + + /** + * @inheritdoc + */ + public function getAttributeData(): array + { + $result = parent::getAttributeData(); + unset($result["{$this->getFrontendInput()}_with_default_value"]); + unset($result["{$this->getFrontendInput()}_without_default_value"]); + + return $result; + } + + /** + * @inheritdoc + */ + public function getAttributeDataWithCheckArray(): array + { + $result = parent::getAttributeDataWithCheckArray(); + unset($result["{$this->getFrontendInput()}_with_default_value"]); + unset($result["{$this->getFrontendInput()}_without_default_value"]); + + return $result; + } + + /** + * @inheritdoc + */ + public function getUpdateProvider(): array + { + $frontendInput = $this->getFrontendInput(); + return array_replace_recursive( + parent::getUpdateProvider(), + [ + "{$frontendInput}_other_attribute_code" => [ + 'post_data' => [ + 'attribute_code' => 'text_attribute_update', + ], + 'expected_data' => [ + 'attribute_code' => 'decimal_attribute', + ], + ], + ] + ); + } + + /** + * @inheritdoc + */ + protected function getFrontendInput(): string + { + return 'price'; + } + + /** + * @inheritdoc + */ + protected function getUpdatePostData(): array + { + return [ + 'frontend_label' => [ + Store::DEFAULT_STORE_ID => 'Decimal Attribute Update', + ], + 'frontend_input' => 'price', + 'is_required' => '1', + 'is_unique' => '1', + 'is_used_in_grid' => '1', + 'is_visible_in_grid' => '1', + 'is_filterable_in_grid' => '1', + 'is_searchable' => '1', + 'search_weight' => '2', + 'is_visible_in_advanced_search' => '1', + 'is_comparable' => '1', + 'is_filterable' => '2', + 'is_filterable_in_search' => '1', + 'position' => '2', + 'is_used_for_promo_rules' => '1', + 'is_html_allowed_on_front' => '1', + 'is_visible_on_front' => '1', + 'used_in_product_listing' => '0', + 'used_for_sort_by' => '1', + ]; + } + + /** + * @inheritdoc + */ + protected function getUpdateExpectedData(): array + { + $updatePostData = $this->getUpdatePostData(); + return array_merge( + $updatePostData, + [ + 'frontend_label' => 'Decimal Attribute Update', + 'attribute_code' => 'decimal_attribute', + 'is_global' => ScopedAttributeInterface::SCOPE_GLOBAL, + 'default_value' => null, + 'frontend_class' => null, + 'is_user_defined' => '1', + 'backend_type' => 'decimal', + 'backend_model' => BackendPrice::class, + ] + ); + } +} diff --git a/dev/tests/integration/framework/Magento/TestFramework/Catalog/Model/Product/Attribute/DataProvider/MediaImage.php b/dev/tests/integration/framework/Magento/TestFramework/Catalog/Model/Product/Attribute/DataProvider/MediaImage.php index dd706ab29c326..7ae0169efa497 100644 --- a/dev/tests/integration/framework/Magento/TestFramework/Catalog/Model/Product/Attribute/DataProvider/MediaImage.php +++ b/dev/tests/integration/framework/Magento/TestFramework/Catalog/Model/Product/Attribute/DataProvider/MediaImage.php @@ -7,6 +7,9 @@ namespace Magento\TestFramework\Catalog\Model\Product\Attribute\DataProvider; +use Magento\Catalog\Model\Product\Attribute\Frontend\Image; +use Magento\Eav\Model\Entity\Attribute\ScopedAttributeInterface; +use Magento\Store\Model\Store; use Magento\TestFramework\Eav\Model\Attribute\DataProvider\AbstractBaseAttributeData; /** @@ -47,6 +50,27 @@ public function getAttributeDataWithCheckArray(): array return $result; } + /** + * @inheritdoc + */ + public function getUpdateProvider(): array + { + $frontendInput = $this->getFrontendInput(); + return array_replace_recursive( + parent::getUpdateProvider(), + [ + "{$frontendInput}_other_attribute_code" => [ + 'post_data' => [ + 'attribute_code' => 'text_attribute_update', + ], + 'expected_data' => [ + 'attribute_code' => 'image_attribute', + ], + ], + ] + ); + } + /** * @inheritdoc */ @@ -54,4 +78,55 @@ protected function getFrontendInput(): string { return 'media_image'; } + + /** + * @inheritdoc + */ + protected function getUpdatePostData(): array + { + return [ + 'frontend_label' => [ + Store::DEFAULT_STORE_ID => 'Decimal Attribute Update', + ], + 'frontend_input' => 'media_image', + 'is_global' => ScopedAttributeInterface::SCOPE_WEBSITE, + 'is_used_in_grid' => '1', + 'is_visible_in_grid' => '1', + 'is_filterable_in_grid' => '1', + ]; + } + + /** + * @inheritdoc + */ + protected function getUpdateExpectedData(): array + { + $updatePostData = $this->getUpdatePostData(); + return array_merge( + $updatePostData, + [ + 'frontend_label' => 'Decimal Attribute Update', + 'is_required' => '0', + 'attribute_code' => 'image_attribute', + 'default_value' => null, + 'is_unique' => '0', + 'frontend_class' => null, + 'is_searchable' => '0', + 'search_weight' => '1', + 'is_visible_in_advanced_search' => '0', + 'is_comparable' => '0', + 'is_filterable' => '0', + 'is_filterable_in_search' => '0', + 'position' => '0', + 'is_used_for_promo_rules' => '0', + 'is_html_allowed_on_front' => '1', + 'is_visible_on_front' => '0', + 'used_in_product_listing' => '1', + 'used_for_sort_by' => '0', + 'is_user_defined' => '1', + 'backend_type' => 'varchar', + 'frontend_model' => Image::class, + ] + ); + } } diff --git a/dev/tests/integration/framework/Magento/TestFramework/Catalog/Model/Product/Attribute/DataProvider/Price.php b/dev/tests/integration/framework/Magento/TestFramework/Catalog/Model/Product/Attribute/DataProvider/Price.php deleted file mode 100644 index 04ee6bb0a5740..0000000000000 --- a/dev/tests/integration/framework/Magento/TestFramework/Catalog/Model/Product/Attribute/DataProvider/Price.php +++ /dev/null @@ -1,59 +0,0 @@ -<?php -/** - * Copyright © Magento, Inc. All rights reserved. - * See COPYING.txt for license details. - */ -declare(strict_types=1); - -namespace Magento\TestFramework\Catalog\Model\Product\Attribute\DataProvider; - -use Magento\TestFramework\Eav\Model\Attribute\DataProvider\AbstractBaseAttributeData; - -/** - * Product attribute data for attribute with input type weee. - */ -class Price extends AbstractBaseAttributeData -{ - /** - * @inheritdoc - */ - public function __construct() - { - parent::__construct(); - $this->defaultAttributePostData['is_filterable'] = '0'; - $this->defaultAttributePostData['is_filterable_in_search'] = '0'; - $this->defaultAttributePostData['used_for_sort_by'] = '0'; - } - - /** - * @inheritdoc - */ - public function getAttributeData(): array - { - $result = parent::getAttributeData(); - unset($result["{$this->getFrontendInput()}_with_default_value"]); - unset($result["{$this->getFrontendInput()}_without_default_value"]); - - return $result; - } - - /** - * @inheritdoc - */ - public function getAttributeDataWithCheckArray(): array - { - $result = parent::getAttributeDataWithCheckArray(); - unset($result["{$this->getFrontendInput()}_with_default_value"]); - unset($result["{$this->getFrontendInput()}_without_default_value"]); - - return $result; - } - - /** - * @inheritdoc - */ - protected function getFrontendInput(): string - { - return 'price'; - } -} diff --git a/dev/tests/integration/framework/Magento/TestFramework/Catalog/Model/Product/Option/DataProvider/Type/File.php b/dev/tests/integration/framework/Magento/TestFramework/Catalog/Model/Product/Option/DataProvider/Type/File.php index 35f449a404410..d2aa20a005ec4 100644 --- a/dev/tests/integration/framework/Magento/TestFramework/Catalog/Model/Product/Option/DataProvider/Type/File.php +++ b/dev/tests/integration/framework/Magento/TestFramework/Catalog/Model/Product/Option/DataProvider/Type/File.php @@ -8,7 +8,7 @@ namespace Magento\TestFramework\Catalog\Model\Product\Option\DataProvider\Type; use Magento\Catalog\Api\Data\ProductCustomOptionInterface; -use Magento\TestFramework\Catalog\Model\Product\Option\DataProvider\Type\AbstractBase; +use Magento\Catalog\Model\Product\Option; /** * Data provider for options from file group with type "file". @@ -20,41 +20,44 @@ class File extends AbstractBase */ public function getDataForCreateOptions(): array { - return array_merge_recursive( - parent::getDataForCreateOptions(), - [ - "type_{$this->getType()}_option_file_extension" => [ - [ - 'record_id' => 0, - 'sort_order' => 1, - 'is_require' => 1, - 'sku' => 'test-option-title-1', - 'max_characters' => 30, - 'title' => 'Test option title 1', - 'type' => $this->getType(), - 'price' => 10, - 'price_type' => 'fixed', - 'file_extension' => 'gif', - 'image_size_x' => 10, - 'image_size_y' => 20, + return $this->injectFileExtension( + array_merge_recursive( + parent::getDataForCreateOptions(), + [ + "type_{$this->getType()}_option_file_extension" => [ + [ + 'record_id' => 0, + 'sort_order' => 1, + 'is_require' => 1, + 'sku' => 'test-option-title-1', + 'max_characters' => 30, + 'title' => 'Test option title 1', + 'type' => $this->getType(), + 'price' => 10, + 'price_type' => 'fixed', + 'file_extension' => 'gif', + 'image_size_x' => 10, + 'image_size_y' => 20, + ], ], - ], - "type_{$this->getType()}_option_maximum_file_size" => [ - [ - 'record_id' => 0, - 'sort_order' => 1, - 'is_require' => 1, - 'sku' => 'test-option-title-1', - 'title' => 'Test option title 1', - 'type' => $this->getType(), - 'price' => 10, - 'price_type' => 'fixed', - 'file_extension' => 'gif', - 'image_size_x' => 10, - 'image_size_y' => 20, + "type_{$this->getType()}_option_maximum_file_size" => [ + [ + 'record_id' => 0, + 'sort_order' => 1, + 'is_require' => 1, + 'sku' => 'test-option-title-1', + 'title' => 'Test option title 1', + 'type' => $this->getType(), + 'price' => 10, + 'price_type' => 'fixed', + 'file_extension' => 'gif', + 'image_size_x' => 10, + 'image_size_y' => 20, + ], ], - ], - ] + ] + ), + 'png' ); } @@ -63,21 +66,24 @@ public function getDataForCreateOptions(): array */ public function getDataForUpdateOptions(): array { - return array_merge_recursive( - parent::getDataForUpdateOptions(), - [ - "type_{$this->getType()}_option_file_extension" => [ - [ - 'file_extension' => 'jpg', + return $this->injectFileExtension( + array_merge_recursive( + parent::getDataForUpdateOptions(), + [ + "type_{$this->getType()}_option_file_extension" => [ + [ + 'file_extension' => 'jpg', + ], ], - ], - "type_{$this->getType()}_option_maximum_file_size" => [ - [ - 'image_size_x' => 300, - 'image_size_y' => 815, + "type_{$this->getType()}_option_maximum_file_size" => [ + [ + 'image_size_x' => 300, + 'image_size_y' => 815, + ], ], - ], - ] + ] + ), + '' ); } @@ -88,4 +94,24 @@ protected function getType(): string { return ProductCustomOptionInterface::OPTION_TYPE_FILE; } + + /** + * Add 'file_extension' value to each option. + * + * @param array $data + * @param string $extension + * @return array + */ + private function injectFileExtension(array $data, string $extension): array + { + foreach ($data as &$caseData) { + foreach ($caseData as &$option) { + if (!isset($option[Option::KEY_FILE_EXTENSION])) { + $option[Option::KEY_FILE_EXTENSION] = $extension; + } + } + } + + return $data; + } } diff --git a/dev/tests/integration/framework/Magento/TestFramework/Catalog/Model/Product/Price/GetPriceIndexDataByProductId.php b/dev/tests/integration/framework/Magento/TestFramework/Catalog/Model/Product/Price/GetPriceIndexDataByProductId.php new file mode 100644 index 0000000000000..b1794c55d06f3 --- /dev/null +++ b/dev/tests/integration/framework/Magento/TestFramework/Catalog/Model/Product/Price/GetPriceIndexDataByProductId.php @@ -0,0 +1,77 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +declare(strict_types=1); + +namespace Magento\TestFramework\Catalog\Model\Product\Price; + +use Magento\Catalog\Model\Indexer\Product\Price\PriceTableResolver; +use Magento\Catalog\Model\ResourceModel\Product as ProductResource; +use Magento\Customer\Model\Indexer\CustomerGroupDimensionProvider; +use Magento\Framework\Indexer\DimensionFactory; +use Magento\Store\Model\Indexer\WebsiteDimensionProvider; + +/** + * Search and return price data from price index table. + */ +class GetPriceIndexDataByProductId +{ + /** + * @var ProductResource + */ + private $productResource; + + /** + * @var PriceTableResolver + */ + private $priceTableResolver; + + /** + * @var DimensionFactory + */ + private $dimensionFactory; + + /** + * @param ProductResource $productResource + * @param PriceTableResolver $priceTableResolver + * @param DimensionFactory $dimensionFactory + */ + public function __construct( + ProductResource $productResource, + PriceTableResolver $priceTableResolver, + DimensionFactory $dimensionFactory + ) { + $this->productResource = $productResource; + $this->priceTableResolver = $priceTableResolver; + $this->dimensionFactory = $dimensionFactory; + } + + /** + * Returns price data by product id. + * + * @param int $productId + * @param int $groupId + * @param int $websiteId + * @return array + */ + public function execute(int $productId, int $groupId, int $websiteId): array + { + $tableName = $this->priceTableResolver->resolve( + 'catalog_product_index_price', + [ + $this->dimensionFactory->create(WebsiteDimensionProvider::DIMENSION_NAME, (string)$websiteId), + $this->dimensionFactory->create(CustomerGroupDimensionProvider::DIMENSION_NAME, (string)$groupId), + ] + ); + + $select = $this->productResource->getConnection()->select() + ->from($tableName) + ->where('entity_id = ?', $productId) + ->where('customer_group_id = ?', $groupId) + ->where('website_id = ?', $websiteId); + + return $this->productResource->getConnection()->fetchAll($select); + } +} diff --git a/dev/tests/integration/framework/Magento/TestFramework/ConfigurableProduct/Block/CustomOptions/DateGroupDataProvider.php b/dev/tests/integration/framework/Magento/TestFramework/ConfigurableProduct/Block/CustomOptions/DateGroupDataProvider.php new file mode 100644 index 0000000000000..a03d3912dbd06 --- /dev/null +++ b/dev/tests/integration/framework/Magento/TestFramework/ConfigurableProduct/Block/CustomOptions/DateGroupDataProvider.php @@ -0,0 +1,31 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +declare(strict_types=1); + +namespace Magento\TestFramework\ConfigurableProduct\Block\CustomOptions; + +use Magento\TestFramework\Catalog\Block\Product\View\Options\DateGroupDataProvider as OptionsDateGroupDataProvider; + +/** + * @inheritdoc + */ +class DateGroupDataProvider extends OptionsDateGroupDataProvider +{ + /** + * @inheritdoc + */ + public function getData(): array + { + $optionsData = parent::getData(); + unset( + $optionsData['type_date_percent_price'], + $optionsData['type_date_and_time_percent_price'], + $optionsData['type_time_percent_price'] + ); + + return $optionsData; + } +} diff --git a/dev/tests/integration/framework/Magento/TestFramework/ConfigurableProduct/Block/CustomOptions/FileGroupDataProvider.php b/dev/tests/integration/framework/Magento/TestFramework/ConfigurableProduct/Block/CustomOptions/FileGroupDataProvider.php new file mode 100644 index 0000000000000..e0a22341a7ca0 --- /dev/null +++ b/dev/tests/integration/framework/Magento/TestFramework/ConfigurableProduct/Block/CustomOptions/FileGroupDataProvider.php @@ -0,0 +1,27 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +declare(strict_types=1); + +namespace Magento\TestFramework\ConfigurableProduct\Block\CustomOptions; + +use Magento\TestFramework\Catalog\Block\Product\View\Options\FileGroupDataProvider as OptionsFileGroupDataProvider; + +/** + * @inheritdoc + */ +class FileGroupDataProvider extends OptionsFileGroupDataProvider +{ + /** + * @inheritdoc + */ + public function getData(): array + { + $optionsData = parent::getData(); + unset($optionsData['type_file_percent_price']); + + return $optionsData; + } +} diff --git a/dev/tests/integration/framework/Magento/TestFramework/ConfigurableProduct/Block/CustomOptions/SelectGroupDataProvider.php b/dev/tests/integration/framework/Magento/TestFramework/ConfigurableProduct/Block/CustomOptions/SelectGroupDataProvider.php new file mode 100644 index 0000000000000..89a94f423d052 --- /dev/null +++ b/dev/tests/integration/framework/Magento/TestFramework/ConfigurableProduct/Block/CustomOptions/SelectGroupDataProvider.php @@ -0,0 +1,32 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +declare(strict_types=1); + +namespace Magento\TestFramework\ConfigurableProduct\Block\CustomOptions; + +use Magento\TestFramework\Catalog\Block\Product\View\Options\SelectGroupDataProvider as OptionsSelectGroupDataProvider; + +/** + * @inheritdoc + */ +class SelectGroupDataProvider extends OptionsSelectGroupDataProvider +{ + /** + * @inheritdoc + */ + public function getData(): array + { + $optionsData = parent::getData(); + unset( + $optionsData['type_drop_down_value_percent_price'], + $optionsData['type_radio_button_value_percent_price'], + $optionsData['type_checkbox_value_percent_price'], + $optionsData['type_multiselect_value_percent_price'] + ); + + return $optionsData; + } +} diff --git a/dev/tests/integration/framework/Magento/TestFramework/ConfigurableProduct/Block/CustomOptions/TextGroupDataProvider.php b/dev/tests/integration/framework/Magento/TestFramework/ConfigurableProduct/Block/CustomOptions/TextGroupDataProvider.php new file mode 100644 index 0000000000000..cf442a978b09a --- /dev/null +++ b/dev/tests/integration/framework/Magento/TestFramework/ConfigurableProduct/Block/CustomOptions/TextGroupDataProvider.php @@ -0,0 +1,30 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +declare(strict_types=1); + +namespace Magento\TestFramework\ConfigurableProduct\Block\CustomOptions; + +use Magento\TestFramework\Catalog\Block\Product\View\Options\TextGroupDataProvider as OptionsTextGroupDataProvider; + +/** + * @inheritdoc + */ +class TextGroupDataProvider extends OptionsTextGroupDataProvider +{ + /** + * @inheritdoc + */ + public function getData(): array + { + $optionsData = parent::getData(); + unset( + $optionsData['type_field_percent_price'], + $optionsData['type_area_percent_price'] + ); + + return $optionsData; + } +} diff --git a/dev/tests/integration/framework/Magento/TestFramework/ConfigurableProduct/Model/DeleteConfigurableProduct.php b/dev/tests/integration/framework/Magento/TestFramework/ConfigurableProduct/Model/DeleteConfigurableProduct.php new file mode 100644 index 0000000000000..3414efa1e30ed --- /dev/null +++ b/dev/tests/integration/framework/Magento/TestFramework/ConfigurableProduct/Model/DeleteConfigurableProduct.php @@ -0,0 +1,70 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +declare(strict_types=1); + +namespace Magento\TestFramework\ConfigurableProduct\Model; + +use Magento\Catalog\Api\ProductRepositoryInterface; +use Magento\Catalog\Model\ResourceModel\Product as ProductResource; +use Magento\Framework\Exception\NoSuchEntityException; +use Magento\Framework\Registry; + +/** + * Delete configurable product with linked products + */ +class DeleteConfigurableProduct +{ + /** @var ProductRepositoryInterface */ + private $productRepository; + + /** @var ProductResource */ + private $productResource; + + /** @var Registry */ + private $registry; + + /** + * @param ProductRepositoryInterface $productRepository + * @param ProductResource $productResource + * @param Registry $registry + */ + public function __construct( + ProductRepositoryInterface $productRepository, + ProductResource $productResource, + Registry $registry + ) { + $this->productRepository = $productRepository; + $this->productResource = $productResource; + $this->registry = $registry; + } + + /** + * Delete configurable product and linked products + * + * @param string $sku + * @return void + */ + public function execute(string $sku): void + { + $configurableProduct = $this->productRepository->get($sku, false, null, true); + $childrenIds = $configurableProduct->getExtensionAttributes()->getConfigurableProductLinks(); + $childrenSkus = array_column($this->productResource->getProductsSku($childrenIds), 'sku'); + $childrenSkus[] = $sku; + $this->registry->unregister('isSecureArea'); + $this->registry->register('isSecureArea', true); + + foreach ($childrenSkus as $childSku) { + try { + $this->productRepository->deleteById($childSku); + } catch (NoSuchEntityException $e) { + //product already removed + } + } + + $this->registry->unregister('isSecureArea'); + $this->registry->register('isSecureArea', false); + } +} diff --git a/dev/tests/integration/framework/Magento/TestFramework/Directory/Model/GetRegionIdByName.php b/dev/tests/integration/framework/Magento/TestFramework/Directory/Model/GetRegionIdByName.php new file mode 100644 index 0000000000000..f1e98cd4ea0bf --- /dev/null +++ b/dev/tests/integration/framework/Magento/TestFramework/Directory/Model/GetRegionIdByName.php @@ -0,0 +1,54 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +declare(strict_types=1); + +namespace Magento\TestFramework\Directory\Model; + +use Magento\Directory\Model\RegionFactory; + +/** + * Return region ID by region default name and country code. + */ +class GetRegionIdByName +{ + /** + * @var RegionFactory + */ + private $regionFactory; + + /** + * @var array + */ + private $regionIdsCache; + + /** + * @param RegionFactory $regionFactory + */ + public function __construct( + RegionFactory $regionFactory + ) { + $this->regionFactory = $regionFactory; + } + + /** + * Get region ID from cache property if region id exist or load it. + * + * @param string $regionName + * @param string $countryId + * @return int|null + */ + public function execute(string $regionName, string $countryId): ?int + { + $cacheKey = "{$regionName}_{$countryId}"; + + if (!isset($this->regionIdsCache[$cacheKey])) { + $region = $this->regionFactory->create()->loadByName($regionName, $countryId); + $this->regionIdsCache[$cacheKey] = $region->getRegionId() ? (int)$region->getRegionId() : null; + } + + return $this->regionIdsCache[$cacheKey]; + } +} diff --git a/dev/tests/integration/framework/Magento/TestFramework/Directory/Model/RemoveCurrencyRateByCode.php b/dev/tests/integration/framework/Magento/TestFramework/Directory/Model/RemoveCurrencyRateByCode.php new file mode 100644 index 0000000000000..86895045db945 --- /dev/null +++ b/dev/tests/integration/framework/Magento/TestFramework/Directory/Model/RemoveCurrencyRateByCode.php @@ -0,0 +1,40 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +declare(strict_types=1); + +namespace Magento\TestFramework\Directory\Model; + +use Magento\Directory\Model\ResourceModel\Currency; + +/** + * Remove currency rates by currency code + */ +class RemoveCurrencyRateByCode +{ + /** @var Currency */ + private $currencyResource; + + /** + * @param Currency $currencyResource + */ + public function __construct(Currency $currencyResource) + { + $this->currencyResource = $currencyResource; + } + + /** + * Remove currency rates + * + * @param string $currencyCode + * @return void + */ + public function execute(string $currencyCode): void + { + $connection = $this->currencyResource->getConnection(); + $rateTable = $this->currencyResource->getTable('directory_currency_rate'); + $connection->delete($rateTable, $connection->quoteInto('currency_to = ? OR currency_from = ?', $currencyCode)); + } +} diff --git a/dev/tests/integration/framework/Magento/TestFramework/Eav/Model/Attribute/DataProvider/AbstractAttributeDataWithOptions.php b/dev/tests/integration/framework/Magento/TestFramework/Eav/Model/Attribute/DataProvider/AbstractAttributeDataWithOptions.php index 8f25651e2e036..affd063d99ab5 100644 --- a/dev/tests/integration/framework/Magento/TestFramework/Eav/Model/Attribute/DataProvider/AbstractAttributeDataWithOptions.php +++ b/dev/tests/integration/framework/Magento/TestFramework/Eav/Model/Attribute/DataProvider/AbstractAttributeDataWithOptions.php @@ -7,6 +7,8 @@ namespace Magento\TestFramework\Eav\Model\Attribute\DataProvider; +use Magento\Store\Model\Store; + /** * Base POST data for create attribute with options. */ @@ -72,6 +74,67 @@ public function getAttributeDataWithCheckArray(): array return $result; } + /** + * Return product attribute data set for update attribute options. + * + * @return array + */ + public function getUpdateOptionsProvider(): array + { + $frontendInput = $this->getFrontendInput(); + return [ + "{$frontendInput}_update_options" => [ + 'post_data' => [ + 'options_array' => [ + 'option_1' => [ + 'order' => '5', + 'value' => [ + Store::DEFAULT_STORE_ID => 'Option 1 Admin', + 'default' => 'Option 1 Store 1', + 'fixture_second_store' => 'Option 1 Store 2', + 'fixture_third_store' => 'Option 1 Store 3', + ], + 'delete' => '', + ], + 'option_2' => [ + 'order' => '6', + 'value' => [ + Store::DEFAULT_STORE_ID => 'Option 2 Admin', + 'default' => 'Option 2 Store 1', + 'fixture_second_store' => 'Option 2 Store 2', + 'fixture_third_store' => 'Option 2 Store 3', + ], + 'delete' => '', + 'default' => 1, + ], + ], + ], + ], + "{$frontendInput}_delete_options" => [ + 'post_data' => [ + 'options_array' => [ + 'option_1' => [ + 'value' => [], + 'delete' => '', + ], + 'option_2' => [ + 'value' => [], + 'delete' => '1', + ], + 'option_3' => [ + 'value' => [], + 'delete' => '', + ], + 'option_4' => [ + 'value' => [], + 'delete' => '1', + ], + ], + ], + ], + ]; + } + /** * Return attribute options data. * diff --git a/dev/tests/integration/framework/Magento/TestFramework/Eav/Model/Attribute/DataProvider/AbstractBaseAttributeData.php b/dev/tests/integration/framework/Magento/TestFramework/Eav/Model/Attribute/DataProvider/AbstractBaseAttributeData.php index af9e58d02fb5a..cd93e57a849cf 100644 --- a/dev/tests/integration/framework/Magento/TestFramework/Eav/Model/Attribute/DataProvider/AbstractBaseAttributeData.php +++ b/dev/tests/integration/framework/Magento/TestFramework/Eav/Model/Attribute/DataProvider/AbstractBaseAttributeData.php @@ -8,6 +8,7 @@ namespace Magento\TestFramework\Eav\Model\Attribute\DataProvider; use Magento\Store\Model\Store; +use Magento\Eav\Model\Entity\Attribute\ScopedAttributeInterface; /** * Base POST data for create attribute. @@ -28,7 +29,7 @@ abstract class AbstractBaseAttributeData 'dropdown_attribute_validation' => '', 'dropdown_attribute_validation_unique' => '', 'attribute_code' => '', - 'is_global' => '0', + 'is_global' => ScopedAttributeInterface::SCOPE_STORE, 'default_value_text' => '', 'default_value_yesno' => '0', 'default_value_date' => '', @@ -68,10 +69,10 @@ public function getAttributeData(): array $this->defaultAttributePostData, ], "{$this->getFrontendInput()}_with_global_scope" => [ - array_merge($this->defaultAttributePostData, ['is_global' => '1']), + array_merge($this->defaultAttributePostData, ['is_global' => ScopedAttributeInterface::SCOPE_GLOBAL]), ], "{$this->getFrontendInput()}_with_website_scope" => [ - array_merge($this->defaultAttributePostData, ['is_global' => '2']), + array_merge($this->defaultAttributePostData, ['is_global' => ScopedAttributeInterface::SCOPE_WEBSITE]), ], "{$this->getFrontendInput()}_with_attribute_code" => [ array_merge($this->defaultAttributePostData, ['attribute_code' => 'test_custom_attribute_code']), @@ -143,19 +144,19 @@ public function getAttributeDataWithCheckArray(): array "{$this->getFrontendInput()}_with_store_view_scope" => [ [ 'attribute_code' => 'test_attribute_name', - 'is_global' => '0', + 'is_global' => ScopedAttributeInterface::SCOPE_STORE, ], ], "{$this->getFrontendInput()}_with_global_scope" => [ [ 'attribute_code' => 'test_attribute_name', - 'is_global' => '1', + 'is_global' => ScopedAttributeInterface::SCOPE_GLOBAL, ], ], "{$this->getFrontendInput()}_with_website_scope" => [ [ 'attribute_code' => 'test_attribute_name', - 'is_global' => '2', + 'is_global' => ScopedAttributeInterface::SCOPE_WEBSITE, ], ], "{$this->getFrontendInput()}_with_attribute_code" => [ @@ -215,10 +216,173 @@ public function getAttributeDataWithCheckArray(): array ); } + /** + * Return product attribute data set for update attribute. + * + * @return array + */ + public function getUpdateProvider(): array + { + $frontendInput = $this->getFrontendInput(); + return [ + "{$frontendInput}_update_all_fields" => [ + 'post_data' => $this->getUpdatePostData(), + 'expected_data' => $this->getUpdateExpectedData(), + ], + "{$frontendInput}_other_is_user_defined" => [ + 'post_data' => [ + 'is_user_defined' => '2', + ], + 'expected_data' => [ + 'is_user_defined' => '1', + ], + ], + "{$frontendInput}_with_is_global_null" => [ + 'post_data' => [ + 'is_global' => null, + ], + 'expected_data' => [ + 'is_global' => ScopedAttributeInterface::SCOPE_GLOBAL, + ], + ], + "{$frontendInput}_is_visible_in_advanced_search" => [ + 'post_data' => [ + 'is_searchable' => '0', + 'is_visible_in_advanced_search' => '1', + ], + 'expected_data' => [ + 'is_searchable' => '0', + 'is_visible_in_advanced_search' => '0', + ], + ], + "{$frontendInput}_update_with_attribute_set" => [ + 'post_data' => [ + 'set' => '4', + 'new_attribute_set_name' => 'Text Attribute Set', + 'group' => 'text_attribute_group', + 'groupName' => 'Text Attribute Group', + 'groupSortOrder' => '1', + ], + 'expected_data' => [], + ], + ]; + } + + /** + * Return product attribute data set with error message for update attribute. + * + * @return array + */ + public function getUpdateProviderWithErrorMessage(): array + { + $frontendInput = $this->getFrontendInput(); + return [ + "{$frontendInput}_same_attribute_set_name" => [ + 'post_data' => [ + 'set' => '4', + 'new_attribute_set_name' => 'Default', + ], + 'error_message' => (string)__('An attribute set named \'Default\' already exists.'), + ], + "{$frontendInput}_empty_set_id" => [ + 'post_data' => [ + 'set' => '', + 'new_attribute_set_name' => 'Text Attribute Set', + ], + 'error_message' => (string)__('Something went wrong while saving the attribute.'), + ], + "{$frontendInput}_nonexistent_attribute_id" => [ + 'post_data' => [ + 'attribute_id' => 9999, + ], + 'error_message' => (string)__('This attribute no longer exists.'), + ], + "{$frontendInput}_attribute_other_entity_type" => [ + 'post_data' => [ + 'attribute_id' => 45, + ], + 'error_message' => (string)__('We can\'t update the attribute.'), + ], + ]; + } + + /** + * Return product attribute data set for update attribute frontend labels. + * + * @return array + */ + public function getUpdateFrontendLabelsProvider(): array + { + $frontendInput = $this->getFrontendInput(); + return [ + "{$frontendInput}_update_frontend_label" => [ + 'post_data' => [ + 'frontend_label' => [ + Store::DEFAULT_STORE_ID => 'Test Attribute Update', + 'default' => 'Default Store Update', + 'fixture_second_store' => 'Second Store Update', + 'fixture_third_store' => 'Third Store Update', + ] + ], + 'expected_data' => [ + 'frontend_label' => 'Test Attribute Update', + 'store_labels' => [ + 'default' => 'Default Store Update', + 'fixture_second_store' => 'Second Store Update', + 'fixture_third_store' => 'Third Store Update', + ], + ], + ], + "{$frontendInput}_remove_frontend_label" => [ + 'post_data' => [ + 'frontend_label' => [ + Store::DEFAULT_STORE_ID => 'Test Attribute Update', + 'default' => 'Default Store Update', + 'fixture_second_store' => '', + 'fixture_third_store' => '', + ] + ], + 'expected_data' => [ + 'frontend_label' => 'Test Attribute Update', + 'store_labels' => [ + 'default' => 'Default Store Update', + ], + ], + ], + "{$frontendInput}_with_frontend_label_string" => [ + 'post_data' => [ + 'frontend_label' => 'Test Attribute Update', + ], + 'expected_data' => [ + 'frontend_label' => 'Test Attribute Update', + 'store_labels' => [ + 'default' => 'Default Store View', + 'fixture_second_store' => 'Fixture Second Store', + 'fixture_third_store' => 'Fixture Third Store', + ], + ], + ], + ]; + } + /** * Return attribute frontend input. * * @return string */ abstract protected function getFrontendInput(): string; + + /** + * Return post data for attribute update. + * + * @return array + */ + abstract protected function getUpdatePostData(): array; + + /** + * Return expected data for attribute update. + * + * @return array + */ + abstract protected function getUpdateExpectedData(): array; } diff --git a/dev/tests/integration/framework/Magento/TestFramework/Eav/Model/Attribute/DataProvider/Date.php b/dev/tests/integration/framework/Magento/TestFramework/Eav/Model/Attribute/DataProvider/Date.php index 7a6f8ee41c1f8..c4e34ef8984a8 100644 --- a/dev/tests/integration/framework/Magento/TestFramework/Eav/Model/Attribute/DataProvider/Date.php +++ b/dev/tests/integration/framework/Magento/TestFramework/Eav/Model/Attribute/DataProvider/Date.php @@ -7,6 +7,9 @@ namespace Magento\TestFramework\Eav\Model\Attribute\DataProvider; +use Magento\Eav\Model\Entity\Attribute\ScopedAttributeInterface; +use Magento\Store\Model\Store; + /** * Product attribute data for attribute with input type date. */ @@ -56,6 +59,46 @@ public function getAttributeDataWithCheckArray(): array ); } + /** + * @inheritdoc + */ + public function getUpdateProvider(): array + { + $frontendInput = $this->getFrontendInput(); + return array_replace_recursive( + parent::getUpdateProvider(), + [ + "{$frontendInput}_other_attribute_code" => [ + 'post_data' => [ + 'attribute_code' => 'text_attribute_update', + ], + 'expected_data' => [ + 'attribute_code' => 'date_attribute', + ], + ], + ] + ); + } + + /** + * @inheritdoc + */ + public function getUpdateProviderWithErrorMessage(): array + { + $frontendInput = $this->getFrontendInput(); + return array_replace_recursive( + parent::getUpdateProviderWithErrorMessage(), + [ + "{$frontendInput}_wrong_default_value" => [ + 'post_data' => [ + 'default_value_date' => '//2019/12/12', + ], + 'error_message' => (string)__('The default date is invalid. Verify the date and try again.'), + ], + ] + ); + } + /** * @inheritdoc */ @@ -63,4 +106,56 @@ protected function getFrontendInput(): string { return 'date'; } + + /** + * @inheritdoc + */ + protected function getUpdatePostData(): array + { + return [ + 'frontend_label' => [ + Store::DEFAULT_STORE_ID => 'Date Attribute Update', + ], + 'frontend_input' => 'date', + 'is_required' => '1', + 'is_global' => ScopedAttributeInterface::SCOPE_WEBSITE, + 'default_value_date' => '12/29/2019', + 'is_unique' => '1', + 'is_used_in_grid' => '1', + 'is_visible_in_grid' => '1', + 'is_filterable_in_grid' => '1', + 'is_searchable' => '1', + 'search_weight' => '2', + 'is_visible_in_advanced_search' => '1', + 'is_comparable' => '1', + 'is_used_for_promo_rules' => '1', + 'is_html_allowed_on_front' => '1', + 'is_visible_on_front' => '1', + 'used_in_product_listing' => '0', + 'used_for_sort_by' => '1', + ]; + } + + /** + * @inheritdoc + */ + protected function getUpdateExpectedData(): array + { + $updatePostData = $this->getUpdatePostData(); + unset($updatePostData['default_value_date']); + return array_merge( + $updatePostData, + [ + 'frontend_label' => 'Date Attribute Update', + 'attribute_code' => 'date_attribute', + 'default_value' => '2019-12-29 00:00:00', + 'frontend_class' => null, + 'is_filterable' => '0', + 'is_filterable_in_search' => '0', + 'position' => '0', + 'is_user_defined' => '1', + 'backend_type' => 'datetime', + ] + ); + } } diff --git a/dev/tests/integration/framework/Magento/TestFramework/Eav/Model/Attribute/DataProvider/DateTime.php b/dev/tests/integration/framework/Magento/TestFramework/Eav/Model/Attribute/DataProvider/DateTime.php new file mode 100644 index 0000000000000..70a8dc670e6d3 --- /dev/null +++ b/dev/tests/integration/framework/Magento/TestFramework/Eav/Model/Attribute/DataProvider/DateTime.php @@ -0,0 +1,161 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +declare(strict_types=1); + +namespace Magento\TestFramework\Eav\Model\Attribute\DataProvider; + +use Magento\Eav\Model\Entity\Attribute\ScopedAttributeInterface; +use Magento\Store\Model\Store; + +/** + * Product attribute data for attribute with input type datetime. + */ +class DateTime extends AbstractBaseAttributeData +{ + /** + * @inheritdoc + */ + public function __construct() + { + parent::__construct(); + $this->defaultAttributePostData['used_for_sort_by'] = '0'; + } + + /** + * @inheritdoc + */ + public function getAttributeData(): array + { + return array_replace_recursive( + parent::getAttributeData(), + [ + "{$this->getFrontendInput()}_with_default_value" => [ + [ + 'default_value_text' => '', + 'default_value_datetime' => '02/4/2020 6:30 AM', + ] + ] + ] + ); + } + + /** + * @inheritdoc + */ + public function getAttributeDataWithCheckArray(): array + { + return array_replace_recursive( + parent::getAttributeDataWithCheckArray(), + [ + "{$this->getFrontendInput()}_with_default_value" => [ + 1 => [ + 'default_value' => '2020-02-04 06:30:00', + ], + ], + ] + ); + } + + /** + * @inheritdoc + */ + public function getUpdateProvider(): array + { + $frontendInput = $this->getFrontendInput(); + return array_replace_recursive( + parent::getUpdateProvider(), + [ + "{$frontendInput}_other_attribute_code" => [ + 'post_data' => [ + 'attribute_code' => 'text_attribute_update', + ], + 'expected_data' => [ + 'attribute_code' => 'datetime_attribute', + ], + ], + ] + ); + } + + /** + * @inheritdoc + */ + public function getUpdateProviderWithErrorMessage(): array + { + $frontendInput = $this->getFrontendInput(); + return array_replace_recursive( + parent::getUpdateProviderWithErrorMessage(), + [ + "{$frontendInput}_wrong_default_value" => [ + 'post_data' => [ + 'default_value_datetime' => '//02/4/2020 6:30 AM', + ], + 'error_message' => (string)__('The default date is invalid. Verify the date and try again.'), + ], + ] + ); + } + + /** + * @inheritdoc + */ + protected function getFrontendInput(): string + { + return 'datetime'; + } + + /** + * @inheritdoc + */ + protected function getUpdatePostData(): array + { + return [ + 'frontend_label' => [ + Store::DEFAULT_STORE_ID => 'Date Time Attribute Update', + ], + 'frontend_input' => 'datetime', + 'is_required' => '1', + 'is_global' => ScopedAttributeInterface::SCOPE_WEBSITE, + 'default_value_datetime' => '02/4/2020 6:30 AM', + 'is_unique' => '1', + 'is_used_in_grid' => '1', + 'is_visible_in_grid' => '1', + 'is_filterable_in_grid' => '1', + 'is_searchable' => '1', + 'search_weight' => '2', + 'is_visible_in_advanced_search' => '1', + 'is_comparable' => '1', + 'is_used_for_promo_rules' => '1', + 'is_html_allowed_on_front' => '1', + 'is_visible_on_front' => '1', + 'used_in_product_listing' => '0', + 'used_for_sort_by' => '1', + ]; + } + + /** + * @inheritdoc + */ + protected function getUpdateExpectedData(): array + { + $updatePostData = $this->getUpdatePostData(); + unset($updatePostData['default_value_datetime']); + return array_merge( + $updatePostData, + [ + 'frontend_label' => 'Date Time Attribute Update', + 'attribute_code' => 'datetime_attribute', + 'default_value' => '2020-02-04 06:30:00', + 'frontend_class' => null, + 'is_filterable' => '0', + 'is_filterable_in_search' => '0', + 'position' => '0', + 'is_user_defined' => '1', + 'backend_type' => 'datetime', + ] + ); + } +} diff --git a/dev/tests/integration/framework/Magento/TestFramework/Eav/Model/Attribute/DataProvider/DropDown.php b/dev/tests/integration/framework/Magento/TestFramework/Eav/Model/Attribute/DataProvider/DropDown.php index 3c1acb5a33a54..8366b13760795 100644 --- a/dev/tests/integration/framework/Magento/TestFramework/Eav/Model/Attribute/DataProvider/DropDown.php +++ b/dev/tests/integration/framework/Magento/TestFramework/Eav/Model/Attribute/DataProvider/DropDown.php @@ -7,6 +7,9 @@ namespace Magento\TestFramework\Eav\Model\Attribute\DataProvider; +use Magento\Eav\Model\Entity\Attribute\ScopedAttributeInterface; +use Magento\Store\Model\Store; + /** * Product attribute data for attribute with input type dropdown. */ @@ -22,6 +25,27 @@ public function __construct() $this->defaultAttributePostData['swatch_input_type'] = 'dropdown'; } + /** + * @inheritdoc + */ + public function getUpdateProvider(): array + { + $frontendInput = $this->getFrontendInput(); + return array_replace_recursive( + parent::getUpdateProvider(), + [ + "{$frontendInput}_other_attribute_code" => [ + 'post_data' => [ + 'attribute_code' => 'text_attribute_update', + ], + 'expected_data' => [ + 'attribute_code' => 'dropdown_attribute', + ], + ], + ] + ); + } + /** * @inheritdoc */ @@ -29,4 +53,54 @@ protected function getFrontendInput(): string { return 'select'; } + + /** + * @inheritdoc + */ + protected function getUpdatePostData(): array + { + return [ + 'frontend_label' => [ + Store::DEFAULT_STORE_ID => 'Drop-Down Attribute Update', + ], + 'frontend_input' => 'select', + 'is_required' => '1', + 'is_global' => ScopedAttributeInterface::SCOPE_WEBSITE, + 'is_unique' => '1', + 'is_used_in_grid' => '1', + 'is_visible_in_grid' => '1', + 'is_filterable_in_grid' => '1', + 'is_searchable' => '1', + 'search_weight' => '2', + 'is_visible_in_advanced_search' => '1', + 'is_comparable' => '1', + 'is_filterable' => '2', + 'is_filterable_in_search' => '1', + 'position' => '2', + 'is_used_for_promo_rules' => '1', + 'is_html_allowed_on_front' => '0', + 'is_visible_on_front' => '0', + 'used_in_product_listing' => '0', + 'used_for_sort_by' => '1', + ]; + } + + /** + * @inheritdoc + */ + protected function getUpdateExpectedData(): array + { + $updatePostData = $this->getUpdatePostData(); + return array_merge( + $updatePostData, + [ + 'frontend_label' => 'Drop-Down Attribute Update', + 'attribute_code' => 'dropdown_attribute', + 'default_value' => null, + 'frontend_class' => null, + 'is_user_defined' => '1', + 'backend_type' => 'varchar', + ] + ); + } } diff --git a/dev/tests/integration/framework/Magento/TestFramework/Eav/Model/Attribute/DataProvider/MultipleSelect.php b/dev/tests/integration/framework/Magento/TestFramework/Eav/Model/Attribute/DataProvider/MultipleSelect.php index 5fb5f745aebdc..4d72f5b316ea0 100644 --- a/dev/tests/integration/framework/Magento/TestFramework/Eav/Model/Attribute/DataProvider/MultipleSelect.php +++ b/dev/tests/integration/framework/Magento/TestFramework/Eav/Model/Attribute/DataProvider/MultipleSelect.php @@ -7,11 +7,35 @@ namespace Magento\TestFramework\Eav\Model\Attribute\DataProvider; +use Magento\Eav\Model\Entity\Attribute\ScopedAttributeInterface; +use Magento\Store\Model\Store; + /** * Product attribute data for attribute with input type multiple select. */ class MultipleSelect extends AbstractAttributeDataWithOptions { + /** + * @inheritdoc + */ + public function getUpdateProvider(): array + { + $frontendInput = $this->getFrontendInput(); + return array_replace_recursive( + parent::getUpdateProvider(), + [ + "{$frontendInput}_other_attribute_code" => [ + 'post_data' => [ + 'attribute_code' => 'text_attribute_update', + ], + 'expected_data' => [ + 'attribute_code' => 'multiselect_attribute', + ], + ], + ] + ); + } + /** * @inheritdoc */ @@ -19,4 +43,54 @@ protected function getFrontendInput(): string { return 'multiselect'; } + + /** + * @inheritdoc + */ + protected function getUpdatePostData(): array + { + return [ + 'frontend_label' => [ + Store::DEFAULT_STORE_ID => 'Multiselect Attribute Update', + ], + 'frontend_input' => 'multiselect', + 'is_required' => '1', + 'is_global' => ScopedAttributeInterface::SCOPE_WEBSITE, + 'is_unique' => '1', + 'is_used_in_grid' => '1', + 'is_visible_in_grid' => '1', + 'is_filterable_in_grid' => '1', + 'is_searchable' => '1', + 'search_weight' => '2', + 'is_visible_in_advanced_search' => '1', + 'is_comparable' => '1', + 'is_filterable' => '2', + 'is_filterable_in_search' => '1', + 'position' => '2', + 'is_used_for_promo_rules' => '1', + 'is_html_allowed_on_front' => '0', + 'is_visible_on_front' => '1', + 'used_in_product_listing' => '1', + ]; + } + + /** + * @inheritdoc + */ + protected function getUpdateExpectedData(): array + { + $updatePostData = $this->getUpdatePostData(); + return array_merge( + $updatePostData, + [ + 'frontend_label' => 'Multiselect Attribute Update', + 'attribute_code' => 'multiselect_attribute', + 'default_value' => null, + 'frontend_class' => null, + 'used_for_sort_by' => '0', + 'is_user_defined' => '1', + 'backend_type' => 'varchar', + ] + ); + } } diff --git a/dev/tests/integration/framework/Magento/TestFramework/Eav/Model/Attribute/DataProvider/Text.php b/dev/tests/integration/framework/Magento/TestFramework/Eav/Model/Attribute/DataProvider/Text.php index 5b37248e27361..654e31a0f4528 100644 --- a/dev/tests/integration/framework/Magento/TestFramework/Eav/Model/Attribute/DataProvider/Text.php +++ b/dev/tests/integration/framework/Magento/TestFramework/Eav/Model/Attribute/DataProvider/Text.php @@ -7,6 +7,9 @@ namespace Magento\TestFramework\Eav\Model\Attribute\DataProvider; +use Magento\Eav\Model\Entity\Attribute\ScopedAttributeInterface; +use Magento\Store\Model\Store; + /** * Product attribute data for attribute with input type text. */ @@ -64,6 +67,27 @@ public function getAttributeDataWithCheckArray(): array ); } + /** + * @inheritdoc + */ + public function getUpdateProvider(): array + { + $frontendInput = $this->getFrontendInput(); + return array_replace_recursive( + parent::getUpdateProvider(), + [ + "{$frontendInput}_other_attribute_code" => [ + 'post_data' => [ + 'attribute_code' => 'varchar_attribute_update', + ], + 'expected_data' => [ + 'attribute_code' => 'varchar_attribute', + ], + ], + ] + ); + } + /** * @inheritdoc */ @@ -71,4 +95,56 @@ protected function getFrontendInput(): string { return 'text'; } + + /** + * @inheritdoc + */ + protected function getUpdatePostData(): array + { + return [ + 'frontend_label' => [ + Store::DEFAULT_STORE_ID => 'Varchar Attribute Update', + ], + 'is_required' => '1', + 'is_global' => ScopedAttributeInterface::SCOPE_WEBSITE, + 'default_value_text' => 'Varchar Attribute Default', + 'is_unique' => '1', + 'frontend_class' => 'validate-alphanum', + 'is_used_in_grid' => '1', + 'is_visible_in_grid' => '1', + 'is_filterable_in_grid' => '1', + 'is_searchable' => '1', + 'search_weight' => '2', + 'is_visible_in_advanced_search' => '1', + 'is_comparable' => '1', + 'is_used_for_promo_rules' => '1', + 'is_html_allowed_on_front' => '0', + 'is_visible_on_front' => '1', + 'used_in_product_listing' => '0', + 'used_for_sort_by' => '1', + ]; + } + + /** + * @inheritdoc + */ + protected function getUpdateExpectedData(): array + { + $updatePostData = $this->getUpdatePostData(); + unset($updatePostData['default_value_text']); + return array_merge( + $updatePostData, + [ + 'frontend_label' => 'Varchar Attribute Update', + 'frontend_input' => 'text', + 'attribute_code' => 'varchar_attribute', + 'default_value' => 'Varchar Attribute Default', + 'is_filterable' => '0', + 'is_filterable_in_search' => '0', + 'position' => '0', + 'is_user_defined' => '1', + 'backend_type' => 'varchar', + ] + ); + } } diff --git a/dev/tests/integration/framework/Magento/TestFramework/Eav/Model/Attribute/DataProvider/TextArea.php b/dev/tests/integration/framework/Magento/TestFramework/Eav/Model/Attribute/DataProvider/TextArea.php index 7588b12700272..2b9414fe01390 100644 --- a/dev/tests/integration/framework/Magento/TestFramework/Eav/Model/Attribute/DataProvider/TextArea.php +++ b/dev/tests/integration/framework/Magento/TestFramework/Eav/Model/Attribute/DataProvider/TextArea.php @@ -7,6 +7,9 @@ namespace Magento\TestFramework\Eav\Model\Attribute\DataProvider; +use Magento\Eav\Model\Entity\Attribute\ScopedAttributeInterface; +use Magento\Store\Model\Store; + /** * Product attribute data for attribute with text area input type. */ @@ -30,6 +33,36 @@ public function getAttributeData(): array ); } + /** + * @inheritdoc + */ + public function getUpdateProvider(): array + { + $frontendInput = $this->getFrontendInput(); + return array_replace_recursive( + parent::getUpdateProvider(), + [ + "{$frontendInput}_other_attribute_code" => [ + 'post_data' => [ + 'attribute_code' => 'text_attribute_update', + ], + 'expected_data' => [ + 'attribute_code' => 'text_attribute', + ], + ], + "{$frontendInput}_change_frontend_input" => [ + 'post_data' => [ + 'frontend_input' => 'texteditor', + ], + 'expected_data' => [ + 'frontend_input' => 'textarea', + 'is_wysiwyg_enabled' => '1' + ], + ], + ] + ); + } + /** * @inheritdoc */ @@ -37,4 +70,57 @@ protected function getFrontendInput(): string { return 'textarea'; } + + /** + * @inheritdoc + */ + protected function getUpdatePostData(): array + { + return [ + 'frontend_label' => [ + Store::DEFAULT_STORE_ID => 'Text Attribute Update', + ], + 'frontend_input' => 'textarea', + 'is_required' => '1', + 'is_global' => ScopedAttributeInterface::SCOPE_WEBSITE, + 'default_value_textarea' => 'Text Attribute Default', + 'is_unique' => '1', + 'is_used_in_grid' => '1', + 'is_visible_in_grid' => '1', + 'is_filterable_in_grid' => '1', + 'is_searchable' => '1', + 'search_weight' => '2', + 'is_visible_in_advanced_search' => '1', + 'is_comparable' => '1', + 'is_used_for_promo_rules' => '1', + 'is_html_allowed_on_front' => '1', + 'is_visible_on_front' => '1', + 'used_in_product_listing' => '1', + ]; + } + + /** + * @inheritdoc + */ + protected function getUpdateExpectedData(): array + { + $updatePostData = $this->getUpdatePostData(); + unset($updatePostData['default_value_textarea']); + return array_merge( + $updatePostData, + [ + 'frontend_label' => 'Text Attribute Update', + 'attribute_code' => 'text_attribute', + 'default_value' => 'Text Attribute Default', + 'frontend_class' => null, + 'is_filterable' => '0', + 'is_filterable_in_search' => '0', + 'position' => '0', + 'used_for_sort_by' => '0', + 'is_user_defined' => '1', + 'backend_type' => 'text', + 'is_wysiwyg_enabled' => '0', + ] + ); + } } diff --git a/dev/tests/integration/framework/Magento/TestFramework/Eav/Model/Attribute/DataProvider/TextEditor.php b/dev/tests/integration/framework/Magento/TestFramework/Eav/Model/Attribute/DataProvider/TextEditor.php index d7a6276c1720f..282031e4377a5 100644 --- a/dev/tests/integration/framework/Magento/TestFramework/Eav/Model/Attribute/DataProvider/TextEditor.php +++ b/dev/tests/integration/framework/Magento/TestFramework/Eav/Model/Attribute/DataProvider/TextEditor.php @@ -7,6 +7,9 @@ namespace Magento\TestFramework\Eav\Model\Attribute\DataProvider; +use Magento\Eav\Model\Entity\Attribute\ScopedAttributeInterface; +use Magento\Store\Model\Store; + /** * Product attribute data for attribute with text editor input type. */ @@ -116,6 +119,36 @@ public function getAttributeDataWithCheckArray(): array ); } + /** + * @inheritdoc + */ + public function getUpdateProvider(): array + { + $frontendInput = $this->getFrontendInput(); + return array_replace_recursive( + parent::getUpdateProvider(), + [ + "{$frontendInput}_other_attribute_code" => [ + 'post_data' => [ + 'attribute_code' => 'text_attribute_update', + ], + 'expected_data' => [ + 'attribute_code' => 'text_editor_attribute', + ], + ], + "{$frontendInput}_change_frontend_input" => [ + 'post_data' => [ + 'frontend_input' => 'textarea', + ], + 'expected_data' => [ + 'frontend_input' => 'textarea', + 'is_wysiwyg_enabled' => '0' + ], + ], + ] + ); + } + /** * @inheritdoc */ @@ -123,4 +156,58 @@ protected function getFrontendInput(): string { return 'texteditor'; } + + /** + * @inheritdoc + */ + protected function getUpdatePostData(): array + { + return [ + 'frontend_label' => [ + Store::DEFAULT_STORE_ID => 'Text Editor Attribute Update', + ], + 'frontend_input' => 'texteditor', + 'is_required' => '1', + 'is_global' => ScopedAttributeInterface::SCOPE_WEBSITE, + 'default_value_textarea' => 'Text Editor Attribute Default', + 'is_unique' => '1', + 'is_used_in_grid' => '1', + 'is_visible_in_grid' => '1', + 'is_filterable_in_grid' => '1', + 'is_searchable' => '1', + 'search_weight' => '2', + 'is_visible_in_advanced_search' => '1', + 'is_comparable' => '1', + 'is_used_for_promo_rules' => '1', + 'is_html_allowed_on_front' => '1', + 'is_visible_on_front' => '1', + 'used_in_product_listing' => '1', + 'used_for_sort_by' => '1', + ]; + } + + /** + * @inheritdoc + */ + protected function getUpdateExpectedData(): array + { + $updatePostData = $this->getUpdatePostData(); + unset($updatePostData['default_value_textarea']); + return array_merge( + $updatePostData, + [ + 'frontend_label' => 'Text Editor Attribute Update', + 'frontend_input' => 'textarea', + 'attribute_code' => 'text_editor_attribute', + 'default_value' => 'Text Editor Attribute Default', + 'frontend_class' => null, + 'is_filterable' => '0', + 'is_filterable_in_search' => '0', + 'position' => '0', + 'is_user_defined' => '1', + 'backend_type' => 'text', + 'is_wysiwyg_enabled' => '1', + ] + ); + } } diff --git a/dev/tests/integration/framework/Magento/TestFramework/Eav/Model/Attribute/DataProvider/YesNo.php b/dev/tests/integration/framework/Magento/TestFramework/Eav/Model/Attribute/DataProvider/YesNo.php index 8fece70f0273c..28428b38be009 100644 --- a/dev/tests/integration/framework/Magento/TestFramework/Eav/Model/Attribute/DataProvider/YesNo.php +++ b/dev/tests/integration/framework/Magento/TestFramework/Eav/Model/Attribute/DataProvider/YesNo.php @@ -7,6 +7,9 @@ namespace Magento\TestFramework\Eav\Model\Attribute\DataProvider; +use Magento\Eav\Model\Entity\Attribute\ScopedAttributeInterface; +use Magento\Store\Model\Store; + /** * Product attribute data for attribute with yes/no input type. */ @@ -58,6 +61,27 @@ public function getAttributeDataWithCheckArray(): array ); } + /** + * @inheritdoc + */ + public function getUpdateProvider(): array + { + $frontendInput = $this->getFrontendInput(); + return array_replace_recursive( + parent::getUpdateProvider(), + [ + "{$frontendInput}_other_attribute_code" => [ + 'post_data' => [ + 'attribute_code' => 'text_attribute_update', + ], + 'expected_data' => [ + 'attribute_code' => 'boolean_attribute', + ], + ], + ] + ); + } + /** * @inheritdoc */ @@ -65,4 +89,56 @@ protected function getFrontendInput(): string { return 'boolean'; } + + /** + * @inheritdoc + */ + protected function getUpdatePostData(): array + { + return [ + 'frontend_label' => [ + Store::DEFAULT_STORE_ID => 'Boolean Attribute Update', + ], + 'frontend_input' => 'boolean', + 'is_required' => '1', + 'is_global' => ScopedAttributeInterface::SCOPE_WEBSITE, + 'default_value_yesno' => '1', + 'is_unique' => '1', + 'is_used_in_grid' => '1', + 'is_visible_in_grid' => '1', + 'is_filterable_in_grid' => '1', + 'is_searchable' => '1', + 'search_weight' => '2', + 'is_visible_in_advanced_search' => '0', + 'is_comparable' => '1', + 'is_filterable' => '2', + 'is_filterable_in_search' => '0', + 'position' => '2', + 'is_used_for_promo_rules' => '1', + 'is_html_allowed_on_front' => '0', + 'is_visible_on_front' => '0', + 'used_in_product_listing' => '0', + 'used_for_sort_by' => '1', + ]; + } + + /** + * @inheritdoc + */ + protected function getUpdateExpectedData(): array + { + $updatePostData = $this->getUpdatePostData(); + unset($updatePostData['default_value_yesno']); + return array_merge( + $updatePostData, + [ + 'frontend_label' => 'Boolean Attribute Update', + 'attribute_code' => 'boolean_attribute', + 'default_value' => '1', + 'frontend_class' => null, + 'is_user_defined' => '1', + 'backend_type' => 'int', + ] + ); + } } diff --git a/dev/tests/integration/framework/Magento/TestFramework/Eav/Model/ResourceModel/GetEntityIdByAttributeId.php b/dev/tests/integration/framework/Magento/TestFramework/Eav/Model/ResourceModel/GetEntityIdByAttributeId.php index edb75a5d8d1bd..76235b3392684 100644 --- a/dev/tests/integration/framework/Magento/TestFramework/Eav/Model/ResourceModel/GetEntityIdByAttributeId.php +++ b/dev/tests/integration/framework/Magento/TestFramework/Eav/Model/ResourceModel/GetEntityIdByAttributeId.php @@ -33,16 +33,24 @@ public function __construct( * * @param int $setId * @param int $attributeId + * @param int|null $attributeGroupId * @return int|null */ - public function execute(int $setId, int $attributeId): ?int + public function execute(int $setId, int $attributeId, ?int $attributeGroupId = null): ?int { $select = $this->attributeSetResource->getConnection()->select() - ->from($this->attributeSetResource->getTable('eav_entity_attribute')) + ->from( + $this->attributeSetResource->getTable('eav_entity_attribute'), + 'entity_attribute_id' + ) ->where('attribute_set_id = ?', $setId) ->where('attribute_id = ?', $attributeId); - $result = $this->attributeSetResource->getConnection()->fetchOne($select); - return $result ? (int)$result : null; + if ($attributeGroupId !== null) { + $select->where('attribute_group_id = ?', $attributeGroupId); + } + $entityAttributeId = $this->attributeSetResource->getConnection()->fetchOne($select); + + return $entityAttributeId ? (int)$entityAttributeId : null; } } diff --git a/dev/tests/integration/framework/Magento/TestFramework/Event/Transaction.php b/dev/tests/integration/framework/Magento/TestFramework/Event/Transaction.php index 9ab6d67014129..2add2ed48fb98 100644 --- a/dev/tests/integration/framework/Magento/TestFramework/Event/Transaction.php +++ b/dev/tests/integration/framework/Magento/TestFramework/Event/Transaction.php @@ -4,11 +4,11 @@ * See COPYING.txt for license details. */ +namespace Magento\TestFramework\Event; + /** * Database transaction events manager */ -namespace Magento\TestFramework\Event; - class Transaction { /** @@ -86,6 +86,7 @@ protected function _processTransactionRequests($eventName, \PHPUnit\Framework\Te * Start transaction and fire 'startTransaction' event * * @param \PHPUnit\Framework\TestCase $test + * @SuppressWarnings(PHPMD.UnusedLocalVariable) */ protected function _startTransaction(\PHPUnit\Framework\TestCase $test) { @@ -93,7 +94,21 @@ protected function _startTransaction(\PHPUnit\Framework\TestCase $test) $this->_getConnection()->beginTransparentTransaction(); $this->_isTransactionActive = true; try { + /** + * Add any warning during transaction execution as a failure. + */ + set_error_handler( + function ($errNo, $errStr, $errFile, $errLine) use ($test) { + $errMsg = sprintf("%s: %s in %s:%s.", "Warning", $errStr, $errFile, $errLine); + $test->getTestResultObject()->addError($test, new \PHPUnit\Framework\Warning($errMsg), 0); + + // Allow error to be handled by next error handler + return false; + }, + E_WARNING + ); $this->_eventManager->fireEvent('startTransaction', [$test]); + restore_error_handler(); } catch (\Exception $e) { $test->getTestResultObject()->addFailure( $test, diff --git a/dev/tests/integration/framework/Magento/TestFramework/Request.php b/dev/tests/integration/framework/Magento/TestFramework/Request.php index 756badb0f333f..b6afc6e4c4ae3 100644 --- a/dev/tests/integration/framework/Magento/TestFramework/Request.php +++ b/dev/tests/integration/framework/Magento/TestFramework/Request.php @@ -5,10 +5,11 @@ */ namespace Magento\TestFramework; -use \Zend\Stdlib\ParametersInterface; +use \Laminas\Stdlib\ParametersInterface; /** * HTTP request implementation that is used instead core one for testing + * @SuppressWarnings(PHPMD.CookieAndSessionMisuse) */ class Request extends \Magento\Framework\App\Request\Http { @@ -21,6 +22,7 @@ class Request extends \Magento\Framework\App\Request\Http /** * Retrieve HTTP HOST. + * * This method is a stub - all parameters are ignored, just static value returned. * * @param bool $trimPort diff --git a/dev/tests/integration/framework/Magento/TestFramework/SendFriend/Model/DeleteLogRowsByIp.php b/dev/tests/integration/framework/Magento/TestFramework/SendFriend/Model/DeleteLogRowsByIp.php new file mode 100644 index 0000000000000..aecf40b575957 --- /dev/null +++ b/dev/tests/integration/framework/Magento/TestFramework/SendFriend/Model/DeleteLogRowsByIp.php @@ -0,0 +1,40 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +declare(strict_types=1); + +namespace Magento\TestFramework\SendFriend\Model; + +use Magento\SendFriend\Model\ResourceModel\SendFriend as SendFriendResource; + +/** + * Delete log rows by ip address + */ +class DeleteLogRowsByIp +{ + /** @var SendFriendResource */ + private $sendFriendResource; + + /** + * @param SendFriendResource $sendFriendResource + */ + public function __construct(SendFriendResource $sendFriendResource) + { + $this->sendFriendResource = $sendFriendResource; + } + + /** + * Delete rows from sendfriend_log table by ip address + * + * @param string $ipAddress + * @return void + */ + public function execute(string $ipAddress): void + { + $connection = $this->sendFriendResource->getConnection(); + $condition = $connection->quoteInto('ip = ?', ip2long($ipAddress)); + $connection->delete($this->sendFriendResource->getMainTable(), $condition); + } +} diff --git a/dev/tests/integration/framework/Magento/TestFramework/Store/ExecuteInStoreContext.php b/dev/tests/integration/framework/Magento/TestFramework/Store/ExecuteInStoreContext.php new file mode 100644 index 0000000000000..eee7b81e8bd32 --- /dev/null +++ b/dev/tests/integration/framework/Magento/TestFramework/Store/ExecuteInStoreContext.php @@ -0,0 +1,56 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +declare(strict_types=1); + +namespace Magento\TestFramework\Store; + +use Magento\Store\Api\Data\StoreInterface; +use Magento\Store\Model\StoreManagerInterface; + +/** + * Execute operation in specified store + */ +class ExecuteInStoreContext +{ + /** @var StoreManagerInterface */ + private $storeManager; + + /** + * @param StoreManagerInterface $storeManager + */ + public function __construct(StoreManagerInterface $storeManager) + { + $this->storeManager = $storeManager; + } + + /** + * Execute callback in store context + * + * @param null|string|bool|int|StoreInterface $store + * @param callable $method + * @param array $arguments + * @return mixed + */ + public function execute($store, callable $method, ...$arguments) + { + $storeCode = $store instanceof StoreInterface + ? $store->getCode() + : $this->storeManager->getStore($store)->getCode(); + $currentStore = $this->storeManager->getStore(); + + try { + if ($currentStore->getCode() !== $storeCode) { + $this->storeManager->setCurrentStore($storeCode); + } + + return $method(...array_values($arguments)); + } finally { + if ($currentStore->getCode() !== $storeCode) { + $this->storeManager->setCurrentStore($currentStore); + } + } + } +} diff --git a/dev/tests/integration/framework/Magento/TestFramework/Swatches/Model/Attribute/DataProvider/TextSwatch.php b/dev/tests/integration/framework/Magento/TestFramework/Swatches/Model/Attribute/DataProvider/TextSwatch.php index c63873469e2f8..bb705b5503c39 100644 --- a/dev/tests/integration/framework/Magento/TestFramework/Swatches/Model/Attribute/DataProvider/TextSwatch.php +++ b/dev/tests/integration/framework/Magento/TestFramework/Swatches/Model/Attribute/DataProvider/TextSwatch.php @@ -7,7 +7,9 @@ namespace Magento\TestFramework\Swatches\Model\Attribute\DataProvider; +use Magento\Eav\Model\Entity\Attribute\ScopedAttributeInterface; use Magento\Swatches\Model\Swatch; +use Magento\Store\Model\Store; /** * Product attribute data for attribute with input type visual swatch. @@ -90,6 +92,88 @@ public function getAttributeDataWithCheckArray(): array ); } + /** + * @inheritdoc + */ + public function getUpdateProvider(): array + { + $frontendInput = $this->getFrontendInput(); + return array_replace_recursive( + parent::getUpdateProvider(), + [ + "{$frontendInput}_other_attribute_code" => [ + 'post_data' => [ + 'attribute_code' => 'text_attribute_update', + ], + 'expected_data' => [ + 'attribute_code' => 'text_swatch_attribute', + ], + ], + "{$frontendInput}_change_frontend_input_swatch_visual" => [ + 'post_data' => [ + 'frontend_input' => Swatch::SWATCH_TYPE_VISUAL_ATTRIBUTE_FRONTEND_INPUT, + 'update_product_preview_image' => '1', + 'use_product_image_for_swatch' => '1', + ], + 'expected_data' => [ + 'frontend_input' => 'select', + 'swatch_input_type' => Swatch::SWATCH_INPUT_TYPE_VISUAL, + 'update_product_preview_image' => '1', + 'use_product_image_for_swatch' => '1', + ], + ], + "{$frontendInput}_change_frontend_input_dropdown" => [ + 'post_data' => [ + 'frontend_input' => 'select', + ], + 'expected_data' => [ + 'frontend_input' => 'select', + 'swatch_input_type' => null, + 'update_product_preview_image' => null, + 'use_product_image_for_swatch' => null, + ], + ], + ] + ); + } + + /** + * @inheritdoc + */ + public function getUpdateOptionsProvider(): array + { + $frontendInput = $this->getFrontendInput(); + return array_replace_recursive( + parent::getUpdateOptionsProvider(), + [ + "{$frontendInput}_update_options" => [ + 'post_data' => [ + 'options_array' => [ + 'option_1' => [ + 'order' => '4', + 'swatch' => [ + Store::DEFAULT_STORE_ID => 'Swatch 1 Admin', + 'default' => 'Swatch 1 Store 1', + 'fixture_second_store' => 'Swatch 1 Store 2', + 'fixture_third_store' => 'Swatch 1 Store 3', + ], + ], + 'option_2' => [ + 'order' => '5', + 'swatch' => [ + Store::DEFAULT_STORE_ID => 'Swatch 2 Admin', + 'default' => 'Swatch 2 Store 1', + 'fixture_second_store' => 'Swatch 2 Store 2', + 'fixture_third_store' => 'Swatch 2 Store 3', + ], + ], + ], + ], + ], + ] + ); + } + /** * @inheritdoc */ @@ -157,4 +241,56 @@ protected function getFrontendInput(): string { return Swatch::SWATCH_TYPE_TEXTUAL_ATTRIBUTE_FRONTEND_INPUT; } + + /** + * @inheritdoc + */ + protected function getUpdatePostData(): array + { + return [ + 'frontend_label' => [ + Store::DEFAULT_STORE_ID => 'Text swatch attribute Update', + ], + 'frontend_input' => Swatch::SWATCH_TYPE_TEXTUAL_ATTRIBUTE_FRONTEND_INPUT, + 'is_required' => '1', + 'update_product_preview_image' => '1', + 'is_global' => ScopedAttributeInterface::SCOPE_WEBSITE, + 'is_unique' => '1', + 'is_used_in_grid' => '1', + 'is_visible_in_grid' => '1', + 'is_filterable_in_grid' => '1', + 'is_searchable' => '1', + 'search_weight' => '2', + 'is_visible_in_advanced_search' => '1', + 'is_comparable' => '1', + 'is_filterable' => '2', + 'is_filterable_in_search' => '1', + 'position' => '2', + 'is_used_for_promo_rules' => '1', + 'is_visible_on_front' => '1', + 'used_in_product_listing' => '0', + 'used_for_sort_by' => '1', + ]; + } + + /** + * @inheritdoc + */ + protected function getUpdateExpectedData(): array + { + $updatePostData = $this->getUpdatePostData(); + return array_merge( + $updatePostData, + [ + 'frontend_label' => 'Text swatch attribute Update', + 'frontend_input' => 'select', + 'attribute_code' => 'text_swatch_attribute', + 'default_value' => null, + 'frontend_class' => null, + 'is_html_allowed_on_front' => '1', + 'is_user_defined' => '1', + 'backend_type' => 'int', + ] + ); + } } diff --git a/dev/tests/integration/framework/Magento/TestFramework/Swatches/Model/Attribute/DataProvider/VisualSwatch.php b/dev/tests/integration/framework/Magento/TestFramework/Swatches/Model/Attribute/DataProvider/VisualSwatch.php index b5e32c40ef8a1..c5195d7d1d1a4 100644 --- a/dev/tests/integration/framework/Magento/TestFramework/Swatches/Model/Attribute/DataProvider/VisualSwatch.php +++ b/dev/tests/integration/framework/Magento/TestFramework/Swatches/Model/Attribute/DataProvider/VisualSwatch.php @@ -7,7 +7,9 @@ namespace Magento\TestFramework\Swatches\Model\Attribute\DataProvider; +use Magento\Eav\Model\Entity\Attribute\ScopedAttributeInterface; use Magento\Swatches\Model\Swatch; +use Magento\Store\Model\Store; /** * Product attribute data for attribute with input type visual swatch. @@ -90,6 +92,81 @@ public function getAttributeDataWithCheckArray(): array ); } + /** + * @inheritdoc + */ + public function getUpdateProvider(): array + { + $frontendInput = $this->getFrontendInput(); + return array_replace_recursive( + parent::getUpdateProvider(), + [ + "{$frontendInput}_other_attribute_code" => [ + 'post_data' => [ + 'attribute_code' => 'text_attribute_update', + ], + 'expected_data' => [ + 'attribute_code' => 'visual_swatch_attribute', + ], + ], + "{$frontendInput}_change_frontend_input_swatch_text" => [ + 'post_data' => [ + 'frontend_input' => Swatch::SWATCH_TYPE_TEXTUAL_ATTRIBUTE_FRONTEND_INPUT, + 'update_product_preview_image' => '1', + ], + 'expected_data' => [ + 'frontend_input' => 'select', + 'swatch_input_type' => Swatch::SWATCH_INPUT_TYPE_TEXT, + 'update_product_preview_image' => '1', + 'use_product_image_for_swatch' => 0, + ], + ], + "{$frontendInput}_change_frontend_input_dropdown" => [ + 'post_data' => [ + 'frontend_input' => 'select', + ], + 'expected_data' => [ + 'frontend_input' => 'select', + 'swatch_input_type' => null, + 'update_product_preview_image' => null, + 'use_product_image_for_swatch' => null, + ], + ], + ] + ); + } + + /** + * @inheritdoc + */ + public function getUpdateOptionsProvider(): array + { + $frontendInput = $this->getFrontendInput(); + return array_replace_recursive( + parent::getUpdateOptionsProvider(), + [ + "{$frontendInput}_update_options" => [ + 'post_data' => [ + 'options_array' => [ + 'option_1' => [ + 'order' => '4', + 'swatch' => [ + Store::DEFAULT_STORE_ID => '#1a1a1a', + ], + ], + 'option_2' => [ + 'order' => '5', + 'swatch' => [ + Store::DEFAULT_STORE_ID => '#2b2b2b', + ], + ], + ], + ], + ], + ] + ); + } + /** * @inheritdoc */ @@ -148,4 +225,57 @@ protected function getFrontendInput(): string { return Swatch::SWATCH_TYPE_VISUAL_ATTRIBUTE_FRONTEND_INPUT; } + + /** + * @inheritdoc + */ + protected function getUpdatePostData(): array + { + return [ + 'frontend_label' => [ + Store::DEFAULT_STORE_ID => 'Visual swatch attribute Update', + ], + 'frontend_input' => Swatch::SWATCH_TYPE_VISUAL_ATTRIBUTE_FRONTEND_INPUT, + 'is_required' => '1', + 'update_product_preview_image' => '1', + 'use_product_image_for_swatch' => '1', + 'is_global' => ScopedAttributeInterface::SCOPE_WEBSITE, + 'is_unique' => '1', + 'is_used_in_grid' => '1', + 'is_visible_in_grid' => '1', + 'is_filterable_in_grid' => '1', + 'is_searchable' => '1', + 'search_weight' => '2', + 'is_visible_in_advanced_search' => '1', + 'is_comparable' => '1', + 'is_filterable' => '2', + 'is_filterable_in_search' => '1', + 'position' => '2', + 'is_used_for_promo_rules' => '1', + 'is_visible_on_front' => '1', + 'used_in_product_listing' => '0', + 'used_for_sort_by' => '1', + ]; + } + + /** + * @inheritdoc + */ + protected function getUpdateExpectedData(): array + { + $updatePostData = $this->getUpdatePostData(); + return array_merge( + $updatePostData, + [ + 'frontend_label' => 'Visual swatch attribute Update', + 'frontend_input' => 'select', + 'attribute_code' => 'visual_swatch_attribute', + 'default_value' => null, + 'frontend_class' => null, + 'is_html_allowed_on_front' => '1', + 'is_user_defined' => '1', + 'backend_type' => 'int', + ] + ); + } } diff --git a/dev/tests/integration/framework/Magento/TestFramework/Weee/Model/Attribute/DataProvider/FixedProductTax.php b/dev/tests/integration/framework/Magento/TestFramework/Weee/Model/Attribute/DataProvider/FixedProductTax.php index 2f1f625ad48ac..ab0b214ccc101 100644 --- a/dev/tests/integration/framework/Magento/TestFramework/Weee/Model/Attribute/DataProvider/FixedProductTax.php +++ b/dev/tests/integration/framework/Magento/TestFramework/Weee/Model/Attribute/DataProvider/FixedProductTax.php @@ -7,7 +7,10 @@ namespace Magento\TestFramework\Weee\Model\Attribute\DataProvider; +use Magento\Eav\Model\Entity\Attribute\ScopedAttributeInterface; use Magento\TestFramework\Eav\Model\Attribute\DataProvider\AbstractBaseAttributeData; +use Magento\Store\Model\Store; +use Magento\Weee\Model\Attribute\Backend\Weee\Tax; /** * Product attribute data for attribute with input type fixed product tax. @@ -47,6 +50,27 @@ public function getAttributeDataWithCheckArray(): array return $result; } + /** + * @inheritdoc + */ + public function getUpdateProvider(): array + { + $frontendInput = $this->getFrontendInput(); + return array_replace_recursive( + parent::getUpdateProvider(), + [ + "{$frontendInput}_other_attribute_code" => [ + 'post_data' => [ + 'attribute_code' => 'text_attribute_update', + ], + 'expected_data' => [ + 'attribute_code' => 'fixed_product_attribute', + ], + ], + ] + ); + } + /** * @inheritdoc */ @@ -54,4 +78,55 @@ protected function getFrontendInput(): string { return 'weee'; } + + /** + * @inheritdoc + */ + protected function getUpdatePostData(): array + { + return [ + 'frontend_label' => [ + Store::DEFAULT_STORE_ID => 'Fixed product tax Update', + ], + 'frontend_input' => 'weee', + 'is_used_in_grid' => '1', + 'is_visible_in_grid' => '1', + 'is_filterable_in_grid' => '1', + ]; + } + + /** + * @inheritdoc + */ + protected function getUpdateExpectedData(): array + { + $updatePostData = $this->getUpdatePostData(); + return array_merge( + $updatePostData, + [ + 'frontend_label' => 'Fixed product tax Update', + 'is_required' => '0', + 'attribute_code' => 'fixed_product_attribute', + 'is_global' => ScopedAttributeInterface::SCOPE_GLOBAL, + 'default_value' => null, + 'is_unique' => '0', + 'frontend_class' => null, + 'is_searchable' => '0', + 'search_weight' => '1', + 'is_visible_in_advanced_search' => '0', + 'is_comparable' => '0', + 'is_filterable' => '0', + 'is_filterable_in_search' => '0', + 'position' => '0', + 'is_used_for_promo_rules' => '0', + 'is_html_allowed_on_front' => '0', + 'is_visible_on_front' => '0', + 'used_in_product_listing' => '0', + 'used_for_sort_by' => '0', + 'is_user_defined' => '1', + 'backend_type' => 'static', + 'backend_model' => Tax::class, + ] + ); + } } diff --git a/dev/tests/integration/framework/Magento/TestFramework/Wishlist/Model/GetWishlistByCustomerId.php b/dev/tests/integration/framework/Magento/TestFramework/Wishlist/Model/GetWishlistByCustomerId.php new file mode 100644 index 0000000000000..93f170466cb40 --- /dev/null +++ b/dev/tests/integration/framework/Magento/TestFramework/Wishlist/Model/GetWishlistByCustomerId.php @@ -0,0 +1,61 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +declare(strict_types=1); + +namespace Magento\TestFramework\Wishlist\Model; + +use Magento\Wishlist\Model\Item; +use Magento\Wishlist\Model\Wishlist; +use Magento\Wishlist\Model\WishlistFactory; + +/** + * Load wish list by customer id. + */ +class GetWishlistByCustomerId +{ + /** @var WishlistFactory */ + private $wishlistFactory; + + /** + * @param WishlistFactory $wishlistFactory + */ + public function __construct(WishlistFactory $wishlistFactory) + { + $this->wishlistFactory = $wishlistFactory; + } + + /** + * Load wish list by customer id. + * + * @param int $customerId + * @return Wishlist + */ + public function execute(int $customerId): Wishlist + { + return $this->wishlistFactory->create()->loadByCustomerId($customerId, true); + } + + /** + * Get wish list item by sku. + * + * @param int $customerId + * @param string $sku + * @return null|Item + */ + public function getItemBySku(int $customerId, string $sku): ?Item + { + $result = null; + $items = $this->execute($customerId)->getItemCollection()->getItems(); + foreach ($items as $item) { + if ($item->getProduct()->getData('sku') === $sku) { + $result = $item; + break; + } + } + + return $result; + } +} diff --git a/dev/tests/integration/framework/tests/unit/testsuite/Magento/Test/Annotation/_files/components/a/aa/aaa/registration.php b/dev/tests/integration/framework/tests/unit/testsuite/Magento/Test/Annotation/_files/components/a/aa/aaa/registration.php index 6dba3551281e3..b3a410e609d52 100644 --- a/dev/tests/integration/framework/tests/unit/testsuite/Magento/Test/Annotation/_files/components/a/aa/aaa/registration.php +++ b/dev/tests/integration/framework/tests/unit/testsuite/Magento/Test/Annotation/_files/components/a/aa/aaa/registration.php @@ -4,6 +4,6 @@ * See COPYING.txt for license details. */ -use \Magento\Framework\Component\ComponentRegistrar; +use Magento\Framework\Component\ComponentRegistrar; ComponentRegistrar::register(ComponentRegistrar::THEME, 'frontend/Magento/theme', __DIR__); diff --git a/dev/tests/integration/framework/tests/unit/testsuite/Magento/Test/Annotation/_files/components/a/aa/registration.php b/dev/tests/integration/framework/tests/unit/testsuite/Magento/Test/Annotation/_files/components/a/aa/registration.php index 30dd39c36f2a4..045a53181a7d3 100644 --- a/dev/tests/integration/framework/tests/unit/testsuite/Magento/Test/Annotation/_files/components/a/aa/registration.php +++ b/dev/tests/integration/framework/tests/unit/testsuite/Magento/Test/Annotation/_files/components/a/aa/registration.php @@ -4,6 +4,6 @@ * See COPYING.txt for license details. */ -use \Magento\Framework\Component\ComponentRegistrar; +use Magento\Framework\Component\ComponentRegistrar; ComponentRegistrar::register(ComponentRegistrar::LANGUAGE, 'magento_language', __DIR__); diff --git a/dev/tests/integration/framework/tests/unit/testsuite/Magento/Test/Annotation/_files/components/b/registration.php b/dev/tests/integration/framework/tests/unit/testsuite/Magento/Test/Annotation/_files/components/b/registration.php index 8d3b9b440fd7d..8c3c7576dc3f4 100644 --- a/dev/tests/integration/framework/tests/unit/testsuite/Magento/Test/Annotation/_files/components/b/registration.php +++ b/dev/tests/integration/framework/tests/unit/testsuite/Magento/Test/Annotation/_files/components/b/registration.php @@ -4,6 +4,6 @@ * See COPYING.txt for license details. */ -use \Magento\Framework\Component\ComponentRegistrar; +use Magento\Framework\Component\ComponentRegistrar; ComponentRegistrar::register(ComponentRegistrar::LIBRARY, 'magento/library', __DIR__); diff --git a/dev/tests/integration/framework/tests/unit/testsuite/Magento/Test/Annotation/_files/components/registration.php b/dev/tests/integration/framework/tests/unit/testsuite/Magento/Test/Annotation/_files/components/registration.php index ddc564ae5a143..effc8b6954787 100644 --- a/dev/tests/integration/framework/tests/unit/testsuite/Magento/Test/Annotation/_files/components/registration.php +++ b/dev/tests/integration/framework/tests/unit/testsuite/Magento/Test/Annotation/_files/components/registration.php @@ -4,6 +4,6 @@ * See COPYING.txt for license details. */ -use \Magento\Framework\Component\ComponentRegistrar; +use Magento\Framework\Component\ComponentRegistrar; ComponentRegistrar::register(ComponentRegistrar::MODULE, 'Magento_ModuleOne', __DIR__); diff --git a/dev/tests/integration/framework/tests/unit/testsuite/Magento/Test/RequestTest.php b/dev/tests/integration/framework/tests/unit/testsuite/Magento/Test/RequestTest.php index a5c9a281c3ffd..6b853aebd41fa 100644 --- a/dev/tests/integration/framework/tests/unit/testsuite/Magento/Test/RequestTest.php +++ b/dev/tests/integration/framework/tests/unit/testsuite/Magento/Test/RequestTest.php @@ -5,7 +5,7 @@ */ namespace Magento\Test; -use Zend\Stdlib\Parameters; +use Laminas\Stdlib\Parameters; class RequestTest extends \PHPUnit\Framework\TestCase { diff --git a/dev/tests/integration/phpunit.xml.dist b/dev/tests/integration/phpunit.xml.dist index 858de7a9873e7..56812163ed5f2 100644 --- a/dev/tests/integration/phpunit.xml.dist +++ b/dev/tests/integration/phpunit.xml.dist @@ -78,8 +78,6 @@ </php> <!-- Test listeners --> <listeners> - <listener class="Magento\TestFramework\Event\PhpUnit"/> - <listener class="Magento\TestFramework\ErrorLog\Listener"/> <listener class="Yandex\Allure\Adapter\AllureAdapter"> <arguments> <string>var/allure-results</string> <!-- XML files output directory --> @@ -127,5 +125,8 @@ </array> </arguments> </listener> + <!-- Run after AllureAdapter to allow it to initialize properly --> + <listener class="Magento\TestFramework\Event\PhpUnit"/> + <listener class="Magento\TestFramework\ErrorLog\Listener"/> </listeners> </phpunit> diff --git a/dev/tests/integration/testsuite/Magento/AdminNotification/Controller/Adminhtml/Notification/MassMarkAsReadTest.php b/dev/tests/integration/testsuite/Magento/AdminNotification/Controller/Adminhtml/Notification/MassMarkAsReadTest.php index c611d6fd7289f..04a69fe200dd1 100644 --- a/dev/tests/integration/testsuite/Magento/AdminNotification/Controller/Adminhtml/Notification/MassMarkAsReadTest.php +++ b/dev/tests/integration/testsuite/Magento/AdminNotification/Controller/Adminhtml/Notification/MassMarkAsReadTest.php @@ -5,12 +5,15 @@ */ namespace Magento\AdminNotification\Controller\Adminhtml\Notification; +use Magento\Framework\App\Request\Http as HttpRequest; + class MassMarkAsReadTest extends \Magento\TestFramework\TestCase\AbstractBackendController { public function setUp() { $this->resource = 'Magento_AdminNotification::mark_as_read'; $this->uri = 'backend/admin/notification/massmarkasread'; + $this->httpMethod = HttpRequest::METHOD_POST; parent::setUp(); } } diff --git a/dev/tests/integration/testsuite/Magento/AdminNotification/Controller/Adminhtml/Notification/MassRemoveTest.php b/dev/tests/integration/testsuite/Magento/AdminNotification/Controller/Adminhtml/Notification/MassRemoveTest.php index f05985015833a..55ee6a58063a4 100644 --- a/dev/tests/integration/testsuite/Magento/AdminNotification/Controller/Adminhtml/Notification/MassRemoveTest.php +++ b/dev/tests/integration/testsuite/Magento/AdminNotification/Controller/Adminhtml/Notification/MassRemoveTest.php @@ -5,12 +5,15 @@ */ namespace Magento\AdminNotification\Controller\Adminhtml\Notification; +use Magento\Framework\App\Request\Http as HttpRequest; + class MassRemoveTest extends \Magento\TestFramework\TestCase\AbstractBackendController { public function setUp() { $this->resource = 'Magento_AdminNotification::adminnotification_remove'; $this->uri = 'backend/admin/notification/massremove'; + $this->httpMethod = HttpRequest::METHOD_POST; parent::setUp(); } } diff --git a/dev/tests/integration/testsuite/Magento/Analytics/Cron/UpdateTest.php b/dev/tests/integration/testsuite/Magento/Analytics/Cron/UpdateTest.php new file mode 100644 index 0000000000000..c8ce98d34074b --- /dev/null +++ b/dev/tests/integration/testsuite/Magento/Analytics/Cron/UpdateTest.php @@ -0,0 +1,239 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ + +declare(strict_types=1); + +namespace Magento\Analytics\Cron; + +use Magento\Analytics\Model\Config\Backend\Baseurl\SubscriptionUpdateHandler; +use Magento\Analytics\Model\Connector\Http\Client\Curl as CurlClient; +use Magento\Analytics\Model\Connector\Http\ClientInterface; +use Magento\Config\Model\PreparedValueFactory; +use Magento\Config\Model\ResourceModel\Config\Data as ConfigDataResource; +use Magento\Framework\App\Config\ScopeConfigInterface; +use Magento\Framework\FlagManager; +use Magento\Store\Model\Store; +use Magento\TestFramework\Helper\Bootstrap; +use Magento\TestFramework\ObjectManager; + +/** + * @magentoAppArea adminhtml + * @SuppressWarnings(PHPMD.CouplingBetweenObjects) + */ +class UpdateTest extends \PHPUnit\Framework\TestCase +{ + /** + * @var ObjectManager + */ + private $objectManager; + + /** + * @var PreparedValueFactory + */ + private $preparedValueFactory; + + /** + * @var ConfigDataResource + */ + private $configValueResourceModel; + + /** + * @var Update + */ + private $updateCron; + + /** + * @var ClientInterface|\PHPUnit\Framework\MockObject\MockObject + */ + private $httpClient; + + /** + * @var FlagManager + */ + private $flagManager; + + /** + * @var ScopeConfigInterface + */ + private $scopeConfig; + + /** + * @return void + */ + protected function setUp() + { + $this->objectManager = Bootstrap::getObjectManager(); + $this->httpClient = $this->getMockBuilder(ClientInterface::class) + ->getMockForAbstractClass(); + $this->objectManager->addSharedInstance($this->httpClient, CurlClient::class); + $this->preparedValueFactory = $this->objectManager->get(PreparedValueFactory::class); + $this->configValueResourceModel = $this->objectManager->get(ConfigDataResource::class); + $this->updateCron = $this->objectManager->get(Update::class); + $this->scopeConfig = $this->objectManager->get(ScopeConfigInterface::class); + $this->flagManager = $this->objectManager->get(FlagManager::class); + } + + /** + * @magentoDbIsolation enabled + * @magentoAppIsolation enabled + * @magentoDataFixture Magento/Analytics/_files/enabled_subscription_with_invalid_token.php + * + */ + public function testSuccessfulAttemptExecute() + { + $this->saveConfigValue( + Store::XML_PATH_SECURE_BASE_URL, + 'http://store.com/' + ); + + $this->mockRequestCall(201, 'URL has been changed'); + + $this->updateCron->execute(); + $this->assertEmpty($this->getUpdateCounterFlag()); + $this->assertEmpty($this->getPreviousBaseUrlFlag()); + $this->assertEmpty($this->getConfigValue(SubscriptionUpdateHandler::UPDATE_CRON_STRING_PATH)); + } + + /** + * @magentoDbIsolation enabled + * @magentoAppIsolation enabled + * @magentoDataFixture Magento/Analytics/_files/enabled_subscription_with_invalid_token.php + * + */ + public function testUnsuccessfulAttemptExecute() + { + $this->saveConfigValue( + Store::XML_PATH_SECURE_BASE_URL, + 'http://store.com/' + ); + + $reverseCounter = $this->getUpdateCounterFlag(); + $this->mockRequestCall(500, 'Unauthorized access'); + + $this->updateCron->execute(); + $this->assertEquals($reverseCounter - 1, $this->getUpdateCounterFlag()); + } + + /** + * @magentoDbIsolation enabled + * @magentoAppIsolation enabled + * @magentoDataFixture Magento/Analytics/_files/enabled_subscription_with_invalid_token.php + * + */ + public function testLastUnsuccessfulAttemptExecute() + { + $this->saveConfigValue( + Store::XML_PATH_SECURE_BASE_URL, + 'http://store.com/' + ); + + $this->setUpdateCounterValue(1); + $this->mockRequestCall(500, 'Unauthorized access'); + + $this->updateCron->execute(); + $this->assertEmpty($this->getUpdateCounterFlag()); + $this->assertEmpty($this->getPreviousBaseUrlFlag()); + $this->assertEmpty($this->getConfigValue(SubscriptionUpdateHandler::UPDATE_CRON_STRING_PATH)); + } + + /** + * Save configuration value + * + * @param string $path The configuration path in format section/group/field_name + * @param string $value The configuration value + * @param string $scope The configuration scope (default, website, or store) + * @return void + * @throws \Magento\Framework\Exception\RuntimeException + * @throws \Magento\Framework\Exception\AlreadyExistsException + */ + private function saveConfigValue( + string $path, + string $value, + string $scope = ScopeConfigInterface::SCOPE_TYPE_DEFAULT + ) { + $configValue = $this->preparedValueFactory->create( + $path, + $value, + $scope + ); + $this->configValueResourceModel->save($configValue); + } + + /** + * Get configuration value + * + * @param string $path + * @param string $scopeType + * @return mixed + */ + private function getConfigValue( + string $path, + string $scopeType = ScopeConfigInterface::SCOPE_TYPE_DEFAULT + ) { + return $this->scopeConfig->getValue( + $path, + $scopeType + ); + } + + /** + * Get update counter flag value + * + * @return int|null + */ + private function getUpdateCounterFlag(): ?int + { + return $this->flagManager + ->getFlagData(SubscriptionUpdateHandler::SUBSCRIPTION_UPDATE_REVERSE_COUNTER_FLAG_CODE); + } + + /** + * Get previous URL flag value + * + * @return string|null + */ + private function getPreviousBaseUrlFlag(): ?string + { + return $this->flagManager + ->getFlagData(SubscriptionUpdateHandler::PREVIOUS_BASE_URL_FLAG_CODE); + } + + /** + * Set response mock for the HTTP client + * + * @param int $responseCode + * @param string $responseMessage + */ + private function mockRequestCall(int $responseCode, string $responseMessage): void + { + $response = $this->objectManager->create( + \Zend_Http_Response::class, + [ + 'code' => $responseCode, + 'headers' => [ + 'Content-Type' => 'application/json' + ], + 'body' => json_encode(['message' => $responseMessage]) + ] + ); + + $this->httpClient + ->expects($this->once()) + ->method('request') + ->willReturn($response); + } + + /** + * Set value for update counter flag + * + * @param int $value + */ + private function setUpdateCounterValue(int $value): void + { + $this->flagManager + ->saveFlag(SubscriptionUpdateHandler::SUBSCRIPTION_UPDATE_REVERSE_COUNTER_FLAG_CODE, $value); + } +} diff --git a/dev/tests/integration/testsuite/Magento/Analytics/Model/Config/Backend/EnabledTest.php b/dev/tests/integration/testsuite/Magento/Analytics/Model/Config/Backend/EnabledTest.php new file mode 100644 index 0000000000000..61cc8b56af949 --- /dev/null +++ b/dev/tests/integration/testsuite/Magento/Analytics/Model/Config/Backend/EnabledTest.php @@ -0,0 +1,163 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ + +declare(strict_types=1); + +namespace Magento\Analytics\Model\Config\Backend; + +use Magento\Analytics\Model\SubscriptionStatusProvider; +use Magento\Config\Model\Config\Source\Enabledisable; +use Magento\Config\Model\PreparedValueFactory; +use Magento\Config\Model\ResourceModel\Config\Data as ConfigDataResource; +use Magento\Framework\App\Config\ReinitableConfigInterface; +use Magento\Framework\App\Config\ScopeConfigInterface; +use Magento\TestFramework\Helper\Bootstrap; + +/** + * @magentoAppArea adminhtml + * @SuppressWarnings(PHPMD.CouplingBetweenObjects) + */ +class EnabledTest extends \PHPUnit\Framework\TestCase +{ + /** + * @var \Magento\Framework\ObjectManagerInterface + */ + private $objectManager; + + /** + * @var ScopeConfigInterface + */ + private $scopeConfig; + + /** + * @var SubscriptionStatusProvider + */ + private $subscriptionStatusProvider; + + /** + * @var PreparedValueFactory + */ + private $preparedValueFactory; + + /** + * @var ConfigDataResource + */ + private $configValueResourceModel; + + /** + * @var ReinitableConfigInterface + */ + private $reinitableConfig; + + /** + * @inheritDoc + */ + protected function setUp() + { + $this->objectManager = Bootstrap::getObjectManager(); + $this->scopeConfig = $this->objectManager->get(ScopeConfigInterface::class); + $this->subscriptionStatusProvider = $this->objectManager->get(SubscriptionStatusProvider::class); + $this->preparedValueFactory = $this->objectManager->get(PreparedValueFactory::class); + $this->configValueResourceModel = $this->objectManager->get(ConfigDataResource::class); + $this->reinitableConfig = $this->objectManager->get(ReinitableConfigInterface::class); + } + + /** + * @magentoDbIsolation enabled + */ + public function testDisable() + { + $this->checkInitialStatus(); + $this->saveConfigValue(Enabled::XML_ENABLED_CONFIG_STRUCTURE_PATH, (string)Enabledisable::DISABLE_VALUE); + $this->reinitableConfig->reinit(); + + $this->checkDisabledStatus(); + } + + /** + * @depends testDisable + * @magentoDbIsolation enabled + */ + public function testReEnable() + { + $this->checkDisabledStatus(); + $this->saveConfigValue(Enabled::XML_ENABLED_CONFIG_STRUCTURE_PATH, (string)Enabledisable::ENABLE_VALUE); + $this->checkReEnabledStatus(); + } + + /** + * Get configuration value + * + * @param string $path + * @param string $scopeType + * @return mixed + */ + private function getConfigValue( + string $path, + string $scopeType = ScopeConfigInterface::SCOPE_TYPE_DEFAULT + ) { + return $this->scopeConfig->getValue( + $path, + $scopeType + ); + } + + /** + * Save configuration value + * + * @param string $path The configuration path in format section/group/field_name + * @param string $value The configuration value + * @param string $scope The configuration scope (default, website, or store) + * @return void + * @throws \Magento\Framework\Exception\RuntimeException + * @throws \Magento\Framework\Exception\AlreadyExistsException + */ + private function saveConfigValue( + string $path, + string $value, + string $scope = ScopeConfigInterface::SCOPE_TYPE_DEFAULT + ) { + $configValue = $this->preparedValueFactory->create( + $path, + $value, + $scope + ); + $this->configValueResourceModel->save($configValue); + } + + /** + * Check the instance status after installation + */ + private function checkInitialStatus() + { + $this->assertNotSame(SubscriptionStatusProvider::DISABLED, $this->subscriptionStatusProvider->getStatus()); + $this->assertNotEmpty($this->getConfigValue(CollectionTime::CRON_SCHEDULE_PATH)); + } + + /** + * Check the instance status after disabling AR synchronisation + */ + private function checkDisabledStatus() + { + $this->assertSame(SubscriptionStatusProvider::DISABLED, $this->subscriptionStatusProvider->getStatus()); + $this->assertEmpty($this->getConfigValue(CollectionTime::CRON_SCHEDULE_PATH)); + } + + /** + * Check the instance status after re-activation AR synchronisation + */ + private function checkReEnabledStatus() + { + $this->assertContains( + $this->subscriptionStatusProvider->getStatus(), + [ + SubscriptionStatusProvider::ENABLED, + SubscriptionStatusProvider::PENDING, + ] + ); + $this->assertNotEmpty($this->getConfigValue(CollectionTime::CRON_SCHEDULE_PATH)); + } +} diff --git a/dev/tests/integration/testsuite/Magento/Analytics/_files/enabled_subscription_with_invalid_token.php b/dev/tests/integration/testsuite/Magento/Analytics/_files/enabled_subscription_with_invalid_token.php index 0106bf6f1bdac..c52de227deae8 100644 --- a/dev/tests/integration/testsuite/Magento/Analytics/_files/enabled_subscription_with_invalid_token.php +++ b/dev/tests/integration/testsuite/Magento/Analytics/_files/enabled_subscription_with_invalid_token.php @@ -3,27 +3,28 @@ * Copyright © Magento, Inc. All rights reserved. * See COPYING.txt for license details. */ + +use Magento\Analytics\Model\AnalyticsToken; use Magento\Analytics\Model\Config\Backend\Enabled\SubscriptionHandler; +use Magento\Framework\App\Config\Storage\WriterInterface; +use Magento\Framework\FlagManager; $objectManager = \Magento\TestFramework\Helper\Bootstrap::getObjectManager(); /** - * @var $configWriter \Magento\Framework\App\Config\Storage\WriterInterface + * @var $configWriter WriterInterface */ -$configWriter = $objectManager->get(\Magento\Framework\App\Config\Storage\WriterInterface::class); - +$configWriter = $objectManager->get(WriterInterface::class); $configWriter->delete(SubscriptionHandler::CRON_STRING_PATH); -$configWriter->save('analytics/subscription/enabled', 1); /** - * @var $analyticsToken \Magento\Analytics\Model\AnalyticsToken + * @var $analyticsToken AnalyticsToken */ -$analyticsToken = $objectManager->get(\Magento\Analytics\Model\AnalyticsToken::class); +$analyticsToken = $objectManager->get(AnalyticsToken::class); $analyticsToken->storeToken('42'); /** - * @var $flagManager \Magento\Framework\FlagManager + * @var $flagManager FlagManager */ -$flagManager = $objectManager->get(\Magento\Framework\FlagManager::class); - +$flagManager = $objectManager->get(FlagManager::class); $flagManager->deleteFlag(SubscriptionHandler::ATTEMPTS_REVERSE_COUNTER_FLAG_CODE); diff --git a/dev/tests/integration/testsuite/Magento/Analytics/_files/enabled_subscription_with_invalid_token_rollback.php b/dev/tests/integration/testsuite/Magento/Analytics/_files/enabled_subscription_with_invalid_token_rollback.php index 3fd3e21e282e0..a47a4b3475c9d 100644 --- a/dev/tests/integration/testsuite/Magento/Analytics/_files/enabled_subscription_with_invalid_token_rollback.php +++ b/dev/tests/integration/testsuite/Magento/Analytics/_files/enabled_subscription_with_invalid_token_rollback.php @@ -3,27 +3,28 @@ * Copyright © Magento, Inc. All rights reserved. * See COPYING.txt for license details. */ + +use Magento\Analytics\Model\AnalyticsToken; use Magento\Analytics\Model\Config\Backend\Enabled\SubscriptionHandler; +use Magento\Framework\App\Config\Storage\WriterInterface; +use Magento\Framework\FlagManager; $objectManager = \Magento\TestFramework\Helper\Bootstrap::getObjectManager(); /** - * @var $configWriter \Magento\Framework\App\Config\Storage\WriterInterface + * @var $configWriter WriterInterface */ -$configWriter = $objectManager->get(\Magento\Framework\App\Config\Storage\WriterInterface::class); - -$configWriter->delete(SubscriptionHandler::CRON_STRING_PATH); -$configWriter->save('analytics/subscription/enabled', 0); +$configWriter = $objectManager->get(WriterInterface::class); +$configWriter->save(SubscriptionHandler::CRON_STRING_PATH, join(' ', SubscriptionHandler::CRON_EXPR_ARRAY)); /** - * @var $analyticsToken \Magento\Analytics\Model\AnalyticsToken + * @var $analyticsToken AnalyticsToken */ -$analyticsToken = $objectManager->get(\Magento\Analytics\Model\AnalyticsToken::class); +$analyticsToken = $objectManager->get(AnalyticsToken::class); $analyticsToken->storeToken(null); /** - * @var $flagManager \Magento\Framework\FlagManager + * @var $flagManager FlagManager */ -$flagManager = $objectManager->get(\Magento\Framework\FlagManager::class); - -$flagManager->deleteFlag(SubscriptionHandler::ATTEMPTS_REVERSE_COUNTER_FLAG_CODE); +$flagManager = $objectManager->get(FlagManager::class); +$flagManager->saveFlag(SubscriptionHandler::ATTEMPTS_REVERSE_COUNTER_FLAG_CODE, 24); diff --git a/dev/tests/integration/testsuite/Magento/AsynchronousOperations/Model/SaveMultipleOperationsTest.php b/dev/tests/integration/testsuite/Magento/AsynchronousOperations/Model/SaveMultipleOperationsTest.php new file mode 100644 index 0000000000000..cbf20d9e8ab59 --- /dev/null +++ b/dev/tests/integration/testsuite/Magento/AsynchronousOperations/Model/SaveMultipleOperationsTest.php @@ -0,0 +1,117 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ + +namespace Magento\AsynchronousOperations\Model; + +use Magento\AsynchronousOperations\Api\Data\OperationInterface; +use Magento\AsynchronousOperations\Api\Data\OperationInterfaceFactory; +use Magento\AsynchronousOperations\Api\SaveMultipleOperationsInterface; +use Magento\AsynchronousOperations\Model\BulkStatus; +use Magento\AsynchronousOperations\Api\Data\BulkSummaryInterface; +use Magento\AsynchronousOperations\Api\Data\BulkSummaryInterfaceFactory; +use Magento\Framework\EntityManager\EntityManager; + +class SaveMultipleOperationsTest extends \PHPUnit\Framework\TestCase +{ + + private const BULK_UUID = "bulk-uuid-multiple-0"; + + /** + * @var BulkStatus + */ + private $bulkStatusManagement; + + /** + * @var OperationInterfaceFactory + */ + private $operationFactory; + + /** + * @var SaveMultipleOperationsInterface + */ + private $saveMultipleOperationsInterface; + + /** + * @var EntityManager + */ + private $entityManager; + + /** + * @var BulkSummaryInterfaceFactory + */ + private $bulkSummaryFactory; + + /** + * Set Up the test + */ + protected function setUp() + { + $this->saveMultipleOperationsInterface = \Magento\TestFramework\Helper\Bootstrap::getObjectManager()->create( + SaveMultipleOperationsInterface::class + ); + $this->operationFactory = \Magento\TestFramework\Helper\Bootstrap::getObjectManager()->create( + OperationInterfaceFactory::class + ); + $this->bulkStatusManagement = \Magento\TestFramework\Helper\Bootstrap::getObjectManager()->create( + BulkStatus::class + ); + $this->bulkSummaryFactory = \Magento\TestFramework\Helper\Bootstrap::getObjectManager()->create( + BulkSummaryInterfaceFactory::class + ); + $this->entityManager = \Magento\TestFramework\Helper\Bootstrap::getObjectManager()->create( + EntityManager::class + ); + } + + /** + * Test execute() of SaveMultipleOperations + */ + public function testExecute() + { + $operation = $this->createOperation(); + $operations = [$operation, $operation, $operation]; + + $bulkSummary = $this->bulkSummaryFactory->create(); + $this->entityManager->load($bulkSummary, self::BULK_UUID); + $bulkSummary->setBulkId(self::BULK_UUID); + $bulkSummary->setDescription("Test Bulk"); + $bulkSummary->setUserId(1); + $bulkSummary->setUserType(1); + $bulkSummary->setOperationCount(count($operations)); + $this->entityManager->save($bulkSummary); + + $this->saveMultipleOperationsInterface->execute($operations); + $operationsCount = $this->bulkStatusManagement + ->getOperationsCountByBulkIdAndStatus(self::BULK_UUID, OperationInterface::STATUS_TYPE_OPEN); + $this->assertEquals($operationsCount, 3); + } + + /** + * Create Operation object and pre-fill with test data + * @return OperationInterface + */ + public function createOperation() + { + $serializedData = [ + 'entity_id' => null, + 'entity_link' => '', + 'meta_information' => json_encode([ + 'entity_id' => 5, + 'meta_information' => 'Test' + ]) + ]; + + $data = [ + 'data' => [ + OperationInterface::BULK_ID => self::BULK_UUID, + OperationInterface::TOPIC_NAME => "topic-4", + OperationInterface::SERIALIZED_DATA => json_encode($serializedData), + OperationInterface::STATUS => OperationInterface::STATUS_TYPE_OPEN, + ], + ]; + return $this->operationFactory->create($data); + } +} diff --git a/dev/tests/integration/testsuite/Magento/Authorizenet/Controller/Adminhtml/Authorizenet/Directpost/Payment/PlaceTest.php b/dev/tests/integration/testsuite/Magento/Authorizenet/Controller/Adminhtml/Authorizenet/Directpost/Payment/PlaceTest.php deleted file mode 100644 index 35892623ef7af..0000000000000 --- a/dev/tests/integration/testsuite/Magento/Authorizenet/Controller/Adminhtml/Authorizenet/Directpost/Payment/PlaceTest.php +++ /dev/null @@ -1,141 +0,0 @@ -<?php -/** - * Copyright © Magento, Inc. All rights reserved. - * See COPYING.txt for license details. - */ -namespace Magento\Authorizenet\Controller\Adminhtml\Authorizenet\Directpost\Payment; - -/** - * Class PlaceTest - * - * @SuppressWarnings(PHPMD.CouplingBetweenObjects) - */ -class PlaceTest extends \Magento\TestFramework\TestCase\AbstractBackendController -{ - /** - * Test requestToAuthorizenetData returning - */ - public function testExecuteAuthorizenetDataReturning() - { - $requestToAuthorizenetData = ['Authorizenet' => 'data']; - - $this->getRequest()->setParam('payment', ['method' => 'authorizenet_directpost']); - $this->getRequest()->setParam('controller', 'order_create'); - $orderCreateMock = $this->getOrderCreateMock($requestToAuthorizenetData); - $directpostMock = $this->getMockBuilder(\Magento\Authorizenet\Model\Directpost::class) - ->setMethods(['getCode']) - ->disableOriginalConstructor() - ->getMock(); - $directpostMock->expects($this->once()) - ->method('getCode') - ->willReturn('authorizenet_directpost'); - $jsonHelper = $this->_objectManager->get(\Magento\Framework\Json\Helper\Data::class); - $objectManagerMock = $this->getMockBuilder(\Magento\Framework\ObjectManagerInterface::class) - ->setMethods(['create', 'get']) - ->getMockForAbstractClass(); - $objectManagerMock->expects($this->atLeastOnce()) - ->method('create') - ->with(\Magento\Authorizenet\Model\Directpost::class) - ->willReturn($directpostMock); - $authorizenetSessionMock = $this->getMockBuilder(\Magento\Authorizenet\Model\Directpost::class) - ->disableOriginalConstructor() - ->getMock(); - $urlMock = $this->getMockBuilder(\Magento\Backend\Model\UrlInterface::class) - ->getMockForAbstractClass(); - $objectManagerMock->expects($this->atLeastOnce()) - ->method('get') - ->willReturnMap([ - [\Magento\Sales\Model\AdminOrder\Create::class, $orderCreateMock], - [\Magento\Framework\Json\Helper\Data::class, $jsonHelper], - [\Magento\Authorizenet\Model\Directpost\Session::class, $authorizenetSessionMock], - [\Magento\Backend\Model\UrlInterface::class, $urlMock], - ]); - - $context = \Magento\TestFramework\Helper\Bootstrap::getObjectManager()->create( - \Magento\Backend\App\Action\Context::class, - [ - 'objectManager' => $objectManagerMock - ] - ); - - $controller = \Magento\TestFramework\Helper\Bootstrap::getObjectManager()->create( - \Magento\Authorizenet\Controller\Adminhtml\Authorizenet\Directpost\Payment\PlaceTesting::class, - ['context' => $context] - ); - $controller->execute(); - $this->assertContains(json_encode($requestToAuthorizenetData), $this->getResponse()->getBody()); - } - - /** - * @param array $requestToAuthorizenetData - * @return \PHPUnit_Framework_MockObject_MockObject - */ - private function getOrderCreateMock($requestToAuthorizenetData) - { - $methodInstanceMock = $this->getMockBuilder(\Magento\Authorizenet\Model\Directpost::class) - ->disableOriginalConstructor() - ->getMock(); - $directpostRequestMock = $this->getMockBuilder(\Magento\Authorizenet\Model\Directpost\Request::class) - ->setMethods(['getData']) - ->disableOriginalConstructor() - ->getMock(); - $directpostRequestMock->expects($this->once()) - ->method('getData') - ->willReturn($requestToAuthorizenetData); - $methodInstanceMock->expects($this->once()) - ->method('generateRequestFromOrder') - ->willReturn($directpostRequestMock); - $paymentMock = $this->getMockBuilder(\Magento\Quote\Model\Quote\Payment::class) - ->setMethods(['getMethod', 'getMethodInstance']) - ->disableOriginalConstructor() - ->getMock(); - $paymentMock->expects($this->once()) - ->method('getMethod') - ->willReturn('authorizenet_directpost'); - $paymentMock->expects($this->once()) - ->method('getMethodInstance') - ->willReturn($methodInstanceMock); - $quoteMock = $this->getMockBuilder(\Magento\Quote\Model\Quote::class) - ->setMethods(['getPayment', 'getStoreId']) - ->disableOriginalConstructor() - ->getMock(); - $quoteMock->expects($this->any()) - ->method('getPayment') - ->willReturn($paymentMock); - $orderMock = $this->getMockBuilder(\Magento\Sales\Model\Order::class) - ->setMethods(['getPayment']) - ->disableOriginalConstructor() - ->getMock(); - $orderMock->expects($this->any()) - ->method('getPayment') - ->willReturn($paymentMock); - $sessionQuoteMock = $this->getMockBuilder(\Magento\Backend\Model\Session\Quote::class) - ->setMethods(['getOrder']) - ->disableOriginalConstructor() - ->getMock(); - $sessionQuoteMock->expects($this->once()) - ->method('getOrder') - ->willReturn($orderMock); - $orderCreateMock = $this->getMockBuilder(\Magento\Sales\Model\AdminOrder\Create::class) - ->setMethods(['getQuote', 'getSession', 'setIsValidate', 'importPostData', 'createOrder', 'setPaymentData']) - ->disableOriginalConstructor() - ->getMock(); - $orderCreateMock->expects($this->any()) - ->method('getQuote') - ->willReturn($quoteMock); - $orderCreateMock->expects($this->once()) - ->method('getSession') - ->willReturn($sessionQuoteMock); - $orderCreateMock->expects($this->once()) - ->method('setIsValidate') - ->willReturnSelf(); - $orderCreateMock->expects($this->once()) - ->method('importPostData') - ->willReturnSelf(); - $orderCreateMock->expects($this->once()) - ->method('createOrder') - ->willReturn($orderMock); - - return $orderCreateMock; - } -} diff --git a/dev/tests/integration/testsuite/Magento/Authorizenet/Controller/Adminhtml/Authorizenet/Directpost/Payment/PlaceTesting.php b/dev/tests/integration/testsuite/Magento/Authorizenet/Controller/Adminhtml/Authorizenet/Directpost/Payment/PlaceTesting.php deleted file mode 100644 index cb13f81381fde..0000000000000 --- a/dev/tests/integration/testsuite/Magento/Authorizenet/Controller/Adminhtml/Authorizenet/Directpost/Payment/PlaceTesting.php +++ /dev/null @@ -1,21 +0,0 @@ -<?php -/** - * Copyright © Magento, Inc. All rights reserved. - * See COPYING.txt for license details. - */ -namespace Magento\Authorizenet\Controller\Adminhtml\Authorizenet\Directpost\Payment; - -/** - * Class PlaceTesting extended test class, used to substitute calls to parent methods - */ -class PlaceTesting extends Place -{ - /** - * {@inheritdoc} - * This method tested in Magento\Sales\Controller\Adminhtml\Order\CreateTest - */ - protected function _processActionData($action = null) - { - // - } -} diff --git a/dev/tests/integration/testsuite/Magento/Authorizenet/Controller/Directpost/Payment/BackendResponseTest.php b/dev/tests/integration/testsuite/Magento/Authorizenet/Controller/Directpost/Payment/BackendResponseTest.php deleted file mode 100644 index 7ab55dc7fd928..0000000000000 --- a/dev/tests/integration/testsuite/Magento/Authorizenet/Controller/Directpost/Payment/BackendResponseTest.php +++ /dev/null @@ -1,63 +0,0 @@ -<?php -/** - * Copyright © Magento, Inc. All rights reserved. - * See COPYING.txt for license details. - */ - -namespace Magento\Authorizenet\Controller\Directpost\Payment; - -use Magento\TestFramework\TestCase\AbstractController; - -class BackendResponseTest extends AbstractController -{ - /** - * @var string - */ - private static $entryPoint = 'authorizenet/directpost_payment/backendresponse'; - - /** - * Checks a test case when request is processed from different to Authorize.net entry point. - */ - public function testUnauthorizedRequest() - { - $data = [ - 'x_response_code' => 1, - 'x_response_reason_code' => 1, - 'x_invoice_num' => '1', - 'x_amount' => 16, - 'x_trans_id' => '32iiw5ve', - 'x_card_type' => 'American Express', - 'x_account_number' => 'XXXX0002', - 'x_MD5_Hash' => 'h6a4s2h' - ]; - $this->getRequest()->setPostValue($data); - $this->dispatch(self::$entryPoint); - - self::assertEquals(302, $this->getResponse()->getHttpResponseCode()); - self::assertEmpty($this->getResponse()->getBody()); - } - - /** - * Checks a test case when request is successfully processed. - * - * @magentoConfigFixture current_store payment/authorizenet_directpost/trans_md5 n4v2c5n0d - * @magentoConfigFixture current_store payment/authorizenet_directpost/login merch1 - */ - public function testSuccess() - { - $data = [ - 'x_response_code' => 1, - 'x_response_reason_code' => 1, - 'x_invoice_num' => '1', - 'x_amount' => 16, - 'x_trans_id' => '32iiw5ve', - 'x_card_type' => 'American Express', - 'x_account_number' => 'XXXX0002', - 'x_MD5_Hash' => '0EAD2F65D3D879CCB0D1A6F24883AC92' - ]; - $this->getRequest()->setPostValue($data); - $this->dispatch(self::$entryPoint); - self::assertEquals(200, $this->getResponse()->getHttpResponseCode()); - self::assertContains('/sales/order/view', $this->getResponse()->getBody()); - } -} diff --git a/dev/tests/integration/testsuite/Magento/Authorizenet/Controller/Directpost/Payment/ResponseTest.php b/dev/tests/integration/testsuite/Magento/Authorizenet/Controller/Directpost/Payment/ResponseTest.php deleted file mode 100644 index 043bae9f2417b..0000000000000 --- a/dev/tests/integration/testsuite/Magento/Authorizenet/Controller/Directpost/Payment/ResponseTest.php +++ /dev/null @@ -1,245 +0,0 @@ -<?php -/** - * Copyright © Magento, Inc. All rights reserved. - * See COPYING.txt for license details. - */ -namespace Magento\Authorizenet\Controller\Directpost\Payment; - -/** - * Class ResponseTest - * - * @magentoAppArea frontend - */ -class ResponseTest extends \Magento\TestFramework\TestCase\AbstractController -{ - /** - * Tests the controller for declines - * - * @param int $invoiceNum - * @param string $hash - * @param string $errorMsg - * @param string[] $params - * - * @dataProvider responseActionAuthorizeCaptureDeclineDataProvider - */ - public function testResponseActionAuthorizeCaptureDecline($invoiceNum, $hash, $errorMsg, $params) - { - $controllerName = 'directpost_payment'; - $controllerModule = 'authorizenet'; - $controllerAction = 'response'; - $params['x_invoice_num'] = $invoiceNum; - $params['x_MD5_Hash'] = $hash; - $this->getRequest()->setControllerName( - $controllerName - )->setControllerModule( - $controllerModule - )->setActionName( - $controllerAction - )->setRouteName( - $controllerModule - )->setRequestUri("/{$controllerModule}/{$controllerName}/{$controllerAction}") - ->setParams($params); - - $objectManager = \Magento\TestFramework\Helper\Bootstrap::getObjectManager(); - - /** @var \Magento\Authorizenet\Controller\Directpost\Payment\Response */ - $controller = $objectManager->create(\Magento\Authorizenet\Controller\Directpost\Payment\Response::class); - - $response = $controller->execute(); - $output = $response->getLayout()->getOutput(); - - $expectedString = "{$controllerModule}/{$controllerName}/redirect/x_invoice_num/{$params['x_invoice_num']}/" - . "success/0/error_msg/{$errorMsg}/controller_action_name/{$controllerName}/"; - - $this->assertContains('window.location', $output); - $this->assertContains($expectedString, $output); - } - - /** - * Tests the controller for created blocks used for sending emails that should not affect layout response - * - * @param string $hash - * @param string[] $params - * - * @dataProvider responseActionAuthorizeCaptureSuccessDataProvider - */ - public function testBlockCreationAffectingResult($hash, $params) - { - $controllerName = 'directpost_payment'; - $controllerModule = 'authorizenet'; - $controllerAction = 'response'; - $params['x_invoice_num'] = 100000002; - $params['x_MD5_Hash'] = $hash; - $this->getRequest()->setControllerName( - $controllerName - )->setControllerModule( - $controllerModule - )->setActionName( - $controllerAction - )->setRouteName( - $controllerModule - )->setRequestUri("/{$controllerModule}/{$controllerName}/{$controllerAction}") - ->setParams($params); - - $objectManager = \Magento\TestFramework\Helper\Bootstrap::getObjectManager(); - - $directpostMock = $this->getMockBuilder(\Magento\Authorizenet\Model\Directpost::class) - ->disableOriginalConstructor() - ->getMock(); - $objectManagerMock = $this->getMockBuilder(\Magento\Framework\ObjectManagerInterface::class) - ->setMethods(['create']) - ->getMockForAbstractClass(); - $objectManagerMock->expects($this->atLeastOnce()) - ->method('create') - ->with(\Magento\Authorizenet\Model\Directpost::class) - ->willReturn($directpostMock); - $context = \Magento\TestFramework\Helper\Bootstrap::getObjectManager()->create( - \Magento\Backend\App\Action\Context::class, - [ - 'objectManager' => $objectManagerMock - ] - ); - - /** @var \Magento\Authorizenet\Controller\Directpost\Payment\Response $controller */ - $controller = $objectManager->create( - \Magento\Authorizenet\Controller\Directpost\Payment\Response::class, - [ - 'context' => $context - ] - ); - - // create one block for potential layout stack modification that should not affect response - /** @var \Magento\Authorizenet\Block\Adminhtml\Order\View\Info\FraudDetails $block */ - $block = $objectManager->get(\Magento\Framework\View\LayoutInterface::class) - ->createBlock(\Magento\Authorizenet\Block\Adminhtml\Order\View\Info\FraudDetails::class); - $block->setTemplate('Magento_Payment::order/view/info/fraud_details.phtml'); - - $response = $controller->execute(); - $output = $response->getLayout()->getOutput(); - - $expectedString = "{$controllerModule}/{$controllerName}/redirect/x_invoice_num/{$params['x_invoice_num']}/" - . "success/1/controller_action_name/{$controllerName}/"; - - $this->assertContains('window.location', $output); - $this->assertContains($expectedString, $output); - } - - /** - * @return array - */ - public function responseActionAuthorizeCaptureDeclineDataProvider() - { - $postArray = [ - 'x_response_code' => 1, - 'x_response_reason_code' => 1, - 'x_response_reason_text' => 'This transaction has been approved.', - 'x_avs_code' => 'Y', - 'x_auth_code' => 'G0L0XR', - 'x_trans_id' => '60016479791', - 'x_method' => 'CC', - 'x_card_type' => 'American Express', - 'x_account_number' => 'XXXX0002', - 'x_first_name' => 'Name', - 'x_last_name' => 'Surname', - 'x_company' => null, - 'x_address' => 'Address', - 'x_city' => 'Austin', - 'x_state' => 'Texas', - 'x_zip' => '78753', - 'x_country' => 'US', - 'x_phone' => '5127242323', - 'x_fax' => null, - 'x_email' => 'customer@example.com', - 'x_description' => null, - 'x_type' => 'auth_capture', - 'x_cust_id' => null, - 'x_ship_to_first_name' => null, - 'x_ship_to_last_name' => null, - 'x_ship_to_company' => null, - 'x_ship_to_address' => null, - 'x_ship_to_city' => null, - 'x_ship_to_state' => null, - 'x_ship_to_zip' => null, - 'x_ship_to_country' => null, - 'x_amount' => 100.00, - 'x_tax' => 0.00, - 'x_duty' => 0.00, - 'x_freight' => 0.00, - 'x_tax_exempt' => false, - 'x_po_num' => null, - 'x_SHA2_Hash' => null, - 'x_cvv2_resp_code' => 'P', - 'x_cavv_response' => 2, - 'x_test_request' => false, - 'controller_action_name' => 'directpost_payment', - 'is_secure' => null - ]; - return [ - 'error_hash' => [ - 'invoice_num' => '1231231', - 'x_MD5_Hash' => 'F9AE81A5DA36057D1312D71C904FCCF2', - 'error_msg' => 'The%20transaction%20was%20declined%20because%20the%20' - . 'response%20hash%20validation%20failed.', - 'post' => $postArray - ] - ]; - } - - /** - * @return array - */ - public function responseActionAuthorizeCaptureSuccessDataProvider() - { - $postArray = [ - 'x_response_code' => 1, - 'x_response_reason_code' => 1, - 'x_response_reason_text' => 'This transaction has been approved.', - 'x_avs_code' => 'Y', - 'x_auth_code' => 'G0L0XR', - 'x_trans_id' => '60016479791', - 'x_method' => 'CC', - 'x_card_type' => 'American Express', - 'x_account_number' => 'XXXX0002', - 'x_first_name' => 'Name', - 'x_last_name' => 'Surname', - 'x_company' => null, - 'x_address' => 'Address', - 'x_city' => 'Austin', - 'x_state' => 'Texas', - 'x_zip' => '78753', - 'x_country' => 'US', - 'x_phone' => '5127242323', - 'x_fax' => null, - 'x_email' => 'integrationtest@magento.com', - 'x_description' => null, - 'x_type' => 'auth_capture', - 'x_cust_id' => null, - 'x_ship_to_first_name' => null, - 'x_ship_to_last_name' => null, - 'x_ship_to_company' => null, - 'x_ship_to_address' => null, - 'x_ship_to_city' => null, - 'x_ship_to_state' => null, - 'x_ship_to_zip' => null, - 'x_ship_to_country' => null, - 'x_amount' => 120.15, - 'x_tax' => 0.00, - 'x_duty' => 0.00, - 'x_freight' => 0.00, - 'x_tax_exempt' => false, - 'x_po_num' => null, - 'x_SHA2_Hash' => null, - 'x_cvv2_resp_code' => 'P', - 'x_cavv_response' => 2, - 'x_test_request' => false, - 'controller_action_name' => 'directpost_payment', - 'is_secure' => null - ]; - return [ - 'success' => [ - 'x_MD5_Hash' => '35DCF749F7760193FB8254886E1D1522', - 'post' => $postArray - ], - ]; - } -} diff --git a/dev/tests/integration/testsuite/Magento/Authorizenet/Controller/Directpost/PaymentTest.php b/dev/tests/integration/testsuite/Magento/Authorizenet/Controller/Directpost/PaymentTest.php deleted file mode 100644 index f6ca9db995023..0000000000000 --- a/dev/tests/integration/testsuite/Magento/Authorizenet/Controller/Directpost/PaymentTest.php +++ /dev/null @@ -1,25 +0,0 @@ -<?php -/** - * Copyright © Magento, Inc. All rights reserved. - * See COPYING.txt for license details. - */ -namespace Magento\Authorizenet\Controller\Directpost; - -/** - * Class PaymentTest - */ -class PaymentTest extends \Magento\TestFramework\TestCase\AbstractController -{ - public function testResponseActionValidationFailed() - { - $this->getRequest()->setPostValue('controller_action_name', 'onepage'); - $this->dispatch('authorizenet/directpost_payment/response'); - // @codingStandardsIgnoreStart - $this->assertContains( - 'authorizenet/directpost_payment/redirect/success/0/error_msg/The%20transaction%20was' - . '%20declined%20because%20the%20response%20hash%20validation%20failed.', - // @codingStandardsIgnoreEnd - $this->getResponse()->getBody() - ); - } -} diff --git a/dev/tests/integration/testsuite/Magento/Authorizenet/Model/Directpost/RequestTest.php b/dev/tests/integration/testsuite/Magento/Authorizenet/Model/Directpost/RequestTest.php deleted file mode 100644 index 8f3cce9679953..0000000000000 --- a/dev/tests/integration/testsuite/Magento/Authorizenet/Model/Directpost/RequestTest.php +++ /dev/null @@ -1,102 +0,0 @@ -<?php -/** - * Copyright © Magento, Inc. All rights reserved. - * See COPYING.txt for license details. - */ -namespace Magento\Authorizenet\Model\Directpost; - -use Magento\Authorizenet\Model\Directpost; -use Magento\Framework\Api\FilterBuilder; -use Magento\Framework\Api\SearchCriteriaBuilder; -use Magento\Framework\App\ObjectManager; -use Magento\Sales\Api\Data\OrderInterface; -use Magento\Sales\Api\OrderRepositoryInterface; -use Magento\Sales\Model\Order; -use Magento\TestFramework\Helper\Bootstrap; -use PHPUnit\Framework\MockObject_MockObject as MockObject; - -/** - * Class contains tests for Authorize.net Direct Post request handler - */ -class RequestTest extends \PHPUnit\Framework\TestCase -{ - /** - * @var Order - */ - private $order; - - /** - * @var Request - */ - private $request; - - /** - * @var ObjectManager - */ - private $objectManager; - - protected function setUp() - { - $this->objectManager = Bootstrap::getObjectManager(); - - $this->order = $this->getOrder(); - $this->request = $this->objectManager->get(Request::class); - } - - /** - * @covers \Magento\Authorizenet\Model\Directpost\Request::setDataFromOrder - * @magentoDataFixture Magento/Authorizenet/_files/order.php - */ - public function testSetDataFromOrder() - { - $customerEmail = 'john.doe@example.com'; - $merchantEmail = 'merchant@example.com'; - - /** @var Directpost|MockObject $payment */ - $payment = $this->getMockBuilder(Directpost::class) - ->disableOriginalConstructor() - ->setMethods(['getConfigData']) - ->getMock(); - - $payment->expects(static::exactly(2)) - ->method('getConfigData') - ->willReturnMap([ - ['email_customer', null, $customerEmail], - ['merchant_email', null, $merchantEmail] - ]); - - $result = $this->request->setDataFromOrder($this->order, $payment); - - static::assertEquals('US', $result->getXCountry()); - static::assertEquals('UK', $result->getXShipToCountry()); - static::assertEquals($customerEmail, $result->getXEmailCustomer()); - static::assertEquals($merchantEmail, $result->getXMerchantEmail()); - } - - /** - * Get stored order - * @return Order - */ - private function getOrder() - { - /** @var FilterBuilder $filterBuilder */ - $filterBuilder = $this->objectManager->get(FilterBuilder::class); - $filters = [ - $filterBuilder->setField(OrderInterface::INCREMENT_ID) - ->setValue('100000002') - ->create() - ]; - - /** @var SearchCriteriaBuilder $searchCriteriaBuilder */ - $searchCriteriaBuilder = $this->objectManager->get(SearchCriteriaBuilder::class); - $searchCriteria = $searchCriteriaBuilder->addFilters($filters) - ->create(); - - $orderRepository = $this->objectManager->get(OrderRepositoryInterface::class); - $orders = $orderRepository->getList($searchCriteria) - ->getItems(); - - /** @var OrderInterface $order */ - return array_pop($orders); - } -} diff --git a/dev/tests/integration/testsuite/Magento/Authorizenet/Model/DirectpostTest.php b/dev/tests/integration/testsuite/Magento/Authorizenet/Model/DirectpostTest.php deleted file mode 100644 index ba4c4efd78f1b..0000000000000 --- a/dev/tests/integration/testsuite/Magento/Authorizenet/Model/DirectpostTest.php +++ /dev/null @@ -1,252 +0,0 @@ -<?php -/** - * Copyright © Magento, Inc. All rights reserved. - * See COPYING.txt for license details. - */ -namespace Magento\Authorizenet\Model; - -use Magento\Framework\Api\SearchCriteriaBuilder; -use Magento\Framework\App\ObjectManager; -use Magento\Framework\HTTP\ZendClient; -use Magento\Framework\HTTP\ZendClientFactory; -use Magento\Framework\Simplexml\Element; -use Magento\Sales\Api\Data\OrderInterface; -use Magento\Sales\Api\OrderRepositoryInterface; -use Magento\Sales\Model\Order; -use Magento\Sales\Model\Order\Payment; -use Magento\TestFramework\Helper\Bootstrap; -use PHPUnit_Framework_MockObject_MockObject as MockObject; - -/** - * Class contains tests for Direct Post integration - * - * @SuppressWarnings(PHPMD.CouplingBetweenObjects) - */ -class DirectpostTest extends \PHPUnit\Framework\TestCase -{ - /** - * @var ObjectManager - */ - private $objectManager; - - /** - * @var ZendClientFactory|MockObject - */ - private $httpClientFactory; - - /** - * @var Directpost - */ - private $directPost; - - protected function setUp() - { - $this->objectManager = Bootstrap::getObjectManager(); - - $this->httpClientFactory = $this->getMockBuilder(ZendClientFactory::class) - ->disableOriginalConstructor() - ->setMethods(['create']) - ->getMock(); - - $this->directPost = $this->objectManager->create(Directpost::class, [ - 'httpClientFactory' => $this->httpClientFactory - ]); - } - - /** - * @covers \Magento\Authorizenet\Model\Directpost::capture - * @magentoDataFixture Magento/Authorizenet/_files/order.php - */ - public function testCapture() - { - $amount = 120.15; - /** @var Payment $payment */ - $payment = $this->getPayment('100000002'); - $transactionId = '106235225'; - - /** @var ZendClient|MockObject $httpClient */ - $httpClient = $this->getMockBuilder(ZendClient::class) - ->disableOriginalConstructor() - ->setMethods(['setUri', 'setConfig', 'setParameterPost', 'setMethod', 'request']) - ->getMock(); - - $this->httpClientFactory->expects(static::once()) - ->method('create') - ->willReturn($httpClient); - - $response = $this->getMockBuilder('Zend_Http_Response') - ->disableOriginalConstructor() - ->setMethods(['getBody']) - ->getMock(); - $response->expects(static::once()) - ->method('getBody') - ->willReturn( - "1(~)1(~)1(~)This transaction has been approved.(~)AWZFTG(~)P(~){$transactionId}(~)100000002(~) - (~)120.15(~)CC(~)prior_auth_capture(~)(~)Anthony(~)Nealy(~)(~)Pearl St(~)Los Angeles(~)California - (~)10020(~)US(~)22-333-44(~)(~)customer@example.com(~)John(~)Doe(~) - (~)Bourne St(~)London(~)(~)DW23W(~)UK(~)0.00(~)(~){$amount}(~)(~) - (~)74B5D54ADFE98093A0FF6446(~)(~)(~)(~)(~)(~)(~)(~)(~)(~)(~)(~)(~)XXXX1111(~)Visa(~)(~)(~)(~)(~) - (~)(~)(~)(~)(~)(~)(~)(~)(~)(~)(~)(~)" - ); - - $httpClient->expects(static::once()) - ->method('request') - ->willReturn($response); - - $this->directPost->capture($payment, $amount); - - static::assertEquals($transactionId, $payment->getTransactionId()); - static::assertFalse($payment->getIsTransactionClosed()); - static::assertEquals('US', $payment->getOrder()->getBillingAddress()->getCountryId()); - static::assertEquals('UK', $payment->getOrder()->getShippingAddress()->getCountryId()); - } - - /** - * Verifies that order is placed in correct state according the action taken for a transaction that - * triggered one or more of the Advanced Fraud Detection Suite filters. - * - * @param string $filterAction - * @param string $orderId - * @param string $expectedOrderState - * - * @magentoConfigFixture current_store payment/authorizenet_directpost/trans_md5 TestHash - * @magentoConfigFixture current_store payment/authorizenet_directpost/login TestLogin - * @magentoDataFixture Magento/Authorizenet/_files/order.php - * @dataProvider fdsFilterActionDataProvider - */ - public function testProcessWithFdsFilterActionReportOnly($filterAction, $orderId, $expectedOrderState) - { - $responseBody = $this->getSuccessResponse($orderId); - $transactionService = $this->getTransactionService($filterAction); - $this->objectManager->addSharedInstance($transactionService, TransactionService::class); - - $this->directPost->process($responseBody); - - /** @var Payment $payment */ - $payment = $this->getPayment($orderId); - $this->objectManager->removeSharedInstance(TransactionService::class); - - static::assertEquals($expectedOrderState, $payment->getOrder()->getState()); - } - - /** - * @return array - */ - public function fdsFilterActionDataProvider() - { - return [ - [ - 'filter_action' => 'authAndHold', - 'order_id' => '100000003', - 'expected_order_state' => Order::STATE_PAYMENT_REVIEW - ], - [ - 'filter_action' => 'report', - 'order_id' => '100000004', - 'expected_order_state' => Order::STATE_COMPLETE - ], - ]; - } - - /** - * @param string $orderId - * @return array - */ - private function getSuccessResponse($orderId) - { - return [ - 'x_response_code' => '1', - 'x_response_reason_code' => '1', - 'x_response_reason_text' => 'This transaction has been approved.', - 'x_avs_code' => 'Y', - 'x_auth_code' => 'YWO2E2', - 'x_trans_id' => '40004862720', - 'x_method' => 'CC', - 'x_card_type' => 'Visa', - 'x_account_number' => 'XXXX1111', - 'x_first_name' => 'John', - 'x_last_name' => 'Smith', - 'x_company' => 'CompanyName', - 'x_address' => 'Green str, 67', - 'x_city' => 'CityM', - 'x_state' => 'Alabama', - 'x_zip' => '93930', - 'x_country' => 'US', - 'x_phone' => '3468676', - 'x_fax' => '04040404', - 'x_email' => 'user_1@example.com', - 'x_invoice_num' => $orderId, - 'x_description' => '', - 'x_type' => 'auth_only', - 'x_cust_id' => '', - 'x_ship_to_first_name' => 'John', - 'x_ship_to_last_name' => 'Smith', - 'x_ship_to_company' => 'CompanyName', - 'x_ship_to_address' => 'Green str, 67', - 'x_ship_to_city' => 'CityM', - 'x_ship_to_state' => 'Alabama', - 'x_ship_to_zip' => '93930', - 'x_ship_to_country' => 'US', - 'x_amount' => '120.15', - 'x_tax' => '0.00', - 'x_duty' => '0.00', - 'x_freight' => '5.00', - 'x_tax_exempt' => 'FALSE', - 'x_po_num' => '', - 'x_MD5_Hash' => 'C1CC5AB9D6F0481E240AD74DFF624584', - 'x_SHA2_Hash' => '', - 'x_cvv2_resp_code' => 'P', - 'x_cavv_response' => '2', - 'x_test_request' => 'false', - 'controller_action_name' => 'directpost_payment', - 'is_secure' => '1', - ]; - } - - /** - * Get order payment. - * - * @param string $orderId - * @return Payment - */ - private function getPayment($orderId) - { - /** @var SearchCriteriaBuilder $searchCriteriaBuilder */ - $searchCriteriaBuilder = $this->objectManager->get(SearchCriteriaBuilder::class); - $searchCriteria = $searchCriteriaBuilder->addFilter(OrderInterface::INCREMENT_ID, $orderId) - ->create(); - - $orderRepository = $this->objectManager->get(OrderRepositoryInterface::class); - $orders = $orderRepository->getList($searchCriteria) - ->getItems(); - - /** @var OrderInterface $order */ - $order = array_pop($orders); - return $order->getPayment(); - } - - /** - * Returns TransactionService mocked object with authorize predefined response. - * - * @param string $filterAction - * @return TransactionService|MockObject - */ - private function getTransactionService($filterAction) - { - $response = str_replace( - '{filterAction}', - $filterAction, - file_get_contents(__DIR__ . '/../_files/transaction_details.xml') - ); - - $transactionService = $this->getMockBuilder(TransactionService::class) - ->disableOriginalConstructor() - ->getMock(); - $transactionService->method('getTransactionDetails') - ->willReturn( - new Element($response) - ); - - return $transactionService; - } -} diff --git a/dev/tests/integration/testsuite/Magento/Authorizenet/_files/order.php b/dev/tests/integration/testsuite/Magento/Authorizenet/_files/order.php deleted file mode 100644 index b8d632f3a87af..0000000000000 --- a/dev/tests/integration/testsuite/Magento/Authorizenet/_files/order.php +++ /dev/null @@ -1,86 +0,0 @@ -<?php -/** - * Copyright © Magento, Inc. All rights reserved. - * See COPYING.txt for license details. - */ -use Magento\Sales\Api\OrderRepositoryInterface; -use Magento\Sales\Model\Order; -use Magento\Sales\Model\Order\Address; -use Magento\Sales\Model\Order\Payment; -use Magento\TestFramework\Helper\Bootstrap; - -$objectManager = Bootstrap::getObjectManager(); - -$amount = 120.15; - -/** @var Payment $payment */ -$payment = $objectManager->create(Payment::class); -$payment - ->setMethod('authorizenet_directpost') - ->setAnetTransType('AUTH_ONLY') - ->setBaseAmountAuthorized($amount) - ->setPoNumber('10101200'); - -/** @var Address\ $billingAddress */ -$billingAddress = $objectManager->create(Address::class, [ - 'data' => [ - 'firstname' => 'John', - 'lastname' => 'Doe', - 'email' => 'customer@example.com', - 'street' => 'Pearl St', - 'city' => 'Los Angeles', - 'region' => 'CA', - 'postcode' => '10020', - 'country_id' => 'US', - 'telephone' => '22-333-44', - 'address_type' => 'billing' - ] -]); - -$shippingAddress = $objectManager->create(Address::class, [ - 'data' => [ - 'firstname' => 'John', - 'lastname' => 'Doe', - 'email' => 'customer@example.com', - 'street' => 'Bourne St', - 'city' => 'London', - 'postcode' => 'DW23W', - 'country_id' => 'UK', - 'telephone' => '22-333-44', - 'address_type' => 'billing' - ] -]); - -/** @var Order $order */ -$order = $objectManager->create(Order::class); -$order->setIncrementId('100000002') - ->setQuoteId(2) - ->setIncrementId('100000002') - ->setBaseGrandTotal($amount) - ->setBaseCurrencyCode('USD') - ->setBaseTaxAmount($amount) - ->setBaseShippingAmount($amount) - ->setCustomerEmail('customer@example.com') - ->setBillingAddress($billingAddress) - ->setShippingAddress($shippingAddress) - ->setPayment($payment); - -/** @var OrderRepositoryInterface $orderRepository */ -$orderRepository = $objectManager->get(OrderRepositoryInterface::class); -$orderRepository->save($order); - -$clonedOrder = clone $order; -$clonedOrder->setIncrementId('100000003') - ->setId(null) - ->setBillingAddress($billingAddress->setId(null)) - ->setShippingAddress($shippingAddress->setId(null)) - ->setPayment($payment->setId(null)); -$orderRepository->save($clonedOrder); - -$clonedOrder = clone $order; -$clonedOrder->setIncrementId('100000004') - ->setId(null) - ->setBillingAddress($billingAddress->setId(null)) - ->setShippingAddress($shippingAddress->setId(null)) - ->setPayment($payment->setId(null)); -$orderRepository->save($clonedOrder); diff --git a/dev/tests/integration/testsuite/Magento/Authorizenet/_files/transaction_details.xml b/dev/tests/integration/testsuite/Magento/Authorizenet/_files/transaction_details.xml deleted file mode 100644 index 98b9f258b0625..0000000000000 --- a/dev/tests/integration/testsuite/Magento/Authorizenet/_files/transaction_details.xml +++ /dev/null @@ -1,81 +0,0 @@ -<?xml version="1.0" encoding="utf-8"?> -<!-- -/** - * Copyright © Magento, Inc. All rights reserved. - * See COPYING.txt for license details. - */ ---> -<getTransactionDetailsResponse> - <messages> - <resultCode>Ok</resultCode> - <message> - <code>I00001</code> - <text>Successful.</text> - </message> - </messages> - <transaction> - <transId>40004862720</transId> - <submitTimeUTC>2017-06-12T13:33:10.1Z</submitTimeUTC> - <submitTimeLocal>2017-06-12T06:33:10.1</submitTimeLocal> - <transactionType>authOnlyTransaction</transactionType> - <transactionStatus>authorizedPendingCapture</transactionStatus> - <responseCode>1</responseCode> - <responseReasonCode>1</responseReasonCode> - <responseReasonDescription>Approval</responseReasonDescription> - <authCode>YWO2E2</authCode> - <AVSResponse>Y</AVSResponse> - <cardCodeResponse>P</cardCodeResponse> - <FDSFilterAction>{filterAction}</FDSFilterAction> - <FDSFilters> - <FDSFilter> - <name>Amount Filter</name> - <action>{filterAction}</action> - </FDSFilter> - </FDSFilters> - <order> - <invoiceNumber>100000002</invoiceNumber> - </order> - <authAmount>120.15</authAmount> - <settleAmount>120.15</settleAmount> - <shipping> - <amount>5.00</amount> - </shipping> - <taxExempt>false</taxExempt> - <payment> - <creditCard> - <cardNumber>XXXX1111</cardNumber> - <expirationDate>XXXX</expirationDate> - <cardType>Visa</cardType> - </creditCard> - </payment> - <customer> - <email>user_1@example.com</email> - </customer> - <billTo> - <firstName>John</firstName> - <lastName>Smith</lastName> - <company>CompanyName</company> - <address>Green str, 67</address> - <city>CityM</city> - <state>Alabama</state> - <zip>93930</zip> - <country>US</country> - <phoneNumber>3468676</phoneNumber> - <faxNumber>04040404</faxNumber> - </billTo> - <shipTo> - <firstName>John</firstName> - <lastName>Smith</lastName> - <company>CompanyName</company> - <address>Green str, 67</address> - <city>CityM</city> - <state>Alabama</state> - <zip>93930</zip> - <country>US</country> - </shipTo> - <recurringBilling>false</recurringBilling> - <customerIP>195.14.124.5</customerIP> - <product>Card Not Present</product> - <marketType>eCommerce</marketType> - </transaction> -</getTransactionDetailsResponse> diff --git a/dev/tests/integration/testsuite/Magento/AuthorizenetAcceptjs/Fixture/full_order_with_capture.php b/dev/tests/integration/testsuite/Magento/AuthorizenetAcceptjs/Fixture/full_order_with_capture.php deleted file mode 100644 index 0b1e8196ef007..0000000000000 --- a/dev/tests/integration/testsuite/Magento/AuthorizenetAcceptjs/Fixture/full_order_with_capture.php +++ /dev/null @@ -1,69 +0,0 @@ -<?php -/** - * Copyright © Magento, Inc. All rights reserved. - * See COPYING.txt for license details. - */ - -declare(strict_types=1); - -use Magento\AuthorizenetAcceptjs\Gateway\Config; -use Magento\Sales\Api\InvoiceRepositoryInterface; -use Magento\Sales\Model\Order\Payment; -use Magento\Sales\Model\OrderRepository; -use Magento\Sales\Model\Service\InvoiceService; -use Magento\TestFramework\Helper\Bootstrap; -use Magento\Sales\Api\TransactionRepositoryInterface; -use Magento\Sales\Model\Order\Payment\Transaction; -use Magento\Sales\Model\Order\Payment\Transaction\BuilderInterface as TransactionBuilder; - -// phpcs:ignore Magento2.Security.IncludeFile.FoundIncludeFile -$order = include __DIR__ . '/../_files/full_order.php'; - -$objectManager = Bootstrap::getObjectManager(); - -/** @var Payment $payment */ -$payment = $order->getPayment(); -$payment->setMethod(Config::METHOD); -$payment->setAuthorizationTransaction(false); -$payment->setParentTransactionId(4321); - -/** @var OrderRepository $orderRepo */ -$orderRepo = $objectManager->get(OrderRepository::class); -$orderRepo->save($order); - -/** @var InvoiceService $invoiceService */ -$invoiceService = $objectManager->get(InvoiceService::class); -$invoice = $invoiceService->prepareInvoice($order); -$invoice->setIncrementId('100000001'); -$invoice->register(); - -/** @var InvoiceRepositoryInterface $invoiceRepository */ -$invoiceRepository = $objectManager->get(InvoiceRepositoryInterface::class); -$invoice = $invoiceRepository->save($invoice); - - -/** @var \Magento\Sales\Model\Order\CreditmemoFactory $creditmemoFactory */ -$creditmemoFactory = $objectManager->get(\Magento\Sales\Model\Order\CreditmemoFactory::class); -$creditmemo = $creditmemoFactory->createByInvoice($invoice, $invoice->getData()); -$creditmemo->setOrder($order); -$creditmemo->setState(Magento\Sales\Model\Order\Creditmemo::STATE_OPEN); -$creditmemo->setIncrementId('100000001'); - -/** @var \Magento\Sales\Api\CreditmemoRepositoryInterface $creditmemoRepository */ -$creditmemoRepository = $objectManager->get(\Magento\Sales\Api\CreditmemoRepositoryInterface::class); -$creditmemoRepository->save($creditmemo); - -/** @var TransactionBuilder $transactionBuilder */ -$transactionBuilder = $objectManager->create(TransactionBuilder::class); -$transactionAuthorize = $transactionBuilder->setPayment($payment) - ->setOrder($order) - ->setTransactionId(1234) - ->build(Transaction::TYPE_AUTH); -$transactionCapture = $transactionBuilder->setPayment($payment) - ->setOrder($order) - ->setTransactionId(4321) - ->build(Transaction::TYPE_CAPTURE); - -$transactionRepository = $objectManager->create(TransactionRepositoryInterface::class); -$transactionRepository->save($transactionAuthorize); -$transactionRepository->save($transactionCapture); diff --git a/dev/tests/integration/testsuite/Magento/AuthorizenetAcceptjs/Fixture/full_order_with_capture_rollback.php b/dev/tests/integration/testsuite/Magento/AuthorizenetAcceptjs/Fixture/full_order_with_capture_rollback.php deleted file mode 100644 index 1a2cb2532fe52..0000000000000 --- a/dev/tests/integration/testsuite/Magento/AuthorizenetAcceptjs/Fixture/full_order_with_capture_rollback.php +++ /dev/null @@ -1,28 +0,0 @@ -<?php -/** - * Copyright © Magento, Inc. All rights reserved. - * See COPYING.txt for license details. - */ -declare(strict_types=1); - -use Magento\Framework\Api\SearchCriteriaBuilder; -use Magento\Sales\Api\OrderRepositoryInterface; -use Magento\TestFramework\ObjectManager; - -$objectManager = ObjectManager::getInstance(); - -/** @var SearchCriteriaBuilder $searchCriteriaBuilder */ -$searchCriteriaBuilder = $objectManager->get(SearchCriteriaBuilder::class); -$searchCriteria = $searchCriteriaBuilder->addFilter('increment_id', '100000001') - ->create(); - -/** @var OrderRepositoryInterface $orderRepository */ -$orderRepository = $objectManager->get(OrderRepositoryInterface::class); -$items = $orderRepository->getList($searchCriteria) - ->getItems(); - -foreach ($items as $item) { - $orderRepository->delete($item); -} - -require __DIR__ . '/../../../Magento/Catalog/_files/product_simple_rollback.php'; diff --git a/dev/tests/integration/testsuite/Magento/AuthorizenetAcceptjs/Fixture/order_auth_only.php b/dev/tests/integration/testsuite/Magento/AuthorizenetAcceptjs/Fixture/order_auth_only.php deleted file mode 100644 index b1d0521c9c610..0000000000000 --- a/dev/tests/integration/testsuite/Magento/AuthorizenetAcceptjs/Fixture/order_auth_only.php +++ /dev/null @@ -1,74 +0,0 @@ -<?php -/** - * Copyright © Magento, Inc. All rights reserved. - * See COPYING.txt for license details. - */ -declare(strict_types=1); - -use Magento\Sales\Api\OrderRepositoryInterface; -use Magento\Sales\Model\Order; -use Magento\Sales\Model\Order\Address; -use Magento\Sales\Model\Order\Item as OrderItem; -use Magento\Store\Model\StoreManagerInterface; -use Magento\TestFramework\Helper\Bootstrap; -use Magento\TestFramework\ObjectManager; -use Magento\Sales\Api\TransactionRepositoryInterface; -use Magento\Sales\Model\Order\Payment\Transaction; -use Magento\Sales\Model\Order\Payment\Transaction\BuilderInterface as TransactionBuilder; - -/** @var ObjectManager $objectManager */ -$objectManager = Bootstrap::getObjectManager(); - -$addressData = include __DIR__ . '/../../Sales/_files/address_data.php'; -require __DIR__ . '/../../../Magento/Catalog/_files/product_simple.php'; - -$billingAddress = $objectManager->create(Address::class, ['data' => $addressData]); -$billingAddress->setAddressType('billing'); - -$shippingAddress = clone $billingAddress; -$shippingAddress->setId(null) - ->setAddressType('shipping'); - -/** @var OrderItem $orderItem */ -$orderItem = $objectManager->create(OrderItem::class); -$orderItem->setProductId($product->getId()) - ->setQtyOrdered(2) - ->setBasePrice($product->getPrice()) - ->setPrice($product->getPrice()) - ->setRowTotal($product->getPrice()) - ->setProductType('simple'); - -require __DIR__ . '/payment.php'; - -$order = $objectManager->create(Order::class); -$order->setIncrementId('100000002') - ->setSubtotal($product->getPrice() * 2) - ->setBaseSubtotal($product->getPrice() * 2) - ->setCustomerEmail('admin@example.com') - ->setCustomerIsGuest(true) - ->setBillingAddress($billingAddress) - ->setShippingAddress($shippingAddress) - ->setStoreId( - $objectManager->get(StoreManagerInterface::class)->getStore() - ->getId() - ) - ->addItem($orderItem) - ->setPayment($payment); - -$payment->setParentTransactionId(1234); - -/** @var OrderRepositoryInterface $orderRepository */ -$orderRepository = $objectManager->get(OrderRepositoryInterface::class); -$orderRepository->save($order); - -/** @var TransactionBuilder $transactionBuilder */ -$transactionBuilder = $objectManager->create(TransactionBuilder::class); -$transactionAuthorize = $transactionBuilder->setPayment($payment) - ->setOrder($order) - ->setTransactionId(1234) - ->build(Transaction::TYPE_AUTH); - -$transactionAuthorize->setAdditionalInformation('real_transaction_id', '1234'); - -$transactionRepository = $objectManager->create(TransactionRepositoryInterface::class); -$transactionRepository->save($transactionAuthorize); diff --git a/dev/tests/integration/testsuite/Magento/AuthorizenetAcceptjs/Fixture/order_auth_only_rollback.php b/dev/tests/integration/testsuite/Magento/AuthorizenetAcceptjs/Fixture/order_auth_only_rollback.php deleted file mode 100644 index 5a65a1fc0d0c7..0000000000000 --- a/dev/tests/integration/testsuite/Magento/AuthorizenetAcceptjs/Fixture/order_auth_only_rollback.php +++ /dev/null @@ -1,9 +0,0 @@ -<?php -/** - * Copyright © Magento, Inc. All rights reserved. - * See COPYING.txt for license details. - */ - -declare(strict_types=1); - -require __DIR__ . '/order_captured_rollback.php'; diff --git a/dev/tests/integration/testsuite/Magento/AuthorizenetAcceptjs/Fixture/order_captured.php b/dev/tests/integration/testsuite/Magento/AuthorizenetAcceptjs/Fixture/order_captured.php deleted file mode 100644 index 9bfc863df7de5..0000000000000 --- a/dev/tests/integration/testsuite/Magento/AuthorizenetAcceptjs/Fixture/order_captured.php +++ /dev/null @@ -1,77 +0,0 @@ -<?php -/** - * Copyright © Magento, Inc. All rights reserved. - * See COPYING.txt for license details. - */ -declare(strict_types=1); - -use Magento\Sales\Api\OrderRepositoryInterface; -use Magento\Sales\Model\Order; -use Magento\Sales\Model\Order\Address; -use Magento\Sales\Model\Order\Item as OrderItem; -use Magento\Store\Model\StoreManagerInterface; -use Magento\TestFramework\Helper\Bootstrap; -use Magento\TestFramework\ObjectManager; -use Magento\Sales\Api\TransactionRepositoryInterface; -use Magento\Sales\Model\Order\Payment\Transaction; -use Magento\Sales\Model\Order\Payment\Transaction\BuilderInterface as TransactionBuilder; - -/** @var ObjectManager $objectManager */ -$objectManager = Bootstrap::getObjectManager(); - -$addressData = include __DIR__ . '/../../Sales/_files/address_data.php'; -require __DIR__ . '/../../../Magento/Catalog/_files/product_simple.php'; - -$billingAddress = $objectManager->create(Address::class, ['data' => $addressData]); -$billingAddress->setAddressType('billing'); - -$shippingAddress = clone $billingAddress; -$shippingAddress->setId(null) - ->setAddressType('shipping'); - -/** @var OrderItem $orderItem */ -$orderItem = $objectManager->create(OrderItem::class); -$orderItem->setProductId($product->getId()) - ->setQtyOrdered(2) - ->setBasePrice($product->getPrice()) - ->setPrice($product->getPrice()) - ->setRowTotal($product->getPrice()) - ->setProductType('simple'); - -require __DIR__ . '/payment.php'; - -$order = $objectManager->create(Order::class); -$order->setIncrementId('100000002') - ->setSubtotal($product->getPrice() * 2) - ->setBaseSubtotal($product->getPrice() * 2) - ->setCustomerEmail('admin@example.com') - ->setCustomerIsGuest(true) - ->setBillingAddress($billingAddress) - ->setShippingAddress($shippingAddress) - ->setStoreId( - $objectManager->get(StoreManagerInterface::class)->getStore() - ->getId() - ) - ->addItem($orderItem) - ->setPayment($payment); - -$payment->setParentTransactionId(4321); - -/** @var OrderRepositoryInterface $orderRepository */ -$orderRepository = $objectManager->get(OrderRepositoryInterface::class); -$orderRepository->save($order); - -/** @var TransactionBuilder $transactionBuilder */ -$transactionBuilder = $objectManager->create(TransactionBuilder::class); -$transactionAuthorize = $transactionBuilder->setPayment($payment) - ->setOrder($order) - ->setTransactionId(1234) - ->build(Transaction::TYPE_AUTH); -$transactionCapture = $transactionBuilder->setPayment($payment) - ->setOrder($order) - ->setTransactionId(4321) - ->build(Transaction::TYPE_CAPTURE); - -$transactionRepository = $objectManager->create(TransactionRepositoryInterface::class); -$transactionRepository->save($transactionAuthorize); -$transactionRepository->save($transactionCapture); diff --git a/dev/tests/integration/testsuite/Magento/AuthorizenetAcceptjs/Fixture/order_captured_rollback.php b/dev/tests/integration/testsuite/Magento/AuthorizenetAcceptjs/Fixture/order_captured_rollback.php deleted file mode 100644 index a2da0b639e98d..0000000000000 --- a/dev/tests/integration/testsuite/Magento/AuthorizenetAcceptjs/Fixture/order_captured_rollback.php +++ /dev/null @@ -1,28 +0,0 @@ -<?php -/** - * Copyright © Magento, Inc. All rights reserved. - * See COPYING.txt for license details. - */ -declare(strict_types=1); - -use Magento\Framework\Api\SearchCriteriaBuilder; -use Magento\Sales\Api\OrderRepositoryInterface; -use Magento\TestFramework\ObjectManager; - -$objectManager = ObjectManager::getInstance(); - -/** @var SearchCriteriaBuilder $searchCriteriaBuilder */ -$searchCriteriaBuilder = $objectManager->get(SearchCriteriaBuilder::class); -$searchCriteria = $searchCriteriaBuilder->addFilter('increment_id', '100000002') - ->create(); - -/** @var OrderRepositoryInterface $orderRepository */ -$orderRepository = $objectManager->get(OrderRepositoryInterface::class); -$items = $orderRepository->getList($searchCriteria) - ->getItems(); - -foreach ($items as $item) { - $orderRepository->delete($item); -} - -require __DIR__ . '/../../../Magento/Catalog/_files/product_simple_rollback.php'; diff --git a/dev/tests/integration/testsuite/Magento/AuthorizenetAcceptjs/Fixture/payment.php b/dev/tests/integration/testsuite/Magento/AuthorizenetAcceptjs/Fixture/payment.php deleted file mode 100644 index 5b15e356a7d8d..0000000000000 --- a/dev/tests/integration/testsuite/Magento/AuthorizenetAcceptjs/Fixture/payment.php +++ /dev/null @@ -1,19 +0,0 @@ -<?php -/** - * Copyright © Magento, Inc. All rights reserved. - * See COPYING.txt for license details. - */ -declare(strict_types=1); - -use Magento\AuthorizenetAcceptjs\Gateway\Config; -use Magento\Sales\Model\Order\Payment; -use Magento\TestFramework\Helper\Bootstrap; -use Magento\TestFramework\ObjectManager; - -/** @var ObjectManager $objectManager */ -$objectManager = Bootstrap::getObjectManager(); - -/** @var Payment $payment */ -$payment = $objectManager->create(Payment::class); -$payment->setMethod(Config::METHOD); -$payment->setAuthorizationTransaction(true); diff --git a/dev/tests/integration/testsuite/Magento/AuthorizenetAcceptjs/Gateway/AbstractTest.php b/dev/tests/integration/testsuite/Magento/AuthorizenetAcceptjs/Gateway/AbstractTest.php deleted file mode 100644 index f1458a19012f3..0000000000000 --- a/dev/tests/integration/testsuite/Magento/AuthorizenetAcceptjs/Gateway/AbstractTest.php +++ /dev/null @@ -1,91 +0,0 @@ -<?php -/** - * Copyright © Magento, Inc. All rights reserved. - * See COPYING.txt for license details. - */ - -declare(strict_types=1); - -namespace Magento\AuthorizenetAcceptjs\Gateway; - -use Magento\Framework\Api\SearchCriteriaBuilder; -use Magento\Framework\App\Area; -use Magento\Framework\HTTP\ZendClient; -use Magento\Framework\HTTP\ZendClientFactory; -use PHPUnit\Framework\MockObject\Builder\InvocationMocker; -use PHPUnit\Framework\MockObject\MockObject; -use PHPUnit\Framework\TestCase; -use Magento\TestFramework\Helper\Bootstrap; -use Magento\TestFramework\ObjectManager; -use Magento\Payment\Gateway\Data\PaymentDataObjectFactory; -use Magento\Quote\Model\Quote\PaymentFactory; -use Magento\Sales\Api\OrderRepositoryInterface; -use Magento\Sales\Model\Order; -use Zend_Http_Response; - -abstract class AbstractTest extends TestCase -{ - /** - * @var ObjectManager - */ - protected $objectManager; - - /** - * @var ZendClient|MockObject|InvocationMocker - */ - protected $clientMock; - - /** - * @var PaymentFactory - */ - protected $paymentFactory; - - /** - * @var Zend_Http_Response - */ - protected $responseMock; - - /** - * @throws \Magento\Framework\Exception\LocalizedException - */ - protected function setUp() - { - $bootstrap = Bootstrap::getInstance(); - $bootstrap->loadArea(Area::AREA_FRONTEND); - $this->objectManager = Bootstrap::getObjectManager(); - $this->clientMock = $this->createMock(ZendClient::class); - $this->responseMock = $this->createMock(Zend_Http_Response::class); - $this->clientMock->method('request') - ->willReturn($this->responseMock); - $this->clientMock->method('setUri') - ->with('https://apitest.authorize.net/xml/v1/request.api'); - $clientFactoryMock = $this->createMock(ZendClientFactory::class); - $clientFactoryMock->method('create') - ->willReturn($this->clientMock); - /** @var PaymentDataObjectFactory $paymentFactory */ - $this->paymentFactory = $this->objectManager->get(PaymentDataObjectFactory::class); - $this->objectManager->addSharedInstance($clientFactoryMock, ZendClientFactory::class); - } - - protected function tearDown() - { - $this->objectManager->removeSharedInstance(ZendClientFactory::class); - parent::tearDown(); - } - - protected function getOrderWithIncrementId(string $incrementId): Order - { - /** @var OrderRepositoryInterface $orderRepository */ - $orderRepository = $this->objectManager->get(OrderRepositoryInterface::class); - $searchCriteria = $this->objectManager->get(SearchCriteriaBuilder::class) - ->addFilter('increment_id', $incrementId) - ->create(); - /** @var Order $order */ - $order = current( - $orderRepository->getList($searchCriteria) - ->getItems() - ); - - return $order; - } -} diff --git a/dev/tests/integration/testsuite/Magento/AuthorizenetAcceptjs/Gateway/Command/AcceptFdsCommandTest.php b/dev/tests/integration/testsuite/Magento/AuthorizenetAcceptjs/Gateway/Command/AcceptFdsCommandTest.php deleted file mode 100644 index 394d9de6684c4..0000000000000 --- a/dev/tests/integration/testsuite/Magento/AuthorizenetAcceptjs/Gateway/Command/AcceptFdsCommandTest.php +++ /dev/null @@ -1,49 +0,0 @@ -<?php -/** - * Copyright © Magento, Inc. All rights reserved. - * See COPYING.txt for license details. - */ - -declare(strict_types=1); - -namespace Magento\AuthorizenetAcceptjs\Gateway\Command; - -use Magento\AuthorizenetAcceptjs\Gateway\AbstractTest; -use Magento\Payment\Gateway\Command\CommandPoolInterface; -use Magento\Sales\Model\Order\Payment; - -class AcceptFdsCommandTest extends AbstractTest -{ - /** - * @magentoConfigFixture default_store payment/authorizenet_acceptjs/environment sandbox - * @magentoConfigFixture default_store payment/authorizenet_acceptjs/login someusername - * @magentoConfigFixture default_store payment/authorizenet_acceptjs/trans_key somepassword - * @magentoConfigFixture default_store payment/authorizenet_acceptjs/trans_signature_key abc - * @magentoDataFixture Magento/AuthorizenetAcceptjs/Fixture/order_auth_only.php - */ - public function testAcceptFdsCommand() - { - /** @var CommandPoolInterface $commandPool */ - $commandPool = $this->objectManager->get('AuthorizenetAcceptjsCommandPool'); - $command = $commandPool->get('accept_fds'); - - $order = $this->getOrderWithIncrementId('100000002'); - $payment = $order->getPayment(); - $paymentDO = $this->paymentFactory->create($payment); - - $expectedRequest = include __DIR__ . '/../../_files/expected_request/accept_fds.php'; - $response = include __DIR__ . '/../../_files/response/generic_success.php'; - - $this->clientMock->expects($this->once()) - ->method('setRawData') - ->with(json_encode($expectedRequest), 'application/json'); - - $this->responseMock->expects($this->once()) - ->method('getBody') - ->willReturn(json_encode($response)); - - $command->execute([ - 'payment' => $paymentDO - ]); - } -} diff --git a/dev/tests/integration/testsuite/Magento/AuthorizenetAcceptjs/Gateway/Command/AuthorizeCommandTest.php b/dev/tests/integration/testsuite/Magento/AuthorizenetAcceptjs/Gateway/Command/AuthorizeCommandTest.php deleted file mode 100644 index 9affd80be0600..0000000000000 --- a/dev/tests/integration/testsuite/Magento/AuthorizenetAcceptjs/Gateway/Command/AuthorizeCommandTest.php +++ /dev/null @@ -1,70 +0,0 @@ -<?php -/** - * Copyright © Magento, Inc. All rights reserved. - * See COPYING.txt for license details. - */ - -declare(strict_types=1); - -namespace Magento\AuthorizenetAcceptjs\Gateway\Command; - -use Magento\AuthorizenetAcceptjs\Gateway\AbstractTest; -use Magento\Payment\Gateway\Command\CommandPoolInterface; -use Magento\Sales\Model\Order\Payment; -use Magento\Sales\Model\Order\Payment\Transaction; - -class AuthorizeCommandTest extends AbstractTest -{ - /** - * @magentoConfigFixture default_store payment/authorizenet_acceptjs/environment sandbox - * @magentoConfigFixture default_store payment/authorizenet_acceptjs/login someusername - * @magentoConfigFixture default_store payment/authorizenet_acceptjs/trans_key somepassword - * @magentoConfigFixture default_store payment/authorizenet_acceptjs/trans_signature_key abc - */ - public function testAuthorizeCommand() - { - /** @var CommandPoolInterface $commandPool */ - $commandPool = $this->objectManager->get('AuthorizenetAcceptjsCommandPool'); - $command = $commandPool->get('authorize'); - - $order = include __DIR__ . '/../../_files/full_order.php'; - $payment = $order->getPayment(); - - $paymentDO = $this->paymentFactory->create($payment); - - $expectedRequest = include __DIR__ . '/../../_files/expected_request/authorize.php'; - $response = include __DIR__ . '/../../_files/response/authorize.php'; - - $this->clientMock->method('setRawData') - ->with(json_encode($expectedRequest), 'application/json'); - - $this->responseMock->method('getBody') - ->willReturn(json_encode($response)); - - $command->execute([ - 'payment' => $paymentDO, - 'amount' => 100.00 - ]); - - /** @var Payment $payment */ - $rawDetails = [ - 'authCode' => 'abc123', - 'avsResultCode' => 'Y', - 'cvvResultCode' => 'P', - 'cavvResultCode' => '2', - 'accountType' => 'Visa', - ]; - $this->assertSame('1111', $payment->getCcLast4()); - $this->assertSame('Y', $payment->getCcAvsStatus()); - $this->assertFalse($payment->getData('is_transaction_closed')); - - $transactionDetails = $payment->getTransactionAdditionalInfo(); - foreach ($rawDetails as $key => $value) { - $this->assertSame($value, $payment->getAdditionalInformation($key)); - $this->assertSame($value, $transactionDetails[Transaction::RAW_DETAILS][$key]); - } - - $this->assertSame('123456', $payment->getTransactionId()); - $this->assertSame('123456', $transactionDetails['real_transaction_id']); - } -} diff --git a/dev/tests/integration/testsuite/Magento/AuthorizenetAcceptjs/Gateway/Command/CancelCommandTest.php b/dev/tests/integration/testsuite/Magento/AuthorizenetAcceptjs/Gateway/Command/CancelCommandTest.php deleted file mode 100644 index aa606a50ae67a..0000000000000 --- a/dev/tests/integration/testsuite/Magento/AuthorizenetAcceptjs/Gateway/Command/CancelCommandTest.php +++ /dev/null @@ -1,62 +0,0 @@ -<?php -/** - * Copyright © Magento, Inc. All rights reserved. - * See COPYING.txt for license details. - */ - -declare(strict_types=1); - -namespace Magento\AuthorizenetAcceptjs\Gateway\Command; - -use Magento\AuthorizenetAcceptjs\Gateway\AbstractTest; -use Magento\Payment\Gateway\Command\CommandPoolInterface; -use Magento\Sales\Model\Order\Payment; - -class CancelCommandTest extends AbstractTest -{ - /** - * @magentoConfigFixture default_store payment/authorizenet_acceptjs/environment sandbox - * @magentoConfigFixture default_store payment/authorizenet_acceptjs/login someusername - * @magentoConfigFixture default_store payment/authorizenet_acceptjs/trans_key somepassword - * @magentoConfigFixture default_store payment/authorizenet_acceptjs/trans_signature_key abc - * @magentoDataFixture Magento/AuthorizenetAcceptjs/Fixture/order_auth_only.php - * @dataProvider aliasesProvider - */ - public function testCancelCommand(string $commandName) - { - /** @var CommandPoolInterface $commandPool */ - $commandPool = $this->objectManager->get('AuthorizenetAcceptjsCommandPool'); - $command = $commandPool->get($commandName); - - $order = $this->getOrderWithIncrementId('100000002'); - $payment = $order->getPayment(); - $paymentDO = $this->paymentFactory->create($payment); - - $expectedRequest = include __DIR__ . '/../../_files/expected_request/void.php'; - $response = include __DIR__ . '/../../_files/response/void.php'; - - $this->clientMock->method('setRawData') - ->with(json_encode($expectedRequest), 'application/json'); - - $this->responseMock->method('getBody') - ->willReturn(json_encode($response)); - - $command->execute([ - 'payment' => $paymentDO - ]); - - /** @var Payment $payment */ - - $this->assertTrue($payment->getIsTransactionClosed()); - $this->assertTrue($payment->getShouldCloseParentTransaction()); - $this->assertArrayNotHasKey('real_transaction_id', $payment->getTransactionAdditionalInfo()); - } - - public function aliasesProvider() - { - return [ - ['cancel'], - ['deny_payment'] - ]; - } -} diff --git a/dev/tests/integration/testsuite/Magento/AuthorizenetAcceptjs/Gateway/Command/FetchTransactionInfoCommandTest.php b/dev/tests/integration/testsuite/Magento/AuthorizenetAcceptjs/Gateway/Command/FetchTransactionInfoCommandTest.php deleted file mode 100644 index 1651dfc7db3d9..0000000000000 --- a/dev/tests/integration/testsuite/Magento/AuthorizenetAcceptjs/Gateway/Command/FetchTransactionInfoCommandTest.php +++ /dev/null @@ -1,184 +0,0 @@ -<?php -/** - * Copyright © Magento, Inc. All rights reserved. - * See COPYING.txt for license details. - */ - -declare(strict_types=1); - -namespace Magento\AuthorizenetAcceptjs\Gateway\Command; - -use Magento\AuthorizenetAcceptjs\Gateway\AbstractTest; -use Magento\Payment\Gateway\Command\CommandPoolInterface; -use Magento\Sales\Model\Order\Payment; - -class FetchTransactionInfoCommandTest extends AbstractTest -{ - /** - * @magentoConfigFixture default_store payment/authorizenet_acceptjs/environment sandbox - * @magentoConfigFixture default_store payment/authorizenet_acceptjs/login someusername - * @magentoConfigFixture default_store payment/authorizenet_acceptjs/trans_key somepassword - * @magentoConfigFixture default_store payment/authorizenet_acceptjs/trans_signature_key abc - * @magentoConfigFixture default_store payment/authorizenet_acceptjs/transactionSyncKeys transId,transactionType - * @magentoDataFixture Magento/AuthorizenetAcceptjs/Fixture/order_auth_only.php - */ - public function testTransactionApproved() - { - /** @var CommandPoolInterface $commandPool */ - $commandPool = $this->objectManager->get('AuthorizenetAcceptjsCommandPool'); - $command = $commandPool->get('fetch_transaction_information'); - - $order = $this->getOrderWithIncrementId('100000002'); - $payment = $order->getPayment(); - $paymentDO = $this->paymentFactory->create($payment); - - $expectedRequest = include __DIR__ . '/../../_files/expected_request/transaction_details_authorized.php'; - $response = include __DIR__ . '/../../_files/response/transaction_details_authorized.php'; - - $this->clientMock->method('setRawData') - ->with(json_encode($expectedRequest), 'application/json'); - - $this->responseMock->method('getBody') - ->willReturn(json_encode($response)); - - $result = $command->execute([ - 'payment' => $paymentDO - ]); - - $expected = [ - 'transId' => '1234', - 'transactionType' => 'authOnlyTransaction' - ]; - $this->assertSame($expected, $result); - - /** @var Payment $payment */ - $this->assertTrue($payment->getIsTransactionApproved()); - $this->assertFalse($payment->getIsTransactionDenied()); - } - - /** - * @magentoConfigFixture default_store payment/authorizenet_acceptjs/environment sandbox - * @magentoConfigFixture default_store payment/authorizenet_acceptjs/login someusername - * @magentoConfigFixture default_store payment/authorizenet_acceptjs/trans_key somepassword - * @magentoConfigFixture default_store payment/authorizenet_acceptjs/trans_signature_key abc - * * @magentoConfigFixture default_store payment/authorizenet_acceptjs/transactionSyncKeys transId,transactionType - * @magentoDataFixture Magento/AuthorizenetAcceptjs/Fixture/order_auth_only.php - */ - public function testTransactionVoided() - { - /** @var CommandPoolInterface $commandPool */ - $commandPool = $this->objectManager->get('AuthorizenetAcceptjsCommandPool'); - $command = $commandPool->get('fetch_transaction_information'); - - $order = $this->getOrderWithIncrementId('100000002'); - $payment = $order->getPayment(); - $paymentDO = $this->paymentFactory->create($payment); - - $expectedRequest = include __DIR__ . '/../../_files/expected_request/transaction_details_authorized.php'; - $response = include __DIR__ . '/../../_files/response/transaction_details_voided.php'; - - $this->clientMock->method('setRawData') - ->with(json_encode($expectedRequest), 'application/json'); - - $this->responseMock->method('getBody') - ->willReturn(json_encode($response)); - - $result = $command->execute([ - 'payment' => $paymentDO - ]); - - $expected = [ - 'transId' => '1234', - 'transactionType' => 'authOnlyTransaction' - ]; - $this->assertSame($expected, $result); - - /** @var Payment $payment */ - $this->assertFalse($payment->getIsTransactionApproved()); - $this->assertTrue($payment->getIsTransactionDenied()); - } - - /** - * @magentoConfigFixture default_store payment/authorizenet_acceptjs/environment sandbox - * @magentoConfigFixture default_store payment/authorizenet_acceptjs/login someusername - * @magentoConfigFixture default_store payment/authorizenet_acceptjs/trans_key somepassword - * @magentoConfigFixture default_store payment/authorizenet_acceptjs/trans_signature_key abc - * @magentoConfigFixture default_store payment/authorizenet_acceptjs/transactionSyncKeys transId,transactionType - * @magentoDataFixture Magento/AuthorizenetAcceptjs/Fixture/order_auth_only.php - */ - public function testTransactionDenied() - { - /** @var CommandPoolInterface $commandPool */ - $commandPool = $this->objectManager->get('AuthorizenetAcceptjsCommandPool'); - $command = $commandPool->get('fetch_transaction_information'); - - $order = $this->getOrderWithIncrementId('100000002'); - $payment = $order->getPayment(); - $paymentDO = $this->paymentFactory->create($payment); - - $expectedRequest = include __DIR__ . '/../../_files/expected_request/transaction_details_authorized.php'; - $response = include __DIR__ . '/../../_files/response/transaction_details_voided.php'; - - $this->clientMock->method('setRawData') - ->with(json_encode($expectedRequest), 'application/json'); - - $this->responseMock->method('getBody') - ->willReturn(json_encode($response)); - - $result = $command->execute([ - 'payment' => $paymentDO - ]); - - $expected = [ - 'transId' => '1234', - 'transactionType' => 'authOnlyTransaction' - ]; - $this->assertSame($expected, $result); - - /** @var Payment $payment */ - $this->assertFalse($payment->getIsTransactionApproved()); - $this->assertTrue($payment->getIsTransactionDenied()); - } - - /** - * @magentoConfigFixture default_store payment/authorizenet_acceptjs/environment sandbox - * @magentoConfigFixture default_store payment/authorizenet_acceptjs/login someusername - * @magentoConfigFixture default_store payment/authorizenet_acceptjs/trans_key somepassword - * @magentoConfigFixture default_store payment/authorizenet_acceptjs/trans_signature_key abc - * @magentoConfigFixture default_store payment/authorizenet_acceptjs/transactionSyncKeys transId,transactionType - * @magentoDataFixture Magento/AuthorizenetAcceptjs/Fixture/order_auth_only.php - */ - public function testTransactionPending() - { - /** @var CommandPoolInterface $commandPool */ - $commandPool = $this->objectManager->get('AuthorizenetAcceptjsCommandPool'); - $command = $commandPool->get('fetch_transaction_information'); - - $order = $this->getOrderWithIncrementId('100000002'); - $payment = $order->getPayment(); - $paymentDO = $this->paymentFactory->create($payment); - - $expectedRequest = include __DIR__ . '/../../_files/expected_request/transaction_details_authorized.php'; - $response = include __DIR__ . '/../../_files/response/transaction_details_fds_pending.php'; - - $this->clientMock->method('setRawData') - ->with(json_encode($expectedRequest), 'application/json'); - - $this->responseMock->method('getBody') - ->willReturn(json_encode($response)); - - $result = $command->execute([ - 'payment' => $paymentDO - ]); - - $expected = [ - 'transId' => '1234', - 'transactionType' => 'authOnlyTransaction' - ]; - $this->assertSame($expected, $result); - - /** @var Payment $payment */ - $this->assertNull($payment->getIsTransactionApproved()); - $this->assertNull($payment->getIsTransactionDenied()); - } -} diff --git a/dev/tests/integration/testsuite/Magento/AuthorizenetAcceptjs/Gateway/Command/RefundSettledCommandTest.php b/dev/tests/integration/testsuite/Magento/AuthorizenetAcceptjs/Gateway/Command/RefundSettledCommandTest.php deleted file mode 100644 index 0206ecd6b876b..0000000000000 --- a/dev/tests/integration/testsuite/Magento/AuthorizenetAcceptjs/Gateway/Command/RefundSettledCommandTest.php +++ /dev/null @@ -1,79 +0,0 @@ -<?php -/** - * Copyright © Magento, Inc. All rights reserved. - * See COPYING.txt for license details. - */ - -declare(strict_types=1); - -namespace Magento\AuthorizenetAcceptjs\Gateway\Command; - -use Magento\AuthorizenetAcceptjs\Gateway\AbstractTest; -use Magento\Payment\Gateway\Command\CommandPoolInterface; -use Magento\Sales\Api\Data\CreditmemoInterface; -use Magento\Sales\Model\Order; -use Magento\Sales\Model\Order\Payment; -use Magento\Sales\Model\ResourceModel\Order\Creditmemo\CollectionFactory as CreditmemoCollectionFactory; - -class RefundSettledCommandTest extends AbstractTest -{ - /** - * @magentoConfigFixture default_store payment/authorizenet_acceptjs/environment sandbox - * @magentoConfigFixture default_store payment/authorizenet_acceptjs/login someusername - * @magentoConfigFixture default_store payment/authorizenet_acceptjs/trans_key somepassword - * @magentoConfigFixture default_store payment/authorizenet_acceptjs/trans_signature_key abc - * @magentoDataFixture Magento/AuthorizenetAcceptjs/Fixture/full_order_with_capture.php - */ - public function testRefundSettledCommand() - { - /** @var CommandPoolInterface $commandPool */ - $commandPool = $this->objectManager->get('AuthorizenetAcceptjsCommandPool'); - $command = $commandPool->get('refund_settled'); - - $order = $this->getOrderWithIncrementId('100000001'); - $payment = $order->getPayment(); - $payment->setCreditmemo($this->getCreditmemo($order)); - - $paymentDO = $this->paymentFactory->create($payment); - - $expectedRequest = include __DIR__ . '/../../_files/expected_request/refund.php'; - $response = include __DIR__ . '/../../_files/response/refund.php'; - - $this->clientMock->method('setRawData') - ->with(json_encode($expectedRequest), 'application/json'); - - $this->responseMock->method('getBody') - ->willReturn(json_encode($response)); - - $command->execute( - [ - 'payment' => $paymentDO, - 'amount' => 100.00 - ] - ); - - /** @var Payment $payment */ - $this->assertTrue($payment->getIsTransactionClosed()); - $this->assertSame('5678', $payment->getTransactionId()); - } - - /** - * Retrieve creditmemo from order. - * - * @param Order $order - * @return CreditmemoInterface - */ - private function getCreditmemo(Order $order): CreditmemoInterface - { - /** @var \Magento\Sales\Model\ResourceModel\Order\Creditmemo\Collection $creditMemoCollection */ - $creditMemoCollection = $this->objectManager->create(CreditmemoCollectionFactory::class)->create(); - - /** @var CreditmemoInterface $creditMemo */ - $creditMemo = $creditMemoCollection - ->setOrderFilter($order) - ->setPageSize(1) - ->getFirstItem(); - - return $creditMemo; - } -} diff --git a/dev/tests/integration/testsuite/Magento/AuthorizenetAcceptjs/Gateway/Command/SaleCommandTest.php b/dev/tests/integration/testsuite/Magento/AuthorizenetAcceptjs/Gateway/Command/SaleCommandTest.php deleted file mode 100644 index 7ae03d36cb752..0000000000000 --- a/dev/tests/integration/testsuite/Magento/AuthorizenetAcceptjs/Gateway/Command/SaleCommandTest.php +++ /dev/null @@ -1,71 +0,0 @@ -<?php -/** - * Copyright © Magento, Inc. All rights reserved. - * See COPYING.txt for license details. - */ - -declare(strict_types=1); - -namespace Magento\AuthorizenetAcceptjs\Gateway\Command; - -use Magento\AuthorizenetAcceptjs\Gateway\AbstractTest; -use Magento\Payment\Gateway\Command\CommandPoolInterface; -use Magento\Sales\Model\Order\Payment; -use Magento\Sales\Model\Order\Payment\Transaction; - -class SaleCommandTest extends AbstractTest -{ - /** - * @magentoConfigFixture default_store payment/authorizenet_acceptjs/environment sandbox - * @magentoConfigFixture default_store payment/authorizenet_acceptjs/login someusername - * @magentoConfigFixture default_store payment/authorizenet_acceptjs/trans_key somepassword - * @magentoConfigFixture default_store payment/authorizenet_acceptjs/trans_signature_key abc - */ - public function testSaleCommand() - { - /** @var CommandPoolInterface $commandPool */ - $commandPool = $this->objectManager->get('AuthorizenetAcceptjsCommandPool'); - $command = $commandPool->get('sale'); - - $order = include __DIR__ . '/../../_files/full_order.php'; - $payment = $order->getPayment(); - - $paymentDO = $this->paymentFactory->create($payment); - - $expectedRequest = include __DIR__ . '/../../_files/expected_request/sale.php'; - $response = include __DIR__ . '/../../_files/response/sale.php'; - - $this->clientMock->method('setRawData') - ->with(json_encode($expectedRequest), 'application/json'); - - $this->responseMock->method('getBody') - ->willReturn(json_encode($response)); - - $command->execute([ - 'payment' => $paymentDO, - 'amount' => 100.00 - ]); - - /** @var Payment $payment */ - $rawDetails = [ - 'authCode' => 'abc123', - 'avsResultCode' => 'Y', - 'cvvResultCode' => 'P', - 'cavvResultCode' => '2', - 'accountType' => 'Visa', - ]; - $this->assertSame('1111', $payment->getCcLast4()); - $this->assertSame('Y', $payment->getCcAvsStatus()); - - $transactionDetails = $payment->getTransactionAdditionalInfo(); - foreach ($rawDetails as $key => $value) { - $this->assertSame($value, $payment->getAdditionalInformation($key)); - $this->assertSame($value, $transactionDetails[Transaction::RAW_DETAILS][$key]); - } - - $this->assertSame('123456', $payment->getTransactionId()); - $this->assertSame('123456', $transactionDetails['real_transaction_id']); - $this->assertTrue($payment->getShouldCloseParentTransaction()); - $this->assertFalse($payment->getData('is_transaction_closed')); - } -} diff --git a/dev/tests/integration/testsuite/Magento/AuthorizenetAcceptjs/Gateway/Command/SettleCommandTest.php b/dev/tests/integration/testsuite/Magento/AuthorizenetAcceptjs/Gateway/Command/SettleCommandTest.php deleted file mode 100644 index bb0a259b165bf..0000000000000 --- a/dev/tests/integration/testsuite/Magento/AuthorizenetAcceptjs/Gateway/Command/SettleCommandTest.php +++ /dev/null @@ -1,52 +0,0 @@ -<?php -/** - * Copyright © Magento, Inc. All rights reserved. - * See COPYING.txt for license details. - */ - -declare(strict_types=1); - -namespace Magento\AuthorizenetAcceptjs\Gateway\Command; - -use Magento\AuthorizenetAcceptjs\Gateway\AbstractTest; -use Magento\Payment\Gateway\Command\CommandPoolInterface; -use Magento\Sales\Model\Order\Payment; - -class SettleCommandTest extends AbstractTest -{ - /** - * @magentoConfigFixture default_store payment/authorizenet_acceptjs/environment sandbox - * @magentoConfigFixture default_store payment/authorizenet_acceptjs/login someusername - * @magentoConfigFixture default_store payment/authorizenet_acceptjs/trans_key somepassword - * @magentoConfigFixture default_store payment/authorizenet_acceptjs/trans_signature_key abc - * @magentoDataFixture Magento/AuthorizenetAcceptjs/Fixture/order_auth_only.php - */ - public function testRefundSettledCommand() - { - /** @var CommandPoolInterface $commandPool */ - $commandPool = $this->objectManager->get('AuthorizenetAcceptjsCommandPool'); - $command = $commandPool->get('settle'); - - $order = $this->getOrderWithIncrementId('100000002'); - $payment = $order->getPayment(); - - $paymentDO = $this->paymentFactory->create($payment); - - $expectedRequest = include __DIR__ . '/../../_files/expected_request/settle.php'; - $response = include __DIR__ . '/../../_files/response/settle.php'; - - $this->clientMock->method('setRawData') - ->with(json_encode($expectedRequest), 'application/json'); - - $this->responseMock->method('getBody') - ->willReturn(json_encode($response)); - - $command->execute([ - 'payment' => $paymentDO, - 'amount' => 100.00 - ]); - - /** @var Payment $payment */ - $this->assertTrue($payment->getShouldCloseParentTransaction()); - } -} diff --git a/dev/tests/integration/testsuite/Magento/AuthorizenetAcceptjs/Gateway/Command/TransactionDetailsCommandTest.php b/dev/tests/integration/testsuite/Magento/AuthorizenetAcceptjs/Gateway/Command/TransactionDetailsCommandTest.php deleted file mode 100644 index d81cffc413b59..0000000000000 --- a/dev/tests/integration/testsuite/Magento/AuthorizenetAcceptjs/Gateway/Command/TransactionDetailsCommandTest.php +++ /dev/null @@ -1,50 +0,0 @@ -<?php -/** - * Copyright © Magento, Inc. All rights reserved. - * See COPYING.txt for license details. - */ - -declare(strict_types=1); - -namespace Magento\AuthorizenetAcceptjs\Gateway\Command; - -use Magento\AuthorizenetAcceptjs\Gateway\AbstractTest; -use Magento\Payment\Gateway\Command\CommandPoolInterface; - -class TransactionDetailsCommandTest extends AbstractTest -{ - /** - * @magentoConfigFixture default_store payment/authorizenet_acceptjs/environment sandbox - * @magentoConfigFixture default_store payment/authorizenet_acceptjs/login someusername - * @magentoConfigFixture default_store payment/authorizenet_acceptjs/trans_key somepassword - * @magentoConfigFixture default_store payment/authorizenet_acceptjs/trans_signature_key abc - * @magentoDataFixture Magento/AuthorizenetAcceptjs/Fixture/order_captured.php - */ - public function testTransactionDetails() - { - /** @var CommandPoolInterface $commandPool */ - $commandPool = $this->objectManager->get('AuthorizenetAcceptjsCommandPool'); - $command = $commandPool->get('get_transaction_details'); - - $order = $this->getOrderWithIncrementId('100000002'); - $payment = $order->getPayment(); - $paymentDO = $this->paymentFactory->create($payment); - - $expectedRequest = include __DIR__ . '/../../_files/expected_request/transaction_details.php'; - $response = include __DIR__ . '/../../_files/response/transaction_details_settled_capture.php'; - - $this->clientMock->method('setRawData') - ->with(json_encode($expectedRequest), 'application/json'); - - $this->responseMock->method('getBody') - ->willReturn(json_encode($response)); - - $result = $command->execute([ - 'payment' => $paymentDO - ]); - - $resultData = $result->get(); - - $this->assertEquals($response, $resultData); - } -} diff --git a/dev/tests/integration/testsuite/Magento/AuthorizenetAcceptjs/Gateway/Command/VoidCommandTest.php b/dev/tests/integration/testsuite/Magento/AuthorizenetAcceptjs/Gateway/Command/VoidCommandTest.php deleted file mode 100644 index f74f8542bfdc3..0000000000000 --- a/dev/tests/integration/testsuite/Magento/AuthorizenetAcceptjs/Gateway/Command/VoidCommandTest.php +++ /dev/null @@ -1,53 +0,0 @@ -<?php -/** - * Copyright © Magento, Inc. All rights reserved. - * See COPYING.txt for license details. - */ - -declare(strict_types=1); - -namespace Magento\AuthorizenetAcceptjs\Gateway\Command; - -use Magento\AuthorizenetAcceptjs\Gateway\AbstractTest; -use Magento\Payment\Gateway\Command\CommandPoolInterface; -use Magento\Sales\Model\Order\Payment; - -class VoidCommandTest extends AbstractTest -{ - /** - * @magentoConfigFixture default_store payment/authorizenet_acceptjs/environment sandbox - * @magentoConfigFixture default_store payment/authorizenet_acceptjs/login someusername - * @magentoConfigFixture default_store payment/authorizenet_acceptjs/trans_key somepassword - * @magentoConfigFixture default_store payment/authorizenet_acceptjs/trans_signature_key abc - * @magentoDataFixture Magento/AuthorizenetAcceptjs/Fixture/order_auth_only.php - */ - public function testVoidCommand() - { - /** @var CommandPoolInterface $commandPool */ - $commandPool = $this->objectManager->get('AuthorizenetAcceptjsCommandPool'); - $command = $commandPool->get('void'); - - $order = $this->getOrderWithIncrementId('100000002'); - $payment = $order->getPayment(); - $paymentDO = $this->paymentFactory->create($payment); - - $expectedRequest = include __DIR__ . '/../../_files/expected_request/void.php'; - $response = include __DIR__ . '/../../_files/response/void.php'; - - $this->clientMock->method('setRawData') - ->with(json_encode($expectedRequest), 'application/json'); - - $this->responseMock->method('getBody') - ->willReturn(json_encode($response)); - - $command->execute([ - 'payment' => $paymentDO - ]); - - /** @var Payment $payment */ - - $this->assertTrue($payment->getIsTransactionClosed()); - $this->assertTrue($payment->getShouldCloseParentTransaction()); - $this->assertEquals('1234', $payment->getTransactionAdditionalInfo()['real_transaction_id']); - } -} diff --git a/dev/tests/integration/testsuite/Magento/AuthorizenetAcceptjs/Gateway/ConfigTest.php b/dev/tests/integration/testsuite/Magento/AuthorizenetAcceptjs/Gateway/ConfigTest.php deleted file mode 100644 index a37f927274242..0000000000000 --- a/dev/tests/integration/testsuite/Magento/AuthorizenetAcceptjs/Gateway/ConfigTest.php +++ /dev/null @@ -1,51 +0,0 @@ -<?php -/** - * Copyright © Magento, Inc. All rights reserved. - * See COPYING.txt for license details. - */ - -declare(strict_types=1); - -namespace Magento\AuthorizenetAcceptjs\Gateway; - -use Magento\Framework\Config\Data; -use Magento\Payment\Model\Method\Adapter; -use Magento\TestFramework\Helper\Bootstrap; -use Magento\TestFramework\ObjectManager; -use PHPUnit\Framework\TestCase; - -class ConfigTest extends TestCase -{ - /** - * @var ObjectManager - */ - private $objectManager; - - protected function setUp() - { - $this->objectManager = Bootstrap::getObjectManager(); - } - - public function testVerifyConfiguration() - { - /** @var Adapter $paymentAdapter */ - $paymentAdapter = $this->objectManager->get('AuthorizenetAcceptjsFacade'); - - $this->assertEquals('authorizenet_acceptjs', $paymentAdapter->getCode()); - $this->assertTrue($paymentAdapter->canAuthorize()); - $this->assertTrue($paymentAdapter->canCapture()); - $this->assertFalse($paymentAdapter->canCapturePartial()); - $this->assertTrue($paymentAdapter->canRefund()); - $this->assertTrue($paymentAdapter->canUseCheckout()); - $this->assertTrue($paymentAdapter->canVoid()); - $this->assertTrue($paymentAdapter->canUseInternal()); - $this->assertTrue($paymentAdapter->canEdit()); - $this->assertTrue($paymentAdapter->canFetchTransactionInfo()); - - /** @var Data $configReader */ - $configReader = $this->objectManager->get('Magento\Payment\Model\Config\Data'); - $value = $configReader->get('methods/authorizenet_acceptjs/allow_multiple_address'); - - $this->assertSame('0', $value); - } -} diff --git a/dev/tests/integration/testsuite/Magento/AuthorizenetAcceptjs/Gateway/Response/CloseTransactionHandlerTest.php b/dev/tests/integration/testsuite/Magento/AuthorizenetAcceptjs/Gateway/Response/CloseTransactionHandlerTest.php deleted file mode 100644 index 1bee2e95cef7d..0000000000000 --- a/dev/tests/integration/testsuite/Magento/AuthorizenetAcceptjs/Gateway/Response/CloseTransactionHandlerTest.php +++ /dev/null @@ -1,47 +0,0 @@ -<?php -/** - * Copyright © Magento, Inc. All rights reserved. - * See COPYING.txt for license details. - */ - -declare(strict_types=1); - -namespace Magento\AuthorizenetAcceptjs\Gateway\Response; - -use Magento\AuthorizenetAcceptjs\Gateway\AbstractTest; - -/** - * Test for Magento\AuthorizenetAcceptjs\Gateway\Response\CloseTransactionHandler class. - */ -class CloseTransactionHandlerTest extends AbstractTest -{ - /** - * @magentoConfigFixture default_store payment/authorizenet_acceptjs/environment sandbox - * @magentoConfigFixture default_store payment/authorizenet_acceptjs/login someusername - * @magentoConfigFixture default_store payment/authorizenet_acceptjs/trans_key somepassword - * @magentoConfigFixture default_store payment/authorizenet_acceptjs/trans_signature_key abc - * @magentoDataFixture Magento/AuthorizenetAcceptjs/Fixture/order_auth_only.php - * - * @return void - */ - public function testTransactionCloseStatus(): void - { - $commandPool = $this->objectManager->get('AuthorizenetAcceptjsCommandPool'); - $command = $commandPool->get('settle'); - $order = $this->getOrderWithIncrementId('100000002'); - $payment = $order->getPayment(); - $paymentDO = $this->paymentFactory->create($payment); - - $expectedRequest = include __DIR__ . '/../../_files/expected_request/settle.php'; - $response = include __DIR__ . '/../../_files/response/settle.php'; - - $this->clientMock->method('setRawData') - ->with(json_encode($expectedRequest), 'application/json'); - $this->responseMock->method('getBody') - ->willReturn(json_encode($response)); - - $command->execute(['payment' => $paymentDO]); - - $this->assertFalse($payment->getIsTransactionClosed()); - } -} diff --git a/dev/tests/integration/testsuite/Magento/AuthorizenetAcceptjs/_files/expected_request/accept_fds.php b/dev/tests/integration/testsuite/Magento/AuthorizenetAcceptjs/_files/expected_request/accept_fds.php deleted file mode 100644 index d843de1c2cac0..0000000000000 --- a/dev/tests/integration/testsuite/Magento/AuthorizenetAcceptjs/_files/expected_request/accept_fds.php +++ /dev/null @@ -1,20 +0,0 @@ -<?php -/** - * Copyright © Magento, Inc. All rights reserved. - * See COPYING.txt for license details. - */ - -declare(strict_types=1); - -return [ - 'updateHeldTransactionRequest' => [ - 'merchantAuthentication' => [ - 'name' => 'someusername', - 'transactionKey' => 'somepassword' - ], - 'heldTransactionRequest' => [ - 'action' => 'approve', - 'refTransId' => '1234', - ] - ] -]; diff --git a/dev/tests/integration/testsuite/Magento/AuthorizenetAcceptjs/_files/expected_request/authorize.php b/dev/tests/integration/testsuite/Magento/AuthorizenetAcceptjs/_files/expected_request/authorize.php deleted file mode 100644 index 16debdb2ef820..0000000000000 --- a/dev/tests/integration/testsuite/Magento/AuthorizenetAcceptjs/_files/expected_request/authorize.php +++ /dev/null @@ -1,66 +0,0 @@ -<?php -/** - * Copyright © Magento, Inc. All rights reserved. - * See COPYING.txt for license details. - */ - -declare(strict_types=1); - -return [ - 'createTransactionRequest' => [ - 'merchantAuthentication' =>[ - 'name' => 'someusername', - 'transactionKey' => 'somepassword', - ], - 'transactionRequest' => [ - 'transactionType' => 'authOnlyTransaction', - 'amount' => '100.00', - 'payment' => [ - 'opaqueData' => [ - 'dataDescriptor' => 'mydescriptor', - 'dataValue' => 'myvalue', - ], - ], - 'solution' => [ - 'id' => 'AAA102993', - ], - 'order' => [ - 'invoiceNumber' => '100000001', - ], - 'poNumber' => null, - 'customer' => [ - 'id' => 1, - 'email' => 'admin@example.com', - ], - 'billTo' => [ - 'firstName' => 'firstname', - 'lastName' => 'lastname', - 'company' => '', - 'address' => 'street', - 'city' => 'Los Angeles', - 'state' => 'CA', - 'zip' => '11111', - 'country' => 'US', - ], - 'shipTo' => [ - 'firstName' => 'John', - 'lastName' => 'Doe', - 'company' => '', - 'address' => '6161 West Centinela Avenue', - 'city' => 'Los Angeles', - 'state' => 'CA', - 'zip' => '11111', - 'country' => 'US', - ], - 'customerIP' => '127.0.0.1', - 'userFields' => [ - 'userField' => [ - [ - 'name' => 'transactionType', - 'value' => 'authOnlyTransaction', - ], - ], - ], - ], - ] -]; diff --git a/dev/tests/integration/testsuite/Magento/AuthorizenetAcceptjs/_files/expected_request/refund.php b/dev/tests/integration/testsuite/Magento/AuthorizenetAcceptjs/_files/expected_request/refund.php deleted file mode 100644 index 5ed331d076f66..0000000000000 --- a/dev/tests/integration/testsuite/Magento/AuthorizenetAcceptjs/_files/expected_request/refund.php +++ /dev/null @@ -1,56 +0,0 @@ -<?php -/** - * Copyright © Magento, Inc. All rights reserved. - * See COPYING.txt for license details. - */ - -declare(strict_types=1); - -return [ - 'createTransactionRequest' => [ - 'merchantAuthentication' =>[ - 'name' => 'someusername', - 'transactionKey' => 'somepassword', - ], - 'transactionRequest' => [ - 'transactionType' => 'refundTransaction', - 'amount' => '100.00', - 'payment' => [ - 'creditCard' => [ - 'cardNumber' => '1111', - 'expirationDate' => 'XXXX' - ] - ], - 'refTransId' => '4321', - 'order' => [ - 'invoiceNumber' => '100000001', - ], - 'poNumber' => null, - 'customer' => [ - 'id' => '1', - 'email' => 'admin@example.com', - ], - 'billTo' => [ - 'firstName' => 'firstname', - 'lastName' => 'lastname', - 'company' => '', - 'address' => 'street', - 'city' => 'Los Angeles', - 'state' => 'CA', - 'zip' => '11111', - 'country' => 'US', - ], - 'shipTo' => [ - 'firstName' => 'John', - 'lastName' => 'Doe', - 'company' => '', - 'address' => '6161 West Centinela Avenue', - 'city' => 'Los Angeles', - 'state' => 'CA', - 'zip' => '11111', - 'country' => 'US', - ], - 'customerIP' => '127.0.0.1' - ], - ] -]; diff --git a/dev/tests/integration/testsuite/Magento/AuthorizenetAcceptjs/_files/expected_request/sale.php b/dev/tests/integration/testsuite/Magento/AuthorizenetAcceptjs/_files/expected_request/sale.php deleted file mode 100644 index 4514acbcb6646..0000000000000 --- a/dev/tests/integration/testsuite/Magento/AuthorizenetAcceptjs/_files/expected_request/sale.php +++ /dev/null @@ -1,66 +0,0 @@ -<?php -/** - * Copyright © Magento, Inc. All rights reserved. - * See COPYING.txt for license details. - */ - -declare(strict_types=1); - -return [ - 'createTransactionRequest' => [ - 'merchantAuthentication' =>[ - 'name' => 'someusername', - 'transactionKey' => 'somepassword', - ], - 'transactionRequest' => [ - 'transactionType' => 'authCaptureTransaction', - 'amount' => '100.00', - 'payment' => [ - 'opaqueData' => [ - 'dataDescriptor' => 'mydescriptor', - 'dataValue' => 'myvalue', - ], - ], - 'solution' => [ - 'id' => 'AAA102993', - ], - 'order' => [ - 'invoiceNumber' => '100000001', - ], - 'poNumber' => null, - 'customer' => [ - 'id' => 1, - 'email' => 'admin@example.com', - ], - 'billTo' => [ - 'firstName' => 'firstname', - 'lastName' => 'lastname', - 'company' => '', - 'address' => 'street', - 'city' => 'Los Angeles', - 'state' => 'CA', - 'zip' => '11111', - 'country' => 'US', - ], - 'shipTo' => [ - 'firstName' => 'John', - 'lastName' => 'Doe', - 'company' => '', - 'address' => '6161 West Centinela Avenue', - 'city' => 'Los Angeles', - 'state' => 'CA', - 'zip' => '11111', - 'country' => 'US', - ], - 'customerIP' => '127.0.0.1', - 'userFields' => [ - 'userField' => [ - [ - 'name' => 'transactionType', - 'value' => 'authCaptureTransaction', - ], - ], - ], - ], - ] -]; diff --git a/dev/tests/integration/testsuite/Magento/AuthorizenetAcceptjs/_files/expected_request/settle.php b/dev/tests/integration/testsuite/Magento/AuthorizenetAcceptjs/_files/expected_request/settle.php deleted file mode 100644 index b4fa88cc1e5a9..0000000000000 --- a/dev/tests/integration/testsuite/Magento/AuthorizenetAcceptjs/_files/expected_request/settle.php +++ /dev/null @@ -1,28 +0,0 @@ -<?php -/** - * Copyright © Magento, Inc. All rights reserved. - * See COPYING.txt for license details. - */ - -declare(strict_types=1); - -return [ - 'createTransactionRequest' => [ - 'merchantAuthentication' =>[ - 'name' => 'someusername', - 'transactionKey' => 'somepassword', - ], - 'transactionRequest' => [ - 'transactionType' => 'priorAuthCaptureTransaction', - 'refTransId' => '1234', - 'userFields' => [ - 'userField' => [ - [ - 'name' => 'transactionType', - 'value' => 'priorAuthCaptureTransaction', - ], - ], - ], - ], - ] -]; diff --git a/dev/tests/integration/testsuite/Magento/AuthorizenetAcceptjs/_files/expected_request/transaction_details.php b/dev/tests/integration/testsuite/Magento/AuthorizenetAcceptjs/_files/expected_request/transaction_details.php deleted file mode 100644 index 110333866766e..0000000000000 --- a/dev/tests/integration/testsuite/Magento/AuthorizenetAcceptjs/_files/expected_request/transaction_details.php +++ /dev/null @@ -1,17 +0,0 @@ -<?php -/** - * Copyright © Magento, Inc. All rights reserved. - * See COPYING.txt for license details. - */ - -declare(strict_types=1); - -return [ - 'getTransactionDetailsRequest' => [ - 'merchantAuthentication' => [ - 'name' => 'someusername', - 'transactionKey' => 'somepassword' - ], - 'transId' => '4321' - ] -]; diff --git a/dev/tests/integration/testsuite/Magento/AuthorizenetAcceptjs/_files/expected_request/transaction_details_authorized.php b/dev/tests/integration/testsuite/Magento/AuthorizenetAcceptjs/_files/expected_request/transaction_details_authorized.php deleted file mode 100644 index c3ffdedba6851..0000000000000 --- a/dev/tests/integration/testsuite/Magento/AuthorizenetAcceptjs/_files/expected_request/transaction_details_authorized.php +++ /dev/null @@ -1,17 +0,0 @@ -<?php -/** - * Copyright © Magento, Inc. All rights reserved. - * See COPYING.txt for license details. - */ - -declare(strict_types=1); - -return [ - 'getTransactionDetailsRequest' => [ - 'merchantAuthentication' => [ - 'name' => 'someusername', - 'transactionKey' => 'somepassword' - ], - 'transId' => '1234' - ] -]; diff --git a/dev/tests/integration/testsuite/Magento/AuthorizenetAcceptjs/_files/expected_request/void.php b/dev/tests/integration/testsuite/Magento/AuthorizenetAcceptjs/_files/expected_request/void.php deleted file mode 100644 index a1d3dade74ff1..0000000000000 --- a/dev/tests/integration/testsuite/Magento/AuthorizenetAcceptjs/_files/expected_request/void.php +++ /dev/null @@ -1,20 +0,0 @@ -<?php -/** - * Copyright © Magento, Inc. All rights reserved. - * See COPYING.txt for license details. - */ - -declare(strict_types=1); - -return [ - 'createTransactionRequest' => [ - 'merchantAuthentication' => [ - 'name' => 'someusername', - 'transactionKey' => 'somepassword', - ], - 'transactionRequest' =>[ - 'transactionType' => 'voidTransaction', - 'refTransId' => '1234', - ], - ] -]; diff --git a/dev/tests/integration/testsuite/Magento/AuthorizenetAcceptjs/_files/full_order.php b/dev/tests/integration/testsuite/Magento/AuthorizenetAcceptjs/_files/full_order.php deleted file mode 100644 index 420d0f55cf34e..0000000000000 --- a/dev/tests/integration/testsuite/Magento/AuthorizenetAcceptjs/_files/full_order.php +++ /dev/null @@ -1,129 +0,0 @@ -<?php -/** - * Copyright © Magento, Inc. All rights reserved. - * See COPYING.txt for license details. - */ - -declare(strict_types=1); - -use Magento\Catalog\Api\ProductRepositoryInterface; -use Magento\Catalog\Model\Product; -use Magento\Catalog\Model\Product\Attribute\Source\Status; -use Magento\Catalog\Model\Product\Type; -use Magento\Catalog\Model\Product\Visibility; -use Magento\Sales\Model\Order; -use Magento\Sales\Model\Order\Payment; -use Magento\Sales\Model\Order\Address; -use Magento\Sales\Model\Order\Item; -use Magento\TestFramework\Helper\Bootstrap; - -$addressData = include __DIR__ . '/../../../Magento/Sales/_files/address_data.php'; -require __DIR__ . '/../../../Magento/Customer/_files/customer.php'; - -$objectManager = Bootstrap::getObjectManager(); - -/** @var $product Product */ -$product = $objectManager->create(Product::class); -$product->isObjectNew(true); -$product->setTypeId(Type::TYPE_SIMPLE) - ->setId(1) - ->setAttributeSetId(4) - ->setWebsiteIds([1]) - ->setName('Simple Product') - ->setSku('simple') - ->setPrice(10) - ->setWeight(1) - ->setShortDescription('Short description') - ->setTaxClassId(0) - ->setDescription('Description with <b>html tag</b>') - ->setMetaTitle('meta title') - ->setMetaKeyword('meta keyword') - ->setMetaDescription('meta description') - ->setVisibility(Visibility::VISIBILITY_BOTH) - ->setStatus(Status::STATUS_ENABLED) - ->setStockData( - [ - 'use_config_manage_stock' => 1, - 'qty' => 100, - 'is_qty_decimal' => 0, - 'is_in_stock' => 1, - ] - )->setCanSaveCustomOptions(true) - ->setHasOptions(false); - -/** @var ProductRepositoryInterface $productRepository */ -$productRepository = $objectManager->create(ProductRepositoryInterface::class); -$productRepository->save($product); - - -$billingAddress = $objectManager->create(Address::class, ['data' => $addressData]); -$billingAddress->setAddressType('billing'); - -$shippingAddress = clone $billingAddress; -$shippingAddress->setId(null) - ->setAddressType('shipping') - ->setStreet(['6161 West Centinela Avenue']) - ->setFirstname('John') - ->setLastname('Doe') - ->setShippingMethod('flatrate_flatrate'); - -/** @var Payment $payment */ -$payment = $objectManager->create(Payment::class); -$payment->setAdditionalInformation('ccLast4', '1111'); -$payment->setAdditionalInformation('opaqueDataDescriptor', 'mydescriptor'); -$payment->setAdditionalInformation('opaqueDataValue', 'myvalue'); - -/** @var Item $orderItem */ -$orderItem1 = $objectManager->create(Item::class); -$orderItem1->setProductId($product->getId()) - ->setSku($product->getSku()) - ->setName($product->getName()) - ->setQtyOrdered(1) - ->setBasePrice($product->getPrice()) - ->setPrice($product->getPrice()) - ->setRowTotal($product->getPrice()) - ->setProductType($product->getTypeId()); - -/** @var Item $orderItem */ -$orderItem2 = $objectManager->create(Item::class); -$orderItem2->setProductId($product->getId()) - ->setSku('simple2') - ->setName('Simple product') - ->setPrice(100) - ->setQtyOrdered(2) - ->setBasePrice($product->getPrice()) - ->setPrice($product->getPrice()) - ->setRowTotal($product->getPrice()) - ->setProductType($product->getTypeId()); - -$orderAmount = 100; -$customerEmail = $billingAddress->getEmail(); - -/** @var Order $order */ -$order = $objectManager->create(Order::class); -$order->setIncrementId('100000001') - ->setState(Order::STATE_PROCESSING) - ->setStatus(Order::STATE_PROCESSING) - ->setCustomerId($customer->getId()) - ->setCustomerIsGuest(false) - ->setRemoteIp('127.0.0.1') - ->setCreatedAt(date('Y-m-d 00:00:55')) - ->setOrderCurrencyCode('USD') - ->setBaseCurrencyCode('USD') - ->setSubtotal($orderAmount) - ->setGrandTotal($orderAmount) - ->setBaseSubtotal($orderAmount) - ->setBaseGrandTotal($orderAmount) - ->setCustomerEmail($customerEmail) - ->setBillingAddress($billingAddress) - ->setShippingAddress($shippingAddress) - ->setShippingDescription('Flat Rate - Fixed') - ->setShippingAmount(10) - ->setBaseShippingAmount(10) - ->setStoreId(1) - ->addItem($orderItem1) - ->addItem($orderItem2) - ->setQuoteId(1) - ->setPayment($payment); - -return $order; diff --git a/dev/tests/integration/testsuite/Magento/AuthorizenetAcceptjs/_files/response/authorize.php b/dev/tests/integration/testsuite/Magento/AuthorizenetAcceptjs/_files/response/authorize.php deleted file mode 100644 index f80495137ca29..0000000000000 --- a/dev/tests/integration/testsuite/Magento/AuthorizenetAcceptjs/_files/response/authorize.php +++ /dev/null @@ -1,47 +0,0 @@ -<?php -/** - * Copyright © Magento, Inc. All rights reserved. - * See COPYING.txt for license details. - */ - -declare(strict_types=1); - -return [ - 'transactionResponse' => [ - 'responseCode' => '1', - 'authCode' => 'abc123', - 'avsResultCode' => 'Y', - 'cvvResultCode' => 'P', - 'cavvResultCode' => '2', - 'transId' => '123456', - 'refTransID' => '', - 'transHash' => 'foobar', - 'testRequest' => '0', - 'accountNumber' => 'XXXX1111', - 'accountType' => 'Visa', - 'messages' => [ - [ - 'code' => '1', - 'description' => 'This transaction has been approved.' - ] - ], - 'userFields' => [ - [ - 'name' => 'transactionType', - 'value' => 'authOnlyTransaction' - ] - ], - 'transHashSha2' => 'CD1E57FB1B5C876FDBD536CB16F8BBBA687580EDD78DD881C7F14DC4467C32BF6C' - . '808620FBD59E5977DF19460B98CCFC0DA0D90755992C0D611CABB8E2BA52B0', - 'SupplementalDataQualificationIndicator' => 0 - ], - 'messages' => [ - 'resultCode' => 'Ok', - 'message' => [ - [ - 'code' => 'I00001', - 'text' => 'Successful.' - ] - ] - ] -]; diff --git a/dev/tests/integration/testsuite/Magento/AuthorizenetAcceptjs/_files/response/generic_success.php b/dev/tests/integration/testsuite/Magento/AuthorizenetAcceptjs/_files/response/generic_success.php deleted file mode 100644 index ea7662e319376..0000000000000 --- a/dev/tests/integration/testsuite/Magento/AuthorizenetAcceptjs/_files/response/generic_success.php +++ /dev/null @@ -1,17 +0,0 @@ -<?php -/** - * Copyright © Magento, Inc. All rights reserved. - * See COPYING.txt for license details. - */ - -declare(strict_types=1); - -return [ - 'messages' => [ - 'resultCode' => 'Ok', - 'message' => [ - 'code' => 'I00001', - 'text' => 'Successful' - ] - ] -]; diff --git a/dev/tests/integration/testsuite/Magento/AuthorizenetAcceptjs/_files/response/refund.php b/dev/tests/integration/testsuite/Magento/AuthorizenetAcceptjs/_files/response/refund.php deleted file mode 100644 index 536f51d659ad8..0000000000000 --- a/dev/tests/integration/testsuite/Magento/AuthorizenetAcceptjs/_files/response/refund.php +++ /dev/null @@ -1,40 +0,0 @@ -<?php -/** - * Copyright © Magento, Inc. All rights reserved. - * See COPYING.txt for license details. - */ - -declare(strict_types=1); - -return [ - 'transactionResponse' => [ - 'responseCode' => '1', - 'authCode' => '', - 'avsResultCode' => 'P', - 'cvvResultCode' => '', - 'cavvResultCode' => '', - 'transId' => '5678', - 'refTransID' => '4321', - 'testRequest' => '0', - 'accountNumber' => 'XXXX1111', - 'accountType' => 'Visa', - 'messages' => [ - [ - 'code' => '1', - 'description' => 'This transaction has been approved.' - ] - ], - 'transHashSha2' => '78BD31BA5BCDF3C3FA3C8373D8DF80EF07FC7E02C3545FCF18A408E2F76ED4F20D' - . 'FF007221374B576FDD1BFD953B3E5CF37249CEC4C135EEF975F7B478D8452C', - 'SupplementalDataQualificationIndicator' => 0 - ], - 'messages' => [ - 'resultCode' => 'Ok', - 'message' => [ - [ - 'code' => 'I00001', - 'text' => 'Successful.' - ] - ] - ] -]; diff --git a/dev/tests/integration/testsuite/Magento/AuthorizenetAcceptjs/_files/response/sale.php b/dev/tests/integration/testsuite/Magento/AuthorizenetAcceptjs/_files/response/sale.php deleted file mode 100644 index 74a80110adece..0000000000000 --- a/dev/tests/integration/testsuite/Magento/AuthorizenetAcceptjs/_files/response/sale.php +++ /dev/null @@ -1,47 +0,0 @@ -<?php -/** - * Copyright © Magento, Inc. All rights reserved. - * See COPYING.txt for license details. - */ - -declare(strict_types=1); - -return [ - 'transactionResponse' => [ - 'responseCode' => '1', - 'authCode' => 'abc123', - 'avsResultCode' => 'Y', - 'cvvResultCode' => 'P', - 'cavvResultCode' => '2', - 'transId' => '123456', - 'refTransID' => '', - 'transHash' => 'foobar', - 'testRequest' => '0', - 'accountNumber' => 'XXXX1111', - 'accountType' => 'Visa', - 'messages' => [ - [ - 'code' => '1', - 'description' => 'This transaction has been approved.' - ] - ], - 'userFields' => [ - [ - 'name' => 'transactionType', - 'value' => 'authCaptureTransaction' - ] - ], - 'transHashSha2' => 'CD1E57FB1B5C876FDBD536CB16F8BBBA687580EDD78DD881C7F14DC4467C32BF6C' - . '808620FBD59E5977DF19460B98CCFC0DA0D90755992C0D611CABB8E2BA52B0', - 'SupplementalDataQualificationIndicator' => 0 - ], - 'messages' => [ - 'resultCode' => 'Ok', - 'message' => [ - [ - 'code' => 'I00001', - 'text' => 'Successful.' - ] - ] - ] -]; diff --git a/dev/tests/integration/testsuite/Magento/AuthorizenetAcceptjs/_files/response/settle.php b/dev/tests/integration/testsuite/Magento/AuthorizenetAcceptjs/_files/response/settle.php deleted file mode 100644 index 5e54c30198741..0000000000000 --- a/dev/tests/integration/testsuite/Magento/AuthorizenetAcceptjs/_files/response/settle.php +++ /dev/null @@ -1,40 +0,0 @@ -<?php -/** - * Copyright © Magento, Inc. All rights reserved. - * See COPYING.txt for license details. - */ - -declare(strict_types=1); - -return [ - 'transactionResponse' => [ - 'responseCode' => '1', - 'authCode' => '', - 'avsResultCode' => 'P', - 'cvvResultCode' => '', - 'cavvResultCode' => '', - 'transId' => '1234', - 'refTransID' => '1234', - 'testRequest' => '0', - 'accountNumber' => 'XXXX1111', - 'accountType' => 'Visa', - 'messages' => [ - [ - 'code' => '1', - 'description' => 'This transaction has been approved.' - ] - ], - 'transHashSha2' => '1B22AB4E4DF750CF2E0D1944BB6903537C145545C7313C87B6FD4A6384' - . '709EA2609CE9A9788C128F2F2EAEEE474F6010418904648C6D000BE3AF7BCD98A5AD8F', - 'SupplementalDataQualificationIndicator' => 0 - ], - 'messages' => [ - 'resultCode' => 'Ok', - 'message' => [ - [ - 'code' => 'I00001', - 'text' => 'Successful.' - ] - ] - ] -]; diff --git a/dev/tests/integration/testsuite/Magento/AuthorizenetAcceptjs/_files/response/transaction_details_authorized.php b/dev/tests/integration/testsuite/Magento/AuthorizenetAcceptjs/_files/response/transaction_details_authorized.php deleted file mode 100644 index 80fd24a5c601a..0000000000000 --- a/dev/tests/integration/testsuite/Magento/AuthorizenetAcceptjs/_files/response/transaction_details_authorized.php +++ /dev/null @@ -1,22 +0,0 @@ -<?php -/** - * Copyright © Magento, Inc. All rights reserved. - * See COPYING.txt for license details. - */ - -declare(strict_types=1); - -return [ - 'messages' => [ - 'resultCode' => 'Ok', - 'message' => [ - 'code' => 'I00001', - 'text' => 'Successful' - ] - ], - 'transaction' => [ - 'transId' => '1234', - 'transactionType' => 'authOnlyTransaction', - 'transactionStatus' => 'authorizedPendingCapture' - ] -]; diff --git a/dev/tests/integration/testsuite/Magento/AuthorizenetAcceptjs/_files/response/transaction_details_declined.php b/dev/tests/integration/testsuite/Magento/AuthorizenetAcceptjs/_files/response/transaction_details_declined.php deleted file mode 100644 index 24c9353e4088a..0000000000000 --- a/dev/tests/integration/testsuite/Magento/AuthorizenetAcceptjs/_files/response/transaction_details_declined.php +++ /dev/null @@ -1,22 +0,0 @@ -<?php -/** - * Copyright © Magento, Inc. All rights reserved. - * See COPYING.txt for license details. - */ - -declare(strict_types=1); - -return [ - 'messages' => [ - 'resultCode' => 'Ok', - 'message' => [ - 'code' => 'I00001', - 'text' => 'Successful' - ] - ], - 'transaction' => [ - 'transId' => '1234', - 'transactionType' => 'authOnlyTransaction', - 'transactionStatus' => 'declined' - ] -]; diff --git a/dev/tests/integration/testsuite/Magento/AuthorizenetAcceptjs/_files/response/transaction_details_fds_pending.php b/dev/tests/integration/testsuite/Magento/AuthorizenetAcceptjs/_files/response/transaction_details_fds_pending.php deleted file mode 100644 index de045f30ab22e..0000000000000 --- a/dev/tests/integration/testsuite/Magento/AuthorizenetAcceptjs/_files/response/transaction_details_fds_pending.php +++ /dev/null @@ -1,22 +0,0 @@ -<?php -/** - * Copyright © Magento, Inc. All rights reserved. - * See COPYING.txt for license details. - */ - -declare(strict_types=1); - -return [ - 'messages' => [ - 'resultCode' => 'Ok', - 'message' => [ - 'code' => 'I00001', - 'text' => 'Successful' - ] - ], - 'transaction' => [ - 'transId' => '1234', - 'transactionType' => 'authOnlyTransaction', - 'transactionStatus' => 'FDSPendingReview' - ] -]; diff --git a/dev/tests/integration/testsuite/Magento/AuthorizenetAcceptjs/_files/response/transaction_details_settled_capture.php b/dev/tests/integration/testsuite/Magento/AuthorizenetAcceptjs/_files/response/transaction_details_settled_capture.php deleted file mode 100644 index 5df2f03a943a6..0000000000000 --- a/dev/tests/integration/testsuite/Magento/AuthorizenetAcceptjs/_files/response/transaction_details_settled_capture.php +++ /dev/null @@ -1,22 +0,0 @@ -<?php -/** - * Copyright © Magento, Inc. All rights reserved. - * See COPYING.txt for license details. - */ - -declare(strict_types=1); - -return [ - 'messages' => [ - 'resultCode' => 'Ok', - 'message' => [ - 'code' => 'I00001', - 'text' => 'Successful' - ] - ], - 'transaction' => [ - 'transId' => '4321', - 'transactionType' => 'captureOnlyTransaction', - 'transactionStatus' => 'settledSuccessfully' - ] -]; diff --git a/dev/tests/integration/testsuite/Magento/AuthorizenetAcceptjs/_files/response/transaction_details_voided.php b/dev/tests/integration/testsuite/Magento/AuthorizenetAcceptjs/_files/response/transaction_details_voided.php deleted file mode 100644 index 7ee735cd8cf36..0000000000000 --- a/dev/tests/integration/testsuite/Magento/AuthorizenetAcceptjs/_files/response/transaction_details_voided.php +++ /dev/null @@ -1,22 +0,0 @@ -<?php -/** - * Copyright © Magento, Inc. All rights reserved. - * See COPYING.txt for license details. - */ - -declare(strict_types=1); - -return [ - 'messages' => [ - 'resultCode' => 'Ok', - 'message' => [ - 'code' => 'I00001', - 'text' => 'Successful' - ] - ], - 'transaction' => [ - 'transId' => '1234', - 'transactionType' => 'authOnlyTransaction', - 'transactionStatus' => 'void' - ] -]; diff --git a/dev/tests/integration/testsuite/Magento/AuthorizenetAcceptjs/_files/response/void.php b/dev/tests/integration/testsuite/Magento/AuthorizenetAcceptjs/_files/response/void.php deleted file mode 100644 index eb71de4dd9667..0000000000000 --- a/dev/tests/integration/testsuite/Magento/AuthorizenetAcceptjs/_files/response/void.php +++ /dev/null @@ -1,30 +0,0 @@ -<?php -/** - * Copyright © Magento, Inc. All rights reserved. - * See COPYING.txt for license details. - */ - -declare(strict_types=1); - -return [ - 'messages' => [ - 'resultCode' => 'Ok', - 'message' => [ - 'code' => 'I00001', - 'text' => 'Successful' - ] - ], - 'transactionResponse' => [ - 'responseCode' => '1', - 'messages' => [ - 'message' => [ - [ - 'code' => 1 - ] - ] - ], - 'transHashSha2' => '1B22AB4E4DF750CF2E0D1944BB6903537C145545C7313C87B6FD4A6384709E' - . 'A2609CE9A9788C128F2F2EAEEE474F6010418904648C6D000BE3AF7BCD98A5AD8F', - 'transId' => '1234' - ] -]; diff --git a/dev/tests/integration/testsuite/Magento/AuthorizenetCardinal/Fixture/expected_request/authorize.php b/dev/tests/integration/testsuite/Magento/AuthorizenetCardinal/Fixture/expected_request/authorize.php deleted file mode 100644 index ceab22403d987..0000000000000 --- a/dev/tests/integration/testsuite/Magento/AuthorizenetCardinal/Fixture/expected_request/authorize.php +++ /dev/null @@ -1,70 +0,0 @@ -<?php -/** - * Copyright © Magento, Inc. All rights reserved. - * See COPYING.txt for license details. - */ - -declare(strict_types=1); - -return [ - 'createTransactionRequest' => [ - 'merchantAuthentication' =>[ - 'name' => 'someusername', - 'transactionKey' => 'somepassword', - ], - 'transactionRequest' => [ - 'transactionType' => 'authOnlyTransaction', - 'amount' => '100.00', - 'payment' => [ - 'opaqueData' => [ - 'dataDescriptor' => 'mydescriptor', - 'dataValue' => 'myvalue', - ], - ], - 'solution' => [ - 'id' => 'AAA102993', - ], - 'order' => [ - 'invoiceNumber' => '100000001', - ], - 'poNumber' => null, - 'customer' => [ - 'id' => 1, - 'email' => 'admin@example.com', - ], - 'billTo' => [ - 'firstName' => 'firstname', - 'lastName' => 'lastname', - 'company' => '', - 'address' => 'street', - 'city' => 'Los Angeles', - 'state' => 'CA', - 'zip' => '11111', - 'country' => 'US', - ], - 'shipTo' => [ - 'firstName' => 'John', - 'lastName' => 'Doe', - 'company' => '', - 'address' => '6161 West Centinela Avenue', - 'city' => 'Los Angeles', - 'state' => 'CA', - 'zip' => '11111', - 'country' => 'US', - ], - 'customerIP' => '127.0.0.1', - 'cardholderAuthentication' => [ - 'authenticationIndicator' => '05', - 'cardholderAuthenticationValue' => 'AAABAWFlmQAAAABjRWWZEEFgFz8=', - ], - 'userFields' => [ - 'userField' => [ - [ - 'name' => 'transactionType', - 'value' => 'authOnlyTransaction', - ], - ], - ], - ], - ] -]; diff --git a/dev/tests/integration/testsuite/Magento/AuthorizenetCardinal/Fixture/expected_request/sale.php b/dev/tests/integration/testsuite/Magento/AuthorizenetCardinal/Fixture/expected_request/sale.php deleted file mode 100644 index f96facb19b3b5..0000000000000 --- a/dev/tests/integration/testsuite/Magento/AuthorizenetCardinal/Fixture/expected_request/sale.php +++ /dev/null @@ -1,70 +0,0 @@ -<?php -/** - * Copyright © Magento, Inc. All rights reserved. - * See COPYING.txt for license details. - */ - -declare(strict_types=1); - -return [ - 'createTransactionRequest' => [ - 'merchantAuthentication' =>[ - 'name' => 'someusername', - 'transactionKey' => 'somepassword', - ], - 'transactionRequest' => [ - 'transactionType' => 'authCaptureTransaction', - 'amount' => '100.00', - 'payment' => [ - 'opaqueData' => [ - 'dataDescriptor' => 'mydescriptor', - 'dataValue' => 'myvalue', - ], - ], - 'solution' => [ - 'id' => 'AAA102993', - ], - 'order' => [ - 'invoiceNumber' => '100000001', - ], - 'poNumber' => null, - 'customer' => [ - 'id' => 1, - 'email' => 'admin@example.com', - ], - 'billTo' => [ - 'firstName' => 'firstname', - 'lastName' => 'lastname', - 'company' => '', - 'address' => 'street', - 'city' => 'Los Angeles', - 'state' => 'CA', - 'zip' => '11111', - 'country' => 'US', - ], - 'shipTo' => [ - 'firstName' => 'John', - 'lastName' => 'Doe', - 'company' => '', - 'address' => '6161 West Centinela Avenue', - 'city' => 'Los Angeles', - 'state' => 'CA', - 'zip' => '11111', - 'country' => 'US', - ], - 'customerIP' => '127.0.0.1', - 'cardholderAuthentication' => [ - 'authenticationIndicator' => '05', - 'cardholderAuthenticationValue' => 'AAABAWFlmQAAAABjRWWZEEFgFz8=', - ], - 'userFields' => [ - 'userField' => [ - [ - 'name' => 'transactionType', - 'value' => 'authCaptureTransaction', - ], - ], - ], - ], - ] -]; diff --git a/dev/tests/integration/testsuite/Magento/AuthorizenetCardinal/Fixture/full_order_with_3dsecure.php b/dev/tests/integration/testsuite/Magento/AuthorizenetCardinal/Fixture/full_order_with_3dsecure.php deleted file mode 100644 index 4f50b502e8554..0000000000000 --- a/dev/tests/integration/testsuite/Magento/AuthorizenetCardinal/Fixture/full_order_with_3dsecure.php +++ /dev/null @@ -1,21 +0,0 @@ -<?php -/** - * Copyright © Magento, Inc. All rights reserved. - * See COPYING.txt for license details. - */ - -declare(strict_types=1); - -use Magento\Sales\Model\Order\Payment; -use Magento\TestFramework\Helper\Bootstrap; - -$order = include __DIR__ . '/../../AuthorizenetAcceptjs/_files/full_order.php'; - -$objectManager = Bootstrap::getObjectManager(); -$cardinalJWT = include __DIR__ . '/response/cardinal_jwt.php'; - -/** @var Payment $payment */ -$payment = $order->getPayment(); -$payment->setAdditionalInformation('cardinalJWT', $cardinalJWT); - -return $order; diff --git a/dev/tests/integration/testsuite/Magento/AuthorizenetCardinal/Fixture/response/authorize.php b/dev/tests/integration/testsuite/Magento/AuthorizenetCardinal/Fixture/response/authorize.php deleted file mode 100644 index c17b14721b157..0000000000000 --- a/dev/tests/integration/testsuite/Magento/AuthorizenetCardinal/Fixture/response/authorize.php +++ /dev/null @@ -1,47 +0,0 @@ -<?php -/** - * Copyright © Magento, Inc. All rights reserved. - * See COPYING.txt for license details. - */ - -declare(strict_types=1); - -return [ - 'transactionResponse' => [ - 'responseCode' => '1', - 'authCode' => 'abc123', - 'avsResultCode' => 'P', - 'cvvResultCode' => '', - 'cavvResultCode' => '2', - 'transId' => '123456', - 'refTransID' => '', - 'transHash' => 'foobar', - 'testRequest' => '0', - 'accountNumber' => 'XXXX1111', - 'accountType' => 'Visa', - 'messages' => [ - [ - 'code' => '1', - 'description' => 'This transaction has been approved.' - ] - ], - 'userFields' => [ - [ - 'name' => 'transactionType', - 'value' => 'authOnlyTransaction' - ] - ], - 'transHashSha2' => 'CD1E57FB1B5C876FDBD536CB16F8BBBA687580EDD78DD881C7F14DC4467C32BF6C' - . '808620FBD59E5977DF19460B98CCFC0DA0D90755992C0D611CABB8E2BA52B0', - 'SupplementalDataQualificationIndicator' => 0 - ], - 'messages' => [ - 'resultCode' => 'Ok', - 'message' => [ - [ - 'code' => 'I00001', - 'text' => 'Successful.' - ] - ] - ] -]; diff --git a/dev/tests/integration/testsuite/Magento/AuthorizenetCardinal/Fixture/response/cardinal_jwt.php b/dev/tests/integration/testsuite/Magento/AuthorizenetCardinal/Fixture/response/cardinal_jwt.php deleted file mode 100644 index 80f42524897f6..0000000000000 --- a/dev/tests/integration/testsuite/Magento/AuthorizenetCardinal/Fixture/response/cardinal_jwt.php +++ /dev/null @@ -1,48 +0,0 @@ -<?php -/** - * Copyright © Magento, Inc. All rights reserved. - * See COPYING.txt for license details. - */ - -declare(strict_types=1); - -use Magento\CardinalCommerce\Model\Config; -use Magento\CardinalCommerce\Model\JwtManagement; -use Magento\TestFramework\Helper\Bootstrap; - -$objectManager = Bootstrap::getObjectManager(); -/** @var JwtManagement $jwtManagment */ -$jwtManagment = $objectManager->get(JwtManagement::class); -/** @var Config $config */ -$config = $objectManager->get(Config::class); -$currentDate = new \DateTime('now', new \DateTimeZone('UTC')); -$response = [ - 'iss' => 'some_api_identifier', - 'iat' => 1559855656, - 'exp' => $currentDate->getTimestamp() + 3600, - 'jti' => '0d695df5-ca06-4f7d-b150-ff169510f6d2', - 'ConsumerSessionId' => '0_9e6a4084-2191-4fd7-9631-19f576375e0a', - 'ReferenceId' => '0_9e6a4084-2191-4fd7-9631-19f576375e0a', - 'aud' => '52efb9cc-843c-4ee9-a38c-107943be6b03', - 'Payload' => [ - 'Validated' => true, - 'Payment' => [ - 'Type' => 'CCA', - 'ProcessorTransactionId' => '4l7xg1WA7CS0YwgPgNZ0', - 'ExtendedData' => [ - 'CAVV' => 'AAABAWFlmQAAAABjRWWZEEFgFz8=', - 'ECIFlag' => '05', - 'XID' => 'NGw3eGcxV0E3Q1MwWXdnUGdOWjA=', - 'Enrolled' => 'Y', - 'PAResStatus' => 'Y', - 'SignatureVerification' => 'Y', - ], - ], - 'ActionCode' => 'SUCCESS', - 'ErrorNumber' => 0, - 'ErrorDescription' => 'Success', - ], -]; -$cardinalJWT = $jwtManagment->encode($response, $config->getApiKey()); - -return $cardinalJWT; diff --git a/dev/tests/integration/testsuite/Magento/AuthorizenetCardinal/Gateway/Command/AuthorizeCommandTest.php b/dev/tests/integration/testsuite/Magento/AuthorizenetCardinal/Gateway/Command/AuthorizeCommandTest.php deleted file mode 100644 index 561734b089213..0000000000000 --- a/dev/tests/integration/testsuite/Magento/AuthorizenetCardinal/Gateway/Command/AuthorizeCommandTest.php +++ /dev/null @@ -1,187 +0,0 @@ -<?php -/** - * Copyright © Magento, Inc. All rights reserved. - * See COPYING.txt for license details. - */ - -declare(strict_types=1); - -namespace Magento\AuthorizenetCardinal\Gateway\Command; - -use Magento\AuthorizenetAcceptjs\Gateway\AbstractTest; -use Magento\Framework\Exception\LocalizedException; -use Magento\Payment\Gateway\Command\CommandPoolInterface; -use Magento\Sales\Model\Order\Payment; -use Magento\Sales\Model\Order\Payment\Transaction; - -/** - * Tests "Authorize" command for Authorize.net payment requests with 3D-Secure. - */ -class AuthorizeCommandTest extends AbstractTest -{ - /** - * Tests Authorize command with enabled 3D secure and valid Cardinal response JWT. - * - * @magentoConfigFixture default_store three_d_secure/cardinal/enabled_authorizenet 1 - * @magentoConfigFixture default_store three_d_secure/cardinal/environment sandbox - * @magentoConfigFixture default_store three_d_secure/cardinal/api_key some_api_key - * @magentoConfigFixture default_store three_d_secure/cardinal/api_identifier some_api_identifier - * @magentoConfigFixture default_store three_d_secure/cardinal/org_unit_id some_org_unit_id - * @magentoConfigFixture default_store payment/authorizenet_acceptjs/environment sandbox - * @magentoConfigFixture default_store payment/authorizenet_acceptjs/login someusername - * @magentoConfigFixture default_store payment/authorizenet_acceptjs/trans_key somepassword - * @magentoConfigFixture default_store payment/authorizenet_acceptjs/trans_signature_key abc - * - * @magentoAppIsolation enabled - */ - public function testAuthorizeCommandWith3dSecure() - { - /** @var CommandPoolInterface $commandPool */ - $commandPool = $this->objectManager->get('AuthorizenetAcceptjsCommandPool'); - $command = $commandPool->get('authorize'); - - $order = include __DIR__ . '/../../Fixture/full_order_with_3dsecure.php'; - $payment = $order->getPayment(); - - $paymentDO = $this->paymentFactory->create($payment); - - $expectedRequest = include __DIR__ . '/../../Fixture/expected_request/authorize.php'; - $response = include __DIR__ . '/../../Fixture/response/authorize.php'; - - $this->clientMock->method('setRawData') - ->with(json_encode($expectedRequest), 'application/json'); - - $this->responseMock->method('getBody') - ->willReturn(json_encode($response)); - - $command->execute( - [ - 'payment' => $paymentDO, - 'amount' => 100.00 - ] - ); - - /** @var Payment $payment */ - $rawDetails = [ - 'authCode' => 'abc123', - 'avsResultCode' => 'P', - 'cvvResultCode' => '', - 'cavvResultCode' => '2', - 'accountType' => 'Visa', - ]; - $this->assertSame('1111', $payment->getCcLast4()); - $this->assertSame('P', $payment->getCcAvsStatus()); - $this->assertFalse($payment->getData('is_transaction_closed')); - - $transactionDetails = $payment->getTransactionAdditionalInfo(); - foreach ($rawDetails as $key => $value) { - $this->assertSame($value, $payment->getAdditionalInformation($key)); - $this->assertSame($value, $transactionDetails[Transaction::RAW_DETAILS][$key]); - } - - $this->assertSame('123456', $payment->getTransactionId()); - $this->assertSame('123456', $transactionDetails['real_transaction_id']); - } - - /** - * Tests Authorize command with enabled 3D secure and invalid Cardinal response JWT. - * - * @magentoConfigFixture default_store three_d_secure/cardinal/enabled_authorizenet 1 - * @magentoConfigFixture default_store three_d_secure/cardinal/environment sandbox - * @magentoConfigFixture default_store three_d_secure/cardinal/api_key some_api_key - * @magentoConfigFixture default_store three_d_secure/cardinal/api_identifier some_api_identifier - * @magentoConfigFixture default_store three_d_secure/cardinal/org_unit_id some_org_unit_id - * @magentoConfigFixture default_store payment/authorizenet_acceptjs/environment sandbox - * @magentoConfigFixture default_store payment/authorizenet_acceptjs/login someusername - * @magentoConfigFixture default_store payment/authorizenet_acceptjs/trans_key somepassword - * @magentoConfigFixture default_store payment/authorizenet_acceptjs/trans_signature_key abc - * - * @magentoAppIsolation enabled - */ - public function testAuthorizeCommandWithInvalidJwt() - { - /** @var CommandPoolInterface $commandPool */ - $commandPool = $this->objectManager->get('AuthorizenetAcceptjsCommandPool'); - $command = $commandPool->get('authorize'); - - $order = include __DIR__ . '/../../../AuthorizenetAcceptjs/_files/full_order.php'; - $payment = $order->getPayment(); - $payment->setAdditionalInformation('cardinalJWT', 'Invalid JWT'); - - $paymentDO = $this->paymentFactory->create($payment); - - $this->expectException(LocalizedException::class); - - $command->execute( - [ - 'payment' => $paymentDO, - 'amount' => 100.00 - ] - ); - } - - /** - * Tests Authorize command with disabled 3D secure. - * - * @magentoConfigFixture default_store three_d_secure/cardinal/enabled_authorizenet 0 - * @magentoConfigFixture default_store three_d_secure/cardinal/environment sandbox - * @magentoConfigFixture default_store three_d_secure/cardinal/api_key some_api_key - * @magentoConfigFixture default_store three_d_secure/cardinal/api_identifier some_api_identifier - * @magentoConfigFixture default_store three_d_secure/cardinal/org_unit_id some_org_unit_id - * @magentoConfigFixture default_store payment/authorizenet_acceptjs/environment sandbox - * @magentoConfigFixture default_store payment/authorizenet_acceptjs/login someusername - * @magentoConfigFixture default_store payment/authorizenet_acceptjs/trans_key somepassword - * @magentoConfigFixture default_store payment/authorizenet_acceptjs/trans_signature_key abc - * - * @magentoAppIsolation enabled - */ - public function testAuthorizeCommandWithDisabled3dSecure() - { - /** @var CommandPoolInterface $commandPool */ - $commandPool = $this->objectManager->get('AuthorizenetAcceptjsCommandPool'); - $command = $commandPool->get('authorize'); - - $order = include __DIR__ . '/../../Fixture/full_order_with_3dsecure.php'; - $payment = $order->getPayment(); - - $paymentDO = $this->paymentFactory->create($payment); - - $expectedRequest = include __DIR__ . '/../../../AuthorizenetAcceptjs/_files/expected_request/authorize.php'; - $response = include __DIR__ . '/../../../AuthorizenetAcceptjs/_files/response/authorize.php'; - $response['transactionResponse']['cavvResultCode'] = '0'; - - $this->clientMock->method('setRawData') - ->with(json_encode($expectedRequest), 'application/json'); - - $this->responseMock->method('getBody') - ->willReturn(json_encode($response)); - - $command->execute( - [ - 'payment' => $paymentDO, - 'amount' => 100.00 - ] - ); - - /** @var Payment $payment */ - $rawDetails = [ - 'authCode' => 'abc123', - 'avsResultCode' => 'Y', - 'cvvResultCode' => 'P', - 'cavvResultCode' => '0', - 'accountType' => 'Visa', - ]; - $this->assertSame('1111', $payment->getCcLast4()); - $this->assertSame('Y', $payment->getCcAvsStatus()); - $this->assertFalse($payment->getData('is_transaction_closed')); - - $transactionDetails = $payment->getTransactionAdditionalInfo(); - foreach ($rawDetails as $key => $value) { - $this->assertSame($value, $payment->getAdditionalInformation($key)); - $this->assertSame($value, $transactionDetails[Transaction::RAW_DETAILS][$key]); - } - - $this->assertSame('123456', $payment->getTransactionId()); - $this->assertSame('123456', $transactionDetails['real_transaction_id']); - } -} diff --git a/dev/tests/integration/testsuite/Magento/AuthorizenetCardinal/Gateway/Command/SaleCommandTest.php b/dev/tests/integration/testsuite/Magento/AuthorizenetCardinal/Gateway/Command/SaleCommandTest.php deleted file mode 100644 index c22e1fceaa84f..0000000000000 --- a/dev/tests/integration/testsuite/Magento/AuthorizenetCardinal/Gateway/Command/SaleCommandTest.php +++ /dev/null @@ -1,85 +0,0 @@ -<?php -/** - * Copyright © Magento, Inc. All rights reserved. - * See COPYING.txt for license details. - */ - -declare(strict_types=1); - -namespace Magento\AuthorizenetCardinal\Gateway\Command; - -use Magento\AuthorizenetAcceptjs\Gateway\AbstractTest; -use Magento\Framework\Exception\LocalizedException; -use Magento\Payment\Gateway\Command\CommandPoolInterface; -use Magento\Sales\Model\Order\Payment; -use Magento\Sales\Model\Order\Payment\Transaction; - -/** - * Tests "Sale" command for Authorize.net payment requests with 3D-Secure. - */ -class SaleCommandTest extends AbstractTest -{ - /** - * Tests Sale command with enabled 3D secure and valid Cardinal response JWT. - * - * @magentoConfigFixture default_store three_d_secure/cardinal/enabled_authorizenet 1 - * @magentoConfigFixture default_store three_d_secure/cardinal/environment sandbox - * @magentoConfigFixture default_store three_d_secure/cardinal/api_key some_api_key - * @magentoConfigFixture default_store three_d_secure/cardinal/api_identifier some_api_identifier - * @magentoConfigFixture default_store three_d_secure/cardinal/org_unit_id some_org_unit_id - * @magentoConfigFixture default_store payment/authorizenet_acceptjs/environment sandbox - * @magentoConfigFixture default_store payment/authorizenet_acceptjs/login someusername - * @magentoConfigFixture default_store payment/authorizenet_acceptjs/trans_key somepassword - * @magentoConfigFixture default_store payment/authorizenet_acceptjs/trans_signature_key abc - * - * @magentoAppIsolation enabled - */ - public function testSaleCommandWith3dSecure() - { - /** @var CommandPoolInterface $commandPool */ - $commandPool = $this->objectManager->get('AuthorizenetAcceptjsCommandPool'); - $command = $commandPool->get('sale'); - - $order = include __DIR__ . '/../../Fixture/full_order_with_3dsecure.php'; - $payment = $order->getPayment(); - - $paymentDO = $this->paymentFactory->create($payment); - - $expectedRequest = include __DIR__ . '/../../Fixture/expected_request/sale.php'; - $response = include __DIR__ . '/../../../AuthorizenetAcceptjs/_files/response/sale.php'; - - $this->clientMock->method('setRawData') - ->with(json_encode($expectedRequest), 'application/json'); - - $this->responseMock->method('getBody') - ->willReturn(json_encode($response)); - - $command->execute( - [ - 'payment' => $paymentDO, - 'amount' => 100.00 - ] - ); - - /** @var Payment $payment */ - $rawDetails = [ - 'authCode' => 'abc123', - 'avsResultCode' => 'Y', - 'cvvResultCode' => 'P', - 'cavvResultCode' => '2', - 'accountType' => 'Visa', - ]; - $this->assertSame('1111', $payment->getCcLast4()); - $this->assertSame('Y', $payment->getCcAvsStatus()); - $this->assertFalse($payment->getData('is_transaction_closed')); - - $transactionDetails = $payment->getTransactionAdditionalInfo(); - foreach ($rawDetails as $key => $value) { - $this->assertSame($value, $payment->getAdditionalInformation($key)); - $this->assertSame($value, $transactionDetails[Transaction::RAW_DETAILS][$key]); - } - - $this->assertSame('123456', $payment->getTransactionId()); - $this->assertSame('123456', $transactionDetails['real_transaction_id']); - } -} diff --git a/dev/tests/integration/testsuite/Magento/AuthorizenetGraphQl/Model/Resolver/Customer/PlaceOrderWithAuthorizeNetTest.php b/dev/tests/integration/testsuite/Magento/AuthorizenetGraphQl/Model/Resolver/Customer/PlaceOrderWithAuthorizeNetTest.php deleted file mode 100644 index fa3869d49bd2a..0000000000000 --- a/dev/tests/integration/testsuite/Magento/AuthorizenetGraphQl/Model/Resolver/Customer/PlaceOrderWithAuthorizeNetTest.php +++ /dev/null @@ -1,158 +0,0 @@ -<?php -/** - * Copyright © Magento, Inc. All rights reserved. - * See COPYING.txt for license details. - */ -declare(strict_types=1); - -namespace Magento\AuthorizenetGraphQl\Model\Resolver\Customer; - -use Magento\Framework\Serialize\SerializerInterface; -use Magento\GraphQl\Quote\GetMaskedQuoteIdByReservedOrderId; -use Magento\GraphQl\Service\GraphQlRequest; -use Magento\Integration\Api\CustomerTokenServiceInterface; -use Magento\TestFramework\Helper\Bootstrap; -use Magento\Framework\HTTP\ZendClient; -use Magento\Framework\HTTP\ZendClientFactory; -use Magento\TestFramework\ObjectManager; -use PHPUnit\Framework\MockObject\Builder\InvocationMocker; -use PHPUnit\Framework\MockObject\MockObject; -use PHPUnit\Framework\TestCase; -use Zend_Http_Response; - -/** - * Tests end to end Place Order process for customer via authorizeNet - * - * @magentoAppArea graphql - * @magentoDbIsolation disabled - */ -class PlaceOrderWithAuthorizeNetTest extends TestCase -{ - /** @var ObjectManager */ - private $objectManager; - - /** @var GetMaskedQuoteIdByReservedOrderId */ - private $getMaskedQuoteIdByReservedOrderId; - - /** @var SerializerInterface */ - private $jsonSerializer; - - /** @var GraphQlRequest */ - private $graphQlRequest; - - /** @var ZendClient|MockObject|InvocationMocker */ - private $clientMock; - - /** @var CustomerTokenServiceInterface */ - private $customerTokenService; - - /** @var Zend_Http_Response */ - protected $responseMock; - - protected function setUp() : void - { - $this->objectManager = Bootstrap::getObjectManager(); - $this->jsonSerializer = $this->objectManager->get(SerializerInterface::class); - $this->graphQlRequest = $this->objectManager->create(GraphQlRequest::class); - $this->getMaskedQuoteIdByReservedOrderId = $this->objectManager->get(GetMaskedQuoteIdByReservedOrderId::class); - $this->customerTokenService = $this->objectManager->get(CustomerTokenServiceInterface::class); - $this->clientMock = $this->createMock(ZendClient::class); - $this->responseMock = $this->createMock(Zend_Http_Response::class); - $this->clientMock->method('request') - ->willReturn($this->responseMock); - $this->clientMock->method('setUri') - ->with('https://apitest.authorize.net/xml/v1/request.api'); - $clientFactoryMock = $this->createMock(ZendClientFactory::class); - $clientFactoryMock->method('create') - ->willReturn($this->clientMock); - $this->objectManager->addSharedInstance($clientFactoryMock, ZendClientFactory::class); - } - - /** - * @magentoConfigFixture default_store payment/authorizenet_acceptjs/active 1 - * @magentoConfigFixture default_store payment/authorizenet_acceptjs/environment sandbox - * @magentoConfigFixture default_store payment/authorizenet_acceptjs/login someusername - * @magentoConfigFixture default_store payment/authorizenet_acceptjs/trans_key somepassword - * @magentoConfigFixture default_store payment/authorizenet_acceptjs/trans_signature_key abc - * @magentoDataFixture Magento/Sales/_files/default_rollback.php - * @magentoDataFixture Magento/Customer/_files/customer.php - * @magentoDataFixture Magento/AuthorizenetGraphQl/_files/simple_product_authorizenet.php - * @magentoDataFixture Magento/GraphQl/Quote/_files/customer/create_empty_cart.php - * @magentoDataFixture Magento/AuthorizenetGraphQl/_files/set_new_shipping_address_authorizenet.php - * @magentoDataFixture Magento/AuthorizenetGraphQl/_files/set_new_billing_address_authorizenet.php - * @magentoDataFixture Magento/AuthorizenetGraphQl/_files/add_simple_products_authorizenet.php - * @magentoDataFixture Magento/GraphQl/Quote/_files/set_flatrate_shipping_method.php - */ - public function testDispatchToPlaceOrderWithRegisteredCustomer(): void - { - $paymentMethod = 'authorizenet_acceptjs'; - $cartId = $this->getMaskedQuoteIdByReservedOrderId->execute('test_quote'); - $query - = <<<QUERY - mutation { - setPaymentMethodOnCart(input: { - cart_id: "$cartId" - payment_method: { - code: "$paymentMethod" - authorizenet_acceptjs: - {opaque_data_descriptor: "mydescriptor", - opaque_data_value: "myvalue", - cc_last_4: 1111} - } - }) { - cart { - selected_payment_method { - code - } - } - } - placeOrder(input: {cart_id: "$cartId"}) { - order { - order_number - } - } -} -QUERY; - - $customerToken = $this->customerTokenService->createCustomerAccessToken('customer@example.com', 'password'); - $requestHeaders = [ - 'Accept' => 'application/json', - 'Authorization' => 'Bearer ' . $customerToken - ]; - // phpcs:ignore Magento2.Security.IncludeFile - $expectedRequest = include __DIR__ . '/../../../_files/request_authorize_customer.php'; - // phpcs:ignore Magento2.Security.IncludeFile - $authorizeResponse = include __DIR__ . '/../../../_files/response_authorize.php'; - - $this->clientMock->method('setRawData') - ->with(json_encode($expectedRequest), 'application/json'); - - $this->responseMock->method('getBody')->willReturn(json_encode($authorizeResponse)); - - $response = $this->graphQlRequest->send($query, [], '', $requestHeaders); - $responseData = $this->jsonSerializer->unserialize($response->getContent()); - - $this->assertArrayNotHasKey('errors', $responseData, 'Response has errors'); - $this->assertTrue( - isset($responseData['data']['setPaymentMethodOnCart']['cart']['selected_payment_method']['code']) - ); - $this->assertEquals( - $paymentMethod, - $responseData['data']['setPaymentMethodOnCart']['cart']['selected_payment_method']['code'] - ); - - $this->assertTrue( - isset($responseData['data']['placeOrder']['order']['order_number']) - ); - - $this->assertEquals( - 'test_quote', - $responseData['data']['placeOrder']['order']['order_number'] - ); - } - - protected function tearDown() - { - $this->objectManager->removeSharedInstance(ZendClientFactory::class); - } -} diff --git a/dev/tests/integration/testsuite/Magento/AuthorizenetGraphQl/Model/Resolver/Customer/SetAuthorizeNetPaymentMethodOnCartTest.php b/dev/tests/integration/testsuite/Magento/AuthorizenetGraphQl/Model/Resolver/Customer/SetAuthorizeNetPaymentMethodOnCartTest.php deleted file mode 100644 index b82469c61d288..0000000000000 --- a/dev/tests/integration/testsuite/Magento/AuthorizenetGraphQl/Model/Resolver/Customer/SetAuthorizeNetPaymentMethodOnCartTest.php +++ /dev/null @@ -1,102 +0,0 @@ -<?php -/** - * Copyright © Magento, Inc. All rights reserved. - * See COPYING.txt for license details. - */ -declare(strict_types=1); - -namespace Magento\AuthorizenetGraphQl\Model\Resolver\Customer; - -use Magento\Framework\Serialize\SerializerInterface; -use Magento\GraphQl\Quote\GetMaskedQuoteIdByReservedOrderId; -use Magento\GraphQl\Service\GraphQlRequest; -use Magento\Integration\Api\CustomerTokenServiceInterface; -use Magento\TestFramework\Helper\Bootstrap; -use PHPUnit\Framework\TestCase; - -/** - * Tests SetPaymentMethod mutation for customer via authorizeNet payment - * - * @magentoAppArea graphql - * @magentoDbIsolation disabled - */ -class SetAuthorizeNetPaymentMethodOnCartTest extends TestCase -{ - /** @var \Magento\Framework\ObjectManagerInterface */ - private $objectManager; - - /** @var GetMaskedQuoteIdByReservedOrderId */ - private $getMaskedQuoteIdByReservedOrderId; - - /** @var SerializerInterface */ - private $jsonSerializer; - - /** @var CustomerTokenServiceInterface */ - private $customerTokenService; - - /** @var GraphQlRequest */ - private $graphQlRequest; - - protected function setUp() : void - { - $this->objectManager = Bootstrap::getObjectManager(); - $this->jsonSerializer = $this->objectManager->get(SerializerInterface::class); - $this->graphQlRequest = $this->objectManager->create(GraphQlRequest::class); - $this->getMaskedQuoteIdByReservedOrderId = $this->objectManager->get(GetMaskedQuoteIdByReservedOrderId::class); - $this->customerTokenService = $this->objectManager->get(CustomerTokenServiceInterface::class); - } - - /** - * @magentoConfigFixture default_store payment/authorizenet_acceptjs/active 1 - * @magentoConfigFixture default_store payment/authorizenet_acceptjs/environment sandbox - * @magentoConfigFixture default_store payment/authorizenet_acceptjs/login someusername - * @magentoConfigFixture default_store payment/authorizenet_acceptjs/trans_key somepassword - * @magentoConfigFixture default_store payment/authorizenet_acceptjs/trans_signature_key abc - * @magentoDataFixture Magento/Customer/_files/customer.php - * @magentoDataFixture Magento/GraphQl/Catalog/_files/simple_product.php - * @magentoDataFixture Magento/GraphQl/Quote/_files/customer/create_empty_cart.php - * @magentoDataFixture Magento/GraphQl/Quote/_files/add_simple_product.php - * @magentoDataFixture Magento/GraphQl/Quote/_files/set_new_shipping_address.php - */ - public function testDispatchToSetPaymentMethodWithAuthorizenet(): void - { - $methodCode = 'authorizenet_acceptjs'; - $maskedQuoteId = $this->getMaskedQuoteIdByReservedOrderId->execute('test_quote'); - $query - = <<<QUERY - mutation { - setPaymentMethodOnCart(input: { - cart_id: "$maskedQuoteId" - payment_method: { - code: "$methodCode" - authorizenet_acceptjs: - {opaque_data_descriptor: "COMMON.ACCEPT.INAPP.PAYMENT", - opaque_data_value: "abx", - cc_last_4: 1111} - } - }) { - cart { - selected_payment_method { - code - } - } - } -} -QUERY; - - $customerToken = $this->customerTokenService->createCustomerAccessToken('customer@example.com', 'password'); - - $requestHeaders = [ - 'Accept' => 'application/json', - 'Authorization' => 'Bearer ' . $customerToken - ]; - - $response = $this->graphQlRequest->send($query, [], '', $requestHeaders); - - $output = $this->jsonSerializer->unserialize($response->getContent()); - $this->assertArrayNotHasKey('errors', $output, 'Response has errors'); - $this->assertArrayHasKey('setPaymentMethodOnCart', $output['data']); - $selectedPaymentMethod = $output['data']['setPaymentMethodOnCart']['cart']['selected_payment_method']; - $this->assertEquals($methodCode, $selectedPaymentMethod['code']); - } -} diff --git a/dev/tests/integration/testsuite/Magento/AuthorizenetGraphQl/Model/Resolver/Guest/PlaceOrderWithAuthorizeNetTest.php b/dev/tests/integration/testsuite/Magento/AuthorizenetGraphQl/Model/Resolver/Guest/PlaceOrderWithAuthorizeNetTest.php deleted file mode 100644 index 4946448f91ccc..0000000000000 --- a/dev/tests/integration/testsuite/Magento/AuthorizenetGraphQl/Model/Resolver/Guest/PlaceOrderWithAuthorizeNetTest.php +++ /dev/null @@ -1,148 +0,0 @@ -<?php -/** - * Copyright © Magento, Inc. All rights reserved. - * See COPYING.txt for license details. - */ -declare(strict_types=1); - -namespace Magento\AuthorizenetGraphQl\Model\Resolver\Guest; - -use Magento\Framework\Serialize\SerializerInterface; -use Magento\GraphQl\Quote\GetMaskedQuoteIdByReservedOrderId; -use Magento\GraphQl\Service\GraphQlRequest; -use Magento\TestFramework\Helper\Bootstrap; -use Magento\Framework\HTTP\ZendClient; -use Magento\Framework\HTTP\ZendClientFactory; -use Magento\TestFramework\ObjectManager; -use PHPUnit\Framework\MockObject\Builder\InvocationMocker; -use PHPUnit\Framework\MockObject\MockObject; -use PHPUnit\Framework\TestCase; -use Zend_Http_Response; - -/** - * Tests end to end Place Order process for non logged in customer using authorizeNet payment - * - * @magentoAppArea graphql - * @magentoDbIsolation disabled - */ -class PlaceOrderWithAuthorizeNetTest extends TestCase -{ - /** @var ObjectManager */ - private $objectManager; - - /** @var GetMaskedQuoteIdByReservedOrderId */ - private $getMaskedQuoteIdByReservedOrderId; - - /** @var SerializerInterface */ - private $jsonSerializer; - - /** @var GraphQlRequest */ - private $graphQlRequest; - - /** @var ZendClient|MockObject|InvocationMocker */ - private $clientMock; - - /** @var Zend_Http_Response */ - protected $responseMock; - - protected function setUp() : void - { - $this->objectManager = Bootstrap::getObjectManager(); - $this->jsonSerializer = $this->objectManager->get(SerializerInterface::class); - $this->getMaskedQuoteIdByReservedOrderId = $this->objectManager->get(GetMaskedQuoteIdByReservedOrderId::class); - $this->graphQlRequest = $this->objectManager->create(GraphQlRequest::class); - $this->clientMock = $this->createMock(ZendClient::class); - $this->responseMock = $this->createMock(Zend_Http_Response::class); - $this->clientMock->method('request') - ->willReturn($this->responseMock); - $this->clientMock->method('setUri') - ->with('https://apitest.authorize.net/xml/v1/request.api'); - $clientFactoryMock = $this->createMock(ZendClientFactory::class); - $clientFactoryMock->method('create') - ->willReturn($this->clientMock); - $this->objectManager->addSharedInstance($clientFactoryMock, ZendClientFactory::class); - } - - protected function tearDown() - { - $this->objectManager->removeSharedInstance(ZendClientFactory::class); - } - - /** - * @magentoConfigFixture default_store payment/authorizenet_acceptjs/active 1 - * @magentoConfigFixture default_store payment/authorizenet_acceptjs/environment sandbox - * @magentoConfigFixture default_store payment/authorizenet_acceptjs/login someusername - * @magentoConfigFixture default_store payment/authorizenet_acceptjs/trans_key somepassword - * @magentoConfigFixture default_store payment/authorizenet_acceptjs/trans_signature_key abc - * @magentoDataFixture Magento/Sales/_files/default_rollback.php - * @magentoDataFixture Magento/AuthorizenetGraphQl/_files/simple_product_authorizenet.php - * @magentoDataFixture Magento/GraphQl/Quote/_files/guest/create_empty_cart.php - * @magentoDataFixture Magento/GraphQl/Quote/_files/guest/set_guest_email.php - * @magentoDataFixture Magento/AuthorizenetGraphQl/_files/set_new_shipping_address_authorizenet.php - * @magentoDataFixture Magento/AuthorizenetGraphQl/_files/set_new_billing_address_authorizenet.php - * @magentoDataFixture Magento/AuthorizenetGraphQl/_files/add_simple_products_authorizenet.php - * @magentoDataFixture Magento/GraphQl/Quote/_files/set_flatrate_shipping_method.php - */ - public function testDispatchToPlaceAnOrderWithAuthorizenet(): void - { - $paymentMethod = 'authorizenet_acceptjs'; - $cartId = $this->getMaskedQuoteIdByReservedOrderId->execute('test_quote'); - $query - = <<<QUERY - mutation { - setPaymentMethodOnCart(input: { - cart_id: "$cartId" - payment_method: { - code: "$paymentMethod" - authorizenet_acceptjs: - {opaque_data_descriptor: "mydescriptor", - opaque_data_value: "myvalue", - cc_last_4: 1111} - } - }) { - cart { - selected_payment_method { - code - } - } - } - placeOrder(input: {cart_id: "$cartId"}) { - order { - order_number - } - } -} -QUERY; - - // phpcs:ignore Magento2.Security.IncludeFile - $expectedRequest = include __DIR__ . '/../../../_files/request_authorize.php'; - // phpcs:ignore Magento2.Security.IncludeFile - $authorizeResponse = include __DIR__ . '/../../../_files/response_authorize.php'; - - $this->clientMock->method('setRawData') - ->with(json_encode($expectedRequest), 'application/json'); - - $this->responseMock->method('getBody')->willReturn(json_encode($authorizeResponse)); - - $response = $this->graphQlRequest->send($query); - $responseData = $this->jsonSerializer->unserialize($response->getContent()); - - $this->assertArrayNotHasKey('errors', $responseData, 'Response has errors'); - $this->assertTrue( - isset($responseData['data']['setPaymentMethodOnCart']['cart']['selected_payment_method']['code']) - ); - $this->assertEquals( - $paymentMethod, - $responseData['data']['setPaymentMethodOnCart']['cart']['selected_payment_method']['code'] - ); - - $this->assertTrue( - isset($responseData['data']['placeOrder']['order']['order_number']) - ); - - $this->assertEquals( - 'test_quote', - $responseData['data']['placeOrder']['order']['order_number'] - ); - } -} diff --git a/dev/tests/integration/testsuite/Magento/AuthorizenetGraphQl/Model/Resolver/Guest/SetAuthorizeNetPaymentMethodOnCartTest.php b/dev/tests/integration/testsuite/Magento/AuthorizenetGraphQl/Model/Resolver/Guest/SetAuthorizeNetPaymentMethodOnCartTest.php deleted file mode 100644 index ff526a491b5d7..0000000000000 --- a/dev/tests/integration/testsuite/Magento/AuthorizenetGraphQl/Model/Resolver/Guest/SetAuthorizeNetPaymentMethodOnCartTest.php +++ /dev/null @@ -1,89 +0,0 @@ -<?php -/** - * Copyright © Magento, Inc. All rights reserved. - * See COPYING.txt for license details. - */ -declare(strict_types=1); - -namespace Magento\AuthorizenetGraphQl\Model\Resolver\Guest; - -use Magento\Framework\Serialize\SerializerInterface; -use Magento\GraphQl\Quote\GetMaskedQuoteIdByReservedOrderId; -use Magento\GraphQl\Service\GraphQlRequest; -use Magento\TestFramework\Helper\Bootstrap; -use Magento\TestFramework\ObjectManager; -use PHPUnit\Framework\TestCase; - -/** - * Tests SetPaymentMethod mutation for guest via authorizeNet payment - * - * @magentoAppArea graphql - * @magentoDbIsolation disabled - */ -class SetAuthorizeNetPaymentMethodOnCartTest extends TestCase -{ - /** @var ObjectManager */ - private $objectManager; - - /** @var GetMaskedQuoteIdByReservedOrderId */ - private $getMaskedQuoteIdByReservedOrderId; - - /** @var SerializerInterface */ - private $jsonSerializer; - - /** @var GraphQlRequest */ - private $graphQlRequest; - - protected function setUp() : void - { - $this->objectManager = Bootstrap::getObjectManager(); - $this->jsonSerializer = $this->objectManager->get(SerializerInterface::class); - $this->getMaskedQuoteIdByReservedOrderId = $this->objectManager->get(GetMaskedQuoteIdByReservedOrderId::class); - $this->graphQlRequest = $this->objectManager->create(GraphQlRequest::class); - } - - /** - * @magentoConfigFixture default_store payment/authorizenet_acceptjs/active 1 - * @magentoConfigFixture default_store payment/authorizenet_acceptjs/environment sandbox - * @magentoConfigFixture default_store payment/authorizenet_acceptjs/login someusername - * @magentoConfigFixture default_store payment/authorizenet_acceptjs/trans_key somepassword - * @magentoConfigFixture default_store payment/authorizenet_acceptjs/trans_signature_key abc - * @magentoDataFixture Magento/GraphQl/Catalog/_files/simple_product.php - * @magentoDataFixture Magento/GraphQl/Quote/_files/guest/create_empty_cart.php - * @magentoDataFixture Magento/GraphQl/Quote/_files/add_simple_product.php - * @magentoDataFixture Magento/GraphQl/Quote/_files/set_new_shipping_address.php - */ - public function testDispatchToSetPaymentMethodWithAuthorizenet(): void - { - $methodCode = 'authorizenet_acceptjs'; - $maskedQuoteId = $this->getMaskedQuoteIdByReservedOrderId->execute('test_quote'); - $query - = <<<QUERY - mutation { - setPaymentMethodOnCart(input: { - cart_id: "$maskedQuoteId" - payment_method: { - code: "$methodCode" - authorizenet_acceptjs: - {opaque_data_descriptor: "COMMON.ACCEPT.INAPP.PAYMENT", - opaque_data_value: "abx", - cc_last_4: 1111} - } - }) { - cart { - selected_payment_method { - code - } - } - } -} -QUERY; - - $response = $this->graphQlRequest->send($query); - $output = $this->jsonSerializer->unserialize($response->getContent()); - $this->assertArrayNotHasKey('errors', $output, 'Response has errors'); - $this->assertArrayHasKey('setPaymentMethodOnCart', $output['data']); - $selectedPaymentMethod = $output['data']['setPaymentMethodOnCart']['cart']['selected_payment_method']; - $this->assertEquals($methodCode, $selectedPaymentMethod['code']); - } -} diff --git a/dev/tests/integration/testsuite/Magento/AuthorizenetGraphQl/_files/add_simple_products_authorizenet.php b/dev/tests/integration/testsuite/Magento/AuthorizenetGraphQl/_files/add_simple_products_authorizenet.php deleted file mode 100644 index 3646e864ab49e..0000000000000 --- a/dev/tests/integration/testsuite/Magento/AuthorizenetGraphQl/_files/add_simple_products_authorizenet.php +++ /dev/null @@ -1,28 +0,0 @@ -<?php -/** - * Copyright © Magento, Inc. All rights reserved. - * See COPYING.txt for license details. - */ -declare(strict_types=1); - -use Magento\Catalog\Api\ProductRepositoryInterface; -use Magento\Quote\Api\CartRepositoryInterface; -use Magento\Quote\Model\QuoteFactory; -use Magento\Quote\Model\ResourceModel\Quote as QuoteResource; -use Magento\TestFramework\Helper\Bootstrap; - -/** @var ProductRepositoryInterface $productRepository */ -$productRepository = Bootstrap::getObjectManager()->get(ProductRepositoryInterface::class); -/** @var QuoteFactory $quoteFactory */ -$quoteFactory = Bootstrap::getObjectManager()->get(QuoteFactory::class); -/** @var QuoteResource $quoteResource */ -$quoteResource = Bootstrap::getObjectManager()->get(QuoteResource::class); -/** @var CartRepositoryInterface $cartRepository */ -$cartRepository = Bootstrap::getObjectManager()->get(CartRepositoryInterface::class); - -$product = $productRepository->get('simple_product'); - -$quote = $quoteFactory->create(); -$quoteResource->load($quote, 'test_quote', 'reserved_order_id'); -$quote->addProduct($product, 4); -$cartRepository->save($quote); diff --git a/dev/tests/integration/testsuite/Magento/AuthorizenetGraphQl/_files/request_authorize.php b/dev/tests/integration/testsuite/Magento/AuthorizenetGraphQl/_files/request_authorize.php deleted file mode 100644 index c91c8081736c4..0000000000000 --- a/dev/tests/integration/testsuite/Magento/AuthorizenetGraphQl/_files/request_authorize.php +++ /dev/null @@ -1,65 +0,0 @@ -<?php -/** - * Copyright © Magento, Inc. All rights reserved. - * See COPYING.txt for license details. - */ - -declare(strict_types=1); - -return [ - 'createTransactionRequest' => [ - 'merchantAuthentication' =>[ - 'name' => 'someusername', - 'transactionKey' => 'somepassword', - ], - 'transactionRequest' => [ - 'transactionType' => 'authOnlyTransaction', - 'amount' => '100.00', - 'payment' => [ - 'opaqueData' => [ - 'dataDescriptor' => 'mydescriptor', - 'dataValue' => 'myvalue', - ], - ], - 'solution' => [ - 'id' => 'AAA102993', - ], - 'order' => [ - 'invoiceNumber' => 'test_quote', - ], - 'poNumber' => null, - 'customer' => [ - 'id' => null, - 'email' => 'guest@example.com', - ], - 'billTo' => [ - 'firstName' => 'firstname', - 'lastName' => 'lastname', - 'company' => '', - 'address' => 'street', - 'city' => 'Los Angeles', - 'state' => 'CA', - 'zip' => '11111', - 'country' => 'US', - ], - 'shipTo' => [ - 'firstName' => 'John', - 'lastName' => 'Doe', - 'company' => '', - 'address' => '6161 West Centinela Avenue', - 'city' => 'Los Angeles', - 'state' => 'CA', - 'zip' => '11111', - 'country' => 'US', - ], - 'userFields' => [ - 'userField' => [ - [ - 'name' => 'transactionType', - 'value' => 'authOnlyTransaction', - ], - ], - ], - ], - ] -]; diff --git a/dev/tests/integration/testsuite/Magento/AuthorizenetGraphQl/_files/request_authorize_customer.php b/dev/tests/integration/testsuite/Magento/AuthorizenetGraphQl/_files/request_authorize_customer.php deleted file mode 100644 index 0ef173009bd6c..0000000000000 --- a/dev/tests/integration/testsuite/Magento/AuthorizenetGraphQl/_files/request_authorize_customer.php +++ /dev/null @@ -1,65 +0,0 @@ -<?php -/** - * Copyright © Magento, Inc. All rights reserved. - * See COPYING.txt for license details. - */ - -declare(strict_types=1); - -return [ - 'createTransactionRequest' => [ - 'merchantAuthentication' =>[ - 'name' => 'someusername', - 'transactionKey' => 'somepassword', - ], - 'transactionRequest' => [ - 'transactionType' => 'authOnlyTransaction', - 'amount' => '100.00', - 'payment' => [ - 'opaqueData' => [ - 'dataDescriptor' => 'mydescriptor', - 'dataValue' => 'myvalue', - ], - ], - 'solution' => [ - 'id' => 'AAA102993', - ], - 'order' => [ - 'invoiceNumber' => 'test_quote', - ], - 'poNumber' => null, - 'customer' => [ - 'id' => '1', - 'email' => 'customer@example.com', - ], - 'billTo' => [ - 'firstName' => 'firstname', - 'lastName' => 'lastname', - 'company' => '', - 'address' => 'street', - 'city' => 'Los Angeles', - 'state' => 'CA', - 'zip' => '11111', - 'country' => 'US', - ], - 'shipTo' => [ - 'firstName' => 'John', - 'lastName' => 'Doe', - 'company' => '', - 'address' => '6161 West Centinela Avenue', - 'city' => 'Los Angeles', - 'state' => 'CA', - 'zip' => '11111', - 'country' => 'US', - ], - 'userFields' => [ - 'userField' => [ - [ - 'name' => 'transactionType', - 'value' => 'authOnlyTransaction', - ], - ], - ], - ], - ] -]; diff --git a/dev/tests/integration/testsuite/Magento/AuthorizenetGraphQl/_files/response_authorize.php b/dev/tests/integration/testsuite/Magento/AuthorizenetGraphQl/_files/response_authorize.php deleted file mode 100644 index f80495137ca29..0000000000000 --- a/dev/tests/integration/testsuite/Magento/AuthorizenetGraphQl/_files/response_authorize.php +++ /dev/null @@ -1,47 +0,0 @@ -<?php -/** - * Copyright © Magento, Inc. All rights reserved. - * See COPYING.txt for license details. - */ - -declare(strict_types=1); - -return [ - 'transactionResponse' => [ - 'responseCode' => '1', - 'authCode' => 'abc123', - 'avsResultCode' => 'Y', - 'cvvResultCode' => 'P', - 'cavvResultCode' => '2', - 'transId' => '123456', - 'refTransID' => '', - 'transHash' => 'foobar', - 'testRequest' => '0', - 'accountNumber' => 'XXXX1111', - 'accountType' => 'Visa', - 'messages' => [ - [ - 'code' => '1', - 'description' => 'This transaction has been approved.' - ] - ], - 'userFields' => [ - [ - 'name' => 'transactionType', - 'value' => 'authOnlyTransaction' - ] - ], - 'transHashSha2' => 'CD1E57FB1B5C876FDBD536CB16F8BBBA687580EDD78DD881C7F14DC4467C32BF6C' - . '808620FBD59E5977DF19460B98CCFC0DA0D90755992C0D611CABB8E2BA52B0', - 'SupplementalDataQualificationIndicator' => 0 - ], - 'messages' => [ - 'resultCode' => 'Ok', - 'message' => [ - [ - 'code' => 'I00001', - 'text' => 'Successful.' - ] - ] - ] -]; diff --git a/dev/tests/integration/testsuite/Magento/AuthorizenetGraphQl/_files/set_new_billing_address_authorizenet.php b/dev/tests/integration/testsuite/Magento/AuthorizenetGraphQl/_files/set_new_billing_address_authorizenet.php deleted file mode 100644 index 4f045c550cd37..0000000000000 --- a/dev/tests/integration/testsuite/Magento/AuthorizenetGraphQl/_files/set_new_billing_address_authorizenet.php +++ /dev/null @@ -1,44 +0,0 @@ -<?php -/** - * Copyright © Magento, Inc. All rights reserved. - * See COPYING.txt for license details. - */ -declare(strict_types=1); - -use Magento\Framework\Api\DataObjectHelper; -use Magento\Quote\Api\Data\AddressInterface; -use Magento\Quote\Api\Data\AddressInterfaceFactory; -use Magento\Quote\Api\BillingAddressManagementInterface; -use Magento\Quote\Model\QuoteFactory; -use Magento\Quote\Model\ResourceModel\Quote as QuoteResource; - -use Magento\TestFramework\Helper\Bootstrap; - -/** @var QuoteFactory $quoteFactory */ -$quoteFactory = Bootstrap::getObjectManager()->get(QuoteFactory::class); -/** @var QuoteResource $quoteResource */ -$quoteResource = Bootstrap::getObjectManager()->get(QuoteResource::class); -/** @var AddressInterfaceFactory $quoteAddressFactory */ -$quoteAddressFactory = Bootstrap::getObjectManager()->get(AddressInterfaceFactory::class); -/** @var DataObjectHelper $dataObjectHelper */ -$dataObjectHelper = Bootstrap::getObjectManager()->get(DataObjectHelper::class); -/** @var BillingAddressManagementInterface $billingAddressManagement */ -$billingAddressManagement = Bootstrap::getObjectManager()->get(BillingAddressManagementInterface::class); - -$quoteAddressData = [ - AddressInterface::KEY_TELEPHONE => 11111111, - AddressInterface::KEY_POSTCODE => 11111, - AddressInterface::KEY_COUNTRY_ID => 'US', - AddressInterface::KEY_CITY => 'Los Angeles', - AddressInterface::KEY_COMPANY => '', - AddressInterface::KEY_STREET => 'street', - AddressInterface::KEY_LASTNAME => 'lastname', - AddressInterface::KEY_FIRSTNAME => 'firstname', - AddressInterface::KEY_REGION_ID => 12, -]; -$quoteAddress = $quoteAddressFactory->create(); -$dataObjectHelper->populateWithArray($quoteAddress, $quoteAddressData, AddressInterfaceFactory::class); - -$quote = $quoteFactory->create(); -$quoteResource->load($quote, 'test_quote', 'reserved_order_id'); -$billingAddressManagement->assign($quote->getId(), $quoteAddress); diff --git a/dev/tests/integration/testsuite/Magento/AuthorizenetGraphQl/_files/set_new_shipping_address_authorizenet.php b/dev/tests/integration/testsuite/Magento/AuthorizenetGraphQl/_files/set_new_shipping_address_authorizenet.php deleted file mode 100644 index 8837a3cb2397c..0000000000000 --- a/dev/tests/integration/testsuite/Magento/AuthorizenetGraphQl/_files/set_new_shipping_address_authorizenet.php +++ /dev/null @@ -1,43 +0,0 @@ -<?php -/** - * Copyright © Magento, Inc. All rights reserved. - * See COPYING.txt for license details. - */ -declare(strict_types=1); - -use Magento\Framework\Api\DataObjectHelper; -use Magento\Quote\Api\Data\AddressInterface; -use Magento\Quote\Api\Data\AddressInterfaceFactory; -use Magento\Quote\Model\QuoteFactory; -use Magento\Quote\Model\ResourceModel\Quote as QuoteResource; -use Magento\Quote\Model\ShippingAddressManagementInterface; -use Magento\TestFramework\Helper\Bootstrap; - -/** @var QuoteFactory $quoteFactory */ -$quoteFactory = Bootstrap::getObjectManager()->get(QuoteFactory::class); -/** @var QuoteResource $quoteResource */ -$quoteResource = Bootstrap::getObjectManager()->get(QuoteResource::class); -/** @var AddressInterfaceFactory $quoteAddressFactory */ -$quoteAddressFactory = Bootstrap::getObjectManager()->get(AddressInterfaceFactory::class); -/** @var DataObjectHelper $dataObjectHelper */ -$dataObjectHelper = Bootstrap::getObjectManager()->get(DataObjectHelper::class); -/** @var ShippingAddressManagementInterface $shippingAddressManagement */ -$shippingAddressManagement = Bootstrap::getObjectManager()->get(ShippingAddressManagementInterface::class); - -$quoteAddressData = [ - AddressInterface::KEY_TELEPHONE => 3468676, - AddressInterface::KEY_POSTCODE => '11111', - AddressInterface::KEY_COUNTRY_ID => 'US', - AddressInterface::KEY_CITY => 'Los Angeles', - AddressInterface::KEY_COMPANY => '', - AddressInterface::KEY_STREET => '6161 West Centinela Avenue', - AddressInterface::KEY_LASTNAME => 'Doe', - AddressInterface::KEY_FIRSTNAME => 'John', - AddressInterface::KEY_REGION_ID => 12, -]; -$quoteAddress = $quoteAddressFactory->create(); -$dataObjectHelper->populateWithArray($quoteAddress, $quoteAddressData, AddressInterfaceFactory::class); - -$quote = $quoteFactory->create(); -$quoteResource->load($quote, 'test_quote', 'reserved_order_id'); -$shippingAddressManagement->assign($quote->getId(), $quoteAddress); diff --git a/dev/tests/integration/testsuite/Magento/AuthorizenetGraphQl/_files/simple_product_authorizenet.php b/dev/tests/integration/testsuite/Magento/AuthorizenetGraphQl/_files/simple_product_authorizenet.php deleted file mode 100644 index 50ec950853711..0000000000000 --- a/dev/tests/integration/testsuite/Magento/AuthorizenetGraphQl/_files/simple_product_authorizenet.php +++ /dev/null @@ -1,47 +0,0 @@ -<?php -/** - * Copyright © Magento, Inc. All rights reserved. - * See COPYING.txt for license details. - */ -declare(strict_types=1); - -use Magento\Catalog\Api\Data\ProductInterface; -use Magento\Catalog\Api\Data\ProductInterfaceFactory; -use Magento\Catalog\Api\ProductRepositoryInterface; -use Magento\Catalog\Model\Product\Attribute\Source\Status; -use Magento\Catalog\Model\Product\Type; -use Magento\Catalog\Model\Product\Visibility; -use Magento\Framework\Api\DataObjectHelper; -use Magento\TestFramework\Helper\Bootstrap; - -$objectManager = Bootstrap::getObjectManager(); -/** @var ProductInterfaceFactory $productFactory */ -$productFactory = $objectManager->get(ProductInterfaceFactory::class); -/** @var DataObjectHelper $dataObjectHelper */ -$dataObjectHelper = Bootstrap::getObjectManager()->get(DataObjectHelper::class); -/** @var ProductRepositoryInterface $productRepository */ -$productRepository = $objectManager->get(ProductRepositoryInterface::class); - -$product = $productFactory->create(); -$productData = [ - ProductInterface::TYPE_ID => Type::TYPE_SIMPLE, - ProductInterface::ATTRIBUTE_SET_ID => 4, - ProductInterface::SKU => 'simple_product', - ProductInterface::NAME => 'Simple Product', - ProductInterface::PRICE => 20, - ProductInterface::VISIBILITY => Visibility::VISIBILITY_BOTH, - ProductInterface::STATUS => Status::STATUS_ENABLED, -]; -$dataObjectHelper->populateWithArray($product, $productData, ProductInterface::class); -/** Out of interface */ -$product - ->setWebsiteIds([1]) - ->setStockData( - [ - 'qty' => 85.5, - 'is_in_stock' => true, - 'manage_stock' => true, - 'is_qty_decimal' => true - ] - ); -$productRepository->save($product); diff --git a/dev/tests/integration/testsuite/Magento/AuthorizenetGraphQl/_files/simple_product_authorizenet_rollback.php b/dev/tests/integration/testsuite/Magento/AuthorizenetGraphQl/_files/simple_product_authorizenet_rollback.php deleted file mode 100644 index 6b37585f8224f..0000000000000 --- a/dev/tests/integration/testsuite/Magento/AuthorizenetGraphQl/_files/simple_product_authorizenet_rollback.php +++ /dev/null @@ -1,8 +0,0 @@ -<?php -/** - * Copyright © Magento, Inc. All rights reserved. - * See COPYING.txt for license details. - */ - -// phpcs:ignore Magento2.Security.IncludeFile -require __DIR__ . '/../../GraphQl/Catalog/_files/simple_product_rollback.php'; diff --git a/dev/tests/integration/testsuite/Magento/Backend/App/Request/BackendValidatorTest.php b/dev/tests/integration/testsuite/Magento/Backend/App/Request/BackendValidatorTest.php index 21ffddf851ac4..68a9bc4d21656 100644 --- a/dev/tests/integration/testsuite/Magento/Backend/App/Request/BackendValidatorTest.php +++ b/dev/tests/integration/testsuite/Magento/Backend/App/Request/BackendValidatorTest.php @@ -27,7 +27,7 @@ use Magento\TestFramework\Bootstrap as TestBootstrap; use Magento\Framework\App\Request\Http as HttpRequest; use Magento\Framework\App\Response\Http as HttpResponse; -use Zend\Stdlib\Parameters; +use Laminas\Stdlib\Parameters; use Magento\Backend\Model\UrlInterface as BackendUrl; use Magento\Framework\App\Response\HttpFactory as HttpResponseFactory; diff --git a/dev/tests/integration/testsuite/Magento/Backend/Block/Dashboard/GraphTest.php b/dev/tests/integration/testsuite/Magento/Backend/Block/Dashboard/GraphTest.php deleted file mode 100644 index 497deb2c99110..0000000000000 --- a/dev/tests/integration/testsuite/Magento/Backend/Block/Dashboard/GraphTest.php +++ /dev/null @@ -1,34 +0,0 @@ -<?php -/** - * Copyright © Magento, Inc. All rights reserved. - * See COPYING.txt for license details. - */ -declare(strict_types=1); - -namespace Magento\Backend\Block\Dashboard; - -/** - * @magentoAppArea adminhtml - */ -class GraphTest extends \PHPUnit\Framework\TestCase -{ - /** - * @var \Magento\Backend\Block\Dashboard\Graph - */ - protected $_block; - - protected function setUp() - { - parent::setUp(); - $objectManager = \Magento\TestFramework\Helper\Bootstrap::getObjectManager(); - /** @var \Magento\Framework\View\LayoutInterface $layout */ - $layout = $objectManager->get(\Magento\Framework\View\LayoutInterface::class); - $this->_block = $layout->createBlock(\Magento\Backend\Block\Dashboard\Graph::class); - $this->_block->setDataHelper($objectManager->get(\Magento\Backend\Helper\Dashboard\Order::class)); - } - - public function testGetChartUrl() - { - $this->assertStringStartsWith('https://image-charts.com/chart', $this->_block->getChartUrl()); - } -} diff --git a/dev/tests/integration/testsuite/Magento/Backend/Block/_files/design/adminhtml/Magento/test_default/registration.php b/dev/tests/integration/testsuite/Magento/Backend/Block/_files/design/adminhtml/Magento/test_default/registration.php index 89cf740bbd4f4..31ad793efca38 100644 --- a/dev/tests/integration/testsuite/Magento/Backend/Block/_files/design/adminhtml/Magento/test_default/registration.php +++ b/dev/tests/integration/testsuite/Magento/Backend/Block/_files/design/adminhtml/Magento/test_default/registration.php @@ -4,6 +4,6 @@ * See COPYING.txt for license details. */ -use \Magento\Framework\Component\ComponentRegistrar; +use Magento\Framework\Component\ComponentRegistrar; ComponentRegistrar::register(ComponentRegistrar::THEME, 'adminhtml/BackendTest/test_default', __DIR__); diff --git a/dev/tests/integration/testsuite/Magento/Backend/Controller/Adminhtml/Dashboard/AjaxBlockTest.php b/dev/tests/integration/testsuite/Magento/Backend/Controller/Adminhtml/Dashboard/AjaxBlockTest.php index 3deb225cead18..a21dff3dd854f 100644 --- a/dev/tests/integration/testsuite/Magento/Backend/Controller/Adminhtml/Dashboard/AjaxBlockTest.php +++ b/dev/tests/integration/testsuite/Magento/Backend/Controller/Adminhtml/Dashboard/AjaxBlockTest.php @@ -1,10 +1,8 @@ <?php /** - * * Copyright © Magento, Inc. All rights reserved. * See COPYING.txt for license details. */ - declare(strict_types=1); namespace Magento\Backend\Controller\Adminhtml\Dashboard; @@ -20,6 +18,9 @@ class AjaxBlockTest extends AbstractBackendController /** * Test execute to check render block * + * @param string $block + * @param string $expectedResult + * * @dataProvider ajaxBlockDataProvider */ public function testExecute($block, $expectedResult) @@ -41,17 +42,9 @@ public function testExecute($block, $expectedResult) * * @return array */ - public function ajaxBlockDataProvider() + public function ajaxBlockDataProvider(): array { return [ - [ - 'tab_orders', - 'order_orders_period' - ], - [ - 'tab_amounts', - 'order_amounts_period' - ], [ 'totals', 'dashboard_diagram_totals' diff --git a/dev/tests/integration/testsuite/Magento/Backend/Controller/Adminhtml/DashboardTest.php b/dev/tests/integration/testsuite/Magento/Backend/Controller/Adminhtml/DashboardTest.php deleted file mode 100644 index 0eb98379b4571..0000000000000 --- a/dev/tests/integration/testsuite/Magento/Backend/Controller/Adminhtml/DashboardTest.php +++ /dev/null @@ -1,70 +0,0 @@ -<?php -/** - * Copyright © Magento, Inc. All rights reserved. - * See COPYING.txt for license details. - */ -declare(strict_types=1); - -namespace Magento\Backend\Controller\Adminhtml; - -/** - * @magentoAppArea adminhtml - */ -class DashboardTest extends \Magento\TestFramework\TestCase\AbstractBackendController -{ - public function testAjaxBlockAction() - { - $this->getRequest()->setParam('block', 'tab_orders'); - $this->dispatch('backend/admin/dashboard/ajaxBlock'); - - $actual = $this->getResponse()->getBody(); - $this->assertContains('dashboard-diagram', $actual); - } - - /** - * Tests tunnelAction - * - * @throws \Exception - * @return void - */ - public function testTunnelAction() - { - // phpcs:disable Magento2.Functions.DiscouragedFunction - $testUrl = \Magento\Backend\Block\Dashboard\Graph::API_URL . '?cht=p3&chd=t:60,40&chs=250x100&chl=Hello|World'; - $handle = curl_init(); - curl_setopt($handle, CURLOPT_URL, $testUrl); - curl_setopt($handle, CURLOPT_RETURNTRANSFER, true); - try { - if (false === curl_exec($handle)) { - $this->markTestSkipped('Third-party service is unavailable: ' . $testUrl); - } - curl_close($handle); - } catch (\Exception $e) { - curl_close($handle); - throw $e; - } - // phpcs:enable - - $gaData = [ - 'cht' => 'lc', - 'chf' => 'bg,s,f4f4f4|c,lg,90,ffffff,0.1,ededed,0', - 'chm' => 'B,f4d4b2,0,0,0', - 'chco' => 'db4814', - 'chd' => 'e:AAAAAAAAf.AAAA', - 'chxt' => 'x,y', - 'chxl' => '0:|10/13/12|10/14/12|10/15/12|10/16/12|10/17/12|10/18/12|10/19/12|1:|0|1|2', - 'chs' => '587x300', - 'chg' => '16.666666666667,50,1,0', - ]; - $gaFixture = urlencode(base64_encode(json_encode($gaData))); - - /** @var $helper \Magento\Backend\Helper\Dashboard\Data */ - $helper = \Magento\TestFramework\Helper\Bootstrap::getObjectManager()->get( - \Magento\Backend\Helper\Dashboard\Data::class - ); - $hash = $helper->getChartDataHash($gaFixture); - $this->getRequest()->setParam('ga', $gaFixture)->setParam('h', $hash); - $this->dispatch('backend/admin/dashboard/tunnel'); - $this->assertStringStartsWith("\x89\x50\x4E\x47", $this->getResponse()->getBody()); // PNG header - } -} diff --git a/dev/tests/integration/testsuite/Magento/Backend/Model/Dashboard/ChartTest.php b/dev/tests/integration/testsuite/Magento/Backend/Model/Dashboard/ChartTest.php new file mode 100644 index 0000000000000..25ac15342e13f --- /dev/null +++ b/dev/tests/integration/testsuite/Magento/Backend/Model/Dashboard/ChartTest.php @@ -0,0 +1,84 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +declare(strict_types=1); + +namespace Magento\Backend\Model\Dashboard; + +use Magento\TestFramework\Helper\Bootstrap; +use PHPUnit\Framework\TestCase; + +/** + * Verify chart data by different period. + * + * @magentoAppArea adminhtml + */ +class ChartTest extends TestCase +{ + /** + * @var Chart + */ + private $model; + + /** + * @inheritdoc + */ + protected function setUp() + { + $this->model = Bootstrap::getObjectManager()->create(Chart::class); + } + + /** + * Verify getByPeriod with all types of period + * + * @magentoDataFixture Magento/Sales/_files/order_list_with_invoice.php + * @dataProvider getChartDataProvider + * @return void + */ + public function testGetByPeriodWithParam(int $expectedDataQty, string $period, string $chartParam): void + { + $ordersData = $this->model->getByPeriod($period, $chartParam); + $ordersCount = array_sum(array_map(function ($item) { + return $item['y']; + }, $ordersData)); + $this->assertGreaterThanOrEqual($expectedDataQty, $ordersCount); + } + + /** + * Expected chart data + * + * @return array + */ + public function getChartDataProvider(): array + { + return [ + [ + 2, + '24h', + 'quantity' + ], + [ + 3, + '7d', + 'quantity' + ], + [ + 4, + '1m', + 'quantity' + ], + [ + 5, + '1y', + 'quantity' + ], + [ + 6, + '2y', + 'quantity' + ] + ]; + } +} diff --git a/dev/tests/integration/testsuite/Magento/Braintree/Controller/Cards/DeleteActionTest.php b/dev/tests/integration/testsuite/Magento/Braintree/Controller/Cards/DeleteActionTest.php index eae831743f9cd..325f02bd621c1 100644 --- a/dev/tests/integration/testsuite/Magento/Braintree/Controller/Cards/DeleteActionTest.php +++ b/dev/tests/integration/testsuite/Magento/Braintree/Controller/Cards/DeleteActionTest.php @@ -3,16 +3,17 @@ * Copyright © Magento, Inc. All rights reserved. * See COPYING.txt for license details. */ + namespace Magento\Braintree\Controller\Cards; use Magento\Customer\Model\Session; use Magento\Framework\Data\Form\FormKey; use Magento\TestFramework\TestCase\AbstractController; use Magento\Vault\Model\CustomerTokenManagement; -use Zend\Http\Request; +use Laminas\Http\Request; /** - * Class DeleteActionTest + * Test for \Magento\Vault\Controller\Cards\DeleteAction */ class DeleteActionTest extends AbstractController { @@ -26,7 +27,7 @@ public function testExecute() /** @var Session $session */ $session = $this->_objectManager->get(Session::class); $session->setCustomerId($customerId); - + /** @var CustomerTokenManagement $tokenManagement */ $tokenManagement = $this->_objectManager->get(CustomerTokenManagement::class); $tokens = $tokenManagement->getCustomerSessionTokens(); @@ -44,7 +45,7 @@ public function testExecute() ]) ->setMethod(Request::METHOD_POST); $this->dispatch('vault/cards/deleteaction'); - + static::assertTrue($this->getResponse()->isRedirect()); static::assertRedirect(static::stringContains('vault/cards/listaction')); static::assertSessionMessages(static::equalTo(['Stored Payment Method was successfully removed'])); diff --git a/dev/tests/integration/testsuite/Magento/Braintree/PaymentInformationManagementTest.php b/dev/tests/integration/testsuite/Magento/Braintree/PaymentInformationManagementTest.php new file mode 100644 index 0000000000000..37169e3161407 --- /dev/null +++ b/dev/tests/integration/testsuite/Magento/Braintree/PaymentInformationManagementTest.php @@ -0,0 +1,314 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +declare(strict_types=1); + +namespace Magento\Braintree; + +use Braintree\Result\Error; +use Braintree\Result\Successful; +use Braintree\Transaction; +use Braintree\Transaction\CreditCardDetails; +use Magento\Braintree\Gateway\Http\Client\TransactionSale; +use Magento\Braintree\Model\Ui\ConfigProvider; +use Magento\Checkout\Api\PaymentInformationManagementInterface; +use Magento\Framework\Api\SearchCriteriaBuilder; +use Magento\Framework\App\State; +use Magento\Framework\App\Area; +use Magento\Quote\Api\CartRepositoryInterface; +use Magento\Quote\Api\Data\CartInterface; +use Magento\Quote\Api\Data\PaymentInterface; +use Magento\Sales\Api\Data\TransactionInterface; +use Magento\Sales\Api\TransactionRepositoryInterface; +use Magento\TestFramework\Helper\Bootstrap; +use Magento\TestFramework\ObjectManager; +use PHPUnit\Framework\TestCase; +use PHPUnit_Framework_MockObject_MockObject as MockObject; + +/** + * @SuppressWarnings(PHPMD.CouplingBetweenObjects) + */ +class PaymentInformationManagementTest extends TestCase +{ + /** + * @var ObjectManager + */ + private $objectManager; + + /** + * @var TransactionSale|MockObject + */ + private $client; + + /** + * @var PaymentInformationManagementInterface + */ + private $management; + + /** + * @inheritdoc + */ + protected function setUp() + { + $this->objectManager = Bootstrap::getObjectManager(); + + $this->client = $this->getMockBuilder(TransactionSale::class) + ->disableOriginalConstructor() + ->getMock(); + $this->objectManager->addSharedInstance($this->client, TransactionSale::class); + $this->management = $this->objectManager->get(PaymentInformationManagementInterface::class); + } + + /** + * @inheritdoc + */ + protected function tearDown() + { + $this->objectManager->removeSharedInstance(TransactionSale::class); + parent::tearDown(); + } + + /** + * Checks a case when payment method triggers an error during place order flow and + * error messages from payment gateway should be mapped. + * Error messages might be specific for different areas. + * + * @magentoAppIsolation enabled + * @magentoDataFixture Magento/Checkout/_files/quote_with_shipping_method.php + * @magentoConfigFixture current_store payment/braintree/active 1 + * @dataProvider getErrorPerAreaDataProvider + * @expectedException \Magento\Framework\Exception\CouldNotSaveException + * @param string $area + * @param array $testErrorCodes + * @param string $expectedOutput + * @throws \Magento\Framework\Exception\LocalizedException + */ + public function testSavePaymentInformationAndPlaceOrderWithErrors( + string $area, + array $testErrorCodes, + string $expectedOutput + ) { + /** @var State $state */ + $state = $this->objectManager->get(State::class); + $state->setAreaCode($area); + + $quote = $this->getQuote('test_order_1'); + $payment = $this->getPayment(); + + $errors = ['errors' => []]; + + foreach ($testErrorCodes as $testErrorCode) { + array_push($errors['errors'], ['code' => $testErrorCode]); + } + + $response = new Error(['errors' => $errors, 'transaction' => ['status' => 'declined']]); + + $this->client->method('placeRequest') + ->willReturn(['object' => $response]); + + $this->expectExceptionMessage($expectedOutput); + + $this->management->savePaymentInformationAndPlaceOrder( + $quote->getId(), + $payment + ); + } + + /** + * Gets list of areas with specific error messages. + * + * @return array + */ + public function getErrorPerAreaDataProvider() + { + $testErrorGlobal = ['code' => 81802, 'message' => 'Company is too long.']; + $testErrorAdmin = ['code' => 91511, 'message' => 'Customer does not have any credit cards.']; + $testErrorFake = ['code' => 'fake_code', 'message' => 'Error message should not be mapped.']; + + return [ + [ + Area::AREA_FRONTEND, + [$testErrorAdmin['code'], $testErrorFake['code']], + 'Transaction has been declined. Please try again later.' + ], [ + Area::AREA_FRONTEND, + [$testErrorGlobal['code'], $testErrorAdmin['code'], $testErrorFake['code']], + $testErrorGlobal['message'] + ], [ + Area::AREA_ADMINHTML, + [$testErrorGlobal['code'], $testErrorAdmin['code'], $testErrorFake['code']], + $testErrorGlobal['message'] . PHP_EOL . $testErrorAdmin['message'] + ], + ]; + } + + /** + * Checks a case when order should be placed with "Sale" payment action. + * + * @magentoAppIsolation enabled + * @magentoDataFixture Magento/Checkout/_files/quote_with_shipping_method.php + * @magentoConfigFixture current_store payment/braintree/active 1 + * @magentoConfigFixture current_store payment/braintree/payment_action authorize_capture + */ + public function testPlaceOrderWithSaleAction() + { + $response = $this->getSuccessfulResponse(Transaction::SUBMITTED_FOR_SETTLEMENT); + $this->client->method('placeRequest') + ->willReturn($response); + + $quote = $this->getQuote('test_order_1'); + $payment = $this->getPayment(); + + $orderId = $this->management->savePaymentInformationAndPlaceOrder($quote->getId(), $payment); + self::assertNotEmpty($orderId); + + $transactions = $this->getPaymentTransactionList((int) $orderId); + self::assertEquals(1, sizeof($transactions), 'Only one transaction should be present.'); + + /** @var TransactionInterface $transaction */ + $transaction = array_pop($transactions); + self::assertEquals( + 'capture', + $transaction->getTxnType(), + 'Order should contain only the "capture" transaction.' + ); + self::assertFalse((bool) $transaction->getIsClosed(), 'Transaction should not be closed.'); + } + + /** + * Checks a case when order should be placed with "Authorize" payment action. + * + * @magentoAppIsolation enabled + * @magentoDataFixture Magento/Checkout/_files/quote_with_shipping_method.php + * @magentoConfigFixture current_store payment/braintree/active 1 + * @magentoConfigFixture current_store payment/braintree/payment_action authorize + */ + public function testPlaceOrderWithAuthorizeAction() + { + $response = $this->getSuccessfulResponse(Transaction::AUTHORIZED); + $this->client->method('placeRequest') + ->willReturn($response); + + $quote = $this->getQuote('test_order_1'); + $payment = $this->getPayment(); + + $orderId = $this->management->savePaymentInformationAndPlaceOrder($quote->getId(), $payment); + self::assertNotEmpty($orderId); + + $transactions = $this->getPaymentTransactionList((int) $orderId); + self::assertEquals(1, sizeof($transactions), 'Only one transaction should be present.'); + + /** @var TransactionInterface $transaction */ + $transaction = array_pop($transactions); + self::assertEquals( + 'authorization', + $transaction->getTxnType(), + 'Order should contain only the "authorization" transaction.' + ); + self::assertFalse((bool) $transaction->getIsClosed(), 'Transaction should not be closed.'); + } + + /** + * Retrieves quote by provided order ID. + * + * @param string $reservedOrderId + * @return CartInterface + */ + private function getQuote(string $reservedOrderId): CartInterface + { + /** @var SearchCriteriaBuilder $searchCriteriaBuilder */ + $searchCriteriaBuilder = $this->objectManager->get(SearchCriteriaBuilder::class); + $searchCriteria = $searchCriteriaBuilder->addFilter('reserved_order_id', $reservedOrderId) + ->create(); + + /** @var CartRepositoryInterface $quoteRepository */ + $quoteRepository = $this->objectManager->get(CartRepositoryInterface::class); + $items = $quoteRepository->getList($searchCriteria) + ->getItems(); + + return array_pop($items); + } + + /** + * Creates Braintree payment method. + * + * @return PaymentInterface + */ + private function getPayment(): PaymentInterface + { + /** @var PaymentInterface $payment */ + $payment = $this->objectManager->create(PaymentInterface::class); + $payment->setMethod(ConfigProvider::CODE); + + return $payment; + } + + /** + * Get list of order transactions. + * + * @param int $orderId + * @return TransactionInterface[] + */ + private function getPaymentTransactionList(int $orderId): array + { + /** @var SearchCriteriaBuilder $searchCriteriaBuilder */ + $searchCriteriaBuilder = $this->objectManager->get(SearchCriteriaBuilder::class); + $searchCriteria = $searchCriteriaBuilder->addFilter('order_id', $orderId) + ->create(); + + /** @var TransactionRepositoryInterface $transactionRepository */ + $transactionRepository = $this->objectManager->get(TransactionRepositoryInterface::class); + return $transactionRepository->getList($searchCriteria) + ->getItems(); + } + + /** + * Returns successful Braintree response. + * + * @param string $transactionStatus + * @return array + */ + private function getSuccessfulResponse(string $transactionStatus): array + { + $successResponse = new Successful(); + $successResponse->success = true; + $successResponse->transaction = $this->getBraintreeTransaction($transactionStatus); + + $response = [ + 'object' => $successResponse, + ]; + + return $response; + } + + /** + * Returns Braintree transaction. + * + * @param string $transactionStatus + * @return Transaction + */ + private function getBraintreeTransaction(string $transactionStatus) + { + $cardData = [ + 'token' => '73nrjn', + 'bin' => '411111', + 'cardType' => 'Visa', + 'expirationMonth' => '12', + 'expirationYear' => '2025', + 'last4' => '1111' + ]; + + $transactionData = [ + 'id' => 'c0n6gvjb', + 'status' => $transactionStatus, + 'creditCard' => $cardData, + 'creditCardDetails' => new CreditCardDetails($cardData) + ]; + + $transaction = Transaction::factory($transactionData); + + return $transaction; + } +} diff --git a/dev/tests/integration/testsuite/Magento/Bundle/Block/Catalog/Product/View/Type/AbstractBundleOptionsViewTest.php b/dev/tests/integration/testsuite/Magento/Bundle/Block/Catalog/Product/View/Type/AbstractBundleOptionsViewTest.php new file mode 100644 index 0000000000000..287a3f07d1964 --- /dev/null +++ b/dev/tests/integration/testsuite/Magento/Bundle/Block/Catalog/Product/View/Type/AbstractBundleOptionsViewTest.php @@ -0,0 +1,194 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +declare(strict_types=1); + +namespace Magento\Bundle\Block\Catalog\Product\View\Type; + +use Magento\Catalog\Api\Data\ProductInterface; +use Magento\Catalog\Api\ProductRepositoryInterface; +use Magento\Catalog\Model\ResourceModel\Product as ProductResource; +use Magento\Framework\ObjectManagerInterface; +use Magento\Framework\Registry; +use Magento\Framework\Serialize\SerializerInterface; +use Magento\Framework\View\Result\PageFactory; +use Magento\TestFramework\Helper\Bootstrap; +use Magento\TestFramework\Helper\Xpath; +use PHPUnit\Framework\TestCase; + +/** + * Class consist of basic logic for bundle options view + */ +abstract class AbstractBundleOptionsViewTest extends TestCase +{ + /** @var ObjectManagerInterface */ + private $objectManager; + + /** @var ProductRepositoryInterface */ + private $productRepository; + + /** @var SerializerInterface */ + private $serializer; + + /** @var Registry */ + private $registry; + + /** @var PageFactory */ + private $pageFactory; + + /** @var ProductResource */ + private $productResource; + + /** @var string */ + private $selectLabelXpath = "//fieldset[contains(@class, 'fieldset-bundle-options')]" + . "//label/span[normalize-space(text()) = '%s']"; + + /** @var string */ + private $backToProductDetailButtonXpath = "//button[contains(@class, 'back customization')]"; + + /** @var string */ + private $titleXpath = "//fieldset[contains(@class, 'bundle-options')]//span[contains(text(), 'Customize %s')]"; + + /** @var string */ + private $singleOptionXpath = "//input[contains(@class, 'bundle-option') and contains(@type, 'hidden')]"; + + /** + * @inheritdoc + */ + protected function setUp() + { + parent::setUp(); + + $this->objectManager = Bootstrap::getObjectManager(); + $this->productRepository = $this->objectManager->get(ProductRepositoryInterface::class); + $this->productRepository->cleanCache(); + $this->serializer = $this->objectManager->get(SerializerInterface::class); + $this->registry = $this->objectManager->get(Registry::class); + $this->pageFactory = $this->objectManager->get(PageFactory::class); + $this->productResource = $this->objectManager->get(ProductResource::class); + } + + /** + * @inheritdoc + */ + protected function tearDown() + { + $this->registry->unregister('product'); + $this->registry->unregister('current_product'); + + parent::tearDown(); + } + + /** + * Process bundle options view with few selections + * + * @param string $sku + * @param string $optionsSelectLabel + * @param array $expectedSelectionsNames + * @param bool $requiredOption + * @return void + */ + protected function processMultiSelectionsView( + string $sku, + string $optionsSelectLabel, + array $expectedSelectionsNames, + bool $requiredOption = false + ): void { + $product = $this->productRepository->get($sku); + $result = $this->renderProductOptionsBlock($product); + $this->assertEquals( + 1, + Xpath::getElementsCountForXpath($this->backToProductDetailButtonXpath, $result), + "'Back to product details' button doesn't exist on the page" + ); + $this->assertEquals( + 1, + Xpath::getElementsCountForXpath(sprintf($this->selectLabelXpath, $optionsSelectLabel), $result), + 'Options select label does not exist on the page' + ); + $this->assertEquals( + 1, + Xpath::getElementsCountForXpath(sprintf($this->titleXpath, $product->getName()), $result), + sprintf('Customize %s label does not exist on the page', $product->getName()) + ); + $selectPath = $requiredOption ? $this->getRequiredSelectXpath() : $this->getNotRequiredSelectXpath(); + foreach ($expectedSelectionsNames as $selection) { + $this->assertEquals( + 1, + Xpath::getElementsCountForXpath(sprintf($selectPath, $selection), $result), + sprintf('Option for product named %s does not exist on the page', $selection) + ); + } + } + + /** + * Process bundle options view with single selection + * + * @param string $sku + * @param string $optionsSelectLabel + * @return void + */ + protected function processSingleSelectionView(string $sku, string $optionsSelectLabel): void + { + $product = $this->productRepository->get($sku); + $result = $this->renderProductOptionsBlock($product); + $this->assertEquals(1, Xpath::getElementsCountForXpath($this->backToProductDetailButtonXpath, $result)); + $this->assertEquals( + 1, + Xpath::getElementsCountForXpath(sprintf($this->selectLabelXpath, $optionsSelectLabel), $result), + 'Options select label does not exist on the page' + ); + $this->assertEquals( + 1, + Xpath::getElementsCountForXpath($this->singleOptionXpath, $result), + 'Bundle product options select with single option does not display correctly' + ); + } + + /** + * Register product + * + * @param ProductInterface $product + * @return void + */ + private function registerProduct(ProductInterface $product): void + { + $this->registry->unregister('product'); + $this->registry->unregister('current_product'); + $this->registry->register('product', $product); + $this->registry->register('current_product', $product); + } + + /** + * Render bundle product options block + * + * @param ProductInterface $product + * @return string + */ + private function renderProductOptionsBlock(ProductInterface $product): string + { + $this->registerProduct($product); + $page = $this->pageFactory->create(); + $page->addHandle(['default', 'catalog_product_view', 'catalog_product_view_type_bundle']); + $page->getLayout()->generateXml(); + $block = $page->getLayout()->getBlock('product.info.bundle.options'); + + return $block->toHtml(); + } + + /** + * Get required select Xpath + * + * @return string + */ + abstract protected function getRequiredSelectXpath(): string; + + /** + * Get not required select Xpath + * + * @return string + */ + abstract protected function getNotRequiredSelectXpath(): string; +} diff --git a/dev/tests/integration/testsuite/Magento/Bundle/Block/Catalog/Product/View/Type/BundleProductPriceTest.php b/dev/tests/integration/testsuite/Magento/Bundle/Block/Catalog/Product/View/Type/BundleProductPriceTest.php new file mode 100644 index 0000000000000..2a61a252e9313 --- /dev/null +++ b/dev/tests/integration/testsuite/Magento/Bundle/Block/Catalog/Product/View/Type/BundleProductPriceTest.php @@ -0,0 +1,172 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +declare(strict_types=1); + +namespace Magento\Bundle\Block\Catalog\Product\View\Type; + +use Magento\Catalog\Api\ProductRepositoryInterface; +use Magento\Framework\ObjectManagerInterface; +use Magento\Framework\Registry; +use Magento\Framework\Serialize\SerializerInterface; +use Magento\Framework\View\LayoutInterface; +use Magento\TestFramework\Helper\Bootstrap; +use PHPUnit\Framework\TestCase; + +/** + * Check bundle product prices. + * + * @magentoDbIsolation enabled + * @magentoAppIsolation disabled + * @magentoAppArea frontend + */ +class BundleProductPriceTest extends TestCase +{ + /** @var ObjectManagerInterface */ + private $objectManager; + + /** @var Registry */ + private $registry; + + /** @var ProductRepositoryInterface */ + private $productRepository; + + /** @var SerializerInterface */ + private $json; + + /** @var Bundle */ + private $block; + + /** + * @inheritdoc + */ + protected function setUp() + { + parent::setUp(); + + $this->objectManager = Bootstrap::getObjectManager(); + $this->productRepository = $this->objectManager->get(ProductRepositoryInterface::class); + $this->productRepository->cleanCache(); + $this->block = $this->objectManager->get(LayoutInterface::class)->createBlock(Bundle::class); + $this->registry = $this->objectManager->get(Registry::class); + $this->json = $this->objectManager->get(SerializerInterface::class); + } + + /** + * @inheritdoc + */ + protected function tearDown() + { + $this->registry->unregister('product'); + + parent::tearDown(); + } + + /** + * @magentoDataFixture Magento/Bundle/_files/dynamic_bundle_product_multiselect_option.php + * + * @return void + */ + public function testDynamicBundleOptionPrices(): void + { + $expectedData = [ + 'options_prices' => [ + [ + 'oldPrice' => ['amount' => 10], + 'basePrice' => ['amount' => 10], + 'finalPrice' => ['amount' => 10], + ], + [ + 'oldPrice' => ['amount' => 20], + 'basePrice' => ['amount' => 20], + 'finalPrice' => ['amount' => 20], + ], + [ + 'oldPrice' => ['amount' => 30], + 'basePrice' => ['amount' => 30], + 'finalPrice' => ['amount' => 30], + ], + ], + 'bundle_prices' => [ + 'oldPrice' => ['amount' => 0], + 'basePrice' => ['amount' => 0], + 'finalPrice' => ['amount' => 0], + ] + ]; + $this->processBundlePriceView('bundle_product', $expectedData); + } + + /** + * @magentoDataFixture Magento/Bundle/_files/product_with_multiple_options_1.php + * + * @return void + */ + public function testFixedBundleOptionPrices(): void + { + $expectedData = [ + 'options_prices' => [ + [ + 'oldPrice' => ['amount' => 2.75], + 'basePrice' => ['amount' => 2.75], + 'finalPrice' => ['amount' => 2.75], + ], + [ + 'oldPrice' => ['amount' => 6.75], + 'basePrice' => ['amount' => 6.75], + 'finalPrice' => ['amount' => 6.75], + ], + ], + 'bundle_prices' => [ + 'oldPrice' => ['amount' => 12.75], + 'basePrice' => ['amount' => 10], + 'finalPrice' => ['amount' => 10], + ] + ]; + $this->processBundlePriceView('bundle-product', $expectedData); + } + + /** + * @param string $productSku + * @param array $expectedData + * @return void + */ + private function processBundlePriceView(string $productSku, array $expectedData): void + { + $this->registerProduct($productSku); + $jsonConfig = $this->json->unserialize($this->block->getJsonConfig()); + $this->assertEquals($expectedData['bundle_prices'], $jsonConfig['prices']); + $this->assertOptionsConfig($expectedData['options_prices'], $jsonConfig); + } + + /** + * Assert options prices. + * + * @param array $expectedData + * @param array $actualData + * @return void + */ + private function assertOptionsConfig(array $expectedData, array $actualData): void + { + $optionConfig = $actualData['options'] ?? null; + $this->assertNotNull($optionConfig); + $optionConfig = reset($optionConfig); + foreach (array_values($optionConfig['selections']) as $key => $selection) { + $this->assertEquals($expectedData[$key], $selection['prices']); + } + } + + /** + * Register the product. + * + * @param string $productSku + * @return void + */ + private function registerProduct(string $productSku): void + { + $product = $this->productRepository->get($productSku); + $this->registry->unregister('product'); + $this->registry->register('product', $product); + } +} diff --git a/dev/tests/integration/testsuite/Magento/Bundle/Block/Catalog/Product/View/Type/BundleTest.php b/dev/tests/integration/testsuite/Magento/Bundle/Block/Catalog/Product/View/Type/BundleTest.php index ce324ed774dc4..dab699064c4bb 100644 --- a/dev/tests/integration/testsuite/Magento/Bundle/Block/Catalog/Product/View/Type/BundleTest.php +++ b/dev/tests/integration/testsuite/Magento/Bundle/Block/Catalog/Product/View/Type/BundleTest.php @@ -7,53 +7,66 @@ namespace Magento\Bundle\Block\Catalog\Product\View\Type; +use Magento\Bundle\Model\Product\Price; +use Magento\Catalog\Api\Data\ProductInterface; +use Magento\Catalog\Api\ProductRepositoryInterface; +use Magento\Framework\ObjectManagerInterface; +use Magento\Framework\Registry; +use Magento\Framework\Serialize\SerializerInterface; +use Magento\Framework\View\LayoutInterface; +use Magento\TestFramework\Helper\Bootstrap; +use PHPUnit\Framework\TestCase; + /** - * Test for Magento\Bundle\Block\Catalog\Product\View\Type\Bundle + * Class checks bundle product view behaviour * * @magentoDataFixture Magento/Bundle/_files/product.php - * @magentoDbIsolation disabled + * @magentoDbIsolation enabled * @magentoAppArea frontend + * @see \Magento\Bundle\Block\Catalog\Product\View\Type\Bundle */ -class BundleTest extends \PHPUnit\Framework\TestCase +class BundleTest extends TestCase { - /** - * @var \Magento\Bundle\Block\Catalog\Product\View\Type\Bundle - */ + /** @var Bundle */ private $block; - /** - * @var \Magento\Catalog\Api\Data\ProductInterface - */ - private $product; - - /** - * @var \Magento\TestFramework\ObjectManager - */ + /** @var ObjectManagerInterface */ private $objectManager; - /** - * @var \Magento\Catalog\Api\ProductRepositoryInterface - */ + /** @var ProductRepositoryInterface */ private $productRepository; + /** @var LayoutInterface */ + private $layout; + + /** @var SerializerInterface */ + private $json; + + /** @var Registry */ + private $registry; + /** * @inheritdoc */ protected function setUp() { - $this->objectManager = \Magento\TestFramework\Helper\Bootstrap::getObjectManager(); - - $this->productRepository = $this->objectManager->create(\Magento\Catalog\Api\ProductRepositoryInterface::class); - $this->product = $this->productRepository->get('bundle-product', false, null, true); - $this->product->setPriceType(\Magento\Bundle\Model\Product\Price::PRICE_TYPE_DYNAMIC)->save(); - $this->objectManager->get(\Magento\Framework\Registry::class)->unregister('product'); - $this->objectManager->get(\Magento\Framework\Registry::class)->register('product', $this->product); - - $this->block = $this->objectManager->get( - \Magento\Framework\View\LayoutInterface::class - )->createBlock( - \Magento\Bundle\Block\Catalog\Product\View\Type\Bundle::class - ); + $this->objectManager = Bootstrap::getObjectManager(); + $this->productRepository = $this->objectManager->get(ProductRepositoryInterface::class); + $this->productRepository->cleanCache(); + $this->layout = $this->objectManager->get(LayoutInterface::class); + $this->block = $this->layout->createBlock(Bundle::class); + $this->json = $this->objectManager->get(SerializerInterface::class); + $this->registry = $this->objectManager->get(Registry::class); + } + + /** + * @inheritdoc + */ + protected function tearDown() + { + $this->registry->unregister('product'); + + parent::tearDown(); } /** @@ -61,12 +74,12 @@ protected function setUp() * * @return void */ - public function testGetJsonConfig() + public function testGetJsonConfig(): void { - $option = $this->productRepository->get('simple'); - $option->setSpecialPrice(5) - ->save(); - $config = json_decode($this->block->getJsonConfig(), true); + $product = $this->updateProduct('bundle-product', ['price_type' => Price::PRICE_TYPE_DYNAMIC]); + $this->registerProduct($product); + $this->updateProduct('simple', ['special_price' => 5]); + $config = $this->json->unserialize($this->block->getJsonConfig()); $options = current($config['options']); $selection = current($options['selections']); $this->assertEquals(10, $selection['prices']['oldPrice']['amount']); @@ -75,10 +88,124 @@ public function testGetJsonConfig() } /** - * Tear Down + * @dataProvider isSalableForStockStatusProvider + * + * @param bool $isSalable + * @param string $expectedValue + * @return void */ - protected function tearDown() + public function testStockStatusView(bool $isSalable, string $expectedValue): void { - $this->objectManager->get(\Magento\Framework\Registry::class)->unregister('product'); + $product = $this->productRepository->get('bundle-product'); + $product->setAllItemsSalable($isSalable); + $this->block->setTemplate('Magento_Bundle::catalog/product/view/type/bundle.phtml'); + $result = $this->renderBlockHtml($product); + $this->assertEquals($expectedValue, trim(strip_tags($result))); + } + + /** + * @return array + */ + public function isSalableForStockStatusProvider(): array + { + return [ + 'is_salable' => [ + 'is_salable' => true, + 'expected_value' => 'In stock', + ], + 'is_not_salable' => [ + 'is_salable' => false, + 'expected_value' => 'Out of stock', + ], + ]; + } + + /** + * @dataProvider isSalableForCustomizeButtonProvider + * + * @param bool $isSalable + * @param string $expectedValue + * @return void + */ + public function testCustomizeButton(bool $isSalable, string $expectedValue): void + { + $product = $this->productRepository->get('bundle-product'); + $product->setSalable($isSalable); + $this->block->setTemplate('Magento_Bundle::catalog/product/view/customize.phtml'); + $result = $this->renderBlockHtml($product); + $this->assertEquals($expectedValue, trim(strip_tags($result))); + } + + /** + * @return array + */ + public function isSalableForCustomizeButtonProvider(): array + { + return [ + 'is_salable' => [ + 'is_salable' => true, + 'expected_value' => 'Customize and Add to Cart', + ], + 'is_not_salable' => [ + 'is_salable' => false, + 'expected_value' => '', + ], + ]; + } + + /** + * @magentoDataFixture Magento/Bundle/_files/empty_bundle_product.php + * + * @param bool $isSalable + * @param string $expectedValue + * @return void + */ + public function testCustomizeButtonProductWithoutOptions(): void + { + $product = $this->productRepository->get('bundle-product'); + $product->setSalable(true); + $this->block->setTemplate('Magento_Bundle::catalog/product/view/customize.phtml'); + $result = $this->renderBlockHtml($product); + $this->assertEmpty(trim(strip_tags($result))); + } + + /** + * Update product + * + * @param ProductInterface|string $productSku + * @param array $data + * @return ProductInterface + */ + private function updateProduct(string $productSku, array $data): ProductInterface + { + $product = $this->productRepository->get($productSku); + $product->addData($data); + + return $this->productRepository->save($product); + } + + /** + * Register product + * + * @param ProductInterface $product + * @return void + */ + private function registerProduct(ProductInterface $product): void + { + $this->registry->unregister('product'); + $this->registry->register('product', $product); + } + + /** + * Render block output + * + * @param ProductInterface $product + * @return string + */ + private function renderBlockHtml(ProductInterface $product): string + { + $this->registerProduct($product); + + return $this->block->toHtml(); } } diff --git a/dev/tests/integration/testsuite/Magento/Bundle/Block/Catalog/Product/View/Type/CheckboxOptionViewTest.php b/dev/tests/integration/testsuite/Magento/Bundle/Block/Catalog/Product/View/Type/CheckboxOptionViewTest.php new file mode 100644 index 0000000000000..cbe150b9fb3f5 --- /dev/null +++ b/dev/tests/integration/testsuite/Magento/Bundle/Block/Catalog/Product/View/Type/CheckboxOptionViewTest.php @@ -0,0 +1,77 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +declare(strict_types=1); + +namespace Magento\Bundle\Block\Catalog\Product\View\Type; + +/** + * Class checks checkbox bundle options appearance + * + * @magentoAppArea frontend + * @magentoAppIsolation enabled + * @magentoDbIsolation enabled + */ +class CheckboxOptionViewTest extends AbstractBundleOptionsViewTest +{ + /** + * @magentoDataFixture Magento/Bundle/_files/bundle_product_checkbox_options.php + * + * @return void + */ + public function testNotRequiredSelectMultiSelectionsView(): void + { + $expectedSelectionsNames = ['Simple Product', 'Simple Product2']; + $this->processMultiSelectionsView( + 'bundle-product-checkbox-options', + 'Checkbox Options', + $expectedSelectionsNames + ); + } + + /** + * @magentoDataFixture Magento/Bundle/_files/bundle_product_checkbox_required_options.php + * + * @return void + */ + public function testRequiredSelectMultiSelectionsView(): void + { + $expectedSelectionsNames = ['Simple Product', 'Simple Product2']; + $this->processMultiSelectionsView( + 'bundle-product-checkbox-required-options', + 'Checkbox Options', + $expectedSelectionsNames, + true + ); + } + + /** + * @magentoDataFixture Magento/Bundle/_files/bundle_product_checkbox_required_option.php + * + * @return void + */ + public function testShowSingle(): void + { + $this->processSingleSelectionView('bundle-product-checkbox-required-option', 'Checkbox Options'); + } + + /** + * @inheritdoc + */ + protected function getRequiredSelectXpath(): string + { + return "//input[@type='checkbox' and contains(@data-validate, 'validate-one-required-by-name')" + . "and contains(@class, 'bundle-option')]/../label//span[normalize-space(text()) = '1 x %s']"; + } + + /** + * @inheritdoc + */ + protected function getNotRequiredSelectXpath(): string + { + return "//input[@type='checkbox' and not(contains(@data-validate, 'validate-one-required-by-name'))" + . "and contains(@class, 'bundle-option')]/../label//span[normalize-space(text()) = '1 x %s']"; + } +} diff --git a/dev/tests/integration/testsuite/Magento/Bundle/Block/Catalog/Product/View/Type/DropDownOptionViewTest.php b/dev/tests/integration/testsuite/Magento/Bundle/Block/Catalog/Product/View/Type/DropDownOptionViewTest.php new file mode 100644 index 0000000000000..128fbe56185f3 --- /dev/null +++ b/dev/tests/integration/testsuite/Magento/Bundle/Block/Catalog/Product/View/Type/DropDownOptionViewTest.php @@ -0,0 +1,77 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +declare(strict_types=1); + +namespace Magento\Bundle\Block\Catalog\Product\View\Type; + +/** + * Class checks dropdown bundle options appearance + * + * @magentoAppArea frontend + * @magentoAppIsolation enabled + * @magentoDbIsolation enabled + */ +class DropDownOptionViewTest extends AbstractBundleOptionsViewTest +{ + /** + * @magentoDataFixture Magento/Bundle/_files/bundle_product_dropdown_options.php + * + * @return void + */ + public function testNotRequiredSelectMultiSelectionsView(): void + { + $expectedSelectionsNames = ['Simple Product', 'Simple Product2']; + $this->processMultiSelectionsView( + 'bundle-product-dropdown-options', + 'Dropdown Options', + $expectedSelectionsNames + ); + } + + /** + * @magentoDataFixture Magento/Bundle/_files/bundle_product_dropdown_required_options.php + * + * @return void + */ + public function testRequiredSelectMultiSelectionsView(): void + { + $expectedSelectionsNames = ['Simple Product', 'Simple Product2']; + $this->processMultiSelectionsView( + 'bundle-product-dropdown-required-options', + 'Dropdown Options', + $expectedSelectionsNames, + true + ); + } + + /** + * @magentoDataFixture Magento/Bundle/_files/product.php + * + * @return void + */ + public function testShowSingle(): void + { + $this->processSingleSelectionView('bundle-product', 'Bundle Product Items'); + } + + /** + * @inheritdoc + */ + protected function getRequiredSelectXpath(): string + { + return "//select[contains(@id, 'bundle-option') and contains(@data-validate, 'required:true')]" + . "/option/span[normalize-space(text()) = '%s']"; + } + + /** + * @inheritdoc + */ + protected function getNotRequiredSelectXpath(): string + { + return "//select[contains(@id, 'bundle-option') and not(contains(@data-validate, 'required:true'))]" + . "/option/span[normalize-space(text()) = '%s']"; + } +} diff --git a/dev/tests/integration/testsuite/Magento/Bundle/Block/Catalog/Product/View/Type/MultiselectOptionViewTest.php b/dev/tests/integration/testsuite/Magento/Bundle/Block/Catalog/Product/View/Type/MultiselectOptionViewTest.php new file mode 100644 index 0000000000000..234981f36fa94 --- /dev/null +++ b/dev/tests/integration/testsuite/Magento/Bundle/Block/Catalog/Product/View/Type/MultiselectOptionViewTest.php @@ -0,0 +1,77 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +declare(strict_types=1); + +namespace Magento\Bundle\Block\Catalog\Product\View\Type; + +/** + * Class checks multiselect bundle options appearance + * + * @magentoAppArea frontend + * @magentoAppIsolation enabled + * @magentoDbIsolation enabled + */ +class MultiselectOptionViewTest extends AbstractBundleOptionsViewTest +{ + /** + * @magentoDataFixture Magento/Bundle/_files/bundle_product_multiselect_options.php + * + * @return void + */ + public function testNotRequiredSelectMultiSelectionsView(): void + { + $expectedSelectionsNames = ['Simple Product', 'Simple Product2']; + $this->processMultiSelectionsView( + 'bundle-product-multiselect-options', + 'Multiselect Options', + $expectedSelectionsNames + ); + } + + /** + * @magentoDataFixture Magento/Bundle/_files/bundle_product_multiselect_required_options.php + * + * @return void + */ + public function testRequiredSelectMultiSelectionsView(): void + { + $expectedSelectionsNames = ['Simple Product', 'Simple Product2']; + $this->processMultiSelectionsView( + 'bundle-product-multiselect-required-options', + 'Multiselect Options', + $expectedSelectionsNames, + true + ); + } + + /** + * @magentoDataFixture Magento/Bundle/_files/bundle_product_multiselect_required_option.php + * + * @return void + */ + public function testShowSingle(): void + { + $this->processSingleSelectionView('bundle-product-multiselect-required-option', 'Multiselect Options'); + } + + /** + * @inheridoc + */ + protected function getRequiredSelectXpath(): string + { + return "//select[contains(@id, 'bundle-option') and @multiple='multiple' " + . "and contains(@data-validate, 'required:true')]/option/span[normalize-space(text()) = '1 x %s']"; + } + + /** + * @inheridoc + */ + protected function getNotRequiredSelectXpath(): string + { + return "//select[contains(@id, 'bundle-option') and @multiple='multiple'" + . "and not(contains(@data-validate, 'required:true'))]/option/span[normalize-space(text()) = '1 x %s']"; + } +} diff --git a/dev/tests/integration/testsuite/Magento/Bundle/Block/Catalog/Product/View/Type/RadioOptionViewTest.php b/dev/tests/integration/testsuite/Magento/Bundle/Block/Catalog/Product/View/Type/RadioOptionViewTest.php new file mode 100644 index 0000000000000..b58f5f7d4189e --- /dev/null +++ b/dev/tests/integration/testsuite/Magento/Bundle/Block/Catalog/Product/View/Type/RadioOptionViewTest.php @@ -0,0 +1,73 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +declare(strict_types=1); + +namespace Magento\Bundle\Block\Catalog\Product\View\Type; + +/** + * Class checks radio buttons bundle options appearance + * + * @magentoAppArea frontend + * @magentoAppIsolation enabled + * @magentoDbIsolation enabled + */ +class RadioOptionViewTest extends AbstractBundleOptionsViewTest +{ + /** + * @magentoDataFixture Magento/Bundle/_files/bundle_product_radio_options.php + * + * @return void + */ + public function testNotRequiredSelectMultiSelectionsView(): void + { + $expectedSelectionsNames = ['Simple Product', 'Simple Product2']; + $this->processMultiSelectionsView('bundle-product-radio-options', 'Radio Options', $expectedSelectionsNames); + } + + /** + * @magentoDataFixture Magento/Bundle/_files/bundle_product_radio_required_options.php + * + * @return void + */ + public function testRequiredSelectMultiSelectionsView(): void + { + $expectedSelectionsNames = ['Simple Product', 'Simple Product2']; + $this->processMultiSelectionsView( + 'bundle-product-radio-required-options', + 'Radio Options', + $expectedSelectionsNames, + true + ); + } + + /** + * @magentoDataFixture Magento/Bundle/_files/bundle_product_radio_required_option.php + * + * @return void + */ + public function testShowSingle(): void + { + $this->processSingleSelectionView('bundle-product-radio-required-option', 'Radio Options'); + } + + /** + * @inheritdoc + */ + protected function getRequiredSelectXpath(): string + { + return "//input[@type='radio' and contains(@data-validate, 'validate-one-required-by-name')" + . "and contains(@class, 'bundle option')]/../label//span[normalize-space(text()) = '%s']"; + } + + /** + * @inheritdoc + */ + protected function getNotRequiredSelectXpath(): string + { + return "//input[@type='radio' and not(contains(@data-validate, 'validate-one-required-by-name'))" + . "and contains(@class, 'bundle option')]/../label//span[normalize-space(text()) = '%s']"; + } +} diff --git a/dev/tests/integration/testsuite/Magento/Bundle/Controller/Adminhtml/Product/AbstractBundleProductSaveTest.php b/dev/tests/integration/testsuite/Magento/Bundle/Controller/Adminhtml/Product/AbstractBundleProductSaveTest.php new file mode 100644 index 0000000000000..3ce3181b9c803 --- /dev/null +++ b/dev/tests/integration/testsuite/Magento/Bundle/Controller/Adminhtml/Product/AbstractBundleProductSaveTest.php @@ -0,0 +1,252 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +declare(strict_types=1); + +namespace Magento\Bundle\Controller\Adminhtml\Product; + +use Magento\Bundle\Model\Product\Type; +use Magento\Bundle\Model\ResourceModel\Option\Collection as OptionCollection; +use Magento\Bundle\Model\ResourceModel\Selection\Collection as SelectionCollection; +use Magento\Catalog\Api\Data\ProductAttributeInterface; +use Magento\Catalog\Api\Data\ProductInterface; +use Magento\Catalog\Api\ProductRepositoryInterface; +use Magento\Catalog\Model\ResourceModel\Product as ProductResource; +use Magento\Eav\Model\Config; +use Magento\Framework\App\Request\Http as HttpRequest; +use Magento\TestFramework\TestCase\AbstractBackendController; + +/** + * Class determine basic logic for bundle product save tests + */ +abstract class AbstractBundleProductSaveTest extends AbstractBackendController +{ + /** @var string */ + protected $productToDelete; + + /** @var ProductRepositoryInterface */ + protected $productRepository; + + /** @var Config */ + private $eavConfig; + + /** @var ProductResource */ + private $productResource; + + /** + * @inheritdoc + */ + protected function setUp() + { + parent::setUp(); + + $this->productRepository = $this->_objectManager->get(ProductRepositoryInterface::class); + $this->eavConfig = $this->_objectManager->get(Config::class); + $this->productResource = $this->_objectManager->get(ProductResource::class); + $this->productToDelete = $this->getStaticProductData()['sku']; + } + + /** + * @inheritdoc + */ + protected function tearDown() + { + if ($this->productToDelete) { + $this->productRepository->deleteById($this->productToDelete); + } + + parent::tearDown(); + } + + /** + * Retrieve default product attribute set id. + * + * @return int + */ + protected function getDefaultAttributeSetId(): int + { + return (int)$this->eavConfig->getEntityType(ProductAttributeInterface::ENTITY_TYPE_CODE) + ->getDefaultAttributeSetId(); + } + + /** + * Prepare request + * + * @param array $post + * @param int|null $id + * @return array + */ + protected function prepareRequestData(array $post, ?int $id = null): array + { + $post = $this->preparePostParams($post); + $this->setRequestparams($post, $id); + + return $post; + } + + /** + * Prepare and assert bundle options + * + * @param array $bundleOptions + * @return void + */ + protected function assertBundleOptions(array $bundleOptions): void + { + $mainProduct = $this->productRepository->get($this->getStaticProductData()['sku'], false, null, true); + $optionsCollection = $mainProduct->getTypeInstance()->getOptionsCollection($mainProduct); + $selectionCollection = $mainProduct->getTypeInstance() + ->getSelectionsCollection($optionsCollection->getAllIds(), $mainProduct); + $this->assertOptionsData($bundleOptions, $optionsCollection, $selectionCollection); + } + + /** + * Prepare post params before dispatch + * + * @param array $post + * @return array + */ + private function preparePostParams(array $post): array + { + $post['product'] = $this->getStaticProductData(); + foreach ($post['bundle_options']['bundle_options'] as &$bundleOption) { + $bundleOption = $this->prepareOptionByType($bundleOption['type'], $bundleOption); + $productIdsBySkus = $this->productResource->getProductsIdsBySkus( + array_column($bundleOption['bundle_selections'], 'sku') + ); + foreach ($bundleOption['bundle_selections'] as &$bundleSelection) { + $bundleSelection = $this->prepareSelection($productIdsBySkus, $bundleSelection); + } + } + + return $post; + } + + /** + * Prepare option params + * + * @param string $type + * @param array $option + * @return array + */ + private function prepareOptionByType(string $type, array $option): array + { + $option['required'] = '1'; + $option['delete'] = ''; + $option['title'] = $option['title'] ?? $type . ' Option Title'; + + return $option; + } + + /** + * Prepare selection params + * + * @param array $productIdsBySkus + * @param array $selection + * @return array + */ + private function prepareSelection(array $productIdsBySkus, array $selection): array + { + $staticData = [ + 'price' => '10', + 'selection_qty' => '5', + 'selection_can_change_qty' => '0' + ]; + $selection['product_id'] = $productIdsBySkus[$selection['sku']]; + $selection = array_merge($selection, $staticData); + + return $selection; + } + + /** + * Assert bundle options data + * + * @param array $expectedOptions + * @param OptionCollection $actualOptions + * @param SelectionCollection $selectionCollection + * @return void + */ + private function assertOptionsData( + array $expectedOptions, + OptionCollection $actualOptions, + SelectionCollection $selectionCollection + ): void { + $this->assertCount(count($expectedOptions['bundle_options']), $actualOptions); + foreach ($expectedOptions['bundle_options'] as $expectedOption) { + $optionToCheck = $actualOptions->getItemByColumnValue('title', $expectedOption['title']); + $this->assertNotNull($optionToCheck->getId()); + $selectionToCheck = $selectionCollection->getItemsByColumnValue('option_id', $optionToCheck->getId()); + $this->assertCount(count($expectedOption['bundle_selections']), $selectionToCheck); + $this->assertSelections($expectedOption['bundle_selections'], $selectionToCheck); + unset($expectedOption['delete'], $expectedOption['bundle_selections']); + foreach ($expectedOption as $key => $value) { + $this->assertEquals($value, $optionToCheck->getData($key)); + } + } + } + + /** + * Assert selections data + * + * @param array $expectedSelections + * @param array $actualSelections + * @return void + */ + private function assertSelections(array $expectedSelections, array $actualSelections): void + { + foreach ($expectedSelections as $expectedSelection) { + $actualSelectionToCheck = $this->getSelectionByProductSku($expectedSelection['sku'], $actualSelections); + $this->assertNotNull($actualSelectionToCheck); + foreach ($expectedSelection as $key => $value) { + $this->assertEquals($value, $actualSelectionToCheck->getData($key)); + } + } + } + + /** + * Get selection by product sku + * + * @param string $sku + * @param array $actualSelections + * @return ProductInterface + */ + private function getSelectionByProductSku(string $sku, array $actualSelections): ProductInterface + { + $item = null; + foreach ($actualSelections as $selection) { + if ($selection->getSku() === $sku) { + $item = $selection; + break; + } + } + + return $item; + } + + /** + * Set request parameters + * + * @param array $post + * @param int|null $id + * @return void + */ + private function setRequestParams(array $post, ?int $id): void + { + $this->getRequest()->setMethod(HttpRequest::METHOD_POST); + $params = ['type' => Type::TYPE_CODE, 'set' => $this->getDefaultAttributeSetId()]; + if ($id) { + $params['id'] = $id; + } + $this->getRequest()->setParams($params); + $this->getRequest()->setPostValue('product', $post['product']); + $this->getRequest()->setPostValue('bundle_options', $post['bundle_options']); + } + + /** + * Get main product data + * + * @return array + */ + abstract protected function getStaticProductData(): array; +} diff --git a/dev/tests/integration/testsuite/Magento/Bundle/Controller/Adminhtml/Product/DynamicBundleProductTest.php b/dev/tests/integration/testsuite/Magento/Bundle/Controller/Adminhtml/Product/DynamicBundleProductTest.php new file mode 100644 index 0000000000000..988baf91981bc --- /dev/null +++ b/dev/tests/integration/testsuite/Magento/Bundle/Controller/Adminhtml/Product/DynamicBundleProductTest.php @@ -0,0 +1,269 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +declare(strict_types=1); + +namespace Magento\Bundle\Controller\Adminhtml\Product; + +use Magento\Bundle\Model\Product\Price; +use Magento\Catalog\Model\Product\Type\AbstractType; + +/** + * Class checks dynamic bundle product save behavior + * + * @magentoAppArea adminhtml + */ +class DynamicBundleProductTest extends AbstractBundleProductSaveTest +{ + /** + * @magentoDataFixture Magento/Catalog/_files/second_product_simple.php + * + * @dataProvider bundleProductDataProvider + * + * @param array $post + * @return void + */ + public function testBundleProductSave(array $post): void + { + $post = $this->prepareRequestData($post); + $this->dispatch('backend/catalog/product/save'); + $this->assertBundleOptions($post['bundle_options']); + } + + /** + * @return array + */ + public function bundleProductDataProvider(): array + { + return [ + 'with_dropdown_option' => [ + 'post' => [ + 'bundle_options' => [ + 'bundle_options' => [ + [ + 'type' => 'select', + 'bundle_selections' => [ + [ + 'name' => 'Simple Product2', + 'sku' => 'simple2', + ], + ], + ], + ], + ], + ], + ], + 'with_radio_buttons_option' => [ + 'post' => [ + 'bundle_options' => [ + 'bundle_options' => [ + [ + 'type' => 'radio', + 'bundle_selections' => [ + [ + 'name' => 'Simple Product2', + 'sku' => 'simple2', + ], + ], + ], + ], + ], + ], + ], + 'with_checkbox_option' => [ + 'post' => [ + 'bundle_options' => [ + 'bundle_options' => [ + [ + 'type' => 'checkbox', + 'bundle_selections' => [ + [ + 'name' => 'Simple Product2', + 'sku' => 'simple2', + ], + ], + ], + ], + ], + ], + ], + 'with_multiselect_option' => [ + 'post' => [ + 'bundle_options' => [ + 'bundle_options' => [ + [ + 'type' => 'multi', + 'bundle_selections' => [ + [ + 'name' => 'Simple Product2', + 'sku' => 'simple2', + ], + ], + ], + ], + ], + ], + ], + ]; + } + + /** + * @magentoDataFixture Magento/Catalog/_files/second_product_simple.php + * @magentoDataFixture Magento/Catalog/_files/product_simple_duplicated.php + * + * @dataProvider multiOptionsDataProvider + * + * @param array $post + * @return void + */ + public function testBundleProductSaveMultiOptions(array $post): void + { + $post = $this->prepareRequestData($post); + $this->dispatch('backend/catalog/product/save'); + $this->assertBundleOptions($post['bundle_options']); + } + + /** + * @return array + */ + public function multiOptionsDataProvider(): array + { + return [ + 'with_two_options_few_selections' => [ + 'post' => [ + 'bundle_options' => [ + 'bundle_options' => [ + [ + 'type' => 'select', + 'bundle_selections' => [ + [ + 'name' => 'Simple Product2', + 'sku' => 'simple2', + ], + [ + 'name' => 'Simple Product', + 'sku' => 'simple-1', + ], + ], + ], + [ + 'type' => 'checkbox', + 'bundle_selections' => [ + [ + 'name' => 'Simple Product', + 'sku' => 'simple-1', + ], + ], + ], + ], + ], + ], + ], + ]; + } + + /** + * @magentoDataFixture Magento/Catalog/_files/second_product_simple.php + * + * @dataProvider emptyOptionTitleDataProvider + * + * @param array $post + * @return void + */ + public function testProductSaveMissedOptionTitle(array $post): void + { + $this->productToDelete = null; + $post = $this->prepareRequestData($post); + $this->dispatch('backend/catalog/product/save'); + $this->assertSessionMessages($this->equalTo(["The option couldn't be saved."])); + } + + /** + * @return array + */ + public function emptyOptionTitleDataProvider(): array + { + return [ + 'empty_option_title' => [ + 'post' => [ + 'bundle_options' => [ + 'bundle_options' => [ + [ + 'title' => '', + 'type' => 'multi', + 'bundle_selections' => [ + [ + 'name' => 'Simple Product2', + 'sku' => 'simple2', + ], + ], + ], + ], + ], + ], + ], + ]; + } + + /** + * @magentoDataFixture Magento/Bundle/_files/bundle_product_checkbox_options.php + * @magentoDataFixture Magento/Catalog/_files/second_product_simple.php + * + * @dataProvider updateProductDataProvider + * + * @param array $post + * @return void + */ + public function testUpdateProduct(array $post): void + { + $id = $this->productRepository->get('bundle-product-checkbox-options')->getId(); + $post = $this->prepareRequestData($post, (int)$id); + $this->dispatch('backend/catalog/product/save'); + $this->assertBundleOptions($post['bundle_options']); + } + + /** + * @return array + */ + public function updateProductDataProvider(): array + { + return [ + 'update_existing_product' => [ + 'post' => [ + 'bundle_options' => [ + 'bundle_options' => [ + [ + 'type' => 'multi', + 'bundle_selections' => [ + [ + 'name' => 'Simple Product2', + 'sku' => 'simple2', + ], + ], + ], + ], + ], + ], + ], + ]; + } + + /** + * @inheritdoc + */ + protected function getStaticProductData(): array + { + return [ + 'sku' => 'bundle-test-product', + 'name' => 'test-bundle', + 'price' => '', + 'sku_type' => '0', + 'price_type' => Price::PRICE_TYPE_DYNAMIC, + 'weight_type' => '0', + 'shipment_type' => AbstractType::SHIPMENT_TOGETHER, + 'attribute_set_id' => $this->getDefaultAttributeSetId(), + ]; + } +} diff --git a/dev/tests/integration/testsuite/Magento/Bundle/Controller/Adminhtml/Product/FixedBundleProductTest.php b/dev/tests/integration/testsuite/Magento/Bundle/Controller/Adminhtml/Product/FixedBundleProductTest.php new file mode 100644 index 0000000000000..908a96368992d --- /dev/null +++ b/dev/tests/integration/testsuite/Magento/Bundle/Controller/Adminhtml/Product/FixedBundleProductTest.php @@ -0,0 +1,226 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +declare(strict_types=1); + +namespace Magento\Bundle\Controller\Adminhtml\Product; + +use Magento\Bundle\Model\Product\Price; +use Magento\Catalog\Model\Product\Type\AbstractType; + +/** + * Class checks fixed bundle product save behavior + * + * @magentoAppArea adminhtml + */ +class FixedBundleProductTest extends AbstractBundleProductSaveTest +{ + /** + * @magentoDataFixture Magento/Catalog/_files/second_product_simple.php + * + * @dataProvider fixedBundleProductDataProvider + * + * @param array $post + * @return void + */ + public function testBundleProductSave(array $post): void + { + $post = $this->prepareRequestData($post); + $this->dispatch('backend/catalog/product/save'); + $this->assertBundleOptions($post['bundle_options']); + } + + /** + * @return array + */ + public function fixedBundleProductDataProvider(): array + { + return [ + 'with_dropdown_option' => [ + 'post' => [ + 'bundle_options' => [ + 'bundle_options' => [ + [ + 'type' => 'select', + 'bundle_selections' => [ + [ + 'name' => 'Simple Product2', + 'sku' => 'simple2', + ], + ], + ], + ], + ], + ], + ], + 'with_radio_buttons_option' => [ + 'post' => [ + 'bundle_options' => [ + 'bundle_options' => [ + [ + 'type' => 'radio', + 'bundle_selections' => [ + [ + 'name' => 'Simple Product2', + 'sku' => 'simple2', + ], + ], + ], + ], + ], + ], + ], + 'with_checkbox_option' => [ + 'post' => [ + 'bundle_options' => [ + 'bundle_options' => [ + [ + 'type' => 'checkbox', + 'bundle_selections' => [ + [ + 'name' => 'Simple Product2', + 'sku' => 'simple2', + ], + ], + ], + ], + ], + ], + ], + 'with_multiselect_option' => [ + 'post' => [ + 'bundle_options' => [ + 'bundle_options' => [ + [ + 'type' => 'multi', + 'bundle_selections' => [ + [ + 'name' => 'Simple Product2', + 'sku' => 'simple2', + ], + ], + ], + ], + ], + ], + ], + ]; + } + + /** + * @magentoDataFixture Magento/Catalog/_files/second_product_simple.php + * @magentoDataFixture Magento/Catalog/_files/product_simple_duplicated.php + * + * @dataProvider multiOptionsDataProvider + * + * @param array $post + * @return void + */ + public function testBundleProductSaveMultiOptions(array $post): void + { + $post = $this->prepareRequestData($post); + $this->dispatch('backend/catalog/product/save'); + $this->assertBundleOptions($post['bundle_options']); + } + + /** + * @return array + */ + public function multiOptionsDataProvider(): array + { + return [ + 'with_two_options_few_selections' => [ + 'post' => [ + 'bundle_options' => [ + 'bundle_options' => [ + [ + 'type' => 'select', + 'bundle_selections' => [ + [ + 'name' => 'Simple Product2', + 'sku' => 'simple2', + ], + [ + 'name' => 'Simple Product', + 'sku' => 'simple-1', + ], + ], + ], + [ + 'type' => 'checkbox', + 'bundle_selections' => [ + [ + 'name' => 'Simple Product', + 'sku' => 'simple-1', + ], + ], + ], + ], + ], + ], + ], + ]; + } + + /** + * @magentoDataFixture Magento/Bundle/_files/bundle_product_checkbox_options.php + * @magentoDataFixture Magento/Catalog/_files/second_product_simple.php + * + * @dataProvider updateProductDataProvider + * + * @param array $post + * @return void + */ + public function testUpdateProduct(array $post): void + { + $id = $this->productRepository->get('bundle-product-checkbox-options')->getId(); + $post = $this->prepareRequestData($post, (int)$id); + $this->dispatch('backend/catalog/product/save'); + $this->assertBundleOptions($post['bundle_options']); + } + + /** + * @return array + */ + public function updateProductDataProvider(): array + { + return [ + 'update_existing_product' => [ + 'post' => [ + 'bundle_options' => [ + 'bundle_options' => [ + [ + 'type' => 'multi', + 'bundle_selections' => [ + [ + 'name' => 'Simple Product2', + 'sku' => 'simple2', + ], + ], + ], + ], + ], + ], + ], + ]; + } + + /** + * @inheritdoc + */ + protected function getStaticProductData(): array + { + return [ + 'sku' => 'bundle-test-product', + 'name' => 'test-bundle', + 'price' => '150', + 'sku_type' => '1', + 'price_type' => Price::PRICE_TYPE_FIXED, + 'weight_type' => '1', + 'shipment_type' => AbstractType::SHIPMENT_TOGETHER, + 'attribute_set_id' => $this->getDefaultAttributeSetId(), + ]; + } +} diff --git a/dev/tests/integration/testsuite/Magento/Bundle/Model/Product/BundlePriceAbstract.php b/dev/tests/integration/testsuite/Magento/Bundle/Model/Product/BundlePriceAbstract.php index e30916810b1e0..2a7b80e62797d 100644 --- a/dev/tests/integration/testsuite/Magento/Bundle/Model/Product/BundlePriceAbstract.php +++ b/dev/tests/integration/testsuite/Magento/Bundle/Model/Product/BundlePriceAbstract.php @@ -3,11 +3,13 @@ * Copyright © Magento, Inc. All rights reserved. * See COPYING.txt for license details. */ +declare(strict_types=1); namespace Magento\Bundle\Model\Product; /** * Abstract class for testing bundle prices + * * @SuppressWarnings(PHPMD.CouplingBetweenObjects) */ abstract class BundlePriceAbstract extends \PHPUnit\Framework\TestCase @@ -29,6 +31,14 @@ abstract class BundlePriceAbstract extends \PHPUnit\Framework\TestCase */ protected $productCollectionFactory; + /** + * @var \Magento\CatalogRule\Model\RuleFactory + */ + private $ruleFactory; + + /** + * @inheritdoc + */ protected function setUp() { $this->objectManager = \Magento\TestFramework\Helper\Bootstrap::getObjectManager(); @@ -42,15 +52,19 @@ protected function setUp() true, \Magento\Store\Model\ScopeInterface::SCOPE_STORE ); + $this->ruleFactory = $this->objectManager->get(\Magento\CatalogRule\Model\RuleFactory::class); } /** - * Get test cases + * Get test cases. + * * @return array */ abstract public function getTestCases(); /** + * Prepare fixture. + * * @param array $strategyModifiers * @param string $productSku * @return void @@ -61,11 +75,14 @@ abstract public function getTestCases(); */ protected function prepareFixture($strategyModifiers, $productSku) { + $this->ruleFactory->create()->clearPriceRulesData(); + $bundleProduct = $this->productRepository->get($productSku); foreach ($strategyModifiers as $modifier) { if (method_exists($this, $modifier['modifierName'])) { array_unshift($modifier['data'], $bundleProduct); + // phpcs:ignore Magento2.Functions.DiscouragedFunction $bundleProduct = call_user_func_array([$this, $modifier['modifierName']], $modifier['data']); } else { throw new \Magento\Framework\Exception\InputException( @@ -113,6 +130,8 @@ protected function addSimpleProduct(\Magento\Catalog\Model\Product $bundleProduc } /** + * Add custom option. + * * @param \Magento\Catalog\Model\Product $bundleProduct * @param array $optionsData * @return \Magento\Catalog\Model\Product diff --git a/dev/tests/integration/testsuite/Magento/Bundle/Model/Product/FixedBundleWithCatalogPriceRuleCalculatorTest.php b/dev/tests/integration/testsuite/Magento/Bundle/Model/Product/FixedBundleWithCatalogPriceRuleCalculatorTest.php index b3c46c75bcfb7..2b0e8c75a15e0 100644 --- a/dev/tests/integration/testsuite/Magento/Bundle/Model/Product/FixedBundleWithCatalogPriceRuleCalculatorTest.php +++ b/dev/tests/integration/testsuite/Magento/Bundle/Model/Product/FixedBundleWithCatalogPriceRuleCalculatorTest.php @@ -3,6 +3,7 @@ * Copyright © Magento, Inc. All rights reserved. * See COPYING.txt for license details. */ +declare(strict_types=1); namespace Magento\Bundle\Model\Product; diff --git a/dev/tests/integration/testsuite/Magento/Bundle/Model/Product/PriceTest.php b/dev/tests/integration/testsuite/Magento/Bundle/Model/Product/PriceTest.php index 4a5757aae3134..b9ffbcf4549ea 100644 --- a/dev/tests/integration/testsuite/Magento/Bundle/Model/Product/PriceTest.php +++ b/dev/tests/integration/testsuite/Magento/Bundle/Model/Product/PriceTest.php @@ -3,68 +3,106 @@ * Copyright © Magento, Inc. All rights reserved. * See COPYING.txt for license details. */ +declare(strict_types=1); + namespace Magento\Bundle\Model\Product; +use Magento\Catalog\Api\Data\ProductInterface; +use Magento\Catalog\Api\Data\TierPriceInterface; +use Magento\Catalog\Api\ProductRepositoryInterface; +use Magento\Customer\Model\Group; +use Magento\Framework\ObjectManagerInterface; +use Magento\Framework\Serialize\SerializerInterface; +use Magento\Store\Api\WebsiteRepositoryInterface; +use Magento\TestFramework\Catalog\Model\GetCategoryByName; +use Magento\TestFramework\Catalog\Model\Product\Price\GetPriceIndexDataByProductId; +use Magento\TestFramework\Helper\Bootstrap; +use PHPUnit\Framework\TestCase; + /** * Class to test bundle prices + * + * @magentoDbIsolation disabled + * @magentoAppIsolation enabled + * @magentoAppArea frontend + * @SuppressWarnings(PHPMD.CouplingBetweenObjects) */ -class PriceTest extends \PHPUnit\Framework\TestCase +class PriceTest extends TestCase { + /** @var ObjectManagerInterface */ + private $objectManager; + + /** @var ProductRepositoryInterface */ + private $productRepository; + + /** @var GetPriceIndexDataByProductId */ + private $getPriceIndexDataByProductId; + + /** @var WebsiteRepositoryInterface */ + private $websiteRepository; + + /** @var Price */ + private $priceModel; + + /** @var SerializerInterface */ + private $json; + + /** @var GetCategoryByName */ + private $getCategoryByName; + /** - * @var \Magento\Bundle\Model\Product\Price + * @inheritdoc */ - protected $_model; - protected function setUp() { - $this->_model = \Magento\TestFramework\Helper\Bootstrap::getObjectManager()->create( - \Magento\Bundle\Model\Product\Price::class - ); + $this->objectManager = Bootstrap::getObjectManager(); + $this->priceModel = $this->objectManager->get(Price::class); + $this->websiteRepository = $this->objectManager->get(WebsiteRepositoryInterface::class); + $this->productRepository = $this->objectManager->get(ProductRepositoryInterface::class); + $this->productRepository->cleanCache(); + $this->getPriceIndexDataByProductId = $this->objectManager->get(GetPriceIndexDataByProductId::class); + $this->json = $this->objectManager->get(SerializerInterface::class); + $this->getCategoryByName = $this->objectManager->get(GetCategoryByName::class); } /** * @magentoDataFixture Magento/Bundle/_files/product_with_tier_pricing.php + * + * @return void */ - public function testGetTierPrice() + public function testGetTierPrice(): void { - /** @var \Magento\Catalog\Api\ProductRepositoryInterface $productRepository */ - $productRepository = \Magento\TestFramework\Helper\Bootstrap::getObjectManager() - ->create(\Magento\Catalog\Api\ProductRepositoryInterface::class); - $product = $productRepository->get('bundle-product'); - // fixture - + $product = $this->productRepository->get('bundle-product'); // Note that this is really not the "tier price" but the "tier discount percentage" // so it is expected to be increasing instead of decreasing - $this->assertEquals(8.0, $this->_model->getTierPrice(2, $product)); - $this->assertEquals(20.0, $this->_model->getTierPrice(3, $product)); - $this->assertEquals(20.0, $this->_model->getTierPrice(4, $product)); - $this->assertEquals(30.0, $this->_model->getTierPrice(5, $product)); + $this->assertEquals(8.0, $this->priceModel->getTierPrice(2, $product)); + $this->assertEquals(20.0, $this->priceModel->getTierPrice(3, $product)); + $this->assertEquals(20.0, $this->priceModel->getTierPrice(4, $product)); + $this->assertEquals(30.0, $this->priceModel->getTierPrice(5, $product)); } /** * Test calculation final price for bundle product with tire price in simple product + * @magentoDataFixture Magento/Bundle/_files/product_with_simple_tier_pricing.php + * @dataProvider getSelectionFinalTotalPriceWithSimpleTierPriceDataProvider * * @param float $bundleQty * @param float $selectionQty * @param float $finalPrice - * @magentoDataFixture Magento/Bundle/_files/product_with_simple_tier_pricing.php - * @dataProvider getSelectionFinalTotalPriceWithSimpleTierPriceDataProvider + * @return void */ public function testGetSelectionFinalTotalPriceWithSimpleTierPrice( float $bundleQty, float $selectionQty, float $finalPrice - ) { - /** @var \Magento\Catalog\Api\ProductRepositoryInterface $productRepository */ - $productRepository = \Magento\TestFramework\Helper\Bootstrap::getObjectManager() - ->create(\Magento\Catalog\Api\ProductRepositoryInterface::class); - $bundleProduct = $productRepository->get('bundle-product'); - $simpleProduct = $productRepository->get('simple'); - $simpleProduct->setCustomerGroupId(\Magento\Customer\Model\Group::CUST_GROUP_ALL); + ): void { + $bundleProduct = $this->productRepository->get('bundle-product'); + $simpleProduct = $this->productRepository->get('simple'); + $simpleProduct->setCustomerGroupId(Group::CUST_GROUP_ALL); $this->assertEquals( $finalPrice, - $this->_model->getSelectionFinalTotalPrice( + $this->priceModel->getSelectionFinalTotalPrice( $bundleProduct, $simpleProduct, $bundleQty, @@ -86,4 +124,435 @@ public function getSelectionFinalTotalPriceWithSimpleTierPriceDataProvider(): ar [5, 1, 5], ]; } + + /** + * Fixed Bundle Product with catalog price rule + * @magentoDataFixture Magento/Bundle/_files/fixed_bundle_product_without_discounts.php + * @magentoDataFixture Magento/CatalogRule/_files/rule_apply_as_percentage_of_original_not_logged_user.php + * + * @return void + */ + public function testFixedBundleProductPriceWithCatalogRule(): void + { + $this->checkBundlePrices( + 'fixed_bundle_product_without_discounts', + ['price' => 50, 'final_price' => 45, 'min_price' => 45, 'max_price' => 75, 'tier_price' => null], + ['simple1' => 55, 'simple2' => 56.25, 'simple3' => 70] + ); + } + + /** + * Fixed Bundle Product without discounts + * @magentoDataFixture Magento/Bundle/_files/fixed_bundle_product_without_discounts.php + * + * @return void + */ + public function testFixedBundleProductPriceWithoutDiscounts(): void + { + $this->checkBundlePrices( + 'fixed_bundle_product_without_discounts', + ['price' => 50, 'final_price' => 50, 'min_price' => 60, 'max_price' => 75, 'tier_price' => null], + ['simple1' => 60, 'simple2' => 62.5, 'simple3' => 75] + ); + } + + /** + * Fixed Bundle Product with special price + * @magentoDataFixture Magento/Bundle/_files/fixed_bundle_product_with_special_price.php + * + * @return void + */ + public function testFixedBundleProductPriceWithSpecialPrice(): void + { + $this->checkBundlePrices( + 'fixed_bundle_product_with_special_price', + ['price' => 50, 'final_price' => 40, 'min_price' => 48, 'max_price' => 60, 'tier_price' => null], + ['simple1' => 48, 'simple2' => 50, 'simple3' => 60] + ); + } + + /** + * Fixed Bundle Product with tier price + * @magentoDataFixture Magento/Bundle/_files/fixed_bundle_product_with_tier_price.php + * + * @return void + */ + public function testFixedBundleProductPriceWithTierPrice(): void + { + $this->checkBundlePrices( + 'fixed_bundle_product_with_tier_price', + ['price' => 50, 'final_price' => 50, 'min_price' => 60, 'max_price' => 75, 'tier_price' => 60], + ['simple1' => 45, 'simple2' => 46.88, 'simple3' => 56.25] + ); + } + + /** + * Dynamic Bundle Product without discount + options without discounts + * @magentoDataFixture Magento/Bundle/_files/dynamic_bundle_product_without_discounts.php + * + * @return void + */ + public function testDynamicBundleProductWithoutDiscountAndOptionsWithoutDiscounts(): void + { + $this->checkBundlePrices( + 'dynamic_bundle_product_without_discounts', + ['price' => 0, 'final_price' => 0, 'min_price' => 10, 'max_price' => 20, 'tier_price' => null], + ['simple1000' => 10, 'simple1001' => 20] + ); + } + + /** + * Dynamic Bundle Product without discount + options with special price + * @magentoDataFixture Magento/Bundle/_files/dynamic_bundle_product_without_discounts.php + * + * @return void + */ + public function testDynamicBundleProductWithoutDiscountsAndOptionsWithSpecialPrices(): void + { + $this->updateProducts($this->specialPricesForOptionsData()); + $this->checkBundlePrices( + 'dynamic_bundle_product_without_discounts', + ['price' => 0, 'final_price' => 0, 'min_price' => 8, 'max_price' => 15, 'tier_price' => null], + ['simple1000' => 8, 'simple1001' => 15] + ); + } + + /** + * Dynamic Bundle Product without discount + options with tier prices + * @magentoDataFixture Magento/Bundle/_files/dynamic_bundle_product_without_discounts.php + * + * @return void + */ + public function testDynamicBundleProductWithoutDiscountsAndOptionsWithTierPrices(): void + { + $this->updateProducts($this->tierPricesForOptionsData()); + $this->checkBundlePrices( + 'dynamic_bundle_product_without_discounts', + ['price' => 0, 'final_price' => 0, 'min_price' => 8, 'max_price' => 17, 'tier_price' => null], + ['simple1000' => 8, 'simple1001' => 17] + ); + } + + /** + * Dynamic Bundle Product without discounts + options with catalog rule + * @magentoDataFixture Magento/Bundle/_files/dynamic_bundle_product_without_discounts.php + * @magentoDataFixture Magento/CatalogRule/_files/catalog_rule_for_category_999.php + * + * @return void + */ + public function testDynamicBundleProductWithoutDiscountsAndOptionsWithCatalogPriceRule(): void + { + $this->checkBundlePrices( + 'dynamic_bundle_product_without_discounts', + ['price' => 0, 'final_price' => 0, 'min_price' => 7.5, 'max_price' => 15, 'tier_price' => null], + ['simple1000' => 7.5, 'simple1001' => 15] + ); + } + + /** + * Dynamic Bundle Product with tier price + options without discounts + * @magentoDataFixture Magento/Bundle/_files/dynamic_bundle_product_with_tier_price.php + * + * @return void + */ + public function testDynamicBundleProductWithTierPriceAndOptionsWithoutDiscounts(): void + { + $this->checkBundlePrices( + 'dynamic_bundle_product_with_tier_price', + ['price' => 0,'final_price' => 0, 'min_price' => 10, 'max_price' => 20, 'tier_price' => 10], + ['simple1000' => 7.5, 'simple1001' => 15] + ); + } + + /** + * Dynamic Bundle Product with tier price + options with special prices + * @magentoDataFixture Magento/Bundle/_files/dynamic_bundle_product_with_tier_price.php + * + * @return void + */ + public function testDynamicBundleProductWithTierPriceAndOptionsWithSpecialPrices(): void + { + $this->updateProducts($this->specialPricesForOptionsData()); + $this->checkBundlePrices( + 'dynamic_bundle_product_with_tier_price', + ['price' => 0, 'final_price' => 0, 'min_price' => 8, 'max_price' => 15, 'tier_price' => 8], + ['simple1000' => 6, 'simple1001' => 11.25] + ); + } + + /** + * Dynamic Bundle Product with tier price + options with tier price + * @magentoDataFixture Magento/Bundle/_files/dynamic_bundle_product_with_tier_price.php + * + * @return void + */ + public function testDynamicBundleProductWithTierPriceAndOptionsWithTierPrices(): void + { + $this->updateProducts($this->tierPricesForOptionsData()); + $this->checkBundlePrices( + 'dynamic_bundle_product_with_tier_price', + ['price' => 0, 'final_price' => 0, 'min_price' => 8, 'max_price' => 17, 'tier_price' => 8], + ['simple1000' => 6, 'simple1001' => 12.75] + ); + } + + /** + * Dynamic Bundle Product with tier price + options with catalog rule + * @magentoDataFixture Magento/Bundle/_files/dynamic_bundle_product_with_tier_price.php + * @magentoDataFixture Magento/CatalogRule/_files/catalog_rule_for_category_999.php + * + * @return void + */ + public function testDynamicBundleProductWithTierPriceAndOptionsWithCatalogPriceRule(): void + { + $this->checkBundlePrices( + 'dynamic_bundle_product_with_tier_price', + ['price' => 0, 'final_price' => 0, 'min_price' => 7.5, 'max_price' => 15, 'tier_price' => 7.5], + ['simple1000' => 5.63, 'simple1001' => 11.25] + ); + } + + /** + * Dynamic Bundle Product with special price + options without discounts + * @magentoDataFixture Magento/Bundle/_files/dynamic_bundle_product_with_special_price.php + * + * @return void + */ + public function testDynamicBundleProductWithSpecialPriceAndOptionsWithoutDiscounts(): void + { + $this->checkBundlePrices( + 'dynamic_bundle_product_with_special_price', + ['price' => 0, 'final_price' => 0, 'min_price' => 7.5, 'max_price' => 15, 'tier_price' => null], + ['simple1000' => 7.5, 'simple1001' => 15] + ); + } + + /** + * Dynamic Bundle Product with special price + options with special prices + * @magentoDataFixture Magento/Bundle/_files/dynamic_bundle_product_with_special_price.php + * + * @return void + */ + public function testDynamicBundleProductWithSpecialPriceAndOptionsWithSpecialPrices(): void + { + $this->updateProducts($this->specialPricesForOptionsData()); + $this->checkBundlePrices( + 'dynamic_bundle_product_with_special_price', + ['price' => 0, 'final_price' => 0, 'min_price' => 6, 'max_price' => 11.25, 'tier_price' => null], + ['simple1000' => 6, 'simple1001' => 11.25] + ); + } + + /** + * Dynamic Bundle Product with special price + options with tier prices + * @magentoDataFixture Magento/Bundle/_files/dynamic_bundle_product_with_special_price.php + * + * @return void + */ + public function testDynamicBundleProductWithSpecialPriceAndOptionsWithTierPrices(): void + { + $this->updateProducts($this->tierPricesForOptionsData()); + $this->checkBundlePrices( + 'dynamic_bundle_product_with_special_price', + ['price' => 0, 'final_price' => 0, 'min_price' => 6, 'max_price' => 12.75, 'tier_price' => null], + ['simple1000' => 6, 'simple1001' => 12.75] + ); + } + + /** + * Dynamic Bundle Product with special price + options with catalog price rule + * @magentoDataFixture Magento/Bundle/_files/dynamic_bundle_product_with_special_price.php + * @magentoDataFixture Magento/CatalogRule/_files/catalog_rule_for_category_999.php + * + * @return void + */ + public function testDynamicBundleProductWithSpecialPriceAndOptionsWithCatalogPriceRule(): void + { + $this->checkBundlePrices( + 'dynamic_bundle_product_with_special_price', + ['price' => 0, 'final_price' => 0, 'min_price' => 5.625, 'max_price' => 11.25, 'tier_price' => null], + ['simple1000' => 5.63, 'simple1001' => 11.25] + ); + } + + /** + * Dynamic Bundle Product with catalog price rule + options without discounts + * @magentoDataFixture Magento/Bundle/_files/dynamic_bundle_product_with_catalog_rule.php + * + * @return void + */ + public function testDynamicBundleProductWithCatalogPriceRuleAndOptionsWithoutDiscounts(): void + { + $this->checkBundlePrices( + 'dynamic_bundle_product_with_catalog_rule', + ['price' => 0, 'final_price' => 0, 'min_price' => 10, 'max_price' => 20, 'tier_price' => null], + ['simple1000' => 10, 'simple1001' => 20] + ); + } + + /** + * Dynamic Bundle Product with catalog price rule + options with catalog price rule + * @magentoDataFixture Magento/Bundle/_files/dynamic_bundle_product_with_catalog_rule.php + * @magentoDataFixture Magento/CatalogRule/_files/catalog_rule_for_category_999.php + * + * @return void + */ + public function testDynamicBundleProductWithCatalogPriceRuleAndOptionsWithCatalogPriceRule(): void + { + $this->checkBundlePrices( + 'dynamic_bundle_product_with_catalog_rule', + ['price' => 0, 'final_price' => 0, 'min_price' => 7.5, 'max_price' => 15, 'tier_price' => null], + ['simple1000' => 7.5, 'simple1001' => 15] + ); + } + + /** + * Dynamic Bundle Product with catalog price rule + options with special prices + * @magentoDataFixture Magento/Bundle/_files/dynamic_bundle_product_with_catalog_rule.php + * + * @return void + */ + public function testDynamicBundleProductWithCatalogPriceRuleAndOptionsWithSpecialPrices(): void + { + $this->updateProducts($this->specialPricesForOptionsData()); + $this->checkBundlePrices( + 'dynamic_bundle_product_with_catalog_rule', + ['price' => 0, 'final_price' => 0, 'min_price' => 8, 'max_price' => 15, 'tier_price' => null], + ['simple1000' => 8, 'simple1001' => 15] + ); + } + + /** + * Dynamic Bundle Product with catalog price rule + options with tier price + * @magentoDataFixture Magento/Bundle/_files/dynamic_bundle_product_with_catalog_rule.php + * + * @return void + */ + public function testDynamicBundleProductWithCatalogPriceRuleAndOptionsWithTierPrice(): void + { + $this->updateProducts($this->tierPricesForOptionsData()); + $this->checkBundlePrices( + 'dynamic_bundle_product_with_catalog_rule', + ['price' => 0, 'final_price' => 0, 'min_price' => 8, 'max_price' => 17, 'tier_price' => null], + ['simple1000' => 8, 'simple1001' => 17] + ); + } + + /** + * Check bundle prices from index table and final bundle option price. + * + * @param string $sku + * @param array $indexPrices + * @param array $expectedPrices + * @return void + */ + private function checkBundlePrices(string $sku, array $indexPrices, array $expectedPrices): void + { + $product = $this->productRepository->get($sku); + $this->assertIndexTableData((int)$product->getId(), $indexPrices); + $this->assertPriceWithChosenOption($product, $expectedPrices); + } + + /** + * Asserts price data in index table. + * + * @param int $productId + * @param array $expectedPrices + * @return void + */ + private function assertIndexTableData(int $productId, array $expectedPrices): void + { + $data = $this->getPriceIndexDataByProductId->execute( + $productId, + Group::NOT_LOGGED_IN_ID, + (int)$this->websiteRepository->get('base')->getId() + ); + $data = reset($data); + foreach ($expectedPrices as $column => $price) { + $this->assertEquals($price, $data[$column]); + } + } + + /** + * Assert bundle final price with chosen option. + * + * @param ProductInterface $bundle + * @param array $expectedPrices + * @return void + */ + private function assertPriceWithChosenOption(ProductInterface $bundle, array $expectedPrices): void + { + $option = $bundle->getExtensionAttributes()->getBundleProductOptions()[0] ?? null; + $this->assertNotNull($option); + foreach ($option->getProductLinks() as $productLink) { + $bundle->addCustomOption('bundle_selection_ids', $this->json->serialize([$productLink->getId()])); + $bundle->addCustomOption('selection_qty_' . $productLink->getId(), 1); + $this->assertEquals( + round($expectedPrices[$productLink->getSku()], 2), + round($this->priceModel->getFinalPrice(1, $bundle), 2) + ); + } + } + + /** + * Update products. + * + * @param array $products + * @return void + */ + private function updateProducts(array $products): void + { + foreach ($products as $sku => $updateData) { + $product = $this->productRepository->get($sku); + $product->addData($updateData); + $this->productRepository->save($product); + } + } + + /** + * @return array + */ + private function specialPricesForOptionsData(): array + { + return [ + 'simple1000' => [ + 'special_price' => 8, + ], + 'simple1001' => [ + 'special_price' => 15, + ], + ]; + } + + /** + * @return array + */ + private function tierPricesForOptionsData(): array + { + return [ + 'simple1000' => [ + 'tier_price' => [ + [ + 'website_id' => 0, + 'cust_group' => Group::CUST_GROUP_ALL, + 'price_qty' => 1, + 'value_type' => TierPriceInterface::PRICE_TYPE_FIXED, + 'price' => 8, + ], + ], + ], + 'simple1001' => [ + 'tier_price' => [ + [ + 'website_id' => 0, + 'cust_group' => Group::CUST_GROUP_ALL, + 'price_qty' => 1, + 'value_type' => TierPriceInterface::PRICE_TYPE_DISCOUNT, + 'website_price' => 20, + 'percentage_value' => 15, + ], + ], + ], + ]; + } } diff --git a/dev/tests/integration/testsuite/Magento/Bundle/_files/bundle_product_checkbox_options.php b/dev/tests/integration/testsuite/Magento/Bundle/_files/bundle_product_checkbox_options.php new file mode 100644 index 0000000000000..4b581a5cbf5d6 --- /dev/null +++ b/dev/tests/integration/testsuite/Magento/Bundle/_files/bundle_product_checkbox_options.php @@ -0,0 +1,77 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +declare(strict_types=1); + +use Magento\Bundle\Model\Product\Price; +use Magento\Catalog\Api\ProductRepositoryInterface; +use Magento\Catalog\Model\Product\Attribute\Source\Status; +use Magento\Catalog\Model\Product\Type; +use Magento\Catalog\Model\Product\Type\AbstractType; +use Magento\Catalog\Model\Product\Visibility; +use Magento\Catalog\Model\ProductFactory; +use Magento\Store\Api\WebsiteRepositoryInterface; +use Magento\TestFramework\Bundle\Model\PrepareBundleLinks; +use Magento\TestFramework\Helper\Bootstrap; + +require __DIR__ . '/../../../Magento/Catalog/_files/product_simple_duplicated.php'; +require __DIR__ . '/../../../Magento/Catalog/_files/second_product_simple.php'; + +$objectManager = Bootstrap::getObjectManager(); +/** @var WebsiteRepositoryInterface $websiteRepository */ +$websiteRepository = $objectManager->get(WebsiteRepositoryInterface::class); +$baseWebsiteId = $websiteRepository->get('base')->getId(); +/** @var PrepareBundleLinks $prepareBundleLinks */ +$prepareBundleLinks = $objectManager->get(PrepareBundleLinks::class); +/** @var ProductFactory $productFactory */ +$productFactory = $objectManager->get(ProductFactory::class); +/** @var ProductRepositoryInterface $productRepository */ +$productRepository = $objectManager->get(ProductRepositoryInterface::class); +$productRepository->cleanCache(); +$bundleProduct = $productFactory->create(); +$bundleProduct->setTypeId(Type::TYPE_BUNDLE) + ->setAttributeSetId($bundleProduct->getDefaultAttributeSetId()) + ->setWebsiteIds([$baseWebsiteId]) + ->setName('Bundle Product Checkbox Options') + ->setSku('bundle-product-checkbox-options') + ->setVisibility(Visibility::VISIBILITY_BOTH) + ->setStatus(Status::STATUS_ENABLED) + ->setStockData(['use_config_manage_stock' => 1, 'qty' => 100, 'is_qty_decimal' => 0, 'is_in_stock' => 1]) + ->setPriceView(0) + ->setSkuType(1) + ->setWeightType(1) + ->setPriceType(Price::PRICE_TYPE_DYNAMIC) + ->setPrice(10.0) + ->setShipmentType(AbstractType::SHIPMENT_TOGETHER); + +$bundleOptionsData = [ + [ + 'title' => 'Checkbox Options', + 'default_title' => 'Checkbox Options', + 'type' => 'checkbox', + 'required' => 0, + 'delete' => '', + ], +]; +$bundleSelectionsData = [ + [ + 'sku' => $product->getSku(), + 'selection_qty' => 1, + 'selection_price_value' => 0, + 'selection_can_change_qty' => 1, + 'delete' => '', + ], + [ + 'sku' => $product2->getSku(), + 'selection_qty' => 1, + 'selection_price_value' => 0, + 'selection_can_change_qty' => 1, + 'delete' => '', + ], +]; + +$bundleProduct = $prepareBundleLinks->execute($bundleProduct, $bundleOptionsData, [$bundleSelectionsData]); + +$productRepository->save($bundleProduct); diff --git a/dev/tests/integration/testsuite/Magento/Bundle/_files/bundle_product_checkbox_options_rollback.php b/dev/tests/integration/testsuite/Magento/Bundle/_files/bundle_product_checkbox_options_rollback.php new file mode 100644 index 0000000000000..3475737790c86 --- /dev/null +++ b/dev/tests/integration/testsuite/Magento/Bundle/_files/bundle_product_checkbox_options_rollback.php @@ -0,0 +1,27 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +declare(strict_types=1); + +use Magento\Catalog\Api\ProductRepositoryInterface; +use Magento\Framework\Exception\NoSuchEntityException; + +require __DIR__ . '/../../../Magento/Catalog/_files/product_simple_duplicated_rollback.php'; +require __DIR__ . '/../../../Magento/Catalog/_files/second_product_simple_rollback.php'; + +$registry->unregister('isSecureArea'); +$registry->register('isSecureArea', true); +/** @var ProductRepositoryInterface $productRepository */ +$productRepository = $objectManager->get(ProductRepositoryInterface::class); + +try { + $product = $productRepository->get('bundle-product-checkbox-options', false, null, true); + $productRepository->delete($product); +} catch (NoSuchEntityException $e) { + //Product already removed +} + +$registry->unregister('isSecureArea'); +$registry->register('isSecureArea', false); diff --git a/dev/tests/integration/testsuite/Magento/Bundle/_files/bundle_product_checkbox_required_option.php b/dev/tests/integration/testsuite/Magento/Bundle/_files/bundle_product_checkbox_required_option.php new file mode 100644 index 0000000000000..1cc9ede5d71e4 --- /dev/null +++ b/dev/tests/integration/testsuite/Magento/Bundle/_files/bundle_product_checkbox_required_option.php @@ -0,0 +1,68 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +declare(strict_types=1); + +use Magento\Bundle\Model\Product\Price; +use Magento\Catalog\Api\ProductRepositoryInterface; +use Magento\Catalog\Model\Product\Attribute\Source\Status; +use Magento\Catalog\Model\Product\Type; +use Magento\Catalog\Model\Product\Type\AbstractType; +use Magento\Catalog\Model\Product\Visibility; +use Magento\Catalog\Model\ProductFactory; +use Magento\Store\Api\WebsiteRepositoryInterface; +use Magento\TestFramework\Bundle\Model\PrepareBundleLinks; +use Magento\TestFramework\Helper\Bootstrap; + +require __DIR__ . '/../../../Magento/Catalog/_files/product_simple_duplicated.php'; + +$objectManager = Bootstrap::getObjectManager(); +/** @var WebsiteRepositoryInterface $websiteRepository */ +$websiteRepository = $objectManager->get(WebsiteRepositoryInterface::class); +$baseWebsiteId = $websiteRepository->get('base')->getId(); +/** @var ProductRepositoryInterface $productRepository */ +$productRepository = $objectManager->get(ProductRepositoryInterface::class); +$productRepository->cleanCache(); +/** @var PrepareBundleLinks $prepareBundleLinks */ +$prepareBundleLinks = $objectManager->get(PrepareBundleLinks::class); +/** @var ProductFactory $productFactory */ +$productFactory = $objectManager->get(ProductFactory::class); +$bundleProduct = $productFactory->create(); +$bundleProduct->setTypeId(Type::TYPE_BUNDLE) + ->setAttributeSetId($bundleProduct->getDefaultAttributeSetId()) + ->setWebsiteIds([$baseWebsiteId]) + ->setName('Bundle Product Checkbox Required Option') + ->setSku('bundle-product-checkbox-required-option') + ->setVisibility(Visibility::VISIBILITY_BOTH) + ->setStatus(Status::STATUS_ENABLED) + ->setStockData(['use_config_manage_stock' => 1, 'qty' => 100, 'is_qty_decimal' => 0, 'is_in_stock' => 1]) + ->setPriceView(0) + ->setSkuType(1) + ->setWeightType(1) + ->setPriceType(Price::PRICE_TYPE_DYNAMIC) + ->setPrice(10.0) + ->setShipmentType(AbstractType::SHIPMENT_TOGETHER); + +$bundleOptionsData = [ + [ + 'title' => 'Checkbox Options', + 'default_title' => 'Checkbox Options', + 'type' => 'checkbox', + 'required' => 1, + 'delete' => '', + ], +]; +$bundleSelectionsData = [ + [ + 'sku' => $product->getSku(), + 'selection_qty' => 1, + 'selection_price_value' => 0, + 'selection_can_change_qty' => 1, + 'delete' => '', + ], +]; + +$bundleProduct = $prepareBundleLinks->execute($bundleProduct, $bundleOptionsData, [$bundleSelectionsData]); +$productRepository->save($bundleProduct); diff --git a/dev/tests/integration/testsuite/Magento/Bundle/_files/bundle_product_checkbox_required_option_rollback.php b/dev/tests/integration/testsuite/Magento/Bundle/_files/bundle_product_checkbox_required_option_rollback.php new file mode 100644 index 0000000000000..f75241fb8b680 --- /dev/null +++ b/dev/tests/integration/testsuite/Magento/Bundle/_files/bundle_product_checkbox_required_option_rollback.php @@ -0,0 +1,26 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +declare(strict_types=1); + +use Magento\Catalog\Api\ProductRepositoryInterface; +use Magento\Framework\Exception\NoSuchEntityException; + +require __DIR__ . '/../../../Magento/Catalog/_files/product_simple_duplicated_rollback.php'; + +$registry->unregister('isSecureArea'); +$registry->register('isSecureArea', true); +/** @var ProductRepositoryInterface $productRepository */ +$productRepository = $objectManager->get(ProductRepositoryInterface::class); + +try { + $product = $productRepository->get('bundle-product-checkbox-required-option', false, null, true); + $productRepository->delete($product); +} catch (NoSuchEntityException $e) { + //Product already removed +} + +$registry->unregister('isSecureArea'); +$registry->register('isSecureArea', false); diff --git a/dev/tests/integration/testsuite/Magento/Bundle/_files/bundle_product_checkbox_required_options.php b/dev/tests/integration/testsuite/Magento/Bundle/_files/bundle_product_checkbox_required_options.php new file mode 100644 index 0000000000000..5bb6fe6973287 --- /dev/null +++ b/dev/tests/integration/testsuite/Magento/Bundle/_files/bundle_product_checkbox_required_options.php @@ -0,0 +1,76 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +declare(strict_types=1); + +use Magento\Bundle\Model\Product\Price; +use Magento\Catalog\Api\ProductRepositoryInterface; +use Magento\Catalog\Model\Product\Attribute\Source\Status; +use Magento\Catalog\Model\Product\Type; +use Magento\Catalog\Model\Product\Type\AbstractType; +use Magento\Catalog\Model\Product\Visibility; +use Magento\Catalog\Model\ProductFactory; +use Magento\Store\Api\WebsiteRepositoryInterface; +use Magento\TestFramework\Bundle\Model\PrepareBundleLinks; +use Magento\TestFramework\Helper\Bootstrap; + +require __DIR__ . '/../../../Magento/Catalog/_files/product_simple_duplicated.php'; +require __DIR__ . '/../../../Magento/Catalog/_files/second_product_simple.php'; + +$objectManager = Bootstrap::getObjectManager(); +/** @var WebsiteRepositoryInterface $websiteRepository */ +$websiteRepository = $objectManager->get(WebsiteRepositoryInterface::class); +$baseWebsiteId = $websiteRepository->get('base')->getId(); +/** @var PrepareBundleLinks $prepareBundleLinks */ +$prepareBundleLinks = $objectManager->get(PrepareBundleLinks::class); +/** @var ProductRepositoryInterface $productRepository */ +$productRepository = $objectManager->get(ProductRepositoryInterface::class); +$productRepository->cleanCache(); +/** @var ProductFactory $productFactory */ +$productFactory = $objectManager->get(ProductFactory::class); +$bundleProduct = $productFactory->create(); +$bundleProduct->setTypeId(Type::TYPE_BUNDLE) + ->setAttributeSetId($product->getDefaultAttributeSetId()) + ->setWebsiteIds([$baseWebsiteId]) + ->setName('Bundle Product Checkbox Required Options') + ->setSku('bundle-product-checkbox-required-options') + ->setVisibility(Visibility::VISIBILITY_BOTH) + ->setStatus(Status::STATUS_ENABLED) + ->setStockData(['use_config_manage_stock' => 1, 'qty' => 100, 'is_qty_decimal' => 0, 'is_in_stock' => 1]) + ->setPriceView(0) + ->setSkuType(1) + ->setWeightType(1) + ->setPriceType(Price::PRICE_TYPE_DYNAMIC) + ->setPrice(10.0) + ->setShipmentType(AbstractType::SHIPMENT_TOGETHER); + +$bundleOptionsData = [ + [ + 'title' => 'Checkbox Options', + 'default_title' => 'Checkbox Options', + 'type' => 'checkbox', + 'required' => 1, + 'delete' => '', + ], +]; +$bundleSelectionsData = [ + [ + 'sku' => $product->getSku(), + 'selection_qty' => 1, + 'selection_price_value' => 0, + 'selection_can_change_qty' => 1, + 'delete' => '', + ], + [ + 'sku' => $product2->getSku(), + 'selection_qty' => 1, + 'selection_price_value' => 0, + 'selection_can_change_qty' => 1, + 'delete' => '', + ], +]; + +$bundleProduct = $prepareBundleLinks->execute($bundleProduct, $bundleOptionsData, [$bundleSelectionsData]); +$productRepository->save($bundleProduct); diff --git a/dev/tests/integration/testsuite/Magento/Bundle/_files/bundle_product_checkbox_required_options_rollback.php b/dev/tests/integration/testsuite/Magento/Bundle/_files/bundle_product_checkbox_required_options_rollback.php new file mode 100644 index 0000000000000..f601d1d6793e6 --- /dev/null +++ b/dev/tests/integration/testsuite/Magento/Bundle/_files/bundle_product_checkbox_required_options_rollback.php @@ -0,0 +1,27 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +declare(strict_types=1); + +use Magento\Catalog\Api\ProductRepositoryInterface; +use Magento\Framework\Exception\NoSuchEntityException; + +require __DIR__ . '/../../../Magento/Catalog/_files/product_simple_duplicated_rollback.php'; +require __DIR__ . '/../../../Magento/Catalog/_files/second_product_simple_rollback.php'; + +$registry->unregister('isSecureArea'); +$registry->register('isSecureArea', true); +/** @var ProductRepositoryInterface $productRepository */ +$productRepository = $objectManager->get(ProductRepositoryInterface::class); + +try { + $product = $productRepository->get('bundle-product-checkbox-required-options', false, null, true); + $productRepository->delete($product); +} catch (NoSuchEntityException $e) { + //Product already removed +} + +$registry->unregister('isSecureArea'); +$registry->register('isSecureArea', false); diff --git a/dev/tests/integration/testsuite/Magento/Bundle/_files/bundle_product_dropdown_options.php b/dev/tests/integration/testsuite/Magento/Bundle/_files/bundle_product_dropdown_options.php new file mode 100644 index 0000000000000..62c80abc6415f --- /dev/null +++ b/dev/tests/integration/testsuite/Magento/Bundle/_files/bundle_product_dropdown_options.php @@ -0,0 +1,76 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +declare(strict_types=1); + +use Magento\Bundle\Model\Product\Price; +use Magento\Catalog\Api\ProductRepositoryInterface; +use Magento\Catalog\Model\Product\Attribute\Source\Status; +use Magento\Catalog\Model\Product\Type; +use Magento\Catalog\Model\Product\Type\AbstractType; +use Magento\Catalog\Model\Product\Visibility; +use Magento\Catalog\Model\ProductFactory; +use Magento\Store\Api\WebsiteRepositoryInterface; +use Magento\TestFramework\Bundle\Model\PrepareBundleLinks; +use Magento\TestFramework\Helper\Bootstrap; + +require __DIR__ . '/../../../Magento/Catalog/_files/product_simple_duplicated.php'; +require __DIR__ . '/../../../Magento/Catalog/_files/second_product_simple.php'; + +$objectManager = Bootstrap::getObjectManager(); +/** @var WebsiteRepositoryInterface $websiteRepository */ +$websiteRepository = $objectManager->get(WebsiteRepositoryInterface::class); +$baseWebsiteId = $websiteRepository->get('base')->getId(); +/** @var ProductRepositoryInterface $productRepository */ +$productRepository = $objectManager->get(ProductRepositoryInterface::class); +$productRepository->cleanCache(); +/** @var PrepareBundleLinks $prepareBundleLinks */ +$prepareBundleLinks = $objectManager->get(PrepareBundleLinks::class); +/** @var ProductFactory $productFactory */ +$productFactory = $objectManager->get(ProductFactory::class); +$bundleProduct = $productFactory->create(); +$bundleProduct->setTypeId(Type::TYPE_BUNDLE) + ->setAttributeSetId($product->getDefaultAttributeSetId()) + ->setWebsiteIds([$baseWebsiteId]) + ->setName('Bundle Product Dropdown options') + ->setSku('bundle-product-dropdown-options') + ->setVisibility(Visibility::VISIBILITY_BOTH) + ->setStatus(Status::STATUS_ENABLED) + ->setStockData(['use_config_manage_stock' => 1, 'qty' => 100, 'is_qty_decimal' => 0, 'is_in_stock' => 1]) + ->setPriceView(0) + ->setSkuType(1) + ->setWeightType(1) + ->setPriceType(Price::PRICE_TYPE_DYNAMIC) + ->setPrice(10.0) + ->setShipmentType(AbstractType::SHIPMENT_TOGETHER); + +$bundleOptionsData = [ + [ + 'title' => 'Dropdown Options', + 'default_title' => 'Dropdown Options', + 'type' => 'select', + 'required' => 0, + 'delete' => '', + ], +]; +$bundleSelectionsData = [ + [ + 'sku' => $product->getSku(), + 'selection_qty' => 1, + 'selection_price_value' => 0, + 'selection_can_change_qty' => 1, + 'delete' => '', + ], + [ + 'sku' => $product2->getSku(), + 'selection_qty' => 1, + 'selection_price_value' => 0, + 'selection_can_change_qty' => 1, + 'delete' => '', + ], +]; + +$bundleProduct = $prepareBundleLinks->execute($bundleProduct, $bundleOptionsData, [$bundleSelectionsData]); +$productRepository->save($bundleProduct); diff --git a/dev/tests/integration/testsuite/Magento/Bundle/_files/bundle_product_dropdown_options_rollback.php b/dev/tests/integration/testsuite/Magento/Bundle/_files/bundle_product_dropdown_options_rollback.php new file mode 100644 index 0000000000000..857e44d0298cb --- /dev/null +++ b/dev/tests/integration/testsuite/Magento/Bundle/_files/bundle_product_dropdown_options_rollback.php @@ -0,0 +1,27 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +declare(strict_types=1); + +use Magento\Catalog\Api\ProductRepositoryInterface; +use Magento\Framework\Exception\NoSuchEntityException; + +require __DIR__ . '/../../../Magento/Catalog/_files/product_simple_duplicated_rollback.php'; +require __DIR__ . '/../../../Magento/Catalog/_files/second_product_simple_rollback.php'; + +$registry->unregister('isSecureArea'); +$registry->register('isSecureArea', true); +/** @var ProductRepositoryInterface $productRepository */ +$productRepository = $objectManager->get(ProductRepositoryInterface::class); + +try { + $product = $productRepository->get('bundle-product-dropdown-options', false, null, true); + $productRepository->delete($product); +} catch (NoSuchEntityException $e) { + //Product already removed +} + +$registry->unregister('isSecureArea'); +$registry->register('isSecureArea', false); diff --git a/dev/tests/integration/testsuite/Magento/Bundle/_files/bundle_product_dropdown_required_options.php b/dev/tests/integration/testsuite/Magento/Bundle/_files/bundle_product_dropdown_required_options.php new file mode 100644 index 0000000000000..4093c9ff057e7 --- /dev/null +++ b/dev/tests/integration/testsuite/Magento/Bundle/_files/bundle_product_dropdown_required_options.php @@ -0,0 +1,76 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +declare(strict_types=1); + +use Magento\Bundle\Model\Product\Price; +use Magento\Catalog\Api\ProductRepositoryInterface; +use Magento\Catalog\Model\Product\Attribute\Source\Status; +use Magento\Catalog\Model\Product\Type; +use Magento\Catalog\Model\Product\Type\AbstractType; +use Magento\Catalog\Model\Product\Visibility; +use Magento\Catalog\Model\ProductFactory; +use Magento\Store\Api\WebsiteRepositoryInterface; +use Magento\TestFramework\Bundle\Model\PrepareBundleLinks; +use Magento\TestFramework\Helper\Bootstrap; + +require __DIR__ . '/../../../Magento/Catalog/_files/product_simple_duplicated.php'; +require __DIR__ . '/../../../Magento/Catalog/_files/second_product_simple.php'; + +$objectManager = Bootstrap::getObjectManager(); +/** @var WebsiteRepositoryInterface $websiteRepository */ +$websiteRepository = $objectManager->get(WebsiteRepositoryInterface::class); +$baseWebsiteId = $websiteRepository->get('base')->getId(); +/** @var ProductRepositoryInterface $productRepository */ +$productRepository = $objectManager->get(ProductRepositoryInterface::class); +$productRepository->cleanCache(); +/** @var PrepareBundleLinks $prepareBundleLinks */ +$prepareBundleLinks = $objectManager->get(PrepareBundleLinks::class); +/** @var ProductFactory $productFactory */ +$productFactory = $objectManager->get(ProductFactory::class); +$bundleProduct = $productFactory->create(); +$bundleProduct->setTypeId(Type::TYPE_BUNDLE) + ->setAttributeSetId($product->getDefaultAttributeSetId()) + ->setWebsiteIds([$baseWebsiteId]) + ->setName('Bundle Product Dropdown Required options') + ->setSku('bundle-product-dropdown-required-options') + ->setVisibility(Visibility::VISIBILITY_BOTH) + ->setStatus(Status::STATUS_ENABLED) + ->setStockData(['use_config_manage_stock' => 1, 'qty' => 100, 'is_qty_decimal' => 0, 'is_in_stock' => 1]) + ->setPriceView(0) + ->setSkuType(1) + ->setWeightType(1) + ->setPriceType(Price::PRICE_TYPE_DYNAMIC) + ->setPrice(10.0) + ->setShipmentType(AbstractType::SHIPMENT_TOGETHER); + +$bundleOptionsData = [ + [ + 'title' => 'Dropdown Options', + 'default_title' => 'Dropdown Options', + 'type' => 'select', + 'required' => 1, + 'delete' => '', + ], +]; +$bundleSelectionsData = [ + [ + 'sku' => $product->getSku(), + 'selection_qty' => 1, + 'selection_price_value' => 0, + 'selection_can_change_qty' => 1, + 'delete' => '', + ], + [ + 'sku' => $product2->getSku(), + 'selection_qty' => 1, + 'selection_price_value' => 0, + 'selection_can_change_qty' => 1, + 'delete' => '', + ], +]; + +$bundleProduct = $prepareBundleLinks->execute($bundleProduct, $bundleOptionsData, [$bundleSelectionsData]); +$productRepository->save($bundleProduct); diff --git a/dev/tests/integration/testsuite/Magento/Bundle/_files/bundle_product_dropdown_required_options_rollback.php b/dev/tests/integration/testsuite/Magento/Bundle/_files/bundle_product_dropdown_required_options_rollback.php new file mode 100644 index 0000000000000..ad4464153dbf1 --- /dev/null +++ b/dev/tests/integration/testsuite/Magento/Bundle/_files/bundle_product_dropdown_required_options_rollback.php @@ -0,0 +1,27 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +declare(strict_types=1); + +use Magento\Catalog\Api\ProductRepositoryInterface; +use Magento\Framework\Exception\NoSuchEntityException; + +require __DIR__ . '/../../../Magento/Catalog/_files/product_simple_duplicated_rollback.php'; +require __DIR__ . '/../../../Magento/Catalog/_files/second_product_simple_rollback.php'; + +$registry->unregister('isSecureArea'); +$registry->register('isSecureArea', true); +/** @var ProductRepositoryInterface $productRepository */ +$productRepository = $objectManager->get(ProductRepositoryInterface::class); + +try { + $product = $productRepository->get('bundle-product-dropdown-required-options', false, null, true); + $productRepository->delete($product); +} catch (NoSuchEntityException $e) { + //Product already removed +} + +$registry->unregister('isSecureArea'); +$registry->register('isSecureArea', false); diff --git a/dev/tests/integration/testsuite/Magento/Bundle/_files/bundle_product_multiselect_options.php b/dev/tests/integration/testsuite/Magento/Bundle/_files/bundle_product_multiselect_options.php new file mode 100644 index 0000000000000..29b7710c47040 --- /dev/null +++ b/dev/tests/integration/testsuite/Magento/Bundle/_files/bundle_product_multiselect_options.php @@ -0,0 +1,76 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +declare(strict_types=1); + +use Magento\Bundle\Model\Product\Price; +use Magento\Catalog\Api\ProductRepositoryInterface; +use Magento\Catalog\Model\Product\Attribute\Source\Status; +use Magento\Catalog\Model\Product\Type; +use Magento\Catalog\Model\Product\Type\AbstractType; +use Magento\Catalog\Model\Product\Visibility; +use Magento\Catalog\Model\ProductFactory; +use Magento\Store\Api\WebsiteRepositoryInterface; +use Magento\TestFramework\Bundle\Model\PrepareBundleLinks; +use Magento\TestFramework\Helper\Bootstrap; + +require __DIR__ . '/../../../Magento/Catalog/_files/product_simple_duplicated.php'; +require __DIR__ . '/../../../Magento/Catalog/_files/second_product_simple.php'; + +$objectManager = Bootstrap::getObjectManager(); +/** @var WebsiteRepositoryInterface $websiteRepository */ +$websiteRepository = $objectManager->get(WebsiteRepositoryInterface::class); +$baseWebsiteId = $websiteRepository->get('base')->getId(); +/** @var ProductRepositoryInterface $productRepository */ +$productRepository = $objectManager->get(ProductRepositoryInterface::class); +$productRepository->cleanCache(); +/** @var PrepareBundleLinks $prepareBundleLinks */ +$prepareBundleLinks = $objectManager->get(PrepareBundleLinks::class); +/** @var ProductFactory $productFactory */ +$productFactory = $objectManager->get(ProductFactory::class); +$bundleProduct = $productFactory->create(); +$bundleProduct->setTypeId(Type::TYPE_BUNDLE) + ->setAttributeSetId($product->getDefaultAttributeSetId()) + ->setWebsiteIds([$baseWebsiteId]) + ->setName('Bundle Product Multiselect Options') + ->setSku('bundle-product-multiselect-options') + ->setVisibility(Visibility::VISIBILITY_BOTH) + ->setStatus(Status::STATUS_ENABLED) + ->setStockData(['use_config_manage_stock' => 1, 'qty' => 100, 'is_qty_decimal' => 0, 'is_in_stock' => 1]) + ->setPriceView(0) + ->setSkuType(1) + ->setWeightType(1) + ->setPriceType(Price::PRICE_TYPE_DYNAMIC) + ->setPrice(10.0) + ->setShipmentType(AbstractType::SHIPMENT_TOGETHER); + +$bundleOptionsData = [ + [ + 'title' => 'Multiselect Options', + 'default_title' => 'Multiselect Options', + 'type' => 'multi', + 'required' => 0, + 'delete' => '', + ], +]; +$bundleSelectionsData = [ + [ + 'sku' => $product->getSku(), + 'selection_qty' => 1, + 'selection_price_value' => 0, + 'selection_can_change_qty' => 1, + 'delete' => '', + ], + [ + 'sku' => $product2->getSku(), + 'selection_qty' => 1, + 'selection_price_value' => 0, + 'selection_can_change_qty' => 1, + 'delete' => '', + ], +]; + +$bundleProduct = $prepareBundleLinks->execute($bundleProduct, $bundleOptionsData, [$bundleSelectionsData]); +$productRepository->save($bundleProduct); diff --git a/dev/tests/integration/testsuite/Magento/Bundle/_files/bundle_product_multiselect_options_rollback.php b/dev/tests/integration/testsuite/Magento/Bundle/_files/bundle_product_multiselect_options_rollback.php new file mode 100644 index 0000000000000..c02f218131b20 --- /dev/null +++ b/dev/tests/integration/testsuite/Magento/Bundle/_files/bundle_product_multiselect_options_rollback.php @@ -0,0 +1,27 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +declare(strict_types=1); + +use Magento\Catalog\Api\ProductRepositoryInterface; +use Magento\Framework\Exception\NoSuchEntityException; + +require __DIR__ . '/../../../Magento/Catalog/_files/product_simple_duplicated_rollback.php'; +require __DIR__ . '/../../../Magento/Catalog/_files/second_product_simple_rollback.php'; + +$registry->unregister('isSecureArea'); +$registry->register('isSecureArea', true); +/** @var ProductRepositoryInterface $productRepository */ +$productRepository = $objectManager->get(ProductRepositoryInterface::class); + +try { + $product = $productRepository->get('bundle-product-multiselect-options', false, null, true); + $productRepository->delete($product); +} catch (NoSuchEntityException $e) { + //Product already removed +} + +$registry->unregister('isSecureArea'); +$registry->register('isSecureArea', false); diff --git a/dev/tests/integration/testsuite/Magento/Bundle/_files/bundle_product_multiselect_required_option.php b/dev/tests/integration/testsuite/Magento/Bundle/_files/bundle_product_multiselect_required_option.php new file mode 100644 index 0000000000000..0cde1e65c9e54 --- /dev/null +++ b/dev/tests/integration/testsuite/Magento/Bundle/_files/bundle_product_multiselect_required_option.php @@ -0,0 +1,68 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +declare(strict_types=1); + +use Magento\Bundle\Model\Product\Price; +use Magento\Catalog\Api\ProductRepositoryInterface; +use Magento\Catalog\Model\Product\Attribute\Source\Status; +use Magento\Catalog\Model\Product\Type; +use Magento\Catalog\Model\Product\Type\AbstractType; +use Magento\Catalog\Model\Product\Visibility; +use Magento\Catalog\Model\ProductFactory; +use Magento\Store\Api\WebsiteRepositoryInterface; +use Magento\TestFramework\Bundle\Model\PrepareBundleLinks; +use Magento\TestFramework\Helper\Bootstrap; + +require __DIR__ . '/../../../Magento/Catalog/_files/product_simple_duplicated.php'; + +$objectManager = Bootstrap::getObjectManager(); +/** @var WebsiteRepositoryInterface $websiteRepository */ +$websiteRepository = $objectManager->get(WebsiteRepositoryInterface::class); +$baseWebsiteId = $websiteRepository->get('base')->getId(); +/** @var ProductRepositoryInterface $productRepository */ +$productRepository = $objectManager->get(ProductRepositoryInterface::class); +$productRepository->cleanCache(); +/** @var PrepareBundleLinks $prepareBundleLinks */ +$prepareBundleLinks = $objectManager->get(PrepareBundleLinks::class); +/** @var ProductFactory $productFactory */ +$productFactory = $objectManager->get(ProductFactory::class); +$bundleProduct = $productFactory->create(); +$bundleProduct->setTypeId(Type::TYPE_BUNDLE) + ->setAttributeSetId($product->getDefaultAttributeSetId()) + ->setWebsiteIds([$baseWebsiteId]) + ->setName('Bundle Product Multiselect Required Option') + ->setSku('bundle-product-multiselect-required-option') + ->setVisibility(Visibility::VISIBILITY_BOTH) + ->setStatus(Status::STATUS_ENABLED) + ->setStockData(['use_config_manage_stock' => 1, 'qty' => 100, 'is_qty_decimal' => 0, 'is_in_stock' => 1]) + ->setPriceView(0) + ->setSkuType(1) + ->setWeightType(1) + ->setPriceType(Price::PRICE_TYPE_DYNAMIC) + ->setPrice(10.0) + ->setShipmentType(AbstractType::SHIPMENT_TOGETHER); + +$bundleOptionsData = [ + [ + 'title' => 'Multiselect Options', + 'default_title' => 'Multiselect Options', + 'type' => 'multi', + 'required' => 1, + 'delete' => '', + ], +]; +$bundleSelectionsData = [ + [ + 'sku' => $product->getSku(), + 'selection_qty' => 1, + 'selection_price_value' => 0, + 'selection_can_change_qty' => 1, + 'delete' => '', + ], +]; + +$bundleProduct = $prepareBundleLinks->execute($bundleProduct, $bundleOptionsData, [$bundleSelectionsData]); +$productRepository->save($bundleProduct); diff --git a/dev/tests/integration/testsuite/Magento/Bundle/_files/bundle_product_multiselect_required_option_rollback.php b/dev/tests/integration/testsuite/Magento/Bundle/_files/bundle_product_multiselect_required_option_rollback.php new file mode 100644 index 0000000000000..8e16ecb5f3890 --- /dev/null +++ b/dev/tests/integration/testsuite/Magento/Bundle/_files/bundle_product_multiselect_required_option_rollback.php @@ -0,0 +1,26 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +declare(strict_types=1); + +use Magento\Catalog\Api\ProductRepositoryInterface; +use Magento\Framework\Exception\NoSuchEntityException; + +require __DIR__ . '/../../../Magento/Catalog/_files/product_simple_duplicated_rollback.php'; + +$registry->unregister('isSecureArea'); +$registry->register('isSecureArea', true); +/** @var ProductRepositoryInterface $productRepository */ +$productRepository = $objectManager->get(ProductRepositoryInterface::class); + +try { + $product = $productRepository->get('bundle-product-multiselect-required-option', false, null, true); + $productRepository->delete($product); +} catch (NoSuchEntityException $e) { + //Product already removed +} + +$registry->unregister('isSecureArea'); +$registry->register('isSecureArea', false); diff --git a/dev/tests/integration/testsuite/Magento/Bundle/_files/bundle_product_multiselect_required_options.php b/dev/tests/integration/testsuite/Magento/Bundle/_files/bundle_product_multiselect_required_options.php new file mode 100644 index 0000000000000..f18494d08215c --- /dev/null +++ b/dev/tests/integration/testsuite/Magento/Bundle/_files/bundle_product_multiselect_required_options.php @@ -0,0 +1,76 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +declare(strict_types=1); + +use Magento\Bundle\Model\Product\Price; +use Magento\Catalog\Api\ProductRepositoryInterface; +use Magento\Catalog\Model\Product\Attribute\Source\Status; +use Magento\Catalog\Model\Product\Type; +use Magento\Catalog\Model\Product\Type\AbstractType; +use Magento\Catalog\Model\Product\Visibility; +use Magento\Catalog\Model\ProductFactory; +use Magento\Store\Api\WebsiteRepositoryInterface; +use Magento\TestFramework\Bundle\Model\PrepareBundleLinks; +use Magento\TestFramework\Helper\Bootstrap; + +require __DIR__ . '/../../../Magento/Catalog/_files/product_simple_duplicated.php'; +require __DIR__ . '/../../../Magento/Catalog/_files/second_product_simple.php'; + +$objectManager = Bootstrap::getObjectManager(); +/** @var WebsiteRepositoryInterface $websiteRepository */ +$websiteRepository = $objectManager->get(WebsiteRepositoryInterface::class); +$baseWebsiteId = $websiteRepository->get('base')->getId(); +/** @var ProductRepositoryInterface $productRepository */ +$productRepository = $objectManager->get(ProductRepositoryInterface::class); +$productRepository->cleanCache(); +/** @var PrepareBundleLinks $prepareBundleLinks */ +$prepareBundleLinks = $objectManager->get(PrepareBundleLinks::class); +/** @var ProductFactory $productFactory */ +$productFactory = $objectManager->get(ProductFactory::class); +$bundleProduct = $productFactory->create(); +$bundleProduct->setTypeId(Type::TYPE_BUNDLE) + ->setAttributeSetId($product->getDefaultAttributeSetId()) + ->setWebsiteIds([$baseWebsiteId]) + ->setName('Bundle Product Multiselect Required Options') + ->setSku('bundle-product-multiselect-required-options') + ->setVisibility(Visibility::VISIBILITY_BOTH) + ->setStatus(Status::STATUS_ENABLED) + ->setStockData(['use_config_manage_stock' => 1, 'qty' => 100, 'is_qty_decimal' => 0, 'is_in_stock' => 1]) + ->setPriceView(0) + ->setSkuType(1) + ->setWeightType(1) + ->setPriceType(Price::PRICE_TYPE_DYNAMIC) + ->setPrice(10.0) + ->setShipmentType(AbstractType::SHIPMENT_TOGETHER); + +$bundleOptionsData = [ + [ + 'title' => 'Multiselect Options', + 'default_title' => 'Multiselect Options', + 'type' => 'multi', + 'required' => 1, + 'delete' => '', + ], +]; +$bundleSelectionsData = [ + [ + 'sku' => $product->getSku(), + 'selection_qty' => 1, + 'selection_price_value' => 0, + 'selection_can_change_qty' => 1, + 'delete' => '', + ], + [ + 'sku' => $product2->getSku(), + 'selection_qty' => 1, + 'selection_price_value' => 0, + 'selection_can_change_qty' => 1, + 'delete' => '', + ], +]; + +$bundleProduct = $prepareBundleLinks->execute($bundleProduct, $bundleOptionsData, [$bundleSelectionsData]); +$productRepository->save($bundleProduct); diff --git a/dev/tests/integration/testsuite/Magento/Bundle/_files/bundle_product_multiselect_required_options_rollback.php b/dev/tests/integration/testsuite/Magento/Bundle/_files/bundle_product_multiselect_required_options_rollback.php new file mode 100644 index 0000000000000..bf78eece56938 --- /dev/null +++ b/dev/tests/integration/testsuite/Magento/Bundle/_files/bundle_product_multiselect_required_options_rollback.php @@ -0,0 +1,27 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +declare(strict_types=1); + +use Magento\Catalog\Api\ProductRepositoryInterface; +use Magento\Framework\Exception\NoSuchEntityException; + +require __DIR__ . '/../../../Magento/Catalog/_files/product_simple_duplicated_rollback.php'; +require __DIR__ . '/../../../Magento/Catalog/_files/second_product_simple_rollback.php'; + +$registry->unregister('isSecureArea'); +$registry->register('isSecureArea', true); +/** @var ProductRepositoryInterface $productRepository */ +$productRepository = $objectManager->get(ProductRepositoryInterface::class); + +try { + $product = $productRepository->get('bundle-product-multiselect-required-options', false, null, true); + $productRepository->delete($product); +} catch (NoSuchEntityException $e) { + //Product already removed +} + +$registry->unregister('isSecureArea'); +$registry->register('isSecureArea', false); diff --git a/dev/tests/integration/testsuite/Magento/Bundle/_files/bundle_product_radio_options.php b/dev/tests/integration/testsuite/Magento/Bundle/_files/bundle_product_radio_options.php new file mode 100644 index 0000000000000..23a5713e46eb0 --- /dev/null +++ b/dev/tests/integration/testsuite/Magento/Bundle/_files/bundle_product_radio_options.php @@ -0,0 +1,76 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +declare(strict_types=1); + +use Magento\Bundle\Model\Product\Price; +use Magento\Catalog\Api\ProductRepositoryInterface; +use Magento\Catalog\Model\Product\Attribute\Source\Status; +use Magento\Catalog\Model\Product\Type; +use Magento\Catalog\Model\Product\Type\AbstractType; +use Magento\Catalog\Model\Product\Visibility; +use Magento\Catalog\Model\ProductFactory; +use Magento\Store\Api\WebsiteRepositoryInterface; +use Magento\TestFramework\Bundle\Model\PrepareBundleLinks; +use Magento\TestFramework\Helper\Bootstrap; + +require __DIR__ . '/../../../Magento/Catalog/_files/product_simple_duplicated.php'; +require __DIR__ . '/../../../Magento/Catalog/_files/second_product_simple.php'; + +$objectManager = Bootstrap::getObjectManager(); +/** @var WebsiteRepositoryInterface $websiteRepository */ +$websiteRepository = $objectManager->get(WebsiteRepositoryInterface::class); +$baseWebsiteId = $websiteRepository->get('base')->getId(); +/** @var PrepareBundleLinks $prepareBundleLinks */ +$prepareBundleLinks = $objectManager->get(PrepareBundleLinks::class); +/** @var ProductRepositoryInterface $productRepository */ +$productRepository = $objectManager->get(ProductRepositoryInterface::class); +$productRepository->cleanCache(); +/** @var ProductFactory $productFactory */ +$productFactory = $objectManager->get(ProductFactory::class); +$bundleProduct = $productFactory->create(); +$bundleProduct->setTypeId(Type::TYPE_BUNDLE) + ->setAttributeSetId($product->getDefaultAttributeSetId()) + ->setWebsiteIds([$baseWebsiteId]) + ->setName('Bundle Product Radio Options') + ->setSku('bundle-product-radio-options') + ->setVisibility(Visibility::VISIBILITY_BOTH) + ->setStatus(Status::STATUS_ENABLED) + ->setStockData(['use_config_manage_stock' => 1, 'qty' => 100, 'is_qty_decimal' => 0, 'is_in_stock' => 1]) + ->setPriceView(0) + ->setSkuType(1) + ->setWeightType(1) + ->setPriceType(Price::PRICE_TYPE_DYNAMIC) + ->setPrice(10.0) + ->setShipmentType(AbstractType::SHIPMENT_TOGETHER); + +$bundleOptionsData = [ + [ + 'title' => 'Radio Options', + 'default_title' => 'Radio Options', + 'type' => 'radio', + 'required' => 0, + 'delete' => '', + ], +]; +$bundleSelectionsData = [ + [ + 'sku' => $product->getSku(), + 'selection_qty' => 1, + 'selection_price_value' => 0, + 'selection_can_change_qty' => 1, + 'delete' => '', + ], + [ + 'sku' => $product2->getSku(), + 'selection_qty' => 1, + 'selection_price_value' => 0, + 'selection_can_change_qty' => 1, + 'delete' => '', + ], +]; + +$bundleProduct = $prepareBundleLinks->execute($bundleProduct, $bundleOptionsData, [$bundleSelectionsData]); +$productRepository->save($bundleProduct); diff --git a/dev/tests/integration/testsuite/Magento/Bundle/_files/bundle_product_radio_options_rollback.php b/dev/tests/integration/testsuite/Magento/Bundle/_files/bundle_product_radio_options_rollback.php new file mode 100644 index 0000000000000..5630b2f88ba4d --- /dev/null +++ b/dev/tests/integration/testsuite/Magento/Bundle/_files/bundle_product_radio_options_rollback.php @@ -0,0 +1,27 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +declare(strict_types=1); + +use Magento\Catalog\Api\ProductRepositoryInterface; +use Magento\Framework\Exception\NoSuchEntityException; + +require __DIR__ . '/../../../Magento/Catalog/_files/product_simple_duplicated_rollback.php'; +require __DIR__ . '/../../../Magento/Catalog/_files/second_product_simple_rollback.php'; + +$registry->unregister('isSecureArea'); +$registry->register('isSecureArea', true); +/** @var ProductRepositoryInterface $productRepository */ +$productRepository = $objectManager->get(ProductRepositoryInterface::class); + +try { + $product = $productRepository->get('bundle-product-radio-options', false, null, true); + $productRepository->delete($product); +} catch (NoSuchEntityException $e) { + //Product already removed +} + +$registry->unregister('isSecureArea'); +$registry->register('isSecureArea', false); diff --git a/dev/tests/integration/testsuite/Magento/Bundle/_files/bundle_product_radio_required_option.php b/dev/tests/integration/testsuite/Magento/Bundle/_files/bundle_product_radio_required_option.php new file mode 100644 index 0000000000000..1472986023e21 --- /dev/null +++ b/dev/tests/integration/testsuite/Magento/Bundle/_files/bundle_product_radio_required_option.php @@ -0,0 +1,68 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +declare(strict_types=1); + +use Magento\Bundle\Model\Product\Price; +use Magento\Catalog\Api\ProductRepositoryInterface; +use Magento\Catalog\Model\Product\Attribute\Source\Status; +use Magento\Catalog\Model\Product\Type; +use Magento\Catalog\Model\Product\Type\AbstractType; +use Magento\Catalog\Model\Product\Visibility; +use Magento\Catalog\Model\ProductFactory; +use Magento\Store\Api\WebsiteRepositoryInterface; +use Magento\TestFramework\Bundle\Model\PrepareBundleLinks; +use Magento\TestFramework\Helper\Bootstrap; + +require __DIR__ . '/../../../Magento/Catalog/_files/product_simple_duplicated.php'; + +$objectManager = Bootstrap::getObjectManager(); +/** @var WebsiteRepositoryInterface $websiteRepository */ +$websiteRepository = $objectManager->get(WebsiteRepositoryInterface::class); +$baseWebsiteId = $websiteRepository->get('base')->getId(); +/** @var ProductRepositoryInterface $productRepository */ +$productRepository = $objectManager->get(ProductRepositoryInterface::class); +$productRepository->cleanCache(); +/** @var PrepareBundleLinks $prepareBundleLinks */ +$prepareBundleLinks = $objectManager->get(PrepareBundleLinks::class); +/** @var ProductFactory $productFactory */ +$productFactory = $objectManager->get(ProductFactory::class); +$bundleProduct = $productFactory->create(); +$bundleProduct->setTypeId(Type::TYPE_BUNDLE) + ->setAttributeSetId($product->getDefaultAttributeSetId()) + ->setWebsiteIds([$baseWebsiteId]) + ->setName('Bundle Product Radio Required Option') + ->setSku('bundle-product-radio-required-option') + ->setVisibility(Visibility::VISIBILITY_BOTH) + ->setStatus(Status::STATUS_ENABLED) + ->setStockData(['use_config_manage_stock' => 1, 'qty' => 100, 'is_qty_decimal' => 0, 'is_in_stock' => 1]) + ->setPriceView(0) + ->setSkuType(1) + ->setWeightType(1) + ->setPriceType(Price::PRICE_TYPE_DYNAMIC) + ->setPrice(10.0) + ->setShipmentType(AbstractType::SHIPMENT_TOGETHER); + +$bundleOptionsData = [ + [ + 'title' => 'Radio Options', + 'default_title' => 'Radio Options', + 'type' => 'radio', + 'required' => 1, + 'delete' => '', + ], +]; +$bundleSelectionsData = [ + [ + 'sku' => $product->getSku(), + 'selection_qty' => 1, + 'selection_price_value' => 0, + 'selection_can_change_qty' => 1, + 'delete' => '', + ], +]; + +$bundleProduct = $prepareBundleLinks->execute($bundleProduct, $bundleOptionsData, [$bundleSelectionsData]); +$productRepository->save($bundleProduct); diff --git a/dev/tests/integration/testsuite/Magento/Bundle/_files/bundle_product_radio_required_option_rollback.php b/dev/tests/integration/testsuite/Magento/Bundle/_files/bundle_product_radio_required_option_rollback.php new file mode 100644 index 0000000000000..9a44ccbca6110 --- /dev/null +++ b/dev/tests/integration/testsuite/Magento/Bundle/_files/bundle_product_radio_required_option_rollback.php @@ -0,0 +1,26 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +declare(strict_types=1); + +use Magento\Catalog\Api\ProductRepositoryInterface; +use Magento\Framework\Exception\NoSuchEntityException; + +require __DIR__ . '/../../../Magento/Catalog/_files/product_simple_duplicated_rollback.php'; + +$registry->unregister('isSecureArea'); +$registry->register('isSecureArea', true); +/** @var ProductRepositoryInterface $productRepository */ +$productRepository = $objectManager->get(ProductRepositoryInterface::class); + +try { + $product = $productRepository->get('bundle-product-radio-required-option', false, null, true); + $productRepository->delete($product); +} catch (NoSuchEntityException $e) { + //Product already removed +} + +$registry->unregister('isSecureArea'); +$registry->register('isSecureArea', false); diff --git a/dev/tests/integration/testsuite/Magento/Bundle/_files/bundle_product_radio_required_options.php b/dev/tests/integration/testsuite/Magento/Bundle/_files/bundle_product_radio_required_options.php new file mode 100644 index 0000000000000..cb703902a0b7a --- /dev/null +++ b/dev/tests/integration/testsuite/Magento/Bundle/_files/bundle_product_radio_required_options.php @@ -0,0 +1,77 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +declare(strict_types=1); + +use Magento\Bundle\Model\Product\Price; +use Magento\Catalog\Api\ProductRepositoryInterface; +use Magento\Catalog\Model\Product\Attribute\Source\Status; +use Magento\Catalog\Model\Product\Type; +use Magento\Catalog\Model\Product\Type\AbstractType; +use Magento\Catalog\Model\Product\Visibility; +use Magento\Catalog\Model\ProductFactory; +use Magento\Store\Api\WebsiteRepositoryInterface; +use Magento\TestFramework\Bundle\Model\PrepareBundleLinks; +use Magento\TestFramework\Helper\Bootstrap; + +require __DIR__ . '/../../../Magento/Catalog/_files/product_simple_duplicated.php'; +require __DIR__ . '/../../../Magento/Catalog/_files/second_product_simple.php'; + +$objectManager = Bootstrap::getObjectManager(); +/** @var WebsiteRepositoryInterface $websiteRepository */ +$websiteRepository = $objectManager->get(WebsiteRepositoryInterface::class); +$baseWebsiteId = $websiteRepository->get('base')->getId(); +/** @var PrepareBundleLinks $prepareBundleLinks */ +$prepareBundleLinks = $objectManager->get(PrepareBundleLinks::class); +/** @var ProductRepositoryInterface $productRepository */ +$productRepository = $objectManager->get(ProductRepositoryInterface::class); +$productRepository->cleanCache(); +/** @var ProductFactory $productFactory */ +$productFactory = $objectManager->get(ProductFactory::class); +$bundleProduct = $productFactory->create(); +$bundleProduct->setTypeId(Type::TYPE_BUNDLE) + ->setAttributeSetId($product->getDefaultAttributeSetId()) + ->setWebsiteIds([$baseWebsiteId]) + ->setName('Bundle Product Radio Required Options') + ->setSku('bundle-product-radio-required-options') + ->setVisibility(Visibility::VISIBILITY_BOTH) + ->setStatus(Status::STATUS_ENABLED) + ->setStockData(['use_config_manage_stock' => 1, 'qty' => 100, 'is_qty_decimal' => 0, 'is_in_stock' => 1]) + ->setPriceView(0) + ->setSkuType(1) + ->setWeightType(1) + ->setPriceType(Price::PRICE_TYPE_DYNAMIC) + ->setPrice(10.0) + ->setShipmentType(AbstractType::SHIPMENT_TOGETHER); + +$bundleOptionsData = [ + [ + 'title' => 'Radio Options', + 'default_title' => 'Radio Options', + 'type' => 'radio', + 'required' => 1, + 'delete' => '', + ], +]; +$bundleSelectionsData = [ + [ + 'sku' => $product->getSku(), + 'selection_qty' => 1, + 'selection_price_value' => 0, + 'selection_can_change_qty' => 1, + 'delete' => '', + ], + [ + 'sku' => $product2->getSku(), + 'selection_qty' => 1, + 'selection_price_value' => 0, + 'selection_can_change_qty' => 1, + 'delete' => '', + 'option_id' => 1, + ], +]; + +$bundleProduct = $prepareBundleLinks->execute($bundleProduct, $bundleOptionsData, [$bundleSelectionsData]); +$productRepository->save($bundleProduct); diff --git a/dev/tests/integration/testsuite/Magento/Bundle/_files/bundle_product_radio_required_options_rollback.php b/dev/tests/integration/testsuite/Magento/Bundle/_files/bundle_product_radio_required_options_rollback.php new file mode 100644 index 0000000000000..8536a76979430 --- /dev/null +++ b/dev/tests/integration/testsuite/Magento/Bundle/_files/bundle_product_radio_required_options_rollback.php @@ -0,0 +1,27 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +declare(strict_types=1); + +use Magento\Catalog\Api\ProductRepositoryInterface; +use Magento\Framework\Exception\NoSuchEntityException; + +require __DIR__ . '/../../../Magento/Catalog/_files/product_simple_duplicated_rollback.php'; +require __DIR__ . '/../../../Magento/Catalog/_files/second_product_simple_rollback.php'; + +$registry->unregister('isSecureArea'); +$registry->register('isSecureArea', true); +/** @var ProductRepositoryInterface $productRepository */ +$productRepository = $objectManager->get(ProductRepositoryInterface::class); + +try { + $product = $productRepository->get('bundle-product-radio-required-options', false, null, true); + $productRepository->delete($product); +} catch (NoSuchEntityException $e) { + //Product already removed +} + +$registry->unregister('isSecureArea'); +$registry->register('isSecureArea', false); diff --git a/dev/tests/integration/testsuite/Magento/Bundle/_files/dynamic_and_fixed_bundle_products_in_category.php b/dev/tests/integration/testsuite/Magento/Bundle/_files/dynamic_and_fixed_bundle_products_in_category.php new file mode 100644 index 0000000000000..9a6cae37fbe25 --- /dev/null +++ b/dev/tests/integration/testsuite/Magento/Bundle/_files/dynamic_and_fixed_bundle_products_in_category.php @@ -0,0 +1,23 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +declare(strict_types=1); + +use Magento\Catalog\Api\CategoryLinkManagementInterface; +use Magento\Catalog\Helper\DefaultCategory; + +require __DIR__ . '/product.php'; +require __DIR__ . '/bundle_product_dropdown_options.php'; +require __DIR__ . '/../../Catalog/_files/category.php'; + +/** @var CategoryLinkManagementInterface $categoryLinkManagement */ +$categoryLinkManagement = $objectManager->create(CategoryLinkManagementInterface::class); +/** @var DefaultCategory $categoryHelper */ +$categoryHelper = $objectManager->get(DefaultCategory::class); +$categoryLinkManagement->assignProductToCategories('bundle-product', [2, $category->getId()]); +$categoryLinkManagement->assignProductToCategories( + 'bundle-product-dropdown-options', + [$categoryHelper->getId(), $category->getId()] +); diff --git a/dev/tests/integration/testsuite/Magento/Bundle/_files/dynamic_and_fixed_bundle_products_in_category_rollback.php b/dev/tests/integration/testsuite/Magento/Bundle/_files/dynamic_and_fixed_bundle_products_in_category_rollback.php new file mode 100644 index 0000000000000..58eb8cacde815 --- /dev/null +++ b/dev/tests/integration/testsuite/Magento/Bundle/_files/dynamic_and_fixed_bundle_products_in_category_rollback.php @@ -0,0 +1,10 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +declare(strict_types=1); + +require __DIR__ . '/product_rollback.php'; +require __DIR__ . '/bundle_product_dropdown_options_rollback.php'; +require __DIR__ . '/../../Catalog/_files/category_rollback.php'; diff --git a/dev/tests/integration/testsuite/Magento/Bundle/_files/dynamic_bundle_product_multiselect_option.php b/dev/tests/integration/testsuite/Magento/Bundle/_files/dynamic_bundle_product_multiselect_option.php new file mode 100644 index 0000000000000..c9e2bfb74090b --- /dev/null +++ b/dev/tests/integration/testsuite/Magento/Bundle/_files/dynamic_bundle_product_multiselect_option.php @@ -0,0 +1,97 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +declare(strict_types=1); + +use Magento\Bundle\Api\Data\LinkInterfaceFactory; +use Magento\Bundle\Api\Data\OptionInterfaceFactory; +use Magento\Bundle\Model\Product\Price; +use Magento\Catalog\Api\Data\ProductExtensionFactory; +use Magento\Catalog\Model\Product\Attribute\Source\Status; +use Magento\Catalog\Model\Product\Type; +use Magento\Catalog\Model\Product\Type\AbstractType; +use Magento\Catalog\Model\Product\Visibility; + +require __DIR__ . '/multiple_products.php'; + +/** @var ProductExtensionFactory $extensionAttributesFactory */ +$extensionAttributesFactory = $objectManager->get(ProductExtensionFactory::class); +/** @var OptionInterfaceFactory $optionFactory */ +$optionFactory = $objectManager->get(OptionInterfaceFactory::class); +/** @var LinkInterfaceFactory $linkFactory */ +$linkFactory = $objectManager->get(LinkInterfaceFactory::class); + +$bundleProduct = $productFactory->create(); +$bundleProduct->setTypeId(Type::TYPE_BUNDLE) + ->setAttributeSetId($bundleProduct->getDefaultAttributeSetId()) + ->setWebsiteIds([$defaultWebsiteId]) + ->setName('Bundle Product') + ->setSku('bundle_product') + ->setVisibility(Visibility::VISIBILITY_BOTH) + ->setStatus(Status::STATUS_ENABLED) + ->setStockData( + [ + 'use_config_manage_stock' => 1, + 'qty' => 100, + 'is_qty_decimal' => 0, + 'is_in_stock' => 1, + ] + ) + ->setSkuType(0) + ->setPriceView(0) + ->setPriceType(Price::PRICE_TYPE_DYNAMIC) + ->setWeightType(0) + ->setShipmentType(AbstractType::SHIPMENT_TOGETHER) + ->setBundleOptionsData( + [ + [ + 'title' => 'Option 1', + 'default_title' => 'Option 1', + 'type' => 'multi', + 'required' => 1, + ], + ] + )->setBundleSelectionsData( + [ + [ + [ + 'product_id' => $product->getId(), + 'sku' => $product->getSku(), + 'selection_qty' => 1, + 'selection_can_change_qty' => 1, + ], + [ + 'product_id' => $product2->getId(), + 'sku' => $product2->getSku(), + 'selection_qty' => 1, + 'selection_can_change_qty' => 1, + ], + [ + 'product_id' => $product3->getId(), + 'sku' => $product3->getSku(), + 'selection_qty' => 1, + 'selection_can_change_qty' => 1, + ], + ] + ] + ); + +$options = []; +foreach ($bundleProduct->getBundleOptionsData() as $key => $optionData) { + $option = $optionFactory->create(['data' => $optionData]); + $option->setSku($bundleProduct->getSku()); + $option->setOptionId(null); + $links = []; + foreach ($bundleProduct->getBundleSelectionsData()[$key] as $linkData) { + $link = $linkFactory->create(['data' => $linkData]); + $links[] = $link; + } + $option->setProductLinks($links); + $options[] = $option; +} +$extensionAttributes = $bundleProduct->getExtensionAttributes() ?: $extensionAttributesFactory->create(); +$extensionAttributes->setBundleProductOptions($options); +$bundleProduct->setExtensionAttributes($extensionAttributes); +$productRepository->save($bundleProduct); diff --git a/dev/tests/integration/testsuite/Magento/Bundle/_files/dynamic_bundle_product_multiselect_option_rollback.php b/dev/tests/integration/testsuite/Magento/Bundle/_files/dynamic_bundle_product_multiselect_option_rollback.php new file mode 100644 index 0000000000000..25ebbd14bfa39 --- /dev/null +++ b/dev/tests/integration/testsuite/Magento/Bundle/_files/dynamic_bundle_product_multiselect_option_rollback.php @@ -0,0 +1,22 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +declare(strict_types=1); + +use Magento\Framework\Exception\NoSuchEntityException; + +require __DIR__ . '/multiple_products_rollback.php'; + +$registry->unregister('isSecureArea'); +$registry->register('isSecureArea', true); + +try { + $product = $productRepository->get('bundle_product', false, null, true); + $productRepository->delete($product); +} catch (NoSuchEntityException $e) { +} + +$registry->unregister('isSecureArea'); +$registry->register('isSecureArea', false); diff --git a/dev/tests/integration/testsuite/Magento/Bundle/_files/dynamic_bundle_product_with_catalog_rule.php b/dev/tests/integration/testsuite/Magento/Bundle/_files/dynamic_bundle_product_with_catalog_rule.php new file mode 100644 index 0000000000000..44d995a4b8baf --- /dev/null +++ b/dev/tests/integration/testsuite/Magento/Bundle/_files/dynamic_bundle_product_with_catalog_rule.php @@ -0,0 +1,117 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +declare(strict_types=1); + +use Magento\Bundle\Model\Product\Price; +use Magento\Catalog\Model\Product\Attribute\Source\Status; +use Magento\Catalog\Model\Product\Type; +use Magento\Catalog\Model\Product\Type\AbstractType; +use Magento\Catalog\Model\Product\Visibility; +use Magento\CatalogRule\Api\CatalogRuleRepositoryInterface; +use Magento\CatalogRule\Api\Data\RuleInterface; +use Magento\CatalogRule\Api\Data\RuleInterfaceFactory; +use Magento\CatalogRule\Model\Indexer\IndexBuilder; +use Magento\CatalogRule\Model\Rule\Condition\Combine; +use Magento\CatalogRule\Model\Rule\Condition\Product; +use Magento\Customer\Model\Group; +use Magento\TestFramework\Bundle\Model\PrepareBundleLinks; + +require __DIR__ . '/../../../Magento/Catalog/_files/category_with_different_price_products.php'; + +/** @var PrepareBundleLinks $prepareBundleLinks */ +$prepareBundleLinks = $objectManager->get(PrepareBundleLinks::class); +/** @var RuleInterfaceFactory $catalogRuleFactory */ +$catalogRuleFactory = $objectManager->get(RuleInterfaceFactory::class); +/** @var CatalogRuleRepositoryInterface $catalogRuleRepository */ +$catalogRuleRepository = $objectManager->get(CatalogRuleRepositoryInterface::class); +/** @var IndexBuilder $indexBuilder */ +$indexBuilder = $objectManager->get(IndexBuilder::class); +$defaultWebsiteId = $storeManager->getWebsite('base')->getId(); + +$category = $categoryFactory->create(); +$category->isObjectNew(true); +$category->setName('Category with bundle product and rule') + ->setParentId($categoryHelper->getId()) + ->setIsActive(true) + ->setPosition(1); +$category = $categoryRepository->save($category); + +$bundleProduct = $productFactory->create(); +$bundleProduct->setTypeId(Type::TYPE_BUNDLE) + ->setAttributeSetId($bundleProduct->getDefaultAttributeSetId()) + ->setWebsiteIds([$defaultWebsiteId]) + ->setName('Bundle Product') + ->setSku('dynamic_bundle_product_with_catalog_rule') + ->setVisibility(Visibility::VISIBILITY_BOTH) + ->setStatus(Status::STATUS_ENABLED) + ->setStockData( + [ + 'use_config_manage_stock' => 1, + 'qty' => 100, + 'is_qty_decimal' => 0, + 'is_in_stock' => 1, + ] + ) + ->setSkuType(0) + ->setPriceView(0) + ->setPriceType(Price::PRICE_TYPE_DYNAMIC) + ->setPrice(null) + ->setWeightType(0) + ->setCategoryIds([$category->getId()]) + ->setShipmentType(AbstractType::SHIPMENT_TOGETHER); + +$bundleOptionsData = [ + [ + 'title' => 'Option 1', + 'default_title' => 'Option 1', + 'type' => 'select', + 'required' => 1, + ], +]; +$bundleSelectionsData = [ + [ + [ + 'sku' => $product->getSku(), + 'selection_qty' => 1, + 'selection_price_value' => 0, + 'selection_can_change_qty' => 1, + ], + [ + 'sku' => $product2->getSku(), + 'selection_qty' => 1, + 'selection_price_value' => 0, + 'selection_can_change_qty' => 1, + ], + ] +]; +$bundleProduct = $prepareBundleLinks->execute($bundleProduct, $bundleOptionsData, $bundleSelectionsData); +$productRepository->save($bundleProduct); + +$ruleData = [ + RuleInterface::NAME => 'Rule for bundle product', + RuleInterface::IS_ACTIVE => 1, + 'website_ids' => [$defaultWebsiteId], + 'customer_group_ids' => Group::NOT_LOGGED_IN_ID, + RuleInterface::DISCOUNT_AMOUNT => 50, + RuleInterface::SIMPLE_ACTION => 'by_percent', + 'conditions' => [ + '1' => [ + 'type' => Combine::class, + 'aggregator' => 'all', + 'value' => '1', + ], + '1--1' => [ + 'type' => Product::class, + 'attribute' => 'category_ids', + 'operator' => '==', + 'value' => $category->getId(), + ], + ], +]; +$catalogRule = $catalogRuleFactory->create(); +$catalogRule->loadPost($ruleData); +$catalogRuleRepository->save($catalogRule); +$indexBuilder->reindexFull(); diff --git a/dev/tests/integration/testsuite/Magento/Bundle/_files/dynamic_bundle_product_with_catalog_rule_rollback.php b/dev/tests/integration/testsuite/Magento/Bundle/_files/dynamic_bundle_product_with_catalog_rule_rollback.php new file mode 100644 index 0000000000000..cf3fd4a9e14ff --- /dev/null +++ b/dev/tests/integration/testsuite/Magento/Bundle/_files/dynamic_bundle_product_with_catalog_rule_rollback.php @@ -0,0 +1,57 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +declare(strict_types=1); + +use Magento\CatalogRule\Api\CatalogRuleRepositoryInterface; +use Magento\CatalogRule\Model\Indexer\IndexBuilder; +use Magento\CatalogRule\Model\ResourceModel\Rule\CollectionFactory; +use Magento\Framework\Exception\NoSuchEntityException; +use Magento\TestFramework\Catalog\Model\GetCategoryByName; + +require __DIR__ . '/../../../Magento/Catalog/_files/category_with_different_price_products_rollback.php'; + +/** @var GetCategoryByName $getCategoryByName */ +$getCategoryByName = $objectManager->create(GetCategoryByName::class); +/** @var CollectionFactory $ruleCollectionFactory */ +$ruleCollectionFactory = $objectManager->get(CollectionFactory::class); +/** @var CatalogRuleRepositoryInterface $ruleRepository */ +$ruleRepository = $objectManager->get(CatalogRuleRepositoryInterface::class); +/** @var IndexBuilder $indexBuilder */ +$indexBuilder = $objectManager->get(IndexBuilder::class); + +$registry->unregister('isSecureArea'); +$registry->register('isSecureArea', true); + +try { + $product = $productRepository->get('dynamic_bundle_product_with_catalog_rule', false, null, true); + $productRepository->delete($product); +} catch (NoSuchEntityException $e) { + //product already deleted. +} + +$category = $getCategoryByName->execute('Category with bundle product and rule'); + +try { + $categoryRepository->delete($category); +} catch (NoSuchEntityException $e) { + //category already deleted. +} + +$ruleCollection = $ruleCollectionFactory->create(); +$ruleCollection->addFieldToFilter('name', 'Rule for bundle product'); +$ruleCollection->setPageSize(1); +$catalogRule = $ruleCollection->getFirstItem(); + +try { + $ruleRepository->delete($catalogRule); +} catch (Exception $ex) { + //Nothing to remove +} + +$indexBuilder->reindexFull(); + +$registry->unregister('isSecureArea'); +$registry->register('isSecureArea', false); diff --git a/dev/tests/integration/testsuite/Magento/Bundle/_files/dynamic_bundle_product_with_special_price.php b/dev/tests/integration/testsuite/Magento/Bundle/_files/dynamic_bundle_product_with_special_price.php new file mode 100644 index 0000000000000..e5fef4e581595 --- /dev/null +++ b/dev/tests/integration/testsuite/Magento/Bundle/_files/dynamic_bundle_product_with_special_price.php @@ -0,0 +1,70 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +declare(strict_types=1); + +use Magento\Bundle\Model\Product\Price; +use Magento\Catalog\Model\Product\Attribute\Source\Status; +use Magento\Catalog\Model\Product\Type; +use Magento\Catalog\Model\Product\Type\AbstractType; +use Magento\Catalog\Model\Product\Visibility; +use Magento\TestFramework\Bundle\Model\PrepareBundleLinks; + +require __DIR__ . '/../../../Magento/Catalog/_files/category_with_different_price_products.php'; + +/** @var PrepareBundleLinks $prepareBundleLinks */ +$prepareBundleLinks = $objectManager->get(PrepareBundleLinks::class); +$defaultWebsiteId = $storeManager->getWebsite('base')->getId(); + +$bundleProduct = $productFactory->create(); +$bundleProduct->setTypeId(Type::TYPE_BUNDLE) + ->setAttributeSetId($bundleProduct->getDefaultAttributeSetId()) + ->setWebsiteIds([$defaultWebsiteId]) + ->setName('Bundle Product') + ->setSku('dynamic_bundle_product_with_special_price') + ->setVisibility(Visibility::VISIBILITY_BOTH) + ->setStatus(Status::STATUS_ENABLED) + ->setStockData( + [ + 'use_config_manage_stock' => 1, + 'qty' => 100, + 'is_qty_decimal' => 0, + 'is_in_stock' => 1, + ] + ) + ->setSkuType(0) + ->setPriceView(0) + ->setPriceType(Price::PRICE_TYPE_DYNAMIC) + ->setPrice(null) + ->setSpecialPrice(75) + ->setWeightType(0) + ->setShipmentType(AbstractType::SHIPMENT_TOGETHER); + +$bundleOptionsData = [ + [ + 'title' => 'Option 1', + 'default_title' => 'Option 1', + 'type' => 'select', + 'required' => 1, + ], +]; +$bundleSelectionsData = [ + [ + [ + 'sku' => $product->getSku(), + 'selection_qty' => 1, + 'selection_price_value' => 0, + 'selection_can_change_qty' => 1, + ], + [ + 'sku' => $product2->getSku(), + 'selection_qty' => 1, + 'selection_price_value' => 0, + 'selection_can_change_qty' => 1, + ], + ] +]; +$bundleProduct = $prepareBundleLinks->execute($bundleProduct, $bundleOptionsData, $bundleSelectionsData); +$productRepository->save($bundleProduct); diff --git a/dev/tests/integration/testsuite/Magento/Bundle/_files/dynamic_bundle_product_with_special_price_rollback.php b/dev/tests/integration/testsuite/Magento/Bundle/_files/dynamic_bundle_product_with_special_price_rollback.php new file mode 100644 index 0000000000000..0ec7cc98131d4 --- /dev/null +++ b/dev/tests/integration/testsuite/Magento/Bundle/_files/dynamic_bundle_product_with_special_price_rollback.php @@ -0,0 +1,23 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +declare(strict_types=1); + +use Magento\Framework\Exception\NoSuchEntityException; + +require __DIR__ . '/../../../Magento/Catalog/_files/category_with_different_price_products_rollback.php'; + +$registry->unregister('isSecureArea'); +$registry->register('isSecureArea', true); + +try { + $product = $productRepository->get('dynamic_bundle_product_with_special_price', false, null, true); + $productRepository->delete($product); +} catch (NoSuchEntityException $e) { + //product already deleted. +} + +$registry->unregister('isSecureArea'); +$registry->register('isSecureArea', false); diff --git a/dev/tests/integration/testsuite/Magento/Bundle/_files/dynamic_bundle_product_with_tier_price.php b/dev/tests/integration/testsuite/Magento/Bundle/_files/dynamic_bundle_product_with_tier_price.php new file mode 100644 index 0000000000000..b1cdfddd061bb --- /dev/null +++ b/dev/tests/integration/testsuite/Magento/Bundle/_files/dynamic_bundle_product_with_tier_price.php @@ -0,0 +1,94 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +declare(strict_types=1); + +use Magento\Bundle\Model\Product\Price; +use Magento\Catalog\Api\Data\ProductTierPriceExtensionFactory; +use Magento\Catalog\Api\Data\ProductTierPriceInterfaceFactory; +use Magento\Catalog\Model\Product\Attribute\Source\Status; +use Magento\Catalog\Model\Product\Type; +use Magento\Catalog\Model\Product\Type\AbstractType; +use Magento\Catalog\Model\Product\Visibility; +use Magento\TestFramework\Bundle\Model\PrepareBundleLinks; +use Magento\Customer\Model\Group; + +require __DIR__ . '/../../../Magento/Catalog/_files/category_with_different_price_products.php'; + +/** @var PrepareBundleLinks $prepareBundleLinks */ +$prepareBundleLinks = $objectManager->get(PrepareBundleLinks::class); +/** @var ProductTierPriceInterfaceFactory $tierPriceFactory */ +$tierPriceFactory = $objectManager->get(ProductTierPriceInterfaceFactory::class); +/** @var $tierPriceExtensionAttributesFactory */ +$tierPriceExtensionAttributesFactory = $objectManager->get(ProductTierPriceExtensionFactory::class); +$defaultWebsiteId = $storeManager->getWebsite('base')->getId(); + +$bundleProduct = $productFactory->create(); +$bundleProduct->setTypeId(Type::TYPE_BUNDLE) + ->setAttributeSetId($bundleProduct->getDefaultAttributeSetId()) + ->setWebsiteIds([$defaultWebsiteId]) + ->setName('Bundle Product') + ->setSku('dynamic_bundle_product_with_tier_price') + ->setVisibility(Visibility::VISIBILITY_BOTH) + ->setStatus(Status::STATUS_ENABLED) + ->setStockData( + [ + 'use_config_manage_stock' => 1, + 'qty' => 100, + 'is_qty_decimal' => 0, + 'is_in_stock' => 1, + ] + ) + ->setSkuType(0) + ->setPriceView(0) + ->setPriceType(Price::PRICE_TYPE_DYNAMIC) + ->setPrice(null) + ->setWeightType(0) + ->setShipmentType(AbstractType::SHIPMENT_TOGETHER); + +$bundleOptionsData = [ + [ + 'title' => 'Option 1', + 'default_title' => 'Option 1', + 'type' => 'select', + 'required' => 1, + ], +]; +$bundleSelectionsData = [ + [ + [ + 'sku' => $product->getSku(), + 'selection_qty' => 1, + 'selection_price_value' => 0, + 'selection_can_change_qty' => 1, + ], + [ + 'sku' => $product2->getSku(), + 'selection_qty' => 1, + 'selection_price_value' => 0, + 'selection_can_change_qty' => 1, + ], + ] +]; +$bundleProduct = $prepareBundleLinks->execute($bundleProduct, $bundleOptionsData, $bundleSelectionsData); + +$tierPriceExtensionAttribute = $tierPriceExtensionAttributesFactory->create( + [ + 'data' => [ + 'website_id' => 0, + 'percentage_value' => 25, + ] + ] +); +$tierPrices[] = $tierPriceFactory->create( + [ + 'data' => [ + 'customer_group_id' => Group::CUST_GROUP_ALL, + 'qty' => 1, + ] + ] +)->setExtensionAttributes($tierPriceExtensionAttribute); +$bundleProduct->setTierPrices($tierPrices); +$productRepository->save($bundleProduct); diff --git a/dev/tests/integration/testsuite/Magento/Bundle/_files/dynamic_bundle_product_with_tier_price_rollback.php b/dev/tests/integration/testsuite/Magento/Bundle/_files/dynamic_bundle_product_with_tier_price_rollback.php new file mode 100644 index 0000000000000..247e2400986d7 --- /dev/null +++ b/dev/tests/integration/testsuite/Magento/Bundle/_files/dynamic_bundle_product_with_tier_price_rollback.php @@ -0,0 +1,23 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +declare(strict_types=1); + +use Magento\Framework\Exception\NoSuchEntityException; + +require __DIR__ . '/../../../Magento/Catalog/_files/category_with_different_price_products_rollback.php'; + +$registry->unregister('isSecureArea'); +$registry->register('isSecureArea', true); + +try { + $product = $productRepository->get('dynamic_bundle_product_with_tier_price', false, null, true); + $productRepository->delete($product); +} catch (NoSuchEntityException $e) { + //product already deleted. +} + +$registry->unregister('isSecureArea'); +$registry->register('isSecureArea', false); diff --git a/dev/tests/integration/testsuite/Magento/Bundle/_files/dynamic_bundle_product_without_discounts.php b/dev/tests/integration/testsuite/Magento/Bundle/_files/dynamic_bundle_product_without_discounts.php new file mode 100644 index 0000000000000..4b0b81f5d9f04 --- /dev/null +++ b/dev/tests/integration/testsuite/Magento/Bundle/_files/dynamic_bundle_product_without_discounts.php @@ -0,0 +1,69 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +declare(strict_types=1); + +use Magento\Bundle\Model\Product\Price; +use Magento\Catalog\Model\Product\Attribute\Source\Status; +use Magento\Catalog\Model\Product\Type; +use Magento\Catalog\Model\Product\Type\AbstractType; +use Magento\Catalog\Model\Product\Visibility; +use Magento\TestFramework\Bundle\Model\PrepareBundleLinks; + +require __DIR__ . '/../../../Magento/Catalog/_files/category_with_different_price_products.php'; + +/** @var PrepareBundleLinks $prepareBundleLinks */ +$prepareBundleLinks = $objectManager->get(PrepareBundleLinks::class); +$defaultWebsiteId = $storeManager->getWebsite('base')->getId(); + +$bundleProduct = $productFactory->create(); +$bundleProduct->setTypeId(Type::TYPE_BUNDLE) + ->setAttributeSetId($bundleProduct->getDefaultAttributeSetId()) + ->setWebsiteIds([$defaultWebsiteId]) + ->setName('Bundle Product') + ->setSku('dynamic_bundle_product_without_discounts') + ->setVisibility(Visibility::VISIBILITY_BOTH) + ->setStatus(Status::STATUS_ENABLED) + ->setStockData( + [ + 'use_config_manage_stock' => 1, + 'qty' => 100, + 'is_qty_decimal' => 0, + 'is_in_stock' => 1, + ] + ) + ->setSkuType(0) + ->setPriceView(0) + ->setPriceType(Price::PRICE_TYPE_DYNAMIC) + ->setPrice(null) + ->setWeightType(0) + ->setShipmentType(AbstractType::SHIPMENT_TOGETHER); + +$bundleOptionsData = [ + [ + 'title' => 'Option 1', + 'default_title' => 'Option 1', + 'type' => 'select', + 'required' => 1, + ], +]; +$bundleSelectionsData = [ + [ + [ + 'sku' => $product->getSku(), + 'selection_qty' => 1, + 'selection_price_value' => 0, + 'selection_can_change_qty' => 1, + ], + [ + 'sku' => $product2->getSku(), + 'selection_qty' => 1, + 'selection_price_value' => 0, + 'selection_can_change_qty' => 1, + ], + ] +]; +$bundleProduct = $prepareBundleLinks->execute($bundleProduct, $bundleOptionsData, $bundleSelectionsData); +$productRepository->save($bundleProduct); diff --git a/dev/tests/integration/testsuite/Magento/Bundle/_files/dynamic_bundle_product_without_discounts_rollback.php b/dev/tests/integration/testsuite/Magento/Bundle/_files/dynamic_bundle_product_without_discounts_rollback.php new file mode 100644 index 0000000000000..cb785aee1ccd7 --- /dev/null +++ b/dev/tests/integration/testsuite/Magento/Bundle/_files/dynamic_bundle_product_without_discounts_rollback.php @@ -0,0 +1,23 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +declare(strict_types=1); + +use Magento\Framework\Exception\NoSuchEntityException; + +require __DIR__ . '/../../../Magento/Catalog/_files/category_with_different_price_products_rollback.php'; + +$registry->unregister('isSecureArea'); +$registry->register('isSecureArea', true); + +try { + $product = $productRepository->get('dynamic_bundle_product_without_discounts', false, null, true); + $productRepository->delete($product); +} catch (NoSuchEntityException $e) { + //product already deleted. +} + +$registry->unregister('isSecureArea'); +$registry->register('isSecureArea', false); diff --git a/dev/tests/integration/testsuite/Magento/Bundle/_files/fixed_bundle_product_with_special_price.php b/dev/tests/integration/testsuite/Magento/Bundle/_files/fixed_bundle_product_with_special_price.php new file mode 100644 index 0000000000000..d7caba1dec4aa --- /dev/null +++ b/dev/tests/integration/testsuite/Magento/Bundle/_files/fixed_bundle_product_with_special_price.php @@ -0,0 +1,77 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +declare(strict_types=1); + +use Magento\Bundle\Model\Product\Price; +use Magento\Catalog\Model\Product\Attribute\Source\Status; +use Magento\Catalog\Model\Product\Type; +use Magento\Catalog\Model\Product\Type\AbstractType; +use Magento\Catalog\Model\Product\Visibility; +use Magento\TestFramework\Bundle\Model\PrepareBundleLinks; + +require __DIR__ . '/multiple_products.php'; + +/** @var PrepareBundleLinks $prepareBundleLinks */ +$prepareBundleLinks = $objectManager->get(PrepareBundleLinks::class); + +$bundleProduct = $productFactory->create(); +$bundleProduct->setTypeId(Type::TYPE_BUNDLE) + ->setAttributeSetId($bundleProduct->getDefaultAttributeSetId()) + ->setWebsiteIds([$defaultWebsiteId]) + ->setName('Bundle Product') + ->setSku('fixed_bundle_product_with_special_price') + ->setVisibility(Visibility::VISIBILITY_BOTH) + ->setStatus(Status::STATUS_ENABLED) + ->setStockData( + [ + 'use_config_manage_stock' => 1, + 'qty' => 100, + 'is_qty_decimal' => 0, + 'is_in_stock' => 1, + ] + ) + ->setPriceView(1) + ->setSkuType(1) + ->setWeightType(1) + ->setPriceType(Price::PRICE_TYPE_FIXED) + ->setPrice(50.0) + ->setSpecialPrice(80) + ->setShipmentType(AbstractType::SHIPMENT_TOGETHER); + +$bundleOptionsData = [ + [ + 'title' => 'Option 1', + 'default_title' => 'Option 1', + 'type' => 'radio', + 'required' => 1, + 'delete' => '', + ], +]; +$bundleSelectionsData = [ + [ + 'sku' => $product->getSku(), + 'selection_qty' => 1, + 'selection_price_value' => 10, + 'selection_price_type' => 0, + 'selection_can_change_qty' => 1, + ], + [ + 'sku' => $product2->getSku(), + 'selection_qty' => 1, + 'selection_price_value' => 25, + 'selection_price_type' => 1, + 'selection_can_change_qty' => 1, + ], + [ + 'sku' => $product3->getSku(), + 'selection_qty' => 1, + 'selection_price_value' => 25, + 'selection_price_type' => 0, + 'selection_can_change_qty' => 1, + ], +]; +$bundleProduct = $prepareBundleLinks->execute($bundleProduct, $bundleOptionsData, [$bundleSelectionsData]); +$productRepository->save($bundleProduct); diff --git a/dev/tests/integration/testsuite/Magento/Bundle/_files/fixed_bundle_product_with_special_price_rollback.php b/dev/tests/integration/testsuite/Magento/Bundle/_files/fixed_bundle_product_with_special_price_rollback.php new file mode 100644 index 0000000000000..2fd27a34a1f2a --- /dev/null +++ b/dev/tests/integration/testsuite/Magento/Bundle/_files/fixed_bundle_product_with_special_price_rollback.php @@ -0,0 +1,23 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +declare(strict_types=1); + +use Magento\Framework\Exception\NoSuchEntityException; + +require __DIR__ . '/multiple_products_rollback.php'; + +$registry->unregister('isSecureArea'); +$registry->register('isSecureArea', true); + +try { + $product = $productRepository->get('fixed_bundle_product_with_special_price', false, null, true); + $productRepository->delete($product); +} catch (NoSuchEntityException $e) { + //Product already removed +} + +$registry->unregister('isSecureArea'); +$registry->register('isSecureArea', false); diff --git a/dev/tests/integration/testsuite/Magento/Bundle/_files/fixed_bundle_product_with_tier_price.php b/dev/tests/integration/testsuite/Magento/Bundle/_files/fixed_bundle_product_with_tier_price.php new file mode 100644 index 0000000000000..64de77d87085d --- /dev/null +++ b/dev/tests/integration/testsuite/Magento/Bundle/_files/fixed_bundle_product_with_tier_price.php @@ -0,0 +1,101 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +declare(strict_types=1); + +use Magento\Bundle\Model\Product\Price; +use Magento\Catalog\Api\Data\ProductTierPriceExtensionFactory; +use Magento\Catalog\Api\Data\ProductTierPriceInterfaceFactory; +use Magento\Catalog\Model\Product\Attribute\Source\Status; +use Magento\Catalog\Model\Product\Type; +use Magento\Catalog\Model\Product\Type\AbstractType; +use Magento\Catalog\Model\Product\Visibility; +use Magento\Customer\Model\Group; +use Magento\TestFramework\Bundle\Model\PrepareBundleLinks; + +require __DIR__ . '/multiple_products.php'; + +/** @var PrepareBundleLinks $prepareBundleLinks */ +$prepareBundleLinks = $objectManager->get(PrepareBundleLinks::class); +/** @var ProductTierPriceInterfaceFactory $tierPriceFactory */ +$tierPriceFactory = $objectManager->get(ProductTierPriceInterfaceFactory::class); +/** @var $tierPriceExtensionAttributesFactory */ +$tierPriceExtensionAttributesFactory = $objectManager->get(ProductTierPriceExtensionFactory::class); + +$bundleProduct = $productFactory->create(); +$bundleProduct->setTypeId(Type::TYPE_BUNDLE) + ->setAttributeSetId($bundleProduct->getDefaultAttributeSetId()) + ->setWebsiteIds([$defaultWebsiteId]) + ->setName('Bundle Product') + ->setSku('fixed_bundle_product_with_tier_price') + ->setVisibility(Visibility::VISIBILITY_BOTH) + ->setStatus(Status::STATUS_ENABLED) + ->setStockData( + [ + 'use_config_manage_stock' => 1, + 'qty' => 100, + 'is_qty_decimal' => 0, + 'is_in_stock' => 1, + ] + ) + ->setPriceView(1) + ->setSkuType(1) + ->setWeightType(1) + ->setPriceType(Price::PRICE_TYPE_FIXED) + ->setPrice(50.0) + ->setShipmentType(AbstractType::SHIPMENT_TOGETHER); + +$bundleOptionsData = [ + [ + 'title' => 'Option 1', + 'default_title' => 'Option 1', + 'type' => 'radio', + 'required' => 1, + 'delete' => '', + ], +]; +$bundleSelectionsData = [ + [ + 'sku' => $product->getSku(), + 'selection_qty' => 1, + 'selection_price_value' => 10, + 'selection_price_type' => 0, + 'selection_can_change_qty' => 1, + ], + [ + 'sku' => $product2->getSku(), + 'selection_qty' => 1, + 'selection_price_value' => 25, + 'selection_price_type' => 1, + 'selection_can_change_qty' => 1, + ], + [ + 'sku' => $product3->getSku(), + 'selection_qty' => 1, + 'selection_price_value' => 25, + 'selection_price_type' => 0, + 'selection_can_change_qty' => 1, + ], +]; +$bundleProduct = $prepareBundleLinks->execute($bundleProduct, $bundleOptionsData, [$bundleSelectionsData]); + +$tierPriceExtensionAttribute = $tierPriceExtensionAttributesFactory->create( + [ + 'data' => [ + 'website_id' => 0, + 'percentage_value' => 25, + ] + ] +); +$tierPrices[] = $tierPriceFactory->create( + [ + 'data' => [ + 'customer_group_id' => Group::CUST_GROUP_ALL, + 'qty' => 1, + ] + ] +)->setExtensionAttributes($tierPriceExtensionAttribute); +$bundleProduct->setTierPrices($tierPrices); +$productRepository->save($bundleProduct); diff --git a/dev/tests/integration/testsuite/Magento/Bundle/_files/fixed_bundle_product_with_tier_price_rollback.php b/dev/tests/integration/testsuite/Magento/Bundle/_files/fixed_bundle_product_with_tier_price_rollback.php new file mode 100644 index 0000000000000..0e3be5936876a --- /dev/null +++ b/dev/tests/integration/testsuite/Magento/Bundle/_files/fixed_bundle_product_with_tier_price_rollback.php @@ -0,0 +1,23 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +declare(strict_types=1); + +use Magento\Framework\Exception\NoSuchEntityException; + +require __DIR__ . '/multiple_products_rollback.php'; + +$registry->unregister('isSecureArea'); +$registry->register('isSecureArea', true); + +try { + $product = $productRepository->get('fixed_bundle_product_with_tier_price', false, null, true); + $productRepository->delete($product); +} catch (NoSuchEntityException $e) { + //Product already removed +} + +$registry->unregister('isSecureArea'); +$registry->register('isSecureArea', false); diff --git a/dev/tests/integration/testsuite/Magento/Bundle/_files/fixed_bundle_product_without_discounts.php b/dev/tests/integration/testsuite/Magento/Bundle/_files/fixed_bundle_product_without_discounts.php new file mode 100644 index 0000000000000..1b5d6b2eb60d2 --- /dev/null +++ b/dev/tests/integration/testsuite/Magento/Bundle/_files/fixed_bundle_product_without_discounts.php @@ -0,0 +1,76 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +declare(strict_types=1); + +use Magento\Bundle\Model\Product\Price; +use Magento\Catalog\Model\Product\Attribute\Source\Status; +use Magento\Catalog\Model\Product\Type; +use Magento\Catalog\Model\Product\Type\AbstractType; +use Magento\Catalog\Model\Product\Visibility; +use Magento\TestFramework\Bundle\Model\PrepareBundleLinks; + +require __DIR__ . '/multiple_products.php'; + +/** @var PrepareBundleLinks $prepareBundleLinks */ +$prepareBundleLinks = $objectManager->get(PrepareBundleLinks::class); + +$bundleProduct = $productFactory->create(); +$bundleProduct->setTypeId(Type::TYPE_BUNDLE) + ->setAttributeSetId($bundleProduct->getDefaultAttributeSetId()) + ->setWebsiteIds([$defaultWebsiteId]) + ->setName('Bundle Product') + ->setSku('fixed_bundle_product_without_discounts') + ->setVisibility(Visibility::VISIBILITY_BOTH) + ->setStatus(Status::STATUS_ENABLED) + ->setStockData( + [ + 'use_config_manage_stock' => 1, + 'qty' => 100, + 'is_qty_decimal' => 0, + 'is_in_stock' => 1, + ] + ) + ->setPriceView(1) + ->setSkuType(1) + ->setWeightType(1) + ->setPriceType(Price::PRICE_TYPE_FIXED) + ->setPrice(50.0) + ->setShipmentType(AbstractType::SHIPMENT_TOGETHER); + +$bundleOptionsData = [ + [ + 'title' => 'Option 1', + 'default_title' => 'Option 1', + 'type' => 'radio', + 'required' => 1, + 'delete' => '', + ], +]; +$bundleSelectionsData = [ + [ + 'sku' => $product->getSku(), + 'selection_qty' => 1, + 'selection_price_value' => 10, + 'selection_price_type' => 0, + 'selection_can_change_qty' => 1, + ], + [ + 'sku' => $product2->getSku(), + 'selection_qty' => 1, + 'selection_price_value' => 25, + 'selection_price_type' => 1, + 'selection_can_change_qty' => 1, + ], + [ + 'sku' => $product3->getSku(), + 'selection_qty' => 1, + 'selection_price_value' => 25, + 'selection_price_type' => 0, + 'selection_can_change_qty' => 1, + ], +]; +$bundleProduct = $prepareBundleLinks->execute($bundleProduct, $bundleOptionsData, [$bundleSelectionsData]); +$productRepository->save($bundleProduct); diff --git a/dev/tests/integration/testsuite/Magento/Bundle/_files/fixed_bundle_product_without_discounts_rollback.php b/dev/tests/integration/testsuite/Magento/Bundle/_files/fixed_bundle_product_without_discounts_rollback.php new file mode 100644 index 0000000000000..d6a301c7937d6 --- /dev/null +++ b/dev/tests/integration/testsuite/Magento/Bundle/_files/fixed_bundle_product_without_discounts_rollback.php @@ -0,0 +1,23 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +declare(strict_types=1); + +use Magento\Framework\Exception\NoSuchEntityException; + +require __DIR__ . '/multiple_products_rollback.php'; + +$registry->unregister('isSecureArea'); +$registry->register('isSecureArea', true); + +try { + $product = $productRepository->get('fixed_bundle_product_without_discounts', false, null, true); + $productRepository->delete($product); +} catch (NoSuchEntityException $e) { + //Product already removed +} + +$registry->unregister('isSecureArea'); +$registry->register('isSecureArea', false); diff --git a/dev/tests/integration/testsuite/Magento/Bundle/_files/multiple_products.php b/dev/tests/integration/testsuite/Magento/Bundle/_files/multiple_products.php index 662b69c89bc6d..fa957a0bfd3f8 100644 --- a/dev/tests/integration/testsuite/Magento/Bundle/_files/multiple_products.php +++ b/dev/tests/integration/testsuite/Magento/Bundle/_files/multiple_products.php @@ -3,31 +3,44 @@ * Copyright © Magento, Inc. All rights reserved. * See COPYING.txt for license details. */ +declare(strict_types=1); -$productRepository = \Magento\TestFramework\Helper\Bootstrap::getObjectManager() - ->create(\Magento\Catalog\Api\ProductRepositoryInterface::class); +use Magento\Catalog\Api\ProductRepositoryInterface; +use Magento\Catalog\Api\Data\ProductInterfaceFactory; +use Magento\Catalog\Model\Product\Type as ProductType; +use Magento\Catalog\Model\Product\Attribute\Source\Status; +use Magento\Catalog\Model\Product\Visibility; +use Magento\Msrp\Model\Product\Attribute\Source\Type; +use Magento\Store\Api\WebsiteRepositoryInterface; +use Magento\TestFramework\Helper\Bootstrap; -/** @var $product \Magento\Catalog\Model\Product */ -$product = \Magento\TestFramework\Helper\Bootstrap::getObjectManager()->create(\Magento\Catalog\Model\Product::class); -$product->isObjectNew(true); -$product->setTypeId(\Magento\Catalog\Model\Product\Type::TYPE_SIMPLE) +$objectManager = Bootstrap::getObjectManager(); +/** @var ProductInterfaceFactory $productFactory */ +$productFactory = $objectManager->get(ProductInterfaceFactory::class); +/** @var ProductRepositoryInterface $productRepository */ +$productRepository = $objectManager->create(ProductRepositoryInterface::class); +/** @var WebsiteRepositoryInterface $websiteRepository */ +$websiteRepository = $objectManager->get(WebsiteRepositoryInterface::class); +$defaultWebsiteId = $websiteRepository->get('base')->getId(); +$product = $productFactory->create(); +$product->setTypeId(ProductType::TYPE_SIMPLE) ->setId(10) - ->setAttributeSetId(4) + ->setAttributeSetId($product->getDefaultAttributeSetId()) ->setName('Simple Product') ->setSku('simple1') - ->setTaxClassId('none') + ->setTaxClassId(0) ->setDescription('description') ->setShortDescription('short description') ->setOptionsContainer('container1') - ->setMsrpDisplayActualPriceType(\Magento\Msrp\Model\Product\Attribute\Source\Type::TYPE_IN_CART) + ->setMsrpDisplayActualPriceType(Type::TYPE_IN_CART) ->setPrice(10) ->setWeight(1) ->setMetaTitle('meta title') ->setMetaKeyword('meta keyword') ->setMetaDescription('meta description') - ->setVisibility(\Magento\Catalog\Model\Product\Visibility::VISIBILITY_BOTH) - ->setStatus(\Magento\Catalog\Model\Product\Attribute\Source\Status::STATUS_ENABLED) - ->setWebsiteIds([1]) + ->setVisibility(Visibility::VISIBILITY_BOTH) + ->setStatus(Status::STATUS_ENABLED) + ->setWebsiteIds([$defaultWebsiteId]) ->setCategoryIds([]) ->setStockData([ 'use_config_manage_stock' => 1, @@ -36,29 +49,27 @@ 'is_in_stock' => 1, 'manage_stock' => 1, ]); - $productRepository->save($product); -$product = \Magento\TestFramework\Helper\Bootstrap::getObjectManager()->create(\Magento\Catalog\Model\Product::class); -$product->isObjectNew(true); -$product->setTypeId(\Magento\Catalog\Model\Product\Type::TYPE_SIMPLE) +$product2 = $productFactory->create(); +$product2->setTypeId(ProductType::TYPE_SIMPLE) ->setId(11) - ->setAttributeSetId(4) + ->setAttributeSetId($product2->getDefaultAttributeSetId()) ->setName('Simple Product2') ->setSku('simple2') - ->setTaxClassId('none') + ->setTaxClassId(0) ->setDescription('description') ->setShortDescription('short description') ->setOptionsContainer('container1') - ->setMsrpDisplayActualPriceType(\Magento\Msrp\Model\Product\Attribute\Source\Type::TYPE_ON_GESTURE) + ->setMsrpDisplayActualPriceType(Type::TYPE_ON_GESTURE) ->setPrice(20) ->setWeight(1) ->setMetaTitle('meta title') ->setMetaKeyword('meta keyword') ->setMetaDescription('meta description') - ->setVisibility(\Magento\Catalog\Model\Product\Visibility::VISIBILITY_IN_CATALOG) - ->setStatus(\Magento\Catalog\Model\Product\Attribute\Source\Status::STATUS_ENABLED) - ->setWebsiteIds([1]) + ->setVisibility(Visibility::VISIBILITY_IN_CATALOG) + ->setStatus(Status::STATUS_ENABLED) + ->setWebsiteIds([$defaultWebsiteId]) ->setCategoryIds([]) ->setStockData([ 'use_config_manage_stock' => 1, @@ -67,24 +78,22 @@ 'is_in_stock' => 1, 'manage_stock' => 1, ]); +$productRepository->save($product2); -$productRepository->save($product); - -$product = \Magento\TestFramework\Helper\Bootstrap::getObjectManager()->create(\Magento\Catalog\Model\Product::class); -$product->isObjectNew(true); -$product->setTypeId(\Magento\Catalog\Model\Product\Type::TYPE_SIMPLE) +$product3 = $productFactory->create(); +$product3->setTypeId(ProductType::TYPE_SIMPLE) ->setId(12) - ->setAttributeSetId(4) + ->setAttributeSetId($product3->getDefaultAttributeSetId()) ->setName('Simple Product 3') ->setSku('simple3') - ->setTaxClassId('none') + ->setTaxClassId(0) ->setDescription('description') ->setShortDescription('short description') ->setPrice(30) ->setWeight(1) - ->setVisibility(\Magento\Catalog\Model\Product\Visibility::VISIBILITY_IN_CATALOG) - ->setStatus(\Magento\Catalog\Model\Product\Attribute\Source\Status::STATUS_ENABLED) - ->setWebsiteIds([1]) + ->setVisibility(Visibility::VISIBILITY_IN_CATALOG) + ->setStatus(Status::STATUS_ENABLED) + ->setWebsiteIds([$defaultWebsiteId]) ->setCategoryIds([]) ->setStockData([ 'use_config_manage_stock' => 1, @@ -93,29 +102,27 @@ 'is_in_stock' => 1, 'manage_stock' => 1, ]); +$productRepository->save($product3); -$productRepository->save($product); - -$product = \Magento\TestFramework\Helper\Bootstrap::getObjectManager()->create(\Magento\Catalog\Model\Product::class); -$product->isObjectNew(true); -$product->setTypeId(\Magento\Catalog\Model\Product\Type::TYPE_SIMPLE) +$product4 = $productFactory->create(); +$product4->setTypeId(ProductType::TYPE_SIMPLE) ->setId(13) - ->setAttributeSetId(4) + ->setAttributeSetId($product4->getDefaultAttributeSetId()) ->setName('Simple Product 4') ->setSku('simple4') - ->setTaxClassId('none') + ->setTaxClassId(0) ->setDescription('description') ->setShortDescription('short description') ->setOptionsContainer('container1') - ->setMsrpDisplayActualPriceType(\Magento\Msrp\Model\Product\Attribute\Source\Type::TYPE_IN_CART) + ->setMsrpDisplayActualPriceType(Type::TYPE_IN_CART) ->setPrice(13) ->setWeight(12) ->setMetaTitle('meta title') ->setMetaKeyword('meta keyword') ->setMetaDescription('meta description') - ->setVisibility(\Magento\Catalog\Model\Product\Visibility::VISIBILITY_BOTH) - ->setStatus(\Magento\Catalog\Model\Product\Attribute\Source\Status::STATUS_ENABLED) - ->setWebsiteIds([1]) + ->setVisibility(Visibility::VISIBILITY_BOTH) + ->setStatus(Status::STATUS_ENABLED) + ->setWebsiteIds([$defaultWebsiteId]) ->setCategoryIds([]) ->setStockData([ 'use_config_manage_stock' => 1, @@ -124,29 +131,27 @@ 'is_in_stock' => 1, 'manage_stock' => 1, ]); +$productRepository->save($product4); -$productRepository->save($product); - -$product = \Magento\TestFramework\Helper\Bootstrap::getObjectManager()->create(\Magento\Catalog\Model\Product::class); -$product->isObjectNew(true); -$product->setTypeId(\Magento\Catalog\Model\Product\Type::TYPE_SIMPLE) +$product5 = $productFactory->create(); +$product5->setTypeId(ProductType::TYPE_SIMPLE) ->setId(14) - ->setAttributeSetId(4) + ->setAttributeSetId($product5->getDefaultAttributeSetId()) ->setName('Simple Product 5') ->setSku('simple5') - ->setTaxClassId('none') + ->setTaxClassId(0) ->setDescription('description') ->setShortDescription('short description') ->setOptionsContainer('container1') - ->setMsrpDisplayActualPriceType(\Magento\Msrp\Model\Product\Attribute\Source\Type::TYPE_IN_CART) + ->setMsrpDisplayActualPriceType(Type::TYPE_IN_CART) ->setPrice(14) ->setWeight(10) ->setMetaTitle('meta title') ->setMetaKeyword('meta keyword') ->setMetaDescription('meta description') - ->setVisibility(\Magento\Catalog\Model\Product\Visibility::VISIBILITY_BOTH) - ->setStatus(\Magento\Catalog\Model\Product\Attribute\Source\Status::STATUS_ENABLED) - ->setWebsiteIds([1]) + ->setVisibility(Visibility::VISIBILITY_BOTH) + ->setStatus(Status::STATUS_ENABLED) + ->setWebsiteIds([$defaultWebsiteId]) ->setCategoryIds([]) ->setStockData([ 'use_config_manage_stock' => 1, @@ -155,5 +160,4 @@ 'is_in_stock' => 1, 'manage_stock' => 1, ]); - -$productRepository->save($product); +$productRepository->save($product5); diff --git a/dev/tests/integration/testsuite/Magento/Catalog/Block/Product/ListProduct/CheckProductPriceTest.php b/dev/tests/integration/testsuite/Magento/Catalog/Block/Product/ListProduct/CheckProductPriceTest.php new file mode 100644 index 0000000000000..511b2afe2e0f4 --- /dev/null +++ b/dev/tests/integration/testsuite/Magento/Catalog/Block/Product/ListProduct/CheckProductPriceTest.php @@ -0,0 +1,310 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +declare(strict_types=1); + +namespace Magento\Catalog\Block\Product\ListProduct; + +use Magento\Catalog\Api\ProductRepositoryInterface; +use Magento\Catalog\Block\Product\ListProduct; +use Magento\Customer\Model\Session; +use Magento\Framework\View\Element\Template; +use Magento\Framework\View\Result\PageFactory; +use Magento\TestFramework\Helper\Bootstrap; +use Magento\TestFramework\ObjectManager; +use PHPUnit\Framework\TestCase; + +/** + * Check that product price render correctly on category page. + * + * @magentoDbIsolation enabled + * @magentoAppArea frontend + */ +class CheckProductPriceTest extends TestCase +{ + /** + * @var ObjectManager + */ + private $objectManager; + + /** + * @var PageFactory + */ + private $pageFactory; + + /** + * @var ProductRepositoryInterface + */ + private $productRepository; + + /** + * @var Session + */ + private $customerSession; + + /** + * @inheritdoc + */ + protected function setUp() + { + $this->objectManager = Bootstrap::getObjectManager(); + $this->pageFactory = $this->objectManager->get(PageFactory::class); + $this->productRepository = $this->objectManager->get(ProductRepositoryInterface::class); + $this->customerSession = $this->objectManager->create(Session::class); + parent::setUp(); + } + + /** + * Assert that product price without additional price configurations will render as expected. + * + * @magentoDataFixture Magento/Catalog/_files/product_simple_tax_none.php + * + * @return void + */ + public function testCheckProductPriceWithoutAdditionalPriceConfigurations(): void + { + $priceHtml = $this->getProductPriceHtml('simple-product-tax-none'); + $this->assertFinalPrice($priceHtml, 205.00); + } + + /** + * Assert that product special price rendered correctly. + * + * @magentoDataFixture Magento/Catalog/_files/product_special_price.php + * + * @return void + */ + public function testCheckSpecialPrice(): void + { + $priceHtml = $this->getProductPriceHtml('simple'); + $this->assertFinalPrice($priceHtml, 5.99); + $this->assertRegularPrice($priceHtml, 10.00); + } + + /** + * Assert that product with fixed tier price is renders correctly. + * + * @magentoDataFixture Magento/Catalog/_files/product_simple_with_fixed_tier_price.php + * + * @return void + */ + public function testCheckFixedTierPrice(): void + { + $priceHtml = $this->getProductPriceHtml('simple-product-tax-none'); + $this->assertFinalPrice($priceHtml, 205.00); + $this->assertAsLowAsPrice($priceHtml, 40.00); + } + + /** + * Assert that price of product with percent tier price rendered correctly. + * + * @magentoDataFixture Magento/Catalog/_files/product_simple_with_percent_tier_price.php + * + * @return void + */ + public function testCheckPercentTierPrice(): void + { + $priceHtml = $this->getProductPriceHtml('simple-product-tax-none'); + $this->assertFinalPrice($priceHtml, 205.00); + $this->assertAsLowAsPrice($priceHtml, 102.50); + } + + /** + * Assert that price of product with fixed tier price for not logged user is renders correctly. + * + * @magentoDataFixture Magento/Catalog/_files/product_simple_with_fixed_tier_price_for_not_logged_user.php + * + * @return void + */ + public function testCheckFixedTierPriceForNotLoggedUser(): void + { + $priceHtml = $this->getProductPriceHtml('simple-product-tax-none'); + $this->assertFinalPrice($priceHtml, 30.00); + $this->assertRegularPrice($priceHtml, 205.00); + } + + /** + * Assert that price of product with fixed tier price for logged user is renders correctly. + * + * @magentoDataFixture Magento/Catalog/_files/product_simple_with_fixed_tier_price_for_logged_user.php + * @magentoDataFixture Magento/Customer/_files/customer.php + * @magentoDbIsolation disabled + * @magentoAppArea frontend + * @magentoAppIsolation enabled + * + * @return void + */ + public function testCheckFixedTierPriceForLoggedUser(): void + { + $priceHtml = $this->getProductPriceHtml('simple-product-tax-none'); + $this->assertFinalPrice($priceHtml, 205.00); + $this->assertNotRegExp('/\$10/', $priceHtml); + $this->customerSession->setCustomerId(1); + try { + $priceHtml = $this->getProductPriceHtml('simple-product-tax-none'); + $this->assertFinalPrice($priceHtml, 10.00); + $this->assertRegularPrice($priceHtml, 205.00); + } finally { + $this->customerSession->setCustomerId(null); + } + } + + /** + * Assert that price of product with catalog rule with action equal to "Apply as percentage of original" + * is renders correctly. + * + * @magentoDataFixture Magento/Catalog/_files/product_simple_tax_none.php + * @magentoDataFixture Magento/CatalogRule/_files/rule_apply_as_percentage_of_original_not_logged_user.php + * @magentoDbIsolation disabled + * @magentoAppArea frontend + * + * @return void + */ + public function testCheckPriceRendersCorrectlyWithApplyAsPercentageOfOriginalRule(): void + { + $priceHtml = $this->getProductPriceHtml('simple-product-tax-none'); + $this->assertFinalPrice($priceHtml, 184.50); + $this->assertRegularPrice($priceHtml, 205.00); + } + + /** + * Assert that price of product with catalog rule with action equal to "Apply as fixed amount" + * is renders correctly. + * + * @magentoDataFixture Magento/Catalog/_files/product_simple_tax_none.php + * @magentoDataFixture Magento/CatalogRule/_files/rule_apply_as_fixed_amount_not_logged_user.php + * @magentoDbIsolation disabled + * @magentoAppArea frontend + * + * @return void + */ + public function testCheckPriceRendersCorrectlyWithApplyAsFixedAmountRule(): void + { + $priceHtml = $this->getProductPriceHtml('simple-product-tax-none'); + $this->assertFinalPrice($priceHtml, 195.00); + $this->assertRegularPrice($priceHtml, 205.00); + } + + /** + * Assert that price of product with catalog rule with action equal to "Adjust final price to this percentage" + * is renders correctly. + * + * @magentoDataFixture Magento/Catalog/_files/product_simple_tax_none.php + * @magentoDataFixture Magento/CatalogRule/_files/rule_adjust_final_price_to_this_percentage_not_logged_user.php + * @magentoDbIsolation disabled + * @magentoAppArea frontend + * + * @return void + */ + public function testCheckPriceRendersCorrectlyWithAdjustFinalPriceToThisPercentageRule(): void + { + $priceHtml = $this->getProductPriceHtml('simple-product-tax-none'); + $this->assertFinalPrice($priceHtml, 20.50); + $this->assertRegularPrice($priceHtml, 205.00); + } + + /** + * Assert that price of product with catalog rule with action equal to "Adjust final price to discount value" + * is renders correctly. + * + * @magentoDataFixture Magento/Catalog/_files/product_simple_tax_none.php + * @magentoDataFixture Magento/CatalogRule/_files/rule_adjust_final_price_to_discount_value_not_logged_user.php + * @magentoDbIsolation disabled + * @magentoAppArea frontend + * + * @return void + */ + public function testCheckPriceRendersCorrectlyWithAdjustFinalPriceToDiscountValueRule(): void + { + $priceHtml = $this->getProductPriceHtml('simple-product-tax-none'); + $this->assertFinalPrice($priceHtml, 10.00); + $this->assertRegularPrice($priceHtml, 205.00); + } + + /** + * Assert that price html contain "As low as" label and expected price amount. + * + * @param string $priceHtml + * @param float $expectedPrice + * @return void + */ + private function assertAsLowAsPrice(string $priceHtml, float $expectedPrice): void + { + $this->assertRegExp( + sprintf( + '/<span class="price-label">As low as<\/span> {1,}<span.*data-price-amount="%s".*>\$%01.2f<\/span>/', + round($expectedPrice, 2), + $expectedPrice + ), + $priceHtml + ); + } + + /** + * Assert that price html contain expected final price amount. + * + * @param string $priceHtml + * @param float $expectedPrice + * @return void + */ + private function assertFinalPrice(string $priceHtml, float $expectedPrice): void + { + $this->assertRegExp( + sprintf( + '/data-price-type="finalPrice".*<span class="price">\$%01.2f<\/span><\/span>/', + $expectedPrice + ), + $priceHtml + ); + } + + /** + * Assert that price html contain "Regular price" label and expected price amount. + * + * @param string $priceHtml + * @param float $expectedPrice + * @return void + */ + private function assertRegularPrice(string $priceHtml, float $expectedPrice): void + { + $regex = '<span class="price-label">Regular Price<\/span> {1,}<span.*data-price-amount="%s".*>\$%01.2f<\/span>'; + $this->assertRegExp( + sprintf("/{$regex}/", round($expectedPrice, 2), $expectedPrice), + $priceHtml + ); + } + + /** + * Return html of product price without new line characters. + * + * @param string $sku + * @return string + */ + private function getProductPriceHtml(string $sku): string + { + $product = $this->productRepository->get($sku, false, null, true); + + return preg_replace('/[\n\r]/', '', $this->getListProductBlock()->getProductPrice($product)); + } + + /** + * Get list product block from layout. + * + * @return ListProduct + */ + private function getListProductBlock(): ListProduct + { + $page = $this->pageFactory->create(); + $page->addHandle([ + 'default', + 'catalog_category_view', + ]); + $page->getLayout()->generateXml(); + /** @var Template $categoryProductsBlock */ + $categoryProductsBlock = $page->getLayout()->getBlock('category.products'); + + return $categoryProductsBlock->getChildBlock('product_list'); + } +} diff --git a/dev/tests/integration/testsuite/Magento/Catalog/Block/Product/ListProduct/ProductInCategoriesViewTest.php b/dev/tests/integration/testsuite/Magento/Catalog/Block/Product/ListProduct/ProductInCategoriesViewTest.php index b9adb051981c0..48f6e455a5b9f 100644 --- a/dev/tests/integration/testsuite/Magento/Catalog/Block/Product/ListProduct/ProductInCategoriesViewTest.php +++ b/dev/tests/integration/testsuite/Magento/Catalog/Block/Product/ListProduct/ProductInCategoriesViewTest.php @@ -100,6 +100,19 @@ public function productDataProvider(): array ]; } + /** + * @magentoConfigFixture default_store cataloginventory/options/show_out_of_stock 1 + * @magentoDataFixture Magento/Catalog/_files/out_of_stock_product_with_category.php + * @return void + */ + public function testCategoryOutOfStockProductView(): void + { + $collection = $this->getCategoryProductCollection(333); + + $this->assertEquals(1, $collection->getSize()); + $this->assertEquals('out-of-stock-product', $collection->getFirstItem()->getSku()); + } + /** * @magentoDataFixture Magento/Catalog/_files/category_product.php * @dataProvider productVisibilityProvider diff --git a/dev/tests/integration/testsuite/Magento/Catalog/Block/Product/ListProduct/SortingTest.php b/dev/tests/integration/testsuite/Magento/Catalog/Block/Product/ListProduct/SortingTest.php index d3c7972453a4b..31da969540d30 100644 --- a/dev/tests/integration/testsuite/Magento/Catalog/Block/Product/ListProduct/SortingTest.php +++ b/dev/tests/integration/testsuite/Magento/Catalog/Block/Product/ListProduct/SortingTest.php @@ -85,6 +85,7 @@ protected function setUp() /** * @magentoDataFixture Magento/Catalog/_files/products_with_not_empty_layered_navigation_attribute.php + * @magentoConfigFixture default/catalog/search/engine mysql * @dataProvider productListSortOrderDataProvider * @param string $sortBy * @param string $direction @@ -100,6 +101,7 @@ public function testProductListSortOrder(string $sortBy, string $direction, arra /** * @magentoDataFixture Magento/Catalog/_files/products_with_not_empty_layered_navigation_attribute.php + * @magentoConfigFixture default/catalog/search/engine mysql * @dataProvider productListSortOrderDataProvider * @param string $sortBy * @param string $direction @@ -108,16 +110,7 @@ public function testProductListSortOrder(string $sortBy, string $direction, arra */ public function testProductListSortOrderWithConfig(string $sortBy, string $direction, array $expectation): void { - $this->objectManager->removeSharedInstance(Config::class); - $this->scopeConfig->setValue( - Config::XML_PATH_LIST_DEFAULT_SORT_BY, - $sortBy, - ScopeInterface::SCOPE_STORE, - Store::DEFAULT_STORE_ID - ); - $category = $this->updateCategorySortBy('Category 1', Store::DEFAULT_STORE_ID, null); - $this->renderBlock($category, $direction); - $this->assertBlockSorting($sortBy, $expectation); + $this->assertProductListSortOrderWithConfig($sortBy, $direction, $expectation); } /** @@ -172,6 +165,7 @@ public function productListSortOrderDataProvider(): array /** * @magentoDataFixture Magento/Store/_files/second_store.php * @magentoDataFixture Magento/Catalog/_files/products_with_not_empty_layered_navigation_attribute.php + * @magentoConfigFixture default/catalog/search/engine mysql * @dataProvider productListSortOrderDataProviderOnStoreView * @param string $sortBy * @param string $direction @@ -195,6 +189,7 @@ public function testProductListSortOrderOnStoreView( /** * @magentoDataFixture Magento/Store/_files/second_store.php * @magentoDataFixture Magento/Catalog/_files/products_with_not_empty_layered_navigation_attribute.php + * @magentoConfigFixture default/catalog/search/engine mysql * @dataProvider productListSortOrderDataProviderOnStoreView * @param string $sortBy * @param string $direction @@ -318,4 +313,89 @@ private function updateCategorySortBy( return $category; } + + /** + * Test product list ordered by price with out-of-stock configurable product options with elasticsearch engine + * + * @magentoDataFixture Magento/Catalog/_files/products_with_not_empty_layered_navigation_attribute.php + * @magentoDataFixture Magento/Framework/Search/_files/product_configurable_with_out-of-stock_child.php + * @magentoConfigFixture current_store cataloginventory/options/show_out_of_stock 1 + * @magentoConfigFixture default/catalog/search/engine elasticsearch7 + * @dataProvider productListWithOutOfStockSortOrderDataProvider + * @param string $sortBy + * @param string $direction + * @param array $expected + * @return void + */ + public function testProductListOutOfStockSortOrderWithElasticsearch( + string $sortBy, + string $direction, + array $expected + ): void { + $this->assertProductListSortOrderWithConfig($sortBy, $direction, $expected); + } + + /** + * Test product list ordered by price with out-of-stock configurable product options with mysql search engine + * + * @magentoDataFixture Magento/Catalog/_files/products_with_not_empty_layered_navigation_attribute.php + * @magentoDataFixture Magento/Framework/Search/_files/product_configurable_with_out-of-stock_child.php + * @magentoConfigFixture current_store cataloginventory/options/show_out_of_stock 1 + * @magentoConfigFixture default/catalog/search/engine mysql + * @dataProvider productListWithOutOfStockSortOrderDataProvider + * @param string $sortBy + * @param string $direction + * @param array $expected + * @return void + */ + public function testProductListOutOfStockSortOrderWithMysql( + string $sortBy, + string $direction, + array $expected + ): void { + $this->assertProductListSortOrderWithConfig($sortBy, $direction, $expected); + } + + /** + * Product list with out-of-stock sort order data provider + * + * @return array + */ + public function productListWithOutOfStockSortOrderDataProvider(): array + { + return [ + 'default_order_price_asc' => [ + 'sort' => 'price', + 'direction' => Collection::SORT_ORDER_ASC, + 'expectation' => ['simple1', 'simple2', 'simple3', 'configurable'], + ], + 'default_order_price_desc' => [ + 'sort' => 'price', + 'direction' => Collection::SORT_ORDER_DESC, + 'expectation' => ['simple3', 'simple2', 'simple1', 'configurable'], + ], + ]; + } + + /** + * Assert product list order + * + * @param string $sortBy + * @param string $direction + * @param array $expected + * @return void + */ + private function assertProductListSortOrderWithConfig(string $sortBy, string $direction, array $expected): void + { + $this->objectManager->removeSharedInstance(Config::class); + $this->scopeConfig->setValue( + Config::XML_PATH_LIST_DEFAULT_SORT_BY, + $sortBy, + ScopeInterface::SCOPE_STORE, + Store::DEFAULT_STORE_ID + ); + $category = $this->updateCategorySortBy('Category 1', Store::DEFAULT_STORE_ID, null); + $this->renderBlock($category, $direction); + $this->assertBlockSorting($sortBy, $expected); + } } diff --git a/dev/tests/integration/testsuite/Magento/Catalog/Block/Product/ProductList/CrosssellTest.php b/dev/tests/integration/testsuite/Magento/Catalog/Block/Product/ProductList/CrosssellTest.php index bf546ccd979b7..e3d8e3b229ffa 100644 --- a/dev/tests/integration/testsuite/Magento/Catalog/Block/Product/ProductList/CrosssellTest.php +++ b/dev/tests/integration/testsuite/Magento/Catalog/Block/Product/ProductList/CrosssellTest.php @@ -29,6 +29,7 @@ public function testAll() $block = $objectManager->get(\Magento\Framework\View\LayoutInterface::class) ->createBlock(\Magento\Catalog\Block\Product\ProductList\Crosssell::class); $block->setLayout($objectManager->get(\Magento\Framework\View\LayoutInterface::class)); + $block->setViewModel($objectManager->get(\Magento\Catalog\ViewModel\Product\Listing\PreparePostData::class)); $block->setTemplate('Magento_Catalog::product/list/items.phtml'); $block->setType('crosssell'); $block->setItemCount(1); @@ -36,7 +37,7 @@ public function testAll() $this->assertNotEmpty($html); $this->assertContains('Simple Cross Sell', $html); /* name */ - $this->assertContains('product\/' . $firstProduct->getId() . '\/', $html); + $this->assertContains('product/' . $firstProduct->getId() . '/', $html); /* part of url */ $this->assertInstanceOf( \Magento\Catalog\Model\ResourceModel\Product\Link\Product\Collection::class, diff --git a/dev/tests/integration/testsuite/Magento/Catalog/Block/Product/View/AbstractCurrencyTest.php b/dev/tests/integration/testsuite/Magento/Catalog/Block/Product/View/AbstractCurrencyTest.php new file mode 100644 index 0000000000000..2ae71797e52e5 --- /dev/null +++ b/dev/tests/integration/testsuite/Magento/Catalog/Block/Product/View/AbstractCurrencyTest.php @@ -0,0 +1,111 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +declare(strict_types=1); + +namespace Magento\Catalog\Block\Product\View; + +use Magento\Catalog\Api\Data\ProductInterface; +use Magento\Catalog\Api\ProductRepositoryInterface; +use Magento\Framework\ObjectManagerInterface; +use Magento\Framework\Registry; +use Magento\Framework\View\Result\PageFactory; +use Magento\TestFramework\Helper\Bootstrap; +use PHPUnit\Framework\TestCase; + +/** + * Class consist of general logic for currency tests + */ +abstract class AbstractCurrencyTest extends TestCase +{ + protected const TIER_PRICE_BLOCK_NAME = 'product.price.tier'; + protected const FINAL_PRICE_BLOCK_NAME = 'product.price.final'; + + /** @var ObjectManagerInterface */ + protected $objectManager; + + /** @var Registry */ + protected $registry; + + /** @var PageFactory */ + private $pageFactory; + + /** @var ProductRepositoryInterface */ + private $productRepository; + + /** + * @inheritdoc + */ + protected function setUp() + { + parent::setUp(); + + $this->objectManager = Bootstrap::getObjectManager(); + $this->registry = $this->objectManager->get(Registry::class); + $this->productRepository = $this->objectManager->get(ProductRepositoryInterface::class); + $this->productRepository->cleanCache(); + $this->pageFactory = $this->objectManager->get(PageFactory::class); + } + + /** + * @inheridoc + */ + protected function tearDown() + { + $this->registry->unregister('product'); + + parent::tearDown(); + } + + /** + * Process price view on product page + * + * @param string|ProductInterface $product + * @param string $blockName + * @return string + */ + protected function processPriceView($product, string $blockName = self::FINAL_PRICE_BLOCK_NAME): string + { + $product = is_string($product) ? $this->productRepository->get($product) : $product; + $this->registerProduct($product); + + return trim( + preg_replace('/(?:\s| )+/', ' ', strip_tags($this->getProductPriceBlockHtml($blockName))) + ); + } + + /** + * Get product price block content + * + * @param string $blockName + * @return string + */ + private function getProductPriceBlockHtml(string $blockName): string + { + $page = $this->pageFactory->create(); + $page->addHandle([ + 'default', + 'catalog_product_view', + 'catalog_product_view_type_configurable', + ]); + $page->getLayout()->generateXml(); + $block = $page->getLayout()->getBlock($blockName); + $this->assertNotFalse($block); + + return $block->toHtml(); + } + + /** + * Register the product + * + * @param ProductInterface $product + * @return void + */ + private function registerProduct(ProductInterface $product): void + { + $this->registry->unregister('product'); + $this->registry->register('product', $product); + } +} diff --git a/dev/tests/integration/testsuite/Magento/Catalog/Block/Product/View/Attribute/AbstractAttributeTest.php b/dev/tests/integration/testsuite/Magento/Catalog/Block/Product/View/Attribute/AbstractAttributeTest.php index 399abea6a0760..80e2ac52cecd6 100644 --- a/dev/tests/integration/testsuite/Magento/Catalog/Block/Product/View/Attribute/AbstractAttributeTest.php +++ b/dev/tests/integration/testsuite/Magento/Catalog/Block/Product/View/Attribute/AbstractAttributeTest.php @@ -16,6 +16,7 @@ use Magento\Framework\ObjectManagerInterface; use Magento\Framework\Registry; use Magento\Framework\View\LayoutInterface; +use Magento\Store\Model\StoreManagerInterface; use Magento\TestFramework\Helper\Bootstrap; use PHPUnit\Framework\TestCase; @@ -48,6 +49,9 @@ abstract class AbstractAttributeTest extends TestCase /** @var Output */ private $outputHelper; + /** @var StoreManagerInterface */ + private $storeManager; + /** * @inheritdoc */ @@ -62,6 +66,7 @@ protected function setUp() $this->registry = $this->objectManager->get(Registry::class); $this->block = $this->layout->createBlock(Attributes::class); $this->outputHelper = $this->objectManager->create(Output::class); + $this->storeManager = $this->objectManager->get(StoreManagerInterface::class); } /** @@ -145,6 +150,35 @@ protected function processAttributeHtmlOutput( $this->assertEquals($expectedAttributeValue, $output); } + /** + * Process attribute view per store views + * + * @param string $sku + * @param int $attributeScopeValue + * @param string $attributeValue + * @param string $expectedAttributeValue + * @param string $storeCode + * @return void + */ + protected function processMultiStoreView( + string $sku, + int $attributeScopeValue, + string $attributeValue, + string $storeCode + ): void { + $currentStore = $this->storeManager->getStore(); + $this->updateAttribute(['is_global' => $attributeScopeValue, 'is_visible_on_front' => true]); + $this->storeManager->setCurrentStore($storeCode); + + try { + $product = $this->updateProduct($sku, $attributeValue); + $this->registerProduct($product); + $this->assertEquals($this->prepareExpectedData($attributeValue), $this->block->getAdditionalData()); + } finally { + $this->storeManager->setCurrentStore($currentStore); + } + } + /** * Get attribute * @@ -185,8 +219,11 @@ private function prepareExpectedData(string $expectedValue): array */ private function updateProduct(string $productSku, string $attributeValue): ProductInterface { + $value = $this->getAttribute()->usesSource() + ? $this->attribute->getSource()->getOptionId($attributeValue) + : $attributeValue; $product = $this->productRepository->get($productSku); - $product->addData([$this->getAttributeCode() => $attributeValue]); + $product->addData([$this->getAttributeCode() => $value]); return $this->productRepository->save($product); } diff --git a/dev/tests/integration/testsuite/Magento/Catalog/Block/Product/View/Attribute/DropdownAttributeTest.php b/dev/tests/integration/testsuite/Magento/Catalog/Block/Product/View/Attribute/DropdownAttributeTest.php index 98799822dcfb0..f6c7e81b13a23 100644 --- a/dev/tests/integration/testsuite/Magento/Catalog/Block/Product/View/Attribute/DropdownAttributeTest.php +++ b/dev/tests/integration/testsuite/Magento/Catalog/Block/Product/View/Attribute/DropdownAttributeTest.php @@ -7,6 +7,8 @@ namespace Magento\Catalog\Block\Product\View\Attribute; +use Magento\Eav\Model\Entity\Attribute\ScopedAttributeInterface; + /** * Class checks dropdown attribute displaying on frontend * @@ -77,6 +79,43 @@ public function attributeWithTagsProvider(): array ]; } + /** + * @magentoDbIsolation disabled + * + * @magentoDataFixture Magento/Catalog/_files/dropdown_attribute.php + * @magentoDataFixture Magento/Store/_files/core_fixturestore.php + * @magentoDataFixture Magento/Catalog/_files/second_product_simple.php + * + * @return void + */ + public function testAttributePerStoreView(): void + { + $this->processMultiStoreView( + 'simple2', + ScopedAttributeInterface::SCOPE_STORE, + 'Option 3', + 'fixturestore' + ); + } + + /** + * @magentoDbIsolation disabled + * + * @magentoDataFixture Magento/Catalog/_files/dropdown_attribute.php + * @magentoDataFixture Magento/Catalog/_files/product_two_websites.php + * + * @return void + */ + public function testAttributePerWebsites(): void + { + $this->processMultiStoreView( + 'simple-on-two-websites', + ScopedAttributeInterface::SCOPE_WEBSITE, + 'Option 3', + 'fixture_second_store' + ); + } + /** * @inheritdoc */ diff --git a/dev/tests/integration/testsuite/Magento/Catalog/Block/Product/View/Attribute/TextAttributeTest.php b/dev/tests/integration/testsuite/Magento/Catalog/Block/Product/View/Attribute/TextAttributeTest.php index b61c5fd22d5b0..dae5fad160128 100644 --- a/dev/tests/integration/testsuite/Magento/Catalog/Block/Product/View/Attribute/TextAttributeTest.php +++ b/dev/tests/integration/testsuite/Magento/Catalog/Block/Product/View/Attribute/TextAttributeTest.php @@ -7,6 +7,8 @@ namespace Magento\Catalog\Block\Product\View\Attribute; +use Magento\Eav\Model\Entity\Attribute\ScopedAttributeInterface; + /** * Class checks text attribute displaying on frontend * @@ -77,6 +79,43 @@ public function attributeWithTagsProvider(): array ]; } + /** + * @magentoDbIsolation disabled + * + * @magentoDataFixture Magento/Catalog/_files/product_varchar_attribute.php + * @magentoDataFixture Magento/Store/_files/core_fixturestore.php + * @magentoDataFixture Magento/Catalog/_files/second_product_simple.php + * + * @return void + */ + public function testAttributePerStoreView(): void + { + $this->processMultiStoreView( + 'simple2', + ScopedAttributeInterface::SCOPE_STORE, + 'second store view value', + 'fixturestore' + ); + } + + /** + * @magentoDbIsolation disabled + * + * @magentoDataFixture Magento/Catalog/_files/product_two_websites.php + * @magentoDataFixture Magento/Catalog/_files/product_varchar_attribute.php + * + * @return void + */ + public function testAttributePerWebsites(): void + { + $this->processMultiStoreView( + 'simple-on-two-websites', + ScopedAttributeInterface::SCOPE_WEBSITE, + 'second website value', + 'fixture_second_store' + ); + } + /** * @inheritdoc */ diff --git a/dev/tests/integration/testsuite/Magento/Catalog/Block/Product/View/MultiStoreCurrencyTest.php b/dev/tests/integration/testsuite/Magento/Catalog/Block/Product/View/MultiStoreCurrencyTest.php new file mode 100644 index 0000000000000..22d30fd3d9ea8 --- /dev/null +++ b/dev/tests/integration/testsuite/Magento/Catalog/Block/Product/View/MultiStoreCurrencyTest.php @@ -0,0 +1,147 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +declare(strict_types=1); + +namespace Magento\Catalog\Block\Product\View; + +use Magento\Store\Model\StoreManagerInterface; + +/** + * Checks currency displaying and converting on the catalog pages on multi store mode + * + * @magentoAppArea frontend + * @magentoDbIsolation disabled + */ +class MultiStoreCurrencyTest extends AbstractCurrencyTest +{ + /** @var StoreManagerInterface */ + private $storeManager; + + /** + * @inheritdoc + */ + protected function setUp() + { + parent::setUp(); + + $this->storeManager = $this->objectManager->get(StoreManagerInterface::class); + } + + /** + * @magentoConfigFixture default/currency/options/base USD + * @magentoConfigFixture current_store currency/options/default CNY + * @magentoConfigFixture current_store currency/options/allow CNY,USD + * @magentoConfigFixture fixturestore_store currency/options/default UAH + * @magentoConfigFixture fixturestore_store currency/options/allow UAH,USD + * + * @magentoDataFixture Magento/Store/_files/core_fixturestore.php + * @magentoDataFixture Magento/Catalog/_files/second_product_simple.php + * @magentoDataFixture Magento/Directory/_files/usd_cny_rate.php + * @magentoDataFixture Magento/Directory/_files/usd_uah_rate.php + * + * @return void + */ + public function testMultiStoreRenderPrice(): void + { + $this->assertProductStorePrice('simple2', 'CN¥70.00'); + $this->reloadProductPriceInfo(); + $this->assertProductStorePrice('simple2', '₴240.00', 'fixturestore'); + } + + /** + * @magentoConfigFixture default/currency/options/base USD + * @magentoConfigFixture current_store currency/options/default CNY + * @magentoConfigFixture current_store currency/options/allow CNY,USD + * @magentoConfigFixture fixturestore_store currency/options/default UAH + * @magentoConfigFixture fixturestore_store currency/options/allow UAH,USD + * + * @magentoDataFixture Magento/Store/_files/core_fixturestore.php + * @magentoDataFixture Magento/Catalog/_files/product_special_price.php + * @magentoDataFixture Magento/Directory/_files/usd_cny_rate.php + * @magentoDataFixture Magento/Directory/_files/usd_uah_rate.php + * + * @return void + */ + public function testMultiStoreRenderSpecialPrice(): void + { + $this->assertProductStorePrice('simple', 'Special Price CN¥41.93 Regular Price CN¥70.00'); + $this->reloadProductPriceInfo(); + $this->assertProductStorePrice('simple', 'Special Price ₴143.76 Regular Price ₴240.00', 'fixturestore'); + } + + /** + * @magentoConfigFixture default/currency/options/base USD + * @magentoConfigFixture current_store currency/options/default CNY + * @magentoConfigFixture current_store currency/options/allow CNY,USD + * @magentoConfigFixture fixturestore_store currency/options/default UAH + * @magentoConfigFixture fixturestore_store currency/options/allow UAH,USD + * + * @magentoDataFixture Magento/Store/_files/core_fixturestore.php + * @magentoDataFixture Magento/Catalog/_files/product_simple_with_fixed_tier_price.php + * @magentoDataFixture Magento/Directory/_files/usd_cny_rate.php + * @magentoDataFixture Magento/Directory/_files/usd_uah_rate.php + * + * @return void + */ + public function testMultiStoreRenderTierPrice(): void + { + $this->assertProductStorePrice( + 'simple-product-tax-none', + 'Buy 2 for CN¥280.00 each and save 80%', + 'default', + self::TIER_PRICE_BLOCK_NAME + ); + $this->reloadProductPriceInfo(); + $this->assertProductStorePrice( + 'simple-product-tax-none', + 'Buy 2 for ₴960.00 each and save 80%', + 'fixturestore', + self::TIER_PRICE_BLOCK_NAME + ); + } + + /** + * Check price per stores + * + * @param string $productSku + * @param string $expectedData + * @param string $storeCode + * @param string $priceBlockName + * @return void + */ + private function assertProductStorePrice( + string $productSku, + string $expectedData, + string $storeCode = 'default', + string $priceBlockName = self::FINAL_PRICE_BLOCK_NAME + ): void { + $currentStore = $this->storeManager->getStore(); + try { + if ($currentStore->getCode() !== $storeCode) { + $this->storeManager->setCurrentStore($storeCode); + } + + $actualData = $this->processPriceView($productSku, $priceBlockName); + $this->assertEquals($expectedData, $actualData); + } finally { + if ($currentStore->getCode() !== $storeCode) { + $this->storeManager->setCurrentStore($currentStore); + } + } + } + + /** + * Reload product price info + * + * @return void + */ + private function reloadProductPriceInfo(): void + { + $product = $this->registry->registry('product'); + $this->assertNotNull($product); + $product->reloadPriceInfo(); + } +} diff --git a/dev/tests/integration/testsuite/Magento/Catalog/Block/Product/View/Options/AbstractRenderCustomOptionsTest.php b/dev/tests/integration/testsuite/Magento/Catalog/Block/Product/View/Options/AbstractRenderCustomOptionsTest.php new file mode 100644 index 0000000000000..659cf83407a9e --- /dev/null +++ b/dev/tests/integration/testsuite/Magento/Catalog/Block/Product/View/Options/AbstractRenderCustomOptionsTest.php @@ -0,0 +1,340 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +declare(strict_types=1); + +namespace Magento\Catalog\Block\Product\View\Options; + +use Magento\Catalog\Api\Data\ProductCustomOptionInterface; +use Magento\Catalog\Api\Data\ProductCustomOptionInterfaceFactory; +use Magento\Catalog\Api\Data\ProductCustomOptionValuesInterfaceFactory; +use Magento\Catalog\Api\Data\ProductInterface; +use Magento\Catalog\Api\ProductRepositoryInterface; +use Magento\Catalog\Block\Product\View\Options; +use Magento\Catalog\Model\Product\Option; +use Magento\Catalog\Model\Product\Option\Value; +use Magento\Framework\View\Element\Template; +use Magento\Framework\View\Result\Page; +use Magento\TestFramework\Helper\Bootstrap; +use Magento\TestFramework\ObjectManager; +use PHPUnit\Framework\TestCase; + +/** + * Base logic for render custom options and check that option renders as expected. + */ +abstract class AbstractRenderCustomOptionsTest extends TestCase +{ + /** + * @var ObjectManager + */ + private $objectManager; + + /** + * @var ProductRepositoryInterface + */ + private $productRepository; + + /** + * @var ProductCustomOptionInterfaceFactory + */ + private $productCustomOptionFactory; + + /** + * @var ProductCustomOptionValuesInterfaceFactory + */ + private $productCustomOptionValuesFactory; + + /** + * @var Page + */ + private $page; + + /** + * @inheritdoc + */ + protected function setUp() + { + $this->objectManager = Bootstrap::getObjectManager(); + $this->productRepository = $this->objectManager->create(ProductRepositoryInterface::class); + $this->productCustomOptionFactory = $this->objectManager->get(ProductCustomOptionInterfaceFactory::class); + $this->productCustomOptionValuesFactory = $this->objectManager->get( + ProductCustomOptionValuesInterfaceFactory::class + ); + $this->page = $this->objectManager->create(Page::class); + parent::setUp(); + } + + /** + * @inheritdoc + */ + protected function tearDown() + { + $this->productRepository->cleanCache(); + parent::tearDown(); + } + + /** + * Add provided options from text group to product, render options block + * and check that options rendered as expected. + * + * @param string $productSku + * @param array $optionData + * @param array $checkArray + * @return void + */ + protected function assertTextOptionRenderingOnProduct( + string $productSku, + array $optionData, + array $checkArray + ): void { + $product = $this->productRepository->get($productSku); + $product = $this->addOptionToProduct($product, $optionData); + $option = $this->findOptionByTitle($product, $optionData[Option::KEY_TITLE]); + $optionHtml = $this->getOptionHtml($product); + $this->baseOptionAsserts($option, $optionHtml, $checkArray); + + if ($optionData[Option::KEY_MAX_CHARACTERS] > 0) { + $this->assertContains($checkArray['max_characters'], $optionHtml); + } else { + $this->assertNotContains('class="character-counter', $optionHtml); + } + } + + /** + * Add provided options from file group to product, render options block + * and check that options rendered as expected. + * + * @param string $productSku + * @param array $optionData + * @param array $checkArray + * @return void + */ + protected function assertFileOptionRenderingOnProduct( + string $productSku, + array $optionData, + array $checkArray + ): void { + $product = $this->productRepository->get($productSku); + $product = $this->addOptionToProduct($product, $optionData); + $option = $this->findOptionByTitle($product, $optionData[Option::KEY_TITLE]); + $optionHtml = $this->getOptionHtml($product); + $this->baseOptionAsserts($option, $optionHtml, $checkArray); + $this->assertContains($checkArray['file_extension'], $optionHtml); + + if (isset($checkArray['file_width'])) { + $checkArray['file_width'] = sprintf($checkArray['file_width'], __('Maximum image width')); + $this->assertRegExp($checkArray['file_width'], $optionHtml); + } + + if (isset($checkArray['file_height'])) { + $checkArray['file_height'] = sprintf($checkArray['file_height'], __('Maximum image height')); + $this->assertRegExp($checkArray['file_height'], $optionHtml); + } + } + + /** + * Add provided options from select group to product, render options block + * and check that options rendered as expected. + * + * @param string $productSku + * @param array $optionData + * @param array $optionValueData + * @param array $checkArray + * @return void + */ + protected function assertSelectOptionRenderingOnProduct( + string $productSku, + array $optionData, + array $optionValueData, + array $checkArray + ): void { + $product = $this->productRepository->get($productSku); + $product = $this->addOptionToProduct($product, $optionData, $optionValueData); + $option = $this->findOptionByTitle($product, $optionData[Option::KEY_TITLE]); + $optionValues = $option->getValues(); + $optionValue = reset($optionValues); + $optionHtml = $this->getOptionHtml($product); + $this->baseOptionAsserts($option, $optionHtml, $checkArray); + + if (isset($checkArray['not_contain_arr'])) { + foreach ($checkArray['not_contain_arr'] as $notContainPattern) { + $this->assertNotRegExp($notContainPattern, $optionHtml); + } + } + + if (isset($checkArray['option_value_item'])) { + $checkArray['option_value_item'] = sprintf( + $checkArray['option_value_item'], + $optionValue->getOptionTypeId(), + $optionValueData[Value::KEY_TITLE] + ); + $this->assertRegExp($checkArray['option_value_item'], $optionHtml); + } + } + + /** + * Add provided options from date group to product, render options block + * and check that options rendered as expected. + * + * @param string $productSku + * @param array $optionData + * @param array $checkArray + * @return void + */ + protected function assertDateOptionRenderingOnProduct( + string $productSku, + array $optionData, + array $checkArray + ): void { + $product = $this->productRepository->get($productSku); + $product = $this->addOptionToProduct($product, $optionData); + $option = $this->findOptionByTitle($product, $optionData[Option::KEY_TITLE]); + $optionHtml = $this->getOptionHtml($product); + $this->baseOptionAsserts($option, $optionHtml, $checkArray); + + switch ($optionData[Option::KEY_TYPE]) { + case ProductCustomOptionInterface::OPTION_TYPE_DATE: + $this->assertContains("<select name=\"options[{$option->getOptionId()}][month]\"", $optionHtml); + $this->assertContains("<select name=\"options[{$option->getOptionId()}][day]\"", $optionHtml); + $this->assertContains("<select name=\"options[{$option->getOptionId()}][year]\"", $optionHtml); + $this->assertNotContains("<select name=\"options[{$option->getOptionId()}][hour]\"", $optionHtml); + $this->assertNotContains("<select name=\"options[{$option->getOptionId()}][minute]\"", $optionHtml); + $this->assertNotContains("<select name=\"options[{$option->getOptionId()}][day_part]\"", $optionHtml); + break; + case ProductCustomOptionInterface::OPTION_TYPE_DATE_TIME: + $this->assertContains("<select name=\"options[{$option->getOptionId()}][month]\"", $optionHtml); + $this->assertContains("<select name=\"options[{$option->getOptionId()}][day]\"", $optionHtml); + $this->assertContains("<select name=\"options[{$option->getOptionId()}][year]\"", $optionHtml); + $this->assertContains("<select name=\"options[{$option->getOptionId()}][hour]\"", $optionHtml); + $this->assertContains("<select name=\"options[{$option->getOptionId()}][minute]\"", $optionHtml); + $this->assertContains("<select name=\"options[{$option->getOptionId()}][day_part]\"", $optionHtml); + break; + case ProductCustomOptionInterface::OPTION_TYPE_TIME: + $this->assertNotContains("<select name=\"options[{$option->getOptionId()}][month]\"", $optionHtml); + $this->assertNotContains("<select name=\"options[{$option->getOptionId()}][day]\"", $optionHtml); + $this->assertNotContains("<select name=\"options[{$option->getOptionId()}][year]\"", $optionHtml); + $this->assertContains("<select name=\"options[{$option->getOptionId()}][hour]\"", $optionHtml); + $this->assertContains("<select name=\"options[{$option->getOptionId()}][minute]\"", $optionHtml); + $this->assertContains("<select name=\"options[{$option->getOptionId()}][day_part]\"", $optionHtml); + break; + } + } + + /** + * Base asserts for rendered options. + * + * @param ProductCustomOptionInterface $option + * @param string $optionHtml + * @param array $checkArray + * @return void + */ + private function baseOptionAsserts( + ProductCustomOptionInterface $option, + string $optionHtml, + array $checkArray + ): void { + $this->assertContains($checkArray['block_with_required_class'], $optionHtml); + $this->assertContains($checkArray['title'], $optionHtml); + + if (isset($checkArray['label_for_created_option'])) { + $checkArray['label_for_created_option'] = sprintf( + $checkArray['label_for_created_option'], + $option->getOptionId() + ); + $this->assertContains($checkArray['label_for_created_option'], $optionHtml); + } + + if (isset($checkArray['price'])) { + $this->assertContains($checkArray['price'], $optionHtml); + } + + if (isset($checkArray['required_element'])) { + $this->assertRegExp($checkArray['required_element'], $optionHtml); + } + } + + /** + * Add custom option to product with data. + * + * @param ProductInterface $product + * @param array $optionData + * @param array $optionValueData + * @return ProductInterface + */ + private function addOptionToProduct( + ProductInterface $product, + array $optionData, + array $optionValueData = [] + ): ProductInterface { + $optionData[Option::KEY_PRODUCT_SKU] = $product->getSku(); + + if (!empty($optionValueData)) { + $optionValueData = $this->productCustomOptionValuesFactory->create(['data' => $optionValueData]); + $optionData['values'] = [$optionValueData]; + } + + $option = $this->productCustomOptionFactory->create(['data' => $optionData]); + $product->setOptions([$option]); + + return $this->productRepository->save($product); + } + + /** + * Render custom options block. + * + * @param ProductInterface $product + * @return string + */ + private function getOptionHtml(ProductInterface $product): string + { + $optionsBlock = $this->getOptionsBlock(); + $optionsBlock->setProduct($product); + + return $optionsBlock->toHtml(); + } + + /** + * Get options block. + * + * @return Options + */ + private function getOptionsBlock(): Options + { + $this->page->addHandle($this->getHandlesList()); + $this->page->getLayout()->generateXml(); + /** @var Template $productInfoFormOptionsBlock */ + $productInfoFormOptionsBlock = $this->page->getLayout()->getBlock('product.info.form.options'); + $optionsWrapperBlock = $productInfoFormOptionsBlock->getChildBlock('product_options_wrapper'); + + return $optionsWrapperBlock->getChildBlock('product_options'); + } + + /** + * Find and return custom option. + * + * @param ProductInterface $product + * @param string $optionTitle + * @return null|Option + */ + private function findOptionByTitle(ProductInterface $product, string $optionTitle): ?Option + { + $option = null; + foreach ($product->getOptions() as $customOption) { + if ($customOption->getTitle() === $optionTitle) { + $option = $customOption; + break; + } + } + + return $option; + } + + /** + * Return all need handles for load. + * + * @return array + */ + abstract protected function getHandlesList(): array; +} diff --git a/dev/tests/integration/testsuite/Magento/Catalog/Block/Product/View/Options/RenderOptionsTest.php b/dev/tests/integration/testsuite/Magento/Catalog/Block/Product/View/Options/RenderOptionsTest.php new file mode 100644 index 0000000000000..da31cfc74476a --- /dev/null +++ b/dev/tests/integration/testsuite/Magento/Catalog/Block/Product/View/Options/RenderOptionsTest.php @@ -0,0 +1,92 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +declare(strict_types=1); + +namespace Magento\Catalog\Block\Product\View\Options; + +/** + * Test cases related to check that simple product custom option renders as expected. + * + * @magentoDbIsolation disabled + * @magentoAppArea frontend + */ +class RenderOptionsTest extends AbstractRenderCustomOptionsTest +{ + /** + * Check that options from text group(field, area) render as expected. + * + * @magentoDataFixture Magento/Catalog/_files/product_without_options_with_stock_data.php + * @dataProvider \Magento\TestFramework\Catalog\Block\Product\View\Options\TextGroupDataProvider::getData + * + * @param array $optionData + * @param array $checkArray + * @return void + */ + public function testRenderCustomOptionsFromTextGroup(array $optionData, array $checkArray): void + { + $this->assertTextOptionRenderingOnProduct('simple', $optionData, $checkArray); + } + + /** + * Check that options from file group(file) render as expected. + * + * @magentoDataFixture Magento/Catalog/_files/product_without_options_with_stock_data.php + * @dataProvider \Magento\TestFramework\Catalog\Block\Product\View\Options\FileGroupDataProvider::getData + * + * @param array $optionData + * @param array $checkArray + * @return void + */ + public function testRenderCustomOptionsFromFileGroup(array $optionData, array $checkArray): void + { + $this->assertFileOptionRenderingOnProduct('simple', $optionData, $checkArray); + } + + /** + * Check that options from select group(drop-down, radio buttons, checkbox, multiple select) render as expected. + * + * @magentoDataFixture Magento/Catalog/_files/product_without_options_with_stock_data.php + * @dataProvider \Magento\TestFramework\Catalog\Block\Product\View\Options\SelectGroupDataProvider::getData + * + * @param array $optionData + * @param array $optionValueData + * @param array $checkArray + * @return void + */ + public function testRenderCustomOptionsFromSelectGroup( + array $optionData, + array $optionValueData, + array $checkArray + ): void { + $this->assertSelectOptionRenderingOnProduct('simple', $optionData, $optionValueData, $checkArray); + } + + /** + * Check that options from date group(date, date & time, time) render as expected. + * + * @magentoDataFixture Magento/Catalog/_files/product_without_options_with_stock_data.php + * @dataProvider \Magento\TestFramework\Catalog\Block\Product\View\Options\DateGroupDataProvider::getData + * + * @param array $optionData + * @param array $checkArray + * @return void + */ + public function testRenderCustomOptionsFromDateGroup(array $optionData, array $checkArray): void + { + $this->assertDateOptionRenderingOnProduct('simple', $optionData, $checkArray); + } + + /** + * @inheritdoc + */ + protected function getHandlesList(): array + { + return [ + 'default', + 'catalog_product_view', + ]; + } +} diff --git a/dev/tests/integration/testsuite/Magento/Catalog/Block/Product/View/OptionsTest.php b/dev/tests/integration/testsuite/Magento/Catalog/Block/Product/View/OptionsTest.php index 152ad6a6286f4..3d767502dd784 100644 --- a/dev/tests/integration/testsuite/Magento/Catalog/Block/Product/View/OptionsTest.php +++ b/dev/tests/integration/testsuite/Magento/Catalog/Block/Product/View/OptionsTest.php @@ -3,8 +3,12 @@ * Copyright © Magento, Inc. All rights reserved. * See COPYING.txt for license details. */ +declare(strict_types=1); + namespace Magento\Catalog\Block\Product\View; +use Magento\CatalogRule\Model\Indexer\IndexBuilder; + /** * Test class for \Magento\Catalog\Block\Product\View\Options. */ @@ -30,12 +34,19 @@ class OptionsTest extends \PHPUnit\Framework\TestCase */ protected $productRepository; + /** + * @var IndexBuilder + */ + private $indexBuilder; + protected function setUp() { $this->objectManager = \Magento\TestFramework\Helper\Bootstrap::getObjectManager(); $this->productRepository = $this->objectManager->create(\Magento\Catalog\Api\ProductRepositoryInterface::class); + $this->indexBuilder = $this->objectManager->create(IndexBuilder::class); + try { $this->product = $this->productRepository->get('simple'); } catch (\Magento\Framework\Exception\NoSuchEntityException $e) { @@ -113,24 +124,63 @@ private function getExpectedJsonConfig() { return [ 0 => [ - 'prices' => - ['oldPrice' => - ['amount' => 10, 'adjustments' => []], + 'prices' => ['oldPrice' => ['amount' => 10, 'adjustments' => []], 'basePrice' => ['amount' => 10], 'finalPrice' => ['amount' => 10] ], - 'type' => 'fixed', - 'name' => 'drop_down option 1', + 'type' => 'fixed', + 'name' => 'drop_down option 1', ], 1 => [ - 'prices' => - ['oldPrice' => - ['amount' => 40, 'adjustments' => []], + 'prices' => ['oldPrice' => ['amount' => 40, 'adjustments' => []], 'basePrice' => ['amount' => 40], 'finalPrice' => ['amount' => 40], ], - 'type' => 'percent', - 'name' => 'drop_down option 2', + 'type' => 'percent', + 'name' => 'drop_down option 2', + ], + ]; + } + + /** + * Test option prices with catalog price rules applied. + * + * @magentoDbIsolation disabled + * @magentoDataFixture Magento/CatalogRule/_files/two_rules.php + * @magentoDataFixture Magento/Catalog/_files/product_with_dropdown_option.php + */ + public function testGetJsonConfigWithCatalogRules() + { + $this->indexBuilder->reindexFull(); + sleep(1); + $config = json_decode($this->block->getJsonConfig(), true); + $configValues = array_values($config); + $this->assertEquals($this->getExpectedJsonConfigWithCatalogRules(), array_values($configValues[0])); + } + + /** + * Expected data for testGetJsonConfigWithCatalogRules + * + * @return array + */ + private function getExpectedJsonConfigWithCatalogRules() + { + return [ + 0 => [ + 'prices' => ['oldPrice' => ['amount' => 10, 'adjustments' => []], + 'basePrice' => ['amount' => 9.5], + 'finalPrice' => ['amount' => 9.5], + ], + 'type' => 'fixed', + 'name' => 'drop_down option 1', + ], + 1 => [ + 'prices' => ['oldPrice' => ['amount' => 40, 'adjustments' => []], + 'basePrice' => ['amount' => 38], + 'finalPrice' => ['amount' => 38], + ], + 'type' => 'percent', + 'name' => 'drop_down option 2', ], ]; } diff --git a/dev/tests/integration/testsuite/Magento/Catalog/Block/Product/View/SingleStoreCurrencyTest.php b/dev/tests/integration/testsuite/Magento/Catalog/Block/Product/View/SingleStoreCurrencyTest.php new file mode 100644 index 0000000000000..284d85ccc9ebd --- /dev/null +++ b/dev/tests/integration/testsuite/Magento/Catalog/Block/Product/View/SingleStoreCurrencyTest.php @@ -0,0 +1,64 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +declare(strict_types=1); + +namespace Magento\Catalog\Block\Product\View; + +/** + * Checks currency displaying and converting on the catalog pages + * + * @magentoAppArea frontend + */ +class SingleStoreCurrencyTest extends AbstractCurrencyTest +{ + /** + * @magentoConfigFixture current_store currency/options/base USD + * @magentoConfigFixture current_store currency/options/default CNY + * @magentoConfigFixture current_store currency/options/allow CNY,USD + * + * @magentoDataFixture Magento/Catalog/_files/second_product_simple.php + * @magentoDataFixture Magento/Directory/_files/usd_cny_rate.php + * + * @return void + */ + public function testRenderPrice(): void + { + $priceHtml = $this->processPriceView('simple2'); + $this->assertEquals('CN¥70.00', $priceHtml); + } + + /** + * @magentoConfigFixture current_store currency/options/base USD + * @magentoConfigFixture current_store currency/options/default CNY + * @magentoConfigFixture current_store currency/options/allow EUR,CNY + * + * @magentoDataFixture Magento/Catalog/_files/product_special_price.php + * @magentoDataFixture Magento/Directory/_files/usd_cny_rate.php + * + * @return void + */ + public function testRenderSpecialPrice(): void + { + $priceHtml = $this->processPriceView('simple'); + $this->assertEquals('Special Price CN¥41.93 Regular Price CN¥70.00', $priceHtml); + } + + /** + * @magentoConfigFixture current_store currency/options/base USD + * @magentoConfigFixture current_store currency/options/default CNY + * @magentoConfigFixture current_store currency/options/allow CNY,USD + * + * @magentoDataFixture Magento/Catalog/_files/product_simple_with_fixed_tier_price.php + * @magentoDataFixture Magento/Directory/_files/usd_cny_rate.php + * + * @return void + */ + public function testRenderTierPrice(): void + { + $priceHtml = $this->processPriceView('simple-product-tax-none', self::TIER_PRICE_BLOCK_NAME); + $this->assertEquals('Buy 2 for CN¥280.00 each and save 80%', $priceHtml); + } +} diff --git a/dev/tests/integration/testsuite/Magento/Catalog/Controller/Adminhtml/Category/Delete/DeleteCategoryWithEnabledFlatTest.php b/dev/tests/integration/testsuite/Magento/Catalog/Controller/Adminhtml/Category/Delete/DeleteCategoryWithEnabledFlatTest.php new file mode 100644 index 0000000000000..efa19b98d077f --- /dev/null +++ b/dev/tests/integration/testsuite/Magento/Catalog/Controller/Adminhtml/Category/Delete/DeleteCategoryWithEnabledFlatTest.php @@ -0,0 +1,136 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +declare(strict_types=1); + +namespace Magento\Catalog\Controller\Adminhtml\Category\Delete; + +use Magento\Catalog\Api\CategoryRepositoryInterface; +use Magento\Catalog\Model\Indexer\Category\Flat\State; +use Magento\Catalog\Model\ResourceModel\Category\Flat as CategoryFlatResource; +use Magento\Catalog\Model\ResourceModel\Category\Flat\CollectionFactory; +use Magento\Framework\App\Request\Http as HttpRequest; +use Magento\Framework\Exception\NoSuchEntityException; +use Magento\Framework\Indexer\IndexerRegistry; +use Magento\TestFramework\TestCase\AbstractBackendController; + +/** + * Test cases related to delete category with enabled category flat. + * + * @magentoAppArea adminhtml + * @magentoDbIsolation disabled + */ +class DeleteCategoryWithEnabledFlatTest extends AbstractBackendController +{ + const STUB_CATEGORY_ID = 333; + + /** + * @var IndexerRegistry + */ + private $indexerRegistry; + + /** + * @var CategoryRepositoryInterface + */ + private $categoryRepository; + + /** + * @var CategoryFlatResource + */ + private $categoryFlatResource; + + /** + * @var CollectionFactory + */ + private $categoryFlatCollectionFactory; + + /** + * @inheritdoc + */ + protected function setUp() + { + parent::setUp(); + $this->indexerRegistry = $this->_objectManager->get(IndexerRegistry::class); + $this->categoryRepository = $this->_objectManager->get(CategoryRepositoryInterface::class); + $this->categoryFlatResource = $this->_objectManager->get(CategoryFlatResource::class); + $this->categoryFlatCollectionFactory = $this->_objectManager->get(CollectionFactory::class); + } + + /** + * @inheritdoc + */ + protected function tearDown() + { + parent::tearDown(); + $categoryFlatIndexer = $this->indexerRegistry->get(State::INDEXER_ID); + $categoryFlatIndexer->invalidate(); + $this->categoryFlatResource->getConnection()->dropTable($this->categoryFlatResource->getMainTable()); + } + + /** + * Check that product is deleted from flat table. + * + * @magentoConfigFixture current_store catalog/frontend/flat_catalog_category true + * + * @magentoDataFixture Magento/Catalog/_files/category.php + * @magentoDataFixture Magento/Catalog/_files/reindex_catalog_category_flat.php + * + * @return void + */ + public function testDeleteCategory(): void + { + // Given + $this->assertEquals(1, $this->getFlatCategoryCollectionSizeByCategoryId(self::STUB_CATEGORY_ID)); + + // When + $this->sendDeleteCategoryRequest(self::STUB_CATEGORY_ID); + + // Then + $this->assertSessionMessages($this->equalTo([(string)__('You deleted the category.')])); + $this->assertEquals(0, $this->getFlatCategoryCollectionSizeByCategoryId(self::STUB_CATEGORY_ID)); + $this->checkCategoryIsDeleted(self::STUB_CATEGORY_ID); + } + + /** + * Return collection size from category flat collection by category ID. + * + * @param int $categoryId + * @return int + */ + private function getFlatCategoryCollectionSizeByCategoryId(int $categoryId): int + { + $categoryFlatCollection = $this->categoryFlatCollectionFactory->create(); + $categoryFlatCollection->addIdFilter($categoryId); + + return $categoryFlatCollection->getSize(); + } + + /** + * Assert that category is deleted. + * + * @param int $categoryId + * @return void + */ + private function checkCategoryIsDeleted(int $categoryId): void + { + $this->expectExceptionObject( + new NoSuchEntityException(__("No such entity with id = %entityId", ['entityId' => $categoryId])) + ); + $this->categoryRepository->get($categoryId); + } + + /** + * Method passes the request to Backend to remove given category. + * + * @param int $categoryId + * @return void + */ + private function sendDeleteCategoryRequest(int $categoryId): void + { + $this->getRequest()->setMethod(HttpRequest::METHOD_POST); + $this->getRequest()->setPostValue(['id' => $categoryId]); + $this->dispatch('backend/catalog/category/delete'); + } +} diff --git a/dev/tests/integration/testsuite/Magento/Catalog/Controller/Adminhtml/Category/Save/AbstractSaveCategoryTest.php b/dev/tests/integration/testsuite/Magento/Catalog/Controller/Adminhtml/Category/Save/AbstractSaveCategoryTest.php new file mode 100644 index 0000000000000..e472220896af9 --- /dev/null +++ b/dev/tests/integration/testsuite/Magento/Catalog/Controller/Adminhtml/Category/Save/AbstractSaveCategoryTest.php @@ -0,0 +1,62 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +declare(strict_types=1); + +namespace Magento\Catalog\Controller\Adminhtml\Category\Save; + +use Magento\Framework\App\Request\Http as HttpRequest; +use Magento\Framework\Serialize\SerializerInterface; +use Magento\TestFramework\TestCase\AbstractBackendController; + +/** + * Abstract save category. + */ +class AbstractSaveCategoryTest extends AbstractBackendController +{ + /** + * @var SerializerInterface + */ + private $serializer; + + /** + * @inheritdoc + */ + protected function setUp() + { + parent::setUp(); + $this->serializer = $this->_objectManager->get(SerializerInterface::class); + } + + /** + * Perform save category request with category POST data. + * + * @param array $data + * @return array + */ + protected function performSaveCategoryRequest(array $data): array + { + $data['return_session_messages_only'] = true; + $this->getRequest()->setMethod(HttpRequest::METHOD_POST); + $this->getRequest()->setPostValue($data); + $this->dispatch('backend/catalog/category/save'); + + return $this->serializer->unserialize($this->getResponse()->getBody()); + } + + /** + * Assert that session has message about successfully category save. + * + * @param array $responseData + * @return void + */ + protected function assertRequestIsSuccessfullyPerformed(array $responseData): void + { + $this->assertTrue(isset($responseData['category']['entity_id'])); + $this->assertFalse($responseData['error'], 'Response message: ' . $responseData['messages']); + $message = str_replace('.', '\.', (string)__('You saved the category.')); + $this->assertRegExp("/>{$message}</", $responseData['messages']); + } +} diff --git a/dev/tests/integration/testsuite/Magento/Catalog/Controller/Adminhtml/Category/Save/SaveCategoryWithEnabledFlatTest.php b/dev/tests/integration/testsuite/Magento/Catalog/Controller/Adminhtml/Category/Save/SaveCategoryWithEnabledFlatTest.php new file mode 100644 index 0000000000000..376e9865b4fb8 --- /dev/null +++ b/dev/tests/integration/testsuite/Magento/Catalog/Controller/Adminhtml/Category/Save/SaveCategoryWithEnabledFlatTest.php @@ -0,0 +1,268 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +declare(strict_types=1); + +namespace Magento\Catalog\Controller\Adminhtml\Category\Save; + +use Magento\Catalog\Api\CategoryRepositoryInterface; +use Magento\Catalog\Model\Category; +use Magento\Catalog\Model\Indexer\Category\Flat; +use Magento\Catalog\Model\Indexer\Category\Flat\State; +use Magento\Catalog\Model\ResourceModel\Category\Flat as CategoryFlatResource; +use Magento\Catalog\Model\ResourceModel\Category\Flat\CollectionFactory; +use Magento\CatalogUrlRewrite\Model\Map\DataCategoryUrlRewriteDatabaseMap; +use Magento\Framework\Exception\NoSuchEntityException; +use Magento\Framework\Indexer\IndexerRegistry; +use Magento\UrlRewrite\Model\ResourceModel\UrlRewrite as UrlRewriteResource; +use Magento\UrlRewrite\Service\V1\Data\UrlRewrite; + +/** + * Test cases related to save category with enabled category flat. + * + * @magentoAppArea adminhtml + * @magentoDbIsolation disabled + */ +class SaveCategoryWithEnabledFlatTest extends AbstractSaveCategoryTest +{ + /** + * @var IndexerRegistry + */ + private $indexerRegistry; + + /** + * @var UrlRewriteResource + */ + private $urlRewriteResource; + + /** + * @var CategoryRepositoryInterface + */ + private $categoryRepository; + + /** + * @var Flat + */ + private $categoryFlatIndexer; + + /** + * @var CategoryFlatResource + */ + private $categoryFlatResource; + + /** + * @var CollectionFactory + */ + private $categoryFlatCollectionFactory; + + /** + * @var string + */ + private $createdCategoryId; + + /** + * @inheritdoc + */ + protected function setUp() + { + parent::setUp(); + $this->indexerRegistry = $this->_objectManager->get(IndexerRegistry::class); + $this->urlRewriteResource = $this->_objectManager->get(UrlRewriteResource::class); + $this->categoryRepository = $this->_objectManager->get(CategoryRepositoryInterface::class); + $this->categoryFlatIndexer = $this->_objectManager->get(Flat::class); + $this->categoryFlatResource = $this->_objectManager->get(CategoryFlatResource::class); + $this->categoryFlatCollectionFactory = $this->_objectManager->get(CollectionFactory::class); + } + + /** + * @inheritdoc + */ + protected function tearDown() + { + parent::tearDown(); + $categoryFlatIndexer = $this->indexerRegistry->get(State::INDEXER_ID); + $categoryFlatIndexer->invalidate(); + $this->categoryFlatResource->getConnection()->dropTable($this->categoryFlatResource->getMainTable()); + $this->deleteAllCategoryUrlRewrites(); + try { + $this->categoryRepository->deleteByIdentifier($this->createdCategoryId); + } catch (NoSuchEntityException $e) { + //Category already deleted. + } + $this->createdCategoryId = null; + } + + /** + * Assert that category flat table is created and flat table contain category with created child category. + * + * @magentoDataFixture Magento/Catalog/_files/category.php + * + * @magentoConfigFixture current_store catalog/frontend/flat_catalog_category true + * + * @return void + */ + public function testAddChildCategory(): void + { + $parentCategory = $this->categoryRepository->get(333); + $postData = [ + 'name' => 'Custom category name', + 'parent' => 333, + 'is_active' => 1, + 'include_in_menu' => 1, + 'display_mode' => 'PRODUCTS', + 'is_anchor' => true, + 'use_config' => [ + 'available_sort_by' => 1, + 'default_sort_by' => 1, + 'filter_price_range' => 1, + ], + ]; + $responseData = $this->performSaveCategoryRequest($postData); + $this->assertRequestIsSuccessfullyPerformed($responseData); + $this->createdCategoryId = $responseData['category']['entity_id']; + $this->categoryFlatIndexer->executeFull(); + $this->assertTrue( + $this->categoryFlatResource->getConnection()->isTableExists($this->categoryFlatResource->getMainTable()) + ); + $this->assertEquals(1, $parentCategory->getChildrenCategories()->getSize()); + $categoryFlatCollection = $this->categoryFlatCollectionFactory->create(); + $categoryFlatCollection->addIdFilter([333, $this->createdCategoryId]); + $this->assertCount(2, $categoryFlatCollection->getItems()); + /** @var Category $createdCategory */ + $createdCategory = $categoryFlatCollection->getItemByColumnValue('entity_id', $this->createdCategoryId); + $this->assertEquals($parentCategory->getPath() . '/' . $this->createdCategoryId, $createdCategory->getPath()); + $this->assertEquals($parentCategory->getEntityId(), $createdCategory->getParentId()); + $this->assertEquals($parentCategory->getLevel() + 1, $createdCategory->getLevel()); + } + + /** + * Assert that category flat table is created and flat table contains category with expected data. + * + * @dataProvider enableCategoryDataProvider + * + * @magentoConfigFixture current_store catalog/frontend/flat_catalog_category true + * + * @param array $postData + * @param array $expectedData + * @return void + */ + public function testSaveCategoryWithData(array $postData, array $expectedData): void + { + $responseData = $this->performSaveCategoryRequest($postData); + $this->assertRequestIsSuccessfullyPerformed($responseData); + $this->createdCategoryId = $responseData['category']['entity_id']; + $this->categoryFlatIndexer->executeFull(); + $this->assertTrue( + $this->categoryFlatResource->getConnection()->isTableExists($this->categoryFlatResource->getMainTable()) + ); + $categoryFlatCollection = $this->categoryFlatCollectionFactory->create(); + $categoryFlatCollection->addAttributeToSelect(array_keys($expectedData)); + $categoryFlatCollection->addIdFilter($this->createdCategoryId); + $this->assertCount(1, $categoryFlatCollection->getItems()); + /** @var Category $createdCategory */ + $createdCategory = $categoryFlatCollection->getFirstItem(); + foreach ($expectedData as $fieldName => $value) { + $this->assertEquals($value, $createdCategory->getDataByKey($fieldName)); + } + } + + /** + * Data provider with create category POST data. + * + * @return array + */ + public function enableCategoryDataProvider(): array + { + return [ + 'category_is_enabled' => [ + [ + 'name' => 'Custom category name', + 'parent' => 2, + 'is_active' => 1, + 'include_in_menu' => 1, + 'display_mode' => 'PRODUCTS', + 'is_anchor' => true, + 'use_config' => [ + 'available_sort_by' => 1, + 'default_sort_by' => 1, + 'filter_price_range' => 1, + ], + ], + [ + 'is_active' => '1', + ], + ], + 'category_is_disabled' => [ + [ + 'name' => 'Custom category name', + 'parent' => 2, + 'is_active' => 0, + 'include_in_menu' => 1, + 'display_mode' => 'PRODUCTS', + 'is_anchor' => true, + 'use_config' => [ + 'available_sort_by' => 1, + 'default_sort_by' => 1, + 'filter_price_range' => 1, + ], + ], + [ + 'is_active' => '0' + ] + ], + 'include_in_menu_is_enabled' => [ + [ + 'name' => 'Custom category name', + 'parent' => 2, + 'is_active' => 1, + 'include_in_menu' => 1, + 'display_mode' => 'PRODUCTS', + 'is_anchor' => true, + 'use_config' => [ + 'available_sort_by' => 1, + 'default_sort_by' => 1, + 'filter_price_range' => 1, + ], + ], + [ + 'include_in_menu' => '1', + ], + ], + 'include_in_menu_is_disabled' => [ + [ + 'name' => 'Custom category name', + 'parent' => 2, + 'is_active' => 1, + 'include_in_menu' => 0, + 'display_mode' => 'PRODUCTS', + 'is_anchor' => true, + 'use_config' => [ + 'available_sort_by' => 1, + 'default_sort_by' => 1, + 'filter_price_range' => 1, + ], + ], + [ + 'include_in_menu' => '0', + ], + ], + ]; + } + + /** + * Delete all URL rewrite with entity type equal to "category". + * + * @return void + */ + private function deleteAllCategoryUrlRewrites(): void + { + $deleteCondition = $this->urlRewriteResource->getConnection() + ->quoteInto(UrlRewrite::ENTITY_TYPE . ' = ?', DataCategoryUrlRewriteDatabaseMap::ENTITY_TYPE); + $this->urlRewriteResource->getConnection()->delete( + $this->urlRewriteResource->getMainTable(), + $deleteCondition + ); + } +} diff --git a/dev/tests/integration/testsuite/Magento/Catalog/Controller/Adminhtml/Category/Save/UrlRewriteTest.php b/dev/tests/integration/testsuite/Magento/Catalog/Controller/Adminhtml/Category/Save/UrlRewriteTest.php index 90f354d90f17a..e9354d7116ae6 100644 --- a/dev/tests/integration/testsuite/Magento/Catalog/Controller/Adminhtml/Category/Save/UrlRewriteTest.php +++ b/dev/tests/integration/testsuite/Magento/Catalog/Controller/Adminhtml/Category/Save/UrlRewriteTest.php @@ -8,9 +8,6 @@ namespace Magento\Catalog\Controller\Adminhtml\Category\Save; use Magento\CatalogUrlRewrite\Model\Map\DataCategoryUrlRewriteDatabaseMap; -use Magento\Framework\App\Request\Http as HttpRequest; -use Magento\Framework\Serialize\Serializer\Json; -use Magento\TestFramework\TestCase\AbstractBackendController; use Magento\UrlRewrite\Model\ResourceModel\UrlRewriteCollectionFactory; use Magento\UrlRewrite\Service\V1\Data\UrlRewrite; @@ -20,23 +17,20 @@ * @magentoAppArea adminhtml * @magentoDbIsolation enabled */ -class UrlRewriteTest extends AbstractBackendController +class UrlRewriteTest extends AbstractSaveCategoryTest { - /** @var $urlRewriteCollectionFactory */ + /** + * @var UrlRewriteCollectionFactory + */ private $urlRewriteCollectionFactory; - /** @var Json */ - private $jsonSerializer; - /** - * @inheritDoc + * @inheritdoc */ protected function setUp() { parent::setUp(); - $this->urlRewriteCollectionFactory = $this->_objectManager->get(UrlRewriteCollectionFactory::class); - $this->jsonSerializer = $this->_objectManager->get(Json::class); } /** @@ -47,19 +41,14 @@ protected function setUp() */ public function testUrlRewrite(array $data): void { - $this->getRequest()->setMethod(HttpRequest::METHOD_POST); - $this->getRequest()->setPostValue($data); - $this->dispatch('backend/catalog/category/save'); - $categoryId = $this->jsonSerializer->unserialize($this->getResponse()->getBody())['category']['entity_id']; + $responseData = $this->performSaveCategoryRequest($data); + $this->assertRequestIsSuccessfullyPerformed($responseData); + $categoryId = $responseData['category']['entity_id']; $this->assertNotNull($categoryId, 'The category was not created'); $urlRewriteCollection = $this->urlRewriteCollectionFactory->create(); $urlRewriteCollection->addFieldToFilter(UrlRewrite::ENTITY_ID, ['eq' => $categoryId]) ->addFieldToFilter(UrlRewrite::ENTITY_TYPE, ['eq' => DataCategoryUrlRewriteDatabaseMap::ENTITY_TYPE]); - $this->assertCount( - 1, - $urlRewriteCollection->getItems(), - 'Wrong count of url rewrites was created' - ); + $this->assertEquals(1, $urlRewriteCollection->getSize(), 'Wrong count of url rewrites was created'); } /** @@ -77,7 +66,6 @@ public function categoryDataProvider(): array 'include_in_menu' => '1', 'display_mode' => 'PRODUCTS', 'is_anchor' => true, - 'return_session_messages_only' => true, 'use_config' => [ 'available_sort_by' => 1, 'default_sort_by' => 1, diff --git a/dev/tests/integration/testsuite/Magento/Catalog/Controller/Adminhtml/CategoryTest.php b/dev/tests/integration/testsuite/Magento/Catalog/Controller/Adminhtml/CategoryTest.php index e89e5aae92cf5..d4bcf576709fc 100644 --- a/dev/tests/integration/testsuite/Magento/Catalog/Controller/Adminhtml/CategoryTest.php +++ b/dev/tests/integration/testsuite/Magento/Catalog/Controller/Adminhtml/CategoryTest.php @@ -890,4 +890,70 @@ public function testSaveWithCustomBackendNameAction(): void MessageInterface::TYPE_ERROR ); } + + /** + * Verify that the category cannot be saved if category name can not be converted to Latin (like Thai) + * + * @return void + */ + public function testSaveWithThaiCategoryNameAction(): void + { + $categoryName = 'ประเภท'; + $errorMessage = 'Invalid URL key. The "%1" category name can not be used to generate Latin URL key. ' . + 'Please add URL key or change category name using Latin letters and numbers to avoid generating ' . + 'URL key issues.'; + $inputData = [ + 'name' => $categoryName, + 'use_config' => [ + 'available_sort_by' => 1, + 'default_sort_by' => 1 + ], + 'is_active' => '1', + 'include_in_menu' => '1', + ]; + $this->getRequest()->setMethod(HttpRequest::METHOD_POST); + $this->getRequest()->setPostValue($inputData); + $this->dispatch('backend/catalog/category/save'); + $this->assertSessionMessages( + $this->equalTo( + [ + (string)__($errorMessage, $categoryName) + ] + ), + MessageInterface::TYPE_ERROR + ); + } + + /** + * Verify that the category cannot be saved if category URL key can not be converted to Latin (like Thai) + * + * @return void + */ + public function testSaveWithThaiCategoryUrlKeyAction(): void + { + $categoryUrlKey = 'ประเภท'; + $errorMessage = 'Invalid URL key. The "%1" URL key can not be used to generate Latin URL key. ' . + 'Please use Latin letters and numbers to avoid generating URL key issues.'; + $inputData = [ + 'name' => 'category name', + 'url_key' => $categoryUrlKey, + 'use_config' => [ + 'available_sort_by' => 1, + 'default_sort_by' => 1 + ], + 'is_active' => '1', + 'include_in_menu' => '1', + ]; + $this->getRequest()->setMethod(HttpRequest::METHOD_POST); + $this->getRequest()->setPostValue($inputData); + $this->dispatch('backend/catalog/category/save'); + $this->assertSessionMessages( + $this->equalTo( + [ + (string)__($errorMessage, $categoryUrlKey) + ] + ), + MessageInterface::TYPE_ERROR + ); + } } diff --git a/dev/tests/integration/testsuite/Magento/Catalog/Controller/Adminhtml/Product/Attribute/Delete/AbstractDeleteAttributeControllerTest.php b/dev/tests/integration/testsuite/Magento/Catalog/Controller/Adminhtml/Product/Attribute/Delete/AbstractDeleteAttributeControllerTest.php new file mode 100644 index 0000000000000..6f1ff8567349f --- /dev/null +++ b/dev/tests/integration/testsuite/Magento/Catalog/Controller/Adminhtml/Product/Attribute/Delete/AbstractDeleteAttributeControllerTest.php @@ -0,0 +1,83 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +declare(strict_types=1); + +namespace Magento\Catalog\Controller\Adminhtml\Product\Attribute\Delete; + +use Magento\Catalog\Api\ProductAttributeRepositoryInterface; +use Magento\Framework\App\Request\Http; +use Magento\Framework\Exception\NoSuchEntityException; +use Magento\Framework\Message\MessageInterface; +use Magento\TestFramework\TestCase\AbstractBackendController; + +/** + * Abstract delete attribute test using catalog/product_attribute/delete controller action. + */ +abstract class AbstractDeleteAttributeControllerTest extends AbstractBackendController +{ + /** + * @var string + */ + protected $uri = 'backend/catalog/product_attribute/delete/attribute_id/%s'; + + /** + * @var ProductAttributeRepositoryInterface + */ + private $productAttributeRepository; + + /** + * @inheritdoc + */ + protected function setUp() + { + parent::setUp(); + $this->productAttributeRepository = $this->_objectManager->get(ProductAttributeRepositoryInterface::class); + } + + /** + * Delete attribute via controller action. + * + * @param string $attributeCode + * @return void + */ + protected function dispatchDeleteAttribute(string $attributeCode): void + { + $attribute = $this->productAttributeRepository->get($attributeCode); + $this->getRequest()->setMethod(Http::METHOD_POST); + $this->dispatch(sprintf($this->uri, $attribute->getAttributeId())); + $this->assertSessionMessages( + $this->equalTo([(string)__('You deleted the product attribute.')]), + MessageInterface::TYPE_SUCCESS + ); + } + + /** + * Assert that attribute is deleted from DB. + * + * @param string $attributeCode + * @return void + */ + protected function assertAttributeIsDeleted(string $attributeCode): void + { + $this->expectExceptionObject( + new NoSuchEntityException( + __( + 'The attribute with a "%1" attributeCode doesn\'t exist. Verify the attribute and try again.', + $attributeCode + ) + ) + ); + $this->productAttributeRepository->get($attributeCode); + } + + /** + * @inheritdoc + */ + public function testAclHasAccess() + { + $this->markTestIncomplete('AclHasAccess test is not complete'); + } +} diff --git a/dev/tests/integration/testsuite/Magento/Catalog/Controller/Adminhtml/Product/Attribute/Delete/CatalogAttributesControllerTest.php b/dev/tests/integration/testsuite/Magento/Catalog/Controller/Adminhtml/Product/Attribute/Delete/CatalogAttributesControllerTest.php new file mode 100644 index 0000000000000..e95737209cb7f --- /dev/null +++ b/dev/tests/integration/testsuite/Magento/Catalog/Controller/Adminhtml/Product/Attribute/Delete/CatalogAttributesControllerTest.php @@ -0,0 +1,145 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +declare(strict_types=1); + +namespace Magento\Catalog\Controller\Adminhtml\Product\Attribute\Delete; + +/** + * Delete catalog product attributes with input types like "media_image", "price", + * "date", "select", "multiselect", "textarea", "texteditor", "text" and "boolean". + * Attributes from Magento_Catalog and Magento_Eav modules. + * + * @magentoAppArea adminhtml + * @magentoDbIsolation enabled + */ +class CatalogAttributesControllerTest extends AbstractDeleteAttributeControllerTest +{ + /** + * Assert that attribute with input type "media_image" will be deleted + * after dispatch delete product attribute action. + * + * @magentoDataFixture Magento/Catalog/_files/product_image_attribute.php + * + * @return void + */ + public function testDeleteMediaImageAttribute(): void + { + $this->dispatchDeleteAttribute('image_attribute'); + $this->assertAttributeIsDeleted('image_attribute'); + } + + /** + * Assert that attribute with input type "price" will be deleted + * after dispatch delete product attribute action. + * + * @magentoDataFixture Magento/Catalog/_files/product_decimal_attribute.php + * + * @return void + */ + public function testDeletePriceAttribute(): void + { + $this->dispatchDeleteAttribute('decimal_attribute'); + $this->assertAttributeIsDeleted('decimal_attribute'); + } + + /** + * Assert that attribute with input type "date" will be deleted + * after dispatch delete product attribute action. + * + * @magentoDataFixture Magento/Catalog/_files/product_date_attribute.php + * + * @return void + */ + public function testDeleteDateAttribute(): void + { + $this->dispatchDeleteAttribute('date_attribute'); + $this->assertAttributeIsDeleted('date_attribute'); + } + + /** + * Assert that attribute with input type "select" will be deleted + * after dispatch delete product attribute action. + * + * @magentoDataFixture Magento/Catalog/_files/product_dropdown_attribute.php + * + * @return void + */ + public function testDeleteSelectAttribute(): void + { + $this->dispatchDeleteAttribute('dropdown_attribute'); + $this->assertAttributeIsDeleted('dropdown_attribute'); + } + + /** + * Assert that attribute with input type "multiselect" will be deleted + * after dispatch delete product attribute action. + * + * @magentoDataFixture Magento/Catalog/_files/multiselect_attribute.php + * + * @return void + */ + public function testDeleteMultiselectAttribute(): void + { + $this->dispatchDeleteAttribute('multiselect_attribute'); + $this->assertAttributeIsDeleted('multiselect_attribute'); + } + + /** + * Assert that attribute with input type "textarea" will be deleted + * after dispatch delete product attribute action. + * + * @magentoDataFixture Magento/Catalog/_files/product_text_attribute.php + * + * @return void + */ + public function testDeleteTextareaAttribute(): void + { + $this->dispatchDeleteAttribute('text_attribute'); + $this->assertAttributeIsDeleted('text_attribute'); + } + + /** + * Assert that attribute with input type "texteditor" will be deleted + * after dispatch delete product attribute action. + * + * @magentoDataFixture Magento/Eav/_files/product_texteditor_attribute.php + * + * @return void + */ + public function testDeleteTextEditorAttribute(): void + { + $this->dispatchDeleteAttribute('text_editor_attribute'); + $this->assertAttributeIsDeleted('text_editor_attribute'); + } + + /** + * Assert that attribute with input type "text" will be deleted + * after dispatch delete product attribute action. + * + * @magentoDataFixture Magento/Catalog/_files/product_varchar_attribute.php + * + * @return void + */ + public function testDeleteTextAttribute(): void + { + $this->dispatchDeleteAttribute('varchar_attribute'); + $this->assertAttributeIsDeleted('varchar_attribute'); + } + + /** + * Assert that attribute with input type "boolean" will be deleted + * after dispatch delete product attribute action. + * + * @magentoDataFixture Magento/Catalog/_files/product_boolean_attribute.php + * + * @return void + */ + public function testDeleteBooleanAttribute(): void + { + $this->dispatchDeleteAttribute('boolean_attribute'); + $this->assertAttributeIsDeleted('boolean_attribute'); + } +} diff --git a/dev/tests/integration/testsuite/Magento/Catalog/Controller/Adminhtml/Product/Attribute/Delete/DeleteAttributeControllerErrorTest.php b/dev/tests/integration/testsuite/Magento/Catalog/Controller/Adminhtml/Product/Attribute/Delete/DeleteAttributeControllerErrorTest.php new file mode 100644 index 0000000000000..31aef9c85b9bd --- /dev/null +++ b/dev/tests/integration/testsuite/Magento/Catalog/Controller/Adminhtml/Product/Attribute/Delete/DeleteAttributeControllerErrorTest.php @@ -0,0 +1,79 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +declare(strict_types=1); + +namespace Magento\Catalog\Controller\Adminhtml\Product\Attribute\Delete; + +use Magento\Catalog\Model\Category; +use Magento\Eav\Api\AttributeRepositoryInterface; +use Magento\Framework\App\Request\Http; +use Magento\Framework\Escaper; +use Magento\Framework\Message\MessageInterface; + +/** + * Error during delete attribute using catalog/product_attribute/delete controller action. + * + * @magentoAppArea adminhtml + * @magentoDbIsolation enabled + */ +class DeleteAttributeControllerErrorTest extends AbstractDeleteAttributeControllerTest +{ + /** + * @var Escaper + */ + private $escaper; + + /** + * @var AttributeRepositoryInterface + */ + private $attributeRepository; + + /** + * @inheritdoc + */ + protected function setUp() + { + parent::setUp(); + $this->escaper = $this->_objectManager->get(Escaper::class); + $this->attributeRepository = $this->_objectManager->get(AttributeRepositoryInterface::class); + } + + /** + * Try to delete attribute via controller action without attribute ID. + * + * @return void + */ + public function testDispatchWithoutAttributeId(): void + { + $this->getRequest()->setMethod(Http::METHOD_POST); + $this->dispatch(sprintf($this->uri, '')); + $this->assertSessionMessages( + $this->equalTo([$this->escaper->escapeHtml((string)__('We can\'t find an attribute to delete.'))]), + MessageInterface::TYPE_ERROR + ); + } + + /** + * Try to delete category attribute via controller action. + * + * @magentoDataFixture Magento/Catalog/_files/category_attribute.php + * + * @return void + */ + public function testDispatchWithNonProductAttribute(): void + { + $categoryAttribute = $this->attributeRepository->get( + Category::ENTITY, + 'test_attribute_code_666' + ); + $this->getRequest()->setMethod(Http::METHOD_POST); + $this->dispatch(sprintf($this->uri, $categoryAttribute->getAttributeId())); + $this->assertSessionMessages( + $this->equalTo([$this->escaper->escapeHtml((string)__('We can\'t delete the attribute.'))]), + MessageInterface::TYPE_ERROR + ); + } +} diff --git a/dev/tests/integration/testsuite/Magento/Catalog/Controller/Adminhtml/Product/Attribute/Save/InputType/AbstractSaveAttributeTest.php b/dev/tests/integration/testsuite/Magento/Catalog/Controller/Adminhtml/Product/Attribute/Save/AbstractSaveAttributeTest.php similarity index 86% rename from dev/tests/integration/testsuite/Magento/Catalog/Controller/Adminhtml/Product/Attribute/Save/InputType/AbstractSaveAttributeTest.php rename to dev/tests/integration/testsuite/Magento/Catalog/Controller/Adminhtml/Product/Attribute/Save/AbstractSaveAttributeTest.php index d0f1256f1fdb7..91650d4b7444e 100644 --- a/dev/tests/integration/testsuite/Magento/Catalog/Controller/Adminhtml/Product/Attribute/Save/InputType/AbstractSaveAttributeTest.php +++ b/dev/tests/integration/testsuite/Magento/Catalog/Controller/Adminhtml/Product/Attribute/Save/AbstractSaveAttributeTest.php @@ -5,11 +5,10 @@ */ declare(strict_types=1); -namespace Magento\Catalog\Controller\Adminhtml\Product\Attribute\Save\InputType; +namespace Magento\Catalog\Controller\Adminhtml\Product\Attribute\Save; -use Magento\Catalog\Api\Data\ProductAttributeInterface; use Magento\Catalog\Api\ProductAttributeOptionManagementInterface; -use Magento\Eav\Api\AttributeRepositoryInterface; +use Magento\Catalog\Api\ProductAttributeRepositoryInterface; use Magento\Eav\Api\Data\AttributeInterface; use Magento\Eav\Model\Entity\Attribute\AbstractAttribute; use Magento\Framework\App\Request\Http as HttpRequest; @@ -22,27 +21,21 @@ /** * Base create and assert attribute data. + * + * @SuppressWarnings(PHPMD.CouplingBetweenObjects) */ abstract class AbstractSaveAttributeTest extends AbstractBackendController { - /** - * @var AttributeRepositoryInterface - */ - protected $attributeRepository; + /** @var ProductAttributeRepositoryInterface */ + protected $productAttributeRepository; - /** - * @var Escaper - */ + /** @var Escaper */ protected $escaper; - /** - * @var Json - */ + /** @var Json */ protected $jsonSerializer; - /** - * @var ProductAttributeOptionManagementInterface - */ + /** @var ProductAttributeOptionManagementInterface */ protected $productAttributeOptionManagement; /** @@ -51,12 +44,12 @@ abstract class AbstractSaveAttributeTest extends AbstractBackendController protected function setUp() { parent::setUp(); - $this->attributeRepository = $this->_objectManager->get(AttributeRepositoryInterface::class); $this->escaper = $this->_objectManager->get(Escaper::class); $this->jsonSerializer = $this->_objectManager->get(Json::class); $this->productAttributeOptionManagement = $this->_objectManager->get( ProductAttributeOptionManagementInterface::class ); + $this->productAttributeRepository = $this->_objectManager->get(ProductAttributeRepositoryInterface::class); } /** @@ -73,15 +66,15 @@ protected function createAttributeUsingDataAndAssert(array $attributeData, array if (isset($attributeData['serialized_options_arr'])) { $attributeData['serialized_options'] = $this->serializeOptions($attributeData['serialized_options_arr']); } - $this->createAttributeViaController($attributeData); + $this->dispatchAttributeSave($attributeData); $this->assertSessionMessages( $this->equalTo([(string)__('You saved the product attribute.')]), MessageInterface::TYPE_SUCCESS ); try { - $attribute = $this->attributeRepository->get(ProductAttributeInterface::ENTITY_TYPE_CODE, $attributeCode); + $attribute = $this->productAttributeRepository->get($attributeCode); $this->assertAttributeData($attribute, $attributeData, $checkArray); - $this->attributeRepository->delete($attribute); + $this->productAttributeRepository->delete($attribute); } catch (NoSuchEntityException $e) { $this->fail("Attribute with code {$attributeCode} was not created."); } @@ -101,15 +94,15 @@ protected function createAttributeUsingDataWithErrorAndAssert(array $attributeDa ) { $attributeData['serialized_options'] = $this->serializeOptions($attributeData['serialized_options_arr']); } - $this->createAttributeViaController($attributeData); + $this->dispatchAttributeSave($attributeData); $this->assertSessionMessages( $this->equalTo([$this->escaper->escapeHtml($errorMessage)]), MessageInterface::TYPE_ERROR ); $attributeCode = $this->getAttributeCodeFromAttributeData($attributeData); try { - $attribute = $this->attributeRepository->get(ProductAttributeInterface::ENTITY_TYPE_CODE, $attributeCode); - $this->attributeRepository->delete($attribute); + $attribute = $this->productAttributeRepository->get($attributeCode); + $this->productAttributeRepository->delete($attribute); } catch (NoSuchEntityException $e) { //Attribute already deleted. } @@ -191,7 +184,7 @@ private function getAttributeCodeFromAttributeData(array $attributeData): string * @param array $attributeData * @return void */ - private function createAttributeViaController(array $attributeData): void + private function dispatchAttributeSave(array $attributeData): void { $this->getRequest()->setMethod(HttpRequest::METHOD_POST); $this->getRequest()->setPostValue($attributeData); diff --git a/dev/tests/integration/testsuite/Magento/Catalog/Controller/Adminhtml/Product/Attribute/Save/InputType/DecimalTest.php b/dev/tests/integration/testsuite/Magento/Catalog/Controller/Adminhtml/Product/Attribute/Save/InputType/DecimalTest.php new file mode 100644 index 0000000000000..943f33b9c1800 --- /dev/null +++ b/dev/tests/integration/testsuite/Magento/Catalog/Controller/Adminhtml/Product/Attribute/Save/InputType/DecimalTest.php @@ -0,0 +1,47 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +declare(strict_types=1); + +namespace Magento\Catalog\Controller\Adminhtml\Product\Attribute\Save\InputType; + +use Magento\Catalog\Controller\Adminhtml\Product\Attribute\Save\AbstractSaveAttributeTest; + +/** + * Test cases related to create attribute with input type price. + * + * @magentoDbIsolation enabled + * @magentoAppArea adminhtml + */ +class DecimalTest extends AbstractSaveAttributeTest +{ + /** + * Test create attribute and compare attribute data and input data. + * + * @dataProvider \Magento\TestFramework\Catalog\Model\Product\Attribute\DataProvider\Decimal::getAttributeDataWithCheckArray + * + * @param array $attributePostData + * @param array $checkArray + * @return void + */ + public function testCreateAttribute(array $attributePostData, array $checkArray): void + { + $this->createAttributeUsingDataAndAssert($attributePostData, $checkArray); + } + + /** + * Test create attribute with error. + * + * @dataProvider \Magento\TestFramework\Catalog\Model\Product\Attribute\DataProvider\Decimal::getAttributeDataWithErrorMessage + * + * @param array $attributePostData + * @param string $errorMessage + * @return void + */ + public function testCreateAttributeWithError(array $attributePostData, string $errorMessage): void + { + $this->createAttributeUsingDataWithErrorAndAssert($attributePostData, $errorMessage); + } +} diff --git a/dev/tests/integration/testsuite/Magento/Catalog/Controller/Adminhtml/Product/Attribute/Save/InputType/MediaImageTest.php b/dev/tests/integration/testsuite/Magento/Catalog/Controller/Adminhtml/Product/Attribute/Save/InputType/MediaImageTest.php index f8adac2872773..c6500e03fa327 100644 --- a/dev/tests/integration/testsuite/Magento/Catalog/Controller/Adminhtml/Product/Attribute/Save/InputType/MediaImageTest.php +++ b/dev/tests/integration/testsuite/Magento/Catalog/Controller/Adminhtml/Product/Attribute/Save/InputType/MediaImageTest.php @@ -7,17 +7,20 @@ namespace Magento\Catalog\Controller\Adminhtml\Product\Attribute\Save\InputType; +use Magento\Catalog\Controller\Adminhtml\Product\Attribute\Save\AbstractSaveAttributeTest; + /** * Test cases related to create attribute with input type media image. * * @magentoDbIsolation enabled + * @magentoAppArea adminhtml */ class MediaImageTest extends AbstractSaveAttributeTest { /** * Test create attribute and compare attribute data and input data. * - * @dataProvider \Magento\TestFramework\Catalog\Model\Product\Attribute\DataProvider\MediaImage::getAttributeDataWithCheckArray() + * @dataProvider \Magento\TestFramework\Catalog\Model\Product\Attribute\DataProvider\MediaImage::getAttributeDataWithCheckArray * * @param array $attributePostData * @param array $checkArray @@ -31,7 +34,7 @@ public function testCreateAttribute(array $attributePostData, array $checkArray) /** * Test create attribute with error. * - * @dataProvider \Magento\TestFramework\Catalog\Model\Product\Attribute\DataProvider\MediaImage::getAttributeDataWithErrorMessage() + * @dataProvider \Magento\TestFramework\Catalog\Model\Product\Attribute\DataProvider\MediaImage::getAttributeDataWithErrorMessage * * @param array $attributePostData * @param string $errorMessage diff --git a/dev/tests/integration/testsuite/Magento/Catalog/Controller/Adminhtml/Product/Attribute/Save/InputType/PriceTest.php b/dev/tests/integration/testsuite/Magento/Catalog/Controller/Adminhtml/Product/Attribute/Save/InputType/PriceTest.php deleted file mode 100644 index fb71f0a4d9d76..0000000000000 --- a/dev/tests/integration/testsuite/Magento/Catalog/Controller/Adminhtml/Product/Attribute/Save/InputType/PriceTest.php +++ /dev/null @@ -1,44 +0,0 @@ -<?php -/** - * Copyright © Magento, Inc. All rights reserved. - * See COPYING.txt for license details. - */ -declare(strict_types=1); - -namespace Magento\Catalog\Controller\Adminhtml\Product\Attribute\Save\InputType; - -/** - * Test cases related to create attribute with input type price. - * - * @magentoDbIsolation enabled - */ -class PriceTest extends AbstractSaveAttributeTest -{ - /** - * Test create attribute and compare attribute data and input data. - * - * @dataProvider \Magento\TestFramework\Catalog\Model\Product\Attribute\DataProvider\Price::getAttributeDataWithCheckArray() - * - * @param array $attributePostData - * @param array $checkArray - * @return void - */ - public function testCreateAttribute(array $attributePostData, array $checkArray): void - { - $this->createAttributeUsingDataAndAssert($attributePostData, $checkArray); - } - - /** - * Test create attribute with error. - * - * @dataProvider \Magento\TestFramework\Catalog\Model\Product\Attribute\DataProvider\Price::getAttributeDataWithErrorMessage() - * - * @param array $attributePostData - * @param string $errorMessage - * @return void - */ - public function testCreateAttributeWithError(array $attributePostData, string $errorMessage): void - { - $this->createAttributeUsingDataWithErrorAndAssert($attributePostData, $errorMessage); - } -} diff --git a/dev/tests/integration/testsuite/Magento/Catalog/Controller/Adminhtml/Product/Attribute/Update/AbstractUpdateAttributeTest.php b/dev/tests/integration/testsuite/Magento/Catalog/Controller/Adminhtml/Product/Attribute/Update/AbstractUpdateAttributeTest.php new file mode 100644 index 0000000000000..60702d83bf4f7 --- /dev/null +++ b/dev/tests/integration/testsuite/Magento/Catalog/Controller/Adminhtml/Product/Attribute/Update/AbstractUpdateAttributeTest.php @@ -0,0 +1,472 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +declare(strict_types=1); + +namespace Magento\Catalog\Controller\Adminhtml\Product\Attribute\Update; + +use Magento\Catalog\Api\Data\ProductAttributeInterface; +use Magento\Catalog\Api\ProductAttributeRepositoryInterface; +use Magento\Catalog\Model\ResourceModel\Attribute as AttributeResource; +use Magento\Catalog\Model\ResourceModel\Eav\Attribute; +use Magento\Eav\Model\ResourceModel\Entity\Attribute\Option as OptionResource; +use Magento\Eav\Model\ResourceModel\Entity\Attribute\Option\CollectionFactory as OptionCollectionFactory; +use Magento\Framework\App\Request\Http as HttpRequest; +use Magento\Framework\Escaper; +use Magento\Framework\Message\MessageInterface; +use Magento\Framework\Serialize\Serializer\Json; +use Magento\Store\Model\StoreManagerInterface; +use Magento\TestFramework\Eav\Model\GetAttributeGroupByName; +use Magento\TestFramework\Eav\Model\GetAttributeSetByName; +use Magento\TestFramework\Eav\Model\ResourceModel\GetEntityIdByAttributeId; +use Magento\TestFramework\TestCase\AbstractBackendController; + +/** + * Base update and assert attribute data. + * + * @SuppressWarnings(PHPMD.CouplingBetweenObjects) + */ +abstract class AbstractUpdateAttributeTest extends AbstractBackendController +{ + /** @var ProductAttributeRepositoryInterface */ + protected $productAttributeRepository; + + /** @var Escaper */ + protected $escaper; + + /** @var Json */ + protected $jsonSerializer; + + /** @var GetAttributeSetByName */ + private $getAttributeSetByName; + + /** @var GetAttributeGroupByName */ + private $getAttributeGroupByName; + + /** @var GetEntityIdByAttributeId */ + private $getEntityIdByAttributeId; + + /** @var StoreManagerInterface */ + private $storeManager; + + /** @var OptionCollectionFactory */ + private $optionCollectionFactory; + + /** @var OptionResource */ + private $attributeOptionResource; + + /** + * @inheritdoc + */ + protected function setUp() + { + parent::setUp(); + $this->escaper = $this->_objectManager->get(Escaper::class); + $this->jsonSerializer = $this->_objectManager->get(Json::class); + $this->productAttributeRepository = $this->_objectManager->get(ProductAttributeRepositoryInterface::class); + $this->getAttributeSetByName = $this->_objectManager->get(GetAttributeSetByName::class); + $this->getAttributeGroupByName = $this->_objectManager->get(GetAttributeGroupByName::class); + $this->getEntityIdByAttributeId = $this->_objectManager->get(GetEntityIdByAttributeId::class); + $this->storeManager = $this->_objectManager->get(StoreManagerInterface::class); + $this->optionCollectionFactory = $this->_objectManager->get(OptionCollectionFactory::class); + $this->attributeOptionResource = $this->_objectManager->get(OptionResource::class); + } + + /** + * Updates attribute frontend labels on stores for a given attribute type. + * + * @param string $attributeCode + * @param array $postData + * @param array $expectedData + * @return void + */ + protected function processUpdateFrontendLabelOnStores( + string $attributeCode, + array $postData, + array $expectedData + ): void { + $this->setAttributeStorelabels($attributeCode); + if (is_array($postData['frontend_label'])) { + $postData['frontend_label'] = $this->prepareStoresData($postData['frontend_label']); + } + $expectedData['store_labels'] = $this->prepareStoresData($expectedData['store_labels']); + + $this->_objectManager->removeSharedInstance(AttributeResource::class); + $this->updateAttributeUsingData($attributeCode, $postData); + $this->assertUpdateAttributeProcess($attributeCode, $postData, $expectedData); + } + + /** + * Updates attribute options on stores for a given attribute type. + * + * @param string $attributeCode + * @param array $postData + * @return void + */ + protected function processUpdateOptionsOnStores(string $attributeCode, array $postData): void + { + $optionsData = $this->prepareStoreOptionsArray($attributeCode, $postData['options_array']); + $optionsPostData = $this->prepareStoreOptionsPostData($optionsData); + $postData['serialized_options'] = $this->serializeOptions($optionsPostData); + $expectedData = $this->prepareStoreOptionsExpectedData($optionsData); + + $this->_objectManager->removeSharedInstance(AttributeResource::class); + $this->updateAttributeUsingData($attributeCode, $postData); + $this->assertUpdateAttributeProcess($attributeCode, $postData, $expectedData); + } + + /** + * Prepare an array of values by store - replace store code with store identifier. + * + * @param array $storesData + * @return array + */ + protected function prepareStoresData(array $storesData): array + { + $storeIdsData = []; + foreach ($storesData as $storeId => $label) { + $store = $this->storeManager->getStore($storeId); + $storeIdsData[$store->getId()] = $label; + } + + return $storeIdsData; + } + + /** + * Update attribute via save product attribute controller. + * + * @param string $attributeCode + * @param array $postData + * @return void + */ + protected function updateAttributeUsingData(string $attributeCode, array $postData): void + { + $attributeId = $postData['attribute_id'] ?? $this->productAttributeRepository->get($attributeCode)->getId(); + $this->dispatchAttributeSave($postData, (int)$attributeId); + } + + /** + * Replace the store code with an identifier in the array of option values + * + * @param array $optionsArray + * @return array + */ + protected function replaceStoreCodeWithId(array $optionsArray): array + { + foreach ($optionsArray as $key => $option) { + $optionsArray[$key]['value'] = $this->prepareStoresData($option['value']); + } + + return $optionsArray; + } + + /** + * Prepare an array of attribute option values that will be saved. + * + * @param string $attributeCode + * @param array $optionsArray + * @return array + */ + protected function prepareStoreOptionsArray(string $attributeCode, array $optionsArray): array + { + $attribute = $this->productAttributeRepository->get($attributeCode); + $replacedOptionsArray = $this->replaceStoreCodeWithId($optionsArray); + $actualOptionsData = $this->getActualOptionsData($attribute->getId()); + $labeledOptionsData = []; + $optionLabelIds = []; + $i = 1; + foreach ($actualOptionsData as $optionId => $optionData) { + $optionLabelIds['option_' . $i] = $optionId; + $labeledOptionsData['option_' . $i] = $optionData; + $i++; + } + + $combineOptionsData = array_replace_recursive($labeledOptionsData, $replacedOptionsArray); + $optionsData = []; + foreach ($optionLabelIds as $optionLabel => $optionId) { + $optionsData[$optionId] = $combineOptionsData[$optionLabel]; + } + + return $optionsData; + } + + /** + * Get actual attribute options data. + * + * @param string $attributeId + * @return array + */ + protected function getActualOptionsData(string $attributeId): array + { + $attributeOptions = $this->getAttributeOptions($attributeId); + $actualOptionsData = []; + foreach ($attributeOptions as $optionId => $option) { + $actualOptionsData[$optionId] = [ + 'order' => $option->getSortOrder(), + 'value' => $this->getAttributeOptionValues($optionId), + ]; + } + + return $actualOptionsData; + } + + /** + * Prepare an array of attribute option values for sending via post parameters. + * + * @param array $optionsData + * @return array + */ + protected function prepareStoreOptionsPostData(array $optionsData): array + { + $optionsPostData = []; + foreach ($optionsData as $optionId => $option) { + $optionsPostData[$optionId]['option'] = [ + 'order' => [ + $optionId => $option['order'], + ], + 'value' => [ + $optionId => $option['value'], + ], + 'delete' => [ + $optionId => $option['delete'] ?? '', + ], + ]; + if (isset($option['default'])) { + $optionsPostData[$optionId]['default'][] = $optionId; + } + } + + return $optionsPostData; + } + + /** + * Prepare an array of attribute option values for verification after saving the attribute. + * + * @param array $optionsData + * @return array + */ + protected function prepareStoreOptionsExpectedData(array $optionsData): array + { + $optionsArray = []; + $defaultValue = ''; + + foreach ($optionsData as $optionId => $option) { + if (!empty($option['delete'])) { + continue; + } + $optionsArray[$optionId] = [ + 'order' => $option['order'], + 'value' => $option['value'], + ]; + if (isset($option['default'])) { + $defaultValue = $optionId; + } + } + + return [ + 'options_array' => $optionsArray, + 'default_value' => $defaultValue, + ]; + } + + /** + * Assert that attribute update correctly. + * + * @param string $attributeCode + * @param array $postData + * @param array $expectedData + * @return void + */ + protected function assertUpdateAttributeProcess(string $attributeCode, array $postData, array $expectedData): void + { + $this->assertSessionMessages( + $this->equalTo([(string)__('You saved the product attribute.')]), + MessageInterface::TYPE_SUCCESS + ); + $updatedAttribute = $this->productAttributeRepository->get($attributeCode); + if (isset($postData['new_attribute_set_name'])) { + $this->assertUpdateAttributeSet($updatedAttribute, $postData); + } elseif (isset($postData['options_array'])) { + $this->assertUpdateAttributeOptions($updatedAttribute, $expectedData['options_array']); + unset($expectedData['options_array']); + $this->assertUpdateAttributeData($updatedAttribute, $expectedData); + } else { + $this->assertUpdateAttributeData($updatedAttribute, $expectedData); + } + } + + /** + * Check that attribute property values match expected values. + * + * @param ProductAttributeInterface $attribute + * @param array $expectedData + * @return void + */ + protected function assertUpdateAttributeData( + ProductAttributeInterface $attribute, + array $expectedData + ): void { + foreach ($expectedData as $key => $expectedValue) { + $this->assertEquals( + $expectedValue, + $attribute->getDataUsingMethod($key), + "Invalid expected value for $key field." + ); + } + } + + /** + * Checks that appropriate error message appears. + * + * @param string $errorMessage + * @return void + */ + protected function assertErrorSessionMessages(string $errorMessage): void + { + $this->assertSessionMessages( + $this->equalTo([$this->escaper->escapeHtml($errorMessage)]), + MessageInterface::TYPE_ERROR + ); + } + + /** + * Create or update attribute using catalog/product_attribute/save action. + * + * @param array $attributeData + * @param int|null $attributeId + * @return void + */ + private function dispatchAttributeSave(array $attributeData, ?int $attributeId = null): void + { + $this->getRequest()->setMethod(HttpRequest::METHOD_POST); + $this->getRequest()->setPostValue($attributeData); + if ($attributeId) { + $this->getRequest()->setParam('attribute_id', $attributeId); + } + $this->dispatch('backend/catalog/product_attribute/save'); + } + + /** + * Create serialized options string. + * + * @param array $optionsArr + * @return string + */ + private function serializeOptions(array $optionsArr): string + { + $resultArr = []; + + foreach ($optionsArr as $option) { + $resultArr[] = http_build_query($option); + } + + return $this->jsonSerializer->serialize($resultArr); + } + + /** + * Set default values of attribute store labels and save. + * + * @param string $attributeCode + * @return void + */ + private function setAttributeStoreLabels(string $attributeCode): void + { + $stores = $this->storeManager->getStores(); + $storeLabels = []; + foreach ($stores as $storeId => $store) { + $storeLabels[$storeId] = $store->getName(); + } + $attribute = $this->productAttributeRepository->get($attributeCode); + $attribute->setStoreLabels($storeLabels); + $this->productAttributeRepository->save($attribute); + } + + /** + * Check that the attribute update was successful after adding it to the + * new attribute set and new attribute group. + * + * @param ProductAttributeInterface|Attribute $attribute + * @param array $postData + * @return void + */ + private function assertUpdateAttributeSet( + ProductAttributeInterface $attribute, + array $postData + ): void { + $attributeSet = $this->getAttributeSetByName->execute($postData['new_attribute_set_name']); + $this->assertNotNull( + $attributeSet, + 'The attribute set ' . $postData['new_attribute_set_name'] . 'was not created' + ); + + $attributeGroup = $this->getAttributeGroupByName->execute((int)$attributeSet->getId(), $postData['groupName']); + $this->assertNotNull( + $attributeGroup, + 'The attribute group ' . $postData['groupName'] . 'was not created' + ); + + $entityAttributeId = $this->getEntityIdByAttributeId->execute( + (int)$attributeSet->getId(), + (int)$attribute->getId(), + (int)$attributeGroup->getId() + ); + + $this->assertNotNull( + $entityAttributeId, + 'The attribute set and attribute group for the current attribute have not been updated.' + ); + } + + /** + * Check that attribute options are saved correctly. + * + * @param ProductAttributeInterface|Attribute $attribute + * @param array $expectedData + * @return void + */ + private function assertUpdateAttributeOptions( + ProductAttributeInterface $attribute, + array $expectedData + ): void { + $actualOptionsData = $this->getActualOptionsData($attribute->getId()); + + $this->assertEquals($expectedData, $actualOptionsData, 'Expected attribute options does not match.'); + } + + /** + * Get attribute options by attribute id and store id. + * + * @param string $attributeId + * @param int|null $storeId + * @return array + */ + private function getAttributeOptions(string $attributeId, ?int $storeId = null): array + { + $attributeOptionCollection = $this->optionCollectionFactory->create(); + $attributeOptionCollection->setAttributeFilter($attributeId); + $attributeOptionCollection->setStoreFilter($storeId); + + return $attributeOptionCollection->getItems(); + } + + /** + * Get attribute option values by option id. + * + * @param int $optionId + * @return array + */ + private function getAttributeOptionValues(int $optionId): array + { + $connection = $this->attributeOptionResource->getConnection(); + $select = $connection->select() + ->from( + ['main_table' => $this->attributeOptionResource->getTable('eav_attribute_option_value')], + ['store_id','value'] + ) + ->where('main_table.option_id = ?', $optionId); + + return $connection->fetchPairs($select); + } +} diff --git a/dev/tests/integration/testsuite/Magento/Catalog/Controller/Adminhtml/Product/Attribute/Update/InputType/DecimalTest.php b/dev/tests/integration/testsuite/Magento/Catalog/Controller/Adminhtml/Product/Attribute/Update/InputType/DecimalTest.php new file mode 100644 index 0000000000000..0febc033592a6 --- /dev/null +++ b/dev/tests/integration/testsuite/Magento/Catalog/Controller/Adminhtml/Product/Attribute/Update/InputType/DecimalTest.php @@ -0,0 +1,67 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +declare(strict_types=1); + +namespace Magento\Catalog\Controller\Adminhtml\Product\Attribute\Update\InputType; + +use Magento\Catalog\Controller\Adminhtml\Product\Attribute\Update\AbstractUpdateAttributeTest; + +/** + * Test cases related to update attribute with input type price. + * + * @magentoDbIsolation enabled + * @magentoAppArea adminhtml + */ +class DecimalTest extends AbstractUpdateAttributeTest +{ + /** + * Test update attribute. + * + * @dataProvider \Magento\TestFramework\Catalog\Model\Product\Attribute\DataProvider\Decimal::getUpdateProvider + * @magentoDataFixture Magento/Catalog/_files/product_decimal_attribute.php + * + * @param array $postData + * @param array $expectedData + * @return void + */ + public function testUpdateAttribute(array $postData, array $expectedData): void + { + $this->updateAttributeUsingData('decimal_attribute', $postData); + $this->assertUpdateAttributeProcess('decimal_attribute', $postData, $expectedData); + } + + /** + * Test update attribute with error. + * + * @dataProvider \Magento\TestFramework\Catalog\Model\Product\Attribute\DataProvider\Decimal::getUpdateProviderWithErrorMessage + * @magentoDataFixture Magento/Catalog/_files/product_decimal_attribute.php + * + * @param array $postData + * @param string $errorMessage + * @return void + */ + public function testUpdateAttributeWithError(array $postData, string $errorMessage): void + { + $this->updateAttributeUsingData('decimal_attribute', $postData); + $this->assertErrorSessionMessages($errorMessage); + } + + /** + * Test update attribute frontend labels on stores. + * + * @dataProvider \Magento\TestFramework\Catalog\Model\Product\Attribute\DataProvider\Decimal::getUpdateFrontendLabelsProvider + * @magentoDataFixture Magento/Store/_files/second_website_with_two_stores.php + * @magentoDataFixture Magento/Catalog/_files/product_decimal_attribute.php + * + * @param array $postData + * @param array $expectedData + * @return void + */ + public function testUpdateFrontendLabelOnStores(array $postData, array $expectedData): void + { + $this->processUpdateFrontendLabelOnStores('decimal_attribute', $postData, $expectedData); + } +} diff --git a/dev/tests/integration/testsuite/Magento/Catalog/Controller/Adminhtml/Product/Attribute/Update/InputType/MediaImageTest.php b/dev/tests/integration/testsuite/Magento/Catalog/Controller/Adminhtml/Product/Attribute/Update/InputType/MediaImageTest.php new file mode 100644 index 0000000000000..806e690dfd5b7 --- /dev/null +++ b/dev/tests/integration/testsuite/Magento/Catalog/Controller/Adminhtml/Product/Attribute/Update/InputType/MediaImageTest.php @@ -0,0 +1,67 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +declare(strict_types=1); + +namespace Magento\Catalog\Controller\Adminhtml\Product\Attribute\Update\InputType; + +use Magento\Catalog\Controller\Adminhtml\Product\Attribute\Update\AbstractUpdateAttributeTest; + +/** + * Test cases related to update attribute with input type media image. + * + * @magentoDbIsolation enabled + * @magentoAppArea adminhtml + */ +class MediaImageTest extends AbstractUpdateAttributeTest +{ + /** + * Test update attribute. + * + * @dataProvider \Magento\TestFramework\Catalog\Model\Product\Attribute\DataProvider\MediaImage::getUpdateProvider + * @magentoDataFixture Magento/Catalog/_files/product_image_attribute.php + * + * @param array $postData + * @param array $expectedData + * @return void + */ + public function testUpdateAttribute(array $postData, array $expectedData): void + { + $this->updateAttributeUsingData('image_attribute', $postData); + $this->assertUpdateAttributeProcess('image_attribute', $postData, $expectedData); + } + + /** + * Test update attribute with error. + * + * @dataProvider \Magento\TestFramework\Catalog\Model\Product\Attribute\DataProvider\MediaImage::getUpdateProviderWithErrorMessage + * @magentoDataFixture Magento/Catalog/_files/product_image_attribute.php + * + * @param array $postData + * @param string $errorMessage + * @return void + */ + public function testUpdateAttributeWithError(array $postData, string $errorMessage): void + { + $this->updateAttributeUsingData('image_attribute', $postData); + $this->assertErrorSessionMessages($errorMessage); + } + + /** + * Test update attribute frontend labels on stores. + * + * @dataProvider \Magento\TestFramework\Catalog\Model\Product\Attribute\DataProvider\MediaImage::getUpdateFrontendLabelsProvider + * @magentoDataFixture Magento/Store/_files/second_website_with_two_stores.php + * @magentoDataFixture Magento/Catalog/_files/product_image_attribute.php + * + * @param array $postData + * @param array $expectedData + * @return void + */ + public function testUpdateFrontendLabelOnStores(array $postData, array $expectedData): void + { + $this->processUpdateFrontendLabelOnStores('image_attribute', $postData, $expectedData); + } +} diff --git a/dev/tests/integration/testsuite/Magento/Catalog/Controller/Adminhtml/Product/Save/AdvancedPricingTest.php b/dev/tests/integration/testsuite/Magento/Catalog/Controller/Adminhtml/Product/Save/AdvancedPricingTest.php new file mode 100644 index 0000000000000..da4cf6335fb05 --- /dev/null +++ b/dev/tests/integration/testsuite/Magento/Catalog/Controller/Adminhtml/Product/Save/AdvancedPricingTest.php @@ -0,0 +1,105 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +declare(strict_types=1); + +namespace Magento\Catalog\Controller\Adminhtml\Product\Save; + +use Magento\Catalog\Api\ProductRepositoryInterface; +use Magento\Customer\Api\Data\GroupInterface; +use Magento\Framework\App\Request\Http; +use Magento\Framework\Message\MessageInterface; +use Magento\TestFramework\TestCase\AbstractBackendController; + +/** + * Test cases for set advanced price to product. + * + * @magentoAppArea adminhtml + * @magentoDbIsolation enabled + */ +class AdvancedPricingTest extends AbstractBackendController +{ + /** + * @var ProductRepositoryInterface + */ + private $productRepository; + + /** + * @inheritdoc + */ + protected function setUp() + { + parent::setUp(); + $this->productRepository = $this->_objectManager->get(ProductRepositoryInterface::class); + } + + /** + * Assert that special price correctly saved to product. + * + * @magentoDataFixture Magento/Catalog/_files/product_without_options.php + * + * @return void + */ + public function testAddSpecialPriceToProduct(): void + { + $product = $this->productRepository->get('simple'); + $postData = [ + 'product' => [ + 'special_price' => 8, + ], + ]; + $this->assertNull($product->getSpecialPrice()); + $this->dispatchWithData((int)$product->getEntityId(), $postData); + $product = $this->productRepository->get('simple', false, null, true); + $this->assertEquals(8, $product->getSpecialPrice()); + } + + /** + * Assert that tier price correctly saved to product. + * + * @magentoDataFixture Magento/Catalog/_files/product_without_options.php + * + * @return void + */ + public function testAddTierPriceToProduct(): void + { + $product = $this->productRepository->get('simple'); + $postData = [ + 'product' => [ + 'tier_price' => [ + [ + 'website_id' => '0', + 'cust_group' => GroupInterface::CUST_GROUP_ALL, + 'price_qty' => '100', + 'price' => 5, + 'value_type' => 'fixed', + ] + ], + ], + ]; + $this->assertEquals(10, $product->getTierPrice(100)); + $this->dispatchWithData((int)$product->getEntityId(), $postData); + $product = $this->productRepository->get('simple', false, null, true); + $this->assertEquals(5, $product->getTierPrice(100)); + } + + /** + * Dispatch product save with data. + * + * @param int $productId + * @param array $productPostData + * @return void + */ + private function dispatchWithData(int $productId, array $productPostData): void + { + $this->getRequest()->setPostValue($productPostData); + $this->getRequest()->setMethod(Http::METHOD_POST); + $this->dispatch('backend/catalog/product/save/id/' . $productId); + $this->assertSessionMessages( + $this->contains('You saved the product.'), + MessageInterface::TYPE_SUCCESS + ); + } +} diff --git a/dev/tests/integration/testsuite/Magento/Catalog/Controller/Adminhtml/Product/Save/CategoryIndexTest.php b/dev/tests/integration/testsuite/Magento/Catalog/Controller/Adminhtml/Product/Save/CategoryIndexTest.php new file mode 100644 index 0000000000000..4d44afe831029 --- /dev/null +++ b/dev/tests/integration/testsuite/Magento/Catalog/Controller/Adminhtml/Product/Save/CategoryIndexTest.php @@ -0,0 +1,134 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +declare(strict_types=1); + +namespace Magento\Catalog\Controller\Adminhtml\Product\Save; + +use Magento\Catalog\Api\Data\ProductInterface; +use Magento\Catalog\Api\ProductRepositoryInterface; +use Magento\Catalog\Helper\DefaultCategory; +use Magento\Catalog\Model\Indexer\Category\Product\TableMaintainer; +use Magento\Catalog\Model\ResourceModel\Product as ProductResource; +use Magento\Framework\App\Request\Http; +use Magento\Framework\DB\Adapter\AdapterInterface; +use Magento\Framework\Message\MessageInterface; +use Magento\Store\Model\Store; +use Magento\TestFramework\TestCase\AbstractBackendController; + +/** + * Checks category product index in cases when category unassigned from product + * + * @magentoDataFixture Magento/Catalog/_files/category_product_assigned_to_website.php + * @magentoAppArea adminhtml + * @magentoDbIsolation disabled + */ +class CategoryIndexTest extends AbstractBackendController +{ + /** @var ProductRepositoryInterface */ + private $productRepository; + + /** @var ProductInterface */ + private $product; + + /** @var TableMaintainer */ + private $tableMaintainer; + + /** @var ProductResource */ + private $productResource; + + /** @var AdapterInterface */ + private $connection; + + /** @var DefaultCategory */ + private $defaultCategoryHelper; + /** + * @inheritdoc + */ + protected function setUp() + { + parent::setUp(); + + $this->productRepository = $this->_objectManager->get(ProductRepositoryInterface::class); + $this->product = $this->productRepository->get('product_with_category'); + $this->tableMaintainer = $this->_objectManager->create(TableMaintainer::class); + $this->productResource = $this->_objectManager->get(ProductResource::class); + $this->connection = $this->productResource->getConnection(); + $this->defaultCategoryHelper = $this->_objectManager->get(DefaultCategory::class); + } + + /** + * @return void + */ + public function testUnassignCategory(): void + { + $postData = $this->preparePostData(); + $this->dispatchSaveProductRequest($postData); + $this->assertEmpty($this->fetchIndexData()); + } + + /** + * @magentoDataFixture Magento/Catalog/_files/category_product_assigned_to_website.php + * @magentoDataFixture Magento/Catalog/_files/category.php + * + * @return void + */ + public function testReassignCategory(): void + { + $postData = $this->preparePostData(333); + $this->dispatchSaveProductRequest($postData); + $result = $this->fetchIndexData(); + $this->assertNotEmpty($result); + $this->assertEquals(333, reset($result)['category_id']); + } + + /** + * Perform request + * + * @param array $postData + * @return void + */ + private function dispatchSaveProductRequest(array $postData): void + { + $this->getRequest()->setPostValue($postData); + $this->getRequest()->setMethod(Http::METHOD_POST); + $this->dispatch('backend/catalog/product/save/id/' . $this->product->getEntityId()); + $this->assertSessionMessages($this->equalTo(['You saved the product.']), MessageInterface::TYPE_SUCCESS); + } + + /** + * Prepare data to request + * + * @param int|null $newCategoryId + * @return array + */ + private function preparePostData(?int $newCategoryId = null): array + { + $this->product->getWebsiteIds(); + $data = $this->product->getData(); + unset($data['entity_id'], $data['category_ids']); + if ($newCategoryId) { + $data['category_ids'] = [$newCategoryId]; + } + + return ['product' => $data]; + } + + /** + * Fetch data from category product index table + * + * @return array + */ + private function fetchIndexData(): array + { + $tableName = $this->tableMaintainer->getMainTable(Store::DISTRO_STORE_ID); + $select = $this->connection->select(); + $select->from(['index_table' => $tableName], 'index_table.category_id') + ->where('index_table.product_id = ?', $this->product->getId()) + ->where('index_table.category_id != ?', $this->defaultCategoryHelper->getId()); + + return $this->connection->fetchAll($select); + } +} diff --git a/dev/tests/integration/testsuite/Magento/Catalog/Controller/Adminhtml/Product/Save/CreateCustomOptionsTest.php b/dev/tests/integration/testsuite/Magento/Catalog/Controller/Adminhtml/Product/Save/CreateCustomOptionsTest.php index 80f15da647b25..a4631526bd4c5 100644 --- a/dev/tests/integration/testsuite/Magento/Catalog/Controller/Adminhtml/Product/Save/CreateCustomOptionsTest.php +++ b/dev/tests/integration/testsuite/Magento/Catalog/Controller/Adminhtml/Product/Save/CreateCustomOptionsTest.php @@ -19,9 +19,15 @@ * * @magentoAppArea adminhtml * @magentoDbIsolation enabled + * @magentoDataFixture Magento/Catalog/_files/product_without_options.php */ class CreateCustomOptionsTest extends AbstractBackendController { + /** + * @var string + */ + protected $productSku = 'simple'; + /** * @var ProductRepositoryInterface */ @@ -46,16 +52,16 @@ protected function setUp() /** * Test add to product custom option with type "field". * - * @magentoDataFixture Magento/Catalog/_files/product_without_options.php - * * @dataProvider productWithNewOptionsDataProvider * * @param array $productPostData + * + * @magentoDbIsolation enabled */ public function testSaveCustomOptionWithTypeField(array $productPostData): void { $this->getRequest()->setPostValue($productPostData); - $product = $this->productRepository->get('simple'); + $product = $this->productRepository->get($this->productSku); $this->getRequest()->setMethod(HttpRequest::METHOD_POST); $this->dispatch('backend/catalog/product/save/id/' . $product->getEntityId()); $this->assertSessionMessages( diff --git a/dev/tests/integration/testsuite/Magento/Catalog/Controller/Adminhtml/Product/Save/DeleteCustomOptionsTest.php b/dev/tests/integration/testsuite/Magento/Catalog/Controller/Adminhtml/Product/Save/DeleteCustomOptionsTest.php index f1af6e6e41cff..6a4ff066f710d 100644 --- a/dev/tests/integration/testsuite/Magento/Catalog/Controller/Adminhtml/Product/Save/DeleteCustomOptionsTest.php +++ b/dev/tests/integration/testsuite/Magento/Catalog/Controller/Adminhtml/Product/Save/DeleteCustomOptionsTest.php @@ -21,9 +21,15 @@ * * @magentoAppArea adminhtml * @magentoDbIsolation enabled + * @magentoDataFixture Magento/Catalog/_files/product_without_options.php */ class DeleteCustomOptionsTest extends AbstractBackendController { + /** + * @var string + */ + protected $productSku = 'simple'; + /** * @var ProductRepositoryInterface */ @@ -54,8 +60,6 @@ protected function setUp() /** * Test delete custom option with type "field". * - * @magentoDataFixture Magento/Catalog/_files/product_without_options.php - * * @dataProvider \Magento\TestFramework\Catalog\Model\Product\Option\DataProvider\Type\Field::getDataForCreateOptions * * @param array $optionData @@ -63,7 +67,7 @@ protected function setUp() */ public function testDeleteCustomOptionWithTypeField(array $optionData): void { - $product = $this->productRepository->get('simple'); + $product = $this->productRepository->get($this->productSku); /** @var ProductCustomOptionInterface $option */ $option = $this->optionRepositoryFactory->create(['data' => $optionData]); $option->setProductSku($product->getSku()); diff --git a/dev/tests/integration/testsuite/Magento/Catalog/Controller/Adminhtml/Product/Save/UpdateCustomOptionsTest.php b/dev/tests/integration/testsuite/Magento/Catalog/Controller/Adminhtml/Product/Save/UpdateCustomOptionsTest.php index a45c21444a5d7..1badf6a1a081a 100644 --- a/dev/tests/integration/testsuite/Magento/Catalog/Controller/Adminhtml/Product/Save/UpdateCustomOptionsTest.php +++ b/dev/tests/integration/testsuite/Magento/Catalog/Controller/Adminhtml/Product/Save/UpdateCustomOptionsTest.php @@ -22,9 +22,15 @@ * * @magentoAppArea adminhtml * @magentoDbIsolation enabled + * @magentoDataFixture Magento/Catalog/_files/product_without_options.php */ class UpdateCustomOptionsTest extends AbstractBackendController { + /** + * @var string + */ + protected $productSku = 'simple'; + /** * @var ProductRepositoryInterface */ @@ -55,8 +61,6 @@ protected function setUp() /** * Test add to product custom option with type "field". * - * @magentoDataFixture Magento/Catalog/_files/product_without_options.php - * * @dataProvider \Magento\TestFramework\Catalog\Model\Product\Option\DataProvider\Type\Field::getDataForUpdateOptions * * @param array $optionData @@ -65,7 +69,7 @@ protected function setUp() */ public function testUpdateCustomOptionWithTypeField(array $optionData, array $updateData): void { - $product = $this->productRepository->get('simple'); + $product = $this->productRepository->get($this->productSku); /** @var ProductCustomOptionInterface|Option $option */ $option = $this->optionRepositoryFactory->create(['data' => $optionData]); $option->setProductSku($product->getSku()); diff --git a/dev/tests/integration/testsuite/Magento/Catalog/Controller/Product/CompareTest.php b/dev/tests/integration/testsuite/Magento/Catalog/Controller/Product/CompareTest.php index 0c3e81fd52e81..c303fb1fe6e0c 100644 --- a/dev/tests/integration/testsuite/Magento/Catalog/Controller/Product/CompareTest.php +++ b/dev/tests/integration/testsuite/Magento/Catalog/Controller/Product/CompareTest.php @@ -96,22 +96,6 @@ public function testAddActionForDisabledProduct(): void $this->_assertCompareListEquals([]); } - /** - * Test comparing a product. - * - * @throws \Magento\Framework\Exception\NoSuchEntityException - */ - public function testIndexActionAddProducts() - { - $this->_requireVisitorWithNoProducts(); - $product = $this->productRepository->get('simple_product_2'); - $this->dispatch('catalog/product_compare/index/items/' . $product->getEntityId()); - - $this->assertRedirect($this->stringStartsWith('http://localhost/index.php/catalog/product_compare/index/')); - - $this->_assertCompareListEquals([$product->getEntityId()]); - } - /** * Test removing a product from compare list. * diff --git a/dev/tests/integration/testsuite/Magento/Catalog/Helper/Product/CompareTest.php b/dev/tests/integration/testsuite/Magento/Catalog/Helper/Product/CompareTest.php index f99344904f68e..6615815569fcf 100644 --- a/dev/tests/integration/testsuite/Magento/Catalog/Helper/Product/CompareTest.php +++ b/dev/tests/integration/testsuite/Magento/Catalog/Helper/Product/CompareTest.php @@ -23,24 +23,11 @@ protected function setUp() $this->_helper = $this->_objectManager->get(\Magento\Catalog\Helper\Product\Compare::class); } - /** - * @magentoDataFixture Magento/Catalog/_files/multiple_products.php - * @magentoDbIsolation disabled - */ public function testGetListUrl() { /** @var $empty \Magento\Catalog\Helper\Product\Compare */ $empty = $this->_objectManager->create(\Magento\Catalog\Helper\Product\Compare::class); $this->assertContains('/catalog/product_compare/index/', $empty->getListUrl()); - - $this->_populateCompareList(); - $productRepository = $this->_objectManager->create(\Magento\Catalog\Api\ProductRepositoryInterface::class); - $id1 = $productRepository->get('simple1')->getId(); - $id2 = $productRepository->get('simple2')->getId(); - $this->assertRegExp( - '#/catalog/product_compare/index/items/(?:' . $id1 . '%2C' . $id2 . '|' . $id2 . '%2C' . $id1. ')/#', - $this->_helper->getListUrl() - ); } public function testGetAddUrl() diff --git a/dev/tests/integration/testsuite/Magento/Catalog/Model/CategoryLinkManagementTest.php b/dev/tests/integration/testsuite/Magento/Catalog/Model/CategoryLinkManagementTest.php new file mode 100644 index 0000000000000..50d0ddbf5dccf --- /dev/null +++ b/dev/tests/integration/testsuite/Magento/Catalog/Model/CategoryLinkManagementTest.php @@ -0,0 +1,227 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +declare(strict_types=1); + +namespace Magento\Catalog\Model; + +use Magento\Catalog\Api\CategoryLinkManagementInterface; +use Magento\Catalog\Api\ProductRepositoryInterface; +use Magento\Catalog\Model\Indexer\Category\Product\TableMaintainer; +use Magento\Catalog\Model\ResourceModel\Category as CategoryResourceModel; +use Magento\Store\Api\StoreRepositoryInterface; +use Magento\TestFramework\Helper\Bootstrap; +use Magento\TestFramework\ObjectManager; +use Magento\UrlRewrite\Model\ResourceModel\UrlRewriteCollectionFactory; +use PHPUnit\Framework\TestCase; + +/** + * Test cases related to assign/unassign product to/from category. + */ +class CategoryLinkManagementTest extends TestCase +{ + /** + * @var ObjectManager + */ + private $objectManager; + + /** + * @var TableMaintainer + */ + private $tableMaintainer; + + /** + * @var StoreRepositoryInterface + */ + private $storeRepository; + + /** + * @var ProductRepositoryInterface + */ + private $productRepository; + + /** + * @var CategoryResourceModel + */ + private $categoryResourceModel; + + /** + * @var CategoryLinkManagementInterface + */ + private $categoryLinkManagement; + + /** + * @inheritdoc + */ + protected function setUp() + { + $this->objectManager = Bootstrap::getObjectManager(); + $this->tableMaintainer = $this->objectManager->get(TableMaintainer::class); + $this->storeRepository = $this->objectManager->get(StoreRepositoryInterface::class); + $this->productRepository = $this->objectManager->get(ProductRepositoryInterface::class); + $this->categoryResourceModel = $this->objectManager->get(CategoryResourceModel::class); + $this->categoryLinkManagement = $this->objectManager->create(CategoryLinkManagementInterface::class); + $this->productRepository->cleanCache(); + parent::setUp(); + } + + /** + * @inheritdoc + */ + protected function tearDown() + { + $this->objectManager->removeSharedInstance(CategoryLinkRepository::class); + $this->objectManager->removeSharedInstance(CategoryRepository::class); + parent::tearDown(); + } + + /** + * Assert that product correctly assigned to category and index table contain indexed data. + * + * @magentoDataFixture Magento/Catalog/_files/second_product_simple.php + * @magentoDataFixture Magento/Catalog/_files/category.php + * + * @magentoDbIsolation disabled + * + * @return void + */ + public function testAssignProductToCategory(): void + { + $product = $this->productRepository->get('simple2'); + $this->assertEquals(0, $this->getCategoryProductRelationRecordsCount((int)$product->getId(), [333])); + $this->assertEquals(0, $this->getCategoryProductIndexRecordsCount((int)$product->getId(), [333])); + $this->categoryLinkManagement->assignProductToCategories('simple2', [333]); + $this->assertEquals(1, $this->getCategoryProductRelationRecordsCount((int)$product->getId(), [333])); + $this->assertEquals(1, $this->getCategoryProductIndexRecordsCount((int)$product->getId(), [333])); + } + + /** + * Assert that product correctly unassigned from category and index table not contain indexed data. + * + * @magentoDataFixture Magento/Catalog/_files/product_with_category.php + * + * @magentoDbIsolation disabled + * + * @return void + */ + public function testUnassignProductFromCategory(): void + { + $product = $this->productRepository->get('in-stock-product'); + $this->assertEquals(1, $this->getCategoryProductRelationRecordsCount((int)$product->getId(), [333])); + $this->assertEquals(1, $this->getCategoryProductIndexRecordsCount((int)$product->getId(), [333])); + $this->categoryLinkManagement->assignProductToCategories('in-stock-product', []); + $this->assertEquals(0, $this->getCategoryProductRelationRecordsCount((int)$product->getId(), [333])); + $this->assertEquals(0, $this->getCategoryProductIndexRecordsCount((int)$product->getId(), [333])); + } + + /** + * Assert that product correctly assigned to category and index table contain index data. + * + * @magentoDataFixture Magento/Catalog/_files/second_product_simple.php + * @magentoDataFixture Magento/Catalog/_files/categories_no_products.php + * + * @magentoDbIsolation disabled + * + * @return void + */ + public function testAssignProductToCategoryWhichHasParentCategories(): void + { + $product = $this->productRepository->get('simple2'); + $this->assertEquals(0, $this->getCategoryProductRelationRecordsCount((int)$product->getId(), [5])); + $this->assertEquals(0, $this->getCategoryProductIndexRecordsCount((int)$product->getId(), [3, 4, 5])); + $this->categoryLinkManagement->assignProductToCategories('simple2', [5]); + $this->assertEquals(1, $this->getCategoryProductRelationRecordsCount((int)$product->getId(), [5])); + $this->assertEquals(3, $this->getCategoryProductIndexRecordsCount((int)$product->getId(), [3, 4, 5])); + } + + /** + * Assert that product correctly unassigned from category and index table doesn't contain index data. + * + * @magentoDataFixture Magento/Catalog/_files/product_simple_with_category_which_has_parent_category.php + * + * @magentoDbIsolation disabled + * + * @return void + */ + public function testUnassignProductFromCategoryWhichHasParentCategories(): void + { + $product = $this->productRepository->get('simple_with_child_category'); + $this->assertEquals(1, $this->getCategoryProductRelationRecordsCount((int)$product->getId(), [5])); + $this->assertEquals(3, $this->getCategoryProductIndexRecordsCount((int)$product->getId(), [3, 4, 5])); + $this->categoryLinkManagement->assignProductToCategories('simple_with_child_category', []); + $this->assertEquals(0, $this->getCategoryProductRelationRecordsCount((int)$product->getId(), [5])); + $this->assertEquals(0, $this->getCategoryProductIndexRecordsCount((int)$product->getId(), [3, 4, 5])); + } + + /** + * Assert that product correctly reassigned to another category. + * + * @magentoDataFixture Magento/Catalog/_files/product_simple_with_category_which_has_parent_category.php + * + * @magentoDbIsolation disabled + * + * @return void + */ + public function testReassignProductToOtherCategory(): void + { + $product = $this->productRepository->get('simple_with_child_category'); + $this->assertEquals(1, $this->getCategoryProductRelationRecordsCount((int)$product->getId(), [5])); + $this->assertEquals(3, $this->getCategoryProductIndexRecordsCount((int)$product->getId(), [3, 4, 5])); + $this->categoryLinkManagement->assignProductToCategories('simple_with_child_category', [6]); + $this->assertEquals(1, $this->getCategoryProductRelationRecordsCount((int)$product->getId(), [6])); + $this->assertEquals(1, $this->getCategoryProductIndexRecordsCount((int)$product->getId(), [6])); + $this->assertEquals(0, $this->getCategoryProductRelationRecordsCount((int)$product->getId(), [5])); + $this->assertEquals(0, $this->getCategoryProductIndexRecordsCount((int)$product->getId(), [3, 4, 5])); + } + + /** + * Return count of product which assigned to provided categories. + * + * @param int $productId + * @param int[] $categoryIds + * @return int + */ + private function getCategoryProductRelationRecordsCount(int $productId, array $categoryIds): int + { + $select = $this->categoryResourceModel->getConnection()->select(); + $select->from( + $this->categoryResourceModel->getCategoryProductTable(), + [ + 'row_count' => new \Zend_Db_Expr('COUNT(*)') + ] + ); + $select->where('product_id = ?', $productId); + $select->where('category_id IN (?)', $categoryIds); + + return (int)$this->categoryResourceModel->getConnection()->fetchOne($select); + } + + /** + * Return count of products which added to index table with all provided category ids. + * + * @param int $productId + * @param array $categoryIds + * @param string $storeCode + * @return int + */ + private function getCategoryProductIndexRecordsCount( + int $productId, + array $categoryIds, + string $storeCode = 'default' + ): int { + $storeId = (int)$this->storeRepository->get($storeCode)->getId(); + $select = $this->categoryResourceModel->getConnection()->select(); + $select->from( + $this->tableMaintainer->getMainTable($storeId), + [ + 'row_count' => new \Zend_Db_Expr('COUNT(*)') + ] + ); + $select->where('product_id = ?', $productId); + $select->where('category_id IN (?)', $categoryIds); + + return (int)$this->categoryResourceModel->getConnection()->fetchOne($select); + } +} diff --git a/dev/tests/integration/testsuite/Magento/Catalog/Model/CategoryRepositoryTest.php b/dev/tests/integration/testsuite/Magento/Catalog/Model/CategoryRepositoryTest.php index e1e4a87c033d0..c4cabe46f5b32 100644 --- a/dev/tests/integration/testsuite/Magento/Catalog/Model/CategoryRepositoryTest.php +++ b/dev/tests/integration/testsuite/Magento/Catalog/Model/CategoryRepositoryTest.php @@ -9,10 +9,10 @@ use Magento\Catalog\Api\CategoryRepositoryInterface; use Magento\Catalog\Api\CategoryRepositoryInterfaceFactory; -use Magento\Framework\Exception\LocalizedException; -use Magento\TestFramework\Catalog\Model\CategoryLayoutUpdateManager; use Magento\Catalog\Model\ResourceModel\Category\CollectionFactory as CategoryCollectionFactory; use Magento\Catalog\Model\ResourceModel\Product\CollectionFactory; +use Magento\Framework\Exception\LocalizedException; +use Magento\TestFramework\Catalog\Model\CategoryLayoutUpdateManager; use Magento\TestFramework\Helper\Bootstrap; use PHPUnit\Framework\TestCase; @@ -21,6 +21,11 @@ */ class CategoryRepositoryTest extends TestCase { + private const FIXTURE_CATEGORY_ID = 333; + private const FIXTURE_TWO_STORES_CATEGORY_ID = 555; + private const FIXTURE_SECOND_STORE_CODE = 'fixturestore'; + private const FIXTURE_FIRST_STORE_CODE = 'default'; + /** * @var CategoryLayoutUpdateManager */ @@ -77,13 +82,13 @@ public function testCustomLayout(): void { //New valid value $repo = $this->createRepo(); - $category = $repo->get(333); + $category = $repo->get(self::FIXTURE_CATEGORY_ID); $newFile = 'test'; - $this->layoutManager->setCategoryFakeFiles(333, [$newFile]); + $this->layoutManager->setCategoryFakeFiles(self::FIXTURE_CATEGORY_ID, [$newFile]); $category->setCustomAttribute('custom_layout_update_file', $newFile); $repo->save($category); $repo = $this->createRepo(); - $category = $repo->get(333); + $category = $repo->get(self::FIXTURE_CATEGORY_ID); $this->assertEquals($newFile, $category->getCustomAttribute('custom_layout_update_file')->getValue()); //Setting non-existent value @@ -126,4 +131,36 @@ public function testCategoryBehaviourAfterDelete(): void 'Wrong categories was deleted' ); } + + /** + * Verifies whether `get()` method `$storeId` attribute works as expected. + * + * @magentoDbIsolation enabled + * @magentoDataFixture Magento/Store/_files/core_fixturestore.php + * @magentoDataFixture Magento/Catalog/_files/category_with_two_stores.php + */ + public function testGetCategoryForProvidedStore() + { + $categoryRepository = $this->repositoryFactory->create(); + + $categoryDefault = $categoryRepository->get( + self::FIXTURE_TWO_STORES_CATEGORY_ID + ); + + $this->assertSame('category-defaultstore', $categoryDefault->getUrlKey()); + + $categoryFirstStore = $categoryRepository->get( + self::FIXTURE_TWO_STORES_CATEGORY_ID, + self::FIXTURE_FIRST_STORE_CODE + ); + + $this->assertSame('category-defaultstore', $categoryFirstStore->getUrlKey()); + + $categorySecondStore = $categoryRepository->get( + self::FIXTURE_TWO_STORES_CATEGORY_ID, + self::FIXTURE_SECOND_STORE_CODE + ); + + $this->assertSame('category-fixturestore', $categorySecondStore->getUrlKey()); + } } diff --git a/dev/tests/integration/testsuite/Magento/Catalog/Model/Indexer/Product/CategoryIndexTest.php b/dev/tests/integration/testsuite/Magento/Catalog/Model/Indexer/Product/CategoryIndexTest.php new file mode 100644 index 0000000000000..06f083781aa26 --- /dev/null +++ b/dev/tests/integration/testsuite/Magento/Catalog/Model/Indexer/Product/CategoryIndexTest.php @@ -0,0 +1,203 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +declare(strict_types=1); + +namespace Magento\Catalog\Model\Indexer\Product; + +use Magento\Catalog\Api\CategoryRepositoryInterface; +use Magento\Catalog\Api\ProductRepositoryInterface; +use Magento\Catalog\Helper\DefaultCategory; +use Magento\Catalog\Model\Indexer\Category\Product\TableMaintainer; +use Magento\Catalog\Model\ResourceModel\Category as CategoryResource; +use Magento\Catalog\Model\ResourceModel\Product as ProductResource; +use Magento\Framework\DB\Adapter\AdapterInterface; +use Magento\Framework\ObjectManagerInterface; +use Magento\Store\Model\StoreManagerInterface; +use Magento\TestFramework\Catalog\Model\GetCategoryByName; +use Magento\TestFramework\Helper\Bootstrap; +use Magento\TestFramework\Indexer\TestCase; + +/** + * Checks category products indexing + * + * @magentoAppArea adminhtml + * @magentoAppIsolation enabled + * @magentoDbIsolation disabled + * @SuppressWarnings(PHPMD.CouplingBetweenObjects) + */ +class CategoryIndexTest extends TestCase +{ + /** @var ObjectManagerInterface */ + private $objectManager; + + /** @var ProductRepositoryInterface */ + private $productRepository; + + /** @var StoreManagerInterface */ + private $storeManager; + + /** @var AdapterInterface */ + private $connection; + + /** @var TableMaintainer */ + private $tableMaintainer; + + /** @var ProductResource */ + private $productResource; + + /** @var CategoryRepositoryInterface */ + private $categoryRepository; + + /** @var CategoryResource */ + private $categoryResource; + + /** @var GetCategoryByName */ + private $getCategoryByName; + + /** @var DefaultCategory */ + private $defaultCategoryHelper; + + /** + * @inheritdoc + */ + protected function setUp() + { + parent::setUp(); + + $this->objectManager = Bootstrap::getObjectManager(); + $this->productRepository = $this->objectManager->get(ProductRepositoryInterface::class); + $this->productResource = $this->objectManager->get(ProductResource::class); + $this->storeManager = $this->objectManager->get(StoreManagerInterface::class); + $this->connection = $this->productResource->getConnection(); + $this->tableMaintainer = $this->objectManager->get(TableMaintainer::class); + $this->categoryRepository = $this->objectManager->get(CategoryRepositoryInterface::class); + $this->categoryResource = $this->objectManager->get(CategoryResource::class); + $this->getCategoryByName = $this->objectManager->create(GetCategoryByName::class); + $this->defaultCategoryHelper = $this->objectManager->get(DefaultCategory::class); + } + + /** + * @magentoDataFixture Magento/Catalog/_files/category_with_parent_anchor.php + * @magentoDataFixture Magento/Catalog/_files/second_product_simple.php + * + * @dataProvider assignCategoriesDataProvider + * + * @param string $categoryName + * @param int $expectedItemsCount + * @return void + */ + public function testProductAssignCategory(string $categoryName, int $expectedItemsCount): void + { + $product = $this->productRepository->get('simple2'); + $category = $this->getCategoryByName->execute($categoryName); + $product->setCategoryIds(array_merge($product->getCategoryIds(), [$category->getId()])); + $this->productResource->save($product); + $result = $this->getIndexRecordsByProductId((int)$product->getId()); + $this->assertEquals($expectedItemsCount, $result); + } + + /** + * @return array + */ + public function assignCategoriesDataProvider(): array + { + return [ + 'assign_to_category' => [ + 'category_name' => 'Parent category', + 'expected_records_count' => 1, + ], + 'assign_to_category_with_parent_anchor_category' => [ + 'category_name' => 'Child category', + 'expected_records_count' => 2, + ], + ]; + } + + /** + * @magentoDataFixture Magento/Catalog/_files/category_with_parent_anchor.php + * @magentoDataFixture Magento/Catalog/_files/second_product_simple.php + * + * @dataProvider assignProductsDataProvider + * + * @param string $categoryName + * @param int $expectedCount + * @return void + */ + public function testCategoryAssignProduct(string $categoryName, int $expectedCount): void + { + $product = $this->productRepository->get('simple2'); + $category = $this->getCategoryByName->execute($categoryName); + $data = ['posted_products' => [$product->getId() => 0]]; + $category->addData($data); + $this->categoryResource->save($category); + $result = $this->getIndexRecordsByProductId((int)$product->getId()); + $this->assertEquals($expectedCount, $result); + } + + /** + * @return array + */ + public function assignProductsDataProvider(): array + { + return [ + 'assign_product_to_category' => [ + 'category_name' => 'Parent category', + 'expected_records_count' => 1, + ], + 'assign_product_to_category_with_parent_anchor_category' => [ + 'category_name' => 'Child category', + 'expected_records_count' => 2, + ], + ]; + } + + /** + * @magentoDataFixture Magento/Catalog/_files/category_product_assigned_to_website.php + * @magentoDataFixture Magento/Catalog/_files/category_with_parent_anchor.php + * + * @return void + */ + public function testCategoryMove(): void + { + $product = $this->productRepository->get('product_with_category'); + $category = $this->getCategoryByName->execute('Category with product'); + $newParentCategory = $this->getCategoryByName->execute('Parent category'); + $afterCategory = $this->getCategoryByName->execute('Child category'); + $category->move($newParentCategory->getId(), $afterCategory->getId()); + $result = $this->getIndexRecordsByProductId((int)$product->getId()); + $this->assertEquals(2, $result); + } + + /** + * @magentoDataFixture Magento/Catalog/_files/category_product_assigned_to_website.php + * + * @return void + */ + public function testDeleteProduct(): void + { + $product = $this->productRepository->get('product_with_category'); + $this->productRepository->delete($product); + $result = $this->getIndexRecordsByProductId((int)$product->getId()); + $this->assertEmpty($result); + } + + /** + * Fetch data from category product index table + * + * @param int $productId + * @return int + */ + private function getIndexRecordsByProductId(int $productId): int + { + $tableName = $this->tableMaintainer->getMainTable((int)$this->storeManager->getStore()->getId()); + $select = $this->connection->select(); + $select->from(['index_table' => $tableName], new \Zend_Db_Expr('COUNT(*)')) + ->where('index_table.product_id = ?', $productId) + ->where('index_table.category_id != ?', $this->defaultCategoryHelper->getId()); + + return (int)$this->connection->fetchOne($select); + } +} diff --git a/dev/tests/integration/testsuite/Magento/Catalog/Model/Indexer/Product/Flat/Action/RowsTest.php b/dev/tests/integration/testsuite/Magento/Catalog/Model/Indexer/Product/Flat/Action/RowsTest.php index baf0a0c1a9c4b..1252245a259c2 100644 --- a/dev/tests/integration/testsuite/Magento/Catalog/Model/Indexer/Product/Flat/Action/RowsTest.php +++ b/dev/tests/integration/testsuite/Magento/Catalog/Model/Indexer/Product/Flat/Action/RowsTest.php @@ -3,63 +3,117 @@ * Copyright © Magento, Inc. All rights reserved. * See COPYING.txt for license details. */ + +declare(strict_types=1); + namespace Magento\Catalog\Model\Indexer\Product\Flat\Action; +use Magento\Catalog\Api\ProductRepositoryInterface; +use Magento\Catalog\Block\Product\ListProduct; +use Magento\Catalog\Model\Indexer\Product\Flat\Processor; +use Magento\Catalog\Model\Product; +use Magento\Catalog\Model\Product\Attribute\Source\Status; +use Magento\Catalog\Model\ResourceModel\Product\CollectionFactory; +use Magento\Catalog\Api\CategoryRepositoryInterface; +use Magento\Framework\View\LayoutInterface; +use Magento\TestFramework\Helper\Bootstrap; +use Magento\TestFramework\Indexer\TestCase; + /** - * Class RowsTest + * Test for \Magento\Catalog\Model\Indexer\Product\Flat\Action\Rows. */ -class RowsTest extends \Magento\TestFramework\Indexer\TestCase +class RowsTest extends TestCase { /** - * @var \Magento\Catalog\Model\Product + * @var Processor */ - protected $_product; + private $processor; /** - * @var \Magento\Catalog\Model\Indexer\Product\Flat\Processor + * @var ProductRepositoryInterface */ - protected $_processor; + private $productRepository; + /** + * @var CategoryRepositoryInterface + */ + private $categoryRepository; + + /** + * @var CollectionFactory + */ + private $productCollectionFactory; + + /** + * @var LayoutInterface + */ + private $layout; + + /** + * @inheritdoc + */ protected function setUp() { - $this->_product = \Magento\TestFramework\Helper\Bootstrap::getObjectManager()->create( - \Magento\Catalog\Model\Product::class - ); - $this->_processor = \Magento\TestFramework\Helper\Bootstrap::getObjectManager()->get( - \Magento\Catalog\Model\Indexer\Product\Flat\Processor::class - ); + $objectManager = Bootstrap::getObjectManager(); + $this->processor = $objectManager->get(Processor::class); + $this->productRepository = $objectManager->get(ProductRepositoryInterface::class); + $this->categoryRepository = $objectManager->get(CategoryRepositoryInterface::class); + $this->productCollectionFactory = $objectManager->get(CollectionFactory::class); + $this->layout = $objectManager->get(LayoutInterface::class); } /** + * Test update category products + * * @magentoDbIsolation disabled * @magentoAppIsolation enabled * @magentoConfigFixture current_store catalog/frontend/flat_catalog_product 1 * @magentoDataFixture Magento/Catalog/_files/product_simple.php + * + * @return void */ - public function testProductsUpdate() + public function testProductsUpdate(): void { - $this->_product->load(1); - - $this->_processor->reindexList([$this->_product->getId()]); - - $categoryFactory = \Magento\TestFramework\Helper\Bootstrap::getObjectManager()->get( - \Magento\Catalog\Model\CategoryFactory::class - ); - $listProduct = \Magento\TestFramework\Helper\Bootstrap::getObjectManager()->get( - \Magento\Catalog\Block\Product\ListProduct::class - ); + $product = $this->productRepository->getById(1); + $this->processor->reindexList([$product->getId()]); - $category = $categoryFactory->create()->load(2); + $category = $this->categoryRepository->get(2); + $listProduct = $this->layout->createBlock(ListProduct::class); $layer = $listProduct->getLayer(); $layer->setCurrentCategory($category); $productCollection = $layer->getProductCollection(); $this->assertCount(1, $productCollection); - /** @var $product \Magento\Catalog\Model\Product */ - foreach ($productCollection as $product) { - $this->assertEquals($this->_product->getName(), $product->getName()); - $this->assertEquals($this->_product->getShortDescription(), $product->getShortDescription()); + /** @var $productItem Product */ + foreach ($productCollection as $productItem) { + $this->assertEquals($product->getName(), $productItem->getName()); + $this->assertEquals($product->getShortDescription(), $productItem->getShortDescription()); } } + + /** + * Products update with different statuses + * + * @magentoDbIsolation disabled + * @magentoAppArea frontend + * @magentoConfigFixture current_store catalog/frontend/flat_catalog_product 1 + * @magentoDataFixture Magento/Catalog/_files/product_simple_with_custom_attribute_in_flat.php + * @magentoDataFixture Magento/Catalog/_files/product_simple_duplicated.php + * + * @return void + */ + public function testProductsDifferentStatusesUpdate(): void + { + $firstProduct = $this->productRepository->get('simple_with_custom_flat_attribute'); + $secondProduct = $this->productRepository->get('simple-1'); + + $this->processor->getIndexer()->setScheduled(true); + $this->productRepository->save($secondProduct->setStatus(Status::STATUS_DISABLED)); + $this->processor->reindexList([$firstProduct->getId(), $secondProduct->getId()], true); + $collection = $this->productCollectionFactory->create(); + + $this->assertCount(1, $collection); + $this->assertEquals($firstProduct->getId(), $collection->getFirstItem()->getId()); + } } diff --git a/dev/tests/integration/testsuite/Magento/Catalog/Model/Layer/Filter/_files/attribute_with_option_rollback.php b/dev/tests/integration/testsuite/Magento/Catalog/Model/Layer/Filter/_files/attribute_with_option_rollback.php index f3989248a8ed1..602af4d0e3c78 100644 --- a/dev/tests/integration/testsuite/Magento/Catalog/Model/Layer/Filter/_files/attribute_with_option_rollback.php +++ b/dev/tests/integration/testsuite/Magento/Catalog/Model/Layer/Filter/_files/attribute_with_option_rollback.php @@ -32,7 +32,7 @@ \Magento\Catalog\Model\Product::class ); $product = $product->loadByAttribute('sku', 'simple_product_' . $option->getId()); - if ($product->getId()) { + if ($product instanceof \Magento\Catalog\Model\Product && $product->getId()) { $product->delete(); } } diff --git a/dev/tests/integration/testsuite/Magento/Catalog/Model/Layout/DepersonalizePluginTest.php b/dev/tests/integration/testsuite/Magento/Catalog/Model/Layout/DepersonalizePluginTest.php new file mode 100644 index 0000000000000..43b5422dbb90a --- /dev/null +++ b/dev/tests/integration/testsuite/Magento/Catalog/Model/Layout/DepersonalizePluginTest.php @@ -0,0 +1,100 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +declare(strict_types=1); + +namespace Magento\Catalog\Model\Layout; + +use Magento\Catalog\Model\Session as CatalogSession; +use Magento\Framework\App\Cache\Type\Layout as LayoutCache; +use Magento\Framework\View\Layout; +use Magento\Framework\View\LayoutFactory; +use Magento\TestFramework\Helper\Bootstrap; +use PHPUnit\Framework\TestCase; + +/** + * Integration tests for \Magento\Catalog\Model\Layout\DepersonalizePlugin class. + * + * @magentoAppArea frontend + */ +class DepersonalizePluginTest extends TestCase +{ + /** + * @var CatalogSession + */ + private $catalogSession; + + /** + * @var Layout + */ + private $layout; + + /** + * @var LayoutCache + */ + private $cache; + + /** + * @inheritdoc + */ + protected function setUp() + { + $this->catalogSession = Bootstrap::getObjectManager()->get(CatalogSession::class); + $this->layout = Bootstrap::getObjectManager()->get(LayoutFactory::class)->create(); + $this->cache = Bootstrap::getObjectManager()->get(LayoutCache::class); + } + + /** + * @inheritdoc + */ + protected function tearDown() + { + $this->catalogSession->clearStorage(); + } + + /** + * @magentoCache full_page enabled + * @dataProvider afterGenerateElementsDataProvider + * + * @param string $layout + * @param array $expectedResult + * @return void + */ + public function testAfterGenerateElements(string $layout, array $expectedResult): void + { + $this->cache->clean(); + $this->assertTrue($this->layout->loadFile($layout)); + $this->catalogSession->setData(['some_data' => 1]); + $this->layout->generateElements(); + $this->assertEquals($expectedResult, $this->catalogSession->getData()); + } + + /** + * @return array + */ + public function afterGenerateElementsDataProvider(): array + { + return [ + 'cacheable' => [ + 'layout' => INTEGRATION_TESTS_DIR . '/testsuite/Magento/Framework/View/_files/layout/cacheable.xml', + 'expectedResult' => [], + ], + 'nonCacheable' => [ + 'layout' => INTEGRATION_TESTS_DIR . '/testsuite/Magento/Framework/View/_files/layout/non_cacheable.xml', + 'expectedResult' => ['some_data' => 1], + ], + 'nonCacheableBlockWithoutReference' => [ + 'layout' => INTEGRATION_TESTS_DIR + . '/testsuite/Magento/Framework/View/_files/layout/non_cacheable_block_with_missing_refference.xml', + 'expectedResult' => [], + ], + 'nonCacheableBlockWithExistedReference' => [ + 'layout' => INTEGRATION_TESTS_DIR + . '/testsuite/Magento/Framework/View/_files/layout/non_cacheable_block_with_declared_reference.xml', + 'expectedResult' => ['some_data' => 1], + ], + ]; + } +} diff --git a/dev/tests/integration/testsuite/Magento/Catalog/Model/Product/CreateCustomOptionsTest.php b/dev/tests/integration/testsuite/Magento/Catalog/Model/Product/CreateCustomOptionsTest.php index 2239170cdc84e..cab75c7848f06 100644 --- a/dev/tests/integration/testsuite/Magento/Catalog/Model/Product/CreateCustomOptionsTest.php +++ b/dev/tests/integration/testsuite/Magento/Catalog/Model/Product/CreateCustomOptionsTest.php @@ -120,6 +120,8 @@ public function testSaveOptionPriceByStore(): void * @dataProvider productCustomOptionsTypeTextDataProvider * * @param array $optionData + * + * @magentoDbIsolation enabled */ public function testCreateOptionsWithTypeText(array $optionData): void { @@ -140,6 +142,8 @@ public function testCreateOptionsWithTypeText(array $optionData): void * * @param string $rawExtensions * @param string $expectedExtensions + * + * @magentoDbIsolation enabled */ public function testFileExtensions(string $rawExtensions, string $expectedExtensions): void { @@ -174,6 +178,8 @@ public function testFileExtensions(string $rawExtensions, string $expectedExtens * * @param array $optionData * @param array $optionValueData + * + * @magentoDbIsolation enabled */ public function testCreateOptionsWithTypeSelect(array $optionData, array $optionValueData): void { @@ -199,6 +205,8 @@ public function testCreateOptionsWithTypeSelect(array $optionData, array $option * @dataProvider productCustomOptionsTypeDateDataProvider * * @param array $optionData + * + * @magentoDbIsolation enabled */ public function testCreateOptionsWithTypeDate(array $optionData): void { @@ -217,6 +225,8 @@ public function testCreateOptionsWithTypeDate(array $optionData): void * * @param array $optionData * @param \Exception $expectedErrorObject + * + * @magentoDbIsolation enabled */ public function testCreateOptionWithError(array $optionData, \Exception $expectedErrorObject): void { diff --git a/dev/tests/integration/testsuite/Magento/Catalog/Model/Product/Option/Type/TextTest.php b/dev/tests/integration/testsuite/Magento/Catalog/Model/Product/Option/Type/TextTest.php new file mode 100644 index 0000000000000..74082c339bd79 --- /dev/null +++ b/dev/tests/integration/testsuite/Magento/Catalog/Model/Product/Option/Type/TextTest.php @@ -0,0 +1,79 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ + +namespace Magento\Catalog\Model\Product\Option\Type; + +use Magento\Catalog\Model\Product\Option; +use Magento\Framework\ObjectManagerInterface; +use Magento\TestFramework\Helper\Bootstrap; +use PHPUnit\Framework\TestCase; + +/** + * Test for customizable product option with "Text" type + */ +class TextTest extends TestCase +{ + const STUB_OPTION_DATA = ['id' => 11, 'type' => 'area']; + + /** + * @var Text + */ + protected $optionText; + + /** + * @var ObjectManagerInterface + */ + private $objectManager; + + /** + * {@inheritDoc} + */ + protected function setUp() + { + $this->objectManager = Bootstrap::getObjectManager(); + $this->optionText = $this->objectManager->create(Text::class); + } + + /** + * Check if newline symbols are normalized in option value + * + * @dataProvider optionValueDataProvider + * @param array $productOptionData + * @param string $optionValue + * @param string $expectedOptionValue + */ + public function testNormalizeNewlineSymbols( + array $productOptionData, + string $optionValue, + string $expectedOptionValue + ) { + $productOption = $this->objectManager->create( + Option::class, + ['data' => $productOptionData] + ); + + $this->optionText->setOption($productOption); + $this->optionText->setUserValue($optionValue); + $this->optionText->validateUserValue([]); + + $this->assertSame($expectedOptionValue, $this->optionText->getUserValue()); + } + + /** + * Data provider for testNormalizeNewlineSymbols + * + * @return array + */ + public function optionValueDataProvider() + { + return [ + [self::STUB_OPTION_DATA, 'string string', 'string string'], + [self::STUB_OPTION_DATA, "string \r\n string", "string \n string"], + [self::STUB_OPTION_DATA, "string \n\r string", "string \n string"], + [self::STUB_OPTION_DATA, "string \r string", "string \n string"] + ]; + } +} diff --git a/dev/tests/integration/testsuite/Magento/Catalog/Model/Product/ProductFrontendAction/SynchronizerTest.php b/dev/tests/integration/testsuite/Magento/Catalog/Model/Product/ProductFrontendAction/SynchronizerTest.php new file mode 100644 index 0000000000000..5cf53349480f1 --- /dev/null +++ b/dev/tests/integration/testsuite/Magento/Catalog/Model/Product/ProductFrontendAction/SynchronizerTest.php @@ -0,0 +1,113 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +declare(strict_types=1); + +namespace Magento\Catalog\Model\Product\ProductFrontendAction; + +use Magento\Catalog\Model\ProductRepository; + +/** + * Test for \Magento\Catalog\Model\Product\ProductFrontendAction\Synchronizer. + */ +class SynchronizerTest extends \PHPUnit\Framework\TestCase +{ + /** + * @var Synchronizer + */ + private $synchronizer; + + /** + * @var ProductRepository + */ + private $productRepository; + + /** + * @inheritDoc + */ + protected function setUp() + { + $objectManager = \Magento\TestFramework\Helper\Bootstrap::getObjectManager(); + + $this->synchronizer = $objectManager->get(Synchronizer::class); + $this->productRepository = $objectManager->get(ProductRepository::class); + } + + /** + * @magentoDataFixture Magento/Catalog/_files/product_simple.php + * @magentoDataFixture Magento/Catalog/_files/second_product_simple.php + * + * @return void + * @throws \Magento\Framework\Exception\LocalizedException + * @throws \Magento\Framework\Exception\NoSuchEntityException + */ + public function testSyncActions(): void + { + $actionsType = 'recently_viewed_product'; + $productScope = 'website'; + $scopeId = 1; + $product1 = $this->productRepository->get('simple'); + $product2 = $this->productRepository->get('simple2'); + $product1Id = $product1->getId(); + $product2Id = $product2->getId(); + $productsData = [ + $productScope . '-' . $scopeId . '-' . $product1Id => [ + 'added_at' => '1576582660', + 'product_id' => $product1Id, + ], + $productScope . '-' . $scopeId . '-' . $product2Id => [ + 'added_at' => '1576587153', + 'product_id' => $product2Id, + ], + ]; + + $this->synchronizer->syncActions($productsData, $actionsType); + + $synchronizedCollection = $this->synchronizer->getActionsByType($actionsType); + $synchronizedCollection->addFieldToFilter( + 'product_id', + [ + $product1Id, + $product2Id, + ] + ); + + foreach ($synchronizedCollection as $item) { + $productScopeId = $productScope . '-' . $scopeId . '-' . $item->getProductId(); + $this->assertArrayHasKey($productScopeId, $productsData); + $this->assertEquals($productsData[$productScopeId]['added_at'], $item->getAddedAt()); + } + } + + /** + * @magentoDataFixture Magento/Catalog/_files/product_simple.php + * @magentoDataFixture Magento/Catalog/_files/second_product_simple.php + * + * @return void + * @throws \Magento\Framework\Exception\LocalizedException + * @throws \Magento\Framework\Exception\NoSuchEntityException + */ + public function testSyncActionsWithoutActionsType(): void + { + $product1 = $this->productRepository->get('simple'); + $product2 = $this->productRepository->get('simple2'); + $product1Id = $product1->getId(); + $product2Id = $product2->getId(); + $productsData = [ + $product1Id => [ + 'id' => $product1Id, + 'name' => $product1->getName(), + 'type' => $product1->getTypeId(), + ], + $product2Id => [ + 'id' => $product2Id, + 'name' => $product2->getName(), + 'type' => $product2->getTypeId(), + ], + ]; + + $this->synchronizer->syncActions($productsData, ''); + } +} diff --git a/dev/tests/integration/testsuite/Magento/Catalog/Model/Product/Type/PriceTest.php b/dev/tests/integration/testsuite/Magento/Catalog/Model/Product/Type/PriceTest.php index 8d3a119873afb..fe7207d310345 100644 --- a/dev/tests/integration/testsuite/Magento/Catalog/Model/Product/Type/PriceTest.php +++ b/dev/tests/integration/testsuite/Magento/Catalog/Model/Product/Type/PriceTest.php @@ -3,105 +3,214 @@ * Copyright © Magento, Inc. All rights reserved. * See COPYING.txt for license details. */ +declare(strict_types=1); + namespace Magento\Catalog\Model\Product\Type; +use Magento\Catalog\Api\Data\ProductCustomOptionInterface; +use Magento\Catalog\Api\ProductRepositoryInterface; use Magento\Catalog\Model\Product; +use Magento\Catalog\Model\Product\Option; +use Magento\Customer\Model\Session; +use Magento\Framework\DataObject; +use Magento\TestFramework\Helper\Bootstrap; +use Magento\TestFramework\ObjectManager; +use PHPUnit\Framework\TestCase; /** + * Simple product price test. + * * @magentoDataFixture Magento/Catalog/_files/product_simple.php + * @magentoDbIsolation enabled */ -class PriceTest extends \PHPUnit\Framework\TestCase +class PriceTest extends TestCase { /** - * @var \Magento\Catalog\Model\Product\Type\Price + * @var ObjectManager + */ + private $objectManager; + + /** + * @var Price */ - protected $_model; + private $productPrice; - protected function setUp() + /** + * @var ProductRepositoryInterface + */ + private $productRepository; + + /** + * @var Session + */ + private $customerSession; + + /** + * @inheritdoc + */ + protected function setUp(): void { - $this->_model = \Magento\TestFramework\Helper\Bootstrap::getObjectManager()->create( - \Magento\Catalog\Model\Product\Type\Price::class - ); + $this->objectManager = Bootstrap::getObjectManager(); + $this->productPrice = $this->objectManager->create(Price::class); + $this->productRepository = $this->objectManager->create(ProductRepositoryInterface::class); + $this->customerSession = $this->objectManager->get(Session::class); } - public function testGetPrice() + /** + * Assert that for logged user product price equal to price from catalog rule. + * + * @magentoDataFixture Magento/Catalog/_files/product_simple.php + * @magentoDataFixture Magento/CatalogRule/_files/catalog_rule_6_off_logged_user.php + * @magentoDataFixture Magento/Customer/_files/customer.php + * + * @magentoDbIsolation disabled + * @magentoAppArea frontend + * @magentoAppIsolation enabled + * + * @return void + */ + public function testPriceByRuleForLoggedUser(): void { - $this->assertEquals('test', $this->_model->getPrice(new \Magento\Framework\DataObject(['price' => 'test']))); + $product = $this->productRepository->get('simple'); + $this->assertEquals(10, $this->productPrice->getFinalPrice(1, $product)); + $this->customerSession->setCustomerId(1); + try { + $this->assertEquals(4, $this->productPrice->getFinalPrice(1, $product)); + } finally { + $this->customerSession->setCustomerId(null); + } } - public function testGetFinalPrice() + /** + * Assert price for different customer groups. + * + * @magentoDataFixture Magento/Catalog/_files/simple_product_with_tier_price_for_logged_user.php + * @magentoDataFixture Magento/Customer/_files/customer.php + * + * @magentoAppIsolation enabled + * + * @return void + */ + public function testTierPriceWithDifferentCustomerGroups(): void { - $repository = \Magento\TestFramework\Helper\Bootstrap::getObjectManager()->create( - \Magento\Catalog\Model\ProductRepository::class - ); - $product = $repository->get('simple'); - // fixture + $product = $this->productRepository->get('simple'); + $this->assertEquals(8, $this->productPrice->getFinalPrice(2, $product)); + $this->assertEquals(5, $this->productPrice->getFinalPrice(3, $product)); + $this->customerSession->setCustomerId(1); + try { + $this->assertEquals(1, $this->productPrice->getFinalPrice(3, $product)); + } finally { + $this->customerSession->setCustomerId(null); + } + } + + /** + * Get price from custom object. + * + * @return void + */ + public function testGetPrice(): void + { + $objectWithPrice = $this->objectManager->create(DataObject::class, ['data' => ['price' => 'test']]); + $this->assertEquals('test', $this->productPrice->getPrice($objectWithPrice)); + } + + /** + * Get product final price for different product count. + * + * @return void + */ + public function testGetFinalPrice(): void + { + $product = $this->productRepository->get('simple'); // regular & tier prices - $this->assertEquals(10.0, $this->_model->getFinalPrice(1, $product)); - $this->assertEquals(8.0, $this->_model->getFinalPrice(2, $product)); - $this->assertEquals(5.0, $this->_model->getFinalPrice(5, $product)); + $this->assertEquals(10.0, $this->productPrice->getFinalPrice(1, $product)); + $this->assertEquals(8.0, $this->productPrice->getFinalPrice(2, $product)); + $this->assertEquals(5.0, $this->productPrice->getFinalPrice(5, $product)); // with options $buyRequest = $this->prepareBuyRequest($product); $product->getTypeInstance()->prepareForCart($buyRequest, $product); //product price + options price(10+1+2+3+3) - $this->assertEquals(19.0, $this->_model->getFinalPrice(1, $product)); + $this->assertEquals(19.0, $this->productPrice->getFinalPrice(1, $product)); //product tier price + options price(5+1+2+3+3) - $this->assertEquals(14.0, $this->_model->getFinalPrice(5, $product)); + $this->assertEquals(14.0, $this->productPrice->getFinalPrice(5, $product)); } - public function testGetFormatedPrice() + /** + * Assert that formated price is correct. + * + * @return void + */ + public function testGetFormatedPrice(): void { - $repository = \Magento\TestFramework\Helper\Bootstrap::getObjectManager()->create( - \Magento\Catalog\Model\ProductRepository::class - ); - $product = $repository->get('simple'); - // fixture - $this->assertEquals('<span class="price">$10.00</span>', $this->_model->getFormatedPrice($product)); + $product = $this->productRepository->get('simple'); + $this->assertEquals('<span class="price">$10.00</span>', $this->productPrice->getFormatedPrice($product)); } - public function testCalculatePrice() + /** + * Test calculate price by date. + * + * @return void + */ + public function testCalculatePrice(): void { - $this->assertEquals(10, $this->_model->calculatePrice(10, 8, '1970-12-12 23:59:59', '1971-01-01 01:01:01')); - $this->assertEquals(8, $this->_model->calculatePrice(10, 8, '1970-12-12 23:59:59', '2034-01-01 01:01:01')); + $this->assertEquals( + 10, + $this->productPrice->calculatePrice(10, 8, '1970-12-12 23:59:59', '1971-01-01 01:01:01') + ); + $this->assertEquals( + 8, + $this->productPrice->calculatePrice(10, 8, '1970-12-12 23:59:59', '2034-01-01 01:01:01') + ); } - public function testCalculateSpecialPrice() + /** + * Test calculate price by date. + * + * @return void + */ + public function testCalculateSpecialPrice(): void { $this->assertEquals( 10, - $this->_model->calculateSpecialPrice(10, 8, '1970-12-12 23:59:59', '1971-01-01 01:01:01') + $this->productPrice->calculateSpecialPrice(10, 8, '1970-12-12 23:59:59', '1971-01-01 01:01:01') ); $this->assertEquals( 8, - $this->_model->calculateSpecialPrice(10, 8, '1970-12-12 23:59:59', '2034-01-01 01:01:01') + $this->productPrice->calculateSpecialPrice(10, 8, '1970-12-12 23:59:59', '2034-01-01 01:01:01') ); } - public function testIsTierPriceFixed() + /** + * Assert that product tier price is fixed. + * + * @return void + */ + public function testIsTierPriceFixed(): void { - $this->assertTrue($this->_model->isTierPriceFixed()); + $this->assertTrue($this->productPrice->isTierPriceFixed()); } /** - * Build buy request based on product custom options + * Build buy request based on product custom options. * * @param Product $product - * @return \Magento\Framework\DataObject + * @return DataObject */ - private function prepareBuyRequest(Product $product) + private function prepareBuyRequest(Product $product): DataObject { $options = []; - /** @var $option \Magento\Catalog\Model\Product\Option */ + /** @var Option $option */ foreach ($product->getOptions() as $option) { switch ($option->getGroupByType()) { - case \Magento\Catalog\Api\Data\ProductCustomOptionInterface::OPTION_GROUP_DATE: + case ProductCustomOptionInterface::OPTION_GROUP_DATE: $value = ['year' => 2013, 'month' => 8, 'day' => 9, 'hour' => 13, 'minute' => 35]; break; - case \Magento\Catalog\Api\Data\ProductCustomOptionInterface::OPTION_GROUP_SELECT: + case ProductCustomOptionInterface::OPTION_GROUP_SELECT: $value = key($option->getValues()); break; default: @@ -111,6 +220,6 @@ private function prepareBuyRequest(Product $product) $options[$option->getId()] = $value; } - return new \Magento\Framework\DataObject(['qty' => 1, 'options' => $options]); + return $this->objectManager->create(DataObject::class, ['data' => ['qty' => 1, 'options' => $options]]); } } diff --git a/dev/tests/integration/testsuite/Magento/Catalog/Model/Product/UrlTest.php b/dev/tests/integration/testsuite/Magento/Catalog/Model/Product/UrlTest.php index ff2979150954e..451e19be28e5f 100644 --- a/dev/tests/integration/testsuite/Magento/Catalog/Model/Product/UrlTest.php +++ b/dev/tests/integration/testsuite/Magento/Catalog/Model/Product/UrlTest.php @@ -53,8 +53,8 @@ public function testGetUrlInStore() * @magentoConfigFixture fixturestore_store web/unsecure/base_url http://sample-second.com/ * @magentoConfigFixture fixturestore_store web/unsecure/base_link_url http://sample-second.com/ * @magentoDataFixture Magento/Catalog/_files/product_simple_multistore.php - * @magentoDbIsolation disabled * @dataProvider getUrlsWithSecondStoreProvider + * @magentoDbIsolation disabled * @magentoAppArea adminhtml */ public function testGetUrlInStoreWithSecondStore($storeCode, $expectedProductUrl) diff --git a/dev/tests/integration/testsuite/Magento/Catalog/Model/ProductTest.php b/dev/tests/integration/testsuite/Magento/Catalog/Model/ProductTest.php index d7da47ef78724..cb1e07c8c104a 100644 --- a/dev/tests/integration/testsuite/Magento/Catalog/Model/ProductTest.php +++ b/dev/tests/integration/testsuite/Magento/Catalog/Model/ProductTest.php @@ -8,8 +8,10 @@ namespace Magento\Catalog\Model; +use Magento\Eav\Model\Config as EavConfig; use Magento\Catalog\Model\Product; use Magento\Framework\App\Filesystem\DirectoryList; +use Magento\TestFramework\ObjectManager; use Magento\Catalog\Api\ProductRepositoryInterface; use Magento\Catalog\Model\Product\Attribute\Source\Status; use Magento\Framework\ObjectManagerInterface; @@ -331,9 +333,9 @@ public function testIsSalable() $this->_model = $this->productRepository->get('simple'); // fixture - $this->assertTrue((bool)$this->_model->isSalable()); - $this->assertTrue((bool)$this->_model->isSaleable()); - $this->assertTrue((bool)$this->_model->isAvailable()); + $this->assertTrue((bool) $this->_model->isSalable()); + $this->assertTrue((bool) $this->_model->isSaleable()); + $this->assertTrue((bool) $this->_model->isAvailable()); $this->assertTrue($this->_model->isInStock()); } @@ -350,9 +352,9 @@ public function testIsNotSalableWhenStatusDisabled() $this->_model = $this->productRepository->get('simple'); $this->_model->setStatus(0); - $this->assertFalse((bool)$this->_model->isSalable()); - $this->assertFalse((bool)$this->_model->isSaleable()); - $this->assertFalse((bool)$this->_model->isAvailable()); + $this->assertFalse((bool) $this->_model->isSalable()); + $this->assertFalse((bool) $this->_model->isSaleable()); + $this->assertFalse((bool) $this->_model->isAvailable()); $this->assertFalse($this->_model->isInStock()); } @@ -631,7 +633,7 @@ public function testGetOptions() continue; } foreach ($option->getValues() as $value) { - $this->assertEquals($expectedValue[$value->getSku()], (float)$value->getPrice()); + $this->assertEquals($expectedValue[$value->getSku()], (float) $value->getPrice()); } } } @@ -723,4 +725,40 @@ public function productWithBackordersDataProvider(): array [1, 1, true], ]; } + + public function testConstructionWithCustomAttributesMapInData() + { + $data = [ + 'custom_attributes' => [ + 'tax_class_id' => '3', + 'category_ids' => '1,2' + ], + ]; + + /** @var Product $product */ + $product = ObjectManager::getInstance()->create(Product::class, ['data' => $data]); + $this->assertSame($product->getCustomAttribute('tax_class_id')->getValue(), '3'); + $this->assertSame($product->getCustomAttribute('category_ids')->getValue(), '1,2'); + } + + public function testConstructionWithCustomAttributesArrayInData() + { + $data = [ + 'custom_attributes' => [ + [ + 'attribute_code' => 'tax_class_id', + 'value' => '3' + ], + [ + 'attribute_code' => 'category_ids', + 'value' => '1,2' + ] + ], + ]; + + /** @var Product $product */ + $product = ObjectManager::getInstance()->create(Product::class, ['data' => $data]); + $this->assertSame($product->getCustomAttribute('tax_class_id')->getValue(), '3'); + $this->assertSame($product->getCustomAttribute('category_ids')->getValue(), '1,2'); + } } diff --git a/dev/tests/integration/testsuite/Magento/Catalog/Model/ResourceModel/Attribute/Entity/AttributeTest.php b/dev/tests/integration/testsuite/Magento/Catalog/Model/ResourceModel/Attribute/Entity/AttributeTest.php index 8ecf3da8e1aae..2df9c468ba10a 100644 --- a/dev/tests/integration/testsuite/Magento/Catalog/Model/ResourceModel/Attribute/Entity/AttributeTest.php +++ b/dev/tests/integration/testsuite/Magento/Catalog/Model/ResourceModel/Attribute/Entity/AttributeTest.php @@ -23,6 +23,7 @@ * @magentoDataFixture Magento/Catalog/_files/dropdown_attribute.php * @magentoDataFixture Magento/Catalog/_files/multiselect_attribute.php * @magentoDataFixture Magento/Catalog/_files/product_without_options.php + * @magentoDbIsolation enabled */ class AttributeTest extends \PHPUnit\Framework\TestCase { diff --git a/dev/tests/integration/testsuite/Magento/Catalog/Model/ResourceModel/Product/CollectionTest.php b/dev/tests/integration/testsuite/Magento/Catalog/Model/ResourceModel/Product/CollectionTest.php index 86dcf9d96d086..2100920ab8ac9 100644 --- a/dev/tests/integration/testsuite/Magento/Catalog/Model/ResourceModel/Product/CollectionTest.php +++ b/dev/tests/integration/testsuite/Magento/Catalog/Model/ResourceModel/Product/CollectionTest.php @@ -14,7 +14,7 @@ use Magento\TestFramework\Helper\Bootstrap; /** - * Collection test + * Test for Magento\Catalog\Model\ResourceModel\Product\Collection * * @SuppressWarnings(PHPMD.CouplingBetweenObjects) */ @@ -287,8 +287,8 @@ public function testAddAttributeToFilterAffectsGetSize(): void */ public function testAddAttributeTierPriceToFilter($condition): void { - $this->collection->addAttributeToFilter('tier_price', $condition); - $this->assertEquals(1, $this->collection->getSize()); + $size = $this->collection->addAttributeToFilter('tier_price', $condition)->getSize(); + $this->assertEquals(1, $size); } /** @@ -314,8 +314,8 @@ public function addAttributeTierPriceToFilterDataProvider(): array */ public function testAddAttributeIsSaleableToFilter($condition): void { - $this->collection->addAttributeToFilter('is_saleable', $condition); - $this->assertEquals(1, $this->collection->getSize()); + $size = $this->collection->addAttributeToFilter('is_saleable', $condition)->getSize(); + $this->assertEquals(1, $size); } /** diff --git a/dev/tests/integration/testsuite/Magento/Catalog/Pricing/Render/CombinationWithDifferentTypePricesTest.php b/dev/tests/integration/testsuite/Magento/Catalog/Pricing/Render/CombinationWithDifferentTypePricesTest.php new file mode 100644 index 0000000000000..5b55bb32ce669 --- /dev/null +++ b/dev/tests/integration/testsuite/Magento/Catalog/Pricing/Render/CombinationWithDifferentTypePricesTest.php @@ -0,0 +1,608 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +declare(strict_types=1); + +namespace Magento\Catalog\Pricing\Render; + +use Magento\Catalog\Api\Data\ProductInterface; +use Magento\Catalog\Api\Data\ProductTierPriceExtensionFactory; +use Magento\Catalog\Api\Data\ProductTierPriceInterfaceFactory; +use Magento\Catalog\Api\ProductRepositoryInterface; +use Magento\CatalogRule\Api\CatalogRuleRepositoryInterface; +use Magento\CatalogRule\Api\Data\RuleInterface; +use Magento\CatalogRule\Api\Data\RuleInterfaceFactory; +use Magento\CatalogRule\Model\Indexer\IndexBuilder; +use Magento\Customer\Model\Group; +use Magento\Customer\Model\Session; +use Magento\Framework\Registry; +use Magento\Framework\View\Result\Page; +use Magento\Store\Api\WebsiteRepositoryInterface; +use Magento\TestFramework\Helper\Bootstrap; +use Magento\TestFramework\Helper\Xpath; +use Magento\TestFramework\ObjectManager; +use PHPUnit\Framework\TestCase; + +/** + * Assertions related to check product price rendering with combination of different price types. + * + * @magentoDbIsolation disabled + * @magentoAppArea frontend + * @SuppressWarnings(PHPMD.CouplingBetweenObjects) + */ +class CombinationWithDifferentTypePricesTest extends TestCase +{ + /** + * @var ObjectManager + */ + private $objectManager; + + /** + * @var Page + */ + private $page; + + /** + * @var Registry + */ + private $registry; + + /** + * @var IndexBuilder + */ + private $indexBuilder; + + /** + * @var Session + */ + private $customerSession; + + /** + * @var WebsiteRepositoryInterface + */ + private $websiteRepository; + + /** + * @var ProductRepositoryInterface + */ + private $productRepository; + + /** + * @var RuleInterfaceFactory + */ + private $catalogRuleFactory; + + /** + * @var CatalogRuleRepositoryInterface + */ + private $catalogRuleRepository; + + /** + * @var ProductTierPriceInterfaceFactory + */ + private $productTierPriceFactory; + + /** + * @var ProductTierPriceExtensionFactory + */ + private $productTierPriceExtensionFactory; + + /** + * @inheritdoc + */ + protected function setUp() + { + parent::setUp(); + $this->objectManager = Bootstrap::getObjectManager(); + $this->page = $this->objectManager->create(Page::class); + $this->registry = $this->objectManager->get(Registry::class); + $this->indexBuilder = $this->objectManager->get(IndexBuilder::class); + $this->customerSession = $this->objectManager->get(Session::class); + $this->websiteRepository = $this->objectManager->get(WebsiteRepositoryInterface::class); + $this->productRepository = $this->objectManager->get(ProductRepositoryInterface::class); + $this->catalogRuleFactory = $this->objectManager->get(RuleInterfaceFactory::class); + $this->catalogRuleRepository = $this->objectManager->get(CatalogRuleRepositoryInterface::class); + $this->productTierPriceFactory = $this->objectManager->get(ProductTierPriceInterfaceFactory::class); + $this->productTierPriceExtensionFactory = $this->objectManager->get(ProductTierPriceExtensionFactory::class); + $this->productRepository->cleanCache(); + } + + /** + * @inheritdoc + */ + protected function tearDown() + { + parent::tearDown(); + $this->registry->unregister('product'); + } + + /** + * Assert that product price rendered with expected special and regular prices if + * product has special price which lower than regular and tier prices. + * + * @magentoDataFixture Magento/Catalog/_files/product_special_price.php + * + * @dataProvider tierPricesForAllCustomerGroupsDataProvider + * + * @param float $specialPrice + * @param float $regularPrice + * @param array $tierPrices + * @param array|null $tierMessageConfig + * @return void + */ + public function testRenderSpecialPriceInCombinationWithTierPrice( + float $specialPrice, + float $regularPrice, + array $tierPrices, + ?array $tierMessageConfig + ): void { + $this->assertRenderedPrices($specialPrice, $regularPrice, $tierPrices, $tierMessageConfig); + } + + /** + * Data provider with tier prices which are for all customers groups. + * + * @return array + */ + public function tierPricesForAllCustomerGroupsDataProvider(): array + { + return [ + 'fixed_tier_price_with_qty_1' => [ + 5.99, + 10, + [ + ['customer_group_id' => Group::CUST_GROUP_ALL, 'qty' => 1, 'value' => 9], + ], + null + ], + 'fixed_tier_price_with_qty_2' => [ + 5.99, + 10, + [ + ['customer_group_id' => Group::CUST_GROUP_ALL, 'qty' => 2, 'value' => 5], + ], + ['qty' => 2, 'price' => 5.00, 'percent' => 17], + ], + 'percent_tier_price_with_qty_2' => [ + 5.99, + 10, + [ + ['customer_group_id' => Group::CUST_GROUP_ALL, 'qty' => 2, 'percent_value' => 70], + ], + ['qty' => 2, 'price' => 3.00, 'percent' => 50], + ], + 'fixed_tier_price_with_qty_1_is_lower_than_special' => [ + 5, + 10, + [ + ['customer_group_id' => Group::CUST_GROUP_ALL, 'qty' => 1, 'value' => 5], + ], + null + ], + 'percent_tier_price_with_qty_1_is_lower_than_special' => [ + 3, + 10, + [ + ['customer_group_id' => Group::NOT_LOGGED_IN_ID, 'qty' => 1, 'percent_value' => 70], + ], + null + ], + ]; + } + + /** + * Assert that product price rendered with expected special and regular prices if + * product has special price which lower than regular and tier prices and customer is logged. + * + * @magentoDataFixture Magento/Catalog/_files/product_special_price.php + * @magentoDataFixture Magento/Customer/_files/customer.php + * + * @magentoAppIsolation enabled + * + * @dataProvider tierPricesForLoggedCustomerGroupDataProvider + * + * @param float $specialPrice + * @param float $regularPrice + * @param array $tierPrices + * @param array|null $tierMessageConfig + * @return void + */ + public function testRenderSpecialPriceInCombinationWithTierPriceForLoggedInUser( + float $specialPrice, + float $regularPrice, + array $tierPrices, + ?array $tierMessageConfig + ): void { + try { + $this->customerSession->setCustomerId(1); + $this->assertRenderedPrices($specialPrice, $regularPrice, $tierPrices, $tierMessageConfig); + } finally { + $this->customerSession->setCustomerId(null); + } + } + + /** + * Data provider with tier prices which are for logged customers group. + * + * @return array + */ + public function tierPricesForLoggedCustomerGroupDataProvider(): array + { + return [ + 'fixed_tier_price_with_qty_1' => [ + 5.99, + 10, + [ + ['customer_group_id' => 1, 'qty' => 1, 'value' => 9], + ], + null + ], + 'percent_tier_price_with_qty_1' => [ + 5.99, + 10, + [ + ['customer_group_id' => 1, 'qty' => 1, 'percent_value' => 30], + ], + null + ], + ]; + } + + /** + * Assert that product price rendered with expected special and regular prices if + * product has catalog rule price with different type of prices. + * + * @magentoDataFixture Magento/Catalog/_files/product_special_price.php + * @magentoDataFixture Magento/CatalogRule/_files/delete_catalog_rule_data.php + * + * @dataProvider catalogRulesDataProvider + * + * @param float $specialPrice + * @param float $regularPrice + * @param array $catalogRules + * @param array $tierPrices + * @param array|null $tierMessageConfig + * @return void + */ + public function testRenderCatalogRulePriceInCombinationWithDifferentPriceTypes( + float $specialPrice, + float $regularPrice, + array $catalogRules, + array $tierPrices, + ?array $tierMessageConfig + ): void { + $this->createCatalogRulesForProduct($catalogRules); + $this->indexBuilder->reindexFull(); + $this->assertRenderedPrices($specialPrice, $regularPrice, $tierPrices, $tierMessageConfig); + } + + /** + * Data provider with expect special and regular price, catalog rule data and tier price. + * + * @return array + */ + public function catalogRulesDataProvider(): array + { + return [ + 'fixed_catalog_rule_price_more_than_special_price' => [ + 5.99, + 10, + [ + [RuleInterface::DISCOUNT_AMOUNT => 2], + ], + [], + null + ], + 'fixed_catalog_rule_price_lower_than_special_price' => [ + 2, + 10, + [ + [RuleInterface::DISCOUNT_AMOUNT => 8], + ], + [], + null + ], + 'fixed_catalog_rule_price_more_than_tier_price' => [ + 4, + 10, + [ + [RuleInterface::DISCOUNT_AMOUNT => 6], + ], + [ + ['customer_group_id' => Group::CUST_GROUP_ALL, 'qty' => 2, 'percent_value' => 70], + ], + ['qty' => 2, 'price' => 3.00, 'percent' => 25], + ], + 'fixed_catalog_rule_price_lower_than_tier_price' => [ + 2, + 10, + [ + [RuleInterface::DISCOUNT_AMOUNT => 7], + ], + [ + ['customer_group_id' => Group::CUST_GROUP_ALL, 'qty' => 1, 'value' => 2], + ], + null + ], + 'adjust_percent_catalog_rule_price_lower_than_special_price' => [ + 4.50, + 10, + [ + [RuleInterface::DISCOUNT_AMOUNT => 45, RuleInterface::SIMPLE_ACTION => 'to_percent'], + ], + [], + null + ], + 'adjust_percent_catalog_rule_price_lower_than_tier_price' => [ + 3, + 10, + [ + [RuleInterface::DISCOUNT_AMOUNT => 30, RuleInterface::SIMPLE_ACTION => 'to_percent'], + ], + [ + ['customer_group_id' => Group::CUST_GROUP_ALL, 'qty' => 1, 'value' => 3.50], + ], + null + ], + 'percent_catalog_rule_price_lower_than_special_price' => [ + 2, + 10, + [ + [RuleInterface::DISCOUNT_AMOUNT => 2, RuleInterface::SIMPLE_ACTION => 'to_fixed'], + ], + [], + null + ], + 'percent_catalog_rule_price_lower_than_tier_price' => [ + 1, + 10, + [ + [RuleInterface::DISCOUNT_AMOUNT => 1, RuleInterface::SIMPLE_ACTION => 'to_fixed'], + ], + [ + ['customer_group_id' => Group::CUST_GROUP_ALL, 'qty' => 1, 'value' => 3], + ], + null + ], + ]; + } + + /** + * Check that price html contain all provided prices. + * + * @param string $priceHtml + * @param float $specialPrice + * @param float $regularPrice + * @return void + */ + private function checkPrices(string $priceHtml, float $specialPrice, float $regularPrice): void + { + $this->assertEquals( + 1, + Xpath::getElementsCountForXpath($this->getSpecialPriceXpath($specialPrice), $priceHtml), + "Special price {$specialPrice} is not as expected. Rendered html: {$priceHtml}" + ); + $this->assertEquals( + 1, + Xpath::getElementsCountForXpath($this->getRegularPriceLabelXpath(), $priceHtml), + "Regular price label 'Regular Price' not founded. Rendered html: {$priceHtml}" + ); + $this->assertEquals( + 1, + Xpath::getElementsCountForXpath($this->getRegularPriceXpath($regularPrice), $priceHtml), + "Regular price {$regularPrice} is not as expected. Rendered html: {$priceHtml}" + ); + } + + /** + * Assert that tier price message. + * + * @param string $priceHtml + * @param array $tierMessageConfig + * @return void + */ + private function checkTierPriceMessage(string $priceHtml, array $tierMessageConfig): void + { + $this->assertEquals( + 1, + Xpath::getElementsCountForXpath($this->getTierPriceMessageXpath($tierMessageConfig), $priceHtml), + "Tier price message not founded. Rendered html: {$priceHtml}" + ); + } + + /** + * Render price render template with product. + * + * @param ProductInterface $product + * @return string + */ + private function getPriceHtml(ProductInterface $product): string + { + $this->registerProduct($product); + $this->page->addHandle([ + 'default', + 'catalog_product_view', + ]); + $this->page->getLayout()->generateXml(); + $priceHtml = ''; + $availableChildNames = [ + 'product.info.price', + 'product.price.tier' + ]; + foreach ($this->page->getLayout()->getChildNames('product.info.main') as $childName) { + if (in_array($childName, $availableChildNames, true)) { + $priceHtml .= $this->page->getLayout()->renderElement($childName, false); + } + } + + return $priceHtml; + } + + /** + * Add product to the registry. + * + * @param ProductInterface $product + * @return void + */ + private function registerProduct(ProductInterface $product): void + { + $this->registry->unregister('product'); + $this->registry->register('product', $product); + } + + /** + * Create provided tier prices for product. + * + * @param ProductInterface $product + * @param array $tierPrices + * @return ProductInterface + */ + private function createTierPricesForProduct(ProductInterface $product, array $tierPrices): ProductInterface + { + if (empty($tierPrices)) { + return $product; + } + + $createdTierPrices = []; + foreach ($tierPrices as $tierPrice) { + $tierPriceExtensionAttribute = $this->productTierPriceExtensionFactory->create(); + $tierPriceExtensionAttribute->setWebsiteId(0); + + if (isset($tierPrice['percent_value'])) { + $tierPriceExtensionAttribute->setPercentageValue($tierPrice['percent_value']); + unset($tierPrice['percent_value']); + } + + $createdTierPrices[] = $this->productTierPriceFactory->create( + [ + 'data' => $tierPrice + ] + )->setExtensionAttributes($tierPriceExtensionAttribute); + } + $product->setTierPrices($createdTierPrices); + + return $this->productRepository->save($product); + } + + /** + * @param float $specialPrice + * @return string + */ + private function getSpecialPriceXpath(float $specialPrice): string + { + $pathsForSearch = [ + "//div[contains(@class, 'price-box') and contains(@class, 'price-final_price')]", + "//span[contains(@class, 'special-price')]", + sprintf("//span[contains(@class, 'price') and text()='$%01.2f']", $specialPrice), + ]; + + return implode('', $pathsForSearch); + } + + /** + * @param float $regularPrice + * @return string + */ + private function getRegularPriceXpath(float $regularPrice): string + { + $pathsForSearch = [ + "//div[contains(@class, 'price-box') and contains(@class, 'price-final_price')]", + "//span[contains(@class, 'old-price')]", + "//span[contains(@class, 'price-container')]", + sprintf("//span[contains(@class, 'price') and text()='$%01.2f']", $regularPrice), + ]; + + return implode('', $pathsForSearch); + } + + /** + * @return string + */ + private function getRegularPriceLabelXpath(): string + { + $pathsForSearch = [ + "//div[contains(@class, 'price-box') and contains(@class, 'price-final_price')]", + "//span[contains(@class, 'old-price')]", + "//span[contains(@class, 'price-container')]", + "//span[text()='Regular Price']", + ]; + + return implode('', $pathsForSearch); + } + + /** + * Return tier price message xpath. Message must contain expected quantity, + * price and discount percent. + * + * @param array $expectedMessage + * @return string + */ + private function getTierPriceMessageXpath(array $expectedMessage): string + { + [$qty, $price, $percent] = array_values($expectedMessage); + $liPaths = [ + "contains(@class, 'item') and contains(text(), 'Buy {$qty} for')", + sprintf("//span[contains(@class, 'price') and text()='$%01.2f']", $price), + "//span[contains(@class, 'percent') and contains(text(), '{$percent}')]", + ]; + + return sprintf( + "//ul[contains(@class, 'prices-tier') and contains(@class, 'items')]//li[%s]", + implode(' and ', $liPaths) + ); + } + + /** + * Process test with combination of special and tier price. + * + * @param float $specialPrice + * @param float $regularPrice + * @param array $tierPrices + * @param array|null $tierMessageConfig + * @return void + */ + private function assertRenderedPrices( + float $specialPrice, + float $regularPrice, + array $tierPrices, + ?array $tierMessageConfig + ): void { + $product = $this->productRepository->get('simple', false, null, true); + $product = $this->createTierPricesForProduct($product, $tierPrices); + $priceHtml = $this->getPriceHtml($product); + $this->checkPrices($priceHtml, $specialPrice, $regularPrice); + if (null !== $tierMessageConfig) { + $this->checkTierPriceMessage($priceHtml, $tierMessageConfig); + } + } + + /** + * Create provided catalog rules. + * + * @param array $catalogRules + * @return void + */ + private function createCatalogRulesForProduct(array $catalogRules): void + { + $baseWebsite = $this->websiteRepository->get('base'); + $staticRuleData = [ + RuleInterface::IS_ACTIVE => 1, + RuleInterface::NAME => 'Test rule name.', + 'customer_group_ids' => Group::NOT_LOGGED_IN_ID, + RuleInterface::SIMPLE_ACTION => 'by_fixed', + RuleInterface::STOP_RULES_PROCESSING => false, + RuleInterface::SORT_ORDER => 0, + 'sub_is_enable' => 0, + 'sub_discount_amount' => 0, + 'website_ids' => [$baseWebsite->getId()] + ]; + + foreach ($catalogRules as $catalogRule) { + $catalogRule = array_replace($staticRuleData, $catalogRule); + $catalogRule = $this->catalogRuleFactory->create(['data' => $catalogRule]); + $this->catalogRuleRepository->save($catalogRule); + } + } +} diff --git a/dev/tests/integration/testsuite/Magento/Catalog/_files/catalog_category_image.php b/dev/tests/integration/testsuite/Magento/Catalog/_files/catalog_category_image.php index 0764d466898b8..3491065323c9f 100644 --- a/dev/tests/integration/testsuite/Magento/Catalog/_files/catalog_category_image.php +++ b/dev/tests/integration/testsuite/Magento/Catalog/_files/catalog_category_image.php @@ -13,7 +13,10 @@ $mediaDirectory = $objectManager->get(\Magento\Framework\Filesystem::class) ->getDirectoryWrite(DirectoryList::MEDIA); $fileName = 'magento_small_image.jpg'; +$fileNameLong = 'magento_long_image_name_magento_long_image_name_magento_long_image_name.jpg'; $filePath = 'catalog/category/' . $fileName; +$filePathLong = 'catalog/category/' . $fileNameLong; $mediaDirectory->create('catalog/category'); copy(__DIR__ . DIRECTORY_SEPARATOR . $fileName, $mediaDirectory->getAbsolutePath($filePath)); +copy(__DIR__ . DIRECTORY_SEPARATOR . $fileNameLong, $mediaDirectory->getAbsolutePath($filePathLong)); diff --git a/dev/tests/integration/testsuite/Magento/Catalog/_files/catalog_category_with_image.php b/dev/tests/integration/testsuite/Magento/Catalog/_files/catalog_category_with_image.php index 934abffcb9c5b..d8dfdbcd84993 100644 --- a/dev/tests/integration/testsuite/Magento/Catalog/_files/catalog_category_with_image.php +++ b/dev/tests/integration/testsuite/Magento/Catalog/_files/catalog_category_with_image.php @@ -4,7 +4,7 @@ * See COPYING.txt for license details. */ -require_once 'catalog_category_image.php'; +require 'catalog_category_image.php'; /** @var $category \Magento\Catalog\Model\Category */ $objectManager = \Magento\TestFramework\Helper\Bootstrap::getObjectManager(); diff --git a/dev/tests/integration/testsuite/Magento/Catalog/_files/catalog_category_with_long_image_name.php b/dev/tests/integration/testsuite/Magento/Catalog/_files/catalog_category_with_long_image_name.php new file mode 100644 index 0000000000000..f1fa3ee3318ee --- /dev/null +++ b/dev/tests/integration/testsuite/Magento/Catalog/_files/catalog_category_with_long_image_name.php @@ -0,0 +1,32 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ + +require 'catalog_category_image.php'; + +/** @var $category \Magento\Catalog\Model\Category */ +$objectManager = \Magento\TestFramework\Helper\Bootstrap::getObjectManager(); + +$categoryParent = $objectManager->create(\Magento\Catalog\Model\Category::class); +$categoryParent->setName('Parent Image Category') + ->setPath('1/2') + ->setLevel(2) + ->setImage($filePathLong) + ->setAvailableSortBy('name') + ->setDefaultSortBy('name') + ->setIsActive(true) + ->setPosition(1) + ->save(); + +$categoryChild = $objectManager->create(\Magento\Catalog\Model\Category::class); +$categoryChild->setName('Child Image Category') + ->setPath($categoryParent->getPath()) + ->setLevel(3) + ->setImage($fileNameLong) + ->setAvailableSortBy('name') + ->setDefaultSortBy('name') + ->setIsActive(true) + ->setPosition(2) + ->save(); diff --git a/dev/tests/integration/testsuite/Magento/Catalog/_files/catalog_category_with_long_image_name_rollback.php b/dev/tests/integration/testsuite/Magento/Catalog/_files/catalog_category_with_long_image_name_rollback.php new file mode 100644 index 0000000000000..baea438e9340c --- /dev/null +++ b/dev/tests/integration/testsuite/Magento/Catalog/_files/catalog_category_with_long_image_name_rollback.php @@ -0,0 +1,24 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ + +/** @var \Magento\Framework\ObjectManagerInterface $objectManager */ +$objectManager = \Magento\TestFramework\Helper\Bootstrap::getObjectManager(); + +/** @var \Magento\Framework\Registry $registry */ +$registry = $objectManager->get(\Magento\Framework\Registry::class); + +$registry->unregister('isSecureArea'); +$registry->register('isSecureArea', true); + +/** @var \Magento\Catalog\Model\ResourceModel\Product\Collection $collection */ +$collection = $objectManager->create(\Magento\Catalog\Model\ResourceModel\Category\Collection::class); +$collection + ->addAttributeToFilter('name', ['in' => ['Parent Image Category', 'Child Image Category']]) + ->load() + ->delete(); + +$registry->unregister('isSecureArea'); +$registry->register('isSecureArea', false); diff --git a/dev/tests/integration/testsuite/Magento/Catalog/_files/categories.php b/dev/tests/integration/testsuite/Magento/Catalog/_files/categories.php index 25bb55ffbc32c..4255d7d3c98e5 100644 --- a/dev/tests/integration/testsuite/Magento/Catalog/_files/categories.php +++ b/dev/tests/integration/testsuite/Magento/Catalog/_files/categories.php @@ -20,7 +20,7 @@ ] ); -/** @var Magento\Catalog\Api\CategoryLinkManagementInterface $linkManagement */ +/** @var Magento\Catalog\Api\CategoryLinkManagementInterface $categoryLinkManagement */ $categoryLinkManagement = $objectManager->create(\Magento\Catalog\Api\CategoryLinkManagementInterface::class); $reflectionClass = new \ReflectionClass(get_class($categoryLinkManagement)); $properties = [ @@ -115,6 +115,7 @@ ->setName('Inactive') ->setParentId(2) ->setPath('1/2/8') + ->setLevel(2) ->setAvailableSortBy('name') ->setDefaultSortBy('name') ->setIsActive(false) diff --git a/dev/tests/integration/testsuite/Magento/Catalog/_files/category_product_assigned_to_website.php b/dev/tests/integration/testsuite/Magento/Catalog/_files/category_product_assigned_to_website.php new file mode 100644 index 0000000000000..b4fd3d997c924 --- /dev/null +++ b/dev/tests/integration/testsuite/Magento/Catalog/_files/category_product_assigned_to_website.php @@ -0,0 +1,61 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +declare(strict_types=1); + +use Magento\Catalog\Api\CategoryRepositoryInterface; +use Magento\Catalog\Api\ProductRepositoryInterface; +use Magento\Catalog\Model\CategoryFactory; +use Magento\Catalog\Model\Product\Attribute\Source\Status; +use Magento\Catalog\Model\Product\Type; +use Magento\Catalog\Model\Product\Visibility; +use Magento\Catalog\Model\ProductFactory; +use Magento\Framework\ObjectManagerInterface; +use Magento\Store\Api\WebsiteRepositoryInterface; +use Magento\TestFramework\Helper\Bootstrap; + +/** @var ObjectManagerInterface $objectManager */ +$objectManager = Bootstrap::getObjectManager(); +/** @var CategoryFactory $categoryFactory */ +$categoryFactory = $objectManager->get(CategoryFactory::class); +/** @var ProductFactory $productFactory */ +$productFactory = $objectManager->get(ProductFactory::class); +/** @var CategoryRepositoryInterface $categoryRepository */ +$categoryRepository = $objectManager->get(CategoryRepositoryInterface::class); +/** @var ProductRepositoryInterface $productRepository */ +$productRepository = $objectManager->get(ProductRepositoryInterface::class); +/** @var WebsiteRepositoryInterface $websiteRepository */ +$websiteRepository = $objectManager->get(WebsiteRepositoryInterface::class); +$defaultWebsiteId = $websiteRepository->get('base')->getId(); +$category = $categoryFactory->create(); +$categoryData = [ + 'name' => 'Category with product', + 'attribute_set_id' => $category->getDefaultAttributeSetId(), + 'parent_id' => 2, + 'is_active' => true, +]; +$category->setData($categoryData); +$category = $categoryRepository->save($category); + +$product = $productFactory->create(); +$productData = [ + 'type_id' => Type::TYPE_SIMPLE, + 'attribute_set_id' => $product->getDefaultAttributeSetId(), + 'sku' => 'product_with_category', + 'website_ids' => [$defaultWebsiteId], + 'name' => 'Product with category', + 'price' => 10, + 'stock_data' => [ + 'use_config_manage_stock' => 1, + 'qty' => 100, + 'is_qty_decimal' => 0, + 'is_in_stock' => 1, + ], + 'category_ids' => [2, $category->getId()], + 'visibility' => Visibility::VISIBILITY_BOTH, + 'status' => Status::STATUS_ENABLED, +]; +$product->setData($productData); +$productRepository->save($product); diff --git a/dev/tests/integration/testsuite/Magento/Catalog/_files/category_product_assigned_to_website_rollback.php b/dev/tests/integration/testsuite/Magento/Catalog/_files/category_product_assigned_to_website_rollback.php new file mode 100644 index 0000000000000..aab3c6f938248 --- /dev/null +++ b/dev/tests/integration/testsuite/Magento/Catalog/_files/category_product_assigned_to_website_rollback.php @@ -0,0 +1,42 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +declare(strict_types=1); + +use Magento\Catalog\Api\CategoryRepositoryInterface; +use Magento\Catalog\Api\ProductRepositoryInterface; +use Magento\Framework\Exception\NoSuchEntityException; +use Magento\Framework\ObjectManagerInterface; +use Magento\Framework\Registry; +use Magento\TestFramework\Catalog\Model\GetCategoryByName; +use Magento\TestFramework\Helper\Bootstrap; + +/** @var ObjectManagerInterface $objectManager */ +$objectManager = Bootstrap::getObjectManager(); +/** @var Registry $registry */ +$registry = $objectManager->get(Registry::class); +/** @var CategoryRepositoryInterface $categoryRepository */ +$categoryRepository = $objectManager->create(CategoryRepositoryInterface::class); +/** @var ProductRepositoryInterface $productRepository */ +$productRepository = $objectManager->get(ProductRepositoryInterface::class); +/** @var GetCategoryByName $getCategoryByName */ +$getCategoryByName = $objectManager->create(GetCategoryByName::class); +$registry->unregister('isSecureArea'); +$registry->register('isSecureArea', true); + +try { + $productRepository->deleteById('product_with_category'); +} catch (NoSuchEntityException $e) { + // product already deleted +} + +$category = $getCategoryByName->execute('Category with product'); + +if ($category->getId()) { + $categoryRepository->delete($category); +} + +$registry->unregister('isSecureArea'); +$registry->register('isSecureArea', false); diff --git a/dev/tests/integration/testsuite/Magento/Catalog/_files/category_tree_with_products.php b/dev/tests/integration/testsuite/Magento/Catalog/_files/category_tree_with_products.php new file mode 100644 index 0000000000000..d98b1b738cf0d --- /dev/null +++ b/dev/tests/integration/testsuite/Magento/Catalog/_files/category_tree_with_products.php @@ -0,0 +1,114 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +declare(strict_types=1); + +use Magento\Catalog\Api\CategoryRepositoryInterface; +use Magento\Catalog\Api\Data\ProductInterfaceFactory; +use Magento\Catalog\Api\ProductRepositoryInterface; +use Magento\Catalog\Api\Data\CategoryInterfaceFactory; +use Magento\Catalog\Model\Product\Attribute\Source\Status; +use Magento\Catalog\Model\Product\Type; +use Magento\Catalog\Model\Product\Visibility; +use Magento\Store\Model\Store; +use Magento\TestFramework\Helper\Bootstrap; +use Magento\Eav\Model\Config; + +$objectManager = Bootstrap::getObjectManager(); +$categoryFactory = $objectManager->get(CategoryInterfaceFactory::class); +$productFactory = $objectManager->get(ProductInterfaceFactory::class); +$productRepository = $objectManager->get(ProductRepositoryInterface::class); +$categoryRepository = $objectManager->get(CategoryRepositoryInterface::class); + +$categoryA = $categoryFactory->create( + [ + 'data' => [ + 'name' => 'Category A', + 'parent_id' => 2, + 'level' => 2, + 'position' => 1, + 'is_active' => true, + 'available_sort_by' =>['position', 'name'], + 'default_sort_by' => 'name', + ], + ] +); +$categoryA->isObjectNew(true); +$categoryA = $categoryRepository->save($categoryA); + +$categoryB = $categoryFactory->create( + [ + 'data' => [ + 'name' => 'Category B', + 'parent_id' => 2, + 'level' => 2, + 'position' => 1, + 'is_active' => true, + 'available_sort_by' =>['position', 'name'], + 'default_sort_by' => 'name', + ], + ] +); +$categoryB->isObjectNew(true); +$categoryB = $categoryRepository->save($categoryB); + +$categoryC = $categoryFactory->create( + [ + 'data' => [ + 'name' => 'Category C', + 'parent_id' => $categoryB->getId(), + 'level' => 2, + 'position' => 1, + 'is_active' => true, + 'available_sort_by' =>['position', 'name'], + 'default_sort_by' => 'name', + ], + ] +); +$categoryC->isObjectNew(true); +$categoryC = $categoryRepository->save($categoryC); + +$defaultAttributeSet = $objectManager->get(Config::class) + ->getEntityType('catalog_product') + ->getDefaultAttributeSetId(); +$product = $productFactory->create( + [ + 'data' => [ + 'type_id' => Type::TYPE_SIMPLE, + 'attribute_set_id' => $defaultAttributeSet, + 'store_id' => Store::DEFAULT_STORE_ID, + 'website_ids' => [1], + 'name' => 'Simple Product B', + 'sku' => 'simpleB', + 'price' => 10, + 'weight' => 1, + 'stock_data' => ['use_config_manage_stock' => 0], + 'category_ids' => [$categoryB->getId()], + 'visibility' => Visibility::VISIBILITY_BOTH, + 'status' => Status::STATUS_ENABLED, + ], + ] +); +$productRepository->save($product); + +$product = $productFactory->create( + [ + 'data' => [ + 'type_id' => Type::TYPE_SIMPLE, + 'attribute_set_id' => $defaultAttributeSet, + 'store_id' => Store::DEFAULT_STORE_ID, + 'website_ids' => [1], + 'name' => 'Simple Product C', + 'sku' => 'simpleC', + 'price' => 20, + 'weight' => 1, + 'stock_data' => ['use_config_manage_stock' => 0], + 'category_ids' => [$categoryC->getId()], + 'visibility' => Visibility::VISIBILITY_BOTH, + 'status' => Status::STATUS_ENABLED, + ], + ] +); +$productRepository->save($product); diff --git a/dev/tests/integration/testsuite/Magento/Catalog/_files/category_tree_with_products_rollback.php b/dev/tests/integration/testsuite/Magento/Catalog/_files/category_tree_with_products_rollback.php new file mode 100644 index 0000000000000..448bc60f50b90 --- /dev/null +++ b/dev/tests/integration/testsuite/Magento/Catalog/_files/category_tree_with_products_rollback.php @@ -0,0 +1,44 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +declare(strict_types=1); + +use Magento\Catalog\Api\CategoryRepositoryInterface; +use Magento\Catalog\Api\ProductRepositoryInterface; +use Magento\Framework\Exception\NoSuchEntityException; +use Magento\Framework\Registry; +use Magento\TestFramework\Helper\Bootstrap; + +$objectManager = Bootstrap::getObjectManager(); +$registry = $objectManager->get(Registry::class); +$registry->unregister('isSecureArea'); +$registry->register('isSecureArea', true); + +$productRepository = $objectManager->get(ProductRepositoryInterface::class); +$categoryRepository = $objectManager->get(CategoryRepositoryInterface::class); + +$productSkus = ['simpleB', 'simpleC']; + +foreach ($productSkus as $productSku) { + try { + $productRepository->deleteById($productSku); + } catch (NoSuchEntityException $e) { + //Already deleted. + } +} + +$categoriesNames = ['Category A', 'Category B', 'Category C']; + +foreach ($categoriesNames as $categoryName) { + try { + $category = $categoryRepository->get($categoryName); + $categoryRepository->delete($category); + } catch (NoSuchEntityException $e) { + //Already deleted. + } +} + +$registry->unregister('isSecureArea'); +$registry->register('isSecureArea', false); diff --git a/dev/tests/integration/testsuite/Magento/Catalog/_files/category_with_different_price_products.php b/dev/tests/integration/testsuite/Magento/Catalog/_files/category_with_different_price_products.php index 2e87e1e820f86..daa7f9f61a841 100644 --- a/dev/tests/integration/testsuite/Magento/Catalog/_files/category_with_different_price_products.php +++ b/dev/tests/integration/testsuite/Magento/Catalog/_files/category_with_different_price_products.php @@ -15,20 +15,30 @@ use Magento\Store\Model\Store; use Magento\Store\Model\StoreManagerInterface; use Magento\TestFramework\Helper\Bootstrap; +use Magento\Catalog\Helper\DefaultCategory; $objectManager = Bootstrap::getObjectManager(); +/** @var StoreManagerInterface $storeManager */ $storeManager = $objectManager->get(StoreManagerInterface::class); +/** @var CategoryInterfaceFactory $categoryFactory */ $categoryFactory = $objectManager->get(CategoryInterfaceFactory::class); +/** @var ProductInterfaceFactory $productFactory */ $productFactory = $objectManager->get(ProductInterfaceFactory::class); +/** @var ProductRepositoryInterface $productRepository */ $productRepository = $objectManager->get(ProductRepositoryInterface::class); +$productRepository->cleanCache(); +/** @var CategoryRepositoryInterface $categoryRepository */ $categoryRepository = $objectManager->get(CategoryRepositoryInterface::class); +/** @var DefaultCategory $categoryHelper */ +$categoryHelper = $objectManager->get(DefaultCategory::class); $currentStoreId = $storeManager->getStore()->getId(); +$defaultWebsiteId = $storeManager->getWebsite('base')->getId(); $storeManager->setCurrentStore(Store::DEFAULT_STORE_ID); $category = $categoryFactory->create(); $category->isObjectNew(true); $category->setName('Category 999') - ->setParentId(2) + ->setParentId($categoryHelper->getId()) ->setLevel(2) ->setAvailableSortBy('name') ->setDefaultSortBy('name') @@ -41,7 +51,7 @@ $product->setTypeId(Type::TYPE_SIMPLE) ->setAttributeSetId($product->getDefaultAttributeSetId()) ->setStoreId(Store::DEFAULT_STORE_ID) - ->setWebsiteIds([1]) + ->setWebsiteIds([$defaultWebsiteId]) ->setName('Simple Product With Price 10') ->setSku('simple1000') ->setPrice(10) @@ -52,11 +62,11 @@ ->setStatus(Status::STATUS_ENABLED); $productRepository->save($product); -$product = $productFactory->create(); -$product->setTypeId(Type::TYPE_SIMPLE) - ->setAttributeSetId($product->getDefaultAttributeSetId()) +$product2 = $productFactory->create(); +$product2->setTypeId(Type::TYPE_SIMPLE) + ->setAttributeSetId($product2->getDefaultAttributeSetId()) ->setStoreId(Store::DEFAULT_STORE_ID) - ->setWebsiteIds([1]) + ->setWebsiteIds([$defaultWebsiteId]) ->setName('Simple Product With Price 20') ->setSku('simple1001') ->setPrice(20) @@ -65,4 +75,4 @@ ->setCategoryIds([$category->getId()]) ->setVisibility(Visibility::VISIBILITY_BOTH) ->setStatus(Status::STATUS_ENABLED); -$productRepository->save($product); +$productRepository->save($product2); diff --git a/dev/tests/integration/testsuite/Magento/Catalog/_files/category_with_different_price_products_on_two_websites.php b/dev/tests/integration/testsuite/Magento/Catalog/_files/category_with_different_price_products_on_two_websites.php new file mode 100644 index 0000000000000..91a5ece179ba6 --- /dev/null +++ b/dev/tests/integration/testsuite/Magento/Catalog/_files/category_with_different_price_products_on_two_websites.php @@ -0,0 +1,26 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +declare(strict_types=1); + +use Magento\Store\Api\WebsiteRepositoryInterface; +use Magento\TestFramework\Helper\Bootstrap; + +require __DIR__ . '/category_with_different_price_products.php'; +require __DIR__ . '/../../Store/_files/second_website_with_two_stores.php'; + +$objectManager = Bootstrap::getObjectManager(); +/** @var WebsiteRepositoryInterface $websiteRepository */ +$websiteRepository = $objectManager->get(WebsiteRepositoryInterface::class); +$defaultWebsiteId = $websiteRepository->get('base')->getId(); +$websiteId = $websiteRepository->get('test')->getId(); + +$product = $productRepository->get('simple1000'); +$product->setWebsiteIds([$defaultWebsiteId, $websiteId]); +$productRepository->save($product); + +$product = $productRepository->get('simple1001'); +$product->setWebsiteIds([$defaultWebsiteId, $websiteId]); +$productRepository->save($product); diff --git a/dev/tests/integration/testsuite/Magento/Catalog/_files/category_with_different_price_products_on_two_websites_rollback.php b/dev/tests/integration/testsuite/Magento/Catalog/_files/category_with_different_price_products_on_two_websites_rollback.php new file mode 100644 index 0000000000000..d4c531c4da4db --- /dev/null +++ b/dev/tests/integration/testsuite/Magento/Catalog/_files/category_with_different_price_products_on_two_websites_rollback.php @@ -0,0 +1,9 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +declare(strict_types=1); + +require __DIR__ . '/category_with_different_price_products_rollback.php'; +require __DIR__ . '/../../Store/_files/second_website_with_two_stores_rollback.php'; diff --git a/dev/tests/integration/testsuite/Magento/Catalog/_files/category_with_parent_anchor.php b/dev/tests/integration/testsuite/Magento/Catalog/_files/category_with_parent_anchor.php new file mode 100644 index 0000000000000..abdec4954135b --- /dev/null +++ b/dev/tests/integration/testsuite/Magento/Catalog/_files/category_with_parent_anchor.php @@ -0,0 +1,41 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +declare(strict_types=1); + +use Magento\Catalog\Model\CategoryFactory; +use Magento\Catalog\Model\CategoryRepository; +use Magento\Framework\ObjectManagerInterface; +use Magento\TestFramework\Helper\Bootstrap; + +/** @var ObjectManagerInterface $objectManager */ +$objectManager = Bootstrap::getObjectManager(); +/** @var CategoryFactory $categoryFactory */ +$categoryFactory = $objectManager->get(CategoryFactory::class); +/** @var CategoryRepository $categoryRepository */ +$categoryRepository = $objectManager->create(CategoryRepository::class); +$parentCategory = $categoryFactory->create(); +$attributeSetId = $parentCategory->getDefaultAttributeSetId(); +$parentCategory->isObjectNew(true); +$parentCategoryData = [ + 'name' => 'Parent category', + 'attribute_set_id' => $attributeSetId, + 'parent_id' => 2, + 'is_active' => true, + 'is_anchor' => true, +]; +$parentCategory->setData($parentCategoryData); +$parentCategoryId = $categoryRepository->save($parentCategory)->getId(); + +$category = $categoryFactory->create(); +$category->isObjectNew(true); +$categoryData = [ + 'name' => 'Child category', + 'attribute_set_id' => $attributeSetId, + 'parent_id' => $parentCategoryId, + 'is_active' => true, +]; +$category->setData($categoryData); +$categoryRepository->save($category); diff --git a/dev/tests/integration/testsuite/Magento/Catalog/_files/category_with_parent_anchor_rollback.php b/dev/tests/integration/testsuite/Magento/Catalog/_files/category_with_parent_anchor_rollback.php new file mode 100644 index 0000000000000..35a0ee38c8cfe --- /dev/null +++ b/dev/tests/integration/testsuite/Magento/Catalog/_files/category_with_parent_anchor_rollback.php @@ -0,0 +1,38 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +declare(strict_types=1); + +use Magento\Catalog\Api\CategoryRepositoryInterface; +use Magento\Catalog\Api\Data\CategoryInterface; +use Magento\Catalog\Model\ResourceModel\Category\CollectionFactory; +use Magento\Framework\ObjectManagerInterface; +use Magento\Framework\Registry; +use Magento\TestFramework\Helper\Bootstrap; + +/** @var ObjectManagerInterface $objectManager */ +$objectManager = Bootstrap::getObjectManager(); +/** @var Registry $registry */ +$registry = $objectManager->get(Registry::class); +/** @var CollectionFactory $categoryCollectionFactory */ +$categoryCollectionFactory = $objectManager->get(CollectionFactory::class); +/** @var CategoryRepositoryInterface $categoryRepository */ +$categoryRepository = $objectManager->create(CategoryRepositoryInterface::class); + +$registry->unregister('isSecureArea'); +$registry->register('isSecureArea', true); + +$categoryCollection = $categoryCollectionFactory->create(); +$categoryCollection->addAttributeToFilter( + CategoryInterface::KEY_NAME, + ['in' => ['Parent category', 'Child category']] +); + +foreach ($categoryCollection as $category) { + $categoryRepository->delete($category); +} + +$registry->unregister('isSecureArea'); +$registry->register('isSecureArea', false); diff --git a/dev/tests/integration/testsuite/Magento/Catalog/_files/category_with_three_products.php b/dev/tests/integration/testsuite/Magento/Catalog/_files/category_with_three_products.php new file mode 100644 index 0000000000000..b29c17e392ed9 --- /dev/null +++ b/dev/tests/integration/testsuite/Magento/Catalog/_files/category_with_three_products.php @@ -0,0 +1,28 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +declare(strict_types=1); + +use Magento\Catalog\Model\Product\Visibility; +use Magento\Catalog\Model\Product\Attribute\Source\Status; +use Magento\Catalog\Model\Product\Type; +use Magento\Store\Model\Store; + +require __DIR__ . '/category_with_different_price_products.php'; + +$product = $productFactory->create(); +$product->setTypeId(Type::TYPE_SIMPLE) + ->setAttributeSetId(4) + ->setStoreId(Store::DEFAULT_STORE_ID) + ->setWebsiteIds([1]) + ->setName('Simple Product2') + ->setSku('simple1002') + ->setPrice(10) + ->setWeight(1) + ->setStockData(['use_config_manage_stock' => 0]) + ->setCategoryIds([$category->getId()]) + ->setVisibility(Visibility::VISIBILITY_BOTH) + ->setStatus(Status::STATUS_ENABLED); +$productRepository->save($product); diff --git a/dev/tests/integration/testsuite/Magento/Catalog/_files/category_with_three_products_rollback.php b/dev/tests/integration/testsuite/Magento/Catalog/_files/category_with_three_products_rollback.php new file mode 100644 index 0000000000000..a90b9e732e827 --- /dev/null +++ b/dev/tests/integration/testsuite/Magento/Catalog/_files/category_with_three_products_rollback.php @@ -0,0 +1,28 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +declare(strict_types=1); + +use Magento\Catalog\Api\ProductRepositoryInterface; +use Magento\Framework\Exception\NoSuchEntityException; +use Magento\Framework\Registry; +use Magento\TestFramework\Helper\Bootstrap; + +$objectManager = Bootstrap::getObjectManager(); +$registry = $objectManager->get(Registry::class); +$registry->unregister('isSecureArea'); +$registry->register('isSecureArea', true); + +$productRepository = $objectManager->get(ProductRepositoryInterface::class); +try { + $productRepository->deleteById('simple1002'); +} catch (NoSuchEntityException $e) { + //Already deleted. +} + +$registry->unregister('isSecureArea'); +$registry->register('isSecureArea', false); + +require __DIR__ . '/category_with_different_price_products_rollback.php'; diff --git a/dev/tests/integration/testsuite/Magento/Catalog/_files/magento_long_image_name_magento_long_image_name_magento_long_image_name.jpg b/dev/tests/integration/testsuite/Magento/Catalog/_files/magento_long_image_name_magento_long_image_name_magento_long_image_name.jpg new file mode 100644 index 0000000000000..bed66dfbcb1c3 Binary files /dev/null and b/dev/tests/integration/testsuite/Magento/Catalog/_files/magento_long_image_name_magento_long_image_name_magento_long_image_name.jpg differ diff --git a/dev/tests/integration/testsuite/Magento/Catalog/_files/order_item_with_product_and_custom_options.php b/dev/tests/integration/testsuite/Magento/Catalog/_files/order_item_with_product_and_custom_options.php index f94aa03a9f832..c53becc3193d4 100644 --- a/dev/tests/integration/testsuite/Magento/Catalog/_files/order_item_with_product_and_custom_options.php +++ b/dev/tests/integration/testsuite/Magento/Catalog/_files/order_item_with_product_and_custom_options.php @@ -48,6 +48,7 @@ /** @var \Magento\Sales\Model\Order\Item $orderItem */ $orderItem = $objectManager->create(\Magento\Sales\Model\Order\Item::class); $orderItem->setProductId($product->getId()); +$orderItem->setSku($product->getSku()); $orderItem->setQtyOrdered(1); $orderItem->setBasePrice($product->getPrice()); $orderItem->setPrice($product->getPrice()); diff --git a/dev/tests/integration/testsuite/Magento/Catalog/_files/out_of_stock_product_with_category.php b/dev/tests/integration/testsuite/Magento/Catalog/_files/out_of_stock_product_with_category.php new file mode 100644 index 0000000000000..43670125e0315 --- /dev/null +++ b/dev/tests/integration/testsuite/Magento/Catalog/_files/out_of_stock_product_with_category.php @@ -0,0 +1,53 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +declare(strict_types=1); + +use Magento\Catalog\Api\CategoryLinkManagementInterface; +use Magento\Catalog\Api\ProductRepositoryInterface; +use Magento\Catalog\Model\Product\Attribute\Source\Status; +use Magento\Catalog\Model\Product\Type; +use Magento\Catalog\Model\Product\Visibility; +use Magento\Catalog\Model\ProductFactory; +use Magento\TestFramework\Helper\Bootstrap; + +require __DIR__ . '/category.php'; + +$objectManager = Bootstrap::getObjectManager(); +/** @var ProductFactory $productFactory */ +$productFactory = $objectManager->get(ProductFactory::class); +/** @var CategoryLinkManagementInterface $categoryLinkManagement */ +$categoryLinkManagement = $objectManager->get(CategoryLinkManagementInterface::class); +$product = $productFactory->create(); +$product->isObjectNew(true); +$product->setTypeId(Type::TYPE_SIMPLE) + ->setAttributeSetId($product->getDefaultAttributeSetId()) + ->setWebsiteIds([1]) + ->setName('Simple Product Out Of Stock') + ->setSku('out-of-stock-product') + ->setPrice(10) + ->setWeight(1) + ->setShortDescription("Short description") + ->setTaxClassId(0) + ->setDescription('Description with <b>html tag</b>') + ->setMetaTitle('meta title') + ->setMetaKeyword('meta keyword') + ->setMetaDescription('meta description') + ->setVisibility(Visibility::VISIBILITY_BOTH) + ->setStatus(Status::STATUS_ENABLED) + ->setCategoryIds([333]) + ->setStockData( + [ + 'use_config_manage_stock' => 1, + 'qty' => 0, + 'is_qty_decimal' => 0, + 'is_in_stock' => 0, + ] + ) + ->setCanSaveCustomOptions(true) + ->setHasOptions(true); +/** @var ProductRepositoryInterface $productRepositoryFactory */ +$productRepository = $objectManager->create(ProductRepositoryInterface::class); +$productRepository->save($product); diff --git a/dev/tests/integration/testsuite/Magento/Catalog/_files/out_of_stock_product_with_category_rollback.php b/dev/tests/integration/testsuite/Magento/Catalog/_files/out_of_stock_product_with_category_rollback.php new file mode 100644 index 0000000000000..ee07b064adc66 --- /dev/null +++ b/dev/tests/integration/testsuite/Magento/Catalog/_files/out_of_stock_product_with_category_rollback.php @@ -0,0 +1,30 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +declare(strict_types=1); + +use Magento\Catalog\Api\ProductRepositoryInterface; +use Magento\Framework\Exception\NoSuchEntityException; +use Magento\Framework\Registry; +use Magento\TestFramework\Helper\Bootstrap; + +require __DIR__ . '/category_rollback.php'; + +$objectManager = Bootstrap::getObjectManager(); +/** @var Registry $registry */ +$registry = $objectManager->get(Registry::class); + +$registry->unregister('isSecureArea'); +$registry->register('isSecureArea', true); +/** @var ProductRepositoryInterface $productRepository */ +$productRepository = $objectManager->create(ProductRepositoryInterface::class); + +try { + $productRepository->deleteById('out-of-stock-product'); +} catch (NoSuchEntityException $e) { + //already removed +} +$registry->unregister('isSecureArea'); +$registry->register('isSecureArea', false); diff --git a/dev/tests/integration/testsuite/Magento/Catalog/_files/product_datetime_attribute.php b/dev/tests/integration/testsuite/Magento/Catalog/_files/product_datetime_attribute.php new file mode 100644 index 0000000000000..c1e788861266c --- /dev/null +++ b/dev/tests/integration/testsuite/Magento/Catalog/_files/product_datetime_attribute.php @@ -0,0 +1,54 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +declare(strict_types=1); + +use Magento\Catalog\Api\Data\ProductAttributeInterface; +use Magento\Catalog\Api\ProductAttributeRepositoryInterface; +use Magento\Catalog\Model\ResourceModel\Eav\Attribute; +use Magento\Catalog\Model\ResourceModel\Eav\AttributeFactory; +use Magento\Catalog\Setup\CategorySetup; +use Magento\TestFramework\Helper\Bootstrap; + +$objectManager = Bootstrap::getObjectManager(); +/** @var CategorySetup $installer */ +$installer = $objectManager->create(CategorySetup::class); +$entityType = $installer->getEntityTypeId(ProductAttributeInterface::ENTITY_TYPE_CODE); +/** @var ProductAttributeRepositoryInterface $attributeRepository */ +$attributeRepository = $objectManager->create(ProductAttributeRepositoryInterface::class); +/** @var Attribute $attribute */ +$attribute = $objectManager->get(AttributeFactory::class)->create(); +if (!$attribute->loadByCode($entityType, 'datetime_attribute')->getAttributeId()) { + $attribute->setData( + [ + 'attribute_code' => 'datetime_attribute', + 'entity_type_id' => $entityType, + 'is_global' => 1, + 'is_user_defined' => 1, + 'frontend_input' => 'datetime', + 'is_unique' => 0, + 'is_required' => 0, + 'is_searchable' => 0, + 'is_visible_in_advanced_search' => 0, + 'is_comparable' => 0, + 'is_filterable' => 0, + 'is_filterable_in_search' => 0, + 'is_used_for_promo_rules' => 0, + 'is_html_allowed_on_front' => 1, + 'is_visible_on_front' => 0, + 'used_in_product_listing' => 1, + 'used_for_sort_by' => 0, + 'frontend_label' => ['Date Time Attribute'], + 'backend_type' => 'datetime', + ] + ); + $attributeRepository->save($attribute); + $installer->addAttributeToGroup( + ProductAttributeInterface::ENTITY_TYPE_CODE, + 'Default', + 'General', + $attribute->getId() + ); +} diff --git a/dev/tests/integration/testsuite/Magento/Catalog/_files/product_datetime_attribute_rollback.php b/dev/tests/integration/testsuite/Magento/Catalog/_files/product_datetime_attribute_rollback.php new file mode 100644 index 0000000000000..51b1ea5418ca9 --- /dev/null +++ b/dev/tests/integration/testsuite/Magento/Catalog/_files/product_datetime_attribute_rollback.php @@ -0,0 +1,25 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +declare(strict_types=1); + +use Magento\Framework\Exception\NoSuchEntityException; +use Magento\TestFramework\Helper\Bootstrap; +use Magento\Framework\Registry; +use Magento\Catalog\Api\ProductAttributeRepositoryInterface; + +$objectManager = Bootstrap::getObjectManager(); +$registry = $objectManager->get(Registry::class); +$registry->unregister('isSecureArea'); +$registry->register('isSecureArea', true); +/** @var ProductAttributeRepositoryInterface $attributeRepository */ +$attributeRepository = $objectManager->create(ProductAttributeRepositoryInterface::class); + +try { + $attributeRepository->deleteById('datetime_attribute'); +} catch (NoSuchEntityException $e) { +} +$registry->unregister('isSecureArea'); +$registry->register('isSecureArea', false); diff --git a/dev/tests/integration/testsuite/Magento/Catalog/_files/product_in_nested_anchor_categories.php b/dev/tests/integration/testsuite/Magento/Catalog/_files/product_in_nested_anchor_categories.php new file mode 100644 index 0000000000000..39d939575f5bf --- /dev/null +++ b/dev/tests/integration/testsuite/Magento/Catalog/_files/product_in_nested_anchor_categories.php @@ -0,0 +1,75 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +declare(strict_types=1); + +$categories = [ + [ + 'id' => 444, + 'parentId' => 2, + 'level' => 2, + 'path' => '1/2/3' + ], + [ + 'id' => 445, + 'parentId' => 444, + 'level' => 3, + 'path' => '1/2/3/4' + ], + [ + 'id' => 446, + 'parentId' => 445, + 'level' => 4, + 'path' => '1/2/3/4/5' + ], +]; + +$products = [ + [ + 'id' => 444, + 'categoryIDs' => [446] + ], + [ + 'id' => 445, + 'categoryIDs' => [446] + ] +]; + +foreach ($categories as $category) { + $categoryModel = \Magento\TestFramework\Helper\Bootstrap::getObjectManager() + ->create(\Magento\Catalog\Model\Category::class); + $categoryModel->isObjectNew(true); + $categoryModel->setId($category['id']) + ->setName('Category ' . $category['id']) + ->setParentId($category['parentId']) + ->setPath($category['path']) + ->setLevel($category['level']) + ->setAvailableSortBy('name') + ->setDefaultSortBy('name') + ->setIsActive(true) + ->setPosition(1) + ->setAvailableSortBy(['position']) + ->save(); +} + +foreach ($products as $product) { + /** @var $product \Magento\Catalog\Model\Product */ + $productModel = \Magento\TestFramework\Helper\Bootstrap::getObjectManager() + ->create(\Magento\Catalog\Model\Product::class); + $productModel->setTypeId(\Magento\Catalog\Model\Product\Type::TYPE_SIMPLE) + ->setId($product['id']) + ->setAttributeSetId(4) + ->setStoreId(1) + ->setWebsiteIds([1]) + ->setName('Simple Product ' . $product['id']) + ->setSku('simple' . $product['id']) + ->setPrice(10) + ->setWeight(18) + ->setStockData(['use_config_manage_stock' => 0]) + ->setCategoryIds($product['categoryIDs']) + ->setVisibility(\Magento\Catalog\Model\Product\Visibility::VISIBILITY_BOTH) + ->setStatus(\Magento\Catalog\Model\Product\Attribute\Source\Status::STATUS_ENABLED) + ->save(); +} diff --git a/dev/tests/integration/testsuite/Magento/Catalog/_files/product_in_nested_anchor_categories_rollback.php b/dev/tests/integration/testsuite/Magento/Catalog/_files/product_in_nested_anchor_categories_rollback.php new file mode 100644 index 0000000000000..f735caa801794 --- /dev/null +++ b/dev/tests/integration/testsuite/Magento/Catalog/_files/product_in_nested_anchor_categories_rollback.php @@ -0,0 +1,35 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +declare(strict_types=1); + +/** @var \Magento\Framework\Registry $registry */ +$registry = \Magento\TestFramework\Helper\Bootstrap::getObjectManager() + ->get(\Magento\Framework\Registry::class); +$registry->unregister('isSecureArea'); +$registry->register('isSecureArea', true); + +$categoryIDs = [444, 445, 446]; +$productIDs = [444, 445]; + +foreach ($productIDs as $productID) { + /** @var $product \Magento\Catalog\Model\Product */ + $product = \Magento\TestFramework\Helper\Bootstrap::getObjectManager() + ->create(\Magento\Catalog\Model\Product::class); + $product->load($productID); + if ($product->getId()) { + $product->delete(); + } +} + +foreach ($categoryIDs as $categoryID) { + /** @var $category \Magento\Catalog\Model\Category */ + $category = \Magento\TestFramework\Helper\Bootstrap::getObjectManager() + ->create(\Magento\Catalog\Model\Category::class); + $category->load($categoryID); + if ($category->getId()) { + $category->delete(); + } +} diff --git a/dev/tests/integration/testsuite/Magento/Catalog/_files/product_simple_tax_none_rollback.php b/dev/tests/integration/testsuite/Magento/Catalog/_files/product_simple_tax_none_rollback.php index ceffb1c87d970..79245d255a7e6 100644 --- a/dev/tests/integration/testsuite/Magento/Catalog/_files/product_simple_tax_none_rollback.php +++ b/dev/tests/integration/testsuite/Magento/Catalog/_files/product_simple_tax_none_rollback.php @@ -5,9 +5,9 @@ */ declare(strict_types=1); -use Magento\Catalog\Api\Data\ProductInterface; use Magento\Catalog\Api\ProductRepositoryInterface; use Magento\Framework\Exception\NoSuchEntityException; +use Magento\Framework\Registry; use Magento\TestFramework\Helper\Bootstrap; use Magento\TestFramework\ObjectManager; @@ -15,19 +15,16 @@ $objectManager = Bootstrap::getObjectManager(); /** @var ProductRepositoryInterface $productRepository */ $productRepository = $objectManager->get(ProductRepositoryInterface::class); -/** @var \Magento\Framework\Registry $registry */ -$registry =$objectManager->get(\Magento\Framework\Registry::class); - +/** @var Registry $registry */ +$registry = $objectManager->get(Registry::class); $registry->unregister('isSecureArea'); $registry->register('isSecureArea', true); - try { - /** @var ProductInterface $product */ $product = $productRepository->get('simple-product-tax-none', false, null, true); $productRepository->delete($product); } catch (NoSuchEntityException $e) { // isolation on } - +$productRepository->cleanCache(); $registry->unregister('isSecureArea'); $registry->register('isSecureArea', false); diff --git a/dev/tests/integration/testsuite/Magento/Catalog/_files/product_simple_with_category_which_has_parent_category.php b/dev/tests/integration/testsuite/Magento/Catalog/_files/product_simple_with_category_which_has_parent_category.php new file mode 100644 index 0000000000000..4d3ad8fcbf2f4 --- /dev/null +++ b/dev/tests/integration/testsuite/Magento/Catalog/_files/product_simple_with_category_which_has_parent_category.php @@ -0,0 +1,47 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +declare(strict_types=1); + +use Magento\Catalog\Api\Data\ProductInterfaceFactory; +use Magento\Catalog\Api\ProductRepositoryInterface; +use Magento\Catalog\Model\Product\Attribute\Source\Status; +use Magento\Catalog\Model\Product\Visibility; +use Magento\Store\Api\WebsiteRepositoryInterface; +use Magento\TestFramework\Helper\Bootstrap; + +require __DIR__ . '/categories_no_products.php'; + +/** @var ProductRepositoryInterface $productRepository */ +$productRepository = $objectManager->get(ProductRepositoryInterface::class); +/** @var WebsiteRepositoryInterface $websiteRepository */ +$websiteRepository = $objectManager->get(WebsiteRepositoryInterface::class); +/** @var ProductInterfaceFactory $productFactory */ +$productFactory = $objectManager->get(ProductInterfaceFactory::class); +$defaultWebsiteId = $websiteRepository->get('base')->getId(); +$product = $productFactory->create(); +$product->setData( + [ + 'attribute_set_id' => $product->getDefaultAttributeSetId(), + 'website_ids' => [ + $defaultWebsiteId + ], + 'name' => 'Simple product with child category', + 'sku' => 'simple_with_child_category', + 'price' => 10, + 'description' => 'Description product with category which has parent category', + 'visibility' => Visibility::VISIBILITY_BOTH, + 'status' => Status::STATUS_ENABLED, + 'category_ids' => [5], + 'stock_data' => [ + 'use_config_manage_stock' => 1, + 'qty' => 100, + 'is_qty_decimal' => 0, + 'is_in_stock' => 1 + ], + 'url_key' => 'simple-with-child-category' + ] +); +$productRepository->save($product); diff --git a/dev/tests/integration/testsuite/Magento/Catalog/_files/product_simple_with_category_which_has_parent_category_rollback.php b/dev/tests/integration/testsuite/Magento/Catalog/_files/product_simple_with_category_which_has_parent_category_rollback.php new file mode 100644 index 0000000000000..58acf8e35129e --- /dev/null +++ b/dev/tests/integration/testsuite/Magento/Catalog/_files/product_simple_with_category_which_has_parent_category_rollback.php @@ -0,0 +1,23 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +declare(strict_types=1); + +use Magento\Catalog\Api\ProductRepositoryInterface; +use Magento\Framework\Exception\NoSuchEntityException; + +require __DIR__ . '/categories_no_products_rollback.php'; + +/** @var ProductRepositoryInterface $productRepository */ +$productRepository = $objectManager->get(ProductRepositoryInterface::class); +$registry->unregister('isSecureArea'); +$registry->register('isSecureArea', true); +try { + $productRepository->deleteById('simple_with_child_category'); +} catch (NoSuchEntityException $exception) { + //Product already deleted. +} +$registry->unregister('isSecureArea'); +$registry->register('isSecureArea', false); diff --git a/dev/tests/integration/testsuite/Magento/Catalog/_files/product_simple_with_fixed_tier_price.php b/dev/tests/integration/testsuite/Magento/Catalog/_files/product_simple_with_fixed_tier_price.php new file mode 100644 index 0000000000000..31576f2baf55b --- /dev/null +++ b/dev/tests/integration/testsuite/Magento/Catalog/_files/product_simple_with_fixed_tier_price.php @@ -0,0 +1,40 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +declare(strict_types=1); + +use Magento\Catalog\Api\Data\ProductTierPriceExtensionFactory; +use Magento\Catalog\Api\Data\ProductTierPriceInterfaceFactory; +use Magento\Customer\Model\Group; +use Magento\Store\Api\WebsiteRepositoryInterface; + +require __DIR__ . '/product_simple_tax_none.php'; + +$product = $productRepository->get('simple-product-tax-none'); +/** @var WebsiteRepositoryInterface $websiteRepository */ +$websiteRepository = $objectManager->get(WebsiteRepositoryInterface::class); +/** @var ProductTierPriceInterfaceFactory $tierPriceFactory */ +$tierPriceFactory = $objectManager->get(ProductTierPriceInterfaceFactory::class); +/** @var ProductTierPriceExtensionFactory $tpExtensionAttributeFactory */ +$tpExtensionAttributeFactory = $objectManager->get(ProductTierPriceExtensionFactory::class); +$adminWebsite = $websiteRepository->get('admin'); +$tierPriceExtensionAttribute = $tpExtensionAttributeFactory->create( + [ + 'data' => [ + 'website_id' => $adminWebsite->getId(), + ] + ] +); +$tierPrices[] = $tierPriceFactory->create( + [ + 'data' => [ + 'customer_group_id' => Group::CUST_GROUP_ALL, + 'qty' => 2, + 'value' => 40 + ] + ] +)->setExtensionAttributes($tierPriceExtensionAttribute); +$product->setTierPrices($tierPrices); +$productRepository->save($product); diff --git a/dev/tests/integration/testsuite/Magento/Catalog/_files/product_simple_with_fixed_tier_price_for_logged_user.php b/dev/tests/integration/testsuite/Magento/Catalog/_files/product_simple_with_fixed_tier_price_for_logged_user.php new file mode 100644 index 0000000000000..44a4c82c277d4 --- /dev/null +++ b/dev/tests/integration/testsuite/Magento/Catalog/_files/product_simple_with_fixed_tier_price_for_logged_user.php @@ -0,0 +1,39 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +declare(strict_types=1); + +use Magento\Catalog\Api\Data\ProductTierPriceExtensionFactory; +use Magento\Catalog\Api\Data\ProductTierPriceInterfaceFactory; +use Magento\Store\Api\WebsiteRepositoryInterface; + +require __DIR__ . '/product_simple_tax_none.php'; + +$product = $productRepository->get('simple-product-tax-none'); +/** @var WebsiteRepositoryInterface $websiteRepository */ +$websiteRepository = $objectManager->get(WebsiteRepositoryInterface::class); +/** @var ProductTierPriceInterfaceFactory $tierPriceFactory */ +$tierPriceFactory = $objectManager->get(ProductTierPriceInterfaceFactory::class); +/** @var ProductTierPriceExtensionFactory $tpExtensionAttributeFactory */ +$tpExtensionAttributeFactory = $objectManager->get(ProductTierPriceExtensionFactory::class); +$adminWebsite = $websiteRepository->get('admin'); +$tierPriceExtensionAttribute = $tpExtensionAttributeFactory->create( + [ + 'data' => [ + 'website_id' => $adminWebsite->getId(), + ] + ] +); +$tierPrices[] = $tierPriceFactory->create( + [ + 'data' => [ + 'customer_group_id' => 1, + 'qty' => 1, + 'value' => 10 + ] + ] +)->setExtensionAttributes($tierPriceExtensionAttribute); +$product->setTierPrices($tierPrices); +$productRepository->save($product); diff --git a/dev/tests/integration/testsuite/Magento/Catalog/_files/product_simple_with_fixed_tier_price_for_logged_user_rollback.php b/dev/tests/integration/testsuite/Magento/Catalog/_files/product_simple_with_fixed_tier_price_for_logged_user_rollback.php new file mode 100644 index 0000000000000..554f953580a5c --- /dev/null +++ b/dev/tests/integration/testsuite/Magento/Catalog/_files/product_simple_with_fixed_tier_price_for_logged_user_rollback.php @@ -0,0 +1,8 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +declare(strict_types=1); + +require __DIR__ . '/product_simple_tax_none_rollback.php'; diff --git a/dev/tests/integration/testsuite/Magento/Catalog/_files/product_simple_with_fixed_tier_price_for_not_logged_user.php b/dev/tests/integration/testsuite/Magento/Catalog/_files/product_simple_with_fixed_tier_price_for_not_logged_user.php new file mode 100644 index 0000000000000..68afbe529808e --- /dev/null +++ b/dev/tests/integration/testsuite/Magento/Catalog/_files/product_simple_with_fixed_tier_price_for_not_logged_user.php @@ -0,0 +1,40 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +declare(strict_types=1); + +use Magento\Catalog\Api\Data\ProductTierPriceExtensionFactory; +use Magento\Catalog\Api\Data\ProductTierPriceInterfaceFactory; +use Magento\Customer\Model\Group; +use Magento\Store\Api\WebsiteRepositoryInterface; + +require __DIR__ . '/product_simple_tax_none.php'; + +$product = $productRepository->get('simple-product-tax-none'); +/** @var WebsiteRepositoryInterface $websiteRepository */ +$websiteRepository = $objectManager->get(WebsiteRepositoryInterface::class); +/** @var ProductTierPriceInterfaceFactory $tierPriceFactory */ +$tierPriceFactory = $objectManager->get(ProductTierPriceInterfaceFactory::class); +/** @var ProductTierPriceExtensionFactory $tpExtensionAttributeFactory */ +$tpExtensionAttributeFactory = $objectManager->get(ProductTierPriceExtensionFactory::class); +$adminWebsite = $websiteRepository->get('admin'); +$tierPriceExtensionAttribute = $tpExtensionAttributeFactory->create( + [ + 'data' => [ + 'website_id' => $adminWebsite->getId(), + ] + ] +); +$tierPrices[] = $tierPriceFactory->create( + [ + 'data' => [ + 'customer_group_id' => Group::NOT_LOGGED_IN_ID, + 'qty' => 1, + 'value' => 30 + ] + ] +)->setExtensionAttributes($tierPriceExtensionAttribute); +$product->setTierPrices($tierPrices); +$productRepository->save($product); diff --git a/dev/tests/integration/testsuite/Magento/Catalog/_files/product_simple_with_fixed_tier_price_for_not_logged_user_rollback.php b/dev/tests/integration/testsuite/Magento/Catalog/_files/product_simple_with_fixed_tier_price_for_not_logged_user_rollback.php new file mode 100644 index 0000000000000..554f953580a5c --- /dev/null +++ b/dev/tests/integration/testsuite/Magento/Catalog/_files/product_simple_with_fixed_tier_price_for_not_logged_user_rollback.php @@ -0,0 +1,8 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +declare(strict_types=1); + +require __DIR__ . '/product_simple_tax_none_rollback.php'; diff --git a/dev/tests/integration/testsuite/Magento/Catalog/_files/product_simple_with_fixed_tier_price_rollback.php b/dev/tests/integration/testsuite/Magento/Catalog/_files/product_simple_with_fixed_tier_price_rollback.php new file mode 100644 index 0000000000000..554f953580a5c --- /dev/null +++ b/dev/tests/integration/testsuite/Magento/Catalog/_files/product_simple_with_fixed_tier_price_rollback.php @@ -0,0 +1,8 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +declare(strict_types=1); + +require __DIR__ . '/product_simple_tax_none_rollback.php'; diff --git a/dev/tests/integration/testsuite/Magento/Catalog/_files/product_simple_with_media_gallery_entries.php b/dev/tests/integration/testsuite/Magento/Catalog/_files/product_simple_with_media_gallery_entries.php index e3a23073b5d31..eb2d56d6d0b3a 100644 --- a/dev/tests/integration/testsuite/Magento/Catalog/_files/product_simple_with_media_gallery_entries.php +++ b/dev/tests/integration/testsuite/Magento/Catalog/_files/product_simple_with_media_gallery_entries.php @@ -4,54 +4,44 @@ * See COPYING.txt for license details. */ +use Magento\Catalog\Api\Data\ProductAttributeMediaGalleryEntryExtensionFactory; +use Magento\Catalog\Api\Data\ProductAttributeMediaGalleryEntryInterfaceFactory; +use Magento\Catalog\Api\ProductAttributeMediaGalleryManagementInterface; +use Magento\Framework\Api\Data\ImageContentInterfaceFactory; +use Magento\Framework\Api\Data\VideoContentInterfaceFactory; +use Magento\TestFramework\Helper\Bootstrap; +use Magento\TestFramework\ObjectManager; + include __DIR__ . '/product_simple_with_full_option_set.php'; -/** - * @var \Magento\TestFramework\ObjectManager $objectManager - */ -$objectManager = \Magento\TestFramework\Helper\Bootstrap::getObjectManager(); +/** @var ObjectManager $objectManager */ +$objectManager = Bootstrap::getObjectManager(); -/** - * @var \Magento\Catalog\Api\Data\ProductAttributeMediaGalleryEntryInterfaceFactory $mediaGalleryEntryFactory - */ - -$mediaGalleryEntryFactory = $objectManager->get( - \Magento\Catalog\Api\Data\ProductAttributeMediaGalleryEntryInterfaceFactory::class -); +/** @var ProductAttributeMediaGalleryEntryInterfaceFactory $mediaGalleryEntryFactory */ +$mediaGalleryEntryFactory = $objectManager->get(ProductAttributeMediaGalleryEntryInterfaceFactory::class); -/** - * @var \Magento\Framework\Api\Data\ImageContentInterfaceFactory $imageContentFactory - */ -$imageContentFactory = $objectManager->get(\Magento\Framework\Api\Data\ImageContentInterfaceFactory::class); +/** @var ImageContentInterfaceFactory $imageContentFactory */ +$imageContentFactory = $objectManager->get(ImageContentInterfaceFactory::class); $imageContent = $imageContentFactory->create(); -$testImagePath = __DIR__ .'/magento_image.jpg'; +$testImagePath = __DIR__ . '/magento_image.jpg'; $imageContent->setBase64EncodedData(base64_encode(file_get_contents($testImagePath))); $imageContent->setType("image/jpeg"); $imageContent->setName("1.jpg"); $video = $mediaGalleryEntryFactory->create(); $video->setDisabled(false); -//$video->setFile('1.png'); $video->setFile('1.jpg'); $video->setLabel('Video Label'); $video->setMediaType('external-video'); $video->setPosition(2); $video->setContent($imageContent); -/** - * @var \Magento\Catalog\Api\Data\ProductAttributeMediaGalleryEntryExtensionFactory $mediaGalleryEntryExtensionFactory - */ -$mediaGalleryEntryExtensionFactory = $objectManager->get( - \Magento\Catalog\Api\Data\ProductAttributeMediaGalleryEntryExtensionFactory::class -); +/** @var ProductAttributeMediaGalleryEntryExtensionFactory $mediaGalleryEntryExtensionFactory */ +$mediaGalleryEntryExtensionFactory = $objectManager->get(ProductAttributeMediaGalleryEntryExtensionFactory::class); $mediaGalleryEntryExtension = $mediaGalleryEntryExtensionFactory->create(); -/** - * @var \Magento\Framework\Api\Data\VideoContentInterfaceFactory $videoContentFactory - */ -$videoContentFactory = $objectManager->get( - \Magento\Framework\Api\Data\VideoContentInterfaceFactory::class -); +/** @var VideoContentInterfaceFactory $videoContentFactory */ +$videoContentFactory = $objectManager->get(VideoContentInterfaceFactory::class); $videoContent = $videoContentFactory->create(); $videoContent->setMediaType('external-video'); $videoContent->setVideoDescription('Video description'); @@ -63,10 +53,6 @@ $mediaGalleryEntryExtension->setVideoContent($videoContent); $video->setExtensionAttributes($mediaGalleryEntryExtension); -/** - * @var \Magento\Catalog\Api\ProductAttributeMediaGalleryManagementInterface $mediaGalleryManagement - */ -$mediaGalleryManagement = $objectManager->get( - \Magento\Catalog\Api\ProductAttributeMediaGalleryManagementInterface::class -); +/** @var ProductAttributeMediaGalleryManagementInterface $mediaGalleryManagement */ +$mediaGalleryManagement = $objectManager->get(ProductAttributeMediaGalleryManagementInterface::class); $mediaGalleryManagement->create('simple', $video); diff --git a/dev/tests/integration/testsuite/Magento/Catalog/_files/product_simple_with_percent_tier_price.php b/dev/tests/integration/testsuite/Magento/Catalog/_files/product_simple_with_percent_tier_price.php new file mode 100644 index 0000000000000..0bef251a254f3 --- /dev/null +++ b/dev/tests/integration/testsuite/Magento/Catalog/_files/product_simple_with_percent_tier_price.php @@ -0,0 +1,40 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +declare(strict_types=1); + +use Magento\Catalog\Api\Data\ProductTierPriceExtensionFactory; +use Magento\Catalog\Api\Data\ProductTierPriceInterfaceFactory; +use Magento\Customer\Model\Group; +use Magento\Store\Api\WebsiteRepositoryInterface; + +require __DIR__ . '/product_simple_tax_none.php'; + +$product = $productRepository->get('simple-product-tax-none'); +/** @var WebsiteRepositoryInterface $websiteRepository */ +$websiteRepository = $objectManager->get(WebsiteRepositoryInterface::class); +/** @var ProductTierPriceInterfaceFactory $tierPriceFactory */ +$tierPriceFactory = $objectManager->get(ProductTierPriceInterfaceFactory::class); +/** @var ProductTierPriceExtensionFactory $tpExtensionAttributeFactory */ +$tpExtensionAttributeFactory = $objectManager->get(ProductTierPriceExtensionFactory::class); +$adminWebsite = $websiteRepository->get('admin'); +$tierPriceExtensionAttribute = $tpExtensionAttributeFactory->create( + [ + 'data' => [ + 'website_id' => $adminWebsite->getId(), + 'percentage_value' => 50, + ] + ] +); +$tierPrices[] = $tierPriceFactory->create( + [ + 'data' => [ + 'customer_group_id' => Group::CUST_GROUP_ALL, + 'qty' => 2, + ] + ] +)->setExtensionAttributes($tierPriceExtensionAttribute); +$product->setTierPrices($tierPrices); +$productRepository->save($product); diff --git a/dev/tests/integration/testsuite/Magento/Catalog/_files/product_simple_with_percent_tier_price_rollback.php b/dev/tests/integration/testsuite/Magento/Catalog/_files/product_simple_with_percent_tier_price_rollback.php new file mode 100644 index 0000000000000..554f953580a5c --- /dev/null +++ b/dev/tests/integration/testsuite/Magento/Catalog/_files/product_simple_with_percent_tier_price_rollback.php @@ -0,0 +1,8 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +declare(strict_types=1); + +require __DIR__ . '/product_simple_tax_none_rollback.php'; diff --git a/dev/tests/integration/testsuite/Magento/Catalog/_files/product_simple_without_custom_options.php b/dev/tests/integration/testsuite/Magento/Catalog/_files/product_simple_without_custom_options.php new file mode 100644 index 0000000000000..7296910614e33 --- /dev/null +++ b/dev/tests/integration/testsuite/Magento/Catalog/_files/product_simple_without_custom_options.php @@ -0,0 +1,66 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ + +use Magento\Catalog\Api\Data\ProductExtensionInterfaceFactory; +use Magento\Catalog\Api\Data\ProductTierPriceExtensionFactory; + +/** @var \Magento\Catalog\Api\ProductRepositoryInterface $productRepository */ +// simple product without custom options +$secondProductSku = 'simple-2'; + +/** @var \Magento\TestFramework\ObjectManager $objectManager */ +$objectManager = \Magento\TestFramework\Helper\Bootstrap::getObjectManager(); + +$productExtensionAttributesFactory = $objectManager->get(ProductExtensionInterfaceFactory::class); + +$adminWebsite = $objectManager->get(\Magento\Store\Api\WebsiteRepositoryInterface::class)->get('admin'); +/** @var $tpExtensionAttributes */ +$tpExtensionAttributesFactory = $objectManager->get(ProductTierPriceExtensionFactory::class); +$tierPriceExtensionAttributes1 = $tpExtensionAttributesFactory->create() + ->setWebsiteId($adminWebsite->getId()); +$productExtensionAttributesWebsiteIds = $productExtensionAttributesFactory->create( + ['website_ids' => $adminWebsite->getId()] +); +/** @var \Magento\Catalog\Api\CategoryLinkManagementInterface $categoryLinkManagement */ +$categoryLinkManagement = $objectManager->get(\Magento\Catalog\Api\CategoryLinkManagementInterface::class); +/** @var $secondProduct \Magento\Catalog\Model\Product */ +$secondProduct = $objectManager->create(\Magento\Catalog\Model\Product::class); +$secondProduct->isObjectNew(true); +$secondProduct->setTypeId(\Magento\Catalog\Model\Product\Type::TYPE_SIMPLE) + ->setId(22) + ->setAttributeSetId(4) + ->setWebsiteIds([1]) + ->setName('Simple Product 2') + ->setSku($secondProductSku) + ->setPrice(11) + ->setWeight(1) + ->setShortDescription("Short description 2") + ->setTaxClassId(0) + ->setDescription('Description with <b>html tag</b>') + ->setExtensionAttributes($productExtensionAttributesWebsiteIds) + ->setMetaTitle('meta title 2') + ->setMetaKeyword('meta keyword 2') + ->setMetaDescription('meta description 2') + ->setVisibility(\Magento\Catalog\Model\Product\Visibility::VISIBILITY_BOTH) + ->setStatus(\Magento\Catalog\Model\Product\Attribute\Source\Status::STATUS_ENABLED) + ->setStockData( + [ + 'use_config_manage_stock' => 1, + 'qty' => 100, + 'is_qty_decimal' => 0, + 'is_in_stock' => 1, + ] + )->setCanSaveCustomOptions(false) + ->setHasOptions(false); + +/** @var \Magento\Catalog\Api\ProductRepositoryInterface $productRepository */ +$productRepository = $objectManager->create(\Magento\Catalog\Api\ProductRepositoryInterface::class); +$productRepository->save($secondProduct); + +$categoryLinkManagement->assignProductToCategories( + $secondProduct->getSku(), + [2] +); diff --git a/dev/tests/integration/testsuite/Magento/Catalog/_files/product_simple_without_custom_options_rollback.php b/dev/tests/integration/testsuite/Magento/Catalog/_files/product_simple_without_custom_options_rollback.php new file mode 100644 index 0000000000000..e51f78e1703dc --- /dev/null +++ b/dev/tests/integration/testsuite/Magento/Catalog/_files/product_simple_without_custom_options_rollback.php @@ -0,0 +1,25 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +use Magento\Framework\Exception\NoSuchEntityException; + +\Magento\TestFramework\Helper\Bootstrap::getInstance()->getInstance()->reinitialize(); + +/** @var \Magento\Framework\Registry $registry */ +$registry = \Magento\TestFramework\Helper\Bootstrap::getObjectManager()->get(\Magento\Framework\Registry::class); + +$registry->unregister('isSecureArea'); +$registry->register('isSecureArea', true); + +/** @var \Magento\Catalog\Api\ProductRepositoryInterface $productRepository */ +$productRepository = \Magento\TestFramework\Helper\Bootstrap::getObjectManager() + ->get(\Magento\Catalog\Api\ProductRepositoryInterface::class); +try { + $product = $productRepository->get('simple-2', false, null, true); + $productRepository->delete($product); +} catch (NoSuchEntityException $e) { +} +$registry->unregister('isSecureArea'); +$registry->register('isSecureArea', false); diff --git a/dev/tests/integration/testsuite/Magento/Catalog/_files/product_text_editor_attribute.php b/dev/tests/integration/testsuite/Magento/Catalog/_files/product_text_editor_attribute.php new file mode 100644 index 0000000000000..2b62f8a78252a --- /dev/null +++ b/dev/tests/integration/testsuite/Magento/Catalog/_files/product_text_editor_attribute.php @@ -0,0 +1,55 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +declare(strict_types=1); + +use Magento\Catalog\Api\Data\ProductAttributeInterface; +use Magento\Catalog\Api\ProductAttributeRepositoryInterface; +use Magento\Catalog\Model\ResourceModel\Eav\Attribute; +use Magento\Catalog\Model\ResourceModel\Eav\AttributeFactory; +use Magento\Catalog\Setup\CategorySetup; +use Magento\TestFramework\Helper\Bootstrap; + +$objectManager = Bootstrap::getObjectManager(); +/** @var CategorySetup $installer */ +$installer = $objectManager->create(CategorySetup::class); +$entityType = $installer->getEntityTypeId(ProductAttributeInterface::ENTITY_TYPE_CODE); +/** @var ProductAttributeRepositoryInterface $attributeRepository */ +$attributeRepository = $objectManager->create(ProductAttributeRepositoryInterface::class); +/** @var Attribute $attribute */ +$attribute = $objectManager->get(AttributeFactory::class)->create(); +if (!$attribute->loadByCode($entityType, 'text_editor_attribute')->getAttributeId()) { + $attribute->setData( + [ + 'attribute_code' => 'text_editor_attribute', + 'entity_type_id' => $entityType, + 'is_global' => 1, + 'is_user_defined' => 1, + 'frontend_input' => 'textarea', + 'is_unique' => 0, + 'is_required' => 0, + 'is_searchable' => 0, + 'is_visible_in_advanced_search' => 0, + 'is_comparable' => 0, + 'is_filterable' => 0, + 'is_filterable_in_search' => 0, + 'is_used_for_promo_rules' => 0, + 'is_html_allowed_on_front' => 1, + 'is_visible_on_front' => 0, + 'used_in_product_listing' => 0, + 'used_for_sort_by' => 0, + 'frontend_label' => ['Text Editor Attribute'], + 'backend_type' => 'text', + 'is_wysiwyg_enabled' => '1', + ] + ); + $attributeRepository->save($attribute); + $installer->addAttributeToGroup( + ProductAttributeInterface::ENTITY_TYPE_CODE, + 'Default', + 'General', + $attribute->getId() + ); +} diff --git a/dev/tests/integration/testsuite/Magento/Catalog/_files/product_text_editor_attribute_rollback.php b/dev/tests/integration/testsuite/Magento/Catalog/_files/product_text_editor_attribute_rollback.php new file mode 100644 index 0000000000000..09d3c1ea392ba --- /dev/null +++ b/dev/tests/integration/testsuite/Magento/Catalog/_files/product_text_editor_attribute_rollback.php @@ -0,0 +1,26 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +declare(strict_types=1); + +use Magento\Framework\Exception\NoSuchEntityException; +use Magento\TestFramework\Helper\Bootstrap; +use Magento\Framework\Registry; +use Magento\Catalog\Api\ProductAttributeRepositoryInterface; + +$objectManager = Bootstrap::getObjectManager(); +/** @var Registry $registry */ +$registry = $objectManager->get(Registry::class); +$registry->unregister('isSecureArea'); +$registry->register('isSecureArea', true); +/** @var ProductAttributeRepositoryInterface $attributeRepository */ +$attributeRepository = $objectManager->create(ProductAttributeRepositoryInterface::class); + +try { + $attributeRepository->deleteById('text_editor_attribute'); +} catch (NoSuchEntityException $e) { +} +$registry->unregister('isSecureArea'); +$registry->register('isSecureArea', false); diff --git a/dev/tests/integration/testsuite/Magento/Catalog/_files/product_with_category.php b/dev/tests/integration/testsuite/Magento/Catalog/_files/product_with_category.php new file mode 100644 index 0000000000000..ebc6bce655198 --- /dev/null +++ b/dev/tests/integration/testsuite/Magento/Catalog/_files/product_with_category.php @@ -0,0 +1,47 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +declare(strict_types=1); + +use Magento\Catalog\Api\ProductRepositoryInterface; +use Magento\Catalog\Model\Product\Attribute\Source\Status; +use Magento\Catalog\Model\Product\Type; +use Magento\Catalog\Model\Product\Visibility; +use Magento\Catalog\Model\ProductFactory; +use Magento\Store\Api\WebsiteRepositoryInterface; +use Magento\TestFramework\Helper\Bootstrap; + +require __DIR__ . '/category.php'; + +$objectManager = Bootstrap::getObjectManager(); +/** @var ProductFactory $productFactory */ +$productFactory = $objectManager->get(ProductFactory::class); +/** @var WebsiteRepositoryInterface $websiteRepository */ +$websiteRepository = $objectManager->get(WebsiteRepositoryInterface::class); +$defaultWebsiteId = $websiteRepository->get('base')->getId(); +$product = $productFactory->create(); +$product->isObjectNew(true); +$product->setTypeId(Type::TYPE_SIMPLE) + ->setAttributeSetId($product->getDefaultAttributeSetId()) + ->setWebsiteIds([$defaultWebsiteId]) + ->setName('Simple Product In Stock') + ->setSku('in-stock-product') + ->setPrice(10) + ->setWeight(1) + ->setShortDescription('Short description') + ->setTaxClassId(0) + ->setDescription('Description with <b>html tag</b>') + ->setMetaTitle('meta title') + ->setMetaKeyword('meta keyword') + ->setMetaDescription('meta description') + ->setVisibility(Visibility::VISIBILITY_BOTH) + ->setStatus(Status::STATUS_ENABLED) + ->setCategoryIds([333]) + ->setStockData(['use_config_manage_stock' => 0]) + ->setCanSaveCustomOptions(true) + ->setHasOptions(true); +/** @var ProductRepositoryInterface $productRepositoryFactory */ +$productRepository = $objectManager->create(ProductRepositoryInterface::class); +$productRepository->save($product); diff --git a/dev/tests/integration/testsuite/Magento/Catalog/_files/product_with_category_rollback.php b/dev/tests/integration/testsuite/Magento/Catalog/_files/product_with_category_rollback.php new file mode 100644 index 0000000000000..12d8c720d10d1 --- /dev/null +++ b/dev/tests/integration/testsuite/Magento/Catalog/_files/product_with_category_rollback.php @@ -0,0 +1,30 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +declare(strict_types=1); + +use Magento\Catalog\Api\ProductRepositoryInterface; +use Magento\Framework\Exception\NoSuchEntityException; +use Magento\Framework\Registry; +use Magento\TestFramework\Helper\Bootstrap; + +require __DIR__ . '/category_rollback.php'; + +$objectManager = Bootstrap::getObjectManager(); +/** @var Registry $registry */ +$registry = $objectManager->get(Registry::class); + +$registry->unregister('isSecureArea'); +$registry->register('isSecureArea', true); +/** @var ProductRepositoryInterface $productRepository */ +$productRepository = $objectManager->create(ProductRepositoryInterface::class); + +try { + $productRepository->deleteById('in-stock-product'); +} catch (NoSuchEntityException $e) { + //already removed +} +$registry->unregister('isSecureArea'); +$registry->register('isSecureArea', false); diff --git a/dev/tests/integration/testsuite/Magento/Catalog/_files/product_with_varchar_attribute.php b/dev/tests/integration/testsuite/Magento/Catalog/_files/product_with_varchar_attribute.php new file mode 100644 index 0000000000000..e2a5f560c8b7b --- /dev/null +++ b/dev/tests/integration/testsuite/Magento/Catalog/_files/product_with_varchar_attribute.php @@ -0,0 +1,21 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +declare(strict_types=1); + +use Magento\Eav\Model\Config; + +require __DIR__ . '/product_varchar_attribute.php'; +require __DIR__ . '/product_simple.php'; + +/** @var Config $eavConfig */ +$eavConfig = $objectManager->get(Config::class); +$eavConfig->clear(); + +$attribute->setDefaultValue('Varchar default value'); +$attributeRepository->save($attribute); + +$product->setCustomAttribute('varchar_attribute', $attribute->getDefaultValue()); +$productRepository->save($product); diff --git a/dev/tests/integration/testsuite/Magento/Catalog/_files/product_with_varchar_attribute_rollback.php b/dev/tests/integration/testsuite/Magento/Catalog/_files/product_with_varchar_attribute_rollback.php new file mode 100644 index 0000000000000..5d6b5dce4b541 --- /dev/null +++ b/dev/tests/integration/testsuite/Magento/Catalog/_files/product_with_varchar_attribute_rollback.php @@ -0,0 +1,9 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +declare(strict_types=1); + +require __DIR__ . '/product_varchar_attribute_rollback.php'; +require __DIR__ . '/product_simple_rollback.php'; diff --git a/dev/tests/integration/testsuite/Magento/Catalog/_files/products_with_layered_navigation_custom_attribute.php b/dev/tests/integration/testsuite/Magento/Catalog/_files/products_with_layered_navigation_custom_attribute.php index 72336c48410d5..795c612ea3be1 100644 --- a/dev/tests/integration/testsuite/Magento/Catalog/_files/products_with_layered_navigation_custom_attribute.php +++ b/dev/tests/integration/testsuite/Magento/Catalog/_files/products_with_layered_navigation_custom_attribute.php @@ -5,127 +5,108 @@ */ declare(strict_types=1); -// phpcs:ignore Magento2.Security.IncludeFile require __DIR__ . '/../../Catalog/_files/attribute_set_based_on_default_set.php'; -// phpcs:ignore Magento2.Security.IncludeFile require __DIR__ . '/../../Catalog/_files/categories.php'; +use Magento\Catalog\Api\ProductRepositoryInterface; +use Magento\Catalog\Model\ResourceModel\Eav\Attribute; +use Magento\Catalog\Setup\CategorySetup; +use Magento\Eav\Model\Config; +use Magento\Framework\Exception\NoSuchEntityException; +use Magento\Indexer\Model\Indexer; +use Magento\Indexer\Model\Indexer\Collection as IndexerCollection; use Magento\TestFramework\Helper\Bootstrap; use Magento\Eav\Api\AttributeRepositoryInterface; -$eavConfig = Bootstrap::getObjectManager()->get(\Magento\Eav\Model\Config::class); -$attribute = $eavConfig->getAttribute('catalog_product', 'test_configurable'); - -$eavConfig->clear(); - -$attribute1 = $eavConfig->getAttribute('catalog_product', ' second_test_configurable'); +$objectManager = Bootstrap::getObjectManager(); +/** @var Config $eavConfig */ +$eavConfig = $objectManager->get(Config::class); +/** @var AttributeRepositoryInterface $attributeRepository */ +$attributeRepository = $objectManager->create(AttributeRepositoryInterface::class); +/** @var CategorySetup $installer */ +$installer = $objectManager->create(CategorySetup::class); $eavConfig->clear(); -/** @var $installer \Magento\Catalog\Setup\CategorySetup */ -$installer = Bootstrap::getObjectManager()->create(\Magento\Catalog\Setup\CategorySetup::class); - +$attribute = $eavConfig->getAttribute('catalog_product', 'test_configurable'); if (!$attribute->getId()) { - - /** @var $attribute \Magento\Catalog\Model\ResourceModel\Eav\Attribute */ - $attribute = Bootstrap::getObjectManager()->create( - \Magento\Catalog\Model\ResourceModel\Eav\Attribute::class - ); - - /** @var AttributeRepositoryInterface $attributeRepository */ - $attributeRepository = Bootstrap::getObjectManager()->create(AttributeRepositoryInterface::class); - - $attribute->setData( - [ - 'attribute_code' => 'test_configurable', - 'entity_type_id' => $installer->getEntityTypeId('catalog_product'), - 'is_global' => 1, - 'is_user_defined' => 1, - 'frontend_input' => 'select', - 'is_unique' => 0, - 'is_required' => 0, - 'is_searchable' => 1, - 'is_visible_in_advanced_search' => 1, - 'is_comparable' => 1, - 'is_filterable' => 1, - 'is_filterable_in_search' => 1, - 'is_used_for_promo_rules' => 0, - 'is_html_allowed_on_front' => 1, - 'is_visible_on_front' => 1, - 'used_in_product_listing' => 1, - 'used_for_sort_by' => 1, - 'frontend_label' => ['Test Configurable'], - 'backend_type' => 'int', - 'option' => [ - 'value' => ['option_0' => ['Option 1'], 'option_1' => ['Option 2']], - 'order' => ['option_0' => 1, 'option_1' => 2], - ], - 'default_value' => 'option_0' - ] - ); - + /** @var $attribute Attribute */ + $attribute->setData([ + 'attribute_code' => 'test_configurable', + 'entity_type_id' => $installer->getEntityTypeId('catalog_product'), + 'is_global' => 1, + 'is_user_defined' => 1, + 'frontend_input' => 'select', + 'is_unique' => 0, + 'is_required' => 0, + 'is_searchable' => 0, + 'is_visible_in_advanced_search' => 0, + 'is_comparable' => 1, + 'is_filterable' => 1, + 'is_filterable_in_search' => 1, + 'is_used_for_promo_rules' => 0, + 'is_html_allowed_on_front' => 1, + 'is_visible_on_front' => 1, + 'used_in_product_listing' => 1, + 'used_for_sort_by' => 1, + 'frontend_label' => ['Test Configurable'], + 'backend_type' => 'int', + 'option' => [ + 'value' => ['option_0' => ['Option 1'], 'option_1' => ['Option 2']], + 'order' => ['option_0' => 1, 'option_1' => 2], + ], + 'default_value' => 'option_0' + ]); $attributeRepository->save($attribute); /* Assign attribute to attribute set */ $installer->addAttributeToGroup('catalog_product', 'Default', 'General', $attribute->getId()); } -// create a second attribute -if (!$attribute1->getId()) { - - /** @var $attribute1 \Magento\Catalog\Model\ResourceModel\Eav\Attribute */ - $attribute1 = Bootstrap::getObjectManager()->create( - \Magento\Catalog\Model\ResourceModel\Eav\Attribute::class - ); - - /** @var AttributeRepositoryInterface $attributeRepository */ - $attributeRepository = Bootstrap::getObjectManager()->create(AttributeRepositoryInterface::class); - $attribute1->setData( - [ - 'attribute_code' => 'second_test_configurable', - 'entity_type_id' => $installer->getEntityTypeId('catalog_product'), - 'is_global' => 1, - 'is_user_defined' => 1, - 'frontend_input' => 'select', - 'is_unique' => 0, - 'is_required' => 0, - 'is_searchable' => 1, - 'is_visible_in_advanced_search' => 1, - 'is_comparable' => 1, - 'is_filterable' => 1, - 'is_filterable_in_search' => 1, - 'is_used_for_promo_rules' => 0, - 'is_html_allowed_on_front' => 1, - 'is_visible_on_front' => 1, - 'used_in_product_listing' => 1, - 'used_for_sort_by' => 1, - 'frontend_label' => ['Second Test Configurable'], - 'backend_type' => 'int', - 'option' => [ - 'value' => ['option_0' => ['Option 3'], 'option_1' => ['Option 4']], - 'order' => ['option_0' => 1, 'option_1' => 2], - ], - 'default' => ['option_0'] - ] - ); - - $attributeRepository->save($attribute1); +// create a second attribute +/** @var Attribute $secondAttribute */ +$secondAttribute = $eavConfig->getAttribute('catalog_product', ' second_test_configurable'); +if (!$secondAttribute->getId()) { + $secondAttribute->setData([ + 'attribute_code' => 'second_test_configurable', + 'entity_type_id' => $installer->getEntityTypeId('catalog_product'), + 'is_global' => 1, + 'is_user_defined' => 1, + 'frontend_input' => 'select', + 'is_unique' => 0, + 'is_required' => 0, + 'is_searchable' => 0, + 'is_visible_in_advanced_search' => 0, + 'is_comparable' => 1, + 'is_filterable' => 1, + 'is_filterable_in_search' => 1, + 'is_used_for_promo_rules' => 0, + 'is_html_allowed_on_front' => 1, + 'is_visible_on_front' => 1, + 'used_in_product_listing' => 1, + 'used_for_sort_by' => 1, + 'frontend_label' => ['Second Test Configurable'], + 'backend_type' => 'int', + 'option' => [ + 'value' => ['option_0' => ['Option 3'], 'option_1' => ['Option 4']], + 'order' => ['option_0' => 1, 'option_1' => 2], + ], + 'default' => ['option_0'] + ]); + $attributeRepository->save($secondAttribute); /* Assign attribute to attribute set */ $installer->addAttributeToGroup( 'catalog_product', $attributeSet->getId(), $attributeSet->getDefaultGroupId(), - $attribute1->getId() + $secondAttribute->getId() ); } $eavConfig->clear(); -/** @var \Magento\Framework\ObjectManagerInterface $objectManager */ -$objectManager = \Magento\TestFramework\Helper\Bootstrap::getObjectManager(); - -/** @var $productRepository \Magento\Catalog\Api\ProductRepositoryInterface */ -$productRepository = $objectManager->get(\Magento\Catalog\Api\ProductRepositoryInterface::class); +/** @var ProductRepositoryInterface $productRepository */ +$productRepository = $objectManager->get(ProductRepositoryInterface::class); $productsWithNewAttributeSet = ['simple', '12345', 'simple-4']; foreach ($productsWithNewAttributeSet as $sku) { @@ -139,14 +120,14 @@ 'is_in_stock' => 1] ); $productRepository->save($product); - } catch (\Magento\Framework\Exception\NoSuchEntityException $e) { + } catch (NoSuchEntityException $e) { } } -/** @var \Magento\Indexer\Model\Indexer\Collection $indexerCollection */ -$indexerCollection = Bootstrap::getObjectManager()->get(\Magento\Indexer\Model\Indexer\Collection::class); -$indexerCollection->load(); -/** @var \Magento\Indexer\Model\Indexer $indexer */ + +/** @var IndexerCollection $indexerCollection */ +$indexerCollection = $objectManager->get(IndexerCollection::class)->load(); +/** @var Indexer $indexer */ foreach ($indexerCollection->getItems() as $indexer) { $indexer->reindexAll(); } diff --git a/dev/tests/integration/testsuite/Magento/Catalog/_files/products_with_layered_navigation_custom_attribute_rollback.php b/dev/tests/integration/testsuite/Magento/Catalog/_files/products_with_layered_navigation_custom_attribute_rollback.php index 5cababbc988c7..2f7a08bf8b460 100644 --- a/dev/tests/integration/testsuite/Magento/Catalog/_files/products_with_layered_navigation_custom_attribute_rollback.php +++ b/dev/tests/integration/testsuite/Magento/Catalog/_files/products_with_layered_navigation_custom_attribute_rollback.php @@ -5,42 +5,44 @@ */ declare(strict_types=1); -// phpcs:ignore Magento2.Security.IncludeFile require __DIR__ . '/../../Eav/_files/empty_attribute_set_rollback.php'; -// phpcs:ignore Magento2.Security.IncludeFile require __DIR__ . '/../../Catalog/_files/categories_rollback.php'; +use Magento\Eav\Api\Data\AttributeInterface; +use Magento\Eav\Model\Config; +use Magento\Eav\Model\Entity\Attribute\Set as AttributeSet; +use Magento\Eav\Model\Entity\Type; +use Magento\Eav\Model\ResourceModel\Entity\Attribute\Set\Collection as AttributeSetCollection; +use Magento\Framework\App\ObjectManager; use Magento\TestFramework\Helper\Bootstrap; use Magento\Eav\Api\AttributeRepositoryInterface; -use Magento\TestFramework\Helper\CacheCleaner; -$eavConfig = Bootstrap::getObjectManager()->get(\Magento\Eav\Model\Config::class); +/** @var ObjectManager $objectManager */ +$objectManager = Bootstrap::getObjectManager(); + +$eavConfig = $objectManager->get(Config::class); $attributesToDelete = ['test_configurable', 'second_test_configurable']; /** @var AttributeRepositoryInterface $attributeRepository */ -$attributeRepository = Bootstrap::getObjectManager()->get(AttributeRepositoryInterface::class); +$attributeRepository = $objectManager->get(AttributeRepositoryInterface::class); foreach ($attributesToDelete as $attributeCode) { - /** @var \Magento\Eav\Api\Data\AttributeInterface $attribute */ + /** @var AttributeInterface $attribute */ $attribute = $attributeRepository->get('catalog_product', $attributeCode); $attributeRepository->delete($attribute); } -/** @var $product \Magento\Catalog\Model\Product */ -$objectManager = Bootstrap::getObjectManager(); - -$entityType = $objectManager->create(\Magento\Eav\Model\Entity\Type::class)->loadByCode('catalog_product'); // remove attribute set - -/** @var \Magento\Eav\Model\ResourceModel\Entity\Attribute\Set\Collection $attributeSetCollection */ +$entityType = $objectManager->create(Type::class)->loadByCode('catalog_product'); +/** @var AttributeSetCollection $attributeSetCollection */ $attributeSetCollection = $objectManager->create( - \Magento\Eav\Model\ResourceModel\Entity\Attribute\Set\Collection::class + AttributeSetCollection::class ); $attributeSetCollection->addFilter('attribute_set_name', 'second_attribute_set'); $attributeSetCollection->addFilter('entity_type_id', $entityType->getId()); -$attributeSetCollection->setOrder('attribute_set_id'); // descending is default value +$attributeSetCollection->setOrder('attribute_set_id'); $attributeSetCollection->setPageSize(1); $attributeSetCollection->load(); -/** @var \Magento\Eav\Model\Entity\Attribute\Set $attributeSet */ +/** @var AttributeSet $attributeSet */ $attributeSet = $attributeSetCollection->fetchItem(); $attributeSet->delete(); diff --git a/dev/tests/integration/testsuite/Magento/Catalog/_files/products_with_layered_navigation_with_multiselect_attribute.php b/dev/tests/integration/testsuite/Magento/Catalog/_files/products_with_layered_navigation_with_multiselect_attribute.php index 7d4f22e154030..7be314499a675 100644 --- a/dev/tests/integration/testsuite/Magento/Catalog/_files/products_with_layered_navigation_with_multiselect_attribute.php +++ b/dev/tests/integration/testsuite/Magento/Catalog/_files/products_with_layered_navigation_with_multiselect_attribute.php @@ -4,49 +4,55 @@ * See COPYING.txt for license details. */ -/** - * Create multiselect attribute - */ +/** Create multiselect attribute */ require __DIR__ . '/multiselect_attribute.php'; +use Magento\Catalog\Api\ProductRepositoryInterface; +use Magento\Catalog\Model\Product; +use Magento\Catalog\Model\Product\Attribute\Source\Status; +use Magento\Catalog\Model\Product\Type as ProductType; +use Magento\Catalog\Model\Product\Visibility; +use Magento\Catalog\Model\ResourceModel\Eav\Attribute as EavAttribute; +use Magento\Catalog\Setup\CategorySetup; +use Magento\Eav\Model\Config as EavConfig; +use Magento\Eav\Model\ResourceModel\Entity\Attribute\Option\Collection as OptionCollection; +use Magento\Indexer\Model\Indexer; +use Magento\Indexer\Model\Indexer\Collection as IndexerCollection; use Magento\TestFramework\Helper\Bootstrap; use Magento\Eav\Api\AttributeRepositoryInterface; /** Create product with options and multiselect attribute */ -/** @var $installer \Magento\Catalog\Setup\CategorySetup */ -$installer = \Magento\TestFramework\Helper\Bootstrap::getObjectManager()->create( - \Magento\Catalog\Setup\CategorySetup::class -); +$objectManager = Bootstrap::getObjectManager(); +/** @var CategorySetup $installer */ +$installer = $objectManager->create(CategorySetup::class); -/** @var $options \Magento\Eav\Model\ResourceModel\Entity\Attribute\Option\Collection */ -$options = \Magento\TestFramework\Helper\Bootstrap::getObjectManager()->create( - \Magento\Eav\Model\ResourceModel\Entity\Attribute\Option\Collection::class -); -$eavConfig = Bootstrap::getObjectManager()->get(\Magento\Eav\Model\Config::class); +/** @var OptionCollection $options */ +$options = $objectManager->create(OptionCollection::class); +$eavConfig = $objectManager->get(EavConfig::class); -/** @var $attribute \Magento\Catalog\Model\ResourceModel\Eav\Attribute */ +/** @var $attribute EavAttribute */ $attribute = $eavConfig->getAttribute('catalog_product', 'multiselect_attribute'); $eavConfig->clear(); $attribute->setIsSearchable(1) ->setIsVisibleInAdvancedSearch(1) - ->setIsFilterable(true) - ->setIsFilterableInSearch(true) - ->setIsVisibleOnFront(1); + ->setIsFilterable(false) + ->setIsFilterableInSearch(false) + ->setIsVisibleOnFront(0); /** @var AttributeRepositoryInterface $attributeRepository */ -$attributeRepository = Bootstrap::getObjectManager()->create(AttributeRepositoryInterface::class); +$attributeRepository = $objectManager->create(AttributeRepositoryInterface::class); $attributeRepository->save($attribute); $options->setAttributeFilter($attribute->getId()); $optionIds = $options->getAllIds(); -/** @var \Magento\Catalog\Api\ProductRepositoryInterface $productRepository */ -$productRepository = Bootstrap::getObjectManager()->get(\Magento\Catalog\Api\ProductRepositoryInterface::class); +/** @var ProductRepositoryInterface $productRepository */ +$productRepository = $objectManager->get(ProductRepositoryInterface::class); -/** @var $product \Magento\Catalog\Model\Product */ -$product = \Magento\TestFramework\Helper\Bootstrap::getObjectManager()->create(\Magento\Catalog\Model\Product::class); -$product->setTypeId(\Magento\Catalog\Model\Product\Type::TYPE_SIMPLE) +/** @var Product $product */ +$product = $objectManager->create(Product::class); +$product->setTypeId(ProductType::TYPE_SIMPLE) ->setId($optionIds[0] * 10) ->setAttributeSetId($installer->getAttributeSetId('catalog_product', 'Default')) ->setWebsiteIds([1]) @@ -54,45 +60,45 @@ ->setSku('simple_ms_1') ->setPrice(10) ->setDescription('Hello " &" Bring the water bottle when you can!') - ->setVisibility(\Magento\Catalog\Model\Product\Visibility::VISIBILITY_BOTH) + ->setVisibility(Visibility::VISIBILITY_BOTH) ->setMultiselectAttribute([$optionIds[1],$optionIds[2]]) - ->setStatus(\Magento\Catalog\Model\Product\Attribute\Source\Status::STATUS_ENABLED) + ->setStatus(Status::STATUS_ENABLED) ->setStockData(['use_config_manage_stock' => 1, 'qty' => 100, 'is_qty_decimal' => 0, 'is_in_stock' => 1]); $productRepository->save($product); -$product = \Magento\TestFramework\Helper\Bootstrap::getObjectManager()->create(\Magento\Catalog\Model\Product::class); -$product->setTypeId(\Magento\Catalog\Model\Product\Type::TYPE_SIMPLE) +$product = $objectManager->create(Product::class); +$product->setTypeId(ProductType::TYPE_SIMPLE) ->setId($optionIds[1] * 10) ->setAttributeSetId($installer->getAttributeSetId('catalog_product', 'Default')) ->setWebsiteIds([1]) ->setName('With Multiselect 2 and 3') ->setSku('simple_ms_2') ->setPrice(10) - ->setVisibility(\Magento\Catalog\Model\Product\Visibility::VISIBILITY_BOTH) + ->setVisibility(Visibility::VISIBILITY_BOTH) ->setMultiselectAttribute([$optionIds[2], $optionIds[3]]) - ->setStatus(\Magento\Catalog\Model\Product\Attribute\Source\Status::STATUS_ENABLED) + ->setStatus(Status::STATUS_ENABLED) ->setStockData(['use_config_manage_stock' => 1, 'qty' => 100, 'is_qty_decimal' => 0, 'is_in_stock' => 1]); $productRepository->save($product); -$product = \Magento\TestFramework\Helper\Bootstrap::getObjectManager()->create(\Magento\Catalog\Model\Product::class); -$product->setTypeId(\Magento\Catalog\Model\Product\Type::TYPE_SIMPLE) +$product = $objectManager->create(Product::class); +$product->setTypeId(ProductType::TYPE_SIMPLE) ->setId($optionIds[2] * 10) ->setAttributeSetId($installer->getAttributeSetId('catalog_product', 'Default')) ->setWebsiteIds([1]) ->setName('With Multiselect 1 and 3') ->setSku('simple_ms_2') ->setPrice(10) - ->setVisibility(\Magento\Catalog\Model\Product\Visibility::VISIBILITY_BOTH) + ->setVisibility(Visibility::VISIBILITY_BOTH) ->setMultiselectAttribute([$optionIds[2], $optionIds[3]]) - ->setStatus(\Magento\Catalog\Model\Product\Attribute\Source\Status::STATUS_ENABLED) + ->setStatus(Status::STATUS_ENABLED) ->setStockData(['use_config_manage_stock' => 1, 'qty' => 100, 'is_qty_decimal' => 0, 'is_in_stock' => 1]); $productRepository->save($product); -/** @var \Magento\Indexer\Model\Indexer\Collection $indexerCollection */ -$indexerCollection = Bootstrap::getObjectManager()->get(\Magento\Indexer\Model\Indexer\Collection::class); +/** @var IndexerCollection $indexerCollection */ +$indexerCollection = $objectManager->get(IndexerCollection::class); $indexerCollection->load(); -/** @var \Magento\Indexer\Model\Indexer $indexer */ +/** @var Indexer $indexer */ foreach ($indexerCollection->getItems() as $indexer) { $indexer->reindexAll(); } diff --git a/dev/tests/integration/testsuite/Magento/Catalog/_files/reindex_catalog_category_flat.php b/dev/tests/integration/testsuite/Magento/Catalog/_files/reindex_catalog_category_flat.php new file mode 100644 index 0000000000000..03a6bd7735422 --- /dev/null +++ b/dev/tests/integration/testsuite/Magento/Catalog/_files/reindex_catalog_category_flat.php @@ -0,0 +1,16 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +declare(strict_types=1); + +use Magento\Catalog\Model\Indexer\Category\Flat\State; +use Magento\Indexer\Model\Indexer; +use Magento\TestFramework\Helper\Bootstrap; + +$objectManager = Bootstrap::getObjectManager(); +/** @var Indexer $indexer */ +$indexer = $objectManager->get(Indexer::class); +$indexer->load(State::INDEXER_ID); +$indexer->reindexAll(); diff --git a/dev/tests/integration/testsuite/Magento/Catalog/_files/simple_product_with_tier_price_for_logged_user.php b/dev/tests/integration/testsuite/Magento/Catalog/_files/simple_product_with_tier_price_for_logged_user.php new file mode 100644 index 0000000000000..e7e64566a4371 --- /dev/null +++ b/dev/tests/integration/testsuite/Magento/Catalog/_files/simple_product_with_tier_price_for_logged_user.php @@ -0,0 +1,23 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +declare(strict_types=1); + +require __DIR__ . '/product_simple.php'; + +$product = $productRepository->get('simple', false, null, true); +$tierPrices = $product->getTierPrices() ?? []; +$tierPriceExtensionAttributes = $tpExtensionAttributesFactory->create()->setWebsiteId($adminWebsite->getId()); +$tierPrices[] = $tierPriceFactory->create( + [ + 'data' => [ + 'customer_group_id' => 1, + 'qty' => 3, + 'value' => 1 + ] + ] +)->setExtensionAttributes($tierPriceExtensionAttributes); +$product->setTierPrices($tierPrices); +$productRepository->save($product); diff --git a/dev/tests/integration/testsuite/Magento/Catalog/_files/simple_product_with_tier_price_for_logged_user_rollback.php b/dev/tests/integration/testsuite/Magento/Catalog/_files/simple_product_with_tier_price_for_logged_user_rollback.php new file mode 100644 index 0000000000000..e17fdac4904c9 --- /dev/null +++ b/dev/tests/integration/testsuite/Magento/Catalog/_files/simple_product_with_tier_price_for_logged_user_rollback.php @@ -0,0 +1,8 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +declare(strict_types=1); + +require __DIR__ . '/product_simple_rollback.php'; diff --git a/dev/tests/integration/testsuite/Magento/CatalogImportExport/Model/AbstractProductExportImportTestCase.php b/dev/tests/integration/testsuite/Magento/CatalogImportExport/Model/AbstractProductExportImportTestCase.php index d3a2e4c53f246..eecdcdf038cf8 100644 --- a/dev/tests/integration/testsuite/Magento/CatalogImportExport/Model/AbstractProductExportImportTestCase.php +++ b/dev/tests/integration/testsuite/Magento/CatalogImportExport/Model/AbstractProductExportImportTestCase.php @@ -7,6 +7,7 @@ use Magento\Framework\App\Bootstrap; use Magento\Framework\App\Filesystem\DirectoryList; +use Magento\ImportExport\Model\Export\Adapter\AbstractAdapter; use Magento\Store\Model\Store; /** @@ -62,6 +63,11 @@ abstract class AbstractProductExportImportTestCase extends \PHPUnit\Framework\Te 'tax_class_id', ]; + /** + * @var AbstractAdapter + */ + private $writer; + /** * @inheritdoc */ @@ -367,7 +373,7 @@ protected function executeImportReplaceTest( * Export products in the system. * * @param \Magento\CatalogImportExport\Model\Export\Product|null $exportProduct - * @return string Return exported file name + * @return string Return exported file */ private function exportProducts(\Magento\CatalogImportExport\Model\Export\Product $exportProduct = null) { @@ -376,12 +382,11 @@ private function exportProducts(\Magento\CatalogImportExport\Model\Export\Produc $exportProduct = $exportProduct ?: $this->objectManager->create( \Magento\CatalogImportExport\Model\Export\Product::class ); - $exportProduct->setWriter( - \Magento\TestFramework\Helper\Bootstrap::getObjectManager()->create( - \Magento\ImportExport\Model\Export\Adapter\Csv::class, - ['fileSystem' => $this->fileSystem, 'destination' => $csvfile] - ) + $this->writer = \Magento\TestFramework\Helper\Bootstrap::getObjectManager()->create( + \Magento\ImportExport\Model\Export\Adapter\Csv::class, + ['fileSystem' => $this->fileSystem, 'destination' => $csvfile] ); + $exportProduct->setWriter($this->writer); $this->assertNotEmpty($exportProduct->export()); return $csvfile; diff --git a/dev/tests/integration/testsuite/Magento/CatalogImportExport/Model/Export/ProductTest.php b/dev/tests/integration/testsuite/Magento/CatalogImportExport/Model/Export/ProductTest.php index 4753d947e9d3c..508560d000271 100644 --- a/dev/tests/integration/testsuite/Magento/CatalogImportExport/Model/Export/ProductTest.php +++ b/dev/tests/integration/testsuite/Magento/CatalogImportExport/Model/Export/ProductTest.php @@ -535,4 +535,84 @@ public function testExportProductWithTwoWebsites() $reinitiableConfig->setValue('catalog/price/scope', \Magento\Store\Model\Store::PRICE_SCOPE_GLOBAL); $switchPriceScope->execute($observer); } + + /** + * Verify that "stock status" filter correctly applies to export result + * + * @param string $value + * @param array $productsIncluded + * @param array $productsNotIncluded + * @magentoDataFixture Magento/Catalog/_files/multiple_products_with_few_out_of_stock.php + * @dataProvider filterByQuantityAndStockStatusDataProvider + */ + public function testFilterByQuantityAndStockStatus( + string $value, + array $productsIncluded, + array $productsNotIncluded + ) { + $exportData = $this->doExport(['quantity_and_stock_status' => $value]); + foreach ($productsIncluded as $productName) { + $this->assertContains($productName, $exportData); + } + foreach ($productsNotIncluded as $productName) { + $this->assertNotContains($productName, $exportData); + } + } + /** + * @return array + */ + public function filterByQuantityAndStockStatusDataProvider(): array + { + return [ + [ + '', + [ + 'Simple Product OOS', + 'Simple Product Not Visible', + 'Simple Product Visible and InStock' + ], + [ + ] + ], + [ + '1', + [ + 'Simple Product Not Visible', + 'Simple Product Visible and InStock' + ], + [ + 'Simple Product OOS' + ] + ], + [ + '0', + [ + 'Simple Product OOS' + ], + [ + 'Simple Product Not Visible', + 'Simple Product Visible and InStock' + ] + ] + ]; + } + + /** + * @param array $filters + * @return string + */ + private function doExport(array $filters = []): string + { + $this->model->setWriter( + $this->objectManager->create( + \Magento\ImportExport\Model\Export\Adapter\Csv::class + ) + ); + $this->model->setParameters( + [ + \Magento\ImportExport\Model\Export::FILTER_ELEMENT_GROUP => $filters + ] + ); + return $this->model->export(); + } } diff --git a/dev/tests/integration/testsuite/Magento/CatalogImportExport/Model/Import/ProductTest.php b/dev/tests/integration/testsuite/Magento/CatalogImportExport/Model/Import/ProductTest.php index c47a4f340f983..a9b1fe6d936ba 100644 --- a/dev/tests/integration/testsuite/Magento/CatalogImportExport/Model/Import/ProductTest.php +++ b/dev/tests/integration/testsuite/Magento/CatalogImportExport/Model/Import/ProductTest.php @@ -5,11 +5,6 @@ */ declare(strict_types=1); -/** - * Test class for \Magento\CatalogImportExport\Model\Import\Product - * - * The "CouplingBetweenObjects" warning is caused by tremendous complexity of the original class - */ namespace Magento\CatalogImportExport\Model\Import; use Magento\Catalog\Api\Data\ProductInterface; @@ -18,7 +13,13 @@ use Magento\Catalog\Model\Category; use Magento\Catalog\Model\Product; use Magento\Catalog\Model\ResourceModel\Product as ProductResource; +use Magento\CatalogImportExport\Model\Import\Product as ImportProduct; use Magento\CatalogImportExport\Model\Import\Product\RowValidatorInterface; +use Magento\CatalogInventory\Model\Stock; +use Magento\CatalogInventory\Model\StockRegistry; +use Magento\CatalogInventory\Model\StockRegistryStorage; +use Magento\Framework\Api\SearchCriteria; +use Magento\Framework\Api\SearchCriteriaBuilder; use Magento\Framework\App\Bootstrap; use Magento\Framework\App\Filesystem\DirectoryList; use Magento\Framework\App\ObjectManager; @@ -26,16 +27,19 @@ use Magento\Framework\Exception\NoSuchEntityException; use Magento\Framework\Filesystem; use Magento\Framework\Registry; +use Magento\ImportExport\Helper\Data; use Magento\ImportExport\Model\Import; +use Magento\ImportExport\Model\Import\ErrorProcessing\ProcessingErrorAggregatorInterface; use Magento\ImportExport\Model\Import\Source\Csv; use Magento\Store\Model\Store; use Magento\Store\Model\StoreManagerInterface; +use Magento\TestFramework\Helper\Bootstrap as BootstrapHelper; use Magento\UrlRewrite\Model\ResourceModel\UrlRewriteCollection; use Psr\Log\LoggerInterface; -use Magento\TestFramework\Helper\Bootstrap as BootstrapHelper; /** - * Class ProductTest + * Integration test for \Magento\CatalogImportExport\Model\Import\Product class. + * * @magentoAppIsolation enabled * @magentoDbIsolation enabled * @magentoAppArea adminhtml @@ -78,6 +82,19 @@ class ProductTest extends \Magento\TestFramework\Indexer\TestCase */ private $logger; + /** + * @var SearchCriteriaBuilder + */ + private $searchCriteriaBuilder; + + /** + * @var ProductRepositoryInterface + */ + private $productRepository; + + /** + * @inheritdoc + */ protected function setUp() { $this->objectManager = \Magento\TestFramework\Helper\Bootstrap::getObjectManager(); @@ -89,6 +106,8 @@ protected function setUp() ['logger' => $this->logger] ); $this->importedProducts = []; + $this->searchCriteriaBuilder = $this->objectManager->get(SearchCriteriaBuilder::class); + $this->productRepository = $this->objectManager->get(ProductRepositoryInterface::class); parent::setUp(); } @@ -230,9 +249,9 @@ public function testSaveStockItemQty() $existingProductIds = [$id1, $id2, $id3]; $stockItems = []; foreach ($existingProductIds as $productId) { - /** @var $stockRegistry \Magento\CatalogInventory\Model\StockRegistry */ + /** @var $stockRegistry StockRegistry */ $stockRegistry = \Magento\TestFramework\Helper\Bootstrap::getObjectManager()->create( - \Magento\CatalogInventory\Model\StockRegistry::class + StockRegistry::class ); $stockItem = $stockRegistry->getStockItem($productId, 1); @@ -261,9 +280,9 @@ public function testSaveStockItemQty() /** @var $stockItmBeforeImport \Magento\CatalogInventory\Model\Stock\Item */ foreach ($stockItems as $productId => $stockItmBeforeImport) { - /** @var $stockRegistry \Magento\CatalogInventory\Model\StockRegistry */ + /** @var $stockRegistry StockRegistry */ $stockRegistry = \Magento\TestFramework\Helper\Bootstrap::getObjectManager()->create( - \Magento\CatalogInventory\Model\StockRegistry::class + StockRegistry::class ); $stockItemAfterImport = $stockRegistry->getStockItem($productId, 1); @@ -400,7 +419,7 @@ public function testSaveCustomOptionsWithMultipleStoreViews() $pathToFile = __DIR__ . '/_files/' . $importFile; $importModel = $this->createImportModel($pathToFile); $errors = $importModel->validateData(); - $this->assertTrue($errors->getErrorsCount() == 0); + $this->assertTrue($errors->getErrorsCount() == 0, 'Import File Validation Failed'); $importModel->importData(); /** @var \Magento\Catalog\Api\ProductRepositoryInterface $productRepository */ $productRepository = \Magento\TestFramework\Helper\Bootstrap::getObjectManager()->create( @@ -419,20 +438,41 @@ public function testSaveCustomOptionsWithMultipleStoreViews() $actualOptions = $actualData['options']; sort($expectedOptions); sort($actualOptions); - $this->assertEquals($expectedOptions, $actualOptions); + $this->assertEquals( + $expectedOptions, + $actualOptions, + 'Expected and actual options arrays does not match' + ); // assert of options data - $this->assertCount(count($expectedData['data']), $actualData['data']); - $this->assertCount(count($expectedData['values']), $actualData['values']); + $this->assertCount( + count($expectedData['data']), + $actualData['data'], + 'Expected and actual data count does not match' + ); + $this->assertCount( + count($expectedData['values']), + $actualData['values'], + 'Expected and actual values count does not match' + ); + foreach ($expectedData['options'] as $expectedId => $expectedOption) { $elementExist = false; // find value in actual options and values foreach ($actualData['options'] as $actualId => $actualOption) { if ($actualOption == $expectedOption) { $elementExist = true; - $this->assertEquals($expectedData['data'][$expectedId], $actualData['data'][$actualId]); + $this->assertEquals( + $expectedData['data'][$expectedId], + $actualData['data'][$actualId], + 'Expected data does not match actual data' + ); if (array_key_exists($expectedId, $expectedData['values'])) { - $this->assertEquals($expectedData['values'][$expectedId], $actualData['values'][$actualId]); + $this->assertEquals( + $expectedData['values'][$expectedId], + $actualData['values'][$actualId], + 'Expected values does not match actual data' + ); } unset($actualData['options'][$actualId]); // remove value in case of duplicating key values @@ -445,7 +485,11 @@ public function testSaveCustomOptionsWithMultipleStoreViews() // Make sure that after importing existing options again, option IDs and option value IDs are not changed $customOptionValues = $this->getCustomOptionValues($sku); $this->createImportModel($pathToFile)->importData(); - $this->assertEquals($customOptionValues, $this->getCustomOptionValues($sku)); + $this->assertEquals( + $customOptionValues, + $this->getCustomOptionValues($sku), + 'Option IDs changed after second import' + ); } } @@ -881,6 +925,23 @@ static function (DataObject $image) { $this->assertEquals('/m/a/magento_additional_image_two.jpg', $imageItem->getFile()); } + /** + * Tests importing product images with "no_selection" attribute. + * + * @magentoDataFixture mediaImportImageFixture + * @magentoAppIsolation enabled + */ + public function testSaveImagesNoSelection() + { + $this->importDataForMediaTest('import_media_with_no_selection.csv'); + $product = $this->getProductBySku('simple_new'); + + $this->assertEquals('/m/a/magento_image.jpg', $product->getData('image')); + $this->assertEquals(null, $product->getData('small_image')); + $this->assertEquals(null, $product->getData('thumbnail')); + $this->assertEquals(null, $product->getData('swatch_image')); + } + /** * Test that new images should be added after the existing ones. * @@ -2031,9 +2092,9 @@ public function testProductWithUseConfigSettings() $this->_model->importData(); foreach ($products as $sku => $manageStockUseConfig) { - /** @var \Magento\CatalogInventory\Model\StockRegistry $stockRegistry */ + /** @var StockRegistry $stockRegistry */ $stockRegistry = \Magento\TestFramework\Helper\Bootstrap::getObjectManager()->create( - \Magento\CatalogInventory\Model\StockRegistry::class + StockRegistry::class ); $stockItem = $stockRegistry->getStockItemBySku($sku); $this->assertEquals($manageStockUseConfig, $stockItem->getUseConfigManageStock()); @@ -2372,6 +2433,16 @@ public function validateRowDataProvider() 'behavior' => Import::BEHAVIOR_REPLACE, 'expectedResult' => true, ], + [ + 'row' => ['sku' => 'sku with whitespace ', + 'name' => 'Test', + 'product_type' => 'simple', + '_attribute_set' => 'Default', + 'price' => 10.20, + ], + 'behavior' => Import::BEHAVIOR_ADD_UPDATE, + 'expectedResult' => false, + ], ]; } @@ -2425,6 +2496,7 @@ public function testImportWithFilesystemImages() * @magentoDataFixture Magento/Catalog/_files/attribute_set_with_renamed_group.php * @magentoDataFixture Magento/Catalog/_files/product_without_options.php * @magentoDataFixture Magento/Catalog/_files/second_product_simple.php + * @magentoDbIsolation enabled */ public function testImportDataChangeAttributeSet() { @@ -2645,35 +2717,46 @@ public function testImportWithBackordersDisabled(): void } /** - * Import file by providing import filename in parameters. + * Import file by providing import filename and bunch size. * * @param string $fileName - * @return void + * @param int $bunchSize + * @return bool */ - private function importFile(string $fileName): void + private function importFile(string $fileName, int $bunchSize = 100): bool { - $filesystem = $this->objectManager->create(\Magento\Framework\Filesystem::class); + $importExportData = $this->getMockBuilder(Data::class) + ->disableOriginalConstructor() + ->getMock(); + $importExportData->expects($this->atLeastOnce()) + ->method('getBunchSize') + ->willReturn($bunchSize); + $this->_model = $this->objectManager->create( + ImportProduct::class, + ['importExportData' => $importExportData] + ); + $filesystem = $this->objectManager->create(Filesystem::class); $directory = $filesystem->getDirectoryWrite(DirectoryList::ROOT); $source = $this->objectManager->create( - \Magento\ImportExport\Model\Import\Source\Csv::class, + Csv::class, [ - 'file' => __DIR__ . '/_files/' . $fileName, + 'file' => __DIR__ . DIRECTORY_SEPARATOR . '_files' . DIRECTORY_SEPARATOR . $fileName, 'directory' => $directory, ] ); $errors = $this->_model->setParameters( [ - 'behavior' => \Magento\ImportExport\Model\Import::BEHAVIOR_APPEND, + 'behavior' => Import::BEHAVIOR_APPEND, 'entity' => 'catalog_product', - \Magento\ImportExport\Model\Import::FIELDS_ENCLOSURE => 1, + Import::FIELDS_ENCLOSURE => 1, ] ) - ->setSource($source) - ->validateData(); + ->setSource($source) + ->validateData(); - $this->assertTrue($errors->getErrorsCount() == 0); + $this->assertTrue($errors->getErrorsCount() === 0); - $this->_model->importData(); + return $this->_model->importData(); } /** @@ -2833,4 +2916,215 @@ public function testChangeImageLabelForStoreView() $this->assertEquals($expectedImageFile, $imageItem->getFile()); $this->assertEquals($expectedLabelForSecondStoreView, $imageItem->getLabel()); } + + /** + * Test that configurable product images are imported correctly. + * + * @magentoDataFixture mediaImportImageFixture + * @magentoDataFixture Magento/Store/_files/core_fixturestore.php + * @magentoDataFixture Magento/ConfigurableProduct/_files/configurable_attribute.php + */ + public function testImportConfigurableProductImages() + { + $this->importDataForMediaTest('import_configurable_product_multistore.csv'); + $expected = [ + 'import-configurable-option-1' => [ + [ + 'file' => '/m/a/magento_image.jpg', + 'label' => 'Base Image Label - Option 1', + ], + [ + 'file' => '/m/a/magento_small_image.jpg', + 'label' => 'Small Image Label - Option 1', + ], + [ + 'file' => '/m/a/magento_thumbnail.jpg', + 'label' => 'Thumbnail Image Label - Option 1', + ], + [ + 'file' => '/m/a/magento_additional_image_one.jpg', + 'label' => '', + ], + ], + 'import-configurable-option-2' => [ + [ + 'file' => '/m/a/magento_image.jpg', + 'label' => 'Base Image Label - Option 2', + ], + [ + 'file' => '/m/a/magento_small_image.jpg', + 'label' => 'Small Image Label - Option 2', + ], + [ + 'file' => '/m/a/magento_thumbnail.jpg', + 'label' => 'Thumbnail Image Label - Option 2', + ], + [ + 'file' => '/m/a/magento_additional_image_two.jpg', + 'label' => '', + ], + ], + 'import-configurable' => [ + [ + 'file' => '/m/a/magento_image.jpg', + 'label' => 'Base Image Label - Configurable', + ], + [ + 'file' => '/m/a/magento_small_image.jpg', + 'label' => 'Small Image Label - Configurable', + ], + [ + 'file' => '/m/a/magento_thumbnail.jpg', + 'label' => 'Thumbnail Image Label - Configurable', + ], + [ + 'file' => '/m/a/magento_additional_image_three.jpg', + 'label' => '', + ], + ] + ]; + $actual = []; + $products = ['import-configurable-option-1', 'import-configurable-option-2', 'import-configurable']; + foreach ($products as $sku) { + $product = $this->getProductBySku($sku); + $gallery = $product->getMediaGalleryImages(); + foreach ($gallery->getItems() as $item) { + $actual[$sku][] = $item->toArray(['file', 'label']); + } + } + $this->assertEquals($expected, $actual); + + $expected['import-configurable'] = [ + [ + 'file' => '/m/a/magento_image.jpg', + 'label' => 'Base Image Label - Configurable (fixturestore)', + ], + [ + 'file' => '/m/a/magento_small_image.jpg', + 'label' => 'Small Image Label - Configurable (fixturestore)', + ], + [ + 'file' => '/m/a/magento_thumbnail.jpg', + 'label' => 'Thumbnail Image Label - Configurable (fixturestore)', + ], + [ + 'file' => '/m/a/magento_additional_image_three.jpg', + 'label' => '', + ], + ]; + + $actual = []; + foreach ($products as $sku) { + $product = $this->getProductBySku($sku, 'fixturestore'); + $gallery = $product->getMediaGalleryImages(); + foreach ($gallery->getItems() as $item) { + $actual[$sku][] = $item->toArray(['file', 'label']); + } + } + $this->assertEquals($expected, $actual); + } + + /** + * Test that product stock status is updated after import + * + * @magentoDataFixture mediaImportImageFixture + * @magentoDataFixture Magento/Catalog/_files/product_simple.php + */ + public function testProductStockStatusShouldBeUpdated() + { + /** @var $stockRegistry StockRegistry */ + $stockRegistry = $this->objectManager->create(StockRegistry::class); + /** @var StockRegistryStorage $stockRegistryStorage */ + $stockRegistryStorage = $this->objectManager->get(StockRegistryStorage::class); + $status = $stockRegistry->getStockStatusBySku('simple'); + $this->assertEquals(Stock::STOCK_IN_STOCK, $status->getStockStatus()); + $this->importDataForMediaTest('disable_product.csv'); + $stockRegistryStorage->clean(); + $status = $stockRegistry->getStockStatusBySku('simple'); + $this->assertEquals(Stock::STOCK_OUT_OF_STOCK, $status->getStockStatus()); + $this->importDataForMediaTest('enable_product.csv'); + $stockRegistryStorage->clean(); + $status = $stockRegistry->getStockStatusBySku('simple'); + $this->assertEquals(Stock::STOCK_IN_STOCK, $status->getStockStatus()); + } + + /** + * Test that product stock status is updated after import on schedule + * + * @magentoDataFixture mediaImportImageFixture + * @magentoDataFixture Magento/Catalog/_files/product_simple.php + * @magentoDataFixture Magento/CatalogImportExport/_files/cataloginventory_stock_item_update_by_schedule.php + * @magentoDbIsolation disabled + */ + public function testProductStockStatusShouldBeUpdatedOnSchedule() + { + /** * @var $indexProcessor \Magento\Indexer\Model\Processor */ + $indexProcessor = $this->objectManager->create(\Magento\Indexer\Model\Processor::class); + /** @var $stockRegistry StockRegistry */ + $stockRegistry = $this->objectManager->create(StockRegistry::class); + /** @var StockRegistryStorage $stockRegistryStorage */ + $stockRegistryStorage = $this->objectManager->get(StockRegistryStorage::class); + $status = $stockRegistry->getStockStatusBySku('simple'); + $this->assertEquals(Stock::STOCK_IN_STOCK, $status->getStockStatus()); + $this->importDataForMediaTest('disable_product.csv'); + $indexProcessor->updateMview(); + $stockRegistryStorage->clean(); + $status = $stockRegistry->getStockStatusBySku('simple'); + $this->assertEquals(Stock::STOCK_OUT_OF_STOCK, $status->getStockStatus()); + $this->importDataForMediaTest('enable_product.csv'); + $indexProcessor->updateMview(); + $stockRegistryStorage->clean(); + $status = $stockRegistry->getStockStatusBySku('simple'); + $this->assertEquals(Stock::STOCK_IN_STOCK, $status->getStockStatus()); + } + + /** + * Tests that empty attribute value in the CSV file will be ignored after update a product by the import. + * + * @magentoDataFixture Magento/Catalog/_files/product_with_varchar_attribute.php + */ + public function testEmptyAttributeValueShouldBeIgnoredAfterUpdateProductByImport() + { + $pathToFile = __DIR__ . DIRECTORY_SEPARATOR . '_files' . DIRECTORY_SEPARATOR + . 'import_product_with_empty_attribute_value.csv'; + /** @var ImportProduct $importModel */ + $importModel = $this->createImportModel($pathToFile); + /** @var ProcessingErrorAggregatorInterface $errors */ + $errors = $importModel->validateData(); + $this->assertTrue($errors->getErrorsCount() === 0, 'Import file validation failed.'); + $importModel->importData(); + + $simpleProduct = $this->productRepository->get('simple', false, null, true); + $this->assertEquals('Varchar default value', $simpleProduct->getData('varchar_attribute')); + $this->assertEquals('Short description', $simpleProduct->getData('short_description')); + } + + /** + * Checks possibility to double importing products using the same import file. + * + * Bunch size is using to test importing the same product that will be chunk to different bunches. + * Example: + * - first bunch + * product-sku,default-store + * product-sku,second-store + * - second bunch + * product-sku,third-store + * + * @magentoDbIsolation disabled + * @magentoDataFixture Magento/Store/_files/core_fixturestore.php + * @magentoDataFixture Magento/Store/_files/second_store.php + */ + public function testCheckDoubleImportOfProducts() + { + /** @var SearchCriteria $searchCriteria */ + $searchCriteria = $this->searchCriteriaBuilder->create(); + + $this->assertEquals(true, $this->importFile('products_with_two_store_views.csv', 2)); + $productsAfterFirstImport = $this->productRepository->getList($searchCriteria)->getItems(); + $this->assertEquals(3, count($productsAfterFirstImport)); + + $this->assertEquals(true, $this->importFile('products_with_two_store_views.csv', 2)); + $productsAfterSecondImport = $this->productRepository->getList($searchCriteria)->getItems(); + $this->assertEquals(3, count($productsAfterSecondImport)); + } } diff --git a/dev/tests/integration/testsuite/Magento/CatalogImportExport/Model/Import/_files/disable_product.csv b/dev/tests/integration/testsuite/Magento/CatalogImportExport/Model/Import/_files/disable_product.csv new file mode 100644 index 0000000000000..b366fb63afd92 --- /dev/null +++ b/dev/tests/integration/testsuite/Magento/CatalogImportExport/Model/Import/_files/disable_product.csv @@ -0,0 +1,2 @@ +"sku", "product_online" +"simple", "0" diff --git a/dev/tests/integration/testsuite/Magento/CatalogImportExport/Model/Import/_files/enable_product.csv b/dev/tests/integration/testsuite/Magento/CatalogImportExport/Model/Import/_files/enable_product.csv new file mode 100644 index 0000000000000..eb36621bc61e1 --- /dev/null +++ b/dev/tests/integration/testsuite/Magento/CatalogImportExport/Model/Import/_files/enable_product.csv @@ -0,0 +1,2 @@ +"sku", "product_online" +"simple", "1" diff --git a/dev/tests/integration/testsuite/Magento/CatalogImportExport/Model/Import/_files/import_configurable_product_multistore.csv b/dev/tests/integration/testsuite/Magento/CatalogImportExport/Model/Import/_files/import_configurable_product_multistore.csv new file mode 100644 index 0000000000000..c13e9b10e90ae --- /dev/null +++ b/dev/tests/integration/testsuite/Magento/CatalogImportExport/Model/Import/_files/import_configurable_product_multistore.csv @@ -0,0 +1,5 @@ +"sku","store_view_code","product_type","categories","name","base_image","base_image_label","thumbnail_image","thumbnail_image_label","small_image","small_image_label","additional_images","weight","product_online","tax_class_name","visibility","price","url_key","allow_backorders","min_cart_qty","max_cart_qty","is_in_stock","additional_attributes","configurable_variations","configurable_variation_labels","qty","attribute_set_code" +"import-configurable-option-1",,"simple","Default Category","import-configurable-option-1","magento_image.jpg","Base Image Label - Option 1","magento_thumbnail.jpg","Thumbnail Image Label - Option 1","magento_small_image.jpg","Small Image Label - Option 1","magento_additional_image_one.jpg","1","1","Taxable Goods","Not Visible Individually","14.99","import-configurable-option-1-key","0","1","10","1","test_configurable=Option 1",,,"100","Default" +"import-configurable-option-2",,"simple","Default Category","import-configurable-option-2","magento_image.jpg","Base Image Label - Option 2","magento_thumbnail.jpg","Thumbnail Image Label - Option 2","magento_small_image.jpg","Small Image Label - Option 2","magento_additional_image_two.jpg","1","1","Taxable Goods","Not Visible Individually","14.99","import-configurable-option-2-key","0","1","10","1","test_configurable=Option 2",,,"100","Default" +"import-configurable",,"configurable","Default Category","import-configurable","magento_image.jpg","Base Image Label - Configurable","magento_thumbnail.jpg","Thumbnail Image Label - Configurable","magento_small_image.jpg","Small Image Label - Configurable","magento_additional_image_three.jpg","1","1","Taxable Goods","Catalog, Search","14.99","import-configurable-key","0",,,,,"sku=import-configurable-option-1,test_configurable=Option 1|sku=import-configurable-option-2,test_configurable=Option 2","test_configurable=test_configurable=test_configurable",,"Default" +"import-configurable","fixturestore","configurable",,"import-configurable (fixturestore)","magento_image.jpg","Base Image Label - Configurable (fixturestore)","magento_thumbnail.jpg","Thumbnail Image Label - Configurable (fixturestore)","magento_small_image.jpg","Small Image Label - Configurable (fixturestore)",,,,,,,,,,,,,,,,"Default" diff --git a/dev/tests/integration/testsuite/Magento/CatalogImportExport/Model/Import/_files/import_media_with_no_selection.csv b/dev/tests/integration/testsuite/Magento/CatalogImportExport/Model/Import/_files/import_media_with_no_selection.csv new file mode 100644 index 0000000000000..e194637867c1a --- /dev/null +++ b/dev/tests/integration/testsuite/Magento/CatalogImportExport/Model/Import/_files/import_media_with_no_selection.csv @@ -0,0 +1,2 @@ +sku,store_view_code,attribute_set_code,product_type,categories,product_websites,name,description,short_description,weight,product_online,tax_class_name,visibility,price,special_price,special_price_from_date,special_price_to_date,url_key,meta_title,meta_keywords,meta_description,base_image,base_image_label,small_image,small_image_label,thumbnail_image,thumbnail_image_label,swatch_image,swatch_image_label,created_at,updated_at,new_from_date,new_to_date,display_product_options_in,map_price,msrp_price,map_enabled,custom_design,custom_design_from,custom_design_to,custom_layout_update,page_layout,product_options_container,msrp_display_actual_price_type,country_of_manufacture,additional_attributes,qty,out_of_stock_qty,use_config_min_qty,is_qty_decimal,allow_backorders,use_config_backorders,min_cart_qty,use_config_min_sale_qty,max_cart_qty,use_config_max_sale_qty,is_in_stock,notify_on_stock_below,use_config_notify_stock_qty,manage_stock,use_config_manage_stock,use_config_qty_increments,qty_increments,use_config_enable_qty_inc,enable_qty_increments,is_decimal_divided,website_id,deferred_stock_update,use_config_deferred_stock_update,related_skus,related_position,crosssell_skus,crosssell_position,upsell_skus,upsell_position,additional_images,additional_image_labels,hide_from_product_page,custom_options,bundle_price_type,bundle_sku_type,bundle_price_view,bundle_weight_type,bundle_values,bundle_shipment_type,use_config_lifetime,use_config_email_template,associated_skus,downloadable_links,downloadable_samples,configurable_variations,configurable_variation_labels +simple_new,,Default,virtual,Default Category/cat1,base,simple_new,,,,1,Taxable Goods,"Catalog, Search",10,,,,simple_new,simple_new,simple_new,simple_new,magento_image.jpg,,no_selection,,no_selection,,no_selection,,"3/12/20, 3:02 PM","3/12/20, 3:02 PM",,,Block after Info Column,,,,,,,,,,Use config,,,123,0,1,0,0,1,1,1,10000,1,1,1,1,1,1,1,1,1,0,0,0,1,1,,,,,,,,,,,,,,,,,,,,,,, diff --git a/dev/tests/integration/testsuite/Magento/CatalogImportExport/Model/Import/_files/import_product_with_empty_attribute_value.csv b/dev/tests/integration/testsuite/Magento/CatalogImportExport/Model/Import/_files/import_product_with_empty_attribute_value.csv new file mode 100644 index 0000000000000..8ebaf0b64319e --- /dev/null +++ b/dev/tests/integration/testsuite/Magento/CatalogImportExport/Model/Import/_files/import_product_with_empty_attribute_value.csv @@ -0,0 +1,2 @@ +sku,name,short_description,product_type,attribute_set_code,price,qty,additional_attributes +simple,Simple Product,,simple,Default,10,100,varchar_attribute= diff --git a/dev/tests/integration/testsuite/Magento/CatalogImportExport/Model/Import/_files/product_with_custom_options_and_multiple_store_views.csv b/dev/tests/integration/testsuite/Magento/CatalogImportExport/Model/Import/_files/product_with_custom_options_and_multiple_store_views.csv index d4c4130e946a4..17858531993db 100644 --- a/dev/tests/integration/testsuite/Magento/CatalogImportExport/Model/Import/_files/product_with_custom_options_and_multiple_store_views.csv +++ b/dev/tests/integration/testsuite/Magento/CatalogImportExport/Model/Import/_files/product_with_custom_options_and_multiple_store_views.csv @@ -2,3 +2,7 @@ sku,website_code,store_view_code,attribute_set_code,product_type,name,descriptio simple,base,,Default,simple,New Product,,,9,1,"Catalog, Search","base,secondwebsite",,10,,,,Taxable Goods,new-product,,,,,,,,,,,,,,,,,,,,,,,,"name=Test Field Title,type=field,required=1,sku=1-text,price=100|name=Test Date and Time Title,type=date_time,required=1,sku=2-date,price=200|name=Test Select,type=drop_down,required=1,sku=3-1-select,price=310,option_title=Select Option 1|name=Test Select,type=drop_down,required=1,sku=3-2-select,price=320,option_title=Select Option 2|name=Test Checkbox,type=checkbox,required=1,sku=4-1-select,price=410,option_title=Checkbox Option 1|name=Test Checkbox,type=checkbox,required=1,sku=4-2-select,price=420,option_title=Checkbox Option 2|name=Test Radio,type=radio,required=1,sku=5-1-radio,price=510,option_title=Radio Option 1|name=Test Radio,type=radio,required=1,sku=5-2-radio,price=520,option_title=Radio Option 2",,1,1,999,0,0,0,1,10000,1,1,0,0,,,,,,,,,,,Block after Info Column,,, simple,,default,Default,simple,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,"name=Test Field Title_default,type=field,sku=1-text|name=Test Date and Time Title_default,type=date_time,sku=2-date|name=Test Select_default,type=drop_down,sku=3-1-select,option_title=Select Option 1_default|name=Test Select_default,type=drop_down,sku=3-2-select,option_title=Select Option 2_default|name=Test Checkbox_default,type=checkbox,sku=4-1-select,option_title=Checkbox Option 1_default|name=Test Checkbox_default,type=checkbox,sku=4-2-select,option_title=Checkbox Option 2_default|name=Test Radio_default,type=radio,sku=5-1-radio,option_title=Radio Option 1_default|name=Test Radio_default,type=radio,sku=5-2-radio,option_title=Radio Option 2_default",,,,,,,,,,,,,,,,,,,,,,,,,,, simple,,secondstore,Default,simple,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,"name=Test Field Title_fixture_second_store,type=field,sku=1-text,price=101|name=Test Date and Time Title_fixture_second_store,type=date_time,sku=2-date,price=201|name=Test Select_fixture_second_store,type=drop_down,sku=3-1-select,price=311,option_title=Select Option 1_fixture_second_store|name=Test Select_fixture_second_store,type=drop_down,sku=3-2-select,price=321,option_title=Select Option 2_fixture_second_store|name=Test Checkbox_second_store,type=checkbox,sku=4-1-select,price=411,option_title=Checkbox Option 1_second_store|name=Test Checkbox_second_store,type=checkbox,sku=4-2-select,price=421,option_title=Checkbox Option 2_second_store|name=Test Radio_fixture_second_store,type=radio,sku=5-1-radio,price=511,option_title=Radio Option 1_fixture_second_store|name=Test Radio_fixture_second_store,type=radio,sku=5-2-radio,price=521,option_title=Radio Option 2_fixture_second_store",,,,,,,,,,,,,,,,,,,,,,,,,,, +newprod2,base,secondstore,Default,configurable,New Product 2,,,9,1,"Catalog, Search","base,secondwebsite",,10,,,,Taxable Goods,new-product-2,,,,,,,,,,,,,,,,,,,,,,,,,,1,1,999,0,0,0,1,10000,1,1,0,0,,,,,,,,,,,Block after Info Column,,, +newprod3,base,,Default,configurable,New Product 3,,,9,1,"Catalog, Search","base,secondwebsite",,10,,,,Taxable Goods,new-product-3,,,,,,,,,,,,,,,,,,,,,,,,"name=Line 1,type=field,max_characters=30,required=1,option_title=Line 1|name=Line 2,type=field,max_characters=30,required=0,option_title=Line 2",,1,1,999,0,0,0,1,10000,1,1,0,0,,,,,,,,,,,Block after Info Column,,, +newprod4,base,secondstore,Default,configurable,New Product 4,,,9,1,"Catalog, Search","base,secondwebsite",,10,,,,Taxable Goods,new-product-4,,,,,,,,,,,,,,,,,,,,,,,,,,1,1,999,0,0,0,1,10000,1,1,0,0,,,,,,,,,,,Block after Info Column,,, +newprod5,base,,Default,configurable,New Product 5,,,9,1,"Catalog, Search","base,secondwebsite",,10,,,,Taxable Goods,new-product-5,,,,,,,,,,,,,,,,,,,,,,,,"name=Line 3,type=field,max_characters=30,required=1,option_title=Line 3|name=Line 4,type=field,max_characters=30,required=0,option_title=Line 4",,1,1,999,0,0,0,1,10000,1,1,0,0,,,,,,,,,,,Block after Info Column,,, diff --git a/dev/tests/integration/testsuite/Magento/CatalogImportExport/Model/Import/_files/products_with_two_store_views.csv b/dev/tests/integration/testsuite/Magento/CatalogImportExport/Model/Import/_files/products_with_two_store_views.csv new file mode 100644 index 0000000000000..38667c7898160 --- /dev/null +++ b/dev/tests/integration/testsuite/Magento/CatalogImportExport/Model/Import/_files/products_with_two_store_views.csv @@ -0,0 +1,10 @@ +sku,name,store_view_code,product_type,attribute_set_code,price,custom_options +simple1,Simple 1,,simple,Default,100,"name=Test Option 1,type=field" +simple1,Simple 1,fixturestore,simple,Default,,"name=Test Option 1,type=field" +simple1,Simple 1,fixture_second_store,simple,Default,,"name=Test Option 1,type=field" +simple2,Simple 2,,simple,Default,200,"name=Test Option 1,type=field" +simple2,Simple 2,fixturestore,simple,Default,,"name=Test Option 1,type=field" +simple2,Simple 2,fixture_second_store,simple,Default,,"name=Test Option 1,type=field" +simple3,Simple 3,,simple,Default,300,"name=Test Option 1,type=field" +simple3,Simple 3,fixturestore,simple,Default,,"name=Test Option 1,type=field" +simple3,Simple 3,fixture_second_store,simple,Default,,"name=Test Option 1,type=field" diff --git a/dev/tests/integration/testsuite/Magento/CatalogImportExport/_files/cataloginventory_stock_item_update_by_schedule.php b/dev/tests/integration/testsuite/Magento/CatalogImportExport/_files/cataloginventory_stock_item_update_by_schedule.php new file mode 100644 index 0000000000000..94e700a778d97 --- /dev/null +++ b/dev/tests/integration/testsuite/Magento/CatalogImportExport/_files/cataloginventory_stock_item_update_by_schedule.php @@ -0,0 +1,13 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +declare(strict_types=1); + +use Magento\CatalogInventory\Model\Indexer\Stock\Processor; +use Magento\TestFramework\Helper\Bootstrap; + +/** * @var $indexerProcessor Processor */ +$indexerProcessor = Bootstrap::getObjectManager()->get(Processor::class); +$indexerProcessor->getIndexer()->setScheduled(true); diff --git a/dev/tests/integration/testsuite/Magento/CatalogImportExport/_files/cataloginventory_stock_item_update_by_schedule_rollback.php b/dev/tests/integration/testsuite/Magento/CatalogImportExport/_files/cataloginventory_stock_item_update_by_schedule_rollback.php new file mode 100644 index 0000000000000..bd9a07529e8e3 --- /dev/null +++ b/dev/tests/integration/testsuite/Magento/CatalogImportExport/_files/cataloginventory_stock_item_update_by_schedule_rollback.php @@ -0,0 +1,13 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +declare(strict_types=1); + +use Magento\CatalogInventory\Model\Indexer\Stock\Processor; +use Magento\TestFramework\Helper\Bootstrap; + +/** * @var $indexerProcessor Processor */ +$indexerProcessor = Bootstrap::getObjectManager()->get(Processor::class); +$indexerProcessor->getIndexer()->setScheduled(false); diff --git a/dev/tests/integration/testsuite/Magento/CatalogRule/Model/Indexer/Product/PriceTest.php b/dev/tests/integration/testsuite/Magento/CatalogRule/Model/Indexer/Product/PriceTest.php index 495d19a2745e5..71ea03b1d362b 100644 --- a/dev/tests/integration/testsuite/Magento/CatalogRule/Model/Indexer/Product/PriceTest.php +++ b/dev/tests/integration/testsuite/Magento/CatalogRule/Model/Indexer/Product/PriceTest.php @@ -5,23 +5,52 @@ */ namespace Magento\CatalogRule\Model\Indexer\Product; -use Magento\TestFramework\Helper\Bootstrap; -use Magento\CatalogRule\Model\ResourceModel\Rule; -use Magento\Catalog\Model\ResourceModel\Product\Collection; use Magento\Catalog\Api\ProductRepositoryInterface; +use Magento\Catalog\Model\ProductRepository; +use Magento\Catalog\Model\ResourceModel\Product\Collection; +use Magento\CatalogRule\Model\Indexer\IndexBuilder; +use Magento\CatalogRule\Model\ResourceModel\Rule; use Magento\Framework\Api\SearchCriteriaInterface; use Magento\Framework\Api\SortOrder; +use Magento\Store\Api\WebsiteRepositoryInterface; +use Magento\TestFramework\Helper\Bootstrap; class PriceTest extends \PHPUnit\Framework\TestCase { + /** + * @var \Magento\Framework\ObjectManagerInterface + */ + private $objectManager; /** * @var Rule */ private $resourceRule; + /** + * @var WebsiteRepositoryInterface + */ + private $websiteRepository; + + /** + * @var ProductRepository + */ + private $productRepository; + + /** + * @var IndexBuilder + */ + private $indexerBuilder; + + /** + * @inheritdoc + */ protected function setUp() { - $this->resourceRule = Bootstrap::getObjectManager()->get(Rule::class); + $this->objectManager = Bootstrap::getObjectManager(); + $this->resourceRule = $this->objectManager->get(Rule::class); + $this->websiteRepository = $this->objectManager->get(WebsiteRepositoryInterface::class); + $this->productRepository = $this->objectManager->create(ProductRepository::class); + $this->indexerBuilder = $this->objectManager->get(IndexBuilder::class); } /** @@ -56,6 +85,28 @@ public function testPriceApplying() $this->assertEquals($simpleProduct->getFinalPrice(), $confProduct->getMinimalPrice()); } + /** + * @magentoDataFixtureBeforeTransaction Magento/CatalogRule/_files/simple_product_with_catalog_rule_50_percent_off.php + * @magentoDbIsolation enabled + * @magentoAppIsolation enabled + * @return void + */ + public function testPriceForSecondStore():void + { + $websiteId = $this->websiteRepository->get('test')->getId(); + $simpleProduct = $this->productRepository->get('simple'); + $simpleProduct->setPriceCalculation(true); + $this->assertEquals('simple', $simpleProduct->getSku()); + $this->assertFalse( + $this->resourceRule->getRulePrice(new \DateTime(), $websiteId, 1, $simpleProduct->getId()) + ); + $this->indexerBuilder->reindexById($simpleProduct->getId()); + $this->assertEquals( + $this->resourceRule->getRulePrice(new \DateTime(), $websiteId, 1, $simpleProduct->getId()), + 25 + ); + } + /** * @magentoDataFixtureBeforeTransaction Magento/CatalogRule/_files/simple_products.php * @magentoDataFixtureBeforeTransaction Magento/CatalogRule/_files/rule_by_attribute.php diff --git a/dev/tests/integration/testsuite/Magento/CatalogRule/_files/catalog_rule_10_off_not_logged.php b/dev/tests/integration/testsuite/Magento/CatalogRule/_files/catalog_rule_10_off_not_logged.php index 48ed1956c88a1..52357706b0610 100644 --- a/dev/tests/integration/testsuite/Magento/CatalogRule/_files/catalog_rule_10_off_not_logged.php +++ b/dev/tests/integration/testsuite/Magento/CatalogRule/_files/catalog_rule_10_off_not_logged.php @@ -31,3 +31,4 @@ $indexBuilder = \Magento\TestFramework\Helper\Bootstrap::getObjectManager() ->get(\Magento\CatalogRule\Model\Indexer\IndexBuilder::class); $indexBuilder->reindexFull(); +sleep(1); diff --git a/dev/tests/integration/testsuite/Magento/CatalogRule/_files/catalog_rule_10_off_not_logged_rollback.php b/dev/tests/integration/testsuite/Magento/CatalogRule/_files/catalog_rule_10_off_not_logged_rollback.php index e7985e8d6b149..7c97c5926ce4f 100644 --- a/dev/tests/integration/testsuite/Magento/CatalogRule/_files/catalog_rule_10_off_not_logged_rollback.php +++ b/dev/tests/integration/testsuite/Magento/CatalogRule/_files/catalog_rule_10_off_not_logged_rollback.php @@ -26,3 +26,4 @@ /** @var \Magento\CatalogRule\Model\Indexer\IndexBuilder $indexBuilder */ $indexBuilder = $objectManager->get(\Magento\CatalogRule\Model\Indexer\IndexBuilder::class); $indexBuilder->reindexFull(); +sleep(1); diff --git a/dev/tests/integration/testsuite/Magento/CatalogRule/_files/catalog_rule_6_off_logged_user.php b/dev/tests/integration/testsuite/Magento/CatalogRule/_files/catalog_rule_6_off_logged_user.php new file mode 100644 index 0000000000000..3343a837d17db --- /dev/null +++ b/dev/tests/integration/testsuite/Magento/CatalogRule/_files/catalog_rule_6_off_logged_user.php @@ -0,0 +1,38 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +declare(strict_types=1); + +use Magento\CatalogRule\Api\CatalogRuleRepositoryInterface; +use Magento\CatalogRule\Api\Data\RuleInterface; +use Magento\CatalogRule\Api\Data\RuleInterfaceFactory; +use Magento\CatalogRule\Model\Indexer\IndexBuilder; +use Magento\TestFramework\Helper\Bootstrap; + +$objectManager = Bootstrap::getObjectManager(); +/** @var IndexBuilder $indexBuilder */ +$indexBuilder = $objectManager->get(IndexBuilder::class); +/** @var CatalogRuleRepositoryInterface $catalogRuleRepository */ +$catalogRuleRepository = $objectManager->get(CatalogRuleRepositoryInterface::class); +/** @var RuleInterfaceFactory $catalogRuleFactory */ +$catalogRuleFactory = $objectManager->get(RuleInterfaceFactory::class); +$catalogRule = $catalogRuleFactory->create( + [ + 'data' => [ + RuleInterface::IS_ACTIVE => 1, + RuleInterface::NAME => 'Test Catalog Rule for logged user', + 'customer_group_ids' => 1, + RuleInterface::DISCOUNT_AMOUNT => 6, + 'website_ids' => [1], + RuleInterface::SIMPLE_ACTION => 'by_fixed', + RuleInterface::STOP_RULES_PROCESSING => false, + RuleInterface::SORT_ORDER => 0, + 'sub_is_enable' => 0, + 'sub_discount_amount' => 0, + ] + ] +); +$catalogRuleRepository->save($catalogRule); +$indexBuilder->reindexFull(); diff --git a/dev/tests/integration/testsuite/Magento/CatalogRule/_files/catalog_rule_6_off_logged_user_rollback.php b/dev/tests/integration/testsuite/Magento/CatalogRule/_files/catalog_rule_6_off_logged_user_rollback.php new file mode 100644 index 0000000000000..37c2c8c1f2173 --- /dev/null +++ b/dev/tests/integration/testsuite/Magento/CatalogRule/_files/catalog_rule_6_off_logged_user_rollback.php @@ -0,0 +1,29 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +declare(strict_types=1); + +use Magento\CatalogRule\Api\CatalogRuleRepositoryInterface; +use Magento\CatalogRule\Model\Indexer\IndexBuilder; +use Magento\CatalogRule\Model\ResourceModel\Rule\CollectionFactory; +use Magento\CatalogRule\Model\Rule; +use Magento\TestFramework\Helper\Bootstrap; + +$objectManager = Bootstrap::getObjectManager(); +/** @var IndexBuilder $indexBuilder */ +$indexBuilder = $objectManager->get(IndexBuilder::class); +/** @var CatalogRuleRepositoryInterface $ruleRepository */ +$ruleRepository = $objectManager->create(CatalogRuleRepositoryInterface::class); +/** @var CollectionFactory $ruleCollectionFactory */ +$ruleCollectionFactory = $objectManager->get(CollectionFactory::class); +$ruleCollection = $ruleCollectionFactory->create(); +$ruleCollection->addFieldToFilter('name', ['eq' => 'Test Catalog Rule for logged user']); +$ruleCollection->setPageSize(1); +/** @var Rule $rule */ +$rule = $ruleCollection->getFirstItem(); +if ($rule->getId()) { + $ruleRepository->delete($rule); +} +$indexBuilder->reindexFull(); diff --git a/dev/tests/integration/testsuite/Magento/CatalogRule/_files/catalog_rule_for_category_999.php b/dev/tests/integration/testsuite/Magento/CatalogRule/_files/catalog_rule_for_category_999.php new file mode 100644 index 0000000000000..32815134c78a1 --- /dev/null +++ b/dev/tests/integration/testsuite/Magento/CatalogRule/_files/catalog_rule_for_category_999.php @@ -0,0 +1,59 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +declare(strict_types=1); + +use Magento\CatalogRule\Api\CatalogRuleRepositoryInterface; +use Magento\CatalogRule\Api\Data\RuleInterface; +use Magento\CatalogRule\Api\Data\RuleInterfaceFactory; +use Magento\CatalogRule\Model\Indexer\IndexBuilder; +use Magento\CatalogRule\Model\Rule\Condition\Combine; +use Magento\CatalogRule\Model\Rule\Condition\Product; +use Magento\Customer\Model\Group; +use Magento\Store\Api\WebsiteRepositoryInterface; +use Magento\TestFramework\Helper\Bootstrap; +use Magento\TestFramework\Catalog\Model\GetCategoryByName; + +$objectManager = Bootstrap::getObjectManager(); +/** @var WebsiteRepositoryInterface $websiteRepository */ +$websiteRepository = $objectManager->get(WebsiteRepositoryInterface::class); +$defaultWebsiteId = $websiteRepository->get('base')->getId(); +/** @var IndexBuilder $indexBuilder */ +$indexBuilder = $objectManager->get(IndexBuilder::class); +/** @var CatalogRuleRepositoryInterface $catalogRuleRepository */ +$catalogRuleRepository = $objectManager->get(CatalogRuleRepositoryInterface::class); +/** @var RuleInterfaceFactory $ruleFactory */ +$catalogRuleFactory = $objectManager->get(RuleInterfaceFactory::class); +/** @var GetCategoryByName $getCategoryByName */ +$getCategoryByName = $objectManager->get(GetCategoryByName::class); + +$category = $getCategoryByName->execute('Category 999'); +if ($category->getId()) { + $ruleData = [ + RuleInterface::NAME => 'Catalog rule for category 999', + RuleInterface::IS_ACTIVE => 1, + 'website_ids' => [$defaultWebsiteId], + 'customer_group_ids' => Group::NOT_LOGGED_IN_ID, + RuleInterface::DISCOUNT_AMOUNT => 25, + RuleInterface::SIMPLE_ACTION => 'by_percent', + 'conditions' => [ + '1' => [ + 'type' => Combine::class, + 'aggregator' => 'all', + 'value' => '1', + ], + '1--1' => [ + 'type' => Product::class, + 'attribute' => 'category_ids', + 'operator' => '==', + 'value' => $category->getId(), + ], + ], + ]; + $catalogRule = $catalogRuleFactory->create(); + $catalogRule->loadPost($ruleData); + $catalogRuleRepository->save($catalogRule); + $indexBuilder->reindexFull(); +} diff --git a/dev/tests/integration/testsuite/Magento/CatalogRule/_files/catalog_rule_for_category_999_rollback.php b/dev/tests/integration/testsuite/Magento/CatalogRule/_files/catalog_rule_for_category_999_rollback.php new file mode 100644 index 0000000000000..f7af2256575b8 --- /dev/null +++ b/dev/tests/integration/testsuite/Magento/CatalogRule/_files/catalog_rule_for_category_999_rollback.php @@ -0,0 +1,28 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +declare(strict_types=1); + +use Magento\CatalogRule\Api\CatalogRuleRepositoryInterface; +use Magento\CatalogRule\Model\Indexer\IndexBuilder; +use Magento\CatalogRule\Model\ResourceModel\Rule\CollectionFactory; +use Magento\TestFramework\Helper\Bootstrap; + +$objectManager = Bootstrap::getObjectManager(); +/** @var CatalogRuleRepositoryInterface $ruleRepository */ +$catalogRuleRepository = $objectManager->get(CatalogRuleRepositoryInterface::class); +/** @var CollectionFactory $ruleCollectionFactory */ +$ruleCollectionFactory = $objectManager->get(CollectionFactory::class); +/** @var IndexBuilder $indexBuilder */ +$indexBuilder = $objectManager->get(IndexBuilder::class); + +$ruleCollection = $ruleCollectionFactory->create(); +$ruleCollection->addFieldToFilter('name', 'Catalog rule for category 999'); +$ruleCollection->setPageSize(1); +$catalogRule = $ruleCollection->getFirstItem(); +if ($catalogRule->getId()) { + $catalogRuleRepository->delete($catalogRule); +} +$indexBuilder->reindexFull(); diff --git a/dev/tests/integration/testsuite/Magento/CatalogRule/_files/delete_catalog_rule_data.php b/dev/tests/integration/testsuite/Magento/CatalogRule/_files/delete_catalog_rule_data.php new file mode 100644 index 0000000000000..37121092d0ba0 --- /dev/null +++ b/dev/tests/integration/testsuite/Magento/CatalogRule/_files/delete_catalog_rule_data.php @@ -0,0 +1,6 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +declare(strict_types=1); diff --git a/dev/tests/integration/testsuite/Magento/CatalogRule/_files/delete_catalog_rule_data_rollback.php b/dev/tests/integration/testsuite/Magento/CatalogRule/_files/delete_catalog_rule_data_rollback.php new file mode 100644 index 0000000000000..77f0d1d3781f0 --- /dev/null +++ b/dev/tests/integration/testsuite/Magento/CatalogRule/_files/delete_catalog_rule_data_rollback.php @@ -0,0 +1,26 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +declare(strict_types=1); + +use Magento\CatalogRule\Api\CatalogRuleRepositoryInterface; +use Magento\CatalogRule\Api\Data\RuleInterface; +use Magento\CatalogRule\Model\ResourceModel\Rule\CollectionFactory; +use Magento\CatalogRule\Model\ResourceModel\Rule\Product\Price; +use Magento\TestFramework\Helper\Bootstrap; + +$objectManager = Bootstrap::getObjectManager(); +/** @var CollectionFactory $catalogRuleCollectionFactory */ +$catalogRuleCollectionFactory = $objectManager->get(CollectionFactory::class); +/** @var CatalogRuleRepositoryInterface $catalogRuleRepository */ +$catalogRuleRepository = $objectManager->get(CatalogRuleRepositoryInterface::class); +/** @var Price $catalogRuleProductPriceResource */ +$catalogRuleProductPriceResource = $objectManager->get(Price::class); +$catalogRuleCollection = $catalogRuleCollectionFactory->create(); +/** @var RuleInterface $catalogRule */ +foreach ($catalogRuleCollection->getItems() as $catalogRule) { + $catalogRuleRepository->delete($catalogRule); +} +$catalogRuleProductPriceResource->getConnection()->delete($catalogRuleProductPriceResource->getMainTable()); diff --git a/dev/tests/integration/testsuite/Magento/CatalogRule/_files/rule_adjust_final_price_to_discount_value_not_logged_user.php b/dev/tests/integration/testsuite/Magento/CatalogRule/_files/rule_adjust_final_price_to_discount_value_not_logged_user.php new file mode 100644 index 0000000000000..55824abe28ee9 --- /dev/null +++ b/dev/tests/integration/testsuite/Magento/CatalogRule/_files/rule_adjust_final_price_to_discount_value_not_logged_user.php @@ -0,0 +1,43 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +declare(strict_types=1); + +use Magento\CatalogRule\Api\CatalogRuleRepositoryInterface; +use Magento\CatalogRule\Api\Data\RuleInterface; +use Magento\CatalogRule\Api\Data\RuleInterfaceFactory; +use Magento\CatalogRule\Model\Indexer\IndexBuilder; +use Magento\Customer\Model\Group; +use Magento\Store\Model\WebsiteRepository; +use Magento\TestFramework\Helper\Bootstrap; + +$objectManager = Bootstrap::getObjectManager(); +/** @var WebsiteRepository $websiteRepository */ +$websiteRepository = $objectManager->get(WebsiteRepository::class); +$baseWebsite = $websiteRepository->get('base'); +/** @var IndexBuilder $indexBuilder */ +$indexBuilder = $objectManager->get(IndexBuilder::class); +/** @var CatalogRuleRepositoryInterface $catalogRuleRepository */ +$catalogRuleRepository = $objectManager->get(CatalogRuleRepositoryInterface::class); +/** @var RuleInterfaceFactory $catalogRuleFactory */ +$catalogRuleFactory = $objectManager->get(RuleInterfaceFactory::class); +$catalogRule = $catalogRuleFactory->create( + [ + 'data' => [ + RuleInterface::IS_ACTIVE => 1, + RuleInterface::NAME => 'Rule adjust final price to discount value. Not logged user.', + 'customer_group_ids' => Group::NOT_LOGGED_IN_ID, + RuleInterface::DISCOUNT_AMOUNT => 10, + 'website_ids' => [$baseWebsite->getId()], + RuleInterface::SIMPLE_ACTION => 'to_fixed', + RuleInterface::STOP_RULES_PROCESSING => false, + RuleInterface::SORT_ORDER => 0, + 'sub_is_enable' => 0, + 'sub_discount_amount' => 0, + ] + ] +); +$catalogRuleRepository->save($catalogRule); +$indexBuilder->reindexFull(); diff --git a/dev/tests/integration/testsuite/Magento/CatalogRule/_files/rule_adjust_final_price_to_discount_value_not_logged_user_rollback.php b/dev/tests/integration/testsuite/Magento/CatalogRule/_files/rule_adjust_final_price_to_discount_value_not_logged_user_rollback.php new file mode 100644 index 0000000000000..8b77787d40f14 --- /dev/null +++ b/dev/tests/integration/testsuite/Magento/CatalogRule/_files/rule_adjust_final_price_to_discount_value_not_logged_user_rollback.php @@ -0,0 +1,29 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +declare(strict_types=1); + +use Magento\CatalogRule\Api\CatalogRuleRepositoryInterface; +use Magento\CatalogRule\Model\Indexer\IndexBuilder; +use Magento\CatalogRule\Model\ResourceModel\Rule\CollectionFactory; +use Magento\CatalogRule\Model\Rule; +use Magento\TestFramework\Helper\Bootstrap; + +$objectManager = Bootstrap::getObjectManager(); +/** @var IndexBuilder $indexBuilder */ +$indexBuilder = $objectManager->get(IndexBuilder::class); +/** @var CatalogRuleRepositoryInterface $ruleRepository */ +$ruleRepository = $objectManager->create(CatalogRuleRepositoryInterface::class); +/** @var CollectionFactory $ruleCollectionFactory */ +$ruleCollectionFactory = $objectManager->get(CollectionFactory::class); +$ruleCollection = $ruleCollectionFactory->create(); +$ruleCollection->addFieldToFilter('name', ['eq' => 'Rule adjust final price to discount value. Not logged user.']); +$ruleCollection->setPageSize(1); +/** @var Rule $rule */ +$rule = $ruleCollection->getFirstItem(); +if ($rule->getId()) { + $ruleRepository->delete($rule); +} +$indexBuilder->reindexFull(); diff --git a/dev/tests/integration/testsuite/Magento/CatalogRule/_files/rule_adjust_final_price_to_this_percentage_not_logged_user.php b/dev/tests/integration/testsuite/Magento/CatalogRule/_files/rule_adjust_final_price_to_this_percentage_not_logged_user.php new file mode 100644 index 0000000000000..233e231d7cac4 --- /dev/null +++ b/dev/tests/integration/testsuite/Magento/CatalogRule/_files/rule_adjust_final_price_to_this_percentage_not_logged_user.php @@ -0,0 +1,43 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +declare(strict_types=1); + +use Magento\CatalogRule\Api\CatalogRuleRepositoryInterface; +use Magento\CatalogRule\Api\Data\RuleInterface; +use Magento\CatalogRule\Api\Data\RuleInterfaceFactory; +use Magento\CatalogRule\Model\Indexer\IndexBuilder; +use Magento\Customer\Model\Group; +use Magento\Store\Model\WebsiteRepository; +use Magento\TestFramework\Helper\Bootstrap; + +$objectManager = Bootstrap::getObjectManager(); +/** @var WebsiteRepository $websiteRepository */ +$websiteRepository = $objectManager->get(WebsiteRepository::class); +$baseWebsite = $websiteRepository->get('base'); +/** @var IndexBuilder $indexBuilder */ +$indexBuilder = $objectManager->get(IndexBuilder::class); +/** @var CatalogRuleRepositoryInterface $catalogRuleRepository */ +$catalogRuleRepository = $objectManager->get(CatalogRuleRepositoryInterface::class); +/** @var RuleInterfaceFactory $catalogRuleFactory */ +$catalogRuleFactory = $objectManager->get(RuleInterfaceFactory::class); +$catalogRule = $catalogRuleFactory->create( + [ + 'data' => [ + RuleInterface::IS_ACTIVE => 1, + RuleInterface::NAME => 'Rule adjust final price to this percentage. Not logged user.', + 'customer_group_ids' => Group::NOT_LOGGED_IN_ID, + RuleInterface::DISCOUNT_AMOUNT => 10, + 'website_ids' => [$baseWebsite->getId()], + RuleInterface::SIMPLE_ACTION => 'to_percent', + RuleInterface::STOP_RULES_PROCESSING => false, + RuleInterface::SORT_ORDER => 0, + 'sub_is_enable' => 0, + 'sub_discount_amount' => 0, + ] + ] +); +$catalogRuleRepository->save($catalogRule); +$indexBuilder->reindexFull(); diff --git a/dev/tests/integration/testsuite/Magento/CatalogRule/_files/rule_adjust_final_price_to_this_percentage_not_logged_user_rollback.php b/dev/tests/integration/testsuite/Magento/CatalogRule/_files/rule_adjust_final_price_to_this_percentage_not_logged_user_rollback.php new file mode 100644 index 0000000000000..5b1b6501019b8 --- /dev/null +++ b/dev/tests/integration/testsuite/Magento/CatalogRule/_files/rule_adjust_final_price_to_this_percentage_not_logged_user_rollback.php @@ -0,0 +1,29 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +declare(strict_types=1); + +use Magento\CatalogRule\Api\CatalogRuleRepositoryInterface; +use Magento\CatalogRule\Model\Indexer\IndexBuilder; +use Magento\CatalogRule\Model\ResourceModel\Rule\CollectionFactory; +use Magento\CatalogRule\Model\Rule; +use Magento\TestFramework\Helper\Bootstrap; + +$objectManager = Bootstrap::getObjectManager(); +/** @var IndexBuilder $indexBuilder */ +$indexBuilder = $objectManager->get(IndexBuilder::class); +/** @var CatalogRuleRepositoryInterface $ruleRepository */ +$ruleRepository = $objectManager->create(CatalogRuleRepositoryInterface::class); +/** @var CollectionFactory $ruleCollectionFactory */ +$ruleCollectionFactory = $objectManager->get(CollectionFactory::class); +$ruleCollection = $ruleCollectionFactory->create(); +$ruleCollection->addFieldToFilter('name', ['eq' => 'Rule adjust final price to this percentage. Not logged user.']); +$ruleCollection->setPageSize(1); +/** @var Rule $rule */ +$rule = $ruleCollection->getFirstItem(); +if ($rule->getId()) { + $ruleRepository->delete($rule); +} +$indexBuilder->reindexFull(); diff --git a/dev/tests/integration/testsuite/Magento/CatalogRule/_files/rule_apply_as_fixed_amount_not_logged_user.php b/dev/tests/integration/testsuite/Magento/CatalogRule/_files/rule_apply_as_fixed_amount_not_logged_user.php new file mode 100644 index 0000000000000..c37e74de0054c --- /dev/null +++ b/dev/tests/integration/testsuite/Magento/CatalogRule/_files/rule_apply_as_fixed_amount_not_logged_user.php @@ -0,0 +1,43 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +declare(strict_types=1); + +use Magento\CatalogRule\Api\CatalogRuleRepositoryInterface; +use Magento\CatalogRule\Api\Data\RuleInterface; +use Magento\CatalogRule\Api\Data\RuleInterfaceFactory; +use Magento\CatalogRule\Model\Indexer\IndexBuilder; +use Magento\Customer\Model\Group; +use Magento\Store\Model\WebsiteRepository; +use Magento\TestFramework\Helper\Bootstrap; + +$objectManager = Bootstrap::getObjectManager(); +/** @var WebsiteRepository $websiteRepository */ +$websiteRepository = $objectManager->get(WebsiteRepository::class); +$baseWebsite = $websiteRepository->get('base'); +/** @var IndexBuilder $indexBuilder */ +$indexBuilder = $objectManager->get(IndexBuilder::class); +/** @var CatalogRuleRepositoryInterface $catalogRuleRepository */ +$catalogRuleRepository = $objectManager->get(CatalogRuleRepositoryInterface::class); +/** @var RuleInterfaceFactory $catalogRuleFactory */ +$catalogRuleFactory = $objectManager->get(RuleInterfaceFactory::class); +$catalogRule = $catalogRuleFactory->create( + [ + 'data' => [ + RuleInterface::IS_ACTIVE => 1, + RuleInterface::NAME => 'Rule apply as fixed amount. Not logged user.', + 'customer_group_ids' => Group::NOT_LOGGED_IN_ID, + RuleInterface::DISCOUNT_AMOUNT => 10, + 'website_ids' => [$baseWebsite->getId()], + RuleInterface::SIMPLE_ACTION => 'by_fixed', + RuleInterface::STOP_RULES_PROCESSING => false, + RuleInterface::SORT_ORDER => 0, + 'sub_is_enable' => 0, + 'sub_discount_amount' => 0, + ] + ] +); +$catalogRuleRepository->save($catalogRule); +$indexBuilder->reindexFull(); diff --git a/dev/tests/integration/testsuite/Magento/CatalogRule/_files/rule_apply_as_fixed_amount_not_logged_user_rollback.php b/dev/tests/integration/testsuite/Magento/CatalogRule/_files/rule_apply_as_fixed_amount_not_logged_user_rollback.php new file mode 100644 index 0000000000000..33b72dac08924 --- /dev/null +++ b/dev/tests/integration/testsuite/Magento/CatalogRule/_files/rule_apply_as_fixed_amount_not_logged_user_rollback.php @@ -0,0 +1,29 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +declare(strict_types=1); + +use Magento\CatalogRule\Api\CatalogRuleRepositoryInterface; +use Magento\CatalogRule\Model\Indexer\IndexBuilder; +use Magento\CatalogRule\Model\ResourceModel\Rule\CollectionFactory; +use Magento\CatalogRule\Model\Rule; +use Magento\TestFramework\Helper\Bootstrap; + +$objectManager = Bootstrap::getObjectManager(); +/** @var IndexBuilder $indexBuilder */ +$indexBuilder = $objectManager->get(IndexBuilder::class); +/** @var CatalogRuleRepositoryInterface $ruleRepository */ +$ruleRepository = $objectManager->create(CatalogRuleRepositoryInterface::class); +/** @var CollectionFactory $ruleCollectionFactory */ +$ruleCollectionFactory = $objectManager->get(CollectionFactory::class); +$ruleCollection = $ruleCollectionFactory->create(); +$ruleCollection->addFieldToFilter('name', ['eq' => 'Rule apply as fixed amount. Not logged user.']); +$ruleCollection->setPageSize(1); +/** @var Rule $rule */ +$rule = $ruleCollection->getFirstItem(); +if ($rule->getId()) { + $ruleRepository->delete($rule); +} +$indexBuilder->reindexFull(); diff --git a/dev/tests/integration/testsuite/Magento/CatalogRule/_files/rule_apply_as_percentage_of_original_not_logged_user.php b/dev/tests/integration/testsuite/Magento/CatalogRule/_files/rule_apply_as_percentage_of_original_not_logged_user.php new file mode 100644 index 0000000000000..633a265241e33 --- /dev/null +++ b/dev/tests/integration/testsuite/Magento/CatalogRule/_files/rule_apply_as_percentage_of_original_not_logged_user.php @@ -0,0 +1,43 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +declare(strict_types=1); + +use Magento\CatalogRule\Api\CatalogRuleRepositoryInterface; +use Magento\CatalogRule\Api\Data\RuleInterface; +use Magento\CatalogRule\Api\Data\RuleInterfaceFactory; +use Magento\CatalogRule\Model\Indexer\IndexBuilder; +use Magento\Customer\Model\Group; +use Magento\Store\Model\WebsiteRepository; +use Magento\TestFramework\Helper\Bootstrap; + +$objectManager = Bootstrap::getObjectManager(); +/** @var WebsiteRepository $websiteRepository */ +$websiteRepository = $objectManager->get(WebsiteRepository::class); +$baseWebsite = $websiteRepository->get('base'); +/** @var IndexBuilder $indexBuilder */ +$indexBuilder = $objectManager->get(IndexBuilder::class); +/** @var CatalogRuleRepositoryInterface $catalogRuleRepository */ +$catalogRuleRepository = $objectManager->get(CatalogRuleRepositoryInterface::class); +/** @var RuleInterfaceFactory $catalogRuleFactory */ +$catalogRuleFactory = $objectManager->get(RuleInterfaceFactory::class); +$catalogRule = $catalogRuleFactory->create( + [ + 'data' => [ + RuleInterface::IS_ACTIVE => 1, + RuleInterface::NAME => 'Rule apply as percentage of original. Not logged user.', + 'customer_group_ids' => Group::NOT_LOGGED_IN_ID, + RuleInterface::DISCOUNT_AMOUNT => 10, + 'website_ids' => [$baseWebsite->getId()], + RuleInterface::SIMPLE_ACTION => 'by_percent', + RuleInterface::STOP_RULES_PROCESSING => false, + RuleInterface::SORT_ORDER => 0, + 'sub_is_enable' => 0, + 'sub_discount_amount' => 0, + ] + ] +); +$catalogRuleRepository->save($catalogRule); +$indexBuilder->reindexFull(); diff --git a/dev/tests/integration/testsuite/Magento/CatalogRule/_files/rule_apply_as_percentage_of_original_not_logged_user_rollback.php b/dev/tests/integration/testsuite/Magento/CatalogRule/_files/rule_apply_as_percentage_of_original_not_logged_user_rollback.php new file mode 100644 index 0000000000000..d9a1e61b7022e --- /dev/null +++ b/dev/tests/integration/testsuite/Magento/CatalogRule/_files/rule_apply_as_percentage_of_original_not_logged_user_rollback.php @@ -0,0 +1,29 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +declare(strict_types=1); + +use Magento\CatalogRule\Api\CatalogRuleRepositoryInterface; +use Magento\CatalogRule\Model\Indexer\IndexBuilder; +use Magento\CatalogRule\Model\ResourceModel\Rule\CollectionFactory; +use Magento\CatalogRule\Model\Rule; +use Magento\TestFramework\Helper\Bootstrap; + +$objectManager = Bootstrap::getObjectManager(); +/** @var IndexBuilder $indexBuilder */ +$indexBuilder = $objectManager->get(IndexBuilder::class); +/** @var CatalogRuleRepositoryInterface $ruleRepository */ +$ruleRepository = $objectManager->create(CatalogRuleRepositoryInterface::class); +/** @var CollectionFactory $ruleCollectionFactory */ +$ruleCollectionFactory = $objectManager->get(CollectionFactory::class); +$ruleCollection = $ruleCollectionFactory->create(); +$ruleCollection->addFieldToFilter('name', ['eq' => 'Rule apply as percentage of original. Not logged user.']); +$ruleCollection->setPageSize(1); +/** @var Rule $rule */ +$rule = $ruleCollection->getFirstItem(); +if ($rule->getId()) { + $ruleRepository->delete($rule); +} +$indexBuilder->reindexFull(); diff --git a/dev/tests/integration/testsuite/Magento/CatalogRule/_files/rule_by_category_ids_rollback.php b/dev/tests/integration/testsuite/Magento/CatalogRule/_files/rule_by_category_ids_rollback.php new file mode 100644 index 0000000000000..67584a300eacd --- /dev/null +++ b/dev/tests/integration/testsuite/Magento/CatalogRule/_files/rule_by_category_ids_rollback.php @@ -0,0 +1,27 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ + +$objectManager = \Magento\TestFramework\Helper\Bootstrap::getObjectManager(); + +/** @var \Magento\CatalogRule\Model\ResourceModel\Rule $catalogRuleResource */ +$catalogRuleResource = $objectManager->create(\Magento\CatalogRule\Model\ResourceModel\Rule::class); + +//Retrieve rule id by name +$select = $catalogRuleResource->getConnection()->select(); +$select->from($catalogRuleResource->getMainTable(), 'rule_id'); +$select->where('name = ?', 'test_category_rule'); +$ruleId = $catalogRuleResource->getConnection()->fetchOne($select); + +try { + /** @var \Magento\CatalogRule\Api\CatalogRuleRepositoryInterface $ruleRepository */ + $ruleRepository = $objectManager->create(\Magento\CatalogRule\Api\CatalogRuleRepositoryInterface::class); + $ruleRepository->deleteById($ruleId); +} catch (\Exception $ex) { + //Nothing to remove +} +/** @var \Magento\CatalogRule\Model\Indexer\IndexBuilder $indexBuilder */ +$indexBuilder = $objectManager->get(\Magento\CatalogRule\Model\Indexer\IndexBuilder::class); +$indexBuilder->reindexFull(); diff --git a/dev/tests/integration/testsuite/Magento/CatalogRule/_files/simple_product_with_catalog_rule_50_percent_off.php b/dev/tests/integration/testsuite/Magento/CatalogRule/_files/simple_product_with_catalog_rule_50_percent_off.php new file mode 100644 index 0000000000000..cad11e38ac8f3 --- /dev/null +++ b/dev/tests/integration/testsuite/Magento/CatalogRule/_files/simple_product_with_catalog_rule_50_percent_off.php @@ -0,0 +1,75 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +declare(strict_types=1); + +use Magento\Catalog\Api\Data\ProductInterfaceFactory; +use Magento\Catalog\Api\ProductRepositoryInterface; +use Magento\Catalog\Model\Product\Attribute\Source\Status; +use Magento\Catalog\Model\Product\Visibility; +use Magento\CatalogRule\Api\CatalogRuleRepositoryInterface; +use Magento\CatalogRule\Model\Indexer\IndexBuilder; +use Magento\CatalogRule\Model\Rule; +use Magento\CatalogRule\Model\RuleFactory; +use Magento\Customer\Model\Group; +use Magento\Store\Api\WebsiteRepositoryInterface; +use Magento\TestFramework\Helper\Bootstrap; + +require __DIR__ . '/../../Store/_files/second_website_with_two_stores.php'; + +$objectManager = Bootstrap::getObjectManager(); +/** @var WebsiteRepositoryInterface $websiteRepository */ +$websiteRepository = $objectManager->get(WebsiteRepositoryInterface::class); +/** @var IndexBuilder $indexBuilder */ +$indexBuilder = $objectManager->get(IndexBuilder::class); +/** @var CatalogRuleRepositoryInterface $catalogRuleRepository */ +$catalogRuleRepository = $objectManager->get(CatalogRuleRepositoryInterface::class); +/** @var ProductInterfaceFactory $productFactory */ +$productFactory = $objectManager->get(ProductInterfaceFactory::class); +/** @var ProductRepositoryInterface $productRepository */ +$productRepository = $objectManager->get(ProductRepositoryInterface::class); +/** @var RuleFactory $ruleFactory */ +$ruleFactory = $objectManager->get(RuleFactory::class); + +$secondWebsite = $websiteRepository->get('test'); +$product = $productFactory->create(); +$product->setTypeId('simple') + ->setAttributeSetId($product->getDefaultAttributeSetId()) + ->setWebsiteIds([$secondWebsite->getId()]) + ->setName('Simple Product') + ->setSku('simple') + ->setPrice(50) + ->setVisibility(Visibility::VISIBILITY_BOTH) + ->setStatus(Status::STATUS_ENABLED) + ->setStockData( + [ + 'use_config_manage_stock' => 1, + 'qty' => 100, + 'is_qty_decimal' => 0, + 'is_in_stock' => 1, + ] + ); +$productRepository->save($product); +/** @var Rule $rule */ +$catalogRule = $ruleFactory->create(); +$catalogRule->loadPost( + [ + 'name' => 'Test Catalog Rule 50% off', + 'is_active' => '1', + 'stop_rules_processing' => 0, + 'website_ids' => [$secondWebsite->getId()], + 'customer_group_ids' => [Group::NOT_LOGGED_IN_ID, 1], + 'discount_amount' => 50, + 'simple_action' => 'by_percent', + 'from_date' => '', + 'to_date' => '', + 'sort_order' => 0, + 'sub_is_enable' => 0, + 'sub_discount_amount' => 0, + 'conditions' => [], + ] +); +$catalogRuleRepository->save($catalogRule); +$indexBuilder->reindexFull(); diff --git a/dev/tests/integration/testsuite/Magento/CatalogRule/_files/simple_product_with_catalog_rule_50_percent_off_rollback.php b/dev/tests/integration/testsuite/Magento/CatalogRule/_files/simple_product_with_catalog_rule_50_percent_off_rollback.php new file mode 100644 index 0000000000000..a0df9b0daea9f --- /dev/null +++ b/dev/tests/integration/testsuite/Magento/CatalogRule/_files/simple_product_with_catalog_rule_50_percent_off_rollback.php @@ -0,0 +1,54 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +declare(strict_types=1); + +use Magento\Catalog\Api\ProductRepositoryInterface; +use Magento\CatalogRule\Api\CatalogRuleRepositoryInterface; +use Magento\CatalogRule\Model\Indexer\IndexBuilder; +use Magento\CatalogRule\Model\ResourceModel\Rule; +use Magento\Framework\Exception\CouldNotDeleteException; +use Magento\Framework\Exception\NoSuchEntityException; +use Magento\Framework\Registry; +use Magento\TestFramework\Helper\Bootstrap; + +$objectManager = Bootstrap::getObjectManager(); +/** @var Registry $registry */ +$registry = $objectManager->get(Registry::class); +/** @var ProductRepositoryInterface $productRepository */ +$productRepository = $objectManager->create(ProductRepositoryInterface::class); +/** @var CatalogRuleRepositoryInterface $ruleRepository */ +$ruleRepository = $objectManager->get(CatalogRuleRepositoryInterface::class); +/** @var IndexBuilder $indexBuilder */ +$indexBuilder = $objectManager->get(IndexBuilder::class); + +$registry->unregister('isSecureArea'); +$registry->register('isSecureArea', true); +try { + $productRepository->deleteById('simple'); +} catch (NoSuchEntityException $e) { + //already removed +} +$registry->unregister('isSecureArea'); +$registry->register('isSecureArea', false); + +/** @var Rule $catalogRuleResource */ +$catalogRuleResource = $objectManager->create(Rule::class); +//Retrieve rule by name +$select = $catalogRuleResource->getConnection() + ->select() + ->from($catalogRuleResource->getMainTable(), 'rule_id') + ->where('name = ?', 'Test Catalog Rule 50% off'); +$ruleId = $catalogRuleResource->getConnection()->fetchOne($select); + +try { + $ruleRepository->deleteById($ruleId); +} catch (CouldNotDeleteException $ex) { + //Nothing to remove +} + +$indexBuilder->reindexFull(); + +require __DIR__ . '/../../Store/_files/second_website_with_two_stores_rollback.php'; diff --git a/dev/tests/integration/testsuite/Magento/CatalogRuleConfigurable/Model/Product/Type/Configurable/PriceTest.php b/dev/tests/integration/testsuite/Magento/CatalogRuleConfigurable/Model/Product/Type/Configurable/PriceTest.php new file mode 100644 index 0000000000000..1d8264522a2c4 --- /dev/null +++ b/dev/tests/integration/testsuite/Magento/CatalogRuleConfigurable/Model/Product/Type/Configurable/PriceTest.php @@ -0,0 +1,193 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +declare(strict_types=1); + +namespace Magento\CatalogRuleConfigurable\Model\Product\Type\Configurable; + +use Magento\Catalog\Api\Data\ProductInterface; +use Magento\Catalog\Api\ProductRepositoryInterface; +use Magento\Catalog\Model\Product\Type\AbstractType; +use Magento\ConfigurableProduct\Model\Product\Type\Configurable\Price; +use Magento\Customer\Model\Group; +use Magento\Framework\ObjectManagerInterface; +use Magento\Store\Api\WebsiteRepositoryInterface; +use Magento\TestFramework\Catalog\Model\Product\Price\GetPriceIndexDataByProductId; +use Magento\TestFramework\Helper\Bootstrap; +use PHPUnit\Framework\TestCase; + +/** + * Provides tests for configurable product pricing with catalog rules. + * + * @magentoDbIsolation disabled + * @magentoAppArea frontend + */ +class PriceTest extends TestCase +{ + /** + * @var ObjectManagerInterface + */ + private $objectManager; + + /** + * @var WebsiteRepositoryInterface + */ + private $websiteRepository; + + /** + * @var ProductRepositoryInterface + */ + private $productRepository; + + /** + * @var Price + */ + private $priceModel; + + /** + * @var GetPriceIndexDataByProductId + */ + private $getPriceIndexDataByProductId; + + /** + * @inheritdoc + */ + protected function setUp() + { + $this->objectManager = Bootstrap::getObjectManager(); + $this->priceModel = $this->objectManager->create(Price::class); + $this->websiteRepository = $this->objectManager->get(WebsiteRepositoryInterface::class); + $this->productRepository = $this->objectManager->get(ProductRepositoryInterface::class); + $this->productRepository->cleanCache(); + $this->getPriceIndexDataByProductId = $this->objectManager->get(GetPriceIndexDataByProductId::class); + } + + /** + * @magentoDataFixture Magento/CatalogRuleConfigurable/_files/configurable_product_with_percent_rule.php + * @return void + */ + public function testGetFinalPriceWithCustomOptionAndCatalogRule(): void + { + $indexPrices = [ + 'simple_10' => [ + 'price' => 10, + 'final_price' => 9, + 'min_price' => 9, + 'max_price' => 9, + 'tier_price' => null + ], + 'simple_20' => [ + 'price' => 20, + 'final_price' => 15, + 'min_price' => 15, + 'max_price' => 15, + 'tier_price' => 15 + ], + 'configurable' => [ + 'price' => 0, + 'final_price' => 0, + 'min_price' => 9, + 'max_price' => 30, + 'tier_price' => 15 + ], + ]; + $this->assertConfigurableProductPrice(20, 25, $indexPrices); + } + + /** + * @magentoDataFixture Magento/CatalogRuleConfigurable/_files/configurable_product_with_percent_rules_for_children.php + * @return void + */ + public function testGetFinalPriceWithCustomOptionAndCatalogRulesForChildren(): void + { + $indexPrices = [ + 'simple_10' => [ + 'price' => 10, + 'final_price' => 4.5, + 'min_price' => 4.5, + 'max_price' => 9, + 'tier_price' => null + ], + 'simple_20' => [ + 'price' => 20, + 'final_price' => 8, + 'min_price' => 8, + 'max_price' => 15, + 'tier_price' => 15 + ], + 'configurable' => [ + 'price' => 0, + 'final_price' => 0, + 'min_price' => 4.5, + 'max_price' => 23, + 'tier_price' => 15 + ], + ]; + $this->assertConfigurableProductPrice(19.5, 23, $indexPrices); + } + + /** + * Asserts configurable product prices. + * + * @param float $priceWithFirstSimple + * @param float $priceWithSecondSimple + * @param array $indexPrices + * @return void + */ + private function assertConfigurableProductPrice( + float $priceWithFirstSimple, + float $priceWithSecondSimple, + array $indexPrices + ): void { + foreach ($indexPrices as $sku => $prices) { + $this->assertIndexTableData($sku, $prices); + } + $configurable = $this->productRepository->get('configurable'); + //Add tier price option + $optionId = $configurable->getOptions()[0]->getId(); + $configurable->addCustomOption(AbstractType::OPTION_PREFIX . $optionId, 'text'); + $configurable->addCustomOption('option_ids', $optionId); + //First simple rule price + Option price + $this->assertFinalPrice($configurable, $priceWithFirstSimple); + $configurable->addCustomOption('simple_product', 20, $this->productRepository->get('simple_20')); + //Second simple rule price + Option price + $this->assertFinalPrice($configurable, $priceWithSecondSimple); + } + + /** + * Asserts product final price. + * + * @param ProductInterface $product + * @param float $expectedPrice + * @return void + */ + private function assertFinalPrice(ProductInterface $product, float $expectedPrice): void + { + $this->assertEquals( + round($expectedPrice, 2), + round($this->priceModel->getFinalPrice(1, $product), 2) + ); + } + + /** + * Asserts price data in index table. + * + * @param string $sku + * @param array $expectedPrices + * @return void + */ + private function assertIndexTableData(string $sku, array $expectedPrices): void + { + $data = $this->getPriceIndexDataByProductId->execute( + (int)$this->productRepository->get($sku)->getId(), + Group::NOT_LOGGED_IN_ID, + (int)$this->websiteRepository->get('base')->getId() + ); + $data = reset($data); + foreach ($expectedPrices as $column => $price) { + $this->assertEquals($price, $data[$column]); + } + } +} diff --git a/dev/tests/integration/testsuite/Magento/CatalogRuleConfigurable/_files/configurable_product_with_percent_rule.php b/dev/tests/integration/testsuite/Magento/CatalogRuleConfigurable/_files/configurable_product_with_percent_rule.php new file mode 100644 index 0000000000000..abdf785c447a3 --- /dev/null +++ b/dev/tests/integration/testsuite/Magento/CatalogRuleConfigurable/_files/configurable_product_with_percent_rule.php @@ -0,0 +1,50 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +declare(strict_types=1); + +use Magento\CatalogRule\Api\CatalogRuleRepositoryInterface; +use Magento\CatalogRule\Model\Rule; +use Magento\CatalogRule\Model\Rule\Condition\Combine; +use Magento\CatalogRule\Model\Rule\Condition\Product; +use Magento\CatalogRule\Model\RuleFactory; +use Magento\Customer\Model\Group; +use Magento\Framework\App\Area; +use Magento\Store\Api\WebsiteRepositoryInterface; +use Magento\Store\Model\StoreManagerInterface; +use Magento\TestFramework\Helper\Bootstrap; + +require __DIR__ . '/../../ConfigurableProduct/_files/configurable_product_with_custom_option_and_simple_tier_price.php'; +Bootstrap::getInstance()->loadArea(Area::AREA_ADMINHTML); + +/** @var StoreManagerInterface $storeManager */ +$storeManager = $objectManager->get(StoreManagerInterface::class); +/** @var WebsiteRepositoryInterface $websiteRepository */ +$websiteRepository = $objectManager->get(WebsiteRepositoryInterface::class); +/** @var CatalogRuleRepositoryInterface $ruleRepository */ +$ruleRepository = $objectManager->get(CatalogRuleRepositoryInterface::class); +/** @var Rule $rule */ +$rule = $objectManager->get(RuleFactory::class)->create(); +$rule->loadPost( + [ + 'name' => 'Percent rule for configurable product', + 'is_active' => '1', + 'stop_rules_processing' => 0, + 'website_ids' => [$websiteRepository->get('base')->getId()], + 'customer_group_ids' => Group::NOT_LOGGED_IN_ID, + 'discount_amount' => 50, + 'simple_action' => 'by_percent', + 'from_date' => '', + 'to_date' => '', + 'sort_order' => 0, + 'sub_is_enable' => 0, + 'sub_discount_amount' => 0, + 'conditions' => [ + '1' => ['type' => Combine::class, 'aggregator' => 'all', 'value' => '1', 'new_child' => ''], + '1--1' => ['type' => Product::class, 'attribute' => 'sku', 'operator' => '==', 'value' => 'configurable'], + ], + ] +); +$ruleRepository->save($rule); diff --git a/dev/tests/integration/testsuite/Magento/CatalogRuleConfigurable/_files/configurable_product_with_percent_rule_rollback.php b/dev/tests/integration/testsuite/Magento/CatalogRuleConfigurable/_files/configurable_product_with_percent_rule_rollback.php new file mode 100644 index 0000000000000..5b23d1918e394 --- /dev/null +++ b/dev/tests/integration/testsuite/Magento/CatalogRuleConfigurable/_files/configurable_product_with_percent_rule_rollback.php @@ -0,0 +1,33 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +declare(strict_types=1); + +use Magento\CatalogRule\Api\CatalogRuleRepositoryInterface; +use Magento\CatalogRule\Model\Indexer\IndexBuilder; +use Magento\CatalogRule\Model\ResourceModel\Rule\CollectionFactory; +use Magento\CatalogRule\Model\Rule; +use Magento\TestFramework\Helper\Bootstrap; + +require __DIR__ + . '/../../ConfigurableProduct/_files/' + . 'configurable_product_with_custom_option_and_simple_tier_price_rollback.php'; + +$objectManager = Bootstrap::getObjectManager(); +/** @var CatalogRuleRepositoryInterface $ruleRepository */ +$ruleRepository = $objectManager->create(CatalogRuleRepositoryInterface::class); +/** @var IndexBuilder $indexBuilder */ +$indexBuilder = $objectManager->get(IndexBuilder::class); +/** @var CollectionFactory $ruleCollectionFactory */ +$ruleCollectionFactory = $objectManager->get(CollectionFactory::class); +$ruleCollection = $ruleCollectionFactory->create() + ->addFieldToFilter('name', ['eq' => 'Percent rule for configurable product']) + ->setPageSize(1); +/** @var Rule $rule */ +$rule = $ruleCollection->getFirstItem(); +if ($rule->getId()) { + $ruleRepository->delete($rule); +} +$indexBuilder->reindexFull(); diff --git a/dev/tests/integration/testsuite/Magento/CatalogRuleConfigurable/_files/configurable_product_with_percent_rules_for_children.php b/dev/tests/integration/testsuite/Magento/CatalogRuleConfigurable/_files/configurable_product_with_percent_rules_for_children.php new file mode 100644 index 0000000000000..79a66c69ae618 --- /dev/null +++ b/dev/tests/integration/testsuite/Magento/CatalogRuleConfigurable/_files/configurable_product_with_percent_rules_for_children.php @@ -0,0 +1,66 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +declare(strict_types=1); + +use Magento\CatalogRule\Api\CatalogRuleRepositoryInterface; +use Magento\CatalogRule\Model\Rule; +use Magento\CatalogRule\Model\Rule\Condition\Combine; +use Magento\CatalogRule\Model\Rule\Condition\Product; +use Magento\CatalogRule\Model\RuleFactory; +use Magento\Customer\Model\Group; + +require __DIR__ . '/configurable_product_with_percent_rule.php'; + +/** @var CatalogRuleRepositoryInterface $ruleRepository */ +$ruleRepository = $objectManager->get(CatalogRuleRepositoryInterface::class); +/** @var Rule $firstRule */ +$ruleFactory = $objectManager->get(RuleFactory::class); + +$firstRule = $ruleFactory->create(); +$firstRule->loadPost( + [ + 'name' => 'Percent rule for first simple product', + 'is_active' => '1', + 'stop_rules_processing' => 0, + 'website_ids' => [$websiteRepository->get('base')->getId()], + 'customer_group_ids' => Group::NOT_LOGGED_IN_ID, + 'discount_amount' => 10, + 'simple_action' => 'by_percent', + 'from_date' => '', + 'to_date' => '', + 'sort_order' => 0, + 'sub_is_enable' => 0, + 'sub_discount_amount' => 0, + 'conditions' => [ + '1' => ['type' => Combine::class, 'aggregator' => 'all', 'value' => '1', 'new_child' => ''], + '1--1' => ['type' => Product::class, 'attribute' => 'sku', 'operator' => '==', 'value' => 'simple_10'], + ], + ] +); +$ruleRepository->save($firstRule); + +$secondRule = $ruleFactory->create(); +$secondRule->loadPost( + [ + 'name' => 'Percent rule for second simple product', + 'is_active' => '1', + 'stop_rules_processing' => 0, + 'website_ids' => [$websiteRepository->get('base')->getId()], + 'customer_group_ids' => Group::NOT_LOGGED_IN_ID, + 'discount_amount' => 20, + 'simple_action' => 'by_percent', + 'from_date' => '', + 'to_date' => '', + 'sort_order' => 0, + 'sub_is_enable' => 0, + 'sub_discount_amount' => 0, + 'conditions' => [ + '1' => ['type' => Combine::class, 'aggregator' => 'all', 'value' => '1', 'new_child' => ''], + '1--1' => ['type' => Product::class, 'attribute' => 'sku', 'operator' => '==', 'value' => 'simple_20'], + ], + ] +); +$ruleRepository->save($secondRule); diff --git a/dev/tests/integration/testsuite/Magento/CatalogRuleConfigurable/_files/configurable_product_with_percent_rules_for_children_rollback.php b/dev/tests/integration/testsuite/Magento/CatalogRuleConfigurable/_files/configurable_product_with_percent_rules_for_children_rollback.php new file mode 100644 index 0000000000000..773abe6236785 --- /dev/null +++ b/dev/tests/integration/testsuite/Magento/CatalogRuleConfigurable/_files/configurable_product_with_percent_rules_for_children_rollback.php @@ -0,0 +1,35 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +declare(strict_types=1); + +use Magento\CatalogRule\Api\CatalogRuleRepositoryInterface; +use Magento\CatalogRule\Model\Indexer\IndexBuilder; +use Magento\CatalogRule\Model\ResourceModel\Rule\CollectionFactory; +use Magento\TestFramework\Helper\Bootstrap; + +require __DIR__ . '/configurable_product_with_percent_rule_rollback.php'; + +$objectManager = Bootstrap::getObjectManager(); +/** @var CatalogRuleRepositoryInterface $ruleRepository */ +$ruleRepository = $objectManager->create(CatalogRuleRepositoryInterface::class); +/** @var IndexBuilder $indexBuilder */ +$indexBuilder = $objectManager->get(IndexBuilder::class); +/** @var CollectionFactory $ruleCollectionFactory */ +$ruleCollectionFactory = $objectManager->get(CollectionFactory::class); +$ruleCollection = $ruleCollectionFactory->create() + ->addFieldToFilter( + 'name', + [ + 'in' => [ + 'Percent rule for first simple product', + 'Percent rule for second simple product', + ] + ] + ); +foreach ($ruleCollection as $rule) { + $ruleRepository->delete($rule); +} +$indexBuilder->reindexFull(); diff --git a/dev/tests/integration/testsuite/Magento/CatalogSearch/Block/ResultTest.php b/dev/tests/integration/testsuite/Magento/CatalogSearch/Block/ResultTest.php index 9c02623085cab..e36e3dee65954 100644 --- a/dev/tests/integration/testsuite/Magento/CatalogSearch/Block/ResultTest.php +++ b/dev/tests/integration/testsuite/Magento/CatalogSearch/Block/ResultTest.php @@ -5,20 +5,94 @@ */ namespace Magento\CatalogSearch\Block; +use Magento\Framework\App\RequestInterface; +use Magento\Framework\ObjectManagerInterface; +use Magento\Framework\View\Element\Template; +use Magento\Framework\View\Element\Text; +use Magento\Framework\View\LayoutInterface; +use Magento\Search\Model\QueryFactory; +use Magento\TestFramework\Helper\Bootstrap; + class ResultTest extends \PHPUnit\Framework\TestCase { + /** + * @var ObjectManagerInterface + */ + private $objectManager; + + /** + * @var LayoutInterface + */ + private $layout; + + /** + * @inheritdoc + */ + protected function setUp() + { + $this->objectManager = Bootstrap::getObjectManager(); + $this->layout = $this->objectManager->get(LayoutInterface::class); + } + public function testSetListOrders() { - /** @var $layout \Magento\Framework\View\Layout */ - $layout = \Magento\TestFramework\Helper\Bootstrap::getObjectManager()->create( - \Magento\Framework\View\LayoutInterface::class - ); - $layout->addBlock(\Magento\Framework\View\Element\Text::class, 'head'); + $this->layout->addBlock(Text::class, 'head'); // The tested block is using head block - /** @var $block \Magento\CatalogSearch\Block\Result */ - $block = $layout->addBlock(\Magento\CatalogSearch\Block\Result::class, 'block'); - $childBlock = $layout->addBlock(\Magento\Framework\View\Element\Text::class, 'search_result_list', 'block'); + /** @var $block Result */ + $block = $this->layout->addBlock(Result::class, 'block'); + $childBlock = $this->layout->addBlock(Text::class, 'search_result_list', 'block'); $this->assertSame($childBlock, $block->getListBlock()); } + + /** + * Verify search value escaping process + * + * @magentoConfigFixture default/catalog/search/engine elasticsearch6 + * @dataProvider toEscapeSearchTextDataProvider + * @magentoAppArea frontend + * @param string $searchValue + * @param string $expectedOutput + * @param string $unexpectedOutput + * @return void + */ + public function testEscapeSearchText(string $searchValue, string $expectedOutput, string $unexpectedOutput): void + { + /** @var Result $searchResultBlock */ + $searchResultBlock = $this->layout->createBlock(Result::class); + /** @var Template $searchBlock */ + $searchBlock = $this->layout->createBlock(Template::class); + $searchBlock->setTemplate('Magento_Search::form.mini.phtml'); + /** @var RequestInterface $request */ + $request = $this->objectManager->get(RequestInterface::class); + + $request->setParam(QueryFactory::QUERY_VAR_NAME, $searchValue); + $searchHtml = $searchBlock->toHtml(); + + $this->assertContains('value=' . '"' . $expectedOutput . '"', $searchHtml); + $this->assertNotContains($unexpectedOutput, $searchHtml); + + $resultTitle = $searchResultBlock->getSearchQueryText()->render(); + $this->assertContains("Search results for: '{$expectedOutput}'", $resultTitle); + $this->assertNotContains($unexpectedOutput, $resultTitle); + } + + /** + * DataProvider for testEscapeSearchText() + * + * @return array + */ + public function toEscapeSearchTextDataProvider(): array + { + return [ + 'less_than_sign_escaped' => ['<', '<', '&lt;'], + 'greater_than_sign_escaped' => ['>', '>', '&gt;'], + 'ampersand_sign_escaped' => ['&', '&', '&amp;'], + 'double_quote_sign_escaped' => ['"', '"', '&quot;'], + 'single_quote_sign_escaped' => ["'", ''', '&#039;'], + 'plus_sign_not_escaped' => ['+', '+', '&+;'], + 'characters_not_escaped' => ['abc', 'abc', '&abc;'], + 'numbers_not_escaped' => ['123', '123', '&123;'], + ]; + } } diff --git a/dev/tests/integration/testsuite/Magento/CatalogSearch/Controller/Advanced/ResultTest.php b/dev/tests/integration/testsuite/Magento/CatalogSearch/Controller/Advanced/ResultTest.php index 32b7df03f922d..7c627c8f56244 100644 --- a/dev/tests/integration/testsuite/Magento/CatalogSearch/Controller/Advanced/ResultTest.php +++ b/dev/tests/integration/testsuite/Magento/CatalogSearch/Controller/Advanced/ResultTest.php @@ -10,10 +10,10 @@ use Magento\Catalog\Api\ProductAttributeRepositoryInterface; use Magento\Catalog\Model\ResourceModel\Eav\Attribute; use Magento\TestFramework\TestCase\AbstractController; -use Zend\Stdlib\Parameters; +use Laminas\Stdlib\Parameters; /** - * Test cases for catalog advanced search using mysql search engine. + * Test cases for catalog advanced search using search engine. * * @magentoDbIsolation disabled * @magentoAppIsolation enabled @@ -37,7 +37,6 @@ protected function setUp() /** * Advanced search test by difference product attributes. * - * @magentoConfigFixture default/catalog/search/engine mysql * @magentoAppArea frontend * @magentoDataFixture Magento/CatalogSearch/_files/product_for_search.php * @magentoDataFixture Magento/CatalogSearch/_files/full_reindex.php @@ -135,8 +134,8 @@ public function searchStringDataProvider(): array 'description' => '', 'short_description' => '', 'price' => [ - 'from' => '50', - 'to' => '150', + 'from' => 50, + 'to' => 150, ], 'test_searchable_attribute' => '', ], diff --git a/dev/tests/integration/testsuite/Magento/CatalogSearch/Controller/Result/IndexTest.php b/dev/tests/integration/testsuite/Magento/CatalogSearch/Controller/Result/IndexTest.php index 0068d6cbaa015..279d718131dcf 100644 --- a/dev/tests/integration/testsuite/Magento/CatalogSearch/Controller/Result/IndexTest.php +++ b/dev/tests/integration/testsuite/Magento/CatalogSearch/Controller/Result/IndexTest.php @@ -10,7 +10,7 @@ use Magento\TestFramework\TestCase\AbstractController; /** - * Test cases for catalog quick search using mysql search engine. + * Test cases for catalog quick search using search engine. * * @magentoDbIsolation disabled * @magentoAppIsolation enabled diff --git a/dev/tests/integration/testsuite/Magento/CatalogSearch/Model/Indexer/Fulltext/Action/DataProviderTest.php b/dev/tests/integration/testsuite/Magento/CatalogSearch/Model/Indexer/Fulltext/Action/DataProviderTest.php index c090f4ea0183c..8f3f9b3b19c0f 100644 --- a/dev/tests/integration/testsuite/Magento/CatalogSearch/Model/Indexer/Fulltext/Action/DataProviderTest.php +++ b/dev/tests/integration/testsuite/Magento/CatalogSearch/Model/Indexer/Fulltext/Action/DataProviderTest.php @@ -17,7 +17,7 @@ use PHPUnit\Framework\TestCase; /** - * Search products by attribute value using mysql search engine. + * Search products by attribute value search engine. */ class DataProviderTest extends TestCase { @@ -65,7 +65,6 @@ protected function setUp() /** * Search product by custom attribute value. * - * @magentoConfigFixture default/catalog/search/engine mysql * @magentoDataFixture Magento/CatalogSearch/_files/product_for_search.php * @magentoDataFixture Magento/CatalogSearch/_files/full_reindex.php * @magentoDbIsolation disabled diff --git a/dev/tests/integration/testsuite/Magento/CatalogSearch/Model/Indexer/Fulltext/Plugin/AttributeTest.php b/dev/tests/integration/testsuite/Magento/CatalogSearch/Model/Indexer/Fulltext/Plugin/AttributeTest.php new file mode 100644 index 0000000000000..c789183069c10 --- /dev/null +++ b/dev/tests/integration/testsuite/Magento/CatalogSearch/Model/Indexer/Fulltext/Plugin/AttributeTest.php @@ -0,0 +1,141 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +declare(strict_types=1); + +namespace Magento\CatalogSearch\Model\Indexer\Fulltext\Plugin; + +use Magento\Catalog\Model\Product; +use Magento\Catalog\Model\ResourceModel\Eav\Attribute; +use Magento\TestFramework\Helper\Bootstrap; +use PHPUnit\Framework\TestCase; +use Magento\CatalogSearch\Model\Indexer\Fulltext\Processor; +use Magento\Catalog\Api\Data\EavAttributeInterface; + +/** + * Check catalogsearch_fulltext index status after create product attribute. + */ +class AttributeTest extends TestCase +{ + /** + * @var Processor + */ + private $indexerProcessor; + + /** + * @var Attribute + */ + private $attribute; + + /** + * @inheritdoc + */ + protected function setUp() + { + $this->indexerProcessor = Bootstrap::getObjectManager()->create(Processor::class); + $this->attribute = Bootstrap::getObjectManager()->create(Attribute::class); + } + + /** + * Check index status on clean database. + * + * @magentoDataFixture Magento/CatalogSearch/_files/full_reindex.php + */ + public function testCheckIndexStatusOnCleanDatabase(): void + { + $this->assertTrue($this->indexerProcessor->getIndexer()->isValid()); + } + + /** + * Check index status after create non searchable attribute. + * + * @return void + * @magentoDataFixture Magento/Catalog/_files/dropdown_attribute.php + * @magentoDbIsolation enabled + * @depends testCheckIndexStatusOnCleanDatabase + */ + public function testCheckIndexStatusAfterCreateNonSearchableAttribute(): void + { + $this->assertTrue($this->indexerProcessor->getIndexer()->isValid()); + } + + /** + * Check index status after non searchable attribute update. + * + * @return void + * @magentoDataFixture Magento/Catalog/_files/dropdown_attribute.php + * @magentoDbIsolation enabled + * @depends testCheckIndexStatusOnCleanDatabase + */ + public function testCheckIndexStatusAfterNonSearchableAttributeUpdate(): void + { + $this->attribute->load('dropdown_attribute', 'attribute_code'); + $this->assertFalse($this->attribute->isObjectNew()); + $this->attribute->setIsGlobal(1)->save(); + $this->assertTrue($this->indexerProcessor->getIndexer()->isValid()); + } + + /** + * Check index status after create searchable attribute. + * + * @return void + * @magentoDataFixture Magento/CatalogSearch/_files/search_attributes.php + * @magentoDbIsolation enabled + * @depends testCheckIndexStatusOnCleanDatabase + */ + public function testCheckIndexStatusAfterCreateSearchableAttribute(): void + { + $this->assertTrue($this->indexerProcessor->getIndexer()->isInvalid()); + } + + /** + * Check index status after update non searchable attribute to searchable. + * + * @param string $field + * @return void + * @dataProvider searchableAttributesDataProvider + * @magentoDataFixture Magento/Catalog/_files/dropdown_attribute.php + * @magentoDbIsolation enabled + */ + public function testCheckIndexStatusAfterUpdateNonSearchableAttributeToSearchable(string $field): void + { + $this->indexerProcessor->reindexAll(); + $this->assertTrue($this->indexerProcessor->getIndexer()->isValid()); + $this->attribute->loadByCode(Product::ENTITY, 'dropdown_attribute'); + $this->assertFalse($this->attribute->isObjectNew()); + $this->attribute->setData($field, true)->save(); + $this->assertFalse($this->indexerProcessor->getIndexer()->isValid()); + } + + /** + * @return array + */ + public function searchableAttributesDataProvider(): array + { + return [ + [EavAttributeInterface::IS_SEARCHABLE], + [EavAttributeInterface::IS_FILTERABLE], + [EavAttributeInterface::IS_VISIBLE_IN_ADVANCED_SEARCH] + ]; + } + + /** + * Check index status after update searchable attribute to non searchable. + * + * @return void + * @magentoDataFixture Magento/CatalogSearch/_files/search_attributes.php + * @magentoDbIsolation enabled + * @depends testCheckIndexStatusOnCleanDatabase + */ + public function testCheckIndexStatusAfterUpdateSearchableAttributeToNonSearchable(): void + { + $this->indexerProcessor->reindexAll(); + $this->assertTrue($this->indexerProcessor->getIndexer()->isValid()); + $this->attribute->loadByCode(Product::ENTITY, 'test_catalog_view'); + $this->assertFalse($this->attribute->isObjectNew()); + $this->attribute->setIsFilterable(false)->save(); + $this->assertFalse($this->indexerProcessor->getIndexer()->isValid()); + } +} diff --git a/dev/tests/integration/testsuite/Magento/CatalogSearch/Model/Layer/Filter/DecimalTest.php b/dev/tests/integration/testsuite/Magento/CatalogSearch/Model/Layer/Filter/DecimalTest.php index b75a984178f24..f0c8402c51879 100644 --- a/dev/tests/integration/testsuite/Magento/CatalogSearch/Model/Layer/Filter/DecimalTest.php +++ b/dev/tests/integration/testsuite/Magento/CatalogSearch/Model/Layer/Filter/DecimalTest.php @@ -48,46 +48,6 @@ protected function setUp() ->create(\Magento\CatalogSearch\Model\Layer\Filter\Decimal::class, ['layer' => $layer]); $this->_model->setAttributeModel($attribute); } - - /** - * Test the filter label is correct - */ - public function testApplyFilterLabel() - { - /** @var $objectManager \Magento\TestFramework\ObjectManager */ - $objectManager = \Magento\TestFramework\Helper\Bootstrap::getObjectManager(); - /** @var $request \Magento\TestFramework\Request */ - $request = $objectManager->get(\Magento\TestFramework\Request::class); - $request->setParam('weight', '10-20'); - $this->_model->apply($request); - - $filters = $this->_model->getLayer()->getState()->getFilters(); - $this->assertArrayHasKey(0, $filters); - $this->assertEquals( - '<span class="price">$10.00</span> - <span class="price">$19.99</span>', - (string)$filters[0]->getLabel() - ); - } - - /** - * Test the filter label is correct when there is empty To value - */ - public function testApplyFilterLabelWithEmptyToValue() - { - /** @var $objectManager \Magento\TestFramework\ObjectManager */ - $objectManager = \Magento\TestFramework\Helper\Bootstrap::getObjectManager(); - /** @var $request \Magento\TestFramework\Request */ - $request = $objectManager->get(\Magento\TestFramework\Request::class); - $request->setParam('weight', '10-'); - $this->_model->apply($request); - - $filters = $this->_model->getLayer()->getState()->getFilters(); - $this->assertArrayHasKey(0, $filters); - $this->assertEquals( - '<span class="price">$10.00</span> and above', - (string)$filters[0]->getLabel() - ); - } public function testApplyNothing() { diff --git a/dev/tests/integration/testsuite/Magento/CatalogSearch/Model/Layer/Filter/PriceTest.php b/dev/tests/integration/testsuite/Magento/CatalogSearch/Model/Layer/Filter/PriceTest.php index a7944566eb8e0..232b4ec00973a 100644 --- a/dev/tests/integration/testsuite/Magento/CatalogSearch/Model/Layer/Filter/PriceTest.php +++ b/dev/tests/integration/testsuite/Magento/CatalogSearch/Model/Layer/Filter/PriceTest.php @@ -37,16 +37,10 @@ protected function setUp() $category->load(4); $layer = $this->objectManager->get(\Magento\Catalog\Model\Layer\Category::class); $layer->setCurrentCategory($category); - /** @var $attribute \Magento\Catalog\Model\Entity\Attribute */ - $attribute = \Magento\TestFramework\Helper\Bootstrap::getObjectManager()->create( - \Magento\Catalog\Model\Entity\Attribute::class - ); - $attribute->loadByCode('catalog_product', 'price'); $this->_model = $this->objectManager->create( \Magento\CatalogSearch\Model\Layer\Filter\Price::class, ['layer' => $layer] ); - $this->_model->setAttributeModel($attribute); } public function testApplyNothing() @@ -91,9 +85,9 @@ public function testApplyWithCustomCurrencyRate() $request->setParam('price', '10-20'); $this->_model->setCurrencyRate(10); - + $this->_model->apply($request); - + $filters = $this->_model->getLayer()->getState()->getFilters(); $this->assertArrayHasKey(0, $filters); $this->assertEquals( diff --git a/dev/tests/integration/testsuite/Magento/CatalogSearch/Model/ResourceModel/Fulltext/CollectionTest.php b/dev/tests/integration/testsuite/Magento/CatalogSearch/Model/ResourceModel/Fulltext/CollectionTest.php index 8863834078214..55465e938e71b 100644 --- a/dev/tests/integration/testsuite/Magento/CatalogSearch/Model/ResourceModel/Fulltext/CollectionTest.php +++ b/dev/tests/integration/testsuite/Magento/CatalogSearch/Model/ResourceModel/Fulltext/CollectionTest.php @@ -40,6 +40,7 @@ public function testLoadWithFilterSearch($request, $filters, $expectedCount) /** * @dataProvider filtersDataProviderQuickSearch * @magentoDataFixture Magento/Framework/Search/_files/products.php + * @magentoAppIsolation enabled */ public function testLoadWithFilterQuickSearch($filters, $expectedCount) { @@ -61,6 +62,7 @@ public function testLoadWithFilterQuickSearch($filters, $expectedCount) /** * @dataProvider filtersDataProviderCatalogView * @magentoDataFixture Magento/Framework/Search/_files/products.php + * @magentoAppIsolation enabled */ public function testLoadWithFilterCatalogView($filters, $expectedCount) { @@ -78,6 +80,7 @@ public function testLoadWithFilterCatalogView($filters, $expectedCount) /** * @magentoDataFixture Magento/Framework/Search/_files/products_with_the_same_search_score.php + * @magentoAppIsolation enabled */ public function testSearchResultsAreTheSameForSameRequests() { @@ -142,4 +145,157 @@ public function filtersDataProviderCatalogView() [[], 5], ]; } + + /** + * Test configurable product with multiple options + * + * @magentoDataFixture Magento/CatalogSearch/_files/product_configurable_two_options.php + * @magentoConfigFixture default/catalog/search/engine mysql + * @magentoDataFixture Magento/CatalogSearch/_files/full_reindex.php + * @magentoAppIsolation enabled + * @dataProvider configurableProductWithMultipleOptionsDataProvider + * @param array $filters + * @param bool $found + * @param array $outOfStock + * @throws \Magento\Framework\Exception\NoSuchEntityException + */ + public function testConfigurableProductWithMultipleOptions(array $filters, bool $found, array $outOfStock = []) + { + $objectManager = \Magento\TestFramework\Helper\Bootstrap::getObjectManager(); + /**@var $stockRegistry \Magento\CatalogInventory\Model\StockRegistry */ + $stockRegistry = $objectManager->get(\Magento\CatalogInventory\Model\StockRegistry::class); + /**@var $stockItemRepository \Magento\CatalogInventory\Api\StockItemRepositoryInterface */ + $stockItemRepository = $objectManager->get(\Magento\CatalogInventory\Api\StockItemRepositoryInterface::class); + $collection = $objectManager->create( + \Magento\CatalogSearch\Model\ResourceModel\Fulltext\Collection::class, + ['searchRequestName' => 'filter_by_configurable_product_options'] + ); + foreach ($outOfStock as $sku) { + $stockItem = $stockRegistry->getStockItemBySku($sku); + $stockItem->setQty(0); + $stockItem->setIsInStock(0); + $stockItemRepository->save($stockItem); + } + + $options = ['test_configurable', 'test_configurable_2']; + foreach ($options as $option) { + if (isset($filters[$option])) { + $filters[$option] = $this->getOptionValue($option, $filters[$option]); + } + } + $filters['category_ids'] = 2; + foreach ($filters as $field => $value) { + $collection->addFieldToFilter($field, $value); + } + $collection->load(); + $items = $collection->getItems(); + if ($found) { + $this->assertCount(1, $items); + $item = array_shift($items); + $this->assertEquals('configurable_with_2_opts', $item['sku']); + } + $this->assertCount(0, $items); + } + + /** + * Provide filters to test configurable product with multiple options + * + * @return array + */ + public function configurableProductWithMultipleOptionsDataProvider(): array + { + return [ + [ + [], + true + ], + [ + ['test_configurable' => 'Option 1'], + true + ], + [ + ['test_configurable' => 'Option 2'], + true + ], + [ + ['test_configurable_2' => 'Option 1'], + true + ], + [ + ['test_configurable_2' => 'Option 2'], + true + ], + [ + ['test_configurable' => 'Option 1', 'test_configurable_2' => 'Option 1'], + true + ], + [ + ['test_configurable' => 'Option 1', 'test_configurable_2' => 'Option 2'], + true + ], + [ + ['test_configurable' => 'Option 2', 'test_configurable_2' => 'Option 1'], + true + ], + [ + ['test_configurable' => 'Option 2', 'test_configurable_2' => 'Option 2'], + true + ], + [ + ['test_configurable' => 'Option 2', 'test_configurable_2' => 'Option 2'], + false, + [ + 'configurable2_option_12', + 'configurable2_option_22', + ] + ], + [ + ['test_configurable' => 'Option 2', 'test_configurable_2' => 'Option 2'], + false, + [ + 'configurable2_option_21', + 'configurable2_option_22', + ] + ], + [ + ['test_configurable' => 'Option 2'], + false, + [ + 'configurable2_option_21', + 'configurable2_option_22', + ] + ], + [ + [], + false, + [ + 'configurable2_option_11', + 'configurable2_option_12', + 'configurable2_option_21', + 'configurable2_option_22', + ] + ], + ]; + } + + /** + * Get attribute option value by label + * + * @param string $attributeName + * @param string $optionLabel + * @return string|null + */ + private function getOptionValue(string $attributeName, string $optionLabel): ?string + { + $objectManager = \Magento\TestFramework\Helper\Bootstrap::getObjectManager(); + $eavConfig = $objectManager->get(\Magento\Eav\Model\Config::class); + $attribute = $eavConfig->getAttribute(\Magento\Catalog\Model\Product::ENTITY, $attributeName); + $option = null; + foreach ($attribute->getOptions() as $option) { + if ($option->getLabel() === $optionLabel) { + return $option->getValue(); + } + } + return null; + } } diff --git a/dev/tests/integration/testsuite/Magento/CatalogSearch/Model/Search/AttributeSearchWeightTest.php b/dev/tests/integration/testsuite/Magento/CatalogSearch/Model/Search/AttributeSearchWeightTest.php index 4ca8e0b0726d4..bfde20950d105 100644 --- a/dev/tests/integration/testsuite/Magento/CatalogSearch/Model/Search/AttributeSearchWeightTest.php +++ b/dev/tests/integration/testsuite/Magento/CatalogSearch/Model/Search/AttributeSearchWeightTest.php @@ -3,18 +3,12 @@ * Copyright © Magento, Inc. All rights reserved. * See COPYING.txt for license details. */ - declare(strict_types=1); namespace Magento\CatalogSearch\Model\Search; use Magento\Catalog\Api\ProductAttributeRepositoryInterface; -use Magento\Catalog\Model\Layer\Search as CatalogLayerSearch; -use Magento\Catalog\Model\Product; -use Magento\CatalogSearch\Model\ResourceModel\Fulltext\CollectionFactory; -use Magento\Framework\Search\Request\Builder; -use Magento\Framework\Search\Request\Config as RequestConfig; -use Magento\Search\Model\Search; +use Magento\TestFramework\Catalog\Model\Layer\QuickSearchByQuery; use Magento\TestFramework\Helper\Bootstrap; use Magento\TestFramework\ObjectManager; use PHPUnit\Framework\TestCase; @@ -42,9 +36,9 @@ class AttributeSearchWeightTest extends TestCase private $collectedAttributesWeight = []; /** - * @var CatalogLayerSearch + * @var QuickSearchByQuery */ - private $catalogLayerSearch; + private $quickSearchByQuery; /** * @inheritdoc @@ -53,7 +47,7 @@ protected function setUp() { $this->objectManager = Bootstrap::getObjectManager(); $this->productAttributeRepository = $this->objectManager->get(ProductAttributeRepositoryInterface::class); - $this->catalogLayerSearch = $this->objectManager->get(CatalogLayerSearch::class); + $this->quickSearchByQuery = $this->objectManager->get(QuickSearchByQuery::class); $this->collectCurrentProductAttributesWeights(); } @@ -85,9 +79,7 @@ public function testAttributeSearchWeight( array $expectedProductNames ): void { $this->updateAttributesWeight($attributeWeights); - $this->removeInstancesCache(); - $products = $this->findProducts($searchQuery); - $actualProductNames = $this->collectProductsName($products); + $actualProductNames = $this->quickSearchByQuery->execute($searchQuery)->getColumnValues('name'); $this->assertEquals($expectedProductNames, $actualProductNames, 'Products order is not as expected.'); } @@ -164,58 +156,11 @@ protected function updateAttributesWeight(array $attributeWeights): void { foreach ($attributeWeights as $attributeCode => $weight) { $attribute = $this->productAttributeRepository->get($attributeCode); - - if ($attribute) { - $attribute->setSearchWeight($weight); - $this->productAttributeRepository->save($attribute); - } + $attribute->setSearchWeight($weight); + $this->productAttributeRepository->save($attribute); } } - /** - * Get all names from founded products. - * - * @param Product[] $products - * @return array - */ - protected function collectProductsName(array $products): array - { - $result = []; - foreach ($products as $product) { - $result[] = $product->getName(); - } - - return $result; - } - - /** - * Reindex catalogsearch fulltext index. - * - * @return void - */ - protected function removeInstancesCache(): void - { - $this->objectManager->removeSharedInstance(RequestConfig::class); - $this->objectManager->removeSharedInstance(Builder::class); - $this->objectManager->removeSharedInstance(Search::class); - $this->objectManager->removeSharedInstance(CatalogLayerSearch::class); - } - - /** - * Find products by search query. - * - * @param string $query - * @return Product[] - */ - protected function findProducts(string $query): array - { - $testProductCollection = $this->catalogLayerSearch->getProductCollection(); - $testProductCollection->addSearchFilter($query); - $testProductCollection->setOrder('relevance', 'desc'); - - return $testProductCollection->getItems(); - } - /** * Collect weight of attributes which use in test. * diff --git a/dev/tests/integration/testsuite/Magento/CatalogSearch/_files/product_configurable_two_options.php b/dev/tests/integration/testsuite/Magento/CatalogSearch/_files/product_configurable_two_options.php new file mode 100644 index 0000000000000..67bd8b831b878 --- /dev/null +++ b/dev/tests/integration/testsuite/Magento/CatalogSearch/_files/product_configurable_two_options.php @@ -0,0 +1,166 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ + +require __DIR__ . '/../../../Magento/ConfigurableProduct/_files/configurable_attribute.php'; +require __DIR__ . '/../../../Magento/ConfigurableProduct/_files/configurable_attribute_2.php'; + +use Magento\Catalog\Api\CategoryLinkManagementInterface; +use Magento\Catalog\Api\ProductRepositoryInterface; +use Magento\Catalog\Model\Product; +use Magento\Catalog\Model\Product\Attribute\Source\Status; +use Magento\Catalog\Model\Product\Type; +use Magento\Catalog\Model\Product\Visibility; +use Magento\Catalog\Setup\CategorySetup; +use Magento\CatalogInventory\Model\Stock\Item; +use Magento\ConfigurableProduct\Helper\Product\Options\Factory; +use Magento\ConfigurableProduct\Model\Product\Type\Configurable; +use Magento\Eav\Api\AttributeRepositoryInterface; +use Magento\Eav\Api\Data\AttributeOptionInterface; +use Magento\Framework\Search\Request\Config; +use Magento\Framework\Search\Request\Config\Converter; +use Magento\TestFramework\Helper\Bootstrap; + +$objectManager = Bootstrap::getObjectManager(); +/** @var AttributeRepositoryInterface $attributeRepository */ +$attributeRepository = $objectManager->create(AttributeRepositoryInterface::class); +/** @var \Magento\Eav\Model\Config $eavConfig */ +$eavConfig = $objectManager->get(\Magento\Eav\Model\Config::class); +$attributes = ['test_configurable', 'test_configurable_2']; +foreach ($attributes as $attributeName) { + $attributeModel = $eavConfig->getAttribute(Product::ENTITY, $attributeName); + $attributeModel->addData([ + 'is_searchable' => 1, + 'is_filterable' => 1, + 'is_filterable_in_search' => 1, + 'is_visible_in_advanced_search' => 1, + ]); + $attributeRepository->save($attributeModel); +} + +/** @var ProductRepositoryInterface $productRepository */ +$productRepository = $objectManager->create(ProductRepositoryInterface::class); + +/** @var $installer CategorySetup */ +$installer = $objectManager->create(CategorySetup::class); + +/** @var AttributeOptionInterface[] $options */ +$options = $attribute->getOptions(); + +$attribute1Values = []; +$attribute2Values = []; +$attributeSetId = $installer->getAttributeSetId('catalog_product', 'Default'); +$associatedProductIds = []; +array_shift($options); +$index1 = 1; +foreach ($options as $option1) { + /** @var AttributeOptionInterface[] $options */ + $options2 = $attribute2->getOptions(); + array_shift($options2); + $index2 = 1; + foreach ($options2 as $option2) { + /** @var $product Product */ + $product = $objectManager->create(Product::class); + $product->setTypeId(Type::TYPE_SIMPLE) + ->setAttributeSetId($attributeSetId) + ->setWebsiteIds([1]) + ->setName('Configurable2 Option' . $index1 . $index2) + ->setSku('configurable2_option_' . $index1 . $index2) + ->setPrice(random_int(10, 100)) + ->setTestConfigurable($option1->getValue()) + ->setTestConfigurable2($option2->getValue()) + ->setVisibility(Visibility::VISIBILITY_NOT_VISIBLE) + ->setStatus(Status::STATUS_ENABLED) + ->setStockData(['use_config_manage_stock' => 1, 'qty' => 100, 'is_qty_decimal' => 0, 'is_in_stock' => 1]); + + $product = $productRepository->save($product); + + /** @var Item $stockItem */ + $stockItem = $objectManager->create(Item::class); + $stockItem->load($product->getId(), 'product_id'); + + if (!$stockItem->getProductId()) { + $stockItem->setProductId($product->getId()); + } + $stockItem->setUseConfigManageStock(1); + $stockItem->setQty(1000); + $stockItem->setIsQtyDecimal(0); + $stockItem->setIsInStock(1); + $stockItem->save(); + + $attribute1Values[] = [ + 'label' => 'test1', + 'attribute_id' => $attribute->getId(), + 'value_index' => $option1->getValue(), + ]; + $attribute2Values[] = [ + 'label' => 'test2', + 'attribute_id' => $attribute2->getId(), + 'value_index' => $option2->getValue(), + ]; + $associatedProductIds[] = $product->getId(); + $index2++; + } + $index1++; +} + +/** @var $product Product */ +$product = $objectManager->create(Product::class); + +/** @var Factory $optionsFactory */ +$optionsFactory = $objectManager->create(Factory::class); + +$configurableAttributesData = [ + [ + 'attribute_id' => $attribute->getId(), + 'code' => $attribute->getAttributeCode(), + 'label' => $attribute->getStoreLabel(), + 'position' => '0', + 'values' => $attribute1Values, + ], + [ + 'attribute_id' => $attribute2->getId(), + 'code' => $attribute2->getAttributeCode(), + 'label' => $attribute2->getStoreLabel(), + 'position' => '1', + 'values' => $attribute2Values, + ], +]; + +$configurableOptions = $optionsFactory->create($configurableAttributesData); + +$extensionConfigurableAttributes = $product->getExtensionAttributes(); +$extensionConfigurableAttributes->setConfigurableProductOptions($configurableOptions); +$extensionConfigurableAttributes->setConfigurableProductLinks($associatedProductIds); + +$product->setExtensionAttributes($extensionConfigurableAttributes); + +$product->setTypeId(Configurable::TYPE_CODE) + ->setAttributeSetId($attributeSetId) + ->setWebsiteIds([1]) + ->setName('configurable with 2 opts') + ->setSku('configurable_with_2_opts') + ->setVisibility(Visibility::VISIBILITY_BOTH) + ->setStatus(Status::STATUS_ENABLED) + ->setStockData(['use_config_manage_stock' => 1, 'is_in_stock' => 1]); + +$productRepository->save($product); + +/** @var CategoryLinkManagementInterface $categoryLinkManagement */ +$categoryLinkManagement = $objectManager->create(CategoryLinkManagementInterface::class); + +$categoryLinkManagement->assignProductToCategories( + $product->getSku(), + [2] +); + +/** @var Converter $converter */ +$converter = $objectManager->create(Converter::class); +$document = new DOMDocument(); +$document->load(dirname(__FILE__) . DIRECTORY_SEPARATOR . 'requests.xml'); +$requestConfig = $converter->convert($document); +/** @var Config $config */ +$config = $objectManager->get(Config::class); +$config->merge($requestConfig); diff --git a/dev/tests/integration/testsuite/Magento/CatalogSearch/_files/product_configurable_two_options_rollback.php b/dev/tests/integration/testsuite/Magento/CatalogSearch/_files/product_configurable_two_options_rollback.php new file mode 100644 index 0000000000000..4bb2ea4fa8178 --- /dev/null +++ b/dev/tests/integration/testsuite/Magento/CatalogSearch/_files/product_configurable_two_options_rollback.php @@ -0,0 +1,49 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ + +use Magento\Catalog\Api\ProductRepositoryInterface; +use Magento\CatalogInventory\Model\Stock\Status; +use Magento\Framework\Exception\NoSuchEntityException; +use Magento\Framework\Registry; +use Magento\TestFramework\Helper\Bootstrap; + +$objectManager = Bootstrap::getObjectManager(); + +/** @var Registry $registry */ +$registry = $objectManager->get(Registry::class); + +$registry->unregister('isSecureArea'); +$registry->register('isSecureArea', true); + +/** @var ProductRepositoryInterface $productRepository */ +$productRepository = $objectManager->get(ProductRepositoryInterface::class); +$list = [ + 'configurable2_option_11', + 'configurable2_option_12', + 'configurable2_option_21', + 'configurable2_option_22', + 'configurable_with_2_opts' +]; + +foreach ($list as $sku) { + try { + $product = $productRepository->get($sku, true); + + $stockStatus = $objectManager->create(Status::class); + $stockStatus->load($product->getEntityId(), 'product_id'); + $stockStatus->delete(); + + $productRepository->delete($product); + } catch (NoSuchEntityException $e) { + //Product already removed + } +} + +require __DIR__ . '/../../../Magento/ConfigurableProduct/_files/configurable_attribute_rollback.php'; +require __DIR__ . '/../../../Magento/ConfigurableProduct/_files/configurable_attribute_2_rollback.php'; + +$registry->unregister('isSecureArea'); +$registry->register('isSecureArea', false); diff --git a/dev/tests/integration/testsuite/Magento/CatalogSearch/_files/requests.xml b/dev/tests/integration/testsuite/Magento/CatalogSearch/_files/requests.xml new file mode 100644 index 0000000000000..660aa36cc291d --- /dev/null +++ b/dev/tests/integration/testsuite/Magento/CatalogSearch/_files/requests.xml @@ -0,0 +1,38 @@ +<?xml version="1.0"?> +<!-- +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +--> +<requests xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="urn:magento:framework:Search/etc/search_request_merged.xsd"> + <request query="filter_by_configurable_product_options" index="catalogsearch_fulltext"> + <dimensions> + <dimension name="scope" value="default"/> + </dimensions> + <queries> + <query xsi:type="boolQuery" name="filter_by_configurable_product_options" boost="1"> + <queryReference clause="must" ref="category"/> + <queryReference clause="must" ref="test_configurable"/> + <queryReference clause="must" ref="test_configurable_2"/> + </query> + <query xsi:type="filteredQuery" name="category"> + <filterReference clause="must" ref="category_filter"/> + </query> + <query xsi:type="filteredQuery" name="test_configurable"> + <filterReference clause="must" ref="test_configurable_filter"/> + </query> + <query xsi:type="filteredQuery" name="test_configurable_2"> + <filterReference clause="must" ref="test_configurable_2_filter"/> + </query> + </queries> + <filters> + <filter xsi:type="termFilter" name="category_filter" field="category_ids" value="$category_ids$"/> + <filter xsi:type="termFilter" name="test_configurable_filter" field="test_configurable" value="$test_configurable$"/> + <filter xsi:type="termFilter" name="test_configurable_2_filter" field="test_configurable_2" value="$test_configurable_2$"/> + </filters> + <aggregations/> + <from>0</from> + <size>10</size> + </request> +</requests> diff --git a/dev/tests/integration/testsuite/Magento/CatalogUrlRewrite/Model/CategoryUrlRewriteGeneratorTest.php b/dev/tests/integration/testsuite/Magento/CatalogUrlRewrite/Model/CategoryUrlRewriteGeneratorTest.php index b6fa2fac2ca80..67a1e0bb43ecb 100644 --- a/dev/tests/integration/testsuite/Magento/CatalogUrlRewrite/Model/CategoryUrlRewriteGeneratorTest.php +++ b/dev/tests/integration/testsuite/Magento/CatalogUrlRewrite/Model/CategoryUrlRewriteGeneratorTest.php @@ -351,7 +351,7 @@ private function getCountOfRewrites($productId, $categoryId): string $model = $this->objectManager->get(Product::class); $connection = $model->getConnection(); $select = $connection->select(); - $select->from(Product::TABLE_NAME, 'COUNT(*)'); + $select->from($model->getTable(Product::TABLE_NAME), 'COUNT(*)'); $select->where('category_id = ?', $categoryId); $select->where('product_id = ?', $productId); return $connection->fetchOne($select); diff --git a/dev/tests/integration/testsuite/Magento/CatalogUrlRewrite/Model/Product/AnchorUrlRewriteGeneratorTest.php b/dev/tests/integration/testsuite/Magento/CatalogUrlRewrite/Model/Product/AnchorUrlRewriteGeneratorTest.php new file mode 100644 index 0000000000000..446b423e17187 --- /dev/null +++ b/dev/tests/integration/testsuite/Magento/CatalogUrlRewrite/Model/Product/AnchorUrlRewriteGeneratorTest.php @@ -0,0 +1,92 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +declare(strict_types=1); + +namespace Magento\CatalogUrlRewrite\Model\Product; + +use Magento\Catalog\Api\ProductRepositoryInterface; +use Magento\CatalogUrlRewrite\Model\ObjectRegistryFactory; +use Magento\Framework\ObjectManagerInterface; +use Magento\Store\Model\Store; +use Magento\TestFramework\Helper\Bootstrap; +use PHPUnit\Framework\TestCase; + +/** + * Verify generate url rewrites for anchor categories. + */ +class AnchorUrlRewriteGeneratorTest extends TestCase +{ + + /** + * @var ObjectManagerInterface + */ + private $objectManager; + + /** + * @var ProductRepositoryInterface + */ + private $productRepository; + + /** + * @var ObjectRegistryFactory + */ + private $objectRegistryFactory; + + /** + * @inheritDoc + */ + public function setUp() + { + parent::setUp(); // TODO: Change the autogenerated stub + + $this->objectManager = Bootstrap::getObjectManager(); + $this->productRepository = $this->objectManager->create(ProductRepositoryInterface::class); + $this->objectRegistryFactory = $this->objectManager->create(ObjectRegistryFactory::class); + } + + /** + * Verify correct generate of the relative "StoreId" + * + * @param string $expect + * @return void + * @throws \Magento\Framework\Exception\LocalizedException + * @throws \Magento\Framework\Exception\NoSuchEntityException + * @magentoDataFixture Magento/CatalogUrlRewrite/_files/product_with_stores.php + * @magentoDbIsolation disabled + * @dataProvider getConfigGenerate + */ + public function testGenerate(string $expect): void + { + $product = $this->productRepository->get('simple'); + $categories = $product->getCategoryCollection(); + $productCategories = $this->objectRegistryFactory->create(['entities' => $categories]); + + /** @var AnchorUrlRewriteGenerator $generator */ + $generator = $this->objectManager->get(AnchorUrlRewriteGenerator::class); + + /** @var $store Store */ + $store = Bootstrap::getObjectManager()->get(Store::class); + $store->load('fixture_second_store', 'code'); + + $urls = $generator->generate($store->getId(), $product, $productCategories); + + $this->assertEquals($expect, $urls[0]->getRequestPath()); + } + + /** + * Data provider for testGenerate + * + * @return array + */ + public function getConfigGenerate(): array + { + return [ + [ + 'expect' => 'category-1-custom/simple-product.html' + ] + ]; + } +} diff --git a/dev/tests/integration/testsuite/Magento/CatalogUrlRewrite/_files/categories_with_stores.php b/dev/tests/integration/testsuite/Magento/CatalogUrlRewrite/_files/categories_with_stores.php new file mode 100644 index 0000000000000..5fc9d75598da6 --- /dev/null +++ b/dev/tests/integration/testsuite/Magento/CatalogUrlRewrite/_files/categories_with_stores.php @@ -0,0 +1,80 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +declare(strict_types=1); + +use Magento\Backend\App\Area\FrontNameResolver; +use Magento\Catalog\Model\Category; +use Magento\Store\Model\Store; +use Magento\TestFramework\Helper\Bootstrap; + +require __DIR__ . '/../../../Magento/Store/_files/second_store.php'; +Bootstrap::getInstance()->loadArea(FrontNameResolver::AREA_CODE); + +$store = Bootstrap::getObjectManager()->get(Store::class); +$store->load('fixture_second_store', 'code'); + +/** @var $category Category */ +$category = Bootstrap::getObjectManager()->create(Category::class); +$category->isObjectNew(true); +$category->setId(3) + ->setName('Category 1') + ->setParentId(2) + ->setPath('1/2/3') + ->setLevel(2) + ->setAvailableSortBy('name') + ->setDefaultSortBy('name') + ->setUrlPath('category-1-default') + ->setUrlKey('category-1-default') + ->setIsActive(true) + ->setPosition(1) + ->save(); + +$category = Bootstrap::getObjectManager()->create(Category::class); +$category->isObjectNew(true); +$category->setId(4) + ->setName('Category 1.1') + ->setParentId(3) + ->setPath('1/2/3/4') + ->setLevel(3) + ->setAvailableSortBy('name') + ->setDefaultSortBy('name') + ->setUrlPath('category-1-1-default') + ->setUrlKey('category-1-1-default') + ->setIsActive(true) + ->setPosition(1) + ->save(); + +$category = Bootstrap::getObjectManager()->create(Category::class); +$category->isObjectNew(true); +$category->setId(3) + ->setName('Category 1') + ->setParentId(2) + ->setPath('1/2/3') + ->setLevel(2) + ->setAvailableSortBy('name') + ->setDefaultSortBy('name') + ->setStoreId($store->getId()) + ->setUrlPath('category-1-custom') + ->setUrlKey('category-1-custom') + ->setIsActive(true) + ->setPosition(1) + ->save(); + +$category = Bootstrap::getObjectManager()->create(Category::class); +$category->isObjectNew(true); +$category->setId(4) + ->setName('Category 1.1') + ->setParentId(3) + ->setPath('1/2/3/4') + ->setLevel(3) + ->setAvailableSortBy('name') + ->setDefaultSortBy('name') + ->setStoreId($store->getId()) + ->setUrlPath('category-1-1-custom') + ->setUrlKey('category-1-1-custom') + ->setIsActive(true) + ->setPosition(1) + ->save(); diff --git a/dev/tests/integration/testsuite/Magento/CatalogUrlRewrite/_files/categories_with_stores_rollback.php b/dev/tests/integration/testsuite/Magento/CatalogUrlRewrite/_files/categories_with_stores_rollback.php new file mode 100644 index 0000000000000..a89c80a61ccbc --- /dev/null +++ b/dev/tests/integration/testsuite/Magento/CatalogUrlRewrite/_files/categories_with_stores_rollback.php @@ -0,0 +1,23 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +declare(strict_types=1); + +/** @var \Magento\Framework\Registry $registry */ +$objectManager = \Magento\TestFramework\Helper\Bootstrap::getObjectManager(); +$registry = $objectManager->get(\Magento\Framework\Registry::class); + +$registry->unregister('isSecureArea'); +$registry->register('isSecureArea', true); + +/** @var \Magento\Catalog\Model\ResourceModel\Product\Collection $collection */ +$collection = $objectManager->create(\Magento\Catalog\Model\ResourceModel\Category\Collection::class); +$collection + ->addAttributeToFilter('level', 2) + ->load() + ->delete(); + +$registry->unregister('isSecureArea'); +$registry->register('isSecureArea', false); diff --git a/dev/tests/integration/testsuite/Magento/CatalogUrlRewrite/_files/product_with_stores.php b/dev/tests/integration/testsuite/Magento/CatalogUrlRewrite/_files/product_with_stores.php new file mode 100644 index 0000000000000..84fa9b3044af9 --- /dev/null +++ b/dev/tests/integration/testsuite/Magento/CatalogUrlRewrite/_files/product_with_stores.php @@ -0,0 +1,45 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +declare(strict_types=1); + +/** @var \Magento\Catalog\Setup\CategorySetup $installer */ +$installer = \Magento\TestFramework\Helper\Bootstrap::getObjectManager()->create( + \Magento\Catalog\Setup\CategorySetup::class +); + +require __DIR__ . '/categories_with_stores.php'; + +$objectManager = \Magento\TestFramework\Helper\Bootstrap::getObjectManager(); +$productRepository = $objectManager->create(\Magento\Catalog\Api\ProductRepositoryInterface::class); +$categoryLinkRepository = $objectManager->create( + \Magento\Catalog\Api\CategoryLinkRepositoryInterface::class, + [ + 'productRepository' => $productRepository + ] +); + +/** @var Magento\Catalog\Api\CategoryLinkManagementInterface $linkManagement */ +$categoryLinkManagement = $objectManager->create( + \Magento\Catalog\Api\CategoryLinkManagementInterface::class, + [ + 'productRepository' => $productRepository, + 'categoryLinkRepository' => $categoryLinkRepository + ] +); + +/** @var $product \Magento\Catalog\Model\Product */ +$product = \Magento\TestFramework\Helper\Bootstrap::getObjectManager()->create(\Magento\Catalog\Model\Product::class); +$product->setTypeId(\Magento\Catalog\Model\Product\Type::TYPE_SIMPLE) + ->setAttributeSetId($installer->getAttributeSetId('catalog_product', 'Default')) + ->setName('Simple Product') + ->setSku('simple') + ->setPrice(10) + ->setWeight(18) + ->setStockData(['use_config_manage_stock' => 0]) + ->setVisibility(\Magento\Catalog\Model\Product\Visibility::VISIBILITY_BOTH) + ->setStatus(\Magento\Catalog\Model\Product\Attribute\Source\Status::STATUS_ENABLED) + ->save(); +$categoryLinkManagement->assignProductToCategories($product->getSku(), [4]); diff --git a/dev/tests/integration/testsuite/Magento/CatalogUrlRewrite/_files/product_with_stores_rollback.php b/dev/tests/integration/testsuite/Magento/CatalogUrlRewrite/_files/product_with_stores_rollback.php new file mode 100644 index 0000000000000..86f0ce34af00c --- /dev/null +++ b/dev/tests/integration/testsuite/Magento/CatalogUrlRewrite/_files/product_with_stores_rollback.php @@ -0,0 +1,29 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +declare(strict_types=1); + +use Magento\Framework\Exception\NoSuchEntityException; + +\Magento\TestFramework\Helper\Bootstrap::getInstance()->getInstance()->reinitialize(); + +/** @var \Magento\Framework\Registry $registry */ +$registry = \Magento\TestFramework\Helper\Bootstrap::getObjectManager()->get(\Magento\Framework\Registry::class); + +$registry->unregister('isSecureArea'); +$registry->register('isSecureArea', true); + +/** @var \Magento\Catalog\Api\ProductRepositoryInterface $productRepository */ +$productRepository = \Magento\TestFramework\Helper\Bootstrap::getObjectManager() + ->get(\Magento\Catalog\Api\ProductRepositoryInterface::class); +try { + $product = $productRepository->get('simple', true); + if ($product->getId()) { + $productRepository->delete($product); + } +} catch (NoSuchEntityException $e) { +} +$registry->unregister('isSecureArea'); +$registry->register('isSecureArea', false); diff --git a/dev/tests/integration/testsuite/Magento/CatalogWidget/Block/Product/ProductListTest.php b/dev/tests/integration/testsuite/Magento/CatalogWidget/Block/Product/ProductListTest.php index 85cd5331a29c4..8bcd0001b8119 100644 --- a/dev/tests/integration/testsuite/Magento/CatalogWidget/Block/Product/ProductListTest.php +++ b/dev/tests/integration/testsuite/Magento/CatalogWidget/Block/Product/ProductListTest.php @@ -6,27 +6,39 @@ namespace Magento\CatalogWidget\Block\Product; +use Magento\Catalog\Model\Indexer\Product\Eav\Processor; +use Magento\Catalog\Model\ResourceModel\Category\Collection as CategoryCollection; +use Magento\Catalog\Model\ResourceModel\Eav\Attribute; +use Magento\Framework\ObjectManagerInterface; +use Magento\TestFramework\Helper\Bootstrap; +use PHPUnit\Framework\TestCase; + /** * Tests for @see \Magento\CatalogWidget\Block\Product\ProductsList */ -class ProductListTest extends \PHPUnit\Framework\TestCase +class ProductListTest extends TestCase { /** - * @var \Magento\CatalogWidget\Block\Product\ProductsList + * @var ProductsList */ protected $block; /** - * @var \Magento\Framework\ObjectManagerInterface + * @var CategoryCollection; + + */ + private $categoryCollection; + + /** + * @var ObjectManagerInterface */ protected $objectManager; protected function setUp() { - $this->objectManager = \Magento\TestFramework\Helper\Bootstrap::getObjectManager(); - $this->block = $this->objectManager->create( - \Magento\CatalogWidget\Block\Product\ProductsList::class - ); + $this->objectManager = Bootstrap::getObjectManager(); + $this->block = $this->objectManager->create(ProductsList::class); + $this->categoryCollection = $this->objectManager->create(CategoryCollection::class); } /** @@ -44,16 +56,16 @@ protected function setUp() public function testCreateCollection() { // Reindex EAV attributes to enable products filtration by created multiselect attribute - /** @var \Magento\Catalog\Model\Indexer\Product\Eav\Processor $eavIndexerProcessor */ + /** @var Processor $eavIndexerProcessor */ $eavIndexerProcessor = $this->objectManager->get( - \Magento\Catalog\Model\Indexer\Product\Eav\Processor::class + Processor::class ); $eavIndexerProcessor->reindexAll(); // Prepare conditions - /** @var $attribute \Magento\Catalog\Model\ResourceModel\Eav\Attribute */ - $attribute = \Magento\TestFramework\Helper\Bootstrap::getObjectManager()->create( - \Magento\Catalog\Model\ResourceModel\Eav\Attribute::class + /** @var $attribute Attribute */ + $attribute = Bootstrap::getObjectManager()->create( + Attribute::class ); $attribute->load('multiselect_attribute', 'attribute_code'); $multiselectAttributeOptionIds = []; @@ -87,9 +99,9 @@ public function testCreateCollection() */ public function testCreateCollectionWithDropdownAttribute() { - /** @var $attribute \Magento\Catalog\Model\ResourceModel\Eav\Attribute */ - $attribute = \Magento\TestFramework\Helper\Bootstrap::getObjectManager()->create( - \Magento\Catalog\Model\ResourceModel\Eav\Attribute::class + /** @var $attribute Attribute */ + $attribute = Bootstrap::getObjectManager()->create( + Attribute::class ); $attribute->load('dropdown_attribute', 'attribute_code'); $dropdownAttributeOptionIds = []; @@ -119,6 +131,7 @@ public function testCreateCollectionWithDropdownAttribute() * * @param int $count * @return void + * @throws \Magento\Framework\Exception\LocalizedException */ private function performAssertions(int $count) { @@ -142,6 +155,7 @@ private function performAssertions(int $count) * @param string $encodedConditions * @param string $sku * @return void + * @throws \Magento\Framework\Exception\LocalizedException */ public function testCreateCollectionForSku($encodedConditions, $sku) { @@ -179,6 +193,7 @@ public function createCollectionForSkuDataProvider() * @magentoDbIsolation disabled * @magentoDataFixture Magento/Catalog/_files/product_simple_with_date_attribute.php * @return void + * @throws \Magento\Framework\Exception\LocalizedException */ public function testProductListWithDateAttribute() { @@ -197,4 +212,124 @@ public function testProductListWithDateAttribute() "Product collection was not filtered according to the widget condition." ); } + + /** + * Make sure CatalogWidget would display anchor category products recursively from children categories. + * + * 1. Create an anchor root category and a sub category inside it + * 2. Create 2 new products and assign them to the sub categories + * 3. Create product list widget condition to display products from the anchor root category + * 4. Load collection for product list widget and make sure that number of loaded products is correct + * + * @magentoDbIsolation disabled + * @magentoDataFixture Magento/Catalog/_files/product_in_nested_anchor_categories.php + */ + public function testCreateAnchorCollection() + { + // Reindex EAV attributes to enable products filtration by created multiselect attribute + /** @var Processor $eavIndexerProcessor */ + $eavIndexerProcessor = $this->objectManager->get( + Processor::class + ); + $eavIndexerProcessor->reindexAll(); + + $this->categoryCollection->addNameToResult()->load(); + $rootCategoryId = $this + ->categoryCollection + ->getItemByColumnValue('name', 'Default Category') + ->getId(); + + $encodedConditions = '^[`1`:^[`type`:`Magento||CatalogWidget||Model||Rule||Condition||Combine`, + `aggregator`:`all`,`value`:`1`,`new_child`:``^], + `1--1`:^[`type`:`Magento||CatalogWidget||Model||Rule||Condition||Product`, + `attribute`:`category_ids`, + `operator`:`==`,`value`:`' . $rootCategoryId . '`^]^]'; + + $this->block->setData('conditions_encoded', $encodedConditions); + + $productCollection = $this->block->createCollection(); + $productCollection->load(); + + $this->assertEquals( + 2, + $productCollection->count(), + "Anchor root category does not contain products of it's children." + ); + } + + /** + * Test that price rule condition works correctly + * + * @magentoDbIsolation disabled + * @magentoDataFixture Magento/Catalog/_files/category_with_different_price_products.php + * @magentoDataFixture Magento/ConfigurableProduct/_files/product_configurable.php + * @param string $operator + * @param int $value + * @param array $matches + * @dataProvider priceFilterDataProvider + */ + public function testPriceFilter(string $operator, int $value, array $matches) + { + $encodedConditions = '^[`1`:^[`type`:`Magento||CatalogWidget||Model||Rule||Condition||Combine`, + `aggregator`:`all`,`value`:`1`,`new_child`:``^], + `1--1`:^[`type`:`Magento||CatalogWidget||Model||Rule||Condition||Product`, + `attribute`:`price`, + `operator`:`' . $operator . '`,`value`:`' . $value . '`^]^]'; + + $this->block->setData('conditions_encoded', $encodedConditions); + + $productCollection = $this->block->createCollection(); + $productCollection->load(); + $skus = array_map( + function ($item) { + return $item['sku']; + }, + $productCollection->getItems() + ); + $this->assertEquals($matches, $skus, '', 0.0, 10, true); + } + + public function priceFilterDataProvider(): array + { + return [ + [ + '>', + 10, + [ + 'simple1001', + ] + ], + [ + '>=', + 10, + [ + 'simple1000', + 'simple1001', + 'configurable', + ] + ], + [ + '<', + 10, + [] + ], + [ + '<', + 20, + [ + 'simple1000', + 'configurable', + ] + ], + [ + '<=', + 20, + [ + 'simple1000', + 'simple1001', + 'configurable', + ] + ], + ]; + } } diff --git a/dev/tests/integration/testsuite/Magento/Checkout/Api/GuestShippingInformationManagementTest.php b/dev/tests/integration/testsuite/Magento/Checkout/Api/GuestShippingInformationManagementTest.php index 50b1256c0f124..8018f76567d16 100644 --- a/dev/tests/integration/testsuite/Magento/Checkout/Api/GuestShippingInformationManagementTest.php +++ b/dev/tests/integration/testsuite/Magento/Checkout/Api/GuestShippingInformationManagementTest.php @@ -11,6 +11,7 @@ use Magento\Checkout\Api\Data\ShippingInformationInterfaceFactory; use Magento\Customer\Api\CustomerRepositoryInterface; use Magento\Framework\Api\SearchCriteriaBuilder; +use Magento\Framework\Exception\InputException; use Magento\Quote\Api\CartRepositoryInterface; use Magento\Quote\Api\Data\ShippingAssignmentInterface; use Magento\TestFramework\Helper\Bootstrap; @@ -53,6 +54,9 @@ class GuestShippingInformationManagementTest extends TestCase */ private $maskFactory; + /** + * @inheritdoc + */ protected function setUp() { $objectManager = Bootstrap::getObjectManager(); @@ -73,10 +77,8 @@ protected function setUp() * @magentoDataFixture Magento/Sales/_files/quote.php * @magentoDataFixture Magento/Customer/_files/customer_with_addresses.php * @dataProvider getAddressesVariation - * @expectedException \Magento\Framework\Exception\InputException - * @expectedExceptionMessage The shipping information was unable to be saved. Verify the input data and try again. */ - public function testDifferentAddresses(bool $swapShipping) + public function testDifferentAddresses(bool $swapShipping): void { $carts = $this->cartRepo->getList( $this->searchCriteria->addFilter('reserved_order_id', 'test01')->create() @@ -107,6 +109,14 @@ public function testDifferentAddresses(bool $swapShipping) /** @var QuoteIdMask $idMask */ $idMask = $this->maskFactory->create(); $idMask->load($cart->getId(), 'quote_id'); + + $this->expectExceptionMessage( + sprintf( + 'The shipping information was unable to be saved. Error: "Invalid customer address id %s"', + $address->getCustomerAddressId() + ) + ); + $this->expectException(InputException::class); $this->management->saveAddressInformation($idMask->getMaskedId(), $shippingInformation); } diff --git a/dev/tests/integration/testsuite/Magento/Checkout/Api/PaymentInformationManagementTest.php b/dev/tests/integration/testsuite/Magento/Checkout/Api/PaymentInformationManagementTest.php deleted file mode 100644 index 1266dc7bb8843..0000000000000 --- a/dev/tests/integration/testsuite/Magento/Checkout/Api/PaymentInformationManagementTest.php +++ /dev/null @@ -1,162 +0,0 @@ -<?php -/** - * Copyright © Magento, Inc. All rights reserved. - * See COPYING.txt for license details. - */ -declare(strict_types=1); - -namespace Magento\Checkout\Api; - -use Braintree\Result\Error; -use Magento\Braintree\Gateway\Http\Client\TransactionSale; -use Magento\Braintree\Model\Ui\ConfigProvider; -use Magento\Framework\Api\SearchCriteriaBuilder; -use Magento\Framework\App\State; -use Magento\Framework\App\Area; -use Magento\Quote\Api\CartRepositoryInterface; -use Magento\Quote\Api\Data\CartInterface; -use Magento\Quote\Api\Data\PaymentInterface; -use Magento\TestFramework\Helper\Bootstrap; -use Magento\TestFramework\ObjectManager; -use PHPUnit\Framework\TestCase; -use PHPUnit_Framework_MockObject_MockObject as MockObject; - -/** - * Class PaymentInformationManagementTest - * - * @SuppressWarnings(PHPMD.CouplingBetweenObjects) - */ -class PaymentInformationManagementTest extends TestCase -{ - /** - * @var ObjectManager - */ - private $objectManager; - - /** - * @var TransactionSale|MockObject - */ - private $client; - - /** - * @inheritdoc - */ - protected function setUp() - { - $this->objectManager = Bootstrap::getObjectManager(); - - $this->client = $this->getMockBuilder(TransactionSale::class) - ->disableOriginalConstructor() - ->getMock(); - $this->objectManager->addSharedInstance($this->client, TransactionSale::class); - } - - /** - * @inheritdoc - */ - protected function tearDown() - { - $this->objectManager->removeSharedInstance(TransactionSale::class); - parent::tearDown(); - } - - /** - * Checks a case when payment method triggers an error during place order flow and - * error messages from payment gateway should be mapped. - * Error messages might be specific for different areas. - * - * @magentoAppIsolation enabled - * @magentoDataFixture Magento/Checkout/_files/quote_with_shipping_method.php - * @magentoConfigFixture current_store payment/braintree/active 1 - * @dataProvider getErrorPerAreaDataProvider - * @expectedException \Magento\Framework\Exception\CouldNotSaveException - * @param string $area - * @param array $testErrorCodes - * @param string $expectedOutput - * @throws \Magento\Framework\Exception\LocalizedException - */ - public function testSavePaymentInformationAndPlaceOrderWithErrors( - string $area, - array $testErrorCodes, - string $expectedOutput - ) { - /** @var State $state */ - $state = $this->objectManager->get(State::class); - $state->setAreaCode($area); - - $quote = $this->getQuote('test_order_1'); - - /** @var PaymentInterface $payment */ - $payment = $this->objectManager->create(PaymentInterface::class); - $payment->setMethod(ConfigProvider::CODE); - - $errors = ['errors' => []]; - - foreach ($testErrorCodes as $testErrorCode) { - array_push($errors['errors'], ['code' => $testErrorCode]); - } - - $response = new Error(['errors' => $errors, 'transaction' => ['status' => 'declined']]); - - $this->client->method('placeRequest') - ->willReturn(['object' => $response]); - - $this->expectExceptionMessage($expectedOutput); - - /** @var PaymentInformationManagementInterface $paymentInformationManagement */ - $paymentInformationManagement = $this->objectManager->get(PaymentInformationManagementInterface::class); - $paymentInformationManagement->savePaymentInformationAndPlaceOrder( - $quote->getId(), - $payment - ); - } - - /** - * Gets list of areas with specific error messages. - * - * @return array - */ - public function getErrorPerAreaDataProvider() - { - $testErrorGlobal = ['code' => 81802, 'message' => 'Company is too long.']; - $testErrorAdmin = ['code' => 91511, 'message' => 'Customer does not have any credit cards.']; - $testErrorFake = ['code' => 'fake_code', 'message' => 'Error message should not be mapped.']; - - return [ - [ - Area::AREA_FRONTEND, - [$testErrorAdmin['code'], $testErrorFake['code']], - 'Transaction has been declined. Please try again later.' - ], [ - Area::AREA_FRONTEND, - [$testErrorGlobal['code'], $testErrorAdmin['code'], $testErrorFake['code']], - $testErrorGlobal['message'] - ], [ - Area::AREA_ADMINHTML, - [$testErrorGlobal['code'], $testErrorAdmin['code'], $testErrorFake['code']], - $testErrorGlobal['message'] . PHP_EOL . $testErrorAdmin['message'] - ], - ]; - } - - /** - * Retrieves quote by provided order ID. - * - * @param string $reservedOrderId - * @return CartInterface - */ - private function getQuote(string $reservedOrderId): CartInterface - { - /** @var SearchCriteriaBuilder $searchCriteriaBuilder */ - $searchCriteriaBuilder = $this->objectManager->get(SearchCriteriaBuilder::class); - $searchCriteria = $searchCriteriaBuilder->addFilter('reserved_order_id', $reservedOrderId) - ->create(); - - /** @var CartRepositoryInterface $quoteRepository */ - $quoteRepository = $this->objectManager->get(CartRepositoryInterface::class); - $items = $quoteRepository->getList($searchCriteria) - ->getItems(); - - return array_pop($items); - } -} diff --git a/dev/tests/integration/testsuite/Magento/Checkout/Api/ShippingInformationManagementTest.php b/dev/tests/integration/testsuite/Magento/Checkout/Api/ShippingInformationManagementTest.php index 7440fb7fd3d98..a0ccc5014bc19 100644 --- a/dev/tests/integration/testsuite/Magento/Checkout/Api/ShippingInformationManagementTest.php +++ b/dev/tests/integration/testsuite/Magento/Checkout/Api/ShippingInformationManagementTest.php @@ -13,6 +13,7 @@ use Magento\Quote\Api\CartRepositoryInterface; use Magento\Quote\Api\Data\ShippingAssignmentInterface; use Magento\TestFramework\Helper\Bootstrap; +use Magento\Framework\Exception\InputException; use PHPUnit\Framework\TestCase; /** @@ -40,6 +41,9 @@ class ShippingInformationManagementTest extends TestCase */ private $shippingFactory; + /** + * @inheritdoc + */ protected function setUp() { $objectManager = Bootstrap::getObjectManager(); @@ -58,10 +62,8 @@ protected function setUp() * @magentoDataFixture Magento/Sales/_files/quote_with_customer.php * @magentoDataFixture Magento/Customer/_files/customer_with_addresses.php * @dataProvider getAddressesVariation - * @expectedException \Magento\Framework\Exception\InputException - * @expectedExceptionMessage The shipping information was unable to be saved. Verify the input data and try again. */ - public function testDifferentAddresses(bool $swapShipping) + public function testDifferentAddresses(bool $swapShipping): void { $cart = $this->cartRepo->getForCustomer(1); $otherCustomer = $this->customerRepo->get('customer_with_addresses@test.com'); @@ -86,6 +88,14 @@ public function testDifferentAddresses(bool $swapShipping) $shippingInformation->setBillingAddress($billingAddress); $shippingInformation->setShippingAddress($shippingAddress); $shippingInformation->setShippingMethodCode('flatrate'); + + $this->expectExceptionMessage( + sprintf( + 'The shipping information was unable to be saved. Error: "Invalid customer address id %s"', + $address->getCustomerAddressId() + ) + ); + $this->expectException(InputException::class); $this->management->saveAddressInformation($cart->getId(), $shippingInformation); } diff --git a/dev/tests/integration/testsuite/Magento/Cms/Controller/Adminhtml/Wysiwyg/Images/DeleteFilesTest.php b/dev/tests/integration/testsuite/Magento/Cms/Controller/Adminhtml/Wysiwyg/Images/DeleteFilesTest.php index 1fc07d32c77b9..15bd56f86486c 100644 --- a/dev/tests/integration/testsuite/Magento/Cms/Controller/Adminhtml/Wysiwyg/Images/DeleteFilesTest.php +++ b/dev/tests/integration/testsuite/Magento/Cms/Controller/Adminhtml/Wysiwyg/Images/DeleteFilesTest.php @@ -10,6 +10,8 @@ /** * Test for \Magento\Cms\Controller\Adminhtml\Wysiwyg\Images\DeleteFiles class. + * + * @magentoAppArea adminhtml */ class DeleteFilesTest extends \PHPUnit\Framework\TestCase { @@ -75,22 +77,47 @@ protected function setUp() * Execute method with correct directory path and file name to check that files under WYSIWYG media directory * can be removed. * + * @param string $filename * @return void + * @dataProvider executeDataProvider */ - public function testExecute() + public function testExecute(string $filename) { + $filePath = $this->fullDirectoryPath . DIRECTORY_SEPARATOR . $filename; + $fixtureDir = realpath(__DIR__ . '/../../../../../Catalog/_files'); + copy($fixtureDir . '/' . $this->fileName, $filePath); + $this->model->getRequest()->setMethod('POST') - ->setPostValue('files', [$this->imagesHelper->idEncode($this->fileName)]); + ->setPostValue('files', [$this->imagesHelper->idEncode($filename)]); $this->model->getStorage()->getSession()->setCurrentPath($this->fullDirectoryPath); $this->model->execute(); $this->assertFalse( $this->mediaDirectory->isExist( - $this->mediaDirectory->getRelativePath($this->fullDirectoryPath . '/' . $this->fileName) + $this->mediaDirectory->getRelativePath($this->fullDirectoryPath . '/' . $filename) ) ); } + /** + * DataProvider for testExecute + * + * @return array + */ + public function executeDataProvider(): array + { + return [ + ['name with spaces.jpg'], + ['name with, comma.jpg'], + ['name with* asterisk.jpg'], + ['name with[ bracket.jpg'], + ['magento_small_image.jpg'], + ['_.jpg'], + [' - .jpg'], + ['-.jpg'], + ]; + } + /** * Check that htaccess file couldn't be removed via * \Magento\Cms\Controller\Adminhtml\Wysiwyg\Images\DeleteFiles::execute method diff --git a/dev/tests/integration/testsuite/Magento/Cms/Controller/Adminhtml/Wysiwyg/Images/DeleteFolderTest.php b/dev/tests/integration/testsuite/Magento/Cms/Controller/Adminhtml/Wysiwyg/Images/DeleteFolderTest.php index af495841b9672..94638f3a71537 100644 --- a/dev/tests/integration/testsuite/Magento/Cms/Controller/Adminhtml/Wysiwyg/Images/DeleteFolderTest.php +++ b/dev/tests/integration/testsuite/Magento/Cms/Controller/Adminhtml/Wysiwyg/Images/DeleteFolderTest.php @@ -11,6 +11,8 @@ /** * Test for \Magento\Cms\Controller\Adminhtml\Wysiwyg\Images\DeleteFolder class. + * + * @magentoAppArea adminhtml */ class DeleteFolderTest extends \PHPUnit\Framework\TestCase { diff --git a/dev/tests/integration/testsuite/Magento/Cms/Controller/Adminhtml/Wysiwyg/Images/NewFolderTest.php b/dev/tests/integration/testsuite/Magento/Cms/Controller/Adminhtml/Wysiwyg/Images/NewFolderTest.php index e509737a0020f..4a572f8cdf228 100644 --- a/dev/tests/integration/testsuite/Magento/Cms/Controller/Adminhtml/Wysiwyg/Images/NewFolderTest.php +++ b/dev/tests/integration/testsuite/Magento/Cms/Controller/Adminhtml/Wysiwyg/Images/NewFolderTest.php @@ -10,6 +10,8 @@ /** * Test for \Magento\Cms\Controller\Adminhtml\Wysiwyg\Images\NewFolder class. + * + * @magentoAppArea adminhtml */ class NewFolderTest extends \PHPUnit\Framework\TestCase { diff --git a/dev/tests/integration/testsuite/Magento/Cms/Controller/Adminhtml/Wysiwyg/Images/UploadTest.php b/dev/tests/integration/testsuite/Magento/Cms/Controller/Adminhtml/Wysiwyg/Images/UploadTest.php index 9303a5eac7868..df8994c1a5a91 100644 --- a/dev/tests/integration/testsuite/Magento/Cms/Controller/Adminhtml/Wysiwyg/Images/UploadTest.php +++ b/dev/tests/integration/testsuite/Magento/Cms/Controller/Adminhtml/Wysiwyg/Images/UploadTest.php @@ -15,6 +15,8 @@ /** * Test for \Magento\Cms\Controller\Adminhtml\Wysiwyg\Images\Upload class. + * + * @magentoAppArea adminhtml */ class UploadTest extends \PHPUnit\Framework\TestCase { diff --git a/dev/tests/integration/testsuite/Magento/Cms/Controller/PageTest.php b/dev/tests/integration/testsuite/Magento/Cms/Controller/PageTest.php index 4600cd28fd3fc..d80644caca086 100644 --- a/dev/tests/integration/testsuite/Magento/Cms/Controller/PageTest.php +++ b/dev/tests/integration/testsuite/Magento/Cms/Controller/PageTest.php @@ -85,4 +85,20 @@ public function testCustomHandles(): void $handles = $layout->getUpdate()->getHandles(); $this->assertContains('cms_page_view_selectable_test_custom_layout_page_3_test_selected', $handles); } + + /** + * Check home page custom handle is applied when rendering a page. + * + * @return void + * @throws \Throwable + * @magentoDataFixture Magento/Cms/_files/home_with_custom_handle.php + */ + public function testHomePageCustomHandles(): void + { + $this->dispatch('/'); + /** @var LayoutInterface $layout */ + $layout = Bootstrap::getObjectManager()->get(LayoutInterface::class); + $handles = $layout->getUpdate()->getHandles(); + $this->assertContains('cms_page_view_selectable_home_page_custom_layout', $handles); + } } diff --git a/dev/tests/integration/testsuite/Magento/Cms/Model/Page/CustomLayoutManagerTest.php b/dev/tests/integration/testsuite/Magento/Cms/Model/Page/CustomLayoutManagerTest.php index e741b95ff4371..6aa3dcd1c34f9 100644 --- a/dev/tests/integration/testsuite/Magento/Cms/Model/Page/CustomLayoutManagerTest.php +++ b/dev/tests/integration/testsuite/Magento/Cms/Model/Page/CustomLayoutManagerTest.php @@ -97,6 +97,9 @@ public function testCustomLayoutUpdate(): void $result = $this->resultFactory->create(); $this->manager->applyUpdate($result, $this->repo->getFor($pageId)); $this->identityMap->remove((int)$page->getId()); - $this->assertContains('___selectable_page100_select2', $result->getLayout()->getUpdate()->getHandles()); + $this->assertContains( + 'cms_page_view_selectable_page100_select2', + $result->getLayout()->getUpdate()->getHandles() + ); } } diff --git a/dev/tests/integration/testsuite/Magento/Cms/Model/Wysiwyg/Images/StorageTest.php b/dev/tests/integration/testsuite/Magento/Cms/Model/Wysiwyg/Images/StorageTest.php index 5d256f2234a53..c77646ca5be1c 100644 --- a/dev/tests/integration/testsuite/Magento/Cms/Model/Wysiwyg/Images/StorageTest.php +++ b/dev/tests/integration/testsuite/Magento/Cms/Model/Wysiwyg/Images/StorageTest.php @@ -9,9 +9,12 @@ use Magento\Framework\App\Filesystem\DirectoryList; /** + * Test methods of class Storage * * @SuppressWarnings(PHPMD.LongVariable) * @SuppressWarnings(PHPMD.CouplingBetweenObjects) + * @SuppressWarnings(PHPMD.CouplingBetweenObjects) + * @SuppressWarnings(PHPMD.TooManyPublicMethods) */ class StorageTest extends \PHPUnit\Framework\TestCase { @@ -260,4 +263,87 @@ public function testUploadFileWithWrongFile(): void $this->assertFalse(is_file(self::$_baseDir . DIRECTORY_SEPARATOR . $fileName)); // phpcs:enable } + + /** + * Test that getThumbnailUrl() returns correct URL for root folder or sub-folders images + * + * @param string $directory + * @param string $filename + * @param string $expectedUrl + * @return void + * @magentoAppIsolation enabled + * @magentoAppArea adminhtml + * @dataProvider getThumbnailUrlDataProvider + */ + public function testGetThumbnailUrl(string $directory, string $filename, string $expectedUrl): void + { + $root = $this->storage->getCmsWysiwygImages()->getStorageRoot(); + $directory = implode('/', array_filter([rtrim($root, '/'), trim($directory, '/')])); + $path = $directory . '/' . $filename; + $this->generateImage($path); + $this->storage->resizeFile($path); + $collection = $this->storage->getFilesCollection($directory, 'image'); + $paths = []; + foreach ($collection as $item) { + $paths[] = parse_url($item->getThumbUrl(), PHP_URL_PATH); + } + $this->assertEquals([$expectedUrl], $paths); + $this->storage->deleteFile($path); + } + + /** + * Provide scenarios for testing getThumbnailUrl() + * + * @return array + */ + public function getThumbnailUrlDataProvider(): array + { + return [ + [ + '/', + 'image1.png', + '/pub/media/.thumbs/image1.png' + ], + [ + '/cms', + 'image2.png', + '/pub/media/.thumbscms/image2.png' + ], + [ + '/cms/pages', + 'image3.png', + '/pub/media/.thumbscms/pages/image3.png' + ] + ]; + } + + /** + * Generate a dummy image of the given width and height. + * + * @param string $path + * @param int $width + * @param int $height + * @return string + */ + private function generateImage(string $path, int $width = 1024, int $height = 768) + { + $dir = dirname($path); + if (!file_exists($dir)) { + mkdir($dir, 0777, true); + } + $file = fopen($path, 'wb'); + $filename = basename($path); + ob_start(); + $image = imagecreatetruecolor($width, $height); + switch (substr($filename, strrpos($filename, '.'))) { + case '.jpeg': + imagejpeg($image); + break; + case '.png': + imagepng($image); + break; + } + fwrite($file, ob_get_clean()); + return $path; + } } diff --git a/dev/tests/integration/testsuite/Magento/Cms/_files/home_with_custom_handle.php b/dev/tests/integration/testsuite/Magento/Cms/_files/home_with_custom_handle.php new file mode 100644 index 0000000000000..2556e0318222d --- /dev/null +++ b/dev/tests/integration/testsuite/Magento/Cms/_files/home_with_custom_handle.php @@ -0,0 +1,30 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +declare(strict_types=1); + +use Magento\Cms\Model\Page as PageModel; +use Magento\Cms\Model\PageFactory as PageModelFactory; +use Magento\TestFramework\Cms\Model\CustomLayoutManager; +use Magento\TestFramework\Helper\Bootstrap; + +$objectManager = Bootstrap::getObjectManager(); + +/** @var PageModelFactory $pageFactory */ +$pageFactory = $objectManager->get(PageModelFactory::class); +/** @var CustomLayoutManager $fakeManager */ +$fakeManager = $objectManager->get(CustomLayoutManager::class); +$layoutRepo = $objectManager->create(PageModel\CustomLayoutRepositoryInterface::class, ['manager' => $fakeManager]); + +$customLayoutName = 'page_custom_layout'; + +/** @var PageModel $page */ +$page = $pageFactory->create(['customLayoutRepository' => $layoutRepo]); +$page->load('home'); +$cmsPageId = (int)$page->getId(); + +$fakeManager->fakeAvailableFiles($cmsPageId, [$customLayoutName]); +$page->setData('layout_update_selected', $customLayoutName); +$page->save(); diff --git a/dev/tests/integration/testsuite/Magento/Cms/_files/home_with_custom_handle_rollback.php b/dev/tests/integration/testsuite/Magento/Cms/_files/home_with_custom_handle_rollback.php new file mode 100644 index 0000000000000..3000e6f85d322 --- /dev/null +++ b/dev/tests/integration/testsuite/Magento/Cms/_files/home_with_custom_handle_rollback.php @@ -0,0 +1,21 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +declare(strict_types=1); + +use Magento\Cms\Api\PageRepositoryInterface; +use Magento\Cms\Model\Page\CustomLayout\CustomLayoutRepository; +use Magento\TestFramework\Helper\Bootstrap; + +$objectManager = Bootstrap::getObjectManager(); + +/** @var PageRepositoryInterface $pageRepository */ +$pageRepository = $objectManager->get(PageRepositoryInterface::class); +$cmsPage = $pageRepository->getById('home'); +$cmsPageId = (int)$cmsPage->getId(); + +/** @var CustomLayoutRepository $customLayoutRepository */ +$customLayoutRepository = $objectManager->get(CustomLayoutRepository::class); +$customLayoutRepository->deleteFor($cmsPageId); diff --git a/dev/tests/integration/testsuite/Magento/ConfigurableProduct/Block/Product/ProductList/RelatedTest.php b/dev/tests/integration/testsuite/Magento/ConfigurableProduct/Block/Product/ProductList/RelatedTest.php new file mode 100644 index 0000000000000..8543b2600138b --- /dev/null +++ b/dev/tests/integration/testsuite/Magento/ConfigurableProduct/Block/Product/ProductList/RelatedTest.php @@ -0,0 +1,114 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +declare(strict_types=1); + +namespace Magento\ConfigurableProduct\Block\Product\ProductList; + +use Magento\Catalog\Api\Data\ProductInterface; +use Magento\Catalog\Block\Product\ProductList\AbstractLinksTest; +use Magento\Catalog\Block\Product\ProductList\Related; + +/** + * Check the correct behavior of related products on the configurable product view page + + * @magentoDbIsolation disabled + * @magentoAppArea frontend + */ +class RelatedTest extends AbstractLinksTest +{ + /** + * @var Related + */ + protected $block; + + /** + * @inheritdoc + */ + protected function setUp() + { + parent::setUp(); + + $this->block = $this->layout->createBlock(Related::class); + $this->linkType = 'related'; + } + + /** + * @magentoDataFixture Magento/Catalog/_files/second_product_simple.php + * @magentoDataFixture Magento/ConfigurableProduct/_files/product_configurable.php + * @return void + */ + public function testRenderConfigurableWithLinkedProduct(): void + { + $this->linkProducts('configurable', ['simple2' => ['position' => 1]]); + $relatedProduct = $this->productRepository->get('simple2'); + $this->block->setProduct($this->productRepository->get('configurable')); + $this->prepareBlock(); + $html = $this->block->toHtml(); + $this->assertNotEmpty($html); + $this->assertContains($relatedProduct->getName(), $html); + $this->assertCount(1, $this->block->getItems()); + } + + /** + * @magentoDataFixture Magento/Catalog/_files/second_product_simple.php + * @magentoDataFixture Magento/ConfigurableProduct/_files/product_configurable.php + * @return void + */ + public function testRenderConfigurableWithLinkedProductOnChild(): void + { + $this->linkProducts('simple_10', ['simple2' => ['position' => 1]]); + $relatedProduct = $this->productRepository->get('simple2'); + $this->block->setProduct($this->productRepository->get('configurable')); + $this->prepareBlock(); + $html = $this->block->toHtml(); + $this->assertNotEmpty($html); + $this->assertNotContains($relatedProduct->getName(), $html); + $this->assertEmpty($this->block->getItems()); + } + + /** + * @magentoDataFixture Magento/Catalog/_files/products_list.php + * @magentoDataFixture Magento/ConfigurableProduct/_files/product_configurable.php + * @return void + */ + public function testLinkedProductsPosition(): void + { + $this->linkProducts( + 'configurable', + ['wrong-simple' => ['position' => 3], 'simple-249' => ['position' => 2], 'simple-156' => ['position' => 1]] + ); + $this->block->setProduct($this->productRepository->get('configurable')); + $this->assertEquals( + ['simple-156', 'simple-249','wrong-simple'], + $this->getActualLinks($this->getLinkedItems()), + 'Expected linked products do not match actual linked products!' + ); + } + + /** + * @magentoDataFixture Magento/Catalog/_files/second_product_simple.php + * @magentoDataFixture Magento/ConfigurableProduct/_files/product_configurable.php + * @return void + */ + public function testGetIdentities(): void + { + $this->linkProducts('configurable', ['simple2' => ['position = 1']]); + $relatedProduct = $this->productRepository->get('simple2'); + $this->block->setProduct($this->productRepository->get('configurable')); + $this->prepareBlock(); + $this->assertEquals(['cat_p_' . $relatedProduct->getId(), 'cat_p'], $this->block->getIdentities()); + } + + /** + * Returns linked products from block. + * + * @return ProductInterface[] + */ + protected function getLinkedItems(): array + { + return $this->block->getItems()->getItems(); + } +} diff --git a/dev/tests/integration/testsuite/Magento/ConfigurableProduct/Block/Product/ProductList/UpsellTest.php b/dev/tests/integration/testsuite/Magento/ConfigurableProduct/Block/Product/ProductList/UpsellTest.php new file mode 100644 index 0000000000000..ad24b84533c79 --- /dev/null +++ b/dev/tests/integration/testsuite/Magento/ConfigurableProduct/Block/Product/ProductList/UpsellTest.php @@ -0,0 +1,43 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +declare(strict_types=1); + +namespace Magento\ConfigurableProduct\Block\Product\ProductList; + +use Magento\Catalog\Block\Product\ProductList\Upsell; + +/** + * Check the correct behavior of up-sell products on the configurable product view page + * + * @magentoDbIsolation disabled + * @magentoAppArea frontend + */ +class UpsellTest extends RelatedTest +{ + /** + * @var Upsell + */ + protected $block; + + /** + * @inheritdoc + */ + protected function setUp() + { + parent::setUp(); + + $this->block = $this->layout->createBlock(Upsell::class); + $this->linkType = 'upsell'; + } + + /** + * @inheritdoc + */ + protected function getLinkedItems(): array + { + return $this->block->getItems(); + } +} diff --git a/dev/tests/integration/testsuite/Magento/ConfigurableProduct/Block/Product/View/CustomOptions/RenderOptionsTest.php b/dev/tests/integration/testsuite/Magento/ConfigurableProduct/Block/Product/View/CustomOptions/RenderOptionsTest.php new file mode 100644 index 0000000000000..55f8b91f07093 --- /dev/null +++ b/dev/tests/integration/testsuite/Magento/ConfigurableProduct/Block/Product/View/CustomOptions/RenderOptionsTest.php @@ -0,0 +1,96 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +declare(strict_types=1); + +namespace Magento\ConfigurableProduct\Block\Product\View\CustomOptions; + +use Magento\Catalog\Block\Product\View\Options\AbstractRenderCustomOptionsTest; + +/** + * Test cases related to check that configurable product custom option renders as expected. + * + * @magentoDbIsolation disabled + * @magentoAppArea frontend + */ +class RenderOptionsTest extends AbstractRenderCustomOptionsTest +{ + /** + * Check that options from text group(field, area) render on configurable product as expected. + * + * @magentoDataFixture Magento/ConfigurableProduct/_files/configurable_product_with_two_child_products.php + * @dataProvider \Magento\TestFramework\ConfigurableProduct\Block\CustomOptions\TextGroupDataProvider::getData + * + * @param array $optionData + * @param array $checkArray + * @return void + */ + public function testRenderCustomOptionsFromTextGroup(array $optionData, array $checkArray): void + { + $this->assertTextOptionRenderingOnProduct('Configurable product', $optionData, $checkArray); + } + + /** + * Check that options from file group(file) render on configurable product as expected. + * + * @magentoDataFixture Magento/ConfigurableProduct/_files/configurable_product_with_two_child_products.php + * @dataProvider \Magento\TestFramework\ConfigurableProduct\Block\CustomOptions\FileGroupDataProvider::getData + * + * @param array $optionData + * @param array $checkArray + * @return void + */ + public function testRenderCustomOptionsFromFileGroup(array $optionData, array $checkArray): void + { + $this->assertFileOptionRenderingOnProduct('Configurable product', $optionData, $checkArray); + } + + /** + * Check that options from select group(drop-down, radio buttons, checkbox, multiple select) render + * on configurable product as expected. + * + * @magentoDataFixture Magento/ConfigurableProduct/_files/configurable_product_with_two_child_products.php + * @dataProvider \Magento\TestFramework\ConfigurableProduct\Block\CustomOptions\SelectGroupDataProvider::getData + * + * @param array $optionData + * @param array $optionValueData + * @param array $checkArray + * @return void + */ + public function testRenderCustomOptionsFromSelectGroup( + array $optionData, + array $optionValueData, + array $checkArray + ): void { + $this->assertSelectOptionRenderingOnProduct('Configurable product', $optionData, $optionValueData, $checkArray); + } + + /** + * Check that options from date group(date, date & time, time) render on configurable product as expected. + * + * @magentoDataFixture Magento/ConfigurableProduct/_files/configurable_product_with_two_child_products.php + * @dataProvider \Magento\TestFramework\ConfigurableProduct\Block\CustomOptions\DateGroupDataProvider::getData + * + * @param array $optionData + * @param array $checkArray + * @return void + */ + public function testRenderCustomOptionsFromDateGroup(array $optionData, array $checkArray): void + { + $this->assertDateOptionRenderingOnProduct('Configurable product', $optionData, $checkArray); + } + + /** + * @inheritdoc + */ + protected function getHandlesList(): array + { + return [ + 'default', + 'catalog_product_view', + 'catalog_product_view_type_configurable', + ]; + } +} diff --git a/dev/tests/integration/testsuite/Magento/ConfigurableProduct/Block/Product/View/Type/ConfigurableProductPriceTest.php b/dev/tests/integration/testsuite/Magento/ConfigurableProduct/Block/Product/View/Type/ConfigurableProductPriceTest.php new file mode 100644 index 0000000000000..977a130eff838 --- /dev/null +++ b/dev/tests/integration/testsuite/Magento/ConfigurableProduct/Block/Product/View/Type/ConfigurableProductPriceTest.php @@ -0,0 +1,211 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +declare(strict_types=1); + +namespace Magento\ConfigurableProduct\Block\Product\View\Type; + +use Magento\Catalog\Api\Data\ProductCustomOptionInterface; +use Magento\Catalog\Api\Data\ProductInterface; +use Magento\Catalog\Api\ProductRepositoryInterface; +use Magento\Framework\ObjectManagerInterface; +use Magento\Framework\Registry; +use Magento\Framework\Serialize\SerializerInterface; +use Magento\Framework\View\Result\Page; +use Magento\TestFramework\Helper\Bootstrap; +use PHPUnit\Framework\TestCase; + +/** + * Check configurable product price displaying + * + * @magentoDbIsolation enabled + * @magentoAppIsolation enabled + * @magentoAppArea frontend + */ +class ConfigurableProductPriceTest extends TestCase +{ + /** @var ObjectManagerInterface */ + private $objectManager; + + /** @var Registry */ + private $registry; + + /** @var ProductRepositoryInterface */ + private $productRepository; + + /** @var Page */ + private $page; + + /** @var ProductCustomOptionInterface */ + private $productCustomOption; + + /** @var SerializerInterface */ + private $json; + + /** + * @inheritdoc + */ + protected function setUp() + { + parent::setUp(); + + $this->objectManager = Bootstrap::getObjectManager(); + $this->registry = $this->objectManager->get(Registry::class); + $this->page = $this->objectManager->get(Page::class); + $this->productRepository = $this->objectManager->get(ProductRepositoryInterface::class); + $this->productRepository->cleanCache(); + $this->productCustomOption = $this->objectManager->get(ProductCustomOptionInterface::class); + $this->json = $this->objectManager->get(SerializerInterface::class); + } + + /** + * @inheritdoc + */ + protected function tearDown() + { + $this->registry->unregister('product'); + $this->registry->unregister('current_product'); + + parent::tearDown(); + } + + /** + * @magentoDataFixture Magento/ConfigurableProduct/_files/product_configurable.php + * + * @return void + */ + public function testConfigurablePrice(): void + { + $this->assertPrice($this->processPriceView('configurable'), 10.00); + } + + /** + * @magentoDataFixture Magento/ConfigurableProduct/_files/product_configurable_disable_first_child.php + * + * @return void + */ + public function testConfigurablePriceWithDisabledFirstChild(): void + { + $this->assertPrice($this->processPriceView('configurable'), 20.00); + } + + /** + * @magentoDataFixture Magento/ConfigurableProduct/_files/product_configurable_zero_qty_first_child.php + * + * @return void + */ + public function testConfigurablePriceWithOutOfStockFirstChild(): void + { + $this->assertPrice($this->processPriceView('configurable'), 20.00); + } + + /** + * @magentoDataFixture Magento/ConfigurableProduct/_files/product_configurable.php + * @magentoDataFixture Magento/CatalogRule/_files/rule_apply_as_percentage_of_original_not_logged_user.php + * @magentoDbIsolation disabled + * + * @return void + */ + public function testConfigurablePriceWithCatalogRule(): void + { + $this->assertPrice($this->processPriceView('configurable'), 9.00); + } + + /** + * @magentoDataFixture Magento/ConfigurableProduct/_files/product_configurable_with_custom_option_type_text.php + * + * @return void + */ + public function testConfigurablePriceWithCustomOption(): void + { + $product = $this->productRepository->get('configurable'); + $this->registerProduct($product); + $this->preparePageLayout(); + $customOptionsBlock = $this->page->getLayout() + ->getChildBlock('product.info.options.wrapper', 'product_options'); + $option = $product->getOptions()[0] ?? null; + $this->assertNotNull($option); + $this->assertJsonConfig($customOptionsBlock->getJsonConfig(), '15', (int)$option->getId()); + $optionBlock = $customOptionsBlock->getChildBlock($this->productCustomOption->getGroupByType('area')); + $optionPrice = $optionBlock->setProduct($product)->setOption($option)->getFormattedPrice(); + $this->assertEquals('+$15.00', preg_replace('/[\n\s]/', '', strip_tags($optionPrice))); + } + + /** + * Register the product. + * + * @param ProductInterface $product + * @return void + */ + private function registerProduct(ProductInterface $product): void + { + $this->registry->unregister('product'); + $this->registry->register('product', $product); + $this->registry->unregister('current_product'); + $this->registry->register('current_product', $product); + } + + /** + * Prepare configurable product page. + * + * @return void + */ + private function preparePageLayout(): void + { + $this->page->addHandle([ + 'default', + 'catalog_product_view', + 'catalog_product_view_type_configurable', + ]); + $this->page->getLayout()->generateXml(); + } + + /** + * Process view product final price block html. + * + * @param string $sku + * @return string + */ + private function processPriceView(string $sku): string + { + $product = $this->productRepository->get($sku); + $this->registerProduct($product); + $this->preparePageLayout(); + + return $this->page->getLayout()->getBlock('product.price.final')->toHtml(); + } + + /** + * Assert that html contain price label and expected final price amount. + * + * @param string $priceBlockHtml + * @param float $expectedPrice + * @return void + */ + private function assertPrice(string $priceBlockHtml, float $expectedPrice): void + { + $regexp = '/<span class="price-label">As low as<\/span>.*'; + $regexp .= '<span.*data-price-amount="%s".*<span class="price">\$%.2f<\/span><\/span>/'; + $this->assertRegExp( + sprintf($regexp, round($expectedPrice, 2), $expectedPrice), + preg_replace('/[\n\r]/', '', $priceBlockHtml) + ); + } + + /** + * Assert custom option price json config. + * + * @param string $config + * @param string $expectedPrice + * @param int $optionId + * @return void + */ + private function assertJsonConfig(string $config, string $expectedPrice, int $optionId): void + { + $price = $this->json->unserialize($config)[$optionId]['prices']['finalPrice']['amount'] ?? null; + $this->assertNotNull($price); + $this->assertEquals($expectedPrice, $price); + } +} diff --git a/dev/tests/integration/testsuite/Magento/ConfigurableProduct/Block/Product/View/Type/ConfigurableTest.php b/dev/tests/integration/testsuite/Magento/ConfigurableProduct/Block/Product/View/Type/ConfigurableTest.php index feca63015ca7c..ee1f4d25e88da 100644 --- a/dev/tests/integration/testsuite/Magento/ConfigurableProduct/Block/Product/View/Type/ConfigurableTest.php +++ b/dev/tests/integration/testsuite/Magento/ConfigurableProduct/Block/Product/View/Type/ConfigurableTest.php @@ -3,79 +3,120 @@ * Copyright © Magento, Inc. All rights reserved. * See COPYING.txt for license details. */ +declare(strict_types=1); + namespace Magento\ConfigurableProduct\Block\Product\View\Type; +use Magento\Catalog\Api\Data\ProductInterface; +use Magento\Catalog\Api\ProductRepositoryInterface; +use Magento\Catalog\Model\ResourceModel\Product as ProductResource; +use Magento\ConfigurableProduct\Model\ResourceModel\Product\Type\Configurable\Attribute\Collection; +use Magento\Framework\Api\SearchCriteriaBuilder; +use Magento\Framework\ObjectManagerInterface; +use Magento\Framework\Serialize\SerializerInterface; +use Magento\Framework\View\LayoutInterface; +use Magento\TestFramework\Helper\Bootstrap; +use PHPUnit\Framework\TestCase; + /** - * Test class for \Magento\ConfigurableProduct\Block\Product\View\Type\Configurable. + * Test class to check configurable product view behaviour. + * + * @see \Magento\ConfigurableProduct\Block\Product\View\Type\Configurable * * @magentoAppIsolation enabled + * @magentoDbIsolation enabled * @magentoDataFixture Magento/ConfigurableProduct/_files/product_configurable.php */ -class ConfigurableTest extends \PHPUnit\Framework\TestCase +class ConfigurableTest extends TestCase { /** - * @var \Magento\ConfigurableProduct\Block\Product\View\Type\Configurable + * @var ObjectManagerInterface + */ + private $objectManager; + + /** + * @var SerializerInterface + */ + private $serializer; + + /** + * @var SearchCriteriaBuilder + */ + private $searchBuilder; + + /** + * @var Configurable + */ + private $block; + + /** + * @var ProductRepositoryInterface */ - protected $_block; + private $productRepository; /** - * @var \Magento\Catalog\Model\Product + * @var ProductResource */ - protected $_product; + private $productResource; + /** + * @var ProductInterface + */ + private $product; + + /** + * @inheritdoc + */ protected function setUp() { - $this->_product = \Magento\TestFramework\Helper\Bootstrap::getObjectManager()->create( - \Magento\Catalog\Model\Product::class - ); - $this->_product->load(1); - $this->_block = \Magento\TestFramework\Helper\Bootstrap::getObjectManager()->get( - \Magento\Framework\View\LayoutInterface::class - )->createBlock( - \Magento\ConfigurableProduct\Block\Product\View\Type\Configurable::class - ); - $this->_block->setProduct($this->_product); + parent::setUp(); + $this->objectManager = Bootstrap::getObjectManager(); + $this->serializer = $this->objectManager->get(SerializerInterface::class); + $this->searchBuilder = $this->objectManager->get(SearchCriteriaBuilder::class); + $this->productRepository = $this->objectManager->get(ProductRepositoryInterface::class); + $this->productRepository->cleanCache(); + $this->productResource = $this->objectManager->create(ProductResource::class); + $this->product = $this->productRepository->get('configurable'); + $this->block = $this->objectManager->get(LayoutInterface::class)->createBlock(Configurable::class); + $this->block->setProduct($this->product); } /** - * @magentoAppIsolation enabled + * @return void */ - public function testGetAllowAttributes() + public function testGetAllowAttributes(): void { - $attributes = $this->_block->getAllowAttributes(); - $this->assertInstanceOf( - \Magento\ConfigurableProduct\Model\ResourceModel\Product\Type\Configurable\Attribute\Collection::class, - $attributes - ); + $attributes = $this->block->getAllowAttributes(); + $this->assertInstanceOf(Collection::class, $attributes); $this->assertGreaterThanOrEqual(1, $attributes->getSize()); } /** - * @magentoAppIsolation enabled + * @return void */ - public function testHasOptions() + public function testHasOptions(): void { - $this->assertTrue($this->_block->hasOptions()); + $this->assertTrue($this->block->hasOptions()); } /** - * @magentoAppIsolation enabled + * @return void */ - public function testGetAllowProducts() + public function testGetAllowProducts(): void { - $products = $this->_block->getAllowProducts(); + $products = $this->block->getAllowProducts(); $this->assertGreaterThanOrEqual(2, count($products)); foreach ($products as $product) { - $this->assertInstanceOf(\Magento\Catalog\Model\Product::class, $product); + $this->assertInstanceOf(ProductInterface::class, $product); } } /** - * @magentoAppIsolation enabled + * @return void */ - public function testGetJsonConfig() + public function testGetJsonConfig(): void { - $config = json_decode($this->_block->getJsonConfig(), true); + $config = $this->serializer->unserialize($this->block->getJsonConfig()); $this->assertNotEmpty($config); $this->assertArrayHasKey('productId', $config); $this->assertEquals(1, $config['productId']); @@ -83,5 +124,115 @@ public function testGetJsonConfig() $this->assertArrayHasKey('template', $config); $this->assertArrayHasKey('prices', $config); $this->assertArrayHasKey('basePrice', $config['prices']); + $this->assertArrayHasKey('images', $config); + $this->assertCount(0, $config['images']); + } + + /** + * @magentoDataFixture Magento/ConfigurableProduct/_files/configurable_product_with_child_products_with_images.php + * @return void + */ + public function testGetJsonConfigWithChildProductsImages(): void + { + $config = $this->serializer->unserialize($this->block->getJsonConfig()); + $this->assertNotEmpty($config); + $this->assertArrayHasKey('images', $config); + $this->assertCount(2, $config['images']); + $products = $this->getProducts( + $this->product->getExtensionAttributes()->getConfigurableProductLinks() + ); + $i = 0; + foreach ($products as $simpleProduct) { + $i++; + $resultImage = reset($config['images'][$simpleProduct->getId()]); + $this->assertContains($simpleProduct->getImage(), $resultImage['thumb']); + $this->assertContains($simpleProduct->getImage(), $resultImage['img']); + $this->assertContains($simpleProduct->getImage(), $resultImage['full']); + $this->assertTrue($resultImage['isMain']); + $this->assertEquals('image', $resultImage['type']); + $this->assertEquals($i, $resultImage['position']); + $this->assertNull($resultImage['videoUrl']); + } + } + + /** + * @dataProvider expectedDataProvider + * + * @param string $label + * @param array $expectedConfig + * @return void + */ + public function testConfigurableProductView(string $label, array $expectedConfig): void + { + $attributes = $this->block->decorateArray($this->block->getAllowAttributes()); + $this->assertCount(1, $attributes); + $attribute = $attributes->getFirstItem(); + $this->assertEquals($label, $attribute->getLabel()); + $config = $this->serializer->unserialize($this->block->getJsonConfig())['attributes'] ?? null; + $this->assertNotNull($config); + $this->assertConfig(reset($config), $expectedConfig); + } + + /** + * @return array + */ + public function expectedDataProvider(): array + { + return [ + [ + 'label' => 'Test Configurable', + 'config_data' => [ + 'label' => 'Test Configurable', + 'options' => [ + [ + 'label' => 'Option 1', + 'sku' => 'simple_10', + ], + [ + 'label' => 'Option 2', + 'sku' => 'simple_20', + ], + ], + ], + ], + ]; + } + + /** + * Assert that data was generated + * + * @param array $data + * @param array $expectedData + * @return void + */ + private function assertConfig(array $data, array $expectedData): void + { + $this->assertEquals($expectedData['label'], $data['label']); + $skus = array_column($expectedData['options'], 'sku'); + $idBySkuMap = $this->productResource->getProductsIdsBySkus($skus); + foreach ($expectedData['options'] as &$option) { + $sku = $option['sku']; + unset($option['sku']); + $option['products'] = [$idBySkuMap[$sku]]; + foreach ($data['options'] as $actualOption) { + if ($option['label'] === $actualOption['label']) { + unset($actualOption['id']); + $this->assertEquals($option, $actualOption); + } + } + } + } + + /** + * Returns products by ids list. + * + * @param array $productIds + * @return ProductInterface[] + */ + private function getProducts(array $productIds): array + { + $criteria = $this->searchBuilder->addFilter('entity_id', $productIds, 'in') + ->create(); + return $this->productRepository->getList($criteria)->getItems(); } } diff --git a/dev/tests/integration/testsuite/Magento/ConfigurableProduct/Block/Product/View/Type/ConfigurableViewOnCategoryPageTest.php b/dev/tests/integration/testsuite/Magento/ConfigurableProduct/Block/Product/View/Type/ConfigurableViewOnCategoryPageTest.php new file mode 100644 index 0000000000000..94aa958b44c26 --- /dev/null +++ b/dev/tests/integration/testsuite/Magento/ConfigurableProduct/Block/Product/View/Type/ConfigurableViewOnCategoryPageTest.php @@ -0,0 +1,83 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +declare(strict_types=1); + +namespace Magento\ConfigurableProduct\Block\Product\View\Type; + +use Magento\Catalog\Block\Product\ListProduct; +use Magento\Eav\Model\Entity\Collection\AbstractCollection; +use Magento\Framework\ObjectManagerInterface; +use Magento\Framework\View\LayoutInterface; +use Magento\TestFramework\Helper\Bootstrap; +use PHPUnit\Framework\TestCase; + +/** + * Class checks configurable product displaying on category view page + * + * @magentoDbIsolation disabled + * @magentoAppIsolation enabled + * @magentoAppArea frontend + * @magentoDataFixture Magento/ConfigurableProduct/_files/configurable_product_with_out_of_stock_children.php + */ +class ConfigurableViewOnCategoryPageTest extends TestCase +{ + /** @var ObjectManagerInterface */ + private $objectManager; + + /** @var LayoutInterface */ + private $layout; + + /** @var ListProduct $listingBlock */ + private $listingBlock; + + /** + * @inheritdoc + */ + protected function setUp() + { + parent::setUp(); + + $this->objectManager = Bootstrap::getObjectManager(); + $this->layout = $this->objectManager->get(LayoutInterface::class); + $this->listingBlock = $this->layout->createBlock(ListProduct::class); + $this->listingBlock->setCategoryId(333); + } + + /** + * @magentoConfigFixture current_store cataloginventory/options/show_out_of_stock 1 + * + * @return void + */ + public function testOutOfStockProductWithEnabledConfigView(): void + { + $collection = $this->listingBlock->getLoadedProductCollection(); + $this->assertCollectionSize(1, $collection); + } + + /** + * @magentoConfigFixture current_store cataloginventory/options/show_out_of_stock 0 + * + * @return void + */ + public function testOutOfStockProductWithDisabledConfigView(): void + { + $collection = $this->listingBlock->getLoadedProductCollection(); + $this->assertCollectionSize(0, $collection); + } + + /** + * Check collection size + * + * @param int $expectedSize + * @param AbstractCollection $collection + * @return void + */ + private function assertCollectionSize(int $expectedSize, AbstractCollection $collection): void + { + $this->assertEquals($expectedSize, $collection->getSize()); + $this->assertCount($expectedSize, $collection->getItems()); + } +} diff --git a/dev/tests/integration/testsuite/Magento/ConfigurableProduct/Block/Product/View/Type/ConfigurableViewOnProductPageTest.php b/dev/tests/integration/testsuite/Magento/ConfigurableProduct/Block/Product/View/Type/ConfigurableViewOnProductPageTest.php new file mode 100644 index 0000000000000..21ba9d2764b91 --- /dev/null +++ b/dev/tests/integration/testsuite/Magento/ConfigurableProduct/Block/Product/View/Type/ConfigurableViewOnProductPageTest.php @@ -0,0 +1,290 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +declare(strict_types=1); + +namespace Magento\ConfigurableProduct\Block\Product\View\Type; + +use Magento\Catalog\Api\Data\ProductInterface; +use Magento\Catalog\Api\ProductRepositoryInterface; +use Magento\Catalog\Model\Product\Attribute\Source\Status; +use Magento\Catalog\Model\ResourceModel\Product as ProductResource; +use Magento\CatalogInventory\Api\Data\StockStatusInterface; +use Magento\Framework\ObjectManagerInterface; +use Magento\Framework\Serialize\SerializerInterface; +use Magento\Framework\View\LayoutInterface; +use Magento\Store\Model\Store; +use Magento\Store\Model\StoreManagerInterface; +use Magento\TestFramework\Helper\Bootstrap; +use PHPUnit\Framework\TestCase; + +/** + * Class checks configurable product view with out of stock children + * + * @magentoAppArea frontend + * @magentoDataFixture Magento/ConfigurableProduct/_files/product_configurable.php + * @SuppressWarnings(PHPMD.CouplingBetweenObjects) + */ +class ConfigurableViewOnProductPageTest extends TestCase +{ + private const STOCK_DISPLAY_TEMPLATE = 'Magento_Catalog::product/view/type/default.phtml'; + + /** @var ObjectManagerInterface */ + private $objectManager; + + /** @var ProductRepositoryInterface */ + private $productRepository; + + /** @var LayoutInterface */ + private $layout; + + /** @var Configurable */ + private $block; + + /** @var SerializerInterface */ + private $json; + + /** @var ProductResource */ + private $productResource; + + /** @var StoreManagerInterface */ + private $storeManager; + + /** + * @inheritdoc + */ + protected function setUp() + { + parent::setUp(); + + $this->objectManager = Bootstrap::getObjectManager(); + $this->productRepository = $this->objectManager->get(ProductRepositoryInterface::class); + $this->productRepository->cleanCache(); + $this->layout = $this->objectManager->get(LayoutInterface::class); + $this->block = $this->layout->createBlock(Configurable::class); + $this->json = $this->objectManager->get(SerializerInterface::class); + $this->productResource = $this->objectManager->get(ProductResource::class); + $this->storeManager = $this->objectManager->get(StoreManagerInterface::class); + } + + /** + * @dataProvider oneChildNotVisibleDataProvider + * @magentoDbIsolation disabled + * + * @param string $sku + * @param array $data + * @param array $expectedData + * @return void + */ + public function testOneChildNotVisible(string $sku, array $data, array $expectedData): void + { + $configurableProduct = $this->prepareConfigurableProduct($sku, $data); + $result = $this->renderStockBlock($configurableProduct); + $this->performAsserts($result, $expectedData); + } + + /** + * @return array + */ + public function oneChildNotVisibleDataProvider(): array + { + return [ + 'one_child_out_of_stock' => [ + 'sku' => 'simple_10', + 'data' => [ + 'stock_data' => [ + 'use_config_manage_stock' => 1, + 'is_in_stock' => StockStatusInterface::STATUS_OUT_OF_STOCK, + ], + ], + 'expected_data' => [ + 'stock_status' => 'In stock', + 'options' => [ + [ + 'label' => 'Option 2', + 'product' => 'simple_20', + ], + ], + ], + ], + 'one_child_disabled' => [ + 'sku' => 'simple_10', + 'data' => [ + 'status' => Status::STATUS_DISABLED, + 'stock_data' => [ + 'use_config_manage_stock' => 1, + 'is_in_stock' => StockStatusInterface::STATUS_IN_STOCK, + ], + ], + 'expected_data' => [ + 'stock_status' => 'In stock', + 'options' => [ + [ + 'label' => 'Option 2', + 'product' => 'simple_20', + ], + ], + ], + ], + ]; + } + + /** + * @magentoConfigFixture current_store cataloginventory/options/show_out_of_stock 1 + * + * @dataProvider oneChildNotVisibleDataProviderWithEnabledConfig + * + * @param string $sku + * @param array $data + * @param array $expectedData + * @return void + */ + public function testOneChildNotVisibleWithEnabledShowOutOfStockProducts( + string $sku, + array $data, + array $expectedData + ): void { + $configurableProduct = $this->prepareConfigurableProduct($sku, $data); + $result = $this->renderStockBlock($configurableProduct); + $this->performAsserts($result, $expectedData); + } + + /** + * @return array + */ + public function oneChildNotVisibleDataProviderWithEnabledConfig(): array + { + return [ + 'one_child_out_of_stock' => [ + 'sku' => 'simple_10', + 'data' => [ + 'stock_data' => [ + 'use_config_manage_stock' => 1, + 'is_in_stock' => StockStatusInterface::STATUS_OUT_OF_STOCK, + ], + ], + 'expected_data' => [ + 'stock_status' => 'In stock', + 'options' => [ + [ + 'label' => 'Option 2', + 'product' => 'simple_20' + ], + [ + 'label' => 'Option 1', + 'product' => 'simple_10', + ], + ], + ], + ], + 'one_child_disabled' => [ + 'sku' => 'simple_10', + 'data' => [ + 'status' => Status::STATUS_DISABLED, + 'stock_data' => [ + 'use_config_manage_stock' => 1, + 'is_in_stock' => StockStatusInterface::STATUS_IN_STOCK, + ], + ], + 'expected_data' => [ + 'stock_status' => 'In stock', + 'options' => [ + [ + 'label' => 'Option 2', + 'product' => 'simple_20', + ], + ], + ], + ], + ]; + } + + /** + * Update product with data + * + * @param array $sku + * @param array $data + * @return void + */ + private function updateProduct(string $sku, array $data): void + { + $currentStore = $this->storeManager->getStore(); + try { + $this->storeManager->setCurrentStore(Store::DEFAULT_STORE_ID); + $product = $this->productRepository->get($sku); + $product->addData($data); + $this->productRepository->save($product); + } finally { + $this->storeManager->setCurrentStore($currentStore); + } + } + + /** + * Check attribute options + * + * @param array $actualData + * @param array $expectedData + * @return void + */ + private function assertConfig(array $actualData, array $expectedData): void + { + $this->assertCount(count($expectedData), $actualData['options_data'], 'Redundant options were loaded'); + $sku = array_column($expectedData, 'product'); + $idBySkuMapping = $this->productResource->getProductsIdsBySkus($sku); + foreach ($expectedData as $expectedOption) { + $expectedId = $idBySkuMapping[$expectedOption['product']]; + $itemToCheck = $actualData['options_data'][$expectedId] ?? null; + $this->assertNotNull($itemToCheck); + foreach ($actualData['attributes']['options'] as $actualAttributeDataItem) { + if ($actualAttributeDataItem['id'] === reset($itemToCheck)) { + $this->assertEquals($expectedOption['label'], $actualAttributeDataItem['label']); + } + } + } + } + + /** + * Render stock block + * + * @param ProductInterface $configurableProduct + * @return string + */ + private function renderStockBlock(ProductInterface $configurableProduct): string + { + $this->block->setProduct($configurableProduct); + $this->block->setTemplate(self::STOCK_DISPLAY_TEMPLATE); + + return $this->block->toHtml(); + } + + /** + * Perform test asserts + * + * @param string $result + * @param array $expectedData + * @return void + */ + private function performAsserts(string $result, array $expectedData): void + { + $this->assertEquals((string)__($expectedData['stock_status']), trim(strip_tags($result))); + $config = $this->json->unserialize($this->block->getJsonConfig()); + $dataToCheck = ['attributes' => reset($config['attributes']), 'options_data' => $config['index']]; + $this->assertConfig($dataToCheck, $expectedData['options']); + } + + /** + * Prepare configurable product with children to test + * + * @param string $sku + * @param array $data + * @return ProductInterface + */ + private function prepareConfigurableProduct(string $sku, array $data): ProductInterface + { + $this->updateProduct($sku, $data); + + return $this->productRepository->get('configurable', false, null, true); + } +} diff --git a/dev/tests/integration/testsuite/Magento/ConfigurableProduct/Block/Product/View/Type/MultiStoreConfigurableViewOnProductPageTest.php b/dev/tests/integration/testsuite/Magento/ConfigurableProduct/Block/Product/View/Type/MultiStoreConfigurableViewOnProductPageTest.php new file mode 100644 index 0000000000000..c1adf0ef1d2be --- /dev/null +++ b/dev/tests/integration/testsuite/Magento/ConfigurableProduct/Block/Product/View/Type/MultiStoreConfigurableViewOnProductPageTest.php @@ -0,0 +1,255 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +declare(strict_types=1); + +namespace Magento\ConfigurableProduct\Block\Product\View\Type; + +use Magento\Catalog\Api\Data\ProductInterface; +use Magento\Catalog\Api\ProductRepositoryInterface; +use Magento\Catalog\Model\Product\Attribute\Source\Status; +use Magento\Catalog\Model\ResourceModel\Product as ProductResource; +use Magento\Framework\ObjectManagerInterface; +use Magento\Framework\Serialize\SerializerInterface; +use Magento\Framework\View\LayoutInterface; +use Magento\Store\Model\StoreManagerInterface; +use Magento\TestFramework\Helper\Bootstrap; +use Magento\TestFramework\Store\ExecuteInStoreContext; +use PHPUnit\Framework\TestCase; + +/** + * Class check configurable product options displaying per stores + * + * @magentoDbIsolation disabled + */ +class MultiStoreConfigurableViewOnProductPageTest extends TestCase +{ + /** @var ObjectManagerInterface */ + private $objectManager; + + /** @var ProductRepositoryInterface */ + private $productRepository; + + /** @var StoreManagerInterface */ + private $storeManager; + + /** @var LayoutInterface */ + private $layout; + + /** @var SerializerInterface */ + private $serializer; + + /** @var ProductResource */ + private $productResource; + + /** @var ExecuteInStoreContext */ + private $executeInStoreContext; + + /** + * @inheritdoc + */ + protected function setUp() + { + parent::setUp(); + + $this->objectManager = Bootstrap::getObjectManager(); + $this->productRepository = $this->objectManager->get(ProductRepositoryInterface::class); + $this->productRepository->cleanCache(); + $this->storeManager = $this->objectManager->get(StoreManagerInterface::class); + $this->layout = $this->objectManager->get(LayoutInterface::class); + $this->serializer = $this->objectManager->get(SerializerInterface::class); + $this->productResource = $this->objectManager->get(ProductResource::class); + $this->executeInStoreContext = $this->objectManager->get(ExecuteInStoreContext::class); + } + + /** + * @magentoDataFixture Magento/ConfigurableProduct/_files/configurable_product_different_option_labeles_per_stores.php + * + * @dataProvider expectedLabelsDataProvider + * + * @param array $expectedStoreData + * @param array $expectedSecondStoreData + * @return void + */ + public function testMultiStoreLabelView(array $expectedStoreData, array $expectedSecondStoreData): void + { + $this->executeInStoreContext->execute('default', [$this, 'assertProductLabel'], $expectedStoreData); + $this->executeInStoreContext->execute('fixturestore', [$this, 'assertProductLabel'], $expectedSecondStoreData); + } + + /** + * @return array + */ + public function expectedLabelsDataProvider(): array + { + return [ + [ + 'options_first_store' => [ + 'simple_option_1_default_store' => [ + 'label' => 'Option 1 Default Store', + ], + 'simple_option_2_default_store' => [ + 'label' => 'Option 2 Default Store', + ], + 'simple_option_3_default_store' => [ + 'label' => 'Option 3 Default Store', + ], + ], + 'options_second_store' => [ + 'simple_option_1_default_store' => [ + 'label' => 'Option 1 Second Store', + ], + 'simple_option_2_default_store' => [ + 'label' => 'Option 2 Second Store', + ], + 'simple_option_3_default_store' => [ + 'label' => 'Option 3 Second Store', + ], + ], + ], + ]; + } + + /** + * Assert configurable product labels config + * + * @param $expectedStoreData + * @return void + */ + public function assertProductLabel($expectedStoreData): void + { + $product = $this->productRepository->get('configurable', false, null, true); + $config = $this->getBlockConfig($product)['attributes'] ?? null; + $this->assertNotNull($config); + $this->assertAttributeConfig($expectedStoreData, reset($config)); + } + + /** + * @magentoDataFixture Magento/ConfigurableProduct/_files/configurable_product_two_websites.php + * + * @dataProvider expectedProductDataProvider + * + * @param array $expectedProducts + * @param array $expectedSecondStoreProducts + * @return void + */ + public function testMultiStoreOptionsView(array $expectedProducts, array $expectedSecondStoreProducts): void + { + $this->prepareConfigurableProduct('configurable', 'fixture_second_store'); + $this->executeInStoreContext->execute('default', [$this, 'assertProductConfig'], $expectedProducts); + $this->executeInStoreContext->execute( + 'fixture_second_store', + [$this, 'assertProductConfig'], + $expectedSecondStoreProducts + ); + } + + /** + * @return array + */ + public function expectedProductDataProvider(): array + { + return [ + [ + 'expected_store_products' => ['simple_option_1', 'simple_option_2'], + 'expected_second_store_products' => ['simple_option_2'], + ], + ]; + } + + /** + * Assert configurable product config + * + * @param $expectedProducts + * @return void + */ + public function assertProductConfig($expectedProducts): void + { + $product = $this->productRepository->get('configurable', false, null, true); + $config = $this->getBlockConfig($product)['index'] ?? null; + $this->assertNotNull($config); + $this->assertProducts($expectedProducts, $config); + } + + /** + * Prepare configurable product to test + * + * @param string $sku + * @param string $storeCode + * @return void + */ + private function prepareConfigurableProduct(string $sku, string $storeCode): void + { + $product = $this->productRepository->get($sku, false, null, true); + $productToUpdate = $product->getTypeInstance()->getUsedProductCollection($product) + ->setPageSize(1)->getFirstItem(); + $this->assertNotEmpty($productToUpdate->getData(), 'Configurable product does not have a child'); + $this->executeInStoreContext->execute($storeCode, [$this, 'setProductDisabled'], $productToUpdate); + } + + /** + * Assert product options display per stores + * + * @param array $expectedProducts + * @param array $config + * @return void + */ + private function assertProducts(array $expectedProducts, array $config): void + { + $this->assertCount(count($expectedProducts), $config); + $idsBySkus = $this->productResource->getProductsIdsBySkus($expectedProducts); + + foreach ($idsBySkus as $productId) { + $this->assertArrayHasKey($productId, $config); + } + } + + /** + * Set product status attribute to disabled + * + * @param ProductInterface $product + * @param string $storeCode + * @return void + */ + public function setProductDisabled(ProductInterface $product): void + { + $product->setStatus(Status::STATUS_DISABLED); + $this->productRepository->save($product); + } + + /** + * Get block config + * + * @param ProductInterface $product + * @return array + */ + private function getBlockConfig(ProductInterface $product): array + { + $block = $this->layout->createBlock(Configurable::class); + $block->setProduct($product); + + return $this->serializer->unserialize($block->getJsonConfig()); + } + + /** + * Assert configurable product config + * + * @param array $expectedData + * @param array $actualOptions + * @return void + */ + private function assertAttributeConfig(array $expectedData, array $actualOptions): void + { + $skus = array_keys($expectedData); + $idBySkuMap = $this->productResource->getProductsIdsBySkus($skus); + array_walk($actualOptions['options'], function (&$option) { + unset($option['id']); + }); + foreach ($expectedData as $sku => &$option) { + $option['products'] = [$idBySkuMap[$sku]]; + } + $this->assertEquals(array_values($expectedData), $actualOptions['options']); + } +} diff --git a/dev/tests/integration/testsuite/Magento/ConfigurableProduct/Block/Product/View/Type/RenderConfigurableOptionsTest.php b/dev/tests/integration/testsuite/Magento/ConfigurableProduct/Block/Product/View/Type/RenderConfigurableOptionsTest.php new file mode 100644 index 0000000000000..6a8dea32be620 --- /dev/null +++ b/dev/tests/integration/testsuite/Magento/ConfigurableProduct/Block/Product/View/Type/RenderConfigurableOptionsTest.php @@ -0,0 +1,145 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +declare(strict_types=1); + +namespace Magento\ConfigurableProduct\Block\Product\View\Type; + +use Magento\Catalog\Api\ProductRepositoryInterface; +use Magento\Catalog\Block\Product\View; +use Magento\Catalog\Model\Product\Visibility; +use Magento\ConfigurableProduct\Helper\Data; +use Magento\ConfigurableProduct\Model\ConfigurableAttributeData; +use Magento\Framework\Registry; +use Magento\Framework\Serialize\Serializer\Json; +use Magento\Framework\View\Result\Page; +use Magento\TestFramework\Helper\Bootstrap; +use Magento\TestFramework\ObjectManager; +use PHPUnit\Framework\TestCase; + +/** + * Test cases related to render configurable options. + * + * @magentoAppArea frontend + * @magentoDbIsolation enabled + */ +class RenderConfigurableOptionsTest extends TestCase +{ + /** + * @var ObjectManager + */ + private $objectManager; + + /** + * @var Data + */ + private $configurableHelper; + + /** + * @var ProductRepositoryInterface + */ + private $productRepository; + + /** + * @var ConfigurableAttributeData + */ + private $configurableAttributeData; + + /** + * @var Registry + */ + private $registry; + + /** + * @var Page + */ + private $page; + + /** + * @var Json + */ + private $json; + + /** + * @inheritdoc + */ + protected function setUp() + { + $this->objectManager = Bootstrap::getObjectManager(); + $this->configurableHelper = $this->objectManager->get(Data::class); + $this->productRepository = $this->objectManager->get(ProductRepositoryInterface::class); + $this->configurableAttributeData = $this->objectManager->get(ConfigurableAttributeData::class); + $this->registry = $this->objectManager->get(Registry::class); + $this->page = $this->objectManager->create(Page::class); + $this->json = $this->objectManager->get(Json::class); + parent::setUp(); + } + + /** + * Assert that all configurable options was rendered correctly if one of + * child product is visible on catalog\search. + * + * @magentoDataFixture Magento/ConfigurableProduct/_files/configurable_product_with_two_child_products.php + * + * @return void + */ + public function testRenderConfigurableOptionsBlockWithOneVisibleOption(): void + { + $configurableProduct = $this->productRepository->get('Configurable product'); + $childProduct = $this->productRepository->get('Simple option 1'); + $childProduct->setVisibility(Visibility::VISIBILITY_BOTH); + $this->productRepository->save($childProduct); + $allProducts = $configurableProduct->getTypeInstance()->getUsedProducts($configurableProduct, null); + $options = $this->configurableHelper->getOptions($configurableProduct, $allProducts); + $confAttrData = $this->configurableAttributeData->getAttributesData($configurableProduct, $options); + $attributesJson = str_replace( + ['[', ']'], + ['\[', '\]'], + $this->json->serialize($confAttrData['attributes']) + ); + $optionsHtml = $this->getConfigurableOptionsHtml('Configurable product'); + $this->assertRegExp("/\"spConfig\": {\"attributes\":{$attributesJson}/", $optionsHtml); + } + + /** + * Render configurable options block. + * + * @param string $configurableSku + * @return string + */ + private function getConfigurableOptionsHtml(string $configurableSku): string + { + $product = $this->productRepository->get($configurableSku); + $this->registry->unregister('product'); + $this->registry->register('product', $product); + $optionsBlock = $this->getOptionsWrapperBlockWithOnlyConfigurableBlock(); + $optionHtml = $optionsBlock->toHtml(); + $this->registry->unregister('product'); + + return $optionHtml; + } + + /** + * Get options wrapper without extra blocks(only configurable child block). + * + * @return View + */ + private function getOptionsWrapperBlockWithOnlyConfigurableBlock(): View + { + $this->page->addHandle([ + 'default', + 'catalog_product_view', + 'catalog_product_view_type_configurable', + ]); + $this->page->getLayout()->generateXml(); + + /** @var View $productInfoOptionsWrapper */ + $productInfoOptionsWrapper = $this->page->getLayout()->getBlock('product.info.options.wrapper'); + $productInfoOptionsWrapper->unsetChild('product_options'); + $productInfoOptionsWrapper->unsetChild('html_calendar'); + + return $productInfoOptionsWrapper; + } +} diff --git a/dev/tests/integration/testsuite/Magento/ConfigurableProduct/Controller/Adminhtml/Product/Initialization/HelperTest.php b/dev/tests/integration/testsuite/Magento/ConfigurableProduct/Controller/Adminhtml/Product/Initialization/HelperTest.php new file mode 100644 index 0000000000000..ba684f37175e4 --- /dev/null +++ b/dev/tests/integration/testsuite/Magento/ConfigurableProduct/Controller/Adminhtml/Product/Initialization/HelperTest.php @@ -0,0 +1,369 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +declare(strict_types=1); + +namespace Magento\ConfigurableProduct\Controller\Adminhtml\Product\Initialization; + +use Magento\Catalog\Api\Data\ProductInterface; +use Magento\Catalog\Api\ProductAttributeRepositoryInterface; +use Magento\Catalog\Api\ProductRepositoryInterface; +use Magento\Catalog\Controller\Adminhtml\Product\Initialization\Helper; +use Magento\Catalog\Model\Product\Media\Config; +use Magento\Catalog\Model\ResourceModel\Product as ProductResource; +use Magento\Framework\Api\SearchCriteriaBuilder; +use Magento\Framework\App\Filesystem\DirectoryList; +use Magento\Framework\App\RequestInterface; +use Magento\Framework\Filesystem; +use Magento\Framework\Filesystem\Directory\WriteInterface; +use Magento\Framework\ObjectManagerInterface; +use Magento\Framework\Serialize\SerializerInterface; +use Magento\Store\Model\Store; +use Magento\TestFramework\Helper\Bootstrap; +use PHPUnit\Framework\TestCase; + +/** + * Tests for image processing plugins for child products by saving a configurable product. + * + * @magentoAppArea adminhtml + * @magentoDbIsolation enabled + * @SuppressWarnings(PHPMD.CouplingBetweenObjects) + */ +class HelperTest extends TestCase +{ + /** + * @var ObjectManagerInterface + */ + private $objectManager; + + /** + * @var Helper + */ + private $helper; + + /** + * @var RequestInterface + */ + private $request; + + /** + * @var ProductRepositoryInterface + */ + private $productRepository; + + /** + * @var ProductResource + */ + private $productResource; + + /** + * @var ProductAttributeRepositoryInterface + */ + private $productAttributeRepository; + + /** + * @var SerializerInterface + */ + private $jsonSerializer; + + /** + * @var SearchCriteriaBuilder + */ + private $searchCriteriaBuilder; + + /** + * @var Config + */ + private $config; + + /** + * @var WriteInterface + */ + private $mediaDirectory; + + /** + * @var ProductInterface + */ + private $configurableProduct; + + /** + * @inheritdoc + */ + protected function setUp() + { + parent::setUp(); + $this->objectManager = Bootstrap::getObjectManager(); + $this->request = $this->objectManager->get(RequestInterface::class); + $this->helper = $this->objectManager->create(Helper::class); + $this->productRepository = $this->objectManager->get(ProductRepositoryInterface::class); + $this->productRepository->cleanCache(); + $this->productResource =$this->objectManager->get(ProductResource::class); + $this->productAttributeRepository = $this->objectManager->create(ProductAttributeRepositoryInterface::class); + $this->jsonSerializer = $this->objectManager->get(SerializerInterface::class); + $this->searchCriteriaBuilder = $this->objectManager->get(SearchCriteriaBuilder::class); + $this->config = $this->objectManager->get(Config::class); + $this->mediaDirectory = $this->objectManager->get(Filesystem::class)->getDirectoryWrite(DirectoryList::MEDIA); + $this->configurableProduct = $this->productRepository->get('configurable'); + } + + /** + * Tests adding images with various roles to child products by saving a configurable product. + * + * @magentoDataFixture Magento/Catalog/_files/product_image.php + * @magentoDataFixture Magento/ConfigurableProduct/_files/product_configurable.php + * @dataProvider initializeDataProvider + * @param array $childProducts + * @param array $expectedImages + * @return void + */ + public function testInitialize(array $childProducts, array $expectedImages): void + { + $this->setRequestParams($childProducts); + $this->helper->initialize($this->configurableProduct); + $this->assertChildProductImages($expectedImages); + } + + /** + * Tests replacing images with various roles to child products by saving a configurable product. + * + * @magentoDataFixture Magento/Catalog/_files/product_image.php + * @magentoDataFixture Magento/ConfigurableProduct/_files/product_configurable.php + * @dataProvider initializeWithExistingChildImagesDataProvider + * @param array $childProducts + * @param array $expectedImages + * @return void + */ + public function testInitializeWithExistingChildImages(array $childProducts, array $expectedImages): void + { + $this->updateChildProductsImages( + [ + 'simple_10' => '/m/a/magento_thumbnail.jpg.tmp', + 'simple_20' => '/m/a/magento_small_image.jpg.tmp', + ] + ); + $this->setRequestParams($childProducts); + $this->helper->initialize($this->configurableProduct); + $this->assertChildProductImages($expectedImages); + } + + /** + * @return array + */ + public function initializeDataProvider(): array + { + return [ + 'children_with_same_image_and_roles' => [ + 'child_products' => [ + 'simple_10' => [ + 'media_gallery' => $this->getMediaGallery(['ben062bdw2v' => '/m/a/magento_image.jpg.tmp']), + 'images' => [ + '/m/a/magento_image.jpg.tmp' => ['swatch_image', 'small_image', 'image', 'thumbnail'], + ], + ], + 'simple_20' => [ + 'media_gallery' => $this->getMediaGallery(['ben062bdw2v' => '/m/a/magento_image.jpg.tmp']), + 'images' => [ + '/m/a/magento_image.jpg.tmp' => ['swatch_image', 'small_image', 'image', 'thumbnail'], + ], + ], + ], + 'expected_images' => [ + 'simple_10' => [ + '/m/a/magento_image_1.jpg' => ['swatch_image', 'small_image', 'image', 'thumbnail'], + ], + 'simple_20' => [ + '/m/a/magento_image_2.jpg' => ['swatch_image', 'small_image', 'image', 'thumbnail'], + ], + ], + ], + 'children_with_different_images' => [ + 'child_products' => [ + 'simple_10' => [ + 'media_gallery' => $this->getMediaGallery(['ben062bdw2v' => '/m/a/magento_image.jpg.tmp']), + 'images' => [ + '/m/a/magento_image.jpg.tmp' => ['swatch_image', 'small_image', 'image', 'thumbnail'], + ], + ], + 'simple_20' => [ + 'media_gallery' => $this->getMediaGallery( + ['lrwuv5ukisn' => '/m/a/magento_small_image.jpg.tmp'] + ), + 'images' => [ + '/m/a/magento_small_image.jpg.tmp' => ['swatch_image', 'small_image', 'image', 'thumbnail'], + ], + ], + ], + 'expected_images' => [ + 'simple_10' => [ + '/m/a/magento_image_1.jpg' => ['swatch_image', 'small_image', 'image', 'thumbnail'], + ], + 'simple_20' => [ + '/m/a/magento_small_image_1.jpg' => ['swatch_image', 'small_image', 'image', 'thumbnail'], + ], + ], + ], + 'children_with_different_image_roles' => [ + 'child_products' => [ + 'simple_10' => [ + 'media_gallery' => $this->getMediaGallery( + [ + 'ben062bdw2v' => '/m/a/magento_image.jpg.tmp', + 'lrwuv5ukisn' => '/m/a/magento_small_image.jpg.tmp', + ] + ), + 'images' => [ + '/m/a/magento_image.jpg.tmp' => ['swatch_image', 'small_image'], + '/m/a/magento_small_image.jpg.tmp' => ['image', 'thumbnail'], + ], + ], + 'simple_20' => [ + 'media_gallery' => $this->getMediaGallery( + [ + 'ben062bdw2v' => '/m/a/magento_image.jpg.tmp', + 'lrwuv5ukisn' => '/m/a/magento_small_image.jpg.tmp', + ] + ), + 'images' => [ + '/m/a/magento_small_image.jpg.tmp' => ['swatch_image', 'small_image'], + '/m/a/magento_image.jpg.tmp' => ['image', 'thumbnail'], + ], + ], + ], + 'expected_images' => [ + 'simple_10' => [ + '/m/a/magento_image_1.jpg' => ['swatch_image', 'small_image'], + '/m/a/magento_small_image_1.jpg' => ['image', 'thumbnail'], + ], + 'simple_20' => [ + '/m/a/magento_small_image_2.jpg' => ['swatch_image', 'small_image'], + '/m/a/magento_image_2.jpg' => ['image', 'thumbnail'], + ], + ], + ], + ]; + } + + /** + * @return array + */ + public function initializeWithExistingChildImagesDataProvider(): array + { + $dataProvider = $this->initializeDataProvider(); + unset($dataProvider['children_with_different_images'], $dataProvider['children_with_different_image_roles']); + + return array_values($dataProvider); + } + + /** + * Sets configurable product params to request. + * + * @param array $childProducts + * @return void + */ + private function setRequestParams(array $childProducts): void + { + $matrix = $associatedProductIds = []; + $attribute = $this->productAttributeRepository->get('test_configurable'); + + foreach ($childProducts as $sku => $product) { + $simpleProduct = $this->productRepository->get($sku); + $attributeValue = $simpleProduct->getData('test_configurable'); + foreach ($product['images'] as $image => $roles) { + foreach ($roles as $role) { + $product[$role] = $image; + } + } + unset($product['images']); + $product['configurable_attribute'] = $this->jsonSerializer->serialize( + ['test_configurable' => $attributeValue] + ); + $product['variationKey'] = $attributeValue; + $product['id'] = $simpleProduct->getId(); + $product['sku'] = $sku; + $product['was_changed'] = true; + $product['newProduct'] = 0; + $matrix[] = $product; + $associatedProductIds[] = $simpleProduct->getId(); + } + $this->request->setParams( + [ + 'attributes' => [$attribute->getAttributeId()], + 'configurable-matrix-serialized' => $this->jsonSerializer->serialize($matrix), + ] + ); + $this->request->setPostValue( + 'associated_product_ids_serialized', + $this->jsonSerializer->serialize($associatedProductIds) + ); + } + + /** + * Asserts child products images. + * + * @param array $expectedImages + * @return void + */ + private function assertChildProductImages(array $expectedImages): void + { + $simpleIds = $this->configurableProduct->getExtensionAttributes()->getConfigurableProductLinks(); + $criteria = $this->searchCriteriaBuilder->addFilter('entity_id', $simpleIds, 'in')->create(); + foreach ($this->productRepository->getList($criteria)->getItems() as $simpleProduct) { + $images = $expectedImages[$simpleProduct->getSku()]; + foreach ($images as $image => $roles) { + foreach ($roles as $role) { + $this->assertEquals($image, $simpleProduct->getData($role)); + } + $this->assertFileExists( + $this->mediaDirectory->getAbsolutePath($this->config->getBaseMediaPath() . $image) + ); + } + } + } + + /** + * Returns media gallery product param. + * + * @param array $imageNames + * @return array + */ + private function getMediaGallery(array $imageNames): array + { + $images = []; + foreach ($imageNames as $key => $item) { + $images[$key] = ['file' => $item, 'label' => '', 'media_type' => 'image']; + } + + return ['images' => $images]; + } + + /** + * Sets image to child products. + * + * @param array $imageNames + * @return void + */ + private function updateChildProductsImages(array $imageNames): void + { + $simpleIds = $this->configurableProduct->getExtensionAttributes()->getConfigurableProductLinks(); + $criteria = $this->searchCriteriaBuilder->addFilter('entity_id', $simpleIds, 'in')->create(); + $products = $this->productRepository->getList($criteria)->getItems(); + foreach ($products as $simpleProduct) { + $simpleProduct->setStoreId(Store::DEFAULT_STORE_ID) + ->setImage($imageNames[$simpleProduct->getSku()]) + ->setSmallImage($imageNames[$simpleProduct->getSku()]) + ->setThumbnail($imageNames[$simpleProduct->getSku()]) + ->setSwatchImage($imageNames[$simpleProduct->getSku()]) + ->setData( + 'media_gallery', + [ + 'images' => [ + ['file' => $imageNames[$simpleProduct->getSku()], 'label' => '', 'media_type' => 'image'] + ] + ] + ); + $this->productResource->save($simpleProduct); + } + } +} diff --git a/dev/tests/integration/testsuite/Magento/ConfigurableProduct/Controller/Adminhtml/Product/Save/CreateCustomOptionsTest.php b/dev/tests/integration/testsuite/Magento/ConfigurableProduct/Controller/Adminhtml/Product/Save/CreateCustomOptionsTest.php new file mode 100644 index 0000000000000..20cfbda0ac920 --- /dev/null +++ b/dev/tests/integration/testsuite/Magento/ConfigurableProduct/Controller/Adminhtml/Product/Save/CreateCustomOptionsTest.php @@ -0,0 +1,26 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +declare(strict_types=1); + +namespace Magento\ConfigurableProduct\Controller\Adminhtml\Product\Save; + +use Magento\Catalog\Controller\Adminhtml\Product\Save\CreateCustomOptionsTest as SimpleProductOptionsTest; + +/** + * Base test cases for configurable product custom options with type "field". + * Option add via dispatch product controller action save with options data in POST data. + * + * @magentoAppArea adminhtml + * @magentoDbIsolation enabled + * @magentoDataFixture Magento/ConfigurableProduct/_files/product_configurable.php + */ +class CreateCustomOptionsTest extends SimpleProductOptionsTest +{ + /** + * @var string + */ + protected $productSku = 'configurable'; +} diff --git a/dev/tests/integration/testsuite/Magento/ConfigurableProduct/Controller/Adminhtml/Product/Save/DeleteCustomOptionsTest.php b/dev/tests/integration/testsuite/Magento/ConfigurableProduct/Controller/Adminhtml/Product/Save/DeleteCustomOptionsTest.php new file mode 100644 index 0000000000000..58b7f0e56fb0c --- /dev/null +++ b/dev/tests/integration/testsuite/Magento/ConfigurableProduct/Controller/Adminhtml/Product/Save/DeleteCustomOptionsTest.php @@ -0,0 +1,26 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +declare(strict_types=1); + +namespace Magento\ConfigurableProduct\Controller\Adminhtml\Product\Save; + +use Magento\Catalog\Controller\Adminhtml\Product\Save\DeleteCustomOptionsTest as SimpleProductOptionsTest; + +/** + * Base test cases for delete configurable product custom option with type "field". + * Option deleting via product controller action save. + * + * @magentoAppArea adminhtml + * @magentoDbIsolation enabled + * @magentoDataFixture Magento/ConfigurableProduct/_files/product_configurable.php + */ +class DeleteCustomOptionsTest extends SimpleProductOptionsTest +{ + /** + * @var string + */ + protected $productSku = 'configurable'; +} diff --git a/dev/tests/integration/testsuite/Magento/ConfigurableProduct/Controller/Adminhtml/Product/Save/UpdateCustomOptionsTest.php b/dev/tests/integration/testsuite/Magento/ConfigurableProduct/Controller/Adminhtml/Product/Save/UpdateCustomOptionsTest.php new file mode 100644 index 0000000000000..ab0b03489cb92 --- /dev/null +++ b/dev/tests/integration/testsuite/Magento/ConfigurableProduct/Controller/Adminhtml/Product/Save/UpdateCustomOptionsTest.php @@ -0,0 +1,26 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +declare(strict_types=1); + +namespace Magento\ConfigurableProduct\Controller\Adminhtml\Product\Save; + +use Magento\Catalog\Controller\Adminhtml\Product\Save\UpdateCustomOptionsTest as SimpleProductOptionsTest; + +/** + * Base test cases for update configurable product custom options with type "field". + * Option updating via dispatch product controller action save with updated options data in POST data. + * + * @magentoAppArea adminhtml + * @magentoDbIsolation enabled + * @magentoDataFixture Magento/ConfigurableProduct/_files/product_configurable.php + */ +class UpdateCustomOptionsTest extends SimpleProductOptionsTest +{ + /** + * @var string + */ + protected $productSku = 'configurable'; +} diff --git a/dev/tests/integration/testsuite/Magento/ConfigurableProduct/Controller/Adminhtml/ProductTest.php b/dev/tests/integration/testsuite/Magento/ConfigurableProduct/Controller/Adminhtml/ProductTest.php index b71507ae43f9f..833100e3e4740 100644 --- a/dev/tests/integration/testsuite/Magento/ConfigurableProduct/Controller/Adminhtml/ProductTest.php +++ b/dev/tests/integration/testsuite/Magento/ConfigurableProduct/Controller/Adminhtml/ProductTest.php @@ -3,76 +3,521 @@ * Copyright © Magento, Inc. All rights reserved. * See COPYING.txt for license details. */ +declare(strict_types=1); + namespace Magento\ConfigurableProduct\Controller\Adminhtml; -use Magento\Catalog\Model\Product; -use Magento\Framework\Registry; -use Magento\TestFramework\ObjectManager; +use Magento\Catalog\Api\Data\ProductAttributeInterface; +use Magento\Catalog\Api\Data\ProductInterface; +use Magento\Catalog\Api\ProductAttributeRepositoryInterface; use Magento\Catalog\Api\ProductRepositoryInterface; +use Magento\Catalog\Model\Product\Attribute\Source\Status; +use Magento\Catalog\Model\Product\Type\Simple; +use Magento\Catalog\Model\Product\Type\Virtual; +use Magento\ConfigurableProduct\Model\Product\Type\Configurable; +use Magento\Eav\Model\Config; use Magento\Framework\App\Request\Http as HttpRequest; +use Magento\Framework\Exception\NoSuchEntityException; +use Magento\Framework\Message\MessageInterface; +use Magento\Framework\Registry; +use Magento\Framework\Serialize\SerializerInterface; +use Magento\TestFramework\TestCase\AbstractBackendController; /** + * Tests for configurable product admin save. + * + * @SuppressWarnings(PHPMD.CouplingBetweenObjects) * @magentoAppArea adminhtml + * @magentoDbIsolation enabled */ -class ProductTest extends \Magento\TestFramework\TestCase\AbstractBackendController +class ProductTest extends AbstractBackendController { + /** + * @var ProductRepositoryInterface + */ + private $productRepository; + + /** + * @var ProductAttributeRepositoryInterface + */ + private $productAttributeRepository; + + /** + * @var Registry + */ + private $registry; + + /** + * @var SerializerInterface + */ + private $jsonSerializer; + + /** + * @var Config + */ + private $eavConfig; + + /** + * @inheritdoc + */ + protected function setUp() + { + parent::setUp(); + $this->productRepository = $this->_objectManager->get(ProductRepositoryInterface::class); + $this->productRepository->cleanCache(); + $this->productAttributeRepository = $this->_objectManager->create(ProductAttributeRepositoryInterface::class); + $this->registry = $this->_objectManager->get(Registry::class); + $this->jsonSerializer = $this->_objectManager->get(SerializerInterface::class); + $this->eavConfig = $this->_objectManager->get(Config::class); + } + /** * @magentoDataFixture Magento/ConfigurableProduct/_files/product_configurable.php * @magentoDataFixture Magento/ConfigurableProduct/_files/associated_products.php + * @return void */ - public function testSaveActionAssociatedProductIds() + public function testSaveActionAssociatedProductIds(): void { $associatedProductIds = ['3', '14', '15', '92']; - $associatedProductIdsJSON = json_encode($associatedProductIds); $this->getRequest()->setMethod(HttpRequest::METHOD_POST); $this->getRequest()->setPostValue( [ 'id' => 1, - 'attributes' => [$this->_getConfigurableAttribute()->getId()], - 'associated_product_ids_serialized' => $associatedProductIdsJSON, + 'attributes' => [$this->getAttribute('test_configurable')->getId()], + 'associated_product_ids_serialized' => $this->jsonSerializer->serialize($associatedProductIds), ] ); + $this->dispatch('backend/catalog/product/save'); + $this->assertSessionMessages($this->equalTo([__('You saved the product.')]), MessageInterface::TYPE_SUCCESS); + $this->assertRegistryConfigurableLinks($associatedProductIds); + $this->assertConfigurableLinks('configurable', $associatedProductIds); + } + /** + * @magentoDataFixture Magento/ConfigurableProduct/_files/configurable_attribute.php + * @dataProvider saveNewProductDataProvider + * @param array $childProducts + * @return void + */ + public function testSaveNewProduct(array $childProducts): void + { + $this->serRequestParams($childProducts); $this->dispatch('backend/catalog/product/save'); + $this->assertSessionMessages($this->equalTo([__('You saved the product.')]), MessageInterface::TYPE_SUCCESS); + $this->assertChildProducts($childProducts); + $this->assertConfigurableOptions('configurable', $childProducts); + $this->assertConfigurableLinks('configurable', $this->getProductIds(array_keys($childProducts))); + } - /** @var $objectManager ObjectManager */ - $objectManager = \Magento\TestFramework\Helper\Bootstrap::getObjectManager(); + /** + * @return array + */ + public function saveNewProductDataProvider(): array + { + return [ + 'with_different_prices_and_qty' => [ + 'child_products' => [ + 'simple_1' => [ + 'name' => 'simple_1', + 'sku' => 'simple_1', + 'price' => '200', + 'weight' => '1', + 'qty' => '100', + 'attributes' => ['test_configurable' => 'Option 1'], + ], + 'simple_2' => [ + 'name' => 'simple_2', + 'sku' => 'simple_2', + 'price' => '100', + 'weight' => '1', + 'qty' => '200', + 'attributes' => ['test_configurable' => 'Option 2'], + ], + ], + ], + 'without_weight' => [ + 'child_products' => [ + 'simple_1' => [ + 'name' => 'simple_1', + 'sku' => 'simple_1', + 'price' => '100', + 'qty' => '100', + 'attributes' => ['test_configurable' => 'Option 1'], + ], + 'simple_2' => [ + 'name' => 'simple_2', + 'sku' => 'simple_2', + 'price' => '100', + 'qty' => '100', + 'attributes' => ['test_configurable' => 'Option 2'], + ], + ], + ], + ]; + } - /** @var $product Product */ - $product = $objectManager->get(Registry::class)->registry('current_product'); - $configurableProductLinks = array_values($product->getExtensionAttributes()->getConfigurableProductLinks()); - self::assertEquals( - $associatedProductIds, - $configurableProductLinks, - 'Product links are not available in the registry' + /** + * @magentoDataFixture Magento/ConfigurableProduct/_files/configurable_product_with_one_simple.php + * @magentoDataFixture Magento/ConfigurableProduct/_files/configurable_attribute_2.php + * @dataProvider saveExistProductDataProvider + * @param array $childProducts + * @param array $associatedProducts + * @return void + */ + public function testSaveExistProduct(array $childProducts, array $associatedProducts): void + { + $configurableProduct = $this->productRepository->get('configurable'); + $this->serRequestParams($childProducts, $associatedProducts, (int)$configurableProduct->getId()); + $this->dispatch('backend/catalog/product/save'); + $this->assertSessionMessages($this->equalTo([__('You saved the product.')]), MessageInterface::TYPE_SUCCESS); + $this->assertChildProducts($childProducts); + $this->assertConfigurableOptions('configurable', $childProducts); + $this->assertConfigurableLinks( + 'configurable', + $this->getProductIds(array_merge($associatedProducts, array_keys($childProducts))) + ); + } + + /** + * @return array + */ + public function saveExistProductDataProvider(): array + { + return [ + 'added_new_option' => [ + 'child_products' => [ + 'simple_2' => [ + 'name' => 'simple_2', + 'sku' => 'simple_2', + 'price' => '100', + 'weight' => '1', + 'qty' => '200', + 'attributes' => ['test_configurable' => 'Option 2'], + ], + ], + 'associated_products' => ['simple_1'], + ], + 'added_new_option_and_delete_old' => [ + 'child_products' => [ + 'simple_2' => [ + 'name' => 'simple_2', + 'sku' => 'simple_2', + 'price' => '100', + 'qty' => '100', + 'attributes' => ['test_configurable' => 'Option 2'], + ], + ], + 'associated_products' => [], + ], + 'delete_all_options' => [ + 'child_products' => [], + 'associated_products' => [], + ], + 'added_new_attribute' => [ + 'child_products' => [ + 'simple_1_1' => [ + 'name' => 'simple_1_1', + 'sku' => 'simple_1_1', + 'price' => '100', + 'weight' => '1', + 'qty' => '200', + 'attributes' => [ + 'test_configurable' => 'Option 1', + 'test_configurable_2' => 'Option 1', + ], + ], + 'simple_1_2' => [ + 'name' => 'simple_1_2', + 'sku' => 'simple_1_2', + 'price' => '100', + 'weight' => '1', + 'qty' => '200', + 'attributes' => [ + 'test_configurable' => 'Option 1', + 'test_configurable_2' => 'Option 2', + ], + ], + ], + 'associated_products' => [], + ], + 'added_new_attribute_and_delete_old' => [ + 'child_products' => [ + 'simple_2_1' => [ + 'name' => 'simple_2_1', + 'sku' => 'simple_2_1', + 'price' => '100', + 'qty' => '100', + 'attributes' => ['test_configurable_2' => 'Option 1'], + ], + 'simple_2_2' => [ + 'name' => 'simple_2_2', + 'sku' => 'simple_2_2', + 'price' => '100', + 'qty' => '100', + 'attributes' => ['test_configurable_2' => 'Option 2'], + ], + ], + 'associated_products' => [], + ], + ]; + } + + /** + * Sets products data into request. + * + * @param array $childProducts + * @param array|null $associatedProducts + * @param int|null $mainProductId + * @return void + */ + private function serRequestParams( + array $childProducts, + ?array $associatedProducts = [], + ?int $mainProductId = null + ): void { + $this->setVariationMatrix($childProducts); + $this->setAssociatedProducts($associatedProducts); + $this->getRequest()->setMethod(HttpRequest::METHOD_POST); + $this->getRequest()->setParams( + [ + 'type' => Configurable::TYPE_CODE, + 'set' => $this->getDefaultAttributeSetId(), + 'id' => $mainProductId, + ] + ); + $this->getRequest()->setPostValue( + 'product', + [ + 'attribute_set_id' => $this->getDefaultAttributeSetId(), + 'name' => 'configurable', + 'sku' => 'configurable', + 'configurable_attributes_data' => $this->getConfigurableAttributesData($childProducts) ?: null, + ] ); + } - /** @var $product \Magento\Catalog\Api\Data\ProductInterface */ - $product = $objectManager->get(ProductRepositoryInterface::class)->getById(1, false, null, true); - $configurableProductLinks = array_values($product->getExtensionAttributes()->getConfigurableProductLinks()); - self::assertEquals( + /** + * Asserts product configurable links. + * + * @param string $sku + * @param array $associatedProductIds + * @return void + */ + private function assertConfigurableLinks(string $sku, array $associatedProductIds): void + { + $product = $this->productRepository->get($sku, false, null, true); + $this->assertEquals( $associatedProductIds, - $configurableProductLinks, + array_values($product->getExtensionAttributes()->getConfigurableProductLinks() ?: []), 'Product links are not available in the database' ); } /** - * Retrieve configurable attribute instance + * Asserts product from registry configurable links. * - * @return \Magento\Catalog\Model\Entity\Attribute - */ - protected function _getConfigurableAttribute() - { - return \Magento\TestFramework\Helper\Bootstrap::getObjectManager()->create( - \Magento\Catalog\Model\Entity\Attribute::class - )->loadByCode( - \Magento\TestFramework\Helper\Bootstrap::getObjectManager()->get( - \Magento\Eav\Model\Config::class - )->getEntityType( - 'catalog_product' - )->getId(), - 'test_configurable' + * @param array $associatedProductIds + * @return void + */ + private function assertRegistryConfigurableLinks(array $associatedProductIds): void + { + $product = $this->registry->registry('current_product'); + $this->assertNotNull($product); + $this->assertEquals( + $associatedProductIds, + array_values($product->getExtensionAttributes()->getConfigurableProductLinks() ?: []), + 'Product links are not available in the registry' + ); + } + + /** + * Asserts child products data. + * + * @param array $childProducts + * @return void + */ + private function assertChildProducts(array $childProducts): void + { + foreach ($this->getProducts(array_column($childProducts, 'sku')) as $product) { + $expectedProduct = $childProducts[$product->getSku()]; + $this->assertEquals($expectedProduct['price'], $product->getPrice()); + + if (!empty($expectedProduct['weight'])) { + $this->assertEquals($expectedProduct['weight'], (double)$product->getWeight()); + $this->assertInstanceOf(Simple::class, $product->getTypeInstance()); + } else { + $this->assertInstanceOf(Virtual::class, $product->getTypeInstance()); + } + + $this->assertEquals($expectedProduct['qty'], $product->getExtensionAttributes()->getStockItem()->getQty()); + } + } + + /** + * Asserts that configurable attributes present in product configurable option list. + * + * @param string $sku + * @param array $childProducts + * @return void + */ + private function assertConfigurableOptions(string $sku, array $childProducts): void + { + $configurableProduct = $this->productRepository->get($sku, false, null, true); + $options = $configurableProduct->getExtensionAttributes()->getConfigurableProductOptions(); + if (empty($childProducts)) { + $this->assertNull($options); + } else { + foreach ($options as $option) { + $attribute = $this->getAttribute($option->getAttributeId()); + foreach ($childProducts as $childProduct) { + $this->assertContains($attribute->getAttributeCode(), array_keys($childProduct['attributes'])); + } + } + } + } + + /** + * Sets configurable product params to request. + * + * @param array $childProducts + * @return void + */ + private function setVariationMatrix(array $childProducts): void + { + $matrix = $attributeIds = $configurableAttributes = []; + foreach ($childProducts as $product) { + foreach ($product['attributes'] as $attributeCode => $optionLabel) { + $attribute = $this->getAttribute($attributeCode); + $configurableAttributes[$attributeCode] = $attribute->getSource()->getOptionId($optionLabel); + $attributeIds[] = $attribute->getAttributeId(); + } + $product['status'] = Status::STATUS_ENABLED; + $product['configurable_attribute'] = $this->jsonSerializer->serialize($configurableAttributes); + $product['newProduct'] = 1; + $product['variationKey'] = implode('-', array_values($configurableAttributes)); + $matrix[] = $product; + } + $this->getRequest()->setPostValue( + [ + 'affect_configurable_product_attributes' => 1, + 'attributes' => $attributeIds, + 'new-variations-attribute-set-id' => $this->getDefaultAttributeSetId(), + 'configurable-matrix-serialized' => $this->jsonSerializer->serialize($matrix), + ] ); } + + /** + * Sets associated product ids param to request. + * + * @param array|null $associatedProducts + */ + private function setAssociatedProducts(?array $associatedProducts): void + { + if (!empty($associatedProducts)) { + $associatedProductIds = array_map( + function (ProductInterface $product) { + return $product->getId(); + }, + $this->getProducts($associatedProducts) + ); + $this->getRequest()->setPostValue( + 'associated_product_ids_serialized', + $this->jsonSerializer->serialize($associatedProductIds) + ); + } + } + + /** + * Returns product configurable attributes data. + * + * @param array $childProducts + * @return array + */ + private function getConfigurableAttributesData(array $childProducts): array + { + $result = []; + foreach ($childProducts as $product) { + foreach ($product['attributes'] as $attributeCode => $optionLabel) { + $attribute = $this->getAttribute($attributeCode); + $optionId = $attribute->getSource()->getOptionId($optionLabel); + if (empty($result[$attribute->getAttributeId()])) { + $result[$attribute->getAttributeId()] = [ + 'attribute_id' =>$attribute->getAttributeId(), + 'code' => $attribute->getAttributeCode(), + 'label' => $attribute->getAttributeCode(), + 'position' => '0', + 'values' => [ + $optionId => [ + 'include' => '1', + 'value_index' => $optionId, + ], + ], + ]; + } else { + $result[$attribute->getAttributeId()]['values'][$optionId] = [ + 'include' => '1', + 'value_index' => $optionId, + ]; + } + } + } + + return $result; + } + + /** + * Retrieve default product attribute set id. + * + * @return int + */ + private function getDefaultAttributeSetId(): int + { + return (int)$this->eavConfig + ->getEntityType(ProductAttributeInterface::ENTITY_TYPE_CODE) + ->getDefaultAttributeSetId(); + } + + /** + * Retrieve configurable attribute instance. + * + * @param string $attributeCode + * @return ProductAttributeInterface + */ + private function getAttribute(string $attributeCode): ProductAttributeInterface + { + return $this->productAttributeRepository->get($attributeCode); + } + + /** + * Returns products by sku list. + * + * @param array $skuList + * @return ProductInterface[] + */ + private function getProducts(array $skuList): array + { + $result = []; + foreach ($skuList as $sku) { + $result[] = $this->productRepository->get($sku); + } + + return $result; + } + + /** + * Returns product ids by sku list. + * + * @param array $skuList + * @return array + */ + private function getProductIds(array $skuList): array + { + $associatedProductIds = []; + foreach ($this->getProducts($skuList) as $product) { + $associatedProductIds[] = $product->getId(); + } + + return $associatedProductIds; + } } diff --git a/dev/tests/integration/testsuite/Magento/ConfigurableProduct/Model/FindByUrlRewriteTest.php b/dev/tests/integration/testsuite/Magento/ConfigurableProduct/Model/FindByUrlRewriteTest.php new file mode 100644 index 0000000000000..23ab905fa0eab --- /dev/null +++ b/dev/tests/integration/testsuite/Magento/ConfigurableProduct/Model/FindByUrlRewriteTest.php @@ -0,0 +1,267 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +declare(strict_types=1); + +namespace Magento\ConfigurableProduct\Model; + +use Magento\Catalog\Api\ProductRepositoryInterface; +use Magento\Catalog\Model\Product\Visibility; +use Magento\Catalog\Model\ResourceModel\Product as ProductResource; +use Magento\TestFramework\Helper\Bootstrap; +use Magento\TestFramework\ObjectManager; +use Magento\UrlRewrite\Model\ResourceModel\UrlRewriteCollection; +use Magento\UrlRewrite\Model\ResourceModel\UrlRewriteCollectionFactory; +use Magento\UrlRewrite\Model\UrlRewrite as UrlRewriteItem; +use Magento\UrlRewrite\Service\V1\Data\UrlRewrite; +use PHPUnit\Framework\TestCase; + +/** + * Test cases related to check that URL rewrite has created or not. + */ +class FindByUrlRewriteTest extends TestCase +{ + /** + * @var ObjectManager + */ + private $objectManger; + + /** + * @var ProductResource + */ + private $productResource; + + /** + * @var ProductRepositoryInterface + */ + private $productRepository; + + /** + * @var UrlRewriteCollectionFactory + */ + private $urlRewriteCollectionFactory; + + /** + * @inheritdoc + */ + protected function setUp() + { + $this->objectManger = Bootstrap::getObjectManager(); + $this->productResource = $this->objectManger->get(ProductResource::class); + $this->productRepository = $this->objectManger->get(ProductRepositoryInterface::class); + $this->urlRewriteCollectionFactory = $this->objectManger->get(UrlRewriteCollectionFactory::class); + parent::setUp(); + } + + /** + * Assert that product is available by URL rewrite with different visibility. + * + * @magentoDataFixture Magento/ConfigurableProduct/_files/configurable_product_with_two_child_products.php + * @dataProvider visibilityWithExpectedResultDataProvider + * @magentoDbIsolation enabled + * + * @param array $productsData + * @return void + */ + public function testCheckIsUrlRewriteForChildrenProductsHasCreated(array $productsData): void + { + $this->checkConfigurableUrlRewriteWasCreated(); + $this->updateProductsVisibility($productsData); + $productIdsBySkus = $this->getProductIdsBySkus($productsData); + $urlRewritesCollection = $this->getUrlRewritesCollectionByProductIds($productIdsBySkus); + $expectedCount = 0; + foreach ($productsData as $productData) { + $productId = $productIdsBySkus[$productData['sku']]; + /** @var UrlRewriteItem $urlRewrite */ + $urlRewrite = $urlRewritesCollection->getItemByColumnValue( + UrlRewrite::TARGET_PATH, + "catalog/product/view/id/{$productId}" + ); + if ($productData['url_rewrite_created']) { + $this->assertNotNull($urlRewrite); + $this->assertEquals($productId, $urlRewrite->getEntityId()); + $this->assertEquals('product', $urlRewrite->getEntityType()); + $expectedCount++; + } else { + $this->assertNull($urlRewrite); + } + } + $this->assertCount($expectedCount, $urlRewritesCollection); + } + + /** + * Return products visibility, expected result and other product additional data. + * + * @SuppressWarnings(PHPMD.ExcessiveMethodLength) + * + * @return array + */ + public function visibilityWithExpectedResultDataProvider(): array + { + return [ + 'visibility_for_both_product_only_catalog' => [ + [ + [ + 'sku' => 'Simple option 1', + 'visibility' => Visibility::VISIBILITY_IN_CATALOG, + 'url_rewrite_created' => true, + ], + [ + 'sku' => 'Simple option 2', + 'visibility' => Visibility::VISIBILITY_IN_CATALOG, + 'url_rewrite_created' => true, + ], + ], + ], + 'visibility_for_both_product_catalog_search' => [ + [ + [ + 'sku' => 'Simple option 1', + 'visibility' => Visibility::VISIBILITY_BOTH, + 'url_rewrite_created' => true, + ], + [ + 'sku' => 'Simple option 2', + 'visibility' => Visibility::VISIBILITY_BOTH, + 'url_rewrite_created' => true, + ], + ], + ], + 'visibility_for_both_product_only_search' => [ + [ + [ + 'sku' => 'Simple option 1', + 'visibility' => Visibility::VISIBILITY_IN_SEARCH, + 'url_rewrite_created' => true, + ], + [ + 'sku' => 'Simple option 2', + 'visibility' => Visibility::VISIBILITY_IN_SEARCH, + 'url_rewrite_created' => true, + ], + ], + ], + 'visibility_for_both_product_not_visible_individuality' => [ + [ + [ + 'sku' => 'Simple option 1', + 'visibility' => Visibility::VISIBILITY_NOT_VISIBLE, + 'url_rewrite_created' => false, + ], + [ + 'sku' => 'Simple option 2', + 'visibility' => Visibility::VISIBILITY_NOT_VISIBLE, + 'url_rewrite_created' => false, + ], + ], + ], + 'visibility_for_one_product_only_catalog' => [ + [ + [ + 'sku' => 'Simple option 1', + 'visibility' => Visibility::VISIBILITY_IN_CATALOG, + 'url_rewrite_created' => true, + ], + [ + 'sku' => 'Simple option 2', + 'visibility' => Visibility::VISIBILITY_NOT_VISIBLE, + 'url_rewrite_created' => false, + ], + ], + ], + 'visibility_for_one_product_catalog_search' => [ + [ + [ + 'sku' => 'Simple option 1', + 'visibility' => Visibility::VISIBILITY_BOTH, + 'url_rewrite_created' => true, + ], + [ + 'sku' => 'Simple option 2', + 'visibility' => Visibility::VISIBILITY_NOT_VISIBLE, + 'url_rewrite_created' => false, + ], + ], + ], + 'visibility_for_one_product_only_search' => [ + [ + [ + 'sku' => 'Simple option 1', + 'visibility' => Visibility::VISIBILITY_IN_SEARCH, + 'url_rewrite_created' => true, + ], + [ + 'sku' => 'Simple option 2', + 'visibility' => Visibility::VISIBILITY_NOT_VISIBLE, + 'url_rewrite_created' => false, + ], + ], + ], + ]; + } + + /** + * Update products visibility. + * + * @param array $productsData + * @return void + */ + private function updateProductsVisibility(array $productsData): void + { + foreach ($productsData as $productData) { + $product = $this->productRepository->get($productData['sku']); + $product->setVisibility($productData['visibility']); + $this->productRepository->save($product); + } + } + + /** + * Get URL rewrite collection by product ids. + * + * @param int[] $productIds + * @param string $storeCode + * @return UrlRewriteCollection + */ + private function getUrlRewritesCollectionByProductIds( + array $productIds, + string $storeCode = 'default' + ): UrlRewriteCollection { + $collection = $this->urlRewriteCollectionFactory->create(); + $collection->addStoreFilter($storeCode); + $collection->addFieldToFilter(UrlRewrite::ENTITY_TYPE, ['eq' => 'product']); + $collection->addFieldToFilter(UrlRewrite::ENTITY_ID, ['in' => $productIds]); + + return $collection; + } + + /** + * Check that configurable url rewrite was created. + * + * @return void + */ + private function checkConfigurableUrlRewriteWasCreated(): void + { + $configurableProduct = $this->productRepository->get('Configurable product'); + $configurableUrlRewrite = $this->getUrlRewritesCollectionByProductIds([$configurableProduct->getId()]) + ->getFirstItem(); + $this->assertEquals( + $configurableUrlRewrite->getTargetPath(), + "catalog/product/view/id/{$configurableProduct->getId()}" + ); + } + + /** + * Load all product ids by skus. + * + * @param array $productsData + * @return array + */ + private function getProductIdsBySkus(array $productsData): array + { + $skus = array_column($productsData, 'sku'); + + return $this->productResource->getProductsIdsBySkus($skus); + } +} diff --git a/dev/tests/integration/testsuite/Magento/ConfigurableProduct/Model/Product/Type/Configurable/PriceTest.php b/dev/tests/integration/testsuite/Magento/ConfigurableProduct/Model/Product/Type/Configurable/PriceTest.php index e530029e9755e..6f491b33a3496 100644 --- a/dev/tests/integration/testsuite/Magento/ConfigurableProduct/Model/Product/Type/Configurable/PriceTest.php +++ b/dev/tests/integration/testsuite/Magento/ConfigurableProduct/Model/Product/Type/Configurable/PriceTest.php @@ -3,48 +3,94 @@ * Copyright © Magento, Inc. All rights reserved. * See COPYING.txt for license details. */ +declare(strict_types=1); + namespace Magento\ConfigurableProduct\Model\Product\Type\Configurable; +use Magento\Catalog\Api\Data\ProductInterface; use Magento\Catalog\Api\ProductRepositoryInterface; use Magento\Catalog\Model\Product\Type\AbstractType; +use Magento\Customer\Model\Group; +use Magento\Framework\ObjectManagerInterface; +use Magento\Store\Api\WebsiteRepositoryInterface; +use Magento\TestFramework\Catalog\Model\Product\Price\GetPriceIndexDataByProductId; +use Magento\TestFramework\Helper\Bootstrap; +use PHPUnit\Framework\TestCase; /** - * Class PriceTest + * Provides tests for configurable product pricing. + * * @magentoDbIsolation disabled */ -class PriceTest extends \PHPUnit\Framework\TestCase +class PriceTest extends TestCase { - /** @var \Magento\Framework\ObjectManagerInterface */ - protected $objectManager; + /** + * @var ObjectManagerInterface + */ + private $objectManager; - /** @var \Magento\Catalog\Api\Data\ProductCustomOptionInterfaceFactory */ - protected $customOptionFactory; + /** + * @var ProductRepositoryInterface + */ + private $productRepository; /** - * + * @var Price + */ + private $priceModel; + + /** + * @var GetPriceIndexDataByProductId + */ + private $getPriceIndexDataByProductId; + + /** + * @var WebsiteRepositoryInterface + */ + private $websiteRepository; + + /** + * @inheritdoc */ protected function setUp() { - $this->objectManager = \Magento\TestFramework\Helper\Bootstrap::getObjectManager(); + $this->objectManager = Bootstrap::getObjectManager(); + $this->priceModel = $this->objectManager->create(Price::class); + $this->productRepository = $this->objectManager->get(ProductRepositoryInterface::class); + $this->productRepository->cleanCache(); + $this->getPriceIndexDataByProductId = $this->objectManager->get(GetPriceIndexDataByProductId::class); + $this->websiteRepository = $this->objectManager->get(WebsiteRepositoryInterface::class); } /** * @magentoDataFixture Magento/ConfigurableProduct/_files/tax_rule.php * @magentoDataFixture Magento/ConfigurableProduct/_files/product_configurable.php - * @magentoDbIsolation disabled + * @return void */ - public function testGetFinalPrice() + public function testGetFinalPrice(): void { $this->assertPrice(10); + $this->assertIndexTableData( + 'configurable', + ['price' => 0, 'final_price' => 0, 'min_price' => 10, 'max_price' => 20, 'tier_price' => null] + ); + $this->assertIndexTableData( + 'simple_10', + ['price' => 10, 'final_price' => 10, 'min_price' => 10, 'max_price' => 10, 'tier_price' => null] + ); + $this->assertIndexTableData( + 'simple_20', + ['price' => 20, 'final_price' => 20, 'min_price' => 20, 'max_price' => 20, 'tier_price' => null] + ); } /** * @magentoConfigFixture current_store tax/display/type 1 * @magentoDataFixture Magento/ConfigurableProduct/_files/tax_rule.php * @magentoDataFixture Magento/ConfigurableProduct/_files/product_configurable.php - * @magentoDbIsolation disabled + * @return void */ - public function testGetFinalPriceExcludingTax() + public function testGetFinalPriceExcludingTax(): void { $this->assertPrice(10); } @@ -53,9 +99,9 @@ public function testGetFinalPriceExcludingTax() * @magentoConfigFixture current_store tax/display/type 2 * @magentoDataFixture Magento/ConfigurableProduct/_files/tax_rule.php * @magentoDataFixture Magento/ConfigurableProduct/_files/product_configurable.php - * @magentoDbIsolation disabled + * @return void */ - public function testGetFinalPriceIncludingTax() + public function testGetFinalPriceIncludingTax(): void { //lowest price of configurable variation + 10% $this->assertPrice(11); @@ -65,9 +111,9 @@ public function testGetFinalPriceIncludingTax() * @magentoConfigFixture current_store tax/display/type 3 * @magentoDataFixture Magento/ConfigurableProduct/_files/tax_rule.php * @magentoDataFixture Magento/ConfigurableProduct/_files/product_configurable.php - * @magentoDbIsolation disabled + * @return void */ - public function testGetFinalPriceIncludingExcludingTax() + public function testGetFinalPriceIncludingExcludingTax(): void { //lowest price of configurable variation + 10% $this->assertPrice(11); @@ -76,109 +122,78 @@ public function testGetFinalPriceIncludingExcludingTax() /** * @magentoDataFixture Magento/ConfigurableProduct/_files/tax_rule.php * @magentoDataFixture Magento/ConfigurableProduct/_files/product_configurable.php - * @magentoDbIsolation disabled + * @return void */ - public function testGetFinalPriceWithSelectedSimpleProduct() + public function testGetFinalPriceWithSelectedSimpleProduct(): void { - $product = $this->getProduct(1); - $product->addCustomOption('simple_product', 20, $this->getProduct(20)); + $product = $this->productRepository->get('configurable'); + $product->addCustomOption('simple_product', 20, $this->productRepository->get('simple_20')); $this->assertPrice(20, $product); } /** - * @magentoConfigFixture current_store tax/display/type 1 - * @magentoDataFixture Magento/ConfigurableProduct/_files/tax_rule.php - * @magentoDataFixture Magento/ConfigurableProduct/_files/product_configurable.php - * @magentoDbIsolation disabled + * @magentoDataFixture Magento/ConfigurableProduct/_files/configurable_product_with_custom_option_and_simple_tier_price.php + * @return void */ - public function testGetFinalPriceWithCustomOption() + public function testGetFinalPriceWithCustomOptionAndSimpleTierPrice(): void { - $product = $this->getProduct(1); - - $options = $this->prepareOptions( - [ - [ - 'option_id' => null, - 'previous_group' => 'text', - 'title' => 'Test Field', - 'type' => 'field', - 'is_require' => 1, - 'sort_order' => 0, - 'price' => 100, - 'price_type' => 'fixed', - 'sku' => '1-text', - 'max_characters' => 100, - ], - ], - $product + $configurable = $this->productRepository->get('configurable'); + $this->assertIndexTableData( + 'configurable', + ['price' => 0, 'final_price' => 0, 'min_price' => 9, 'max_price' => 30, 'tier_price' => 15] ); - - $product->setOptions($options); - $product->setCanSaveCustomOptions(true); - - /** @var \Magento\Catalog\Api\ProductRepositoryInterface $productRepository */ - $productRepository = $this->objectManager->get(ProductRepositoryInterface::class); - $product = $productRepository->save($product); - - $optionId = $product->getOptions()[0]->getId(); - $product->addCustomOption(AbstractType::OPTION_PREFIX . $optionId, 'text'); - $product->addCustomOption('option_ids', $optionId); - $this->assertPrice(110, $product); + $this->assertIndexTableData( + 'simple_10', + ['price' => 10, 'final_price' => 9, 'min_price' => 9, 'max_price' => 9, 'tier_price' => null] + ); + $this->assertIndexTableData( + 'simple_20', + ['price' => 20, 'final_price' => 15, 'min_price' => 15, 'max_price' => 15, 'tier_price' => 15] + ); + $optionId = $configurable->getOptions()[0]->getId(); + $configurable->addCustomOption(AbstractType::OPTION_PREFIX . $optionId, 'text'); + $configurable->addCustomOption('option_ids', $optionId); + // First simple special price (9) + Option price (15) + $this->assertPrice(24, $configurable); + $configurable->addCustomOption('simple_product', 20, $this->productRepository->get('simple_20')); + // Second simple tier price (15) + Option price (15) + $this->assertPrice(30, $configurable); } /** - * @param array $options - * @param \Magento\Catalog\Model\Product $product - * @return \Magento\Catalog\Api\Data\ProductCustomOptionInterface[] + * Asserts price data in index table. + * + * @param string $sku + * @param array $expectedPrices + * @return void */ - protected function prepareOptions($options, $product) + private function assertIndexTableData(string $sku, array $expectedPrices): void { - $preparedOptions = []; - - if (!$this->customOptionFactory) { - $this->customOptionFactory = $this->objectManager->create( - \Magento\Catalog\Api\Data\ProductCustomOptionInterfaceFactory::class - ); - } - - foreach ($options as $option) { - $option = $this->customOptionFactory->create(['data' => $option]); - $option->setProductSku($product->getSku()); - - $preparedOptions[] = $option; + $data = $this->getPriceIndexDataByProductId->execute( + (int)$this->productRepository->get($sku)->getId(), + Group::NOT_LOGGED_IN_ID, + (int)$this->websiteRepository->get('base')->getId() + ); + $data = reset($data); + foreach ($expectedPrices as $column => $price) { + $this->assertEquals($price, $data[$column], $column); } - - return $preparedOptions; } /** - * Test + * Asserts product final price. * - * @param $expectedPrice - * @param null $product + * @param float $expectedPrice + * @param ProductInterface|null $product * @return void */ - protected function assertPrice($expectedPrice, $product = null) + private function assertPrice(float $expectedPrice, ?ProductInterface $product = null): void { - $product = $product ?: $this->getProduct(1); - - /** @var $model \Magento\ConfigurableProduct\Model\Product\Type\Configurable\Price */ - $model = $this->objectManager->create( - \Magento\ConfigurableProduct\Model\Product\Type\Configurable\Price::class - ); - + $product = $product ?: $this->productRepository->get('configurable'); // final price is the lowest price of configurable variations - $this->assertEquals(round($expectedPrice, 2), round($model->getFinalPrice(1, $product), 2)); - } - - /** - * @param int $id - * @return \Magento\Catalog\Model\Product - */ - private function getProduct($id) - { - /** @var $productRepository ProductRepositoryInterface */ - $productRepository = $this->objectManager->create(ProductRepositoryInterface::class); - return $productRepository->getById($id, true, null, true); + $this->assertEquals( + round($expectedPrice, 2), + round($this->priceModel->getFinalPrice(1, $product), 2) + ); } } diff --git a/dev/tests/integration/testsuite/Magento/ConfigurableProduct/Model/Product/Type/Configurable/SalableTest.php b/dev/tests/integration/testsuite/Magento/ConfigurableProduct/Model/Product/Type/Configurable/SalableTest.php new file mode 100644 index 0000000000000..33c82dce21963 --- /dev/null +++ b/dev/tests/integration/testsuite/Magento/ConfigurableProduct/Model/Product/Type/Configurable/SalableTest.php @@ -0,0 +1,121 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +declare(strict_types=1); + +namespace Magento\ConfigurableProduct\Model\Product\Type\Configurable; + +use Magento\Catalog\Api\ProductRepositoryInterface; +use Magento\Catalog\Model\Product\Attribute\Source\Status; +use Magento\CatalogInventory\Api\Data\StockStatusInterface; +use Magento\Framework\ObjectManagerInterface; +use Magento\TestFramework\Helper\Bootstrap; +use PHPUnit\Framework\TestCase; + +/** + * Check is configurable product salable with different conditions + * + * @magentoAppArea frontend + */ +class SalableTest extends TestCase +{ + /** @var ObjectManagerInterface */ + private $objectManager; + + /** @var ProductRepositoryInterface */ + private $productRepository; + + /** + * @inheritdoc + */ + protected function setUp() + { + parent::setUp(); + + $this->objectManager = Bootstrap::getObjectManager(); + $this->productRepository = $this->objectManager->get(ProductRepositoryInterface::class); + $this->productRepository->cleanCache(); + } + + /** + * @magentoDataFixture Magento/ConfigurableProduct/_files/product_configurable.php + * + * @dataProvider salableDataProvider + * + * @param array $productSkus + * @param array $productData + * @param bool $expectedValue + * @return void + */ + public function testIsSalable(array $productSkus, array $productData, bool $expectedValue): void + { + $this->updateProduct($productSkus, $productData); + $configurableProduct = $this->productRepository->get('configurable', false, null, true); + + $this->assertEquals($expectedValue, $configurableProduct->getIsSalable()); + } + + /** + * @return array + */ + public function salableDataProvider(): array + { + return [ + 'all children enabled_and_in_stock' => [ + 'product_skus' => [], + 'data' => [], + 'expected_value' => true, + ], + 'one_child_out_of_stock' => [ + 'product_skus' => ['simple_10'], + 'data' => [ + 'stock_data' => [ + 'use_config_manage_stock' => 1, + 'is_in_stock' => StockStatusInterface::STATUS_OUT_OF_STOCK, + ], + ], + 'expected_value' => true, + ], + 'one_child_disabled' => [ + 'product_skus' => ['simple_10'], + 'data' => ['status' => Status::STATUS_DISABLED], + 'expected_value' => true, + ], + 'all_children_disabled' => [ + 'product_skus' => ['simple_10', 'simple_20'], + 'data' => ['status' => Status::STATUS_DISABLED], + 'expected_value' => false, + ], + 'all_children_out_of_stock' => [ + 'product_skus' => ['simple_10', 'simple_20'], + 'data' => [ + 'stock_data' => [ + 'use_config_manage_stock' => 1, + 'is_in_stock' => StockStatusInterface::STATUS_OUT_OF_STOCK, + ], + ], + 'expected_value' => false, + ] + ]; + } + + /** + * Update product with data + * + * @param array $skus + * @param array $data + * @return void + */ + private function updateProduct(array $skus, array $data): void + { + if (!empty($skus)) { + foreach ($skus as $sku) { + $product = $this->productRepository->get($sku); + $product->addData($data); + $this->productRepository->save($product); + } + } + } +} diff --git a/dev/tests/integration/testsuite/Magento/ConfigurableProduct/Model/Product/VariationHandlerTest.php b/dev/tests/integration/testsuite/Magento/ConfigurableProduct/Model/Product/VariationHandlerTest.php index fc2e99d6a9d10..c3b845694c6a0 100644 --- a/dev/tests/integration/testsuite/Magento/ConfigurableProduct/Model/Product/VariationHandlerTest.php +++ b/dev/tests/integration/testsuite/Magento/ConfigurableProduct/Model/Product/VariationHandlerTest.php @@ -3,78 +3,105 @@ * Copyright © Magento, Inc. All rights reserved. * See COPYING.txt for license details. */ +declare(strict_types=1); namespace Magento\ConfigurableProduct\Model\Product; +use Magento\Catalog\Api\Data\ProductInterface; +use Magento\Catalog\Api\ProductRepositoryInterface; +use Magento\CatalogInventory\Api\StockRegistryInterface; +use Magento\ConfigurableProduct\Model\Product\Type\Configurable; +use Magento\Framework\ObjectManagerInterface; use Magento\TestFramework\Helper\Bootstrap; +use PHPUnit\Framework\TestCase; /** + * Tests for simple products generation by saving a configurable product. + * * @magentoAppIsolation enabled - * @magentoDataFixture Magento/ConfigurableProduct/_files/product_configurable.php + * @magentoDbIsolation enabled */ -class VariationHandlerTest extends \PHPUnit\Framework\TestCase +class VariationHandlerTest extends TestCase { - /** @var \Magento\ConfigurableProduct\Model\Product\VariationHandler */ - private $_model; + /** + * @var ObjectManagerInterface + */ + private $objectManager; - /** @var \Magento\Catalog\Model\Product */ - private $_product; + /** + * @var VariationHandler + */ + private $variationHandler; - /** @var \Magento\CatalogInventory\Api\StockRegistryInterface */ + /** + * @var ProductInterface + */ + private $product; + + /** + * @var ProductRepositoryInterface + */ + private $productRepository; + + /** + * @var StockRegistryInterface + */ private $stockRegistry; + /** + * @inheritdoc + */ protected function setUp() { - $this->_product = Bootstrap::getObjectManager()->create( - \Magento\Catalog\Model\Product::class - ); - $this->_product->load(1); - - $this->_model = Bootstrap::getObjectManager()->create( - \Magento\ConfigurableProduct\Model\Product\VariationHandler::class - ); - // prevent fatal errors by assigning proper "singleton" of type instance to the product - $this->_product->setTypeInstance($this->_model); - $this->stockRegistry = Bootstrap::getObjectManager()->get( - \Magento\CatalogInventory\Api\StockRegistryInterface::class - ); + $this->objectManager = Bootstrap::getObjectManager(); + $this->productRepository = $this->objectManager->get(ProductRepositoryInterface::class); + $this->productRepository->cleanCache(); + $this->variationHandler = $this->objectManager->create(VariationHandler::class); + $this->product = $this->productRepository->get('configurable'); + $this->stockRegistry = $this->objectManager->get(StockRegistryInterface::class); } /** - * @param array $productsData + * @magentoDataFixture Magento/ConfigurableProduct/_files/product_configurable.php * @dataProvider generateSimpleProductsDataProvider + * @param array $productsData + * @return void */ - public function testGenerateSimpleProducts($productsData) + public function testGenerateSimpleProducts(array $productsData): void { - $this->_product->setNewVariationsAttributeSetId(4); - // Default attribute set id - $generatedProducts = $this->_model->generateSimpleProducts($this->_product, $productsData); + $this->product->setImage('some_test_image.jpg') + ->setSmallImage('some_test_image.jpg') + ->setThumbnail('some_test_image.jpg') + ->setSwatchImage('some_test_image.jpg') + ->setNewVariationsAttributeSetId($this->product->getDefaultAttributeSetId()); + $generatedProducts = $this->variationHandler->generateSimpleProducts($this->product, $productsData); $this->assertEquals(3, count($generatedProducts)); foreach ($generatedProducts as $productId) { $stockItem = $this->stockRegistry->getStockItem($productId); - /** @var $product \Magento\Catalog\Model\Product */ - $product = Bootstrap::getObjectManager()->create( - \Magento\Catalog\Model\Product::class - ); - $product->load($productId); + $product = $this->productRepository->getById($productId); $this->assertNotNull($product->getName()); $this->assertNotNull($product->getSku()); $this->assertNotNull($product->getPrice()); $this->assertNotNull($product->getWeight()); $this->assertEquals('1', $stockItem->getIsInStock()); + $this->assertNull($product->getImage()); + $this->assertNull($product->getSmallImage()); + $this->assertNull($product->getThumbnail()); + $this->assertNull($product->getSwatchImage()); } } /** * @param array $productsData + * @magentoDataFixture Magento/ConfigurableProduct/_files/product_configurable.php * @dataProvider generateSimpleProductsWithPartialDataDataProvider - * @magentoDbIsolation enabled + * @return void */ - public function testGenerateSimpleProductsWithPartialData($productsData) + public function testGenerateSimpleProductsWithPartialData(array $productsData): void { - $this->_product->setNewVariationsAttributeSetId(4); - $generatedProducts = $this->_model->generateSimpleProducts($this->_product, $productsData); - $parentStockItem = $this->stockRegistry->getStockItem($this->_product->getId()); + $this->product->setNewVariationsAttributeSetId(4); + $generatedProducts = $this->variationHandler->generateSimpleProducts($this->product, $productsData); + $parentStockItem = $this->stockRegistry->getStockItem($this->product->getId()); foreach ($generatedProducts as $productId) { $stockItem = $this->stockRegistry->getStockItem($productId); $this->assertEquals($parentStockItem->getManageStock(), $stockItem->getManageStock()); @@ -85,7 +112,7 @@ public function testGenerateSimpleProductsWithPartialData($productsData) /** * @return array */ - public static function generateSimpleProductsDataProvider() + public function generateSimpleProductsDataProvider(): array { return [ [ @@ -122,7 +149,7 @@ public static function generateSimpleProductsDataProvider() /** * @return array */ - public static function generateSimpleProductsWithPartialDataDataProvider() + public function generateSimpleProductsWithPartialDataDataProvider(): array { return [ [ diff --git a/dev/tests/integration/testsuite/Magento/ConfigurableProduct/Model/QuickSearchTest.php b/dev/tests/integration/testsuite/Magento/ConfigurableProduct/Model/QuickSearchTest.php new file mode 100644 index 0000000000000..701fb2c17d966 --- /dev/null +++ b/dev/tests/integration/testsuite/Magento/ConfigurableProduct/Model/QuickSearchTest.php @@ -0,0 +1,168 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +declare(strict_types=1); + +namespace Magento\ConfigurableProduct\Model; + +use Magento\Catalog\Api\ProductRepositoryInterface; +use Magento\Catalog\Model\Product; +use Magento\Catalog\Model\Product\Visibility; +use Magento\TestFramework\Catalog\Model\Layer\QuickSearchByQuery; +use Magento\TestFramework\Helper\Bootstrap; +use Magento\TestFramework\ObjectManager; +use PHPUnit\Framework\TestCase; + +/** + * Test cases related to find configurable product via quick search using search engine. + * + * @magentoDataFixture Magento/ConfigurableProduct/_files/configurable_product_with_two_child_products.php + * @magentoDataFixture Magento/CatalogSearch/_files/full_reindex.php + * + * @magentoDbIsolation disabled + * @magentoAppIsolation enabled + */ +class QuickSearchTest extends TestCase +{ + /** + * @var ObjectManager + */ + private $objectManager; + + /** + * @var QuickSearchByQuery + */ + private $quickSearchByQuery; + + /** + * @var ProductRepositoryInterface + */ + private $productRepository; + + /** + * @inheritdoc + */ + protected function setUp() + { + $this->objectManager = Bootstrap::getObjectManager(); + $this->quickSearchByQuery = $this->objectManager->get(QuickSearchByQuery::class); + $this->productRepository = $this->objectManager->get(ProductRepositoryInterface::class); + parent::setUp(); + } + + /** + * Assert that configurable child products has not found by query using mysql search engine. + * + * @magentoConfigFixture default/catalog/search/engine mysql + * + * @return void + */ + public function testChildProductsHasNotFoundedByQuery(): void + { + $this->checkThatOnlyConfigurableProductIsAvailableBySearch('Configurable Option'); + } + + /** + * Assert that child product of configurable will be available by search after + * set to product visibility by catalog and search using mysql search engine. + * + * @dataProvider productAvailabilityInSearchByVisibilityDataProvider + * + * @param int $visibility + * @param bool $expectedResult + * @return void + */ + public function testOneOfChildIsAvailableBySearch(int $visibility, bool $expectedResult): void + { + $this->checkThatOnlyConfigurableProductIsAvailableBySearch('Configurable Option'); + $this->updateProductVisibility($visibility); + $this->checkProductAvailabilityInSearch($expectedResult); + $this->checkThatOnlyConfigurableProductIsAvailableBySearch('White'); + } + + /** + * Return data with product visibility and expected result. + * + * @return array + */ + public function productAvailabilityInSearchByVisibilityDataProvider(): array + { + return [ + 'visible_catalog_only' => [ + Visibility::VISIBILITY_IN_CATALOG, + false, + ], + 'visible_catalog_and_search' => [ + Visibility::VISIBILITY_BOTH, + true, + ], + 'visible_search_only' => [ + Visibility::VISIBILITY_IN_SEARCH, + true, + ], + 'visible_search_not_visible_individuality' => [ + Visibility::VISIBILITY_NOT_VISIBLE, + false, + ], + ]; + } + + /** + * Assert that configurable product was found by option value using mysql search engine. + * + * + * @return void + */ + public function testSearchByOptionValue(): void + { + $this->checkThatOnlyConfigurableProductIsAvailableBySearch('Option 1'); + } + + /** + * Assert that anyone child product is not available by quick search. + * + * @param string $searchQuery + * + * @return void + */ + private function checkThatOnlyConfigurableProductIsAvailableBySearch(string $searchQuery): void + { + $searchResult = $this->quickSearchByQuery->execute($searchQuery); + $this->assertCount(1, $searchResult->getItems()); + /** @var Product $configurableProduct */ + $configurableProduct = $searchResult->getFirstItem(); + $this->assertEquals('Configurable product', $configurableProduct->getSku()); + } + + /** + * Update product visibility. + * + * @param int $visibility + * @return void + */ + private function updateProductVisibility(int $visibility): void + { + $childProduct = $this->productRepository->get('Simple option 1'); + $childProduct->setVisibility($visibility); + $this->productRepository->save($childProduct); + } + + /** + * Assert that configurable and one of child product is available by search. + * + * @param bool $firstChildIsVisible + * @return void + */ + private function checkProductAvailabilityInSearch(bool $firstChildIsVisible): void + { + $searchResult = $this->quickSearchByQuery->execute('Black'); + $this->assertNotNull($searchResult->getItemByColumnValue(Product::SKU, 'Configurable product')); + $this->assertEquals( + $firstChildIsVisible, + (bool)$searchResult->getItemByColumnValue(Product::SKU, 'Simple option 1') + ); + $this->assertNull($searchResult->getItemByColumnValue(Product::SKU, 'Simple option 2')); + } +} diff --git a/dev/tests/integration/testsuite/Magento/ConfigurableProduct/_files/configurable_attribute_different_labels_per_stores.php b/dev/tests/integration/testsuite/Magento/ConfigurableProduct/_files/configurable_attribute_different_labels_per_stores.php new file mode 100644 index 0000000000000..0d99869d4adf9 --- /dev/null +++ b/dev/tests/integration/testsuite/Magento/ConfigurableProduct/_files/configurable_attribute_different_labels_per_stores.php @@ -0,0 +1,82 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +declare(strict_types=1); + +use Magento\Catalog\Api\Data\ProductAttributeInterface; +use Magento\Catalog\Api\ProductAttributeRepositoryInterface; +use Magento\Catalog\Model\ResourceModel\Eav\Attribute; +use Magento\Catalog\Model\ResourceModel\Eav\AttributeFactory; +use Magento\Catalog\Setup\CategorySetup; +use Magento\Eav\Model\Entity\Attribute\ScopedAttributeInterface; +use Magento\Store\Model\Store; +use Magento\TestFramework\Helper\Bootstrap; + +require __DIR__ . '/../../Store/_files/core_fixturestore.php'; + +$objectManager = Bootstrap::getObjectManager(); +$defaultInstalledStoreId = $storeManager->getStore('default')->getId(); +$secondStoreId = $storeManager->getStore('fixturestore')->getId(); +/** @var CategorySetup $installer */ +$installer = $objectManager->get(CategorySetup::class); +/** @var Attribute $attribute */ +$attribute = $objectManager->get(AttributeFactory::class)->create(); +/** @var ProductAttributeRepositoryInterface $attributeRepository */ +$attributeRepository = $objectManager->get(ProductAttributeRepositoryInterface::class); +$entityType = $installer->getEntityTypeId(ProductAttributeInterface::ENTITY_TYPE_CODE); +if (!$attribute->loadByCode($entityType, 'different_labels_attribute')->getAttributeId()) { + $attribute->setData( + [ + 'frontend_label' => ['Different option labels dropdown attribute'], + 'entity_type_id' => $entityType, + 'frontend_input' => 'select', + 'backend_type' => 'int', + 'is_required' => '0', + 'attribute_code' => 'different_labels_attribute', + 'is_global' => ScopedAttributeInterface::SCOPE_GLOBAL, + 'is_user_defined' => 1, + 'is_unique' => '0', + 'is_searchable' => '0', + 'is_comparable' => '0', + 'is_filterable' => '1', + 'is_filterable_in_search' => '0', + 'is_used_for_promo_rules' => '0', + 'is_html_allowed_on_front' => '1', + 'used_in_product_listing' => '1', + 'used_for_sort_by' => '0', + 'option' => [ + 'value' => [ + 'option_1' => [ + Store::DEFAULT_STORE_ID => 'Option 1', + $defaultInstalledStoreId => 'Option 1 Default Store', + $secondStoreId => 'Option 1 Second Store', + ], + 'option_2' => [ + Store::DEFAULT_STORE_ID => 'Option 2', + $defaultInstalledStoreId => 'Option 2 Default Store', + $secondStoreId => 'Option 2 Second Store', + ], + 'option_3' => [ + Store::DEFAULT_STORE_ID => 'Option 3', + $defaultInstalledStoreId => 'Option 3 Default Store', + $secondStoreId => 'Option 3 Second Store', + ], + ], + 'order' => [ + 'option_1' => 1, + 'option_2' => 2, + 'option_3' => 3, + ], + ], + ] + ); + $attributeRepository->save($attribute); + $installer->addAttributeToGroup( + ProductAttributeInterface::ENTITY_TYPE_CODE, + 'Default', + 'General', + $attribute->getId() + ); +} diff --git a/dev/tests/integration/testsuite/Magento/ConfigurableProduct/_files/configurable_attribute_different_labels_per_stores_rollback.php b/dev/tests/integration/testsuite/Magento/ConfigurableProduct/_files/configurable_attribute_different_labels_per_stores_rollback.php new file mode 100644 index 0000000000000..f69545c831a98 --- /dev/null +++ b/dev/tests/integration/testsuite/Magento/ConfigurableProduct/_files/configurable_attribute_different_labels_per_stores_rollback.php @@ -0,0 +1,30 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +declare(strict_types=1); + +use Magento\Framework\Exception\NoSuchEntityException; +use Magento\TestFramework\Helper\Bootstrap; +use Magento\Framework\Registry; +use Magento\Catalog\Api\ProductAttributeRepositoryInterface; + +$objectManager = Bootstrap::getObjectManager(); +/** @var Registry $registry */ +$registry = $objectManager->get(Registry::class); +$registry->unregister('isSecureArea'); +$registry->register('isSecureArea', true); +/** @var ProductAttributeRepositoryInterface $attributeRepository */ +$attributeRepository = $objectManager->get(ProductAttributeRepositoryInterface::class); + +try { + $attributeRepository->deleteById('different_labels_attribute'); +} catch (NoSuchEntityException $e) { + //already deleted +} + +$registry->unregister('isSecureArea'); +$registry->register('isSecureArea', false); + +require __DIR__ . '/../../Store/_files/core_fixturestore_rollback.php'; diff --git a/dev/tests/integration/testsuite/Magento/ConfigurableProduct/_files/configurable_product_different_option_labeles_per_stores.php b/dev/tests/integration/testsuite/Magento/ConfigurableProduct/_files/configurable_product_different_option_labeles_per_stores.php new file mode 100644 index 0000000000000..c4498c6beae4e --- /dev/null +++ b/dev/tests/integration/testsuite/Magento/ConfigurableProduct/_files/configurable_product_different_option_labeles_per_stores.php @@ -0,0 +1,92 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +declare(strict_types=1); + +use Magento\Catalog\Api\Data\ProductExtensionFactory; +use Magento\Catalog\Api\ProductAttributeRepositoryInterface; +use Magento\Catalog\Api\ProductRepositoryInterface; +use Magento\Catalog\Model\Product\Attribute\Source\Status; +use Magento\Catalog\Model\Product\Type as ProductType; +use Magento\Catalog\Model\Product\Visibility; +use Magento\Catalog\Model\ProductFactory; +use Magento\ConfigurableProduct\Helper\Product\Options\Factory; +use Magento\ConfigurableProduct\Model\Product\Type\Configurable; +use Magento\Store\Api\WebsiteRepositoryInterface; +use Magento\TestFramework\Helper\Bootstrap; + +require __DIR__ . '/configurable_attribute_different_labels_per_stores.php'; + +$objectManager = Bootstrap::getObjectManager(); +/** @var ProductAttributeRepositoryInterface $productAttributeRepository */ +$productAttributeRepository = $objectManager->get(ProductAttributeRepositoryInterface::class); +$attribute = $productAttributeRepository->get('different_labels_attribute'); +$options = $attribute->getOptions(); +/** @var WebsiteRepositoryInterface $websiteRepository */ +$websiteRepository = $objectManager->get(WebsiteRepositoryInterface::class); +$baseWebsite = $websiteRepository->get('base'); +/** @var ProductRepositoryInterface $productRepository */ +$productRepository = $objectManager->get(ProductRepositoryInterface::class); +$productRepository->cleanCache(); +/** @var ProductFactory $productFactory */ +$productFactory = $objectManager->get(ProductFactory::class); +$attributeValues = []; +$associatedProductIds = []; +$rootCategoryId = $baseWebsite->getDefaultStore()->getRootCategoryId(); +array_shift($options); + +foreach ($options as $option) { + $product = $productFactory->create(); + $product->setTypeId(ProductType::TYPE_SIMPLE) + ->setAttributeSetId($product->getDefaultAttributeSetId()) + ->setWebsiteIds([$baseWebsite->getId()]) + ->setName('Configurable Option ' . $option->getLabel()) + ->setSku(strtolower(str_replace(' ', '_', 'simple ' . $option->getLabel()))) + ->setPrice(150) + ->setDifferentLabelsAttribute($option->getValue()) + ->setVisibility(Visibility::VISIBILITY_NOT_VISIBLE) + ->setStatus(Status::STATUS_ENABLED) + ->setCategoryIds([$rootCategoryId]) + ->setStockData(['use_config_manage_stock' => 1, 'qty' => 100, 'is_qty_decimal' => 0, 'is_in_stock' => 1]); + $product = $productRepository->save($product); + + $attributeValues[] = [ + 'label' => 'test', + 'attribute_id' => $attribute->getId(), + 'value_index' => $option->getValue(), + ]; + $associatedProductIds[] = $product->getId(); +} +/** @var Factory $optionsFactory */ +$optionsFactory = $objectManager->get(Factory::class); +$configurableAttributesData = [ + [ + 'attribute_id' => $attribute->getId(), + 'code' => $attribute->getAttributeCode(), + 'label' => $attribute->getStoreLabel(), + 'position' => '0', + 'values' => $attributeValues, + ], +]; +$configurableOptions = $optionsFactory->create($configurableAttributesData); + +$product = $productFactory->create(); +/** @var ProductExtensionFactory $extensionAttributesFactory */ +$extensionAttributesFactory = $objectManager->get(ProductExtensionFactory::class); +$extensionConfigurableAttributes = $product->getExtensionAttributes() ?: $extensionAttributesFactory->create(); +$extensionConfigurableAttributes->setConfigurableProductOptions($configurableOptions); +$extensionConfigurableAttributes->setConfigurableProductLinks($associatedProductIds); +$product->setExtensionAttributes($extensionConfigurableAttributes); + +$product->setTypeId(Configurable::TYPE_CODE) + ->setAttributeSetId($product->getDefaultAttributeSetId()) + ->setWebsiteIds([$baseWebsite->getId()]) + ->setName('Configurable Product') + ->setSku('configurable') + ->setVisibility(Visibility::VISIBILITY_BOTH) + ->setStatus(Status::STATUS_ENABLED) + ->setCategoryIds([$rootCategoryId]) + ->setStockData(['use_config_manage_stock' => 1, 'is_in_stock' => 1]); +$productRepository->save($product); diff --git a/dev/tests/integration/testsuite/Magento/ConfigurableProduct/_files/configurable_product_different_option_labeles_per_stores_rollback.php b/dev/tests/integration/testsuite/Magento/ConfigurableProduct/_files/configurable_product_different_option_labeles_per_stores_rollback.php new file mode 100644 index 0000000000000..c82da5f653bd8 --- /dev/null +++ b/dev/tests/integration/testsuite/Magento/ConfigurableProduct/_files/configurable_product_different_option_labeles_per_stores_rollback.php @@ -0,0 +1,16 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +declare(strict_types=1); + +use Magento\TestFramework\ConfigurableProduct\Model\DeleteConfigurableProduct; +use Magento\TestFramework\Helper\Bootstrap; + +$objectManager = Bootstrap::getObjectManager(); +/** @var DeleteConfigurableProduct $deleteConfigurableProduct */ +$deleteConfigurableProduct = $objectManager->get(DeleteConfigurableProduct::class); +$deleteConfigurableProduct->execute('configurable'); + +require __DIR__ . '/configurable_attribute_different_labels_per_stores_rollback.php'; diff --git a/dev/tests/integration/testsuite/Magento/ConfigurableProduct/_files/configurable_product_two_websites.php b/dev/tests/integration/testsuite/Magento/ConfigurableProduct/_files/configurable_product_two_websites.php new file mode 100644 index 0000000000000..17837deb15a03 --- /dev/null +++ b/dev/tests/integration/testsuite/Magento/ConfigurableProduct/_files/configurable_product_two_websites.php @@ -0,0 +1,94 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +declare(strict_types=1); + +use Magento\Catalog\Api\Data\ProductExtensionFactory; +use Magento\Catalog\Api\ProductAttributeRepositoryInterface; +use Magento\Catalog\Api\ProductRepositoryInterface; +use Magento\Catalog\Model\Product\Attribute\Source\Status; +use Magento\Catalog\Model\Product\Type as ProductType; +use Magento\Catalog\Model\Product\Visibility; +use Magento\Catalog\Model\ProductFactory; +use Magento\ConfigurableProduct\Helper\Product\Options\Factory; +use Magento\ConfigurableProduct\Model\Product\Type\Configurable; +use Magento\Store\Api\WebsiteRepositoryInterface; +use Magento\TestFramework\Helper\Bootstrap; + +require __DIR__ . '/../../Store/_files/second_website_with_two_stores.php'; +require __DIR__ . '/configurable_attribute.php'; + +$objectManager = Bootstrap::getObjectManager(); +/** @var ProductAttributeRepositoryInterface $productAttributeRepository */ +$productAttributeRepository = $objectManager->get(ProductAttributeRepositoryInterface::class); +$attribute = $productAttributeRepository->get('test_configurable'); +$options = $attribute->getOptions(); +/** @var WebsiteRepositoryInterface $websiteRepository */ +$websiteRepository = $objectManager->get(WebsiteRepositoryInterface::class); +$baseWebsite = $websiteRepository->get('base'); +$secondWebsite = $websiteRepository->get('test'); +/** @var ProductRepositoryInterface $productRepository */ +$productRepository = $objectManager->get(ProductRepositoryInterface::class); +$productRepository->cleanCache(); +/** @var ProductFactory $productFactory */ +$productFactory = $objectManager->get(ProductFactory::class); +$attributeValues = []; +$associatedProductIds = []; +$rootCategoryId = $baseWebsite->getDefaultStore()->getRootCategoryId(); +array_shift($options); + +foreach ($options as $option) { + $product = $productFactory->create(); + $product->setTypeId(ProductType::TYPE_SIMPLE) + ->setAttributeSetId($product->getDefaultAttributeSetId()) + ->setWebsiteIds([$baseWebsite->getId(), $secondWebsite->getId()]) + ->setName('Configurable Option ' . $option->getLabel()) + ->setSku(strtolower(str_replace(' ', '_', 'simple ' . $option->getLabel()))) + ->setPrice(150) + ->setTestConfigurable($option->getValue()) + ->setVisibility(Visibility::VISIBILITY_NOT_VISIBLE) + ->setStatus(Status::STATUS_ENABLED) + ->setCategoryIds([$rootCategoryId]) + ->setStockData(['use_config_manage_stock' => 1, 'qty' => 100, 'is_qty_decimal' => 0, 'is_in_stock' => 1]); + $product = $productRepository->save($product); + + $attributeValues[] = [ + 'label' => 'test', + 'attribute_id' => $attribute->getId(), + 'value_index' => $option->getValue(), + ]; + $associatedProductIds[] = $product->getId(); +} +/** @var Factory $optionsFactory */ +$optionsFactory = $objectManager->get(Factory::class); +$configurableAttributesData = [ + [ + 'attribute_id' => $attribute->getId(), + 'code' => $attribute->getAttributeCode(), + 'label' => $attribute->getStoreLabel(), + 'position' => '0', + 'values' => $attributeValues, + ], +]; +$configurableOptions = $optionsFactory->create($configurableAttributesData); + +$product = $productFactory->create(); +/** @var ProductExtensionFactory $extensionAttributesFactory */ +$extensionAttributesFactory = $objectManager->get(ProductExtensionFactory::class); +$extensionConfigurableAttributes = $product->getExtensionAttributes() ?: $extensionAttributesFactory->create(); +$extensionConfigurableAttributes->setConfigurableProductOptions($configurableOptions); +$extensionConfigurableAttributes->setConfigurableProductLinks($associatedProductIds); +$product->setExtensionAttributes($extensionConfigurableAttributes); + +$product->setTypeId(Configurable::TYPE_CODE) + ->setAttributeSetId($product->getDefaultAttributeSetId()) + ->setWebsiteIds([$baseWebsite->getId(), $secondWebsite->getId()]) + ->setName('Configurable Product') + ->setSku('configurable') + ->setVisibility(Visibility::VISIBILITY_BOTH) + ->setStatus(Status::STATUS_ENABLED) + ->setCategoryIds([$rootCategoryId]) + ->setStockData(['use_config_manage_stock' => 1, 'is_in_stock' => 1]); +$productRepository->save($product); diff --git a/dev/tests/integration/testsuite/Magento/ConfigurableProduct/_files/configurable_product_two_websites_rollback.php b/dev/tests/integration/testsuite/Magento/ConfigurableProduct/_files/configurable_product_two_websites_rollback.php new file mode 100644 index 0000000000000..78e3109352693 --- /dev/null +++ b/dev/tests/integration/testsuite/Magento/ConfigurableProduct/_files/configurable_product_two_websites_rollback.php @@ -0,0 +1,17 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +declare(strict_types=1); + +use Magento\TestFramework\ConfigurableProduct\Model\DeleteConfigurableProduct; +use Magento\TestFramework\Helper\Bootstrap; + +$objectManager = Bootstrap::getObjectManager(); +/** @var DeleteConfigurableProduct $deleteConfigurableProduct */ +$deleteConfigurableProduct = $objectManager->get(DeleteConfigurableProduct::class); +$deleteConfigurableProduct->execute('configurable'); + +require __DIR__ . '/configurable_attribute_rollback.php'; +require __DIR__ . '/../../Store/_files/second_website_with_two_stores_rollback.php'; diff --git a/dev/tests/integration/testsuite/Magento/ConfigurableProduct/_files/configurable_product_with_category.php b/dev/tests/integration/testsuite/Magento/ConfigurableProduct/_files/configurable_product_with_category.php new file mode 100644 index 0000000000000..0db8b8b097644 --- /dev/null +++ b/dev/tests/integration/testsuite/Magento/ConfigurableProduct/_files/configurable_product_with_category.php @@ -0,0 +1,23 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +declare(strict_types=1); + +use Magento\Catalog\Api\CategoryLinkManagementInterface; +use Magento\Catalog\Helper\DefaultCategory; +use Magento\TestFramework\Helper\Bootstrap; + +require __DIR__ . '/../../Catalog/_files/category.php'; +require __DIR__ . '/product_configurable.php'; + +$objectManager = Bootstrap::getObjectManager(); +/** @var CategoryLinkManagementInterface $categoryLinkManagement */ +$categoryLinkManagement = $objectManager->create(CategoryLinkManagementInterface::class); +/** @var DefaultCategory $categoryHelper */ +$categoryHelper = $objectManager->get(DefaultCategory::class); + +foreach (['simple_10', 'simple_20', 'configurable'] as $sku) { + $categoryLinkManagement->assignProductToCategories($sku, [$categoryHelper->getId(), 333]); +} diff --git a/dev/tests/integration/testsuite/Magento/ConfigurableProduct/_files/configurable_product_with_category_rollback.php b/dev/tests/integration/testsuite/Magento/ConfigurableProduct/_files/configurable_product_with_category_rollback.php new file mode 100644 index 0000000000000..36d070f566bb4 --- /dev/null +++ b/dev/tests/integration/testsuite/Magento/ConfigurableProduct/_files/configurable_product_with_category_rollback.php @@ -0,0 +1,9 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +declare(strict_types=1); + +require __DIR__ . '/../../Catalog/_files/category_rollback.php'; +require __DIR__ . '/product_configurable_rollback.php'; diff --git a/dev/tests/integration/testsuite/Magento/ConfigurableProduct/_files/configurable_product_with_child_products_with_images.php b/dev/tests/integration/testsuite/Magento/ConfigurableProduct/_files/configurable_product_with_child_products_with_images.php new file mode 100644 index 0000000000000..d2418ccaaddec --- /dev/null +++ b/dev/tests/integration/testsuite/Magento/ConfigurableProduct/_files/configurable_product_with_child_products_with_images.php @@ -0,0 +1,62 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +declare(strict_types=1); + +use Magento\Catalog\Api\ProductRepositoryInterface; +use Magento\Catalog\Model\Product; +use Magento\Store\Model\Store; +use Magento\TestFramework\Helper\Bootstrap; + +require __DIR__ . '/../../../Magento/Catalog/_files/product_image.php'; +require __DIR__ . '/product_configurable.php'; + +$objectManager = Bootstrap::getObjectManager(); +$productRepository = $objectManager->create(ProductRepositoryInterface::class); +$firstSimple = $productRepository->get('simple_10'); +$secondSimple = $productRepository->get('simple_20'); +/** @var $firstSimple Product */ +$firstSimple->setStoreId(Store::DEFAULT_STORE_ID) + ->setImage('/m/a/magento_image.jpg') + ->setSmallImage('/m/a/magento_image.jpg') + ->setThumbnail('/m/a/magento_image.jpg') + ->setData( + 'media_gallery', + [ + 'images' => [ + [ + 'file' => '/m/a/magento_image.jpg', + 'position' => 1, + 'label' => 'Image Alt Text', + 'disabled' => 0, + 'media_type' => 'image' + ], + ] + ] + ) + ->setCanSaveCustomOptions(true) + ->save(); +/** @var $secondSimple Product */ +$secondSimple->setStoreId(Store::DEFAULT_STORE_ID) + ->setImage('/m/a/magento_thumbnail.jpg') + ->setSmallImage('/m/a/magento_thumbnail.jpg') + ->setThumbnail('/m/a/magento_thumbnail.jpg') + ->setSwatchImage('/m/a/magento_thumbnail.jpg') + ->setData( + 'media_gallery', + [ + 'images' => [ + [ + 'file' => '/m/a/magento_thumbnail.jpg', + 'position' => 2, + 'label' => 'Thumbnail Image', + 'disabled' => 0, + 'media_type' => 'image' + ], + ] + ] + ) + ->setCanSaveCustomOptions(true) + ->save(); diff --git a/dev/tests/integration/testsuite/Magento/ConfigurableProduct/_files/configurable_product_with_child_products_with_images_rollback.php b/dev/tests/integration/testsuite/Magento/ConfigurableProduct/_files/configurable_product_with_child_products_with_images_rollback.php new file mode 100644 index 0000000000000..11350999ae7aa --- /dev/null +++ b/dev/tests/integration/testsuite/Magento/ConfigurableProduct/_files/configurable_product_with_child_products_with_images_rollback.php @@ -0,0 +1,9 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +declare(strict_types=1); + +require __DIR__ . '/product_configurable_rollback.php'; +require __DIR__ . '/../../../Magento/Catalog/_files/product_image_rollback.php'; diff --git a/dev/tests/integration/testsuite/Magento/ConfigurableProduct/_files/configurable_product_with_custom_option_and_simple_tier_price.php b/dev/tests/integration/testsuite/Magento/ConfigurableProduct/_files/configurable_product_with_custom_option_and_simple_tier_price.php new file mode 100644 index 0000000000000..994d1d0f27583 --- /dev/null +++ b/dev/tests/integration/testsuite/Magento/ConfigurableProduct/_files/configurable_product_with_custom_option_and_simple_tier_price.php @@ -0,0 +1,34 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +declare(strict_types=1); + +use Magento\Catalog\Api\Data\ProductTierPriceExtensionFactory; +use Magento\Catalog\Api\Data\ProductTierPriceInterfaceFactory; +use Magento\Customer\Model\Group; +use Magento\Store\Api\WebsiteRepositoryInterface; + +require __DIR__ . '/product_configurable_with_custom_option_type_text.php'; + +/** @var WebsiteRepositoryInterface $websiteRepository */ +$websiteRepository = $objectManager->get(WebsiteRepositoryInterface::class); +/** @var ProductTierPriceInterfaceFactory $tierPriceFactory */ +$tierPriceFactory = $objectManager->get(ProductTierPriceInterfaceFactory::class); +/** @var ProductTierPriceExtensionFactory $tpExtensionAttributeFactory */ +$tpExtensionAttributeFactory = $objectManager->get(ProductTierPriceExtensionFactory::class); + +$firstSimple = $productRepository->get('simple_10'); +$firstSimple->setSpecialPrice(9); +$productRepository->save($firstSimple); + +$secondSimple = $productRepository->get('simple_20'); +$tierPriceExtensionAttribute = $tpExtensionAttributeFactory->create( + ['data' => ['website_id' => $websiteRepository->get('admin')->getId(), 'percentage_value' => 25]] +); +$tierPrices[] = $tierPriceFactory + ->create(['data' => ['customer_group_id' => Group::CUST_GROUP_ALL, 'qty' => 1]]) + ->setExtensionAttributes($tierPriceExtensionAttribute); +$secondSimple->setTierPrices($tierPrices); +$productRepository->save($secondSimple); diff --git a/dev/tests/integration/testsuite/Magento/ConfigurableProduct/_files/configurable_product_with_custom_option_and_simple_tier_price_rollback.php b/dev/tests/integration/testsuite/Magento/ConfigurableProduct/_files/configurable_product_with_custom_option_and_simple_tier_price_rollback.php new file mode 100644 index 0000000000000..5157ec05a8834 --- /dev/null +++ b/dev/tests/integration/testsuite/Magento/ConfigurableProduct/_files/configurable_product_with_custom_option_and_simple_tier_price_rollback.php @@ -0,0 +1,8 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +declare(strict_types=1); + +require __DIR__ . '/product_configurable_with_custom_option_type_text_rollback.php'; diff --git a/dev/tests/integration/testsuite/Magento/ConfigurableProduct/_files/configurable_product_with_one_simple.php b/dev/tests/integration/testsuite/Magento/ConfigurableProduct/_files/configurable_product_with_one_simple.php new file mode 100644 index 0000000000000..dd3a31bb29016 --- /dev/null +++ b/dev/tests/integration/testsuite/Magento/ConfigurableProduct/_files/configurable_product_with_one_simple.php @@ -0,0 +1,72 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +declare(strict_types=1); + +use Magento\Catalog\Api\Data\ProductExtensionInterfaceFactory; +use Magento\Catalog\Api\ProductRepositoryInterface; +use Magento\Catalog\Model\Product\Attribute\Source\Status; +use Magento\Catalog\Model\Product\Type; +use Magento\Catalog\Model\Product\Visibility; +use Magento\Catalog\Model\ProductFactory; +use Magento\ConfigurableProduct\Helper\Product\Options\Factory; +use Magento\ConfigurableProduct\Model\Product\Type\Configurable; +use Magento\Store\Api\WebsiteRepositoryInterface; +use Magento\TestFramework\Helper\Bootstrap; + +require __DIR__ . '/configurable_attribute.php'; +$objectManager = Bootstrap::getObjectManager(); +/** @var ProductRepositoryInterface $productRepository */ +$productRepository = $objectManager->get(ProductRepositoryInterface::class); +/** @var ProductFactory $productFactory */ +$productFactory = $objectManager->get(ProductFactory::class); +/** @var Factory $optionsFactory */ +$optionsFactory = $objectManager->get(Factory::class); +/** @var ProductExtensionInterfaceFactory $productExtensionAttributes */ +$productExtensionAttributesFactory = $objectManager->get(ProductExtensionInterfaceFactory::class); +/** @var WebsiteRepositoryInterface $websiteRepository */ +$websiteRepository = $objectManager->get(WebsiteRepositoryInterface::class); +$defaultWebsiteId = $websiteRepository->get('base')->getId(); + +$option = $attribute->getSource()->getOptionId('Option 1'); +$product = $productFactory->create(); +$product->setTypeId(Type::TYPE_SIMPLE) + ->setAttributeSetId($product->getDefaultAttributeSetId()) + ->setWebsiteIds([$defaultWebsiteId]) + ->setName('Configurable Option 1') + ->setSku('simple_1') + ->setPrice(10.00) + ->setTestConfigurable($option) + ->setVisibility(Visibility::VISIBILITY_NOT_VISIBLE) + ->setStatus(Status::STATUS_ENABLED) + ->setStockData(['use_config_manage_stock' => 1, 'qty' => 100, 'is_qty_decimal' => 0, 'is_in_stock' => 1]); +$product = $productRepository->save($product); + +$configurableOptions = $optionsFactory->create( + [ + [ + 'attribute_id' => $attribute->getId(), + 'code' => $attribute->getAttributeCode(), + 'label' => $attribute->getStoreLabel(), + 'position' => '0', + 'values' => [['label' => 'test', 'attribute_id' => $attribute->getId(), 'value_index' => $option]], + ], + ] +); +$extensionConfigurableAttributes = $product->getExtensionAttributes() ?: $productExtensionAttributesFactory->create(); +$extensionConfigurableAttributes->setConfigurableProductOptions($configurableOptions); +$extensionConfigurableAttributes->setConfigurableProductLinks([$product->getId()]); + +$configurableProduct = $productFactory->create(); +$configurableProduct->setExtensionAttributes($extensionConfigurableAttributes); +$configurableProduct->setTypeId(Configurable::TYPE_CODE) + ->setAttributeSetId($configurableProduct->getDefaultAttributeSetId()) + ->setWebsiteIds([$defaultWebsiteId]) + ->setName('Configurable Product') + ->setSku('configurable') + ->setVisibility(Visibility::VISIBILITY_BOTH) + ->setStatus(Status::STATUS_ENABLED) + ->setStockData(['use_config_manage_stock' => 1, 'is_in_stock' => 1]); +$productRepository->save($configurableProduct); diff --git a/dev/tests/integration/testsuite/Magento/ConfigurableProduct/_files/configurable_product_with_one_simple_rollback.php b/dev/tests/integration/testsuite/Magento/ConfigurableProduct/_files/configurable_product_with_one_simple_rollback.php new file mode 100644 index 0000000000000..165b714caa508 --- /dev/null +++ b/dev/tests/integration/testsuite/Magento/ConfigurableProduct/_files/configurable_product_with_one_simple_rollback.php @@ -0,0 +1,32 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +declare(strict_types=1); + +use Magento\Catalog\Api\ProductRepositoryInterface; +use Magento\Framework\Exception\NoSuchEntityException; +use Magento\Framework\Registry; +use Magento\TestFramework\Helper\Bootstrap; + +$objectManager = Bootstrap::getObjectManager(); +/** @var Registry $registry */ +$registry = $objectManager->get(Registry::class); +/** @var ProductRepositoryInterface $productRepository */ +$productRepository = $objectManager->get(ProductRepositoryInterface::class); +$registry->unregister('isSecureArea'); +$registry->register('isSecureArea', true); + +foreach (['simple_1', 'configurable'] as $sku) { + try { + $product = $productRepository->get($sku); + $productRepository->delete($product); + } catch (NoSuchEntityException $e) { + //Product already removed + } +} + +$registry->unregister('isSecureArea'); +$registry->register('isSecureArea', false); +require __DIR__ . '/configurable_attribute_rollback.php'; diff --git a/dev/tests/integration/testsuite/Magento/ConfigurableProduct/_files/configurable_product_with_out_of_stock_children.php b/dev/tests/integration/testsuite/Magento/ConfigurableProduct/_files/configurable_product_with_out_of_stock_children.php new file mode 100644 index 0000000000000..5c749584b2917 --- /dev/null +++ b/dev/tests/integration/testsuite/Magento/ConfigurableProduct/_files/configurable_product_with_out_of_stock_children.php @@ -0,0 +1,104 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +declare(strict_types=1); + +use Magento\Catalog\Api\Data\ProductExtensionFactory; +use Magento\Catalog\Api\ProductAttributeRepositoryInterface; +use Magento\Catalog\Api\ProductRepositoryInterface; +use Magento\Catalog\Model\Product\Attribute\Source\Status; +use Magento\Catalog\Model\Product\Type as ProductType; +use Magento\Catalog\Model\Product\Visibility; +use Magento\Catalog\Model\ProductFactory; +use Magento\CatalogInventory\Api\Data\StockStatusInterface; +use Magento\ConfigurableProduct\Helper\Product\Options\Factory; +use Magento\ConfigurableProduct\Model\Product\Type\Configurable; +use Magento\Framework\ObjectManagerInterface; +use Magento\Store\Api\WebsiteRepositoryInterface; +use Magento\TestFramework\Helper\Bootstrap; + +require __DIR__ . '/configurable_attribute.php'; +require __DIR__ . '/../../Catalog/_files/category.php'; + +/** @var ObjectManagerInterface $objectManager */ +$objectManager = Bootstrap::getObjectManager(); +/** @var ProductAttributeRepositoryInterface $productAttributeRepository */ +$productAttributeRepository = $objectManager->create(ProductAttributeRepositoryInterface::class); +$attribute = $productAttributeRepository->get('test_configurable'); +$options = $attribute->getOptions(); +/** @var WebsiteRepositoryInterface $websiteRepository */ +$websiteRepository = $objectManager->get(WebsiteRepositoryInterface::class); +$baseWebsite = $websiteRepository->get('base'); +/** @var ProductRepositoryInterface $productRepository */ +$productRepository = $objectManager->create(ProductRepositoryInterface::class); +/** @var ProductFactory $productFactory */ +$productFactory = $objectManager->get(ProductFactory::class); +$attributeValues = []; +$associatedProductIds = []; +$rootCategoryId = $baseWebsite->getDefaultStore()->getRootCategoryId(); +array_shift($options); + +foreach ($options as $option) { + $product = $productFactory->create(); + $product->setTypeId(ProductType::TYPE_SIMPLE) + ->setAttributeSetId($product->getDefaultAttributeSetId()) + ->setWebsiteIds([$baseWebsite->getId()]) + ->setName('Configurable Option ' . $option->getLabel()) + ->setSku(strtolower(str_replace(' ', '_', 'simple ' . $option->getLabel()))) + ->setPrice(150) + ->setTestConfigurable($option->getValue()) + ->setVisibility(Visibility::VISIBILITY_NOT_VISIBLE) + ->setStatus(Status::STATUS_ENABLED) + ->setCategoryIds([$rootCategoryId, 333]) + ->setStockData([ + 'use_config_manage_stock' => 1, + 'qty' => 100, + 'is_qty_decimal' => 0, + 'is_in_stock' => StockStatusInterface::STATUS_OUT_OF_STOCK + ]); + $product = $productRepository->save($product); + + $attributeValues[] = [ + 'label' => 'test', + 'attribute_id' => $attribute->getId(), + 'value_index' => $option->getValue(), + ]; + $associatedProductIds[] = $product->getId(); +} +/** @var Factory $optionsFactory */ +$optionsFactory = $objectManager->get(Factory::class); +$configurableAttributesData = [ + [ + 'attribute_id' => $attribute->getId(), + 'code' => $attribute->getAttributeCode(), + 'label' => $attribute->getStoreLabel(), + 'position' => '0', + 'values' => $attributeValues, + ], +]; +$configurableOptions = $optionsFactory->create($configurableAttributesData); + +$product = $productFactory->create(); +/** @var ProductExtensionFactory $extensionAttributesFactory */ +$extensionAttributesFactory = $objectManager->get(ProductExtensionFactory::class); +$extensionConfigurableAttributes = $product->getExtensionAttributes() ?: $extensionAttributesFactory->create(); +$extensionConfigurableAttributes->setConfigurableProductOptions($configurableOptions); +$extensionConfigurableAttributes->setConfigurableProductLinks($associatedProductIds); +$product->setExtensionAttributes($extensionConfigurableAttributes); + +$product->setTypeId(Configurable::TYPE_CODE) + ->setAttributeSetId($product->getDefaultAttributeSetId()) + ->setWebsiteIds([$baseWebsite->getId()]) + ->setName('Configurable Product') + ->setSku('configurable') + ->setVisibility(Visibility::VISIBILITY_BOTH) + ->setStatus(Status::STATUS_ENABLED) + ->setCategoryIds([$rootCategoryId, 333]) + ->setStockData([ + 'use_config_manage_stock' => 1, + 'qty' => 100, + 'is_in_stock' => StockStatusInterface::STATUS_IN_STOCK + ]); +$productRepository->save($product); diff --git a/dev/tests/integration/testsuite/Magento/ConfigurableProduct/_files/configurable_product_with_out_of_stock_children_rollback.php b/dev/tests/integration/testsuite/Magento/ConfigurableProduct/_files/configurable_product_with_out_of_stock_children_rollback.php new file mode 100644 index 0000000000000..d13b7688f6d91 --- /dev/null +++ b/dev/tests/integration/testsuite/Magento/ConfigurableProduct/_files/configurable_product_with_out_of_stock_children_rollback.php @@ -0,0 +1,19 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +declare(strict_types=1); + +use Magento\Framework\ObjectManagerInterface; +use Magento\TestFramework\ConfigurableProduct\Model\DeleteConfigurableProduct; +use Magento\TestFramework\Helper\Bootstrap; + +/** @var ObjectManagerInterface $objectManager */ +$objectManager = Bootstrap::getObjectManager(); +/** @var DeleteConfigurableProduct $deleteConfigurableProductService */ +$deleteConfigurableProductService = $objectManager->get(DeleteConfigurableProduct::class); +$deleteConfigurableProductService->execute('configurable'); + +require __DIR__ . '/configurable_attribute_rollback.php'; +require __DIR__ . '/../../Catalog/_files/category_rollback.php'; diff --git a/dev/tests/integration/testsuite/Magento/ConfigurableProduct/_files/configurable_product_with_two_child_products.php b/dev/tests/integration/testsuite/Magento/ConfigurableProduct/_files/configurable_product_with_two_child_products.php new file mode 100644 index 0000000000000..bdf7b1e87d77c --- /dev/null +++ b/dev/tests/integration/testsuite/Magento/ConfigurableProduct/_files/configurable_product_with_two_child_products.php @@ -0,0 +1,93 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +declare(strict_types=1); + +use Magento\Catalog\Api\Data\ProductExtensionInterfaceFactory; +use Magento\Catalog\Api\ProductRepositoryInterface; +use Magento\Catalog\Model\Product\Attribute\Source\Status; +use Magento\Catalog\Model\Product\Type; +use Magento\Catalog\Model\Product\Visibility; +use Magento\Catalog\Model\ProductFactory; +use Magento\CatalogInventory\Model\Stock\ItemFactory; +use Magento\ConfigurableProduct\Helper\Product\Options\Factory; +use Magento\ConfigurableProduct\Model\Product\Type\Configurable; +use Magento\Eav\Api\Data\AttributeOptionInterface; +use Magento\Store\Api\WebsiteRepositoryInterface; +use Magento\TestFramework\Helper\Bootstrap; + +require __DIR__ . '/configurable_attribute.php'; + +$objectManager = Bootstrap::getObjectManager(); +/** @var Factory $optionsFactory */ +$optionsFactory = $objectManager->get(Factory::class); +/** @var ProductRepositoryInterface $productRepository */ +$productRepository = $objectManager->get(ProductRepositoryInterface::class); +/** @var ProductExtensionInterfaceFactory $productExtensionFactory */ +$productExtensionFactory = $objectManager->get(ProductExtensionInterfaceFactory::class); +/** @var ProductFactory $productFactory */ +$productFactory = $objectManager->get(ProductFactory::class); +/** @var WebsiteRepositoryInterface $websiteRepository */ +$websiteRepository = $objectManager->get(WebsiteRepositoryInterface::class); +$baseWebsite = $websiteRepository->get('base'); +/** @var AttributeOptionInterface[] $options */ +$options = $attribute->getOptions(); +$associatedProductIds = $attributeValues = []; +$simpleProductsData = [ + ['Simple option 1', 10, 'Black'], + ['Simple option 2', 20, 'White'], +]; +foreach ($options as $option) { + if (!$option->getValue()) { + continue; + } + [$productSku, $productPrice, $productDescription] = array_shift($simpleProductsData); + $product = $productFactory->create(); + $product->isObjectNew(true); + $product->setTypeId(Type::TYPE_SIMPLE) + ->setAttributeSetId($product->getDefaultAttributeSetId()) + ->setWebsiteIds([$baseWebsite->getId()]) + ->setName('Configurable ' . $option->getLabel()) + ->setSku($productSku) + ->setPrice($productPrice) + ->setTestConfigurable($option->getValue()) + ->setDescription($productDescription) + ->setVisibility(Visibility::VISIBILITY_NOT_VISIBLE) + ->setStatus(Status::STATUS_ENABLED) + ->setStockData(['use_config_manage_stock' => 1, 'qty' => 100, 'is_qty_decimal' => 0, 'is_in_stock' => 1]); + $product = $productRepository->save($product); + $attributeValues[] = [ + 'label' => 'test', + 'attribute_id' => $attribute->getId(), + 'value_index' => $option->getValue(), + ]; + $associatedProductIds[] = $product->getId(); +} +$product = $productFactory->create(); +$product->isObjectNew(true); +$product->setTypeId(Configurable::TYPE_CODE) + ->setAttributeSetId($product->getDefaultAttributeSetId()) + ->setWebsiteIds([$baseWebsite->getId()]) + ->setName('Configurable product with two child') + ->setSku('Configurable product') + ->setVisibility(Visibility::VISIBILITY_BOTH) + ->setStatus(Status::STATUS_ENABLED) + ->setStockData(['use_config_manage_stock' => 1, 'is_in_stock' => 1]); +$configurableOptions = $optionsFactory->create( + [ + [ + 'attribute_id' => $attribute->getId(), + 'code' => $attribute->getAttributeCode(), + 'label' => $attribute->getStoreLabel(), + 'position' => '0', + 'values' => $attributeValues, + ], + ] +); +$extensionConfigurableAttributes = $product->getExtensionAttributes() ?? $productExtensionFactory->create(); +$extensionConfigurableAttributes->setConfigurableProductOptions($configurableOptions); +$extensionConfigurableAttributes->setConfigurableProductLinks($associatedProductIds); +$product->setExtensionAttributes($extensionConfigurableAttributes); +$productRepository->save($product); diff --git a/dev/tests/integration/testsuite/Magento/ConfigurableProduct/_files/configurable_product_with_two_child_products_rollback.php b/dev/tests/integration/testsuite/Magento/ConfigurableProduct/_files/configurable_product_with_two_child_products_rollback.php new file mode 100644 index 0000000000000..b0d9b3d80e11e --- /dev/null +++ b/dev/tests/integration/testsuite/Magento/ConfigurableProduct/_files/configurable_product_with_two_child_products_rollback.php @@ -0,0 +1,29 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +declare(strict_types=1); + +use Magento\Catalog\Api\ProductRepositoryInterface; +use Magento\Framework\Exception\NoSuchEntityException; +use Magento\Framework\Registry; +use Magento\TestFramework\Helper\Bootstrap; + +$objectManager = Bootstrap::getObjectManager(); +/** @var Registry $registry */ +$registry = $objectManager->get(Registry::class); +$registry->unregister('isSecureArea'); +$registry->register('isSecureArea', true); +/** @var ProductRepositoryInterface $productRepository */ +$productRepository = $objectManager->get(ProductRepositoryInterface::class); +foreach (['Simple option 1', 'Simple option 2', 'Configurable product'] as $sku) { + try { + $productRepository->deleteById($sku); + } catch (NoSuchEntityException $e) { + //Product has deleted. + } +} +$registry->unregister('isSecureArea'); +$registry->register('isSecureArea', false); +require __DIR__ . '/configurable_attribute_rollback.php'; diff --git a/dev/tests/integration/testsuite/Magento/ConfigurableProduct/_files/product_configurable_disable_first_child.php b/dev/tests/integration/testsuite/Magento/ConfigurableProduct/_files/product_configurable_disable_first_child.php new file mode 100644 index 0000000000000..51d192f76c807 --- /dev/null +++ b/dev/tests/integration/testsuite/Magento/ConfigurableProduct/_files/product_configurable_disable_first_child.php @@ -0,0 +1,23 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +declare(strict_types=1); + +require __DIR__ . '/product_configurable_sku.php'; + +use Magento\Catalog\Api\Data\ProductAttributeInterface; +use Magento\Catalog\Model\Product\Action; +use Magento\Catalog\Model\Product\Attribute\Source\Status; +use Magento\TestFramework\Helper\Bootstrap; + +$childSku = 'simple_10'; + +$childProduct = $productRepository->get($childSku); +$productAction = Bootstrap::getObjectManager()->get(Action::class); +$productAction->updateAttributes( + [$childProduct->getEntityId()], + [ProductAttributeInterface::CODE_STATUS => Status::STATUS_DISABLED], + $childProduct->getStoreId() +); diff --git a/dev/tests/integration/testsuite/Magento/ConfigurableProduct/_files/product_configurable_disable_first_child_rollback.php b/dev/tests/integration/testsuite/Magento/ConfigurableProduct/_files/product_configurable_disable_first_child_rollback.php new file mode 100644 index 0000000000000..7c73fa65cfcd1 --- /dev/null +++ b/dev/tests/integration/testsuite/Magento/ConfigurableProduct/_files/product_configurable_disable_first_child_rollback.php @@ -0,0 +1,8 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +declare(strict_types=1); + +require __DIR__ . '/product_configurable_sku_rollback.php'; diff --git a/dev/tests/integration/testsuite/Magento/ConfigurableProduct/_files/product_configurable_with_custom_option_type_text.php b/dev/tests/integration/testsuite/Magento/ConfigurableProduct/_files/product_configurable_with_custom_option_type_text.php new file mode 100644 index 0000000000000..dc173b1cd7607 --- /dev/null +++ b/dev/tests/integration/testsuite/Magento/ConfigurableProduct/_files/product_configurable_with_custom_option_type_text.php @@ -0,0 +1,32 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +declare(strict_types=1); + +use Magento\Catalog\Api\Data\ProductCustomOptionInterface; +use Magento\Catalog\Api\Data\ProductCustomOptionInterfaceFactory; +use Magento\Framework\ObjectManagerInterface; +use Magento\TestFramework\Helper\Bootstrap; + +require __DIR__ . '/product_configurable.php'; + +/** @var ObjectManagerInterface $objectManager */ +$objectManager = Bootstrap::getObjectManager(); +/** @var ProductCustomOptionInterfaceFactory $optionRepository */ +$optionRepository = $objectManager->get(ProductCustomOptionInterfaceFactory::class); + +$createdOption = $optionRepository->create([ + 'data' => [ + 'is_require' => 0, + 'sku' => 'option-1', + 'title' => 'Option 1', + 'type' => ProductCustomOptionInterface::OPTION_TYPE_AREA, + 'price' => 15, + 'price_type' => 'fixed', + ] +]); +$createdOption->setProductSku($product->getSku()); +$product->setOptions([$createdOption]); +$productRepository->save($product); diff --git a/dev/tests/integration/testsuite/Magento/ConfigurableProduct/_files/product_configurable_with_custom_option_type_text_rollback.php b/dev/tests/integration/testsuite/Magento/ConfigurableProduct/_files/product_configurable_with_custom_option_type_text_rollback.php new file mode 100644 index 0000000000000..c6c17b956ee37 --- /dev/null +++ b/dev/tests/integration/testsuite/Magento/ConfigurableProduct/_files/product_configurable_with_custom_option_type_text_rollback.php @@ -0,0 +1,8 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +declare(strict_types=1); + +require __DIR__ . '/product_configurable_rollback.php'; diff --git a/dev/tests/integration/testsuite/Magento/ConfigurableProduct/_files/product_configurable_zero_qty_first_child.php b/dev/tests/integration/testsuite/Magento/ConfigurableProduct/_files/product_configurable_zero_qty_first_child.php new file mode 100644 index 0000000000000..b923ae6399cc3 --- /dev/null +++ b/dev/tests/integration/testsuite/Magento/ConfigurableProduct/_files/product_configurable_zero_qty_first_child.php @@ -0,0 +1,21 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +declare(strict_types=1); + +require __DIR__ . '/product_configurable_sku.php'; + +$childSku = 'simple_10'; + +$childProduct = $productRepository->get($childSku); +$childProduct->setStockData( + [ + 'use_config_manage_stock' => 1, + 'qty' => 0, + 'is_qty_decimal' => 0, + 'is_in_stock' => 0 + ] +); +$productRepository->save($childProduct); diff --git a/dev/tests/integration/testsuite/Magento/ConfigurableProduct/_files/product_configurable_zero_qty_first_child_rollback.php b/dev/tests/integration/testsuite/Magento/ConfigurableProduct/_files/product_configurable_zero_qty_first_child_rollback.php new file mode 100644 index 0000000000000..7c73fa65cfcd1 --- /dev/null +++ b/dev/tests/integration/testsuite/Magento/ConfigurableProduct/_files/product_configurable_zero_qty_first_child_rollback.php @@ -0,0 +1,8 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +declare(strict_types=1); + +require __DIR__ . '/product_configurable_sku_rollback.php'; diff --git a/dev/tests/integration/testsuite/Magento/Csp/Model/Policy/Renderer/SimplePolicyHeaderRendererTest.php b/dev/tests/integration/testsuite/Magento/Csp/Model/Policy/Renderer/SimplePolicyHeaderRendererTest.php index a67665c6d3c48..93e7833038a42 100644 --- a/dev/tests/integration/testsuite/Magento/Csp/Model/Policy/Renderer/SimplePolicyHeaderRendererTest.php +++ b/dev/tests/integration/testsuite/Magento/Csp/Model/Policy/Renderer/SimplePolicyHeaderRendererTest.php @@ -53,7 +53,13 @@ public function testRenderRestrictMode(): void $this->assertNotEmpty($header = $this->response->getHeader('Content-Security-Policy')); $this->assertEmpty($this->response->getHeader('Content-Security-Policy-Report-Only')); - $this->assertEquals('default-src https://magento.com \'self\';', $header->getFieldValue()); + $contentSecurityPolicyContent = []; + if ($header instanceof \ArrayIterator) { + foreach ($header as $item) { + $contentSecurityPolicyContent[] = $item->getFieldValue(); + } + } + $this->assertEquals(['default-src https://magento.com \'self\';'], $contentSecurityPolicyContent); } /** @@ -73,9 +79,15 @@ public function testRenderRestrictWithReportingMode(): void $this->assertNotEmpty($header = $this->response->getHeader('Content-Security-Policy')); $this->assertEmpty($this->response->getHeader('Content-Security-Policy-Report-Only')); + $contentSecurityPolicyContent = []; + if ($header instanceof \ArrayIterator) { + foreach ($header as $item) { + $contentSecurityPolicyContent[] = $item->getFieldValue(); + } + } $this->assertEquals( - 'default-src https://magento.com \'self\'; report-uri /csp-reports/; report-to report-endpoint;', - $header->getFieldValue() + ['default-src https://magento.com \'self\'; report-uri /csp-reports/; report-to report-endpoint;'], + $contentSecurityPolicyContent ); $this->assertNotEmpty($reportToHeader = $this->response->getHeader('Report-To')); $this->assertNotEmpty($reportData = json_decode("[{$reportToHeader->getFieldValue()}]", true)); diff --git a/dev/tests/integration/testsuite/Magento/Customer/Block/Account/ResetPasswordTest.php b/dev/tests/integration/testsuite/Magento/Customer/Block/Account/ResetPasswordTest.php new file mode 100644 index 0000000000000..cc087e006025d --- /dev/null +++ b/dev/tests/integration/testsuite/Magento/Customer/Block/Account/ResetPasswordTest.php @@ -0,0 +1,86 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +declare(strict_types=1); + +namespace Magento\Customer\Block\Account; + +use Magento\Framework\Math\Random; +use Magento\Framework\ObjectManagerInterface; +use Magento\Framework\View\LayoutInterface; +use Magento\TestFramework\Helper\Bootstrap; +use Magento\TestFramework\Helper\Xpath; +use PHPUnit\Framework\TestCase; + +/** + * Class checks password reset block output + * + * @see \Magento\Customer\Block\Account\Resetpassword + * @magentoAppArea frontend + */ +class ResetPasswordTest extends TestCase +{ + private const FORM_XPATH = "//form[contains(@action, '?token=%s')]"; + private const SET_NEW_PASSWORD_BUTTON_XPATH = "//button/span[contains(text(),'Set a New Password')]"; + private const NEW_PASSWORD_LABEL_XPATH = "//label[@for='password']/span[contains(text(), 'New Password')]"; + private const PASSWORD_CONFIRMATION_LABEL_XPATH = "//label[@for='password-confirmation']" + . "/span[contains(text(), 'Confirm New Password')]"; + + /** @var ObjectManagerInterface */ + private $objectManager; + + /** @var LayoutInterface */ + private $layout; + + /** @var Resetpassword */ + private $block; + + /** @var Random */ + private $random; + + /** + * @inheritdoc + */ + protected function setUp() + { + parent::setUp(); + + $this->objectManager = Bootstrap::getObjectManager(); + $this->layout = $this->objectManager->get(LayoutInterface::class); + $this->random = $this->objectManager->get(Random::class); + $this->block = $this->layout->createBlock(Resetpassword::class); + $this->block->setTemplate('Magento_Customer::form/resetforgottenpassword.phtml'); + } + + /** + * @return void + */ + public function testResetPasswordForm(): void + { + $token = $this->random->getUniqueHash(); + $this->block->setResetPasswordLinkToken($token); + $output = $this->block->toHtml(); + $this->assertEquals( + 1, + Xpath::getElementsCountForXpath(sprintf(self::FORM_XPATH, $token), $output), + 'Form action does not include correct token' + ); + $this->assertEquals( + 1, + Xpath::getElementsCountForXpath(self::NEW_PASSWORD_LABEL_XPATH, $output), + 'New password label was not found on the page' + ); + $this->assertEquals( + 1, + Xpath::getElementsCountForXpath(self::PASSWORD_CONFIRMATION_LABEL_XPATH, $output), + 'Confirm password label was not found on the page' + ); + $this->assertEquals( + 1, + Xpath::getElementsCountForXpath(self::SET_NEW_PASSWORD_BUTTON_XPATH, $output), + 'Set password button was not found on the page' + ); + } +} diff --git a/dev/tests/integration/testsuite/Magento/Customer/Block/Adminhtml/Edit/Tab/CartTest.php b/dev/tests/integration/testsuite/Magento/Customer/Block/Adminhtml/Edit/Tab/CartTest.php index 2152d70c13d5e..34e28838d0e0d 100644 --- a/dev/tests/integration/testsuite/Magento/Customer/Block/Adminhtml/Edit/Tab/CartTest.php +++ b/dev/tests/integration/testsuite/Magento/Customer/Block/Adminhtml/Edit/Tab/CartTest.php @@ -5,32 +5,52 @@ */ namespace Magento\Customer\Block\Adminhtml\Edit\Tab; +use Magento\Backend\Block\Template\Context; +use Magento\Backend\Model\Session\Quote as SessionQuote; use Magento\Customer\Controller\RegistryConstants; +use Magento\Framework\ObjectManagerInterface; +use Magento\Framework\Registry; +use Magento\Quote\Model\Quote; +use Magento\Store\Model\StoreManagerInterface; /** * Magento\Customer\Block\Adminhtml\Edit\Tab\Cart * * @magentoAppArea adminhtml + * @SuppressWarnings(PHPMD.CouplingBetweenObjects) */ class CartTest extends \PHPUnit\Framework\TestCase { const CUSTOMER_ID_VALUE = 1234; - /** @var \Magento\Backend\Block\Template\Context */ + /** + * @var Context + */ private $_context; - /** @var \Magento\Framework\Registry */ + /** + * @var Registry + */ private $_coreRegistry; - /** @var \Magento\Store\Model\StoreManagerInterface */ + /** + * @var StoreManagerInterface + */ private $_storeManager; - /** @var Cart */ + /** + * @var Cart + */ private $_block; - /** @var \Magento\Framework\ObjectManagerInterface */ + /** + * @var ObjectManagerInterface + */ private $_objectManager; + /** + * @inheritdoc + */ public function setUp() { $this->_objectManager = \Magento\TestFramework\Helper\Bootstrap::getObjectManager(); @@ -53,22 +73,96 @@ public function setUp() ); } + /** + * @inheritdoc + */ public function tearDown() { $this->_coreRegistry->unregister(RegistryConstants::CURRENT_CUSTOMER_ID); } - public function testGetCustomerId() + /** + * Verify Grid with quote items + * + * @magentoDataFixture Magento/Sales/_files/quote_with_two_products_and_customer.php + * @magentoDataFixture Magento/Customer/_files/customer.php + * @dataProvider getQuoteDataProvider + * + * @param int $customerId + * @param bool $guest + * @param bool $contains + * @return void + */ + public function testVerifyCollectionWithQuote(int $customerId, bool $guest, bool $contains): void + { + $session = $this->_objectManager->create(SessionQuote::class); + $session->setCustomerId($customerId); + $quoteFixture = $this->_objectManager->create(Quote::class); + $quoteFixture->load('test01', 'reserved_order_id'); + $quoteFixture->setCustomerIsGuest($guest) + ->setCustomerId($customerId) + ->save(); + $this->_block->toHtml(); + if ($contains) { + $this->assertContains( + "We couldn't find any records", + $this->_block->getGridParentHtml() + ); + } else { + $this->assertNotContains( + "We couldn't find any records", + $this->_block->getGridParentHtml() + ); + } + } + + /** + * Data provider for withQuoteTest + * + * @return array + */ + public function getQuoteDataProvider(): array + { + return [ + [ + 6, + false, + true + ], + [ + self::CUSTOMER_ID_VALUE, + true, + false + ], + ]; + } + + /** + * Verify Customer id + * + * @return void + */ + public function testGetCustomerId(): void { $this->assertEquals(self::CUSTOMER_ID_VALUE, $this->_block->getCustomerId()); } - public function testGetGridUrl() + /** + * Verify get grid url + * + * @return void + */ + public function testGetGridUrl(): void { $this->assertContains('/backend/customer/index/cart', $this->_block->getGridUrl()); } - public function testGetGridParentHtml() + /** + * Verify grid parent html + * + * @return void + */ + public function testGetGridParentHtml(): void { $this->_block = $this->_objectManager->get( \Magento\Framework\View\LayoutInterface::class @@ -87,14 +181,24 @@ public function testGetGridParentHtml() ); } - public function testGetRowUrl() + /** + * Verify row url + * + * @return void + */ + public function testGetRowUrl(): void { $row = new \Magento\Framework\DataObject(); $row->setProductId(1); $this->assertContains('/backend/catalog/product/edit/id/1', $this->_block->getRowUrl($row)); } - public function testGetHtml() + /** + * Verify get html + * + * @return void + */ + public function testGetHtml(): void { $html = $this->_block->toHtml(); $this->assertContains("<div id=\"customer_cart_grid\"", $html); diff --git a/dev/tests/integration/testsuite/Magento/Customer/Block/Adminhtml/Edit/Tab/CartsTest.php b/dev/tests/integration/testsuite/Magento/Customer/Block/Adminhtml/Edit/Tab/CartsTest.php index fa8577c6c6a40..604f7d8fcb2a1 100644 --- a/dev/tests/integration/testsuite/Magento/Customer/Block/Adminhtml/Edit/Tab/CartsTest.php +++ b/dev/tests/integration/testsuite/Magento/Customer/Block/Adminhtml/Edit/Tab/CartsTest.php @@ -55,12 +55,12 @@ public function testGetHtml() ); $html = $this->_block->toHtml(); - $this->assertContains("<div id=\"customer_cart_grid1\"", $html); + $this->assertContains("<div id=\"customer_cart_grid\"", $html); $this->assertRegExp( '/<div class=".*admin__data-grid-toolbar"/', $html ); - $this->assertContains("customer_cart_grid1JsObject = new varienGrid(\"customer_cart_grid1\",", $html); + $this->assertContains("customer_cart_gridJsObject = new varienGrid(\"customer_cart_grid\",", $html); $this->assertContains( 'backend\u002Fcustomer\u002Fcart_product_composite_cart\u002Fconfigure\u002Fwebsite_id\u002F1', $html diff --git a/dev/tests/integration/testsuite/Magento/Customer/Block/Adminhtml/Edit/Tab/Orders/RenderOrdersTabTest.php b/dev/tests/integration/testsuite/Magento/Customer/Block/Adminhtml/Edit/Tab/Orders/RenderOrdersTabTest.php new file mode 100644 index 0000000000000..1d18814487bf8 --- /dev/null +++ b/dev/tests/integration/testsuite/Magento/Customer/Block/Adminhtml/Edit/Tab/Orders/RenderOrdersTabTest.php @@ -0,0 +1,433 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +declare(strict_types=1); + +namespace Magento\Customer\Block\Adminhtml\Edit\Tab\Orders; + +use Magento\Customer\Block\Adminhtml\Edit\Tab\Orders; +use Magento\Customer\Controller\RegistryConstants; +use Magento\Directory\Model\Currency; +use Magento\Framework\App\Config\ScopeConfigInterface; +use Magento\Framework\Locale\CurrencyInterface; +use Magento\Framework\ObjectManagerInterface; +use Magento\Framework\Registry; +use Magento\Framework\Stdlib\DateTime\TimezoneInterface; +use Magento\Framework\View\Element\UiComponent\DataProvider\Document; +use Magento\Framework\View\LayoutInterface; +use Magento\Sales\Api\Data\OrderInterface; +use Magento\Store\Model\System\Store; +use Magento\TestFramework\Helper\Bootstrap; +use Magento\TestFramework\Helper\Xpath; +use PHPUnit\Framework\TestCase; + +/** + * Test cases related to check that orders tab with customer orders + * grid correctly renders and contains all necessary data. + * + * @SuppressWarnings(PHPMD.CouplingBetweenObjects) + * @magentoAppArea adminhtml + * @magentoDbIsolation enabled + */ +class RenderOrdersTabTest extends TestCase +{ + private const PATHS_TO_TABLE_BODY = [ + "//div[contains(@data-grid-id, 'customer_orders_grid')]", + "//table[contains(@class, 'data-grid')]", + "//tbody", + ]; + + /** + * @var ObjectManagerInterface + */ + private $objectManager; + + /** + * @var Store + */ + private $store; + + /** + * @var LayoutInterface + */ + private $layout; + + /** + * @var CurrencyInterface + */ + private $currency; + + /** + * @var TimezoneInterface + */ + private $timezone; + + /** + * @var Registry + */ + private $registry; + + /** + * @var ScopeConfigInterface + */ + private $scopeConfig; + + /** + * @var Orders + */ + private $ordersGridBlock; + + /** + * @inheritdoc + */ + protected function setUp() + { + $this->objectManager = Bootstrap::getObjectManager(); + $this->store = $this->objectManager->get(Store::class); + $this->layout = $this->objectManager->get(LayoutInterface::class); + $this->currency = $this->objectManager->get(CurrencyInterface::class); + $this->timezone = $this->objectManager->get(TimezoneInterface::class); + $this->registry = $this->objectManager->get(Registry::class); + $this->scopeConfig = $this->objectManager->get(ScopeConfigInterface::class); + parent::setUp(); + } + + /** + * @inheritdoc + */ + protected function tearDown() + { + $this->registry->unregister(RegistryConstants::CURRENT_CUSTOMER_ID); + parent::tearDown(); + } + + /** + * Assert that customer orders tab renders with message "We couldn't find any records." + * when customer doesn't have any orders. + * + * @magentoDataFixture Magento/Customer/_files/customer.php + * + * @return void + */ + public function testRenderBlockWithoutOrders(): void + { + $this->processCheckOrdersGridByCustomerId(1, 0); + } + + /** + * Assert that customer orders tab renders without message "We couldn't find any records." + * and contains rendered order item when customer has one order. + * + * @magentoDataFixture Magento/Sales/_files/order_with_customer.php + * + * @return void + */ + public function testRenderBlockWithOneOrder(): void + { + $this->processCheckOrdersGridByCustomerId(1, 1); + } + + /** + * Assert that customer orders tab renders without message "We couldn't find any records." + * and contains rendered orders items when customer has few orders. + * + * @magentoDataFixture Magento/Customer/_files/customer.php + * @magentoDataFixture Magento/Sales/_files/orders_with_customer.php + * + * @return void + */ + public function testRenderBlockWithFewOrders(): void + { + $this->processCheckOrdersGridByCustomerId(1, 5); + } + + /** + * Render orders grid and assert that all data rendered as expected. + * + * @param int $customerId + * @param int $expectedOrderCount + * @return void + */ + private function processCheckOrdersGridByCustomerId(int $customerId, int $expectedOrderCount): void + { + $this->registerCustomerId($customerId); + $ordersGridHtml = $this->getOrdersGridHtml(); + $orderItemsData = $this->getOrderGridItemsData(); + $this->assertOrdersCount($expectedOrderCount, $ordersGridHtml); + $this->assertIsEmptyGridMessageArrears($ordersGridHtml, $expectedOrderCount === 0); + $this->checkOrderItemsFields($orderItemsData, $ordersGridHtml); + } + + /** + * Add customer id to registry. + * + * @param int $customerId + * @return void + */ + private function registerCustomerId(int $customerId): void + { + $this->registry->unregister(RegistryConstants::CURRENT_CUSTOMER_ID); + $this->registry->register(RegistryConstants::CURRENT_CUSTOMER_ID, $customerId); + } + + /** + * Render customer orders tab. + * + * @return string + */ + private function getOrdersGridHtml(): string + { + $this->ordersGridBlock = $this->layout->createBlock(Orders::class); + + return $this->ordersGridBlock->toHtml(); + } + + /** + * Check that rendered html contains all provided order items. + * + * @param array $orderItemsData + * @param string $html + * @return void + */ + private function checkOrderItemsFields(array $orderItemsData, string $html): void + { + foreach ($orderItemsData as $itemOrder => $orderItemData) { + $this->assertViewOrderUrl($itemOrder, $orderItemData['order_id'], $html); + $this->assertReorderUrl($itemOrder, $orderItemData['order_id'], $html); + $this->assertStoreViewLabels($itemOrder, $orderItemData['store_view_labels'], $html); + unset($orderItemData['order_id'], $orderItemData['store_view_labels']); + $this->assertColumnsValues($itemOrder, $orderItemData, $html); + } + } + + /** + * Assert that field store_id contains all provided store codes. + * + * @param int $itemOrder + * @param array $storeViewLabels + * @param string $html + * @return void + */ + private function assertStoreViewLabels(int $itemOrder, array $storeViewLabels, string $html): void + { + if (empty($storeViewLabels)) { + return; + } + + $elementPaths = array_merge(self::PATHS_TO_TABLE_BODY, [ + "//tr[{$itemOrder}]", + "//td[contains(@class, 'store_id') and %s]", + ]); + $storeLabelsPaths = []; + foreach ($storeViewLabels as $labelIndex => $storeViewLabel) { + $storeLabelsPaths[] = "contains(text()[{$labelIndex}], '{$storeViewLabel}')"; + } + $checkStoreViewsXPath = sprintf(implode('', $elementPaths), implode(' and ', $storeLabelsPaths)); + $this->assertEquals( + 1, + Xpath::getElementsCountForXpath($checkStoreViewsXPath, $html), + sprintf("Some store view label not found. Labels: %s. Html: %s", implode(', ', $storeViewLabels), $html) + ); + } + + /** + * Assert that columns values as expected. + * + * @param int $itemOrder + * @param array $columnsData + * @param string $html + * @return void + */ + private function assertColumnsValues(int $itemOrder, array $columnsData, string $html): void + { + $elementPaths = array_merge(self::PATHS_TO_TABLE_BODY, [ + "//tr[{$itemOrder}]", + "//td[contains(@class, '%s') and contains(text(), '%s')]", + ]); + $elementXPathTemplate = implode('', $elementPaths); + foreach ($columnsData as $columnName => $columnValue) { + $preparedXPath = sprintf($elementXPathTemplate, $columnName, $columnValue); + $this->assertEquals( + 1, + Xpath::getElementsCountForXpath($preparedXPath, $html), + sprintf("Column %s doesn't have value %s. Html: %s", $columnName, $columnValue, $html) + ); + } + } + + /** + * Assert that rendered html contains URL to reorder by order id. + * + * @param int $itemOrder + * @param int $orderId + * @param string $html + * @return void + */ + private function assertReorderUrl(int $itemOrder, int $orderId, string $html): void + { + $urlLabel = (string)__('Reorder'); + $elementPaths = array_merge(self::PATHS_TO_TABLE_BODY, [ + "//tr[{$itemOrder}]", + "//td[contains(@class, 'action')]", + "//a[contains(@href, 'sales/order_create/reorder/order_id/$orderId') and contains(text(), '{$urlLabel}')]", + ]); + $reorderUrlXPath = implode('', $elementPaths); + $this->assertEquals( + 1, + Xpath::getElementsCountForXpath($reorderUrlXPath, $html), + sprintf('Reorder URL is not as expected. Html: %s', $html) + ); + } + + /** + * Assert that rendered html contains URL to order view by order id. + * + * @param int $itemOrder + * @param int $orderId + * @param string $html + * @return void + */ + private function assertViewOrderUrl(int $itemOrder, int $orderId, string $html): void + { + $elementPaths = array_merge(self::PATHS_TO_TABLE_BODY, [ + "//tr[{$itemOrder}][contains(@title, 'sales/order/view/order_id/{$orderId}')]", + ]); + $viewOrderUrlXPath = implode('', $elementPaths); + $this->assertEquals( + 1, + Xpath::getElementsCountForXpath($viewOrderUrlXPath, $html), + sprintf('URL to view order is not as expected. Html: %s', $html) + ); + } + + /** + * Assert that provided orders count and count in html are equals. + * + * @param int $expectedOrdersCount + * @param string $html + * @return void + */ + private function assertOrdersCount(int $expectedOrdersCount, string $html): void + { + $elementPaths = [ + "//div[contains(@data-grid-id, 'customer_orders_grid')]", + "//div[contains(@class, 'grid-header-row')]", + "//div[contains(@class, 'control-support-text')]", + sprintf("//span[contains(@id, 'grid-total-count') and contains(text(), '%s')]", $expectedOrdersCount), + ]; + $ordersCountXPath = implode('', $elementPaths); + $this->assertEquals( + 1, + Xpath::getElementsCountForXpath($ordersCountXPath, $html), + sprintf('Provided count and count in html are not equals. Html: %s', $html) + ); + } + + /** + * Assert that grid contains or not contains message "We couldn't find any records.". + * + * @param string $html + * @param bool $isMessageAppears + * @return void + */ + private function assertIsEmptyGridMessageArrears(string $html, bool $isMessageAppears = false): void + { + $gridText = (string)__("We couldn't find any records."); + $elementPaths = array_merge(self::PATHS_TO_TABLE_BODY, [ + "//tr[contains(@class, 'tr-no-data')]", + "//td[contains(@class, 'empty-text') and contains(text(), \"{$gridText}\")]", + ]); + $emptyTextXPath = implode('', $elementPaths); + $this->assertEquals( + $isMessageAppears ? 1 : 0, + Xpath::getElementsCountForXpath($emptyTextXPath, $html), + sprintf('Message "We couldn\'t find any records." not found in html. Html: %s', $html) + ); + } + + /** + * Build array with rendered orders for check that all contained data appears. + * + * @return array + */ + private function getOrderGridItemsData(): array + { + $orders = []; + $orderNumber = 1; + /** @var Document $order */ + foreach ($this->ordersGridBlock->getCollection() as $order) { + $orderGrandTotal = $this->prepareGrandTotal( + $order->getData('grand_total'), + $order->getData('order_currency_code') + ); + $orders[$orderNumber] = [ + 'order_id' => (int)$order->getData(OrderInterface::ENTITY_ID), + 'increment_id' => $order->getData(OrderInterface::INCREMENT_ID), + 'created_at' => $this->prepareCreatedAtDate($order->getData(OrderInterface::CREATED_AT)), + 'billing_name' => $order->getData('billing_name'), + 'shipping_name' => $order->getData('shipping_name'), + 'grand_total' => $orderGrandTotal, + 'store_view_labels' => $this->prepareStoreViewLabels([$order->getData(OrderInterface::STORE_ID)]), + ]; + $orderNumber++; + } + + return $orders; + } + + /** + * Normalize created at date. + * + * @param string $createdAt + * @return string + */ + private function prepareCreatedAtDate(string $createdAt): string + { + $date = new \DateTime($createdAt); + + return $this->timezone->formatDateTime($date, \IntlDateFormatter::MEDIUM, \IntlDateFormatter::MEDIUM); + } + + /** + * Normalize grand total. + * + * @param string $grandTotal + * @param string|null $orderCurrencyCode + * @return string + */ + private function prepareGrandTotal(string $grandTotal, ?string $orderCurrencyCode = null): string + { + $resultGrandTotal = sprintf("%f", (float)$grandTotal * 1.0); + $orderCurrencyCode = $orderCurrencyCode ?: + $this->scopeConfig->getValue(Currency::XML_PATH_CURRENCY_BASE, 'default'); + + return $this->currency->getCurrency($orderCurrencyCode)->toCurrency($resultGrandTotal); + } + + /** + * Normalize store ids. + * + * @param array $orderStoreIds + * @return array + */ + private function prepareStoreViewLabels(array $orderStoreIds): array + { + $result = []; + $storeStructure = $this->store->getStoresStructure(false, $orderStoreIds); + $textIndex = 0; + foreach ($storeStructure as $website) { + $textIndex++; + foreach ($website['children'] as $group) { + $textIndex++; + foreach ($group['children'] as $store) { + $textIndex++; + $result[$textIndex] = $store['label']; + } + } + } + + return $result; + } +} diff --git a/dev/tests/integration/testsuite/Magento/Customer/Block/Form/LoginTest.php b/dev/tests/integration/testsuite/Magento/Customer/Block/Form/LoginTest.php new file mode 100644 index 0000000000000..f500885a35ed2 --- /dev/null +++ b/dev/tests/integration/testsuite/Magento/Customer/Block/Form/LoginTest.php @@ -0,0 +1,92 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +declare(strict_types=1); + +namespace Magento\Customer\Block\Form; + +use Magento\Framework\ObjectManagerInterface; +use Magento\Framework\View\LayoutInterface; +use Magento\TestFramework\Helper\Bootstrap; +use Magento\TestFramework\Helper\Xpath; +use PHPUnit\Framework\TestCase; + +/** + * Class checks login form view + * + * @magentoAppArea frontend + */ +class LoginTest extends TestCase +{ + private const EMAIL_LABEL_XPATH = "//label[@for='email']/span[contains(text(), 'Email')]"; + private const PASSWORD_LABEL_XPATH = "//label[@for='pass' ]/span[contains(text(), 'Password')]"; + private const EMAIL_INPUT_XPATH = "//input[@name ='login[username]' and contains(@data-validate,'required:true')" + . "and contains(@data-validate, \"'validate-email':true\")]"; + private const PASSWORD_INPUT_XPATH = "//input[@name='login[password]'" + . "and contains(@data-validate,'required:true')]"; + private const SIGN_IN_BUTTON_XPATH = "//button[@type='submit']/span[contains(text(), 'Sign In')]"; + private const FORGOT_PASSWORD_LINK_PATH = "//a[contains(@href, 'customer/account/forgotpassword')]" + . "/span[contains(text(), 'Forgot Your Password?')] "; + + /** @var ObjectManagerInterface */ + private $objectManager; + + /** @var LayoutInterface */ + private $layout; + + /** @var Login */ + private $block; + + /** + * @inheritdoc + */ + protected function setUp() + { + $this->objectManager = Bootstrap::getObjectManager(); + $this->layout = $this->objectManager->get(LayoutInterface::class); + $this->block = $this->layout->createBlock(Login::class); + $this->block->setTemplate('Magento_Customer::form/login.phtml'); + + parent::setUp(); + } + + /** + * @return void + */ + public function testLoginForm(): void + { + $result = $this->block->toHtml(); + $this->assertEquals( + 1, + Xpath::getElementsCountForXpath(self::EMAIL_LABEL_XPATH, $result), + 'Email label does not exist on the page' + ); + $this->assertEquals( + 1, + Xpath::getElementsCountForXpath(self::PASSWORD_LABEL_XPATH, $result), + 'Password label does not exist on the page' + ); + $this->assertEquals( + 1, + Xpath::getElementsCountForXpath(self::EMAIL_INPUT_XPATH, $result), + 'Email input does not exist on the page' + ); + $this->assertEquals( + 1, + Xpath::getElementsCountForXpath(self::PASSWORD_INPUT_XPATH, $result), + 'Password input does not exist on the page' + ); + $this->assertEquals( + 1, + Xpath::getElementsCountForXpath(self::SIGN_IN_BUTTON_XPATH, $result), + 'Sign in button does not exist on the page' + ); + $this->assertEquals( + 1, + Xpath::getElementsCountForXpath(self::FORGOT_PASSWORD_LINK_PATH, $result), + 'Forgot password link does not exist on the page' + ); + } +} diff --git a/dev/tests/integration/testsuite/Magento/Customer/Block/NewsletterTest.php b/dev/tests/integration/testsuite/Magento/Customer/Block/NewsletterTest.php new file mode 100644 index 0000000000000..8778a00e81499 --- /dev/null +++ b/dev/tests/integration/testsuite/Magento/Customer/Block/NewsletterTest.php @@ -0,0 +1,89 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +declare(strict_types=1); + +namespace Magento\Customer\Block; + +use Magento\Customer\Model\Session; +use Magento\Framework\ObjectManagerInterface; +use Magento\Framework\View\LayoutInterface; +use Magento\TestFramework\Helper\Bootstrap; +use Magento\TestFramework\Helper\Xpath; +use PHPUnit\Framework\TestCase; + +/** + * Class check newsletter subscription block behavior + * + * @see \Magento\Customer\Block\Newsletter + * @magentoAppArea frontend + * @magentoDbIsolation enabled + * @magentoDataFixture Magento/Customer/_files/customer.php + */ +class NewsletterTest extends TestCase +{ + private const LABEL_XPATH = "//form[contains(@class, 'form-newsletter-manage')]" + . "//span[contains(text(), 'Subscription option')]"; + private const CHECKBOX_XPATH = "//form[contains(@class, 'form-newsletter-manage')]" + . "//input[@type='checkbox' and @name='is_subscribed']"; + private const CHECKBOX_TITLE_XPATH = "//form[contains(@class, 'form-newsletter-manage')]" + . "//label/span[contains(text(), 'General Subscription')]"; + private const SAVE_BUTTON_XPATH = "//form[contains(@class, 'form-newsletter-manage')]" + . "//button[@type='submit']/span[contains(text(), 'Save')]"; + + /** @var ObjectManagerInterface */ + private $objectManager; + + /** @var LayoutInterface */ + private $layout; + + /** @var Newsletter */ + private $block; + + /** @var Session */ + private $customerSession; + + /** + * @inheritdoc + */ + protected function setUp() + { + parent::setUp(); + + $this->objectManager = Bootstrap::getObjectManager(); + $this->layout = $this->objectManager->get(LayoutInterface::class); + $this->block = $this->layout->createBlock(Newsletter::class); + $this->customerSession = $this->objectManager->get(Session::class); + } + + /** + * @return void + */ + public function testSubscriptionCheckbox(): void + { + $this->customerSession->loginById(1); + $html = $this->block->toHtml(); + $this->assertEquals( + 1, + Xpath::getElementsCountForXpath(self::LABEL_XPATH, $html), + 'Subscription label is not present on the page' + ); + $this->assertEquals( + 1, + Xpath::getElementsCountForXpath(self::CHECKBOX_XPATH, $html), + 'Subscription checkbox is not present on the page' + ); + $this->assertEquals( + 1, + Xpath::getElementsCountForXpath(self::CHECKBOX_TITLE_XPATH, $html), + 'Subscription checkbox label is not present on the page' + ); + $this->assertEquals( + 1, + Xpath::getElementsCountForXpath(self::SAVE_BUTTON_XPATH, $html), + 'Subscription save button is not present on the page' + ); + } +} diff --git a/dev/tests/integration/testsuite/Magento/Customer/Controller/Account/LoginPostTest.php b/dev/tests/integration/testsuite/Magento/Customer/Controller/Account/LoginPostTest.php new file mode 100644 index 0000000000000..10476eb01f306 --- /dev/null +++ b/dev/tests/integration/testsuite/Magento/Customer/Controller/Account/LoginPostTest.php @@ -0,0 +1,194 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +declare(strict_types=1); + +namespace Magento\Customer\Controller\Account; + +use Magento\Customer\Model\Session; +use Magento\Customer\Model\Url; +use Magento\Framework\App\Request\Http as HttpRequest; +use Magento\Framework\Message\MessageInterface; +use Magento\Framework\Phrase; +use Magento\Framework\Url\EncoderInterface; +use Magento\TestFramework\TestCase\AbstractController; + +/** + * Class checks customer login action + * + * @see \Magento\Customer\Controller\Account\LoginPost + */ +class LoginPostTest extends AbstractController +{ + /** @var Session */ + private $session; + + /** @var EncoderInterface */ + private $urlEncoder; + + /** + * @inheritdoc + */ + protected function setUp() + { + parent::setUp(); + + $this->session = $this->_objectManager->get(Session::class); + $this->urlEncoder = $this->_objectManager->get(EncoderInterface::class); + } + + /** + * @magentoConfigFixture current_store customer/captcha/enable 0 + * + * @magentoDataFixture Magento/Customer/_files/customer.php + * + * @dataProvider missingParametersDataProvider + * + * @param string|null $email + * @param string|null $password + * @param string $expectedErrorMessage + * @return void + */ + public function testLoginIncorrectParameters(?string $email, ?string $password, string $expectedErrorMessage): void + { + $this->prepareRequest($email, $password); + $this->dispatch('customer/account/loginPost'); + $this->assertSessionMessages( + $this->equalTo([(string)__($expectedErrorMessage)]), + MessageInterface::TYPE_ERROR + ); + } + + /** + * @return array + */ + public function missingParametersDataProvider(): array + { + return [ + 'missing_email' => [ + 'email' => null, + 'password' => 'password', + 'expected_error_message' => 'A login and a password are required.', + ], + 'missing_password' => [ + 'email' => 'customer@example.com', + 'password' => null, + 'expected_error_message' => 'A login and a password are required.', + ], + 'missing_both_parameters' => [ + 'email' => null, + 'password' => null, + 'expected_error_message' => 'A login and a password are required.', + ], + 'wrong_email' => [ + 'email' => 'wrongemail@example.com', + 'password' => 'password', + 'expected_error_message' => 'The account sign-in was incorrect or your account is disabled temporarily.' + . ' Please wait and try again later.', + ], + 'wrong_password' => [ + 'email' => 'customer@example.com', + 'password' => 'wrongpassword', + 'expected_error_message' => 'The account sign-in was incorrect or your account is disabled temporarily.' + . ' Please wait and try again later.', + ], + ]; + } + + /** + * @magentoDataFixture Magento/Customer/_files/customer_confirmation_config_enable.php + * @magentoDataFixture Magento/Customer/_files/unconfirmed_customer.php + * + * @magentoConfigFixture current_store customer/captcha/enable 0 + * + * @return void + */ + public function testLoginWithUnconfirmedPassword(): void + { + $this->markTestSkipped('Blocked by MC-31370.'); + $email = 'unconfirmedcustomer@example.com'; + $this->prepareRequest($email, 'Qwert12345'); + $this->dispatch('customer/account/loginPost'); + $this->assertEquals($email, $this->session->getUsername()); + $this->assertSessionMessages( + $this->equalTo([(string)__('This account is not confirmed. Click here to resend confirmation email.')]), + MessageInterface::TYPE_ERROR + ); + } + + /** + * @magentoConfigFixture current_store customer/startup/redirect_dashboard 0 + * @magentoConfigFixture current_store customer/captcha/enable 0 + * + * @magentoDataFixture Magento/Customer/_files/customer.php + * + * @return void + */ + public function testLoginWithRedirectToDashboardDisabled(): void + { + $this->prepareRequest('customer@example.com', 'password'); + $this->getRequest()->setParam(Url::REFERER_QUERY_PARAM_NAME, $this->urlEncoder->encode('test_redirect')); + $this->dispatch('customer/account/loginPost'); + $this->assertTrue($this->session->isLoggedIn()); + $this->assertRedirect($this->stringContains('test_redirect')); + } + + /** + * @magentoConfigFixture current_store customer/startup/redirect_dashboard 1 + * @magentoConfigFixture current_store customer/captcha/enable 0 + * + * @magentoDataFixture Magento/Customer/_files/customer.php + * + * @return void + */ + public function testLoginWithRedirectToDashboard(): void + { + $this->prepareRequest('customer@example.com', 'password'); + $this->getRequest()->setParam(Url::REFERER_QUERY_PARAM_NAME, $this->urlEncoder->encode('test_redirect')); + $this->dispatch('customer/account/loginPost'); + $this->assertTrue($this->session->isLoggedIn()); + $this->assertRedirect($this->stringContains('customer/account/')); + } + + /** + * @magentoConfigFixture current_store customer/startup/redirect_dashboard 1 + * @magentoConfigFixture current_store customer/captcha/enable 0 + * + * @magentoDataFixture Magento/Customer/_files/customer.php + * + * @return void + */ + public function testNoFormKeyLoginPostAction(): void + { + $this->prepareRequest('customer@example.com', 'password'); + $this->getRequest()->setPostValue('form_key', null); + $this->getRequest()->setParam(Url::REFERER_QUERY_PARAM_NAME, $this->urlEncoder->encode('test_redirect')); + $this->dispatch('customer/account/loginPost'); + $this->assertFalse($this->session->isLoggedIn()); + $this->assertRedirect($this->stringContains('customer/account/')); + $this->assertSessionMessages( + $this->equalTo([new Phrase('Invalid Form Key. Please refresh the page.')]), + MessageInterface::TYPE_ERROR + ); + } + + /** + * Prepare request + * + * @param string|null $email + * @param string|null $password + * @return void + */ + private function prepareRequest(?string $email, ?string $password): void + { + $this->getRequest()->setMethod(HttpRequest::METHOD_POST); + $this->getRequest()->setPostValue([ + 'login' => [ + 'username' => $email, + 'password' => $password, + ], + ]); + } +} diff --git a/dev/tests/integration/testsuite/Magento/Customer/Controller/AccountTest.php b/dev/tests/integration/testsuite/Magento/Customer/Controller/AccountTest.php index df4acf3acca91..7b0a4823f701e 100644 --- a/dev/tests/integration/testsuite/Magento/Customer/Controller/AccountTest.php +++ b/dev/tests/integration/testsuite/Magento/Customer/Controller/AccountTest.php @@ -8,17 +8,15 @@ use Magento\Customer\Api\CustomerRepositoryInterface; use Magento\Customer\Api\Data\CustomerInterface; -use Magento\Customer\Model\Account\Redirect; use Magento\Customer\Model\CustomerRegistry; use Magento\Customer\Model\Session; use Magento\Framework\Api\FilterBuilder; use Magento\Framework\Api\SearchCriteriaBuilder; -use Magento\Framework\App\Config\ScopeConfigInterface; -use Magento\Framework\App\Config\Value; use Magento\Framework\App\Http; use Magento\Framework\App\Request\Http as HttpRequest; use Magento\Framework\Data\Form\FormKey; use Magento\Framework\Message\MessageInterface; +use Magento\Framework\Phrase; use Magento\Framework\Serialize\Serializer\Json; use Magento\Framework\Stdlib\CookieManagerInterface; use Magento\Store\Model\StoreManager; @@ -26,10 +24,8 @@ use Magento\TestFramework\Helper\Bootstrap; use Magento\TestFramework\Mail\Template\TransportBuilderMock; use Magento\TestFramework\Request; -use Magento\TestFramework\Response; use Magento\Theme\Controller\Result\MessagePlugin; use PHPUnit\Framework\Constraint\StringContains; -use Zend\Stdlib\Parameters; /** * @SuppressWarnings(PHPMD.CouplingBetweenObjects) @@ -77,35 +73,6 @@ public function testIndexAction() $this->assertContains('Green str, 67', $body); } - /** - * @magentoDataFixture Magento/Customer/_files/customer_no_password.php - */ - public function testLoginWithIncorrectPassword() - { - $expectedMessage = 'The account sign-in was incorrect or your account is disabled temporarily. ' - . 'Please wait and try again later.'; - $this->getRequest() - ->setMethod('POST') - ->setPostValue( - [ - 'login' => [ - 'username' => 'customer@example.com', - 'password' => '123123q' - ] - ] - ); - - $this->dispatch('customer/account/loginPost'); - $this->assertRedirect($this->stringContains('customer/account/login')); - $this->assertSessionMessages( - $this->equalTo( - [ - $expectedMessage - ] - ) - ); - } - /** * Test sign up form displaying. */ @@ -132,35 +99,6 @@ public function testLogoutAction() $this->assertRedirect($this->stringContains('customer/account/logoutSuccess')); } - /** - * Test that forgot password email message displays special characters correctly. - * - * @codingStandardsIgnoreStart - * @magentoConfigFixture current_store customer/password/limit_password_reset_requests_method 0 - * @magentoConfigFixture current_store customer/password/forgot_email_template customer_password_forgot_email_template - * @magentoConfigFixture current_store customer/password/forgot_email_identity support - * @magentoConfigFixture current_store general/store_information/name Test special' characters - * @magentoConfigFixture current_store customer/captcha/enable 0 - * @magentoDataFixture Magento/Customer/_files/customer.php - * @codingStandardsIgnoreEnd - */ - public function testForgotPasswordEmailMessageWithSpecialCharacters() - { - $email = 'customer@example.com'; - - $this->getRequest()->setPostValue(['email' => $email]); - $this->getRequest()->setMethod(HttpRequest::METHOD_POST); - - $this->dispatch('customer/account/forgotPasswordPost'); - $this->assertRedirect($this->stringContains('customer/account/')); - - $subject = $this->transportBuilderMock->getSentMessage()->getSubject(); - $this->assertContains( - 'Test special\' characters', - $subject - ); - } - /** * @magentoDataFixture Magento/Customer/_files/customer.php */ @@ -290,6 +228,10 @@ public function testNoFormKeyCreatePostAction() $this->assertNull($this->getCustomerByEmail('test1@email.com')); $this->assertRedirect($this->stringEndsWith('customer/account/create/')); + $this->assertSessionMessages( + $this->equalTo([new Phrase('Invalid Form Key. Please refresh the page.')]), + MessageInterface::TYPE_ERROR + ); } /** @@ -397,56 +339,6 @@ public function testActiveUserConfirmationAction() ); } - /** - * @codingStandardsIgnoreStart - * @magentoConfigFixture current_store customer/password/limit_password_reset_requests_method 0 - * @magentoConfigFixture current_store customer/password/forgot_email_template customer_password_forgot_email_template - * @magentoConfigFixture current_store customer/password/forgot_email_identity support - * @magentoConfigFixture current_store customer/captcha/enable 0 - * @magentoDataFixture Magento/Customer/_files/customer.php - * @codingStandardsIgnoreEnd - */ - public function testForgotPasswordPostAction() - { - $email = 'customer@example.com'; - - $this->getRequest()->setMethod(HttpRequest::METHOD_POST); - $this->getRequest()->setPostValue(['email' => $email]); - - $this->dispatch('customer/account/forgotPasswordPost'); - $this->assertRedirect($this->stringContains('customer/account/')); - - $message = __( - 'If there is an account associated with %1 you will receive an email with a link to reset your password.', - $email - ); - $this->assertSessionMessages( - $this->equalTo([$message]), - MessageInterface::TYPE_SUCCESS - ); - } - - /** - * @magentoConfigFixture current_store customer/captcha/enable 0 - */ - public function testForgotPasswordPostWithBadEmailAction() - { - $this->getRequest()->setMethod(HttpRequest::METHOD_POST); - $this->getRequest() - ->setPostValue( - [ - 'email' => 'bad@email', - ] - ); - - $this->dispatch('customer/account/forgotPasswordPost'); - $this->assertRedirect($this->stringContains('customer/account/forgotpassword')); - $this->assertSessionMessages( - $this->equalTo(['The email address is incorrect. Verify the email address and try again.']), - MessageInterface::TYPE_ERROR - ); - } - /** * @magentoDataFixture Magento/Customer/_files/customer.php */ @@ -509,11 +401,12 @@ public function testEditAction() $this->assertEquals(200, $this->getResponse()->getHttpResponseCode(), $body); $this->assertContains('<div class="field field-name-firstname required">', $body); // Verify the password check box is not checked - $this->assertContains( - '<input type="checkbox" name="change_password" id="change-password" ' - . 'data-role="change-password" value="1" title="Change Password" class="checkbox" />', - $body - ); + $expectedString = <<<EXPECTED_HTML +<input type="checkbox" name="change_password" id="change-password" data-role="change-password" value="1" + title="Change Password" + class="checkbox" /> +EXPECTED_HTML; + $this->assertContains($expectedString, $body); } /** @@ -529,12 +422,12 @@ public function testChangePasswordEditAction() $this->assertEquals(200, $this->getResponse()->getHttpResponseCode(), $body); $this->assertContains('<div class="field field-name-firstname required">', $body); // Verify the password check box is checked - $this->assertContains( - '<input type="checkbox" name="change_password" id="change-password" ' - . 'data-role="change-password" value="1" title="Change Password" checked="checked" ' - . 'class="checkbox" />', - $body - ); + $expectedString = <<<EXPECTED_HTML +<input type="checkbox" name="change_password" id="change-password" data-role="change-password" value="1" + title="Change Password" + checked="checked" class="checkbox" /> +EXPECTED_HTML; + $this->assertContains($expectedString, $body); } /** @@ -719,40 +612,15 @@ public function testWrongConfirmationEditPostAction() ); } - /** - * Test redirect customer to account dashboard after logging in. - * - * @param bool|null $redirectDashboard - * @param string $redirectUrl - * @magentoDbIsolation enabled - * @magentoAppIsolation enabled - * @magentoDataFixture Magento/Customer/_files/customer.php - * @dataProvider loginPostRedirectDataProvider - */ - public function testLoginPostRedirect($redirectDashboard, string $redirectUrl) - { - if (isset($redirectDashboard)) { - $this->_objectManager->get(ScopeConfigInterface::class)->setValue( - 'customer/startup/redirect_dashboard', - $redirectDashboard - ); - } - $this->_objectManager->get(Redirect::class)->setRedirectCookie('test'); - $configValue = $this->_objectManager->create(Value::class); - $configValue->load('web/unsecure/base_url', 'path'); - $baseUrl = $configValue->getValue() ?: 'http://localhost/'; - $request = $this->prepareRequest(); - $app = $this->_objectManager->create(Http::class, ['_request' => $request]); - $response = $app->launch(); - $this->assertResponseRedirect($response, $baseUrl . $redirectUrl); - $this->assertTrue($this->_objectManager->get(Session::class)->isLoggedIn()); - } - /** * Register Customer with email confirmation. * * @magentoDataFixture Magento/Customer/_files/customer_confirmation_config_enable.php * @return void + * @throws \Magento\Framework\Exception\InputException + * @throws \Magento\Framework\Exception\LocalizedException + * @throws \Magento\Framework\Exception\NoSuchEntityException + * @throws \Magento\Framework\Stdlib\Cookie\FailureToSendException */ public function testRegisterCustomerWithEmailConfirmation(): void { @@ -832,14 +700,14 @@ public function testConfirmationEmailWithSpecialCharacters(): void $message = $this->transportBuilderMock->getSentMessage(); $rawMessage = $message->getRawMessage(); - /** @var \Zend\Mime\Part $messageBodyPart */ + /** @var \Laminas\Mime\Part $messageBodyPart */ $messageBodyParts = $message->getBody()->getParts(); $messageBodyPart = reset($messageBodyParts); $messageEncoding = $messageBodyPart->getCharset(); $name = 'John Smith'; if (strtoupper($messageEncoding) !== 'ASCII') { - $name = \Zend\Mail\Header\HeaderWrap::mimeEncodeValue($name, $messageEncoding); + $name = \Laminas\Mail\Header\HeaderWrap::mimeEncodeValue($name, $messageEncoding); } $nameEmail = sprintf('%s <%s>', $name, $email); @@ -866,6 +734,10 @@ public function testConfirmationEmailWithSpecialCharacters(): void * @magentoConfigFixture current_store customer/captcha/enable 0 * * @return void + * @throws \Magento\Framework\Exception\InputException + * @throws \Magento\Framework\Exception\LocalizedException + * @throws \Magento\Framework\Exception\NoSuchEntityException + * @throws \Magento\Framework\Exception\State\InputMismatchException */ public function testResetPasswordWhenEmailChanged(): void { @@ -994,20 +866,6 @@ private function resetRequest(): void $this->_request = null; } - /** - * Data provider for testLoginPostRedirect. - * - * @return array - */ - public function loginPostRedirectDataProvider() - { - return [ - [null, 'index.php/'], - [0, 'index.php/'], - [1, 'index.php/customer/account/'], - ]; - } - /** * @param string $email * @return void @@ -1076,44 +934,6 @@ private function getCustomerByEmail($email) return $customer; } - /** - * Prepare request for customer login. - * - * @return Request - */ - private function prepareRequest() - { - $post = new Parameters( - [ - 'form_key' => $this->_objectManager->get(FormKey::class)->getFormKey(), - 'login' => [ - 'username' => 'customer@example.com', - 'password' => 'password' - ] - ] - ); - $request = $this->getRequest(); - $formKey = $this->_objectManager->get(FormKey::class); - $request->setParam('form_key', $formKey->getFormKey()); - $request->setMethod(Request::METHOD_POST); - $request->setRequestUri('customer/account/loginPost/'); - $request->setPost($post); - return $request; - } - - /** - * Assert response is redirect. - * - * @param Response $response - * @param string $redirectUrl - * @return void - */ - private function assertResponseRedirect(Response $response, string $redirectUrl) - { - $this->assertTrue($response->isRedirect()); - $this->assertSame($redirectUrl, $response->getHeader('Location')->getUri()); - } - /** * Add new request info (request uri, path info, action name). * diff --git a/dev/tests/integration/testsuite/Magento/Customer/Controller/Address/Delete/DeleteAddressTest.php b/dev/tests/integration/testsuite/Magento/Customer/Controller/Address/Delete/DeleteAddressTest.php new file mode 100644 index 0000000000000..86a443d7aa3e1 --- /dev/null +++ b/dev/tests/integration/testsuite/Magento/Customer/Controller/Address/Delete/DeleteAddressTest.php @@ -0,0 +1,217 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +declare(strict_types=1); + +namespace Magento\Customer\Controller\Address\Delete; + +use Magento\Customer\Api\AddressRepositoryInterface; +use Magento\Customer\Api\CustomerRepositoryInterface; +use Magento\Customer\Api\Data\AddressInterface; +use Magento\Customer\Model\Session; +use Magento\Framework\App\Request\Http; +use Magento\Framework\Escaper; +use Magento\Framework\Exception\LocalizedException; +use Magento\Framework\Message\MessageInterface; +use Magento\TestFramework\TestCase\AbstractController; + +/** + * Test cases related to check that customer address correctly deleted on frontend + * or wasn't deleted and proper error message appears. + * + * @magentoAppArea frontend + * @magentoDbIsolation enabled + * + * @see \Magento\Customer\Controller\Address\Delete::execute + */ +class DeleteAddressTest extends AbstractController +{ + /** + * @var Escaper + */ + private $escaper; + + /** + * @var Session + */ + private $customerSession; + + /** + * @var AddressRepositoryInterface + */ + private $addressRepository; + + /** + * @var CustomerRepositoryInterface + */ + private $customerRepository; + + /** + * @inheritdoc + */ + protected function setUp() + { + parent::setUp(); + $this->escaper = $this->_objectManager->get(Escaper::class); + $this->customerSession = $this->_objectManager->get(Session::class); + $this->addressRepository = $this->_objectManager->get(AddressRepositoryInterface::class); + $this->customerRepository = $this->_objectManager->get(CustomerRepositoryInterface::class); + } + + /** + * Assert that customer address deleted successfully. + * + * @magentoDataFixture Magento/Customer/_files/customer.php + * @magentoDataFixture Magento/Customer/_files/customer_address.php + * + * @return void + */ + public function testSuccessDeleteExistCustomerAddress(): void + { + $customer = $this->customerRepository->get('customer@example.com'); + $customerAddresses = $customer->getAddresses() ?? []; + $this->assertCount(1, $customerAddresses); + /** @var AddressInterface $currentCustomerAddress */ + $currentCustomerAddress = reset($customerAddresses); + $this->customerSession->setCustomerId($customer->getId()); + $this->performAddressDeleteRequest((int)$currentCustomerAddress->getId()); + $this->checkRequestPerformedSuccessfully(); + $customer = $this->customerRepository->get('customer@example.com'); + $this->assertCount(0, $customer->getAddresses() ?? []); + try { + $this->addressRepository->getById((int)$currentCustomerAddress->getId()); + $this->fail('Customer address is not deleted.'); + } catch (LocalizedException $e) { + //Do nothing, this block mean that address deleted successfully from DB. + } + } + + /** + * Check that customer address will not be deleted if we don't pass address ID parameter. + * + * @magentoDataFixture Magento/Customer/_files/customer.php + * @magentoDataFixture Magento/Customer/_files/customer_address.php + * + * @return void + */ + public function testDeleteWithoutParam(): void + { + $customer = $this->customerRepository->get('customer@example.com'); + $customerAddresses = $customer->getAddresses() ?? []; + $this->assertCount(1, $customerAddresses); + /** @var AddressInterface $currentCustomerAddress */ + $currentCustomerAddress = reset($customerAddresses); + $this->customerSession->setCustomerId($customer->getId()); + $this->performAddressDeleteRequest(); + $this->assertRedirect($this->stringContains('customer/address/index')); + $customer = $this->customerRepository->get('customer@example.com'); + $this->assertCount(1, $customer->getAddresses() ?? []); + $this->checkAddressWasntDeleted((int)$currentCustomerAddress->getId()); + } + + /** + * Check that customer address will not be deleted if customer id in address and in session are not equals. + * + * @magentoDataFixture Magento/Customer/_files/customer.php + * @magentoDataFixture Magento/Customer/_files/customer_address.php + * @magentoDataFixture Magento/Customer/_files/customer_with_uk_address.php + * + * @return void + */ + public function testDeleteDifferentCustomerAddress(): void + { + $firstCustomer = $this->customerRepository->get('customer@example.com'); + $customerAddresses = $firstCustomer->getAddresses() ?? []; + $this->assertCount(1, $customerAddresses); + /** @var AddressInterface $currentCustomerAddress */ + $currentCustomerAddress = reset($customerAddresses); + $this->customerSession->setCustomerId('1'); + $secondCustomer = $this->customerRepository->get('customer_uk_address@test.com'); + $secondCustomerAddresses = $secondCustomer->getAddresses() ?? []; + /** @var AddressInterface $secondCustomerAddress */ + $secondCustomerAddress = reset($secondCustomerAddresses); + $this->performAddressDeleteRequest((int)$secondCustomerAddress->getId()); + $this->checkRequestPerformedWithError(true); + $firstCustomer = $this->customerRepository->get('customer@example.com'); + $this->assertCount(1, $firstCustomer->getAddresses() ?? []); + $this->checkAddressWasntDeleted((int)$currentCustomerAddress->getId()); + } + + /** + * Check that error message appear if we try to delete non-exits address. + * + * @magentoDataFixture Magento/Customer/_files/customer.php + * + * @return void + */ + public function testDeleteNonExistAddress(): void + { + $customer = $this->customerRepository->get('customer@example.com'); + $this->customerSession->setCustomerId($customer->getId()); + $this->performAddressDeleteRequest(999); + $this->checkRequestPerformedWithError(); + } + + /** + * Perform delete request by provided address id. + * + * @param int|null $processAddressId + * @return void + */ + private function performAddressDeleteRequest(?int $processAddressId = null): void + { + $this->getRequest()->setMethod(Http::METHOD_POST); + if (null !== $processAddressId) { + $this->getRequest()->setPostValue(['id' => $processAddressId]); + } + $this->dispatch('customer/address/delete'); + } + + /** + * Check that delete address request performed successfully + * (proper success message and redirect to customer/address/index are appear). + * + * @return void + */ + private function checkRequestPerformedSuccessfully(): void + { + $this->assertRedirect($this->stringContains('customer/address/index')); + $this->assertSessionMessages( + $this->equalTo([(string)__('You deleted the address.')]), + MessageInterface::TYPE_SUCCESS + ); + } + + /** + * Check that delete address request performed with error. + * (proper error messages and redirect to customer/address/edit are appear). + * + * @param bool $isNeedEscapeMessage + * @return void + */ + private function checkRequestPerformedWithError(bool $isNeedEscapeMessage = false): void + { + $message = (string)__("We can't delete the address right now."); + if ($isNeedEscapeMessage) { + $message = $this->escaper->escapeHtml($message); + } + $this->assertSessionMessages($this->contains($message), MessageInterface::TYPE_ERROR); + } + + /** + * Assert that customer address wasn't deleted. + * + * @param int $addressId + * @return void + */ + private function checkAddressWasntDeleted(int $addressId): void + { + try { + $this->addressRepository->getById($addressId); + } catch (LocalizedException $e) { + $this->fail('Expects that customer address will not be deleted.'); + } + } +} diff --git a/dev/tests/integration/testsuite/Magento/Customer/Controller/Address/FormPost/CreateAddressTest.php b/dev/tests/integration/testsuite/Magento/Customer/Controller/Address/FormPost/CreateAddressTest.php new file mode 100644 index 0000000000000..1e152008043a7 --- /dev/null +++ b/dev/tests/integration/testsuite/Magento/Customer/Controller/Address/FormPost/CreateAddressTest.php @@ -0,0 +1,391 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +declare(strict_types=1); + +namespace Magento\Customer\Controller\Address\FormPost; + +use Magento\Customer\Api\CustomerRepositoryInterface; +use Magento\Customer\Api\Data\AddressInterface; +use Magento\Customer\Api\Data\RegionInterface; +use Magento\Customer\Model\CustomerRegistry; +use Magento\Customer\Model\Session; +use Magento\Framework\App\Request\Http; +use Magento\Framework\Escaper; +use Magento\Framework\Message\MessageInterface; +use Magento\TestFramework\Directory\Model\GetRegionIdByName; +use Magento\TestFramework\TestCase\AbstractController; + +/** + * Test cases related to check that customer address correctly created from + * customer account page on frontend or wasn't create and proper error message appears. + * + * @magentoDataFixture Magento/Customer/_files/customer_no_address.php + * + * @magentoAppArea frontend + * @magentoDbIsolation enabled + * + * @see \Magento\Customer\Controller\Address\FormPost::execute + */ +class CreateAddressTest extends AbstractController +{ + /** + * POST static data for create customer address via controller on frontend. + */ + private const STATIC_POST_ADDRESS_DATA = [ + AddressInterface::TELEPHONE => '+380505282812', + AddressInterface::POSTCODE => 75477, + AddressInterface::COUNTRY_ID => 'US', + 'custom_region_name' => 'Alabama', + AddressInterface::CITY => 'CityM', + AddressInterface::STREET => [ + 'Green str, 67', + ], + AddressInterface::FIRSTNAME => 'John', + AddressInterface::LASTNAME => 'Smith', + ]; + + /** + * @var Escaper + */ + private $escaper; + + /** + * @var Session + */ + private $customerSession; + + /** + * @var CustomerRegistry + */ + private $customerRegistry; + + /** + * @var GetRegionIdByName + */ + private $getRegionIdByName; + + /** + * @var CustomerRepositoryInterface + */ + private $customerRepository; + + /** + * @inheritdoc + */ + protected function setUp() + { + parent::setUp(); + $this->escaper = $this->_objectManager->get(Escaper::class); + $this->customerSession = $this->_objectManager->get(Session::class); + $this->customerRegistry = $this->_objectManager->get(CustomerRegistry::class); + $this->getRegionIdByName = $this->_objectManager->get(GetRegionIdByName::class); + $this->customerRepository = $this->_objectManager->get(CustomerRepositoryInterface::class); + $this->customerSession->setCustomerId('5'); + } + + /** + * @inheritdoc + */ + protected function tearDown() + { + $this->customerSession->setCustomerId(null); + $this->customerRegistry->removeByEmail('customer5@example.com'); + parent::tearDown(); + } + + /** + * Assert that default or non-default customer address successfully created via controller on frontend. + * + * @dataProvider postDataForSuccessCreateDefaultAddressDataProvider + * + * @param array $postData + * @param bool $isShippingDefault + * @param bool $isBillingDefault + * @return void + */ + public function testAddressSuccessfullyCreatedAsDefaultForCustomer( + array $postData, + bool $isShippingDefault, + bool $isBillingDefault + ): void { + $customer = $this->customerRepository->get('customer5@example.com'); + $this->assertNull($customer->getDefaultShipping(), 'Customer already have default shipping address'); + $this->assertNull($customer->getDefaultBilling(), 'Customer already have default billing address'); + $this->assertEmpty($customer->getAddresses(), 'Customer already has address'); + $this->performRequestWithData($postData); + $this->checkRequestPerformedSuccessfully(); + $customer = $this->customerRepository->get('customer5@example.com'); + $customerAddresses = $customer->getAddresses(); + $this->assertCount(1, $customerAddresses); + /** @var AddressInterface $address */ + $address = reset($customerAddresses); + $expectedShippingId = $isShippingDefault ? $address->getId() : null; + $expectedBillingId = $isBillingDefault ? $address->getId() : null; + $this->assertEquals($expectedShippingId, $customer->getDefaultShipping()); + $this->assertEquals($expectedBillingId, $customer->getDefaultBilling()); + } + + /** + * Data provider which contain proper POST data for create default or non-default customer address. + * + * @return array + */ + public function postDataForSuccessCreateDefaultAddressDataProvider(): array + { + return [ + 'any_addresses_are_default' => [ + array_merge( + self::STATIC_POST_ADDRESS_DATA, + [AddressInterface::DEFAULT_SHIPPING => 0, AddressInterface::DEFAULT_BILLING => 0] + ), + false, + false, + ], + 'shipping_address_is_default' => [ + array_merge( + self::STATIC_POST_ADDRESS_DATA, + [AddressInterface::DEFAULT_SHIPPING => 1, AddressInterface::DEFAULT_BILLING => 0] + ), + true, + false, + ], + 'billing_address_is_default' => [ + array_merge( + self::STATIC_POST_ADDRESS_DATA, + [AddressInterface::DEFAULT_SHIPPING => 0, AddressInterface::DEFAULT_BILLING => 1] + ), + false, + true, + ], + 'all_addresses_are_default' => [ + array_merge( + self::STATIC_POST_ADDRESS_DATA, + [AddressInterface::DEFAULT_SHIPPING => 1, AddressInterface::DEFAULT_BILLING => 1] + ), + true, + true, + ], + ]; + } + + /** + * Assert that customer address successfully created via controller on frontend. + * + * @dataProvider postDataForSuccessCreateAddressDataProvider + * + * @param array $postData + * @param array $expectedData + * @return void + */ + public function testAddressSuccessfullyCreatedForCustomer(array $postData, array $expectedData): void + { + if (isset($expectedData['custom_region_name'])) { + $expectedData[AddressInterface::REGION_ID] = $this->getRegionIdByName->execute( + $expectedData['custom_region_name'], + $expectedData[AddressInterface::COUNTRY_ID] + ); + unset($expectedData['custom_region_name']); + } + $this->performRequestWithData($postData); + $this->checkRequestPerformedSuccessfully(); + $customer = $this->customerRepository->get('customer5@example.com'); + $customerAddresses = $customer->getAddresses(); + $this->assertCount(1, $customerAddresses); + /** @var AddressInterface $address */ + $address = reset($customerAddresses); + $createdAddressData = $address->__toArray(); + foreach ($expectedData as $fieldCode => $expectedValue) { + $this->assertArrayHasKey($fieldCode, $createdAddressData, "Field $fieldCode wasn't found."); + $this->assertEquals($expectedValue, $createdAddressData[$fieldCode]); + } + } + + /** + * Data provider which contain proper POST data for create customer address. + * + * @return array + */ + public function postDataForSuccessCreateAddressDataProvider(): array + { + return [ + 'required_fields_valid_data' => [ + self::STATIC_POST_ADDRESS_DATA, + [ + AddressInterface::TELEPHONE => '+380505282812', + AddressInterface::COUNTRY_ID => 'US', + AddressInterface::POSTCODE => 75477, + 'custom_region_name' => 'Alabama', + AddressInterface::FIRSTNAME => 'John', + AddressInterface::LASTNAME => 'Smith', + AddressInterface::STREET => ['Green str, 67'], + AddressInterface::CITY => 'CityM', + ], + ], + 'required_field_empty_postcode_for_uk' => [ + array_replace( + self::STATIC_POST_ADDRESS_DATA, + [AddressInterface::POSTCODE => '', AddressInterface::COUNTRY_ID => 'GB'] + ), + [ + AddressInterface::COUNTRY_ID => 'GB', + AddressInterface::POSTCODE => null, + ], + ], + 'required_field_empty_region_id_for_ua' => [ + array_replace( + self::STATIC_POST_ADDRESS_DATA, + [AddressInterface::REGION_ID => '', AddressInterface::COUNTRY_ID => 'UA'] + ), + [ + AddressInterface::COUNTRY_ID => 'UA', + AddressInterface::REGION => [ + RegionInterface::REGION => null, + RegionInterface::REGION_CODE => null, + RegionInterface::REGION_ID => 0, + ], + ], + ], + 'required_field_street_as_array' => [ + array_replace(self::STATIC_POST_ADDRESS_DATA, [AddressInterface::STREET => ['', 'Green str, 67']]), + [AddressInterface::STREET => ['Green str, 67']], + ], + 'field_company_name' => [ + array_merge(self::STATIC_POST_ADDRESS_DATA, [AddressInterface::COMPANY => 'My company']), + [AddressInterface::COMPANY => 'My company'], + ], + 'field_vat_number' => [ + array_merge(self::STATIC_POST_ADDRESS_DATA, [AddressInterface::VAT_ID => 'My VAT number']), + [AddressInterface::VAT_ID => 'My VAT number'], + ], + ]; + } + + /** + * Assert that customer address wasn't created via controller on frontend + * when POST data broken. + * + * @dataProvider postDataForCreateAddressWithErrorDataProvider + * + * @param array $postData + * @param array $expectedSessionMessages + * @return void + */ + public function testAddressWasntCreatedForCustomer(array $postData, array $expectedSessionMessages): void + { + $this->performRequestWithData($postData); + $this->checkRequestPerformedWithInputValidationErrors($expectedSessionMessages); + } + + /** + * Data provider which contain broken POST data for create customer address with error. + * + * @return array + */ + public function postDataForCreateAddressWithErrorDataProvider(): array + { + return [ + 'empty_post_data' => [ + [], + [ + 'One or more input exceptions have occurred.', + '"firstname" is required. Enter and try again.', + '"lastname" is required. Enter and try again.', + '"street" is required. Enter and try again.', + '"city" is required. Enter and try again.', + '"telephone" is required. Enter and try again.', + '"postcode" is required. Enter and try again.', + '"countryId" is required. Enter and try again.', + ] + ], + 'required_field_empty_telephone' => [ + array_replace(self::STATIC_POST_ADDRESS_DATA, [AddressInterface::TELEPHONE => '']), + ['"telephone" is required. Enter and try again.'], + ], + 'required_field_empty_postcode_for_us' => [ + array_replace(self::STATIC_POST_ADDRESS_DATA, [AddressInterface::POSTCODE => '']), + ['"postcode" is required. Enter and try again.'], + ], +// TODO: Uncomment this variation after fix issue https://jira.corp.magento.com/browse/MC-31031 +// 'required_field_empty_region_id_for_us' => [ +// array_replace(self::STATIC_POST_ADDRESS_DATA, [AddressInterface::REGION_ID => '']), +// ['"regionId" is required. Enter and try again.'], +// ], + 'required_field_empty_firstname' => [ + array_replace(self::STATIC_POST_ADDRESS_DATA, [AddressInterface::FIRSTNAME => '']), + ['"firstname" is required. Enter and try again.'], + ], + 'required_field_empty_lastname' => [ + array_replace(self::STATIC_POST_ADDRESS_DATA, [AddressInterface::LASTNAME => '']), + ['"lastname" is required. Enter and try again.'], + ], + 'required_field_empty_street_as_string' => [ + array_replace(self::STATIC_POST_ADDRESS_DATA, [AddressInterface::STREET => '']), + ['"street" is required. Enter and try again.'], + ], + 'required_field_empty_street_as_array' => [ + array_replace(self::STATIC_POST_ADDRESS_DATA, [AddressInterface::STREET => []]), + ['"street" is required. Enter and try again.'], + ], + 'required_field_empty_city' => [ + array_replace(self::STATIC_POST_ADDRESS_DATA, [AddressInterface::CITY => '']), + ['"city" is required. Enter and try again.'], + ], + ]; + } + + /** + * Perform request with provided POST data. + * + * @param array $postData + * @return void + */ + private function performRequestWithData(array $postData): void + { + if (isset($postData['custom_region_name'])) { + $postData[AddressInterface::REGION_ID] = $this->getRegionIdByName->execute( + $postData['custom_region_name'], + $postData[AddressInterface::COUNTRY_ID] + ); + unset($postData['custom_region_name']); + } + + $this->getRequest()->setPostValue($postData)->setMethod(Http::METHOD_POST); + $this->dispatch('customer/address/formPost'); + } + + /** + * Check that save address request performed successfully + * (proper success message and redirect to customer/address/index are appear). + * + * @return void + */ + private function checkRequestPerformedSuccessfully(): void + { + $this->assertRedirect($this->stringContains('customer/address/index')); + $this->assertSessionMessages( + $this->equalTo([(string)__('You saved the address.')]), + MessageInterface::TYPE_SUCCESS + ); + } + + /** + * Check that save address request performed with input validation errors + * (proper error messages and redirect to customer/address/edit are appear). + * + * @param array $expectedSessionMessages + * @return void + */ + private function checkRequestPerformedWithInputValidationErrors(array $expectedSessionMessages): void + { + $this->assertRedirect($this->stringContains('customer/address/edit')); + foreach ($expectedSessionMessages as $expectedMessage) { + $this->assertSessionMessages( + $this->contains($this->escaper->escapeHtml((string)__($expectedMessage))), + MessageInterface::TYPE_ERROR + ); + } + } +} diff --git a/dev/tests/integration/testsuite/Magento/Customer/Controller/Address/FormPost/UpdateAddressTest.php b/dev/tests/integration/testsuite/Magento/Customer/Controller/Address/FormPost/UpdateAddressTest.php new file mode 100644 index 0000000000000..fb18cc45511c8 --- /dev/null +++ b/dev/tests/integration/testsuite/Magento/Customer/Controller/Address/FormPost/UpdateAddressTest.php @@ -0,0 +1,403 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +declare(strict_types=1); + +namespace Magento\Customer\Controller\Address\FormPost; + +use Magento\Customer\Api\CustomerRepositoryInterface; +use Magento\Customer\Api\Data\AddressInterface; +use Magento\Customer\Api\Data\RegionInterface; +use Magento\Customer\Model\AddressRegistry; +use Magento\Customer\Model\CustomerRegistry; +use Magento\Customer\Model\Session; +use Magento\Framework\App\Request\Http; +use Magento\Framework\Escaper; +use Magento\Framework\Message\MessageInterface; +use Magento\TestFramework\Directory\Model\GetRegionIdByName; +use Magento\TestFramework\TestCase\AbstractController; + +/** + * Test cases related to check that customer address correctly updated from + * customer account page on frontend or wasn't updated and proper error message appears. + * + * @magentoDataFixture Magento/Customer/_files/customer.php + * @magentoDataFixture Magento/Customer/_files/customer_address.php + * + * @magentoAppArea frontend + * @magentoDbIsolation enabled + * + * @see \Magento\Customer\Controller\Address\FormPost::execute + */ +class UpdateAddressTest extends AbstractController +{ + /** + * POST static data for update customer address via controller on frontend. + */ + private const STATIC_POST_ADDRESS_DATA = [ + AddressInterface::TELEPHONE => 9548642, + AddressInterface::POSTCODE => 95556, + AddressInterface::COUNTRY_ID => 'US', + 'custom_region_name' => 'Arkansas', + AddressInterface::CITY => 'Mukachevo', + AddressInterface::STREET => [ + 'Yellow str, 228', + ], + AddressInterface::FIRSTNAME => 'Foma', + AddressInterface::LASTNAME => 'Kiniaev', + ]; + + /** + * @var Escaper + */ + private $escaper; + + /** + * @var Session + */ + private $customerSession; + + /** + * @var AddressRegistry + */ + private $addressRegistry; + + /** + * @var CustomerRegistry + */ + private $customerRegistry; + + /** + * @var GetRegionIdByName + */ + private $getRegionIdByName; + + /** + * @var CustomerRepositoryInterface + */ + private $customerRepository; + + /** + * @var array + */ + private $processedAddressesIds = []; + + /** + * @inheritdoc + */ + protected function setUp() + { + parent::setUp(); + $this->escaper = $this->_objectManager->get(Escaper::class); + $this->customerSession = $this->_objectManager->get(Session::class); + $this->addressRegistry = $this->_objectManager->get(AddressRegistry::class); + $this->customerRegistry = $this->_objectManager->get(CustomerRegistry::class); + $this->getRegionIdByName = $this->_objectManager->get(GetRegionIdByName::class); + $this->customerRepository = $this->_objectManager->get(CustomerRepositoryInterface::class); + $this->customerSession->setCustomerId('1'); + $this->processedAddressesIds[] = 1; + } + + /** + * @inheritdoc + */ + protected function tearDown() + { + $this->customerSession->setCustomerId(null); + $this->customerRegistry->removeByEmail('customer@example.com'); + foreach ($this->processedAddressesIds as $addressesId) { + $this->addressRegistry->remove($addressesId); + } + parent::tearDown(); + } + + /** + * Assert that default customer address successfully changed via controller on frontend. + * + * @magentoDataFixture Magento/Customer/_files/customer.php + * @magentoDataFixture Magento/Customer/_files/customer_two_addresses.php + * + * @dataProvider postDataForSuccessCreateDefaultAddressDataProvider + * + * @param array $postData + * @param int $expectedShippingId + * @param int $expectedBillingId + * @return void + */ + public function testAddressSuccessfullyCreatedAsDefaultForCustomer( + array $postData, + int $expectedShippingId, + int $expectedBillingId + ): void { + $this->processedAddressesIds = [1, 2]; + $customer = $this->customerRepository->get('customer@example.com'); + $this->assertEquals(1, $customer->getDefaultShipping(), "Customer doesn't have shipping address"); + $this->assertEquals(1, $customer->getDefaultBilling(), "Customer doesn't have billing address"); + $this->performRequestWithData($postData, 2); + $this->checkRequestPerformedSuccessfully(); + $customer = $this->customerRepository->get('customer@example.com'); + $this->assertEquals($expectedShippingId, $customer->getDefaultShipping()); + $this->assertEquals($expectedBillingId, $customer->getDefaultBilling()); + } + + /** + * Data provider which contain proper POST data for change default customer address. + * + * @return array + */ + public function postDataForSuccessCreateDefaultAddressDataProvider(): array + { + return [ + 'any_addresses_are_default' => [ + array_merge( + self::STATIC_POST_ADDRESS_DATA, + [AddressInterface::DEFAULT_SHIPPING => 2, AddressInterface::DEFAULT_BILLING => 2] + ), + 2, + 2, + ], + 'shipping_address_is_default' => [ + array_merge( + self::STATIC_POST_ADDRESS_DATA, + [AddressInterface::DEFAULT_BILLING => 2] + ), + 1, + 2, + ], + 'billing_address_is_default' => [ + array_merge( + self::STATIC_POST_ADDRESS_DATA, + [AddressInterface::DEFAULT_SHIPPING => 2] + ), + 2, + 1, + ], + ]; + } + + /** + * Assert that customer address successfully updated via controller on frontend. + * + * @dataProvider postDataForSuccessUpdateAddressDataProvider + * + * @param array $postData + * @param array $expectedData + * @return void + */ + public function testAddressSuccessfullyUpdatedForCustomer(array $postData, array $expectedData): void + { + if (isset($expectedData['custom_region_name'])) { + $expectedData[AddressInterface::REGION_ID] = $this->getRegionIdByName->execute( + $expectedData['custom_region_name'], + $expectedData[AddressInterface::COUNTRY_ID] + ); + unset($expectedData['custom_region_name']); + } + $this->performRequestWithData($postData, 1); + $this->checkRequestPerformedSuccessfully(); + $customer = $this->customerRepository->get('customer@example.com'); + $customerAddresses = $customer->getAddresses(); + $this->assertCount(1, $customerAddresses); + /** @var AddressInterface $address */ + $address = reset($customerAddresses); + $createdAddressData = $address->__toArray(); + foreach ($expectedData as $fieldCode => $expectedValue) { + if (null === $expectedValue) { + $this->assertArrayNotHasKey($fieldCode, $createdAddressData); + continue; + } + $this->assertArrayHasKey($fieldCode, $createdAddressData, "Field $fieldCode wasn't found."); + $this->assertEquals($expectedValue, $createdAddressData[$fieldCode]); + } + } + + /** + * Data provider which contain proper POST data for update customer address. + * + * @return array + */ + public function postDataForSuccessUpdateAddressDataProvider(): array + { + return [ + 'required_fields_valid_data' => [ + self::STATIC_POST_ADDRESS_DATA, + [ + AddressInterface::TELEPHONE => 9548642, + AddressInterface::COUNTRY_ID => 'US', + AddressInterface::POSTCODE => 95556, + 'custom_region_name' => 'Arkansas', + AddressInterface::FIRSTNAME => 'Foma', + AddressInterface::LASTNAME => 'Kiniaev', + AddressInterface::STREET => ['Yellow str, 228'], + AddressInterface::CITY => 'Mukachevo', + ], + ], + 'required_field_empty_postcode_for_uk' => [ + array_replace( + self::STATIC_POST_ADDRESS_DATA, + [AddressInterface::POSTCODE => '', AddressInterface::COUNTRY_ID => 'GB'] + ), + [ + AddressInterface::COUNTRY_ID => 'GB', + AddressInterface::POSTCODE => null, + ], + ], + 'required_field_empty_region_id_for_ua' => [ + array_replace( + self::STATIC_POST_ADDRESS_DATA, + [AddressInterface::REGION_ID => '', AddressInterface::COUNTRY_ID => 'UA'] + ), + [ + AddressInterface::COUNTRY_ID => 'UA', + AddressInterface::REGION => [ + RegionInterface::REGION => null, + RegionInterface::REGION_CODE => null, + RegionInterface::REGION_ID => 0, + ], + ], + ], + 'required_field_street_as_array' => [ + array_replace(self::STATIC_POST_ADDRESS_DATA, [AddressInterface::STREET => ['', 'Green str, 67']]), + [AddressInterface::STREET => ['Green str, 67']], + ], + 'field_company_name' => [ + array_merge(self::STATIC_POST_ADDRESS_DATA, [AddressInterface::COMPANY => 'My company']), + [AddressInterface::COMPANY => 'My company'], + ], + 'field_vat_number' => [ + array_merge(self::STATIC_POST_ADDRESS_DATA, [AddressInterface::VAT_ID => 'My VAT number']), + [AddressInterface::VAT_ID => 'My VAT number'], + ], + ]; + } + + /** + * Assert that customer address wasn't updated via controller on frontend + * when POST data broken. + * + * @dataProvider postDataForUpdateAddressWithErrorDataProvider + * + * @param array $postData + * @param array $expectedSessionMessages + * @return void + */ + public function testAddressWasntUpdatedForCustomer(array $postData, array $expectedSessionMessages): void + { + $this->performRequestWithData($postData, 1); + $this->checkRequestPerformedWithInputValidationErrors($expectedSessionMessages); + } + + /** + * Data provider which contain broken POST data for update customer address with error. + * + * @return array + */ + public function postDataForUpdateAddressWithErrorDataProvider(): array + { + return [ + 'empty_post_data' => [ + [], + [ + 'One or more input exceptions have occurred.', + '"firstname" is required. Enter and try again.', + '"lastname" is required. Enter and try again.', + '"street" is required. Enter and try again.', + '"city" is required. Enter and try again.', + '"telephone" is required. Enter and try again.', + '"postcode" is required. Enter and try again.', + '"countryId" is required. Enter and try again.', + ] + ], + 'required_field_empty_telephone' => [ + array_replace(self::STATIC_POST_ADDRESS_DATA, [AddressInterface::TELEPHONE => '']), + ['"telephone" is required. Enter and try again.'], + ], + 'required_field_empty_postcode_for_us' => [ + array_replace(self::STATIC_POST_ADDRESS_DATA, [AddressInterface::POSTCODE => '']), + ['"postcode" is required. Enter and try again.'], + ], +// TODO: Uncomment this variation after fix issue https://jira.corp.magento.com/browse/MC-31031 +// 'required_field_empty_region_id_for_us' => [ +// array_replace(self::STATIC_POST_ADDRESS_DATA, [AddressInterface::REGION_ID => '']), +// ['"regionId" is required. Enter and try again.'], +// ], + 'required_field_empty_firstname' => [ + array_replace(self::STATIC_POST_ADDRESS_DATA, [AddressInterface::FIRSTNAME => '']), + ['"firstname" is required. Enter and try again.'], + ], + 'required_field_empty_lastname' => [ + array_replace(self::STATIC_POST_ADDRESS_DATA, [AddressInterface::LASTNAME => '']), + ['"lastname" is required. Enter and try again.'], + ], + 'required_field_empty_street_as_string' => [ + array_replace(self::STATIC_POST_ADDRESS_DATA, [AddressInterface::STREET => '']), + ['"street" is required. Enter and try again.'], + ], + 'required_field_empty_street_as_array' => [ + array_replace(self::STATIC_POST_ADDRESS_DATA, [AddressInterface::STREET => []]), + ['"street" is required. Enter and try again.'], + ], + 'required_field_empty_city' => [ + array_replace(self::STATIC_POST_ADDRESS_DATA, [AddressInterface::CITY => '']), + ['"city" is required. Enter and try again.'], + ], + ]; + } + + /** + * Perform request with provided POST data. + * + * @param array $postData + * @param int $processAddressId + * @return void + */ + private function performRequestWithData(array $postData, int $processAddressId): void + { + $postData[AddressInterface::ID] = $processAddressId; + if (isset($postData['custom_region_name'])) { + $postData[AddressInterface::REGION_ID] = $this->getRegionIdByName->execute( + $postData['custom_region_name'], + $postData[AddressInterface::COUNTRY_ID] + ); + unset($postData['custom_region_name']); + } + + $this->getRequest()->setPostValue($postData)->setMethod(Http::METHOD_POST); + $this->dispatch('customer/address/formPost'); + } + + /** + * Check that save address request performed successfully + * (proper success message and redirect to customer/address/index are appear). + * + * @return void + */ + private function checkRequestPerformedSuccessfully(): void + { + $this->assertRedirect($this->stringContains('customer/address/index')); + $this->assertSessionMessages( + $this->equalTo([(string)__('You saved the address.')]), + MessageInterface::TYPE_SUCCESS + ); + } + + /** + * Check that save address request performed with input validation errors + * (proper error messages and redirect to customer/address/edit are appear). + * + * @param array $expectedSessionMessages + * @return void + */ + private function checkRequestPerformedWithInputValidationErrors(array $expectedSessionMessages): void + { + $this->assertRedirect($this->stringContains('customer/address/edit')); + foreach ($expectedSessionMessages as $expectedMessage) { + $this->assertSessionMessages( + $this->contains($this->escaper->escapeHtml((string)__($expectedMessage))), + MessageInterface::TYPE_ERROR + ); + } + } +} diff --git a/dev/tests/integration/testsuite/Magento/Customer/Controller/Adminhtml/Index/DeleteTest.php b/dev/tests/integration/testsuite/Magento/Customer/Controller/Adminhtml/Index/DeleteTest.php new file mode 100644 index 0000000000000..1f40c459c43e0 --- /dev/null +++ b/dev/tests/integration/testsuite/Magento/Customer/Controller/Adminhtml/Index/DeleteTest.php @@ -0,0 +1,104 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +declare(strict_types=1); + +namespace Magento\Customer\Controller\Adminhtml\Index; + +use Magento\Framework\App\Request\Http as HttpRequest; +use Magento\Framework\Data\Form\FormKey; +use Magento\Framework\Message\MessageInterface; +use Magento\TestFramework\TestCase\AbstractBackendController; + +/** + * Tests for delete customer via backend/customer/index/delete controller. + * + * @magentoAppArea adminhtml + */ +class DeleteTest extends AbstractBackendController +{ + /** @var FormKey */ + private $formKey; + + /** + * @inheritdoc + */ + protected function setUp() + { + parent::setUp(); + + $this->formKey = $this->_objectManager->get(FormKey::class); + } + + /** + * Delete customer + * + * @dataProvider deleteCustomerProvider + * @magentoDataFixture Magento/Customer/_files/customer_sample.php + * + * @param array $paramsData + * @param string $expected + * @return void + */ + public function testDeleteCustomer(array $paramsData, array $expected): void + { + $this->dispatchCustomerDelete($paramsData); + + $this->assertRedirect($this->stringContains('customer/index')); + $this->assertSessionMessages( + $this->equalTo([(string)__(...$expected['message'])]), + $expected['message_type'] + ); + } + + /** + * Delete customer provider + * + * @return array + */ + public function deleteCustomerProvider(): array + { + return [ + 'delete_customer_success' => [ + 'params_data' => [ + 'id' => 1, + ], + 'expected' => [ + 'message' => ['You deleted the customer.'], + 'message_type' => MessageInterface::TYPE_SUCCESS, + ], + ], + 'not_existing_customer_error' => [ + 'params_data' => [ + 'id' => 2, + ], + 'expected' => [ + 'message' => [ + 'No such entity with %fieldName = %fieldValue', + [ + 'fieldName' => 'customerId', + 'fieldValue' => '2', + ], + ], + 'message_type' => MessageInterface::TYPE_ERROR, + ], + ], + ]; + } + + /** + * Delete customer using backend/customer/index/delete action. + * + * @param array $params + * @return void + */ + private function dispatchCustomerDelete(array $params): void + { + $params['form_key'] = $this->formKey->getFormKey(); + $this->getRequest()->setMethod(HttpRequest::METHOD_POST); + $this->getRequest()->setParams($params); + $this->dispatch('backend/customer/index/delete'); + } +} diff --git a/dev/tests/integration/testsuite/Magento/Customer/Controller/Adminhtml/Index/InlineEditTest.php b/dev/tests/integration/testsuite/Magento/Customer/Controller/Adminhtml/Index/InlineEditTest.php new file mode 100644 index 0000000000000..9879c8300f66c --- /dev/null +++ b/dev/tests/integration/testsuite/Magento/Customer/Controller/Adminhtml/Index/InlineEditTest.php @@ -0,0 +1,153 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +declare(strict_types=1); + +namespace Magento\Customer\Controller\Adminhtml\Index; + +use Magento\Customer\Api\CustomerMetadataInterface; +use Magento\Customer\Api\CustomerRepositoryInterface; +use Magento\Customer\Api\Data\CustomerInterface; +use Magento\Eav\Model\AttributeRepository; +use Magento\Framework\App\Request\Http as HttpRequest; +use Magento\Framework\ObjectManagerInterface; +use Magento\Framework\Serialize\SerializerInterface; +use Magento\Store\Api\WebsiteRepositoryInterface; +use Magento\TestFramework\Helper\Bootstrap; +use Magento\TestFramework\TestCase\AbstractBackendController; + +/** + * Test inline edit action on customers grid. + * + * @magentoAppArea adminhtml + * @magentoDbIsolation enabled + */ +class InlineEditTest extends AbstractBackendController +{ + /** @var ObjectManagerInterface */ + private $objectManager; + + /** @var CustomerRepositoryInterface */ + private $customerRepository; + + /** @var SerializerInterface */ + private $json; + + /** @var WebsiteRepositoryInterface */ + private $websiteRepository; + + /** @var AttributeRepository */ + private $attributeRepository; + + /** + * @inheritdoc + */ + protected function setUp() + { + parent::setUp(); + + $this->objectManager = Bootstrap::getObjectManager(); + $this->customerRepository = $this->objectManager->get(CustomerRepositoryInterface::class); + $this->json = $this->objectManager->get(SerializerInterface::class); + $this->websiteRepository = $this->objectManager->get(WebsiteRepositoryInterface::class); + $this->attributeRepository = $this->objectManager->get(AttributeRepository::class); + } + + /** + * @magentoDataFixture Magento/Customer/_files/two_customers.php + * + * @return void + */ + public function testInlineEditAction(): void + { + $firstCustomer = $this->customerRepository->get('customer@example.com'); + $secondCustomer = $this->customerRepository->get('customer_two@example.com'); + $defaultWebsiteId = $this->websiteRepository->get('base')->getId(); + $genderId = $this->attributeRepository->get(CustomerMetadataInterface::ENTITY_TYPE_CUSTOMER, 'gender') + ->getSource()->getOptionId('Male'); + $params = [ + 'items' => [ + $firstCustomer->getId() => [ + CustomerInterface::EMAIL => 'updated_customer@example.com', + CustomerInterface::GROUP_ID => 2, + CustomerInterface::WEBSITE_ID => $defaultWebsiteId, + CustomerInterface::TAXVAT => 123123, + CustomerInterface::GENDER => $genderId, + ], + $secondCustomer->getId() => [ + CustomerInterface::EMAIL => 'updated_customer_two@example.com', + CustomerInterface::GROUP_ID => 3, + CustomerInterface::WEBSITE_ID => $defaultWebsiteId, + CustomerInterface::TAXVAT => 456456, + CustomerInterface::GENDER => $genderId, + ], + ], + 'isAjax' => true, + ]; + $actual = $this->performInlineEditRequest($params); + $this->assertEmpty($actual['messages']); + $this->assertFalse($actual['error']); + $this->assertCustomersData($params); + } + + /** + * @dataProvider inlineEditParametersDataProvider + * + * @param array $params + * @return void + */ + public function testInlineEditWithWrongParams(array $params): void + { + $actual = $this->performInlineEditRequest($params); + $this->assertEquals([(string)__('Please correct the data sent.')], $actual['messages']); + $this->assertTrue($actual['error']); + } + + /** + * @return array + */ + public function inlineEditParametersDataProvider(): array + { + return [ + [ + 'items' => [], + 'isAjax' => true, + ], + [ + 'items' => [], + ], + ]; + } + + /** + * Perform inline edit request. + * + * @param array $params + * @return array + */ + private function performInlineEditRequest(array $params): array + { + $this->getRequest()->setParams($params)->setMethod(HttpRequest::METHOD_POST); + $this->dispatch('backend/customer/index/inlineEdit'); + + return $this->json->unserialize($this->getResponse()->getBody()); + } + + /** + * Assert customers data. + * + * @param array $data + * @return void + */ + private function assertCustomersData(array $data): void + { + foreach ($data['items'] as $customerId => $expectedData) { + $customerData = $this->customerRepository->getById($customerId)->__toArray(); + foreach ($expectedData as $key => $value) { + $this->assertEquals($value, $customerData[$key]); + } + } + } +} diff --git a/dev/tests/integration/testsuite/Magento/Customer/Controller/Adminhtml/Index/MassUnsubscribeTest.php b/dev/tests/integration/testsuite/Magento/Customer/Controller/Adminhtml/Index/MassUnsubscribeTest.php new file mode 100644 index 0000000000000..586d06eb42b24 --- /dev/null +++ b/dev/tests/integration/testsuite/Magento/Customer/Controller/Adminhtml/Index/MassUnsubscribeTest.php @@ -0,0 +1,91 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +declare(strict_types=1); + +namespace Magento\Customer\Controller\Adminhtml\Index; + +use Magento\Framework\App\Request\Http as HttpRequest; +use Magento\Framework\Message\MessageInterface; +use Magento\Framework\ObjectManagerInterface; +use Magento\Newsletter\Model\ResourceModel\Subscriber\CollectionFactory; +use Magento\Newsletter\Model\Subscriber; +use Magento\TestFramework\Helper\Bootstrap; +use Magento\Customer\Api\CustomerRepositoryInterface; +use Magento\TestFramework\TestCase\AbstractBackendController; + +/** + * Test mass subscribe action on customers grid. + * + * @magentoAppArea adminhtml + * @magentoDbIsolation enabled + */ +class MassUnsubscribeTest extends AbstractBackendController +{ + /** @var ObjectManagerInterface */ + private $objectManager; + + /** @var CustomerRepositoryInterface */ + private $customerRepository; + + /** @var CollectionFactory */ + private $subscriberCollectionFactory; + + /** + * @inheritdoc + */ + protected function setUp() + { + parent::setUp(); + + $this->objectManager = Bootstrap::getObjectManager(); + $this->subscriberCollectionFactory = $this->objectManager->get(CollectionFactory::class); + $this->customerRepository = $this->objectManager->get(CustomerRepositoryInterface::class); + } + + /** + * @magentoDataFixture Magento/Newsletter/_files/three_subscribers.php + * + * @return void + */ + public function testMassUnsubscribeAction(): void + { + $params = [ + 'selected' => [1, 2, 3], + 'namespace' => 'customer_listing', + ]; + $this->getRequest()->setParams($params)->setMethod(HttpRequest::METHOD_POST); + $this->dispatch('backend/customer/index/massUnsubscribe'); + $this->assertRedirect($this->stringContains('backend/customer/index/index')); + $this->assertSessionMessages( + $this->equalTo([(string)__('A total of 3 record(s) were updated.')]), + MessageInterface::TYPE_SUCCESS + ); + $emails = ['customer@search.example.com', 'customer2@search.example.com', 'customer3@search.example.com']; + $collection = $this->subscriberCollectionFactory->create()->addFieldToFilter('subscriber_email', $emails) + ->addFieldToSelect('subscriber_status'); + $this->assertCount(3, $collection); + foreach ($collection as $subscriber) { + $this->assertEquals(Subscriber::STATUS_UNSUBSCRIBED, $subscriber->getData('subscriber_status')); + } + } + + /** + * @return void + */ + public function testMassSubscriberActionNoSelection(): void + { + $params = [ + 'namespace' => 'customer_listing', + ]; + $this->getRequest()->setParams($params)->setMethod(HttpRequest::METHOD_POST); + $this->dispatch('backend/customer/index/massUnsubscribe'); + $this->assertRedirect($this->stringContains('backend/customer/index/index')); + $this->assertSessionMessages( + $this->equalTo([(string)__('An item needs to be selected. Select and try again.')]), + MessageInterface::TYPE_ERROR + ); + } +} diff --git a/dev/tests/integration/testsuite/Magento/Customer/Controller/Adminhtml/Index/SaveTest.php b/dev/tests/integration/testsuite/Magento/Customer/Controller/Adminhtml/Index/SaveTest.php new file mode 100644 index 0000000000000..f532e2fcb7182 --- /dev/null +++ b/dev/tests/integration/testsuite/Magento/Customer/Controller/Adminhtml/Index/SaveTest.php @@ -0,0 +1,584 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +declare(strict_types=1); + +namespace Magento\Customer\Controller\Adminhtml\Index; + +use Magento\Backend\Model\Session; +use Magento\Customer\Api\CustomerNameGenerationInterface; +use Magento\Customer\Api\CustomerRepositoryInterface; +use Magento\Customer\Model\Data\Customer as CustomerData; +use Magento\Customer\Model\EmailNotification; +use Magento\Framework\App\Area; +use Magento\Framework\App\Request\Http as HttpRequest; +use Magento\Framework\Mail\Template\TransportBuilder; +use Magento\Framework\Mail\TransportInterface; +use Magento\Framework\Message\MessageInterface; +use Magento\Newsletter\Model\Subscriber; +use Magento\Newsletter\Model\SubscriberFactory; +use Magento\Store\Model\Store; +use Magento\Store\Model\StoreManagerInterface; +use Magento\TestFramework\TestCase\AbstractBackendController; + +/** + * Tests for save customer via backend/customer/index/save controller. + * + * @magentoAppArea adminhtml + * @SuppressWarnings(PHPMD.CouplingBetweenObjects) + */ +class SaveTest extends AbstractBackendController +{ + /** + * Base controller URL + * + * @var string + */ + private $baseControllerUrl = 'backend/customer/index/'; + + /** @var CustomerRepositoryInterface */ + private $customerRepository; + + /**@var CustomerNameGenerationInterface */ + private $customerViewHelper; + + /** @var SubscriberFactory */ + private $subscriberFactory; + + /** @var Session */ + private $session; + + /** @var StoreManagerInterface */ + private $storeManager; + + /** + * @inheritdoc + */ + protected function setUp() + { + parent::setUp(); + $this->customerRepository = $this->_objectManager->get(CustomerRepositoryInterface::class); + $this->customerViewHelper = $this->_objectManager->get(CustomerNameGenerationInterface::class); + $this->subscriberFactory = $this->_objectManager->get(SubscriberFactory::class); + $this->session = $this->_objectManager->get(Session::class); + $this->storeManager = $this->_objectManager->get(StoreManagerInterface::class); + } + + /** + * Create customer + * + * @dataProvider createCustomerProvider + * @magentoDbIsolation enabled + * + * @param array $postData + * @param array $expectedData + * @return void + */ + public function testCreateCustomer(array $postData, array $expectedData): void + { + $this->dispatchCustomerSave($postData); + $this->assertSessionMessages( + $this->equalTo([(string)__('You saved the customer.')]), + MessageInterface::TYPE_SUCCESS + ); + $this->assertRedirect($this->stringContains($this->baseControllerUrl . 'index/key/')); + $this->assertCustomerData( + $postData['customer'][CustomerData::EMAIL], + (int)$postData['customer'][CustomerData::WEBSITE_ID], + $expectedData + ); + } + + /** + * Create customer provider + * + * @return array + */ + public function createCustomerProvider(): array + { + $defaultCustomerData = $this->getDefaultCustomerData(); + $expectedCustomerData = $this->getExpectedCustomerData($defaultCustomerData); + return [ + "fill_all_fields" => [ + 'post_data' => $defaultCustomerData, + 'expected_data' => $expectedCustomerData + ], + 'only_require_fields' => [ + 'post_data' => array_replace_recursive( + $defaultCustomerData, + [ + 'customer' => [ + CustomerData::DISABLE_AUTO_GROUP_CHANGE => '0', + CustomerData::PREFIX => '', + CustomerData::MIDDLENAME => '', + CustomerData::SUFFIX => '', + CustomerData::DOB => '', + CustomerData::TAXVAT => '', + CustomerData::GENDER => '', + ], + ] + ), + 'expected_data' => array_replace_recursive( + $expectedCustomerData, + [ + 'customer' => [ + CustomerData::DISABLE_AUTO_GROUP_CHANGE => '0', + CustomerData::PREFIX => '', + CustomerData::MIDDLENAME => '', + CustomerData::SUFFIX => '', + CustomerData::DOB => '', + CustomerData::TAXVAT => '', + CustomerData::GENDER => '0', + ], + ] + ), + ], + ]; + } + + /** + * Create customer with exceptions + * + * @dataProvider createCustomerErrorsProvider + * @magentoDbIsolation enabled + * + * @param array $postData + * @param array $expectedData + * @param array $expectedMessage + * @return void + */ + public function testCreateCustomerErrors(array $postData, array $expectedData, array $expectedMessage): void + { + $this->dispatchCustomerSave($postData); + $this->assertSessionMessages( + $this->equalTo($expectedMessage), + MessageInterface::TYPE_ERROR + ); + $this->assertNotEmpty($this->session->getCustomerFormData()); + $this->assertArraySubset($expectedData, $this->session->getCustomerFormData()); + $this->assertRedirect($this->stringContains($this->baseControllerUrl . 'new/key/')); + } + + /** + * Create customer errors provider + * + * @return array + */ + public function createCustomerErrorsProvider(): array + { + $defaultCustomerData = $this->getDefaultCustomerData(); + return [ + 'without_some_require_fields' => [ + 'post_data' => array_replace_recursive( + $defaultCustomerData, + [ + 'customer' => [ + CustomerData::FIRSTNAME => '', + CustomerData::LASTNAME => '', + ], + ] + ), + 'expected_data' => array_replace_recursive( + $defaultCustomerData, + [ + 'customer' => [ + CustomerData::FIRSTNAME => '', + CustomerData::LASTNAME => '', + CustomerData::DOB => '2000-01-01', + ], + ] + ), + 'expected_message' => [ + (string)__('"%1" is a required value.', 'First Name'), + (string)__('"%1" is a required value.', 'Last Name'), + ], + ], + 'with_empty_post_data' => [ + 'post_data' => [], + 'expected_data' => [], + 'expected_message' => [ + (string)__('The customer email is missing. Enter and try again.'), + ], + ], + 'with_invalid_form_data' => [ + 'post_data' => [ + 'account' => [ + 'middlename' => 'test middlename', + 'group_id' => 1, + ], + ], + 'expected_data' => [ + 'account' => [ + 'middlename' => 'test middlename', + 'group_id' => 1, + ], + ], + 'expected_message' => [ + (string)__('The customer email is missing. Enter and try again.'), + ], + ] + ]; + } + + /** + * Update customer with subscription and redirect to edit page. + * + * @magentoDataFixture Magento/Store/_files/core_fixturestore.php + * @magentoDataFixture Magento/Customer/_files/customer.php + * @return void + */ + public function testUpdateCustomer(): void + { + /** @var CustomerData $customerData */ + $customerData = $this->customerRepository->getById(1); + $secondStore = $this->storeManager->getStore('fixturestore'); + $postData = $expectedData = [ + 'customer' => [ + CustomerData::FIRSTNAME => 'Jane', + CustomerData::MIDDLENAME => 'Mdl', + CustomerData::LASTNAME => 'Doe', + ], + 'subscription_status' => [$customerData->getWebsiteId() => '1'], + 'subscription_store' => [$customerData->getWebsiteId() => $secondStore->getId()], + ]; + $postData['customer']['entity_id'] = $customerData->getId(); + $params = ['back' => true]; + + $this->dispatchCustomerSave($postData, $params); + $this->assertSessionMessages( + $this->equalTo([(string)__('You saved the customer.')]), + MessageInterface::TYPE_SUCCESS + ); + $this->assertRedirect($this->stringContains( + $this->baseControllerUrl . 'edit/id/' . $customerData->getId() + )); + $this->assertCustomerData($customerData->getEmail(), (int)$customerData->getWebsiteId(), $expectedData); + $this->assertCustomerSubscription( + (int)$customerData->getId(), + (int)$customerData->getWebsiteId(), + Subscriber::STATUS_SUBSCRIBED, + (int)$secondStore->getId() + ); + } + + /** + * @magentoDataFixture Magento/Newsletter/_files/subscribers.php + * @return void + */ + public function testExistingCustomerUnsubscribeNewsletter(): void + { + /** @var CustomerData $customerData */ + $customerData = $this->customerRepository->getById(1); + /** @var Store $defaultStore */ + $defaultStore = $this->storeManager->getWebsite()->getDefaultStore(); + $postData = [ + 'customer' => [ + 'entity_id' => $customerData->getId(), + CustomerData::EMAIL => 'customer@example.com', + CustomerData::FIRSTNAME => 'test firstname', + CustomerData::LASTNAME => 'test lastname', + 'sendemail_store_id' => '1' + ], + 'subscription_status' => [$customerData->getWebsiteId() => '0'], + 'subscription_store' => [$customerData->getWebsiteId() => $defaultStore->getId()], + ]; + $this->dispatchCustomerSave($postData); + $this->assertSessionMessages( + $this->equalTo([(string)__('You saved the customer.')]), + MessageInterface::TYPE_SUCCESS + ); + $this->assertRedirect($this->stringContains($this->baseControllerUrl . 'index/key/')); + $this->assertCustomerSubscription( + (int)$customerData->getId(), + (int)$customerData->getWebsiteId(), + Subscriber::STATUS_UNSUBSCRIBED, + (int)$defaultStore->getId() + ); + } + + /** + * Ensure that an email is sent during save action + * + * @magentoConfigFixture current_store customer/account_information/change_email_template change_email_template + * @magentoConfigFixture current_store customer/password/forgot_email_identity support + * @magentoDataFixture Magento/Customer/_files/customer_sample.php + * @return void + */ + public function testExistingCustomerChangeEmail(): void + { + $customerId = 1; + $newEmail = 'newcustomer@example.com'; + $transportBuilderMock = $this->prepareEmailMock( + 2, + 'change_email_template', + [ + 'name' => 'CustomerSupport', + 'email' => 'support@example.com', + ], + $customerId, + $newEmail + ); + $this->addEmailMockToClass($transportBuilderMock, EmailNotification::class); + $postData = [ + 'customer' => [ + 'entity_id' => $customerId, + CustomerData::WEBSITE_ID => '1', + CustomerData::GROUP_ID => '1', + CustomerData::FIRSTNAME => 'test firstname', + CustomerData::MIDDLENAME => 'test middlename', + CustomerData::LASTNAME => 'test lastname', + CustomerData::EMAIL => $newEmail, + 'new_password' => 'auto', + 'sendemail_store_id' => '1', + 'sendemail' => '1', + CustomerData::CREATED_AT => '2000-01-01 00:00:00', + CustomerData::DEFAULT_SHIPPING => '_item1', + CustomerData::DEFAULT_BILLING => '1' + ] + ]; + $this->dispatchCustomerSave($postData); + + /** + * Check that no errors were generated and set to session + */ + $this->assertSessionMessages($this->isEmpty(), MessageInterface::TYPE_ERROR); + $this->assertRedirect($this->stringContains($this->baseControllerUrl . 'index/key/')); + } + + /** + * @magentoDataFixture Magento/Customer/_files/customer_sample.php + * @return void + */ + public function testCreateSameEmailFormatDateError(): void + { + $postData = [ + 'customer' => [ + CustomerData::WEBSITE_ID => '1', + CustomerData::FIRSTNAME => 'test firstname', + CustomerData::MIDDLENAME => 'test middlename', + CustomerData::LASTNAME => 'test lastname', + CustomerData::EMAIL => 'customer@example.com', + CustomerData::DOB => '12/3/1996', + ], + ]; + $postFormatted = array_replace_recursive( + $postData, + [ + 'customer' => [ + CustomerData::DOB => '1996-12-03', + ], + ] + ); + $this->dispatchCustomerSave($postData); + $this->assertSessionMessages( + $this->equalTo([ + (string)__('A customer with the same email address already exists in an associated website.'), + ]), + MessageInterface::TYPE_ERROR + ); + $this->assertArraySubset( + $postFormatted, + $this->session->getCustomerFormData(), + true, + 'Customer form data should be formatted' + ); + $this->assertRedirect($this->stringContains($this->baseControllerUrl . 'new/key/')); + } + + /** + * Default values for customer creation + * + * @return array + */ + private function getDefaultCustomerData(): array + { + return [ + 'customer' => [ + CustomerData::WEBSITE_ID => '1', + CustomerData::GROUP_ID => '1', + CustomerData::DISABLE_AUTO_GROUP_CHANGE => '1', + CustomerData::PREFIX => 'Mr.', + CustomerData::FIRSTNAME => 'Jane', + CustomerData::MIDDLENAME => 'Mdl', + CustomerData::LASTNAME => 'Doe', + CustomerData::SUFFIX => 'Esq.', + CustomerData::EMAIL => 'janedoe' . uniqid() . '@example.com', + CustomerData::DOB => '01/01/2000', + CustomerData::TAXVAT => '121212', + CustomerData::GENDER => 'Male', + 'sendemail_store_id' => '1', + ] + ]; + } + + /** + * Expected values for customer creation + * + * @param array $defaultCustomerData + * @return array + */ + private function getExpectedCustomerData(array $defaultCustomerData): array + { + unset($defaultCustomerData['customer']['sendemail_store_id']); + return array_replace_recursive( + $defaultCustomerData, + [ + 'customer' => [ + CustomerData::DOB => '2000-01-01', + CustomerData::GENDER => '0', + CustomerData::STORE_ID => 1, + CustomerData::CREATED_IN => 'Default Store View', + ], + ] + ); + } + + /** + * Create or update customer using backend/customer/index/save action. + * + * @param array $postData + * @param array $params + * @return void + */ + private function dispatchCustomerSave(array $postData, array $params = []): void + { + $this->getRequest()->setMethod(HttpRequest::METHOD_POST); + $this->getRequest()->setPostValue($postData); + if (!empty($params)) { + $this->getRequest()->setParams($params); + } + $this->dispatch($this->baseControllerUrl . 'save'); + } + + /** + * Check that customer parameters match expected values. + * + * @param string $customerEmail + * @param int $customerWebsiteId + * @param array $expectedData + * @return void + */ + private function assertCustomerData( + string $customerEmail, + int $customerWebsiteId, + array $expectedData + ): void { + /** @var CustomerData $customerData */ + $customerData = $this->customerRepository->get($customerEmail, $customerWebsiteId); + $actualCustomerArray = $customerData->__toArray(); + foreach ($expectedData['customer'] as $key => $expectedValue) { + $this->assertEquals( + $expectedValue, + $actualCustomerArray[$key], + "Invalid expected value for $key field." + ); + } + } + + /** + * Check that customer subscription status match expected status. + * + * @param int $customerId + * @param int $websiteId + * @param int $expectedStatus + * @param int $expectedStoreId + * @return void + */ + private function assertCustomerSubscription( + int $customerId, + int $websiteId, + int $expectedStatus, + int $expectedStoreId + ): void { + $subscriber = $this->subscriberFactory->create(); + $subscriber->loadByCustomer($customerId, $websiteId); + $this->assertNotEmpty($subscriber->getId()); + $this->assertEquals($expectedStatus, $subscriber->getStatus()); + $this->assertEquals($expectedStoreId, $subscriber->getStoreId()); + } + + /** + * Prepare email mock to test emails. + * + * @magentoDataFixture Magento/Customer/_files/customer.php + * @param int $occurrenceNumber + * @param string $templateId + * @param array $sender + * @param int $customerId + * @param string|null $newEmail + * @return \PHPUnit_Framework_MockObject_MockObject + */ + private function prepareEmailMock( + int $occurrenceNumber, + string $templateId, + array $sender, + int $customerId, + $newEmail = null + ) : \PHPUnit_Framework_MockObject_MockObject { + $area = Area::AREA_FRONTEND; + $customer = $this->customerRepository->getById($customerId); + $storeId = $customer->getStoreId(); + $name = $this->customerViewHelper->getCustomerName($customer); + + $transportMock = $this->getMockBuilder(TransportInterface::class) + ->setMethods(['sendMessage']) + ->getMockForAbstractClass(); + $transportMock->expects($this->exactly($occurrenceNumber)) + ->method('sendMessage'); + $transportBuilderMock = $this->getMockBuilder(TransportBuilder::class) + ->disableOriginalConstructor() + ->setMethods( + [ + 'addTo', + 'setFrom', + 'setTemplateIdentifier', + 'setTemplateVars', + 'setTemplateOptions', + 'getTransport', + ] + ) + ->getMock(); + $transportBuilderMock->method('setTemplateIdentifier') + ->with($templateId) + ->willReturnSelf(); + $transportBuilderMock->method('setTemplateOptions') + ->with(['area' => $area, 'store' => $storeId]) + ->willReturnSelf(); + $transportBuilderMock->method('setTemplateVars') + ->willReturnSelf(); + $transportBuilderMock->method('setFrom') + ->with($sender) + ->willReturnSelf(); + $transportBuilderMock->method('addTo') + ->with($this->logicalOr($customer->getEmail(), $newEmail), $name) + ->willReturnSelf(); + $transportBuilderMock->expects($this->exactly($occurrenceNumber)) + ->method('getTransport') + ->willReturn($transportMock); + + return $transportBuilderMock; + } + + /** + * Add email mock to class + * + * @param \PHPUnit_Framework_MockObject_MockObject $transportBuilderMock + * @param string $className + * @return void + */ + private function addEmailMockToClass( + \PHPUnit_Framework_MockObject_MockObject $transportBuilderMock, + $className + ): void { + $mocked = $this->_objectManager->create( + $className, + ['transportBuilder' => $transportBuilderMock] + ); + $this->_objectManager->addSharedInstance( + $mocked, + $className + ); + } +} diff --git a/dev/tests/integration/testsuite/Magento/Customer/Controller/Adminhtml/Index/ValidateTest.php b/dev/tests/integration/testsuite/Magento/Customer/Controller/Adminhtml/Index/ValidateTest.php new file mode 100644 index 0000000000000..d42132823d3da --- /dev/null +++ b/dev/tests/integration/testsuite/Magento/Customer/Controller/Adminhtml/Index/ValidateTest.php @@ -0,0 +1,123 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +declare(strict_types=1); + +namespace Magento\Customer\Controller\Adminhtml\Index; + +use Magento\Framework\App\Request\Http as HttpRequest; +use Magento\Framework\Message\MessageInterface; +use Magento\Framework\Serialize\SerializerInterface; +use Magento\TestFramework\TestCase\AbstractBackendController; + +/** + * Tests for validation customer via backend/customer/index/validate controller. + * + * @magentoAppArea adminhtml + */ +class ValidateTest extends AbstractBackendController +{ + /** @var SerializerInterface */ + private $jsonSerializer; + + /** + * @inheritdoc + */ + protected function setUp() + { + parent::setUp(); + + $this->jsonSerializer = $this->_objectManager->get(SerializerInterface::class); + } + + /** + * Validate customer with exception + * + * @magentoDbIsolation enabled + * @return void + */ + public function testValidateCustomerErrors(): void + { + $postData = [ + 'customer' => [], + ]; + $attributeEmptyMessage = 'The "%1" attribute value is empty. Set the attribute and try again.'; + $expectedErrors = [ + 'error' => true, + 'messages' => [ + (string)__($attributeEmptyMessage, 'First Name'), + (string)__($attributeEmptyMessage, 'Last Name'), + (string)__($attributeEmptyMessage, 'Email'), + ], + ]; + + $this->dispatchCustomerValidate($postData); + $errors = $this->jsonSerializer->unserialize($this->getResponse()->getBody()); + $this->assertEquals($expectedErrors, $errors); + } + + /** + * @magentoDataFixture Magento/Customer/_files/customer.php + * @magentoDataFixture Magento/Customer/_files/customer_address.php + * @return void + */ + public function testValidateCustomerWithAddressSuccess(): void + { + $customerData = [ + 'customer' => [ + 'entity_id' => '1', + 'middlename' => 'new middlename', + 'group_id' => 1, + 'website_id' => 1, + 'firstname' => 'new firstname', + 'lastname' => 'new lastname', + 'email' => 'example@domain.com', + 'default_shipping' => '_item1', + 'new_password' => 'auto', + 'sendemail_store_id' => '1', + 'sendemail' => '1', + ], + 'address' => [ + '_item1' => [ + 'firstname' => 'update firstname', + 'lastname' => 'update lastname', + 'street' => ['update street'], + 'city' => 'update city', + 'country_id' => 'US', + 'region_id' => 10, + 'postcode' => '01001', + 'telephone' => '+7000000001', + ], + '_template_' => [ + 'firstname' => '', + 'lastname' => '', + 'street' => [], + 'city' => '', + 'country_id' => 'US', + 'postcode' => '', + 'telephone' => '', + ], + ], + ]; + $this->dispatchCustomerValidate($customerData); + + $this->assertSessionMessages($this->isEmpty(), MessageInterface::TYPE_ERROR); + $errors = $this->jsonSerializer->unserialize($this->getResponse()->getBody()); + $this->assertEquals(['error' => 0], $errors); + } + + /** + * Validate customer using backend/customer/index/validate action. + * + * @param array $postData + * @return void + */ + private function dispatchCustomerValidate(array $postData): void + { + $this->getRequest()->setMethod(HttpRequest::METHOD_POST); + $this->getRequest()->setPostValue($postData); + $this->dispatch('backend/customer/index/validate'); + } +} diff --git a/dev/tests/integration/testsuite/Magento/Customer/Controller/Adminhtml/IndexTest.php b/dev/tests/integration/testsuite/Magento/Customer/Controller/Adminhtml/IndexTest.php index d76c520ade3b1..e2ba275a5a438 100644 --- a/dev/tests/integration/testsuite/Magento/Customer/Controller/Adminhtml/IndexTest.php +++ b/dev/tests/integration/testsuite/Magento/Customer/Controller/Adminhtml/IndexTest.php @@ -6,44 +6,32 @@ namespace Magento\Customer\Controller\Adminhtml; -use Magento\Customer\Api\AccountManagementInterface; -use Magento\Customer\Api\AddressRepositoryInterface; +use Magento\Backend\Model\Session; +use Magento\Customer\Api\CustomerNameGenerationInterface; use Magento\Customer\Api\CustomerRepositoryInterface; -use Magento\Customer\Controller\RegistryConstants; use Magento\Customer\Model\EmailNotification; use Magento\TestFramework\Helper\Bootstrap; use Magento\Framework\App\Request\Http as HttpRequest; +use Magento\TestFramework\TestCase\AbstractBackendController; /** * @magentoAppArea adminhtml * @SuppressWarnings(PHPMD.CouplingBetweenObjects) */ -class IndexTest extends \Magento\TestFramework\TestCase\AbstractBackendController +class IndexTest extends AbstractBackendController { /** * Base controller URL * * @var string */ - protected $_baseControllerUrl; + private $baseControllerUrl = 'backend/customer/index/'; /** @var CustomerRepositoryInterface */ - protected $customerRepository; + private $customerRepository; - /** @var AddressRepositoryInterface */ - protected $addressRepository; - - /** @var AccountManagementInterface */ - protected $accountManagement; - - /** @var \Magento\Framework\Data\Form\FormKey */ - protected $formKey; - - /**@var \Magento\Customer\Helper\View */ - protected $customerViewHelper; - - /** @var \Magento\TestFramework\ObjectManager */ - protected $objectManager; + /** @var CustomerNameGenerationInterface */ + private $customerViewHelper; /** * @inheritDoc @@ -51,24 +39,8 @@ class IndexTest extends \Magento\TestFramework\TestCase\AbstractBackendControlle protected function setUp() { parent::setUp(); - $this->_baseControllerUrl = 'http://localhost/index.php/backend/customer/index/'; - $this->customerRepository = Bootstrap::getObjectManager()->get( - \Magento\Customer\Api\CustomerRepositoryInterface::class - ); - $this->addressRepository = Bootstrap::getObjectManager()->get( - \Magento\Customer\Api\AddressRepositoryInterface::class - ); - $this->accountManagement = Bootstrap::getObjectManager()->get( - \Magento\Customer\Api\AccountManagementInterface::class - ); - $this->formKey = Bootstrap::getObjectManager()->get( - \Magento\Framework\Data\Form\FormKey::class - ); - - $this->objectManager = Bootstrap::getObjectManager(); - $this->customerViewHelper = $this->objectManager->get( - \Magento\Customer\Helper\View::class - ); + $this->customerRepository = $this->_objectManager->get(CustomerRepositoryInterface::class); + $this->customerViewHelper = $this->_objectManager->get(CustomerNameGenerationInterface::class); } /** @@ -79,144 +51,12 @@ protected function tearDown() /** * Unset customer data */ - Bootstrap::getObjectManager()->get(\Magento\Backend\Model\Session::class)->setCustomerData(null); + $this->_objectManager->get(Session::class)->setCustomerData(null); /** * Unset messages */ - Bootstrap::getObjectManager()->get(\Magento\Backend\Model\Session::class)->getMessages(true); - } - - /** - * @magentoDbIsolation enabled - */ - public function testSaveActionWithEmptyPostData() - { - $this->getRequest()->setPostValue([])->setMethod(HttpRequest::METHOD_POST); - $this->dispatch('backend/customer/index/save'); - $this->assertRedirect($this->stringStartsWith($this->_baseControllerUrl)); - } - - /** - * @magentoDbIsolation enabled - */ - public function testSaveActionWithInvalidFormData() - { - $post = ['account' => ['middlename' => 'test middlename', 'group_id' => 1]]; - $this->getRequest()->setPostValue($post)->setMethod(HttpRequest::METHOD_POST); - $this->dispatch('backend/customer/index/save'); - /** - * Check that errors was generated and set to session - */ - $this->assertSessionMessages( - $this->logicalNot($this->isEmpty()), - \Magento\Framework\Message\MessageInterface::TYPE_ERROR - ); - /** @var \Magento\Backend\Model\Session $session */ - $session = $this->objectManager->get(\Magento\Backend\Model\Session::class); - /** - * Check that customer data were set to session - */ - $this->assertNotEmpty($session->getCustomerFormData()); - $this->assertArraySubset($post, $session->getCustomerFormData()); - $this->assertRedirect($this->stringStartsWith($this->_baseControllerUrl . 'new')); - } - - /** - * @magentoDataFixture Magento/Newsletter/_files/subscribers.php - */ - public function testSaveActionExistingCustomerUnsubscribeNewsletter() - { - $customerId = 1; - $websiteId = 1; - - /** @var \Magento\Newsletter\Model\Subscriber $subscriber */ - $subscriber = $this->objectManager->get(\Magento\Newsletter\Model\SubscriberFactory::class)->create(); - $this->assertEmpty($subscriber->getId()); - $subscriber->loadByCustomerId($customerId); - $this->assertNotEmpty($subscriber->getId()); - $this->assertEquals(1, $subscriber->getStatus()); - - $post = [ - 'customer' => [ - 'entity_id' => $customerId, - 'email' => 'customer@example.com', - 'firstname' => 'test firstname', - 'lastname' => 'test lastname', - 'sendemail_store_id' => 1 - ], - 'subscription_status' => [$websiteId => '0'] - ]; - $this->getRequest()->setPostValue($post)->setMethod(HttpRequest::METHOD_POST); - $this->getRequest()->setParam('id', 1); - $this->dispatch('backend/customer/index/save'); - - /** @var \Magento\Newsletter\Model\Subscriber $subscriber */ - $subscriber = $this->objectManager->get(\Magento\Newsletter\Model\SubscriberFactory::class)->create(); - $this->assertEmpty($subscriber->getId()); - $subscriber->loadByCustomerId($customerId); - $this->assertNotEmpty($subscriber->getId()); - $this->assertEquals(3, $subscriber->getStatus()); - - /** - * Check that success message is set - */ - $this->assertSessionMessages( - $this->equalTo(['You saved the customer.']), - \Magento\Framework\Message\MessageInterface::TYPE_SUCCESS - ); - - $this->assertRedirect($this->stringStartsWith($this->_baseControllerUrl . 'index/key/')); - } - - /** - * Ensure that an email is sent during save action - * - * @magentoConfigFixture current_store customer/account_information/change_email_template change_email_template - * @magentoConfigFixture current_store customer/password/forgot_email_identity support - * @magentoDataFixture Magento/Customer/_files/customer_sample.php - */ - public function testSaveActionExistingCustomerChangeEmail() - { - $customerId = 1; - $newEmail = 'newcustomer@example.com'; - $transportBuilderMock = $this->prepareEmailMock( - 2, - 'change_email_template', - [ - 'name' => 'CustomerSupport', - 'email' => 'support@example.com', - ], - $customerId, - $newEmail - ); - $this->addEmailMockToClass($transportBuilderMock, EmailNotification::class); - $post = [ - 'customer' => [ - 'entity_id' => $customerId, - 'middlename' => 'test middlename', - 'group_id' => 1, - 'website_id' => 1, - 'firstname' => 'test firstname', - 'lastname' => 'test lastname', - 'email' => $newEmail, - 'new_password' => 'auto', - 'sendemail_store_id' => '1', - 'sendemail' => '1', - 'created_at' => '2000-01-01 00:00:00', - 'default_shipping' => '_item1', - 'default_billing' => 1, - ] - ]; - $this->getRequest()->setPostValue($post)->setMethod(HttpRequest::METHOD_POST); - $this->getRequest()->setParam('id', 1); - $this->dispatch('backend/customer/index/save'); - - /** - * Check that no errors were generated and set to session - */ - $this->assertSessionMessages($this->isEmpty(), \Magento\Framework\Message\MessageInterface::TYPE_ERROR); - $this->assertRedirect($this->stringStartsWith($this->_baseControllerUrl . 'index/key/')); + $this->_objectManager->get(Session::class)->getMessages(true); } /** @@ -265,83 +105,6 @@ public function testInlineEditChangeEmail() $this->assertSessionMessages($this->isEmpty(), \Magento\Framework\Message\MessageInterface::TYPE_ERROR); } - /** - * @magentoDataFixture Magento/Customer/_files/customer_sample.php - */ - public function testSaveActionCoreException() - { - $post = [ - 'customer' => [ - 'middlename' => 'test middlename', - 'group_id' => 1, - 'website_id' => 1, - 'firstname' => 'test firstname', - 'lastname' => 'test lastname', - 'email' => 'customer@example.com', - 'password' => 'password', - ], - ]; - $this->getRequest()->setPostValue($post)->setMethod(HttpRequest::METHOD_POST); - $this->dispatch('backend/customer/index/save'); - /* - * Check that error message is set - */ - $this->assertSessionMessages( - $this->equalTo(['A customer with the same email address already exists in an associated website.']), - \Magento\Framework\Message\MessageInterface::TYPE_ERROR - ); - $this->assertArraySubset( - $post, - Bootstrap::getObjectManager()->get(\Magento\Backend\Model\Session::class)->getCustomerFormData() - ); - $this->assertRedirect($this->stringStartsWith($this->_baseControllerUrl . 'new/key/')); - } - - /** - * @magentoDataFixture Magento/Customer/_files/customer_sample.php - */ - public function testSaveActionCoreExceptionFormatFormData() - { - $post = [ - 'customer' => [ - 'middlename' => 'test middlename', - 'website_id' => 1, - 'firstname' => 'test firstname', - 'lastname' => 'test lastname', - 'email' => 'customer@example.com', - 'dob' => '12/3/1996', - ], - ]; - $postCustomerFormatted = [ - 'middlename' => 'test middlename', - 'website_id' => 1, - 'firstname' => 'test firstname', - 'lastname' => 'test lastname', - 'email' => 'customer@example.com', - 'dob' => '1996-12-03', - ]; - - $this->getRequest()->setPostValue($post)->setMethod(HttpRequest::METHOD_POST); - $this->dispatch('backend/customer/index/save'); - /* - * Check that error message is set - */ - $this->assertSessionMessages( - $this->equalTo(['A customer with the same email address already exists in an associated website.']), - \Magento\Framework\Message\MessageInterface::TYPE_ERROR - ); - - $customerFormData = Bootstrap::getObjectManager() - ->get(\Magento\Backend\Model\Session::class) - ->getCustomerFormData(); - $this->assertEquals( - $postCustomerFormatted, - $customerFormData['customer'], - 'Customer form data should be formatted' - ); - $this->assertRedirect($this->stringStartsWith($this->_baseControllerUrl . 'new/key/')); - } - /** * @magentoDataFixture Magento/Customer/_files/customer_sample.php */ @@ -390,42 +153,6 @@ public function te1stNewActionWithCustomerData() $this->testNewAction(); } - /** - * @magentoDataFixture Magento/Customer/_files/customer_sample.php - */ - public function testDeleteAction() - { - $this->getRequest()->setParam('id', 1); - $this->getRequest()->setParam('form_key', $this->formKey->getFormKey()); - - $this->getRequest()->setMethod(\Zend\Http\Request::METHOD_POST); - - $this->dispatch('backend/customer/index/delete'); - $this->assertRedirect($this->stringContains('customer/index')); - $this->assertSessionMessages( - $this->equalTo(['You deleted the customer.']), - \Magento\Framework\Message\MessageInterface::TYPE_SUCCESS - ); - } - - /** - * @magentoDataFixture Magento/Customer/_files/customer_sample.php - */ - public function testNotExistingCustomerDeleteAction() - { - $this->getRequest()->setParam('id', 2); - $this->getRequest()->setParam('form_key', $this->formKey->getFormKey()); - - $this->getRequest()->setMethod(\Zend\Http\Request::METHOD_POST); - - $this->dispatch('backend/customer/index/delete'); - $this->assertRedirect($this->stringContains('customer/index')); - $this->assertSessionMessages( - $this->equalTo(['No such entity with customerId = 2']), - \Magento\Framework\Message\MessageInterface::TYPE_ERROR - ); - } - /** * @magentoDataFixture Magento/Customer/_files/customer_sample.php */ @@ -434,75 +161,7 @@ public function testCartAction() $this->getRequest()->setParam('id', 1)->setParam('website_id', 1)->setPostValue('delete', 1); $this->dispatch('backend/customer/index/cart'); $body = $this->getResponse()->getBody(); - $this->assertContains('<div id="customer_cart_grid1"', $body); - } - - /** - * @magentoDataFixture Magento/Customer/_files/customer_sample.php - */ - public function testProductReviewsAction() - { - $this->getRequest()->setParam('id', 1); - $this->dispatch('backend/customer/index/productReviews'); - $body = $this->getResponse()->getBody(); - $this->assertContains('<div id="reviewGrid"', $body); - } - - /** - * @magentoDataFixture Magento/Customer/_files/customer.php - * @magentoDataFixture Magento/Customer/_files/customer_address.php - */ - public function testValidateCustomerWithAddressSuccess() - { - $customerData = [ - 'customer' => [ - 'entity_id' => '1', - 'middlename' => 'new middlename', - 'group_id' => 1, - 'website_id' => 1, - 'firstname' => 'new firstname', - 'lastname' => 'new lastname', - 'email' => 'example@domain.com', - 'default_shipping' => '_item1', - 'new_password' => 'auto', - 'sendemail_store_id' => '1', - 'sendemail' => '1', - ], - 'address' => [ - '_item1' => [ - 'firstname' => 'update firstname', - 'lastname' => 'update lastname', - 'street' => ['update street'], - 'city' => 'update city', - 'country_id' => 'US', - 'region_id' => 10, - 'postcode' => '01001', - 'telephone' => '+7000000001', - ], - '_template_' => [ - 'firstname' => '', - 'lastname' => '', - 'street' => [], - 'city' => '', - 'country_id' => 'US', - 'postcode' => '', - 'telephone' => '', - ], - ], - ]; - /** - * set customer data - */ - $this->getRequest()->setParams($customerData)->setMethod(HttpRequest::METHOD_POST); - $this->dispatch('backend/customer/index/validate'); - $body = $this->getResponse()->getBody(); - - /** - * Check that no errors were generated and set to session - */ - $this->assertSessionMessages($this->isEmpty(), \Magento\Framework\Message\MessageInterface::TYPE_ERROR); - - $this->assertEquals('{"error":0}', $body); + $this->assertContains('<div id="customer_cart_grid"', $body); } /** @@ -513,7 +172,7 @@ public function testResetPasswordActionNoCustomerId() // No customer ID in post, will just get redirected to base $this->getRequest()->setMethod(HttpRequest::METHOD_GET); $this->dispatch('backend/customer/index/resetPassword'); - $this->assertRedirect($this->stringStartsWith($this->_baseControllerUrl)); + $this->assertRedirect($this->stringContains($this->baseControllerUrl)); } /** @@ -525,7 +184,7 @@ public function testResetPasswordActionBadCustomerId() $this->getRequest()->setMethod(HttpRequest::METHOD_GET); $this->getRequest()->setPostValue(['customer_id' => '789']); $this->dispatch('backend/customer/index/resetPassword'); - $this->assertRedirect($this->stringStartsWith($this->_baseControllerUrl)); + $this->assertRedirect($this->stringContains($this->baseControllerUrl)); } /** @@ -540,7 +199,7 @@ public function testResetPasswordActionSuccess() $this->equalTo(['The customer will receive an email with a link to reset password.']), \Magento\Framework\Message\MessageInterface::TYPE_SUCCESS ); - $this->assertRedirect($this->stringStartsWith($this->_baseControllerUrl . 'edit')); + $this->assertRedirect($this->stringContains($this->baseControllerUrl . 'edit')); } /** diff --git a/dev/tests/integration/testsuite/Magento/Customer/Controller/AuthenticationTest.php b/dev/tests/integration/testsuite/Magento/Customer/Controller/AuthenticationTest.php new file mode 100644 index 0000000000000..822b3ab8f35d1 --- /dev/null +++ b/dev/tests/integration/testsuite/Magento/Customer/Controller/AuthenticationTest.php @@ -0,0 +1,70 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +declare(strict_types=1); + +namespace Magento\Customer\Controller; + +use Magento\Customer\Controller\Plugin\Account as AccountPlugin; +use Magento\TestFramework\TestCase\AbstractController; + +/** + * Set of Tests to verify that Authentication methods work properly + */ +class AuthenticationTest extends AbstractController +{ + /** + * Make sure that customized AccountPlugin was reverted. + */ + protected function tearDown() + { + $this->resetAllowedActions(); + parent::tearDown(); + } + + /** + * After changes to `di.xml` and overriding list of allowed actions, unallowed ones should cause redirect. + */ + public function testExpectRedirectResponseWhenDispatchNotAllowedAction() + { + $this->overrideAllowedActions(['notExistingRoute']); + + $this->dispatch('customer/account/create'); + $this->assertRedirect($this->stringContains('customer/account/login')); + } + + /** + * Allowed actions should be rendered normally + */ + public function testExpectPageResponseWhenAllowedAction() + { + $this->overrideAllowedActions(['create']); + + $this->dispatch('customer/account/create'); + $this->assertEquals(200, $this->getResponse()->getStatusCode()); + } + + /** + * Overrides list of `allowedActions` for Authorization Plugin + * + * @param string[] $allowedActions + * @see \Magento\Customer\Controller\Plugin\Account + */ + private function overrideAllowedActions(array $allowedActions): void + { + $allowedActions = array_combine($allowedActions, $allowedActions); + $pluginFake = $this->_objectManager->create(AccountPlugin::class, ['allowedActions' => $allowedActions]); + $this->_objectManager->addSharedInstance($pluginFake, AccountPlugin::class); + } + + /** + * Removes all the customizations applied to `allowedActions` + * @see \Magento\Customer\Controller\Plugin\Account + */ + private function resetAllowedActions() + { + $this->_objectManager->removeSharedInstance(AccountPlugin::class); + } +} diff --git a/dev/tests/integration/testsuite/Magento/Customer/Controller/CreatePasswordTest.php b/dev/tests/integration/testsuite/Magento/Customer/Controller/CreatePasswordTest.php new file mode 100644 index 0000000000000..bbaf55494294e --- /dev/null +++ b/dev/tests/integration/testsuite/Magento/Customer/Controller/CreatePasswordTest.php @@ -0,0 +1,97 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +declare(strict_types=1); + +namespace Magento\Customer\Controller; + +use Magento\Customer\Model\CustomerRegistry; +use Magento\Customer\Model\ResourceModel\Customer as CustomerResource; +use Magento\Customer\Model\Session; +use Magento\Framework\Math\Random; +use Magento\Framework\ObjectManagerInterface; +use Magento\Framework\View\LayoutInterface; +use Magento\Store\Api\WebsiteRepositoryInterface; +use Magento\TestFramework\Helper\Bootstrap; +use Magento\TestFramework\TestCase\AbstractController; + +/** + * Class checks password forgot scenarios + * + * @magentoDbIsolation enabled + */ +class CreatePasswordTest extends AbstractController +{ + /** @var ObjectManagerInterface */ + private $objectManager; + + /** @var Session */ + private $session; + + /** @var LayoutInterface */ + private $layout; + + /** @var Random */ + private $random; + + /** @var CustomerResource */ + private $customerResource; + + /** @var CustomerRegistry */ + private $customerRegistry; + + /** @var WebsiteRepositoryInterface */ + private $websiteRepository; + + /** @var int */ + private $customerId; + + /** + * @inheritdoc + */ + protected function setUp() + { + parent::setUp(); + + $this->objectManager = Bootstrap::getObjectManager(); + $this->session = $this->objectManager->get(Session::class); + $this->layout = $this->objectManager->get(LayoutInterface::class); + $this->random = $this->objectManager->get(Random::class); + $this->customerResource = $this->objectManager->get(CustomerResource::class); + $this->customerRegistry = $this->objectManager->get(CustomerRegistry::class); + $this->websiteRepository = $this->objectManager->get(WebsiteRepositoryInterface::class); + } + + /** + * @inheritdoc + */ + protected function tearDown() + { + $this->customerRegistry->remove($this->customerId); + + parent::tearDown(); + } + + /** + * @magentoDataFixture Magento/Customer/_files/customer_with_website.php + * + * @return void + */ + public function testCreatePassword(): void + { + $defaultWebsite = $this->websiteRepository->get('base')->getId(); + $customer = $this->customerRegistry->retrieveByEmail('john.doe@magento.com', $defaultWebsite); + $this->customerId = $customer->getId(); + $token = $this->random->getUniqueHash(); + $customer->changeResetPasswordLinkToken($token); + $customer->setData('confirmation', 'confirmation'); + $this->customerResource->save($customer); + $this->session->setRpToken($token); + $this->session->setRpCustomerId($customer->getId()); + $this->dispatch('customer/account/createPassword'); + $block = $this->layout->getBlock('resetPassword'); + $this->assertEquals($token, $block->getResetPasswordLinkToken()); + } +} diff --git a/dev/tests/integration/testsuite/Magento/Customer/Controller/ForgotPasswordPostTest.php b/dev/tests/integration/testsuite/Magento/Customer/Controller/ForgotPasswordPostTest.php new file mode 100644 index 0000000000000..8bfe3b5524487 --- /dev/null +++ b/dev/tests/integration/testsuite/Magento/Customer/Controller/ForgotPasswordPostTest.php @@ -0,0 +1,137 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +declare(strict_types=1); + +namespace Magento\Customer\Controller; + +use Magento\Framework\App\Request\Http as HttpRequest; +use Magento\Framework\Message\MessageInterface; +use Magento\Framework\ObjectManagerInterface; +use Magento\TestFramework\Helper\Bootstrap; +use Magento\TestFramework\Mail\Template\TransportBuilderMock; +use Magento\TestFramework\TestCase\AbstractController; + +/** + * Class checks password forgot scenarios + * + * @see \Magento\Customer\Controller\Account\ForgotPasswordPost + * @magentoDbIsolation enabled + */ +class ForgotPasswordPostTest extends AbstractController +{ + /** @var ObjectManagerInterface */ + private $objectManager; + + /** @var TransportBuilderMock */ + private $transportBuilderMock; + + /** + * @inheritdoc + */ + protected function setUp() + { + parent::setUp(); + + $this->objectManager = Bootstrap::getObjectManager(); + $this->transportBuilderMock = $this->objectManager->get(TransportBuilderMock::class); + } + + /** + * @magentoConfigFixture current_store customer/captcha/enable 0 + * + * @return void + */ + public function testWithoutEmail(): void + { + $this->getRequest()->setMethod(HttpRequest::METHOD_POST); + $this->getRequest()->setPostValue(['email' => '']); + $this->dispatch('customer/account/forgotPasswordPost'); + $this->assertSessionMessages( + $this->equalTo([(string)__('Please enter your email.')]), + MessageInterface::TYPE_ERROR + ); + $this->assertRedirect($this->stringContains('customer/account/forgotpassword')); + } + + /** + * Test that forgot password email message displays special characters correctly. + * + * @magentoConfigFixture current_store customer/password/limit_password_reset_requests_method 0 + * @codingStandardsIgnoreStart + * @magentoConfigFixture current_store customer/password/forgot_email_template customer_password_forgot_email_template + * @codingStandardsIgnoreEnd + * @magentoConfigFixture current_store customer/password/forgot_email_identity support + * @magentoConfigFixture current_store general/store_information/name Test special' characters + * @magentoConfigFixture current_store customer/captcha/enable 0 + * @magentoDataFixture Magento/Customer/_files/customer.php + * + * @return void + */ + public function testForgotPasswordEmailMessageWithSpecialCharacters(): void + { + $email = 'customer@example.com'; + $this->getRequest()->setPostValue(['email' => $email]); + $this->getRequest()->setMethod(HttpRequest::METHOD_POST); + $this->dispatch('customer/account/forgotPasswordPost'); + $this->assertRedirect($this->stringContains('customer/account/')); + $this->assertSuccessSessionMessage($email); + $subject = $this->transportBuilderMock->getSentMessage()->getSubject(); + $this->assertContains('Test special\' characters', $subject); + } + + /** + * @magentoConfigFixture current_store customer/password/limit_password_reset_requests_method 0 + * @codingStandardsIgnoreStart + * @magentoConfigFixture current_store customer/password/forgot_email_template customer_password_forgot_email_template + * @codingStandardsIgnoreEnd + * @magentoConfigFixture current_store customer/password/forgot_email_identity support + * @magentoConfigFixture current_store customer/captcha/enable 0 + * @magentoDataFixture Magento/Customer/_files/customer.php + * + * @return void + */ + public function testForgotPasswordPostAction(): void + { + $email = 'customer@example.com'; + $this->getRequest()->setMethod(HttpRequest::METHOD_POST); + $this->getRequest()->setPostValue(['email' => $email]); + $this->dispatch('customer/account/forgotPasswordPost'); + $this->assertRedirect($this->stringContains('customer/account/')); + $this->assertSuccessSessionMessage($email); + } + + /** + * @magentoConfigFixture current_store customer/captcha/enable 0 + * + * @return void + */ + public function testForgotPasswordPostWithBadEmailAction(): void + { + $this->getRequest()->setMethod(HttpRequest::METHOD_POST); + $this->getRequest()->setPostValue(['email' => 'bad@email']); + $this->dispatch('customer/account/forgotPasswordPost'); + $this->assertRedirect($this->stringContains('customer/account/forgotpassword')); + $this->assertSessionMessages( + $this->equalTo(['The email address is incorrect. Verify the email address and try again.']), + MessageInterface::TYPE_ERROR + ); + } + + /** + * Assert success session message + * + * @param string $email + * @return void + */ + private function assertSuccessSessionMessage(string $email): void + { + $message = __( + 'If there is an account associated with %1 you will receive an email with a link to reset your password.', + $email + ); + $this->assertSessionMessages($this->equalTo([$message]), MessageInterface::TYPE_SUCCESS); + } +} diff --git a/dev/tests/integration/testsuite/Magento/Customer/Controller/Section/LoadTest.php b/dev/tests/integration/testsuite/Magento/Customer/Controller/Section/LoadTest.php index 3db22b8379850..0869832091fec 100644 --- a/dev/tests/integration/testsuite/Magento/Customer/Controller/Section/LoadTest.php +++ b/dev/tests/integration/testsuite/Magento/Customer/Controller/Section/LoadTest.php @@ -3,19 +3,104 @@ * Copyright © Magento, Inc. All rights reserved. * See COPYING.txt for license details. */ +declare(strict_types=1); namespace Magento\Customer\Controller\Section; -class LoadTest extends \Magento\TestFramework\TestCase\AbstractController +use Magento\Customer\Model\Session; +use Magento\Framework\App\Request\Http as HttpRequest; +use Magento\Framework\Serialize\SerializerInterface; +use Magento\TestFramework\TestCase\AbstractController; +use Magento\Framework\Escaper; + +/** + * Load customer data test class. + * + * @magentoDbIsolation enabled + * @magentoAppArea frontend + */ +class LoadTest extends AbstractController { - public function testLoadInvalidSection() + /** @var Session */ + private $customerSession; + + /** @var SerializerInterface */ + private $json; + + /** @var Escaper */ + private $escaper; + + /** + * @inheritdoc + */ + protected function setUp() + { + parent::setUp(); + + $this->customerSession = $this->_objectManager->get(Session::class); + $this->json = $this->_objectManager->get(SerializerInterface::class); + $this->escaper = $this->_objectManager->get(Escaper::class); + } + + /** + * @inheritdoc + */ + protected function tearDown() + { + $this->customerSession->setCustomerId(null); + + parent::tearDown(); + } + + /** + * @return void + */ + public function testLoadInvalidSection(): void { - $expected = [ - 'message' => 'The "section<invalid" section source isn't supported.', - ]; + $message = $this->escaper->escapeHtml('The "section<invalid" section source isn\'t supported.'); + $expected = ['message' => $message]; $this->dispatch( '/customer/section/load/?sections=section<invalid&force_new_section_timestamp=false&_=147066166394' ); - self::assertEquals(json_encode($expected), $this->getResponse()->getBody()); + $this->assertEquals($this->json->serialize($expected), $this->getResponse()->getBody()); + } + + /** + * @magentoConfigFixture current_store wishlist/wishlist_link/use_qty 1 + * @magentoDataFixture Magento/Wishlist/_files/wishlist_with_product_qty_three.php + * + * @return void + */ + public function testWishListCounterUseQty(): void + { + $this->customerSession->setCustomerId(1); + $response = $this->performWishListSectionRequest(); + $this->assertEquals('3 items', $response['wishlist']['counter']); + } + + /** + * @magentoConfigFixture current_store wishlist/wishlist_link/use_qty 0 + * @magentoDataFixture Magento/Wishlist/_files/wishlist_with_product_qty_three.php + * + * @return void + */ + public function testWishListCounterNotUseQty(): void + { + $this->customerSession->setCustomerId(1); + $response = $this->performWishListSectionRequest(); + $this->assertEquals('1 item', $response['wishlist']['counter']); + } + + /** + * Perform wish list section request. + * + * @return array + */ + private function performWishListSectionRequest(): array + { + $this->getRequest()->setParam('sections', 'wishlist')->setMethod(HttpRequest::METHOD_GET); + $this->dispatch('customer/section/load'); + + return $this->json->unserialize($this->getResponse()->getBody()); } } diff --git a/dev/tests/integration/testsuite/Magento/Customer/Model/AccountManagement/CreateAccountTest.php b/dev/tests/integration/testsuite/Magento/Customer/Model/AccountManagement/CreateAccountTest.php new file mode 100644 index 0000000000000..8be3dfc10d86e --- /dev/null +++ b/dev/tests/integration/testsuite/Magento/Customer/Model/AccountManagement/CreateAccountTest.php @@ -0,0 +1,567 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +declare(strict_types=1); + +namespace Magento\Customer\Model\AccountManagement; + +use Magento\Customer\Api\AccountManagementInterface; +use Magento\Customer\Api\CustomerRepositoryInterface; +use Magento\Customer\Api\Data\CustomerInterface; +use Magento\Customer\Api\Data\CustomerInterfaceFactory; +use Magento\Customer\Model\Customer; +use Magento\Customer\Model\CustomerFactory; +use Magento\Framework\Api\DataObjectHelper; +use Magento\Framework\Api\ExtensibleDataObjectConverter; +use Magento\Framework\Api\SimpleDataObjectConverter; +use Magento\Framework\Encryption\EncryptorInterface; +use Magento\Framework\Exception\InputException; +use Magento\Framework\Exception\LocalizedException; +use Magento\Framework\Math\Random; +use Magento\Framework\ObjectManagerInterface; +use Magento\Framework\Validator\Exception; +use Magento\Store\Model\StoreManagerInterface; +use Magento\TestFramework\Helper\Bootstrap; +use Magento\TestFramework\Helper\Xpath; +use Magento\TestFramework\Mail\Template\TransportBuilderMock; +use PHPUnit\Framework\TestCase; + +/** + * Tests for customer creation via customer account management service. + * + * @magentoDbIsolation enabled + * @SuppressWarnings(PHPMD.CouplingBetweenObjects) + */ +class CreateAccountTest extends TestCase +{ + /** + * @var ObjectManagerInterface + */ + private $objectManager; + + /** + * @var AccountManagementInterface + */ + private $accountManagement; + + /** + * @var CustomerInterfaceFactory + */ + private $customerFactory; + + /** + * @var DataObjectHelper + */ + private $dataObjectHelper; + + /** + * @var array + */ + private $defaultCustomerData = [ + 'email' => 'customer@example.com', + 'firstname' => 'First name', + 'lastname' => 'Last name', + ]; + + /** + * @var TransportBuilderMock + */ + private $transportBuilderMock; + + /** + * @var StoreManagerInterface + */ + private $storeManager; + + /** + * @var CustomerRepositoryInterface + */ + private $customerRepository; + + /** + * @var ExtensibleDataObjectConverter + */ + private $extensibleDataObjectConverter; + + /** + * @var CustomerFactory + */ + private $customerModelFactory; + + /** + * @var Random + */ + private $random; + + /** + * @var EncryptorInterface + */ + private $encryptor; + + /** + * @inheritdoc + */ + protected function setUp() + { + $this->objectManager = Bootstrap::getObjectManager(); + $this->accountManagement = $this->objectManager->get(AccountManagementInterface::class); + $this->customerFactory = $this->objectManager->get(CustomerInterfaceFactory::class); + $this->dataObjectHelper = $this->objectManager->create(DataObjectHelper::class); + $this->transportBuilderMock = $this->objectManager->get(TransportBuilderMock::class); + $this->storeManager = $this->objectManager->get(StoreManagerInterface::class); + $this->customerRepository = $this->objectManager->get(CustomerRepositoryInterface::class); + $this->extensibleDataObjectConverter = $this->objectManager->get(ExtensibleDataObjectConverter::class); + $this->customerModelFactory = $this->objectManager->get(CustomerFactory::class); + $this->random = $this->objectManager->get(Random::class); + $this->encryptor = $this->objectManager->get(EncryptorInterface::class); + parent::setUp(); + } + + /** + * @dataProvider createInvalidAccountDataProvider + * @param array $customerData + * @param string $password + * @param string $errorType + * @param string $errorMessage + * @return void + */ + public function testCreateAccountWithInvalidFields( + array $customerData, + string $password, + string $errorType, + array $errorMessage + ): void { + $customerEntity = $this->populateCustomerEntity($this->defaultCustomerData, $customerData); + $this->expectException($errorType); + $this->expectExceptionMessage((string)__(...$errorMessage)); + $this->accountManagement->createAccount($customerEntity, $password); + } + + /** + * @return array + */ + public function createInvalidAccountDataProvider(): array + { + return [ + 'empty_firstname' => [ + 'customer_data' => ['firstname' => ''], + 'password' => '_aPassword1', + 'error_type' => Exception::class, + 'error_message' => ['"%1" is a required value.', 'First Name'], + ], + 'empty_lastname' => [ + 'customer_data' => ['lastname' => ''], + 'password' => '_aPassword1', + 'error_type' => Exception::class, + 'error_message' => ['"%1" is a required value.', 'Last Name'], + ], + 'empty_email' => [ + 'customer_data' => ['email' => ''], + 'password' => '_aPassword1', + 'error_type' => Exception::class, + 'error_message' => ['The customer email is missing. Enter and try again.'], + ], + 'invalid_email' => [ + 'customer_data' => ['email' => 'zxczxczxc'], + 'password' => '_aPassword1', + 'error_type' => Exception::class, + 'error_message' => ['"%1" is not a valid email address.', 'Email'], + ], + 'empty_password' => [ + 'customer_data' => [], + 'password' => '', + 'error_type' => InputException::class, + 'error_message' => ['The password needs at least 8 characters. Create a new password and try again.'], + ], + 'invalid_password_minimum_length' => [ + 'customer_data' => [], + 'password' => 'test', + 'error_type' => InputException::class, + 'error_message' => ['The password needs at least 8 characters. Create a new password and try again.'], + ], + 'invalid_password_maximum_length' => [ + 'customer_data' => [], + 'password' => $this->getRandomNumericString(257), + 'error_type' => InputException::class, + 'error_message' => ['Please enter a password with at most 256 characters.'], + ], + 'invalid_password_without_minimum_characters_classes' => [ + 'customer_data' => [], + 'password' => 'test_password', + 'error_type' => InputException::class, + 'error_message' => [ + 'Minimum of different classes of characters in password is %1.' + . ' Classes of characters: Lower Case, Upper Case, Digits, Special Characters.', + 3, + ], + ], + 'password_same_as_email' => [ + 'customer_data' => ['email' => 'test1@test.com'], + 'password' => 'test1@test.com', + 'error_type' => LocalizedException::class, + 'error_message' => [ + 'The password can\'t be the same as the email address. Create a new password and try again.', + ], + ], + 'send_email_store_id_not_match_website' => [ + 'customer_data' => [ + CustomerInterface::WEBSITE_ID => 1, + CustomerInterface::STORE_ID => 5, + ], + 'password' => '_aPassword1', + 'error_type' => LocalizedException::class, + 'error_message' => [ + 'The store view is not in the associated website.', + ], + ], + ]; + } + + /** + * Assert that when you create customer account via admin, link with "set password" is send to customer email. + * + * @return void + */ + public function testSendEmailWithSetPasswordLink(): void + { + $customerEntity = $this->populateCustomerEntity($this->defaultCustomerData); + $newCustomerEntity = $this->accountManagement->createAccount($customerEntity); + $mailTemplate = $this->transportBuilderMock->getSentMessage()->getBody()->getParts()[0]->getRawContent(); + + $this->assertEquals( + 1, + Xpath::getElementsCountForXpath( + sprintf("//a[contains(@href, 'customer/account/createPassword/?id=%s')]", $newCustomerEntity->getId()), + $mailTemplate + ), + 'Password creation link was not found.' + ); + } + + /** + * @magentoDataFixture Magento/Store/_files/second_website_with_two_stores.php + * @return void + */ + public function testCreateCustomerOnSecondWebsite(): void + { + $customerData = [ + CustomerInterface::WEBSITE_ID => $this->storeManager->getWebsite('test')->getId(), + CustomerInterface::STORE_ID => $this->storeManager->getStore('fixture_third_store')->getId(), + ]; + $expectedCustomerData = array_merge($this->defaultCustomerData, $customerData); + $customerEntity = $this->populateCustomerEntity($this->defaultCustomerData, $customerData); + $savedCustomerEntity = $this->accountManagement->createAccount($customerEntity); + + $this->assertNotNull($savedCustomerEntity->getId()); + $this->assertCustomerData($savedCustomerEntity, $expectedCustomerData); + } + + /** + * @return void + */ + public function testCreateNewCustomerWithPasswordHash(): void + { + $customerData = $expectedCustomerData = [ + CustomerInterface::EMAIL => 'email@example.com', + CustomerInterface::STORE_ID => 1, + CustomerInterface::FIRSTNAME => 'Tester', + CustomerInterface::LASTNAME => 'McTest', + CustomerInterface::GROUP_ID => 1, + ]; + $newCustomerEntity = $this->populateCustomerEntity($customerData); + $password = $this->random->getRandomString(8); + $passwordHash = $this->encryptor->getHash($password, true); + $savedCustomer = $this->accountManagement->createAccountWithPasswordHash( + $newCustomerEntity, + $passwordHash + ); + $this->assertNotNull($savedCustomer->getId()); + $this->assertCustomerData($savedCustomer, $expectedCustomerData); + $this->assertEmpty($savedCustomer->getSuffix()); + $this->assertEquals( + $savedCustomer->getId(), + $this->accountManagement->authenticate($customerData[CustomerInterface::EMAIL], $password)->getId() + ); + } + + /** + * Customer has two addresses one of it is allowed in website and second is not + * + * @magentoDataFixture Magento/Customer/_files/customer.php + * @magentoDataFixture Magento/Customer/_files/customer_two_addresses.php + * @magentoDataFixture Magento/Store/_files/websites_different_countries.php + * @magentoConfigFixture fixture_second_store_store general/country/allow UA + * @return void + */ + public function testCreateNewCustomerWithPasswordHashWithNotAllowedCountry(): void + { + $customerId = 1; + $allowedCountryIdForSecondWebsite = 'UA'; + $store = $this->storeManager->getStore('fixture_second_store'); + $customerData = $this->customerRepository->getById($customerId); + $customerData->getAddresses()[1]->setRegion(null)->setCountryId($allowedCountryIdForSecondWebsite) + ->setRegionId(null); + $customerData->setStoreId($store->getId())->setWebsiteId($store->getWebsiteId())->setId(null); + $password = $this->random->getRandomString(8); + $passwordHash = $this->encryptor->getHash($password, true); + $savedCustomer = $this->accountManagement->createAccountWithPasswordHash( + $customerData, + $passwordHash + ); + $this->assertCount( + 1, + $savedCustomer->getAddresses(), + 'The wrong address quantity was saved' + ); + $this->assertSame( + 'UA', + $savedCustomer->getAddresses()[0]->getCountryId(), + 'The address with the disallowed country was saved' + ); + } + + /** + * @magentoAppArea frontend + * @magentoDataFixture Magento/Customer/_files/customer.php + * @return void + */ + public function testCreateNoExistingCustomer(): void + { + $existingCustId = 1; + $existingCustomer = $this->customerRepository->getById($existingCustId); + $customerData = $expectedCustomerData = [ + CustomerInterface::EMAIL => 'savecustomer@example.com', + CustomerInterface::FIRSTNAME => 'Firstsave', + CustomerInterface::LASTNAME => 'Lastsave', + CustomerInterface::ID => null, + ]; + unset($expectedCustomerData[CustomerInterface::ID]); + $customerEntity = $this->populateCustomerEntity($existingCustomer->__toArray(), $customerData); + + $customerAfter = $this->accountManagement->createAccount($customerEntity, '_aPassword1'); + $this->assertGreaterThan(0, $customerAfter->getId()); + $this->assertCustomerData($customerAfter, $expectedCustomerData); + $this->accountManagement->authenticate( + $customerAfter->getEmail(), + '_aPassword1' + ); + $attributesBefore = $this->extensibleDataObjectConverter->toFlatArray( + $existingCustomer, + [], + CustomerInterface::class + ); + $attributesAfter = $this->extensibleDataObjectConverter->toFlatArray( + $customerAfter, + [], + CustomerInterface::class + ); + // ignore 'updated_at' + unset($attributesBefore['updated_at']); + unset($attributesAfter['updated_at']); + $inBeforeOnly = array_diff_assoc($attributesBefore, $attributesAfter); + $inAfterOnly = array_diff_assoc($attributesAfter, $attributesBefore); + $expectedInBefore = [ + 'email', + 'firstname', + 'id', + 'lastname', + ]; + sort($expectedInBefore); + $actualInBeforeOnly = array_keys($inBeforeOnly); + sort($actualInBeforeOnly); + $this->assertEquals($expectedInBefore, $actualInBeforeOnly); + $expectedInAfter = [ + 'created_in', + 'email', + 'firstname', + 'id', + 'lastname', + ]; + $actualInAfterOnly = array_keys($inAfterOnly); + foreach ($expectedInAfter as $item) { + $this->assertContains($item, $actualInAfterOnly); + } + } + + /** + * @return void + */ + public function testCreateCustomerInServiceVsInModel(): void + { + $password = '_aPassword1'; + $firstCustomerData = $secondCustomerData = [ + CustomerInterface::EMAIL => 'email@example.com', + CustomerInterface::FIRSTNAME => 'Tester', + CustomerInterface::LASTNAME => 'McTest', + CustomerInterface::GROUP_ID => 1, + ]; + $secondCustomerData[CustomerInterface::EMAIL] = 'email2@example.com'; + + /** @var Customer $customerModel */ + $customerModel = $this->customerModelFactory->create(); + $customerModel->setData($firstCustomerData)->setPassword($password); + $customerModel->save(); + /** @var Customer $customerModel */ + $savedModel = $this->customerModelFactory->create()->load($customerModel->getId()); + $dataInModel = $savedModel->getData(); + $newCustomerEntity = $this->populateCustomerEntity($secondCustomerData); + + $customerData = $this->accountManagement->createAccount($newCustomerEntity, $password); + $this->assertNotNull($customerData->getId()); + $savedCustomer = $this->customerRepository->getById($customerData->getId()); + + /** @var SimpleDataObjectConverter $simpleDataObjectConverter */ + $simpleDataObjectConverter = $this->objectManager->get(SimpleDataObjectConverter::class); + + $dataInService = $simpleDataObjectConverter->toFlatArray( + $savedCustomer, + CustomerInterface::class + ); + $expectedDifferences = [ + 'created_at', + 'updated_at', + 'email', + 'is_active', + 'entity_id', + 'entity_type_id', + 'password_hash', + 'attribute_set_id', + 'disable_auto_group_change', + 'confirmation', + 'reward_update_notification', + 'reward_warning_notification', + ]; + foreach ($dataInModel as $key => $value) { + if (!in_array($key, $expectedDifferences)) { + if ($value === null) { + $this->assertArrayNotHasKey($key, $dataInService); + } elseif (isset($dataInService[$key])) { + $this->assertEquals($value, $dataInService[$key], 'Failed asserting value for ' . $key); + } + } + } + $this->assertEquals($secondCustomerData[CustomerInterface::EMAIL], $dataInService['email']); + $this->assertArrayNotHasKey('is_active', $dataInService); + $this->assertArrayNotHasKey('password_hash', $dataInService); + } + + /** + * @return void + */ + public function testCreateNewCustomer(): void + { + $customerData = $expectedCustomerData = [ + CustomerInterface::EMAIL => 'email@example.com', + CustomerInterface::STORE_ID => 1, + CustomerInterface::FIRSTNAME => 'Tester', + CustomerInterface::LASTNAME => 'McTest', + CustomerInterface::GROUP_ID => 1, + ]; + $newCustomerEntity = $this->populateCustomerEntity($customerData); + + $savedCustomer = $this->accountManagement->createAccount($newCustomerEntity, '_aPassword1'); + $this->assertNotNull($savedCustomer->getId()); + $this->assertCustomerData($savedCustomer, $expectedCustomerData); + $this->assertEmpty($savedCustomer->getSuffix()); + } + + /** + * @magentoAppArea frontend + * @magentoDataFixture Magento/Customer/_files/customer.php + * @return void + */ + public function testCreateNewCustomerFromClone(): void + { + $existingCustId = 1; + $existingCustomer = $this->customerRepository->getById($existingCustId); + $customerEntity = $this->customerFactory->create(); + $this->dataObjectHelper->mergeDataObjects( + CustomerInterface::class, + $customerEntity, + $existingCustomer + ); + $customerData = $expectedCustomerData = [ + CustomerInterface::EMAIL => 'savecustomer@example.com', + CustomerInterface::FIRSTNAME => 'Firstsave', + CustomerInterface::LASTNAME => 'Lastsave', + CustomerInterface::ID => null, + ]; + unset($expectedCustomerData[CustomerInterface::ID]); + $customerEntity = $this->populateCustomerEntity($customerData, [], $customerEntity); + + $customer = $this->accountManagement->createAccount($customerEntity, '_aPassword1'); + $this->assertNotEmpty($customer->getId()); + $this->assertCustomerData($customer, $expectedCustomerData); + $this->accountManagement->authenticate( + $customer->getEmail(), + '_aPassword1', + true + ); + } + + /** + * Returns random numeric string with given length. + * + * @param int $length + * @return string + */ + private function getRandomNumericString(int $length): string + { + $string = ''; + for ($i = 0; $i <= $length; $i++) { + $string .= Random::getRandomNumber(0, 9); + } + + return $string; + } + + /** + * Fill in customer entity using array of customer data and additional customer data. + * + * @param array $customerData + * @param array $additionalCustomerData + * @param CustomerInterface|null $customerEntity + * @return CustomerInterface + */ + private function populateCustomerEntity( + array $customerData, + array $additionalCustomerData = [], + ?CustomerInterface $customerEntity = null + ): CustomerInterface { + $customerEntity = $customerEntity ?? $this->customerFactory->create(); + $customerData = array_merge( + $customerData, + $additionalCustomerData + ); + $this->dataObjectHelper->populateWithArray( + $customerEntity, + $customerData, + CustomerInterface::class + ); + + return $customerEntity; + } + + /** + * Check that customer parameters match expected values. + * + * @param CustomerInterface $customer + * @param array $expectedData + * return void + */ + private function assertCustomerData( + CustomerInterface $customer, + array $expectedData + ): void { + $actualCustomerArray = $customer->__toArray(); + foreach ($expectedData as $key => $expectedValue) { + $this->assertEquals( + $expectedValue, + $actualCustomerArray[$key], + "Invalid expected value for $key field." + ); + } + } +} diff --git a/dev/tests/integration/testsuite/Magento/Customer/Model/AccountManagement/ForgotPasswordTest.php b/dev/tests/integration/testsuite/Magento/Customer/Model/AccountManagement/ForgotPasswordTest.php new file mode 100644 index 0000000000000..7820316d9f41f --- /dev/null +++ b/dev/tests/integration/testsuite/Magento/Customer/Model/AccountManagement/ForgotPasswordTest.php @@ -0,0 +1,64 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +declare(strict_types=1); + +namespace Magento\Customer\Model\AccountManagement; + +use Magento\Customer\Api\AccountManagementInterface; +use Magento\Customer\Model\AccountManagement; +use Magento\Framework\ObjectManagerInterface; +use Magento\TestFramework\Helper\Bootstrap; +use Magento\TestFramework\Helper\Xpath; +use Magento\TestFramework\Mail\Template\TransportBuilderMock; +use PHPUnit\Framework\TestCase; + +/** + * Class checks password forgot scenarios + * + * @magentoDbIsolation enabled + */ +class ForgotPasswordTest extends TestCase +{ + /** @var ObjectManagerInterface */ + private $objectManager; + + /** @var AccountManagementInterface */ + private $accountManagement; + + /** @var TransportBuilderMock */ + private $transportBuilder; + + /** @var string */ + private $newPasswordLinkPath = "//a[contains(@href, 'customer/account/createPassword') " + . "and contains(text(), 'Set a New Password')]"; + + /** + * @inheritdoc + */ + protected function setUp() + { + parent::setUp(); + + $this->objectManager = Bootstrap::getObjectManager(); + $this->accountManagement = $this->objectManager->get(AccountManagementInterface::class); + $this->transportBuilder = $this->objectManager->get(TransportBuilderMock::class); + } + + /** + * @magentoDataFixture Magento/Customer/_files/customer.php + * + * @return void + */ + public function testForgotPassword(): void + { + $email = 'customer@example.com'; + $result = $this->accountManagement->initiatePasswordReset($email, AccountManagement::EMAIL_RESET); + $message = $this->transportBuilder->getSentMessage(); + $messageContent = $message->getBody()->getParts()[0]->getRawContent(); + $this->assertTrue($result); + $this->assertEquals(1, Xpath::getElementsCountForXpath($this->newPasswordLinkPath, $messageContent)); + } +} diff --git a/dev/tests/integration/testsuite/Magento/Customer/Model/AccountManagement/ResetPasswordTest.php b/dev/tests/integration/testsuite/Magento/Customer/Model/AccountManagement/ResetPasswordTest.php new file mode 100644 index 0000000000000..012838ebdf697 --- /dev/null +++ b/dev/tests/integration/testsuite/Magento/Customer/Model/AccountManagement/ResetPasswordTest.php @@ -0,0 +1,147 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +declare(strict_types=1); + +namespace Magento\Customer\Model\AccountManagement; + +use Magento\Customer\Api\AccountManagementInterface; +use Magento\Customer\Model\AccountManagement; +use Magento\Customer\Model\CustomerRegistry; +use Magento\Framework\Exception\NoSuchEntityException; +use Magento\Framework\ObjectManagerInterface; +use Magento\Store\Model\StoreManagerInterface; +use Magento\TestFramework\Helper\Bootstrap; +use Magento\TestFramework\Helper\Xpath; +use Magento\TestFramework\Mail\Template\TransportBuilderMock; +use PHPUnit\Framework\TestCase; + +/** + * Tests for customer password reset via customer account management service. + * + * @magentoDbIsolation enabled + */ +class ResetPasswordTest extends TestCase +{ + /** @var ObjectManagerInterface */ + private $objectManager; + + /** @var AccountManagementInterface */ + private $accountManagement; + + /** @var TransportBuilderMock*/ + private $transportBuilderMock; + + /** @var CustomerRegistry */ + private $customerRegistry; + + /** @var StoreManagerInterface */ + private $storeManager; + + /** + * @inheritdoc + */ + protected function setUp() + { + $this->objectManager = Bootstrap::getObjectManager(); + $this->accountManagement = $this->objectManager->get(AccountManagementInterface::class); + $this->transportBuilderMock = $this->objectManager->get(TransportBuilderMock::class); + $this->customerRegistry = $this->objectManager->get(CustomerRegistry::class); + $this->storeManager = $this->objectManager->get(StoreManagerInterface::class); + parent::setUp(); + } + + /** + * Assert that when you reset customer password via admin, link with "Set a New Password" is send to customer email. + * + * @magentoDataFixture Magento/Customer/_files/customer.php + * @return void + */ + public function testSendEmailWithSetNewPasswordLink(): void + { + $this->accountManagement->initiatePasswordReset( + 'customer@example.com', + AccountManagement::EMAIL_REMINDER, + 1 + ); + $customerSecure = $this->customerRegistry->retrieveSecureData(1); + $mailTemplate = $this->transportBuilderMock->getSentMessage()->getBody()->getParts()[0]->getRawContent(); + + $this->assertEquals( + 1, + Xpath::getElementsCountForXpath( + sprintf( + '//a[contains(@href, \'customer/account/createPassword/?id=%1$d&token=%2$s\')]', + $customerSecure->getId(), + $customerSecure->getRpToken() + ), + $mailTemplate + ), + 'Reset password creation link was not found.' + ); + } + + /** + * @magentoAppArea frontend + * @magentoDataFixture Magento/Customer/_files/customer.php + * @return void + */ + public function testSendPasswordResetLink(): void + { + $email = 'customer@example.com'; + $websiteId = (int)$this->storeManager->getWebsite('base')->getId(); + + $this->accountManagement->initiatePasswordReset($email, AccountManagement::EMAIL_RESET, $websiteId); + } + + /** + * @magentoAppArea frontend + * @magentoDataFixture Magento/Customer/_files/customer.php + * @return void + */ + public function testSendPasswordResetLinkDefaultWebsite(): void + { + $email = 'customer@example.com'; + + $this->accountManagement->initiatePasswordReset($email, AccountManagement::EMAIL_RESET); + } + + /** + * @magentoAppArea frontend + * @dataProvider passwordResetErrorsProvider + * @magentoDataFixture Magento/Customer/_files/customer.php + * @param string $email + * @param int|null $websiteId + * @return void + */ + public function testPasswordResetErrors(string $email, ?int $websiteId = null): void + { + $websiteId = $websiteId ?? (int)$this->storeManager->getWebsite('base')->getId(); + $this->expectExceptionObject( + NoSuchEntityException::doubleField('email', $email, 'websiteId', $websiteId) + ); + $this->accountManagement->initiatePasswordReset( + $email, + AccountManagement::EMAIL_RESET, + $websiteId + ); + } + + /** + * @return array + */ + public function passwordResetErrorsProvider(): array + { + return [ + 'wrong_email' => [ + 'email' => 'foo@example.com', + ], + 'wrong_website_id' => [ + 'email' => 'customer@example.com', + 'website_id' => 0, + ], + ]; + } +} diff --git a/dev/tests/integration/testsuite/Magento/Customer/Model/AccountManagement/ValidateTest.php b/dev/tests/integration/testsuite/Magento/Customer/Model/AccountManagement/ValidateTest.php new file mode 100644 index 0000000000000..8daa310d6dc03 --- /dev/null +++ b/dev/tests/integration/testsuite/Magento/Customer/Model/AccountManagement/ValidateTest.php @@ -0,0 +1,113 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +declare(strict_types=1); + +namespace Magento\Customer\Model\AccountManagement; + +use Magento\Customer\Api\AccountManagementInterface; +use Magento\Customer\Api\Data\CustomerInterface; +use Magento\Customer\Api\Data\CustomerInterfaceFactory; +use Magento\Framework\Api\DataObjectHelper; +use Magento\Framework\ObjectManagerInterface; +use Magento\TestFramework\Helper\Bootstrap; +use PHPUnit\Framework\TestCase; + +/** + * Tests for customer validation via customer account management service. + * + * @magentoDbIsolation enabled + */ +class ValidateTest extends TestCase +{ + /** @var ObjectManagerInterface */ + private $objectManager; + + /** @var AccountManagementInterface */ + private $accountManagement; + + /** @var CustomerInterfaceFactory */ + private $customerFactory; + + /** @var DataObjectHelper */ + private $dataObjectHelper; + + /** + * @inheritdoc + */ + protected function setUp() + { + $this->objectManager = Bootstrap::getObjectManager(); + $this->accountManagement = $this->objectManager->get(AccountManagementInterface::class); + $this->customerFactory = $this->objectManager->get(CustomerInterfaceFactory::class); + $this->dataObjectHelper = $this->objectManager->get(DataObjectHelper::class); + parent::setUp(); + } + + /** + * Validate customer fields. + * + * @dataProvider validateFieldsProvider + * + * @param array $customerData + * @param array $expectedResults + * @return void + */ + public function testValidateFields( + array $customerData, + array $expectedResults + ): void { + $customerEntity = $this->customerFactory->create(); + $this->dataObjectHelper->populateWithArray( + $customerEntity, + $customerData, + CustomerInterface::class + ); + $validationResults = $this->accountManagement->validate($customerEntity); + $this->assertEquals( + $expectedResults, + [ + 'valid' => $validationResults->isValid(), + 'messages' => $validationResults->getMessages(), + ] + ); + } + + /** + * @return array + */ + public function validateFieldsProvider(): array + { + $attributeEmptyMessage = 'The "%1" attribute value is empty. Set the attribute and try again.'; + return [ + 'without_required_fields' => [ + 'customer_data' => [], + 'expectedResults' => [ + 'valid' => false, + 'messages' => [ + (string)__($attributeEmptyMessage, 'Associate to Website'), + (string)__($attributeEmptyMessage, 'Group'), + (string)__($attributeEmptyMessage, 'First Name'), + (string)__($attributeEmptyMessage, 'Last Name'), + (string)__($attributeEmptyMessage, 'Email'), + ], + ], + ], + 'with_required_fields' => [ + 'customer_data' => [ + CustomerInterface::WEBSITE_ID => 1, + CustomerInterface::GROUP_ID => 1, + CustomerInterface::FIRSTNAME => 'Jane', + CustomerInterface::LASTNAME => 'Doe', + CustomerInterface::EMAIL => 'janedoe' . uniqid() . '@example.com', + ], + 'expectedResults' => [ + 'valid' => true, + 'messages' => [], + ], + ], + ]; + } +} diff --git a/dev/tests/integration/testsuite/Magento/Customer/Model/AccountManagementTest.php b/dev/tests/integration/testsuite/Magento/Customer/Model/AccountManagementTest.php index 754c949747d61..4ff16189df1ba 100644 --- a/dev/tests/integration/testsuite/Magento/Customer/Model/AccountManagementTest.php +++ b/dev/tests/integration/testsuite/Magento/Customer/Model/AccountManagementTest.php @@ -8,13 +8,11 @@ use Magento\Customer\Api\AccountManagementInterface; use Magento\Customer\Api\AddressRepositoryInterface; -use Magento\Customer\Api\CustomerRepositoryInterface; use Magento\Customer\Api\Data\AddressInterface; use Magento\Framework\Exception\InputException; use Magento\Framework\Exception\NoSuchEntityException; use Magento\Framework\Exception\State\ExpiredException; use Magento\Framework\Reflection\DataObjectProcessor; -use Magento\Store\Model\StoreManagerInterface; use Magento\TestFramework\Helper\Bootstrap; /** @@ -30,9 +28,6 @@ class AccountManagementTest extends \PHPUnit\Framework\TestCase /** @var AccountManagementInterface */ private $accountManagement; - /** @var CustomerRepositoryInterface */ - private $customerRepository; - /** @var AddressRepositoryInterface needed to setup tests */ private $addressRepository; @@ -45,18 +40,9 @@ class AccountManagementTest extends \PHPUnit\Framework\TestCase /** @var \Magento\Customer\Api\Data\AddressInterfaceFactory */ private $addressFactory; - /** @var \Magento\Customer\Api\Data\CustomerInterfaceFactory */ - private $customerFactory; - /** @var DataObjectProcessor */ private $dataProcessor; - /** @var \Magento\Framework\Api\ExtensibleDataObjectConverter */ - private $extensibleDataObjectConverter; - - /** @var StoreManagerInterface */ - private $storeManager; - /** @var \Magento\Framework\Api\DataObjectHelper */ protected $dataObjectHelper; @@ -65,16 +51,10 @@ protected function setUp() $this->objectManager = Bootstrap::getObjectManager(); $this->accountManagement = $this->objectManager ->create(\Magento\Customer\Api\AccountManagementInterface::class); - $this->customerRepository = $this->objectManager - ->create(\Magento\Customer\Api\CustomerRepositoryInterface::class); $this->addressRepository = $this->objectManager->create(\Magento\Customer\Api\AddressRepositoryInterface::class); $this->addressFactory = $this->objectManager->create(\Magento\Customer\Api\Data\AddressInterfaceFactory::class); - $this->customerFactory = $this->objectManager->create( - \Magento\Customer\Api\Data\CustomerInterfaceFactory::class - ); - $this->dataObjectHelper = $this->objectManager->create(\Magento\Framework\Api\DataObjectHelper::class); $regionFactory = $this->objectManager->create(\Magento\Customer\Api\Data\RegionInterfaceFactory::class); $address = $this->addressFactory->create(); @@ -115,12 +95,6 @@ protected function setUp() $this->dataProcessor = $this->objectManager ->create(\Magento\Framework\Reflection\DataObjectProcessor::class); - - $this->extensibleDataObjectConverter = $this->objectManager - ->create(\Magento\Framework\Api\ExtensibleDataObjectConverter::class); - - $this->storeManager = $this->objectManager - ->create(StoreManagerInterface::class); } /** @@ -387,73 +361,6 @@ public function testValidateResetPasswordLinkTokenAmbiguous() $this->accountManagement->validateResetPasswordLinkToken(null, $token); } - /** - * @magentoAppArea frontend - * @magentoDataFixture Magento/Customer/_files/customer.php - */ - public function testSendPasswordResetLink() - { - $email = 'customer@example.com'; - - $this->accountManagement->initiatePasswordReset($email, AccountManagement::EMAIL_RESET, 1); - } - - /** - * @magentoAppArea frontend - * @magentoDataFixture Magento/Customer/_files/customer.php - */ - public function testSendPasswordResetLinkDefaultWebsite() - { - $email = 'customer@example.com'; - - $this->accountManagement->initiatePasswordReset($email, AccountManagement::EMAIL_RESET); - } - - /** - * @magentoDataFixture Magento/Customer/_files/customer.php - * - */ - public function testSendPasswordResetLinkBadEmailOrWebsite() - { - $email = 'foo@example.com'; - - try { - $this->accountManagement->initiatePasswordReset( - $email, - AccountManagement::EMAIL_RESET, - 0 - ); - $this->fail('Expected exception not thrown.'); - } catch (NoSuchEntityException $e) { - $expectedParams = [ - 'fieldName' => 'email', - 'fieldValue' => $email, - 'field2Name' => 'websiteId', - 'field2Value' => 0, - ]; - $this->assertEquals($expectedParams, $e->getParameters()); - } - } - - /** - * @magentoDataFixture Magento/Customer/_files/customer.php - */ - public function testSendPasswordResetLinkBadEmailDefaultWebsite() - { - $email = 'foo@example.com'; - - try { - $this->accountManagement->initiatePasswordReset( - $email, - AccountManagement::EMAIL_RESET - ); - $this->fail('Expected exception not thrown.'); - } catch (NoSuchEntityException $nsee) { - // App area is frontend, so we expect websiteId of 1. - $this->assertEquals('No such entity with email = foo@example.com, websiteId = 1', $nsee->getMessage()); - } - } - /** * @magentoDataFixture Magento/Customer/_files/customer.php */ @@ -620,310 +527,6 @@ public function testResendConfirmationNotNeeded() $this->accountManagement->resendConfirmation('customer@example.com', 1); } - /** - * @magentoDbIsolation enabled - */ - public function testCreateCustomerException() - { - $customerEntity = $this->customerFactory->create(); - - try { - $this->accountManagement->createAccount($customerEntity); - $this->fail('Expected exception not thrown'); - } catch (InputException $ie) { - $this->assertEquals('The customer email is missing. Enter and try again.', $ie->getMessage()); - } - } - - /** - * @magentoAppArea frontend - * @magentoDataFixture Magento/Customer/_files/customer.php - * @magentoDbIsolation enabled - */ - public function testCreateNonexistingCustomer() - { - $existingCustId = 1; - $existingCustomer = $this->customerRepository->getById($existingCustId); - - $email = 'savecustomer@example.com'; - $firstName = 'Firstsave'; - $lastName = 'Lastsave'; - $customerData = array_merge( - $existingCustomer->__toArray(), - [ - 'email' => $email, - 'firstname' => $firstName, - 'lastname' => $lastName, - 'id' => null - ] - ); - $customerEntity = $this->customerFactory->create(); - $this->dataObjectHelper->populateWithArray( - $customerEntity, - $customerData, - \Magento\Customer\Api\Data\CustomerInterface::class - ); - - $customerAfter = $this->accountManagement->createAccount($customerEntity, '_aPassword1'); - $this->assertGreaterThan(0, $customerAfter->getId()); - $this->assertEquals($email, $customerAfter->getEmail()); - $this->assertEquals($firstName, $customerAfter->getFirstname()); - $this->assertEquals($lastName, $customerAfter->getLastname()); - $this->accountManagement->authenticate( - $customerAfter->getEmail(), - '_aPassword1' - ); - $attributesBefore = $this->extensibleDataObjectConverter->toFlatArray( - $existingCustomer, - [], - \Magento\Customer\Api\Data\CustomerInterface::class - ); - $attributesAfter = $this->extensibleDataObjectConverter->toFlatArray( - $customerAfter, - [], - \Magento\Customer\Api\Data\CustomerInterface::class - ); - // ignore 'updated_at' - unset($attributesBefore['updated_at']); - unset($attributesAfter['updated_at']); - $inBeforeOnly = array_diff_assoc($attributesBefore, $attributesAfter); - $inAfterOnly = array_diff_assoc($attributesAfter, $attributesBefore); - $expectedInBefore = [ - 'email', - 'firstname', - 'id', - 'lastname', - ]; - sort($expectedInBefore); - $actualInBeforeOnly = array_keys($inBeforeOnly); - sort($actualInBeforeOnly); - $this->assertEquals($expectedInBefore, $actualInBeforeOnly); - $expectedInAfter = [ - 'created_in', - 'email', - 'firstname', - 'id', - 'lastname', - ]; - $actualInAfterOnly = array_keys($inAfterOnly); - foreach ($expectedInAfter as $item) { - $this->assertContains($item, $actualInAfterOnly); - } - } - - /** - * @magentoDbIsolation enabled - */ - public function testCreateCustomerInServiceVsInModel() - { - $email = 'email@example.com'; - $email2 = 'email2@example.com'; - $firstname = 'Tester'; - $lastname = 'McTest'; - $groupId = 1; - $password = '_aPassword1'; - - /** @var \Magento\Customer\Model\Customer $customerModel */ - $customerModel = $this->objectManager->create(\Magento\Customer\Model\CustomerFactory::class)->create(); - $customerModel->setEmail($email) - ->setFirstname($firstname) - ->setLastname($lastname) - ->setGroupId($groupId) - ->setPassword($password); - $customerModel->save(); - /** @var \Magento\Customer\Model\Customer $customerModel */ - $savedModel = $this->objectManager - ->create(\Magento\Customer\Model\CustomerFactory::class) - ->create() - ->load($customerModel->getId()); - $dataInModel = $savedModel->getData(); - - $newCustomerEntity = $this->customerFactory->create() - ->setEmail($email2) - ->setFirstname($firstname) - ->setLastname($lastname) - ->setGroupId($groupId); - $customerData = $this->accountManagement->createAccount($newCustomerEntity, $password); - $this->assertNotNull($customerData->getId()); - $savedCustomer = $this->customerRepository->getById($customerData->getId()); - - /** @var \Magento\Framework\Api\SimpleDataObjectConverter $simpleDataObjectConverter */ - $simpleDataObjectConverter = Bootstrap::getObjectManager() - ->get(\Magento\Framework\Api\SimpleDataObjectConverter::class); - - $dataInService = $simpleDataObjectConverter->toFlatArray( - $savedCustomer, - \Magento\Customer\Api\Data\CustomerInterface::class - ); - $expectedDifferences = [ - 'created_at', - 'updated_at', - 'email', - 'is_active', - 'entity_id', - 'entity_type_id', - 'password_hash', - 'attribute_set_id', - 'disable_auto_group_change', - 'confirmation', - 'reward_update_notification', - 'reward_warning_notification', - ]; - foreach ($dataInModel as $key => $value) { - if (!in_array($key, $expectedDifferences)) { - if ($value === null) { - $this->assertArrayNotHasKey($key, $dataInService); - } else { - if (isset($dataInService[$key])) { - $this->assertEquals($value, $dataInService[$key], 'Failed asserting value for ' . $key); - } - } - } - } - $this->assertEquals($email2, $dataInService['email']); - $this->assertArrayNotHasKey('is_active', $dataInService); - $this->assertArrayNotHasKey('password_hash', $dataInService); - } - - /** - * @magentoDbIsolation enabled - */ - public function testCreateNewCustomer() - { - $email = 'email@example.com'; - $storeId = 1; - $firstname = 'Tester'; - $lastname = 'McTest'; - $groupId = 1; - - $newCustomerEntity = $this->customerFactory->create() - ->setStoreId($storeId) - ->setEmail($email) - ->setFirstname($firstname) - ->setLastname($lastname) - ->setGroupId($groupId); - $savedCustomer = $this->accountManagement->createAccount($newCustomerEntity, '_aPassword1'); - $this->assertNotNull($savedCustomer->getId()); - $this->assertEquals($email, $savedCustomer->getEmail()); - $this->assertEquals($storeId, $savedCustomer->getStoreId()); - $this->assertEquals($firstname, $savedCustomer->getFirstname()); - $this->assertEquals($lastname, $savedCustomer->getLastname()); - $this->assertEquals($groupId, $savedCustomer->getGroupId()); - $this->assertTrue(!$savedCustomer->getSuffix()); - } - - /** - * @magentoDbIsolation enabled - */ - public function testCreateNewCustomerWithPasswordHash() - { - $email = 'email@example.com'; - $storeId = 1; - $firstname = 'Tester'; - $lastname = 'McTest'; - $groupId = 1; - - $newCustomerEntity = $this->customerFactory->create() - ->setStoreId($storeId) - ->setEmail($email) - ->setFirstname($firstname) - ->setLastname($lastname) - ->setGroupId($groupId); - /** @var \Magento\Framework\Math\Random $mathRandom */ - $password = $this->objectManager->get(\Magento\Framework\Math\Random::class)->getRandomString(8); - /** @var \Magento\Framework\Encryption\EncryptorInterface $encryptor */ - $encryptor = $this->objectManager->get(\Magento\Framework\Encryption\EncryptorInterface::class); - $passwordHash = $encryptor->getHash($password, true); - $savedCustomer = $this->accountManagement->createAccountWithPasswordHash( - $newCustomerEntity, - $passwordHash - ); - $this->assertNotNull($savedCustomer->getId()); - $this->assertEquals($email, $savedCustomer->getEmail()); - $this->assertEquals($storeId, $savedCustomer->getStoreId()); - $this->assertEquals($firstname, $savedCustomer->getFirstname()); - $this->assertEquals($lastname, $savedCustomer->getLastname()); - $this->assertEquals($groupId, $savedCustomer->getGroupId()); - $this->assertTrue(!$savedCustomer->getSuffix()); - $this->assertEquals( - $savedCustomer->getId(), - $this->accountManagement->authenticate($email, $password)->getId() - ); - } - - /** - * Customer has two addresses one of it is allowed in website and second is not - * - * @magentoDataFixture Magento/Customer/_files/customer.php - * @magentoDataFixture Magento/Customer/_files/customer_two_addresses.php - * @magentoDataFixture Magento/Store/_files/websites_different_countries.php - * @magentoConfigFixture fixture_second_store_store general/country/allow UA - * @return void - */ - public function testCreateNewCustomerWithPasswordHashWithNotAllowedCountry() - { - $customerId = 1; - $allowedCountryIdForSecondWebsite = 'UA'; - $store = $this->storeManager->getStore('fixture_second_store'); - $customerData = $this->customerRepository->getById($customerId); - $customerData->getAddresses()[1]->setRegion(null)->setCountryId($allowedCountryIdForSecondWebsite) - ->setRegionId(null); - $customerData->setStoreId($store->getId())->setWebsiteId($store->getWebsiteId())->setId(null); - $encryptor = $this->objectManager->get(\Magento\Framework\Encryption\EncryptorInterface::class); - /** @var \Magento\Framework\Math\Random $mathRandom */ - $password = $this->objectManager->get(\Magento\Framework\Math\Random::class)->getRandomString(8); - $passwordHash = $encryptor->getHash($password, true); - $savedCustomer = $this->accountManagement->createAccountWithPasswordHash( - $customerData, - $passwordHash - ); - $this->assertCount( - 1, - $savedCustomer->getAddresses(), - 'The wrong address quantity was saved' - ); - $this->assertSame( - 'UA', - $savedCustomer->getAddresses()[0]->getCountryId(), - 'The address with the disallowed country was saved' - ); - } - - /** - * @magentoAppArea frontend - * @magentoDataFixture Magento/Customer/_files/customer.php - */ - public function testCreateNewCustomerFromClone() - { - $email = 'savecustomer@example.com'; - $firstName = 'Firstsave'; - $lastname = 'Lastsave'; - - $existingCustId = 1; - $existingCustomer = $this->customerRepository->getById($existingCustId); - $customerEntity = $this->customerFactory->create(); - $this->dataObjectHelper->mergeDataObjects( - \Magento\Customer\Api\Data\CustomerInterface::class, - $customerEntity, - $existingCustomer - ); - $customerEntity->setEmail($email) - ->setFirstname($firstName) - ->setLastname($lastname) - ->setId(null); - - $customer = $this->accountManagement->createAccount($customerEntity, '_aPassword1'); - $this->assertNotEmpty($customer->getId()); - $this->assertEquals($email, $customer->getEmail()); - $this->assertEquals($firstName, $customer->getFirstname()); - $this->assertEquals($lastname, $customer->getLastname()); - $this->accountManagement->authenticate( - $customer->getEmail(), - '_aPassword1', - true - ); - } - /** * @magentoDataFixture Magento/Customer/_files/customer.php */ diff --git a/dev/tests/integration/testsuite/Magento/Customer/Model/Address/CreateAddressTest.php b/dev/tests/integration/testsuite/Magento/Customer/Model/Address/CreateAddressTest.php new file mode 100644 index 0000000000000..c6e1a9bbf8ac5 --- /dev/null +++ b/dev/tests/integration/testsuite/Magento/Customer/Model/Address/CreateAddressTest.php @@ -0,0 +1,364 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +declare(strict_types=1); + +namespace Magento\Customer\Model\Address; + +use Magento\Customer\Api\AddressRepositoryInterface; +use Magento\Customer\Api\CustomerRepositoryInterface; +use Magento\Customer\Api\Data\AddressInterface; +use Magento\Customer\Api\Data\AddressInterfaceFactory; +use Magento\Customer\Model\AddressRegistry; +use Magento\Customer\Model\CustomerRegistry; +use Magento\Customer\Model\ResourceModel\Address; +use Magento\Framework\Exception\InputException; +use Magento\TestFramework\Directory\Model\GetRegionIdByName; +use Magento\TestFramework\Helper\Bootstrap; +use Magento\TestFramework\ObjectManager; +use PHPUnit\Framework\TestCase; + +/** + * Assert that address was created as expected or address create throws expected error. + * + * @SuppressWarnings(PHPMD.CouplingBetweenObjects) + * + * @magentoDbIsolation enabled + */ +class CreateAddressTest extends TestCase +{ + /** + * Static customer address data. + */ + private const STATIC_CUSTOMER_ADDRESS_DATA = [ + AddressInterface::TELEPHONE => 3468676, + AddressInterface::POSTCODE => 75477, + AddressInterface::COUNTRY_ID => 'US', + 'custom_region_name' => 'Alabama', + AddressInterface::CITY => 'CityM', + AddressInterface::STREET => ['Green str, 67'], + AddressInterface::LASTNAME => 'Smith', + AddressInterface::FIRSTNAME => 'John', + ]; + + /** + * @var ObjectManager + */ + protected $objectManager; + + /** + * @var AddressInterfaceFactory + */ + protected $addressFactory; + + /** + * @var CustomerRegistry + */ + protected $customerRegistry; + + /** + * @var AddressRepositoryInterface + */ + protected $addressRepository; + + /** + * @var GetRegionIdByName + */ + protected $getRegionIdByName; + + /** + * @var CustomerRepositoryInterface + */ + protected $customerRepository; + + /** + * @var AddressRegistry + */ + private $addressRegistry; + + /** + * @var Address + */ + private $addressResource; + + /** + * @var int[] + */ + private $createdAddressesIds = []; + + /** + * @inheritdoc + */ + protected function setUp() + { + $this->objectManager = Bootstrap::getObjectManager(); + $this->addressFactory = $this->objectManager->get(AddressInterfaceFactory::class); + $this->customerRegistry = $this->objectManager->get(CustomerRegistry::class); + $this->addressRepository = $this->objectManager->get(AddressRepositoryInterface::class); + $this->getRegionIdByName = $this->objectManager->get(GetRegionIdByName::class); + $this->customerRepository = $this->objectManager->get(CustomerRepositoryInterface::class); + $this->addressRegistry = $this->objectManager->get(AddressRegistry::class); + $this->addressResource = $this->objectManager->get(Address::class); + parent::setUp(); + } + + /** + * @inheritdoc + */ + protected function tearDown() + { + foreach ($this->createdAddressesIds as $createdAddressesId) { + $this->addressRegistry->remove($createdAddressesId); + } + parent::tearDown(); + } + + /** + * Assert that default addresses properly created for customer. + * + * @magentoDataFixture Magento/Customer/_files/customer_no_address.php + * + * @dataProvider createDefaultAddressesDataProvider + * + * @param array $addressData + * @param bool $isShippingDefault + * @param bool $isBillingDefault + * @return void + */ + public function testCreateDefaultAddress( + array $addressData, + bool $isShippingDefault, + bool $isBillingDefault + ): void { + $customer = $this->customerRepository->get('customer5@example.com'); + $this->assertNull($customer->getDefaultShipping(), 'Customer already has default shipping address'); + $this->assertNull($customer->getDefaultBilling(), 'Customer already has default billing address'); + $address = $this->createAddress( + (int)$customer->getId(), + $addressData, + $isShippingDefault, + $isBillingDefault + ); + $expectedShipping = $isShippingDefault ? $address->getId() : null; + $expectedBilling = $isBillingDefault ? $address->getId() : null; + $customer = $this->customerRepository->get('customer5@example.com'); + $this->assertEquals($expectedShipping, $customer->getDefaultShipping()); + $this->assertEquals($expectedBilling, $customer->getDefaultBilling()); + } + + /** + * Data provider for create default or not default address. + * + * @return array + */ + public function createDefaultAddressesDataProvider(): array + { + return [ + 'any_addresses_are_default' => [self::STATIC_CUSTOMER_ADDRESS_DATA, false, false], + 'shipping_address_is_default' => [self::STATIC_CUSTOMER_ADDRESS_DATA, true, false], + 'billing_address_is_default' => [self::STATIC_CUSTOMER_ADDRESS_DATA, false, true], + 'all_addresses_are_default' => [self::STATIC_CUSTOMER_ADDRESS_DATA, true, true], + ]; + } + + /** + * Assert that address created successfully. + * + * @magentoDataFixture Magento/Customer/_files/customer_no_address.php + * + * @dataProvider createAddressesDataProvider + * + * @param array $addressData + * @param array $expectedData + * @return void + */ + public function testAddressCreatedWithProperData(array $addressData, array $expectedData): void + { + if (isset($expectedData['custom_region_name'])) { + $expectedData[AddressInterface::REGION_ID] = $this->getRegionIdByName->execute( + $expectedData['custom_region_name'], + $expectedData[AddressInterface::COUNTRY_ID] + ); + unset($expectedData['custom_region_name']); + } + $customer = $this->customerRepository->get('customer5@example.com'); + $createdAddressData = $this->createAddress((int)$customer->getId(), $addressData)->__toArray(); + foreach ($expectedData as $fieldCode => $expectedValue) { + $this->assertTrue(isset($createdAddressData[$fieldCode]), "Field $fieldCode wasn't found."); + $this->assertEquals($createdAddressData[$fieldCode], $expectedValue); + } + } + + /** + * Data provider for create address with proper data. + * + * @return array + */ + public function createAddressesDataProvider(): array + { + return [ + 'required_fields_valid_data' => [ + self::STATIC_CUSTOMER_ADDRESS_DATA, + [ + AddressInterface::TELEPHONE => 3468676, + AddressInterface::COUNTRY_ID => 'US', + AddressInterface::POSTCODE => 75477, + 'custom_region_name' => 'Alabama', + AddressInterface::FIRSTNAME => 'John', + AddressInterface::LASTNAME => 'Smith', + AddressInterface::STREET => ['Green str, 67'], + AddressInterface::CITY => 'CityM', + ], + ], + 'required_field_empty_postcode_for_uk' => [ + array_replace( + self::STATIC_CUSTOMER_ADDRESS_DATA, + [AddressInterface::POSTCODE => '', AddressInterface::COUNTRY_ID => 'GB'] + ), + [ + AddressInterface::COUNTRY_ID => 'GB', + AddressInterface::POSTCODE => null, + ], + ], + 'required_field_empty_region_id_for_ua' => [ + array_replace( + self::STATIC_CUSTOMER_ADDRESS_DATA, + [AddressInterface::REGION_ID => '', AddressInterface::COUNTRY_ID => 'UA'] + ), + [ + AddressInterface::COUNTRY_ID => 'UA', + AddressInterface::REGION => [ + 'region' => null, + 'region_code' => null, + 'region_id' => 0, + ], + ], + ], + 'required_field_street_as_array' => [ + array_replace(self::STATIC_CUSTOMER_ADDRESS_DATA, [AddressInterface::STREET => ['', 'Green str, 67']]), + [AddressInterface::STREET => ['Green str, 67']], + ], + 'field_name_prefix' => [ + array_merge(self::STATIC_CUSTOMER_ADDRESS_DATA, [AddressInterface::PREFIX => 'My prefix']), + [AddressInterface::PREFIX => 'My prefix'], + ], + 'field_middle_name_initial' => [ + array_merge(self::STATIC_CUSTOMER_ADDRESS_DATA, [AddressInterface::MIDDLENAME => 'My middle name']), + [AddressInterface::MIDDLENAME => 'My middle name'], + ], + 'field_name_suffix' => [ + array_merge(self::STATIC_CUSTOMER_ADDRESS_DATA, [AddressInterface::SUFFIX => 'My suffix']), + [AddressInterface::SUFFIX => 'My suffix'], + ], + 'field_company_name' => [ + array_merge(self::STATIC_CUSTOMER_ADDRESS_DATA, [AddressInterface::COMPANY => 'My company']), + [AddressInterface::COMPANY => 'My company'], + ], + 'field_vat_number' => [ + array_merge(self::STATIC_CUSTOMER_ADDRESS_DATA, [AddressInterface::VAT_ID => 'My VAT number']), + [AddressInterface::VAT_ID => 'My VAT number'], + ], + ]; + } + + /** + * Assert that proper error message has thrown if address creating with wrong data. + * + * @magentoDataFixture Magento/Customer/_files/customer_no_address.php + * + * @dataProvider createWrongAddressesDataProvider + * + * @param array $addressData + * @param \Exception $expectException + * @return void + */ + public function testExceptionThrownDuringCreateAddress(array $addressData, \Exception $expectException): void + { + $customer = $this->customerRepository->get('customer5@example.com'); + $this->expectExceptionObject($expectException); + $this->createAddress((int)$customer->getId(), $addressData); + } + + /** + * Data provider for create address with wrong data. + * + * @return array + */ + public function createWrongAddressesDataProvider(): array + { + return [ + 'required_field_empty_telephone' => [ + array_replace(self::STATIC_CUSTOMER_ADDRESS_DATA, [AddressInterface::TELEPHONE => '']), + InputException::requiredField('telephone'), + ], + 'required_field_empty_postcode_for_us' => [ + array_replace(self::STATIC_CUSTOMER_ADDRESS_DATA, [AddressInterface::POSTCODE => '']), + InputException::requiredField('postcode'), + ], +// TODO: Uncomment this variation after fix issue https://jira.corp.magento.com/browse/MC-31031 +// 'required_field_empty_region_id_for_us' => [ +// array_replace(self::STATIC_CUSTOMER_ADDRESS_DATA, [AddressInterface::REGION_ID => '']), +// InputException::requiredField('regionId'), +// ], + 'required_field_empty_firstname' => [ + array_replace(self::STATIC_CUSTOMER_ADDRESS_DATA, [AddressInterface::FIRSTNAME => '']), + InputException::requiredField('firstname'), + ], + 'required_field_empty_lastname' => [ + array_replace(self::STATIC_CUSTOMER_ADDRESS_DATA, [AddressInterface::LASTNAME => '']), + InputException::requiredField('lastname'), + ], + 'required_field_empty_street_as_array' => [ + array_replace(self::STATIC_CUSTOMER_ADDRESS_DATA, [AddressInterface::STREET => []]), + InputException::requiredField('street'), + ], + 'required_field_empty_city' => [ + array_replace(self::STATIC_CUSTOMER_ADDRESS_DATA, [AddressInterface::CITY => '']), + InputException::requiredField('city'), + ], +// TODO: Uncomment this variation after fix issue https://jira.corp.magento.com/browse/MC-31031 +// 'field_invalid_vat_number' => [ +// array_merge(self::STATIC_CUSTOMER_ADDRESS_DATA, [AddressInterface::VAT_ID => '/>.<*']), +// null// It need to create some error but currently magento doesn't has validation for this field., +// ], + ]; + } + + /** + * Create customer address with provided address data. + * + * @param int $customerId + * @param array $addressData + * @param bool $isDefaultShipping + * @param bool $isDefaultBilling + * @return AddressInterface + */ + protected function createAddress( + int $customerId, + array $addressData, + bool $isDefaultShipping = false, + bool $isDefaultBilling = false + ): AddressInterface { + if (isset($addressData['custom_region_name'])) { + $addressData[AddressInterface::REGION_ID] = $this->getRegionIdByName->execute( + $addressData['custom_region_name'], + $addressData[AddressInterface::COUNTRY_ID] + ); + unset($addressData['custom_region_name']); + } + + $addressData['attribute_set_id'] = $this->addressResource->getEntityType()->getDefaultAttributeSetId(); + $address = $this->addressFactory->create(['data' => $addressData]); + $address->setCustomerId($customerId); + $address->setIsDefaultShipping($isDefaultShipping); + $address->setIsDefaultBilling($isDefaultBilling); + $address = $this->addressRepository->save($address); + $this->customerRegistry->remove($customerId); + $this->addressRegistry->remove($address->getId()); + $this->createdAddressesIds[] = (int)$address->getId(); + + return $address; + } +} diff --git a/dev/tests/integration/testsuite/Magento/Customer/Model/Address/DeleteAddressTest.php b/dev/tests/integration/testsuite/Magento/Customer/Model/Address/DeleteAddressTest.php new file mode 100644 index 0000000000000..b303e8b0d1ca7 --- /dev/null +++ b/dev/tests/integration/testsuite/Magento/Customer/Model/Address/DeleteAddressTest.php @@ -0,0 +1,92 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +declare(strict_types=1); + +namespace Magento\Customer\Model\Address; + +use Magento\Customer\Api\AddressRepositoryInterface; +use Magento\Customer\Api\CustomerRepositoryInterface; +use Magento\Customer\Model\CustomerRegistry; +use Magento\Framework\Exception\NoSuchEntityException; +use Magento\TestFramework\Helper\Bootstrap; +use Magento\TestFramework\ObjectManager; +use PHPUnit\Framework\TestCase; + +/** + * Assert that address was deleted successfully. + * + * @magentoDbIsolation enabled + */ +class DeleteAddressTest extends TestCase +{ + /** + * @var ObjectManager + */ + private $objectManager; + + /** + * @var CustomerRegistry + */ + protected $customerRegistry; + + /** + * @var AddressRepositoryInterface + */ + protected $addressRepository; + + /** + * @var CustomerRepositoryInterface + */ + protected $customerRepository; + + /** + * @inheritdoc + */ + protected function setUp() + { + $this->objectManager = Bootstrap::getObjectManager(); + $this->customerRegistry = $this->objectManager->get(CustomerRegistry::class); + $this->addressRepository = $this->objectManager->get(AddressRepositoryInterface::class); + $this->customerRepository = $this->objectManager->get(CustomerRepositoryInterface::class); + parent::setUp(); + } + + /** + * Assert that address deleted successfully. + * + * @magentoDataFixture Magento/Customer/_files/customer.php + * @magentoDataFixture Magento/Customer/_files/customer_address.php + * + * @return void + */ + public function testDeleteDefaultAddress(): void + { + $customer = $this->customerRepository->get('customer@example.com'); + $this->assertEquals(1, $customer->getDefaultShipping()); + $this->assertEquals(1, $customer->getDefaultBilling()); + $customerAddresses = $customer->getAddresses() ?? []; + foreach ($customerAddresses as $address) { + $this->addressRepository->delete($address); + } + $this->customerRegistry->remove($customer->getId()); + $customer = $this->customerRepository->get('customer@example.com'); + $this->assertNull($customer->getDefaultShipping()); + $this->assertNull($customer->getDefaultBilling()); + } + + /** + * Assert that deleting non-existent address throws exception. + * + * @expectedException \Magento\Framework\Exception\NoSuchEntityException + * @expectedExceptionMessage No such entity with addressId = 1 + * + * @return void + */ + public function testDeleteMissingAddress(): void + { + $this->addressRepository->deleteById(1); + } +} diff --git a/dev/tests/integration/testsuite/Magento/Customer/Model/Address/UpdateAddressTest.php b/dev/tests/integration/testsuite/Magento/Customer/Model/Address/UpdateAddressTest.php new file mode 100644 index 0000000000000..902c9ae2407a7 --- /dev/null +++ b/dev/tests/integration/testsuite/Magento/Customer/Model/Address/UpdateAddressTest.php @@ -0,0 +1,294 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +declare(strict_types=1); + +namespace Magento\Customer\Model\Address; + +use Magento\Customer\Api\AddressRepositoryInterface; +use Magento\Customer\Api\CustomerRepositoryInterface; +use Magento\Customer\Api\Data\AddressInterface; +use Magento\Customer\Model\AddressRegistry; +use Magento\Customer\Model\CustomerRegistry; +use Magento\Customer\Model\ResourceModel\Address; +use Magento\Framework\Exception\InputException; +use Magento\TestFramework\Helper\Bootstrap; +use Magento\TestFramework\ObjectManager; +use PHPUnit\Framework\TestCase; + +/** + * Assert that address was updated as expected or address update throws expected error. + * + * @magentoDbIsolation enabled + */ +class UpdateAddressTest extends TestCase +{ + /** + * @var ObjectManager + */ + private $objectManager; + + /** + * @var AddressRegistry + */ + protected $addressRegistry; + + /** + * @var Address + */ + protected $addressResource; + + /** + * @var CustomerRegistry + */ + protected $customerRegistry; + + /** + * @var AddressRepositoryInterface + */ + protected $addressRepository; + + /** + * @var CustomerRepositoryInterface + */ + protected $customerRepository; + + /** + * @var int[] + */ + protected $processedAddressesIds = []; + + /** + * @inheritdoc + */ + protected function setUp() + { + $this->objectManager = Bootstrap::getObjectManager(); + $this->addressRegistry = $this->objectManager->get(AddressRegistry::class); + $this->addressResource = $this->objectManager->get(Address::class); + $this->customerRegistry = $this->objectManager->get(CustomerRegistry::class); + $this->addressRepository = $this->objectManager->get(AddressRepositoryInterface::class); + $this->customerRepository = $this->objectManager->get(CustomerRepositoryInterface::class); + parent::setUp(); + } + + /** + * @inheritdoc + */ + protected function tearDown() + { + foreach ($this->processedAddressesIds as $createdAddressesId) { + $this->addressRegistry->remove($createdAddressesId); + } + parent::tearDown(); + } + + /** + * Assert that default addresses properly updated for customer. + * + * @magentoDataFixture Magento/Customer/_files/customer.php + * @magentoDataFixture Magento/Customer/_files/customer_address.php + * + * @dataProvider updateAddressIsDefaultDataProvider + * + * @param bool $isShippingDefault + * @param bool $isBillingDefault + * @param int|null $expectedShipping + * @param int|null $expectedBilling + * @return void + */ + public function testUpdateAddressIsDefault( + bool $isShippingDefault, + bool $isBillingDefault, + ?int $expectedShipping, + ?int $expectedBilling + ): void { + $customer = $this->customerRepository->get('customer@example.com'); + $this->assertEquals(1, $customer->getDefaultShipping()); + $this->assertEquals(1, $customer->getDefaultBilling()); + $this->processedAddressesIds[] = 1; + $address = $this->addressRepository->getById(1); + $address->setIsDefaultShipping($isShippingDefault); + $address->setIsDefaultBilling($isBillingDefault); + $this->addressRepository->save($address); + $this->customerRegistry->remove(1); + $customer = $this->customerRepository->get('customer@example.com'); + $this->assertEquals($customer->getDefaultShipping(), $expectedShipping); + $this->assertEquals($customer->getDefaultBilling(), $expectedBilling); + } + + /** + * Data provider for update address as default billing or default shipping. + * + * @return array + */ + public function updateAddressIsDefaultDataProvider(): array + { + return [ + 'update_shipping_address_default' => [true, false, 1, null], + 'update_billing_address_default' => [false, true, null, 1], + ]; + } + + /** + * Assert that address updated successfully. + * + * @magentoDataFixture Magento/Customer/_files/customer.php + * @magentoDataFixture Magento/Customer/_files/customer_address.php + * + * @dataProvider updateAddressesDataProvider + * + * @param array $updateData + * @param array $expectedData + * @return void + */ + public function testUpdateAddress(array $updateData, array $expectedData): void + { + $this->processedAddressesIds[] = 1; + $address = $this->addressRepository->getById(1); + foreach ($updateData as $setFieldName => $setValue) { + $address->setData($setFieldName, $setValue); + } + $updatedAddressData = $this->addressRepository->save($address)->__toArray(); + foreach ($expectedData as $getFieldName => $getValue) { + $this->assertTrue(isset($updatedAddressData[$getFieldName]), "Field $getFieldName wasn't found."); + $this->assertEquals($getValue, $updatedAddressData[$getFieldName]); + } + } + + /** + * Data provider for update address with proper data. + * + * @return array + */ + public function updateAddressesDataProvider(): array + { + return [ + 'required_field_telephone' => [ + [AddressInterface::TELEPHONE => 251512979595], + [AddressInterface::TELEPHONE => 251512979595], + ], + 'required_field_postcode' => [ + [AddressInterface::POSTCODE => 55425], + [AddressInterface::POSTCODE => 55425], + ], + 'required_field_empty_postcode_for_uk' => [ + [AddressInterface::COUNTRY_ID => 'GB', AddressInterface::POSTCODE => ''], + [AddressInterface::COUNTRY_ID => 'GB', AddressInterface::POSTCODE => null], + ], + 'required_field_empty_region_id_for_ua' => [ + [AddressInterface::COUNTRY_ID => 'UA', AddressInterface::REGION_ID => ''], + [ + AddressInterface::COUNTRY_ID => 'UA', + AddressInterface::REGION_ID => 0, + ], + ], + 'required_field_firstname' => [ + [AddressInterface::FIRSTNAME => 'Test firstname'], + [AddressInterface::FIRSTNAME => 'Test firstname'], + ], + 'required_field_lastname' => [ + [AddressInterface::LASTNAME => 'Test lastname'], + [AddressInterface::LASTNAME => 'Test lastname'], + ], + 'required_field_street_as_array' => [ + [AddressInterface::STREET => ['', 'Test str, 55']], + [AddressInterface::STREET => ['Test str, 55']], + ], + 'required_field_city' => [ + [AddressInterface::CITY => 'Test city'], + [AddressInterface::CITY => 'Test city'], + ], + 'field_name_prefix' => [ + [AddressInterface::PREFIX => 'My prefix'], + [AddressInterface::PREFIX => 'My prefix'], + ], + 'field_middle_name_initial' => [ + [AddressInterface::MIDDLENAME => 'My middle name'], + [AddressInterface::MIDDLENAME => 'My middle name'], + ], + 'field_name_suffix' => [ + [AddressInterface::SUFFIX => 'My suffix'], + [AddressInterface::SUFFIX => 'My suffix'], + ], + 'field_company_name' => [ + [AddressInterface::COMPANY => 'My company'], + [AddressInterface::COMPANY => 'My company'], + ], + 'field_vat_number' => [ + [AddressInterface::VAT_ID => 'My VAT number'], + [AddressInterface::VAT_ID => 'My VAT number'], + ], + ]; + } + + /** + * Assert that error message has thrown during process address update. + * + * @magentoDataFixture Magento/Customer/_files/customer.php + * @magentoDataFixture Magento/Customer/_files/customer_address.php + * + * @dataProvider updateWrongAddressesDataProvider + * + * @param array $updateData + * @param \Exception $expectException + * @return void + */ + public function testExceptionThrownDuringUpdateAddress(array $updateData, \Exception $expectException): void + { + $this->processedAddressesIds[] = 1; + $address = $this->addressRepository->getById(1); + foreach ($updateData as $setFieldName => $setValue) { + $address->setData($setFieldName, $setValue); + } + $this->expectExceptionObject($expectException); + $this->addressRepository->save($address); + } + + /** + * Data provider for update address with proper data or with error. + * + * @return array + */ + public function updateWrongAddressesDataProvider(): array + { + return [ + 'required_field_empty_telephone' => [ + [AddressInterface::TELEPHONE => ''], + InputException::requiredField('telephone'), + ], + 'required_field_empty_postcode_for_us' => [ + [AddressInterface::POSTCODE => ''], + InputException::requiredField('postcode'), + ], +// TODO: Uncomment this variation after fix issue https://jira.corp.magento.com/browse/MC-31031 +// 'required_field_empty_region_id_for_us' => [ +// [AddressInterface::REGION_ID => ''], +// InputException::requiredField('regionId'), +// ], + 'required_field_empty_firstname' => [ + [AddressInterface::FIRSTNAME => ''], + InputException::requiredField('firstname'), + ], + 'required_field_empty_lastname' => [ + [AddressInterface::LASTNAME => ''], + InputException::requiredField('lastname'), + ], + 'required_field_empty_street_as_array' => [ + [AddressInterface::STREET => []], + InputException::requiredField('street'), + ], + 'required_field_empty_city' => [ + [AddressInterface::CITY => ''], + InputException::requiredField('city'), + ], +// TODO: Uncomment this variation after fix issue https://jira.corp.magento.com/browse/MC-31031 +// 'field_invalid_vat_number' => [ +// [AddressInterface::VAT_ID => '/>.<*'], +// null// It need to create some error but currently magento doesn't has validation for this field., +// ], + ]; + } +} diff --git a/dev/tests/integration/testsuite/Magento/Customer/Model/CustomerAddressAttributeTest.php b/dev/tests/integration/testsuite/Magento/Customer/Model/CustomerAddressAttributeTest.php new file mode 100644 index 0000000000000..866fd2c97207e --- /dev/null +++ b/dev/tests/integration/testsuite/Magento/Customer/Model/CustomerAddressAttributeTest.php @@ -0,0 +1,63 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +declare(strict_types=1); + +namespace Magento\Customer\Model; + +use Magento\Customer\Model\Metadata\AddressMetadata; +use Magento\Eav\Model\Config; +use Magento\Store\Model\StoreManagerInterface; +use Magento\TestFramework\Helper\Bootstrap; + +class CustomerAddressAttributeTest extends \PHPUnit\Framework\TestCase +{ + /** + * @var Config + */ + private $config; + + /** + * @var StoreManagerInterface + */ + private $storeManager; + + /** + * @inheritdoc + */ + protected function setUp() + { + $objectManager = Bootstrap::getObjectManager(); + $this->config = $objectManager->get(Config::class); + $this->storeManager = $objectManager->get(StoreManagerInterface::class); + } + + /** + * Tests cached scope_is_required attribute value for a certain website + * + * @return void + * @magentoDataFixture Magento/Store/_files/second_website_with_two_stores.php + */ + public function testGetScopeIsRequiredAttributeValueFromCache(): void + { + $attributeCode = 'telephone'; + $entityType = AddressMetadata::ENTITY_TYPE_ADDRESS; + $attribute = $this->config->getAttribute($entityType, $attributeCode); + $currentStore = $this->storeManager->getStore(); + $secondWebsite = $this->storeManager->getWebsite('test'); + $attribute->setWebsite($secondWebsite->getId()); + $attribute->setData('scope_is_required', '0'); + $attribute->save(); + $this->config->getAttribute($entityType, $attributeCode); + $this->storeManager->setCurrentStore('fixture_second_store'); + try { + $this->config->getEntityAttributes($attribute->getEntityTypeId(), $attribute); + $scopeAttribute = $this->config->getAttribute($entityType, $attributeCode); + $this->assertEquals(0, $scopeAttribute->getIsRequired()); + } finally { + $this->storeManager->setCurrentStore($currentStore); + } + } +} diff --git a/dev/tests/integration/testsuite/Magento/Customer/Model/Metadata/FormTest.php b/dev/tests/integration/testsuite/Magento/Customer/Model/Metadata/FormTest.php index 355167d809ba3..a510c6c4e6616 100644 --- a/dev/tests/integration/testsuite/Magento/Customer/Model/Metadata/FormTest.php +++ b/dev/tests/integration/testsuite/Magento/Customer/Model/Metadata/FormTest.php @@ -5,9 +5,11 @@ */ namespace Magento\Customer\Model\Metadata; +use Magento\Framework\App\RequestInterface; use Magento\TestFramework\Helper\Bootstrap; +use PHPUnit\Framework\TestCase; -class FormTest extends \PHPUnit\Framework\TestCase +class FormTest extends TestCase { /** * @var Form @@ -17,7 +19,7 @@ class FormTest extends \PHPUnit\Framework\TestCase /** @var array */ protected $_attributes; - /** @var \Magento\Framework\App\RequestInterface */ + /** @var RequestInterface */ protected $_request; /** @var array */ @@ -31,7 +33,7 @@ public function setUp() $objectManager = Bootstrap::getObjectManager(); /** @var FormFactory $formFactory */ - $formFactory = $objectManager->create(\Magento\Customer\Model\Metadata\FormFactory::class); + $formFactory = $objectManager->create(FormFactory::class); $this->_form = $formFactory->create('customer_address', 'customer_address_edit'); $this->_attributes = [ @@ -70,7 +72,7 @@ public function setUp() 'region_id' => 12, 'region' => 'California', ]; - $this->_request = $objectManager->get(\Magento\Framework\App\RequestInterface::class); + $this->_request = $objectManager->get(RequestInterface::class); $this->_request->setParams($requestData); $this->_expected = array_merge($this->_attributes, $requestData); @@ -105,10 +107,10 @@ public function testGetAttributes() 'suffix', 'company', 'street', - 'city', 'country_id', 'region', 'region_id', + 'city', 'postcode', 'telephone', 'fax', diff --git a/dev/tests/integration/testsuite/Magento/Customer/Model/ResourceModel/AddressRepositoryTest.php b/dev/tests/integration/testsuite/Magento/Customer/Model/ResourceModel/AddressRepositoryTest.php index 299d09d9400de..fb88a66423e65 100644 --- a/dev/tests/integration/testsuite/Magento/Customer/Model/ResourceModel/AddressRepositoryTest.php +++ b/dev/tests/integration/testsuite/Magento/Customer/Model/ResourceModel/AddressRepositoryTest.php @@ -40,6 +40,8 @@ class AddressRepositoryTest extends \PHPUnit\Framework\TestCase /** @var \Magento\Framework\Api\DataObjectHelper */ private $dataObjectHelper; + private $customerRegistry; + /** * Set up. */ @@ -56,6 +58,7 @@ protected function setUp() \Magento\Customer\Api\Data\AddressInterfaceFactory::class ); $this->dataObjectHelper = $this->objectManager->create(\Magento\Framework\Api\DataObjectHelper::class); + $this->customerRegistry = $this->objectManager->get(\Magento\Customer\Model\CustomerRegistry::class); $regionFactory = $this->objectManager->get(RegionInterfaceFactory::class); $region = $regionFactory->create() @@ -96,10 +99,7 @@ protected function setUp() */ protected function tearDown() { - $objectManager = \Magento\TestFramework\Helper\Bootstrap::getObjectManager(); - /** @var \Magento\Customer\Model\CustomerRegistry $customerRegistry */ - $customerRegistry = $objectManager->get(\Magento\Customer\Model\CustomerRegistry::class); - $customerRegistry->remove(1); + $this->customerRegistry->remove(1); } /** @@ -506,6 +506,60 @@ public function testSaveAddressWithRestrictedCountries() self::assertNotEmpty($saved->getId()); } + /** + * Test for saving address with extra spaces in phone. + * + * @magentoDataFixture Magento/Customer/_files/customer.php + * @magentoDataFixture Magento/Customer/_files/customer_address.php + */ + public function testSaveNewAddressWithExtraSpacesInPhone() + { + $proposedAddress = $this->_createSecondAddress() + ->setCustomerId(1) + ->setTelephone(' 123456 '); + $returnedAddress = $this->repository->save($proposedAddress); + $savedAddress = $this->repository->getById($returnedAddress->getId()); + $this->assertEquals('123456', $savedAddress->getTelephone()); + } + + /** + * Scenario for customer's default shipping and billing address saving and rollback. + * + * @magentoDataFixture Magento/Customer/_files/customer_without_addresses.php + */ + public function testCustomerAddressRelationSynchronisation() + { + /** + * Creating new address which is default shipping and billing for existing customer. + */ + $address = $this->expectedAddresses[0]; + $address->setId(null); + $address->setCustomerId(1); + $address->setIsDefaultShipping(true); + $address->setIsDefaultBilling(true); + $savedAddress = $this->repository->save($address); + + /** + * Customer registry should be updated with default shipping and billing addresses. + */ + $customer = $this->getCustomer('customer@example.com', 1); + $this->assertEquals($savedAddress->getId(), $customer->getDefaultShipping()); + $this->assertEquals($savedAddress->getId(), $customer->getDefaultBilling()); + + /** + * Registry should be clean up for reading data from DB. + */ + $this->repository->deleteById($savedAddress->getId()); + $this->customerRegistry->removeByEmail('customer@example.com'); + + /** + * Customer's default shipping and billing addresses should be updated. + */ + $customer = $this->getCustomer('customer@example.com', 1); + $this->assertNull($customer->getDefaultShipping()); + $this->assertNull($customer->getDefaultBilling()); + } + /** * Helper function that returns an Address Data Object that matches the data from customer_address fixture * @@ -571,20 +625,4 @@ private function getWebsite(string $code): WebsiteInterface $repository = $this->objectManager->get(WebsiteRepositoryInterface::class); return $repository->get($code); } - - /** - * Test for saving address with extra spaces in phone. - * - * @magentoDataFixture Magento/Customer/_files/customer.php - * @magentoDataFixture Magento/Customer/_files/customer_address.php - */ - public function testSaveNewAddressWithExtraSpacesInPhone() - { - $proposedAddress = $this->_createSecondAddress() - ->setCustomerId(1) - ->setTelephone(' 123456 '); - $returnedAddress = $this->repository->save($proposedAddress); - $savedAddress = $this->repository->getById($returnedAddress->getId()); - $this->assertEquals('123456', $savedAddress->getTelephone()); - } } diff --git a/dev/tests/integration/testsuite/Magento/Customer/Model/ResourceModel/CustomerRepository/CreateAddressTest.php b/dev/tests/integration/testsuite/Magento/Customer/Model/ResourceModel/CustomerRepository/CreateAddressTest.php new file mode 100644 index 0000000000000..a866910332b0c --- /dev/null +++ b/dev/tests/integration/testsuite/Magento/Customer/Model/ResourceModel/CustomerRepository/CreateAddressTest.php @@ -0,0 +1,69 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +declare(strict_types=1); + +namespace Magento\Customer\Model\ResourceModel\CustomerRepository; + +use Magento\Customer\Api\Data\AddressInterface; +use Magento\Customer\Model\Address\CreateAddressTest as CreateAddressViaAddressRepositoryTest; +use Magento\Framework\Api\DataObjectHelper; + +/** + * Test cases related to create customer address using customer repository. + * + * @magentoDbIsolation enabled + */ +class CreateAddressTest extends CreateAddressViaAddressRepositoryTest +{ + /** + * @var DataObjectHelper + */ + private $dataObjectHelper; + + /** + * @inheritdoc + */ + protected function setUp() + { + parent::setUp(); + $this->dataObjectHelper = $this->objectManager->get(DataObjectHelper::class); + } + + /** + * Create customer address with provided address data. + * + * @param int $customerId + * @param array $addressData + * @param bool $isDefaultShipping + * @param bool $isDefaultBilling + * @return AddressInterface + */ + protected function createAddress( + int $customerId, + array $addressData, + bool $isDefaultShipping = false, + bool $isDefaultBilling = false + ): AddressInterface { + if (isset($addressData['custom_region_name'])) { + $addressData[AddressInterface::REGION_ID] = $this->getRegionIdByName->execute( + $addressData['custom_region_name'], + $addressData[AddressInterface::COUNTRY_ID] + ); + unset($addressData['custom_region_name']); + } + $address = $this->addressFactory->create(); + $this->dataObjectHelper->populateWithArray($address, $addressData, AddressInterface::class); + $address->setIsDefaultShipping($isDefaultShipping); + $address->setIsDefaultBilling($isDefaultBilling); + $customer = $this->customerRepository->getById($customerId); + $customer->setAddresses([$address]); + $this->customerRepository->save($customer); + $addressId = (int)$address->getId(); + $this->customerRegistry->remove($customerId); + + return $this->addressRepository->getById($addressId); + } +} diff --git a/dev/tests/integration/testsuite/Magento/Customer/Model/ResourceModel/CustomerRepository/DeleteAddressTest.php b/dev/tests/integration/testsuite/Magento/Customer/Model/ResourceModel/CustomerRepository/DeleteAddressTest.php new file mode 100644 index 0000000000000..dd3adebc92cc2 --- /dev/null +++ b/dev/tests/integration/testsuite/Magento/Customer/Model/ResourceModel/CustomerRepository/DeleteAddressTest.php @@ -0,0 +1,42 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +declare(strict_types=1); + +namespace Magento\Customer\Model\ResourceModel\CustomerRepository; + +use Magento\Customer\Model\Address\DeleteAddressTest as DeleteAddressViaAddressRepositoryTest; +use Magento\Framework\Exception\NoSuchEntityException; + +/** + * Test cases related to delete customer address using customer repository. + * + * @magentoDbIsolation enabled + */ +class DeleteAddressTest extends DeleteAddressViaAddressRepositoryTest +{ + /** + * Assert that address deleted successfully. + * + * @magentoDataFixture Magento/Customer/_files/customer.php + * @magentoDataFixture Magento/Customer/_files/customer_address.php + * + * @return void + */ + public function testDeleteDefaultAddress(): void + { + $customer = $this->customerRepository->get('customer@example.com'); + $this->assertEquals(1, $customer->getDefaultShipping()); + $this->assertEquals(1, $customer->getDefaultBilling()); + $customer->setAddresses([]); + $this->customerRepository->save($customer); + $this->customerRegistry->remove($customer->getId()); + $customer = $this->customerRepository->get('customer@example.com'); + $this->assertNull($customer->getDefaultShipping()); + $this->assertNull($customer->getDefaultBilling()); + $this->expectExceptionObject(new NoSuchEntityException(__('No such entity with addressId = 1'))); + $this->addressRepository->getById(1); + } +} diff --git a/dev/tests/integration/testsuite/Magento/Customer/Model/ResourceModel/CustomerRepository/UpdateAddressTest.php b/dev/tests/integration/testsuite/Magento/Customer/Model/ResourceModel/CustomerRepository/UpdateAddressTest.php new file mode 100644 index 0000000000000..789c61bdbaf5c --- /dev/null +++ b/dev/tests/integration/testsuite/Magento/Customer/Model/ResourceModel/CustomerRepository/UpdateAddressTest.php @@ -0,0 +1,107 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +declare(strict_types=1); + +namespace Magento\Customer\Model\ResourceModel\CustomerRepository; + +use Magento\Customer\Model\Address\UpdateAddressTest as UpdateAddressViaAddressRepositoryTest; + +/** + * Test cases related to update customer address using customer repository. + * + * @magentoDbIsolation enabled + */ +class UpdateAddressTest extends UpdateAddressViaAddressRepositoryTest +{ + /** + * Assert that default addresses properly updated for customer. + * + * @magentoDataFixture Magento/Customer/_files/customer.php + * @magentoDataFixture Magento/Customer/_files/customer_address.php + * + * @dataProvider updateAddressIsDefaultDataProvider + * + * @param bool $isShippingDefault + * @param bool $isBillingDefault + * @param int|null $expectedShipping + * @param int|null $expectedBilling + * @return void + */ + public function testUpdateAddressIsDefault( + bool $isShippingDefault, + bool $isBillingDefault, + ?int $expectedShipping, + ?int $expectedBilling + ): void { + $customer = $this->customerRepository->get('customer@example.com'); + $this->assertEquals(1, $customer->getDefaultShipping()); + $this->assertEquals(1, $customer->getDefaultBilling()); + $this->processedAddressesIds[] = 1; + $address = $this->addressRepository->getById(1); + $address->setIsDefaultShipping($isShippingDefault); + $address->setIsDefaultBilling($isBillingDefault); + $customer->setAddresses([$address]); + $this->customerRepository->save($customer); + $this->customerRegistry->remove(1); + $customer = $this->customerRepository->get('customer@example.com'); + $this->assertEquals($customer->getDefaultShipping(), $expectedShipping); + $this->assertEquals($customer->getDefaultBilling(), $expectedBilling); + } + + /** + * Assert that address updated successfully. + * + * @magentoDataFixture Magento/Customer/_files/customer.php + * @magentoDataFixture Magento/Customer/_files/customer_address.php + * + * @dataProvider updateAddressesDataProvider + * + * @param array $updateData + * @param array $expectedData + * @return void + */ + public function testUpdateAddress(array $updateData, array $expectedData): void + { + $this->processedAddressesIds[] = 1; + $address = $this->addressRepository->getById(1); + foreach ($updateData as $setFieldName => $setValue) { + $address->setData($setFieldName, $setValue); + } + $customer = $this->customerRepository->get('customer@example.com'); + $customer->setAddresses([$address]); + $this->customerRepository->save($customer); + $updatedAddressData = $this->addressRepository->getById((int)$address->getId())->__toArray(); + foreach ($expectedData as $getFieldName => $getValue) { + $this->assertTrue(isset($updatedAddressData[$getFieldName]), "Field $getFieldName wasn't found."); + $this->assertEquals($getValue, $updatedAddressData[$getFieldName]); + } + } + + /** + * Assert that error message has thrown during process address update. + * + * @magentoDataFixture Magento/Customer/_files/customer.php + * @magentoDataFixture Magento/Customer/_files/customer_address.php + * + * @dataProvider updateWrongAddressesDataProvider + * + * @param array $updateData + * @param \Exception $expectException + * @return void + */ + public function testExceptionThrownDuringUpdateAddress(array $updateData, \Exception $expectException): void + { + $this->processedAddressesIds[] = 1; + $address = $this->addressRepository->getById(1); + $customer = $this->customerRepository->get('customer@example.com'); + foreach ($updateData as $setFieldName => $setValue) { + $address->setData($setFieldName, $setValue); + } + $customer->setAddresses([$address]); + $this->expectExceptionObject($expectException); + $this->customerRepository->save($customer); + } +} diff --git a/dev/tests/integration/testsuite/Magento/Customer/Model/SessionTest.php b/dev/tests/integration/testsuite/Magento/Customer/Model/SessionTest.php index 9497e93dd47b2..4b6d3e3019dc1 100644 --- a/dev/tests/integration/testsuite/Magento/Customer/Model/SessionTest.php +++ b/dev/tests/integration/testsuite/Magento/Customer/Model/SessionTest.php @@ -6,9 +6,12 @@ namespace Magento\Customer\Model; use Magento\Framework\App\PageCache\FormKey; +use Magento\Framework\App\ResponseInterface; +use Magento\Framework\Session\SidResolverInterface; use Magento\Framework\Stdlib\Cookie\CookieMetadataFactory; use Magento\Framework\Stdlib\Cookie\PublicCookieMetadata; use Magento\TestFramework\Helper\Bootstrap; +use Magento\Framework\App\Response\Http as HttpResponse; /** * @magentoDataFixture Magento/Customer/_files/customer.php @@ -29,6 +32,11 @@ class SessionTest extends \PHPUnit\Framework\TestCase /** @var PublicCookieMetadata $cookieMetadata */ protected $cookieMetadata; + /** + * @var HttpResponse + */ + private $response; + protected function setUp() { $this->_customerSession = Bootstrap::getObjectManager()->create( @@ -48,6 +56,7 @@ protected function setUp() 'form_key', $this->cookieMetadata ); + $this->response = Bootstrap::getObjectManager()->get(ResponseInterface::class); } public function testLoginById() @@ -100,4 +109,26 @@ public function testLogoutActionFlushesFormKey() $this->assertNotEquals($beforeKey, $afterKey); } + + /** + * Check that SID is not used in redirects. + * + * @return void + * @magentoConfigFixture current_store web/session/use_frontend_sid 1 + */ + public function testNoSid(): void + { + $this->_customerSession->authenticate(); + $location = (string)$this->response->getHeader('Location'); + $this->assertNotEmpty($location); + $this->assertNotContains(SidResolverInterface::SESSION_ID_QUERY_PARAM .'=', $location); + $beforeAuthUrl = $this->_customerSession->getData('before_auth_url'); + $this->assertNotEmpty($beforeAuthUrl); + $this->assertNotContains(SidResolverInterface::SESSION_ID_QUERY_PARAM .'=', $beforeAuthUrl); + + $this->_customerSession->authenticate('/customer/account'); + $location = (string)$this->response->getHeader('Location'); + $this->assertNotEmpty($location); + $this->assertNotContains(SidResolverInterface::SESSION_ID_QUERY_PARAM .'=', $location); + } } diff --git a/dev/tests/integration/testsuite/Magento/Customer/_files/customer_group_rollback.php b/dev/tests/integration/testsuite/Magento/Customer/_files/customer_group_rollback.php new file mode 100644 index 0000000000000..20a1f4623a1f7 --- /dev/null +++ b/dev/tests/integration/testsuite/Magento/Customer/_files/customer_group_rollback.php @@ -0,0 +1,30 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +declare(strict_types=1); + +use Magento\Framework\Api\SearchCriteriaBuilder; +use Magento\Customer\Api\GroupRepositoryInterface; +use Magento\TestFramework\Helper\Bootstrap; +use Magento\Framework\ObjectManagerInterface; +use Magento\Framework\Exception\NoSuchEntityException; +use Magento\Customer\Api\Data\GroupInterface; + +/** @var ObjectManagerInterface $objectManager */ +$objectManager = Bootstrap::getObjectManager(); +$groupRepository = $objectManager->get(GroupRepositoryInterface::class); +/** @var SearchCriteriaBuilder $searchBuilder */ +$searchBuilder = $objectManager->get(SearchCriteriaBuilder::class); +$searchCriteria = $searchBuilder->addFilter(GroupInterface::CODE, 'custom_group') + ->create(); +$groups = $groupRepository->getList($searchCriteria) + ->getItems(); +foreach ($groups as $group) { + try { + $groupRepository->delete($group); + } catch (NoSuchEntityException $exception) { + //Group already removed + } +} diff --git a/dev/tests/integration/testsuite/Magento/Customer/_files/customer_no_address_rollback.php b/dev/tests/integration/testsuite/Magento/Customer/_files/customer_no_address_rollback.php new file mode 100644 index 0000000000000..9909856322e13 --- /dev/null +++ b/dev/tests/integration/testsuite/Magento/Customer/_files/customer_no_address_rollback.php @@ -0,0 +1,30 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +declare(strict_types=1); + +use Magento\Customer\Api\CustomerRepositoryInterface; +use Magento\Customer\Model\CustomerRegistry; +use Magento\Framework\Exception\NoSuchEntityException; +use Magento\Framework\Registry; +use Magento\TestFramework\Helper\Bootstrap; + +$objectManager = Bootstrap::getObjectManager(); +/** @var Registry $registry */ +$registry = $objectManager->get(Registry::class); +/** @var CustomerRepositoryInterface $customerRepository */ +$customerRepository = $objectManager->get(CustomerRepositoryInterface::class); +/** @var CustomerRegistry $customerRegistry */ +$customerRegistry = $objectManager->get(CustomerRegistry::class); +$registry->unregister('isSecureArea'); +$registry->register('isSecureArea', true); +try { + $customerRegistry->remove(5); + $customerRepository->deleteById(5); +} catch (NoSuchEntityException $e) { + //Customer already deleted. +} +$registry->unregister('isSecureArea'); +$registry->register('isSecureArea', false); diff --git a/dev/tests/integration/testsuite/Magento/Customer/_files/customer_with_uk_address.php b/dev/tests/integration/testsuite/Magento/Customer/_files/customer_with_uk_address.php new file mode 100644 index 0000000000000..a7ad0bb82719f --- /dev/null +++ b/dev/tests/integration/testsuite/Magento/Customer/_files/customer_with_uk_address.php @@ -0,0 +1,78 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +declare(strict_types=1); + +use Magento\Customer\Api\AddressMetadataInterface; +use Magento\Customer\Api\AddressRepositoryInterface; +use Magento\Customer\Api\CustomerRepositoryInterface; +use Magento\Customer\Api\Data\AddressInterface; +use Magento\Customer\Model\Address; +use Magento\Customer\Model\AddressFactory; +use Magento\Customer\Model\CustomerFactory; +use Magento\Customer\Model\CustomerRegistry; +use Magento\Customer\Model\AddressRegistry; +use Magento\Store\Api\WebsiteRepositoryInterface; +use Magento\Store\Model\Website; +use Magento\Store\Model\WebsiteRepository; +use Magento\TestFramework\Helper\Bootstrap; + +$objectManager = Bootstrap::getObjectManager(); +/** @var CustomerRepositoryInterface $customerRepository */ +$customerRepository = $objectManager->create(CustomerRepositoryInterface::class); +/** @var CustomerFactory $customerFactory */ +$customerFactory = $objectManager->get(CustomerFactory::class); +$customer = $customerFactory->create(); +/** @var CustomerRegistry $customerRegistry */ +$customerRegistry = $objectManager->get(CustomerRegistry::class); +/** @var WebsiteRepository $websiteRepository */ +$websiteRepository = $objectManager->create(WebsiteRepositoryInterface::class); +/** @var Website $mainWebsite */ +$mainWebsite = $websiteRepository->get('base'); +$customer->setWebsiteId($mainWebsite->getId()) + ->setEmail('customer_uk_address@test.com') + ->setPassword('password') + ->setGroupId(1) + ->setStoreId($mainWebsite->getDefaultStore()->getId()) + ->setIsActive(1) + ->setPrefix('Mr.') + ->setFirstname('John') + ->setMiddlename('A') + ->setLastname('Smith') + ->setSuffix('Esq.') + ->setTaxvat('12') + ->setGender(0); +/** @var AddressFactory $customerAddressFactory */ +$customerAddressFactory = $objectManager->get(AddressFactory::class); +/** @var AddressRepositoryInterface $customerAddressRepository */ +$customerAddressRepository = $objectManager->create(AddressRepositoryInterface::class); +/** @var Address $customerAddress */ +$customerAddress = $customerAddressFactory->create(); +$customerAddress->isObjectNew(true); +$customerAddress->setData( + [ + 'attribute_set_id' => AddressMetadataInterface::ATTRIBUTE_SET_ID_ADDRESS, + AddressInterface::TELEPHONE => 3468676, + AddressInterface::POSTCODE => 'EC1A 1AA', + AddressInterface::COUNTRY_ID => 'GB', + AddressInterface::CITY => 'London', + AddressInterface::COMPANY => 'CompanyName', + AddressInterface::STREET => 'test street address', + AddressInterface::LASTNAME => 'Smith', + AddressInterface::FIRSTNAME => 'John', + AddressInterface::REGION_ID => 1, + ] +); +$customer->addAddress($customerAddress); +$customer->isObjectNew(true); +$customerDataModel = $customerRepository->save($customer->getDataModel()); +$addressId = $customerDataModel->getAddresses()[0]->getId(); +$customerDataModel->setDefaultShipping($addressId); +$customerDataModel->setDefaultBilling($addressId); +$customerRepository->save($customerDataModel); +$customerRegistry->remove($customerDataModel->getId()); +/** @var AddressRegistry $addressRegistry */ +$addressRegistry = $objectManager->get(AddressRegistry::class); +$addressRegistry->remove($customerAddress->getId()); diff --git a/dev/tests/integration/testsuite/Magento/Customer/_files/customer_with_uk_address_rollback.php b/dev/tests/integration/testsuite/Magento/Customer/_files/customer_with_uk_address_rollback.php new file mode 100644 index 0000000000000..e00d80833ea04 --- /dev/null +++ b/dev/tests/integration/testsuite/Magento/Customer/_files/customer_with_uk_address_rollback.php @@ -0,0 +1,30 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +declare(strict_types=1); + +use Magento\Customer\Api\AddressRepositoryInterface; +use Magento\Customer\Api\CustomerRepositoryInterface; +use Magento\Framework\Exception\NoSuchEntityException; +use Magento\Framework\Registry; +use Magento\TestFramework\Helper\Bootstrap; + +$objectManager = Bootstrap::getObjectManager(); +/** @var Registry $registry */ +$registry = $objectManager->get(Registry::class); +$registry->unregister('isSecureArea'); +$registry->register('isSecureArea', true); +/** @var CustomerRepositoryInterface $customerRepository */ +$customerRepository = $objectManager->get(CustomerRepositoryInterface::class); + +try { + $customer = $customerRepository->get('customer_uk_address@test.com'); + $customerRepository->delete($customer); +} catch (NoSuchEntityException $exception) { + //Already deleted +} + +$registry->unregister('isSecureArea'); +$registry->register('isSecureArea', false); diff --git a/dev/tests/integration/testsuite/Magento/Customer/_files/import_export/customers_with_websites.php b/dev/tests/integration/testsuite/Magento/Customer/_files/import_export/customers_with_websites.php new file mode 100644 index 0000000000000..3ee6530063dbd --- /dev/null +++ b/dev/tests/integration/testsuite/Magento/Customer/_files/import_export/customers_with_websites.php @@ -0,0 +1,18 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +declare(strict_types=1); + +use Magento\TestFramework\Helper\Bootstrap; +use Magento\Customer\Api\CustomerRepositoryInterface; + +require __DIR__ . '/customers.php'; +require __DIR__ . '/../../../Store/_files/website.php'; + +$objectManager = Bootstrap::getObjectManager(); +$repository = $objectManager->create(CustomerRepositoryInterface::class); +$customer = $repository->get('customer@example.com'); +$customer->setWebsiteId($website->getId()); +$repository->save($customer); diff --git a/dev/tests/integration/testsuite/Magento/Customer/_files/import_export/customers_with_websites_rollback.php b/dev/tests/integration/testsuite/Magento/Customer/_files/import_export/customers_with_websites_rollback.php new file mode 100644 index 0000000000000..09858172126f5 --- /dev/null +++ b/dev/tests/integration/testsuite/Magento/Customer/_files/import_export/customers_with_websites_rollback.php @@ -0,0 +1,9 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +declare(strict_types=1); + +include __DIR__ . '/customers_rollback.php'; +include __DIR__ . '/../../../Store/_files/website_rollback.php'; diff --git a/dev/tests/integration/testsuite/Magento/Customer/_files/new_customer.php b/dev/tests/integration/testsuite/Magento/Customer/_files/new_customer.php new file mode 100644 index 0000000000000..2e298a7e508a3 --- /dev/null +++ b/dev/tests/integration/testsuite/Magento/Customer/_files/new_customer.php @@ -0,0 +1,44 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +declare(strict_types=1); + +use Magento\Customer\Api\AccountManagementInterface; +use Magento\Customer\Api\CustomerMetadataInterface; +use Magento\Customer\Model\Data\CustomerFactory; +use Magento\Customer\Model\GroupManagement; +use Magento\Eav\Model\AttributeRepository; +use Magento\Store\Model\StoreManagerInterface; +use Magento\TestFramework\Helper\Bootstrap; + +$objectManager = Bootstrap::getObjectManager(); +/** @var AccountManagementInterface $accountManagement */ +$accountManagement = $objectManager->get(AccountManagementInterface::class); +$customerFactory = $objectManager->get(CustomerFactory::class); +$customerFactory->create(); +/** @var StoreManagerInterface $storeManager */ +$storeManager = $objectManager->get(StoreManagerInterface::class); +$website = $storeManager->getWebsite('base'); +/** @var GroupManagement $groupManagement */ +$groupManagement = $objectManager->get(GroupManagement::class); +$defaultStoreId = $website->getDefaultStore()->getId(); +/** @var AttributeRepository $attributeRepository */ +$attributeRepository = $objectManager->get(AttributeRepository::class); +$gender = $attributeRepository->get(CustomerMetadataInterface::ENTITY_TYPE_CUSTOMER, 'gender') + ->getSource()->getOptionId('Male'); +$customer = $customerFactory->create(); +$customer->setWebsiteId($website->getId()) + ->setEmail('new_customer@example.com') + ->setGroupId($groupManagement->getDefaultGroup($defaultStoreId)->getId()) + ->setStoreId($defaultStoreId) + ->setPrefix('Mr.') + ->setFirstname('John') + ->setMiddlename('A') + ->setLastname('Smith') + ->setSuffix('Esq.') + ->setDefaultBilling(1) + ->setDefaultShipping(1) + ->setGender($gender); +$accountManagement->createAccount($customer, 'Qwert12345'); diff --git a/dev/tests/integration/testsuite/Magento/Customer/_files/new_customer_rollback.php b/dev/tests/integration/testsuite/Magento/Customer/_files/new_customer_rollback.php new file mode 100644 index 0000000000000..48da309f6878f --- /dev/null +++ b/dev/tests/integration/testsuite/Magento/Customer/_files/new_customer_rollback.php @@ -0,0 +1,33 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +declare(strict_types=1); + +use Magento\Customer\Api\CustomerRepositoryInterface; +use Magento\Framework\Exception\NoSuchEntityException; +use Magento\Framework\Registry; +use Magento\Store\Api\WebsiteRepositoryInterface; +use Magento\TestFramework\Helper\Bootstrap; + +$objectManager = Bootstrap::getObjectManager(); +/** @var Registry $registry */ +$registry = $objectManager->get(Registry::class); +/** @var CustomerRepositoryInterface $customerRepository */ +$customerRepository = $objectManager->get(CustomerRepositoryInterface::class); +/** @var WebsiteRepositoryInterface $websiteRepository */ +$websiteRepository = $objectManager->get(WebsiteRepositoryInterface::class); +$websiteId = $websiteRepository->get('base')->getId(); +$registry->unregister('isSecureArea'); +$registry->register('isSecureArea', true); + +try { + $customer = $customerRepository->get('new_customer@example.com', $websiteId); + $customerRepository->delete($customer); +} catch (NoSuchEntityException $e) { + //customer already deleted +} + +$registry->unregister('isSecureArea'); +$registry->register('isSecureArea', false); diff --git a/dev/tests/integration/testsuite/Magento/Customer/_files/unconfirmed_customer.php b/dev/tests/integration/testsuite/Magento/Customer/_files/unconfirmed_customer.php new file mode 100644 index 0000000000000..e27010dc042ca --- /dev/null +++ b/dev/tests/integration/testsuite/Magento/Customer/_files/unconfirmed_customer.php @@ -0,0 +1,47 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +declare(strict_types=1); + +use Magento\Customer\Api\AccountManagementInterface; +use Magento\Customer\Api\CustomerMetadataInterface; +use Magento\Customer\Model\Data\CustomerFactory; +use Magento\Eav\Model\AttributeRepository; +use Magento\Framework\Math\Random; +use Magento\Store\Api\WebsiteRepositoryInterface; +use Magento\TestFramework\Helper\Bootstrap; + +$objectManager = Bootstrap::getObjectManager(); +/** @var AccountManagementInterface $accountManagment */ +$accountManagment = $objectManager->get(AccountManagementInterface::class); +/** @var CustomerFactory $customerFactory */ +$customerFactory = $objectManager->get(CustomerFactory::class); +/** @var Random $random */ +$random = $objectManager->get(Random::class); +/** @var WebsiteRepositoryInterface $websiteRepository */ +$websiteRepository = $objectManager->get(WebsiteRepositoryInterface::class); +$customer = $customerFactory->create(); +$website = $websiteRepository->get('base'); +$defaultStoreId = $website->getDefaultStore()->getId(); +/** @var AttributeRepository $attributeRepository */ +$attributeRepository = $objectManager->get(AttributeRepository::class); +$gender = $attributeRepository->get(CustomerMetadataInterface::ENTITY_TYPE_CUSTOMER, 'gender') + ->getSource()->getOptionId('Male'); + +$customer->setWebsiteId($website->getId()) + ->setEmail('unconfirmedcustomer@example.com') + ->setGroupId(1) + ->setStoreId($defaultStoreId) + ->setPrefix('Mr.') + ->setFirstname('John') + ->setMiddlename('A') + ->setLastname('Smith') + ->setSuffix('Esq.') + ->setDefaultBilling(1) + ->setDefaultShipping(1) + ->setConfirmation($random->getUniqueHash()) + ->setGender($gender); + +$accountManagment->createAccount($customer, 'Qwert12345'); diff --git a/dev/tests/integration/testsuite/Magento/Customer/_files/unconfirmed_customer_rollback.php b/dev/tests/integration/testsuite/Magento/Customer/_files/unconfirmed_customer_rollback.php new file mode 100644 index 0000000000000..cbe30d4d24fcb --- /dev/null +++ b/dev/tests/integration/testsuite/Magento/Customer/_files/unconfirmed_customer_rollback.php @@ -0,0 +1,33 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +declare(strict_types=1); + +use Magento\Customer\Api\CustomerRepositoryInterface; +use Magento\Framework\Exception\NoSuchEntityException; +use Magento\Framework\Registry; +use Magento\Store\Api\WebsiteRepositoryInterface; +use Magento\TestFramework\Helper\Bootstrap; + +$objectManager = Bootstrap::getObjectManager(); +/** @var Registry $registry */ +$registry = $objectManager->get(Registry::class); +/** @var CustomerRepositoryInterface $customerRepository */ +$customerRepository = $objectManager->get(CustomerRepositoryInterface::class); +/** @var WebsiteRepositoryInterface $websiteRepository */ +$websiteRepository = $objectManager->get(WebsiteRepositoryInterface::class); +$websiteId = $websiteRepository->get('base')->getId(); +$registry->unregister('isSecureArea'); +$registry->register('isSecureArea', true); + +try { + $customer = $customerRepository->get('unconfirmedcustomer@example.com', $websiteId); + $customerRepository->delete($customer); +} catch (NoSuchEntityException $e) { + //customer already deleted +} + +$registry->unregister('isSecureArea'); +$registry->register('isSecureArea', false); diff --git a/dev/tests/integration/testsuite/Magento/CustomerImportExport/Model/Export/CustomerTest.php b/dev/tests/integration/testsuite/Magento/CustomerImportExport/Model/Export/CustomerTest.php index 884a4a38ebe0f..9a178aafb0aee 100644 --- a/dev/tests/integration/testsuite/Magento/CustomerImportExport/Model/Export/CustomerTest.php +++ b/dev/tests/integration/testsuite/Magento/CustomerImportExport/Model/Export/CustomerTest.php @@ -75,6 +75,20 @@ public function testExport() $this->checkExportData($lines, $expectedAttributes); } + /** + * Export with Multi Websites "Customer Main File". + * + * @magentoDataFixture Magento/Customer/_files/import_export/customers_with_websites.php + * @return void + */ + public function testExportWithMultiWebsites(): void + { + $this->processCustomerAttribute(); + $expectedAttributes = $this->getExpectedAttributes(); + $lines = $this->export($expectedAttributes); + $this->checkExportData($lines, $expectedAttributes); + } + /** * Return attributes which should be exported. * diff --git a/dev/tests/integration/testsuite/Magento/CustomerImportExport/Model/Import/AddressTest.php b/dev/tests/integration/testsuite/Magento/CustomerImportExport/Model/Import/AddressTest.php index 96b7f8ce8ed67..e83b144e395b2 100644 --- a/dev/tests/integration/testsuite/Magento/CustomerImportExport/Model/Import/AddressTest.php +++ b/dev/tests/integration/testsuite/Magento/CustomerImportExport/Model/Import/AddressTest.php @@ -14,6 +14,7 @@ use Magento\ImportExport\Model\Import as ImportModel; use Magento\ImportExport\Model\Import\Adapter as ImportAdapter; use Magento\TestFramework\Helper\Bootstrap; +use Magento\Framework\Indexer\StateInterface; /** * @SuppressWarnings(PHPMD.CouplingBetweenObjects) @@ -84,6 +85,11 @@ class AddressTest extends \PHPUnit\Framework\TestCase /** @var \Magento\Customer\Model\ResourceModel\Customer */ protected $customerResource; + /** + * @var \Magento\Customer\Model\Indexer\Processor + */ + private $indexerProcessor; + /** * Init new instance of address entity adapter */ @@ -96,6 +102,9 @@ protected function setUp() $this->_entityAdapter = Bootstrap::getObjectManager()->create( $this->_testClassName ); + $this->indexerProcessor = Bootstrap::getObjectManager()->create( + \Magento\Customer\Model\Indexer\Processor::class + ); } /** @@ -353,6 +362,7 @@ public function testImportDataAddUpdate() $requiredAttributes[] = $keyAttribute; foreach (['update', 'remove'] as $action) { foreach ($this->_updateData[$action] as $attributes) { + // phpcs:ignore Magento2.Performance.ForeachArrayMerge $requiredAttributes = array_merge($requiredAttributes, array_keys($attributes)); } } @@ -494,4 +504,29 @@ public function testDifferentOptions(): void $imported = $this->_entityAdapter->importData(); $this->assertTrue($imported, 'Must be successfully imported'); } + + /** + * Test customer indexer gets invalidated after import when Update on Schedule mode is set + * + * @magentoDbIsolation enabled + */ + public function testCustomerIndexer(): void + { + $file = __DIR__ . '/_files/address_import_update.csv'; + $filesystem = Bootstrap::getObjectManager()->create(Filesystem::class); + $directoryWrite = $filesystem->getDirectoryWrite(DirectoryList::ROOT); + $source = new \Magento\ImportExport\Model\Import\Source\Csv($file, $directoryWrite); + $this->_entityAdapter + ->setParameters(['behavior' => ImportModel::BEHAVIOR_ADD_UPDATE]) + ->setSource($source) + ->validateData() + ->hasToBeTerminated(); + $this->indexerProcessor->getIndexer()->reindexAll(); + $statusBeforeImport = $this->indexerProcessor->getIndexer()->getStatus(); + $this->indexerProcessor->getIndexer()->setScheduled(true); + $this->_entityAdapter->importData(); + $statusAfterImport = $this->indexerProcessor->getIndexer()->getStatus(); + $this->assertEquals(StateInterface::STATUS_VALID, $statusBeforeImport); + $this->assertEquals(StateInterface::STATUS_INVALID, $statusAfterImport); + } } diff --git a/dev/tests/integration/testsuite/Magento/CustomerImportExport/Model/Import/CustomerTest.php b/dev/tests/integration/testsuite/Magento/CustomerImportExport/Model/Import/CustomerTest.php index 77ceae27e0774..89c84d7c18068 100644 --- a/dev/tests/integration/testsuite/Magento/CustomerImportExport/Model/Import/CustomerTest.php +++ b/dev/tests/integration/testsuite/Magento/CustomerImportExport/Model/Import/CustomerTest.php @@ -6,11 +6,18 @@ namespace Magento\CustomerImportExport\Model\Import; +use Magento\Customer\Api\CustomerRepositoryInterface; +use Magento\Customer\Api\Data\CustomerInterface; use Magento\Framework\App\Filesystem\DirectoryList; +use Magento\Framework\Exception\NoSuchEntityException; use Magento\ImportExport\Model\Import; +use Magento\Framework\Indexer\StateInterface; /** * Test for class \Magento\CustomerImportExport\Model\Import\Customer which covers validation logic + * + * @SuppressWarnings(PHPMD.CouplingBetweenObjects) + * @SuppressWarnings(PHPMD.TooManyPublicMethods) */ class CustomerTest extends \PHPUnit\Framework\TestCase { @@ -33,6 +40,11 @@ class CustomerTest extends \PHPUnit\Framework\TestCase */ protected $directoryWrite; + /** + * @var \Magento\Customer\Model\Indexer\Processor + */ + private $indexerProcessor; + /** * Create all necessary data for tests */ @@ -43,6 +55,8 @@ protected function setUp() $this->_model = \Magento\TestFramework\Helper\Bootstrap::getObjectManager() ->create(\Magento\CustomerImportExport\Model\Import\Customer::class); $this->_model->setParameters(['behavior' => Import::BEHAVIOR_ADD_UPDATE]); + $this->indexerProcessor = \Magento\TestFramework\Helper\Bootstrap::getObjectManager() + ->create(\Magento\Customer\Model\Indexer\Processor::class); $propertyAccessor = new \ReflectionProperty($this->_model, 'errorMessageTemplates'); $propertyAccessor->setAccessible(true); @@ -82,6 +96,8 @@ public function testImportData() $this->directoryWrite ); + $existingCustomer = $this->getCustomer('CharlesTAlston@teleworm.us', 1); + /** @var $customersCollection \Magento\Customer\Model\ResourceModel\Customer\Collection */ $customersCollection = \Magento\TestFramework\Helper\Bootstrap::getObjectManager()->create( \Magento\Customer\Model\ResourceModel\Customer\Collection::class @@ -107,13 +123,6 @@ public function testImportData() $this->assertEquals($expectAddedCustomers, $addedCustomers, 'Added unexpected amount of customers'); - /** @var $objectManager \Magento\TestFramework\ObjectManager */ - $objectManager = \Magento\TestFramework\Helper\Bootstrap::getObjectManager(); - - $existingCustomer = $objectManager->get( - \Magento\Framework\Registry::class - )->registry('_fixture/Magento_ImportExport_Customer'); - $updatedCustomer = $customers[$existingCustomer->getId()]; $this->assertNotEquals( @@ -154,6 +163,12 @@ public function testImportDataWithOneAdditionalColumn(): void $this->directoryWrite ); + $existingCustomer = \Magento\TestFramework\Helper\Bootstrap::getObjectManager()->create( + \Magento\Customer\Model\Customer::class + ); + $existingCustomer->setWebsiteId(1); + $existingCustomer = $existingCustomer->loadByEmail('CharlesTAlston@teleworm.us'); + /** @var $customersCollection \Magento\Customer\Model\ResourceModel\Customer\Collection */ $customersCollection = \Magento\TestFramework\Helper\Bootstrap::getObjectManager()->create( \Magento\Customer\Model\ResourceModel\Customer\Collection::class @@ -171,12 +186,6 @@ public function testImportDataWithOneAdditionalColumn(): void $customers = $customersCollection->getItems(); - /** @var $objectManager \Magento\TestFramework\ObjectManager */ - $objectManager = \Magento\TestFramework\Helper\Bootstrap::getObjectManager(); - - $existingCustomer = $objectManager->get(\Magento\Framework\Registry::class) - ->registry('_fixture/Magento_ImportExport_Customer'); - $updatedCustomer = $customers[$existingCustomer->getId()]; $this->assertNotEquals( @@ -210,8 +219,8 @@ public function testImportDataWithOneAdditionalColumn(): void ); $this->assertEquals( - $existingCustomer->getCustomerGroupId(), - $updatedCustomer->getCustomerGroupId(), + $existingCustomer->getGroupId(), + $updatedCustomer->getGroupId(), 'Customer group must not be changed' ); } @@ -352,4 +361,78 @@ public function testValidateEmailForDeleteBehavior() $this->_model->getErrorAggregator()->getErrorsByCode([Customer::ERROR_CUSTOMER_NOT_FOUND]) ); } + + /** + * Test import existing customers + * + * @magentoDataFixture Magento/Customer/_files/import_export/customers.php + * @return void + */ + public function testUpdateExistingCustomers(): void + { + $this->doImport(__DIR__ . '/_files/customers_to_update.csv', Import::BEHAVIOR_ADD_UPDATE); + $customer = $this->getCustomer('customer@example.com', 1); + $this->assertEquals('Firstname-updated', $customer->getFirstname()); + $this->assertEquals('Lastname-updated', $customer->getLastname()); + $this->assertEquals(1, $customer->getStoreId()); + $customer = $this->getCustomer('julie.worrell@example.com', 1); + $this->assertEquals('Julie-updated', $customer->getFirstname()); + $this->assertEquals('Worrell-updated', $customer->getLastname()); + $this->assertEquals(1, $customer->getStoreId()); + $customer = $this->getCustomer('david.lamar@example.com', 1); + $this->assertEquals('David-updated', $customer->getFirstname()); + $this->assertEquals('Lamar-updated', $customer->getLastname()); + $this->assertEquals(1, $customer->getStoreId()); + } + + /** + * Test customer indexer gets invalidated after import when Update on Schedule mode is set + * + * @magentoDbIsolation enabled + * @return void + */ + public function testCustomerIndexer(): void + { + $this->indexerProcessor->getIndexer()->reindexAll(); + $statusBeforeImport = $this->indexerProcessor->getIndexer()->getStatus(); + $this->indexerProcessor->getIndexer()->setScheduled(true); + $this->doImport(__DIR__ . '/_files/customers_with_gender_to_import.csv', Import::BEHAVIOR_ADD_UPDATE); + $statusAfterImport = $this->indexerProcessor->getIndexer()->getStatus(); + $this->assertEquals(StateInterface::STATUS_VALID, $statusBeforeImport); + $this->assertEquals(StateInterface::STATUS_INVALID, $statusAfterImport); + } + + /** + * Gets customer entity. + * + * @param string $email + * @param int $websiteId + * @return CustomerInterface + * @throws NoSuchEntityException + * @throws \Magento\Framework\Exception\LocalizedException + */ + private function getCustomer(string $email, int $websiteId): CustomerInterface + { + $objectManager = \Magento\TestFramework\Helper\Bootstrap::getObjectManager(); + /** @var CustomerRepositoryInterface $repository */ + $repository = $objectManager->get(CustomerRepositoryInterface::class); + return $repository->get($email, $websiteId); + } + + /** + * Import using given file and behavior + * + * @param string $file + * @param string $behavior + */ + private function doImport(string $file, string $behavior): void + { + $source = new \Magento\ImportExport\Model\Import\Source\Csv($file, $this->directoryWrite); + $this->_model + ->setParameters(['behavior' => $behavior]) + ->setSource($source) + ->validateData() + ->hasToBeTerminated(); + $this->_model->importData(); + } } diff --git a/dev/tests/integration/testsuite/Magento/CustomerImportExport/Model/Import/_files/customers_to_update.csv b/dev/tests/integration/testsuite/Magento/CustomerImportExport/Model/Import/_files/customers_to_update.csv new file mode 100644 index 0000000000000..b6d8e24860ed3 --- /dev/null +++ b/dev/tests/integration/testsuite/Magento/CustomerImportExport/Model/Import/_files/customers_to_update.csv @@ -0,0 +1,4 @@ +email,_website,_store,confirmation,created_at,created_in,default_billing,default_shipping,disable_auto_group_change,dob,firstname,gender,group_id,lastname,middlename,password_hash,prefix,rp_token,rp_token_created_at,store_id,suffix,taxvat,website_id,password +customer@example.com,base,"default",,5/6/2012 16:15,Admin,"1","1",0,,"Firstname-updated",Male,1,"Lastname-updated",T.,145d12bfff8a6a279eb61e277e3d727c0ba95acc1131237f1594ddbb7687a564:l1,,,,0,,,"1", +julie.worrell@example.com,base,"",,5/6/2012 16:19,Admin,"1","1",0,,"Julie-updated",Female,1,"Worrell-updated",T.,145d12bfff8a6a279eb61e277e3d727c0ba95acc1131237f1594ddbb7687a564:l1,,,,0,,,"1", +david.lamar@example.com,base,"",,5/6/2012 16:25,Admin,"1","1",0,,"David-updated",Male,1,"Lamar-updated",T.,145d12bfff8a6a279eb61e277e3d727c0ba95acc1131237f1594ddbb7687a564:l1,,,,0,,,"1", diff --git a/dev/tests/integration/testsuite/Magento/Deploy/_files/zoom1/registration.php b/dev/tests/integration/testsuite/Magento/Deploy/_files/zoom1/registration.php index 52d41c2a2ebb3..21c05427ed6f5 100644 --- a/dev/tests/integration/testsuite/Magento/Deploy/_files/zoom1/registration.php +++ b/dev/tests/integration/testsuite/Magento/Deploy/_files/zoom1/registration.php @@ -4,6 +4,6 @@ * See COPYING.txt for license details. */ -use \Magento\Framework\Component\ComponentRegistrar; +use Magento\Framework\Component\ComponentRegistrar; ComponentRegistrar::register(ComponentRegistrar::THEME, 'frontend/Magento/zoom1', __DIR__); diff --git a/dev/tests/integration/testsuite/Magento/Deploy/_files/zoom2/registration.php b/dev/tests/integration/testsuite/Magento/Deploy/_files/zoom2/registration.php index a6979a1cae451..b40bf194aee19 100644 --- a/dev/tests/integration/testsuite/Magento/Deploy/_files/zoom2/registration.php +++ b/dev/tests/integration/testsuite/Magento/Deploy/_files/zoom2/registration.php @@ -4,6 +4,6 @@ * See COPYING.txt for license details. */ -use \Magento\Framework\Component\ComponentRegistrar; +use Magento\Framework\Component\ComponentRegistrar; ComponentRegistrar::register(ComponentRegistrar::THEME, 'frontend/Magento/zoom2', __DIR__); diff --git a/dev/tests/integration/testsuite/Magento/Deploy/_files/zoom3/registration.php b/dev/tests/integration/testsuite/Magento/Deploy/_files/zoom3/registration.php index fc0867f7d0107..bd6adc89add51 100644 --- a/dev/tests/integration/testsuite/Magento/Deploy/_files/zoom3/registration.php +++ b/dev/tests/integration/testsuite/Magento/Deploy/_files/zoom3/registration.php @@ -4,6 +4,6 @@ * See COPYING.txt for license details. */ -use \Magento\Framework\Component\ComponentRegistrar; +use Magento\Framework\Component\ComponentRegistrar; ComponentRegistrar::register(ComponentRegistrar::THEME, 'frontend/Magento/zoom3', __DIR__); diff --git a/dev/tests/integration/testsuite/Magento/Developer/Helper/DataTest.php b/dev/tests/integration/testsuite/Magento/Developer/Helper/DataTest.php index f62773a68c144..e2c98eb4aef5f 100644 --- a/dev/tests/integration/testsuite/Magento/Developer/Helper/DataTest.php +++ b/dev/tests/integration/testsuite/Magento/Developer/Helper/DataTest.php @@ -5,7 +5,7 @@ */ namespace Magento\Developer\Helper; -use \Zend\Stdlib\Parameters; +use \Laminas\Stdlib\Parameters; class DataTest extends \PHPUnit\Framework\TestCase { diff --git a/dev/tests/integration/testsuite/Magento/Developer/Model/Logger/Handler/DebugTest.php b/dev/tests/integration/testsuite/Magento/Developer/Model/Logger/Handler/DebugTest.php index f7a47017f8b18..fec71206accd8 100644 --- a/dev/tests/integration/testsuite/Magento/Developer/Model/Logger/Handler/DebugTest.php +++ b/dev/tests/integration/testsuite/Magento/Developer/Model/Logger/Handler/DebugTest.php @@ -173,6 +173,7 @@ private function reinitDeploymentConfig() { $this->etcDirectory->delete(self::$configFile); $this->etcDirectory->copyFile(self::$backupFile, self::$configFile); + $this->deploymentConfig->resetData(); } /** diff --git a/dev/tests/integration/testsuite/Magento/Directory/Block/CurrencyTest.php b/dev/tests/integration/testsuite/Magento/Directory/Block/CurrencyTest.php new file mode 100644 index 0000000000000..30527bc2fa926 --- /dev/null +++ b/dev/tests/integration/testsuite/Magento/Directory/Block/CurrencyTest.php @@ -0,0 +1,123 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +declare(strict_types=1); + +namespace Magento\Directory\Block; + +use Magento\Framework\ObjectManagerInterface; +use Magento\Framework\View\LayoutInterface; +use Magento\Store\Model\StoreManagerInterface; +use Magento\TestFramework\Helper\Bootstrap; +use PHPUnit\Framework\TestCase; + +/** + * Check currency block behaviour + * + * @see \Magento\Directory\Block\Currency + * @magentoAppArea frontend + * @magentoAppIsolation enabled + * @magentoDbIsolation disabled + */ +class CurrencyTest extends TestCase +{ + private const CURRENCY_SWITCHER_TEMPLATE = 'Magento_Directory::currency.phtml'; + + /** @var ObjectManagerInterface */ + private $objectManager; + + /** @var LayoutInterface */ + private $layout; + + /** @var StoreManagerInterface */ + private $storeManager; + + /** + * @inheritdoc + */ + protected function setUp() + { + parent::setUp(); + + $this->objectManager = Bootstrap::getObjectManager(); + $this->layout = $this->objectManager->get(LayoutInterface::class); + $this->storeManager = $this->objectManager->get(StoreManagerInterface::class); + } + + /** + * @magentoConfigFixture current_store currency/options/allow USD + * + * @return void + */ + public function testDefaultCurrencySwitcher(): void + { + $this->assertCurrencySwitcherPerStore(''); + } + + /** + * @magentoConfigFixture current_store currency/options/allow EUR,USD + * + * @return void + */ + public function testCurrencySwitcher(): void + { + $this->assertCurrencySwitcherPerStore('Currency USD - US Dollar EUR - Euro'); + } + + /** + * @magentoConfigFixture current_store currency/options/allow USD,CNY + * @magentoConfigFixture fixturestore_store currency/options/allow USD,UAH + * + * @magentoDataFixture Magento/Store/_files/core_fixturestore.php + * @magentoDataFixture Magento/Directory/_files/usd_cny_rate.php + * @magentoDataFixture Magento/Directory/_files/usd_uah_rate.php + * + * @return void + */ + public function testMultiStoreCurrencySwitcher(): void + { + $this->assertCurrencySwitcherPerStore('Currency USD - US Dollar CNY - Chinese Yuan'); + $this->assertCurrencySwitcherPerStore('Currency USD - US Dollar UAH - Ukrainian Hryvnia', 'fixturestore'); + } + + /** + * Check currency switcher diplaying per stores + * + * @param string $expectedData + * @param string $storeCode + * @return void + */ + private function assertCurrencySwitcherPerStore( + string $expectedData, + string $storeCode = 'default' + ): void { + $currentStore = $this->storeManager->getStore(); + try { + if ($currentStore->getCode() !== $storeCode) { + $this->storeManager->setCurrentStore($storeCode); + } + + $actualData = trim(preg_replace('/\s+/', ' ', strip_tags($this->getBlock()->toHtml()))); + $this->assertEquals($expectedData, $actualData); + } finally { + if ($currentStore->getCode() !== $storeCode) { + $this->storeManager->setCurrentStore($currentStore); + } + } + } + + /** + * Get currency block + * + * @return Currency + */ + private function getBlock(): Currency + { + $block = $this->layout->createBlock(Currency::class); + $block->setTemplate(self::CURRENCY_SWITCHER_TEMPLATE); + + return $block; + } +} diff --git a/dev/tests/integration/testsuite/Magento/Directory/Model/RegionTest.php b/dev/tests/integration/testsuite/Magento/Directory/Model/RegionTest.php new file mode 100644 index 0000000000000..e0e790b265572 --- /dev/null +++ b/dev/tests/integration/testsuite/Magento/Directory/Model/RegionTest.php @@ -0,0 +1,62 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +declare(strict_types=1); + +namespace Magento\Directory\Model; + +use Magento\TestFramework\Helper\Bootstrap; +use PHPUnit\Framework\TestCase; + +class RegionTest extends TestCase +{ + /** + * @var Country + */ + protected $country; + + /** + * @inheritDoc + */ + public function setUp() + { + $this->country = Bootstrap::getObjectManager()->create(Country::class); + } + + /** + * Verify country has regions. + * + * @var string $countryId + * @dataProvider getCountryIdDataProvider + */ + public function testCountryHasRegions($countryId) + { + $country = $this->country->loadByCode($countryId); + $region = $country->getRegions()->getItems(); + + $this->assertTrue(!empty($region), 'Country ' . $countryId . ' not have regions'); + } + + /** + * Data provider for testCountryHasRegions + * + * @return array + */ + public function getCountryIdDataProvider():array + { + return [ + ['countryId' => 'US'], + ['countryId' => 'CA'], + ['countryId' => 'CN'], + ['countryId' => 'IN'], + ['countryId' => 'AU'], + ['countryId' => 'BE'], + ['countryId' => 'CO'], + ['countryId' => 'MX'], + ['countryId' => 'PL'], + ['countryId' => 'IT'] + ]; + } +} diff --git a/dev/tests/integration/testsuite/Magento/Directory/_files/usd_cny_rate.php b/dev/tests/integration/testsuite/Magento/Directory/_files/usd_cny_rate.php new file mode 100644 index 0000000000000..8651f2cc760d2 --- /dev/null +++ b/dev/tests/integration/testsuite/Magento/Directory/_files/usd_cny_rate.php @@ -0,0 +1,16 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +declare(strict_types=1); + +use Magento\Directory\Model\Currency; +use Magento\TestFramework\Helper\Bootstrap; + +$objectManager = Bootstrap::getObjectManager(); + +$rates = ['USD' => ['CNY' => '7.0000']]; +/** @var Currency $currencyModel */ +$currencyModel = $objectManager->create(Currency::class); +$currencyModel->saveRates($rates); diff --git a/dev/tests/integration/testsuite/Magento/Directory/_files/usd_cny_rate_rollback.php b/dev/tests/integration/testsuite/Magento/Directory/_files/usd_cny_rate_rollback.php new file mode 100644 index 0000000000000..c553995e6288c --- /dev/null +++ b/dev/tests/integration/testsuite/Magento/Directory/_files/usd_cny_rate_rollback.php @@ -0,0 +1,14 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +declare(strict_types=1); + +use Magento\TestFramework\Directory\Model\RemoveCurrencyRateByCode; +use Magento\TestFramework\Helper\Bootstrap; + +$objectManager = Bootstrap::getObjectManager(); +/** @var RemoveCurrencyRateByCode $deleteRateByCode */ +$deleteRateByCode = $objectManager->get(RemoveCurrencyRateByCode::class); +$deleteRateByCode->execute('CNY'); diff --git a/dev/tests/integration/testsuite/Magento/Directory/_files/usd_uah_rate.php b/dev/tests/integration/testsuite/Magento/Directory/_files/usd_uah_rate.php new file mode 100644 index 0000000000000..3bb4bded1979c --- /dev/null +++ b/dev/tests/integration/testsuite/Magento/Directory/_files/usd_uah_rate.php @@ -0,0 +1,16 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +declare(strict_types=1); + +use Magento\Directory\Model\Currency; +use Magento\TestFramework\Helper\Bootstrap; + +$objectManager = Bootstrap::getObjectManager(); + +$rates = ['USD' => ['UAH' => '24.0000']]; +/** @var Currency $currencyModel */ +$currencyModel = $objectManager->create(Currency::class); +$currencyModel->saveRates($rates); diff --git a/dev/tests/integration/testsuite/Magento/Directory/_files/usd_uah_rate_rollback.php b/dev/tests/integration/testsuite/Magento/Directory/_files/usd_uah_rate_rollback.php new file mode 100644 index 0000000000000..131f533666132 --- /dev/null +++ b/dev/tests/integration/testsuite/Magento/Directory/_files/usd_uah_rate_rollback.php @@ -0,0 +1,14 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +declare(strict_types=1); + +use Magento\TestFramework\Directory\Model\RemoveCurrencyRateByCode; +use Magento\TestFramework\Helper\Bootstrap; + +$objectManager = Bootstrap::getObjectManager(); +/** @var RemoveCurrencyRateByCode $deleteRateByCode */ +$deleteRateByCode = $objectManager->get(RemoveCurrencyRateByCode::class); +$deleteRateByCode->execute('UAH'); diff --git a/dev/tests/integration/testsuite/Magento/Downloadable/_files/product_downloadable_with_link_url_and_sample_url.php b/dev/tests/integration/testsuite/Magento/Downloadable/_files/product_downloadable_with_link_url_and_sample_url.php new file mode 100644 index 0000000000000..2686b7c095b2f --- /dev/null +++ b/dev/tests/integration/testsuite/Magento/Downloadable/_files/product_downloadable_with_link_url_and_sample_url.php @@ -0,0 +1,85 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ + +use Magento\Downloadable\Api\DomainManagerInterface; +use Magento\Store\Model\StoreManagerInterface; +use Magento\TestFramework\Helper\Bootstrap; +use Magento\Downloadable\Api\Data\LinkInterfaceFactory; +use Magento\Catalog\Api\Data\ProductInterface; +use Magento\Downloadable\Model\Product\Type; +use Magento\Catalog\Model\Product\Visibility; +use Magento\Catalog\Model\Product\Attribute\Source\Status; +use Magento\Downloadable\Helper\Download; +use Magento\Downloadable\Model\Link; +use Magento\Downloadable\Api\Data\SampleInterfaceFactory; +use Magento\Catalog\Api\ProductRepositoryInterface; +use Magento\Downloadable\Api\Data\LinkInterface; + +$objectManager = Bootstrap::getObjectManager(); + +$storeManager = $objectManager->get(StoreManagerInterface::class); +$storeManager->setCurrentStore($storeManager->getStore('admin')->getId()); + +$domainManager = $objectManager->get(DomainManagerInterface::class); +$domainManager->addDomains( + [ + 'example.com', + 'www.example.com', + 'www.sample.example.com', + 'google.com' + ] +); + +$product = $objectManager->get(ProductInterface::class); +$product + ->setTypeId(Type::TYPE_DOWNLOADABLE) + ->setId(1) + ->setAttributeSetId(4) + ->setName('Downloadable Product') + ->setSku('downloadable-product') + ->setPrice(10) + ->setVisibility(Visibility::VISIBILITY_BOTH) + ->setStatus(Status::STATUS_ENABLED) + ->setLinksPurchasedSeparately(true) + ->setStockData( + [ + 'qty' => 100, + 'is_in_stock' => 1, + 'manage_stock' => 1, + ] + ); + +$linkFactory = $objectManager->get(LinkInterfaceFactory::class); +/** @var LinkInterface $link */ +$link = $linkFactory->create(); +$link->setTitle('Downloadable Product Link'); +$link->setIsShareable(Link::LINK_SHAREABLE_CONFIG); +$link->setLinkUrl('http://example.com/downloadable.txt'); +$link->setLinkType(Download::LINK_TYPE_URL); +$link->setStoreId($product->getStoreId()); +$link->setWebsiteId($product->getStore()->getWebsiteId()); +$link->setProductWebsiteIds($product->getWebsiteIds()); +$link->setSortOrder(1); +$link->setPrice(0); +$link->setNumberOfDownloads(0); + +$sampleFactory = $objectManager->get(SampleInterfaceFactory::class); +$sample = $sampleFactory->create(); +$sample->setTitle('Downloadable Product Sample') + ->setSampleType(Download::LINK_TYPE_URL) + ->setSampleUrl('http://example.com/downloadable.txt') + ->setStoreId($product->getStoreId()) + ->setWebsiteId($product->getStore()->getWebsiteId()) + ->setProductWebsiteIds($product->getWebsiteIds()) + ->setSortOrder(10); + +$extension = $product->getExtensionAttributes(); +$extension->setDownloadableProductLinks([$link]); +$extension->setDownloadableProductSamples([$sample]); +$product->setExtensionAttributes($extension); + +$productRepository = $objectManager->get(ProductRepositoryInterface::class); +$productRepository->save($product); diff --git a/dev/tests/integration/testsuite/Magento/Downloadable/_files/product_downloadable_with_link_url_and_sample_url_rollback.php b/dev/tests/integration/testsuite/Magento/Downloadable/_files/product_downloadable_with_link_url_and_sample_url_rollback.php new file mode 100644 index 0000000000000..9a2e1c74fcd33 --- /dev/null +++ b/dev/tests/integration/testsuite/Magento/Downloadable/_files/product_downloadable_with_link_url_and_sample_url_rollback.php @@ -0,0 +1,40 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ + +use Magento\Downloadable\Api\DomainManagerInterface; +use Magento\Framework\Exception\NoSuchEntityException; + +\Magento\TestFramework\Helper\Bootstrap::getInstance()->getInstance()->reinitialize(); + +$objectManager = \Magento\TestFramework\Helper\Bootstrap::getObjectManager(); + +/** @var DomainManagerInterface $domainManager */ +$domainManager = $objectManager->get(DomainManagerInterface::class); +$domainManager->removeDomains( + [ + 'example.com', + 'www.example.com', + 'www.sample.example.com', + 'google.com' + ] +); + +/** @var \Magento\Framework\Registry $registry */ +$registry = $objectManager->get(\Magento\Framework\Registry::class); + +$registry->unregister('isSecureArea'); +$registry->register('isSecureArea', true); + +/** @var \Magento\Catalog\Api\ProductRepositoryInterface $productRepository */ +$productRepository = $objectManager + ->get(\Magento\Catalog\Api\ProductRepositoryInterface::class); +try { + $product = $productRepository->get('downloadable-product', false, null, true); + $productRepository->delete($product); +} catch (NoSuchEntityException $e) { // @codingStandardsIgnoreLine +} +$registry->unregister('isSecureArea'); +$registry->register('isSecureArea', false); diff --git a/dev/tests/integration/testsuite/Magento/DownloadableImportExport/Model/DownloadableTest.php b/dev/tests/integration/testsuite/Magento/DownloadableImportExport/Model/DownloadableTest.php index d0e4471e2ea68..861be98c13e72 100644 --- a/dev/tests/integration/testsuite/Magento/DownloadableImportExport/Model/DownloadableTest.php +++ b/dev/tests/integration/testsuite/Magento/DownloadableImportExport/Model/DownloadableTest.php @@ -6,7 +6,11 @@ namespace Magento\DownloadableImportExport\Model; use Magento\CatalogImportExport\Model\AbstractProductExportImportTestCase; +use Magento\Catalog\Model\Product; +/** + * Test export and import downloadable products + */ class DownloadableTest extends AbstractProductExportImportTestCase { /** @@ -17,15 +21,7 @@ public function exportImportDataProvider(): array return [ 'downloadable-product' => [ [ - 'Magento/Downloadable/_files/product_downloadable.php' - ], - [ - 'downloadable-product', - ], - ], - 'downloadable-product-with-files' => [ - [ - 'Magento/Downloadable/_files/product_downloadable_with_files.php' + 'Magento/Downloadable/_files/product_downloadable_with_link_url_and_sample_url.php' ], [ 'downloadable-product', @@ -46,43 +42,54 @@ public function exportImportDataProvider(): array * @param string[] $skippedAttributes * @return void * @dataProvider exportImportDataProvider - * @SuppressWarnings(PHPMD.UnusedFormalParameter) */ public function testImportExport(array $fixtures, array $skus, array $skippedAttributes = []): void { - $this->markTestSkipped('Uncomment after MAGETWO-38240 resolved'); + $skippedAttributes = array_merge(self::$skippedAttributes, ['downloadable_links']); + parent::testImportExport($fixtures, $skus, $skippedAttributes); } /** * @inheritdoc */ protected function assertEqualsSpecificAttributes( - \Magento\Catalog\Model\Product $expectedProduct, - \Magento\Catalog\Model\Product $actualProduct + Product $expectedProduct, + Product $actualProduct ): void { - $expectedProductLinks = $expectedProduct->getExtensionAttributes()->getDownloadableProductLinks(); + $expectedProductLinks = $expectedProduct->getExtensionAttributes()->getDownloadableProductLinks(); $expectedProductSamples = $expectedProduct->getExtensionAttributes()->getDownloadableProductSamples(); - $actualProductLinks = $actualProduct->getExtensionAttributes()->getDownloadableProductLinks(); + $actualProductLinks = $actualProduct->getExtensionAttributes()->getDownloadableProductLinks(); $actualProductSamples = $actualProduct->getExtensionAttributes()->getDownloadableProductSamples(); $this->assertEquals(count($expectedProductLinks), count($actualProductLinks)); $this->assertEquals(count($expectedProductSamples), count($actualProductSamples)); - - $expectedLinksArray = []; - foreach ($expectedProductLinks as $link) { - $expectedLinksArray[] = $link->getData(); + $actualLinks = $this->getDataWithSortingById($actualProductLinks); + $expectedLinks = $this->getDataWithSortingById($actualProductLinks); + foreach ($actualLinks as $key => $actualLink) { + $this->assertEquals($expectedLinks[$key], $actualLink); } - foreach ($actualProductLinks as $actualLink) { - $this->assertContains($expectedLinksArray, $actualLink->getData()); + $actualSamples = $this->getDataWithSortingById($actualProductSamples); + $expectedSamples = $this->getDataWithSortingById($expectedProductSamples); + foreach ($actualSamples as $key => $actualSample) { + $this->assertEquals($expectedSamples[$key], $actualSample); } + } - $expectedSamplesArray = []; - foreach ($expectedProductSamples as $sample) { - $expectedSamplesArray[] = $sample->getData(); - } - foreach ($actualProductSamples as $actualSample) { - $this->assertContains($expectedSamplesArray, $actualSample->getData()); + /** + * Get data with sorting by id + * + * @param array $objects + * + * @return array + */ + private function getDataWithSortingById(array $objects) + { + $result = []; + foreach ($objects as $object) { + $result[$object->getId()] = $object->getData(); } + + return $result; } } diff --git a/dev/tests/integration/testsuite/Magento/Eav/Controller/Adminhtml/Product/Attribute/Save/InputType/DateTest.php b/dev/tests/integration/testsuite/Magento/Eav/Controller/Adminhtml/Product/Attribute/Save/InputType/DateTest.php index cb75d3e0d4a8e..9057415cf5248 100644 --- a/dev/tests/integration/testsuite/Magento/Eav/Controller/Adminhtml/Product/Attribute/Save/InputType/DateTest.php +++ b/dev/tests/integration/testsuite/Magento/Eav/Controller/Adminhtml/Product/Attribute/Save/InputType/DateTest.php @@ -7,19 +7,20 @@ namespace Magento\Eav\Controller\Adminhtml\Product\Attribute\Save\InputType; -use Magento\Catalog\Controller\Adminhtml\Product\Attribute\Save\InputType\AbstractSaveAttributeTest; +use Magento\Catalog\Controller\Adminhtml\Product\Attribute\Save\AbstractSaveAttributeTest; /** * Test cases related to create attribute with input type date. * * @magentoDbIsolation enabled + * @magentoAppArea adminhtml */ class DateTest extends AbstractSaveAttributeTest { /** * Test create attribute and compare attribute data and input data. * - * @dataProvider \Magento\TestFramework\Eav\Model\Attribute\DataProvider\Date::getAttributeDataWithCheckArray() + * @dataProvider \Magento\TestFramework\Eav\Model\Attribute\DataProvider\Date::getAttributeDataWithCheckArray * * @param array $attributePostData * @param array $checkArray @@ -33,7 +34,7 @@ public function testCreateAttribute(array $attributePostData, array $checkArray) /** * Test create attribute with error. * - * @dataProvider \Magento\TestFramework\Eav\Model\Attribute\DataProvider\Date::getAttributeDataWithErrorMessage() + * @dataProvider \Magento\TestFramework\Eav\Model\Attribute\DataProvider\Date::getAttributeDataWithErrorMessage * * @param array $attributePostData * @param string $errorMessage diff --git a/dev/tests/integration/testsuite/Magento/Eav/Controller/Adminhtml/Product/Attribute/Save/InputType/DateTimeTest.php b/dev/tests/integration/testsuite/Magento/Eav/Controller/Adminhtml/Product/Attribute/Save/InputType/DateTimeTest.php new file mode 100644 index 0000000000000..e80a29877a508 --- /dev/null +++ b/dev/tests/integration/testsuite/Magento/Eav/Controller/Adminhtml/Product/Attribute/Save/InputType/DateTimeTest.php @@ -0,0 +1,48 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +declare(strict_types=1); + +namespace Magento\Eav\Controller\Adminhtml\Product\Attribute\Save\InputType; + +use Magento\Catalog\Controller\Adminhtml\Product\Attribute\Save\AbstractSaveAttributeTest; + +/** + * Test cases related to create attribute with input type datetime. + * + * @magentoDbIsolation enabled + * @magentoAppArea adminhtml + */ +class DateTimeTest extends AbstractSaveAttributeTest +{ + /** + * Test create attribute and compare attribute data and input data. + * + * @dataProvider \Magento\TestFramework\Eav\Model\Attribute\DataProvider\DateTime::getAttributeDataWithCheckArray + * @magentoConfigFixture default/general/locale/timezone UTC + * + * @param array $attributePostData + * @param array $checkArray + * @return void + */ + public function testCreateAttribute(array $attributePostData, array $checkArray): void + { + $this->createAttributeUsingDataAndAssert($attributePostData, $checkArray); + } + + /** + * Test create attribute with error. + * + * @dataProvider \Magento\TestFramework\Eav\Model\Attribute\DataProvider\DateTime::getAttributeDataWithErrorMessage + * + * @param array $attributePostData + * @param string $errorMessage + * @return void + */ + public function testCreateAttributeWithError(array $attributePostData, string $errorMessage): void + { + $this->createAttributeUsingDataWithErrorAndAssert($attributePostData, $errorMessage); + } +} diff --git a/dev/tests/integration/testsuite/Magento/Eav/Controller/Adminhtml/Product/Attribute/Save/InputType/DropDownTest.php b/dev/tests/integration/testsuite/Magento/Eav/Controller/Adminhtml/Product/Attribute/Save/InputType/DropDownTest.php index 1a3f363832d6e..070dc850057cf 100644 --- a/dev/tests/integration/testsuite/Magento/Eav/Controller/Adminhtml/Product/Attribute/Save/InputType/DropDownTest.php +++ b/dev/tests/integration/testsuite/Magento/Eav/Controller/Adminhtml/Product/Attribute/Save/InputType/DropDownTest.php @@ -7,19 +7,20 @@ namespace Magento\Eav\Controller\Adminhtml\Product\Attribute\Save\InputType; -use Magento\Catalog\Controller\Adminhtml\Product\Attribute\Save\InputType\AbstractSaveAttributeTest; +use Magento\Catalog\Controller\Adminhtml\Product\Attribute\Save\AbstractSaveAttributeTest; /** * Test cases related to create attribute with input type dropdown. * * @magentoDbIsolation enabled + * @magentoAppArea adminhtml */ class DropDownTest extends AbstractSaveAttributeTest { /** * Test create attribute and compare attribute data and input data. * - * @dataProvider \Magento\TestFramework\Eav\Model\Attribute\DataProvider\DropDown::getAttributeDataWithCheckArray() + * @dataProvider \Magento\TestFramework\Eav\Model\Attribute\DataProvider\DropDown::getAttributeDataWithCheckArray * * @param array $attributePostData * @param array $checkArray @@ -33,7 +34,7 @@ public function testCreateAttribute(array $attributePostData, array $checkArray) /** * Test create attribute with error. * - * @dataProvider \Magento\TestFramework\Eav\Model\Attribute\DataProvider\DropDown::getAttributeDataWithErrorMessage() + * @dataProvider \Magento\TestFramework\Eav\Model\Attribute\DataProvider\DropDown::getAttributeDataWithErrorMessage * * @param array $attributePostData * @param string $errorMessage diff --git a/dev/tests/integration/testsuite/Magento/Eav/Controller/Adminhtml/Product/Attribute/Save/InputType/MultipleSelectTest.php b/dev/tests/integration/testsuite/Magento/Eav/Controller/Adminhtml/Product/Attribute/Save/InputType/MultipleSelectTest.php index 1c0f5ea720f70..6f51546a5d62d 100644 --- a/dev/tests/integration/testsuite/Magento/Eav/Controller/Adminhtml/Product/Attribute/Save/InputType/MultipleSelectTest.php +++ b/dev/tests/integration/testsuite/Magento/Eav/Controller/Adminhtml/Product/Attribute/Save/InputType/MultipleSelectTest.php @@ -7,19 +7,20 @@ namespace Magento\Eav\Controller\Adminhtml\Product\Attribute\Save\InputType; -use Magento\Catalog\Controller\Adminhtml\Product\Attribute\Save\InputType\AbstractSaveAttributeTest; +use Magento\Catalog\Controller\Adminhtml\Product\Attribute\Save\AbstractSaveAttributeTest; /** * Test cases related to create attribute with input type multiselect. * * @magentoDbIsolation enabled + * @magentoAppArea adminhtml */ class MultipleSelectTest extends AbstractSaveAttributeTest { /** * Test create attribute and compare attribute data and input data. * - * @dataProvider \Magento\TestFramework\Eav\Model\Attribute\DataProvider\MultipleSelect::getAttributeDataWithCheckArray() + * @dataProvider \Magento\TestFramework\Eav\Model\Attribute\DataProvider\MultipleSelect::getAttributeDataWithCheckArray * * @param array $attributePostData * @param array $checkArray @@ -33,7 +34,7 @@ public function testCreateAttribute(array $attributePostData, array $checkArray) /** * Test create attribute with error. * - * @dataProvider \Magento\TestFramework\Eav\Model\Attribute\DataProvider\MultipleSelect::getAttributeDataWithErrorMessage() + * @dataProvider \Magento\TestFramework\Eav\Model\Attribute\DataProvider\MultipleSelect::getAttributeDataWithErrorMessage * * @param array $attributePostData * @param string $errorMessage diff --git a/dev/tests/integration/testsuite/Magento/Eav/Controller/Adminhtml/Product/Attribute/Save/InputType/TextAreaTest.php b/dev/tests/integration/testsuite/Magento/Eav/Controller/Adminhtml/Product/Attribute/Save/InputType/TextAreaTest.php index 9c5b1a8587674..c315f61b89148 100644 --- a/dev/tests/integration/testsuite/Magento/Eav/Controller/Adminhtml/Product/Attribute/Save/InputType/TextAreaTest.php +++ b/dev/tests/integration/testsuite/Magento/Eav/Controller/Adminhtml/Product/Attribute/Save/InputType/TextAreaTest.php @@ -7,19 +7,20 @@ namespace Magento\Eav\Controller\Adminhtml\Product\Attribute\Save\InputType; -use Magento\Catalog\Controller\Adminhtml\Product\Attribute\Save\InputType\AbstractSaveAttributeTest; +use Magento\Catalog\Controller\Adminhtml\Product\Attribute\Save\AbstractSaveAttributeTest; /** * Test cases related to create attribute with input type text_area. * * @magentoDbIsolation enabled + * @magentoAppArea adminhtml */ class TextAreaTest extends AbstractSaveAttributeTest { /** * Test create attribute and compare attribute data and input data. * - * @dataProvider \Magento\TestFramework\Eav\Model\Attribute\DataProvider\TextArea::getAttributeDataWithCheckArray() + * @dataProvider \Magento\TestFramework\Eav\Model\Attribute\DataProvider\TextArea::getAttributeDataWithCheckArray * * @param array $attributePostData * @param array $checkArray @@ -33,7 +34,7 @@ public function testCreateAttribute(array $attributePostData, array $checkArray) /** * Test create attribute with error. * - * @dataProvider \Magento\TestFramework\Eav\Model\Attribute\DataProvider\TextArea::getAttributeDataWithErrorMessage() + * @dataProvider \Magento\TestFramework\Eav\Model\Attribute\DataProvider\TextArea::getAttributeDataWithErrorMessage * * @param array $attributePostData * @param string $errorMessage diff --git a/dev/tests/integration/testsuite/Magento/Eav/Controller/Adminhtml/Product/Attribute/Save/InputType/TextEditorTest.php b/dev/tests/integration/testsuite/Magento/Eav/Controller/Adminhtml/Product/Attribute/Save/InputType/TextEditorTest.php index 807e0cfd570b2..36e95550a562a 100644 --- a/dev/tests/integration/testsuite/Magento/Eav/Controller/Adminhtml/Product/Attribute/Save/InputType/TextEditorTest.php +++ b/dev/tests/integration/testsuite/Magento/Eav/Controller/Adminhtml/Product/Attribute/Save/InputType/TextEditorTest.php @@ -7,19 +7,20 @@ namespace Magento\Eav\Controller\Adminhtml\Product\Attribute\Save\InputType; -use Magento\Catalog\Controller\Adminhtml\Product\Attribute\Save\InputType\AbstractSaveAttributeTest; +use Magento\Catalog\Controller\Adminhtml\Product\Attribute\Save\AbstractSaveAttributeTest; /** * Test cases related to create attribute with input type text_editor. * * @magentoDbIsolation enabled + * @magentoAppArea adminhtml */ class TextEditorTest extends AbstractSaveAttributeTest { /** * Test create attribute and compare attribute data and input data. * - * @dataProvider \Magento\TestFramework\Eav\Model\Attribute\DataProvider\TextEditor::getAttributeDataWithCheckArray() + * @dataProvider \Magento\TestFramework\Eav\Model\Attribute\DataProvider\TextEditor::getAttributeDataWithCheckArray * * @param array $attributePostData * @param array $checkArray @@ -33,7 +34,7 @@ public function testCreateAttribute(array $attributePostData, array $checkArray) /** * Test create attribute with error. * - * @dataProvider \Magento\TestFramework\Eav\Model\Attribute\DataProvider\TextEditor::getAttributeDataWithErrorMessage() + * @dataProvider \Magento\TestFramework\Eav\Model\Attribute\DataProvider\TextEditor::getAttributeDataWithErrorMessage * * @param array $attributePostData * @param string $errorMessage diff --git a/dev/tests/integration/testsuite/Magento/Eav/Controller/Adminhtml/Product/Attribute/Save/InputType/TextTest.php b/dev/tests/integration/testsuite/Magento/Eav/Controller/Adminhtml/Product/Attribute/Save/InputType/TextTest.php index 70069dcedd0e4..3a7747f59939a 100644 --- a/dev/tests/integration/testsuite/Magento/Eav/Controller/Adminhtml/Product/Attribute/Save/InputType/TextTest.php +++ b/dev/tests/integration/testsuite/Magento/Eav/Controller/Adminhtml/Product/Attribute/Save/InputType/TextTest.php @@ -7,19 +7,20 @@ namespace Magento\Eav\Controller\Adminhtml\Product\Attribute\Save\InputType; -use Magento\Catalog\Controller\Adminhtml\Product\Attribute\Save\InputType\AbstractSaveAttributeTest; +use Magento\Catalog\Controller\Adminhtml\Product\Attribute\Save\AbstractSaveAttributeTest; /** * Test cases related to create attribute with input type text. * * @magentoDbIsolation enabled + * @magentoAppArea adminhtml */ class TextTest extends AbstractSaveAttributeTest { /** * Test create attribute and compare attribute data and input data. * - * @dataProvider \Magento\TestFramework\Eav\Model\Attribute\DataProvider\Text::getAttributeDataWithCheckArray() + * @dataProvider \Magento\TestFramework\Eav\Model\Attribute\DataProvider\Text::getAttributeDataWithCheckArray * * @param array $attributePostData * @param array $checkArray @@ -33,7 +34,7 @@ public function testCreateAttribute(array $attributePostData, array $checkArray) /** * Test create attribute with error. * - * @dataProvider \Magento\TestFramework\Eav\Model\Attribute\DataProvider\Text::getAttributeDataWithErrorMessage() + * @dataProvider \Magento\TestFramework\Eav\Model\Attribute\DataProvider\Text::getAttributeDataWithErrorMessage * * @param array $attributePostData * @param string $errorMessage diff --git a/dev/tests/integration/testsuite/Magento/Eav/Controller/Adminhtml/Product/Attribute/Save/InputType/YesNoTest.php b/dev/tests/integration/testsuite/Magento/Eav/Controller/Adminhtml/Product/Attribute/Save/InputType/YesNoTest.php index 7bb26556c3fd6..28ba04465b870 100644 --- a/dev/tests/integration/testsuite/Magento/Eav/Controller/Adminhtml/Product/Attribute/Save/InputType/YesNoTest.php +++ b/dev/tests/integration/testsuite/Magento/Eav/Controller/Adminhtml/Product/Attribute/Save/InputType/YesNoTest.php @@ -7,19 +7,20 @@ namespace Magento\Eav\Controller\Adminhtml\Product\Attribute\Save\InputType; -use Magento\Catalog\Controller\Adminhtml\Product\Attribute\Save\InputType\AbstractSaveAttributeTest; +use Magento\Catalog\Controller\Adminhtml\Product\Attribute\Save\AbstractSaveAttributeTest; /** * Test cases related to create attribute with yes/no input type. * * @magentoDbIsolation enabled + * @magentoAppArea adminhtml */ class YesNoTest extends AbstractSaveAttributeTest { /** * Test create attribute and compare attribute data and input data. * - * @dataProvider \Magento\TestFramework\Eav\Model\Attribute\DataProvider\YesNo::getAttributeDataWithCheckArray() + * @dataProvider \Magento\TestFramework\Eav\Model\Attribute\DataProvider\YesNo::getAttributeDataWithCheckArray * * @param array $attributePostData * @param array $checkArray @@ -33,7 +34,7 @@ public function testCreateAttribute(array $attributePostData, array $checkArray) /** * Test create attribute with error. * - * @dataProvider \Magento\TestFramework\Eav\Model\Attribute\DataProvider\YesNo::getAttributeDataWithErrorMessage() + * @dataProvider \Magento\TestFramework\Eav\Model\Attribute\DataProvider\YesNo::getAttributeDataWithErrorMessage * * @param array $attributePostData * @param string $errorMessage diff --git a/dev/tests/integration/testsuite/Magento/Eav/Controller/Adminhtml/Product/Attribute/Update/InputType/DateTest.php b/dev/tests/integration/testsuite/Magento/Eav/Controller/Adminhtml/Product/Attribute/Update/InputType/DateTest.php new file mode 100644 index 0000000000000..5df39674d05c8 --- /dev/null +++ b/dev/tests/integration/testsuite/Magento/Eav/Controller/Adminhtml/Product/Attribute/Update/InputType/DateTest.php @@ -0,0 +1,67 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +declare(strict_types=1); + +namespace Magento\Eav\Controller\Adminhtml\Product\Attribute\Update\InputType; + +use Magento\Catalog\Controller\Adminhtml\Product\Attribute\Update\AbstractUpdateAttributeTest; + +/** + * Test cases related to update attribute with input type date. + * + * @magentoDbIsolation enabled + * @magentoAppArea adminhtml + */ +class DateTest extends AbstractUpdateAttributeTest +{ + /** + * Test update attribute. + * + * @dataProvider \Magento\TestFramework\Eav\Model\Attribute\DataProvider\Date::getUpdateProvider + * @magentoDataFixture Magento/Catalog/_files/product_date_attribute.php + * + * @param array $postData + * @param array $expectedData + * @return void + */ + public function testUpdateAttribute(array $postData, array $expectedData): void + { + $this->updateAttributeUsingData('date_attribute', $postData); + $this->assertUpdateAttributeProcess('date_attribute', $postData, $expectedData); + } + + /** + * Test update attribute with error. + * + * @dataProvider \Magento\TestFramework\Eav\Model\Attribute\DataProvider\Date::getUpdateProviderWithErrorMessage + * @magentoDataFixture Magento/Catalog/_files/product_date_attribute.php + * + * @param array $postData + * @param string $errorMessage + * @return void + */ + public function testUpdateAttributeWithError(array $postData, string $errorMessage): void + { + $this->updateAttributeUsingData('date_attribute', $postData); + $this->assertErrorSessionMessages($errorMessage); + } + + /** + * Test update attribute frontend labels on stores. + * + * @dataProvider \Magento\TestFramework\Eav\Model\Attribute\DataProvider\Date::getUpdateFrontendLabelsProvider + * @magentoDataFixture Magento/Store/_files/second_website_with_two_stores.php + * @magentoDataFixture Magento/Catalog/_files/product_date_attribute.php + * + * @param array $postData + * @param array $expectedData + * @return void + */ + public function testUpdateFrontendLabelOnStores(array $postData, array $expectedData): void + { + $this->processUpdateFrontendLabelOnStores('date_attribute', $postData, $expectedData); + } +} diff --git a/dev/tests/integration/testsuite/Magento/Eav/Controller/Adminhtml/Product/Attribute/Update/InputType/DateTimeTest.php b/dev/tests/integration/testsuite/Magento/Eav/Controller/Adminhtml/Product/Attribute/Update/InputType/DateTimeTest.php new file mode 100644 index 0000000000000..2a6f730baf624 --- /dev/null +++ b/dev/tests/integration/testsuite/Magento/Eav/Controller/Adminhtml/Product/Attribute/Update/InputType/DateTimeTest.php @@ -0,0 +1,68 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +declare(strict_types=1); + +namespace Magento\Eav\Controller\Adminhtml\Product\Attribute\Update\InputType; + +use Magento\Catalog\Controller\Adminhtml\Product\Attribute\Update\AbstractUpdateAttributeTest; + +/** + * Test cases related to update attribute with input type date. + * + * @magentoDbIsolation enabled + * @magentoAppArea adminhtml + */ +class DateTimeTest extends AbstractUpdateAttributeTest +{ + /** + * Test update attribute. + * + * @dataProvider \Magento\TestFramework\Eav\Model\Attribute\DataProvider\DateTime::getUpdateProvider + * @magentoConfigFixture default/general/locale/timezone UTC + * @magentoDataFixture Magento/Catalog/_files/product_datetime_attribute.php + * + * @param array $postData + * @param array $expectedData + * @return void + */ + public function testUpdateAttribute(array $postData, array $expectedData): void + { + $this->updateAttributeUsingData('datetime_attribute', $postData); + $this->assertUpdateAttributeProcess('datetime_attribute', $postData, $expectedData); + } + + /** + * Test update attribute with error. + * + * @dataProvider \Magento\TestFramework\Eav\Model\Attribute\DataProvider\DateTime::getUpdateProviderWithErrorMessage + * @magentoDataFixture Magento/Catalog/_files/product_datetime_attribute.php + * + * @param array $postData + * @param string $errorMessage + * @return void + */ + public function testUpdateAttributeWithError(array $postData, string $errorMessage): void + { + $this->updateAttributeUsingData('datetime_attribute', $postData); + $this->assertErrorSessionMessages($errorMessage); + } + + /** + * Test update attribute frontend labels on stores. + * + * @dataProvider \Magento\TestFramework\Eav\Model\Attribute\DataProvider\DateTime::getUpdateFrontendLabelsProvider + * @magentoDataFixture Magento/Store/_files/second_website_with_two_stores.php + * @magentoDataFixture Magento/Catalog/_files/product_datetime_attribute.php + * + * @param array $postData + * @param array $expectedData + * @return void + */ + public function testUpdateFrontendLabelOnStores(array $postData, array $expectedData): void + { + $this->processUpdateFrontendLabelOnStores('datetime_attribute', $postData, $expectedData); + } +} diff --git a/dev/tests/integration/testsuite/Magento/Eav/Controller/Adminhtml/Product/Attribute/Update/InputType/DropDownTest.php b/dev/tests/integration/testsuite/Magento/Eav/Controller/Adminhtml/Product/Attribute/Update/InputType/DropDownTest.php new file mode 100644 index 0000000000000..4b3fb2cf6fac9 --- /dev/null +++ b/dev/tests/integration/testsuite/Magento/Eav/Controller/Adminhtml/Product/Attribute/Update/InputType/DropDownTest.php @@ -0,0 +1,82 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +declare(strict_types=1); + +namespace Magento\Eav\Controller\Adminhtml\Product\Attribute\Update\InputType; + +use Magento\Catalog\Controller\Adminhtml\Product\Attribute\Update\AbstractUpdateAttributeTest; + +/** + * Test cases related to update attribute with input type dropdown. + * + * @magentoDbIsolation enabled + * @magentoAppArea adminhtml + */ +class DropDownTest extends AbstractUpdateAttributeTest +{ + /** + * Test update attribute. + * + * @dataProvider \Magento\TestFramework\Eav\Model\Attribute\DataProvider\DropDown::getUpdateProvider + * @magentoDataFixture Magento/Catalog/_files/dropdown_attribute.php + * + * @param array $postData + * @param array $expectedData + * @return void + */ + public function testUpdateAttribute(array $postData, array $expectedData): void + { + $this->updateAttributeUsingData('dropdown_attribute', $postData); + $this->assertUpdateAttributeProcess('dropdown_attribute', $postData, $expectedData); + } + + /** + * Test update attribute with error. + * + * @dataProvider \Magento\TestFramework\Eav\Model\Attribute\DataProvider\DropDown::getUpdateProviderWithErrorMessage + * @magentoDataFixture Magento/Catalog/_files/dropdown_attribute.php + * + * @param array $postData + * @param string $errorMessage + * @return void + */ + public function testUpdateAttributeWithError(array $postData, string $errorMessage): void + { + $this->updateAttributeUsingData('dropdown_attribute', $postData); + $this->assertErrorSessionMessages($errorMessage); + } + + /** + * Test update attribute frontend labels on stores. + * + * @dataProvider \Magento\TestFramework\Eav\Model\Attribute\DataProvider\DropDown::getUpdateFrontendLabelsProvider + * @magentoDataFixture Magento/Store/_files/second_website_with_two_stores.php + * @magentoDataFixture Magento/Catalog/_files/dropdown_attribute.php + * + * @param array $postData + * @param array $expectedData + * @return void + */ + public function testUpdateFrontendLabelOnStores(array $postData, array $expectedData): void + { + $this->processUpdateFrontendLabelOnStores('dropdown_attribute', $postData, $expectedData); + } + + /** + * Test update attribute options on stores. + * + * @dataProvider \Magento\TestFramework\Eav\Model\Attribute\DataProvider\DropDown::getUpdateOptionsProvider + * @magentoDataFixture Magento/Store/_files/second_website_with_two_stores.php + * @magentoDataFixture Magento/Catalog/_files/dropdown_attribute.php + * + * @param array $postData + * @return void + */ + public function testUpdateOptionsOnStores(array $postData): void + { + $this->processUpdateOptionsOnStores('dropdown_attribute', $postData); + } +} diff --git a/dev/tests/integration/testsuite/Magento/Eav/Controller/Adminhtml/Product/Attribute/Update/InputType/MultipleSelectTest.php b/dev/tests/integration/testsuite/Magento/Eav/Controller/Adminhtml/Product/Attribute/Update/InputType/MultipleSelectTest.php new file mode 100644 index 0000000000000..fa8b63a2d034c --- /dev/null +++ b/dev/tests/integration/testsuite/Magento/Eav/Controller/Adminhtml/Product/Attribute/Update/InputType/MultipleSelectTest.php @@ -0,0 +1,82 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +declare(strict_types=1); + +namespace Magento\Eav\Controller\Adminhtml\Product\Attribute\Update\InputType; + +use Magento\Catalog\Controller\Adminhtml\Product\Attribute\Update\AbstractUpdateAttributeTest; + +/** + * Test cases related to update attribute with input type multiselect. + * + * @magentoDbIsolation enabled + * @magentoAppArea adminhtml + */ +class MultipleSelectTest extends AbstractUpdateAttributeTest +{ + /** + * Test update attribute. + * + * @dataProvider \Magento\TestFramework\Eav\Model\Attribute\DataProvider\MultipleSelect::getUpdateProvider + * @magentoDataFixture Magento/Catalog/_files/multiselect_attribute.php + * + * @param array $postData + * @param array $expectedData + * @return void + */ + public function testUpdateAttribute(array $postData, array $expectedData): void + { + $this->updateAttributeUsingData('multiselect_attribute', $postData); + $this->assertUpdateAttributeProcess('multiselect_attribute', $postData, $expectedData); + } + + /** + * Test update attribute with error. + * + * @dataProvider \Magento\TestFramework\Eav\Model\Attribute\DataProvider\MultipleSelect::getUpdateProviderWithErrorMessage + * @magentoDataFixture Magento/Catalog/_files/multiselect_attribute.php + * + * @param array $postData + * @param string $errorMessage + * @return void + */ + public function testUpdateAttributeWithError(array $postData, string $errorMessage): void + { + $this->updateAttributeUsingData('multiselect_attribute', $postData); + $this->assertErrorSessionMessages($errorMessage); + } + + /** + * Test update attribute frontend labels on stores. + * + * @dataProvider \Magento\TestFramework\Eav\Model\Attribute\DataProvider\MultipleSelect::getUpdateFrontendLabelsProvider + * @magentoDataFixture Magento/Store/_files/second_website_with_two_stores.php + * @magentoDataFixture Magento/Catalog/_files/multiselect_attribute.php + * + * @param array $postData + * @param array $expectedData + * @return void + */ + public function testUpdateFrontendLabelOnStores(array $postData, array $expectedData): void + { + $this->processUpdateFrontendLabelOnStores('multiselect_attribute', $postData, $expectedData); + } + + /** + * Test update attribute options on stores. + * + * @dataProvider \Magento\TestFramework\Eav\Model\Attribute\DataProvider\MultipleSelect::getUpdateOptionsProvider + * @magentoDataFixture Magento/Store/_files/second_website_with_two_stores.php + * @magentoDataFixture Magento/Catalog/_files/multiselect_attribute.php + * + * @param array $postData + * @return void + */ + public function testUpdateOptionsOnStores(array $postData): void + { + $this->processUpdateOptionsOnStores('multiselect_attribute', $postData); + } +} diff --git a/dev/tests/integration/testsuite/Magento/Eav/Controller/Adminhtml/Product/Attribute/Update/InputType/TextAreaTest.php b/dev/tests/integration/testsuite/Magento/Eav/Controller/Adminhtml/Product/Attribute/Update/InputType/TextAreaTest.php new file mode 100644 index 0000000000000..708de8dec916c --- /dev/null +++ b/dev/tests/integration/testsuite/Magento/Eav/Controller/Adminhtml/Product/Attribute/Update/InputType/TextAreaTest.php @@ -0,0 +1,67 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +declare(strict_types=1); + +namespace Magento\Eav\Controller\Adminhtml\Product\Attribute\Update\InputType; + +use Magento\Catalog\Controller\Adminhtml\Product\Attribute\Update\AbstractUpdateAttributeTest; + +/** + * Test cases related to update attribute with input type text_area. + * + * @magentoDbIsolation enabled + * @magentoAppArea adminhtml + */ +class TextAreaTest extends AbstractUpdateAttributeTest +{ + /** + * Test update attribute. + * + * @dataProvider \Magento\TestFramework\Eav\Model\Attribute\DataProvider\TextArea::getUpdateProvider + * @magentoDataFixture Magento/Catalog/_files/product_text_attribute.php + * + * @param array $postData + * @param array $expectedData + * @return void + */ + public function testUpdateAttribute(array $postData, array $expectedData): void + { + $this->updateAttributeUsingData('text_attribute', $postData); + $this->assertUpdateAttributeProcess('text_attribute', $postData, $expectedData); + } + + /** + * Test update attribute with error. + * + * @dataProvider \Magento\TestFramework\Eav\Model\Attribute\DataProvider\TextArea::getUpdateProviderWithErrorMessage + * @magentoDataFixture Magento/Catalog/_files/product_text_attribute.php + * + * @param array $postData + * @param string $errorMessage + * @return void + */ + public function testUpdateAttributeWithError(array $postData, string $errorMessage): void + { + $this->updateAttributeUsingData('text_attribute', $postData); + $this->assertErrorSessionMessages($errorMessage); + } + + /** + * Test update attribute frontend labels on stores. + * + * @dataProvider \Magento\TestFramework\Eav\Model\Attribute\DataProvider\TextArea::getUpdateFrontendLabelsProvider + * @magentoDataFixture Magento/Store/_files/second_website_with_two_stores.php + * @magentoDataFixture Magento/Catalog/_files/product_text_attribute.php + * + * @param array $postData + * @param array $expectedData + * @return void + */ + public function testUpdateFrontendLabelOnStores(array $postData, array $expectedData): void + { + $this->processUpdateFrontendLabelOnStores('text_attribute', $postData, $expectedData); + } +} diff --git a/dev/tests/integration/testsuite/Magento/Eav/Controller/Adminhtml/Product/Attribute/Update/InputType/TextEditorTest.php b/dev/tests/integration/testsuite/Magento/Eav/Controller/Adminhtml/Product/Attribute/Update/InputType/TextEditorTest.php new file mode 100644 index 0000000000000..14e0f84741782 --- /dev/null +++ b/dev/tests/integration/testsuite/Magento/Eav/Controller/Adminhtml/Product/Attribute/Update/InputType/TextEditorTest.php @@ -0,0 +1,67 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +declare(strict_types=1); + +namespace Magento\Eav\Controller\Adminhtml\Product\Attribute\Update\InputType; + +use Magento\Catalog\Controller\Adminhtml\Product\Attribute\Update\AbstractUpdateAttributeTest; + +/** + * Test cases related to update attribute with input type text_editor. + * + * @magentoDbIsolation enabled + * @magentoAppArea adminhtml + */ +class TextEditorTest extends AbstractUpdateAttributeTest +{ + /** + * Test update attribute. + * + * @dataProvider \Magento\TestFramework\Eav\Model\Attribute\DataProvider\TextEditor::getUpdateProvider + * @magentoDataFixture Magento/Catalog/_files/product_text_editor_attribute.php + * + * @param array $postData + * @param array $expectedData + * @return void + */ + public function testUpdateAttribute(array $postData, array $expectedData): void + { + $this->updateAttributeUsingData('text_editor_attribute', $postData); + $this->assertUpdateAttributeProcess('text_editor_attribute', $postData, $expectedData); + } + + /** + * Test update attribute with error. + * + * @dataProvider \Magento\TestFramework\Eav\Model\Attribute\DataProvider\TextEditor::getUpdateProviderWithErrorMessage + * @magentoDataFixture Magento/Catalog/_files/product_text_editor_attribute.php + * + * @param array $postData + * @param string $errorMessage + * @return void + */ + public function testUpdateAttributeWithError(array $postData, string $errorMessage): void + { + $this->updateAttributeUsingData('text_editor_attribute', $postData); + $this->assertErrorSessionMessages($errorMessage); + } + + /** + * Test update attribute frontend labels on stores. + * + * @dataProvider \Magento\TestFramework\Eav\Model\Attribute\DataProvider\TextEditor::getUpdateFrontendLabelsProvider + * @magentoDataFixture Magento/Store/_files/second_website_with_two_stores.php + * @magentoDataFixture Magento/Catalog/_files/product_text_editor_attribute.php + * + * @param array $postData + * @param array $expectedData + * @return void + */ + public function testUpdateFrontendLabelOnStores(array $postData, array $expectedData): void + { + $this->processUpdateFrontendLabelOnStores('text_editor_attribute', $postData, $expectedData); + } +} diff --git a/dev/tests/integration/testsuite/Magento/Eav/Controller/Adminhtml/Product/Attribute/Update/InputType/TextTest.php b/dev/tests/integration/testsuite/Magento/Eav/Controller/Adminhtml/Product/Attribute/Update/InputType/TextTest.php new file mode 100644 index 0000000000000..fb025e4280c96 --- /dev/null +++ b/dev/tests/integration/testsuite/Magento/Eav/Controller/Adminhtml/Product/Attribute/Update/InputType/TextTest.php @@ -0,0 +1,67 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +declare(strict_types=1); + +namespace Magento\Eav\Controller\Adminhtml\Product\Attribute\Update\InputType; + +use Magento\Catalog\Controller\Adminhtml\Product\Attribute\Update\AbstractUpdateAttributeTest; + +/** + * Test cases related to update attribute with input type text. + * + * @magentoDbIsolation enabled + * @magentoAppArea adminhtml + */ +class TextTest extends AbstractUpdateAttributeTest +{ + /** + * Test update attribute. + * + * @dataProvider \Magento\TestFramework\Eav\Model\Attribute\DataProvider\Text::getUpdateProvider + * @magentoDataFixture Magento/Catalog/_files/product_varchar_attribute.php + * + * @param array $postData + * @param array $expectedData + * @return void + */ + public function testUpdateAttribute(array $postData, array $expectedData): void + { + $this->updateAttributeUsingData('varchar_attribute', $postData); + $this->assertUpdateAttributeProcess('varchar_attribute', $postData, $expectedData); + } + + /** + * Test update attribute with error. + * + * @dataProvider \Magento\TestFramework\Eav\Model\Attribute\DataProvider\Text::getUpdateProviderWithErrorMessage + * @magentoDataFixture Magento/Catalog/_files/product_varchar_attribute.php + * + * @param array $postData + * @param string $errorMessage + * @return void + */ + public function testUpdateAttributeWithError(array $postData, string $errorMessage): void + { + $this->updateAttributeUsingData('varchar_attribute', $postData); + $this->assertErrorSessionMessages($errorMessage); + } + + /** + * Test update attribute frontend labels on stores. + * + * @dataProvider \Magento\TestFramework\Eav\Model\Attribute\DataProvider\Text::getUpdateFrontendLabelsProvider + * @magentoDataFixture Magento/Store/_files/second_website_with_two_stores.php + * @magentoDataFixture Magento/Catalog/_files/product_varchar_attribute.php + * + * @param array $postData + * @param array $expectedData + * @return void + */ + public function testUpdateFrontendLabelOnStores(array $postData, array $expectedData): void + { + $this->processUpdateFrontendLabelOnStores('varchar_attribute', $postData, $expectedData); + } +} diff --git a/dev/tests/integration/testsuite/Magento/Eav/Controller/Adminhtml/Product/Attribute/Update/InputType/YesNoTest.php b/dev/tests/integration/testsuite/Magento/Eav/Controller/Adminhtml/Product/Attribute/Update/InputType/YesNoTest.php new file mode 100644 index 0000000000000..c2baeba182836 --- /dev/null +++ b/dev/tests/integration/testsuite/Magento/Eav/Controller/Adminhtml/Product/Attribute/Update/InputType/YesNoTest.php @@ -0,0 +1,67 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +declare(strict_types=1); + +namespace Magento\Eav\Controller\Adminhtml\Product\Attribute\Update\InputType; + +use Magento\Catalog\Controller\Adminhtml\Product\Attribute\Update\AbstractUpdateAttributeTest; + +/** + * Test cases related to update attribute with yes/no input type. + * + * @magentoDbIsolation enabled + * @magentoAppArea adminhtml + */ +class YesNoTest extends AbstractUpdateAttributeTest +{ + /** + * Test update attribute. + * + * @dataProvider \Magento\TestFramework\Eav\Model\Attribute\DataProvider\YesNo::getUpdateProvider + * @magentoDataFixture Magento/Catalog/_files/product_boolean_attribute.php + * + * @param array $postData + * @param array $expectedData + * @return void + */ + public function testUpdateAttribute(array $postData, array $expectedData): void + { + $this->updateAttributeUsingData('boolean_attribute', $postData); + $this->assertUpdateAttributeProcess('boolean_attribute', $postData, $expectedData); + } + + /** + * Test update attribute with error. + * + * @dataProvider \Magento\TestFramework\Eav\Model\Attribute\DataProvider\YesNo::getUpdateProviderWithErrorMessage + * @magentoDataFixture Magento/Catalog/_files/product_boolean_attribute.php + * + * @param array $postData + * @param string $errorMessage + * @return void + */ + public function testUpdateAttributeWithError(array $postData, string $errorMessage): void + { + $this->updateAttributeUsingData('boolean_attribute', $postData); + $this->assertErrorSessionMessages($errorMessage); + } + + /** + * Test update attribute frontend labels on stores. + * + * @dataProvider \Magento\TestFramework\Eav\Model\Attribute\DataProvider\YesNo::getUpdateFrontendLabelsProvider + * @magentoDataFixture Magento/Store/_files/second_website_with_two_stores.php + * @magentoDataFixture Magento/Catalog/_files/product_boolean_attribute.php + * + * @param array $postData + * @param array $expectedData + * @return void + */ + public function testUpdateFrontendLabelOnStores(array $postData, array $expectedData): void + { + $this->processUpdateFrontendLabelOnStores('boolean_attribute', $postData, $expectedData); + } +} diff --git a/dev/tests/integration/testsuite/Magento/Eav/_files/product_texteditor_attribute.php b/dev/tests/integration/testsuite/Magento/Eav/_files/product_texteditor_attribute.php new file mode 100644 index 0000000000000..1ac4a35e8c6a5 --- /dev/null +++ b/dev/tests/integration/testsuite/Magento/Eav/_files/product_texteditor_attribute.php @@ -0,0 +1,51 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +declare(strict_types=1); + +use Magento\Catalog\Api\ProductAttributeRepositoryInterface; +use Magento\Catalog\Model\Product; +use Magento\Catalog\Model\ResourceModel\Eav\AttributeFactory; +use Magento\Eav\Setup\EavSetup; +use Magento\TestFramework\Helper\Bootstrap; +use Magento\Catalog\Model\Product\Attribute\Frontend\Inputtype\Presentation; + +$objectManager = Bootstrap::getObjectManager(); +/** @var AttributeFactory $attributeFactory */ +$attributeFactory = $objectManager->get(AttributeFactory::class); +/** @var ProductAttributeRepositoryInterface $productAttributeRepository */ +$productAttributeRepository = $objectManager->get(ProductAttributeRepositoryInterface::class); +$attribute = $attributeFactory->create()->loadByCode(Product::ENTITY, 'text_editor_attribute'); +if (!$attribute->getId()) { + /** @var Presentation $presentation */ + $presentation = $objectManager->get(Presentation::class); + /** @var EavSetup $installer */ + $installer = $objectManager->create(EavSetup::class); + $attributeData = [ + 'attribute_code' => 'text_editor_attribute', + 'is_global' => 1, + 'is_user_defined' => 1, + 'frontend_input' => 'texteditor', + 'is_unique' => 0, + 'is_required' => 0, + 'is_searchable' => 0, + 'is_visible_in_advanced_search' => 0, + 'is_comparable' => 0, + 'is_filterable' => 0, + 'is_filterable_in_search' => 0, + 'is_used_for_promo_rules' => 0, + 'is_html_allowed_on_front' => 1, + 'is_visible_on_front' => 0, + 'used_in_product_listing' => 0, + 'used_for_sort_by' => 0, + 'frontend_label' => ['Text editor attribute'], + 'backend_type' => 'text', + ]; + $attribute->setData($presentation->convertPresentationDataToInputType($attributeData)); + $productAttributeRepository->save($attribute); + $attribute = $productAttributeRepository->get('text_editor_attribute'); + /* Assign attribute to attribute set */ + $installer->addAttributeToGroup(Product::ENTITY, 'Default', 'Attributes', $attribute->getId()); +} diff --git a/dev/tests/integration/testsuite/Magento/Eav/_files/product_texteditor_attribute_rollback.php b/dev/tests/integration/testsuite/Magento/Eav/_files/product_texteditor_attribute_rollback.php new file mode 100644 index 0000000000000..e3b1fa0202a44 --- /dev/null +++ b/dev/tests/integration/testsuite/Magento/Eav/_files/product_texteditor_attribute_rollback.php @@ -0,0 +1,27 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +declare(strict_types=1); + +use Magento\Catalog\Api\ProductAttributeRepositoryInterface; +use Magento\Framework\Exception\NoSuchEntityException; +use Magento\Framework\Registry; +use Magento\TestFramework\Helper\Bootstrap; + +$objectManager = Bootstrap::getObjectManager(); +/** @var ProductAttributeRepositoryInterface $productAttributeRepository */ +$productAttributeRepository = $objectManager->get(ProductAttributeRepositoryInterface::class); +/** @var Registry $registry */ +$registry = $objectManager->get(Registry::class); +$registry->unregister('isSecureArea'); +$registry->register('isSecureArea', true); +try { + $attribute = $productAttributeRepository->get('text_editor_attribute'); + $productAttributeRepository->delete($attribute); +} catch (NoSuchEntityException $e) { + //Attribute already deleted. +} +$registry->unregister('isSecureArea'); +$registry->register('isSecureArea', false); diff --git a/dev/tests/integration/testsuite/Magento/Elasticsearch/Catalog/Model/Indexer/Category/Product/Action/RowsTest.php b/dev/tests/integration/testsuite/Magento/Elasticsearch/Catalog/Model/Indexer/Category/Product/Action/RowsTest.php new file mode 100644 index 0000000000000..c3aced62eecd3 --- /dev/null +++ b/dev/tests/integration/testsuite/Magento/Elasticsearch/Catalog/Model/Indexer/Category/Product/Action/RowsTest.php @@ -0,0 +1,163 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +declare(strict_types=1); + +namespace Magento\Elasticsearch\Catalog\Model\Indexer\Category\Product\Action; + +use Magento\Catalog\Api\CategoryListInterface; +use Magento\Catalog\Api\Data\CategoryInterface; +use Magento\Catalog\Api\ProductRepositoryInterface; +use Magento\Catalog\Helper\DefaultCategory; +use Magento\Catalog\Model\Indexer\Category\Product\Action\Rows; +use Magento\CatalogSearch\Model\ResourceModel\Fulltext\SearchCollectionFactory; +use Magento\Framework\Api\SearchCriteriaBuilder; +use Magento\TestFramework\Helper\Bootstrap; +use Magento\TestFramework\ObjectManager; +use Magento\TestModuleCatalogSearch\Model\ElasticsearchVersionChecker; +use Magento\Framework\Search\EngineResolverInterface; + +/** + * Test for Magento\Catalog\Model\Indexer\Category\Product\Action\Rows class. + * This test executable with any configuration of ES and should not be deleted with removal of ES2. + * + * @magentoAppIsolation enabled + * @magentoDbIsolation enabled + * @SuppressWarnings(PHPMD.CouplingBetweenObjects) + */ +class RowsTest extends \PHPUnit\Framework\TestCase +{ + /** + * @var string + */ + private $searchEngine; + + /** + * @var ObjectManager + */ + private $objectManager; + + /** + * @var Rows + */ + private $rowsIndexer; + + /** + * @var DefaultCategory + */ + private $defaultCategoryHelper; + + /** + * @var SearchCollectionFactory + */ + private $fulltextSearchCollectionFactory; + + /** + * @inheritdoc + */ + protected function setUp() + { + $this->objectManager = Bootstrap::getObjectManager(); + $this->rowsIndexer = $this->objectManager->get(Rows::class); + $this->defaultCategoryHelper = $this->objectManager->get(DefaultCategory::class); + $this->fulltextSearchCollectionFactory = $this->objectManager->get(SearchCollectionFactory::class); + } + + /** + * @inheritdoc + */ + protected function assertPreConditions() + { + $currentEngine = $this->objectManager->get(EngineResolverInterface::class)->getCurrentSearchEngine(); + $this->assertEquals($this->getInstalledSearchEngine(), $currentEngine); + } + + /** + * Returns installed on server search service. + * + * @return string + */ + private function getInstalledSearchEngine(): string + { + if (!$this->searchEngine) { + // phpstan:ignore "Class Magento\TestModuleCatalogSearch\Model\ElasticsearchVersionChecker not found." + $version = $this->objectManager->get(ElasticsearchVersionChecker::class)->getVersion(); + $this->searchEngine = 'elasticsearch' . $version; + } + + return $this->searchEngine; + } + + /** + * @magentoDataFixture Magento/Catalog/_files/category_tree_with_products.php + * @magentoDataFixture Magento/CatalogSearch/_files/full_reindex.php + * @magentoConfigFixture current_store catalog/search/elasticsearch_index_prefix indexerhandlertest + * @magentoDataFixtureBeforeTransaction Magento/Catalog/_files/enable_reindex_schedule.php + * @return void + */ + public function testLoadWithFilterCatalogView() + { + $categoryA = $this->getCategory('Category A'); + $categoryB = $this->getCategory('Category B'); + $categoryC = $this->getCategory('Category C'); + + /** Move $categoryB to $categoryA */ + $categoryB->move($categoryA->getId(), null); + $this->rowsIndexer->execute( + [ + $this->defaultCategoryHelper->getId(), + $categoryA->getId(), + $categoryB->getId(), + $categoryC->getId(), + ], + true + ); + + $fulltextCollection = $this->fulltextSearchCollectionFactory->create() + ->addCategoryFilter($categoryA); + + $this->assertProductsArePresentInCollection($fulltextCollection->getAllIds()); + } + + /** + * Assert that expected products are present in collection. + * + * @param array $productIds + * + * @return void + */ + private function assertProductsArePresentInCollection(array $productIds): void + { + /** @var ProductRepositoryInterface $productRepository */ + $productRepository = $this->objectManager->get(ProductRepositoryInterface::class); + + $firstProductId = $productRepository->get('simpleB')->getId(); + $secondProductId = $productRepository->get('simpleC')->getId(); + + $this->assertCount(2, $productIds); + $this->assertContains($secondProductId, $productIds); + $this->assertContains($firstProductId, $productIds); + } + + /** + * Gets category by name. + * + * @param string $name + * @return CategoryInterface + */ + private function getCategory(string $name): CategoryInterface + { + /** @var SearchCriteriaBuilder $searchCriteriaBuilder */ + $searchCriteriaBuilder = $this->objectManager->get(SearchCriteriaBuilder::class); + $searchCriteria = $searchCriteriaBuilder->addFilter('name', $name) + ->create(); + /** @var CategoryListInterface $repository */ + $repository = $this->objectManager->get(CategoryListInterface::class); + $items = $repository->getList($searchCriteria) + ->getItems(); + + return array_pop($items); + } +} diff --git a/dev/tests/integration/testsuite/Magento/Elasticsearch/Controller/QuickSearchTest.php b/dev/tests/integration/testsuite/Magento/Elasticsearch/Controller/QuickSearchTest.php new file mode 100644 index 0000000000000..4bbbf396d0e4d --- /dev/null +++ b/dev/tests/integration/testsuite/Magento/Elasticsearch/Controller/QuickSearchTest.php @@ -0,0 +1,31 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +declare(strict_types=1); + +namespace Magento\Elasticsearch\Controller; + +use Magento\TestFramework\TestCase\AbstractController; + +class QuickSearchTest extends AbstractController +{ + /** + * Tests quick search with "Minimum Terms to Match" sets to "100%". + * + * @magentoAppArea frontend + * @magentoDbIsolation disabled + * @magentoConfigFixture current_store catalog/search/elasticsearch7_minimum_should_match 100% + * @magentoConfigFixture current_store catalog/search/elasticsearch6_minimum_should_match 100% + * @magentoDataFixture Magento/Elasticsearch/_files/products_for_search.php + * @magentoDataFixture Magento/CatalogSearch/_files/full_reindex.php + */ + public function testQuickSearchWithImprovedPriceRangeCalculation() + { + $this->dispatch('/catalogsearch/result/?q=24+MB04'); + $responseBody = $this->getResponse()->getBody(); + $this->assertContains('search product 2', $responseBody); + $this->assertNotContains('search product 1', $responseBody); + } +} diff --git a/dev/tests/integration/testsuite/Magento/Elasticsearch/Elasticsearch5/SearchAdapter/AdapterTest.php b/dev/tests/integration/testsuite/Magento/Elasticsearch/Elasticsearch5/SearchAdapter/AdapterTest.php index a52c5bb9e21b7..d3fa3569ac004 100644 --- a/dev/tests/integration/testsuite/Magento/Elasticsearch/Elasticsearch5/SearchAdapter/AdapterTest.php +++ b/dev/tests/integration/testsuite/Magento/Elasticsearch/Elasticsearch5/SearchAdapter/AdapterTest.php @@ -10,7 +10,7 @@ use Magento\TestFramework\Helper\Bootstrap; /** - * Class AdapterTest + * Class AdapterTest to test Elasticsearch search adapter */ class AdapterTest extends \PHPUnit\Framework\TestCase { @@ -20,7 +20,7 @@ class AdapterTest extends \PHPUnit\Framework\TestCase private $adapter; /** - * @var \Magento\Elasticsearch\Model\Client\Elasticsearch|\PHPUnit\Framework\MockObject\MockObject + * @var \Magento\AdvancedSearch\Model\Client\ClientInterface|\PHPUnit\Framework\MockObject\MockObject */ private $clientMock; @@ -43,7 +43,8 @@ protected function setUp() $contentManager = $this->getMockBuilder(\Magento\Elasticsearch\SearchAdapter\ConnectionManager::class) ->disableOriginalConstructor() ->getMock(); - $this->clientMock = $this->getMockBuilder(\Magento\Elasticsearch6\Model\Client\Elasticsearch::class) + $this->clientMock = $this->getMockBuilder(\Magento\AdvancedSearch\Model\Client\ClientInterface::class) + ->setMethods(['query', 'testConnection']) ->disableOriginalConstructor() ->getMock(); $contentManager @@ -78,7 +79,6 @@ protected function setUp() /** * @magentoAppIsolation enabled - * @magentoConfigFixture default/catalog/search/engine elasticsearch6 * @magentoConfigFixture current_store catalog/search/elasticsearch_index_prefix adaptertest * @return void */ diff --git a/dev/tests/integration/testsuite/Magento/Elasticsearch/Model/Client/ElasticsearchTest.php b/dev/tests/integration/testsuite/Magento/Elasticsearch/Model/Client/ElasticsearchTest.php index 3eea2497daa1f..545a20d4a6475 100644 --- a/dev/tests/integration/testsuite/Magento/Elasticsearch/Model/Client/ElasticsearchTest.php +++ b/dev/tests/integration/testsuite/Magento/Elasticsearch/Model/Client/ElasticsearchTest.php @@ -13,6 +13,8 @@ use Magento\Elasticsearch6\Model\Client\Elasticsearch as ElasticsearchClient; use Magento\Elasticsearch\Model\Config; use Magento\Elasticsearch\SearchAdapter\SearchIndexNameResolver; +use Magento\TestModuleCatalogSearch\Model\ElasticsearchVersionChecker; +use Magento\Framework\Search\EngineResolverInterface; /** * @magentoDbIsolation enabled @@ -21,6 +23,11 @@ */ class ElasticsearchTest extends \PHPUnit\Framework\TestCase { + /** + * @var string + */ + private $searchEngine; + /** * @var ConnectionManager */ @@ -65,6 +72,15 @@ protected function setUp() $indexer->reindexAll(); } + /** + * Make sure that correct engine is set + */ + protected function assertPreConditions() + { + $currentEngine = Bootstrap::getObjectManager()->get(EngineResolverInterface::class)->getCurrentSearchEngine(); + $this->assertEquals($this->getInstalledSearchEngine(), $currentEngine); + } + /** * @param string $text * @return array @@ -95,7 +111,6 @@ private function search($text) } /** - * @magentoConfigFixture default/catalog/search/engine elasticsearch6 * @magentoConfigFixture current_store catalog/search/elasticsearch_index_prefix composite_product_search */ public function testSearchConfigurableProductBySimpleProductName() @@ -104,7 +119,6 @@ public function testSearchConfigurableProductBySimpleProductName() } /** - * @magentoConfigFixture default/catalog/search/engine elasticsearch6 * @magentoConfigFixture current_store catalog/search/elasticsearch_index_prefix composite_product_search */ public function testSearchConfigurableProductBySimpleProductAttributeMultiselect() @@ -113,7 +127,6 @@ public function testSearchConfigurableProductBySimpleProductAttributeMultiselect } /** - * @magentoConfigFixture default/catalog/search/engine elasticsearch6 * @magentoConfigFixture current_store catalog/search/elasticsearch_index_prefix composite_product_search */ public function testSearchConfigurableProductBySimpleProductAttributeSelect() @@ -122,7 +135,6 @@ public function testSearchConfigurableProductBySimpleProductAttributeSelect() } /** - * @magentoConfigFixture default/catalog/search/engine elasticsearch6 * @magentoConfigFixture current_store catalog/search/elasticsearch_index_prefix composite_product_search */ public function testSearchConfigurableProductBySimpleProductAttributeShortDescription() @@ -146,4 +158,19 @@ private function assertProductWithSkuFound($sku, array $result) } return false; } + + /** + * Returns installed on server search service + * + * @return string + */ + private function getInstalledSearchEngine() + { + if (!$this->searchEngine) { + // phpstan:ignore "Class Magento\TestModuleCatalogSearch\Model\ElasticsearchVersionChecker not found." + $version = Bootstrap::getObjectManager()->get(ElasticsearchVersionChecker::class)->getVersion(); + $this->searchEngine = 'elasticsearch' . $version; + } + return $this->searchEngine; + } } diff --git a/dev/tests/integration/testsuite/Magento/Elasticsearch/Model/Indexer/IndexHandlerTest.php b/dev/tests/integration/testsuite/Magento/Elasticsearch/Model/Indexer/IndexHandlerTest.php index 77533e83b719c..cb94df5ffbcd1 100644 --- a/dev/tests/integration/testsuite/Magento/Elasticsearch/Model/Indexer/IndexHandlerTest.php +++ b/dev/tests/integration/testsuite/Magento/Elasticsearch/Model/Indexer/IndexHandlerTest.php @@ -17,6 +17,8 @@ use Magento\Elasticsearch\Model\Config; use Magento\Elasticsearch\SearchAdapter\SearchIndexNameResolver; use Magento\Indexer\Model\Indexer; +use Magento\Framework\Search\EngineResolverInterface; +use Magento\TestModuleCatalogSearch\Model\ElasticsearchVersionChecker; /** * Important: Please make sure that each integration test file works with unique elastic search index. In order to @@ -29,6 +31,11 @@ */ class IndexHandlerTest extends \PHPUnit\Framework\TestCase { + /** + * @var string + */ + private $searchEngine; + /** * @var ProductRepositoryInterface */ @@ -87,7 +94,15 @@ protected function setUp() } /** - * @magentoConfigFixture default/catalog/search/engine elasticsearch6 + * Make sure that correct engine is set + */ + protected function assertPreConditions() + { + $currentEngine = Bootstrap::getObjectManager()->get(EngineResolverInterface::class)->getCurrentSearchEngine(); + $this->assertEquals($this->getInstalledSearchEngine(), $currentEngine); + } + + /** * @magentoConfigFixture current_store catalog/search/elasticsearch_index_prefix indexerhandlertest * @return void */ @@ -106,7 +121,6 @@ public function testReindexAll(): void /** * @magentoAppIsolation enabled - * @magentoConfigFixture default/catalog/search/engine elasticsearch6 * @magentoConfigFixture current_store catalog/search/elasticsearch_index_prefix indexerhandlertest * @return void */ @@ -131,7 +145,6 @@ public function testReindexRowAfterEdit(): void } /** - * @magentoConfigFixture default/catalog/search/engine elasticsearch6 * @magentoConfigFixture current_store catalog/search/elasticsearch_index_prefix indexerhandlertest * @return void */ @@ -170,7 +183,6 @@ public function testReindexRowAfterMassAction(): void } /** - * @magentoConfigFixture default/catalog/search/engine elasticsearch6 * @magentoConfigFixture current_store catalog/search/elasticsearch_index_prefix indexerhandlertest * @magentoAppArea adminhtml * @return void @@ -192,7 +204,6 @@ public function testReindexRowAfterDelete(): void /** * @magentoDbIsolation enabled * @magentoAppArea adminhtml - * @magentoConfigFixture default/catalog/search/engine elasticsearch6 * @magentoConfigFixture current_store catalog/search/elasticsearch_index_prefix indexerhandlertest * @magentoDataFixture Magento/Elasticsearch/_files/configurable_products.php * @return void @@ -254,4 +265,19 @@ private function searchByName(string $text, int $storeId): array return $products; } + + /** + * Returns installed on server search service + * + * @return string + */ + private function getInstalledSearchEngine() + { + if (!$this->searchEngine) { + // phpstan:ignore "Class Magento\TestModuleCatalogSearch\Model\ElasticsearchVersionChecker not found." + $version = Bootstrap::getObjectManager()->get(ElasticsearchVersionChecker::class)->getVersion(); + $this->searchEngine = 'elasticsearch' . $version; + } + return $this->searchEngine; + } } diff --git a/dev/tests/integration/testsuite/Magento/Elasticsearch/Model/Indexer/ReindexAllTest.php b/dev/tests/integration/testsuite/Magento/Elasticsearch/Model/Indexer/ReindexAllTest.php index 7d4aa8e005e4e..828e45953cca1 100644 --- a/dev/tests/integration/testsuite/Magento/Elasticsearch/Model/Indexer/ReindexAllTest.php +++ b/dev/tests/integration/testsuite/Magento/Elasticsearch/Model/Indexer/ReindexAllTest.php @@ -10,9 +10,11 @@ use Magento\TestFramework\Helper\Bootstrap; use Magento\Store\Model\StoreManagerInterface; use Magento\Elasticsearch\SearchAdapter\ConnectionManager; -use Magento\Elasticsearch\Model\Client\Elasticsearch as ElasticsearchClient; +use Magento\AdvancedSearch\Model\Client\ClientInterface as ElasticsearchClient; use Magento\Elasticsearch\Model\Config; use Magento\Elasticsearch\SearchAdapter\SearchIndexNameResolver; +use Magento\Framework\Search\EngineResolverInterface; +use Magento\TestModuleCatalogSearch\Model\ElasticsearchVersionChecker; /** * Important: Please make sure that each integration test file works with unique elastic search index. In order to @@ -22,9 +24,15 @@ * * @magentoDbIsolation disabled * @magentoAppIsolation enabled + * @SuppressWarnings(PHPMD.CouplingBetweenObjects) */ class ReindexAllTest extends \PHPUnit\Framework\TestCase { + /** + * @var string + */ + private $searchEngine; + /** * @var ConnectionManager */ @@ -65,10 +73,18 @@ protected function setUp() $this->productRepository = Bootstrap::getObjectManager()->create(ProductRepositoryInterface::class); } + /** + * Make sure that correct engine is set + */ + protected function assertPreConditions() + { + $currentEngine = Bootstrap::getObjectManager()->get(EngineResolverInterface::class)->getCurrentSearchEngine(); + $this->assertEquals($this->getInstalledSearchEngine(), $currentEngine); + } + /** * Test search of all products after full reindex * - * @magentoConfigFixture default/catalog/search/engine elasticsearch6 * @magentoConfigFixture current_store catalog/search/elasticsearch_index_prefix indexerhandlertest_configurable * @magentoDataFixture Magento/ConfigurableProduct/_files/configurable_products.php */ @@ -79,10 +95,44 @@ public function testSearchAll() self::assertGreaterThanOrEqual(2, $result); } + /** + * Test sorting of all products after full reindex + * + * @magentoDbIsolation enabled + * @magentoConfigFixture current_store catalog/search/elasticsearch_index_prefix indexerhandlertest_configurable + * @magentoDataFixture Magento/ConfigurableProduct/_files/configurable_products.php + */ + public function testSort() + { + /** @var $productFifth \Magento\Catalog\Model\Product */ + $productSimple = Bootstrap::getObjectManager()->create(\Magento\Catalog\Model\Product::class); + $productSimple->setTypeId('simple') + ->setAttributeSetId(4) + ->setWebsiteIds([1]) + ->setName('ABC') + ->setSku('abc-first-in-sort') + ->setPrice(20) + ->setMetaTitle('meta title') + ->setMetaKeyword('meta keyword') + ->setMetaDescription('meta description') + ->setVisibility(\Magento\Catalog\Model\Product\Visibility::VISIBILITY_BOTH) + ->setStatus(\Magento\Catalog\Model\Product\Attribute\Source\Status::STATUS_ENABLED) + ->setStockData(['use_config_manage_stock' => 0]) + ->save(); + $productConfigurableOption = $this->productRepository->get('simple_10'); + $productConfigurableOption->setName('1ABC'); + $this->productRepository->save($productConfigurableOption); + $this->reindexAll(); + $productSimple = $this->productRepository->get('abc-first-in-sort'); + $result = $this->sortByName(); + $firstInSearchResults = (int) $result[0]['_id']; + $productSimpleId = (int) $productSimple->getId(); + $this->assertEquals($productSimpleId, $firstInSearchResults); + } + /** * Test search of specific product after full reindex * - * @magentoConfigFixture default/catalog/search/engine elasticsearch6 * @magentoConfigFixture current_store catalog/search/elasticsearch_index_prefix indexerhandlertest_configurable * @magentoDataFixture Magento/ConfigurableProduct/_files/configurable_products.php */ @@ -125,6 +175,38 @@ private function searchByName($text) return isset($queryResult['hits']['hits']) ? $queryResult['hits']['hits'] : []; } + /** + * @return array + */ + private function sortByName() + { + $storeId = $this->storeManager->getDefaultStoreView()->getId(); + $searchQuery = [ + 'index' => $this->searchIndexNameResolver->getIndexName($storeId, 'catalogsearch_fulltext'), + 'type' => $this->clientConfig->getEntityType(), + 'body' => [ + 'sort' => [ + 'name.sort_name' => [ + 'order' => 'asc' + ], + ], + 'query' => [ + 'bool' => [ + 'must' => [ + [ + 'terms' => [ + 'visibility' => [2, 4], + ], + ], + ], + ], + ], + ], + ]; + $queryResult = $this->client->query($searchQuery); + return isset($queryResult['hits']['hits']) ? $queryResult['hits']['hits'] : []; + } + /** * Make fulltext catalog search reindex * @@ -138,4 +220,19 @@ private function reindexAll() $indexer->load('catalogsearch_fulltext'); $indexer->reindexAll(); } + + /** + * Returns installed on server search service + * + * @return string + */ + private function getInstalledSearchEngine() + { + if (!$this->searchEngine) { + // phpstan:ignore "Class Magento\TestModuleCatalogSearch\Model\ElasticsearchVersionChecker not found." + $version = Bootstrap::getObjectManager()->get(ElasticsearchVersionChecker::class)->getVersion(); + $this->searchEngine = 'elasticsearch' . $version; + } + return $this->searchEngine; + } } diff --git a/dev/tests/integration/testsuite/Magento/Elasticsearch/SearchAdapter/AdapterTest.php b/dev/tests/integration/testsuite/Magento/Elasticsearch/SearchAdapter/AdapterTest.php index a3da32e0d6c40..1a3618965ce02 100644 --- a/dev/tests/integration/testsuite/Magento/Elasticsearch/SearchAdapter/AdapterTest.php +++ b/dev/tests/integration/testsuite/Magento/Elasticsearch/SearchAdapter/AdapterTest.php @@ -5,6 +5,9 @@ */ namespace Magento\Elasticsearch\SearchAdapter; +use Magento\Framework\Search\EngineResolverInterface; +use Magento\TestModuleCatalogSearch\Model\ElasticsearchVersionChecker; + /** * Class AdapterTest * @@ -17,6 +20,7 @@ * * In ElasticSearch, a reindex is required if the test includes a new data fixture with new items to search, see * testAdvancedSearchDateField(). + * phpstan:ignore * */ class AdapterTest extends \Magento\Framework\Search\Adapter\Mysql\AdapterTest @@ -24,7 +28,7 @@ class AdapterTest extends \Magento\Framework\Search\Adapter\Mysql\AdapterTest /** * @var string */ - protected $searchEngine = 'elasticsearch6'; + protected $searchEngine; /** * Get request config path @@ -41,13 +45,22 @@ protected function getRequestConfigPath() */ protected function createAdapter() { - return $this->objectManager->create(\Magento\Elasticsearch\Elasticsearch5\SearchAdapter\Adapter::class); + return $this->objectManager->create(\Magento\Search\Model\AdapterFactory::class)->create(); + } + + /** + * Make sure that correct engine is set + */ + protected function assertPreConditions() + { + $currentEngine = $this->objectManager->get(EngineResolverInterface::class)->getCurrentSearchEngine(); + $this->assertEquals($this->getInstalledSearchEngine(), $currentEngine); } /** * @magentoAppIsolation enabled - * @magentoConfigFixture default/catalog/search/engine elasticsearch6 * @magentoConfigFixture current_store catalog/search/elasticsearch_index_prefix adaptertest + * phpcs:disable Generic.CodeAnalysis.UselessOverridingMethod */ public function testMatchQuery() { @@ -56,7 +69,6 @@ public function testMatchQuery() /** * @magentoAppIsolation enabled - * @magentoConfigFixture default/catalog/search/engine elasticsearch6 * @magentoConfigFixture current_store catalog/search/elasticsearch_index_prefix adaptertest */ public function testMatchOrderedQuery() @@ -68,7 +80,6 @@ public function testMatchOrderedQuery() /** * @magentoAppIsolation enabled - * @magentoConfigFixture default/catalog/search/engine elasticsearch6 * @magentoConfigFixture current_store catalog/search/elasticsearch_index_prefix adaptertest */ public function testAggregationsQuery() @@ -78,8 +89,8 @@ public function testAggregationsQuery() /** * @magentoAppIsolation enabled - * @magentoConfigFixture default/catalog/search/engine elasticsearch6 * @magentoConfigFixture current_store catalog/search/elasticsearch_index_prefix adaptertest + * phpcs:disable Generic.CodeAnalysis.UselessOverridingMethod */ public function testMatchQueryFilters() { @@ -90,8 +101,8 @@ public function testMatchQueryFilters() * Range filter test with all fields filled * * @magentoAppIsolation enabled - * @magentoConfigFixture default/catalog/search/engine elasticsearch6 * @magentoConfigFixture current_store catalog/search/elasticsearch_index_prefix adaptertest + * phpcs:disable Generic.CodeAnalysis.UselessOverridingMethod */ public function testRangeFilterWithAllFields() { @@ -102,8 +113,8 @@ public function testRangeFilterWithAllFields() * Range filter test with all fields filled * * @magentoAppIsolation enabled - * @magentoConfigFixture default/catalog/search/engine elasticsearch6 * @magentoConfigFixture current_store catalog/search/elasticsearch_index_prefix adaptertest + * phpcs:disable Generic.CodeAnalysis.UselessOverridingMethod */ public function testRangeFilterWithoutFromField() { @@ -114,8 +125,8 @@ public function testRangeFilterWithoutFromField() * Range filter test with all fields filled * * @magentoAppIsolation enabled - * @magentoConfigFixture default/catalog/search/engine elasticsearch6 * @magentoConfigFixture current_store catalog/search/elasticsearch_index_prefix adaptertest + * phpcs:disable Generic.CodeAnalysis.UselessOverridingMethod */ public function testRangeFilterWithoutToField() { @@ -126,8 +137,8 @@ public function testRangeFilterWithoutToField() * Term filter test * * @magentoAppIsolation enabled - * @magentoConfigFixture default/catalog/search/engine elasticsearch6 * @magentoConfigFixture current_store catalog/search/elasticsearch_index_prefix adaptertest + * phpcs:disable Generic.CodeAnalysis.UselessOverridingMethod */ public function testTermFilter() { @@ -138,8 +149,8 @@ public function testTermFilter() * Term filter test * * @magentoAppIsolation enabled - * @magentoConfigFixture default/catalog/search/engine elasticsearch6 * @magentoConfigFixture current_store catalog/search/elasticsearch_index_prefix adaptertest + * phpcs:disable Generic.CodeAnalysis.UselessOverridingMethod */ public function testTermFilterArray() { @@ -150,8 +161,8 @@ public function testTermFilterArray() * Term filter test * * @magentoAppIsolation enabled - * @magentoConfigFixture default/catalog/search/engine elasticsearch6 * @magentoConfigFixture current_store catalog/search/elasticsearch_index_prefix adaptertest + * phpcs:disable Generic.CodeAnalysis.UselessOverridingMethod */ public function testWildcardFilter() { @@ -162,8 +173,8 @@ public function testWildcardFilter() * Request limits test * * @magentoAppIsolation enabled - * @magentoConfigFixture default/catalog/search/engine elasticsearch6 * @magentoConfigFixture current_store catalog/search/elasticsearch_index_prefix adaptertest + * phpcs:disable Generic.CodeAnalysis.UselessOverridingMethod */ public function testSearchLimit() { @@ -174,8 +185,8 @@ public function testSearchLimit() * Bool filter test * * @magentoAppIsolation enabled - * @magentoConfigFixture default/catalog/search/engine elasticsearch6 * @magentoConfigFixture current_store catalog/search/elasticsearch_index_prefix adaptertest + * phpcs:disable Generic.CodeAnalysis.UselessOverridingMethod */ public function testBoolFilter() { @@ -186,8 +197,8 @@ public function testBoolFilter() * Test bool filter with nested negative bool filter * * @magentoAppIsolation enabled - * @magentoConfigFixture default/catalog/search/engine elasticsearch6 * @magentoConfigFixture current_store catalog/search/elasticsearch_index_prefix adaptertest + * phpcs:disable Generic.CodeAnalysis.UselessOverridingMethod */ public function testBoolFilterWithNestedNegativeBoolFilter() { @@ -198,8 +209,8 @@ public function testBoolFilterWithNestedNegativeBoolFilter() * Test range inside nested negative bool filter * * @magentoAppIsolation enabled - * @magentoConfigFixture default/catalog/search/engine elasticsearch6 * @magentoConfigFixture current_store catalog/search/elasticsearch_index_prefix adaptertest + * phpcs:disable Generic.CodeAnalysis.UselessOverridingMethod */ public function testBoolFilterWithNestedRangeInNegativeBoolFilter() { @@ -211,12 +222,12 @@ public function testBoolFilterWithNestedRangeInNegativeBoolFilter() * * @dataProvider elasticSearchAdvancedSearchDataProvider * @magentoAppIsolation enabled - * @magentoConfigFixture default/catalog/search/engine elasticsearch6 * @magentoConfigFixture current_store catalog/search/elasticsearch_index_prefix adaptertest * @param string $nameQuery * @param string $descriptionQuery * @param array $rangeFilter * @param int $expectedRecordsCount + * phpcs:disable Generic.CodeAnalysis.UselessOverridingMethod */ public function testSimpleAdvancedSearch( $nameQuery, @@ -257,7 +268,6 @@ public function elasticSearchAdvancedSearchDataProvider() /** * @magentoAppIsolation enabled * @magentoDataFixture Magento/Framework/Search/_files/filterable_attribute.php - * @magentoConfigFixture default/catalog/search/engine elasticsearch6 * @magentoConfigFixture current_store catalog/search/elasticsearch_index_prefix adaptertest */ public function testCustomFilterableAttribute() @@ -272,7 +282,6 @@ public function testCustomFilterableAttribute() * * @magentoAppIsolation enabled * @magentoDataFixture Magento/Framework/Search/_files/filterable_attributes.php - * @magentoConfigFixture default/catalog/search/engine elasticsearch6 * @magentoConfigFixture current_store catalog/search/elasticsearch_index_prefix adaptertest * @dataProvider filterByAttributeValuesDataProvider * @param string $requestName @@ -292,7 +301,6 @@ public function testFilterByAttributeValues($requestName, $additionalData) * @param $rangeFilter * @param $expectedRecordsCount * @magentoDataFixture Magento/Framework/Search/_files/date_attribute.php - * @magentoConfigFixture default/catalog/search/engine elasticsearch6 * @magentoConfigFixture current_store catalog/search/elasticsearch_index_prefix adaptertest * @magentoAppIsolation enabled * @dataProvider dateDataProvider @@ -307,8 +315,8 @@ public function testAdvancedSearchDateField($rangeFilter, $expectedRecordsCount) /** * @magentoDataFixture Magento/Framework/Search/_files/product_configurable.php * @magentoAppIsolation enabled - * @magentoConfigFixture default/catalog/search/engine elasticsearch6 * @magentoConfigFixture current_store catalog/search/elasticsearch_index_prefix adaptertest + * phpcs:disable Generic.CodeAnalysis.UselessOverridingMethod */ public function testAdvancedSearchCompositeProductWithOutOfStockOption() { @@ -318,7 +326,6 @@ public function testAdvancedSearchCompositeProductWithOutOfStockOption() /** * @magentoDataFixture Magento/Framework/Search/_files/product_configurable_with_disabled_child.php * @magentoAppIsolation enabled - * @magentoConfigFixture default/catalog/search/engine elasticsearch6 * @magentoConfigFixture current_store catalog/search/elasticsearch_index_prefix adaptertest */ public function testAdvancedSearchCompositeProductWithDisabledChild() @@ -331,7 +338,6 @@ public function testAdvancedSearchCompositeProductWithDisabledChild() /** * @magentoDataFixture Magento/Framework/Search/_files/search_weight_products.php * @magentoAppIsolation enabled - * @magentoConfigFixture default/catalog/search/engine elasticsearch6 * @magentoConfigFixture current_store catalog/search/elasticsearch_index_prefix adaptertest */ public function testSearchQueryBoost() @@ -381,4 +387,19 @@ public function filterByAttributeValuesDataProvider() return $variations; } + + /** + * Returns installed on server search service + * + * @return string + */ + private function getInstalledSearchEngine() + { + if (!$this->searchEngine) { + // phpstan:ignore "Class Magento\TestModuleCatalogSearch\Model\ElasticsearchVersionChecker not found." + $version = $this->objectManager->get(ElasticsearchVersionChecker::class)->getVersion(); + $this->searchEngine = 'elasticsearch' . $version; + } + return $this->searchEngine; + } } diff --git a/dev/tests/integration/testsuite/Magento/Elasticsearch/_files/products_for_search.php b/dev/tests/integration/testsuite/Magento/Elasticsearch/_files/products_for_search.php new file mode 100644 index 0000000000000..4f271d2a78a39 --- /dev/null +++ b/dev/tests/integration/testsuite/Magento/Elasticsearch/_files/products_for_search.php @@ -0,0 +1,108 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +declare(strict_types=1); + +include __DIR__ . '/../../Catalog/_files/category.php'; + +use Magento\Catalog\Api\CategoryLinkManagementInterface; +use Magento\Catalog\Model\Product; +use Magento\Catalog\Model\Product\Attribute\Source\Status; +use Magento\Catalog\Model\Product\Visibility; + +$categoryId = 333; +$objectManager = \Magento\TestFramework\Helper\Bootstrap::getObjectManager(); +$attributeSet = $objectManager->create(\Magento\Eav\Model\Entity\Attribute\Set::class); +$entityType = $objectManager->create(\Magento\Eav\Model\Entity\Type::class)->loadByCode('catalog_product'); +$defaultSetId = $objectManager->create(\Magento\Catalog\Model\Product::class)->getDefaultAttributeSetid(); + +$products = [ + [ + 'type' => 'simple', + 'name' => 'search product 1', + 'sku' => '24 MB06', + 'status' => Status::STATUS_ENABLED, + 'visibility' => Visibility::VISIBILITY_BOTH, + 'attribute_set' => $defaultSetId, + 'website_ids' => [\Magento\Store\Model\Store::DISTRO_STORE_ID], + 'price' => 10, + 'category_id' => $categoryId, + 'meta_title' => 'Key Title', + 'meta_keyword' => 'meta keyword', + 'meta_description' => 'meta description', + ], + [ + 'type' => 'simple', + 'name' => 'search product 2', + 'sku' => '24 MB04', + 'status' => Status::STATUS_ENABLED, + 'visibility' => Visibility::VISIBILITY_BOTH, + 'attribute_set' => $defaultSetId, + 'website_ids' => [\Magento\Store\Model\Store::DISTRO_STORE_ID], + 'price' => 10, + 'category_id' => $categoryId, + 'meta_title' => 'Last Title', + 'meta_keyword' => 'meta keyword', + 'meta_description' => 'meta description', + ], + [ + 'type' => 'simple', + 'name' => 'search product 3', + 'sku' => '24 MB02', + 'status' => Status::STATUS_ENABLED, + 'visibility' => Visibility::VISIBILITY_BOTH, + 'attribute_set' => $defaultSetId, + 'website_ids' => [\Magento\Store\Model\Store::DISTRO_STORE_ID], + 'price' => 20, + 'category_id' => $categoryId, + 'meta_title' => 'First Title', + 'meta_keyword' => 'meta keyword', + 'meta_description' => 'meta description', + ], + [ + 'type' => 'simple', + 'name' => 'search product 4', + 'sku' => '24 MB01', + 'status' => Status::STATUS_ENABLED, + 'visibility' => Visibility::VISIBILITY_BOTH, + 'attribute_set' => $defaultSetId, + 'website_ids' => [\Magento\Store\Model\Store::DISTRO_STORE_ID], + 'price' => 30, + 'category_id' => $categoryId, + 'meta_title' => 'A title', + 'meta_keyword' => 'meta keyword', + 'meta_description' => 'meta description', + ], +]; + +/** @var CategoryLinkManagementInterface $categoryLinkManagement */ +$categoryLinkManagement = $objectManager->create(CategoryLinkManagementInterface::class); + +$categoriesToAssign = []; + +foreach ($products as $data) { + /** @var $product Product */ + $product = $objectManager->create(Product::class); + $product + ->setTypeId($data['type']) + ->setAttributeSetId($data['attribute_set']) + ->setWebsiteIds($data['website_ids']) + ->setName($data['name']) + ->setSku($data['sku']) + ->setPrice($data['price']) + ->setMetaTitle($data['meta_title']) + ->setMetaKeyword($data['meta_keyword']) + ->setMetaDescription($data['meta_keyword']) + ->setVisibility($data['visibility']) + ->setStatus($data['status']) + ->setStockData(['use_config_manage_stock' => 0]) + ->save(); + + $categoriesToAssign[$data['sku']][] = $data['category_id']; +} + +foreach ($categoriesToAssign as $sku => $categoryIds) { + $categoryLinkManagement->assignProductToCategories($sku, $categoryIds); +} diff --git a/dev/tests/integration/testsuite/Magento/Elasticsearch/_files/products_for_search_rollback.php b/dev/tests/integration/testsuite/Magento/Elasticsearch/_files/products_for_search_rollback.php new file mode 100644 index 0000000000000..120816c38232d --- /dev/null +++ b/dev/tests/integration/testsuite/Magento/Elasticsearch/_files/products_for_search_rollback.php @@ -0,0 +1,36 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +declare(strict_types=1); + +use Magento\Catalog\Api\ProductRepositoryInterface; +use Magento\Framework\Exception\NoSuchEntityException; +use Magento\Framework\Registry; +use Magento\TestFramework\Helper\Bootstrap; + +Bootstrap::getInstance()->getInstance()->reinitialize(); + +/** @var Registry $registry */ +$registry = Bootstrap::getObjectManager()->get(Registry::class); + +$registry->unregister('isSecureArea'); +$registry->register('isSecureArea', true); + +/** @var ProductRepositoryInterface $productRepository */ +$productRepository = Bootstrap::getObjectManager()->get(ProductRepositoryInterface::class); + +$productSkus = ['24 MB06', '24 MB04', '24 MB02', '24 MB01']; +foreach ($productSkus as $sku) { + try { + $product = $productRepository->get($sku, false, null, true); + $productRepository->delete($product); + } catch (NoSuchEntityException $e) { + } +} + +include __DIR__ . '/../../Catalog/_files/category_rollback.php'; + +$registry->unregister('isSecureArea'); +$registry->register('isSecureArea', false); diff --git a/dev/tests/integration/testsuite/Magento/Elasticsearch6/CatalogSearch/Controller/Advanced/ResultTest.php b/dev/tests/integration/testsuite/Magento/Elasticsearch6/CatalogSearch/Controller/Advanced/ResultTest.php deleted file mode 100644 index 5f20c1bf82062..0000000000000 --- a/dev/tests/integration/testsuite/Magento/Elasticsearch6/CatalogSearch/Controller/Advanced/ResultTest.php +++ /dev/null @@ -1,37 +0,0 @@ -<?php -/** - * Copyright © Magento, Inc. All rights reserved. - * See COPYING.txt for license details. - */ -declare(strict_types=1); - -namespace Magento\Elasticsearch6\CatalogSearch\Controller\Advanced; - -use Magento\CatalogSearch\Controller\Advanced\ResultTest as CatalogSearchResultTest; - -/** - * Test cases for catalog advanced search using Elasticsearch 6.0+ search engine. - * - * @magentoDbIsolation disabled - * @magentoAppIsolation enabled - */ -class ResultTest extends CatalogSearchResultTest -{ - /** - * Advanced search test by difference product attributes. - * - * @magentoAppArea frontend - * @magentoConfigFixture default/catalog/search/engine elasticsearch6 - * @magentoDataFixture Magento/CatalogSearch/_files/product_for_search.php - * @magentoDataFixture Magento/CatalogSearch/_files/full_reindex.php - * @dataProvider searchStringDataProvider - * phpcs:disable Generic.CodeAnalysis.UselessOverridingMethod - * - * @param array $searchParams - * @return void - */ - public function testExecute(array $searchParams): void - { - parent::testExecute($searchParams); - } -} diff --git a/dev/tests/integration/testsuite/Magento/Elasticsearch6/CatalogSearch/Controller/Result/IndexTest.php b/dev/tests/integration/testsuite/Magento/Elasticsearch6/CatalogSearch/Controller/Result/IndexTest.php deleted file mode 100644 index 492983eb8726d..0000000000000 --- a/dev/tests/integration/testsuite/Magento/Elasticsearch6/CatalogSearch/Controller/Result/IndexTest.php +++ /dev/null @@ -1,37 +0,0 @@ -<?php -/** - * Copyright © Magento, Inc. All rights reserved. - * See COPYING.txt for license details. - */ -declare(strict_types=1); - -namespace Magento\Elasticsearch6\CatalogSearch\Controller\Result; - -use Magento\CatalogSearch\Controller\Result\IndexTest as CatalogSearchIndexTest; - -/** - * Test cases for catalog quick search using Elasticsearch 6.0+ search engine. - * - * @magentoDbIsolation disabled - * @magentoAppIsolation enabled - */ -class IndexTest extends CatalogSearchIndexTest -{ - /** - * Quick search test by difference product attributes. - * - * @magentoAppArea frontend - * @magentoConfigFixture default/catalog/search/engine elasticsearch6 - * @magentoDataFixture Magento/CatalogSearch/_files/product_for_search.php - * @magentoDataFixture Magento/CatalogSearch/_files/full_reindex.php - * @dataProvider searchStringDataProvider - * phpcs:disable Generic.CodeAnalysis.UselessOverridingMethod - * - * @param string $searchString - * @return void - */ - public function testExecute(string $searchString): void - { - parent::testExecute($searchString); - } -} diff --git a/dev/tests/integration/testsuite/Magento/Elasticsearch6/CatalogSearch/Model/Indexer/fulltext/Action/DataProviderTest.php b/dev/tests/integration/testsuite/Magento/Elasticsearch6/CatalogSearch/Model/Indexer/fulltext/Action/DataProviderTest.php deleted file mode 100644 index b50d9034c0f88..0000000000000 --- a/dev/tests/integration/testsuite/Magento/Elasticsearch6/CatalogSearch/Model/Indexer/fulltext/Action/DataProviderTest.php +++ /dev/null @@ -1,32 +0,0 @@ -<?php -/** - * Copyright © Magento, Inc. All rights reserved. - * See COPYING.txt for license details. - */ -declare(strict_types=1); - -namespace Magento\Elasticsearch6\CatalogSearch\Model\Indexer\fulltext\Action; - -use Magento\CatalogSearch\Model\Indexer\Fulltext\Action\DataProviderTest as CatalogSearchDataProviderTest; - -/** - * Search products by attribute value using Elasticsearch 6.0+ search engine. - */ -class DataProviderTest extends CatalogSearchDataProviderTest -{ - /** - * Search product by custom attribute value. - * - * @magentoConfigFixture default/catalog/search/engine elasticsearch6 - * @magentoDataFixture Magento/CatalogSearch/_files/product_for_search.php - * @magentoDataFixture Magento/CatalogSearch/_files/full_reindex.php - * @magentoDbIsolation disabled - * phpcs:disable Generic.CodeAnalysis.UselessOverridingMethod - * - * @return void - */ - public function testSearchProductByAttribute(): void - { - parent::testSearchProductByAttribute(); - } -} diff --git a/dev/tests/integration/testsuite/Magento/Elasticsearch6/CatalogSearch/Model/Search/AttributeSearchWeightTest.php b/dev/tests/integration/testsuite/Magento/Elasticsearch6/CatalogSearch/Model/Search/AttributeSearchWeightTest.php index 71dfebe5a4e84..84fec6dafd089 100644 --- a/dev/tests/integration/testsuite/Magento/Elasticsearch6/CatalogSearch/Model/Search/AttributeSearchWeightTest.php +++ b/dev/tests/integration/testsuite/Magento/Elasticsearch6/CatalogSearch/Model/Search/AttributeSearchWeightTest.php @@ -8,10 +8,11 @@ namespace Magento\Elasticsearch6\CatalogSearch\Model\Search; use Magento\CatalogSearch\Model\Search\AttributeSearchWeightTest as CatalogSearchAttributeSearchWeightTest; +use Magento\TestModuleCatalogSearch\Model\ElasticsearchVersionChecker; +use Magento\TestFramework\Helper\Bootstrap; /** * Test founded products order after quick search with changed attribute search weight - * using Elasticsearch 6.0+ search engine. * * @magentoAppIsolation enabled */ @@ -20,7 +21,6 @@ class AttributeSearchWeightTest extends CatalogSearchAttributeSearchWeightTest /** * Perform search by word and check founded product order in different cases. * - * @magentoConfigFixture default/catalog/search/engine elasticsearch6 * @magentoDataFixture Magento/CatalogSearch/_files/products_for_sku_search_weight_score.php * @magentoDataFixture Magento/CatalogSearch/_files/full_reindex.php * @dataProvider attributeSearchWeightDataProvider diff --git a/dev/tests/integration/testsuite/Magento/Elasticsearch6/Controller/QuickSearchTest.php b/dev/tests/integration/testsuite/Magento/Elasticsearch6/Controller/QuickSearchTest.php new file mode 100644 index 0000000000000..eb6cf6971573c --- /dev/null +++ b/dev/tests/integration/testsuite/Magento/Elasticsearch6/Controller/QuickSearchTest.php @@ -0,0 +1,69 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +declare(strict_types=1); + +namespace Magento\Elasticsearch6\Controller; + +use Magento\Store\Model\StoreManagerInterface; +use Magento\TestFramework\TestCase\AbstractController; +use Magento\TestModuleCatalogSearch\Model\ElasticsearchVersionChecker; + +/** + * Tests quick search on Storefront. + */ +class QuickSearchTest extends AbstractController +{ + /** + * @var StoreManagerInterface + */ + private $storeManager; + + /** + * @inheritdoc + */ + protected function setUp() + { + parent::setUp(); + // phpstan:ignore "Class Magento\TestModuleCatalogSearch\Model\ElasticsearchVersionChecker not found." + $checker = $this->_objectManager->get(ElasticsearchVersionChecker::class); + + if ($checker->getVersion() !== 6) { + $this->markTestSkipped('The installed elasticsearch version isn\'t supported by test'); + } + $this->storeManager = $this->_objectManager->get(StoreManagerInterface::class); + } + + /** + * Tests quick search with "Price Navigation Step Calculation" sets to "Automatic (equalize product counts)". + * + * @magentoAppArea frontend + * @magentoDbIsolation disabled + * @magentoConfigFixture fixturestore_store catalog/layered_navigation/price_range_calculation improved + * @magentoConfigFixture fixturestore_store catalog/layered_navigation/one_price_interval 1 + * @magentoConfigFixture fixturestore_store catalog/layered_navigation/interval_division_limit 1 + * @magentoConfigFixture default/catalog/search/engine elasticsearch6 + * @magentoConfigFixture default_store catalog/search/elasticsearch6_index_prefix storefront_quick_search + * @magentoConfigFixture fixturestore_store catalog/search/elasticsearch6_index_prefix storefront_quick_search + * @magentoDataFixture Magento/Catalog/_files/products_for_search.php + * @magentoDataFixture Magento/Store/_files/core_fixturestore.php + * @magentoDataFixture Magento/Elasticsearch6/_files/full_reindex.php + */ + public function testQuickSearchWithImprovedPriceRangeCalculation() + { + $secondStore = $this->storeManager->getStore('fixturestore'); + $this->storeManager->setCurrentStore($secondStore); + + try { + $this->dispatch('/catalogsearch/result/?q=search+product'); + $responseBody = $this->getResponse()->getBody(); + } finally { + $defaultStore = $this->storeManager->getStore('default'); + $this->storeManager->setCurrentStore($defaultStore); + } + + $this->assertContains('search product 1', $responseBody); + } +} diff --git a/dev/tests/integration/testsuite/Magento/Elasticsearch6/_files/full_reindex.php b/dev/tests/integration/testsuite/Magento/Elasticsearch6/_files/full_reindex.php new file mode 100644 index 0000000000000..66c556d25ae19 --- /dev/null +++ b/dev/tests/integration/testsuite/Magento/Elasticsearch6/_files/full_reindex.php @@ -0,0 +1,13 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +declare(strict_types=1); + +$checker = \Magento\TestFramework\Helper\Bootstrap::getObjectManager()->get( + \Magento\TestModuleCatalogSearch\Model\ElasticsearchVersionChecker::class +); +if ($checker->getVersion() === 6) { + include __DIR__ . '/../../../Magento/CatalogSearch/_files/full_reindex.php'; +} diff --git a/dev/tests/integration/testsuite/Magento/Email/Model/_files/design/adminhtml/Magento/default/registration.php b/dev/tests/integration/testsuite/Magento/Email/Model/_files/design/adminhtml/Magento/default/registration.php index 4ef58e212e553..fd9a15a8185c7 100644 --- a/dev/tests/integration/testsuite/Magento/Email/Model/_files/design/adminhtml/Magento/default/registration.php +++ b/dev/tests/integration/testsuite/Magento/Email/Model/_files/design/adminhtml/Magento/default/registration.php @@ -4,6 +4,6 @@ * See COPYING.txt for license details. */ -use \Magento\Framework\Component\ComponentRegistrar; +use Magento\Framework\Component\ComponentRegistrar; ComponentRegistrar::register(ComponentRegistrar::THEME, 'adminhtml/Magento_EmailTest/default', __DIR__); diff --git a/dev/tests/integration/testsuite/Magento/Email/Model/_files/design/adminhtml/Vendor/custom_theme/registration.php b/dev/tests/integration/testsuite/Magento/Email/Model/_files/design/adminhtml/Vendor/custom_theme/registration.php index dac1b36e4880f..b03665afa7861 100644 --- a/dev/tests/integration/testsuite/Magento/Email/Model/_files/design/adminhtml/Vendor/custom_theme/registration.php +++ b/dev/tests/integration/testsuite/Magento/Email/Model/_files/design/adminhtml/Vendor/custom_theme/registration.php @@ -4,6 +4,6 @@ * See COPYING.txt for license details. */ -use \Magento\Framework\Component\ComponentRegistrar; +use Magento\Framework\Component\ComponentRegistrar; ComponentRegistrar::register(ComponentRegistrar::THEME, 'adminhtml/Vendor_EmailTest/custom_theme', __DIR__); diff --git a/dev/tests/integration/testsuite/Magento/Email/Model/_files/design/adminhtml/Vendor/default/registration.php b/dev/tests/integration/testsuite/Magento/Email/Model/_files/design/adminhtml/Vendor/default/registration.php index 879ba0266769e..e38e516d784e2 100644 --- a/dev/tests/integration/testsuite/Magento/Email/Model/_files/design/adminhtml/Vendor/default/registration.php +++ b/dev/tests/integration/testsuite/Magento/Email/Model/_files/design/adminhtml/Vendor/default/registration.php @@ -4,6 +4,6 @@ * See COPYING.txt for license details. */ -use \Magento\Framework\Component\ComponentRegistrar; +use Magento\Framework\Component\ComponentRegistrar; ComponentRegistrar::register(ComponentRegistrar::THEME, 'adminhtml/Vendor_EmailTest/default', __DIR__); diff --git a/dev/tests/integration/testsuite/Magento/Email/Model/_files/design/frontend/Magento/default/registration.php b/dev/tests/integration/testsuite/Magento/Email/Model/_files/design/frontend/Magento/default/registration.php index 92f1ead3c6d55..2cba0d2b2378c 100644 --- a/dev/tests/integration/testsuite/Magento/Email/Model/_files/design/frontend/Magento/default/registration.php +++ b/dev/tests/integration/testsuite/Magento/Email/Model/_files/design/frontend/Magento/default/registration.php @@ -4,6 +4,6 @@ * See COPYING.txt for license details. */ -use \Magento\Framework\Component\ComponentRegistrar; +use Magento\Framework\Component\ComponentRegistrar; ComponentRegistrar::register(ComponentRegistrar::THEME, 'frontend/Magento_EmailTest/default', __DIR__); diff --git a/dev/tests/integration/testsuite/Magento/Email/Model/_files/design/frontend/Vendor/custom_theme/registration.php b/dev/tests/integration/testsuite/Magento/Email/Model/_files/design/frontend/Vendor/custom_theme/registration.php index d2953eb161503..39d4b3364aa0c 100644 --- a/dev/tests/integration/testsuite/Magento/Email/Model/_files/design/frontend/Vendor/custom_theme/registration.php +++ b/dev/tests/integration/testsuite/Magento/Email/Model/_files/design/frontend/Vendor/custom_theme/registration.php @@ -4,6 +4,6 @@ * See COPYING.txt for license details. */ -use \Magento\Framework\Component\ComponentRegistrar; +use Magento\Framework\Component\ComponentRegistrar; ComponentRegistrar::register(ComponentRegistrar::THEME, 'frontend/Vendor_EmailTest/custom_theme', __DIR__); diff --git a/dev/tests/integration/testsuite/Magento/Email/Model/_files/design/frontend/Vendor/default/registration.php b/dev/tests/integration/testsuite/Magento/Email/Model/_files/design/frontend/Vendor/default/registration.php index 3d310791d8f48..3f46f3beb6962 100644 --- a/dev/tests/integration/testsuite/Magento/Email/Model/_files/design/frontend/Vendor/default/registration.php +++ b/dev/tests/integration/testsuite/Magento/Email/Model/_files/design/frontend/Vendor/default/registration.php @@ -4,6 +4,6 @@ * See COPYING.txt for license details. */ -use \Magento\Framework\Component\ComponentRegistrar; +use Magento\Framework\Component\ComponentRegistrar; ComponentRegistrar::register(ComponentRegistrar::THEME, 'frontend/Vendor_EmailTest/default', __DIR__); diff --git a/dev/tests/integration/testsuite/Magento/Framework/App/AreaTest.php b/dev/tests/integration/testsuite/Magento/Framework/App/AreaTest.php index 64ff52ff4ec4d..c18b1ecfa4868 100644 --- a/dev/tests/integration/testsuite/Magento/Framework/App/AreaTest.php +++ b/dev/tests/integration/testsuite/Magento/Framework/App/AreaTest.php @@ -5,7 +5,7 @@ */ namespace Magento\Framework\App; -use Zend\Stdlib\Parameters; +use Laminas\Stdlib\Parameters; class AreaTest extends \PHPUnit\Framework\TestCase { diff --git a/dev/tests/integration/testsuite/Magento/Framework/App/Cache/Frontend/FactoryTest.php b/dev/tests/integration/testsuite/Magento/Framework/App/Cache/Frontend/FactoryTest.php index e6ee5297e4532..a79f5511f1533 100644 --- a/dev/tests/integration/testsuite/Magento/Framework/App/Cache/Frontend/FactoryTest.php +++ b/dev/tests/integration/testsuite/Magento/Framework/App/Cache/Frontend/FactoryTest.php @@ -63,6 +63,7 @@ public function testRemoteSynchronizedCache() //Removing data sleep(2); $this->assertTrue($this->model->remove($secondIdentifier)); + $this->assertTrue($this->model->remove($identifier)); $this->assertEquals($this->model->load($identifier), false); $this->assertEquals($this->model->load($secondIdentifier), false); @@ -73,11 +74,5 @@ public function testRemoteSynchronizedCache() //Checking data $this->assertEquals($this->model->load($identifier), $data); $this->assertEquals($this->model->load($secondIdentifier), $secondData); - - //Removing data - sleep(2); - $this->assertTrue($this->model->remove($identifier)); - $this->assertEquals($this->model->load($identifier), false); - $this->assertEquals($this->model->load($secondIdentifier), false); } } diff --git a/dev/tests/integration/testsuite/Magento/Framework/App/ControllerActionTest.php b/dev/tests/integration/testsuite/Magento/Framework/App/ControllerActionTest.php new file mode 100644 index 0000000000000..12be7607e1872 --- /dev/null +++ b/dev/tests/integration/testsuite/Magento/Framework/App/ControllerActionTest.php @@ -0,0 +1,207 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ + +declare(strict_types=1); + +namespace Magento\Framework\App; + +use Magento\Backend\Model\Auth as BackendAuth; +use Magento\Backend\Model\UrlInterface as BackendUrl; +use Magento\Framework\App\TestStubs\InheritanceBasedBackendAction; +use Magento\Framework\App\TestStubs\InheritanceBasedFrontendAction; +use Magento\Framework\App\TestStubs\InterfaceOnlyBackendAction; +use Magento\Framework\App\TestStubs\InterfaceOnlyFrontendAction; +use Magento\Framework\Event; +use Magento\TestFramework\Bootstrap as TestFramework; +use Magento\TestFramework\ObjectManager; +use Magento\TestFramework\Request as TestHttpRequest; +use PHPUnit\Framework\TestCase; + +/** + * @magentoAppIsolation enabled + * @SuppressWarnings(PHPMD.CouplingBetweenObjects) + * @SuppressWarnings(PHPMD.UnusedLocalVariable) + * @SuppressWarnings(PHPMD.StaticAccess) + */ +class ControllerActionTest extends TestCase +{ + public function setupEventManagerSpy() + { + /** @var ObjectManager $objectManager */ + $objectManager = ObjectManager::getInstance(); + + $originalEventManager = $objectManager->create(Event\ManagerInterface::class); + $eventManagerSpy = new class($originalEventManager) implements Event\ManagerInterface { + /** + * @var Event\ManagerInterface + */ + private $delegate; + + /** + * @var array[]; + */ + private $dispatchedEvents = []; + + public function __construct(Event\ManagerInterface $delegate) + { + $this->delegate = $delegate; + } + + public function dispatch($eventName, array $data = []) + { + $this->dispatchedEvents[$eventName][] = [$eventName, $data]; + $this->delegate->dispatch($eventName, $data); + } + + public function spyOnDispatchedEvent(string $eventName): array + { + return $this->dispatchedEvents[$eventName] ?? []; + } + }; + + $objectManager->addSharedInstance($eventManagerSpy, get_class($originalEventManager)); + } + + private function assertEventDispatchCount($eventName, $expectedCount) + { + $message = sprintf('Event %s was expected to be dispatched %d time(s).', $eventName, $expectedCount); + $this->assertCount($expectedCount, $this->getEventManager()->spyOnDispatchedEvent($eventName), $message); + } + + /** + * @return TestHttpRequest + */ + private function getRequest(): RequestInterface + { + return ObjectManager::getInstance()->get(TestHttpRequest::class); + } + + private function fakeAuthenticatedBackendRequest() + { + $objectManager = ObjectManager::getInstance(); + $objectManager->get(BackendUrl::class)->turnOffSecretKey(); + + $auth = $objectManager->get(BackendAuth::class); + $auth->login(TestFramework::ADMIN_NAME, TestFramework::ADMIN_PASSWORD); + } + + private function configureRequestForAction(string $route, string $actionPath, string $actionName) + { + $request = $this->getRequest(); + + $request->setRouteName($route); + $request->setControllerName($actionPath); + $request->setActionName($actionName); + $request->setDispatched(); + } + + private function getEventManager(): Event\ManagerInterface + { + return ObjectManager::getInstance()->get(Event\ManagerInterface::class); + } + + private function assertPreAndPostDispatchEventsAreDispatched() + { + $this->assertEventDispatchCount('controller_action_predispatch', 1); + $this->assertEventDispatchCount('controller_action_predispatch_testroute', 1); + $this->assertEventDispatchCount('controller_action_predispatch_testroute_actionpath_actionname', 1); + $this->assertEventDispatchCount('controller_action_postdispatch_testroute_actionpath_actionname', 1); + $this->assertEventDispatchCount('controller_action_postdispatch_testroute', 1); + $this->assertEventDispatchCount('controller_action_postdispatch', 1); + } + + /** + * @magentoAppArea frontend + */ + public function testInheritanceBasedFrontendActionDispatchesEvents() + { + $this->setupEventManagerSpy(); + + /** @var InheritanceBasedFrontendAction $action */ + $action = ObjectManager::getInstance()->create(InheritanceBasedFrontendAction::class); + $this->configureRequestForAction('testroute', 'actionpath', 'actionname'); + + $action->dispatch($this->getRequest()); + + $this->assertPreAndPostDispatchEventsAreDispatched(); + } + + /** + * @magentoAppArea frontend + */ + public function testInterfaceOnlyFrontendActionDispatchesEvents() + { + $this->setupEventManagerSpy(); + + /** @var InterfaceOnlyFrontendAction $action */ + $action = ObjectManager::getInstance()->create(InterfaceOnlyFrontendAction::class); + $this->configureRequestForAction('testroute', 'actionpath', 'actionname'); + + $action->execute(); + + $this->assertPreAndPostDispatchEventsAreDispatched(); + } + + /** + * @magentoAppArea adminhtml + */ + public function testInheritanceBasedAdminhtmlActionDispatchesEvents() + { + $this->fakeAuthenticatedBackendRequest(); + + $this->setupEventManagerSpy(); + + /** @var InheritanceBasedBackendAction $action */ + $action = ObjectManager::getInstance()->create(InheritanceBasedBackendAction::class); + $this->configureRequestForAction('testroute', 'actionpath', 'actionname'); + + $action->dispatch($this->getRequest()); + + $this->assertPreAndPostDispatchEventsAreDispatched(); + } + + /** + * @magentoAppArea adminhtml + */ + public function testInterfaceOnlyAdminhtmlActionDispatchesEvents() + { + $this->setupEventManagerSpy(); + + /** @var InterfaceOnlyBackendAction $action */ + $action = ObjectManager::getInstance()->create(InterfaceOnlyBackendAction::class); + $this->configureRequestForAction('testroute', 'actionpath', 'actionname'); + + $action->execute(); + + $this->assertPreAndPostDispatchEventsAreDispatched(); + } + + /** + * @magentoAppArea frontend + */ + public function testSettingTheNoDispatchActionFlagProhibitsExecuteAndPostdispatchEvents() + { + $this->setupEventManagerSpy(); + + /** @var InterfaceOnlyFrontendAction $action */ + $action = ObjectManager::getInstance()->create(InterfaceOnlyFrontendAction::class); + $this->configureRequestForAction('testroute', 'actionpath', 'actionname'); + + /** @var ActionFlag $actionFlag */ + $actionFlag = ObjectManager::getInstance()->get(ActionFlag::class); + $actionFlag->set('', ActionInterface::FLAG_NO_DISPATCH, true); + + $action->execute(); + + $this->assertFalse($action->isExecuted(), 'The controller execute() method was not expected to be called.'); + $this->assertEventDispatchCount('controller_action_predispatch', 1); + $this->assertEventDispatchCount('controller_action_predispatch_testroute', 1); + $this->assertEventDispatchCount('controller_action_predispatch_testroute_actionpath_actionname', 1); + $this->assertEventDispatchCount('controller_action_postdispatch_testroute_actionpath_actionname', 0); + $this->assertEventDispatchCount('controller_action_postdispatch_testroute', 0); + $this->assertEventDispatchCount('controller_action_postdispatch', 0); + } +} diff --git a/dev/tests/integration/testsuite/Magento/Framework/App/Filesystem/CreatePdfFileTest.php b/dev/tests/integration/testsuite/Magento/Framework/App/Filesystem/CreatePdfFileTest.php index 9ac778da91f29..d7b492bf5153c 100644 --- a/dev/tests/integration/testsuite/Magento/Framework/App/Filesystem/CreatePdfFileTest.php +++ b/dev/tests/integration/testsuite/Magento/Framework/App/Filesystem/CreatePdfFileTest.php @@ -12,7 +12,7 @@ use Magento\Framework\App\Response\Http\FileFactory; use Magento\Framework\Filesystem; use Magento\TestFramework\Helper\Bootstrap; -use Zend\Http\Header\ContentType; +use Laminas\Http\Header\ContentType; /** * Class CreatePdfFileTest diff --git a/dev/tests/integration/testsuite/Magento/Framework/App/Language/DictionaryTest.php b/dev/tests/integration/testsuite/Magento/Framework/App/Language/DictionaryTest.php index 363f84627350f..bc299053bd4fe 100644 --- a/dev/tests/integration/testsuite/Magento/Framework/App/Language/DictionaryTest.php +++ b/dev/tests/integration/testsuite/Magento/Framework/App/Language/DictionaryTest.php @@ -63,7 +63,9 @@ public function dictionaryDataProvider() // Second case with inheritance of package with the same language code 'a case with inheritance similar language code' => $this->getDataInheritanceWitSimilarCode(), // Third case with circular inheritance, when two packages depend on each other - 'a case with circular inheritance' => $this->getDataCircularInheritance() + 'a case with circular inheritance' => $this->getDataCircularInheritance(), + // Fourth case with multiple inheritance from dev docs + 'a case with multiple inheritance from dev docs' => $this->getDataMultipleInheritanceFromDevDocs() ]; } @@ -113,7 +115,7 @@ private function getDataCircularInheritance() { return [ // Dictionary that will be requested - 'language_code' => 'en_US', + 'language_code' => 'en_AZ', // Expected merged dictionary data 'expectation' => [ 'one' => '1.0', @@ -123,4 +125,48 @@ private function getDataCircularInheritance() ] ]; } + + /** + * If a language package inherits from two packages: + * ... + * <code>en_AK</code> + * ... + * <use vendor="parent-package-one" package="language_package_one"/> + * <use vendor= "parent-package-two" package="language_package_two"/> + * ... + * + * In the preceding example: + * language_package_one inherits from en_au_package and en_au_package inherits from en_ie_package + * language_package_two inherits from en_ca_package and en_ca_package inherits from en_us_package + * + * If the Magento application cannot find word or phrase in the en_AK package, + * it looks in other packages in following sequence: + * parent-package-one/language_package_one + * <vendorname>/en_au_package + * <vendorname>/en_ie_package + * parent-package-two/language_package_two + * <vendorname>/en_ca_package + * <vendorname>/en_us_package + * + * @return array + */ + private function getDataMultipleInheritanceFromDevDocs() + { + return [ + // Dictionary that will be requested + 'language_code' => 'en_AK', + // Expected merged dictionary data + 'expectation' => [ + 'one' => 'en_us_package_one', + 'two' => 'en_ca_package_two', + 'three' => 'language_package_two_three', + 'four' => 'en_ie_package_four', + 'five' => 'en_au_package_five', + 'six' => 'language_package_one_six', + 'seven' => 'en_ak_seven', + 'eight' => 'en_ak_eight', + 'nine' => 'en_ak_nine', + ] + ]; + } } diff --git a/dev/tests/integration/testsuite/Magento/Framework/App/Language/_files/bar/en_us/a.csv b/dev/tests/integration/testsuite/Magento/Framework/App/Language/_files/bar/en_az/a.csv similarity index 100% rename from dev/tests/integration/testsuite/Magento/Framework/App/Language/_files/bar/en_us/a.csv rename to dev/tests/integration/testsuite/Magento/Framework/App/Language/_files/bar/en_az/a.csv diff --git a/dev/tests/integration/testsuite/Magento/Framework/App/Language/_files/bar/en_us/b.csv b/dev/tests/integration/testsuite/Magento/Framework/App/Language/_files/bar/en_az/b.csv similarity index 100% rename from dev/tests/integration/testsuite/Magento/Framework/App/Language/_files/bar/en_us/b.csv rename to dev/tests/integration/testsuite/Magento/Framework/App/Language/_files/bar/en_az/b.csv diff --git a/dev/tests/integration/testsuite/Magento/Framework/App/Language/_files/bar/en_az/language.xml b/dev/tests/integration/testsuite/Magento/Framework/App/Language/_files/bar/en_az/language.xml new file mode 100644 index 0000000000000..3cc5d8965c2e8 --- /dev/null +++ b/dev/tests/integration/testsuite/Magento/Framework/App/Language/_files/bar/en_az/language.xml @@ -0,0 +1,13 @@ +<?xml version="1.0"?> +<!-- +/** +* Copyright © Magento, Inc. All rights reserved. +* See COPYING.txt for license details. +*/ +--> +<language xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="urn:magento:framework:App/Language/package.xsd"> + <code>en_AZ</code> + <vendor>bar</vendor> + <package>en_az</package> + <sort_order>0</sort_order> +</language> diff --git a/dev/tests/integration/testsuite/Magento/Framework/App/Language/_files/bar/en_az/registration.php b/dev/tests/integration/testsuite/Magento/Framework/App/Language/_files/bar/en_az/registration.php new file mode 100644 index 0000000000000..41e17fbcf515d --- /dev/null +++ b/dev/tests/integration/testsuite/Magento/Framework/App/Language/_files/bar/en_az/registration.php @@ -0,0 +1,9 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ + +use Magento\Framework\Component\ComponentRegistrar; + +ComponentRegistrar::register(ComponentRegistrar::LANGUAGE, 'bar_en_az', __DIR__); diff --git a/dev/tests/integration/testsuite/Magento/Framework/App/Language/_files/bar/en_gb/language.xml b/dev/tests/integration/testsuite/Magento/Framework/App/Language/_files/bar/en_gb/language.xml index faf531fbf5cc2..736e9dbe738d7 100644 --- a/dev/tests/integration/testsuite/Magento/Framework/App/Language/_files/bar/en_gb/language.xml +++ b/dev/tests/integration/testsuite/Magento/Framework/App/Language/_files/bar/en_gb/language.xml @@ -10,5 +10,5 @@ <vendor>bar</vendor> <package>en_gb</package> <sort_order>100</sort_order> - <use package="en_us" vendor="bar"/> + <use package="en_az" vendor="bar"/> </language> diff --git a/dev/tests/integration/testsuite/Magento/Framework/App/Language/_files/bar/en_gb/registration.php b/dev/tests/integration/testsuite/Magento/Framework/App/Language/_files/bar/en_gb/registration.php index 9c774ed93ae56..6eb6124298e84 100644 --- a/dev/tests/integration/testsuite/Magento/Framework/App/Language/_files/bar/en_gb/registration.php +++ b/dev/tests/integration/testsuite/Magento/Framework/App/Language/_files/bar/en_gb/registration.php @@ -4,6 +4,6 @@ * See COPYING.txt for license details. */ -use \Magento\Framework\Component\ComponentRegistrar; +use Magento\Framework\Component\ComponentRegistrar; ComponentRegistrar::register(ComponentRegistrar::LANGUAGE, 'bar_en_gb', __DIR__); diff --git a/dev/tests/integration/testsuite/Magento/Framework/App/Language/_files/bar/en_us/language.xml b/dev/tests/integration/testsuite/Magento/Framework/App/Language/_files/bar/en_us/language.xml deleted file mode 100644 index a4c5630d5fcd9..0000000000000 --- a/dev/tests/integration/testsuite/Magento/Framework/App/Language/_files/bar/en_us/language.xml +++ /dev/null @@ -1,13 +0,0 @@ -<?xml version="1.0"?> -<!-- -/** -* Copyright © Magento, Inc. All rights reserved. -* See COPYING.txt for license details. -*/ ---> -<language xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="urn:magento:framework:App/Language/package.xsd"> - <code>en_US</code> - <vendor>bar</vendor> - <package>en_us</package> - <sort_order>0</sort_order> -</language> diff --git a/dev/tests/integration/testsuite/Magento/Framework/App/Language/_files/bar/en_us/registration.php b/dev/tests/integration/testsuite/Magento/Framework/App/Language/_files/bar/en_us/registration.php deleted file mode 100644 index 19127f4a0e2c8..0000000000000 --- a/dev/tests/integration/testsuite/Magento/Framework/App/Language/_files/bar/en_us/registration.php +++ /dev/null @@ -1,9 +0,0 @@ -<?php -/** - * Copyright © Magento, Inc. All rights reserved. - * See COPYING.txt for license details. - */ - -use \Magento\Framework\Component\ComponentRegistrar; - -ComponentRegistrar::register(ComponentRegistrar::LANGUAGE, 'bar_en_us', __DIR__); diff --git a/dev/tests/integration/testsuite/Magento/Framework/App/Language/_files/baz/en_gb/1.csv b/dev/tests/integration/testsuite/Magento/Framework/App/Language/_files/baz/en_gb/1.csv index ea501f9d96944..61ef9dd8a49b3 100644 --- a/dev/tests/integration/testsuite/Magento/Framework/App/Language/_files/baz/en_gb/1.csv +++ b/dev/tests/integration/testsuite/Magento/Framework/App/Language/_files/baz/en_gb/1.csv @@ -1 +1,5 @@ -four and 5/10,4.5 \ No newline at end of file +one,1.00 +two,2.00 +three,3.00 +four,4.00 +four and 5/10,4.5 diff --git a/dev/tests/integration/testsuite/Magento/Framework/App/Language/_files/baz/en_gb/registration.php b/dev/tests/integration/testsuite/Magento/Framework/App/Language/_files/baz/en_gb/registration.php index c66abb59b91c1..d065d77ad8ca3 100644 --- a/dev/tests/integration/testsuite/Magento/Framework/App/Language/_files/baz/en_gb/registration.php +++ b/dev/tests/integration/testsuite/Magento/Framework/App/Language/_files/baz/en_gb/registration.php @@ -4,6 +4,6 @@ * See COPYING.txt for license details. */ -use \Magento\Framework\Component\ComponentRegistrar; +use Magento\Framework\Component\ComponentRegistrar; ComponentRegistrar::register(ComponentRegistrar::LANGUAGE, 'baz_en_gb', __DIR__); diff --git a/dev/tests/integration/testsuite/Magento/Framework/App/Language/_files/devdoc/en_ak/1.csv b/dev/tests/integration/testsuite/Magento/Framework/App/Language/_files/devdoc/en_ak/1.csv new file mode 100644 index 0000000000000..8ed274ad7d886 --- /dev/null +++ b/dev/tests/integration/testsuite/Magento/Framework/App/Language/_files/devdoc/en_ak/1.csv @@ -0,0 +1,3 @@ +seven,en_ak_seven +eight,en_ak_eight +nine,en_ak_nine diff --git a/dev/tests/integration/testsuite/Magento/Framework/App/Language/_files/devdoc/en_ak/language.xml b/dev/tests/integration/testsuite/Magento/Framework/App/Language/_files/devdoc/en_ak/language.xml new file mode 100644 index 0000000000000..d0db854f329d0 --- /dev/null +++ b/dev/tests/integration/testsuite/Magento/Framework/App/Language/_files/devdoc/en_ak/language.xml @@ -0,0 +1,15 @@ +<?xml version="1.0"?> +<!-- +/** +* Copyright © Magento, Inc. All rights reserved. +* See COPYING.txt for license details. +*/ +--> +<language xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="urn:magento:framework:App/Language/package.xsd"> + <code>en_AK</code> + <vendor>devdoc</vendor> + <package>en_ak</package> + <sort_order>0</sort_order> + <use vendor="parent-package-one" package="language_package_one"/> + <use vendor="parent-package-two" package="language_package_two"/> +</language> diff --git a/dev/tests/integration/testsuite/Magento/Framework/App/Language/_files/devdoc/en_ak/registration.php b/dev/tests/integration/testsuite/Magento/Framework/App/Language/_files/devdoc/en_ak/registration.php new file mode 100644 index 0000000000000..13b498edcd0aa --- /dev/null +++ b/dev/tests/integration/testsuite/Magento/Framework/App/Language/_files/devdoc/en_ak/registration.php @@ -0,0 +1,9 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ + +use Magento\Framework\Component\ComponentRegistrar; + +ComponentRegistrar::register(ComponentRegistrar::LANGUAGE, 'devdoc_en_ak', __DIR__); diff --git a/dev/tests/integration/testsuite/Magento/Framework/App/Language/_files/first/en_us/1.csv b/dev/tests/integration/testsuite/Magento/Framework/App/Language/_files/first/en_az/1.csv similarity index 100% rename from dev/tests/integration/testsuite/Magento/Framework/App/Language/_files/first/en_us/1.csv rename to dev/tests/integration/testsuite/Magento/Framework/App/Language/_files/first/en_az/1.csv diff --git a/dev/tests/integration/testsuite/Magento/Framework/App/Language/_files/first/en_az/language.xml b/dev/tests/integration/testsuite/Magento/Framework/App/Language/_files/first/en_az/language.xml new file mode 100644 index 0000000000000..2b8354a70dc28 --- /dev/null +++ b/dev/tests/integration/testsuite/Magento/Framework/App/Language/_files/first/en_az/language.xml @@ -0,0 +1,14 @@ +<?xml version="1.0"?> +<!-- +/** +* Copyright © Magento, Inc. All rights reserved. +* See COPYING.txt for license details. +*/ +--> +<language xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="urn:magento:framework:App/Language/package.xsd"> + <code>en_AZ</code> + <vendor>first</vendor> + <package>en_az</package> + <sort_order>0</sort_order> + <use package="en_gb" vendor="second"/> +</language> diff --git a/dev/tests/integration/testsuite/Magento/Framework/App/Language/_files/first/en_az/registration.php b/dev/tests/integration/testsuite/Magento/Framework/App/Language/_files/first/en_az/registration.php new file mode 100644 index 0000000000000..85e88eb4546ce --- /dev/null +++ b/dev/tests/integration/testsuite/Magento/Framework/App/Language/_files/first/en_az/registration.php @@ -0,0 +1,9 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ + +use Magento\Framework\Component\ComponentRegistrar; + +ComponentRegistrar::register(ComponentRegistrar::LANGUAGE, 'first_en_az', __DIR__); diff --git a/dev/tests/integration/testsuite/Magento/Framework/App/Language/_files/first/en_us/language.xml b/dev/tests/integration/testsuite/Magento/Framework/App/Language/_files/first/en_us/language.xml deleted file mode 100644 index 6c67871476b5f..0000000000000 --- a/dev/tests/integration/testsuite/Magento/Framework/App/Language/_files/first/en_us/language.xml +++ /dev/null @@ -1,14 +0,0 @@ -<?xml version="1.0"?> -<!-- -/** -* Copyright © Magento, Inc. All rights reserved. -* See COPYING.txt for license details. -*/ ---> -<language xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="urn:magento:framework:App/Language/package.xsd"> - <code>en_US</code> - <vendor>first</vendor> - <package>en_us</package> - <sort_order>0</sort_order> - <use package="en_gb" vendor="second"/> -</language> diff --git a/dev/tests/integration/testsuite/Magento/Framework/App/Language/_files/first/en_us/registration.php b/dev/tests/integration/testsuite/Magento/Framework/App/Language/_files/first/en_us/registration.php deleted file mode 100644 index d15b384138fbc..0000000000000 --- a/dev/tests/integration/testsuite/Magento/Framework/App/Language/_files/first/en_us/registration.php +++ /dev/null @@ -1,9 +0,0 @@ -<?php -/** - * Copyright © Magento, Inc. All rights reserved. - * See COPYING.txt for license details. - */ - -use \Magento\Framework\Component\ComponentRegistrar; - -ComponentRegistrar::register(ComponentRegistrar::LANGUAGE, 'first_en_us', __DIR__); diff --git a/dev/tests/integration/testsuite/Magento/Framework/App/Language/_files/foo/en_au/registration.php b/dev/tests/integration/testsuite/Magento/Framework/App/Language/_files/foo/en_au/registration.php index b8deab01733d0..ba7fd323cfd94 100644 --- a/dev/tests/integration/testsuite/Magento/Framework/App/Language/_files/foo/en_au/registration.php +++ b/dev/tests/integration/testsuite/Magento/Framework/App/Language/_files/foo/en_au/registration.php @@ -4,6 +4,6 @@ * See COPYING.txt for license details. */ -use \Magento\Framework\Component\ComponentRegistrar; +use Magento\Framework\Component\ComponentRegistrar; ComponentRegistrar::register(ComponentRegistrar::LANGUAGE, 'foo_en_au', __DIR__); diff --git a/dev/tests/integration/testsuite/Magento/Framework/App/Language/_files/my/ru_ru/registration.php b/dev/tests/integration/testsuite/Magento/Framework/App/Language/_files/my/ru_ru/registration.php index c9e0d2dbf14b4..e7e7a045a36a7 100644 --- a/dev/tests/integration/testsuite/Magento/Framework/App/Language/_files/my/ru_ru/registration.php +++ b/dev/tests/integration/testsuite/Magento/Framework/App/Language/_files/my/ru_ru/registration.php @@ -4,6 +4,6 @@ * See COPYING.txt for license details. */ -use \Magento\Framework\Component\ComponentRegistrar; +use Magento\Framework\Component\ComponentRegistrar; ComponentRegistrar::register(ComponentRegistrar::LANGUAGE, 'my_ru_ru', __DIR__); diff --git a/dev/tests/integration/testsuite/Magento/Framework/App/Language/_files/parent-package-one/en_au_package/1.csv b/dev/tests/integration/testsuite/Magento/Framework/App/Language/_files/parent-package-one/en_au_package/1.csv new file mode 100644 index 0000000000000..f1d7dc7c106c7 --- /dev/null +++ b/dev/tests/integration/testsuite/Magento/Framework/App/Language/_files/parent-package-one/en_au_package/1.csv @@ -0,0 +1,5 @@ +five,en_au_package_five +six,en_au_package_six +seven,en_au_package_seven +eight,en_au_package_eight +nine,en_au_package_nine diff --git a/dev/tests/integration/testsuite/Magento/Framework/App/Language/_files/parent-package-one/en_au_package/language.xml b/dev/tests/integration/testsuite/Magento/Framework/App/Language/_files/parent-package-one/en_au_package/language.xml new file mode 100644 index 0000000000000..64d558d9c1d63 --- /dev/null +++ b/dev/tests/integration/testsuite/Magento/Framework/App/Language/_files/parent-package-one/en_au_package/language.xml @@ -0,0 +1,14 @@ +<?xml version="1.0"?> +<!-- +/** +* Copyright © Magento, Inc. All rights reserved. +* See COPYING.txt for license details. +*/ +--> +<language xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="urn:magento:framework:App/Language/package.xsd"> + <code>en_US</code> + <vendor>parent-package-one</vendor> + <package>en_au_package</package> + <sort_order>0</sort_order> + <use vendor="parent-package-one" package="en_ie_package"/> +</language> diff --git a/dev/tests/integration/testsuite/Magento/Framework/App/Language/_files/parent-package-one/en_au_package/registration.php b/dev/tests/integration/testsuite/Magento/Framework/App/Language/_files/parent-package-one/en_au_package/registration.php new file mode 100644 index 0000000000000..664a6d34a814b --- /dev/null +++ b/dev/tests/integration/testsuite/Magento/Framework/App/Language/_files/parent-package-one/en_au_package/registration.php @@ -0,0 +1,9 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ + +use Magento\Framework\Component\ComponentRegistrar; + +ComponentRegistrar::register(ComponentRegistrar::LANGUAGE, 'parent-package-one_en_au_package', __DIR__); diff --git a/dev/tests/integration/testsuite/Magento/Framework/App/Language/_files/parent-package-one/en_ie_package/1.csv b/dev/tests/integration/testsuite/Magento/Framework/App/Language/_files/parent-package-one/en_ie_package/1.csv new file mode 100644 index 0000000000000..8c580556c2e26 --- /dev/null +++ b/dev/tests/integration/testsuite/Magento/Framework/App/Language/_files/parent-package-one/en_ie_package/1.csv @@ -0,0 +1,6 @@ +four,en_ie_package_four +five,en_ie_package_five +six,en_ie_package_six +seven,en_ie_package_seven +eight,en_ie_package_eight +nine,en_ie_package_nine diff --git a/dev/tests/integration/testsuite/Magento/Framework/App/Language/_files/parent-package-one/en_ie_package/language.xml b/dev/tests/integration/testsuite/Magento/Framework/App/Language/_files/parent-package-one/en_ie_package/language.xml new file mode 100644 index 0000000000000..3b681d012b192 --- /dev/null +++ b/dev/tests/integration/testsuite/Magento/Framework/App/Language/_files/parent-package-one/en_ie_package/language.xml @@ -0,0 +1,13 @@ +<?xml version="1.0"?> +<!-- +/** +* Copyright © Magento, Inc. All rights reserved. +* See COPYING.txt for license details. +*/ +--> +<language xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="urn:magento:framework:App/Language/package.xsd"> + <code>en_US</code> + <vendor>parent-package-one</vendor> + <package>en_ie_package</package> + <sort_order>0</sort_order> +</language> diff --git a/dev/tests/integration/testsuite/Magento/Framework/App/Language/_files/parent-package-one/en_ie_package/registration.php b/dev/tests/integration/testsuite/Magento/Framework/App/Language/_files/parent-package-one/en_ie_package/registration.php new file mode 100644 index 0000000000000..81ae45c3d5a40 --- /dev/null +++ b/dev/tests/integration/testsuite/Magento/Framework/App/Language/_files/parent-package-one/en_ie_package/registration.php @@ -0,0 +1,9 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ + +use Magento\Framework\Component\ComponentRegistrar; + +ComponentRegistrar::register(ComponentRegistrar::LANGUAGE, 'parent-package-one_en_ie_package', __DIR__); diff --git a/dev/tests/integration/testsuite/Magento/Framework/App/Language/_files/parent-package-one/language_package_one/1.csv b/dev/tests/integration/testsuite/Magento/Framework/App/Language/_files/parent-package-one/language_package_one/1.csv new file mode 100644 index 0000000000000..54bb6bee04f17 --- /dev/null +++ b/dev/tests/integration/testsuite/Magento/Framework/App/Language/_files/parent-package-one/language_package_one/1.csv @@ -0,0 +1,4 @@ +six,language_package_one_six +seven,language_package_one_seven +eight,language_package_one_eight +nine,language_package_one_nine diff --git a/dev/tests/integration/testsuite/Magento/Framework/App/Language/_files/parent-package-one/language_package_one/language.xml b/dev/tests/integration/testsuite/Magento/Framework/App/Language/_files/parent-package-one/language_package_one/language.xml new file mode 100644 index 0000000000000..4d19c728222ba --- /dev/null +++ b/dev/tests/integration/testsuite/Magento/Framework/App/Language/_files/parent-package-one/language_package_one/language.xml @@ -0,0 +1,14 @@ +<?xml version="1.0"?> +<!-- +/** +* Copyright © Magento, Inc. All rights reserved. +* See COPYING.txt for license details. +*/ +--> +<language xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="urn:magento:framework:App/Language/package.xsd"> + <code>en_US</code> + <vendor>parent-package-one</vendor> + <package>language_package_one</package> + <sort_order>0</sort_order> + <use vendor="parent-package-one" package="en_au_package"/> +</language> diff --git a/dev/tests/integration/testsuite/Magento/Framework/App/Language/_files/parent-package-one/language_package_one/registration.php b/dev/tests/integration/testsuite/Magento/Framework/App/Language/_files/parent-package-one/language_package_one/registration.php new file mode 100644 index 0000000000000..0432f835f0849 --- /dev/null +++ b/dev/tests/integration/testsuite/Magento/Framework/App/Language/_files/parent-package-one/language_package_one/registration.php @@ -0,0 +1,9 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ + +use Magento\Framework\Component\ComponentRegistrar; + +ComponentRegistrar::register(ComponentRegistrar::LANGUAGE, 'parent-package-one_language_package_one', __DIR__); diff --git a/dev/tests/integration/testsuite/Magento/Framework/App/Language/_files/parent-package-two/en_ca_package/1.csv b/dev/tests/integration/testsuite/Magento/Framework/App/Language/_files/parent-package-two/en_ca_package/1.csv new file mode 100644 index 0000000000000..b3c5408b439b4 --- /dev/null +++ b/dev/tests/integration/testsuite/Magento/Framework/App/Language/_files/parent-package-two/en_ca_package/1.csv @@ -0,0 +1,8 @@ +two,en_ca_package_two +three,en_ca_package_three +four,en_ca_package_four +five,en_ca_package_five +six,en_ca_package_six +seven,en_ca_package_seven +eight,en_ca_package_eight +nine,en_ca_package_nine diff --git a/dev/tests/integration/testsuite/Magento/Framework/App/Language/_files/parent-package-two/en_ca_package/language.xml b/dev/tests/integration/testsuite/Magento/Framework/App/Language/_files/parent-package-two/en_ca_package/language.xml new file mode 100644 index 0000000000000..68914dc8df672 --- /dev/null +++ b/dev/tests/integration/testsuite/Magento/Framework/App/Language/_files/parent-package-two/en_ca_package/language.xml @@ -0,0 +1,14 @@ +<?xml version="1.0"?> +<!-- +/** +* Copyright © Magento, Inc. All rights reserved. +* See COPYING.txt for license details. +*/ +--> +<language xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="urn:magento:framework:App/Language/package.xsd"> + <code>en_US</code> + <vendor>parent-package-two</vendor> + <package>en_ca_package</package> + <sort_order>0</sort_order> + <use vendor="parent-package-two" package="en_us_package"/> +</language> diff --git a/dev/tests/integration/testsuite/Magento/Framework/App/Language/_files/parent-package-two/en_ca_package/registration.php b/dev/tests/integration/testsuite/Magento/Framework/App/Language/_files/parent-package-two/en_ca_package/registration.php new file mode 100644 index 0000000000000..b87032884f7db --- /dev/null +++ b/dev/tests/integration/testsuite/Magento/Framework/App/Language/_files/parent-package-two/en_ca_package/registration.php @@ -0,0 +1,9 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ + +use Magento\Framework\Component\ComponentRegistrar; + +ComponentRegistrar::register(ComponentRegistrar::LANGUAGE, 'parent-package-two_en_ca_package', __DIR__); diff --git a/dev/tests/integration/testsuite/Magento/Framework/App/Language/_files/parent-package-two/en_us_package/1.csv b/dev/tests/integration/testsuite/Magento/Framework/App/Language/_files/parent-package-two/en_us_package/1.csv new file mode 100644 index 0000000000000..ef932c4326fb3 --- /dev/null +++ b/dev/tests/integration/testsuite/Magento/Framework/App/Language/_files/parent-package-two/en_us_package/1.csv @@ -0,0 +1,9 @@ +one,en_us_package_one +two,en_us_package_two +three,en_us_package_three +four,en_us_package_four +five,en_us_package_five +six,en_us_package_six +seven,en_us_package_seven +eight,en_us_package_eight +nine,en_us_package_nine diff --git a/dev/tests/integration/testsuite/Magento/Framework/App/Language/_files/parent-package-two/en_us_package/language.xml b/dev/tests/integration/testsuite/Magento/Framework/App/Language/_files/parent-package-two/en_us_package/language.xml new file mode 100644 index 0000000000000..fa7ecda34c975 --- /dev/null +++ b/dev/tests/integration/testsuite/Magento/Framework/App/Language/_files/parent-package-two/en_us_package/language.xml @@ -0,0 +1,13 @@ +<?xml version="1.0"?> +<!-- +/** +* Copyright © Magento, Inc. All rights reserved. +* See COPYING.txt for license details. +*/ +--> +<language xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="urn:magento:framework:App/Language/package.xsd"> + <code>en_US</code> + <vendor>parent-package-two</vendor> + <package>en_us_package</package> + <sort_order>0</sort_order> +</language> diff --git a/dev/tests/integration/testsuite/Magento/Framework/App/Language/_files/parent-package-two/en_us_package/registration.php b/dev/tests/integration/testsuite/Magento/Framework/App/Language/_files/parent-package-two/en_us_package/registration.php new file mode 100644 index 0000000000000..48ed54033a96e --- /dev/null +++ b/dev/tests/integration/testsuite/Magento/Framework/App/Language/_files/parent-package-two/en_us_package/registration.php @@ -0,0 +1,9 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ + +use Magento\Framework\Component\ComponentRegistrar; + +ComponentRegistrar::register(ComponentRegistrar::LANGUAGE, 'parent-package-two_en_us_package', __DIR__); diff --git a/dev/tests/integration/testsuite/Magento/Framework/App/Language/_files/parent-package-two/language_package_two/1.csv b/dev/tests/integration/testsuite/Magento/Framework/App/Language/_files/parent-package-two/language_package_two/1.csv new file mode 100644 index 0000000000000..ef85e7dd72575 --- /dev/null +++ b/dev/tests/integration/testsuite/Magento/Framework/App/Language/_files/parent-package-two/language_package_two/1.csv @@ -0,0 +1,7 @@ +three,language_package_two_three +four,language_package_two_four +five,language_package_two_five +six,language_package_two_six +seven,language_package_two_seven +eight,language_package_two_eight +nine,language_package_two_nine diff --git a/dev/tests/integration/testsuite/Magento/Framework/App/Language/_files/parent-package-two/language_package_two/language.xml b/dev/tests/integration/testsuite/Magento/Framework/App/Language/_files/parent-package-two/language_package_two/language.xml new file mode 100644 index 0000000000000..5e72723312c68 --- /dev/null +++ b/dev/tests/integration/testsuite/Magento/Framework/App/Language/_files/parent-package-two/language_package_two/language.xml @@ -0,0 +1,14 @@ +<?xml version="1.0"?> +<!-- +/** +* Copyright © Magento, Inc. All rights reserved. +* See COPYING.txt for license details. +*/ +--> +<language xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="urn:magento:framework:App/Language/package.xsd"> + <code>en_US</code> + <vendor>parent-package-two</vendor> + <package>language_package_two</package> + <sort_order>0</sort_order> + <use vendor="parent-package-two" package="en_ca_package"/> +</language> diff --git a/dev/tests/integration/testsuite/Magento/Framework/App/Language/_files/parent-package-two/language_package_two/registration.php b/dev/tests/integration/testsuite/Magento/Framework/App/Language/_files/parent-package-two/language_package_two/registration.php new file mode 100644 index 0000000000000..f7eb7d2373a29 --- /dev/null +++ b/dev/tests/integration/testsuite/Magento/Framework/App/Language/_files/parent-package-two/language_package_two/registration.php @@ -0,0 +1,9 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ + +use Magento\Framework\Component\ComponentRegistrar; + +ComponentRegistrar::register(ComponentRegistrar::LANGUAGE, 'parent-package-two_language_package_two', __DIR__); diff --git a/dev/tests/integration/testsuite/Magento/Framework/App/Language/_files/second/en_gb/language.xml b/dev/tests/integration/testsuite/Magento/Framework/App/Language/_files/second/en_gb/language.xml index 860e21ad009ab..12f36154efffe 100644 --- a/dev/tests/integration/testsuite/Magento/Framework/App/Language/_files/second/en_gb/language.xml +++ b/dev/tests/integration/testsuite/Magento/Framework/App/Language/_files/second/en_gb/language.xml @@ -10,5 +10,5 @@ <vendor>second</vendor> <package>en_gb</package> <sort_order>0</sort_order> - <use package="en_us" vendor="first"/> + <use package="en_az" vendor="first"/> </language> diff --git a/dev/tests/integration/testsuite/Magento/Framework/App/Language/_files/second/en_gb/registration.php b/dev/tests/integration/testsuite/Magento/Framework/App/Language/_files/second/en_gb/registration.php index 742164c4fa56d..6c84974fea235 100644 --- a/dev/tests/integration/testsuite/Magento/Framework/App/Language/_files/second/en_gb/registration.php +++ b/dev/tests/integration/testsuite/Magento/Framework/App/Language/_files/second/en_gb/registration.php @@ -4,6 +4,6 @@ * See COPYING.txt for license details. */ -use \Magento\Framework\Component\ComponentRegistrar; +use Magento\Framework\Component\ComponentRegistrar; ComponentRegistrar::register(ComponentRegistrar::LANGUAGE, 'second_en_gb', __DIR__); diff --git a/dev/tests/integration/testsuite/Magento/Framework/App/Language/_files/theirs/ru_ru/registration.php b/dev/tests/integration/testsuite/Magento/Framework/App/Language/_files/theirs/ru_ru/registration.php index 6924c5e624d32..e78422d5ebeb3 100644 --- a/dev/tests/integration/testsuite/Magento/Framework/App/Language/_files/theirs/ru_ru/registration.php +++ b/dev/tests/integration/testsuite/Magento/Framework/App/Language/_files/theirs/ru_ru/registration.php @@ -4,6 +4,6 @@ * See COPYING.txt for license details. */ -use \Magento\Framework\Component\ComponentRegistrar; +use Magento\Framework\Component\ComponentRegistrar; ComponentRegistrar::register(ComponentRegistrar::LANGUAGE, 'theirs_ru_ru', __DIR__); diff --git a/dev/tests/integration/testsuite/Magento/Framework/App/Request/CsrfValidatorTest.php b/dev/tests/integration/testsuite/Magento/Framework/App/Request/CsrfValidatorTest.php index 9246be52f41bf..7e5edf361e03a 100644 --- a/dev/tests/integration/testsuite/Magento/Framework/App/Request/CsrfValidatorTest.php +++ b/dev/tests/integration/testsuite/Magento/Framework/App/Request/CsrfValidatorTest.php @@ -19,7 +19,7 @@ use PHPUnit\Framework\TestCase; use Magento\TestFramework\Helper\Bootstrap; use Magento\Framework\App\Request\Http as HttpRequest; -use Zend\Stdlib\Parameters; +use Laminas\Stdlib\Parameters; use Magento\Framework\App\Response\Http as HttpResponse; use Magento\Framework\App\Response\HttpFactory as HttpResponseFactory; diff --git a/dev/tests/integration/testsuite/Magento/Framework/App/Response/HeaderProvider/AbstractHeaderTestCase.php b/dev/tests/integration/testsuite/Magento/Framework/App/Response/HeaderProvider/AbstractHeaderTestCase.php index 8183a5878ba85..1fc58ff136c92 100644 --- a/dev/tests/integration/testsuite/Magento/Framework/App/Response/HeaderProvider/AbstractHeaderTestCase.php +++ b/dev/tests/integration/testsuite/Magento/Framework/App/Response/HeaderProvider/AbstractHeaderTestCase.php @@ -5,24 +5,33 @@ */ namespace Magento\Framework\App\Response\HeaderProvider; +use Laminas\Http\Header\HeaderInterface; +use Magento\Framework\App\Response\Http as HttpResponse; + +/** + * Class AbstractHeaderTestCase + */ abstract class AbstractHeaderTestCase extends \Magento\TestFramework\TestCase\AbstractController { - /** @var \Magento\Framework\App\Response\Http */ + /** + * @var HttpResponse + */ private $interceptedResponse; + /** + * @inheritDoc + */ public function setUp() { parent::setUp(); $this->_objectManager->configure( [ - 'preferences' => - [ - \Magento\Framework\App\Response\Http::class => - \Magento\Framework\App\Response\Http\Interceptor::class + 'preferences' => [ + HttpResponse::class => 'Magento\Framework\App\Response\Http\Interceptor' ] ] ); - $this->interceptedResponse = $this->_objectManager->create(\Magento\Framework\App\Response\Http::class); + $this->interceptedResponse = $this->_objectManager->create(HttpResponse::class); } /** @@ -33,16 +42,30 @@ public function setUp() */ protected function assertHeaderPresent($name, $value) { + $value = [$value]; $this->interceptedResponse->sendResponse(); - $header = $this->interceptedResponse->getHeader($name); - $this->assertTrue(is_subclass_of($header, \Zend\Http\Header\HeaderInterface::class, false)); + + $headerContent = []; + if ($header instanceof \ArrayIterator) { + foreach ($header as $item) { + $headerContent[] = $item->getFieldValue(); + } + } elseif ($header instanceof HeaderInterface) { + $headerContent[] = $header->getFieldValue(); + } + $this->assertSame( $value, - $header->getFieldValue() + $headerContent ); } + /** + * Assert is no header. + * + * @param string $name + */ protected function assertHeaderNotPresent($name) { $this->interceptedResponse->sendResponse(); diff --git a/dev/tests/integration/testsuite/Magento/Framework/App/Response/HeaderProvider/UpgradeInsecureTest.php b/dev/tests/integration/testsuite/Magento/Framework/App/Response/HeaderProvider/UpgradeInsecureTest.php index 18d58bea14b93..8b621c1d65974 100644 --- a/dev/tests/integration/testsuite/Magento/Framework/App/Response/HeaderProvider/UpgradeInsecureTest.php +++ b/dev/tests/integration/testsuite/Magento/Framework/App/Response/HeaderProvider/UpgradeInsecureTest.php @@ -14,7 +14,7 @@ class UpgradeInsecureTest extends AbstractHeaderTestCase */ public function testHeaderPresent() { - $this->assertHeaderPresent('Content-Security-Policy', 'upgrade-insecure-requests'); + $this->assertHeaderPresent('Content-Security-Policy', 'upgrade-insecure-requests;'); } /** diff --git a/dev/tests/integration/testsuite/Magento/Framework/App/TestStubs/InheritanceBasedBackendAction.php b/dev/tests/integration/testsuite/Magento/Framework/App/TestStubs/InheritanceBasedBackendAction.php new file mode 100644 index 0000000000000..b24cc2e30382c --- /dev/null +++ b/dev/tests/integration/testsuite/Magento/Framework/App/TestStubs/InheritanceBasedBackendAction.php @@ -0,0 +1,46 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ + +declare(strict_types=1); + +namespace Magento\Framework\App\TestStubs; + +use Magento\Backend\App\Action; +use Magento\Framework\App\ResponseInterface; +use Magento\Framework\Controller\ResultInterface; +use Magento\Framework\View\Result\Page; +use Magento\Framework\View\Result\PageFactory; + +/** + * Stub inheritance based backend action controller for testing purposes. + */ +class InheritanceBasedBackendAction extends Action +{ + /** + * @var PageFactory + */ + private $pageFactory; + + /** + * @param Action\Context $context + * @param PageFactory $pageFactory + */ + public function __construct(Action\Context $context, PageFactory $pageFactory) + { + parent::__construct($context); + $this->pageFactory = $pageFactory; + } + + /** + * Creates Page + * + * @return ResponseInterface|ResultInterface|Page + */ + public function execute() + { + return $this->pageFactory->create(); + } +} diff --git a/dev/tests/integration/testsuite/Magento/Framework/App/TestStubs/InheritanceBasedFrontendAction.php b/dev/tests/integration/testsuite/Magento/Framework/App/TestStubs/InheritanceBasedFrontendAction.php new file mode 100644 index 0000000000000..315df9585c715 --- /dev/null +++ b/dev/tests/integration/testsuite/Magento/Framework/App/TestStubs/InheritanceBasedFrontendAction.php @@ -0,0 +1,63 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ + +declare(strict_types=1); + +namespace Magento\Framework\App\TestStubs; + +use Magento\Framework\App\Action\Action; +use Magento\Framework\App\Action\Context; +use Magento\Framework\App\ResponseInterface; +use Magento\Framework\Controller\ResultInterface; +use Magento\Framework\View\Result\Page; +use Magento\Framework\View\Result\PageFactory; + +/** + * Stub inheritance based frontend action controller for testing purposes. + */ +class InheritanceBasedFrontendAction extends Action +{ + /** + * @var PageFactory + */ + private $pageFactory; + + /** + * @var bool + */ + private $executeWasCalled = false; + + /** + * @param Context $context + * @param PageFactory $pageFactory + */ + public function __construct(Context $context, PageFactory $pageFactory) + { + parent::__construct($context); + $this->pageFactory = $pageFactory; + } + + /** + * Runs `execute()` method to create Page + * + * @return ResponseInterface|ResultInterface|Page + */ + public function execute() + { + $this->executeWasCalled = true; + return $this->pageFactory->create(); + } + + /** + * Determines whether execute method was called + * + * @return bool + */ + public function isExecuted(): bool + { + return $this->executeWasCalled; + } +} diff --git a/dev/tests/integration/testsuite/Magento/Framework/App/TestStubs/InterfaceOnlyBackendAction.php b/dev/tests/integration/testsuite/Magento/Framework/App/TestStubs/InterfaceOnlyBackendAction.php new file mode 100644 index 0000000000000..86244fd67237b --- /dev/null +++ b/dev/tests/integration/testsuite/Magento/Framework/App/TestStubs/InterfaceOnlyBackendAction.php @@ -0,0 +1,43 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +declare(strict_types=1); + +namespace Magento\Framework\App\TestStubs; + +use Magento\Framework\App\ActionInterface; +use Magento\Framework\App\ResponseInterface; +use Magento\Framework\Controller\ResultInterface; +use Magento\Framework\View\Result\Page; +use Magento\Framework\View\Result\PageFactory; + +/** + * Stub interface action controller implementation for testing purposes. + */ +class InterfaceOnlyBackendAction implements ActionInterface +{ + /** + * @var PageFactory + */ + private $pageFactory; + + /** + * @param PageFactory $pageFactory + */ + public function __construct(PageFactory $pageFactory) + { + $this->pageFactory = $pageFactory; + } + + /** + * Creates Page object + * + * @return ResponseInterface|ResultInterface|Page + */ + public function execute() + { + return $this->pageFactory->create(); + } +} diff --git a/dev/tests/integration/testsuite/Magento/Framework/App/TestStubs/InterfaceOnlyFrontendAction.php b/dev/tests/integration/testsuite/Magento/Framework/App/TestStubs/InterfaceOnlyFrontendAction.php new file mode 100644 index 0000000000000..123fe2ea87be1 --- /dev/null +++ b/dev/tests/integration/testsuite/Magento/Framework/App/TestStubs/InterfaceOnlyFrontendAction.php @@ -0,0 +1,59 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +declare(strict_types=1); + +namespace Magento\Framework\App\TestStubs; + +use Magento\Framework\App\ActionInterface; +use Magento\Framework\App\ResponseInterface; +use Magento\Framework\Controller\ResultInterface; +use Magento\Framework\View\Result\Page; +use Magento\Framework\View\Result\PageFactory; + +/** + * Stub interface only based frontend action controller for testing purposes. + */ +class InterfaceOnlyFrontendAction implements ActionInterface +{ + /** + * @var PageFactory + */ + private $pageFactory; + + /** + * @var bool + */ + private $executeWasCalled = false; + + /** + * @param PageFactory $pageFactory + */ + public function __construct(PageFactory $pageFactory) + { + $this->pageFactory = $pageFactory; + } + + /** + * Creates Page object + * + * @return ResponseInterface|ResultInterface|Page + */ + public function execute() + { + $this->executeWasCalled = true; + return $this->pageFactory->create(); + } + + /** + * Returns whether `execute()` method was ran + * + * @return bool + */ + public function isExecuted(): bool + { + return $this->executeWasCalled; + } +} diff --git a/dev/tests/integration/testsuite/Magento/Framework/App/Utility/_files/fixtures/language/registration.php b/dev/tests/integration/testsuite/Magento/Framework/App/Utility/_files/fixtures/language/registration.php index 0c8673f00dbf6..1a5326ad6627d 100644 --- a/dev/tests/integration/testsuite/Magento/Framework/App/Utility/_files/fixtures/language/registration.php +++ b/dev/tests/integration/testsuite/Magento/Framework/App/Utility/_files/fixtures/language/registration.php @@ -4,6 +4,6 @@ * See COPYING.txt for license details. */ -use \Magento\Framework\Component\ComponentRegistrar; +use Magento\Framework\Component\ComponentRegistrar; ComponentRegistrar::register(ComponentRegistrar::LANGUAGE, 'magento_test_lang', __DIR__); diff --git a/dev/tests/integration/testsuite/Magento/Framework/App/Utility/_files/fixtures/library/registration.php b/dev/tests/integration/testsuite/Magento/Framework/App/Utility/_files/fixtures/library/registration.php index 037159b48ce64..f14a169b2e56b 100644 --- a/dev/tests/integration/testsuite/Magento/Framework/App/Utility/_files/fixtures/library/registration.php +++ b/dev/tests/integration/testsuite/Magento/Framework/App/Utility/_files/fixtures/library/registration.php @@ -4,6 +4,6 @@ * See COPYING.txt for license details. */ -use \Magento\Framework\Component\ComponentRegistrar; +use Magento\Framework\Component\ComponentRegistrar; ComponentRegistrar::register(ComponentRegistrar::LIBRARY, 'magento/test', __DIR__); diff --git a/dev/tests/integration/testsuite/Magento/Framework/App/Utility/_files/fixtures/module/registration.php b/dev/tests/integration/testsuite/Magento/Framework/App/Utility/_files/fixtures/module/registration.php index 5e082df446665..95def30dd5f26 100644 --- a/dev/tests/integration/testsuite/Magento/Framework/App/Utility/_files/fixtures/module/registration.php +++ b/dev/tests/integration/testsuite/Magento/Framework/App/Utility/_files/fixtures/module/registration.php @@ -4,6 +4,6 @@ * See COPYING.txt for license details. */ -use \Magento\Framework\Component\ComponentRegistrar; +use Magento\Framework\Component\ComponentRegistrar; ComponentRegistrar::register(ComponentRegistrar::MODULE, 'Magento_Module', __DIR__); diff --git a/dev/tests/integration/testsuite/Magento/Framework/App/Utility/_files/fixtures/theme/registration.php b/dev/tests/integration/testsuite/Magento/Framework/App/Utility/_files/fixtures/theme/registration.php index c286f6c57d87f..da95bfe5f1519 100644 --- a/dev/tests/integration/testsuite/Magento/Framework/App/Utility/_files/fixtures/theme/registration.php +++ b/dev/tests/integration/testsuite/Magento/Framework/App/Utility/_files/fixtures/theme/registration.php @@ -4,6 +4,6 @@ * See COPYING.txt for license details. */ -use \Magento\Framework\Component\ComponentRegistrar; +use Magento\Framework\Component\ComponentRegistrar; ComponentRegistrar::register(ComponentRegistrar::THEME, 'frontend/Test/theme', __DIR__); diff --git a/dev/tests/integration/testsuite/Magento/Framework/Code/GeneratorTest/ParentClassWithNamespace.php b/dev/tests/integration/testsuite/Magento/Framework/Code/GeneratorTest/ParentClassWithNamespace.php index 01e63126e5cde..27792e092d6b8 100644 --- a/dev/tests/integration/testsuite/Magento/Framework/Code/GeneratorTest/ParentClassWithNamespace.php +++ b/dev/tests/integration/testsuite/Magento/Framework/Code/GeneratorTest/ParentClassWithNamespace.php @@ -5,7 +5,7 @@ */ namespace Magento\Framework\Code\GeneratorTest; -use Zend\Code\Generator\DocBlockGenerator; +use Laminas\Code\Generator\DocBlockGenerator; /** * phpcs:ignoreFile @@ -15,7 +15,7 @@ class ParentClassWithNamespace /** * Public parent method * - * @param \Zend\Code\Generator\DocBlockGenerator $docBlockGenerator + * @param \Laminas\Code\Generator\DocBlockGenerator $docBlockGenerator * @param string $param1 * @param string $param2 * @param string $param3 @@ -35,7 +35,7 @@ public function publicParentMethod( /** * Protected parent method * - * @param \Zend\Code\Generator\DocBlockGenerator $docBlockGenerator + * @param \Laminas\Code\Generator\DocBlockGenerator $docBlockGenerator * @param string $param1 * @param string $param2 * @param string $param3 @@ -55,7 +55,7 @@ protected function _protectedParentMethod( /** * Private parent method * - * @param \Zend\Code\Generator\DocBlockGenerator $docBlockGenerator + * @param \Laminas\Code\Generator\DocBlockGenerator $docBlockGenerator * @param string $param1 * @param string $param2 * @param string $param3 diff --git a/dev/tests/integration/testsuite/Magento/Framework/Code/GeneratorTest/SourceClassWithNamespace.php b/dev/tests/integration/testsuite/Magento/Framework/Code/GeneratorTest/SourceClassWithNamespace.php index b9fc351ff64e6..d76167a9af6ed 100644 --- a/dev/tests/integration/testsuite/Magento/Framework/Code/GeneratorTest/SourceClassWithNamespace.php +++ b/dev/tests/integration/testsuite/Magento/Framework/Code/GeneratorTest/SourceClassWithNamespace.php @@ -5,7 +5,7 @@ */ namespace Magento\Framework\Code\GeneratorTest; -use Zend\Code\Generator\ClassGenerator; +use Laminas\Code\Generator\ClassGenerator; /** * phpcs:ignoreFile @@ -28,7 +28,7 @@ public function __construct($param1 = '', $param2 = '\\', $param3 = '\'') /** * Public child method * - * @param \Zend\Code\Generator\ClassGenerator $classGenerator + * @param \Laminas\Code\Generator\ClassGenerator $classGenerator * @param string $param1 * @param string $param2 * @param string $param3 @@ -49,7 +49,7 @@ public function publicChildMethod( /** * Public child method with reference * - * @param \Zend\Code\Generator\ClassGenerator $classGenerator + * @param \Laminas\Code\Generator\ClassGenerator $classGenerator * @param string $param1 * @param array $array * @@ -62,7 +62,7 @@ public function publicMethodWithReference(ClassGenerator &$classGenerator, &$par /** * Protected child method * - * @param \Zend\Code\Generator\ClassGenerator $classGenerator + * @param \Laminas\Code\Generator\ClassGenerator $classGenerator * @param string $param1 * @param string $param2 * @param string $param3 @@ -80,7 +80,7 @@ protected function _protectedChildMethod( /** * Private child method * - * @param \Zend\Code\Generator\ClassGenerator $classGenerator + * @param \Laminas\Code\Generator\ClassGenerator $classGenerator * @param string $param1 * @param string $param2 * @param string $param3 @@ -152,6 +152,7 @@ public function public71( */ public function public71Another(?\DateTime $arg1, $arg2 = false): ?string { + // phpstan:ignore } /** @@ -164,5 +165,6 @@ public function public71Another(?\DateTime $arg1, $arg2 = false): ?string */ public function publicWithSelf($arg = false): self { + // phpstan:ignore } } diff --git a/dev/tests/integration/testsuite/Magento/Framework/Code/_expected/SourceClassWithNamespaceInterceptor.php.sample b/dev/tests/integration/testsuite/Magento/Framework/Code/_expected/SourceClassWithNamespaceInterceptor.php.sample index 83191f2d1b099..74c1522fa41f0 100644 --- a/dev/tests/integration/testsuite/Magento/Framework/Code/_expected/SourceClassWithNamespaceInterceptor.php.sample +++ b/dev/tests/integration/testsuite/Magento/Framework/Code/_expected/SourceClassWithNamespaceInterceptor.php.sample @@ -18,7 +18,7 @@ class Interceptor extends \Magento\Framework\Code\GeneratorTest\SourceClassWithN /** * {@inheritdoc} */ - public function publicChildMethod(\Zend\Code\Generator\ClassGenerator $classGenerator, $param1 = '', $param2 = '\\', $param3 = '\'', array $array = []) + public function publicChildMethod(\Laminas\Code\Generator\ClassGenerator $classGenerator, $param1 = '', $param2 = '\\', $param3 = '\'', array $array = []) { $pluginInfo = $this->pluginList->getNext($this->subjectType, 'publicChildMethod'); if (!$pluginInfo) { @@ -31,7 +31,7 @@ class Interceptor extends \Magento\Framework\Code\GeneratorTest\SourceClassWithN /** * {@inheritdoc} */ - public function publicMethodWithReference(\Zend\Code\Generator\ClassGenerator &$classGenerator, &$param1, array &$array) + public function publicMethodWithReference(\Laminas\Code\Generator\ClassGenerator &$classGenerator, &$param1, array &$array) { $pluginInfo = $this->pluginList->getNext($this->subjectType, 'publicMethodWithReference'); if (!$pluginInfo) { @@ -96,7 +96,7 @@ class Interceptor extends \Magento\Framework\Code\GeneratorTest\SourceClassWithN /** * {@inheritdoc} */ - public function publicParentMethod(\Zend\Code\Generator\DocBlockGenerator $docBlockGenerator, $param1 = '', $param2 = '\\', $param3 = '\'', array $array = []) + public function publicParentMethod(\Laminas\Code\Generator\DocBlockGenerator $docBlockGenerator, $param1 = '', $param2 = '\\', $param3 = '\'', array $array = []) { $pluginInfo = $this->pluginList->getNext($this->subjectType, 'publicParentMethod'); if (!$pluginInfo) { diff --git a/dev/tests/integration/testsuite/Magento/Framework/Code/_expected/SourceClassWithNamespaceProxy.php.sample b/dev/tests/integration/testsuite/Magento/Framework/Code/_expected/SourceClassWithNamespaceProxy.php.sample index 359854f2d481c..42f766c786c0b 100644 --- a/dev/tests/integration/testsuite/Magento/Framework/Code/_expected/SourceClassWithNamespaceProxy.php.sample +++ b/dev/tests/integration/testsuite/Magento/Framework/Code/_expected/SourceClassWithNamespaceProxy.php.sample @@ -93,7 +93,7 @@ class Proxy extends \Magento\Framework\Code\GeneratorTest\SourceClassWithNamespa /** * {@inheritdoc} */ - public function publicChildMethod(\Zend\Code\Generator\ClassGenerator $classGenerator, $param1 = '', $param2 = '\\', $param3 = '\'', array $array = []) + public function publicChildMethod(\Laminas\Code\Generator\ClassGenerator $classGenerator, $param1 = '', $param2 = '\\', $param3 = '\'', array $array = []) { return $this->_getSubject()->publicChildMethod($classGenerator, $param1, $param2, $param3, $array); } @@ -101,7 +101,7 @@ class Proxy extends \Magento\Framework\Code\GeneratorTest\SourceClassWithNamespa /** * {@inheritdoc} */ - public function publicMethodWithReference(\Zend\Code\Generator\ClassGenerator &$classGenerator, &$param1, array &$array) + public function publicMethodWithReference(\Laminas\Code\Generator\ClassGenerator &$classGenerator, &$param1, array &$array) { return $this->_getSubject()->publicMethodWithReference($classGenerator, $param1, $array); } @@ -141,7 +141,7 @@ class Proxy extends \Magento\Framework\Code\GeneratorTest\SourceClassWithNamespa /** * {@inheritdoc} */ - public function publicParentMethod(\Zend\Code\Generator\DocBlockGenerator $docBlockGenerator, $param1 = '', $param2 = '\\', $param3 = '\'', array $array = []) + public function publicParentMethod(\Laminas\Code\Generator\DocBlockGenerator $docBlockGenerator, $param1 = '', $param2 = '\\', $param3 = '\'', array $array = []) { return $this->_getSubject()->publicParentMethod($docBlockGenerator, $param1, $param2, $param3, $array); } diff --git a/dev/tests/integration/testsuite/Magento/Framework/Composer/_files/testFromClone/composer.json b/dev/tests/integration/testsuite/Magento/Framework/Composer/_files/testFromClone/composer.json index ed9965622dc40..3e4e3c49a9eb3 100644 --- a/dev/tests/integration/testsuite/Magento/Framework/Composer/_files/testFromClone/composer.json +++ b/dev/tests/integration/testsuite/Magento/Framework/Composer/_files/testFromClone/composer.json @@ -26,27 +26,27 @@ "phpseclib/phpseclib": "~0.3", "symfony/console": "~2.3", "tubalmartin/cssmin": "2.4.8-p6", - "zendframework/zend-code": "2.4.0", - "zendframework/zend-config": "2.4.0", - "zendframework/zend-console": "2.4.0", - "zendframework/zend-di": "2.4.0", - "zendframework/zend-eventmanager": "2.4.0", - "zendframework/zend-form": "2.4.0", - "zendframework/zend-http": "2.4.0", - "zendframework/zend-i18n": "2.4.0", - "zendframework/zend-json": "2.4.0", - "zendframework/zend-log": "2.4.0", - "zendframework/zend-modulemanager": "2.4.0", - "zendframework/zend-mvc": "2.4.0", - "zendframework/zend-serializer": "2.4.0", - "zendframework/zend-server": "2.4.0", - "zendframework/zend-servicemanager": "2.4.0", - "zendframework/zend-soap": "2.4.0", - "zendframework/zend-stdlib": "2.4.0", - "zendframework/zend-text": "2.4.0", - "zendframework/zend-uri": "2.4.0", - "zendframework/zend-validator": "2.4.0", - "zendframework/zend-view": "2.4.0" + "laminas/laminas-code": "2.4.0", + "laminas/laminas-config": "2.4.0", + "laminas/laminas-console": "2.4.0", + "laminas/laminas-di": "2.4.0", + "laminas/laminas-eventmanager": "2.4.0", + "laminas/laminas-form": "2.4.0", + "laminas/laminas-http": "2.4.0", + "laminas/laminas-i18n": "2.4.0", + "laminas/laminas-json": "2.4.0", + "laminas/laminas-log": "2.4.0", + "laminas/laminas-modulemanager": "2.4.0", + "laminas/laminas-mvc": "2.4.0", + "laminas/laminas-serializer": "2.4.0", + "laminas/laminas-server": "2.4.0", + "laminas/laminas-servicemanager": "2.4.0", + "laminas/laminas-soap": "2.4.0", + "laminas/laminas-stdlib": "2.4.0", + "laminas/laminas-text": "2.4.0", + "laminas/laminas-uri": "2.4.0", + "laminas/laminas-validator": "2.4.0", + "laminas/laminas-view": "2.4.0" }, "require-dev": { "fabpot/php-cs-fixer": "~1.2", diff --git a/dev/tests/integration/testsuite/Magento/Framework/Composer/_files/testFromClone/composer.lock b/dev/tests/integration/testsuite/Magento/Framework/Composer/_files/testFromClone/composer.lock index 094f899a2605b..4dd3612ccb020 100644 --- a/dev/tests/integration/testsuite/Magento/Framework/Composer/_files/testFromClone/composer.lock +++ b/dev/tests/integration/testsuite/Magento/Framework/Composer/_files/testFromClone/composer.lock @@ -2482,38 +2482,38 @@ "time": "2018-09-02T14:59:54+00:00" }, { - "name": "zendframework/zend-captcha", + "name": "laminas/laminas-captcha", "version": "2.8.0", "source": { "type": "git", - "url": "https://github.com/zendframework/zend-captcha.git", + "url": "https://github.com/laminas/laminas-captcha.git", "reference": "37e9b6a4f632a9399eecbf2e5e325ad89083f87b" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/zendframework/zend-captcha/zipball/37e9b6a4f632a9399eecbf2e5e325ad89083f87b", + "url": "https://api.github.com/repos/laminas/laminas-captcha/zipball/37e9b6a4f632a9399eecbf2e5e325ad89083f87b", "reference": "37e9b6a4f632a9399eecbf2e5e325ad89083f87b", "shasum": "" }, "require": { "php": "^5.6 || ^7.0", - "zendframework/zend-math": "^2.7 || ^3.0", - "zendframework/zend-stdlib": "^2.7.7 || ^3.1" + "laminas/laminas-math": "^2.7 || ^3.0", + "laminas/laminas-stdlib": "^2.7.7 || ^3.1" }, "require-dev": { "phpunit/phpunit": "^5.7.27 || ^6.5.8 || ^7.1.2", - "zendframework/zend-coding-standard": "~1.0.0", - "zendframework/zend-session": "^2.8", - "zendframework/zend-text": "^2.6", - "zendframework/zend-validator": "^2.10.1", - "zendframework/zendservice-recaptcha": "^3.0" + "laminas/laminas-coding-standard": "~1.0.0", + "laminas/laminas-session": "^2.8", + "laminas/laminas-text": "^2.6", + "laminas/laminas-validator": "^2.10.1", + "laminas/laminas-recaptcha": "^3.0" }, "suggest": { - "zendframework/zend-i18n-resources": "Translations of captcha messages", - "zendframework/zend-session": "Zend\\Session component", - "zendframework/zend-text": "Zend\\Text component", - "zendframework/zend-validator": "Zend\\Validator component", - "zendframework/zendservice-recaptcha": "ZendService\\ReCaptcha component" + "laminas/laminas-i18n-resources": "Translations of captcha messages", + "laminas/laminas-session": "Laminas\\Session component", + "laminas/laminas-text": "Laminas\\Text component", + "laminas/laminas-validator": "Laminas\\Validator component", + "laminas/laminas-recaptcha": "Laminas\\ReCaptcha component" }, "type": "library", "extra": { @@ -2524,7 +2524,7 @@ }, "autoload": { "psr-4": { - "Zend\\Captcha\\": "src/" + "Laminas\\Captcha\\": "src/" } }, "notification-url": "https://packagist.org/downloads/", @@ -2540,33 +2540,33 @@ "time": "2018-04-24T17:24:10+00:00" }, { - "name": "zendframework/zend-code", + "name": "laminas/laminas-code", "version": "3.3.1", "source": { "type": "git", - "url": "https://github.com/zendframework/zend-code.git", + "url": "https://github.com/laminas/laminas-code.git", "reference": "c21db169075c6ec4b342149f446e7b7b724f95eb" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/zendframework/zend-code/zipball/c21db169075c6ec4b342149f446e7b7b724f95eb", + "url": "https://api.github.com/repos/laminas/laminas-code/zipball/c21db169075c6ec4b342149f446e7b7b724f95eb", "reference": "c21db169075c6ec4b342149f446e7b7b724f95eb", "shasum": "" }, "require": { "php": "^7.1", - "zendframework/zend-eventmanager": "^2.6 || ^3.0" + "laminas/laminas-eventmanager": "^2.6 || ^3.0" }, "require-dev": { "doctrine/annotations": "~1.0", "ext-phar": "*", "phpunit/phpunit": "^6.2.3", - "zendframework/zend-coding-standard": "^1.0.0", - "zendframework/zend-stdlib": "^2.7 || ^3.0" + "laminas/laminas-coding-standard": "^1.0.0", + "laminas/laminas-stdlib": "^2.7 || ^3.0" }, "suggest": { "doctrine/annotations": "Doctrine\\Common\\Annotations >=1.0 for annotation features", - "zendframework/zend-stdlib": "Zend\\Stdlib component" + "laminas/laminas-stdlib": "Laminas\\Stdlib component" }, "type": "library", "extra": { @@ -2577,7 +2577,7 @@ }, "autoload": { "psr-4": { - "Zend\\Code\\": "src/" + "Laminas\\Code\\": "src/" } }, "notification-url": "https://packagist.org/downloads/", @@ -2585,7 +2585,7 @@ "BSD-3-Clause" ], "description": "provides facilities to generate arbitrary code using an object oriented interface", - "homepage": "https://github.com/zendframework/zend-code", + "homepage": "https://github.com/laminas/laminas-code", "keywords": [ "code", "zf2" @@ -2593,36 +2593,36 @@ "time": "2018-08-13T20:36:59+00:00" }, { - "name": "zendframework/zend-config", + "name": "laminas/laminas-config", "version": "2.6.0", "source": { "type": "git", - "url": "https://github.com/zendframework/zend-config.git", + "url": "https://github.com/laminas/laminas-config.git", "reference": "2920e877a9f6dca9fa8f6bd3b1ffc2e19bb1e30d" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/zendframework/zend-config/zipball/2920e877a9f6dca9fa8f6bd3b1ffc2e19bb1e30d", + "url": "https://api.github.com/repos/laminas/laminas-config/zipball/2920e877a9f6dca9fa8f6bd3b1ffc2e19bb1e30d", "reference": "2920e877a9f6dca9fa8f6bd3b1ffc2e19bb1e30d", "shasum": "" }, "require": { "php": "^5.5 || ^7.0", - "zendframework/zend-stdlib": "^2.7 || ^3.0" + "laminas/laminas-stdlib": "^2.7 || ^3.0" }, "require-dev": { "fabpot/php-cs-fixer": "1.7.*", "phpunit/phpunit": "~4.0", - "zendframework/zend-filter": "^2.6", - "zendframework/zend-i18n": "^2.5", - "zendframework/zend-json": "^2.6.1", - "zendframework/zend-servicemanager": "^2.7.5 || ^3.0.3" + "laminas/laminas-filter": "^2.6", + "laminas/laminas-i18n": "^2.5", + "laminas/laminas-json": "^2.6.1", + "laminas/laminas-servicemanager": "^2.7.5 || ^3.0.3" }, "suggest": { - "zendframework/zend-filter": "Zend\\Filter component", - "zendframework/zend-i18n": "Zend\\I18n component", - "zendframework/zend-json": "Zend\\Json to use the Json reader or writer classes", - "zendframework/zend-servicemanager": "Zend\\ServiceManager for use with the Config Factory to retrieve reader and writer instances" + "laminas/laminas-filter": "Laminas\\Filter component", + "laminas/laminas-i18n": "Laminas\\I18n component", + "laminas/laminas-json": "Laminas\\Json to use the Json reader or writer classes", + "laminas/laminas-servicemanager": "Laminas\\ServiceManager for use with the Config Factory to retrieve reader and writer instances" }, "type": "library", "extra": { @@ -2633,7 +2633,7 @@ }, "autoload": { "psr-4": { - "Zend\\Config\\": "src/" + "Laminas\\Config\\": "src/" } }, "notification-url": "https://packagist.org/downloads/", @@ -2641,7 +2641,7 @@ "BSD-3-Clause" ], "description": "provides a nested object property based user interface for accessing this configuration data within application code", - "homepage": "https://github.com/zendframework/zend-config", + "homepage": "https://github.com/laminas/laminas-config", "keywords": [ "config", "zf2" @@ -2649,33 +2649,33 @@ "time": "2016-02-04T23:01:10+00:00" }, { - "name": "zendframework/zend-console", + "name": "laminas/laminas-console", "version": "2.7.0", "source": { "type": "git", - "url": "https://github.com/zendframework/zend-console.git", + "url": "https://github.com/laminas/laminas-console.git", "reference": "e8aa08da83de3d265256c40ba45cd649115f0e18" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/zendframework/zend-console/zipball/e8aa08da83de3d265256c40ba45cd649115f0e18", + "url": "https://api.github.com/repos/laminas/laminas-console/zipball/e8aa08da83de3d265256c40ba45cd649115f0e18", "reference": "e8aa08da83de3d265256c40ba45cd649115f0e18", "shasum": "" }, "require": { "php": "^5.6 || ^7.0", - "zendframework/zend-stdlib": "^2.7.7 || ^3.1" + "laminas/laminas-stdlib": "^2.7.7 || ^3.1" }, "require-dev": { "phpunit/phpunit": "^5.7.23 || ^6.4.3", - "zendframework/zend-coding-standard": "~1.0.0", - "zendframework/zend-filter": "^2.7.2", - "zendframework/zend-json": "^2.6 || ^3.0", - "zendframework/zend-validator": "^2.10.1" + "laminas/laminas-coding-standard": "~1.0.0", + "laminas/laminas-filter": "^2.7.2", + "laminas/laminas-json": "^2.6 || ^3.0", + "laminas/laminas-validator": "^2.10.1" }, "suggest": { - "zendframework/zend-filter": "To support DefaultRouteMatcher usage", - "zendframework/zend-validator": "To support DefaultRouteMatcher usage" + "laminas/laminas-filter": "To support DefaultRouteMatcher usage", + "laminas/laminas-validator": "To support DefaultRouteMatcher usage" }, "type": "library", "extra": { @@ -2686,7 +2686,7 @@ }, "autoload": { "psr-4": { - "Zend\\Console\\": "src/" + "Laminas\\Console\\": "src/" } }, "notification-url": "https://packagist.org/downloads/", @@ -2702,31 +2702,31 @@ "time": "2018-01-25T19:08:04+00:00" }, { - "name": "zendframework/zend-crypt", + "name": "laminas/laminas-crypt", "version": "2.6.0", "source": { "type": "git", - "url": "https://github.com/zendframework/zend-crypt.git", + "url": "https://github.com/laminas/laminas-crypt.git", "reference": "1b2f5600bf6262904167116fa67b58ab1457036d" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/zendframework/zend-crypt/zipball/1b2f5600bf6262904167116fa67b58ab1457036d", + "url": "https://api.github.com/repos/laminas/laminas-crypt/zipball/1b2f5600bf6262904167116fa67b58ab1457036d", "reference": "1b2f5600bf6262904167116fa67b58ab1457036d", "shasum": "" }, "require": { "container-interop/container-interop": "~1.0", "php": "^5.5 || ^7.0", - "zendframework/zend-math": "^2.6", - "zendframework/zend-stdlib": "^2.7 || ^3.0" + "laminas/laminas-math": "^2.6", + "laminas/laminas-stdlib": "^2.7 || ^3.0" }, "require-dev": { "fabpot/php-cs-fixer": "1.7.*", "phpunit/phpunit": "~4.0" }, "suggest": { - "ext-mcrypt": "Required for most features of Zend\\Crypt" + "ext-mcrypt": "Required for most features of Laminas\\Crypt" }, "type": "library", "extra": { @@ -2737,14 +2737,14 @@ }, "autoload": { "psr-4": { - "Zend\\Crypt\\": "src/" + "Laminas\\Crypt\\": "src/" } }, "notification-url": "https://packagist.org/downloads/", "license": [ "BSD-3-Clause" ], - "homepage": "https://github.com/zendframework/zend-crypt", + "homepage": "https://github.com/laminas/laminas-crypt", "keywords": [ "crypt", "zf2" @@ -2752,34 +2752,34 @@ "time": "2016-02-03T23:46:30+00:00" }, { - "name": "zendframework/zend-db", + "name": "laminas/laminas-db", "version": "2.10.0", "source": { "type": "git", - "url": "https://github.com/zendframework/zend-db.git", + "url": "https://github.com/laminas/laminas-db.git", "reference": "77022f06f6ffd384fa86d22ab8d8bbdb925a1e8e" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/zendframework/zend-db/zipball/77022f06f6ffd384fa86d22ab8d8bbdb925a1e8e", + "url": "https://api.github.com/repos/laminas/laminas-db/zipball/77022f06f6ffd384fa86d22ab8d8bbdb925a1e8e", "reference": "77022f06f6ffd384fa86d22ab8d8bbdb925a1e8e", "shasum": "" }, "require": { "php": "^5.6 || ^7.0", - "zendframework/zend-stdlib": "^2.7 || ^3.0" + "laminas/laminas-stdlib": "^2.7 || ^3.0" }, "require-dev": { "phpunit/phpunit": "^5.7.25 || ^6.4.4", - "zendframework/zend-coding-standard": "~1.0.0", - "zendframework/zend-eventmanager": "^2.6.2 || ^3.0", - "zendframework/zend-hydrator": "^1.1 || ^2.1 || ^3.0", - "zendframework/zend-servicemanager": "^2.7.5 || ^3.0.3" + "laminas/laminas-coding-standard": "~1.0.0", + "laminas/laminas-eventmanager": "^2.6.2 || ^3.0", + "laminas/laminas-hydrator": "^1.1 || ^2.1 || ^3.0", + "laminas/laminas-servicemanager": "^2.7.5 || ^3.0.3" }, "suggest": { - "zendframework/zend-eventmanager": "Zend\\EventManager component", - "zendframework/zend-hydrator": "Zend\\Hydrator component for using HydratingResultSets", - "zendframework/zend-servicemanager": "Zend\\ServiceManager component" + "laminas/laminas-eventmanager": "Laminas\\EventManager component", + "laminas/laminas-hydrator": "Laminas\\Hydrator component for using HydratingResultSets", + "laminas/laminas-servicemanager": "Laminas\\ServiceManager component" }, "type": "library", "extra": { @@ -2788,13 +2788,13 @@ "dev-develop": "2.10-dev" }, "zf": { - "component": "Zend\\Db", - "config-provider": "Zend\\Db\\ConfigProvider" + "component": "Laminas\\Db", + "config-provider": "Laminas\\Db\\ConfigProvider" } }, "autoload": { "psr-4": { - "Zend\\Db\\": "src/" + "Laminas\\Db\\": "src/" } }, "notification-url": "https://packagist.org/downloads/", @@ -2810,24 +2810,24 @@ "time": "2019-02-25T11:37:45+00:00" }, { - "name": "zendframework/zend-di", + "name": "laminas/laminas-di", "version": "2.6.1", "source": { "type": "git", - "url": "https://github.com/zendframework/zend-di.git", + "url": "https://github.com/laminas/laminas-di.git", "reference": "1fd1ba85660b5a2718741b38639dc7c4c3194b37" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/zendframework/zend-di/zipball/1fd1ba85660b5a2718741b38639dc7c4c3194b37", + "url": "https://api.github.com/repos/laminas/laminas-di/zipball/1fd1ba85660b5a2718741b38639dc7c4c3194b37", "reference": "1fd1ba85660b5a2718741b38639dc7c4c3194b37", "shasum": "" }, "require": { "container-interop/container-interop": "^1.1", "php": "^5.5 || ^7.0", - "zendframework/zend-code": "^2.6 || ^3.0", - "zendframework/zend-stdlib": "^2.7 || ^3.0" + "laminas/laminas-code": "^2.6 || ^3.0", + "laminas/laminas-stdlib": "^2.7 || ^3.0" }, "require-dev": { "fabpot/php-cs-fixer": "1.7.*", @@ -2842,14 +2842,14 @@ }, "autoload": { "psr-4": { - "Zend\\Di\\": "src/" + "Laminas\\Di\\": "src/" } }, "notification-url": "https://packagist.org/downloads/", "license": [ "BSD-3-Clause" ], - "homepage": "https://github.com/zendframework/zend-di", + "homepage": "https://github.com/laminas/laminas-di", "keywords": [ "di", "zf2" @@ -2857,16 +2857,16 @@ "time": "2016-04-25T20:58:11+00:00" }, { - "name": "zendframework/zend-diactoros", + "name": "laminas/laminas-diactoros", "version": "1.8.6", "source": { "type": "git", - "url": "https://github.com/zendframework/zend-diactoros.git", + "url": "https://github.com/laminas/laminas-diactoros.git", "reference": "20da13beba0dde8fb648be3cc19765732790f46e" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/zendframework/zend-diactoros/zipball/20da13beba0dde8fb648be3cc19765732790f46e", + "url": "https://api.github.com/repos/laminas/laminas-diactoros/zipball/20da13beba0dde8fb648be3cc19765732790f46e", "reference": "20da13beba0dde8fb648be3cc19765732790f46e", "shasum": "" }, @@ -2882,7 +2882,7 @@ "ext-libxml": "*", "php-http/psr7-integration-tests": "dev-master", "phpunit/phpunit": "^5.7.16 || ^6.0.8 || ^7.2.7", - "zendframework/zend-coding-standard": "~1.0" + "laminas/laminas-coding-standard": "~1.0" }, "type": "library", "extra": { @@ -2904,7 +2904,7 @@ "src/functions/parse_cookie_header.php" ], "psr-4": { - "Zend\\Diactoros\\": "src/" + "Laminas\\Diactoros\\": "src/" } }, "notification-url": "https://packagist.org/downloads/", @@ -2912,7 +2912,7 @@ "BSD-2-Clause" ], "description": "PSR HTTP Message implementations", - "homepage": "https://github.com/zendframework/zend-diactoros", + "homepage": "https://github.com/laminas/laminas-diactoros", "keywords": [ "http", "psr", @@ -2921,16 +2921,16 @@ "time": "2018-09-05T19:29:37+00:00" }, { - "name": "zendframework/zend-escaper", + "name": "laminas/laminas-escaper", "version": "2.6.0", "source": { "type": "git", - "url": "https://github.com/zendframework/zend-escaper.git", + "url": "https://github.com/laminas/laminas-escaper.git", "reference": "31d8aafae982f9568287cb4dce987e6aff8fd074" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/zendframework/zend-escaper/zipball/31d8aafae982f9568287cb4dce987e6aff8fd074", + "url": "https://api.github.com/repos/laminas/laminas-escaper/zipball/31d8aafae982f9568287cb4dce987e6aff8fd074", "reference": "31d8aafae982f9568287cb4dce987e6aff8fd074", "shasum": "" }, @@ -2939,7 +2939,7 @@ }, "require-dev": { "phpunit/phpunit": "^5.7.27 || ^6.5.8 || ^7.1.2", - "zendframework/zend-coding-standard": "~1.0.0" + "laminas/laminas-coding-standard": "~1.0.0" }, "type": "library", "extra": { @@ -2950,7 +2950,7 @@ }, "autoload": { "psr-4": { - "Zend\\Escaper\\": "src/" + "Laminas\\Escaper\\": "src/" } }, "notification-url": "https://packagist.org/downloads/", @@ -2966,22 +2966,22 @@ "time": "2018-04-25T15:48:53+00:00" }, { - "name": "zendframework/zend-eventmanager", + "name": "laminas/laminas-eventmanager", "version": "2.6.4", "source": { "type": "git", - "url": "https://github.com/zendframework/zend-eventmanager.git", + "url": "https://github.com/laminas/laminas-eventmanager.git", "reference": "d238c443220dce4b6396579c8ab2200ec25f9108" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/zendframework/zend-eventmanager/zipball/d238c443220dce4b6396579c8ab2200ec25f9108", + "url": "https://api.github.com/repos/laminas/laminas-eventmanager/zipball/d238c443220dce4b6396579c8ab2200ec25f9108", "reference": "d238c443220dce4b6396579c8ab2200ec25f9108", "shasum": "" }, "require": { "php": "^5.5 || ^7.0", - "zendframework/zend-stdlib": "^2.7" + "laminas/laminas-stdlib": "^2.7" }, "require-dev": { "athletic/athletic": "dev-master", @@ -2998,14 +2998,14 @@ }, "autoload": { "psr-4": { - "Zend\\EventManager\\": "src/" + "Laminas\\EventManager\\": "src/" } }, "notification-url": "https://packagist.org/downloads/", "license": [ "BSD-3-Clause" ], - "homepage": "https://github.com/zendframework/zend-eventmanager", + "homepage": "https://github.com/laminas/laminas-eventmanager", "keywords": [ "eventmanager", "zf2" @@ -3013,41 +3013,41 @@ "time": "2017-12-12T17:48:56+00:00" }, { - "name": "zendframework/zend-feed", + "name": "laminas/laminas-feed", "version": "2.10.3", "source": { "type": "git", - "url": "https://github.com/zendframework/zend-feed.git", + "url": "https://github.com/laminas/laminas-feed.git", "reference": "6641f4cf3f4586c63f83fd70b6d19966025c8888" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/zendframework/zend-feed/zipball/6641f4cf3f4586c63f83fd70b6d19966025c8888", + "url": "https://api.github.com/repos/laminas/laminas-feed/zipball/6641f4cf3f4586c63f83fd70b6d19966025c8888", "reference": "6641f4cf3f4586c63f83fd70b6d19966025c8888", "shasum": "" }, "require": { "php": "^5.6 || ^7.0", - "zendframework/zend-escaper": "^2.5.2", - "zendframework/zend-stdlib": "^2.7.7 || ^3.1" + "laminas/laminas-escaper": "^2.5.2", + "laminas/laminas-stdlib": "^2.7.7 || ^3.1" }, "require-dev": { "phpunit/phpunit": "^5.7.23 || ^6.4.3", "psr/http-message": "^1.0.1", - "zendframework/zend-cache": "^2.7.2", - "zendframework/zend-coding-standard": "~1.0.0", - "zendframework/zend-db": "^2.8.2", - "zendframework/zend-http": "^2.7", - "zendframework/zend-servicemanager": "^2.7.8 || ^3.3", - "zendframework/zend-validator": "^2.10.1" + "laminas/laminas-cache": "^2.7.2", + "laminas/laminas-coding-standard": "~1.0.0", + "laminas/laminas-db": "^2.8.2", + "laminas/laminas-http": "^2.7", + "laminas/laminas-servicemanager": "^2.7.8 || ^3.3", + "laminas/laminas-validator": "^2.10.1" }, "suggest": { - "psr/http-message": "PSR-7 ^1.0.1, if you wish to use Zend\\Feed\\Reader\\Http\\Psr7ResponseDecorator", - "zendframework/zend-cache": "Zend\\Cache component, for optionally caching feeds between requests", - "zendframework/zend-db": "Zend\\Db component, for use with PubSubHubbub", - "zendframework/zend-http": "Zend\\Http for PubSubHubbub, and optionally for use with Zend\\Feed\\Reader", - "zendframework/zend-servicemanager": "Zend\\ServiceManager component, for easily extending ExtensionManager implementations", - "zendframework/zend-validator": "Zend\\Validator component, for validating email addresses used in Atom feeds and entries when using the Writer subcomponent" + "psr/http-message": "PSR-7 ^1.0.1, if you wish to use Laminas\\Feed\\Reader\\Http\\Psr7ResponseDecorator", + "laminas/laminas-cache": "Laminas\\Cache component, for optionally caching feeds between requests", + "laminas/laminas-db": "Laminas\\Db component, for use with PubSubHubbub", + "laminas/laminas-http": "Laminas\\Http for PubSubHubbub, and optionally for use with Laminas\\Feed\\Reader", + "laminas/laminas-servicemanager": "Laminas\\ServiceManager component, for easily extending ExtensionManager implementations", + "laminas/laminas-validator": "Laminas\\Validator component, for validating email addresses used in Atom feeds and entries when using the Writer subcomponent" }, "type": "library", "extra": { @@ -3058,7 +3058,7 @@ }, "autoload": { "psr-4": { - "Zend\\Feed\\": "src/" + "Laminas\\Feed\\": "src/" } }, "notification-url": "https://packagist.org/downloads/", @@ -3074,41 +3074,41 @@ "time": "2018-08-01T13:53:20+00:00" }, { - "name": "zendframework/zend-filter", + "name": "laminas/laminas-filter", "version": "2.9.1", "source": { "type": "git", - "url": "https://github.com/zendframework/zend-filter.git", + "url": "https://github.com/laminas/laminas-filter.git", "reference": "1c3e6d02f9cd5f6c929c9859498f5efbe216e86f" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/zendframework/zend-filter/zipball/1c3e6d02f9cd5f6c929c9859498f5efbe216e86f", + "url": "https://api.github.com/repos/laminas/laminas-filter/zipball/1c3e6d02f9cd5f6c929c9859498f5efbe216e86f", "reference": "1c3e6d02f9cd5f6c929c9859498f5efbe216e86f", "shasum": "" }, "require": { "php": "^5.6 || ^7.0", - "zendframework/zend-stdlib": "^2.7.7 || ^3.1" + "laminas/laminas-stdlib": "^2.7.7 || ^3.1" }, "conflict": { - "zendframework/zend-validator": "<2.10.1" + "laminas/laminas-validator": "<2.10.1" }, "require-dev": { "pear/archive_tar": "^1.4.3", "phpunit/phpunit": "^5.7.23 || ^6.4.3", "psr/http-factory": "^1.0", - "zendframework/zend-coding-standard": "~1.0.0", - "zendframework/zend-crypt": "^3.2.1", - "zendframework/zend-servicemanager": "^2.7.8 || ^3.3", - "zendframework/zend-uri": "^2.6" + "laminas/laminas-coding-standard": "~1.0.0", + "laminas/laminas-crypt": "^3.2.1", + "laminas/laminas-servicemanager": "^2.7.8 || ^3.3", + "laminas/laminas-uri": "^2.6" }, "suggest": { "psr/http-factory-implementation": "psr/http-factory-implementation, for creating file upload instances when consuming PSR-7 in file upload filters", - "zendframework/zend-crypt": "Zend\\Crypt component, for encryption filters", - "zendframework/zend-i18n": "Zend\\I18n component for filters depending on i18n functionality", - "zendframework/zend-servicemanager": "Zend\\ServiceManager component, for using the filter chain functionality", - "zendframework/zend-uri": "Zend\\Uri component, for the UriNormalize filter" + "laminas/laminas-crypt": "Laminas\\Crypt component, for encryption filters", + "laminas/laminas-i18n": "Laminas\\I18n component for filters depending on i18n functionality", + "laminas/laminas-servicemanager": "Laminas\\ServiceManager component, for using the filter chain functionality", + "laminas/laminas-uri": "Laminas\\Uri component, for the UriNormalize filter" }, "type": "library", "extra": { @@ -3117,13 +3117,13 @@ "dev-develop": "2.10.x-dev" }, "zf": { - "component": "Zend\\Filter", - "config-provider": "Zend\\Filter\\ConfigProvider" + "component": "Laminas\\Filter", + "config-provider": "Laminas\\Filter\\ConfigProvider" } }, "autoload": { "psr-4": { - "Zend\\Filter\\": "src/" + "Laminas\\Filter\\": "src/" } }, "notification-url": "https://packagist.org/downloads/", @@ -3139,51 +3139,51 @@ "time": "2018-12-17T16:00:04+00:00" }, { - "name": "zendframework/zend-form", + "name": "laminas/laminas-form", "version": "2.13.0", "source": { "type": "git", - "url": "https://github.com/zendframework/zend-form.git", + "url": "https://github.com/laminas/laminas-form.git", "reference": "c713a12ccbd43148b71c9339e171ca11e3f8a1da" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/zendframework/zend-form/zipball/c713a12ccbd43148b71c9339e171ca11e3f8a1da", + "url": "https://api.github.com/repos/laminas/laminas-form/zipball/c713a12ccbd43148b71c9339e171ca11e3f8a1da", "reference": "c713a12ccbd43148b71c9339e171ca11e3f8a1da", "shasum": "" }, "require": { "php": "^5.6 || ^7.0", - "zendframework/zend-hydrator": "^1.1 || ^2.1 || ^3.0", - "zendframework/zend-inputfilter": "^2.8", - "zendframework/zend-stdlib": "^2.7 || ^3.0" + "laminas/laminas-hydrator": "^1.1 || ^2.1 || ^3.0", + "laminas/laminas-inputfilter": "^2.8", + "laminas/laminas-stdlib": "^2.7 || ^3.0" }, "require-dev": { "doctrine/annotations": "~1.0", "phpunit/phpunit": "^5.7.23 || ^6.5.3", - "zendframework/zend-cache": "^2.6.1", - "zendframework/zend-captcha": "^2.7.1", - "zendframework/zend-code": "^2.6 || ^3.0", - "zendframework/zend-coding-standard": "~1.0.0", - "zendframework/zend-escaper": "^2.5", - "zendframework/zend-eventmanager": "^2.6.2 || ^3.0", - "zendframework/zend-filter": "^2.6", - "zendframework/zend-i18n": "^2.6", - "zendframework/zend-servicemanager": "^2.7.5 || ^3.0.3", - "zendframework/zend-session": "^2.8.1", - "zendframework/zend-text": "^2.6", - "zendframework/zend-validator": "^2.6", - "zendframework/zend-view": "^2.6.2", - "zendframework/zendservice-recaptcha": "^3.0.0" + "laminas/laminas-cache": "^2.6.1", + "laminas/laminas-captcha": "^2.7.1", + "laminas/laminas-code": "^2.6 || ^3.0", + "laminas/laminas-coding-standard": "~1.0.0", + "laminas/laminas-escaper": "^2.5", + "laminas/laminas-eventmanager": "^2.6.2 || ^3.0", + "laminas/laminas-filter": "^2.6", + "laminas/laminas-i18n": "^2.6", + "laminas/laminas-servicemanager": "^2.7.5 || ^3.0.3", + "laminas/laminas-session": "^2.8.1", + "laminas/laminas-text": "^2.6", + "laminas/laminas-validator": "^2.6", + "laminas/laminas-view": "^2.6.2", + "laminas/laminas-recaptcha": "^3.0.0" }, "suggest": { - "zendframework/zend-captcha": "^2.7.1, required for using CAPTCHA form elements", - "zendframework/zend-code": "^2.6 || ^3.0, required to use zend-form annotations support", - "zendframework/zend-eventmanager": "^2.6.2 || ^3.0, reuired for zend-form annotations support", - "zendframework/zend-i18n": "^2.6, required when using zend-form view helpers", - "zendframework/zend-servicemanager": "^2.7.5 || ^3.0.3, required to use the form factories or provide services", - "zendframework/zend-view": "^2.6.2, required for using the zend-form view helpers", - "zendframework/zendservice-recaptcha": "in order to use the ReCaptcha form element" + "laminas/laminas-captcha": "^2.7.1, required for using CAPTCHA form elements", + "laminas/laminas-code": "^2.6 || ^3.0, required to use zend-form annotations support", + "laminas/laminas-eventmanager": "^2.6.2 || ^3.0, reuired for zend-form annotations support", + "laminas/laminas-i18n": "^2.6, required when using zend-form view helpers", + "laminas/laminas-servicemanager": "^2.7.5 || ^3.0.3, required to use the form factories or provide services", + "laminas/laminas-view": "^2.6.2, required for using the zend-form view helpers", + "laminas/laminas-recaptcha": "in order to use the ReCaptcha form element" }, "type": "library", "extra": { @@ -3192,13 +3192,13 @@ "dev-develop": "2.14.x-dev" }, "zf": { - "component": "Zend\\Form", - "config-provider": "Zend\\Form\\ConfigProvider" + "component": "Laminas\\Form", + "config-provider": "Laminas\\Form\\ConfigProvider" } }, "autoload": { "psr-4": { - "Zend\\Form\\": "src/" + "Laminas\\Form\\": "src/" }, "files": [ "autoload/formElementManagerPolyfill.php" @@ -3217,30 +3217,30 @@ "time": "2018-12-11T22:51:29+00:00" }, { - "name": "zendframework/zend-http", + "name": "laminas/laminas-http", "version": "2.8.4", "source": { "type": "git", - "url": "https://github.com/zendframework/zend-http.git", + "url": "https://github.com/laminas/laminas-http.git", "reference": "d160aedc096be230af0fe9c31151b2b33ad4e807" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/zendframework/zend-http/zipball/d160aedc096be230af0fe9c31151b2b33ad4e807", + "url": "https://api.github.com/repos/laminas/laminas-http/zipball/d160aedc096be230af0fe9c31151b2b33ad4e807", "reference": "d160aedc096be230af0fe9c31151b2b33ad4e807", "shasum": "" }, "require": { "php": "^5.6 || ^7.0", - "zendframework/zend-loader": "^2.5.1", - "zendframework/zend-stdlib": "^3.1 || ^2.7.7", - "zendframework/zend-uri": "^2.5.2", - "zendframework/zend-validator": "^2.10.1" + "laminas/laminas-loader": "^2.5.1", + "laminas/laminas-stdlib": "^3.1 || ^2.7.7", + "laminas/laminas-uri": "^2.5.2", + "laminas/laminas-validator": "^2.10.1" }, "require-dev": { "phpunit/phpunit": "^5.7.27 || ^6.5.8 || ^7.1.3", - "zendframework/zend-coding-standard": "~1.0.0", - "zendframework/zend-config": "^3.1 || ^2.6" + "laminas/laminas-coding-standard": "~1.0.0", + "laminas/laminas-config": "^3.1 || ^2.6" }, "suggest": { "paragonie/certainty": "For automated management of cacert.pem" @@ -3254,7 +3254,7 @@ }, "autoload": { "psr-4": { - "Zend\\Http\\": "src/" + "Laminas\\Http\\": "src/" } }, "notification-url": "https://packagist.org/downloads/", @@ -3272,37 +3272,37 @@ "time": "2019-02-07T17:47:08+00:00" }, { - "name": "zendframework/zend-hydrator", + "name": "laminas/laminas-hydrator", "version": "1.1.0", "source": { "type": "git", - "url": "https://github.com/zendframework/zend-hydrator.git", + "url": "https://github.com/laminas/laminas-hydrator.git", "reference": "22652e1661a5a10b3f564cf7824a2206cf5a4a65" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/zendframework/zend-hydrator/zipball/22652e1661a5a10b3f564cf7824a2206cf5a4a65", + "url": "https://api.github.com/repos/laminas/laminas-hydrator/zipball/22652e1661a5a10b3f564cf7824a2206cf5a4a65", "reference": "22652e1661a5a10b3f564cf7824a2206cf5a4a65", "shasum": "" }, "require": { "php": "^5.5 || ^7.0", - "zendframework/zend-stdlib": "^2.7 || ^3.0" + "laminas/laminas-stdlib": "^2.7 || ^3.0" }, "require-dev": { "phpunit/phpunit": "~4.0", "squizlabs/php_codesniffer": "^2.0@dev", - "zendframework/zend-eventmanager": "^2.6.2 || ^3.0", - "zendframework/zend-filter": "^2.6", - "zendframework/zend-inputfilter": "^2.6", - "zendframework/zend-serializer": "^2.6.1", - "zendframework/zend-servicemanager": "^2.7.5 || ^3.0.3" + "laminas/laminas-eventmanager": "^2.6.2 || ^3.0", + "laminas/laminas-filter": "^2.6", + "laminas/laminas-inputfilter": "^2.6", + "laminas/laminas-serializer": "^2.6.1", + "laminas/laminas-servicemanager": "^2.7.5 || ^3.0.3" }, "suggest": { - "zendframework/zend-eventmanager": "^2.6.2 || ^3.0, to support aggregate hydrator usage", - "zendframework/zend-filter": "^2.6, to support naming strategy hydrator usage", - "zendframework/zend-serializer": "^2.6.1, to use the SerializableStrategy", - "zendframework/zend-servicemanager": "^2.7.5 || ^3.0.3, to support hydrator plugin manager usage" + "laminas/laminas-eventmanager": "^2.6.2 || ^3.0, to support aggregate hydrator usage", + "laminas/laminas-filter": "^2.6, to support naming strategy hydrator usage", + "laminas/laminas-serializer": "^2.6.1, to use the SerializableStrategy", + "laminas/laminas-servicemanager": "^2.7.5 || ^3.0.3, to support hydrator plugin manager usage" }, "type": "library", "extra": { @@ -3315,14 +3315,14 @@ }, "autoload": { "psr-4": { - "Zend\\Hydrator\\": "src/" + "Laminas\\Hydrator\\": "src/" } }, "notification-url": "https://packagist.org/downloads/", "license": [ "BSD-3-Clause" ], - "homepage": "https://github.com/zendframework/zend-hydrator", + "homepage": "https://github.com/laminas/laminas-hydrator", "keywords": [ "hydrator", "zf2" @@ -3330,44 +3330,44 @@ "time": "2016-02-18T22:38:26+00:00" }, { - "name": "zendframework/zend-i18n", + "name": "laminas/laminas-i18n", "version": "2.9.0", "source": { "type": "git", - "url": "https://github.com/zendframework/zend-i18n.git", + "url": "https://github.com/laminas/laminas-i18n.git", "reference": "6d69af5a04e1a4de7250043cb1322f077a0cdb7f" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/zendframework/zend-i18n/zipball/6d69af5a04e1a4de7250043cb1322f077a0cdb7f", + "url": "https://api.github.com/repos/laminas/laminas-i18n/zipball/6d69af5a04e1a4de7250043cb1322f077a0cdb7f", "reference": "6d69af5a04e1a4de7250043cb1322f077a0cdb7f", "shasum": "" }, "require": { "php": "^5.6 || ^7.0", - "zendframework/zend-stdlib": "^2.7 || ^3.0" + "laminas/laminas-stdlib": "^2.7 || ^3.0" }, "require-dev": { "phpunit/phpunit": "^5.7.27 || ^6.5.8 || ^7.1.2", - "zendframework/zend-cache": "^2.6.1", - "zendframework/zend-coding-standard": "~1.0.0", - "zendframework/zend-config": "^2.6", - "zendframework/zend-eventmanager": "^2.6.2 || ^3.0", - "zendframework/zend-filter": "^2.6.1", - "zendframework/zend-servicemanager": "^2.7.5 || ^3.0.3", - "zendframework/zend-validator": "^2.6", - "zendframework/zend-view": "^2.6.3" + "laminas/laminas-cache": "^2.6.1", + "laminas/laminas-coding-standard": "~1.0.0", + "laminas/laminas-config": "^2.6", + "laminas/laminas-eventmanager": "^2.6.2 || ^3.0", + "laminas/laminas-filter": "^2.6.1", + "laminas/laminas-servicemanager": "^2.7.5 || ^3.0.3", + "laminas/laminas-validator": "^2.6", + "laminas/laminas-view": "^2.6.3" }, "suggest": { - "ext-intl": "Required for most features of Zend\\I18n; included in default builds of PHP", - "zendframework/zend-cache": "Zend\\Cache component", - "zendframework/zend-config": "Zend\\Config component", - "zendframework/zend-eventmanager": "You should install this package to use the events in the translator", - "zendframework/zend-filter": "You should install this package to use the provided filters", - "zendframework/zend-i18n-resources": "Translation resources", - "zendframework/zend-servicemanager": "Zend\\ServiceManager component", - "zendframework/zend-validator": "You should install this package to use the provided validators", - "zendframework/zend-view": "You should install this package to use the provided view helpers" + "ext-intl": "Required for most features of Laminas\\I18n; included in default builds of PHP", + "laminas/laminas-cache": "Laminas\\Cache component", + "laminas/laminas-config": "Laminas\\Config component", + "laminas/laminas-eventmanager": "You should install this package to use the events in the translator", + "laminas/laminas-filter": "You should install this package to use the provided filters", + "laminas/laminas-i18n-resources": "Translation resources", + "laminas/laminas-servicemanager": "Laminas\\ServiceManager component", + "laminas/laminas-validator": "You should install this package to use the provided validators", + "laminas/laminas-view": "You should install this package to use the provided view helpers" }, "type": "library", "extra": { @@ -3376,13 +3376,13 @@ "dev-develop": "2.10.x-dev" }, "zf": { - "component": "Zend\\I18n", - "config-provider": "Zend\\I18n\\ConfigProvider" + "component": "Laminas\\I18n", + "config-provider": "Laminas\\I18n\\ConfigProvider" } }, "autoload": { "psr-4": { - "Zend\\I18n\\": "src/" + "Laminas\\I18n\\": "src/" } }, "notification-url": "https://packagist.org/downloads/", @@ -3398,30 +3398,30 @@ "time": "2018-05-16T16:39:13+00:00" }, { - "name": "zendframework/zend-inputfilter", + "name": "laminas/laminas-inputfilter", "version": "2.10.0", "source": { "type": "git", - "url": "https://github.com/zendframework/zend-inputfilter.git", + "url": "https://github.com/laminas/laminas-inputfilter.git", "reference": "4f52b71ec9cef3a06e3bba8f5c2124e94055ec0c" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/zendframework/zend-inputfilter/zipball/4f52b71ec9cef3a06e3bba8f5c2124e94055ec0c", + "url": "https://api.github.com/repos/laminas/laminas-inputfilter/zipball/4f52b71ec9cef3a06e3bba8f5c2124e94055ec0c", "reference": "4f52b71ec9cef3a06e3bba8f5c2124e94055ec0c", "shasum": "" }, "require": { "php": "^5.6 || ^7.0", - "zendframework/zend-filter": "^2.9.1", - "zendframework/zend-servicemanager": "^2.7.10 || ^3.3.1", - "zendframework/zend-stdlib": "^2.7 || ^3.0", - "zendframework/zend-validator": "^2.11" + "laminas/laminas-filter": "^2.9.1", + "laminas/laminas-servicemanager": "^2.7.10 || ^3.3.1", + "laminas/laminas-stdlib": "^2.7 || ^3.0", + "laminas/laminas-validator": "^2.11" }, "require-dev": { "phpunit/phpunit": "^5.7.23 || ^6.4.3", "psr/http-message": "^1.0", - "zendframework/zend-coding-standard": "~1.0.0" + "laminas/laminas-coding-standard": "~1.0.0" }, "suggest": { "psr/http-message-implementation": "PSR-7 is required if you wish to validate PSR-7 UploadedFileInterface payloads" @@ -3433,13 +3433,13 @@ "dev-develop": "2.11.x-dev" }, "zf": { - "component": "Zend\\InputFilter", - "config-provider": "Zend\\InputFilter\\ConfigProvider" + "component": "Laminas\\InputFilter", + "config-provider": "Laminas\\InputFilter\\ConfigProvider" } }, "autoload": { "psr-4": { - "Zend\\InputFilter\\": "src/" + "Laminas\\InputFilter\\": "src/" } }, "notification-url": "https://packagist.org/downloads/", @@ -3455,16 +3455,16 @@ "time": "2019-01-30T16:58:51+00:00" }, { - "name": "zendframework/zend-json", + "name": "laminas/laminas-json", "version": "2.6.1", "source": { "type": "git", - "url": "https://github.com/zendframework/zend-json.git", + "url": "https://github.com/laminas/laminas-json.git", "reference": "4c8705dbe4ad7d7e51b2876c5b9eea0ef916ba28" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/zendframework/zend-json/zipball/4c8705dbe4ad7d7e51b2876c5b9eea0ef916ba28", + "url": "https://api.github.com/repos/laminas/laminas-json/zipball/4c8705dbe4ad7d7e51b2876c5b9eea0ef916ba28", "reference": "4c8705dbe4ad7d7e51b2876c5b9eea0ef916ba28", "shasum": "" }, @@ -3474,16 +3474,16 @@ "require-dev": { "fabpot/php-cs-fixer": "1.7.*", "phpunit/phpunit": "~4.0", - "zendframework/zend-http": "^2.5.4", - "zendframework/zend-server": "^2.6.1", - "zendframework/zend-stdlib": "^2.5 || ^3.0", + "laminas/laminas-http": "^2.5.4", + "laminas/laminas-server": "^2.6.1", + "laminas/laminas-stdlib": "^2.5 || ^3.0", "zendframework/zendxml": "^1.0.2" }, "suggest": { - "zendframework/zend-http": "Zend\\Http component, required to use Zend\\Json\\Server", - "zendframework/zend-server": "Zend\\Server component, required to use Zend\\Json\\Server", - "zendframework/zend-stdlib": "Zend\\Stdlib component, for use with caching Zend\\Json\\Server responses", - "zendframework/zendxml": "To support Zend\\Json\\Json::fromXml() usage" + "laminas/laminas-http": "Laminas\\Http component, required to use Laminas\\Json\\Server", + "laminas/laminas-server": "Laminas\\Server component, required to use Laminas\\Json\\Server", + "laminas/laminas-stdlib": "Laminas\\Stdlib component, for use with caching Laminas\\Json\\Server responses", + "zendframework/zendxml": "To support Laminas\\Json\\Json::fromXml() usage" }, "type": "library", "extra": { @@ -3494,7 +3494,7 @@ }, "autoload": { "psr-4": { - "Zend\\Json\\": "src/" + "Laminas\\Json\\": "src/" } }, "notification-url": "https://packagist.org/downloads/", @@ -3502,7 +3502,7 @@ "BSD-3-Clause" ], "description": "provides convenience methods for serializing native PHP to JSON and decoding JSON to native PHP", - "homepage": "https://github.com/zendframework/zend-json", + "homepage": "https://github.com/laminas/laminas-json", "keywords": [ "json", "zf2" @@ -3510,16 +3510,16 @@ "time": "2016-02-04T21:20:26+00:00" }, { - "name": "zendframework/zend-loader", + "name": "laminas/laminas-loader", "version": "2.6.0", "source": { "type": "git", - "url": "https://github.com/zendframework/zend-loader.git", + "url": "https://github.com/laminas/laminas-loader.git", "reference": "78f11749ea340f6ca316bca5958eef80b38f9b6c" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/zendframework/zend-loader/zipball/78f11749ea340f6ca316bca5958eef80b38f9b6c", + "url": "https://api.github.com/repos/laminas/laminas-loader/zipball/78f11749ea340f6ca316bca5958eef80b38f9b6c", "reference": "78f11749ea340f6ca316bca5958eef80b38f9b6c", "shasum": "" }, @@ -3528,7 +3528,7 @@ }, "require-dev": { "phpunit/phpunit": "^5.7.27 || ^6.5.8 || ^7.1.4", - "zendframework/zend-coding-standard": "~1.0.0" + "laminas/laminas-coding-standard": "~1.0.0" }, "type": "library", "extra": { @@ -3539,7 +3539,7 @@ }, "autoload": { "psr-4": { - "Zend\\Loader\\": "src/" + "Laminas\\Loader\\": "src/" } }, "notification-url": "https://packagist.org/downloads/", @@ -3555,24 +3555,24 @@ "time": "2018-04-30T15:20:54+00:00" }, { - "name": "zendframework/zend-log", + "name": "laminas/laminas-log", "version": "2.10.0", "source": { "type": "git", - "url": "https://github.com/zendframework/zend-log.git", + "url": "https://github.com/laminas/laminas-log.git", "reference": "9cec3b092acb39963659c2f32441cccc56b3f430" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/zendframework/zend-log/zipball/9cec3b092acb39963659c2f32441cccc56b3f430", + "url": "https://api.github.com/repos/laminas/laminas-log/zipball/9cec3b092acb39963659c2f32441cccc56b3f430", "reference": "9cec3b092acb39963659c2f32441cccc56b3f430", "shasum": "" }, "require": { "php": "^5.6 || ^7.0", "psr/log": "^1.0", - "zendframework/zend-servicemanager": "^2.7.5 || ^3.0.3", - "zendframework/zend-stdlib": "^2.7 || ^3.0" + "laminas/laminas-servicemanager": "^2.7.5 || ^3.0.3", + "laminas/laminas-stdlib": "^2.7 || ^3.0" }, "provide": { "psr/log-implementation": "1.0.0" @@ -3580,21 +3580,21 @@ "require-dev": { "mikey179/vfsstream": "^1.6", "phpunit/phpunit": "^5.7.15 || ^6.0.8", - "zendframework/zend-coding-standard": "~1.0.0", - "zendframework/zend-db": "^2.6", - "zendframework/zend-escaper": "^2.5", - "zendframework/zend-filter": "^2.5", - "zendframework/zend-mail": "^2.6.1", - "zendframework/zend-validator": "^2.10.1" + "laminas/laminas-coding-standard": "~1.0.0", + "laminas/laminas-db": "^2.6", + "laminas/laminas-escaper": "^2.5", + "laminas/laminas-filter": "^2.5", + "laminas/laminas-mail": "^2.6.1", + "laminas/laminas-validator": "^2.10.1" }, "suggest": { "ext-mongo": "mongo extension to use Mongo writer", "ext-mongodb": "mongodb extension to use MongoDB writer", - "zendframework/zend-console": "Zend\\Console component to use the RequestID log processor", - "zendframework/zend-db": "Zend\\Db component to use the database log writer", - "zendframework/zend-escaper": "Zend\\Escaper component, for use in the XML log formatter", - "zendframework/zend-mail": "Zend\\Mail component to use the email log writer", - "zendframework/zend-validator": "Zend\\Validator component to block invalid log messages" + "laminas/laminas-console": "Laminas\\Console component to use the RequestID log processor", + "laminas/laminas-db": "Laminas\\Db component to use the database log writer", + "laminas/laminas-escaper": "Laminas\\Escaper component, for use in the XML log formatter", + "laminas/laminas-mail": "Laminas\\Mail component to use the email log writer", + "laminas/laminas-validator": "Laminas\\Validator component to block invalid log messages" }, "type": "library", "extra": { @@ -3603,13 +3603,13 @@ "dev-develop": "2.11.x-dev" }, "zf": { - "component": "Zend\\Log", - "config-provider": "Zend\\Log\\ConfigProvider" + "component": "Laminas\\Log", + "config-provider": "Laminas\\Log\\ConfigProvider" } }, "autoload": { "psr-4": { - "Zend\\Log\\": "src/" + "Laminas\\Log\\": "src/" } }, "notification-url": "https://packagist.org/downloads/", @@ -3617,7 +3617,7 @@ "BSD-3-Clause" ], "description": "component for general purpose logging", - "homepage": "https://github.com/zendframework/zend-log", + "homepage": "https://github.com/laminas/laminas-log", "keywords": [ "log", "logging", @@ -3626,16 +3626,16 @@ "time": "2018-04-09T21:59:51+00:00" }, { - "name": "zendframework/zend-mail", + "name": "laminas/laminas-mail", "version": "2.10.0", "source": { "type": "git", - "url": "https://github.com/zendframework/zend-mail.git", + "url": "https://github.com/laminas/laminas-mail.git", "reference": "d7beb63d5f7144a21ac100072c453e63860cdab8" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/zendframework/zend-mail/zipball/d7beb63d5f7144a21ac100072c453e63860cdab8", + "url": "https://api.github.com/repos/laminas/laminas-mail/zipball/d7beb63d5f7144a21ac100072c453e63860cdab8", "reference": "d7beb63d5f7144a21ac100072c453e63860cdab8", "shasum": "" }, @@ -3643,21 +3643,21 @@ "ext-iconv": "*", "php": "^5.6 || ^7.0", "true/punycode": "^2.1", - "zendframework/zend-loader": "^2.5", - "zendframework/zend-mime": "^2.5", - "zendframework/zend-stdlib": "^2.7 || ^3.0", - "zendframework/zend-validator": "^2.10.2" + "laminas/laminas-loader": "^2.5", + "laminas/laminas-mime": "^2.5", + "laminas/laminas-stdlib": "^2.7 || ^3.0", + "laminas/laminas-validator": "^2.10.2" }, "require-dev": { "phpunit/phpunit": "^5.7.25 || ^6.4.4 || ^7.1.4", - "zendframework/zend-coding-standard": "~1.0.0", - "zendframework/zend-config": "^2.6", - "zendframework/zend-crypt": "^2.6 || ^3.0", - "zendframework/zend-servicemanager": "^2.7.10 || ^3.3.1" + "laminas/laminas-coding-standard": "~1.0.0", + "laminas/laminas-config": "^2.6", + "laminas/laminas-crypt": "^2.6 || ^3.0", + "laminas/laminas-servicemanager": "^2.7.10 || ^3.3.1" }, "suggest": { - "zendframework/zend-crypt": "Crammd5 support in SMTP Auth", - "zendframework/zend-servicemanager": "^2.7.10 || ^3.3.1 when using SMTP to deliver messages" + "laminas/laminas-crypt": "Crammd5 support in SMTP Auth", + "laminas/laminas-servicemanager": "^2.7.10 || ^3.3.1 when using SMTP to deliver messages" }, "type": "library", "extra": { @@ -3666,13 +3666,13 @@ "dev-develop": "2.11.x-dev" }, "zf": { - "component": "Zend\\Mail", - "config-provider": "Zend\\Mail\\ConfigProvider" + "component": "Laminas\\Mail", + "config-provider": "Laminas\\Mail\\ConfigProvider" } }, "autoload": { "psr-4": { - "Zend\\Mail\\": "src/" + "Laminas\\Mail\\": "src/" } }, "notification-url": "https://packagist.org/downloads/", @@ -3688,16 +3688,16 @@ "time": "2018-06-07T13:37:07+00:00" }, { - "name": "zendframework/zend-math", + "name": "laminas/laminas-math", "version": "2.7.1", "source": { "type": "git", - "url": "https://github.com/zendframework/zend-math.git", + "url": "https://github.com/laminas/laminas-math.git", "reference": "1abce074004dacac1a32cd54de94ad47ef960d38" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/zendframework/zend-math/zipball/1abce074004dacac1a32cd54de94ad47ef960d38", + "url": "https://api.github.com/repos/laminas/laminas-math/zipball/1abce074004dacac1a32cd54de94ad47ef960d38", "reference": "1abce074004dacac1a32cd54de94ad47ef960d38", "shasum": "" }, @@ -3712,7 +3712,7 @@ "suggest": { "ext-bcmath": "If using the bcmath functionality", "ext-gmp": "If using the gmp functionality", - "ircmaxell/random-lib": "Fallback random byte generator for Zend\\Math\\Rand if Mcrypt extensions is unavailable" + "ircmaxell/random-lib": "Fallback random byte generator for Laminas\\Math\\Rand if Mcrypt extensions is unavailable" }, "type": "library", "extra": { @@ -3723,14 +3723,14 @@ }, "autoload": { "psr-4": { - "Zend\\Math\\": "src/" + "Laminas\\Math\\": "src/" } }, "notification-url": "https://packagist.org/downloads/", "license": [ "BSD-3-Clause" ], - "homepage": "https://github.com/zendframework/zend-math", + "homepage": "https://github.com/laminas/laminas-math", "keywords": [ "math", "zf2" @@ -3738,30 +3738,30 @@ "time": "2018-12-04T15:34:17+00:00" }, { - "name": "zendframework/zend-mime", + "name": "laminas/laminas-mime", "version": "2.7.1", "source": { "type": "git", - "url": "https://github.com/zendframework/zend-mime.git", + "url": "https://github.com/laminas/laminas-mime.git", "reference": "52ae5fa9f12845cae749271034a2d594f0e4c6f2" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/zendframework/zend-mime/zipball/52ae5fa9f12845cae749271034a2d594f0e4c6f2", + "url": "https://api.github.com/repos/laminas/laminas-mime/zipball/52ae5fa9f12845cae749271034a2d594f0e4c6f2", "reference": "52ae5fa9f12845cae749271034a2d594f0e4c6f2", "shasum": "" }, "require": { "php": "^5.6 || ^7.0", - "zendframework/zend-stdlib": "^2.7 || ^3.0" + "laminas/laminas-stdlib": "^2.7 || ^3.0" }, "require-dev": { "phpunit/phpunit": "^5.7.21 || ^6.3", - "zendframework/zend-coding-standard": "~1.0.0", - "zendframework/zend-mail": "^2.6" + "laminas/laminas-coding-standard": "~1.0.0", + "laminas/laminas-mail": "^2.6" }, "suggest": { - "zendframework/zend-mail": "Zend\\Mail component" + "laminas/laminas-mail": "Laminas\\Mail component" }, "type": "library", "extra": { @@ -3772,7 +3772,7 @@ }, "autoload": { "psr-4": { - "Zend\\Mime\\": "src/" + "Laminas\\Mime\\": "src/" } }, "notification-url": "https://packagist.org/downloads/", @@ -3780,7 +3780,7 @@ "BSD-3-Clause" ], "description": "Create and parse MIME messages and parts", - "homepage": "https://github.com/zendframework/zend-mime", + "homepage": "https://github.com/laminas/laminas-mime", "keywords": [ "ZendFramework", "mime", @@ -3789,39 +3789,39 @@ "time": "2018-05-14T19:02:50+00:00" }, { - "name": "zendframework/zend-modulemanager", + "name": "laminas/laminas-modulemanager", "version": "2.8.2", "source": { "type": "git", - "url": "https://github.com/zendframework/zend-modulemanager.git", + "url": "https://github.com/laminas/laminas-modulemanager.git", "reference": "394df6e12248ac430a312d4693f793ee7120baa6" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/zendframework/zend-modulemanager/zipball/394df6e12248ac430a312d4693f793ee7120baa6", + "url": "https://api.github.com/repos/laminas/laminas-modulemanager/zipball/394df6e12248ac430a312d4693f793ee7120baa6", "reference": "394df6e12248ac430a312d4693f793ee7120baa6", "shasum": "" }, "require": { "php": "^5.6 || ^7.0", - "zendframework/zend-config": "^3.1 || ^2.6", - "zendframework/zend-eventmanager": "^3.2 || ^2.6.3", - "zendframework/zend-stdlib": "^3.1 || ^2.7" + "laminas/laminas-config": "^3.1 || ^2.6", + "laminas/laminas-eventmanager": "^3.2 || ^2.6.3", + "laminas/laminas-stdlib": "^3.1 || ^2.7" }, "require-dev": { "phpunit/phpunit": "^6.0.8 || ^5.7.15", - "zendframework/zend-coding-standard": "~1.0.0", - "zendframework/zend-console": "^2.6", - "zendframework/zend-di": "^2.6", - "zendframework/zend-loader": "^2.5", - "zendframework/zend-mvc": "^3.0 || ^2.7", - "zendframework/zend-servicemanager": "^3.0.3 || ^2.7.5" + "laminas/laminas-coding-standard": "~1.0.0", + "laminas/laminas-console": "^2.6", + "laminas/laminas-di": "^2.6", + "laminas/laminas-loader": "^2.5", + "laminas/laminas-mvc": "^3.0 || ^2.7", + "laminas/laminas-servicemanager": "^3.0.3 || ^2.7.5" }, "suggest": { - "zendframework/zend-console": "Zend\\Console component", - "zendframework/zend-loader": "Zend\\Loader component if you are not using Composer autoloading for your modules", - "zendframework/zend-mvc": "Zend\\Mvc component", - "zendframework/zend-servicemanager": "Zend\\ServiceManager component" + "laminas/laminas-console": "Laminas\\Console component", + "laminas/laminas-loader": "Laminas\\Loader component if you are not using Composer autoloading for your modules", + "laminas/laminas-mvc": "Laminas\\Mvc component", + "laminas/laminas-servicemanager": "Laminas\\ServiceManager component" }, "type": "library", "extra": { @@ -3832,7 +3832,7 @@ }, "autoload": { "psr-4": { - "Zend\\ModuleManager\\": "src/" + "Laminas\\ModuleManager\\": "src/" } }, "notification-url": "https://packagist.org/downloads/", @@ -3840,7 +3840,7 @@ "BSD-3-Clause" ], "description": "Modular application system for zend-mvc applications", - "homepage": "https://github.com/zendframework/zend-modulemanager", + "homepage": "https://github.com/laminas/laminas-modulemanager", "keywords": [ "ZendFramework", "modulemanager", @@ -3849,73 +3849,73 @@ "time": "2017-12-02T06:11:18+00:00" }, { - "name": "zendframework/zend-mvc", + "name": "laminas/laminas-mvc", "version": "2.7.15", "source": { "type": "git", - "url": "https://github.com/zendframework/zend-mvc.git", + "url": "https://github.com/laminas/laminas-mvc.git", "reference": "a8d45689d37a9e4ff4b75ea0b7478fa3d4f9c089" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/zendframework/zend-mvc/zipball/a8d45689d37a9e4ff4b75ea0b7478fa3d4f9c089", + "url": "https://api.github.com/repos/laminas/laminas-mvc/zipball/a8d45689d37a9e4ff4b75ea0b7478fa3d4f9c089", "reference": "a8d45689d37a9e4ff4b75ea0b7478fa3d4f9c089", "shasum": "" }, "require": { "container-interop/container-interop": "^1.1", "php": "^5.5 || ^7.0", - "zendframework/zend-console": "^2.7", - "zendframework/zend-eventmanager": "^2.6.4 || ^3.0", - "zendframework/zend-form": "^2.11", - "zendframework/zend-hydrator": "^1.1 || ^2.4", - "zendframework/zend-psr7bridge": "^0.2", - "zendframework/zend-servicemanager": "^2.7.10 || ^3.0.3", - "zendframework/zend-stdlib": "^2.7.5 || ^3.0" + "laminas/laminas-console": "^2.7", + "laminas/laminas-eventmanager": "^2.6.4 || ^3.0", + "laminas/laminas-form": "^2.11", + "laminas/laminas-hydrator": "^1.1 || ^2.4", + "laminas/laminas-psr7bridge": "^0.2", + "laminas/laminas-servicemanager": "^2.7.10 || ^3.0.3", + "laminas/laminas-stdlib": "^2.7.5 || ^3.0" }, "replace": { - "zendframework/zend-router": "^2.0" + "laminas/laminas-router": "^2.0" }, "require-dev": { "friendsofphp/php-cs-fixer": "1.7.*", "phpunit/phpunit": "^4.8.36", "sebastian/comparator": "^1.2.4", "sebastian/version": "^1.0.4", - "zendframework/zend-authentication": "^2.6", - "zendframework/zend-cache": "^2.8", - "zendframework/zend-di": "^2.6", - "zendframework/zend-filter": "^2.8", - "zendframework/zend-http": "^2.8", - "zendframework/zend-i18n": "^2.8", - "zendframework/zend-inputfilter": "^2.8", - "zendframework/zend-json": "^2.6.1", - "zendframework/zend-log": "^2.9.3", - "zendframework/zend-modulemanager": "^2.8", - "zendframework/zend-serializer": "^2.8", - "zendframework/zend-session": "^2.8.1", - "zendframework/zend-text": "^2.7", - "zendframework/zend-uri": "^2.6", - "zendframework/zend-validator": "^2.10", - "zendframework/zend-view": "^2.9" + "laminas/laminas-authentication": "^2.6", + "laminas/laminas-cache": "^2.8", + "laminas/laminas-di": "^2.6", + "laminas/laminas-filter": "^2.8", + "laminas/laminas-http": "^2.8", + "laminas/laminas-i18n": "^2.8", + "laminas/laminas-inputfilter": "^2.8", + "laminas/laminas-json": "^2.6.1", + "laminas/laminas-log": "^2.9.3", + "laminas/laminas-modulemanager": "^2.8", + "laminas/laminas-serializer": "^2.8", + "laminas/laminas-session": "^2.8.1", + "laminas/laminas-text": "^2.7", + "laminas/laminas-uri": "^2.6", + "laminas/laminas-validator": "^2.10", + "laminas/laminas-view": "^2.9" }, "suggest": { - "zendframework/zend-authentication": "Zend\\Authentication component for Identity plugin", - "zendframework/zend-config": "Zend\\Config component", - "zendframework/zend-di": "Zend\\Di component", - "zendframework/zend-filter": "Zend\\Filter component", - "zendframework/zend-http": "Zend\\Http component", - "zendframework/zend-i18n": "Zend\\I18n component for translatable segments", - "zendframework/zend-inputfilter": "Zend\\Inputfilter component", - "zendframework/zend-json": "Zend\\Json component", - "zendframework/zend-log": "Zend\\Log component", - "zendframework/zend-modulemanager": "Zend\\ModuleManager component", - "zendframework/zend-serializer": "Zend\\Serializer component", - "zendframework/zend-servicemanager-di": "^1.0.1, if using zend-servicemanager v3 and requiring the zend-di integration", - "zendframework/zend-session": "Zend\\Session component for FlashMessenger, PRG, and FPRG plugins", - "zendframework/zend-text": "Zend\\Text component", - "zendframework/zend-uri": "Zend\\Uri component", - "zendframework/zend-validator": "Zend\\Validator component", - "zendframework/zend-view": "Zend\\View component" + "laminas/laminas-authentication": "Laminas\\Authentication component for Identity plugin", + "laminas/laminas-config": "Laminas\\Config component", + "laminas/laminas-di": "Laminas\\Di component", + "laminas/laminas-filter": "Laminas\\Filter component", + "laminas/laminas-http": "Laminas\\Http component", + "laminas/laminas-i18n": "Laminas\\I18n component for translatable segments", + "laminas/laminas-inputfilter": "Zend\\Inputfilter component", + "laminas/laminas-json": "Laminas\\Json component", + "laminas/laminas-log": "Laminas\\Log component", + "laminas/laminas-modulemanager": "Laminas\\ModuleManager component", + "laminas/laminas-serializer": "Laminas\\Serializer component", + "laminas/laminas-servicemanager-di": "^1.0.1, if using zend-servicemanager v3 and requiring the zend-di integration", + "laminas/laminas-session": "Laminas\\Session component for FlashMessenger, PRG, and FPRG plugins", + "laminas/laminas-text": "Laminas\\Text component", + "laminas/laminas-uri": "Laminas\\Uri component", + "laminas/laminas-validator": "Laminas\\Validator component", + "laminas/laminas-view": "Laminas\\View component" }, "type": "library", "extra": { @@ -3929,14 +3929,14 @@ "src/autoload.php" ], "psr-4": { - "Zend\\Mvc\\": "src/" + "Laminas\\Mvc\\": "src/" } }, "notification-url": "https://packagist.org/downloads/", "license": [ "BSD-3-Clause" ], - "homepage": "https://github.com/zendframework/zend-mvc", + "homepage": "https://github.com/laminas/laminas-mvc", "keywords": [ "mvc", "zf2" @@ -3944,24 +3944,24 @@ "time": "2018-05-03T13:13:41+00:00" }, { - "name": "zendframework/zend-psr7bridge", + "name": "laminas/laminas-psr7bridge", "version": "0.2.2", "source": { "type": "git", - "url": "https://github.com/zendframework/zend-psr7bridge.git", + "url": "https://github.com/laminas/laminas-psr7bridge.git", "reference": "86c0b53b0c6381391c4add4a93a56e51d5c74605" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/zendframework/zend-psr7bridge/zipball/86c0b53b0c6381391c4add4a93a56e51d5c74605", + "url": "https://api.github.com/repos/laminas/laminas-psr7bridge/zipball/86c0b53b0c6381391c4add4a93a56e51d5c74605", "reference": "86c0b53b0c6381391c4add4a93a56e51d5c74605", "shasum": "" }, "require": { "php": ">=5.5", "psr/http-message": "^1.0", - "zendframework/zend-diactoros": "^1.1", - "zendframework/zend-http": "^2.5" + "laminas/laminas-diactoros": "^1.1", + "laminas/laminas-http": "^2.5" }, "require-dev": { "phpunit/phpunit": "^4.7", @@ -3976,15 +3976,15 @@ }, "autoload": { "psr-4": { - "Zend\\Psr7Bridge\\": "src/" + "Laminas\\Psr7Bridge\\": "src/" } }, "notification-url": "https://packagist.org/downloads/", "license": [ "BSD-3-Clause" ], - "description": "PSR-7 <-> Zend\\Http bridge", - "homepage": "https://github.com/zendframework/zend-psr7bridge", + "description": "PSR-7 <-> Laminas\\Http bridge", + "homepage": "https://github.com/laminas/laminas-psr7bridge", "keywords": [ "http", "psr", @@ -3993,33 +3993,33 @@ "time": "2016-05-10T21:44:39+00:00" }, { - "name": "zendframework/zend-serializer", + "name": "laminas/laminas-serializer", "version": "2.9.0", "source": { "type": "git", - "url": "https://github.com/zendframework/zend-serializer.git", + "url": "https://github.com/laminas/laminas-serializer.git", "reference": "0172690db48d8935edaf625c4cba38b79719892c" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/zendframework/zend-serializer/zipball/0172690db48d8935edaf625c4cba38b79719892c", + "url": "https://api.github.com/repos/laminas/laminas-serializer/zipball/0172690db48d8935edaf625c4cba38b79719892c", "reference": "0172690db48d8935edaf625c4cba38b79719892c", "shasum": "" }, "require": { "php": "^5.6 || ^7.0", - "zendframework/zend-json": "^2.5 || ^3.0", - "zendframework/zend-stdlib": "^2.7 || ^3.0" + "laminas/laminas-json": "^2.5 || ^3.0", + "laminas/laminas-stdlib": "^2.7 || ^3.0" }, "require-dev": { "phpunit/phpunit": "^5.7.25 || ^6.4.4", - "zendframework/zend-coding-standard": "~1.0.0", - "zendframework/zend-math": "^2.6 || ^3.0", - "zendframework/zend-servicemanager": "^2.7.5 || ^3.0.3" + "laminas/laminas-coding-standard": "~1.0.0", + "laminas/laminas-math": "^2.6 || ^3.0", + "laminas/laminas-servicemanager": "^2.7.5 || ^3.0.3" }, "suggest": { - "zendframework/zend-math": "(^2.6 || ^3.0) To support Python Pickle serialization", - "zendframework/zend-servicemanager": "(^2.7.5 || ^3.0.3) To support plugin manager support" + "laminas/laminas-math": "(^2.6 || ^3.0) To support Python Pickle serialization", + "laminas/laminas-servicemanager": "(^2.7.5 || ^3.0.3) To support plugin manager support" }, "type": "library", "extra": { @@ -4028,13 +4028,13 @@ "dev-develop": "2.10.x-dev" }, "zf": { - "component": "Zend\\Serializer", - "config-provider": "Zend\\Serializer\\ConfigProvider" + "component": "Laminas\\Serializer", + "config-provider": "Laminas\\Serializer\\ConfigProvider" } }, "autoload": { "psr-4": { - "Zend\\Serializer\\": "src/" + "Laminas\\Serializer\\": "src/" } }, "notification-url": "https://packagist.org/downloads/", @@ -4050,27 +4050,27 @@ "time": "2018-05-14T18:45:18+00:00" }, { - "name": "zendframework/zend-server", + "name": "laminas/laminas-server", "version": "2.8.0", "source": { "type": "git", - "url": "https://github.com/zendframework/zend-server.git", + "url": "https://github.com/laminas/laminas-server.git", "reference": "23a2e9a5599c83c05da831cb7c649e8a7809595e" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/zendframework/zend-server/zipball/23a2e9a5599c83c05da831cb7c649e8a7809595e", + "url": "https://api.github.com/repos/laminas/laminas-server/zipball/23a2e9a5599c83c05da831cb7c649e8a7809595e", "reference": "23a2e9a5599c83c05da831cb7c649e8a7809595e", "shasum": "" }, "require": { "php": "^5.6 || ^7.0", - "zendframework/zend-code": "^2.5 || ^3.0", - "zendframework/zend-stdlib": "^2.5 || ^3.0" + "laminas/laminas-code": "^2.5 || ^3.0", + "laminas/laminas-stdlib": "^2.5 || ^3.0" }, "require-dev": { "phpunit/phpunit": "^5.7.27 || ^6.5.8 || ^7.1.4", - "zendframework/zend-coding-standard": "~1.0.0" + "laminas/laminas-coding-standard": "~1.0.0" }, "type": "library", "extra": { @@ -4081,7 +4081,7 @@ }, "autoload": { "psr-4": { - "Zend\\Server\\": "src/" + "Laminas\\Server\\": "src/" } }, "notification-url": "https://packagist.org/downloads/", @@ -4097,16 +4097,16 @@ "time": "2018-04-30T22:21:28+00:00" }, { - "name": "zendframework/zend-servicemanager", + "name": "laminas/laminas-servicemanager", "version": "2.7.11", "source": { "type": "git", - "url": "https://github.com/zendframework/zend-servicemanager.git", + "url": "https://github.com/laminas/laminas-servicemanager.git", "reference": "99ec9ed5d0f15aed9876433c74c2709eb933d4c7" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/zendframework/zend-servicemanager/zipball/99ec9ed5d0f15aed9876433c74c2709eb933d4c7", + "url": "https://api.github.com/repos/laminas/laminas-servicemanager/zipball/99ec9ed5d0f15aed9876433c74c2709eb933d4c7", "reference": "99ec9ed5d0f15aed9876433c74c2709eb933d4c7", "shasum": "" }, @@ -4118,12 +4118,12 @@ "athletic/athletic": "dev-master", "fabpot/php-cs-fixer": "1.7.*", "phpunit/phpunit": "~4.0", - "zendframework/zend-di": "~2.5", - "zendframework/zend-mvc": "~2.5" + "laminas/laminas-di": "~2.5", + "laminas/laminas-mvc": "~2.5" }, "suggest": { "ocramius/proxy-manager": "ProxyManager 0.5.* to handle lazy initialization of services", - "zendframework/zend-di": "Zend\\Di component" + "laminas/laminas-di": "Laminas\\Di component" }, "type": "library", "extra": { @@ -4134,14 +4134,14 @@ }, "autoload": { "psr-4": { - "Zend\\ServiceManager\\": "src/" + "Laminas\\ServiceManager\\": "src/" } }, "notification-url": "https://packagist.org/downloads/", "license": [ "BSD-3-Clause" ], - "homepage": "https://github.com/zendframework/zend-servicemanager", + "homepage": "https://github.com/laminas/laminas-servicemanager", "keywords": [ "servicemanager", "zf2" @@ -4149,43 +4149,43 @@ "time": "2018-06-22T14:49:54+00:00" }, { - "name": "zendframework/zend-session", + "name": "laminas/laminas-session", "version": "2.8.5", "source": { "type": "git", - "url": "https://github.com/zendframework/zend-session.git", + "url": "https://github.com/laminas/laminas-session.git", "reference": "2cfd90e1a2f6b066b9f908599251d8f64f07021b" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/zendframework/zend-session/zipball/2cfd90e1a2f6b066b9f908599251d8f64f07021b", + "url": "https://api.github.com/repos/laminas/laminas-session/zipball/2cfd90e1a2f6b066b9f908599251d8f64f07021b", "reference": "2cfd90e1a2f6b066b9f908599251d8f64f07021b", "shasum": "" }, "require": { "php": "^5.6 || ^7.0", - "zendframework/zend-eventmanager": "^2.6.2 || ^3.0", - "zendframework/zend-stdlib": "^2.7 || ^3.0" + "laminas/laminas-eventmanager": "^2.6.2 || ^3.0", + "laminas/laminas-stdlib": "^2.7 || ^3.0" }, "require-dev": { "container-interop/container-interop": "^1.1", "mongodb/mongodb": "^1.0.1", "php-mock/php-mock-phpunit": "^1.1.2 || ^2.0", "phpunit/phpunit": "^5.7.5 || >=6.0.13 <6.5.0", - "zendframework/zend-cache": "^2.6.1", - "zendframework/zend-coding-standard": "~1.0.0", - "zendframework/zend-db": "^2.7", - "zendframework/zend-http": "^2.5.4", - "zendframework/zend-servicemanager": "^2.7.5 || ^3.0.3", - "zendframework/zend-validator": "^2.6" + "laminas/laminas-cache": "^2.6.1", + "laminas/laminas-coding-standard": "~1.0.0", + "laminas/laminas-db": "^2.7", + "laminas/laminas-http": "^2.5.4", + "laminas/laminas-servicemanager": "^2.7.5 || ^3.0.3", + "laminas/laminas-validator": "^2.6" }, "suggest": { "mongodb/mongodb": "If you want to use the MongoDB session save handler", - "zendframework/zend-cache": "Zend\\Cache component", - "zendframework/zend-db": "Zend\\Db component", - "zendframework/zend-http": "Zend\\Http component", - "zendframework/zend-servicemanager": "Zend\\ServiceManager component", - "zendframework/zend-validator": "Zend\\Validator component" + "laminas/laminas-cache": "Laminas\\Cache component", + "laminas/laminas-db": "Laminas\\Db component", + "laminas/laminas-http": "Laminas\\Http component", + "laminas/laminas-servicemanager": "Laminas\\ServiceManager component", + "laminas/laminas-validator": "Laminas\\Validator component" }, "type": "library", "extra": { @@ -4194,13 +4194,13 @@ "dev-develop": "2.9-dev" }, "zf": { - "component": "Zend\\Session", - "config-provider": "Zend\\Session\\ConfigProvider" + "component": "Laminas\\Session", + "config-provider": "Laminas\\Session\\ConfigProvider" } }, "autoload": { "psr-4": { - "Zend\\Session\\": "src/" + "Laminas\\Session\\": "src/" } }, "notification-url": "https://packagist.org/downloads/", @@ -4216,34 +4216,34 @@ "time": "2018-02-22T16:33:54+00:00" }, { - "name": "zendframework/zend-soap", + "name": "laminas/laminas-soap", "version": "2.7.0", "source": { "type": "git", - "url": "https://github.com/zendframework/zend-soap.git", + "url": "https://github.com/laminas/laminas-soap.git", "reference": "af03c32f0db2b899b3df8cfe29aeb2b49857d284" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/zendframework/zend-soap/zipball/af03c32f0db2b899b3df8cfe29aeb2b49857d284", + "url": "https://api.github.com/repos/laminas/laminas-soap/zipball/af03c32f0db2b899b3df8cfe29aeb2b49857d284", "reference": "af03c32f0db2b899b3df8cfe29aeb2b49857d284", "shasum": "" }, "require": { "ext-soap": "*", "php": "^5.6 || ^7.0", - "zendframework/zend-server": "^2.6.1", - "zendframework/zend-stdlib": "^2.7 || ^3.0", - "zendframework/zend-uri": "^2.5.2" + "laminas/laminas-server": "^2.6.1", + "laminas/laminas-stdlib": "^2.7 || ^3.0", + "laminas/laminas-uri": "^2.5.2" }, "require-dev": { "phpunit/phpunit": "^5.7.21 || ^6.3", - "zendframework/zend-coding-standard": "~1.0.0", - "zendframework/zend-config": "^2.6", - "zendframework/zend-http": "^2.5.4" + "laminas/laminas-coding-standard": "~1.0.0", + "laminas/laminas-config": "^2.6", + "laminas/laminas-http": "^2.5.4" }, "suggest": { - "zendframework/zend-http": "Zend\\Http component" + "laminas/laminas-http": "Laminas\\Http component" }, "type": "library", "extra": { @@ -4254,14 +4254,14 @@ }, "autoload": { "psr-4": { - "Zend\\Soap\\": "src/" + "Laminas\\Soap\\": "src/" } }, "notification-url": "https://packagist.org/downloads/", "license": [ "BSD-3-Clause" ], - "homepage": "https://github.com/zendframework/zend-soap", + "homepage": "https://github.com/laminas/laminas-soap", "keywords": [ "soap", "zf2" @@ -4269,39 +4269,39 @@ "time": "2018-01-29T17:51:26+00:00" }, { - "name": "zendframework/zend-stdlib", + "name": "laminas/laminas-stdlib", "version": "2.7.7", "source": { "type": "git", - "url": "https://github.com/zendframework/zend-stdlib.git", + "url": "https://github.com/laminas/laminas-stdlib.git", "reference": "0e44eb46788f65e09e077eb7f44d2659143bcc1f" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/zendframework/zend-stdlib/zipball/0e44eb46788f65e09e077eb7f44d2659143bcc1f", + "url": "https://api.github.com/repos/laminas/laminas-stdlib/zipball/0e44eb46788f65e09e077eb7f44d2659143bcc1f", "reference": "0e44eb46788f65e09e077eb7f44d2659143bcc1f", "shasum": "" }, "require": { "php": "^5.5 || ^7.0", - "zendframework/zend-hydrator": "~1.1" + "laminas/laminas-hydrator": "~1.1" }, "require-dev": { "athletic/athletic": "~0.1", "fabpot/php-cs-fixer": "1.7.*", "phpunit/phpunit": "~4.0", - "zendframework/zend-config": "~2.5", - "zendframework/zend-eventmanager": "~2.5", - "zendframework/zend-filter": "~2.5", - "zendframework/zend-inputfilter": "~2.5", - "zendframework/zend-serializer": "~2.5", - "zendframework/zend-servicemanager": "~2.5" + "laminas/laminas-config": "~2.5", + "laminas/laminas-eventmanager": "~2.5", + "laminas/laminas-filter": "~2.5", + "laminas/laminas-inputfilter": "~2.5", + "laminas/laminas-serializer": "~2.5", + "laminas/laminas-servicemanager": "~2.5" }, "suggest": { - "zendframework/zend-eventmanager": "To support aggregate hydrator usage", - "zendframework/zend-filter": "To support naming strategy hydrator usage", - "zendframework/zend-serializer": "Zend\\Serializer component", - "zendframework/zend-servicemanager": "To support hydrator plugin manager usage" + "laminas/laminas-eventmanager": "To support aggregate hydrator usage", + "laminas/laminas-filter": "To support naming strategy hydrator usage", + "laminas/laminas-serializer": "Laminas\\Serializer component", + "laminas/laminas-servicemanager": "To support hydrator plugin manager usage" }, "type": "library", "extra": { @@ -4313,14 +4313,14 @@ }, "autoload": { "psr-4": { - "Zend\\Stdlib\\": "src/" + "Laminas\\Stdlib\\": "src/" } }, "notification-url": "https://packagist.org/downloads/", "license": [ "BSD-3-Clause" ], - "homepage": "https://github.com/zendframework/zend-stdlib", + "homepage": "https://github.com/laminas/laminas-stdlib", "keywords": [ "stdlib", "zf2" @@ -4328,28 +4328,28 @@ "time": "2016-04-12T21:17:31+00:00" }, { - "name": "zendframework/zend-text", + "name": "laminas/laminas-text", "version": "2.7.0", "source": { "type": "git", - "url": "https://github.com/zendframework/zend-text.git", + "url": "https://github.com/laminas/laminas-text.git", "reference": "ca987dd4594f5f9508771fccd82c89bc7fbb39ac" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/zendframework/zend-text/zipball/ca987dd4594f5f9508771fccd82c89bc7fbb39ac", + "url": "https://api.github.com/repos/laminas/laminas-text/zipball/ca987dd4594f5f9508771fccd82c89bc7fbb39ac", "reference": "ca987dd4594f5f9508771fccd82c89bc7fbb39ac", "shasum": "" }, "require": { "php": "^5.6 || ^7.0", - "zendframework/zend-servicemanager": "^2.7.5 || ^3.0.3", - "zendframework/zend-stdlib": "^2.7 || ^3.0" + "laminas/laminas-servicemanager": "^2.7.5 || ^3.0.3", + "laminas/laminas-stdlib": "^2.7 || ^3.0" }, "require-dev": { "phpunit/phpunit": "^5.7.27 || ^6.5.8 || ^7.1.4", - "zendframework/zend-coding-standard": "~1.0.0", - "zendframework/zend-config": "^2.6" + "laminas/laminas-coding-standard": "~1.0.0", + "laminas/laminas-config": "^2.6" }, "type": "library", "extra": { @@ -4360,7 +4360,7 @@ }, "autoload": { "psr-4": { - "Zend\\Text\\": "src/" + "Laminas\\Text\\": "src/" } }, "notification-url": "https://packagist.org/downloads/", @@ -4376,27 +4376,27 @@ "time": "2018-04-30T14:55:10+00:00" }, { - "name": "zendframework/zend-uri", + "name": "laminas/laminas-uri", "version": "2.7.0", "source": { "type": "git", - "url": "https://github.com/zendframework/zend-uri.git", + "url": "https://github.com/laminas/laminas-uri.git", "reference": "b2785cd38fe379a784645449db86f21b7739b1ee" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/zendframework/zend-uri/zipball/b2785cd38fe379a784645449db86f21b7739b1ee", + "url": "https://api.github.com/repos/laminas/laminas-uri/zipball/b2785cd38fe379a784645449db86f21b7739b1ee", "reference": "b2785cd38fe379a784645449db86f21b7739b1ee", "shasum": "" }, "require": { "php": "^5.6 || ^7.0", - "zendframework/zend-escaper": "^2.5", - "zendframework/zend-validator": "^2.10" + "laminas/laminas-escaper": "^2.5", + "laminas/laminas-validator": "^2.10" }, "require-dev": { "phpunit/phpunit": "^5.7.27 || ^6.5.8 || ^7.1.4", - "zendframework/zend-coding-standard": "~1.0.0" + "laminas/laminas-coding-standard": "~1.0.0" }, "type": "library", "extra": { @@ -4407,7 +4407,7 @@ }, "autoload": { "psr-4": { - "Zend\\Uri\\": "src/" + "Laminas\\Uri\\": "src/" } }, "notification-url": "https://packagist.org/downloads/", @@ -4423,49 +4423,49 @@ "time": "2019-02-27T21:39:04+00:00" }, { - "name": "zendframework/zend-validator", + "name": "laminas/laminas-validator", "version": "2.11.1", "source": { "type": "git", - "url": "https://github.com/zendframework/zend-validator.git", + "url": "https://github.com/laminas/laminas-validator.git", "reference": "3c28dfe4e5951ba38059cea895244d9d206190b3" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/zendframework/zend-validator/zipball/3c28dfe4e5951ba38059cea895244d9d206190b3", + "url": "https://api.github.com/repos/laminas/laminas-validator/zipball/3c28dfe4e5951ba38059cea895244d9d206190b3", "reference": "3c28dfe4e5951ba38059cea895244d9d206190b3", "shasum": "" }, "require": { "container-interop/container-interop": "^1.1", "php": "^5.6 || ^7.0", - "zendframework/zend-stdlib": "^2.7.6 || ^3.1" + "laminas/laminas-stdlib": "^2.7.6 || ^3.1" }, "require-dev": { "phpunit/phpunit": "^6.0.8 || ^5.7.15", "psr/http-message": "^1.0", - "zendframework/zend-cache": "^2.6.1", - "zendframework/zend-coding-standard": "~1.0.0", - "zendframework/zend-config": "^2.6", - "zendframework/zend-db": "^2.7", - "zendframework/zend-filter": "^2.6", - "zendframework/zend-http": "^2.5.4", - "zendframework/zend-i18n": "^2.6", - "zendframework/zend-math": "^2.6", - "zendframework/zend-servicemanager": "^2.7.5 || ^3.0.3", - "zendframework/zend-session": "^2.8", - "zendframework/zend-uri": "^2.5" + "laminas/laminas-cache": "^2.6.1", + "laminas/laminas-coding-standard": "~1.0.0", + "laminas/laminas-config": "^2.6", + "laminas/laminas-db": "^2.7", + "laminas/laminas-filter": "^2.6", + "laminas/laminas-http": "^2.5.4", + "laminas/laminas-i18n": "^2.6", + "laminas/laminas-math": "^2.6", + "laminas/laminas-servicemanager": "^2.7.5 || ^3.0.3", + "laminas/laminas-session": "^2.8", + "laminas/laminas-uri": "^2.5" }, "suggest": { "psr/http-message": "psr/http-message, required when validating PSR-7 UploadedFileInterface instances via the Upload and UploadFile validators", - "zendframework/zend-db": "Zend\\Db component, required by the (No)RecordExists validator", - "zendframework/zend-filter": "Zend\\Filter component, required by the Digits validator", - "zendframework/zend-i18n": "Zend\\I18n component to allow translation of validation error messages", - "zendframework/zend-i18n-resources": "Translations of validator messages", - "zendframework/zend-math": "Zend\\Math component, required by the Csrf validator", - "zendframework/zend-servicemanager": "Zend\\ServiceManager component to allow using the ValidatorPluginManager and validator chains", - "zendframework/zend-session": "Zend\\Session component, ^2.8; required by the Csrf validator", - "zendframework/zend-uri": "Zend\\Uri component, required by the Uri and Sitemap\\Loc validators" + "laminas/laminas-db": "Laminas\\Db component, required by the (No)RecordExists validator", + "laminas/laminas-filter": "Laminas\\Filter component, required by the Digits validator", + "laminas/laminas-i18n": "Laminas\\I18n component to allow translation of validation error messages", + "laminas/laminas-i18n-resources": "Translations of validator messages", + "laminas/laminas-math": "Laminas\\Math component, required by the Csrf validator", + "laminas/laminas-servicemanager": "Laminas\\ServiceManager component to allow using the ValidatorPluginManager and validator chains", + "laminas/laminas-session": "Laminas\\Session component, ^2.8; required by the Csrf validator", + "laminas/laminas-uri": "Laminas\\Uri component, required by the Uri and Sitemap\\Loc validators" }, "type": "library", "extra": { @@ -4474,13 +4474,13 @@ "dev-develop": "2.12.x-dev" }, "zf": { - "component": "Zend\\Validator", - "config-provider": "Zend\\Validator\\ConfigProvider" + "component": "Laminas\\Validator", + "config-provider": "Laminas\\Validator\\ConfigProvider" } }, "autoload": { "psr-4": { - "Zend\\Validator\\": "src/" + "Laminas\\Validator\\": "src/" } }, "notification-url": "https://packagist.org/downloads/", @@ -4488,7 +4488,7 @@ "BSD-3-Clause" ], "description": "provides a set of commonly needed validators", - "homepage": "https://github.com/zendframework/zend-validator", + "homepage": "https://github.com/laminas/laminas-validator", "keywords": [ "validator", "zf2" @@ -4496,64 +4496,64 @@ "time": "2019-01-29T22:26:39+00:00" }, { - "name": "zendframework/zend-view", + "name": "laminas/laminas-view", "version": "2.10.1", "source": { "type": "git", - "url": "https://github.com/zendframework/zend-view.git", + "url": "https://github.com/laminas/laminas-view.git", "reference": "c1a3f2043fb75b5983ab9adfc369ae396601be7e" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/zendframework/zend-view/zipball/c1a3f2043fb75b5983ab9adfc369ae396601be7e", + "url": "https://api.github.com/repos/laminas/laminas-view/zipball/c1a3f2043fb75b5983ab9adfc369ae396601be7e", "reference": "c1a3f2043fb75b5983ab9adfc369ae396601be7e", "shasum": "" }, "require": { "php": "^5.6 || ^7.0", - "zendframework/zend-eventmanager": "^2.6.2 || ^3.0", - "zendframework/zend-json": "^2.6.1", - "zendframework/zend-loader": "^2.5", - "zendframework/zend-stdlib": "^2.7 || ^3.0" + "laminas/laminas-eventmanager": "^2.6.2 || ^3.0", + "laminas/laminas-json": "^2.6.1", + "laminas/laminas-loader": "^2.5", + "laminas/laminas-stdlib": "^2.7 || ^3.0" }, "require-dev": { "phpunit/phpunit": "^5.7.15 || ^6.0.8", - "zendframework/zend-authentication": "^2.5", - "zendframework/zend-cache": "^2.6.1", - "zendframework/zend-coding-standard": "~1.0.0", - "zendframework/zend-config": "^2.6", - "zendframework/zend-console": "^2.6", - "zendframework/zend-escaper": "^2.5", - "zendframework/zend-feed": "^2.7", - "zendframework/zend-filter": "^2.6.1", - "zendframework/zend-http": "^2.5.4", - "zendframework/zend-i18n": "^2.6", - "zendframework/zend-log": "^2.7", - "zendframework/zend-modulemanager": "^2.7.1", - "zendframework/zend-mvc": "^2.7.14 || ^3.0", - "zendframework/zend-navigation": "^2.5", - "zendframework/zend-paginator": "^2.5", - "zendframework/zend-permissions-acl": "^2.6", - "zendframework/zend-router": "^3.0.1", - "zendframework/zend-serializer": "^2.6.1", - "zendframework/zend-servicemanager": "^2.7.5 || ^3.0.3", - "zendframework/zend-session": "^2.8.1", - "zendframework/zend-uri": "^2.5" + "laminas/laminas-authentication": "^2.5", + "laminas/laminas-cache": "^2.6.1", + "laminas/laminas-coding-standard": "~1.0.0", + "laminas/laminas-config": "^2.6", + "laminas/laminas-console": "^2.6", + "laminas/laminas-escaper": "^2.5", + "laminas/laminas-feed": "^2.7", + "laminas/laminas-filter": "^2.6.1", + "laminas/laminas-http": "^2.5.4", + "laminas/laminas-i18n": "^2.6", + "laminas/laminas-log": "^2.7", + "laminas/laminas-modulemanager": "^2.7.1", + "laminas/laminas-mvc": "^2.7.14 || ^3.0", + "laminas/laminas-navigation": "^2.5", + "laminas/laminas-paginator": "^2.5", + "laminas/laminas-permissions-acl": "^2.6", + "laminas/laminas-router": "^3.0.1", + "laminas/laminas-serializer": "^2.6.1", + "laminas/laminas-servicemanager": "^2.7.5 || ^3.0.3", + "laminas/laminas-session": "^2.8.1", + "laminas/laminas-uri": "^2.5" }, "suggest": { - "zendframework/zend-authentication": "Zend\\Authentication component", - "zendframework/zend-escaper": "Zend\\Escaper component", - "zendframework/zend-feed": "Zend\\Feed component", - "zendframework/zend-filter": "Zend\\Filter component", - "zendframework/zend-http": "Zend\\Http component", - "zendframework/zend-i18n": "Zend\\I18n component", - "zendframework/zend-mvc": "Zend\\Mvc component", - "zendframework/zend-mvc-plugin-flashmessenger": "zend-mvc-plugin-flashmessenger component, if you want to use the FlashMessenger view helper with zend-mvc versions 3 and up", - "zendframework/zend-navigation": "Zend\\Navigation component", - "zendframework/zend-paginator": "Zend\\Paginator component", - "zendframework/zend-permissions-acl": "Zend\\Permissions\\Acl component", - "zendframework/zend-servicemanager": "Zend\\ServiceManager component", - "zendframework/zend-uri": "Zend\\Uri component" + "laminas/laminas-authentication": "Laminas\\Authentication component", + "laminas/laminas-escaper": "Laminas\\Escaper component", + "laminas/laminas-feed": "Laminas\\Feed component", + "laminas/laminas-filter": "Laminas\\Filter component", + "laminas/laminas-http": "Laminas\\Http component", + "laminas/laminas-i18n": "Laminas\\I18n component", + "laminas/laminas-mvc": "Laminas\\Mvc component", + "laminas/laminas-mvc-plugin-flashmessenger": "zend-mvc-plugin-flashmessenger component, if you want to use the FlashMessenger view helper with zend-mvc versions 3 and up", + "laminas/laminas-navigation": "Laminas\\Navigation component", + "laminas/laminas-paginator": "Laminas\\Paginator component", + "laminas/laminas-permissions-acl": "Laminas\\Permissions\\Acl component", + "laminas/laminas-servicemanager": "Laminas\\ServiceManager component", + "laminas/laminas-uri": "Laminas\\Uri component" }, "bin": [ "bin/templatemap_generator.php" @@ -4567,7 +4567,7 @@ }, "autoload": { "psr-4": { - "Zend\\View\\": "src/" + "Laminas\\View\\": "src/" } }, "notification-url": "https://packagist.org/downloads/", @@ -4575,7 +4575,7 @@ "BSD-3-Clause" ], "description": "provides a system of helpers, output filters, and variable escaping", - "homepage": "https://github.com/zendframework/zend-view", + "homepage": "https://github.com/laminas/laminas-view", "keywords": [ "view", "zf2" diff --git a/dev/tests/integration/testsuite/Magento/Framework/Composer/_files/testFromCreateProject/composer.lock b/dev/tests/integration/testsuite/Magento/Framework/Composer/_files/testFromCreateProject/composer.lock index 4fb998ab77b34..fbcaf8d1600fb 100644 --- a/dev/tests/integration/testsuite/Magento/Framework/Composer/_files/testFromCreateProject/composer.lock +++ b/dev/tests/integration/testsuite/Magento/Framework/Composer/_files/testFromCreateProject/composer.lock @@ -1870,13 +1870,13 @@ "symfony/console": "~4.1.0", "symfony/process": "~4.1.0", "tedivm/jshrink": "~1.3.0", - "zendframework/zend-code": "~3.3.0", - "zendframework/zend-crypt": "^2.6.0", - "zendframework/zend-http": "^2.6.0", - "zendframework/zend-mvc": "~2.7.0", - "zendframework/zend-stdlib": "^2.7.7", - "zendframework/zend-uri": "^2.5.1", - "zendframework/zend-validator": "^2.6.0" + "laminas/laminas-code": "~3.3.0", + "laminas/laminas-crypt": "^2.6.0", + "laminas/laminas-http": "^2.6.0", + "laminas/laminas-mvc": "~2.7.0", + "laminas/laminas-stdlib": "^2.7.7", + "laminas/laminas-uri": "^2.5.1", + "laminas/laminas-validator": "^2.6.0" }, "suggest": { "ext-imagick": "Use Image Magick >=3.0.0 as an optional alternative image processing library" @@ -2335,28 +2335,28 @@ "symfony/event-dispatcher": "~4.1.0", "tedivm/jshrink": "~1.3.0", "tubalmartin/cssmin": "4.1.1", - "zendframework/zend-code": "~3.3.0", - "zendframework/zend-config": "^2.6.0", - "zendframework/zend-console": "^2.6.0", - "zendframework/zend-crypt": "^2.6.0", - "zendframework/zend-di": "^2.6.1", - "zendframework/zend-eventmanager": "^2.6.3", - "zendframework/zend-form": "^2.10.0", - "zendframework/zend-http": "^2.6.0", - "zendframework/zend-i18n": "^2.7.3", - "zendframework/zend-json": "^2.6.1", - "zendframework/zend-log": "^2.9.1", - "zendframework/zend-modulemanager": "^2.7", - "zendframework/zend-mvc": "~2.7.0", - "zendframework/zend-serializer": "^2.7.2", - "zendframework/zend-server": "^2.6.1", - "zendframework/zend-servicemanager": "^2.7.8", - "zendframework/zend-soap": "^2.7.0", - "zendframework/zend-stdlib": "^2.7.7", - "zendframework/zend-text": "^2.6.0", - "zendframework/zend-uri": "^2.5.1", - "zendframework/zend-validator": "^2.6.0", - "zendframework/zend-view": "~2.10.0" + "laminas/laminas-code": "~3.3.0", + "laminas/laminas-config": "^2.6.0", + "laminas/laminas-console": "^2.6.0", + "laminas/laminas-crypt": "^2.6.0", + "laminas/laminas-di": "^2.6.1", + "laminas/laminas-eventmanager": "^2.6.3", + "laminas/laminas-form": "^2.10.0", + "laminas/laminas-http": "^2.6.0", + "laminas/laminas-i18n": "^2.7.3", + "laminas/laminas-json": "^2.6.1", + "laminas/laminas-log": "^2.9.1", + "laminas/laminas-modulemanager": "^2.7", + "laminas/laminas-mvc": "~2.7.0", + "laminas/laminas-serializer": "^2.7.2", + "laminas/laminas-server": "^2.6.1", + "laminas/laminas-servicemanager": "^2.7.8", + "laminas/laminas-soap": "^2.7.0", + "laminas/laminas-stdlib": "^2.7.7", + "laminas/laminas-text": "^2.6.0", + "laminas/laminas-uri": "^2.5.1", + "laminas/laminas-validator": "^2.6.0", + "laminas/laminas-view": "~2.10.0" }, "conflict": { "gene/bluefoot": "*" @@ -2460,10 +2460,6 @@ "app/etc/registration_globlist.php", "app/etc/registration_globlist.php" ], - [ - "app/functions.php", - "app/functions.php" - ], [ "auth.json.sample", "auth.json.sample" @@ -3128,79 +3124,6 @@ ], "description": "Authorization module provides access to Magento ACL functionality." }, - { - "name": "magento/module-authorizenet", - "version": "100.3.1", - "dist": { - "type": "zip", - "url": "https://repo.magento.com/archives/magento/module-authorizenet/magento-module-authorizenet-100.3.1.0.zip", - "reference": null, - "shasum": "ab9dc9018f1a5b71cfaa54c1975d6ee6b5c7610c" - }, - "require": { - "magento/framework": "102.0.*", - "magento/module-backend": "101.0.*", - "magento/module-catalog": "103.0.*", - "magento/module-checkout": "100.3.*", - "magento/module-payment": "100.3.*", - "magento/module-quote": "101.1.*", - "magento/module-sales": "102.0.*", - "magento/module-store": "101.0.*", - "php": "~7.1.3||~7.2.0" - }, - "suggest": { - "magento/module-config": "101.1.*" - }, - "type": "magento2-module", - "autoload": { - "files": [ - "registration.php" - ], - "psr-4": { - "Magento\\Authorizenet\\": "" - } - }, - "license": [ - "OSL-3.0", - "AFL-3.0" - ], - "description": "N/A" - }, - { - "name": "magento/module-authorizenet-acceptjs", - "version": "100.3.0", - "dist": { - "type": "zip", - "url": "https://repo.magento.com/archives/magento/module-authorizenet-acceptjs/magento-module-authorizenet-acceptjs-100.3.0.0.zip", - "reference": null, - "shasum": "16bc1b50a4e7f63bfa3869fa2c3bd4c67cedd8a3" - }, - "require": { - "magento/framework": "102.0.*", - "magento/module-backend": "101.0.*", - "magento/module-checkout": "100.3.*", - "magento/module-config": "101.1.*", - "magento/module-payment": "100.3.*", - "magento/module-quote": "101.1.*", - "magento/module-sales": "102.0.*", - "magento/module-store": "101.0.*", - "php": "~7.1.3||~7.2.0" - }, - "type": "magento2-module", - "autoload": { - "files": [ - "registration.php" - ], - "psr-4": { - "Magento\\AuthorizenetAcceptjs\\": "" - } - }, - "license": [ - "OSL-3.0", - "AFL-3.0" - ], - "description": "N/A" - }, { "name": "magento/module-backend", "version": "101.0.1", @@ -3485,9 +3408,9 @@ "magento/module-customer": "102.0.*", "magento/module-store": "101.0.*", "php": "~7.1.3||~7.2.0", - "zendframework/zend-captcha": "^2.7.1", - "zendframework/zend-db": "^2.8.2", - "zendframework/zend-session": "^2.7.3" + "laminas/laminas-captcha": "^2.7.1", + "laminas/laminas-db": "^2.8.2", + "laminas/laminas-session": "^2.7.3" }, "type": "magento2-module", "autoload": { @@ -4995,7 +4918,7 @@ "shasum": "ed1da1137848560dde1a85f0f54dc2fac262359e" }, "require": { - "elasticsearch/elasticsearch": "~2.0|~5.1|~6.1", + "elasticsearch/elasticsearch": "~7.6", "magento/framework": "102.0.*", "magento/module-advanced-search": "100.3.*", "magento/module-catalog": "103.0.*", @@ -5035,7 +4958,7 @@ "shasum": "a9da3243900390ad163efc7969b07116d2eb793f" }, "require": { - "elasticsearch/elasticsearch": "~2.0|~5.1|~6.1", + "elasticsearch/elasticsearch": "~7.6", "magento/framework": "102.0.*", "magento/module-advanced-search": "100.3.*", "magento/module-catalog-search": "101.0.*", @@ -8441,45 +8364,6 @@ ], "description": "N/A" }, - { - "name": "magento/module-signifyd", - "version": "100.3.1", - "dist": { - "type": "zip", - "url": "https://repo.magento.com/archives/magento/module-signifyd/magento-module-signifyd-100.3.1.0.zip", - "reference": null, - "shasum": "768442b807e31bacfb5bbf914e155bac3ca88dde" - }, - "require": { - "magento/framework": "102.0.*", - "magento/module-backend": "101.0.*", - "magento/module-checkout": "100.3.*", - "magento/module-config": "101.1.*", - "magento/module-customer": "102.0.*", - "magento/module-directory": "100.3.*", - "magento/module-payment": "100.3.*", - "magento/module-sales": "102.0.*", - "magento/module-store": "101.0.*", - "php": "~7.1.3||~7.2.0" - }, - "suggest": { - "magento/module-config": "101.1.*" - }, - "type": "magento2-module", - "autoload": { - "files": [ - "registration.php" - ], - "psr-4": { - "Magento\\Signifyd\\": "" - } - }, - "license": [ - "OSL-3.0", - "AFL-3.0" - ], - "description": "Submitting Case Entry to Signifyd on Order Creation" - }, { "name": "magento/module-sitemap", "version": "100.3.1", @@ -9668,7 +9552,7 @@ "colinmollenhour/php-redis-session-abstract": "~1.4.0", "composer/composer": "^1.6", "dotmailer/dotmailer-magento2-extension": "3.1.1", - "elasticsearch/elasticsearch": "~2.0|~5.1|~6.1", + "elasticsearch/elasticsearch": "~7.6", "ext-bcmath": "*", "ext-ctype": "*", "ext-curl": "*", @@ -9709,8 +9593,6 @@ "magento/module-analytics": "100.3.1", "magento/module-asynchronous-operations": "100.3.1", "magento/module-authorization": "100.3.1", - "magento/module-authorizenet": "100.3.1", - "magento/module-authorizenet-acceptjs": "100.3.0", "magento/module-backend": "101.0.1", "magento/module-backup": "100.3.1", "magento/module-braintree": "100.3.1", @@ -9820,7 +9702,6 @@ "magento/module-send-friend": "100.3.1", "magento/module-send-friend-graph-ql": "100.3.0", "magento/module-shipping": "100.3.1", - "magento/module-signifyd": "100.3.1", "magento/module-sitemap": "100.3.1", "magento/module-store": "101.0.1", "magento/module-store-graph-ql": "100.3.1", @@ -9878,33 +9759,33 @@ "tubalmartin/cssmin": "4.1.1", "vertex/product-magento-module": "3.1.0", "webonyx/graphql-php": "^0.12.6", - "zendframework/zend-captcha": "^2.7.1", - "zendframework/zend-code": "~3.3.0", - "zendframework/zend-config": "^2.6.0", - "zendframework/zend-console": "^2.6.0", - "zendframework/zend-crypt": "^2.6.0", - "zendframework/zend-db": "^2.8.2", - "zendframework/zend-di": "^2.6.1", - "zendframework/zend-eventmanager": "^2.6.3", - "zendframework/zend-feed": "^2.9.0", - "zendframework/zend-form": "^2.10.0", - "zendframework/zend-http": "^2.6.0", - "zendframework/zend-i18n": "^2.7.3", - "zendframework/zend-json": "^2.6.1", - "zendframework/zend-log": "^2.9.1", - "zendframework/zend-mail": "^2.9.0", - "zendframework/zend-modulemanager": "^2.7", - "zendframework/zend-mvc": "~2.7.0", - "zendframework/zend-serializer": "^2.7.2", - "zendframework/zend-server": "^2.6.1", - "zendframework/zend-servicemanager": "^2.7.8", - "zendframework/zend-session": "^2.7.3", - "zendframework/zend-soap": "^2.7.0", - "zendframework/zend-stdlib": "^2.7.7", - "zendframework/zend-text": "^2.6.0", - "zendframework/zend-uri": "^2.5.1", - "zendframework/zend-validator": "^2.6.0", - "zendframework/zend-view": "~2.10.0" + "laminas/laminas-captcha": "^2.7.1", + "laminas/laminas-code": "~3.3.0", + "laminas/laminas-config": "^2.6.0", + "laminas/laminas-console": "^2.6.0", + "laminas/laminas-crypt": "^2.6.0", + "laminas/laminas-db": "^2.8.2", + "laminas/laminas-di": "^2.6.1", + "laminas/laminas-eventmanager": "^2.6.3", + "laminas/laminas-feed": "^2.9.0", + "laminas/laminas-form": "^2.10.0", + "laminas/laminas-http": "^2.6.0", + "laminas/laminas-i18n": "^2.7.3", + "laminas/laminas-json": "^2.6.1", + "laminas/laminas-log": "^2.9.1", + "laminas/laminas-mail": "^2.9.0", + "laminas/laminas-modulemanager": "^2.7", + "laminas/laminas-mvc": "~2.7.0", + "laminas/laminas-serializer": "^2.7.2", + "laminas/laminas-server": "^2.6.1", + "laminas/laminas-servicemanager": "^2.7.8", + "laminas/laminas-session": "^2.7.3", + "laminas/laminas-soap": "^2.7.0", + "laminas/laminas-stdlib": "^2.7.7", + "laminas/laminas-text": "^2.6.0", + "laminas/laminas-uri": "^2.5.1", + "laminas/laminas-validator": "^2.6.0", + "laminas/laminas-view": "~2.10.0" }, "type": "metapackage", "license": [ @@ -12065,8 +11946,8 @@ "monolog/monolog": "^1.17.0", "php": "~7.1.3||~7.2.0", "psr/log": "~1.0", - "zendframework/zend-barcode": "^2.7.0", - "zendframework/zend-http": "^2.6.0" + "laminas/laminas-barcode": "^2.7.0", + "laminas/laminas-http": "^2.6.0" }, "suggest": { "magento/module-rma": "^101.1.0", @@ -12399,29 +12280,29 @@ "time": "2018-09-07T08:16:44+00:00" }, { - "name": "zendframework/zend-barcode", + "name": "laminas/laminas-barcode", "version": "2.7.0", "source": { "type": "git", - "url": "https://github.com/zendframework/zend-barcode.git", + "url": "https://github.com/laminas/laminas-barcode.git", "reference": "50f24f604ef2172a0127efe91e786bc2caf2e8cf" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/zendframework/zend-barcode/zipball/50f24f604ef2172a0127efe91e786bc2caf2e8cf", + "url": "https://api.github.com/repos/laminas/laminas-barcode/zipball/50f24f604ef2172a0127efe91e786bc2caf2e8cf", "reference": "50f24f604ef2172a0127efe91e786bc2caf2e8cf", "shasum": "" }, "require": { "php": "^5.6 || ^7.0", - "zendframework/zend-servicemanager": "^2.7.8 || ^3.3", - "zendframework/zend-stdlib": "^2.7.7 || ^3.1", - "zendframework/zend-validator": "^2.10.1" + "laminas/laminas-servicemanager": "^2.7.8 || ^3.3", + "laminas/laminas-stdlib": "^2.7.7 || ^3.1", + "laminas/laminas-validator": "^2.10.1" }, "require-dev": { "phpunit/phpunit": "^5.7.23 || ^6.4.3", - "zendframework/zend-coding-standard": "~1.0.0", - "zendframework/zend-config": "^2.6 || ^3.1", + "laminas/laminas-coding-standard": "~1.0.0", + "laminas/laminas-config": "^2.6 || ^3.1", "zendframework/zendpdf": "^2.0.2" }, "suggest": { @@ -12436,7 +12317,7 @@ }, "autoload": { "psr-4": { - "Zend\\Barcode\\": "src/" + "Laminas\\Barcode\\": "src/" } }, "notification-url": "https://packagist.org/downloads/", @@ -12452,38 +12333,38 @@ "time": "2017-12-11T15:30:02+00:00" }, { - "name": "zendframework/zend-captcha", + "name": "laminas/laminas-captcha", "version": "2.8.0", "source": { "type": "git", - "url": "https://github.com/zendframework/zend-captcha.git", + "url": "https://github.com/laminas/laminas-captcha.git", "reference": "37e9b6a4f632a9399eecbf2e5e325ad89083f87b" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/zendframework/zend-captcha/zipball/37e9b6a4f632a9399eecbf2e5e325ad89083f87b", + "url": "https://api.github.com/repos/laminas/laminas-captcha/zipball/37e9b6a4f632a9399eecbf2e5e325ad89083f87b", "reference": "37e9b6a4f632a9399eecbf2e5e325ad89083f87b", "shasum": "" }, "require": { "php": "^5.6 || ^7.0", - "zendframework/zend-math": "^2.7 || ^3.0", - "zendframework/zend-stdlib": "^2.7.7 || ^3.1" + "laminas/laminas-math": "^2.7 || ^3.0", + "laminas/laminas-stdlib": "^2.7.7 || ^3.1" }, "require-dev": { "phpunit/phpunit": "^5.7.27 || ^6.5.8 || ^7.1.2", - "zendframework/zend-coding-standard": "~1.0.0", - "zendframework/zend-session": "^2.8", - "zendframework/zend-text": "^2.6", - "zendframework/zend-validator": "^2.10.1", - "zendframework/zendservice-recaptcha": "^3.0" + "laminas/laminas-coding-standard": "~1.0.0", + "laminas/laminas-session": "^2.8", + "laminas/laminas-text": "^2.6", + "laminas/laminas-validator": "^2.10.1", + "laminas/laminas-recaptcha": "^3.0" }, "suggest": { - "zendframework/zend-i18n-resources": "Translations of captcha messages", - "zendframework/zend-session": "Zend\\Session component", - "zendframework/zend-text": "Zend\\Text component", - "zendframework/zend-validator": "Zend\\Validator component", - "zendframework/zendservice-recaptcha": "ZendService\\ReCaptcha component" + "laminas/laminas-i18n-resources": "Translations of captcha messages", + "laminas/laminas-session": "Laminas\\Session component", + "laminas/laminas-text": "Laminas\\Text component", + "laminas/laminas-validator": "Laminas\\Validator component", + "laminas/laminas-recaptcha": "Laminas\\ReCaptcha component" }, "type": "library", "extra": { @@ -12494,7 +12375,7 @@ }, "autoload": { "psr-4": { - "Zend\\Captcha\\": "src/" + "Laminas\\Captcha\\": "src/" } }, "notification-url": "https://packagist.org/downloads/", @@ -12510,33 +12391,33 @@ "time": "2018-04-24T17:24:10+00:00" }, { - "name": "zendframework/zend-code", + "name": "laminas/laminas-code", "version": "3.3.1", "source": { "type": "git", - "url": "https://github.com/zendframework/zend-code.git", + "url": "https://github.com/laminas/laminas-code.git", "reference": "c21db169075c6ec4b342149f446e7b7b724f95eb" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/zendframework/zend-code/zipball/c21db169075c6ec4b342149f446e7b7b724f95eb", + "url": "https://api.github.com/repos/laminas/laminas-code/zipball/c21db169075c6ec4b342149f446e7b7b724f95eb", "reference": "c21db169075c6ec4b342149f446e7b7b724f95eb", "shasum": "" }, "require": { "php": "^7.1", - "zendframework/zend-eventmanager": "^2.6 || ^3.0" + "laminas/laminas-eventmanager": "^2.6 || ^3.0" }, "require-dev": { "doctrine/annotations": "~1.0", "ext-phar": "*", "phpunit/phpunit": "^6.2.3", - "zendframework/zend-coding-standard": "^1.0.0", - "zendframework/zend-stdlib": "^2.7 || ^3.0" + "laminas/laminas-coding-standard": "^1.0.0", + "laminas/laminas-stdlib": "^2.7 || ^3.0" }, "suggest": { "doctrine/annotations": "Doctrine\\Common\\Annotations >=1.0 for annotation features", - "zendframework/zend-stdlib": "Zend\\Stdlib component" + "laminas/laminas-stdlib": "Laminas\\Stdlib component" }, "type": "library", "extra": { @@ -12547,7 +12428,7 @@ }, "autoload": { "psr-4": { - "Zend\\Code\\": "src/" + "Laminas\\Code\\": "src/" } }, "notification-url": "https://packagist.org/downloads/", @@ -12555,7 +12436,7 @@ "BSD-3-Clause" ], "description": "provides facilities to generate arbitrary code using an object oriented interface", - "homepage": "https://github.com/zendframework/zend-code", + "homepage": "https://github.com/laminas/laminas-code", "keywords": [ "code", "zf2" @@ -12563,36 +12444,36 @@ "time": "2018-08-13T20:36:59+00:00" }, { - "name": "zendframework/zend-config", + "name": "laminas/laminas-config", "version": "2.6.0", "source": { "type": "git", - "url": "https://github.com/zendframework/zend-config.git", + "url": "https://github.com/laminas/laminas-config.git", "reference": "2920e877a9f6dca9fa8f6bd3b1ffc2e19bb1e30d" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/zendframework/zend-config/zipball/2920e877a9f6dca9fa8f6bd3b1ffc2e19bb1e30d", + "url": "https://api.github.com/repos/laminas/laminas-config/zipball/2920e877a9f6dca9fa8f6bd3b1ffc2e19bb1e30d", "reference": "2920e877a9f6dca9fa8f6bd3b1ffc2e19bb1e30d", "shasum": "" }, "require": { "php": "^5.5 || ^7.0", - "zendframework/zend-stdlib": "^2.7 || ^3.0" + "laminas/laminas-stdlib": "^2.7 || ^3.0" }, "require-dev": { "fabpot/php-cs-fixer": "1.7.*", "phpunit/phpunit": "~4.0", - "zendframework/zend-filter": "^2.6", - "zendframework/zend-i18n": "^2.5", - "zendframework/zend-json": "^2.6.1", - "zendframework/zend-servicemanager": "^2.7.5 || ^3.0.3" + "laminas/laminas-filter": "^2.6", + "laminas/laminas-i18n": "^2.5", + "laminas/laminas-json": "^2.6.1", + "laminas/laminas-servicemanager": "^2.7.5 || ^3.0.3" }, "suggest": { - "zendframework/zend-filter": "Zend\\Filter component", - "zendframework/zend-i18n": "Zend\\I18n component", - "zendframework/zend-json": "Zend\\Json to use the Json reader or writer classes", - "zendframework/zend-servicemanager": "Zend\\ServiceManager for use with the Config Factory to retrieve reader and writer instances" + "laminas/laminas-filter": "Laminas\\Filter component", + "laminas/laminas-i18n": "Laminas\\I18n component", + "laminas/laminas-json": "Laminas\\Json to use the Json reader or writer classes", + "laminas/laminas-servicemanager": "Laminas\\ServiceManager for use with the Config Factory to retrieve reader and writer instances" }, "type": "library", "extra": { @@ -12603,7 +12484,7 @@ }, "autoload": { "psr-4": { - "Zend\\Config\\": "src/" + "Laminas\\Config\\": "src/" } }, "notification-url": "https://packagist.org/downloads/", @@ -12611,7 +12492,7 @@ "BSD-3-Clause" ], "description": "provides a nested object property based user interface for accessing this configuration data within application code", - "homepage": "https://github.com/zendframework/zend-config", + "homepage": "https://github.com/laminas/laminas-config", "keywords": [ "config", "zf2" @@ -12619,33 +12500,33 @@ "time": "2016-02-04T23:01:10+00:00" }, { - "name": "zendframework/zend-console", + "name": "laminas/laminas-console", "version": "2.7.0", "source": { "type": "git", - "url": "https://github.com/zendframework/zend-console.git", + "url": "https://github.com/laminas/laminas-console.git", "reference": "e8aa08da83de3d265256c40ba45cd649115f0e18" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/zendframework/zend-console/zipball/e8aa08da83de3d265256c40ba45cd649115f0e18", + "url": "https://api.github.com/repos/laminas/laminas-console/zipball/e8aa08da83de3d265256c40ba45cd649115f0e18", "reference": "e8aa08da83de3d265256c40ba45cd649115f0e18", "shasum": "" }, "require": { "php": "^5.6 || ^7.0", - "zendframework/zend-stdlib": "^2.7.7 || ^3.1" + "laminas/laminas-stdlib": "^2.7.7 || ^3.1" }, "require-dev": { "phpunit/phpunit": "^5.7.23 || ^6.4.3", - "zendframework/zend-coding-standard": "~1.0.0", - "zendframework/zend-filter": "^2.7.2", - "zendframework/zend-json": "^2.6 || ^3.0", - "zendframework/zend-validator": "^2.10.1" + "laminas/laminas-coding-standard": "~1.0.0", + "laminas/laminas-filter": "^2.7.2", + "laminas/laminas-json": "^2.6 || ^3.0", + "laminas/laminas-validator": "^2.10.1" }, "suggest": { - "zendframework/zend-filter": "To support DefaultRouteMatcher usage", - "zendframework/zend-validator": "To support DefaultRouteMatcher usage" + "laminas/laminas-filter": "To support DefaultRouteMatcher usage", + "laminas/laminas-validator": "To support DefaultRouteMatcher usage" }, "type": "library", "extra": { @@ -12656,7 +12537,7 @@ }, "autoload": { "psr-4": { - "Zend\\Console\\": "src/" + "Laminas\\Console\\": "src/" } }, "notification-url": "https://packagist.org/downloads/", @@ -12672,31 +12553,31 @@ "time": "2018-01-25T19:08:04+00:00" }, { - "name": "zendframework/zend-crypt", + "name": "laminas/laminas-crypt", "version": "2.6.0", "source": { "type": "git", - "url": "https://github.com/zendframework/zend-crypt.git", + "url": "https://github.com/laminas/laminas-crypt.git", "reference": "1b2f5600bf6262904167116fa67b58ab1457036d" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/zendframework/zend-crypt/zipball/1b2f5600bf6262904167116fa67b58ab1457036d", + "url": "https://api.github.com/repos/laminas/laminas-crypt/zipball/1b2f5600bf6262904167116fa67b58ab1457036d", "reference": "1b2f5600bf6262904167116fa67b58ab1457036d", "shasum": "" }, "require": { "container-interop/container-interop": "~1.0", "php": "^5.5 || ^7.0", - "zendframework/zend-math": "^2.6", - "zendframework/zend-stdlib": "^2.7 || ^3.0" + "laminas/laminas-math": "^2.6", + "laminas/laminas-stdlib": "^2.7 || ^3.0" }, "require-dev": { "fabpot/php-cs-fixer": "1.7.*", "phpunit/phpunit": "~4.0" }, "suggest": { - "ext-mcrypt": "Required for most features of Zend\\Crypt" + "ext-mcrypt": "Required for most features of Laminas\\Crypt" }, "type": "library", "extra": { @@ -12707,14 +12588,14 @@ }, "autoload": { "psr-4": { - "Zend\\Crypt\\": "src/" + "Laminas\\Crypt\\": "src/" } }, "notification-url": "https://packagist.org/downloads/", "license": [ "BSD-3-Clause" ], - "homepage": "https://github.com/zendframework/zend-crypt", + "homepage": "https://github.com/laminas/laminas-crypt", "keywords": [ "crypt", "zf2" @@ -12722,34 +12603,34 @@ "time": "2016-02-03T23:46:30+00:00" }, { - "name": "zendframework/zend-db", + "name": "laminas/laminas-db", "version": "2.10.0", "source": { "type": "git", - "url": "https://github.com/zendframework/zend-db.git", + "url": "https://github.com/laminas/laminas-db.git", "reference": "77022f06f6ffd384fa86d22ab8d8bbdb925a1e8e" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/zendframework/zend-db/zipball/77022f06f6ffd384fa86d22ab8d8bbdb925a1e8e", + "url": "https://api.github.com/repos/laminas/laminas-db/zipball/77022f06f6ffd384fa86d22ab8d8bbdb925a1e8e", "reference": "77022f06f6ffd384fa86d22ab8d8bbdb925a1e8e", "shasum": "" }, "require": { "php": "^5.6 || ^7.0", - "zendframework/zend-stdlib": "^2.7 || ^3.0" + "laminas/laminas-stdlib": "^2.7 || ^3.0" }, "require-dev": { "phpunit/phpunit": "^5.7.25 || ^6.4.4", - "zendframework/zend-coding-standard": "~1.0.0", - "zendframework/zend-eventmanager": "^2.6.2 || ^3.0", - "zendframework/zend-hydrator": "^1.1 || ^2.1 || ^3.0", - "zendframework/zend-servicemanager": "^2.7.5 || ^3.0.3" + "laminas/laminas-coding-standard": "~1.0.0", + "laminas/laminas-eventmanager": "^2.6.2 || ^3.0", + "laminas/laminas-hydrator": "^1.1 || ^2.1 || ^3.0", + "laminas/laminas-servicemanager": "^2.7.5 || ^3.0.3" }, "suggest": { - "zendframework/zend-eventmanager": "Zend\\EventManager component", - "zendframework/zend-hydrator": "Zend\\Hydrator component for using HydratingResultSets", - "zendframework/zend-servicemanager": "Zend\\ServiceManager component" + "laminas/laminas-eventmanager": "Laminas\\EventManager component", + "laminas/laminas-hydrator": "Laminas\\Hydrator component for using HydratingResultSets", + "laminas/laminas-servicemanager": "Laminas\\ServiceManager component" }, "type": "library", "extra": { @@ -12758,13 +12639,13 @@ "dev-develop": "2.10-dev" }, "zf": { - "component": "Zend\\Db", - "config-provider": "Zend\\Db\\ConfigProvider" + "component": "Laminas\\Db", + "config-provider": "Laminas\\Db\\ConfigProvider" } }, "autoload": { "psr-4": { - "Zend\\Db\\": "src/" + "Laminas\\Db\\": "src/" } }, "notification-url": "https://packagist.org/downloads/", @@ -12780,24 +12661,24 @@ "time": "2019-02-25T11:37:45+00:00" }, { - "name": "zendframework/zend-di", + "name": "laminas/laminas-di", "version": "2.6.1", "source": { "type": "git", - "url": "https://github.com/zendframework/zend-di.git", + "url": "https://github.com/laminas/laminas-di.git", "reference": "1fd1ba85660b5a2718741b38639dc7c4c3194b37" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/zendframework/zend-di/zipball/1fd1ba85660b5a2718741b38639dc7c4c3194b37", + "url": "https://api.github.com/repos/laminas/laminas-di/zipball/1fd1ba85660b5a2718741b38639dc7c4c3194b37", "reference": "1fd1ba85660b5a2718741b38639dc7c4c3194b37", "shasum": "" }, "require": { "container-interop/container-interop": "^1.1", "php": "^5.5 || ^7.0", - "zendframework/zend-code": "^2.6 || ^3.0", - "zendframework/zend-stdlib": "^2.7 || ^3.0" + "laminas/laminas-code": "^2.6 || ^3.0", + "laminas/laminas-stdlib": "^2.7 || ^3.0" }, "require-dev": { "fabpot/php-cs-fixer": "1.7.*", @@ -12812,14 +12693,14 @@ }, "autoload": { "psr-4": { - "Zend\\Di\\": "src/" + "Laminas\\Di\\": "src/" } }, "notification-url": "https://packagist.org/downloads/", "license": [ "BSD-3-Clause" ], - "homepage": "https://github.com/zendframework/zend-di", + "homepage": "https://github.com/laminas/laminas-di", "keywords": [ "di", "zf2" @@ -12827,16 +12708,16 @@ "time": "2016-04-25T20:58:11+00:00" }, { - "name": "zendframework/zend-diactoros", + "name": "laminas/laminas-diactoros", "version": "1.8.6", "source": { "type": "git", - "url": "https://github.com/zendframework/zend-diactoros.git", + "url": "https://github.com/laminas/laminas-diactoros.git", "reference": "20da13beba0dde8fb648be3cc19765732790f46e" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/zendframework/zend-diactoros/zipball/20da13beba0dde8fb648be3cc19765732790f46e", + "url": "https://api.github.com/repos/laminas/laminas-diactoros/zipball/20da13beba0dde8fb648be3cc19765732790f46e", "reference": "20da13beba0dde8fb648be3cc19765732790f46e", "shasum": "" }, @@ -12852,7 +12733,7 @@ "ext-libxml": "*", "php-http/psr7-integration-tests": "dev-master", "phpunit/phpunit": "^5.7.16 || ^6.0.8 || ^7.2.7", - "zendframework/zend-coding-standard": "~1.0" + "laminas/laminas-coding-standard": "~1.0" }, "type": "library", "extra": { @@ -12874,7 +12755,7 @@ "src/functions/parse_cookie_header.php" ], "psr-4": { - "Zend\\Diactoros\\": "src/" + "Laminas\\Diactoros\\": "src/" } }, "notification-url": "https://packagist.org/downloads/", @@ -12882,7 +12763,7 @@ "BSD-2-Clause" ], "description": "PSR HTTP Message implementations", - "homepage": "https://github.com/zendframework/zend-diactoros", + "homepage": "https://github.com/laminas/laminas-diactoros", "keywords": [ "http", "psr", @@ -12891,16 +12772,16 @@ "time": "2018-09-05T19:29:37+00:00" }, { - "name": "zendframework/zend-escaper", + "name": "laminas/laminas-escaper", "version": "2.6.0", "source": { "type": "git", - "url": "https://github.com/zendframework/zend-escaper.git", + "url": "https://github.com/laminas/laminas-escaper.git", "reference": "31d8aafae982f9568287cb4dce987e6aff8fd074" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/zendframework/zend-escaper/zipball/31d8aafae982f9568287cb4dce987e6aff8fd074", + "url": "https://api.github.com/repos/laminas/laminas-escaper/zipball/31d8aafae982f9568287cb4dce987e6aff8fd074", "reference": "31d8aafae982f9568287cb4dce987e6aff8fd074", "shasum": "" }, @@ -12909,7 +12790,7 @@ }, "require-dev": { "phpunit/phpunit": "^5.7.27 || ^6.5.8 || ^7.1.2", - "zendframework/zend-coding-standard": "~1.0.0" + "laminas/laminas-coding-standard": "~1.0.0" }, "type": "library", "extra": { @@ -12920,7 +12801,7 @@ }, "autoload": { "psr-4": { - "Zend\\Escaper\\": "src/" + "Laminas\\Escaper\\": "src/" } }, "notification-url": "https://packagist.org/downloads/", @@ -12936,22 +12817,22 @@ "time": "2018-04-25T15:48:53+00:00" }, { - "name": "zendframework/zend-eventmanager", + "name": "laminas/laminas-eventmanager", "version": "2.6.4", "source": { "type": "git", - "url": "https://github.com/zendframework/zend-eventmanager.git", + "url": "https://github.com/laminas/laminas-eventmanager.git", "reference": "d238c443220dce4b6396579c8ab2200ec25f9108" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/zendframework/zend-eventmanager/zipball/d238c443220dce4b6396579c8ab2200ec25f9108", + "url": "https://api.github.com/repos/laminas/laminas-eventmanager/zipball/d238c443220dce4b6396579c8ab2200ec25f9108", "reference": "d238c443220dce4b6396579c8ab2200ec25f9108", "shasum": "" }, "require": { "php": "^5.5 || ^7.0", - "zendframework/zend-stdlib": "^2.7" + "laminas/laminas-stdlib": "^2.7" }, "require-dev": { "athletic/athletic": "dev-master", @@ -12968,14 +12849,14 @@ }, "autoload": { "psr-4": { - "Zend\\EventManager\\": "src/" + "Laminas\\EventManager\\": "src/" } }, "notification-url": "https://packagist.org/downloads/", "license": [ "BSD-3-Clause" ], - "homepage": "https://github.com/zendframework/zend-eventmanager", + "homepage": "https://github.com/laminas/laminas-eventmanager", "keywords": [ "eventmanager", "zf2" @@ -12983,41 +12864,41 @@ "time": "2017-12-12T17:48:56+00:00" }, { - "name": "zendframework/zend-feed", + "name": "laminas/laminas-feed", "version": "2.10.3", "source": { "type": "git", - "url": "https://github.com/zendframework/zend-feed.git", + "url": "https://github.com/laminas/laminas-feed.git", "reference": "6641f4cf3f4586c63f83fd70b6d19966025c8888" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/zendframework/zend-feed/zipball/6641f4cf3f4586c63f83fd70b6d19966025c8888", + "url": "https://api.github.com/repos/laminas/laminas-feed/zipball/6641f4cf3f4586c63f83fd70b6d19966025c8888", "reference": "6641f4cf3f4586c63f83fd70b6d19966025c8888", "shasum": "" }, "require": { "php": "^5.6 || ^7.0", - "zendframework/zend-escaper": "^2.5.2", - "zendframework/zend-stdlib": "^2.7.7 || ^3.1" + "laminas/laminas-escaper": "^2.5.2", + "laminas/laminas-stdlib": "^2.7.7 || ^3.1" }, "require-dev": { "phpunit/phpunit": "^5.7.23 || ^6.4.3", "psr/http-message": "^1.0.1", - "zendframework/zend-cache": "^2.7.2", - "zendframework/zend-coding-standard": "~1.0.0", - "zendframework/zend-db": "^2.8.2", - "zendframework/zend-http": "^2.7", - "zendframework/zend-servicemanager": "^2.7.8 || ^3.3", - "zendframework/zend-validator": "^2.10.1" + "laminas/laminas-cache": "^2.7.2", + "laminas/laminas-coding-standard": "~1.0.0", + "laminas/laminas-db": "^2.8.2", + "laminas/laminas-http": "^2.7", + "laminas/laminas-servicemanager": "^2.7.8 || ^3.3", + "laminas/laminas-validator": "^2.10.1" }, "suggest": { - "psr/http-message": "PSR-7 ^1.0.1, if you wish to use Zend\\Feed\\Reader\\Http\\Psr7ResponseDecorator", - "zendframework/zend-cache": "Zend\\Cache component, for optionally caching feeds between requests", - "zendframework/zend-db": "Zend\\Db component, for use with PubSubHubbub", - "zendframework/zend-http": "Zend\\Http for PubSubHubbub, and optionally for use with Zend\\Feed\\Reader", - "zendframework/zend-servicemanager": "Zend\\ServiceManager component, for easily extending ExtensionManager implementations", - "zendframework/zend-validator": "Zend\\Validator component, for validating email addresses used in Atom feeds and entries when using the Writer subcomponent" + "psr/http-message": "PSR-7 ^1.0.1, if you wish to use Laminas\\Feed\\Reader\\Http\\Psr7ResponseDecorator", + "laminas/laminas-cache": "Laminas\\Cache component, for optionally caching feeds between requests", + "laminas/laminas-db": "Laminas\\Db component, for use with PubSubHubbub", + "laminas/laminas-http": "Laminas\\Http for PubSubHubbub, and optionally for use with Laminas\\Feed\\Reader", + "laminas/laminas-servicemanager": "Laminas\\ServiceManager component, for easily extending ExtensionManager implementations", + "laminas/laminas-validator": "Laminas\\Validator component, for validating email addresses used in Atom feeds and entries when using the Writer subcomponent" }, "type": "library", "extra": { @@ -13028,7 +12909,7 @@ }, "autoload": { "psr-4": { - "Zend\\Feed\\": "src/" + "Laminas\\Feed\\": "src/" } }, "notification-url": "https://packagist.org/downloads/", @@ -13044,41 +12925,41 @@ "time": "2018-08-01T13:53:20+00:00" }, { - "name": "zendframework/zend-filter", + "name": "laminas/laminas-filter", "version": "2.9.1", "source": { "type": "git", - "url": "https://github.com/zendframework/zend-filter.git", + "url": "https://github.com/laminas/laminas-filter.git", "reference": "1c3e6d02f9cd5f6c929c9859498f5efbe216e86f" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/zendframework/zend-filter/zipball/1c3e6d02f9cd5f6c929c9859498f5efbe216e86f", + "url": "https://api.github.com/repos/laminas/laminas-filter/zipball/1c3e6d02f9cd5f6c929c9859498f5efbe216e86f", "reference": "1c3e6d02f9cd5f6c929c9859498f5efbe216e86f", "shasum": "" }, "require": { "php": "^5.6 || ^7.0", - "zendframework/zend-stdlib": "^2.7.7 || ^3.1" + "laminas/laminas-stdlib": "^2.7.7 || ^3.1" }, "conflict": { - "zendframework/zend-validator": "<2.10.1" + "laminas/laminas-validator": "<2.10.1" }, "require-dev": { "pear/archive_tar": "^1.4.3", "phpunit/phpunit": "^5.7.23 || ^6.4.3", "psr/http-factory": "^1.0", - "zendframework/zend-coding-standard": "~1.0.0", - "zendframework/zend-crypt": "^3.2.1", - "zendframework/zend-servicemanager": "^2.7.8 || ^3.3", - "zendframework/zend-uri": "^2.6" + "laminas/laminas-coding-standard": "~1.0.0", + "laminas/laminas-crypt": "^3.2.1", + "laminas/laminas-servicemanager": "^2.7.8 || ^3.3", + "laminas/laminas-uri": "^2.6" }, "suggest": { "psr/http-factory-implementation": "psr/http-factory-implementation, for creating file upload instances when consuming PSR-7 in file upload filters", - "zendframework/zend-crypt": "Zend\\Crypt component, for encryption filters", - "zendframework/zend-i18n": "Zend\\I18n component for filters depending on i18n functionality", - "zendframework/zend-servicemanager": "Zend\\ServiceManager component, for using the filter chain functionality", - "zendframework/zend-uri": "Zend\\Uri component, for the UriNormalize filter" + "laminas/laminas-crypt": "Laminas\\Crypt component, for encryption filters", + "laminas/laminas-i18n": "Laminas\\I18n component for filters depending on i18n functionality", + "laminas/laminas-servicemanager": "Laminas\\ServiceManager component, for using the filter chain functionality", + "laminas/laminas-uri": "Laminas\\Uri component, for the UriNormalize filter" }, "type": "library", "extra": { @@ -13087,13 +12968,13 @@ "dev-develop": "2.10.x-dev" }, "zf": { - "component": "Zend\\Filter", - "config-provider": "Zend\\Filter\\ConfigProvider" + "component": "Laminas\\Filter", + "config-provider": "Laminas\\Filter\\ConfigProvider" } }, "autoload": { "psr-4": { - "Zend\\Filter\\": "src/" + "Laminas\\Filter\\": "src/" } }, "notification-url": "https://packagist.org/downloads/", @@ -13109,51 +12990,51 @@ "time": "2018-12-17T16:00:04+00:00" }, { - "name": "zendframework/zend-form", + "name": "laminas/laminas-form", "version": "2.13.0", "source": { "type": "git", - "url": "https://github.com/zendframework/zend-form.git", + "url": "https://github.com/laminas/laminas-form.git", "reference": "c713a12ccbd43148b71c9339e171ca11e3f8a1da" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/zendframework/zend-form/zipball/c713a12ccbd43148b71c9339e171ca11e3f8a1da", + "url": "https://api.github.com/repos/laminas/laminas-form/zipball/c713a12ccbd43148b71c9339e171ca11e3f8a1da", "reference": "c713a12ccbd43148b71c9339e171ca11e3f8a1da", "shasum": "" }, "require": { "php": "^5.6 || ^7.0", - "zendframework/zend-hydrator": "^1.1 || ^2.1 || ^3.0", - "zendframework/zend-inputfilter": "^2.8", - "zendframework/zend-stdlib": "^2.7 || ^3.0" + "laminas/laminas-hydrator": "^1.1 || ^2.1 || ^3.0", + "laminas/laminas-inputfilter": "^2.8", + "laminas/laminas-stdlib": "^2.7 || ^3.0" }, "require-dev": { "doctrine/annotations": "~1.0", "phpunit/phpunit": "^5.7.23 || ^6.5.3", - "zendframework/zend-cache": "^2.6.1", - "zendframework/zend-captcha": "^2.7.1", - "zendframework/zend-code": "^2.6 || ^3.0", - "zendframework/zend-coding-standard": "~1.0.0", - "zendframework/zend-escaper": "^2.5", - "zendframework/zend-eventmanager": "^2.6.2 || ^3.0", - "zendframework/zend-filter": "^2.6", - "zendframework/zend-i18n": "^2.6", - "zendframework/zend-servicemanager": "^2.7.5 || ^3.0.3", - "zendframework/zend-session": "^2.8.1", - "zendframework/zend-text": "^2.6", - "zendframework/zend-validator": "^2.6", - "zendframework/zend-view": "^2.6.2", - "zendframework/zendservice-recaptcha": "^3.0.0" + "laminas/laminas-cache": "^2.6.1", + "laminas/laminas-captcha": "^2.7.1", + "laminas/laminas-code": "^2.6 || ^3.0", + "laminas/laminas-coding-standard": "~1.0.0", + "laminas/laminas-escaper": "^2.5", + "laminas/laminas-eventmanager": "^2.6.2 || ^3.0", + "laminas/laminas-filter": "^2.6", + "laminas/laminas-i18n": "^2.6", + "laminas/laminas-servicemanager": "^2.7.5 || ^3.0.3", + "laminas/laminas-session": "^2.8.1", + "laminas/laminas-text": "^2.6", + "laminas/laminas-validator": "^2.6", + "laminas/laminas-view": "^2.6.2", + "laminas/laminas-recaptcha": "^3.0.0" }, "suggest": { - "zendframework/zend-captcha": "^2.7.1, required for using CAPTCHA form elements", - "zendframework/zend-code": "^2.6 || ^3.0, required to use zend-form annotations support", - "zendframework/zend-eventmanager": "^2.6.2 || ^3.0, reuired for zend-form annotations support", - "zendframework/zend-i18n": "^2.6, required when using zend-form view helpers", - "zendframework/zend-servicemanager": "^2.7.5 || ^3.0.3, required to use the form factories or provide services", - "zendframework/zend-view": "^2.6.2, required for using the zend-form view helpers", - "zendframework/zendservice-recaptcha": "in order to use the ReCaptcha form element" + "laminas/laminas-captcha": "^2.7.1, required for using CAPTCHA form elements", + "laminas/laminas-code": "^2.6 || ^3.0, required to use zend-form annotations support", + "laminas/laminas-eventmanager": "^2.6.2 || ^3.0, reuired for zend-form annotations support", + "laminas/laminas-i18n": "^2.6, required when using zend-form view helpers", + "laminas/laminas-servicemanager": "^2.7.5 || ^3.0.3, required to use the form factories or provide services", + "laminas/laminas-view": "^2.6.2, required for using the zend-form view helpers", + "laminas/laminas-recaptcha": "in order to use the ReCaptcha form element" }, "type": "library", "extra": { @@ -13162,13 +13043,13 @@ "dev-develop": "2.14.x-dev" }, "zf": { - "component": "Zend\\Form", - "config-provider": "Zend\\Form\\ConfigProvider" + "component": "Laminas\\Form", + "config-provider": "Laminas\\Form\\ConfigProvider" } }, "autoload": { "psr-4": { - "Zend\\Form\\": "src/" + "Laminas\\Form\\": "src/" }, "files": [ "autoload/formElementManagerPolyfill.php" @@ -13187,30 +13068,30 @@ "time": "2018-12-11T22:51:29+00:00" }, { - "name": "zendframework/zend-http", + "name": "laminas/laminas-http", "version": "2.8.4", "source": { "type": "git", - "url": "https://github.com/zendframework/zend-http.git", + "url": "https://github.com/laminas/laminas-http.git", "reference": "d160aedc096be230af0fe9c31151b2b33ad4e807" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/zendframework/zend-http/zipball/d160aedc096be230af0fe9c31151b2b33ad4e807", + "url": "https://api.github.com/repos/laminas/laminas-http/zipball/d160aedc096be230af0fe9c31151b2b33ad4e807", "reference": "d160aedc096be230af0fe9c31151b2b33ad4e807", "shasum": "" }, "require": { "php": "^5.6 || ^7.0", - "zendframework/zend-loader": "^2.5.1", - "zendframework/zend-stdlib": "^3.1 || ^2.7.7", - "zendframework/zend-uri": "^2.5.2", - "zendframework/zend-validator": "^2.10.1" + "laminas/laminas-loader": "^2.5.1", + "laminas/laminas-stdlib": "^3.1 || ^2.7.7", + "laminas/laminas-uri": "^2.5.2", + "laminas/laminas-validator": "^2.10.1" }, "require-dev": { "phpunit/phpunit": "^5.7.27 || ^6.5.8 || ^7.1.3", - "zendframework/zend-coding-standard": "~1.0.0", - "zendframework/zend-config": "^3.1 || ^2.6" + "laminas/laminas-coding-standard": "~1.0.0", + "laminas/laminas-config": "^3.1 || ^2.6" }, "suggest": { "paragonie/certainty": "For automated management of cacert.pem" @@ -13224,7 +13105,7 @@ }, "autoload": { "psr-4": { - "Zend\\Http\\": "src/" + "Laminas\\Http\\": "src/" } }, "notification-url": "https://packagist.org/downloads/", @@ -13242,37 +13123,37 @@ "time": "2019-02-07T17:47:08+00:00" }, { - "name": "zendframework/zend-hydrator", + "name": "laminas/laminas-hydrator", "version": "1.1.0", "source": { "type": "git", - "url": "https://github.com/zendframework/zend-hydrator.git", + "url": "https://github.com/laminas/laminas-hydrator.git", "reference": "22652e1661a5a10b3f564cf7824a2206cf5a4a65" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/zendframework/zend-hydrator/zipball/22652e1661a5a10b3f564cf7824a2206cf5a4a65", + "url": "https://api.github.com/repos/laminas/laminas-hydrator/zipball/22652e1661a5a10b3f564cf7824a2206cf5a4a65", "reference": "22652e1661a5a10b3f564cf7824a2206cf5a4a65", "shasum": "" }, "require": { "php": "^5.5 || ^7.0", - "zendframework/zend-stdlib": "^2.7 || ^3.0" + "laminas/laminas-stdlib": "^2.7 || ^3.0" }, "require-dev": { "phpunit/phpunit": "~4.0", "squizlabs/php_codesniffer": "^2.0@dev", - "zendframework/zend-eventmanager": "^2.6.2 || ^3.0", - "zendframework/zend-filter": "^2.6", - "zendframework/zend-inputfilter": "^2.6", - "zendframework/zend-serializer": "^2.6.1", - "zendframework/zend-servicemanager": "^2.7.5 || ^3.0.3" + "laminas/laminas-eventmanager": "^2.6.2 || ^3.0", + "laminas/laminas-filter": "^2.6", + "laminas/laminas-inputfilter": "^2.6", + "laminas/laminas-serializer": "^2.6.1", + "laminas/laminas-servicemanager": "^2.7.5 || ^3.0.3" }, "suggest": { - "zendframework/zend-eventmanager": "^2.6.2 || ^3.0, to support aggregate hydrator usage", - "zendframework/zend-filter": "^2.6, to support naming strategy hydrator usage", - "zendframework/zend-serializer": "^2.6.1, to use the SerializableStrategy", - "zendframework/zend-servicemanager": "^2.7.5 || ^3.0.3, to support hydrator plugin manager usage" + "laminas/laminas-eventmanager": "^2.6.2 || ^3.0, to support aggregate hydrator usage", + "laminas/laminas-filter": "^2.6, to support naming strategy hydrator usage", + "laminas/laminas-serializer": "^2.6.1, to use the SerializableStrategy", + "laminas/laminas-servicemanager": "^2.7.5 || ^3.0.3, to support hydrator plugin manager usage" }, "type": "library", "extra": { @@ -13285,14 +13166,14 @@ }, "autoload": { "psr-4": { - "Zend\\Hydrator\\": "src/" + "Laminas\\Hydrator\\": "src/" } }, "notification-url": "https://packagist.org/downloads/", "license": [ "BSD-3-Clause" ], - "homepage": "https://github.com/zendframework/zend-hydrator", + "homepage": "https://github.com/laminas/laminas-hydrator", "keywords": [ "hydrator", "zf2" @@ -13300,44 +13181,44 @@ "time": "2016-02-18T22:38:26+00:00" }, { - "name": "zendframework/zend-i18n", + "name": "laminas/laminas-i18n", "version": "2.9.0", "source": { "type": "git", - "url": "https://github.com/zendframework/zend-i18n.git", + "url": "https://github.com/laminas/laminas-i18n.git", "reference": "6d69af5a04e1a4de7250043cb1322f077a0cdb7f" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/zendframework/zend-i18n/zipball/6d69af5a04e1a4de7250043cb1322f077a0cdb7f", + "url": "https://api.github.com/repos/laminas/laminas-i18n/zipball/6d69af5a04e1a4de7250043cb1322f077a0cdb7f", "reference": "6d69af5a04e1a4de7250043cb1322f077a0cdb7f", "shasum": "" }, "require": { "php": "^5.6 || ^7.0", - "zendframework/zend-stdlib": "^2.7 || ^3.0" + "laminas/laminas-stdlib": "^2.7 || ^3.0" }, "require-dev": { "phpunit/phpunit": "^5.7.27 || ^6.5.8 || ^7.1.2", - "zendframework/zend-cache": "^2.6.1", - "zendframework/zend-coding-standard": "~1.0.0", - "zendframework/zend-config": "^2.6", - "zendframework/zend-eventmanager": "^2.6.2 || ^3.0", - "zendframework/zend-filter": "^2.6.1", - "zendframework/zend-servicemanager": "^2.7.5 || ^3.0.3", - "zendframework/zend-validator": "^2.6", - "zendframework/zend-view": "^2.6.3" + "laminas/laminas-cache": "^2.6.1", + "laminas/laminas-coding-standard": "~1.0.0", + "laminas/laminas-config": "^2.6", + "laminas/laminas-eventmanager": "^2.6.2 || ^3.0", + "laminas/laminas-filter": "^2.6.1", + "laminas/laminas-servicemanager": "^2.7.5 || ^3.0.3", + "laminas/laminas-validator": "^2.6", + "laminas/laminas-view": "^2.6.3" }, "suggest": { - "ext-intl": "Required for most features of Zend\\I18n; included in default builds of PHP", - "zendframework/zend-cache": "Zend\\Cache component", - "zendframework/zend-config": "Zend\\Config component", - "zendframework/zend-eventmanager": "You should install this package to use the events in the translator", - "zendframework/zend-filter": "You should install this package to use the provided filters", - "zendframework/zend-i18n-resources": "Translation resources", - "zendframework/zend-servicemanager": "Zend\\ServiceManager component", - "zendframework/zend-validator": "You should install this package to use the provided validators", - "zendframework/zend-view": "You should install this package to use the provided view helpers" + "ext-intl": "Required for most features of Laminas\\I18n; included in default builds of PHP", + "laminas/laminas-cache": "Laminas\\Cache component", + "laminas/laminas-config": "Laminas\\Config component", + "laminas/laminas-eventmanager": "You should install this package to use the events in the translator", + "laminas/laminas-filter": "You should install this package to use the provided filters", + "laminas/laminas-i18n-resources": "Translation resources", + "laminas/laminas-servicemanager": "Laminas\\ServiceManager component", + "laminas/laminas-validator": "You should install this package to use the provided validators", + "laminas/laminas-view": "You should install this package to use the provided view helpers" }, "type": "library", "extra": { @@ -13346,13 +13227,13 @@ "dev-develop": "2.10.x-dev" }, "zf": { - "component": "Zend\\I18n", - "config-provider": "Zend\\I18n\\ConfigProvider" + "component": "Laminas\\I18n", + "config-provider": "Laminas\\I18n\\ConfigProvider" } }, "autoload": { "psr-4": { - "Zend\\I18n\\": "src/" + "Laminas\\I18n\\": "src/" } }, "notification-url": "https://packagist.org/downloads/", @@ -13368,30 +13249,30 @@ "time": "2018-05-16T16:39:13+00:00" }, { - "name": "zendframework/zend-inputfilter", + "name": "laminas/laminas-inputfilter", "version": "2.10.0", "source": { "type": "git", - "url": "https://github.com/zendframework/zend-inputfilter.git", + "url": "https://github.com/laminas/laminas-inputfilter.git", "reference": "4f52b71ec9cef3a06e3bba8f5c2124e94055ec0c" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/zendframework/zend-inputfilter/zipball/4f52b71ec9cef3a06e3bba8f5c2124e94055ec0c", + "url": "https://api.github.com/repos/laminas/laminas-inputfilter/zipball/4f52b71ec9cef3a06e3bba8f5c2124e94055ec0c", "reference": "4f52b71ec9cef3a06e3bba8f5c2124e94055ec0c", "shasum": "" }, "require": { "php": "^5.6 || ^7.0", - "zendframework/zend-filter": "^2.9.1", - "zendframework/zend-servicemanager": "^2.7.10 || ^3.3.1", - "zendframework/zend-stdlib": "^2.7 || ^3.0", - "zendframework/zend-validator": "^2.11" + "laminas/laminas-filter": "^2.9.1", + "laminas/laminas-servicemanager": "^2.7.10 || ^3.3.1", + "laminas/laminas-stdlib": "^2.7 || ^3.0", + "laminas/laminas-validator": "^2.11" }, "require-dev": { "phpunit/phpunit": "^5.7.23 || ^6.4.3", "psr/http-message": "^1.0", - "zendframework/zend-coding-standard": "~1.0.0" + "laminas/laminas-coding-standard": "~1.0.0" }, "suggest": { "psr/http-message-implementation": "PSR-7 is required if you wish to validate PSR-7 UploadedFileInterface payloads" @@ -13403,13 +13284,13 @@ "dev-develop": "2.11.x-dev" }, "zf": { - "component": "Zend\\InputFilter", - "config-provider": "Zend\\InputFilter\\ConfigProvider" + "component": "Laminas\\InputFilter", + "config-provider": "Laminas\\InputFilter\\ConfigProvider" } }, "autoload": { "psr-4": { - "Zend\\InputFilter\\": "src/" + "Laminas\\InputFilter\\": "src/" } }, "notification-url": "https://packagist.org/downloads/", @@ -13425,16 +13306,16 @@ "time": "2019-01-30T16:58:51+00:00" }, { - "name": "zendframework/zend-json", + "name": "laminas/laminas-json", "version": "2.6.1", "source": { "type": "git", - "url": "https://github.com/zendframework/zend-json.git", + "url": "https://github.com/laminas/laminas-json.git", "reference": "4c8705dbe4ad7d7e51b2876c5b9eea0ef916ba28" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/zendframework/zend-json/zipball/4c8705dbe4ad7d7e51b2876c5b9eea0ef916ba28", + "url": "https://api.github.com/repos/laminas/laminas-json/zipball/4c8705dbe4ad7d7e51b2876c5b9eea0ef916ba28", "reference": "4c8705dbe4ad7d7e51b2876c5b9eea0ef916ba28", "shasum": "" }, @@ -13444,16 +13325,16 @@ "require-dev": { "fabpot/php-cs-fixer": "1.7.*", "phpunit/phpunit": "~4.0", - "zendframework/zend-http": "^2.5.4", - "zendframework/zend-server": "^2.6.1", - "zendframework/zend-stdlib": "^2.5 || ^3.0", + "laminas/laminas-http": "^2.5.4", + "laminas/laminas-server": "^2.6.1", + "laminas/laminas-stdlib": "^2.5 || ^3.0", "zendframework/zendxml": "^1.0.2" }, "suggest": { - "zendframework/zend-http": "Zend\\Http component, required to use Zend\\Json\\Server", - "zendframework/zend-server": "Zend\\Server component, required to use Zend\\Json\\Server", - "zendframework/zend-stdlib": "Zend\\Stdlib component, for use with caching Zend\\Json\\Server responses", - "zendframework/zendxml": "To support Zend\\Json\\Json::fromXml() usage" + "laminas/laminas-http": "Laminas\\Http component, required to use Laminas\\Json\\Server", + "laminas/laminas-server": "Laminas\\Server component, required to use Laminas\\Json\\Server", + "laminas/laminas-stdlib": "Laminas\\Stdlib component, for use with caching Laminas\\Json\\Server responses", + "zendframework/zendxml": "To support Laminas\\Json\\Json::fromXml() usage" }, "type": "library", "extra": { @@ -13464,7 +13345,7 @@ }, "autoload": { "psr-4": { - "Zend\\Json\\": "src/" + "Laminas\\Json\\": "src/" } }, "notification-url": "https://packagist.org/downloads/", @@ -13472,7 +13353,7 @@ "BSD-3-Clause" ], "description": "provides convenience methods for serializing native PHP to JSON and decoding JSON to native PHP", - "homepage": "https://github.com/zendframework/zend-json", + "homepage": "https://github.com/laminas/laminas-json", "keywords": [ "json", "zf2" @@ -13480,16 +13361,16 @@ "time": "2016-02-04T21:20:26+00:00" }, { - "name": "zendframework/zend-loader", + "name": "laminas/laminas-loader", "version": "2.6.0", "source": { "type": "git", - "url": "https://github.com/zendframework/zend-loader.git", + "url": "https://github.com/laminas/laminas-loader.git", "reference": "78f11749ea340f6ca316bca5958eef80b38f9b6c" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/zendframework/zend-loader/zipball/78f11749ea340f6ca316bca5958eef80b38f9b6c", + "url": "https://api.github.com/repos/laminas/laminas-loader/zipball/78f11749ea340f6ca316bca5958eef80b38f9b6c", "reference": "78f11749ea340f6ca316bca5958eef80b38f9b6c", "shasum": "" }, @@ -13498,7 +13379,7 @@ }, "require-dev": { "phpunit/phpunit": "^5.7.27 || ^6.5.8 || ^7.1.4", - "zendframework/zend-coding-standard": "~1.0.0" + "laminas/laminas-coding-standard": "~1.0.0" }, "type": "library", "extra": { @@ -13509,7 +13390,7 @@ }, "autoload": { "psr-4": { - "Zend\\Loader\\": "src/" + "Laminas\\Loader\\": "src/" } }, "notification-url": "https://packagist.org/downloads/", @@ -13525,24 +13406,24 @@ "time": "2018-04-30T15:20:54+00:00" }, { - "name": "zendframework/zend-log", + "name": "laminas/laminas-log", "version": "2.10.0", "source": { "type": "git", - "url": "https://github.com/zendframework/zend-log.git", + "url": "https://github.com/laminas/laminas-log.git", "reference": "9cec3b092acb39963659c2f32441cccc56b3f430" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/zendframework/zend-log/zipball/9cec3b092acb39963659c2f32441cccc56b3f430", + "url": "https://api.github.com/repos/laminas/laminas-log/zipball/9cec3b092acb39963659c2f32441cccc56b3f430", "reference": "9cec3b092acb39963659c2f32441cccc56b3f430", "shasum": "" }, "require": { "php": "^5.6 || ^7.0", "psr/log": "^1.0", - "zendframework/zend-servicemanager": "^2.7.5 || ^3.0.3", - "zendframework/zend-stdlib": "^2.7 || ^3.0" + "laminas/laminas-servicemanager": "^2.7.5 || ^3.0.3", + "laminas/laminas-stdlib": "^2.7 || ^3.0" }, "provide": { "psr/log-implementation": "1.0.0" @@ -13550,21 +13431,21 @@ "require-dev": { "mikey179/vfsstream": "^1.6", "phpunit/phpunit": "^5.7.15 || ^6.0.8", - "zendframework/zend-coding-standard": "~1.0.0", - "zendframework/zend-db": "^2.6", - "zendframework/zend-escaper": "^2.5", - "zendframework/zend-filter": "^2.5", - "zendframework/zend-mail": "^2.6.1", - "zendframework/zend-validator": "^2.10.1" + "laminas/laminas-coding-standard": "~1.0.0", + "laminas/laminas-db": "^2.6", + "laminas/laminas-escaper": "^2.5", + "laminas/laminas-filter": "^2.5", + "laminas/laminas-mail": "^2.6.1", + "laminas/laminas-validator": "^2.10.1" }, "suggest": { "ext-mongo": "mongo extension to use Mongo writer", "ext-mongodb": "mongodb extension to use MongoDB writer", - "zendframework/zend-console": "Zend\\Console component to use the RequestID log processor", - "zendframework/zend-db": "Zend\\Db component to use the database log writer", - "zendframework/zend-escaper": "Zend\\Escaper component, for use in the XML log formatter", - "zendframework/zend-mail": "Zend\\Mail component to use the email log writer", - "zendframework/zend-validator": "Zend\\Validator component to block invalid log messages" + "laminas/laminas-console": "Laminas\\Console component to use the RequestID log processor", + "laminas/laminas-db": "Laminas\\Db component to use the database log writer", + "laminas/laminas-escaper": "Laminas\\Escaper component, for use in the XML log formatter", + "laminas/laminas-mail": "Laminas\\Mail component to use the email log writer", + "laminas/laminas-validator": "Laminas\\Validator component to block invalid log messages" }, "type": "library", "extra": { @@ -13573,13 +13454,13 @@ "dev-develop": "2.11.x-dev" }, "zf": { - "component": "Zend\\Log", - "config-provider": "Zend\\Log\\ConfigProvider" + "component": "Laminas\\Log", + "config-provider": "Laminas\\Log\\ConfigProvider" } }, "autoload": { "psr-4": { - "Zend\\Log\\": "src/" + "Laminas\\Log\\": "src/" } }, "notification-url": "https://packagist.org/downloads/", @@ -13587,7 +13468,7 @@ "BSD-3-Clause" ], "description": "component for general purpose logging", - "homepage": "https://github.com/zendframework/zend-log", + "homepage": "https://github.com/laminas/laminas-log", "keywords": [ "log", "logging", @@ -13596,16 +13477,16 @@ "time": "2018-04-09T21:59:51+00:00" }, { - "name": "zendframework/zend-mail", + "name": "laminas/laminas-mail", "version": "2.10.0", "source": { "type": "git", - "url": "https://github.com/zendframework/zend-mail.git", + "url": "https://github.com/laminas/laminas-mail.git", "reference": "d7beb63d5f7144a21ac100072c453e63860cdab8" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/zendframework/zend-mail/zipball/d7beb63d5f7144a21ac100072c453e63860cdab8", + "url": "https://api.github.com/repos/laminas/laminas-mail/zipball/d7beb63d5f7144a21ac100072c453e63860cdab8", "reference": "d7beb63d5f7144a21ac100072c453e63860cdab8", "shasum": "" }, @@ -13613,21 +13494,21 @@ "ext-iconv": "*", "php": "^5.6 || ^7.0", "true/punycode": "^2.1", - "zendframework/zend-loader": "^2.5", - "zendframework/zend-mime": "^2.5", - "zendframework/zend-stdlib": "^2.7 || ^3.0", - "zendframework/zend-validator": "^2.10.2" + "laminas/laminas-loader": "^2.5", + "laminas/laminas-mime": "^2.5", + "laminas/laminas-stdlib": "^2.7 || ^3.0", + "laminas/laminas-validator": "^2.10.2" }, "require-dev": { "phpunit/phpunit": "^5.7.25 || ^6.4.4 || ^7.1.4", - "zendframework/zend-coding-standard": "~1.0.0", - "zendframework/zend-config": "^2.6", - "zendframework/zend-crypt": "^2.6 || ^3.0", - "zendframework/zend-servicemanager": "^2.7.10 || ^3.3.1" + "laminas/laminas-coding-standard": "~1.0.0", + "laminas/laminas-config": "^2.6", + "laminas/laminas-crypt": "^2.6 || ^3.0", + "laminas/laminas-servicemanager": "^2.7.10 || ^3.3.1" }, "suggest": { - "zendframework/zend-crypt": "Crammd5 support in SMTP Auth", - "zendframework/zend-servicemanager": "^2.7.10 || ^3.3.1 when using SMTP to deliver messages" + "laminas/laminas-crypt": "Crammd5 support in SMTP Auth", + "laminas/laminas-servicemanager": "^2.7.10 || ^3.3.1 when using SMTP to deliver messages" }, "type": "library", "extra": { @@ -13636,13 +13517,13 @@ "dev-develop": "2.11.x-dev" }, "zf": { - "component": "Zend\\Mail", - "config-provider": "Zend\\Mail\\ConfigProvider" + "component": "Laminas\\Mail", + "config-provider": "Laminas\\Mail\\ConfigProvider" } }, "autoload": { "psr-4": { - "Zend\\Mail\\": "src/" + "Laminas\\Mail\\": "src/" } }, "notification-url": "https://packagist.org/downloads/", @@ -13658,16 +13539,16 @@ "time": "2018-06-07T13:37:07+00:00" }, { - "name": "zendframework/zend-math", + "name": "laminas/laminas-math", "version": "2.7.1", "source": { "type": "git", - "url": "https://github.com/zendframework/zend-math.git", + "url": "https://github.com/laminas/laminas-math.git", "reference": "1abce074004dacac1a32cd54de94ad47ef960d38" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/zendframework/zend-math/zipball/1abce074004dacac1a32cd54de94ad47ef960d38", + "url": "https://api.github.com/repos/laminas/laminas-math/zipball/1abce074004dacac1a32cd54de94ad47ef960d38", "reference": "1abce074004dacac1a32cd54de94ad47ef960d38", "shasum": "" }, @@ -13682,7 +13563,7 @@ "suggest": { "ext-bcmath": "If using the bcmath functionality", "ext-gmp": "If using the gmp functionality", - "ircmaxell/random-lib": "Fallback random byte generator for Zend\\Math\\Rand if Mcrypt extensions is unavailable" + "ircmaxell/random-lib": "Fallback random byte generator for Laminas\\Math\\Rand if Mcrypt extensions is unavailable" }, "type": "library", "extra": { @@ -13693,14 +13574,14 @@ }, "autoload": { "psr-4": { - "Zend\\Math\\": "src/" + "Laminas\\Math\\": "src/" } }, "notification-url": "https://packagist.org/downloads/", "license": [ "BSD-3-Clause" ], - "homepage": "https://github.com/zendframework/zend-math", + "homepage": "https://github.com/laminas/laminas-math", "keywords": [ "math", "zf2" @@ -13708,30 +13589,30 @@ "time": "2018-12-04T15:34:17+00:00" }, { - "name": "zendframework/zend-mime", + "name": "laminas/laminas-mime", "version": "2.7.1", "source": { "type": "git", - "url": "https://github.com/zendframework/zend-mime.git", + "url": "https://github.com/laminas/laminas-mime.git", "reference": "52ae5fa9f12845cae749271034a2d594f0e4c6f2" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/zendframework/zend-mime/zipball/52ae5fa9f12845cae749271034a2d594f0e4c6f2", + "url": "https://api.github.com/repos/laminas/laminas-mime/zipball/52ae5fa9f12845cae749271034a2d594f0e4c6f2", "reference": "52ae5fa9f12845cae749271034a2d594f0e4c6f2", "shasum": "" }, "require": { "php": "^5.6 || ^7.0", - "zendframework/zend-stdlib": "^2.7 || ^3.0" + "laminas/laminas-stdlib": "^2.7 || ^3.0" }, "require-dev": { "phpunit/phpunit": "^5.7.21 || ^6.3", - "zendframework/zend-coding-standard": "~1.0.0", - "zendframework/zend-mail": "^2.6" + "laminas/laminas-coding-standard": "~1.0.0", + "laminas/laminas-mail": "^2.6" }, "suggest": { - "zendframework/zend-mail": "Zend\\Mail component" + "laminas/laminas-mail": "Laminas\\Mail component" }, "type": "library", "extra": { @@ -13742,7 +13623,7 @@ }, "autoload": { "psr-4": { - "Zend\\Mime\\": "src/" + "Laminas\\Mime\\": "src/" } }, "notification-url": "https://packagist.org/downloads/", @@ -13750,7 +13631,7 @@ "BSD-3-Clause" ], "description": "Create and parse MIME messages and parts", - "homepage": "https://github.com/zendframework/zend-mime", + "homepage": "https://github.com/laminas/laminas-mime", "keywords": [ "ZendFramework", "mime", @@ -13759,39 +13640,39 @@ "time": "2018-05-14T19:02:50+00:00" }, { - "name": "zendframework/zend-modulemanager", + "name": "laminas/laminas-modulemanager", "version": "2.8.2", "source": { "type": "git", - "url": "https://github.com/zendframework/zend-modulemanager.git", + "url": "https://github.com/laminas/laminas-modulemanager.git", "reference": "394df6e12248ac430a312d4693f793ee7120baa6" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/zendframework/zend-modulemanager/zipball/394df6e12248ac430a312d4693f793ee7120baa6", + "url": "https://api.github.com/repos/laminas/laminas-modulemanager/zipball/394df6e12248ac430a312d4693f793ee7120baa6", "reference": "394df6e12248ac430a312d4693f793ee7120baa6", "shasum": "" }, "require": { "php": "^5.6 || ^7.0", - "zendframework/zend-config": "^3.1 || ^2.6", - "zendframework/zend-eventmanager": "^3.2 || ^2.6.3", - "zendframework/zend-stdlib": "^3.1 || ^2.7" + "laminas/laminas-config": "^3.1 || ^2.6", + "laminas/laminas-eventmanager": "^3.2 || ^2.6.3", + "laminas/laminas-stdlib": "^3.1 || ^2.7" }, "require-dev": { "phpunit/phpunit": "^6.0.8 || ^5.7.15", - "zendframework/zend-coding-standard": "~1.0.0", - "zendframework/zend-console": "^2.6", - "zendframework/zend-di": "^2.6", - "zendframework/zend-loader": "^2.5", - "zendframework/zend-mvc": "^3.0 || ^2.7", - "zendframework/zend-servicemanager": "^3.0.3 || ^2.7.5" + "laminas/laminas-coding-standard": "~1.0.0", + "laminas/laminas-console": "^2.6", + "laminas/laminas-di": "^2.6", + "laminas/laminas-loader": "^2.5", + "laminas/laminas-mvc": "^3.0 || ^2.7", + "laminas/laminas-servicemanager": "^3.0.3 || ^2.7.5" }, "suggest": { - "zendframework/zend-console": "Zend\\Console component", - "zendframework/zend-loader": "Zend\\Loader component if you are not using Composer autoloading for your modules", - "zendframework/zend-mvc": "Zend\\Mvc component", - "zendframework/zend-servicemanager": "Zend\\ServiceManager component" + "laminas/laminas-console": "Laminas\\Console component", + "laminas/laminas-loader": "Laminas\\Loader component if you are not using Composer autoloading for your modules", + "laminas/laminas-mvc": "Laminas\\Mvc component", + "laminas/laminas-servicemanager": "Laminas\\ServiceManager component" }, "type": "library", "extra": { @@ -13802,7 +13683,7 @@ }, "autoload": { "psr-4": { - "Zend\\ModuleManager\\": "src/" + "Laminas\\ModuleManager\\": "src/" } }, "notification-url": "https://packagist.org/downloads/", @@ -13810,7 +13691,7 @@ "BSD-3-Clause" ], "description": "Modular application system for zend-mvc applications", - "homepage": "https://github.com/zendframework/zend-modulemanager", + "homepage": "https://github.com/laminas/laminas-modulemanager", "keywords": [ "ZendFramework", "modulemanager", @@ -13819,73 +13700,73 @@ "time": "2017-12-02T06:11:18+00:00" }, { - "name": "zendframework/zend-mvc", + "name": "laminas/laminas-mvc", "version": "2.7.15", "source": { "type": "git", - "url": "https://github.com/zendframework/zend-mvc.git", + "url": "https://github.com/laminas/laminas-mvc.git", "reference": "a8d45689d37a9e4ff4b75ea0b7478fa3d4f9c089" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/zendframework/zend-mvc/zipball/a8d45689d37a9e4ff4b75ea0b7478fa3d4f9c089", + "url": "https://api.github.com/repos/laminas/laminas-mvc/zipball/a8d45689d37a9e4ff4b75ea0b7478fa3d4f9c089", "reference": "a8d45689d37a9e4ff4b75ea0b7478fa3d4f9c089", "shasum": "" }, "require": { "container-interop/container-interop": "^1.1", "php": "^5.5 || ^7.0", - "zendframework/zend-console": "^2.7", - "zendframework/zend-eventmanager": "^2.6.4 || ^3.0", - "zendframework/zend-form": "^2.11", - "zendframework/zend-hydrator": "^1.1 || ^2.4", - "zendframework/zend-psr7bridge": "^0.2", - "zendframework/zend-servicemanager": "^2.7.10 || ^3.0.3", - "zendframework/zend-stdlib": "^2.7.5 || ^3.0" + "laminas/laminas-console": "^2.7", + "laminas/laminas-eventmanager": "^2.6.4 || ^3.0", + "laminas/laminas-form": "^2.11", + "laminas/laminas-hydrator": "^1.1 || ^2.4", + "laminas/laminas-psr7bridge": "^0.2", + "laminas/laminas-servicemanager": "^2.7.10 || ^3.0.3", + "laminas/laminas-stdlib": "^2.7.5 || ^3.0" }, "replace": { - "zendframework/zend-router": "^2.0" + "laminas/laminas-router": "^2.0" }, "require-dev": { "friendsofphp/php-cs-fixer": "1.7.*", "phpunit/phpunit": "^4.8.36", "sebastian/comparator": "^1.2.4", "sebastian/version": "^1.0.4", - "zendframework/zend-authentication": "^2.6", - "zendframework/zend-cache": "^2.8", - "zendframework/zend-di": "^2.6", - "zendframework/zend-filter": "^2.8", - "zendframework/zend-http": "^2.8", - "zendframework/zend-i18n": "^2.8", - "zendframework/zend-inputfilter": "^2.8", - "zendframework/zend-json": "^2.6.1", - "zendframework/zend-log": "^2.9.3", - "zendframework/zend-modulemanager": "^2.8", - "zendframework/zend-serializer": "^2.8", - "zendframework/zend-session": "^2.8.1", - "zendframework/zend-text": "^2.7", - "zendframework/zend-uri": "^2.6", - "zendframework/zend-validator": "^2.10", - "zendframework/zend-view": "^2.9" + "laminas/laminas-authentication": "^2.6", + "laminas/laminas-cache": "^2.8", + "laminas/laminas-di": "^2.6", + "laminas/laminas-filter": "^2.8", + "laminas/laminas-http": "^2.8", + "laminas/laminas-i18n": "^2.8", + "laminas/laminas-inputfilter": "^2.8", + "laminas/laminas-json": "^2.6.1", + "laminas/laminas-log": "^2.9.3", + "laminas/laminas-modulemanager": "^2.8", + "laminas/laminas-serializer": "^2.8", + "laminas/laminas-session": "^2.8.1", + "laminas/laminas-text": "^2.7", + "laminas/laminas-uri": "^2.6", + "laminas/laminas-validator": "^2.10", + "laminas/laminas-view": "^2.9" }, "suggest": { - "zendframework/zend-authentication": "Zend\\Authentication component for Identity plugin", - "zendframework/zend-config": "Zend\\Config component", - "zendframework/zend-di": "Zend\\Di component", - "zendframework/zend-filter": "Zend\\Filter component", - "zendframework/zend-http": "Zend\\Http component", - "zendframework/zend-i18n": "Zend\\I18n component for translatable segments", - "zendframework/zend-inputfilter": "Zend\\Inputfilter component", - "zendframework/zend-json": "Zend\\Json component", - "zendframework/zend-log": "Zend\\Log component", - "zendframework/zend-modulemanager": "Zend\\ModuleManager component", - "zendframework/zend-serializer": "Zend\\Serializer component", - "zendframework/zend-servicemanager-di": "^1.0.1, if using zend-servicemanager v3 and requiring the zend-di integration", - "zendframework/zend-session": "Zend\\Session component for FlashMessenger, PRG, and FPRG plugins", - "zendframework/zend-text": "Zend\\Text component", - "zendframework/zend-uri": "Zend\\Uri component", - "zendframework/zend-validator": "Zend\\Validator component", - "zendframework/zend-view": "Zend\\View component" + "laminas/laminas-authentication": "Laminas\\Authentication component for Identity plugin", + "laminas/laminas-config": "Laminas\\Config component", + "laminas/laminas-di": "Laminas\\Di component", + "laminas/laminas-filter": "Laminas\\Filter component", + "laminas/laminas-http": "Laminas\\Http component", + "laminas/laminas-i18n": "Laminas\\I18n component for translatable segments", + "laminas/laminas-inputfilter": "Zend\\Inputfilter component", + "laminas/laminas-json": "Laminas\\Json component", + "laminas/laminas-log": "Laminas\\Log component", + "laminas/laminas-modulemanager": "Laminas\\ModuleManager component", + "laminas/laminas-serializer": "Laminas\\Serializer component", + "laminas/laminas-servicemanager-di": "^1.0.1, if using zend-servicemanager v3 and requiring the zend-di integration", + "laminas/laminas-session": "Laminas\\Session component for FlashMessenger, PRG, and FPRG plugins", + "laminas/laminas-text": "Laminas\\Text component", + "laminas/laminas-uri": "Laminas\\Uri component", + "laminas/laminas-validator": "Laminas\\Validator component", + "laminas/laminas-view": "Laminas\\View component" }, "type": "library", "extra": { @@ -13899,14 +13780,14 @@ "src/autoload.php" ], "psr-4": { - "Zend\\Mvc\\": "src/" + "Laminas\\Mvc\\": "src/" } }, "notification-url": "https://packagist.org/downloads/", "license": [ "BSD-3-Clause" ], - "homepage": "https://github.com/zendframework/zend-mvc", + "homepage": "https://github.com/laminas/laminas-mvc", "keywords": [ "mvc", "zf2" @@ -13914,24 +13795,24 @@ "time": "2018-05-03T13:13:41+00:00" }, { - "name": "zendframework/zend-psr7bridge", + "name": "laminas/laminas-psr7bridge", "version": "0.2.2", "source": { "type": "git", - "url": "https://github.com/zendframework/zend-psr7bridge.git", + "url": "https://github.com/laminas/laminas-psr7bridge.git", "reference": "86c0b53b0c6381391c4add4a93a56e51d5c74605" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/zendframework/zend-psr7bridge/zipball/86c0b53b0c6381391c4add4a93a56e51d5c74605", + "url": "https://api.github.com/repos/laminas/laminas-psr7bridge/zipball/86c0b53b0c6381391c4add4a93a56e51d5c74605", "reference": "86c0b53b0c6381391c4add4a93a56e51d5c74605", "shasum": "" }, "require": { "php": ">=5.5", "psr/http-message": "^1.0", - "zendframework/zend-diactoros": "^1.1", - "zendframework/zend-http": "^2.5" + "laminas/laminas-diactoros": "^1.1", + "laminas/laminas-http": "^2.5" }, "require-dev": { "phpunit/phpunit": "^4.7", @@ -13946,15 +13827,15 @@ }, "autoload": { "psr-4": { - "Zend\\Psr7Bridge\\": "src/" + "Laminas\\Psr7Bridge\\": "src/" } }, "notification-url": "https://packagist.org/downloads/", "license": [ "BSD-3-Clause" ], - "description": "PSR-7 <-> Zend\\Http bridge", - "homepage": "https://github.com/zendframework/zend-psr7bridge", + "description": "PSR-7 <-> Laminas\\Http bridge", + "homepage": "https://github.com/laminas/laminas-psr7bridge", "keywords": [ "http", "psr", @@ -13963,33 +13844,33 @@ "time": "2016-05-10T21:44:39+00:00" }, { - "name": "zendframework/zend-serializer", + "name": "laminas/laminas-serializer", "version": "2.9.0", "source": { "type": "git", - "url": "https://github.com/zendframework/zend-serializer.git", + "url": "https://github.com/laminas/laminas-serializer.git", "reference": "0172690db48d8935edaf625c4cba38b79719892c" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/zendframework/zend-serializer/zipball/0172690db48d8935edaf625c4cba38b79719892c", + "url": "https://api.github.com/repos/laminas/laminas-serializer/zipball/0172690db48d8935edaf625c4cba38b79719892c", "reference": "0172690db48d8935edaf625c4cba38b79719892c", "shasum": "" }, "require": { "php": "^5.6 || ^7.0", - "zendframework/zend-json": "^2.5 || ^3.0", - "zendframework/zend-stdlib": "^2.7 || ^3.0" + "laminas/laminas-json": "^2.5 || ^3.0", + "laminas/laminas-stdlib": "^2.7 || ^3.0" }, "require-dev": { "phpunit/phpunit": "^5.7.25 || ^6.4.4", - "zendframework/zend-coding-standard": "~1.0.0", - "zendframework/zend-math": "^2.6 || ^3.0", - "zendframework/zend-servicemanager": "^2.7.5 || ^3.0.3" + "laminas/laminas-coding-standard": "~1.0.0", + "laminas/laminas-math": "^2.6 || ^3.0", + "laminas/laminas-servicemanager": "^2.7.5 || ^3.0.3" }, "suggest": { - "zendframework/zend-math": "(^2.6 || ^3.0) To support Python Pickle serialization", - "zendframework/zend-servicemanager": "(^2.7.5 || ^3.0.3) To support plugin manager support" + "laminas/laminas-math": "(^2.6 || ^3.0) To support Python Pickle serialization", + "laminas/laminas-servicemanager": "(^2.7.5 || ^3.0.3) To support plugin manager support" }, "type": "library", "extra": { @@ -13998,13 +13879,13 @@ "dev-develop": "2.10.x-dev" }, "zf": { - "component": "Zend\\Serializer", - "config-provider": "Zend\\Serializer\\ConfigProvider" + "component": "Laminas\\Serializer", + "config-provider": "Laminas\\Serializer\\ConfigProvider" } }, "autoload": { "psr-4": { - "Zend\\Serializer\\": "src/" + "Laminas\\Serializer\\": "src/" } }, "notification-url": "https://packagist.org/downloads/", @@ -14020,27 +13901,27 @@ "time": "2018-05-14T18:45:18+00:00" }, { - "name": "zendframework/zend-server", + "name": "laminas/laminas-server", "version": "2.8.0", "source": { "type": "git", - "url": "https://github.com/zendframework/zend-server.git", + "url": "https://github.com/laminas/laminas-server.git", "reference": "23a2e9a5599c83c05da831cb7c649e8a7809595e" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/zendframework/zend-server/zipball/23a2e9a5599c83c05da831cb7c649e8a7809595e", + "url": "https://api.github.com/repos/laminas/laminas-server/zipball/23a2e9a5599c83c05da831cb7c649e8a7809595e", "reference": "23a2e9a5599c83c05da831cb7c649e8a7809595e", "shasum": "" }, "require": { "php": "^5.6 || ^7.0", - "zendframework/zend-code": "^2.5 || ^3.0", - "zendframework/zend-stdlib": "^2.5 || ^3.0" + "laminas/laminas-code": "^2.5 || ^3.0", + "laminas/laminas-stdlib": "^2.5 || ^3.0" }, "require-dev": { "phpunit/phpunit": "^5.7.27 || ^6.5.8 || ^7.1.4", - "zendframework/zend-coding-standard": "~1.0.0" + "laminas/laminas-coding-standard": "~1.0.0" }, "type": "library", "extra": { @@ -14051,7 +13932,7 @@ }, "autoload": { "psr-4": { - "Zend\\Server\\": "src/" + "Laminas\\Server\\": "src/" } }, "notification-url": "https://packagist.org/downloads/", @@ -14067,16 +13948,16 @@ "time": "2018-04-30T22:21:28+00:00" }, { - "name": "zendframework/zend-servicemanager", + "name": "laminas/laminas-servicemanager", "version": "2.7.11", "source": { "type": "git", - "url": "https://github.com/zendframework/zend-servicemanager.git", + "url": "https://github.com/laminas/laminas-servicemanager.git", "reference": "99ec9ed5d0f15aed9876433c74c2709eb933d4c7" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/zendframework/zend-servicemanager/zipball/99ec9ed5d0f15aed9876433c74c2709eb933d4c7", + "url": "https://api.github.com/repos/laminas/laminas-servicemanager/zipball/99ec9ed5d0f15aed9876433c74c2709eb933d4c7", "reference": "99ec9ed5d0f15aed9876433c74c2709eb933d4c7", "shasum": "" }, @@ -14088,12 +13969,12 @@ "athletic/athletic": "dev-master", "fabpot/php-cs-fixer": "1.7.*", "phpunit/phpunit": "~4.0", - "zendframework/zend-di": "~2.5", - "zendframework/zend-mvc": "~2.5" + "laminas/laminas-di": "~2.5", + "laminas/laminas-mvc": "~2.5" }, "suggest": { "ocramius/proxy-manager": "ProxyManager 0.5.* to handle lazy initialization of services", - "zendframework/zend-di": "Zend\\Di component" + "laminas/laminas-di": "Laminas\\Di component" }, "type": "library", "extra": { @@ -14104,14 +13985,14 @@ }, "autoload": { "psr-4": { - "Zend\\ServiceManager\\": "src/" + "Laminas\\ServiceManager\\": "src/" } }, "notification-url": "https://packagist.org/downloads/", "license": [ "BSD-3-Clause" ], - "homepage": "https://github.com/zendframework/zend-servicemanager", + "homepage": "https://github.com/laminas/laminas-servicemanager", "keywords": [ "servicemanager", "zf2" @@ -14119,43 +14000,43 @@ "time": "2018-06-22T14:49:54+00:00" }, { - "name": "zendframework/zend-session", + "name": "laminas/laminas-session", "version": "2.8.5", "source": { "type": "git", - "url": "https://github.com/zendframework/zend-session.git", + "url": "https://github.com/laminas/laminas-session.git", "reference": "2cfd90e1a2f6b066b9f908599251d8f64f07021b" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/zendframework/zend-session/zipball/2cfd90e1a2f6b066b9f908599251d8f64f07021b", + "url": "https://api.github.com/repos/laminas/laminas-session/zipball/2cfd90e1a2f6b066b9f908599251d8f64f07021b", "reference": "2cfd90e1a2f6b066b9f908599251d8f64f07021b", "shasum": "" }, "require": { "php": "^5.6 || ^7.0", - "zendframework/zend-eventmanager": "^2.6.2 || ^3.0", - "zendframework/zend-stdlib": "^2.7 || ^3.0" + "laminas/laminas-eventmanager": "^2.6.2 || ^3.0", + "laminas/laminas-stdlib": "^2.7 || ^3.0" }, "require-dev": { "container-interop/container-interop": "^1.1", "mongodb/mongodb": "^1.0.1", "php-mock/php-mock-phpunit": "^1.1.2 || ^2.0", "phpunit/phpunit": "^5.7.5 || >=6.0.13 <6.5.0", - "zendframework/zend-cache": "^2.6.1", - "zendframework/zend-coding-standard": "~1.0.0", - "zendframework/zend-db": "^2.7", - "zendframework/zend-http": "^2.5.4", - "zendframework/zend-servicemanager": "^2.7.5 || ^3.0.3", - "zendframework/zend-validator": "^2.6" + "laminas/laminas-cache": "^2.6.1", + "laminas/laminas-coding-standard": "~1.0.0", + "laminas/laminas-db": "^2.7", + "laminas/laminas-http": "^2.5.4", + "laminas/laminas-servicemanager": "^2.7.5 || ^3.0.3", + "laminas/laminas-validator": "^2.6" }, "suggest": { "mongodb/mongodb": "If you want to use the MongoDB session save handler", - "zendframework/zend-cache": "Zend\\Cache component", - "zendframework/zend-db": "Zend\\Db component", - "zendframework/zend-http": "Zend\\Http component", - "zendframework/zend-servicemanager": "Zend\\ServiceManager component", - "zendframework/zend-validator": "Zend\\Validator component" + "laminas/laminas-cache": "Laminas\\Cache component", + "laminas/laminas-db": "Laminas\\Db component", + "laminas/laminas-http": "Laminas\\Http component", + "laminas/laminas-servicemanager": "Laminas\\ServiceManager component", + "laminas/laminas-validator": "Laminas\\Validator component" }, "type": "library", "extra": { @@ -14164,13 +14045,13 @@ "dev-develop": "2.9-dev" }, "zf": { - "component": "Zend\\Session", - "config-provider": "Zend\\Session\\ConfigProvider" + "component": "Laminas\\Session", + "config-provider": "Laminas\\Session\\ConfigProvider" } }, "autoload": { "psr-4": { - "Zend\\Session\\": "src/" + "Laminas\\Session\\": "src/" } }, "notification-url": "https://packagist.org/downloads/", @@ -14186,34 +14067,34 @@ "time": "2018-02-22T16:33:54+00:00" }, { - "name": "zendframework/zend-soap", + "name": "laminas/laminas-soap", "version": "2.7.0", "source": { "type": "git", - "url": "https://github.com/zendframework/zend-soap.git", + "url": "https://github.com/laminas/laminas-soap.git", "reference": "af03c32f0db2b899b3df8cfe29aeb2b49857d284" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/zendframework/zend-soap/zipball/af03c32f0db2b899b3df8cfe29aeb2b49857d284", + "url": "https://api.github.com/repos/laminas/laminas-soap/zipball/af03c32f0db2b899b3df8cfe29aeb2b49857d284", "reference": "af03c32f0db2b899b3df8cfe29aeb2b49857d284", "shasum": "" }, "require": { "ext-soap": "*", "php": "^5.6 || ^7.0", - "zendframework/zend-server": "^2.6.1", - "zendframework/zend-stdlib": "^2.7 || ^3.0", - "zendframework/zend-uri": "^2.5.2" + "laminas/laminas-server": "^2.6.1", + "laminas/laminas-stdlib": "^2.7 || ^3.0", + "laminas/laminas-uri": "^2.5.2" }, "require-dev": { "phpunit/phpunit": "^5.7.21 || ^6.3", - "zendframework/zend-coding-standard": "~1.0.0", - "zendframework/zend-config": "^2.6", - "zendframework/zend-http": "^2.5.4" + "laminas/laminas-coding-standard": "~1.0.0", + "laminas/laminas-config": "^2.6", + "laminas/laminas-http": "^2.5.4" }, "suggest": { - "zendframework/zend-http": "Zend\\Http component" + "laminas/laminas-http": "Laminas\\Http component" }, "type": "library", "extra": { @@ -14224,14 +14105,14 @@ }, "autoload": { "psr-4": { - "Zend\\Soap\\": "src/" + "Laminas\\Soap\\": "src/" } }, "notification-url": "https://packagist.org/downloads/", "license": [ "BSD-3-Clause" ], - "homepage": "https://github.com/zendframework/zend-soap", + "homepage": "https://github.com/laminas/laminas-soap", "keywords": [ "soap", "zf2" @@ -14239,39 +14120,39 @@ "time": "2018-01-29T17:51:26+00:00" }, { - "name": "zendframework/zend-stdlib", + "name": "laminas/laminas-stdlib", "version": "2.7.7", "source": { "type": "git", - "url": "https://github.com/zendframework/zend-stdlib.git", + "url": "https://github.com/laminas/laminas-stdlib.git", "reference": "0e44eb46788f65e09e077eb7f44d2659143bcc1f" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/zendframework/zend-stdlib/zipball/0e44eb46788f65e09e077eb7f44d2659143bcc1f", + "url": "https://api.github.com/repos/laminas/laminas-stdlib/zipball/0e44eb46788f65e09e077eb7f44d2659143bcc1f", "reference": "0e44eb46788f65e09e077eb7f44d2659143bcc1f", "shasum": "" }, "require": { "php": "^5.5 || ^7.0", - "zendframework/zend-hydrator": "~1.1" + "laminas/laminas-hydrator": "~1.1" }, "require-dev": { "athletic/athletic": "~0.1", "fabpot/php-cs-fixer": "1.7.*", "phpunit/phpunit": "~4.0", - "zendframework/zend-config": "~2.5", - "zendframework/zend-eventmanager": "~2.5", - "zendframework/zend-filter": "~2.5", - "zendframework/zend-inputfilter": "~2.5", - "zendframework/zend-serializer": "~2.5", - "zendframework/zend-servicemanager": "~2.5" + "laminas/laminas-config": "~2.5", + "laminas/laminas-eventmanager": "~2.5", + "laminas/laminas-filter": "~2.5", + "laminas/laminas-inputfilter": "~2.5", + "laminas/laminas-serializer": "~2.5", + "laminas/laminas-servicemanager": "~2.5" }, "suggest": { - "zendframework/zend-eventmanager": "To support aggregate hydrator usage", - "zendframework/zend-filter": "To support naming strategy hydrator usage", - "zendframework/zend-serializer": "Zend\\Serializer component", - "zendframework/zend-servicemanager": "To support hydrator plugin manager usage" + "laminas/laminas-eventmanager": "To support aggregate hydrator usage", + "laminas/laminas-filter": "To support naming strategy hydrator usage", + "laminas/laminas-serializer": "Laminas\\Serializer component", + "laminas/laminas-servicemanager": "To support hydrator plugin manager usage" }, "type": "library", "extra": { @@ -14283,14 +14164,14 @@ }, "autoload": { "psr-4": { - "Zend\\Stdlib\\": "src/" + "Laminas\\Stdlib\\": "src/" } }, "notification-url": "https://packagist.org/downloads/", "license": [ "BSD-3-Clause" ], - "homepage": "https://github.com/zendframework/zend-stdlib", + "homepage": "https://github.com/laminas/laminas-stdlib", "keywords": [ "stdlib", "zf2" @@ -14298,28 +14179,28 @@ "time": "2016-04-12T21:17:31+00:00" }, { - "name": "zendframework/zend-text", + "name": "laminas/laminas-text", "version": "2.7.0", "source": { "type": "git", - "url": "https://github.com/zendframework/zend-text.git", + "url": "https://github.com/laminas/laminas-text.git", "reference": "ca987dd4594f5f9508771fccd82c89bc7fbb39ac" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/zendframework/zend-text/zipball/ca987dd4594f5f9508771fccd82c89bc7fbb39ac", + "url": "https://api.github.com/repos/laminas/laminas-text/zipball/ca987dd4594f5f9508771fccd82c89bc7fbb39ac", "reference": "ca987dd4594f5f9508771fccd82c89bc7fbb39ac", "shasum": "" }, "require": { "php": "^5.6 || ^7.0", - "zendframework/zend-servicemanager": "^2.7.5 || ^3.0.3", - "zendframework/zend-stdlib": "^2.7 || ^3.0" + "laminas/laminas-servicemanager": "^2.7.5 || ^3.0.3", + "laminas/laminas-stdlib": "^2.7 || ^3.0" }, "require-dev": { "phpunit/phpunit": "^5.7.27 || ^6.5.8 || ^7.1.4", - "zendframework/zend-coding-standard": "~1.0.0", - "zendframework/zend-config": "^2.6" + "laminas/laminas-coding-standard": "~1.0.0", + "laminas/laminas-config": "^2.6" }, "type": "library", "extra": { @@ -14330,7 +14211,7 @@ }, "autoload": { "psr-4": { - "Zend\\Text\\": "src/" + "Laminas\\Text\\": "src/" } }, "notification-url": "https://packagist.org/downloads/", @@ -14346,27 +14227,27 @@ "time": "2018-04-30T14:55:10+00:00" }, { - "name": "zendframework/zend-uri", + "name": "laminas/laminas-uri", "version": "2.7.0", "source": { "type": "git", - "url": "https://github.com/zendframework/zend-uri.git", + "url": "https://github.com/laminas/laminas-uri.git", "reference": "b2785cd38fe379a784645449db86f21b7739b1ee" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/zendframework/zend-uri/zipball/b2785cd38fe379a784645449db86f21b7739b1ee", + "url": "https://api.github.com/repos/laminas/laminas-uri/zipball/b2785cd38fe379a784645449db86f21b7739b1ee", "reference": "b2785cd38fe379a784645449db86f21b7739b1ee", "shasum": "" }, "require": { "php": "^5.6 || ^7.0", - "zendframework/zend-escaper": "^2.5", - "zendframework/zend-validator": "^2.10" + "laminas/laminas-escaper": "^2.5", + "laminas/laminas-validator": "^2.10" }, "require-dev": { "phpunit/phpunit": "^5.7.27 || ^6.5.8 || ^7.1.4", - "zendframework/zend-coding-standard": "~1.0.0" + "laminas/laminas-coding-standard": "~1.0.0" }, "type": "library", "extra": { @@ -14377,7 +14258,7 @@ }, "autoload": { "psr-4": { - "Zend\\Uri\\": "src/" + "Laminas\\Uri\\": "src/" } }, "notification-url": "https://packagist.org/downloads/", @@ -14393,49 +14274,49 @@ "time": "2019-02-27T21:39:04+00:00" }, { - "name": "zendframework/zend-validator", + "name": "laminas/laminas-validator", "version": "2.11.1", "source": { "type": "git", - "url": "https://github.com/zendframework/zend-validator.git", + "url": "https://github.com/laminas/laminas-validator.git", "reference": "3c28dfe4e5951ba38059cea895244d9d206190b3" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/zendframework/zend-validator/zipball/3c28dfe4e5951ba38059cea895244d9d206190b3", + "url": "https://api.github.com/repos/laminas/laminas-validator/zipball/3c28dfe4e5951ba38059cea895244d9d206190b3", "reference": "3c28dfe4e5951ba38059cea895244d9d206190b3", "shasum": "" }, "require": { "container-interop/container-interop": "^1.1", "php": "^5.6 || ^7.0", - "zendframework/zend-stdlib": "^2.7.6 || ^3.1" + "laminas/laminas-stdlib": "^2.7.6 || ^3.1" }, "require-dev": { "phpunit/phpunit": "^6.0.8 || ^5.7.15", "psr/http-message": "^1.0", - "zendframework/zend-cache": "^2.6.1", - "zendframework/zend-coding-standard": "~1.0.0", - "zendframework/zend-config": "^2.6", - "zendframework/zend-db": "^2.7", - "zendframework/zend-filter": "^2.6", - "zendframework/zend-http": "^2.5.4", - "zendframework/zend-i18n": "^2.6", - "zendframework/zend-math": "^2.6", - "zendframework/zend-servicemanager": "^2.7.5 || ^3.0.3", - "zendframework/zend-session": "^2.8", - "zendframework/zend-uri": "^2.5" + "laminas/laminas-cache": "^2.6.1", + "laminas/laminas-coding-standard": "~1.0.0", + "laminas/laminas-config": "^2.6", + "laminas/laminas-db": "^2.7", + "laminas/laminas-filter": "^2.6", + "laminas/laminas-http": "^2.5.4", + "laminas/laminas-i18n": "^2.6", + "laminas/laminas-math": "^2.6", + "laminas/laminas-servicemanager": "^2.7.5 || ^3.0.3", + "laminas/laminas-session": "^2.8", + "laminas/laminas-uri": "^2.5" }, "suggest": { "psr/http-message": "psr/http-message, required when validating PSR-7 UploadedFileInterface instances via the Upload and UploadFile validators", - "zendframework/zend-db": "Zend\\Db component, required by the (No)RecordExists validator", - "zendframework/zend-filter": "Zend\\Filter component, required by the Digits validator", - "zendframework/zend-i18n": "Zend\\I18n component to allow translation of validation error messages", - "zendframework/zend-i18n-resources": "Translations of validator messages", - "zendframework/zend-math": "Zend\\Math component, required by the Csrf validator", - "zendframework/zend-servicemanager": "Zend\\ServiceManager component to allow using the ValidatorPluginManager and validator chains", - "zendframework/zend-session": "Zend\\Session component, ^2.8; required by the Csrf validator", - "zendframework/zend-uri": "Zend\\Uri component, required by the Uri and Sitemap\\Loc validators" + "laminas/laminas-db": "Laminas\\Db component, required by the (No)RecordExists validator", + "laminas/laminas-filter": "Laminas\\Filter component, required by the Digits validator", + "laminas/laminas-i18n": "Laminas\\I18n component to allow translation of validation error messages", + "laminas/laminas-i18n-resources": "Translations of validator messages", + "laminas/laminas-math": "Laminas\\Math component, required by the Csrf validator", + "laminas/laminas-servicemanager": "Laminas\\ServiceManager component to allow using the ValidatorPluginManager and validator chains", + "laminas/laminas-session": "Laminas\\Session component, ^2.8; required by the Csrf validator", + "laminas/laminas-uri": "Laminas\\Uri component, required by the Uri and Sitemap\\Loc validators" }, "type": "library", "extra": { @@ -14444,13 +14325,13 @@ "dev-develop": "2.12.x-dev" }, "zf": { - "component": "Zend\\Validator", - "config-provider": "Zend\\Validator\\ConfigProvider" + "component": "Laminas\\Validator", + "config-provider": "Laminas\\Validator\\ConfigProvider" } }, "autoload": { "psr-4": { - "Zend\\Validator\\": "src/" + "Laminas\\Validator\\": "src/" } }, "notification-url": "https://packagist.org/downloads/", @@ -14458,7 +14339,7 @@ "BSD-3-Clause" ], "description": "provides a set of commonly needed validators", - "homepage": "https://github.com/zendframework/zend-validator", + "homepage": "https://github.com/laminas/laminas-validator", "keywords": [ "validator", "zf2" @@ -14466,64 +14347,64 @@ "time": "2019-01-29T22:26:39+00:00" }, { - "name": "zendframework/zend-view", + "name": "laminas/laminas-view", "version": "2.10.1", "source": { "type": "git", - "url": "https://github.com/zendframework/zend-view.git", + "url": "https://github.com/laminas/laminas-view.git", "reference": "c1a3f2043fb75b5983ab9adfc369ae396601be7e" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/zendframework/zend-view/zipball/c1a3f2043fb75b5983ab9adfc369ae396601be7e", + "url": "https://api.github.com/repos/laminas/laminas-view/zipball/c1a3f2043fb75b5983ab9adfc369ae396601be7e", "reference": "c1a3f2043fb75b5983ab9adfc369ae396601be7e", "shasum": "" }, "require": { "php": "^5.6 || ^7.0", - "zendframework/zend-eventmanager": "^2.6.2 || ^3.0", - "zendframework/zend-json": "^2.6.1", - "zendframework/zend-loader": "^2.5", - "zendframework/zend-stdlib": "^2.7 || ^3.0" + "laminas/laminas-eventmanager": "^2.6.2 || ^3.0", + "laminas/laminas-json": "^2.6.1", + "laminas/laminas-loader": "^2.5", + "laminas/laminas-stdlib": "^2.7 || ^3.0" }, "require-dev": { "phpunit/phpunit": "^5.7.15 || ^6.0.8", - "zendframework/zend-authentication": "^2.5", - "zendframework/zend-cache": "^2.6.1", - "zendframework/zend-coding-standard": "~1.0.0", - "zendframework/zend-config": "^2.6", - "zendframework/zend-console": "^2.6", - "zendframework/zend-escaper": "^2.5", - "zendframework/zend-feed": "^2.7", - "zendframework/zend-filter": "^2.6.1", - "zendframework/zend-http": "^2.5.4", - "zendframework/zend-i18n": "^2.6", - "zendframework/zend-log": "^2.7", - "zendframework/zend-modulemanager": "^2.7.1", - "zendframework/zend-mvc": "^2.7.14 || ^3.0", - "zendframework/zend-navigation": "^2.5", - "zendframework/zend-paginator": "^2.5", - "zendframework/zend-permissions-acl": "^2.6", - "zendframework/zend-router": "^3.0.1", - "zendframework/zend-serializer": "^2.6.1", - "zendframework/zend-servicemanager": "^2.7.5 || ^3.0.3", - "zendframework/zend-session": "^2.8.1", - "zendframework/zend-uri": "^2.5" + "laminas/laminas-authentication": "^2.5", + "laminas/laminas-cache": "^2.6.1", + "laminas/laminas-coding-standard": "~1.0.0", + "laminas/laminas-config": "^2.6", + "laminas/laminas-console": "^2.6", + "laminas/laminas-escaper": "^2.5", + "laminas/laminas-feed": "^2.7", + "laminas/laminas-filter": "^2.6.1", + "laminas/laminas-http": "^2.5.4", + "laminas/laminas-i18n": "^2.6", + "laminas/laminas-log": "^2.7", + "laminas/laminas-modulemanager": "^2.7.1", + "laminas/laminas-mvc": "^2.7.14 || ^3.0", + "laminas/laminas-navigation": "^2.5", + "laminas/laminas-paginator": "^2.5", + "laminas/laminas-permissions-acl": "^2.6", + "laminas/laminas-router": "^3.0.1", + "laminas/laminas-serializer": "^2.6.1", + "laminas/laminas-servicemanager": "^2.7.5 || ^3.0.3", + "laminas/laminas-session": "^2.8.1", + "laminas/laminas-uri": "^2.5" }, "suggest": { - "zendframework/zend-authentication": "Zend\\Authentication component", - "zendframework/zend-escaper": "Zend\\Escaper component", - "zendframework/zend-feed": "Zend\\Feed component", - "zendframework/zend-filter": "Zend\\Filter component", - "zendframework/zend-http": "Zend\\Http component", - "zendframework/zend-i18n": "Zend\\I18n component", - "zendframework/zend-mvc": "Zend\\Mvc component", - "zendframework/zend-mvc-plugin-flashmessenger": "zend-mvc-plugin-flashmessenger component, if you want to use the FlashMessenger view helper with zend-mvc versions 3 and up", - "zendframework/zend-navigation": "Zend\\Navigation component", - "zendframework/zend-paginator": "Zend\\Paginator component", - "zendframework/zend-permissions-acl": "Zend\\Permissions\\Acl component", - "zendframework/zend-servicemanager": "Zend\\ServiceManager component", - "zendframework/zend-uri": "Zend\\Uri component" + "laminas/laminas-authentication": "Laminas\\Authentication component", + "laminas/laminas-escaper": "Laminas\\Escaper component", + "laminas/laminas-feed": "Laminas\\Feed component", + "laminas/laminas-filter": "Laminas\\Filter component", + "laminas/laminas-http": "Laminas\\Http component", + "laminas/laminas-i18n": "Laminas\\I18n component", + "laminas/laminas-mvc": "Laminas\\Mvc component", + "laminas/laminas-mvc-plugin-flashmessenger": "zend-mvc-plugin-flashmessenger component, if you want to use the FlashMessenger view helper with zend-mvc versions 3 and up", + "laminas/laminas-navigation": "Laminas\\Navigation component", + "laminas/laminas-paginator": "Laminas\\Paginator component", + "laminas/laminas-permissions-acl": "Laminas\\Permissions\\Acl component", + "laminas/laminas-servicemanager": "Laminas\\ServiceManager component", + "laminas/laminas-uri": "Laminas\\Uri component" }, "bin": [ "bin/templatemap_generator.php" @@ -14537,7 +14418,7 @@ }, "autoload": { "psr-4": { - "Zend\\View\\": "src/" + "Laminas\\View\\": "src/" } }, "notification-url": "https://packagist.org/downloads/", @@ -14545,7 +14426,7 @@ "BSD-3-Clause" ], "description": "provides a system of helpers, output filters, and variable escaping", - "homepage": "https://github.com/zendframework/zend-view", + "homepage": "https://github.com/laminas/laminas-view", "keywords": [ "view", "zf2" diff --git a/dev/tests/integration/testsuite/Magento/Framework/Composer/_files/testSkeleton/composer.lock b/dev/tests/integration/testsuite/Magento/Framework/Composer/_files/testSkeleton/composer.lock index 36a98e6cd9596..6c51c6a2072e9 100644 --- a/dev/tests/integration/testsuite/Magento/Framework/Composer/_files/testSkeleton/composer.lock +++ b/dev/tests/integration/testsuite/Magento/Framework/Composer/_files/testSkeleton/composer.lock @@ -1870,13 +1870,13 @@ "symfony/console": "~4.1.0", "symfony/process": "~4.1.0", "tedivm/jshrink": "~1.3.0", - "zendframework/zend-code": "~3.3.0", - "zendframework/zend-crypt": "^2.6.0", - "zendframework/zend-http": "^2.6.0", - "zendframework/zend-mvc": "~2.7.0", - "zendframework/zend-stdlib": "^2.7.7", - "zendframework/zend-uri": "^2.5.1", - "zendframework/zend-validator": "^2.6.0" + "laminas/laminas-code": "~3.3.0", + "laminas/laminas-crypt": "^2.6.0", + "laminas/laminas-http": "^2.6.0", + "laminas/laminas-mvc": "~2.7.0", + "laminas/laminas-stdlib": "^2.7.7", + "laminas/laminas-uri": "^2.5.1", + "laminas/laminas-validator": "^2.6.0" }, "suggest": { "ext-imagick": "Use Image Magick >=3.0.0 as an optional alternative image processing library" @@ -2335,28 +2335,28 @@ "symfony/event-dispatcher": "~4.1.0", "tedivm/jshrink": "~1.3.0", "tubalmartin/cssmin": "4.1.1", - "zendframework/zend-code": "~3.3.0", - "zendframework/zend-config": "^2.6.0", - "zendframework/zend-console": "^2.6.0", - "zendframework/zend-crypt": "^2.6.0", - "zendframework/zend-di": "^2.6.1", - "zendframework/zend-eventmanager": "^2.6.3", - "zendframework/zend-form": "^2.10.0", - "zendframework/zend-http": "^2.6.0", - "zendframework/zend-i18n": "^2.7.3", - "zendframework/zend-json": "^2.6.1", - "zendframework/zend-log": "^2.9.1", - "zendframework/zend-modulemanager": "^2.7", - "zendframework/zend-mvc": "~2.7.0", - "zendframework/zend-serializer": "^2.7.2", - "zendframework/zend-server": "^2.6.1", - "zendframework/zend-servicemanager": "^2.7.8", - "zendframework/zend-soap": "^2.7.0", - "zendframework/zend-stdlib": "^2.7.7", - "zendframework/zend-text": "^2.6.0", - "zendframework/zend-uri": "^2.5.1", - "zendframework/zend-validator": "^2.6.0", - "zendframework/zend-view": "~2.10.0" + "laminas/laminas-code": "~3.3.0", + "laminas/laminas-config": "^2.6.0", + "laminas/laminas-console": "^2.6.0", + "laminas/laminas-crypt": "^2.6.0", + "laminas/laminas-di": "^2.6.1", + "laminas/laminas-eventmanager": "^2.6.3", + "laminas/laminas-form": "^2.10.0", + "laminas/laminas-http": "^2.6.0", + "laminas/laminas-i18n": "^2.7.3", + "laminas/laminas-json": "^2.6.1", + "laminas/laminas-log": "^2.9.1", + "laminas/laminas-modulemanager": "^2.7", + "laminas/laminas-mvc": "~2.7.0", + "laminas/laminas-serializer": "^2.7.2", + "laminas/laminas-server": "^2.6.1", + "laminas/laminas-servicemanager": "^2.7.8", + "laminas/laminas-soap": "^2.7.0", + "laminas/laminas-stdlib": "^2.7.7", + "laminas/laminas-text": "^2.6.0", + "laminas/laminas-uri": "^2.5.1", + "laminas/laminas-validator": "^2.6.0", + "laminas/laminas-view": "~2.10.0" }, "conflict": { "gene/bluefoot": "*" @@ -2460,10 +2460,6 @@ "app/etc/registration_globlist.php", "app/etc/registration_globlist.php" ], - [ - "app/functions.php", - "app/functions.php" - ], [ "auth.json.sample", "auth.json.sample" @@ -3128,79 +3124,6 @@ ], "description": "Authorization module provides access to Magento ACL functionality." }, - { - "name": "magento/module-authorizenet", - "version": "100.3.1", - "dist": { - "type": "zip", - "url": "https://repo.magento.com/archives/magento/module-authorizenet/magento-module-authorizenet-100.3.1.0.zip", - "reference": null, - "shasum": "ab9dc9018f1a5b71cfaa54c1975d6ee6b5c7610c" - }, - "require": { - "magento/framework": "102.0.*", - "magento/module-backend": "101.0.*", - "magento/module-catalog": "103.0.*", - "magento/module-checkout": "100.3.*", - "magento/module-payment": "100.3.*", - "magento/module-quote": "101.1.*", - "magento/module-sales": "102.0.*", - "magento/module-store": "101.0.*", - "php": "~7.1.3||~7.2.0" - }, - "suggest": { - "magento/module-config": "101.1.*" - }, - "type": "magento2-module", - "autoload": { - "files": [ - "registration.php" - ], - "psr-4": { - "Magento\\Authorizenet\\": "" - } - }, - "license": [ - "OSL-3.0", - "AFL-3.0" - ], - "description": "N/A" - }, - { - "name": "magento/module-authorizenet-acceptjs", - "version": "100.3.0", - "dist": { - "type": "zip", - "url": "https://repo.magento.com/archives/magento/module-authorizenet-acceptjs/magento-module-authorizenet-acceptjs-100.3.0.0.zip", - "reference": null, - "shasum": "16bc1b50a4e7f63bfa3869fa2c3bd4c67cedd8a3" - }, - "require": { - "magento/framework": "102.0.*", - "magento/module-backend": "101.0.*", - "magento/module-checkout": "100.3.*", - "magento/module-config": "101.1.*", - "magento/module-payment": "100.3.*", - "magento/module-quote": "101.1.*", - "magento/module-sales": "102.0.*", - "magento/module-store": "101.0.*", - "php": "~7.1.3||~7.2.0" - }, - "type": "magento2-module", - "autoload": { - "files": [ - "registration.php" - ], - "psr-4": { - "Magento\\AuthorizenetAcceptjs\\": "" - } - }, - "license": [ - "OSL-3.0", - "AFL-3.0" - ], - "description": "N/A" - }, { "name": "magento/module-backend", "version": "101.0.1", @@ -3485,9 +3408,9 @@ "magento/module-customer": "102.0.*", "magento/module-store": "101.0.*", "php": "~7.1.3||~7.2.0", - "zendframework/zend-captcha": "^2.7.1", - "zendframework/zend-db": "^2.8.2", - "zendframework/zend-session": "^2.7.3" + "laminas/laminas-captcha": "^2.7.1", + "laminas/laminas-db": "^2.8.2", + "laminas/laminas-session": "^2.7.3" }, "type": "magento2-module", "autoload": { @@ -4995,7 +4918,7 @@ "shasum": "ed1da1137848560dde1a85f0f54dc2fac262359e" }, "require": { - "elasticsearch/elasticsearch": "~2.0|~5.1|~6.1", + "elasticsearch/elasticsearch": "~7.6", "magento/framework": "102.0.*", "magento/module-advanced-search": "100.3.*", "magento/module-catalog": "103.0.*", @@ -5035,7 +4958,7 @@ "shasum": "a9da3243900390ad163efc7969b07116d2eb793f" }, "require": { - "elasticsearch/elasticsearch": "~2.0|~5.1|~6.1", + "elasticsearch/elasticsearch": "~7.6", "magento/framework": "102.0.*", "magento/module-advanced-search": "100.3.*", "magento/module-catalog-search": "101.0.*", @@ -8441,45 +8364,6 @@ ], "description": "N/A" }, - { - "name": "magento/module-signifyd", - "version": "100.3.1", - "dist": { - "type": "zip", - "url": "https://repo.magento.com/archives/magento/module-signifyd/magento-module-signifyd-100.3.1.0.zip", - "reference": null, - "shasum": "768442b807e31bacfb5bbf914e155bac3ca88dde" - }, - "require": { - "magento/framework": "102.0.*", - "magento/module-backend": "101.0.*", - "magento/module-checkout": "100.3.*", - "magento/module-config": "101.1.*", - "magento/module-customer": "102.0.*", - "magento/module-directory": "100.3.*", - "magento/module-payment": "100.3.*", - "magento/module-sales": "102.0.*", - "magento/module-store": "101.0.*", - "php": "~7.1.3||~7.2.0" - }, - "suggest": { - "magento/module-config": "101.1.*" - }, - "type": "magento2-module", - "autoload": { - "files": [ - "registration.php" - ], - "psr-4": { - "Magento\\Signifyd\\": "" - } - }, - "license": [ - "OSL-3.0", - "AFL-3.0" - ], - "description": "Submitting Case Entry to Signifyd on Order Creation" - }, { "name": "magento/module-sitemap", "version": "100.3.1", @@ -9668,7 +9552,7 @@ "colinmollenhour/php-redis-session-abstract": "~1.4.0", "composer/composer": "^1.6", "dotmailer/dotmailer-magento2-extension": "3.1.1", - "elasticsearch/elasticsearch": "~2.0|~5.1|~6.1", + "elasticsearch/elasticsearch": "~7.6", "ext-bcmath": "*", "ext-ctype": "*", "ext-curl": "*", @@ -9709,8 +9593,6 @@ "magento/module-analytics": "100.3.1", "magento/module-asynchronous-operations": "100.3.1", "magento/module-authorization": "100.3.1", - "magento/module-authorizenet": "100.3.1", - "magento/module-authorizenet-acceptjs": "100.3.0", "magento/module-backend": "101.0.1", "magento/module-backup": "100.3.1", "magento/module-braintree": "100.3.1", @@ -9820,7 +9702,6 @@ "magento/module-send-friend": "100.3.1", "magento/module-send-friend-graph-ql": "100.3.0", "magento/module-shipping": "100.3.1", - "magento/module-signifyd": "100.3.1", "magento/module-sitemap": "100.3.1", "magento/module-store": "101.0.1", "magento/module-store-graph-ql": "100.3.1", @@ -9878,33 +9759,33 @@ "tubalmartin/cssmin": "4.1.1", "vertex/product-magento-module": "3.1.0", "webonyx/graphql-php": "^0.12.6", - "zendframework/zend-captcha": "^2.7.1", - "zendframework/zend-code": "~3.3.0", - "zendframework/zend-config": "^2.6.0", - "zendframework/zend-console": "^2.6.0", - "zendframework/zend-crypt": "^2.6.0", - "zendframework/zend-db": "^2.8.2", - "zendframework/zend-di": "^2.6.1", - "zendframework/zend-eventmanager": "^2.6.3", - "zendframework/zend-feed": "^2.9.0", - "zendframework/zend-form": "^2.10.0", - "zendframework/zend-http": "^2.6.0", - "zendframework/zend-i18n": "^2.7.3", - "zendframework/zend-json": "^2.6.1", - "zendframework/zend-log": "^2.9.1", - "zendframework/zend-mail": "^2.9.0", - "zendframework/zend-modulemanager": "^2.7", - "zendframework/zend-mvc": "~2.7.0", - "zendframework/zend-serializer": "^2.7.2", - "zendframework/zend-server": "^2.6.1", - "zendframework/zend-servicemanager": "^2.7.8", - "zendframework/zend-session": "^2.7.3", - "zendframework/zend-soap": "^2.7.0", - "zendframework/zend-stdlib": "^2.7.7", - "zendframework/zend-text": "^2.6.0", - "zendframework/zend-uri": "^2.5.1", - "zendframework/zend-validator": "^2.6.0", - "zendframework/zend-view": "~2.10.0" + "laminas/laminas-captcha": "^2.7.1", + "laminas/laminas-code": "~3.3.0", + "laminas/laminas-config": "^2.6.0", + "laminas/laminas-console": "^2.6.0", + "laminas/laminas-crypt": "^2.6.0", + "laminas/laminas-db": "^2.8.2", + "laminas/laminas-di": "^2.6.1", + "laminas/laminas-eventmanager": "^2.6.3", + "laminas/laminas-feed": "^2.9.0", + "laminas/laminas-form": "^2.10.0", + "laminas/laminas-http": "^2.6.0", + "laminas/laminas-i18n": "^2.7.3", + "laminas/laminas-json": "^2.6.1", + "laminas/laminas-log": "^2.9.1", + "laminas/laminas-mail": "^2.9.0", + "laminas/laminas-modulemanager": "^2.7", + "laminas/laminas-mvc": "~2.7.0", + "laminas/laminas-serializer": "^2.7.2", + "laminas/laminas-server": "^2.6.1", + "laminas/laminas-servicemanager": "^2.7.8", + "laminas/laminas-session": "^2.7.3", + "laminas/laminas-soap": "^2.7.0", + "laminas/laminas-stdlib": "^2.7.7", + "laminas/laminas-text": "^2.6.0", + "laminas/laminas-uri": "^2.5.1", + "laminas/laminas-validator": "^2.6.0", + "laminas/laminas-view": "~2.10.0" }, "type": "metapackage", "license": [ @@ -12065,8 +11946,8 @@ "monolog/monolog": "^1.17.0", "php": "~7.1.3||~7.2.0", "psr/log": "~1.0", - "zendframework/zend-barcode": "^2.7.0", - "zendframework/zend-http": "^2.6.0" + "laminas/laminas-barcode": "^2.7.0", + "laminas/laminas-http": "^2.6.0" }, "suggest": { "magento/module-rma": "^101.1.0", @@ -12399,29 +12280,29 @@ "time": "2018-09-07T08:16:44+00:00" }, { - "name": "zendframework/zend-barcode", + "name": "laminas/laminas-barcode", "version": "2.7.0", "source": { "type": "git", - "url": "https://github.com/zendframework/zend-barcode.git", + "url": "https://github.com/laminas/laminas-barcode.git", "reference": "50f24f604ef2172a0127efe91e786bc2caf2e8cf" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/zendframework/zend-barcode/zipball/50f24f604ef2172a0127efe91e786bc2caf2e8cf", + "url": "https://api.github.com/repos/laminas/laminas-barcode/zipball/50f24f604ef2172a0127efe91e786bc2caf2e8cf", "reference": "50f24f604ef2172a0127efe91e786bc2caf2e8cf", "shasum": "" }, "require": { "php": "^5.6 || ^7.0", - "zendframework/zend-servicemanager": "^2.7.8 || ^3.3", - "zendframework/zend-stdlib": "^2.7.7 || ^3.1", - "zendframework/zend-validator": "^2.10.1" + "laminas/laminas-servicemanager": "^2.7.8 || ^3.3", + "laminas/laminas-stdlib": "^2.7.7 || ^3.1", + "laminas/laminas-validator": "^2.10.1" }, "require-dev": { "phpunit/phpunit": "^5.7.23 || ^6.4.3", - "zendframework/zend-coding-standard": "~1.0.0", - "zendframework/zend-config": "^2.6 || ^3.1", + "laminas/laminas-coding-standard": "~1.0.0", + "laminas/laminas-config": "^2.6 || ^3.1", "zendframework/zendpdf": "^2.0.2" }, "suggest": { @@ -12436,7 +12317,7 @@ }, "autoload": { "psr-4": { - "Zend\\Barcode\\": "src/" + "Laminas\\Barcode\\": "src/" } }, "notification-url": "https://packagist.org/downloads/", @@ -12452,38 +12333,38 @@ "time": "2017-12-11T15:30:02+00:00" }, { - "name": "zendframework/zend-captcha", + "name": "laminas/laminas-captcha", "version": "2.8.0", "source": { "type": "git", - "url": "https://github.com/zendframework/zend-captcha.git", + "url": "https://github.com/laminas/laminas-captcha.git", "reference": "37e9b6a4f632a9399eecbf2e5e325ad89083f87b" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/zendframework/zend-captcha/zipball/37e9b6a4f632a9399eecbf2e5e325ad89083f87b", + "url": "https://api.github.com/repos/laminas/laminas-captcha/zipball/37e9b6a4f632a9399eecbf2e5e325ad89083f87b", "reference": "37e9b6a4f632a9399eecbf2e5e325ad89083f87b", "shasum": "" }, "require": { "php": "^5.6 || ^7.0", - "zendframework/zend-math": "^2.7 || ^3.0", - "zendframework/zend-stdlib": "^2.7.7 || ^3.1" + "laminas/laminas-math": "^2.7 || ^3.0", + "laminas/laminas-stdlib": "^2.7.7 || ^3.1" }, "require-dev": { "phpunit/phpunit": "^5.7.27 || ^6.5.8 || ^7.1.2", - "zendframework/zend-coding-standard": "~1.0.0", - "zendframework/zend-session": "^2.8", - "zendframework/zend-text": "^2.6", - "zendframework/zend-validator": "^2.10.1", - "zendframework/zendservice-recaptcha": "^3.0" + "laminas/laminas-coding-standard": "~1.0.0", + "laminas/laminas-session": "^2.8", + "laminas/laminas-text": "^2.6", + "laminas/laminas-validator": "^2.10.1", + "laminas/laminas-recaptcha": "^3.0" }, "suggest": { - "zendframework/zend-i18n-resources": "Translations of captcha messages", - "zendframework/zend-session": "Zend\\Session component", - "zendframework/zend-text": "Zend\\Text component", - "zendframework/zend-validator": "Zend\\Validator component", - "zendframework/zendservice-recaptcha": "ZendService\\ReCaptcha component" + "laminas/laminas-i18n-resources": "Translations of captcha messages", + "laminas/laminas-session": "Laminas\\Session component", + "laminas/laminas-text": "Laminas\\Text component", + "laminas/laminas-validator": "Laminas\\Validator component", + "laminas/laminas-recaptcha": "Laminas\\ReCaptcha component" }, "type": "library", "extra": { @@ -12494,7 +12375,7 @@ }, "autoload": { "psr-4": { - "Zend\\Captcha\\": "src/" + "Laminas\\Captcha\\": "src/" } }, "notification-url": "https://packagist.org/downloads/", @@ -12510,33 +12391,33 @@ "time": "2018-04-24T17:24:10+00:00" }, { - "name": "zendframework/zend-code", + "name": "laminas/laminas-code", "version": "3.3.1", "source": { "type": "git", - "url": "https://github.com/zendframework/zend-code.git", + "url": "https://github.com/laminas/laminas-code.git", "reference": "c21db169075c6ec4b342149f446e7b7b724f95eb" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/zendframework/zend-code/zipball/c21db169075c6ec4b342149f446e7b7b724f95eb", + "url": "https://api.github.com/repos/laminas/laminas-code/zipball/c21db169075c6ec4b342149f446e7b7b724f95eb", "reference": "c21db169075c6ec4b342149f446e7b7b724f95eb", "shasum": "" }, "require": { "php": "^7.1", - "zendframework/zend-eventmanager": "^2.6 || ^3.0" + "laminas/laminas-eventmanager": "^2.6 || ^3.0" }, "require-dev": { "doctrine/annotations": "~1.0", "ext-phar": "*", "phpunit/phpunit": "^6.2.3", - "zendframework/zend-coding-standard": "^1.0.0", - "zendframework/zend-stdlib": "^2.7 || ^3.0" + "laminas/laminas-coding-standard": "^1.0.0", + "laminas/laminas-stdlib": "^2.7 || ^3.0" }, "suggest": { "doctrine/annotations": "Doctrine\\Common\\Annotations >=1.0 for annotation features", - "zendframework/zend-stdlib": "Zend\\Stdlib component" + "laminas/laminas-stdlib": "Laminas\\Stdlib component" }, "type": "library", "extra": { @@ -12547,7 +12428,7 @@ }, "autoload": { "psr-4": { - "Zend\\Code\\": "src/" + "Laminas\\Code\\": "src/" } }, "notification-url": "https://packagist.org/downloads/", @@ -12555,7 +12436,7 @@ "BSD-3-Clause" ], "description": "provides facilities to generate arbitrary code using an object oriented interface", - "homepage": "https://github.com/zendframework/zend-code", + "homepage": "https://github.com/laminas/laminas-code", "keywords": [ "code", "zf2" @@ -12563,36 +12444,36 @@ "time": "2018-08-13T20:36:59+00:00" }, { - "name": "zendframework/zend-config", + "name": "laminas/laminas-config", "version": "2.6.0", "source": { "type": "git", - "url": "https://github.com/zendframework/zend-config.git", + "url": "https://github.com/laminas/laminas-config.git", "reference": "2920e877a9f6dca9fa8f6bd3b1ffc2e19bb1e30d" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/zendframework/zend-config/zipball/2920e877a9f6dca9fa8f6bd3b1ffc2e19bb1e30d", + "url": "https://api.github.com/repos/laminas/laminas-config/zipball/2920e877a9f6dca9fa8f6bd3b1ffc2e19bb1e30d", "reference": "2920e877a9f6dca9fa8f6bd3b1ffc2e19bb1e30d", "shasum": "" }, "require": { "php": "^5.5 || ^7.0", - "zendframework/zend-stdlib": "^2.7 || ^3.0" + "laminas/laminas-stdlib": "^2.7 || ^3.0" }, "require-dev": { "fabpot/php-cs-fixer": "1.7.*", "phpunit/phpunit": "~4.0", - "zendframework/zend-filter": "^2.6", - "zendframework/zend-i18n": "^2.5", - "zendframework/zend-json": "^2.6.1", - "zendframework/zend-servicemanager": "^2.7.5 || ^3.0.3" + "laminas/laminas-filter": "^2.6", + "laminas/laminas-i18n": "^2.5", + "laminas/laminas-json": "^2.6.1", + "laminas/laminas-servicemanager": "^2.7.5 || ^3.0.3" }, "suggest": { - "zendframework/zend-filter": "Zend\\Filter component", - "zendframework/zend-i18n": "Zend\\I18n component", - "zendframework/zend-json": "Zend\\Json to use the Json reader or writer classes", - "zendframework/zend-servicemanager": "Zend\\ServiceManager for use with the Config Factory to retrieve reader and writer instances" + "laminas/laminas-filter": "Laminas\\Filter component", + "laminas/laminas-i18n": "Laminas\\I18n component", + "laminas/laminas-json": "Laminas\\Json to use the Json reader or writer classes", + "laminas/laminas-servicemanager": "Laminas\\ServiceManager for use with the Config Factory to retrieve reader and writer instances" }, "type": "library", "extra": { @@ -12603,7 +12484,7 @@ }, "autoload": { "psr-4": { - "Zend\\Config\\": "src/" + "Laminas\\Config\\": "src/" } }, "notification-url": "https://packagist.org/downloads/", @@ -12611,7 +12492,7 @@ "BSD-3-Clause" ], "description": "provides a nested object property based user interface for accessing this configuration data within application code", - "homepage": "https://github.com/zendframework/zend-config", + "homepage": "https://github.com/laminas/laminas-config", "keywords": [ "config", "zf2" @@ -12619,33 +12500,33 @@ "time": "2016-02-04T23:01:10+00:00" }, { - "name": "zendframework/zend-console", + "name": "laminas/laminas-console", "version": "2.7.0", "source": { "type": "git", - "url": "https://github.com/zendframework/zend-console.git", + "url": "https://github.com/laminas/laminas-console.git", "reference": "e8aa08da83de3d265256c40ba45cd649115f0e18" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/zendframework/zend-console/zipball/e8aa08da83de3d265256c40ba45cd649115f0e18", + "url": "https://api.github.com/repos/laminas/laminas-console/zipball/e8aa08da83de3d265256c40ba45cd649115f0e18", "reference": "e8aa08da83de3d265256c40ba45cd649115f0e18", "shasum": "" }, "require": { "php": "^5.6 || ^7.0", - "zendframework/zend-stdlib": "^2.7.7 || ^3.1" + "laminas/laminas-stdlib": "^2.7.7 || ^3.1" }, "require-dev": { "phpunit/phpunit": "^5.7.23 || ^6.4.3", - "zendframework/zend-coding-standard": "~1.0.0", - "zendframework/zend-filter": "^2.7.2", - "zendframework/zend-json": "^2.6 || ^3.0", - "zendframework/zend-validator": "^2.10.1" + "laminas/laminas-coding-standard": "~1.0.0", + "laminas/laminas-filter": "^2.7.2", + "laminas/laminas-json": "^2.6 || ^3.0", + "laminas/laminas-validator": "^2.10.1" }, "suggest": { - "zendframework/zend-filter": "To support DefaultRouteMatcher usage", - "zendframework/zend-validator": "To support DefaultRouteMatcher usage" + "laminas/laminas-filter": "To support DefaultRouteMatcher usage", + "laminas/laminas-validator": "To support DefaultRouteMatcher usage" }, "type": "library", "extra": { @@ -12656,7 +12537,7 @@ }, "autoload": { "psr-4": { - "Zend\\Console\\": "src/" + "Laminas\\Console\\": "src/" } }, "notification-url": "https://packagist.org/downloads/", @@ -12672,31 +12553,31 @@ "time": "2018-01-25T19:08:04+00:00" }, { - "name": "zendframework/zend-crypt", + "name": "laminas/laminas-crypt", "version": "2.6.0", "source": { "type": "git", - "url": "https://github.com/zendframework/zend-crypt.git", + "url": "https://github.com/laminas/laminas-crypt.git", "reference": "1b2f5600bf6262904167116fa67b58ab1457036d" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/zendframework/zend-crypt/zipball/1b2f5600bf6262904167116fa67b58ab1457036d", + "url": "https://api.github.com/repos/laminas/laminas-crypt/zipball/1b2f5600bf6262904167116fa67b58ab1457036d", "reference": "1b2f5600bf6262904167116fa67b58ab1457036d", "shasum": "" }, "require": { "container-interop/container-interop": "~1.0", "php": "^5.5 || ^7.0", - "zendframework/zend-math": "^2.6", - "zendframework/zend-stdlib": "^2.7 || ^3.0" + "laminas/laminas-math": "^2.6", + "laminas/laminas-stdlib": "^2.7 || ^3.0" }, "require-dev": { "fabpot/php-cs-fixer": "1.7.*", "phpunit/phpunit": "~4.0" }, "suggest": { - "ext-mcrypt": "Required for most features of Zend\\Crypt" + "ext-mcrypt": "Required for most features of Laminas\\Crypt" }, "type": "library", "extra": { @@ -12707,14 +12588,14 @@ }, "autoload": { "psr-4": { - "Zend\\Crypt\\": "src/" + "Laminas\\Crypt\\": "src/" } }, "notification-url": "https://packagist.org/downloads/", "license": [ "BSD-3-Clause" ], - "homepage": "https://github.com/zendframework/zend-crypt", + "homepage": "https://github.com/laminas/laminas-crypt", "keywords": [ "crypt", "zf2" @@ -12722,34 +12603,34 @@ "time": "2016-02-03T23:46:30+00:00" }, { - "name": "zendframework/zend-db", + "name": "laminas/laminas-db", "version": "2.10.0", "source": { "type": "git", - "url": "https://github.com/zendframework/zend-db.git", + "url": "https://github.com/laminas/laminas-db.git", "reference": "77022f06f6ffd384fa86d22ab8d8bbdb925a1e8e" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/zendframework/zend-db/zipball/77022f06f6ffd384fa86d22ab8d8bbdb925a1e8e", + "url": "https://api.github.com/repos/laminas/laminas-db/zipball/77022f06f6ffd384fa86d22ab8d8bbdb925a1e8e", "reference": "77022f06f6ffd384fa86d22ab8d8bbdb925a1e8e", "shasum": "" }, "require": { "php": "^5.6 || ^7.0", - "zendframework/zend-stdlib": "^2.7 || ^3.0" + "laminas/laminas-stdlib": "^2.7 || ^3.0" }, "require-dev": { "phpunit/phpunit": "^5.7.25 || ^6.4.4", - "zendframework/zend-coding-standard": "~1.0.0", - "zendframework/zend-eventmanager": "^2.6.2 || ^3.0", - "zendframework/zend-hydrator": "^1.1 || ^2.1 || ^3.0", - "zendframework/zend-servicemanager": "^2.7.5 || ^3.0.3" + "laminas/laminas-coding-standard": "~1.0.0", + "laminas/laminas-eventmanager": "^2.6.2 || ^3.0", + "laminas/laminas-hydrator": "^1.1 || ^2.1 || ^3.0", + "laminas/laminas-servicemanager": "^2.7.5 || ^3.0.3" }, "suggest": { - "zendframework/zend-eventmanager": "Zend\\EventManager component", - "zendframework/zend-hydrator": "Zend\\Hydrator component for using HydratingResultSets", - "zendframework/zend-servicemanager": "Zend\\ServiceManager component" + "laminas/laminas-eventmanager": "Laminas\\EventManager component", + "laminas/laminas-hydrator": "Laminas\\Hydrator component for using HydratingResultSets", + "laminas/laminas-servicemanager": "Laminas\\ServiceManager component" }, "type": "library", "extra": { @@ -12758,13 +12639,13 @@ "dev-develop": "2.10-dev" }, "zf": { - "component": "Zend\\Db", - "config-provider": "Zend\\Db\\ConfigProvider" + "component": "Laminas\\Db", + "config-provider": "Laminas\\Db\\ConfigProvider" } }, "autoload": { "psr-4": { - "Zend\\Db\\": "src/" + "Laminas\\Db\\": "src/" } }, "notification-url": "https://packagist.org/downloads/", @@ -12780,24 +12661,24 @@ "time": "2019-02-25T11:37:45+00:00" }, { - "name": "zendframework/zend-di", + "name": "laminas/laminas-di", "version": "2.6.1", "source": { "type": "git", - "url": "https://github.com/zendframework/zend-di.git", + "url": "https://github.com/laminas/laminas-di.git", "reference": "1fd1ba85660b5a2718741b38639dc7c4c3194b37" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/zendframework/zend-di/zipball/1fd1ba85660b5a2718741b38639dc7c4c3194b37", + "url": "https://api.github.com/repos/laminas/laminas-di/zipball/1fd1ba85660b5a2718741b38639dc7c4c3194b37", "reference": "1fd1ba85660b5a2718741b38639dc7c4c3194b37", "shasum": "" }, "require": { "container-interop/container-interop": "^1.1", "php": "^5.5 || ^7.0", - "zendframework/zend-code": "^2.6 || ^3.0", - "zendframework/zend-stdlib": "^2.7 || ^3.0" + "laminas/laminas-code": "^2.6 || ^3.0", + "laminas/laminas-stdlib": "^2.7 || ^3.0" }, "require-dev": { "fabpot/php-cs-fixer": "1.7.*", @@ -12812,14 +12693,14 @@ }, "autoload": { "psr-4": { - "Zend\\Di\\": "src/" + "Laminas\\Di\\": "src/" } }, "notification-url": "https://packagist.org/downloads/", "license": [ "BSD-3-Clause" ], - "homepage": "https://github.com/zendframework/zend-di", + "homepage": "https://github.com/laminas/laminas-di", "keywords": [ "di", "zf2" @@ -12827,16 +12708,16 @@ "time": "2016-04-25T20:58:11+00:00" }, { - "name": "zendframework/zend-diactoros", + "name": "laminas/laminas-diactoros", "version": "1.8.6", "source": { "type": "git", - "url": "https://github.com/zendframework/zend-diactoros.git", + "url": "https://github.com/laminas/laminas-diactoros.git", "reference": "20da13beba0dde8fb648be3cc19765732790f46e" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/zendframework/zend-diactoros/zipball/20da13beba0dde8fb648be3cc19765732790f46e", + "url": "https://api.github.com/repos/laminas/laminas-diactoros/zipball/20da13beba0dde8fb648be3cc19765732790f46e", "reference": "20da13beba0dde8fb648be3cc19765732790f46e", "shasum": "" }, @@ -12852,7 +12733,7 @@ "ext-libxml": "*", "php-http/psr7-integration-tests": "dev-master", "phpunit/phpunit": "^5.7.16 || ^6.0.8 || ^7.2.7", - "zendframework/zend-coding-standard": "~1.0" + "laminas/laminas-coding-standard": "~1.0" }, "type": "library", "extra": { @@ -12874,7 +12755,7 @@ "src/functions/parse_cookie_header.php" ], "psr-4": { - "Zend\\Diactoros\\": "src/" + "Laminas\\Diactoros\\": "src/" } }, "notification-url": "https://packagist.org/downloads/", @@ -12882,7 +12763,7 @@ "BSD-2-Clause" ], "description": "PSR HTTP Message implementations", - "homepage": "https://github.com/zendframework/zend-diactoros", + "homepage": "https://github.com/laminas/laminas-diactoros", "keywords": [ "http", "psr", @@ -12891,16 +12772,16 @@ "time": "2018-09-05T19:29:37+00:00" }, { - "name": "zendframework/zend-escaper", + "name": "laminas/laminas-escaper", "version": "2.6.0", "source": { "type": "git", - "url": "https://github.com/zendframework/zend-escaper.git", + "url": "https://github.com/laminas/laminas-escaper.git", "reference": "31d8aafae982f9568287cb4dce987e6aff8fd074" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/zendframework/zend-escaper/zipball/31d8aafae982f9568287cb4dce987e6aff8fd074", + "url": "https://api.github.com/repos/laminas/laminas-escaper/zipball/31d8aafae982f9568287cb4dce987e6aff8fd074", "reference": "31d8aafae982f9568287cb4dce987e6aff8fd074", "shasum": "" }, @@ -12909,7 +12790,7 @@ }, "require-dev": { "phpunit/phpunit": "^5.7.27 || ^6.5.8 || ^7.1.2", - "zendframework/zend-coding-standard": "~1.0.0" + "laminas/laminas-coding-standard": "~1.0.0" }, "type": "library", "extra": { @@ -12920,7 +12801,7 @@ }, "autoload": { "psr-4": { - "Zend\\Escaper\\": "src/" + "Laminas\\Escaper\\": "src/" } }, "notification-url": "https://packagist.org/downloads/", @@ -12936,22 +12817,22 @@ "time": "2018-04-25T15:48:53+00:00" }, { - "name": "zendframework/zend-eventmanager", + "name": "laminas/laminas-eventmanager", "version": "2.6.4", "source": { "type": "git", - "url": "https://github.com/zendframework/zend-eventmanager.git", + "url": "https://github.com/laminas/laminas-eventmanager.git", "reference": "d238c443220dce4b6396579c8ab2200ec25f9108" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/zendframework/zend-eventmanager/zipball/d238c443220dce4b6396579c8ab2200ec25f9108", + "url": "https://api.github.com/repos/laminas/laminas-eventmanager/zipball/d238c443220dce4b6396579c8ab2200ec25f9108", "reference": "d238c443220dce4b6396579c8ab2200ec25f9108", "shasum": "" }, "require": { "php": "^5.5 || ^7.0", - "zendframework/zend-stdlib": "^2.7" + "laminas/laminas-stdlib": "^2.7" }, "require-dev": { "athletic/athletic": "dev-master", @@ -12968,14 +12849,14 @@ }, "autoload": { "psr-4": { - "Zend\\EventManager\\": "src/" + "Laminas\\EventManager\\": "src/" } }, "notification-url": "https://packagist.org/downloads/", "license": [ "BSD-3-Clause" ], - "homepage": "https://github.com/zendframework/zend-eventmanager", + "homepage": "https://github.com/laminas/laminas-eventmanager", "keywords": [ "eventmanager", "zf2" @@ -12983,41 +12864,41 @@ "time": "2017-12-12T17:48:56+00:00" }, { - "name": "zendframework/zend-feed", + "name": "laminas/laminas-feed", "version": "2.10.3", "source": { "type": "git", - "url": "https://github.com/zendframework/zend-feed.git", + "url": "https://github.com/laminas/laminas-feed.git", "reference": "6641f4cf3f4586c63f83fd70b6d19966025c8888" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/zendframework/zend-feed/zipball/6641f4cf3f4586c63f83fd70b6d19966025c8888", + "url": "https://api.github.com/repos/laminas/laminas-feed/zipball/6641f4cf3f4586c63f83fd70b6d19966025c8888", "reference": "6641f4cf3f4586c63f83fd70b6d19966025c8888", "shasum": "" }, "require": { "php": "^5.6 || ^7.0", - "zendframework/zend-escaper": "^2.5.2", - "zendframework/zend-stdlib": "^2.7.7 || ^3.1" + "laminas/laminas-escaper": "^2.5.2", + "laminas/laminas-stdlib": "^2.7.7 || ^3.1" }, "require-dev": { "phpunit/phpunit": "^5.7.23 || ^6.4.3", "psr/http-message": "^1.0.1", - "zendframework/zend-cache": "^2.7.2", - "zendframework/zend-coding-standard": "~1.0.0", - "zendframework/zend-db": "^2.8.2", - "zendframework/zend-http": "^2.7", - "zendframework/zend-servicemanager": "^2.7.8 || ^3.3", - "zendframework/zend-validator": "^2.10.1" + "laminas/laminas-cache": "^2.7.2", + "laminas/laminas-coding-standard": "~1.0.0", + "laminas/laminas-db": "^2.8.2", + "laminas/laminas-http": "^2.7", + "laminas/laminas-servicemanager": "^2.7.8 || ^3.3", + "laminas/laminas-validator": "^2.10.1" }, "suggest": { - "psr/http-message": "PSR-7 ^1.0.1, if you wish to use Zend\\Feed\\Reader\\Http\\Psr7ResponseDecorator", - "zendframework/zend-cache": "Zend\\Cache component, for optionally caching feeds between requests", - "zendframework/zend-db": "Zend\\Db component, for use with PubSubHubbub", - "zendframework/zend-http": "Zend\\Http for PubSubHubbub, and optionally for use with Zend\\Feed\\Reader", - "zendframework/zend-servicemanager": "Zend\\ServiceManager component, for easily extending ExtensionManager implementations", - "zendframework/zend-validator": "Zend\\Validator component, for validating email addresses used in Atom feeds and entries when using the Writer subcomponent" + "psr/http-message": "PSR-7 ^1.0.1, if you wish to use Laminas\\Feed\\Reader\\Http\\Psr7ResponseDecorator", + "laminas/laminas-cache": "Laminas\\Cache component, for optionally caching feeds between requests", + "laminas/laminas-db": "Laminas\\Db component, for use with PubSubHubbub", + "laminas/laminas-http": "Laminas\\Http for PubSubHubbub, and optionally for use with Laminas\\Feed\\Reader", + "laminas/laminas-servicemanager": "Laminas\\ServiceManager component, for easily extending ExtensionManager implementations", + "laminas/laminas-validator": "Laminas\\Validator component, for validating email addresses used in Atom feeds and entries when using the Writer subcomponent" }, "type": "library", "extra": { @@ -13028,7 +12909,7 @@ }, "autoload": { "psr-4": { - "Zend\\Feed\\": "src/" + "Laminas\\Feed\\": "src/" } }, "notification-url": "https://packagist.org/downloads/", @@ -13044,41 +12925,41 @@ "time": "2018-08-01T13:53:20+00:00" }, { - "name": "zendframework/zend-filter", + "name": "laminas/laminas-filter", "version": "2.9.1", "source": { "type": "git", - "url": "https://github.com/zendframework/zend-filter.git", + "url": "https://github.com/laminas/laminas-filter.git", "reference": "1c3e6d02f9cd5f6c929c9859498f5efbe216e86f" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/zendframework/zend-filter/zipball/1c3e6d02f9cd5f6c929c9859498f5efbe216e86f", + "url": "https://api.github.com/repos/laminas/laminas-filter/zipball/1c3e6d02f9cd5f6c929c9859498f5efbe216e86f", "reference": "1c3e6d02f9cd5f6c929c9859498f5efbe216e86f", "shasum": "" }, "require": { "php": "^5.6 || ^7.0", - "zendframework/zend-stdlib": "^2.7.7 || ^3.1" + "laminas/laminas-stdlib": "^2.7.7 || ^3.1" }, "conflict": { - "zendframework/zend-validator": "<2.10.1" + "laminas/laminas-validator": "<2.10.1" }, "require-dev": { "pear/archive_tar": "^1.4.3", "phpunit/phpunit": "^5.7.23 || ^6.4.3", "psr/http-factory": "^1.0", - "zendframework/zend-coding-standard": "~1.0.0", - "zendframework/zend-crypt": "^3.2.1", - "zendframework/zend-servicemanager": "^2.7.8 || ^3.3", - "zendframework/zend-uri": "^2.6" + "laminas/laminas-coding-standard": "~1.0.0", + "laminas/laminas-crypt": "^3.2.1", + "laminas/laminas-servicemanager": "^2.7.8 || ^3.3", + "laminas/laminas-uri": "^2.6" }, "suggest": { "psr/http-factory-implementation": "psr/http-factory-implementation, for creating file upload instances when consuming PSR-7 in file upload filters", - "zendframework/zend-crypt": "Zend\\Crypt component, for encryption filters", - "zendframework/zend-i18n": "Zend\\I18n component for filters depending on i18n functionality", - "zendframework/zend-servicemanager": "Zend\\ServiceManager component, for using the filter chain functionality", - "zendframework/zend-uri": "Zend\\Uri component, for the UriNormalize filter" + "laminas/laminas-crypt": "Laminas\\Crypt component, for encryption filters", + "laminas/laminas-i18n": "Laminas\\I18n component for filters depending on i18n functionality", + "laminas/laminas-servicemanager": "Laminas\\ServiceManager component, for using the filter chain functionality", + "laminas/laminas-uri": "Laminas\\Uri component, for the UriNormalize filter" }, "type": "library", "extra": { @@ -13087,13 +12968,13 @@ "dev-develop": "2.10.x-dev" }, "zf": { - "component": "Zend\\Filter", - "config-provider": "Zend\\Filter\\ConfigProvider" + "component": "Laminas\\Filter", + "config-provider": "Laminas\\Filter\\ConfigProvider" } }, "autoload": { "psr-4": { - "Zend\\Filter\\": "src/" + "Laminas\\Filter\\": "src/" } }, "notification-url": "https://packagist.org/downloads/", @@ -13109,51 +12990,51 @@ "time": "2018-12-17T16:00:04+00:00" }, { - "name": "zendframework/zend-form", + "name": "laminas/laminas-form", "version": "2.13.0", "source": { "type": "git", - "url": "https://github.com/zendframework/zend-form.git", + "url": "https://github.com/laminas/laminas-form.git", "reference": "c713a12ccbd43148b71c9339e171ca11e3f8a1da" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/zendframework/zend-form/zipball/c713a12ccbd43148b71c9339e171ca11e3f8a1da", + "url": "https://api.github.com/repos/laminas/laminas-form/zipball/c713a12ccbd43148b71c9339e171ca11e3f8a1da", "reference": "c713a12ccbd43148b71c9339e171ca11e3f8a1da", "shasum": "" }, "require": { "php": "^5.6 || ^7.0", - "zendframework/zend-hydrator": "^1.1 || ^2.1 || ^3.0", - "zendframework/zend-inputfilter": "^2.8", - "zendframework/zend-stdlib": "^2.7 || ^3.0" + "laminas/laminas-hydrator": "^1.1 || ^2.1 || ^3.0", + "laminas/laminas-inputfilter": "^2.8", + "laminas/laminas-stdlib": "^2.7 || ^3.0" }, "require-dev": { "doctrine/annotations": "~1.0", "phpunit/phpunit": "^5.7.23 || ^6.5.3", - "zendframework/zend-cache": "^2.6.1", - "zendframework/zend-captcha": "^2.7.1", - "zendframework/zend-code": "^2.6 || ^3.0", - "zendframework/zend-coding-standard": "~1.0.0", - "zendframework/zend-escaper": "^2.5", - "zendframework/zend-eventmanager": "^2.6.2 || ^3.0", - "zendframework/zend-filter": "^2.6", - "zendframework/zend-i18n": "^2.6", - "zendframework/zend-servicemanager": "^2.7.5 || ^3.0.3", - "zendframework/zend-session": "^2.8.1", - "zendframework/zend-text": "^2.6", - "zendframework/zend-validator": "^2.6", - "zendframework/zend-view": "^2.6.2", - "zendframework/zendservice-recaptcha": "^3.0.0" + "laminas/laminas-cache": "^2.6.1", + "laminas/laminas-captcha": "^2.7.1", + "laminas/laminas-code": "^2.6 || ^3.0", + "laminas/laminas-coding-standard": "~1.0.0", + "laminas/laminas-escaper": "^2.5", + "laminas/laminas-eventmanager": "^2.6.2 || ^3.0", + "laminas/laminas-filter": "^2.6", + "laminas/laminas-i18n": "^2.6", + "laminas/laminas-servicemanager": "^2.7.5 || ^3.0.3", + "laminas/laminas-session": "^2.8.1", + "laminas/laminas-text": "^2.6", + "laminas/laminas-validator": "^2.6", + "laminas/laminas-view": "^2.6.2", + "laminas/laminas-recaptcha": "^3.0.0" }, "suggest": { - "zendframework/zend-captcha": "^2.7.1, required for using CAPTCHA form elements", - "zendframework/zend-code": "^2.6 || ^3.0, required to use zend-form annotations support", - "zendframework/zend-eventmanager": "^2.6.2 || ^3.0, reuired for zend-form annotations support", - "zendframework/zend-i18n": "^2.6, required when using zend-form view helpers", - "zendframework/zend-servicemanager": "^2.7.5 || ^3.0.3, required to use the form factories or provide services", - "zendframework/zend-view": "^2.6.2, required for using the zend-form view helpers", - "zendframework/zendservice-recaptcha": "in order to use the ReCaptcha form element" + "laminas/laminas-captcha": "^2.7.1, required for using CAPTCHA form elements", + "laminas/laminas-code": "^2.6 || ^3.0, required to use zend-form annotations support", + "laminas/laminas-eventmanager": "^2.6.2 || ^3.0, reuired for zend-form annotations support", + "laminas/laminas-i18n": "^2.6, required when using zend-form view helpers", + "laminas/laminas-servicemanager": "^2.7.5 || ^3.0.3, required to use the form factories or provide services", + "laminas/laminas-view": "^2.6.2, required for using the zend-form view helpers", + "laminas/laminas-recaptcha": "in order to use the ReCaptcha form element" }, "type": "library", "extra": { @@ -13162,13 +13043,13 @@ "dev-develop": "2.14.x-dev" }, "zf": { - "component": "Zend\\Form", - "config-provider": "Zend\\Form\\ConfigProvider" + "component": "Laminas\\Form", + "config-provider": "Laminas\\Form\\ConfigProvider" } }, "autoload": { "psr-4": { - "Zend\\Form\\": "src/" + "Laminas\\Form\\": "src/" }, "files": [ "autoload/formElementManagerPolyfill.php" @@ -13187,30 +13068,30 @@ "time": "2018-12-11T22:51:29+00:00" }, { - "name": "zendframework/zend-http", + "name": "laminas/laminas-http", "version": "2.8.4", "source": { "type": "git", - "url": "https://github.com/zendframework/zend-http.git", + "url": "https://github.com/laminas/laminas-http.git", "reference": "d160aedc096be230af0fe9c31151b2b33ad4e807" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/zendframework/zend-http/zipball/d160aedc096be230af0fe9c31151b2b33ad4e807", + "url": "https://api.github.com/repos/laminas/laminas-http/zipball/d160aedc096be230af0fe9c31151b2b33ad4e807", "reference": "d160aedc096be230af0fe9c31151b2b33ad4e807", "shasum": "" }, "require": { "php": "^5.6 || ^7.0", - "zendframework/zend-loader": "^2.5.1", - "zendframework/zend-stdlib": "^3.1 || ^2.7.7", - "zendframework/zend-uri": "^2.5.2", - "zendframework/zend-validator": "^2.10.1" + "laminas/laminas-loader": "^2.5.1", + "laminas/laminas-stdlib": "^3.1 || ^2.7.7", + "laminas/laminas-uri": "^2.5.2", + "laminas/laminas-validator": "^2.10.1" }, "require-dev": { "phpunit/phpunit": "^5.7.27 || ^6.5.8 || ^7.1.3", - "zendframework/zend-coding-standard": "~1.0.0", - "zendframework/zend-config": "^3.1 || ^2.6" + "laminas/laminas-coding-standard": "~1.0.0", + "laminas/laminas-config": "^3.1 || ^2.6" }, "suggest": { "paragonie/certainty": "For automated management of cacert.pem" @@ -13224,7 +13105,7 @@ }, "autoload": { "psr-4": { - "Zend\\Http\\": "src/" + "Laminas\\Http\\": "src/" } }, "notification-url": "https://packagist.org/downloads/", @@ -13242,37 +13123,37 @@ "time": "2019-02-07T17:47:08+00:00" }, { - "name": "zendframework/zend-hydrator", + "name": "laminas/laminas-hydrator", "version": "1.1.0", "source": { "type": "git", - "url": "https://github.com/zendframework/zend-hydrator.git", + "url": "https://github.com/laminas/laminas-hydrator.git", "reference": "22652e1661a5a10b3f564cf7824a2206cf5a4a65" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/zendframework/zend-hydrator/zipball/22652e1661a5a10b3f564cf7824a2206cf5a4a65", + "url": "https://api.github.com/repos/laminas/laminas-hydrator/zipball/22652e1661a5a10b3f564cf7824a2206cf5a4a65", "reference": "22652e1661a5a10b3f564cf7824a2206cf5a4a65", "shasum": "" }, "require": { "php": "^5.5 || ^7.0", - "zendframework/zend-stdlib": "^2.7 || ^3.0" + "laminas/laminas-stdlib": "^2.7 || ^3.0" }, "require-dev": { "phpunit/phpunit": "~4.0", "squizlabs/php_codesniffer": "^2.0@dev", - "zendframework/zend-eventmanager": "^2.6.2 || ^3.0", - "zendframework/zend-filter": "^2.6", - "zendframework/zend-inputfilter": "^2.6", - "zendframework/zend-serializer": "^2.6.1", - "zendframework/zend-servicemanager": "^2.7.5 || ^3.0.3" + "laminas/laminas-eventmanager": "^2.6.2 || ^3.0", + "laminas/laminas-filter": "^2.6", + "laminas/laminas-inputfilter": "^2.6", + "laminas/laminas-serializer": "^2.6.1", + "laminas/laminas-servicemanager": "^2.7.5 || ^3.0.3" }, "suggest": { - "zendframework/zend-eventmanager": "^2.6.2 || ^3.0, to support aggregate hydrator usage", - "zendframework/zend-filter": "^2.6, to support naming strategy hydrator usage", - "zendframework/zend-serializer": "^2.6.1, to use the SerializableStrategy", - "zendframework/zend-servicemanager": "^2.7.5 || ^3.0.3, to support hydrator plugin manager usage" + "laminas/laminas-eventmanager": "^2.6.2 || ^3.0, to support aggregate hydrator usage", + "laminas/laminas-filter": "^2.6, to support naming strategy hydrator usage", + "laminas/laminas-serializer": "^2.6.1, to use the SerializableStrategy", + "laminas/laminas-servicemanager": "^2.7.5 || ^3.0.3, to support hydrator plugin manager usage" }, "type": "library", "extra": { @@ -13285,14 +13166,14 @@ }, "autoload": { "psr-4": { - "Zend\\Hydrator\\": "src/" + "Laminas\\Hydrator\\": "src/" } }, "notification-url": "https://packagist.org/downloads/", "license": [ "BSD-3-Clause" ], - "homepage": "https://github.com/zendframework/zend-hydrator", + "homepage": "https://github.com/laminas/laminas-hydrator", "keywords": [ "hydrator", "zf2" @@ -13300,44 +13181,44 @@ "time": "2016-02-18T22:38:26+00:00" }, { - "name": "zendframework/zend-i18n", + "name": "laminas/laminas-i18n", "version": "2.9.0", "source": { "type": "git", - "url": "https://github.com/zendframework/zend-i18n.git", + "url": "https://github.com/laminas/laminas-i18n.git", "reference": "6d69af5a04e1a4de7250043cb1322f077a0cdb7f" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/zendframework/zend-i18n/zipball/6d69af5a04e1a4de7250043cb1322f077a0cdb7f", + "url": "https://api.github.com/repos/laminas/laminas-i18n/zipball/6d69af5a04e1a4de7250043cb1322f077a0cdb7f", "reference": "6d69af5a04e1a4de7250043cb1322f077a0cdb7f", "shasum": "" }, "require": { "php": "^5.6 || ^7.0", - "zendframework/zend-stdlib": "^2.7 || ^3.0" + "laminas/laminas-stdlib": "^2.7 || ^3.0" }, "require-dev": { "phpunit/phpunit": "^5.7.27 || ^6.5.8 || ^7.1.2", - "zendframework/zend-cache": "^2.6.1", - "zendframework/zend-coding-standard": "~1.0.0", - "zendframework/zend-config": "^2.6", - "zendframework/zend-eventmanager": "^2.6.2 || ^3.0", - "zendframework/zend-filter": "^2.6.1", - "zendframework/zend-servicemanager": "^2.7.5 || ^3.0.3", - "zendframework/zend-validator": "^2.6", - "zendframework/zend-view": "^2.6.3" + "laminas/laminas-cache": "^2.6.1", + "laminas/laminas-coding-standard": "~1.0.0", + "laminas/laminas-config": "^2.6", + "laminas/laminas-eventmanager": "^2.6.2 || ^3.0", + "laminas/laminas-filter": "^2.6.1", + "laminas/laminas-servicemanager": "^2.7.5 || ^3.0.3", + "laminas/laminas-validator": "^2.6", + "laminas/laminas-view": "^2.6.3" }, "suggest": { - "ext-intl": "Required for most features of Zend\\I18n; included in default builds of PHP", - "zendframework/zend-cache": "Zend\\Cache component", - "zendframework/zend-config": "Zend\\Config component", - "zendframework/zend-eventmanager": "You should install this package to use the events in the translator", - "zendframework/zend-filter": "You should install this package to use the provided filters", - "zendframework/zend-i18n-resources": "Translation resources", - "zendframework/zend-servicemanager": "Zend\\ServiceManager component", - "zendframework/zend-validator": "You should install this package to use the provided validators", - "zendframework/zend-view": "You should install this package to use the provided view helpers" + "ext-intl": "Required for most features of Laminas\\I18n; included in default builds of PHP", + "laminas/laminas-cache": "Laminas\\Cache component", + "laminas/laminas-config": "Laminas\\Config component", + "laminas/laminas-eventmanager": "You should install this package to use the events in the translator", + "laminas/laminas-filter": "You should install this package to use the provided filters", + "laminas/laminas-i18n-resources": "Translation resources", + "laminas/laminas-servicemanager": "Laminas\\ServiceManager component", + "laminas/laminas-validator": "You should install this package to use the provided validators", + "laminas/laminas-view": "You should install this package to use the provided view helpers" }, "type": "library", "extra": { @@ -13346,13 +13227,13 @@ "dev-develop": "2.10.x-dev" }, "zf": { - "component": "Zend\\I18n", - "config-provider": "Zend\\I18n\\ConfigProvider" + "component": "Laminas\\I18n", + "config-provider": "Laminas\\I18n\\ConfigProvider" } }, "autoload": { "psr-4": { - "Zend\\I18n\\": "src/" + "Laminas\\I18n\\": "src/" } }, "notification-url": "https://packagist.org/downloads/", @@ -13368,30 +13249,30 @@ "time": "2018-05-16T16:39:13+00:00" }, { - "name": "zendframework/zend-inputfilter", + "name": "laminas/laminas-inputfilter", "version": "2.10.0", "source": { "type": "git", - "url": "https://github.com/zendframework/zend-inputfilter.git", + "url": "https://github.com/laminas/laminas-inputfilter.git", "reference": "4f52b71ec9cef3a06e3bba8f5c2124e94055ec0c" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/zendframework/zend-inputfilter/zipball/4f52b71ec9cef3a06e3bba8f5c2124e94055ec0c", + "url": "https://api.github.com/repos/laminas/laminas-inputfilter/zipball/4f52b71ec9cef3a06e3bba8f5c2124e94055ec0c", "reference": "4f52b71ec9cef3a06e3bba8f5c2124e94055ec0c", "shasum": "" }, "require": { "php": "^5.6 || ^7.0", - "zendframework/zend-filter": "^2.9.1", - "zendframework/zend-servicemanager": "^2.7.10 || ^3.3.1", - "zendframework/zend-stdlib": "^2.7 || ^3.0", - "zendframework/zend-validator": "^2.11" + "laminas/laminas-filter": "^2.9.1", + "laminas/laminas-servicemanager": "^2.7.10 || ^3.3.1", + "laminas/laminas-stdlib": "^2.7 || ^3.0", + "laminas/laminas-validator": "^2.11" }, "require-dev": { "phpunit/phpunit": "^5.7.23 || ^6.4.3", "psr/http-message": "^1.0", - "zendframework/zend-coding-standard": "~1.0.0" + "laminas/laminas-coding-standard": "~1.0.0" }, "suggest": { "psr/http-message-implementation": "PSR-7 is required if you wish to validate PSR-7 UploadedFileInterface payloads" @@ -13403,13 +13284,13 @@ "dev-develop": "2.11.x-dev" }, "zf": { - "component": "Zend\\InputFilter", - "config-provider": "Zend\\InputFilter\\ConfigProvider" + "component": "Laminas\\InputFilter", + "config-provider": "Laminas\\InputFilter\\ConfigProvider" } }, "autoload": { "psr-4": { - "Zend\\InputFilter\\": "src/" + "Laminas\\InputFilter\\": "src/" } }, "notification-url": "https://packagist.org/downloads/", @@ -13425,16 +13306,16 @@ "time": "2019-01-30T16:58:51+00:00" }, { - "name": "zendframework/zend-json", + "name": "laminas/laminas-json", "version": "2.6.1", "source": { "type": "git", - "url": "https://github.com/zendframework/zend-json.git", + "url": "https://github.com/laminas/laminas-json.git", "reference": "4c8705dbe4ad7d7e51b2876c5b9eea0ef916ba28" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/zendframework/zend-json/zipball/4c8705dbe4ad7d7e51b2876c5b9eea0ef916ba28", + "url": "https://api.github.com/repos/laminas/laminas-json/zipball/4c8705dbe4ad7d7e51b2876c5b9eea0ef916ba28", "reference": "4c8705dbe4ad7d7e51b2876c5b9eea0ef916ba28", "shasum": "" }, @@ -13444,16 +13325,16 @@ "require-dev": { "fabpot/php-cs-fixer": "1.7.*", "phpunit/phpunit": "~4.0", - "zendframework/zend-http": "^2.5.4", - "zendframework/zend-server": "^2.6.1", - "zendframework/zend-stdlib": "^2.5 || ^3.0", + "laminas/laminas-http": "^2.5.4", + "laminas/laminas-server": "^2.6.1", + "laminas/laminas-stdlib": "^2.5 || ^3.0", "zendframework/zendxml": "^1.0.2" }, "suggest": { - "zendframework/zend-http": "Zend\\Http component, required to use Zend\\Json\\Server", - "zendframework/zend-server": "Zend\\Server component, required to use Zend\\Json\\Server", - "zendframework/zend-stdlib": "Zend\\Stdlib component, for use with caching Zend\\Json\\Server responses", - "zendframework/zendxml": "To support Zend\\Json\\Json::fromXml() usage" + "laminas/laminas-http": "Laminas\\Http component, required to use Laminas\\Json\\Server", + "laminas/laminas-server": "Laminas\\Server component, required to use Laminas\\Json\\Server", + "laminas/laminas-stdlib": "Laminas\\Stdlib component, for use with caching Laminas\\Json\\Server responses", + "zendframework/zendxml": "To support Laminas\\Json\\Json::fromXml() usage" }, "type": "library", "extra": { @@ -13464,7 +13345,7 @@ }, "autoload": { "psr-4": { - "Zend\\Json\\": "src/" + "Laminas\\Json\\": "src/" } }, "notification-url": "https://packagist.org/downloads/", @@ -13472,7 +13353,7 @@ "BSD-3-Clause" ], "description": "provides convenience methods for serializing native PHP to JSON and decoding JSON to native PHP", - "homepage": "https://github.com/zendframework/zend-json", + "homepage": "https://github.com/laminas/laminas-json", "keywords": [ "json", "zf2" @@ -13480,16 +13361,16 @@ "time": "2016-02-04T21:20:26+00:00" }, { - "name": "zendframework/zend-loader", + "name": "laminas/laminas-loader", "version": "2.6.0", "source": { "type": "git", - "url": "https://github.com/zendframework/zend-loader.git", + "url": "https://github.com/laminas/laminas-loader.git", "reference": "78f11749ea340f6ca316bca5958eef80b38f9b6c" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/zendframework/zend-loader/zipball/78f11749ea340f6ca316bca5958eef80b38f9b6c", + "url": "https://api.github.com/repos/laminas/laminas-loader/zipball/78f11749ea340f6ca316bca5958eef80b38f9b6c", "reference": "78f11749ea340f6ca316bca5958eef80b38f9b6c", "shasum": "" }, @@ -13498,7 +13379,7 @@ }, "require-dev": { "phpunit/phpunit": "^5.7.27 || ^6.5.8 || ^7.1.4", - "zendframework/zend-coding-standard": "~1.0.0" + "laminas/laminas-coding-standard": "~1.0.0" }, "type": "library", "extra": { @@ -13509,7 +13390,7 @@ }, "autoload": { "psr-4": { - "Zend\\Loader\\": "src/" + "Laminas\\Loader\\": "src/" } }, "notification-url": "https://packagist.org/downloads/", @@ -13525,24 +13406,24 @@ "time": "2018-04-30T15:20:54+00:00" }, { - "name": "zendframework/zend-log", + "name": "laminas/laminas-log", "version": "2.10.0", "source": { "type": "git", - "url": "https://github.com/zendframework/zend-log.git", + "url": "https://github.com/laminas/laminas-log.git", "reference": "9cec3b092acb39963659c2f32441cccc56b3f430" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/zendframework/zend-log/zipball/9cec3b092acb39963659c2f32441cccc56b3f430", + "url": "https://api.github.com/repos/laminas/laminas-log/zipball/9cec3b092acb39963659c2f32441cccc56b3f430", "reference": "9cec3b092acb39963659c2f32441cccc56b3f430", "shasum": "" }, "require": { "php": "^5.6 || ^7.0", "psr/log": "^1.0", - "zendframework/zend-servicemanager": "^2.7.5 || ^3.0.3", - "zendframework/zend-stdlib": "^2.7 || ^3.0" + "laminas/laminas-servicemanager": "^2.7.5 || ^3.0.3", + "laminas/laminas-stdlib": "^2.7 || ^3.0" }, "provide": { "psr/log-implementation": "1.0.0" @@ -13550,21 +13431,21 @@ "require-dev": { "mikey179/vfsstream": "^1.6", "phpunit/phpunit": "^5.7.15 || ^6.0.8", - "zendframework/zend-coding-standard": "~1.0.0", - "zendframework/zend-db": "^2.6", - "zendframework/zend-escaper": "^2.5", - "zendframework/zend-filter": "^2.5", - "zendframework/zend-mail": "^2.6.1", - "zendframework/zend-validator": "^2.10.1" + "laminas/laminas-coding-standard": "~1.0.0", + "laminas/laminas-db": "^2.6", + "laminas/laminas-escaper": "^2.5", + "laminas/laminas-filter": "^2.5", + "laminas/laminas-mail": "^2.6.1", + "laminas/laminas-validator": "^2.10.1" }, "suggest": { "ext-mongo": "mongo extension to use Mongo writer", "ext-mongodb": "mongodb extension to use MongoDB writer", - "zendframework/zend-console": "Zend\\Console component to use the RequestID log processor", - "zendframework/zend-db": "Zend\\Db component to use the database log writer", - "zendframework/zend-escaper": "Zend\\Escaper component, for use in the XML log formatter", - "zendframework/zend-mail": "Zend\\Mail component to use the email log writer", - "zendframework/zend-validator": "Zend\\Validator component to block invalid log messages" + "laminas/laminas-console": "Laminas\\Console component to use the RequestID log processor", + "laminas/laminas-db": "Laminas\\Db component to use the database log writer", + "laminas/laminas-escaper": "Laminas\\Escaper component, for use in the XML log formatter", + "laminas/laminas-mail": "Laminas\\Mail component to use the email log writer", + "laminas/laminas-validator": "Laminas\\Validator component to block invalid log messages" }, "type": "library", "extra": { @@ -13573,13 +13454,13 @@ "dev-develop": "2.11.x-dev" }, "zf": { - "component": "Zend\\Log", - "config-provider": "Zend\\Log\\ConfigProvider" + "component": "Laminas\\Log", + "config-provider": "Laminas\\Log\\ConfigProvider" } }, "autoload": { "psr-4": { - "Zend\\Log\\": "src/" + "Laminas\\Log\\": "src/" } }, "notification-url": "https://packagist.org/downloads/", @@ -13587,7 +13468,7 @@ "BSD-3-Clause" ], "description": "component for general purpose logging", - "homepage": "https://github.com/zendframework/zend-log", + "homepage": "https://github.com/laminas/laminas-log", "keywords": [ "log", "logging", @@ -13596,16 +13477,16 @@ "time": "2018-04-09T21:59:51+00:00" }, { - "name": "zendframework/zend-mail", + "name": "laminas/laminas-mail", "version": "2.10.0", "source": { "type": "git", - "url": "https://github.com/zendframework/zend-mail.git", + "url": "https://github.com/laminas/laminas-mail.git", "reference": "d7beb63d5f7144a21ac100072c453e63860cdab8" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/zendframework/zend-mail/zipball/d7beb63d5f7144a21ac100072c453e63860cdab8", + "url": "https://api.github.com/repos/laminas/laminas-mail/zipball/d7beb63d5f7144a21ac100072c453e63860cdab8", "reference": "d7beb63d5f7144a21ac100072c453e63860cdab8", "shasum": "" }, @@ -13613,21 +13494,21 @@ "ext-iconv": "*", "php": "^5.6 || ^7.0", "true/punycode": "^2.1", - "zendframework/zend-loader": "^2.5", - "zendframework/zend-mime": "^2.5", - "zendframework/zend-stdlib": "^2.7 || ^3.0", - "zendframework/zend-validator": "^2.10.2" + "laminas/laminas-loader": "^2.5", + "laminas/laminas-mime": "^2.5", + "laminas/laminas-stdlib": "^2.7 || ^3.0", + "laminas/laminas-validator": "^2.10.2" }, "require-dev": { "phpunit/phpunit": "^5.7.25 || ^6.4.4 || ^7.1.4", - "zendframework/zend-coding-standard": "~1.0.0", - "zendframework/zend-config": "^2.6", - "zendframework/zend-crypt": "^2.6 || ^3.0", - "zendframework/zend-servicemanager": "^2.7.10 || ^3.3.1" + "laminas/laminas-coding-standard": "~1.0.0", + "laminas/laminas-config": "^2.6", + "laminas/laminas-crypt": "^2.6 || ^3.0", + "laminas/laminas-servicemanager": "^2.7.10 || ^3.3.1" }, "suggest": { - "zendframework/zend-crypt": "Crammd5 support in SMTP Auth", - "zendframework/zend-servicemanager": "^2.7.10 || ^3.3.1 when using SMTP to deliver messages" + "laminas/laminas-crypt": "Crammd5 support in SMTP Auth", + "laminas/laminas-servicemanager": "^2.7.10 || ^3.3.1 when using SMTP to deliver messages" }, "type": "library", "extra": { @@ -13636,13 +13517,13 @@ "dev-develop": "2.11.x-dev" }, "zf": { - "component": "Zend\\Mail", - "config-provider": "Zend\\Mail\\ConfigProvider" + "component": "Laminas\\Mail", + "config-provider": "Laminas\\Mail\\ConfigProvider" } }, "autoload": { "psr-4": { - "Zend\\Mail\\": "src/" + "Laminas\\Mail\\": "src/" } }, "notification-url": "https://packagist.org/downloads/", @@ -13658,16 +13539,16 @@ "time": "2018-06-07T13:37:07+00:00" }, { - "name": "zendframework/zend-math", + "name": "laminas/laminas-math", "version": "2.7.1", "source": { "type": "git", - "url": "https://github.com/zendframework/zend-math.git", + "url": "https://github.com/laminas/laminas-math.git", "reference": "1abce074004dacac1a32cd54de94ad47ef960d38" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/zendframework/zend-math/zipball/1abce074004dacac1a32cd54de94ad47ef960d38", + "url": "https://api.github.com/repos/laminas/laminas-math/zipball/1abce074004dacac1a32cd54de94ad47ef960d38", "reference": "1abce074004dacac1a32cd54de94ad47ef960d38", "shasum": "" }, @@ -13682,7 +13563,7 @@ "suggest": { "ext-bcmath": "If using the bcmath functionality", "ext-gmp": "If using the gmp functionality", - "ircmaxell/random-lib": "Fallback random byte generator for Zend\\Math\\Rand if Mcrypt extensions is unavailable" + "ircmaxell/random-lib": "Fallback random byte generator for Laminas\\Math\\Rand if Mcrypt extensions is unavailable" }, "type": "library", "extra": { @@ -13693,14 +13574,14 @@ }, "autoload": { "psr-4": { - "Zend\\Math\\": "src/" + "Laminas\\Math\\": "src/" } }, "notification-url": "https://packagist.org/downloads/", "license": [ "BSD-3-Clause" ], - "homepage": "https://github.com/zendframework/zend-math", + "homepage": "https://github.com/laminas/laminas-math", "keywords": [ "math", "zf2" @@ -13708,30 +13589,30 @@ "time": "2018-12-04T15:34:17+00:00" }, { - "name": "zendframework/zend-mime", + "name": "laminas/laminas-mime", "version": "2.7.1", "source": { "type": "git", - "url": "https://github.com/zendframework/zend-mime.git", + "url": "https://github.com/laminas/laminas-mime.git", "reference": "52ae5fa9f12845cae749271034a2d594f0e4c6f2" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/zendframework/zend-mime/zipball/52ae5fa9f12845cae749271034a2d594f0e4c6f2", + "url": "https://api.github.com/repos/laminas/laminas-mime/zipball/52ae5fa9f12845cae749271034a2d594f0e4c6f2", "reference": "52ae5fa9f12845cae749271034a2d594f0e4c6f2", "shasum": "" }, "require": { "php": "^5.6 || ^7.0", - "zendframework/zend-stdlib": "^2.7 || ^3.0" + "laminas/laminas-stdlib": "^2.7 || ^3.0" }, "require-dev": { "phpunit/phpunit": "^5.7.21 || ^6.3", - "zendframework/zend-coding-standard": "~1.0.0", - "zendframework/zend-mail": "^2.6" + "laminas/laminas-coding-standard": "~1.0.0", + "laminas/laminas-mail": "^2.6" }, "suggest": { - "zendframework/zend-mail": "Zend\\Mail component" + "laminas/laminas-mail": "Laminas\\Mail component" }, "type": "library", "extra": { @@ -13742,7 +13623,7 @@ }, "autoload": { "psr-4": { - "Zend\\Mime\\": "src/" + "Laminas\\Mime\\": "src/" } }, "notification-url": "https://packagist.org/downloads/", @@ -13750,7 +13631,7 @@ "BSD-3-Clause" ], "description": "Create and parse MIME messages and parts", - "homepage": "https://github.com/zendframework/zend-mime", + "homepage": "https://github.com/laminas/laminas-mime", "keywords": [ "ZendFramework", "mime", @@ -13759,39 +13640,39 @@ "time": "2018-05-14T19:02:50+00:00" }, { - "name": "zendframework/zend-modulemanager", + "name": "laminas/laminas-modulemanager", "version": "2.8.2", "source": { "type": "git", - "url": "https://github.com/zendframework/zend-modulemanager.git", + "url": "https://github.com/laminas/laminas-modulemanager.git", "reference": "394df6e12248ac430a312d4693f793ee7120baa6" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/zendframework/zend-modulemanager/zipball/394df6e12248ac430a312d4693f793ee7120baa6", + "url": "https://api.github.com/repos/laminas/laminas-modulemanager/zipball/394df6e12248ac430a312d4693f793ee7120baa6", "reference": "394df6e12248ac430a312d4693f793ee7120baa6", "shasum": "" }, "require": { "php": "^5.6 || ^7.0", - "zendframework/zend-config": "^3.1 || ^2.6", - "zendframework/zend-eventmanager": "^3.2 || ^2.6.3", - "zendframework/zend-stdlib": "^3.1 || ^2.7" + "laminas/laminas-config": "^3.1 || ^2.6", + "laminas/laminas-eventmanager": "^3.2 || ^2.6.3", + "laminas/laminas-stdlib": "^3.1 || ^2.7" }, "require-dev": { "phpunit/phpunit": "^6.0.8 || ^5.7.15", - "zendframework/zend-coding-standard": "~1.0.0", - "zendframework/zend-console": "^2.6", - "zendframework/zend-di": "^2.6", - "zendframework/zend-loader": "^2.5", - "zendframework/zend-mvc": "^3.0 || ^2.7", - "zendframework/zend-servicemanager": "^3.0.3 || ^2.7.5" + "laminas/laminas-coding-standard": "~1.0.0", + "laminas/laminas-console": "^2.6", + "laminas/laminas-di": "^2.6", + "laminas/laminas-loader": "^2.5", + "laminas/laminas-mvc": "^3.0 || ^2.7", + "laminas/laminas-servicemanager": "^3.0.3 || ^2.7.5" }, "suggest": { - "zendframework/zend-console": "Zend\\Console component", - "zendframework/zend-loader": "Zend\\Loader component if you are not using Composer autoloading for your modules", - "zendframework/zend-mvc": "Zend\\Mvc component", - "zendframework/zend-servicemanager": "Zend\\ServiceManager component" + "laminas/laminas-console": "Laminas\\Console component", + "laminas/laminas-loader": "Laminas\\Loader component if you are not using Composer autoloading for your modules", + "laminas/laminas-mvc": "Laminas\\Mvc component", + "laminas/laminas-servicemanager": "Laminas\\ServiceManager component" }, "type": "library", "extra": { @@ -13802,7 +13683,7 @@ }, "autoload": { "psr-4": { - "Zend\\ModuleManager\\": "src/" + "Laminas\\ModuleManager\\": "src/" } }, "notification-url": "https://packagist.org/downloads/", @@ -13810,7 +13691,7 @@ "BSD-3-Clause" ], "description": "Modular application system for zend-mvc applications", - "homepage": "https://github.com/zendframework/zend-modulemanager", + "homepage": "https://github.com/laminas/laminas-modulemanager", "keywords": [ "ZendFramework", "modulemanager", @@ -13819,73 +13700,73 @@ "time": "2017-12-02T06:11:18+00:00" }, { - "name": "zendframework/zend-mvc", + "name": "laminas/laminas-mvc", "version": "2.7.15", "source": { "type": "git", - "url": "https://github.com/zendframework/zend-mvc.git", + "url": "https://github.com/laminas/laminas-mvc.git", "reference": "a8d45689d37a9e4ff4b75ea0b7478fa3d4f9c089" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/zendframework/zend-mvc/zipball/a8d45689d37a9e4ff4b75ea0b7478fa3d4f9c089", + "url": "https://api.github.com/repos/laminas/laminas-mvc/zipball/a8d45689d37a9e4ff4b75ea0b7478fa3d4f9c089", "reference": "a8d45689d37a9e4ff4b75ea0b7478fa3d4f9c089", "shasum": "" }, "require": { "container-interop/container-interop": "^1.1", "php": "^5.5 || ^7.0", - "zendframework/zend-console": "^2.7", - "zendframework/zend-eventmanager": "^2.6.4 || ^3.0", - "zendframework/zend-form": "^2.11", - "zendframework/zend-hydrator": "^1.1 || ^2.4", - "zendframework/zend-psr7bridge": "^0.2", - "zendframework/zend-servicemanager": "^2.7.10 || ^3.0.3", - "zendframework/zend-stdlib": "^2.7.5 || ^3.0" + "laminas/laminas-console": "^2.7", + "laminas/laminas-eventmanager": "^2.6.4 || ^3.0", + "laminas/laminas-form": "^2.11", + "laminas/laminas-hydrator": "^1.1 || ^2.4", + "laminas/laminas-psr7bridge": "^0.2", + "laminas/laminas-servicemanager": "^2.7.10 || ^3.0.3", + "laminas/laminas-stdlib": "^2.7.5 || ^3.0" }, "replace": { - "zendframework/zend-router": "^2.0" + "laminas/laminas-router": "^2.0" }, "require-dev": { "friendsofphp/php-cs-fixer": "1.7.*", "phpunit/phpunit": "^4.8.36", "sebastian/comparator": "^1.2.4", "sebastian/version": "^1.0.4", - "zendframework/zend-authentication": "^2.6", - "zendframework/zend-cache": "^2.8", - "zendframework/zend-di": "^2.6", - "zendframework/zend-filter": "^2.8", - "zendframework/zend-http": "^2.8", - "zendframework/zend-i18n": "^2.8", - "zendframework/zend-inputfilter": "^2.8", - "zendframework/zend-json": "^2.6.1", - "zendframework/zend-log": "^2.9.3", - "zendframework/zend-modulemanager": "^2.8", - "zendframework/zend-serializer": "^2.8", - "zendframework/zend-session": "^2.8.1", - "zendframework/zend-text": "^2.7", - "zendframework/zend-uri": "^2.6", - "zendframework/zend-validator": "^2.10", - "zendframework/zend-view": "^2.9" + "laminas/laminas-authentication": "^2.6", + "laminas/laminas-cache": "^2.8", + "laminas/laminas-di": "^2.6", + "laminas/laminas-filter": "^2.8", + "laminas/laminas-http": "^2.8", + "laminas/laminas-i18n": "^2.8", + "laminas/laminas-inputfilter": "^2.8", + "laminas/laminas-json": "^2.6.1", + "laminas/laminas-log": "^2.9.3", + "laminas/laminas-modulemanager": "^2.8", + "laminas/laminas-serializer": "^2.8", + "laminas/laminas-session": "^2.8.1", + "laminas/laminas-text": "^2.7", + "laminas/laminas-uri": "^2.6", + "laminas/laminas-validator": "^2.10", + "laminas/laminas-view": "^2.9" }, "suggest": { - "zendframework/zend-authentication": "Zend\\Authentication component for Identity plugin", - "zendframework/zend-config": "Zend\\Config component", - "zendframework/zend-di": "Zend\\Di component", - "zendframework/zend-filter": "Zend\\Filter component", - "zendframework/zend-http": "Zend\\Http component", - "zendframework/zend-i18n": "Zend\\I18n component for translatable segments", - "zendframework/zend-inputfilter": "Zend\\Inputfilter component", - "zendframework/zend-json": "Zend\\Json component", - "zendframework/zend-log": "Zend\\Log component", - "zendframework/zend-modulemanager": "Zend\\ModuleManager component", - "zendframework/zend-serializer": "Zend\\Serializer component", - "zendframework/zend-servicemanager-di": "^1.0.1, if using zend-servicemanager v3 and requiring the zend-di integration", - "zendframework/zend-session": "Zend\\Session component for FlashMessenger, PRG, and FPRG plugins", - "zendframework/zend-text": "Zend\\Text component", - "zendframework/zend-uri": "Zend\\Uri component", - "zendframework/zend-validator": "Zend\\Validator component", - "zendframework/zend-view": "Zend\\View component" + "laminas/laminas-authentication": "Laminas\\Authentication component for Identity plugin", + "laminas/laminas-config": "Laminas\\Config component", + "laminas/laminas-di": "Laminas\\Di component", + "laminas/laminas-filter": "Laminas\\Filter component", + "laminas/laminas-http": "Laminas\\Http component", + "laminas/laminas-i18n": "Laminas\\I18n component for translatable segments", + "laminas/laminas-inputfilter": "Zend\\Inputfilter component", + "laminas/laminas-json": "Laminas\\Json component", + "laminas/laminas-log": "Laminas\\Log component", + "laminas/laminas-modulemanager": "Laminas\\ModuleManager component", + "laminas/laminas-serializer": "Laminas\\Serializer component", + "laminas/laminas-servicemanager-di": "^1.0.1, if using zend-servicemanager v3 and requiring the zend-di integration", + "laminas/laminas-session": "Laminas\\Session component for FlashMessenger, PRG, and FPRG plugins", + "laminas/laminas-text": "Laminas\\Text component", + "laminas/laminas-uri": "Laminas\\Uri component", + "laminas/laminas-validator": "Laminas\\Validator component", + "laminas/laminas-view": "Laminas\\View component" }, "type": "library", "extra": { @@ -13899,14 +13780,14 @@ "src/autoload.php" ], "psr-4": { - "Zend\\Mvc\\": "src/" + "Laminas\\Mvc\\": "src/" } }, "notification-url": "https://packagist.org/downloads/", "license": [ "BSD-3-Clause" ], - "homepage": "https://github.com/zendframework/zend-mvc", + "homepage": "https://github.com/laminas/laminas-mvc", "keywords": [ "mvc", "zf2" @@ -13914,24 +13795,24 @@ "time": "2018-05-03T13:13:41+00:00" }, { - "name": "zendframework/zend-psr7bridge", + "name": "laminas/laminas-psr7bridge", "version": "0.2.2", "source": { "type": "git", - "url": "https://github.com/zendframework/zend-psr7bridge.git", + "url": "https://github.com/laminas/laminas-psr7bridge.git", "reference": "86c0b53b0c6381391c4add4a93a56e51d5c74605" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/zendframework/zend-psr7bridge/zipball/86c0b53b0c6381391c4add4a93a56e51d5c74605", + "url": "https://api.github.com/repos/laminas/laminas-psr7bridge/zipball/86c0b53b0c6381391c4add4a93a56e51d5c74605", "reference": "86c0b53b0c6381391c4add4a93a56e51d5c74605", "shasum": "" }, "require": { "php": ">=5.5", "psr/http-message": "^1.0", - "zendframework/zend-diactoros": "^1.1", - "zendframework/zend-http": "^2.5" + "laminas/laminas-diactoros": "^1.1", + "laminas/laminas-http": "^2.5" }, "require-dev": { "phpunit/phpunit": "^4.7", @@ -13946,15 +13827,15 @@ }, "autoload": { "psr-4": { - "Zend\\Psr7Bridge\\": "src/" + "Laminas\\Psr7Bridge\\": "src/" } }, "notification-url": "https://packagist.org/downloads/", "license": [ "BSD-3-Clause" ], - "description": "PSR-7 <-> Zend\\Http bridge", - "homepage": "https://github.com/zendframework/zend-psr7bridge", + "description": "PSR-7 <-> Laminas\\Http bridge", + "homepage": "https://github.com/laminas/laminas-psr7bridge", "keywords": [ "http", "psr", @@ -13963,33 +13844,33 @@ "time": "2016-05-10T21:44:39+00:00" }, { - "name": "zendframework/zend-serializer", + "name": "laminas/laminas-serializer", "version": "2.9.0", "source": { "type": "git", - "url": "https://github.com/zendframework/zend-serializer.git", + "url": "https://github.com/laminas/laminas-serializer.git", "reference": "0172690db48d8935edaf625c4cba38b79719892c" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/zendframework/zend-serializer/zipball/0172690db48d8935edaf625c4cba38b79719892c", + "url": "https://api.github.com/repos/laminas/laminas-serializer/zipball/0172690db48d8935edaf625c4cba38b79719892c", "reference": "0172690db48d8935edaf625c4cba38b79719892c", "shasum": "" }, "require": { "php": "^5.6 || ^7.0", - "zendframework/zend-json": "^2.5 || ^3.0", - "zendframework/zend-stdlib": "^2.7 || ^3.0" + "laminas/laminas-json": "^2.5 || ^3.0", + "laminas/laminas-stdlib": "^2.7 || ^3.0" }, "require-dev": { "phpunit/phpunit": "^5.7.25 || ^6.4.4", - "zendframework/zend-coding-standard": "~1.0.0", - "zendframework/zend-math": "^2.6 || ^3.0", - "zendframework/zend-servicemanager": "^2.7.5 || ^3.0.3" + "laminas/laminas-coding-standard": "~1.0.0", + "laminas/laminas-math": "^2.6 || ^3.0", + "laminas/laminas-servicemanager": "^2.7.5 || ^3.0.3" }, "suggest": { - "zendframework/zend-math": "(^2.6 || ^3.0) To support Python Pickle serialization", - "zendframework/zend-servicemanager": "(^2.7.5 || ^3.0.3) To support plugin manager support" + "laminas/laminas-math": "(^2.6 || ^3.0) To support Python Pickle serialization", + "laminas/laminas-servicemanager": "(^2.7.5 || ^3.0.3) To support plugin manager support" }, "type": "library", "extra": { @@ -13998,13 +13879,13 @@ "dev-develop": "2.10.x-dev" }, "zf": { - "component": "Zend\\Serializer", - "config-provider": "Zend\\Serializer\\ConfigProvider" + "component": "Laminas\\Serializer", + "config-provider": "Laminas\\Serializer\\ConfigProvider" } }, "autoload": { "psr-4": { - "Zend\\Serializer\\": "src/" + "Laminas\\Serializer\\": "src/" } }, "notification-url": "https://packagist.org/downloads/", @@ -14020,27 +13901,27 @@ "time": "2018-05-14T18:45:18+00:00" }, { - "name": "zendframework/zend-server", + "name": "laminas/laminas-server", "version": "2.8.0", "source": { "type": "git", - "url": "https://github.com/zendframework/zend-server.git", + "url": "https://github.com/laminas/laminas-server.git", "reference": "23a2e9a5599c83c05da831cb7c649e8a7809595e" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/zendframework/zend-server/zipball/23a2e9a5599c83c05da831cb7c649e8a7809595e", + "url": "https://api.github.com/repos/laminas/laminas-server/zipball/23a2e9a5599c83c05da831cb7c649e8a7809595e", "reference": "23a2e9a5599c83c05da831cb7c649e8a7809595e", "shasum": "" }, "require": { "php": "^5.6 || ^7.0", - "zendframework/zend-code": "^2.5 || ^3.0", - "zendframework/zend-stdlib": "^2.5 || ^3.0" + "laminas/laminas-code": "^2.5 || ^3.0", + "laminas/laminas-stdlib": "^2.5 || ^3.0" }, "require-dev": { "phpunit/phpunit": "^5.7.27 || ^6.5.8 || ^7.1.4", - "zendframework/zend-coding-standard": "~1.0.0" + "laminas/laminas-coding-standard": "~1.0.0" }, "type": "library", "extra": { @@ -14051,7 +13932,7 @@ }, "autoload": { "psr-4": { - "Zend\\Server\\": "src/" + "Laminas\\Server\\": "src/" } }, "notification-url": "https://packagist.org/downloads/", @@ -14067,16 +13948,16 @@ "time": "2018-04-30T22:21:28+00:00" }, { - "name": "zendframework/zend-servicemanager", + "name": "laminas/laminas-servicemanager", "version": "2.7.11", "source": { "type": "git", - "url": "https://github.com/zendframework/zend-servicemanager.git", + "url": "https://github.com/laminas/laminas-servicemanager.git", "reference": "99ec9ed5d0f15aed9876433c74c2709eb933d4c7" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/zendframework/zend-servicemanager/zipball/99ec9ed5d0f15aed9876433c74c2709eb933d4c7", + "url": "https://api.github.com/repos/laminas/laminas-servicemanager/zipball/99ec9ed5d0f15aed9876433c74c2709eb933d4c7", "reference": "99ec9ed5d0f15aed9876433c74c2709eb933d4c7", "shasum": "" }, @@ -14088,12 +13969,12 @@ "athletic/athletic": "dev-master", "fabpot/php-cs-fixer": "1.7.*", "phpunit/phpunit": "~4.0", - "zendframework/zend-di": "~2.5", - "zendframework/zend-mvc": "~2.5" + "laminas/laminas-di": "~2.5", + "laminas/laminas-mvc": "~2.5" }, "suggest": { "ocramius/proxy-manager": "ProxyManager 0.5.* to handle lazy initialization of services", - "zendframework/zend-di": "Zend\\Di component" + "laminas/laminas-di": "Laminas\\Di component" }, "type": "library", "extra": { @@ -14104,14 +13985,14 @@ }, "autoload": { "psr-4": { - "Zend\\ServiceManager\\": "src/" + "Laminas\\ServiceManager\\": "src/" } }, "notification-url": "https://packagist.org/downloads/", "license": [ "BSD-3-Clause" ], - "homepage": "https://github.com/zendframework/zend-servicemanager", + "homepage": "https://github.com/laminas/laminas-servicemanager", "keywords": [ "servicemanager", "zf2" @@ -14119,43 +14000,43 @@ "time": "2018-06-22T14:49:54+00:00" }, { - "name": "zendframework/zend-session", + "name": "laminas/laminas-session", "version": "2.8.5", "source": { "type": "git", - "url": "https://github.com/zendframework/zend-session.git", + "url": "https://github.com/laminas/laminas-session.git", "reference": "2cfd90e1a2f6b066b9f908599251d8f64f07021b" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/zendframework/zend-session/zipball/2cfd90e1a2f6b066b9f908599251d8f64f07021b", + "url": "https://api.github.com/repos/laminas/laminas-session/zipball/2cfd90e1a2f6b066b9f908599251d8f64f07021b", "reference": "2cfd90e1a2f6b066b9f908599251d8f64f07021b", "shasum": "" }, "require": { "php": "^5.6 || ^7.0", - "zendframework/zend-eventmanager": "^2.6.2 || ^3.0", - "zendframework/zend-stdlib": "^2.7 || ^3.0" + "laminas/laminas-eventmanager": "^2.6.2 || ^3.0", + "laminas/laminas-stdlib": "^2.7 || ^3.0" }, "require-dev": { "container-interop/container-interop": "^1.1", "mongodb/mongodb": "^1.0.1", "php-mock/php-mock-phpunit": "^1.1.2 || ^2.0", "phpunit/phpunit": "^5.7.5 || >=6.0.13 <6.5.0", - "zendframework/zend-cache": "^2.6.1", - "zendframework/zend-coding-standard": "~1.0.0", - "zendframework/zend-db": "^2.7", - "zendframework/zend-http": "^2.5.4", - "zendframework/zend-servicemanager": "^2.7.5 || ^3.0.3", - "zendframework/zend-validator": "^2.6" + "laminas/laminas-cache": "^2.6.1", + "laminas/laminas-coding-standard": "~1.0.0", + "laminas/laminas-db": "^2.7", + "laminas/laminas-http": "^2.5.4", + "laminas/laminas-servicemanager": "^2.7.5 || ^3.0.3", + "laminas/laminas-validator": "^2.6" }, "suggest": { "mongodb/mongodb": "If you want to use the MongoDB session save handler", - "zendframework/zend-cache": "Zend\\Cache component", - "zendframework/zend-db": "Zend\\Db component", - "zendframework/zend-http": "Zend\\Http component", - "zendframework/zend-servicemanager": "Zend\\ServiceManager component", - "zendframework/zend-validator": "Zend\\Validator component" + "laminas/laminas-cache": "Laminas\\Cache component", + "laminas/laminas-db": "Laminas\\Db component", + "laminas/laminas-http": "Laminas\\Http component", + "laminas/laminas-servicemanager": "Laminas\\ServiceManager component", + "laminas/laminas-validator": "Laminas\\Validator component" }, "type": "library", "extra": { @@ -14164,13 +14045,13 @@ "dev-develop": "2.9-dev" }, "zf": { - "component": "Zend\\Session", - "config-provider": "Zend\\Session\\ConfigProvider" + "component": "Laminas\\Session", + "config-provider": "Laminas\\Session\\ConfigProvider" } }, "autoload": { "psr-4": { - "Zend\\Session\\": "src/" + "Laminas\\Session\\": "src/" } }, "notification-url": "https://packagist.org/downloads/", @@ -14186,34 +14067,34 @@ "time": "2018-02-22T16:33:54+00:00" }, { - "name": "zendframework/zend-soap", + "name": "laminas/laminas-soap", "version": "2.7.0", "source": { "type": "git", - "url": "https://github.com/zendframework/zend-soap.git", + "url": "https://github.com/laminas/laminas-soap.git", "reference": "af03c32f0db2b899b3df8cfe29aeb2b49857d284" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/zendframework/zend-soap/zipball/af03c32f0db2b899b3df8cfe29aeb2b49857d284", + "url": "https://api.github.com/repos/laminas/laminas-soap/zipball/af03c32f0db2b899b3df8cfe29aeb2b49857d284", "reference": "af03c32f0db2b899b3df8cfe29aeb2b49857d284", "shasum": "" }, "require": { "ext-soap": "*", "php": "^5.6 || ^7.0", - "zendframework/zend-server": "^2.6.1", - "zendframework/zend-stdlib": "^2.7 || ^3.0", - "zendframework/zend-uri": "^2.5.2" + "laminas/laminas-server": "^2.6.1", + "laminas/laminas-stdlib": "^2.7 || ^3.0", + "laminas/laminas-uri": "^2.5.2" }, "require-dev": { "phpunit/phpunit": "^5.7.21 || ^6.3", - "zendframework/zend-coding-standard": "~1.0.0", - "zendframework/zend-config": "^2.6", - "zendframework/zend-http": "^2.5.4" + "laminas/laminas-coding-standard": "~1.0.0", + "laminas/laminas-config": "^2.6", + "laminas/laminas-http": "^2.5.4" }, "suggest": { - "zendframework/zend-http": "Zend\\Http component" + "laminas/laminas-http": "Laminas\\Http component" }, "type": "library", "extra": { @@ -14224,14 +14105,14 @@ }, "autoload": { "psr-4": { - "Zend\\Soap\\": "src/" + "Laminas\\Soap\\": "src/" } }, "notification-url": "https://packagist.org/downloads/", "license": [ "BSD-3-Clause" ], - "homepage": "https://github.com/zendframework/zend-soap", + "homepage": "https://github.com/laminas/laminas-soap", "keywords": [ "soap", "zf2" @@ -14239,39 +14120,39 @@ "time": "2018-01-29T17:51:26+00:00" }, { - "name": "zendframework/zend-stdlib", + "name": "laminas/laminas-stdlib", "version": "2.7.7", "source": { "type": "git", - "url": "https://github.com/zendframework/zend-stdlib.git", + "url": "https://github.com/laminas/laminas-stdlib.git", "reference": "0e44eb46788f65e09e077eb7f44d2659143bcc1f" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/zendframework/zend-stdlib/zipball/0e44eb46788f65e09e077eb7f44d2659143bcc1f", + "url": "https://api.github.com/repos/laminas/laminas-stdlib/zipball/0e44eb46788f65e09e077eb7f44d2659143bcc1f", "reference": "0e44eb46788f65e09e077eb7f44d2659143bcc1f", "shasum": "" }, "require": { "php": "^5.5 || ^7.0", - "zendframework/zend-hydrator": "~1.1" + "laminas/laminas-hydrator": "~1.1" }, "require-dev": { "athletic/athletic": "~0.1", "fabpot/php-cs-fixer": "1.7.*", "phpunit/phpunit": "~4.0", - "zendframework/zend-config": "~2.5", - "zendframework/zend-eventmanager": "~2.5", - "zendframework/zend-filter": "~2.5", - "zendframework/zend-inputfilter": "~2.5", - "zendframework/zend-serializer": "~2.5", - "zendframework/zend-servicemanager": "~2.5" + "laminas/laminas-config": "~2.5", + "laminas/laminas-eventmanager": "~2.5", + "laminas/laminas-filter": "~2.5", + "laminas/laminas-inputfilter": "~2.5", + "laminas/laminas-serializer": "~2.5", + "laminas/laminas-servicemanager": "~2.5" }, "suggest": { - "zendframework/zend-eventmanager": "To support aggregate hydrator usage", - "zendframework/zend-filter": "To support naming strategy hydrator usage", - "zendframework/zend-serializer": "Zend\\Serializer component", - "zendframework/zend-servicemanager": "To support hydrator plugin manager usage" + "laminas/laminas-eventmanager": "To support aggregate hydrator usage", + "laminas/laminas-filter": "To support naming strategy hydrator usage", + "laminas/laminas-serializer": "Laminas\\Serializer component", + "laminas/laminas-servicemanager": "To support hydrator plugin manager usage" }, "type": "library", "extra": { @@ -14283,14 +14164,14 @@ }, "autoload": { "psr-4": { - "Zend\\Stdlib\\": "src/" + "Laminas\\Stdlib\\": "src/" } }, "notification-url": "https://packagist.org/downloads/", "license": [ "BSD-3-Clause" ], - "homepage": "https://github.com/zendframework/zend-stdlib", + "homepage": "https://github.com/laminas/laminas-stdlib", "keywords": [ "stdlib", "zf2" @@ -14298,28 +14179,28 @@ "time": "2016-04-12T21:17:31+00:00" }, { - "name": "zendframework/zend-text", + "name": "laminas/laminas-text", "version": "2.7.0", "source": { "type": "git", - "url": "https://github.com/zendframework/zend-text.git", + "url": "https://github.com/laminas/laminas-text.git", "reference": "ca987dd4594f5f9508771fccd82c89bc7fbb39ac" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/zendframework/zend-text/zipball/ca987dd4594f5f9508771fccd82c89bc7fbb39ac", + "url": "https://api.github.com/repos/laminas/laminas-text/zipball/ca987dd4594f5f9508771fccd82c89bc7fbb39ac", "reference": "ca987dd4594f5f9508771fccd82c89bc7fbb39ac", "shasum": "" }, "require": { "php": "^5.6 || ^7.0", - "zendframework/zend-servicemanager": "^2.7.5 || ^3.0.3", - "zendframework/zend-stdlib": "^2.7 || ^3.0" + "laminas/laminas-servicemanager": "^2.7.5 || ^3.0.3", + "laminas/laminas-stdlib": "^2.7 || ^3.0" }, "require-dev": { "phpunit/phpunit": "^5.7.27 || ^6.5.8 || ^7.1.4", - "zendframework/zend-coding-standard": "~1.0.0", - "zendframework/zend-config": "^2.6" + "laminas/laminas-coding-standard": "~1.0.0", + "laminas/laminas-config": "^2.6" }, "type": "library", "extra": { @@ -14330,7 +14211,7 @@ }, "autoload": { "psr-4": { - "Zend\\Text\\": "src/" + "Laminas\\Text\\": "src/" } }, "notification-url": "https://packagist.org/downloads/", @@ -14346,27 +14227,27 @@ "time": "2018-04-30T14:55:10+00:00" }, { - "name": "zendframework/zend-uri", + "name": "laminas/laminas-uri", "version": "2.7.0", "source": { "type": "git", - "url": "https://github.com/zendframework/zend-uri.git", + "url": "https://github.com/laminas/laminas-uri.git", "reference": "b2785cd38fe379a784645449db86f21b7739b1ee" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/zendframework/zend-uri/zipball/b2785cd38fe379a784645449db86f21b7739b1ee", + "url": "https://api.github.com/repos/laminas/laminas-uri/zipball/b2785cd38fe379a784645449db86f21b7739b1ee", "reference": "b2785cd38fe379a784645449db86f21b7739b1ee", "shasum": "" }, "require": { "php": "^5.6 || ^7.0", - "zendframework/zend-escaper": "^2.5", - "zendframework/zend-validator": "^2.10" + "laminas/laminas-escaper": "^2.5", + "laminas/laminas-validator": "^2.10" }, "require-dev": { "phpunit/phpunit": "^5.7.27 || ^6.5.8 || ^7.1.4", - "zendframework/zend-coding-standard": "~1.0.0" + "laminas/laminas-coding-standard": "~1.0.0" }, "type": "library", "extra": { @@ -14377,7 +14258,7 @@ }, "autoload": { "psr-4": { - "Zend\\Uri\\": "src/" + "Laminas\\Uri\\": "src/" } }, "notification-url": "https://packagist.org/downloads/", @@ -14393,49 +14274,49 @@ "time": "2019-02-27T21:39:04+00:00" }, { - "name": "zendframework/zend-validator", + "name": "laminas/laminas-validator", "version": "2.11.1", "source": { "type": "git", - "url": "https://github.com/zendframework/zend-validator.git", + "url": "https://github.com/laminas/laminas-validator.git", "reference": "3c28dfe4e5951ba38059cea895244d9d206190b3" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/zendframework/zend-validator/zipball/3c28dfe4e5951ba38059cea895244d9d206190b3", + "url": "https://api.github.com/repos/laminas/laminas-validator/zipball/3c28dfe4e5951ba38059cea895244d9d206190b3", "reference": "3c28dfe4e5951ba38059cea895244d9d206190b3", "shasum": "" }, "require": { "container-interop/container-interop": "^1.1", "php": "^5.6 || ^7.0", - "zendframework/zend-stdlib": "^2.7.6 || ^3.1" + "laminas/laminas-stdlib": "^2.7.6 || ^3.1" }, "require-dev": { "phpunit/phpunit": "^6.0.8 || ^5.7.15", "psr/http-message": "^1.0", - "zendframework/zend-cache": "^2.6.1", - "zendframework/zend-coding-standard": "~1.0.0", - "zendframework/zend-config": "^2.6", - "zendframework/zend-db": "^2.7", - "zendframework/zend-filter": "^2.6", - "zendframework/zend-http": "^2.5.4", - "zendframework/zend-i18n": "^2.6", - "zendframework/zend-math": "^2.6", - "zendframework/zend-servicemanager": "^2.7.5 || ^3.0.3", - "zendframework/zend-session": "^2.8", - "zendframework/zend-uri": "^2.5" + "laminas/laminas-cache": "^2.6.1", + "laminas/laminas-coding-standard": "~1.0.0", + "laminas/laminas-config": "^2.6", + "laminas/laminas-db": "^2.7", + "laminas/laminas-filter": "^2.6", + "laminas/laminas-http": "^2.5.4", + "laminas/laminas-i18n": "^2.6", + "laminas/laminas-math": "^2.6", + "laminas/laminas-servicemanager": "^2.7.5 || ^3.0.3", + "laminas/laminas-session": "^2.8", + "laminas/laminas-uri": "^2.5" }, "suggest": { "psr/http-message": "psr/http-message, required when validating PSR-7 UploadedFileInterface instances via the Upload and UploadFile validators", - "zendframework/zend-db": "Zend\\Db component, required by the (No)RecordExists validator", - "zendframework/zend-filter": "Zend\\Filter component, required by the Digits validator", - "zendframework/zend-i18n": "Zend\\I18n component to allow translation of validation error messages", - "zendframework/zend-i18n-resources": "Translations of validator messages", - "zendframework/zend-math": "Zend\\Math component, required by the Csrf validator", - "zendframework/zend-servicemanager": "Zend\\ServiceManager component to allow using the ValidatorPluginManager and validator chains", - "zendframework/zend-session": "Zend\\Session component, ^2.8; required by the Csrf validator", - "zendframework/zend-uri": "Zend\\Uri component, required by the Uri and Sitemap\\Loc validators" + "laminas/laminas-db": "Laminas\\Db component, required by the (No)RecordExists validator", + "laminas/laminas-filter": "Laminas\\Filter component, required by the Digits validator", + "laminas/laminas-i18n": "Laminas\\I18n component to allow translation of validation error messages", + "laminas/laminas-i18n-resources": "Translations of validator messages", + "laminas/laminas-math": "Laminas\\Math component, required by the Csrf validator", + "laminas/laminas-servicemanager": "Laminas\\ServiceManager component to allow using the ValidatorPluginManager and validator chains", + "laminas/laminas-session": "Laminas\\Session component, ^2.8; required by the Csrf validator", + "laminas/laminas-uri": "Laminas\\Uri component, required by the Uri and Sitemap\\Loc validators" }, "type": "library", "extra": { @@ -14444,13 +14325,13 @@ "dev-develop": "2.12.x-dev" }, "zf": { - "component": "Zend\\Validator", - "config-provider": "Zend\\Validator\\ConfigProvider" + "component": "Laminas\\Validator", + "config-provider": "Laminas\\Validator\\ConfigProvider" } }, "autoload": { "psr-4": { - "Zend\\Validator\\": "src/" + "Laminas\\Validator\\": "src/" } }, "notification-url": "https://packagist.org/downloads/", @@ -14458,7 +14339,7 @@ "BSD-3-Clause" ], "description": "provides a set of commonly needed validators", - "homepage": "https://github.com/zendframework/zend-validator", + "homepage": "https://github.com/laminas/laminas-validator", "keywords": [ "validator", "zf2" @@ -14466,64 +14347,64 @@ "time": "2019-01-29T22:26:39+00:00" }, { - "name": "zendframework/zend-view", + "name": "laminas/laminas-view", "version": "2.10.1", "source": { "type": "git", - "url": "https://github.com/zendframework/zend-view.git", + "url": "https://github.com/laminas/laminas-view.git", "reference": "c1a3f2043fb75b5983ab9adfc369ae396601be7e" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/zendframework/zend-view/zipball/c1a3f2043fb75b5983ab9adfc369ae396601be7e", + "url": "https://api.github.com/repos/laminas/laminas-view/zipball/c1a3f2043fb75b5983ab9adfc369ae396601be7e", "reference": "c1a3f2043fb75b5983ab9adfc369ae396601be7e", "shasum": "" }, "require": { "php": "^5.6 || ^7.0", - "zendframework/zend-eventmanager": "^2.6.2 || ^3.0", - "zendframework/zend-json": "^2.6.1", - "zendframework/zend-loader": "^2.5", - "zendframework/zend-stdlib": "^2.7 || ^3.0" + "laminas/laminas-eventmanager": "^2.6.2 || ^3.0", + "laminas/laminas-json": "^2.6.1", + "laminas/laminas-loader": "^2.5", + "laminas/laminas-stdlib": "^2.7 || ^3.0" }, "require-dev": { "phpunit/phpunit": "^5.7.15 || ^6.0.8", - "zendframework/zend-authentication": "^2.5", - "zendframework/zend-cache": "^2.6.1", - "zendframework/zend-coding-standard": "~1.0.0", - "zendframework/zend-config": "^2.6", - "zendframework/zend-console": "^2.6", - "zendframework/zend-escaper": "^2.5", - "zendframework/zend-feed": "^2.7", - "zendframework/zend-filter": "^2.6.1", - "zendframework/zend-http": "^2.5.4", - "zendframework/zend-i18n": "^2.6", - "zendframework/zend-log": "^2.7", - "zendframework/zend-modulemanager": "^2.7.1", - "zendframework/zend-mvc": "^2.7.14 || ^3.0", - "zendframework/zend-navigation": "^2.5", - "zendframework/zend-paginator": "^2.5", - "zendframework/zend-permissions-acl": "^2.6", - "zendframework/zend-router": "^3.0.1", - "zendframework/zend-serializer": "^2.6.1", - "zendframework/zend-servicemanager": "^2.7.5 || ^3.0.3", - "zendframework/zend-session": "^2.8.1", - "zendframework/zend-uri": "^2.5" + "laminas/laminas-authentication": "^2.5", + "laminas/laminas-cache": "^2.6.1", + "laminas/laminas-coding-standard": "~1.0.0", + "laminas/laminas-config": "^2.6", + "laminas/laminas-console": "^2.6", + "laminas/laminas-escaper": "^2.5", + "laminas/laminas-feed": "^2.7", + "laminas/laminas-filter": "^2.6.1", + "laminas/laminas-http": "^2.5.4", + "laminas/laminas-i18n": "^2.6", + "laminas/laminas-log": "^2.7", + "laminas/laminas-modulemanager": "^2.7.1", + "laminas/laminas-mvc": "^2.7.14 || ^3.0", + "laminas/laminas-navigation": "^2.5", + "laminas/laminas-paginator": "^2.5", + "laminas/laminas-permissions-acl": "^2.6", + "laminas/laminas-router": "^3.0.1", + "laminas/laminas-serializer": "^2.6.1", + "laminas/laminas-servicemanager": "^2.7.5 || ^3.0.3", + "laminas/laminas-session": "^2.8.1", + "laminas/laminas-uri": "^2.5" }, "suggest": { - "zendframework/zend-authentication": "Zend\\Authentication component", - "zendframework/zend-escaper": "Zend\\Escaper component", - "zendframework/zend-feed": "Zend\\Feed component", - "zendframework/zend-filter": "Zend\\Filter component", - "zendframework/zend-http": "Zend\\Http component", - "zendframework/zend-i18n": "Zend\\I18n component", - "zendframework/zend-mvc": "Zend\\Mvc component", - "zendframework/zend-mvc-plugin-flashmessenger": "zend-mvc-plugin-flashmessenger component, if you want to use the FlashMessenger view helper with zend-mvc versions 3 and up", - "zendframework/zend-navigation": "Zend\\Navigation component", - "zendframework/zend-paginator": "Zend\\Paginator component", - "zendframework/zend-permissions-acl": "Zend\\Permissions\\Acl component", - "zendframework/zend-servicemanager": "Zend\\ServiceManager component", - "zendframework/zend-uri": "Zend\\Uri component" + "laminas/laminas-authentication": "Laminas\\Authentication component", + "laminas/laminas-escaper": "Laminas\\Escaper component", + "laminas/laminas-feed": "Laminas\\Feed component", + "laminas/laminas-filter": "Laminas\\Filter component", + "laminas/laminas-http": "Laminas\\Http component", + "laminas/laminas-i18n": "Laminas\\I18n component", + "laminas/laminas-mvc": "Laminas\\Mvc component", + "laminas/laminas-mvc-plugin-flashmessenger": "zend-mvc-plugin-flashmessenger component, if you want to use the FlashMessenger view helper with zend-mvc versions 3 and up", + "laminas/laminas-navigation": "Laminas\\Navigation component", + "laminas/laminas-paginator": "Laminas\\Paginator component", + "laminas/laminas-permissions-acl": "Laminas\\Permissions\\Acl component", + "laminas/laminas-servicemanager": "Laminas\\ServiceManager component", + "laminas/laminas-uri": "Laminas\\Uri component" }, "bin": [ "bin/templatemap_generator.php" @@ -14537,7 +14418,7 @@ }, "autoload": { "psr-4": { - "Zend\\View\\": "src/" + "Laminas\\View\\": "src/" } }, "notification-url": "https://packagist.org/downloads/", @@ -14545,7 +14426,7 @@ "BSD-3-Clause" ], "description": "provides a system of helpers, output filters, and variable escaping", - "homepage": "https://github.com/zendframework/zend-view", + "homepage": "https://github.com/laminas/laminas-view", "keywords": [ "view", "zf2" diff --git a/dev/tests/integration/testsuite/Magento/Framework/Css/PreProcessor/_files/code/Magento/Other/registration.php b/dev/tests/integration/testsuite/Magento/Framework/Css/PreProcessor/_files/code/Magento/Other/registration.php index 1b40e66b1f7a1..34fb5831076b8 100644 --- a/dev/tests/integration/testsuite/Magento/Framework/Css/PreProcessor/_files/code/Magento/Other/registration.php +++ b/dev/tests/integration/testsuite/Magento/Framework/Css/PreProcessor/_files/code/Magento/Other/registration.php @@ -4,6 +4,6 @@ * See COPYING.txt for license details. */ -use \Magento\Framework\Component\ComponentRegistrar; +use Magento\Framework\Component\ComponentRegistrar; ComponentRegistrar::register(ComponentRegistrar::MODULE, 'MagentoFrameworkCssTest_Other', __DIR__); diff --git a/dev/tests/integration/testsuite/Magento/Framework/Css/PreProcessor/_files/code/Magento/Third/registration.php b/dev/tests/integration/testsuite/Magento/Framework/Css/PreProcessor/_files/code/Magento/Third/registration.php index dfc0010056f6c..add6e676f220c 100644 --- a/dev/tests/integration/testsuite/Magento/Framework/Css/PreProcessor/_files/code/Magento/Third/registration.php +++ b/dev/tests/integration/testsuite/Magento/Framework/Css/PreProcessor/_files/code/Magento/Third/registration.php @@ -4,6 +4,6 @@ * See COPYING.txt for license details. */ -use \Magento\Framework\Component\ComponentRegistrar; +use Magento\Framework\Component\ComponentRegistrar; ComponentRegistrar::register(ComponentRegistrar::MODULE, 'MagentoFrameworkCssTest_Third', __DIR__); diff --git a/dev/tests/integration/testsuite/Magento/Framework/Css/PreProcessor/_files/design/frontend/Test/default/registration.php b/dev/tests/integration/testsuite/Magento/Framework/Css/PreProcessor/_files/design/frontend/Test/default/registration.php index 1c605694456d2..2fc5a738b3c66 100644 --- a/dev/tests/integration/testsuite/Magento/Framework/Css/PreProcessor/_files/design/frontend/Test/default/registration.php +++ b/dev/tests/integration/testsuite/Magento/Framework/Css/PreProcessor/_files/design/frontend/Test/default/registration.php @@ -4,6 +4,6 @@ * See COPYING.txt for license details. */ -use \Magento\Framework\Component\ComponentRegistrar; +use Magento\Framework\Component\ComponentRegistrar; ComponentRegistrar::register(ComponentRegistrar::THEME, 'frontend/FrameworkCssTest/default', __DIR__); diff --git a/dev/tests/integration/testsuite/Magento/Framework/Css/PreProcessor/_files/design/frontend/Test/parent/registration.php b/dev/tests/integration/testsuite/Magento/Framework/Css/PreProcessor/_files/design/frontend/Test/parent/registration.php index e8b4db3543611..d856d3cd7be29 100644 --- a/dev/tests/integration/testsuite/Magento/Framework/Css/PreProcessor/_files/design/frontend/Test/parent/registration.php +++ b/dev/tests/integration/testsuite/Magento/Framework/Css/PreProcessor/_files/design/frontend/Test/parent/registration.php @@ -4,6 +4,6 @@ * See COPYING.txt for license details. */ -use \Magento\Framework\Component\ComponentRegistrar; +use Magento\Framework\Component\ComponentRegistrar; ComponentRegistrar::register(ComponentRegistrar::THEME, 'frontend/FrameworkCssTest/parent', __DIR__); diff --git a/dev/tests/integration/testsuite/Magento/Framework/DataObject/IdentityValidatorTest.php b/dev/tests/integration/testsuite/Magento/Framework/DataObject/IdentityValidatorTest.php new file mode 100644 index 0000000000000..15f54ba01a795 --- /dev/null +++ b/dev/tests/integration/testsuite/Magento/Framework/DataObject/IdentityValidatorTest.php @@ -0,0 +1,41 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +namespace Magento\Framework\DataObject; + +class IdentityValidatorTest extends \PHPUnit\Framework\TestCase +{ + const VALID_UUID = 'fe563e12-cf9d-4faf-82cd-96e011b557b7'; + const INVALID_UUID = 'abcdef'; + + /** + * @var IdentityValidator + */ + protected $identityValidator; + + protected function setUp() + { + $this->identityValidator = \Magento\TestFramework\Helper\Bootstrap::getObjectManager() + ->get(IdentityValidator::class); + } + + public function testIsValid() + { + $isValid = $this->identityValidator->isValid(self::VALID_UUID); + $this->assertEquals(true, $isValid); + } + + public function testIsNotValid() + { + $isValid = $this->identityValidator->isValid(self::INVALID_UUID); + $this->assertEquals(false, $isValid); + } + + public function testEmptyValue() + { + $isValid = $this->identityValidator->isValid(''); + $this->assertEquals(false, $isValid); + } +} diff --git a/dev/tests/integration/testsuite/Magento/Framework/Filesystem/Directory/ReadTest.php b/dev/tests/integration/testsuite/Magento/Framework/Filesystem/Directory/ReadTest.php index bc77eeb932c9a..e04063eabc36b 100644 --- a/dev/tests/integration/testsuite/Magento/Framework/Filesystem/Directory/ReadTest.php +++ b/dev/tests/integration/testsuite/Magento/Framework/Filesystem/Directory/ReadTest.php @@ -73,17 +73,17 @@ public function testGetRelativePathOutside() $exceptions = 0; $dir = $this->getDirectoryInstance('foo'); try { - $dir->getRelativePath(__DIR__ .'/ReadTest.php'); + $dir->getRelativePath(__DIR__ . '/ReadTest.php'); } catch (ValidatorException $exception) { $exceptions++; } try { - $dir->getRelativePath(__DIR__ .'//./..////Directory/ReadTest.php'); + $dir->getRelativePath(__DIR__ . '//./..////Directory/ReadTest.php'); } catch (ValidatorException $exception) { $exceptions++; } try { - $dir->getRelativePath(__DIR__ .'\..\Directory\ReadTest.php'); + $dir->getRelativePath(__DIR__ . '\..\Directory\ReadTest.php'); } catch (ValidatorException $exception) { $exceptions++; } @@ -222,7 +222,13 @@ public function testIsExist($dirPath, $path, $exists) */ public function existsProvider() { - return [['foo', 'bar', true], ['foo', 'bar/baz/', true], ['foo', 'bar/notexists', false]]; + return [ + ['foo', 'bar', true], + ['foo', 'bar/baz/', true], + ['foo', 'bar/notexists', false], + ['foo', 'foo/../bar/', true], + ['foo', 'foo/../notexists/', false] + ]; } public function testIsExistOutside() diff --git a/dev/tests/integration/testsuite/Magento/Framework/Filesystem/Driver/FileTest.php b/dev/tests/integration/testsuite/Magento/Framework/Filesystem/Driver/FileTest.php index 5f53e62165502..5dec995f83827 100644 --- a/dev/tests/integration/testsuite/Magento/Framework/Filesystem/Driver/FileTest.php +++ b/dev/tests/integration/testsuite/Magento/Framework/Filesystem/Driver/FileTest.php @@ -5,11 +5,17 @@ * Copyright © Magento, Inc. All rights reserved. * See COPYING.txt for license details. */ +declare(strict_types=1); + namespace Magento\Framework\Filesystem\Driver; use Magento\Framework\Exception\FileSystemException; +use PHPUnit\Framework\TestCase; -class FileTest extends \PHPUnit\Framework\TestCase +/** + * Verify File class + */ +class FileTest extends TestCase { /** * @var File @@ -50,16 +56,46 @@ public function setUp() /** * @inheritdoc + * + * @return void */ - protected function tearDown() + protected function tearDown(): void { $this->removeGeneratedDirectory(); } + /** + * Tests read directory with symlynked folders. + * + * @return void + */ + public function testReadDirectoryRecursivelyWithSymlinkedFolders(): void + { + $sourceDirectory = $this->generatedPath . '/source'; + $destinationDirectory = $this->generatedPath . '/destination'; + + $this->driver->createDirectory($sourceDirectory); + $this->driver->createDirectory($sourceDirectory . '/directory1'); + $this->driver->createDirectory($destinationDirectory); + + $linkName = $destinationDirectory . '/link'; + $this->driver->symlink($sourceDirectory, $linkName); + + $paths = [ + $destinationDirectory . '/link' . '/directory1', + $destinationDirectory . '/link' + + ]; + $actual = $this->driver->readDirectoryRecursively($destinationDirectory); + $this->assertEquals($paths, $actual); + } + /** * Tests directory recursive read. + * + * @return void */ - public function testReadDirectoryRecursively() + public function testReadDirectoryRecursively(): void { $paths = [ 'foo/bar', @@ -77,9 +113,10 @@ public function testReadDirectoryRecursively() /** * Tests directory reading exception. * + * @return void * @expectedException \Magento\Framework\Exception\FileSystemException */ - public function testReadDirectoryRecursivelyFailure() + public function testReadDirectoryRecursivelyFailure(): void { $this->driver->readDirectoryRecursively($this->getTestPath('not-existing-directory')); } @@ -88,8 +125,9 @@ public function testReadDirectoryRecursivelyFailure() * Tests of directory creating. * * @throws FileSystemException + * @return void */ - public function testCreateDirectory() + public function testCreateDirectory(): void { $generatedPath = $this->getTestPath('generated/roo/bar/baz/foo'); $generatedPathBase = $this->getTestPath('generated'); @@ -123,6 +161,18 @@ public function testSymlinks(): void self::assertTrue($this->driver->deleteDirectory($linkName)); } + /** + * Verify file put content without content. + * + * @return void + * @throws FileSystemException + */ + public function testFilePutWithoutContents(): void + { + $path = $this->absolutePath . 'foo/file_three.txt'; + $this->assertEquals(0, $this->driver->filePutContents($path, '')); + } + /** * Remove generated directories. * diff --git a/dev/tests/integration/testsuite/Magento/Framework/Filesystem/Io/FileTest.php b/dev/tests/integration/testsuite/Magento/Framework/Filesystem/Io/FileTest.php new file mode 100644 index 0000000000000..39503ec87f8e6 --- /dev/null +++ b/dev/tests/integration/testsuite/Magento/Framework/Filesystem/Io/FileTest.php @@ -0,0 +1,92 @@ +<?php +/** + * Test for \Magento\Framework\Filesystem\Io\File + * + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +declare(strict_types=1); + +namespace Magento\Framework\Filesystem\Io; + +use Magento\Framework\Exception\FileSystemException; +use Magento\TestFramework\Helper\Bootstrap; +use PHPUnit\Framework\TestCase; + +/** + * Verify filesystem client + */ +class FileTest extends TestCase +{ + + /** + * @var File + */ + private $io; + + /** + * @var String + */ + private $absolutePath; + + /** + * @var String + */ + private $generatedPath; + + /** + * @inheritDoc + */ + public function setUp() + { + $this->io = new File(); + $this->absolutePath = Bootstrap::getInstance()->getAppTempDir(); + $this->generatedPath = $this->getTestPath('/rollback_test_'); + $this->io->mkdir($this->generatedPath); + } + + /** + * @inheritdoc + * + * @return void + */ + protected function tearDown(): void + { + $this->removeGeneratedDirectory(); + } + + /** + * Verify file put without content. + * + * @return void + */ + public function testWrite(): void + { + $path = $this->generatedPath . '/file_three.txt'; + $this->assertEquals(0, $this->io->write($path, '', 0444)); + $this->assertEquals(false, is_writable($path)); + } + + /** + * Returns relative path for the test. + * + * @param $relativePath + * @return string + */ + protected function getTestPath($relativePath): string + { + return $this->absolutePath . $relativePath . time(); + } + + /** + * Remove generated directories. + * + * @return void + */ + private function removeGeneratedDirectory(): void + { + if (is_dir($this->generatedPath)) { + $this->io->rmdir($this->generatedPath, true); + } + } +} diff --git a/dev/tests/integration/testsuite/Magento/Framework/GraphQl/Config/GraphQlReaderTest.php b/dev/tests/integration/testsuite/Magento/Framework/GraphQl/Config/GraphQlReaderTest.php index 2ffce44b32cfe..d5eb962b7cb91 100644 --- a/dev/tests/integration/testsuite/Magento/Framework/GraphQl/Config/GraphQlReaderTest.php +++ b/dev/tests/integration/testsuite/Magento/Framework/GraphQl/Config/GraphQlReaderTest.php @@ -57,10 +57,12 @@ protected function setUp() ['fileResolver' => $fileResolverMock] ); $reader = $this->objectManager->create( + // phpstan:ignore \Magento\Framework\GraphQlSchemaStitching\Reader::class, ['readers' => ['graphql_reader' => $graphQlReader]] ); $data = $this->objectManager->create( + // phpstan:ignore \Magento\Framework\GraphQl\Config\Data ::class, ['reader' => $reader] ); @@ -183,7 +185,7 @@ enumValues(includeDeprecated: true) { $request->setPathInfo('/graphql'); $request->setMethod('POST'); $request->setContent(json_encode($postData)); - $headers = $this->objectManager->create(\Zend\Http\Headers::class) + $headers = $this->objectManager->create(\Laminas\Http\Headers::class) ->addHeaders(['Content-Type' => 'application/json']); $request->setHeaders($headers); diff --git a/dev/tests/integration/testsuite/Magento/Framework/HTTP/HeaderTest.php b/dev/tests/integration/testsuite/Magento/Framework/HTTP/HeaderTest.php index 6c6b0b4aafba9..1bd182c20b290 100644 --- a/dev/tests/integration/testsuite/Magento/Framework/HTTP/HeaderTest.php +++ b/dev/tests/integration/testsuite/Magento/Framework/HTTP/HeaderTest.php @@ -5,7 +5,7 @@ */ namespace Magento\Framework\HTTP; -use Zend\Stdlib\Parameters; +use Laminas\Stdlib\Parameters; class HeaderTest extends \PHPUnit\Framework\TestCase { diff --git a/dev/tests/integration/testsuite/Magento/Framework/Search/_files/product_configurable_with_out-of-stock_child.php b/dev/tests/integration/testsuite/Magento/Framework/Search/_files/product_configurable_with_out-of-stock_child.php new file mode 100644 index 0000000000000..e46228e52a117 --- /dev/null +++ b/dev/tests/integration/testsuite/Magento/Framework/Search/_files/product_configurable_with_out-of-stock_child.php @@ -0,0 +1,43 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +declare(strict_types=1); + +use Magento\Catalog\Api\CategoryLinkManagementInterface; +use Magento\Catalog\Api\ProductRepositoryInterface; +use Magento\Catalog\Helper\DefaultCategory; +use Magento\TestFramework\Helper\Bootstrap; + +require __DIR__ . '/product_configurable.php'; + +$objectManager = Bootstrap::getObjectManager(); + +/** @var ProductRepositoryInterface $productRepository */ +$productRepository = $objectManager->create(ProductRepositoryInterface::class); + +$product = $productRepository->get('simple_1010'); +$product->setStockData( + [ + 'qty' => 0, + ] +); +$productRepository->save($product); + +$product = $productRepository->get('simple_1020'); +$product->setStockData( + [ + 'qty' => 0, + ] +); +$productRepository->save($product); + +/** @var CategoryLinkManagementInterface $categoryLinkManagement */ +$categoryLinkManagement = $objectManager->create(CategoryLinkManagementInterface::class); +/** @var DefaultCategory $categoryHelper */ +$categoryHelper = $objectManager->get(DefaultCategory::class); + +foreach (['simple_1010', 'simple_1020', 'configurable'] as $sku) { + $categoryLinkManagement->assignProductToCategories($sku, [$categoryHelper->getId(), 333]); +} diff --git a/dev/tests/integration/testsuite/Magento/Framework/Search/_files/product_configurable_with_out-of-stock_child_rollback.php b/dev/tests/integration/testsuite/Magento/Framework/Search/_files/product_configurable_with_out-of-stock_child_rollback.php new file mode 100644 index 0000000000000..05b0d8959a5d3 --- /dev/null +++ b/dev/tests/integration/testsuite/Magento/Framework/Search/_files/product_configurable_with_out-of-stock_child_rollback.php @@ -0,0 +1,9 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +declare(strict_types=1); + +require __DIR__ . '/../../../Catalog/_files/category_rollback.php'; +require __DIR__ . '/product_configurable_rollback.php'; diff --git a/dev/tests/integration/testsuite/Magento/Framework/Session/SaveHandlerTest.php b/dev/tests/integration/testsuite/Magento/Framework/Session/SaveHandlerTest.php index c420f6fa9dd75..dd493a499e855 100644 --- a/dev/tests/integration/testsuite/Magento/Framework/Session/SaveHandlerTest.php +++ b/dev/tests/integration/testsuite/Magento/Framework/Session/SaveHandlerTest.php @@ -3,18 +3,25 @@ * Copyright © Magento, Inc. All rights reserved. * See COPYING.txt for license details. */ + namespace Magento\Framework\Session; use Magento\Framework\App\DeploymentConfig; use Magento\Framework\Exception\SessionException; use Magento\Framework\Phrase; use Magento\Framework\Session\Config\ConfigInterface; -use Magento\Framework\App\ObjectManager; +use Magento\TestFramework\Helper\Bootstrap; +use Magento\TestFramework\ObjectManager; +/** + * Tests \Magento\Framework\Session\SaveHandler functionality. + * + * @SuppressWarnings(PHPMD.CouplingBetweenObjects) + */ class SaveHandlerTest extends \PHPUnit\Framework\TestCase { /** - * @var \Magento\TestFramework\ObjectManager + * @var ObjectManager */ private $objectManager; @@ -28,15 +35,21 @@ class SaveHandlerTest extends \PHPUnit\Framework\TestCase */ private $saveHandlerFactoryMock; + /** + * @inheritdoc + */ protected function setUp() { - $this->objectManager = \Magento\TestFramework\Helper\Bootstrap::getObjectManager(); + $this->objectManager = Bootstrap::getObjectManager(); $this->deploymentConfigMock = $this->createMock(DeploymentConfig::class); $this->objectManager->addSharedInstance($this->deploymentConfigMock, DeploymentConfig::class); $this->saveHandlerFactoryMock = $this->createMock(SaveHandlerFactory::class); $this->objectManager->addSharedInstance($this->saveHandlerFactoryMock, SaveHandlerFactory::class); } + /** + * @inheritdoc + */ protected function tearDown() { $this->objectManager->removeSharedInstance(DeploymentConfig::class); @@ -44,99 +57,74 @@ protected function tearDown() } /** - * Tests that the session handler is correctly set when object is created. - * - * @dataProvider saveHandlerProvider - * @param string $deploymentConfigHandler + * @return void */ - public function testConstructor($deploymentConfigHandler) + public function testRedisSaveHandler(): void { - $expected = $this->getExpectedSaveHandler($deploymentConfigHandler, ini_get('session.save_handler')); - $this->deploymentConfigMock->method('get') - ->willReturnCallback(function ($configPath) use ($deploymentConfigHandler) { - switch ($configPath) { - case Config::PARAM_SESSION_SAVE_METHOD: - return $deploymentConfigHandler; - case Config::PARAM_SESSION_CACHE_LIMITER: - return 'private_no_expire'; - case Config::PARAM_SESSION_SAVE_PATH: - return 'explicit_save_path'; - default: - return null; - } - }); - - $this->saveHandlerFactoryMock->expects($this->once()) - ->method('create') - ->with($expected); - $sessionConfig = $this->objectManager->create(ConfigInterface::class); - $this->objectManager->create(SaveHandler::class, ['sessionConfig' => $sessionConfig]); + ->willReturnMap( + [ + [Config::PARAM_SESSION_SAVE_METHOD, null, 'redis'], + [Config::PARAM_SESSION_SAVE_PATH, null, 'explicit_save_path'], + ] + ); - // Test expectation - $this->assertEquals( - $expected, - $sessionConfig->getOption('session.save_handler') - ); - } + $redisHandlerMock = $this->getMockBuilder(SaveHandler\Redis::class) + ->disableOriginalConstructor() + ->getMock(); + $redisHandlerMock->method('open') + ->with('explicit_save_path', 'test_session_id') + ->willReturn(true); - public function saveHandlerProvider() - { - return [ - ['db'], - ['redis'], - ['files'], - [false], - ]; + $this->saveHandlerFactoryMock->expects($this->exactly(1)) + ->method('create') + ->with('redis') + ->willReturn($redisHandlerMock); + + $sessionConfig = $this->objectManager->create(ConfigInterface::class); + /** @var SaveHandler $saveHandler */ + $saveHandler = $this->objectManager->create(SaveHandler::class, ['sessionConfig' => $sessionConfig]); + $result = $saveHandler->open('explicit_save_path', 'test_session_id'); + $this->assertTrue($result); } /** - * Retrieve expected session.save_handler - * - * @param string $deploymentConfigHandler - * @param string $iniHandler - * @return string + * @return void */ - private function getExpectedSaveHandler($deploymentConfigHandler, $iniHandler) - { - if ($deploymentConfigHandler) { - return $deploymentConfigHandler; - } elseif ($iniHandler) { - return $iniHandler; - } else { - return SaveHandlerInterface::DEFAULT_HANDLER; - } - } - - public function testConstructorWithException() + public function testRedisSaveHandlerFallbackToDefaultOnSessionException(): void { $this->deploymentConfigMock->method('get') - ->willReturnCallback(function ($configPath) { - switch ($configPath) { - case Config::PARAM_SESSION_SAVE_METHOD: - return 'db'; - case Config::PARAM_SESSION_CACHE_LIMITER: - return 'private_no_expire'; - case Config::PARAM_SESSION_SAVE_PATH: - return 'explicit_save_path'; - default: - return null; - } - }); + ->willReturnMap( + [ + [Config::PARAM_SESSION_SAVE_METHOD, null, 'redis'], + [Config::PARAM_SESSION_SAVE_PATH, null, 'explicit_save_path'], + ] + ); + + $redisHandlerMock = $this->getMockBuilder(SaveHandler\Redis::class) + ->disableOriginalConstructor() + ->getMock(); + $redisHandlerMock->method('open') + ->with('explicit_save_path', 'test_session_id') + ->willThrowException(new SessionException(new Phrase('Session Exception'))); + + $defaultHandlerMock = $this->getMockBuilder(SaveHandler\Native::class) + ->disableOriginalConstructor() + ->getMock(); + $defaultHandlerMock->expects($this->once())->method('open')->with('explicit_save_path', 'test_session_id'); $this->saveHandlerFactoryMock->expects($this->at(0)) ->method('create') - ->willThrowException(new SessionException(new Phrase('Session Exception'))); + ->with('redis') + ->willReturn($redisHandlerMock); $this->saveHandlerFactoryMock->expects($this->at(1)) ->method('create') - ->with(SaveHandlerInterface::DEFAULT_HANDLER); - $sessionConfig = $this->objectManager->create(ConfigInterface::class); - $this->objectManager->create(SaveHandler::class, ['sessionConfig' => $sessionConfig]); + ->with(SaveHandlerInterface::DEFAULT_HANDLER) + ->willReturn($defaultHandlerMock); - // Test expectation - $this->assertEquals( - 'db', - $sessionConfig->getOption('session.save_handler') - ); + $sessionConfig = $this->objectManager->create(ConfigInterface::class); + /** @var SaveHandler $saveHandler */ + $saveHandler = $this->objectManager->create(SaveHandler::class, ['sessionConfig' => $sessionConfig]); + $saveHandler->open('explicit_save_path', 'test_session_id'); } } diff --git a/dev/tests/integration/testsuite/Magento/Framework/Session/SessionManagerTest.php b/dev/tests/integration/testsuite/Magento/Framework/Session/SessionManagerTest.php index 3205c19445ee1..482a3c9cbd619 100644 --- a/dev/tests/integration/testsuite/Magento/Framework/Session/SessionManagerTest.php +++ b/dev/tests/integration/testsuite/Magento/Framework/Session/SessionManagerTest.php @@ -52,7 +52,7 @@ function ini_set($varName, $newValue) SessionManagerTest::$isIniSetInvoked[$varName] = $newValue; return true; } - return call_user_func_array('\ini_set', func_get_args()); + return call_user_func_array('\ini_set', [$varName, $newValue]); } /** @@ -213,15 +213,16 @@ public function testDestroy() public function testSetSessionId() { $this->initializeModel(); - $sessionId = $this->model->getSessionId(); - $this->appState->expects($this->atLeastOnce()) + $this->assertNotEmpty($this->model->getSessionId()); + $this->appState->expects($this->any()) ->method('getAreaCode') ->willReturn(\Magento\Framework\App\Area::AREA_FRONTEND); - $this->model->setSessionId($this->sidResolver->getSid($this->model)); - $this->assertEquals($sessionId, $this->model->getSessionId()); $this->model->setSessionId('test'); $this->assertEquals('test', $this->model->getSessionId()); + /* Use not valid identifier */ + $this->model->setSessionId('test_id'); + $this->assertEquals('test', $this->model->getSessionId()); } /** @@ -230,17 +231,14 @@ public function testSetSessionId() public function testSetSessionIdFromParam() { $this->initializeModel(); - $this->appState->expects($this->atLeastOnce()) + $this->appState->expects($this->any()) ->method('getAreaCode') ->willReturn(\Magento\Framework\App\Area::AREA_FRONTEND); + $currentId = $this->model->getSessionId(); $this->assertNotEquals('test_id', $this->model->getSessionId()); - $this->request->getQuery()->set($this->sidResolver->getSessionIdQueryParam($this->model), 'test-id'); - $this->model->setSessionId($this->sidResolver->getSid($this->model)); - $this->assertEquals('test-id', $this->model->getSessionId()); - /* Use not valid identifier */ - $this->request->getQuery()->set($this->sidResolver->getSessionIdQueryParam($this->model), 'test_id'); + $this->request->getQuery()->set(SidResolverInterface::SESSION_ID_QUERY_PARAM, 'test-id'); $this->model->setSessionId($this->sidResolver->getSid($this->model)); - $this->assertEquals('test-id', $this->model->getSessionId()); + $this->assertEquals($currentId, $this->model->getSessionId()); } public function testGetSessionIdForHost() diff --git a/dev/tests/integration/testsuite/Magento/Framework/Stdlib/Cookie/CookieScopeTest.php b/dev/tests/integration/testsuite/Magento/Framework/Stdlib/Cookie/CookieScopeTest.php index 9dee98fcb9121..2b5cb27e84dcb 100644 --- a/dev/tests/integration/testsuite/Magento/Framework/Stdlib/Cookie/CookieScopeTest.php +++ b/dev/tests/integration/testsuite/Magento/Framework/Stdlib/Cookie/CookieScopeTest.php @@ -9,7 +9,7 @@ use Magento\Framework\App\RequestInterface; use Magento\Framework\ObjectManagerInterface; use Magento\TestFramework\Helper\Bootstrap; -use Zend\Stdlib\Parameters; +use Laminas\Stdlib\Parameters; /** * Test CookieScope diff --git a/dev/tests/integration/testsuite/Magento/Framework/UrlTest.php b/dev/tests/integration/testsuite/Magento/Framework/UrlTest.php index db830d228201c..c6c561e1de53d 100644 --- a/dev/tests/integration/testsuite/Magento/Framework/UrlTest.php +++ b/dev/tests/integration/testsuite/Magento/Framework/UrlTest.php @@ -5,7 +5,7 @@ */ namespace Magento\Framework; -use Zend\Stdlib\Parameters; +use Laminas\Stdlib\Parameters; use Magento\TestFramework\Helper\Bootstrap; /** @@ -477,6 +477,8 @@ public function testGetDirectUrl() } /** + * Check that SID is removed from URL. + * * Note: isolation flushes the URL memory cache * @magentoAppIsolation enabled * @@ -485,11 +487,8 @@ public function testGetDirectUrl() */ public function testSessionUrlVar() { - $sessionId = \Magento\TestFramework\Helper\Bootstrap::getObjectManager()->get( - \Magento\Framework\Session\Generic::class - )->getSessionId(); $sessionUrl = $this->model->sessionUrlVar('<a href="http://example.com/?___SID=U">www.example.com</a>'); - $this->assertEquals('<a href="http://example.com/?SID=' . $sessionId . '">www.example.com</a>', $sessionUrl); + $this->assertEquals('<a href="http://example.com/">www.example.com</a>', $sessionUrl); } public function testUseSessionIdForUrl() diff --git a/dev/tests/integration/testsuite/Magento/Framework/View/Element/AbstractBlockTest.php b/dev/tests/integration/testsuite/Magento/Framework/View/Element/AbstractBlockTest.php index 5f0c7176bce7a..a398207c9e649 100644 --- a/dev/tests/integration/testsuite/Magento/Framework/View/Element/AbstractBlockTest.php +++ b/dev/tests/integration/testsuite/Magento/Framework/View/Element/AbstractBlockTest.php @@ -5,15 +5,19 @@ */ namespace Magento\Framework\View\Element; -use Magento\Framework\View\Element\AbstractBlock; +use Magento\Framework\Math\Random; +use Magento\Framework\Session\SessionManagerInterface; +use Magento\Framework\Session\SidResolverInterface; +use Magento\TestFramework\Helper\Bootstrap; /** * @magentoAppIsolation enabled + * @SuppressWarnings(PHPMD.CouplingBetweenObjects) */ class AbstractBlockTest extends \PHPUnit\Framework\TestCase { /** - * @var \Magento\Framework\View\Element\AbstractBlock + * @var AbstractBlock */ protected $_block; @@ -24,22 +28,29 @@ class AbstractBlockTest extends \PHPUnit\Framework\TestCase protected static $_mocks = []; + /** + * @var SessionManagerInterface + */ + private $session; + protected function setUp() { - \Magento\TestFramework\Helper\Bootstrap::getObjectManager()->get(\Magento\Framework\App\State::class) - ->setAreaCode('frontend'); - \Magento\TestFramework\Helper\Bootstrap::getObjectManager()->get( - \Magento\Framework\View\DesignInterface::class - )->setDefaultDesignTheme(); + /** @var \Magento\Framework\App\State $state */ + $state = Bootstrap::getObjectManager()->get(\Magento\Framework\App\State::class); + $state->setAreaCode('frontend'); + /** @var \Magento\Framework\View\DesignInterface $design */ + $design = Bootstrap::getObjectManager()->get(\Magento\Framework\View\DesignInterface::class); + $design->setDefaultDesignTheme(); $this->_block = $this->getMockForAbstractClass( - \Magento\Framework\View\Element\AbstractBlock::class, + AbstractBlock::class, [ - \Magento\TestFramework\Helper\Bootstrap::getObjectManager()->get( + Bootstrap::getObjectManager()->get( \Magento\Framework\View\Element\Context::class ), ['module_name' => 'Magento_Theme'] ] ); + $this->session = Bootstrap::getObjectManager()->get(SessionManagerInterface::class); } /** @@ -113,9 +124,9 @@ public function testSetGetNameInLayout() ); $layout->createBlock(\Magento\Framework\View\Element\Template::class, $name); $block = $layout->getBlock($name); - $this->assertInstanceOf(\Magento\Framework\View\Element\AbstractBlock::class, $block); + $this->assertInstanceOf(AbstractBlock::class, $block); $block->setNameInLayout($name); - $this->assertInstanceOf(\Magento\Framework\View\Element\AbstractBlock::class, $layout->getBlock($name)); + $this->assertInstanceOf(AbstractBlock::class, $layout->getBlock($name)); $this->assertEquals($name, $block->getNameInLayout()); $this->assertTrue($layout->hasElement($name)); $newName = 'new_name'; @@ -549,7 +560,7 @@ public function testGetCacheKeyInfo() public function testGetCacheKey() { $name = uniqid('block.'); - $block = \Magento\TestFramework\Helper\Bootstrap::getObjectManager()->get( + $block = Bootstrap::getObjectManager()->get( \Magento\Framework\View\LayoutInterface::class )->createBlock( \Magento\Framework\View\Element\Text::class @@ -564,6 +575,35 @@ public function testGetCacheKey() $this->assertEquals(AbstractBlock::CACHE_KEY_PREFIX . 'key', $block->getCacheKey()); } + /** + * Check that SIDs inside blocks are not being replaced. + * + * @return void + * @magentoCache block_html enabled + */ + public function testNoSid(): void + { + $blockId = 'block-with-sid' .Random::getRandomNumber(1, 9999); + $block = $this->_createBlockWithLayout( + $blockId, + $blockId, + \Magento\Framework\View\Element\Text::class + ); + $outerId = 'block-outer' .Random::getRandomNumber(1, 9999); + $outer = $this->_createBlockWithLayout($outerId, $outerId); + $block->setText( + $text = 'Some text with ' .SidResolverInterface::SESSION_ID_QUERY_PARAM + .'=' .$this->session->getSessionId() + ); + $block->setData('cache_lifetime', 3600); + //Caching the block's content + $outer->getBlockHtml($blockId); + //New ID generated, must not be replace in block's content. + $this->session->regenerateId(); + $html = $outer->getBlockHtml($blockId); + $this->assertEquals($text, $html); + } + /** * Create <N> sample blocks * @@ -610,7 +650,7 @@ protected function _createSampleBlocks( protected function _createBlockWithLayout( $name = 'block', $alias = null, - $type = \Magento\Framework\View\Element\AbstractBlock::class + $type = AbstractBlock::class ) { $typePart = explode('\\', $type); $mockClass = array_pop($typePart) . 'Mock'; @@ -618,7 +658,7 @@ protected function _createBlockWithLayout( self::$_mocks[$mockClass] = $this->getMockForAbstractClass( $type, [ - \Magento\TestFramework\Helper\Bootstrap::getObjectManager()->get( + Bootstrap::getObjectManager()->get( \Magento\Framework\View\Element\Context::class ), ['module_name' => 'Magento_Theme'] @@ -627,7 +667,7 @@ protected function _createBlockWithLayout( ); } if ($this->_layout === null) { - $this->_layout = \Magento\TestFramework\Helper\Bootstrap::getObjectManager()->get( + $this->_layout = Bootstrap::getObjectManager()->get( \Magento\Framework\View\LayoutInterface::class ); } diff --git a/dev/tests/integration/testsuite/Magento/Framework/View/LayoutTest.php b/dev/tests/integration/testsuite/Magento/Framework/View/LayoutTest.php index 66e8eb3e453f9..1029e904f7501 100644 --- a/dev/tests/integration/testsuite/Magento/Framework/View/LayoutTest.php +++ b/dev/tests/integration/testsuite/Magento/Framework/View/LayoutTest.php @@ -253,17 +253,28 @@ public function testAddContainer($htmlTag) public function addContainerDataProvider() { return [ + ['aside'], ['dd'], ['div'], ['dl'], ['fieldset'], + ['main'], + ['nav'], ['header'], + ['footer'], ['ol'], ['p'], ['section'], ['table'], ['tfoot'], - ['ul'] + ['ul'], + ['article'], + ['h1'], + ['h2'], + ['h3'], + ['h4'], + ['h5'], + ['h6'], ]; } @@ -274,7 +285,7 @@ public function testAddContainerInvalidHtmlTag() { $msg = 'Html tag "span" is forbidden for usage in containers. ' . 'Consider to use one of the allowed: aside, dd, div, dl, fieldset, main, nav, ' . - 'header, footer, ol, p, section, table, tfoot, ul.'; + 'header, footer, ol, p, section, table, tfoot, ul, article, h1, h2, h3, h4, h5, h6.'; $this->expectException(\Magento\Framework\Exception\LocalizedException::class); $this->expectExceptionMessage($msg); $this->_layout->addContainer('container', 'Container', ['htmlTag' => 'span']); diff --git a/dev/tests/integration/testsuite/Magento/Framework/View/_files/Fixture_Module/registration.php b/dev/tests/integration/testsuite/Magento/Framework/View/_files/Fixture_Module/registration.php index da748f3b3299b..7e073d8f7cf50 100644 --- a/dev/tests/integration/testsuite/Magento/Framework/View/_files/Fixture_Module/registration.php +++ b/dev/tests/integration/testsuite/Magento/Framework/View/_files/Fixture_Module/registration.php @@ -4,6 +4,6 @@ * See COPYING.txt for license details. */ -use \Magento\Framework\Component\ComponentRegistrar; +use Magento\Framework\Component\ComponentRegistrar; ComponentRegistrar::register(ComponentRegistrar::MODULE, 'Fixture_Module', __DIR__); diff --git a/dev/tests/integration/testsuite/Magento/Framework/View/_files/UiComponent/theme/registration.php b/dev/tests/integration/testsuite/Magento/Framework/View/_files/UiComponent/theme/registration.php index cbba56dcaafa2..886dad410ba72 100644 --- a/dev/tests/integration/testsuite/Magento/Framework/View/_files/UiComponent/theme/registration.php +++ b/dev/tests/integration/testsuite/Magento/Framework/View/_files/UiComponent/theme/registration.php @@ -4,6 +4,6 @@ * See COPYING.txt for license details. */ -use \Magento\Framework\Component\ComponentRegistrar; +use Magento\Framework\Component\ComponentRegistrar; ComponentRegistrar::register(ComponentRegistrar::THEME, 'adminhtml/FrameworkViewUiComponent/default', __DIR__); diff --git a/dev/tests/integration/testsuite/Magento/Framework/View/_files/fallback/app/code/ViewTest_Module/registration.php b/dev/tests/integration/testsuite/Magento/Framework/View/_files/fallback/app/code/ViewTest_Module/registration.php index 7b1644fc8f40a..eafa89e3abf29 100644 --- a/dev/tests/integration/testsuite/Magento/Framework/View/_files/fallback/app/code/ViewTest_Module/registration.php +++ b/dev/tests/integration/testsuite/Magento/Framework/View/_files/fallback/app/code/ViewTest_Module/registration.php @@ -4,6 +4,6 @@ * See COPYING.txt for license details. */ -use \Magento\Framework\Component\ComponentRegistrar; +use Magento\Framework\Component\ComponentRegistrar; ComponentRegistrar::register(ComponentRegistrar::MODULE, 'ViewTest_Module', __DIR__); diff --git a/dev/tests/integration/testsuite/Magento/Framework/View/_files/fallback/design/frontend/Vendor/custom_theme/registration.php b/dev/tests/integration/testsuite/Magento/Framework/View/_files/fallback/design/frontend/Vendor/custom_theme/registration.php index 6150fcb34dc85..219ec82584f50 100644 --- a/dev/tests/integration/testsuite/Magento/Framework/View/_files/fallback/design/frontend/Vendor/custom_theme/registration.php +++ b/dev/tests/integration/testsuite/Magento/Framework/View/_files/fallback/design/frontend/Vendor/custom_theme/registration.php @@ -4,6 +4,6 @@ * See COPYING.txt for license details. */ -use \Magento\Framework\Component\ComponentRegistrar; +use Magento\Framework\Component\ComponentRegistrar; ComponentRegistrar::register(ComponentRegistrar::THEME, 'frontend/Vendor_ViewTest/custom_theme', __DIR__); diff --git a/dev/tests/integration/testsuite/Magento/Framework/View/_files/fallback/design/frontend/Vendor/custom_theme2/registration.php b/dev/tests/integration/testsuite/Magento/Framework/View/_files/fallback/design/frontend/Vendor/custom_theme2/registration.php index a1d5f994fb75d..938ab2a46e5c0 100644 --- a/dev/tests/integration/testsuite/Magento/Framework/View/_files/fallback/design/frontend/Vendor/custom_theme2/registration.php +++ b/dev/tests/integration/testsuite/Magento/Framework/View/_files/fallback/design/frontend/Vendor/custom_theme2/registration.php @@ -4,6 +4,6 @@ * See COPYING.txt for license details. */ -use \Magento\Framework\Component\ComponentRegistrar; +use Magento\Framework\Component\ComponentRegistrar; ComponentRegistrar::register(ComponentRegistrar::THEME, 'frontend/Vendor_ViewTest/custom_theme2', __DIR__); diff --git a/dev/tests/integration/testsuite/Magento/Framework/View/_files/fallback/design/frontend/Vendor/default/registration.php b/dev/tests/integration/testsuite/Magento/Framework/View/_files/fallback/design/frontend/Vendor/default/registration.php index 8302085feb23f..36caee1923ac3 100644 --- a/dev/tests/integration/testsuite/Magento/Framework/View/_files/fallback/design/frontend/Vendor/default/registration.php +++ b/dev/tests/integration/testsuite/Magento/Framework/View/_files/fallback/design/frontend/Vendor/default/registration.php @@ -4,6 +4,6 @@ * See COPYING.txt for license details. */ -use \Magento\Framework\Component\ComponentRegistrar; +use Magento\Framework\Component\ComponentRegistrar; ComponentRegistrar::register(ComponentRegistrar::THEME, 'frontend/Vendor_ViewTest/default', __DIR__); diff --git a/dev/tests/integration/testsuite/Magento/Framework/View/_files/fallback/design/frontend/Vendor/standalone_theme/registration.php b/dev/tests/integration/testsuite/Magento/Framework/View/_files/fallback/design/frontend/Vendor/standalone_theme/registration.php index ad8402df470e1..5b9e91753c424 100644 --- a/dev/tests/integration/testsuite/Magento/Framework/View/_files/fallback/design/frontend/Vendor/standalone_theme/registration.php +++ b/dev/tests/integration/testsuite/Magento/Framework/View/_files/fallback/design/frontend/Vendor/standalone_theme/registration.php @@ -4,6 +4,6 @@ * See COPYING.txt for license details. */ -use \Magento\Framework\Component\ComponentRegistrar; +use Magento\Framework\Component\ComponentRegistrar; ComponentRegistrar::register(ComponentRegistrar::THEME, 'frontend/Vendor_ViewTest/standalone_theme', __DIR__); diff --git a/dev/tests/integration/testsuite/Magento/Framework/View/_files/layout/non_cacheable_block_with_declared_reference.xml b/dev/tests/integration/testsuite/Magento/Framework/View/_files/layout/non_cacheable_block_with_declared_reference.xml new file mode 100644 index 0000000000000..b99452e3345ba --- /dev/null +++ b/dev/tests/integration/testsuite/Magento/Framework/View/_files/layout/non_cacheable_block_with_declared_reference.xml @@ -0,0 +1,22 @@ +<?xml version="1.0"?> +<!-- +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +--> +<layout xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:noNamespaceSchemaLocation="urn:magento:framework:View/Layout/etc/page_layout.xsd"> + <container name="cacheable_container1-1" label="test1" htmlTag="div" htmlId="cacheable_container11" + htmlClass="class11"> + <block class="Magento\Framework\View\Element\Text" name="block-1-in-cacheable-container1"> + <action method="setText"> + <argument name="text" xsi:type="string">Test11</argument> + </action> + </block> + </container> + + <referenceContainer name="cacheable_container1-1"> + <block name="non_cacheable_block" cacheable="false"/> + </referenceContainer> +</layout> diff --git a/dev/tests/integration/testsuite/Magento/Framework/View/_files/layout/non_cacheable_block_with_missing_refference.xml b/dev/tests/integration/testsuite/Magento/Framework/View/_files/layout/non_cacheable_block_with_missing_refference.xml new file mode 100644 index 0000000000000..2dc6ac25a71bf --- /dev/null +++ b/dev/tests/integration/testsuite/Magento/Framework/View/_files/layout/non_cacheable_block_with_missing_refference.xml @@ -0,0 +1,22 @@ +<?xml version="1.0"?> +<!-- +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +--> +<layout xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:noNamespaceSchemaLocation="urn:magento:framework:View/Layout/etc/page_layout.xsd"> + <container name="cacheable_container1-1" label="test1" htmlTag="div" htmlId="cacheable_container11" + htmlClass="class11"> + <block class="Magento\Framework\View\Element\Text" name="block-1-in-cacheable-container1"> + <action method="setText"> + <argument name="text" xsi:type="string">Test11</argument> + </action> + </block> + </container> + + <referenceContainer name="cacheable_container1-1-2"> + <block name="non_cacheable_block" cacheable="false"/> + </referenceContainer> +</layout> diff --git a/dev/tests/integration/testsuite/Magento/Framework/View/_files/static/theme/registration.php b/dev/tests/integration/testsuite/Magento/Framework/View/_files/static/theme/registration.php index c2e0ac8d8c34e..d51fc082d90cc 100644 --- a/dev/tests/integration/testsuite/Magento/Framework/View/_files/static/theme/registration.php +++ b/dev/tests/integration/testsuite/Magento/Framework/View/_files/static/theme/registration.php @@ -4,6 +4,6 @@ * See COPYING.txt for license details. */ -use \Magento\Framework\Component\ComponentRegistrar; +use Magento\Framework\Component\ComponentRegistrar; ComponentRegistrar::register(ComponentRegistrar::THEME, 'frontend/FrameworkViewMinifier/default', __DIR__); diff --git a/dev/tests/integration/testsuite/Magento/GraphQl/AuthorizenetAcceptjs/_files/enable_authorizenetacceptjs.php b/dev/tests/integration/testsuite/Magento/GraphQl/AuthorizenetAcceptjs/_files/enable_authorizenetacceptjs.php deleted file mode 100644 index 3043a5eaf2ae1..0000000000000 --- a/dev/tests/integration/testsuite/Magento/GraphQl/AuthorizenetAcceptjs/_files/enable_authorizenetacceptjs.php +++ /dev/null @@ -1,32 +0,0 @@ -<?php -/** - * Copyright © Magento, Inc. All rights reserved. - * See COPYING.txt for license details. - */ -declare(strict_types=1); - -use Magento\Framework\App\Config\Storage\Writer; -use Magento\Framework\App\Config\Storage\WriterInterface; -use Magento\TestFramework\Helper\Bootstrap; -use Magento\Framework\App\Config\ScopeConfigInterface; -use Magento\Framework\Encryption\EncryptorInterface; -use Magento\AuthorizenetAcceptjs\Gateway\Config; - -$objectManager = Bootstrap::getObjectManager(); -/** @var EncryptorInterface $encryptor */ -$encryptor = $objectManager->get(EncryptorInterface::class); - -/** @var Writer $configWriter */ -$configWriter = $objectManager->get(WriterInterface::class); -$configWriter->save('payment/' . Config::METHOD . '/active', '1'); -$configWriter->save('payment/' . Config::METHOD . '/environment', 'sandbox'); -$configWriter->save('payment/' . Config::METHOD . '/login', $encryptor->encrypt('def_login')); -$configWriter->save('payment/' . Config::METHOD . '/trans_key', $encryptor->encrypt('def_trans_key')); -$configWriter->save('payment/' . Config::METHOD . '/public_client_key', $encryptor->encrypt('def_public_client_key')); -$configWriter->save( - 'payment/' . Config::METHOD . '/trans_signature_key', - $encryptor->encrypt('def_trans_signature_key') -); - -$scopeConfig = $objectManager->get(ScopeConfigInterface::class); -$scopeConfig->clean(); diff --git a/dev/tests/integration/testsuite/Magento/GraphQl/AuthorizenetAcceptjs/_files/enable_authorizenetacceptjs_rollback.php b/dev/tests/integration/testsuite/Magento/GraphQl/AuthorizenetAcceptjs/_files/enable_authorizenetacceptjs_rollback.php deleted file mode 100644 index b73883f80f333..0000000000000 --- a/dev/tests/integration/testsuite/Magento/GraphQl/AuthorizenetAcceptjs/_files/enable_authorizenetacceptjs_rollback.php +++ /dev/null @@ -1,26 +0,0 @@ -<?php -/** - * Copyright © Magento, Inc. All rights reserved. - * See COPYING.txt for license details. - */ -declare(strict_types=1); - -use Magento\Framework\App\Config\Storage\Writer; -use Magento\Framework\App\Config\Storage\WriterInterface; -use Magento\TestFramework\Helper\Bootstrap; -use Magento\Framework\App\Config\ScopeConfigInterface; -use Magento\AuthorizenetAcceptjs\Gateway\Config; - -$objectManager = Bootstrap::getObjectManager(); - -/** @var Writer $configWriter */ -$configWriter = $objectManager->get(WriterInterface::class); -$configWriter->delete('payment/' . Config::METHOD . '/active'); -$configWriter->delete('payment/' . Config::METHOD . '/environment'); -$configWriter->delete('payment/' . Config::METHOD . '/login'); -$configWriter->delete('payment/' . Config::METHOD . '/trans_key'); -$configWriter->delete('payment/' . Config::METHOD . '/public_client_key'); -$configWriter->delete('payment/' . Config::METHOD . '/trans_signature_key'); - -$scopeConfig = $objectManager->get(ScopeConfigInterface::class); -$scopeConfig->clean(); diff --git a/dev/tests/integration/testsuite/Magento/GraphQl/Controller/GraphQlControllerTest.php b/dev/tests/integration/testsuite/Magento/GraphQl/Controller/GraphQlControllerTest.php index 2fd388d9db3f5..527e471ff3e39 100644 --- a/dev/tests/integration/testsuite/Magento/GraphQl/Controller/GraphQlControllerTest.php +++ b/dev/tests/integration/testsuite/Magento/GraphQl/Controller/GraphQlControllerTest.php @@ -98,7 +98,7 @@ public function testDispatch() : void $this->request->setPathInfo('/graphql'); $this->request->setMethod('POST'); $this->request->setContent(json_encode($postData)); - $headers = $this->objectManager->create(\Zend\Http\Headers::class) + $headers = $this->objectManager->create(\Laminas\Http\Headers::class) ->addHeaders(['Content-Type' => 'application/json']); $this->request->setHeaders($headers); $response = $this->graphql->dispatch($this->request); @@ -241,7 +241,7 @@ public function testError() : void $this->request->setPathInfo('/graphql'); $this->request->setMethod('POST'); $this->request->setContent(json_encode($postData)); - $headers = $this->objectManager->create(\Zend\Http\Headers::class) + $headers = $this->objectManager->create(\Laminas\Http\Headers::class) ->addHeaders(['Content-Type' => 'application/json']); $this->request->setHeaders($headers); $response = $this->graphql->dispatch($this->request); diff --git a/dev/tests/integration/testsuite/Magento/GraphQl/Quote/_files/add_configurable_product.php b/dev/tests/integration/testsuite/Magento/GraphQl/Quote/_files/add_configurable_product.php new file mode 100644 index 0000000000000..e46fafe7bb4ce --- /dev/null +++ b/dev/tests/integration/testsuite/Magento/GraphQl/Quote/_files/add_configurable_product.php @@ -0,0 +1,50 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +declare(strict_types=1); + +use Magento\Catalog\Api\ProductRepositoryInterface; +use Magento\Quote\Api\CartRepositoryInterface; +use Magento\Quote\Model\QuoteFactory; +use Magento\Quote\Model\ResourceModel\Quote as QuoteResource; +use Magento\TestFramework\Helper\Bootstrap; + +/** @var ProductRepositoryInterface $productRepository */ +$productRepository = Bootstrap::getObjectManager()->get(ProductRepositoryInterface::class); +/** @var QuoteFactory $quoteFactory */ +$quoteFactory = Bootstrap::getObjectManager()->get(QuoteFactory::class); +/** @var QuoteResource $quoteResource */ +$quoteResource = Bootstrap::getObjectManager()->get(QuoteResource::class); +/** @var CartRepositoryInterface $cartRepository */ +$cartRepository = Bootstrap::getObjectManager()->get(CartRepositoryInterface::class); + +/** @var $product \Magento\Catalog\Model\Product */ +$product = $productRepository->get('configurable'); +/** @var $options \Magento\Eav\Model\ResourceModel\Entity\Attribute\Option\Collection */ +$options = Bootstrap::getObjectManager()->create( + \Magento\Eav\Model\ResourceModel\Entity\Attribute\Option\Collection::class +); + +$attributeRepository = Bootstrap::getObjectManager()->get(\Magento\Eav\Api\AttributeRepositoryInterface::class); +$attribute = $attributeRepository->get('catalog_product', 'test_configurable'); + +$option = $options->setAttributeFilter($attribute->getId())->getFirstItem(); + +$requestInfo = new \Magento\Framework\DataObject( + [ + 'product' => 1, + 'selected_configurable_option' => 1, + 'qty' => 1, + 'super_attribute' => [ + $attribute->getId() => $option->getId() + ] + ] +); + + +$quote = $quoteFactory->create(); +$quoteResource->load($quote, 'test_quote', 'reserved_order_id'); +$quote->addProduct($product, $requestInfo); +$cartRepository->save($quote); diff --git a/dev/tests/integration/testsuite/Magento/GraphQl/Quote/_files/enable_offline_payment_methods.php b/dev/tests/integration/testsuite/Magento/GraphQl/Quote/_files/enable_offline_payment_methods.php index ff2a2835448c6..1af8d900b23dc 100644 --- a/dev/tests/integration/testsuite/Magento/GraphQl/Quote/_files/enable_offline_payment_methods.php +++ b/dev/tests/integration/testsuite/Magento/GraphQl/Quote/_files/enable_offline_payment_methods.php @@ -9,7 +9,6 @@ * @magentoConfigFixture default_store payment/cashondelivery/active 1 * @magentoConfigFixture default_store payment/checkmo/active 1 * @magentoConfigFixture default_store payment/purchaseorder/active 1 - * @magentoConfigFixture default_store payment/authorizenet_acceptjs/active 1 */ declare(strict_types=1); @@ -26,7 +25,6 @@ $configWriter->save('payment/cashondelivery/active', 1); $configWriter->save('payment/checkmo/active', 1); $configWriter->save('payment/purchaseorder/active', 1); -$configWriter->save('payment/authorizenet_acceptjs/active', 1); $scopeConfig = $objectManager->get(ScopeConfigInterface::class); $scopeConfig->clean(); diff --git a/dev/tests/integration/testsuite/Magento/GraphQl/Quote/_files/enable_offline_payment_methods_rollback.php b/dev/tests/integration/testsuite/Magento/GraphQl/Quote/_files/enable_offline_payment_methods_rollback.php index 3be528eb41de8..b3268ecc9ee98 100644 --- a/dev/tests/integration/testsuite/Magento/GraphQl/Quote/_files/enable_offline_payment_methods_rollback.php +++ b/dev/tests/integration/testsuite/Magento/GraphQl/Quote/_files/enable_offline_payment_methods_rollback.php @@ -20,4 +20,3 @@ $configWriter->delete('payment/cashondelivery/active'); $configWriter->delete('payment/checkmo/active'); $configWriter->delete('payment/purchaseorder/active'); -$configWriter->delete('payment/authorizenet_acceptjs/active'); diff --git a/dev/tests/integration/testsuite/Magento/GroupedProduct/Block/Order/Email/Items/CreditMemo/GroupedTest.php b/dev/tests/integration/testsuite/Magento/GroupedProduct/Block/Order/Email/Items/CreditMemo/GroupedTest.php new file mode 100644 index 0000000000000..8856356227ed9 --- /dev/null +++ b/dev/tests/integration/testsuite/Magento/GroupedProduct/Block/Order/Email/Items/CreditMemo/GroupedTest.php @@ -0,0 +1,60 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +declare(strict_types=1); + +namespace Magento\GroupedProduct\Block\Order\Email\Items\CreditMemo; + +use Magento\Sales\Block\Order\Email\Items\DefaultItems; +use Magento\Sales\Model\Order\Creditmemo; +use Magento\TestFramework\Helper\Bootstrap; +use PHPUnit\Framework\TestCase; + +/** + * Verify grouped product block will output correct data. + * + * @magentoAppArea frontend + */ +class GroupedTest extends TestCase +{ + /** + * Test subject. + * + * @var Grouped + */ + private $block; + + /** + * @var CreditMemo + */ + private $creditMemo; + + /** + * @inheritDoc + */ + protected function setUp() + { + $this->block = Bootstrap::getObjectManager()->get(Grouped::class); + $this->creditMemo = Bootstrap::getObjectManager()->get(CreditMemo::class); + } + + /** + * Verify, grouped block will output correct product sku and name. + * + * @magentoDataFixture Magento/Sales/_files/creditmemo_with_grouped_product.php + */ + public function testToHtml() + { + $creditMemo = $this->creditMemo->load('100000002', 'increment_id'); + $creditMemoItem = $creditMemo->getItemsCollection()->getFirstItem(); + $priceBlock = Bootstrap::getObjectManager()->create(DefaultItems::class); + $this->block->setTemplate('Magento_Sales::email/items/creditmemo/default.phtml'); + $this->block->setItem($creditMemoItem); + $this->block->getLayout()->setBlock('item_price', $priceBlock); + $output = $this->block->toHtml(); + self::assertContains('SKU: simple_11', $output); + self::assertContains('"product-name">Simple 11', $output); + } +} diff --git a/dev/tests/integration/testsuite/Magento/ImportExport/Controller/Adminhtml/Export/File/DeleteTest.php b/dev/tests/integration/testsuite/Magento/ImportExport/Controller/Adminhtml/Export/File/DeleteTest.php index 91764684da173..6b1463aa1a9df 100644 --- a/dev/tests/integration/testsuite/Magento/ImportExport/Controller/Adminhtml/Export/File/DeleteTest.php +++ b/dev/tests/integration/testsuite/Magento/ImportExport/Controller/Adminhtml/Export/File/DeleteTest.php @@ -29,6 +29,16 @@ class DeleteTest extends AbstractBackendController */ private $fileName = 'catalog_product.csv'; + /** + * @var Filesystem + */ + private $fileSystem; + + /** + * @var string + */ + private $sourceFilePath; + /** * @inheritdoc */ @@ -36,35 +46,61 @@ protected function setUp() { parent::setUp(); - $filesystem = $this->_objectManager->get(Filesystem::class); - $sourceFilePath = __DIR__ . '/../../Import/_files' . DIRECTORY_SEPARATOR . $this->fileName; - $destinationFilePath = 'export' . DIRECTORY_SEPARATOR . $this->fileName; + $this->fileSystem = $this->_objectManager->get(Filesystem::class); + $this->sourceFilePath = __DIR__ . '/../../Import/_files' . DIRECTORY_SEPARATOR . $this->fileName; //Refers to tests 'var' directory - $this->varDirectory = $filesystem->getDirectoryRead(DirectoryList::VAR_DIR); - //Refers to application root directory - $rootDirectory = $filesystem->getDirectoryWrite(DirectoryList::ROOT); - $rootDirectory->copyFile($sourceFilePath, $this->varDirectory->getAbsolutePath($destinationFilePath)); + $this->varDirectory = $this->fileSystem->getDirectoryRead(DirectoryList::VAR_DIR); } /** * Check that file can be removed under var/export directory. * + * @param string $file + * @dataProvider testExecuteProvider * @return void * @magentoConfigFixture default_store admin/security/use_form_key 1 */ - public function testExecute(): void + public function testExecute($file): void { + $fullPath = 'export/' . $file; + $this->copyFile($fullPath); $request = $this->getRequest(); - $request->setParam('filename', $this->fileName); + $request->setParam('filename', $file); $request->setMethod(Http::METHOD_POST); - if ($this->varDirectory->isExist('export/' . $this->fileName)) { + if ($this->varDirectory->isExist($fullPath)) { $this->dispatch('backend/admin/export_file/delete'); } else { throw new \AssertionError('Export product file supposed to exist'); } - $this->assertFalse($this->varDirectory->isExist('export/' . $this->fileName)); + $this->assertFalse($this->varDirectory->isExist($fullPath)); + } + + /** + * Copy csv file from sourceFilePath to destinationFilePath + * + * @param $destinationFilePath + * @return void + */ + private function copyFile($destinationFilePath): void + { + //Refers to application root directory + $rootDirectory = $this->fileSystem->getDirectoryWrite(DirectoryList::ROOT); + $rootDirectory->copyFile($this->sourceFilePath, $this->varDirectory->getAbsolutePath($destinationFilePath)); + } + + /** + * Csv file path for copying from sourceFilePath and for future deleting + * + * @return array + */ + public static function testExecuteProvider(): array + { + return [ + ['catalog_product.csv'], + ['test/catalog_product.csv'] + ]; } /** diff --git a/dev/tests/integration/testsuite/Magento/ImportExport/Controller/Adminhtml/Export/File/DownloadTest.php b/dev/tests/integration/testsuite/Magento/ImportExport/Controller/Adminhtml/Export/File/DownloadTest.php index 073ecc6fd06a4..578df4290a99e 100644 --- a/dev/tests/integration/testsuite/Magento/ImportExport/Controller/Adminhtml/Export/File/DownloadTest.php +++ b/dev/tests/integration/testsuite/Magento/ImportExport/Controller/Adminhtml/Export/File/DownloadTest.php @@ -7,7 +7,6 @@ namespace Magento\ImportExport\Controller\Adminhtml\Export\File; -use Magento\Backend\Model\Auth\Session; use Magento\Framework\App\Filesystem\DirectoryList; use Magento\Framework\App\Request\Http; use Magento\Framework\Filesystem; @@ -16,7 +15,6 @@ use Magento\TestFramework\TestCase\AbstractBackendController; use Magento\Backend\Model\UrlInterface as BackendUrl; use Magento\Backend\Model\Auth; -use Magento\TestFramework\Bootstrap as TestBootstrap; /** * Test for \Magento\ImportExport\Controller\Adminhtml\Export\File\Download class. @@ -28,11 +26,6 @@ class DownloadTest extends AbstractBackendController */ private $fileName = 'catalog_product.csv'; - /** - * @var string - */ - private $filesize; - /** * @var Auth */ @@ -43,6 +36,21 @@ class DownloadTest extends AbstractBackendController */ private $backendUrl; + /** + * @var WriteInterface + */ + private $varDirectory; + + /** + * @var Filesystem + */ + private $fileSystem; + + /** + * @var string + */ + private $sourceFilePath; + /** * @inheritdoc */ @@ -50,31 +58,29 @@ protected function setUp() { parent::setUp(); - $filesystem = $this->_objectManager->get(Filesystem::class); + $this->fileSystem = $this->_objectManager->get(Filesystem::class); $auth = $this->_objectManager->get(Auth::class); $auth->getAuthStorage()->setIsFirstPageAfterLogin(false); $this->backendUrl = $this->_objectManager->get(BackendUrl::class); $this->backendUrl->turnOnSecretKey(); - - $sourceFilePath = __DIR__ . '/../../Import/_files' . DIRECTORY_SEPARATOR . $this->fileName; - $destinationFilePath = 'export' . DIRECTORY_SEPARATOR . $this->fileName; + $this->sourceFilePath = __DIR__ . '/../../Import/_files' . DIRECTORY_SEPARATOR . $this->fileName; //Refers to tests 'var' directory - $varDirectory = $filesystem->getDirectoryRead(DirectoryList::VAR_DIR); - //Refers to application root directory - $rootDirectory = $filesystem->getDirectoryWrite(DirectoryList::ROOT); - $rootDirectory->copyFile($sourceFilePath, $varDirectory->getAbsolutePath($destinationFilePath)); - $this->filesize = $varDirectory->stat($destinationFilePath)['size']; + $this->varDirectory = $this->fileSystem->getDirectoryRead(DirectoryList::VAR_DIR); } /** * Check that file can be downloaded. * + * @param string $file + * @dataProvider testExecuteProvider * @return void * @magentoConfigFixture default_store admin/security/use_form_key 1 * @magentoAppArea adminhtml */ - public function testExecute(): void + public function testExecute($file): void { + $this->copyFile('export/' . $file); + $fileSize = $this->varDirectory->stat('export/' . $file)['size']; $request = $this->getRequest(); list($routeName, $controllerName, $actionName) = explode('/', Download::URL); $request->setMethod(Http::METHOD_GET) @@ -104,12 +110,38 @@ public function testExecute(): void 'Incorrect response header "content-disposition"' ); $this->assertEquals( - $this->filesize, + $fileSize, $contentLength->getFieldValue(), 'Incorrect response header "content-length"' ); } + /** + * Copy csv file from sourceFilePath to destinationFilePath + * + * @param $destinationFilePath + * @return void + */ + private function copyFile($destinationFilePath): void + { + //Refers to application root directory + $rootDirectory = $this->fileSystem->getDirectoryWrite(DirectoryList::ROOT); + $rootDirectory->copyFile($this->sourceFilePath, $this->varDirectory->getAbsolutePath($destinationFilePath)); + } + + /** + * Csv file path for copying from sourceFilePath and for future deleting + * + * @return array + */ + public static function testExecuteProvider(): array + { + return [ + ['catalog_product.csv'], + ['test/catalog_product.csv'] + ]; + } + /** * @inheritdoc */ diff --git a/dev/tests/integration/testsuite/Magento/ImportExport/Model/Export/Adapter/CsvTest.php b/dev/tests/integration/testsuite/Magento/ImportExport/Model/Export/Adapter/CsvTest.php new file mode 100644 index 0000000000000..b874e55aea9ed --- /dev/null +++ b/dev/tests/integration/testsuite/Magento/ImportExport/Model/Export/Adapter/CsvTest.php @@ -0,0 +1,70 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +declare(strict_types = 1); + +namespace Magento\ImportExport\Model\Export\Adapter; + +use Magento\Framework\App\Filesystem\DirectoryList; +use Magento\Framework\Filesystem; +use Magento\Framework\ObjectManagerInterface; +use Magento\TestFramework\Helper\Bootstrap; +use PHPUnit\Framework\TestCase; + +/** + * Test for Export adapter csv + */ +class CsvTest extends TestCase +{ + /** + * @var string Destination file name + */ + private $destination = 'destinationFile'; + + /** + * @var ObjectManagerInterface + */ + private $objectManager; + + /** + * @var Csv + */ + private $csv; + + /** + * @inheritdoc + */ + protected function setUp() + { + parent::setUp(); + + $this->objectManager = Bootstrap::getObjectManager(); + $this->csv = $this->objectManager->create( + Csv::class, + ['destination' => $this->destination] + ); + } + + /** + * Test to destruct export adapter + */ + public function testDestruct(): void + { + /** @var Filesystem $fileSystem */ + $fileSystem = $this->objectManager->get(Filesystem::class); + $directoryHandle = $fileSystem->getDirectoryRead(DirectoryList::VAR_DIR); + /** Assert that the destination file is present after construct */ + $this->assertFileExists( + $directoryHandle->getAbsolutePath($this->destination), + 'The destination file was\'t created after construct' + ); + /** Assert that the destination file was removed after destruct */ + $this->csv = null; + $this->assertFileNotExists( + $directoryHandle->getAbsolutePath($this->destination), + 'The destination file was\'t removed after destruct' + ); + } +} diff --git a/dev/tests/integration/testsuite/Magento/Integration/Model/AdminTokenServiceTest.php b/dev/tests/integration/testsuite/Magento/Integration/Model/AdminTokenServiceTest.php index 369d71ddbff9b..68e049804e95a 100644 --- a/dev/tests/integration/testsuite/Magento/Integration/Model/AdminTokenServiceTest.php +++ b/dev/tests/integration/testsuite/Magento/Integration/Model/AdminTokenServiceTest.php @@ -59,6 +59,24 @@ public function testCreateAdminAccessToken() $this->assertEquals($accessToken, $token); } + /** + * @magentoDataFixture Magento/Security/_files/expired_users.php + * @expectedException \Magento\Framework\Exception\AuthenticationException + */ + public function testCreateAdminAccessTokenExpiredUser() + { + $adminUserNameFromFixture = 'adminUserExpired'; + $this->tokenService->createAdminAccessToken( + $adminUserNameFromFixture, + \Magento\TestFramework\Bootstrap::ADMIN_PASSWORD + ); + + $this->expectExceptionMessage( + 'The account sign-in was incorrect or your account is disabled temporarily. ' + . 'Please wait and try again later.' + ); + } + /** * @dataProvider validationDataProvider */ diff --git a/dev/tests/integration/testsuite/Magento/LayeredNavigation/Block/Navigation/AbstractFiltersTest.php b/dev/tests/integration/testsuite/Magento/LayeredNavigation/Block/Navigation/AbstractFiltersTest.php index 472eac444b9d4..fc0c23b346fe2 100644 --- a/dev/tests/integration/testsuite/Magento/LayeredNavigation/Block/Navigation/AbstractFiltersTest.php +++ b/dev/tests/integration/testsuite/Magento/LayeredNavigation/Block/Navigation/AbstractFiltersTest.php @@ -74,22 +74,9 @@ protected function setUp() $this->objectManager = Bootstrap::getObjectManager(); $this->categoryCollectionFactory = $this->objectManager->create(CollectionFactory::class); $this->layout = $this->objectManager->get(LayoutInterface::class); - $layerResolver = $this->objectManager->create(Resolver::class); - - if ($this->getLayerType() === Resolver::CATALOG_LAYER_SEARCH) { - $layerResolver->create(Resolver::CATALOG_LAYER_SEARCH); - $this->navigationBlock = $this->objectManager->create( - SearchNavigationBlock::class, - [ - 'layerResolver' => $layerResolver, - ] - ); - } else { - $this->navigationBlock = $this->objectManager->create(CategoryNavigationBlock::class); - } - $this->attributeRepository = $this->objectManager->create(ProductAttributeRepositoryInterface::class); $this->productRepository = $this->objectManager->create(ProductRepositoryInterface::class); + $this->createNavigationBlockInstance(); } /** @@ -121,7 +108,7 @@ protected function getCategoryFiltersAndAssert( array $expectation, string $categoryName ): void { - $this->updateAttribute($this->getAttributeCode(), $attributeData); + $this->updateAttribute($attributeData); $this->updateProducts($products, $this->getAttributeCode()); $this->clearInstanceAndReindexSearch(); $category = $this->loadCategory($categoryName, Store::DEFAULT_STORE_ID); @@ -150,10 +137,10 @@ protected function getSearchFiltersAndAssert( array $attributeData, array $expectation ): void { - $this->updateAttribute($this->getAttributeCode(), $attributeData); + $this->updateAttribute($attributeData); $this->updateProducts($products, $this->getAttributeCode()); $this->clearInstanceAndReindexSearch(); - $this->navigationBlock->getRequest()->setParams(['q' => 'Simple Product']); + $this->navigationBlock->getRequest()->setParams(['q' => $this->getSearchString()]); $this->navigationBlock->setLayout($this->layout); $filter = $this->getFilterByCode($this->navigationBlock->getFilters(), $this->getAttributeCode()); @@ -188,17 +175,19 @@ function (AbstractFilter $filter) use ($code) { /** * Updates attribute data. * - * @param string $attributeCode * @param array $data * @return void */ protected function updateAttribute( - string $attributeCode, array $data ): void { - $attribute = $this->attributeRepository->get($attributeCode); + $attribute = $this->attributeRepository->get($this->getAttributeCode()); + $attribute->setDataChanges(false); $attribute->addData($data); - $this->attributeRepository->save($attribute); + + if ($attribute->hasDataChanges()) { + $this->attributeRepository->save($attribute); + } } /** @@ -226,16 +215,23 @@ protected function prepareFilterItems(AbstractFilter $filter): array * * @param array $products * @param string $attributeCode + * @param int $storeId * @return void */ - protected function updateProducts(array $products, string $attributeCode): void - { + protected function updateProducts( + array $products, + string $attributeCode, + int $storeId = Store::DEFAULT_STORE_ID + ): void { $attribute = $this->attributeRepository->get($attributeCode); foreach ($products as $productSku => $stringValue) { - $product = $this->productRepository->get($productSku, false, Store::DEFAULT_STORE_ID, true); + $product = $this->productRepository->get($productSku, false, $storeId, true); + $productValue = $attribute->usesSource() + ? $attribute->getSource()->getOptionId($stringValue) + : $stringValue; $product->addData( - [$attribute->getAttributeCode() => $attribute->getSource()->getOptionId($stringValue)] + [$attribute->getAttributeCode() => $productValue] ); $this->productRepository->save($product); } @@ -275,4 +271,36 @@ protected function loadCategory(string $categoryName, int $storeId): CategoryInt return $category; } + + /** + * Creates navigation block instance. + * + * @return void + */ + protected function createNavigationBlockInstance(): void + { + $layerResolver = $this->objectManager->create(Resolver::class); + + if ($this->getLayerType() === Resolver::CATALOG_LAYER_SEARCH) { + $layerResolver->create(Resolver::CATALOG_LAYER_SEARCH); + $this->navigationBlock = $this->objectManager->create( + SearchNavigationBlock::class, + [ + 'layerResolver' => $layerResolver, + ] + ); + } else { + $this->navigationBlock = $this->objectManager->create(CategoryNavigationBlock::class); + } + } + + /** + * Returns search query for filters on search page. + * + * @return string + */ + protected function getSearchString(): string + { + return 'Simple Product'; + } } diff --git a/dev/tests/integration/testsuite/Magento/LayeredNavigation/Block/Navigation/Category/Bundle/MultiselectFilterTest.php b/dev/tests/integration/testsuite/Magento/LayeredNavigation/Block/Navigation/Category/Bundle/MultiselectFilterTest.php new file mode 100644 index 0000000000000..518a0a19f857f --- /dev/null +++ b/dev/tests/integration/testsuite/Magento/LayeredNavigation/Block/Navigation/Category/Bundle/MultiselectFilterTest.php @@ -0,0 +1,109 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +declare(strict_types=1); + +namespace Magento\LayeredNavigation\Block\Navigation\Category\Bundle; + +use Magento\Catalog\Model\Layer\Resolver; +use Magento\Framework\Module\Manager; +use Magento\LayeredNavigation\Block\Navigation\AbstractFiltersTest; +use Magento\Catalog\Model\Layer\Filter\AbstractFilter; + +/** + * Provides tests for custom multiselect filter in navigation block on category page with bundle products. + * + * @magentoAppArea frontend + * @magentoAppIsolation enabled + * @magentoDbIsolation disabled + */ +class MultiselectFilterTest extends AbstractFiltersTest +{ + /** + * @var Manager + */ + private $moduleManager; + + /** + * @inheritdoc + */ + protected function setUp() + { + parent::setUp(); + $this->moduleManager = $this->objectManager->get(Manager::class); + //This check is needed because LayeredNavigation independent of Magento_Bundle + if (!$this->moduleManager->isEnabled('Magento_Bundle')) { + $this->markTestSkipped('Magento_Bundle module disabled.'); + } + } + + /** + * @magentoDataFixture Magento/Catalog/_files/multiselect_attribute.php + * @magentoDataFixture Magento/Bundle/_files/dynamic_and_fixed_bundle_products_in_category.php + * @dataProvider getFiltersWithCustomAttributeDataProvider + * @param array $products + * @param array $attributeData + * @param array $expectation + * @return void + */ + public function testGetFiltersWithCustomAttribute(array $products, array $attributeData, array $expectation): void + { + $this->getCategoryFiltersAndAssert($products, $attributeData, $expectation, 'Category 1'); + } + + /** + * @return array + */ + public function getFiltersWithCustomAttributeDataProvider(): array + { + return [ + 'not_used_in_navigation' => [ + 'products_data' => [], + 'attribute_data' => ['is_filterable' => 0], + 'expectation' => [], + ], + 'used_in_navigation_with_results' => [ + 'products_data' => [ + 'bundle-product' => 'Option 1', + 'bundle-product-dropdown-options' => 'Option 2', + ], + 'attribute_data' => ['is_filterable' => AbstractFilter::ATTRIBUTE_OPTIONS_ONLY_WITH_RESULTS], + 'expectation' => [ + ['label' => 'Option 1', 'count' => 1], + ['label' => 'Option 2', 'count' => 1], + ], + ], + 'used_in_navigation_without_results' => [ + 'products_data' => [ + 'bundle-product' => 'Option 1', + 'bundle-product-dropdown-options' => 'Option 2', + ], + 'attribute_data' => ['is_filterable' => 2], + 'expectation' => [ + ['label' => 'Option 1', 'count' => 1], + ['label' => 'Option 2', 'count' => 1], + ['label' => 'Option 3', 'count' => 0], + ['label' => 'Option 4 "!@#$%^&*', 'count' => 0], + ], + ], + ]; + } + + /** + * @inheritdoc + */ + protected function getLayerType(): string + { + return Resolver::CATALOG_LAYER_CATEGORY; + } + + /** + * @inheritdoc + */ + protected function getAttributeCode(): string + { + return 'multiselect_attribute'; + } +} diff --git a/dev/tests/integration/testsuite/Magento/LayeredNavigation/Block/Navigation/Category/Bundle/PriceFilterTest.php b/dev/tests/integration/testsuite/Magento/LayeredNavigation/Block/Navigation/Category/Bundle/PriceFilterTest.php new file mode 100644 index 0000000000000..3817c5efc86fc --- /dev/null +++ b/dev/tests/integration/testsuite/Magento/LayeredNavigation/Block/Navigation/Category/Bundle/PriceFilterTest.php @@ -0,0 +1,96 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +declare(strict_types=1); + +namespace Magento\LayeredNavigation\Block\Navigation\Category\Bundle; + +use Magento\Catalog\Model\Layer\Resolver; +use Magento\Framework\Module\Manager; +use Magento\LayeredNavigation\Block\Navigation\AbstractFiltersTest; +use Magento\Catalog\Model\Layer\Filter\AbstractFilter; +use Magento\Catalog\Model\Layer\Filter\Item; + +/** + * Provides price filter tests for bundle products in navigation block on category page. + * + * @magentoAppArea frontend + * @magentoAppIsolation enabled + * @magentoDbIsolation disabled + */ +class PriceFilterTest extends AbstractFiltersTest +{ + /** + * @var Manager + */ + private $moduleManager; + + /** + * @inheritdoc + */ + protected function setUp() + { + parent::setUp(); + $this->moduleManager = $this->objectManager->get(Manager::class); + //This check is needed because LayeredNavigation independent of Magento_Bundle + if (!$this->moduleManager->isEnabled('Magento_Bundle')) { + $this->markTestSkipped('Magento_Bundle module disabled.'); + } + } + + /** + * @magentoDataFixture Magento/Bundle/_files/dynamic_and_fixed_bundle_products_in_category.php + * @magentoConfigFixture current_store catalog/layered_navigation/price_range_calculation manual + * @magentoConfigFixture current_store catalog/layered_navigation/price_range_step 10 + * @return void + */ + public function testGetFilters(): void + { + $this->getCategoryFiltersAndAssert( + ['bundle-product' => 20.00], + ['is_filterable' => '1'], + [ + ['label' => '$10.00 - $19.99', 'value' => '10-20', 'count' => 1], + ['label' => '$20.00 and above', 'value' => '20-', 'count' => 1], + ], + 'Category 1' + ); + } + + /** + * @inheritdoc + */ + protected function getLayerType(): string + { + return Resolver::CATALOG_LAYER_CATEGORY; + } + + /** + * @inheritdoc + */ + protected function getAttributeCode(): string + { + return 'price'; + } + + /** + * @inheritdoc + */ + protected function prepareFilterItems(AbstractFilter $filter): array + { + $items = []; + /** @var Item $item */ + foreach ($filter->getItems() as $item) { + $item = [ + 'label' => strip_tags(__($item->getData('label'))->render()), + 'value' => $item->getData('value'), + 'count' => $item->getData('count'), + ]; + $items[] = $item; + } + + return $items; + } +} diff --git a/dev/tests/integration/testsuite/Magento/LayeredNavigation/Block/Navigation/Category/Bundle/SelectFilterTest.php b/dev/tests/integration/testsuite/Magento/LayeredNavigation/Block/Navigation/Category/Bundle/SelectFilterTest.php new file mode 100644 index 0000000000000..7b3be2afbdbdb --- /dev/null +++ b/dev/tests/integration/testsuite/Magento/LayeredNavigation/Block/Navigation/Category/Bundle/SelectFilterTest.php @@ -0,0 +1,108 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +declare(strict_types=1); + +namespace Magento\LayeredNavigation\Block\Navigation\Category\Bundle; + +use Magento\Catalog\Model\Layer\Resolver; +use Magento\Framework\Module\Manager; +use Magento\LayeredNavigation\Block\Navigation\AbstractFiltersTest; +use Magento\Catalog\Model\Layer\Filter\AbstractFilter; + +/** + * Provides tests for custom select filter for bundle products in navigation block on category page. + * + * @magentoAppArea frontend + * @magentoAppIsolation enabled + * @magentoDbIsolation disabled + */ +class SelectFilterTest extends AbstractFiltersTest +{ + /** + * @var Manager + */ + private $moduleManager; + + /** + * @inheritdoc + */ + protected function setUp() + { + parent::setUp(); + $this->moduleManager = $this->objectManager->get(Manager::class); + //This check is needed because LayeredNavigation independent of Magento_Bundle + if (!$this->moduleManager->isEnabled('Magento_Bundle')) { + $this->markTestSkipped('Magento_Bundle module disabled.'); + } + } + + /** + * @magentoDataFixture Magento/Catalog/_files/product_dropdown_attribute.php + * @magentoDataFixture Magento/Bundle/_files/dynamic_and_fixed_bundle_products_in_category.php + * @dataProvider getFiltersWithCustomAttributeDataProvider + * @param array $products + * @param array $attributeData + * @param array $expectation + * @return void + */ + public function testGetFiltersWithCustomAttribute(array $products, array $attributeData, array $expectation): void + { + $this->getCategoryFiltersAndAssert($products, $attributeData, $expectation, 'Category 1'); + } + + /** + * @return array + */ + public function getFiltersWithCustomAttributeDataProvider(): array + { + return [ + 'not_used_in_navigation' => [ + 'products_data' => [], + 'attribute_data' => ['is_filterable' => 0], + 'expectation' => [], + ], + 'used_in_navigation_with_results' => [ + 'products_data' => [ + 'bundle-product' => 'Option 1', + 'bundle-product-dropdown-options' => 'Option 2', + ], + 'attribute_data' => ['is_filterable' => AbstractFilter::ATTRIBUTE_OPTIONS_ONLY_WITH_RESULTS], + 'expectation' => [ + ['label' => 'Option 1', 'count' => 1], + ['label' => 'Option 2', 'count' => 1], + ], + ], + 'used_in_navigation_without_results' => [ + 'products_data' => [ + 'bundle-product' => 'Option 1', + 'bundle-product-dropdown-options' => 'Option 2', + ], + 'attribute_data' => ['is_filterable' => 2], + 'expectation' => [ + ['label' => 'Option 1', 'count' => 1], + ['label' => 'Option 2', 'count' => 1], + ['label' => 'Option 3', 'count' => 0], + ], + ], + ]; + } + + /** + * @inheritdoc + */ + protected function getLayerType(): string + { + return Resolver::CATALOG_LAYER_CATEGORY; + } + + /** + * @inheritdoc + */ + protected function getAttributeCode(): string + { + return 'dropdown_attribute'; + } +} diff --git a/dev/tests/integration/testsuite/Magento/LayeredNavigation/Block/Navigation/Category/Configurable/PriceFilterTest.php b/dev/tests/integration/testsuite/Magento/LayeredNavigation/Block/Navigation/Category/Configurable/PriceFilterTest.php new file mode 100644 index 0000000000000..9dcc713bc4867 --- /dev/null +++ b/dev/tests/integration/testsuite/Magento/LayeredNavigation/Block/Navigation/Category/Configurable/PriceFilterTest.php @@ -0,0 +1,157 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +declare(strict_types=1); + +namespace Magento\LayeredNavigation\Block\Navigation\Category\Configurable; + +use Magento\Catalog\Model\Layer\Resolver; +use Magento\Catalog\Model\Product\Attribute\Source\Status; +use Magento\Framework\Module\Manager; +use Magento\LayeredNavigation\Block\Navigation\AbstractFiltersTest; +use Magento\Catalog\Model\Layer\Filter\AbstractFilter; +use Magento\Catalog\Model\Layer\Filter\Item; +use Magento\Store\Model\Store; + +/** + * Provides price filter tests for configurable in navigation block on category page. + * + * @magentoAppArea frontend + * @magentoAppIsolation enabled + * @magentoDbIsolation disabled + */ +class PriceFilterTest extends AbstractFiltersTest +{ + /** + * @var Manager + */ + private $moduleManager; + + /** + * @inheritdoc + */ + protected function setUp() + { + parent::setUp(); + $this->moduleManager = $this->objectManager->get(Manager::class); + //This check is needed because LayeredNavigation independent of Magento_ConfigurableProduct + if (!$this->moduleManager->isEnabled('Magento_ConfigurableProduct')) { + $this->markTestSkipped('Magento_ConfigurableProduct module disabled.'); + } + } + + /** + * @magentoDataFixture Magento/ConfigurableProduct/_files/configurable_product_with_category.php + * @magentoDataFixture Magento/Catalog/_files/category_product.php + * @magentoConfigFixture current_store catalog/layered_navigation/price_range_calculation manual + * @magentoConfigFixture current_store catalog/layered_navigation/price_range_step 10 + * @dataProvider getFiltersDataProvider + * @param array $products + * @param array $expectation + * @return void + */ + public function testGetFilters(array $products, array $expectation): void + { + $this->updateProductData($products); + $this->getCategoryFiltersAndAssert([], ['is_filterable' => '1'], $expectation, 'Category 1'); + } + + /** + * @return array + */ + public function getFiltersDataProvider(): array + { + return [ + 'all_children_active' => [ + 'products_data' => [ + 'simple333' => ['price' => 60.00], + ], + 'expectation' => [ + [ + 'label' => '<span class="price">$10.00</span> - <span class="price">$19.99</span>', + 'value' => '10-20', + 'count' => 1, + ], + [ + 'label' => '<span class="price">$60.00</span> and above', + 'value' => '60-', + 'count' => 1, + ], + ], + ], + 'one_child_disabled' => [ + 'products_data' => [ + 'simple333' => ['price' => 50.00], + 'simple_10' => ['status' => Status::STATUS_DISABLED], + ], + 'expectation' => [ + [ + 'label' => '<span class="price">$20.00</span> - <span class="price">$29.99</span>', + 'value' => '20-30', + 'count' => 1, + ], + [ + 'label' => '<span class="price">$50.00</span> and above', + 'value' => '50-', + 'count' => 1, + ], + ], + ], + ]; + } + + /** + * @inheritdoc + */ + protected function getLayerType(): string + { + return Resolver::CATALOG_LAYER_CATEGORY; + } + + /** + * @inheritdoc + */ + protected function getAttributeCode(): string + { + return 'price'; + } + + /** + * @inheritdoc + */ + protected function prepareFilterItems(AbstractFilter $filter): array + { + $items = []; + /** @var Item $item */ + foreach ($filter->getItems() as $item) { + $item = [ + 'label' => __($item->getData('label'))->render(), + 'value' => $item->getData('value'), + 'count' => $item->getData('count'), + ]; + $items[] = $item; + } + + return $items; + } + + /** + * Updates products data. + * + * @param array $products + * @param int $storeId + * @return void + */ + private function updateProductData( + array $products, + int $storeId = Store::DEFAULT_STORE_ID + ): void { + foreach ($products as $productSku => $data) { + $product = $this->productRepository->get($productSku, false, $storeId, true); + $product->addData($data); + $this->productRepository->save($product); + } + } +} diff --git a/dev/tests/integration/testsuite/Magento/LayeredNavigation/Block/Navigation/Category/DecimalFilterTest.php b/dev/tests/integration/testsuite/Magento/LayeredNavigation/Block/Navigation/Category/DecimalFilterTest.php index c8cb6397b12fd..53425bd42fbd9 100644 --- a/dev/tests/integration/testsuite/Magento/LayeredNavigation/Block/Navigation/Category/DecimalFilterTest.php +++ b/dev/tests/integration/testsuite/Magento/LayeredNavigation/Block/Navigation/Category/DecimalFilterTest.php @@ -71,22 +71,6 @@ protected function prepareFilterItems(AbstractFilter $filter): array return $items; } - /** - * @inheritdoc - */ - protected function updateProducts(array $products, string $attributeCode): void - { - $attribute = $this->attributeRepository->get($attributeCode); - - foreach ($products as $productSku => $value) { - $product = $this->productRepository->get($productSku, false, Store::DEFAULT_STORE_ID, true); - $product->addData( - [$attribute->getAttributeCode() => $value] - ); - $this->productRepository->save($product); - } - } - /** * @return array */ @@ -98,7 +82,10 @@ public function getFiltersWithCustomAttributeDataProvider(): array 'attribute_data' => ['is_filterable' => 0], 'expectation' => [], ], - 'used_in_navigation_with_results' => [ + + /* @TODO: Should be uncommented in MC-16650 */ + + /*'used_in_navigation_with_results' => [ 'products_data' => [ 'simple1000' => 10.00, 'simple1001' => 20.00, @@ -116,7 +103,7 @@ public function getFiltersWithCustomAttributeDataProvider(): array 'count' => 1, ], ], - ], + ],*/ ]; } } diff --git a/dev/tests/integration/testsuite/Magento/LayeredNavigation/Block/Navigation/Category/FilterScopeTest.php b/dev/tests/integration/testsuite/Magento/LayeredNavigation/Block/Navigation/Category/FilterScopeTest.php new file mode 100644 index 0000000000000..5dad3c0cafa24 --- /dev/null +++ b/dev/tests/integration/testsuite/Magento/LayeredNavigation/Block/Navigation/Category/FilterScopeTest.php @@ -0,0 +1,160 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +declare(strict_types=1); + +namespace Magento\LayeredNavigation\Block\Navigation\Category; + +use Magento\Catalog\Model\Layer\Filter\AbstractFilter; +use Magento\Catalog\Model\Layer\Resolver; +use Magento\Eav\Model\Entity\Attribute\ScopedAttributeInterface; +use Magento\LayeredNavigation\Block\Navigation\AbstractFiltersTest; +use Magento\Store\Model\StoreManagerInterface; + +/** + * Provides tests for custom filter with different scopes in navigation block on category page. + * + * @magentoAppArea frontend + * @magentoAppIsolation enabled + * @magentoDbIsolation disabled + */ +class FilterScopeTest extends AbstractFiltersTest +{ + /** + * @var StoreManagerInterface + */ + private $storeManager; + + /** + * @var int + */ + private $oldStoreId; + + /** + * @var int + */ + private $currentStoreId; + + /** + * @inheritdoc + */ + protected function setUp() + { + parent::setUp(); + $this->storeManager = $this->objectManager->get(StoreManagerInterface::class); + $this->oldStoreId = (int)$this->storeManager->getStore()->getId(); + $this->currentStoreId = (int)$this->storeManager->getStore('fixture_second_store')->getId(); + } + + /** + * @magentoDataFixture Magento/Catalog/_files/product_dropdown_attribute.php + * @magentoDataFixture Magento/Catalog/_files/category_with_different_price_products_on_two_websites.php + * @dataProvider filtersWithScopeDataProvider + * @param int $scope + * @param array $products + * @param array $expectation + * @return void + */ + public function testGetFilters(int $scope, array $products, array $expectation): void + { + $this->updateAttribute( + [ + 'is_filterable' => AbstractFilter::ATTRIBUTE_OPTIONS_ONLY_WITH_RESULTS, + 'is_global' => $scope, + ] + ); + $this->updateProductsOnStore($products); + $this->clearInstanceAndReindexSearch(); + try { + $this->storeManager->setCurrentStore($this->currentStoreId); + $this->navigationBlock->getLayer()->setCurrentCategory( + $this->loadCategory('Category 999', $this->currentStoreId) + ); + $this->navigationBlock->setLayout($this->layout); + $filter = $this->getFilterByCode($this->navigationBlock->getFilters(), $this->getAttributeCode()); + $this->assertNotNull($filter); + $this->assertEquals($expectation, $this->prepareFilterItems($filter)); + } finally { + $this->storeManager->setCurrentStore($this->oldStoreId); + } + } + + /** + * @return array + */ + public function filtersWithScopeDataProvider(): array + { + return [ + 'with_scope_store' => [ + 'scope' => ScopedAttributeInterface::SCOPE_STORE, + 'products' => [ + 'default' => ['simple1000' => 'Option 1', 'simple1001' => 'Option 2'], + 'fixture_second_store' => ['simple1000' => 'Option 2', 'simple1001' => 'Option 3'], + ], + 'expectation' => [ + ['label' => 'Option 2', 'count' => 1], + ['label' => 'Option 3', 'count' => 1], + ], + ], + 'with_scope_website' => [ + 'scope' => ScopedAttributeInterface::SCOPE_WEBSITE, + 'products' => [ + 'default' => ['simple1000' => 'Option 3', 'simple1001' => 'Option 2'], + 'fixture_second_store' => ['simple1000' => 'Option 1', 'simple1001' => 'Option 2'], + ], + 'expectation' => [ + ['label' => 'Option 1', 'count' => 1], + ['label' => 'Option 2', 'count' => 1], + ], + ], + 'with_scope_global' => [ + 'scope' => ScopedAttributeInterface::SCOPE_GLOBAL, + 'products' => [ + 'default' => ['simple1000' => 'Option 1'], + 'fixture_second_store' => ['simple1001' => 'Option 2'], + ], + 'expectation' => [ + ['label' => 'Option 1', 'count' => 1], + ['label' => 'Option 2', 'count' => 1], + ], + ], + ]; + } + + /** + * @inheritdoc + */ + protected function getLayerType(): string + { + return Resolver::CATALOG_LAYER_CATEGORY; + } + + /** + * @inheritdoc + */ + protected function getAttributeCode(): string + { + return 'dropdown_attribute'; + } + + /** + * Updates products data for store. + * + * @param array $productsData + * @return void + */ + private function updateProductsOnStore(array $productsData): void + { + try { + foreach ($productsData as $storeCode => $products) { + $storeId = (int)$this->storeManager->getStore($storeCode)->getId(); + $this->storeManager->setCurrentStore($storeId); + $this->updateProducts($products, $this->getAttributeCode(), $storeId); + } + } finally { + $this->storeManager->setCurrentStore($this->oldStoreId); + } + } +} diff --git a/dev/tests/integration/testsuite/Magento/LayeredNavigation/Block/Navigation/Category/OutOfStockProductsFilterTest.php b/dev/tests/integration/testsuite/Magento/LayeredNavigation/Block/Navigation/Category/OutOfStockProductsFilterTest.php new file mode 100644 index 0000000000000..9a0deedea94ab --- /dev/null +++ b/dev/tests/integration/testsuite/Magento/LayeredNavigation/Block/Navigation/Category/OutOfStockProductsFilterTest.php @@ -0,0 +1,110 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +declare(strict_types=1); + +namespace Magento\LayeredNavigation\Block\Navigation\Category; + +use Magento\Catalog\Model\Layer\Resolver; +use Magento\CatalogInventory\Model\Configuration; +use Magento\Framework\App\Config\MutableScopeConfigInterface; +use Magento\Framework\App\ScopeInterface; +use Magento\LayeredNavigation\Block\Navigation\AbstractFiltersTest; +use Magento\Catalog\Model\Layer\Filter\AbstractFilter; +use Magento\Store\Model\ScopeInterface as StoreScope; + +/** + * Provides tests for select filter in navigation block on category page with out of stock products + * and enabled out of stock products displaying. + * + * @magentoAppArea frontend + * @magentoAppIsolation enabled + * @magentoDbIsolation disabled + */ +class OutOfStockProductsFilterTest extends AbstractFiltersTest +{ + /** + * @var MutableScopeConfigInterface + */ + private $scopeConfig; + + /** + * @inheritdoc + */ + protected function setUp() + { + parent::setUp(); + $this->scopeConfig = $this->objectManager->get(MutableScopeConfigInterface::class); + } + + /** + * @magentoDataFixture Magento/Catalog/_files/product_dropdown_attribute.php + * @magentoDataFixture Magento/Catalog/_files/out_of_stock_product_with_category.php + * @magentoDataFixture Magento/Catalog/_files/product_with_category.php + * @dataProvider getFiltersWithOutOfStockProduct + * @param int $showOutOfStock + * @param array $expectation + * @return void + */ + public function testGetFiltersWithOutOfStockProduct(int $showOutOfStock, array $expectation): void + { + $this->updateConfigShowOutOfStockFlag($showOutOfStock); + $this->getCategoryFiltersAndAssert( + ['out-of-stock-product' => 'Option 1', 'in-stock-product' => 'Option 2'], + ['is_filterable' => AbstractFilter::ATTRIBUTE_OPTIONS_ONLY_WITH_RESULTS], + $expectation, + 'Category 1' + ); + } + + /** + * @return array + */ + public function getFiltersWithOutOfStockProduct(): array + { + return [ + 'show_out_of_stock' => [ + 'show_out_of_stock' => 1, + 'expectation' => [['label' => 'Option 1', 'count' => 1], ['label' => 'Option 2', 'count' => 1]], + ], + 'not_show_out_of_stock' => [ + 'show_out_of_stock' => 0, + 'expectation' => [['label' => 'Option 2', 'count' => 1]], + ], + ]; + } + + /** + * @inheritdoc + */ + protected function getLayerType(): string + { + return Resolver::CATALOG_LAYER_CATEGORY; + } + + /** + * @inheritdoc + */ + protected function getAttributeCode(): string + { + return 'dropdown_attribute'; + } + + /** + * Updates store config 'cataloginventory/options/show_out_of_stock' flag. + * + * @param int $showOutOfStock + * @return void + */ + protected function updateConfigShowOutOfStockFlag(int $showOutOfStock): void + { + $this->scopeConfig->setValue( + Configuration::XML_PATH_SHOW_OUT_OF_STOCK, + $showOutOfStock, + StoreScope::SCOPE_STORE, + ScopeInterface::SCOPE_DEFAULT + ); + } +} diff --git a/dev/tests/integration/testsuite/Magento/LayeredNavigation/Block/Navigation/Category/PriceFilterTest.php b/dev/tests/integration/testsuite/Magento/LayeredNavigation/Block/Navigation/Category/PriceFilterTest.php new file mode 100644 index 0000000000000..a82b4bf0fd00d --- /dev/null +++ b/dev/tests/integration/testsuite/Magento/LayeredNavigation/Block/Navigation/Category/PriceFilterTest.php @@ -0,0 +1,212 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +declare(strict_types=1); + +namespace Magento\LayeredNavigation\Block\Navigation\Category; + +use Magento\Catalog\Model\Layer\Resolver; +use Magento\Framework\App\Config\MutableScopeConfigInterface; +use Magento\Framework\App\ScopeInterface; +use Magento\LayeredNavigation\Block\Navigation\AbstractFiltersTest; +use Magento\Catalog\Model\Layer\Filter\AbstractFilter; +use Magento\Catalog\Model\Layer\Filter\Item; +use Magento\Store\Model\ScopeInterface as StoreScope; +use Magento\Store\Model\Store; + +/** + * Provides price filter tests with different price ranges calculation in navigation block on category page. + * + * @magentoAppArea frontend + * @magentoAppIsolation enabled + * @magentoDbIsolation disabled + */ +class PriceFilterTest extends AbstractFiltersTest +{ + /** + * @var MutableScopeConfigInterface + */ + private $scopeConfig; + + /** + * @inheritdoc + */ + protected function setUp() + { + parent::setUp(); + $this->scopeConfig = $this->objectManager->get(MutableScopeConfigInterface::class); + } + + /** + * @magentoDataFixture Magento/Catalog/_files/category_with_three_products.php + * @dataProvider getFiltersDataProvider + * @param array $config + * @param array $products + * @param array $expectation + * @return void + */ + public function testGetFilters(array $config, array $products, array $expectation): void + { + $this->applyCatalogConfig($config); + $this->getCategoryFiltersAndAssert( + $products, + ['is_filterable' => AbstractFilter::ATTRIBUTE_OPTIONS_ONLY_WITH_RESULTS], + $expectation, + 'Category 999' + ); + } + + /** + * @SuppressWarnings(PHPMD.ExcessiveMethodLength) + * @return array + */ + public function getFiltersDataProvider(): array + { + return [ + 'auto_calculation_variation_with_small_price_difference' => [ + 'config' => ['catalog/layered_navigation/price_range_calculation' => 'auto'], + 'products_data' => ['simple1000' => 10.00, 'simple1001' => 20.00, 'simple1002' => 50.00], + 'expectation' => [ + ['label' => '$10.00 - $19.99', 'value' => '10-20', 'count' => 1], + ['label' => '$20.00 - $29.99', 'value' => '20-30', 'count' => 1], + ['label' => '$50.00 and above', 'value' => '50-', 'count' => 1], + ], + ], + 'auto_calculation_variation_with_big_price_difference' => [ + 'config' => ['catalog/layered_navigation/price_range_calculation' => 'auto'], + 'products_data' => ['simple1000' => 10.00, 'simple1001' => 20.00, 'simple1002' => 300.00], + 'expectation' => [ + ['label' => '$0.00 - $99.99', 'value' => '-100', 'count' => 2], + ['label' => '$300.00 and above', 'value' => '300-', 'count' => 1], + ], + ], + 'auto_calculation_variation_with_fixed_price_step' => [ + 'config' => ['catalog/layered_navigation/price_range_calculation' => 'auto'], + 'products_data' => ['simple1000' => 300.00, 'simple1001' => 400.00, 'simple1002' => 500.00], + 'expectation' => [ + ['label' => '$300.00 - $399.99', 'value' => '300-400', 'count' => 1], + ['label' => '$400.00 - $499.99', 'value' => '400-500', 'count' => 1], + ['label' => '$500.00 and above', 'value' => '500-', 'count' => 1], + ], + ], + 'improved_calculation_variation_with_small_price_difference' => [ + 'config' => [ + 'catalog/layered_navigation/price_range_calculation' => 'improved', + 'catalog/layered_navigation/interval_division_limit' => 3, + ], + 'products_data' => ['simple1000' => 10.00, 'simple1001' => 20.00, 'simple1002' => 50.00], + 'expectation' => [ + ['label' => '$0.00 - $49.99', 'value' => '-50', 'count' => 2], + ['label' => '$50.00 and above', 'value' => '50-', 'count' => 1], + ], + ], + 'improved_calculation_variation_with_big_price_difference' => [ + 'config' => [ + 'catalog/layered_navigation/price_range_calculation' => 'improved', + 'catalog/layered_navigation/interval_division_limit' => 3, + ], + 'products_data' => ['simple1000' => 10.00, 'simple1001' => 20.00, 'simple1002' => 300.00], + 'expectation' => [ + ['label' => '$0.00 - $299.99', 'value' => '-300', 'count' => 2.0], + ['label' => '$300.00 and above', 'value' => '300-', 'count' => 1.0], + ], + ], + 'manual_calculation_with_price_step_200' => [ + 'config' => [ + 'catalog/layered_navigation/price_range_calculation' => 'manual', + 'catalog/layered_navigation/price_range_step' => 200, + ], + 'products_data' => ['simple1000' => 300.00, 'simple1001' => 300.00, 'simple1002' => 500.00], + 'expectation' => [ + ['label' => '$200.00 - $399.99', 'value' => '200-400', 'count' => 2], + ['label' => '$400.00 and above', 'value' => '400-', 'count' => 1], + ], + ], + 'manual_calculation_with_price_step_10' => [ + 'config' => [ + 'catalog/layered_navigation/price_range_calculation' => 'manual', + 'catalog/layered_navigation/price_range_step' => 10, + ], + 'products_data' => ['simple1000' => 300.00, 'simple1001' => 300.00, 'simple1002' => 500.00], + 'expectation' => [ + ['label' => '$300.00 - $309.99', 'value' => '300-310', 'count' => 2], + ['label' => '$500.00 and above', 'value' => '500-', 'count' => 1], + ], + ], + 'manual_calculation_with_number_of_intervals_10' => [ + 'config' => [ + 'catalog/layered_navigation/price_range_calculation' => 'manual', + 'catalog/layered_navigation/price_range_step' => 10, + 'catalog/layered_navigation/price_range_max_intervals' => 10, + ], + 'products_data' => ['simple1000' => 10.00, 'simple1001' => 20.00, 'simple1002' => 30.00], + 'expectation' => [ + ['label' => '$10.00 - $19.99', 'value' => '10-20', 'count' => 1], + ['label' => '$20.00 - $29.99', 'value' => '20-30', 'count' => 1], + ['label' => '$30.00 and above', 'value' => '30-', 'count' => 1], + ], + ], + 'manual_calculation_with_number_of_intervals_2' => [ + 'config' => [ + 'catalog/layered_navigation/price_range_calculation' => 'manual', + 'catalog/layered_navigation/price_range_step' => 10, + 'catalog/layered_navigation/price_range_max_intervals' => 2, + ], + 'products_data' => ['simple1000' => 10.00, 'simple1001' => 20.00, 'simple1002' => 30.00], + 'expectation' => [ + ['label' => '$10.00 - $19.99', 'value' => '10-20', 'count' => 1], + ['label' => '$20.00 and above', 'value' => '20-', 'count' => 2], + ], + ], + ]; + } + + /** + * @inheritdoc + */ + protected function getLayerType(): string + { + return Resolver::CATALOG_LAYER_CATEGORY; + } + + /** + * @inheritdoc + */ + protected function getAttributeCode(): string + { + return 'price'; + } + + /** + * @inheritdoc + */ + protected function prepareFilterItems(AbstractFilter $filter): array + { + $items = []; + /** @var Item $item */ + foreach ($filter->getItems() as $item) { + $items[] = [ + 'label' => strip_tags(__($item->getData('label'))->render()), + 'value' => $item->getData('value'), + 'count' => $item->getData('count'), + ]; + } + + return $items; + } + + /** + * Updates price filter store configuration. + * + * @param array $config + * @return void + */ + protected function applyCatalogConfig(array $config): void + { + foreach ($config as $path => $value) { + $this->scopeConfig->setValue($path, $value, StoreScope::SCOPE_STORE, ScopeInterface::SCOPE_DEFAULT); + } + } +} diff --git a/dev/tests/integration/testsuite/Magento/LayeredNavigation/Block/Navigation/Search/Bundle/PriceFilterTest.php b/dev/tests/integration/testsuite/Magento/LayeredNavigation/Block/Navigation/Search/Bundle/PriceFilterTest.php new file mode 100644 index 0000000000000..435dd29e16dfa --- /dev/null +++ b/dev/tests/integration/testsuite/Magento/LayeredNavigation/Block/Navigation/Search/Bundle/PriceFilterTest.php @@ -0,0 +1,55 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +declare(strict_types=1); + +namespace Magento\LayeredNavigation\Block\Navigation\Search\Bundle; + +use Magento\Catalog\Model\Layer\Resolver; +use Magento\LayeredNavigation\Block\Navigation\Category\Bundle\PriceFilterTest as CategoryFilterTest; + +/** + * Provides price filter tests for bundle product in navigation block on search page. + * + * @magentoAppArea frontend + * @magentoAppIsolation enabled + * @magentoDbIsolation disabled + */ +class PriceFilterTest extends CategoryFilterTest +{ + /** + * @magentoDataFixture Magento/Bundle/_files/dynamic_and_fixed_bundle_products_in_category.php + * @magentoConfigFixture current_store catalog/layered_navigation/price_range_calculation manual + * @magentoConfigFixture current_store catalog/layered_navigation/price_range_step 10 + * @return void + */ + public function testGetFilters(): void + { + $this->getSearchFiltersAndAssert( + ['bundle-product' => 20.00], + ['is_filterable_in_search' => 1], + [ + ['label' => '$10.00 - $19.99', 'value' => '10-20', 'count' => 1], + ['label' => '$20.00 and above', 'value' => '20-', 'count' => 1], + ] + ); + } + + /** + * @inheritdoc + */ + protected function getLayerType(): string + { + return Resolver::CATALOG_LAYER_SEARCH; + } + + /** + * @inheritdoc + */ + protected function getSearchString(): string + { + return 'Bundle'; + } +} diff --git a/dev/tests/integration/testsuite/Magento/LayeredNavigation/Block/Navigation/Search/DecimalFilterTest.php b/dev/tests/integration/testsuite/Magento/LayeredNavigation/Block/Navigation/Search/DecimalFilterTest.php index a4d8a64add4b7..325bf1a3d50dc 100644 --- a/dev/tests/integration/testsuite/Magento/LayeredNavigation/Block/Navigation/Search/DecimalFilterTest.php +++ b/dev/tests/integration/testsuite/Magento/LayeredNavigation/Block/Navigation/Search/DecimalFilterTest.php @@ -50,12 +50,15 @@ public function getFiltersWithCustomAttributeDataProvider(): array 'not_used_in_navigation' => [ 'attribute_data' => ['is_filterable' => 0, 'is_filterable_in_search' => 0], ], - 'used_in_navigation_with_results' => [ + + /* @TODO: Should be uncommented in MC-16650 */ + + /*'used_in_navigation_with_results' => [ 'attribute_data' => [ 'is_filterable' => AbstractFilter::ATTRIBUTE_OPTIONS_ONLY_WITH_RESULTS, 'is_filterable_in_search' => 1, ], - ], + ],*/ ] ); diff --git a/dev/tests/integration/testsuite/Magento/LayeredNavigation/Block/Navigation/Search/OutOfStockProductsFilterTest.php b/dev/tests/integration/testsuite/Magento/LayeredNavigation/Block/Navigation/Search/OutOfStockProductsFilterTest.php new file mode 100644 index 0000000000000..c4b7b3bdcc68b --- /dev/null +++ b/dev/tests/integration/testsuite/Magento/LayeredNavigation/Block/Navigation/Search/OutOfStockProductsFilterTest.php @@ -0,0 +1,53 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +declare(strict_types=1); + +namespace Magento\LayeredNavigation\Block\Navigation\Search; + +use Magento\Catalog\Model\Layer\Filter\AbstractFilter; +use Magento\Catalog\Model\Layer\Resolver; +use Magento\LayeredNavigation\Block\Navigation\Category\OutOfStockProductsFilterTest as CategoryFilterTest; + +/** + * Provides tests for select filter in navigation block on search page with out of stock products + * and enabled out of stock products displaying. + * + * @magentoAppArea frontend + * @magentoAppIsolation enabled + * @magentoDbIsolation disabled + */ +class OutOfStockProductsFilterTest extends CategoryFilterTest +{ + /** + * @magentoDataFixture Magento/Catalog/_files/product_dropdown_attribute.php + * @magentoDataFixture Magento/Catalog/_files/out_of_stock_product_with_category.php + * @magentoDataFixture Magento/Catalog/_files/product_with_category.php + * @dataProvider getFiltersWithOutOfStockProduct + * @param int $showOutOfStock + * @param array $expectation + * @return void + */ + public function testGetFiltersWithOutOfStockProduct(int $showOutOfStock, array $expectation): void + { + $this->updateConfigShowOutOfStockFlag($showOutOfStock); + $this->getSearchFiltersAndAssert( + ['out-of-stock-product' => 'Option 1', 'in-stock-product' => 'Option 2'], + [ + 'is_filterable' => AbstractFilter::ATTRIBUTE_OPTIONS_ONLY_WITH_RESULTS, + 'is_filterable_in_search' => 1, + ], + $expectation + ); + } + + /** + * @inheritdoc + */ + protected function getLayerType(): string + { + return Resolver::CATALOG_LAYER_SEARCH; + } +} diff --git a/dev/tests/integration/testsuite/Magento/LayeredNavigation/Block/Navigation/Search/PriceFilterTest.php b/dev/tests/integration/testsuite/Magento/LayeredNavigation/Block/Navigation/Search/PriceFilterTest.php new file mode 100644 index 0000000000000..d9ac02b2bff11 --- /dev/null +++ b/dev/tests/integration/testsuite/Magento/LayeredNavigation/Block/Navigation/Search/PriceFilterTest.php @@ -0,0 +1,51 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +declare(strict_types=1); + +namespace Magento\LayeredNavigation\Block\Navigation\Search; + +use Magento\Catalog\Model\Layer\Filter\AbstractFilter; +use Magento\Catalog\Model\Layer\Resolver; +use Magento\LayeredNavigation\Block\Navigation\Category\PriceFilterTest as CategoryPriceFilterTest; + +/** + * Provides price filter tests with different price ranges calculation in navigation block on search page. + * + * @magentoAppArea frontend + * @magentoAppIsolation enabled + * @magentoDbIsolation disabled + */ +class PriceFilterTest extends CategoryPriceFilterTest +{ + /** + * @magentoDataFixture Magento/Catalog/_files/category_with_three_products.php + * @dataProvider getFiltersDataProvider + * @param array $config + * @param array $products + * @param array $expectation + * @return void + */ + public function testGetFilters(array $config, array $products, array $expectation): void + { + $this->applyCatalogConfig($config); + $this->getSearchFiltersAndAssert( + $products, + [ + 'is_filterable' => AbstractFilter::ATTRIBUTE_OPTIONS_ONLY_WITH_RESULTS, + 'is_filterable_in_search' => 1, + ], + $expectation + ); + } + + /** + * @inheritdoc + */ + protected function getLayerType(): string + { + return Resolver::CATALOG_LAYER_SEARCH; + } +} diff --git a/dev/tests/integration/testsuite/Magento/Newsletter/Block/Account/LinkTest.php b/dev/tests/integration/testsuite/Magento/Newsletter/Block/Account/LinkTest.php new file mode 100644 index 0000000000000..6dac22bd49384 --- /dev/null +++ b/dev/tests/integration/testsuite/Magento/Newsletter/Block/Account/LinkTest.php @@ -0,0 +1,79 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +declare(strict_types=1); + +namespace Magento\Newsletter\Block\Account; + +use Magento\Framework\ObjectManagerInterface; +use Magento\Framework\View\Result\Page; +use Magento\TestFramework\Helper\Bootstrap; +use PHPUnit\Framework\TestCase; + +/** + * Checks Newsletter Subscriptions link displaying in account dashboard + * + * @magentoAppArea frontend + * @magentoDbIsolation enabled + * @magentoAppIsolation enabled + */ +class LinkTest extends TestCase +{ + /** @var ObjectManagerInterface */ + private $objectManager; + + /** @var Page */ + private $page; + + /** + * @inheritdoc + */ + protected function setUp() + { + parent::setUp(); + + $this->objectManager = Bootstrap::getObjectManager(); + $this->page = $this->objectManager->create(Page::class); + } + + /** + * @return void + */ + public function testNewsletterLink(): void + { + $this->preparePage(); + $block = $this->page->getLayout()->getBlock('customer-account-navigation-newsletter-subscriptions-link'); + $this->assertNotFalse($block); + $html = $block->toHtml(); + $this->assertContains('newsletter/manage/', $html); + $this->assertEquals('Newsletter Subscriptions', strip_tags($html)); + } + + /** + * @magentoConfigFixture current_store newsletter/general/active 0 + * + * @return void + */ + public function testNewsletterLinkDisabled(): void + { + $this->preparePage(); + $block = $this->page->getLayout()->getBlock('customer-account-navigation-newsletter-subscriptions-link'); + $this->assertFalse($block); + } + + /** + * Prepare page before render + * + * @return void + */ + private function preparePage(): void + { + $this->page->addHandle([ + 'default', + 'customer_account', + ]); + $this->page->getLayout()->generateXml(); + } +} diff --git a/dev/tests/integration/testsuite/Magento/Newsletter/Controller/Adminhtml/NewsletterTemplateTest.php b/dev/tests/integration/testsuite/Magento/Newsletter/Controller/Adminhtml/NewsletterTemplateTest.php index ae57703f9e8e2..06450d2a1d187 100644 --- a/dev/tests/integration/testsuite/Magento/Newsletter/Controller/Adminhtml/NewsletterTemplateTest.php +++ b/dev/tests/integration/testsuite/Magento/Newsletter/Controller/Adminhtml/NewsletterTemplateTest.php @@ -36,7 +36,7 @@ protected function setUp() 'text' => 'Template Content', 'form_key' => $this->formKey, ]; - $this->getRequest()->setPostValue($post)->setMethod(\Zend\Http\Request::METHOD_POST); + $this->getRequest()->setPostValue($post)->setMethod(\Laminas\Http\Request::METHOD_POST); $this->model = \Magento\TestFramework\Helper\Bootstrap::getObjectManager()->create( \Magento\Newsletter\Model\Template::class ); @@ -141,7 +141,7 @@ public function testSaveActionTemplateWithGetAndVerifyRedirect() // Loading by code, since ID will vary. template_code is not actually used to load anywhere else. $this->model->load('some_unique_code', 'template_code'); - $this->getRequest()->setMethod(\Zend\Http\Request::METHOD_GET)->setParam('id', $this->model->getId()); + $this->getRequest()->setMethod(\Laminas\Http\Request::METHOD_GET)->setParam('id', $this->model->getId()); $this->dispatch('backend/newsletter/template/save'); $this->assertEquals(404, $this->getResponse()->getStatusCode()); diff --git a/dev/tests/integration/testsuite/Magento/Newsletter/Controller/Ajax/StatusTest.php b/dev/tests/integration/testsuite/Magento/Newsletter/Controller/Ajax/StatusTest.php new file mode 100755 index 0000000000000..3d16cb27e8668 --- /dev/null +++ b/dev/tests/integration/testsuite/Magento/Newsletter/Controller/Ajax/StatusTest.php @@ -0,0 +1,67 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +declare(strict_types=1); + +namespace Magento\Newsletter\Controller\Ajax; + +use Magento\Framework\Serialize\SerializerInterface; +use Magento\TestFramework\TestCase\AbstractController; + +/** + * Test Subscriber status ajax + */ +class StatusTest extends AbstractController +{ + /** + * @var SerializerInterface + */ + private $json; + + /** + * @inheritDoc + */ + protected function setUp() + { + parent::setUp(); + $this->json = $this->_objectManager->get(SerializerInterface::class); + } + + /** + * Check newsletter subscription status verification + * + * @magentoDataFixture Magento/Newsletter/_files/subscribers.php + * @dataProvider ajaxSubscriberDataProvider + * @param string $expStatus + * @param string $email + * + * @return void + */ + public function testExecute(string $expStatus, string $email): void + { + $this->getRequest()->setParam('email', $email); + $this->dispatch('newsletter/ajax/status'); + $actual = $this->json->unserialize($this->getResponse()->getBody()); + + $this->assertEquals($expStatus, $actual['subscribed']); + } + + /** + * Provides data and Expected Result + * + * @param void + * @return array + */ + public function ajaxSubscriberDataProvider(): array + { + return [ + 'empty_email_parameter' => [false, ''], + 'invalid_email' => [false, 'invalid_email.com'], + 'unsubscribed_email' => [false, 'sample@email.com'], + 'subscribed_guest_email' => [true, 'customer_two@example.com'], + 'subscribed_registered_customer_email' => [false, 'customer@example.com'], + ]; + } +} diff --git a/dev/tests/integration/testsuite/Magento/Newsletter/Controller/Manage/SaveTest.php b/dev/tests/integration/testsuite/Magento/Newsletter/Controller/Manage/SaveTest.php new file mode 100644 index 0000000000000..87b022c942318 --- /dev/null +++ b/dev/tests/integration/testsuite/Magento/Newsletter/Controller/Manage/SaveTest.php @@ -0,0 +1,157 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +declare(strict_types=1); + +namespace Magento\Newsletter\Controller\Manage; + +use Magento\Customer\Api\CustomerRepositoryInterface; +use Magento\Customer\Model\CustomerRegistry; +use Magento\Customer\Model\Session; +use Magento\Framework\Data\Form\FormKey; +use Magento\Framework\Message\MessageInterface; +use Magento\Newsletter\Model\Plugin\CustomerPlugin; +use Magento\TestFramework\TestCase\AbstractController; + +/** + * Class checks customer subscription + * + * @magentoDbIsolation enabled + */ +class SaveTest extends AbstractController +{ + /** @var Session */ + protected $customerSession; + + /** @var CustomerRepositoryInterface */ + private $customerRepository; + + /** @var FormKey */ + private $formKey; + + /** @var CustomerRegistry */ + private $customerRegistry; + + /** + * @inheritdoc + */ + protected function setUp() + { + parent::setUp(); + + $this->customerSession = $this->_objectManager->get(Session::class); + $this->customerRepository = $this->_objectManager->get(CustomerRepositoryInterface::class); + $this->formKey = $this->_objectManager->get(FormKey::class); + $this->customerRegistry = $this->_objectManager->get(CustomerRegistry::class); + } + + /** + * @inheritdoc + */ + protected function tearDown() + { + $this->customerSession->logout(); + + parent::tearDown(); + } + + /** + * @magentoDataFixture Magento/Customer/_files/new_customer.php + * + * @dataProvider subscriptionDataProvider + * + * @param bool $isSubscribed + * @param string $expectedMessage + * @return void + */ + public function testSaveAction(bool $isSubscribed, string $expectedMessage): void + { + $this->loginCustomer('new_customer@example.com'); + $this->_objectManager->removeSharedInstance(CustomerPlugin::class); + $this->dispatchSaveAction($isSubscribed); + $this->assertSuccessSubscription($expectedMessage); + } + + /** + * @return array + */ + public function subscriptionDataProvider(): array + { + return [ + 'subscribe_customer' => [ + 'is_subscribed' => true, + 'expected_message' => 'We have saved your subscription.', + ], + 'unsubscribe_customer' => [ + 'is_subscribed' => false, + 'expected_message' => 'We have updated your subscription.', + ], + ]; + } + + /** + * @magentoDataFixture Magento/Customer/_files/customer_confirmation_config_enable.php + * @magentoDataFixture Magento/Customer/_files/new_customer.php + * + * @return void + */ + public function testSubscribeWithEnabledConfirmation(): void + { + $this->loginCustomer('new_customer@example.com'); + $this->dispatchSaveAction(true); + $this->assertSuccessSubscription('A confirmation request has been sent.'); + } + + /** + * @magentoDataFixture Magento/Newsletter/_files/customer_with_subscription.php + * + * @return void + */ + public function testUnsubscribeSubscribedCustomer(): void + { + $this->loginCustomer('new_customer@example.com'); + $this->_objectManager->removeSharedInstance(CustomerPlugin::class); + $this->dispatchSaveAction(false); + $this->assertSuccessSubscription('We have removed your newsletter subscription.'); + } + + /** + * Dispatch save action with parameters + * + * @param string $isSubscribed + * @return void + */ + private function dispatchSaveAction(bool $isSubscribed): void + { + $this->_objectManager->removeSharedInstance(CustomerPlugin::class); + $this->getRequest()->setParam('form_key', $this->formKey->getFormKey()) + ->setParam('is_subscribed', $isSubscribed); + $this->dispatch('newsletter/manage/save'); + } + + /** + * Login customer by email + * + * @param string $email + * @return void + */ + private function loginCustomer(string $email): void + { + $customer = $this->customerRepository->get($email); + $this->customerSession->loginById($customer->getId()); + } + + /** + * Assert that action was successfully done + * + * @param string $expectedMessage + * @return void + */ + private function assertSuccessSubscription(string $expectedMessage): void + { + $this->assertRedirect($this->stringContains('customer/account/')); + $this->assertSessionMessages($this->equalTo([(string)__($expectedMessage)]), MessageInterface::TYPE_SUCCESS); + } +} diff --git a/dev/tests/integration/testsuite/Magento/Newsletter/Controller/ManageTest.php b/dev/tests/integration/testsuite/Magento/Newsletter/Controller/ManageTest.php deleted file mode 100644 index 175c1c7c6c668..0000000000000 --- a/dev/tests/integration/testsuite/Magento/Newsletter/Controller/ManageTest.php +++ /dev/null @@ -1,98 +0,0 @@ -<?php -/** - * Copyright © Magento, Inc. All rights reserved. - * See COPYING.txt for license details. - */ - -namespace Magento\Newsletter\Controller; - -/** - * @magentoDbIsolation enabled - */ -class ManageTest extends \Magento\TestFramework\TestCase\AbstractController -{ - /** - * @var \Magento\Customer\Model\Session - */ - protected $customerSession; - - /** - * @var \Magento\Framework\Session\Generic - */ - protected $coreSession; - - /** - * Test setup - */ - protected function setUp() - { - parent::setUp(); - $objectManager = \Magento\TestFramework\Helper\Bootstrap::getObjectManager(); - $this->customerSession = $objectManager->get(\Magento\Customer\Model\Session::class); - $this->customerSession->setCustomerId(1); - $this->coreSession = $objectManager->get(\Magento\Framework\Session\Generic::class); - $this->coreSession->setData('_form_key', 'formKey'); - } - - /** - * test tearDown - */ - protected function tearDown() - { - $this->customerSession->setCustomerId(null); - $this->coreSession->unsetData('_form_key'); - } - - /** - * @magentoDataFixture Magento/Customer/_files/customer.php - */ - public function testSaveAction() - { - $this->getRequest() - ->setParam('form_key', 'formKey') - ->setParam('is_subscribed', '1'); - $this->dispatch('newsletter/manage/save'); - - $this->assertRedirect($this->stringContains('customer/account/')); - - /** - * Check that errors - */ - $this->assertSessionMessages($this->isEmpty(), \Magento\Framework\Message\MessageInterface::TYPE_ERROR); - - /** - * Check that success message - */ - $this->assertSessionMessages( - $this->equalTo(['We have saved your subscription.']), - \Magento\Framework\Message\MessageInterface::TYPE_SUCCESS - ); - } - - /** - * @magentoDataFixture Magento/Customer/_files/customer.php - */ - public function testSaveActionRemoveSubscription() - { - - $this->getRequest() - ->setParam('form_key', 'formKey') - ->setParam('is_subscribed', '0'); - $this->dispatch('newsletter/manage/save'); - - $this->assertRedirect($this->stringContains('customer/account/')); - - /** - * Check that errors - */ - $this->assertSessionMessages($this->isEmpty(), \Magento\Framework\Message\MessageInterface::TYPE_ERROR); - - /** - * Check that success message - */ - $this->assertSessionMessages( - $this->equalTo(['We have updated your subscription.']), - \Magento\Framework\Message\MessageInterface::TYPE_SUCCESS - ); - } -} diff --git a/dev/tests/integration/testsuite/Magento/Newsletter/Controller/Subscriber/NewActionTest.php b/dev/tests/integration/testsuite/Magento/Newsletter/Controller/Subscriber/NewActionTest.php new file mode 100644 index 0000000000000..d64702f80fe61 --- /dev/null +++ b/dev/tests/integration/testsuite/Magento/Newsletter/Controller/Subscriber/NewActionTest.php @@ -0,0 +1,255 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +declare(strict_types=1); + +namespace Magento\Newsletter\Controller\Subscriber; + +use Magento\Customer\Api\CustomerRepositoryInterface; +use Magento\Customer\Model\Session; +use Magento\Customer\Model\Url; +use Magento\Framework\App\Request\Http as HttpRequest; +use Magento\Newsletter\Model\ResourceModel\Subscriber as SubscriberResource; +use Magento\Newsletter\Model\ResourceModel\Subscriber\CollectionFactory; +use Magento\TestFramework\TestCase\AbstractController; +use Laminas\Stdlib\Parameters; + +/** + * Class checks subscription behaviour from frontend + * + * @magentoDbIsolation enabled + * @see \Magento\Newsletter\Controller\Subscriber\NewAction + */ +class NewActionTest extends AbstractController +{ + /** @var Session */ + private $session; + + /** @var CollectionFactory */ + private $subscriberCollectionFactory; + + /** @var SubscriberResource */ + private $subscriberResource; + + /** @var string|null */ + private $subscriberToDelete; + + /** @var CustomerRepositoryInterface */ + private $customerRepository; + + /** @var Url */ + private $customerUrl; + + /** + * @inheritdoc + */ + protected function setUp() + { + parent::setUp(); + + $this->session = $this->_objectManager->get(Session::class); + $this->subscriberCollectionFactory = $this->_objectManager->get(CollectionFactory::class); + $this->subscriberResource = $this->_objectManager->get(SubscriberResource::class); + $this->customerRepository = $this->_objectManager->get(CustomerRepositoryInterface::class); + $this->customerUrl = $this->_objectManager->get(Url::class); + } + + /** + * @inheritdoc + */ + protected function tearDown() + { + if ($this->subscriberToDelete) { + $this->deleteSubscriber($this->subscriberToDelete); + } + + parent::tearDown(); + } + + /** + * @dataProvider subscribersDataProvider + * + * @param string $email + * @param string $expectedMessage + * @return void + */ + public function testNewAction(string $email, string $expectedMessage): void + { + $this->subscriberToDelete = $email ? $email : null; + $this->prepareRequest($email); + $this->dispatch('newsletter/subscriber/new'); + + $this->performAsserts($expectedMessage); + } + + /** + * @return array + */ + public function subscribersDataProvider(): array + { + return [ + 'without_email' => [ + 'email' => '', + 'message' => '', + ], + 'with_unused_email' => [ + 'email' => 'not_used@example.com', + 'message' => 'Thank you for your subscription.', + ], + 'with_invalid_email' => [ + 'email' => 'invalid_email.com', + 'message' => 'Please enter a valid email address.' + ], + ]; + } + + /** + * @magentoDataFixture Magento/Customer/_files/new_customer.php + * + * @return void + */ + public function testNewActionUsedEmail(): void + { + $this->prepareRequest('new_customer@example.com'); + $this->dispatch('newsletter/subscriber/new'); + + $this->performAsserts('Thank you for your subscription.'); + } + + /** + * @magentoDataFixture Magento/Customer/_files/new_customer.php + * + * @return void + */ + public function testNewActionOwnerEmail(): void + { + $this->prepareRequest('new_customer@example.com'); + $this->session->loginById(1); + $this->dispatch('newsletter/subscriber/new'); + + $this->performAsserts('Thank you for your subscription.'); + } + + /** + * @magentoDataFixture Magento/Newsletter/_files/customer_with_subscription.php + * + * @return void + */ + public function testAlreadyExistEmail(): void + { + $this->prepareRequest('new_customer@example.com'); + $this->dispatch('newsletter/subscriber/new'); + + $this->performAsserts('This email address is already subscribed.'); + } + + /** + * @magentoConfigFixture current_store newsletter/subscription/allow_guest_subscribe 0 + * + * @return void + */ + public function testWithNotAllowedGuestSubscription(): void + { + $message = sprintf( + 'Sorry, but the administrator denied subscription for guests. Please <a href="%s">register</a>.', + $this->customerUrl->getRegisterUrl() + ); + $this->subscriberToDelete = 'guest@example.com'; + $this->prepareRequest('guest@example.com'); + $this->dispatch('newsletter/subscriber/new'); + + $this->performAsserts($message); + } + + /** + * @magentoConfigFixture current_store newsletter/subscription/allow_guest_subscribe 0 + * + * @magentoDataFixture Magento/Customer/_files/new_customer.php + * + * @return void + */ + public function testCustomerSubscribeUnrelatedEmailWithNotAllowedGuestSubscription(): void + { + $this->markTestSkipped('Blocked by MC-31662'); + $this->subscriberToDelete = 'guest@example.com'; + $this->session->loginById($this->customerRepository->get('new_customer@example.com')->getId()); + $this->prepareRequest('guest@example.com'); + $this->dispatch('newsletter/subscriber/new'); + //ToDo message need to be specified after bug MC-31662 fixing + $this->performAsserts(''); + } + + /** + * @magentoConfigFixture current_store newsletter/subscription/confirm 1 + * + * @return void + */ + public function testWithRequiredConfirmation(): void + { + $this->subscriberToDelete = 'guest@example.com'; + $this->prepareRequest('guest@example.com'); + $this->dispatch('newsletter/subscriber/new'); + + $this->performAsserts('The confirmation request has been sent.'); + } + + /** + * @magentoDataFixture Magento/Newsletter/_files/three_subscribers.php + * + * @return void + */ + public function testWithEmailAssignedToAnotherCustomer(): void + { + $this->session->loginById(1); + $this->prepareRequest('customer2@search.example.com'); + $this->dispatch('newsletter/subscriber/new'); + + $this->performAsserts('This email address is already assigned to another user.'); + } + + /** + * Prepare request + * + * @param string $email + * @return void + */ + private function prepareRequest(string $email): void + { + $parameters = $this->_objectManager->create(Parameters::class); + $parameters->set('HTTP_REFERER', 'http://localhost/testRedirect'); + $this->getRequest()->setServer($parameters); + $this->getRequest()->setMethod(HttpRequest::METHOD_POST); + $this->getRequest()->setPostValue(['email' => $email]); + } + + /** + * Assert session message and expected redirect + * + * @param string $message + * @return void + */ + private function performAsserts(string $message): void + { + if ($message) { + $this->assertSessionMessages($this->equalTo([(string)__($message)])); + } + $this->assertRedirect($this->equalTo('http://localhost/testRedirect')); + } + + /** + * Delete subscribers by email + * + * @param string $email + * @return void + */ + private function deleteSubscriber(string $email): void + { + $collection = $this->subscriberCollectionFactory->create(); + $item = $collection->addFieldToFilter('subscriber_email', $email)->setPageSize(1)->getFirstItem(); + if ($item->getId()) { + $this->subscriberResource->delete($item); + } + } +} diff --git a/dev/tests/integration/testsuite/Magento/Newsletter/Controller/SubscriberTest.php b/dev/tests/integration/testsuite/Magento/Newsletter/Controller/SubscriberTest.php index bf19d6ddefc36..ec6ae77c385fd 100644 --- a/dev/tests/integration/testsuite/Magento/Newsletter/Controller/SubscriberTest.php +++ b/dev/tests/integration/testsuite/Magento/Newsletter/Controller/SubscriberTest.php @@ -23,59 +23,6 @@ */ class SubscriberTest extends AbstractController { - public function testNewAction() - { - $this->getRequest()->setMethod('POST'); - - $this->dispatch('newsletter/subscriber/new'); - - $this->assertSessionMessages($this->isEmpty()); - $this->assertRedirect($this->anything()); - } - - /** - * @magentoDbIsolation enabled - */ - public function testNewActionUnusedEmail() - { - $this->getRequest()->setMethod('POST'); - $this->getRequest()->setPostValue(['email' => 'not_used@example.com']); - - $this->dispatch('newsletter/subscriber/new'); - - $this->assertSessionMessages($this->equalTo(['Thank you for your subscription.'])); - $this->assertRedirect($this->anything()); - } - - /** - * @magentoDataFixture Magento/Customer/_files/customer.php - */ - public function testNewActionUsedEmail() - { - $this->getRequest()->setMethod('POST'); - $this->getRequest()->setPostValue(['email' => 'customer@example.com']); - - $this->dispatch('newsletter/subscriber/new'); - - $this->assertSessionMessages($this->equalTo(['Thank you for your subscription.'])); - $this->assertRedirect($this->anything()); - } - - /** - * @magentoDataFixture Magento/Customer/_files/customer.php - */ - public function testNewActionOwnerEmail() - { - $this->getRequest()->setMethod('POST'); - $this->getRequest()->setPostValue(['email' => 'customer@example.com']); - $this->login(1); - - $this->dispatch('newsletter/subscriber/new'); - - $this->assertSessionMessages($this->equalTo(['Thank you for your subscription.'])); - $this->assertRedirect($this->anything()); - } - /** * Check that Customer still subscribed for newsletters emails after registration. * @@ -149,18 +96,4 @@ private function fillRequestWithAccountDataAndFormKey($email) ->setPostValue('create_address', true) ->setParam('form_key', Bootstrap::getObjectManager()->get(FormKey::class)->getFormKey()); } - - /** - * Login the user - * - * @param string $customerId Customer to mark as logged in for the session - * @return void - */ - protected function login($customerId) - { - /** @var \Magento\Customer\Model\Session $session */ - $session = Bootstrap::getObjectManager() - ->get(\Magento\Customer\Model\Session::class); - $session->loginById($customerId); - } } diff --git a/dev/tests/integration/testsuite/Magento/Newsletter/Model/SubscriberTest.php b/dev/tests/integration/testsuite/Magento/Newsletter/Model/SubscriberTest.php index bdcbdc035d2b0..06c8902f45897 100644 --- a/dev/tests/integration/testsuite/Magento/Newsletter/Model/SubscriberTest.php +++ b/dev/tests/integration/testsuite/Magento/Newsletter/Model/SubscriberTest.php @@ -3,104 +3,175 @@ * Copyright © Magento, Inc. All rights reserved. * See COPYING.txt for license details. */ +declare(strict_types=1); + namespace Magento\Newsletter\Model; +use Magento\Customer\Api\CustomerRepositoryInterface; +use Magento\Framework\ObjectManagerInterface; +use Magento\TestFramework\Helper\Bootstrap; use Magento\TestFramework\Mail\Template\TransportBuilderMock; +use PHPUnit\Framework\TestCase; /** - * \Magento\Newsletter\Model\Subscriber tests + * Class checks subscription behavior. + * + * @see \Magento\Newsletter\Model\Subscriber */ -class SubscriberTest extends \PHPUnit\Framework\TestCase +class SubscriberTest extends TestCase { + /** @var ObjectManagerInterface */ + private $objectManager; + + /** @var SubscriberFactory */ + private $subscriberFactory; + + /** @var TransportBuilderMock */ + private $transportBuilder; + + /** @var CustomerRepositoryInterface */ + private $customerRepository; + /** - * @var Subscriber + * @inheritdoc */ - private $model; - protected function setUp() { - $this->model = \Magento\TestFramework\Helper\Bootstrap::getObjectManager()->create( - \Magento\Newsletter\Model\Subscriber::class - ); + $this->objectManager = Bootstrap::getObjectManager(); + $this->subscriberFactory = $this->objectManager->get(SubscriberFactory::class); + $this->transportBuilder = $this->objectManager->get(TransportBuilderMock::class); + $this->customerRepository = $this->objectManager->get(CustomerRepositoryInterface::class); } /** - * @magentoDataFixture Magento/Newsletter/_files/subscribers.php * @magentoConfigFixture current_store newsletter/subscription/confirm 1 + * + * @magentoDataFixture Magento/Newsletter/_files/subscribers.php + * + * @return void */ - public function testEmailConfirmation() + public function testEmailConfirmation(): void { - $this->model->subscribe('customer_confirm@example.com'); - /** @var TransportBuilderMock $transportBuilder */ - $transportBuilder = \Magento\TestFramework\Helper\Bootstrap::getObjectManager() - ->get(\Magento\TestFramework\Mail\Template\TransportBuilderMock::class); + $subscriber = $this->subscriberFactory->create(); + $subscriber->subscribe('customer_confirm@example.com'); // confirmationCode 'ysayquyajua23iq29gxwu2eax2qb6gvy' is taken from fixture $this->assertContains( - '/newsletter/subscriber/confirm/id/' . $this->model->getSubscriberId() + '/newsletter/subscriber/confirm/id/' . $subscriber->getSubscriberId() . '/code/ysayquyajua23iq29gxwu2eax2qb6gvy', - $transportBuilder->getSentMessage()->getBody()->getParts()[0]->getRawContent() + $this->transportBuilder->getSentMessage()->getBody()->getParts()[0]->getRawContent() ); - $this->assertEquals(Subscriber::STATUS_NOT_ACTIVE, $this->model->getSubscriberStatus()); + $this->assertEquals(Subscriber::STATUS_NOT_ACTIVE, $subscriber->getSubscriberStatus()); } /** * @magentoDataFixture Magento/Newsletter/_files/subscribers.php + * + * @return void */ - public function testLoadByCustomerId() + public function testLoadByCustomerId(): void { - $this->assertSame($this->model, $this->model->loadByCustomerId(1)); - $this->assertEquals('customer@example.com', $this->model->getSubscriberEmail()); + $subscriber = $this->subscriberFactory->create(); + $this->assertSame($subscriber, $subscriber->loadByCustomerId(1)); + $this->assertEquals('customer@example.com', $subscriber->getSubscriberEmail()); } /** * @magentoDataFixture Magento/Newsletter/_files/subscribers.php - * @magentoAppArea frontend + * + * @magentoAppArea frontend + * + * @return void */ - public function testUnsubscribeSubscribe() + public function testUnsubscribeSubscribe(): void { - // Unsubscribe and verify - $this->assertSame($this->model, $this->model->loadByCustomerId(1)); - $this->assertEquals($this->model, $this->model->unsubscribe()); - $this->assertEquals(Subscriber::STATUS_UNSUBSCRIBED, $this->model->getSubscriberStatus()); - + $subscriber = $this->subscriberFactory->create(); + $this->assertSame($subscriber, $subscriber->loadByCustomerId(1)); + $this->assertEquals($subscriber, $subscriber->unsubscribe()); + $this->assertContains( + 'You have been unsubscribed from the newsletter.', + $this->transportBuilder->getSentMessage()->getRawMessage() + ); + $this->assertEquals(Subscriber::STATUS_UNSUBSCRIBED, $subscriber->getSubscriberStatus()); // Subscribe and verify - $this->assertEquals(Subscriber::STATUS_SUBSCRIBED, $this->model->subscribe('customer@example.com')); - $this->assertEquals(Subscriber::STATUS_SUBSCRIBED, $this->model->getSubscriberStatus()); + $this->assertEquals(Subscriber::STATUS_SUBSCRIBED, $subscriber->subscribe('customer@example.com')); + $this->assertEquals(Subscriber::STATUS_SUBSCRIBED, $subscriber->getSubscriberStatus()); + $this->assertContains( + 'You have been successfully subscribed to our newsletter.', + $this->transportBuilder->getSentMessage()->getRawMessage() + ); } /** * @magentoDataFixture Magento/Newsletter/_files/subscribers.php - * @magentoAppArea frontend + * + * @magentoAppArea frontend + * + * @return void */ - public function testUnsubscribeSubscribeByCustomerId() + public function testUnsubscribeSubscribeByCustomerId(): void { + $subscriber = $this->subscriberFactory->create(); // Unsubscribe and verify - $this->assertSame($this->model, $this->model->unsubscribeCustomerById(1)); - $this->assertEquals(Subscriber::STATUS_UNSUBSCRIBED, $this->model->getSubscriberStatus()); - + $this->assertSame($subscriber, $subscriber->unsubscribeCustomerById(1)); + $this->assertEquals(Subscriber::STATUS_UNSUBSCRIBED, $subscriber->getSubscriberStatus()); + $this->assertContains( + 'You have been unsubscribed from the newsletter.', + $this->transportBuilder->getSentMessage()->getRawMessage() + ); // Subscribe and verify - $this->assertSame($this->model, $this->model->subscribeCustomerById(1)); - $this->assertEquals(Subscriber::STATUS_SUBSCRIBED, $this->model->getSubscriberStatus()); + $this->assertSame($subscriber, $subscriber->subscribeCustomerById(1)); + $this->assertEquals(Subscriber::STATUS_SUBSCRIBED, $subscriber->getSubscriberStatus()); + $this->assertContains( + 'You have been successfully subscribed to our newsletter.', + $this->transportBuilder->getSentMessage()->getRawMessage() + ); } /** - * @magentoDataFixture Magento/Newsletter/_files/subscribers.php * @magentoConfigFixture current_store newsletter/subscription/confirm 1 + * + * @magentoDataFixture Magento/Newsletter/_files/subscribers.php + * + * @return void */ - public function testConfirm() + public function testConfirm(): void { + $subscriber = $this->subscriberFactory->create(); $customerEmail = 'customer_confirm@example.com'; - $this->model->subscribe($customerEmail); - $this->model->loadByEmail($customerEmail); - $this->model->confirm($this->model->getSubscriberConfirmCode()); - - $transportBuilder = \Magento\TestFramework\Helper\Bootstrap::getObjectManager()->get( - \Magento\TestFramework\Mail\Template\TransportBuilderMock::class - ); - + $subscriber->subscribe($customerEmail); + $subscriber->loadByEmail($customerEmail); + $subscriber->confirm($subscriber->getSubscriberConfirmCode()); $this->assertContains( 'You have been successfully subscribed to our newsletter.', - $transportBuilder->getSentMessage()->getBody()->getParts()[0]->getRawContent() + $this->transportBuilder->getSentMessage()->getRawMessage() ); } + + /** + * @magentoDataFixture Magento/Customer/_files/customer_confirmation_config_enable.php + * @magentoDataFixture Magento/Newsletter/_files/newsletter_unconfirmed_customer.php + * + * @return void + */ + public function testSubscribeUnconfirmedCustomerWithSubscription(): void + { + $customer = $this->customerRepository->get('unconfirmedcustomer@example.com'); + $subscriber = $this->subscriberFactory->create(); + $subscriber->subscribeCustomerById($customer->getId()); + $this->assertEquals(Subscriber::STATUS_SUBSCRIBED, $subscriber->getStatus()); + } + + /** + * @magentoDataFixture Magento/Customer/_files/customer_confirmation_config_enable.php + * @magentoDataFixture Magento/Customer/_files/unconfirmed_customer.php + * + * @return void + */ + public function testSubscribeUnconfirmedCustomerWithoutSubscription(): void + { + $customer = $this->customerRepository->get('unconfirmedcustomer@example.com'); + $subscriber = $this->subscriberFactory->create(); + $subscriber->subscribeCustomerById($customer->getId()); + $this->assertEquals(Subscriber::STATUS_UNCONFIRMED, $subscriber->getStatus()); + } } diff --git a/dev/tests/integration/testsuite/Magento/Newsletter/_files/customer_with_subscription.php b/dev/tests/integration/testsuite/Magento/Newsletter/_files/customer_with_subscription.php new file mode 100644 index 0000000000000..f700889881e8d --- /dev/null +++ b/dev/tests/integration/testsuite/Magento/Newsletter/_files/customer_with_subscription.php @@ -0,0 +1,13 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +declare(strict_types=1); + +use Magento\Newsletter\Model\SubscriberFactory; + +require __DIR__ . '/../../../Magento/Customer/_files/new_customer.php'; + +$subscriberFactory = $objectManager->get(SubscriberFactory::class); +$subscriberFactory->create()->subscribe('new_customer@example.com'); diff --git a/dev/tests/integration/testsuite/Magento/Newsletter/_files/customer_with_subscription_rollback.php b/dev/tests/integration/testsuite/Magento/Newsletter/_files/customer_with_subscription_rollback.php new file mode 100644 index 0000000000000..145f6a9b43bbb --- /dev/null +++ b/dev/tests/integration/testsuite/Magento/Newsletter/_files/customer_with_subscription_rollback.php @@ -0,0 +1,8 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +declare(strict_types=1); + +require __DIR__ . '/../../../Magento/Customer/_files/new_customer_rollback.php'; diff --git a/dev/tests/integration/testsuite/Magento/Newsletter/_files/newsletter_unconfirmed_customer.php b/dev/tests/integration/testsuite/Magento/Newsletter/_files/newsletter_unconfirmed_customer.php new file mode 100644 index 0000000000000..405ac1c67bd7b --- /dev/null +++ b/dev/tests/integration/testsuite/Magento/Newsletter/_files/newsletter_unconfirmed_customer.php @@ -0,0 +1,14 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +declare(strict_types=1); + +use Magento\Newsletter\Model\SubscriberFactory; + +require __DIR__ . '/../../../Magento/Customer/_files/unconfirmed_customer.php'; + +/** @var SubscriberFactory $subscriberFactory */ +$subscriberFactory = $objectManager->get(SubscriberFactory::class); +$subscriberFactory->create()->subscribe('unconfirmedcustomer@example.com'); diff --git a/dev/tests/integration/testsuite/Magento/Newsletter/_files/newsletter_unconfirmed_customer_rollback.php b/dev/tests/integration/testsuite/Magento/Newsletter/_files/newsletter_unconfirmed_customer_rollback.php new file mode 100644 index 0000000000000..5742526988187 --- /dev/null +++ b/dev/tests/integration/testsuite/Magento/Newsletter/_files/newsletter_unconfirmed_customer_rollback.php @@ -0,0 +1,8 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +declare(strict_types=1); + +require __DIR__ . '/../../../Magento/Customer/_files/unconfirmed_customer_rollback.php'; diff --git a/dev/tests/integration/testsuite/Magento/Newsletter/_files/three_subscribers.php b/dev/tests/integration/testsuite/Magento/Newsletter/_files/three_subscribers.php new file mode 100644 index 0000000000000..5eb0ec5843bd8 --- /dev/null +++ b/dev/tests/integration/testsuite/Magento/Newsletter/_files/three_subscribers.php @@ -0,0 +1,18 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +declare(strict_types=1); + +use Magento\Newsletter\Model\SubscriberFactory; +use Magento\TestFramework\Helper\Bootstrap; + +require __DIR__ . '/../../../Magento/Customer/_files/three_customers.php'; + +$objectManager = Bootstrap::getObjectManager(); +$subscriberFactory = $objectManager->get(SubscriberFactory::class); + +$subscriberFactory->create()->subscribe('customer@search.example.com'); +$subscriberFactory->create()->subscribe('customer2@search.example.com'); +$subscriberFactory->create()->subscribe('customer3@search.example.com'); diff --git a/dev/tests/integration/testsuite/Magento/Newsletter/_files/three_subscribers_rollback.php b/dev/tests/integration/testsuite/Magento/Newsletter/_files/three_subscribers_rollback.php new file mode 100644 index 0000000000000..e1760db7b1e52 --- /dev/null +++ b/dev/tests/integration/testsuite/Magento/Newsletter/_files/three_subscribers_rollback.php @@ -0,0 +1,8 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +declare(strict_types=1); + +require __DIR__ . '/../../../Magento/Customer/_files/three_customers_rollback.php'; diff --git a/dev/tests/integration/testsuite/Magento/OfflineShipping/_files/tablerates_price.php b/dev/tests/integration/testsuite/Magento/OfflineShipping/_files/tablerates_price.php new file mode 100644 index 0000000000000..337f4e4f9b1ef --- /dev/null +++ b/dev/tests/integration/testsuite/Magento/OfflineShipping/_files/tablerates_price.php @@ -0,0 +1,57 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ + +use Magento\OfflineShipping\Model\ResourceModel\Carrier\Tablerate; +use Magento\TestFramework\Helper\Bootstrap; + +$objectManager = Bootstrap::getObjectManager(); +/** + * @var Tablerate $resourceModel + */ +$resourceModel = $objectManager->create(Tablerate::class); +$tableRatesData = [ + [ + 'website_id' => 1, + 'dest_country_id' => 'US', + 'dest_region_id' => 0, + 'dest_zip' => '*', + 'condition_name' => 'package_value_with_discount', + 'condition_value' => 0.00, + 'price' => 15, + 'cost' => 0 + ], + [ + 'website_id' => 1, + 'dest_country_id' => 'US', + 'dest_region_id' => 0, + 'dest_zip' => '*', + 'condition_name' => 'package_value_with_discount', + 'condition_value' => 50.00, + 'price' => 10, + 'cost' => 0 + ], + [ + 'website_id' => 1, + 'dest_country_id' => 'US', + 'dest_region_id' => 0, + 'dest_zip' => '*', + 'condition_name' => 'package_value_with_discount', + 'condition_value' => 100.00, + 'price' => 5, + 'cost' => 0 + ] + ]; +$columns = [ + 'website_id', + 'dest_country_id', + 'dest_region_id', + 'dest_zip', + 'condition_name', + 'condition_value', + 'price', + 'cost' +]; +$resourceModel->getConnection()->insertArray($resourceModel->getMainTable(), $columns, $tableRatesData); diff --git a/dev/tests/integration/testsuite/Magento/OfflineShipping/_files/tablerates_price_rollback.php b/dev/tests/integration/testsuite/Magento/OfflineShipping/_files/tablerates_price_rollback.php new file mode 100644 index 0000000000000..ca1971033dea0 --- /dev/null +++ b/dev/tests/integration/testsuite/Magento/OfflineShipping/_files/tablerates_price_rollback.php @@ -0,0 +1,12 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ + +use Magento\OfflineShipping\Model\ResourceModel\Carrier\Tablerate; +use Magento\TestFramework\Helper\Bootstrap; + +$objectManager = Bootstrap::getObjectManager(); +$resourceModel = $objectManager->create(Tablerate::class); +$resourceModel->getConnection()->delete($resourceModel->getMainTable()); diff --git a/dev/tests/integration/testsuite/Magento/PageCache/Model/Layout/DepersonalizePluginTest.php b/dev/tests/integration/testsuite/Magento/PageCache/Model/Layout/DepersonalizePluginTest.php new file mode 100644 index 0000000000000..96cd5c009d6c3 --- /dev/null +++ b/dev/tests/integration/testsuite/Magento/PageCache/Model/Layout/DepersonalizePluginTest.php @@ -0,0 +1,100 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +declare(strict_types=1); + +namespace Magento\PageCache\Model\Layout; + +use Magento\Framework\App\Cache\Type\Layout as LayoutCache; +use Magento\Framework\Message\Session; +use Magento\Framework\View\Layout; +use Magento\Framework\View\LayoutFactory; +use Magento\TestFramework\Helper\Bootstrap; +use PHPUnit\Framework\TestCase; + +/** + * Integration tests for \Magento\PageCache\Model\Layout\DepersonalizePlugin class. + * + * @magentoAppArea frontend + */ +class DepersonalizePluginTest extends TestCase +{ + /** + * @var Session + */ + private $messageSession; + + /** + * @var Layout + */ + private $layout; + + /** + * @var LayoutCache + */ + private $cache; + + /** + * @inheritdoc + */ + protected function setUp() + { + $this->messageSession = Bootstrap::getObjectManager()->get(Session::class); + $this->layout = Bootstrap::getObjectManager()->get(LayoutFactory::class)->create(); + $this->cache = Bootstrap::getObjectManager()->get(LayoutCache::class); + } + + /** + * @inheritDoc + */ + protected function tearDown() + { + $this->messageSession->clearStorage(); + } + + /** + * @magentoCache full_page enabled + * @dataProvider afterGenerateElementsDataProvider + * + * @param string $layout + * @param array $expectedResult + * @return void + */ + public function testAfterGenerateElements(string $layout, array $expectedResult): void + { + $this->cache->clean(); + $this->assertTrue($this->layout->loadFile($layout)); + $this->messageSession->setData(['some_data' => 1]); + $this->layout->generateElements(); + $this->assertEquals($expectedResult, $this->messageSession->getData()); + } + + /** + * @return array + */ + public function afterGenerateElementsDataProvider(): array + { + return [ + 'cacheable' => [ + 'layout' => INTEGRATION_TESTS_DIR . '/testsuite/Magento/Framework/View/_files/layout/cacheable.xml', + 'expectedResult' => [], + ], + 'nonCacheable' => [ + 'layout' => INTEGRATION_TESTS_DIR . '/testsuite/Magento/Framework/View/_files/layout/non_cacheable.xml', + 'expectedResult' => ['some_data' => 1], + ], + 'nonCacheableBlockWithoutReference' => [ + 'layout' => INTEGRATION_TESTS_DIR + . '/testsuite/Magento/Framework/View/_files/layout/non_cacheable_block_with_missing_refference.xml', + 'expectedResult' => [], + ], + 'nonCacheableBlockWithExistedReference' => [ + 'layout' => INTEGRATION_TESTS_DIR + . '/testsuite/Magento/Framework/View/_files/layout/non_cacheable_block_with_declared_reference.xml', + 'expectedResult' => ['some_data' => 1], + ], + ]; + } +} diff --git a/dev/tests/integration/testsuite/Magento/Payment/Model/PaymentInfoTest.php b/dev/tests/integration/testsuite/Magento/Payment/Model/PaymentInfoTest.php new file mode 100644 index 0000000000000..3d037ceb17044 --- /dev/null +++ b/dev/tests/integration/testsuite/Magento/Payment/Model/PaymentInfoTest.php @@ -0,0 +1,63 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +namespace Magento\Payment\Model; + +use Magento\Sales\Model\Order; +use Magento\Quote\Model\Quote; +use Magento\TestFramework\Helper\Bootstrap; +use Magento\TestFramework\ObjectManager; + +/** + * @magentoAppArea adminhtml + * @SuppressWarnings(PHPMD.CouplingBetweenObjects) + */ +class PaymentInfoTest extends \PHPUnit\Framework\TestCase +{ + /** + * @var ObjectManager + */ + protected $_objectManager; + + /** + * @var Order + */ + protected $_order; + + /** @var Quote */ + protected $_quote; + + protected function setUp() + { + $this->_objectManager = Bootstrap::getObjectManager(); + $this->_order = $this->_objectManager->create( + Order::class + ); + $this->_quote = $this->_objectManager->create( + Quote::class + ); + } + + /** + * @magentoDbIsolation enabled + * @magentoAppIsolation enabled + * @magentoDataFixture Magento/Payment/_files/payment_info.php + */ + public function testUnsetPaymentInformation() + { + $order = $this->_order->loadByIncrementId('100000001'); + /** @var \Magento\Sales\Model\Order\Payment $paymentOrder */ + $paymentOrder = $order->getPayment(); + $paymentOrder->unsAdditionalInformation('testing'); + + $quote = $this->_quote->load('reserved_order_id', 'reserved_order_id'); + /** @var \Magento\Quote\Model\Quote\Payment $paymentQuote */ + $paymentQuote = $quote->getPayment(); + $paymentQuote->unsAdditionalInformation('testing'); + + $this->assertFalse($paymentOrder->hasAdditionalInformation('testing')); + $this->assertFalse($paymentQuote->hasAdditionalInformation('testing')); + } +} diff --git a/dev/tests/integration/testsuite/Magento/Payment/_files/payment_info.php b/dev/tests/integration/testsuite/Magento/Payment/_files/payment_info.php new file mode 100644 index 0000000000000..bf40cb6b99820 --- /dev/null +++ b/dev/tests/integration/testsuite/Magento/Payment/_files/payment_info.php @@ -0,0 +1,83 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ + +use Magento\Quote\Api\CartRepositoryInterface; +use Magento\TestFramework\Helper\Bootstrap; +use Magento\Sales\Model\Order\Address; +use Magento\Sales\Model\Order\Payment; +use Magento\Paypal\Model\Config; +use Magento\Sales\Model\Order; +use Magento\Quote\Model\Quote; +use Magento\Quote\Model\Quote\Payment as PaymentQuote; + +/** @var $objectManager \Magento\TestFramework\ObjectManager */ +$objectManager = Bootstrap::getObjectManager(); + +$addressData = [ + 'firstname' => 'guest', + 'lastname' => 'guest', + 'email' => 'customer@example.com', + 'street' => 'street', + 'city' => 'Los Angeles', + 'region' => 'CA', + 'postcode' => '1', + 'country_id' => 'US', + 'telephone' => '1' +]; +$billingAddress = $objectManager->create( + Address::class, + ['data' => $addressData] +); +$billingAddress->setAddressType('billing'); +$shippingAddress = clone $billingAddress; +$shippingAddress->setId(null)->setAddressType('shipping'); + +/** @var Payment $paymentOrder */ +$paymentOrder = $objectManager->create( + Payment::class +); + +$paymentOrder->setMethod(Config::METHOD_WPP_EXPRESS); +$paymentOrder->setAdditionalInformation('testing', 'testing additional data'); + +$amount = 100; + +/** @var Order $order */ +$order = $objectManager->create(Order::class); +$order->setCustomerEmail('co@co.co') + ->setIncrementId('100000001') + ->setSubtotal($amount) + ->setBaseSubtotal($amount) + ->setBaseGrandTotal($amount) + ->setGrandTotal($amount) + ->setBaseCurrencyCode('USD') + ->setCustomerIsGuest(true) + ->setStoreId(1) + ->setEmailSent(true) + ->setBillingAddress($billingAddress) + ->setShippingAddress($shippingAddress) + ->setPayment($paymentOrder); +$order->save(); + + + +/** @var Quote $quote */ +$quote = $objectManager->create(Quote::class); +$quote->setStoreId(1) + ->setIsActive(true) + ->setIsMultiShipping(false) + ->setReservedOrderId('reserved_order_id'); + +$quote->getPayment() + ->setMethod(Config::METHOD_WPP_EXPRESS) + ->setAdditionalInformation('testing', 'testing additional data'); + +$quote->collectTotals(); + + +/** @var CartRepositoryInterface $repository */ +$repository = $objectManager->get(CartRepositoryInterface::class); +$repository->save($quote); diff --git a/dev/tests/integration/testsuite/Magento/Paypal/Controller/Billing/AgreementTest.php b/dev/tests/integration/testsuite/Magento/Paypal/Controller/Billing/AgreementTest.php index fd8b4fd2efad5..6f192b6479acd 100644 --- a/dev/tests/integration/testsuite/Magento/Paypal/Controller/Billing/AgreementTest.php +++ b/dev/tests/integration/testsuite/Magento/Paypal/Controller/Billing/AgreementTest.php @@ -19,6 +19,7 @@ class AgreementTest extends \Magento\TestFramework\TestCase\AbstractController * * @magentoDataFixture Magento/Customer/_files/customer.php * @magentoDbIsolation enabled + * @magentoAppArea frontend */ public function testReturnWizardAction() { diff --git a/dev/tests/integration/testsuite/Magento/Paypal/Controller/Transparent/RedirectTest.php b/dev/tests/integration/testsuite/Magento/Paypal/Controller/Transparent/RedirectTest.php new file mode 100644 index 0000000000000..4a30231012acf --- /dev/null +++ b/dev/tests/integration/testsuite/Magento/Paypal/Controller/Transparent/RedirectTest.php @@ -0,0 +1,112 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +declare(strict_types=1); + +namespace Magento\Paypal\Controller\Transparent; + +use Magento\TestFramework\TestCase\AbstractController; +use Zend\Stdlib\Parameters; + +/** + * Tests PayPal transparent redirect controller. + */ +class RedirectTest extends AbstractController +{ + /** + * Tests transparent redirect for PayPal PayflowPro payment flow. + * + * @SuppressWarnings(PHPMD.Superglobals) + */ + public function testRequestRedirect() + { + $redirectUri = 'paypal/transparent/redirect'; + $postData = [ + 'BILLTOCITY' => 'culver city', + 'AMT' => '0.00', + 'BILLTOEMAIL' => 'user_1@example.com', + 'BILLTOSTREET' => '123 Freedom Blvd. #123 app.111', + 'VISACARDLEVEL' => '12', + 'SHIPTOCITY' => 'culver city' + ]; + + $this->setRequestUri($redirectUri); + $this->getRequest()->setPostValue($postData); + $this->getRequest()->setMethod('POST'); + + $this->dispatch($redirectUri); + + $responseHtml = $this->getResponse()->getBody(); + try { + $responseNvp = $this->convertToNvp($responseHtml); + $this->assertEquals( + $postData, + $responseNvp, + 'POST form should contain all params from POST request' + ); + } catch (\InvalidArgumentException $exception) { + $this->fail($exception->getMessage()); + } + + $this->assertEmpty( + $_SESSION, + 'Session start has to be skipped for current controller' + ); + } + + /** + * Sets REQUEST_URI into request object. + * + * @param string $requestUri + * @return void + */ + private function setRequestUri(string $requestUri) + { + $request = $this->getRequest(); + $reflection = new \ReflectionClass($request); + $property = $reflection->getProperty('requestUri'); + $property->setAccessible(true); + $property->setValue($request, null); + + $request->setServer(new Parameters(['REQUEST_URI' => $requestUri])); + } + + /** + * Converts HTML response to NVP structure + * + * @param string $response + * @return array + */ + private function convertToNvp(string $response): array + { + $document = new \DOMDocument(); + + libxml_use_internal_errors(true); + if (!$document->loadHTML($response)) { + throw new \InvalidArgumentException( + __('The response format was incorrect. Should be valid HTML') + ); + } + libxml_use_internal_errors(false); + + $document->getElementsByTagName('input'); + + $convertedResponse = []; + /** @var \DOMNode $inputNode */ + foreach ($document->getElementsByTagName('input') as $inputNode) { + if (!$inputNode->attributes->getNamedItem('value') + || !$inputNode->attributes->getNamedItem('name') + ) { + continue; + } + $convertedResponse[$inputNode->attributes->getNamedItem('name')->nodeValue] + = $inputNode->attributes->getNamedItem('value')->nodeValue; + } + + unset($convertedResponse['form_key']); + + return $convertedResponse; + } +} diff --git a/dev/tests/integration/testsuite/Magento/Paypal/Model/Express/CheckoutTest.php b/dev/tests/integration/testsuite/Magento/Paypal/Model/Express/CheckoutTest.php index 8d6e4dbf30ae5..23dc60d347427 100644 --- a/dev/tests/integration/testsuite/Magento/Paypal/Model/Express/CheckoutTest.php +++ b/dev/tests/integration/testsuite/Magento/Paypal/Model/Express/CheckoutTest.php @@ -6,6 +6,7 @@ namespace Magento\Paypal\Model\Express; use Magento\Checkout\Model\Type\Onepage; +use Magento\Directory\Model\CountryFactory; use Magento\Framework\ObjectManagerInterface; use Magento\Paypal\Model\Api\Nvp; use Magento\Paypal\Model\Api\Type\Factory; @@ -17,8 +18,6 @@ use Magento\TestFramework\Helper\Bootstrap; /** - * Class CheckoutTest - * * @SuppressWarnings(PHPMD.CouplingBetweenObjects) */ class CheckoutTest extends \PHPUnit\Framework\TestCase @@ -634,10 +633,6 @@ public function testGuestReturnFromPaypal() ->setMethods(['call', 'getExportedShippingAddress', 'getExportedBillingAddress']) ->getMock(); - $api->expects($this->any()) - ->method('call') - ->will($this->returnValue([])); - $apiTypeFactory->expects($this->any()) ->method('create') ->will($this->returnValue($api)); @@ -652,6 +647,14 @@ public function testGuestReturnFromPaypal() ->method('getExportedShippingAddress') ->will($this->returnValue($exportedShippingAddress)); + $this->addCountryFactory($api); + $data = [ + 'COUNTRYCODE' => $quote->getShippingAddress()->getCountryId(), + 'STATE' => 'unknown' + ]; + $api->method('call') + ->willReturn($data); + $paypalInfo->expects($this->once()) ->method('importToPayment') ->with($api, $quote->getPayment()); @@ -710,4 +713,19 @@ private function getFixtureQuote(): Quote return $quoteCollection->getLastItem(); } + + /** + * Adds countryFactory to a mock. + * + * @param \PHPUnit\Framework\MockObject\MockObject $api + * @throws \ReflectionException + * @return void + */ + private function addCountryFactory(\PHPUnit\Framework\MockObject\MockObject $api): void + { + $reflection = new \ReflectionClass($api); + $property = $reflection->getProperty('_countryFactory'); + $property->setAccessible(true); + $property->setValue($api, $this->objectManager->get(CountryFactory::class)); + } } diff --git a/dev/tests/integration/testsuite/Magento/Paypal/Model/IpnTest.php b/dev/tests/integration/testsuite/Magento/Paypal/Model/IpnTest.php index 1a22ea947f85a..1877e1faaec67 100644 --- a/dev/tests/integration/testsuite/Magento/Paypal/Model/IpnTest.php +++ b/dev/tests/integration/testsuite/Magento/Paypal/Model/IpnTest.php @@ -5,10 +5,13 @@ */ namespace Magento\Paypal\Model; -use Magento\Paypal\Model\IpnFactory; +use Magento\Framework\Api\SearchCriteriaBuilder; use Magento\Sales\Api\Data\OrderInterface; +use Magento\Sales\Api\OrderRepositoryInterface; use Magento\Sales\Model\Order; use Magento\Sales\Model\Order\Creditmemo; +use Magento\Sales\Model\Order\Invoice; +use Magento\TestFramework\Helper\Bootstrap; /** * @magentoAppArea frontend @@ -22,7 +25,7 @@ class IpnTest extends \PHPUnit\Framework\TestCase protected function setUp() { - $this->_objectManager = \Magento\TestFramework\Helper\Bootstrap::getObjectManager(); + $this->_objectManager = Bootstrap::getObjectManager(); } /** @@ -158,6 +161,39 @@ public function testProcessIpnRequestRestRefund() $this->assertEmpty($order->getTotalOfflineRefunded()); } + /** + * Verifies canceling an order that was in payment review state by PayPal Express IPN message service. + * + * @magentoDataFixture Magento/Paypal/_files/order_express_with_invoice_payment_review.php + * @magentoConfigFixture current_store payment/paypal_express/active 1 + * @magentoConfigFixture current_store paypal/general/merchant_country US + */ + public function testProcessIpnRequestWithFailedStatus() + { + $ipnData = require __DIR__ . '/../_files/ipn_failed.php'; + + /** @var IpnFactory $ipnFactory */ + $ipnFactory = $this->_objectManager->create(IpnFactory::class); + $ipnModel = $ipnFactory->create( + [ + 'data' => $ipnData, + 'curlFactory' => $this->_createMockedHttpAdapter() + ] + ); + + $ipnModel->processIpnRequest(); + + $order = $this->getOrder($ipnData['invoice']); + $invoiceItems = $order->getInvoiceCollection() + ->getItems(); + /** @var Invoice $invoice */ + $invoice = array_pop($invoiceItems); + $invoice->getState(); + + $this->assertEquals(Order::STATE_CANCELED, $order->getState()); + $this->assertEquals(Invoice::STATE_CANCELED, $invoice->getState()); + } + /** * Test processIpnRequest() currency check for paypal_express and paypal_standard payment methods * @@ -224,4 +260,25 @@ protected function _createMockedHttpAdapter() $factory->expects($this->once())->method('create')->with()->will($this->returnValue($adapter)); return $factory; } + + /** + * Get stored order. + * + * @param string $incrementId + * @return OrderInterface + */ + private function getOrder(string $incrementId) + { + /** @var SearchCriteriaBuilder $searchCriteriaBuilder */ + $searchCriteriaBuilder = $this->_objectManager->get(SearchCriteriaBuilder::class); + $searchCriteria = $searchCriteriaBuilder->addFilter(OrderInterface::INCREMENT_ID, $incrementId) + ->create(); + + $orderRepository = $this->_objectManager->get(OrderRepositoryInterface::class); + $orders = $orderRepository->getList($searchCriteria) + ->getItems(); + + /** @var OrderInterface $order */ + return array_pop($orders); + } } diff --git a/dev/tests/integration/testsuite/Magento/Paypal/Model/Payflow/TransparentTest.php b/dev/tests/integration/testsuite/Magento/Paypal/Model/Payflow/TransparentTest.php new file mode 100644 index 0000000000000..20720f491c18d --- /dev/null +++ b/dev/tests/integration/testsuite/Magento/Paypal/Model/Payflow/TransparentTest.php @@ -0,0 +1,167 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +declare(strict_types=1); + +namespace Magento\Paypal\Model\Payflow; + +use Magento\Checkout\Api\PaymentInformationManagementInterface; +use Magento\Framework\Api\SearchCriteriaBuilder; +use Magento\Paypal\Model\Config; +use Magento\Paypal\Model\Payflowpro; +use Magento\Quote\Api\CartRepositoryInterface; +use Magento\Quote\Api\Data\CartInterface; +use Magento\Sales\Api\Data\TransactionInterface; +use Magento\Sales\Api\OrderManagementInterface; +use Magento\Sales\Api\OrderRepositoryInterface; +use Magento\Sales\Api\TransactionRepositoryInterface; +use Magento\Sales\Model\Order; +use Magento\TestFramework\Helper\Bootstrap; +use Magento\TestFramework\ObjectManager; +use PHPUnit\Framework\TestCase; + +/** + * @SuppressWarnings(PHPMD.CouplingBetweenObjects) + */ +class TransparentTest extends TestCase +{ + /** + * @var ObjectManager + */ + private $objectManager; + + /** + * @var PaymentInformationManagementInterface + */ + private $management; + + /** + * @inheritdoc + */ + protected function setUp() + { + $this->objectManager = Bootstrap::getObjectManager(); + $this->management = $this->objectManager->get(PaymentInformationManagementInterface::class); + } + + /** + * Checks a case when order should be placed in "Suspected Fraud" status based on after account verification. + * + * @magentoDataFixture Magento/Checkout/_files/quote_with_shipping_method.php + * @magentoConfigFixture current_store payment/payflowpro/active 1 + * @magentoConfigFixture current_store payment/payflowpro/payment_action Authorization + * @magentoConfigFixture current_store payment/payflowpro/fmf 1 + */ + public function testPlaceOrderSuspectedFraud() + { + $quote = $this->getQuote('test_order_1'); + $this->addFraudPayment($quote); + $payment = $quote->getPayment(); + $pnref = $payment->getAdditionalInformation(Payflowpro::PNREF); + + $orderId = (int)$this->management->savePaymentInformationAndPlaceOrder($quote->getId(), $payment); + self::assertNotEmpty($orderId); + + /** @var OrderRepositoryInterface $orderManagement */ + $orderManagement = $this->objectManager->get(OrderRepositoryInterface::class); + $order = $orderManagement->get($orderId); + + self::assertEquals(Order::STATUS_FRAUD, $order->getStatus()); + self::assertEquals(Order::STATE_PAYMENT_REVIEW, $order->getState()); + + $transactions = $this->getPaymentTransactionList((int) $orderId); + self::assertEquals(1, sizeof($transactions), 'Only one transaction should be present.'); + + /** @var TransactionInterface $transaction */ + $transaction = array_pop($transactions); + self::assertEquals( + $pnref, + $transaction->getTxnId(), + 'Authorization transaction id should be equal to PNREF.' + ); + + self::assertContains( + 'Order is suspended as an account verification transaction is suspected to be fraudulent.', + $this->getOrderComment($orderId) + ); + } + + /** + * Retrieves quote by provided order ID. + * + * @param string $reservedOrderId + * @return CartInterface + */ + private function getQuote(string $reservedOrderId): CartInterface + { + /** @var SearchCriteriaBuilder $searchCriteriaBuilder */ + $searchCriteriaBuilder = $this->objectManager->get(SearchCriteriaBuilder::class); + $searchCriteria = $searchCriteriaBuilder->addFilter('reserved_order_id', $reservedOrderId) + ->create(); + + /** @var CartRepositoryInterface $quoteRepository */ + $quoteRepository = $this->objectManager->get(CartRepositoryInterface::class); + $items = $quoteRepository->getList($searchCriteria) + ->getItems(); + + return array_pop($items); + } + + /** + * Sets payment with fraud to quote. + * + * @return void + */ + private function addFraudPayment(CartInterface $quote) + { + $payment = $quote->getPayment(); + $payment->setMethod(Config::METHOD_PAYFLOWPRO); + $payment->setAdditionalInformation(Payflowpro::PNREF, 'A90A0D1B361D'); + $payment->setAdditionalInformation('result_code', Payflowpro::RESPONSE_CODE_FRAUDSERVICE_FILTER); + $payment->setCcType('VI'); + $payment->setCcLast4('1111'); + $payment->setCcExpMonth('3'); + $payment->setCcExpYear('2025'); + + /** @var CartRepositoryInterface $quoteRepository */ + $quoteRepository = $this->objectManager->get(CartRepositoryInterface::class); + $quoteRepository->save($quote); + } + + /** + * Get list of order transactions. + * + * @param int $orderId + * @return TransactionInterface[] + */ + private function getPaymentTransactionList(int $orderId): array + { + /** @var SearchCriteriaBuilder $searchCriteriaBuilder */ + $searchCriteriaBuilder = $this->objectManager->get(SearchCriteriaBuilder::class); + $searchCriteria = $searchCriteriaBuilder->addFilter('order_id', $orderId) + ->create(); + + /** @var TransactionRepositoryInterface $transactionRepository */ + $transactionRepository = $this->objectManager->get(TransactionRepositoryInterface::class); + return $transactionRepository->getList($searchCriteria) + ->getItems(); + } + + /** + * Returns order comment. + * + * @param int $orderId + * @return string + */ + private function getOrderComment(int $orderId): string + { + /** @var OrderManagementInterface $orderManagement */ + $orderManagement = $this->objectManager->get(OrderManagementInterface::class); + $comments = $orderManagement->getCommentsList($orderId)->getItems(); + $comment = reset($comments); + + return $comment ? $comment->getComment() : ''; + } +} diff --git a/dev/tests/integration/testsuite/Magento/Paypal/_files/ipn_failed.php b/dev/tests/integration/testsuite/Magento/Paypal/_files/ipn_failed.php new file mode 100644 index 0000000000000..cf1822c9a1a52 --- /dev/null +++ b/dev/tests/integration/testsuite/Magento/Paypal/_files/ipn_failed.php @@ -0,0 +1,17 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +declare(strict_types=1); + +return [ + 'invoice' => '100000002', + 'payment_status' => 'Failed', + 'receiver_email' => 'merchant_2012050718_biz@example.com', + 'parent_txn_id' => '84J11393WC835693U', + 'payer_status' => 'verified', + 'payment_type' => 'instant', + 'txn_id' => '1P566839F9694230H', + 'txn_type' => 'cart' +]; diff --git a/dev/tests/integration/testsuite/Magento/Paypal/_files/order_express_with_invoice_payment_review.php b/dev/tests/integration/testsuite/Magento/Paypal/_files/order_express_with_invoice_payment_review.php new file mode 100644 index 0000000000000..eb6654b274ba7 --- /dev/null +++ b/dev/tests/integration/testsuite/Magento/Paypal/_files/order_express_with_invoice_payment_review.php @@ -0,0 +1,88 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +declare(strict_types=1); + +use Magento\Framework\DB\Transaction; +use Magento\Paypal\Model\Config; +use Magento\Sales\Api\InvoiceManagementInterface; +use Magento\Sales\Api\OrderRepositoryInterface; +use Magento\Sales\Model\Order; +use Magento\Sales\Model\Order\Item; +use Magento\Sales\Model\Order\Address; +use Magento\Sales\Model\Order\Payment; +use Magento\Sales\Model\Service\InvoiceService; +use Magento\TestFramework\Helper\Bootstrap; + +require __DIR__ . '/../../../Magento/Catalog/_files/product_simple.php'; + +$objectManager = Bootstrap::getObjectManager(); + +$addressData = include __DIR__ . '/address_data.php'; +$billingAddress = $objectManager->create( + Address::class, + ['data' => $addressData] +); +$billingAddress->setAddressType('billing'); +$shippingAddress = clone $billingAddress; +$shippingAddress->setId(null)->setAddressType('shipping'); + +$payment = $objectManager->create(Payment::class); +$payment->setMethod(Config::METHOD_WPP_EXPRESS); + +/** @var Item $orderItem */ +$orderItem = $objectManager->create(Item::class); +$orderItem->setProductId($product->getId())->setQtyOrdered(1); +$orderItem->setBasePrice($product->getPrice()); +$orderItem->setPrice($product->getPrice()); +$orderItem->setRowTotal($product->getPrice()); +$orderItem->setRowTotalInclTax($product->getPrice()); +$orderItem->setBaseRowTotal($product->getPrice()); +$orderItem->setBaseRowTotalInclTax($product->getPrice()); +$orderItem->setBaseRowInvoiced($product->getPrice()); +$orderItem->setProductType('simple'); + +$itemsAmount = $product->getPrice(); +$shippingAmount = 20; +$totalAmount = $itemsAmount + $shippingAmount; + +/** @var Order $order */ +$order = $objectManager->create(Order::class); +$order->setCustomerEmail('co@co.co') + ->setIncrementId('100000002') + ->addItem($orderItem) + ->setSubtotal($itemsAmount) + ->setBaseSubtotal($itemsAmount) + ->setBaseGrandTotal($totalAmount) + ->setGrandTotal($totalAmount) + ->setBaseCurrencyCode('USD') + ->setCustomerIsGuest(true) + ->setStoreId(1) + ->setEmailSent(true) + ->setState(Order::STATE_PAYMENT_REVIEW) + ->setBillingAddress($billingAddress) + ->setShippingAddress($shippingAddress) + ->setBaseTotalPaid($totalAmount) + ->setTotalPaid($totalAmount) + ->setData('base_to_global_rate', 1) + ->setData('base_to_order_rate', 1) + ->setData('shipping_amount', $shippingAmount) + ->setData('base_shipping_amount', $shippingAmount) + ->setPayment($payment); + +/** @var OrderRepositoryInterface $orderRepository */ +$orderRepository = $objectManager->get(OrderRepositoryInterface::class); +$orderRepository->save($order); + +/** @var InvoiceService $invoiceService */ +$invoiceService = $objectManager->create(InvoiceManagementInterface::class); + +/** @var Transaction $transaction */ +$transaction = $objectManager->create(Transaction::class); + +$invoice = $invoiceService->prepareInvoice($order, [$orderItem->getId() => 1]); +$invoice->register(); + +$transaction->addObject($invoice)->addObject($order)->save(); diff --git a/dev/tests/integration/testsuite/Magento/Phpserver/PhpserverTest.php b/dev/tests/integration/testsuite/Magento/Phpserver/PhpserverTest.php index b11991fc03255..2c0c3e5233d51 100644 --- a/dev/tests/integration/testsuite/Magento/Phpserver/PhpserverTest.php +++ b/dev/tests/integration/testsuite/Magento/Phpserver/PhpserverTest.php @@ -22,7 +22,7 @@ class PhpserverTest extends \PHPUnit\Framework\TestCase private static $serverPid; /** - * @var \Zend\Http\Client + * @var \Laminas\Http\Client */ private $httpClient; @@ -42,6 +42,7 @@ public static function setUpBeforeClass() $baseDir, static::BASE_URL ); + // phpcs:ignore exec($command, $return); static::$serverPid = (int) $return[0]; } @@ -53,7 +54,7 @@ private function getUrl($url) public function setUp() { - $this->httpClient = new \Zend\Http\Client(null, ['timeout' => 10]); + $this->httpClient = new \Laminas\Http\Client(null, ['timeout' => 10]); } public function testServerHasPid() diff --git a/dev/tests/integration/testsuite/Magento/Quote/Model/Quote/AddressTest.php b/dev/tests/integration/testsuite/Magento/Quote/Model/Quote/AddressTest.php index 545638bcb0c57..dcd36d4078f8c 100644 --- a/dev/tests/integration/testsuite/Magento/Quote/Model/Quote/AddressTest.php +++ b/dev/tests/integration/testsuite/Magento/Quote/Model/Quote/AddressTest.php @@ -5,30 +5,41 @@ */ namespace Magento\Quote\Model\Quote; +use Magento\Customer\Api\AddressRepositoryInterface; +use Magento\Customer\Api\CustomerRepositoryInterface; +use Magento\Customer\Api\Data\AddressInterface; +use Magento\Customer\Api\Data\AddressInterfaceFactory; +use Magento\Customer\Api\Data\CustomerInterface; +use Magento\Customer\Model\CustomerRegistry; +use Magento\Framework\Reflection\DataObjectProcessor; +use Magento\Quote\Model\Quote; use Magento\TestFramework\Helper\Bootstrap; +use Magento\TestFramework\Indexer\TestCase; /** + * Class to test Sales Quote address model functionality + * * @magentoDataFixture Magento/Sales/_files/quote_with_customer.php * @magentoDataFixture Magento/Customer/_files/customer_two_addresses.php */ -class AddressTest extends \Magento\TestFramework\Indexer\TestCase +class AddressTest extends TestCase { - /** @var \Magento\Quote\Model\Quote $quote */ + /** @var Quote $quote */ protected $_quote; - /** @var \Magento\Customer\Api\Data\CustomerInterface $customer */ + /** @var CustomerInterface $customer */ protected $_customer; - /** @var \Magento\Quote\Model\Quote\Address */ + /** @var Address */ protected $_address; - /**@var \Magento\Customer\Api\CustomerRepositoryInterface $customerRepository */ + /**@var CustomerRepositoryInterface $customerRepository */ protected $customerRepository; - /** @var \Magento\Customer\Api\AddressRepositoryInterface $addressRepository */ + /** @var AddressRepositoryInterface $addressRepository */ protected $addressRepository; - /** @var \Magento\Framework\Reflection\DataObjectProcessor */ + /** @var DataObjectProcessor */ protected $dataProcessor; /** @@ -36,7 +47,7 @@ class AddressTest extends \Magento\TestFramework\Indexer\TestCase */ public static function setUpBeforeClass() { - $db = \Magento\TestFramework\Helper\Bootstrap::getInstance()->getBootstrap() + $db = Bootstrap::getInstance()->getBootstrap() ->getApplication() ->getDbInstance(); if (!$db->isDbDumpExists()) { @@ -48,43 +59,46 @@ public static function setUpBeforeClass() } /** - * Initialize quote and customer fixtures + * @inheritdoc */ public function setUp() { - $this->_quote = \Magento\TestFramework\Helper\Bootstrap::getObjectManager()->create( - \Magento\Quote\Model\Quote::class + $this->_quote = Bootstrap::getObjectManager()->create( + Quote::class ); $this->_quote->load('test01', 'reserved_order_id'); $this->_quote->setIsMultiShipping('0'); - $this->customerRepository = \Magento\TestFramework\Helper\Bootstrap::getObjectManager()->create( - \Magento\Customer\Api\CustomerRepositoryInterface::class + $this->customerRepository = Bootstrap::getObjectManager()->create( + CustomerRepositoryInterface::class ); $this->_customer = $this->customerRepository->getById(1); /** @var \Magento\Sales\Model\Order\Address $address */ - $this->_address = \Magento\TestFramework\Helper\Bootstrap::getObjectManager()->create( - \Magento\Quote\Model\Quote\Address::class + $this->_address = Bootstrap::getObjectManager()->create( + Address::class ); $this->_address->setId(1); $this->_address->load($this->_address->getId()); $this->_address->setQuote($this->_quote); - $this->addressRepository = \Magento\TestFramework\Helper\Bootstrap::getObjectManager()->create( - \Magento\Customer\Api\AddressRepositoryInterface::class + $this->addressRepository = Bootstrap::getObjectManager()->create( + AddressRepositoryInterface::class ); - $this->dataProcessor = \Magento\TestFramework\Helper\Bootstrap::getObjectManager()->create( - \Magento\Framework\Reflection\DataObjectProcessor::class + $this->dataProcessor = Bootstrap::getObjectManager()->create( + DataObjectProcessor::class ); } + /** + * @inheritdoc + */ protected function tearDown() { - /** @var \Magento\Customer\Model\CustomerRegistry $customerRegistry */ - $customerRegistry = \Magento\TestFramework\Helper\Bootstrap::getObjectManager() - ->get(\Magento\Customer\Model\CustomerRegistry::class); + /** @var CustomerRegistry $customerRegistry */ + $customerRegistry = Bootstrap::getObjectManager() + ->get(CustomerRegistry::class); //Cleanup customer from registry $customerRegistry->remove(1); } @@ -102,9 +116,9 @@ public function testSameAsBillingForBillingAddress($unsetId) if ($unsetId) { $address->setId(null); } - /** @var \Magento\Customer\Api\AddressRepositoryInterface $addressRepository */ + /** @var AddressRepositoryInterface $addressRepository */ $addressRepository = Bootstrap::getObjectManager() - ->create(\Magento\Customer\Api\AddressRepositoryInterface::class); + ->create(AddressRepositoryInterface::class); $customerAddressData = $addressRepository->getById($this->_customer->getDefaultBilling()); $address->setSameAsBilling(0)->setCustomerAddressData($customerAddressData)->save(); $this->assertEquals(0, $this->_quote->getBillingAddress()->getSameAsBilling()); @@ -155,9 +169,9 @@ public function testSameAsBillingWhenQuoteAddressHasNoCustomerAddress($unsetId) */ public function testSameAsBillingWhenCustomerHasNoDefaultShippingAddress($unsetId) { - /** @var \Magento\Customer\Api\AddressRepositoryInterface $addressRepository */ + /** @var AddressRepositoryInterface $addressRepository */ $addressRepository = Bootstrap::getObjectManager() - ->create(\Magento\Customer\Api\AddressRepositoryInterface::class); + ->create(AddressRepositoryInterface::class); $this->_customer->setDefaultShipping(-1) ->setAddresses( [ @@ -194,9 +208,9 @@ public function testSameAsBillingWhenCustomerHasBillingSameShipping($unsetId) */ public function testSameAsBillingWhenCustomerHasDefaultShippingAddress() { - /** @var \Magento\Customer\Api\AddressRepositoryInterface $addressRepository */ + /** @var AddressRepositoryInterface $addressRepository */ $addressRepository = Bootstrap::getObjectManager() - ->create(\Magento\Customer\Api\AddressRepositoryInterface::class); + ->create(AddressRepositoryInterface::class); $this->_customer->setDefaultShipping(2) ->setAddresses([$addressRepository->getById($this->_address->getId())]); $this->_customer = $this->customerRepository->save($this->_customer); @@ -218,19 +232,39 @@ protected function _setCustomerAddressAndSave($unsetId) if ($unsetId) { $shippingAddress->setId(null); } - /** @var \Magento\Customer\Api\AddressRepositoryInterface $addressRepository */ + /** @var AddressRepositoryInterface $addressRepository */ $addressRepository = Bootstrap::getObjectManager() - ->create(\Magento\Customer\Api\AddressRepositoryInterface::class); + ->create(AddressRepositoryInterface::class); $shippingAddress->setSameAsBilling(0) ->setCustomerAddressData($addressRepository->getById($this->_customer->getDefaultBilling())) ->save(); } + /** + * @return array + */ public function unsetAddressIdDataProvider() { return [[true], [false]]; } + /** + * Test to get same as billing flag after change quote customer + */ + public function testSameAsBillingAfterCustomerWesChanged() + { + $shippingAddressId = 2; + $this->_quote->setCustomer($this->_customer); + /** Make different default shipping and default billing addresses */ + $this->_customer->setDefaultShipping($shippingAddressId); + $this->_quote->getShippingAddress()->setCustomerAddressId($shippingAddressId); + /** Emulate to change customer */ + $this->_quote->setOrigData('customer_id', null); + $shippingAddress = $this->_quote->getShippingAddress(); + $shippingAddress->beforeSave(); + $this->assertEquals(false, $this->_quote->getShippingAddress()->getSameAsBilling()); + } + /** * Import customer address to quote address */ @@ -241,13 +275,13 @@ public function testImportCustomerAddressDataWithCustomer() $city = 'TestCity'; $street = 'Street1'; - /** @var \Magento\Customer\Api\Data\AddressInterfaceFactory $addressFactory */ + /** @var AddressInterfaceFactory $addressFactory */ $addressFactory = Bootstrap::getObjectManager()->create( - \Magento\Customer\Api\Data\AddressInterfaceFactory::class + AddressInterfaceFactory::class ); - /** @var \Magento\Customer\Api\AddressRepositoryInterface $addressRepository */ + /** @var AddressRepositoryInterface $addressRepository */ $addressRepository = Bootstrap::getObjectManager()->create( - \Magento\Customer\Api\AddressRepositoryInterface::class + AddressRepositoryInterface::class ); $addressData = $addressFactory->create() ->setCustomerId($customerIdFromFixture) @@ -284,6 +318,9 @@ public function testExportCustomerAddressData() $this->assertEquals($company, $customerAddress->getCompany(), 'Company was exported incorrectly.'); } + /** + * Test to Set the required fields + */ public function testPopulateBeforeSaveData() { /** Preconditions */ @@ -303,9 +340,9 @@ public function testPopulateBeforeSaveData() "Precondition failed: Customer address ID was not set." ); - /** @var \Magento\Customer\Api\Data\AddressInterfaceFactory $addressFactory */ + /** @var AddressInterfaceFactory $addressFactory */ $addressFactory = Bootstrap::getObjectManager()->create( - \Magento\Customer\Api\Data\AddressInterfaceFactory::class + AddressInterfaceFactory::class ); $customerAddressData = $addressFactory->create()->setId($customerAddressId); $this->_address->setCustomerAddressData($customerAddressData); @@ -317,22 +354,26 @@ public function testPopulateBeforeSaveData() } /** - * Tests + * Test to retrieve applied taxes * - * @covers \Magento\Quote\Model\Quote\Address::setAppliedTaxes() - * @covers \Magento\Quote\Model\Quote\Address::getAppliedTaxes() - * @dataProvider dataProvider * @param $taxes * @param $expected + * @covers \Magento\Quote\Model\Quote\Address::setAppliedTaxes() + * @covers \Magento\Quote\Model\Quote\Address::getAppliedTaxes() + * @dataProvider appliedTaxesDataProvider */ public function testAppliedTaxes($taxes, $expected) { $this->_address->setAppliedTaxes($taxes); - $this->assertSame($expected, $this->_address->getAppliedTaxes()); } - public function dataProvider() + /** + * Retrieve applied taxes data provider + * + * @return array + */ + public function appliedTaxesDataProvider() { return [ ['test', 'test'], @@ -340,6 +381,9 @@ public function dataProvider() ]; } + /** + * Test to sate shipping address without region + */ public function testSaveShippingAddressWithEmptyRegionId() { $customerAddress = $this->addressRepository->getById(1); @@ -347,7 +391,7 @@ public function testSaveShippingAddressWithEmptyRegionId() $address = $this->dataProcessor->buildOutputDataArray( $customerAddress, - \Magento\Customer\Api\Data\AddressInterface::class + AddressInterface::class ); $shippingAddress = $this->_quote->getShippingAddress(); diff --git a/dev/tests/integration/testsuite/Magento/Quote/Model/ShippingMethodManagementTest.php b/dev/tests/integration/testsuite/Magento/Quote/Model/ShippingMethodManagementTest.php index 8db7b65d0142d..c080d7a3ab229 100644 --- a/dev/tests/integration/testsuite/Magento/Quote/Model/ShippingMethodManagementTest.php +++ b/dev/tests/integration/testsuite/Magento/Quote/Model/ShippingMethodManagementTest.php @@ -6,17 +6,57 @@ namespace Magento\Quote\Model; +use Magento\Customer\Api\AddressRepositoryInterface; +use Magento\Customer\Api\CustomerRepositoryInterface; +use Magento\Customer\Api\Data\AddressInterface; +use Magento\Customer\Api\Data\GroupInterface; +use Magento\Customer\Api\GroupRepositoryInterface; +use Magento\Customer\Model\Vat; +use Magento\Framework\Api\SearchCriteriaBuilder; +use Magento\Framework\App\Config\MutableScopeConfigInterface; +use Magento\Framework\ObjectManagerInterface; +use Magento\Quote\Api\CartRepositoryInterface; +use Magento\Quote\Api\ShippingMethodManagementInterface; +use Magento\Store\Model\ScopeInterface; +use Magento\Tax\Api\Data\TaxClassInterface; +use Magento\Tax\Api\TaxClassRepositoryInterface; +use Magento\Tax\Model\ClassModel; +use Magento\Tax\Model\Config as TaxConfig; +use Magento\TestFramework\Helper\Bootstrap; +use Magento\TestFramework\Quote\Model\GetQuoteByReservedOrderId; + /** - * Class ShippingMethodManagementTest + * Test for shipping methods management * * @magentoDbIsolation enabled + * @SuppressWarnings(PHPMD.CouplingBetweenObjects) */ class ShippingMethodManagementTest extends \PHPUnit\Framework\TestCase { + /** @var ObjectManagerInterface $objectManager */ + private $objectManager; + + /** @var GroupRepositoryInterface $groupRepository */ + private $groupRepository; + + /** @var TaxClassRepositoryInterface $taxClassRepository */ + private $taxClassRepository; + + /** + * @inheritdoc + */ + protected function setUp() + { + $this->objectManager = Bootstrap::getObjectManager(); + $this->groupRepository = $this->objectManager->get(GroupRepositoryInterface::class); + $this->taxClassRepository = $this->objectManager->get(TaxClassRepositoryInterface::class); + } + /** * @magentoDataFixture Magento/SalesRule/_files/cart_rule_100_percent_off.php * @magentoDataFixture Magento/Sales/_files/quote_with_customer.php * @return void + * @throws \Magento\Framework\Exception\NoSuchEntityException */ public function testRateAppliedToShipping(): void { @@ -79,6 +119,71 @@ public function testTableRateFreeShipping() } } + /** + * Test table rate amount for the cart that contains some items with free shipping applied. + * + * @magentoConfigFixture current_store carriers/tablerate/active 1 + * @magentoConfigFixture current_store carriers/flatrate/active 0 + * @magentoConfigFixture current_store carriers/freeshipping/active 0 + * @magentoConfigFixture current_store carriers/tablerate/condition_name package_value_with_discount + * @magentoDataFixture Magento/Catalog/_files/categories.php + * @magentoDataFixture Magento/SalesRule/_files/cart_rule_free_shipping_by_category.php + * @magentoDataFixture Magento/Sales/_files/quote_with_multiple_products.php + * @magentoDataFixture Magento/OfflineShipping/_files/tablerates_price.php + * @return void + */ + public function testTableRateWithCartRuleForFreeShipping() + { + $objectManager = \Magento\TestFramework\Helper\Bootstrap::getObjectManager(); + $quote = $this->getQuote('tableRate'); + $cartId = $quote->getId(); + if (!$cartId) { + $this->fail('quote fixture failed'); + } + /** @var \Magento\Quote\Model\QuoteIdMask $quoteIdMask */ + $quoteIdMask = \Magento\TestFramework\Helper\Bootstrap::getObjectManager() + ->create(\Magento\Quote\Model\QuoteIdMaskFactory::class) + ->create(); + $quoteIdMask->load($cartId, 'quote_id'); + //Use masked cart Id + $cartId = $quoteIdMask->getMaskedId(); + $addressFactory = $this->objectManager->get(\Magento\Quote\Api\Data\AddressInterfaceFactory::class); + /** @var \Magento\Quote\Api\Data\AddressInterface $address */ + $address = $addressFactory->create(); + $address->setCountryId('US'); + /** @var \Magento\Quote\Api\GuestShippingMethodManagementInterface $shippingEstimation */ + $shippingEstimation = $objectManager->get(\Magento\Quote\Api\GuestShippingMethodManagementInterface::class); + $result = $shippingEstimation->estimateByExtendedAddress($cartId, $address); + $this->assertCount(1, $result); + $rate = reset($result); + $expectedResult = [ + 'method_code' => 'bestway', + 'amount' => 10 + ]; + $this->assertEquals($expectedResult['method_code'], $rate->getMethodCode()); + $this->assertEquals($expectedResult['amount'], $rate->getAmount()); + } + + /** + * Retrieves quote by reserved order id. + * + * @param string $reservedOrderId + * @return Quote + */ + private function getQuote(string $reservedOrderId): Quote + { + /** @var SearchCriteriaBuilder $searchCriteriaBuilder */ + $searchCriteriaBuilder = $this->objectManager->get(SearchCriteriaBuilder::class); + $searchCriteria = $searchCriteriaBuilder->addFilter('reserved_order_id', $reservedOrderId) + ->create(); + + /** @var CartRepositoryInterface $quoteRepository */ + $quoteRepository = $this->objectManager->get(CartRepositoryInterface::class); + $items = $quoteRepository->getList($searchCriteria)->getItems(); + + return array_pop($items); + } + /** * @magentoConfigFixture current_store carriers/tablerate/active 1 * @magentoConfigFixture current_store carriers/tablerate/condition_name package_qty @@ -173,4 +278,130 @@ private function executeTestFlow($flatRateAmount, $tableRateAmount) $this->assertEquals($expectedResult[$rate->getCarrierCode()]['method_code'], $rate->getMethodCode()); } } + + /** + * Test for estimate shipping with tax and changed VAT customer group + * + * @magentoDbIsolation disabled + * @magentoDataFixture Magento/Tax/_files/tax_classes_de.php + * @magentoDataFixture Magento/Sales/_files/quote_with_customer.php + * @magentoDataFixture Magento/Customer/_files/customer_group.php + * @magentoDataFixture Magento/Customer/_files/customer_address.php + * @magentoConfigFixture current_store customer/create_account/tax_calculation_address_type shipping + * @magentoConfigFixture current_store customer/create_account/default_group 1 + * @magentoConfigFixture current_store customer/create_account/auto_group_assign 1 + * @magentoConfigFixture current_store tax/calculation/price_includes_tax 1 + * @magentoConfigFixture current_store tax/calculation/shipping_includes_tax 1 + */ + public function testEstimateByAddressWithInclExclTaxAndVATGroup() + { + /** @var CustomerRepositoryInterface $customerRepository */ + $customerRepository = $this->objectManager->get(CustomerRepositoryInterface::class); + $customer = $customerRepository->get('customer@example.com'); + + /** @var GroupInterface $customerGroup */ + $customerGroup = $this->findCustomerGroupByCode('custom_group'); + $customerGroup->setTaxClassId($this->getTaxClass('CustomerTaxClass')->getClassId()); + $this->groupRepository->save($customerGroup); + + $customer->setGroupId($customerGroup->getId()); + $customer->setTaxvat('12'); + $customerRepository->save($customer); + $this->setConfig($customerGroup->getId(), $this->getTaxClass('ProductTaxClass')->getClassId()); + $this->changeCustomerAddress($customer->getDefaultShipping()); + + $quote = $this->objectManager->get(GetQuoteByReservedOrderId::class)->execute('test01'); + + /** @var ShippingMethodManagementInterface $shippingEstimation */ + $shippingEstimation = $this->objectManager->get(ShippingMethodManagementInterface::class); + $result = $shippingEstimation->estimateByAddressId($quote->getId(), $customer->getDefaultShipping()); + + $this->assertEquals(6.05, $result[0]->getPriceInclTax()); + $this->assertEquals(5.0, $result[0]->getPriceExclTax()); + } + + /** + * Find the group with a given code. + * + * @param string $code + * + * @return GroupInterface + */ + protected function findCustomerGroupByCode(string $code): ?GroupInterface + { + /** @var SearchCriteriaBuilder $searchBuilder */ + $searchBuilder = $this->objectManager->get(SearchCriteriaBuilder::class); + $searchCriteria = $searchBuilder->addFilter('code', $code) + ->create(); + $groups = $this->groupRepository->getList($searchCriteria) + ->getItems(); + + return array_shift($groups); + } + + /** + * Change customer address + * + * @param int $customerAddressId + * + * @return AddressInterface + */ + private function changeCustomerAddress(int $customerAddressId): AddressInterface + { + $addressRepository = $this->objectManager->get(AddressRepositoryInterface::class); + $address = $addressRepository->getById($customerAddressId); + $address->setVatId(12345); + $address->setCountryId('DE'); + $address->setRegionId(0); + $address->setPostcode(10178); + + return $addressRepository->save($address); + } + + /** + * Get tax class. + * + * @param string $name + * + * @return TaxClassInterface + */ + private function getTaxClass(string $name): ?TaxClassInterface + { + /** @var SearchCriteriaBuilder $searchBuilder */ + $searchBuilder = $this->objectManager->get(SearchCriteriaBuilder::class); + $searchCriteria = $searchBuilder->addFilter(ClassModel::KEY_NAME, $name) + ->create(); + $searchResults = $this->taxClassRepository->getList($searchCriteria) + ->getItems(); + + return array_shift($searchResults); + } + + /** + * Set the configuration. + * + * @param int $customerGroupId + * @param int $productTaxClassId + * + * @return void + */ + private function setConfig(int $customerGroupId, int $productTaxClassId): void + { + $configData = [ + [ + 'path' => Vat::XML_PATH_CUSTOMER_VIV_INVALID_GROUP, + 'value' => $customerGroupId, + 'scope' => ScopeInterface::SCOPE_STORE, + ], + [ + 'path' => TaxConfig::CONFIG_XML_PATH_SHIPPING_TAX_CLASS, + 'value' => $productTaxClassId, + 'scope' => ScopeInterface::SCOPE_STORE, + ], + ]; + $config = $this->objectManager->get(MutableScopeConfigInterface::class); + foreach ($configData as $data) { + $config->setValue($data['path'], $data['value'], $data['scope']); + } + } } diff --git a/dev/tests/integration/testsuite/Magento/Reports/Block/Adminhtml/Config/Form/Field/YtdStartTest.php b/dev/tests/integration/testsuite/Magento/Reports/Block/Adminhtml/Config/Form/Field/YtdStartTest.php new file mode 100644 index 0000000000000..d378b3e7344e1 --- /dev/null +++ b/dev/tests/integration/testsuite/Magento/Reports/Block/Adminhtml/Config/Form/Field/YtdStartTest.php @@ -0,0 +1,58 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +declare(strict_types=1); + +namespace Magento\Reports\Block\Adminhtml\Config\Form\Field; + +use Magento\TestFramework\TestCase\AbstractBackendController; + +/** + * Test for \Magento\Reports\Block\Adminhtml\Config\Form\Field\YtdStart. + * + * @magentoAppArea adminhtml + */ +class YtdStartTest extends AbstractBackendController +{ + /** + * @var array + */ + private $monthNumbers = ['01', '02', '03', '04', '05', '06', '07', '08', '09', '10', '11', '12']; + + /** + * Test Get Month and Day Element renderer + * + * @return void + * @magentoAppIsolation enabled + * @magentoDbIsolation enabled + */ + public function testGetElementHtml(): void + { + $this->dispatch('backend/admin/system_config/edit/section/reports/'); + $body = $this->getResponse()->getBody(); + + $this->assertContains($this->getOptionsHtml('01'), $body); + } + + /** + * Options html + * + * @param string $selected + * @return string + */ + private function getOptionsHtml(string $selected): string + { + $html = ''; + foreach ($this->monthNumbers as $number) { + $html .= $number === $selected + ? '<option value="' . $selected . '" selected="selected">' . $selected . '</option>' + : '<option value="' . $number . '">' . $number . '</option>'; + + $html .= PHP_EOL; + } + + return $html; + } +} diff --git a/dev/tests/integration/testsuite/Magento/Reports/Model/ResourceModel/Report/Product/Viewed/CollectionTest.php b/dev/tests/integration/testsuite/Magento/Reports/Model/ResourceModel/Report/Product/Viewed/CollectionTest.php index fff057fd05688..18b6aa6405663 100644 --- a/dev/tests/integration/testsuite/Magento/Reports/Model/ResourceModel/Report/Product/Viewed/CollectionTest.php +++ b/dev/tests/integration/testsuite/Magento/Reports/Model/ResourceModel/Report/Product/Viewed/CollectionTest.php @@ -72,20 +72,20 @@ public function testTableSelection($period, $expectedTable, $dateFrom, $dateTo, $this->assertArrayHasKey('tableName', $from[$dbTableName]); } else { $union = $this->_collection->getSelect()->getPart('union'); + $count = count($union); if ($period !== null && $dateFrom !== null && $dateTo !== null && $period != 'month') { - $count = count($union); if ($period == 'year') { if ($dbTableName == "report_viewed_product_aggregated_daily") { - $this->assertEquals($count, 2); + $this->assertEquals(2, $count); } if ($dbTableName == "report_viewed_product_aggregated_yearly") { - $this->assertEquals($count, 3); + $this->assertEquals(3, $count); } } else { - $this->assertEquals($count, 3); + $this->assertEquals(3, $count); } } else { - $this->assertEquals(count($union), 2); + $this->assertEquals(2, $count); } } } @@ -98,8 +98,8 @@ public function testTableSelection($period, $expectedTable, $dateFrom, $dateTo, */ public function tableForPeriodDataProvider() { - $dateNow = date('Y-m-d', time()); - $dateYearAgo = date('Y-m-d', strtotime($dateNow . ' -1 year')); + $dateFrom = '2019-10-15'; + $dateYearBefore = date('Y-m-d', strtotime($dateFrom . ' -1 year')); return [ [ 'period' => 'year', @@ -111,32 +111,32 @@ public function tableForPeriodDataProvider() [ 'period' => 'year', 'table' => 'report_viewed_product_aggregated_yearly', - 'date_from' => $dateYearAgo, - 'date_to' => $dateNow, + 'date_from' => $dateYearBefore, + 'date_to' => $dateFrom, ], [ 'period' => 'year', 'table' => 'report_viewed_product_aggregated_yearly', - 'date_from' => $dateYearAgo, + 'date_from' => $dateYearBefore, 'date_to' => null, ], [ 'period' => 'month', 'table' => 'report_viewed_product_aggregated_monthly', 'date_from' => null, - 'date_to' => $dateNow, + 'date_to' => $dateFrom, ], [ 'period' => 'year', 'table' => 'report_viewed_product_aggregated_yearly', - 'date_from' => $dateYearAgo, + 'date_from' => $dateYearBefore, 'date_to' => null, ], [ 'period' => 'year', 'table' => 'report_viewed_product_aggregated_yearly', 'date_from' => null, - 'date_to' => $dateNow, + 'date_to' => $dateFrom, ], [ 'period' => 'month', @@ -147,19 +147,19 @@ public function tableForPeriodDataProvider() [ 'period' => 'month', 'table' => 'report_viewed_product_aggregated_monthly', - 'date_from' => $dateYearAgo, - 'date_to' => $dateYearAgo, + 'date_from' => $dateYearBefore, + 'date_to' => $dateYearBefore, ], [ 'period' => 'month', 'table' => 'report_viewed_product_aggregated_monthly', 'date_from' => null, - 'date_to' => $dateYearAgo, + 'date_to' => $dateYearBefore, ], [ 'period' => 'month', 'table' => 'report_viewed_product_aggregated_monthly', - 'date_from' => $dateYearAgo, + 'date_from' => $dateYearBefore, 'date_to' => null, ], [ @@ -177,32 +177,32 @@ public function tableForPeriodDataProvider() [ 'period' => null, 'table' => 'report_viewed_product_aggregated_daily', - 'date_from' => $dateYearAgo, - 'date_to' => $dateNow, + 'date_from' => $dateYearBefore, + 'date_to' => $dateFrom, ], [ 'period' => null, 'table' => 'report_viewed_product_aggregated_daily', - 'date_from' => $dateNow, - 'date_to' => $dateNow, + 'date_from' => $dateFrom, + 'date_to' => $dateFrom, ], [ 'period' => 'day', 'table' => 'report_viewed_product_aggregated_daily', - 'date_from' => $dateYearAgo, - 'date_to' => $dateYearAgo, + 'date_from' => $dateYearBefore, + 'date_to' => $dateYearBefore, ], [ 'period' => 'year', 'table' => 'report_viewed_product_aggregated_daily', - 'date_from' => $dateYearAgo, - 'date_to' => $dateYearAgo, + 'date_from' => $dateYearBefore, + 'date_to' => $dateYearBefore, ], [ 'period' => 'year', 'table' => 'report_viewed_product_aggregated_daily', 'date_from' => null, - 'date_to' => $dateYearAgo, + 'date_to' => $dateYearBefore, ], [ 'period' => null, diff --git a/dev/tests/integration/testsuite/Magento/Review/Controller/Adminhtml/Customer/ProductReviewsTest.php b/dev/tests/integration/testsuite/Magento/Review/Controller/Adminhtml/Customer/ProductReviewsTest.php new file mode 100644 index 0000000000000..4203fb9c16b29 --- /dev/null +++ b/dev/tests/integration/testsuite/Magento/Review/Controller/Adminhtml/Customer/ProductReviewsTest.php @@ -0,0 +1,31 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +declare(strict_types=1); + +namespace Magento\Review\Controller\Adminhtml\Customer; + +use Magento\Framework\App\Request\Http; +use Magento\TestFramework\TestCase\AbstractBackendController; + +/** + * Test for customer product reviews page. + */ +class ProductReviewsTest extends AbstractBackendController +{ + /** + * Check Customer product review action. + * + * @magentoDataFixture Magento/Customer/_files/customer_sample.php + * @return void + */ + public function testProductReviewsAction(): void + { + $this->getRequest()->setPostValue(['id' => 1])->setMethod(Http::METHOD_POST); + $this->dispatch('backend/review/customer/productReviews'); + $body = $this->getResponse()->getBody(); + $this->assertContains('<div id="reviewGrid"', $body); + } +} diff --git a/dev/tests/integration/testsuite/Magento/Review/Controller/Adminhtml/Product/MassUpdateTest.php b/dev/tests/integration/testsuite/Magento/Review/Controller/Adminhtml/Product/MassUpdateTest.php index d6664d1fbb7ac..60d5d8cd5c4f4 100644 --- a/dev/tests/integration/testsuite/Magento/Review/Controller/Adminhtml/Product/MassUpdateTest.php +++ b/dev/tests/integration/testsuite/Magento/Review/Controller/Adminhtml/Product/MassUpdateTest.php @@ -13,7 +13,7 @@ use Magento\TestFramework\Helper\Bootstrap; use Magento\Framework\UrlInterface; use Magento\Review\Model\ResourceModel\Review\CollectionFactory; -use Zend\Http\Request; +use Laminas\Http\Request; /** * Test Mass Update action. diff --git a/dev/tests/integration/testsuite/Magento/Sales/Controller/Adminhtml/Order/Create/SaveTest.php b/dev/tests/integration/testsuite/Magento/Sales/Controller/Adminhtml/Order/Create/SaveTest.php index 6dba48092c400..2ac14fbdc68cc 100644 --- a/dev/tests/integration/testsuite/Magento/Sales/Controller/Adminhtml/Order/Create/SaveTest.php +++ b/dev/tests/integration/testsuite/Magento/Sales/Controller/Adminhtml/Order/Create/SaveTest.php @@ -68,6 +68,14 @@ protected function setUp() */ public function testExecuteWithPaymentOperation() { + /** @var OrderService|MockObject $orderService */ + $orderService = $this->getMockBuilder(OrderService::class) + ->disableOriginalConstructor() + ->getMock(); + $orderService->method('place') + ->willThrowException(new LocalizedException(__('Transaction has been declined.'))); + $this->_objectManager->addSharedInstance($orderService, OrderService::class); + $quote = $this->getQuote('2000000001'); $session = $this->_objectManager->get(Quote::class); $session->setQuoteId($quote->getId()); @@ -82,14 +90,6 @@ public function testExecuteWithPaymentOperation() $this->getRequest()->setMethod(Http::METHOD_POST); $this->getRequest()->setPostValue(['order' => $data]); - /** @var OrderService|MockObject $orderService */ - $orderService = $this->getMockBuilder(OrderService::class) - ->disableOriginalConstructor() - ->getMock(); - $orderService->method('place') - ->willThrowException(new LocalizedException(__('Transaction has been declined.'))); - $this->_objectManager->addSharedInstance($orderService, OrderService::class); - $this->dispatch('backend/sales/order_create/save'); $this->assertSessionMessages( self::equalTo(['Transaction has been declined.']), diff --git a/dev/tests/integration/testsuite/Magento/Sales/Model/Order/Email/Sender/ShipmentSenderTest.php b/dev/tests/integration/testsuite/Magento/Sales/Model/Order/Email/Sender/ShipmentSenderTest.php index 724865176188a..83bc7e10647b4 100644 --- a/dev/tests/integration/testsuite/Magento/Sales/Model/Order/Email/Sender/ShipmentSenderTest.php +++ b/dev/tests/integration/testsuite/Magento/Sales/Model/Order/Email/Sender/ShipmentSenderTest.php @@ -10,6 +10,9 @@ /** * @magentoAppArea frontend + * + * @deprecated since ShipmentSender is deprecated + * @see \Magento\Sales\Model\Order\Email\Sender\ShipmentSender */ class ShipmentSenderTest extends \PHPUnit\Framework\TestCase { diff --git a/dev/tests/integration/testsuite/Magento/Sales/Model/Service/PaymentFailuresServiceTest.php b/dev/tests/integration/testsuite/Magento/Sales/Model/Service/PaymentFailuresServiceTest.php index 383af7968e047..c85408f21819a 100644 --- a/dev/tests/integration/testsuite/Magento/Sales/Model/Service/PaymentFailuresServiceTest.php +++ b/dev/tests/integration/testsuite/Magento/Sales/Model/Service/PaymentFailuresServiceTest.php @@ -7,10 +7,12 @@ namespace Magento\Sales\Model\Service; +use Magento\Framework\Stdlib\DateTime\TimezoneInterface; use Magento\Quote\Api\CartRepositoryInterface; use Magento\Quote\Model\Quote; use Magento\Sales\Api\PaymentFailuresInterface; use Magento\TestFramework\Helper\Bootstrap; +use PHPUnit\Framework\MockObject\MockObject; /** * Tests \Magento\Sales\Api\PaymentFailuresInterface. @@ -28,10 +30,15 @@ class PaymentFailuresServiceTest extends \PHPUnit\Framework\TestCase private $quote; /** - * @var CartRepositoryInterface|\PHPUnit_Framework_MockObject_MockObject + * @var CartRepositoryInterface|MockObject */ private $cartRepositoryMock; + /** + * @var TimezoneInterface|MockObject + */ + private $localeDateMock; + /** * @inheritdoc */ @@ -42,11 +49,15 @@ protected function setUp() ->disableOriginalConstructor() ->setMethods(['get']) ->getMockForAbstractClass(); + $this->localeDateMock = $this->getMockBuilder(TimezoneInterface::class) + ->setMethods(['formatDateTime']) + ->getMockForAbstractClass(); $this->paymentFailures = Bootstrap::getObjectManager()->create( PaymentFailuresInterface::class, [ 'cartRepository' => $this->cartRepositoryMock, + 'localeDate' => $this->localeDateMock, ] ); } @@ -69,20 +80,19 @@ public function testHandlerWithCustomer(): void ->with($this->quote->getId()) ->willReturn($this->quote); + $dateAndTime = 'Nov 22, 2019, 1:00:00 AM'; + $this->localeDateMock->expects($this->atLeastOnce())->method('formatDateTime')->willReturn($dateAndTime); $this->paymentFailures->handle((int)$this->quote->getId(), $errorMessage->render()); $paymentReflection = new \ReflectionClass($this->paymentFailures); - $templateTimeMethod = $paymentReflection->getMethod('getLocaleDate'); - $templateTimeMethod->setAccessible(true); - $templateVarsMethod = $paymentReflection->getMethod('getTemplateVars'); $templateVarsMethod->setAccessible(true); $templateVars = $templateVarsMethod->invoke($this->paymentFailures, $this->quote, $errorMessage, $checkoutType); $expectedVars = [ - 'reason' => $errorMessage, + 'reason' => $errorMessage->render(), 'checkoutType' => $checkoutType, - 'dateAndTime' => $templateTimeMethod->invoke($this->paymentFailures), + 'dateAndTime' => $dateAndTime, 'customer' => 'John Smith', 'customerEmail' => 'aaa@aaa.com', 'paymentMethod' => 'Some Title Of The Method', diff --git a/dev/tests/integration/testsuite/Magento/Sales/_files/creditmemo_with_grouped_product.php b/dev/tests/integration/testsuite/Magento/Sales/_files/creditmemo_with_grouped_product.php new file mode 100644 index 0000000000000..5f5f534ae2cee --- /dev/null +++ b/dev/tests/integration/testsuite/Magento/Sales/_files/creditmemo_with_grouped_product.php @@ -0,0 +1,36 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +declare(strict_types=1); + +use Magento\Sales\Model\Order\Creditmemo; +use Magento\Sales\Model\Order\Creditmemo\Item; +use Magento\Sales\Model\Order\CreditmemoFactory; +use Magento\TestFramework\Helper\Bootstrap; + +require __DIR__ . '/order_with_grouped_product.php'; + +$objectManager = Bootstrap::getObjectManager(); + +$creditmemoFactory = $objectManager->get(CreditmemoFactory::class); +$creditmemo = $creditmemoFactory->createByOrder($order, $order->getData()); +$creditmemo->setOrder($order); +$creditmemo->setState(Creditmemo::STATE_OPEN); +$creditmemo->setIncrementId('100000002'); +$creditmemo->save(); + +$orderItem = current($order->getAllItems()); +$orderItem->setName('Test item') + ->setQtyRefunded(1) + ->setQtyInvoiced(10) + ->setOriginalPrice(20); + +$creditItem = $objectManager->get(Item::class); +$creditItem->setCreditmemo($creditmemo) + ->setName('Creditmemo item') + ->setOrderItemId($orderItem->getId()) + ->setQty(1) + ->setPrice(20) + ->save(); diff --git a/dev/tests/integration/testsuite/Magento/Sales/_files/creditmemo_with_grouped_product_rollback.php b/dev/tests/integration/testsuite/Magento/Sales/_files/creditmemo_with_grouped_product_rollback.php new file mode 100644 index 0000000000000..c8420f8a252ca --- /dev/null +++ b/dev/tests/integration/testsuite/Magento/Sales/_files/creditmemo_with_grouped_product_rollback.php @@ -0,0 +1,18 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +declare(strict_types=1); + +use Magento\Sales\Model\Order\Creditmemo; +use Magento\Sales\Model\ResourceModel\Order\Creditmemo\Collection; +use Magento\TestFramework\Helper\Bootstrap; + +require 'default_rollback.php'; + +/** @var $creditmemo Creditmemo */ +$creditmemoCollection = Bootstrap::getObjectManager()->create(Collection::class); +foreach ($creditmemoCollection as $creditmemo) { + $creditmemo->delete(); +} diff --git a/dev/tests/integration/testsuite/Magento/Sales/_files/customer_order_item_with_product_and_custom_options.php b/dev/tests/integration/testsuite/Magento/Sales/_files/customer_order_item_with_product_and_custom_options.php new file mode 100644 index 0000000000000..e56bba76fe196 --- /dev/null +++ b/dev/tests/integration/testsuite/Magento/Sales/_files/customer_order_item_with_product_and_custom_options.php @@ -0,0 +1,12 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ + +require __DIR__ . '/../../../Magento/Customer/_files/customer.php'; +require __DIR__ . '/../../../Magento/Catalog/_files/order_item_with_product_and_custom_options.php'; + +$customerIdFromFixture = 1; +/** @var $order \Magento\Sales\Model\Order */ +$order->setCustomerId($customerIdFromFixture)->setCustomerIsGuest(false)->save(); diff --git a/dev/tests/integration/testsuite/Magento/Sales/_files/customer_order_item_with_product_and_custom_options_rollback.php b/dev/tests/integration/testsuite/Magento/Sales/_files/customer_order_item_with_product_and_custom_options_rollback.php new file mode 100644 index 0000000000000..5b0bf62721096 --- /dev/null +++ b/dev/tests/integration/testsuite/Magento/Sales/_files/customer_order_item_with_product_and_custom_options_rollback.php @@ -0,0 +1,8 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ + +require __DIR__ . '/../../../Magento/Catalog/_files/order_item_with_product_and_custom_options_rollback.php'; +require __DIR__ . '/../../../Magento/Customer/_files/customer_rollback.php'; diff --git a/dev/tests/integration/testsuite/Magento/Sales/_files/order_list_with_invoice.php b/dev/tests/integration/testsuite/Magento/Sales/_files/order_list_with_invoice.php new file mode 100644 index 0000000000000..1460f5742c1fc --- /dev/null +++ b/dev/tests/integration/testsuite/Magento/Sales/_files/order_list_with_invoice.php @@ -0,0 +1,104 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +declare(strict_types=1); + +use Magento\Framework\DB\Transaction; +use Magento\Framework\Stdlib\DateTime; +use Magento\Sales\Api\Data\OrderInterfaceFactory; +use Magento\Sales\Api\InvoiceManagementInterface; +use Magento\Sales\Api\OrderRepositoryInterface; +use Magento\Sales\Model\Order; +use Magento\Sales\Model\Order\Invoice; +use Magento\Sales\Model\OrderFactory; + +require __DIR__ . '/order.php'; + +/** @var OrderFactory $orderFactory */ +$orderFactory = $objectManager->get(OrderInterfaceFactory::class); +/** @var OrderRepositoryInterface $orderRepository */ +$orderRepository = $objectManager->get(OrderRepositoryInterface::class); +/** @var InvoiceManagementInterface $invoiceManagement */ +$invoiceManagement = $objectManager->get(InvoiceManagementInterface::class); +/** @var Transaction $transaction */ +$transaction = $objectManager->get(Transaction::class); + +$dateTime = new \DateTimeImmutable(); +$ordersData = [ + [ + 'increment_id' => '100000002', + 'state' => Order::STATE_PROCESSING, + 'status' => 'processing', + 'base_to_global_rate' => 1, + 'base_grand_total' => 120.00, + 'grand_total' => 120.00, + 'subtotal' => 120.00, + 'created_at' => $dateTime->modify('-1 hour')->format(DateTime::DATETIME_PHP_FORMAT), + ], + [ + 'increment_id' => '100000003', + 'state' => Order::STATE_PROCESSING, + 'status' => 'processing', + 'base_to_global_rate' => 1, + 'base_grand_total' => 130.00, + 'grand_total' => 130.00, + 'subtotal' => 130.00, + 'created_at' => max($dateTime->modify('-1 day'), $dateTime->modify('first day of this month')) + ->format(DateTime::DATETIME_PHP_FORMAT), + ], + [ + 'increment_id' => '100000004', + 'state' => Order::STATE_PROCESSING, + 'status' => 'processing', + 'base_to_global_rate' => 1, + 'base_grand_total' => 140.00, + 'grand_total' => 140.00, + 'subtotal' => 140.00, + 'created_at' => $dateTime->modify('first day of this month')->format(DateTime::DATETIME_PHP_FORMAT), + ], + [ + 'increment_id' => '100000005', + 'state' => Order::STATE_PROCESSING, + 'status' => 'processing', + 'base_to_global_rate' => 1, + 'base_grand_total' => 150.00, + 'grand_total' => 150.00, + 'subtotal' => 150.00, + 'created_at' => $dateTime->modify('first day of january this year')->format(DateTime::DATETIME_PHP_FORMAT), + ], + [ + 'increment_id' => '100000006', + 'state' => Order::STATE_PROCESSING, + 'status' => 'processing', + 'base_to_global_rate' => 1, + 'base_grand_total' => 160.00, + 'grand_total' => 160.00, + 'subtotal' => 160.00, + 'created_at' => $dateTime->modify('first day of january last year')->format(DateTime::DATETIME_PHP_FORMAT), + ], +]; + +foreach ($ordersData as $orderData) { + /** @var Order $order */ + $order = $orderFactory->create(); + $order + ->setData($orderData) + ->addItem($orderItem) + ->setCustomerIsGuest(true) + ->setCustomerEmail('customer@null.com') + ->setBillingAddress($billingAddress) + ->setShippingAddress($shippingAddress) + ->setPayment($payment); + $orderRepository->save($order); + + /** @var Invoice $invoice */ + $invoice = $invoiceManagement->prepareInvoice($order); + $invoice->register(); + $order->setIsInProcess(true); + $transaction + ->addObject($order) + ->addObject($invoice) + ->save(); +} diff --git a/dev/tests/integration/testsuite/Magento/Sales/_files/order_list_with_invoice_rollback.php b/dev/tests/integration/testsuite/Magento/Sales/_files/order_list_with_invoice_rollback.php new file mode 100644 index 0000000000000..2595d6bf4084a --- /dev/null +++ b/dev/tests/integration/testsuite/Magento/Sales/_files/order_list_with_invoice_rollback.php @@ -0,0 +1,8 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +declare(strict_types=1); + +require __DIR__ . '/default_rollback.php'; diff --git a/dev/tests/integration/testsuite/Magento/Sales/_files/order_with_1_qty_product.php b/dev/tests/integration/testsuite/Magento/Sales/_files/order_with_1_qty_product.php new file mode 100644 index 0000000000000..162eca7d68209 --- /dev/null +++ b/dev/tests/integration/testsuite/Magento/Sales/_files/order_with_1_qty_product.php @@ -0,0 +1,28 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ + +use Magento\TestFramework\Helper\Bootstrap; + +require __DIR__ . '/../../../Magento/Sales/_files/order_with_two_simple_products_qty_10.php'; + +$customerIdFromFixture = 1; +/** @var $order \Magento\Sales\Model\Order */ +$order->setCustomerId($customerIdFromFixture)->setCustomerIsGuest(false)->save(); + +// load product and set qty to 1 +/** @var \Magento\Catalog\Api\ProductRepositoryInterface $repository */ +$productRepository = Bootstrap::getObjectManager()->create(\Magento\Catalog\Api\ProductRepositoryInterface::class); +$productSku = 'simple'; +/** @var \Magento\Catalog\Model\Product $product */ +$product = $productRepository->get($productSku); +// set product qty to 1 +$product->setStockData( + [ + 'use_config_manage_stock' => 1, + 'qty' => 1, + ] +); +$productRepository->save($product); diff --git a/dev/tests/integration/testsuite/Magento/Sales/_files/order_with_1_qty_product_rollback.php b/dev/tests/integration/testsuite/Magento/Sales/_files/order_with_1_qty_product_rollback.php new file mode 100644 index 0000000000000..db665e644238c --- /dev/null +++ b/dev/tests/integration/testsuite/Magento/Sales/_files/order_with_1_qty_product_rollback.php @@ -0,0 +1,7 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ + +require __DIR__ . '/../../../Magento/Sales/_files/order_with_two_simple_products_qty_10_rollback.php'; diff --git a/dev/tests/integration/testsuite/Magento/Sales/_files/order_with_different_types_of_product.php b/dev/tests/integration/testsuite/Magento/Sales/_files/order_with_different_types_of_product.php new file mode 100644 index 0000000000000..6f7f26099c0b1 --- /dev/null +++ b/dev/tests/integration/testsuite/Magento/Sales/_files/order_with_different_types_of_product.php @@ -0,0 +1,214 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ + +use Magento\Sales\Api\OrderRepositoryInterface; + +require __DIR__ . '/../../../Magento/Customer/_files/customer.php'; +require __DIR__ . '/../../../Magento/Catalog/_files/product_simple_without_custom_options.php'; +$simpleProduct = $secondProduct; + +require __DIR__ . '/../../../Magento/ConfigurableProduct/_files/product_configurable.php'; +$configurableProduct = $productRepository->get($product->getSku()); +// this change is require for compatibility with downloadable product fixture, +// it sets id=1 for product that causes rewrite of configurable product +$configurableProduct->setId(111); +$configurableProduct->setUrlKey('configurable-product-new'); +$productRepository->save($configurableProduct); + +require __DIR__ . '/../../../Magento/GraphQl/Catalog/_files/virtual_product.php'; +$virtualProduct = $productRepository->get($product->getSku()); + +require __DIR__ . '/../../../Magento/Bundle/_files/bundle_product_radio_required_option.php'; +$bundleProduct = $productRepository->get($bundleProduct->getSku()); + +require __DIR__ . '/../../../Magento/Downloadable/_files/product_downloadable.php'; +$downloadableProduct = $productRepository->get($product->getSku()); + +/** \Magento\Customer\Model\Customer $customer */ +$addressData = include __DIR__ . '/../../../Magento/Sales/_files/address_data.php'; +$billingAddress = $objectManager->create(\Magento\Sales\Model\Order\Address::class, ['data' => $addressData]); +$billingAddress->setAddressType('billing'); +$shippingAddress = clone $billingAddress; +$shippingAddress->setId(null)->setAddressType('shipping'); + +$objectManager = \Magento\TestFramework\Helper\Bootstrap::getObjectManager(); +$payment = $objectManager->create(\Magento\Sales\Model\Order\Payment::class); +$payment->setMethod('checkmo'); +$customerIdFromFixture = 1; + +/** + * simple product + */ +$simpleProductItem = $objectManager->create(\Magento\Sales\Model\Order\Item::class); +$requestInfo = [ + 'qty' => 1 +]; +$simpleProductItem->setProductId($simpleProduct->getId()) + ->setQtyOrdered(1) + ->setBasePrice($simpleProduct->getPrice()) + ->setPrice($simpleProduct->getPrice()) + ->setRowTotal($simpleProduct->getPrice()) + ->setProductType($simpleProduct->getTypeId()) + ->setName($simpleProduct->getName()) + ->setSku($simpleProduct->getSku()) + ->setStoreId(0) + ->setProductOptions(['info_buyRequest' => $requestInfo]); + +/** + * configurable product + */ +/** @var \Magento\Eav\Model\Config $eavConfig */ +$eavConfig = $objectManager->get(\Magento\Eav\Model\Config::class); +/** @var $attribute \Magento\Catalog\Model\ResourceModel\Eav\Attribute */ +$attribute = $eavConfig->getAttribute('catalog_product', 'test_configurable'); + +/** @var $options \Magento\Eav\Model\ResourceModel\Entity\Attribute\Option\Collection */ +$options = $objectManager->create(\Magento\Eav\Model\ResourceModel\Entity\Attribute\Option\Collection::class); +$option = $options->setAttributeFilter($attribute->getId()) + ->getFirstItem(); + +$requestInfo = [ + 'qty' => 1, + 'super_attribute' => [ + $attribute->getId() => $option->getId(), + ], +]; + +$qtyOrdered = 1; +/** @var \Magento\Sales\Model\Order\Item $orderItem */ +$orderConfigurableItem = $objectManager->create(\Magento\Sales\Model\Order\Item::class); +$orderConfigurableItem->setProductId($configurableProduct->getId())->setQtyOrdered($qtyOrdered); +$orderConfigurableItem->setBasePrice($configurableProduct->getPrice()); +$orderConfigurableItem->setPrice($configurableProduct->getPrice()); +$orderConfigurableItem->setRowTotal($configurableProduct->getPrice()); +$orderConfigurableItem->setParentItemId(null); +$orderConfigurableItem->setProductType('configurable'); +$orderConfigurableItem->setProductOptions(['info_buyRequest' => $requestInfo]); +if ($configurableProduct->getExtensionAttributes() + && (array)$configurableProduct->getExtensionAttributes()->getConfigurableProductLinks() +) { + $simpleProductId = current($configurableProduct->getExtensionAttributes()->getConfigurableProductLinks()); + /** @var \Magento\Catalog\Api\Data\ProductInterface $simpleProduct */ + $simpleProduct = $productRepository->getById($simpleProductId); + $requestInfo['product'] = $simpleProductId; + $requestInfo['item'] = $simpleProduct; + $orderConfigurableItem->setProductOptions(['info_buyRequest' => $requestInfo]); +} + +/** + * virtual product + */ +$virtualProductItem = $objectManager->create(\Magento\Sales\Model\Order\Item::class); +$requestInfo = [ + 'qty' => 1 +]; +$virtualProductItem->setProductId($virtualProduct->getId()) + ->setQtyOrdered(1) + ->setBasePrice($virtualProduct->getPrice()) + ->setPrice($virtualProduct->getPrice()) + ->setRowTotal($virtualProduct->getPrice()) + ->setProductType($virtualProduct->getTypeId()) + ->setName($virtualProduct->getName()) + ->setSku($virtualProduct->getSku()) + ->setStoreId(0) + ->setProductOptions(['info_buyRequest' => $requestInfo]); +/** + * downloadable product + */ +/** @var $linkCollection \Magento\Downloadable\Model\ResourceModel\Link\Collection */ +$linkCollection = \Magento\TestFramework\Helper\Bootstrap::getObjectManager()->create( + \Magento\Downloadable\Model\Link::class +)->getCollection()->addProductToFilter( + $downloadableProduct->getId() +)->addTitleToResult( + $downloadableProduct->getStoreId() +)->addPriceToResult( + $downloadableProduct->getStore()->getWebsiteId() +); + +/** @var $link \Magento\Downloadable\Model\Link */ +$links = $linkCollection->getItems(); +$requestInfo = [ + 'qty' => 1, + 'links' => array_keys($links) +]; + +$downloadableProductItem = $objectManager->create(\Magento\Sales\Model\Order\Item::class); +$downloadableProductItem->setProductId($downloadableProduct->getId()) + ->setQtyOrdered(1) + ->setBasePrice($downloadableProduct->getPrice()) + ->setPrice($downloadableProduct->getPrice()) + ->setRowTotal($downloadableProduct->getPrice()) + ->setProductType($downloadableProduct->getTypeId()) + ->setName($downloadableProduct->getName()) + ->setSku($downloadableProduct->getSku()) + ->setStoreId($downloadableProduct->getStoreId()) + ->setProductOptions(['info_buyRequest' => $requestInfo]); + +/** + * bundle product + */ +/** @var $typeInstance \Magento\Bundle\Model\Product\Type */ +$typeInstance = $bundleProduct->getTypeInstance(); +$typeInstance->setStoreFilter($bundleProduct->getStoreId(), $bundleProduct); +$optionCollection = $typeInstance->getOptionsCollection($bundleProduct); + +$bundleOptions = []; +$bundleOptionsQty = []; +foreach ($optionCollection as $option) { + /** @var $option \Magento\Bundle\Model\Option */ + $selectionsCollection = $typeInstance->getSelectionsCollection([$option->getId()], $bundleProduct); + if ($option->isMultiSelection()) { + $bundleOptions[$option->getId()] = array_column($selectionsCollection->toArray(), 'selection_id'); + } else { + $bundleOptions[$option->getId()] = $selectionsCollection->getFirstItem()->getSelectionId(); + } + $bundleOptionsQty[$option->getId()] = 1; +} + +$requestInfo = [ + 'product' => $bundleProduct->getId(), + 'bundle_option' => $bundleOptions, + 'bundle_option_qty' => $bundleOptionsQty, + 'qty' => 1, +]; +/** @var \Magento\Sales\Model\Order\Item $orderItem */ +$orderBundleItem = $objectManager->create(\Magento\Sales\Model\Order\Item::class); +$orderBundleItem->setProductId($bundleProduct->getId()); +$orderBundleItem->setQtyOrdered(1); +$orderBundleItem->setBasePrice($bundleProduct->getPrice()); +$orderBundleItem->setPrice($bundleProduct->getPrice()); +$orderBundleItem->setRowTotal($bundleProduct->getPrice()); +$orderBundleItem->setProductType($bundleProduct->getTypeId()); +$orderBundleItem->setProductOptions(['info_buyRequest' => $requestInfo]); + +/** @var \Magento\Sales\Model\Order $order */ +$order = $objectManager->create(\Magento\Sales\Model\Order::class); +$order->setIncrementId('100000001'); +$order->setState(\Magento\Sales\Model\Order::STATE_NEW); +$order->setStatus($order->getConfig()->getStateDefaultStatus(\Magento\Sales\Model\Order::STATE_NEW)); +$order->setCustomerIsGuest(false); +$order->setCustomerId($customer->getId()); +$order->setCustomerEmail($customer->getEmail()); +$order->setCustomerFirstname($customer->getName()); +$order->setCustomerLastname($customer->getLastname()); +$order->setBillingAddress($billingAddress); +$order->setShippingAddress($shippingAddress); +$order->setAddresses([$billingAddress, $shippingAddress]); +$order->setPayment($payment); +$order->addItem($simpleProductItem); +$order->addItem($orderConfigurableItem); +$order->addItem($virtualProductItem); +$order->addItem($orderBundleItem); +$order->addItem($downloadableProductItem); +$order->setStoreId($objectManager->get(\Magento\Store\Model\StoreManagerInterface::class)->getStore()->getId()); +$order->setSubtotal(100); +$order->setBaseSubtotal(100); +$order->setBaseGrandTotal(100); +$order->setCustomerId($customerIdFromFixture)->setCustomerIsGuest(false); + +$orderRepository = $objectManager->create(OrderRepositoryInterface::class); +$orderRepository->save($order); diff --git a/dev/tests/integration/testsuite/Magento/Sales/_files/order_with_different_types_of_product_rollback.php b/dev/tests/integration/testsuite/Magento/Sales/_files/order_with_different_types_of_product_rollback.php new file mode 100644 index 0000000000000..e7acd7961e85e --- /dev/null +++ b/dev/tests/integration/testsuite/Magento/Sales/_files/order_with_different_types_of_product_rollback.php @@ -0,0 +1,13 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ + +require __DIR__ . '/../../../Magento/Customer/_files/customer_rollback.php'; +require __DIR__ . '/../../../Magento/Catalog/_files/product_simple_without_custom_options_rollback.php'; +require __DIR__ . '/../../../Magento/ConfigurableProduct/_files/product_configurable_rollback.php'; +require __DIR__ . '/../../../Magento/GraphQl/Catalog/_files/virtual_product_rollback.php'; +require __DIR__ . '/../../../Magento/Bundle/_files/bundle_product_radio_required_option_rollback.php'; +require __DIR__ . '/../../../Magento/Downloadable/_files/product_downloadable_rollback.php'; +require 'default_rollback.php'; diff --git a/dev/tests/integration/testsuite/Magento/Sales/_files/order_with_grouped_product.php b/dev/tests/integration/testsuite/Magento/Sales/_files/order_with_grouped_product.php new file mode 100644 index 0000000000000..0cdc2d10e6f06 --- /dev/null +++ b/dev/tests/integration/testsuite/Magento/Sales/_files/order_with_grouped_product.php @@ -0,0 +1,65 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +declare(strict_types=1); + +use Magento\Catalog\Api\ProductRepositoryInterface; +use Magento\Sales\Api\OrderRepositoryInterface; +use Magento\Sales\Model\Order; +use Magento\Sales\Model\Order\Address as OrderAddress; +use Magento\Sales\Model\Order\Item as OrderItem; +use Magento\Sales\Model\Order\Payment; +use Magento\Store\Model\StoreManagerInterface; +use Magento\TestFramework\Helper\Bootstrap; + +require 'default_rollback.php'; +require __DIR__ . '/../../../Magento/GroupedProduct/_files/product_grouped_with_simple.php'; +$addressData = include __DIR__ . '/address_data.php'; + +$objectManager = Bootstrap::getObjectManager(); +$billingAddress = $objectManager->create(OrderAddress::class, ['data' => $addressData]); +$billingAddress->setAddressType('billing'); + +$shippingAddress = clone $billingAddress; +$shippingAddress->setId(null)->setAddressType('shipping'); + +$payment = $objectManager->create(Payment::class); +$payment->setMethod('checkmo') + ->setAdditionalInformation('last_trans_id', '11122') + ->setAdditionalInformation( + 'metadata', + [ + 'type' => 'free', + 'fraudulent' => false, + ] + ); +$product = $objectManager->get(ProductRepositoryInterface::class)->get('simple_11'); +$orderItem = $objectManager->create(OrderItem::class); +$orderItem->setProductId($product->getId()) + ->setQtyOrdered(2) + ->setBasePrice($product->getPrice()) + ->setPrice($product->getPrice()) + ->setRowTotal($product->getPrice()) + ->setProductType('grouped') + ->setName($product->getName()) + ->setSku($product->getSku()); + +$order = $objectManager->create(Order::class); +$order->setIncrementId('100000002') + ->setState(Order::STATE_PROCESSING) + ->setStatus($order->getConfig()->getStateDefaultStatus(Order::STATE_PROCESSING)) + ->setSubtotal(100) + ->setGrandTotal(100) + ->setBaseSubtotal(100) + ->setBaseGrandTotal(100) + ->setCustomerIsGuest(true) + ->setCustomerEmail('customer@null.com') + ->setBillingAddress($billingAddress) + ->setShippingAddress($shippingAddress) + ->setStoreId($objectManager->get(StoreManagerInterface::class)->getStore()->getId()) + ->addItem($orderItem) + ->setPayment($payment); +$orderRepository = $objectManager->create(OrderRepositoryInterface::class); +$orderRepository->save($order); diff --git a/dev/tests/integration/testsuite/Magento/Sales/_files/order_with_grouped_product_rollback.php b/dev/tests/integration/testsuite/Magento/Sales/_files/order_with_grouped_product_rollback.php new file mode 100644 index 0000000000000..1fb4b4636ab29 --- /dev/null +++ b/dev/tests/integration/testsuite/Magento/Sales/_files/order_with_grouped_product_rollback.php @@ -0,0 +1,8 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +declare(strict_types=1); + +require 'default_rollback.php'; diff --git a/dev/tests/integration/testsuite/Magento/Sales/_files/order_with_product_out_of_stock.php b/dev/tests/integration/testsuite/Magento/Sales/_files/order_with_product_out_of_stock.php new file mode 100644 index 0000000000000..65ef24f5a03d2 --- /dev/null +++ b/dev/tests/integration/testsuite/Magento/Sales/_files/order_with_product_out_of_stock.php @@ -0,0 +1,30 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ + +use Magento\TestFramework\Helper\Bootstrap; + +require __DIR__ . '/../../../Magento/Sales/_files/customer_order_item_with_product_and_custom_options.php'; + +$customerIdFromFixture = 1; +/** @var $order \Magento\Sales\Model\Order */ +$order->setCustomerId($customerIdFromFixture)->setCustomerIsGuest(false)->save(); + +// load product and set it out of stock +/** @var \Magento\Catalog\Api\ProductRepositoryInterface $repository */ +$productRepository = Bootstrap::getObjectManager()->create(\Magento\Catalog\Api\ProductRepositoryInterface::class); +$productSku = 'simple'; +/** @var \Magento\Catalog\Model\Product $product */ +$product = $productRepository->get($productSku); +// set product as out of stock +$product->setStockData( + [ + 'use_config_manage_stock' => 1, + 'qty' => 0, + 'is_qty_decimal' => 0, + 'is_in_stock' => 0, + ] +); +$productRepository->save($product); diff --git a/dev/tests/integration/testsuite/Magento/Sales/_files/order_with_product_out_of_stock_rollback.php b/dev/tests/integration/testsuite/Magento/Sales/_files/order_with_product_out_of_stock_rollback.php new file mode 100644 index 0000000000000..9e9e808c853d1 --- /dev/null +++ b/dev/tests/integration/testsuite/Magento/Sales/_files/order_with_product_out_of_stock_rollback.php @@ -0,0 +1,7 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ + +require __DIR__ . '/../../../Magento/Sales/_files/customer_order_item_with_product_and_custom_options_rollback.php'; diff --git a/dev/tests/integration/testsuite/Magento/Sales/_files/order_with_simple_product.php b/dev/tests/integration/testsuite/Magento/Sales/_files/order_with_simple_product.php new file mode 100644 index 0000000000000..1e8e9beccc87f --- /dev/null +++ b/dev/tests/integration/testsuite/Magento/Sales/_files/order_with_simple_product.php @@ -0,0 +1,99 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ + +use Magento\Sales\Api\OrderRepositoryInterface; + +require __DIR__ . '/../../../Magento/Customer/_files/customer.php'; +require __DIR__ . '/../../../Magento/Catalog/_files/product_simple.php'; +require __DIR__ . '/../../../Magento/Catalog/_files/product_simple_without_custom_options.php'; + +/** \Magento\Customer\Model\Customer $customer */ +$addressData = include __DIR__ . '/../../../Magento/Sales/_files/address_data.php'; +$billingAddress = $objectManager->create(\Magento\Sales\Model\Order\Address::class, ['data' => $addressData]); +$billingAddress->setAddressType('billing'); +$shippingAddress = clone $billingAddress; +$shippingAddress->setId(null)->setAddressType('shipping'); + +$objectManager = \Magento\TestFramework\Helper\Bootstrap::getObjectManager(); +$payment = $objectManager->create(\Magento\Sales\Model\Order\Payment::class); +$payment->setMethod('checkmo'); +$customerIdFromFixture = 1; + +$optionValuesByType = [ + 'field' => 'Test value', + 'date_time' => [ + 'year' => '2015', + 'month' => '9', + 'day' => '9', + 'hour' => '2', + 'minute' => '2', + 'day_part' => 'am', + 'date_internal' => '', + ], + 'drop_down' => '3-1-select', + 'radio' => '4-1-radio', +]; + +$requestInfo = ['options' => [], 'qty' => 1]; +$productOptions = $product->getOptions(); +foreach ($productOptions as $option) { + $requestInfo['options'][$option->getOptionId()] = $optionValuesByType[$option->getType()]; +} + +/** @var \Magento\Sales\Model\Order\Item $orderItem */ +$orderItem = $objectManager->create(\Magento\Sales\Model\Order\Item::class); +$orderItem->setProductId($product->getId()); +$orderItem->setQtyOrdered(1); +$orderItem->setBasePrice($product->getPrice()); +$orderItem->setPrice($product->getPrice()); +$orderItem->setRowTotal($product->getPrice()); +$orderItem->setProductType($product->getTypeId()); +$orderItem->setProductOptions(['info_buyRequest' => $requestInfo]); +$orderItem->setName($product->getName()); +$orderItem->setSku($product->getSku()); +$orderItem->setStoreId(0); +// create second order item + +$orderItem2 = $objectManager->create(\Magento\Sales\Model\Order\Item::class); +$requestInfo = [ + 'qty' => 1 +]; +$orderItem2->setProductId($secondProduct->getId()) + ->setQtyOrdered(1) + ->setBasePrice($secondProduct->getPrice()) + ->setPrice($secondProduct->getPrice()) + ->setRowTotal($secondProduct->getPrice()) + ->setProductType($secondProduct->getTypeId()) + ->setName($secondProduct->getName()) + ->setSku($secondProduct->getSku()) + ->setStoreId(0) + ->setProductId($secondProduct->getId()) + ->setSku($secondProductSku) + ->setProductOptions(['info_buyRequest' => $requestInfo]); + +/** @var \Magento\Sales\Model\Order $order */ +$order = $objectManager->create(\Magento\Sales\Model\Order::class); +$order->setIncrementId('100001001'); +$order->setState(\Magento\Sales\Model\Order::STATE_NEW); +$order->setStatus($order->getConfig()->getStateDefaultStatus(\Magento\Sales\Model\Order::STATE_NEW)); +$order->setCustomerIsGuest(false); +$order->setCustomerId($customer->getId()); +$order->setCustomerEmail($customer->getEmail()); +$order->setCustomerFirstname($customer->getName()); +$order->setCustomerLastname($customer->getLastname()); +$order->setBillingAddress($billingAddress); +$order->setShippingAddress($shippingAddress); +$order->setAddresses([$billingAddress, $shippingAddress]); +$order->setPayment($payment); +$order->addItem($orderItem); +$order->addItem($orderItem2); +$order->setStoreId($objectManager->get(\Magento\Store\Model\StoreManagerInterface::class)->getStore()->getId()); +$order->setSubtotal(100); +$order->setBaseSubtotal(100); +$order->setBaseGrandTotal(100); + +$orderRepository = $objectManager->create(OrderRepositoryInterface::class); +$orderRepository->save($order); diff --git a/dev/tests/integration/testsuite/Magento/Sales/_files/order_with_simple_product_rollback.php b/dev/tests/integration/testsuite/Magento/Sales/_files/order_with_simple_product_rollback.php new file mode 100644 index 0000000000000..e242a8035250a --- /dev/null +++ b/dev/tests/integration/testsuite/Magento/Sales/_files/order_with_simple_product_rollback.php @@ -0,0 +1,12 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ + +use Magento\Sales\Api\OrderRepositoryInterface; + +require __DIR__ . '/../../../Magento/Customer/_files/customer_rollback.php'; +require __DIR__ . '/../../../Magento/Catalog/_files/product_simple_rollback.php'; +require __DIR__ . '/../../../Magento/Catalog/_files/product_simple_without_custom_options_rollback.php'; +require 'default_rollback.php'; diff --git a/dev/tests/integration/testsuite/Magento/Sales/_files/order_with_two_configurable_variations.php b/dev/tests/integration/testsuite/Magento/Sales/_files/order_with_two_configurable_variations.php new file mode 100644 index 0000000000000..9c639612348a4 --- /dev/null +++ b/dev/tests/integration/testsuite/Magento/Sales/_files/order_with_two_configurable_variations.php @@ -0,0 +1,94 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ + +use Magento\Sales\Api\OrderRepositoryInterface; +use Magento\TestFramework\Helper\Bootstrap; + +$objectManager = Bootstrap::getObjectManager(); + +require __DIR__ . '/../../../Magento/Customer/_files/customer.php'; +require __DIR__ . '/../../../Magento/ConfigurableProduct/_files/product_configurable.php'; +$configurableProduct = $productRepository->get($product->getSku()); + +/** \Magento\Customer\Model\Customer $customer */ +$addressData = include __DIR__ . '/../../../Magento/Sales/_files/address_data.php'; +$billingAddress = $objectManager->create(\Magento\Sales\Model\Order\Address::class, ['data' => $addressData]); +$billingAddress->setAddressType('billing'); +$shippingAddress = clone $billingAddress; +$shippingAddress->setId(null)->setAddressType('shipping'); + +$objectManager = \Magento\TestFramework\Helper\Bootstrap::getObjectManager(); +$payment = $objectManager->create(\Magento\Sales\Model\Order\Payment::class); +$payment->setMethod('checkmo'); +$customerIdFromFixture = 1; + +// configurable product +/** @var \Magento\Eav\Model\Config $eavConfig */ +$eavConfig = $objectManager->get(\Magento\Eav\Model\Config::class); +/** @var $attribute \Magento\Catalog\Model\ResourceModel\Eav\Attribute */ +$attribute = $eavConfig->getAttribute('catalog_product', 'test_configurable'); + +/** @var $options \Magento\Eav\Model\ResourceModel\Entity\Attribute\Option\Collection */ +$options = $objectManager->create(\Magento\Eav\Model\ResourceModel\Entity\Attribute\Option\Collection::class); +$configurableOptions = $options->setAttributeFilter($attribute->getId())->getItems(); +foreach ($configurableOptions as $option) { + $requestInfo[] = [ + 'qty' => 1, + 'super_attribute' => [ + $attribute->getId() => $option->getId(), + ], + ]; +} +$qtyOrdered = 1; +/** @var \Magento\Sales\Model\Order\Item $orderItem */ +$orderConfigurableItem = $objectManager->create(\Magento\Sales\Model\Order\Item::class); +$orderConfigurableItem->setProductId($configurableProduct->getId())->setQtyOrdered($qtyOrdered); +$orderConfigurableItem->setBasePrice($configurableProduct->getPrice()); +$orderConfigurableItem->setPrice($configurableProduct->getPrice()); +$orderConfigurableItem->setRowTotal($configurableProduct->getPrice()); +$orderConfigurableItem->setParentItemId(null); +$orderConfigurableItem->setProductType('configurable'); +$configurableVariations = []; +$producLinks = array_values($configurableProduct->getExtensionAttributes()->getConfigurableProductLinks()); +foreach ($producLinks as $key => $variationId) { + $simpleProductId = current($configurableProduct->getExtensionAttributes()->getConfigurableProductLinks()); + + /** @var \Magento\Catalog\Api\Data\ProductInterface $simpleProduct */ + $simpleProduct = $productRepository->getById($simpleProductId); + + $info = $requestInfo[$key]; + $info['product'] = $simpleProductId; + $info['item'] = $simpleProduct; + + $orderConfigurableParentItem = clone $orderConfigurableItem; + $orderConfigurableParentItem->setProductOptions(['info_buyRequest' => $info]); + $configurableItems[] = $orderConfigurableParentItem; +} + +/** @var \Magento\Sales\Model\Order $order */ +$order = $objectManager->create(\Magento\Sales\Model\Order::class); +$order->setIncrementId('100000001'); +$order->setState(\Magento\Sales\Model\Order::STATE_NEW); +$order->setStatus($order->getConfig()->getStateDefaultStatus(\Magento\Sales\Model\Order::STATE_NEW)); +$order->setCustomerIsGuest(false); +$order->setCustomerId($customer->getId()); +$order->setCustomerEmail($customer->getEmail()); +$order->setCustomerFirstname($customer->getName()); +$order->setCustomerLastname($customer->getLastname()); +$order->setBillingAddress($billingAddress); +$order->setShippingAddress($shippingAddress); +$order->setAddresses([$billingAddress, $shippingAddress]); +$order->setPayment($payment); +$order->setStoreId($objectManager->get(\Magento\Store\Model\StoreManagerInterface::class)->getStore()->getId()); +$order->setSubtotal(100); +$order->setBaseSubtotal(100); +$order->setBaseGrandTotal(100); +$order->setCustomerId($customerIdFromFixture)->setCustomerIsGuest(false); +foreach ($configurableItems as $item) { + $order->addItem($item); +} +$orderRepository = $objectManager->create(OrderRepositoryInterface::class); +$orderRepository->save($order); diff --git a/dev/tests/integration/testsuite/Magento/Sales/_files/order_with_two_configurable_variations_rollback.php b/dev/tests/integration/testsuite/Magento/Sales/_files/order_with_two_configurable_variations_rollback.php new file mode 100644 index 0000000000000..0950ba3579a7e --- /dev/null +++ b/dev/tests/integration/testsuite/Magento/Sales/_files/order_with_two_configurable_variations_rollback.php @@ -0,0 +1,9 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ + +require __DIR__ . '/../../../Magento/Customer/_files/customer_rollback.php'; +require __DIR__ . '/../../../Magento/ConfigurableProduct/_files/product_configurable_rollback.php'; +require 'default_rollback.php'; diff --git a/dev/tests/integration/testsuite/Magento/Sales/_files/order_with_two_order_items_with_simple_product.php b/dev/tests/integration/testsuite/Magento/Sales/_files/order_with_two_order_items_with_simple_product.php new file mode 100644 index 0000000000000..48f5d5dd99f80 --- /dev/null +++ b/dev/tests/integration/testsuite/Magento/Sales/_files/order_with_two_order_items_with_simple_product.php @@ -0,0 +1,88 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +declare(strict_types=1); + +use Magento\Sales\Api\Data\OrderAddressInterfaceFactory; +use Magento\Sales\Api\Data\OrderInterfaceFactory; +use Magento\Sales\Api\Data\OrderItemInterfaceFactory; +use Magento\Sales\Api\Data\OrderPaymentInterfaceFactory; +use Magento\Sales\Api\OrderRepositoryInterface; +use Magento\Sales\Model\Order; +use Magento\Sales\Model\Order\Address; +use Magento\Store\Model\StoreManagerInterface; + +require __DIR__ . '/../../../Magento/Customer/_files/customer.php'; +require __DIR__ . '/../../../Magento/Catalog/_files/products.php'; + +$addressData = include __DIR__ . '/address_data.php'; + +/** @var OrderAddressInterfaceFactory $addressFactory */ +$addressFactory = $objectManager->get(OrderAddressInterfaceFactory::class); +/** @var OrderPaymentInterfaceFactory $paymentFactory */ +$paymentFactory = $objectManager->get(OrderPaymentInterfaceFactory::class); +/** @var OrderInterfaceFactory $orderFactory */ +$orderFactory = $objectManager->get(OrderInterfaceFactory::class); +/** @var OrderItemInterfaceFactory $orderItemFactory */ +$orderItemFactory = $objectManager->get(OrderItemInterfaceFactory::class); +/** @var StoreManagerInterface $storeManager */ +$storeManager = $objectManager->get(StoreManagerInterface::class); +/** @var OrderRepositoryInterface $orderRepository */ +$orderRepository = $objectManager->get(OrderRepositoryInterface::class); + +$billingAddress = $addressFactory->create(['data' => $addressData]); +$billingAddress->setAddressType(Address::TYPE_BILLING); +$shippingAddress = $addressFactory->create(['data' => $addressData]); +$shippingAddress->setAddressType(Address::TYPE_SHIPPING); +$payment = $paymentFactory->create(); +$payment->setMethod('checkmo')->setAdditionalInformation( + [ + 'last_trans_id' => '11122', + 'metadata' => [ + 'type' => 'free', + 'fraudulent' => false, + ] + ] +); + +$defaultStoreId = $storeManager->getStore('default')->getId(); +$order = $orderFactory->create(); +$order->setIncrementId('100000001') + ->setState(Order::STATE_PROCESSING) + ->setStatus($order->getConfig()->getStateDefaultStatus(Order::STATE_PROCESSING)) + ->setSubtotal(20) + ->setGrandTotal(20) + ->setBaseSubtotal(20) + ->setBaseGrandTotal(20) + ->setCustomerIsGuest(false) + ->setCustomerId($customer->getId()) + ->setCustomerEmail($customer->getEmail()) + ->setBillingAddress($billingAddress) + ->setShippingAddress($shippingAddress) + ->setStoreId($defaultStoreId) + ->setPayment($payment); + +$orderItem = $orderItemFactory->create(); +$orderItem->setProductId($product->getId()) + ->setQtyOrdered(5) + ->setBasePrice($product->getPrice()) + ->setPrice($product->getPrice()) + ->setRowTotal($product->getPrice()) + ->setProductType($product->getTypeId()) + ->setName($product->getName()) + ->setSku($product->getSku()); +$order->addItem($orderItem); + +$orderItem = $orderItemFactory->create(); +$orderItem->setProductId($customDesignProduct->getId()) + ->setQtyOrdered(5) + ->setBasePrice($customDesignProduct->getPrice()) + ->setPrice($customDesignProduct->getPrice()) + ->setRowTotal($customDesignProduct->getPrice()) + ->setProductType($customDesignProduct->getTypeId()) + ->setName($customDesignProduct->getName()) + ->setSku($customDesignProduct->getSku()); +$order->addItem($orderItem); +$orderRepository->save($order); diff --git a/dev/tests/integration/testsuite/Magento/Sales/_files/order_with_two_order_items_with_simple_product_rollback.php b/dev/tests/integration/testsuite/Magento/Sales/_files/order_with_two_order_items_with_simple_product_rollback.php new file mode 100644 index 0000000000000..b76b5178d3d25 --- /dev/null +++ b/dev/tests/integration/testsuite/Magento/Sales/_files/order_with_two_order_items_with_simple_product_rollback.php @@ -0,0 +1,36 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +declare(strict_types=1); + +use Magento\Framework\Registry; +use Magento\Sales\Api\Data\OrderInterface; +use Magento\Sales\Api\OrderRepositoryInterface; +use Magento\Sales\Model\ResourceModel\Order\CollectionFactory; +use Magento\TestFramework\Helper\Bootstrap; + +$objectManager = Bootstrap::getObjectManager(); +/** @var Registry $registry */ +$registry = $objectManager->get(Registry::class); +/** @var OrderRepositoryInterface $orderRepository */ +$orderRepository = $objectManager->get(OrderRepositoryInterface::class); +/** @var CollectionFactory $orderCollectionFactory */ +$orderCollectionFactory = $objectManager->get(CollectionFactory::class); + +$registry->unregister('isSecureArea'); +$registry->register('isSecureArea', true); +$order = $orderCollectionFactory->create() + ->addFieldToFilter(OrderInterface::INCREMENT_ID, '100000001') + ->setPageSize(1) + ->getFirstItem(); +if ($order->getId()) { + $orderRepository->delete($order); +} + +$registry->unregister('isSecureArea'); +$registry->register('isSecureArea', false); + +require __DIR__ . '/../../../Magento/Customer/_files/customer_rollback.php'; +require __DIR__ . '/../../../Magento/Catalog/_files/products_rollback.php'; diff --git a/dev/tests/integration/testsuite/Magento/Sales/_files/order_with_two_simple_products.php b/dev/tests/integration/testsuite/Magento/Sales/_files/order_with_two_simple_products.php new file mode 100644 index 0000000000000..ae99114490974 --- /dev/null +++ b/dev/tests/integration/testsuite/Magento/Sales/_files/order_with_two_simple_products.php @@ -0,0 +1,98 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ + +use Magento\Sales\Api\OrderRepositoryInterface; + +require __DIR__ . '/../../../Magento/Customer/_files/customer.php'; +require __DIR__ . '/../../../Magento/Catalog/_files/product_simple.php'; +require __DIR__ . '/../../../Magento/Catalog/_files/product_simple_without_custom_options.php'; + +/** \Magento\Customer\Model\Customer $customer */ +$addressData = include __DIR__ . '/../../../Magento/Sales/_files/address_data.php'; +$billingAddress = $objectManager->create(\Magento\Sales\Model\Order\Address::class, ['data' => $addressData]); +$billingAddress->setAddressType('billing'); +$shippingAddress = clone $billingAddress; +$shippingAddress->setId(null)->setAddressType('shipping'); + +$objectManager = \Magento\TestFramework\Helper\Bootstrap::getObjectManager(); +$payment = $objectManager->create(\Magento\Sales\Model\Order\Payment::class); +$payment->setMethod('checkmo'); +$customerIdFromFixture = 1; + +$optionValuesByType = [ + 'field' => 'Test value', + 'date_time' => [ + 'year' => '2015', + 'month' => '9', + 'day' => '9', + 'hour' => '2', + 'minute' => '2', + 'day_part' => 'am', + 'date_internal' => '', + ], + 'drop_down' => '3-1-select', + 'radio' => '4-1-radio', +]; + +$requestInfo = ['options' => [], 'qty' => 1]; +$productOptions = $product->getOptions(); +foreach ($productOptions as $option) { + $requestInfo['options'][$option->getOptionId()] = $optionValuesByType[$option->getType()]; +} + +/** @var \Magento\Sales\Model\Order\Item $orderItem */ +$orderItem = $objectManager->create(\Magento\Sales\Model\Order\Item::class); +$orderItem->setProductId($product->getId()); +$orderItem->setQtyOrdered(1); +$orderItem->setBasePrice($product->getPrice()); +$orderItem->setPrice($product->getPrice()); +$orderItem->setRowTotal($product->getPrice()); +$orderItem->setProductType($product->getTypeId()); +$orderItem->setProductOptions(['info_buyRequest' => $requestInfo]); +$orderItem->setName($product->getName()); +$orderItem->setSku($product->getSku()); +$orderItem->setStoreId(0); +// create second order item + +$orderItem2 = $objectManager->create(\Magento\Sales\Model\Order\Item::class); +$requestInfo = [ + 'qty' => 1 +]; +$orderItem2->setProductId($secondProduct->getId()) + ->setQtyOrdered(1) + ->setBasePrice($secondProduct->getPrice()) + ->setPrice($secondProduct->getPrice()) + ->setRowTotal($secondProduct->getPrice()) + ->setProductType($secondProduct->getTypeId()) + ->setName($secondProduct->getName()) + ->setSku($secondProduct->getSku()) + ->setStoreId(0) + ->setSku($secondProductSku) + ->setProductOptions(['info_buyRequest' => $requestInfo]); + +/** @var \Magento\Sales\Model\Order $order */ +$order = $objectManager->create(\Magento\Sales\Model\Order::class); +$order->setIncrementId('100000001'); +$order->setState(\Magento\Sales\Model\Order::STATE_NEW); +$order->setStatus($order->getConfig()->getStateDefaultStatus(\Magento\Sales\Model\Order::STATE_NEW)); +$order->setCustomerIsGuest(false); +$order->setCustomerId($customer->getId()); +$order->setCustomerEmail($customer->getEmail()); +$order->setCustomerFirstname($customer->getName()); +$order->setCustomerLastname($customer->getLastname()); +$order->setBillingAddress($billingAddress); +$order->setShippingAddress($shippingAddress); +$order->setAddresses([$billingAddress, $shippingAddress]); +$order->setPayment($payment); +$order->addItem($orderItem); +$order->addItem($orderItem2); +$order->setStoreId($objectManager->get(\Magento\Store\Model\StoreManagerInterface::class)->getStore()->getId()); +$order->setSubtotal(100); +$order->setBaseSubtotal(100); +$order->setBaseGrandTotal(100); + +$orderRepository = $objectManager->create(OrderRepositoryInterface::class); +$orderRepository->save($order); diff --git a/dev/tests/integration/testsuite/Magento/Sales/_files/order_with_two_simple_products_qty_10.php b/dev/tests/integration/testsuite/Magento/Sales/_files/order_with_two_simple_products_qty_10.php new file mode 100644 index 0000000000000..40e5a168655a2 --- /dev/null +++ b/dev/tests/integration/testsuite/Magento/Sales/_files/order_with_two_simple_products_qty_10.php @@ -0,0 +1,99 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ + +use Magento\Sales\Api\OrderRepositoryInterface; + +require __DIR__ . '/../../../Magento/Customer/_files/customer.php'; +require __DIR__ . '/../../../Magento/Catalog/_files/product_simple.php'; +require __DIR__ . '/../../../Magento/Catalog/_files/product_simple_without_custom_options.php'; + +/** \Magento\Customer\Model\Customer $customer */ +$addressData = include __DIR__ . '/../../../Magento/Sales/_files/address_data.php'; +$billingAddress = $objectManager->create(\Magento\Sales\Model\Order\Address::class, ['data' => $addressData]); +$billingAddress->setAddressType('billing'); +$shippingAddress = clone $billingAddress; +$shippingAddress->setId(null)->setAddressType('shipping'); + +$objectManager = \Magento\TestFramework\Helper\Bootstrap::getObjectManager(); +$payment = $objectManager->create(\Magento\Sales\Model\Order\Payment::class); +$payment->setMethod('checkmo'); +$customerIdFromFixture = 1; + +$optionValuesByType = [ + 'field' => 'Test value', + 'date_time' => [ + 'year' => '2015', + 'month' => '9', + 'day' => '9', + 'hour' => '2', + 'minute' => '2', + 'day_part' => 'am', + 'date_internal' => '', + ], + 'drop_down' => '3-1-select', + 'radio' => '4-1-radio', +]; + +$requestInfo = ['options' => [], 'qty' => 1]; +$productOptions = $product->getOptions(); +foreach ($productOptions as $option) { + $requestInfo['options'][$option->getOptionId()] = $optionValuesByType[$option->getType()]; +} + +/** @var \Magento\Sales\Model\Order\Item $orderItem */ +$orderItem = $objectManager->create(\Magento\Sales\Model\Order\Item::class); +$orderItem->setProductId($product->getId()); +$orderItem->setQtyOrdered(10); +$orderItem->setBasePrice($product->getPrice()); +$orderItem->setPrice($product->getPrice()); +$orderItem->setRowTotal($product->getPrice()); +$orderItem->setProductType($product->getTypeId()); +$orderItem->setProductOptions(['info_buyRequest' => $requestInfo]); +$orderItem->setName($product->getName()); +$orderItem->setSku($product->getSku()); +$orderItem->setStoreId(0); +// create second order item + +$orderItem2 = $objectManager->create(\Magento\Sales\Model\Order\Item::class); +$requestInfo = [ + 'qty' => 1 +]; +$orderItem2->setProductId($secondProduct->getId()) + ->setQtyOrdered(10) + ->setBasePrice($secondProduct->getPrice()) + ->setPrice($secondProduct->getPrice()) + ->setRowTotal($secondProduct->getPrice()) + ->setProductType($secondProduct->getTypeId()) + ->setName($secondProduct->getName()) + ->setSku($secondProduct->getSku()) + ->setStoreId(0) + ->setProductId($secondProduct->getId()) + ->setSku($secondProductSku) + ->setProductOptions(['info_buyRequest' => $requestInfo]); + +/** @var \Magento\Sales\Model\Order $order */ +$order = $objectManager->create(\Magento\Sales\Model\Order::class); +$order->setIncrementId('100000001'); +$order->setState(\Magento\Sales\Model\Order::STATE_NEW); +$order->setStatus($order->getConfig()->getStateDefaultStatus(\Magento\Sales\Model\Order::STATE_NEW)); +$order->setCustomerIsGuest(false); +$order->setCustomerId($customer->getId()); +$order->setCustomerEmail($customer->getEmail()); +$order->setCustomerFirstname($customer->getName()); +$order->setCustomerLastname($customer->getLastname()); +$order->setBillingAddress($billingAddress); +$order->setShippingAddress($shippingAddress); +$order->setAddresses([$billingAddress, $shippingAddress]); +$order->setPayment($payment); +$order->addItem($orderItem); +$order->addItem($orderItem2); +$order->setStoreId($objectManager->get(\Magento\Store\Model\StoreManagerInterface::class)->getStore()->getId()); +$order->setSubtotal(100); +$order->setBaseSubtotal(100); +$order->setBaseGrandTotal(100); + +$orderRepository = $objectManager->create(OrderRepositoryInterface::class); +$orderRepository->save($order); diff --git a/dev/tests/integration/testsuite/Magento/Sales/_files/order_with_two_simple_products_qty_10_rollback.php b/dev/tests/integration/testsuite/Magento/Sales/_files/order_with_two_simple_products_qty_10_rollback.php new file mode 100644 index 0000000000000..4980c3ca265df --- /dev/null +++ b/dev/tests/integration/testsuite/Magento/Sales/_files/order_with_two_simple_products_qty_10_rollback.php @@ -0,0 +1,10 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ + +require __DIR__ . '/../../../Magento/Customer/_files/customer_rollback.php'; +require __DIR__ . '/../../../Magento/Catalog/_files/product_simple_rollback.php'; +require __DIR__ . '/../../../Magento/Catalog/_files/product_simple_without_custom_options_rollback.php'; +require 'default_rollback.php'; diff --git a/dev/tests/integration/testsuite/Magento/Sales/_files/order_with_two_simple_products_rollback.php b/dev/tests/integration/testsuite/Magento/Sales/_files/order_with_two_simple_products_rollback.php new file mode 100644 index 0000000000000..4980c3ca265df --- /dev/null +++ b/dev/tests/integration/testsuite/Magento/Sales/_files/order_with_two_simple_products_rollback.php @@ -0,0 +1,10 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ + +require __DIR__ . '/../../../Magento/Customer/_files/customer_rollback.php'; +require __DIR__ . '/../../../Magento/Catalog/_files/product_simple_rollback.php'; +require __DIR__ . '/../../../Magento/Catalog/_files/product_simple_without_custom_options_rollback.php'; +require 'default_rollback.php'; diff --git a/dev/tests/integration/testsuite/Magento/Sales/_files/order_with_zero_qty_product.php b/dev/tests/integration/testsuite/Magento/Sales/_files/order_with_zero_qty_product.php new file mode 100644 index 0000000000000..cdcda1d5dc2c4 --- /dev/null +++ b/dev/tests/integration/testsuite/Magento/Sales/_files/order_with_zero_qty_product.php @@ -0,0 +1,32 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ + +use Magento\CatalogInventory\Model\Stock; +use Magento\TestFramework\Helper\Bootstrap; + +require __DIR__ . '/../../../Magento/Sales/_files/order_with_two_simple_products.php'; + +$customerIdFromFixture = 1; +/** @var $order \Magento\Sales\Model\Order */ +$order->setCustomerId($customerIdFromFixture)->setCustomerIsGuest(false)->save(); + +// load product and set qty to 0 +/** @var \Magento\Catalog\Api\ProductRepositoryInterface $repository */ +$productRepository = Bootstrap::getObjectManager()->create(\Magento\Catalog\Api\ProductRepositoryInterface::class); +$productSku = 'simple'; +/** @var \Magento\Catalog\Model\Product $product */ +$product = $productRepository->get($productSku); +// set product qty to zero +$product->setStockData( + [ + 'use_config_manage_stock' => 1, + 'qty' => 0, + 'is_in_stock' => 1, + 'use_config_backorders' => 0, + 'backorders' => Stock::BACKORDERS_YES_NONOTIFY, + ] +); +$productRepository->save($product); diff --git a/dev/tests/integration/testsuite/Magento/Sales/_files/order_with_zero_qty_product_rollback.php b/dev/tests/integration/testsuite/Magento/Sales/_files/order_with_zero_qty_product_rollback.php new file mode 100644 index 0000000000000..34cded299350d --- /dev/null +++ b/dev/tests/integration/testsuite/Magento/Sales/_files/order_with_zero_qty_product_rollback.php @@ -0,0 +1,7 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ + +require __DIR__ . '/../../../Magento/Sales/_files/order_with_two_simple_products_rollback.php'; diff --git a/dev/tests/integration/testsuite/Magento/Sales/_files/orders_with_customer.php b/dev/tests/integration/testsuite/Magento/Sales/_files/orders_with_customer.php index 1a0a94b0ca951..4c962d5527c49 100644 --- a/dev/tests/integration/testsuite/Magento/Sales/_files/orders_with_customer.php +++ b/dev/tests/integration/testsuite/Magento/Sales/_files/orders_with_customer.php @@ -24,7 +24,6 @@ 'base_grand_total' => 120.00, 'store_id' => 1, 'website_id' => 1, - 'payment' => $payment ], [ 'increment_id' => '100000003', @@ -36,7 +35,6 @@ 'total_paid' => 130.00, 'store_id' => 0, 'website_id' => 0, - 'payment' => $payment ], [ 'increment_id' => '100000004', @@ -47,7 +45,6 @@ 'subtotal' => 140.00, 'store_id' => 1, 'website_id' => 1, - 'payment' => $payment ], [ 'increment_id' => '100000005', @@ -59,7 +56,6 @@ 'total_paid' => 150.00, 'store_id' => 1, 'website_id' => 1, - 'payment' => $payment ], [ 'increment_id' => '100000006', @@ -71,7 +67,6 @@ 'total_paid' => 160.00, 'store_id' => 1, 'website_id' => 1, - 'payment' => $payment ], ]; @@ -79,6 +74,8 @@ $orderRepository = $objectManager->create(OrderRepositoryInterface::class); /** @var array $orderData */ foreach ($orders as $orderData) { + $newPayment = clone $payment; + $newPayment->setId(null); /** @var $order \Magento\Sales\Model\Order */ $order = \Magento\TestFramework\Helper\Bootstrap::getObjectManager()->create( \Magento\Sales\Model\Order::class @@ -108,7 +105,8 @@ ->setCustomerId(1) ->setCustomerEmail('customer@example.com') ->setBillingAddress($billingAddress) - ->setShippingAddress($shippingAddress); + ->setShippingAddress($shippingAddress) + ->setPayment($newPayment); $orderRepository->save($order); } diff --git a/dev/tests/integration/testsuite/Magento/Sales/_files/quote_with_multiple_products.php b/dev/tests/integration/testsuite/Magento/Sales/_files/quote_with_multiple_products.php new file mode 100644 index 0000000000000..4164a17ea3a86 --- /dev/null +++ b/dev/tests/integration/testsuite/Magento/Sales/_files/quote_with_multiple_products.php @@ -0,0 +1,137 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ + +use Magento\Catalog\Api\ProductRepositoryInterface; +use Magento\Catalog\Model\Product; +use Magento\Catalog\Model\Product\Attribute\Source\Status; +use Magento\Catalog\Model\Product\Visibility; +use Magento\Quote\Api\CartRepositoryInterface; +use Magento\Quote\Model\Quote; +use Magento\Quote\Model\Quote\Address; +use Magento\Quote\Model\QuoteIdMask; +use Magento\Quote\Model\QuoteIdMaskFactory; +use Magento\Store\Model\StoreManagerInterface; +use Magento\TestFramework\Helper\Bootstrap; +use Magento\CatalogInventory\Api\Data\StockItemInterface; + +Bootstrap::getInstance()->loadArea('frontend'); +/** @var ObjectManager $objectManager */ +$objectManager = Bootstrap::getObjectManager(); +$productRepository = $objectManager + ->create(ProductRepositoryInterface::class); +$firstProduct = $objectManager->create(Product::class); +$firstProduct->setTypeId('simple') + ->setCategoryIds([3]) + ->setId(123) + ->setAttributeSetId(4) + ->setName('First Test Product For TableRate') + ->setSku('simple-tableRate-1') + ->setPrice(30) + ->setTaxClassId(0) + ->setMetaTitle('meta title') + ->setMetaKeyword('meta keyword') + ->setMetaDescription('meta description') + ->setVisibility(Visibility::VISIBILITY_BOTH) + ->setStatus(Status::STATUS_ENABLED); +/** @var StockItemInterface $stockItem */ +$firstStockItem = $objectManager->create(StockItemInterface::class); +$firstStockItem->setQty(100) + ->setIsInStock(true) + ->setManageStock(true); +$extensionAttributes = $firstProduct->getExtensionAttributes(); +$extensionAttributes->setStockItem($firstStockItem); + +/** @var ProductRepositoryInterface $productRepository */ +$firstProduct = $productRepository->save($firstProduct); + +$secondProduct = $objectManager->create(Product::class); +$secondProduct->setTypeId('simple') + ->setCategoryIds([6]) + ->setId(124) + ->setAttributeSetId(4) + ->setName('Second Test Product For TableRate') + ->setSku('simple-tableRate-2') + ->setPrice(40) + ->setTaxClassId(0) + ->setMetaTitle('meta title') + ->setMetaKeyword('meta keyword') + ->setMetaDescription('meta description') + ->setVisibility(Visibility::VISIBILITY_BOTH) + ->setStatus(Status::STATUS_ENABLED); +/** @var StockItemInterface $stockItem */ +$secondStockItem = $objectManager->create(StockItemInterface::class); +$secondStockItem->setQty(100) + ->setIsInStock(true) + ->setManageStock(true); +$extensionAttributes = $secondProduct->getExtensionAttributes(); +$extensionAttributes->setStockItem($secondStockItem); + +/** @var ProductRepositoryInterface $productRepository */ +$secondProduct = $productRepository->save($secondProduct); + +$thirdProduct = $objectManager->create(Product::class); +$thirdProduct->setTypeId('simple') + ->setCategoryIds([6]) + ->setId(658) + ->setAttributeSetId(4) + ->setName('Third Test Product For TableRate') + ->setSku('simple-tableRate-3') + ->setPrice(50) + ->setTaxClassId(0) + ->setMetaTitle('meta title') + ->setMetaKeyword('meta keyword') + ->setMetaDescription('meta description') + ->setVisibility(Visibility::VISIBILITY_BOTH) + ->setStatus(Status::STATUS_ENABLED); +/** @var StockItemInterface $stockItem */ +$thirdStockItem = $objectManager->create(StockItemInterface::class); +$thirdStockItem->setQty(100) + ->setIsInStock(true) + ->setManageStock(true); +$extensionAttributes = $thirdProduct->getExtensionAttributes(); +$extensionAttributes->setStockItem($thirdStockItem); + +/** @var ProductRepositoryInterface $productRepository */ +$thirdProduct = $productRepository->save($thirdProduct); + +$addressData = include __DIR__ . '/address_data.php'; +$billingAddress = $objectManager->create( + Address::class, + ['data' => $addressData] +); +$billingAddress->setAddressType('billing'); +$shippingAddress = $objectManager->create( + Address::class, + ['data' => $addressData] +); +$shippingAddress->setAddressType('shipping'); +$store = $objectManager + ->get(StoreManagerInterface::class) + ->getStore(); + +/** @var Quote $quote */ +$quote = $objectManager->create(Quote::class); +$quote->setCustomerIsGuest(true) + ->setStoreId($store->getId()) + ->setReservedOrderId('tableRate') + ->setBillingAddress($billingAddress) + ->setShippingAddress($shippingAddress); +$quote->getPayment()->setMethod('checkmo'); +$quote->addProduct($firstProduct); +$quote->addProduct($secondProduct); +$quote->addProduct($thirdProduct); +$quote->setIsMultiShipping(false); +$quoteRepository = $objectManager + ->get(CartRepositoryInterface::class); +$quoteRepository->save($quote); + +/** @var QuoteIdMask $quoteIdMask */ +$quoteIdMask = $objectManager + ->create(QuoteIdMaskFactory::class) + ->create(); +$quoteIdMask->setQuoteId($quote->getId()); +$quoteIdMask->setDataChanges(true); +$quoteIdMask->save(); diff --git a/dev/tests/integration/testsuite/Magento/Sales/_files/quote_with_multiple_products_rollback.php b/dev/tests/integration/testsuite/Magento/Sales/_files/quote_with_multiple_products_rollback.php new file mode 100644 index 0000000000000..73801919ab912 --- /dev/null +++ b/dev/tests/integration/testsuite/Magento/Sales/_files/quote_with_multiple_products_rollback.php @@ -0,0 +1,57 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ + +use Magento\Catalog\Api\ProductRepositoryInterface; +use Magento\CatalogInventory\Model\StockRegistryStorage; +use Magento\Framework\Exception\NoSuchEntityException; +use Magento\Framework\Registry; +use Magento\Quote\Model\Quote; +use Magento\TestFramework\Helper\Bootstrap; + +/** @var ObjectManager $objectManager */ +$objectManager = Bootstrap::getObjectManager(); +/** @var Registry $registry */ +$registry = $objectManager->get(Registry::class); +$registry->unregister('isSecureArea'); +$registry->register('isSecureArea', true); + +/** @var $quote Quote */ +$quote = $objectManager->create(Quote::class); +$quote->load('tableRate', 'reserved_order_id'); +if ($quote->getId()) { + $quote->delete(); +} + +/** @var ProductRepositoryInterface $productRepository */ +$productRepository = $objectManager + ->create(ProductRepositoryInterface::class); + +try { + $firstProduct = $productRepository->get('simple-tableRate-1', false, null, true); + $productRepository->delete($firstProduct); +} catch (NoSuchEntityException $exception) { + //Product already removed +} +try { + $secondProduct = $productRepository->get('simple-tableRate-2', false, null, true); + $productRepository->delete($secondProduct); +} catch (NoSuchEntityException $exception) { + //Product already removed +} +try { + $thirdProduct = $productRepository->get('simple-tableRate-3', false, null, true); + $productRepository->delete($thirdProduct); +} catch (NoSuchEntityException $exception) { + //Product already removed +} +/** @var StockRegistryStorage $stockRegistryStorage */ +$stockRegistryStorage = $objectManager + ->get(StockRegistryStorage::class); +$stockRegistryStorage->removeStockItem(123); +$stockRegistryStorage->removeStockItem(124); +$stockRegistryStorage->removeStockItem(658); +$registry->unregister('isSecureArea'); +$registry->register('isSecureArea', false); diff --git a/dev/tests/integration/testsuite/Magento/Sales/_files/quotes.php b/dev/tests/integration/testsuite/Magento/Sales/_files/quotes.php index 508563df2d5c0..bd0ae81b15862 100644 --- a/dev/tests/integration/testsuite/Magento/Sales/_files/quotes.php +++ b/dev/tests/integration/testsuite/Magento/Sales/_files/quotes.php @@ -5,6 +5,7 @@ */ declare(strict_types=1); +use Magento\Framework\App\Config; use Magento\Store\Model\StoreRepository; use Magento\Quote\Model\QuoteFactory; use Magento\Quote\Model\Quote; @@ -23,6 +24,9 @@ $quoteRepository = $objectManager->get(QuoteRepository::class); /** @var StoreRepository $storeRepository */ $storeRepository = $objectManager->get(StoreRepository::class); +/** @var Config $appConfig */ +$appConfig = $objectManager->get(Config::class); +$appConfig->clean(); /** @var Store $defaultStore */ $defaultStore = $storeRepository->getActiveStoreByCode('default'); diff --git a/dev/tests/integration/testsuite/Magento/Sales/_files/shipment_for_order_with_customer.php b/dev/tests/integration/testsuite/Magento/Sales/_files/shipment_for_order_with_customer.php new file mode 100644 index 0000000000000..c5304f5b8809f --- /dev/null +++ b/dev/tests/integration/testsuite/Magento/Sales/_files/shipment_for_order_with_customer.php @@ -0,0 +1,27 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +declare(strict_types=1); + +use Magento\Framework\DB\Transaction; +use Magento\Sales\Model\Order\ShipmentFactory; +use Magento\TestFramework\Helper\Bootstrap; + +require __DIR__ . '/order_with_customer.php'; + +$objectManager = Bootstrap::getObjectManager(); +$order->setIsInProcess(true); +/** @var Transaction $transaction */ +$transaction = $objectManager->create(Transaction::class); + +$items = []; +foreach ($order->getItems() as $orderItem) { + $items[$orderItem->getId()] = $orderItem->getQtyOrdered(); +} + +$shipment = $objectManager->get(ShipmentFactory::class)->create($order, $items); +$shipment->register(); + +$transaction->addObject($shipment)->addObject($order)->save(); diff --git a/dev/tests/integration/testsuite/Magento/Sales/_files/shipment_for_order_with_customer_rollback.php b/dev/tests/integration/testsuite/Magento/Sales/_files/shipment_for_order_with_customer_rollback.php new file mode 100644 index 0000000000000..2595d6bf4084a --- /dev/null +++ b/dev/tests/integration/testsuite/Magento/Sales/_files/shipment_for_order_with_customer_rollback.php @@ -0,0 +1,8 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +declare(strict_types=1); + +require __DIR__ . '/default_rollback.php'; diff --git a/dev/tests/integration/testsuite/Magento/SalesRule/Controller/Adminhtml/Promo/Quote/NewActionHtmlTest.php b/dev/tests/integration/testsuite/Magento/SalesRule/Controller/Adminhtml/Promo/Quote/NewActionHtmlTest.php index 82f1c53d8f161..b2fc8365c90ea 100644 --- a/dev/tests/integration/testsuite/Magento/SalesRule/Controller/Adminhtml/Promo/Quote/NewActionHtmlTest.php +++ b/dev/tests/integration/testsuite/Magento/SalesRule/Controller/Adminhtml/Promo/Quote/NewActionHtmlTest.php @@ -12,6 +12,7 @@ /** * New action html test * + * Verify the request object contains the proper form object for action * @magentoAppArea adminhtml */ class NewActionHtmlTest extends AbstractBackendController @@ -31,6 +32,11 @@ class NewActionHtmlTest extends AbstractBackendController */ private $formName = 'test_form'; + /** + * @var string + */ + private $requestFormName = 'rule_actions_fieldset_'; + /** * Test verifies that execute method has the proper data-form-part value in html response * @@ -73,6 +79,7 @@ private function prepareRequest(): void $this->getRequest()->setParams( [ 'id' => 1, + 'form' => $this->requestFormName, 'form_namespace' => $this->formName, 'type' => 'Magento\SalesRule\Model\Rule\Condition\Product|quote_item_price', ] diff --git a/dev/tests/integration/testsuite/Magento/SalesRule/Controller/Adminhtml/Promo/Quote/NewConditionHtmlTest.php b/dev/tests/integration/testsuite/Magento/SalesRule/Controller/Adminhtml/Promo/Quote/NewConditionHtmlTest.php new file mode 100644 index 0000000000000..f15befedfbca7 --- /dev/null +++ b/dev/tests/integration/testsuite/Magento/SalesRule/Controller/Adminhtml/Promo/Quote/NewConditionHtmlTest.php @@ -0,0 +1,88 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +declare(strict_types=1); + +namespace Magento\SalesRule\Controller\Adminhtml\Promo\Quote; + +use Magento\TestFramework\TestCase\AbstractBackendController; + +/** + * New condition html test + * + * Verify the request object contains the proper form object for condition + * @magentoAppArea adminhtml + */ +class NewConditionHtmlTest extends AbstractBackendController +{ + /** + * @var string + */ + protected $resource = 'Magento_SalesRule::quote'; + + /** + * @var string + */ + protected $uri = 'backend/sales_rule/promo_quote/newConditionHtml'; + + /** + * @var string + */ + private $formName = 'test_form'; + + /** + * @var string + */ + private $requestFormName = 'rule_conditions_fieldset_'; + + /** + * Test verifies that execute method has the proper data-form-part value in html response + * + * @return void + */ + public function testExecute(): void + { + $this->prepareRequest(); + $this->dispatch($this->uri); + $html = $this->getResponse() + ->getBody(); + $this->assertContains($this->formName, $html); + } + + /** + * @inheritdoc + */ + public function testAclHasAccess() + { + $this->prepareRequest(); + parent::testAclHasAccess(); + } + + /** + * @inheritdoc + */ + public function testAclNoAccess() + { + $this->prepareRequest(); + parent::testAclNoAccess(); + } + + /** + * Prepare request + * + * @return void + */ + private function prepareRequest(): void + { + $this->getRequest()->setParams( + [ + 'id' => 1, + 'form' => $this->requestFormName, + 'form_namespace' => $this->formName, + 'type' => 'Magento\SalesRule\Model\Rule\Condition\Product|category_ids', + ] + )->setMethod('POST'); + } +} diff --git a/dev/tests/integration/testsuite/Magento/SalesRule/Model/Observer/AssignCouponDataAfterOrderCustomerAssignTest.php b/dev/tests/integration/testsuite/Magento/SalesRule/Model/Observer/AssignCouponDataAfterOrderCustomerAssignTest.php index 9eaca30e4bd5d..71d07342c108f 100644 --- a/dev/tests/integration/testsuite/Magento/SalesRule/Model/Observer/AssignCouponDataAfterOrderCustomerAssignTest.php +++ b/dev/tests/integration/testsuite/Magento/SalesRule/Model/Observer/AssignCouponDataAfterOrderCustomerAssignTest.php @@ -8,8 +8,8 @@ use Magento\Customer\Model\Data\Customer; use Magento\Customer\Model\GroupManagement; -use Magento\Framework\Controller\Result\Redirect; use Magento\Sales\Model\Order; +use Magento\Sales\Model\Service\OrderService; use Magento\SalesRule\Api\CouponRepositoryInterface; use Magento\SalesRule\Model\Coupon; use Magento\SalesRule\Model\Rule; @@ -17,8 +17,6 @@ use Magento\TestFramework\Helper\Bootstrap; /** - * Class AssignCouponDataAfterOrderCustomerAssignTest - * * @magentoAppIsolation enabled * * @SuppressWarnings(PHPMD.CouplingBetweenObjects) @@ -80,6 +78,11 @@ class AssignCouponDataAfterOrderCustomerAssignTest extends \PHPUnit\Framework\Te */ private $customer; + /** + * @var OrderService + */ + private $orderService; + /** * @inheritdoc */ @@ -94,6 +97,7 @@ protected function setUp() $this->assignCouponToCustomerObserver = $this->objectManager->get( \Magento\SalesRule\Observer\AssignCouponDataAfterOrderCustomerAssignObserver::class ); + $this->orderService = $this->objectManager->get(OrderService::class); $this->salesRule = $this->prepareSalesRule(); $this->coupon = $this->attachSalesruleCoupon($this->salesRule); @@ -142,7 +146,7 @@ public function testOrderCancelingDecreasesCouponUsages() $this->processOrder($this->order); // Should not throw exception as bux is fixed now - $this->order->cancel(); + $this->orderService->cancel($this->order->getId()); $ruleCustomer = $this->getSalesruleCustomerUsage($this->customer, $this->salesRule); // Assert, that rule customer model has been created for specific customer diff --git a/dev/tests/integration/testsuite/Magento/SalesRule/Model/Rule/Action/Discount/CartFixedTest.php b/dev/tests/integration/testsuite/Magento/SalesRule/Model/Rule/Action/Discount/CartFixedTest.php index f56fb0a2c6135..583316d97d68c 100644 --- a/dev/tests/integration/testsuite/Magento/SalesRule/Model/Rule/Action/Discount/CartFixedTest.php +++ b/dev/tests/integration/testsuite/Magento/SalesRule/Model/Rule/Action/Discount/CartFixedTest.php @@ -10,7 +10,9 @@ use Magento\Catalog\Api\Data\ProductInterface; use Magento\Catalog\Model\Product; use Magento\Catalog\Model\ProductRepository; +use Magento\Checkout\Model\Session as CheckoutSession; use Magento\Framework\Api\SearchCriteriaBuilder; +use Magento\Multishipping\Model\Checkout\Type\Multishipping; use Magento\Quote\Api\CartRepositoryInterface; use Magento\Quote\Api\Data\CartItemInterface; use Magento\Quote\Api\GuestCartItemRepositoryInterface; @@ -21,6 +23,10 @@ use Magento\Quote\Model\QuoteIdMask; use Magento\Sales\Api\Data\OrderInterface; use Magento\Sales\Api\OrderRepositoryInterface; +use Magento\Sales\Model\Order; +use Magento\Sales\Model\Order\Email\Sender\OrderSender; +use Magento\SalesRule\Api\RuleRepositoryInterface; +use Magento\SalesRule\Model\Rule; use Magento\TestFramework\Helper\Bootstrap; /** @@ -187,11 +193,12 @@ public function testDiscountsOnQuoteWithFixedDiscount(): void /** * Load cart from fixture. * + * @param string $reservedOrderId * @return Quote */ - private function getQuote(): Quote + private function getQuote(string $reservedOrderId = 'test01'): Quote { - $searchCriteria = $this->criteriaBuilder->addFilter('reserved_order_id', 'test01')->create(); + $searchCriteria = $this->criteriaBuilder->addFilter('reserved_order_id', $reservedOrderId)->create(); $carts = $this->quoteRepository->getList($searchCriteria) ->getItems(); if (!$carts) { @@ -290,4 +297,283 @@ private function getOrder(string $incrementId): OrderInterface return array_pop($items); } + + /** + * Checks "fixed amount discount for whole cart" with multiple orders with different shipping addresses + * + * @magentoAppIsolation enabled + * @magentoDataFixture Magento/SalesRule/_files/coupon_cart_fixed_discount.php + * @magentoDataFixture Magento/Multishipping/Fixtures/quote_with_split_items.php + * @dataProvider multishippingDataProvider + * @param float $discount + * @param array $firstOrderTotals + * @param array $secondOrderTotals + * @param array $thirdOrderTotals + * @return void + */ + public function testMultishipping( + float $discount, + array $firstOrderTotals, + array $secondOrderTotals, + array $thirdOrderTotals + ): void { + $store = $this->objectManager->get(\Magento\Store\Model\StoreManagerInterface::class)->getStore(); + $salesRule = $this->getRule('15$ fixed discount on whole cart'); + $salesRule->setDiscountAmount($discount); + $this->saveRule($salesRule); + $quote = $this->getQuote('multishipping_quote_id'); + $quote->setStoreId($store->getId()); + $quote->setCouponCode('CART_FIXED_DISCOUNT_15'); + $quote->collectTotals(); + $this->quoteRepository->save($quote); + /** @var CheckoutSession $session */ + $session = $this->objectManager->get(CheckoutSession::class); + $session->replaceQuote($quote); + $orderSender = $this->getMockBuilder(OrderSender::class) + ->disableOriginalConstructor() + ->getMock(); + + $model = $this->objectManager->create( + Multishipping::class, + ['orderSender' => $orderSender] + ); + $model->createOrders(); + $orderList = $this->getOrderList((int)$quote->getId()); + $this->assertCount(3, $orderList); + /** + * The order with $10 simple product + * @var Order $firstOrder + */ + $firstOrder = array_shift($orderList); + + $this->assertEquals( + $firstOrderTotals['subtotal'], + $firstOrder->getSubtotal() + ); + $this->assertEquals( + $firstOrderTotals['discount_amount'], + $firstOrder->getDiscountAmount() + ); + $this->assertEquals( + $firstOrderTotals['shipping_amount'], + $firstOrder->getShippingAmount() + ); + $this->assertEquals( + $firstOrderTotals['grand_total'], + $firstOrder->getGrandTotal() + ); + /** + * The order with $20 simple product + * @var Order $secondOrder + */ + $secondOrder = array_shift($orderList); + $this->assertEquals( + $secondOrderTotals['subtotal'], + $secondOrder->getSubtotal() + ); + $this->assertEquals( + $secondOrderTotals['discount_amount'], + $secondOrder->getDiscountAmount() + ); + $this->assertEquals( + $secondOrderTotals['shipping_amount'], + $secondOrder->getShippingAmount() + ); + $this->assertEquals( + $secondOrderTotals['grand_total'], + $secondOrder->getGrandTotal() + ); + /** + * The order with $5 virtual product and billing address as shipping + * @var Order $thirdOrder + */ + $thirdOrder = array_shift($orderList); + $this->assertEquals( + $thirdOrderTotals['subtotal'], + $thirdOrder->getSubtotal() + ); + $this->assertEquals( + $thirdOrderTotals['discount_amount'], + $thirdOrder->getDiscountAmount() + ); + $this->assertEquals( + $thirdOrderTotals['shipping_amount'], + $thirdOrder->getShippingAmount() + ); + $this->assertEquals( + $thirdOrderTotals['grand_total'], + $thirdOrder->getGrandTotal() + ); + } + + /** + * @return array + * @SuppressWarnings(PHPMD.ExcessiveMethodLength) + */ + public function multishippingDataProvider(): array + { + return [ + 'Discount < 1stOrderSubtotal: only 1st order gets discount' => [ + 5, + [ + 'subtotal' => 10.00, + 'discount_amount' => -5.00, + 'shipping_amount' => 5.00, + 'grand_total' => 10.00, + ], + [ + 'subtotal' => 20.00, + 'discount_amount' => -0.00, + 'shipping_amount' => 5.00, + 'grand_total' => 25.00, + ], + [ + 'subtotal' => 5.00, + 'discount_amount' => -0.00, + 'shipping_amount' => 0.00, + 'grand_total' => 5.00, + ] + ], + 'Discount = 1stOrderSubtotal: only 1st order gets discount' => [ + 10, + [ + 'subtotal' => 10.00, + 'discount_amount' => -10.00, + 'shipping_amount' => 5.00, + 'grand_total' => 5.00, + ], + [ + 'subtotal' => 20.00, + 'discount_amount' => -0.00, + 'shipping_amount' => 5.00, + 'grand_total' => 25.00, + ], + [ + 'subtotal' => 5.00, + 'discount_amount' => -0.00, + 'shipping_amount' => 0.00, + 'grand_total' => 5.00, + ] + ], + 'Discount > 1stOrderSubtotal: 1st order get 100% discount and 2nd order get the remaining discount' => [ + 15, + [ + 'subtotal' => 10.00, + 'discount_amount' => -10.00, + 'shipping_amount' => 5.00, + 'grand_total' => 5.00, + ], + [ + 'subtotal' => 20.00, + 'discount_amount' => -5.00, + 'shipping_amount' => 5.00, + 'grand_total' => 20.00, + ], + [ + 'subtotal' => 5.00, + 'discount_amount' => -0.00, + 'shipping_amount' => 0.00, + 'grand_total' => 5.00, + ] + ], + 'Discount = 1stOrderSubtotal + 2ndOrderSubtotal: 1st order and 2nd order get 100% discount' => [ + 30, + [ + 'subtotal' => 10.00, + 'discount_amount' => -10.00, + 'shipping_amount' => 5.00, + 'grand_total' => 5.00, + ], + [ + 'subtotal' => 20.00, + 'discount_amount' => -20.00, + 'shipping_amount' => 5.00, + 'grand_total' => 5.00, + ], + [ + 'subtotal' => 5.00, + 'discount_amount' => -0.00, + 'shipping_amount' => 0.00, + 'grand_total' => 5.00, + ] + ], + 'Discount > 1stOrdSubtotal + 2ndOrdSubtotal: 1st order and 2nd order get 100% discount + and 3rd order get remaining discount' => [ + 31, + [ + 'subtotal' => 10.00, + 'discount_amount' => -10.00, + 'shipping_amount' => 5.00, + 'grand_total' => 5.00, + ], + [ + 'subtotal' => 20.00, + 'discount_amount' => -20.00, + 'shipping_amount' => 5.00, + 'grand_total' => 5.00, + ], + [ + 'subtotal' => 5.00, + 'discount_amount' => -1.00, + 'shipping_amount' => 0.00, + 'grand_total' => 4.00, + ] + ] + ]; + } + + /** + * Get list of orders by quote id. + * + * @param int $quoteId + * @return array + */ + private function getOrderList(int $quoteId): array + { + /** @var SearchCriteriaBuilder $searchCriteriaBuilder */ + $searchCriteriaBuilder = $this->objectManager->get(SearchCriteriaBuilder::class); + $searchCriteria = $searchCriteriaBuilder->addFilter('quote_id', $quoteId) + ->create(); + + /** @var OrderRepositoryInterface $orderRepository */ + $orderRepository = $this->objectManager->get(OrderRepositoryInterface::class); + return $orderRepository->getList($searchCriteria)->getItems(); + } + + /** + * Get rule by name + * + * @param string $name + * @return Rule + */ + private function getRule(string $name): Rule + { + /** @var SearchCriteriaBuilder $searchCriteriaBuilder */ + $searchCriteriaBuilder = $this->objectManager->get(SearchCriteriaBuilder::class); + $searchCriteria = $searchCriteriaBuilder->addFilter('name', $name) + ->create(); + /** @var RuleRepositoryInterface $ruleRepository */ + $ruleRepository = $this->objectManager->get(RuleRepositoryInterface::class); + $items = $ruleRepository->getList($searchCriteria) + ->getItems(); + /** @var Rule $salesRule */ + $dataModel = array_pop($items); + /** @var Rule $ruleModel */ + $ruleModel = $this->objectManager->get(\Magento\SalesRule\Model\RuleFactory::class)->create(); + $ruleModel->load($dataModel->getRuleId()); + return $ruleModel; + } + + /** + * Save rule into database + * + * @param Rule $rule + * @return void + */ + private function saveRule(Rule $rule): void + { + /** @var \Magento\SalesRule\Model\ResourceModel\Rule $resourceModel */ + $resourceModel = $this->objectManager->get(\Magento\SalesRule\Model\ResourceModel\Rule::class); + $resourceModel->save($rule); + } } diff --git a/dev/tests/integration/testsuite/Magento/SalesRule/Model/Rule/Condition/AddressTest.php b/dev/tests/integration/testsuite/Magento/SalesRule/Model/Rule/Condition/AddressTest.php new file mode 100644 index 0000000000000..17730262d2dfd --- /dev/null +++ b/dev/tests/integration/testsuite/Magento/SalesRule/Model/Rule/Condition/AddressTest.php @@ -0,0 +1,50 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +namespace Magento\SalesRule\Model\Rule\Condition; + +use Magento\TestFramework\Helper\Bootstrap; +use PHPUnit\Framework\TestCase; + +/** + * Test for \Magento\SalesRule\Model\Rule\Condition\Address. + */ +class AddressTest extends TestCase +{ + use ConditionHelper; + + /** + * @var \Magento\Framework\ObjectManagerInterface + */ + private $objectManager; + + /** + * @inheritDoc + */ + protected function setUp() + { + $this->objectManager = Bootstrap::getObjectManager(); + } + + /** + * Tests cart price rule validation. + * + * @magentoDbIsolation enabled + * @magentoAppIsolation enabled + * @magentoConfigFixture default_store payment/checkmo/active 1 + * @magentoDataFixture Magento/SalesRule/_files/rules_payment_method.php + * @magentoDataFixture Magento/Checkout/_files/quote_with_payment_saved.php + */ + public function testValidateRule() + { + $quote = $this->getQuote('test_order_1_with_payment'); + $rule = $this->getSalesRule('50% Off on Checkmo Payment Method'); + + $this->assertTrue( + $rule->validate($quote->getBillingAddress()), + 'Cart price rule validation failed.' + ); + } +} diff --git a/dev/tests/integration/testsuite/Magento/SalesRule/Model/Rule/Condition/ConditionHelper.php b/dev/tests/integration/testsuite/Magento/SalesRule/Model/Rule/Condition/ConditionHelper.php new file mode 100644 index 0000000000000..e857ab902fcc5 --- /dev/null +++ b/dev/tests/integration/testsuite/Magento/SalesRule/Model/Rule/Condition/ConditionHelper.php @@ -0,0 +1,62 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +namespace Magento\SalesRule\Model\Rule\Condition; + +use Magento\Framework\Api\SearchCriteriaBuilder; +use Magento\Quote\Api\CartRepositoryInterface; +use Magento\Quote\Api\Data\CartInterface; +use Magento\SalesRule\Api\RuleRepositoryInterface; + +/** + * Helper class for testing cart price rule conditions. + */ +trait ConditionHelper +{ + /** + * Gets quote by reserved order id. + * + * @param string $reservedOrderId + * @return CartInterface + */ + private function getQuote($reservedOrderId) + { + /** @var SearchCriteriaBuilder $searchCriteriaBuilder */ + $searchCriteriaBuilder = $this->objectManager->get(SearchCriteriaBuilder::class); + $searchCriteria = $searchCriteriaBuilder->addFilter('reserved_order_id', $reservedOrderId) + ->create(); + + /** @var CartRepositoryInterface $quoteRepository */ + $quoteRepository = $this->objectManager->get(CartRepositoryInterface::class); + $items = $quoteRepository->getList($searchCriteria)->getItems(); + return array_pop($items); + } + + /** + * Gets rule by name. + * + * @param string $name + * @return \Magento\SalesRule\Model\Rule + * @throws \Magento\Framework\Exception\InputException + * @throws \Magento\Framework\Exception\NoSuchEntityException + */ + private function getSalesRule(string $name): \Magento\SalesRule\Model\Rule + { + /** @var SearchCriteriaBuilder $searchCriteriaBuilder */ + $searchCriteriaBuilder = $this->objectManager->get(SearchCriteriaBuilder::class); + $searchCriteria = $searchCriteriaBuilder->addFilter('name', $name) + ->create(); + + /** @var CartRepositoryInterface $quoteRepository */ + $ruleRepository = $this->objectManager->get(RuleRepositoryInterface::class); + $items = $ruleRepository->getList($searchCriteria)->getItems(); + + $rule = array_pop($items); + /** @var \Magento\SalesRule\Model\Converter\ToModel $converter */ + $converter = $this->objectManager->get(\Magento\SalesRule\Model\Converter\ToModel::class); + + return $converter->toModel($rule); + } +} diff --git a/dev/tests/integration/testsuite/Magento/SalesRule/Model/Rule/Condition/ProductTest.php b/dev/tests/integration/testsuite/Magento/SalesRule/Model/Rule/Condition/ProductTest.php index 70fa11fc78c87..066f30667b53c 100644 --- a/dev/tests/integration/testsuite/Magento/SalesRule/Model/Rule/Condition/ProductTest.php +++ b/dev/tests/integration/testsuite/Magento/SalesRule/Model/Rule/Condition/ProductTest.php @@ -6,21 +6,24 @@ namespace Magento\SalesRule\Model\Rule\Condition; -use Magento\Quote\Api\CartRepositoryInterface; -use Magento\Framework\Api\SearchCriteriaBuilder; -use Magento\Quote\Api\Data\CartInterface; -use Magento\SalesRule\Api\RuleRepositoryInterface; +use Magento\Framework\Registry; +use Magento\SalesRule\Model\Rule; /** * @SuppressWarnings(PHPMD.CouplingBetweenObjects) */ class ProductTest extends \PHPUnit\Framework\TestCase { + use ConditionHelper; + /** * @var \Magento\Framework\ObjectManagerInterface */ private $objectManager; + /** + * @inheritDoc + */ protected function setUp() { $this->objectManager = \Magento\TestFramework\Helper\Bootstrap::getObjectManager(); @@ -50,8 +53,8 @@ public function testValidateCategorySalesRuleIncludesChildren($categoryId, $expe ->load('test_cart_with_configurable', 'reserved_order_id'); // Load the SalesRule looking for products in a specific category - /** @var $rule \Magento\SalesRule\Model\Rule */ - $rule = $this->objectManager->get(\Magento\Framework\Registry::class) + /** @var $rule Rule */ + $rule = $this->objectManager->get(Registry::class) ->registry('_fixture/Magento_SalesRule_Category'); // Prepare the parent product with the given category setting @@ -80,8 +83,8 @@ public function testValidateSalesRuleExcludesBundleChildren(): void ->load('test_cart_with_bundle_and_options', 'reserved_order_id'); // Load the SalesRule looking for excluding products with selected sku - /** @var $rule \Magento\SalesRule\Model\Rule */ - $rule = $this->objectManager->get(\Magento\Framework\Registry::class) + /** @var $rule Rule */ + $rule = $this->objectManager->get(Registry::class) ->registry('_fixture/Magento_SalesRule_Sku_Exclude'); $this->assertEquals(false, $rule->validate($quote)); @@ -129,47 +132,23 @@ public function testValidateQtySalesRuleWithConfigurable() } /** - * Gets quote by reserved order id. + * Ensure that SalesRules filtering on quote items quantity validates configurable product parent category correctly * - * @param string $reservedOrderId - * @return CartInterface - */ - private function getQuote($reservedOrderId) - { - /** @var SearchCriteriaBuilder $searchCriteriaBuilder */ - $searchCriteriaBuilder = $this->objectManager->get(SearchCriteriaBuilder::class); - $searchCriteria = $searchCriteriaBuilder->addFilter('reserved_order_id', $reservedOrderId) - ->create(); - - /** @var CartRepositoryInterface $quoteRepository */ - $quoteRepository = $this->objectManager->get(CartRepositoryInterface::class); - $items = $quoteRepository->getList($searchCriteria)->getItems(); - return array_pop($items); - } - - /** - * Gets rule by name. - * - * @param string $name - * @return \Magento\SalesRule\Model\Rule - * @throws \Magento\Framework\Exception\InputException - * @throws \Magento\Framework\Exception\NoSuchEntityException + * @magentoDataFixture Magento/ConfigurableProduct/_files/quote_with_configurable_product.php + * @magentoDataFixture Magento/SalesRule/_files/rules_parent_category.php */ - private function getSalesRule(string $name): \Magento\SalesRule\Model\Rule + public function testValidateParentCategoryWithConfigurable() { - /** @var SearchCriteriaBuilder $searchCriteriaBuilder */ - $searchCriteriaBuilder = $this->objectManager->get(SearchCriteriaBuilder::class); - $searchCriteria = $searchCriteriaBuilder->addFilter('name', $name) - ->create(); - - /** @var CartRepositoryInterface $quoteRepository */ - $ruleRepository = $this->objectManager->get(RuleRepositoryInterface::class); - $items = $ruleRepository->getList($searchCriteria)->getItems(); - - $rule = array_pop($items); - /** @var \Magento\SalesRule\Model\Converter\ToModel $converter */ - $converter = $this->objectManager->get(\Magento\SalesRule\Model\Converter\ToModel::class); + $quote = $this->getQuote('test_cart_with_configurable'); + $registry = $this->objectManager->get(Registry::class); + /** @var Rule $rule */ + $rule = $this->objectManager->create(Rule::class); + $ruleId = $registry->registry('50% Off on Configurable parent category'); + $rule->load($ruleId); - return $converter->toModel($rule); + $this->assertFalse( + $rule->validate($quote->getBillingAddress()), + 'Cart price rule validation failed.' + ); } } diff --git a/dev/tests/integration/testsuite/Magento/SalesRule/Plugin/CouponUsagesTest.php b/dev/tests/integration/testsuite/Magento/SalesRule/Plugin/CouponUsagesTest.php index e0477993eea52..23c4dbad12f62 100644 --- a/dev/tests/integration/testsuite/Magento/SalesRule/Plugin/CouponUsagesTest.php +++ b/dev/tests/integration/testsuite/Magento/SalesRule/Plugin/CouponUsagesTest.php @@ -9,6 +9,7 @@ use Magento\Framework\DataObject; use Magento\Framework\ObjectManagerInterface; use Magento\Sales\Model\Order; +use Magento\Sales\Model\Service\OrderService; use Magento\SalesRule\Model\Coupon; use Magento\SalesRule\Model\ResourceModel\Coupon\Usage; use Magento\TestFramework\Helper\Bootstrap; @@ -46,6 +47,24 @@ class CouponUsagesTest extends \PHPUnit\Framework\TestCase */ private $order; + /** + * @var OrderService + */ + private $orderService; + + /** + * @inheritdoc + */ + protected function setUp() + { + $this->objectManager = Bootstrap::getObjectManager(); + $this->coupon = $this->objectManager->get(Coupon::class); + $this->usage = $this->objectManager->get(Usage::class); + $this->couponUsage = $this->objectManager->get(DataObject::class); + $this->order = $this->objectManager->get(Order::class); + $this->orderService = $this->objectManager->get(OrderService::class); + } + /** * Test increasing coupon usages after after order placing and decreasing after order cancellation. * @@ -62,7 +81,7 @@ public function testOrderCancellation() $this->order->loadByIncrementId($orderId); // Make sure coupon usages value is incremented then order is placed. - $this->order->place(); + $this->orderService->place($this->order); $this->usage->loadByCustomerCoupon($this->couponUsage, $customerId, $this->coupon->getId()); $this->coupon->loadByCode($couponCode); @@ -76,7 +95,7 @@ public function testOrderCancellation() ); // Make sure order coupon usages value is decremented then order is cancelled. - $this->order->cancel(); + $this->orderService->cancel($this->order->getId()); $this->usage->loadByCustomerCoupon($this->couponUsage, $customerId, $this->coupon->getId()); $this->coupon->loadByCode($couponCode); @@ -89,13 +108,4 @@ public function testOrderCancellation() $this->couponUsage->getTimesUsed() ); } - - protected function setUp() - { - $this->objectManager = Bootstrap::getObjectManager(); - $this->coupon = $this->objectManager->get(Coupon::class); - $this->usage = $this->objectManager->get(Usage::class); - $this->couponUsage = $this->objectManager->get(DataObject::class); - $this->order = $this->objectManager->get(Order::class); - } } diff --git a/dev/tests/integration/testsuite/Magento/SalesRule/_files/cart_rule_50_percent_off_no_condition.php b/dev/tests/integration/testsuite/Magento/SalesRule/_files/cart_rule_50_percent_off_no_condition.php new file mode 100644 index 0000000000000..77178abdb2384 --- /dev/null +++ b/dev/tests/integration/testsuite/Magento/SalesRule/_files/cart_rule_50_percent_off_no_condition.php @@ -0,0 +1,38 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +/** @var \Magento\Framework\Registry $registry */ +/** @var \Magento\SalesRule\Model\Rule $salesRule */ +/** @var \Magento\SalesRule\Model\RuleRepository $salesRuleRepository */ + +$objectManager = \Magento\TestFramework\Helper\Bootstrap::getObjectManager(); +$registry = $objectManager->get(\Magento\Framework\Registry::class); +$salesRule = $objectManager->create(\Magento\SalesRule\Model\Rule::class); +$salesRuleRepository = $objectManager->create(\Magento\SalesRule\Model\RuleRepository::class); +$allRules = $salesRuleRepository->getList($objectManager->get(\Magento\Framework\Api\SearchCriteriaInterface::class)); +foreach ($allRules->getItems() as $rule) { + $salesRuleRepository->deleteById($rule->getRuleId()); +} +$salesRule->setData( + [ + 'name' => '50% off - July 4', + 'is_active' => 1, + 'customer_group_ids' => [\Magento\Customer\Model\GroupManagement::NOT_LOGGED_IN_ID], + 'coupon_type' => \Magento\SalesRule\Model\Rule::COUPON_TYPE_NO_COUPON, + 'simple_action' => 'by_percent', + 'discount_amount' => 50, + 'discount_step' => 0, + 'stop_rules_processing' => 1, + 'website_ids' => [ + \Magento\TestFramework\Helper\Bootstrap::getObjectManager()->get( + \Magento\Store\Model\StoreManagerInterface::class + )->getWebsite()->getId() + ] + ] +); +$salesRule->save(); + +$registry->unregister('Magento/SalesRule/_files/cart_rule_50_percent_off_no_condition/salesRuleId'); +$registry->register('Magento/SalesRule/_files/cart_rule_50_percent_off_no_condition/salesRuleId', $salesRule->getId()); diff --git a/dev/tests/integration/testsuite/Magento/SalesRule/_files/cart_rule_50_percent_off_no_condition_rollback.php b/dev/tests/integration/testsuite/Magento/SalesRule/_files/cart_rule_50_percent_off_no_condition_rollback.php new file mode 100644 index 0000000000000..bafaf1a48f960 --- /dev/null +++ b/dev/tests/integration/testsuite/Magento/SalesRule/_files/cart_rule_50_percent_off_no_condition_rollback.php @@ -0,0 +1,19 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +declare(strict_types=1); + +/** @var \Magento\Framework\Registry $registry */ +/** @var \Magento\SalesRule\Model\Rule $salesRule */ +$objectManager = \Magento\TestFramework\Helper\Bootstrap::getObjectManager(); +$registry = $objectManager->get(\Magento\Framework\Registry::class); +$salesRule = $objectManager->create(\Magento\SalesRule\Model\Rule::class); +$salesRuleId = $registry->registry('Magento/SalesRule/_files/cart_rule_50_percent_off_no_condition/salesRuleId'); +if ($salesRuleId) { + $salesRule->load($salesRuleId); + if ($salesRule->getId()) { + $salesRule->delete(); + } +} diff --git a/dev/tests/integration/testsuite/Magento/SalesRule/_files/cart_rule_free_shipping_by_category.php b/dev/tests/integration/testsuite/Magento/SalesRule/_files/cart_rule_free_shipping_by_category.php new file mode 100644 index 0000000000000..9f973b704e8e6 --- /dev/null +++ b/dev/tests/integration/testsuite/Magento/SalesRule/_files/cart_rule_free_shipping_by_category.php @@ -0,0 +1,82 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ + +use Magento\Customer\Model\GroupManagement; +use Magento\Framework\Registry; +use Magento\SalesRule\Model\Rule; +use Magento\SalesRule\Model\Rule\Condition\Product; +use Magento\SalesRule\Model\Rule\Condition\Product\Combine; +use Magento\SalesRule\Model\RuleFactory; +use Magento\Store\Model\StoreManagerInterface; +use Magento\TestFramework\Helper\Bootstrap; + +$objectManager = Bootstrap::getObjectManager(); +$salesRuleFactory = $objectManager->create(RuleFactory::class); +/** @var Rule $salesRule */ +$salesRule = $salesRuleFactory->create(); +$row = + [ + 'name' => 'TableRate Free shipping if category is 3', + 'is_active' => 1, + 'customer_group_ids' => [GroupManagement::NOT_LOGGED_IN_ID], + 'coupon_type' => Rule::COUPON_TYPE_NO_COUPON, + 'conditions' => [ + 1 => [ + 'type' => Rule\Condition\Combine::class, + 'attribute' => null, + 'operator' => null, + 'value' => '1', + 'is_value_processed' => null, + 'aggregator' => 'all', + ] + ], + 'actions' => [ + 1 => [ + 'type' => Combine::class, + 'attribute' => null, + 'operator' => null, + 'value' => '1', + 'is_value_processed' => null, + 'aggregator' => 'all', + 'conditions' => [], + 'actions' => [ + 1 => [ + 'type' => Product::class, + 'attribute' => 'category_ids', + 'operator' => '==', + 'value' => '3', + 'is_value_processed' => false, + 'attribute_scope' => '' + ] + ] + ] + ], + 'is_advanced' => 1, + 'simple_action' => 'by_percent', + 'discount_amount' => 0, + 'stop_rules_processing' => 0, + 'discount_qty' => 0, + 'discount_step' => 0, + 'apply_to_shipping' => 1, + 'times_used' => 0, + 'is_rss' => 1, + 'use_auto_generation' => 0, + 'uses_per_coupon' => 0, + 'simple_free_shipping' => 1, + 'website_ids' => [ + $objectManager->get( + StoreManagerInterface::class + )->getWebsite()->getId() + ] + ]; +$salesRule->loadPost($row); +$salesRule->save(); + +/** @var Registry $registry */ +$registry = $objectManager->get(Registry::class); + +$registry->unregister('cart_rule_free_shipping_by_category'); +$registry->register('cart_rule_free_shipping_by_category', $salesRule); diff --git a/dev/tests/integration/testsuite/Magento/SalesRule/_files/cart_rule_free_shipping_by_category_rollback.php b/dev/tests/integration/testsuite/Magento/SalesRule/_files/cart_rule_free_shipping_by_category_rollback.php new file mode 100644 index 0000000000000..837e06ea824e2 --- /dev/null +++ b/dev/tests/integration/testsuite/Magento/SalesRule/_files/cart_rule_free_shipping_by_category_rollback.php @@ -0,0 +1,18 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ + +use Magento\Framework\Registry; +use Magento\SalesRule\Model\Rule; +use Magento\TestFramework\Helper\Bootstrap; + +/** @var Registry $registry */ +$registry = Bootstrap::getObjectManager()->get(Registry::class); + +/** @var Rule $rule */ +$rule = $registry->registry('cart_rule_free_shipping_by_category'); +if ($rule) { + $rule->delete(); +} diff --git a/dev/tests/integration/testsuite/Magento/SalesRule/_files/rules_parent_category.php b/dev/tests/integration/testsuite/Magento/SalesRule/_files/rules_parent_category.php new file mode 100644 index 0000000000000..c525fa7152447 --- /dev/null +++ b/dev/tests/integration/testsuite/Magento/SalesRule/_files/rules_parent_category.php @@ -0,0 +1,66 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +declare(strict_types=1); + +use Magento\Framework\Registry; +use Magento\TestFramework\Helper\Bootstrap; + +/** @var Registry $registry */ +$registry = Bootstrap::getObjectManager()->get(Registry::class); +/** @var \Magento\SalesRule\Model\Rule $rule */ +$salesRule = Bootstrap::getObjectManager()->create(\Magento\SalesRule\Model\Rule::class); +$salesRule->setData( + [ + 'name' => '50% Off on Configurable parent category', + 'is_active' => 1, + 'customer_group_ids' => [\Magento\Customer\Model\GroupManagement::NOT_LOGGED_IN_ID], + 'coupon_type' => \Magento\SalesRule\Model\Rule::COUPON_TYPE_NO_COUPON, + 'simple_action' => 'by_percent', + 'discount_amount' => 50, + 'discount_step' => 0, + 'stop_rules_processing' => 1, + 'website_ids' => [ + Bootstrap::getObjectManager()->get( + \Magento\Store\Model\StoreManagerInterface::class + )->getWebsite()->getId() + ] + ] +); + +$salesRule->getConditions()->loadArray([ + 'type' => \Magento\SalesRule\Model\Rule\Condition\Combine::class, + 'attribute' => null, + 'operator' => null, + 'value' => '1', + 'is_value_processed' => null, + 'aggregator' => 'all', + 'conditions' => + [ + [ + 'type' => \Magento\SalesRule\Model\Rule\Condition\Product\Subselect::class, + 'attribute' => 'qty', + 'operator' => '==', + 'value' => '1', + 'is_value_processed' => null, + 'aggregator' => 'all', + 'conditions' => + [ + [ + 'type' => \Magento\SalesRule\Model\Rule\Condition\Product::class, + 'attribute' => 'category_ids', + 'attribute_scope' => 'parent', + 'operator' => '!=', + 'value' => '2', + 'is_value_processed' => false, + ], + ], + ], + ], +]); + +$salesRule->save(); +$registry->unregister('50% Off on Configurable parent category'); +$registry->register('50% Off on Configurable parent category', $salesRule->getRuleId()); diff --git a/dev/tests/integration/testsuite/Magento/SalesRule/_files/rules_parent_category_rollback.php b/dev/tests/integration/testsuite/Magento/SalesRule/_files/rules_parent_category_rollback.php new file mode 100644 index 0000000000000..edefd5e1650e7 --- /dev/null +++ b/dev/tests/integration/testsuite/Magento/SalesRule/_files/rules_parent_category_rollback.php @@ -0,0 +1,21 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +declare(strict_types=1); + +use Magento\SalesRule\Model\Rule; +use Magento\TestFramework\Helper\Bootstrap; +use Magento\Framework\Registry; + +/** @var Registry $registry */ +$registry = Bootstrap::getObjectManager()->get(Registry::class); +$rule = Bootstrap::getObjectManager()->get(Rule::class); + +/** @var Rule $rule */ +$ruleId = $registry->registry('50% Off on Configurable parent category'); +$rule->load($ruleId); +if ($rule->getId()) { + $rule->delete(); +} diff --git a/dev/tests/integration/testsuite/Magento/SalesRule/_files/rules_payment_method.php b/dev/tests/integration/testsuite/Magento/SalesRule/_files/rules_payment_method.php new file mode 100644 index 0000000000000..25f208d34d8e0 --- /dev/null +++ b/dev/tests/integration/testsuite/Magento/SalesRule/_files/rules_payment_method.php @@ -0,0 +1,47 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ + +use Magento\TestFramework\Helper\Bootstrap; + +/** @var \Magento\SalesRule\Model\Rule $rule */ +$salesRule = Bootstrap::getObjectManager()->create(\Magento\SalesRule\Model\Rule::class); +$salesRule->setData( + [ + 'name' => '50% Off on Checkmo Payment Method', + 'is_active' => 1, + 'customer_group_ids' => [\Magento\Customer\Model\GroupManagement::NOT_LOGGED_IN_ID], + 'coupon_type' => \Magento\SalesRule\Model\Rule::COUPON_TYPE_NO_COUPON, + 'simple_action' => 'by_percent', + 'discount_amount' => 50, + 'discount_step' => 0, + 'stop_rules_processing' => 1, + 'website_ids' => [ + Bootstrap::getObjectManager()->get( + \Magento\Store\Model\StoreManagerInterface::class + )->getWebsite()->getId() + ] + ] +); + +$salesRule->getConditions()->loadArray([ + 'type' => \Magento\SalesRule\Model\Rule\Condition\Combine::class, + 'attribute' => null, + 'operator' => null, + 'value' => '1', + 'is_value_processed' => null, + 'aggregator' => 'any', + 'conditions' => + [ + [ + 'type' => \Magento\SalesRule\Model\Rule\Condition\Address::class, + 'attribute' => 'payment_method', + 'operator' => '==', + 'value' => 'checkmo' + ], + ], +]); + +$salesRule->save(); diff --git a/dev/tests/integration/testsuite/Magento/Security/Model/AdminSessionsManagerTest.php b/dev/tests/integration/testsuite/Magento/Security/Model/AdminSessionsManagerTest.php index a201dbfdf1b03..eb10b34f5482b 100644 --- a/dev/tests/integration/testsuite/Magento/Security/Model/AdminSessionsManagerTest.php +++ b/dev/tests/integration/testsuite/Magento/Security/Model/AdminSessionsManagerTest.php @@ -5,6 +5,9 @@ */ namespace Magento\Security\Model; +/** + * @magentoAppArea adminhtml + */ class AdminSessionsManagerTest extends \PHPUnit\Framework\TestCase { /** diff --git a/dev/tests/integration/testsuite/Magento/Security/Model/Plugin/AuthSessionTest.php b/dev/tests/integration/testsuite/Magento/Security/Model/Plugin/AuthSessionTest.php index 52268dc96d8a3..a979165c5bd59 100644 --- a/dev/tests/integration/testsuite/Magento/Security/Model/Plugin/AuthSessionTest.php +++ b/dev/tests/integration/testsuite/Magento/Security/Model/Plugin/AuthSessionTest.php @@ -6,7 +6,9 @@ namespace Magento\Security\Model\Plugin; /** + * @magentoAppArea adminhtml * @magentoAppIsolation enabled + * @SuppressWarnings(PHPMD.CouplingBetweenObjects) */ class AuthSessionTest extends \PHPUnit\Framework\TestCase { @@ -140,4 +142,46 @@ public function testProcessProlong() $this->assertGreaterThan(strtotime($oldUpdatedAt), strtotime($updatedAt)); } + + /** + * Test processing prolong with an expired user. + * + * @magentoDbIsolation enabled + */ + public function testProcessProlongWithExpiredUser() + { + $this->auth->login( + \Magento\TestFramework\Bootstrap::ADMIN_NAME, + \Magento\TestFramework\Bootstrap::ADMIN_PASSWORD + ); + + $expireDate = new \DateTime(); + $expireDate->modify('-10 days'); + /** @var \Magento\User\Model\User $user */ + $user = $this->objectManager->create(\Magento\User\Model\User::class); + $user->loadByUsername(\Magento\TestFramework\Bootstrap::ADMIN_NAME); + $userExpirationFactory = + $this->objectManager->create(\Magento\Security\Api\Data\UserExpirationInterfaceFactory::class); + /** @var \Magento\Security\Api\Data\UserExpirationInterface $userExpiration */ + $userExpiration = $userExpirationFactory->create(); + $userExpiration->setId($user->getId()) + ->setExpiresAt($expireDate->format('Y-m-d H:i:s')) + ->save(); + + // need to trigger a prolong + $sessionId = $this->authSession->getSessionId(); + $prolongsDiff = 4 * log($this->securityConfig->getAdminSessionLifetime()) + 2; + $dateInPast = $this->dateTime->formatDate($this->authSession->getUpdatedAt() - $prolongsDiff); + $this->adminSessionsManager->getCurrentSession() + ->setData( + 'updated_at', + $dateInPast + ) + ->save(); + $this->adminSessionInfo->load($sessionId, 'session_id'); + $this->authSession->prolong(); + static::assertFalse($this->auth->isLoggedIn()); + $user->reload(); + static::assertFalse((bool)$user->getIsActive()); + } } diff --git a/dev/tests/integration/testsuite/Magento/Security/Model/ResourceModel/UserExpiration/CollectionTest.php b/dev/tests/integration/testsuite/Magento/Security/Model/ResourceModel/UserExpiration/CollectionTest.php new file mode 100644 index 0000000000000..e52f84c68d851 --- /dev/null +++ b/dev/tests/integration/testsuite/Magento/Security/Model/ResourceModel/UserExpiration/CollectionTest.php @@ -0,0 +1,74 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ + +declare(strict_types=1); + +namespace Magento\Security\Model\ResourceModel\UserExpiration; + +/** + * Test UserExpiration collection filters. + */ +class CollectionTest extends \PHPUnit\Framework\TestCase +{ + /** + * @var \Magento\Security\Model\ResourceModel\UserExpiration\CollectionFactory + */ + protected $collectionModelFactory; + + /** + * @var \Magento\Framework\ObjectManagerInterface + */ + protected $objectManager; + + /** + * Set up + */ + protected function setUp() + { + $this->objectManager = \Magento\TestFramework\Helper\Bootstrap::getObjectManager(); + $this->collectionModelFactory = $this->objectManager + ->create(\Magento\Security\Model\ResourceModel\UserExpiration\CollectionFactory::class); + } + + /** + * @magentoDataFixture Magento/Security/_files/expired_users.php + */ + public function testAddExpiredActiveUsersFilter() + { + /** @var \Magento\Security\Model\ResourceModel\UserExpiration\Collection $collectionModel */ + $collectionModel = $this->collectionModelFactory->create(); + $collectionModel->addActiveExpiredUsersFilter(); + static::assertEquals(1, $collectionModel->getSize()); + } + + /** + * @magentoDataFixture Magento/Security/_files/expired_users.php + */ + public function testAddUserIdsFilter() + { + $adminUserNameFromFixture = 'adminUserExpired'; + $user = $this->objectManager->create(\Magento\User\Model\User::class); + $user->loadByUsername($adminUserNameFromFixture); + + /** @var \Magento\Security\Model\ResourceModel\UserExpiration\Collection $collectionModel */ + $collectionModel = $this->collectionModelFactory->create()->addUserIdsFilter([$user->getId()]); + static::assertEquals(1, $collectionModel->getSize()); + } + + /** + * @magentoDataFixture Magento/Security/_files/expired_users.php + */ + public function testAddExpiredRecordsForUserFilter() + { + $adminUserNameFromFixture = 'adminUserExpired'; + $user = $this->objectManager->create(\Magento\User\Model\User::class); + $user->loadByUsername($adminUserNameFromFixture); + + /** @var \Magento\Security\Model\ResourceModel\UserExpiration\Collection $collectionModel */ + $collectionModel = $this->collectionModelFactory->create()->addExpiredRecordsForUserFilter($user->getId()); + static::assertEquals(1, $collectionModel->getSize()); + } +} diff --git a/dev/tests/integration/testsuite/Magento/Security/Model/UserExpirationManagerTest.php b/dev/tests/integration/testsuite/Magento/Security/Model/UserExpirationManagerTest.php new file mode 100644 index 0000000000000..adb7b7a120f1f --- /dev/null +++ b/dev/tests/integration/testsuite/Magento/Security/Model/UserExpirationManagerTest.php @@ -0,0 +1,177 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +declare(strict_types=1); + +namespace Magento\Security\Model; + +/** + * Tests for \Magento\Security\Model\UserExpirationManager + * @magentoAppArea adminhtml + * @SuppressWarnings(PHPMD.CouplingBetweenObjects) + */ +class UserExpirationManagerTest extends \PHPUnit\Framework\TestCase +{ + /** + * @var \Magento\Framework\ObjectManagerInterface + */ + private $objectManager; + + /** + * @var \Magento\Backend\Model\Auth + */ + private $auth; + + /** + * @var \Magento\Backend\Model\Auth\Session + */ + private $authSession; + + /** + * @var \Magento\Security\Model\AdminSessionInfo + */ + private $adminSessionInfo; + + /** + * @var \Magento\Security\Model\UserExpirationManager + */ + private $userExpirationManager; + + protected function setUp() + { + $this->objectManager = \Magento\TestFramework\Helper\Bootstrap::getObjectManager(); + $this->auth = $this->objectManager->create(\Magento\Backend\Model\Auth::class); + $this->authSession = $this->objectManager->create(\Magento\Backend\Model\Auth\Session::class); + $this->adminSessionInfo = $this->objectManager->create(\Magento\Security\Model\AdminSessionInfo::class); + $this->auth->setAuthStorage($this->authSession); + $this->userExpirationManager = + $this->objectManager->create(\Magento\Security\Model\UserExpirationManager::class); + } + + /** + * @magentoDataFixture Magento/Security/_files/expired_users.php + */ + public function testUserIsExpired() + { + $adminUserNameFromFixture = 'adminUserExpired'; + $user = $this->loadUserByUsername($adminUserNameFromFixture); + static::assertTrue($this->userExpirationManager->isUserExpired($user->getId())); + } + + /** + * @magentoDataFixture Magento/Security/_files/expired_users.php + * @magentoAppIsolation enabled + */ + public function testDeactivateExpiredUsersWithExpiredUser() + { + $adminUsernameFromFixture = 'adminUserNotExpired'; + $this->loginUser($adminUsernameFromFixture); + $user = $this->loadUserByUsername($adminUsernameFromFixture); + $sessionId = $this->authSession->getSessionId(); + $this->expireUser($user); + $this->userExpirationManager->deactivateExpiredUsersById([$user->getId()]); + $this->adminSessionInfo->load($sessionId, 'session_id'); + $user->reload(); + $userExpirationModel = $this->loadExpiredUserModelByUser($user); + static::assertEquals(0, $user->getIsActive()); + static::assertNull($userExpirationModel->getId()); + static::assertEquals(AdminSessionInfo::LOGGED_OUT, (int)$this->adminSessionInfo->getStatus()); + } + + /** + * @magentoDataFixture Magento/Security/_files/expired_users.php + * @magentoAppIsolation enabled + */ + public function testDeactivateExpiredUsersWithNonExpiredUser() + { + $adminUsernameFromFixture = 'adminUserNotExpired'; + $this->loginUser($adminUsernameFromFixture); + $user = $this->loadUserByUsername($adminUsernameFromFixture); + $sessionId = $this->authSession->getSessionId(); + $this->userExpirationManager->deactivateExpiredUsersById([$user->getId()]); + $user->reload(); + $userExpirationModel = $this->loadExpiredUserModelByUser($user); + $this->adminSessionInfo->load($sessionId, 'session_id'); + static::assertEquals(1, $user->getIsActive()); + static::assertEquals($user->getId(), $userExpirationModel->getId()); + static::assertEquals(AdminSessionInfo::LOGGED_IN, (int)$this->adminSessionInfo->getStatus()); + } + + /** + * Test deactivating without inputting a user. + * + * @magentoDataFixture Magento/Security/_files/expired_users.php + */ + public function testDeactivateExpiredUsers() + { + $notExpiredUser = $this->loadUserByUsername('adminUserNotExpired'); + $expiredUser = $this->loadUserByUsername('adminUserExpired'); + $this->userExpirationManager->deactivateExpiredUsers(); + $notExpiredUserExpirationModel = $this->loadExpiredUserModelByUser($notExpiredUser); + $expiredUserExpirationModel = $this->loadExpiredUserModelByUser($expiredUser); + + static::assertNotNull($notExpiredUserExpirationModel->getId()); + static::assertNull($expiredUserExpirationModel->getId()); + $notExpiredUser->reload(); + $expiredUser->reload(); + static::assertEquals($notExpiredUser->getIsActive(), 1); + static::assertEquals($expiredUser->getIsActive(), 0); + } + + /** + * Login the given user and return a user model. + * + * @param string $username + * @throws \Magento\Framework\Exception\AuthenticationException + */ + private function loginUser(string $username) + { + $this->auth->login( + $username, + \Magento\TestFramework\Bootstrap::ADMIN_PASSWORD + ); + } + + /** + * @param $username + * @return \Magento\User\Model\User + */ + private function loadUserByUsername(string $username): \Magento\User\Model\User + { + /** @var \Magento\User\Model\User $user */ + $user = $this->objectManager->create(\Magento\User\Model\User::class); + $user->loadByUsername($username); + return $user; + } + + /** + * Expire the given user and return the UserExpiration model. + * + * @param \Magento\User\Model\User $user + * @throws \Exception + */ + private function expireUser(\Magento\User\Model\User $user) + { + $expireDate = new \DateTime(); + $expireDate->modify('-10 days'); + /** @var \Magento\Security\Api\Data\UserExpirationInterface $userExpiration */ + $userExpiration = $this->objectManager->create(\Magento\Security\Api\Data\UserExpirationInterface::class); + $userExpiration->setId($user->getId()) + ->setExpiresAt($expireDate->format('Y-m-d H:i:s')) + ->save(); + } + + /** + * @param \Magento\User\Model\User $user + * @return \Magento\Security\Model\UserExpiration + */ + private function loadExpiredUserModelByUser(\Magento\User\Model\User $user): \Magento\Security\Model\UserExpiration + { + /** @var \Magento\Security\Model\UserExpiration $expiredUserModel */ + $expiredUserModel = $this->objectManager->create(\Magento\Security\Model\UserExpiration::class); + $expiredUserModel->load($user->getId()); + return $expiredUserModel; + } +} diff --git a/dev/tests/integration/testsuite/Magento/Security/Observer/AdminUserAuthenticateBeforeTest.php b/dev/tests/integration/testsuite/Magento/Security/Observer/AdminUserAuthenticateBeforeTest.php new file mode 100644 index 0000000000000..f25b836b80514 --- /dev/null +++ b/dev/tests/integration/testsuite/Magento/Security/Observer/AdminUserAuthenticateBeforeTest.php @@ -0,0 +1,46 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +declare(strict_types=1); + +namespace Magento\Security\Observer; + +use Magento\TestFramework\Helper\Bootstrap; + +/** + * Test for \Magento\Security\Observer\AdminUserAuthenticateBefore + */ +class AdminUserAuthenticateBeforeTest extends \PHPUnit\Framework\TestCase +{ + + /** + * @magentoDataFixture Magento/Security/_files/expired_users.php + * @expectedException \Magento\Framework\Exception\Plugin\AuthenticationException + * @expectedExceptionMessage The account sign-in was incorrect or your account is disabled temporarily. + * Please wait and try again later + */ + public function testWithExpiredUser() + { + $adminUserNameFromFixture = 'adminUserExpired'; + $password = \Magento\TestFramework\Bootstrap::ADMIN_PASSWORD; + /** @var \Magento\User\Model\User $user */ + $user = Bootstrap::getObjectManager()->create(\Magento\User\Model\User::class); + $user->authenticate($adminUserNameFromFixture, $password); + static::assertFalse((bool)$user->getIsActive()); + } + + /** + * @magentoDataFixture Magento/Security/_files/expired_users.php + */ + public function testWithNonExpiredUser() + { + $adminUserNameFromFixture = 'adminUserNotExpired'; + $password = \Magento\TestFramework\Bootstrap::ADMIN_PASSWORD; + /** @var \Magento\User\Model\User $user */ + $user = Bootstrap::getObjectManager()->create(\Magento\User\Model\User::class); + $user->authenticate($adminUserNameFromFixture, $password); + static::assertTrue((bool)$user->getIsActive()); + } +} diff --git a/dev/tests/integration/testsuite/Magento/Security/Observer/AfterAdminUserSaveTest.php b/dev/tests/integration/testsuite/Magento/Security/Observer/AfterAdminUserSaveTest.php new file mode 100644 index 0000000000000..ee2ae79b62cf8 --- /dev/null +++ b/dev/tests/integration/testsuite/Magento/Security/Observer/AfterAdminUserSaveTest.php @@ -0,0 +1,126 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +declare(strict_types=1); + +namespace Magento\Security\Observer; + +use Magento\TestFramework\Helper\Bootstrap; + +/** + * Test for \Magento\Security\Observer\AfterAdminUserSave + */ +class AfterAdminUserSaveTest extends \PHPUnit\Framework\TestCase +{ + + /** + * Save a new UserExpiration record + * + * @magentoDataFixture Magento/User/_files/dummy_user.php + */ + public function testSaveNewUserExpiration() + { + $adminUserNameFromFixture = 'dummy_username'; + $testDate = $this->getFutureDateInStoreTime(); + $user = Bootstrap::getObjectManager()->create(\Magento\User\Model\User::class); + $user->loadByUsername($adminUserNameFromFixture); + $user->setExpiresAt($testDate); + $user->save(); + + $userExpirationFactory = + Bootstrap::getObjectManager()->create(\Magento\Security\Model\UserExpirationFactory::class); + /** @var \Magento\Security\Model\UserExpiration $userExpiration */ + $userExpiration = $userExpirationFactory->create(); + $userExpiration->load($user->getId()); + static::assertNotNull($userExpiration->getId()); + static::assertEquals($userExpiration->getExpiresAt(), $testDate); + } + + /** + * Save a new UserExpiration; used to validate that date conversion is working correctly. + * + * @magentoDataFixture Magento/User/_files/dummy_user.php + */ + public function testSaveNewUserExpirationInMinutes() + { + $adminUserNameFromFixture = 'dummy_username'; + $testDate = $this->getFutureDateInStoreTime('+2 minutes'); + $user = Bootstrap::getObjectManager()->create(\Magento\User\Model\User::class); + $user->loadByUsername($adminUserNameFromFixture); + $user->setExpiresAt($testDate); + $user->save(); + + $userExpirationFactory = + Bootstrap::getObjectManager()->create(\Magento\Security\Model\UserExpirationFactory::class); + /** @var \Magento\Security\Model\UserExpiration $userExpiration */ + $userExpiration = $userExpirationFactory->create(); + $userExpiration->load($user->getId()); + static::assertNotNull($userExpiration->getId()); + static::assertEquals($userExpiration->getExpiresAt(), $testDate); + } + + /** + * Remove the UserExpiration record + * + * @magentoDataFixture Magento/Security/_files/expired_users.php + */ + public function testClearUserExpiration() + { + $adminUserNameFromFixture = 'adminUserExpired'; + $user = Bootstrap::getObjectManager()->create(\Magento\User\Model\User::class); + $user->loadByUsername($adminUserNameFromFixture); + $user->setExpiresAt(null); + $user->save(); + + $userExpirationFactory = + Bootstrap::getObjectManager()->create(\Magento\Security\Model\UserExpirationFactory::class); + /** @var \Magento\Security\Model\UserExpiration $userExpiration */ + $userExpiration = $userExpirationFactory->create(); + $userExpiration->load($user->getId()); + static::assertNull($userExpiration->getId()); + } + + /** + * Change the UserExpiration record + * + * @magentoDataFixture Magento/Security/_files/expired_users.php + */ + public function testChangeUserExpiration() + { + $adminUserNameFromFixture = 'adminUserNotExpired'; + $testDate = $this->getFutureDateInStoreTime(); + $user = Bootstrap::getObjectManager()->create(\Magento\User\Model\User::class); + $user->loadByUsername($adminUserNameFromFixture); + + $userExpirationFactory = + Bootstrap::getObjectManager()->create(\Magento\Security\Model\UserExpirationFactory::class); + /** @var \Magento\Security\Model\UserExpiration $userExpiration */ + $userExpiration = $userExpirationFactory->create(); + $userExpiration->load($user->getId()); + $existingExpiration = $userExpiration->getExpiresAt(); + + $user->setExpiresAt($testDate); + $user->save(); + $userExpiration->load($user->getId()); + static::assertNotNull($userExpiration->getId()); + static::assertEquals($userExpiration->getExpiresAt(), $testDate); + static::assertNotEquals($existingExpiration, $userExpiration->getExpiresAt()); + } + + /** + * @param string $timeToAdd Amount of time to add + * @return string + * @throws \Exception + */ + private function getFutureDateInStoreTime($timeToAdd = '+20 days') + { + /** @var \Magento\Framework\Stdlib\DateTime\TimezoneInterface $locale */ + $locale = Bootstrap::getObjectManager()->get(\Magento\Framework\Stdlib\DateTime\TimezoneInterface::class); + $testDate = new \DateTime(); + $testDate->modify($timeToAdd); + $storeDate = $locale->date($testDate); + return $storeDate->format('Y-m-d H:i:s'); + } +} diff --git a/dev/tests/integration/testsuite/Magento/Security/_files/expired_users.php b/dev/tests/integration/testsuite/Magento/Security/_files/expired_users.php new file mode 100644 index 0000000000000..ac45f120ca6d3 --- /dev/null +++ b/dev/tests/integration/testsuite/Magento/Security/_files/expired_users.php @@ -0,0 +1,58 @@ +<?php +declare(strict_types=1); +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ + +$objectManager = \Magento\TestFramework\Helper\Bootstrap::getObjectManager(); + +/** + * Create an admin user with expired and non-expired access date + */ + +/** @var $userModelNotExpired \Magento\User\Model\User */ +$userModelNotExpired = $objectManager->create(\Magento\User\Model\User::class); +$userModelNotExpired->setFirstName("John") + ->setLastName("Doe") + ->setUserName('adminUserNotExpired') + ->setPassword(\Magento\TestFramework\Bootstrap::ADMIN_PASSWORD) + ->setEmail('adminUserNotExpired@example.com') + ->setRoleType('G') + ->setResourceId('Magento_Adminhtml::all') + ->setPrivileges("") + ->setAssertId(0) + ->setRoleId(1) + ->setPermission('allow') + ->setIsActive(1) + ->save(); +$futureDate = new \DateTime(); +$futureDate->modify('+10 days'); +$notExpiredRecord = $objectManager->create(\Magento\Security\Model\UserExpiration::class); +$notExpiredRecord + ->setId($userModelNotExpired->getId()) + ->setExpiresAt($futureDate->format('Y-m-d H:i:s')) + ->save(); + +/** @var $userModelExpired \Magento\User\Model\User */ +$pastDate = new \DateTime(); +$pastDate->modify('-10 days'); +$userModelExpired = $objectManager->create(\Magento\User\Model\User::class); +$userModelExpired->setFirstName("John") + ->setLastName("Doe") + ->setUserName('adminUserExpired') + ->setPassword(\Magento\TestFramework\Bootstrap::ADMIN_PASSWORD) + ->setEmail('adminUserExpired@example.com') + ->setRoleType('G') + ->setResourceId('Magento_Adminhtml::all') + ->setPrivileges("") + ->setAssertId(0) + ->setRoleId(1) + ->setPermission('allow') + ->setIsActive(1) + ->save(); +$expiredRecord = $objectManager->create(\Magento\Security\Model\UserExpiration::class); +$expiredRecord + ->setId($userModelExpired->getId()) + ->setExpiresAt($pastDate->format('Y-m-d H:i:s')) + ->save(); diff --git a/dev/tests/integration/testsuite/Magento/SendFriend/Block/ProductViewTest.php b/dev/tests/integration/testsuite/Magento/SendFriend/Block/ProductViewTest.php new file mode 100644 index 0000000000000..daa7b0bab84e3 --- /dev/null +++ b/dev/tests/integration/testsuite/Magento/SendFriend/Block/ProductViewTest.php @@ -0,0 +1,104 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +declare(strict_types=1); + +namespace Magento\SendFriend\Block; + +use Magento\Catalog\Api\Data\ProductInterface; +use Magento\Catalog\Api\ProductRepositoryInterface; +use Magento\Catalog\Block\Product\View; +use Magento\Framework\ObjectManagerInterface; +use Magento\Framework\Registry; +use Magento\Framework\View\LayoutInterface; +use Magento\TestFramework\Helper\Bootstrap; +use PHPUnit\Framework\TestCase; + +/** + * Class checks send friend link visibility + * + * @magentoAppArea frontend + * @magentoDataFixture Magento/Catalog/_files/second_product_simple.php + */ +class ProductViewTest extends TestCase +{ + /** @var ObjectManagerInterface */ + private $objectManager; + + /** @var Registry */ + private $registry; + + /** @var LayoutInterface */ + private $layout; + + /** @var View */ + private $block; + + /** @var ProductRepositoryInterface */ + private $productRepository; + + /** + * @inheritdoc + */ + protected function setUp() + { + parent::setUp(); + + $this->objectManager = Bootstrap::getObjectManager(); + $this->layout = $this->objectManager->get(LayoutInterface::class); + $this->block = $this->layout->createBlock(View::class); + $this->block->setTemplate('Magento_Catalog::product/view/mailto.phtml'); + $this->registry = $this->objectManager->get(Registry::class); + $this->productRepository = $this->objectManager->get(ProductRepositoryInterface::class); + $this->productRepository->cleanCache(); + } + + /** + * @inheritdoc + */ + protected function tearDown() + { + parent::tearDown(); + + $this->registry->unregister('product'); + } + + /** + * @return void + */ + public function testSendFriendLinkDisabled(): void + { + $this->registerProduct('simple2'); + $this->assertEmpty($this->block->toHtml()); + } + + /** + * @magentoConfigFixture current_store sendfriend/email/enabled 1 + * + * @return void + */ + public function testSendFriendLinkEnabled(): void + { + $product = $this->registerProduct('simple2'); + $html = $this->block->toHtml(); + $this->assertContains('sendfriend/product/send/id/' . $product->getId(), $html); + $this->assertEquals('Email', trim(strip_tags($html))); + } + + /** + * Register product by sku + * + * @param string $sku + * @return ProductInterface + */ + private function registerProduct(string $sku): ProductInterface + { + $product = $this->productRepository->get($sku); + $this->registry->unregister('product'); + $this->registry->register('product', $product); + + return $product; + } +} diff --git a/dev/tests/integration/testsuite/Magento/SendFriend/Block/SendTest.php b/dev/tests/integration/testsuite/Magento/SendFriend/Block/SendTest.php index 1c6bfe29f876d..539293480d5bb 100644 --- a/dev/tests/integration/testsuite/Magento/SendFriend/Block/SendTest.php +++ b/dev/tests/integration/testsuite/Magento/SendFriend/Block/SendTest.php @@ -3,40 +3,94 @@ * Copyright © Magento, Inc. All rights reserved. * See COPYING.txt for license details. */ +declare(strict_types=1); + namespace Magento\SendFriend\Block; +use Magento\Customer\Api\AccountManagementInterface; +use Magento\Customer\Model\Session; +use Magento\Framework\ObjectManagerInterface; +use Magento\Framework\View\LayoutInterface; use Magento\TestFramework\Helper\Bootstrap; +use Magento\TestFramework\Helper\Xpath; +use PHPUnit\Framework\TestCase; -class SendTest extends \PHPUnit\Framework\TestCase +/** + * Class checks send friend email block + * + * @see \Magento\SendFriend\Block\Send + * + * @magentoAppArea frontend + */ +class SendTest extends TestCase { + /** @var array */ + private $elementsXpath = [ + 'sender name field' => "//input[@name='sender[name]']", + 'sender email field' => "//input[@name='sender[email]']", + 'sender message field' => "//textarea[@name='sender[message]']", + 'recipient name field' => "//input[contains(@name, 'recipients[name]')]", + 'recipient email field' => "//input[contains(@name, 'recipients[email]')]", + 'submit button' => "//button[@type='submit']/span[contains(text(), 'Send Email')]", + 'notice massage' => "//div[@id='max-recipient-message']" + . "/span[contains(text(), 'Maximum 1 email addresses allowed.')]" + ]; + + /** @var ObjectManagerInterface */ + private $objectManager; + + /** @var LayoutInterface */ + private $layout; + + /** @var Send */ + private $block; + + /** @var Session */ + private $session; + + /** @var AccountManagementInterface */ + private $accountManagement; + /** - * @var \Magento\SendFriend\Block\Send + * @inheritdoc */ - protected $_block; - protected function setUp() { - $this->_block = Bootstrap::getObjectManager()->create(\Magento\SendFriend\Block\Send::class); + $this->objectManager = Bootstrap::getObjectManager(); + $this->layout = $this->objectManager->get(LayoutInterface::class); + $this->block = $this->layout->createBlock(Send::class); + $this->session = $this->objectManager->get(Session::class); + $this->accountManagement = $this->objectManager->get(AccountManagementInterface::class); + } + + /** + * @inheritdoc + */ + protected function tearDown() + { + parent::tearDown(); + + $this->session->logout(); } /** + * @dataProvider formDataProvider + * * @param string $field * @param string $value - * @dataProvider formDataProvider - * @covers \Magento\SendFriend\Block\Send::getUserName - * @covers \Magento\SendFriend\Block\Send::getEmail + * @return void */ - public function testGetCustomerFieldFromFormData($field, $value) + public function testGetCustomerFieldFromFormData(string $field, string $value): void { $formData = ['sender' => [$field => $value]]; - $this->_block->setFormData($formData); + $this->block->setFormData($formData); $this->assertEquals(trim($value), $this->_callBlockMethod($field)); } /** * @return array */ - public function formDataProvider() + public function formDataProvider(): array { return [ ['name', 'Customer Form Name'], @@ -45,29 +99,27 @@ public function formDataProvider() } /** + * @magentoDataFixture Magento/Customer/_files/customer.php + * + * @dataProvider customerSessionDataProvider + * + * @magentoAppIsolation enabled + * * @param string $field * @param string $value - * @dataProvider customerSessionDataProvider - * @covers \Magento\SendFriend\Block\Send::getUserName - * @covers \Magento\SendFriend\Block\Send::getEmail - * @magentoDataFixture Magento/Customer/_files/customer.php + * @return void */ - public function testGetCustomerFieldFromSession($field, $value) + public function testGetCustomerFieldFromSession(string $field, string $value): void { - $logger = $this->createMock(\Psr\Log\LoggerInterface::class); - /** @var $session \Magento\Customer\Model\Session */ - $session = Bootstrap::getObjectManager()->create(\Magento\Customer\Model\Session::class, [$logger]); - /** @var \Magento\Customer\Api\AccountManagementInterface $service */ - $service = Bootstrap::getObjectManager()->create(\Magento\Customer\Api\AccountManagementInterface::class); - $customer = $service->authenticate('customer@example.com', 'password'); - $session->setCustomerDataAsLoggedIn($customer); + $customer = $this->accountManagement->authenticate('customer@example.com', 'password'); + $this->session->setCustomerDataAsLoggedIn($customer); $this->assertEquals($value, $this->_callBlockMethod($field)); } /** * @return array */ - public function customerSessionDataProvider() + public function customerSessionDataProvider(): array { return [ ['name', 'John Smith'], @@ -75,19 +127,37 @@ public function customerSessionDataProvider() ]; } + /** + * @magentoConfigFixture current_store sendfriend/email/max_recipients 1 + * + * @return void + */ + public function testBlockAppearance(): void + { + $this->block->setTemplate('Magento_SendFriend::send.phtml'); + $html = preg_replace('#<script(.*?)>#i', '', $this->block->toHtml()); + foreach ($this->elementsXpath as $key => $xpath) { + $this->assertEquals( + 1, + Xpath::getElementsCountForXpath($xpath, $html), + sprintf('The %s field is not found on the page', $key) + ); + } + } + /** * Call block method based on form field * * @param string $field * @return null|string */ - protected function _callBlockMethod($field) + protected function _callBlockMethod(string $field): ?string { switch ($field) { case 'name': - return $this->_block->getUserName(); + return $this->block->getUserName(); case 'email': - return $this->_block->getEmail(); + return $this->block->getEmail(); default: return null; } diff --git a/dev/tests/integration/testsuite/Magento/SendFriend/Controller/SendTest.php b/dev/tests/integration/testsuite/Magento/SendFriend/Controller/SendTest.php new file mode 100644 index 0000000000000..eccf4ee4c5ef9 --- /dev/null +++ b/dev/tests/integration/testsuite/Magento/SendFriend/Controller/SendTest.php @@ -0,0 +1,161 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +declare(strict_types=1); + +namespace Magento\SendFriend\Controller; + +use Magento\Customer\Model\Session; +use Magento\SendFriend\Model\SendFriend; +use Magento\TestFramework\Response; +use Magento\TestFramework\TestCase\AbstractController; + +/** + * Class for send friend send action + * + * @magentoAppArea frontend + * + * @see \Magento\SendFriend\Controller\Product\Send + */ +class SendTest extends AbstractController +{ + /** @var Session */ + private $customerSession; + + /** + * @inheritdoc + */ + protected function setUp() + { + parent::setUp(); + + $this->customerSession = $this->_objectManager->get(Session::class); + } + + /** + * @inheritdoc + */ + protected function tearDown() + { + parent::tearDown(); + + $this->customerSession->logout(); + } + + /** + * @magentoConfigFixture current_store sendfriend/email/enabled 0 + * + * @magentoDataFixture Magento/Catalog/_files/second_product_simple.php + * + * @return void + */ + public function testSendMailNotAllowed(): void + { + $this->dispatchWithProductIdParam(6); + $this->assert404NotFound(); + } + + /** + * @magentoConfigFixture current_store sendfriend/email/enabled 1 + * @magentoConfigFixture current_store sendfriend/email/allow_guest 0 + * + * @magentoDataFixture Magento/Catalog/_files/second_product_simple.php + * + * @return void + */ + public function testGuestSendMailNotAllowed(): void + { + $this->dispatchWithProductIdParam(6); + $this->assertRedirect($this->stringContains('customer/account/login')); + } + + /** + * @magentoConfigFixture current_store sendfriend/email/enabled 1 + * @magentoConfigFixture current_store sendfriend/email/allow_guest 1 + * + * @magentoDataFixture Magento/Catalog/_files/second_product_simple.php + * + * @return void + */ + public function testGuestSendMailAllowed(): void + { + $this->dispatchWithProductIdParam(6); + $this->assertEquals(Response::STATUS_CODE_200, $this->getResponse()->getHttpResponseCode()); + } + + /** + * @magentoConfigFixture current_store sendfriend/email/enabled 1 + * + * @magentoDataFixture Magento/Customer/_files/customer.php + * @magentoDataFixture Magento/Catalog/_files/second_product_simple.php + * + * @return void + */ + public function testLoggedInCustomer(): void + { + $this->customerSession->loginById(1); + $this->dispatchWithProductIdParam(6); + $this->assertEquals(Response::STATUS_CODE_200, $this->getResponse()->getHttpResponseCode()); + } + + /** + * @magentoConfigFixture current_store sendfriend/email/enabled 1 + * + * @magentoDataFixture Magento/Customer/_files/customer.php + * + * @return void + */ + public function testWithoutProductId(): void + { + $this->customerSession->loginById(1); + $this->dispatch('sendfriend/product/send/'); + $this->assert404NotFound(); + } + + /** + * @magentoConfigFixture current_store sendfriend/email/enabled 1 + * @magentoConfigFixture current_store sendfriend/email/max_per_hour 1 + * + * @magentoDataFixture Magento/Customer/_files/customer.php + * @magentoDataFixture Magento/Catalog/_files/second_product_simple.php + * + * @return void + */ + public function testToMachSendRequests(): void + { + $this->createSendFriendMock(); + $this->customerSession->loginById(1); + $this->dispatchWithProductIdParam(6); + $this->assertSessionMessages( + $this->equalTo([(string)__('You can\'t send messages more than 5 times an hour.')]) + ); + $this->assertEquals(Response::STATUS_CODE_200, $this->getResponse()->getHttpResponseCode()); + } + + /** + * Set product id parameter and dispatch controller + * + * @param int $productId + * @return void + */ + private function dispatchWithProductIdParam(int $productId): void + { + $this->getRequest()->setParam('id', $productId); + $this->dispatch('sendfriend/product/send/'); + } + + /** + * Create mock to imitate to mach send requests + * + * @return void + */ + private function createSendFriendMock(): void + { + $mock = $this->createMock(SendFriend::class); + $mock->method('isExceedLimit')->willReturn(true); + $mock->method('getMaxSendsToFriend')->willReturn(5); + $this->_objectManager->addSharedInstance($mock, SendFriend::class); + } +} diff --git a/dev/tests/integration/testsuite/Magento/SendFriend/Controller/SendmailTest.php b/dev/tests/integration/testsuite/Magento/SendFriend/Controller/SendmailTest.php index 1d6adf52466d2..850225bcee1b2 100644 --- a/dev/tests/integration/testsuite/Magento/SendFriend/Controller/SendmailTest.php +++ b/dev/tests/integration/testsuite/Magento/SendFriend/Controller/SendmailTest.php @@ -10,79 +10,128 @@ use Magento\Catalog\Api\Data\ProductInterface; use Magento\Catalog\Api\ProductRepositoryInterface; use Magento\Customer\Model\Session; -use Magento\Framework\Data\Form\FormKey; -use Magento\TestFramework\Helper\Bootstrap; use Magento\Framework\Message\MessageInterface; +use Magento\TestFramework\Helper\Xpath; +use Magento\TestFramework\Mail\Template\TransportBuilderMock; use Magento\TestFramework\Request; use Magento\TestFramework\TestCase\AbstractController; /** - * Class SendmailTest + * Class checks send mail action + * + * @see \Magento\SendFriend\Controller\Product\Sendmail + * + * @magentoAppArea frontend + * @magentoDbIsolation enabled + * @magentoAppIsolation enabled */ class SendmailTest extends AbstractController { + private const MESSAGE_PRODUCT_LINK_XPATH = "//a[contains(@href, '%s') and contains(text(), '%s')]"; + + /** @var Session */ + private $session; + + /** @var ProductRepositoryInterface */ + private $productRepository; + + /** @var TransportBuilderMock */ + private $transportBuilder; + + /** @var array */ + private $staticData = [ + 'sender' => [ + 'name' => 'Test', + 'email' => 'test@example.com', + 'message' => 'Message', + ], + 'recipients' => [ + 'name' => [ + 'Recipient 1', + 'Recipient 2' + ], + 'email' => [ + 'r1@example.com', + 'r2@example.com' + ] + ], + ]; + + /** + * @inheritdoc + */ + protected function setUp() + { + parent::setUp(); + + $this->session = $this->_objectManager->get(Session::class); + $this->productRepository = $this->_objectManager->get(ProductRepositoryInterface::class); + $this->transportBuilder = $this->_objectManager->get(TransportBuilderMock::class); + } + + /** + * @inheritdoc + */ + protected function tearDown() + { + parent::tearDown(); + + $this->session->logout(); + } + /** * Share the product to friend as logged in customer * - * @magentoAppArea frontend - * @magentoDbIsolation enabled - * @magentoAppIsolation enabled * @magentoConfigFixture default_store sendfriend/email/allow_guest 0 * @magentoConfigFixture default_store sendfriend/email/enabled 1 * @magentoDataFixture Magento/Customer/_files/customer.php * @magentoDataFixture Magento/Catalog/_files/products.php + * + * @return void */ - public function testSendActionAsLoggedIn() + public function testSendActionAsLoggedIn(): void { - $product = $this->getProduct(); - $this->login(1); + $product = $this->productRepository->get('custom-design-simple-product'); + $this->session->loginById(1); $this->prepareRequestData(); - $this->dispatch('sendfriend/product/sendmail/id/' . $product->getId()); - $this->assertSessionMessages( - $this->equalTo(['The link to a friend was sent.']), - MessageInterface::TYPE_SUCCESS - ); + $this->checkSuccess($product); } /** * Share the product to friend as guest customer * - * @magentoDbIsolation enabled - * @magentoAppIsolation enabled * @magentoConfigFixture default_store sendfriend/email/enabled 1 * @magentoConfigFixture default_store sendfriend/email/allow_guest 1 * @magentoDataFixture Magento/Catalog/_files/products.php + * + * @return void */ - public function testSendActionAsGuest() + public function testSendActionAsGuest(): void { - $product = $this->getProduct(); + $product = $this->productRepository->get('custom-design-simple-product'); $this->prepareRequestData(); - $this->dispatch('sendfriend/product/sendmail/id/' . $product->getId()); - $this->assertSessionMessages( - $this->equalTo(['The link to a friend was sent.']), - MessageInterface::TYPE_SUCCESS - ); + $this->checkSuccess($product); } /** * Share the product to friend as guest customer with invalid post data * - * @magentoDbIsolation enabled - * @magentoAppIsolation enabled * @magentoConfigFixture default_store sendfriend/email/enabled 1 * @magentoConfigFixture default_store sendfriend/email/allow_guest 1 * @magentoDataFixture Magento/Catalog/_files/products.php + * + * @return void */ - public function testSendActionAsGuestWithInvalidData() + public function testSendActionAsGuestWithInvalidData(): void { - $product = $this->getProduct(); - $this->prepareRequestData(true); - + $product = $this->productRepository->get('custom-design-simple-product'); + unset($this->staticData['sender']['email']); + $this->prepareRequestData(); $this->dispatch('sendfriend/product/sendmail/id/' . $product->getId()); $this->assertSessionMessages( - $this->equalTo(['Invalid Sender Email']), + $this->equalTo([(string)__('Invalid Sender Email')]), MessageInterface::TYPE_ERROR ); } @@ -90,82 +139,53 @@ public function testSendActionAsGuestWithInvalidData() /** * Share the product invisible in catalog to friend as guest customer * - * @magentoDbIsolation enabled - * @magentoAppIsolation enabled * @magentoConfigFixture default_store sendfriend/email/enabled 1 * @magentoConfigFixture default_store sendfriend/email/allow_guest 1 * @magentoDataFixture Magento/Catalog/_files/simple_products_not_visible_individually.php + * + * @return void */ - public function testSendInvisibleProduct() + public function testSendInvisibleProduct(): void { - $product = $this->getInvisibleProduct(); + $product = $this->productRepository->get('simple_not_visible_1'); $this->prepareRequestData(); - $this->dispatch('sendfriend/product/sendmail/id/' . $product->getId()); $this->assert404NotFound(); } /** - * @return ProductInterface - */ - private function getProduct() - { - return $this->_objectManager->get(ProductRepositoryInterface::class)->get('custom-design-simple-product'); - } - - /** - * @return ProductInterface - */ - private function getInvisibleProduct() - { - return $this->_objectManager->get(ProductRepositoryInterface::class)->get('simple_not_visible_1'); - } - - /** - * Login the user + * Check success session message and email content * - * @param string $customerId Customer to mark as logged in for the session + * @param ProductInterface $product * @return void */ - protected function login($customerId) + private function checkSuccess(ProductInterface $product): void { - /** @var Session $session */ - $session = Bootstrap::getObjectManager() - ->get(Session::class); - $session->loginById($customerId); + $this->assertSessionMessages( + $this->equalTo([(string)__('The link to a friend was sent.')]), + MessageInterface::TYPE_SUCCESS + ); + $message = $this->transportBuilder->getSentMessage(); + $this->assertNotNull($message, 'The message was not sent'); + $content = $message->getBody()->getParts()[0]->getRawContent(); + $this->assertEquals( + 1, + Xpath::getElementsCountForXpath( + sprintf(self::MESSAGE_PRODUCT_LINK_XPATH, $product->getUrlKey(), $product->getName()), + $content + ), + 'Sent message does not contain product link' + ); } /** - * @param bool $invalidData + * Prepare request before dispatch + * * @return void */ - private function prepareRequestData($invalidData = false) + private function prepareRequestData(): void { - /** @var FormKey $formKey */ - $formKey = $this->_objectManager->get(FormKey::class); - $post = [ - 'sender' => [ - 'name' => 'Test', - 'email' => 'test@example.com', - 'message' => 'Message', - ], - 'recipients' => [ - 'name' => [ - 'Recipient 1', - 'Recipient 2' - ], - 'email' => [ - 'r1@example.com', - 'r2@example.com' - ] - ], - 'form_key' => $formKey->getFormKey(), - ]; - if ($invalidData) { - unset($post['sender']['email']); - } - $this->getRequest()->setMethod(Request::METHOD_POST); - $this->getRequest()->setPostValue($post); + $this->getRequest()->setPostValue($this->staticData); } } diff --git a/dev/tests/integration/testsuite/Magento/SendFriend/Model/SendFriendTest.php b/dev/tests/integration/testsuite/Magento/SendFriend/Model/SendFriendTest.php new file mode 100644 index 0000000000000..9a9ef6440261a --- /dev/null +++ b/dev/tests/integration/testsuite/Magento/SendFriend/Model/SendFriendTest.php @@ -0,0 +1,227 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +declare(strict_types=1); + +namespace Magento\SendFriend\Model; + +use Magento\Framework\App\RequestInterface; +use Magento\Framework\ObjectManagerInterface; +use Magento\Framework\Stdlib\CookieManagerInterface; +use Magento\SendFriend\Helper\Data as SendFriendHelper; +use Magento\TestFramework\Helper\Bootstrap; +use PHPUnit\Framework\TestCase; +use Zend\Stdlib\Parameters; + +/** + * Class checks send friend model behavior + * + * @see \Magento\SendFriend\Model\SendFriend + */ +class SendFriendTest extends TestCase +{ + /** @var ObjectManagerInterface */ + private $objectManager; + + /** @var SendFriend */ + private $sendFriend; + + /** @var CookieManagerInterface */ + private $cookieManager; + + /** @var RequestInterface */ + private $request; + + /** + * @inheritdoc + */ + protected function setUp() + { + parent::setUp(); + + $this->objectManager = Bootstrap::getObjectManager(); + $this->sendFriend = $this->objectManager->get(SendFriendFactory::class)->create(); + $this->cookieManager = $this->objectManager->get(CookieManagerInterface::class); + $this->request = $this->objectManager->get(RequestInterface::class); + } + + /** + * @magentoConfigFixture current_store sendfriend/email/max_recipients 1 + * + * @dataProvider validateDataProvider + * + * @param array $sender + * @param array $recipients + * @param string|bool $expectedResult + * @return void + */ + public function testValidate(array $sender, array $recipients, $expectedResult): void + { + $this->prepareData($sender, $recipients); + $this->checkResult($expectedResult, $this->sendFriend->validate()); + } + + /** + * @return array + */ + public function validateDataProvider(): array + { + return [ + 'valid_data' => [ + 'sender' => [ + 'name' => 'Sender Name', + 'email' => 'm1111ytest@mail.com', + 'message' => 'test message', + ], + 'recipients' => [ + 'name' => [ + 'recipient_name', + ], + 'email' => [ + 'recipient_email@example.com', + ], + ], + 'expected_result' => true, + ], + 'empty_message' => [ + 'sender' => [ + 'name' => 'Sender Name', + 'email' => 'm1111ytest@mail.com', + 'message' => '', + ], + 'recipients' => [ + 'name' => [ + 'recipient name', + ], + 'email' => [ + 'recipient_email@example.com', + ], + ], + 'expected_result' => 'Please enter a message.', + ], + 'empty_sender_name' => [ + 'sender' => [ + 'name' => '', + 'email' => 'customer_email@example.com', + 'message' => 'test message', + ], + 'recipients' => [ + 'name' => [ + 'recipient name', + ], + 'email' => [ + 'recipient_email@example.com', + ], + ], + 'expected_result' => 'Please enter a sender name.', + ], + 'empty_recipients' => [ + 'sender' => [ + 'name' => 'Sender Name', + 'email' => 'm1111ytest@mail.com', + 'message' => 'test message', + ], + 'recipients' => [ + 'name' => [], + 'email' => [], + ], + 'expected_result' => 'Please specify at least one recipient.', + ], + 'wrong_recipient_email' => [ + 'sender' => [ + 'name' => 'Sender Name', + 'email' => 'm1111ytest@mail.com', + 'message' => 'test message', + ], + 'recipients' => [ + 'name' => [ + 'recipient name', + ], + 'email' => [ + '123123', + ], + ], + 'expected_result' => 'Please enter a correct recipient email address.', + ], + 'to_much_recipients' => [ + 'sender' => [ + 'name' => 'Sender Name', + 'email' => 'm1111ytest@mail.com', + 'message' => 'test message', + ], + 'recipients' => [ + 'name' => [ + 'recipient name', + 'second name', + ], + 'email' => [ + 'recipient_email@example.com', + 'recipient2_email@example.com', + ], + ], + 'expected_result' => 'No more than 1 emails can be sent at a time.', + ], + ]; + } + + /** + * @magentoConfigFixture current_store sendfriend/email/check_by 0 + * @magentoConfigFixture current_store sendfriend/email/max_per_hour 1 + * + * @return void + */ + public function testisExceedLimitByCookies(): void + { + $this->cookieManager->setPublicCookie(SendFriendHelper::COOKIE_NAME, (string)time()); + $this->assertTrue($this->sendFriend->isExceedLimit()); + } + + /** + * @magentoConfigFixture current_store sendfriend/email/check_by 1 + * @magentoConfigFixture current_store sendfriend/email/max_per_hour 1 + * + * @magentoDataFixture Magento/SendFriend/_files/sendfriend_log_record_half_hour_before.php + * + * @magentoDbIsolation disabled + * @return void + */ + public function testisExceedLimitByIp(): void + { + $this->markTestSkipped('Blocked by MC-31968'); + $parameters = $this->objectManager->create(Parameters::class); + $parameters->set('REMOTE_ADDR', '127.0.0.1'); + $this->request->setServer($parameters); + $this->assertTrue($this->sendFriend->isExceedLimit()); + } + + /** + * Check result + * + * @param array|bool $expectedResult + * @param array|bool $result + * @return void + */ + private function checkResult($expectedResult, $result): void + { + if ($expectedResult === true) { + $this->assertTrue($result); + } else { + $this->assertEquals($expectedResult, (string)reset($result) ?? ''); + } + } + + /** + * Prepare sender and recipient data + * + * @param array $sender + * @param array $recipients + * @return void + */ + private function prepareData(array $sender, array $recipients): void + { + $this->sendFriend->setSender($sender); + $this->sendFriend->setRecipients($recipients); + } +} diff --git a/dev/tests/integration/testsuite/Magento/SendFriend/_files/sendfriend_log_record_half_hour_before.php b/dev/tests/integration/testsuite/Magento/SendFriend/_files/sendfriend_log_record_half_hour_before.php new file mode 100644 index 0000000000000..132cbe97d43ee --- /dev/null +++ b/dev/tests/integration/testsuite/Magento/SendFriend/_files/sendfriend_log_record_half_hour_before.php @@ -0,0 +1,20 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +declare(strict_types=1); + +use Magento\SendFriend\Model\ResourceModel\SendFriend as SendFriendResource; +use Magento\Store\Api\WebsiteRepositoryInterface; +use Magento\TestFramework\Helper\Bootstrap; + +$objectManager = Bootstrap::getObjectManager(); +/** @var WebsiteRepositoryInterface $websiteRepository */ +$websiteRepository = $objectManager->get(WebsiteRepositoryInterface::class); +$baseWebsiteId = $websiteRepository->get('base')->getId(); +$ip = ip2long('127.0.0.1'); +$updateDatetime = new \DateTime('-0.5 hours'); +/** @var SendFriendResource $sendFriendResource */ +$sendFriendResource = $objectManager->get(SendFriendResource::class); +$sendFriendResource->addSendItem($ip, $updateDatetime->getTimestamp(), $baseWebsiteId); diff --git a/dev/tests/integration/testsuite/Magento/SendFriend/_files/sendfriend_log_record_half_hour_before_rollback.php b/dev/tests/integration/testsuite/Magento/SendFriend/_files/sendfriend_log_record_half_hour_before_rollback.php new file mode 100644 index 0000000000000..9a700f20bf92c --- /dev/null +++ b/dev/tests/integration/testsuite/Magento/SendFriend/_files/sendfriend_log_record_half_hour_before_rollback.php @@ -0,0 +1,13 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +declare(strict_types=1); + +use Magento\TestFramework\Helper\Bootstrap; +use Magento\TestFramework\SendFriend\Model\DeleteLogRowsByIp; + +/** @var DeleteLogRowsByIp $deleteLogRowsByIp */ +$deleteLogRowsByIp = Bootstrap::getObjectManager()->get(DeleteLogRowsByIp::class); +$deleteLogRowsByIp->execute('127.0.0.1'); diff --git a/dev/tests/integration/testsuite/Magento/Setup/Console/Command/_files/root/app/code/Magento/A/registration.php b/dev/tests/integration/testsuite/Magento/Setup/Console/Command/_files/root/app/code/Magento/A/registration.php index f4f907d8814f0..990c43e2ae22b 100644 --- a/dev/tests/integration/testsuite/Magento/Setup/Console/Command/_files/root/app/code/Magento/A/registration.php +++ b/dev/tests/integration/testsuite/Magento/Setup/Console/Command/_files/root/app/code/Magento/A/registration.php @@ -4,6 +4,6 @@ * See COPYING.txt for license details. */ -use \Magento\Framework\Component\ComponentRegistrar; +use Magento\Framework\Component\ComponentRegistrar; ComponentRegistrar::register(ComponentRegistrar::MODULE, 'Magento_A', __DIR__); diff --git a/dev/tests/integration/testsuite/Magento/Setup/Console/Command/_files/root/app/code/Magento/B/registration.php b/dev/tests/integration/testsuite/Magento/Setup/Console/Command/_files/root/app/code/Magento/B/registration.php index e5746b1350c08..8a615308ffafd 100644 --- a/dev/tests/integration/testsuite/Magento/Setup/Console/Command/_files/root/app/code/Magento/B/registration.php +++ b/dev/tests/integration/testsuite/Magento/Setup/Console/Command/_files/root/app/code/Magento/B/registration.php @@ -4,6 +4,6 @@ * See COPYING.txt for license details. */ -use \Magento\Framework\Component\ComponentRegistrar; +use Magento\Framework\Component\ComponentRegistrar; ComponentRegistrar::register(ComponentRegistrar::MODULE, 'Magento_B', __DIR__); diff --git a/dev/tests/integration/testsuite/Magento/Setup/Console/Command/_files/root/app/code/Magento/C/registration.php b/dev/tests/integration/testsuite/Magento/Setup/Console/Command/_files/root/app/code/Magento/C/registration.php index 6e987dcef353e..34a4e9f6be45b 100644 --- a/dev/tests/integration/testsuite/Magento/Setup/Console/Command/_files/root/app/code/Magento/C/registration.php +++ b/dev/tests/integration/testsuite/Magento/Setup/Console/Command/_files/root/app/code/Magento/C/registration.php @@ -4,6 +4,6 @@ * See COPYING.txt for license details. */ -use \Magento\Framework\Component\ComponentRegistrar; +use Magento\Framework\Component\ComponentRegistrar; ComponentRegistrar::register(ComponentRegistrar::MODULE, 'Magento_C', __DIR__); diff --git a/dev/tests/integration/testsuite/Magento/Setup/Console/Command/_files/root/app/code/Magento/D/registration.php b/dev/tests/integration/testsuite/Magento/Setup/Console/Command/_files/root/app/code/Magento/D/registration.php index f243f094d7a10..34b6e22067d67 100644 --- a/dev/tests/integration/testsuite/Magento/Setup/Console/Command/_files/root/app/code/Magento/D/registration.php +++ b/dev/tests/integration/testsuite/Magento/Setup/Console/Command/_files/root/app/code/Magento/D/registration.php @@ -4,6 +4,6 @@ * See COPYING.txt for license details. */ -use \Magento\Framework\Component\ComponentRegistrar; +use Magento\Framework\Component\ComponentRegistrar; ComponentRegistrar::register(ComponentRegistrar::MODULE, 'Magento_D', __DIR__); diff --git a/dev/tests/integration/testsuite/Magento/Setup/Controller/UrlCheckTest.php b/dev/tests/integration/testsuite/Magento/Setup/Controller/UrlCheckTest.php index c5c54b8c1dd3b..80796c0294958 100644 --- a/dev/tests/integration/testsuite/Magento/Setup/Controller/UrlCheckTest.php +++ b/dev/tests/integration/testsuite/Magento/Setup/Controller/UrlCheckTest.php @@ -6,8 +6,8 @@ namespace Magento\Setup\Controller; use Magento\TestFramework\Helper\Bootstrap; -use Zend\Stdlib\RequestInterface as Request; -use Zend\View\Model\JsonModel; +use Laminas\Stdlib\RequestInterface as Request; +use Laminas\View\Model\JsonModel; class UrlCheckTest extends \PHPUnit\Framework\TestCase { diff --git a/dev/tests/integration/testsuite/Magento/Setup/Model/ConfigOptionsListCollectorTest.php b/dev/tests/integration/testsuite/Magento/Setup/Model/ConfigOptionsListCollectorTest.php index 455d6436f724d..c7868d90af7a4 100644 --- a/dev/tests/integration/testsuite/Magento/Setup/Model/ConfigOptionsListCollectorTest.php +++ b/dev/tests/integration/testsuite/Magento/Setup/Model/ConfigOptionsListCollectorTest.php @@ -39,7 +39,7 @@ public function testCollectOptionsLists() ] ); - $serviceLocator = $this->getMockForAbstractClass(\Zend\ServiceManager\ServiceLocatorInterface::class); + $serviceLocator = $this->getMockForAbstractClass(\Laminas\ServiceManager\ServiceLocatorInterface::class); $serviceLocator->expects($this->once()) ->method('get') diff --git a/dev/tests/integration/testsuite/Magento/Setup/Model/ObjectManagerProviderTest.php b/dev/tests/integration/testsuite/Magento/Setup/Model/ObjectManagerProviderTest.php index a80da16be67eb..d66329761336b 100644 --- a/dev/tests/integration/testsuite/Magento/Setup/Model/ObjectManagerProviderTest.php +++ b/dev/tests/integration/testsuite/Magento/Setup/Model/ObjectManagerProviderTest.php @@ -11,7 +11,7 @@ use PHPUnit\Framework\TestCase; use PHPUnit_Framework_MockObject_MockObject; use Symfony\Component\Console\Application; -use Zend\ServiceManager\ServiceLocatorInterface; +use Laminas\ServiceManager\ServiceLocatorInterface; /** * Tests ObjectManagerProvider diff --git a/dev/tests/integration/testsuite/Magento/Setup/Model/_files/testSkeleton/composer.lock b/dev/tests/integration/testsuite/Magento/Setup/Model/_files/testSkeleton/composer.lock index 48fa6d0d0cd34..4027a0f2c41c3 100644 --- a/dev/tests/integration/testsuite/Magento/Setup/Model/_files/testSkeleton/composer.lock +++ b/dev/tests/integration/testsuite/Magento/Setup/Model/_files/testSkeleton/composer.lock @@ -887,10 +887,6 @@ "app/.htaccess", "app/.htaccess" ], - [ - "app/functions.php", - "app/functions.php" - ], [ "app/autoload.php", "app/autoload.php" @@ -4057,26 +4053,26 @@ "oyejorge/less.php": "1.7.0.3", "php": "~5.5.0|~5.6.0|~7.0.0", "tubalmartin/cssmin": "2.4.8-p4", - "zendframework/zend-code": "2.3.1", - "zendframework/zend-config": "2.3.1", - "zendframework/zend-console": "2.3.1", - "zendframework/zend-di": "2.3.1", - "zendframework/zend-eventmanager": "2.3.1", - "zendframework/zend-form": "2.3.1", - "zendframework/zend-http": "2.3.1", - "zendframework/zend-json": "2.3.1", - "zendframework/zend-log": "2.3.1", - "zendframework/zend-modulemanager": "2.3.1", - "zendframework/zend-mvc": "2.3.1", - "zendframework/zend-serializer": "2.3.1", - "zendframework/zend-server": "2.3.1", - "zendframework/zend-servicemanager": "2.3.1", - "zendframework/zend-soap": "2.3.1", - "zendframework/zend-stdlib": "2.3.1", - "zendframework/zend-text": "2.3.1", - "zendframework/zend-uri": "2.3.1", - "zendframework/zend-validator": "2.3.1", - "zendframework/zend-view": "2.3.1" + "laminas/laminas-code": "2.3.1", + "laminas/laminas-config": "2.3.1", + "laminas/laminas-console": "2.3.1", + "laminas/laminas-di": "2.3.1", + "laminas/laminas-eventmanager": "2.3.1", + "laminas/laminas-form": "2.3.1", + "laminas/laminas-http": "2.3.1", + "laminas/laminas-json": "2.3.1", + "laminas/laminas-log": "2.3.1", + "laminas/laminas-modulemanager": "2.3.1", + "laminas/laminas-mvc": "2.3.1", + "laminas/laminas-serializer": "2.3.1", + "laminas/laminas-server": "2.3.1", + "laminas/laminas-servicemanager": "2.3.1", + "laminas/laminas-soap": "2.3.1", + "laminas/laminas-stdlib": "2.3.1", + "laminas/laminas-text": "2.3.1", + "laminas/laminas-uri": "2.3.1", + "laminas/laminas-validator": "2.3.1", + "laminas/laminas-view": "2.3.1" }, "require-dev": { "ext-ctype": "*", @@ -4696,33 +4692,33 @@ "time": "2014-09-22 08:08:50" }, { - "name": "zendframework/zend-code", + "name": "laminas/laminas-code", "version": "2.3.1", "source": { "type": "git", - "url": "https://github.com/zendframework/zend-code.git", + "url": "https://github.com/laminas/laminas-code.git", "reference": "a64e90c9ee8c939335ee7e21e39e3342c0e54526" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/zendframework/zend-code/zipball/a64e90c9ee8c939335ee7e21e39e3342c0e54526", + "url": "https://api.github.com/repos/laminas/laminas-code/zipball/a64e90c9ee8c939335ee7e21e39e3342c0e54526", "reference": "a64e90c9ee8c939335ee7e21e39e3342c0e54526", "shasum": "" }, "require": { "php": ">=5.3.23", - "zendframework/zend-eventmanager": "self.version" + "laminas/laminas-eventmanager": "self.version" }, "require-dev": { "doctrine/common": ">=2.1", "fabpot/php-cs-fixer": "1.7.*", "phpunit/phpunit": "~4.0", "satooshi/php-coveralls": "dev-master", - "zendframework/zend-stdlib": "self.version" + "laminas/laminas-stdlib": "self.version" }, "suggest": { "doctrine/common": "Doctrine\\Common >=2.1 for annotation features", - "zendframework/zend-stdlib": "Zend\\Stdlib component" + "laminas/laminas-stdlib": "Laminas\\Stdlib component" }, "type": "library", "extra": { @@ -4733,7 +4729,7 @@ }, "autoload": { "psr-4": { - "Zend\\Code\\": "src/" + "Laminas\\Code\\": "src/" } }, "notification-url": "https://packagist.org/downloads/", @@ -4741,7 +4737,7 @@ "BSD-3-Clause" ], "description": "provides facilities to generate arbitrary code using an object oriented interface", - "homepage": "https://github.com/zendframework/zend-code", + "homepage": "https://github.com/laminas/laminas-code", "keywords": [ "code", "zf2" @@ -4749,37 +4745,37 @@ "time": "2014-04-15 14:47:18" }, { - "name": "zendframework/zend-config", + "name": "laminas/laminas-config", "version": "2.3.1", "source": { "type": "git", - "url": "https://github.com/zendframework/zend-config.git", + "url": "https://github.com/laminas/laminas-config.git", "reference": "698c139707380b29fd09791ec1c21b837e9a3f15" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/zendframework/zend-config/zipball/698c139707380b29fd09791ec1c21b837e9a3f15", + "url": "https://api.github.com/repos/laminas/laminas-config/zipball/698c139707380b29fd09791ec1c21b837e9a3f15", "reference": "698c139707380b29fd09791ec1c21b837e9a3f15", "shasum": "" }, "require": { "php": ">=5.3.23", - "zendframework/zend-stdlib": "self.version" + "laminas/laminas-stdlib": "self.version" }, "require-dev": { "fabpot/php-cs-fixer": "1.7.*", "phpunit/phpunit": "~4.0", "satooshi/php-coveralls": "dev-master", - "zendframework/zend-filter": "self.version", - "zendframework/zend-i18n": "self.version", - "zendframework/zend-json": "self.version", - "zendframework/zend-servicemanager": "self.version" + "laminas/laminas-filter": "self.version", + "laminas/laminas-i18n": "self.version", + "laminas/laminas-json": "self.version", + "laminas/laminas-servicemanager": "self.version" }, "suggest": { - "zendframework/zend-filter": "Zend\\Filter component", - "zendframework/zend-i18n": "Zend\\I18n component", - "zendframework/zend-json": "Zend\\Json to use the Json reader or writer classes", - "zendframework/zend-servicemanager": "Zend\\ServiceManager for use with the Config Factory to retrieve reader and writer instances" + "laminas/laminas-filter": "Laminas\\Filter component", + "laminas/laminas-i18n": "Laminas\\I18n component", + "laminas/laminas-json": "Laminas\\Json to use the Json reader or writer classes", + "laminas/laminas-servicemanager": "Laminas\\ServiceManager for use with the Config Factory to retrieve reader and writer instances" }, "type": "library", "extra": { @@ -4790,7 +4786,7 @@ }, "autoload": { "psr-4": { - "Zend\\Config\\": "src/" + "Laminas\\Config\\": "src/" } }, "notification-url": "https://packagist.org/downloads/", @@ -4798,7 +4794,7 @@ "BSD-3-Clause" ], "description": "provides a nested object property based user interface for accessing this configuration data within application code", - "homepage": "https://github.com/zendframework/zend-config", + "homepage": "https://github.com/laminas/laminas-config", "keywords": [ "config", "zf2" @@ -4806,22 +4802,22 @@ "time": "2014-04-15 13:59:53" }, { - "name": "zendframework/zend-console", + "name": "laminas/laminas-console", "version": "2.3.1", "source": { "type": "git", - "url": "https://github.com/zendframework/zend-console.git", + "url": "https://github.com/laminas/laminas-console.git", "reference": "4253efd75a022d97ef326eac38a06c8eebb48a37" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/zendframework/zend-console/zipball/4253efd75a022d97ef326eac38a06c8eebb48a37", + "url": "https://api.github.com/repos/laminas/laminas-console/zipball/4253efd75a022d97ef326eac38a06c8eebb48a37", "reference": "4253efd75a022d97ef326eac38a06c8eebb48a37", "shasum": "" }, "require": { "php": ">=5.3.23", - "zendframework/zend-stdlib": "self.version" + "laminas/laminas-stdlib": "self.version" }, "require-dev": { "fabpot/php-cs-fixer": "1.7.*", @@ -4829,8 +4825,8 @@ "satooshi/php-coveralls": "dev-master" }, "suggest": { - "zendframework/zend-filter": "To support DefaultRouteMatcher usage", - "zendframework/zend-validator": "To support DefaultRouteMatcher usage" + "laminas/laminas-filter": "To support DefaultRouteMatcher usage", + "laminas/laminas-validator": "To support DefaultRouteMatcher usage" }, "type": "library", "extra": { @@ -4841,14 +4837,14 @@ }, "autoload": { "psr-4": { - "Zend\\Console\\": "src/" + "Laminas\\Console\\": "src/" } }, "notification-url": "https://packagist.org/downloads/", "license": [ "BSD-3-Clause" ], - "homepage": "https://github.com/zendframework/zend-console", + "homepage": "https://github.com/laminas/laminas-console", "keywords": [ "console", "zf2" @@ -4856,32 +4852,32 @@ "time": "2014-04-14 19:50:30" }, { - "name": "zendframework/zend-di", + "name": "laminas/laminas-di", "version": "2.3.1", "source": { "type": "git", - "url": "https://github.com/zendframework/zend-di.git", + "url": "https://github.com/laminas/laminas-di.git", "reference": "7502db10f8023bfd4e860ce83c9cdeda0db42c31" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/zendframework/zend-di/zipball/7502db10f8023bfd4e860ce83c9cdeda0db42c31", + "url": "https://api.github.com/repos/laminas/laminas-di/zipball/7502db10f8023bfd4e860ce83c9cdeda0db42c31", "reference": "7502db10f8023bfd4e860ce83c9cdeda0db42c31", "shasum": "" }, "require": { "php": ">=5.3.23", - "zendframework/zend-code": "self.version", - "zendframework/zend-stdlib": "self.version" + "laminas/laminas-code": "self.version", + "laminas/laminas-stdlib": "self.version" }, "require-dev": { "fabpot/php-cs-fixer": "1.7.*", "phpunit/phpunit": "~4.0", "satooshi/php-coveralls": "dev-master", - "zendframework/zend-servicemanager": "self.version" + "laminas/laminas-servicemanager": "self.version" }, "suggest": { - "zendframework/zend-servicemanager": "Zend\\ServiceManager component" + "laminas/laminas-servicemanager": "Laminas\\ServiceManager component" }, "type": "library", "extra": { @@ -4892,14 +4888,14 @@ }, "autoload": { "psr-4": { - "Zend\\Di\\": "src/" + "Laminas\\Di\\": "src/" } }, "notification-url": "https://packagist.org/downloads/", "license": [ "BSD-3-Clause" ], - "homepage": "https://github.com/zendframework/zend-di", + "homepage": "https://github.com/laminas/laminas-di", "keywords": [ "di", "zf2" @@ -4907,16 +4903,16 @@ "time": "2014-04-14 19:50:30" }, { - "name": "zendframework/zend-escaper", + "name": "laminas/laminas-escaper", "version": "2.3.1", "source": { "type": "git", - "url": "https://github.com/zendframework/zend-escaper.git", + "url": "https://github.com/laminas/laminas-escaper.git", "reference": "2b2fcb6141cc2a2c6cc0c596e82771c72ef4ddc1" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/zendframework/zend-escaper/zipball/2b2fcb6141cc2a2c6cc0c596e82771c72ef4ddc1", + "url": "https://api.github.com/repos/laminas/laminas-escaper/zipball/2b2fcb6141cc2a2c6cc0c596e82771c72ef4ddc1", "reference": "2b2fcb6141cc2a2c6cc0c596e82771c72ef4ddc1", "shasum": "" }, @@ -4937,14 +4933,14 @@ }, "autoload": { "psr-4": { - "Zend\\Escaper\\": "src/" + "Laminas\\Escaper\\": "src/" } }, "notification-url": "https://packagist.org/downloads/", "license": [ "BSD-3-Clause" ], - "homepage": "https://github.com/zendframework/zend-escaper", + "homepage": "https://github.com/laminas/laminas-escaper", "keywords": [ "escaper", "zf2" @@ -4952,22 +4948,22 @@ "time": "2014-04-14 19:50:30" }, { - "name": "zendframework/zend-eventmanager", + "name": "laminas/laminas-eventmanager", "version": "2.3.1", "source": { "type": "git", - "url": "https://github.com/zendframework/zend-eventmanager.git", + "url": "https://github.com/laminas/laminas-eventmanager.git", "reference": "ec158e89d0290f988a29ccd6bf179b561efbb702" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/zendframework/zend-eventmanager/zipball/ec158e89d0290f988a29ccd6bf179b561efbb702", + "url": "https://api.github.com/repos/laminas/laminas-eventmanager/zipball/ec158e89d0290f988a29ccd6bf179b561efbb702", "reference": "ec158e89d0290f988a29ccd6bf179b561efbb702", "shasum": "" }, "require": { "php": ">=5.3.23", - "zendframework/zend-stdlib": "self.version" + "laminas/laminas-stdlib": "self.version" }, "require-dev": { "fabpot/php-cs-fixer": "1.7.*", @@ -4983,7 +4979,7 @@ }, "autoload": { "psr-4": { - "Zend\\EventManager\\": "src/" + "Laminas\\EventManager\\": "src/" } }, "notification-url": "https://packagist.org/downloads/", @@ -4998,36 +4994,36 @@ "time": "2014-04-15 13:59:53" }, { - "name": "zendframework/zend-filter", + "name": "laminas/laminas-filter", "version": "2.3.1", "source": { "type": "git", - "url": "https://github.com/zendframework/zend-filter.git", + "url": "https://github.com/laminas/laminas-filter.git", "reference": "307fe694659e08ffd710c70e4db8bc60187bcc84" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/zendframework/zend-filter/zipball/307fe694659e08ffd710c70e4db8bc60187bcc84", + "url": "https://api.github.com/repos/laminas/laminas-filter/zipball/307fe694659e08ffd710c70e4db8bc60187bcc84", "reference": "307fe694659e08ffd710c70e4db8bc60187bcc84", "shasum": "" }, "require": { "php": ">=5.3.23", - "zendframework/zend-stdlib": "self.version" + "laminas/laminas-stdlib": "self.version" }, "require-dev": { "fabpot/php-cs-fixer": "1.7.*", "phpunit/phpunit": "~4.0", "satooshi/php-coveralls": "dev-master", - "zendframework/zend-crypt": "self.version", - "zendframework/zend-servicemanager": "self.version", - "zendframework/zend-uri": "self.version" + "laminas/laminas-crypt": "self.version", + "laminas/laminas-servicemanager": "self.version", + "laminas/laminas-uri": "self.version" }, "suggest": { - "zendframework/zend-crypt": "Zend\\Crypt component", - "zendframework/zend-i18n": "Zend\\I18n component", - "zendframework/zend-servicemanager": "Zend\\ServiceManager component", - "zendframework/zend-uri": "Zend\\Uri component for UriNormalize filter" + "laminas/laminas-crypt": "Laminas\\Crypt component", + "laminas/laminas-i18n": "Laminas\\I18n component", + "laminas/laminas-servicemanager": "Laminas\\ServiceManager component", + "laminas/laminas-uri": "Laminas\\Uri component for UriNormalize filter" }, "type": "library", "extra": { @@ -5038,7 +5034,7 @@ }, "autoload": { "psr-4": { - "Zend\\Filter\\": "src/" + "Laminas\\Filter\\": "src/" } }, "notification-url": "https://packagist.org/downloads/", @@ -5046,7 +5042,7 @@ "BSD-3-Clause" ], "description": "provides a set of commonly needed data filters", - "homepage": "https://github.com/zendframework/zend-filter", + "homepage": "https://github.com/laminas/laminas-filter", "keywords": [ "filter", "zf2" @@ -5054,48 +5050,48 @@ "time": "2014-04-14 19:50:30" }, { - "name": "zendframework/zend-form", + "name": "laminas/laminas-form", "version": "2.3.1", "source": { "type": "git", - "url": "https://github.com/zendframework/zend-form.git", + "url": "https://github.com/laminas/laminas-form.git", "reference": "d7a1f5bc4626b1df990391502a868b28c37ba65d" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/zendframework/zend-form/zipball/d7a1f5bc4626b1df990391502a868b28c37ba65d", + "url": "https://api.github.com/repos/laminas/laminas-form/zipball/d7a1f5bc4626b1df990391502a868b28c37ba65d", "reference": "d7a1f5bc4626b1df990391502a868b28c37ba65d", "shasum": "" }, "require": { "php": ">=5.3.23", - "zendframework/zend-inputfilter": "self.version", - "zendframework/zend-stdlib": "self.version" + "laminas/laminas-inputfilter": "self.version", + "laminas/laminas-stdlib": "self.version" }, "require-dev": { "fabpot/php-cs-fixer": "1.7.*", "phpunit/phpunit": "~4.0", "satooshi/php-coveralls": "dev-master", - "zendframework/zend-captcha": "self.version", - "zendframework/zend-code": "self.version", - "zendframework/zend-eventmanager": "self.version", - "zendframework/zend-filter": "self.version", - "zendframework/zend-i18n": "self.version", - "zendframework/zend-servicemanager": "self.version", - "zendframework/zend-validator": "self.version", - "zendframework/zend-view": "self.version", - "zendframework/zendservice-recaptcha": "*" + "laminas/laminas-captcha": "self.version", + "laminas/laminas-code": "self.version", + "laminas/laminas-eventmanager": "self.version", + "laminas/laminas-filter": "self.version", + "laminas/laminas-i18n": "self.version", + "laminas/laminas-servicemanager": "self.version", + "laminas/laminas-validator": "self.version", + "laminas/laminas-view": "self.version", + "laminas/laminas-recaptcha": "*" }, "suggest": { - "zendframework/zend-captcha": "Zend\\Captcha component", - "zendframework/zend-code": "Zend\\Code component", - "zendframework/zend-eventmanager": "Zend\\EventManager component", - "zendframework/zend-filter": "Zend\\Filter component", - "zendframework/zend-i18n": "Zend\\I18n component", - "zendframework/zend-servicemanager": "Zend\\ServiceManager component", - "zendframework/zend-validator": "Zend\\Validator component", - "zendframework/zend-view": "Zend\\View component", - "zendframework/zendservice-recaptcha": "ZendService\\ReCaptcha component" + "laminas/laminas-captcha": "Laminas\\Captcha component", + "laminas/laminas-code": "Laminas\\Code component", + "laminas/laminas-eventmanager": "Laminas\\EventManager component", + "laminas/laminas-filter": "Laminas\\Filter component", + "laminas/laminas-i18n": "Laminas\\I18n component", + "laminas/laminas-servicemanager": "Laminas\\ServiceManager component", + "laminas/laminas-validator": "Laminas\\Validator component", + "laminas/laminas-view": "Laminas\\View component", + "laminas/laminas-recaptcha": "Laminas\\ReCaptcha component" }, "type": "library", "extra": { @@ -5106,14 +5102,14 @@ }, "autoload": { "psr-4": { - "Zend\\Form\\": "src/" + "Laminas\\Form\\": "src/" } }, "notification-url": "https://packagist.org/downloads/", "license": [ "BSD-3-Clause" ], - "homepage": "https://github.com/zendframework/zend-form", + "homepage": "https://github.com/laminas/laminas-form", "keywords": [ "form", "zf2" @@ -5121,25 +5117,25 @@ "time": "2014-04-15 14:36:41" }, { - "name": "zendframework/zend-http", + "name": "laminas/laminas-http", "version": "2.3.1", "source": { "type": "git", - "url": "https://github.com/zendframework/zend-http.git", + "url": "https://github.com/laminas/laminas-http.git", "reference": "869ce7bdf60727e14d85c305d2948fbe831c3534" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/zendframework/zend-http/zipball/869ce7bdf60727e14d85c305d2948fbe831c3534", + "url": "https://api.github.com/repos/laminas/laminas-http/zipball/869ce7bdf60727e14d85c305d2948fbe831c3534", "reference": "869ce7bdf60727e14d85c305d2948fbe831c3534", "shasum": "" }, "require": { "php": ">=5.3.23", - "zendframework/zend-loader": "self.version", - "zendframework/zend-stdlib": "self.version", - "zendframework/zend-uri": "self.version", - "zendframework/zend-validator": "self.version" + "laminas/laminas-loader": "self.version", + "laminas/laminas-stdlib": "self.version", + "laminas/laminas-uri": "self.version", + "laminas/laminas-validator": "self.version" }, "require-dev": { "fabpot/php-cs-fixer": "1.7.*", @@ -5155,7 +5151,7 @@ }, "autoload": { "psr-4": { - "Zend\\Http\\": "src/" + "Laminas\\Http\\": "src/" } }, "notification-url": "https://packagist.org/downloads/", @@ -5163,7 +5159,7 @@ "BSD-3-Clause" ], "description": "provides an easy interface for performing Hyper-Text Transfer Protocol (HTTP) requests", - "homepage": "https://github.com/zendframework/zend-http", + "homepage": "https://github.com/laminas/laminas-http", "keywords": [ "http", "zf2" @@ -5171,33 +5167,33 @@ "time": "2014-04-15 13:59:53" }, { - "name": "zendframework/zend-inputfilter", + "name": "laminas/laminas-inputfilter", "version": "2.3.1", "source": { "type": "git", - "url": "https://github.com/zendframework/zend-inputfilter.git", + "url": "https://github.com/laminas/laminas-inputfilter.git", "reference": "abca740015a856d03542f5b6c535b8874f84b622" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/zendframework/zend-inputfilter/zipball/abca740015a856d03542f5b6c535b8874f84b622", + "url": "https://api.github.com/repos/laminas/laminas-inputfilter/zipball/abca740015a856d03542f5b6c535b8874f84b622", "reference": "abca740015a856d03542f5b6c535b8874f84b622", "shasum": "" }, "require": { "php": ">=5.3.23", - "zendframework/zend-filter": "self.version", - "zendframework/zend-stdlib": "self.version", - "zendframework/zend-validator": "self.version" + "laminas/laminas-filter": "self.version", + "laminas/laminas-stdlib": "self.version", + "laminas/laminas-validator": "self.version" }, "require-dev": { "fabpot/php-cs-fixer": "1.7.*", "phpunit/phpunit": "~4.0", "satooshi/php-coveralls": "dev-master", - "zendframework/zend-servicemanager": "self.version" + "laminas/laminas-servicemanager": "self.version" }, "suggest": { - "zendframework/zend-servicemanager": "To support plugin manager support" + "laminas/laminas-servicemanager": "To support plugin manager support" }, "type": "library", "extra": { @@ -5208,7 +5204,7 @@ }, "autoload": { "psr-4": { - "Zend\\InputFilter\\": "src/" + "Laminas\\InputFilter\\": "src/" } }, "notification-url": "https://packagist.org/downloads/", @@ -5223,34 +5219,34 @@ "time": "2014-04-15 13:59:53" }, { - "name": "zendframework/zend-json", + "name": "laminas/laminas-json", "version": "2.3.1", "source": { "type": "git", - "url": "https://github.com/zendframework/zend-json.git", + "url": "https://github.com/laminas/laminas-json.git", "reference": "5284687fc3aeab27961d2e17ada08973ae6daafe" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/zendframework/zend-json/zipball/5284687fc3aeab27961d2e17ada08973ae6daafe", + "url": "https://api.github.com/repos/laminas/laminas-json/zipball/5284687fc3aeab27961d2e17ada08973ae6daafe", "reference": "5284687fc3aeab27961d2e17ada08973ae6daafe", "shasum": "" }, "require": { "php": ">=5.3.23", - "zendframework/zend-stdlib": "self.version" + "laminas/laminas-stdlib": "self.version" }, "require-dev": { "fabpot/php-cs-fixer": "1.7.*", "phpunit/phpunit": "~4.0", "satooshi/php-coveralls": "dev-master", - "zendframework/zend-http": "self.version", - "zendframework/zend-server": "self.version" + "laminas/laminas-http": "self.version", + "laminas/laminas-server": "self.version" }, "suggest": { - "zendframework/zend-http": "Zend\\Http component", - "zendframework/zend-server": "Zend\\Server component", - "zendframework/zendxml": "To support Zend\\Json\\Json::fromXml() usage" + "laminas/laminas-http": "Laminas\\Http component", + "laminas/laminas-server": "Laminas\\Server component", + "zendframework/zendxml": "To support Laminas\\Json\\Json::fromXml() usage" }, "type": "library", "extra": { @@ -5261,7 +5257,7 @@ }, "autoload": { "psr-4": { - "Zend\\Json\\": "src/" + "Laminas\\Json\\": "src/" } }, "notification-url": "https://packagist.org/downloads/", @@ -5269,7 +5265,7 @@ "BSD-3-Clause" ], "description": "provides convenience methods for serializing native PHP to JSON and decoding JSON to native PHP", - "homepage": "https://github.com/zendframework/zend-json", + "homepage": "https://github.com/laminas/laminas-json", "keywords": [ "json", "zf2" @@ -5277,16 +5273,16 @@ "time": "2014-04-14 19:50:30" }, { - "name": "zendframework/zend-loader", + "name": "laminas/laminas-loader", "version": "2.3.1", "source": { "type": "git", - "url": "https://github.com/zendframework/zend-loader.git", + "url": "https://github.com/laminas/laminas-loader.git", "reference": "5e0bd7e28c644078685f525cf8ae03d9a01ae292" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/zendframework/zend-loader/zipball/5e0bd7e28c644078685f525cf8ae03d9a01ae292", + "url": "https://api.github.com/repos/laminas/laminas-loader/zipball/5e0bd7e28c644078685f525cf8ae03d9a01ae292", "reference": "5e0bd7e28c644078685f525cf8ae03d9a01ae292", "shasum": "" }, @@ -5307,14 +5303,14 @@ }, "autoload": { "psr-4": { - "Zend\\Loader\\": "src/" + "Laminas\\Loader\\": "src/" } }, "notification-url": "https://packagist.org/downloads/", "license": [ "BSD-3-Clause" ], - "homepage": "https://github.com/zendframework/zend-loader", + "homepage": "https://github.com/laminas/laminas-loader", "keywords": [ "loader", "zf2" @@ -5322,41 +5318,41 @@ "time": "2014-04-14 19:50:30" }, { - "name": "zendframework/zend-log", + "name": "laminas/laminas-log", "version": "2.3.1", "source": { "type": "git", - "url": "https://github.com/zendframework/zend-log.git", + "url": "https://github.com/laminas/laminas-log.git", "reference": "217611433f5cb56d4420a1db8f164e5db85d815d" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/zendframework/zend-log/zipball/217611433f5cb56d4420a1db8f164e5db85d815d", + "url": "https://api.github.com/repos/laminas/laminas-log/zipball/217611433f5cb56d4420a1db8f164e5db85d815d", "reference": "217611433f5cb56d4420a1db8f164e5db85d815d", "shasum": "" }, "require": { "php": ">=5.3.23", - "zendframework/zend-servicemanager": "self.version", - "zendframework/zend-stdlib": "self.version" + "laminas/laminas-servicemanager": "self.version", + "laminas/laminas-stdlib": "self.version" }, "require-dev": { "fabpot/php-cs-fixer": "1.7.*", "phpunit/phpunit": "~4.0", "satooshi/php-coveralls": "dev-master", - "zendframework/zend-console": "self.version", - "zendframework/zend-db": "self.version", - "zendframework/zend-escaper": "self.version", - "zendframework/zend-mail": "self.version", - "zendframework/zend-validator": "self.version" + "laminas/laminas-console": "self.version", + "laminas/laminas-db": "self.version", + "laminas/laminas-escaper": "self.version", + "laminas/laminas-mail": "self.version", + "laminas/laminas-validator": "self.version" }, "suggest": { "ext-mongo": "*", - "zendframework/zend-console": "Zend\\Console component", - "zendframework/zend-db": "Zend\\Db component", - "zendframework/zend-escaper": "Zend\\Escaper component, for use in the XML formatter", - "zendframework/zend-mail": "Zend\\Mail component", - "zendframework/zend-validator": "Zend\\Validator component" + "laminas/laminas-console": "Laminas\\Console component", + "laminas/laminas-db": "Laminas\\Db component", + "laminas/laminas-escaper": "Laminas\\Escaper component, for use in the XML formatter", + "laminas/laminas-mail": "Laminas\\Mail component", + "laminas/laminas-validator": "Laminas\\Validator component" }, "type": "library", "extra": { @@ -5367,7 +5363,7 @@ }, "autoload": { "psr-4": { - "Zend\\Log\\": "src/" + "Laminas\\Log\\": "src/" } }, "notification-url": "https://packagist.org/downloads/", @@ -5375,7 +5371,7 @@ "BSD-3-Clause" ], "description": "component for general purpose logging", - "homepage": "https://github.com/zendframework/zend-log", + "homepage": "https://github.com/laminas/laminas-log", "keywords": [ "log", "logging", @@ -5384,16 +5380,16 @@ "time": "2014-04-15 13:59:53" }, { - "name": "zendframework/zend-math", + "name": "laminas/laminas-math", "version": "2.3.1", "source": { "type": "git", - "url": "https://github.com/zendframework/zend-math.git", + "url": "https://github.com/laminas/laminas-math.git", "reference": "63225fcebb196fc6e20094f5f01e9354779ec31e" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/zendframework/zend-math/zipball/63225fcebb196fc6e20094f5f01e9354779ec31e", + "url": "https://api.github.com/repos/laminas/laminas-math/zipball/63225fcebb196fc6e20094f5f01e9354779ec31e", "reference": "63225fcebb196fc6e20094f5f01e9354779ec31e", "shasum": "" }, @@ -5408,8 +5404,8 @@ "suggest": { "ext-bcmath": "If using the bcmath functionality", "ext-gmp": "If using the gmp functionality", - "ircmaxell/random-lib": "Fallback random byte generator for Zend\\Math\\Rand if OpenSSL/Mcrypt extensions are unavailable", - "zendframework/zend-servicemanager": ">= current version, if using the BigInteger::factory functionality" + "ircmaxell/random-lib": "Fallback random byte generator for Laminas\\Math\\Rand if OpenSSL/Mcrypt extensions are unavailable", + "laminas/laminas-servicemanager": ">= current version, if using the BigInteger::factory functionality" }, "type": "library", "extra": { @@ -5420,14 +5416,14 @@ }, "autoload": { "psr-4": { - "Zend\\Math\\": "src/" + "Laminas\\Math\\": "src/" } }, "notification-url": "https://packagist.org/downloads/", "license": [ "BSD-3-Clause" ], - "homepage": "https://github.com/zendframework/zend-math", + "homepage": "https://github.com/laminas/laminas-math", "keywords": [ "math", "zf2" @@ -5435,40 +5431,40 @@ "time": "2014-04-14 19:50:30" }, { - "name": "zendframework/zend-modulemanager", + "name": "laminas/laminas-modulemanager", "version": "2.3.1", "source": { "type": "git", - "url": "https://github.com/zendframework/zend-modulemanager.git", + "url": "https://github.com/laminas/laminas-modulemanager.git", "reference": "d4591b958e40b8f5ae8110d9b203331437aa19f2" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/zendframework/zend-modulemanager/zipball/d4591b958e40b8f5ae8110d9b203331437aa19f2", + "url": "https://api.github.com/repos/laminas/laminas-modulemanager/zipball/d4591b958e40b8f5ae8110d9b203331437aa19f2", "reference": "d4591b958e40b8f5ae8110d9b203331437aa19f2", "shasum": "" }, "require": { "php": ">=5.3.23", - "zendframework/zend-eventmanager": "self.version", - "zendframework/zend-stdlib": "self.version" + "laminas/laminas-eventmanager": "self.version", + "laminas/laminas-stdlib": "self.version" }, "require-dev": { "fabpot/php-cs-fixer": "1.7.*", "phpunit/phpunit": "~4.0", "satooshi/php-coveralls": "dev-master", - "zendframework/zend-config": "self.version", - "zendframework/zend-console": "self.version", - "zendframework/zend-loader": "self.version", - "zendframework/zend-mvc": "self.version", - "zendframework/zend-servicemanager": "self.version" + "laminas/laminas-config": "self.version", + "laminas/laminas-console": "self.version", + "laminas/laminas-loader": "self.version", + "laminas/laminas-mvc": "self.version", + "laminas/laminas-servicemanager": "self.version" }, "suggest": { - "zendframework/zend-config": "Zend\\Config component", - "zendframework/zend-console": "Zend\\Console component", - "zendframework/zend-loader": "Zend\\Loader component", - "zendframework/zend-mvc": "Zend\\Mvc component", - "zendframework/zend-servicemanager": "Zend\\ServiceManager component" + "laminas/laminas-config": "Laminas\\Config component", + "laminas/laminas-console": "Laminas\\Console component", + "laminas/laminas-loader": "Laminas\\Loader component", + "laminas/laminas-mvc": "Laminas\\Mvc component", + "laminas/laminas-servicemanager": "Laminas\\ServiceManager component" }, "type": "library", "extra": { @@ -5479,7 +5475,7 @@ }, "autoload": { "psr-4": { - "Zend\\ModuleManager\\": "src/" + "Laminas\\ModuleManager\\": "src/" } }, "notification-url": "https://packagist.org/downloads/", @@ -5494,70 +5490,70 @@ "time": "2014-04-14 19:50:30" }, { - "name": "zendframework/zend-mvc", + "name": "laminas/laminas-mvc", "version": "2.3.1", "source": { "type": "git", - "url": "https://github.com/zendframework/zend-mvc.git", + "url": "https://github.com/laminas/laminas-mvc.git", "reference": "ee76ddd009ecb0c507bb8ab396fbe719aea8f1ff" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/zendframework/zend-mvc/zipball/ee76ddd009ecb0c507bb8ab396fbe719aea8f1ff", + "url": "https://api.github.com/repos/laminas/laminas-mvc/zipball/ee76ddd009ecb0c507bb8ab396fbe719aea8f1ff", "reference": "ee76ddd009ecb0c507bb8ab396fbe719aea8f1ff", "shasum": "" }, "require": { "php": ">=5.3.23", - "zendframework/zend-eventmanager": "self.version", - "zendframework/zend-form": "self.version", - "zendframework/zend-servicemanager": "self.version", - "zendframework/zend-stdlib": "self.version" + "laminas/laminas-eventmanager": "self.version", + "laminas/laminas-form": "self.version", + "laminas/laminas-servicemanager": "self.version", + "laminas/laminas-stdlib": "self.version" }, "require-dev": { "fabpot/php-cs-fixer": "1.7.*", "phpunit/phpunit": "~4.0", "satooshi/php-coveralls": "dev-master", - "zendframework/zend-authentication": "self.version", - "zendframework/zend-console": "self.version", - "zendframework/zend-di": "self.version", - "zendframework/zend-filter": "self.version", - "zendframework/zend-form": "self.version", - "zendframework/zend-http": "self.version", - "zendframework/zend-i18n": "self.version", - "zendframework/zend-inputfilter": "self.version", - "zendframework/zend-json": "self.version", - "zendframework/zend-log": "self.version", - "zendframework/zend-modulemanager": "self.version", - "zendframework/zend-serializer": "self.version", - "zendframework/zend-session": "self.version", - "zendframework/zend-text": "self.version", - "zendframework/zend-uri": "self.version", - "zendframework/zend-validator": "self.version", + "laminas/laminas-authentication": "self.version", + "laminas/laminas-console": "self.version", + "laminas/laminas-di": "self.version", + "laminas/laminas-filter": "self.version", + "laminas/laminas-form": "self.version", + "laminas/laminas-http": "self.version", + "laminas/laminas-i18n": "self.version", + "laminas/laminas-inputfilter": "self.version", + "laminas/laminas-json": "self.version", + "laminas/laminas-log": "self.version", + "laminas/laminas-modulemanager": "self.version", + "laminas/laminas-serializer": "self.version", + "laminas/laminas-session": "self.version", + "laminas/laminas-text": "self.version", + "laminas/laminas-uri": "self.version", + "laminas/laminas-validator": "self.version", "zendframework/zend-version": "self.version", - "zendframework/zend-view": "self.version" + "laminas/laminas-view": "self.version" }, "suggest": { - "zendframework/zend-authentication": "Zend\\Authentication component for Identity plugin", - "zendframework/zend-config": "Zend\\Config component", - "zendframework/zend-console": "Zend\\Console component", - "zendframework/zend-di": "Zend\\Di component", - "zendframework/zend-filter": "Zend\\Filter component", - "zendframework/zend-form": "Zend\\Form component", - "zendframework/zend-http": "Zend\\Http component", - "zendframework/zend-i18n": "Zend\\I18n component for translatable segments", - "zendframework/zend-inputfilter": "Zend\\Inputfilter component", - "zendframework/zend-json": "Zend\\Json component", - "zendframework/zend-log": "Zend\\Log component", - "zendframework/zend-modulemanager": "Zend\\ModuleManager component", - "zendframework/zend-serializer": "Zend\\Serializer component", - "zendframework/zend-session": "Zend\\Session component for FlashMessenger, PRG, and FPRG plugins", - "zendframework/zend-stdlib": "Zend\\Stdlib component", - "zendframework/zend-text": "Zend\\Text component", - "zendframework/zend-uri": "Zend\\Uri component", - "zendframework/zend-validator": "Zend\\Validator component", + "laminas/laminas-authentication": "Laminas\\Authentication component for Identity plugin", + "laminas/laminas-config": "Laminas\\Config component", + "laminas/laminas-console": "Laminas\\Console component", + "laminas/laminas-di": "Laminas\\Di component", + "laminas/laminas-filter": "Laminas\\Filter component", + "laminas/laminas-form": "Laminas\\Form component", + "laminas/laminas-http": "Laminas\\Http component", + "laminas/laminas-i18n": "Laminas\\I18n component for translatable segments", + "laminas/laminas-inputfilter": "Zend\\Inputfilter component", + "laminas/laminas-json": "Laminas\\Json component", + "laminas/laminas-log": "Laminas\\Log component", + "laminas/laminas-modulemanager": "Laminas\\ModuleManager component", + "laminas/laminas-serializer": "Laminas\\Serializer component", + "laminas/laminas-session": "Laminas\\Session component for FlashMessenger, PRG, and FPRG plugins", + "laminas/laminas-stdlib": "Laminas\\Stdlib component", + "laminas/laminas-text": "Laminas\\Text component", + "laminas/laminas-uri": "Laminas\\Uri component", + "laminas/laminas-validator": "Laminas\\Validator component", "zendframework/zend-version": "Zend\\Version component", - "zendframework/zend-view": "Zend\\View component" + "laminas/laminas-view": "Laminas\\View component" }, "type": "library", "extra": { @@ -5568,14 +5564,14 @@ }, "autoload": { "psr-4": { - "Zend\\Mvc\\": "src/" + "Laminas\\Mvc\\": "src/" } }, "notification-url": "https://packagist.org/downloads/", "license": [ "BSD-3-Clause" ], - "homepage": "https://github.com/zendframework/zend-mvc", + "homepage": "https://github.com/laminas/laminas-mvc", "keywords": [ "mvc", "zf2" @@ -5583,33 +5579,33 @@ "time": "2014-04-14 19:50:30" }, { - "name": "zendframework/zend-serializer", + "name": "laminas/laminas-serializer", "version": "2.3.1", "source": { "type": "git", - "url": "https://github.com/zendframework/zend-serializer.git", + "url": "https://github.com/laminas/laminas-serializer.git", "reference": "22f73b0d0ff1158216bd5bcacf6bd00f7be1a0f6" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/zendframework/zend-serializer/zipball/22f73b0d0ff1158216bd5bcacf6bd00f7be1a0f6", + "url": "https://api.github.com/repos/laminas/laminas-serializer/zipball/22f73b0d0ff1158216bd5bcacf6bd00f7be1a0f6", "reference": "22f73b0d0ff1158216bd5bcacf6bd00f7be1a0f6", "shasum": "" }, "require": { "php": ">=5.3.23", - "zendframework/zend-json": "self.version", - "zendframework/zend-math": "self.version", - "zendframework/zend-stdlib": "self.version" + "laminas/laminas-json": "self.version", + "laminas/laminas-math": "self.version", + "laminas/laminas-stdlib": "self.version" }, "require-dev": { "fabpot/php-cs-fixer": "1.7.*", "phpunit/phpunit": "~4.0", "satooshi/php-coveralls": "dev-master", - "zendframework/zend-servicemanager": "self.version" + "laminas/laminas-servicemanager": "self.version" }, "suggest": { - "zendframework/zend-servicemanager": "To support plugin manager support" + "laminas/laminas-servicemanager": "To support plugin manager support" }, "type": "library", "extra": { @@ -5620,7 +5616,7 @@ }, "autoload": { "psr-4": { - "Zend\\Serializer\\": "src/" + "Laminas\\Serializer\\": "src/" } }, "notification-url": "https://packagist.org/downloads/", @@ -5628,7 +5624,7 @@ "BSD-3-Clause" ], "description": "provides an adapter based interface to simply generate storable representation of PHP types by different facilities, and recover", - "homepage": "https://github.com/zendframework/zend-serializer", + "homepage": "https://github.com/laminas/laminas-serializer", "keywords": [ "serializer", "zf2" @@ -5636,23 +5632,23 @@ "time": "2014-04-14 19:50:30" }, { - "name": "zendframework/zend-server", + "name": "laminas/laminas-server", "version": "2.3.1", "source": { "type": "git", - "url": "https://github.com/zendframework/zend-server.git", + "url": "https://github.com/laminas/laminas-server.git", "reference": "bc5fb97f4ac48a5dc54bd18dded21a3e1eea552c" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/zendframework/zend-server/zipball/bc5fb97f4ac48a5dc54bd18dded21a3e1eea552c", + "url": "https://api.github.com/repos/laminas/laminas-server/zipball/bc5fb97f4ac48a5dc54bd18dded21a3e1eea552c", "reference": "bc5fb97f4ac48a5dc54bd18dded21a3e1eea552c", "shasum": "" }, "require": { "php": ">=5.3.23", - "zendframework/zend-code": "self.version", - "zendframework/zend-stdlib": "self.version" + "laminas/laminas-code": "self.version", + "laminas/laminas-stdlib": "self.version" }, "require-dev": { "fabpot/php-cs-fixer": "1.7.*", @@ -5668,14 +5664,14 @@ }, "autoload": { "psr-4": { - "Zend\\Server\\": "src/" + "Laminas\\Server\\": "src/" } }, "notification-url": "https://packagist.org/downloads/", "license": [ "BSD-3-Clause" ], - "homepage": "https://github.com/zendframework/zend-server", + "homepage": "https://github.com/laminas/laminas-server", "keywords": [ "server", "zf2" @@ -5683,16 +5679,16 @@ "time": "2014-04-14 19:50:30" }, { - "name": "zendframework/zend-servicemanager", + "name": "laminas/laminas-servicemanager", "version": "2.3.1", "source": { "type": "git", - "url": "https://github.com/zendframework/zend-servicemanager.git", + "url": "https://github.com/laminas/laminas-servicemanager.git", "reference": "7a428b595a1fcd4c2a8026ee5d5f89a56036f682" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/zendframework/zend-servicemanager/zipball/7a428b595a1fcd4c2a8026ee5d5f89a56036f682", + "url": "https://api.github.com/repos/laminas/laminas-servicemanager/zipball/7a428b595a1fcd4c2a8026ee5d5f89a56036f682", "reference": "7a428b595a1fcd4c2a8026ee5d5f89a56036f682", "shasum": "" }, @@ -5703,10 +5699,10 @@ "fabpot/php-cs-fixer": "1.7.*", "phpunit/phpunit": "~4.0", "satooshi/php-coveralls": "dev-master", - "zendframework/zend-di": "self.version" + "laminas/laminas-di": "self.version" }, "suggest": { - "zendframework/zend-di": "Zend\\Di component" + "laminas/laminas-di": "Laminas\\Di component" }, "type": "library", "extra": { @@ -5717,7 +5713,7 @@ }, "autoload": { "psr-4": { - "Zend\\ServiceManager\\": "src/" + "Laminas\\ServiceManager\\": "src/" } }, "notification-url": "https://packagist.org/downloads/", @@ -5732,33 +5728,33 @@ "time": "2014-04-15 13:59:53" }, { - "name": "zendframework/zend-soap", + "name": "laminas/laminas-soap", "version": "2.3.1", "source": { "type": "git", - "url": "https://github.com/zendframework/zend-soap.git", + "url": "https://github.com/laminas/laminas-soap.git", "reference": "fab9f5309be04cacf01af54f694aed5102592c5c" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/zendframework/zend-soap/zipball/fab9f5309be04cacf01af54f694aed5102592c5c", + "url": "https://api.github.com/repos/laminas/laminas-soap/zipball/fab9f5309be04cacf01af54f694aed5102592c5c", "reference": "fab9f5309be04cacf01af54f694aed5102592c5c", "shasum": "" }, "require": { "php": ">=5.3.23", - "zendframework/zend-server": "self.version", - "zendframework/zend-stdlib": "self.version", - "zendframework/zend-uri": "self.version" + "laminas/laminas-server": "self.version", + "laminas/laminas-stdlib": "self.version", + "laminas/laminas-uri": "self.version" }, "require-dev": { "fabpot/php-cs-fixer": "1.7.*", "phpunit/phpunit": "~4.0", "satooshi/php-coveralls": "dev-master", - "zendframework/zend-http": "self.version" + "laminas/laminas-http": "self.version" }, "suggest": { - "zendframework/zend-http": "Zend\\Http component" + "laminas/laminas-http": "Laminas\\Http component" }, "type": "library", "extra": { @@ -5769,14 +5765,14 @@ }, "autoload": { "psr-4": { - "Zend\\Soap\\": "src/" + "Laminas\\Soap\\": "src/" } }, "notification-url": "https://packagist.org/downloads/", "license": [ "BSD-3-Clause" ], - "homepage": "https://github.com/zendframework/zend-soap", + "homepage": "https://github.com/laminas/laminas-soap", "keywords": [ "soap", "zf2" @@ -5784,16 +5780,16 @@ "time": "2014-04-14 19:50:30" }, { - "name": "zendframework/zend-stdlib", + "name": "laminas/laminas-stdlib", "version": "2.3.1", "source": { "type": "git", - "url": "https://github.com/zendframework/zend-stdlib.git", + "url": "https://github.com/laminas/laminas-stdlib.git", "reference": "6079302963d57f36a9ba92ed3f38b992997aa78d" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/zendframework/zend-stdlib/zipball/6079302963d57f36a9ba92ed3f38b992997aa78d", + "url": "https://api.github.com/repos/laminas/laminas-stdlib/zipball/6079302963d57f36a9ba92ed3f38b992997aa78d", "reference": "6079302963d57f36a9ba92ed3f38b992997aa78d", "shasum": "" }, @@ -5804,16 +5800,16 @@ "fabpot/php-cs-fixer": "1.7.*", "phpunit/phpunit": "~4.0", "satooshi/php-coveralls": "dev-master", - "zendframework/zend-eventmanager": "self.version", - "zendframework/zend-filter": "self.version", - "zendframework/zend-serializer": "self.version", - "zendframework/zend-servicemanager": "self.version" + "laminas/laminas-eventmanager": "self.version", + "laminas/laminas-filter": "self.version", + "laminas/laminas-serializer": "self.version", + "laminas/laminas-servicemanager": "self.version" }, "suggest": { - "zendframework/zend-eventmanager": "To support aggregate hydrator usage", - "zendframework/zend-filter": "To support naming strategy hydrator usage", - "zendframework/zend-serializer": "Zend\\Serializer component", - "zendframework/zend-servicemanager": "To support hydrator plugin manager usage" + "laminas/laminas-eventmanager": "To support aggregate hydrator usage", + "laminas/laminas-filter": "To support naming strategy hydrator usage", + "laminas/laminas-serializer": "Laminas\\Serializer component", + "laminas/laminas-servicemanager": "To support hydrator plugin manager usage" }, "type": "library", "extra": { @@ -5824,14 +5820,14 @@ }, "autoload": { "psr-4": { - "Zend\\Stdlib\\": "src/" + "Laminas\\Stdlib\\": "src/" } }, "notification-url": "https://packagist.org/downloads/", "license": [ "BSD-3-Clause" ], - "homepage": "https://github.com/zendframework/zend-stdlib", + "homepage": "https://github.com/laminas/laminas-stdlib", "keywords": [ "stdlib", "zf2" @@ -5839,23 +5835,23 @@ "time": "2014-04-15 13:59:53" }, { - "name": "zendframework/zend-text", + "name": "laminas/laminas-text", "version": "2.3.1", "source": { "type": "git", - "url": "https://github.com/zendframework/zend-text.git", + "url": "https://github.com/laminas/laminas-text.git", "reference": "e9b3fffcc6241f7cfdb33282ed10979cd8ba9b90" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/zendframework/zend-text/zipball/e9b3fffcc6241f7cfdb33282ed10979cd8ba9b90", + "url": "https://api.github.com/repos/laminas/laminas-text/zipball/e9b3fffcc6241f7cfdb33282ed10979cd8ba9b90", "reference": "e9b3fffcc6241f7cfdb33282ed10979cd8ba9b90", "shasum": "" }, "require": { "php": ">=5.3.23", - "zendframework/zend-servicemanager": "self.version", - "zendframework/zend-stdlib": "self.version" + "laminas/laminas-servicemanager": "self.version", + "laminas/laminas-stdlib": "self.version" }, "require-dev": { "fabpot/php-cs-fixer": "1.7.*", @@ -5871,14 +5867,14 @@ }, "autoload": { "psr-4": { - "Zend\\Text\\": "src/" + "Laminas\\Text\\": "src/" } }, "notification-url": "https://packagist.org/downloads/", "license": [ "BSD-3-Clause" ], - "homepage": "https://github.com/zendframework/zend-text", + "homepage": "https://github.com/laminas/laminas-text", "keywords": [ "text", "zf2" @@ -5886,23 +5882,23 @@ "time": "2014-04-14 19:50:30" }, { - "name": "zendframework/zend-uri", + "name": "laminas/laminas-uri", "version": "2.3.1", "source": { "type": "git", - "url": "https://github.com/zendframework/zend-uri.git", + "url": "https://github.com/laminas/laminas-uri.git", "reference": "0de6ba8c166a235588783ff8e88a19b364630d89" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/zendframework/zend-uri/zipball/0de6ba8c166a235588783ff8e88a19b364630d89", + "url": "https://api.github.com/repos/laminas/laminas-uri/zipball/0de6ba8c166a235588783ff8e88a19b364630d89", "reference": "0de6ba8c166a235588783ff8e88a19b364630d89", "shasum": "" }, "require": { "php": ">=5.3.23", - "zendframework/zend-escaper": "self.version", - "zendframework/zend-validator": "self.version" + "laminas/laminas-escaper": "self.version", + "laminas/laminas-validator": "self.version" }, "require-dev": { "fabpot/php-cs-fixer": "1.7.*", @@ -5918,7 +5914,7 @@ }, "autoload": { "psr-4": { - "Zend\\Uri\\": "src/" + "Laminas\\Uri\\": "src/" } }, "notification-url": "https://packagist.org/downloads/", @@ -5926,7 +5922,7 @@ "BSD-3-Clause" ], "description": "a component that aids in manipulating and validating » Uniform Resource Identifiers (URIs)", - "homepage": "https://github.com/zendframework/zend-uri", + "homepage": "https://github.com/laminas/laminas-uri", "keywords": [ "uri", "zf2" @@ -5934,44 +5930,44 @@ "time": "2014-04-14 19:50:30" }, { - "name": "zendframework/zend-validator", + "name": "laminas/laminas-validator", "version": "2.3.1", "source": { "type": "git", - "url": "https://github.com/zendframework/zend-validator.git", + "url": "https://github.com/laminas/laminas-validator.git", "reference": "a5f97f6c3a27a27b1235f724ad0048715459f013" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/zendframework/zend-validator/zipball/a5f97f6c3a27a27b1235f724ad0048715459f013", + "url": "https://api.github.com/repos/laminas/laminas-validator/zipball/a5f97f6c3a27a27b1235f724ad0048715459f013", "reference": "a5f97f6c3a27a27b1235f724ad0048715459f013", "shasum": "" }, "require": { "php": ">=5.3.23", - "zendframework/zend-stdlib": "self.version" + "laminas/laminas-stdlib": "self.version" }, "require-dev": { "fabpot/php-cs-fixer": "1.7.*", "phpunit/phpunit": "~4.0", "satooshi/php-coveralls": "dev-master", - "zendframework/zend-db": "self.version", - "zendframework/zend-filter": "self.version", - "zendframework/zend-i18n": "self.version", - "zendframework/zend-math": "self.version", - "zendframework/zend-servicemanager": "self.version", - "zendframework/zend-session": "self.version", - "zendframework/zend-uri": "self.version" + "laminas/laminas-db": "self.version", + "laminas/laminas-filter": "self.version", + "laminas/laminas-i18n": "self.version", + "laminas/laminas-math": "self.version", + "laminas/laminas-servicemanager": "self.version", + "laminas/laminas-session": "self.version", + "laminas/laminas-uri": "self.version" }, "suggest": { - "zendframework/zend-db": "Zend\\Db component", - "zendframework/zend-filter": "Zend\\Filter component, required by the Digits validator", - "zendframework/zend-i18n": "Zend\\I18n component to allow translation of validation error messages as well as to use the various Date validators", - "zendframework/zend-math": "Zend\\Math component", + "laminas/laminas-db": "Laminas\\Db component", + "laminas/laminas-filter": "Laminas\\Filter component, required by the Digits validator", + "laminas/laminas-i18n": "Laminas\\I18n component to allow translation of validation error messages as well as to use the various Date validators", + "laminas/laminas-math": "Laminas\\Math component", "zendframework/zend-resources": "Translations of validator messages", - "zendframework/zend-servicemanager": "Zend\\ServiceManager component to allow using the ValidatorPluginManager and validator chains", - "zendframework/zend-session": "Zend\\Session component", - "zendframework/zend-uri": "Zend\\Uri component, required by the Uri and Sitemap\\Loc validators" + "laminas/laminas-servicemanager": "Laminas\\ServiceManager component to allow using the ValidatorPluginManager and validator chains", + "laminas/laminas-session": "Laminas\\Session component", + "laminas/laminas-uri": "Laminas\\Uri component, required by the Uri and Sitemap\\Loc validators" }, "type": "library", "extra": { @@ -5982,7 +5978,7 @@ }, "autoload": { "psr-4": { - "Zend\\Validator\\": "src/" + "Laminas\\Validator\\": "src/" } }, "notification-url": "https://packagist.org/downloads/", @@ -5990,7 +5986,7 @@ "BSD-3-Clause" ], "description": "provides a set of commonly needed validators", - "homepage": "https://github.com/zendframework/zend-validator", + "homepage": "https://github.com/laminas/laminas-validator", "keywords": [ "validator", "zf2" @@ -5998,57 +5994,57 @@ "time": "2014-04-14 19:50:30" }, { - "name": "zendframework/zend-view", + "name": "laminas/laminas-view", "version": "2.3.1", "source": { "type": "git", - "url": "https://github.com/zendframework/zend-view.git", + "url": "https://github.com/laminas/laminas-view.git", "reference": "e308d498fa7851f26bd639bfe3ebbfba397c47bc" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/zendframework/zend-view/zipball/e308d498fa7851f26bd639bfe3ebbfba397c47bc", + "url": "https://api.github.com/repos/laminas/laminas-view/zipball/e308d498fa7851f26bd639bfe3ebbfba397c47bc", "reference": "e308d498fa7851f26bd639bfe3ebbfba397c47bc", "shasum": "" }, "require": { "php": ">=5.3.23", - "zendframework/zend-eventmanager": "self.version", - "zendframework/zend-loader": "self.version", - "zendframework/zend-stdlib": "self.version" + "laminas/laminas-eventmanager": "self.version", + "laminas/laminas-loader": "self.version", + "laminas/laminas-stdlib": "self.version" }, "require-dev": { "fabpot/php-cs-fixer": "1.7.*", "phpunit/phpunit": "~4.0", "satooshi/php-coveralls": "dev-master", - "zendframework/zend-authentication": "self.version", - "zendframework/zend-escaper": "self.version", - "zendframework/zend-feed": "self.version", - "zendframework/zend-filter": "self.version", - "zendframework/zend-http": "self.version", - "zendframework/zend-i18n": "self.version", - "zendframework/zend-json": "self.version", - "zendframework/zend-mvc": "self.version", - "zendframework/zend-navigation": "self.version", - "zendframework/zend-paginator": "self.version", - "zendframework/zend-permissions-acl": "self.version", - "zendframework/zend-servicemanager": "self.version", - "zendframework/zend-uri": "self.version" + "laminas/laminas-authentication": "self.version", + "laminas/laminas-escaper": "self.version", + "laminas/laminas-feed": "self.version", + "laminas/laminas-filter": "self.version", + "laminas/laminas-http": "self.version", + "laminas/laminas-i18n": "self.version", + "laminas/laminas-json": "self.version", + "laminas/laminas-mvc": "self.version", + "laminas/laminas-navigation": "self.version", + "laminas/laminas-paginator": "self.version", + "laminas/laminas-permissions-acl": "self.version", + "laminas/laminas-servicemanager": "self.version", + "laminas/laminas-uri": "self.version" }, "suggest": { - "zendframework/zend-authentication": "Zend\\Authentication component", - "zendframework/zend-escaper": "Zend\\Escaper component", - "zendframework/zend-feed": "Zend\\Feed component", - "zendframework/zend-filter": "Zend\\Filter component", - "zendframework/zend-http": "Zend\\Http component", - "zendframework/zend-i18n": "Zend\\I18n component", - "zendframework/zend-json": "Zend\\Json component", - "zendframework/zend-mvc": "Zend\\Mvc component", - "zendframework/zend-navigation": "Zend\\Navigation component", - "zendframework/zend-paginator": "Zend\\Paginator component", - "zendframework/zend-permissions-acl": "Zend\\Permissions\\Acl component", - "zendframework/zend-servicemanager": "Zend\\ServiceManager component", - "zendframework/zend-uri": "Zend\\Uri component" + "laminas/laminas-authentication": "Laminas\\Authentication component", + "laminas/laminas-escaper": "Laminas\\Escaper component", + "laminas/laminas-feed": "Laminas\\Feed component", + "laminas/laminas-filter": "Laminas\\Filter component", + "laminas/laminas-http": "Laminas\\Http component", + "laminas/laminas-i18n": "Laminas\\I18n component", + "laminas/laminas-json": "Laminas\\Json component", + "laminas/laminas-mvc": "Laminas\\Mvc component", + "laminas/laminas-navigation": "Laminas\\Navigation component", + "laminas/laminas-paginator": "Laminas\\Paginator component", + "laminas/laminas-permissions-acl": "Laminas\\Permissions\\Acl component", + "laminas/laminas-servicemanager": "Laminas\\ServiceManager component", + "laminas/laminas-uri": "Laminas\\Uri component" }, "type": "library", "extra": { @@ -6059,7 +6055,7 @@ }, "autoload": { "psr-4": { - "Zend\\View\\": "src/" + "Laminas\\View\\": "src/" } }, "notification-url": "https://packagist.org/downloads/", @@ -6067,7 +6063,7 @@ "BSD-3-Clause" ], "description": "provides a system of helpers, output filters, and variable escaping", - "homepage": "https://github.com/zendframework/zend-view", + "homepage": "https://github.com/laminas/laminas-view", "keywords": [ "view", "zf2" diff --git a/dev/tests/integration/testsuite/Magento/Shipping/Controller/Adminhtml/Order/Shipment/Save/CreateShipmentForOrderTest.php b/dev/tests/integration/testsuite/Magento/Shipping/Controller/Adminhtml/Order/Shipment/Save/CreateShipmentForOrderTest.php new file mode 100644 index 0000000000000..20543f808c316 --- /dev/null +++ b/dev/tests/integration/testsuite/Magento/Shipping/Controller/Adminhtml/Order/Shipment/Save/CreateShipmentForOrderTest.php @@ -0,0 +1,334 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +declare(strict_types=1); + +namespace Magento\Shipping\Controller\Adminhtml\Order\Shipment\Save; + +use Magento\Framework\App\Request\Http as HttpRequest; +use Magento\Framework\Message\MessageInterface; +use Magento\Shipping\Controller\Adminhtml\Order\Shipment\AbstractShipmentControllerTest; +use Magento\Framework\Escaper; + +/** + * Test cases related to check that shipment creates as expected or proper error message appear. + * + * @magentoAppArea adminhtml + * @magentoDbIsolation enabled + * + * @see \Magento\Shipping\Controller\Adminhtml\Order\Shipment\Save::execute + */ +class CreateShipmentForOrderTest extends AbstractShipmentControllerTest +{ + /** + * @var Escaper + */ + private $escaper; + + /** + * @inheritdoc + */ + protected function setUp() + { + parent::setUp(); + $this->escaper = $this->_objectManager->get(Escaper::class); + } + + /** + * Assert that shipment created successfully. + * + * @magentoDataFixture Magento/Sales/_files/order_with_two_order_items_with_simple_product.php + * @dataProvider dataForCreateShipmentDataProvider + * + * @param array $dataForBuildPostData + * @param array $expectedShippedQtyBySku + * @return void + */ + public function testCreateOrderShipment(array $dataForBuildPostData, array $expectedShippedQtyBySku): void + { + $postData = $this->createPostData($dataForBuildPostData); + $order = $this->orderRepository->get($postData['order_id']); + $shipment = $this->getShipment($order); + $this->assertNull($shipment->getEntityId()); + $this->performShipmentCreationRequest($postData); + $this->assertCreateShipmentRequestSuccessfullyPerformed((int)$order->getEntityId()); + $createdShipment = $this->getShipment($order); + $this->assertNotNull($createdShipment->getEntityId()); + $shipmentItems = $createdShipment->getItems(); + $this->assertCount(count($expectedShippedQtyBySku), $shipmentItems); + foreach ($shipmentItems as $shipmentItem) { + $this->assertEquals($expectedShippedQtyBySku[$shipmentItem->getSku()], $shipmentItem->getQty()); + } + } + + /** + * Data for create full or partial order shipment POST data. + * + * @return array + */ + public function dataForCreateShipmentDataProvider(): array + { + return [ + 'create_full_shipment' => [ + [ + 'order_increment_id' => '100000001', + 'items_count_to_ship_by_sku' => [ + 'simple' => 5, + 'custom-design-simple-product' => 5, + ], + 'comment_text' => 'Create full shipment', + ], + [ + 'simple' => 5, + 'custom-design-simple-product' => 5, + ], + ], + 'create_partial_shipment' => [ + [ + 'order_increment_id' => '100000001', + 'items_count_to_ship_by_sku' => [ + 'simple' => 3, + 'custom-design-simple-product' => 2, + ], + 'comment_text' => 'Create partial shipment', + ], + [ + 'simple' => 3, + 'custom-design-simple-product' => 2, + ], + ], + 'create_shipment_for_one_of_product' => [ + [ + 'order_increment_id' => '100000001', + 'items_count_to_ship_by_sku' => [ + 'simple' => 4, + 'custom-design-simple-product' => 0, + ], + 'comment_text' => 'Create partial shipment', + ], + [ + 'simple' => 4, + ], + ], + 'create_shipment_qty_more_that_ordered' => [ + [ + 'order_increment_id' => '100000001', + 'items_count_to_ship_by_sku' => [ + 'simple' => 150, + 'custom-design-simple-product' => 5, + ], + 'comment_text' => 'Create partial shipment', + ], + [ + 'simple' => 5, + 'custom-design-simple-product' => 5, + ], + ], + 'create_shipment_qty_less_that_zero' => [ + [ + 'order_increment_id' => '100000001', + 'items_count_to_ship_by_sku' => [ + 'simple' => -10, + 'custom-design-simple-product' => 5, + ], + 'comment_text' => 'Create partial shipment', + ], + [ + 'custom-design-simple-product' => 5, + ], + ], + 'create_shipment_without_counts' => [ + [ + 'order_increment_id' => '100000001', + 'items_count_to_ship_by_sku' => [], + 'comment_text' => 'Create partial shipment', + ], + [ + 'simple' => 5, + 'custom-design-simple-product' => 5, + ], + ], + ]; + } + + /** + * Assert that body contains 404 error and forwarded to no-route if + * requested POST data is empty. + * + * @magentoDataFixture Magento/Sales/_files/order_with_customer.php + * + * @return void + */ + public function testCreateOrderShipmentWithoutPostData(): void + { + $this->performShipmentCreationRequest([]); + $this->assert404NotFound(); + } + + /** + * Assert that if we doesn't send order_id it will forwarded to no-rout and contains error 404. + * + * @magentoDataFixture Magento/Sales/_files/order_with_customer.php + * + * @return void + */ + public function testCreateOrderShipmentWithoutOrderId(): void + { + $dataToSend = [ + 'order_increment_id' => '100000001', + 'items_count_to_ship_by_sku' => [ + 'simple' => 2, + ], + 'comment_text' => 'Create full shipment', + ]; + $postData = $this->createPostData($dataToSend); + unset($postData['order_id']); + $this->performShipmentCreationRequest($postData); + $this->assert404NotFound(); + } + + /** + * Assert that if we send POST data with wrong order item ids we got error message. + * + * @magentoDataFixture Magento/Sales/_files/order_with_customer.php + * + * @return void + */ + public function testCreateOrderShipmentWithWrongItemsIds(): void + { + $orderId = $this->getOrder('100000001')->getEntityId(); + $postData = [ + 'order_id' => $orderId, + 'shipment' => + [ + 'items' => [ + 678678 => 4 + ], + ], + ]; + $this->performShipmentCreationRequest($postData); + $this->assertCreateShipmentRequestWithError( + "Shipment Document Validation Error(s):\nYou can't create a shipment without products.", + "admin/order_shipment/new/order_id/{$orderId}" + ); + } + + /** + * Assert that if we send POST data with alphabet order item count we got error message. + * + * @magentoDataFixture Magento/Sales/_files/order_with_customer.php + * + * @return void + */ + public function testCreateOrderShipmentWithAlphabetQty(): void + { + $dataToSend = [ + 'order_increment_id' => '100000001', + 'items_count_to_ship_by_sku' => [ + 'simple' => 'test_letters', + ], + 'comment_text' => 'Create full shipment', + ]; + $postData = $this->createPostData($dataToSend); + $this->performShipmentCreationRequest($postData); + $this->assertCreateShipmentRequestWithError( + "Shipment Document Validation Error(s):\nYou can't create a shipment without products.", + "admin/order_shipment/new/order_id/{$postData['order_id']}" + ); + } + + /** + * @inheritdoc + */ + public function assert404NotFound() + { + $this->assertEquals('noroute', $this->getRequest()->getControllerName()); + $this->assertContains('404 Error', $this->getResponse()->getBody()); + $this->assertContains('Page not found', $this->getResponse()->getBody()); + } + + /** + * Set POST data and type POST to request + * and perform request by path backend/admin/order_shipment/save. + * + * @param array $postData + * @return void + */ + private function performShipmentCreationRequest(array $postData): void + { + $this->getRequest()->setPostValue($postData) + ->setMethod(HttpRequest::METHOD_POST); + $this->dispatch('backend/admin/order_shipment/save'); + } + + /** + * Assert that after create shipment session message manager + * contain message "The shipment has been created." and redirect to sales/order/view is created. + * + * @param int $orderId + * @return void + */ + private function assertCreateShipmentRequestSuccessfullyPerformed(int $orderId): void + { + $this->assertSessionMessages( + $this->equalTo([(string)__('The shipment has been created.')]), + MessageInterface::TYPE_SUCCESS + ); + $this->assertRedirect($this->stringContains('sales/order/view/order_id/' . $orderId)); + } + + /** + * Assert that session message manager contain provided error message and + * redirect created if it was provided. + * + * @param string $message + * @param string|null $redirect + * @param bool $isNeedEscapeMessage + * @return void + */ + private function assertCreateShipmentRequestWithError( + string $message, + ?string $redirect = null, + bool $isNeedEscapeMessage = true + ): void { + $assertMessage = $isNeedEscapeMessage + ? $this->escaper->escapeHtml((string)__($message)) : (string)__($message); + $this->assertSessionMessages( + $this->equalTo([$assertMessage]), + MessageInterface::TYPE_ERROR + ); + $this->assertRedirect($this->stringContains($redirect)); + } + + /** + * Create POST data for create shipment for order. + * + * @param array $dataForBuildPostData + * @return array + */ + private function createPostData(array $dataForBuildPostData): array + { + $result = []; + $order = $this->getOrder($dataForBuildPostData['order_increment_id']); + $result['order_id'] = (int)$order->getEntityId(); + $shipment = []; + $shipment['items'] = []; + foreach ($order->getItems() as $orderItem) { + if (!isset($dataForBuildPostData['items_count_to_ship_by_sku'][$orderItem->getSku()])) { + continue; + } + $shipment['items'][$orderItem->getItemId()] + = $dataForBuildPostData['items_count_to_ship_by_sku'][$orderItem->getSku()]; + } + if (empty($shipment['items'])) { + unset($shipment['items']); + } + + $shipment['comment_text'] = $dataForBuildPostData['comment_text']; + $result['shipment'] = $shipment; + + return $result; + } +} diff --git a/dev/tests/integration/testsuite/Magento/Signifyd/Block/Adminhtml/CaseInfoTest.php b/dev/tests/integration/testsuite/Magento/Signifyd/Block/Adminhtml/CaseInfoTest.php deleted file mode 100644 index 7607526b1389e..0000000000000 --- a/dev/tests/integration/testsuite/Magento/Signifyd/Block/Adminhtml/CaseInfoTest.php +++ /dev/null @@ -1,123 +0,0 @@ -<?php -/** - * Copyright © Magento, Inc. All rights reserved. - * See COPYING.txt for license details. - */ -namespace Magento\Signifyd\Block\Adminhtml; - -use Magento\Framework\App\RequestInterface; -use Magento\Framework\ObjectManagerInterface; -use Magento\Framework\View\Element\Template\Context; -use Magento\Sales\Model\Order; -use Magento\TestFramework\Helper\Bootstrap; - -class CaseInfoTest extends \PHPUnit\Framework\TestCase -{ - /** - * @var ObjectManagerInterface - */ - private $objectManager; - - /** - * @var Order - */ - private $order; - - /** - * @var \Magento\Framework\View\LayoutFactory - */ - protected $layoutFactory; - - /** - * @inheritdoc - */ - protected function setUp() - { - $this->objectManager = Bootstrap::getObjectManager(); - $this->order = $this->objectManager->create(Order::class); - $this->layoutFactory = $this->objectManager->get(\Magento\Framework\View\LayoutFactory::class); - } - - /** - * Checks that block has contents when case entity for order is exists - * even if Signifyd module is inactive. - * - * @magentoConfigFixture current_store fraud_protection/signifyd/active 0 - * @magentoDataFixture Magento/Signifyd/_files/case.php - * @magentoAppArea adminhtml - */ - public function testModuleIsInactive() - { - $this->order->loadByIncrementId('100000001'); - - self::assertNotEmpty($this->getBlock()->toHtml()); - } - - /** - * Checks that block does not give contents - * if there is no case entity created for order. - * - * @covers \Magento\Signifyd\Block\Adminhtml\CaseInfo::getCaseEntity - * @magentoConfigFixture current_store fraud_protection/signifyd/active 1 - * @magentoDataFixture Magento/Signifyd/_files/order_with_customer_and_two_simple_products.php - * @magentoAppArea adminhtml - */ - public function testCaseEntityNotExists() - { - $this->order->loadByIncrementId('100000001'); - - self::assertEmpty($this->getBlock()->toHtml()); - } - - /** - * Checks that: - * - block give contents - * - block contents guarantee decision field - * - * @covers \Magento\Signifyd\Block\Adminhtml\CaseInfo::getScoreClass - * @magentoConfigFixture current_store fraud_protection/signifyd/active 1 - * @magentoDataFixture Magento/Signifyd/_files/case.php - * @magentoAppArea adminhtml - */ - public function testCaseEntityExists() - { - $this->order->loadByIncrementId('100000001'); - - $block = $this->getBlock(); - self::assertNotEmpty($block->toHtml()); - self::assertContains((string) $block->getCaseGuaranteeDisposition(), $block->toHtml()); - } - - /** - * Gets block. - * - * @return CaseInfo - */ - private function getBlock() - { - $layout = $this->layoutFactory->create(); - - $layout->addContainer('order_additional_info', 'Container'); - - /** @var CaseInfo $block */ - $block = $layout->addBlock(CaseInfo::class, 'order_case_info', 'order_additional_info'); - $block->setAttribute('context', $this->getContext()); - $block->setTemplate('Magento_Signifyd::case_info.phtml'); - - return $block; - } - - /** - * Creates template context with necessary order id param. - * - * @return Context - */ - private function getContext() - { - /** @var RequestInterface $request */ - $request = $this->objectManager->get(RequestInterface::class); - $request->setParams(['order_id' => $this->order->getEntityId()]); - - return $this->objectManager->create(Context::class, ['request' => $request]); - } -} diff --git a/dev/tests/integration/testsuite/Magento/Signifyd/Block/FingerprintTest.php b/dev/tests/integration/testsuite/Magento/Signifyd/Block/FingerprintTest.php deleted file mode 100644 index 1efbb8b6d33e4..0000000000000 --- a/dev/tests/integration/testsuite/Magento/Signifyd/Block/FingerprintTest.php +++ /dev/null @@ -1,63 +0,0 @@ -<?php -/** - * Copyright © Magento, Inc. All rights reserved. - * See COPYING.txt for license details. - */ -namespace Magento\Signifyd\Block; - -use Magento\Framework\App\Area; -use Magento\Framework\App\ObjectManager; -use Magento\Framework\View\LayoutInterface; -use Magento\TestFramework\Helper\Bootstrap; - -class FingerprintTest extends \PHPUnit\Framework\TestCase -{ - /** - * @var ObjectManager - */ - private $objectManager; - - /** - * @inheritdoc - */ - protected function setUp() - { - $bootstrap = Bootstrap::getInstance(); - $bootstrap->loadArea(Area::AREA_FRONTEND); - - $this->objectManager = Bootstrap::getObjectManager(); - } - - /** - * Checks if session id attribute is present when the module is enabled. - * - * @magentoConfigFixture current_store fraud_protection/signifyd/active 1 - */ - public function testSessionIdPresent() - { - self::assertContains('data-order-session-id', $this->getBlockContents()); - } - - /** - * Checks if block is an empty when the module is disabled. - * - * @magentoConfigFixture current_store fraud_protection/signifyd/active 0 - */ - public function testBlockEmpty() - { - self::assertEmpty($this->getBlockContents()); - } - - /** - * Renders block contents. - * - * @return string - */ - private function getBlockContents() - { - $block = $this->objectManager->get(LayoutInterface::class) - ->createBlock(Fingerprint::class); - - return $block->fetchView($block->getTemplateFile()); - } -} diff --git a/dev/tests/integration/testsuite/Magento/Signifyd/Controller/Webhooks/HandlerTest.php b/dev/tests/integration/testsuite/Magento/Signifyd/Controller/Webhooks/HandlerTest.php deleted file mode 100644 index 667cb079f9096..0000000000000 --- a/dev/tests/integration/testsuite/Magento/Signifyd/Controller/Webhooks/HandlerTest.php +++ /dev/null @@ -1,135 +0,0 @@ -<?php -/** - * Copyright © Magento, Inc. All rights reserved. - * See COPYING.txt for license details. - */ -namespace Magento\Signifyd\Controller\Webhooks; - -use Magento\TestFramework\TestCase\AbstractController; -use Magento\Signifyd\Model\SignifydGateway\Response\WebhookRequest; -use Magento\Signifyd\Api\CaseRepositoryInterface; -use Magento\Signifyd\Api\Data\CaseInterface; -use Magento\Sales\Api\Data\OrderStatusHistoryInterface; -use Magento\Sales\Api\OrderRepositoryInterface; - -/** - * Class tests handling webhook post from Signifyd service. - */ -class HandlerTest extends AbstractController -{ - /** - * @var string - */ - private static $entryPoint = 'signifyd/webhooks/handler'; - - /** - * Tests handling webhook message of cases/rescore type. - * Checks updated case entity and comment in order history. - * - * @covers \Magento\Signifyd\Controller\Webhooks\Handler::execute - * @magentoConfigFixture current_store fraud_protection/signifyd/active 1 - * @magentoConfigFixture current_store fraud_protection/signifyd/api_key ApFZZvxGgIxuP8BazSm3v8eGN - * @magentoDataFixture Magento/Signifyd/_files/case.php - */ - public function testExecuteSuccess() - { - $caseId = 123; - $webhookRequest = $this->getWebhookRequest(); - $this->_objectManager->addSharedInstance($webhookRequest, WebhookRequest::class); - - $this->dispatch(self::$entryPoint); - - /** @var CaseRepositoryInterface $caseManagement */ - $caseRepository = $this->_objectManager->get(CaseRepositoryInterface::class); - /** @var CaseInterface $caseEntity */ - $caseEntity = $caseRepository->getByCaseId($caseId); - $orderEntityId = $caseEntity->getOrderId(); - - self::assertNotEmpty($caseEntity); - self::assertEquals('2017-01-06 12:47:03', $caseEntity->getCreatedAt()); - self::assertEquals('2017-01-06 12:47:03', $caseEntity->getUpdatedAt()); - self::assertEquals('Magento', $caseEntity->getAssociatedTeam()['teamName']); - self::assertEquals(true, $caseEntity->isGuaranteeEligible()); - self::assertEquals(CaseInterface::STATUS_OPEN, $caseEntity->getStatus()); - self::assertEquals($orderEntityId, $caseEntity->getOrderId()); - - /** @var OrderRepositoryInterface $orderRepository */ - $orderRepository = $this->_objectManager->get(OrderRepositoryInterface::class); - $order = $orderRepository->get($caseEntity->getOrderId()); - $histories = $order->getStatusHistories(); - self::assertNotEmpty($histories); - - /** @var OrderStatusHistoryInterface $caseCreationComment */ - $caseComment = array_pop($histories); - self::assertInstanceOf(OrderStatusHistoryInterface::class, $caseComment); - - self::assertEquals( - "Case Update: New score for the order is 384. Previous score was 553.", - $caseComment->getComment() - ); - - $this->_objectManager->removeSharedInstance(WebhookRequest::class); - } - - /** - * Tests handling webhook message of cases/test type. - * Controller should response with code 200. - * - * @covers \Magento\Signifyd\Controller\Webhooks\Handler::execute - * @magentoConfigFixture current_store fraud_protection/signifyd/active 1 - */ - public function testExecuteTestSuccess() - { - $webhookRequest = $this->getTestWebhookRequest(); - $this->_objectManager->addSharedInstance($webhookRequest, WebhookRequest::class); - $this->dispatch(self::$entryPoint); - $this->assertEquals(200, $this->getResponse()->getHttpResponseCode()); - $this->_objectManager->removeSharedInstance(WebhookRequest::class); - } - - /** - * Returns mocked WebhookRequest - * - * @return WebhookRequest|\PHPUnit\Framework\MockObject_MockObject - */ - private function getWebhookRequest() - { - $webhookRequest = $this->getMockBuilder(WebhookRequest::class) - ->disableOriginalConstructor() - ->getMock(); - $webhookRequest->expects($this->any()) - ->method('getBody') - ->willReturn(file_get_contents(__DIR__ . '/../../_files/webhook_body.json')); - $webhookRequest->expects($this->any()) - ->method('getEventTopic') - ->willReturn('cases/rescore'); - $webhookRequest->expects($this->any()) - ->method('getHash') - ->willReturn('m/X29RcHWPSCDPgQuSXjnyTfKISJDopcdGbVsRLeqy8='); - - return $webhookRequest; - } - - /** - * Returns mocked test WebhookRequest - * - * @return WebhookRequest|\PHPUnit\Framework\MockObject_MockObject - */ - private function getTestWebhookRequest() - { - $webhookRequest = $this->getMockBuilder(WebhookRequest::class) - ->disableOriginalConstructor() - ->getMock(); - $webhookRequest->expects($this->any()) - ->method('getBody') - ->willReturn(file_get_contents(__DIR__ . '/../../_files/webhook_body.json')); - $webhookRequest->expects($this->any()) - ->method('getEventTopic') - ->willReturn('cases/test'); - $webhookRequest->expects($this->any()) - ->method('getHash') - ->willReturn('wyG0r9mOmv1IqVlN6ZqJ5sgA635yKW6lbSsqlYF2b8U='); - - return $webhookRequest; - } -} diff --git a/dev/tests/integration/testsuite/Magento/Signifyd/Model/CaseManagementTest.php b/dev/tests/integration/testsuite/Magento/Signifyd/Model/CaseManagementTest.php deleted file mode 100644 index 30603baf867ff..0000000000000 --- a/dev/tests/integration/testsuite/Magento/Signifyd/Model/CaseManagementTest.php +++ /dev/null @@ -1,83 +0,0 @@ -<?php -/** - * Copyright © Magento, Inc. All rights reserved. - * See COPYING.txt for license details. - */ -namespace Magento\Signifyd\Model; - -use Magento\Framework\Api\SearchCriteriaBuilder; -use Magento\Framework\App\ObjectManager; -use Magento\Sales\Api\Data\OrderInterface; -use Magento\Sales\Api\OrderRepositoryInterface; -use Magento\Signifyd\Api\Data\CaseInterface; -use Magento\TestFramework\Helper\Bootstrap; - -/** - * Contains test methods for case management service - */ -class CaseManagementTest extends \PHPUnit\Framework\TestCase -{ - /** - * @var CaseManagement - */ - private $caseManagement; - - /** - * @var ObjectManager - */ - private $objectManager; - - protected function setup() - { - $this->objectManager = Bootstrap::getObjectManager(); - $this->caseManagement = $this->objectManager->get(CaseManagement::class); - } - - /** - * @covers \Magento\Signifyd\Model\CaseManagement::create - * @magentoDataFixture Magento/Signifyd/_files/order_with_customer_and_two_simple_products.php - */ - public function testCreate() - { - $order = $this->getOrder(); - $case = $this->caseManagement->create($order->getEntityId()); - - self::assertNotEmpty($case->getEntityId()); - self::assertEquals(CaseInterface::STATUS_PENDING, $case->getStatus()); - self::assertEquals(CaseInterface::GUARANTEE_PENDING, $case->getGuaranteeDisposition()); - } - - /** - * @covers \Magento\Signifyd\Model\CaseManagement::getByOrderId - * @magentoDataFixture Magento/Signifyd/_files/case.php - */ - public function testGetByOrderId() - { - $order = $this->getOrder(); - $case = $this->caseManagement->getByOrderId($order->getEntityId()); - - self::assertEquals(CaseInterface::STATUS_PROCESSING, $case->getStatus()); - self::assertEquals(CaseInterface::DISPOSITION_GOOD, $case->getReviewDisposition()); - self::assertEquals('2016-12-12 15:17:17', $case->getCreatedAt()); - self::assertEquals('2016-12-12 19:23:16', $case->getUpdatedAt()); - } - - /** - * Get stored order - * @return OrderInterface - */ - private function getOrder() - { - /** @var SearchCriteriaBuilder $searchCriteriaBuilder */ - $searchCriteriaBuilder = $this->objectManager->get(SearchCriteriaBuilder::class); - $searchCriteria = $searchCriteriaBuilder->addFilter(OrderInterface::INCREMENT_ID, '100000001') - ->create(); - - $orderRepository = $this->objectManager->get(OrderRepositoryInterface::class); - $orders = $orderRepository->getList($searchCriteria) - ->getItems(); - - /** @var OrderInterface $order */ - return array_pop($orders); - } -} diff --git a/dev/tests/integration/testsuite/Magento/Signifyd/Model/CaseRepositoryTest.php b/dev/tests/integration/testsuite/Magento/Signifyd/Model/CaseRepositoryTest.php deleted file mode 100644 index ca98a20b15bec..0000000000000 --- a/dev/tests/integration/testsuite/Magento/Signifyd/Model/CaseRepositoryTest.php +++ /dev/null @@ -1,148 +0,0 @@ -<?php -/** - * Copyright © Magento, Inc. All rights reserved. - * See COPYING.txt for license details. - */ -namespace Magento\Signifyd\Model; - -use Magento\Framework\Api\FilterBuilder; -use Magento\Framework\Api\SearchCriteriaBuilder; -use Magento\Framework\App\ObjectManager; -use Magento\Signifyd\Api\Data\CaseInterface; -use Magento\TestFramework\Helper\Bootstrap; - -/** - * Testing case repository - */ -class CaseRepositoryTest extends \PHPUnit\Framework\TestCase -{ - /** - * @var ObjectManager - */ - private $objectManager; - - /** - * @var FilterBuilder - */ - private $filterBuilder; - - /** - * @var SearchCriteriaBuilder - */ - private $searchCriteriaBuilder; - - /** - * @var CaseRepository - */ - private $repository; - - /** - * @inheritdoc - */ - protected function setUp() - { - $this->objectManager = Bootstrap::getObjectManager(); - $this->filterBuilder = $this->objectManager->get(FilterBuilder::class); - $this->searchCriteriaBuilder = $this->objectManager->get(SearchCriteriaBuilder::class); - $this->repository = $this->objectManager->get(CaseRepository::class); - } - - /** - * @covers \Magento\Signifyd\Model\CaseRepository::delete - * @magentoDataFixture Magento/Signifyd/_files/case.php - */ - public function testDelete() - { - $filters = [ - $this->filterBuilder->setField('case_id') - ->setValue(123) - ->create() - ]; - $searchCriteria = $this->searchCriteriaBuilder->addFilters($filters)->create(); - $cases = $this->repository->getList($searchCriteria) - ->getItems(); - - $case = array_pop($cases); - $this->repository->delete($case); - - self::assertEmpty($this->repository->getList($searchCriteria)->getItems()); - } - - /** - * @covers \Magento\Signifyd\Model\CaseRepository::getById - * @magentoDataFixture Magento/Signifyd/_files/case.php - */ - public function testGetById() - { - $filters = [ - $this->filterBuilder->setField('case_id') - ->setValue(123) - ->create() - ]; - $searchCriteria = $this->searchCriteriaBuilder->addFilters($filters)->create(); - $cases = $this->repository->getList($searchCriteria) - ->getItems(); - - $case = array_pop($cases); - - $found = $this->repository->getById($case->getEntityId()); - - self::assertNotEmpty($found->getEntityId()); - self::assertEquals($case->getEntityId(), $found->getEntityId()); - self::assertEquals($case->getOrderId(), $found->getOrderId()); - } - - /** - * @covers \Magento\Signifyd\Model\CaseRepository::getList - * @magentoDataFixture Magento/Signifyd/_files/multiple_cases.php - */ - public function testGetListDateInterval() - { - $startDateInterval = [ - $this->filterBuilder->setField('created_at') - ->setConditionType('gteq') - ->setValue('2016-12-01 00:00:01') - ->create() - ]; - $endDateInterval = [ - $this->filterBuilder->setField('created_at') - ->setConditionType('lteq') - ->setValue('2016-12-03 23:59:59') - ->create() - ]; - - $this->searchCriteriaBuilder->addFilters($startDateInterval); - $searchCriteria = $this->searchCriteriaBuilder->addFilters($endDateInterval)->create(); - $items = $this->repository->getList($searchCriteria) - ->getItems(); - - self::assertCount(3, $items); - - for ($i = 1; $i < 4; $i ++) { - $current = array_shift($items); - self::assertEquals($i, $current->getCaseId()); - } - } - - /** - * @covers \Magento\Signifyd\Model\CaseRepository::getList - * @magentoDataFixture Magento/Signifyd/_files/multiple_cases.php - */ - public function testGetListStatusProcessing() - { - $filters = [ - $this->filterBuilder->setField('status') - ->setValue(CaseInterface::STATUS_PROCESSING) - ->create() - ]; - - $searchCriteria = $this->searchCriteriaBuilder->addFilters($filters)->create(); - $items = $this->repository->getList($searchCriteria) - ->getItems(); - - self::assertCount(1, $items); - - $case = array_pop($items); - self::assertEquals(123, $case->getCaseId()); - } -} diff --git a/dev/tests/integration/testsuite/Magento/Signifyd/Model/CaseServices/CreationServiceTest.php b/dev/tests/integration/testsuite/Magento/Signifyd/Model/CaseServices/CreationServiceTest.php deleted file mode 100644 index 6cf1e0bfddc9a..0000000000000 --- a/dev/tests/integration/testsuite/Magento/Signifyd/Model/CaseServices/CreationServiceTest.php +++ /dev/null @@ -1,245 +0,0 @@ -<?php -/** - * Copyright © Magento, Inc. All rights reserved. - * See COPYING.txt for license details. - */ -namespace Magento\Signifyd\Model\CaseServices; - -use Magento\Framework\Api\SearchCriteriaBuilder; -use Magento\Framework\App\ObjectManager; -use Magento\Sales\Api\Data\OrderInterface; -use Magento\Sales\Api\Data\OrderStatusHistoryInterface; -use Magento\Sales\Api\OrderRepositoryInterface; -use Magento\Sales\Model\Order; -use Magento\Sales\Model\ResourceModel\Order\Grid\Collection; -use Magento\Signifyd\Api\CaseRepositoryInterface; -use Magento\Signifyd\Model\SignifydGateway\ApiCallException; -use Magento\Signifyd\Model\SignifydGateway\ApiClient; -use Magento\Signifyd\Model\SignifydGateway\Client\RequestBuilder; -use Magento\Signifyd\Model\SignifydGateway\Gateway; -use Magento\TestFramework\Helper\Bootstrap; -use PHPUnit\Framework\MockObject_MockObject as MockObject; -use Psr\Log\LoggerInterface; - -/** - * Class tests interaction with Signifyd Case creation service - * - * @SuppressWarnings(PHPMD.CouplingBetweenObjects) - */ -class CreationServiceTest extends \PHPUnit\Framework\TestCase -{ - /** - * @var ObjectManager - */ - private $objectManager; - - /** - * @var OrderInterface - */ - private $order; - - /** - * @var RequestBuilder|MockObject - */ - private $requestBuilder; - - /** - * @var LoggerInterface|MockObject - */ - private $logger; - - /** - * @var CreationService - */ - private $service; - - /** - * @inheritdoc - */ - protected function setUp() - { - $this->objectManager = Bootstrap::getObjectManager(); - - $this->requestBuilder = $this->getMockBuilder(RequestBuilder::class) - ->disableOriginalConstructor() - ->setMethods(['doRequest']) - ->getMock(); - - $apiClient = $this->objectManager->create( - ApiClient::class, - ['requestBuilder' => $this->requestBuilder] - ); - - $gateway = $this->objectManager->create( - Gateway::class, - ['apiClient' => $apiClient] - ); - - $this->logger = $this->getMockBuilder(LoggerInterface::class) - ->disableOriginalConstructor() - ->setMethods(['error']) - ->getMockForAbstractClass(); - - $this->service = $this->objectManager->create( - CreationService::class, - [ - 'signifydGateway' => $gateway, - 'logger' => $this->logger - ] - ); - } - - /** - * @covers \Magento\Signifyd\Model\CaseServices\CreationService::createForOrder - * @magentoDataFixture Magento/Signifyd/_files/order_with_customer_and_two_simple_products.php - */ - public function testCreateForOrderWithEmptyResponse() - { - $order = $this->getOrder(); - $exceptionMessage = 'Response is not valid JSON: Decoding failed: Syntax error'; - - $this->requestBuilder->expects(self::once()) - ->method('doRequest') - ->willThrowException(new ApiCallException($exceptionMessage)); - - $this->logger->expects(self::once()) - ->method('error') - ->with($exceptionMessage); - - $result = $this->service->createForOrder($order->getEntityId()); - self::assertTrue($result); - } - - /** - * @covers \Magento\Signifyd\Model\CaseServices\CreationService::createForOrder - * @magentoDataFixture Magento/Signifyd/_files/order_with_customer_and_two_simple_products.php - */ - public function testCreateForOrderWithBadResponse() - { - $order = $this->getOrder(); - $responseData = [ - 'messages' => [ - 'Something wrong' - ] - ]; - $exceptionMessage = 'Bad Request - The request could not be parsed. Response: ' . json_encode($responseData); - - $this->requestBuilder->expects(self::once()) - ->method('doRequest') - ->willThrowException(new ApiCallException($exceptionMessage)); - - $this->logger->expects(self::once()) - ->method('error') - ->with($exceptionMessage); - - $result = $this->service->createForOrder($order->getEntityId()); - self::assertTrue($result); - } - - /** - * @covers \Magento\Signifyd\Model\CaseServices\CreationService::createForOrder - * @magentoDataFixture Magento/Signifyd/_files/order_with_customer_and_two_simple_products.php - */ - public function testCreateOrderWithEmptyInvestigationId() - { - $order = $this->getOrder(); - - $this->requestBuilder->expects(self::once()) - ->method('doRequest') - ->willReturn([]); - - $this->logger->expects(self::once()) - ->method('error') - ->with('Expected field "investigationId" missed.'); - - $result = $this->service->createForOrder($order->getEntityId()); - self::assertTrue($result); - } - - /** - * @covers \Magento\Signifyd\Model\CaseServices\CreationService::createForOrder - * @magentoDataFixture Magento/Signifyd/_files/order_with_customer_and_two_simple_products.php - */ - public function testCreateForOrder() - { - $order = $this->getOrder(); - - $this->requestBuilder->expects(self::once()) - ->method('doRequest') - ->willReturn(['investigationId' => 123123]); - - $this->logger->expects(self::never()) - ->method('error'); - - $result = $this->service->createForOrder($order->getEntityId()); - self::assertTrue($result); - - /** @var CaseRepositoryInterface $caseRepository */ - $caseRepository = $this->objectManager->get(CaseRepositoryInterface::class); - $caseEntity = $caseRepository->getByCaseId(123123); - $gridGuarantyStatus = $this->getOrderGridGuarantyStatus($caseEntity->getOrderId()); - - self::assertNotEmpty($caseEntity); - self::assertEquals($order->getEntityId(), $caseEntity->getOrderId()); - self::assertEquals( - $gridGuarantyStatus, - $caseEntity->getGuaranteeDisposition(), - 'Signifyd guaranty status in sales_order_grid table does not match case entity guaranty status' - ); - - /** @var OrderRepositoryInterface $orderRepository */ - $orderRepository = $this->objectManager->get(OrderRepositoryInterface::class); - $order = $orderRepository->get($caseEntity->getOrderId()); - self::assertEquals(Order::STATE_HOLDED, $order->getState()); - - $histories = $order->getStatusHistories(); - self::assertNotEmpty($histories); - - /** @var OrderStatusHistoryInterface $orderHoldComment */ - $orderHoldComment = array_pop($histories); - self::assertInstanceOf(OrderStatusHistoryInterface::class, $orderHoldComment); - self::assertEquals("Awaiting the Signifyd guarantee disposition.", $orderHoldComment->getComment()); - } - - /** - * Get stored order - * - * @return OrderInterface - */ - private function getOrder() - { - if ($this->order === null) { - - /** @var SearchCriteriaBuilder $searchCriteriaBuilder */ - $searchCriteriaBuilder = $this->objectManager->get(SearchCriteriaBuilder::class); - $searchCriteria = $searchCriteriaBuilder->addFilter(OrderInterface::INCREMENT_ID, '100000001') - ->create(); - - $orderRepository = $this->objectManager->get(OrderRepositoryInterface::class); - $orders = $orderRepository->getList($searchCriteria) - ->getItems(); - - $this->order = array_pop($orders); - } - - return $this->order; - } - - /** - * Returns value of signifyd_guarantee_status column from sales order grid - * - * @param int $orderEntityId - * @return string|null - */ - private function getOrderGridGuarantyStatus($orderEntityId) - { - /** @var Collection $orderGridCollection */ - $orderGridCollection = $this->objectManager->get(Collection::class); - - $items = $orderGridCollection->addFilter($orderGridCollection->getIdFieldName(), $orderEntityId) - ->getItems(); - $result = array_pop($items); - - return isset($result['signifyd_guarantee_status']) ? $result['signifyd_guarantee_status'] : null; - } -} diff --git a/dev/tests/integration/testsuite/Magento/Signifyd/Model/CaseServices/UpdatingServiceTest.php b/dev/tests/integration/testsuite/Magento/Signifyd/Model/CaseServices/UpdatingServiceTest.php deleted file mode 100644 index 50e510ca072be..0000000000000 --- a/dev/tests/integration/testsuite/Magento/Signifyd/Model/CaseServices/UpdatingServiceTest.php +++ /dev/null @@ -1,186 +0,0 @@ -<?php -/** - * Copyright © Magento, Inc. All rights reserved. - * See COPYING.txt for license details. - */ -namespace Magento\Signifyd\Model\CaseServices; - -use Magento\Framework\App\ObjectManager; -use Magento\Sales\Api\Data\OrderStatusHistoryInterface; -use Magento\Sales\Api\OrderRepositoryInterface; -use Magento\Sales\Model\Order; -use Magento\Sales\Model\ResourceModel\Order\Grid\Collection; -use Magento\Signifyd\Api\CaseRepositoryInterface; -use Magento\Signifyd\Api\Data\CaseInterface; -use Magento\Signifyd\Model\MessageGenerators\GeneratorFactory; -use Magento\TestFramework\Helper\Bootstrap; - -/** - * Contains tests for case entity updating service. - */ -class UpdatingServiceTest extends \PHPUnit\Framework\TestCase -{ - /** - * @var ObjectManager - */ - private $objectManager; - - /** - * @var UpdatingService - */ - private $service; - - /** - * @inheritdoc - */ - protected function setUp() - { - $this->objectManager = Bootstrap::getObjectManager(); - - /** @var GeneratorFactory $messageFactory */ - $messageFactory = $this->objectManager->get(GeneratorFactory::class); - $messageGenerator = $messageFactory->create('cases/creation'); - - $this->service = $this->objectManager->create(UpdatingService::class, [ - 'messageGenerator' => $messageGenerator - ]); - } - - /** - * Checks case updating flow and messages in order comments history. - * Also checks that order is unholded when case guarantee disposition is APPROVED. - * - * @covers \Magento\Signifyd\Model\CaseServices\UpdatingService::update - * @magentoDataFixture Magento/Signifyd/_files/case.php - */ - public function testUpdate() - { - $caseId = 123; - $data = [ - 'caseId' => $caseId, - 'score' => 750, - 'orderId' => '100000001', - 'reviewDisposition' => CaseInterface::DISPOSITION_FRAUDULENT, - 'associatedTeam' => [ - 'teamName' => 'AnyTeam', - 'teamId' => 26, - 'getAutoDismiss' => true, - 'getTeamDismissalDays' => 2 - ], - 'createdAt' => '2017-01-05T14:23:26-0800', - 'updatedAt' => '2017-01-05T14:44:26-0800', - 'guaranteeDisposition' => CaseInterface::GUARANTEE_APPROVED - ]; - - /** @var CaseRepositoryInterface $caseRepository */ - $caseRepository = $this->objectManager->get(CaseRepositoryInterface::class); - /** @var CaseInterface $caseEntity */ - $caseEntity = $caseRepository->getByCaseId($caseId); - - $this->service->update($caseEntity, $data); - - $caseEntity = $caseRepository->getByCaseId($caseId); - $orderEntityId = $caseEntity->getOrderId(); - $gridGuarantyStatus = $this->getOrderGridGuarantyStatus($orderEntityId); - - self::assertNotEmpty($caseEntity); - self::assertEquals('2017-01-05 22:23:26', $caseEntity->getCreatedAt()); - self::assertEquals(CaseInterface::GUARANTEE_APPROVED, $caseEntity->getGuaranteeDisposition()); - self::assertEquals('AnyTeam', $caseEntity->getAssociatedTeam()['teamName']); - self::assertEquals(true, $caseEntity->isGuaranteeEligible()); - self::assertEquals(CaseInterface::STATUS_PROCESSING, $caseEntity->getStatus()); - self::assertEquals($orderEntityId, $caseEntity->getOrderId()); - self::assertEquals( - $gridGuarantyStatus, - $caseEntity->getGuaranteeDisposition(), - 'Signifyd guaranty status in sales_order_grid table does not match case entity guaranty status' - ); - - /** @var OrderRepositoryInterface $orderRepository */ - $orderRepository = $this->objectManager->get(OrderRepositoryInterface::class); - $order = $orderRepository->get($caseEntity->getOrderId()); - self::assertEquals(Order::STATE_PROCESSING, $order->getState()); - $histories = $order->getStatusHistories(); - self::assertNotEmpty($histories); - - /** @var OrderStatusHistoryInterface $caseCreationComment */ - $caseCreationComment = array_pop($histories); - self::assertInstanceOf(OrderStatusHistoryInterface::class, $caseCreationComment); - self::assertEquals("Signifyd Case $caseId has been created for order.", $caseCreationComment->getComment()); - } - - /** - * Checks that order is holded when case guarantee disposition is DECLINED. - * - * @covers \Magento\Signifyd\Model\CaseServices\UpdatingService::update - * @magentoDataFixture Magento/Signifyd/_files/approved_case.php - */ - public function testOrderStateAfterDeclinedGuaranteeDisposition() - { - $caseId = 123; - $data = [ - 'caseId' => $caseId, - 'orderId' => '100000001', - 'guaranteeDisposition' => CaseInterface::GUARANTEE_DECLINED - ]; - - /** @var CaseRepositoryInterface $caseRepository */ - $caseRepository = $this->objectManager->get(CaseRepositoryInterface::class); - $caseEntity = $caseRepository->getByCaseId($caseId); - - $this->service->update($caseEntity, $data); - - /** @var OrderRepositoryInterface $orderRepository */ - $orderRepository = $this->objectManager->get(OrderRepositoryInterface::class); - $order = $orderRepository->get($caseEntity->getOrderId()); - - self::assertEquals(Order::STATE_HOLDED, $order->getState()); - } - - /** - * Checks that order doesn't become holded - * when previous case guarantee disposition was DECLINED - * and webhook without guarantee disposition was received. - * - * @covers \Magento\Signifyd\Model\CaseServices\UpdatingService::update - * @magentoDataFixture Magento/Signifyd/_files/declined_case.php - */ - public function testOrderStateAfterWebhookWithoutGuaranteeDisposition() - { - $caseId = 123; - $data = [ - 'caseId' => $caseId, - 'orderId' => '100000001' - ]; - - /** @var CaseRepositoryInterface $caseRepository */ - $caseRepository = $this->objectManager->get(CaseRepositoryInterface::class); - $caseEntity = $caseRepository->getByCaseId($caseId); - - $this->service->update($caseEntity, $data); - - /** @var OrderRepositoryInterface $orderRepository */ - $orderRepository = $this->objectManager->get(OrderRepositoryInterface::class); - $order = $orderRepository->get($caseEntity->getOrderId()); - - self::assertEquals(Order::STATE_PROCESSING, $order->getState()); - } - - /** - * Returns value of signifyd_guarantee_status column from sales order grid - * - * @param int $orderEntityId - * @return string|null - */ - private function getOrderGridGuarantyStatus($orderEntityId) - { - /** @var Collection $orderGridCollection */ - $orderGridCollection = $this->objectManager->get(Collection::class); - - $items = $orderGridCollection->addFilter($orderGridCollection->getIdFieldName(), $orderEntityId) - ->getItems(); - $result = array_pop($items); - - return isset($result['signifyd_guarantee_status']) ? $result['signifyd_guarantee_status'] : null; - } -} diff --git a/dev/tests/integration/testsuite/Magento/Signifyd/Model/Guarantee/CancelingServiceTest.php b/dev/tests/integration/testsuite/Magento/Signifyd/Model/Guarantee/CancelingServiceTest.php deleted file mode 100644 index 2cc7a9a1f240a..0000000000000 --- a/dev/tests/integration/testsuite/Magento/Signifyd/Model/Guarantee/CancelingServiceTest.php +++ /dev/null @@ -1,158 +0,0 @@ -<?php -/** - * Copyright © Magento, Inc. All rights reserved. - * See COPYING.txt for license details. - */ -namespace Magento\Signifyd\Model\Guarantee; - -use Magento\Framework\App\ObjectManager; -use Magento\Sales\Api\Data\OrderStatusHistoryInterface; -use Magento\Sales\Api\OrderRepositoryInterface; -use Magento\Signifyd\Api\CaseRepositoryInterface; -use Magento\Signifyd\Api\Data\CaseInterface; -use Magento\Signifyd\Model\SignifydGateway\Gateway; -use Magento\Signifyd\Model\SignifydGateway\GatewayException; -use Magento\TestFramework\Helper\Bootstrap; -use PHPUnit\Framework\MockObject_MockObject as MockObject; -use Psr\Log\LoggerInterface; - -/** - * Contains test cases for canceling Signifyd guarantee flow. - */ -class CancelingServiceTest extends \PHPUnit\Framework\TestCase -{ - private static $caseId = 123; - - /** - * @var ObjectManager - */ - private $objectManager; - - /** - * @var Gateway|MockObject - */ - private $gateway; - - /** - * @var LoggerInterface|MockObject - */ - private $logger; - - /** - * @var CancelingService; - */ - private $service; - - /** - * @inheritdoc - */ - protected function setUp() - { - $this->objectManager = Bootstrap::getObjectManager(); - - $this->gateway = $this->getMockBuilder(Gateway::class) - ->disableOriginalConstructor() - ->setMethods(['cancelGuarantee']) - ->getMock(); - - $this->logger = $this->getMockBuilder(LoggerInterface::class) - ->disableOriginalConstructor() - ->getMock(); - - $this->service = $this->objectManager->create(CancelingService::class, [ - 'gateway' => $this->gateway, - 'logger' => $this->logger - ]); - } - - /** - * Checks a test case, when Signifyd guarantee was canceled. - * - * @covers \Magento\Signifyd\Model\Guarantee\CancelingService::cancelForOrder - * @magentoDataFixture Magento/Signifyd/_files/case.php - * @magentoConfigFixture current_store fraud_protection/signifyd/active 1 - */ - public function testCancelForOrderWithCanceledGuarantee() - { - /** @var CaseRepositoryInterface $caseRepository */ - $caseRepository = $this->objectManager->get(CaseRepositoryInterface::class); - $caseEntity = $caseRepository->getByCaseId(self::$caseId); - $caseEntity->setGuaranteeDisposition(CaseInterface::GUARANTEE_CANCELED); - $caseRepository->save($caseEntity); - - $this->gateway->expects(self::never()) - ->method('cancelGuarantee'); - - $this->logger->expects(self::never()) - ->method('error'); - - $result = $this->service->cancelForOrder($caseEntity->getOrderId()); - self::assertFalse($result); - } - - /** - * Checks a test case, when Signifyd gateway throws an exception. - * - * @covers \Magento\Signifyd\Model\Guarantee\CancelingService::cancelForOrder - * @magentoDataFixture Magento/Signifyd/_files/approved_case.php - * @magentoConfigFixture current_store fraud_protection/signifyd/active 1 - */ - public function testCancelForOrderWithFailedRequest() - { - $exceptionMessage = 'Something wrong.'; - /** @var CaseRepositoryInterface $caseRepository */ - $caseRepository = $this->objectManager->get(CaseRepositoryInterface::class); - $caseEntity = $caseRepository->getByCaseId(self::$caseId); - - $this->gateway->expects(self::once()) - ->method('cancelGuarantee') - ->with(self::equalTo(self::$caseId)) - ->willThrowException(new GatewayException($exceptionMessage)); - - $this->logger->expects(self::once()) - ->method('error') - ->with(self::equalTo($exceptionMessage)); - - $result = $this->service->cancelForOrder($caseEntity->getOrderId()); - self::assertFalse($result); - } - - /** - * Checks a test case, when request to cancel is submitted and case entity is updated successfully. - * - * @covers \Magento\Signifyd\Model\Guarantee\CancelingService::cancelForOrder - * @magentoDataFixture Magento/Signifyd/_files/approved_case.php - * @magentoConfigFixture current_store fraud_protection/signifyd/active 1 - */ - public function testCancelForOrder() - { - /** @var CaseRepositoryInterface $caseRepository */ - $caseRepository = $this->objectManager->get(CaseRepositoryInterface::class); - $caseEntity = $caseRepository->getByCaseId(self::$caseId); - - $this->gateway->expects(self::once()) - ->method('cancelGuarantee') - ->with(self::equalTo(self::$caseId)) - ->willReturn(CaseInterface::GUARANTEE_CANCELED); - - $this->logger->expects(self::never()) - ->method('error'); - - $result = $this->service->cancelForOrder($caseEntity->getOrderId()); - self::assertTrue($result); - - $updatedCase = $caseRepository->getByCaseId(self::$caseId); - self::assertEquals(CaseInterface::GUARANTEE_CANCELED, $updatedCase->getGuaranteeDisposition()); - - /** @var OrderRepositoryInterface $orderRepository */ - $orderRepository = $this->objectManager->get(OrderRepositoryInterface::class); - $order = $orderRepository->get($updatedCase->getOrderId()); - $histories = $order->getStatusHistories(); - self::assertNotEmpty($histories); - - /** @var OrderStatusHistoryInterface $caseCreationComment */ - $caseCreationComment = array_pop($histories); - self::assertInstanceOf(OrderStatusHistoryInterface::class, $caseCreationComment); - self::assertEquals('Case Update: Case guarantee has been cancelled.', $caseCreationComment->getComment()); - } -} diff --git a/dev/tests/integration/testsuite/Magento/Signifyd/Model/Guarantee/CreationServiceTest.php b/dev/tests/integration/testsuite/Magento/Signifyd/Model/Guarantee/CreationServiceTest.php deleted file mode 100644 index 157e3270648b3..0000000000000 --- a/dev/tests/integration/testsuite/Magento/Signifyd/Model/Guarantee/CreationServiceTest.php +++ /dev/null @@ -1,155 +0,0 @@ -<?php -/** - * Copyright © Magento, Inc. All rights reserved. - * See COPYING.txt for license details. - */ -namespace Magento\Signifyd\Model\Guarantee; - -use Magento\Framework\App\ObjectManager; -use Magento\Sales\Api\Data\OrderStatusHistoryInterface; -use Magento\Sales\Api\OrderRepositoryInterface; -use Magento\Signifyd\Api\CaseRepositoryInterface; -use Magento\Signifyd\Api\Data\CaseInterface; -use Magento\Signifyd\Model\SignifydGateway\Gateway; -use Magento\Signifyd\Model\SignifydGateway\GatewayException; -use Magento\TestFramework\Helper\Bootstrap; -use PHPUnit\Framework\MockObject_MockObject as MockObject; -use Psr\Log\LoggerInterface; - -/** - * Contains positive and negative test cases for Signifyd case guarantee creation flow. - */ -class CreationServiceTest extends \PHPUnit\Framework\TestCase -{ - /** - * @var CreationService - */ - private $service; - - /** - * @var Gateway|MockObject - */ - private $gateway; - - /** - * @var LoggerInterface|MockObject - */ - private $logger; - - /** - * @var ObjectManager - */ - private $objectManager; - - /** - * @inheritdoc - */ - protected function setUp() - { - /** @var ObjectManager $objectManager */ - $this->objectManager = Bootstrap::getObjectManager(); - - $this->gateway = $this->getMockBuilder(Gateway::class) - ->disableOriginalConstructor() - ->setMethods(['submitCaseForGuarantee']) - ->getMock(); - - $this->logger = $this->getMockBuilder(LoggerInterface::class) - ->disableOriginalConstructor() - ->getMock(); - - $this->service = $this->objectManager->create(CreationService::class, [ - 'gateway' => $this->gateway, - 'logger' => $this->logger - ]); - } - - /** - * Checks a test case, when Signifyd case entity cannot be found - * for a specified order. - * - * @covers \Magento\Signifyd\Model\Guarantee\CreationService::createForOrder - */ - public function testCreateWithoutCaseEntity() - { - $orderId = 123; - - $this->gateway->expects(self::never()) - ->method('submitCaseForGuarantee'); - - $result = $this->service->createForOrder($orderId); - self::assertFalse($result); - } - - /** - * Checks a test case, when request is failing. - * - * @covers \Magento\Signifyd\Model\Guarantee\CreationService::createForOrder - * @magentoDataFixture Magento/Signifyd/_files/case.php - */ - public function testCreateWithFailedRequest() - { - $caseEntity = $this->getCaseEntity(); - - $this->gateway->expects(self::once()) - ->method('submitCaseForGuarantee') - ->willThrowException(new GatewayException('Something wrong')); - - $this->logger->expects(self::once()) - ->method('error') - ->with('Something wrong'); - - $result = $this->service->createForOrder($caseEntity->getOrderId()); - self::assertFalse($result); - } - - /** - * Checks a test case, when case entity is updated successfully. - * - * @covers \Magento\Signifyd\Model\Guarantee\CreationService::createForOrder - * @magentoDataFixture Magento/Signifyd/_files/case.php - * @magentoConfigFixture current_store fraud_protection/signifyd/active 1 - */ - public function testCreate() - { - $caseEntity = $this->getCaseEntity(); - - $this->gateway->expects(self::once()) - ->method('submitCaseForGuarantee') - ->with($caseEntity->getCaseId()) - ->willReturn(CaseInterface::GUARANTEE_IN_REVIEW); - - $this->logger->expects(self::never()) - ->method('error'); - - $result = $this->service->createForOrder($caseEntity->getOrderId()); - self::assertTrue($result); - - $updatedCase = $this->getCaseEntity(); - self::assertEquals(CaseInterface::GUARANTEE_IN_REVIEW, $updatedCase->getGuaranteeDisposition()); - self::assertEquals(CaseInterface::STATUS_PROCESSING, $updatedCase->getStatus()); - - /** @var OrderRepositoryInterface $orderRepository */ - $orderRepository = $this->objectManager->get(OrderRepositoryInterface::class); - $order = $orderRepository->get($updatedCase->getOrderId()); - $histories = $order->getStatusHistories(); - self::assertNotEmpty($histories); - - /** @var OrderStatusHistoryInterface $caseCreationComment */ - $caseCreationComment = array_pop($histories); - self::assertInstanceOf(OrderStatusHistoryInterface::class, $caseCreationComment); - self::assertEquals('Case Update: Case is submitted for guarantee.', $caseCreationComment->getComment()); - } - - /** - * Gets case entity. - * - * @return \Magento\Signifyd\Api\Data\CaseInterface|null - */ - private function getCaseEntity() - { - /** @var CaseRepositoryInterface $caseRepository */ - $caseRepository = $this->objectManager->get(CaseRepositoryInterface::class); - return $caseRepository->getByCaseId(123); - } -} diff --git a/dev/tests/integration/testsuite/Magento/Signifyd/Model/SignifydGateway/Request/CreateCaseBuilderTest.php b/dev/tests/integration/testsuite/Magento/Signifyd/Model/SignifydGateway/Request/CreateCaseBuilderTest.php deleted file mode 100644 index 594c065185ac7..0000000000000 --- a/dev/tests/integration/testsuite/Magento/Signifyd/Model/SignifydGateway/Request/CreateCaseBuilderTest.php +++ /dev/null @@ -1,291 +0,0 @@ -<?php -/** - * Copyright © Magento, Inc. All rights reserved. - * See COPYING.txt for license details. - */ -namespace Magento\Signifyd\Model\SignifydGateway\Request; - -use Magento\Framework\Config\ScopeInterface; -use Magento\Signifyd\Model\SignifydOrderSessionId; -use Magento\TestFramework\Helper\Bootstrap; -use Magento\Framework\App\Area; -use Magento\Framework\Intl\DateTimeFactory; -use Magento\Framework\ObjectManagerInterface; -use Magento\Sales\Model\Order; -use Magento\Customer\Api\CustomerRepositoryInterface; -use Magento\Framework\App\ProductMetadataInterface; - -/** - * Class CreateCaseBuilderTest - * @magentoAppIsolation enabled - * @SuppressWarnings(PHPMD.CouplingBetweenObjects) - */ -class CreateCaseBuilderTest extends \PHPUnit\Framework\TestCase -{ - /** - * @var ObjectManagerInterface - */ - private $objectManager; - - /** - * @var CreateCaseBuilder - */ - private $caseBuilder; - - /** - * @var DateTimeFactory - */ - private $dateTimeFactory; - - /** - * Initial setup - */ - protected function setUp() - { - $bootstrap = Bootstrap::getInstance(); - $bootstrap->loadArea(Area::AREA_FRONTEND); - $this->objectManager = Bootstrap::getObjectManager(); - $this->dateTimeFactory = $this->objectManager->create(DateTimeFactory::class); - $this->caseBuilder = $this->objectManager->create(CreateCaseBuilder::class); - } - - /** - * Test builder on order with customer, simple product, frontend area, - * PayPal gateway, shipping and billing addresses, with two orders - * - * @covers \Magento\Signifyd\Model\SignifydGateway\Request\CreateCaseBuilder::build() - * @magentoDataFixture Magento/Signifyd/_files/order_with_customer_and_two_simple_products.php - * @SuppressWarnings(PHPMD.ExcessiveMethodLength) - */ - public function testCreateCaseBuilderWithFullSetOfData() - { - /** @var Order $order */ - $order = $this->objectManager->create(Order::class); - $order->loadByIncrementId('100000001'); - - $orderItems = $order->getAllItems(); - $payment = $order->getPayment(); - $billingAddress = $order->getBillingAddress(); - $shippingAddress = $order->getShippingAddress(); - - /** @var CustomerRepositoryInterface $customerRepository */ - $customerRepository = $this->objectManager->create(CustomerRepositoryInterface::class); - $customer = $customerRepository->getById($order->getCustomerId()); - - $productMetadata = $this->objectManager->create(ProductMetadataInterface::class); - - /** @var SignifydOrderSessionId $signifydOrderSessionId */ - $signifydOrderSessionId = $this->objectManager->create(SignifydOrderSessionId::class); - - $expected = [ - 'purchase' => [ - 'orderSessionId' => $signifydOrderSessionId->get($order->getQuoteId()), - 'browserIpAddress' => $order->getRemoteIp(), - 'orderId' => $order->getIncrementId(), - 'createdAt' => date('c', strtotime(date('Y-m-d 00:00:55'))), - 'paymentGateway' => 'paypal_account', - 'transactionId' => $payment->getLastTransId(), - 'currency' => $order->getOrderCurrencyCode(), - 'avsResponseCode' => '', - 'cvvResponseCode' => '', - 'orderChannel' => 'WEB', - 'totalPrice' => $order->getGrandTotal(), - 'shipments' => [ - 0 => [ - 'shipper' => 'Flat Rate', - 'shippingMethod' => 'Fixed', - 'shippingPrice' => $order->getShippingAmount() - ] - ], - 'products' => [ - 0 => [ - 'itemId' => $orderItems[0]->getSku(), - 'itemName' => $orderItems[0]->getName(), - 'itemPrice' => $orderItems[0]->getPrice(), - 'itemQuantity' => $orderItems[0]->getQtyOrdered(), - 'itemUrl' => $orderItems[0]->getProduct()->getProductUrl(), - 'itemWeight' => $orderItems[0]->getProduct()->getWeight() - ], - 1 => [ - 'itemId' => $orderItems[1]->getSku(), - 'itemName' => $orderItems[1]->getName(), - 'itemPrice' => $orderItems[1]->getPrice(), - 'itemQuantity' => $orderItems[1]->getQtyOrdered(), - 'itemUrl' => $orderItems[1]->getProduct()->getProductUrl(), - 'itemWeight' => $orderItems[1]->getProduct()->getWeight() - ] - ], - 'paymentMethod' => 'PAYPAL_ACCOUNT' - ], - 'card' => [ - 'cardHolderName' => 'firstname lastname', - 'last4' => $payment->getCcLast4(), - 'expiryMonth' => $payment->getCcExpMonth(), - 'expiryYear' => $payment->getCcExpYear(), - 'billingAddress' => [ - 'streetAddress' => 'street', - 'city' => $billingAddress->getCity(), - 'provinceCode' => $billingAddress->getRegionCode(), - 'postalCode' => $billingAddress->getPostcode(), - 'countryCode' => $billingAddress->getCountryId(), - 'unit' => '' - ] - ], - 'recipient' => [ - 'fullName' => $shippingAddress->getName(), - 'confirmationEmail' => $shippingAddress->getEmail(), - 'confirmationPhone' => $shippingAddress->getTelephone(), - 'deliveryAddress' => [ - 'streetAddress' => '6161 West Centinela Avenue', - 'unit' => 'app. 33', - 'city' => $shippingAddress->getCity(), - 'provinceCode' => $shippingAddress->getRegionCode(), - 'postalCode' => $shippingAddress->getPostcode(), - 'countryCode' => $shippingAddress->getCountryId() - ] - ], - 'userAccount' => [ - 'email' => $customer->getEmail(), - 'username' => $customer->getEmail(), - 'phone' => $order->getBillingAddress()->getTelephone(), - 'accountNumber' => $customer->getId(), - 'createdDate' => $this->formatDate($customer->getCreatedAt()), - 'lastUpdateDate' => $this->formatDate($customer->getUpdatedAt()), - 'aggregateOrderCount' => 2, - 'aggregateOrderDollars' => 150.0 - ], - 'seller' => $this->getSellerData(), - 'platformAndClient' => [ - 'storePlatform' => $productMetadata->getName() . ' ' . $productMetadata->getEdition(), - 'storePlatformVersion' => $productMetadata->getVersion(), - 'signifydClientApp' => $productMetadata->getName(), - 'signifydClientAppVersion' => '1.0' - ] - ]; - - self::assertEquals( - $expected, - $this->caseBuilder->build($order->getEntityId()) - ); - } - - /** - * Test builder on order with guest, virtual product, admin area, - * none PayPal gateway, no shipping address, without credit card data - * - * @covers \Magento\Signifyd\Model\SignifydGateway\Request\CreateCaseBuilder::build() - * @magentoDataFixture Magento/Signifyd/_files/order_with_guest_and_virtual_product.php - */ - public function testCreateCaseBuilderWithVirtualProductAndGuest() - { - /** @var Order $order */ - $order = $this->objectManager->create(Order::class); - $order->loadByIncrementId('100000002'); - - $scope = $this->objectManager->get(ScopeInterface::class); - $scope->setCurrentScope(Area::AREA_ADMINHTML); - - $orderItems = $order->getAllItems(); - $product = $orderItems[0]->getProduct(); - $payment = $order->getPayment(); - $billingAddress = $order->getBillingAddress(); - $productMetadata = $this->objectManager->create(ProductMetadataInterface::class); - - /** @var SignifydOrderSessionId $quoteSessionId */ - $quoteSessionId = $this->objectManager->create(SignifydOrderSessionId::class); - - $expected = [ - 'purchase' => [ - 'orderSessionId' => $quoteSessionId->get($order->getQuoteId()), - 'browserIpAddress' => $order->getRemoteIp(), - 'orderId' => $order->getIncrementId(), - 'createdAt' => '2016-12-12T12:00:55+00:00', - 'paymentGateway' => $payment->getMethod(), - 'transactionId' => $payment->getLastTransId(), - 'currency' => $order->getOrderCurrencyCode(), - 'avsResponseCode' => 'Y', - 'cvvResponseCode' => 'M', - 'orderChannel' => 'PHONE', - 'totalPrice' => $order->getGrandTotal(), - 'products' => [ - 0 => [ - 'itemId' => $orderItems[0]->getSku(), - 'itemName' => $orderItems[0]->getName(), - 'itemPrice' => $orderItems[0]->getPrice(), - 'itemQuantity' => $orderItems[0]->getQtyOrdered(), - 'itemUrl' => $product->getProductUrl() - ], - ], - 'paymentMethod' => 'PAYMENT_CARD' - ], - 'card' => [ - 'cardHolderName' => 'firstname lastname', - 'billingAddress' => [ - 'streetAddress' => 'street', - 'city' => $billingAddress->getCity(), - 'provinceCode' => $billingAddress->getRegionCode(), - 'postalCode' => $billingAddress->getPostcode(), - 'countryCode' => $billingAddress->getCountryId(), - 'unit' => '' - ] - ], - 'seller' => $this->getSellerData(), - 'platformAndClient' => [ - 'storePlatform' => $productMetadata->getName() . ' ' . $productMetadata->getEdition(), - 'storePlatformVersion' => $productMetadata->getVersion(), - 'signifydClientApp' => $productMetadata->getName(), - 'signifydClientAppVersion' => '1.0' - ] - ]; - - self::assertEquals( - $expected, - $this->caseBuilder->build($order->getEntityId()) - ); - } - - /** - * Return seller data according to fixture - * - * @return array - */ - private function getSellerData() - { - return [ - 'name' => 'Sample Store', - 'domain' => 'm2.com', - 'shipFromAddress' => [ - 'streetAddress' => '6161 West Centinela Avenue', - 'unit' => 'app. 111', - 'city' => 'Culver City', - 'provinceCode' => 'AE', - 'postalCode' => '90230', - 'countryCode' => 'US', - ], - 'corporateAddress' => [ - 'streetAddress' => '5th Avenue', - 'unit' => '75', - 'city' => 'New York', - 'provinceCode' => 'MH', - 'postalCode' => '19032', - 'countryCode' => 'US', - ], - ]; - } - - /** - * Format date in ISO8601 - * - * @param string $date - * @return string - */ - private function formatDate($date) - { - $result = $this->dateTimeFactory->create( - $date, - new \DateTimeZone('UTC') - ); - - return $result->format(\DateTime::ATOM); - } -} diff --git a/dev/tests/integration/testsuite/Magento/Signifyd/Observer/PlaceOrderTest.php b/dev/tests/integration/testsuite/Magento/Signifyd/Observer/PlaceOrderTest.php deleted file mode 100644 index e547187be5ed7..0000000000000 --- a/dev/tests/integration/testsuite/Magento/Signifyd/Observer/PlaceOrderTest.php +++ /dev/null @@ -1,218 +0,0 @@ -<?php -/** - * Copyright © Magento, Inc. All rights reserved. - * See COPYING.txt for license details. - */ -namespace Magento\Signifyd\Observer; - -use Magento\Framework\Api\SearchCriteriaBuilder; -use Magento\Framework\App\ObjectManager; -use Magento\Framework\Event; -use Magento\Framework\Event\Observer; -use Magento\Sales\Api\Data\OrderInterface; -use Magento\Sales\Api\OrderRepositoryInterface; -use Magento\Signifyd\Api\CaseCreationServiceInterface; -use Magento\Store\Api\StoreRepositoryInterface; -use Magento\Store\Model\StoreManagerInterface; -use Magento\TestFramework\Helper\Bootstrap; -use PHPUnit\Framework\MockObject_MockObject as MockObject; -use Psr\Log\LoggerInterface; - -/** - * Test for Magento\Signifyd\Observer\PlaceOrderTest class. - * - * @SuppressWarnings(PHPMD.CouplingBetweenObjects) - */ -class PlaceOrderTest extends \PHPUnit\Framework\TestCase -{ - /** - * @var CaseCreationServiceInterface|MockObject - */ - private $creationService; - - /** - * @var LoggerInterface|MockObject - */ - private $logger; - - /** - * @var PlaceOrder - */ - private $placeOrder; - - /** - * @var ObjectManager - */ - private $objectManager; - - /** - * @inheritdoc - */ - protected function setUp() - { - $this->objectManager = Bootstrap::getObjectManager(); - - $this->creationService = $this->getMockBuilder(CaseCreationServiceInterface::class) - ->disableOriginalConstructor() - ->setMethods(['createForOrder']) - ->getMock(); - - $this->logger = $this->getMockBuilder(LoggerInterface::class) - ->disableOriginalConstructor() - ->getMock(); - - $this->placeOrder = $this->objectManager->create(PlaceOrder::class, [ - 'caseCreationService' => $this->creationService, - 'logger' => $this->logger - ]); - } - - /** - * Checks a case when order placed with offline payment method. - * - * @covers \Magento\Signifyd\Observer\PlaceOrder::execute - * @magentoConfigFixture current_store fraud_protection/signifyd/active 1 - * @magentoDataFixture Magento/Signifyd/_files/order_with_customer_and_two_simple_products.php - */ - public function testExecuteWithOfflinePayment() - { - $order = $this->getOrder('100000005'); - $this->creationService->expects(self::never()) - ->method('createForOrder'); - - $event = $this->objectManager->create( - Event::class, - [ - 'data' => ['order' => $order] - ] - ); - - /** @var Observer $observer */ - $observer = $this->objectManager->get(Observer::class); - $observer->setEvent($event); - - $this->placeOrder->execute($observer); - } - - /** - * Checks a test case when order placed with online payment method. - * - * @covers \Magento\Signifyd\Observer\PlaceOrder::execute - * @magentoConfigFixture current_store fraud_protection/signifyd/active 1 - * @magentoDataFixture Magento/Signifyd/_files/order_with_customer_and_two_simple_products.php - */ - public function testExecute() - { - $order = $this->getOrder('100000001'); - - $this->creationService->expects(self::once()) - ->method('createForOrder') - ->with(self::equalTo($order->getEntityId())); - - $event = $this->objectManager->create( - Event::class, - [ - 'data' => ['order' => $order], - ] - ); - - /** @var Observer $observer */ - $observer = $this->objectManager->get(Observer::class); - $observer->setEvent($event); - - $this->placeOrder->execute($observer); - } - - /** - * Signifyd is enabled for default store. - * Checks a test case when order placed with website where signifyd is disabled. - * - * @return void - * @covers \Magento\Signifyd\Observer\PlaceOrder::execute - * @magentoDataFixture Magento/Signifyd/_files/order_with_customer_and_two_simple_products.php - * @magentoDataFixture Magento/Signifyd/_files/website_configuration.php - */ - public function testExecuteWithWebsiteConfiguration(): void - { - /** @var StoreRepositoryInterface $storeRepository */ - $storeRepository = $this->objectManager->get(StoreRepositoryInterface::class); - $store = $storeRepository->get('test_second_store'); - - /** @var StoreManagerInterface $storeManager */ - $storeManager = $this->objectManager->get(StoreManagerInterface::class); - $storeManager->setCurrentStore($store->getId()); - - $order = $this->getOrder('100000001'); - $order->setStoreId($store->getId()); - - $this->creationService->expects(self::never()) - ->method('createForOrder'); - - $event = $this->objectManager->create( - Event::class, - [ - 'data' => ['order' => $order], - ] - ); - - /** @var Observer $observer */ - $observer = $this->objectManager->get(Observer::class); - $observer->setEvent($event); - - $this->placeOrder->execute($observer); - } - - /** - * Checks a test case when observer event contains two orders: - * one order with offline payment and one order with online payment. - * - * @covers \Magento\Signifyd\Observer\PlaceOrder::execute - * @magentoConfigFixture current_store fraud_protection/signifyd/active 1 - * @magentoDataFixture Magento/Signifyd/_files/order_with_customer_and_two_simple_products.php - */ - public function testExecuteWithMultipleOrders() - { - $orderWithOnlinePayment = $this->getOrder('100000001'); - $orderWithOfflinePayment = $this->getOrder('100000005'); - - // this service mock should be called only once for the order with online payment method. - $this->creationService->expects(self::once()) - ->method('createForOrder') - ->with(self::equalTo($orderWithOnlinePayment->getEntityId())); - - $event = $this->objectManager->create( - Event::class, - [ - 'data' => ['orders' => [$orderWithOfflinePayment, $orderWithOnlinePayment]] - ] - ); - - /** @var Observer $observer */ - $observer = $this->objectManager->get(Observer::class); - $observer->setEvent($event); - - $this->placeOrder->execute($observer); - } - - /** - * Gets stored order. - * - * @param string $incrementId - * @return OrderInterface - */ - private function getOrder($incrementId) - { - /** @var SearchCriteriaBuilder $searchCriteriaBuilder */ - $searchCriteriaBuilder = $this->objectManager->get(SearchCriteriaBuilder::class); - $searchCriteria = $searchCriteriaBuilder->addFilter(OrderInterface::INCREMENT_ID, $incrementId) - ->create(); - - $orderRepository = $this->objectManager->get(OrderRepositoryInterface::class); - $orders = $orderRepository->getList($searchCriteria) - ->getItems(); - - $order = array_pop($orders); - - return $order; - } -} diff --git a/dev/tests/integration/testsuite/Magento/Signifyd/Plugin/CancelOrderTest.php b/dev/tests/integration/testsuite/Magento/Signifyd/Plugin/CancelOrderTest.php deleted file mode 100644 index 7c1af95bdb89c..0000000000000 --- a/dev/tests/integration/testsuite/Magento/Signifyd/Plugin/CancelOrderTest.php +++ /dev/null @@ -1,114 +0,0 @@ -<?php -/** - * Copyright © Magento, Inc. All rights reserved. - * See COPYING.txt for license details. - */ -namespace Magento\Signifyd\Plugin; - -use Magento\Framework\Api\SearchCriteriaBuilder; -use Magento\Sales\Api\Data\OrderInterface; -use Magento\Sales\Api\OrderManagementInterface; -use Magento\Sales\Api\OrderRepositoryInterface; -use Magento\Signifyd\Api\CaseRepositoryInterface; -use Magento\Signifyd\Api\Data\CaseInterface; -use Magento\Signifyd\Model\SignifydGateway\ApiClient; -use Magento\TestFramework\Helper\Bootstrap; -use Magento\TestFramework\ObjectManager; -use PHPUnit\Framework\MockObject_MockObject as MockObject; - -class CancelOrderTest extends \PHPUnit\Framework\TestCase -{ - /** - * @var int - */ - private static $caseId = 123; - - /** - * @var ObjectManager - */ - private $objectManager; - - /** - * @var ApiClient|MockObject - */ - private $apiClient; - - /** - * @inheritdoc - */ - protected function setUp() - { - $this->objectManager = Bootstrap::getObjectManager(); - - $this->apiClient = $this->getMockBuilder(ApiClient::class) - ->disableOriginalConstructor() - ->setMethods(['makeApiCall']) - ->getMock(); - - $this->objectManager->addSharedInstance($this->apiClient, ApiClient::class); - } - - /** - * @inheritdoc - */ - protected function tearDown() - { - $this->objectManager->removeSharedInstance(ApiClient::class); - } - - /** - * Checks a test case, when order has been cancelled - * and calls plugin to cancel Signifyd case guarantee. - * - * @covers \Magento\Signifyd\Plugin\OrderPlugin::afterCancel - * @magentoDataFixture Magento/Signifyd/_files/approved_case.php - * @magentoConfigFixture current_store fraud_protection/signifyd/active 1 - */ - public function testAfterCancel() - { - $order = $this->getOrder(); - - $this->apiClient->expects(self::once()) - ->method('makeApiCall') - ->with( - self::equalTo('/cases/' . self::$caseId . '/guarantee'), - 'PUT', - [ - 'guaranteeDisposition' => CaseInterface::GUARANTEE_CANCELED - ] - ) - ->willReturn([ - 'disposition' => CaseInterface::GUARANTEE_CANCELED - ]); - - /** @var OrderManagementInterface $orderService */ - $orderService = $this->objectManager->get(OrderManagementInterface::class); - $orderService->cancel($order->getEntityId()); - - /** @var CaseRepositoryInterface $caseRepository */ - $caseRepository = $this->objectManager->get(CaseRepositoryInterface::class); - $case = $caseRepository->getByCaseId(self::$caseId); - - self::assertEquals(CaseInterface::GUARANTEE_CANCELED, $case->getGuaranteeDisposition()); - } - - /** - * Get stored order. - * - * @return OrderInterface - */ - private function getOrder() - { - /** @var SearchCriteriaBuilder $searchCriteriaBuilder */ - $searchCriteriaBuilder = $this->objectManager->get(SearchCriteriaBuilder::class); - $searchCriteria = $searchCriteriaBuilder->addFilter(OrderInterface::INCREMENT_ID, '100000001') - ->create(); - - $orderRepository = $this->objectManager->get(OrderRepositoryInterface::class); - $orders = $orderRepository->getList($searchCriteria) - ->getItems(); - - /** @var OrderInterface $order */ - return array_pop($orders); - } -} diff --git a/dev/tests/integration/testsuite/Magento/Signifyd/Plugin/DenyPaymentTest.php b/dev/tests/integration/testsuite/Magento/Signifyd/Plugin/DenyPaymentTest.php deleted file mode 100644 index 72da71a630dff..0000000000000 --- a/dev/tests/integration/testsuite/Magento/Signifyd/Plugin/DenyPaymentTest.php +++ /dev/null @@ -1,209 +0,0 @@ -<?php -/** - * Copyright © Magento, Inc. All rights reserved. - * See COPYING.txt for license details. - */ -namespace Magento\Signifyd\Plugin; - -use Magento\Framework\Api\SearchCriteriaBuilder; -use Magento\Framework\Registry; -use Magento\Payment\Model\Info as PaymentInfo; -use Magento\Paypal\Model\Api\Nvp; -use Magento\Paypal\Model\Config; -use Magento\Paypal\Model\Express; -use Magento\Paypal\Model\Info; -use Magento\Paypal\Model\Pro; -use Magento\Paypal\Model\ProFactory; -use Magento\Sales\Api\Data\OrderInterface; -use Magento\Sales\Api\OrderRepositoryInterface; -use Magento\Signifyd\Api\CaseRepositoryInterface; -use Magento\Signifyd\Api\Data\CaseInterface; -use Magento\Signifyd\Model\SignifydGateway\ApiClient; -use Magento\TestFramework\Helper\Bootstrap; -use Magento\TestFramework\ObjectManager; -use PHPUnit\Framework\MockObject_MockObject as MockObject; - -/** - * @SuppressWarnings(PHPMD.CouplingBetweenObjects) - */ -class DenyPaymentTest extends \PHPUnit\Framework\TestCase -{ - /** - * @var int - */ - private static $caseId = 123; - - /** - * @var ObjectManager - */ - private $objectManager; - - /** - * @var ApiClient|MockObject - */ - private $apiClient; - - /** - * @var Registry - */ - private $registry; - - /** - * @inheritdoc - */ - protected function setUp() - { - $this->objectManager = Bootstrap::getObjectManager(); - - $this->apiClient = $this->getMockBuilder(ApiClient::class) - ->disableOriginalConstructor() - ->setMethods(['makeApiCall']) - ->getMock(); - - $this->registry = $this->objectManager->get(Registry::class); - - $this->objectManager->addSharedInstance($this->apiClient, ApiClient::class); - } - - /** - * @inheritdoc - */ - protected function tearDown() - { - $this->objectManager->removeSharedInstance(ApiClient::class); - } - - /** - * Checks a test case, when payment has been denied - * and calls plugin to cancel Signifyd case guarantee. - * - * @covers \Magento\Signifyd\Plugin\PaymentPlugin::afterDenyPayment - * @magentoDataFixture Magento/Signifyd/_files/approved_case.php - * @magentoConfigFixture current_store fraud_protection/signifyd/active 1 - */ - public function testAfterDenyPayment() - { - $order = $this->getOrder(); - $this->registry->register('current_order', $order); - - $this->apiClient->expects(self::once()) - ->method('makeApiCall') - ->with( - self::equalTo('/cases/' . self::$caseId . '/guarantee'), - 'PUT', - [ - 'guaranteeDisposition' => CaseInterface::GUARANTEE_CANCELED - ] - ) - ->willReturn([ - 'disposition' => CaseInterface::GUARANTEE_CANCELED - ]); - - /** @var \Magento\Sales\Model\Order\Payment $payment */ - $payment = $order->getPayment(); - $payment->setData('method_instance', $this->getMethodInstance()); - $payment->deny(); - - /** @var CaseRepositoryInterface $caseRepository */ - $caseRepository = $this->objectManager->get(CaseRepositoryInterface::class); - $case = $caseRepository->getByCaseId(self::$caseId); - - self::assertEquals(CaseInterface::GUARANTEE_CANCELED, $case->getGuaranteeDisposition()); - } - - /** - * Get stored order. - * - * @return OrderInterface - */ - private function getOrder() - { - /** @var SearchCriteriaBuilder $searchCriteriaBuilder */ - $searchCriteriaBuilder = $this->objectManager->get(SearchCriteriaBuilder::class); - $searchCriteria = $searchCriteriaBuilder->addFilter(OrderInterface::INCREMENT_ID, '100000001') - ->create(); - - $orderRepository = $this->objectManager->get(OrderRepositoryInterface::class); - $orders = $orderRepository->getList($searchCriteria) - ->getItems(); - - /** @var OrderInterface $order */ - return array_pop($orders); - } - - /** - * Gets payment method instance. - * - * @return Express - */ - private function getMethodInstance() - { - /** @var PaymentInfo $infoInstance */ - $infoInstance = $this->objectManager->get(PaymentInfo::class); - $infoInstance->setAdditionalInformation( - Info::PAYMENT_STATUS_GLOBAL, - Info::PAYMENTSTATUS_PENDING - ); - $infoInstance->setAdditionalInformation( - Info::PENDING_REASON_GLOBAL, - Info::PAYMENTSTATUS_PENDING - ); - - /** @var Express $methodInstance */ - $methodInstance = $this->objectManager->create( - Express::class, - ['proFactory' => $this->getProFactory()] - ); - $methodInstance->setData('info_instance', $infoInstance); - - return $methodInstance; - } - - /** - * Gets Pro factory mock. - * - * @return ProFactory|MockObject - */ - protected function getProFactory() - { - $pro = $this->getMockBuilder(Pro::class) - ->disableOriginalConstructor() - ->setMethods(['getApi', 'setMethod', 'getConfig', '__wakeup', 'reviewPayment']) - ->getMock(); - $nvpClient = $this->getMockBuilder(Nvp::class) - ->disableOriginalConstructor() - ->getMock(); - - $pro->method('getConfig') - ->willReturn($this->getConfig()); - $pro->method('getApi') - ->willReturn($nvpClient); - $pro->method('reviewPayment') - ->willReturn(true); - - $proFactory = $this->getMockBuilder(ProFactory::class) - ->disableOriginalConstructor() - ->getMock(); - $proFactory->method('create') - ->willReturn($pro); - - return $proFactory; - } - - /** - * Gets config mock. - * - * @return Config|MockObject - */ - protected function getConfig() - { - $config = $this->getMockBuilder(Config::class) - ->disableOriginalConstructor() - ->getMock(); - $config->method('getValue') - ->with('payment_action') - ->willReturn(Config::PAYMENT_ACTION_AUTH); - - return $config; - } -} diff --git a/dev/tests/integration/testsuite/Magento/Signifyd/_files/approved_case.php b/dev/tests/integration/testsuite/Magento/Signifyd/_files/approved_case.php deleted file mode 100644 index eaa3622c04e0e..0000000000000 --- a/dev/tests/integration/testsuite/Magento/Signifyd/_files/approved_case.php +++ /dev/null @@ -1,38 +0,0 @@ -<?php -/** - * Copyright © Magento, Inc. All rights reserved. - * See COPYING.txt for license details. - */ - -use Magento\Signifyd\Api\CaseRepositoryInterface; -use Magento\Signifyd\Api\Data\CaseInterface; -use Magento\Signifyd\Api\Data\CaseInterfaceFactory; - -require __DIR__ . '/order_with_customer_and_two_simple_products.php'; - -/** @var CaseInterfaceFactory $caseFactory */ -$caseFactory = $objectManager->get(CaseInterfaceFactory::class); - -$associatedTeam = [ - 'teamName' => 'Some Team', - 'teamId' => 123, - 'getAutoDismiss' => true, - 'getTeamDismissalDays' => 3 -]; - -/** @var CaseInterface $case */ -$case = $caseFactory->create(); -$case->setCaseId(123) - ->setGuaranteeEligible(false) - ->setGuaranteeDisposition(CaseInterface::GUARANTEE_APPROVED) - ->setStatus(CaseInterface::STATUS_PROCESSING) - ->setScore(553) - ->setOrderId($order->getEntityId()) - ->setAssociatedTeam($associatedTeam) - ->setReviewDisposition(CaseInterface::DISPOSITION_GOOD) - ->setCreatedAt('2016-12-12T15:17:17+0000') - ->setUpdatedAt('2016-12-12T19:23:16+0000'); - -/** @var CaseRepositoryInterface $caseRepository */ -$caseRepository = $objectManager->get(CaseRepositoryInterface::class); -$caseRepository->save($case); diff --git a/dev/tests/integration/testsuite/Magento/Signifyd/_files/case.php b/dev/tests/integration/testsuite/Magento/Signifyd/_files/case.php deleted file mode 100644 index 4102233f6fb1f..0000000000000 --- a/dev/tests/integration/testsuite/Magento/Signifyd/_files/case.php +++ /dev/null @@ -1,42 +0,0 @@ -<?php -/** - * Copyright © Magento, Inc. All rights reserved. - * See COPYING.txt for license details. - */ -use Magento\Sales\Api\OrderManagementInterface; -use Magento\Signifyd\Api\CaseRepositoryInterface; -use Magento\Signifyd\Api\Data\CaseInterface; -use Magento\Signifyd\Api\Data\CaseInterfaceFactory; - -require __DIR__ . '/order_with_customer_and_two_simple_products.php'; - -/** @var OrderManagementInterface $orderManagement */ -$orderManagement = $objectManager->create(OrderManagementInterface::class); -$orderManagement->hold($order->getEntityId()); - -/** @var CaseInterfaceFactory $caseFactory */ -$caseFactory = $objectManager->get(CaseInterfaceFactory::class); - -$associatedTeam = [ - 'teamName' => 'Some Team', - 'teamId' => 123, - 'getAutoDismiss' => true, - 'getTeamDismissalDays' => 3 -]; - -/** @var CaseInterface $case */ -$case = $caseFactory->create(); -$case->setCaseId(123) - ->setGuaranteeEligible(true) - ->setStatus(CaseInterface::STATUS_PROCESSING) - ->setScore(553) - ->setOrderId($order->getEntityId()) - ->setAssociatedTeam($associatedTeam) - ->setReviewDisposition(CaseInterface::DISPOSITION_GOOD) - ->setGuaranteeDisposition(CaseInterface::GUARANTEE_PENDING) - ->setCreatedAt('2016-12-12T15:17:17+0000') - ->setUpdatedAt('2016-12-12T19:23:16+0000'); - -/** @var CaseRepositoryInterface $caseRepository */ -$caseRepository = $objectManager->get(CaseRepositoryInterface::class); -$caseRepository->save($case); diff --git a/dev/tests/integration/testsuite/Magento/Signifyd/_files/customer.php b/dev/tests/integration/testsuite/Magento/Signifyd/_files/customer.php deleted file mode 100644 index 7c5f34cd203fa..0000000000000 --- a/dev/tests/integration/testsuite/Magento/Signifyd/_files/customer.php +++ /dev/null @@ -1,38 +0,0 @@ -<?php -/** - * Copyright © Magento, Inc. All rights reserved. - * See COPYING.txt for license details. - */ - -use Magento\Customer\Model\Customer; -use Magento\TestFramework\Helper\Bootstrap; -use Magento\Customer\Api\Data\CustomerInterface; -use Magento\Customer\Model\CustomerRegistry; - -$objectManager = Bootstrap::getObjectManager(); -/** @var CustomerRegistry $customerRegistry */ -$customerRegistry = $objectManager->get(CustomerRegistry::class); -$customer = $objectManager->create(Customer::class); - -/** @var CustomerInterface $customer */ -$customer->setWebsiteId(1) - ->setId(1) - ->setEmail('customer@example.com') - ->setGroupId(1) - ->setStoreId(1) - ->setPrefix('Mr.') - ->setFirstname('John') - ->setMiddlename('A') - ->setLastname('Smith') - ->setSuffix('Esq.') - ->setDefaultBilling(1) - ->setDefaultShipping(1) - ->setTaxvat('12') - ->setGender(0) - ->setCreatedAt('2016-12-12T11:00:00+0000') - ->setUpdatedAt('2016-12-12T11:05:00+0000'); - -$customer->isObjectNew(true); -$customer->save(); - -$customerRegistry->remove($customer->getId()); diff --git a/dev/tests/integration/testsuite/Magento/Signifyd/_files/declined_case.php b/dev/tests/integration/testsuite/Magento/Signifyd/_files/declined_case.php deleted file mode 100644 index 041cdb0291d83..0000000000000 --- a/dev/tests/integration/testsuite/Magento/Signifyd/_files/declined_case.php +++ /dev/null @@ -1,38 +0,0 @@ -<?php -/** - * Copyright © Magento, Inc. All rights reserved. - * See COPYING.txt for license details. - */ - -use Magento\Signifyd\Api\CaseRepositoryInterface; -use Magento\Signifyd\Api\Data\CaseInterface; -use Magento\Signifyd\Api\Data\CaseInterfaceFactory; - -require __DIR__ . '/order_with_customer_and_two_simple_products.php'; - -/** @var CaseInterfaceFactory $caseFactory */ -$caseFactory = $objectManager->get(CaseInterfaceFactory::class); - -$associatedTeam = [ - 'teamName' => 'Some Team', - 'teamId' => 123, - 'getAutoDismiss' => true, - 'getTeamDismissalDays' => 3 -]; - -/** @var CaseInterface $case */ -$case = $caseFactory->create(); -$case->setCaseId(123) - ->setGuaranteeEligible(false) - ->setGuaranteeDisposition(CaseInterface::GUARANTEE_DECLINED) - ->setStatus(CaseInterface::STATUS_PROCESSING) - ->setScore(553) - ->setOrderId($order->getEntityId()) - ->setAssociatedTeam($associatedTeam) - ->setReviewDisposition(CaseInterface::DISPOSITION_FRAUDULENT) - ->setCreatedAt('2016-12-12T15:17:17+0000') - ->setUpdatedAt('2016-12-12T19:23:16+0000'); - -/** @var CaseRepositoryInterface $caseRepository */ -$caseRepository = $objectManager->get(CaseRepositoryInterface::class); -$caseRepository->save($case); diff --git a/dev/tests/integration/testsuite/Magento/Signifyd/_files/multiple_cases.php b/dev/tests/integration/testsuite/Magento/Signifyd/_files/multiple_cases.php deleted file mode 100644 index 4930906954148..0000000000000 --- a/dev/tests/integration/testsuite/Magento/Signifyd/_files/multiple_cases.php +++ /dev/null @@ -1,27 +0,0 @@ -<?php -/** - * Copyright © Magento, Inc. All rights reserved. - * See COPYING.txt for license details. - */ -use Magento\Signifyd\Api\Data\CaseInterface; - -require __DIR__ . '/case.php'; - -for ($i = 1; $i < 4; $i ++) { - $newOrder = clone $order; - $newOrder->setEntityId(null) - ->setIncrementId($order->getIncrementId() + $i); - - $orderRepository->save($newOrder); - - $newCase = clone $case; - $newCase->setEntityId(null) - ->setCaseId($i) - ->setOrderId($newOrder->getEntityId()) - ->setStatus(CaseInterface::STATUS_OPEN) - ->setCreatedAt('2016-12-0' . $i . 'T15:' . $i . ':17+0000') - ->setUpdatedAt('2016-12-12T0' . $i . ':23:16+0000') - ->setId(null); - - $caseRepository->save($newCase); -} diff --git a/dev/tests/integration/testsuite/Magento/Signifyd/_files/order_with_customer_and_two_simple_products.php b/dev/tests/integration/testsuite/Magento/Signifyd/_files/order_with_customer_and_two_simple_products.php deleted file mode 100644 index 49a0a2d33e236..0000000000000 --- a/dev/tests/integration/testsuite/Magento/Signifyd/_files/order_with_customer_and_two_simple_products.php +++ /dev/null @@ -1,119 +0,0 @@ -<?php -/** - * Copyright © Magento, Inc. All rights reserved. - * See COPYING.txt for license details. - */ -use Magento\Sales\Model\Order; -use Magento\Sales\Model\Order\Payment; -use Magento\Sales\Model\Order\Address; -use Magento\Sales\Model\Order\Item; -use Magento\Sales\Api\OrderRepositoryInterface; -use Magento\TestFramework\Helper\Bootstrap; - -require __DIR__ . '/../../../Magento/Catalog/_files/product_simple.php'; -require __DIR__ . '/../../../Magento/Customer/_files/customer.php'; -require __DIR__ . '/store.php'; - -$addressData = include __DIR__ . '/../../../Magento/Sales/_files/address_data.php'; - -$objectManager = Bootstrap::getObjectManager(); - -$billingAddress = $objectManager->create(Address::class, ['data' => $addressData]); -$billingAddress->setAddressType('billing'); - -$shippingAddress = clone $billingAddress; -$shippingAddress->setId(null) - ->setAddressType('shipping') - ->setStreet(['6161 West Centinela Avenue', 'app. 33']) - ->setFirstname('John') - ->setLastname('Doe') - ->setShippingMethod('flatrate_flatrate'); - -$payment = $objectManager->create(Payment::class); -$payment->setMethod('paypal_express') - ->setLastTransId('00001') - ->setCcLast4('1234') - ->setCcExpMonth('01') - ->setCcExpYear('21'); - -/** @var Item $orderItem */ -$orderItem1 = $objectManager->create(Item::class); -$orderItem1->setProductId($product->getId()) - ->setSku($product->getSku()) - ->setName($product->getName()) - ->setQtyOrdered(1) - ->setBasePrice($product->getPrice()) - ->setPrice($product->getPrice()) - ->setRowTotal($product->getPrice()) - ->setProductType($product->getTypeId()); - -/** @var Item $orderItem */ -$orderItem2 = $objectManager->create(Item::class); -$orderItem2->setProductId($product->getId()) - ->setSku('simple2') - ->setName('Simple product') - ->setPrice(100) - ->setQtyOrdered(2) - ->setBasePrice($product->getPrice()) - ->setPrice($product->getPrice()) - ->setRowTotal($product->getPrice()) - ->setProductType($product->getTypeId()); - -$orderAmount = 100; -$customerEmail = $billingAddress->getEmail(); - -/** @var Order $order */ -$order = $objectManager->create(Order::class); -$order->setIncrementId('100000001') - ->setState(Order::STATE_PROCESSING) - ->setStatus(Order::STATE_PROCESSING) - ->setCustomerId($customer->getId()) - ->setCustomerIsGuest(false) - ->setRemoteIp('127.0.0.1') - ->setCreatedAt(date('Y-m-d 00:00:55')) - ->setOrderCurrencyCode('USD') - ->setBaseCurrencyCode('USD') - ->setSubtotal($orderAmount) - ->setGrandTotal($orderAmount) - ->setBaseSubtotal($orderAmount) - ->setBaseGrandTotal($orderAmount) - ->setCustomerEmail($customerEmail) - ->setBillingAddress($billingAddress) - ->setShippingAddress($shippingAddress) - ->setShippingDescription('Flat Rate - Fixed') - ->setShippingAmount(10) - ->setStoreId($store->getId()) - ->addItem($orderItem1) - ->addItem($orderItem2) - ->setPayment($payment) - ->setQuoteId(1); - -/** @var OrderRepositoryInterface $orderRepository */ -$orderRepository = $objectManager->get(OrderRepositoryInterface::class); -$orderRepository->save($order); - -$orderAmount2 = 50; -$payment2 = $objectManager->create(Payment::class); -$payment2->setMethod('checkmo'); -/** @var Order $order2 */ -$order2 = $objectManager->create(Order::class); -$order2->setIncrementId('100000005') - ->setCustomerId($customer->getId()) - ->setCustomerIsGuest(false) - ->setRemoteIp('127.0.0.1') - ->setCreatedAt('2016-12-12T12:00:55+0000') - ->setOrderCurrencyCode('USD') - ->setBaseCurrencyCode('USD') - ->setGrandTotal($orderAmount2) - ->setBaseGrandTotal($orderAmount2) - ->setCustomerEmail($customerEmail) - ->setBillingAddress($billingAddress) - ->setShippingAddress($shippingAddress) - ->setShippingDescription('Flat Rate - Fixed') - ->setShippingAmount(10) - ->setStoreId($store->getId()) - ->addItem($orderItem1) - ->setPayment($payment2) - ->setQuoteId(2); - -$orderRepository->save($order2); diff --git a/dev/tests/integration/testsuite/Magento/Signifyd/_files/order_with_guest_and_virtual_product.php b/dev/tests/integration/testsuite/Magento/Signifyd/_files/order_with_guest_and_virtual_product.php deleted file mode 100644 index ba0e92687dc17..0000000000000 --- a/dev/tests/integration/testsuite/Magento/Signifyd/_files/order_with_guest_and_virtual_product.php +++ /dev/null @@ -1,68 +0,0 @@ -<?php -/** - * Copyright © Magento, Inc. All rights reserved. - * See COPYING.txt for license details. - */ -use Magento\Sales\Model\Order; -use Magento\Sales\Model\Order\Payment; -use Magento\Sales\Model\Order\Address; -use Magento\Sales\Model\Order\Item; -use Magento\Sales\Api\OrderRepositoryInterface; -use Magento\TestFramework\Helper\Bootstrap; -use Magento\Sales\Api\Data\OrderPaymentInterface; - -require __DIR__ . '/../../../Magento/Catalog/_files/product_virtual.php'; -require __DIR__ . '/store.php'; -$addressData = include __DIR__ . '/../../../Magento/Sales/_files/address_data.php'; - -$objectManager = Bootstrap::getObjectManager(); - -$billingAddress = $objectManager->create(Address::class, ['data' => $addressData]); -$billingAddress->setAddressType('billing'); - -/** @var OrderPaymentInterface $payment */ -$payment = $objectManager->create(Payment::class); -$payment->setMethod('braintree') - ->setLastTransId('00001') - ->setAdditionalInformation('avsPostalCodeResponseCode', 'M') - ->setAdditionalInformation('avsStreetAddressResponseCode', 'M') - ->setAdditionalInformation('cvvResponseCode', 'M'); - -/** @var Item $orderItem */ -$orderItem1 = $objectManager->create(Item::class); -$orderItem1->setProductId($product->getId()) - ->setSku($product->getSku()) - ->setName($product->getName()) - ->setQtyOrdered(1) - ->setBasePrice($product->getPrice()) - ->setPrice($product->getPrice()) - ->setRowTotal($product->getPrice()) - ->setProductType($product->getTypeId()); - -$orderAmount = 100; -$customerEmail = $billingAddress->getEmail(); - -/** @var Order $order */ -$order = $objectManager->create(Order::class); -$order->setIncrementId('100000002') - ->setState(Order::STATE_PROCESSING) - ->setStatus(Order::STATE_PROCESSING) - ->setCustomerIsGuest(true) - ->setRemoteIp('127.0.0.1') - ->setCreatedAt('2016-12-12T12:00:55+0000') - ->setOrderCurrencyCode('USD') - ->setBaseCurrencyCode('USD') - ->setSubtotal($orderAmount) - ->setGrandTotal($orderAmount) - ->setBaseSubtotal($orderAmount) - ->setBaseGrandTotal($orderAmount) - ->setCustomerEmail($customerEmail) - ->setBillingAddress($billingAddress) - ->setStoreId($store->getId()) - ->addItem($orderItem1) - ->setPayment($payment) - ->setQuoteId(1); - -/** @var OrderRepositoryInterface $orderRepository */ -$orderRepository = $objectManager->get(OrderRepositoryInterface::class); -$orderRepository->save($order); diff --git a/dev/tests/integration/testsuite/Magento/Signifyd/_files/store.php b/dev/tests/integration/testsuite/Magento/Signifyd/_files/store.php deleted file mode 100644 index b814263bdc5ef..0000000000000 --- a/dev/tests/integration/testsuite/Magento/Signifyd/_files/store.php +++ /dev/null @@ -1,33 +0,0 @@ -<?php -/** - * Copyright © Magento, Inc. All rights reserved. - * See COPYING.txt for license details. - */ - -use Magento\Store\Model\Store; -use Magento\Store\Model\Information; -use Magento\Store\Model\ScopeInterface; -use Magento\Store\Model\StoreManagerInterface; -use Magento\Sales\Model\Order\Shipment; -use Magento\Framework\App\Config\MutableScopeConfigInterface; -use Magento\TestFramework\Helper\Bootstrap; - -$objectManager = Bootstrap::getObjectManager(); -$store = $objectManager->get(StoreManagerInterface::class)->getStore(); -/** @var MutableScopeConfigInterface $mutableConfig */ -$mutableConfig = $objectManager->get(MutableScopeConfigInterface::class); -$mutableConfig->setValue(Information::XML_PATH_STORE_INFO_NAME, 'Sample Store', ScopeInterface::SCOPE_STORE); -$mutableConfig->setValue(Store::XML_PATH_UNSECURE_BASE_LINK_URL, 'http://m2.com/', ScopeInterface::SCOPE_STORE); -$mutableConfig->setValue(Shipment::XML_PATH_STORE_ADDRESS1, '6161 West Centinela Avenue', ScopeInterface::SCOPE_STORE); -$mutableConfig->setValue(Shipment::XML_PATH_STORE_ADDRESS2, 'app. 111', ScopeInterface::SCOPE_STORE); -$mutableConfig->setValue(Shipment::XML_PATH_STORE_CITY, 'Culver City', ScopeInterface::SCOPE_STORE); -$mutableConfig->setValue(Shipment::XML_PATH_STORE_REGION_ID, 10, ScopeInterface::SCOPE_STORE); -$mutableConfig->setValue(Shipment::XML_PATH_STORE_ZIP, '90230', ScopeInterface::SCOPE_STORE); -$mutableConfig->setValue(Shipment::XML_PATH_STORE_COUNTRY_ID, 'US', ScopeInterface::SCOPE_STORE); - -$mutableConfig->setValue(Information::XML_PATH_STORE_INFO_STREET_LINE1, '5th Avenue', ScopeInterface::SCOPE_STORE); -$mutableConfig->setValue(Information::XML_PATH_STORE_INFO_STREET_LINE2, '75', ScopeInterface::SCOPE_STORE); -$mutableConfig->setValue(Information::XML_PATH_STORE_INFO_CITY, 'New York', ScopeInterface::SCOPE_STORE); -$mutableConfig->setValue(Information::XML_PATH_STORE_INFO_REGION_CODE, 30, ScopeInterface::SCOPE_STORE); -$mutableConfig->setValue(Information::XML_PATH_STORE_INFO_POSTCODE, '19032', ScopeInterface::SCOPE_STORE); -$mutableConfig->setValue(Information::XML_PATH_STORE_INFO_COUNTRY_CODE, 'US', ScopeInterface::SCOPE_STORE); diff --git a/dev/tests/integration/testsuite/Magento/Signifyd/_files/webhook_body.json b/dev/tests/integration/testsuite/Magento/Signifyd/_files/webhook_body.json deleted file mode 100644 index 4308c8bf833ef..0000000000000 --- a/dev/tests/integration/testsuite/Magento/Signifyd/_files/webhook_body.json +++ /dev/null @@ -1 +0,0 @@ -{"investigationId":123,"analysisUrl":"https://signifyd.com/v2/cases/185088720/analysis","entriesUrl":"https://signifyd.com/v2/cases/185088720/entries","notesUrl":"https://signifyd.com/v2/cases/185088720/notes","orderUrl":"https://signifyd.com/v2/cases/185088720/order","currency":"USD","uuid":"368df42c-d25f-44ef-a1d9-92755f743901","createdAt":"2017-01-06T12:47:03+0000","updatedAt":"2017-01-06T12:47:03+0000","status":"OPEN","caseId":123,"score":384,"headline":"John Doe","orderId":"000000003","adjustedScore":385,"orderDate":"2017-01-06T12:46:58+0000","orderAmount":5.85,"orderOutcome":"SUCCESSFUL","associatedTeam":{"teamName":"Magento","teamId":7940},"testInvestigation":true,"reviewDisposition":null} \ No newline at end of file diff --git a/dev/tests/integration/testsuite/Magento/Signifyd/_files/website_configuration.php b/dev/tests/integration/testsuite/Magento/Signifyd/_files/website_configuration.php deleted file mode 100644 index e53b0431503e7..0000000000000 --- a/dev/tests/integration/testsuite/Magento/Signifyd/_files/website_configuration.php +++ /dev/null @@ -1,67 +0,0 @@ -<?php -/** - * Copyright © Magento, Inc. All rights reserved. - * See COPYING.txt for license details. - */ -declare(strict_types=1); - -use Magento\Config\Model\Config; -use Magento\Framework\App\Config\ScopeConfigInterface; -use Magento\Store\Model\ResourceModel\Store as StoreResourceModel; -use Magento\Store\Model\ResourceModel\Website as WebsiteResourceModel; -use Magento\Store\Model\ScopeInterface; -use Magento\Store\Model\Store; -use Magento\Store\Model\StoreManagerInterface; -use Magento\Store\Model\Website; -use Magento\TestFramework\Helper\Bootstrap; - -$objectManager = Bootstrap::getObjectManager(); - -/** @var $website Website */ -$website = $objectManager->create(Website::class); -$website->setData(['code' => 'test_website', 'name' => 'Test Website', 'default_group_id' => '1', 'is_default' => '0']); -$websiteResourceModel = $objectManager->create(WebsiteResourceModel::class); -$websiteResourceModel->save($website); - -$websiteId = $website->getId(); -$store = $objectManager->create(Store::class); -$groupId = Bootstrap::getObjectManager()->get(StoreManagerInterface::class) - ->getWebsite() - ->getDefaultGroupId(); -$store->setCode('test_second_store') - ->setWebsiteId($websiteId) - ->setGroupId($groupId) - ->setName('Test Second Store') - ->setSortOrder(10) - ->setIsActive(1); -$storeResourceModel = $objectManager->create(StoreResourceModel::class); -$storeResourceModel->save($store); - -/* Refresh stores memory cache */ -$objectManager->get(StoreManagerInterface::class)->reinitStores(); - -$processConfigData = function (Config $config, array $data) { - foreach ($data as $key => $value) { - $config->setDataByPath($key, $value); - $config->save(); - } -}; - -// save signifyd configuration for the default scope -$configData = [ - 'fraud_protection/signifyd/active' => '1', -]; -/** @var Config $defConfig */ -$defConfig = $objectManager->create(Config::class); -$defConfig->setScope(ScopeConfigInterface::SCOPE_TYPE_DEFAULT); -$processConfigData($defConfig, $configData); - -// save signifyd website config data -$websiteConfigData = [ - 'fraud_protection/signifyd/active' => '0', -]; -/** @var Config $websiteConfig */ -$websiteConfig = $objectManager->create(Config::class); -$websiteConfig->setScope(ScopeInterface::SCOPE_WEBSITES); -$websiteConfig->setWebsite($websiteId); -$processConfigData($websiteConfig, $websiteConfigData); diff --git a/dev/tests/integration/testsuite/Magento/Signifyd/_files/website_configuration_rollback.php b/dev/tests/integration/testsuite/Magento/Signifyd/_files/website_configuration_rollback.php deleted file mode 100644 index 9b731813fea3b..0000000000000 --- a/dev/tests/integration/testsuite/Magento/Signifyd/_files/website_configuration_rollback.php +++ /dev/null @@ -1,44 +0,0 @@ -<?php -/** - * Copyright © Magento, Inc. All rights reserved. - * See COPYING.txt for license details. - */ -declare(strict_types=1); - -use Magento\Framework\App\Config\ScopeConfigInterface; -use Magento\Framework\App\Config\Storage\WriterInterface; -use Magento\Store\Api\WebsiteRepositoryInterface; -use Magento\Store\Model\ScopeInterface; -use Magento\Store\Model\Store; -use Magento\Store\Model\Website; -use Magento\TestFramework\Helper\Bootstrap; - -$objectManager = Bootstrap::getObjectManager(); - -$deleteConfigData = function (WriterInterface $writer, $scope, $scopeId) { - $configData = [ - 'fraud_protection/signifyd/active', - ]; - foreach ($configData as $path) { - $writer->delete($path, $scope, $scopeId); - } -}; - -/** @var WriterInterface $configWriter */ -$configWriter = $objectManager->get(WriterInterface::class); -$deleteConfigData($configWriter, ScopeConfigInterface::SCOPE_TYPE_DEFAULT, null); - -/** @var WebsiteRepositoryInterface $websiteRepository */ -$websiteRepository = $objectManager->get(WebsiteRepositoryInterface::class); -$website = $websiteRepository->get('test_website'); -$deleteConfigData($configWriter, ScopeInterface::SCOPE_WEBSITES, $website->getId()); - -$website = $objectManager->create(Website::class); -/** @var $website Website */ -if ($website->load('test_website', 'code')->getId()) { - $website->delete(); -} -$store = $objectManager->create(Store::class); -if ($store->load('test_second_store', 'code')->getId()) { - $store->delete(); -} diff --git a/dev/tests/integration/testsuite/Magento/Sitemap/Model/SitemapTest.php b/dev/tests/integration/testsuite/Magento/Sitemap/Model/SitemapTest.php index 73863e0915c66..7f4a8df2f3c6f 100644 --- a/dev/tests/integration/testsuite/Magento/Sitemap/Model/SitemapTest.php +++ b/dev/tests/integration/testsuite/Magento/Sitemap/Model/SitemapTest.php @@ -14,7 +14,7 @@ use Magento\Sitemap\Model\Sitemap; use Magento\TestFramework\Helper\Bootstrap; use Magento\TestFramework\Request; -use Zend\Stdlib\Parameters; +use Laminas\Stdlib\Parameters; use PHPUnit\Framework\TestCase; /** diff --git a/dev/tests/integration/testsuite/Magento/Store/App/Config/Source/InitialConfigSourceTest.php b/dev/tests/integration/testsuite/Magento/Store/App/Config/Source/InitialConfigSourceTest.php new file mode 100644 index 0000000000000..282b2d1cc24bb --- /dev/null +++ b/dev/tests/integration/testsuite/Magento/Store/App/Config/Source/InitialConfigSourceTest.php @@ -0,0 +1,175 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +declare(strict_types=1); + +namespace Magento\Store\App\Config\Source; + +use Magento\Framework\App\DeploymentConfig\FileReader; +use Magento\Framework\App\DeploymentConfig\Writer; +use Magento\Framework\App\Filesystem\DirectoryList; +use Magento\Framework\Config\File\ConfigFilePool; +use Magento\Framework\Filesystem; +use Magento\Store\Model\StoreManagerInterface; +use Magento\TestFramework\Helper\Bootstrap; +use PHPUnit\Framework\TestCase; + +/** + * Test that initial scopes config are loaded if database is available + */ +class InitialConfigSourceTest extends TestCase +{ + /** + * @var Filesystem + */ + private $filesystem; + + /** + * @var ConfigFilePool + */ + private $configFilePool; + + /** + * @var FileReader + */ + private $reader; + + /** + * @var Writer + */ + private $writer; + + /** + * @var array + */ + private $config; + + /** + * @var array + */ + private $envConfig; + + /** + * @var StoreManagerInterface + */ + private $storeManager; + + /** + * @inheritdoc + */ + protected function setUp() + { + $objectManager = Bootstrap::getObjectManager(); + $this->reader = $objectManager->get(FileReader::class); + $this->writer = $objectManager->get(Writer::class); + $this->filesystem = $objectManager->get(Filesystem::class); + $this->configFilePool = $objectManager->get(ConfigFilePool::class); + $this->storeManager = $objectManager->get(StoreManagerInterface::class); + $this->config = $this->loadConfig(); + $this->envConfig = $this->loadEnvConfig(); + $this->loadDumpConfig(); + $this->storeManager->reinitStores(); + } + + /** + * @inheritdoc + */ + public function tearDown() + { + $this->clearConfig(ConfigFilePool::APP_CONFIG); + $this->clearConfig(ConfigFilePool::APP_ENV); + $this->writer->saveConfig([ConfigFilePool::APP_CONFIG => $this->config]); + $this->writer->saveConfig([ConfigFilePool::APP_ENV => $this->envConfig]); + $this->storeManager->reinitStores(); + } + + /** + * Test that initial scopes config are loaded if database is available + * + * @param array $websites + * @param string $defaultWebsite + * @param bool $offline + * @throws \Magento\Framework\Exception\LocalizedException + * @dataProvider getDefaultDataProvider + */ + public function testGetWebsites(array $websites, string $defaultWebsite, bool $offline = false): void + { + if ($offline) { + // remove application environment config for emulate work without db + $this->clearConfig(ConfigFilePool::APP_ENV); + } + $this->assertEquals($defaultWebsite, $this->storeManager->getWebsite()->getCode()); + $this->assertEquals($websites, array_keys($this->storeManager->getWebsites(true, true)), '', 0.0, 10, true); + } + + /** + * @return array + */ + public function getDefaultDataProvider(): array + { + return [ + [ + [ + 'admin', + 'base', + ], + 'base', + false + ], + [ + [ + 'admin', + 'main', + ], + 'main', + true + ] + ]; + } + + private function clearConfig(string $type): void + { + $this->filesystem->getDirectoryWrite(DirectoryList::CONFIG)->writeFile( + $this->configFilePool->getPath($type), + "<?php\n return [];\n" + ); + } + + /** + * @return void + */ + private function loadDumpConfig(): void + { + $data = array_replace_recursive( + $this->config, + $this->getDumpConfig() + ); + $this->writer->saveConfig([ConfigFilePool::APP_CONFIG => $data], true); + } + + /** + * @return array + */ + private function getDumpConfig(): array + { + return require __DIR__ . '/../../../_files/dump_config.php'; + } + + /** + * @return array + */ + private function loadConfig(): array + { + return $this->reader->load(ConfigFilePool::APP_CONFIG); + } + + /** + * @return array + */ + private function loadEnvConfig(): array + { + return $this->reader->load(ConfigFilePool::APP_ENV); + } +} diff --git a/dev/tests/integration/testsuite/Magento/Store/App/FrontController/Plugin/RequestPreprocessorTest.php b/dev/tests/integration/testsuite/Magento/Store/App/FrontController/Plugin/RequestPreprocessorTest.php index a1a99ecd32b89..b4d1f94b74ffe 100644 --- a/dev/tests/integration/testsuite/Magento/Store/App/FrontController/Plugin/RequestPreprocessorTest.php +++ b/dev/tests/integration/testsuite/Magento/Store/App/FrontController/Plugin/RequestPreprocessorTest.php @@ -11,7 +11,7 @@ use Magento\Framework\App\Config\Value; use Magento\Framework\Data\Form\FormKey; use Magento\TestFramework\Response; -use Zend\Stdlib\Parameters; +use Laminas\Stdlib\Parameters; /** * Tests \Magento\Store\App\FrontController\Plugin\RequestPreprocessor. diff --git a/dev/tests/integration/testsuite/Magento/Store/Controller/Store/RedirectTest.php b/dev/tests/integration/testsuite/Magento/Store/Controller/Store/RedirectTest.php new file mode 100644 index 0000000000000..df45846dfc832 --- /dev/null +++ b/dev/tests/integration/testsuite/Magento/Store/Controller/Store/RedirectTest.php @@ -0,0 +1,40 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +declare(strict_types=1); + +namespace Magento\Store\Controller\Store; + +use Magento\Framework\Session\SidResolverInterface; +use Magento\Store\Model\StoreResolver; +use Magento\TestFramework\TestCase\AbstractController; + +/** + * Test Redirect controller. + * + * @magentoAppArea frontend + */ +class RedirectTest extends AbstractController +{ + /** + * Check that there's no SID in redirect URL. + * + * @return void + * @magentoDataFixture Magento/Store/_files/store.php + * @magentoDataFixture Magento/Store/_files/second_store.php + * @magentoConfigFixture current_store web/session/use_frontend_sid 1 + */ + public function testNoSid(): void + { + $this->getRequest()->setParam(StoreResolver::PARAM_NAME, 'fixture_second_store'); + $this->getRequest()->setParam('___from_store', 'test'); + + $this->dispatch('/stores/store/redirect'); + + $result = (string)$this->getResponse()->getHeader('location'); + $this->assertNotEmpty($result); + $this->assertNotContains(SidResolverInterface::SESSION_ID_QUERY_PARAM .'=', $result); + } +} diff --git a/dev/tests/integration/testsuite/Magento/Store/Model/HashGeneratorTest.php b/dev/tests/integration/testsuite/Magento/Store/Model/HashGeneratorTest.php deleted file mode 100644 index 1bacd79b74f49..0000000000000 --- a/dev/tests/integration/testsuite/Magento/Store/Model/HashGeneratorTest.php +++ /dev/null @@ -1,180 +0,0 @@ -<?php -/** - * Copyright © Magento, Inc. All rights reserved. - * See COPYING.txt for license details. - */ -declare(strict_types=1); - -namespace Magento\Store\Model; - -use Magento\Framework\ObjectManagerInterface as ObjectManager; -use Magento\TestFramework\Helper\Bootstrap; -use Magento\Store\Model\StoreSwitcher\HashGenerator; -use Magento\Customer\Api\AccountManagementInterface; -use Magento\Customer\Model\Session as CustomerSession; -use \Magento\Framework\App\DeploymentConfig as DeploymentConfig; -use Magento\Framework\Config\ConfigOptionsListConstants; -use Magento\Framework\Url\Helper\Data as UrlHelper; -use Magento\Store\Model\StoreSwitcher\HashGenerator\HashData; - -/** - * Test class for \Magento\Store\Model\StoreSwitcher\HashGenerator - * @SuppressWarnings(PHPMD.CouplingBetweenObjects) - */ -class HashGeneratorTest extends \PHPUnit\Framework\TestCase -{ - /** - * @var HashGenerator - */ - private $hashGenerator; - - /** - * @var ObjectManager - */ - private $objectManager; - - /** - * @var int - */ - private $customerId; - - /** @var AccountManagementInterface */ - private $accountManagement; - - /** - * @var \Magento\Customer\Model\Authorization\CustomerSessionUserContext - */ - private $customerSessionUserContext; - - /** - * @var \Magento\Framework\App\DeploymentConfig - */ - private $deploymentConfig; - - /** - * @var string - */ - private $key; - - /** - * @var UrlHelper - */ - private $urlHelper; - - /** - * @var HashData - */ - private $hashData; - - /** - * @var CustomerSession - */ - private $customerSession; - - /** - * Class dependencies initialization - * @return void - * @throws \Magento\Framework\Exception\LocalizedException - */ - protected function setUp() - { - $this->objectManager = Bootstrap::getObjectManager(); - $this->customerSession = $this->objectManager->create( - CustomerSession::class - ); - $this->accountManagement = $this->objectManager->create(AccountManagementInterface::class); - $customer = $this->accountManagement->authenticate('customer@example.com', 'password'); - $this->customerSession->setCustomerDataAsLoggedIn($customer); - $this->customerSessionUserContext = $this->objectManager->create( - \Magento\Customer\Model\Authorization\CustomerSessionUserContext::class, - ['customerSession' => $this->customerSession] - ); - $this->hashGenerator = $this->objectManager->create( - StoreSwitcher\HashGenerator::class, - ['currentUser' => $this->customerSessionUserContext] - ); - $this->customerId = $customer->getId(); - $this->deploymentConfig = $this->objectManager->get(DeploymentConfig::class); - $this->key = (string)$this->deploymentConfig->get(ConfigOptionsListConstants::CONFIG_PATH_CRYPT_KEY); - $this->urlHelper=$this->objectManager->create(UrlHelper::class); - $this->hashData=$this->objectManager->create(HashData::class); - } - - /** - * @inheritdoc - */ - protected function tearDown() - { - $this->customerSession->logout(); - parent::tearDown(); - } - - /** - * @magentoAppIsolation enabled - * @magentoDataFixture Magento/Customer/_files/customer.php - * @return void - */ - public function testSwitch(): void - { - $redirectUrl = "http://domain.com/"; - $fromStoreCode = 'test'; - $fromStore = $this->createPartialMock(Store::class, ['getCode']); - $toStore = $this->createPartialMock(Store::class, ['getCode']); - $fromStore->expects($this->once())->method('getCode')->willReturn($fromStoreCode); - $targetUrl=$this->hashGenerator->switch($fromStore, $toStore, $redirectUrl); - // phpcs:ignore - $urlParts=parse_url($targetUrl, PHP_URL_QUERY); - $signature=''; - // phpcs:ignore - parse_str($urlParts, $params); - - if (isset($params['signature'])) { - $signature=$params['signature']; - } - $this->assertEquals($params['customer_id'], $this->customerId); - $this->assertEquals($params['___from_store'], $fromStoreCode); - - $data = new HashData( - [ - "customer_id" => $this->customerId, - "time_stamp" => $params['time_stamp'], - "___from_store" => $fromStoreCode - ] - ); - $this->assertTrue($this->hashGenerator->validateHash($signature, $data)); - } - - /** - * @magentoAppIsolation enabled - * @magentoDataFixture Magento/Customer/_files/customer.php - * @return void - */ - public function testValidateHashWithInCorrectData(): void - { - $timeStamp = 0; - $customerId = 8; - $fromStoreCode = 'store1'; - $data = new HashData( - [ - "customer_id" => $customerId, - "time_stamp" => $timeStamp, - "___from_store" => $fromStoreCode - ] - ); - $redirectUrl = "http://domain.com/"; - $fromStore = $this->createPartialMock(Store::class, ['getCode']); - $toStore = $this->createPartialMock(Store::class, ['getCode']); - $fromStore->expects($this->once())->method('getCode')->willReturn($fromStoreCode); - $targetUrl = $this->hashGenerator->switch($fromStore, $toStore, $redirectUrl); - // phpcs:ignore - $urlParts = parse_url($targetUrl,PHP_URL_QUERY); - $signature = ''; - // phpcs:ignore - parse_str($urlParts, $params); - - if (isset($params['signature'])) { - $signature = $params['signature']; - } - $this->assertFalse($this->hashGenerator->validateHash($signature, $data)); - } -} diff --git a/dev/tests/integration/testsuite/Magento/Store/Model/StoreTest.php b/dev/tests/integration/testsuite/Magento/Store/Model/StoreTest.php index 00de5544d8fb7..1032dca18d5aa 100644 --- a/dev/tests/integration/testsuite/Magento/Store/Model/StoreTest.php +++ b/dev/tests/integration/testsuite/Magento/Store/Model/StoreTest.php @@ -9,9 +9,12 @@ use Magento\Catalog\Model\ProductRepository; use Magento\Framework\App\Bootstrap; use Magento\Framework\App\Filesystem\DirectoryList; +use Magento\Framework\App\RequestInterface; +use Magento\Framework\Session\SidResolverInterface; use Magento\Framework\UrlInterface; use Magento\Store\Api\StoreRepositoryInterface; -use Zend\Stdlib\Parameters; +use Laminas\Stdlib\Parameters; +use Magento\Framework\App\Request\Http as HttpRequest; /** * @SuppressWarnings(PHPMD.CouplingBetweenObjects) @@ -29,6 +32,11 @@ class StoreTest extends \PHPUnit\Framework\TestCase */ protected $model; + /** + * @var HttpRequest + */ + private $request; + protected function setUp() { $this->model = $this->_getStoreModel(); @@ -40,6 +48,7 @@ protected function setUp() protected function _getStoreModel() { $objectManager = \Magento\TestFramework\Helper\Bootstrap::getObjectManager(); + $this->request = $objectManager->get(RequestInterface::class); $this->modelParams = [ 'context' => $objectManager->get(\Magento\Framework\Model\Context::class), 'registry' => $objectManager->get(\Magento\Framework\Registry::class), @@ -49,7 +58,7 @@ protected function _getStoreModel() 'coreFileStorageDatabase' => $objectManager->get(\Magento\MediaStorage\Helper\File\Storage\Database::class), 'configCacheType' => $objectManager->get(\Magento\Framework\App\Cache\Type\Config::class), 'url' => $objectManager->get(\Magento\Framework\Url::class), - 'request' => $objectManager->get(\Magento\Framework\App\RequestInterface::class), + 'request' => $this->request, 'configDataResource' => $objectManager->get(\Magento\Config\Model\ResourceModel\Config\Data::class), 'filesystem' => $objectManager->get(\Magento\Framework\Filesystem::class), 'config' => $objectManager->get(\Magento\Framework\App\Config\ReinitableConfigInterface::class), @@ -176,11 +185,13 @@ public function getBaseUrlDataProvider() */ public function testGetBaseUrlInPub() { - \Magento\TestFramework\Helper\Bootstrap::getInstance()->reinitialize([ - Bootstrap::INIT_PARAM_FILESYSTEM_DIR_PATHS => [ - DirectoryList::PUB => [DirectoryList::URL_PATH => ''], - ], - ]); + \Magento\TestFramework\Helper\Bootstrap::getInstance()->reinitialize( + [ + Bootstrap::INIT_PARAM_FILESYSTEM_DIR_PATHS => [ + DirectoryList::PUB => [DirectoryList::URL_PATH => ''], + ], + ] + ); $this->model = $this->_getStoreModel(); $this->model->load('default'); @@ -276,14 +287,14 @@ public function testIsCanDelete() /** * @magentoDataFixture Magento/Store/_files/core_second_third_fixturestore.php - * @magentoDataFixture Magento/Catalog/_files/product_simple.php - * @magentoDbIsolation disabled + * @magentoDataFixture Magento/Catalog/_files/products.php + * @magentoAppIsolation enabled */ public function testGetCurrentUrl() { $objectManager = \Magento\TestFramework\Helper\Bootstrap::getObjectManager(); $objectManager->get(\Magento\Framework\App\Config\MutableScopeConfigInterface::class) - ->setValue('web/url/use_store', true, ScopeInterface::SCOPE_STORE, 'secondstore'); + ->setValue('web/url/use_store', true, ScopeInterface::SCOPE_STORE, 'secondstore'); $this->model->load('admin'); $this->model @@ -292,6 +303,13 @@ public function testGetCurrentUrl() $this->assertStringEndsWith('default', $this->model->getCurrentUrl()); $this->assertStringEndsNotWith('default', $this->model->getCurrentUrl(false)); + $this->model + ->expects($this->any())->method('getUrl') + ->willReturn('http://localhost/index.php?' .SidResolverInterface::SESSION_ID_QUERY_PARAM .'=12345'); + $this->request->setParams([SidResolverInterface::SESSION_ID_QUERY_PARAM, '12345']); + $this->request->setQueryValue(SidResolverInterface::SESSION_ID_QUERY_PARAM, '12345'); + $this->assertContains(SidResolverInterface::SESSION_ID_QUERY_PARAM .'=12345', $this->model->getCurrentUrl()); + /** @var \Magento\Store\Model\Store $secondStore */ $secondStore = $objectManager->get(StoreRepositoryInterface::class)->get('secondstore'); @@ -306,19 +324,20 @@ public function testGetCurrentUrl() $url ); $this->assertEquals( - $secondStore->getBaseUrl() . '?___from_store=default', + $secondStore->getBaseUrl() . '?SID=12345&___from_store=default', $secondStore->getCurrentUrl() ); $this->assertEquals( - $secondStore->getBaseUrl(), + $secondStore->getBaseUrl() . '?SID=12345', $secondStore->getCurrentUrl(false) ); } /** - * @magentoDataFixture Magento/Store/_files/second_store.php - * @magentoDataFixture Magento/Catalog/_files/category_product.php - * @magentoDbIsolation disabled + * @magentoDataFixture Magento/Store/_files/core_second_third_fixturestore.php + * @magentoDataFixture Magento/Catalog/_files/products.php + * @magentoAppIsolation enabled + * @magentoDbIsolation enabled */ public function testGetCurrentUrlWithUseStoreInUrlFalse() { @@ -327,34 +346,34 @@ public function testGetCurrentUrlWithUseStoreInUrlFalse() ->setValue('web/url/use_store', false, ScopeInterface::SCOPE_STORE, 'default'); /** @var \Magento\Store\Model\Store $secondStore */ - $secondStore = $objectManager->get(StoreRepositoryInterface::class)->get('fixture_second_store'); + $secondStore = $objectManager->get(StoreRepositoryInterface::class)->get('secondstore'); /** @var \Magento\Catalog\Model\ProductRepository $productRepository */ $productRepository = $objectManager->create(ProductRepository::class); - $product = $productRepository->get('simple333'); + $product = $productRepository->get('simple'); $product->setStoreId($secondStore->getId()); $url = $product->getUrlInStore(); /** @var \Magento\Catalog\Model\CategoryRepository $categoryRepository */ $categoryRepository = $objectManager->get(\Magento\Catalog\Model\CategoryRepository::class); - $category = $categoryRepository->get(333, $secondStore->getStoreId()); + $category = $categoryRepository->get(2, $secondStore->getStoreId()); $this->assertEquals( - $secondStore->getBaseUrl() . 'catalog/category/view/s/category-1/id/333/', + $secondStore->getBaseUrl() . 'catalog/category/view/s/default-category/id/2/', $category->getUrl() ); $this->assertEquals( $secondStore->getBaseUrl() . - 'catalog/product/view/id/333/s/simple-product-three/?___store=fixture_second_store', + 'catalog/product/view/id/1/s/simple-product/?___store=secondstore', $url ); $this->assertEquals( - $secondStore->getBaseUrl() . '?___store=fixture_second_store&___from_store=default', + $secondStore->getBaseUrl() . '?___store=secondstore&___from_store=default', $secondStore->getCurrentUrl() ); $this->assertEquals( - $secondStore->getBaseUrl() . '?___store=fixture_second_store', + $secondStore->getBaseUrl() . '?___store=secondstore', $secondStore->getCurrentUrl(false) ); } @@ -366,14 +385,16 @@ public function testGetCurrentUrlWithUseStoreInUrlFalse() */ public function testCRUD() { - $this->model->setData([ - 'code' => 'test', - 'website_id' => 1, - 'group_id' => 1, - 'name' => 'test name', - 'sort_order' => 0, - 'is_active' => 1, - ]); + $this->model->setData( + [ + 'code' => 'test', + 'website_id' => 1, + 'group_id' => 1, + 'name' => 'test name', + 'sort_order' => 0, + 'is_active' => 1, + ] + ); $crud = new \Magento\TestFramework\Entity( $this->model, ['name' => 'new name'], @@ -446,8 +467,8 @@ public function testIsUseStoreInUrl($storeInUrl, $disableStoreInUrl, $expectedRe } /** - * @see self::testIsUseStoreInUrl; * @return array + * @see self::testIsUseStoreInUrl; */ public function isUseStoreInUrlDataProvider() { diff --git a/dev/tests/integration/testsuite/Magento/Store/_files/dump_config.php b/dev/tests/integration/testsuite/Magento/Store/_files/dump_config.php new file mode 100644 index 0000000000000..7e4337ee65200 --- /dev/null +++ b/dev/tests/integration/testsuite/Magento/Store/_files/dump_config.php @@ -0,0 +1,65 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +return [ + 'scopes' => [ + 'websites' => [ + 'admin' => [ + 'website_id' => '0', + 'code' => 'admin', + 'name' => 'Admin', + 'sort_order' => '0', + 'default_group_id' => '0', + 'is_default' => '0', + ], + 'main' => [ // base website code was changed to main + 'website_id' => '1', + 'code' => 'main', + 'name' => 'Main Website', + 'sort_order' => '0', + 'default_group_id' => '1', + 'is_default' => '1', + ], + ], + 'groups' => [ + 0 => [ + 'group_id' => '0', + 'website_id' => '0', + 'code' => 'default', + 'name' => 'Default', + 'root_category_id' => '0', + 'default_store_id' => '0', + ], + 1 => [ + 'group_id' => '1', + 'website_id' => '1', + 'code' => 'main_website_store', + 'name' => 'Main Website Store', + 'root_category_id' => '2', + 'default_store_id' => '1', + ], + ], + 'stores' => [ + 'admin' => [ + 'store_id' => '0', + 'code' => 'admin', + 'website_id' => '0', + 'group_id' => '0', + 'name' => 'Admin', + 'sort_order' => '0', + 'is_active' => '1', + ], + 'default' => [ + 'store_id' => '1', + 'code' => 'default', + 'website_id' => '1', + 'group_id' => '1', + 'name' => 'Default Store View', + 'sort_order' => '0', + 'is_active' => '1', + ], + ], + ] +]; diff --git a/dev/tests/integration/testsuite/Magento/Store/_files/inactive_store.php b/dev/tests/integration/testsuite/Magento/Store/_files/inactive_store.php new file mode 100644 index 0000000000000..bcd170bf3c2d7 --- /dev/null +++ b/dev/tests/integration/testsuite/Magento/Store/_files/inactive_store.php @@ -0,0 +1,34 @@ +<?php +/** + * Create store fixture + * + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +declare(strict_types=1); + +/** @var \Magento\Store\Model\Store $store */ +$store = \Magento\TestFramework\Helper\Bootstrap::getObjectManager()->create(\Magento\Store\Model\Store::class); +if (!$store->load('inactive_store', 'code')->getId()) { + $websiteId = \Magento\TestFramework\Helper\Bootstrap::getObjectManager()->get( + \Magento\Store\Model\StoreManagerInterface::class + )->getWebsite() + ->getId(); + $groupId = \Magento\TestFramework\Helper\Bootstrap::getObjectManager()->get( + \Magento\Store\Model\StoreManagerInterface::class + )->getWebsite()->getDefaultGroupId(); + $store->setCode( + 'inactive_store' + )->setWebsiteId( + $websiteId + )->setGroupId( + $groupId + )->setName( + 'Inactive Store' + )->setSortOrder( + 15 + )->setIsActive( + 0 + ); + $store->save(); +} diff --git a/dev/tests/integration/testsuite/Magento/Store/_files/inactive_store_rollback.php b/dev/tests/integration/testsuite/Magento/Store/_files/inactive_store_rollback.php new file mode 100644 index 0000000000000..4b03d2b0d5fde --- /dev/null +++ b/dev/tests/integration/testsuite/Magento/Store/_files/inactive_store_rollback.php @@ -0,0 +1,24 @@ +<?php +/** + * + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +declare(strict_types=1); + +/** @var \Magento\Framework\Registry $registry */ +$registry = \Magento\TestFramework\Helper\Bootstrap::getObjectManager()->get(\Magento\Framework\Registry::class); + +$registry->unregister('isSecureArea'); +$registry->register('isSecureArea', true); + +/** @var Magento\Store\Model\Store $store */ +$store = \Magento\TestFramework\Helper\Bootstrap::getObjectManager()->create(\Magento\Store\Model\Store::class); +$store->load('inactive_store'); + +if ($store->getId()) { + $store->delete(); +} + +$registry->unregister('isSecureArea'); +$registry->register('isSecureArea', false); diff --git a/dev/tests/integration/testsuite/Magento/Swatches/Block/Product/ListProductTest.php b/dev/tests/integration/testsuite/Magento/Swatches/Block/Product/ListProductTest.php new file mode 100644 index 0000000000000..e6f566ac156db --- /dev/null +++ b/dev/tests/integration/testsuite/Magento/Swatches/Block/Product/ListProductTest.php @@ -0,0 +1,238 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +declare(strict_types=1); + +namespace Magento\Swatches\Block\Product; + +use Magento\Catalog\Api\ProductAttributeRepositoryInterface; +use Magento\Catalog\Api\ProductRepositoryInterface; +use Magento\Catalog\Block\Product\Image; +use Magento\Catalog\Block\Product\ListProduct; +use Magento\Catalog\Model\ResourceModel\Product as ProductResource; +use Magento\Framework\App\RequestInterface; +use Magento\Framework\ObjectManagerInterface; +use Magento\Framework\View\LayoutInterface; +use Magento\Store\Model\Store; +use Magento\Swatches\Model\Plugin\ProductImage; +use Magento\TestFramework\Helper\Bootstrap; +use PHPUnit\Framework\TestCase; + +/** + * Tests for displaying configurable product image with swatch attributes. + * + * @magentoAppIsolation enabled + * @magentoDbIsolation enabled + * @magentoAppArea frontend + */ +class ListProductTest extends TestCase +{ + /** + * @var ObjectManagerInterface + */ + private $objectManager; + + /** + * @var ProductRepositoryInterface + */ + private $productRepository; + + /** + * @var ProductResource + */ + private $productResource; + + /** + * @var ProductAttributeRepositoryInterface + */ + private $attributeRepository; + + /** + * @var RequestInterface + */ + private $request; + + /** + * @var LayoutInterface + */ + private $layout; + + /** + * @var ListProduct + */ + private $listingBlock; + + /** + * @inheritdoc + */ + protected function setUp() + { + parent::setUp(); + $this->objectManager = Bootstrap::getObjectManager(); + $this->productRepository = $this->objectManager->get(ProductRepositoryInterface::class); + $this->productResource = $this->objectManager->get(ProductResource::class); + $this->attributeRepository = $this->objectManager->get(ProductAttributeRepositoryInterface::class); + $this->request = $this->objectManager->get(RequestInterface::class); + $this->layout = $this->objectManager->get(LayoutInterface::class); + $this->listingBlock = $this->layout->createBlock(ListProduct::class); + } + + /** + * @magentoDataFixture Magento/Swatches/_files/configurable_product_text_swatch_attribute.php + * @magentoDataFixture Magento/Catalog/_files/product_image.php + * @dataProvider getImageDataProvider + * @param array $images + * @param string $area + * @param array $expectation + * @return void + */ + public function testGetImageForTextSwatchConfigurable(array $images, string $area, array $expectation): void + { + $this->updateAttributePreviewImageFlag('text_swatch_attribute'); + $this->addFilterToRequest('text_swatch_attribute', 'option 1'); + $this->assertProductImage($images, $area, $expectation); + } + + /** + * @magentoDataFixture Magento/Swatches/_files/configurable_product_visual_swatch_attribute.php + * @magentoDataFixture Magento/Catalog/_files/product_image.php + * @dataProvider getImageDataProvider + * @param array $images + * @param string $area + * @param array $expectation + * @return void + */ + public function testGetImageForVisualSwatchConfigurable(array $images, string $area, array $expectation): void + { + $this->updateAttributePreviewImageFlag('visual_swatch_attribute'); + $this->addFilterToRequest('visual_swatch_attribute', 'option 1'); + $this->assertProductImage($images, $area, $expectation); + } + + /** + * @return array + */ + public function getImageDataProvider(): array + { + return [ + 'without_images_and_display_grid' => [ + 'images' => [], + 'display_area' => ProductImage::CATEGORY_PAGE_GRID_LOCATION, + 'expectation' => ['image_url' => 'placeholder/small_image.jpg', 'label' => 'Configurable Product'], + ], + 'without_images_and_display_list' => [ + 'images' => [], + 'display_area' => ProductImage::CATEGORY_PAGE_LIST_LOCATION, + 'expectation' => ['image_url' => 'placeholder/small_image.jpg', 'label' => 'Configurable Product'], + ], + 'with_image_on_configurable_and_display_grid' => [ + 'images' => ['configurable' => '/m/a/magento_image.jpg'], + 'display_area' => ProductImage::CATEGORY_PAGE_GRID_LOCATION, + 'expectation' => ['image_url' => '/m/a/magento_image.jpg', 'label' => 'Image Alt Text'], + ], + 'with_image_on_configurable_and_display_list' => [ + 'images' => ['configurable' => '/m/a/magento_image.jpg'], + 'display_area' => ProductImage::CATEGORY_PAGE_LIST_LOCATION, + 'expectation' => ['image_url' => '/m/a/magento_image.jpg', 'label' => 'Image Alt Text'], + ], + 'with_image_on_simple' => [ + 'images' => ['simple_option_1' => '/m/a/magento_small_image.jpg'], + 'display_area' => ProductImage::CATEGORY_PAGE_GRID_LOCATION, + 'expectation' => ['image_url' => '/m/a/magento_small_image.jpg', 'label' => 'Image Alt Text'], + ], + 'with_image_on_simple_and_configurable' => [ + 'images' => [ + 'configurable' => '/m/a/magento_image.jpg', + 'simple_option_1' => '/m/a/magento_small_image.jpg', + ], + 'display_area' => ProductImage::CATEGORY_PAGE_GRID_LOCATION, + 'expectation' => ['image_url' => '/m/a/magento_small_image.jpg', 'label' => 'Image Alt Text'], + ], + ]; + } + + /** + * Asserts image data. + * + * @param array $images + * @param string $area + * @param array $expectation + * @return void + */ + private function assertProductImage(array $images, string $area, array $expectation): void + { + $this->updateProductImages($images); + $productImage = $this->listingBlock->getImage($this->productRepository->get('configurable'), $area); + $this->assertInstanceOf(Image::class, $productImage); + $this->assertEquals($productImage->getCustomAttributes(), []); + $this->assertEquals($productImage->getClass(), 'product-image-photo'); + $this->assertEquals($productImage->getRatio(), 1.25); + $this->assertEquals($productImage->getLabel(), $expectation['label']); + $this->assertStringEndsWith($expectation['image_url'], $productImage->getImageUrl()); + $this->assertEquals($productImage->getWidth(), 240); + $this->assertEquals($productImage->getHeight(), 300); + } + + /** + * Updates products images. + * + * @param array $images + * @return void + */ + private function updateProductImages(array $images): void + { + foreach ($images as $sku => $imageName) { + $product = $this->productRepository->get($sku); + $product->setStoreId(Store::DEFAULT_STORE_ID) + ->setImage($imageName) + ->setSmallImage($imageName) + ->setThumbnail($imageName) + ->setData( + 'media_gallery', + [ + 'images' => [ + [ + 'file' => $imageName, + 'position' => 1, + 'label' => 'Image Alt Text', + 'disabled' => 0, + 'media_type' => 'image' + ], + ], + ] + ) + ->setCanSaveCustomOptions(true); + $this->productResource->save($product); + } + } + + /** + * Updates attribute "Update Product Preview Image" flag. + * + * @param string $attributeCode + * @return void + */ + private function updateAttributePreviewImageFlag(string $attributeCode): void + { + $attribute = $this->attributeRepository->get($attributeCode); + $attribute->setData('update_product_preview_image', 1); + $this->attributeRepository->save($attribute); + } + + /** + * Adds attribute param to request. + * + * @param string $attributeCode + * @param string $optionLabel + * @return void + */ + private function addFilterToRequest(string $attributeCode, string $optionLabel): void + { + $attribute = $this->attributeRepository->get($attributeCode); + $this->request->setParams( + [$attributeCode => $attribute->getSource()->getOptionId($optionLabel)] + ); + } +} diff --git a/dev/tests/integration/testsuite/Magento/Swatches/Block/Product/Renderer/Configurable/Listing/CategoryPageViewTest.php b/dev/tests/integration/testsuite/Magento/Swatches/Block/Product/Renderer/Configurable/Listing/CategoryPageViewTest.php new file mode 100644 index 0000000000000..7f842bf49644e --- /dev/null +++ b/dev/tests/integration/testsuite/Magento/Swatches/Block/Product/Renderer/Configurable/Listing/CategoryPageViewTest.php @@ -0,0 +1,109 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +declare(strict_types=1); + +namespace Magento\Swatches\Block\Product\Renderer\Configurable\Listing; + +use Magento\Swatches\Block\Product\Renderer\Configurable\ProductPageViewTest; +use Magento\Swatches\Block\Product\Renderer\Listing\Configurable; + +/** + * Test class to check configurable product with swatch attributes view behaviour on category page + * + * @magentoDbIsolation enabled + * @magentoAppIsolation enabled + */ +class CategoryPageViewTest extends ProductPageViewTest +{ + /** + * @inheritdoc + */ + protected function setUp() + { + parent::setUp(); + + $this->block = $this->layout->createBlock(Configurable::class); + $this->template = 'Magento_Swatches::product/listing/renderer.phtml'; + } + + /** + * @magentoDataFixture Magento/Swatches/_files/configurable_product_visual_swatch_attribute.php + * + * @dataProvider expectedVisualSwatchDataProvider + * + * @param array $expectedConfig + * @param array $expectedSwatchConfig + * @return void + */ + public function testCategoryPageVisualSwatchAttributeView(array $expectedConfig, array $expectedSwatchConfig): void + { + $this->checkProductViewCategoryPage($expectedConfig, $expectedSwatchConfig, ['visual_swatch_attribute']); + } + + /** + * @magentoDataFixture Magento/Swatches/_files/configurable_product_text_swatch_attribute.php + * + * @dataProvider expectedTextSwatchDataProvider + * + * @param array $expectedConfig + * @param array $expectedSwatchConfig + * @return void + */ + public function testCategoryPageTextSwatchAttributeView(array $expectedConfig, array $expectedSwatchConfig): void + { + $this->checkProductViewCategoryPage($expectedConfig, $expectedSwatchConfig, ['text_swatch_attribute']); + } + + /** + * @magentoDataFixture Magento/Swatches/_files/configurable_product_two_attributes.php + * + * @dataProvider expectedTwoAttributesProvider + * + * @param array $expectedConfig + * @param array $expectedSwatchConfig + * @return void + */ + public function testCategoryPageTwoAttributesView(array $expectedConfig, array $expectedSwatchConfig): void + { + $this->checkProductViewCategoryPage( + $expectedConfig, + $expectedSwatchConfig, + ['visual_swatch_attribute', 'text_swatch_attribute'] + ); + } + + /** + * Check configurable product view on category view page + * + * @param array $expectedConfig + * @param array $expectedSwatchConfig + * @param array $attributes + * @return void + */ + private function checkProductViewCategoryPage( + array $expectedConfig, + array $expectedSwatchConfig, + array $attributes + ): void { + $this->setAttributeUsedInProductListing($attributes); + $this->checkProductView($expectedConfig, $expectedSwatchConfig); + } + + /** + * Set used in product listing attributes value to true + * + * @param array $attributeCodes + * @return void + */ + private function setAttributeUsedInProductListing(array $attributeCodes): void + { + foreach ($attributeCodes as $attributeCode) { + $attribute = $this->productAttributeRepository->get($attributeCode); + $attribute->setUsedInProductListing('1'); + $this->productAttributeRepository->save($attribute); + } + } +} diff --git a/dev/tests/integration/testsuite/Magento/Swatches/Block/Product/Renderer/Configurable/PriceTest.php b/dev/tests/integration/testsuite/Magento/Swatches/Block/Product/Renderer/Configurable/PriceTest.php new file mode 100644 index 0000000000000..2c5bf805f92de --- /dev/null +++ b/dev/tests/integration/testsuite/Magento/Swatches/Block/Product/Renderer/Configurable/PriceTest.php @@ -0,0 +1,187 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +declare(strict_types=1); + +namespace Magento\Swatches\Block\Product\Renderer\Configurable; + +use Magento\Catalog\Api\Data\ProductInterface; +use Magento\Catalog\Api\Data\TierPriceInterface; +use Magento\Catalog\Api\ProductRepositoryInterface; +use Magento\Customer\Model\Group; +use Magento\Framework\ObjectManagerInterface; +use Magento\Framework\Registry; +use Magento\Framework\Serialize\SerializerInterface; +use Magento\Framework\View\Result\Page; +use Magento\Swatches\Block\Product\Renderer\Configurable; +use Magento\TestFramework\Helper\Bootstrap; +use PHPUnit\Framework\TestCase; + +/** + * Check configurable product price + * + * @magentoDbIsolation enabled + * @magentoAppIsolation enabled + * @magentoAppArea frontend + */ +class PriceTest extends TestCase +{ + /** @var ObjectManagerInterface */ + private $objectManager; + + /** @var Registry */ + private $registry; + + /** @var ProductRepositoryInterface */ + private $productRepository; + + /** @var Page */ + private $page; + + /** @var SerializerInterface */ + private $json; + + /** + * @inheritdoc + */ + protected function setUp() + { + parent::setUp(); + + $this->objectManager = Bootstrap::getObjectManager(); + $this->registry = $this->objectManager->get(Registry::class); + $this->page = $this->objectManager->get(Page::class); + $this->productRepository = $this->objectManager->get(ProductRepositoryInterface::class); + $this->productRepository->cleanCache(); + $this->json = $this->objectManager->get(SerializerInterface::class); + } + + /** + * @inheritdoc + */ + protected function tearDown() + { + $this->registry->unregister('product'); + + parent::tearDown(); + } + + /** + * @dataProvider childProductsDataProvider + * @magentoDataFixture Magento/Swatches/_files/configurable_product_visual_swatch_attribute.php + * @magentoCache config disabled + * + * @param array $updateData + * @param array $expectedData + * @return void + */ + public function testConfigurableOptionPrices(array $updateData, array $expectedData): void + { + $this->updateProducts($updateData); + $product = $this->productRepository->get('configurable'); + $this->registerProduct($product); + $configurableOptions = $this->getProductSwatchOptionsBlock()->getJsonConfig(); + $optionsData = $this->json->unserialize($configurableOptions); + $this->assertArrayHasKey('optionPrices', $optionsData); + $this->assertEquals($expectedData, array_values($optionsData['optionPrices'])); + } + + /** + * @return array + */ + public function childProductsDataProvider(): array + { + return [ + [ + 'update_data' => [ + 'simple_option_1' => [ + 'special_price' => 50, + ], + 'simple_option_2' => [ + 'special_price' => 58.55, + ], + 'simple_option_3' => [ + 'tier_price' => [ + [ + 'website_id' => 0, + 'cust_group' => Group::CUST_GROUP_ALL, + 'price_qty' => 1, + 'value_type' => TierPriceInterface::PRICE_TYPE_FIXED, + 'price' => 75, + ], + ], + ], + ], + 'expected_data' => [ + [ + 'oldPrice' => ['amount' => 150], + 'basePrice' => ['amount' => 50], + 'finalPrice' => ['amount' => 50], + 'tierPrices' => [], + 'msrpPrice' => ['amount' => null], + ], + [ + 'oldPrice' => ['amount' => 150], + 'basePrice' => ['amount' => 58.55], + 'finalPrice' => ['amount' => 58.55], + 'tierPrices' => [], + 'msrpPrice' => ['amount' => null], + ], + [ + 'oldPrice' => ['amount' => 150], + 'basePrice' => ['amount' => 75], + 'finalPrice' => ['amount' => 75], + 'tierPrices' => [], + 'msrpPrice' => ['amount' => null], + ], + ] + ], + ]; + } + + /** + * Update products. + * + * @param array $data + * @return void + */ + private function updateProducts(array $data): void + { + foreach ($data as $sku => $updateData) { + $product = $this->productRepository->get($sku); + $product->addData($updateData); + $this->productRepository->save($product); + } + } + + /** + * Register the product. + * + * @param ProductInterface $product + * @return void + */ + private function registerProduct(ProductInterface $product): void + { + $this->registry->unregister('product'); + $this->registry->register('product', $product); + } + + /** + * Get product swatch options block. + * + * @return Configurable + */ + private function getProductSwatchOptionsBlock(): Configurable + { + $this->page->addHandle([ + 'default', + 'catalog_product_view', + 'catalog_product_view_type_configurable', + ]); + $this->page->getLayout()->generateXml(); + + return $this->page->getLayout()->getChildBlock('product.info.options.wrapper', 'swatch_options'); + } +} diff --git a/dev/tests/integration/testsuite/Magento/Swatches/Block/Product/Renderer/Configurable/ProductPageViewTest.php b/dev/tests/integration/testsuite/Magento/Swatches/Block/Product/Renderer/Configurable/ProductPageViewTest.php new file mode 100644 index 0000000000000..2d016ef48faf5 --- /dev/null +++ b/dev/tests/integration/testsuite/Magento/Swatches/Block/Product/Renderer/Configurable/ProductPageViewTest.php @@ -0,0 +1,414 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +declare(strict_types=1); + +namespace Magento\Swatches\Block\Product\Renderer\Configurable; + +use Magento\Catalog\Api\ProductAttributeRepositoryInterface; +use Magento\Catalog\Api\ProductRepositoryInterface; +use Magento\Catalog\Model\ResourceModel\Product as ProductResource; +use Magento\Framework\ObjectManagerInterface; +use Magento\Framework\Registry; +use Magento\Framework\Serialize\SerializerInterface; +use Magento\Framework\View\LayoutInterface; +use Magento\Swatches\Block\Product\Renderer\Configurable; +use Magento\Swatches\Model\Swatch; +use Magento\TestFramework\Helper\Bootstrap; +use PHPUnit\Framework\TestCase; + +/** + * Test class to check configurable product with swatch attributes view behaviour on product page + * + * @magentoDbIsolation enabled + * @magentoAppIsolation enabled + */ +class ProductPageViewTest extends TestCase +{ + /** @var ObjectManagerInterface */ + protected $objectManager; + + /** @var Configurable */ + protected $block; + + /** @var string */ + protected $template; + + /** @var ProductAttributeRepositoryInterface */ + protected $productAttributeRepository; + + /** @var LayoutInterface */ + protected $layout; + + /** @var ProductRepositoryInterface */ + private $productRepository; + + /** @var Registry */ + private $registry; + + /** @var SerializerInterface */ + private $json; + + /** @var ProductResource */ + private $productResource; + + /** + * @inheritdoc + */ + protected function setUp() + { + parent::setUp(); + + $this->objectManager = Bootstrap::getObjectManager(); + $this->productRepository = $this->objectManager->create(ProductRepositoryInterface::class); + $this->layout = $this->objectManager->get(LayoutInterface::class); + $this->block = $this->layout->createBlock(Configurable::class); + $this->registry = $this->objectManager->get(Registry::class); + $this->json = $this->objectManager->get(SerializerInterface::class); + $this->productAttributeRepository = $this->objectManager->create(ProductAttributeRepositoryInterface::class); + $this->productResource = $this->objectManager->create(ProductResource::class); + $this->template = Configurable::SWATCH_RENDERER_TEMPLATE; + } + + /** + * @magentoDataFixture Magento/Swatches/_files/configurable_product_text_swatch_attribute.php + * + * @dataProvider expectedTextSwatchDataProvider + * + * @param array $expectedConfig + * @param array $expectedSwatchConfig + * @return void + */ + public function testProductPageTextSwatchAttributeView(array $expectedConfig, array $expectedSwatchConfig): void + { + $this->checkProductView($expectedConfig, $expectedSwatchConfig); + } + + /** + * @return array + */ + public function expectedTextSwatchDataProvider(): array + { + return [ + [ + 'json_config' => [ + 'text_swatch_attribute' => [ + 'label' => 'Text swatch attribute', + 'options' => [ + ['label' => 'Option 3', 'skus' => ['simple_option_3']], + ['label' => 'Option 1', 'skus' => ['simple_option_1']], + ['label' => 'Option 2', 'skus' => ['simple_option_2']], + ], + ], + ], + 'json_swatch_config' => [ + Swatch::SWATCH_INPUT_TYPE_TEXT => [ + [ + 'type' => Swatch::SWATCH_TYPE_TEXTUAL, + 'value' => 'Swatch 3', + 'label' => 'Option 3', + ], + [ + 'type' => Swatch::SWATCH_TYPE_TEXTUAL, + 'value' => 'Swatch 1', + 'label' => 'Option 1', + ], + [ + 'type' => Swatch::SWATCH_TYPE_TEXTUAL, + 'value' => 'Swatch 2', + 'label' => 'Option 2', + ], + 'additional_data' => "{\"swatch_input_type\":\"text\"}", + ], + + ], + ], + ]; + } + + /** + * @magentoDataFixture Magento/Swatches/_files/configurable_product_visual_swatch_attribute.php + * + * @dataProvider expectedVisualSwatchDataProvider + * + * @param array $expectedConfig + * @param array $expectedSwatchConfig + * @return void + */ + public function testProductPageVisualSwatchAttributeView(array $expectedConfig, array $expectedSwatchConfig): void + { + $this->checkProductView($expectedConfig, $expectedSwatchConfig); + } + + /** + * @return array + */ + public function expectedVisualSwatchDataProvider(): array + { + return [ + [ + 'json_config' => [ + 'visual_swatch_attribute' => [ + 'label' => 'Visual swatch attribute', + 'options' => [ + ['label' => 'option 3', 'skus' => ['simple_option_3']], + ['label' => 'option 2', 'skus' => ['simple_option_2']], + ['label' => 'option 1', 'skus' => ['simple_option_1']], + ], + ], + ], + 'json_swatch_config' => [ + Swatch::SWATCH_INPUT_TYPE_VISUAL => [ + [ + 'type' => Swatch::SWATCH_TYPE_VISUAL_COLOR, + 'value' => '#555555', + 'label' => 'option 1', + ], + [ + 'type' => Swatch::SWATCH_TYPE_VISUAL_COLOR, + 'value' => '#aaaaaa', + 'label' => 'option 2', + ], + [ + 'type' => Swatch::SWATCH_TYPE_VISUAL_COLOR, + 'value' => '#ffffff', + 'label' => 'option 3', + ], + 'additional_data' => "{\"swatch_input_type\":\"visual\"}", + ], + ], + ], + ]; + } + + /** + * @magentoDataFixture Magento/Swatches/_files/configurable_product_two_attributes.php + * + * @dataProvider expectedTwoAttributesProvider + * + * @param array $expectedConfig + * @param array $expectedSwatchConfig + * @return void + */ + public function testProductPageTwoAttributesView(array $expectedConfig, array $expectedSwatchConfig): void + { + $this->checkProductView($expectedConfig, $expectedSwatchConfig); + } + + /** + * @SuppressWarnings(PHPMD.ExcessiveMethodLength) + * @return array + */ + public function expectedTwoAttributesProvider(): array + { + return [ + [ + 'json_config' => [ + 'visual_swatch_attribute' => [ + 'label' => 'Visual swatch attribute', + 'options' => [ + [ + 'label' => 'option 3', + 'skus' => [ + 'simple_option_3_option_3', + 'simple_option_1_option_3', + 'simple_option_2_option_3', + ], + ], + [ + 'label' => 'option 2', + 'skus' => [ + 'simple_option_3_option_2', + 'simple_option_1_option_2', + 'simple_option_2_option_2', + ], + ], + [ + 'label' => 'option 1', + 'skus' => [ + 'simple_option_3_option_1', + 'simple_option_1_option_1', + 'simple_option_2_option_1', + ], + ], + ], + ], + 'text_swatch_attribute' => [ + 'label' => 'Text swatch attribute', + 'options' => [ + [ + 'label' => 'Option 3', + 'skus' => [ + 'simple_option_3_option_1', + 'simple_option_3_option_2', + 'simple_option_3_option_3', + ], + ], + [ + 'label' => 'Option 2', + 'skus' => [ + 'simple_option_2_option_1', + 'simple_option_2_option_2', + 'simple_option_2_option_3', + ], + ], + [ + 'label' => 'Option 1', + 'skus' => [ + 'simple_option_1_option_1', + 'simple_option_1_option_2', + 'simple_option_1_option_3', + ], + ], + ], + ], + + ], + 'json_swatch_config' => [ + Swatch::SWATCH_INPUT_TYPE_VISUAL => [ + [ + 'type' => Swatch::SWATCH_TYPE_VISUAL_COLOR, + 'value' => '#555555', + 'label' => 'option 1', + ], + [ + 'type' => Swatch::SWATCH_TYPE_VISUAL_COLOR, + 'value' => '#aaaaaa', + 'label' => 'option 2', + ], + [ + 'type' => Swatch::SWATCH_TYPE_VISUAL_COLOR, + 'value' => '#ffffff', + 'label' => 'option 3', + ], + 'additional_data' => "{\"swatch_input_type\":\"visual\"}", + ], + Swatch::SWATCH_INPUT_TYPE_TEXT => [ + [ + 'type' => Swatch::SWATCH_TYPE_TEXTUAL, + 'value' => 'Swatch 3', + 'label' => 'Option 3', + ], + [ + 'type' => Swatch::SWATCH_TYPE_TEXTUAL, + 'value' => 'Swatch 1', + 'label' => 'Option 1', + ], + [ + 'type' => Swatch::SWATCH_TYPE_TEXTUAL, + 'value' => 'Swatch 2', + 'label' => 'Option 2', + ], + 'additional_data' => "{\"swatch_input_type\":\"text\"}", + ], + ], + ], + ]; + } + + /** + * Check configurable product view + * + * @param $expectedConfig + * @param $expectedSwatchConfig + * @return void + */ + protected function checkProductView($expectedConfig, $expectedSwatchConfig): void + { + $actualConfig = $this->generateBlockJsonConfigData(); + $this->checkResultIsNotEmpty($actualConfig); + $this->assertConfig($actualConfig['json_config'], $expectedConfig); + $this->assertSwatchConfig($actualConfig['json_swatch_config'], $expectedSwatchConfig); + } + + /** + * Generate block config data + * + * @return array + */ + + private function generateBlockJsonConfigData(): array + { + $product = $this->productRepository->get('configurable'); + $this->block->setProduct($product); + $this->block->setTemplate($this->template); + $jsonConfig = $this->json->unserialize($this->block->getJsonConfig())['attributes'] ?? []; + $jsonSwatchConfig = $this->json->unserialize($this->block->getJsonSwatchConfig()); + + return ['json_config' => $jsonConfig, 'json_swatch_config' => $jsonSwatchConfig]; + } + + /** + * Assert that correct data was generated + * + * @param array $actualData + * @param array $expectedData + * @return void + */ + private function assertSwatchConfig(array $actualData, array $expectedData): void + { + foreach ($actualData as $actualDataItem) { + $currentType = $this->json->unserialize($actualDataItem['additional_data'])['swatch_input_type'] ?? null; + $this->assertNotNull($currentType); + $this->assertEquals($expectedData[$currentType]['additional_data'], $actualDataItem['additional_data']); + unset($actualDataItem['additional_data']); + foreach ($actualDataItem as $item) { + $this->assertContains($item, $expectedData[$currentType]); + } + } + } + + /** + * Assert that correct swatch data was generated + * + * @param array $actualData + * @param array $expectedData + * @return void + */ + private function assertConfig(array $actualData, array $expectedData): void + { + foreach ($actualData as $actualDataItem) { + $expectedItem = $expectedData[$actualDataItem['code']]; + $this->assertEquals($expectedItem['label'], $actualDataItem['label']); + $this->checkOptions($actualDataItem, $expectedItem); + } + } + + /** + * Check result is not not empty + * + * @param array $result + */ + private function checkResultIsNotEmpty(array $result): void + { + foreach ($result as $item) { + $this->assertNotEmpty($item); + } + } + + /** + * Check attribute options + * + * @param array $actualDataItem + * @param array $expectedItem + * @return void + */ + private function checkOptions(array $actualDataItem, array $expectedItem): void + { + foreach ($expectedItem['options'] as $expectedOption) { + $expectedSkus = array_values($expectedOption['skus']); + $expectedIds = array_values($this->productResource->getProductsIdsBySkus($expectedSkus)); + foreach ($actualDataItem['options'] as $option) { + if ($option['label'] === $expectedOption['label']) { + $this->assertEquals( + sort($expectedIds), + sort($option['products']), + 'Wrong product linked as option' + ); + } + } + } + } +} diff --git a/dev/tests/integration/testsuite/Magento/Swatches/Block/Product/Renderer/ConfigurableTest.php b/dev/tests/integration/testsuite/Magento/Swatches/Block/Product/Renderer/ConfigurableTest.php new file mode 100644 index 0000000000000..a750abf1e0bb3 --- /dev/null +++ b/dev/tests/integration/testsuite/Magento/Swatches/Block/Product/Renderer/ConfigurableTest.php @@ -0,0 +1,252 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +declare(strict_types=1); + +namespace Magento\Swatches\Block\Product\Renderer; + +use Magento\Catalog\Api\Data\ProductAttributeInterface; +use Magento\Catalog\Api\Data\ProductInterface; +use Magento\Catalog\Api\ProductAttributeRepositoryInterface; +use Magento\Catalog\Api\ProductRepositoryInterface; +use Magento\Catalog\Model\Product\Image\UrlBuilder; +use Magento\Catalog\Model\ResourceModel\Product as ProductResource; +use Magento\ConfigurableProduct\Block\Product\View\Type\Configurable; +use Magento\Framework\ObjectManagerInterface; +use Magento\Framework\Serialize\SerializerInterface; +use Magento\Framework\View\LayoutInterface; +use Magento\Store\Model\Store; +use Magento\Swatches\Block\Product\Renderer\Configurable as ConfigurableBlock; +use Magento\Swatches\Helper\Media; +use Magento\Swatches\Model\Swatch; +use Magento\TestFramework\Helper\Bootstrap; +use PHPUnit\Framework\TestCase; + +/** + * Tests for configurable products options block with swatch attribute. + * + * @magentoAppIsolation enabled + * @magentoDbIsolation enabled + * @magentoAppArea frontend + * @SuppressWarnings(PHPMD.CouplingBetweenObjects) + */ +class ConfigurableTest extends TestCase +{ + /** + * @var ObjectManagerInterface + */ + private $objectManager; + + /** + * @var SerializerInterface + */ + private $serializer; + + /** + * @var Media + */ + private $swatchHelper; + + /** + * @var UrlBuilder + */ + private $imageUrlBuilder; + + /** + * @var Configurable + */ + private $block; + + /** + * @var ProductRepositoryInterface + */ + private $productRepository; + + /** + * @var ProductAttributeRepositoryInterface + */ + private $productAttributeRepository; + + /** + * @var ProductResource + */ + private $productResource; + + /** + * @var ProductAttributeInterface + */ + private $configurableAttribute; + + /** + * @var ProductInterface + */ + private $product; + + /** + * @inheritdoc + */ + protected function setUp() + { + parent::setUp(); + $this->objectManager = Bootstrap::getObjectManager(); + $this->serializer = $this->objectManager->get(SerializerInterface::class); + $this->swatchHelper = $this->objectManager->get(Media::class); + $this->imageUrlBuilder = $this->objectManager->get(UrlBuilder::class); + $this->productAttributeRepository = $this->objectManager->get(ProductAttributeRepositoryInterface::class); + $this->configurableAttribute = $this->productAttributeRepository->get('test_configurable'); + $this->productRepository = $this->objectManager->get(ProductRepositoryInterface::class); + $this->productRepository->cleanCache(); + $this->product = $this->productRepository->get('configurable'); + $this->productResource = $this->objectManager->get(ProductResource::class); + $this->block = $this->objectManager->get(LayoutInterface::class)->createBlock(ConfigurableBlock::class); + $this->block->setProduct($this->product); + } + + /** + * @magentoDataFixture Magento/Swatches/_files/configurable_product_with_visual_swatch_attribute.php + * @return void + */ + public function testGetJsonSwatchConfig(): void + { + $expectedOptions = $this->getDefaultOptionsList(); + $expectedOptions['option 2']['value'] = $this->swatchHelper->getSwatchAttributeImage( + Swatch::SWATCH_IMAGE_NAME, + '/visual_swatch_attribute_option_type_image.jpg' + ); + $expectedOptions['option 2']['thumb'] = $this->swatchHelper->getSwatchAttributeImage( + Swatch::SWATCH_THUMBNAIL_NAME, + '/visual_swatch_attribute_option_type_image.jpg' + ); + $config = $this->serializer->unserialize($this->block->getJsonSwatchConfig()); + $this->assertOptionsData($config, $expectedOptions, ['swatch_input_type' => 'visual']); + } + + /** + * @magentoDataFixture Magento/Swatches/_files/configurable_product_with_visual_swatch_attribute.php + * @magentoDataFixture Magento/Catalog/_files/product_image.php + * @return void + */ + public function testGetJsonSwatchConfigUsedProductImage(): void + { + $this->updateAttributeUseProductImageFlag(); + $this->updateProductImage('simple_option_2', '/m/a/magento_image.jpg'); + $expectedOptions = $this->getDefaultOptionsList(); + $expectedOptions['option 2']['value'] = $this->imageUrlBuilder->getUrl( + '/m/a/magento_image.jpg', + 'swatch_image_base' + ); + $expectedOptions['option 2']['thumb'] = $this->imageUrlBuilder->getUrl( + '/m/a/magento_image.jpg', + 'swatch_thumb_base' + ); + $this->assertOptionsData( + $this->serializer->unserialize($this->block->getJsonSwatchConfig()), + $expectedOptions, + ['swatch_input_type' => 'visual', 'use_product_image_for_swatch' => 1] + ); + } + + /** + * @magentoDataFixture Magento/Swatches/_files/configurable_product_with_visual_swatch_attribute.php + * @return void + */ + public function testGetJsonSwatchConfigUsedEmptyProductImage(): void + { + $this->updateAttributeUseProductImageFlag(); + $expectedOptions = $this->getDefaultOptionsList(); + $expectedOptions['option 2']['value'] = $this->swatchHelper->getSwatchAttributeImage( + Swatch::SWATCH_IMAGE_NAME, + '/visual_swatch_attribute_option_type_image.jpg' + ); + $expectedOptions['option 2']['thumb'] = $this->swatchHelper->getSwatchAttributeImage( + Swatch::SWATCH_THUMBNAIL_NAME, + '/visual_swatch_attribute_option_type_image.jpg' + ); + $this->assertOptionsData( + $this->serializer->unserialize($this->block->getJsonSwatchConfig()), + $expectedOptions, + ['swatch_input_type' => 'visual', 'use_product_image_for_swatch' => 1] + ); + } + + /** + * @return array + */ + private function getDefaultOptionsList(): array + { + return [ + 'option 1' => ['type' => '1', 'value' => '#000000', 'label' => 'option 1'], + 'option 2' => ['type' => '2', 'value' => '', 'thumb' => '', 'label' => 'option 2'], + 'option 3' => ['type' => '3', 'value' => null, 'label' => 'option 3'], + ]; + } + + /** + * Asserts swatch options data. + * + * @param array $config + * @param array $expectedOptions + * @param array $expectedAdditional + * @return void + */ + private function assertOptionsData(array $config, array $expectedOptions, array $expectedAdditional): void + { + $this->assertNotEmpty($config); + $resultOptions = $config[$this->configurableAttribute->getAttributeId()]; + foreach ($expectedOptions as $label => $data) { + $resultOption = $resultOptions[$this->configurableAttribute->getSource()->getOptionId($label)]; + $this->assertEquals($data['type'], $resultOption['type']); + $this->assertEquals($data['label'], $resultOption['label']); + $this->assertEquals($data['value'], $resultOption['value']); + if (!empty($data['thumb'])) { + $this->assertEquals($data['thumb'], $resultOption['thumb']); + } + } + $this->assertEquals($expectedAdditional, $this->serializer->unserialize($resultOptions['additional_data'])); + } + + /** + * Updates attribute 'use_product_image_for_swatch' flag. + * + * @return void + */ + private function updateAttributeUseProductImageFlag(): void + { + $this->configurableAttribute->setData('use_product_image_for_swatch', 1); + $this->configurableAttribute = $this->productAttributeRepository->save($this->configurableAttribute); + } + + /** + * Updates Product image. + * + * @param string $sku + * @param string $imageName + * @return void + */ + private function updateProductImage(string $sku, string $imageName): void + { + $product = $this->productRepository->get($sku); + $product->setStoreId(Store::DEFAULT_STORE_ID) + ->setImage($imageName) + ->setSmallImage($imageName) + ->setThumbnail($imageName) + ->setData( + 'media_gallery', + [ + 'images' => [ + [ + 'file' => $imageName, + 'position' => 1, + 'label' => 'Image Alt Text', + 'disabled' => 0, + 'media_type' => 'image' + ], + ] + ] + ) + ->setCanSaveCustomOptions(true); + $this->productResource->save($product); + } +} diff --git a/dev/tests/integration/testsuite/Magento/Swatches/Controller/Adminhtml/Product/Attribute/Delete/SwatchesAttributesControllerTest.php b/dev/tests/integration/testsuite/Magento/Swatches/Controller/Adminhtml/Product/Attribute/Delete/SwatchesAttributesControllerTest.php new file mode 100644 index 0000000000000..1ff53b312e65a --- /dev/null +++ b/dev/tests/integration/testsuite/Magento/Swatches/Controller/Adminhtml/Product/Attribute/Delete/SwatchesAttributesControllerTest.php @@ -0,0 +1,48 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +declare(strict_types=1); + +namespace Magento\Swatches\Controller\Adminhtml\Product\Attribute\Delete; + +use Magento\Catalog\Controller\Adminhtml\Product\Attribute\Delete\AbstractDeleteAttributeControllerTest; + +/** + * Delete catalog product attributes with input types like "swatch_text" and "swatch_visual". + * Attributes from Magento_Swatches module. + * + * @magentoAppArea adminhtml + * @magentoDbIsolation enabled + */ +class SwatchesAttributesControllerTest extends AbstractDeleteAttributeControllerTest +{ + /** + * Assert that attribute with input type "swatch_text" will be deleted + * after dispatch delete product attribute action. + * + * @magentoDataFixture Magento/Swatches/_files/product_text_swatch_attribute.php + * + * @return void + */ + public function testDeleteSwatchTextAttribute(): void + { + $this->dispatchDeleteAttribute('text_swatch_attribute'); + $this->assertAttributeIsDeleted('text_swatch_attribute'); + } + + /** + * Assert that attribute with input type "swatch_visual" will be deleted + * after dispatch delete product attribute action. + * + * @magentoDataFixture Magento/Swatches/_files/swatch_attribute.php + * + * @return void + */ + public function testDeleteSwatchVisualAttribute(): void + { + $this->dispatchDeleteAttribute('color_swatch'); + $this->assertAttributeIsDeleted('color_swatch'); + } +} diff --git a/dev/tests/integration/testsuite/Magento/Swatches/Controller/Adminhtml/Product/Attribute/Save/InputType/TextSwatchTest.php b/dev/tests/integration/testsuite/Magento/Swatches/Controller/Adminhtml/Product/Attribute/Save/InputType/TextSwatchTest.php index e9839266b07a0..348afff7fe9ba 100644 --- a/dev/tests/integration/testsuite/Magento/Swatches/Controller/Adminhtml/Product/Attribute/Save/InputType/TextSwatchTest.php +++ b/dev/tests/integration/testsuite/Magento/Swatches/Controller/Adminhtml/Product/Attribute/Save/InputType/TextSwatchTest.php @@ -7,7 +7,7 @@ namespace Magento\Swatches\Controller\Adminhtml\Product\Attribute\Save\InputType; -use Magento\Catalog\Controller\Adminhtml\Product\Attribute\Save\InputType\AbstractSaveAttributeTest; +use Magento\Catalog\Controller\Adminhtml\Product\Attribute\Save\AbstractSaveAttributeTest; use Magento\Eav\Api\Data\AttributeInterface; use Magento\Eav\Model\Entity\Attribute\Source\Table; @@ -15,13 +15,14 @@ * Test cases related to create attribute with input type text swatch. * * @magentoDbIsolation enabled + * @magentoAppArea adminhtml */ class TextSwatchTest extends AbstractSaveAttributeTest { /** * Test create attribute and compare attribute data and input data. * - * @dataProvider \Magento\TestFramework\Swatches\Model\Attribute\DataProvider\TextSwatch::getAttributeDataWithCheckArray() + * @dataProvider \Magento\TestFramework\Swatches\Model\Attribute\DataProvider\TextSwatch::getAttributeDataWithCheckArray * * @param array $attributePostData * @param array $checkArray @@ -35,7 +36,7 @@ public function testCreateAttribute(array $attributePostData, array $checkArray) /** * Test create attribute with error. * - * @dataProvider \Magento\TestFramework\Swatches\Model\Attribute\DataProvider\TextSwatch::getAttributeDataWithErrorMessage() + * @dataProvider \Magento\TestFramework\Swatches\Model\Attribute\DataProvider\TextSwatch::getAttributeDataWithErrorMessage * * @param array $attributePostData * @param string $errorMessage diff --git a/dev/tests/integration/testsuite/Magento/Swatches/Controller/Adminhtml/Product/Attribute/Save/InputType/VisualSwatchTest.php b/dev/tests/integration/testsuite/Magento/Swatches/Controller/Adminhtml/Product/Attribute/Save/InputType/VisualSwatchTest.php index 56b051c8ec9c2..0ee64f0de9ca3 100644 --- a/dev/tests/integration/testsuite/Magento/Swatches/Controller/Adminhtml/Product/Attribute/Save/InputType/VisualSwatchTest.php +++ b/dev/tests/integration/testsuite/Magento/Swatches/Controller/Adminhtml/Product/Attribute/Save/InputType/VisualSwatchTest.php @@ -7,7 +7,7 @@ namespace Magento\Swatches\Controller\Adminhtml\Product\Attribute\Save\InputType; -use Magento\Catalog\Controller\Adminhtml\Product\Attribute\Save\InputType\AbstractSaveAttributeTest; +use Magento\Catalog\Controller\Adminhtml\Product\Attribute\Save\AbstractSaveAttributeTest; use Magento\Eav\Api\Data\AttributeInterface; use Magento\Eav\Model\Entity\Attribute\Source\Table; @@ -15,13 +15,14 @@ * Test cases related to create attribute with input type visual swatch. * * @magentoDbIsolation enabled + * @magentoAppArea adminhtml */ class VisualSwatchTest extends AbstractSaveAttributeTest { /** * Test create attribute and compare attribute data and input data. * - * @dataProvider \Magento\TestFramework\Swatches\Model\Attribute\DataProvider\VisualSwatch::getAttributeDataWithCheckArray() + * @dataProvider \Magento\TestFramework\Swatches\Model\Attribute\DataProvider\VisualSwatch::getAttributeDataWithCheckArray * * @param array $attributePostData * @param array $checkArray @@ -35,7 +36,7 @@ public function testCreateAttribute(array $attributePostData, array $checkArray) /** * Test create attribute with error. * - * @dataProvider \Magento\TestFramework\Swatches\Model\Attribute\DataProvider\VisualSwatch::getAttributeDataWithErrorMessage() + * @dataProvider \Magento\TestFramework\Swatches\Model\Attribute\DataProvider\VisualSwatch::getAttributeDataWithErrorMessage * * @param array $attributePostData * @param string $errorMessage diff --git a/dev/tests/integration/testsuite/Magento/Swatches/Controller/Adminhtml/Product/Attribute/Update/AbstractUpdateSwatchAttributeTest.php b/dev/tests/integration/testsuite/Magento/Swatches/Controller/Adminhtml/Product/Attribute/Update/AbstractUpdateSwatchAttributeTest.php new file mode 100644 index 0000000000000..2152a9c93419c --- /dev/null +++ b/dev/tests/integration/testsuite/Magento/Swatches/Controller/Adminhtml/Product/Attribute/Update/AbstractUpdateSwatchAttributeTest.php @@ -0,0 +1,146 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +declare(strict_types=1); + +namespace Magento\Swatches\Controller\Adminhtml\Product\Attribute\Update; + +use Magento\Catalog\Api\Data\ProductAttributeInterface; +use Magento\Catalog\Controller\Adminhtml\Product\Attribute\Update\AbstractUpdateAttributeTest; +use Magento\Swatches\Model\ResourceModel\Swatch\CollectionFactory as SwatchCollectionFactory; +use Magento\Swatches\Model\Swatch; +use Magento\Swatches\Model\SwatchAttributeType; + +/** + * Base update and assert swatch attribute data. + */ +abstract class AbstractUpdateSwatchAttributeTest extends AbstractUpdateAttributeTest +{ + /** @var SwatchAttributeType */ + private $swatchAttributeType; + + /** @var SwatchCollectionFactory */ + private $swatchCollectionFactory; + + /** + * @inheritdoc + */ + protected function setUp() + { + parent::setUp(); + $this->swatchAttributeType = $this->_objectManager->get(SwatchAttributeType::class); + $this->swatchCollectionFactory = $this->_objectManager->get(SwatchCollectionFactory::class); + } + + /** + * @inheritdoc + */ + protected function replaceStoreCodeWithId(array $optionsArray): array + { + $optionsArray = parent::replaceStoreCodeWithId($optionsArray); + foreach ($optionsArray as $key => $option) { + if (isset($option['swatch'])) { + $optionsArray[$key]['swatch'] = $this->prepareStoresData($option['swatch']); + } + } + + return $optionsArray; + } + + /** + * @inheritdoc + */ + protected function getActualOptionsData(string $attributeId): array + { + $actualOptionsData = parent::getActualOptionsData($attributeId); + foreach (array_keys($actualOptionsData) as $optionId) { + $actualOptionsData[$optionId]['swatch'] = $this->getAttributeOptionSwatchValues($optionId); + } + + return $actualOptionsData; + } + + /** + * @inheritdoc + */ + protected function prepareStoreOptionsPostData(array $optionsData): array + { + $optionsPostData = parent::prepareStoreOptionsPostData($optionsData); + $swatchType = $this->getSwatchType(); + $swatchOptionsPostData = []; + + foreach ($optionsData as $optionId => $option) { + $data = []; + $data['option' . $swatchType] = $optionsPostData[$optionId]['option']; + $optionSwatch = $swatchType == Swatch::SWATCH_INPUT_TYPE_VISUAL ? $option['swatch'][0] : $option['swatch']; + + $data['swatch' . $swatchType] = [ + 'value' => [ + $optionId => $optionSwatch, + ], + ]; + if (isset($optionsPostData[$optionId]['default'])) { + $data['default' . $swatchType] = $optionsPostData[$optionId]['default']; + } + $swatchOptionsPostData[] = $data; + } + + return $swatchOptionsPostData; + } + + /** + * @inheritdoc + */ + protected function prepareStoreOptionsExpectedData(array $optionsData): array + { + $optionsExpectedData = parent::prepareStoreOptionsExpectedData($optionsData); + $optionsArray = $optionsExpectedData['options_array']; + foreach (array_keys($optionsArray) as $optionId) { + $optionsArray[$optionId]['swatch'] = $optionsData[$optionId]['swatch']; + } + + return [ + 'options_array' => $optionsArray, + 'default_value' => $optionsExpectedData['default_value'], + ]; + } + + /** + * @inheritdoc + */ + protected function assertUpdateAttributeData( + ProductAttributeInterface $attribute, + array $expectedData + ): void { + $this->swatchAttributeType->isSwatchAttribute($attribute); + parent::assertUpdateAttributeData($attribute, $expectedData); + } + + /** + * Get attribute option swatch values by option id. + * + * @param int $optionId + * @return array + */ + private function getAttributeOptionSwatchValues(int $optionId): array + { + $swatchValues = []; + $collection = $this->swatchCollectionFactory->create(); + $collection->addFieldToFilter('option_id', $optionId); + + foreach ($collection as $item) { + $swatchValues[$item->getData('store_id')] = $item->getData('value'); + } + + return $swatchValues; + } + + /** + * Get swatch type. + * + * @return string + */ + abstract protected function getSwatchType(): string; +} diff --git a/dev/tests/integration/testsuite/Magento/Swatches/Controller/Adminhtml/Product/Attribute/Update/InputType/TextSwatchTest.php b/dev/tests/integration/testsuite/Magento/Swatches/Controller/Adminhtml/Product/Attribute/Update/InputType/TextSwatchTest.php new file mode 100644 index 0000000000000..b62671bef04a4 --- /dev/null +++ b/dev/tests/integration/testsuite/Magento/Swatches/Controller/Adminhtml/Product/Attribute/Update/InputType/TextSwatchTest.php @@ -0,0 +1,91 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +declare(strict_types=1); + +namespace Magento\Swatches\Controller\Adminhtml\Product\Attribute\Update\InputType; + +use Magento\Swatches\Controller\Adminhtml\Product\Attribute\Update\AbstractUpdateSwatchAttributeTest; +use Magento\Swatches\Model\Swatch; + +/** + * Test cases related to update attribute with input type text swatch. + * + * @magentoDbIsolation enabled + * @magentoAppArea adminhtml + */ +class TextSwatchTest extends AbstractUpdateSwatchAttributeTest +{ + /** + * Test update attribute. + * + * @dataProvider \Magento\TestFramework\Swatches\Model\Attribute\DataProvider\TextSwatch::getUpdateProvider + * @magentoDataFixture Magento/Swatches/_files/product_text_swatch_attribute.php + * + * @param array $postData + * @param array $expectedData + * @return void + */ + public function testUpdateAttribute(array $postData, array $expectedData): void + { + $this->updateAttributeUsingData('text_swatch_attribute', $postData); + $this->assertUpdateAttributeProcess('text_swatch_attribute', $postData, $expectedData); + } + + /** + * Test update attribute with error. + * + * @dataProvider \Magento\TestFramework\Swatches\Model\Attribute\DataProvider\TextSwatch::getUpdateProviderWithErrorMessage + * @magentoDataFixture Magento/Swatches/_files/product_text_swatch_attribute.php + * + * @param array $postData + * @param string $errorMessage + * @return void + */ + public function testUpdateAttributeWithError(array $postData, string $errorMessage): void + { + $this->updateAttributeUsingData('text_swatch_attribute', $postData); + $this->assertErrorSessionMessages($errorMessage); + } + + /** + * Test update attribute frontend labels on stores. + * + * @dataProvider \Magento\TestFramework\Swatches\Model\Attribute\DataProvider\TextSwatch::getUpdateFrontendLabelsProvider + * @magentoDataFixture Magento/Store/_files/second_website_with_two_stores.php + * @magentoDataFixture Magento/Swatches/_files/product_text_swatch_attribute.php + * + * @param array $postData + * @param array $expectedData + * @return void + */ + public function testUpdateFrontendLabelOnStores(array $postData, array $expectedData): void + { + $this->processUpdateFrontendLabelOnStores('text_swatch_attribute', $postData, $expectedData); + } + + /** + * Test update attribute options on stores. + * + * @dataProvider \Magento\TestFramework\Swatches\Model\Attribute\DataProvider\TextSwatch::getUpdateOptionsProvider + * @magentoDataFixture Magento/Store/_files/second_website_with_two_stores.php + * @magentoDataFixture Magento/Swatches/_files/product_text_swatch_attribute.php + * + * @param array $postData + * @return void + */ + public function testUpdateOptionsOnStores(array $postData): void + { + $this->processUpdateOptionsOnStores('text_swatch_attribute', $postData); + } + + /** + * @inheritdoc + */ + protected function getSwatchType(): string + { + return Swatch::SWATCH_INPUT_TYPE_TEXT; + } +} diff --git a/dev/tests/integration/testsuite/Magento/Swatches/Controller/Adminhtml/Product/Attribute/Update/InputType/VisualSwatchTest.php b/dev/tests/integration/testsuite/Magento/Swatches/Controller/Adminhtml/Product/Attribute/Update/InputType/VisualSwatchTest.php new file mode 100644 index 0000000000000..b5aa58bbd3339 --- /dev/null +++ b/dev/tests/integration/testsuite/Magento/Swatches/Controller/Adminhtml/Product/Attribute/Update/InputType/VisualSwatchTest.php @@ -0,0 +1,91 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +declare(strict_types=1); + +namespace Magento\Swatches\Controller\Adminhtml\Product\Attribute\Update\InputType; + +use Magento\Swatches\Controller\Adminhtml\Product\Attribute\Update\AbstractUpdateSwatchAttributeTest; +use Magento\Swatches\Model\Swatch; + +/** + * Test cases related to update attribute with input type visual swatch. + * + * @magentoDbIsolation enabled + * @magentoAppArea adminhtml + */ +class VisualSwatchTest extends AbstractUpdateSwatchAttributeTest +{ + /** + * Test update attribute. + * + * @dataProvider \Magento\TestFramework\Swatches\Model\Attribute\DataProvider\VisualSwatch::getUpdateProvider + * @magentoDataFixture Magento/Swatches/_files/product_visual_swatch_attribute.php + * + * @param array $postData + * @param array $expectedData + * @return void + */ + public function testUpdateAttribute(array $postData, array $expectedData): void + { + $this->updateAttributeUsingData('visual_swatch_attribute', $postData); + $this->assertUpdateAttributeProcess('visual_swatch_attribute', $postData, $expectedData); + } + + /** + * Test update attribute with error. + * + * @dataProvider \Magento\TestFramework\Swatches\Model\Attribute\DataProvider\VisualSwatch::getUpdateProviderWithErrorMessage + * @magentoDataFixture Magento/Swatches/_files/product_visual_swatch_attribute.php + * + * @param array $postData + * @param string $errorMessage + * @return void + */ + public function testUpdateAttributeWithError(array $postData, string $errorMessage): void + { + $this->updateAttributeUsingData('visual_swatch_attribute', $postData); + $this->assertErrorSessionMessages($errorMessage); + } + + /** + * Test update attribute frontend labels on stores. + * + * @dataProvider \Magento\TestFramework\Swatches\Model\Attribute\DataProvider\VisualSwatch::getUpdateFrontendLabelsProvider + * @magentoDataFixture Magento/Store/_files/second_website_with_two_stores.php + * @magentoDataFixture Magento/Swatches/_files/product_visual_swatch_attribute.php + * + * @param array $postData + * @param array $expectedData + * @return void + */ + public function testUpdateFrontendLabelOnStores(array $postData, array $expectedData): void + { + $this->processUpdateFrontendLabelOnStores('visual_swatch_attribute', $postData, $expectedData); + } + + /** + * Test update attribute options on stores. + * + * @dataProvider \Magento\TestFramework\Swatches\Model\Attribute\DataProvider\VisualSwatch::getUpdateOptionsProvider + * @magentoDataFixture Magento/Store/_files/second_website_with_two_stores.php + * @magentoDataFixture Magento/Swatches/_files/product_visual_swatch_attribute.php + * + * @param array $postData + * @return void + */ + public function testUpdateOptionsOnStores(array $postData): void + { + $this->processUpdateOptionsOnStores('visual_swatch_attribute', $postData); + } + + /** + * @inheritdoc + */ + protected function getSwatchType(): string + { + return Swatch::SWATCH_INPUT_TYPE_VISUAL; + } +} diff --git a/dev/tests/integration/testsuite/Magento/Swatches/_files/configurable_product_text_swatch_attribute.php b/dev/tests/integration/testsuite/Magento/Swatches/_files/configurable_product_text_swatch_attribute.php new file mode 100644 index 0000000000000..b5586acdfeebf --- /dev/null +++ b/dev/tests/integration/testsuite/Magento/Swatches/_files/configurable_product_text_swatch_attribute.php @@ -0,0 +1,93 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +declare(strict_types=1); + +use Magento\Catalog\Api\Data\ProductExtensionFactory; +use Magento\Catalog\Api\ProductAttributeRepositoryInterface; +use Magento\Catalog\Api\ProductRepositoryInterface; +use Magento\Catalog\Model\Product\Attribute\Source\Status; +use Magento\Catalog\Model\Product\Type as ProductType; +use Magento\Catalog\Model\Product\Visibility; +use Magento\Catalog\Model\ProductFactory; +use Magento\ConfigurableProduct\Helper\Product\Options\Factory; +use Magento\ConfigurableProduct\Model\Product\Type\Configurable; +use Magento\Framework\ObjectManagerInterface; +use Magento\Store\Api\WebsiteRepositoryInterface; +use Magento\TestFramework\Helper\Bootstrap; + +require __DIR__ . '/product_text_swatch_attribute.php'; + +/** @var ObjectManagerInterface $objectManager */ +$objectManager = Bootstrap::getObjectManager(); +/** @var ProductAttributeRepositoryInterface $productAttributeRepository */ +$productAttributeRepository = $objectManager->create(ProductAttributeRepositoryInterface::class); +$attribute = $productAttributeRepository->get('text_swatch_attribute'); +$options = $attribute->getOptions(); +/** @var WebsiteRepositoryInterface $websiteRepository */ +$websiteRepository = $objectManager->get(WebsiteRepositoryInterface::class); +$baseWebsite = $websiteRepository->get('base'); +/** @var ProductRepositoryInterface $productRepository */ +$productRepository = $objectManager->create(ProductRepositoryInterface::class); +/** @var ProductFactory $productFactory */ +$productFactory = $objectManager->get(ProductFactory::class); +$attributeValues = []; +$associatedProductIds = []; +$rootCategoryId = $baseWebsite->getDefaultStore()->getRootCategoryId(); +array_shift($options); + +foreach ($options as $option) { + $product = $productFactory->create(); + $product->setTypeId(ProductType::TYPE_SIMPLE) + ->setAttributeSetId($product->getDefaultAttributeSetId()) + ->setWebsiteIds([$baseWebsite->getId()]) + ->setName('Configurable Option ' . $option->getLabel()) + ->setSku(strtolower(str_replace(' ', '_', 'simple ' . $option->getLabel()))) + ->setPrice(150) + ->setTextSwatchAttribute($option->getValue()) + ->setVisibility(Visibility::VISIBILITY_NOT_VISIBLE) + ->setStatus(Status::STATUS_ENABLED) + ->setCategoryIds([$rootCategoryId]) + ->setStockData(['use_config_manage_stock' => 1, 'qty' => 100, 'is_qty_decimal' => 0, 'is_in_stock' => 1]); + $product = $productRepository->save($product); + + $attributeValues[] = [ + 'label' => 'test', + 'attribute_id' => $attribute->getId(), + 'value_index' => $option->getValue(), + ]; + $associatedProductIds[] = $product->getId(); +} +/** @var Factory $optionsFactory */ +$optionsFactory = $objectManager->get(Factory::class); +$configurableAttributesData = [ + [ + 'attribute_id' => $attribute->getId(), + 'code' => $attribute->getAttributeCode(), + 'label' => $attribute->getStoreLabel(), + 'position' => '0', + 'values' => $attributeValues, + ], +]; +$configurableOptions = $optionsFactory->create($configurableAttributesData); + +$product = $productFactory->create(); +/** @var ProductExtensionFactory $extensionAttributesFactory */ +$extensionAttributesFactory = $objectManager->get(ProductExtensionFactory::class); +$extensionConfigurableAttributes = $product->getExtensionAttributes() ?: $extensionAttributesFactory->create(); +$extensionConfigurableAttributes->setConfigurableProductOptions($configurableOptions); +$extensionConfigurableAttributes->setConfigurableProductLinks($associatedProductIds); +$product->setExtensionAttributes($extensionConfigurableAttributes); + +$product->setTypeId(Configurable::TYPE_CODE) + ->setAttributeSetId($product->getDefaultAttributeSetId()) + ->setWebsiteIds([$baseWebsite->getId()]) + ->setName('Configurable Product') + ->setSku('configurable') + ->setVisibility(Visibility::VISIBILITY_BOTH) + ->setStatus(Status::STATUS_ENABLED) + ->setCategoryIds([$rootCategoryId]) + ->setStockData(['use_config_manage_stock' => 1, 'is_in_stock' => 1]); +$productRepository->save($product); diff --git a/dev/tests/integration/testsuite/Magento/Swatches/_files/configurable_product_text_swatch_attribute_rollback.php b/dev/tests/integration/testsuite/Magento/Swatches/_files/configurable_product_text_swatch_attribute_rollback.php new file mode 100644 index 0000000000000..f5c91e255e1a3 --- /dev/null +++ b/dev/tests/integration/testsuite/Magento/Swatches/_files/configurable_product_text_swatch_attribute_rollback.php @@ -0,0 +1,44 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +declare(strict_types=1); + +use Magento\Catalog\Api\ProductAttributeRepositoryInterface; +use Magento\Catalog\Api\ProductRepositoryInterface; +use Magento\Framework\Exception\NoSuchEntityException; +use Magento\Framework\ObjectManagerInterface; +use Magento\Framework\Registry; +use Magento\TestFramework\Helper\Bootstrap; + +/** @var ObjectManagerInterface $objectManager */ +$objectManager = Bootstrap::getObjectManager(); +/** @var Registry $registry */ +$registry = $objectManager->get(Registry::class); +$registry->unregister('isSecureArea'); +$registry->register('isSecureArea', true); +/** @var ProductRepositoryInterface $productRepository */ +$productRepository = $objectManager->get(ProductRepositoryInterface::class); +/** @var ProductAttributeRepositoryInterface $productAttributeRepository */ +$productAttributeRepository = $objectManager->get(ProductAttributeRepositoryInterface::class); +$attribute = $productAttributeRepository->get('text_swatch_attribute'); +$options = $attribute->getOptions(); +array_shift($options); +$productsArray = []; +foreach ($options as $option) { + $productsArray [] = strtolower(str_replace(' ', '_', 'simple ' . $option->getLabel())); +} +$productsArray[] = 'configurable'; +foreach ($productsArray as $sku) { + try { + $productRepository->deleteById($sku); + } catch (NoSuchEntityException $e) { + //Product already removed + } +} + +$registry->unregister('isSecureArea'); +$registry->register('isSecureArea', false); + +require __DIR__ . '/product_text_swatch_attribute_rollback.php'; diff --git a/dev/tests/integration/testsuite/Magento/Swatches/_files/configurable_product_two_attributes.php b/dev/tests/integration/testsuite/Magento/Swatches/_files/configurable_product_two_attributes.php new file mode 100644 index 0000000000000..b0d1948ac8be2 --- /dev/null +++ b/dev/tests/integration/testsuite/Magento/Swatches/_files/configurable_product_two_attributes.php @@ -0,0 +1,120 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +declare(strict_types=1); + +use Magento\Catalog\Api\Data\ProductExtensionFactory; +use Magento\Catalog\Api\ProductAttributeRepositoryInterface; +use Magento\Catalog\Api\ProductRepositoryInterface; +use Magento\Catalog\Model\Product; +use Magento\Catalog\Model\Product\Attribute\Source\Status; +use Magento\Catalog\Model\Product\Type as ProductType; +use Magento\Catalog\Model\Product\Visibility; +use Magento\Catalog\Model\ProductFactory; +use Magento\ConfigurableProduct\Helper\Product\Options\Factory; +use Magento\ConfigurableProduct\Model\Product\Type\Configurable; +use Magento\Framework\ObjectManagerInterface; +use Magento\Store\Api\WebsiteRepositoryInterface; +use Magento\TestFramework\Helper\Bootstrap; + +require __DIR__ . '/product_text_swatch_attribute.php'; +require __DIR__ . '/product_visual_swatch_attribute.php'; + +/** @var ObjectManagerInterface $objectManager */ +$objectManager = Bootstrap::getObjectManager(); +/** @var ProductAttributeRepositoryInterface $productAttributeRepository */ +$productAttributeRepository = $objectManager->get(ProductAttributeRepositoryInterface::class); +$attribute = $productAttributeRepository->get('text_swatch_attribute'); +$secondAttribute = $productAttributeRepository->get('visual_swatch_attribute'); +$options = $attribute->getOptions(); +$secondAttributeOptions = $secondAttribute->getOptions(); +/** @var WebsiteRepositoryInterface $websiteRepository */ +$websiteRepository = $objectManager->get(WebsiteRepositoryInterface::class); +$baseWebsite = $websiteRepository->get('base'); +/** @var ProductAttributeRepositoryInterface $productRepository */ +$productRepository = $objectManager->create(ProductRepositoryInterface::class); +$attributeValues = []; +$secondAttributeValues = []; +$associatedProductIds = []; +$associatedProductIdsViaSecondAttribute = []; +$attributeSetId = $installer->getAttributeSetId(Product::ENTITY, 'Default'); +$productFactory = $objectManager->get(ProductFactory::class); +$rootCategoryId = $baseWebsite->getDefaultStore()->getRootCategoryId(); +array_shift($options); +array_shift($secondAttributeOptions); + +foreach ($options as $option) { + foreach ($secondAttributeOptions as $secondAttrOption) { + $product = $productFactory->create(); + $product->setTypeId(ProductType::TYPE_SIMPLE) + ->setAttributeSetId($product->getDefaultAttributeSetId()) + ->setWebsiteIds([$baseWebsite->getId()]) + ->setName('Configurable Option ' . $option->getLabel()) + ->setSku( + strtolower( + str_replace(' ', '_', 'simple ' . $option->getLabel() . '_' . $secondAttrOption->getLabel()) + ) + ) + ->setPrice(150) + ->setTextSwatchAttribute($option->getValue()) + ->setVisualSwatchAttribute($secondAttrOption->getValue()) + ->setVisibility(Visibility::VISIBILITY_NOT_VISIBLE) + ->setStatus(Status::STATUS_ENABLED) + ->setCategoryIds([$rootCategoryId]) + ->setStockData(['use_config_manage_stock' => 1, 'qty' => 100, 'is_qty_decimal' => 0, 'is_in_stock' => 1]); + $product = $productRepository->save($product, true); + $associatedProductIds[] = $product->getId(); + } + + $attributeValues[] = [ + 'label' => 'test1', + 'attribute_id' => $attribute->getId(), + 'value_index' => $option->getValue(), + ]; +} +foreach ($secondAttributeOptions as $secondAttrOption) { + $secondAttributeValues[] = [ + 'label' => 'test2', + 'attribute_id' => $secondAttribute->getId(), + 'value_index' => $secondAttrOption->getValue(), + ]; +} + +$allAttributes = [$attribute, $secondAttribute]; +$optionsFactory = $objectManager->get(Factory::class); + +foreach ($allAttributes as $attribute) { + $configurableAttributesData[] = + [ + 'attribute_id' => $attribute->getId(), + 'code' => $attribute->getAttributeCode(), + 'label' => $attribute->getStoreLabel(), + 'position' => '0', + 'values' => $attribute->getAttributeCode() === 'text_swatch_attribute' + ? $attributeValues + : $secondAttributeValues, + ]; + +} + +$configurableOptions = $optionsFactory->create($configurableAttributesData); +$product = $productFactory->create(); +/** @var ProductExtensionFactory $extensionAttributesFactory */ +$extensionAttributesFactory = $objectManager->get(ProductExtensionFactory::class); +$extensionConfigurableAttributes = $product->getExtensionAttributes() ?: $extensionAttributesFactory->create(); +$extensionConfigurableAttributes->setConfigurableProductOptions($configurableOptions); +$extensionConfigurableAttributes->setConfigurableProductLinks($associatedProductIds); +$product->setExtensionAttributes($extensionConfigurableAttributes); + +$product->setTypeId(Configurable::TYPE_CODE) + ->setAttributeSetId($product->getDefaultAttributeSetId()) + ->setWebsiteIds([$baseWebsite->getId()]) + ->setName('Configurable Product') + ->setSku('configurable') + ->setVisibility(Visibility::VISIBILITY_BOTH) + ->setStatus(Status::STATUS_ENABLED) + ->setCategoryIds([$rootCategoryId]) + ->setStockData(['use_config_manage_stock' => 1, 'is_in_stock' => 1]); +$productRepository->save($product); diff --git a/dev/tests/integration/testsuite/Magento/Swatches/_files/configurable_product_two_attributes_rollback.php b/dev/tests/integration/testsuite/Magento/Swatches/_files/configurable_product_two_attributes_rollback.php new file mode 100644 index 0000000000000..c92330e49688b --- /dev/null +++ b/dev/tests/integration/testsuite/Magento/Swatches/_files/configurable_product_two_attributes_rollback.php @@ -0,0 +1,52 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +declare(strict_types=1); + +use Magento\Catalog\Api\ProductAttributeRepositoryInterface; +use Magento\Catalog\Api\ProductRepositoryInterface; +use Magento\Framework\Exception\NoSuchEntityException; +use Magento\Framework\ObjectManagerInterface; +use Magento\Framework\Registry; +use Magento\TestFramework\Helper\Bootstrap; + +/** @var ObjectManagerInterface $objectManager */ +$objectManager = Bootstrap::getObjectManager(); +/** @var Registry $registry */ +$registry = $objectManager->get(Registry::class); +$registry->unregister('isSecureArea'); +$registry->register('isSecureArea', true); +/** @var ProductRepositoryInterface $productRepository */ +$productRepository = $objectManager->get(ProductRepositoryInterface::class); +/** @var ProductAttributeRepositoryInterface $productAttributeRepository */ +$productAttributeRepository = $objectManager->create(ProductAttributeRepositoryInterface::class); +$options = $productAttributeRepository->get('text_swatch_attribute')->getOptions(); +$secondAttributeOptions = $productAttributeRepository->get('visual_swatch_attribute')->getOptions(); +array_shift($options); +array_shift($secondAttributeOptions); +$productsArray = []; + +foreach ($options as $option) { + foreach ($secondAttributeOptions as $secondAttrOption) { + $productsArray[] = strtolower( + str_replace(' ', '_', 'simple ' . $option->getLabel() . '_' . $secondAttrOption->getLabel()) + ); + } +} + +$productsArray[] = 'configurable'; +foreach ($productsArray as $sku) { + try { + $productRepository->deleteById($sku); + } catch (NoSuchEntityException $e) { + //Product already removed + } +} + +$registry->unregister('isSecureArea'); +$registry->register('isSecureArea', false); + +require __DIR__ . '/product_text_swatch_attribute_rollback.php'; +require __DIR__ . '/product_visual_swatch_attribute_rollback.php'; diff --git a/dev/tests/integration/testsuite/Magento/Swatches/_files/configurable_product_visual_swatch_attribute.php b/dev/tests/integration/testsuite/Magento/Swatches/_files/configurable_product_visual_swatch_attribute.php new file mode 100644 index 0000000000000..c47be2717f5a8 --- /dev/null +++ b/dev/tests/integration/testsuite/Magento/Swatches/_files/configurable_product_visual_swatch_attribute.php @@ -0,0 +1,93 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +declare(strict_types=1); + +use Magento\Catalog\Api\Data\ProductExtensionFactory; +use Magento\Catalog\Api\ProductAttributeRepositoryInterface; +use Magento\Catalog\Api\ProductRepositoryInterface; +use Magento\Catalog\Model\Product\Attribute\Source\Status; +use Magento\Catalog\Model\Product\Type as ProductType; +use Magento\Catalog\Model\Product\Visibility; +use Magento\Catalog\Model\ProductFactory; +use Magento\ConfigurableProduct\Helper\Product\Options\Factory; +use Magento\ConfigurableProduct\Model\Product\Type\Configurable; +use Magento\Framework\ObjectManagerInterface; +use Magento\Store\Api\WebsiteRepositoryInterface; +use Magento\TestFramework\Helper\Bootstrap; + +require __DIR__ . '/product_visual_swatch_attribute.php'; + +/** @var ObjectManagerInterface $objectManager */ +$objectManager = Bootstrap::getObjectManager(); +/** @var ProductAttributeRepositoryInterface $productAttributeRepository */ +$productAttributeRepository = $objectManager->create(ProductAttributeRepositoryInterface::class); +$attribute = $productAttributeRepository->get('visual_swatch_attribute'); +$options = $attribute->getOptions(); +/** @var WebsiteRepositoryInterface $websiteRepository */ +$websiteRepository = $objectManager->get(WebsiteRepositoryInterface::class); +$baseWebsite = $websiteRepository->get('base'); +/** @var ProductRepositoryInterface $productRepository */ +$productRepository = $objectManager->create(ProductRepositoryInterface::class); +/** @var ProductFactory $productFactory */ +$productFactory = $objectManager->get(ProductFactory::class); +$attributeValues = []; +$associatedProductIds = []; +$rootCategoryId = $baseWebsite->getDefaultStore()->getRootCategoryId(); +array_shift($options); + +foreach ($options as $option) { + $product = $productFactory->create(); + $product->setTypeId(ProductType::TYPE_SIMPLE) + ->setAttributeSetId($product->getDefaultAttributeSetId()) + ->setWebsiteIds([$baseWebsite->getId()]) + ->setName('Configurable Option ' . $option->getLabel()) + ->setSku(strtolower(str_replace(' ', '_', 'simple ' . $option->getLabel()))) + ->setPrice(150) + ->setVisualSwatchAttribute($option->getValue()) + ->setVisibility(Visibility::VISIBILITY_NOT_VISIBLE) + ->setStatus(Status::STATUS_ENABLED) + ->setCategoryIds([$rootCategoryId]) + ->setStockData(['use_config_manage_stock' => 1, 'qty' => 100, 'is_qty_decimal' => 0, 'is_in_stock' => 1]); + $product = $productRepository->save($product); + + $attributeValues[] = [ + 'label' => 'test', + 'attribute_id' => $attribute->getId(), + 'value_index' => $option->getValue(), + ]; + $associatedProductIds[] = $product->getId(); +} +/** @var Factory $optionsFactory */ +$optionsFactory = $objectManager->get(Factory::class); +$configurableAttributesData = [ + [ + 'attribute_id' => $attribute->getId(), + 'code' => $attribute->getAttributeCode(), + 'label' => $attribute->getStoreLabel(), + 'position' => '0', + 'values' => $attributeValues, + ], +]; +$configurableOptions = $optionsFactory->create($configurableAttributesData); + +$product = $productFactory->create(); +/** @var ProductExtensionFactory $extensionAttributesFactory */ +$extensionAttributesFactory = $objectManager->get(ProductExtensionFactory::class); +$extensionConfigurableAttributes = $product->getExtensionAttributes() ?: $extensionAttributesFactory->create(); +$extensionConfigurableAttributes->setConfigurableProductOptions($configurableOptions); +$extensionConfigurableAttributes->setConfigurableProductLinks($associatedProductIds); +$product->setExtensionAttributes($extensionConfigurableAttributes); + +$product->setTypeId(Configurable::TYPE_CODE) + ->setAttributeSetId($product->getDefaultAttributeSetId()) + ->setWebsiteIds([$baseWebsite->getId()]) + ->setName('Configurable Product') + ->setSku('configurable') + ->setVisibility(Visibility::VISIBILITY_BOTH) + ->setStatus(Status::STATUS_ENABLED) + ->setCategoryIds([$rootCategoryId]) + ->setStockData(['use_config_manage_stock' => 1, 'is_in_stock' => 1]); +$productRepository->save($product); diff --git a/dev/tests/integration/testsuite/Magento/Swatches/_files/configurable_product_visual_swatch_attribute_rollback.php b/dev/tests/integration/testsuite/Magento/Swatches/_files/configurable_product_visual_swatch_attribute_rollback.php new file mode 100644 index 0000000000000..22ea728f36327 --- /dev/null +++ b/dev/tests/integration/testsuite/Magento/Swatches/_files/configurable_product_visual_swatch_attribute_rollback.php @@ -0,0 +1,46 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +declare(strict_types=1); + +use Magento\Catalog\Api\ProductAttributeRepositoryInterface; +use Magento\Catalog\Api\ProductRepositoryInterface; +use Magento\Framework\Exception\NoSuchEntityException; +use Magento\Framework\ObjectManagerInterface; +use Magento\Framework\Registry; +use Magento\TestFramework\Helper\Bootstrap; + +/** @var ObjectManagerInterface $objectManager */ +$objectManager = Bootstrap::getObjectManager(); +/** @var Registry $registry */ +$registry = $objectManager->get(Registry::class); +$registry->unregister('isSecureArea'); +$registry->register('isSecureArea', true); +/** @var ProductRepositoryInterface $productRepository */ +$productRepository = $objectManager->get(ProductRepositoryInterface::class); +/** @var ProductAttributeRepositoryInterface $productAttributeRepository */ +$productAttributeRepository = $objectManager->create(ProductAttributeRepositoryInterface::class); +$attribute = $productAttributeRepository->get('visual_swatch_attribute'); +$options = $attribute->getOptions(); +array_shift($options); +$productsArray = []; + +foreach ($options as $option) { + $productsArray [] = strtolower(str_replace(' ', '_', 'simple ' . $option->getLabel())); +} + +$productsArray[] = 'configurable'; +foreach ($productsArray as $sku) { + try { + $productRepository->deleteById($sku); + } catch (NoSuchEntityException $e) { + //Product already removed + } +} + +$registry->unregister('isSecureArea'); +$registry->register('isSecureArea', false); + +require __DIR__ . '/product_visual_swatch_attribute_rollback.php'; diff --git a/dev/tests/integration/testsuite/Magento/Swatches/_files/configurable_product_with_visual_swatch_attribute.php b/dev/tests/integration/testsuite/Magento/Swatches/_files/configurable_product_with_visual_swatch_attribute.php new file mode 100644 index 0000000000000..0d1eb1ce12f76 --- /dev/null +++ b/dev/tests/integration/testsuite/Magento/Swatches/_files/configurable_product_with_visual_swatch_attribute.php @@ -0,0 +1,86 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +declare(strict_types=1); + +use Magento\Catalog\Api\Data\ProductExtensionInterfaceFactory; +use Magento\Catalog\Api\ProductRepositoryInterface; +use Magento\Catalog\Model\Product; +use Magento\Catalog\Model\Product\Attribute\Source\Status; +use Magento\Catalog\Model\Product\Type; +use Magento\Catalog\Model\Product\Visibility; +use Magento\Catalog\Model\ProductFactory; +use Magento\ConfigurableProduct\Helper\Product\Options\Factory; +use Magento\ConfigurableProduct\Model\Product\Type\Configurable; +use Magento\Store\Api\WebsiteRepositoryInterface; +use Magento\TestFramework\Helper\Bootstrap; + +require __DIR__ . '/visual_swatch_attribute_with_different_options_type.php'; + +$objectManager = Bootstrap::getObjectManager(); +/** @var ProductRepositoryInterface $productRepository */ +$productRepository = $objectManager->get(ProductRepositoryInterface::class); +/** @var ProductFactory $productFactory */ +$productFactory = $objectManager->get(ProductFactory::class); +/** @var Factory $optionsFactory */ +$optionsFactory = $objectManager->get(Factory::class); +/** @var ProductExtensionInterfaceFactory $productExtensionAttributes */ +$productExtensionAttributesFactory = $objectManager->get(ProductExtensionInterfaceFactory::class); +/** @var WebsiteRepositoryInterface $websiteRepository */ +$websiteRepository = $objectManager->get(WebsiteRepositoryInterface::class); +$defaultWebsiteId = $websiteRepository->get('base')->getId(); +$attributeValues = $associatedProductIds = []; +$options = $attribute->getSource()->getAllOptions(); +array_shift($options); +foreach ($options as $option) { + /** @var Product $product */ + $product = $productFactory->create(); + $product->setTypeId(Type::TYPE_SIMPLE) + ->setAttributeSetId($product->getDefaultAttributeSetId()) + ->setWebsiteIds([$defaultWebsiteId]) + ->setName('Configurable Option' . $option['label']) + ->setSku('simple_' . str_replace(' ', '_', $option['label'])) + ->setPrice(100) + ->setData('test_configurable', $option['value']) + ->setVisibility(Visibility::VISIBILITY_NOT_VISIBLE) + ->setStatus(Status::STATUS_ENABLED) + ->setStockData(['use_config_manage_stock' => 1, 'qty' => 100, 'is_qty_decimal' => 0, 'is_in_stock' => 1]); + $product = $productRepository->save($product); + $attributeValues[] = [ + 'label' => 'test', + 'attribute_id' => $attribute->getId(), + 'value_index' => $option['value'], + ]; + $associatedProductIds[] = $product->getId(); +} + +/** @var Product $configurableProduct */ +$configurableProduct = $productFactory->create(); +$configurableOptions = $optionsFactory->create( + [ + [ + 'attribute_id' => $attribute->getId(), + 'code' => $attribute->getAttributeCode(), + 'label' => $attribute->getStoreLabel(), + 'position' => '0', + 'values' => $attributeValues, + ], + ] +); +$extensionConfigurableAttributes = $configurableProduct->getExtensionAttributes() + ?: $productExtensionAttributesFactory->create(); +$extensionConfigurableAttributes->setConfigurableProductOptions($configurableOptions); +$extensionConfigurableAttributes->setConfigurableProductLinks($associatedProductIds); + +$configurableProduct->setExtensionAttributes($extensionConfigurableAttributes); +$configurableProduct->setTypeId(Configurable::TYPE_CODE) + ->setAttributeSetId($configurableProduct->getDefaultAttributeSetId()) + ->setWebsiteIds([$defaultWebsiteId]) + ->setName('Configurable Product') + ->setSku('configurable') + ->setVisibility(Visibility::VISIBILITY_BOTH) + ->setStatus(Status::STATUS_ENABLED) + ->setStockData(['use_config_manage_stock' => 1, 'is_in_stock' => 1]); +$productRepository->save($configurableProduct); diff --git a/dev/tests/integration/testsuite/Magento/Swatches/_files/configurable_product_with_visual_swatch_attribute_rollback.php b/dev/tests/integration/testsuite/Magento/Swatches/_files/configurable_product_with_visual_swatch_attribute_rollback.php new file mode 100644 index 0000000000000..e2cddfa9065e2 --- /dev/null +++ b/dev/tests/integration/testsuite/Magento/Swatches/_files/configurable_product_with_visual_swatch_attribute_rollback.php @@ -0,0 +1,44 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +declare(strict_types=1); + +use Magento\Catalog\Api\ProductAttributeRepositoryInterface; +use Magento\Catalog\Api\ProductRepositoryInterface; +use Magento\Framework\Exception\NoSuchEntityException; +use Magento\Framework\Registry; +use Magento\TestFramework\Helper\Bootstrap; + +$objectManager = Bootstrap::getObjectManager(); +/** @var Registry $registry */ +$registry = $objectManager->get(Registry::class); +/** @var ProductRepositoryInterface $productRepository */ +$productRepository = $objectManager->get(ProductRepositoryInterface::class); +/** @var ProductAttributeRepositoryInterface $attributeRepository */ +$attributeRepository = $objectManager->create(ProductAttributeRepositoryInterface::class); +$attribute = $attributeRepository->get('test_configurable'); + +$registry->unregister('isSecureArea'); +$registry->register('isSecureArea', true); +$options = $attribute->getSource()->getAllOptions(); +array_shift($options); +foreach ($options as $option) { + try { + $productRepository->deleteById('simple_' . str_replace(' ', '_', $option['label'])); + } catch (NoSuchEntityException $e) { + //Product already removed + } +} + +try { + $productRepository->deleteById('configurable'); +} catch (NoSuchEntityException $e) { + //Product already removed +} + +require __DIR__ . '/visual_swatch_attribute_with_different_options_type_rollback.php'; + +$registry->unregister('isSecureArea'); +$registry->register('isSecureArea', false); diff --git a/dev/tests/integration/testsuite/Magento/Swatches/_files/product_visual_swatch_attribute_rollback.php b/dev/tests/integration/testsuite/Magento/Swatches/_files/product_visual_swatch_attribute_rollback.php index 67157532bdb98..e6b23a757441d 100644 --- a/dev/tests/integration/testsuite/Magento/Swatches/_files/product_visual_swatch_attribute_rollback.php +++ b/dev/tests/integration/testsuite/Magento/Swatches/_files/product_visual_swatch_attribute_rollback.php @@ -11,6 +11,7 @@ use Magento\Catalog\Api\ProductAttributeRepositoryInterface; $objectManager = Bootstrap::getObjectManager(); +/** @var Registry $registry */ $registry = $objectManager->get(Registry::class); $registry->unregister('isSecureArea'); $registry->register('isSecureArea', true); @@ -18,7 +19,7 @@ $attributeRepository = $objectManager->create(ProductAttributeRepositoryInterface::class); try { - $attributeRepository->deleteById('text_swatch_attribute'); + $attributeRepository->deleteById('visual_swatch_attribute'); } catch (NoSuchEntityException $e) { } $registry->unregister('isSecureArea'); diff --git a/dev/tests/integration/testsuite/Magento/Swatches/_files/text_swatch_attribute.php b/dev/tests/integration/testsuite/Magento/Swatches/_files/text_swatch_attribute.php new file mode 100644 index 0000000000000..26602d2b2e2f0 --- /dev/null +++ b/dev/tests/integration/testsuite/Magento/Swatches/_files/text_swatch_attribute.php @@ -0,0 +1,103 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +declare(strict_types=1); + +use Magento\Catalog\Api\Data\ProductAttributeInterface; +use Magento\Catalog\Setup\CategorySetup; +use Magento\Eav\Api\AttributeRepositoryInterface; +use Magento\Eav\Api\Data\AttributeOptionInterface; +use Magento\Swatches\Model\Swatch; +use Magento\TestFramework\Helper\Bootstrap; + +/** @var $installer CategorySetup */ +$installer = Bootstrap::getObjectManager()->create(\Magento\Catalog\Setup\CategorySetup::class); +/** @var AttributeRepositoryInterface $attributeRepository */ +$attributeRepository = Bootstrap::getObjectManager()->create(AttributeRepositoryInterface::class); + +// Add attribute data +$data = [ + 'attribute_code' => 'test_configurable', + 'entity_type_id' => $installer->getEntityTypeId('catalog_product'), + 'is_global' => 1, + 'is_user_defined' => 1, + 'frontend_input' => 'select', + 'is_unique' => 0, + 'is_required' => 0, + 'is_searchable' => 0, + 'is_visible_in_advanced_search' => 0, + 'is_comparable' => 0, + 'is_filterable' => 0, + 'is_filterable_in_search' => 0, + 'is_used_for_promo_rules' => 0, + 'is_html_allowed_on_front' => 1, + 'is_visible_on_front' => 0, + 'used_in_product_listing' => 0, + 'used_for_sort_by' => 0, + 'frontend_label' => ['Test Configurable'], + 'backend_type' => 'int', +]; + +$optionsPerAttribute = 3; + +$data['frontend_input'] = 'select'; +$data['swatch_input_type'] = Swatch::SWATCH_INPUT_TYPE_TEXT; + +$data['swatchtext']['value'] = array_reduce( + range(1, $optionsPerAttribute), + function ($values, $index) use ($optionsPerAttribute) { + $values['option_' . $index] = ['option ' . $index]; + return $values; + }, + [] +); + +$data['optiontext']['value'] = array_reduce( + range(1, $optionsPerAttribute), + function ($values, $index) use ($optionsPerAttribute) { + $values['option_' . $index] = ['option ' . $index]; + return $values; + }, + [] +); + +$data['optiontext']['order'] = array_reduce( + range(1, $optionsPerAttribute), + function ($values, $index) use ($optionsPerAttribute) { + $values['option_' . $index] = $index; + return $values; + }, + [] +); + +$data['options']['option'] = array_reduce( + range(1, $optionsPerAttribute), + function ($values, $index) use ($optionsPerAttribute) { + $values[] = [ + 'label' => 'option ' . $index, + 'value' => 'option_' . $index, + ]; + return $values; + }, + [] +); + +$options = []; +foreach ($data['options']['option'] as $optionData) { + $options[] = Bootstrap::getObjectManager()->create(AttributeOptionInterface::class) + ->setLabel($optionData['label']) + ->setValue($optionData['value']); +} + +$attribute = Bootstrap::getObjectManager()->create( + ProductAttributeInterface::class, + ['data' => $data] +); + +$attribute->setOptions($options); +$attributeRepository->save($attribute); + +/* Assign attribute to attribute set */ +$installer->addAttributeToGroup('catalog_product', 'Default', 'General', $attribute->getId()); diff --git a/dev/tests/integration/testsuite/Magento/Swatches/_files/text_swatch_attribute_rollback.php b/dev/tests/integration/testsuite/Magento/Swatches/_files/text_swatch_attribute_rollback.php new file mode 100644 index 0000000000000..3b1810766c915 --- /dev/null +++ b/dev/tests/integration/testsuite/Magento/Swatches/_files/text_swatch_attribute_rollback.php @@ -0,0 +1,23 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +/** @var \Magento\Framework\Registry $registry */ +$registry = \Magento\TestFramework\Helper\Bootstrap::getObjectManager()->get(\Magento\Framework\Registry::class); + +$registry->unregister('isSecureArea'); +$registry->register('isSecureArea', true); + +/** @var \Magento\Catalog\Model\ResourceModel\Eav\Attribute $attribute */ +$attribute = \Magento\TestFramework\Helper\Bootstrap::getObjectManager() + ->create(\Magento\Catalog\Model\ResourceModel\Eav\Attribute::class); + +$attribute->loadByCode(4, 'test_configurable'); + +if ($attribute->getId()) { + $attribute->delete(); +} + +$registry->unregister('isSecureArea'); +$registry->register('isSecureArea', false); diff --git a/dev/tests/integration/testsuite/Magento/Swatches/_files/visual_swatch_attribute_with_different_options_type.php b/dev/tests/integration/testsuite/Magento/Swatches/_files/visual_swatch_attribute_with_different_options_type.php new file mode 100644 index 0000000000000..8d2b427d7f7f3 --- /dev/null +++ b/dev/tests/integration/testsuite/Magento/Swatches/_files/visual_swatch_attribute_with_different_options_type.php @@ -0,0 +1,116 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +declare(strict_types=1); + +use Magento\Catalog\Api\Data\ProductAttributeInterface; +use Magento\Catalog\Setup\CategorySetup; +use Magento\Eav\Api\AttributeRepositoryInterface; +use Magento\Eav\Api\Data\AttributeOptionInterface; +use Magento\Setup\Fixtures\ImagesGenerator\ImagesGenerator; +use Magento\Swatches\Helper\Media as SwatchesMedia; +use Magento\Swatches\Model\Swatch; +use Magento\TestFramework\Helper\Bootstrap; + +/** @var $installer CategorySetup */ +$installer = Bootstrap::getObjectManager()->create(\Magento\Catalog\Setup\CategorySetup::class); +/** @var AttributeRepositoryInterface $attributeRepository */ +$attributeRepository = Bootstrap::getObjectManager()->create(AttributeRepositoryInterface::class); + +// Generate swatch image +/** @var ImagesGenerator $imagesGenerator */ +$imagesGenerator = Bootstrap::getObjectManager()->get(ImagesGenerator::class); +/** @var SwatchesMedia $swatchesMedia */ +$swatchesMedia = Bootstrap::getObjectManager()->get(SwatchesMedia::class); +$imageName = '/visual_swatch_attribute_option_type_image.jpg'; +$imagesGenerator->generate([ + 'image-width' => 110, + 'image-height' => 90, + 'image-name' => $imageName, +]); +$imagePath = substr($swatchesMedia->moveImageFromTmp($imageName), 1); +$swatchesMedia->generateSwatchVariations($imagePath); + +// Add attribute data +$data = [ + 'attribute_code' => 'test_configurable', + 'entity_type_id' => $installer->getEntityTypeId('catalog_product'), + 'is_global' => 1, + 'is_user_defined' => 1, + 'frontend_input' => 'select', + 'is_unique' => 0, + 'is_required' => 0, + 'is_searchable' => 0, + 'is_visible_in_advanced_search' => 0, + 'is_comparable' => 0, + 'is_filterable' => 0, + 'is_filterable_in_search' => 0, + 'is_used_for_promo_rules' => 0, + 'is_html_allowed_on_front' => 1, + 'is_visible_on_front' => 0, + 'used_in_product_listing' => 0, + 'used_for_sort_by' => 0, + 'frontend_label' => ['Test Configurable'], + 'backend_type' => 'int', +]; + +$optionsPerAttribute = 3; + +$data['frontend_input'] = 'select'; +$data['swatch_input_type'] = Swatch::SWATCH_INPUT_TYPE_VISUAL; + +$data['swatchvisual']['value'] = [ + 'option_1' => '#000000', // HEX color (color type) + 'option_2' => $imagePath, // image path (image type) + 'option_3' => null, // null (empty type) +]; + +$data['optionvisual']['value'] = array_reduce( + range(1, $optionsPerAttribute), + function ($values, $index) use ($optionsPerAttribute) { + $values['option_' . $index] = ['option ' . $index]; + return $values; + }, + [] +); + +$data['optionvisual']['order'] = array_reduce( + range(1, $optionsPerAttribute), + function ($values, $index) use ($optionsPerAttribute) { + $values['option_' . $index] = $index; + return $values; + }, + [] +); + +$data['options']['option'] = array_reduce( + range(1, $optionsPerAttribute), + function ($values, $index) use ($optionsPerAttribute) { + $values[] = [ + 'label' => 'option ' . $index, + 'value' => 'option_' . $index, + ]; + return $values; + }, + [] +); + +$options = []; +foreach ($data['options']['option'] as $optionData) { + $options[] = Bootstrap::getObjectManager()->create(AttributeOptionInterface::class) + ->setLabel($optionData['label']) + ->setValue($optionData['value']); +} + +$attribute = Bootstrap::getObjectManager()->create( + ProductAttributeInterface::class, + ['data' => $data] +); + +$attribute->setOptions($options); +$attributeRepository->save($attribute); + +/* Assign attribute to attribute set */ +$installer->addAttributeToGroup('catalog_product', 'Default', 'General', $attribute->getId()); diff --git a/dev/tests/integration/testsuite/Magento/Swatches/_files/visual_swatch_attribute_with_different_options_type_rollback.php b/dev/tests/integration/testsuite/Magento/Swatches/_files/visual_swatch_attribute_with_different_options_type_rollback.php new file mode 100644 index 0000000000000..c480906619a4a --- /dev/null +++ b/dev/tests/integration/testsuite/Magento/Swatches/_files/visual_swatch_attribute_with_different_options_type_rollback.php @@ -0,0 +1,35 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +declare(strict_types=1); + +use Magento\Framework\App\Filesystem\DirectoryList; +use Magento\Framework\Filesystem; +use Magento\Framework\Filesystem\Directory\WriteInterface; +use Magento\Swatches\Helper\Media as SwatchesMedia; +use Magento\TestFramework\Helper\Bootstrap; + +/** @var WriteInterface $mediaDirectory */ +$mediaDirectory = Bootstrap::getObjectManager()->get(Filesystem::class) + ->getDirectoryWrite( + DirectoryList::MEDIA + ); + +/** @var SwatchesMedia $swatchesMedia */ +$swatchesMedia = Bootstrap::getObjectManager()->get(SwatchesMedia::class); + +$testImageName = 'visual_swatch_attribute_option_type_image.jpg'; +$testImageSwatchPath = $swatchesMedia->getAttributeSwatchPath($testImageName); +$mediaDirectory->delete($testImageSwatchPath); + +$imageConfig = $swatchesMedia->getImageConfig(); +$swatchTypes = ['swatch_image', 'swatch_thumb']; + +foreach ($swatchTypes as $swatchType) { + $absolutePath = $mediaDirectory->getAbsolutePath($swatchesMedia->getSwatchCachePath($swatchType)); + $swatchTypePath = $absolutePath . $swatchesMedia->getFolderNameSize($swatchType, $imageConfig) . + '/' . $testImageName; + $mediaDirectory->delete($swatchTypePath); +} diff --git a/dev/tests/integration/testsuite/Magento/Swatches/_files/visual_swatch_attribute_with_enabled_product_image_for_swatch.php b/dev/tests/integration/testsuite/Magento/Swatches/_files/visual_swatch_attribute_with_enabled_product_image_for_swatch.php new file mode 100644 index 0000000000000..0e171094516ba --- /dev/null +++ b/dev/tests/integration/testsuite/Magento/Swatches/_files/visual_swatch_attribute_with_enabled_product_image_for_swatch.php @@ -0,0 +1,35 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +declare(strict_types=1); + +use Magento\Catalog\Model\Product; + +require __DIR__ . '/visual_swatch_attribute_with_different_options_type.php'; +require __DIR__ . '/../../../Magento/ConfigurableProduct/_files/configurable_products.php'; +require __DIR__ . '/../../../Magento/Catalog/_files/product_image.php'; + +// set 'Product Image for Swatch' for attribute +$attribute->setData('use_product_image_for_swatch', 1); +$attributeRepository->save($attribute); + +// get first child and set image +$childrenProducts = $product->getTypeInstance()->getUsedProducts($product); +/** @var Product $firstChildSimpleProduct */ +$firstChildSimpleProduct = array_shift($childrenProducts); +$firstChildSimpleProduct + ->setImage('/m/a/magento_image.jpg') + ->setSmallImage('/m/a/magento_image.jpg') + ->setThumbnail('/m/a/magento_image.jpg') + ->setData('media_gallery', ['images' => [ + [ + 'file' => '/m/a/magento_image.jpg', + 'position' => 1, + 'label' => 'Image Alt Text', + 'disabled' => 0, + 'media_type' => 'image' + ], + ]]) + ->save(); diff --git a/dev/tests/integration/testsuite/Magento/Swatches/_files/visual_swatch_attribute_with_enabled_product_image_for_swatch_rollback.php b/dev/tests/integration/testsuite/Magento/Swatches/_files/visual_swatch_attribute_with_enabled_product_image_for_swatch_rollback.php new file mode 100644 index 0000000000000..85c02049a992a --- /dev/null +++ b/dev/tests/integration/testsuite/Magento/Swatches/_files/visual_swatch_attribute_with_enabled_product_image_for_swatch_rollback.php @@ -0,0 +1,10 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +declare(strict_types=1); + +require __DIR__ . '/visual_swatch_attribute_with_different_options_type_rollback.php'; +require __DIR__ . '/../../../Magento/ConfigurableProduct/_files/configurable_products_rollback.php'; +require __DIR__ . '/../../../Magento/Catalog/_files/product_image_rollback.php'; diff --git a/dev/tests/integration/testsuite/Magento/Tax/Model/Sales/Total/Quote/SubtotalTest.php b/dev/tests/integration/testsuite/Magento/Tax/Model/Sales/Total/Quote/SubtotalTest.php index ae1f475d43b71..5a2351f7a1660 100644 --- a/dev/tests/integration/testsuite/Magento/Tax/Model/Sales/Total/Quote/SubtotalTest.php +++ b/dev/tests/integration/testsuite/Magento/Tax/Model/Sales/Total/Quote/SubtotalTest.php @@ -67,110 +67,110 @@ protected function getCustomerById($id) * @magentoDataFixture Magento/Catalog/_files/products.php * @magentoConfigFixture current_store tax/calculation/algorithm UNIT_BASE_CALCULATION * @dataProvider collectUnitBasedDataProvider + * @param array $quoteItems + * @param array $expected + * @return void */ - public function testCollectUnitBased($expected) + public function testCollectUnitBased(array $quoteItems, array $expected): void { - $customerTaxClassId = $this->getCustomerTaxClassId(); - $fixtureCustomerId = 1; - /** @var \Magento\Customer\Model\Customer $customer */ - $customer = $this->objectManager->create(\Magento\Customer\Model\Customer::class)->load($fixtureCustomerId); - /** @var \Magento\Customer\Model\Group $customerGroup */ - $customerGroup = $this->objectManager->create( - \Magento\Customer\Model\Group::class - )->load( - 'custom_group', - 'customer_group_code' - ); - $customerGroup->setTaxClassId($customerTaxClassId)->save(); - $customer->setGroupId($customerGroup->getId())->save(); + $this->quote($quoteItems, $expected); + } + + public function collectUnitBasedDataProvider(): array + { + return [ + 'one_item' => [ + [ + [ + 'sku' => 'simple', + 'qty' => 2 + ], + ], + [ + [ + 'subtotal' => 20, + 'subtotal_incl_tax' => 21.5, + 'base_subtotal_total_incl_tax' => 21.5, + 'tax_amount' => 1.5, + 'discount_amount' => 0, + ], + [ + [ + 'tax_amount' => 1.5, + 'price' => 10, + 'price_incl_tax' => 10.75, + 'row_total' => 20, + 'row_total_incl_tax' => 21.5, + 'tax_percent' => 7.5, + ], + ], + ], + ], + ]; + } + /** + * @magentoDataFixture Magento/Customer/_files/customer.php + * @magentoDataFixture Magento/Customer/_files/customer_address.php + * @magentoDataFixture Magento/Tax/_files/tax_classes.php + * @magentoDataFixture Magento/Customer/_files/customer_group.php + * @magentoDataFixture Magento/Bundle/_files/product.php + * @magentoConfigFixture current_store tax/calculation/algorithm UNIT_BASE_CALCULATION + * @dataProvider collectUnitBasedBundleProductDataProvider + * @param array $quoteItems + * @param array $expected + * @return void + */ + public function testCollectUnitBasedBundleProduct(array $quoteItems, array $expected): void + { $productTaxClassId = $this->getProductTaxClassId(); /** @var \Magento\Catalog\Model\Product $product */ - $product = $this->productRepository->get('simple'); - $product->setTaxClassId($productTaxClassId)->save(); - - $quoteShippingAddressDataObject = $this->getShippingAddressDataObject($fixtureCustomerId); - - /** @var \Magento\Quote\Model\Quote\Address $quoteShippingAddress */ - $quoteShippingAddress = $this->objectManager->create(\Magento\Quote\Model\Quote\Address::class); - $quoteShippingAddress->importCustomerAddressData($quoteShippingAddressDataObject); - $quantity = 2; - - /** @var \Magento\Quote\Model\Quote $quote */ - $quote = $this->objectManager->create(\Magento\Quote\Model\Quote::class); - $quote->setStoreId( - 1 - )->setIsActive( - true - )->setIsMultiShipping( - false - )->assignCustomerWithAddressChange( - $this->getCustomerById($customer->getId()) - )->setShippingAddress( - $quoteShippingAddress - )->setBillingAddress( - $quoteShippingAddress - )->setCheckoutMethod( - $customer->getMode() - )->setPasswordHash( - $customer->encryptPassword($customer->getPassword()) - )->addProduct( - $product->load($product->getId()), - $quantity - ); - $address = $quote->getShippingAddress(); - /** @var \Magento\Quote\Model\ShippingAssignment $shippingAssignment */ - $shippingAssignment = $this->objectManager->create(\Magento\Quote\Model\ShippingAssignment::class); - $shipping = $this->objectManager->create(\Magento\Quote\Model\Shipping::class); - $shipping->setAddress($address); - $shippingAssignment->setShipping($shipping); - $shippingAssignment->setItems($address->getAllItems()); - /** @var \Magento\Quote\Model\Quote\Address\Total $total */ - $total = $this->objectManager->create(\Magento\Quote\Model\Quote\Address\Total::class); - /** @var \Magento\Quote\Model\Quote\Address\Total\Subtotal $addressSubtotalCollector */ - $addressSubtotalCollector = $this->objectManager->create( - \Magento\Quote\Model\Quote\Address\Total\Subtotal::class - ); - $addressSubtotalCollector->collect($quote, $shippingAssignment, $total); - - /** @var \Magento\Tax\Model\Sales\Total\Quote\Subtotal $subtotalCollector */ - $subtotalCollector = $this->objectManager->create(\Magento\Tax\Model\Sales\Total\Quote\Subtotal::class); - $subtotalCollector->collect($quote, $shippingAssignment, $total); - - $this->assertEquals($expected['subtotal'], $total->getSubtotal()); - $this->assertEquals($expected['subtotal'] + $expected['tax_amount'], $total->getSubtotalInclTax()); - $this->assertEquals($expected['subtotal'] + $expected['tax_amount'], $address->getBaseSubtotalTotalInclTax()); - $this->assertEquals($expected['discount_amount'], $total->getDiscountAmount()); - $items = $address->getAllItems(); - /** @var \Magento\Quote\Model\Quote\Address\Item $item */ - $item = $items[0]; - $this->assertEquals($expected['items'][0]['price'], $item->getPrice()); - $this->assertEquals($expected['items'][0]['price_incl_tax'], $item->getPriceInclTax()); - $this->assertEquals($expected['items'][0]['row_total'], $item->getRowTotal()); - $this->assertEquals($expected['items'][0]['row_total_incl_tax'], $item->getRowTotalInclTax()); - $this->assertEquals($expected['items'][0]['tax_percent'], $item->getTaxPercent()); + $childProduct = $this->productRepository->get('simple'); + $childProduct->setTaxClassId($productTaxClassId)->save(); + /** @var \Magento\Catalog\Model\Product $product */ + $product = $this->productRepository->get('bundle-product'); + $product->setTaxClassId($productTaxClassId) + ->setPriceType(\Magento\Catalog\Model\Product\Type\AbstractType::CALCULATE_CHILD) + ->save(); + $quoteItems[0]['product'] = $product; + $this->quote($quoteItems, $expected); } - public function collectUnitBasedDataProvider() + public function collectUnitBasedBundleProductDataProvider(): array { return [ 'one_item' => [ [ - 'subtotal' => 20, - 'tax_amount' => 1.5, - 'discount_amount' => 0, - 'items' => [ + [ + 'sku' => 'bundle-product', + 'qty' => 2 + ], + ], + [ + [ + 'subtotal' => 20, + 'subtotal_incl_tax' => 21.5, + 'base_subtotal_total_incl_tax' => 21.5, + 'tax_amount' => 1.5, + 'discount_amount' => 0, + ], + [ [ 'tax_amount' => 1.5, 'price' => 10, 'price_incl_tax' => 10.75, 'row_total' => 20, 'row_total_incl_tax' => 21.5, - 'taxable_amount' => 10, - 'code' => 'simple', - 'type' => 'product', - 'tax_percent' => 7.5, + 'tax_percent' => null, ], + [ + 'tax_amount' => 1.5, + 'price' => 10, + 'price_incl_tax' => 10.75, + 'row_total' => 20, + 'row_total_incl_tax' => 21.5, + 'tax_percent' => 7.5, + ] ], ], ], @@ -178,16 +178,96 @@ public function collectUnitBasedDataProvider() } /** - * @magentoDbIsolation disabled + * @magentoAppIsolation enabled + * @magentoDbIsolation enabled + * @magentoConfigFixture current_store tax/calculation/cross_border_trade_enabled 1 * @magentoDataFixture Magento/Customer/_files/customer.php * @magentoDataFixture Magento/Customer/_files/customer_address.php * @magentoDataFixture Magento/Tax/_files/tax_classes.php * @magentoDataFixture Magento/Customer/_files/customer_group.php - * @magentoDataFixture Magento/Bundle/_files/product.php + * @magentoDataFixture Magento/Catalog/_files/products.php * @magentoConfigFixture current_store tax/calculation/algorithm UNIT_BASE_CALCULATION - * @dataProvider collectUnitBasedDataProvider + * @magentoConfigFixture current_store tax/calculation/price_includes_tax 1 + * @dataProvider collectUnitBasedPriceIncludesTaxDataProvider + * @param array $quoteItems + * @param array $expected + */ + public function testCollectUnitBasedPriceIncludesTax(array $quoteItems, array $expected): void + { + $this->quote($quoteItems, $expected); + } + + /** + * @return array + */ + public function collectUnitBasedPriceIncludesTaxDataProvider(): array + { + return [ + [ + [ + [ + 'sku' => 'simple', + 'qty' => 1 + ], + ], + [ + [ + 'subtotal' => 9.3, + 'subtotal_incl_tax' => 10, + 'base_subtotal_total_incl_tax' => 10, + 'tax_amount' => 0.7, + 'discount_amount' => 0, + ], + [ + [ + 'tax_amount' => 0.7, + 'price' => 9.3, + 'price_incl_tax' => 10, + 'row_total' => 9.3, + 'row_total_incl_tax' => 10, + 'tax_percent' => 7.5, + ], + ], + ], + ], + [ + [ + [ + 'sku' => 'simple', + 'qty' => 2 + ], + ], + [ + [ + 'subtotal' => 18.6, + 'subtotal_incl_tax' => 20, + 'base_subtotal_total_incl_tax' => 20, + 'tax_amount' => 1.4, + 'discount_amount' => 0, + ], + [ + [ + 'tax_amount' => 1.4, + 'price' => 9.3, + 'price_incl_tax' => 10, + 'row_total' => 18.6, + 'row_total_incl_tax' => 20, + 'tax_percent' => 7.5, + ], + ], + ], + ], + ]; + } + + /** + * Create quote and assert totals values + * + * @param array $quoteItems + * @param array $expected + * @return void */ - public function testCollectUnitBasedBundleProduct($expected) + private function quote(array $quoteItems, array $expected): void { $customerTaxClassId = $this->getCustomerTaxClassId(); $fixtureCustomerId = 1; @@ -202,23 +282,14 @@ public function testCollectUnitBasedBundleProduct($expected) ); $customerGroup->setTaxClassId($customerTaxClassId)->save(); $customer->setGroupId($customerGroup->getId())->save(); - $productTaxClassId = $this->getProductTaxClassId(); - /** @var \Magento\Catalog\Model\Product $product */ - $childProduct = $this->productRepository->get('simple'); - $childProduct->setTaxClassId($productTaxClassId)->save(); - /** @var \Magento\Catalog\Model\Product $product */ - $product = $this->productRepository->get('bundle-product'); - $product->setTaxClassId($productTaxClassId) - ->setPriceType(\Magento\Catalog\Model\Product\Type\AbstractType::CALCULATE_CHILD) - ->save(); + $quoteShippingAddressDataObject = $this->getShippingAddressDataObject($fixtureCustomerId); /** @var \Magento\Quote\Model\Quote\Address $quoteShippingAddress */ $quoteShippingAddress = $this->objectManager->create(\Magento\Quote\Model\Quote\Address::class); $quoteShippingAddress->importCustomerAddressData($quoteShippingAddressDataObject); - $quantity = 2; /** @var \Magento\Quote\Model\Quote $quote */ $quote = $this->objectManager->create(\Magento\Quote\Model\Quote::class); @@ -238,17 +309,25 @@ public function testCollectUnitBasedBundleProduct($expected) $customer->getMode() )->setPasswordHash( $customer->encryptPassword($customer->getPassword()) - )->addProduct( - $product->load($product->getId()), - $quantity ); + + foreach ($quoteItems as $quoteItem) { + $product = $quoteItem['product'] ?? null; + if ($product === null) { + /** @var \Magento\Catalog\Model\Product $product */ + $product = $this->productRepository->get($quoteItem['sku'] ?? 'simple'); + $product->setTaxClassId($productTaxClassId)->save(); + } + $quote->addProduct($product, $quoteItem['qty']); + } + $address = $quote->getShippingAddress(); /** @var \Magento\Quote\Model\ShippingAssignment $shippingAssignment */ $shippingAssignment = $this->objectManager->create(\Magento\Quote\Model\ShippingAssignment::class); $shipping = $this->objectManager->create(\Magento\Quote\Model\Shipping::class); $shipping->setAddress($address); $shippingAssignment->setShipping($shipping); - $shippingAssignment->setItems($quote->getAllItems()); + $shippingAssignment->setItems($address->getAllItems()); /** @var \Magento\Quote\Model\Quote\Address\Total $total */ $total = $this->objectManager->create(\Magento\Quote\Model\Quote\Address\Total::class); /** @var \Magento\Quote\Model\Quote\Address\Total\Subtotal $addressSubtotalCollector */ @@ -261,16 +340,20 @@ public function testCollectUnitBasedBundleProduct($expected) $subtotalCollector = $this->objectManager->create(\Magento\Tax\Model\Sales\Total\Quote\Subtotal::class); $subtotalCollector->collect($quote, $shippingAssignment, $total); - $this->assertEquals($expected['subtotal'], $total->getSubtotal()); - $this->assertEquals($expected['subtotal'] + $expected['tax_amount'], $total->getSubtotalInclTax()); - $this->assertEquals($expected['discount_amount'], $total->getDiscountAmount()); - $items = $address->getAllItems(); - /** @var \Magento\Quote\Model\Quote\Address\Item $item */ - $item = $items[0]; - $this->assertEquals($expected['items'][0]['price'], $item->getPrice()); - $this->assertEquals($expected['items'][0]['price_incl_tax'], $item->getPriceInclTax()); - $this->assertEquals($expected['items'][0]['row_total'], $item->getRowTotal()); - $this->assertEquals($expected['items'][0]['row_total_incl_tax'], $item->getRowTotalInclTax()); + $this->assertEquals($address->getSubtotal(), $total->getSubtotal()); + $this->assertEquals($address->getBaseSubtotal(), $total->getBaseSubtotal()); + $this->assertEquals($address->getBaseSubtotalTotalInclTax(), $total->getBaseSubtotalTotalInclTax()); + + $this->assertEquals($expected[0], $total->toArray(array_keys($expected[0]))); + $actualAddressItemsData = []; + if ($expected[1]) { + $keys = array_keys($expected[1][0]); + /** @var \Magento\Quote\Model\Quote\Address\Item $addressItem */ + foreach ($address->getAllItems() as $addressItem) { + $actualAddressItemsData[] = array_intersect_key($addressItem->toArray($keys), array_flip($keys)); + } + } + $this->assertEquals($expected[1], $actualAddressItemsData); } /** diff --git a/dev/tests/integration/testsuite/Magento/Tax/_files/tax_classes_de.php b/dev/tests/integration/testsuite/Magento/Tax/_files/tax_classes_de.php new file mode 100644 index 0000000000000..d2115e4488b14 --- /dev/null +++ b/dev/tests/integration/testsuite/Magento/Tax/_files/tax_classes_de.php @@ -0,0 +1,58 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +declare(strict_types=1); + +use Magento\Tax\Api\TaxClassManagementInterface; +use Magento\TestFramework\Helper\Bootstrap; +use Magento\Framework\ObjectManagerInterface; +use Magento\Tax\Api\TaxClassRepositoryInterface; +use Magento\Tax\Api\TaxRuleRepositoryInterface; +use Magento\Tax\Api\Data\TaxClassInterfaceFactory; +use Magento\Tax\Api\Data\TaxClassInterface; +use Magento\Tax\Api\Data\TaxRateInterfaceFactory; +use Magento\Tax\Api\Data\TaxRateInterface; +use Magento\Tax\Api\TaxRateRepositoryInterface; +use Magento\Tax\Api\Data\TaxRuleInterfaceFactory; +use Magento\Tax\Api\Data\TaxRuleInterface; + +/** @var ObjectManagerInterface $objectManager */ +$objectManager = Bootstrap::getObjectManager(); +/** @var TaxClassRepositoryInterface $taxClassRepository */ +$taxClassRepository = $objectManager->get(TaxClassRepositoryInterface::class); +$taxClassFactory = $objectManager->get(TaxClassInterfaceFactory::class); +/** @var TaxClassInterface $taxClassDataObject */ +$taxClassDataObject = $taxClassFactory->create(); +$taxClassDataObject->setClassName('CustomerTaxClass') + ->setClassType(TaxClassManagementInterface::TYPE_CUSTOMER); +$taxCustomerClassId = $taxClassRepository->save($taxClassDataObject); +$taxClassDataObject = $taxClassFactory->create(); +$taxClassDataObject->setClassName('ProductTaxClass') + ->setClassType(TaxClassManagementInterface::TYPE_PRODUCT); +$taxProductClassId = $taxClassRepository->save($taxClassDataObject); + +$taxRateFactory = $objectManager->get(TaxRateInterfaceFactory::class); +/** @var TaxRateInterface $taxRate */ +$taxRate = $taxRateFactory->create(); +$taxRate->setTaxCountryId('DE') + ->setTaxRegionId(0) + ->setTaxPostcode('*') + ->setCode('Denmark') + ->setRate('21'); +/** @var TaxRateRepositoryInterface $taxRateRepository */ +$taxRateRepository = $objectManager->get(TaxRateRepositoryInterface::class); +$taxRate = $taxRateRepository->save($taxRate); + +/** @var TaxRuleRepositoryInterface $taxRuleRepository */ +$taxRuleRepository = $objectManager->get(TaxRuleRepositoryInterface::class); +$taxRuleFactory = $objectManager->get(TaxRuleInterfaceFactory::class); +/** @var TaxRuleInterface $taxRule */ +$taxRule = $taxRuleFactory->create(); +$taxRule->setCode('Test Rule') + ->setCustomerTaxClassIds([$taxCustomerClassId]) + ->setProductTaxClassIds([$taxProductClassId]) + ->setTaxRateIds([$taxRate->getId()]) + ->setPriority(0); +$taxRuleRepository->save($taxRule); diff --git a/dev/tests/integration/testsuite/Magento/Tax/_files/tax_classes_de_rollback.php b/dev/tests/integration/testsuite/Magento/Tax/_files/tax_classes_de_rollback.php new file mode 100644 index 0000000000000..a87a031ee78a6 --- /dev/null +++ b/dev/tests/integration/testsuite/Magento/Tax/_files/tax_classes_de_rollback.php @@ -0,0 +1,64 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +declare(strict_types=1); + +use Magento\Framework\Api\SearchCriteriaBuilder; +use Magento\Tax\Model\ClassModel; +use Magento\TestFramework\Helper\Bootstrap; +use Magento\Framework\ObjectManagerInterface; +use Magento\Tax\Api\TaxClassRepositoryInterface; +use Magento\Tax\Api\TaxRuleRepositoryInterface; +use Magento\Framework\Exception\NoSuchEntityException; +use Magento\Tax\Model\Calculation\Rule; +use Magento\Tax\Model\Calculation\Rate; +use Magento\Tax\Api\TaxRateRepositoryInterface; + +/** @var ObjectManagerInterface $objectManager */ +$objectManager = Bootstrap::getObjectManager(); +$taxClasses = [ + 'CustomerTaxClass', + 'ProductTaxClass', +]; +$taxRuleRepository = $objectManager->get(TaxRuleRepositoryInterface::class); +/** @var SearchCriteriaBuilder $searchBuilder */ +$searchBuilder = $objectManager->get(SearchCriteriaBuilder::class); +$searchCriteria = $searchBuilder->addFilter(Rule::KEY_CODE, 'Test Rule') + ->create(); +$taxRules = $taxRuleRepository->getList($searchCriteria) + ->getItems(); +foreach ($taxRules as $taxRule) { + try { + $taxRuleRepository->delete($taxRule); + } catch (NoSuchEntityException $exception) { + //Rule already removed + } +} +$searchCriteria = $searchBuilder->addFilter(ClassModel::KEY_NAME, $taxClasses, 'in') + ->create(); +/** @var TaxClassRepositoryInterface $groupRepository */ +$taxClassRepository = $objectManager->get(TaxClassRepositoryInterface::class); +$taxClasses = $taxClassRepository->getList($searchCriteria) + ->getItems(); +foreach ($taxClasses as $taxClass) { + try { + $taxClassRepository->delete($taxClass); + } catch (NoSuchEntityException $exception) { + //TaxClass already removed + } +} +$searchCriteria = $searchBuilder->addFilter(Rate::KEY_CODE, 'Denmark') + ->create(); +/** @var TaxRateRepositoryInterface $groupRepository */ +$taxRateRepository = $objectManager->get(TaxRateRepositoryInterface::class); +$taxRates = $taxRateRepository->getList($searchCriteria) + ->getItems(); +foreach ($taxRates as $taxRate) { + try { + $taxRateRepository->delete($taxRate); + } catch (NoSuchEntityException $exception) { + //TaxRate already removed + } +} diff --git a/dev/tests/integration/testsuite/Magento/Test/Integrity/Modular/_files/skip_blocks_ce.php b/dev/tests/integration/testsuite/Magento/Test/Integrity/Modular/_files/skip_blocks_ce.php index 229207aa03899..7a8f04d0160ff 100644 --- a/dev/tests/integration/testsuite/Magento/Test/Integrity/Modular/_files/skip_blocks_ce.php +++ b/dev/tests/integration/testsuite/Magento/Test/Integrity/Modular/_files/skip_blocks_ce.php @@ -20,5 +20,6 @@ // Fails because of dependence on registry \Magento\Reminder\Block\Adminhtml\Reminder\Edit\Tab\Customers::class, \Magento\LayeredNavigation\Block\Navigation::class, - \Magento\LayeredNavigation\Block\Navigation\State::class + \Magento\LayeredNavigation\Block\Navigation\State::class, + \Magento\Paypal\Block\Express\InContext\Minicart\Button::class, ]; diff --git a/dev/tests/integration/testsuite/Magento/Test/Integrity/Modular/_files/skip_template_blocks_ce.php b/dev/tests/integration/testsuite/Magento/Test/Integrity/Modular/_files/skip_template_blocks_ce.php index 91d74b0908078..1edb23738820f 100644 --- a/dev/tests/integration/testsuite/Magento/Test/Integrity/Modular/_files/skip_template_blocks_ce.php +++ b/dev/tests/integration/testsuite/Magento/Test/Integrity/Modular/_files/skip_template_blocks_ce.php @@ -12,4 +12,5 @@ \Magento\Reminder\Block\Adminhtml\Reminder\Edit\Tab\Customers::class, \Magento\LayeredNavigation\Block\Navigation::class, \Magento\LayeredNavigation\Block\Navigation\State::class, + \Magento\Paypal\Block\Express\InContext\Minicart\Button::class, ]; diff --git a/dev/tests/integration/testsuite/Magento/Theme/Model/_files/design/adminhtml/Vendor/test/registration.php b/dev/tests/integration/testsuite/Magento/Theme/Model/_files/design/adminhtml/Vendor/test/registration.php index e1e5021b46204..f2dad036d5265 100644 --- a/dev/tests/integration/testsuite/Magento/Theme/Model/_files/design/adminhtml/Vendor/test/registration.php +++ b/dev/tests/integration/testsuite/Magento/Theme/Model/_files/design/adminhtml/Vendor/test/registration.php @@ -4,6 +4,6 @@ * See COPYING.txt for license details. */ -use \Magento\Framework\Component\ComponentRegistrar; +use Magento\Framework\Component\ComponentRegistrar; ComponentRegistrar::register(ComponentRegistrar::THEME, 'adminhtml/FrameworkThemeTest/test', __DIR__); diff --git a/dev/tests/integration/testsuite/Magento/Theme/Model/_files/design/area_two/Vendor/theme_one/registration.php b/dev/tests/integration/testsuite/Magento/Theme/Model/_files/design/area_two/Vendor/theme_one/registration.php index 3499cf9743a4b..32f7321714c4d 100644 --- a/dev/tests/integration/testsuite/Magento/Theme/Model/_files/design/area_two/Vendor/theme_one/registration.php +++ b/dev/tests/integration/testsuite/Magento/Theme/Model/_files/design/area_two/Vendor/theme_one/registration.php @@ -4,6 +4,6 @@ * See COPYING.txt for license details. */ -use \Magento\Framework\Component\ComponentRegistrar; +use Magento\Framework\Component\ComponentRegistrar; ComponentRegistrar::register(ComponentRegistrar::THEME, 'area_two/FrameworkThemeTest/theme_one', __DIR__); diff --git a/dev/tests/integration/testsuite/Magento/Theme/Model/_files/design/design_area/Vendor/theme_one/registration.php b/dev/tests/integration/testsuite/Magento/Theme/Model/_files/design/design_area/Vendor/theme_one/registration.php index 29e5fa798ca7d..4c1c9297b7ccc 100644 --- a/dev/tests/integration/testsuite/Magento/Theme/Model/_files/design/design_area/Vendor/theme_one/registration.php +++ b/dev/tests/integration/testsuite/Magento/Theme/Model/_files/design/design_area/Vendor/theme_one/registration.php @@ -4,6 +4,6 @@ * See COPYING.txt for license details. */ -use \Magento\Framework\Component\ComponentRegistrar; +use Magento\Framework\Component\ComponentRegistrar; ComponentRegistrar::register(ComponentRegistrar::THEME, 'design_area/FrameworkThemeTest/theme_one', __DIR__); diff --git a/dev/tests/integration/testsuite/Magento/Theme/Model/_files/design/frontend/Magento/default/registration.php b/dev/tests/integration/testsuite/Magento/Theme/Model/_files/design/frontend/Magento/default/registration.php index f9c18ad1a2ca5..cc40b0c221176 100644 --- a/dev/tests/integration/testsuite/Magento/Theme/Model/_files/design/frontend/Magento/default/registration.php +++ b/dev/tests/integration/testsuite/Magento/Theme/Model/_files/design/frontend/Magento/default/registration.php @@ -4,6 +4,6 @@ * See COPYING.txt for license details. */ -use \Magento\Framework\Component\ComponentRegistrar; +use Magento\Framework\Component\ComponentRegistrar; ComponentRegistrar::register(ComponentRegistrar::THEME, 'frontend/Magento_FrameworkThemeTest/default', __DIR__); diff --git a/dev/tests/integration/testsuite/Magento/Theme/Model/_files/design/frontend/Magento/default_iphone/registration.php b/dev/tests/integration/testsuite/Magento/Theme/Model/_files/design/frontend/Magento/default_iphone/registration.php index 30cc438826189..713abc2dd5245 100644 --- a/dev/tests/integration/testsuite/Magento/Theme/Model/_files/design/frontend/Magento/default_iphone/registration.php +++ b/dev/tests/integration/testsuite/Magento/Theme/Model/_files/design/frontend/Magento/default_iphone/registration.php @@ -4,6 +4,6 @@ * See COPYING.txt for license details. */ -use \Magento\Framework\Component\ComponentRegistrar; +use Magento\Framework\Component\ComponentRegistrar; ComponentRegistrar::register(ComponentRegistrar::THEME, 'frontend/Magento_FrameworkThemeTest/default_iphone', __DIR__); diff --git a/dev/tests/integration/testsuite/Magento/Theme/Model/_files/design/frontend/Test/cache_test_theme/registration.php b/dev/tests/integration/testsuite/Magento/Theme/Model/_files/design/frontend/Test/cache_test_theme/registration.php index 40aba67cf4bda..db6a478fc65a4 100644 --- a/dev/tests/integration/testsuite/Magento/Theme/Model/_files/design/frontend/Test/cache_test_theme/registration.php +++ b/dev/tests/integration/testsuite/Magento/Theme/Model/_files/design/frontend/Test/cache_test_theme/registration.php @@ -4,6 +4,6 @@ * See COPYING.txt for license details. */ -use \Magento\Framework\Component\ComponentRegistrar; +use Magento\Framework\Component\ComponentRegistrar; ComponentRegistrar::register(ComponentRegistrar::THEME, 'frontend/Test_FrameworkThemeTest/cache_test_theme', __DIR__); diff --git a/dev/tests/integration/testsuite/Magento/Theme/Model/_files/design/frontend/Test/default/registration.php b/dev/tests/integration/testsuite/Magento/Theme/Model/_files/design/frontend/Test/default/registration.php index 3e6938955b6d7..3cfea7d3edf06 100644 --- a/dev/tests/integration/testsuite/Magento/Theme/Model/_files/design/frontend/Test/default/registration.php +++ b/dev/tests/integration/testsuite/Magento/Theme/Model/_files/design/frontend/Test/default/registration.php @@ -4,6 +4,6 @@ * See COPYING.txt for license details. */ -use \Magento\Framework\Component\ComponentRegistrar; +use Magento\Framework\Component\ComponentRegistrar; ComponentRegistrar::register(ComponentRegistrar::THEME, 'frontend/Test_FrameworkThemeTest/default', __DIR__); diff --git a/dev/tests/integration/testsuite/Magento/Theme/Model/_files/design/frontend/Test/publication/registration.php b/dev/tests/integration/testsuite/Magento/Theme/Model/_files/design/frontend/Test/publication/registration.php index f3668dd5ec603..f59384e42f41b 100644 --- a/dev/tests/integration/testsuite/Magento/Theme/Model/_files/design/frontend/Test/publication/registration.php +++ b/dev/tests/integration/testsuite/Magento/Theme/Model/_files/design/frontend/Test/publication/registration.php @@ -4,6 +4,6 @@ * See COPYING.txt for license details. */ -use \Magento\Framework\Component\ComponentRegistrar; +use Magento\Framework\Component\ComponentRegistrar; ComponentRegistrar::register(ComponentRegistrar::THEME, 'frontend/Test_FrameworkThemeTest/publication', __DIR__); diff --git a/dev/tests/integration/testsuite/Magento/Theme/Model/_files/design/frontend/Test/test_theme/registration.php b/dev/tests/integration/testsuite/Magento/Theme/Model/_files/design/frontend/Test/test_theme/registration.php index adcbacd78594c..0a1c9e5ef8e79 100644 --- a/dev/tests/integration/testsuite/Magento/Theme/Model/_files/design/frontend/Test/test_theme/registration.php +++ b/dev/tests/integration/testsuite/Magento/Theme/Model/_files/design/frontend/Test/test_theme/registration.php @@ -4,6 +4,6 @@ * See COPYING.txt for license details. */ -use \Magento\Framework\Component\ComponentRegistrar; +use Magento\Framework\Component\ComponentRegistrar; ComponentRegistrar::register(ComponentRegistrar::THEME, 'frontend/Test_FrameworkThemeTest/test_theme', __DIR__); diff --git a/dev/tests/integration/testsuite/Magento/Theme/Model/_files/design/frontend/Vendor/custom_theme/registration.php b/dev/tests/integration/testsuite/Magento/Theme/Model/_files/design/frontend/Vendor/custom_theme/registration.php index e1ce31a7c8d0b..b0e80d1106d22 100644 --- a/dev/tests/integration/testsuite/Magento/Theme/Model/_files/design/frontend/Vendor/custom_theme/registration.php +++ b/dev/tests/integration/testsuite/Magento/Theme/Model/_files/design/frontend/Vendor/custom_theme/registration.php @@ -4,6 +4,6 @@ * See COPYING.txt for license details. */ -use \Magento\Framework\Component\ComponentRegistrar; +use Magento\Framework\Component\ComponentRegistrar; ComponentRegistrar::register(ComponentRegistrar::THEME, 'frontend/Vendor_FrameworkThemeTest/custom_theme', __DIR__); diff --git a/dev/tests/integration/testsuite/Magento/Theme/Model/_files/design/frontend/Vendor/default/registration.php b/dev/tests/integration/testsuite/Magento/Theme/Model/_files/design/frontend/Vendor/default/registration.php index f2e0918421ba0..1c2d96475f9fc 100644 --- a/dev/tests/integration/testsuite/Magento/Theme/Model/_files/design/frontend/Vendor/default/registration.php +++ b/dev/tests/integration/testsuite/Magento/Theme/Model/_files/design/frontend/Vendor/default/registration.php @@ -4,6 +4,6 @@ * See COPYING.txt for license details. */ -use \Magento\Framework\Component\ComponentRegistrar; +use Magento\Framework\Component\ComponentRegistrar; ComponentRegistrar::register(ComponentRegistrar::THEME, 'frontend/Vendor_FrameworkThemeTest/default', __DIR__); diff --git a/dev/tests/integration/testsuite/Magento/Ui/Controller/Index/RenderTest.php b/dev/tests/integration/testsuite/Magento/Ui/Controller/Index/RenderTest.php index c63a6fe75f5fc..5873c0e1da53e 100644 --- a/dev/tests/integration/testsuite/Magento/Ui/Controller/Index/RenderTest.php +++ b/dev/tests/integration/testsuite/Magento/Ui/Controller/Index/RenderTest.php @@ -8,7 +8,7 @@ namespace Magento\Ui\Controller\Index; use Magento\TestFramework\TestCase\AbstractController; -use Zend\Http\Headers; +use Laminas\Http\Headers; /** * Test component rendering on storefront. diff --git a/dev/tests/integration/testsuite/Magento/UrlRewrite/Controller/UrlRewriteTest.php b/dev/tests/integration/testsuite/Magento/UrlRewrite/Controller/UrlRewriteTest.php index 5f8adc5d65113..867fcd4c2f338 100644 --- a/dev/tests/integration/testsuite/Magento/UrlRewrite/Controller/UrlRewriteTest.php +++ b/dev/tests/integration/testsuite/Magento/UrlRewrite/Controller/UrlRewriteTest.php @@ -44,8 +44,7 @@ public function testMatchUrlRewrite( $location = $response->getHeader('Location')->getFieldValue(); $this->assertStringEndsWith( $redirect, - $location, - 'Invalid location header' + $location ); } } @@ -80,6 +79,75 @@ public function requestDataProvider(): array 'request' => '/page-similar/', 'redirect' => '/page-b', ], + 'Use Case #7: Rewrite: page-similar --(301)--> page-a; ' + . 'Request: page-similar?param=1 --(301)--> page-a?param=1' => [ + 'request' => '/page-similar?param=1', + 'redirect' => '/page-a?param=1', + ], + 'Use Case #8: Rewrite: page-similar/ --(301)--> page-b; ' + . 'Request: page-similar/?param=1 --(301)--> page-b?param=1' => [ + 'request' => '/page-similar/?param=1', + 'redirect' => '/page-b?param=1', + ], + 'Use Case #9: Rewrite: page-similar-query-param --(301)--> page-d?param1=1;' + . 'Request: page-similar-query-param --(301)--> page-d?param1=1' => [ + 'request' => '/page-similar-query-param', + 'redirect' => '/page-d?param1=1', + ], + 'Use Case #10: Rewrite: page-similar-query-param --(301)--> page-d?param1=1; ' + . 'Request: page-similar-query-param?param2=1 --(301)--> page-d?param1=1¶m2=1' => [ + 'request' => '/page-similar-query-param?param2=1', + 'redirect' => '/page-d?param1=1¶m2=1', + ], + 'Use Case #11: Rewrite: page-similar-query-param/ --(301)--> page-e?param1=1; ' + . 'Request: page-similar-query-param/ --(301)--> page-e?param1=1' => [ + 'request' => '/page-similar-query-param/', + 'redirect' => '/page-e?param1=1', + ], + 'Use Case #12: Rewrite: page-similar-query-param/ --(301)--> page-e?param1=1;' + . 'Request: page-similar-query-param/?param2=1 --(301)--> page-e?param1=1¶m2=1' => [ + 'request' => '/page-similar-query-param/?param2=1', + 'redirect' => '/page-e?param1=1¶m2=1', + ], + 'Use Case #13: Rewrite: page-external1 --(301)--> http://example.com/external;' + . 'Request: page-external1?param1=1 --(301)--> http://example.com/external (not fills get params)' => [ + 'request' => '/page-external1?param1=1', + 'redirect' => 'http://example.com/external', + ], + 'Use Case #14: Rewrite: page-external2/ --(301)--> https://example.com/external2/;' + . 'Request: page-external2?param2=1 --(301)--> https://example.com/external2/ (not fills get params)' => [ + 'request' => '/page-external2?param2=1', + 'redirect' => 'https://example.com/external2/', + ], + 'Use Case #15: Rewrite: page-external3 --(301)--> http://example.com/external?param1=value1;' + . 'Request: page-external3?param1=custom1¶m2=custom2 --(301)--> ' + . 'http://example.com/external?param1=value1' + . ' (fills get param from target path)' => [ + 'request' => '/page-external3?param1=custom1¶m2=custom2', + 'redirect' => 'http://example.com/external?param1=value1', + ], + 'Use Case #16: Rewrite: page-external4/ --(301)--> https://example.com/external2/?param2=value2;' + . 'Request: page-external4?param1=custom1¶m2=custom2 --(301)--> ' + . 'https://example.com/external2/?param2=value2 ' + . ' (fills get param from target path)' => [ + 'request' => '/page-external4?param1=custom1¶m2=custom2', + 'redirect' => 'https://example.com/external2/?param2=value2', + ], + 'Use Case #17: Rewrite: / --(301)--> /; No redirect' => [ + 'request' => '/', + 'redirect' => '/', + 'expectedCode' => HttpResponse::STATUS_CODE_200, + ], + 'Use Case #18: Rewrite: contact/ --(301)--> contact?param1=1; ' + . 'Request: contact/ --(301)--> contact?param1=1' => [ + 'request' => 'contact/', + 'redirect' => 'contact?param1=1', + ], + 'Use Case #19: Rewrite: contact/?param2=2 --(301)--> contact?param1=1¶m2=2; ' + . 'Request: contact/?¶m2=2 --(301)--> contact?param1=1¶m2=2' => [ + 'request' => 'contact/?¶m2=2', + 'redirect' => 'contact?param1=1¶m2=2', + ], ]; } } diff --git a/dev/tests/integration/testsuite/Magento/UrlRewrite/Model/StoreSwitcher/RewriteUrlTest.php b/dev/tests/integration/testsuite/Magento/UrlRewrite/Model/StoreSwitcher/RewriteUrlTest.php index 317d26abd3370..7e10eb8766aa1 100644 --- a/dev/tests/integration/testsuite/Magento/UrlRewrite/Model/StoreSwitcher/RewriteUrlTest.php +++ b/dev/tests/integration/testsuite/Magento/UrlRewrite/Model/StoreSwitcher/RewriteUrlTest.php @@ -11,12 +11,10 @@ use Magento\Customer\Api\CustomerRepositoryInterface; use Magento\Customer\Api\Data\CustomerInterface; use Magento\Customer\Model\Session; -use Magento\Framework\App\ActionInterface; use Magento\Framework\App\Config\ReinitableConfigInterface; use Magento\Framework\App\Config\Value; use Magento\Framework\Exception\NoSuchEntityException; use Magento\Framework\ObjectManagerInterface as ObjectManager; -use Magento\Framework\Url\DecoderInterface; use Magento\Store\Api\Data\StoreInterface; use Magento\Store\Api\StoreRepositoryInterface; use Magento\Store\Model\ScopeInterface; @@ -146,13 +144,9 @@ public function testSwitchCmsPageToAnotherStoreAsCustomer(): void $redirectUrl = "http://localhost/index.php/page-c/"; $expectedUrl = "http://localhost/index.php/page-c-on-2nd-store"; - /** @var DecoderInterface $decoder */ - $decoder = $this->objectManager->create(DecoderInterface::class); + $secureRedirectUrl = $this->storeSwitcher->switch($fromStore, $toStore, $redirectUrl); - parse_str(parse_url($secureRedirectUrl, PHP_URL_QUERY), $secureRedirectUrlQueryParams); - $encodedActualUrl = $secureRedirectUrlQueryParams[ActionInterface::PARAM_NAME_URL_ENCODED]; - $actualUrl = $decoder->decode($encodedActualUrl); - $this->assertEquals($expectedUrl, $actualUrl); + $this->assertEquals($expectedUrl, $secureRedirectUrl); } /** diff --git a/dev/tests/integration/testsuite/Magento/UrlRewrite/_files/url_rewrite.php b/dev/tests/integration/testsuite/Magento/UrlRewrite/_files/url_rewrite.php index 8e82aa853d4a4..62aa27562537a 100644 --- a/dev/tests/integration/testsuite/Magento/UrlRewrite/_files/url_rewrite.php +++ b/dev/tests/integration/testsuite/Magento/UrlRewrite/_files/url_rewrite.php @@ -64,6 +64,26 @@ ->setStores([$storeID, $secondStoreId]); $pageResource->save($page); +$page = $objectManager->create(Page::class); +$page->setTitle('Cms D') + ->setIdentifier('page-d') + ->setIsActive(1) + ->setContent('<h1>Cms Page D</h1>') + ->setPageLayout('1column') + ->setCustomTheme('Magento/blank') + ->setStores([$storeID, $secondStoreId]); +$pageResource->save($page); + +$page = $objectManager->create(Page::class); +$page->setTitle('Cms E') + ->setIdentifier('page-e') + ->setIsActive(1) + ->setContent('<h1>Cms Page E</h1>') + ->setPageLayout('1column') + ->setCustomTheme('Magento/blank') + ->setStores([$storeID, $secondStoreId]); +$pageResource->save($page); + $rewrite = $objectManager->create(UrlRewrite::class); $rewrite->setEntityType('custom') ->setRequestPath('page-one/') @@ -88,7 +108,7 @@ ->setTargetPath('page-a') ->setRedirectType(OptionProvider::PERMANENT) ->setStoreId($storeID) - ->setDescription('From age-similar without trailing slash to page-a'); + ->setDescription('From page-similar without trailing slash to page-a'); $rewriteResource->save($rewrite); $rewrite = $objectManager->create(UrlRewrite::class); @@ -97,7 +117,7 @@ ->setTargetPath('page-b') ->setRedirectType(OptionProvider::PERMANENT) ->setStoreId($storeID) - ->setDescription('From age-similar with trailing slash to page-b'); + ->setDescription('From page-similar with trailing slash to page-b'); $rewriteResource->save($rewrite); //Emulating auto-generated aliases (like the ones used for categories). @@ -117,3 +137,75 @@ ->setRedirectType(0) ->setStoreId($secondStoreId); $rewriteResource->save($rewrite); + +$rewrite = $objectManager->create(UrlRewrite::class); +$rewrite->setEntityType('custom') + ->setRequestPath('page-similar-query-param') + ->setTargetPath('page-d?param1=1') + ->setRedirectType(OptionProvider::PERMANENT) + ->setStoreId($storeID) + ->setDescription('From page-similar-query-param to page-d with query param'); +$rewriteResource->save($rewrite); + +$rewrite = $objectManager->create(UrlRewrite::class); +$rewrite->setEntityType('custom') + ->setRequestPath('page-similar-query-param/') + ->setTargetPath('page-e?param1=1') + ->setRedirectType(OptionProvider::PERMANENT) + ->setStoreId($storeID) + ->setDescription('From page-similar-query-param with trailing slash to page-e with query param'); +$rewriteResource->save($rewrite); + +$rewrite = $objectManager->create(UrlRewrite::class); +$rewrite->setEntityType('custom') + ->setRequestPath('/') + ->setTargetPath('/') + ->setRedirectType(OptionProvider::PERMANENT) + ->setStoreId($storeID) + ->setDescription('From / to /'); +$rewriteResource->save($rewrite); + +$rewrite = $objectManager->create(UrlRewrite::class); +$rewrite->setEntityType('custom') + ->setRequestPath('contact/') + ->setTargetPath('contact?param1=1') + ->setRedirectType(OptionProvider::PERMANENT) + ->setStoreId($storeID) + ->setDescription('From contact with trailing slash to contact with query param'); +$rewriteResource->save($rewrite); + +$rewrite = $objectManager->create(UrlRewrite::class); +$rewrite->setEntityType('custom') + ->setRequestPath('page-external1') + ->setTargetPath('http://example.com/external') + ->setRedirectType(OptionProvider::PERMANENT) + ->setStoreId($storeID) + ->setDescription('From page-external to external URL'); +$rewriteResource->save($rewrite); + +$rewrite = $objectManager->create(UrlRewrite::class); +$rewrite->setEntityType('custom') + ->setRequestPath('page-external2/') + ->setTargetPath('https://example.com/external2/') + ->setRedirectType(OptionProvider::PERMANENT) + ->setStoreId($storeID) + ->setDescription('From page-external with trailing slash to external URL'); +$rewriteResource->save($rewrite); + +$rewrite = $objectManager->create(UrlRewrite::class); +$rewrite->setEntityType('custom') + ->setRequestPath('page-external3') + ->setTargetPath('http://example.com/external?param1=value1') + ->setRedirectType(OptionProvider::PERMANENT) + ->setStoreId($storeID) + ->setDescription('From page-external to external URL'); +$rewriteResource->save($rewrite); + +$rewrite = $objectManager->create(UrlRewrite::class); +$rewrite->setEntityType('custom') + ->setRequestPath('page-external4/') + ->setTargetPath('https://example.com/external2/?param2=value2') + ->setRedirectType(OptionProvider::PERMANENT) + ->setStoreId($storeID) + ->setDescription('From page-external with trailing slash to external URL'); +$rewriteResource->save($rewrite); diff --git a/dev/tests/integration/testsuite/Magento/UrlRewrite/_files/url_rewrite_rollback.php b/dev/tests/integration/testsuite/Magento/UrlRewrite/_files/url_rewrite_rollback.php index 22d95751fbf26..4d2c148141943 100644 --- a/dev/tests/integration/testsuite/Magento/UrlRewrite/_files/url_rewrite_rollback.php +++ b/dev/tests/integration/testsuite/Magento/UrlRewrite/_files/url_rewrite_rollback.php @@ -19,6 +19,8 @@ $pageRepository->deleteById('page-a'); $pageRepository->deleteById('page-b'); $pageRepository->deleteById('page-c'); +$pageRepository->deleteById('page-d'); +$pageRepository->deleteById('page-e'); /** @var \Magento\Catalog\Api\ProductRepositoryInterface $productRepository */ $productRepository = \Magento\TestFramework\Helper\Bootstrap::getObjectManager() @@ -29,7 +31,23 @@ ->create(\Magento\UrlRewrite\Model\ResourceModel\UrlRewriteCollection::class); $collection = $urlRewriteCollection ->addFieldToFilter('entity_type', 'custom') - ->addFieldToFilter('target_path', ['page-a/', 'page-a', 'page-b', 'page-c']) + ->addFieldToFilter( + 'target_path', + [ + 'page-a/', + 'page-a', + 'page-b', + 'page-c', + 'page-d?param1=1', + 'page-e?param1=1', + 'http://example.com/external', + 'https://example.com/external2/', + 'http://example.com/external?param1=value1', + 'https://example.com/external2/?param2=value2', + '/', + 'contact?param1=1' + ] + ) ->load() ->walk('delete'); diff --git a/dev/tests/integration/testsuite/Magento/User/Model/ResourceModel/UserTest.php b/dev/tests/integration/testsuite/Magento/User/Model/ResourceModel/UserTest.php index 53a0b748c32f6..0eabe3a214ec2 100644 --- a/dev/tests/integration/testsuite/Magento/User/Model/ResourceModel/UserTest.php +++ b/dev/tests/integration/testsuite/Magento/User/Model/ResourceModel/UserTest.php @@ -3,40 +3,60 @@ * Copyright © Magento, Inc. All rights reserved. * See COPYING.txt for license details. */ +declare(strict_types=1); + namespace Magento\User\Model\ResourceModel; +use Magento\Authorization\Model\ResourceModel\Role\Collection as UserRoleCollection; +use Magento\Authorization\Model\ResourceModel\Role\CollectionFactory as UserRoleCollectionFactory; +use Magento\Authorization\Model\UserContextInterface; use Magento\TestFramework\Helper\Bootstrap; -use Magento\User\Model\User; use Magento\User\Model\ResourceModel\User as UserResourceModel; +use Magento\User\Model\User; +use Magento\User\Model\UserFactory; /** * @magentoAppArea adminhtml */ class UserTest extends \PHPUnit\Framework\TestCase { - /** @var UserResourceModel */ + /** + * @var UserResourceModel + */ private $model; + /** + * @var UserRoleCollectionFactory + */ + private $userRoleCollectionFactory; + + /** + * @var UserFactory + */ + private $userFactory; + + /** + * @inheritdoc + */ protected function setUp() { - $this->model = Bootstrap::getObjectManager()->get( - UserResourceModel::class - ); + $this->model = Bootstrap::getObjectManager()->get(UserResourceModel::class); + $this->userRoleCollectionFactory = Bootstrap::getObjectManager()->get(UserRoleCollectionFactory::class); + $this->userFactory = Bootstrap::getObjectManager()->get(UserFactory::class); } /** * Tests if latest password is stored after user creating * when password lifetime config value is zero (disabled as fact) * + * @return void * @magentoConfigFixture current_store admin/security/password_lifetime 0 * @magentoDataFixture Magento/User/_files/dummy_user.php */ - public function testGetLatestPasswordWhenZeroPasswordLifetime() + public function testGetLatestPasswordWhenZeroPasswordLifetime(): void { /** @var User $user */ - $user = Bootstrap::getObjectManager()->create( - User::class - ); + $user = $this->userFactory->create(); $user->loadByUsername('dummy_username'); $latestPassword = $this->model->getLatestPassword($user->getId()); @@ -46,12 +66,49 @@ public function testGetLatestPasswordWhenZeroPasswordLifetime() ); } - public function testCountAll() + /** + * Test that user role is not deleted after deleting empty user. + * + * @return void + */ + public function testDelete(): void + { + $this->checkRoleCollectionSize(); + /** @var User $user */ + $user = $this->userFactory->create(); + $this->model->delete($user); + $this->checkRoleCollectionSize(); + } + + /** + * Ensure that role collection size is correct. + * + * @return void + */ + private function checkRoleCollectionSize(): void + { + /** @var UserRoleCollection $roleCollection */ + $roleCollection = $this->userRoleCollectionFactory->create(); + $roleCollection->setUserFilter(0, UserContextInterface::USER_TYPE_ADMIN); + $this->assertEquals(1, $roleCollection->getSize()); + } + + /** + * Check total user count. + * + * @return void + */ + public function testCountAll(): void { $this->assertSame(1, $this->model->countAll()); } - public function testGetValidationRulesBeforeSave() + /** + * Check validation rules has correct type. + * + * @return void + */ + public function testGetValidationRulesBeforeSave(): void { $rules = $this->model->getValidationRulesBeforeSave(); $this->assertInstanceOf('Zend_Validate_Interface', $rules); diff --git a/dev/tests/integration/testsuite/Magento/Webapi/_files/webapi_user.php b/dev/tests/integration/testsuite/Magento/Webapi/_files/webapi_user.php index 63479bee9cf6d..50204998c7da3 100644 --- a/dev/tests/integration/testsuite/Magento/Webapi/_files/webapi_user.php +++ b/dev/tests/integration/testsuite/Magento/Webapi/_files/webapi_user.php @@ -15,7 +15,7 @@ ->create(Magento\Framework\App\ResourceConnection::class); $adapter = $connection->getConnection(); $select = $adapter->select() - ->from('authorization_role', ['role_id']) + ->from($connection->getTableName('authorization_role'), ['role_id']) ->where('role_name = ?', 'Administrators') ->where('parent_id = ?', 0) ->limit(1); diff --git a/dev/tests/integration/testsuite/Magento/Weee/Block/Product/View/Attribute/FixedProductTaxAttributeTest.php b/dev/tests/integration/testsuite/Magento/Weee/Block/Product/View/Attribute/FixedProductTaxAttributeTest.php index 6cb7d6dc0346d..a0aeb13f77518 100644 --- a/dev/tests/integration/testsuite/Magento/Weee/Block/Product/View/Attribute/FixedProductTaxAttributeTest.php +++ b/dev/tests/integration/testsuite/Magento/Weee/Block/Product/View/Attribute/FixedProductTaxAttributeTest.php @@ -11,11 +11,14 @@ use Magento\Catalog\Api\ProductRepositoryInterface; use Magento\Catalog\Block\Product\ListProduct; use Magento\Catalog\Pricing\Render as CatalogPricingRender; +use Magento\Customer\Api\CustomerRepositoryInterface; +use Magento\Customer\Model\Session; use Magento\Framework\ObjectManagerInterface; use Magento\Framework\Pricing\Render; use Magento\Framework\Pricing\Render\RendererPool; use Magento\Framework\Registry; use Magento\Framework\View\LayoutInterface; +use Magento\Store\Model\StoreManagerInterface; use Magento\TestFramework\Helper\Bootstrap; use PHPUnit\Framework\TestCase; @@ -27,20 +30,12 @@ * @magentoAppArea frontend * @magentoDataFixture Magento/Weee/_files/fixed_product_attribute.php * @magentoDataFixture Magento/Catalog/_files/second_product_simple.php + * @SuppressWarnings(PHPMD.CouplingBetweenObjects) */ class FixedProductTaxAttributeTest extends TestCase { - /** @var array */ - private const TEST_TAX_DATA = [ - [ - 'region_id' => '1', - 'country' => 'US', - 'val' => '', - 'value' => '5', - 'website_id' => '1', - 'state' => '', - ] - ]; + /** @var array */ + private $textTaxData; /** @var ObjectManagerInterface */ private $objectManager; @@ -60,6 +55,18 @@ class FixedProductTaxAttributeTest extends TestCase /** @var Registry */ private $registry; + /** @var StoreManagerInterface */ + private $storeManager; + + /** @var CustomerRepositoryInterface */ + private $customerRepository; + + /** @var Session */ + private $customerSession; + + /** @var int */ + private $baseWebsiteId; + /** * @inheritdoc */ @@ -73,6 +80,19 @@ protected function setUp() $this->productListBlock = $this->layout->createBlock(ListProduct::class); $this->attributeCode = 'fixed_product_attribute'; $this->registry = $this->objectManager->get(Registry::class); + $this->storeManager = $this->objectManager->get(StoreManagerInterface::class); + $this->customerRepository = $this->objectManager->create(CustomerRepositoryInterface::class); + $this->customerSession = $this->objectManager->get(Session::class); + $this->baseWebsiteId = (int) $this->storeManager->getWebsite('base')->getId(); + $this->textTaxData = [ + [ + 'country' => 'US', + 'val' => '', + 'value' => '5', + 'website_id' => $this->baseWebsiteId, + 'state' => '', + ] + ]; } /** @@ -81,6 +101,8 @@ protected function setUp() protected function tearDown() { $this->registry->unregister('product'); + $this->registry->unregister('current_product'); + $this->customerSession->logout(); parent::tearDown(); } @@ -88,11 +110,13 @@ protected function tearDown() /** * @magentoConfigFixture default_store tax/weee/enable 1 * @magentoConfigFixture default_store tax/weee/display_list 0 + * + * @return void */ public function testFPTCategoryPageIncludingFPTOnly(): void { $this->prepareLayoutCategoryPage(); - $product = $this->updateProduct('simple2', self::TEST_TAX_DATA); + $product = $this->updateProduct('simple2', $this->textTaxData); $productPrice = $this->productListBlock->getProductPrice($product); $this->assertEquals('$15.00', preg_replace('/\s+/', '', strip_tags($productPrice))); } @@ -100,11 +124,13 @@ public function testFPTCategoryPageIncludingFPTOnly(): void /** * @magentoConfigFixture default_store tax/weee/enable 1 * @magentoConfigFixture default_store tax/weee/display_list 1 + * + * @return void */ public function testFPTCategoryPageIncludingFPTAndDescription(): void { $this->prepareLayoutCategoryPage(); - $product = $this->updateProduct('simple2', self::TEST_TAX_DATA); + $product = $this->updateProduct('simple2', $this->textTaxData); $productPrice = $this->productListBlock->getProductPrice($product); $this->assertContains('data-label="fixed product tax"', $productPrice); $this->assertEquals('$15.00$5.00', preg_replace('/\s+/', '', strip_tags($productPrice))); @@ -113,11 +139,13 @@ public function testFPTCategoryPageIncludingFPTAndDescription(): void /** * @magentoConfigFixture default_store tax/weee/enable 1 * @magentoConfigFixture default_store tax/weee/display_list 2 + * + * @return void */ public function testFPTCategoryPageExcludingFPTIncludingDescriptionAndPrice(): void { $this->prepareLayoutCategoryPage(); - $product = $this->updateProduct('simple2', self::TEST_TAX_DATA); + $product = $this->updateProduct('simple2', $this->textTaxData); $productPrice = $this->productListBlock->getProductPrice($product); $this->assertContains('data-label="fixed product tax"', $productPrice); $this->assertEquals('$10.00$5.00$15.00', preg_replace('/\s+/', '', strip_tags($productPrice))); @@ -126,22 +154,26 @@ public function testFPTCategoryPageExcludingFPTIncludingDescriptionAndPrice(): v /** * @magentoConfigFixture default_store tax/weee/enable 1 * @magentoConfigFixture default_store tax/weee/display_list 3 + * + * @return void */ public function testFPTCategoryPageExcludingFPT(): void { $this->prepareLayoutCategoryPage(); - $product = $this->updateProduct('simple2', self::TEST_TAX_DATA); + $product = $this->updateProduct('simple2', $this->textTaxData); $productPrice = $this->productListBlock->getProductPrice($product); $this->assertEquals('$10.00', preg_replace('/\s+/', '', strip_tags($productPrice))); } /** * @magentoConfigFixture default_store tax/weee/enable 1 - * @magentoConfigFixture default_store tax/weee/display_list 0 + * @magentoConfigFixture default_store tax/weee/display 0 + * + * @return void */ public function testFPTProductPageIncludingFPTOnly(): void { - $product = $this->updateProduct('simple2', self::TEST_TAX_DATA); + $product = $this->updateProduct('simple2', $this->textTaxData); $this->registerProduct($product); $block = $this->prepareLayoutProductPage(); $productPrice = $block->toHtml(); @@ -150,11 +182,13 @@ public function testFPTProductPageIncludingFPTOnly(): void /** * @magentoConfigFixture default_store tax/weee/enable 1 - * @magentoConfigFixture default_store tax/weee/display_list 1 + * @magentoConfigFixture default_store tax/weee/display 1 + * + * @return void */ public function testFPTProductPageIncludingFPTAndDescription(): void { - $product = $this->updateProduct('simple2', self::TEST_TAX_DATA); + $product = $this->updateProduct('simple2', $this->textTaxData); $this->registerProduct($product); $block = $this->prepareLayoutProductPage(); $productPrice = $block->toHtml(); @@ -164,11 +198,13 @@ public function testFPTProductPageIncludingFPTAndDescription(): void /** * @magentoConfigFixture default_store tax/weee/enable 1 - * @magentoConfigFixture default_store tax/weee/display_list 2 + * @magentoConfigFixture default_store tax/weee/display 2 + * + * @return void */ public function testFPTProductPageExcludingFPTIncludingDescriptionAndPrice(): void { - $product = $this->updateProduct('simple2', self::TEST_TAX_DATA); + $product = $this->updateProduct('simple2', $this->textTaxData); $this->registerProduct($product); $block = $this->prepareLayoutProductPage(); $productPrice = $block->toHtml(); @@ -178,11 +214,148 @@ public function testFPTProductPageExcludingFPTIncludingDescriptionAndPrice(): vo /** * @magentoConfigFixture default_store tax/weee/enable 1 - * @magentoConfigFixture default_store tax/weee/display_list 3 + * @magentoConfigFixture default_store tax/weee/display 3 + * + * @return void */ public function testFPTProductPageExcludingFPT(): void { - $product = $this->updateProduct('simple2', self::TEST_TAX_DATA); + $product = $this->updateProduct('simple2', $this->textTaxData); + $this->registerProduct($product); + $block = $this->prepareLayoutProductPage(); + $productPrice = $block->toHtml(); + $this->assertEquals('$10.00', preg_replace('/\s+/', '', strip_tags($productPrice))); + } + + /** + * @magentoDbIsolation disabled + * + * @magentoConfigFixture default/catalog/price/scope 1 + * @magentoConfigFixture fixture_second_store_store tax/weee/enable 1 + * @magentoConfigFixture fixture_second_store_store tax/weee/display 2 + * + * @magentoDataFixture Magento/Weee/_files/fixed_product_attribute.php + * @magentoDataFixture Magento/Catalog/_files/product_two_websites.php + * + * @return void + */ + public function testFPTPerWebsites(): void + { + $currentStore = $this->storeManager->getStore(); + try { + $secondStore = $this->storeManager->getStore('fixture_second_store'); + $taxData = [ + [ + 'region_id' => '1', + 'country' => 'US', + 'val' => '', + 'value' => '5', + 'website_id' => $secondStore->getWebsiteId(), + 'state' => '', + ] + ]; + $this->storeManager->setCurrentStore($secondStore); + $product = $this->updateProduct('simple-on-two-websites', $taxData); + $this->registerProduct($product); + $block = $this->prepareLayoutProductPage(); + $productPrice = $block->toHtml(); + $this->assertEquals('$10.00$5.00$15.00', preg_replace('/\s+/', '', strip_tags($productPrice))); + } finally { + $this->storeManager->setCurrentStore($currentStore); + } + } + + /** + * @magentoConfigFixture default_store tax/weee/enable 1 + * @magentoConfigFixture default_store tax/weee/display 0 + * + * @magentoDataFixture Magento/Weee/_files/fixed_product_attribute.php + * @magentoDataFixture Magento/Customer/_files/customer_one_address.php + * @magentoDataFixture Magento/Catalog/_files/second_product_simple.php + * + * @return void + */ + public function testApplyTwoFPTForCustomer(): void + { + $email = 'customer_one_address@test.com'; + $expectedPrice = '$30.00'; + $taxData = [ + [ + 'country' => 'US', + 'val' => '', + 'value' => '5', + 'website_id' => $this->baseWebsiteId, + 'state' => '', + ], + [ + 'country' => 'US', + 'val' => '', + 'value' => '15', + 'website_id' => $this->baseWebsiteId, + 'state' => 1, + ] + ]; + $this->loginCustomerByEmail($email); + $product = $this->updateProduct('simple2', $taxData); + $this->registerProduct($product); + $block = $this->prepareLayoutProductPage(); + $productPrice = $block->toHtml(); + $this->assertEquals($expectedPrice, preg_replace('/\s+/', '', strip_tags($productPrice))); + } + + /** + * @magentoConfigFixture default_store tax/defaults/country GB + * @magentoConfigFixture default_store tax/weee/enable 1 + * @magentoConfigFixture default_store tax/weee/display 0 + * + * @magentoDataFixture Magento/Weee/_files/fixed_product_attribute.php + * @magentoDataFixture Magento/Customer/_files/customer_no_address.php + * @magentoDataFixture Magento/Catalog/_files/second_product_simple.php + * + * @return void + */ + public function testApplyFPTWithoutAddressCustomer(): void + { + $email = 'customer5@example.com'; + $expectedPrice = '$10.00'; + $taxData = [ + [ + 'country' => 'US', + 'val' => '', + 'value' => '5', + 'website_id' => $this->baseWebsiteId, + 'state' => '', + ], + [ + 'country' => 'US', + 'val' => '', + 'value' => '15', + 'website_id' => $this->baseWebsiteId, + 'state' => 1, + ], + ]; + $this->loginCustomerByEmail($email); + $product = $this->updateProduct('simple2', $taxData); + $this->registerProduct($product); + $block = $this->prepareLayoutProductPage(); + $productPrice = $block->toHtml(); + $this->assertEquals($expectedPrice, preg_replace('/\s+/', '', strip_tags($productPrice))); + } + + /** + * @magentoConfigFixture default_store tax/weee/enable 1 + * @magentoConfigFixture default_store tax/weee/display 0 + * + * @magentoDataFixture Magento/Weee/_files/fixed_product_attribute.php + * @magentoDataFixture Magento/Catalog/_files/second_product_simple.php + * @magentoDataFixture Magento/Customer/_files/customer_with_uk_address.php + * + * @return void + */ + public function testApplyFPTWithForeignCountryAddress(): void + { + $this->loginCustomerByEmail('customer_uk_address@test.com'); + $product = $this->updateProduct('simple2', $this->textTaxData); $this->registerProduct($product); $block = $this->prepareLayoutProductPage(); $productPrice = $block->toHtml(); @@ -248,5 +421,19 @@ private function registerProduct(ProductInterface $product): void { $this->registry->unregister('product'); $this->registry->register('product', $product); + $this->registry->unregister('current_product'); + $this->registry->register('current_product', $product); + } + + /** + * Login customer by email + * + * @param string $email + * @return void + */ + private function loginCustomerByEmail(string $email): void + { + $customer = $this->customerRepository->get($email); + $this->customerSession->loginById($customer->getId()); } } diff --git a/dev/tests/integration/testsuite/Magento/Weee/Controller/Adminhtml/Product/Attribute/Delete/WeeeAttributesControllerTest.php b/dev/tests/integration/testsuite/Magento/Weee/Controller/Adminhtml/Product/Attribute/Delete/WeeeAttributesControllerTest.php new file mode 100644 index 0000000000000..19e4ea2259641 --- /dev/null +++ b/dev/tests/integration/testsuite/Magento/Weee/Controller/Adminhtml/Product/Attribute/Delete/WeeeAttributesControllerTest.php @@ -0,0 +1,34 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +declare(strict_types=1); + +namespace Magento\Weee\Controller\Adminhtml\Product\Attribute\Delete; + +use Magento\Catalog\Controller\Adminhtml\Product\Attribute\Delete\AbstractDeleteAttributeControllerTest; + +/** + * Delete catalog product attributes with input types like "weee". + * Attributes from Magento_Weee module. + * + * @magentoAppArea adminhtml + * @magentoDbIsolation enabled + */ +class WeeeAttributesControllerTest extends AbstractDeleteAttributeControllerTest +{ + /** + * Assert that attribute with input type "weee" will be deleted + * after dispatch delete product attribute action. + * + * @magentoDataFixture Magento/Weee/_files/fixed_product_attribute.php + * + * @return void + */ + public function testDeleteSwatchTextAttribute(): void + { + $this->dispatchDeleteAttribute('fixed_product_attribute'); + $this->assertAttributeIsDeleted('fixed_product_attribute'); + } +} diff --git a/dev/tests/integration/testsuite/Magento/Weee/Controller/Adminhtml/Product/Attribute/Save/InputType/FixedProductTaxTest.php b/dev/tests/integration/testsuite/Magento/Weee/Controller/Adminhtml/Product/Attribute/Save/InputType/FixedProductTaxTest.php index 5a6065d249b51..0c9b648a6123b 100644 --- a/dev/tests/integration/testsuite/Magento/Weee/Controller/Adminhtml/Product/Attribute/Save/InputType/FixedProductTaxTest.php +++ b/dev/tests/integration/testsuite/Magento/Weee/Controller/Adminhtml/Product/Attribute/Save/InputType/FixedProductTaxTest.php @@ -7,19 +7,20 @@ namespace Magento\Weee\Controller\Adminhtml\Product\Attribute\Save\InputType; -use Magento\Catalog\Controller\Adminhtml\Product\Attribute\Save\InputType\AbstractSaveAttributeTest; +use Magento\Catalog\Controller\Adminhtml\Product\Attribute\Save\AbstractSaveAttributeTest; /** * Test cases related to create attribute with input type fixed product tax. * * @magentoDbIsolation enabled + * @magentoAppArea adminhtml */ class FixedProductTaxTest extends AbstractSaveAttributeTest { /** * Test create attribute and compare attribute data and input data. * - * @dataProvider \Magento\TestFramework\Weee\Model\Attribute\DataProvider\FixedProductTax::getAttributeDataWithCheckArray() + * @dataProvider \Magento\TestFramework\Weee\Model\Attribute\DataProvider\FixedProductTax::getAttributeDataWithCheckArray * * @param array $attributePostData * @param array $checkArray @@ -33,7 +34,7 @@ public function testCreateAttribute(array $attributePostData, array $checkArray) /** * Test create attribute with error. * - * @dataProvider \Magento\TestFramework\Weee\Model\Attribute\DataProvider\FixedProductTax::getAttributeDataWithErrorMessage() + * @dataProvider \Magento\TestFramework\Weee\Model\Attribute\DataProvider\FixedProductTax::getAttributeDataWithErrorMessage * * @param array $attributePostData * @param string $errorMessage diff --git a/dev/tests/integration/testsuite/Magento/Weee/Controller/Adminhtml/Product/Attribute/Update/InputType/FixedProductTaxTest.php b/dev/tests/integration/testsuite/Magento/Weee/Controller/Adminhtml/Product/Attribute/Update/InputType/FixedProductTaxTest.php new file mode 100644 index 0000000000000..ec788bde0a002 --- /dev/null +++ b/dev/tests/integration/testsuite/Magento/Weee/Controller/Adminhtml/Product/Attribute/Update/InputType/FixedProductTaxTest.php @@ -0,0 +1,67 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +declare(strict_types=1); + +namespace Magento\Weee\Controller\Adminhtml\Product\Attribute\Update\InputType; + +use Magento\Catalog\Controller\Adminhtml\Product\Attribute\Update\AbstractUpdateAttributeTest; + +/** + * Test cases related to update attribute with input type fixed product tax. + * + * @magentoDbIsolation enabled + * @magentoAppArea adminhtml + */ +class FixedProductTaxTest extends AbstractUpdateAttributeTest +{ + /** + * Test update attribute. + * + * @dataProvider \Magento\TestFramework\Weee\Model\Attribute\DataProvider\FixedProductTax::getUpdateProvider + * @magentoDataFixture Magento/Weee/_files/fixed_product_attribute.php + * + * @param array $postData + * @param array $expectedData + * @return void + */ + public function testUpdateAttribute(array $postData, array $expectedData): void + { + $this->updateAttributeUsingData('fixed_product_attribute', $postData); + $this->assertUpdateAttributeProcess('fixed_product_attribute', $postData, $expectedData); + } + + /** + * Test update attribute with error. + * + * @dataProvider \Magento\TestFramework\Weee\Model\Attribute\DataProvider\FixedProductTax::getUpdateProviderWithErrorMessage + * @magentoDataFixture Magento/Weee/_files/fixed_product_attribute.php + * + * @param array $postData + * @param string $errorMessage + * @return void + */ + public function testUpdateAttributeWithError(array $postData, string $errorMessage): void + { + $this->updateAttributeUsingData('fixed_product_attribute', $postData); + $this->assertErrorSessionMessages($errorMessage); + } + + /** + * Test update attribute frontend labels on stores. + * + * @dataProvider \Magento\TestFramework\Weee\Model\Attribute\DataProvider\FixedProductTax::getUpdateFrontendLabelsProvider + * @magentoDataFixture Magento/Store/_files/second_website_with_two_stores.php + * @magentoDataFixture Magento/Weee/_files/fixed_product_attribute.php + * + * @param array $postData + * @param array $expectedData + * @return void + */ + public function testUpdateFrontendLabelOnStores(array $postData, array $expectedData): void + { + $this->processUpdateFrontendLabelOnStores('fixed_product_attribute', $postData, $expectedData); + } +} diff --git a/dev/tests/integration/testsuite/Magento/Wishlist/Block/Account/LinkTest.php b/dev/tests/integration/testsuite/Magento/Wishlist/Block/Account/LinkTest.php new file mode 100644 index 0000000000000..f4d66b2ff8cde --- /dev/null +++ b/dev/tests/integration/testsuite/Magento/Wishlist/Block/Account/LinkTest.php @@ -0,0 +1,79 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +declare(strict_types=1); + +namespace Magento\Wishlist\Block\Account; + +use Magento\Framework\ObjectManagerInterface; +use Magento\Framework\View\Result\Page; +use Magento\TestFramework\Helper\Bootstrap; +use PHPUnit\Framework\TestCase; + +/** + * Checks My Wish List link displaying in account dashboard + * + * @magentoAppArea frontend + * @magentoDbIsolation enabled + * @magentoAppIsolation enabled + */ +class LinkTest extends TestCase +{ + /** @var ObjectManagerInterface */ + private $objectManager; + + /** @var Page */ + private $page; + + /** + * @inheritdoc + */ + protected function setUp() + { + parent::setUp(); + + $this->objectManager = Bootstrap::getObjectManager(); + $this->page = $this->objectManager->create(Page::class); + } + + /** + * @return void + */ + public function testNewsletterLink(): void + { + $this->preparePage(); + $block = $this->page->getLayout()->getBlock('customer-account-navigation-wish-list-link'); + $this->assertNotFalse($block); + $html = $block->toHtml(); + $this->assertContains('wishlist/', $html); + $this->assertEquals('My Wish List', strip_tags($html)); + } + + /** + * @magentoConfigFixture current_store wishlist/general/active 0 + * + * @return void + */ + public function testNewsletterLinkDisabled(): void + { + $this->preparePage(); + $block = $this->page->getLayout()->getBlock('customer-account-navigation-wish-list-link'); + $this->assertFalse($block); + } + + /** + * Prepare page before render + * + * @return void + */ + private function preparePage(): void + { + $this->page->addHandle([ + 'default', + 'customer_account', + ]); + $this->page->getLayout()->generateXml(); + } +} diff --git a/dev/tests/integration/testsuite/Magento/Wishlist/Block/Catalog/Product/ProductList/Item/AddTo/WishlistTest.php b/dev/tests/integration/testsuite/Magento/Wishlist/Block/Catalog/Product/ProductList/Item/AddTo/WishlistTest.php new file mode 100644 index 0000000000000..36bd4dd4f312e --- /dev/null +++ b/dev/tests/integration/testsuite/Magento/Wishlist/Block/Catalog/Product/ProductList/Item/AddTo/WishlistTest.php @@ -0,0 +1,69 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +declare(strict_types=1); + +namespace Magento\Wishlist\Block\Catalog\Product\ProductList\Item\AddTo; + +use Magento\Catalog\Api\ProductRepositoryInterface; +use Magento\Framework\ObjectManagerInterface; +use Magento\Framework\View\LayoutInterface; +use Magento\TestFramework\Helper\Bootstrap; +use PHPUnit\Framework\TestCase; + +/** + * Checks add to wishlist button on category page. + * + * @magentoAppArea frontend + * @magentoDbIsolation enabled + * @magentoAppIsolation disabled + */ +class WishlistTest extends TestCase +{ + /** @var ObjectManagerInterface */ + private $objectManager; + + /** @var Wishlist */ + private $block; + + /** @var ProductRepositoryInterface */ + private $productRepository; + + /** + * @inheritdoc + */ + protected function setUp() + { + $this->objectManager = Bootstrap::getObjectManager(); + $this->productRepository = $this->objectManager->get(ProductRepositoryInterface::class); + $this->productRepository->cleanCache(); + $this->block = $this->objectManager->get(LayoutInterface::class) + ->createBlock(Wishlist::class)->setTemplate('Magento_Wishlist::catalog/product/list/addto/wishlist.phtml'); + } + + /** + * @magentoDataFixture Magento/Catalog/_files/second_product_simple.php + * + * @return void + */ + public function testAddToWishListVisible(): void + { + $product = $this->productRepository->get('simple2'); + $html = $this->block->setProduct($product)->toHtml(); + $this->assertEquals('Add to Wish List', trim(strip_tags($html))); + } + + /** + * @magentoConfigFixture current_store wishlist/general/active 0 + * @magentoDataFixture Magento/Catalog/_files/second_product_simple.php + * + * @return void + */ + public function testAddToWishListNotVisible(): void + { + $product = $this->productRepository->get('simple2'); + $this->assertEmpty($this->block->setProduct($product)->toHtml()); + } +} diff --git a/dev/tests/integration/testsuite/Magento/Wishlist/Block/Catalog/Product/View/AddTo/WishlistTest.php b/dev/tests/integration/testsuite/Magento/Wishlist/Block/Catalog/Product/View/AddTo/WishlistTest.php new file mode 100644 index 0000000000000..8d0226bfe9a2b --- /dev/null +++ b/dev/tests/integration/testsuite/Magento/Wishlist/Block/Catalog/Product/View/AddTo/WishlistTest.php @@ -0,0 +1,108 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +declare(strict_types=1); + +namespace Magento\Wishlist\Block\Catalog\Product\View\AddTo; + +use Magento\Catalog\Api\Data\ProductInterface; +use Magento\Catalog\Api\ProductRepositoryInterface; +use Magento\Framework\ObjectManagerInterface; +use Magento\Framework\Registry; +use Magento\Framework\View\LayoutInterface; +use Magento\TestFramework\Helper\Bootstrap; +use Magento\TestFramework\Helper\Xpath; +use PHPUnit\Framework\TestCase; + +/** + * Checks add to wishlist button on product page. + * + * @magentoAppArea frontend + * @magentoDbIsolation enabled + * @magentoAppIsolation disabled + */ +class WishlistTest extends TestCase +{ + private const ADD_TO_WISHLIST_XPATH = "//a[@data-action='add-to-wishlist']" + . "/span[contains(text(), 'Add to Wish List')]"; + + /** @var ObjectManagerInterface */ + private $objectManager; + + /** @var Registry */ + private $registry; + + /** @var Wishlist */ + private $block; + + /** @var ProductRepositoryInterface */ + private $productRepository; + + /** + * @inheritdoc + */ + protected function setUp() + { + $this->objectManager = Bootstrap::getObjectManager(); + $this->registry = $this->objectManager->get(Registry::class); + $this->productRepository = $this->objectManager->get(ProductRepositoryInterface::class); + $this->productRepository->cleanCache(); + $this->block = $this->objectManager->get(LayoutInterface::class) + ->createBlock(Wishlist::class)->setTemplate('Magento_Wishlist::catalog/product/view/addto/wishlist.phtml'); + } + + /** + * @inheritdoc + */ + protected function tearDown() + { + $this->registry->unregister('product'); + + parent::tearDown(); + } + + /** + * @magentoDataFixture Magento/Catalog/_files/second_product_simple.php + * + * @return void + */ + public function testAddToWishListVisible(): void + { + $product = $this->productRepository->get('simple2'); + $this->registerProduct($product); + $this->assertEquals( + 1, + Xpath::getElementsCountForXpath(self::ADD_TO_WISHLIST_XPATH, $this->block->toHtml()) + ); + } + + /** + * @magentoConfigFixture current_store wishlist/general/active 0 + * @magentoDataFixture Magento/Catalog/_files/second_product_simple.php + * + * @return void + */ + public function testAddToWishListNotVisible(): void + { + $product = $this->productRepository->get('simple2'); + $this->registerProduct($product); + $this->assertEquals( + 0, + Xpath::getElementsCountForXpath(self::ADD_TO_WISHLIST_XPATH, $this->block->toHtml()) + ); + } + + /** + * Register the product. + * + * @param ProductInterface $product + * @return void + */ + private function registerProduct(ProductInterface $product): void + { + $this->registry->unregister('product'); + $this->registry->register('product', $product); + } +} diff --git a/dev/tests/integration/testsuite/Magento/Wishlist/Block/Customer/SharingTest.php b/dev/tests/integration/testsuite/Magento/Wishlist/Block/Customer/SharingTest.php new file mode 100644 index 0000000000000..a96a7e9b4c7b3 --- /dev/null +++ b/dev/tests/integration/testsuite/Magento/Wishlist/Block/Customer/SharingTest.php @@ -0,0 +1,61 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +declare(strict_types=1); + +namespace Magento\Wishlist\Block\Customer; + +use Magento\Framework\ObjectManagerInterface; +use Magento\Framework\View\LayoutInterface; +use Magento\TestFramework\Helper\Bootstrap; +use Magento\TestFramework\Helper\Xpath; +use PHPUnit\Framework\TestCase; + +/** + * Class test share wish list block. + * + * @magentoDbIsolation enabled + * @magentoAppArea frontend + */ +class SharingTest extends TestCase +{ + /** @var ObjectManagerInterface */ + private $objectManager; + + /** @var Sharing */ + private $block; + + /** + * @inheritdoc + */ + protected function setUp() + { + parent::setUp(); + + $this->objectManager = Bootstrap::getObjectManager(); + $this->block = $this->objectManager->get(LayoutInterface::class)->createBlock(Sharing::class); + } + + /** + * @return void + */ + public function testDisplayWishListSharingForm(): void + { + $elementsXpath = [ + 'Emails input' => "//form[contains(@class, 'share')]//textarea[@name='emails' and @id='email_address']", + 'Message input' => "//form[contains(@class, 'share')]//textarea[@name='message' and @id='message']", + 'Share button' => "//form[contains(@class, 'share')]//button[contains(@class, 'submit')]" + . "/span[contains(text(), 'Share Wish List')]", + ]; + $blockHtml = $this->block->setTemplate('Magento_Wishlist::sharing.phtml')->toHtml(); + foreach ($elementsXpath as $element => $xpath) { + $this->assertEquals( + 1, + Xpath::getElementsCountForXpath($xpath, $blockHtml), + sprintf("%s was not found.", $element) + ); + } + } +} diff --git a/dev/tests/integration/testsuite/Magento/Wishlist/Block/Customer/SidebarTest.php b/dev/tests/integration/testsuite/Magento/Wishlist/Block/Customer/SidebarTest.php new file mode 100644 index 0000000000000..6e6e88ab73019 --- /dev/null +++ b/dev/tests/integration/testsuite/Magento/Wishlist/Block/Customer/SidebarTest.php @@ -0,0 +1,94 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +declare(strict_types=1); + +namespace Magento\Wishlist\Block\Customer; + +use Magento\Framework\App\ProductMetadata; +use Magento\Framework\App\ProductMetadataInterface; +use Magento\Framework\ObjectManagerInterface; +use Magento\Framework\View\Result\Page; +use Magento\TestFramework\Helper\Bootstrap; +use Magento\TestFramework\Helper\Xpath; +use PHPUnit\Framework\TestCase; + +/** + * Class test sidebar wish list block. + * + * @magentoAppArea frontend + * @magentoDbIsolation enabled + * @magentoAppIsolation enabled + */ +class SidebarTest extends TestCase +{ + private const BLOCK_NAME = 'wishlist_sidebar'; + + /** @var ObjectManagerInterface */ + private $objectManager; + + /** @var Page */ + private $page; + + /** + * @inheritdoc + */ + protected function setUp() + { + $this->objectManager = Bootstrap::getObjectManager(); + $productMetadataInterface = $this->objectManager->get(ProductMetadataInterface::class); + if ($productMetadataInterface->getEdition() !== ProductMetadata::EDITION_NAME) { + $this->markTestSkipped('Skipped, because this logic is rewritten on EE.'); + } + $this->page = $this->objectManager->create(Page::class); + } + + /** + * @magentoConfigFixture current_store wishlist/general/show_in_sidebar 1 + * + * @return void + */ + public function testSidebarWishListVisible(): void + { + $this->preparePageLayout(); + $block = $this->page->getLayout()->getBlock(self::BLOCK_NAME); + $this->assertNotFalse($block); + $this->assertEquals( + 1, + Xpath::getElementsCountForXpath( + "//div[contains(@class, 'block-wishlist')]//strong[contains(text(), 'My Wish List')]", + $block->toHtml() + ) + ); + } + + /** + * @magentoConfigFixture current_store wishlist/general/show_in_sidebar 0 + * + * @return void + */ + public function testSidebarWishListNotVisible(): void + { + $this->preparePageLayout(); + $this->assertFalse( + $this->page->getLayout()->getBlock(self::BLOCK_NAME), + 'Sidebar wish list should not be visible.' + ); + } + + /** + * Prepare category page. + * + * @return void + */ + private function preparePageLayout(): void + { + $this->page->addHandle([ + 'default', + 'catalog_category_view', + ]); + $this->page->getLayout()->generateXml(); + } +} diff --git a/dev/tests/integration/testsuite/Magento/Wishlist/Block/Customer/Wishlist/Item/ColumnTest.php b/dev/tests/integration/testsuite/Magento/Wishlist/Block/Customer/Wishlist/Item/ColumnTest.php index 668aec616533c..ffce4354dd097 100644 --- a/dev/tests/integration/testsuite/Magento/Wishlist/Block/Customer/Wishlist/Item/ColumnTest.php +++ b/dev/tests/integration/testsuite/Magento/Wishlist/Block/Customer/Wishlist/Item/ColumnTest.php @@ -3,44 +3,130 @@ * Copyright © Magento, Inc. All rights reserved. * See COPYING.txt for license details. */ +declare(strict_types=1); + namespace Magento\Wishlist\Block\Customer\Wishlist\Item; -class ColumnTest extends \PHPUnit\Framework\TestCase +use Magento\Customer\Model\Session; +use Magento\Framework\ObjectManagerInterface; +use Magento\Framework\View\Element\Text; +use Magento\Framework\View\LayoutInterface; +use Magento\Framework\View\Result\PageFactory; +use Magento\TestFramework\Helper\Bootstrap; +use Magento\TestFramework\Helper\Xpath; +use Magento\TestFramework\Wishlist\Model\GetWishlistByCustomerId; +use Magento\Wishlist\Block\Customer\Wishlist\Items; +use PHPUnit\Framework\TestCase; + +/** + * Test wish list item column. + * + * @magentoDbIsolation enabled + * @magentoAppArea frontend + * @SuppressWarnings(PHPMD.CouplingBetweenObjects) + */ +class ColumnTest extends TestCase { + /** @var ObjectManagerInterface */ + private $objectManager; + + /** @var Session */ + private $customerSession; + + /** @var LayoutInterface */ + private $layout; + + /** @var Column */ + private $block; + + /** @var GetWishlistByCustomerId */ + private $getWishlistItemsByCustomerId; + /** - * @var \Magento\Framework\View\LayoutInterface + * @inheritdoc */ - protected $_layout = null; + protected function setUp() + { + $this->objectManager = Bootstrap::getObjectManager(); + $this->customerSession = $this->objectManager->get(Session::class); + $this->layout = $this->objectManager->get(LayoutInterface::class); + $this->block = $this->layout->addBlock(Column::class, 'test'); + $this->layout->addBlock(Text::class, 'child', 'test'); + $this->getWishlistItemsByCustomerId = $this->objectManager->get(GetWishlistByCustomerId::class); + } /** - * @var \Magento\Wishlist\Block\Customer\Wishlist\Item\Column + * @inheritdoc */ - protected $_block = null; - - protected function setUp() + protected function tearDown() { - $this->_layout = \Magento\TestFramework\Helper\Bootstrap::getObjectManager()->get( - \Magento\Framework\View\LayoutInterface::class - ); - $this->_block = $this->_layout->addBlock(\Magento\Wishlist\Block\Customer\Wishlist\Item\Column::class, 'test'); - $this->_layout->addBlock(\Magento\Framework\View\Element\Text::class, 'child', 'test'); + $this->customerSession->setCustomerId(null); + + parent::tearDown(); } /** * @magentoAppIsolation enabled + * + * @return void */ - public function testToHtml() + public function testToHtml(): void { $item = new \StdClass(); - $this->_block->setItem($item); - $this->_block->toHtml(); - $this->assertSame($item, $this->_layout->getBlock('child')->getItem()); + $this->block->setItem($item); + $this->block->toHtml(); + $this->assertSame($item, $this->layout->getBlock('child')->getItem()); } - public function testGetJs() + /** + * @return void + */ + public function testGetJs(): void { $expected = uniqid(); - $this->_layout->getBlock('child')->setJs($expected); - $this->assertEquals($expected, $this->_block->getJs()); + $this->layout->getBlock('child')->setJs($expected); + $this->assertEquals($expected, $this->block->getJs()); + } + + /** + * @magentoDataFixture Magento/Wishlist/_files/wishlist.php + * + * @return void + */ + public function testWishListItemButtons(): void + { + $buttons = [ + "Add to Cart button" => "//button[contains(@class, 'tocart')]/span[contains(text(), 'Add to Cart')]", + "Edit button" => "//a[contains(@class, 'edit')]/span[contains(text(), 'Edit')]", + "Remove item button" => "//a[contains(@class, 'delete')]/span[contains(text(), 'Remove item')]", + ]; + $item = $this->getWishlistItemsByCustomerId->getItemBySku(1, 'simple'); + $this->assertNotNull($item); + $block = $this->getWishListItemsBlock()->getChildBlock('customer.wishlist.item.inner'); + $blockHtml = $block->setItem($item)->toHtml(); + foreach ($buttons as $buttonName => $xpath) { + $this->assertEquals( + 1, + Xpath::getElementsCountForXpath($xpath, $blockHtml), + sprintf("%s wasn't found.", $buttonName) + ); + } + } + + /** + * Get wish list items block. + * + * @return Items + */ + private function getWishListItemsBlock(): Items + { + $page = $this->objectManager->create(PageFactory::class)->create(); + $page->addHandle([ + 'default', + 'wishlist_index_index', + ]); + $page->getLayout()->generateXml(); + + return $page->getLayout()->getBlock('customer.wishlist.items'); } } diff --git a/dev/tests/integration/testsuite/Magento/Wishlist/Block/Customer/WishlistTest.php b/dev/tests/integration/testsuite/Magento/Wishlist/Block/Customer/WishlistTest.php new file mode 100644 index 0000000000000..36cd7fe3e4c89 --- /dev/null +++ b/dev/tests/integration/testsuite/Magento/Wishlist/Block/Customer/WishlistTest.php @@ -0,0 +1,129 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +declare(strict_types=1); + +namespace Magento\Wishlist\Block\Customer; + +use Magento\Customer\Model\Session; +use Magento\Framework\ObjectManagerInterface; +use Magento\Framework\View\Result\Page; +use Magento\TestFramework\Helper\Bootstrap; +use Magento\TestFramework\Helper\Xpath; +use PHPUnit\Framework\TestCase; + +/** + * Class test block wish list on customer account page. + * + * @magentoAppArea frontend + * @magentoDbIsolation enabled + * @magentoAppIsolation disabled + */ +class WishlistTest extends TestCase +{ + private const ITEMS_COUNT_XPATH = "//div[contains(@class, 'pager')]//span[contains(@class, 'toolbar-number')" + . " and contains(text(), '%s Item')]"; + + /** @var ObjectManagerInterface */ + private $objectManager; + + /** @var Page */ + private $page; + + /** @var Session */ + private $customerSession; + + /** + * @inheritdoc + */ + protected function setUp() + { + parent::setUp(); + + $this->objectManager = Bootstrap::getObjectManager(); + $this->page = $this->objectManager->create(Page::class); + $this->customerSession = $this->objectManager->get(Session::class); + } + + /** + * @inheritdoc + */ + protected function tearDown() + { + $this->customerSession->setCustomerId(null); + + parent::tearDown(); + } + + /** + * @magentoConfigFixture current_store wishlist/wishlist_link/use_qty 0 + * @magentoDataFixture Magento/Wishlist/_files/wishlist_with_product_qty_three.php + * + * @return void + */ + public function testDisplayNumberOfItemsInWishList(): void + { + $this->customerSession->setCustomerId(1); + $pagerBlockHtml = $this->getWishListBlock()->getChildBlock('wishlist_item_pager')->toHtml(); + $this->assertEquals( + 1, + Xpath::getElementsCountForXpath(sprintf(self::ITEMS_COUNT_XPATH, 1), $pagerBlockHtml), + "Element items count wasn't found." + ); + } + + /** + * @magentoConfigFixture current_store wishlist/wishlist_link/use_qty 1 + * @magentoDataFixture Magento/Wishlist/_files/wishlist_with_product_qty_three.php + * + * @return void + */ + public function testDisplayItemQuantitiesInWishList(): void + { + $this->markTestSkipped('Test is blocked by issue MC-31595'); + $this->customerSession->setCustomerId(1); + $pagerBlockHtml = $this->getWishListBlock()->getChildBlock('wishlist_item_pager')->toHtml(); + $this->assertEquals( + 1, + Xpath::getElementsCountForXpath(sprintf(self::ITEMS_COUNT_XPATH, 3), $pagerBlockHtml), + "Element items count wasn't found." + ); + } + + /** + * @magentoDataFixture Magento/Wishlist/_files/wishlist.php + * + * @return void + */ + public function testDisplayActionButtonsInWishList(): void + { + $buttonsXpath = [ + "//button[contains(@class, 'update') and @type='submit']/span[contains(text(), 'Update Wish List')]", + "//button[contains(@class, 'share') and @type='submit']/span[contains(text(), 'Share Wish List')]", + "//button[contains(@class, 'tocart') and @type='button']/span[contains(text(), 'Add All to Cart')]", + ]; + $this->customerSession->setCustomerId(1); + $blockHtml = $this->getWishListBlock()->toHtml(); + foreach ($buttonsXpath as $xpath) { + $this->assertEquals(1, Xpath::getElementsCountForXpath($xpath, $blockHtml)); + } + } + + /** + * Get wish list block. + * + * @return Wishlist + */ + private function getWishListBlock(): Wishlist + { + $this->page->addHandle([ + 'default', + 'wishlist_index_index', + ]); + $this->page->getLayout()->generateXml(); + + return $this->page->getLayout()->getBlock('customer.wishlist'); + } +} diff --git a/dev/tests/integration/testsuite/Magento/Wishlist/Block/LinkTest.php b/dev/tests/integration/testsuite/Magento/Wishlist/Block/LinkTest.php new file mode 100644 index 0000000000000..b7f124f7c1f6d --- /dev/null +++ b/dev/tests/integration/testsuite/Magento/Wishlist/Block/LinkTest.php @@ -0,0 +1,56 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +declare(strict_types=1); + +namespace Magento\Wishlist\Block; + +use Magento\Framework\ObjectManagerInterface; +use Magento\Framework\View\LayoutInterface; +use Magento\TestFramework\Helper\Bootstrap; +use PHPUnit\Framework\TestCase; + +/** + * Class test link my wish list in customer menu. + * + * @magentoAppArea frontend + * @magentoDbIsolation enabled + * @magentoAppIsolation disabled + */ +class LinkTest extends TestCase +{ + /** @var ObjectManagerInterface */ + private $objectManager; + + /** @var Link */ + private $block; + + /** + * @inheritdoc + */ + protected function setUp() + { + $this->objectManager = Bootstrap::getObjectManager(); + $this->block = $this->objectManager->get(LayoutInterface::class)->createBlock(Link::class); + } + + /** + * @return void + */ + public function testWishListLinkVisible(): void + { + $this->assertContains('My Wish List', strip_tags($this->block->toHtml())); + } + + /** + * @magentoConfigFixture current_store wishlist/general/active 0 + * + * @return void + */ + public function testWishListLinkNotVisible(): void + { + $this->assertEmpty($this->block->toHtml()); + } +} diff --git a/dev/tests/integration/testsuite/Magento/Wishlist/Controller/Index/AddTest.php b/dev/tests/integration/testsuite/Magento/Wishlist/Controller/Index/AddTest.php new file mode 100644 index 0000000000000..82ae8e92d2979 --- /dev/null +++ b/dev/tests/integration/testsuite/Magento/Wishlist/Controller/Index/AddTest.php @@ -0,0 +1,189 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +declare(strict_types=1); + +namespace Magento\Wishlist\Controller\Index; + +use Magento\Catalog\Api\ProductRepositoryInterface; +use Magento\Customer\Model\Session; +use Magento\Framework\App\Request\Http as HttpRequest; +use Magento\Framework\Escaper; +use Magento\Framework\Message\MessageInterface; +use Magento\TestFramework\TestCase\AbstractController; +use Magento\TestFramework\Wishlist\Model\GetWishlistByCustomerId; +use Zend\Stdlib\Parameters; + +/** + * Test for add product to wish list. + * + * @magentoDbIsolation enabled + * @magentoAppArea frontend + * @magentoDataFixture Magento/Customer/_files/customer.php + */ +class AddTest extends AbstractController +{ + /** @var Session */ + private $customerSession; + + /** @var GetWishlistByCustomerId */ + private $getWishlistByCustomerId; + + /** @var ProductRepositoryInterface */ + private $productRepository; + + /** @var Escaper */ + private $escaper; + + /** + * @inheritdoc + */ + protected function setUp() + { + parent::setUp(); + + $this->customerSession = $this->_objectManager->get(Session::class); + $this->getWishlistByCustomerId = $this->_objectManager->get(GetWishlistByCustomerId::class); + $this->productRepository = $this->_objectManager->get(ProductRepositoryInterface::class); + $this->productRepository->cleanCache(); + $this->escaper = $this->_objectManager->get(Escaper::class); + } + + /** + * @inheritdoc + */ + protected function tearDown() + { + $this->customerSession->setCustomerId(null); + + parent::tearDown(); + } + + /** + * @magentoDataFixture Magento/Customer/_files/customer.php + * @magentoDataFixture Magento/Catalog/_files/product_simple_xss.php + * + * @return void + */ + public function testAddActionProductNameXss(): void + { + $this->prepareReferer(); + $this->customerSession->setCustomerId(1); + $product = $this->productRepository->get('product-with-xss'); + $escapedProductName = $this->escaper->escapeHtml($product->getName()); + $this->performAddToWishListRequest(['product' => $product->getId()]); + $this->assertSuccess(1, 1, $escapedProductName); + } + + /** + * @magentoDataFixture Magento/Customer/_files/customer.php + * @magentoDataFixture Magento/Wishlist/_files/wishlist_with_configurable_product.php + * @magentoDbIsolation disabled + * + * @return void + */ + public function testAddConfigurableProductToWishList(): void + { + $this->prepareReferer(); + $this->customerSession->setCustomerId(1); + $product = $this->productRepository->get('Configurable product'); + $this->performAddToWishListRequest(['product' => $product->getId()]); + $this->assertSuccess(1, 1, $product->getName()); + } + + /** + * @magentoDataFixture Magento/Customer/_files/customer.php + * @magentoDataFixture Magento/Catalog/_files/multiple_products.php + * + * @return void + */ + public function testAddDisabledProductToWishList(): void + { + $expectedMessage = $this->escaper->escapeHtml("We can't specify a product."); + $this->customerSession->setCustomerId(1); + $product = $this->productRepository->get('simple3'); + $this->performAddToWishListRequest(['product' => $product->getId()]); + $this->assertSessionMessages($this->equalTo([(string)__($expectedMessage)]), MessageInterface::TYPE_ERROR); + $this->assertRedirect($this->stringContains('wishlist/')); + } + + /** + * @return void + */ + public function testAddToWishListWithoutParams(): void + { + $this->customerSession->setCustomerId(1); + $this->performAddToWishListRequest([]); + $this->assertSessionMessages($this->isEmpty(), MessageInterface::TYPE_ERROR); + $this->assertSessionMessages($this->isEmpty(), MessageInterface::TYPE_SUCCESS); + $this->assertRedirect($this->stringContains('wishlist/')); + } + + /** + * @return void + */ + public function testAddNotExistingProductToWishList(): void + { + $this->customerSession->setCustomerId(1); + $expectedMessage = $this->escaper->escapeHtml("We can't specify a product."); + $this->performAddToWishListRequest(['product' => 989]); + $this->assertSessionMessages($this->equalTo([(string)__($expectedMessage)]), MessageInterface::TYPE_ERROR); + $this->assertRedirect($this->stringContains('wishlist/')); + } + + /** + * @return void + */ + public function testAddToNotExistingWishList(): void + { + $expectedMessage = $this->escaper->escapeHtml("The requested Wish List doesn't exist."); + $this->customerSession->setCustomerId(1); + $this->performAddToWishListRequest(['wishlist_id' => 989]); + $this->assertSessionMessages($this->equalTo([(string)__($expectedMessage)]), MessageInterface::TYPE_ERROR); + $this->assert404NotFound(); + } + + /** + * Perform request add item to wish list. + * + * @param array $params + * @return void + */ + private function performAddToWishListRequest(array $params): void + { + $this->getRequest()->setParams($params)->setMethod(HttpRequest::METHOD_POST); + $this->dispatch('wishlist/index/add'); + } + + /** + * Assert success response and items count. + * + * @param int $customerId + * @param int $itemsCount + * @param string $productName + * @return void + */ + private function assertSuccess(int $customerId, int $itemsCount, string $productName): void + { + $expectedMessage = sprintf("\n%s has been added to your Wish List.", $productName) + . " Click <a href=\"http://localhost/test\">here</a> to continue shopping."; + $this->assertSessionMessages($this->equalTo([(string)__($expectedMessage)]), MessageInterface::TYPE_SUCCESS); + $wishlist = $this->getWishlistByCustomerId->execute($customerId); + $this->assertCount($itemsCount, $wishlist->getItemCollection()); + $this->assertRedirect($this->stringContains('wishlist/index/index/wishlist_id/' . $wishlist->getId())); + } + + /** + * Prepare referer to test. + * + * @return void + */ + private function prepareReferer(): void + { + $parameters = $this->_objectManager->create(Parameters::class); + $parameters->set('HTTP_REFERER', 'http://localhost/test'); + $this->getRequest()->setServer($parameters); + } +} diff --git a/dev/tests/integration/testsuite/Magento/Wishlist/Controller/Index/AllcartTest.php b/dev/tests/integration/testsuite/Magento/Wishlist/Controller/Index/AllcartTest.php new file mode 100644 index 0000000000000..bc589c2791eb5 --- /dev/null +++ b/dev/tests/integration/testsuite/Magento/Wishlist/Controller/Index/AllcartTest.php @@ -0,0 +1,110 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +declare(strict_types=1); + +namespace Magento\Wishlist\Controller\Index; + +use Magento\Checkout\Model\CartFactory; +use Magento\Checkout\Model\Cart as CartModel; +use Magento\Customer\Model\Session; +use Magento\Framework\App\Request\Http as HttpRequest; +use Magento\Framework\Escaper; +use Magento\Framework\Message\MessageInterface; +use Magento\TestFramework\TestCase\AbstractController; +use Magento\TestFramework\Wishlist\Model\GetWishlistByCustomerId; + +/** + * Test for add all products to cart from wish list. + * + * @magentoDbIsolation disabled + * @magentoAppArea frontend + */ +class AllcartTest extends AbstractController +{ + /** @var Session */ + private $customerSession; + + /** @var CartModel */ + private $cart; + + /** @var Escaper */ + private $escaper; + + /** @var GetWishlistByCustomerId */ + private $getWishlistByCustomerId; + + /** + * @inheritdoc + */ + protected function setUp() + { + parent::setUp(); + + $this->customerSession = $this->_objectManager->get(Session::class); + $this->cart = $this->_objectManager->get(CartFactory::class)->create(); + $this->escaper = $this->_objectManager->get(Escaper::class); + $this->getWishlistByCustomerId = $this->_objectManager->get(GetWishlistByCustomerId::class); + } + + /** + * @inheritdoc + */ + protected function tearDown() + { + $this->customerSession->setCustomerId(null); + + parent::tearDown(); + } + + /** + * @magentoDataFixture Magento/Wishlist/_files/wishlist_with_product_qty_increments.php + * + * @return void + */ + public function testAddProductQtyIncrementToCartFromWishList(): void + { + $this->customerSession->setCustomerId(1); + $this->performAddAllToCartRequest(); + $wishlistCollection = $this->getWishlistByCustomerId->execute(1)->getItemCollection(); + $this->assertCount(1, $wishlistCollection); + $this->assertCount(0, $this->cart->getQuote()->getItemsCollection()); + $item = $this->getWishlistByCustomerId->getItemBySku(1, 'simple'); + $this->assertNotNull($item); + $expectedMessage = $this->escaper->escapeHtml( + sprintf('You can buy this product only in quantities of 5 at a time for "%s".', $item->getName()) + ); + $this->assertSessionMessages($this->equalTo([(string)__($expectedMessage)]), MessageInterface::TYPE_ERROR); + } + + /** + * @magentoDataFixture Magento/Wishlist/_files/wishlist_with_simple_product.php + * + * @return void + */ + public function testAddAllProductToCartFromWishList(): void + { + $this->customerSession->setCustomerId(1); + $this->performAddAllToCartRequest(); + $quoteCollection = $this->cart->getQuote()->getItemsCollection(); + $this->assertCount(1, $quoteCollection); + $item = $quoteCollection->getFirstItem(); + $expectedMessage = $this->escaper->escapeHtml( + sprintf('1 product(s) have been added to shopping cart: "%s".', $item->getName()) + ); + $this->assertSessionMessages($this->equalTo([(string)__($expectedMessage)]), MessageInterface::TYPE_SUCCESS); + } + + /** + * Perform add all products to cart from wish list request. + * + * @return void + */ + private function performAddAllToCartRequest(): void + { + $this->getRequest()->setMethod(HttpRequest::METHOD_POST); + $this->dispatch('wishlist/index/allcart'); + } +} diff --git a/dev/tests/integration/testsuite/Magento/Wishlist/Controller/Index/CartTest.php b/dev/tests/integration/testsuite/Magento/Wishlist/Controller/Index/CartTest.php new file mode 100644 index 0000000000000..3681409e40156 --- /dev/null +++ b/dev/tests/integration/testsuite/Magento/Wishlist/Controller/Index/CartTest.php @@ -0,0 +1,120 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +declare(strict_types=1); + +namespace Magento\Wishlist\Controller\Index; + +use Magento\Checkout\Model\CartFactory; +use Magento\Customer\Model\Session; +use Magento\Framework\App\Request\Http as HttpRequest; +use Magento\Framework\Escaper; +use Magento\Framework\Message\MessageInterface; +use Magento\TestFramework\TestCase\AbstractController; +use Magento\TestFramework\Wishlist\Model\GetWishlistByCustomerId; + +/** + * Test for add product to cart from wish list. + * + * @magentoDbIsolation disabled + * @magentoAppArea frontend + */ +class CartTest extends AbstractController +{ + /** @var Session */ + private $customerSession; + + /** @var GetWishlistByCustomerId */ + private $getWishlistByCustomerId; + + /** @var CartFactory */ + private $cartFactory; + + /** @var Escaper */ + private $escaper; + + /** + * @inheritdoc + */ + protected function setUp() + { + parent::setUp(); + + $this->customerSession = $this->_objectManager->get(Session::class); + $this->getWishlistByCustomerId = $this->_objectManager->get(GetWishlistByCustomerId::class); + $this->cartFactory = $this->_objectManager->get(CartFactory::class); + $this->escaper = $this->_objectManager->get(Escaper::class); + } + + /** + * @inheritdoc + */ + protected function tearDown() + { + $this->customerSession->setCustomerId(null); + + parent::tearDown(); + } + + /** + * @magentoDataFixture Magento/Wishlist/_files/wishlist_with_simple_product.php + * + * @return void + */ + public function testAddSimpleProductToCart(): void + { + $this->customerSession->setCustomerId(1); + $item = $this->getWishlistByCustomerId->getItemBySku(1, 'simple-1'); + $this->assertNotNull($item); + $this->performAddToCartRequest(['item' => $item->getId(), 'qty' => 3]); + $message = sprintf('You added %s to your shopping cart.', $item->getName()); + $this->assertSessionMessages($this->equalTo([(string)__($message)]), MessageInterface::TYPE_SUCCESS); + $this->assertCount(0, $this->getWishlistByCustomerId->execute(1)->getItemCollection()); + $cart = $this->cartFactory->create(); + $this->assertEquals(1, $cart->getItemsCount()); + $this->assertEquals(3, $cart->getItemsQty()); + } + + /** + * @magentoDataFixture Magento/Wishlist/_files/wishlist_with_configurable_product.php + * + * @return void + */ + public function testAddItemWithNotChosenOptionToCart(): void + { + $this->customerSession->setCustomerId(1); + $item = $this->getWishlistByCustomerId->getItemBySku(1, 'Configurable product'); + $this->assertNotNull($item); + $this->performAddToCartRequest(['item' => $item->getId(), 'qty' => 1]); + $redirectUrl = sprintf("wishlist/index/configure/id/%s/product_id/%s", $item->getId(), $item->getProductId()); + $this->assertRedirect($this->stringContains($redirectUrl)); + $message = 'You need to choose options for your item.'; + $this->assertSessionMessages($this->equalTo([(string)__($message)]), MessageInterface::TYPE_NOTICE); + } + + /** + * @magentoDataFixture Magento/Customer/_files/customer.php + * + * @return void + */ + public function testAddNotExistingItemToCart(): void + { + $this->customerSession->setCustomerId(1); + $this->performAddToCartRequest(['item' => 989]); + $this->assertRedirect($this->stringContains('wishlist/index/')); + } + + /** + * Perform request add to cart from wish list. + * + * @param array $params + * @return void + */ + private function performAddToCartRequest(array $params): void + { + $this->getRequest()->setParams($params)->setMethod(HttpRequest::METHOD_POST); + $this->dispatch('wishlist/index/cart'); + } +} diff --git a/dev/tests/integration/testsuite/Magento/Wishlist/Controller/Index/IndexTest.php b/dev/tests/integration/testsuite/Magento/Wishlist/Controller/Index/IndexTest.php new file mode 100644 index 0000000000000..b92b2f3e5e85b --- /dev/null +++ b/dev/tests/integration/testsuite/Magento/Wishlist/Controller/Index/IndexTest.php @@ -0,0 +1,82 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +declare(strict_types=1); + +namespace Magento\Wishlist\Controller\Index; + +use Magento\Customer\Model\Session; +use Magento\TestFramework\Helper\Xpath; +use Magento\TestFramework\TestCase\AbstractController; + +/** + * Test wish list on customer account page. + * + * @magentoDbIsolation enabled + * @magentoAppArea frontend + */ +class IndexTest extends AbstractController +{ + /** @var Session */ + private $customerSession; + + /** + * @inheritdoc + */ + protected function setUp() + { + parent::setUp(); + + $this->customerSession = $this->_objectManager->get(Session::class); + } + + /** + * @inheritdoc + */ + protected function tearDown() + { + $this->customerSession->setCustomerId(null); + + parent::tearDown(); + } + + /** + * Verify wishlist view action + * + * The following is verified: + * - \Magento\Wishlist\Model\ResourceModel\Item\Collection + * - \Magento\Wishlist\Block\Customer\Wishlist + * - \Magento\Wishlist\Block\Customer\Wishlist\Items + * - \Magento\Wishlist\Block\Customer\Wishlist\Item\Column + * - \Magento\Wishlist\Block\Customer\Wishlist\Item\Column\Cart + * - \Magento\Wishlist\Block\Customer\Wishlist\Item\Column\Comment + * - \Magento\Wishlist\Block\Customer\Wishlist\Button + * - that \Magento\Wishlist\Block\Customer\Wishlist\Item\Options doesn't throw a fatal error + * + * @magentoDataFixture Magento/Wishlist/_files/wishlist.php + * + * @return void + */ + public function testItemColumnBlock(): void + { + $this->customerSession->setCustomerId(1); + $this->dispatch('wishlist/index/index'); + $body = $this->getResponse()->getBody(); + $this->assertEquals( + 1, + Xpath::getElementsCountForXpath( + '//img[contains(@src, "small_image.jpg") and @alt = "Simple Product"]', + $body + ) + ); + $this->assertEquals( + 1, + Xpath::getElementsCountForXpath( + '//textarea[contains(@name, "description")]', + $body + ) + ); + } +} diff --git a/dev/tests/integration/testsuite/Magento/Wishlist/Controller/Index/PluginTest.php b/dev/tests/integration/testsuite/Magento/Wishlist/Controller/Index/PluginTest.php index 5303c9f352b87..7193791bdbe6c 100644 --- a/dev/tests/integration/testsuite/Magento/Wishlist/Controller/Index/PluginTest.php +++ b/dev/tests/integration/testsuite/Magento/Wishlist/Controller/Index/PluginTest.php @@ -7,12 +7,17 @@ namespace Magento\Wishlist\Controller\Index; +use Magento\Framework\App\Request\Http as HttpRequest; +use Magento\Framework\Message\MessageInterface; use Magento\TestFramework\TestCase\AbstractController; use Magento\Customer\Model\Session as CustomerSession; use Magento\Catalog\Api\ProductRepositoryInterface; /** * Test for wishlist plugin before dispatch + * + * @magentoDbIsolation enabled + * @magentoAppArea frontend */ class PluginTest extends AbstractController { @@ -21,13 +26,20 @@ class PluginTest extends AbstractController */ private $customerSession; + /** + * @var ProductRepositoryInterface + */ + private $productRepository; + /** * @inheritdoc */ protected function setUp() { parent::setUp(); + $this->customerSession = $this->_objectManager->get(CustomerSession::class); + $this->productRepository = $this->_objectManager->get(ProductRepositoryInterface::class); } /** @@ -37,20 +49,22 @@ protected function tearDown() { $this->customerSession->logout(); $this->customerSession = null; + parent::tearDown(); } /** * Test for adding product to wishlist with invalidate credentials * - * @return void * @magentoDataFixture Magento/Catalog/_files/product_simple_xss.php * @magentoDataFixture Magento/Customer/_files/customer.php - * @magentoAppArea frontend + * + * @return void */ public function testAddActionProductWithInvalidCredentials(): void { - $this->getRequest()->setMethod('POST'); + $product = $this->productRepository->get('product-with-xss'); + $this->getRequest()->setMethod(HttpRequest::METHOD_POST); $this->getRequest()->setPostValue( [ 'login' => [ @@ -59,14 +73,23 @@ public function testAddActionProductWithInvalidCredentials(): void ], ] ); - - /** @var ProductRepositoryInterface $productRepository */ - $productRepository = $this->_objectManager->get(ProductRepositoryInterface::class); - - $product = $productRepository->get('product-with-xss'); - - $this->dispatch('wishlist/index/add/product/' . $product->getId() . '?nocookie=1'); - + $this->getRequest()->setParams(['product' => $product->getId(), 'nocookie' => 1]); + $this->dispatch('wishlist/index/add'); $this->assertArrayNotHasKey('login', $this->customerSession->getBeforeWishlistRequest()); + $expectedMessage = 'You must login or register to add items to your wishlist.'; + $this->assertSessionMessages($this->equalTo([(string)__($expectedMessage)]), MessageInterface::TYPE_ERROR); + } + + /** + * @magentoConfigFixture current_store wishlist/general/active 0 + * @magentoDataFixture Magento/Customer/_files/customer.php + * + * @return void + */ + public function testWithDisabledWishList(): void + { + $this->customerSession->setCustomerId(1); + $this->dispatch('wishlist/index/index'); + $this->assert404NotFound(); } } diff --git a/dev/tests/integration/testsuite/Magento/Wishlist/Controller/Index/RemoveTest.php b/dev/tests/integration/testsuite/Magento/Wishlist/Controller/Index/RemoveTest.php new file mode 100644 index 0000000000000..5bd8d006e5fe5 --- /dev/null +++ b/dev/tests/integration/testsuite/Magento/Wishlist/Controller/Index/RemoveTest.php @@ -0,0 +1,79 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +declare(strict_types=1); + +namespace Magento\Wishlist\Controller\Index; + +use Magento\Customer\Model\Session; +use Magento\Framework\App\Request\Http as HttpRequest; +use Magento\Framework\Message\MessageInterface; +use Magento\TestFramework\TestCase\AbstractController; +use Magento\TestFramework\Wishlist\Model\GetWishlistByCustomerId; + +/** + * Test for remove product from wish list. + * + * @magentoDbIsolation enabled + * @magentoAppArea frontend + * @magentoDataFixture Magento/Wishlist/_files/wishlist.php + */ +class RemoveTest extends AbstractController +{ + /** @var Session */ + private $customerSession; + + /** @var GetWishlistByCustomerId */ + private $getWishlistByCustomerId; + + /** + * @inheritdoc + */ + protected function setUp() + { + parent::setUp(); + + $this->customerSession = $this->_objectManager->get(Session::class); + $this->getWishlistByCustomerId = $this->_objectManager->get(GetWishlistByCustomerId::class); + } + + /** + * @inheritdoc + */ + protected function tearDown() + { + $this->customerSession->setCustomerId(null); + + parent::tearDown(); + } + + /** + * @return void + */ + public function testRemoveProductFromWishList(): void + { + $customerId = 1; + $this->customerSession->setCustomerId($customerId); + $item = $this->getWishlistByCustomerId->getItemBySku($customerId, 'simple'); + $this->assertNotNull($item); + $productName = $item->getProduct()->getName(); + $this->getRequest()->setParam('item', $item->getId())->setMethod(HttpRequest::METHOD_POST); + $this->dispatch('wishlist/index/remove'); + $message = sprintf("\n%s has been removed from your Wish List.\n", $productName); + $this->assertSessionMessages($this->equalTo([(string)__($message)]), MessageInterface::TYPE_SUCCESS); + $this->assertCount(0, $this->getWishlistByCustomerId->execute($customerId)->getItemCollection()); + } + + /** + * @return void + */ + public function testRemoveNotExistingItemFromWishList(): void + { + $this->customerSession->setCustomerId(1); + $this->getRequest()->setParams(['item' => 989])->setMethod(HttpRequest::METHOD_POST); + $this->dispatch('wishlist/index/remove'); + $this->assert404NotFound(); + } +} diff --git a/dev/tests/integration/testsuite/Magento/Wishlist/Controller/Index/SendTest.php b/dev/tests/integration/testsuite/Magento/Wishlist/Controller/Index/SendTest.php new file mode 100644 index 0000000000000..6a89be8bdb3b0 --- /dev/null +++ b/dev/tests/integration/testsuite/Magento/Wishlist/Controller/Index/SendTest.php @@ -0,0 +1,180 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +declare(strict_types=1); + +namespace Magento\Wishlist\Controller\Index; + +use Magento\Catalog\Api\ProductRepositoryInterface; +use Magento\Customer\Api\CustomerNameGenerationInterface; +use Magento\Customer\Model\Session; +use Magento\Framework\App\Request\Http as HttpRequest; +use Magento\Framework\Message\MessageInterface; +use Magento\TestFramework\Helper\Xpath; +use Magento\TestFramework\Mail\Template\TransportBuilderMock; +use Magento\TestFramework\TestCase\AbstractController; + +/** + * Test sending wish list. + * + * @magentoDbIsolation enabled + * @magentoAppArea frontend + * @magentoDataFixture Magento/Wishlist/_files/wishlist.php + */ +class SendTest extends AbstractController +{ + /** @var Session */ + private $customerSession; + + /** @var CustomerNameGenerationInterface */ + private $customerNameGeneration; + + /** @var ProductRepositoryInterface */ + private $productRepository; + + /** @var TransportBuilderMock */ + private $transportBuilder; + + /** + * @inheritdoc + */ + protected function setUp() + { + parent::setUp(); + + $this->customerSession = $this->_objectManager->get(Session::class); + $this->customerNameGeneration = $this->_objectManager->get(CustomerNameGenerationInterface::class); + $this->productRepository = $this->_objectManager->get(ProductRepositoryInterface::class); + $this->productRepository->cleanCache(); + $this->transportBuilder = $this->_objectManager->get(TransportBuilderMock::class); + } + + /** + * @inheritdoc + */ + protected function tearDown() + { + $this->customerSession->setCustomerId(null); + + parent::tearDown(); + } + + /** + * @return void + */ + public function testSendWishList(): void + { + $product = $this->productRepository->get('simple'); + $this->customerSession->setCustomerId(1); + $shareMessage = 'Here\'s what I want for my birthday.'; + $postValues = ['emails' => 'test@example.com', 'message' => $shareMessage]; + $this->dispatchSendWishListRequest($postValues); + $this->assertSessionMessages( + $this->equalTo([(string)__('Your wish list has been shared.')]), + MessageInterface::TYPE_SUCCESS + ); + $this->assertNotNull($this->transportBuilder->getSentMessage()); + $messageContent = $this->transportBuilder->getSentMessage()->getBody()->getParts()[0]->getRawContent(); + $this->assertContains($shareMessage, $messageContent); + $this->assertContains( + sprintf( + '%s wants to share this Wish List', + $this->customerNameGeneration->getCustomerName($this->customerSession->getCustomerDataObject()) + ), + $messageContent + ); + $this->assertEquals( + 1, + Xpath::getElementsCountForXpath( + sprintf( + "//a[contains(@href, '%s')]/strong[contains(text(), '%s')]", + $product->getProductUrl(), + $product->getName() + ), + $messageContent + ) + ); + $this->assertEquals( + 1, + Xpath::getElementsCountForXpath( + "//a[contains(@href, 'wishlist/shared/index/code/fixture_unique_code/')" + . " and contains(text(), 'View all Wish List')]", + $messageContent + ) + ); + } + + /** + * @magentoConfigFixture current_store wishlist/email/number_limit 2 + * + * @return void + */ + public function testSendWishListWithEmailsLimit(): void + { + $this->customerSession->setCustomerId(1); + $postValues = ['emails' => 'test@example.com, test2@example.com, test3@example.com']; + $this->dispatchSendWishListRequest($postValues); + $this->assertResponseWithError('Maximum of 2 emails can be sent.'); + } + + /** + * @magentoConfigFixture current_store wishlist/email/text_limit 10 + * + * @return void + */ + public function testSendWishListWithTextLimit(): void + { + $this->customerSession->setCustomerId(1); + $postValues = ['emails' => 'test@example.com', 'message' => 'Test message']; + $this->dispatchSendWishListRequest($postValues); + $this->assertResponseWithError('Message length must not exceed 10 symbols'); + } + + /** + * @return void + */ + public function testSendWishListWithoutEmails(): void + { + $this->customerSession->setCustomerId(1); + $postValues = ['emails' => '']; + $this->dispatchSendWishListRequest($postValues); + $this->assertResponseWithError('Please enter an email address.'); + } + + /** + * @return void + */ + public function testSendWishListWithInvalidEmail(): void + { + $this->customerSession->setCustomerId(1); + $postValues = ['emails' => 'test @example.com']; + $this->dispatchSendWishListRequest($postValues); + $this->assertResponseWithError('Please enter a valid email address.'); + } + + /** + * Dispatch send wish list request. + * + * @param array $postValues + * @return void + */ + private function dispatchSendWishListRequest(array $postValues): void + { + $this->getRequest()->setPostValue($postValues)->setMethod(HttpRequest::METHOD_POST); + $this->dispatch('wishlist/index/send'); + } + + /** + * Assert error message and redirect. + * + * @param string $message + * @return void + */ + private function assertResponseWithError(string $message): void + { + $this->assertSessionMessages($this->equalTo([__($message)]), MessageInterface::TYPE_ERROR); + $this->assertRedirect($this->stringContains('wishlist/index/share')); + } +} diff --git a/dev/tests/integration/testsuite/Magento/Wishlist/Controller/Index/UpdateItemOptionsTest.php b/dev/tests/integration/testsuite/Magento/Wishlist/Controller/Index/UpdateItemOptionsTest.php new file mode 100644 index 0000000000000..40646be11f684 --- /dev/null +++ b/dev/tests/integration/testsuite/Magento/Wishlist/Controller/Index/UpdateItemOptionsTest.php @@ -0,0 +1,198 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +declare(strict_types=1); + +namespace Magento\Wishlist\Controller\Index; + +use Magento\Catalog\Api\Data\ProductInterface; +use Magento\Catalog\Api\ProductRepositoryInterface; +use Magento\Customer\Model\Session; +use Magento\Framework\App\Request\Http as HttpRequest; +use Magento\Framework\Escaper; +use Magento\Framework\Message\MessageInterface; +use Magento\Framework\Serialize\SerializerInterface; +use Magento\TestFramework\TestCase\AbstractController; +use Magento\TestFramework\Wishlist\Model\GetWishlistByCustomerId; +use Magento\Wishlist\Model\Item; + +/** + * Test for update wish list item. + * + * @magentoDbIsolation enabled + * @magentoAppArea frontend + */ +class UpdateItemOptionsTest extends AbstractController +{ + /** @var Session */ + private $customerSession; + + /** @var ProductRepositoryInterface */ + private $productRepository; + + /** @var Escaper */ + private $escaper; + + /** @var SerializerInterface */ + private $json; + + /** @var GetWishlistByCustomerId */ + private $getWishlistByCustomerId; + + /** + * @inheritdoc + */ + protected function setUp() + { + parent::setUp(); + + $this->customerSession = $this->_objectManager->get(Session::class); + $this->getWishlistByCustomerId = $this->_objectManager->get(GetWishlistByCustomerId::class); + $this->productRepository = $this->_objectManager->get(ProductRepositoryInterface::class); + $this->productRepository->cleanCache(); + $this->escaper = $this->_objectManager->get(Escaper::class); + $this->json = $this->_objectManager->get(SerializerInterface::class); + } + + /** + * @inheritdoc + */ + protected function tearDown() + { + $this->customerSession->setCustomerId(null); + + parent::tearDown(); + } + + /** + * @magentoDataFixture Magento/Wishlist/_files/wishlist_with_configurable_product.php + * @magentoDbIsolation disabled + * + * @return void + */ + public function testUpdateItemOptions(): void + { + $this->customerSession->setCustomerId(1); + $item = $this->getWishlistByCustomerId->getItemBySku(1, 'Configurable product'); + $this->assertNotNull($item); + $params = [ + 'id' => $item->getId(), + 'product' => $item->getProductId(), + 'super_attribute' => $this->performConfigurableOption($item->getProduct()), + 'qty' => 5, + ]; + $this->performUpdateWishListItemRequest($params); + $message = sprintf("%s has been updated in your Wish List.", $item->getProduct()->getName()); + $this->assertSessionMessages($this->equalTo([(string)__($message)]), MessageInterface::TYPE_SUCCESS); + $this->assertRedirect($this->stringContains('wishlist/index/index/wishlist_id/' . $item->getWishlistId())); + $this->assertUpdatedItem( + $this->getWishlistByCustomerId->getItemBySku(1, 'Configurable product'), + $params + ); + } + + /** + * @magentoDataFixture Magento/Customer/_files/customer.php + * + * @return void + */ + public function testUpdateItemOptionsWithoutParams(): void + { + $this->customerSession->setCustomerId(1); + $this->performUpdateWishListItemRequest([]); + $this->assertSessionMessages($this->isEmpty(), MessageInterface::TYPE_ERROR); + $this->assertSessionMessages($this->isEmpty(), MessageInterface::TYPE_SUCCESS); + $this->assertRedirect($this->stringContains('wishlist/')); + } + + /** + * @magentoDataFixture Magento/Customer/_files/customer.php + * + * @return void + */ + public function testUpdateNotExistingItem(): void + { + $this->customerSession->setCustomerId(1); + $this->performUpdateWishListItemRequest(['product' => 989]); + $message = $this->escaper->escapeHtml("We can't specify a product."); + $this->assertSessionMessages($this->equalTo([(string)__($message)]), MessageInterface::TYPE_ERROR); + $this->assertRedirect($this->stringContains('wishlist/')); + } + + /** + * @magentoDataFixture Magento/Customer/_files/customer.php + * @magentoDataFixture Magento/Catalog/_files/multiple_products.php + * + * @return void + */ + public function testUpdateOutOfStockItem(): void + { + $product = $this->productRepository->get('simple3'); + $this->customerSession->setCustomerId(1); + $this->performUpdateWishListItemRequest(['product' => $product->getId()]); + $message = $this->escaper->escapeHtml("We can't specify a product."); + $this->assertSessionMessages($this->equalTo([(string)__($message)]), MessageInterface::TYPE_ERROR); + $this->assertRedirect($this->stringContains('wishlist/')); + } + + /** + * @magentoDataFixture Magento/Customer/_files/customer.php + * @magentoDataFixture Magento/Catalog/_files/product_out_of_stock_with_multiselect_attribute.php + * + * @return void + */ + public function testUpdateItemNotSpecifyAsWishListItem(): void + { + $product = $this->productRepository->get('simple_ms_out_of_stock'); + $this->customerSession->setCustomerId(1); + $this->performUpdateWishListItemRequest(['product' => $product->getId()]); + $message = $this->escaper->escapeHtml("We can't specify a wish list item."); + $this->assertSessionMessages($this->equalTo([(string)__($message)]), MessageInterface::TYPE_ERROR); + $this->assertRedirect($this->stringContains('wishlist/index/index/wishlist_id/')); + } + + /** + * Perform request update wish list item. + * + * @param array $params + * @return void + */ + private function performUpdateWishListItemRequest(array $params): void + { + $this->getRequest()->setParams($params)->setMethod(HttpRequest::METHOD_POST); + $this->dispatch('wishlist/index/updateItemOptions'); + } + + /** + * Assert updated item in wish list. + * + * @param Item $item + * @param array $expectedData + * @return void + */ + private function assertUpdatedItem(Item $item, array $expectedData): void + { + $this->assertEquals($expectedData['qty'], $item->getQty()); + $buyRequestOption = $this->json->unserialize($item->getOptionByCode('info_buyRequest')->getValue()); + foreach ($expectedData as $key => $value) { + $this->assertEquals($value, $buyRequestOption[$key]); + } + } + + /** + * Perform configurable option to select. + * + * @param ProductInterface $product + * @return array + */ + private function performConfigurableOption(ProductInterface $product): array + { + $configurableOptions = $product->getTypeInstance()->getConfigurableOptions($product); + $attributeId = key($configurableOptions); + $option = reset($configurableOptions[$attributeId]); + + return [$attributeId => $option['value_index']]; + } +} diff --git a/dev/tests/integration/testsuite/Magento/Wishlist/Controller/Index/UpdateTest.php b/dev/tests/integration/testsuite/Magento/Wishlist/Controller/Index/UpdateTest.php new file mode 100644 index 0000000000000..8126ec71eb7db --- /dev/null +++ b/dev/tests/integration/testsuite/Magento/Wishlist/Controller/Index/UpdateTest.php @@ -0,0 +1,98 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +declare(strict_types=1); + +namespace Magento\Wishlist\Controller\Index; + +use Magento\Customer\Model\Session; +use Magento\Framework\App\Request\Http as HttpRequest; +use Magento\Framework\Message\MessageInterface; +use Magento\TestFramework\TestCase\AbstractController; +use Magento\TestFramework\Wishlist\Model\GetWishlistByCustomerId; + +/** + * Test for update wish list item. + * + * @magentoDbIsolation enabled + * @magentoAppArea frontend + * @magentoDataFixture Magento/Wishlist/_files/wishlist.php + */ +class UpdateTest extends AbstractController +{ + /** @var Session */ + private $customerSession; + + /** @var GetWishlistByCustomerId */ + private $getWishlistByCustomerId; + + /** + * @inheritdoc + */ + protected function setUp() + { + parent::setUp(); + + $this->customerSession = $this->_objectManager->get(Session::class); + $this->getWishlistByCustomerId = $this->_objectManager->get(GetWishlistByCustomerId::class); + } + + /** + * @inheritdoc + */ + protected function tearDown() + { + $this->customerSession->setCustomerId(null); + + parent::tearDown(); + } + + /** + * @return void + */ + public function testUpdateWishListItem(): void + { + $this->customerSession->setCustomerId(1); + $item = $this->getWishlistByCustomerId->getItemBySku(1, 'simple'); + $this->assertNotNull($item); + $params = ['description' => [$item->getId() => 'Some description.'], 'qty' => [$item->getId() => 5]]; + $this->performUpdateWishListItemRequest($params); + $message = sprintf("%s has been updated in your Wish List.", $item->getProduct()->getName()); + $this->assertSessionMessages($this->equalTo([(string)__($message)]), MessageInterface::TYPE_SUCCESS); + $this->assertRedirect($this->stringContains('wishlist/index/index/wishlist_id/' . $item->getWishlistId())); + $updatedItem = $this->getWishlistByCustomerId->getItemBySku(1, 'simple'); + $this->assertNotNull($updatedItem); + $this->assertEquals(5, $updatedItem->getQty()); + $this->assertEquals('Some description.', $updatedItem->getDescription()); + } + + /** + * @return void + */ + public function testUpdateWishListItemZeroQty(): void + { + $this->customerSession->setCustomerId(1); + $item = $this->getWishlistByCustomerId->getItemBySku(1, 'simple'); + $this->assertNotNull($item); + $params = ['description' => [$item->getId() => ''], 'qty' => [$item->getId() => 0]]; + $this->performUpdateWishListItemRequest($params); + $message = sprintf("%s has been updated in your Wish List.", $item->getProduct()->getName()); + $this->assertSessionMessages($this->equalTo([(string)__($message)]), MessageInterface::TYPE_SUCCESS); + $this->assertRedirect($this->stringContains('wishlist/index/index/wishlist_id/' . $item->getWishlistId())); + $this->assertCount(0, $this->getWishlistByCustomerId->execute(1)->getItemCollection()); + } + + /** + * Perform update wish list item request. + * + * @param array $params + * @return void + */ + private function performUpdateWishListItemRequest(array $params): void + { + $this->getRequest()->setPostValue($params)->setMethod(HttpRequest::METHOD_POST); + $this->dispatch('wishlist/index/update'); + } +} diff --git a/dev/tests/integration/testsuite/Magento/Wishlist/Controller/IndexTest.php b/dev/tests/integration/testsuite/Magento/Wishlist/Controller/IndexTest.php deleted file mode 100644 index d225b40dc39da..0000000000000 --- a/dev/tests/integration/testsuite/Magento/Wishlist/Controller/IndexTest.php +++ /dev/null @@ -1,190 +0,0 @@ -<?php -/** - * Copyright © Magento, Inc. All rights reserved. - * See COPYING.txt for license details. - */ -namespace Magento\Wishlist\Controller; - -/** - * @SuppressWarnings(PHPMD.CouplingBetweenObjects) - */ -class IndexTest extends \Magento\TestFramework\TestCase\AbstractController -{ - /** - * @var \Magento\Customer\Model\Session - */ - protected $_customerSession; - - /** - * @var \Magento\Framework\Message\ManagerInterface - */ - protected $_messages; - - /** - * @var \Magento\Customer\Helper\View - */ - protected $_customerViewHelper; - - protected function setUp() - { - parent::setUp(); - $logger = $this->createMock(\Psr\Log\LoggerInterface::class); - $this->_customerSession = \Magento\TestFramework\Helper\Bootstrap::getObjectManager()->get( - \Magento\Customer\Model\Session::class, - [$logger] - ); - /** @var \Magento\Customer\Api\AccountManagementInterface $service */ - $service = \Magento\TestFramework\Helper\Bootstrap::getObjectManager()->create( - \Magento\Customer\Api\AccountManagementInterface::class - ); - $customer = $service->authenticate('customer@example.com', 'password'); - $this->_customerSession->setCustomerDataAsLoggedIn($customer); - - $this->_customerViewHelper = $this->_objectManager->create(\Magento\Customer\Helper\View::class); - - $this->_messages = \Magento\TestFramework\Helper\Bootstrap::getObjectManager()->get( - \Magento\Framework\Message\ManagerInterface::class - ); - } - - protected function tearDown() - { - $this->_customerSession->logout(); - $this->_customerSession = null; - parent::tearDown(); - } - - /** - * Verify wishlist view action - * - * The following is verified: - * - \Magento\Wishlist\Model\ResourceModel\Item\Collection - * - \Magento\Wishlist\Block\Customer\Wishlist - * - \Magento\Wishlist\Block\Customer\Wishlist\Items - * - \Magento\Wishlist\Block\Customer\Wishlist\Item\Column - * - \Magento\Wishlist\Block\Customer\Wishlist\Item\Column\Cart - * - \Magento\Wishlist\Block\Customer\Wishlist\Item\Column\Comment - * - \Magento\Wishlist\Block\Customer\Wishlist\Button - * - that \Magento\Wishlist\Block\Customer\Wishlist\Item\Options doesn't throw a fatal error - * - * @magentoDataFixture Magento/Wishlist/_files/wishlist.php - */ - public function testItemColumnBlock() - { - $this->dispatch('wishlist/index/index'); - $body = $this->getResponse()->getBody(); - $this->assertEquals( - 1, - \Magento\TestFramework\Helper\Xpath::getElementsCountForXpath( - '//img[contains(@src, "small_image.jpg") and @alt = "Simple Product"]', - $body - ) - ); - $this->assertEquals( - 1, - \Magento\TestFramework\Helper\Xpath::getElementsCountForXpath( - '//textarea[contains(@name, "description")]', - $body - ) - ); - } - - /** - * @magentoDataFixture Magento/Catalog/_files/product_simple_xss.php - * @magentoDataFixture Magento/Customer/_files/customer.php - * @magentoAppArea frontend - */ - public function testAddActionProductNameXss() - { - /** @var \Magento\Framework\Data\Form\FormKey $formKey */ - $formKey = $this->_objectManager->get(\Magento\Framework\Data\Form\FormKey::class); - $this->getRequest()->setMethod('POST'); - $this->getRequest()->setPostValue( - [ - 'form_key' => $formKey->getFormKey(), - ] - ); - - /** @var \Magento\Catalog\Api\ProductRepositoryInterface $productRepository */ - $productRepository = \Magento\TestFramework\Helper\Bootstrap::getObjectManager() - ->create(\Magento\Catalog\Api\ProductRepositoryInterface::class); - - $product = $productRepository->get('product-with-xss'); - - $this->dispatch('wishlist/index/add/product/' . $product->getId() . '?nocookie=1'); - - $this->assertSessionMessages( - $this->equalTo( - [ - "\n<script>alert("xss");</script> has been added to your Wish List. " - . 'Click <a href="http://localhost/index.php/">here</a> to continue shopping.', - ] - ), - \Magento\Framework\Message\MessageInterface::TYPE_SUCCESS - ); - } - - /** - * @magentoDbIsolation disabled - * @magentoDataFixture Magento/Wishlist/_files/wishlist_with_product_qty_increments.php - */ - public function testAllcartAction() - { - $formKey = $this->_objectManager->get(\Magento\Framework\Data\Form\FormKey::class)->getFormKey(); - $this->getRequest()->setMethod('POST'); - $this->getRequest()->setParam('form_key', $formKey); - $this->dispatch('wishlist/index/allcart'); - - /** @var \Magento\Checkout\Model\Cart $cart */ - $cart = $this->_objectManager->get(\Magento\Checkout\Model\Cart::class); - $quoteCount = $cart->getQuote()->getItemsCollection()->count(); - - $this->assertEquals(0, $quoteCount); - $this->assertSessionMessages( - $this->contains( - htmlspecialchars( - 'You can buy this product only in quantities of 5 at a time for "Simple Product".' - ) - ), - \Magento\Framework\Message\MessageInterface::TYPE_ERROR - ); - } - - /** - * @magentoDataFixture Magento/Wishlist/_files/wishlist.php - */ - public function testSendAction() - { - \Magento\TestFramework\Helper\Bootstrap::getInstance() - ->loadArea(\Magento\Framework\App\Area::AREA_FRONTEND); - - $request = [ - 'form_key' => $this->_objectManager->get(\Magento\Framework\Data\Form\FormKey::class)->getFormKey(), - 'emails' => 'test@tosend.com', - 'message' => 'message', - 'rss_url' => null, // no rss - ]; - - $this->getRequest()->setPostValue($request); - $this->getRequest()->setMethod('POST'); - - $this->_objectManager->get(\Magento\Framework\Registry::class)->register( - 'wishlist', - $this->_objectManager->get(\Magento\Wishlist\Model\Wishlist::class)->loadByCustomerId(1) - ); - $this->dispatch('wishlist/index/send'); - - /** @var \Magento\TestFramework\Mail\Template\TransportBuilderMock $transportBuilder */ - $transportBuilder = $this->_objectManager->get( - \Magento\TestFramework\Mail\Template\TransportBuilderMock::class - ); - - $actualResult = $transportBuilder->getSentMessage()->getBody()->getParts()[0]->getRawContent(); - - $this->assertStringMatchesFormat( - '%A' . $this->_customerViewHelper->getCustomerName($this->_customerSession->getCustomerDataObject()) - . ' wants to share this Wish List%A', - $actualResult - ); - } -} diff --git a/dev/tests/integration/testsuite/Magento/Wishlist/Model/ItemTest.php b/dev/tests/integration/testsuite/Magento/Wishlist/Model/ItemTest.php index 896b59c7983fe..afd15929ae685 100644 --- a/dev/tests/integration/testsuite/Magento/Wishlist/Model/ItemTest.php +++ b/dev/tests/integration/testsuite/Magento/Wishlist/Model/ItemTest.php @@ -3,57 +3,94 @@ * Copyright © Magento, Inc. All rights reserved. * See COPYING.txt for license details. */ +declare(strict_types=1); namespace Magento\Wishlist\Model; +use Magento\Catalog\Api\ProductRepositoryInterface; +use Magento\Catalog\Model\Product\Exception as ProductException; +use Magento\Checkout\Model\CartFactory; +use Magento\Framework\App\ObjectManager; +use Magento\Framework\DataObjectFactory; +use Magento\TestFramework\Wishlist\Model\GetWishlistByCustomerId; +use Magento\Wishlist\Model\Item\OptionFactory; +use PHPUnit\Framework\TestCase; + /** - * Item test class. + * Tests for wish list item model. + * + * @magentoDbIsolation enabled + * @magentoAppIsolation disabled */ -class ItemTest extends \PHPUnit\Framework\TestCase +class ItemTest extends TestCase { - /** - * @var \Magento\Framework\App\ObjectManager - */ + /** @var ObjectManager */ private $objectManager; - /** - * @var \Magento\Wishlist\Model\Item - */ + /** @var Item */ private $model; + /** @var DataObjectFactory */ + private $dataObjectFactory; + + /** @var OptionFactory */ + private $optionFactory; + + /** @var GetWishlistByCustomerId */ + private $getWishlistByCustomerId; + + /** @var CartFactory */ + private $cartFactory; + + /** @var ProductRepositoryInterface */ + private $productRepository; + + /** @var ItemFactory */ + private $itemFactory; + /** - * {@inheritDoc} + * @inheritdoc */ public function setUp() { - $this->objectManager = \Magento\Framework\App\ObjectManager::getInstance(); - $this->model = $this->objectManager->get(\Magento\Wishlist\Model\Item::class); + parent::setUp(); + + $this->objectManager = ObjectManager::getInstance(); + $this->dataObjectFactory = $this->objectManager->get(DataObjectFactory::class); + $this->model = $this->objectManager->get(Item::class); + $this->itemFactory = $this->objectManager->get(ItemFactory::class); + $this->optionFactory = $this->objectManager->get(OptionFactory::class); + $this->getWishlistByCustomerId = $this->objectManager->get(GetWishlistByCustomerId::class); + $this->cartFactory = $this->objectManager->get(CartFactory::class); + $this->productRepository = $this->objectManager->get(ProductRepositoryInterface::class); + $this->productRepository->cleanCache(); } /** - * @magentoDataFixture Magento/Catalog/_files/product_simple.php - * @magentoAppIsolation enabled - * @magentoDbIsolation enabled + * @inheritdoc */ - public function testBuyRequest() + protected function tearDown() { - /** @var \Magento\Catalog\Api\ProductRepositoryInterface $productRepository */ - $productRepository = $this->objectManager->get(\Magento\Catalog\Api\ProductRepositoryInterface::class); - $product = $productRepository->getById(1); + $this->cartFactory->create()->truncate(); - /** @var \Magento\Wishlist\Model\Item\Option $option */ - $option = $this->objectManager->create( - \Magento\Wishlist\Model\Item\Option::class, + parent::tearDown(); + } + + /** + * @magentoDataFixture Magento/Catalog/_files/product_simple.php + * + * @return void + */ + public function testBuyRequest(): void + { + $product = $this->productRepository->get('simple'); + $option = $this->optionFactory->create( ['data' => ['code' => 'info_buyRequest', 'value' => '{"qty":23}']] ); $option->setProduct($product); $this->model->addOption($option); - - // Assert getBuyRequest method $buyRequest = $this->model->getBuyRequest(); $this->assertEquals($buyRequest->getOriginalQty(), 23); - - // Assert mergeBuyRequest method $this->model->mergeBuyRequest(['qty' => 11, 'additional_data' => 'some value']); $buyRequest = $this->model->getBuyRequest(); $this->assertEquals( @@ -62,18 +99,87 @@ public function testBuyRequest() ); } - public function testSetBuyRequest() + /** + * @return void + */ + public function testSetBuyRequest(): void { - $buyRequest = $this->objectManager->create( - \Magento\Framework\DataObject::class, + $buyRequest = $this->dataObjectFactory->create( ['data' => ['field_1' => 'some data', 'field_2' => 234]] ); - $this->model->setBuyRequest($buyRequest); - - $this->assertEquals( + $this->assertJsonStringEqualsJsonString( '{"field_1":"some data","field_2":234,"id":null}', $this->model->getData('buy_request') ); } + + /** + * @magentoDataFixture Magento/Wishlist/_files/wishlist_with_simple_product.php + * @magentoDbIsolation disabled + * + * @return void + */ + public function testAddItemToCart(): void + { + $item = $this->getWishlistByCustomerId->getItemBySku(1, 'simple-1'); + $this->assertNotNull($item); + $cart = $this->cartFactory->create(); + $this->assertTrue($item->addToCart($cart)); + $this->assertCount(1, $cart->getQuote()->getItemsCollection()); + $this->assertCount(1, $this->getWishlistByCustomerId->execute(1)->getItemCollection()); + } + + /** + * @magentoDataFixture Magento/Wishlist/_files/wishlist_with_simple_product.php + * @magentoDbIsolation disabled + * + * @return void + */ + public function testAddItemToCartAndDeleteFromWishList(): void + { + $item = $this->getWishlistByCustomerId->getItemBySku(1, 'simple-1'); + $this->assertNotNull($item); + $cart = $this->cartFactory->create(); + $item->addToCart($cart, true); + $this->assertCount(1, $cart->getQuote()->getItemsCollection()); + $this->assertCount(0, $this->getWishlistByCustomerId->execute(1)->getItemCollection()); + } + + /** + * @magentoDataFixture Magento/Catalog/_files/product_simple_out_of_stock.php + * + * @return void + */ + public function testAddOutOfStockItemToCart(): void + { + $product = $this->productRepository->get('simple-out-of-stock'); + $item = $this->itemFactory->create()->setProduct($product); + $this->expectExceptionObject(new ProductException(__('Product is not salable.'))); + $item->addToCart($this->cartFactory->create()); + } + + /** + * @magentoDataFixture Magento/Catalog/_files/multiple_products.php + * + * @return void + */ + public function testAddDisabledItemToCart(): void + { + $product = $this->productRepository->get('simple3'); + $item = $this->itemFactory->create()->setProduct($product); + $this->assertFalse($item->addToCart($this->cartFactory->create())); + } + + /** + * @magentoDataFixture Magento/Catalog/_files/simple_products_not_visible_individually.php + * + * @return void + */ + public function testAddNotVisibleItemToCart(): void + { + $product = $this->productRepository->get('simple_not_visible_1'); + $item = $this->itemFactory->create()->setProduct($product)->setStoreId($product->getStoreId()); + $this->assertFalse($item->addToCart($this->cartFactory->create())); + } } diff --git a/dev/tests/integration/testsuite/Magento/Wishlist/Model/WishlistTest.php b/dev/tests/integration/testsuite/Magento/Wishlist/Model/WishlistTest.php index b684da05dd254..f12299b32945d 100644 --- a/dev/tests/integration/testsuite/Magento/Wishlist/Model/WishlistTest.php +++ b/dev/tests/integration/testsuite/Magento/Wishlist/Model/WishlistTest.php @@ -3,63 +3,86 @@ * Copyright © Magento, Inc. All rights reserved. * See COPYING.txt for license details. */ +declare(strict_types=1); + namespace Magento\Wishlist\Model; +use Magento\Bundle\Model\Product\OptionList; use Magento\Catalog\Api\ProductRepositoryInterface; use Magento\Catalog\Model\Product\Attribute\Source\Status as ProductStatus; use Magento\Framework\App\ObjectManager; -use Magento\Framework\DataObject; +use Magento\Framework\DataObjectFactory; +use Magento\Framework\Exception\LocalizedException; +use Magento\Framework\Serialize\SerializerInterface; +use Magento\TestFramework\Wishlist\Model\GetWishlistByCustomerId; +use PHPUnit\Framework\TestCase; -class WishlistTest extends \PHPUnit\Framework\TestCase +/** + * Tests for wish list model. + * + * @magentoDbIsolation enabled + * @magentoAppIsolation disabled + */ +class WishlistTest extends TestCase { - /** - * @var ObjectManager - */ + /** @var ObjectManager */ private $objectManager; - /** - * @var Wishlist - */ - private $wishlist; + /** @var WishlistFactory */ + private $wishlistFactory; + + /** @var GetWishlistByCustomerId */ + private $getWishlistByCustomerId; + + /** @var ProductRepositoryInterface */ + private $productRepository; + + /** @var DataObjectFactory */ + private $dataObjectFactory; + + /** @var SerializerInterface */ + private $json; /** - * {@inheritDoc} + * @inheritdoc */ protected function setUp() { $this->objectManager = ObjectManager::getInstance(); - $this->wishlist = $this->objectManager->get(Wishlist::class); + $this->wishlistFactory = $this->objectManager->get(WishlistFactory::class); + $this->getWishlistByCustomerId = $this->objectManager->get(GetWishlistByCustomerId::class); + $this->productRepository = $this->objectManager->get(ProductRepositoryInterface::class); + $this->productRepository->cleanCache(); + $this->dataObjectFactory = $this->objectManager->get(DataObjectFactory::class); + $this->json = $this->objectManager->get(SerializerInterface::class); } /** * @magentoDataFixture Magento/Catalog/_files/product_simple.php * @magentoDataFixture Magento/Customer/_files/customer.php - * @magentoAppIsolation enabled - * @magentoDbIsolation enabled + * + * @return void */ - public function testAddNewItem() + public function testAddNewItem(): void { $productSku = 'simple'; $customerId = 1; - /** @var ProductRepositoryInterface $productRepository */ - $productRepository = $this->objectManager->create(ProductRepositoryInterface::class); - $product = $productRepository->get($productSku); - $this->wishlist->loadByCustomerId($customerId, true); - $this->wishlist->addNewItem( + $product = $this->productRepository->get($productSku); + $wishlist = $this->getWishlistByCustomerId->execute($customerId); + $wishlist->addNewItem( $product, '{"qty":2}' ); - $this->wishlist->addNewItem( + $wishlist->addNewItem( $product, ['qty' => 3] ); - $this->wishlist->addNewItem( + $wishlist->addNewItem( $product, - new DataObject(['qty' => 4]) + $this->dataObjectFactory->create(['data' => ['qty' => 4]]) ); - $this->wishlist->addNewItem($product); - /** @var Item $wishlistItem */ - $wishlistItem = $this->wishlist->getItemCollection()->getFirstItem(); + $wishlist->addNewItem($product); + $wishlistItem = $this->getWishlistByCustomerId->getItemBySku(1, $productSku); $this->assertInstanceOf(Item::class, $wishlistItem); $this->assertEquals($wishlistItem->getQty(), 10); } @@ -67,58 +90,169 @@ public function testAddNewItem() /** * @magentoDataFixture Magento/Catalog/_files/product_simple.php * @magentoDataFixture Magento/Customer/_files/customer.php - * @magentoAppIsolation enabled - * @magentoDbIsolation enabled - * @expectedException \InvalidArgumentException - * @expectedExceptionMessage Invalid wishlist item configuration. + * + * @return void */ - public function testAddNewItemInvalidWishlistItemConfiguration() + public function testAddNewItemInvalidWishlistItemConfiguration(): void { $productSku = 'simple'; $customerId = 1; - /** @var ProductRepositoryInterface $productRepository */ - $productRepository = $this->objectManager->create(ProductRepositoryInterface::class); - $product = $productRepository->get($productSku); - $this->wishlist->loadByCustomerId($customerId, true); - $this->wishlist->addNewItem( - $product, - '{"qty":2' - ); - $this->wishlist->addNewItem($product); + $product = $this->productRepository->get($productSku); + $wishlist = $this->getWishlistByCustomerId->execute($customerId); + $this->expectExceptionObject(new \InvalidArgumentException('Invalid wishlist item configuration.')); + $wishlist->addNewItem($product, '{"qty":2'); + } + + /** + * @magentoDataFixture Magento/Wishlist/_files/wishlist.php + * + * @return void + */ + public function testGetItemCollection(): void + { + $productSku = 'simple'; + $item = $this->getWishlistByCustomerId->getItemBySku(1, $productSku); + $this->assertNotNull($item); } /** - * @magentoDbIsolation disabled * @magentoDataFixture Magento/Wishlist/_files/wishlist.php + * + * @return void */ - public function testGetItemCollection() + public function testGetItemCollectionWithDisabledProduct(): void { $productSku = 'simple'; $customerId = 1; + $product = $this->productRepository->get($productSku); + $product->setStatus(ProductStatus::STATUS_DISABLED); + $this->productRepository->save($product); + $this->assertEmpty($this->getWishlistByCustomerId->execute($customerId)->getItemCollection()->getItems()); + } - $this->wishlist->loadByCustomerId($customerId, true); - $itemCollection = $this->wishlist->getItemCollection(); - /** @var \Magento\Wishlist\Model\Item $item */ - $item = $itemCollection->getFirstItem(); - $this->assertEquals($productSku, $item->getProduct()->getSku()); + /** + * @magentoDataFixture Magento/ConfigurableProduct/_files/configurable_product_with_two_child_products.php + * @magentoDataFixture Magento/Customer/_files/customer.php + * @magentoDbIsolation disabled + * + * @return void + */ + public function testAddConfigurableProductToWishList(): void + { + $configurableProduct = $this->productRepository->get('Configurable product'); + $configurableOptions = $configurableProduct->getTypeInstance()->getConfigurableOptions($configurableProduct); + $attributeId = key($configurableOptions); + $option = reset($configurableOptions[$attributeId]); + $buyRequest = ['super_attribute' => [$attributeId => $option['value_index']]]; + $wishlist = $this->getWishlistByCustomerId->execute(1); + $wishlist->addNewItem($configurableProduct, $buyRequest); + $item = $this->getWishlistByCustomerId->getItemBySku(1, 'Configurable product'); + $this->assertNotNull($item); + $this->assertWishListItem($item, $option['sku'], $buyRequest); } /** + * @magentoDataFixture Magento/Bundle/_files/fixed_bundle_product_without_discounts.php + * @magentoDataFixture Magento/Customer/_files/customer.php * @magentoDbIsolation disabled + * + * @return void + */ + public function testAddBundleProductToWishList(): void + { + $bundleProduct = $this->productRepository->get('fixed_bundle_product_without_discounts'); + $bundleOptionList = $this->objectManager->create(OptionList::class); + $bundleOptions = $bundleOptionList->getItems($bundleProduct); + $option = reset($bundleOptions); + $productLinks = $option->getProductLinks(); + $this->assertNotNull($productLinks[0]); + $buyRequest = ['bundle_option' => [$option->getOptionId() => $productLinks[0]->getId()]]; + $skuWithChosenOption = implode('-', [$bundleProduct->getSku(), $productLinks[0]->getSku()]); + $wishlist = $this->getWishlistByCustomerId->execute(1); + $wishlist->addNewItem($bundleProduct, $buyRequest); + $item = $this->getWishlistByCustomerId->getItemBySku(1, 'fixed_bundle_product_without_discounts'); + $this->assertNotNull($item); + $this->assertWishListItem($item, $skuWithChosenOption, $buyRequest); + } + + /** + * @magentoDataFixture Magento/Customer/_files/customer.php + * + * @return void + */ + public function testAddNotExistingItemToWishList(): void + { + $wishlist = $this->getWishlistByCustomerId->execute(1); + $this->expectExceptionObject(new LocalizedException(__('Cannot specify product.'))); + $wishlist->addNewItem(989); + } + + /** + * @magentoDataFixture Magento/Customer/_files/customer.php + * @magentoDataFixture Magento/Catalog/_files/product_out_of_stock_with_multiselect_attribute.php + * + * @return void + */ + public function testAddOutOfStockItemToWishList(): void + { + $product = $this->productRepository->get('simple_ms_out_of_stock'); + $wishlist = $this->getWishlistByCustomerId->execute(1); + $this->expectExceptionObject(new LocalizedException(__('Cannot add product without stock to wishlist.'))); + $wishlist->addNewItem($product); + } + + /** * @magentoDataFixture Magento/Wishlist/_files/wishlist.php + * + * @return void */ - public function testGetItemCollectionWithDisabledProduct() + public function testUpdateItemQtyInWishList(): void { - $productSku = 'simple'; - $customerId = 1; + $wishlist = $this->getWishlistByCustomerId->execute(1); + $item = $this->getWishlistByCustomerId->getItemBySku(1, 'simple'); + $this->assertNotNull($item); + $buyRequest = $this->dataObjectFactory->create(['data' => ['qty' => 55]]); + $wishlist->updateItem($item->getId(), $buyRequest); + $updatedItem = $this->getWishlistByCustomerId->getItemBySku(1, 'simple'); + $this->assertEquals(55, $updatedItem->getQty()); + } - $productRepository = $this->objectManager->get(ProductRepositoryInterface::class); - $product = $productRepository->get($productSku); - $product->setStatus(ProductStatus::STATUS_DISABLED); - $productRepository->save($product); + /** + * @return void + */ + public function testUpdateNotExistingItemInWishList(): void + { + $this->expectExceptionObject(new LocalizedException(__('We can\'t specify a wish list item.'))); + $this->wishlistFactory->create()->updateItem(989, []); + } - $this->wishlist->loadByCustomerId($customerId, true); - $itemCollection = $this->wishlist->getItemCollection(); - $this->assertEmpty($itemCollection->getItems()); + /** + * @magentoDataFixture Magento/Wishlist/_files/wishlist.php + * + * @return void + */ + public function testUpdateNotExistingProductInWishList(): void + { + $wishlist = $this->getWishlistByCustomerId->execute(1); + $item = $this->getWishlistByCustomerId->getItemBySku(1, 'simple'); + $this->assertNotNull($item); + $item->getProduct()->setId(null); + $this->expectExceptionObject(new LocalizedException(__('The product does not exist.'))); + $wishlist->updateItem($item, []); + } + + /** + * Assert item in wish list. + * + * @param Item $item + * @param string $itemSku + * @param array $buyRequest + * @return void + */ + private function assertWishListItem(Item $item, string $itemSku, array $buyRequest): void + { + $this->assertEquals($itemSku, $item->getProduct()->getSku()); + $buyRequestOption = $item->getOptionByCode('info_buyRequest'); + $this->assertEquals($buyRequest, $this->json->unserialize($buyRequestOption->getValue())); } } diff --git a/dev/tests/integration/testsuite/Magento/Wishlist/_files/wishlist_shared_rollback.php b/dev/tests/integration/testsuite/Magento/Wishlist/_files/wishlist_shared_rollback.php new file mode 100644 index 0000000000000..24bbccd5739f4 --- /dev/null +++ b/dev/tests/integration/testsuite/Magento/Wishlist/_files/wishlist_shared_rollback.php @@ -0,0 +1,9 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +declare(strict_types=1); + +require __DIR__ . '/../../../Magento/Catalog/_files/product_simple_rollback.php'; +require __DIR__ . '/../../../Magento/Customer/_files/customer_rollback.php'; diff --git a/dev/tests/integration/testsuite/Magento/Wishlist/_files/wishlist_with_configurable_product.php b/dev/tests/integration/testsuite/Magento/Wishlist/_files/wishlist_with_configurable_product.php new file mode 100644 index 0000000000000..c67c3e32f24bc --- /dev/null +++ b/dev/tests/integration/testsuite/Magento/Wishlist/_files/wishlist_with_configurable_product.php @@ -0,0 +1,17 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +declare(strict_types=1); + +use Magento\Wishlist\Model\WishlistFactory; + +require __DIR__ . '/../../../Magento/Customer/_files/customer.php'; +require __DIR__ . '/../../../Magento/ConfigurableProduct/_files/configurable_product_with_two_child_products.php'; + +$wishlistFactory = $objectManager->get(WishlistFactory::class); +$wishlist = $wishlistFactory->create(); +$wishlist->loadByCustomerId($customer->getId(), true); +$product = $productRepository->get('Configurable product'); +$wishlist->addNewItem($product); diff --git a/dev/tests/integration/testsuite/Magento/Wishlist/_files/wishlist_with_configurable_product_rollback.php b/dev/tests/integration/testsuite/Magento/Wishlist/_files/wishlist_with_configurable_product_rollback.php new file mode 100644 index 0000000000000..776d17137db30 --- /dev/null +++ b/dev/tests/integration/testsuite/Magento/Wishlist/_files/wishlist_with_configurable_product_rollback.php @@ -0,0 +1,10 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +declare(strict_types=1); + +require __DIR__ . '/../../../Magento/Customer/_files/customer_rollback.php'; +require __DIR__ + . '/../../../Magento/ConfigurableProduct/_files/configurable_product_with_two_child_products_rollback.php'; diff --git a/dev/tests/integration/testsuite/Magento/Wishlist/_files/wishlist_with_product_qty_three.php b/dev/tests/integration/testsuite/Magento/Wishlist/_files/wishlist_with_product_qty_three.php new file mode 100644 index 0000000000000..11752d5ba39f3 --- /dev/null +++ b/dev/tests/integration/testsuite/Magento/Wishlist/_files/wishlist_with_product_qty_three.php @@ -0,0 +1,18 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +declare(strict_types=1); + +use Magento\Wishlist\Model\WishlistFactory; +use Magento\Framework\Serialize\SerializerInterface; + +require __DIR__ . '/../../../Magento/Customer/_files/customer.php'; +require __DIR__ . '/../../../Magento/Catalog/_files/product_simple.php'; + +$json = $objectManager->get(SerializerInterface::class); +$wishlistFactory = $objectManager->get(WishlistFactory::class); +$wishlist = $wishlistFactory->create(); +$wishlist->loadByCustomerId($customer->getId(), true); +$wishlist->addNewItem($product, $json->serialize(['qty' => 3])); diff --git a/dev/tests/integration/testsuite/Magento/Wishlist/_files/wishlist_with_product_qty_three_rollback.php b/dev/tests/integration/testsuite/Magento/Wishlist/_files/wishlist_with_product_qty_three_rollback.php new file mode 100644 index 0000000000000..24bbccd5739f4 --- /dev/null +++ b/dev/tests/integration/testsuite/Magento/Wishlist/_files/wishlist_with_product_qty_three_rollback.php @@ -0,0 +1,9 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +declare(strict_types=1); + +require __DIR__ . '/../../../Magento/Catalog/_files/product_simple_rollback.php'; +require __DIR__ . '/../../../Magento/Customer/_files/customer_rollback.php'; diff --git a/dev/tests/integration/testsuite/Magento/Wishlist/_files/wishlist_with_simple_product.php b/dev/tests/integration/testsuite/Magento/Wishlist/_files/wishlist_with_simple_product.php new file mode 100644 index 0000000000000..61448c54dd7ab --- /dev/null +++ b/dev/tests/integration/testsuite/Magento/Wishlist/_files/wishlist_with_simple_product.php @@ -0,0 +1,16 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +declare(strict_types=1); + +use Magento\Wishlist\Model\WishlistFactory; + +require __DIR__ . '/../../../Magento/Customer/_files/customer.php'; +require __DIR__ . '/../../../Magento/Catalog/_files/product_simple_duplicated.php'; + +$wishlistFactory = $objectManager->get(WishlistFactory::class); +$wishlist = $wishlistFactory->create(); +$wishlist->loadByCustomerId($customer->getId(), true); +$wishlist->addNewItem($product); diff --git a/dev/tests/integration/testsuite/Magento/Wishlist/_files/wishlist_with_simple_product_rollback.php b/dev/tests/integration/testsuite/Magento/Wishlist/_files/wishlist_with_simple_product_rollback.php new file mode 100644 index 0000000000000..ffa99feba6652 --- /dev/null +++ b/dev/tests/integration/testsuite/Magento/Wishlist/_files/wishlist_with_simple_product_rollback.php @@ -0,0 +1,9 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +declare(strict_types=1); + +require __DIR__ . '/../../../Magento/Catalog/_files/product_simple_duplicated_rollback.php'; +require __DIR__ . '/../../../Magento/Customer/_files/customer_rollback.php'; diff --git a/dev/tests/js/jasmine/tests/app/code/Magento/Bundle/base/js/price-bundle.test.js b/dev/tests/js/jasmine/tests/app/code/Magento/Bundle/base/js/price-bundle.test.js new file mode 100644 index 0000000000000..04dd5a9b51407 --- /dev/null +++ b/dev/tests/js/jasmine/tests/app/code/Magento/Bundle/base/js/price-bundle.test.js @@ -0,0 +1,73 @@ +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ + +define([ + 'jquery', + 'Magento_Bundle/js/price-bundle', + 'Magento_Catalog/js/price-box' +], function ($) { + 'use strict'; + + describe('Magento_Bundle/js/price-bundle', function () { + + var htmlContainer; + + beforeEach(function () { + htmlContainer = $('<div class="price-final_price" data-role="priceBox"><ul class="price-box"></ul></div>'); + }); + + afterEach(function () { + htmlContainer.remove(); + }); + + it('Widget extends jQuery object.', function () { + expect($.fn.priceBundle).toBeDefined(); + }); + + it('Check _updatePriceBox method call.', function () { + + spyOn($.mage.priceBundle.prototype, '_updatePriceBox'); + + htmlContainer.priceBundle(); + + expect($.mage.priceBundle.prototype._updatePriceBox).toEqual(jasmine.any(Function)); + expect($.mage.priceBundle.prototype._updatePriceBox).toHaveBeenCalledTimes(1); + }); + + it('Check _updatePriceBox method call after priceBox was initialized.', function () { + spyOn($.mage.priceBundle.prototype, '_updatePriceBox').and.callThrough(); + htmlContainer.priceBundle(); + $('.price-box', htmlContainer).priceBox(); + expect($.mage.priceBundle.prototype._updatePriceBox).toEqual(jasmine.any(Function)); + expect($.mage.priceBundle.prototype._updatePriceBox).toHaveBeenCalledTimes(2); + }); + + it('Check _applyOptionNodeFix method doesn\'t call after priceBox initialization.', function () { + var optionConfig = { + optionConfig: { + prices: {} + } + }, + priceConfig = { + priceConfig: 10 + }; + + spyOn($.mage.priceBundle.prototype, '_applyOptionNodeFix').and.callThrough(); + htmlContainer.priceBundle(optionConfig); + $('.price-box', htmlContainer).priceBox(priceConfig); + $('.price-box', htmlContainer).trigger('price-box-initialized'); + expect($.mage.priceBundle.prototype._applyOptionNodeFix).toEqual(jasmine.any(Function)); + expect($.mage.priceBundle.prototype._applyOptionNodeFix).toHaveBeenCalledTimes(2); + }); + + it('Check _updatePriceBox method call before priceBox was initialized.', function () { + spyOn($.mage.priceBundle.prototype, '_updatePriceBox').and.callThrough(); + $('.price-box', htmlContainer).priceBox(); + htmlContainer.priceBundle(); + expect($.mage.priceBundle.prototype._updatePriceBox).toEqual(jasmine.any(Function)); + expect($.mage.priceBundle.prototype._updatePriceBox).toHaveBeenCalledTimes(1); + }); + }); +}); diff --git a/dev/tests/js/jasmine/tests/app/code/Magento/Checkout/frontend/js/sidebar.test.js b/dev/tests/js/jasmine/tests/app/code/Magento/Checkout/frontend/js/sidebar.test.js index 7c21a8aae2f26..e9b606eebac3d 100644 --- a/dev/tests/js/jasmine/tests/app/code/Magento/Checkout/frontend/js/sidebar.test.js +++ b/dev/tests/js/jasmine/tests/app/code/Magento/Checkout/frontend/js/sidebar.test.js @@ -67,7 +67,9 @@ define([ sidebar._removeItemAfter(elem); expect(mocks['Magento_Customer/js/customer-data'].get).toHaveBeenCalledWith('cart'); expect(jQuery('body').trigger).toHaveBeenCalledWith('ajax:removeFromCart', { - 'productIds': ['5'] + 'productIds': ['5'], 'productInfo': [{ + 'id': '5' + }] }); }); diff --git a/dev/tests/js/jasmine/tests/app/code/Magento/Checkout/frontend/js/view/shipping.test.js b/dev/tests/js/jasmine/tests/app/code/Magento/Checkout/frontend/js/view/shipping.test.js index 46d9e1974bdb7..f6f4927aaeda2 100644 --- a/dev/tests/js/jasmine/tests/app/code/Magento/Checkout/frontend/js/view/shipping.test.js +++ b/dev/tests/js/jasmine/tests/app/code/Magento/Checkout/frontend/js/view/shipping.test.js @@ -12,7 +12,7 @@ require.config({ } }); -define(['squire', 'ko', 'jquery', 'uiRegistry', 'jquery/validate'], function (Squire, ko, $, registry) { +define(['squire', 'ko', 'jquery', 'jquery/validate'], function (Squire, ko, $) { 'use strict'; var injector = new Squire(), @@ -20,6 +20,16 @@ define(['squire', 'ko', 'jquery', 'uiRegistry', 'jquery/validate'], function (Sq openModal: jasmine.createSpy(), closeModal: jasmine.createSpy() }, + country = { + /** Stub */ + on: function () {}, + + /** Stub */ + get: function () {}, + + /** Stub */ + set: function () {} + }, mocks = { 'Magento_Customer/js/model/customer': { isLoggedIn: ko.observable() @@ -28,17 +38,7 @@ define(['squire', 'ko', 'jquery', 'uiRegistry', 'jquery/validate'], function (Sq 'Magento_Checkout/js/model/address-converter': jasmine.createSpy(), 'Magento_Checkout/js/model/quote': { isVirtual: jasmine.createSpy(), - shippingMethod: ko.observable(), - - /** - * Stub - */ - shippingAddress: function () { - - return { - 'countryId': 'AD' - }; - } + shippingMethod: ko.observable() }, 'Magento_Checkout/js/action/create-shipping-address': jasmine.createSpy().and.returnValue( jasmine.createSpyObj('newShippingAddress', ['getKey']) @@ -62,7 +62,18 @@ define(['squire', 'ko', 'jquery', 'uiRegistry', 'jquery/validate'], function (Sq 'checkoutData', ['setSelectedShippingAddress', 'setNewCustomerShippingAddress', 'setSelectedShippingRate'] ), - 'Magento_Ui/js/lib/registry/registry': registry, + 'Magento_Ui/js/lib/registry/registry': { + async: jasmine.createSpy().and.returnValue(function () {}), + create: jasmine.createSpy(), + set: jasmine.createSpy(), + get: jasmine.createSpy().and.callFake(function (query) { + if (query === 'test.shippingAddress.shipping-address-fieldset.country_id') { + return country; + } else if (query === 'checkout.errors') { + return {}; + } + }) + }, 'Magento_Checkout/js/model/shipping-rate-service': jasmine.createSpy() }, obj; @@ -73,7 +84,6 @@ define(['squire', 'ko', 'jquery', 'uiRegistry', 'jquery/validate'], function (Sq obj = new Constr({ provider: 'provName', name: '', - parentName: 'test', index: '', popUpForm: { options: { @@ -174,16 +184,6 @@ define(['squire', 'ko', 'jquery', 'uiRegistry', 'jquery/validate'], function (Sq describe('"validateShippingInformation" method', function () { it('Check method call on negative cases.', function () { - /* jscs:disable */ - var country = { - on: function () {}, - get: function () {}, - set: function () {} - }; - /* jscs:enable */ - - registry.set('test.shippingAddress.shipping-address-fieldset.country_id', country); - registry.set('checkout.errors', {}); obj.source = { get: jasmine.createSpy().and.returnValue(true), set: jasmine.createSpy(), @@ -199,20 +199,10 @@ define(['squire', 'ko', 'jquery', 'uiRegistry', 'jquery/validate'], function (Sq expect(obj.validateShippingInformation()).toBeFalsy(); }); it('Check method call on positive case.', function () { - /* jscs:disable */ - var country = { - on: function () {}, - get: function () {}, - set: function () {} - }; - /* jscs:enable */ - $('body').append('<form data-role="email-with-possible-login">' + '<input type="text" name="username" />' + '</form>'); - registry.set('test.shippingAddress.shipping-address-fieldset.country_id', country); - registry.set('checkout.errors', {}); obj.source = { get: jasmine.createSpy().and.returnValue(true), set: jasmine.createSpy(), diff --git a/dev/tests/js/jasmine/tests/app/code/Magento/ConfigurableProduct/view/adminhtml/web/js/components/dynamic-rows-configurable.test.js b/dev/tests/js/jasmine/tests/app/code/Magento/ConfigurableProduct/view/adminhtml/web/js/components/dynamic-rows-configurable.test.js new file mode 100644 index 0000000000000..546392d35fe84 --- /dev/null +++ b/dev/tests/js/jasmine/tests/app/code/Magento/ConfigurableProduct/view/adminhtml/web/js/components/dynamic-rows-configurable.test.js @@ -0,0 +1,53 @@ +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ + +/* eslint-disable max-nested-callbacks */ +define([ + 'jquery', + 'Magento_ConfigurableProduct/js/components/dynamic-rows-configurable', + 'Magento_Ui/js/dynamic-rows/dynamic-rows' +], function ($, DynamicRowsConf, DynamicRows) { + 'use strict'; + + describe('Magento_ConfigurableProduct/js/components/dynamic-rows-configurable', function () { + var model; + + beforeEach(function () { + model = new DynamicRowsConf(new DynamicRows({ + isEmpty: jasmine.createSpy().and.returnValue(1), + isShowAddProductButton: jasmine.createSpy().and.returnValue(1) + })); + + }); + + it('Verify processingUnionInsertDat method', function () { + var expectedData = [], + mockData = [ + { + attributes: 'Color: dsfsd', + sku: 'Conf&-sdfs' + }, + { + attributes: 'Color: sdfs', + sku: 'Conf-dsfsd' + } + ], + sourceMock = { + get: jasmine.createSpy().and.returnValue(['code1', 'code2']), + set: jasmine.createSpy().and.callFake(function (path, row) { + expectedData.push(row); + }) + }; + + model.getChildItems = jasmine.createSpy().and.returnValue($('')); + model.source = sourceMock; + model.processingUnionInsertData(mockData); + expect(model.source.get).toHaveBeenCalled(); + expect(model.getChildItems).toHaveBeenCalled(); + expect(expectedData[1].sku).toBe('Conf&-sdfs'); + }); + + }); +}); diff --git a/dev/tests/js/jasmine/tests/app/code/Magento/ConfigurableProduct/view/adminhtml/web/js/components/qty-configurable.test.js b/dev/tests/js/jasmine/tests/app/code/Magento/ConfigurableProduct/view/adminhtml/web/js/components/qty-configurable.test.js new file mode 100644 index 0000000000000..1c81c9fabf85e --- /dev/null +++ b/dev/tests/js/jasmine/tests/app/code/Magento/ConfigurableProduct/view/adminhtml/web/js/components/qty-configurable.test.js @@ -0,0 +1,107 @@ +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ + +define(['squire'], function (Squire) { + 'use strict'; + + var injector = new Squire(), + Component, + params = { + name: 'qty-element', + dataScope: '', + value: 1000, + setListeners: jasmine.createSpy().and.callFake(function () { + return this; + }), + setLinks: jasmine.createSpy().and.callFake(function () { + this.isConfigurable = false; + + return this; + }) + }; + + beforeEach(function (done) { + injector.require( + ['Magento_ConfigurableProduct/js/components/qty-configurable'], function (QtyConfigurable) { + Component = QtyConfigurable; + done(); + }); + }); + + afterEach(function () { + try { + injector.remove(); + injector.clean(); + } catch (e) { + } + }); + + describe('Magento_ConfigurableProduct/js/components/qty-configurable', function () { + it('Product is not configurable by default', function () { + var component = new Component(params); + + expect(component.disabled()).toBeFalsy(); + expect(component.value()).toEqual(1000); + }); + + it('State of component does not changed', function () { + var component = new Component(params); + + expect(component.disabled()).toBeFalsy(); + + component.value(99); + component.handleQtyValue(false); + + expect(component.disabled()).toBeFalsy(); + expect(component.value()).toEqual(99); + }); + + it('Product changed to configurable', function () { + var component = new Component(params); + + expect(component.disabled()).toBeFalsy(); + expect(component.value()).toEqual(1000); + + component.handleQtyValue(true); + + expect(component.disabled()).toBeTruthy(); + expect(component.value()).toEqual(''); + }); + + it('Product is configurable by default', function () { + var component = new Component($.extend({}, params, { + // eslint-disable-next-line max-nested-callbacks + setLinks: jasmine.createSpy().and.callFake(function () { + this.isConfigurable = true; + + return this; + }) + })); + + expect(component.disabled()).toBeTruthy(); + expect(component.value()).toEqual(''); + }); + + it('Product changed from configurable to another one', function () { + var component = new Component($.extend({}, params, { + // eslint-disable-next-line max-nested-callbacks + setLinks: jasmine.createSpy().and.callFake(function () { + this.isConfigurable = true; + + return this; + }) + })); + + expect(component.disabled()).toBeTruthy(); + expect(component.value()).toEqual(''); + + component.value(100); + component.handleQtyValue(false); + + expect(component.disabled()).toBeFalsy(); + expect(component.value()).toEqual(100); + }); + }); +}); diff --git a/dev/tests/js/jasmine/tests/app/code/Magento/ConfigurableProduct/view/frontend/js/configurable.test.js b/dev/tests/js/jasmine/tests/app/code/Magento/ConfigurableProduct/view/frontend/js/configurable.test.js new file mode 100644 index 0000000000000..21492b20b779c --- /dev/null +++ b/dev/tests/js/jasmine/tests/app/code/Magento/ConfigurableProduct/view/frontend/js/configurable.test.js @@ -0,0 +1,69 @@ +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ + +define([ + 'jquery', + 'Magento_ConfigurableProduct/js/configurable' +], function ($, Configurable) { + 'use strict'; + + var widget, + option = '<select name=\'super_attribute[93]\'' + + 'data-selector=\'super_attribute[93]\'' + + 'data-validate=\'{required:true}\'' + + 'id=\'attribute93\'' + + 'class=\'super-attribute-select\'>' + + '<option value=\'\'></option>' + + '</select>', + selectElement = $(option); + + beforeEach(function () { + widget = new Configurable(); + widget.options = { + spConfig: { + chooseText: 'Chose an Option...', + attributes: + { + 'size': { + options: [ + { + id: '2', + value: '2' + }, + { + id: 3, + value: 'red' + + } + ] + } + }, + prices: { + finalPrice: { + amount: 12 + } + } + }, + values: { + } + }; + }); + + describe('Magento_ConfigurableProduct/js/configurable', function () { + + it('check if attribute value is possible to be set as configurable option', function () { + expect($.mage.configurable).toBeDefined(); + widget._parseQueryParams('size=2'); + expect(widget.options.values.size).toBe('2'); + }); + + it('check that attribute value is not set if provided option does not exists', function () { + expect($.mage.configurable).toBeDefined(); + widget._parseQueryParams('size=10'); + widget._fillSelect(selectElement[0]); + expect(widget.options.values.size).toBe(undefined); + }); + }); +}); diff --git a/dev/tests/js/jasmine/tests/app/code/Magento/Customer/frontend/js/section-config.test.js b/dev/tests/js/jasmine/tests/app/code/Magento/Customer/frontend/js/section-config.test.js new file mode 100644 index 0000000000000..ef7222883b738 --- /dev/null +++ b/dev/tests/js/jasmine/tests/app/code/Magento/Customer/frontend/js/section-config.test.js @@ -0,0 +1,221 @@ +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ + +/* eslint max-nested-callbacks: 0 */ +define(['squire'], function (Squire) { + 'use strict'; + + var injector = new Squire(), + obj; + + beforeEach(function (done) { + // injector.mock(mocks); + injector.require(['Magento_Customer/js/section-config'], function (Constr) { + obj = Constr; + done(); + }); + }); + + afterEach(function () { + try { + injector.clean(); + injector.remove(); + } catch (e) {} + }); + + describe('Magento_Customer/js/section-config', function () { + describe('"getAffectedSections" method', function () { + it('Does not throw before component is initialized.', function () { + expect(function () { + obj.getAffectedSections('http://localhost.com/path'); + }).not.toThrow(); + }); + + it('Returns proper sections when URL contains base URL.', function () { + obj['Magento_Customer/js/section-config']({ + sections: { + 'path': [ + 'section' + ] + }, + baseUrls: [ + 'http://localhost.com/', + 'https://localhost.com/' + ] + }); + + expect(obj.getAffectedSections('https://localhost.com/path')).toEqual(['section']); + }); + + it('Returns proper sections when glob pattern is used at the end.', function () { + obj['Magento_Customer/js/section-config']({ + sections: { + 'path/*': [ + 'section' + ] + }, + baseUrls: [ + 'http://localhost.com/', + 'https://localhost.com/' + ] + }); + + expect(obj.getAffectedSections('https://localhost.com/path/subpath')).toEqual(['section']); + }); + + it('Returns proper sections when glob pattern is used inside.', function () { + obj['Magento_Customer/js/section-config']({ + sections: { + '*/subpath': [ + 'section' + ] + }, + baseUrls: [ + 'http://localhost.com/', + 'https://localhost.com/' + ] + }); + + expect(obj.getAffectedSections('https://localhost.com/path/subpath')).toEqual(['section']); + }); + + it('Strips "index.php" suffix from provided URL.', function () { + obj['Magento_Customer/js/section-config']({ + sections: { + 'path': [ + 'section' + ] + }, + baseUrls: [ + 'http://localhost.com/' + ] + }); + + expect(obj.getAffectedSections('http://localhost.com/path/index.php')).toEqual(['section']); + }); + + it('Adds sections for all URLs "*" to found ones.', function () { + obj['Magento_Customer/js/section-config']({ + sections: { + 'path': [ + 'section' + ], + '*': [ + 'all' + ] + }, + baseUrls: [ + 'http://localhost.com/' + ] + }); + + expect(obj.getAffectedSections('http://localhost.com/path')).toEqual(['section', 'all']); + }); + + it('Returns "*" sections for all URLs.', function () { + obj['Magento_Customer/js/section-config']({ + sections: { + '*': [ + 'all' + ] + }, + baseUrls: [ + 'http://localhost.com/' + ] + }); + + expect(obj.getAffectedSections('http://localhost.com/path')).toEqual(['all']); + }); + + it('Ignores capitalization in parts of URL.', function () { + obj['Magento_Customer/js/section-config']({ + sections: { + 'path': [ + 'section' + ] + }, + baseUrls: [ + 'http://localhost.com/' + ] + }); + + expect(obj.getAffectedSections('http://localhost.com/PaTh')).toEqual(['section']); + }); + }); + + describe('"filterClientSideSections" method', function () { + it('Does not throw before component is initialized.', function () { + expect(function () { + obj.filterClientSideSections(); + }).not.toThrow(); + }); + + it('Returns empty array when all sections are client side.', function () { + var sections = ['test']; + + obj['Magento_Customer/js/section-config']({ + clientSideSections: sections + }); + expect(obj.filterClientSideSections(sections)).toEqual([]); + }); + + it('Filters out client side sections.', function () { + var allSections = ['test', 'client'], + clientSections = ['client']; + + obj['Magento_Customer/js/section-config']({ + clientSideSections: clientSections + }); + expect(obj.filterClientSideSections(allSections)).toEqual(['test']); + }); + }); + + describe('"isClientSideSection" method', function () { + it('Does not throw before component is initialized.', function () { + expect(function () { + obj.isClientSideSection(); + }).not.toThrow(); + }); + + it('Returns true if section is defined as client side.', function () { + obj['Magento_Customer/js/section-config']({ + clientSideSections: ['client'] + }); + expect(obj.isClientSideSection('client')).toBe(true); + }); + + it('Returns false if section is not defined as client side.', function () { + obj['Magento_Customer/js/section-config']({ + clientSideSections: ['client'] + }); + expect(obj.isClientSideSection('test')).toBe(false); + }); + + it('Returns false if section is not client side and sections are not defined.', function () { + obj['Magento_Customer/js/section-config']({ + clientSideSections: [] + }); + expect(obj.isClientSideSection('test')).toBe(false); + }); + }); + + describe('"getSectionNames" method', function () { + it('Does not throw before component is initialized.', function () { + expect(function () { + obj.getSectionNames(); + }).not.toThrow(); + }); + + it('Returns defined section names.', function () { + var sections = ['test']; + + obj['Magento_Customer/js/section-config']({ + sectionNames: sections + }); + expect(obj.getSectionNames()).toBe(sections); + }); + }); + }); +}); diff --git a/dev/tests/js/jasmine/tests/app/code/Magento/Newsletter/frontend/js/newsletter-sign-up.test.js b/dev/tests/js/jasmine/tests/app/code/Magento/Newsletter/frontend/js/newsletter-sign-up.test.js new file mode 100644 index 0000000000000..345a3fc47e7c3 --- /dev/null +++ b/dev/tests/js/jasmine/tests/app/code/Magento/Newsletter/frontend/js/newsletter-sign-up.test.js @@ -0,0 +1,141 @@ +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ + +define([ + 'squire', + 'ko', + 'jquery', + 'mage/validation' +], function (Squire, ko, $) { + 'use strict'; + + var injector = new Squire(), + obj, + checkbox, + emailElem, + button, + response = ko.observable({}), + resolverMock = jasmine.createSpy('subscription-status-resolver', function (email, deferred) { + if (response().errors) { + deferred.reject(); + } else { + deferred.resolve(response().subscribed); + } + }).and.callThrough(), + mocks = { + 'Magento_Newsletter/js/subscription-status-resolver': resolverMock + }; + + beforeEach(function (done) { + checkbox = $('<input type="checkbox" class="checkbox" name="is_subscribed" id="is_subscribed"/>'); + emailElem = $('<input type="email" name="email" id="email_address"/>'); + button = $('<button type="submit" id="button"/>'); + $(document.body).append(checkbox).append(emailElem).append(button); + + injector.mock(mocks); + injector.require(['Magento_Newsletter/js/newsletter-sign-up'], function (Constr) { + obj = new Constr({ + submitButton: '#button', + signUpElement: '#is_subscribed' + }, '#email_address'); + done(); + }); + }); + + afterEach(function () { + try { + injector.clean(); + injector.remove(); + } catch (e) {} + + checkbox.remove(); + emailElem.remove(); + button.remove(); + }); + + describe('Magento_Newsletter/js/newsletter-sign-up', function () { + it('Check for properties defined', function () { + expect(obj.hasOwnProperty('submitButton')).toBeDefined(); + expect(typeof obj.submitButton).toEqual('string'); + expect(obj.hasOwnProperty('signUpElement')).toBeDefined(); + expect(typeof obj.signUpElement).toEqual('string'); + expect(obj.hasOwnProperty('element')).toBeDefined(); + expect(typeof obj.element).toEqual('string'); + }); + + it('Check sign-up process when Subscription is checked', function () { + emailElem.val('email@example.com'); + checkbox.prop('checked', true); + + obj.updateSignUpStatus(); + + expect(resolverMock).not.toHaveBeenCalled(); + expect(button.is(':disabled')).toBeFalsy(); + expect(checkbox.is(':checked')).toBeTruthy(); + }); + + it('Check sign-up process without email', function () { + checkbox.prop('checked', false); + + obj.updateSignUpStatus(); + + expect(resolverMock).not.toHaveBeenCalled(); + expect(checkbox.is(':checked')).toBeFalsy(); + }); + + it('Check sign-up process with incorrect email', function () { + emailElem.val('emailexample.com'); + checkbox.prop('checked', false); + + obj.updateSignUpStatus(); + + expect(resolverMock).not.toHaveBeenCalled(); + expect(checkbox.is(':checked')).toBeFalsy(); + }); + + it('Check Subscription with correct data', function () { + response({ + subscribed: true, + errors: false + }); + emailElem.val('email@example.com'); + checkbox.prop('checked', false); + + obj.updateSignUpStatus(); + + expect(resolverMock).toHaveBeenCalled(); + expect(checkbox.is(':checked')).toBeTruthy(); + expect(button.is(':disabled')).toBeFalsy(); + }); + + it('Check sign-up process with non-subscribed email', function () { + response({ + subscribed: false, + errors: false + }); + emailElem.val('email@example.com'); + checkbox.prop('checked', false); + + obj.updateSignUpStatus(); + + expect(resolverMock).toHaveBeenCalled(); + expect(checkbox.is(':checked')).toBeFalsy(); + }); + + it('Check sign-up process with errors', function () { + response({ + subscribed: true, + errors: true + }); + emailElem.val('email@example.com'); + checkbox.prop('checked', false); + + obj.updateSignUpStatus(); + + expect(resolverMock).toHaveBeenCalled(); + expect(checkbox.is(':checked')).toBeFalsy(); + }); + }); +}); diff --git a/dev/tests/js/jasmine/tests/app/code/Magento/Signifyd/frontend/js/Fingerprint.test.js b/dev/tests/js/jasmine/tests/app/code/Magento/Signifyd/frontend/js/Fingerprint.test.js deleted file mode 100644 index 0be178c5a31f0..0000000000000 --- a/dev/tests/js/jasmine/tests/app/code/Magento/Signifyd/frontend/js/Fingerprint.test.js +++ /dev/null @@ -1,38 +0,0 @@ -/** - * Copyright © Magento, Inc. All rights reserved. - * See COPYING.txt for license details. - */ - -define([ - 'jquery' -], function ($) { - 'use strict'; - - /*eslint max-nested-callbacks: ["error", 5]*/ - describe('Signifyd device fingerprint client script', function () { - - it('SIGNIFYD_GLOBAL object initialization check', function (done) { - var script = document.createElement('script'); - - script.setAttribute('src', 'https://cdn-scripts.signifyd.com/api/script-tag.js'); - script.setAttribute('id', 'sig-api'); - script.setAttribute('type', 'text/javascript'); - script.setAttribute('async', ''); - script.setAttribute('data-order-session-id', 'mage-jasmin-test'); - - $(document.body).append(script); - - setTimeout(function () { - var signifyd = window.SIGNIFYD_GLOBAL; - - expect(signifyd).toBeDefined(); - expect(typeof signifyd).toBe('object'); - expect(signifyd.scriptTagHasLoaded).toBeDefined(); - expect(typeof signifyd.scriptTagHasLoaded).toBe('function'); - expect(signifyd.scriptTagHasLoaded()).toBe(true); - done(); - }, 10000); - - }, 12000); - }); -}); diff --git a/dev/tests/js/jasmine/tests/app/code/Magento/Swatches/view/frontend/web/js/swatch-renderer.test.js b/dev/tests/js/jasmine/tests/app/code/Magento/Swatches/view/frontend/web/js/swatch-renderer.test.js index bf0ff3466c529..bdc9410d369af 100644 --- a/dev/tests/js/jasmine/tests/app/code/Magento/Swatches/view/frontend/web/js/swatch-renderer.test.js +++ b/dev/tests/js/jasmine/tests/app/code/Magento/Swatches/view/frontend/web/js/swatch-renderer.test.js @@ -1,80 +1,134 @@ -/** - * Copyright © Magento, Inc. All rights reserved. - * See COPYING.txt for license details. - */ - -define([ - 'jquery', - 'Magento_Swatches/js/swatch-renderer' -], function ($, SwatchRenderer) { - 'use strict'; - - describe('Testing "_RenderSwatchOptions" method of SwatchRenderer Widget', function () { - var widget, - html, - optionConfig, - attribute, - optionId = 2, - swathImageHeight = '60', - swathImageWidth = '70', - swathThumbImageHeight = '40', - swathThumbImageWidth = '50'; - - beforeEach(function () { - widget = new SwatchRenderer(); - attribute = { - id: 1, - options: [{ - id: optionId - }] - }; - widget.options = { - classes: { - optionClass: 'swatch-option' - }, - jsonSwatchConfig: { - 1: { - 2: { - type: 2 - } - } - }, - jsonSwatchImageSizeConfig: { - swatchImage: { - width: swathImageWidth, - height: swathImageHeight - }, - swatchThumb: { - width: swathThumbImageWidth, - height: swathThumbImageHeight - } - } - }; - optionConfig = widget.options.jsonSwatchConfig[attribute.id]; - html = $(widget._RenderSwatchOptions(attribute, 'option-label-control-id-1'))[0]; - }); - - it('check if swatch config has attribute id', function () { - expect(widget.options.jsonSwatchConfig.hasOwnProperty(attribute.id)).toEqual(true); - }); - - it('check if option config has option id', function () { - expect(optionConfig.hasOwnProperty(optionId)).toEqual(true); - }); - - it('check swatch thumbnail image height attribute', function () { - expect(html.hasAttribute('thumb-height')).toBe(true); - expect(html.getAttribute('thumb-height')).toEqual(swathThumbImageHeight); - }); - - it('check swatch thumbnail image width attribute', function () { - expect(html.hasAttribute('thumb-width')).toBe(true); - expect(html.getAttribute('thumb-width')).toEqual(swathThumbImageWidth); - }); - - it('check swatch image styles', function () { - expect(html.style.height).toEqual(swathImageHeight + 'px'); - expect(html.style.width).toEqual(swathImageWidth + 'px'); - }); - }); -}); +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ + +define([ + 'jquery', + 'Magento_Swatches/js/swatch-renderer' +], function ($, SwatchRenderer) { + 'use strict'; + + describe('Testing "_RenderSwatchOptions" method of SwatchRenderer Widget', function () { + var widget, + html, + optionConfig, + attribute, + optionId = 2, + swathImageHeight = '60', + swathImageWidth = '70', + swathThumbImageHeight = '40', + swathThumbImageWidth = '50'; + + beforeEach(function () { + widget = new SwatchRenderer(); + attribute = { + id: 1, + options: [{ + id: optionId + }] + }; + + widget.options = { + classes: { + optionClass: 'swatch-option' + }, + jsonSwatchConfig: { + 1: { + 2: { + type: 2 + } + } + }, + jsonSwatchImageSizeConfig: { + swatchImage: { + width: swathImageWidth, + height: swathImageHeight + }, + swatchThumb: { + width: swathThumbImageWidth, + height: swathThumbImageHeight + } + } + }; + + optionConfig = widget.options.jsonSwatchConfig[attribute.id]; + html = $(widget._RenderSwatchOptions(attribute, 'option-label-control-id-1'))[0]; + }); + + it('check if swatch config has attribute id', function () { + expect(widget.options.jsonSwatchConfig.hasOwnProperty(attribute.id)).toEqual(true); + }); + + it('check if option config has option id', function () { + expect(optionConfig.hasOwnProperty(optionId)).toEqual(true); + }); + + it('check swatch thumbnail image height attribute', function () { + expect(html.hasAttribute('data-thumb-height')).toBe(true); + expect(html.getAttribute('data-thumb-height')).toEqual(swathThumbImageHeight); + }); + + it('check swatch thumbnail image width attribute', function () { + expect(html.hasAttribute('data-thumb-width')).toBe(true); + expect(html.getAttribute('data-thumb-width')).toEqual(swathThumbImageWidth); + }); + + it('check swatch image styles', function () { + expect(html.style.height).toEqual(swathImageHeight + 'px'); + expect(html.style.width).toEqual(swathImageWidth + 'px'); + }); + + it('check udate price method', function () { + var productPriceMock = { + find: jasmine.createSpy().and.returnValue({ + hide: jasmine.createSpy(), + priceBox: jasmine.createSpy().and.returnValue(''), + trigger: jasmine.createSpy(), + find: jasmine.createSpy().and.returnValue({ + toggleClass: jasmine.createSpy() + }) + }) + }; + + widget.element = { + parents: jasmine.createSpy().and.returnValue(productPriceMock) + }; + widget._getNewPrices = jasmine.createSpy().and.returnValue(undefined); + widget._UpdatePrice(); + expect(productPriceMock.find().find.calls.count()).toBe(1); + }); + + it('check getSelectedOptionPriceIndex', function () { + var optionMock = '<div class="swatch-attribute" data-attribute-id="2" data-option-selected="4"></div>', + element = $('<div class="' + widget.options.tooltipClass + + '"><div class="image"></div><div class="title"></div><div class="corner"></div>' + + optionMock + '</div>' + ), + optionPricesMock = { + optionPrices: { + p: { + finalPrice: { + amount: 12 + } + } + } + }; + + widget.element = element; + widget.options.classes.attributeClass = 'swatch-attribute'; + widget.options.jsonConfig = optionPricesMock; + widget.optionsMap = { + 2: { + 4: { + products: 'p' + }, + hasOwnProperty: jasmine.createSpy().and.returnValue(true) + }, + hasOwnProperty: jasmine.createSpy().and.returnValue(true) + }; + + expect(widget._getSelectedOptionPriceIndex()).toBe('p'); + }); + }); +}); diff --git a/dev/tests/js/jasmine/tests/app/code/Magento/Theme/view/frontend/web/js/cookie-status.test.js b/dev/tests/js/jasmine/tests/app/code/Magento/Theme/view/frontend/web/js/cookie-status.test.js new file mode 100644 index 0000000000000..67ba24fe3bcfb --- /dev/null +++ b/dev/tests/js/jasmine/tests/app/code/Magento/Theme/view/frontend/web/js/cookie-status.test.js @@ -0,0 +1,50 @@ +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ + +define([ + 'jquery', + 'cookieStatus' +], function ($, Cookie) { + 'use strict'; + + describe('Magento_Theme/js/cookie-status', function () { + var widget, + htmlContainer = '<div id="cookie-status" style="display: none"></div>', + navigator; + + beforeEach(function () { + widget = new Cookie(); + navigator = window.navigator; + $('.modal-popup').remove(); + $('#cookie-status').remove(); + $(document.body).append(htmlContainer); + }); + + afterEach(function () { + window.navigator = navigator; + }); + + it('defines cookieStatus widget', function () { + expect($.fn.cookieStatus).toBeDefined(); + }); + + it('does not show a modal when cookies are supported', function () { + window.navigator = { + cookieEnabled: true + }; + widget._init(); + expect($(document.body).html()).not.toContain('<aside role="dialog" class="modal-popup'); + }); + + it('shows the modal when cookies are not supported', function () { + window.navigator = { + cookieEnabled: false + }; + widget._init(); + expect($(document.body).html()).toContain('<aside role="dialog" class="modal-popup'); + }); + + }); +}); diff --git a/dev/tests/js/jasmine/tests/app/code/Magento/Ui/base/js/form/form.test.js b/dev/tests/js/jasmine/tests/app/code/Magento/Ui/base/js/form/form.test.js index fa149f285c0e3..5726184df2103 100644 --- a/dev/tests/js/jasmine/tests/app/code/Magento/Ui/base/js/form/form.test.js +++ b/dev/tests/js/jasmine/tests/app/code/Magento/Ui/base/js/form/form.test.js @@ -3,30 +3,50 @@ * See COPYING.txt for license details. */ +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ + define([ - 'underscore', - 'uiRegistry', - 'Magento_Ui/js/form/form' -], function (_, registry, Constr) { + 'squire' +], function (Squire) { 'use strict'; describe('Magento_Ui/js/form/form', function () { - - var obj = new Constr({ - provider: 'provName', - name: '', - index: '' - }); - - registry.set('provName', { - /** Stub */ - on: function () {}, - - /** Stub */ - get: function () {}, - - /** Stub */ - set: function () {} + var injector = new Squire(), + mocks = { + 'Magento_Ui/js/lib/registry/registry': { + /** Method stub. */ + get: function () { + return { + get: jasmine.createSpy(), + set: jasmine.createSpy() + }; + }, + options: jasmine.createSpy(), + create: jasmine.createSpy(), + set: jasmine.createSpy(), + async: jasmine.createSpy() + } + }, + obj, + dataScope = 'dataScope'; + + beforeEach(function (done) { + injector.mock(mocks); + injector.require([ + 'Magento_Ui/js/form/form' + ], function (Constr) { + obj = new Constr({ + provider: 'provName', + name: '', + index: '', + dataScope: dataScope + }); + + done(); + }); }); describe('"initAdapter" method', function () { diff --git a/dev/tests/js/jasmine/tests/app/code/Magento/Ui/base/js/grid/columns/image-preview.test.js b/dev/tests/js/jasmine/tests/app/code/Magento/Ui/base/js/grid/columns/image-preview.test.js index b5c6e75248bfa..77ce2fb25f688 100644 --- a/dev/tests/js/jasmine/tests/app/code/Magento/Ui/base/js/grid/columns/image-preview.test.js +++ b/dev/tests/js/jasmine/tests/app/code/Magento/Ui/base/js/grid/columns/image-preview.test.js @@ -13,10 +13,26 @@ define([ describe('Ui/js/grid/columns/image-preview', function () { var record = { - _rowIndex: 1, - rowNumber: 1 - }, - imagePreview; + _rowIndex: 1, + rowNumber: 1 + }, + imagePreview, + recordMock = { + _rowIndex: 2 + }, + secondRecordMock = { + _rowIndex: 1, + rowNumber: 1 + }, + elementMock = { + keyCode: 37 + }, + masonryMock = { + shows: jasmine.createSpy().and.returnValue([]), + rows: jasmine.createSpy().and.returnValue({ + 1: secondRecordMock + }) + }; beforeEach(function () { imagePreview = new Preview(); @@ -46,5 +62,36 @@ define([ }); }); + + describe('handleKeyDown method', function () { + + it('verify record changed on key down', function () { + var imageMock = document.createElement('img'), + originMock = $.fn.get; + + spyOn($.fn, 'get').and.returnValue(imageMock); + imagePreview.visibleRecord = jasmine.createSpy().and.returnValue(2); + imagePreview.displayedRecord = ko.observable(); + imagePreview.displayedRecord(recordMock); + imagePreview.masonry = jasmine.createSpy().and.returnValue(masonryMock); + imagePreview.handleKeyDown(elementMock); + expect(imagePreview.displayedRecord()._rowIndex).toBe(secondRecordMock._rowIndex); + + $.fn.get = originMock; + }); + + it('verify record not changed on key down when active element input', function () { + var input = $('<input id=\'input-fixture\'/>'); + + $(document.body).append(input); + input.focus(); + imagePreview.visibleRecord = jasmine.createSpy().and.returnValue(1); + imagePreview.displayedRecord = ko.observable(1); + imagePreview.handleKeyDown(elementMock); + expect(imagePreview.displayedRecord()).toBe(1); + + $('#input-fixture').remove(); + }); + }); }); }); diff --git a/dev/tests/js/jasmine/tests/app/code/Magento/Ui/base/js/grid/data-storage.test.js b/dev/tests/js/jasmine/tests/app/code/Magento/Ui/base/js/grid/data-storage.test.js index 5f8bfc2c98cc2..b53f49bd6103d 100644 --- a/dev/tests/js/jasmine/tests/app/code/Magento/Ui/base/js/grid/data-storage.test.js +++ b/dev/tests/js/jasmine/tests/app/code/Magento/Ui/base/js/grid/data-storage.test.js @@ -6,63 +6,190 @@ /*eslint max-nested-callbacks: 0*/ /*jscs:disable requireCamelCaseOrUpperCaseIdentifiers*/ define([ - 'mageUtils', + 'jquery', 'Magento_Ui/js/grid/data-storage' -], function (utils, DataStorage) { +], function ($, DataStorage) { 'use strict'; describe('Magento_Ui/js/grid/data-storage', function () { - describe('costructor', function () { - it('converts dataScope property to array', function () { + + describe('"initConfig" method', function () { + + it('returns self', function () { var model = new DataStorage({ dataScope: 'magento' }); - expect(model.dataScope).toEqual(['magento']); + expect(model.initConfig()).toEqual(model); }); - }); - describe('hasScopeChanged', function () { - it('is function', function () { + it('changes string dataScope property to an array', function () { var model = new DataStorage({ - dataScope: '' + dataScope: 'magento' }); - expect(model.hasScopeChanged).toBeDefined(); - expect(typeof model.hasScopeChanged).toEqual('function'); + expect(model.dataScope).toEqual(['magento']); }); - it('returns false if no requests have been made', function () { + it('changes empty string dataScope property to an empty array', function () { var model = new DataStorage({ dataScope: '' }); - expect(model.hasScopeChanged()).toBeFalsy(); + expect(model.dataScope).toEqual([]); }); - it('tells whether parameters defined in the dataScope property have changed', function () { - var params, newParams, model; + it('doesn\'t change non-string dataScope property', function () { + var testScope = { + testKey: 'test value' + }, + model = new DataStorage({ + dataScope: testScope + }); - params = { - namespace: 'magento', - search: '', - filters: { - store_id: 0 + expect(model.dataScope).toEqual(testScope); + }); + + it('initializes _requests property as an empty array', function () { + var model = new DataStorage(); + + model._requests = null; + model.initConfig(); + expect(model._requests).toEqual([]); + }); + }); + + describe('"getByIds" method', function () { + + it('returns false if data for ids is missing', function () { + var model = new DataStorage(); + + expect(model.getByIds([1,2,3])).toEqual(false); + }); + + it('returns array of items', function () { + var item = { + id_field_name: 'entity_id', + entity_id: '1' }, - sorting: {}, - paging: {} - }; + model = new DataStorage({ + data: { + 1: item + } + }); - newParams = utils.extend({}, params, { - search: 'magento', - filters: { - store_id: 1 - } - }); + expect(model.getByIds([1])).toEqual([item]); + }); - model = new DataStorage({ - dataScope: 'filters.store_id' - }); + }); + + describe('"getIds" method', function () { + + it('returns an array of entity_id\'s from provided data', function () { + var model = new DataStorage(), + ids = [ + { + id_field_name: 'entity_id', + entity_id: '1' + }, + { + id_field_name: 'entity_id', + entity_id: '54' + } + ]; + + expect(model.getIds(ids)).toEqual(['1', '54']); + }); + + it('returns an array of entity_id\'s from stored data if no arguments provided', function () { + var model = new DataStorage({ + data: { + 1: { + id_field_name: 'entity_id', + entity_id: '1' + }, + 2: { + id_field_name: 'entity_id', + entity_id: '42' + } + } + }); + + expect(model.getIds()).toEqual(['1', '42']); + }); + + }); + + describe('"getData" method', function () { + + var model = new DataStorage(); + + it('returns the result of requestData method if scope have been changed', function () { + var requestDataResult = 'requestDataResult'; + + spyOn(model, 'clearRequests'); + spyOn(model, 'hasScopeChanged').and.returnValue(true); + spyOn(model, 'requestData').and.returnValue(requestDataResult); + spyOn(model, 'getRequest'); + expect(model.getData()).toEqual(requestDataResult); + expect(model.clearRequests).toHaveBeenCalled(); + expect(model.getRequest).not.toHaveBeenCalled(); + }); + + it('returns the cached result if scope have not been changed', function () { + var cachedRequestDataResult = 'cachedRequestDataResult'; + + spyOn(model, 'clearRequests'); + spyOn(model, 'requestData'); + spyOn(model, 'hasScopeChanged').and.returnValue(false); + spyOn(model, 'getRequest').and.returnValue(true); + spyOn(model, 'getRequestData').and.returnValue(cachedRequestDataResult); + + expect(model.getData()).toEqual(cachedRequestDataResult); + expect(model.clearRequests).not.toHaveBeenCalled(); + expect(model.requestData).not.toHaveBeenCalled(); + }); + + it('returns the result of requestData method if refresh option is provided', function () { + var requestDataResult = 'requestDataResult', + options = { + refresh: true + }; + + spyOn(model, 'getRequest').and.returnValue(true); + spyOn(model, 'clearRequests'); + spyOn(model, 'hasScopeChanged').and.returnValue(true); + spyOn(model, 'requestData').and.returnValue(requestDataResult); + expect(model.getData({}, options)).toEqual(requestDataResult); + expect(model.clearRequests).toHaveBeenCalled(); + }); + + }); + + describe('"hasScopeChanged" method', function () { + + it('returns false if no requests have been made', function () { + var model = new DataStorage(); + + expect(model.hasScopeChanged()).toBeFalsy(); + }); + + it('returns true for not cached params', function () { + var params = { + search: '1', + filters: { + store_id: 0 + } + }, + newParams = { + search: '2', + filters: { + store_id: 1 + } + }, + model = new DataStorage({ + dataScope: 'filters.store_id' + }); model.cacheRequest({ totalRecords: 0 @@ -72,5 +199,303 @@ define([ expect(model.hasScopeChanged(newParams)).toBeTruthy(); }); }); + + describe('"updateData" method', function () { + var model = new DataStorage({ + dataScope: 'magento', + requestConfig: { + url: 'magento.com', + method: 'GET', + dataType: 'json' + }, + data: { + 1: { + id_field_name: 'entity_id', + entity_id: '1', + field: 'value' + } + } + }); + + it('updates data items', function () { + var data = [{ + id_field_name: 'entity_id', + entity_id: '1', + field: 'updatedValue' + }]; + + expect(model.updateData(data)).toEqual(model); + expect(model.getByIds([1])).toEqual(data); + }); + }); + + describe('"requestData" method', function () { + var model = new DataStorage(); + + it('Check Ajax request', function () { + var result = 'result'; + + spyOn(model, 'onRequestComplete').and.returnValue(result); + spyOn($, 'ajax').and.returnValue({ + /** + * Success result for ajax request + * + * @param {Function} handler + * @returns {*} + */ + done: function (handler) { + return handler(); + } + }); + expect(model.requestData({})).toEqual(result); + }); + }); + + describe('"getRequest" method', function () { + var model = new DataStorage({ + dataScope: 'magento' + }); + + it('returns cached request', function () { + var params = { + namespace: 'magento', + search: '', + sorting: {}, + paging: {} + }, + request = { + ids: ['1'], + params: params, + totalRecords: 1, + errorMessage: '' + }; + + model._requests.push(request); + expect(model.getRequest(params)).toEqual(request); + }); + }); + + describe('"getRequestData" method', function () { + it('returns request data', function () { + var request = { + ids: [1,2], + totalRecords: 2, + errorMessage: '' + }, + items = [ + { + id_field_name: 'entity_id', + entity_id: '1' + }, + { + id_field_name: 'entity_id', + entity_id: '2' + } + ], + result = { + items: items, + totalRecords: 2, + errorMessage: '' + }, + model = new DataStorage({ + cachedRequestDelay: 0 + }); + + spyOn(model, 'getByIds').and.returnValue(items); + model.getRequestData(request).then(function (promiseResult) { + expect(promiseResult).toEqual(result); + }); + }); + }); + + describe('"cacheRequest" method', function () { + var model = new DataStorage({ + dataScope: 'magento' + }); + + it('adds the request to the cache', function () { + var params = { + namespace: 'magento', + search: '', + sorting: {}, + paging: {} + }, + ids = ['1','2','3'], + data = { + items: ids, + totalRecords: 3, + errorMessage: '' + }, + request = { + ids: ids, + params: params, + totalRecords: 3, + errorMessage: '' + }; + + spyOn(model, 'removeRequest'); + spyOn(model, 'getIds').and.returnValue(ids); + model.cacheRequest(data, params); + expect(model.getRequest(params)).toEqual(request); + expect(model.removeRequest).not.toHaveBeenCalled(); + }); + + it('overwrites the previously cached request for the same params', function () { + var params = { + namespace: 'magento', + search: '', + sorting: {}, + paging: {} + }, + ids = ['1','2','3'], + firstData = { + items: ids, + totalRecords: 3, + errorMessage: '' + }, + secondData = { + items: ids, + totalRecords: 3, + errorMessage: 'Error message' + }, + firstRequest = { + ids: ids, + params: params, + totalRecords: 3, + errorMessage: '' + }, + secondRequest = { + ids: ids, + params: params, + totalRecords: 3, + errorMessage: 'Error message' + }; + + spyOn(model, 'getIds').and.returnValue(ids); + model.cacheRequest(firstData, params); + expect(model.getRequest(params)).toEqual(firstRequest); + model.cacheRequest(secondData, params); + expect(model.getRequest(params)).toEqual(secondRequest); + }); + }); + + describe('"clearRequests" method', function () { + + it('removes all cached requests', function () { + var model = new DataStorage(), + params = { + namespace: 'magento', + search: 'magento', + filters: { + store_id: 1 + } + }; + + model._requests.push({ + ids: ['1','2','3','4'], + params: params, + totalRecords: 4, + errorMessage: 'errorMessage' + }); + model.clearRequests(); + expect(model._requests).toEqual([]); + }); + }); + + describe('"removeRequest" method', function () { + + var model = new DataStorage(); + + it('removes the request from the cache', function () { + var params = { + namespace: 'magento', + search: '', + sorting: {}, + paging: {} + }, + request = { + ids: ['1','2','3'], + params: params, + totalRecords: 3, + errorMessage: '' + }; + + model._requests = [request]; + expect(model.getRequest(params)).toEqual(request); + model.removeRequest(request); + expect(model.getRequest(params)).toBeFalsy(); + }); + }); + + describe('"wasRequested" method', function () { + var model = new DataStorage({ + dataScope: 'magento' + }); + + it('returns false if request is not present in cache', function () { + var params = { + namespace: 'magento', + search: '', + sorting: {}, + paging: {} + }; + + expect(model.wasRequested(params)).toBeFalsy(); + }); + + it('returns true if request is present in cache', function () { + var params = { + namespace: 'magento', + search: '', + sorting: {}, + paging: {} + }, + request = { + ids: ['1','2','3'], + params: params, + totalRecords: 3, + errorMessage: '' + }; + + model._requests = [request]; + + expect(model.wasRequested(params)).toBeTruthy(); + }); + }); + + describe('"onRequestComplete" method', function () { + + it('updates data and does not cache the request if caching is disabled', function () { + var model = new DataStorage({ + cacheRequests: false + }), + data = { + items: [] + }, + params = {}; + + spyOn(model, 'updateData'); + spyOn(model, 'cacheRequest'); + model.onRequestComplete(params, data); + expect(model.updateData).toHaveBeenCalled(); + expect(model.cacheRequest).not.toHaveBeenCalled(); + }); + + it('updates data and adds the request to cache if caching is enabled', function () { + var model = new DataStorage({ + cacheRequests: true + }), + data = { + items: [] + }, + params = {}; + + spyOn(model, 'updateData'); + spyOn(model, 'cacheRequest'); + model.onRequestComplete(params, data); + expect(model.updateData).toHaveBeenCalled(); + expect(model.cacheRequest).toHaveBeenCalled(); + }); + }); }); }); diff --git a/dev/tests/js/jasmine/tests/app/code/Magento/Ui/base/js/grid/sortBy.test.js b/dev/tests/js/jasmine/tests/app/code/Magento/Ui/base/js/grid/sortBy.test.js new file mode 100644 index 0000000000000..064329ac3bce1 --- /dev/null +++ b/dev/tests/js/jasmine/tests/app/code/Magento/Ui/base/js/grid/sortBy.test.js @@ -0,0 +1,83 @@ +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ + +/*eslint max-nested-callbacks: 0*/ +define([ + 'Magento_Ui/js/grid/sortBy' +], function (SortBy) { + 'use strict'; + + describe('Magento_Ui/js/grid/sortBy', function () { + + var sortByObj; + + beforeEach(function () { + sortByObj = new SortBy({ + options: [] + }); + }); + + describe('"preparedOptions" method', function () { + it('sort option will not available if sorting is disabled for the columns', function () { + var columns = { + sortable: false, + label: 'magento', + index: 'name' + }; + + sortByObj.preparedOptions([columns]); + expect(sortByObj.options[0]).toBeUndefined(); + expect(sortByObj.options[0]).toBeUndefined(); + }); + + it('sort option will available if sorting is enabled for the columns', function () { + var columns = { + sortable: true, + label: 'magento', + index: 'name' + }; + + sortByObj.preparedOptions([columns]); + expect(sortByObj.options[0].value).toEqual('name'); + expect(sortByObj.options[0].label).toEqual('magento'); + }); + + it('return "isVisible" method true if sorting is enabled for column', function () { + var columns = { + sortable: true, + label: 'magento', + index: 'name' + }; + + sortByObj.preparedOptions([columns]); + expect(sortByObj.isVisible()).toBeTruthy(); + }); + + it('return "isVisible" method false if sorting is disabled for column', function () { + var columns = { + sortable: false, + label: 'magento', + index: 'name' + }; + + sortByObj.preparedOptions([columns]); + expect(sortByObj.isVisible()).toBeFalsy(); + }); + }); + describe('"applyChanges" method', function () { + it('return applied options for sorting column', function () { + var applied = { + field: 'selectedOption', + direction: 'asc' + }; + + spyOn(sortByObj, 'selectedOption').and.returnValue('selectedOption'); + sortByObj.applyChanges(); + expect(sortByObj.applied()).toEqual(applied); + expect(sortByObj.selectedOption).toHaveBeenCalled(); + }); + }); + }); +}); diff --git a/dev/tests/js/jasmine/tests/app/code/Magento/Ui/base/js/modal/modal.test.js b/dev/tests/js/jasmine/tests/app/code/Magento/Ui/base/js/modal/modal.test.js index 8b00ecd3a2aed..3625d0898e942 100644 --- a/dev/tests/js/jasmine/tests/app/code/Magento/Ui/base/js/modal/modal.test.js +++ b/dev/tests/js/jasmine/tests/app/code/Magento/Ui/base/js/modal/modal.test.js @@ -10,18 +10,45 @@ define([ 'use strict'; describe('ui/js/modal/modal', function () { - var element = $('<div>some element</div>'), + + var element, + modal; + + beforeEach(function () { + element = $('<div id="element">Element</div>'); modal = element.modal({}).data('mage-modal'); + $(element).append('<h1 class="modal-title"' + + ' data-role="title">Title</h1>' + + '<span class="modal-subtitle"' + + ' data-role="subTitle"></span>'); + }); + + afterEach(function () { + $('.modal-title').remove(); + $('#element').remove(); + + }); + it('Check for modal definition', function () { expect(modal).toBeDefined(); }); + it('Show/hide function check', function () { expect(element.trigger('openModal')).toBe(element); expect(element.trigger('closeModal')).toBe(element); }); + it('Integration: modal created on page', function () { expect($(modal).length).toEqual(1); }); + + it('Verify set title', function () { + var newTitle = 'New modal title'; + + modal.setTitle(newTitle); + expect($(modal.options.modalTitle).text()).toContain(newTitle); + expect($(modal.options.modalTitle).find(modal.options.modalSubTitle).length).toBe(1); + }); }); }); diff --git a/dev/tests/js/jasmine/tests/app/code/Magento/Ui/base/js/modal/prompt.test.js b/dev/tests/js/jasmine/tests/app/code/Magento/Ui/base/js/modal/prompt.test.js index f2c74729e0ee8..7a0d3ac3fef91 100644 --- a/dev/tests/js/jasmine/tests/app/code/Magento/Ui/base/js/modal/prompt.test.js +++ b/dev/tests/js/jasmine/tests/app/code/Magento/Ui/base/js/modal/prompt.test.js @@ -10,8 +10,20 @@ define([ 'use strict'; describe('ui/js/modal/prompt', function () { - var element = $('<div>some element</div>'), + + var element, + prompt, + widget; + + beforeEach(function () { + element = $('<div id="element">some element</div>'), prompt = element.prompt({}); + widget = element.prompt({}).data('mage-prompt'); + }); + + afterEach(function () { + $('#element').remove(); + }); it('Check for modal definition', function () { expect(prompt).toBeDefined(); @@ -23,5 +35,12 @@ define([ it('Integration: modal created on page', function () { expect(prompt.length).toEqual(1); }); + it('Check cancel action', function () { + var cancel = spyOn(widget.options.actions, 'cancel'); + + jQuery('.modals-overlay').click(); + expect(widget.options.outerClickHandler).toBeDefined(); + expect(cancel).toHaveBeenCalled(); + }); }); }); diff --git a/dev/tests/js/jasmine/tests/app/code/Magento/Ui/frontend/js/model/messages.test.js b/dev/tests/js/jasmine/tests/app/code/Magento/Ui/frontend/js/model/messages.test.js new file mode 100644 index 0000000000000..54134c5b3166c --- /dev/null +++ b/dev/tests/js/jasmine/tests/app/code/Magento/Ui/frontend/js/model/messages.test.js @@ -0,0 +1,109 @@ +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ + +define([ + 'uiRegistry', + 'Magento_Ui/js/model/messages' +], function (registry, Constr) { + 'use strict'; + + describe('Magento_Ui/js/model/messages', function () { + + var obj, + errorMessageText, + successMessageText, + messageObj; + + beforeEach(function () { + obj = new Constr( + { + provider: 'provName', + name: '', + index: '' + }); + errorMessageText = 'Error message test'; + successMessageText = 'Success message test'; + + registry.set('provName', { + /** Stub */ + on: function () { + }, + + /** Stub */ + get: function () { + }, + + /** Stub */ + set: function () { + } + }); + }); + + it('adds massage without parameters', function () { + var type = []; + + messageObj = { + message: 'Message test' + }; + expect(obj.add(messageObj, type)).toEqual(true); + expect(type).toEqual([messageObj.message]); + }); + + it('add message with parameters', function () { + var returnedObj, + type = []; + + messageObj = { + message: 'Message test case %1, case %2 and case %3', + parameters: [ + 'one', + 'two', + 'three' + ] + }; + returnedObj = ['Message test case ' + messageObj.parameters[0] + ', case ' + + messageObj.parameters[1] + ' and case ' + messageObj.parameters[2]]; + + expect(obj.add(messageObj, type)).toEqual(true); + expect(type).toEqual(returnedObj); + }); + + it('add error message, get error message, verify has error message', function () { + messageObj = { + message: errorMessageText + }; + + expect(obj.hasMessages()).toEqual(false); + expect(obj.addErrorMessage(messageObj)).toEqual(true); + expect(obj.getErrorMessages()()).toEqual([errorMessageText]); + expect(obj.hasMessages()).toEqual(true); + }); + + it('add success message, get success message, verify has success message', function () { + messageObj = { + message: successMessageText + }; + + expect(obj.addSuccessMessage(messageObj)).toEqual(true); + expect(obj.getSuccessMessages()()).toEqual([successMessageText]); + expect(obj.hasMessages()).toEqual(true); + }); + + it('cleaning messages', function () { + messageObj = { + message: 'Message test case %1, case %2 and case %3', + parameters: [ + 'one', + 'two', + 'three' + ] + }; + expect(obj.addErrorMessage(messageObj)).toEqual(true); + obj.clear(); + expect(obj.getErrorMessages()()).toEqual([]); + expect(obj.hasMessages()).toEqual(false); + }); + }); +}); diff --git a/dev/tests/js/jasmine/tests/app/code/Magento/Vault/view/frontend/web/js/view/payment/method-renderer/vault.test.js b/dev/tests/js/jasmine/tests/app/code/Magento/Vault/view/frontend/web/js/view/payment/method-renderer/vault.test.js new file mode 100644 index 0000000000000..3724a3ab0c9f3 --- /dev/null +++ b/dev/tests/js/jasmine/tests/app/code/Magento/Vault/view/frontend/web/js/view/payment/method-renderer/vault.test.js @@ -0,0 +1,123 @@ +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ + +define([ + 'squire', + 'ko' +], function (Squire, ko) { + 'use strict'; + + var injector = new Squire(), + vault, + mocks = { + 'Magento_Checkout/js/model/checkout-data-resolver': { + resolveBillingAddress: jasmine.createSpy().and.returnValue(true) + }, + 'Magento_Checkout/js/checkout-data': { + setSelectedPaymentMethod: jasmine.createSpy().and.returnValue(false) + }, + 'Magento_Checkout/js/model/quote': { + billingAddress: ko.observable(null), + shippingAddress: ko.observable(null), + paymentMethod: ko.observable(null), + totals: ko.observable({}) + } + }, + billingAddress = { + city: 'Culver City', + company: 'Magento', + country_id: 'US',// jscs:ignore requireCamelCaseOrUpperCaseIdentifiers + firstname: 'John', + lastname: 'Doe', + postcode: '90230', + region: '', + region_id: '12',// jscs:ignore requireCamelCaseOrUpperCaseIdentifiers + street: { + 0: '6161 West Centinela Avenue', + 1: '' + }, + telephone: '+15555555555' + }; + + beforeEach(function (done) { + window.checkoutConfig = { + quoteData: { + /* jscs:disable requireCamelCaseOrUpperCaseIdentifiers */ + entity_Id: 1 + }, + formKey: 'formKey' + }; + injector.mock(mocks); + injector.require(['Magento_Vault/js/view/payment/method-renderer/vault'], function (Constr) { + var params = { + index: 'vaultIndex', + item: { + method: 'vault' + } + }; + + vault = new Constr(params); + // eslint-disable-next-line max-nested-callbacks + /** Stub */ + vault.isChecked = function () { + return mocks['Magento_Checkout/js/model/quote'].paymentMethod() ? + mocks['Magento_Checkout/js/model/quote'].paymentMethod().method : null; + }; + done(); + }); + }); + + afterEach(function () { + try { + injector.remove(); + injector.clean(); + } catch (e) { + } + mocks['Magento_Checkout/js/model/quote'].billingAddress(null); + mocks['Magento_Checkout/js/model/quote'].paymentMethod(null); + }); + + describe('Magento_Vault/js/view/payment/method-renderer/vault', function () { + + it('There is no payment method and billing address', function () { + expect(vault.isButtonActive()).toBeFalsy(); + + expect(vault.isActive()).toBeFalsy(); + expect(vault.isPlaceOrderActionAllowed()).toBeFalsy(); + }); + + it('Payment method exists, but place order action is not allowed', function () { + vault.selectPaymentMethod(); + expect(mocks['Magento_Checkout/js/model/quote'].paymentMethod().method).toEqual('vaultIndex'); + + expect(vault.isButtonActive()).toBeFalsy(); + + expect(vault.isActive()).toBeTruthy(); + expect(vault.isPlaceOrderActionAllowed()).toBeFalsy(); + + }); + + it('Billing address exists, but there is no selected payment method', function () { + mocks['Magento_Checkout/js/model/quote'].billingAddress(billingAddress); + + expect(vault.isButtonActive()).toBeFalsy(); + + expect(vault.isActive()).toBeFalsy(); + expect(vault.isPlaceOrderActionAllowed).toBeTruthy(); + }); + + it('Button is active', function () { + vault.selectPaymentMethod(); + expect(mocks['Magento_Checkout/js/model/quote'].paymentMethod().method).toEqual('vaultIndex'); + + mocks['Magento_Checkout/js/model/quote'].billingAddress(billingAddress); + + expect(vault.isButtonActive()).toBeTruthy(); + + expect(vault.isActive()).toBeTruthy(); + expect(vault.isPlaceOrderActionAllowed()).toBeTruthy(); + }); + }); +}); diff --git a/dev/tests/js/jasmine/tests/app/code/Magento/Wishlist/view/frontend/web/js/add-to-wishlist.test.js b/dev/tests/js/jasmine/tests/app/code/Magento/Wishlist/view/frontend/web/js/add-to-wishlist.test.js index 207d14bf990c3..f157ae27ee532 100644 --- a/dev/tests/js/jasmine/tests/app/code/Magento/Wishlist/view/frontend/web/js/add-to-wishlist.test.js +++ b/dev/tests/js/jasmine/tests/app/code/Magento/Wishlist/view/frontend/web/js/add-to-wishlist.test.js @@ -6,15 +6,22 @@ define([ 'jquery', 'Magento_Wishlist/js/add-to-wishlist' -], function ($) { +], function ($, Widget) { 'use strict'; describe('Testing addToWishlist widget', function () { - var wdContainer; + var wdContainer, + wishlistWidget, + eventMock = { + preventDefault: jasmine.createSpy(), + stopPropagation: jasmine.createSpy() + }; beforeEach(function () { wdContainer = $('<input type="hidden" class="bundle-option-11 product bundle option" \n' + 'name="bundle_option[11]" value="15" aria-required="true"/>'); + wishlistWidget = new Widget(); + $.fn.validation = {}; }); afterEach(function () { @@ -31,5 +38,15 @@ define([ }); expect(wdContainer.addToWishlist('option', 'bundleInfo')).toBe('test'); }); + + it('verify update wichlist with validate product qty, valid qty', function () { + var validation = spyOn($.fn, 'validation').and.returnValue(false); + + wishlistWidget._validateWishlistQty(eventMock); + expect(validation).toHaveBeenCalled(); + expect(eventMock.preventDefault).toHaveBeenCalled(); + expect(eventMock.stopPropagation).toHaveBeenCalled(); + }); + }); }); diff --git a/dev/tests/js/jasmine/tests/lib/mage/gallery/gallery.test.js b/dev/tests/js/jasmine/tests/lib/mage/gallery/gallery.test.js new file mode 100644 index 0000000000000..7700c3d64a1b7 --- /dev/null +++ b/dev/tests/js/jasmine/tests/lib/mage/gallery/gallery.test.js @@ -0,0 +1,105 @@ +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ + +/* eslint-disable max-nested-callbacks */ +define([ + 'mage/gallery/gallery', + 'jquery' +], function (Gallery, $) { + 'use strict'; + + var gallery, + options, + element, + jqueryDataMock, + originSpy; + + beforeEach(function () { + options = { + options: { + allowfullscreen: true, + arrows: true, + height: 700, + keyboard: true, + loop: true, + nav: 'thumbs', + navarrows: true, + navdir: 'horizontal', + navtype: 'slides', + showCaption: false, + thumbheight: 110, + thumbwidth: 88, + transition: 'slide', + transitionduration: 500, + width: 700 + }, + fullscreen: { + arrows: true, + loop: true, + nav: 'thumbs', + navarrows: false, + navdir: 'horizontal', + navtype: 'slides', + showCaption: false, + transition: 'slide', + transitionduration: 500 + }, + breakpoints: { + mobile: {} + }, + data: [ + { + caption: 'Simple product', + isMain: true, + position: '1', + type: 'image', + videoUrl: null, + thumb: '', + full: '', + img: '' + } + ], + magnifierOpts: { + enabled: false, + eventType: 'hover', + fullscreenzoom: '20', + height: 100, + largeWrapper: '[data-gallery-role=\'magnifier\']', + left: 10, + mode: 'outside', + thumb: '.fotorama__img', + top: 10, + width: 100, + zoomable: false + } + }; + element = $('<div class="gallery-placeholder' + + ' _block-content-loading" data-gallery-role="gallery-placeholder">' + + '<img alt="main product photo" class="gallery-placeholder__image" src="">' + + '</div>'); + }); + + describe('"initGallery" method', function () { + it('Verify gallery initialization', function () { + originSpy = $.fn.data; + jqueryDataMock = { + setOptions: jasmine.createSpy().and.returnValue(true), + updateOptions: jasmine.createSpy().and.returnValue(true) + }; + spyOn($.fn, 'data').and.callFake(function () { + return jqueryDataMock; + }); + + gallery = new Gallery(options, element); + expect(gallery.settings.$elementF.class).toBe(element[1]); + expect(gallery.settings.fullscreenConfig).toBeDefined(); + expect(gallery.settings.fotoramaApi).toBeDefined(); + expect(gallery.settings.data).toBeDefined(); + expect(gallery.settings.api).toBeDefined(); + + $.fn.data = originSpy; + }); + }); +}); diff --git a/dev/tests/js/jasmine/tests/lib/mage/translate.test.js b/dev/tests/js/jasmine/tests/lib/mage/translate.test.js index dc6c6ce7eb966..a29dbab63232b 100644 --- a/dev/tests/js/jasmine/tests/lib/mage/translate.test.js +++ b/dev/tests/js/jasmine/tests/lib/mage/translate.test.js @@ -33,7 +33,8 @@ define([ translation = { 'Hello World!': 'Hallo Welt!', - 'Some text with symbols!-+"%#*': 'Ein Text mit Symbolen!-+"%#*' + 'Some text with symbols!-+"%#*': 'Ein Text mit Symbolen!-+"%#*', + 'Text with empty value': '' }; $.mage.translate.add(translation); diff --git a/dev/tests/setup-integration/_files/Magento/TestSetupDeclarationModule1/etc/db_schema.xml b/dev/tests/setup-integration/_files/Magento/TestSetupDeclarationModule1/etc/db_schema.xml index ae6c98e4627d2..9de94cf0549be 100644 --- a/dev/tests/setup-integration/_files/Magento/TestSetupDeclarationModule1/etc/db_schema.xml +++ b/dev/tests/setup-integration/_files/Magento/TestSetupDeclarationModule1/etc/db_schema.xml @@ -48,6 +48,7 @@ <column xsi:type="longtext" name="longtext"/> <column xsi:type="mediumtext" name="mediumtext"/> <column xsi:type="varchar" name="varchar" length="254" nullable="true"/> + <column xsi:type="char" name="char" length="255" nullable="true"/> <column xsi:type="mediumblob" name="mediumblob"/> <column xsi:type="blob" name="blob"/> <column xsi:type="boolean" name="boolean"/> diff --git a/dev/tests/setup-integration/_files/Magento/TestSetupDeclarationModule1/fixture/declarative_installer/column_modification.php b/dev/tests/setup-integration/_files/Magento/TestSetupDeclarationModule1/fixture/declarative_installer/column_modification.php index a69e456ec4a8b..f2a34c338edc2 100644 --- a/dev/tests/setup-integration/_files/Magento/TestSetupDeclarationModule1/fixture/declarative_installer/column_modification.php +++ b/dev/tests/setup-integration/_files/Magento/TestSetupDeclarationModule1/fixture/declarative_installer/column_modification.php @@ -36,6 +36,7 @@ `longtext` longtext, `mediumtext` mediumtext, `varchar` varchar(100) DEFAULT NULL, + `char` char(255) DEFAULT NULL, `mediumblob` mediumblob, `blob` blob, `boolean` tinyint(1) DEFAULT \'1\', diff --git a/dev/tests/setup-integration/_files/Magento/TestSetupDeclarationModule1/fixture/declarative_installer/constraint_modification.php b/dev/tests/setup-integration/_files/Magento/TestSetupDeclarationModule1/fixture/declarative_installer/constraint_modification.php index 01e007edb7684..0b1ec6245478b 100644 --- a/dev/tests/setup-integration/_files/Magento/TestSetupDeclarationModule1/fixture/declarative_installer/constraint_modification.php +++ b/dev/tests/setup-integration/_files/Magento/TestSetupDeclarationModule1/fixture/declarative_installer/constraint_modification.php @@ -38,6 +38,7 @@ `longtext` longtext, `mediumtext` mediumtext, `varchar` varchar(254) DEFAULT NULL, + `char` char(255) DEFAULT NULL, `mediumblob` mediumblob, `blob` blob, `boolean` tinyint(1) DEFAULT NULL, diff --git a/dev/tests/setup-integration/_files/Magento/TestSetupDeclarationModule1/fixture/dry_run_log.php b/dev/tests/setup-integration/_files/Magento/TestSetupDeclarationModule1/fixture/dry_run_log.php index e642e57701149..7735bd433d9d2 100644 --- a/dev/tests/setup-integration/_files/Magento/TestSetupDeclarationModule1/fixture/dry_run_log.php +++ b/dev/tests/setup-integration/_files/Magento/TestSetupDeclarationModule1/fixture/dry_run_log.php @@ -38,6 +38,7 @@ `longtext` longtext NULL , `mediumtext` mediumtext NULL , `varchar` varchar(254) NULL , +`char` char(255) NULL , `mediumblob` mediumblob NULL , `blob` blob NULL , `boolean` BOOLEAN NULL , diff --git a/dev/tests/setup-integration/_files/Magento/TestSetupDeclarationModule1/fixture/valid_xml_revision_1.php b/dev/tests/setup-integration/_files/Magento/TestSetupDeclarationModule1/fixture/valid_xml_revision_1.php index a064d096f6d38..0109b644e546f 100644 --- a/dev/tests/setup-integration/_files/Magento/TestSetupDeclarationModule1/fixture/valid_xml_revision_1.php +++ b/dev/tests/setup-integration/_files/Magento/TestSetupDeclarationModule1/fixture/valid_xml_revision_1.php @@ -199,6 +199,12 @@ 'type' => 'mediumtext', 'name' => 'mediumtext', ], + 'char' => [ + 'type' => 'char', + 'name' => 'char', + 'length' => '255', + 'nullable' => 'true', + ], 'varchar' => [ 'type' => 'varchar', 'name' => 'varchar', diff --git a/dev/tests/static/framework/Magento/PhpStan/Formatters/FilteredErrorFormatter.php b/dev/tests/static/framework/Magento/PhpStan/Formatters/FilteredErrorFormatter.php index b3a4bd9ae0791..3da08f324d761 100644 --- a/dev/tests/static/framework/Magento/PhpStan/Formatters/FilteredErrorFormatter.php +++ b/dev/tests/static/framework/Magento/PhpStan/Formatters/FilteredErrorFormatter.php @@ -61,6 +61,7 @@ public function formatErrors(AnalysisResult $analysisResult, Output $output): in $clearedAnalysisResult = new AnalysisResult( $fileSpecificErrorsWithoutIgnoredErrors, $analysisResult->getNotFileSpecificErrors(), + $analysisResult->getWarnings(), $analysisResult->isDefaultLevelUsed(), $analysisResult->hasInferrablePropertyTypesFromConstructor(), $analysisResult->getProjectConfigFile() diff --git a/dev/tests/static/framework/Magento/Sniffs/Annotation/AnnotationFormatValidator.php b/dev/tests/static/framework/Magento/Sniffs/Annotation/AnnotationFormatValidator.php index 20442f9388235..cd4c8689ea004 100644 --- a/dev/tests/static/framework/Magento/Sniffs/Annotation/AnnotationFormatValidator.php +++ b/dev/tests/static/framework/Magento/Sniffs/Annotation/AnnotationFormatValidator.php @@ -94,7 +94,7 @@ private function validateSpacingBetweenShortAndLongDescriptions( && $tokens[$shortPtr]['line'] + 1 === $tokens[$shortPtrEnd]['line'] && $tokens[$shortPtrEnd]['code'] !== T_DOC_COMMENT_TAG ) { - $error = 'There must be exactly one blank line between lines'; + $error = 'There must be exactly one blank line between lines short and long descriptions'; $phpcsFile->addFixableError($error, $shortPtrEnd + 1, 'MethodAnnotation'); } if ($shortPtrEnd != $shortPtr) { diff --git a/dev/tests/static/framework/Magento/Sniffs/Annotation/ClassAnnotationStructureSniff.php b/dev/tests/static/framework/Magento/Sniffs/Annotation/ClassAnnotationStructureSniff.php deleted file mode 100644 index 43df5658bbe0d..0000000000000 --- a/dev/tests/static/framework/Magento/Sniffs/Annotation/ClassAnnotationStructureSniff.php +++ /dev/null @@ -1,125 +0,0 @@ -<?php -/** - * Copyright © Magento, Inc. All rights reserved. - * See COPYING.txt for license details. - */ -declare(strict_types=1); -namespace Magento\Sniffs\Annotation; - -use PHP_CodeSniffer\Sniffs\Sniff; -use PHP_CodeSniffer\Files\File; - -/** - * Sniff to validate structure of class, interface annotations - */ -class ClassAnnotationStructureSniff implements Sniff -{ - /** - * @var AnnotationFormatValidator - */ - private $annotationFormatValidator; - - /** - * @inheritdoc - */ - public function register() - { - return [ - T_CLASS - ]; - } - - /** - * AnnotationStructureSniff constructor. - */ - public function __construct() - { - $this->annotationFormatValidator = new AnnotationFormatValidator(); - } - - /** - * Validates whether annotation block exists for interface, abstract or final classes - * - * @param File $phpcsFile - * @param int $previousCommentClosePtr - * @param int $stackPtr - */ - private function validateInterfaceOrAbstractOrFinalClassAnnotationBlockExists( - File $phpcsFile, - int $previousCommentClosePtr, - int $stackPtr - ) : void { - $tokens = $phpcsFile->getTokens(); - if ($tokens[$stackPtr]['type'] === 'T_CLASS') { - if ($tokens[$stackPtr - 2]['type'] === 'T_ABSTRACT' && - $tokens[$stackPtr - 4]['content'] != $tokens[$previousCommentClosePtr]['content'] - ) { - $error = 'Interface or abstract class is missing annotation block'; - $phpcsFile->addFixableError($error, $stackPtr, 'ClassAnnotation'); - } - if ($tokens[$stackPtr - 2]['type'] === 'T_FINAL' && - $tokens[$stackPtr - 4]['content'] != $tokens[$previousCommentClosePtr]['content'] - ) { - $error = 'Final class is missing annotation block'; - $phpcsFile->addFixableError($error, $stackPtr, 'ClassAnnotation'); - } - } - } - - /** - * Validates whether annotation block exists - * - * @param File $phpcsFile - * @param int $previousCommentClosePtr - * @param int $stackPtr - */ - private function validateAnnotationBlockExists(File $phpcsFile, int $previousCommentClosePtr, int $stackPtr) : void - { - $tokens = $phpcsFile->getTokens(); - $this->validateInterfaceOrAbstractOrFinalClassAnnotationBlockExists( - $phpcsFile, - $previousCommentClosePtr, - $stackPtr - ); - if ($tokens[$stackPtr - 2]['content'] != 'class' && $tokens[$stackPtr - 2]['content'] != 'abstract' - && $tokens[$stackPtr - 2]['content'] != 'final' - && $tokens[$stackPtr - 2]['content'] !== $tokens[$previousCommentClosePtr]['content'] - ) { - $error = 'Class is missing annotation block'; - $phpcsFile->addFixableError($error, $stackPtr, 'ClassAnnotation'); - } - } - - /** - * @inheritdoc - */ - public function process(File $phpcsFile, $stackPtr) - { - $tokens = $phpcsFile->getTokens(); - $previousCommentClosePtr = $phpcsFile->findPrevious(T_DOC_COMMENT_CLOSE_TAG, $stackPtr - 1, 0); - if (!$previousCommentClosePtr) { - $phpcsFile->addError('Comment block is missing', $stackPtr -1, 'MethodArguments'); - return; - } - $this->validateAnnotationBlockExists($phpcsFile, $previousCommentClosePtr, $stackPtr); - $commentStartPtr = $phpcsFile->findPrevious(T_DOC_COMMENT_OPEN_TAG, $stackPtr - 1, 0); - $commentCloserPtr = $tokens[$commentStartPtr]['comment_closer']; - $emptyTypeTokens = [ - T_DOC_COMMENT_WHITESPACE, - T_DOC_COMMENT_STAR - ]; - $shortPtr = $phpcsFile->findNext($emptyTypeTokens, $commentStartPtr +1, $commentCloserPtr, true); - if ($shortPtr === false) { - $error = 'Annotation block is empty'; - $phpcsFile->addError($error, $commentStartPtr, 'MethodAnnotation'); - } else { - $this->annotationFormatValidator->validateDescriptionFormatStructure( - $phpcsFile, - $commentStartPtr, - (int) $shortPtr, - $previousCommentClosePtr, - $emptyTypeTokens - ); - } - } -} diff --git a/dev/tests/static/framework/Magento/TestFramework/Inspection/AbstractCommand.php b/dev/tests/static/framework/Magento/TestFramework/Inspection/AbstractCommand.php index 9ce9570b53ffb..f917559fa1bf9 100644 --- a/dev/tests/static/framework/Magento/TestFramework/Inspection/AbstractCommand.php +++ b/dev/tests/static/framework/Magento/TestFramework/Inspection/AbstractCommand.php @@ -9,6 +9,9 @@ */ namespace Magento\TestFramework\Inspection; +/** + * Abstract class for commands + */ abstract class AbstractCommand { /** @@ -47,6 +50,7 @@ public function __construct($reportFile) * @param array $whiteList Files/directories to be inspected * @param array $blackList Files/directories to be excluded from the inspection * @return bool + * phpcs:disable Generic.CodeAnalysis.UselessOverridingMethod */ public function run(array $whiteList, array $blackList = []) { @@ -118,7 +122,9 @@ abstract protected function _buildShellCmd($whiteList, $blackList); protected function _execShellCmd($shellCmd) { $output = []; + //phpcs:disable exec($shellCmd . ' 2>&1', $output, $this->_lastExitCode); + //phpcs:enable $this->_lastOutput = implode(PHP_EOL, $output); return $this->_lastExitCode === 0 ? $this->_lastOutput : false; } diff --git a/dev/tests/static/framework/Magento/TestFramework/Integrity/Library/Injectable.php b/dev/tests/static/framework/Magento/TestFramework/Integrity/Library/Injectable.php index f6ea0c393260a..524a4f7be3616 100644 --- a/dev/tests/static/framework/Magento/TestFramework/Integrity/Library/Injectable.php +++ b/dev/tests/static/framework/Magento/TestFramework/Integrity/Library/Injectable.php @@ -5,11 +5,12 @@ */ namespace Magento\TestFramework\Integrity\Library; -use Zend\Code\Reflection\ClassReflection; -use Zend\Code\Reflection\FileReflection; -use Zend\Code\Reflection\ParameterReflection; +use Laminas\Code\Reflection\ClassReflection; +use Laminas\Code\Reflection\FileReflection; +use Laminas\Code\Reflection\ParameterReflection; /** + * Provide dependencies for the file */ class Injectable { @@ -19,6 +20,8 @@ class Injectable protected $dependencies = []; /** + * Get dependencies + * * @param FileReflection $fileReflection * @return \ReflectionException[] * @throws \ReflectionException @@ -28,7 +31,7 @@ public function getDependencies(FileReflection $fileReflection) foreach ($fileReflection->getClasses() as $class) { /** @var ClassReflection $class */ foreach ($class->getMethods() as $method) { - /** @var \Zend\Code\Reflection\MethodReflection $method */ + /** @var \Laminas\Code\Reflection\MethodReflection $method */ if ($method->getDeclaringClass()->getName() != $class->getName()) { continue; } diff --git a/dev/tests/static/framework/tests/unit/testsuite/Magento/Sniffs/Annotation/ClassAnnotationStructureSniffTest.php b/dev/tests/static/framework/tests/unit/testsuite/Magento/Sniffs/Annotation/ClassAnnotationStructureSniffTest.php deleted file mode 100644 index afe559fdd6759..0000000000000 --- a/dev/tests/static/framework/tests/unit/testsuite/Magento/Sniffs/Annotation/ClassAnnotationStructureSniffTest.php +++ /dev/null @@ -1,86 +0,0 @@ -<?php -/** - * Copyright © Magento, Inc. All rights reserved. - * See COPYING.txt for license details. - */ -declare(strict_types=1); - -namespace Magento\Sniffs\Annotation; - -class ClassAnnotationStructureSniffTest extends \PHPUnit\Framework\TestCase -{ - /** - * @return array - */ - public function processDataProvider() - { - return [ - [ - 'ClassAnnotationFixture.php', - 'class_annotation_errors.txt', - ], - [ - 'AbstractClassAnnotationFixture.php', - 'abstract_class_annotation_errors.txt', - ], - [ - 'ClassAnnotationNoShortDescriptionFixture.php', - 'class_annotation_noshortdescription_errors.txt', - ], - [ - 'ClassAnnotationNoSpacingBetweenLinesFixture.php', - 'class_annotation_nospacingbetweenLines_errors.txt', - ] - ]; - } - - /** - * Copy a file - * - * @param string $source - * @param string $destination - */ - private function copyFile($source, $destination) : void - { - $sourcePath = $source; - $destinationPath = $destination; - $sourceDirectory = opendir($sourcePath); - while ($readFile = readdir($sourceDirectory)) { - if ($readFile != '.' && $readFile != '..') { - if (!file_exists($destinationPath . $readFile)) { - copy($sourcePath . $readFile, $destinationPath . $readFile); - } - } - } - closedir($sourceDirectory); - } - - /** - * @param string $fileUnderTest - * @param string $expectedReportFile - * @dataProvider processDataProvider - */ - public function testProcess($fileUnderTest, $expectedReportFile) - { - $reportFile = __DIR__ . DIRECTORY_SEPARATOR . '_files' . DIRECTORY_SEPARATOR . 'phpcs_report.txt'; - $this->copyFile( - __DIR__ . DIRECTORY_SEPARATOR . '_files' . DIRECTORY_SEPARATOR, - TESTS_TEMP_DIR . DIRECTORY_SEPARATOR - ); - $codeSniffer = new \Magento\TestFramework\CodingStandard\Tool\CodeSniffer( - 'Magento', - $reportFile, - new \Magento\TestFramework\CodingStandard\Tool\CodeSniffer\Wrapper() - ); - $result = $codeSniffer->run( - [TESTS_TEMP_DIR . DIRECTORY_SEPARATOR . $fileUnderTest] - ); - $actual = file_get_contents($reportFile); - $expected = file_get_contents( - TESTS_TEMP_DIR . DIRECTORY_SEPARATOR . $expectedReportFile - ); - unlink($reportFile); - $this->assertEquals(2, $result); - $this->assertEquals($expected, $actual); - } -} diff --git a/dev/tests/static/framework/tests/unit/testsuite/Magento/Sniffs/Annotation/_files/AbstractClassAnnotationFixture.php b/dev/tests/static/framework/tests/unit/testsuite/Magento/Sniffs/Annotation/_files/AbstractClassAnnotationFixture.php deleted file mode 100644 index 43973b3083e56..0000000000000 --- a/dev/tests/static/framework/tests/unit/testsuite/Magento/Sniffs/Annotation/_files/AbstractClassAnnotationFixture.php +++ /dev/null @@ -1,13 +0,0 @@ -<?php -/** - * Copyright © Magento, Inc. All rights reserved. - * See COPYING.txt for license details. - */ - -declare(strict_types=1); - -namespace Magento\Sniffs\Annotation; - -abstract class AbstractClassAnnotationFixture -{ -} diff --git a/dev/tests/static/framework/tests/unit/testsuite/Magento/Sniffs/Annotation/_files/ClassAnnotationFixture.php b/dev/tests/static/framework/tests/unit/testsuite/Magento/Sniffs/Annotation/_files/ClassAnnotationFixture.php deleted file mode 100644 index f9a997dcfeb9d..0000000000000 --- a/dev/tests/static/framework/tests/unit/testsuite/Magento/Sniffs/Annotation/_files/ClassAnnotationFixture.php +++ /dev/null @@ -1,20 +0,0 @@ -<?php -/** - * Copyright © Magento, Inc. All rights reserved. - * See COPYING.txt for license details. - */ - -declare(strict_types=1); - -namespace Magento\Sniffs\Annotation; - -class ClassAnnotationFixture -{ - /** - * - * @inheritdoc - */ - public function getProductListDefaultSortBy1() - { - } -} diff --git a/dev/tests/static/framework/tests/unit/testsuite/Magento/Sniffs/Annotation/_files/ClassAnnotationNoShortDescriptionFixture.php b/dev/tests/static/framework/tests/unit/testsuite/Magento/Sniffs/Annotation/_files/ClassAnnotationNoShortDescriptionFixture.php deleted file mode 100644 index be49cc6c788e9..0000000000000 --- a/dev/tests/static/framework/tests/unit/testsuite/Magento/Sniffs/Annotation/_files/ClassAnnotationNoShortDescriptionFixture.php +++ /dev/null @@ -1,16 +0,0 @@ -<?php -/** - * Copyright © Magento, Inc. All rights reserved. - * See COPYING.txt for license details. - */ - -declare(strict_types=1); - -namespace Magento\Sniffs\Annotation; - -/** - * @SuppressWarnings(PHPMD.NumberOfChildren) - */ -class ClassAnnotationNoShortDescriptionFixture -{ -} diff --git a/dev/tests/static/framework/tests/unit/testsuite/Magento/Sniffs/Annotation/_files/ClassAnnotationNoSpacingBetweenLinesFixture.php b/dev/tests/static/framework/tests/unit/testsuite/Magento/Sniffs/Annotation/_files/ClassAnnotationNoSpacingBetweenLinesFixture.php deleted file mode 100644 index 382185734c281..0000000000000 --- a/dev/tests/static/framework/tests/unit/testsuite/Magento/Sniffs/Annotation/_files/ClassAnnotationNoSpacingBetweenLinesFixture.php +++ /dev/null @@ -1,18 +0,0 @@ -<?php -/** - * Copyright © Magento, Inc. All rights reserved. - * See COPYING.txt for license details. - */ - -declare(strict_types=1); - -namespace Magento\Sniffs\Annotation; - -/** - * Class ClassAnnotationNoSpacingBetweenLinesFixture - * @see Magento\Sniffs\Annotation\_files - */ -class ClassAnnotationNoSpacingBetweenLinesFixture -{ - -} diff --git a/dev/tests/static/framework/tests/unit/testsuite/Magento/Sniffs/Annotation/_files/abstract_class_annotation_errors.txt b/dev/tests/static/framework/tests/unit/testsuite/Magento/Sniffs/Annotation/_files/abstract_class_annotation_errors.txt deleted file mode 100644 index 23698cfb72e27..0000000000000 --- a/dev/tests/static/framework/tests/unit/testsuite/Magento/Sniffs/Annotation/_files/abstract_class_annotation_errors.txt +++ /dev/null @@ -1,10 +0,0 @@ -FILE: ...o/Sniffs/Annotation/_fixtures/AbstractClassAnnotationFixture.php ----------------------------------------------------------------------- -FOUND 1 ERROR AFFECTING 1 LINE ----------------------------------------------------------------------- - 11 | ERROR | [x] Interface or abstract class is missing annotation - | | block ----------------------------------------------------------------------- -PHPCBF CAN FIX THE 1 MARKED SNIFF VIOLATIONS AUTOMATICALLY\n - - diff --git a/dev/tests/static/framework/tests/unit/testsuite/Magento/Sniffs/Annotation/_files/class_annotation_errors.txt b/dev/tests/static/framework/tests/unit/testsuite/Magento/Sniffs/Annotation/_files/class_annotation_errors.txt deleted file mode 100644 index aca2721131608..0000000000000 --- a/dev/tests/static/framework/tests/unit/testsuite/Magento/Sniffs/Annotation/_files/class_annotation_errors.txt +++ /dev/null @@ -1,11 +0,0 @@ - -FILE: ...Magento/Sniffs/Annotation/_fixtures/class_annotation_fixture.php ----------------------------------------------------------------------- -FOUND 1 ERROR AFFECTING 1 LINE ----------------------------------------------------------------------- - 11 | ERROR | [x] Class is missing annotation block ----------------------------------------------------------------------- -PHPCBF CAN FIX THE 1 MARKED SNIFF VIOLATIONS AUTOMATICALLY ----------------------------------------------------------------------- - - diff --git a/dev/tests/static/framework/tests/unit/testsuite/Magento/Sniffs/Annotation/_files/class_annotation_noshortdescription_errors.txt b/dev/tests/static/framework/tests/unit/testsuite/Magento/Sniffs/Annotation/_files/class_annotation_noshortdescription_errors.txt deleted file mode 100644 index ecc702c568934..0000000000000 --- a/dev/tests/static/framework/tests/unit/testsuite/Magento/Sniffs/Annotation/_files/class_annotation_noshortdescription_errors.txt +++ /dev/null @@ -1,14 +0,0 @@ -'\n -FILE: ...nnotation/_fixtures/ClassAnnotationNoShortDescriptionFixture.php\n -----------------------------------------------------------------------\n -FOUND 1 ERROR AFFECTING 1 LINE\n -----------------------------------------------------------------------\n - 11 | ERROR | [x] Missing short description\n -----------------------------------------------------------------------\n -PHPCBF CAN FIX THE 1 MARKED SNIFF VIOLATIONS AUTOMATICALLY\n -----------------------------------------------------------------------\n -\n -\n -' - - diff --git a/dev/tests/static/framework/tests/unit/testsuite/Magento/Sniffs/Annotation/_files/class_annotation_nospacingbetweenLines_errors.txt b/dev/tests/static/framework/tests/unit/testsuite/Magento/Sniffs/Annotation/_files/class_annotation_nospacingbetweenLines_errors.txt deleted file mode 100644 index 01c145ad6c29f..0000000000000 --- a/dev/tests/static/framework/tests/unit/testsuite/Magento/Sniffs/Annotation/_files/class_annotation_nospacingbetweenLines_errors.txt +++ /dev/null @@ -1,12 +0,0 @@ -'\n -FILE: ...tation/_fixtures/ClassAnnotationNoSpacingBetweenLinesFixture.php\n -----------------------------------------------------------------------\n -FOUND 1 ERROR AFFECTING 1 LINE\n -----------------------------------------------------------------------\n - 13 | ERROR | [x] There must be exactly one blank line between lines\n -----------------------------------------------------------------------\n -PHPCBF CAN FIX THE 1 MARKED SNIFF VIOLATIONS AUTOMATICALLY\n -----------------------------------------------------------------------\n -\n -\n -' \ No newline at end of file diff --git a/dev/tests/static/framework/tests/unit/testsuite/Magento/Test/Integrity/Library/InjectableTest.php b/dev/tests/static/framework/tests/unit/testsuite/Magento/Test/Integrity/Library/InjectableTest.php index 4e5bb8d9d0aa9..2ead129e34043 100644 --- a/dev/tests/static/framework/tests/unit/testsuite/Magento/Test/Integrity/Library/InjectableTest.php +++ b/dev/tests/static/framework/tests/unit/testsuite/Magento/Test/Integrity/Library/InjectableTest.php @@ -8,6 +8,7 @@ use Magento\TestFramework\Integrity\Library\Injectable; /** + * Test for Magento\TestFramework\Integrity\Library\Injectable */ class InjectableTest extends \PHPUnit\Framework\TestCase { @@ -17,7 +18,7 @@ class InjectableTest extends \PHPUnit\Framework\TestCase protected $injectable; /** - * @var \Zend\Code\Reflection\FileReflection + * @var \Laminas\Code\Reflection\FileReflection */ protected $fileReflection; @@ -38,23 +39,23 @@ public function setUp() { $this->injectable = new Injectable(); $this->fileReflection = $this->getMockBuilder( - \Zend\Code\Reflection\FileReflection::class + \Laminas\Code\Reflection\FileReflection::class )->disableOriginalConstructor()->getMock(); $classReflection = $this->getMockBuilder( - \Zend\Code\Reflection\ClassReflection::class + \Laminas\Code\Reflection\ClassReflection::class )->disableOriginalConstructor()->getMock(); $methodReflection = $this->getMockBuilder( - \Zend\Code\Reflection\MethodReflection::class + \Laminas\Code\Reflection\MethodReflection::class )->disableOriginalConstructor()->getMock(); $this->parameterReflection = $this->getMockBuilder( - \Zend\Code\Reflection\ParameterReflection::class + \Laminas\Code\Reflection\ParameterReflection::class )->disableOriginalConstructor()->getMock(); $this->declaredClass = $this->getMockBuilder( - \Zend\Code\Reflection\ClassReflection::class + \Laminas\Code\Reflection\ClassReflection::class )->disableOriginalConstructor()->getMock(); $methodReflection->expects( @@ -98,7 +99,7 @@ public function setUp() public function testGetDependencies() { $classReflection = $this->getMockBuilder( - \Zend\Code\Reflection\ClassReflection::class + \Laminas\Code\Reflection\ClassReflection::class )->disableOriginalConstructor()->getMock(); $classReflection->expects( @@ -106,7 +107,7 @@ public function testGetDependencies() )->method( 'getName' )->will( - $this->returnValue(\Magento\Core\Model\Object::class) + $this->returnValue(\Magento\Framework\DataObject::class) ); $this->parameterReflection->expects( @@ -118,7 +119,7 @@ public function testGetDependencies() ); $this->assertEquals( - [\Magento\Core\Model\Object::class], + [\Magento\Framework\DataObject::class], $this->injectable->getDependencies($this->fileReflection) ); } @@ -133,13 +134,14 @@ public function testGetDependenciesWithException() $this->parameterReflection->expects($this->once())->method('getClass')->will( $this->returnCallback( function () { - throw new \ReflectionException('Class Magento\Core\Model\Object does not exist'); + throw new \ReflectionException('Class Magento\Framework\DataObject does not exist'); } ) ); $this->assertEquals( - [\Magento\Core\Model\Object::class], + + [\Magento\Framework\DataObject::class], $this->injectable->getDependencies($this->fileReflection) ); } diff --git a/dev/tests/static/testsuite/Magento/Test/Integrity/ClassesTest.php b/dev/tests/static/testsuite/Magento/Test/Integrity/ClassesTest.php index b5a4e41b63279..c5447ef716eb2 100644 --- a/dev/tests/static/testsuite/Magento/Test/Integrity/ClassesTest.php +++ b/dev/tests/static/testsuite/Magento/Test/Integrity/ClassesTest.php @@ -501,7 +501,7 @@ private function removeSpecialCases(array $badClasses, string $file, string $con } // Remove usage of classes that have been declared as "use" or "include" - // Also deals with case like: "use \Zend\Code\Scanner\FileScanner, Magento\Tools\Di\Compiler\Log\Log;" + // Also deals with case like: "use \Laminas\Code\Scanner\FileScanner, Magento\Tools\Di\Compiler\Log\Log;" // (continued) where there is a comma separating two different classes. if (preg_match('/use\s.*[\\n]?.*' . str_replace('\\', '\\\\', $badClass) . '[\,\;]/', $contents)) { unset($badClasses[array_search($badClass, $badClasses)]); diff --git a/dev/tests/static/testsuite/Magento/Test/Integrity/ComposerTest.php b/dev/tests/static/testsuite/Magento/Test/Integrity/ComposerTest.php index 438d9530ec5d5..e45bb324306c0 100644 --- a/dev/tests/static/testsuite/Magento/Test/Integrity/ComposerTest.php +++ b/dev/tests/static/testsuite/Magento/Test/Integrity/ComposerTest.php @@ -45,6 +45,11 @@ class ComposerTest extends \PHPUnit\Framework\TestCase */ private static $moduleNameBlacklist; + /** + * @var string + */ + private static $magentoFrameworkLibraryName = 'magento/framework'; + public static function setUpBeforeClass() { self::$root = BP; @@ -68,6 +73,7 @@ public static function getBlacklist(string $pattern) { $blacklist = []; foreach (glob($pattern) as $list) { + //phpcs:ignore Magento2.Performance.ForeachArrayMerge $blacklist = array_merge($blacklist, file($list, FILE_IGNORE_NEW_LINES | FILE_SKIP_EMPTY_LINES)); } return $blacklist; @@ -311,9 +317,9 @@ private function assertDependsOnPhp(\StdClass $json) private function assertDependsOnFramework(\StdClass $json) { $this->assertObjectHasAttribute( - 'magento/framework', + self::$magentoFrameworkLibraryName, $json, - 'This component is expected to depend on magento/framework' + 'This component is expected to depend on ' . self::$magentoFrameworkLibraryName ); } @@ -496,4 +502,42 @@ private function checkProject() ); } } + + /** + * Check the correspondence between the root composer file and magento/framework composer file. + */ + public function testConsistencyOfDeclarationsInComposerFiles() + { + if (strpos(self::$rootJson['name'], 'magento/project-') !== false) { + // The Dependency test is skipped for vendor/magento build + self::markTestSkipped( + 'The build is running for composer installation. Consistency test for composer files is skipped.' + ); + } + + $componentRegistrar = new ComponentRegistrar(); + $magentoFrameworkLibraryDir = + $componentRegistrar->getPath(ComponentRegistrar::LIBRARY, self::$magentoFrameworkLibraryName); + $magentoFrameworkComposerFile = + json_decode( + file_get_contents($magentoFrameworkLibraryDir . DIRECTORY_SEPARATOR . 'composer.json'), + true + ); + + $inconsistentDependencies = []; + foreach ($magentoFrameworkComposerFile['require'] as $dependency => $constraint) { + if (isset(self::$rootJson['require'][$dependency]) + && self::$rootJson['require'][$dependency] !== $constraint + ) { + $inconsistentDependencies[] = $dependency; + } + } + + $this->assertEmpty( + $inconsistentDependencies, + 'There is a discrepancy between the declared versions of the following modules in "' + . self::$magentoFrameworkLibraryName . '" and the root composer.json: ' + . implode(', ', $inconsistentDependencies) + ); + } } diff --git a/dev/tests/static/testsuite/Magento/Test/Integrity/DependencyTest.php b/dev/tests/static/testsuite/Magento/Test/Integrity/DependencyTest.php index fa0d365061858..60855043a8c4e 100644 --- a/dev/tests/static/testsuite/Magento/Test/Integrity/DependencyTest.php +++ b/dev/tests/static/testsuite/Magento/Test/Integrity/DependencyTest.php @@ -154,6 +154,13 @@ class DependencyTest extends \PHPUnit\Framework\TestCase */ private static $routesWhitelist = null; + /** + * Redundant dependencies whitelist + * + * @var array|null + */ + private static $redundantDependenciesWhitelist = null; + /** * @var RouteMapper */ @@ -185,6 +192,7 @@ public static function setUpBeforeClass() self::_prepareMapLayoutHandles(); self::getLibraryWhiteLists(); + self::getRedundantDependenciesWhiteLists(); self::_initDependencies(); self::_initThemes(); @@ -206,6 +214,26 @@ private static function getLibraryWhiteLists() } } + /** + * Initialize redundant dependencies whitelist + * + * @return array + */ + private static function getRedundantDependenciesWhiteLists(): array + { + if (is_null(self::$redundantDependenciesWhitelist)) { + $redundantDependenciesWhitelistFilePattern = + realpath(__DIR__) . '/_files/dependency_test/whitelist/redundant_dependencies_*.php'; + $redundantDependenciesWhitelist = []; + foreach (glob($redundantDependenciesWhitelistFilePattern) as $fileName) { + //phpcs:ignore Magento2.Performance.ForeachArrayMerge + $redundantDependenciesWhitelist = array_merge($redundantDependenciesWhitelist, include $fileName); + } + self::$redundantDependenciesWhitelist = $redundantDependenciesWhitelist; + } + return self::$redundantDependenciesWhitelist; + } + /** * Initialize default themes */ @@ -532,6 +560,9 @@ public function testRedundant() foreach (array_keys(self::$mapDependencies) as $module) { $result = []; $redundant = $this->_getDependencies($module, self::TYPE_HARD, self::MAP_TYPE_REDUNDANT); + if (isset(self::$redundantDependenciesWhitelist[$module])) { + $redundant = array_diff($redundant, self::$redundantDependenciesWhitelist[$module]); + } if (!empty($redundant)) { $result[] = sprintf( "\r\nModule %s: %s [%s]", diff --git a/dev/tests/static/testsuite/Magento/Test/Integrity/Library/DependencyTest.php b/dev/tests/static/testsuite/Magento/Test/Integrity/Library/DependencyTest.php index a4baacbe4d169..b2c117960352a 100644 --- a/dev/tests/static/testsuite/Magento/Test/Integrity/Library/DependencyTest.php +++ b/dev/tests/static/testsuite/Magento/Test/Integrity/Library/DependencyTest.php @@ -11,7 +11,7 @@ use Magento\TestFramework\Integrity\Library\Injectable; use Magento\TestFramework\Integrity\Library\PhpParser\ParserFactory; use Magento\TestFramework\Integrity\Library\PhpParser\Tokens; -use Zend\Code\Reflection\FileReflection; +use Laminas\Code\Reflection\FileReflection; /** * Test check if Magento library components contain incorrect dependencies to application layer diff --git a/dev/tests/static/testsuite/Magento/Test/Integrity/Magento/Backend/_files/controller_acl_test_whitelist_ce.txt b/dev/tests/static/testsuite/Magento/Test/Integrity/Magento/Backend/_files/controller_acl_test_whitelist_ce.txt index 1119824f217bb..8561818022112 100644 --- a/dev/tests/static/testsuite/Magento/Test/Integrity/Magento/Backend/_files/controller_acl_test_whitelist_ce.txt +++ b/dev/tests/static/testsuite/Magento/Test/Integrity/Magento/Backend/_files/controller_acl_test_whitelist_ce.txt @@ -21,6 +21,7 @@ Magento\ConfigurableProduct\Controller\Adminhtml\Product\Initialization\Helper\P Magento\Downloadable\Controller\Adminhtml\Product\Initialization\Helper\Plugin\Downloadable Magento\Paypal\Controller\Adminhtml\Transparent\RequestSecureToken Magento\Paypal\Controller\Adminhtml\Transparent\Response +Magento\Paypal\Controller\Adminhtml\Transparent\Redirect Magento\Sales\Controller\Adminhtml\Order\CreditmemoLoader Magento\Search\Controller\Adminhtml\Synonyms\ResultPageBuilder Magento\Shipping\Controller\Adminhtml\Order\ShipmentLoader diff --git a/dev/tests/static/testsuite/Magento/Test/Integrity/Magento/Framework/Api/ExtensibleInterfacesTest.php b/dev/tests/static/testsuite/Magento/Test/Integrity/Magento/Framework/Api/ExtensibleInterfacesTest.php index 571ed4afbb6ce..9076c16981a49 100644 --- a/dev/tests/static/testsuite/Magento/Test/Integrity/Magento/Framework/Api/ExtensibleInterfacesTest.php +++ b/dev/tests/static/testsuite/Magento/Test/Integrity/Magento/Framework/Api/ExtensibleInterfacesTest.php @@ -155,7 +155,7 @@ private function checkSetExtensionAttributes( /** * Ensure that all classes extended from extensible classes implement getter and setter for extension attributes. */ - public function testExtensibleClassesWithMissingInterface() + public function testExtensibleClassesWithMissingInterface() //phpcs:ignore Generic.Metrics.NestingLevel { $invoker = new \Magento\Framework\App\Utility\AggregateInvoker($this); $invoker( @@ -170,7 +170,7 @@ function ($filename) { if (preg_match('/' . $extensibleClassPattern . '/', $fileContent) && !preg_match('/' . $abstractExtensibleClassPattern . '/', $fileContent) ) { - $fileReflection = new \Zend\Code\Reflection\FileReflection($filename, true); + $fileReflection = new \Laminas\Code\Reflection\FileReflection($filename, true); foreach ($fileReflection->getClasses() as $classReflection) { if ($classReflection->isSubclassOf(self::EXTENSIBLE_DATA_INTERFACE)) { $methodsToCheck = ['setExtensionAttributes', 'getExtensionAttributes']; @@ -241,6 +241,7 @@ protected function getFiles($dir, $pattern) { $files = glob($dir . '/' . $pattern, GLOB_NOSORT); foreach (glob($dir . '/*', GLOB_ONLYDIR | GLOB_NOSORT) as $newDir) { + // phpcs:ignore Magento2.Performance.ForeachArrayMerge $files = array_merge($files, $this->getFiles($newDir, $pattern)); } return $files; diff --git a/dev/tests/static/testsuite/Magento/Test/Integrity/Magento/Framework/Cache/ConfigTest.php b/dev/tests/static/testsuite/Magento/Test/Integrity/Magento/Framework/Cache/ConfigTest.php new file mode 100644 index 0000000000000..2d4a1dc0c2ce3 --- /dev/null +++ b/dev/tests/static/testsuite/Magento/Test/Integrity/Magento/Framework/Cache/ConfigTest.php @@ -0,0 +1,89 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +declare(strict_types = 1); + +namespace Magento\Test\Integrity\Magento\Framework\Cache; + +use Magento\Framework\Config\Dom\UrnResolver; +use Magento\Framework\TestFramework\Unit\Utility\XsdValidator; +use PHPUnit\Framework\TestCase; + +/** + * Unit test of the cache configuration + */ +class ConfigTest extends TestCase +{ + /** + * Path to xsd schema file + * @var string + */ + private $xsdSchema; + + /** + * @var UrnResolver + */ + private $urnResolver; + + /** + * @var XsdValidator + */ + private $xsdValidator; + + /** + * Setup environment for test + */ + protected function setUp(): void + { + if (!function_exists('libxml_set_external_entity_loader')) { + $this->markTestSkipped('Skipped on HHVM. Will be fixed in MAGETWO-45033'); + } + $this->urnResolver = new UrnResolver(); + $this->xsdSchema = $this->urnResolver->getRealPath( + 'urn:magento:framework:Cache/etc/cache.xsd' + ); + $this->xsdValidator = new XsdValidator(); + } + + /** + * Tests invalid configurations + * + * @param string $xmlString + * @param array $expectedError + * @dataProvider schemaCorrectlyIdentifiesInvalidXmlDataProvider + */ + public function testSchemaCorrectlyIdentifiesInvalidXml( + string $xmlString, + array $expectedError + ): void { + $actualError = $this->xsdValidator->validate( + $this->xsdSchema, + $xmlString + ); + $this->assertEquals($expectedError, $actualError); + } + + /** + * Tests valid configurations + */ + public function testSchemaCorrectlyIdentifiesValidXml(): void + { + $xmlString = file_get_contents(__DIR__ . '/_files/valid_cache_config.xml'); + $actualResult = $this->xsdValidator->validate( + $this->xsdSchema, + $xmlString + ); + + $this->assertEmpty($actualResult); + } + + /** + * Data provider with invalid xml array according to cache.xsd + */ + public function schemaCorrectlyIdentifiesInvalidXmlDataProvider(): array + { + return include __DIR__ . '/_files/invalidCacheConfigXmlArray.php'; + } +} diff --git a/dev/tests/static/testsuite/Magento/Test/Integrity/Magento/Framework/Cache/_files/invalidCacheConfigXmlArray.php b/dev/tests/static/testsuite/Magento/Test/Integrity/Magento/Framework/Cache/_files/invalidCacheConfigXmlArray.php new file mode 100644 index 0000000000000..8d2d631334a9b --- /dev/null +++ b/dev/tests/static/testsuite/Magento/Test/Integrity/Magento/Framework/Cache/_files/invalidCacheConfigXmlArray.php @@ -0,0 +1,52 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +return [ + 'without_type_handle' => [ + '<?xml version="1.0"?><config></config>', + ["Element 'config': Missing child element(s). Expected is ( type ).\nLine: 1\n"], + ], + 'cache_config_with_notallowed_attribute' => [ + '<?xml version="1.0"?><config>' . + '<type name="test" translate="label,description" instance="Class\Name" notallowed="some value">' . + '<label>Test</label><description>Test</description></type></config>', + ["Element 'type', attribute 'notallowed': The attribute 'notallowed' is not allowed.\nLine: 1\n"], + ], + 'cache_config_without_name_attribute' => [ + '<?xml version="1.0"?><config><type translate="label,description" instance="Class\Name">' . + '<label>Test</label><description>Test</description></type></config>', + ["Element 'type': The attribute 'name' is required but missing.\nLine: 1\n"], + ], + 'cache_config_without_instance_attribute' => [ + '<?xml version="1.0"?><config><type name="test" translate="label,description">' . + '<label>Test</label><description>Test</description></type></config>', + ["Element 'type': The attribute 'instance' is required but missing.\nLine: 1\n"], + ], + 'cache_config_without_label_element' => [ + '<?xml version="1.0"?><config><type name="test" translate="label,description" instance="Class\Name">' . + '<description>Test</description></type></config>', + ["Element 'type': Missing child element(s). Expected is ( label ).\nLine: 1\n"], + ], + 'cache_config_without_description_element' => [ + '<?xml version="1.0"?><config><type name="test" translate="label,description" instance="Class\Name">' . + '<label>Test</label></type></config>', + ["Element 'type': Missing child element(s). Expected is ( description ).\nLine: 1\n"], + ], + 'cache_config_without_child_elements' => [ + '<?xml version="1.0"?><config><type name="test" translate="label,description" instance="Class\Name">' . + '</type></config>', + ["Element 'type': Missing child element(s). Expected is one of ( label, description ).\nLine: 1\n"], + ], + 'cache_config_cache_name_not_unique' => [ + '<?xml version="1.0"?><config><type name="test" translate="label,description" instance="Class\Name1">' . + '<label>Test1</label><description>Test1</description></type>' . + '<type name="test" translate="label,description" instance="Class\Name2">' . + '<label>Test2</label><description>Test2</description></type></config>', + [ + "Element 'type': Duplicate key-sequence ['test'] in unique identity-constraint" + . " 'uniqueCacheName'.\nLine: 1\n" + ], + ], +]; diff --git a/dev/tests/static/testsuite/Magento/Test/Integrity/Magento/Framework/Cache/_files/valid_cache_config.xml b/dev/tests/static/testsuite/Magento/Test/Integrity/Magento/Framework/Cache/_files/valid_cache_config.xml new file mode 100644 index 0000000000000..ef45c083daf0d --- /dev/null +++ b/dev/tests/static/testsuite/Magento/Test/Integrity/Magento/Framework/Cache/_files/valid_cache_config.xml @@ -0,0 +1,17 @@ +<?xml version="1.0"?> +<!-- +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +--> +<config xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="urn:magento:framework:Cache/etc/cache.xsd"> + <type name="type_name_1" translate="label,description" instance="Instance1\Class\Name"> + <label>Type1</label> + <description>Type1</description> + </type> + <type name="type_name_2" translate="label,description" instance="Instance2\Class\Name"> + <label>Type2</label> + <description>Type2</description> + </type> +</config> diff --git a/dev/tests/static/testsuite/Magento/Test/Integrity/PublicCodeTest.php b/dev/tests/static/testsuite/Magento/Test/Integrity/PublicCodeTest.php index 6dd9d2f2bddfd..a3dc60641795c 100644 --- a/dev/tests/static/testsuite/Magento/Test/Integrity/PublicCodeTest.php +++ b/dev/tests/static/testsuite/Magento/Test/Integrity/PublicCodeTest.php @@ -42,6 +42,7 @@ private function getWhitelist(): array ); $whiteListItems = []; foreach (glob($whiteListFiles) as $fileName) { + // phpcs:ignore Magento2.Performance.ForeachArrayMerge $whiteListItems = array_merge( $whiteListItems, file($fileName, FILE_IGNORE_NEW_LINES | FILE_SKIP_EMPTY_LINES) @@ -167,7 +168,7 @@ public function publicPHPTypesDataProvider() * Retrieve list of classes and interfaces declared in the file * * @param string $file - * @return \Zend\Code\Scanner\ClassScanner[] + * @return \Laminas\Code\Scanner\ClassScanner[] */ private function getDeclaredClassesAndInterfaces($file) { diff --git a/dev/tests/static/testsuite/Magento/Test/Integrity/_files/blacklist/exception_hierarchy.txt b/dev/tests/static/testsuite/Magento/Test/Integrity/_files/blacklist/exception_hierarchy.txt index 8eb3392476400..195c88274ae4a 100644 --- a/dev/tests/static/testsuite/Magento/Test/Integrity/_files/blacklist/exception_hierarchy.txt +++ b/dev/tests/static/testsuite/Magento/Test/Integrity/_files/blacklist/exception_hierarchy.txt @@ -10,6 +10,4 @@ \Magento\Framework\DB\DataConverter\DataConversionException \Magento\Framework\DB\FieldDataConversionException \Magento\Catalog\Model\Product\Image\NotLoadInfoImageException -\Magento\Signifyd\Model\SignifydGateway\GatewayException -\Magento\Signifyd\Model\SignifydGateway\ApiCallException \Magento\Framework\MessageQueue\ConnectionLostException diff --git a/dev/tests/static/testsuite/Magento/Test/Integrity/_files/dependency_test/tables_ce.php b/dev/tests/static/testsuite/Magento/Test/Integrity/_files/dependency_test/tables_ce.php index 3fb53be2ec400..b120c65d3a759 100644 --- a/dev/tests/static/testsuite/Magento/Test/Integrity/_files/dependency_test/tables_ce.php +++ b/dev/tests/static/testsuite/Magento/Test/Integrity/_files/dependency_test/tables_ce.php @@ -190,7 +190,6 @@ 'oauth_consumer' => 'Magento\Integration', 'oauth_nonce' => 'Magento\Integration', 'oauth_token' => 'Magento\Integration', - 'authorizenet_debug' => 'Magento\Authorizenet', 'admin_passwords' => 'Magento\User', 'paypal_cert' => 'Magento\Paypal', 'paypal_payment_transaction' => 'Magento\Paypal', diff --git a/dev/tests/static/testsuite/Magento/Test/Js/_files/blacklist/magento.txt b/dev/tests/static/testsuite/Magento/Test/Js/_files/blacklist/magento.txt index 181ecd43ce564..a143424feb015 100644 --- a/dev/tests/static/testsuite/Magento/Test/Js/_files/blacklist/magento.txt +++ b/dev/tests/static/testsuite/Magento/Test/Js/_files/blacklist/magento.txt @@ -2,7 +2,6 @@ app/code/Magento/Customer/view/frontend/web/js/zxcvbn.js app/code/Magento/Swagger/view/frontend/web/swagger-ui/js/swagger-ui-bundle.js app/code/Magento/Swagger/view/frontend/web/swagger-ui/js/swagger-ui-standalone-preset.js app/code/Magento/Tinymce3/view/base/web/tiny_mce/**/*.js -app/code/Magento/Authorizenet/view/adminhtml/web/js/direct-post.js app/code/Magento/Catalog/view/adminhtml/web/catalog/product/composite/configure.js app/code/Magento/Rule/view/adminhtml/web/rules.js app/code/Magento/Sales/view/adminhtml/web/order/create/giftmessage.js diff --git a/dev/tests/static/testsuite/Magento/Test/Legacy/ModuleDBChangeTest.php b/dev/tests/static/testsuite/Magento/Test/Legacy/ModuleDBChangeTest.php index 876944e0027b4..15b3dc0e0a899 100644 --- a/dev/tests/static/testsuite/Magento/Test/Legacy/ModuleDBChangeTest.php +++ b/dev/tests/static/testsuite/Magento/Test/Legacy/ModuleDBChangeTest.php @@ -64,21 +64,6 @@ public static function setUpBeforeClass() } } - /** - * Test changes for module.xml files - */ - public function testModuleXmlFiles() - { - if (!self::$actualBranch) { - preg_match_all('|etc/module\.xml$|mi', self::$changedFileList, $matches); - $this->assertEmpty( - reset($matches), - 'module.xml changes for patch releases in non-actual branches are not allowed:' . PHP_EOL . - implode(PHP_EOL, array_values(reset($matches))) - ); - } - } - /** * Test changes for files in Module Setup dir */ diff --git a/dev/tests/static/testsuite/Magento/Test/Legacy/_files/autogenerated_class_not_in_constructor_whitelist.php b/dev/tests/static/testsuite/Magento/Test/Legacy/_files/autogenerated_class_not_in_constructor_whitelist.php index ba8f7e3dd1420..f8979ef6ab15e 100644 --- a/dev/tests/static/testsuite/Magento/Test/Legacy/_files/autogenerated_class_not_in_constructor_whitelist.php +++ b/dev/tests/static/testsuite/Magento/Test/Legacy/_files/autogenerated_class_not_in_constructor_whitelist.php @@ -9,5 +9,6 @@ 'Symfony\Component\Console\Application', 'Customer\Address\Attributes', 'Order\Address\Type', - 'Order\Address\Attributes' + 'Order\Address\Attributes', + 'This\Is\Another\Ns', ]; diff --git a/dev/tests/static/testsuite/Magento/Test/Legacy/_files/blacklist/obsolete_mage.php b/dev/tests/static/testsuite/Magento/Test/Legacy/_files/blacklist/obsolete_mage.php index 62816cd4e4f76..e4d5407a341b9 100644 --- a/dev/tests/static/testsuite/Magento/Test/Legacy/_files/blacklist/obsolete_mage.php +++ b/dev/tests/static/testsuite/Magento/Test/Legacy/_files/blacklist/obsolete_mage.php @@ -11,5 +11,6 @@ 'lib/internal/Magento/Framework/ObjectManager/Test/Unit/Factory/CompiledTest.php', 'dev/tests/integration/testsuite/Magento/Indexer/Model/Config/_files/result.php', 'lib/internal/Magento/Framework/Encryption/Test/Unit/EncryptorTest.php', - 'lib/internal/Magento/Framework/Encryption/Test/Unit/CryptTest.php' + 'lib/internal/Magento/Framework/Encryption/Test/Unit/CryptTest.php', + 'setup/src/Zend/Mvc/Controller/LazyControllerAbstractFactory.php', ]; diff --git a/dev/tests/static/testsuite/Magento/Test/Legacy/_files/copyright/blacklist.php b/dev/tests/static/testsuite/Magento/Test/Legacy/_files/copyright/blacklist.php index 242e4ebb22a54..48eb64ffea27e 100644 --- a/dev/tests/static/testsuite/Magento/Test/Legacy/_files/copyright/blacklist.php +++ b/dev/tests/static/testsuite/Magento/Test/Legacy/_files/copyright/blacklist.php @@ -10,4 +10,5 @@ '/setup\/src\/Zend\/Mvc\/Controller\/LazyControllerAbstractFactory\.php/', '/app\/code\/(?!Magento)[^\/]*/', '#dev/tests/setup-integration/testsuite/Magento/Developer/_files/\S*\.xml$#', + '/lib\/internal\/Magento\/Framework\/File\/Test\/Unit\/_files\/blank.html$/' ]; diff --git a/dev/tests/static/testsuite/Magento/Test/Legacy/_files/obsolete_classes.php b/dev/tests/static/testsuite/Magento/Test/Legacy/_files/obsolete_classes.php index af785c28db414..99f27bec1bb57 100644 --- a/dev/tests/static/testsuite/Magento/Test/Legacy/_files/obsolete_classes.php +++ b/dev/tests/static/testsuite/Magento/Test/Legacy/_files/obsolete_classes.php @@ -4238,9 +4238,23 @@ 'Magento\Elasticsearch\Test\Unit\Model\SearchAdapter\ConnectionManagerTest', 'Magento\Elasticsearch\Test\Unit\SearchAdapter\ConnectionManagerTest' ], - ['Zend_Feed', 'Zend\Feed'], - ['Zend_Uri', 'Zend\Uri\Uri'], + ['Zend_Feed', 'Laminas\Feed'], + ['Zend_Uri', 'Laminas\Uri\Uri'], ['Zend_Mime', 'Magento\Framework\HTTP\Mime'], ['Magento\Framework\Encryption\Crypt', 'Magento\Framework\Encryption\EncryptionAdapterInterface'], ['Magento\Wishlist\Setup\Patch\Schema\AddProductIdConstraint'], + ['Magento\Elasticsearch\Block\Adminhtml\System\Config\TestConnection'], + ['Magento\Elasticsearch\Model\Adapter\BatchDataMapper\CategoryFieldsProvider'], + ['Magento\Elasticsearch\Model\Adapter\DataMapper\ProductDataMapper'], + ['Magento\Elasticsearch\Model\Adapter\FieldMapper\ProductFieldMapper'], + ['Magento\Elasticsearch\Model\Client\Elasticsearch'], + ['Magento\Elasticsearch\SearchAdapter\Aggregation\Interval'], + ['Magento\Elasticsearch\Elasticsearch5\Model\Adapter\FieldType'], + ['Magento\Elasticsearch\Model\Adapter\DataMapperInterface'], + ['Magento\Elasticsearch\Elasticsearch5\Model\Adapter\DataMapper\ProductDataMapperProxy'], + ['Magento\Elasticsearch\Elasticsearch5\Model\Adapter\DataMapper\ProductDataMapper'], + ['Magento\Elasticsearch\Model\Adapter\DataMapper\DataMapperResolver'], + ['Magento\Elasticsearch\Model\Adapter\Container\Attribute'], + ['PHPUnit_Framework_MockObject_MockObject', 'PHPUnit\Framework\MockObject\MockObject'], + ['PHPUnit\Framework\BaseTestListener'], ]; diff --git a/dev/tests/static/testsuite/Magento/Test/Legacy/_files/obsolete_namespaces.php b/dev/tests/static/testsuite/Magento/Test/Legacy/_files/obsolete_namespaces.php index 0d9da334ea3ab..2849e61c0208c 100644 --- a/dev/tests/static/testsuite/Magento/Test/Legacy/_files/obsolete_namespaces.php +++ b/dev/tests/static/testsuite/Magento/Test/Legacy/_files/obsolete_namespaces.php @@ -200,4 +200,10 @@ ['Magento\BraintreeTwo', 'Magento\Braintree'], ['Magento\MysqlMq\Model\Resource', 'Magento\MysqlMq\Model\ResourceModel'], ['Magento\BulkOperations', 'Magento\AsynchronousOperations'], + ['Zend', 'Laminas'], + ['Magento\Signifyd'], + ['Magento\Authorizenet'], + ['Magento\AuthorizenetAcceptjs'], + ['Magento\AuthorizenetCardinal'], + ['Magento\AuthorizenetGraphQl'], ]; diff --git a/dev/tests/static/testsuite/Magento/Test/Php/LiveCodeTest.php b/dev/tests/static/testsuite/Magento/Test/Php/LiveCodeTest.php index 8ccda77a25191..b7717492fd124 100644 --- a/dev/tests/static/testsuite/Magento/Test/Php/LiveCodeTest.php +++ b/dev/tests/static/testsuite/Magento/Test/Php/LiveCodeTest.php @@ -142,7 +142,7 @@ private static function getAddedFilesList($changedFilesBaseDir) function () { // if no list files, probably, this is the dev environment // phpcs:ignore Generic.PHP.NoSilencedErrors,Magento2.Security.InsecureFunction - @exec('git diff --cached --name-only', $addedFiles); + @exec('git diff --cached --name-only --diff-filter=A', $addedFiles); return $addedFiles; } ); diff --git a/dev/tests/static/testsuite/Magento/Test/Php/_files/phpcpd/blacklist/common.txt b/dev/tests/static/testsuite/Magento/Test/Php/_files/phpcpd/blacklist/common.txt index 59b171a86e1cd..fc8c0c17447d8 100644 --- a/dev/tests/static/testsuite/Magento/Test/Php/_files/phpcpd/blacklist/common.txt +++ b/dev/tests/static/testsuite/Magento/Test/Php/_files/phpcpd/blacklist/common.txt @@ -1,5 +1,4 @@ Magento/Adminhtml -Magento/Authorizenet/Model Magento/Backend Magento/Bundle Magento/Catalog/Block/Product/ProductList @@ -217,4 +216,5 @@ Magento/Config/App/Config/Type Magento/InventoryReservationCli/Test/Integration Magento/InventoryAdminUi/Controller/Adminhtml Magento/Newsletter/Model/Queue -Magento/Framework/Mail/Template \ No newline at end of file +Magento/Framework/Mail/Template +Magento/CheckoutAgreements/Model/Checkout/Plugin diff --git a/dev/tests/static/testsuite/Magento/Test/Php/_files/phpstan/blacklist/common.txt b/dev/tests/static/testsuite/Magento/Test/Php/_files/phpstan/blacklist/common.txt index f54defbd57604..d153593a5ed4f 100644 --- a/dev/tests/static/testsuite/Magento/Test/Php/_files/phpstan/blacklist/common.txt +++ b/dev/tests/static/testsuite/Magento/Test/Php/_files/phpstan/blacklist/common.txt @@ -5,6 +5,7 @@ # dev/tests/static/framework/bootstrap.php lib/internal/Magento/Framework/Interception/Test/Unit/Config/ConfigTest.php lib/internal/Magento/Framework/Cache/Backend/Eaccelerator.php +lib/internal/Magento/Framework/Image/Adapter/ImageMagick.php dev/tests/integration/framework/deployTestModules.php dev/tests/integration/testsuite/Magento/Framework/Session/ConfigTest.php dev/tests/integration/testsuite/Magento/Framework/Session/SessionManagerTest.php diff --git a/dev/tools/UpgradeScripts/pre_composer_update_2.3.php b/dev/tools/UpgradeScripts/pre_composer_update_2.3.php index 665af637443be..e6f1ddb31c4a3 100644 --- a/dev/tools/UpgradeScripts/pre_composer_update_2.3.php +++ b/dev/tools/UpgradeScripts/pre_composer_update_2.3.php @@ -17,7 +17,7 @@ Steps included: - Require new version of the metapackage - Update "require-dev" section - - Add "Zend\\Mvc\\Controller\\": "setup/src/Zend/Mvc/Controller/" to composer.json "autoload":"psr-4" section + - Add "Laminas\\Mvc\\Controller\\": "setup/src/Zend/Mvc/Controller/" to composer.json "autoload":"psr-4" section - Update Magento/Updater if it's installed - Update name, version, and description fields in the root composer.json @@ -248,7 +248,7 @@ runComposer('remove --dev sjparkinson/static-review fabpot/php-cs-fixer --no-update'); output('\nAdding "Zend\\\\Mvc\\\\Controller\\\\": "setup/src/Zend/Mvc/Controller/" to "autoload": "psr-4"'); - $composerData['autoload']['psr-4']['Zend\\Mvc\\Controller\\'] = 'setup/src/Zend/Mvc/Controller/'; + $composerData['autoload']['psr-4']['Laminas\\Mvc\\Controller\\'] = 'setup/src/Zend/Mvc/Controller/'; if (preg_match('/^magento\/project\-(community|enterprise)\-edition$/', $composerData['name'])) { output('\nUpdating project name, version, and description'); diff --git a/lib/internal/Magento/Framework/Amqp/registration.php b/lib/internal/Magento/Framework/Amqp/registration.php index ab14d5aab8e94..0e97ffb62a8b8 100644 --- a/lib/internal/Magento/Framework/Amqp/registration.php +++ b/lib/internal/Magento/Framework/Amqp/registration.php @@ -4,6 +4,6 @@ * See COPYING.txt for license details. */ -use \Magento\Framework\Component\ComponentRegistrar; +use Magento\Framework\Component\ComponentRegistrar; ComponentRegistrar::register(ComponentRegistrar::LIBRARY, 'magento/framework-amqp', __DIR__); diff --git a/lib/internal/Magento/Framework/Api/AbstractExtensibleObject.php b/lib/internal/Magento/Framework/Api/AbstractExtensibleObject.php index 97c24167d47e1..902709bbedcd3 100644 --- a/lib/internal/Magento/Framework/Api/AbstractExtensibleObject.php +++ b/lib/internal/Magento/Framework/Api/AbstractExtensibleObject.php @@ -11,7 +11,10 @@ * Base Class for extensible data Objects * * @SuppressWarnings(PHPMD.NumberOfChildren) + * phpcs:disable Magento2.Classes.AbstractApi * @api + * @deprecated + * @see \Magento\Framework\Model\AbstractExtensibleModel */ abstract class AbstractExtensibleObject extends AbstractSimpleObject implements CustomAttributesDataInterface { diff --git a/lib/internal/Magento/Framework/App/Action/Action.php b/lib/internal/Magento/Framework/App/Action/Action.php index b68e7e873be6a..6a3b665c7d3ed 100644 --- a/lib/internal/Magento/Framework/App/Action/Action.php +++ b/lib/internal/Magento/Framework/App/Action/Action.php @@ -3,20 +3,29 @@ * Copyright © Magento, Inc. All rights reserved. * See COPYING.txt for license details. */ + namespace Magento\Framework\App\Action; +use Magento\Framework\App\ActionFlag; use Magento\Framework\App\RequestInterface; +use Magento\Framework\App\Response\RedirectInterface; use Magento\Framework\App\ResponseInterface; -use Magento\Framework\Controller\ResultInterface; +use Magento\Framework\App\ViewInterface; +use Magento\Framework\Event\ManagerInterface as EventManagerInterface; use Magento\Framework\Exception\NotFoundException; +use Magento\Framework\Message\ManagerInterface as MessageManagerInterface; +use Magento\Framework\ObjectManagerInterface; +use Magento\Framework\Profiler; +use Magento\Framework\UrlInterface; /** * Extend from this class to create actions controllers in frontend area of your application. * It contains standard action behavior (event dispatching, flag checks) * Action classes that do not extend from this class will lose this behavior and might not function correctly * - * TODO: Remove this class. Allow implementation of Action Controllers by just implementing Action Interface. + * @deprecated Use \Magento\Framework\App\ActionInterface * + * phpcs:disable Magento2.Classes.AbstractApi * @api * @SuppressWarnings(PHPMD.CouplingBetweenObjects) * @SuppressWarnings(PHPMD.NumberOfChildren) @@ -24,7 +33,7 @@ abstract class Action extends AbstractAction { /** - * @var \Magento\Framework\ObjectManagerInterface + * @var ObjectManagerInterface */ protected $_objectManager; @@ -37,32 +46,32 @@ abstract class Action extends AbstractAction protected $_sessionNamespace; /** - * @var \Magento\Framework\Event\ManagerInterface + * @var EventManagerInterface */ protected $_eventManager; /** - * @var \Magento\Framework\App\ActionFlag + * @var ActionFlag */ protected $_actionFlag; /** - * @var \Magento\Framework\App\Response\RedirectInterface + * @var RedirectInterface */ protected $_redirect; /** - * @var \Magento\Framework\App\ViewInterface + * @var ViewInterface */ protected $_view; /** - * @var \Magento\Framework\UrlInterface + * @var UrlInterface */ protected $_url; /** - * @var \Magento\Framework\Message\ManagerInterface + * @var MessageManagerInterface */ protected $messageManager; @@ -92,35 +101,15 @@ public function dispatch(RequestInterface $request) { $this->_request = $request; $profilerKey = 'CONTROLLER_ACTION:' . $request->getFullActionName(); - $eventParameters = ['controller_action' => $this, 'request' => $request]; - $this->_eventManager->dispatch('controller_action_predispatch', $eventParameters); - $this->_eventManager->dispatch('controller_action_predispatch_' . $request->getRouteName(), $eventParameters); - $this->_eventManager->dispatch( - 'controller_action_predispatch_' . $request->getFullActionName(), - $eventParameters - ); - \Magento\Framework\Profiler::start($profilerKey); + Profiler::start($profilerKey); $result = null; if ($request->isDispatched() && !$this->_actionFlag->get('', self::FLAG_NO_DISPATCH)) { - \Magento\Framework\Profiler::start('action_body'); + Profiler::start('action_body'); $result = $this->execute(); - \Magento\Framework\Profiler::start('postdispatch'); - if (!$this->_actionFlag->get('', self::FLAG_NO_POST_DISPATCH)) { - $this->_eventManager->dispatch( - 'controller_action_postdispatch_' . $request->getFullActionName(), - $eventParameters - ); - $this->_eventManager->dispatch( - 'controller_action_postdispatch_' . $request->getRouteName(), - $eventParameters - ); - $this->_eventManager->dispatch('controller_action_postdispatch', $eventParameters); - } - \Magento\Framework\Profiler::stop('postdispatch'); - \Magento\Framework\Profiler::stop('action_body'); + Profiler::stop('action_body'); } - \Magento\Framework\Profiler::stop($profilerKey); + Profiler::stop($profilerKey); return $result ?: $this->_response; } @@ -159,9 +148,9 @@ protected function _forward($action, $controller = null, $module = null, array $ /** * Set redirect into response * - * @param string $path - * @param array $arguments - * @return ResponseInterface + * @param string $path + * @param array $arguments + * @return ResponseInterface */ protected function _redirect($path, $arguments = []) { @@ -170,6 +159,8 @@ protected function _redirect($path, $arguments = []) } /** + * Returns ActionFlag value + * * @return \Magento\Framework\App\ActionFlag */ public function getActionFlag() diff --git a/lib/internal/Magento/Framework/App/Action/Plugin/ActionFlagNoDispatchPlugin.php b/lib/internal/Magento/Framework/App/Action/Plugin/ActionFlagNoDispatchPlugin.php new file mode 100644 index 0000000000000..263e3663513d3 --- /dev/null +++ b/lib/internal/Magento/Framework/App/Action/Plugin/ActionFlagNoDispatchPlugin.php @@ -0,0 +1,53 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ + +declare(strict_types=1); + +namespace Magento\Framework\App\Action\Plugin; + +use Magento\Framework\App\ActionFlag; +use Magento\Framework\App\ActionInterface; +use Magento\Framework\App\ResponseInterface; + +/** + * Do not call Action::execute() if the action flag FLAG_NO_DISPATCH is set. + */ +class ActionFlagNoDispatchPlugin +{ + /** + * @var ActionFlag + */ + private $actionFlag; + + /** + * @var ResponseInterface + */ + private $response; + + /** + * @param ActionFlag $actionFlag + * @param ResponseInterface $response + */ + public function __construct(ActionFlag $actionFlag, ResponseInterface $response) + { + $this->actionFlag = $actionFlag; + $this->response = $response; + } + + /** + * Do not call proceed if the no dispatch action flag is set. + * + * @param ActionInterface $subject + * @param callable $proceed + * @return ResponseInterface + * + * @SuppressWarnings(PHPMD.UnusedFormalParameter) + */ + public function aroundExecute(ActionInterface $subject, callable $proceed) + { + return $this->actionFlag->get('', ActionInterface::FLAG_NO_DISPATCH) ? $this->response : $proceed(); + } +} diff --git a/lib/internal/Magento/Framework/App/Action/Plugin/Design.php b/lib/internal/Magento/Framework/App/Action/Plugin/Design.php deleted file mode 100644 index 2893f1209dc98..0000000000000 --- a/lib/internal/Magento/Framework/App/Action/Plugin/Design.php +++ /dev/null @@ -1,59 +0,0 @@ -<?php -/** - * Copyright © Magento, Inc. All rights reserved. - * See COPYING.txt for license details. - */ -namespace Magento\Framework\App\Action\Plugin; - -use Magento\Framework\Message\MessageInterface; - -class Design -{ - /** - * @var \Magento\Framework\View\DesignLoader - */ - protected $_designLoader; - - /** - * @var \Magento\Framework\Message\ManagerInterface - */ - protected $messageManager; - - /** - * @param \Magento\Framework\View\DesignLoader $designLoader - * @param \Magento\Framework\Message\ManagerInterface $messageManager - */ - public function __construct( - \Magento\Framework\View\DesignLoader $designLoader, - \Magento\Framework\Message\ManagerInterface $messageManager - ) { - $this->_designLoader = $designLoader; - $this->messageManager = $messageManager; - } - - /** - * Initialize design - * - * @param \Magento\Framework\App\ActionInterface $subject - * @param \Magento\Framework\App\RequestInterface $request - * - * @return void - * @SuppressWarnings(PHPMD.UnusedFormalParameter) - */ - public function beforeDispatch( - \Magento\Framework\App\ActionInterface $subject, - \Magento\Framework\App\RequestInterface $request - ) { - try { - $this->_designLoader->load(); - } catch (\Magento\Framework\Exception\LocalizedException $e) { - if ($e->getPrevious() instanceof \Magento\Framework\Config\Dom\ValidationException) { - /** @var MessageInterface $message */ - $message = $this->messageManager - ->createMessage(MessageInterface::TYPE_ERROR) - ->setText($e->getMessage()); - $this->messageManager->addUniqueMessages([$message]); - } - } - } -} diff --git a/lib/internal/Magento/Framework/App/Action/Plugin/EventDispatchPlugin.php b/lib/internal/Magento/Framework/App/Action/Plugin/EventDispatchPlugin.php new file mode 100644 index 0000000000000..7d07d1f4cf457 --- /dev/null +++ b/lib/internal/Magento/Framework/App/Action/Plugin/EventDispatchPlugin.php @@ -0,0 +1,138 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ + +declare(strict_types=1); + +namespace Magento\Framework\App\Action\Plugin; + +use Magento\Framework\App\Action\Action; +use Magento\Framework\App\ActionFlag; +use Magento\Framework\App\ActionInterface; +use Magento\Framework\App\Request\Http; +use Magento\Framework\App\RequestInterface; +use Magento\Framework\Controller\ResultInterface; +use Magento\Framework\Event\ManagerInterface; +use Magento\Framework\HTTP\PhpEnvironment\Response; +use Magento\Framework\Profiler; + +/** + * Dispatch the controller_action_predispatch and controller_action_post_dispatch events. + */ +class EventDispatchPlugin +{ + /** + * @var Http|RequestInterface + */ + private $request; + + /** + * @var ManagerInterface + */ + private $eventManager; + + /** + * @var ActionFlag + */ + private $actionFlag; + + /** + * @param RequestInterface $request + * @param ManagerInterface $eventManager + * @param ActionFlag $actionFlag + */ + public function __construct(RequestInterface $request, ManagerInterface $eventManager, ActionFlag $actionFlag) + { + $this->request = $request; + $this->eventManager = $eventManager; + $this->actionFlag = $actionFlag; + } + + /** + * Trigger the controller_action_predispatch events + * + * @param ActionInterface $subject + */ + public function beforeExecute(ActionInterface $subject) + { + $this->dispatchPreDispatchEvents($subject); + } + + /** + * Build the event parameter array + * + * @param ActionInterface $subject + * @return array + */ + private function getEventParameters(ActionInterface $subject): array + { + return ['controller_action' => $subject, 'request' => $this->request]; + } + + /** + * Trigger the controller_action_postdispatch events if the suppressing action flag is not set + * + * @param ActionInterface $subject + * @param ResultInterface|Response|null $result + * @return ResultInterface|Response|null + */ + public function afterExecute(ActionInterface $subject, $result) + { + if (!$this->isSetActionNoPostDispatchFlag()) { + $this->dispatchPostDispatchEvents($subject); + } + + return $result; + } + + /** + * Check if action flags are set that would suppress the post dispatch events. + * + * @return bool + */ + private function isSetActionNoPostDispatchFlag(): bool + { + return $this->actionFlag->get('', Action::FLAG_NO_DISPATCH) || + $this->actionFlag->get('', Action::FLAG_NO_POST_DISPATCH); + } + + /** + * Dispatch the controller_action_predispatch events. + * + * @param ActionInterface $action + */ + private function dispatchPreDispatchEvents(ActionInterface $action) + { + $this->eventManager->dispatch('controller_action_predispatch', $this->getEventParameters($action)); + $this->eventManager->dispatch( + 'controller_action_predispatch_' . $this->request->getRouteName(), + $this->getEventParameters($action) + ); + $this->eventManager->dispatch( + 'controller_action_predispatch_' . $this->request->getFullActionName(), + $this->getEventParameters($action) + ); + } + + /** + * Dispatch the controller_action_postdispatch events. + * + * @param ActionInterface $action + */ + private function dispatchPostDispatchEvents(ActionInterface $action) + { + Profiler::start('postdispatch'); + $this->eventManager->dispatch( + 'controller_action_postdispatch_' . $this->request->getFullActionName(), + $this->getEventParameters($action) + ); + $this->eventManager->dispatch( + 'controller_action_postdispatch_' . $this->request->getRouteName(), + $this->getEventParameters($action) + ); + $this->eventManager->dispatch('controller_action_postdispatch', $this->getEventParameters($action)); + Profiler::stop('postdispatch'); + } +} diff --git a/lib/internal/Magento/Framework/App/Action/Plugin/LoadDesignPlugin.php b/lib/internal/Magento/Framework/App/Action/Plugin/LoadDesignPlugin.php new file mode 100644 index 0000000000000..2cda49c43c2ce --- /dev/null +++ b/lib/internal/Magento/Framework/App/Action/Plugin/LoadDesignPlugin.php @@ -0,0 +1,64 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ + +namespace Magento\Framework\App\Action\Plugin; + +use Magento\Framework\App\ActionInterface; +use Magento\Framework\Config\Dom\ValidationException; +use Magento\Framework\Exception\LocalizedException; +use Magento\Framework\Message\ManagerInterface as MessageManagerInterface; +use Magento\Framework\Message\MessageInterface; +use Magento\Framework\View\DesignLoader; + +/** + * Handling Exceptions on Design Loading + */ +class LoadDesignPlugin +{ + /** + * @var DesignLoader + */ + protected $_designLoader; + + /** + * @var MessageManagerInterface + */ + protected $messageManager; + + /** + * @param DesignLoader $designLoader + * @param MessageManagerInterface $messageManager + */ + public function __construct( + DesignLoader $designLoader, + MessageManagerInterface $messageManager + ) { + $this->_designLoader = $designLoader; + $this->messageManager = $messageManager; + } + + /** + * Initialize design + * + * @param ActionInterface $subject + * @return void + * @SuppressWarnings(PHPMD.UnusedFormalParameter) + */ + public function beforeExecute(ActionInterface $subject) + { + try { + $this->_designLoader->load(); + } catch (LocalizedException $e) { + if ($e->getPrevious() instanceof ValidationException) { + /** @var MessageInterface $message */ + $message = $this->messageManager + ->createMessage(MessageInterface::TYPE_ERROR) + ->setText($e->getMessage()); + $this->messageManager->addUniqueMessages([$message]); + } + } + } +} diff --git a/lib/internal/Magento/Framework/App/ActionInterface.php b/lib/internal/Magento/Framework/App/ActionInterface.php index 921e1a2e16b1e..1501abe9eb956 100644 --- a/lib/internal/Magento/Framework/App/ActionInterface.php +++ b/lib/internal/Magento/Framework/App/ActionInterface.php @@ -25,8 +25,6 @@ interface ActionInterface /** * Execute action based on request and return result * - * Note: Request will be added as operation argument in future - * * @return \Magento\Framework\Controller\ResultInterface|ResponseInterface * @throws \Magento\Framework\Exception\NotFoundException */ diff --git a/lib/internal/Magento/Framework/App/Bootstrap.php b/lib/internal/Magento/Framework/App/Bootstrap.php index 717b810cffd29..cb86552489472 100644 --- a/lib/internal/Magento/Framework/App/Bootstrap.php +++ b/lib/internal/Magento/Framework/App/Bootstrap.php @@ -225,7 +225,7 @@ public function getParams() * * @param string $type * @param array $arguments - * @return \Magento\Framework\AppInterface + * @return \Magento\Framework\AppInterface | void * @throws \InvalidArgumentException */ public function createApplication($type, $arguments = []) @@ -267,7 +267,7 @@ public function run(AppInterface $application) throw $e; } } - } catch (\Exception $e) { + } catch (\Throwable $e) { $this->terminate($e); } } // phpcs:enable @@ -418,12 +418,12 @@ public function isDeveloperMode() /** * Display an exception and terminate program execution * - * @param \Exception $e + * @param \Throwable $e * @return void * * phpcs:disable Magento2.Security.LanguageConstruct, Squiz.Commenting.FunctionCommentThrowTag */ - protected function terminate(\Exception $e) + protected function terminate(\Throwable $e) { if ($this->isDeveloperMode()) { diff --git a/lib/internal/Magento/Framework/App/Cache.php b/lib/internal/Magento/Framework/App/Cache.php index 34729df9f1e55..7a569e5409f7d 100644 --- a/lib/internal/Magento/Framework/App/Cache.php +++ b/lib/internal/Magento/Framework/App/Cache.php @@ -4,12 +4,11 @@ * See COPYING.txt for license details. */ -/** - * System cache model - * support id and tags prefix support, - */ namespace Magento\Framework\App; +/** + * System cache model support id and tags prefix support. + */ class Cache implements CacheInterface { /** @@ -30,12 +29,13 @@ class Cache implements CacheInterface protected $_frontend; /** - * @param \Magento\Framework\App\Cache\Frontend\Pool $frontendPool + * @param Cache\Frontend\Pool $frontendPool + * @param string|null $cacheIdentifier */ - public function __construct(\Magento\Framework\App\Cache\Frontend\Pool $frontendPool) + public function __construct(\Magento\Framework\App\Cache\Frontend\Pool $frontendPool, $cacheIdentifier = null) { $this->_frontendPool = $frontendPool; - $this->_frontend = $frontendPool->get($this->_frontendIdentifier); + $this->_frontend = $frontendPool->get($cacheIdentifier ?? $this->_frontendIdentifier); } /** diff --git a/lib/internal/Magento/Framework/App/Cache/Frontend/Pool.php b/lib/internal/Magento/Framework/App/Cache/Frontend/Pool.php index a4c9fb4380651..daa7cba20139d 100644 --- a/lib/internal/Magento/Framework/App/Cache/Frontend/Pool.php +++ b/lib/internal/Magento/Framework/App/Cache/Frontend/Pool.php @@ -152,6 +152,15 @@ public function get($identifier) if (isset($this->_instances[$identifier])) { return $this->_instances[$identifier]; } - throw new \InvalidArgumentException("Cache frontend '{$identifier}' is not recognized."); + + if (!isset($this->_instances[self::DEFAULT_FRONTEND_ID])) { + throw new \InvalidArgumentException( + "Cache frontend '{$identifier}' is not recognized. As well as " . + self::DEFAULT_FRONTEND_ID . + "cache is not configured" + ); + } + + return $this->_instances[self::DEFAULT_FRONTEND_ID]; } } diff --git a/lib/internal/Magento/Framework/App/Cache/TypeList.php b/lib/internal/Magento/Framework/App/Cache/TypeList.php index b695ee3a37fa8..c0790c4d40ad4 100644 --- a/lib/internal/Magento/Framework/App/Cache/TypeList.php +++ b/lib/internal/Magento/Framework/App/Cache/TypeList.php @@ -8,6 +8,9 @@ use Magento\Framework\App\ObjectManager; use Magento\Framework\Serialize\SerializerInterface; +/** + * Application cache type list + */ class TypeList implements TypeListInterface { const INVALIDATED_TYPES = 'core_cache_invalidate'; @@ -68,9 +71,7 @@ public function __construct( protected function _getTypeInstance($type) { $config = $this->_config->getType($type); - if (!isset($config['instance'])) { - return null; - } + return $this->_factory->get($config['instance']); } @@ -132,7 +133,7 @@ public function getTypes() } /** - * {@inheritdoc} + * @inheritdoc */ public function getTypeLabels() { diff --git a/lib/internal/Magento/Framework/App/DeploymentConfig.php b/lib/internal/Magento/Framework/App/DeploymentConfig.php index 40b03b068d6ab..ddd7faa80b906 100644 --- a/lib/internal/Magento/Framework/App/DeploymentConfig.php +++ b/lib/internal/Magento/Framework/App/DeploymentConfig.php @@ -7,6 +7,9 @@ namespace Magento\Framework\App; use Magento\Framework\Config\ConfigOptionsListConstants; +use Magento\Framework\Exception\FileSystemException; +use Magento\Framework\Exception\RuntimeException; +use Magento\Framework\Phrase; /** * Application deployment configuration @@ -63,6 +66,8 @@ public function __construct(DeploymentConfig\Reader $reader, $overrideData = []) * @param string $key * @param mixed $defaultValue * @return mixed|null + * @throws FileSystemException + * @throws RuntimeException */ public function get($key = null, $defaultValue = null) { @@ -82,10 +87,11 @@ public function get($key = null, $defaultValue = null) * Checks if data available * * @return bool + * @throws FileSystemException + * @throws RuntimeException */ public function isAvailable() { - $this->data = null; $this->load(); return isset($this->flatData[ConfigOptionsListConstants::CONFIG_PATH_INSTALL_DATE]); } @@ -95,6 +101,8 @@ public function isAvailable() * * @param string $key * @return null|mixed + * @throws FileSystemException + * @throws RuntimeException */ public function getConfigData($key = null) { @@ -104,11 +112,7 @@ public function getConfigData($key = null) return null; } - if (isset($this->data[$key])) { - return $this->data[$key]; - } - - return $this->data; + return $this->data[$key] ?? $this->data; } /** @@ -125,6 +129,8 @@ public function resetData() * Check if data from deploy files is available * * @return bool + * @throws FileSystemException + * @throws RuntimeException * @since 100.1.3 */ public function isDbAvailable() @@ -137,6 +143,8 @@ public function isDbAvailable() * Loads the configuration data * * @return void + * @throws FileSystemException + * @throws RuntimeException */ private function load() { @@ -158,12 +166,15 @@ private function load() * * @param array $params * @param string $path + * @param array $flattenResult * @return array - * @throws \Exception + * @throws RuntimeException */ - private function flattenParams(array $params, $path = null) + private function flattenParams(array $params, $path = null, array &$flattenResult = null) : array { - $cache = []; + if (null === $flattenResult) { + $flattenResult = []; + } foreach ($params as $key => $param) { if ($path) { @@ -171,15 +182,15 @@ private function flattenParams(array $params, $path = null) } else { $newPath = $key; } - if (isset($cache[$newPath])) { - throw new \Exception("Key collision {$newPath} is already defined."); + if (isset($flattenResult[$newPath])) { + throw new RuntimeException(new Phrase("Key collision '%1' is already defined.", [$newPath])); } - $cache[$newPath] = $param; + $flattenResult[$newPath] = $param; if (is_array($param)) { - $cache = array_merge($cache, $this->flattenParams($param, $newPath)); + $this->flattenParams($param, $newPath, $flattenResult); } } - return $cache; + return $flattenResult; } } diff --git a/lib/internal/Magento/Framework/App/DeploymentConfig/Reader.php b/lib/internal/Magento/Framework/App/DeploymentConfig/Reader.php index a53ea9423d449..df9dee7459464 100644 --- a/lib/internal/Magento/Framework/App/DeploymentConfig/Reader.php +++ b/lib/internal/Magento/Framework/App/DeploymentConfig/Reader.php @@ -124,21 +124,4 @@ public function load($fileKey = null) } return $result ?: []; } - - /** - * Loads the configuration file. - * - * @param string $fileKey The file key - * @param string $pathConfig The path config - * @param bool $ignoreInitialConfigFiles Whether ignore custom pools - * @return array - * @throws FileSystemException - * @throws RuntimeException - * @deprecated 100.2.0 Magento does not support custom config file pools since 2.2.0 version - * @SuppressWarnings(PHPMD.UnusedFormalParameter) - */ - public function loadConfigFile($fileKey, $pathConfig, $ignoreInitialConfigFiles = false) - { - return $this->load($fileKey); - } } diff --git a/lib/internal/Magento/Framework/App/Feed.php b/lib/internal/Magento/Framework/App/Feed.php index 1154749a75b94..d6a38a90bdf17 100644 --- a/lib/internal/Magento/Framework/App/Feed.php +++ b/lib/internal/Magento/Framework/App/Feed.php @@ -7,7 +7,7 @@ namespace Magento\Framework\App; -use Zend\Feed\Writer\FeedFactory; +use Laminas\Feed\Writer\FeedFactory; /** * Default XML feed class @@ -29,7 +29,7 @@ public function __construct(array $data) } /** - * {@inheritdoc} + * @inheritDoc */ public function getFormattedContent() : string { diff --git a/lib/internal/Magento/Framework/App/FrontController.php b/lib/internal/Magento/Framework/App/FrontController.php index 2e6ce65970b97..d72c548be4fba 100644 --- a/lib/internal/Magento/Framework/App/FrontController.php +++ b/lib/internal/Magento/Framework/App/FrontController.php @@ -5,14 +5,15 @@ */ namespace Magento\Framework\App; +use Magento\Framework\App\Action\AbstractAction; +use Magento\Framework\App\Request\Http as HttpRequest; use Magento\Framework\App\Request\InvalidRequestException; -use Magento\Framework\Controller\ResultInterface; use Magento\Framework\App\Request\ValidatorInterface as RequestValidator; +use Magento\Framework\Controller\ResultInterface; +use Magento\Framework\Exception\LocalizedException; use Magento\Framework\Exception\NotFoundException; use Magento\Framework\Message\ManagerInterface as MessageManager; -use Magento\Framework\App\Action\AbstractAction; use Psr\Log\LoggerInterface; -use Magento\Framework\App\Request\Http as HttpRequest; /** * Front controller responsible for dispatching application requests @@ -51,19 +52,33 @@ class FrontController implements FrontControllerInterface */ private $validatedRequest = false; + /** + * @var State + */ + private $appState; + + /** + * @var AreaList + */ + private $areaList; + /** * @param RouterListInterface $routerList * @param ResponseInterface $response * @param RequestValidator|null $requestValidator * @param MessageManager|null $messageManager * @param LoggerInterface|null $logger + * @param State $appState + * @param AreaList $areaList */ public function __construct( RouterListInterface $routerList, ResponseInterface $response, ?RequestValidator $requestValidator = null, ?MessageManager $messageManager = null, - ?LoggerInterface $logger = null + ?LoggerInterface $logger = null, + ?State $appState = null, + ?AreaList $areaList = null ) { $this->_routerList = $routerList; $this->response = $response; @@ -73,6 +88,10 @@ public function __construct( ?? ObjectManager::getInstance()->get(MessageManager::class); $this->logger = $logger ?? ObjectManager::getInstance()->get(LoggerInterface::class); + $this->appState = $appState + ?? ObjectManager::getInstance()->get(State::class); + $this->areaList = $areaList + ?? ObjectManager::getInstance()->get(AreaList::class); } /** @@ -81,6 +100,7 @@ public function __construct( * @param RequestInterface|HttpRequest $request * @return ResponseInterface|ResultInterface * @throws \LogicException + * @throws LocalizedException */ public function dispatch(RequestInterface $request) { @@ -120,9 +140,10 @@ public function dispatch(RequestInterface $request) * * @param HttpRequest $request * @param ActionInterface $actionInstance - * @throws NotFoundException - * * @return ResponseInterface|ResultInterface + * @throws LocalizedException + * + * @throws NotFoundException */ private function processRequest( HttpRequest $request, @@ -147,6 +168,9 @@ private function processRequest( ["exception" => $exception] ); $result = $exception->getReplaceResult(); + $area = $this->areaList->getArea($this->appState->getAreaCode()); + $area->load(Area::PART_DESIGN); + $area->load(Area::PART_TRANSLATE); if ($messages = $exception->getMessages()) { foreach ($messages as $message) { $this->messages->addErrorMessage($message); diff --git a/lib/internal/Magento/Framework/App/Language/Dictionary.php b/lib/internal/Magento/Framework/App/Language/Dictionary.php index d9a5ccb00d892..b4a6fdc1b5ce3 100644 --- a/lib/internal/Magento/Framework/App/Language/Dictionary.php +++ b/lib/internal/Magento/Framework/App/Language/Dictionary.php @@ -103,7 +103,9 @@ public function getDictionary($languageCode) foreach ($languages as $languageConfig) { $this->collectInheritedPacks($languageConfig, $packs); } - uasort($packs, [$this, 'sortInherited']); + + // Get sorted packs + $packs = $this->getSortedPacks($packs); // Merge all packages of translation to one dictionary $result = []; @@ -118,6 +120,37 @@ public function getDictionary($languageCode) return $result; } + /** + * Get sorted packs + * + * First level packs (inheritance_level eq 0) sort by 'sort order' (ascending) + * Inherited packs has the same order as declared in parent config (language.xml) + * + * @param array $allPacks + * + * @return array + */ + private function getSortedPacks($allPacks) + { + // Get first level (inheritance_level) packs and sort by provided sort order (descending) + $firstLevelPacks = array_filter( + $allPacks, + function ($pack) { + return $pack['inheritance_level'] === 0; + } + ); + uasort($firstLevelPacks, [$this, 'sortPacks']); + + // Add inherited packs + $sortedPacks = []; + foreach ($firstLevelPacks as $pack) { + $this->addInheritedPacks($allPacks, $pack, $sortedPacks); + } + + // Reverse array: the first element has the lowest priority, the last one - the highest + return array_reverse($sortedPacks, true); + } + /** * Line up (flatten) a tree of inheritance of language packs * @@ -152,28 +185,46 @@ private function collectInheritedPacks($languageConfig, &$result, $level = 0, ar } /** - * Sub-routine for custom sorting packs using inheritance level and sort order + * Add inherited packs to sorted packs * - * First sort by inheritance level descending, then by sort order ascending + * @param array $packs + * @param array $pack + * @param array $sortedPacks + * + * @return void + */ + private function addInheritedPacks($packs, $pack, &$sortedPacks) + { + if (isset($sortedPacks[$pack['key']])) { + return; + } + + $sortedPacks[$pack['key']] = $pack; + foreach ($pack['language']->getUses() as $reuse) { + $packKey = implode('|', [$reuse['vendor'], $reuse['package']]); + if (isset($packs[$packKey])) { + $this->addInheritedPacks($packs, $packs[$packKey], $sortedPacks); + } + } + } + + /** + * Sub-routine for custom sorting packs using sort order (descending) * * @param array $current * @param array $next + * * @return int * @SuppressWarnings(PHPMD.UnusedPrivateMethod) */ - private function sortInherited($current, $next) + private function sortPacks($current, $next) { - if ($current['inheritance_level'] > $next['inheritance_level']) { - return -1; - } elseif ($current['inheritance_level'] < $next['inheritance_level']) { - return 1; - } if ($current['sort_order'] > $next['sort_order']) { - return 1; - } elseif ($current['sort_order'] < $next['sort_order']) { return -1; + } elseif ($current['sort_order'] < $next['sort_order']) { + return 1; } - return strcmp($current['key'], $next['key']); + return strcmp($next['key'], $current['key']); } /** diff --git a/lib/internal/Magento/Framework/App/ObjectManager/ConfigLoader/Compiled.php b/lib/internal/Magento/Framework/App/ObjectManager/ConfigLoader/Compiled.php index 01e5031461f85..50769e9e17774 100644 --- a/lib/internal/Magento/Framework/App/ObjectManager/ConfigLoader/Compiled.php +++ b/lib/internal/Magento/Framework/App/ObjectManager/ConfigLoader/Compiled.php @@ -1,6 +1,5 @@ <?php /** - * * Copyright © Magento, Inc. All rights reserved. * See COPYING.txt for license details. */ @@ -8,9 +7,10 @@ use Magento\Framework\App\Filesystem\DirectoryList; use Magento\Framework\ObjectManager\ConfigLoaderInterface; -use Magento\Framework\Serialize\SerializerInterface; -use Magento\Framework\Serialize\Serializer\Serialize; +/** + * Load configuration files + */ class Compiled implements ConfigLoaderInterface { /** @@ -21,7 +21,7 @@ class Compiled implements ConfigLoaderInterface private $configCache = []; /** - * {inheritdoc} + * @inheritdoc */ public function load($area) { diff --git a/lib/internal/Magento/Framework/App/Request/Http.php b/lib/internal/Magento/Framework/App/Request/Http.php index 4e709ed276954..5fc4716f4bbf8 100644 --- a/lib/internal/Magento/Framework/App/Request/Http.php +++ b/lib/internal/Magento/Framework/App/Request/Http.php @@ -8,7 +8,7 @@ use Magento\Framework\App\HttpRequestInterface; use Magento\Framework\App\RequestContentInterface; use Magento\Framework\App\RequestSafetyInterface; -use Magento\Framework\App\Route\ConfigInterface\Proxy as ConfigInterface; +use Magento\Framework\App\Route\ConfigInterface; use Magento\Framework\HTTP\PhpEnvironment\Request; use Magento\Framework\ObjectManagerInterface; use Magento\Framework\Stdlib\Cookie\CookieReaderInterface; @@ -16,6 +16,7 @@ /** * Http request + * @SuppressWarnings(PHPMD.CookieAndSessionMisuse) */ class Http extends Request implements RequestContentInterface, RequestSafetyInterface, HttpRequestInterface { @@ -106,7 +107,7 @@ class Http extends Request implements RequestContentInterface, RequestSafetyInte * @param ConfigInterface $routeConfig * @param PathInfoProcessorInterface $pathInfoProcessor * @param ObjectManagerInterface $objectManager - * @param \Zend\Uri\UriInterface|string|null $uri + * @param \Laminas\Uri\UriInterface|string|null $uri * @param array $directFrontNames * @param PathInfo|null $pathInfoService */ diff --git a/lib/internal/Magento/Framework/App/Request/HttpMethodMap.php b/lib/internal/Magento/Framework/App/Request/HttpMethodMap.php index 4e7216954b0d4..ddc3a03416c68 100644 --- a/lib/internal/Magento/Framework/App/Request/HttpMethodMap.php +++ b/lib/internal/Magento/Framework/App/Request/HttpMethodMap.php @@ -59,7 +59,7 @@ private function processMap(array $map): array * * @return string[] * - * @see \Zend\Http\Request Has list of methods as METHOD_* constants. + * @see \Laminas\Http\Request Has list of methods as METHOD_* constants. */ public function getMap(): array { diff --git a/lib/internal/Magento/Framework/App/Response/HttpInterface.php b/lib/internal/Magento/Framework/App/Response/HttpInterface.php index 08b1257f73abe..17825aeb88d65 100644 --- a/lib/internal/Magento/Framework/App/Response/HttpInterface.php +++ b/lib/internal/Magento/Framework/App/Response/HttpInterface.php @@ -48,7 +48,7 @@ public function setHeader($name, $value, $replace = false); * If header with specified name was not found returns false. * * @param string $name - * @return \Zend\Http\Header\HeaderInterface|bool + * @return \Laminas\Http\Header\HeaderInterface|bool * @since 100.2.0 */ public function getHeader($name); diff --git a/lib/internal/Magento/Framework/App/Router/ActionList.php b/lib/internal/Magento/Framework/App/Router/ActionList.php index 1640d4a98d354..63a24d58a3c81 100644 --- a/lib/internal/Magento/Framework/App/Router/ActionList.php +++ b/lib/internal/Magento/Framework/App/Router/ActionList.php @@ -5,6 +5,8 @@ */ namespace Magento\Framework\App\Router; +use Magento\Framework\App\Filesystem\DirectoryList; +use Magento\Framework\App\State; use Magento\Framework\Serialize\SerializerInterface; use Magento\Framework\Serialize\Serializer\Serialize; use Magento\Framework\Module\Dir\Reader as ModuleReader; @@ -70,12 +72,26 @@ public function __construct( $this->reservedWords = array_merge($reservedWords, $this->reservedWords); $this->actionInterface = $actionInterface; $this->serializer = $serializer ?: \Magento\Framework\App\ObjectManager::getInstance()->get(Serialize::class); - $data = $cache->load($cacheKey); - if (!$data) { - $this->actions = $moduleReader->getActionFiles(); - $cache->save($this->serializer->serialize($this->actions), $cacheKey); + $objectManager = \Magento\Framework\App\ObjectManager::getInstance(); + $state = $objectManager->get(State::class); + + if ($state->getMode() === State::MODE_PRODUCTION) { + $directoryList = $objectManager->get(DirectoryList::class); + $file = $directoryList->getPath(DirectoryList::GENERATED_METADATA) . '/' . $cacheKey . '.' . 'php'; + + if (file_exists($file)) { + $this->actions = (include $file) ?? $moduleReader->getActionFiles(); + } else { + $this->actions = $moduleReader->getActionFiles(); + } } else { - $this->actions = $this->serializer->unserialize($data); + $data = $cache->load($cacheKey); + if (!$data) { + $this->actions = $moduleReader->getActionFiles(); + $cache->save($this->serializer->serialize($this->actions), $cacheKey); + } else { + $this->actions = $this->serializer->unserialize($data); + } } } diff --git a/lib/internal/Magento/Framework/App/Router/Base.php b/lib/internal/Magento/Framework/App/Router/Base.php index 9c0d1633e8bba..a0a3f6f8fd4aa 100644 --- a/lib/internal/Magento/Framework/App/Router/Base.php +++ b/lib/internal/Magento/Framework/App/Router/Base.php @@ -374,6 +374,6 @@ protected function _checkShouldBeSecure(\Magento\Framework\App\RequestInterface */ protected function _shouldRedirectToSecure() { - return $this->_url->getUseSession(); + return false; } } diff --git a/lib/internal/Magento/Framework/App/Test/Unit/Action/ActionTest.php b/lib/internal/Magento/Framework/App/Test/Unit/Action/ActionTest.php index ebd72f5badccf..c2d30187845a4 100644 --- a/lib/internal/Magento/Framework/App/Test/Unit/Action/ActionTest.php +++ b/lib/internal/Magento/Framework/App/Test/Unit/Action/ActionTest.php @@ -8,95 +8,87 @@ use \Magento\Framework\App\Action\Action; +use Magento\Framework\App\ActionFlag; +use Magento\Framework\App\Request\Http as HttpRequest; +use Magento\Framework\App\Response\RedirectInterface; +use Magento\Framework\App\ResponseInterface; +use Magento\Framework\App\ViewInterface; +use Magento\Framework\Event\ManagerInterface; use Magento\Framework\TestFramework\Unit\Helper\ObjectManager as ObjectManagerHelper; +use Magento\Framework\View\Page\Config as PageConfig; +use PHPUnit\Framework\MockObject\MockObject; class ActionTest extends \PHPUnit\Framework\TestCase { - /** @var \Magento\Framework\App\Test\Unit\Action\ActionFake */ + /** + * @var ActionFake + */ protected $action; - /** @var ObjectManagerHelper */ + /** + * @var ObjectManagerHelper + */ protected $objectManagerHelper; /** - * @var \Magento\Framework\App\Request\Http|\PHPUnit_Framework_MockObject_MockObject + * @var HttpRequest|MockObject */ protected $_requestMock; /** - * @var \Magento\Framework\App\ResponseInterface|\PHPUnit_Framework_MockObject_MockObject + * @var ResponseInterface|MockObject */ protected $_responseMock; /** - * @var \Magento\Framework\Event\ManagerInterface|\PHPUnit_Framework_MockObject_MockObject + * @var ManagerInterface|MockObject */ protected $_eventManagerMock; /** - * @var \Magento\Framework\App\ActionFlag|\PHPUnit_Framework_MockObject_MockObject + * @var ActionFlag|MockObject */ protected $_actionFlagMock; /** - * @var \Magento\Framework\App\Response\RedirectInterface|\PHPUnit_Framework_MockObject_MockObject + * @var RedirectInterface|MockObject */ protected $_redirectMock; /** - * @var \Magento\Framework\App\ViewInterface|\PHPUnit_Framework_MockObject_MockObject + * @var ViewInterface|MockObject */ protected $viewMock; /** - * @var \Magento\Framework\View\Page\Config|\PHPUnit_Framework_MockObject_MockObject + * @var PageConfig|MockObject */ protected $pageConfigMock; - /** - * Full action name - */ - const FULL_ACTION_NAME = 'module/controller/someaction'; - - /** - * Route name - */ - const ROUTE_NAME = 'module/controller/actionroute'; - - /** - * Action name - */ - const ACTION_NAME = 'someaction'; - - /** - * Controller name - */ - const CONTROLLER_NAME = 'controller'; - - /** - * Module name - */ - const MODULE_NAME = 'module'; + public const FULL_ACTION_NAME = 'module/controller/someaction'; + public const ROUTE_NAME = 'module/controller/actionroute'; + public const ACTION_NAME = 'someaction'; + public const CONTROLLER_NAME = 'controller'; + public const MODULE_NAME = 'module'; public static $actionParams = ['param' => 'value']; protected function setUp() { - $this->_eventManagerMock = $this->createMock(\Magento\Framework\Event\ManagerInterface::class); - $this->_actionFlagMock = $this->createMock(\Magento\Framework\App\ActionFlag::class); - $this->_redirectMock = $this->createMock(\Magento\Framework\App\Response\RedirectInterface::class); - $this->_requestMock = $this->getMockBuilder(\Magento\Framework\App\Request\Http::class) - ->disableOriginalConstructor()->getMock(); - $this->_responseMock = $this->createMock(\Magento\Framework\App\ResponseInterface::class); - - $this->pageConfigMock = $this->createPartialMock(\Magento\Framework\View\Page\Config::class, ['getConfig']); - $this->viewMock = $this->createMock(\Magento\Framework\App\ViewInterface::class); + $this->_eventManagerMock = $this->createMock(ManagerInterface::class); + $this->_actionFlagMock = $this->createMock(ActionFlag::class); + $this->_redirectMock = $this->createMock(RedirectInterface::class); + $this->_requestMock = $this->createMock(HttpRequest::class); + $this->_responseMock = $this->createMock(ResponseInterface::class); + + $this->pageConfigMock = $this->createPartialMock(PageConfig::class, ['getConfig']); + $this->viewMock = $this->createMock(ViewInterface::class); $this->viewMock->expects($this->any())->method('getPage')->will($this->returnValue($this->pageConfigMock)); - $this->pageConfigMock->expects($this->any())->method('getConfig')->will($this->returnValue(1)); + $this->pageConfigMock->expects($this->any())->method('getConfig')->willReturn(1); $this->objectManagerHelper = new ObjectManagerHelper($this); $this->action = $this->objectManagerHelper->getObject( - \Magento\Framework\App\Test\Unit\Action\ActionFake::class, + ActionFake::class, [ 'request' => $this->_requestMock, 'response' => $this->_responseMock, @@ -111,29 +103,12 @@ protected function setUp() public function testDispatchPostDispatch() { - $this->_requestMock->expects($this->exactly(3))->method('getFullActionName')->will( - $this->returnValue(self::FULL_ACTION_NAME) - ); - $this->_requestMock->expects($this->exactly(2))->method('getRouteName')->will( - $this->returnValue(self::ROUTE_NAME) - ); - $expectedEventParameters = ['controller_action' => $this->action, 'request' => $this->_requestMock]; - $this->_eventManagerMock->expects($this->at(0))->method('dispatch')->with( - 'controller_action_predispatch', - $expectedEventParameters - ); - $this->_eventManagerMock->expects($this->at(1))->method('dispatch')->with( - 'controller_action_predispatch_' . self::ROUTE_NAME, - $expectedEventParameters - ); - $this->_eventManagerMock->expects($this->at(2))->method('dispatch')->with( - 'controller_action_predispatch_' . self::FULL_ACTION_NAME, - $expectedEventParameters - ); - - $this->_requestMock->expects($this->once())->method('isDispatched')->will($this->returnValue(true)); - $this->_actionFlagMock->expects($this->at(0))->method('get')->with('', Action::FLAG_NO_DISPATCH)->will( - $this->returnValue(false) + $this->_requestMock->method('getFullActionName')->willReturn(self::FULL_ACTION_NAME); + $this->_requestMock->method('getRouteName')->willReturn(self::ROUTE_NAME); + $this->_requestMock->method('isDispatched')->willReturn(true); + $this->_actionFlagMock->method('get')->willReturnMap( + ['', Action::FLAG_NO_DISPATCH, false], + ['', Action::FLAG_NO_POST_DISPATCH] ); // _forward expectations @@ -151,23 +126,6 @@ public function testDispatchPostDispatch() self::$actionParams ); - $this->_actionFlagMock->expects($this->at(1))->method('get')->with('', Action::FLAG_NO_POST_DISPATCH)->will( - $this->returnValue(false) - ); - - $this->_eventManagerMock->expects($this->at(3))->method('dispatch')->with( - 'controller_action_postdispatch_' . self::FULL_ACTION_NAME, - $expectedEventParameters - ); - $this->_eventManagerMock->expects($this->at(4))->method('dispatch')->with( - 'controller_action_postdispatch_' . self::ROUTE_NAME, - $expectedEventParameters - ); - $this->_eventManagerMock->expects($this->at(5))->method('dispatch')->with( - 'controller_action_postdispatch', - $expectedEventParameters - ); - $this->assertEquals($this->_responseMock, $this->action->dispatch($this->_requestMock)); } } diff --git a/lib/internal/Magento/Framework/App/Test/Unit/Action/Plugin/DesignTest.php b/lib/internal/Magento/Framework/App/Test/Unit/Action/Plugin/DesignTest.php deleted file mode 100644 index d5c7f4d0355ca..0000000000000 --- a/lib/internal/Magento/Framework/App/Test/Unit/Action/Plugin/DesignTest.php +++ /dev/null @@ -1,20 +0,0 @@ -<?php -/** - * Copyright © Magento, Inc. All rights reserved. - * See COPYING.txt for license details. - */ -namespace Magento\Framework\App\Test\Unit\Action\Plugin; - -class DesignTest extends \PHPUnit\Framework\TestCase -{ - public function testAroundDispatch() - { - $subjectMock = $this->createMock(\Magento\Framework\App\Action\Action::class); - $designLoaderMock = $this->createMock(\Magento\Framework\View\DesignLoader::class); - $messageManagerMock = $this->createMock(\Magento\Framework\Message\ManagerInterface::class); - $requestMock = $this->createMock(\Magento\Framework\App\RequestInterface::class); - $plugin = new \Magento\Framework\App\Action\Plugin\Design($designLoaderMock, $messageManagerMock); - $designLoaderMock->expects($this->once())->method('load'); - $plugin->beforeDispatch($subjectMock, $requestMock); - } -} diff --git a/lib/internal/Magento/Framework/App/Test/Unit/Action/Plugin/LoadDesignPluginTest.php b/lib/internal/Magento/Framework/App/Test/Unit/Action/Plugin/LoadDesignPluginTest.php new file mode 100644 index 0000000000000..90acfde426931 --- /dev/null +++ b/lib/internal/Magento/Framework/App/Test/Unit/Action/Plugin/LoadDesignPluginTest.php @@ -0,0 +1,34 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +namespace Magento\Framework\App\Test\Unit\Action\Plugin; + +use Magento\Framework\App\Action\Action; +use Magento\Framework\App\Action\Plugin\LoadDesignPlugin; +use Magento\Framework\App\ActionInterface; +use Magento\Framework\Message\ManagerInterface; +use Magento\Framework\View\DesignLoader; +use PHPUnit\Framework\MockObject\MockObject; +use PHPUnit\Framework\TestCase; + +class LoadDesignPluginTest extends TestCase +{ + public function testBeforeExecute() + { + /** @var MockObject|ActionInterface $actionMock */ + $actionMock = $this->createMock(Action::class); + + /** @var MockObject|DesignLoader $designLoaderMock */ + $designLoaderMock = $this->createMock(DesignLoader::class); + + /** @var MockObject|ManagerInterface $messageManagerMock */ + $messageManagerMock = $this->createMock(ManagerInterface::class); + + $plugin = new LoadDesignPlugin($designLoaderMock, $messageManagerMock); + + $designLoaderMock->expects($this->once())->method('load'); + $plugin->beforeExecute($actionMock); + } +} diff --git a/lib/internal/Magento/Framework/App/Test/Unit/Cache/Frontend/PoolTest.php b/lib/internal/Magento/Framework/App/Test/Unit/Cache/Frontend/PoolTest.php index 5ec3dd658737b..f6398f08ebf27 100644 --- a/lib/internal/Magento/Framework/App/Test/Unit/Cache/Frontend/PoolTest.php +++ b/lib/internal/Magento/Framework/App/Test/Unit/Cache/Frontend/PoolTest.php @@ -208,12 +208,8 @@ public function testGet() } } - /** - * @expectedException \InvalidArgumentException - * @expectedExceptionMessage Cache frontend 'unknown' is not recognized - */ - public function testGetUnknownFrontendId() + public function testFallbackOnDefault() { - $this->_model->get('unknown'); + $this->assertSame($this->_frontendInstances[Pool::DEFAULT_FRONTEND_ID], $this->_model->get('unknown')); } } diff --git a/lib/internal/Magento/Framework/App/Test/Unit/Cache/TypeListTest.php b/lib/internal/Magento/Framework/App/Test/Unit/Cache/TypeListTest.php index 8d9b297d7dded..02c9a872fe97f 100644 --- a/lib/internal/Magento/Framework/App/Test/Unit/Cache/TypeListTest.php +++ b/lib/internal/Magento/Framework/App/Test/Unit/Cache/TypeListTest.php @@ -6,21 +6,30 @@ namespace Magento\Framework\App\Test\Unit\Cache; -use \Magento\Framework\App\Cache\TypeList; +use Magento\Framework\App\Cache\InstanceFactory; +use Magento\Framework\App\Cache\StateInterface; +use Magento\Framework\App\Cache\TypeList; +use Magento\Framework\App\CacheInterface; +use Magento\Framework\Cache\Frontend\Decorator\TagScope; +use Magento\Framework\Cache\ConfigInterface; +use Magento\Framework\DataObject; use Magento\Framework\Serialize\SerializerInterface; +use Magento\Framework\TestFramework\Unit\Helper\ObjectManager; +use PHPUnit\Framework\TestCase; +use PHPUnit\Framework\MockObject\MockObject; /** * Test class for \Magento\Framework\App\Cache\TypeList */ -class TypeListTest extends \PHPUnit\Framework\TestCase +class TypeListTest extends TestCase { /** - * @var \Magento\Framework\App\Cache\TypeList + * @var TypeList */ protected $_typeList; /** - * @var \Magento\Framework\App\CacheInterface|\PHPUnit_Framework_MockObject_MockObject + * @var CacheInterface|MockObject */ protected $_cache; @@ -30,7 +39,7 @@ class TypeListTest extends \PHPUnit\Framework\TestCase protected $_typesArray; /** - * @var \Magento\Framework\Cache\ConfigInterface|\PHPUnit_Framework_MockObject_MockObject + * @var ConfigInterface|MockObject */ protected $_config; @@ -47,10 +56,10 @@ class TypeListTest extends \PHPUnit\Framework\TestCase /** * Expected cache type */ - const CACHE_TYPE = \Magento\Framework\Cache\FrontendInterface::class; + const CACHE_TYPE = TagScope::class; /** - * @var SerializerInterface|\PHPUnit_Framework_MockObject_MockObject + * @var SerializerInterface|MockObject */ private $serializerMock; @@ -58,33 +67,44 @@ protected function setUp() { $this->_typesArray = [ self::TYPE_KEY => [ + 'name' => self::TYPE_KEY, + 'instance' => self::CACHE_TYPE, 'label' => 'Type Label', 'description' => 'Type Description', ], ]; - $this->_config = - $this->createPartialMock(\Magento\Framework\Cache\ConfigInterface::class, ['getTypes', 'getType']); - $this->_config->expects($this->any())->method('getTypes')->will($this->returnValue($this->_typesArray)); + $this->_config = $this->createPartialMock( + ConfigInterface::class, + ['getTypes', 'getType'] + ); + $this->_config->expects($this->any())->method('getTypes') + ->will($this->returnValue($this->_typesArray)); + $this->_config->expects($this->any())->method('getType') + ->with(self::TYPE_KEY) + ->will($this->returnValue($this->_typesArray[self::TYPE_KEY])); $cacheState = $this->createPartialMock( - \Magento\Framework\App\Cache\StateInterface::class, + StateInterface::class, ['isEnabled', 'setEnabled', 'persist'] ); - $cacheState->expects($this->any())->method('isEnabled')->will($this->returnValue(self::IS_CACHE_ENABLED)); - $cacheBlockMock = $this->createMock(self::CACHE_TYPE); - $factory = $this->createPartialMock(\Magento\Framework\App\Cache\InstanceFactory::class, ['get']); - $factory->expects($this->any())->method('get')->with(self::CACHE_TYPE)->will( - $this->returnValue($cacheBlockMock) - ); + $cacheState->expects($this->any())->method('isEnabled') + ->will($this->returnValue(self::IS_CACHE_ENABLED)); + $cacheTypeMock = $this->createMock(self::CACHE_TYPE); + $cacheTypeMock->expects($this->any())->method('getTag') + ->will($this->returnValue('TEST')); + $factory = $this->createPartialMock(InstanceFactory::class, ['get']); + $factory->expects($this->any())->method('get') + ->with(self::CACHE_TYPE) + ->will($this->returnValue($cacheTypeMock)); $this->_cache = $this->createPartialMock( - \Magento\Framework\App\CacheInterface::class, + CacheInterface::class, ['load', 'getFrontend', 'save', 'remove', 'clean'] ); $this->serializerMock = $this->createMock(SerializerInterface::class); - $objectHelper = new \Magento\Framework\TestFramework\Unit\Helper\ObjectManager($this); + $objectHelper = new ObjectManager($this); $this->_typeList = $objectHelper->getObject( - \Magento\Framework\App\Cache\TypeList::class, + TypeList::class, [ 'config' => $this->_config, 'cacheState' => $cacheState, @@ -114,9 +134,9 @@ public function testGetTypeLabels() public function testGetInvalidated() { $expectation = [self::TYPE_KEY => $this->_getPreparedType()]; - $this->_cache->expects($this->once())->method('load')->with(TypeList::INVALIDATED_TYPES)->will( - $this->returnValue('serializedData') - ); + $this->_cache->expects($this->once())->method('load') + ->with(TypeList::INVALIDATED_TYPES) + ->will($this->returnValue('serializedData')); $this->serializerMock->expects($this->once()) ->method('unserialize') ->with('serializedData') @@ -127,9 +147,9 @@ public function testGetInvalidated() public function testInvalidate() { // there are no invalidated types - $this->_cache->expects($this->once())->method('load')->with(TypeList::INVALIDATED_TYPES)->will( - $this->returnValue([]) - ); + $this->_cache->expects($this->once())->method('load') + ->with(TypeList::INVALIDATED_TYPES) + ->will($this->returnValue([])); $expectedInvalidated = [ self::TYPE_KEY => 1, ]; @@ -137,18 +157,16 @@ public function testInvalidate() ->method('serialize') ->with($expectedInvalidated) ->willReturn('serializedData'); - $this->_cache->expects($this->once())->method('save')->with( - 'serializedData', - TypeList::INVALIDATED_TYPES - ); + $this->_cache->expects($this->once())->method('save') + ->with('serializedData', TypeList::INVALIDATED_TYPES); $this->_typeList->invalidate(self::TYPE_KEY); } public function testInvalidateList() { - $this->_cache->expects($this->once())->method('load')->with(TypeList::INVALIDATED_TYPES)->will( - $this->returnValue([]) - ); + $this->_cache->expects($this->once())->method('load') + ->with(TypeList::INVALIDATED_TYPES) + ->will($this->returnValue([])); $expectedInvalidated = [ self::TYPE_KEY => 1, ]; @@ -156,10 +174,8 @@ public function testInvalidateList() ->method('serialize') ->with($expectedInvalidated) ->willReturn('serializedData'); - $this->_cache->expects($this->once())->method('save')->with( - 'serializedData', - TypeList::INVALIDATED_TYPES - ); + $this->_cache->expects($this->once())->method('save') + ->with('serializedData', TypeList::INVALIDATED_TYPES); $this->_typeList->invalidate([self::TYPE_KEY]); } @@ -169,38 +185,36 @@ public function testCleanType() ->method('unserialize') ->with('serializedData') ->willReturn($this->_typesArray); - $this->_cache->expects($this->once())->method('load')->with(TypeList::INVALIDATED_TYPES)->will( - $this->returnValue('serializedData') - ); - $this->_config->expects($this->once())->method('getType')->with(self::TYPE_KEY)->will( - $this->returnValue(['instance' => self::CACHE_TYPE]) - ); + $this->_cache->expects($this->once())->method('load') + ->with(TypeList::INVALIDATED_TYPES) + ->will($this->returnValue('serializedData')); + $this->_config->expects($this->once())->method('getType') + ->with(self::TYPE_KEY) + ->will($this->returnValue(['instance' => self::CACHE_TYPE])); unset($this->_typesArray[self::TYPE_KEY]); $this->serializerMock->expects($this->once()) ->method('serialize') ->with($this->_typesArray) ->willReturn('serializedData'); - $this->_cache->expects($this->once())->method('save')->with( - 'serializedData', - TypeList::INVALIDATED_TYPES - ); + $this->_cache->expects($this->once())->method('save') + ->with('serializedData', TypeList::INVALIDATED_TYPES); $this->_typeList->cleanType(self::TYPE_KEY); } /** * Returns prepared type * - * @return \Magento\Framework\DataObject + * @return DataObject */ private function _getPreparedType() { - return new \Magento\Framework\DataObject( + return new DataObject( [ 'id' => self::TYPE_KEY, 'cache_type' => $this->_typesArray[self::TYPE_KEY]['label'], 'description' => $this->_typesArray[self::TYPE_KEY]['description'], - 'tags' => '', - 'status' => self::IS_CACHE_ENABLED, + 'tags' => 'TEST', + 'status' => (int)self::IS_CACHE_ENABLED, ] ); } diff --git a/lib/internal/Magento/Framework/App/Test/Unit/DeploymentConfigTest.php b/lib/internal/Magento/Framework/App/Test/Unit/DeploymentConfigTest.php index 80ab2302dc91c..81a4f842bdf1d 100644 --- a/lib/internal/Magento/Framework/App/Test/Unit/DeploymentConfigTest.php +++ b/lib/internal/Magento/Framework/App/Test/Unit/DeploymentConfigTest.php @@ -14,27 +14,33 @@ class DeploymentConfigTest extends \PHPUnit\Framework\TestCase /** * @var array */ - private static $fixture = [ - 'configData1' => 'scalar_value', - 'configData2' => [ - 'foo' => 1, - 'bar' => ['baz' => 2], - ], - ]; + private static $fixture + = [ + 'configData1' => 'scalar_value', + 'configData2' => [ + 'foo' => 1, + 'bar' => ['baz' => 2], + ], + 'configData3' => null, + 'test_override' => 'original', + ]; /** * @var array */ - private static $flattenedFixture = [ - 'configData1' => 'scalar_value', - 'configData2' => [ - 'foo' => 1, - 'bar' => ['baz' => 2], - ], - 'configData2/foo' => 1, - 'configData2/bar' => ['baz' => 2], - 'configData2/bar/baz' => 2, - ]; + private static $flattenedFixture + = [ + 'configData1' => 'scalar_value', + 'configData2' => [ + 'foo' => 1, + 'bar' => ['baz' => 2], + ], + 'configData2/foo' => 1, + 'configData2/bar' => ['baz' => 2], + 'configData2/bar/baz' => 2, + 'configData3' => null, + 'test_override' => 'overridden', + ]; /** * @var array @@ -63,21 +69,24 @@ class DeploymentConfigTest extends \PHPUnit\Framework\TestCase public static function setUpBeforeClass() { - self::$fixtureConfig = require __DIR__ . '/_files/config.php'; - self::$fixtureConfigMerged = require __DIR__ . '/_files/other/local_developer_merged.php'; + self::$fixtureConfig = require __DIR__.'/_files/config.php'; + self::$fixtureConfigMerged = require __DIR__.'/_files/other/local_developer_merged.php'; } protected function setUp() { - $this->reader = $this->createMock(\Magento\Framework\App\DeploymentConfig\Reader::class); - $this->_deploymentConfig = new \Magento\Framework\App\DeploymentConfig($this->reader, []); + $this->reader = $this->createMock(\Magento\Framework\App\DeploymentConfig\Reader::class); + $this->_deploymentConfig = new \Magento\Framework\App\DeploymentConfig( + $this->reader, + ['test_override' => 'overridden'] + ); $this->_deploymentConfigMerged = new \Magento\Framework\App\DeploymentConfig( $this->reader, - require __DIR__ . '/_files/other/local_developer.php' + require __DIR__.'/_files/other/local_developer.php' ); } - public function testGetters() + public function testGetters(): void { $this->reader->expects($this->once())->method('load')->willReturn(self::$fixture); $this->assertSame(self::$flattenedFixture, $this->_deploymentConfig->get()); @@ -85,33 +94,40 @@ public function testGetters() $this->assertSame(self::$flattenedFixture, $this->_deploymentConfig->get()); $this->assertSame('scalar_value', $this->_deploymentConfig->getConfigData('configData1')); $this->assertSame(self::$fixture['configData2'], $this->_deploymentConfig->getConfigData('configData2')); + $this->assertSame(self::$fixture['configData3'], $this->_deploymentConfig->getConfigData('configData3')); + $this->assertSame('', $this->_deploymentConfig->get('configData3')); + $this->assertSame('defaultValue', $this->_deploymentConfig->get('invalid_key', 'defaultValue')); + $this->assertNull($this->_deploymentConfig->getConfigData('invalid_key')); + $this->assertSame('overridden', $this->_deploymentConfig->get('test_override')); } - public function testIsAvailable() + public function testIsAvailable(): void { - $this->reader->expects($this->once())->method('load')->willReturn([ - ConfigOptionsListConstants::CONFIG_PATH_INSTALL_DATE => 1 - ]); + $this->reader->expects($this->once())->method('load')->willReturn( + [ + ConfigOptionsListConstants::CONFIG_PATH_INSTALL_DATE => 1, + ] + ); $object = new DeploymentConfig($this->reader); $this->assertTrue($object->isAvailable()); } - public function testNotAvailable() + public function testNotAvailable(): void { $this->reader->expects($this->once())->method('load')->willReturn([]); $object = new DeploymentConfig($this->reader); $this->assertFalse($object->isAvailable()); } - public function testNotAvailableThenAvailable() + /** + * test if the configuration changes during the same request, the configuration remain the same + */ + public function testNotAvailableThenAvailable(): void { - $this->reader->expects($this->at(0))->method('load')->willReturn([]); - $this->reader->expects($this->at(1))->method('load')->willReturn([ - ConfigOptionsListConstants::CONFIG_PATH_INSTALL_DATE => 1 - ]); + $this->reader->expects($this->once())->method('load')->willReturn([]); $object = new DeploymentConfig($this->reader); $this->assertFalse($object->isAvailable()); - $this->assertTrue($object->isAvailable()); + $this->assertFalse($object->isAvailable()); } /** @@ -120,7 +136,7 @@ public function testNotAvailableThenAvailable() * @expectedExceptionMessage Key collision * @dataProvider keyCollisionDataProvider */ - public function testKeyCollision(array $data) + public function testKeyCollision(array $data): void { $this->reader->expects($this->once())->method('load')->willReturn($data); $object = new DeploymentConfig($this->reader); @@ -130,14 +146,32 @@ public function testKeyCollision(array $data) /** * @return array */ - public function keyCollisionDataProvider() + public function keyCollisionDataProvider(): array { return [ [ ['foo' => ['bar' => '1'], 'foo/bar' => '2'], ['foo/bar' => '1', 'foo' => ['bar' => '2']], ['foo' => ['subfoo' => ['subbar' => '1'], 'subfoo/subbar' => '2'], 'bar' => '3'], - ] + ], ]; } + + public function testResetData(): void + { + $this->reader->expects($this->exactly(2))->method('load')->willReturn(self::$fixture); + $this->assertSame(self::$flattenedFixture, $this->_deploymentConfig->get()); + $this->_deploymentConfig->resetData(); + // second time to ensure loader will be invoked only once after reset + $this->assertSame(self::$flattenedFixture, $this->_deploymentConfig->get()); + $this->assertSame(self::$flattenedFixture, $this->_deploymentConfig->get()); + } + + public function testIsDbAvailable(): void + { + $this->reader->expects($this->exactly(2))->method('load')->willReturnOnConsecutiveCalls([], ['db' => []]); + $this->assertFalse($this->_deploymentConfig->isDbAvailable()); + $this->_deploymentConfig->resetData(); + $this->assertTrue($this->_deploymentConfig->isDbAvailable()); + } } diff --git a/lib/internal/Magento/Framework/App/Test/Unit/FrontControllerTest.php b/lib/internal/Magento/Framework/App/Test/Unit/FrontControllerTest.php index e088bb92c8782..727e27080569c 100644 --- a/lib/internal/Magento/Framework/App/Test/Unit/FrontControllerTest.php +++ b/lib/internal/Magento/Framework/App/Test/Unit/FrontControllerTest.php @@ -6,8 +6,12 @@ namespace Magento\Framework\App\Test\Unit; +use Magento\Framework\App\Area; +use Magento\Framework\App\AreaInterface; +use Magento\Framework\App\AreaList; use Magento\Framework\App\Request\InvalidRequestException; use Magento\Framework\App\Request\ValidatorInterface; +use Magento\Framework\App\State; use Magento\Framework\Exception\NotFoundException; use Magento\Framework\Message\ManagerInterface as MessageManager; use Psr\Log\LoggerInterface; @@ -57,6 +61,21 @@ class FrontControllerTest extends \PHPUnit\Framework\TestCase */ private $logger; + /** + * @var \PHPUnit\Framework\MockObject\MockObject|AreaList + */ + private $areaListMock; + + /** + * @var \PHPUnit\Framework\MockObject\MockObject|State + */ + private $appStateMock; + + /** + * @var \PHPUnit\Framework\MockObject\MockObject|AreaInterface + */ + private $areaMock; + protected function setUp() { $this->request = $this->getMockBuilder(\Magento\Framework\App\Request\Http::class) @@ -70,12 +89,19 @@ protected function setUp() $this->requestValidator = $this->createMock(ValidatorInterface::class); $this->messages = $this->createMock(MessageManager::class); $this->logger = $this->createMock(LoggerInterface::class); + $this->appStateMock = $this->getMockBuilder(State::class) + ->disableOriginalConstructor() + ->getMock(); + $this->areaListMock = $this->createMock(AreaList::class); + $this->areaMock = $this->createMock(AreaInterface::class); $this->model = new \Magento\Framework\App\FrontController( $this->routerList, $this->response, $this->requestValidator, $this->messages, - $this->logger + $this->logger, + $this->appStateMock, + $this->areaListMock ); } @@ -114,6 +140,10 @@ public function testAddingValidationFailureMessageToDebugLog() $exceptionMessage = 'exception_message'; $exception = new InvalidRequestException($exceptionMessage); + $this->appStateMock->expects($this->any())->method('getAreaCode')->willReturn('frontend'); + $this->areaMock->expects($this->at(0))->method('load')->with(Area::PART_DESIGN)->willReturnSelf(); + $this->areaMock->expects($this->at(1))->method('load')->with(Area::PART_TRANSLATE)->willReturnSelf(); + $this->areaListMock->expects($this->any())->method('getArea')->will($this->returnValue($this->areaMock)); $this->routerList->expects($this->any()) ->method('valid') ->will($this->returnValue(true)); diff --git a/lib/internal/Magento/Framework/App/Test/Unit/PageCache/KernelTest.php b/lib/internal/Magento/Framework/App/Test/Unit/PageCache/KernelTest.php index db200f962f5b5..81df376a5d42f 100644 --- a/lib/internal/Magento/Framework/App/Test/Unit/PageCache/KernelTest.php +++ b/lib/internal/Magento/Framework/App/Test/Unit/PageCache/KernelTest.php @@ -52,7 +52,7 @@ class KernelTest extends \PHPUnit\Framework\TestCase */ protected function setUp() { - $headersMock = $this->createMock(\Zend\Http\Headers::class); + $headersMock = $this->createMock(\Laminas\Http\Headers::class); $this->cacheMock = $this->createMock(\Magento\Framework\App\PageCache\Cache::class); $this->fullPageCacheMock = $this->createMock(\Magento\PageCache\Model\Cache\Type::class); $this->contextMock = $this->createMock(\Magento\Framework\App\Http\Context::class); @@ -204,7 +204,7 @@ function ($value) { } ); - $cacheControlHeader = \Zend\Http\Header\CacheControl::fromString( + $cacheControlHeader = \Laminas\Http\Header\CacheControl::fromString( 'Cache-Control: public, max-age=100, s-maxage=100' ); @@ -261,7 +261,7 @@ public function testProcessSaveCacheDataProvider() */ public function testProcessNotSaveCache($cacheControlHeader, $httpCode, $isGet, $overrideHeaders) { - $header = \Zend\Http\Header\CacheControl::fromString("Cache-Control: $cacheControlHeader"); + $header = \Laminas\Http\Header\CacheControl::fromString("Cache-Control: $cacheControlHeader"); $this->responseMock->expects( $this->once() )->method( diff --git a/lib/internal/Magento/Framework/Autoload/AutoloaderRegistry.php b/lib/internal/Magento/Framework/Autoload/AutoloaderRegistry.php index f051d215808a3..037d8c2181dbf 100644 --- a/lib/internal/Magento/Framework/Autoload/AutoloaderRegistry.php +++ b/lib/internal/Magento/Framework/Autoload/AutoloaderRegistry.php @@ -5,6 +5,7 @@ */ namespace Magento\Framework\Autoload; +use InvalidArgumentException; use Magento\Framework\Autoload\AutoloaderInterface; /** @@ -23,7 +24,7 @@ class AutoloaderRegistry * @param AutoloaderInterface $newAutoloader * @return void */ - public static function registerAutoloader(AutoloaderInterface $newAutoloader) + public static function registerAutoloader(AutoloaderInterface $newAutoloader): void { self::$autoloader = $newAutoloader; } @@ -31,15 +32,15 @@ public static function registerAutoloader(AutoloaderInterface $newAutoloader) /** * Returns the registered autoloader * - * @throws \Exception + * @throws InvalidArgumentException * @return AutoloaderInterface */ - public static function getAutoloader() + public static function getAutoloader(): AutoloaderInterface { - if (self::$autoloader !== null) { - return self::$autoloader; - } else { - throw new \Exception('Autoloader is not registered, cannot be retrieved.'); + if (!self::$autoloader instanceof AutoloaderInterface) { + throw new InvalidArgumentException('Autoloader is not registered, cannot be retrieved.'); } + + return self::$autoloader; } } diff --git a/lib/internal/Magento/Framework/Bulk/registration.php b/lib/internal/Magento/Framework/Bulk/registration.php index f9f5606b76841..00c35b91e4a24 100644 --- a/lib/internal/Magento/Framework/Bulk/registration.php +++ b/lib/internal/Magento/Framework/Bulk/registration.php @@ -4,6 +4,6 @@ * See COPYING.txt for license details. */ -use \Magento\Framework\Component\ComponentRegistrar; +use Magento\Framework\Component\ComponentRegistrar; ComponentRegistrar::register(ComponentRegistrar::LIBRARY, 'magento/framework-bulk', __DIR__); diff --git a/lib/internal/Magento/Framework/Cache/Backend/Database.php b/lib/internal/Magento/Framework/Cache/Backend/Database.php index 231a8584cc8a5..a33c8f1a0de0e 100644 --- a/lib/internal/Magento/Framework/Cache/Backend/Database.php +++ b/lib/internal/Magento/Framework/Cache/Backend/Database.php @@ -59,6 +59,7 @@ class Database extends \Zend_Cache_Backend implements \Zend_Cache_Backend_Extend * Constructor * * @param array $options associative array of options + * @throws \Zend_Cache_Exception */ public function __construct($options = []) { @@ -82,6 +83,7 @@ public function __construct($options = []) * Get DB adapter * * @return \Magento\Framework\DB\Adapter\AdapterInterface + * @throws \Zend_Cache_Exception */ protected function _getConnection() { @@ -106,6 +108,7 @@ protected function _getConnection() * Get table name where data is stored * * @return string + * @throws \Zend_Cache_Exception */ protected function _getDataTable() { @@ -122,6 +125,7 @@ protected function _getDataTable() * Get table name where tags are stored * * @return string + * @throws \Zend_Cache_Exception */ protected function _getTagsTable() { @@ -139,9 +143,10 @@ protected function _getTagsTable() * * Note : return value is always "string" (unserialization is done by the core not by the backend) * - * @param string $id Cache id - * @param boolean $doNotTestCacheValidity If set to true, the cache validity won't be tested + * @param string $id Cache id + * @param boolean $doNotTestCacheValidity If set to true, the cache validity won't be tested * @return string|false cached datas + * @throws \Zend_Cache_Exception */ public function load($id, $doNotTestCacheValidity = false) { @@ -166,8 +171,9 @@ public function load($id, $doNotTestCacheValidity = false) /** * Test if a cache is available or not (for the given id) * - * @param string $id cache id + * @param string $id cache id * @return mixed|false (a cache is not available) or "last modified" timestamp (int) of the available cache record + * @throws \Zend_Cache_Exception */ public function test($id) { @@ -196,11 +202,13 @@ public function test($id) * Note : $data is always "string" (serialization is done by the * core not by the backend) * - * @param string $data Datas to cache - * @param string $id Cache id - * @param string[] $tags Array of strings, the cache record will be tagged by each string entry - * @param int|bool $specificLifetime Integer to set a specific lifetime or null for infinite lifetime + * @param string $data Datas to cache + * @param string $id Cache id + * @param string[] $tags Array of strings, the cache record will be tagged by each string entry + * @param int|bool $specificLifetime Integer to set a specific lifetime or null for infinite lifetime * @return bool true if no problem + * @throws \Zend_Db_Statement_Exception + * @throws \Zend_Cache_Exception */ public function save($data, $id, $tags = [], $specificLifetime = false) { @@ -239,8 +247,9 @@ public function save($data, $id, $tags = [], $specificLifetime = false) /** * Remove a cache record * - * @param string $id Cache id - * @return boolean True if no problem + * @param string $id Cache id + * @return int|boolean Number of affected rows or false on failure + * @throws \Zend_Cache_Exception */ public function remove($id) { @@ -266,9 +275,10 @@ public function remove($id) * \Zend_Cache::CLEANING_MODE_MATCHING_ANY_TAG => remove cache entries matching any given tags * ($tags can be an array of strings or a single string) * - * @param string $mode Clean mode - * @param string[] $tags Array of tags + * @param string $mode Clean mode + * @param string[] $tags Array of tags * @return boolean true if no problem + * @throws \Zend_Cache_Exception */ public function clean($mode = \Zend_Cache::CLEANING_MODE_ALL, $tags = []) { @@ -301,6 +311,7 @@ public function clean($mode = \Zend_Cache::CLEANING_MODE_ALL, $tags = []) * Return an array of stored cache ids * * @return string[] array of stored cache ids (string) + * @throws \Zend_Cache_Exception */ public function getIds() { @@ -316,6 +327,7 @@ public function getIds() * Return an array of stored tags * * @return string[] array of stored tags (string) + * @throws \Zend_Cache_Exception */ public function getTags() { @@ -330,6 +342,7 @@ public function getTags() * * @param string[] $tags array of tags * @return string[] array of matching cache ids (string) + * @throws \Zend_Cache_Exception */ public function getIdsMatchingTags($tags = []) { @@ -356,6 +369,7 @@ public function getIdsMatchingTags($tags = []) * * @param string[] $tags array of tags * @return string[] array of not matching cache ids (string) + * @throws \Zend_Cache_Exception */ public function getIdsNotMatchingTags($tags = []) { @@ -369,6 +383,7 @@ public function getIdsNotMatchingTags($tags = []) * * @param string[] $tags array of tags * @return string[] array of any matching cache ids (string) + * @throws \Zend_Cache_Exception */ public function getIdsMatchingAnyTags($tags = []) { @@ -404,6 +419,7 @@ public function getFillingPercentage() * * @param string $id cache id * @return array|false array of metadatas (false if the cache id is not found) + * @throws \Zend_Cache_Exception */ public function getMetadatas($id) { @@ -425,6 +441,7 @@ public function getMetadatas($id) * @param string $id cache id * @param int $extraLifetime * @return boolean true if ok + * @throws \Zend_Cache_Exception */ public function touch($id, $extraLifetime) { @@ -471,6 +488,7 @@ public function getCapabilities() * @param string $id * @param string[] $tags * @return bool + * @throws \Zend_Cache_Exception */ protected function _saveTags($id, $tags) { @@ -509,6 +527,8 @@ protected function _saveTags($id, $tags) * @param string $mode * @param string[] $tags * @return bool + * @throws \Zend_Cache_Exception + * @throws \Zend_Db_Statement_Exception * @SuppressWarnings(PHPMD.CyclomaticComplexity) */ protected function _cleanByTags($mode, $tags) @@ -558,6 +578,7 @@ protected function _cleanByTags($mode, $tags) * * @param \Magento\Framework\DB\Adapter\AdapterInterface $connection * @return bool + * @throws \Zend_Cache_Exception */ private function cleanAll(\Magento\Framework\DB\Adapter\AdapterInterface $connection) { @@ -575,6 +596,7 @@ private function cleanAll(\Magento\Framework\DB\Adapter\AdapterInterface $connec * * @param \Magento\Framework\DB\Adapter\AdapterInterface $connection * @return bool + * @throws \Zend_Cache_Exception */ private function cleanOld(\Magento\Framework\DB\Adapter\AdapterInterface $connection) { diff --git a/lib/internal/Magento/Framework/Cache/Backend/Redis.php b/lib/internal/Magento/Framework/Cache/Backend/Redis.php new file mode 100644 index 0000000000000..ed97cb9f50c3b --- /dev/null +++ b/lib/internal/Magento/Framework/Cache/Backend/Redis.php @@ -0,0 +1,83 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ + +namespace Magento\Framework\Cache\Backend; + +/** + * Redis wrapper to extend current implementation behaviour. + */ +class Redis extends \Cm_Cache_Backend_Redis +{ + /** + * Local state of preloaded keys. + * + * @var array + */ + private $preloadedData = []; + + /** + * Array of keys to be preloaded. + * + * @var array + */ + private $preloadKeys = []; + + /** + * @param array $options + */ + public function __construct($options = []) + { + $this->preloadKeys = $options['preload_keys'] ?? []; + parent::__construct($options); + } + + /** + * Load value with given id from cache + * + * @param string $id Cache id + * @param boolean $doNotTestCacheValidity If set to true, the cache validity won't be tested + * @return bool|string + */ + public function load($id, $doNotTestCacheValidity = false) + { + if (!empty($this->preloadKeys) && empty($this->preloadedData)) { + $redis = $this->_slave ?? $this->_redis; + $redis = $redis->pipeline(); + + foreach ($this->preloadKeys as $key) { + $redis->hGet(self::PREFIX_KEY . $key, self::FIELD_DATA); + } + + $this->preloadedData = array_filter(array_combine($this->preloadKeys, $redis->exec())); + } + + if (isset($this->preloadedData[$id])) { + return $this->_decodeData($this->preloadedData[$id]); + } + + return parent::load($id, $doNotTestCacheValidity); + } + + /** + * Cover errors on save operations, which may occurs when Redis cannot evict keys, which is expected in some cases. + * + * @param string $data + * @param string $id + * @param array $tags + * @param bool $specificLifetime + * @return bool + */ + public function save($data, $id, $tags = [], $specificLifetime = false) + { + try { + parent::save($data, $id, $tags, $specificLifetime); + } catch (\Throwable $exception) { + return false; + } + + return true; + } +} diff --git a/lib/internal/Magento/Framework/Cache/Backend/RemoteSynchronizedCache.php b/lib/internal/Magento/Framework/Cache/Backend/RemoteSynchronizedCache.php index 007f921259bed..90c1bf5808737 100644 --- a/lib/internal/Magento/Framework/Cache/Backend/RemoteSynchronizedCache.php +++ b/lib/internal/Magento/Framework/Cache/Backend/RemoteSynchronizedCache.php @@ -9,8 +9,10 @@ /** * Remote synchronized cache * - * This class created for correct work local caches with multiple web nodes, - * that will be check cache status from remote cache + * This class created for correct work witch local caches and multiple web nodes, + * in order to be sure that we always have up to date local version of cache. + * This class will be check cache version from remote cache and in case it newer + * than local one, it will update local one from remote cache a.k.a two level cache. */ class RemoteSynchronizedCache extends \Zend_Cache_Backend implements \Zend_Cache_Backend_ExtendedInterface { @@ -36,11 +38,15 @@ class RemoteSynchronizedCache extends \Zend_Cache_Backend implements \Zend_Cache protected $cacheInvalidationTime; /** - * {@inheritdoc} + * Suffix for hash to compare data version in cache storage. + */ + private const HASH_SUFFIX = ':hash'; + + /** + * @inheritdoc */ protected $_options = [ 'remote_backend' => '', - 'remote_backend_invalidation_time_id' => 'default_remote_backend_invalidation_time', 'remote_backend_custom_naming' => true, 'remote_backend_autoload' => true, 'remote_backend_options' => [], @@ -52,6 +58,7 @@ class RemoteSynchronizedCache extends \Zend_Cache_Backend implements \Zend_Cache /** * @param array $options + * @throws \Zend_Cache_Exception */ public function __construct(array $options = []) { @@ -97,76 +104,137 @@ public function __construct(array $options = []) } /** - * Update remote cache status info + * @inheritdoc + */ + public function setDirectives($directives) + { + return $this->local->setDirectives($directives); + } + + /** + * Return hash sign of the data. * - * @return void + * @param string $data + * @return string */ - private function updateRemoteCacheStatusInfo() + private function getDataVersion(string $data) { - $this->remote->save(time(), $this->_options['remote_backend_invalidation_time_id'], [], null); - $this->cacheInvalidationTime = null; + return \hash('sha256', $data); } /** - * {@inheritdoc} + * Load data version by id from remote. + * + * @param string $id + * @return false|string */ - public function setDirectives($directives) + private function loadRemoteDataVersion(string $id) { - return $this->local->setDirectives($directives); + return $this->remote->load( + $id . self::HASH_SUFFIX + ); } /** - * {@inheritdoc} + * Save new data version to remote. + * + * @param string $data + * @param string $id + * @param array $tags + * @param mixed $specificLifetime + * @return bool + */ + private function saveRemoteDataVersion(string $data, string $id, array $tags, $specificLifetime = false) + { + return $this->remote->save($this->getDataVersion($data), $id . self::HASH_SUFFIX, $tags, $specificLifetime); + } + + /** + * Remove remote data version. + * + * @param string $id + * @return bool + */ + private function removeRemoteDataVersion($id) + { + return $this->remote->remove($id . self::HASH_SUFFIX); + } + + /** + * @inheritdoc */ public function load($id, $doNotTestCacheValidity = false) { - $dataModificationTime = $this->local->test($id); - if ($this->cacheInvalidationTime === null) { - $this->cacheInvalidationTime = $this->remote->load($this->_options['remote_backend_invalidation_time_id']); - } - if ($dataModificationTime >= $this->cacheInvalidationTime) { - return $this->local->load($id, $doNotTestCacheValidity); + $localData = $this->local->load($id); + $remoteData = false; + + if (false === $localData) { + $remoteData = $this->remote->load($id); + + if (false === $remoteData) { + return false; + } } else { - return false; + if ($this->getDataVersion($localData) !== $this->loadRemoteDataVersion($id)) { + $localData = false; + $remoteData = $this->remote->load($id); + } } + + if ($remoteData !== false) { + $this->local->save($remoteData, $id); + $localData = $remoteData; + } + + return $localData; } /** - * {@inheritdoc} + * @inheritdoc */ public function test($id) { - return $this->local->test($id); + return $this->local->test($id) ?? $this->remote->test($id); } /** - * {@inheritdoc} + * @inheritdoc */ public function save($data, $id, $tags = [], $specificLifetime = false) { - return $this->local->save($data, $id, $tags, $specificLifetime); + $dataToSave = $data; + $remHash = $this->loadRemoteDataVersion($id); + + if ($remHash !== false) { + $dataToSave = $this->remote->load($id); + } else { + $this->remote->save($data, $id, $tags, $specificLifetime); + $this->saveRemoteDataVersion($data, $id, $tags, $specificLifetime); + } + + return $this->local->save($dataToSave, $id, [], $specificLifetime); } /** - * {@inheritdoc} + * @inheritdoc */ public function remove($id) { - $this->updateRemoteCacheStatusInfo(); - return $this->local->remove($id); + return $this->removeRemoteDataVersion($id) && + $this->remote->remove($id) && + $this->local->remove($id); } /** - * {@inheritdoc} + * @inheritdoc */ public function clean($mode = \Zend_Cache::CLEANING_MODE_ALL, $tags = []) { - $this->updateRemoteCacheStatusInfo(); - return $this->local->clean($mode, $tags); + return $this->remote->clean($mode, $tags); } /** - * {@inheritdoc} + * @inheritdoc */ public function getIds() { @@ -174,7 +242,7 @@ public function getIds() } /** - * {@inheritdoc} + * @inheritdoc */ public function getTags() { @@ -182,7 +250,7 @@ public function getTags() } /** - * {@inheritdoc} + * @inheritdoc */ public function getIdsMatchingTags($tags = []) { @@ -190,7 +258,7 @@ public function getIdsMatchingTags($tags = []) } /** - * {@inheritdoc} + * @inheritdoc */ public function getIdsNotMatchingTags($tags = []) { @@ -198,7 +266,7 @@ public function getIdsNotMatchingTags($tags = []) } /** - * {@inheritdoc} + * @inheritdoc */ public function getIdsMatchingAnyTags($tags = []) { @@ -206,7 +274,7 @@ public function getIdsMatchingAnyTags($tags = []) } /** - * {@inheritdoc} + * @inheritdoc */ public function getFillingPercentage() { @@ -214,7 +282,7 @@ public function getFillingPercentage() } /** - * {@inheritdoc} + * @inheritdoc */ public function getMetadatas($id) { @@ -222,7 +290,7 @@ public function getMetadatas($id) } /** - * {@inheritdoc} + * @inheritdoc */ public function touch($id, $extraLifetime) { @@ -230,7 +298,7 @@ public function touch($id, $extraLifetime) } /** - * {@inheritdoc} + * @inheritdoc */ public function getCapabilities() { diff --git a/lib/internal/Magento/Framework/Cache/Config/Reader.php b/lib/internal/Magento/Framework/Cache/Config/Reader.php index 445e91240e7e5..942a3931e0173 100644 --- a/lib/internal/Magento/Framework/Cache/Config/Reader.php +++ b/lib/internal/Magento/Framework/Cache/Config/Reader.php @@ -3,9 +3,19 @@ * Copyright © Magento, Inc. All rights reserved. * See COPYING.txt for license details. */ + namespace Magento\Framework\Cache\Config; -class Reader extends \Magento\Framework\Config\Reader\Filesystem +use Magento\Framework\App\Area; +use Magento\Framework\Config\Dom; +use Magento\Framework\Config\FileResolverInterface; +use Magento\Framework\Config\Reader\Filesystem; +use Magento\Framework\Config\ValidationStateInterface; + +/** + * Cache configuration reader + */ +class Reader extends Filesystem { /** * List of id attributes for merge @@ -15,24 +25,27 @@ class Reader extends \Magento\Framework\Config\Reader\Filesystem protected $_idAttributes = ['/config/type' => 'name']; /** - * @param \Magento\Framework\Config\FileResolverInterface $fileResolver + * Initialize dependencies. + * + * @param FileResolverInterface $fileResolver * @param Converter $converter * @param SchemaLocator $schemaLocator - * @param \Magento\Framework\Config\ValidationStateInterface $validationState + * @param ValidationStateInterface $validationState * @param string $fileName * @param array $idAttributes * @param string $domDocumentClass * @param string $defaultScope + * phpcs:disable Generic.CodeAnalysis.UselessOverridingMethod */ public function __construct( - \Magento\Framework\Config\FileResolverInterface $fileResolver, - \Magento\Framework\Cache\Config\Converter $converter, - \Magento\Framework\Cache\Config\SchemaLocator $schemaLocator, - \Magento\Framework\Config\ValidationStateInterface $validationState, + FileResolverInterface $fileResolver, + Converter $converter, + SchemaLocator $schemaLocator, + ValidationStateInterface $validationState, $fileName = 'cache.xml', $idAttributes = [], - $domDocumentClass = \Magento\Framework\Config\Dom::class, - $defaultScope = 'global' + $domDocumentClass = Dom::class, + $defaultScope = Area::AREA_GLOBAL ) { parent::__construct( $fileResolver, diff --git a/lib/internal/Magento/Framework/Cache/Config/SchemaLocator.php b/lib/internal/Magento/Framework/Cache/Config/SchemaLocator.php index 5471dbcfb6c62..2d3be1b1a4067 100644 --- a/lib/internal/Magento/Framework/Cache/Config/SchemaLocator.php +++ b/lib/internal/Magento/Framework/Cache/Config/SchemaLocator.php @@ -7,6 +7,9 @@ */ namespace Magento\Framework\Cache\Config; +/** + * Cache configuration schema locator + */ class SchemaLocator implements \Magento\Framework\Config\SchemaLocatorInterface { /** @@ -15,6 +18,9 @@ class SchemaLocator implements \Magento\Framework\Config\SchemaLocatorInterface protected $urnResolver; /** + * Initialize dependencies. + * + * @param \Magento\Framework\Config\Dom\UrnResolver $urnResolver */ public function __construct(\Magento\Framework\Config\Dom\UrnResolver $urnResolver) { @@ -25,6 +31,7 @@ public function __construct(\Magento\Framework\Config\Dom\UrnResolver $urnResolv * Get path to merged config schema * * @return string|null + * @throws \Magento\Framework\Exception\NotFoundException */ public function getSchema() { @@ -34,10 +41,11 @@ public function getSchema() /** * Get path to pre file validation schema * - * @return null + * @return string|null + * @throws \Magento\Framework\Exception\NotFoundException */ public function getPerFileSchema() { - return null; + return $this->urnResolver->getRealPath('urn:magento:framework:Cache/etc/cache.xsd'); } } diff --git a/lib/internal/Magento/Framework/Cache/LockGuardedCacheLoader.php b/lib/internal/Magento/Framework/Cache/LockGuardedCacheLoader.php index 216d8e9a0a01b..d15f23e0d7c0b 100644 --- a/lib/internal/Magento/Framework/Cache/LockGuardedCacheLoader.php +++ b/lib/internal/Magento/Framework/Cache/LockGuardedCacheLoader.php @@ -37,18 +37,33 @@ class LockGuardedCacheLoader private $delayTimeout; /** + * Timeout for information to be collected and saved. + * If timeout passed that means that data cannot be saved right now. + * And we will just return collected data. + * + * Value of the variable in milliseconds. + * + * @var int + */ + private $loadTimeout; + + /** + * LockGuardedCacheLoader constructor. * @param LockManagerInterface $locker * @param int $lockTimeout * @param int $delayTimeout + * @param int $loadTimeout */ public function __construct( LockManagerInterface $locker, int $lockTimeout = 10000, - int $delayTimeout = 20 + int $delayTimeout = 20, + int $loadTimeout = 10000 ) { $this->locker = $locker; $this->lockTimeout = $lockTimeout; $this->delayTimeout = $delayTimeout; + $this->loadTimeout = $loadTimeout; } /** @@ -67,21 +82,21 @@ public function lockedLoadData( callable $dataSaver ) { $cachedData = $dataLoader(); //optimistic read - - while ($cachedData === false && $this->locker->isLocked($lockName)) { - usleep($this->delayTimeout * 1000); - $cachedData = $dataLoader(); - } + $deadline = microtime(true) + $this->loadTimeout; while ($cachedData === false) { - try { - if ($this->locker->lock($lockName, $this->lockTimeout / 1000)) { + if ($deadline <= microtime(true)) { + return $dataCollector(); + } + + if ($this->locker->lock($lockName, $this->lockTimeout / 1000)) { + try { $data = $dataCollector(); $dataSaver($data); $cachedData = $data; + } finally { + $this->locker->unlock($lockName); } - } finally { - $this->locker->unlock($lockName); } if ($cachedData === false) { diff --git a/lib/internal/Magento/Framework/Cache/Test/Unit/Backend/RemoteSynchronizedCacheTest.php b/lib/internal/Magento/Framework/Cache/Test/Unit/Backend/RemoteSynchronizedCacheTest.php index 6aca2bedd4989..9a1afc2ebd6ea 100644 --- a/lib/internal/Magento/Framework/Cache/Test/Unit/Backend/RemoteSynchronizedCacheTest.php +++ b/lib/internal/Magento/Framework/Cache/Test/Unit/Backend/RemoteSynchronizedCacheTest.php @@ -6,6 +6,9 @@ namespace Magento\Framework\Cache\Test\Unit\Backend; +use Magento\Framework\Cache\Backend\Database; +use Magento\Framework\Cache\Backend\RemoteSynchronizedCache; + class RemoteSynchronizedCacheTest extends \PHPUnit\Framework\TestCase { /** @@ -13,9 +16,43 @@ class RemoteSynchronizedCacheTest extends \PHPUnit\Framework\TestCase */ protected $objectManager; + /** + * @var \Cm_Cache_Backend_File|\PHPUnit_Framework_MockObject_MockObject + */ + private $localCacheMockExample; + + /** + * @var Database|\PHPUnit_Framework_MockObject_MockObject + */ + private $remoteCacheMockExample; + + /** + * @var RemoteSynchronizedCache + */ + private $remoteSyncCacheInstance; + protected function setUp() { $this->objectManager = new \Magento\Framework\TestFramework\Unit\Helper\ObjectManager($this); + + $this->localCacheMockExample = $this->getMockBuilder(\Cm_Cache_Backend_File::class) + ->disableOriginalConstructor() + ->getMock(); + + $this->remoteCacheMockExample = $this->getMockBuilder(\Magento\Framework\Cache\Backend\Database::class) + ->disableOriginalConstructor() + ->getMock(); + /** @var \Magento\Framework\Cache\Backend\Database $databaseCacheInstance */ + + $this->remoteSyncCacheInstance = $this->objectManager->getObject( + \Magento\Framework\Cache\Backend\RemoteSynchronizedCache::class, + [ + 'options' => [ + 'remote_backend' => $this->remoteCacheMockExample, + 'local_backend' => $this->localCacheMockExample, + ], + ] + ); } /** @@ -109,152 +146,135 @@ public function initializeWithOutExceptionDataProvider() } /** - * @param array $options - * @param bool|string $expected - * - * @dataProvider loadDataProvider + * Test that load will always return newest data. */ - public function testLoad($options, $expected) + public function testLoadWithLocalData() { - /** @var \Magento\Framework\Cache\Backend\Database $database */ - $database = $this->objectManager->getObject( - \Magento\Framework\Cache\Backend\RemoteSynchronizedCache::class, - [ - 'options' => $options, - ] - ); + $localData = 1; + $remoteData = 2; - $this->assertEquals($expected, $database->load(5)); - } + $this->localCacheMockExample + ->expects($this->at(0)) + ->method('load') + ->will($this->returnValue($localData)); - /** - * @return array - */ - public function loadDataProvider() - { - return [ - 'cacheInvalidationTime_is_less_than_that_dataModificationTime' => [ - 'options' => [ - 'remote_backend' => $this->getDatabaseMock(444), - 'local_backend' => $this->getFileMock(555, 'loaded_value'), - ], - 'expected' => 'loaded_value', - ], - 'cacheInvalidationTime_is_greater_than_that_dataModificationTime' => [ - 'options' => [ - 'remote_backend' => $this->getDatabaseMock(444), - 'local_backend' => $this->getFileMock(333, 'loaded_value'), - ], - 'expected' => false, - ], - 'cacheInvalidationTime_is_equal_to_the_dataModificationTime' => [ - 'options' => [ - 'remote_backend' => $this->getDatabaseMock(444), - 'local_backend' => $this->getFileMock(444, 'loaded_value'), - ], - 'expected' => 'loaded_value', - ], - ]; - } + $this->remoteCacheMockExample + ->expects($this->at(0)) + ->method('load') + ->will($this->returnValue(\hash('sha256', $remoteData))); - /** - * @param integer $cacheInvalidationTime - * @return \Magento\Framework\Cache\Backend\Database|\PHPUnit_Framework_MockObject_MockObject - */ - public function getDatabaseMock($cacheInvalidationTime) - { - $databaseMock = $this->getMockBuilder(\Magento\Framework\Cache\Backend\Database::class) - ->setMethods(['load']) - ->disableOriginalConstructor() - ->getMock(); - $databaseMock->expects($this->once()) + $this->remoteCacheMockExample + ->expects($this->at(1)) ->method('load') - ->will($this->returnValue($cacheInvalidationTime)); + ->will($this->returnValue($remoteData)); - return $databaseMock; + $this->localCacheMockExample + ->expects($this->atLeastOnce()) + ->method('save') + ->with($remoteData) + ->will($this->returnValue(true)); + + $this->assertEquals($remoteData, $this->remoteSyncCacheInstance->load(1)); } - /** - * @param integer $dataModificationTime - * @return \Cm_Cache_Backend_File|\PHPUnit_Framework_MockObject_MockObject - */ - public function getFileMock($dataModificationTime, $cacheResult) + public function testLoadWithNoLocalAndNoRemoteData() { - $fileMock = $this->getMockBuilder(\Cm_Cache_Backend_File::class) - ->setMethods(['test', 'load']) - ->disableOriginalConstructor() - ->getMock(); - $fileMock->expects($this->once()) - ->method('test') - ->will($this->returnValue($dataModificationTime)); - $fileMock->expects($this->any()) + $localData = false; + $remoteData = false; + + $this->localCacheMockExample + ->expects($this->at(0)) + ->method('load') + ->will($this->returnValue($localData)); + + $this->remoteCacheMockExample + ->expects($this->at(0)) ->method('load') - ->will($this->returnValue($cacheResult)); + ->will($this->returnValue($remoteData)); - return $fileMock; + $this->assertEquals($remoteData, $this->remoteSyncCacheInstance->load(1)); } - public function testRemove() + public function testLoadWithNoLocalAndRemoteData() { - $databaseMock = $this->getMockBuilder(\Magento\Framework\Cache\Backend\Database::class) - ->setMethods(['save']) - ->disableOriginalConstructor() - ->getMock(); - $databaseMock->expects($this->once()) + $localData = false; + $remoteData = 1; + + $this->localCacheMockExample + ->expects($this->atLeastOnce()) + ->method('load') + ->will($this->returnValue($localData)); + + $this->remoteCacheMockExample + ->expects($this->at(0)) + ->method('load') + ->will($this->returnValue($remoteData)); + + $this->localCacheMockExample + ->expects($this->atLeastOnce()) ->method('save') ->will($this->returnValue(true)); - $fileMock = $this->getMockBuilder(\Cm_Cache_Backend_File::class) - ->setMethods(['remove']) - ->disableOriginalConstructor() - ->getMock(); - $fileMock->expects($this->once()) + $this->assertEquals($remoteData, $this->remoteSyncCacheInstance->load(1)); + } + + public function testRemove() + { + $this->remoteCacheMockExample + ->expects($this->exactly(2)) ->method('remove') - ->will($this->returnValue(true)); + ->willReturn(true); - /** @var \Magento\Framework\Cache\Backend\Database $database */ - $database = $this->objectManager->getObject( - \Magento\Framework\Cache\Backend\RemoteSynchronizedCache::class, - [ - 'options' => [ - 'remote_backend' => $databaseMock, - 'local_backend' => $fileMock, - ] - ] - ); + $this->localCacheMockExample + ->expects($this->exactly(1)) + ->method('remove') + ->willReturn(true); - $this->assertEquals(true, $database->remove(5)); + $this->remoteSyncCacheInstance->remove(1); } public function testClean() { - $databaseMock = $this->getMockBuilder(\Magento\Framework\Cache\Backend\Database::class) - ->setMethods(['save']) - ->disableOriginalConstructor() - ->getMock(); - $databaseMock->expects($this->once()) + $this->remoteCacheMockExample + ->expects($this->exactly(1)) + ->method('clean') + ->willReturn(true); + + $this->remoteSyncCacheInstance->clean(); + } + + public function testSaveWithRemoteData() + { + $remoteData = 1; + + $this->remoteCacheMockExample + ->expects($this->at(0)) + ->method('load') + ->willReturn(\hash('sha256', $remoteData)); + + $this->remoteCacheMockExample + ->expects($this->at(1)) + ->method('load') + ->willReturn($remoteData); + + $this->localCacheMockExample + ->expects($this->once()) ->method('save') - ->will($this->returnValue(true)); + ->willReturn(true); - $fileMock = $this->getMockBuilder(\Cm_Cache_Backend_File::class) - ->setMethods(['clean']) - ->disableOriginalConstructor() - ->getMock(); - $fileMock->expects($this->once()) - ->method('clean') - ->will($this->returnValue(true)); + $this->remoteSyncCacheInstance->save($remoteData, 1); + } - /** @var \Magento\Framework\Cache\Backend\Database $database */ - $database = $this->objectManager->getObject( - \Magento\Framework\Cache\Backend\RemoteSynchronizedCache::class, - [ - 'options' => [ - 'remote_backend' => $databaseMock, - 'local_backend' => $fileMock, - ] - ] - ); + public function testSaveWithoutRemoteData() + { + $this->remoteCacheMockExample + ->expects($this->at(0)) + ->method('load') + ->willReturn(false); + + $this->remoteCacheMockExample->expects($this->exactly(2))->method('save'); + $this->localCacheMockExample->expects($this->once())->method('save'); - $this->assertEquals(true, $database->clean()); + $this->remoteSyncCacheInstance->save(1, 1); } } diff --git a/lib/internal/Magento/Framework/Cache/Test/Unit/Config/ConverterTest.php b/lib/internal/Magento/Framework/Cache/Test/Unit/Config/ConverterTest.php deleted file mode 100644 index 7f86e162311c8..0000000000000 --- a/lib/internal/Magento/Framework/Cache/Test/Unit/Config/ConverterTest.php +++ /dev/null @@ -1,30 +0,0 @@ -<?php -/** - * Copyright © Magento, Inc. All rights reserved. - * See COPYING.txt for license details. - */ -namespace Magento\Framework\Cache\Test\Unit\Config; - -class ConverterTest extends \PHPUnit\Framework\TestCase -{ - /** - * @var \PHPUnit_Framework_MockObject_MockObject|\Magento\Framework\Cache\Config\Converter - */ - protected $_model; - - protected function setUp() - { - $this->_model = new \Magento\Framework\Cache\Config\Converter(); - } - - public function testConvert() - { - $dom = new \DOMDocument(); - $xmlFile = __DIR__ . '/_files/cache_config.xml'; - $dom->loadXML(file_get_contents($xmlFile)); - - $convertedFile = __DIR__ . '/_files/cache_config.php'; - $expectedResult = include $convertedFile; - $this->assertEquals($expectedResult, $this->_model->convert($dom)); - } -} diff --git a/lib/internal/Magento/Framework/Cache/Test/Unit/Config/_files/cache_config.php b/lib/internal/Magento/Framework/Cache/Test/Unit/Config/_files/cache_config.php deleted file mode 100644 index 0a45e50bbe198..0000000000000 --- a/lib/internal/Magento/Framework/Cache/Test/Unit/Config/_files/cache_config.php +++ /dev/null @@ -1,23 +0,0 @@ -<?php -/** - * Copyright © Magento, Inc. All rights reserved. - * See COPYING.txt for license details. - */ -return [ - 'types' => [ - 'config' => [ - 'name' => 'config', - 'translate' => 'label,description', - 'instance' => \Magento\Framework\App\Cache\Type\Config::class, - 'label' => 'Configuration', - 'description' => 'Cache Description', - ], - 'layout' => [ - 'name' => 'layout', - 'translate' => 'label,description', - 'instance' => \Magento\Framework\App\Cache\Type\Layout::class, - 'label' => 'Layouts', - 'description' => 'Layout building instructions', - ], - ] -]; diff --git a/lib/internal/Magento/Framework/Cache/Test/Unit/Config/_files/cache_config.xml b/lib/internal/Magento/Framework/Cache/Test/Unit/Config/_files/cache_config.xml deleted file mode 100644 index 315ddd9cec67c..0000000000000 --- a/lib/internal/Magento/Framework/Cache/Test/Unit/Config/_files/cache_config.xml +++ /dev/null @@ -1,17 +0,0 @@ -<?xml version="1.0"?> -<!-- -/** - * Copyright © Magento, Inc. All rights reserved. - * See COPYING.txt for license details. - */ ---> -<config xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="urn:magento:framework:Cache/etc/cache.xsd"> - <type name="config" translate="label,description" instance="Magento\Framework\App\Cache\Type\Config"> - <label>Configuration</label> - <description>Cache Description</description> - </type> - <type name="layout" translate="label,description" instance="Magento\Framework\App\Cache\Type\Layout"> - <label>Layouts</label> - <description>Layout building instructions</description> - </type> -</config> diff --git a/lib/internal/Magento/Framework/Cache/etc/cache.xsd b/lib/internal/Magento/Framework/Cache/etc/cache.xsd index 74b831bb6ac03..d997e295140f5 100644 --- a/lib/internal/Magento/Framework/Cache/etc/cache.xsd +++ b/lib/internal/Magento/Framework/Cache/etc/cache.xsd @@ -6,24 +6,36 @@ */ --> <xs:schema xmlns:xs="http://www.w3.org/2001/XMLSchema"> - <xs:element name="config" type="configType" /> - - <xs:complexType name="configType"> - <xs:sequence> - <xs:element type="cacheType" name="type" maxOccurs="unbounded" minOccurs="0"/> - </xs:sequence> - </xs:complexType> - <xs:complexType name="cacheType"> <xs:annotation> - <xs:documentation>Cache type declaration</xs:documentation> + <xs:documentation> + Cache type declaration + </xs:documentation> </xs:annotation> - <xs:choice maxOccurs="unbounded" minOccurs="0"> - <xs:element name="label" type="xs:string" /> - <xs:element name="description" type="xs:string" /> - </xs:choice> + <xs:all> + <xs:element name="label" type="xs:string" minOccurs="1" maxOccurs="1"/> + <xs:element name="description" type="xs:string" minOccurs="1" maxOccurs="1"/> + </xs:all> <xs:attribute type="xs:string" name="name" use="required"/> <xs:attribute type="xs:string" name="translate" use="optional"/> - <xs:attribute type="xs:string" name="instance" use="optional"/> + <xs:attribute type="xs:string" name="instance" use="required"/> + </xs:complexType> + + <xs:element name="config" type="configType"> + <xs:unique name="uniqueCacheName"> + <xs:annotation> + <xs:documentation> + Cache name must be unique. + </xs:documentation> + </xs:annotation> + <xs:selector xpath="type"/> + <xs:field xpath="@name"/> + </xs:unique> + </xs:element> + + <xs:complexType name="configType"> + <xs:sequence> + <xs:element type="cacheType" name="type" maxOccurs="unbounded" minOccurs="1"/> + </xs:sequence> </xs:complexType> </xs:schema> diff --git a/lib/internal/Magento/Framework/Code/Generator/ClassGenerator.php b/lib/internal/Magento/Framework/Code/Generator/ClassGenerator.php index 0a48945e55c2d..b3521698bf396 100644 --- a/lib/internal/Magento/Framework/Code/Generator/ClassGenerator.php +++ b/lib/internal/Magento/Framework/Code/Generator/ClassGenerator.php @@ -3,13 +3,17 @@ * Copyright © Magento, Inc. All rights reserved. * See COPYING.txt for license details. */ + namespace Magento\Framework\Code\Generator; -use Zend\Code\Generator\MethodGenerator; -use Zend\Code\Generator\PropertyGenerator; +use Laminas\Code\Generator\MethodGenerator; +use Laminas\Code\Generator\PropertyGenerator; -class ClassGenerator extends \Zend\Code\Generator\ClassGenerator implements - \Magento\Framework\Code\Generator\CodeGeneratorInterface +/** + * Class code generator + */ +class ClassGenerator extends \Laminas\Code\Generator\ClassGenerator implements + CodeGeneratorInterface { /** * Possible doc block options @@ -64,6 +68,8 @@ class ClassGenerator extends \Zend\Code\Generator\ClassGenerator implements ]; /** + * Set data to object + * * @param object $object * @param array $data * @param array $map @@ -86,7 +92,7 @@ protected function _setDataToObject($object, array $data, array $map) */ public function setClassDocBlock(array $docBlock) { - $docBlockObject = new \Zend\Code\Generator\DocBlockGenerator(); + $docBlockObject = new \Laminas\Code\Generator\DocBlockGenerator(); $docBlockObject->setWordWrap(false); $this->_setDataToObject($docBlockObject, $docBlock, $this->_docBlockOptions); @@ -94,7 +100,7 @@ public function setClassDocBlock(array $docBlock) } /** - * addMethods() + * Add methods * * @param array $methods * @return $this @@ -115,7 +121,7 @@ public function addMethods(array $methods) ) { $parametersArray = []; foreach ($methodOptions['parameters'] as $parameterOptions) { - $parameterObject = new \Zend\Code\Generator\ParameterGenerator(); + $parameterObject = new \Laminas\Code\Generator\ParameterGenerator(); $this->_setDataToObject($parameterObject, $parameterOptions, $this->_parameterOptions); $parametersArray[] = $parameterObject; } @@ -124,7 +130,7 @@ public function addMethods(array $methods) } if (isset($methodOptions['docblock']) && is_array($methodOptions['docblock'])) { - $docBlockObject = new \Zend\Code\Generator\DocBlockGenerator(); + $docBlockObject = new \Laminas\Code\Generator\DocBlockGenerator(); $docBlockObject->setWordWrap(false); $this->_setDataToObject($docBlockObject, $methodOptions['docblock'], $this->_docBlockOptions); @@ -157,7 +163,7 @@ public function addMethodFromGenerator(MethodGenerator $method) } /** - * addProperties() + * Add properties * * @param array $properties * @return $this @@ -172,7 +178,7 @@ public function addProperties(array $properties) if (isset($propertyOptions['docblock'])) { $docBlock = $propertyOptions['docblock']; if (is_array($docBlock)) { - $docBlockObject = new \Zend\Code\Generator\DocBlockGenerator(); + $docBlockObject = new \Laminas\Code\Generator\DocBlockGenerator(); $docBlockObject->setWordWrap(false); $this->_setDataToObject($docBlockObject, $docBlock, $this->_docBlockOptions); $propertyObject->setDocBlock($docBlockObject); @@ -212,6 +218,8 @@ protected function createMethodGenerator() } /** + * Get namespace name + * * @return string|null */ public function getNamespaceName() diff --git a/lib/internal/Magento/Framework/Code/Generator/CodeGeneratorInterface.php b/lib/internal/Magento/Framework/Code/Generator/CodeGeneratorInterface.php index 81ada6a1ee369..59fc5522a12e2 100644 --- a/lib/internal/Magento/Framework/Code/Generator/CodeGeneratorInterface.php +++ b/lib/internal/Magento/Framework/Code/Generator/CodeGeneratorInterface.php @@ -9,7 +9,7 @@ * Interface \Magento\Framework\Code\Generator\CodeGeneratorInterface * */ -interface CodeGeneratorInterface extends \Zend\Code\Generator\GeneratorInterface +interface CodeGeneratorInterface extends \Laminas\Code\Generator\GeneratorInterface { /** * Set class name. diff --git a/lib/internal/Magento/Framework/Code/Generator/EntityAbstract.php b/lib/internal/Magento/Framework/Code/Generator/EntityAbstract.php index 3efe110ccf193..f29474f476b45 100644 --- a/lib/internal/Magento/Framework/Code/Generator/EntityAbstract.php +++ b/lib/internal/Magento/Framework/Code/Generator/EntityAbstract.php @@ -5,12 +5,15 @@ */ namespace Magento\Framework\Code\Generator; -use Zend\Code\Generator\ValueGenerator; +use Laminas\Code\Generator\ValueGenerator; +/** + * Abstract entity + */ abstract class EntityAbstract { /** - * Entity type + * Entity type abstract */ const ENTITY_TYPE = 'abstract'; @@ -183,7 +186,6 @@ protected function _getDefaultResultClassName($modelClassName) */ protected function _getClassProperties() { - // protected $_objectManager = null; $objectManager = [ 'name' => '_objectManager', 'visibility' => 'protected', @@ -238,6 +240,8 @@ protected function _addError($message) } /** + * Validate data + * * @return bool */ protected function _validateData() @@ -263,6 +267,8 @@ protected function _validateData() } /** + * Get class DocBlock + * * @return array */ protected function _getClassDocBlock() @@ -272,6 +278,8 @@ protected function _getClassDocBlock() } /** + * Get generated code + * * @return string */ protected function _getGeneratedCode() @@ -281,6 +289,8 @@ protected function _getGeneratedCode() } /** + * Fix code style + * * @param string $sourceCode * @return string */ @@ -305,8 +315,9 @@ protected function _getNullDefaultValue() } /** - * @param \ReflectionParameter $parameter + * Extract parameter type * + * @param \ReflectionParameter $parameter * @return null|string */ private function extractParameterType( @@ -336,9 +347,11 @@ private function extractParameterType( } /** - * @param \ReflectionParameter $parameter + * Extract parameter default value * + * @param \ReflectionParameter $parameter * @return null|ValueGenerator + * @throws \ReflectionException */ private function extractParameterDefaultValue( \ReflectionParameter $parameter @@ -362,6 +375,7 @@ private function extractParameterDefaultValue( * * @param \ReflectionParameter $parameter * @return array + * @throws \ReflectionException */ protected function _getMethodParameterInfo(\ReflectionParameter $parameter) { diff --git a/lib/internal/Magento/Framework/Code/Generator/InterfaceMethodGenerator.php b/lib/internal/Magento/Framework/Code/Generator/InterfaceMethodGenerator.php index d73c94eb89e00..173906f0fffcd 100644 --- a/lib/internal/Magento/Framework/Code/Generator/InterfaceMethodGenerator.php +++ b/lib/internal/Magento/Framework/Code/Generator/InterfaceMethodGenerator.php @@ -6,12 +6,12 @@ namespace Magento\Framework\Code\Generator; /** - * Interface method generator. + * Interface method code generator */ -class InterfaceMethodGenerator extends \Zend\Code\Generator\MethodGenerator +class InterfaceMethodGenerator extends \Laminas\Code\Generator\MethodGenerator { /** - * {@inheritdoc} + * @inheritDoc */ public function generate() { diff --git a/lib/internal/Magento/Framework/Code/Reader/ArgumentsReader.php b/lib/internal/Magento/Framework/Code/Reader/ArgumentsReader.php index d2e6c01997b30..c9a8cce706af4 100644 --- a/lib/internal/Magento/Framework/Code/Reader/ArgumentsReader.php +++ b/lib/internal/Magento/Framework/Code/Reader/ArgumentsReader.php @@ -5,6 +5,9 @@ */ namespace Magento\Framework\Code\Reader; +/** + * The class arguments reader + */ class ArgumentsReader { const NO_DEFAULT_VALUE = 'NO-DEFAULT'; @@ -54,7 +57,7 @@ public function getConstructorArguments(\ReflectionClass $class, $groupByPositio return $output; } - $constructor = new \Zend\Code\Reflection\MethodReflection($class->getName(), '__construct'); + $constructor = new \Laminas\Code\Reflection\MethodReflection($class->getName(), '__construct'); foreach ($constructor->getParameters() as $parameter) { $name = $parameter->getName(); $position = $parameter->getPosition(); @@ -90,10 +93,10 @@ public function getConstructorArguments(\ReflectionClass $class, $groupByPositio * Process argument type. * * @param \ReflectionClass $class - * @param \Zend\Code\Reflection\ParameterReflection $parameter + * @param \Laminas\Code\Reflection\ParameterReflection $parameter * @return string */ - private function processType(\ReflectionClass $class, \Zend\Code\Reflection\ParameterReflection $parameter) + private function processType(\ReflectionClass $class, \Laminas\Code\Reflection\ParameterReflection $parameter) { if ($parameter->getClass()) { return NamespaceResolver::NS_SEPARATOR . $parameter->getClass()->getName(); diff --git a/lib/internal/Magento/Framework/Code/Test/Unit/Generator/ClassGeneratorTest.php b/lib/internal/Magento/Framework/Code/Test/Unit/Generator/ClassGeneratorTest.php index 79d94b4d25a3d..3887744e45126 100644 --- a/lib/internal/Magento/Framework/Code/Test/Unit/Generator/ClassGeneratorTest.php +++ b/lib/internal/Magento/Framework/Code/Test/Unit/Generator/ClassGeneratorTest.php @@ -5,6 +5,9 @@ */ namespace Magento\Framework\Code\Test\Unit\Generator; +/** + * Test for Magento\Framework\Code\Generator\ClassGenerator + */ class ClassGeneratorTest extends \PHPUnit\Framework\TestCase { /**#@+ @@ -138,11 +141,11 @@ public function testSetClassDocBlock() /** * @param array $expectedDocBlock - * @param \Zend\Code\Generator\DocBlockGenerator $actualDocBlock + * @param \Laminas\Code\Generator\DocBlockGenerator $actualDocBlock */ protected function _assertDocBlockData( array $expectedDocBlock, - \Zend\Code\Generator\DocBlockGenerator $actualDocBlock + \Laminas\Code\Generator\DocBlockGenerator $actualDocBlock ) { // assert plain string data foreach ($expectedDocBlock as $propertyName => $propertyData) { @@ -156,7 +159,7 @@ protected function _assertDocBlockData( $expectedTagsData = $expectedDocBlock['tags']; $actualTags = $actualDocBlock->getTags(); $this->assertSameSize($expectedTagsData, $actualTags); - /** @var $actualTag \Zend\Code\Generator\DocBlock\Tag */ + /** @var $actualTag \Laminas\Code\Generator\DocBlock\Tag */ foreach ($actualTags as $actualTag) { $tagName = $actualTag->getName(); $this->assertArrayHasKey($tagName, $expectedTagsData); @@ -173,7 +176,7 @@ public function testAddMethods() $this->assertSameSize($this->_methodData, $actualMethods); - /** @var $method \Zend\Code\Generator\MethodGenerator */ + /** @var $method \Laminas\Code\Generator\MethodGenerator */ foreach ($actualMethods as $methodName => $method) { $this->assertArrayHasKey($methodName, $this->_methodData); $expectedMethodData = $this->_methodData[$methodName]; @@ -196,7 +199,7 @@ public function testAddMethods() foreach ($expectedMethodData['parameters'] as $parameterData) { $parameterName = $parameterData['name']; $this->assertArrayHasKey($parameterName, $actualParameters); - /** @var $actualParameter \Zend\Code\Generator\ParameterGenerator */ + /** @var $actualParameter \Laminas\Code\Generator\ParameterGenerator */ $actualParameter = $actualParameters[$parameterName]; $this->assertEquals($parameterName, $actualParameter->getName()); @@ -210,7 +213,7 @@ public function testAddMethods() // assert default value if (isset($parameterData['defaultValue'])) { - /** @var $actualDefaultValue \Zend\Code\Generator\ValueGenerator */ + /** @var $actualDefaultValue \Laminas\Code\Generator\ValueGenerator */ $actualDefaultValue = $actualParameter->getDefaultValue(); $this->assertEquals($parameterData['defaultValue'], $actualDefaultValue->getValue()); } @@ -242,11 +245,11 @@ protected function _assertFlag($flagType, array $expectedData, $actualObject) /** * @param array $expectedData - * @param \Zend\Code\Generator\AbstractMemberGenerator $actualObject + * @param \Laminas\Code\Generator\AbstractMemberGenerator $actualObject */ protected function _assertVisibility( array $expectedData, - \Zend\Code\Generator\AbstractMemberGenerator $actualObject + \Laminas\Code\Generator\AbstractMemberGenerator $actualObject ) { $expectedVisibility = isset($expectedData['visibility']) ? $expectedData['visibility'] : 'public'; $this->assertEquals($expectedVisibility, $actualObject->getVisibility()); @@ -260,7 +263,7 @@ protected function _assertVisibility( */ public function testAddMethodFromGenerator() { - $invalidMethod = new \Zend\Code\Generator\MethodGenerator(); + $invalidMethod = new \Laminas\Code\Generator\MethodGenerator(); $this->_model->addMethodFromGenerator($invalidMethod); } @@ -271,7 +274,7 @@ public function testAddProperties() $this->assertSameSize($this->_propertyData, $actualProperties); - /** @var $property \Zend\Code\Generator\PropertyGenerator */ + /** @var $property \Laminas\Code\Generator\PropertyGenerator */ foreach ($actualProperties as $propertyName => $property) { $this->assertArrayHasKey($propertyName, $this->_propertyData); $expectedPropertyData = $this->_propertyData[$propertyName]; @@ -287,7 +290,7 @@ public function testAddProperties() // assert default value if (isset($expectedPropertyData['defaultValue'])) { - /** @var $actualDefaultValue \Zend\Code\Generator\ValueGenerator */ + /** @var $actualDefaultValue \Laminas\Code\Generator\ValueGenerator */ $actualDefaultValue = $property->getDefaultValue(); $this->assertEquals($expectedPropertyData['defaultValue'], $actualDefaultValue->getValue()); } @@ -308,7 +311,7 @@ public function testAddProperties() */ public function testAddPropertyFromGenerator() { - $invalidProperty = new \Zend\Code\Generator\PropertyGenerator(); + $invalidProperty = new \Laminas\Code\Generator\PropertyGenerator(); $this->_model->addPropertyFromGenerator($invalidProperty); } @@ -333,9 +336,9 @@ public function testNamespaceName($actualNamespace, $expectedNamespace) public function providerNamespaces() { return [ - ['Zend', 'Zend'], - ['\Zend', 'Zend'], - ['\Zend\SomeClass', 'Zend\SomeClass'], + ['Laminas', 'Laminas'], + ['\Laminas', 'Laminas'], + ['\Laminas\SomeClass', 'Laminas\SomeClass'], ['', null], ]; } diff --git a/lib/internal/Magento/Framework/Code/Test/Unit/Generator/TestAsset/ParentClass.php b/lib/internal/Magento/Framework/Code/Test/Unit/Generator/TestAsset/ParentClass.php index 4565620a7557e..c5a7cd84686c9 100644 --- a/lib/internal/Magento/Framework/Code/Test/Unit/Generator/TestAsset/ParentClass.php +++ b/lib/internal/Magento/Framework/Code/Test/Unit/Generator/TestAsset/ParentClass.php @@ -5,7 +5,7 @@ */ namespace Magento\Framework\Code\Test\Unit\Generator\TestAsset; -use Zend\Code\Generator\DocBlockGenerator; +use Laminas\Code\Generator\DocBlockGenerator; /** * phpcs:ignoreFile @@ -15,7 +15,7 @@ class ParentClass /** * Public parent method * - * @param \Zend\Code\Generator\DocBlockGenerator $docBlockGenerator + * @param \Laminas\Code\Generator\DocBlockGenerator $docBlockGenerator * @param string $param1 * @param string $param2 * @param string $param3 @@ -35,7 +35,7 @@ public function publicParentMethod( /** * Protected parent method * - * @param \Zend\Code\Generator\DocBlockGenerator $docBlockGenerator + * @param \Laminas\Code\Generator\DocBlockGenerator $docBlockGenerator * @param string $param1 * @param string $param2 * @param string $param3 @@ -55,7 +55,7 @@ protected function _protectedParentMethod( /** * Private parent method * - * @param \Zend\Code\Generator\DocBlockGenerator $docBlockGenerator + * @param \Laminas\Code\Generator\DocBlockGenerator $docBlockGenerator * @param string $param1 * @param string $param2 * @param string $param3 diff --git a/lib/internal/Magento/Framework/Code/Test/Unit/Generator/TestAsset/SourceClass.php b/lib/internal/Magento/Framework/Code/Test/Unit/Generator/TestAsset/SourceClass.php index 5ba3031a2ae4d..08f781c01756a 100644 --- a/lib/internal/Magento/Framework/Code/Test/Unit/Generator/TestAsset/SourceClass.php +++ b/lib/internal/Magento/Framework/Code/Test/Unit/Generator/TestAsset/SourceClass.php @@ -5,7 +5,7 @@ */ namespace Magento\Framework\Code\Test\Unit\Generator\TestAsset; -use Zend\Code\Generator\ClassGenerator; +use Laminas\Code\Generator\ClassGenerator; /** * phpcs:ignoreFile @@ -15,7 +15,7 @@ class SourceClass extends ParentClass /** * Public child constructor * - * @param \Zend\Code\Generator\ClassGenerator $classGenerator + * @param \Laminas\Code\Generator\ClassGenerator $classGenerator * @param string $param1 * @param string $param2 * @param string $param3 @@ -35,7 +35,7 @@ public function __construct( /** * Public child method * - * @param \Zend\Code\Generator\ClassGenerator $classGenerator + * @param \Laminas\Code\Generator\ClassGenerator $classGenerator * @param string $param1 * @param string $param2 * @param string $param3 @@ -58,7 +58,7 @@ public function publicChildMethod( /** * Public child method with reference * - * @param \Zend\Code\Generator\ClassGenerator $classGenerator + * @param \Laminas\Code\Generator\ClassGenerator $classGenerator * @param array $array * * @SuppressWarnings(PHPMD.UnusedFormalParameter) @@ -70,7 +70,7 @@ public function publicMethodWithReference(ClassGenerator &$classGenerator, array /** * Protected child method * - * @param \Zend\Code\Generator\ClassGenerator $classGenerator + * @param \Laminas\Code\Generator\ClassGenerator $classGenerator * @param string $param1 * @param string $param2 * @param string $param3 @@ -88,7 +88,7 @@ protected function _protectedChildMethod( /** * Private child method * - * @param \Zend\Code\Generator\ClassGenerator $classGenerator + * @param \Laminas\Code\Generator\ClassGenerator $classGenerator * @param string $param1 * @param string $param2 * @param string $param3 diff --git a/lib/internal/Magento/Framework/Component/ComponentRegistrar.php b/lib/internal/Magento/Framework/Component/ComponentRegistrar.php index 0a54d770300e8..bd20582875ce0 100644 --- a/lib/internal/Magento/Framework/Component/ComponentRegistrar.php +++ b/lib/internal/Magento/Framework/Component/ComponentRegistrar.php @@ -8,8 +8,6 @@ /** * Provides ability to statically register components. * - * @author Josh Di Fabio <joshdifabio@gmail.com> - * * @api */ class ComponentRegistrar implements ComponentRegistrarInterface @@ -50,13 +48,12 @@ public static function register($type, $componentName, $path) ucfirst($type) . ' \'' . $componentName . '\' from \'' . $path . '\' ' . 'has been already defined in \'' . self::$paths[$type][$componentName] . '\'.' ); - } else { - self::$paths[$type][$componentName] = str_replace('\\', '/', $path); } + self::$paths[$type][$componentName] = str_replace('\\', '/', $path); } /** - * {@inheritdoc} + * @inheritdoc */ public function getPaths($type) { @@ -65,7 +62,7 @@ public function getPaths($type) } /** - * {@inheritdoc} + * @inheritdoc */ public function getPath($type, $componentName) { diff --git a/lib/internal/Magento/Framework/Component/ComponentRegistrarInterface.php b/lib/internal/Magento/Framework/Component/ComponentRegistrarInterface.php index 04d7676eff3c0..eb7144306b593 100644 --- a/lib/internal/Magento/Framework/Component/ComponentRegistrarInterface.php +++ b/lib/internal/Magento/Framework/Component/ComponentRegistrarInterface.php @@ -5,9 +5,6 @@ */ namespace Magento\Framework\Component; -/** - * @author Josh Di Fabio <joshdifabio@gmail.com> - */ interface ComponentRegistrarInterface { /** diff --git a/lib/internal/Magento/Framework/Console/GenerationDirectoryAccess.php b/lib/internal/Magento/Framework/Console/GenerationDirectoryAccess.php index b8978c9ef7181..9ea37b0d0f6bd 100644 --- a/lib/internal/Magento/Framework/Console/GenerationDirectoryAccess.php +++ b/lib/internal/Magento/Framework/Console/GenerationDirectoryAccess.php @@ -9,7 +9,7 @@ use Magento\Framework\App\Filesystem\DirectoryList; use Magento\Framework\Filesystem\Directory\WriteFactory; use Magento\Framework\Filesystem\DriverPool; -use Zend\ServiceManager\ServiceManager; +use Laminas\ServiceManager\ServiceManager; use Magento\Setup\Mvc\Bootstrap\InitParamListener; /** diff --git a/lib/internal/Magento/Framework/Crontab/CrontabManager.php b/lib/internal/Magento/Framework/Crontab/CrontabManager.php index fb3537ca0648f..a6496e4d01c04 100644 --- a/lib/internal/Magento/Framework/Crontab/CrontabManager.php +++ b/lib/internal/Magento/Framework/Crontab/CrontabManager.php @@ -203,7 +203,7 @@ private function getCrontabContent() */ private function save($content) { - $content = str_replace(['%', '"'], ['%%', '\"'], $content); + $content = str_replace(['%', '"', '$'], ['%%', '\"', '\$'], $content); try { $this->shell->execute('echo "' . $content . '" | crontab -'); diff --git a/lib/internal/Magento/Framework/Crontab/Test/Unit/CrontabManagerTest.php b/lib/internal/Magento/Framework/Crontab/Test/Unit/CrontabManagerTest.php index f6c863d9d9fad..4ef00fbb65576 100644 --- a/lib/internal/Magento/Framework/Crontab/Test/Unit/CrontabManagerTest.php +++ b/lib/internal/Magento/Framework/Crontab/Test/Unit/CrontabManagerTest.php @@ -3,32 +3,34 @@ * Copyright © Magento, Inc. All rights reserved. * See COPYING.txt for license details. */ - +declare(strict_types=1); namespace Magento\Framework\Crontab\Test\Unit; +use Magento\Framework\App\Filesystem\DirectoryList; use Magento\Framework\Crontab\CrontabManager; use Magento\Framework\Crontab\CrontabManagerInterface; -use Magento\Framework\ShellInterface; -use Magento\Framework\Phrase; use Magento\Framework\Exception\LocalizedException; use Magento\Framework\Filesystem; -use Magento\Framework\App\Filesystem\DirectoryList; use Magento\Framework\Filesystem\Directory\ReadInterface; use Magento\Framework\Filesystem\DriverPool; +use Magento\Framework\Phrase; +use Magento\Framework\ShellInterface; +use PHPUnit\Framework\TestCase; +use PHPUnit_Framework_MockObject_MockObject as MockObject; /** * Tests crontab manager functionality. */ -class CrontabManagerTest extends \PHPUnit\Framework\TestCase +class CrontabManagerTest extends TestCase { /** - * @var ShellInterface|\PHPUnit_Framework_MockObject_MockObject + * @var ShellInterface|MockObject */ private $shellMock; /** - * @var Filesystem|\PHPUnit_Framework_MockObject_MockObject + * @var Filesystem|MockObject */ private $filesystemMock; @@ -38,7 +40,7 @@ class CrontabManagerTest extends \PHPUnit\Framework\TestCase private $crontabManager; /** - * @return void + * @inheritDoc */ protected function setUp() { @@ -53,9 +55,11 @@ protected function setUp() } /** + * Verify get tasks without cronetab. + * * @return void */ - public function testGetTasksNoCrontab() + public function testGetTasksNoCrontab(): void { $exception = new \Exception('crontab: no crontab for user'); $localizedException = new LocalizedException(new Phrase('Some error'), $exception); @@ -69,12 +73,14 @@ public function testGetTasksNoCrontab() } /** + * Verify get tasks. + * * @param string $content * @param array $tasks * @return void * @dataProvider getTasksDataProvider */ - public function testGetTasks($content, $tasks) + public function testGetTasks($content, $tasks): void { $this->shellMock->expects($this->once()) ->method('execute') @@ -85,9 +91,11 @@ public function testGetTasks($content, $tasks) } /** + * Data provider to get tasks. + * * @return array */ - public function getTasksDataProvider() + public function getTasksDataProvider(): array { return [ [ @@ -120,11 +128,13 @@ public function getTasksDataProvider() } /** + * Verify remove tasks with exception. + * * @return void * @expectedException \Magento\Framework\Exception\LocalizedException * @expectedExceptionMessage Shell error */ - public function testRemoveTasksWithException() + public function testRemoveTasksWithException(): void { $exception = new \Exception('Shell error'); $localizedException = new LocalizedException(new Phrase('Some error'), $exception); @@ -143,12 +153,14 @@ public function testRemoveTasksWithException() } /** + * Verify remove tasks. + * * @param string $contentBefore * @param string $contentAfter * @return void * @dataProvider removeTasksDataProvider */ - public function testRemoveTasks($contentBefore, $contentAfter) + public function testRemoveTasks($contentBefore, $contentAfter): void { $this->shellMock->expects($this->at(0)) ->method('execute') @@ -163,9 +175,11 @@ public function testRemoveTasks($contentBefore, $contentAfter) } /** + * Data provider to remove tasks. + * * @return array */ - public function removeTasksDataProvider() + public function removeTasksDataProvider(): array { return [ [ @@ -195,11 +209,13 @@ public function removeTasksDataProvider() } /** + * Verify save tasks with empty tasks list. + * * @return void * @expectedException \Magento\Framework\Exception\LocalizedException * @expectedExceptionMessage The list of tasks is empty. Add tasks and try again. */ - public function testSaveTasksWithEmptyTasksList() + public function testSaveTasksWithEmptyTasksList(): void { $baseDirMock = $this->getMockBuilder(ReadInterface::class) ->getMockForAbstractClass(); @@ -222,11 +238,13 @@ public function testSaveTasksWithEmptyTasksList() } /** + * Verify save tasks with out command. + * * @return void * @expectedException \Magento\Framework\Exception\LocalizedException * @expectedExceptionMessage The command shouldn't be empty. Enter and try again. */ - public function testSaveTasksWithoutCommand() + public function testSaveTasksWithoutCommand(): void { $baseDirMock = $this->getMockBuilder(ReadInterface::class) ->getMockForAbstractClass(); @@ -252,13 +270,15 @@ public function testSaveTasksWithoutCommand() } /** + * Verify sava task. + * * @param array $tasks * @param string $content * @param string $contentToSave * @return void * @dataProvider saveTasksDataProvider */ - public function testSaveTasks($tasks, $content, $contentToSave) + public function testSaveTasks($tasks, $content, $contentToSave): void { $baseDirMock = $this->getMockBuilder(ReadInterface::class) ->getMockForAbstractClass(); @@ -291,9 +311,11 @@ public function testSaveTasks($tasks, $content, $contentToSave) } /** + * Data provider to save tasks. + * * @return array */ - public function saveTasksDataProvider() + public function saveTasksDataProvider(): array { $content = '* * * * * /bin/php /var/www/cron.php' . PHP_EOL . CrontabManagerInterface::TASKS_BLOCK_START . ' ' . hash("sha256", BP) . PHP_EOL @@ -354,6 +376,17 @@ public function saveTasksDataProvider() . ' %% cron:run | grep -v \"Ran \'jobs\' by schedule\"' . PHP_EOL . CrontabManagerInterface::TASKS_BLOCK_END . ' ' . hash("sha256", BP) . PHP_EOL, ], + [ + 'tasks' => [ + ['command' => '{magentoRoot}run.php mysqldump db > db-$(date +%F).sql'] + ], + 'content' => '* * * * * /bin/php /var/www/cron.php', + 'contentToSave' => '* * * * * /bin/php /var/www/cron.php' . PHP_EOL + . CrontabManagerInterface::TASKS_BLOCK_START . ' ' . hash("sha256", BP) . PHP_EOL + . '* * * * * ' . PHP_BINARY . ' /var/www/magento2/run.php' + . ' mysqldump db > db-\$(date +%%F).sql' . PHP_EOL + . CrontabManagerInterface::TASKS_BLOCK_END . ' ' . hash("sha256", BP) . PHP_EOL, + ], ]; } } diff --git a/lib/internal/Magento/Framework/DB/Adapter/Pdo/Mysql.php b/lib/internal/Magento/Framework/DB/Adapter/Pdo/Mysql.php index 058bf18b16d3c..ec2731c667ee6 100644 --- a/lib/internal/Magento/Framework/DB/Adapter/Pdo/Mysql.php +++ b/lib/internal/Magento/Framework/DB/Adapter/Pdo/Mysql.php @@ -1827,6 +1827,7 @@ public function modifyColumnByDdl($tableName, $columnName, $definition, $flushDa */ protected function _getColumnTypeByDdl($column) { + // phpstan:ignore switch ($column['DATA_TYPE']) { case 'bool': return Table::TYPE_BOOLEAN; @@ -1957,7 +1958,7 @@ public function insertOnDuplicate($table, array $data, array $fields = []) $field = $this->quoteIdentifier($k); if ($v instanceof \Zend_Db_Expr) { $value = $v->__toString(); - } elseif ($v instanceof \Zend\Db\Sql\Expression) { + } elseif ($v instanceof \Laminas\Db\Sql\Expression) { $value = $v->getExpression(); } elseif (is_string($v)) { $value = sprintf('VALUES(%s)', $this->quoteIdentifier($v)); @@ -2732,6 +2733,7 @@ public function addIndex( } catch (\Exception $e) { if (in_array(strtolower($indexType), ['primary', 'unique'])) { $match = []; + // phpstan:ignore if (preg_match('#SQLSTATE\[23000\]: [^:]+: 1062[^\']+\'([\d-\.]+)\'#', $e->getMessage(), $match)) { $ids = explode('-', $match[1]); $this->_removeDuplicateEntry($tableName, $fields, $ids); diff --git a/lib/internal/Magento/Framework/DB/Select.php b/lib/internal/Magento/Framework/DB/Select.php index ceeed40787123..075aa6b24faa7 100644 --- a/lib/internal/Magento/Framework/DB/Select.php +++ b/lib/internal/Magento/Framework/DB/Select.php @@ -91,6 +91,12 @@ public function __construct( * $select->where('id = :id'); * </code> * + * You may also construct IN statements: + * + * <code> + * $select->where('entity_id IN (?)', ['1', '2', '3']); + * </code> + * * Note that it is more correct to use named bindings in your * queries for values other than strings. When you use named * bindings, don't forget to pass the values when actually @@ -101,7 +107,7 @@ public function __construct( * </code> * * @param string $cond The WHERE condition. - * @param string $value OPTIONAL A single value to quote into the condition. + * @param string|array|null $value OPTIONAL An optional single or array value to quote into the condition. * @param string|int|null $type OPTIONAL The type of the given value * @return \Magento\Framework\DB\Select */ diff --git a/lib/internal/Magento/Framework/DB/Select/SelectRenderer.php b/lib/internal/Magento/Framework/DB/Select/SelectRenderer.php index 11dbaeb82317a..7c239913987a7 100644 --- a/lib/internal/Magento/Framework/DB/Select/SelectRenderer.php +++ b/lib/internal/Magento/Framework/DB/Select/SelectRenderer.php @@ -8,10 +8,15 @@ use Magento\Framework\DB\Select; /** - * Class SelectRenderer + * Phrase renderer interface */ class SelectRenderer implements RendererInterface { + private const MANDATORY_SELECT_PARTS = [ + Select::COLUMNS => true, + Select::FROM => true + ]; + /** * @var RendererInterface[] */ @@ -67,7 +72,8 @@ public function render(Select $select, $sql = '') { $sql = Select::SQL_SELECT; foreach ($this->renderers as $renderer) { - if (in_array($renderer['part'], [Select::COLUMNS, Select::FROM]) || $select->getPart($renderer['part'])) { + $part = $renderer['part']; + if (isset(self::MANDATORY_SELECT_PARTS[$part]) || $select->getPart($part)) { $sql = $renderer['renderer']->render($select, $sql); } } diff --git a/lib/internal/Magento/Framework/Data/Collection.php b/lib/internal/Magento/Framework/Data/Collection.php index c497c4de5a5e4..7c9cf02ac6a47 100644 --- a/lib/internal/Magento/Framework/Data/Collection.php +++ b/lib/internal/Magento/Framework/Data/Collection.php @@ -6,7 +6,10 @@ namespace Magento\Framework\Data; +use Magento\Framework\App\ObjectManager; use Magento\Framework\Data\Collection\EntityFactoryInterface; +use Magento\Framework\DataObject; +use Magento\Framework\Exception\AlreadyExistsException; use Magento\Framework\Option\ArrayInterface; /** @@ -25,7 +28,7 @@ class Collection implements \IteratorAggregate, \Countable, ArrayInterface, Coll /** * Collection items * - * @var \Magento\Framework\DataObject[] + * @var DataObject[] */ protected $_items = []; @@ -34,7 +37,7 @@ class Collection implements \IteratorAggregate, \Countable, ArrayInterface, Coll * * @var string */ - protected $_itemObjectClass = \Magento\Framework\DataObject::class; + protected $_itemObjectClass = DataObject::class; /** * Order configuration @@ -46,7 +49,7 @@ class Collection implements \IteratorAggregate, \Countable, ArrayInterface, Coll /** * Filters configuration * - * @var \Magento\Framework\DataObject[] + * @var DataObject[] */ protected $_filters = []; @@ -117,7 +120,7 @@ public function __construct(EntityFactoryInterface $entityFactory) */ public function addFilter($field, $value, $type = 'and') { - $filter = new \Magento\Framework\DataObject(); + $filter = new DataObject(); // implements ArrayAccess $filter['field'] = $field; $filter['value'] = $value; @@ -163,9 +166,9 @@ public function addFilter($field, $value, $type = 'and') * * @param string|array $field * @param string|int|array $condition - * @throws \Magento\Framework\Exception\LocalizedException if some error in the input could be detected. * @return $this * @SuppressWarnings(PHPMD.UnusedFormalParameter) + * @throws \Magento\Framework\Exception\LocalizedException if some error in the input could be detected. */ public function addFieldToFilter($field, $condition) { @@ -182,7 +185,7 @@ public function addFieldToFilter($field, $condition) * - array() -- get all filters * * @param string|string[] $field - * @return \Magento\Framework\DataObject|\Magento\Framework\DataObject[]|void + * @return DataObject|DataObject[]|void */ public function getFilter($field) { @@ -234,18 +237,16 @@ protected function _setIsLoaded($flag = true) /** * Get current collection page * - * @param int $displacement + * @param int $displacement * @return int */ public function getCurPage($displacement = 0) { if ($this->_curPage + $displacement < 1) { return 1; - } elseif ($this->_curPage + $displacement > $this->getLastPageNumber()) { - return $this->getLastPageNumber(); - } else { - return $this->_curPage + $displacement; } + + return $this->_curPage + $displacement; } /** @@ -260,9 +261,9 @@ public function getLastPageNumber() return 1; } elseif ($this->_pageSize) { return (int)ceil($collectionSize / $this->_pageSize); - } else { - return 1; } + + return 1; } /** @@ -292,7 +293,7 @@ public function getSize() /** * Retrieve collection first item * - * @return \Magento\Framework\DataObject + * @return DataObject */ public function getFirstItem() { @@ -309,7 +310,7 @@ public function getFirstItem() /** * Retrieve collection last item * - * @return \Magento\Framework\DataObject + * @return DataObject */ public function getLastItem() { @@ -325,7 +326,7 @@ public function getLastItem() /** * Retrieve collection items * - * @return \Magento\Framework\DataObject[] + * @return DataObject[] */ public function getItems() { @@ -336,8 +337,8 @@ public function getItems() /** * Retrieve field values from all items * - * @param string $colName - * @return array + * @param string $colName + * @return array */ public function getColumnValues($colName) { @@ -353,9 +354,9 @@ public function getColumnValues($colName) /** * Search all items by field value * - * @param string $column - * @param mixed $value - * @return array + * @param string $column + * @param array $value + * @return array */ public function getItemsByColumnValue($column, $value) { @@ -373,9 +374,9 @@ public function getItemsByColumnValue($column, $value) /** * Search first item by field value * - * @param string $column - * @param mixed $value - * @return \Magento\Framework\DataObject || null + * @param string $column + * @param string|int $value + * @return DataObject|null */ public function getItemByColumnValue($column, $value) { @@ -392,11 +393,11 @@ public function getItemByColumnValue($column, $value) /** * Adding item to item array * - * @param \Magento\Framework\DataObject $item + * @param DataObject $item * @return $this * @throws \Exception */ - public function addItem(\Magento\Framework\DataObject $item) + public function addItem(DataObject $item) { $itemId = $this->_getItemId($item); @@ -417,7 +418,7 @@ public function addItem(\Magento\Framework\DataObject $item) /** * Add item that has no id to collection * - * @param \Magento\Framework\DataObject $item + * @param DataObject $item * @return $this */ protected function _addItem($item) @@ -429,10 +430,10 @@ protected function _addItem($item) /** * Retrieve item id * - * @param \Magento\Framework\DataObject $item - * @return mixed + * @param DataObject $item + * @return string|int */ - protected function _getItemId(\Magento\Framework\DataObject $item) + protected function _getItemId(DataObject $item) { return $item->getId(); } @@ -454,7 +455,7 @@ public function getAllIds() /** * Remove item from collection by item key * - * @param mixed $key + * @param string $key * @return $this */ public function removeItemByKey($key) @@ -542,8 +543,8 @@ public function each($objMethod, $args = []) /** * Setting data for all collection items * - * @param mixed $key - * @param mixed $value + * @param string $key + * @param string|int|null $value * @return $this */ public function setDataToAll($key, $value = null) @@ -606,7 +607,7 @@ public function setOrder($field, $direction = self::SORT_ORDER_DESC) */ public function setItemObjectClass($className) { - if (!is_a($className, \Magento\Framework\DataObject::class, true)) { + if (!is_a($className, DataObject::class, true)) { throw new \InvalidArgumentException($className . ' does not extend \Magento\Framework\DataObject'); } $this->_itemObjectClass = $className; @@ -616,7 +617,7 @@ public function setItemObjectClass($className) /** * Retrieve collection empty item * - * @return \Magento\Framework\DataObject + * @return DataObject */ public function getNewEmptyItem() { @@ -748,8 +749,8 @@ public function toArray($arrRequiredFields = []) * Return items array * array( * $index => array( - * 'value' => mixed - * 'label' => mixed + * 'value' => string + * 'label' => string * ) * ) * @@ -815,8 +816,8 @@ protected function _toOptionHash($valueField = 'id', $labelField = 'name') /** * Retrieve item by id * - * @param mixed $idValue - * @return \Magento\Framework\DataObject + * @param string|int $idValue + * @return DataObject */ public function getItemById($idValue) { @@ -910,7 +911,7 @@ public function __sleep() */ public function __wakeup() { - $objectManager = \Magento\Framework\App\ObjectManager::getInstance(); + $objectManager = ObjectManager::getInstance(); $this->_entityFactory = $objectManager->get(EntityFactoryInterface::class); } } diff --git a/lib/internal/Magento/Framework/Data/Test/Unit/Form/FormKeyTest.php b/lib/internal/Magento/Framework/Data/Test/Unit/Form/FormKeyTest.php index eb642278694c0..7c9682ea444d8 100644 --- a/lib/internal/Magento/Framework/Data/Test/Unit/Form/FormKeyTest.php +++ b/lib/internal/Magento/Framework/Data/Test/Unit/Form/FormKeyTest.php @@ -7,11 +7,12 @@ namespace Magento\Framework\Data\Test\Unit\Form; use Magento\Framework\Data\Form\FormKey; +use Magento\Framework\Escaper; use Magento\Framework\Math\Random; use Magento\Framework\Session\SessionManager; /** - * Class FormKeyTest + * Test for Magento\Framework\Data\Form\FormKey */ class FormKeyTest extends \PHPUnit\Framework\TestCase { @@ -26,7 +27,7 @@ class FormKeyTest extends \PHPUnit\Framework\TestCase protected $sessionMock; /** - * @var \Zend\Escaper\Escaper|\PHPUnit_Framework_MockObject_MockObject + * @var Escaper|\PHPUnit_Framework_MockObject_MockObject */ protected $escaperMock; @@ -40,7 +41,7 @@ protected function setUp() $this->mathRandomMock = $this->createMock(\Magento\Framework\Math\Random::class); $methods = ['setData', 'getData']; $this->sessionMock = $this->createPartialMock(\Magento\Framework\Session\SessionManager::class, $methods); - $this->escaperMock = $this->createMock(\Magento\Framework\Escaper::class); + $this->escaperMock = $this->createMock(Escaper::class); $this->escaperMock->expects($this->any())->method('escapeJs')->willReturnArgument(0); $this->formKey = new FormKey( $this->mathRandomMock, diff --git a/lib/internal/Magento/Framework/DataObject/IdentityValidator.php b/lib/internal/Magento/Framework/DataObject/IdentityValidator.php new file mode 100644 index 0000000000000..e8c068a093cb5 --- /dev/null +++ b/lib/internal/Magento/Framework/DataObject/IdentityValidator.php @@ -0,0 +1,27 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +declare(strict_types=1); + +namespace Magento\Framework\DataObject; + +use Ramsey\Uuid\Uuid; + +/** + * Class IdentityValidator + * + * Class for validating Uuid's + */ +class IdentityValidator implements IdentityValidatorInterface +{ + /** + * @inheritDoc + */ + public function isValid(string $value): bool + { + $isValid = Uuid::isValid($value); + return $isValid; + } +} diff --git a/lib/internal/Magento/Framework/DataObject/IdentityValidatorInterface.php b/lib/internal/Magento/Framework/DataObject/IdentityValidatorInterface.php new file mode 100644 index 0000000000000..a1979721f0cc9 --- /dev/null +++ b/lib/internal/Magento/Framework/DataObject/IdentityValidatorInterface.php @@ -0,0 +1,21 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +namespace Magento\Framework\DataObject; + +/** + * Interface IdentityValidatorInterface + */ +interface IdentityValidatorInterface +{ + /** + * Checks if uuid is valid + * + * @param string $value + * + * @return bool + */ + public function isValid(string $value): bool; +} diff --git a/lib/internal/Magento/Framework/Encryption/Helper/Security.php b/lib/internal/Magento/Framework/Encryption/Helper/Security.php index 63884b5c7fb3e..0320468b35f02 100644 --- a/lib/internal/Magento/Framework/Encryption/Helper/Security.php +++ b/lib/internal/Magento/Framework/Encryption/Helper/Security.php @@ -6,10 +6,10 @@ namespace Magento\Framework\Encryption\Helper; -use Zend\Crypt\Utils; +use Laminas\Crypt\Utils; /** - * Class implements compareString from Zend\Crypt + * Class implements compareString from Laminas\Crypt * * @api */ diff --git a/lib/internal/Magento/Framework/Escaper.php b/lib/internal/Magento/Framework/Escaper.php index 1b766dea2105c..dd7a780af09fe 100644 --- a/lib/internal/Magento/Framework/Escaper.php +++ b/lib/internal/Magento/Framework/Escaper.php @@ -310,9 +310,9 @@ public function escapeCss($string) } /** - * Escape quotes in java script + * Escape single quotes/apostrophes ('), or other specified $quote character in javascript * - * @param string|array $data + * @param string|string[]|array $data * @param string $quote * @return string|array * @deprecated 100.2.0 diff --git a/lib/internal/Magento/Framework/File/Mime.php b/lib/internal/Magento/Framework/File/Mime.php index ed370b1beae54..148f43d47cfd4 100644 --- a/lib/internal/Magento/Framework/File/Mime.php +++ b/lib/internal/Magento/Framework/File/Mime.php @@ -78,6 +78,18 @@ class Mime 'svg' => 'image/svg+xml', ]; + /** + * List of generic MIME types + * + * The file mime type should be detected by the file's extension if the native mime type is one of the listed below. + * + * @var array + */ + private $genericMimeTypes = [ + 'application/x-empty', + 'inode/x-empty', + ]; + /** * Get mime type of a file * @@ -120,7 +132,11 @@ private function getNativeMimeType(string $file): string $extension = $this->getFileExtension($file); $result = mime_content_type($file); if (isset($this->mimeTypes[$extension], $this->defineByExtensionList[$extension]) - && (strpos($result, 'text/') === 0 || strpos($result, 'image/svg') === 0) + && ( + strpos($result, 'text/') === 0 + || strpos($result, 'image/svg') === 0 + || in_array($result, $this->genericMimeTypes, true) + ) ) { $result = $this->mimeTypes[$extension]; } diff --git a/lib/internal/Magento/Framework/File/Test/Unit/MimeTest.php b/lib/internal/Magento/Framework/File/Test/Unit/MimeTest.php index 1a964c141dd34..0ae1542a2c0e1 100644 --- a/lib/internal/Magento/Framework/File/Test/Unit/MimeTest.php +++ b/lib/internal/Magento/Framework/File/Test/Unit/MimeTest.php @@ -5,6 +5,9 @@ */ namespace Magento\Framework\File\Test\Unit; +/** + * Test mime type utility for correct + */ class MimeTest extends \PHPUnit\Framework\TestCase { /** @@ -12,6 +15,9 @@ class MimeTest extends \PHPUnit\Framework\TestCase */ private $object; + /** + * @inheritDoc + */ protected function setUp() { $this->object = new \Magento\Framework\File\Mime(); @@ -42,12 +48,13 @@ public function testGetMimeType($file, $expectedType) /** * @return array */ - public function getMimeTypeDataProvider() + public function getMimeTypeDataProvider(): array { return [ 'javascript' => [__DIR__ . '/_files/javascript.js', 'application/javascript'], 'weird extension' => [__DIR__ . '/_files/file.weird', 'application/octet-stream'], 'weird uppercase extension' => [__DIR__ . '/_files/UPPERCASE.WEIRD', 'application/octet-stream'], + 'generic mime type' => [__DIR__ . '/_files/blank.html', 'text/html'], ]; } } diff --git a/lib/internal/Magento/Framework/File/Test/Unit/Transfer/Adapter/HttpTest.php b/lib/internal/Magento/Framework/File/Test/Unit/Transfer/Adapter/HttpTest.php index 023c4cc4ddba6..65fde6f27fd8a 100644 --- a/lib/internal/Magento/Framework/File/Test/Unit/Transfer/Adapter/HttpTest.php +++ b/lib/internal/Magento/Framework/File/Test/Unit/Transfer/Adapter/HttpTest.php @@ -90,7 +90,7 @@ public function testSendWithOptions(): void $file = __DIR__ . '/../../_files/javascript.js'; $contentType = 'content/type'; - $headers = $this->getMockBuilder(\Zend\Http\Headers::class)->getMock(); + $headers = $this->getMockBuilder(\Laminas\Http\Headers::class)->getMock(); $this->response->expects($this->atLeastOnce()) ->method('setHeader') ->withConsecutive(['Content-length', filesize($file)], ['Content-Type', $contentType]); diff --git a/lib/internal/Magento/Framework/File/Test/Unit/_files/blank.html b/lib/internal/Magento/Framework/File/Test/Unit/_files/blank.html new file mode 100644 index 0000000000000..e69de29bb2d1d diff --git a/lib/internal/Magento/Framework/File/Transfer/Adapter/Http.php b/lib/internal/Magento/Framework/File/Transfer/Adapter/Http.php index cd42c8d04b477..07fc73eacf282 100644 --- a/lib/internal/Magento/Framework/File/Transfer/Adapter/Http.php +++ b/lib/internal/Magento/Framework/File/Transfer/Adapter/Http.php @@ -10,7 +10,7 @@ use Magento\Framework\File\Mime; use Magento\Framework\App\Request\Http as HttpRequest; use Magento\Framework\App\ObjectManager; -use Zend\Http\Headers; +use Laminas\Http\Headers; /** * File adapter to send the file to the client. diff --git a/lib/internal/Magento/Framework/Filesystem/Directory/Read.php b/lib/internal/Magento/Framework/Filesystem/Directory/Read.php index a3a4cec59953f..e23eadd57d866 100644 --- a/lib/internal/Magento/Framework/Filesystem/Directory/Read.php +++ b/lib/internal/Magento/Framework/Filesystem/Directory/Read.php @@ -9,6 +9,7 @@ use Magento\Framework\Exception\ValidatorException; /** + * Filesystem directory instance for read operations * @api */ class Read implements ReadInterface @@ -40,8 +41,6 @@ class Read implements ReadInterface private $pathValidator; /** - * Constructor. Set properties. - * * @param \Magento\Framework\Filesystem\File\ReadFactory $fileFactory * @param \Magento\Framework\Filesystem\DriverInterface $driver * @param string $path @@ -60,6 +59,8 @@ public function __construct( } /** + * Validate the path is correct and within the directory + * * @param null|string $path * @param null|string $scheme * @param bool $absolutePath @@ -96,8 +97,7 @@ protected function setPath($path) } /** - * Retrieves absolute path - * E.g.: /var/www/application/file.txt + * Retrieves absolute path i.e. /var/www/application/file.txt * * @param string $path * @param string $scheme @@ -151,7 +151,7 @@ public function read($path = null) /** * Read recursively * - * @param null $path + * @param string|null $path * @throws ValidatorException * @return string[] */ @@ -207,7 +207,9 @@ public function isExist($path = null) { $this->validatePath($path); - return $this->driver->isExists($this->driver->getAbsolutePath($this->path, $path)); + return $this->driver->isExists( + $this->driver->getRealPathSafety($this->driver->getAbsolutePath($this->path, $path)) + ); } /** diff --git a/lib/internal/Magento/Framework/Filesystem/Directory/Write.php b/lib/internal/Magento/Framework/Filesystem/Directory/Write.php index f23ed87971a39..484eed347be0f 100644 --- a/lib/internal/Magento/Framework/Filesystem/Directory/Write.php +++ b/lib/internal/Magento/Framework/Filesystem/Directory/Write.php @@ -67,8 +67,8 @@ protected function assertWritable($path) */ protected function assertIsFile($path) { - clearstatcache(); $absolutePath = $this->driver->getAbsolutePath($this->path, $path); + clearstatcache(true, $absolutePath); if (!$this->driver->isFile($absolutePath)) { throw new FileSystemException( new \Magento\Framework\Phrase('The "%1" file doesn\'t exist.', [$absolutePath]) diff --git a/lib/internal/Magento/Framework/Filesystem/Driver/File.php b/lib/internal/Magento/Framework/Filesystem/Driver/File.php index 59c9775d73a0a..aec9a64a132fe 100644 --- a/lib/internal/Magento/Framework/Filesystem/Driver/File.php +++ b/lib/internal/Magento/Framework/Filesystem/Driver/File.php @@ -11,11 +11,13 @@ use Magento\Framework\Exception\FileSystemException; use Magento\Framework\Filesystem\DriverInterface; use Magento\Framework\Filesystem\Glob; +use Magento\Framework\Phrase; /** - * Class File + * Filesystem driver that uses the local filesystem. + * + * Assumed that stat cache is cleanup before test filesystem * - * @package Magento\Framework\Filesystem\Driver * @SuppressWarnings(PHPMD.ExcessiveClassComplexity) */ class File implements DriverInterface @@ -48,11 +50,12 @@ protected function getWarningMessage() */ public function isExists($path) { - clearstatcache(); - $result = @file_exists($this->getScheme() . $path); + $filename = $this->getScheme() . $path; + clearstatcache(false, $filename); + $result = @file_exists($filename); if ($result === null) { throw new FileSystemException( - new \Magento\Framework\Phrase('An error occurred during "%1" execution.', [$this->getWarningMessage()]) + new Phrase('An error occurred during "%1" execution.', [$this->getWarningMessage()]) ); } return $result; @@ -67,11 +70,12 @@ public function isExists($path) */ public function stat($path) { - clearstatcache(); - $result = @stat($this->getScheme() . $path); + $filename = $this->getScheme() . $path; + clearstatcache(false, $filename); + $result = @stat($filename); if (!$result) { throw new FileSystemException( - new \Magento\Framework\Phrase('Cannot gather stats! %1', [$this->getWarningMessage()]) + new Phrase('Cannot gather stats! %1', [$this->getWarningMessage()]) ); } return $result; @@ -86,11 +90,12 @@ public function stat($path) */ public function isReadable($path) { - clearstatcache(); - $result = @is_readable($this->getScheme() . $path); + $filename = $this->getScheme() . $path; + clearstatcache(false, $filename); + $result = @is_readable($filename); if ($result === null) { throw new FileSystemException( - new \Magento\Framework\Phrase('An error occurred during "%1" execution.', [$this->getWarningMessage()]) + new Phrase('An error occurred during "%1" execution.', [$this->getWarningMessage()]) ); } return $result; @@ -105,11 +110,12 @@ public function isReadable($path) */ public function isFile($path) { - clearstatcache(); - $result = @is_file($this->getScheme() . $path); + $filename = $this->getScheme() . $path; + clearstatcache(false, $filename); + $result = @is_file($filename); if ($result === null) { throw new FileSystemException( - new \Magento\Framework\Phrase('An error occurred during "%1" execution.', [$this->getWarningMessage()]) + new Phrase('An error occurred during "%1" execution.', [$this->getWarningMessage()]) ); } return $result; @@ -124,11 +130,12 @@ public function isFile($path) */ public function isDirectory($path) { - clearstatcache(); - $result = @is_dir($this->getScheme() . $path); + $filename = $this->getScheme() . $path; + clearstatcache(false, $filename); + $result = @is_dir($filename); if ($result === null) { throw new FileSystemException( - new \Magento\Framework\Phrase('An error occurred during "%1" execution.', [$this->getWarningMessage()]) + new Phrase('An error occurred during "%1" execution.', [$this->getWarningMessage()]) ); } return $result; @@ -145,11 +152,12 @@ public function isDirectory($path) */ public function fileGetContents($path, $flag = null, $context = null) { - clearstatcache(); - $result = @file_get_contents($this->getScheme() . $path, $flag, $context); + $filename = $this->getScheme() . $path; + clearstatcache(false, $filename); + $result = @file_get_contents($filename, $flag, $context); if (false === $result) { throw new FileSystemException( - new \Magento\Framework\Phrase( + new Phrase( 'The contents from the "%1" file can\'t be read. %2', [$path, $this->getWarningMessage()] ) @@ -167,11 +175,12 @@ public function fileGetContents($path, $flag = null, $context = null) */ public function isWritable($path) { - clearstatcache(); - $result = @is_writable($this->getScheme() . $path); + $filename = $this->getScheme() . $path; + clearstatcache(false, $filename); + $result = @is_writable($filename); if ($result === null) { throw new FileSystemException( - new \Magento\Framework\Phrase('An error occurred during "%1" execution.', [$this->getWarningMessage()]) + new Phrase('An error occurred during "%1" execution.', [$this->getWarningMessage()]) ); } return $result; @@ -225,7 +234,7 @@ private function mkdirRecursive($path, $permissions = 0777) $result = true; } else { throw new FileSystemException( - new \Magento\Framework\Phrase( + new Phrase( 'Directory "%1" cannot be created %2', [$path, $this->getWarningMessage()] ) @@ -245,7 +254,10 @@ private function mkdirRecursive($path, $permissions = 0777) public function readDirectory($path) { try { - $flags = \FilesystemIterator::SKIP_DOTS | \FilesystemIterator::UNIX_PATHS; + $flags = \FilesystemIterator::SKIP_DOTS | + \FilesystemIterator::UNIX_PATHS | + \RecursiveDirectoryIterator::FOLLOW_SYMLINKS; + $iterator = new \FilesystemIterator($path, $flags); $result = []; /** @var \FilesystemIterator $file */ @@ -255,7 +267,7 @@ public function readDirectory($path) sort($result); return $result; } catch (\Exception $e) { - throw new FileSystemException(new \Magento\Framework\Phrase($e->getMessage()), $e); + throw new FileSystemException(new Phrase($e->getMessage()), $e); } } @@ -298,7 +310,7 @@ public function rename($oldPath, $newPath, DriverInterface $targetDriver = null) } if (!$result) { throw new FileSystemException( - new \Magento\Framework\Phrase( + new Phrase( 'The path "%1" cannot be renamed into "%2" %3', [$oldPath, $newPath, $this->getWarningMessage()] ) @@ -327,7 +339,7 @@ public function copy($source, $destination, DriverInterface $targetDriver = null } if (!$result) { throw new FileSystemException( - new \Magento\Framework\Phrase( + new Phrase( 'The file or directory "%1" cannot be copied to "%2" %3', [ $source, @@ -357,7 +369,7 @@ public function symlink($source, $destination, DriverInterface $targetDriver = n } if (!$result) { throw new FileSystemException( - new \Magento\Framework\Phrase( + new Phrase( 'A symlink for "%1" can\'t be created and placed to "%2". %3', [ $source, @@ -382,7 +394,7 @@ public function deleteFile($path) $result = @unlink($this->getScheme() . $path); if (!$result) { throw new FileSystemException( - new \Magento\Framework\Phrase( + new Phrase( 'The "%1" file can\'t be deleted. %2', [$path, $this->getWarningMessage()] ) @@ -418,7 +430,7 @@ public function deleteDirectory($path) if (!empty($exceptionMessages)) { throw new FileSystemException( - new \Magento\Framework\Phrase( + new Phrase( \implode(' ', $exceptionMessages) ) ); @@ -432,7 +444,7 @@ public function deleteDirectory($path) } if (!$result) { throw new FileSystemException( - new \Magento\Framework\Phrase( + new Phrase( 'The directory "%1" cannot be deleted %2', [$path, $this->getWarningMessage()] ) @@ -454,7 +466,7 @@ public function changePermissions($path, $permissions) $result = @chmod($this->getScheme() . $path, $permissions); if (!$result) { throw new FileSystemException( - new \Magento\Framework\Phrase( + new Phrase( 'The permissions can\'t be changed for the "%1" path. %2.', [$path, $this->getWarningMessage()] ) @@ -482,7 +494,7 @@ public function changePermissionsRecursively($path, $dirPermissions, $filePermis } if (!$result) { throw new FileSystemException( - new \Magento\Framework\Phrase( + new Phrase( 'The permissions can\'t be changed for the "%1" path. %2.', [$path, $this->getWarningMessage()] ) @@ -504,7 +516,7 @@ public function changePermissionsRecursively($path, $dirPermissions, $filePermis } if (!$result) { throw new FileSystemException( - new \Magento\Framework\Phrase( + new Phrase( 'The permissions can\'t be changed for the "%1" path. %2.', [$path, $this->getWarningMessage()] ) @@ -531,7 +543,7 @@ public function touch($path, $modificationTime = null) } if (!$result) { throw new FileSystemException( - new \Magento\Framework\Phrase( + new Phrase( 'The "%1" file or directory can\'t be touched. %2', [$path, $this->getWarningMessage()] ) @@ -552,9 +564,9 @@ public function touch($path, $modificationTime = null) public function filePutContents($path, $content, $mode = null) { $result = @file_put_contents($this->getScheme() . $path, $content, $mode); - if (!$result) { + if ($result === false) { throw new FileSystemException( - new \Magento\Framework\Phrase( + new Phrase( 'The specified "%1" file couldn\'t be written. %2', [$path, $this->getWarningMessage()] ) @@ -576,7 +588,7 @@ public function fileOpen($path, $mode) $result = @fopen($this->getScheme() . $path, $mode); if (!$result) { throw new FileSystemException( - new \Magento\Framework\Phrase('File "%1" cannot be opened %2', [$path, $this->getWarningMessage()]) + new Phrase('File "%1" cannot be opened %2', [$path, $this->getWarningMessage()]) ); } return $result; @@ -593,12 +605,15 @@ public function fileOpen($path, $mode) */ public function fileReadLine($resource, $length, $ending = null) { + // phpcs:disable $result = @stream_get_line($resource, $length, $ending); + // phpcs:enable if (false === $result) { throw new FileSystemException( - new \Magento\Framework\Phrase('File cannot be read %1', [$this->getWarningMessage()]) + new Phrase('File cannot be read %1', [$this->getWarningMessage()]) ); } + return $result; } @@ -615,7 +630,7 @@ public function fileRead($resource, $length) $result = @fread($resource, $length); if ($result === false) { throw new FileSystemException( - new \Magento\Framework\Phrase('File cannot be read %1', [$this->getWarningMessage()]) + new Phrase('File cannot be read %1', [$this->getWarningMessage()]) ); } return $result; @@ -637,7 +652,7 @@ public function fileGetCsv($resource, $length = 0, $delimiter = ',', $enclosure $result = @fgetcsv($resource, $length, $delimiter, $enclosure, $escape); if ($result === null) { throw new FileSystemException( - new \Magento\Framework\Phrase( + new Phrase( 'The "%1" CSV handle is incorrect. Verify the handle and try again.', [$this->getWarningMessage()] ) @@ -658,7 +673,7 @@ public function fileTell($resource) $result = @ftell($resource); if ($result === null) { throw new FileSystemException( - new \Magento\Framework\Phrase('An error occurred during "%1" execution.', [$this->getWarningMessage()]) + new Phrase('An error occurred during "%1" execution.', [$this->getWarningMessage()]) ); } return $result; @@ -678,7 +693,7 @@ public function fileSeek($resource, $offset, $whence = SEEK_SET) $result = @fseek($resource, $offset, $whence); if ($result === -1) { throw new FileSystemException( - new \Magento\Framework\Phrase( + new Phrase( 'An error occurred during "%1" fileSeek execution.', [$this->getWarningMessage()] ) @@ -710,7 +725,7 @@ public function fileClose($resource) $result = @fclose($resource); if (!$result) { throw new FileSystemException( - new \Magento\Framework\Phrase( + new Phrase( 'An error occurred during "%1" fileClose execution.', [$this->getWarningMessage()] ) @@ -756,7 +771,7 @@ public function fileWrite($resource, $data) */ private function fileSystemException($message, $arguments = []) { - throw new FileSystemException(new \Magento\Framework\Phrase($message, $arguments)); + throw new FileSystemException(new Phrase($message, $arguments)); } /** @@ -775,7 +790,7 @@ public function filePutCsv($resource, array $data, $delimiter = ',', $enclosure * Security enhancement for CSV data processing by Excel-like applications. * @see https://bugzilla.mozilla.org/show_bug.cgi?id=1054702 * - * @var $value string|\Magento\Framework\Phrase + * @var $value string|Phrase */ foreach ($data as $key => $value) { if (!is_string($value)) { @@ -789,7 +804,7 @@ public function filePutCsv($resource, array $data, $delimiter = ',', $enclosure $result = @fputcsv($resource, $data, $delimiter, $enclosure); if (!$result) { throw new FileSystemException( - new \Magento\Framework\Phrase( + new Phrase( 'An error occurred during "%1" filePutCsv execution.', [$this->getWarningMessage()] ) @@ -810,7 +825,7 @@ public function fileFlush($resource) $result = @fflush($resource); if (!$result) { throw new FileSystemException( - new \Magento\Framework\Phrase( + new Phrase( 'An error occurred during "%1" fileFlush execution.', [$this->getWarningMessage()] ) @@ -832,7 +847,7 @@ public function fileLock($resource, $lockMode = LOCK_EX) $result = @flock($resource, $lockMode); if (!$result) { throw new FileSystemException( - new \Magento\Framework\Phrase( + new Phrase( 'An error occurred during "%1" fileLock execution.', [$this->getWarningMessage()] ) @@ -853,7 +868,7 @@ public function fileUnlock($resource) $result = @flock($resource, LOCK_UN); if (!$result) { throw new FileSystemException( - new \Magento\Framework\Phrase( + new Phrase( 'An error occurred during "%1" fileUnlock execution.', [$this->getWarningMessage()] ) @@ -934,7 +949,10 @@ protected function getScheme($scheme = null) public function readDirectoryRecursively($path = null) { $result = []; - $flags = \FilesystemIterator::SKIP_DOTS | \FilesystemIterator::UNIX_PATHS; + $flags = \FilesystemIterator::SKIP_DOTS | + \FilesystemIterator::UNIX_PATHS | + \RecursiveDirectoryIterator::FOLLOW_SYMLINKS; + try { $iterator = new \RecursiveIteratorIterator( new \RecursiveDirectoryIterator($path, $flags), @@ -945,7 +963,7 @@ public function readDirectoryRecursively($path = null) $result[] = $file->getPathname(); } } catch (\Exception $e) { - throw new FileSystemException(new \Magento\Framework\Phrase($e->getMessage()), $e); + throw new FileSystemException(new Phrase($e->getMessage()), $e); } return $result; } @@ -976,7 +994,7 @@ public function getRealPathSafety($path) //Removing redundant directory separators. $path = preg_replace( - '/\\' .DIRECTORY_SEPARATOR .'\\' .DIRECTORY_SEPARATOR .'+/', + '/\\' . DIRECTORY_SEPARATOR . '\\' . DIRECTORY_SEPARATOR . '+/', DIRECTORY_SEPARATOR, $path ); diff --git a/lib/internal/Magento/Framework/Filesystem/Driver/Http.php b/lib/internal/Magento/Framework/Filesystem/Driver/Http.php index f32624f4e7513..273425183577e 100644 --- a/lib/internal/Magento/Framework/Filesystem/Driver/Http.php +++ b/lib/internal/Magento/Framework/Filesystem/Driver/Http.php @@ -11,7 +11,7 @@ use Magento\Framework\Exception\FileSystemException; /** - * Class Http + * Allows interacting with http endpoint like with FileSystem */ class Http extends File { @@ -83,8 +83,9 @@ public function stat($path) */ public function fileGetContents($path, $flags = null, $context = null) { - clearstatcache(); - $result = @file_get_contents($this->getScheme() . $path, $flags, $context); + $fullPath = $this->getScheme() . $path; + clearstatcache(false, $fullPath); + $result = @file_get_contents($fullPath, $flags, $context); if (false === $result) { throw new FileSystemException( new \Magento\Framework\Phrase( @@ -109,7 +110,7 @@ public function fileGetContents($path, $flags = null, $context = null) public function filePutContents($path, $content, $mode = null, $context = null) { $result = @file_put_contents($this->getScheme() . $path, $content, $mode, $context); - if (!$result) { + if ($result === false) { throw new FileSystemException( new \Magento\Framework\Phrase( 'The specified "%1" file couldn\'t be written. %2', @@ -196,8 +197,13 @@ public function fileOpen($path, $mode) */ public function fileReadLine($resource, $length, $ending = null) { - $result = @stream_get_line($resource, $length, $ending); - + try { + $result = @stream_get_line($resource, $length, $ending); + } catch (\Exception $e) { + throw new FileSystemException( + new \Magento\Framework\Phrase('Stream get line failed %1', [$e->getMessage()]) + ); + } return $result; } diff --git a/lib/internal/Magento/Framework/Filesystem/Driver/StatefulFile.php b/lib/internal/Magento/Framework/Filesystem/Driver/StatefulFile.php new file mode 100644 index 0000000000000..beeb1e928262c --- /dev/null +++ b/lib/internal/Magento/Framework/Filesystem/Driver/StatefulFile.php @@ -0,0 +1,1007 @@ +<?php +/** + * Origin filesystem driver + * + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ + +namespace Magento\Framework\Filesystem\Driver; + +use Magento\Framework\Exception\FileSystemException; +use Magento\Framework\Filesystem\DriverInterface; +use Magento\Framework\Filesystem\Glob; +use Magento\Framework\Phrase; + +/** + * Filesystem driver that uses the local filesystem. + * + * Assumed that stat cache is cleanup by data modification methods + * + * @SuppressWarnings(PHPMD.ExcessiveClassComplexity) + */ +class StatefulFile implements DriverInterface +{ + /** + * @var string + */ + protected $scheme = ''; + + /** + * Returns last warning message string + * + * @return string + */ + protected function getWarningMessage() + { + $warning = error_get_last(); + if ($warning && $warning['type'] == E_WARNING) { + return 'Warning!' . $warning['message']; + } + return null; + } + + /** + * Is file or directory exist in file system + * + * @param string $path + * @return bool + * @throws FileSystemException + */ + public function isExists($path) + { + $result = @file_exists($this->getScheme() . $path); + if ($result === null) { + throw new FileSystemException( + new Phrase('An error occurred during "%1" execution.', [$this->getWarningMessage()]) + ); + } + return $result; + } + + /** + * Gathers the statistics of the given path + * + * @param string $path + * @return array + * @throws FileSystemException + */ + public function stat($path) + { + $result = @stat($this->getScheme() . $path); + if (!$result) { + throw new FileSystemException( + new Phrase('Cannot gather stats! %1', [$this->getWarningMessage()]) + ); + } + return $result; + } + + /** + * Check permissions for reading file or directory + * + * @param string $path + * @return bool + * @throws FileSystemException + */ + public function isReadable($path) + { + $result = @is_readable($this->getScheme() . $path); + if ($result === null) { + throw new FileSystemException( + new Phrase('An error occurred during "%1" execution.', [$this->getWarningMessage()]) + ); + } + return $result; + } + + /** + * Tells whether the filename is a regular file + * + * @param string $path + * @return bool + * @throws FileSystemException + */ + public function isFile($path) + { + $result = @is_file($this->getScheme() . $path); + if ($result === null) { + throw new FileSystemException( + new Phrase('An error occurred during "%1" execution.', [$this->getWarningMessage()]) + ); + } + return $result; + } + + /** + * Tells whether the filename is a regular directory + * + * @param string $path + * @return bool + * @throws FileSystemException + */ + public function isDirectory($path) + { + $result = @is_dir($this->getScheme() . $path); + if ($result === null) { + throw new FileSystemException( + new Phrase('An error occurred during "%1" execution.', [$this->getWarningMessage()]) + ); + } + return $result; + } + + /** + * Retrieve file contents from given path + * + * @param string $path + * @param string|null $flag + * @param resource|null $context + * @return string + * @throws FileSystemException + */ + public function fileGetContents($path, $flag = null, $context = null) + { + $result = @file_get_contents($this->getScheme() . $path, $flag, $context); + if (false === $result) { + throw new FileSystemException( + new Phrase( + 'The contents from the "%1" file can\'t be read. %2', + [$path, $this->getWarningMessage()] + ) + ); + } + return $result; + } + + /** + * Check if given path is writable + * + * @param string $path + * @return bool + * @throws FileSystemException + */ + public function isWritable($path) + { + $result = @is_writable($this->getScheme() . $path); + if ($result === null) { + throw new FileSystemException( + new Phrase('An error occurred during "%1" execution.', [$this->getWarningMessage()]) + ); + } + return $result; + } + + /** + * Returns parent directory's path + * + * @param string $path + * @return string + */ + public function getParentDirectory($path) + { + return dirname($this->getScheme() . $path); + } + + /** + * Create directory + * + * @param string $path + * @param int $permissions + * @return bool + * @throws FileSystemException + */ + public function createDirectory($path, $permissions = 0777) + { + clearstatcache(true, $path); + return $this->mkdirRecursive($path, $permissions); + } + + /** + * Create a directory recursively taking into account race conditions + * + * @param string $path + * @param int $permissions + * @return bool + * @throws FileSystemException + */ + private function mkdirRecursive($path, $permissions = 0777) + { + $path = $this->getScheme() . $path; + if (is_dir($path)) { + return true; + } + $parentDir = dirname($path); + while (!is_dir($parentDir)) { + $this->mkdirRecursive($parentDir, $permissions); + } + $result = @mkdir($path, $permissions); + clearstatcache(true, $path); + if (!$result) { + if (is_dir($path)) { + $result = true; + } else { + throw new FileSystemException( + new Phrase( + 'Directory "%1" cannot be created %2', + [$path, $this->getWarningMessage()] + ) + ); + } + } + return $result; + } + + /** + * Read directory + * + * @param string $path + * @return string[] + * @throws FileSystemException + */ + public function readDirectory($path) + { + try { + $flags = \FilesystemIterator::SKIP_DOTS | \FilesystemIterator::UNIX_PATHS; + $iterator = new \FilesystemIterator($path, $flags); + $result = []; + /** @var \FilesystemIterator $file */ + foreach ($iterator as $file) { + $result[] = $file->getPathname(); + } + sort($result); + return $result; + } catch (\Exception $e) { + throw new FileSystemException(new Phrase($e->getMessage()), $e); + } + } + + /** + * Search paths by given regex + * + * @param string $pattern + * @param string $path + * @return string[] + * @throws FileSystemException + */ + public function search($pattern, $path) + { + $globPattern = rtrim($path, '/') . '/' . ltrim($pattern, '/'); + $result = Glob::glob($globPattern, Glob::GLOB_BRACE); + return is_array($result) ? $result : []; + } + + /** + * Renames a file or directory + * + * @param string $oldPath + * @param string $newPath + * @param DriverInterface|null $targetDriver + * @return bool + * @throws FileSystemException + */ + public function rename($oldPath, $newPath, DriverInterface $targetDriver = null) + { + $result = false; + $targetDriver = $targetDriver ?: $this; + if (get_class($targetDriver) == get_class($this)) { + $result = @rename($this->getScheme() . $oldPath, $newPath); + clearstatcache(true, $this->getScheme() . $oldPath); + clearstatcache(true, $newPath); + } else { + $content = $this->fileGetContents($oldPath); + if (false !== $targetDriver->filePutContents($newPath, $content)) { + $result = $this->deleteFile($newPath); + } + } + if (!$result) { + throw new FileSystemException( + new Phrase( + 'The path "%1" cannot be renamed into "%2" %3', + [$oldPath, $newPath, $this->getWarningMessage()] + ) + ); + } + return $result; + } + + /** + * Copy source into destination + * + * @param string $source + * @param string $destination + * @param DriverInterface|null $targetDriver + * @return bool + * @throws FileSystemException + */ + public function copy($source, $destination, DriverInterface $targetDriver = null) + { + $targetDriver = $targetDriver ?: $this; + if (get_class($targetDriver) == get_class($this)) { + $result = @copy($this->getScheme() . $source, $destination); + clearstatcache(true, $destination); + } else { + $content = $this->fileGetContents($source); + $result = $targetDriver->filePutContents($destination, $content); + } + if (!$result) { + throw new FileSystemException( + new Phrase( + 'The file or directory "%1" cannot be copied to "%2" %3', + [ + $source, + $destination, + $this->getWarningMessage() + ] + ) + ); + } + return $result; + } + + /** + * Create symlink on source and place it into destination + * + * @param string $source + * @param string $destination + * @param DriverInterface|null $targetDriver + * @return bool + * @throws FileSystemException + */ + public function symlink($source, $destination, DriverInterface $targetDriver = null) + { + $result = false; + if ($targetDriver === null || get_class($targetDriver) == get_class($this)) { + $result = @symlink($this->getScheme() . $source, $destination); + clearstatcache(true, $destination); + } + if (!$result) { + throw new FileSystemException( + new Phrase( + 'A symlink for "%1" can\'t be created and placed to "%2". %3', + [ + $source, + $destination, + $this->getWarningMessage() + ] + ) + ); + } + return $result; + } + + /** + * Delete file + * + * @param string $path + * @return bool + * @throws FileSystemException + */ + public function deleteFile($path) + { + $result = @unlink($this->getScheme() . $path); + clearstatcache(true, $this->getScheme() . $path); + if (!$result) { + throw new FileSystemException( + new Phrase( + 'The "%1" file can\'t be deleted. %2', + [$path, $this->getWarningMessage()] + ) + ); + } + return $result; + } + + /** + * Recursive delete directory + * + * @param string $path + * @return bool + * @throws FileSystemException + */ + public function deleteDirectory($path) + { + $exceptionMessages = []; + $flags = \FilesystemIterator::SKIP_DOTS | \FilesystemIterator::UNIX_PATHS; + $iterator = new \FilesystemIterator($path, $flags); + /** @var \FilesystemIterator $entity */ + foreach ($iterator as $entity) { + try { + if ($entity->isDir()) { + $this->deleteDirectory($entity->getPathname()); + } else { + $this->deleteFile($entity->getPathname()); + } + } catch (FileSystemException $exception) { + $exceptionMessages[] = $exception->getMessage(); + } + } + + if (!empty($exceptionMessages)) { + throw new FileSystemException( + new Phrase( + \implode(' ', $exceptionMessages) + ) + ); + } + + $fullPath = $this->getScheme() . $path; + if (is_link($fullPath)) { + $result = @unlink($fullPath); + } else { + $result = @rmdir($fullPath); + } + clearstatcache(true, $fullPath); + if (!$result) { + throw new FileSystemException( + new Phrase( + 'The directory "%1" cannot be deleted %2', + [$path, $this->getWarningMessage()] + ) + ); + } + return $result; + } + + /** + * Change permissions of given path + * + * @param string $path + * @param int $permissions + * @return bool + * @throws FileSystemException + */ + public function changePermissions($path, $permissions) + { + $result = @chmod($this->getScheme() . $path, $permissions); + clearstatcache(false, $this->getScheme() . $path); + if (!$result) { + throw new FileSystemException( + new Phrase( + 'The permissions can\'t be changed for the "%1" path. %2.', + [$path, $this->getWarningMessage()] + ) + ); + } + return $result; + } + + /** + * Recursively change permissions of given path + * + * @param string $path + * @param int $dirPermissions + * @param int $filePermissions + * @return bool + * @throws FileSystemException + */ + public function changePermissionsRecursively($path, $dirPermissions, $filePermissions) + { + $result = true; + if ($this->isFile($path)) { + $result = @chmod($path, $filePermissions); + } else { + $result = @chmod($path, $dirPermissions); + } + clearstatcache(false, $this->getScheme() . $path); + + if (!$result) { + throw new FileSystemException( + new Phrase( + 'The permissions can\'t be changed for the "%1" path. %2.', + [$path, $this->getWarningMessage()] + ) + ); + } + + $flags = \FilesystemIterator::SKIP_DOTS | \FilesystemIterator::UNIX_PATHS; + + $iterator = new \RecursiveIteratorIterator( + new \RecursiveDirectoryIterator($path, $flags), + \RecursiveIteratorIterator::CHILD_FIRST + ); + /** @var \FilesystemIterator $entity */ + foreach ($iterator as $entity) { + if ($entity->isDir()) { + $result = @chmod($entity->getPathname(), $dirPermissions); + } else { + $result = @chmod($entity->getPathname(), $filePermissions); + } + if (!$result) { + throw new FileSystemException( + new Phrase( + 'The permissions can\'t be changed for the "%1" path. %2.', + [$path, $this->getWarningMessage()] + ) + ); + } + } + return $result; + } + + /** + * Sets access and modification time of file. + * + * @param string $path + * @param int|null $modificationTime + * @return bool + * @throws FileSystemException + */ + public function touch($path, $modificationTime = null) + { + if (!$modificationTime) { + $result = @touch($this->getScheme() . $path); + } else { + $result = @touch($this->getScheme() . $path, $modificationTime); + } + clearstatcache(true, $this->getScheme() . $path); + if (!$result) { + throw new FileSystemException( + new Phrase( + 'The "%1" file or directory can\'t be touched. %2', + [$path, $this->getWarningMessage()] + ) + ); + } + return $result; + } + + /** + * Write contents to file in given path + * + * @param string $path + * @param string $content + * @param string|null $mode + * @return int The number of bytes that were written. + * @throws FileSystemException + */ + public function filePutContents($path, $content, $mode = null) + { + $result = @file_put_contents($this->getScheme() . $path, $content, $mode); + clearstatcache(true, $this->getScheme() . $path); + if (!$result) { + throw new FileSystemException( + new Phrase( + 'The specified "%1" file couldn\'t be written. %2', + [$path, $this->getWarningMessage()] + ) + ); + } + return $result; + } + + /** + * Open file + * + * @param string $path + * @param string $mode + * @return resource file + * @throws FileSystemException + */ + public function fileOpen($path, $mode) + { + $result = @fopen($this->getScheme() . $path, $mode); + clearstatcache(true, $this->getScheme() . $path); + if (!$result) { + throw new FileSystemException( + new Phrase('File "%1" cannot be opened %2', [$path, $this->getWarningMessage()]) + ); + } + return $result; + } + + /** + * Reads the line content from file pointer (with specified number of bytes from the current position). + * + * @param resource $resource + * @param int $length + * @param string $ending [optional] + * @return string + * @throws FileSystemException + */ + public function fileReadLine($resource, $length, $ending = null) + { + // phpcs:disable + $result = @stream_get_line($resource, $length, $ending); + // phpcs:enable + if (false === $result) { + throw new FileSystemException( + new Phrase('File cannot be read %1', [$this->getWarningMessage()]) + ); + } + return $result; + } + + /** + * Reads the specified number of bytes from the current position. + * + * @param resource $resource + * @param int $length + * @return string + * @throws FileSystemException + */ + public function fileRead($resource, $length) + { + $result = @fread($resource, $length); + if ($result === false) { + throw new FileSystemException( + new Phrase('File cannot be read %1', [$this->getWarningMessage()]) + ); + } + return $result; + } + + /** + * Reads one CSV row from the file + * + * @param resource $resource + * @param int $length [optional] + * @param string $delimiter [optional] + * @param string $enclosure [optional] + * @param string $escape [optional] + * @return array|bool|null + * @throws FileSystemException + */ + public function fileGetCsv($resource, $length = 0, $delimiter = ',', $enclosure = '"', $escape = '\\') + { + $result = @fgetcsv($resource, $length, $delimiter, $enclosure, $escape); + if ($result === null) { + throw new FileSystemException( + new Phrase( + 'The "%1" CSV handle is incorrect. Verify the handle and try again.', + [$this->getWarningMessage()] + ) + ); + } + return $result; + } + + /** + * Returns position of read/write pointer + * + * @param resource $resource + * @return int + * @throws FileSystemException + */ + public function fileTell($resource) + { + $result = @ftell($resource); + if ($result === null) { + throw new FileSystemException( + new Phrase('An error occurred during "%1" execution.', [$this->getWarningMessage()]) + ); + } + return $result; + } + + /** + * Seeks to the specified offset + * + * @param resource $resource + * @param int $offset + * @param int $whence + * @return int + * @throws FileSystemException + */ + public function fileSeek($resource, $offset, $whence = SEEK_SET) + { + $result = @fseek($resource, $offset, $whence); + if ($result === -1) { + throw new FileSystemException( + new Phrase( + 'An error occurred during "%1" fileSeek execution.', + [$this->getWarningMessage()] + ) + ); + } + return $result; + } + + /** + * Returns true if pointer at the end of file or in case of exception + * + * @param resource $resource + * @return boolean + */ + public function endOfFile($resource) + { + return feof($resource); + } + + /** + * Close file + * + * @param resource $resource + * @return boolean + * @throws FileSystemException + */ + public function fileClose($resource) + { + $result = @fclose($resource); + if (!$result) { + throw new FileSystemException( + new Phrase( + 'An error occurred during "%1" fileClose execution.', + [$this->getWarningMessage()] + ) + ); + } + return $result; + } + + /** + * Writes data to file + * + * @param resource $resource + * @param string $data + * @return int + * @throws FileSystemException + */ + public function fileWrite($resource, $data) + { + $lenData = strlen($data); + for ($result = 0; $result < $lenData; $result += $fwrite) { + $fwrite = @fwrite($resource, substr($data, $result)); + if (0 === $fwrite) { + $this->fileSystemException('Unable to write'); + } + if (false === $fwrite) { + $this->fileSystemException( + 'An error occurred during "%1" fileWrite execution.', + [$this->getWarningMessage()] + ); + } + } + + return $result; + } + + /** + * Throw a FileSystemException with a Phrase of message and optional arguments + * + * @param string $message + * @param array $arguments + * @return void + * @throws FileSystemException + */ + private function fileSystemException($message, $arguments = []) + { + throw new FileSystemException(new Phrase($message, $arguments)); + } + + /** + * Writes one CSV row to the file. + * + * @param resource $resource + * @param array $data + * @param string $delimiter + * @param string $enclosure + * @return int + * @throws FileSystemException + */ + public function filePutCsv($resource, array $data, $delimiter = ',', $enclosure = '"') + { + /** + * Security enhancement for CSV data processing by Excel-like applications. + * @see https://bugzilla.mozilla.org/show_bug.cgi?id=1054702 + * + * @var $value string|Phrase + */ + foreach ($data as $key => $value) { + if (!is_string($value)) { + $value = (string)$value; + } + if (isset($value[0]) && in_array($value[0], ['=', '+', '-'])) { + $data[$key] = ' ' . $value; + } + } + + $result = @fputcsv($resource, $data, $delimiter, $enclosure); + if (!$result) { + throw new FileSystemException( + new Phrase( + 'An error occurred during "%1" filePutCsv execution.', + [$this->getWarningMessage()] + ) + ); + } + return $result; + } + + /** + * Flushes the output + * + * @param resource $resource + * @return bool + * @throws FileSystemException + */ + public function fileFlush($resource) + { + $result = @fflush($resource); + if (!$result) { + throw new FileSystemException( + new Phrase( + 'An error occurred during "%1" fileFlush execution.', + [$this->getWarningMessage()] + ) + ); + } + return $result; + } + + /** + * Lock file in selected mode + * + * @param resource $resource + * @param int $lockMode + * @return bool + * @throws FileSystemException + */ + public function fileLock($resource, $lockMode = LOCK_EX) + { + $result = @flock($resource, $lockMode); + if (!$result) { + throw new FileSystemException( + new Phrase( + 'An error occurred during "%1" fileLock execution.', + [$this->getWarningMessage()] + ) + ); + } + return $result; + } + + /** + * Unlock file + * + * @param resource $resource + * @return bool + * @throws FileSystemException + */ + public function fileUnlock($resource) + { + $result = @flock($resource, LOCK_UN); + if (!$result) { + throw new FileSystemException( + new Phrase( + 'An error occurred during "%1" fileUnlock execution.', + [$this->getWarningMessage()] + ) + ); + } + return $result; + } + + /** + * Returns an absolute path for the given one. + * + * @param string $basePath + * @param string $path + * @param string|null $scheme + * @return string + */ + public function getAbsolutePath($basePath, $path, $scheme = null) + { + // check if the path given is already an absolute path containing the + // basepath. so if the basepath starts at position 0 in the path, we + // must not concatinate them again because path is already absolute. + if (0 === strpos($path, $basePath)) { + return $this->getScheme($scheme) . $path; + } + + return $this->getScheme($scheme) . $basePath . ltrim($this->fixSeparator($path), '/'); + } + + /** + * Retrieves relative path + * + * @param string $basePath + * @param string $path + * @return string + */ + public function getRelativePath($basePath, $path = null) + { + $path = $this->fixSeparator($path); + if (strpos($path, $basePath) === 0 || $basePath == $path . '/') { + $result = substr($path, strlen($basePath)); + } else { + $result = $path; + } + return $result; + } + + /** + * Fixes path separator. + * + * Utility method. + * + * @param string $path + * @return string + */ + protected function fixSeparator($path) + { + return str_replace('\\', '/', $path); + } + + /** + * Return path with scheme + * + * @param null|string $scheme + * @return string + */ + protected function getScheme($scheme = null) + { + return $scheme ? $scheme . '://' : ''; + } + + /** + * Read directory recursively + * + * @param string $path + * @return string[] + * @throws FileSystemException + */ + public function readDirectoryRecursively($path = null) + { + $result = []; + $flags = \FilesystemIterator::SKIP_DOTS | \FilesystemIterator::UNIX_PATHS; + try { + $iterator = new \RecursiveIteratorIterator( + new \RecursiveDirectoryIterator($path, $flags), + \RecursiveIteratorIterator::CHILD_FIRST + ); + /** @var \FilesystemIterator $file */ + foreach ($iterator as $file) { + $result[] = $file->getPathname(); + } + } catch (\Exception $e) { + throw new FileSystemException(new Phrase($e->getMessage()), $e); + } + return $result; + } + + /** + * Get real path + * + * @param string $path + * + * @return string|bool + */ + public function getRealPath($path) + { + return realpath($path); + } + + /** + * Return correct path for link + * + * @param string $path + * @return mixed + */ + public function getRealPathSafety($path) + { + if (strpos($path, DIRECTORY_SEPARATOR . '..' . DIRECTORY_SEPARATOR) === false) { + return $path; + } + + //Removing redundant directory separators. + $path = preg_replace( + '/\\' . DIRECTORY_SEPARATOR . '\\' . DIRECTORY_SEPARATOR . '+/', + DIRECTORY_SEPARATOR, + $path + ); + $pathParts = explode(DIRECTORY_SEPARATOR, $path); + $realPath = []; + foreach ($pathParts as $pathPart) { + if ($pathPart == '.') { + continue; + } + if ($pathPart == '..') { + array_pop($realPath); + continue; + } + $realPath[] = $pathPart; + } + return implode(DIRECTORY_SEPARATOR, $realPath); + } +} diff --git a/lib/internal/Magento/Framework/Filesystem/Glob.php b/lib/internal/Magento/Framework/Filesystem/Glob.php index 415bb5494c2e8..9d8de333e35fa 100644 --- a/lib/internal/Magento/Framework/Filesystem/Glob.php +++ b/lib/internal/Magento/Framework/Filesystem/Glob.php @@ -3,29 +3,30 @@ * Copyright © Magento, Inc. All rights reserved. * See COPYING.txt for license details. */ + namespace Magento\Framework\Filesystem; -use Zend\Stdlib\Glob as ZendGlob; -use Zend\Stdlib\Exception\RuntimeException as ZendRuntimeException; +use Laminas\Stdlib\Glob as LaminasGlob; +use Laminas\Stdlib\Exception\RuntimeException as LaminasRuntimeException; /** - * Wrapper for Zend\Stdlib\Glob + * Wrapper for Laminas\Stdlib\Glob */ -class Glob extends ZendGlob +class Glob extends LaminasGlob { /** * Find pathnames matching a pattern. * - * @param string $pattern - * @param int $flags - * @param bool $forceFallback + * @param string $pattern + * @param int $flags + * @param bool $forceFallback * @return array */ public static function glob($pattern, $flags = 0, $forceFallback = false) { try { - $result = ZendGlob::glob($pattern, $flags, $forceFallback); - } catch (ZendRuntimeException $e) { + $result = LaminasGlob::glob($pattern, $flags, $forceFallback); + } catch (LaminasRuntimeException $e) { $result = []; } return $result; diff --git a/lib/internal/Magento/Framework/Filesystem/Io/File.php b/lib/internal/Magento/Framework/Filesystem/Io/File.php index 8fec7f7630257..2e81325c8ec57 100644 --- a/lib/internal/Magento/Framework/Filesystem/Io/File.php +++ b/lib/internal/Magento/Framework/Filesystem/Io/File.php @@ -3,10 +3,15 @@ * Copyright © Magento, Inc. All rights reserved. * See COPYING.txt for license details. */ + namespace Magento\Framework\Filesystem\Io; +use Magento\Framework\Exception\LocalizedException; +use Magento\Framework\Phrase; + /** * Filesystem client + * * @SuppressWarnings(PHPMD.ExcessiveClassComplexity) */ class File extends AbstractIo @@ -186,7 +191,7 @@ public function streamWriteCsv(array $row, $delimiter = ',', $enclosure = '"') * Security enhancement for CSV data processing by Excel-like applications. * @see https://bugzilla.mozilla.org/show_bug.cgi?id=1054702 * - * @var $value string|\Magento\Framework\Phrase + * @var $value string|Phrase */ foreach ($row as $key => $value) { if (!is_string($value)) { @@ -201,6 +206,7 @@ public function streamWriteCsv(array $row, $delimiter = ',', $enclosure = '"') /** * Close an open file pointer + * * Set chmod on a file * * @return bool @@ -328,6 +334,7 @@ public function rmdir($dir, $recursive = false) /** * Delete a directory recursively + * * @param string $dir * @param bool $recursive * @return bool @@ -405,8 +412,8 @@ public function pwd() * * @param string $dir * @return true - * @throws \Exception * @SuppressWarnings(PHPMD.ShortMethodName) + * @throws LocalizedException */ public function cd($dir) { @@ -415,7 +422,9 @@ public function cd($dir) $this->_cwd = realpath($dir); return true; } else { - throw new \Exception('Unable to list current working directory.'); + throw new LocalizedException( + new Phrase('Unable to list current working directory.') + ); } } @@ -479,7 +488,7 @@ public function write($filename, $src, $mode = null) } else { $result = @file_put_contents($filename, $src); } - if ($mode !== null && $result) { + if ($mode !== null && $result !== false) { @chmod($filename, $mode); } $this->_iwd(); @@ -553,7 +562,7 @@ public function createDestinationDir($path) * @param string $folder * @param int $mode * @return true - * @throws \Exception + * @throws LocalizedException */ public function checkAndCreateFolder($folder, $mode = 0777) { @@ -564,7 +573,9 @@ public function checkAndCreateFolder($folder, $mode = 0777) $this->checkAndCreateFolder(dirname($folder), $mode); } if (!is_dir($folder) && !$this->mkdir($folder, $mode)) { - throw new \Exception("Unable to create directory '{$folder}'. Access forbidden."); + throw new LocalizedException( + new Phrase("Unable to create directory '{$folder}'. Access forbidden.") + ); } return true; } @@ -672,7 +683,7 @@ public static function chmodRecursive($dir, $mode) * * @param string|null $grep * @return array - * @throws \Exception + * @throws LocalizedException * @SuppressWarnings(PHPMD.CyclomaticComplexity) * @SuppressWarnings(PHPMD.ShortMethodName) */ @@ -685,7 +696,9 @@ public function ls($grep = null) } elseif (is_dir($this->_iwd)) { $dir = $this->_iwd; } else { - throw new \Exception('Unable to list current working directory.'); + throw new LocalizedException( + new Phrase('Unable to list current working directory.') + ); } $list = []; @@ -742,7 +755,9 @@ public function ls($grep = null) } closedir($dirHandler); } else { - throw new \Exception('Unable to list current working directory. Access forbidden.'); + throw new LocalizedException( + new Phrase('Unable to list current working directory. Access forbidden.') + ); } return $list; diff --git a/lib/internal/Magento/Framework/Filesystem/Test/Unit/Driver/HttpTest.php b/lib/internal/Magento/Framework/Filesystem/Test/Unit/Driver/HttpTest.php index 8fc57f458334e..a851d30ef930e 100644 --- a/lib/internal/Magento/Framework/Filesystem/Test/Unit/Driver/HttpTest.php +++ b/lib/internal/Magento/Framework/Filesystem/Test/Unit/Driver/HttpTest.php @@ -3,12 +3,17 @@ * Copyright © Magento, Inc. All rights reserved. * See COPYING.txt for license details. */ +declare(strict_types=1); namespace Magento\Framework\Filesystem\Test\Unit\Driver; -use \Magento\Framework\Filesystem\Driver\Http; +use Magento\Framework\Filesystem\Driver\Http; +use PHPUnit\Framework\TestCase; -class HttpTest extends \PHPUnit\Framework\TestCase +/** + * Verify HttpTest class. + */ +class HttpTest extends TestCase { /** @var array Result of get_headers() function */ public static $headers; @@ -22,6 +27,9 @@ class HttpTest extends \PHPUnit\Framework\TestCase /** @var bool Result of fsockopen() function */ public static $fsockopen; + /** + * @inheritDoc + */ protected function setUp() { require_once __DIR__ . '/../_files/http_mock.php'; @@ -33,35 +41,49 @@ protected function setUp() } /** + * Verify IsExists. + * + * @param string $status + * @param bool $result * @dataProvider dataProviderForTestIsExists + * @return void */ - public function testIsExists($status, $result) + public function testIsExists(string $status, bool $result): void { self::$headers = [$status]; $this->assertEquals($result, (new Http())->isExists('')); } /** + * Data provider fot test IsExists. + * * @return array */ - public function dataProviderForTestIsExists() + public function dataProviderForTestIsExists(): array { return [['200 OK', true], ['404 Not Found', false]]; } /** + * Verify Stat. + * + * @param array $headers + * @param array $result * @dataProvider dataProviderForTestStat + * @return void */ - public function testStat($headers, $result) + public function testStat(array $headers, array $result): void { self::$headers = $headers; $this->assertEquals($result, (new Http())->stat('')); } /** + * Data provider for test Stat. + * * @return array */ - public function dataProviderForTestStat() + public function dataProviderForTestStat(): array { $headers1 = [ 'Content-Length' => 128, @@ -106,45 +128,87 @@ protected function _resultForStat($nonEmptyValues = []) return array_merge($result, $nonEmptyValues); } - public function testFileGetContents() + /** + * Verify File get contents. + * + * @throws \Magento\Framework\Exception\FileSystemException + * @return void + */ + public function testFileGetContents(): void { $content = 'some content'; self::$fileGetContents = $content; $this->assertEquals($content, (new Http())->fileGetContents('')); } - public function testFileGetContentsNoContent() + /** + * Verify File get contents without content. + * + * @throws \Magento\Framework\Exception\FileSystemException + * @return void + */ + public function testFileGetContentsNoContent(): void { $content = ''; self::$fileGetContents = ''; $this->assertEquals($content, (new Http())->fileGetContents('')); } - public function testFilePutContents() + /** + * Verify File put contents. + * + * @throws \Magento\Framework\Exception\FileSystemException + * @return void + */ + public function testFilePutContents(): void { self::$filePutContents = true; $this->assertTrue((new Http())->filePutContents('', '')); } /** + * Verify file put contents without content. + * + * @throws \Magento\Framework\Exception\FileSystemException + * @return void + */ + public function testFilePutContentsNoContent(): void + { + self::$filePutContents = 0; + $this->assertEquals(0, (new Http())->filePutContents('', '')); + } + + /** + * Verify File put contents if is fail. + * * @expectedException \Magento\Framework\Exception\FileSystemException + * @return void */ - public function testFilePutContentsFail() + public function testFilePutContentsFail(): void { self::$filePutContents = false; (new Http())->filePutContents('', ''); } /** + * Verify File open invalid url. + * * @expectedException \Magento\Framework\Exception\FileSystemException * @expectedExceptionMessage The download URL is incorrect. Verify and try again. + * @return void */ - public function testFileOpenInvalidUrl() + public function testFileOpenInvalidUrl(): void { (new Http())->fileOpen('', ''); } - public function testFileOpen() + /** + * Verify File open. + * + * @throws \Magento\Framework\Exception\FileSystemException + * @return void + */ + public function testFileOpen(): void { $fsockopenResult = 'resource'; self::$fsockopen = $fsockopenResult; diff --git a/lib/internal/Magento/Framework/HTTP/Adapter/Curl.php b/lib/internal/Magento/Framework/HTTP/Adapter/Curl.php index f2a703a193e28..19b23abbc44d4 100644 --- a/lib/internal/Magento/Framework/HTTP/Adapter/Curl.php +++ b/lib/internal/Magento/Framework/HTTP/Adapter/Curl.php @@ -166,16 +166,19 @@ public function write($method, $url, $http_ver = '1.1', $headers = [], $body = ' // set url to post to curl_setopt($this->_getResource(), CURLOPT_URL, $url); curl_setopt($this->_getResource(), CURLOPT_RETURNTRANSFER, true); - if ($method == \Zend_Http_Client::POST) { + if ($method === \Zend_Http_Client::POST) { curl_setopt($this->_getResource(), CURLOPT_POST, true); curl_setopt($this->_getResource(), CURLOPT_CUSTOMREQUEST, 'POST'); curl_setopt($this->_getResource(), CURLOPT_POSTFIELDS, $body); - } elseif ($method == \Zend_Http_Client::PUT) { + } elseif ($method === \Zend_Http_Client::PUT) { curl_setopt($this->_getResource(), CURLOPT_CUSTOMREQUEST, 'PUT'); curl_setopt($this->_getResource(), CURLOPT_POSTFIELDS, $body); - } elseif ($method == \Zend_Http_Client::GET) { + } elseif ($method === \Zend_Http_Client::GET) { curl_setopt($this->_getResource(), CURLOPT_HTTPGET, true); curl_setopt($this->_getResource(), CURLOPT_CUSTOMREQUEST, 'GET'); + } elseif ($method === \Zend_Http_Client::DELETE) { + curl_setopt($this->_getResource(), CURLOPT_CUSTOMREQUEST, 'DELETE'); + curl_setopt($this->_getResource(), CURLOPT_POSTFIELDS, $body); } if ($http_ver === \Zend_Http_Client::HTTP_1) { diff --git a/lib/internal/Magento/Framework/HTTP/Client/Curl.php b/lib/internal/Magento/Framework/HTTP/Client/Curl.php index 8b90897481259..60825e231504d 100644 --- a/lib/internal/Magento/Framework/HTTP/Client/Curl.php +++ b/lib/internal/Magento/Framework/HTTP/Client/Curl.php @@ -107,9 +107,9 @@ class Curl implements \Magento\Framework\HTTP\ClientInterface protected $_headerCount = 0; /** - * Set request timeout, msec + * Set request timeout * - * @param int $value + * @param int $value value in seconds * @return void */ public function setTimeout($value) @@ -423,6 +423,7 @@ protected function makeRequest($method, $uri, $params = []) */ public function doError($string) { + // phpcs:ignore Magento2.Exceptions.DirectThrow throw new \Exception($string); } diff --git a/lib/internal/Magento/Framework/HTTP/PhpEnvironment/Request.php b/lib/internal/Magento/Framework/HTTP/PhpEnvironment/Request.php index 3ecf360f36894..13d6e7b72d89f 100644 --- a/lib/internal/Magento/Framework/HTTP/PhpEnvironment/Request.php +++ b/lib/internal/Magento/Framework/HTTP/PhpEnvironment/Request.php @@ -7,11 +7,11 @@ use Magento\Framework\Stdlib\Cookie\CookieReaderInterface; use Magento\Framework\Stdlib\StringUtils; -use Zend\Http\Header\HeaderInterface; -use Zend\Stdlib\Parameters; -use Zend\Stdlib\ParametersInterface; -use Zend\Uri\UriFactory; -use Zend\Uri\UriInterface; +use Laminas\Http\Header\HeaderInterface; +use Laminas\Stdlib\Parameters; +use Laminas\Stdlib\ParametersInterface; +use Laminas\Uri\UriFactory; +use Laminas\Uri\UriInterface; /** * HTTP Request for current PHP environment. @@ -19,7 +19,7 @@ * @SuppressWarnings(PHPMD.ExcessiveClassComplexity) * @SuppressWarnings(PHPMD.CookieAndSessionMisuse) */ -class Request extends \Zend\Http\PhpEnvironment\Request +class Request extends \Laminas\Http\PhpEnvironment\Request { /**#@+ * Protocols diff --git a/lib/internal/Magento/Framework/HTTP/PhpEnvironment/Response.php b/lib/internal/Magento/Framework/HTTP/PhpEnvironment/Response.php index dc3e63fcc7df8..5179eefd97a4c 100644 --- a/lib/internal/Magento/Framework/HTTP/PhpEnvironment/Response.php +++ b/lib/internal/Magento/Framework/HTTP/PhpEnvironment/Response.php @@ -10,7 +10,7 @@ /** * Base HTTP response object */ -class Response extends \Zend\Http\PhpEnvironment\Response implements \Magento\Framework\App\Response\HttpInterface +class Response extends \Laminas\Http\PhpEnvironment\Response implements \Magento\Framework\App\Response\HttpInterface { /** * Flag; is this response a redirect? @@ -183,27 +183,4 @@ public function __sleep() { return ['content', 'isRedirect', 'statusCode']; } - - /** - * Sending provided headers. - * - * Had to be overridden because the original did not work correctly with multi-headers. - */ - public function sendHeaders() - { - if ($this->headersSent()) { - return $this; - } - - $status = $this->renderStatusLine(); - header($status); - - /** @var \Zend\Http\Header\HeaderInterface $header */ - foreach ($this->getHeaders() as $header) { - header($header->toString(), false); - } - - $this->headersSent = true; - return $this; - } } diff --git a/lib/internal/Magento/Framework/HTTP/Test/Unit/PhpEnvironment/RequestTest.php b/lib/internal/Magento/Framework/HTTP/Test/Unit/PhpEnvironment/RequestTest.php index 6bd8a977f2a2c..addd4ba93576e 100644 --- a/lib/internal/Magento/Framework/HTTP/Test/Unit/PhpEnvironment/RequestTest.php +++ b/lib/internal/Magento/Framework/HTTP/Test/Unit/PhpEnvironment/RequestTest.php @@ -7,7 +7,7 @@ use \Magento\Framework\HTTP\PhpEnvironment\Request; -use Zend\Stdlib\Parameters; +use Laminas\Stdlib\Parameters; class RequestTest extends \PHPUnit\Framework\TestCase { diff --git a/lib/internal/Magento/Framework/HTTP/Test/Unit/PhpEnvironment/ResponseTest.php b/lib/internal/Magento/Framework/HTTP/Test/Unit/PhpEnvironment/ResponseTest.php index ba6746e75ce34..7ba897ee21cbe 100644 --- a/lib/internal/Magento/Framework/HTTP/Test/Unit/PhpEnvironment/ResponseTest.php +++ b/lib/internal/Magento/Framework/HTTP/Test/Unit/PhpEnvironment/ResponseTest.php @@ -12,7 +12,7 @@ class ResponseTest extends \PHPUnit\Framework\TestCase /** @var \PHPUnit_Framework_MockObject_MockObject|\Magento\Framework\HTTP\PhpEnvironment\Response */ protected $response; - /** @var \PHPUnit_Framework_MockObject_MockObject|\Zend\Http\Headers */ + /** @var \PHPUnit_Framework_MockObject_MockObject|\Laminas\Http\Headers */ protected $headers; protected function setUp() @@ -22,7 +22,7 @@ protected function setUp() ['getHeaders', 'send', 'clearHeader'] ); $this->headers = $this->createPartialMock( - \Zend\Http\Headers::class, + \Laminas\Http\Headers::class, ['has', 'get', 'current', 'removeHeader'] ); } @@ -117,7 +117,7 @@ public function testClearHeaderIfHeaderExistsAndWasFound() $this->headers->addHeaderLine('Header-name: header-value'); - $header = \Zend\Http\Header\GenericHeader::fromString('Header-name: header-value'); + $header = \Laminas\Http\Header\GenericHeader::fromString('Header-name: header-value'); $this->headers ->expects($this->once()) @@ -152,7 +152,7 @@ public function testClearHeaderAndHeaderNotExists() $this->headers->addHeaderLine('Header-name: header-value'); - $header = \Zend\Http\Header\GenericHeader::fromString('Header-name: header-value'); + $header = \Laminas\Http\Header\GenericHeader::fromString('Header-name: header-value'); $this->headers ->expects($this->once()) diff --git a/lib/internal/Magento/Framework/Image/Adapter/AbstractAdapter.php b/lib/internal/Magento/Framework/Image/Adapter/AbstractAdapter.php index b06f2f9e62397..8b983809e643f 100644 --- a/lib/internal/Magento/Framework/Image/Adapter/AbstractAdapter.php +++ b/lib/internal/Magento/Framework/Image/Adapter/AbstractAdapter.php @@ -24,8 +24,9 @@ abstract class AbstractAdapter implements AdapterInterface */ public $imageBackgroundColor = 0; - /** - * Position constants + /**#@+ + * Position constants. + * Used mainly for watermarks */ const POSITION_TOP_LEFT = 'top-left'; @@ -40,9 +41,10 @@ abstract class AbstractAdapter implements AdapterInterface const POSITION_TILE = 'tile'; const POSITION_CENTER = 'center'; + /**#@-*/ /** - * Default font size + * The size of the font to use as default */ const DEFAULT_FONT_SIZE = 15; diff --git a/lib/internal/Magento/Framework/Image/Adapter/AdapterInterface.php b/lib/internal/Magento/Framework/Image/Adapter/AdapterInterface.php index b31ed5c773495..a981df9d9fddc 100644 --- a/lib/internal/Magento/Framework/Image/Adapter/AdapterInterface.php +++ b/lib/internal/Magento/Framework/Image/Adapter/AdapterInterface.php @@ -28,6 +28,8 @@ interface AdapterInterface public function getColorAt($x, $y); /** + * Render image and return its binary contents + * * @see \Magento\Framework\Image\Adapter\AbstractAdapter::getImage * @return string */ @@ -99,6 +101,7 @@ public function crop($top = 0, $left = 0, $right = 0, $bottom = 0); /** * Save image to specific path. + * * If some folders of path does not exist they will be created * * @param null|string $destination diff --git a/lib/internal/Magento/Framework/Image/Adapter/Config.php b/lib/internal/Magento/Framework/Image/Adapter/Config.php index 636bbcdcbdb41..d4a28b4efc600 100644 --- a/lib/internal/Magento/Framework/Image/Adapter/Config.php +++ b/lib/internal/Magento/Framework/Image/Adapter/Config.php @@ -18,13 +18,13 @@ class Config implements ConfigInterface, UploadConfigInterface /** * Config path for the maximal image width value - * @deprecated + * @deprecated Used in a method that is deprecated */ const XML_PATH_MAX_WIDTH_IMAGE = 'system/upload_configuration/max_width'; /** * Config path for the maximal image height value - * @deprecated + * @deprecated Used in a method that is deprecated */ const XML_PATH_MAX_HEIGHT_IMAGE = 'system/upload_configuration/max_height'; diff --git a/lib/internal/Magento/Framework/Image/Adapter/ConfigInterface.php b/lib/internal/Magento/Framework/Image/Adapter/ConfigInterface.php index dab642b14ac6c..aa014b61e867a 100644 --- a/lib/internal/Magento/Framework/Image/Adapter/ConfigInterface.php +++ b/lib/internal/Magento/Framework/Image/Adapter/ConfigInterface.php @@ -12,11 +12,15 @@ interface ConfigInterface { /** + * Get adapter alias + * * @return string */ public function getAdapterAlias(); /** + * Get adapters + * * @return array */ public function getAdapters(); diff --git a/lib/internal/Magento/Framework/Image/Adapter/Gd2.php b/lib/internal/Magento/Framework/Image/Adapter/Gd2.php index 7e92b336cfdc0..5cb968b1e5441 100644 --- a/lib/internal/Magento/Framework/Image/Adapter/Gd2.php +++ b/lib/internal/Magento/Framework/Image/Adapter/Gd2.php @@ -11,7 +11,7 @@ * * @SuppressWarnings(PHPMD.ExcessiveClassComplexity) */ -class Gd2 extends \Magento\Framework\Image\Adapter\AbstractAdapter +class Gd2 extends AbstractAdapter { /** * Required extensions @@ -75,16 +75,6 @@ public function open($filename) $this->_getCallback('create', null, sprintf('Unsupported image format. File: %s', $this->_fileName)), $this->_fileName ); - $fileType = $this->getImageType(); - if (in_array($fileType, [IMAGETYPE_PNG, IMAGETYPE_GIF])) { - $this->_keepTransparency = true; - if ($this->_imageHandler) { - $isAlpha = $this->checkAlpha($this->_fileName); - if ($isAlpha) { - $this->_fillBackgroundColor($this->_imageHandler); - } - } - } } /** @@ -822,8 +812,9 @@ protected function _createEmptyImage($width, $height) * @param int $src_w * @param int $src_h * @param int $pct - * * @return bool + * @SuppressWarnings(PHPMD.CyclomaticComplexity) + * @SuppressWarnings(PHPMD.NPathComplexity) */ private function imagecopymergeWithAlphaFix( $dst_im, @@ -859,12 +850,24 @@ private function imagecopymergeWithAlphaFix( return false; } + if (false === imagesavealpha($tmpImg, true)) { + return false; + } + if (false === imagecopy($tmpImg, $src_im, 0, 0, 0, 0, $sizeX, $sizeY)) { return false; } - $transparancy = 127 - (($pct*127)/100); - if (false === imagefilter($tmpImg, IMG_FILTER_COLORIZE, 0, 0, 0, $transparancy)) { + $transparency = 127 - (($pct*127)/100); + if (false === imagefilter($tmpImg, IMG_FILTER_COLORIZE, 0, 0, 0, $transparency)) { + return false; + } + + if (false === imagealphablending($dst_im, true)) { + return false; + } + + if (false === imagesavealpha($dst_im, true)) { return false; } diff --git a/lib/internal/Magento/Framework/Image/Adapter/ImageMagick.php b/lib/internal/Magento/Framework/Image/Adapter/ImageMagick.php index cd49f283d33a7..24e036c02e718 100644 --- a/lib/internal/Magento/Framework/Image/Adapter/ImageMagick.php +++ b/lib/internal/Magento/Framework/Image/Adapter/ImageMagick.php @@ -5,7 +5,12 @@ */ namespace Magento\Framework\Image\Adapter; -class ImageMagick extends \Magento\Framework\Image\Adapter\AbstractAdapter +use Magento\Framework\Exception\LocalizedException; + +/** + * Image adapter from ImageMagick + */ +class ImageMagick extends AbstractAdapter { /** * The blur factor where > 1 is blurry, < 1 is sharp @@ -66,7 +71,7 @@ public function backgroundColor($color = null) * * @param string $filename * @return void - * @throws \Exception + * @throws \Magento\Framework\Exception\LocalizedException */ public function open($filename) { @@ -77,7 +82,11 @@ public function open($filename) try { $this->_imageHandler = new \Imagick($this->_fileName); } catch (\ImagickException $e) { - throw new \Exception(sprintf('Unsupported image format. File: %s', $this->_fileName), $e->getCode(), $e); + throw new LocalizedException( + __('Unsupported image format. File: %1', $this->_fileName), + $e, + $e->getCode() + ); } $this->backgroundColor(); @@ -86,12 +95,13 @@ public function open($filename) /** * Save image to specific path. + * * If some folders of path does not exist they will be created * * @param null|string $destination * @param null|string $newName * @return void - * @throws \Exception If destination path is not writable + * @throws \Magento\Framework\Exception\LocalizedException If destination path is not writable */ public function save($destination = null, $newName = null) { @@ -124,6 +134,8 @@ protected function _applyOptions() } /** + * Render image and return its binary contents + * * @see \Magento\Framework\Image\Adapter\AbstractAdapter::getImage * @return string */ @@ -241,7 +253,7 @@ public function crop($top = 0, $left = 0, $right = 0, $bottom = 0) * @param bool $tile * @return void * @throws \LogicException - * @throws \Exception + * @throws \Magento\Framework\Exception\LocalizedException * @SuppressWarnings(PHPMD.CyclomaticComplexity) * @SuppressWarnings(PHPMD.NPathComplexity) */ @@ -253,28 +265,12 @@ public function watermark($imagePath, $positionX = 0, $positionY = 0, $opacity = $this->_checkCanProcess(); $opacity = $this->getWatermarkImageOpacity() ? $this->getWatermarkImageOpacity() : $opacity; - $opacity = (double)number_format($opacity / 100, 1); - $watermark = new \Imagick($imagePath); - if ($this->getWatermarkWidth() && - $this->getWatermarkHeight() && - $this->getWatermarkPosition() != self::POSITION_STRETCH - ) { - $watermark->resizeImage( - $this->getWatermarkWidth(), - $this->getWatermarkHeight(), - \Imagick::FILTER_CUBIC, - self::BLUR_FACTOR - ); - } + $watermark = new \Imagick($imagePath); - if (method_exists($watermark, 'getImageAlphaChannel')) { - // available from imagick 6.4.0 - if ($watermark->getImageAlphaChannel() == 0) { - $watermark->setImageAlphaChannel(\Imagick::ALPHACHANNEL_OPAQUE); - } - } + $this->resizeWatermark($watermark); + $this->handleWatermarkAlphaChannel($watermark); $compositeChannels = \Imagick::CHANNEL_ALL; $watermark->evaluateImage(\Imagick::EVALUATE_MULTIPLY, $opacity, \Imagick::CHANNEL_OPACITY); @@ -307,33 +303,16 @@ public function watermark($imagePath, $positionX = 0, $positionY = 0, $opacity = try { if ($tile) { - $offsetX = $positionX; - $offsetY = $positionY; - while ($offsetY <= $this->_imageSrcHeight + $watermark->getImageHeight()) { - while ($offsetX <= $this->_imageSrcWidth + $watermark->getImageWidth()) { - $this->_imageHandler->compositeImage( - $watermark, - \Imagick::COMPOSITE_OVER, - $offsetX, - $offsetY, - $compositeChannels - ); - $offsetX += $watermark->getImageWidth(); - } - $offsetX = $positionX; - $offsetY += $watermark->getImageHeight(); - } + $this->addTiledWatermark($positionX, $positionY, $watermark, $compositeChannels); } else { - $this->_imageHandler->compositeImage( - $watermark, - \Imagick::COMPOSITE_OVER, - $positionX, - $positionY, - $compositeChannels - ); + $this->addSingleWatermark($positionX, $positionY, $watermark, $compositeChannels); } } catch (\ImagickException $e) { - throw new \Exception('Unable to create watermark.', $e->getCode(), $e); + throw new LocalizedException( + __('Unable to create watermark.'), + $e, + $e->getCode() + ); } // merge layers @@ -346,12 +325,14 @@ public function watermark($imagePath, $positionX = 0, $positionY = 0, $opacity = * Checks required dependencies * * @return void - * @throws \Exception If some of dependencies are missing + * @throws \Magento\Framework\Exception\LocalizedException If some of dependencies are missing */ public function checkDependencies() { - if (!class_exists('\Imagick', false)) { - throw new \Exception("Required PHP extension 'Imagick' was not loaded."); + if (!class_exists('Imagick', false)) { + throw new LocalizedException( + __('Required PHP extension \'Imagick\' was not loaded.') + ); } } @@ -499,4 +480,86 @@ protected function _getImagickPixelObject($color = null) { return new \ImagickPixel($color); } + + /** + * Resizes watermark to desired size, when it is not stretched + * + * @param \Imagick $watermark + */ + private function resizeWatermark(\Imagick $watermark): void + { + if ($this->getWatermarkWidth() && + $this->getWatermarkHeight() && + $this->getWatermarkPosition() != self::POSITION_STRETCH + ) { + $watermark->resizeImage( + $this->getWatermarkWidth(), + $this->getWatermarkHeight(), + \Imagick::FILTER_CUBIC, + self::BLUR_FACTOR + ); + } + } + + /** + * Keeps transparenty if watermark is transparent + * + * @param \Imagick $watermark + */ + private function handleWatermarkAlphaChannel(\Imagick $watermark): void + { + if (method_exists($watermark, 'getImageAlphaChannel')) { + // available from imagick 6.4.0 + if ($watermark->getImageAlphaChannel() == 0) { + $watermark->setImageAlphaChannel(\Imagick::ALPHACHANNEL_OPAQUE); + } + } + } + + /** + * Add tiled watermark at starting given X,Y position + * + * @param int $positionX + * @param int $positionY + * @param \Imagick $watermark + * @param bool $compositeChannels + */ + private function addTiledWatermark($positionX, $positionY, \Imagick $watermark, $compositeChannels): void + { + $offsetX = $positionX; + $offsetY = $positionY; + while ($offsetY <= $this->_imageSrcHeight + $watermark->getImageHeight()) { + while ($offsetX <= $this->_imageSrcWidth + $watermark->getImageWidth()) { + $this->_imageHandler->compositeImage( + $watermark, + \Imagick::COMPOSITE_OVER, + $offsetX, + $offsetY, + $compositeChannels + ); + $offsetX += $watermark->getImageWidth(); + } + $offsetX = $positionX; + $offsetY += $watermark->getImageHeight(); + } + } + + /** + * Add watermark at given X,Y position + * + * @param int $positionX + * @param int $positionY + * @param \Imagick $watermark + * @param bool $compositeChannels + */ + private function addSingleWatermark($positionX, int $positionY, \Imagick $watermark, bool $compositeChannels): void + { + $this->_imageHandler->compositeImage( + $watermark, + \Imagick::COMPOSITE_OVER, + $positionX, + $positionY, + $compositeChannels + ); + } } diff --git a/lib/internal/Magento/Framework/Image/Adapter/UploadConfigInterface.php b/lib/internal/Magento/Framework/Image/Adapter/UploadConfigInterface.php index 0a2dbefff8ee0..54fa7a57c7c7e 100644 --- a/lib/internal/Magento/Framework/Image/Adapter/UploadConfigInterface.php +++ b/lib/internal/Magento/Framework/Image/Adapter/UploadConfigInterface.php @@ -9,6 +9,7 @@ /** * Interface UploadConfigInterface + * * @deprecated moved to proper namespace and extended * @see \Magento\Backend\Model\Image\UploadResizeConfigInterface; */ diff --git a/lib/internal/Magento/Framework/Image/AdapterFactory.php b/lib/internal/Magento/Framework/Image/AdapterFactory.php index 8cb7b65ae73c9..4a380bab433db 100644 --- a/lib/internal/Magento/Framework/Image/AdapterFactory.php +++ b/lib/internal/Magento/Framework/Image/AdapterFactory.php @@ -5,6 +5,9 @@ */ namespace Magento\Framework\Image; +/** + * Factory for Adapters that Image Library is using to process images + */ class AdapterFactory { /** diff --git a/lib/internal/Magento/Framework/Image/Factory.php b/lib/internal/Magento/Framework/Image/Factory.php index a0fb6bef4ae46..06cada6504058 100644 --- a/lib/internal/Magento/Framework/Image/Factory.php +++ b/lib/internal/Magento/Framework/Image/Factory.php @@ -8,6 +8,9 @@ use Magento\Framework\ObjectManagerInterface; +/** + * Factory for object to manipulate images + */ class Factory { /** diff --git a/lib/internal/Magento/Framework/Interception/Config/Config.php b/lib/internal/Magento/Framework/Interception/Config/Config.php index 3f16e9275bd08..abf2a6d9d57b7 100644 --- a/lib/internal/Magento/Framework/Interception/Config/Config.php +++ b/lib/internal/Magento/Framework/Interception/Config/Config.php @@ -187,8 +187,6 @@ public function hasPlugins($type) */ private function initializeUncompiled($classDefinitions = []) { - $this->cacheManager->clean($this->_cacheId); - $this->generateIntercepted($classDefinitions); $this->cacheManager->save($this->_cacheId, $this->_intercepted); diff --git a/lib/internal/Magento/Framework/Locale/Config.php b/lib/internal/Magento/Framework/Locale/Config.php index c2d0147c7fdc8..08f66e25be773 100644 --- a/lib/internal/Magento/Framework/Locale/Config.php +++ b/lib/internal/Magento/Framework/Locale/Config.php @@ -108,6 +108,7 @@ class Config implements \Magento\Framework\Locale\ConfigInterface 'es_VE', /*Spanish (Venezuela)*/ 'en_IE', /*English (Ireland)*/ 'es_BO', /*Spanish (Bolivia)*/ + 'es_US', /*Spanish (United States)*/ ]; /** diff --git a/lib/internal/Magento/Framework/Lock/Backend/Cache.php b/lib/internal/Magento/Framework/Lock/Backend/Cache.php index dfe6bbb828352..5e1cdd706e326 100644 --- a/lib/internal/Magento/Framework/Lock/Backend/Cache.php +++ b/lib/internal/Magento/Framework/Lock/Backend/Cache.php @@ -37,6 +37,10 @@ public function __construct(FrontendInterface $cache) */ public function lock(string $name, int $timeout = -1): bool { + if ((bool)$this->cache->test($this->getIdentifier($name))) { + return false; + } + return $this->cache->save('1', $this->getIdentifier($name), [], $timeout); } @@ -45,7 +49,7 @@ public function lock(string $name, int $timeout = -1): bool */ public function unlock(string $name): bool { - return $this->cache->remove($this->getIdentifier($name)); + return (bool)$this->cache->remove($this->getIdentifier($name)); } /** diff --git a/lib/internal/Magento/Framework/Lock/Backend/FileLock.php b/lib/internal/Magento/Framework/Lock/Backend/FileLock.php index d168e910a4ab7..dd8eda9621667 100644 --- a/lib/internal/Magento/Framework/Lock/Backend/FileLock.php +++ b/lib/internal/Magento/Framework/Lock/Backend/FileLock.php @@ -91,6 +91,7 @@ public function lock(string $name, int $timeout = -1): bool while (!$this->tryToLock($fileResource)) { if (!$skipDeadline && $deadline <= microtime(true)) { + $this->tryToUnlock($fileResource); $this->fileDriver->fileClose($fileResource); return false; } @@ -124,6 +125,7 @@ public function isLocked(string $name): bool } else { $result = true; } + $this->tryToUnlock($fileResource); $this->fileDriver->fileClose($fileResource); } } catch (FileSystemException $exception) { diff --git a/lib/internal/Magento/Framework/Lock/Test/Unit/Backend/CacheTest.php b/lib/internal/Magento/Framework/Lock/Test/Unit/Backend/CacheTest.php new file mode 100644 index 0000000000000..2ec812092d654 --- /dev/null +++ b/lib/internal/Magento/Framework/Lock/Test/Unit/Backend/CacheTest.php @@ -0,0 +1,64 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +declare(strict_types=1); + +namespace Magento\Framework\Lock\Test\Unit\Backend; + +use Magento\Framework\Cache\FrontendInterface; +use Magento\Framework\Lock\Backend\Cache; +use Magento\Framework\TestFramework\Unit\Helper\ObjectManager as ObjectManagerHelper; +use PHPUnit\Framework\MockObject\MockObject; +use PHPUnit\Framework\TestCase; + +class CacheTest extends TestCase +{ + const LOCK_PREFIX = 'LOCKED_RECORD_INFO_'; + + /** + * @var FrontendInterface|MockObject + */ + private $frontendCacheMock; + + /** + * @var Cache + */ + private $cache; + + /** + * @inheritDoc + */ + public function setUp() + { + $this->frontendCacheMock = $this->createMock(FrontendInterface::class); + + $objectManager = new ObjectManagerHelper($this); + + $this->cache = $objectManager->getObject( + Cache::class, + [ + 'cache' => $this->frontendCacheMock + ] + ); + } + + /** + * Verify released a lock. + * + * @return void + */ + public function testUnlock(): void + { + $identifier = 'lock_name'; + + $this->frontendCacheMock + ->expects($this->once()) + ->method('remove') + ->with(self::LOCK_PREFIX . $identifier) + ->willReturn(true); + + $this->assertEquals(true, $this->cache->unlock($identifier)); + } +} diff --git a/lib/internal/Magento/Framework/Mail/EmailMessage.php b/lib/internal/Magento/Framework/Mail/EmailMessage.php index 02c75977cd093..5083d5475465e 100644 --- a/lib/internal/Magento/Framework/Mail/EmailMessage.php +++ b/lib/internal/Magento/Framework/Mail/EmailMessage.php @@ -8,12 +8,12 @@ namespace Magento\Framework\Mail; use Magento\Framework\Mail\Exception\InvalidArgumentException; -use Zend\Mail\Address as ZendAddress; -use Zend\Mail\AddressList; -use Zend\Mime\Message as ZendMimeMessage; +use Laminas\Mail\Address as LaminasAddress; +use Laminas\Mail\AddressList; +use Laminas\Mime\Message as LaminasMimeMessage; /** - * Email message + * Magento Framework Email message */ class EmailMessage extends Message implements EmailMessageInterface { @@ -28,8 +28,6 @@ class EmailMessage extends Message implements EmailMessageInterface private $addressFactory; /** - * EmailMessage constructor - * * @param MimeMessageInterface $body * @param array $to * @param MimeMessageInterfaceFactory $mimeMessageFactory @@ -61,7 +59,7 @@ public function __construct( ?string $encoding = 'utf-8' ) { parent::__construct($encoding); - $mimeMessage = new ZendMimeMessage(); + $mimeMessage = new LaminasMimeMessage(); $mimeMessage->setParts($body->getParts()); $this->zendMessage->setBody($mimeMessage); if ($subject) { @@ -153,15 +151,15 @@ public function getReplyTo(): ?array */ public function getSender(): ?Address { - /** @var ZendAddress $zendSender */ - if (!$zendSender = $this->zendMessage->getSender()) { + /** @var LaminasAddress $laminasSender */ + if (!$laminasSender = $this->zendMessage->getSender()) { return null; } return $this->addressFactory->create( [ - 'email' => $zendSender->getEmail(), - 'name' => $zendSender->getName() + 'email' => $laminasSender->getEmail(), + 'name' => $laminasSender->getName() ] ); } @@ -222,11 +220,11 @@ private function convertAddressListToAddressArray(AddressList $addressList): arr */ private function convertAddressArrayToAddressList(array $arrayList): AddressList { - $zendAddressList = new AddressList(); + $laminasAddressList = new AddressList(); foreach ($arrayList as $address) { - $zendAddressList->add($address->getEmail(), $address->getName()); + $laminasAddressList->add($address->getEmail(), $address->getName()); } - return $zendAddressList; + return $laminasAddressList; } } diff --git a/lib/internal/Magento/Framework/Mail/Message.php b/lib/internal/Magento/Framework/Mail/Message.php index 0e4d79aac9331..b140676466e5f 100644 --- a/lib/internal/Magento/Framework/Mail/Message.php +++ b/lib/internal/Magento/Framework/Mail/Message.php @@ -5,19 +5,19 @@ */ namespace Magento\Framework\Mail; -use Zend\Mime\Mime; -use Zend\Mime\Part; +use Laminas\Mime\Mime; +use Laminas\Mime\Part; /** * Class Message for email transportation * - * @deprecated + * @deprecated a new message implementation was added * @see \Magento\Framework\Mail\EmailMessage */ class Message implements MailMessageInterface { /** - * @var \Zend\Mail\Message + * @var \Laminas\Mail\Message */ protected $zendMessage; @@ -35,7 +35,7 @@ class Message implements MailMessageInterface */ public function __construct($charset = 'utf-8') { - $this->zendMessage = new \Zend\Mail\Message(); + $this->zendMessage = new \Laminas\Mail\Message(); $this->zendMessage->setEncoding($charset); } @@ -164,7 +164,7 @@ public function getRawMessage() * * @param string $body * @param string $messageType - * @return \Zend\Mime\Message + * @return \Laminas\Mime\Message */ private function createMimeFromString($body, $messageType) { @@ -172,7 +172,7 @@ private function createMimeFromString($body, $messageType) $part->setCharset($this->zendMessage->getEncoding()); $part->setEncoding(Mime::ENCODING_QUOTEDPRINTABLE); $part->setType($messageType); - $mimeMessage = new \Zend\Mime\Message(); + $mimeMessage = new \Laminas\Mime\Message(); $mimeMessage->addPart($part); return $mimeMessage; } diff --git a/lib/internal/Magento/Framework/Mail/MimeInterface.php b/lib/internal/Magento/Framework/Mail/MimeInterface.php index 026dd188d1685..a7910e195a160 100644 --- a/lib/internal/Magento/Framework/Mail/MimeInterface.php +++ b/lib/internal/Magento/Framework/Mail/MimeInterface.php @@ -9,7 +9,7 @@ /** * Interface MimeInterface used providing constants * - * @see \Zend\Mime\Mime + * @see \Laminas\Mime\Mime */ interface MimeInterface { diff --git a/lib/internal/Magento/Framework/Mail/MimeMessage.php b/lib/internal/Magento/Framework/Mail/MimeMessage.php index 4d783dafd1d7a..3482fb33bd848 100644 --- a/lib/internal/Magento/Framework/Mail/MimeMessage.php +++ b/lib/internal/Magento/Framework/Mail/MimeMessage.php @@ -7,15 +7,15 @@ namespace Magento\Framework\Mail; -use Zend\Mime\Message as ZendMimeMessage; +use Laminas\Mime\Message as LaminasMimeMessage; /** - * Class MimeMessage + * Magento Framework Mime message */ class MimeMessage implements MimeMessageInterface { /** - * @var ZendMimeMessage + * @var LaminasMimeMessage */ private $mimeMessage; @@ -26,7 +26,7 @@ class MimeMessage implements MimeMessageInterface */ public function __construct(array $parts) { - $this->mimeMessage = new ZendMimeMessage(); + $this->mimeMessage = new LaminasMimeMessage(); $this->mimeMessage->setParts($parts); } diff --git a/lib/internal/Magento/Framework/Mail/MimePart.php b/lib/internal/Magento/Framework/Mail/MimePart.php index a43ed4b36e072..d02ebffd5dc7b 100644 --- a/lib/internal/Magento/Framework/Mail/MimePart.php +++ b/lib/internal/Magento/Framework/Mail/MimePart.php @@ -8,7 +8,7 @@ namespace Magento\Framework\Mail; use Magento\Framework\Mail\Exception\InvalidArgumentException; -use Zend\Mime\Part as ZendMimePart; +use Laminas\Mime\Part as LaminasMimePart; /** * @inheritDoc @@ -21,7 +21,7 @@ class MimePart implements MimePartInterface public const CHARSET_UTF8 = 'utf-8'; /** - * @var ZendMimePart + * @var LaminasMimePart */ private $mimePart; @@ -61,7 +61,7 @@ public function __construct( ?bool $isStream = null ) { try { - $this->mimePart = new ZendMimePart($content); + $this->mimePart = new LaminasMimePart($content); } catch (\Exception $e) { throw new InvalidArgumentException($e->getMessage()); } diff --git a/lib/internal/Magento/Framework/Mail/Transport.php b/lib/internal/Magento/Framework/Mail/Transport.php index 8a7ace17cc9a0..c1772075baaf3 100644 --- a/lib/internal/Magento/Framework/Mail/Transport.php +++ b/lib/internal/Magento/Framework/Mail/Transport.php @@ -7,15 +7,18 @@ use Magento\Framework\Exception\MailException; use Magento\Framework\Phrase; -use Zend\Mail\Message as ZendMessage; -use Zend\Mail\Transport\Sendmail; +use Laminas\Mail\Message as LaminasMessage; +use Laminas\Mail\Transport\Sendmail; +/** + * Mail transport + */ class Transport implements \Magento\Framework\Mail\TransportInterface { /** * @var Sendmail */ - private $zendTransport; + private $laminasTransport; /** * @var MessageInterface @@ -28,7 +31,7 @@ class Transport implements \Magento\Framework\Mail\TransportInterface */ public function __construct(MessageInterface $message, $parameters = null) { - $this->zendTransport = new Sendmail($parameters); + $this->laminasTransport = new Sendmail($parameters); $this->message = $message; } @@ -38,8 +41,8 @@ public function __construct(MessageInterface $message, $parameters = null) public function sendMessage() { try { - $this->zendTransport->send( - ZendMessage::fromString($this->message->getRawMessage()) + $this->laminasTransport->send( + LaminasMessage::fromString($this->message->getRawMessage()) ); } catch (\Exception $e) { throw new MailException(new Phrase($e->getMessage()), $e); diff --git a/lib/internal/Magento/Framework/MessageQueue/Code/Generator/RemoteServiceGenerator.php b/lib/internal/Magento/Framework/MessageQueue/Code/Generator/RemoteServiceGenerator.php index 0cd62963c547c..160d2b4fa8d5a 100644 --- a/lib/internal/Magento/Framework/MessageQueue/Code/Generator/RemoteServiceGenerator.php +++ b/lib/internal/Magento/Framework/MessageQueue/Code/Generator/RemoteServiceGenerator.php @@ -11,7 +11,7 @@ use Magento\Framework\Communication\ConfigInterface as CommunicationConfig; use Magento\Framework\MessageQueue\Code\Generator\Config\RemoteServiceReader\Communication as RemoteServiceReader; use Magento\Framework\Reflection\MethodsMap as ServiceMethodsMap; -use Zend\Code\Reflection\MethodReflection; +use Laminas\Code\Reflection\MethodReflection; /** * Code generator for remote services. @@ -74,7 +74,7 @@ public function __construct( } /** - * {@inheritdoc} + * @inheritDoc */ protected function _getDefaultConstructorDefinition() { @@ -97,7 +97,7 @@ protected function _getDefaultConstructorDefinition() } /** - * {@inheritdoc} + * @inheritDoc */ protected function _getClassProperties() { @@ -119,19 +119,19 @@ protected function _getClassProperties() } /** - * {@inheritdoc} + * @inheritDoc */ protected function _getClassMethods() { $methods = [$this->_getDefaultConstructorDefinition()]; $interfaceMethodsMap = $this->serviceMethodsMap->getMethodsMap($this->getSourceClassName()); foreach (array_keys($interfaceMethodsMap) as $methodName) { - // Uses Zend Reflection instead MethodsMap service, because second does not support features of PHP 7.x + // Uses Laminas Reflection instead MethodsMap service, because second does not support features of PHP 7.x $methodReflection = new MethodReflection($this->getSourceClassName(), $methodName); $sourceMethodParameters = $methodReflection->getParameters(); $methodParameters = []; $topicParameters = []; - /** @var \Zend\Code\Reflection\ParameterReflection $methodParameter */ + /** @var \Laminas\Code\Reflection\ParameterReflection $methodParameter */ foreach ($sourceMethodParameters as $methodParameter) { $parameterName = $methodParameter->getName(); $parameter = [ @@ -166,7 +166,7 @@ protected function _getClassMethods() } /** - * {@inheritdoc} + * @inheritDoc */ protected function _validateData() { @@ -175,7 +175,7 @@ protected function _validateData() } /** - * {@inheritdoc} + * @inheritDoc */ protected function _generateCode() { diff --git a/lib/internal/Magento/Framework/MessageQueue/Topology/Config.php b/lib/internal/Magento/Framework/MessageQueue/Topology/Config.php index 7add2bd286e1d..10fce32abade0 100644 --- a/lib/internal/Magento/Framework/MessageQueue/Topology/Config.php +++ b/lib/internal/Magento/Framework/MessageQueue/Topology/Config.php @@ -24,9 +24,9 @@ class Config implements ConfigInterface private $exchangeIterator; /** - * Exchange config data iterator. + * Queue config data iterator. * - * @var ExchangeIterator + * @var QueueIterator */ private $queueIterator; @@ -43,7 +43,7 @@ public function __construct(ExchangeIterator $exchangeIterator, QueueIterator $q } /** - * {@inheritdoc} + * @inheritdoc */ public function getExchange($name, $connection) { @@ -63,7 +63,7 @@ public function getExchange($name, $connection) } /** - * {@inheritdoc} + * @inheritdoc */ public function getExchanges() { @@ -71,7 +71,7 @@ public function getExchanges() } /** - * {@inheritdoc} + * @inheritdoc */ public function getQueues() { diff --git a/lib/internal/Magento/Framework/MessageQueue/registration.php b/lib/internal/Magento/Framework/MessageQueue/registration.php index aafb0fcffa7c5..d6b908671f9b7 100644 --- a/lib/internal/Magento/Framework/MessageQueue/registration.php +++ b/lib/internal/Magento/Framework/MessageQueue/registration.php @@ -4,6 +4,6 @@ * See COPYING.txt for license details. */ -use \Magento\Framework\Component\ComponentRegistrar; +use Magento\Framework\Component\ComponentRegistrar; ComponentRegistrar::register(ComponentRegistrar::LIBRARY, 'magento/framework-message-queue', __DIR__); diff --git a/lib/internal/Magento/Framework/Model/AbstractExtensibleModel.php b/lib/internal/Magento/Framework/Model/AbstractExtensibleModel.php index 949e002a14208..5484103cc27ef 100644 --- a/lib/internal/Magento/Framework/Model/AbstractExtensibleModel.php +++ b/lib/internal/Magento/Framework/Model/AbstractExtensibleModel.php @@ -15,6 +15,8 @@ * This class defines basic data structure of how custom attributes are stored in an ExtensibleModel. * Implementations may choose to process custom attributes as their persistence requires them to. * @SuppressWarnings(PHPMD.NumberOfChildren) + * phpcs:disable Magento2.Classes.AbstractApi + * @api */ abstract class AbstractExtensibleModel extends AbstractModel implements \Magento\Framework\Api\CustomAttributesDataInterface @@ -74,6 +76,28 @@ public function __construct( } } + /** + * Convert the custom attributes array format to map format + * + * The method \Magento\Framework\Reflection\DataObjectProcessor::buildOutputDataArray generates a custom_attributes + * array representation where each custom attribute is a sub-array with a `attribute_code and value key. + * This method maps such an array to the plain code => value map format exprected by filterCustomAttributes + * + * @param array[] $customAttributesData + * @return array + */ + private function flattenCustomAttributesArrayToMap(array $customAttributesData): array + { + return array_reduce( + $customAttributesData, + function (array $acc, array $customAttribute): array { + $acc[$customAttribute['attribute_code']] = $customAttribute['value']; + return $acc; + }, + [] + ); + } + /** * Verify custom attributes set on $data and unset if not a valid custom attribute * @@ -85,9 +109,12 @@ protected function filterCustomAttributes($data) if (empty($data[self::CUSTOM_ATTRIBUTES])) { return $data; } - $customAttributesCodes = $this->getCustomAttributesCodes(); + if (isset($data[self::CUSTOM_ATTRIBUTES][0])) { + $data[self::CUSTOM_ATTRIBUTES] = $this->flattenCustomAttributesArrayToMap($data[self::CUSTOM_ATTRIBUTES]); + } + $customAttributesCodes = $this->getCustomAttributesCodes(); $data[self::CUSTOM_ATTRIBUTES] = array_intersect_key( - (array)$data[self::CUSTOM_ATTRIBUTES], + (array) $data[self::CUSTOM_ATTRIBUTES], array_flip($customAttributesCodes) ); foreach ($data[self::CUSTOM_ATTRIBUTES] as $code => $value) { @@ -102,8 +129,6 @@ protected function filterCustomAttributes($data) /** * Initialize customAttributes based on existing data - * - * @return $this */ protected function initializeCustomAttributes() { diff --git a/lib/internal/Magento/Framework/Module/Dir.php b/lib/internal/Magento/Framework/Module/Dir.php index e6b60453b9577..4a03d0edc49fd 100644 --- a/lib/internal/Magento/Framework/Module/Dir.php +++ b/lib/internal/Magento/Framework/Module/Dir.php @@ -1,7 +1,5 @@ <?php /** - * Encapsulates directories structure of a Magento module - * * Copyright © Magento, Inc. All rights reserved. * See COPYING.txt for license details. */ @@ -10,6 +8,9 @@ use Magento\Framework\Component\ComponentRegistrar; use Magento\Framework\Component\ComponentRegistrarInterface; +/** + * Encapsulates directories structure of a Magento module + */ class Dir { /**#@+ @@ -22,6 +23,14 @@ class Dir const MODULE_SETUP_DIR = 'Setup'; /**#@-*/ + private const ALLOWED_DIR_TYPES = [ + self::MODULE_ETC_DIR => true, + self::MODULE_I18N_DIR => true, + self::MODULE_VIEW_DIR => true, + self::MODULE_CONTROLLER_DIR => true, + self::MODULE_SETUP_DIR => true + ]; + /**#@-*/ private $componentRegistrar; @@ -52,13 +61,7 @@ public function getDir($moduleName, $type = '') } if ($type) { - if (!in_array($type, [ - self::MODULE_ETC_DIR, - self::MODULE_I18N_DIR, - self::MODULE_VIEW_DIR, - self::MODULE_CONTROLLER_DIR, - self::MODULE_SETUP_DIR - ])) { + if (!isset(self::ALLOWED_DIR_TYPES[$type])) { throw new \InvalidArgumentException("Directory type '{$type}' is not recognized."); } $path .= '/' . $type; diff --git a/lib/internal/Magento/Framework/Module/ModuleList.php b/lib/internal/Magento/Framework/Module/ModuleList.php index 6ee061cffb3d0..5a60a1c102b05 100644 --- a/lib/internal/Magento/Framework/Module/ModuleList.php +++ b/lib/internal/Magento/Framework/Module/ModuleList.php @@ -59,7 +59,7 @@ public function __construct(DeploymentConfig $config, ModuleList\Loader $loader) } /** - * {@inheritdoc} + * @inheritdoc * * Note that this triggers loading definitions of all existing modules in the system. * Use this method only when you actually need modules' declared meta-information. @@ -84,8 +84,7 @@ public function getAll() } /** - * {@inheritdoc} - * @see has() + * @inheritdoc */ public function getOne($name) { @@ -94,7 +93,7 @@ public function getOne($name) } /** - * {@inheritdoc} + * @inheritdoc */ public function getNames() { @@ -107,7 +106,7 @@ public function getNames() } /** - * {@inheritdoc} + * @inheritdoc */ public function has($name) { @@ -136,12 +135,16 @@ public function isModuleInfoAvailable() * Loads configuration data only * * @return void + * @throws \Magento\Framework\Exception\FileSystemException + * @throws \Magento\Framework\Exception\RuntimeException */ private function loadConfigData() { - $this->config->resetData(); - if (null === $this->configData && null !== $this->config->get(ConfigOptionsListConstants::KEY_MODULES)) { - $this->configData = $this->config->get(ConfigOptionsListConstants::KEY_MODULES); + if (null === $this->configData) { + $this->config->resetData(); + if (null !== $this->config->get(ConfigOptionsListConstants::KEY_MODULES)) { + $this->configData = $this->config->get(ConfigOptionsListConstants::KEY_MODULES); + } } } } diff --git a/lib/internal/Magento/Framework/Module/ModuleList/Loader.php b/lib/internal/Magento/Framework/Module/ModuleList/Loader.php index b1d21a6db5f16..7e484407d7a54 100644 --- a/lib/internal/Magento/Framework/Module/ModuleList/Loader.php +++ b/lib/internal/Magento/Framework/Module/ModuleList/Loader.php @@ -111,8 +111,7 @@ public function load(array $exclude = []) * </code> * * @return \Traversable - * - * @author Josh Di Fabio <joshdifabio@gmail.com> + * @throws \Magento\Framework\Exception\FileSystemException */ private function getModuleConfigs() { diff --git a/lib/internal/Magento/Framework/Module/Test/Unit/ModuleListTest.php b/lib/internal/Magento/Framework/Module/Test/Unit/ModuleListTest.php index 9b4f238725138..3142bbbc6771a 100644 --- a/lib/internal/Magento/Framework/Module/Test/Unit/ModuleListTest.php +++ b/lib/internal/Magento/Framework/Module/Test/Unit/ModuleListTest.php @@ -47,7 +47,7 @@ protected function setUp() public function testGetAll() { - $this->config->expects($this->exactly(2))->method('resetData'); + $this->config->expects($this->once())->method('resetData'); $this->setLoadAllExpectation(); $this->setLoadConfigExpectation(); $expected = ['foo' => self::$allFixture['foo']]; @@ -65,7 +65,7 @@ public function testGetAllNoData() public function testGetOne() { - $this->config->expects($this->exactly(2))->method('resetData'); + $this->config->expects($this->once())->method('resetData'); $this->setLoadAllExpectation(); $this->setLoadConfigExpectation(); $this->assertSame(['key' => 'value'], $this->model->getOne('foo')); @@ -74,7 +74,7 @@ public function testGetOne() public function testGetNames() { - $this->config->expects($this->exactly(2))->method('resetData'); + $this->config->expects($this->once())->method('resetData'); $this->setLoadAllExpectation(false); $this->setLoadConfigExpectation(); $this->assertSame(['foo'], $this->model->getNames()); @@ -83,7 +83,7 @@ public function testGetNames() public function testHas() { - $this->config->expects($this->exactly(2))->method('resetData'); + $this->config->expects($this->once())->method('resetData'); $this->setLoadAllExpectation(false); $this->setLoadConfigExpectation(); $this->assertTrue($this->model->has('foo')); diff --git a/lib/internal/Magento/Framework/Oauth/Helper/Request.php b/lib/internal/Magento/Framework/Oauth/Helper/Request.php index 548c4a5990efe..1134275270bc0 100644 --- a/lib/internal/Magento/Framework/Oauth/Helper/Request.php +++ b/lib/internal/Magento/Framework/Oauth/Helper/Request.php @@ -6,8 +6,11 @@ namespace Magento\Framework\Oauth\Helper; use Magento\Framework\App\RequestInterface; -use Zend\Uri\UriFactory; +use Laminas\Uri\UriFactory; +/** + * Request helper + */ class Request { /**#@+ @@ -110,7 +113,7 @@ protected function _processRequest($authHeaderValue, $contentTypeHeader, $reques /** * Retrieve protocol parameters from query string * - * @param array &$protocolParams + * @param array $protocolParams * @param array $queryString * @return void */ diff --git a/lib/internal/Magento/Framework/ObjectManager/Code/Generator/Repository.php b/lib/internal/Magento/Framework/ObjectManager/Code/Generator/Repository.php index be484f074342d..b0687d619fac6 100644 --- a/lib/internal/Magento/Framework/ObjectManager/Code/Generator/Repository.php +++ b/lib/internal/Magento/Framework/ObjectManager/Code/Generator/Repository.php @@ -11,8 +11,8 @@ use Magento\Framework\Api\SearchCriteriaInterface; use Magento\Framework\Exception\NoSuchEntityException; use Magento\Framework\Exception\InputException; -use Zend\Code\Reflection\MethodReflection; -use Zend\Code\Reflection\ParameterReflection; +use Laminas\Code\Reflection\MethodReflection; +use Laminas\Code\Reflection\ParameterReflection; /** * Class Repository @@ -23,7 +23,7 @@ class Repository extends \Magento\Framework\Code\Generator\EntityAbstract { /** - * Entity type + * Entity type repository */ const ENTITY_TYPE = 'repository'; @@ -130,6 +130,7 @@ protected function _getSourcePersistorPropertyName() /** * Returns source collection factory property name + * * @return string */ protected function _getSourceCollectionFactoryPropertyName() @@ -620,7 +621,7 @@ protected function _getClassMethods() } /** - * {@inheritdoc} + * @inheritDoc */ protected function _validateData() { diff --git a/lib/internal/Magento/Framework/ObjectManager/etc/config.xsd b/lib/internal/Magento/Framework/ObjectManager/etc/config.xsd index c41e0afbd8054..6ca93d3ab78c3 100644 --- a/lib/internal/Magento/Framework/ObjectManager/etc/config.xsd +++ b/lib/internal/Magento/Framework/ObjectManager/etc/config.xsd @@ -14,6 +14,7 @@ <xs:complexContent> <xs:extension base="argumentType"> <xs:attribute name="translatable" use="optional" type="xs:boolean" /> + <xs:attribute name="sortOrder" use="optional" type="xs:integer"/> </xs:extension> </xs:complexContent> </xs:complexType> diff --git a/lib/internal/Magento/Framework/Phrase/Renderer/Translate.php b/lib/internal/Magento/Framework/Phrase/Renderer/Translate.php index c9329caf49370..34f47a02bfcf4 100644 --- a/lib/internal/Magento/Framework/Phrase/Renderer/Translate.php +++ b/lib/internal/Magento/Framework/Phrase/Renderer/Translate.php @@ -1,7 +1,5 @@ <?php /** - * Translate Phrase renderer - * * Copyright © Magento, Inc. All rights reserved. * See COPYING.txt for license details. */ @@ -11,6 +9,9 @@ use Magento\Framework\TranslateInterface; use Psr\Log\LoggerInterface; +/** + * Translate Phrase renderer + */ class Translate implements RendererInterface { /** @@ -50,8 +51,7 @@ public function render(array $source, array $arguments) { $text = end($source); /* If phrase contains escaped quotes then use translation for phrase with non-escaped quote */ - $text = str_replace('\"', '"', $text); - $text = str_replace("\\'", "'", $text); + $text = strtr($text, ['\"' => '"', "\\'" => "'"]); try { $data = $this->translator->getData(); diff --git a/lib/internal/Magento/Framework/Pricing/PriceCurrencyInterface.php b/lib/internal/Magento/Framework/Pricing/PriceCurrencyInterface.php index eb379b54d257f..ab3d26f3e0c6c 100644 --- a/lib/internal/Magento/Framework/Pricing/PriceCurrencyInterface.php +++ b/lib/internal/Magento/Framework/Pricing/PriceCurrencyInterface.php @@ -13,9 +13,6 @@ */ interface PriceCurrencyInterface { - /** - * Default precision - */ const DEFAULT_PRECISION = 2; /** @@ -47,7 +44,7 @@ public function convertAndRound($amount, $scope = null, $currency = null, $preci * @param int $precision * @param null|string|bool|int|\Magento\Framework\App\ScopeInterface $scope * @param \Magento\Framework\Model\AbstractModel|string|null $currency - * @return float + * @return string */ public function format( $amount, diff --git a/lib/internal/Magento/Framework/Reflection/ExtensionAttributesProcessor.php b/lib/internal/Magento/Framework/Reflection/ExtensionAttributesProcessor.php index aa978e7f337cc..8f3e31811ccec 100644 --- a/lib/internal/Magento/Framework/Reflection/ExtensionAttributesProcessor.php +++ b/lib/internal/Magento/Framework/Reflection/ExtensionAttributesProcessor.php @@ -11,7 +11,7 @@ use Magento\Framework\AuthorizationInterface; use Magento\Framework\Phrase; use Magento\Framework\Api\ExtensionAttributesInterface; -use Zend\Code\Reflection\MethodReflection; +use Laminas\Code\Reflection\MethodReflection; /** * Processes extension attributes and produces an array for the data. @@ -140,6 +140,8 @@ public function buildOutputDataArray(ExtensionAttributesInterface $dataObject, $ } /** + * Is attribute permissions valid + * * @param string $dataObjectType * @param string $attributeCode * @return bool @@ -158,6 +160,8 @@ private function isAttributePermissionValid($dataObjectType, $attributeCode) } /** + * Get regular type for extension attribute type + * * @param string $name * @return string */ @@ -167,6 +171,8 @@ private function getRegularTypeForExtensionAttributesType($name) } /** + * Get permissions for attribute type + * * @param string $typeName * @param string $attributeCode * @return string[] A list of permissions diff --git a/lib/internal/Magento/Framework/Reflection/MethodsMap.php b/lib/internal/Magento/Framework/Reflection/MethodsMap.php index 6b0ddfbfc2127..c4a738ac28caa 100644 --- a/lib/internal/Magento/Framework/Reflection/MethodsMap.php +++ b/lib/internal/Magento/Framework/Reflection/MethodsMap.php @@ -7,13 +7,14 @@ namespace Magento\Framework\Reflection; use Magento\Framework\Serialize\SerializerInterface; -use Zend\Code\Reflection\ClassReflection; -use Zend\Code\Reflection\MethodReflection; -use Zend\Code\Reflection\ParameterReflection; +use Laminas\Code\Reflection\ClassReflection; +use Laminas\Code\Reflection\MethodReflection; +use Laminas\Code\Reflection\ParameterReflection; use Magento\Framework\App\Cache\Type\Reflection as ReflectionCache; /** * Gathers method metadata information. + * @SuppressWarnings(PHPMD.CouplingBetweenObjects) */ class MethodsMap { @@ -51,6 +52,11 @@ class MethodsMap */ private $serializer; + /** + * @var \Magento\Framework\Api\AttributeTypeResolverInterface + */ + private $attributeTypeResolver; + /** * @param \Magento\Framework\Cache\FrontendInterface $cache * @param TypeProcessor $typeProcessor @@ -99,6 +105,7 @@ public function getMethodReturnType($typeName, $methodName) */ public function getMethodsMap($interfaceName) { + //phpcs:ignore Magento2.Security.InsecureFunction $key = self::SERVICE_INTERFACE_METHODS_CACHE_PREFIX . "-" . md5($interfaceName); if (!isset($this->serviceInterfaceMethodsMap[$key])) { $methodMap = $this->cache->load($key); diff --git a/lib/internal/Magento/Framework/Reflection/NameFinder.php b/lib/internal/Magento/Framework/Reflection/NameFinder.php index 5cd4ab744bb1b..476aa23e6e841 100644 --- a/lib/internal/Magento/Framework/Reflection/NameFinder.php +++ b/lib/internal/Magento/Framework/Reflection/NameFinder.php @@ -6,8 +6,11 @@ namespace Magento\Framework\Reflection; -use Zend\Code\Reflection\ClassReflection; +use Laminas\Code\Reflection\ClassReflection; +/** + * Reflection NameFinder + */ class NameFinder { /** diff --git a/lib/internal/Magento/Framework/Reflection/Test/Unit/Fixture/TSample.php b/lib/internal/Magento/Framework/Reflection/Test/Unit/Fixture/TSample.php index 1d78f9ed0a7d8..544165774db82 100644 --- a/lib/internal/Magento/Framework/Reflection/Test/Unit/Fixture/TSample.php +++ b/lib/internal/Magento/Framework/Reflection/Test/Unit/Fixture/TSample.php @@ -22,4 +22,20 @@ public function getName() { return ''; } + + /** + * @inheritdoc + */ + public function getWithNull() + { + return null; + } + + /** + * @inheritdoc + */ + public function getOnlyNull() + { + return null; + } } diff --git a/lib/internal/Magento/Framework/Reflection/Test/Unit/Fixture/TSampleInterface.php b/lib/internal/Magento/Framework/Reflection/Test/Unit/Fixture/TSampleInterface.php index db2fbd19736bf..647c1a7c1dfbd 100644 --- a/lib/internal/Magento/Framework/Reflection/Test/Unit/Fixture/TSampleInterface.php +++ b/lib/internal/Magento/Framework/Reflection/Test/Unit/Fixture/TSampleInterface.php @@ -18,4 +18,16 @@ public function getPropertyName(); * Doc block without return tag. */ public function getName(); + + /** + * return annotation with a null type at first position + * @return null|string + */ + public function getWithNull(); + + /** + * return annotation with only null type + * @return null + */ + public function getOnlyNull(); } diff --git a/lib/internal/Magento/Framework/Reflection/Test/Unit/NameFinderTest.php b/lib/internal/Magento/Framework/Reflection/Test/Unit/NameFinderTest.php index e4c0294c0cfb5..a0ae3b4841d56 100644 --- a/lib/internal/Magento/Framework/Reflection/Test/Unit/NameFinderTest.php +++ b/lib/internal/Magento/Framework/Reflection/Test/Unit/NameFinderTest.php @@ -6,7 +6,7 @@ // @codingStandardsIgnoreStart namespace Magento\Framework\Reflection\Test\Unit; -use Zend\Code\Reflection\ClassReflection; +use Laminas\Code\Reflection\ClassReflection; /** * NameFinder Unit Test @@ -64,7 +64,7 @@ public function testGetSetterMethodNameWrongCamelCasedAttribute() */ public function testFindAccessorMethodName() { - $reflectionClass = $this->createMock(\Zend\Code\Reflection\ClassReflection::class); + $reflectionClass = $this->createMock(\Laminas\Code\Reflection\ClassReflection::class); $reflectionClass->expects($this->atLeastOnce())->method('hasMethod')->willReturn(false); $reflectionClass->expects($this->atLeastOnce())->method('getName')->willReturn('className'); diff --git a/lib/internal/Magento/Framework/Reflection/Test/Unit/TypeProcessorTest.php b/lib/internal/Magento/Framework/Reflection/Test/Unit/TypeProcessorTest.php index 1a8702c0e1c5b..dc8c6aef4d8b0 100644 --- a/lib/internal/Magento/Framework/Reflection/Test/Unit/TypeProcessorTest.php +++ b/lib/internal/Magento/Framework/Reflection/Test/Unit/TypeProcessorTest.php @@ -15,7 +15,7 @@ use Magento\Framework\Reflection\Test\Unit\Fixture\UseClasses\SampleTwo\SampleFour; use Magento\Framework\Reflection\Test\Unit\Fixture\UseSample; use Magento\Framework\Reflection\TypeProcessor; -use Zend\Code\Reflection\ClassReflection; +use Laminas\Code\Reflection\ClassReflection; /** * @SuppressWarnings(PHPMD.CouplingBetweenObjects) @@ -364,6 +364,25 @@ public function testGetReturnTypeWithoutReturnTag() $this->typeProcessor->getGetterReturnType($methodReflection); } + /** + * Checks a case when method return annotation has a null-type at first position, + * and a valid type at second. + */ + public function testGetReturnTypeNullAtFirstPos() + { + $expected = [ + 'type' => 'string', + 'isRequired' => false, + 'description' => null, + 'parameterCount' => 0 + ]; + + $classReflection = new ClassReflection(TSample::class); + $methodReflection = $classReflection->getMethod('getWithNull'); + + self::assertEquals($expected, $this->typeProcessor->getGetterReturnType($methodReflection)); + } + /** * Simple and complex data provider * diff --git a/lib/internal/Magento/Framework/Reflection/TypeProcessor.php b/lib/internal/Magento/Framework/Reflection/TypeProcessor.php index 9571fa53547ab..2f16d0410c673 100644 --- a/lib/internal/Magento/Framework/Reflection/TypeProcessor.php +++ b/lib/internal/Magento/Framework/Reflection/TypeProcessor.php @@ -8,12 +8,12 @@ use Magento\Framework\Exception\SerializationException; use Magento\Framework\Phrase; -use Zend\Code\Reflection\ClassReflection; -use Zend\Code\Reflection\DocBlock\Tag\ParamTag; -use Zend\Code\Reflection\DocBlock\Tag\ReturnTag; -use Zend\Code\Reflection\DocBlockReflection; -use Zend\Code\Reflection\MethodReflection; -use Zend\Code\Reflection\ParameterReflection; +use Laminas\Code\Reflection\ClassReflection; +use Laminas\Code\Reflection\DocBlock\Tag\ParamTag; +use Laminas\Code\Reflection\DocBlock\Tag\ReturnTag; +use Laminas\Code\Reflection\DocBlockReflection; +use Laminas\Code\Reflection\MethodReflection; +use Laminas\Code\Reflection\ParameterReflection; /** * Type processor of config reader properties @@ -286,7 +286,14 @@ public function getGetterReturnType($methodReflection) { $returnAnnotation = $this->getMethodReturnAnnotation($methodReflection); $types = $returnAnnotation->getTypes(); - $returnType = current($types); + $returnType = null; + foreach ($types as $type) { + if ($type !== 'null') { + $returnType = $type; + break; + } + } + $nullable = in_array('null', $types); return [ @@ -310,8 +317,9 @@ public function getExceptions($methodReflection) if ($methodDocBlock->hasTag('throws')) { $throwsTypes = $methodDocBlock->getTags('throws'); if (is_array($throwsTypes)) { - /** @var $throwsType \Zend\Code\Reflection\DocBlock\Tag\ThrowsTag */ + /** @var $throwsType \Laminas\Code\Reflection\DocBlock\Tag\ThrowsTag */ foreach ($throwsTypes as $throwsType) { + // phpcs:ignore $exceptions = array_merge($exceptions, $throwsType->getTypes()); } } @@ -513,13 +521,15 @@ public function getParamType(ParameterReflection $param) { $type = $param->detectType(); if ($type === 'null') { - throw new \LogicException(sprintf( - '@param annotation is incorrect for the parameter "%s" in the method "%s:%s".' - . ' First declared type should not be null. E.g. string|null', - $param->getName(), - $param->getDeclaringClass()->getName(), - $param->getDeclaringFunction()->name - )); + throw new \LogicException( + sprintf( + '@param annotation is incorrect for the parameter "%s" in the method "%s:%s".' + . ' First declared type should not be null. E.g. string|null', + $param->getName(), + $param->getDeclaringClass()->getName(), + $param->getDeclaringFunction()->name + ) + ); } if ($type === 'array') { // try to determine class, if it's array of objects diff --git a/lib/internal/Magento/Framework/RequireJs/Config.php b/lib/internal/Magento/Framework/RequireJs/Config.php index f158aa8b1ebce..67fdd00b3d919 100644 --- a/lib/internal/Magento/Framework/RequireJs/Config.php +++ b/lib/internal/Magento/Framework/RequireJs/Config.php @@ -155,22 +155,15 @@ public function getConfig() { $distributedConfig = ''; $customConfigFiles = $this->fileSource->getFiles($this->design->getDesignTheme(), self::CONFIG_FILE_NAME); + foreach ($customConfigFiles as $file) { /** @var $fileReader \Magento\Framework\Filesystem\File\Read */ $fileReader = $this->readFactory->create($file->getFilename(), DriverPool::FILE); $config = $fileReader->readAll($file->getName()); - $distributedConfig .= str_replace( - ['%config%', '%context%'], - [$config, $file->getModule()], - self::PARTIAL_CONFIG_TEMPLATE - ); + $distributedConfig .= str_replace('%config%', $config, self::PARTIAL_CONFIG_TEMPLATE); } - $fullConfig = str_replace( - ['%function%', '%usages%'], - [$distributedConfig], - self::FULL_CONFIG_TEMPLATE - ); + $fullConfig = str_replace(['%function%', '%usages%'], [$distributedConfig], self::FULL_CONFIG_TEMPLATE); if ($this->minification->isEnabled('js')) { $fullConfig = $this->minifyAdapter->minify($fullConfig); diff --git a/lib/internal/Magento/Framework/RequireJs/Test/Unit/ConfigTest.php b/lib/internal/Magento/Framework/RequireJs/Test/Unit/ConfigTest.php index 05f040e8f406d..b6b8203bef2bc 100644 --- a/lib/internal/Magento/Framework/RequireJs/Test/Unit/ConfigTest.php +++ b/lib/internal/Magento/Framework/RequireJs/Test/Unit/ConfigTest.php @@ -115,9 +115,6 @@ function ($file) { $fileOne->expects($this->once()) ->method('getName') ->will($this->returnValue('file_one.js')); - $fileOne->expects($this->once()) - ->method('getModule') - ->will($this->returnValue('Module_One')); $fileTwo = $this->createMock(\Magento\Framework\View\File::class); $fileTwo->expects($this->once()) ->method('getFilename') diff --git a/lib/internal/Magento/Framework/Session/Config/Validator/CookieDomainValidator.php b/lib/internal/Magento/Framework/Session/Config/Validator/CookieDomainValidator.php index 7d286eca4ed3f..e145484d22a43 100644 --- a/lib/internal/Magento/Framework/Session/Config/Validator/CookieDomainValidator.php +++ b/lib/internal/Magento/Framework/Session/Config/Validator/CookieDomainValidator.php @@ -6,10 +6,13 @@ namespace Magento\Framework\Session\Config\Validator; +/** + * Session cookie domain validator + */ class CookieDomainValidator extends \Magento\Framework\Validator\AbstractValidator { /** - * {@inheritdoc} + * @inheritDoc */ public function isValid($value) { @@ -19,7 +22,7 @@ public function isValid($value) return false; } - $validator = new \Zend\Validator\Hostname(\Zend\Validator\Hostname::ALLOW_ALL); + $validator = new \Laminas\Validator\Hostname(\Laminas\Validator\Hostname::ALLOW_ALL); if (!empty($value) && !$validator->isValid($value)) { $this->_addMessages($validator->getMessages()); diff --git a/lib/internal/Magento/Framework/Session/SaveHandler.php b/lib/internal/Magento/Framework/Session/SaveHandler.php index 7959130d1e41c..b43468caa5659 100644 --- a/lib/internal/Magento/Framework/Session/SaveHandler.php +++ b/lib/internal/Magento/Framework/Session/SaveHandler.php @@ -3,13 +3,14 @@ * Copyright © Magento, Inc. All rights reserved. * See COPYING.txt for license details. */ + namespace Magento\Framework\Session; use Magento\Framework\Session\Config\ConfigInterface; -use \Magento\Framework\Exception\SessionException; +use Magento\Framework\Exception\SessionException; /** - * Magento session save handler + * Magento session save handler. */ class SaveHandler implements SaveHandlerInterface { @@ -21,8 +22,21 @@ class SaveHandler implements SaveHandlerInterface protected $saveHandlerAdapter; /** - * Constructor - * + * @var SaveHandlerFactory + */ + private $saveHandlerFactory; + + /** + * @var ConfigInterface + */ + private $sessionConfig; + + /** + * @var string + */ + private $defaultHandler; + + /** * @param SaveHandlerFactory $saveHandlerFactory * @param ConfigInterface $sessionConfig * @param string $default @@ -32,23 +46,13 @@ public function __construct( ConfigInterface $sessionConfig, $default = self::DEFAULT_HANDLER ) { - /** - * Session handler - * - * Save handler may be set to custom value in deployment config, which will override everything else. - * Otherwise, try to read PHP settings for session.save_handler value. Otherwise, use 'files' as default. - */ - $saveMethod = $sessionConfig->getOption('session.save_handler') ?: $default; - - try { - $this->saveHandlerAdapter = $saveHandlerFactory->create($saveMethod); - } catch (SessionException $e) { - $this->saveHandlerAdapter = $saveHandlerFactory->create($default); - } + $this->saveHandlerFactory = $saveHandlerFactory; + $this->sessionConfig = $sessionConfig; + $this->defaultHandler = $default; } /** - * Open Session - retrieve resources + * Open Session - retrieve resources. * * @param string $savePath * @param string $name @@ -56,32 +60,32 @@ public function __construct( */ public function open($savePath, $name) { - return $this->saveHandlerAdapter->open($savePath, $name); + return $this->callSafely('open', $savePath, $name); } /** - * Close Session - free resources + * Close Session - free resources. * * @return bool */ public function close() { - return $this->saveHandlerAdapter->close(); + return $this->callSafely('close'); } /** - * Read session data + * Read session data. * * @param string $sessionId * @return string */ public function read($sessionId) { - return $this->saveHandlerAdapter->read($sessionId); + return $this->callSafely('read', $sessionId); } /** - * Write Session - commit data to resource + * Write Session - commit data to resource. * * @param string $sessionId * @param string $data @@ -89,23 +93,22 @@ public function read($sessionId) */ public function write($sessionId, $data) { - return $this->saveHandlerAdapter->write($sessionId, $data); + return $this->callSafely('write', $sessionId, $data); } /** - * Destroy Session - remove data from resource for given session id + * Destroy Session - remove data from resource for given session id. * * @param string $sessionId * @return bool */ public function destroy($sessionId) { - return $this->saveHandlerAdapter->destroy($sessionId); + return $this->callSafely('destroy', $sessionId); } /** - * Garbage Collection - remove old session data older - * than $maxLifetime (in seconds) + * Garbage Collection - remove old session data older than $maxLifetime (in seconds). * * @param int $maxLifetime * @return bool @@ -113,6 +116,30 @@ public function destroy($sessionId) */ public function gc($maxLifetime) { - return $this->saveHandlerAdapter->gc($maxLifetime); + return $this->callSafely('gc', $maxLifetime); + } + + /** + * Call save handler adapter method. + * + * In case custom handler failed, default files handler is used. + * + * @param string $method + * @param mixed $arguments + * + * @return mixed + */ + private function callSafely(string $method, ...$arguments) + { + try { + if ($this->saveHandlerAdapter === null) { + $saveMethod = $this->sessionConfig->getOption('session.save_handler') ?: $this->defaultHandler; + $this->saveHandlerAdapter = $this->saveHandlerFactory->create($saveMethod); + } + return $this->saveHandlerAdapter->{$method}(...$arguments); + } catch (SessionException $exception) { + $this->saveHandlerAdapter = $this->saveHandlerFactory->create($this->defaultHandler); + return $this->saveHandlerAdapter->{$method}(...$arguments); + } } } diff --git a/lib/internal/Magento/Framework/Session/SidResolver.php b/lib/internal/Magento/Framework/Session/SidResolver.php index 041995d20fcd2..fd7af781981a9 100644 --- a/lib/internal/Magento/Framework/Session/SidResolver.php +++ b/lib/internal/Magento/Framework/Session/SidResolver.php @@ -10,7 +10,8 @@ use Magento\Framework\App\State; /** - * Class SidResolver + * Resolves SID by processing request parameters. + * * @deprecated 2.3.3 SIDs in URLs are no longer used */ class SidResolver implements SidResolverInterface @@ -27,11 +28,13 @@ class SidResolver implements SidResolverInterface /** * @var \Magento\Framework\UrlInterface + * @deprecated Not used anymore. */ protected $urlBuilder; /** * @var \Magento\Framework\App\RequestInterface + * @deprecated Not used anymore. */ protected $request; @@ -60,11 +63,6 @@ class SidResolver implements SidResolverInterface */ protected $_scopeType; - /** - * @var State - */ - private $appState; - /** * @param \Magento\Framework\App\Config\ScopeConfigInterface $scopeConfig * @param \Magento\Framework\UrlInterface $urlBuilder @@ -72,6 +70,7 @@ class SidResolver implements SidResolverInterface * @param string $scopeType * @param array $sidNameMap * @param State|null $appState + * @SuppressWarnings(PHPMD.UnusedFormalParameter) */ public function __construct( \Magento\Framework\App\Config\ScopeConfigInterface $scopeConfig, @@ -86,34 +85,19 @@ public function __construct( $this->request = $request; $this->sidNameMap = $sidNameMap; $this->_scopeType = $scopeType; - $this->appState = $appState ?: \Magento\Framework\App\ObjectManager::getInstance()->get(State::class); } /** * Get Sid * * @param SessionManagerInterface $session - * * @return string|null * @throws \Magento\Framework\Exception\LocalizedException + * @SuppressWarnings(PHPMD.UnusedFormalParameter) */ public function getSid(SessionManagerInterface $session) { - if ($this->appState->getAreaCode() !== \Magento\Framework\App\Area::AREA_FRONTEND) { - return null; - } - - $sidKey = null; - - $useSidOnFrontend = $this->getUseSessionInUrl(); - if ($useSidOnFrontend && $this->request->getQuery( - $this->getSessionIdQueryParam($session), - false - ) && $this->urlBuilder->isOwnOriginUrl() - ) { - $sidKey = $this->request->getQuery($this->getSessionIdQueryParam($session)); - } - return $sidKey; + return null; } /** diff --git a/lib/internal/Magento/Framework/Setup/Declaration/Schema/Db/MySQL/Definition/Columns/Json.php b/lib/internal/Magento/Framework/Setup/Declaration/Schema/Db/MySQL/Definition/Columns/Json.php new file mode 100644 index 0000000000000..b25b70674e0d0 --- /dev/null +++ b/lib/internal/Magento/Framework/Setup/Declaration/Schema/Db/MySQL/Definition/Columns/Json.php @@ -0,0 +1,74 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ + +namespace Magento\Framework\Setup\Declaration\Schema\Db\MySQL\Definition\Columns; + +use Magento\Framework\App\ResourceConnection; +use Magento\Framework\Setup\Declaration\Schema\Db\DbDefinitionProcessorInterface; +use Magento\Framework\Setup\Declaration\Schema\Dto\ElementInterface; + +/** + * Process json data type. + */ +class Json implements DbDefinitionProcessorInterface +{ + /** + * @var Nullable + */ + private $nullable; + + /** + * @var ResourceConnection + */ + private $resourceConnection; + + /** + * @var Comment + */ + private $comment; + + /** + * Blob constructor. + * + * @param Nullable $nullable + * @param Comment $comment + * @param ResourceConnection $resourceConnection + */ + public function __construct( + Nullable $nullable, + Comment $comment, + ResourceConnection $resourceConnection + ) { + $this->nullable = $nullable; + $this->resourceConnection = $resourceConnection; + $this->comment = $comment; + } + + /** + * @inheritdoc + */ + public function toDefinition(ElementInterface $column) + { + return sprintf( + '%s %s %s %s', + $this->resourceConnection->getConnection()->quoteIdentifier($column->getName()), + $column->getType(), + $this->nullable->toDefinition($column), + $this->comment->toDefinition($column) + ); + } + + /** + * Returns an array of column definitions + * + * @param array $data + * @return array + */ + public function fromDefinition(array $data) + { + return $data; + } +} diff --git a/lib/internal/Magento/Framework/Setup/Declaration/Schema/Dto/Columns/Json.php b/lib/internal/Magento/Framework/Setup/Declaration/Schema/Dto/Columns/Json.php new file mode 100644 index 0000000000000..0f1e785730d38 --- /dev/null +++ b/lib/internal/Magento/Framework/Setup/Declaration/Schema/Dto/Columns/Json.php @@ -0,0 +1,69 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ + +namespace Magento\Framework\Setup\Declaration\Schema\Dto\Columns; + +use Magento\Framework\Setup\Declaration\Schema\Dto\Column; +use Magento\Framework\Setup\Declaration\Schema\Dto\ElementDiffAwareInterface; +use Magento\Framework\Setup\Declaration\Schema\Dto\Table; + +/** + * Json + * + * Text column. + * Declared in SQL, like: JSON + */ +class Json extends Column implements ElementDiffAwareInterface, ColumnNullableAwareInterface +{ + /** + * @var bool + */ + private $nullable; + + /** + * Constructor. + * + * @param string $name + * @param string $type + * @param Table $table + * @param bool $nullable + * @param string|null $comment + * @param string|null $onCreate + */ + public function __construct( + string $name, + string $type, + Table $table, + bool $nullable = true, + string $comment = null, + string $onCreate = null + ) { + parent::__construct($name, $type, $table, $comment, $onCreate); + $this->nullable = $nullable; + } + + /** + * Check whether column can be nullable. + * + * @return bool + */ + public function isNullable() + { + return $this->nullable; + } + + /** + * @inheritdoc + */ + public function getDiffSensitiveParams() + { + return [ + 'type' => $this->getType(), + 'nullable' => $this->isNullable(), + 'comment' => $this->getComment() + ]; + } +} diff --git a/lib/internal/Magento/Framework/Setup/Declaration/Schema/Dto/Columns/StringBinary.php b/lib/internal/Magento/Framework/Setup/Declaration/Schema/Dto/Columns/StringBinary.php index 58e6df1146300..4de198ae631f4 100644 --- a/lib/internal/Magento/Framework/Setup/Declaration/Schema/Dto/Columns/StringBinary.php +++ b/lib/internal/Magento/Framework/Setup/Declaration/Schema/Dto/Columns/StringBinary.php @@ -11,7 +11,7 @@ /** * String or Binary column. - * Declared in SQL, like VARCHAR(L), BINARY(L) + * Declared in SQL, like CHAR(L), VARCHAR(L), BINARY(L) * where L - length. */ class StringBinary extends Column implements @@ -73,10 +73,9 @@ public function isNullable() } /** - * Return default value. - * Note: default value should be string. + * Return default value, Note: default value should be string. * - * @return string | null + * @return string|null */ public function getDefault() { diff --git a/lib/internal/Magento/Framework/Setup/Declaration/Schema/Dto/Factories/Json.php b/lib/internal/Magento/Framework/Setup/Declaration/Schema/Dto/Factories/Json.php new file mode 100644 index 0000000000000..5c9c88bb4fe26 --- /dev/null +++ b/lib/internal/Magento/Framework/Setup/Declaration/Schema/Dto/Factories/Json.php @@ -0,0 +1,52 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ + +namespace Magento\Framework\Setup\Declaration\Schema\Dto\Factories; + +use Magento\Framework\ObjectManagerInterface; + +/** + * Class Json + * + * Json Factory + */ +class Json implements FactoryInterface +{ + /** + * @var ObjectManagerInterface + */ + private $objectManager; + + /** + * @var string + */ + private $className; + + /** + * Constructor. + * + * @param ObjectManagerInterface $objectManager + * @param string $className + */ + public function __construct( + ObjectManagerInterface $objectManager, + $className = \Magento\Framework\Setup\Declaration\Schema\Dto\Columns\Blob::class + ) { + $this->objectManager = $objectManager; + $this->className = $className; + } + + /** + * Create element using definition data array. + * + * @param array $data + * @return \Magento\Framework\Setup\Declaration\Schema\Dto\ElementInterface|mixed + */ + public function create(array $data) + { + return $this->objectManager->create($this->className, $data); + } +} diff --git a/lib/internal/Magento/Framework/Setup/Declaration/Schema/etc/schema.xsd b/lib/internal/Magento/Framework/Setup/Declaration/Schema/etc/schema.xsd index e3c54413f810b..2fbab18161925 100644 --- a/lib/internal/Magento/Framework/Setup/Declaration/Schema/etc/schema.xsd +++ b/lib/internal/Magento/Framework/Setup/Declaration/Schema/etc/schema.xsd @@ -20,6 +20,8 @@ <xs:include schemaLocation="urn:magento:framework:Setup/Declaration/Schema/etc/types/texts/longtext.xsd" /> <xs:include schemaLocation="urn:magento:framework:Setup/Declaration/Schema/etc/types/texts/mediumtext.xsd" /> <xs:include schemaLocation="urn:magento:framework:Setup/Declaration/Schema/etc/types/texts/varchar.xsd" /> + <xs:include schemaLocation="urn:magento:framework:Setup/Declaration/Schema/etc/types/texts/char.xsd" /> + <xs:include schemaLocation="urn:magento:framework:Setup/Declaration/Schema/etc/types/texts/json.xsd" /> <xs:include schemaLocation="urn:magento:framework:Setup/Declaration/Schema/etc/types/binaries/blob.xsd" /> <xs:include schemaLocation="urn:magento:framework:Setup/Declaration/Schema/etc/types/binaries/mediumblob.xsd" /> <xs:include schemaLocation="urn:magento:framework:Setup/Declaration/Schema/etc/types/binaries/longblob.xsd" /> diff --git a/lib/internal/Magento/Framework/Setup/Declaration/Schema/etc/types/texts/char.xsd b/lib/internal/Magento/Framework/Setup/Declaration/Schema/etc/types/texts/char.xsd new file mode 100644 index 0000000000000..27ec7852b3b8d --- /dev/null +++ b/lib/internal/Magento/Framework/Setup/Declaration/Schema/etc/types/texts/char.xsd @@ -0,0 +1,33 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +--> +<xs:schema xmlns:xs="http://www.w3.org/2001/XMLSchema" elementFormDefault="qualified"> + <xs:include schemaLocation="urn:magento:framework:Setup/Declaration/Schema/etc/types/column.xsd"/> + + <xs:complexType name="char"> + <xs:complexContent> + <xs:extension base="abstractColumnType"> + <xs:annotation> + <xs:documentation> + Here plain text can be persisted without trailing spaces. Length of this field can't be more than 255 characters + When CHAR values are retrieved, trailing spaces are removed unless the PAD_CHAR_TO_FULL_LENGTH SQL mode is enabled. + </xs:documentation> + </xs:annotation> + + <xs:attribute name="length"> + <xs:simpleType> + <xs:restriction base="xs:integer"> + <xs:maxInclusive value="255"/> + </xs:restriction> + </xs:simpleType> + </xs:attribute> + <xs:attribute name="default" type="xs:string" /> + <xs:attribute name="nullable" type="xs:boolean" /> + </xs:extension> + </xs:complexContent> + </xs:complexType> +</xs:schema> diff --git a/lib/internal/Magento/Framework/Setup/Declaration/Schema/etc/types/texts/json.xsd b/lib/internal/Magento/Framework/Setup/Declaration/Schema/etc/types/texts/json.xsd new file mode 100644 index 0000000000000..690f84a5ef43f --- /dev/null +++ b/lib/internal/Magento/Framework/Setup/Declaration/Schema/etc/types/texts/json.xsd @@ -0,0 +1,23 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +--> +<xs:schema xmlns:xs="http://www.w3.org/2001/XMLSchema" elementFormDefault="qualified"> + <xs:include schemaLocation="urn:magento:framework:Setup/Declaration/Schema/etc/types/column.xsd"/> + + <xs:complexType name="json"> + <xs:complexContent> + <xs:extension base="abstractColumnType"> + <xs:annotation> + <xs:documentation> + Well formatted Json object + </xs:documentation> + </xs:annotation> + <xs:attribute name="nullable" type="xs:boolean" /> + </xs:extension> + </xs:complexContent> + </xs:complexType> +</xs:schema> diff --git a/lib/internal/Magento/Framework/Setup/SchemaListenerDefinition/CharDefinition.php b/lib/internal/Magento/Framework/Setup/SchemaListenerDefinition/CharDefinition.php new file mode 100644 index 0000000000000..058ca69564871 --- /dev/null +++ b/lib/internal/Magento/Framework/Setup/SchemaListenerDefinition/CharDefinition.php @@ -0,0 +1,29 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ + +namespace Magento\Framework\Setup\SchemaListenerDefinition; + +/** + * Char type definition. + */ +class CharDefinition implements DefinitionConverterInterface +{ + private const DEFAULT_TEXT_LENGTH = 255; + + /** + * @inheritdoc + */ + public function convertToDefinition(array $definition) + { + return [ + 'xsi:type' => $definition['type'], + 'name' => $definition['name'], + 'length' => $definition['length'] ?? self::DEFAULT_TEXT_LENGTH, + 'default' => isset($definition['default']) ? (bool) $definition['default'] : null, + 'nullable' => $definition['nullable'] ?? true, + ]; + } +} diff --git a/lib/internal/Magento/Framework/Setup/SchemaListenerDefinition/JsonDefinition.php b/lib/internal/Magento/Framework/Setup/SchemaListenerDefinition/JsonDefinition.php new file mode 100644 index 0000000000000..04866dde943f4 --- /dev/null +++ b/lib/internal/Magento/Framework/Setup/SchemaListenerDefinition/JsonDefinition.php @@ -0,0 +1,25 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ + +namespace Magento\Framework\Setup\SchemaListenerDefinition; + +/** + * Json type definition. + */ +class JsonDefinition implements DefinitionConverterInterface +{ + /** + * @inheritdoc + */ + public function convertToDefinition(array $definition) + { + return [ + 'xsi:type' => $definition['type'], + 'name' => $definition['name'], + 'nullable' => $definition['nullable'] ?? true + ]; + } +} diff --git a/lib/internal/Magento/Framework/Stdlib/DateTime/DateTime.php b/lib/internal/Magento/Framework/Stdlib/DateTime/DateTime.php index 7ac93c7b94305..646908f99693c 100644 --- a/lib/internal/Magento/Framework/Stdlib/DateTime/DateTime.php +++ b/lib/internal/Magento/Framework/Stdlib/DateTime/DateTime.php @@ -13,13 +13,6 @@ */ class DateTime { - /** - * Current config offset in seconds - * - * @var int - */ - private $_offset = 0; - /** * @var TimezoneInterface */ @@ -31,7 +24,6 @@ class DateTime public function __construct(TimezoneInterface $localeDate) { $this->_localeDate = $localeDate; - $this->_offset = $this->calculateOffset($this->_localeDate->getConfigTimezone()); } /** @@ -78,8 +70,7 @@ public function gmtDate($format = null, $input = null) } /** - * Converts input date into date with timezone offset - * Input date must be in GMT timezone + * Converts input date into date with timezone offset. Input date must be in GMT timezone. * * @param string $format * @param int|string $input date in GMT timezone @@ -122,8 +113,7 @@ public function gmtTimestamp($input = null) } /** - * Converts input date into timestamp with timezone offset - * Input date must be in GMT timezone + * Converts input date into timestamp with timezone offset. Input date must be in GMT timezone. * * @param int|string $input date in GMT timezone * @return int @@ -157,18 +147,18 @@ public function timestamp($input = null) */ public function getGmtOffset($type = 'seconds') { - $result = $this->_offset; + $offset = $this->calculateOffset($this->_localeDate->getConfigTimezone()); switch ($type) { case 'seconds': default: break; case 'minutes': - $result = $result / 60; + $offset = $offset / 60; break; case 'hours': - $result = $result / 60 / 60; + $offset = $offset / 60 / 60; break; } - return $result; + return $offset; } } diff --git a/lib/internal/Magento/Framework/Stdlib/DateTime/Timezone.php b/lib/internal/Magento/Framework/Stdlib/DateTime/Timezone.php index 118a3e053bd79..0791c89ab793a 100644 --- a/lib/internal/Magento/Framework/Stdlib/DateTime/Timezone.php +++ b/lib/internal/Magento/Framework/Stdlib/DateTime/Timezone.php @@ -177,12 +177,12 @@ public function date($date = null, $locale = null, $useTimezone = true, $include $timeType = $includeTime ? \IntlDateFormatter::SHORT : \IntlDateFormatter::NONE; $formatter = new \IntlDateFormatter( $locale, - \IntlDateFormatter::SHORT, + \IntlDateFormatter::MEDIUM, $timeType, new \DateTimeZone($timezone) ); - $date = $this->appendTimeIfNeeded($date, $includeTime); + $date = $this->appendTimeIfNeeded($date, $includeTime, $timezone, $locale); $date = $formatter->parse($date) ?: (new \DateTime($date))->getTimestamp(); break; } @@ -206,25 +206,9 @@ public function scopeDate($scope = null, $date = null, $includeTime = false) case ($date instanceof \DateTimeImmutable): $date = $date->setTimezone($timezone); break; - case (!is_numeric($date)): - $timeType = $includeTime ? \IntlDateFormatter::SHORT : \IntlDateFormatter::NONE; - $formatter = new \IntlDateFormatter( - $this->_localeResolver->getLocale(), - \IntlDateFormatter::SHORT, - $timeType, - $timezone - ); - $timestamp = $formatter->parse($date); - $date = $timestamp - ? (new \DateTime('@' . $timestamp))->setTimezone($timezone) - : new \DateTime($date, $timezone); - break; - case (is_numeric($date)): - $date = new \DateTime('@' . $date); - $date = $date->setTimezone($timezone); - break; default: - $date = new \DateTime($date, $timezone); + $date = new \DateTime(is_numeric($date) ? '@' . $date : $date); + $date->setTimezone($timezone); break; } @@ -347,16 +331,44 @@ public function convertConfigTimeToUtc($date, $format = 'Y-m-d H:i:s') } /** - * Retrieve date with time + * Append time to DateTime * * @param string $date - * @param bool $includeTime + * @param boolean $includeTime + * @param string $timezone + * @param string $locale * @return string + * @throws LocalizedException */ - private function appendTimeIfNeeded($date, $includeTime) + private function appendTimeIfNeeded($date, $includeTime, $timezone, $locale) { if ($includeTime && !preg_match('/\d{1}:\d{2}/', $date)) { - $date .= " 0:00am"; + + $formatterWithoutHour = new \IntlDateFormatter( + $locale, + \IntlDateFormatter::MEDIUM, + \IntlDateFormatter::NONE, + new \DateTimeZone($timezone) + ); + $convertedDate = $formatterWithoutHour->parse($date); + + if (!$convertedDate) { + throw new LocalizedException( + new Phrase( + 'Could not append time to DateTime' + ) + ); + + } + + $formatterWithHour = new \IntlDateFormatter( + $locale, + \IntlDateFormatter::MEDIUM, + \IntlDateFormatter::SHORT, + new \DateTimeZone($timezone) + ); + + $date = $formatterWithHour->format($convertedDate); } return $date; } diff --git a/lib/internal/Magento/Framework/Stdlib/Parameters.php b/lib/internal/Magento/Framework/Stdlib/Parameters.php index dcefaf85d390e..580d94ac983fc 100644 --- a/lib/internal/Magento/Framework/Stdlib/Parameters.php +++ b/lib/internal/Magento/Framework/Stdlib/Parameters.php @@ -7,24 +7,23 @@ namespace Magento\Framework\Stdlib; -use Zend\Stdlib\Parameters as ZendParameters; +use Laminas\Stdlib\Parameters as LaminasParameters; /** - * Class Parameters + * Stdlib parameters */ class Parameters { /** - * @var ZendParameters + * @var LaminasParameters */ private $parameters; /** - * @param ZendParameters $parameters + * @param LaminasParameters $parameters */ - public function __construct( - ZendParameters $parameters - ) { + public function __construct(LaminasParameters $parameters) + { $this->parameters = $parameters; } @@ -100,7 +99,7 @@ public function get($name, $default = null) * * @param string $name * @param mixed $value - * @return \Zend\Stdlib\Parameters + * @return \Laminas\Stdlib\Parameters */ public function set($name, $value) { diff --git a/lib/internal/Magento/Framework/Stdlib/Test/Unit/DateTime/DateTimeTest.php b/lib/internal/Magento/Framework/Stdlib/Test/Unit/DateTime/DateTimeTest.php index f86269b1647b2..210e36ee05ef1 100644 --- a/lib/internal/Magento/Framework/Stdlib/Test/Unit/DateTime/DateTimeTest.php +++ b/lib/internal/Magento/Framework/Stdlib/Test/Unit/DateTime/DateTimeTest.php @@ -5,13 +5,18 @@ */ namespace Magento\Framework\Stdlib\Test\Unit\DateTime; +use DateTimeImmutable; +use DateTimeInterface; +use Exception; use Magento\Framework\Stdlib\DateTime\DateTime; use Magento\Framework\Stdlib\DateTime\TimezoneInterface; +use PHPUnit\Framework\MockObject\MockObject; +use PHPUnit\Framework\TestCase; /** - * Magento\Framework\Stdlib\DateTimeTest test case + * Tests for @see DateTime */ -class DateTimeTest extends \PHPUnit\Framework\TestCase +class DateTimeTest extends TestCase { /** * @var string @@ -19,12 +24,14 @@ class DateTimeTest extends \PHPUnit\Framework\TestCase private $testDate = '2015-04-02 21:03:00'; /** - * @param int|string|\DateTimeInterface $input + * @param int|string|DateTimeInterface $input + * @throws Exception + * * @dataProvider dateTimeInputDataProvider */ public function testGmtTimestamp($input) { - /** @var TimezoneInterface|\PHPUnit_Framework_MockObject_MockObject $timezone */ + /** @var TimezoneInterface|MockObject $timezone */ $timezone = $this->getMockBuilder(TimezoneInterface::class)->getMock(); $timezone->method('date')->willReturn(new \DateTime($this->testDate)); @@ -33,12 +40,14 @@ public function testGmtTimestamp($input) } /** - * @param int|string|\DateTimeInterface $input + * @param int|string|DateTimeInterface $input + * @throws Exception + * * @dataProvider dateTimeInputDataProvider */ public function testTimestamp($input) { - /** @var TimezoneInterface|\PHPUnit_Framework_MockObject_MockObject $timezone */ + /** @var TimezoneInterface|MockObject $timezone */ $timezone = $this->getMockBuilder(TimezoneInterface::class)->getMock(); $timezone->method('date')->willReturn(new \DateTime($this->testDate)); @@ -46,15 +55,52 @@ public function testTimestamp($input) $this->assertEquals($expected, (new DateTime($timezone))->timestamp($input)); } + public function testGtmOffset() + { + /** @var TimezoneInterface|MockObject $timezone */ + $timezone = $this->getMockBuilder(TimezoneInterface::class)->getMock(); + $timezone->method('getConfigTimezone')->willReturn('Europe/Amsterdam'); + + /** @var DateTime|MockObject $dateTime */ + $dateTime = $this->getMockBuilder(DateTime::class) + ->setConstructorArgs([$timezone]) + ->setMethods(null) + ->getMock(); + + $this->assertEquals( + $this->getExpectedGtmOffset($timezone->getConfigTimezone()), + $dateTime->getGmtOffset() + ); + } + + /** + * Returns expected offset according to Daylight Saving Time in timezone + * + * @param string $timezoneIdentifier + * @return int + */ + private function getExpectedGtmOffset(string $timezoneIdentifier): int + { + $timeZoneToReturn = date_default_timezone_get(); + date_default_timezone_set($timezoneIdentifier); + $expectedOffset = (date('I', time()) + 1) * 3600; + date_default_timezone_set($timeZoneToReturn); + + return (int) $expectedOffset; + } + /** + * Data provider + * * @return array + * @throws Exception */ public function dateTimeInputDataProvider() { return [ 'string' => [$this->testDate], 'int' => [strtotime($this->testDate)], - \DateTimeInterface::class => [new \DateTimeImmutable($this->testDate)], + DateTimeInterface::class => [new DateTimeImmutable($this->testDate)], ]; } } diff --git a/lib/internal/Magento/Framework/Stdlib/Test/Unit/DateTime/TimezoneTest.php b/lib/internal/Magento/Framework/Stdlib/Test/Unit/DateTime/TimezoneTest.php index 53980e574c267..12ba34778b66e 100644 --- a/lib/internal/Magento/Framework/Stdlib/Test/Unit/DateTime/TimezoneTest.php +++ b/lib/internal/Magento/Framework/Stdlib/Test/Unit/DateTime/TimezoneTest.php @@ -128,6 +128,18 @@ public function dateIncludeTimeDataProvider(): array true, // include time 1495170060 // expected timestamp ], + 'Parse greek d/m/y date without time' => [ + '30/10/2021', // datetime + 'el_GR', // locale + false, // include time + 1635570000 // expected timestamp + ], + 'Parse greek d/m/y date with time' => [ + '30/10/2021, 12:01 π.μ.', // datetime + 'el_GR', // locale + true, // include time + 1635570060 // expected timestamp + ], ]; } @@ -302,14 +314,15 @@ public function scopeDateDataProvider(): array return [ ['2018-10-20 00:00:00', 'UTC', 'en_US', '2018-10-20 00:00:00'], - ['2018-10-20 00:00:00', 'America/Los_Angeles', 'en_US', '2018-10-20 00:00:00'], - ['2018-10-20 00:00:00', 'Asia/Qatar', 'en_US', '2018-10-20 00:00:00'], + ['2018-10-20 00:00:00', 'America/Los_Angeles', 'en_US', '2018-10-19 17:00:00'], + ['2018-10-20 00:00:00', 'Asia/Qatar', 'en_US', '2018-10-20 03:00:00'], + ['2018-10-20 00:00:00', 'America/Los_Angeles', 'en_GB', '2018-10-19 17:00:00'], ['10/20/18 00:00', 'UTC', 'en_US', '2018-10-20 00:00:00'], - ['10/20/18 00:00', 'America/Los_Angeles', 'en_US', '2018-10-20 00:00:00'], - ['10/20/18 00:00', 'Asia/Qatar', 'en_US', '2018-10-20 00:00:00'], - ['20/10/18 00:00', 'UTC', 'fr_FR', '2018-10-20 00:00:00'], - ['20/10/18 00:00', 'America/Los_Angeles', 'fr_FR', '2018-10-20 00:00:00'], - ['20/10/18 00:00', 'Asia/Qatar', 'fr_FR', '2018-10-20 00:00:00'], + ['10/20/18 00:00', 'America/Los_Angeles', 'en_US', '2018-10-19 17:00:00'], + ['10/20/18 00:00', 'Asia/Qatar', 'en_US', '2018-10-20 03:00:00'], + ['10/20/18 00:00', 'UTC', 'fr_FR', '2018-10-20 00:00:00'], + ['10/20/18 00:00', 'America/Los_Angeles', 'fr_FR', '2018-10-19 17:00:00'], + ['10/20/18 00:00', 'Asia/Qatar', 'fr_FR', '2018-10-20 03:00:00'], [1539993600, 'UTC', 'en_US', '2018-10-20 00:00:00'], [1539993600, 'America/Los_Angeles', 'en_US', '2018-10-19 17:00:00'], [1539993600, 'Asia/Qatar', 'en_US', '2018-10-20 03:00:00'], diff --git a/lib/internal/Magento/Framework/Test/Unit/UrlTest.php b/lib/internal/Magento/Framework/Test/Unit/UrlTest.php index 046a9d63fc8f4..ee39b612852b9 100644 --- a/lib/internal/Magento/Framework/Test/Unit/UrlTest.php +++ b/lib/internal/Magento/Framework/Test/Unit/UrlTest.php @@ -108,7 +108,7 @@ protected function getRouteParamsResolverFactory($resolve = true) { $routeParamsResolverFactoryMock = $this->createMock(\Magento\Framework\Url\RouteParamsResolverFactory::class); if ($resolve) { - $routeParamsResolverFactoryMock->expects($this->once())->method('create') + $routeParamsResolverFactoryMock->expects($this->any())->method('create') ->will($this->returnValue($this->routeParamsResolverMock)); } return $routeParamsResolverFactoryMock; @@ -183,7 +183,7 @@ public function testGetUseSession() $this->assertFalse((bool)$model->getUseSession()); $model->setUseSession(true); - $this->assertTrue($model->getUseSession()); + $this->assertFalse($model->getUseSession()); } public function testGetBaseUrlNotLinkType() @@ -467,10 +467,10 @@ public function testGetRedirectUrl() ] ); - $this->sidResolverMock->expects($this->once())->method('getUseSessionInUrl')->will($this->returnValue(true)); - $this->sessionMock->expects($this->once())->method('getSessionIdForHost')->will($this->returnValue(false)); - $this->sidResolverMock->expects($this->once())->method('getUseSessionVar')->will($this->returnValue(true)); - $this->routeParamsResolverMock->expects($this->once())->method('hasData')->with('secure_is_forced') + $this->sidResolverMock->expects($this->any())->method('getUseSessionInUrl')->will($this->returnValue(true)); + $this->sessionMock->expects($this->any())->method('getSessionIdForHost')->will($this->returnValue(false)); + $this->sidResolverMock->expects($this->any())->method('getUseSessionVar')->will($this->returnValue(true)); + $this->routeParamsResolverMock->expects($this->any())->method('hasData')->with('secure_is_forced') ->will($this->returnValue(true)); $this->sidResolverMock->expects($this->never())->method('getSessionIdQueryParam'); $this->queryParamsResolverMock->expects($this->once()) @@ -491,11 +491,11 @@ public function testGetRedirectUrlWithSessionId() ] ); - $this->sidResolverMock->expects($this->once())->method('getUseSessionInUrl')->will($this->returnValue(true)); - $this->sessionMock->expects($this->once())->method('getSessionIdForHost') + $this->sidResolverMock->expects($this->never())->method('getUseSessionInUrl')->will($this->returnValue(true)); + $this->sessionMock->expects($this->never())->method('getSessionIdForHost') ->will($this->returnValue('session-id')); - $this->sidResolverMock->expects($this->once())->method('getUseSessionVar')->will($this->returnValue(false)); - $this->sidResolverMock->expects($this->once())->method('getSessionIdQueryParam'); + $this->sidResolverMock->expects($this->never())->method('getUseSessionVar')->will($this->returnValue(false)); + $this->sidResolverMock->expects($this->never())->method('getSessionIdQueryParam'); $this->queryParamsResolverMock->expects($this->once()) ->method('getQuery') ->will($this->returnValue('foo=bar')); @@ -540,10 +540,10 @@ public function testAddSessionParam() 'queryParamsResolver' => $this->queryParamsResolverMock, ]); - $this->sidResolverMock->expects($this->once())->method('getSessionIdQueryParam')->with($this->sessionMock) + $this->sidResolverMock->expects($this->never())->method('getSessionIdQueryParam')->with($this->sessionMock) ->will($this->returnValue('sid')); - $this->sessionMock->expects($this->once())->method('getSessionId')->will($this->returnValue('session-id')); - $this->queryParamsResolverMock->expects($this->once())->method('setQueryParam')->with('sid', 'session-id'); + $this->sessionMock->expects($this->never())->method('getSessionId')->will($this->returnValue('session-id')); + $this->queryParamsResolverMock->expects($this->never())->method('setQueryParam')->with('sid', 'session-id'); $model->addSessionParam(); } @@ -681,10 +681,10 @@ public function testSessionUrlVarWithMatchedHostsAndBaseUrl($html, $result) ] ); - $requestMock->expects($this->once()) + $requestMock->expects($this->any()) ->method('getHttpHost') ->will($this->returnValue('localhost')); - $this->scopeMock->expects($this->once()) + $this->scopeMock->expects($this->any()) ->method('getBaseUrl') ->will($this->returnValue('http://localhost')); $this->scopeResolverMock->expects($this->any()) @@ -709,20 +709,20 @@ public function testSessionUrlVarWithoutMatchedHostsAndBaseUrl() ] ); - $requestMock->expects($this->once())->method('getHttpHost')->will($this->returnValue('localhost')); - $this->scopeMock->expects($this->once()) + $requestMock->expects($this->never())->method('getHttpHost')->will($this->returnValue('localhost')); + $this->scopeMock->expects($this->any()) ->method('getBaseUrl') ->will($this->returnValue('http://example.com')); $this->scopeResolverMock->expects($this->any()) ->method('getScope') ->will($this->returnValue($this->scopeMock)); - $this->sidResolverMock->expects($this->once())->method('getSessionIdQueryParam') + $this->sidResolverMock->expects($this->never())->method('getSessionIdQueryParam') ->will($this->returnValue('SID')); - $this->sessionMock->expects($this->once())->method('getSessionId') + $this->sessionMock->expects($this->never())->method('getSessionId') ->will($this->returnValue('session-id')); $this->assertEquals( - '<a href="http://example.com/?SID=session-id">www.example.com</a>', + '<a href="http://example.com/">www.example.com</a>', $model->sessionUrlVar('<a href="http://example.com/?___SID=U">www.example.com</a>') ); } diff --git a/lib/internal/Magento/Framework/TestFramework/Unit/Listener/GarbageCleanup.php b/lib/internal/Magento/Framework/TestFramework/Unit/Listener/GarbageCleanup.php index bee6c0d0d7d42..34a8963c198cc 100644 --- a/lib/internal/Magento/Framework/TestFramework/Unit/Listener/GarbageCleanup.php +++ b/lib/internal/Magento/Framework/TestFramework/Unit/Listener/GarbageCleanup.php @@ -5,98 +5,24 @@ */ namespace Magento\Framework\TestFramework\Unit\Listener; -use PHPUnit\Framework\Test; -use PHPUnit\Framework\Warning; +use PHPUnit\Framework\TestListener; +use PHPUnit\Framework\TestSuite; /** * Listener of PHPUnit built-in events that enforces cleanup of cyclic object references * */ -class GarbageCleanup implements \PHPUnit\Framework\TestListener +class GarbageCleanup implements TestListener { - /** - * {@inheritdoc} - * @SuppressWarnings(PHPMD.ShortVariable) - * @SuppressWarnings(PHPMD.UnusedFormalParameter) - */ - public function addError(\PHPUnit\Framework\Test $test, \Exception $e, $time) - { - } - - /** - * {@inheritdoc} - * @SuppressWarnings(PHPMD.ShortVariable) - * @SuppressWarnings(PHPMD.UnusedFormalParameter) - */ - public function addFailure(\PHPUnit\Framework\Test $test, \PHPUnit\Framework\AssertionFailedError $e, $time) - { - } + use \PHPUnit\Framework\TestListenerDefaultImplementation; /** - * {@inheritdoc} - * @SuppressWarnings(PHPMD.ShortVariable) + * @inheritdoc + * * @SuppressWarnings(PHPMD.UnusedFormalParameter) */ - public function addIncompleteTest(\PHPUnit\Framework\Test $test, \Exception $e, $time) - { - } - - /** - * {@inheritdoc} - * @SuppressWarnings(PHPMD.ShortVariable) - * @SuppressWarnings(PHPMD.UnusedFormalParameter) - */ - public function addRiskyTest(\PHPUnit\Framework\Test $test, \Exception $e, $time) - { - } - - /** - * {@inheritdoc} - * @SuppressWarnings(PHPMD.ShortVariable) - * @SuppressWarnings(PHPMD.UnusedFormalParameter) - */ - public function addSkippedTest(\PHPUnit\Framework\Test $test, \Exception $e, $time) - { - } - - /** - * {@inheritdoc} - * @SuppressWarnings(PHPMD.UnusedFormalParameter) - */ - public function startTestSuite(\PHPUnit\Framework\TestSuite $suite) - { - } - - /** - * {@inheritdoc} - * @SuppressWarnings(PHPMD.UnusedFormalParameter) - */ - public function endTestSuite(\PHPUnit\Framework\TestSuite $suite) + public function endTestSuite(TestSuite $suite): void { gc_collect_cycles(); } - - /** - * {@inheritdoc} - * @SuppressWarnings(PHPMD.UnusedFormalParameter) - */ - public function startTest(\PHPUnit\Framework\Test $test) - { - } - - /** - * {@inheritdoc} - * @SuppressWarnings(PHPMD.UnusedFormalParameter) - */ - public function endTest(\PHPUnit\Framework\Test $test, $time) - { - } - - /** - * {@inheritdoc} - * @SuppressWarnings(PHPMD.UnusedFormalParameter) - */ - public function addWarning(Test $test, Warning $e, $time) - { - } } diff --git a/lib/internal/Magento/Framework/TestFramework/Unit/Listener/ReplaceObjectManager.php b/lib/internal/Magento/Framework/TestFramework/Unit/Listener/ReplaceObjectManager.php index 79745599e8393..1d71263a100cb 100644 --- a/lib/internal/Magento/Framework/TestFramework/Unit/Listener/ReplaceObjectManager.php +++ b/lib/internal/Magento/Framework/TestFramework/Unit/Listener/ReplaceObjectManager.php @@ -7,12 +7,16 @@ use Magento\Framework\App\ObjectManager; use Magento\Framework\ObjectManagerInterface; +use PHPUnit\Framework\Test; +use PHPUnit\Framework\TestCase; +use PHPUnit\Framework\TestListener; /** * The event listener which instantiates ObjectManager before test run */ -class ReplaceObjectManager extends \PHPUnit\Framework\BaseTestListener +class ReplaceObjectManager implements TestListener { + use \PHPUnit\Framework\TestListenerDefaultImplementation; /** * Replaces ObjectManager before run for each test * @@ -21,13 +25,13 @@ class ReplaceObjectManager extends \PHPUnit\Framework\BaseTestListener * This avoids the issue with a not initialized ObjectManager * and makes working with ObjectManager predictable as it always contains clear mock for each test * - * @param \PHPUnit\Framework\Test $test + * @param Test $test * @return void * @SuppressWarnings(PHPMD.UnusedFormalParameter) */ - public function startTest(\PHPUnit\Framework\Test $test) + public function startTest(Test $test): void { - if ($test instanceof \PHPUnit\Framework\TestCase) { + if ($test instanceof TestCase) { $objectManagerMock = $test->getMockBuilder(ObjectManagerInterface::class) ->getMockForAbstractClass(); $createMockCallback = function ($type) use ($test) { diff --git a/lib/internal/Magento/Framework/Url.php b/lib/internal/Magento/Framework/Url.php index c67a20f0a157d..a2318f1169715 100644 --- a/lib/internal/Magento/Framework/Url.php +++ b/lib/internal/Magento/Framework/Url.php @@ -111,7 +111,7 @@ class Url extends \Magento\Framework\DataObject implements \Magento\Framework\Ur * * @var bool */ - protected $_useSession; + protected $_useSession = false; /** * Url security info list @@ -292,10 +292,7 @@ public function setUseSession($useSession) */ public function getUseSession() { - if ($this->_useSession === null) { - $this->_useSession = $this->_sidResolver->getUseSessionInUrl(); - } - return $this->_useSession; + return false; } /** @@ -763,10 +760,6 @@ public function getRouteUrl($routePath = null, $routeParams = null) */ public function addSessionParam() { - $this->setQueryParam( - $this->_sidResolver->getSessionIdQueryParam($this->_session), - $this->_session->getSessionId() - ); return $this; } @@ -945,10 +938,6 @@ private function createUrl($routePath = null, array $routeParams = null) } } - if ($noSid !== true) { - $this->_prepareSessionUrl($url); - } - $query = $this->_getQuery($escapeQuery); if ($query) { $mark = strpos($url, '?') === false ? '?' : ($escapeQuery ? '&' : '&'); @@ -972,18 +961,10 @@ private function createUrl($routePath = null, array $routeParams = null) * @param string $url * * @return \Magento\Framework\UrlInterface + * @SuppressWarnings(PHPMD.UnusedFormalParameter) */ protected function _prepareSessionUrl($url) { - if (!$this->getUseSession()) { - return $this; - } - $sessionId = $this->_session->getSessionIdForHost($url); - if ($this->_sidResolver->getUseSessionVar() && !$sessionId) { - $this->setQueryParam('___SID', $this->_isSecure() ? 'S' : 'U'); - } elseif ($sessionId) { - $this->setQueryParam($this->_sidResolver->getSessionIdQueryParam($this->_session), $sessionId); - } return $this; } @@ -1004,8 +985,6 @@ public function getRebuiltUrl($url) } $url = $this->getScheme() . '://' . $this->getHost() . $port . $this->getPath(); - $this->_prepareSessionUrl($url); - $query = $this->_getQuery(); if ($query) { $url .= '?' . $query; @@ -1067,15 +1046,10 @@ public function sessionUrlVar($html) */ // @codingStandardsIgnoreEnd function ($match) { - if ($this->useSessionIdForUrl($match[2] == 'S')) { - return $match[1] . $this->_sidResolver->getSessionIdQueryParam($this->_session) . '=' - . $this->_session->getSessionId() . (isset($match[3]) ? $match[3] : ''); - } else { - if ($match[1] == '?') { - return isset($match[3]) ? '?' : ''; - } elseif ($match[1] == '&' || $match[1] == '&') { - return $match[3] ?? ''; - } + if ($match[1] == '?') { + return isset($match[3]) ? '?' : ''; + } elseif ($match[1] == '&' || $match[1] == '&') { + return $match[3] ?? ''; } }, $html @@ -1118,7 +1092,7 @@ public function isOwnOriginUrl() } /** - * Return frontend redirect URL with SID and other session parameters if any + * Return frontend redirect URL without SID * * @param string $url * diff --git a/lib/internal/Magento/Framework/Url/Test/Unit/ValidatorTest.php b/lib/internal/Magento/Framework/Url/Test/Unit/ValidatorTest.php index 843ee8b66e710..fbdfec82a58dd 100644 --- a/lib/internal/Magento/Framework/Url/Test/Unit/ValidatorTest.php +++ b/lib/internal/Magento/Framework/Url/Test/Unit/ValidatorTest.php @@ -12,8 +12,8 @@ class ValidatorTest extends \PHPUnit\Framework\TestCase /** @var \Magento\Framework\Url\Validator */ protected $object; - /** @var \Zend\Validator\Uri */ - protected $zendValidator; + /** @var \Laminas\Validator\Uri */ + protected $laminasValidator; /** @var string[] */ protected $expectedValidationMessages = ['invalidUrl' => "Invalid URL '%value%'."]; @@ -22,10 +22,10 @@ protected function setUp() { $objectManager = new ObjectManager($this); - $this->zendValidator = $this->createMock(\Zend\Validator\Uri::class); + $this->laminasValidator = $this->createMock(\Laminas\Validator\Uri::class); $this->object = $objectManager->getObject( \Magento\Framework\Url\Validator::class, - ['validator' => $this->zendValidator] + ['validator' => $this->laminasValidator] ); } @@ -36,7 +36,7 @@ public function testConstruct() public function testIsValidWhenValid() { - $this->zendValidator + $this->laminasValidator ->method('isValid') ->with('http://example.com') ->willReturn(true); @@ -47,7 +47,7 @@ public function testIsValidWhenValid() public function testIsValidWhenInvalid() { - $this->zendValidator + $this->laminasValidator ->method('isValid') ->with('%value%') ->willReturn(false); diff --git a/lib/internal/Magento/Framework/Url/Validator.php b/lib/internal/Magento/Framework/Url/Validator.php index 489e8c502179d..6ce201e511e3e 100644 --- a/lib/internal/Magento/Framework/Url/Validator.php +++ b/lib/internal/Magento/Framework/Url/Validator.php @@ -4,13 +4,13 @@ * See COPYING.txt for license details. */ -/** - * Validate URL - * - * @author Magento Core Team <core@magentocommerce.com> - */ namespace Magento\Framework\Url; +use Laminas\Validator\Uri; + +/** + * URL validator + */ class Validator extends \Zend_Validate_Abstract { /**#@+ @@ -20,14 +20,15 @@ class Validator extends \Zend_Validate_Abstract /**#@-*/ /** - * @var \Zend\Validator\Uri + * @var Uri */ private $validator; /** - * Object constructor + * @param Uri $validator + * @throws \Zend_Validate_Exception */ - public function __construct(\Zend\Validator\Uri $validator) + public function __construct(Uri $validator) { // set translated message template $this->setMessage((string)new \Magento\Framework\Phrase("Invalid URL '%value%'."), self::INVALID_URL); diff --git a/lib/internal/Magento/Framework/Validator/AllowedProtocols.php b/lib/internal/Magento/Framework/Validator/AllowedProtocols.php index a25346d673408..7e6df4655d990 100644 --- a/lib/internal/Magento/Framework/Validator/AllowedProtocols.php +++ b/lib/internal/Magento/Framework/Validator/AllowedProtocols.php @@ -1,19 +1,15 @@ <?php /** - * Protocol validator - * * Copyright © Magento, Inc. All rights reserved. * See COPYING.txt for license details. */ + namespace Magento\Framework\Validator; -use \Zend\Uri\Uri; +use \Laminas\Uri\Uri; /** - * Check is URI starts from allowed protocol - * - * Class AllowedProtocols - * @package Magento\Framework\Validator + * Protocol validator */ class AllowedProtocols extends AbstractValidator { @@ -29,6 +25,7 @@ class AllowedProtocols extends AbstractValidator /** * Constructor. + * * @param array $listOfProtocols */ public function __construct($listOfProtocols = []) @@ -54,6 +51,7 @@ public function isValid($value) if (!$isValid) { $this->_addMessages(["Protocol isn't allowed"]); } + return $isValid; } } diff --git a/lib/internal/Magento/Framework/View/Element/AbstractBlock.php b/lib/internal/Magento/Framework/View/Element/AbstractBlock.php index a70b87b8099f6..95ff209dd4571 100644 --- a/lib/internal/Magento/Framework/View/Element/AbstractBlock.php +++ b/lib/internal/Magento/Framework/View/Element/AbstractBlock.php @@ -55,6 +55,7 @@ abstract class AbstractBlock extends \Magento\Framework\DataObject implements Bl * SID Resolver * * @var \Magento\Framework\Session\SidResolverInterface + * @deprecated Not used anymore. */ protected $_sidResolver; @@ -995,10 +996,11 @@ public function escapeQuote($data, $addSlashes = false) } /** - * Escape quotes in java scripts + * Escape single quotes/apostrophes ('), or other specified $quote character in javascript * - * @param string|array $data + * @param string|string[]|array $data * @param string $quote + * * @return string|array * @deprecated 100.2.0 */ @@ -1116,18 +1118,7 @@ protected function _loadCache() return $html; } $loadAction = function () { - $cacheKey = $this->getCacheKey(); - $cacheData = $this->_cache->load($cacheKey); - if ($cacheData) { - $cacheData = str_replace( - $this->_getSidPlaceholder($cacheKey), - $this->_sidResolver->getSessionIdQueryParam($this->_session) - . '=' - . $this->_session->getSessionId(), - $cacheData - ); - } - return $cacheData; + return $this->_cache->load($this->getCacheKey()); }; $saveAction = function ($data) { @@ -1157,11 +1148,6 @@ protected function _saveCache($data) return false; } $cacheKey = $this->getCacheKey(); - $data = str_replace( - $this->_sidResolver->getSessionIdQueryParam($this->_session) . '=' . $this->_session->getSessionId(), - $this->_getSidPlaceholder($cacheKey), - $data - ); $this->_cache->save($data, $cacheKey, array_unique($this->getCacheTags()), $this->getCacheLifetime()); return $this; diff --git a/lib/internal/Magento/Framework/View/Element/BlockFactory.php b/lib/internal/Magento/Framework/View/Element/BlockFactory.php index 068cb49a66319..d691af8d98f53 100644 --- a/lib/internal/Magento/Framework/View/Element/BlockFactory.php +++ b/lib/internal/Magento/Framework/View/Element/BlockFactory.php @@ -8,7 +8,7 @@ use Magento\Framework\ObjectManagerInterface; /** - * Class BlockFactory + * Creates Blocks * * @api */ diff --git a/lib/internal/Magento/Framework/View/Element/UiComponent/DataProvider/FilterPool.php b/lib/internal/Magento/Framework/View/Element/UiComponent/DataProvider/FilterPool.php index 24ea542649875..2c0b16f27df8b 100644 --- a/lib/internal/Magento/Framework/View/Element/UiComponent/DataProvider/FilterPool.php +++ b/lib/internal/Magento/Framework/View/Element/UiComponent/DataProvider/FilterPool.php @@ -3,6 +3,7 @@ * Copyright © Magento, Inc. All rights reserved. * See COPYING.txt for license details. */ +declare(strict_types=1); namespace Magento\Framework\View\Element\UiComponent\DataProvider; @@ -10,19 +11,19 @@ use Magento\Framework\Api\Search\SearchCriteriaInterface; /** - * Class FilterPool + * Filter poll apply filters from search criteria * * @api */ class FilterPool { /** - * @var array + * @var FilterApplierInterface[] */ protected $appliers; /** - * @param array $appliers + * @param FilterApplierInterface[] $appliers */ public function __construct(array $appliers = []) { @@ -30,6 +31,8 @@ public function __construct(array $appliers = []) } /** + * Apply filters from search criteria + * * @param Collection $collection * @param SearchCriteriaInterface $criteria * @return void @@ -38,12 +41,7 @@ public function applyFilters(Collection $collection, SearchCriteriaInterface $cr { foreach ($criteria->getFilterGroups() as $filterGroup) { foreach ($filterGroup->getFilters() as $filter) { - /** @var $filterApplier FilterApplierInterface*/ - if (isset($this->appliers[$filter->getConditionType()])) { - $filterApplier = $this->appliers[$filter->getConditionType()]; - } else { - $filterApplier = $this->appliers['regular']; - } + $filterApplier = $this->appliers[$filter->getConditionType()] ?? $this->appliers['regular']; $filterApplier->apply($collection, $filter); } } diff --git a/lib/internal/Magento/Framework/View/Element/UiComponent/SubjectInterface.php b/lib/internal/Magento/Framework/View/Element/UiComponent/SubjectInterface.php index dce42950e0ab6..821ebbb4d5050 100644 --- a/lib/internal/Magento/Framework/View/Element/UiComponent/SubjectInterface.php +++ b/lib/internal/Magento/Framework/View/Element/UiComponent/SubjectInterface.php @@ -12,6 +12,7 @@ interface SubjectInterface { /** * Attach an observer by type + * * @param string $type * @param ObserverInterface $observer * @return void @@ -20,6 +21,7 @@ public function attach($type, ObserverInterface $observer); /** * Detach an observer by type + * * @param string $type * @param ObserverInterface $observer * @return void @@ -28,6 +30,7 @@ public function detach($type, ObserverInterface $observer); /** * Notify an observer(s) by type + * * @param string $type * @return void */ diff --git a/lib/internal/Magento/Framework/View/Layout.php b/lib/internal/Magento/Framework/View/Layout.php index a622006f32a1e..ebefc6200cdfa 100644 --- a/lib/internal/Magento/Framework/View/Layout.php +++ b/lib/internal/Magento/Framework/View/Layout.php @@ -3,6 +3,8 @@ * Copyright © Magento, Inc. All rights reserved. * See COPYING.txt for license details. */ +declare(strict_types=1); + namespace Magento\Framework\View; use Magento\Framework\App\ObjectManager; @@ -261,7 +263,7 @@ protected function build() /** * Public build. * - * Will be eliminated in MAGETWO-28359 + * @todo Will be eliminated in MAGETWO-28359 * * @return void */ @@ -1101,15 +1103,24 @@ protected function _prepareMessageGroup($messageGroups) } /** - * Check is exists non-cacheable layout elements + * Check existed non-cacheable layout elements. * * @return bool */ public function isCacheable() { $this->build(); - $cacheableXml = !(bool)count($this->getXml()->xpath('//' . Element::TYPE_BLOCK . '[@cacheable="false"]')); - return $this->cacheable && $cacheableXml; + $elements = $this->getXml()->xpath('//' . Element::TYPE_BLOCK . '[@cacheable="false"]'); + $cacheable = $this->cacheable; + foreach ($elements as $element) { + $blockName = $element->getBlockName(); + if ($blockName !== false && $this->structure->hasElement($blockName)) { + $cacheable = false; + break; + } + } + + return $cacheable; } /** diff --git a/lib/internal/Magento/Framework/View/Layout/Generator/Container.php b/lib/internal/Magento/Framework/View/Layout/Generator/Container.php index d220808e21455..87357e90845c0 100644 --- a/lib/internal/Magento/Framework/View/Layout/Generator/Container.php +++ b/lib/internal/Magento/Framework/View/Layout/Generator/Container.php @@ -7,6 +7,9 @@ use Magento\Framework\View\Layout; +/** + * Layout Container Class + */ class Container implements Layout\GeneratorInterface { /**#@+ @@ -39,10 +42,17 @@ class Container implements Layout\GeneratorInterface 'table', 'tfoot', 'ul', + 'article', + 'h1', + 'h2', + 'h3', + 'h4', + 'h5', + 'h6', ]; /** - * {@inheritdoc} + * @inheritdoc * * @return string */ @@ -95,8 +105,12 @@ public function generateContainer( } /** + * Validate container options + * * @param array $options + * * @return void + * * @throws \Magento\Framework\Exception\LocalizedException */ protected function validateOptions($options) diff --git a/lib/internal/Magento/Framework/View/Layout/etc/elements.xsd b/lib/internal/Magento/Framework/View/Layout/etc/elements.xsd index 17857c9ab0658..51f193109d938 100644 --- a/lib/internal/Magento/Framework/View/Layout/etc/elements.xsd +++ b/lib/internal/Magento/Framework/View/Layout/etc/elements.xsd @@ -140,6 +140,13 @@ <xs:enumeration value="table"/> <xs:enumeration value="tfoot"/> <xs:enumeration value="ul"/> + <xs:enumeration value="article"/> + <xs:enumeration value="h1"/> + <xs:enumeration value="h2"/> + <xs:enumeration value="h3"/> + <xs:enumeration value="h4"/> + <xs:enumeration value="h5"/> + <xs:enumeration value="h6"/> </xs:restriction> </xs:simpleType> diff --git a/lib/internal/Magento/Framework/View/Page/Config/Renderer.php b/lib/internal/Magento/Framework/View/Page/Config/Renderer.php index 6e64ed2150d4d..80f6fcbfc1b54 100644 --- a/lib/internal/Magento/Framework/View/Page/Config/Renderer.php +++ b/lib/internal/Magento/Framework/View/Page/Config/Renderer.php @@ -414,6 +414,7 @@ protected function renderAssetHtml(\Magento\Framework\View\Asset\PropertyGroup $ $attributes = $this->getGroupAttributes($group); $result = ''; + $template = ''; try { /** @var $asset \Magento\Framework\View\Asset\AssetInterface */ foreach ($assets as $asset) { diff --git a/lib/internal/Magento/Framework/View/TemplateEngine/Xhtml/Template.php b/lib/internal/Magento/Framework/View/TemplateEngine/Xhtml/Template.php index e75c80777ec0c..64cdf543a7571 100644 --- a/lib/internal/Magento/Framework/View/TemplateEngine/Xhtml/Template.php +++ b/lib/internal/Magento/Framework/View/TemplateEngine/Xhtml/Template.php @@ -6,7 +6,7 @@ namespace Magento\Framework\View\TemplateEngine\Xhtml; /** - * Class Template + * XML Template Engine */ class Template { @@ -34,7 +34,7 @@ public function __construct( ) { $this->logger = $logger; $document = new \DOMDocument(static::XML_VERSION, static::XML_ENCODING); - $document->loadXML($content); + $document->loadXML($content, LIBXML_PARSEHUGE); $this->templateNode = $document->documentElement; } @@ -56,9 +56,12 @@ public function getDocumentElement() */ public function append($content) { - $newFragment = $this->templateNode->ownerDocument->createDocumentFragment(); - $newFragment->appendXML($content); - $this->templateNode->appendChild($newFragment); + $ownerDocument= $this->templateNode->ownerDocument; + $document = new \DOMDocument(); + $document->loadXml($content, LIBXML_PARSEHUGE); + $this->templateNode->appendChild( + $ownerDocument->importNode($document->documentElement, true) + ); } /** diff --git a/lib/internal/Magento/Framework/View/Test/Unit/Layout/Generator/ContainerTest.php b/lib/internal/Magento/Framework/View/Test/Unit/Layout/Generator/ContainerTest.php index 68c673e95a82d..1752847943dd3 100644 --- a/lib/internal/Magento/Framework/View/Test/Unit/Layout/Generator/ContainerTest.php +++ b/lib/internal/Magento/Framework/View/Test/Unit/Layout/Generator/ContainerTest.php @@ -138,6 +138,26 @@ public function processDataProvider() ['first_container', Container::CONTAINER_OPT_HTML_ID, 'dd_id'], ], 'setAttributeCalls' => 3, + ], + 'Article as allowed container tag' => [ + 'structureElements' => [ + 'first_container' => [ + 'container', + [ + 'attributes' => [ + Container::CONTAINER_OPT_HTML_TAG => 'article', + Container::CONTAINER_OPT_HTML_CLASS => 'article_class', + Container::CONTAINER_OPT_HTML_ID => 'article_id', + ] + ], + ], + ], + 'setAttributeData' => [ + ['first_container', Container::CONTAINER_OPT_HTML_TAG, 'article'], + ['first_container', Container::CONTAINER_OPT_HTML_CLASS, 'article_class'], + ['first_container', Container::CONTAINER_OPT_HTML_ID, 'article_id'], + ], + 'setAttributeCalls' => 3, ] ]; } diff --git a/lib/internal/Magento/Framework/View/Test/Unit/LayoutTest.php b/lib/internal/Magento/Framework/View/Test/Unit/LayoutTest.php index a11d132b193a0..b9754150bb92e 100644 --- a/lib/internal/Magento/Framework/View/Test/Unit/LayoutTest.php +++ b/lib/internal/Magento/Framework/View/Test/Unit/LayoutTest.php @@ -3,172 +3,193 @@ * Copyright © Magento, Inc. All rights reserved. * See COPYING.txt for license details. */ +declare(strict_types=1); namespace Magento\Framework\View\Test\Unit; +use Magento\Framework\App\State; +use Magento\Framework\Cache\FrontendInterface; +use Magento\Framework\DataObject; +use Magento\Framework\Event\ManagerInterface as EventManager; +use Magento\Framework\Message\ManagerInterface; use Magento\Framework\Serialize\SerializerInterface; +use Magento\Framework\TestFramework\Unit\Helper\ObjectManager as ObjectManagerHelper; +use Magento\Framework\View\Design\Theme\ResolverInterface; +use Magento\Framework\View\Design\ThemeInterface; +use Magento\Framework\View\Element\AbstractBlock; +use Magento\Framework\View\Element\Template; +use Magento\Framework\View\Layout; +use Magento\Framework\View\Layout\Data\Structure as LayoutStructure; +use Magento\Framework\View\Layout\Element; +use Magento\Framework\View\Layout\Generator\Block; +use Magento\Framework\View\Layout\Generator\Container; +use Magento\Framework\View\Layout\Generator\Context; +use Magento\Framework\View\Layout\Generator\ContextFactory; +use Magento\Framework\View\Layout\GeneratorPool; +use Magento\Framework\View\Layout\ProcessorFactory; +use Magento\Framework\View\Layout\Reader\Context as LayoutReaderContext; +use Magento\Framework\View\Layout\Reader\ContextFactory as LayoutReaderContextFactory; +use Magento\Framework\View\Layout\ReaderPool; +use Magento\Framework\View\Layout\ScheduledStructure; +use Magento\Framework\View\Model\Layout\Merge; +use Magento\Framework\View\Page\Config\Structure; +use PHPUnit\Framework\MockObject\MockObject; +use PHPUnit\Framework\TestCase; +use Psr\Log\LoggerInterface; /** + * Unit tests for \Magento\Framework\View\Layout class. + * * @SuppressWarnings(PHPMD.TooManyFields) * @SuppressWarnings(PHPMD.CouplingBetweenObjects) */ -class LayoutTest extends \PHPUnit\Framework\TestCase +class LayoutTest extends TestCase { /** - * @var \Magento\Framework\View\Layout + * @var Layout */ - protected $model; + private $model; /** - * @var \PHPUnit_Framework_MockObject_MockObject + * @var LayoutStructure|MockObject */ - protected $structureMock; + private $structureMock; /** - * @var \Magento\Framework\View\Layout\ProcessorFactory|\PHPUnit_Framework_MockObject_MockObject + * @var ProcessorFactory|MockObject */ - protected $processorFactoryMock; + private $processorFactoryMock; /** - * @var \Magento\Framework\View\Design\Theme\ResolverInterface|\PHPUnit_Framework_MockObject_MockObject + * @var ResolverInterface|MockObject */ - protected $themeResolverMock; + private $themeResolverMock; /** - * @var \Magento\Framework\View\Model\Layout\Merge|\PHPUnit_Framework_MockObject_MockObject + * @var Merge|MockObject */ - protected $processorMock; + private $processorMock; /** - * @var \Magento\Framework\Event\ManagerInterface|\PHPUnit_Framework_MockObject_MockObject + * @var EventManager|MockObject */ - protected $eventManagerMock; + private $eventManagerMock; /** - * @var \Magento\Framework\View\Layout\Generator\Block|\PHPUnit_Framework_MockObject_MockObject + * @var Block|MockObject */ - protected $generatorBlockMock; + private $generatorBlockMock; /** - * @var \Magento\Framework\View\Layout\Generator\Container|\PHPUnit_Framework_MockObject_MockObject + * @var Container|MockObject */ - protected $generatorContainerMock; + private $generatorContainerMock; /** - * @var \Magento\Framework\Cache\FrontendInterface|\PHPUnit_Framework_MockObject_MockObject + * @var FrontendInterface|MockObject */ - protected $cacheMock; + private $cacheMock; /** - * @var \Magento\Framework\View\Layout\ReaderPool|\PHPUnit_Framework_MockObject_MockObject + * @var ReaderPool|MockObject */ - protected $readerPoolMock; + private $readerPoolMock; /** - * @var \Magento\Framework\View\Layout\GeneratorPool|\PHPUnit_Framework_MockObject_MockObject + * @var GeneratorPool|MockObject */ - protected $generatorPoolMock; + private $generatorPoolMock; /** - * @var \Magento\Framework\Message\ManagerInterface|\PHPUnit_Framework_MockObject_MockObject + * @var ManagerInterface|MockObject */ - protected $messageManagerMock; + private $messageManagerMock; /** - * @var \Magento\Framework\View\Layout\Reader\ContextFactory|\PHPUnit_Framework_MockObject_MockObject + * @var LayoutReaderContextFactory|MockObject */ - protected $readerContextFactoryMock; + private $readerContextFactoryMock; /** - * @var \Magento\Framework\View\Layout\Reader\Context|\PHPUnit_Framework_MockObject_MockObject + * @var Layout\Reader\Context|MockObject */ - protected $readerContextMock; + private $readerContextMock; /** - * @var \Magento\Framework\View\Page\Config\Structure|\PHPUnit_Framework_MockObject_MockObject + * @var Structure|MockObject */ private $pageConfigStructure; /** - * @var \Magento\Framework\View\Layout\ScheduledStructure|\PHPUnit_Framework_MockObject_MockObject + * @var ScheduledStructure|MockObject */ private $layoutScheduledSructure; /** - * @var \Magento\Framework\View\Layout\Generator\ContextFactory|\PHPUnit_Framework_MockObject_MockObject + * @var ContextFactory|MockObject */ - protected $generatorContextFactoryMock; + private $generatorContextFactoryMock; /** - * @var \Magento\Framework\App\State|\PHPUnit_Framework_MockObject_MockObject + * @var State|MockObject */ - protected $appStateMock; + private $appStateMock; /** - * @var \Psr\Log\LoggerInterface|\PHPUnit_Framework_MockObject_MockObject + * @var LoggerInterface|MockObject */ - protected $loggerMock; + private $loggerMock; /** - * @var SerializerInterface|\PHPUnit_Framework_MockObject_MockObject + * @var SerializerInterface|MockObject */ private $serializer; /** + * @inheritdoc * @SuppressWarnings(PHPMD.ExcessiveMethodLength) */ protected function setUp() { - $this->structureMock = $this->getMockBuilder(\Magento\Framework\View\Layout\Data\Structure::class) + $this->structureMock = $this->getMockBuilder(LayoutStructure::class) ->disableOriginalConstructor() ->getMock(); - $this->processorFactoryMock = $this->createPartialMock( - \Magento\Framework\View\Layout\ProcessorFactory::class, - ['create'] - ); - $this->themeResolverMock = $this->getMockForAbstractClass( - \Magento\Framework\View\Design\Theme\ResolverInterface::class - ); - $this->processorMock = $this->createMock(\Magento\Framework\View\Model\Layout\Merge::class); - $this->eventManagerMock = $this->createMock(\Magento\Framework\Event\ManagerInterface::class); - $this->generatorBlockMock = $this->getMockBuilder(\Magento\Framework\View\Layout\Generator\Block::class) - ->disableOriginalConstructor()->getMock(); - $this->generatorContainerMock = $this->getMockBuilder(\Magento\Framework\View\Layout\Generator\Container::class) - ->disableOriginalConstructor()->getMock(); - $this->cacheMock = $this->getMockBuilder(\Magento\Framework\Cache\FrontendInterface::class) + $this->processorFactoryMock = $this->createPartialMock(ProcessorFactory::class, ['create']); + $this->themeResolverMock = $this->getMockForAbstractClass(ResolverInterface::class); + $this->processorMock = $this->createMock(Merge::class); + $this->eventManagerMock = $this->getMockForAbstractClass(EventManager::class); + $this->generatorBlockMock = $this->getMockBuilder(Block::class) ->disableOriginalConstructor() ->getMock(); - $this->readerPoolMock = $this->getMockBuilder(\Magento\Framework\View\Layout\ReaderPool::class) + $this->generatorContainerMock = $this->getMockBuilder(Container::class) ->disableOriginalConstructor() ->getMock(); - $this->messageManagerMock = $this->getMockBuilder(\Magento\Framework\Message\ManagerInterface::class) + $this->cacheMock = $this->getMockForAbstractClass(FrontendInterface::class); + $this->readerPoolMock = $this->getMockBuilder(ReaderPool::class) + ->disableOriginalConstructor() + ->getMock(); + $this->messageManagerMock = $this->getMockForAbstractClass(ManagerInterface::class); + $this->generatorPoolMock = $this->getMockBuilder(GeneratorPool::class) ->disableOriginalConstructor() ->getMock(); - - $this->generatorPoolMock = $this->getMockBuilder(\Magento\Framework\View\Layout\GeneratorPool::class) - ->disableOriginalConstructor()->getMock(); $this->generatorPoolMock->expects($this->any()) ->method('getGenerator') - ->will( - $this->returnValueMap( - [ - [\Magento\Framework\View\Layout\Generator\Block::TYPE, $this->generatorBlockMock], - [\Magento\Framework\View\Layout\Generator\Container::TYPE, $this->generatorContainerMock], - ] - ) + ->willReturnMap( + [ + [Block::TYPE, $this->generatorBlockMock], + [Container::TYPE, $this->generatorContainerMock], + ] ); - - $this->readerContextFactoryMock = $this->getMockBuilder( - \Magento\Framework\View\Layout\Reader\ContextFactory::class - )->disableOriginalConstructor()->getMock(); - - $this->pageConfigStructure = $this->getMockBuilder(\Magento\Framework\View\Page\Config\Structure::class) + $this->readerContextFactoryMock = $this->getMockBuilder(LayoutReaderContextFactory::class) + ->disableOriginalConstructor() + ->getMock(); + $this->pageConfigStructure = $this->getMockBuilder(Structure::class) ->setMethods(['__toArray', 'populateWithArray']) ->getMock(); - $this->layoutScheduledSructure = $this->getMockBuilder(\Magento\Framework\View\Layout\ScheduledStructure::class) + $this->layoutScheduledSructure = $this->getMockBuilder(ScheduledStructure::class) ->setMethods(['__toArray', 'populateWithArray']) ->getMock(); - $this->readerContextMock = $this->getMockBuilder(\Magento\Framework\View\Layout\Reader\Context::class) + $this->readerContextMock = $this->getMockBuilder(LayoutReaderContext::class) ->setMethods(['getPageConfigStructure', 'getScheduledStructure']) ->disableOriginalConstructor() ->getMock(); @@ -176,125 +197,135 @@ protected function setUp() ->willReturn($this->pageConfigStructure); $this->readerContextMock->expects($this->any())->method('getScheduledStructure') ->willReturn($this->layoutScheduledSructure); - - $this->generatorContextFactoryMock = $this->getMockBuilder( - \Magento\Framework\View\Layout\Generator\ContextFactory::class - ) + $this->generatorContextFactoryMock = $this->getMockBuilder(ContextFactory::class) ->disableOriginalConstructor() ->getMock(); - $this->appStateMock = $this->getMockBuilder(\Magento\Framework\App\State::class) + $this->appStateMock = $this->getMockBuilder(State::class) ->disableOriginalConstructor() ->getMock(); - $this->loggerMock = $this->getMockBuilder(\Psr\Log\LoggerInterface::class) - ->getMock(); - $this->serializer = $this->createMock(\Magento\Framework\Serialize\SerializerInterface::class); + $this->loggerMock = $this->getMockForAbstractClass(LoggerInterface::class); + $this->serializer = $this->getMockForAbstractClass(SerializerInterface::class); $this->serializer->expects($this->any())->method('serialize') ->willReturnCallback( function ($value) { return json_encode($value); } ); - $this->serializer->expects( - $this->any() - )->method('unserialize') + $this->serializer->expects($this->any())->method('unserialize') ->willReturnCallback( function ($value) { return json_decode($value, true); } ); - $this->model = new \Magento\Framework\View\Layout( - $this->processorFactoryMock, - $this->eventManagerMock, - $this->structureMock, - $this->messageManagerMock, - $this->themeResolverMock, - $this->readerPoolMock, - $this->generatorPoolMock, - $this->cacheMock, - $this->readerContextFactoryMock, - $this->generatorContextFactoryMock, - $this->appStateMock, - $this->loggerMock, - true, - $this->serializer + $this->model = (new ObjectManagerHelper($this))->getObject( + Layout::class, + [ + 'processorFactory' => $this->processorFactoryMock, + 'eventManager' => $this->eventManagerMock, + 'structure' => $this->structureMock, + 'messageManager' => $this->messageManagerMock, + 'themeResolver' => $this->themeResolverMock, + 'readerPool' => $this->readerPoolMock, + 'generatorPool' => $this->generatorPoolMock, + 'cache' => $this->cacheMock, + 'readerContextFactory' => $this->readerContextFactoryMock, + 'generatorContextFactory' => $this->generatorContextFactoryMock, + 'appState' => $this->appStateMock, + 'logger' => $this->loggerMock, + 'cacheable' => true, + 'serializer' => $this->serializer, + ] ); } - public function testCreateBlockSuccess() + /** + * @return void + */ + public function testCreateBlockSuccess(): void { - $blockMock = $this->getMockBuilder(\Magento\Framework\View\Element\AbstractBlock::class) + $blockMock = $this->getMockBuilder(AbstractBlock::class) ->disableOriginalConstructor() ->getMockForAbstractClass(); $this->structureMock->expects($this->once()) ->method('createStructuralElement') ->with( 'blockname', - \Magento\Framework\View\Layout\Element::TYPE_BLOCK, - \Magento\Framework\View\Element\AbstractBlock::class - )->willReturn('blockname'); - $this->generatorBlockMock->expects($this->once())->method('createBlock')->will($this->returnValue($blockMock)); + Element::TYPE_BLOCK, + AbstractBlock::class + ) + ->willReturn('blockname'); + $this->generatorBlockMock->expects($this->once())->method('createBlock')->willReturn($blockMock); - $this->model->createBlock(\Magento\Framework\View\Element\AbstractBlock::class, 'blockname', []); + $this->model->createBlock(AbstractBlock::class, 'blockname', []); $this->assertInstanceOf( - \Magento\Framework\View\Element\AbstractBlock::class, + AbstractBlock::class, $this->model->getBlock('blockname') ); $this->assertFalse($this->model->getBlock('not_exist')); } - public function testGetUpdate() + /** + * @return void + */ + public function testGetUpdate(): void { - $themeMock = $this->getMockForAbstractClass(\Magento\Framework\View\Design\ThemeInterface::class); + $themeMock = $this->getMockForAbstractClass(ThemeInterface::class); $this->themeResolverMock->expects($this->once()) ->method('get') - ->will($this->returnValue($themeMock)); + ->willReturn($themeMock); $this->processorFactoryMock->expects($this->once()) ->method('create') ->with(['theme' => $themeMock]) - ->will($this->returnValue($this->processorMock)); + ->willReturn($this->processorMock); $this->assertEquals($this->processorMock, $this->model->getUpdate()); $this->assertEquals($this->processorMock, $this->model->getUpdate()); } - public function testGenerateXml() + /** + * @return void + */ + public function testGenerateXml(): void { - $themeMock = $this->getMockForAbstractClass(\Magento\Framework\View\Design\ThemeInterface::class); + $themeMock = $this->getMockForAbstractClass(ThemeInterface::class); $this->themeResolverMock->expects($this->once()) ->method('get') - ->will($this->returnValue($themeMock)); + ->willReturn($themeMock); $this->processorFactoryMock->expects($this->once()) ->method('create') ->with(['theme' => $themeMock]) - ->will($this->returnValue($this->processorMock)); + ->willReturn($this->processorMock); $xmlString = '<?xml version="1.0"?><layout xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">' . '<some_update>123</some_update></layout>'; - $xml = simplexml_load_string($xmlString, \Magento\Framework\View\Layout\Element::class); + $xml = simplexml_load_string($xmlString, Element::class); $this->processorMock->expects($this->once()) ->method('asSimplexml') - ->will($this->returnValue($xml)); + ->willReturn($xml); $this->structureMock->expects($this->once()) ->method('importElements') ->with($this->equalTo([])) - ->will($this->returnSelf()); + ->willReturnSelf(); $this->assertSame($this->model, $this->model->generateXml()); $this->assertSame('<some_update>123</some_update>', $this->model->getNode('some_update')->asXML()); } - public function testGetChildBlock() + /** + * @return void + */ + public function testGetChildBlock(): void { $customBlockName = 'custom_block'; $customBlockParentName = 'custom_block_parent'; $customBlockAlias = 'custom_block_alias'; - $blockMock = $this->getMockBuilder(\Magento\Framework\View\Element\Template::class) + $blockMock = $this->getMockBuilder(Template::class) ->disableOriginalConstructor() ->getMock(); @@ -311,16 +342,19 @@ public function testGetChildBlock() $this->structureMock->expects($this->once()) ->method('getAttribute') ->with($customBlockName, 'type') - ->willReturn(\Magento\Framework\View\Layout\Element::TYPE_BLOCK); + ->willReturn(Element::TYPE_BLOCK); $this->model->setBlock($customBlockName, $blockMock); $this->assertInstanceOf( - \Magento\Framework\View\Element\AbstractBlock::class, + AbstractBlock::class, $this->model->getChildBlock($customBlockParentName, $customBlockAlias) ); } - public function testGetChildNonExistBlock() + /** + * @return void + */ + public function testGetChildNonExistBlock(): void { $this->structureMock->expects($this->once()) ->method('getChildId') @@ -329,7 +363,10 @@ public function testGetChildNonExistBlock() $this->assertFalse($this->model->getChildBlock('non_exist_parent', 'non_exist_alias')); } - public function testSetChild() + /** + * @return void + */ + public function testSetChild(): void { $elementName = 'child'; $parentName = 'parent'; @@ -337,104 +374,131 @@ public function testSetChild() $this->structureMock->expects($this->once()) ->method('setAsChild') ->with($this->equalTo($elementName), $this->equalTo($parentName), $this->equalTo($alias)) - ->will($this->returnSelf()); + ->willReturnSelf(); $this->assertSame($this->model, $this->model->setChild($parentName, $elementName, $alias)); } - public function testUnsetChild() + /** + * @return void + */ + public function testUnsetChild(): void { $parentName = 'parent'; $alias = 'some_alias'; $this->structureMock->expects($this->once()) ->method('unsetChild') ->with($this->equalTo($parentName), $this->equalTo($alias)) - ->will($this->returnSelf()); + ->willReturnSelf(); $this->assertSame($this->model, $this->model->unsetChild($parentName, $alias)); } - public function testGetChildNames() + /** + * @return void + */ + public function testGetChildNames(): void { $parentName = 'parent'; $childrenArray = ['key1' => 'value1', 'key2' => 'value2']; $this->structureMock->expects($this->once()) ->method('getChildren') ->with($this->equalTo($parentName)) - ->will($this->returnValue($childrenArray)); + ->willReturn($childrenArray); $this->assertSame(['key1', 'key2'], $this->model->getChildNames($parentName)); } - public function testGetChildBlocks() + /** + * @return void + */ + public function testGetChildBlocks(): void { $parentName = 'parent'; $childrenArray = ['block_name' => 'value1']; $this->structureMock->expects($this->once()) ->method('getChildren') ->with($this->equalTo($parentName)) - ->will($this->returnValue($childrenArray)); + ->willReturn($childrenArray); - $blockMock = $this->getMockBuilder(\Magento\Framework\View\Element\AbstractBlock::class) + $blockMock = $this->getMockBuilder(AbstractBlock::class) ->disableOriginalConstructor() ->getMockForAbstractClass(); $this->structureMock->expects($this->once()) ->method('createStructuralElement') ->with( 'block_name', - \Magento\Framework\View\Layout\Element::TYPE_BLOCK, - \Magento\Framework\View\Element\AbstractBlock::class - )->willReturn('block_name'); - $this->generatorBlockMock->expects($this->once())->method('createBlock')->will($this->returnValue($blockMock)); + Element::TYPE_BLOCK, + AbstractBlock::class + ) + ->willReturn('block_name'); + $this->generatorBlockMock->expects($this->once()) + ->method('createBlock') + ->willReturn($blockMock); $this->assertSame( $blockMock, - $this->model->createBlock(\Magento\Framework\View\Element\AbstractBlock::class, 'block_name', []) + $this->model->createBlock(AbstractBlock::class, 'block_name', []) ); $this->assertSame(['value1' => $blockMock], $this->model->getChildBlocks($parentName)); } - public function testGetChildName() + /** + * @return void + */ + public function testGetChildName(): void { $parentName = 'parent'; $alias = 'some_alias'; $this->structureMock->expects($this->once()) ->method('getChildId') ->with($this->equalTo($parentName), $this->equalTo($alias)) - ->will($this->returnValue('1')); + ->willReturn('1'); $this->assertSame('1', $this->model->getChildName($parentName, $alias)); } - public function testAddToParentGroup() + /** + * @return void + */ + public function testAddToParentGroup(): void { $blockName = 'block_name'; $parentGroup = 'parent_group'; $this->structureMock->expects($this->once()) ->method('addToParentGroup') ->with($this->equalTo($blockName), $this->equalTo($parentGroup)) - ->will($this->returnSelf()); + ->willReturnSelf(); $this->assertSame($this->structureMock, $this->model->addToParentGroup($blockName, $parentGroup)); } - public function testGetGroupChildNames() + /** + * @return void + */ + public function testGetGroupChildNames(): void { $blockName = 'block_name'; $groupName = 'group_name'; $this->structureMock->expects($this->once()) ->method('getGroupChildNames') ->with($this->equalTo($blockName), $this->equalTo($groupName)) - ->will($this->returnSelf()); + ->willReturnSelf(); $this->assertSame($this->structureMock, $this->model->getGroupChildNames($blockName, $groupName)); } - public function testHasElement() + /** + * @return void + */ + public function testHasElement(): void { $elementName = 'name'; $this->structureMock->expects($this->once()) ->method('hasElement') ->with($this->equalTo($elementName)) - ->will($this->returnValue(true)); + ->willReturn(true); $this->assertTrue($this->model->hasElement($elementName)); } - public function testGetElementProperty() + /** + * @return void + */ + public function testGetElementProperty(): void { $elementName = 'name'; $elementAttr = 'attribute'; @@ -442,7 +506,7 @@ public function testGetElementProperty() $this->structureMock->expects($this->once()) ->method('getAttribute') ->with($this->equalTo($elementName), $this->equalTo($elementAttr)) - ->will($this->returnValue($result)); + ->willReturn($result); $this->assertSame($result, $this->model->getElementProperty($elementName, $elementAttr)); } @@ -450,20 +514,21 @@ public function testGetElementProperty() * @param bool $hasElement * @param string $attribute * @param bool $result + * @return void * @dataProvider isContainerDataProvider */ - public function testIsContainer($hasElement, $attribute, $result) + public function testIsContainer($hasElement, $attribute, $result): void { $elementName = 'element_name'; $this->structureMock->expects($this->once()) ->method('hasElement') ->with($this->equalTo($elementName)) - ->will($this->returnValue($hasElement)); + ->willReturn($hasElement); if ($hasElement) { $this->structureMock->expects($this->once()) ->method('getAttribute') ->with($this->equalTo($elementName), $this->equalTo('type')) - ->will($this->returnValue($attribute)); + ->willReturn($attribute); } $this->assertSame($result, $this->model->isContainer($elementName)); } @@ -471,7 +536,7 @@ public function testIsContainer($hasElement, $attribute, $result) /** * @return array */ - public function isContainerDataProvider() + public function isContainerDataProvider(): array { return [ [false, '', false], @@ -485,25 +550,26 @@ public function isContainerDataProvider() * @param bool $parentName * @param array $containerConfig * @param bool $result + * @return void * @dataProvider isManipulationAllowedDataProvider */ - public function testIsManipulationAllowed($parentName, $containerConfig, $result) + public function testIsManipulationAllowed($parentName, $containerConfig, $result): void { $elementName = 'element_name'; $this->structureMock->expects($this->once()) ->method('getParentId') ->with($this->equalTo($elementName)) - ->will($this->returnValue($parentName)); + ->willReturn($parentName); if ($parentName) { $this->structureMock->expects($this->once()) ->method('hasElement') ->with($this->equalTo($parentName)) - ->will($this->returnValue($containerConfig['has_element'])); + ->willReturn($containerConfig['has_element']); if ($containerConfig['has_element']) { $this->structureMock->expects($this->once()) ->method('getAttribute') ->with($this->equalTo($parentName), $this->equalTo('type')) - ->will($this->returnValue($containerConfig['attribute'])); + ->willReturn($containerConfig['attribute']); } } @@ -513,7 +579,7 @@ public function testIsManipulationAllowed($parentName, $containerConfig, $result /** * @return array */ - public function isManipulationAllowedDataProvider() + public function isManipulationAllowedDataProvider(): array { return [ ['parent', ['has_element' => true, 'attribute' => 'container'], true], @@ -526,11 +592,12 @@ public function isManipulationAllowedDataProvider() * @covers \Magento\Framework\View\Layout::setBlock * @covers \Magento\Framework\View\Layout::getAllBlocks * @covers \Magento\Framework\View\Layout::unsetElement + * @return void */ - public function testSetGetBlock() + public function testSetGetBlock(): void { $blockName = 'some_name'; - $blockMock = $this->getMockBuilder(\Magento\Framework\View\Element\AbstractBlock::class) + $blockMock = $this->getMockBuilder(AbstractBlock::class) ->disableOriginalConstructor() ->getMockForAbstractClass(); $this->assertSame($this->model, $this->model->setBlock($blockName, $blockMock)); @@ -538,40 +605,49 @@ public function testSetGetBlock() $this->structureMock->expects($this->once()) ->method('unsetElement') ->with($this->equalTo($blockName)) - ->will($this->returnSelf()); + ->willReturnSelf(); $this->assertSame($this->model, $this->model->unsetElement($blockName)); $this->assertSame([], $this->model->getAllBlocks()); } - public function testRenameElement() + /** + * @return void + */ + public function testRenameElement(): void { $oldName = 'old_name'; $newName = 'new_name'; - $blockMock = $this->getMockBuilder(\Magento\Framework\View\Element\AbstractBlock::class) + $blockMock = $this->getMockBuilder(AbstractBlock::class) ->disableOriginalConstructor() ->getMockForAbstractClass(); $this->structureMock->expects($this->once()) ->method('renameElement') ->with($this->equalTo($oldName), $this->equalTo($newName)) - ->will($this->returnSelf()); + ->willReturnSelf(); $this->assertSame($this->model, $this->model->setBlock($oldName, $blockMock)); $this->assertSame($this->model, $this->model->renameElement($oldName, $newName)); $this->assertSame([$newName => $blockMock], $this->model->getAllBlocks()); } - public function testGetParentName() + /** + * @return void + */ + public function testGetParentName(): void { $childName = 'child_name'; $parentId = 'parent_id'; $this->structureMock->expects($this->once()) ->method('getParentId') ->with($this->equalTo($childName)) - ->will($this->returnValue($parentId)); + ->willReturn($parentId); $this->assertSame($parentId, $this->model->getParentName($childName)); } - public function testGetElementAlias() + /** + * @return void + */ + public function testGetElementAlias(): void { $name = 'child_name'; $parentId = 'parent_id'; @@ -579,21 +655,27 @@ public function testGetElementAlias() $this->structureMock->expects($this->once()) ->method('getParentId') ->with($this->equalTo($name)) - ->will($this->returnValue($parentId)); + ->willReturn($parentId); $this->structureMock->expects($this->once()) ->method('getChildAlias') ->with($this->equalTo($parentId), $this->equalTo($name)) - ->will($this->returnValue($alias)); + ->willReturn($alias); $this->assertSame($alias, $this->model->getElementAlias($name)); } - public function testAddRemoveOutputElement() + /** + * @return void + */ + public function testAddRemoveOutputElement(): void { $this->assertSame($this->model, $this->model->addOutputElement('name')); $this->assertSame($this->model, $this->model->removeOutputElement('name')); } - public function testIsPrivate() + /** + * @return void + */ + public function testIsPrivate(): void { $this->assertFalse($this->model->isPrivate()); $this->assertSame($this->model, $this->model->setIsPrivate(true)); @@ -603,18 +685,19 @@ public function testIsPrivate() /** * @param array $type * @param array $blockInstance + * @return void * @dataProvider getBlockSingletonDataProvider */ - public function testGetBlockSingleton($type, $blockInstance, $isAbstract) + public function testGetBlockSingleton($type, $blockInstance, $isAbstract): void { $blockMock = $this->createMock($blockInstance); - $this->generatorBlockMock->expects($this->once())->method('createBlock')->will($this->returnValue($blockMock)); + $this->generatorBlockMock->expects($this->once())->method('createBlock')->willReturn($blockMock); if ($isAbstract) { $blockMock->expects($this->any()) ->method('setLayout') ->with($this->equalTo($this->model)) - ->will($this->returnSelf()); + ->willReturnSelf(); } $this->assertInstanceOf($blockInstance, $this->model->getBlockSingleton($type)); // singleton test @@ -624,11 +707,12 @@ public function testGetBlockSingleton($type, $blockInstance, $isAbstract) /** * @return array */ - public function getBlockSingletonDataProvider() + public function getBlockSingletonDataProvider(): array { return [ [ - 'some_type', \Magento\Framework\View\Element\Template::class, + 'some_type', + Template::class, true, ], ]; @@ -638,9 +722,10 @@ public function getBlockSingletonDataProvider() * @param array $rendererData * @param array $getData * @param bool $result + * @return void * @dataProvider getRendererOptionsDataProvider */ - public function testAddGetRendererOptions($rendererData, $getData, $result) + public function testAddGetRendererOptions($rendererData, $getData, $result): void { $this->assertSame( $this->model, @@ -662,7 +747,7 @@ public function testAddGetRendererOptions($rendererData, $getData, $result) /** * @return array */ - public function getRendererOptionsDataProvider() + public function getRendererOptionsDataProvider(): array { $rendererData = [ 'namespace' => 'namespace_value', @@ -718,47 +803,94 @@ public function getRendererOptionsDataProvider() /** * @param string $xmlString - * @param bool $result + * @param string $blockName + * @param bool $hasElement + * @param bool $cacheable + * @return void * @dataProvider isCacheableDataProvider */ - public function testIsCacheable($xmlString, $result) + public function testIsCacheable(string $xmlString, string $blockName, bool $hasElement, bool $cacheable): void { - $xml = simplexml_load_string($xmlString, \Magento\Framework\View\Layout\Element::class); - $this->assertSame($this->model, $this->model->setXml($xml)); - $this->assertSame($result, $this->model->isCacheable()); + $this->structureMock->method('hasElement')->with($blockName)->willReturn($hasElement); + + $this->assertTrue($this->model->loadString($xmlString)); + $this->assertSame($cacheable, $this->model->isCacheable()); } /** * @return array */ - public function isCacheableDataProvider() + public function isCacheableDataProvider(): array { return [ - [ - '<?xml version="1.0"?><layout xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">' - . '<block></block></layout>', - true, + 'blockWithoutName' => [ + 'xml' => '<?xml version="1.0"?><layout xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">' + . '<block></block></layout>', + 'blockName' => '', + 'hasElement' => true, + 'cacheable' => true, ], - [ - '<?xml version="1.0"?><layout xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">' - . '<block cacheable="false"></block></layout>', - false + 'notCacheableBlockWithoutName' => [ + 'xml' => '<?xml version="1.0"?><layout xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">' + . '<block cacheable="false"></block></layout>', + 'blockName' => '', + 'hasElement' => true, + 'cacheable' => true, + ], + 'notCacheableBlockWithMissingBlockReference' => [ + 'xml' => '<?xml version="1.0"?><layout xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">' + . '<referenceBlock name="not_existing_block">' + . '<block name="non_cacheable_block" cacheable="false"></block>' + . '</referenceBlock></layout>', + 'blockName' => 'non_cacheable_block', + 'hasElement' => false, + 'cacheable' => true, + ], + 'notCacheableBlockWithMissingContainerReference' => [ + 'xml' => '<?xml version="1.0"?><layout xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">' + . '<referenceContainer name="not_existing_container">' + . '<block name="non_cacheable_block" cacheable="false"></block>' + . '</referenceContainer></layout>', + 'blockName' => 'non_cacheable_block', + 'hasElement' => false, + 'cacheable' => true, + ], + 'notCacheableBlockWithExistingBlockReference' => [ + 'xml' => '<?xml version="1.0"?><layout xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">' + . '<referenceBlock name="existing_block">' + . '<block name="non_cacheable_block" cacheable="false"></block>' + . '</referenceBlock></layout>', + 'blockName' => 'non_cacheable_block', + 'hasElement' => true, + 'cacheable' => false, + ], + 'notCacheableBlockWithExistingContainerReference' => [ + 'xml' => '<?xml version="1.0"?><layout xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">' + . '<referenceContainer name="existing_container">' + . '<block name="non_cacheable_block" cacheable="false"></block>' + . '</referenceContainer></layout>', + 'blockName' => 'non_cacheable_block', + 'hasElement' => true, + 'cacheable' => false, ], ]; } - public function testGenerateElementsWithoutCache() + /** + * @return void + */ + public function testGenerateElementsWithoutCache(): void { $this->readerContextFactoryMock->expects($this->once()) ->method('create') ->willReturn($this->readerContextMock); $layoutCacheId = 'layout_cache_id'; $handles = ['default', 'another']; - /** @var \Magento\Framework\View\Layout\Element $xml */ - $xml = simplexml_load_string('<layout/>', \Magento\Framework\View\Layout\Element::class); + /** @var Element $xml */ + $xml = simplexml_load_string('<layout/>', Element::class); $this->model->setXml($xml); - $themeMock = $this->getMockForAbstractClass(\Magento\Framework\View\Design\ThemeInterface::class); + $themeMock = $this->getMockForAbstractClass(ThemeInterface::class); $this->themeResolverMock->expects($this->once()) ->method('get') ->willReturn($themeMock); @@ -791,20 +923,22 @@ public function testGenerateElementsWithoutCache() 'field_3_1' => '1244', 'field_3_2' => null, 'field_3_3' => false, - ] + ], ]; - $this->pageConfigStructure->expects($this->any())->method('__toArray') + $this->pageConfigStructure->expects($this->any()) + ->method('__toArray') ->willReturn($pageConfigStructureData); $layoutScheduledStructureData = [ 'field_1' => 1283, - 'field_2' => 'text_qwertyuiop[]asdfghjkl;' + 'field_2' => 'text_qwertyuiop[]asdfghjkl;', ]; - $this->layoutScheduledSructure->expects($this->any())->method('__toArray') + $this->layoutScheduledSructure->expects($this->any()) + ->method('__toArray') ->willReturn($layoutScheduledStructureData); $data = [ 'pageConfigStructure' => $pageConfigStructureData, - 'scheduledStructure' => $layoutScheduledStructureData + 'scheduledStructure' => $layoutScheduledStructureData, ]; $this->cacheMock->expects($this->once()) @@ -812,7 +946,7 @@ public function testGenerateElementsWithoutCache() ->with(json_encode($data), 'structure_' . $layoutCacheId, $handles) ->willReturn(true); - $generatorContextMock = $this->getMockBuilder(\Magento\Framework\View\Layout\Generator\Context::class) + $generatorContextMock = $this->getMockBuilder(Context::class) ->disableOriginalConstructor() ->getMock(); $this->generatorContextFactoryMock->expects($this->once()) @@ -827,9 +961,9 @@ public function testGenerateElementsWithoutCache() $elements = [ 'name_1' => ['type' => '', 'parent' => null], - 'name_2' => ['type' => \Magento\Framework\View\Layout\Element::TYPE_CONTAINER, 'parent' => null], + 'name_2' => ['type' => Element::TYPE_CONTAINER, 'parent' => null], 'name_3' => ['type' => '', 'parent' => 'parent'], - 'name_4' => ['type' => \Magento\Framework\View\Layout\Element::TYPE_CONTAINER, 'parent' => 'parent'], + 'name_4' => ['type' => Element::TYPE_CONTAINER, 'parent' => 'parent'], ]; $this->structureMock->expects($this->once()) @@ -839,17 +973,20 @@ public function testGenerateElementsWithoutCache() $this->model->generateElements(); } - public function testGenerateElementsWithCache() + /** + * @return void + */ + public function testGenerateElementsWithCache(): void { $layoutCacheId = 'layout_cache_id'; - /** @var \Magento\Framework\View\Layout\Element $xml */ - $xml = simplexml_load_string('<layout/>', \Magento\Framework\View\Layout\Element::class); + /** @var Element $xml */ + $xml = simplexml_load_string('<layout/>', Element::class); $this->model->setXml($xml); $this->readerContextFactoryMock->expects($this->once()) ->method('create') ->willReturn($this->readerContextMock); - $themeMock = $this->getMockForAbstractClass(\Magento\Framework\View\Design\ThemeInterface::class); + $themeMock = $this->getMockForAbstractClass(ThemeInterface::class); $this->themeResolverMock->expects($this->once()) ->method('get') ->willReturn($themeMock); @@ -871,18 +1008,20 @@ public function testGenerateElementsWithCache() 'field_3_3' => false, ] ]; - $this->pageConfigStructure->expects($this->once())->method('populateWithArray') + $this->pageConfigStructure->expects($this->once()) + ->method('populateWithArray') ->with($pageConfigStructureData); $layoutScheduledStructureData = [ 'field_1' => 1283, - 'field_2' => 'text_qwertyuiop[]asdfghjkl;' + 'field_2' => 'text_qwertyuiop[]asdfghjkl;', ]; - $this->layoutScheduledSructure->expects($this->once())->method('populateWithArray') + $this->layoutScheduledSructure->expects($this->once()) + ->method('populateWithArray') ->with($layoutScheduledStructureData); $data = [ 'pageConfigStructure' => $pageConfigStructureData, - 'scheduledStructure' => $layoutScheduledStructureData + 'scheduledStructure' => $layoutScheduledStructureData, ]; $this->cacheMock->expects($this->once()) @@ -895,7 +1034,7 @@ public function testGenerateElementsWithCache() $this->cacheMock->expects($this->never()) ->method('save'); - $generatorContextMock = $this->getMockBuilder(\Magento\Framework\View\Layout\Generator\Context::class) + $generatorContextMock = $this->getMockBuilder(Context::class) ->disableOriginalConstructor() ->getMock(); $this->generatorContextFactoryMock->expects($this->once()) @@ -910,9 +1049,9 @@ public function testGenerateElementsWithCache() $elements = [ 'name_1' => ['type' => '', 'parent' => null], - 'name_2' => ['type' => \Magento\Framework\View\Layout\Element::TYPE_CONTAINER, 'parent' => null], + 'name_2' => ['type' => Element::TYPE_CONTAINER, 'parent' => null], 'name_3' => ['type' => '', 'parent' => 'parent'], - 'name_4' => ['type' => \Magento\Framework\View\Layout\Element::TYPE_CONTAINER, 'parent' => 'parent'], + 'name_4' => ['type' => Element::TYPE_CONTAINER, 'parent' => 'parent'], ]; $this->structureMock->expects($this->once()) @@ -922,17 +1061,21 @@ public function testGenerateElementsWithCache() $this->model->generateElements(); } - public function testGetXml() + /** + * @return void + */ + public function testGetXml(): void { $xml = '<layout/>'; - $this->assertSame($xml, \Magento\Framework\View\Layout::LAYOUT_NODE); + $this->assertSame($xml, Layout::LAYOUT_NODE); } /** * @param mixed $displayValue + * @return void * @dataProvider renderElementDisplayDataProvider */ - public function testRenderElementDisplay($displayValue) + public function testRenderElementDisplay($displayValue): void { $name = 'test_container'; $child = 'child_block'; @@ -945,14 +1088,14 @@ public function testRenderElementDisplay($displayValue) [ [$name, 'display', $displayValue], [$child, 'display', $displayValue], - [$child, 'type', \Magento\Framework\View\Layout\Element::TYPE_BLOCK] + [$child, 'type', Element::TYPE_BLOCK], ] ); $this->structureMock->expects($this->atLeastOnce())->method('hasElement') ->willReturnMap( [ - [$child, true] + [$child, true], ] ); @@ -961,10 +1104,10 @@ public function testRenderElementDisplay($displayValue) ->with($name) ->willReturn($children); - $block = $this->createMock(\Magento\Framework\View\Element\AbstractBlock::class); + $block = $this->createMock(AbstractBlock::class); $block->expects($this->once())->method('toHtml')->willReturn($blockHtml); - $renderingOutput = new \Magento\Framework\DataObject(); + $renderingOutput = new DataObject(); $renderingOutput->setData('output', $blockHtml); $this->eventManagerMock->expects($this->at(0)) @@ -990,7 +1133,6 @@ public function testRenderElementDisplay($displayValue) */ public function testRenderElementDoNotDisplay($displayValue) { - $displayValue = 'false'; $name = 'test_container'; $blockHtml = ''; @@ -1004,19 +1146,19 @@ public function testRenderElementDoNotDisplay($displayValue) /** * @return array */ - public function renderElementDoNotDisplayDataProvider() + public function renderElementDoNotDisplayDataProvider(): array { return [ ['false'], ['0'], - [0] + [0], ]; } /** * @return array */ - public function renderElementDisplayDataProvider() + public function renderElementDisplayDataProvider(): array { return [ [true], @@ -1024,7 +1166,7 @@ public function renderElementDisplayDataProvider() [1], ['true'], [false], - [null] + [null], ]; } } diff --git a/lib/internal/Magento/Framework/View/Test/Unit/TemplateEngine/Xhtml/TemplateTest.php b/lib/internal/Magento/Framework/View/Test/Unit/TemplateEngine/Xhtml/TemplateTest.php new file mode 100644 index 0000000000000..3a3a7de47fbab --- /dev/null +++ b/lib/internal/Magento/Framework/View/Test/Unit/TemplateEngine/Xhtml/TemplateTest.php @@ -0,0 +1,67 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +declare(strict_types=1); + +namespace Magento\Framework\View\Test\Unit\TemplateEngine\Xhtml; + +use Magento\Framework\View\TemplateEngine\Xhtml\Template; +use PHPUnit\Framework\TestCase; +use Psr\Log\LoggerInterface; + +/** + * Test XML template engine + */ +class TemplateTest extends TestCase +{ + /** + * @var Template + */ + private $model; + + /** + * @inheritDoc + */ + protected function setUp() + { + parent::setUp(); + $this->model = new Template( + $this->getMockForAbstractClass(LoggerInterface::class), + file_get_contents(__DIR__ . '/../_files/simple.xml') + ); + } + + /** + * Test that xml content is correctly appended to the current element + */ + public function testAppend() + { + $body = <<<HTML +<body> + <h1>Home Page</h1> + <p>CMS homepage content goes here.</p> +</body> +HTML; + $expected = <<<HTML +<!-- +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +--><html xmlns="http://www.w3.org/1999/xhtml"> + <head> + <title>Home Page + + +

Home Page

+

CMS homepage content goes here.

+ + +HTML; + + $this->model->append($body); + $this->assertEquals($expected, (string) $this->model); + } +} diff --git a/lib/internal/Magento/Framework/View/Test/Unit/TemplateEngine/_files/simple.xml b/lib/internal/Magento/Framework/View/Test/Unit/TemplateEngine/_files/simple.xml new file mode 100644 index 0000000000000..0c73702b572c0 --- /dev/null +++ b/lib/internal/Magento/Framework/View/Test/Unit/TemplateEngine/_files/simple.xml @@ -0,0 +1,12 @@ + + + + + Home Page + + diff --git a/lib/internal/Magento/Framework/Webapi/ServiceInputProcessor.php b/lib/internal/Magento/Framework/Webapi/ServiceInputProcessor.php index f93d7efda5c8a..902e67bf015b7 100644 --- a/lib/internal/Magento/Framework/Webapi/ServiceInputProcessor.php +++ b/lib/internal/Magento/Framework/Webapi/ServiceInputProcessor.php @@ -21,7 +21,7 @@ use Magento\Framework\Reflection\TypeProcessor; use Magento\Framework\Webapi\Exception as WebapiException; use Magento\Framework\Webapi\CustomAttribute\PreprocessorInterface; -use Zend\Code\Reflection\ClassReflection; +use Laminas\Code\Reflection\ClassReflection; /** * Deserialize arguments from API requests. diff --git a/lib/internal/Magento/Framework/Webapi/ServiceOutputProcessor.php b/lib/internal/Magento/Framework/Webapi/ServiceOutputProcessor.php index 224421d6561c8..25eacb00c23ae 100644 --- a/lib/internal/Magento/Framework/Webapi/ServiceOutputProcessor.php +++ b/lib/internal/Magento/Framework/Webapi/ServiceOutputProcessor.php @@ -11,7 +11,7 @@ use Magento\Framework\Reflection\DataObjectProcessor; use Magento\Framework\Reflection\MethodsMap; use Magento\Framework\Reflection\TypeProcessor; -use Zend\Code\Reflection\ClassReflection; +use Laminas\Code\Reflection\ClassReflection; /** * Data object converter diff --git a/lib/internal/Magento/Framework/ZendEscaper.php b/lib/internal/Magento/Framework/ZendEscaper.php index 37182ab97d49d..6edd84d7c87f4 100644 --- a/lib/internal/Magento/Framework/ZendEscaper.php +++ b/lib/internal/Magento/Framework/ZendEscaper.php @@ -8,6 +8,6 @@ /** * Magento wrapper for Zend's Escaper class */ -class ZendEscaper extends \Zend\Escaper\Escaper +class ZendEscaper extends \Laminas\Escaper\Escaper { } diff --git a/lib/internal/Magento/Framework/composer.json b/lib/internal/Magento/Framework/composer.json index dfbfb5a25debe..35fc402d3805f 100644 --- a/lib/internal/Magento/Framework/composer.json +++ b/lib/internal/Magento/Framework/composer.json @@ -11,6 +11,7 @@ }, "require": { "php": "~7.1.3||~7.2.0||~7.3.0", + "ext-bcmath": "*", "ext-curl": "*", "ext-dom": "*", "ext-gd": "*", @@ -20,26 +21,26 @@ "ext-openssl": "*", "ext-simplexml": "*", "ext-xsl": "*", - "ext-bcmath": "*", "lib-libxml": "*", "colinmollenhour/php-redis-session-abstract": "~1.4.0", "composer/composer": "^1.6", + "guzzlehttp/guzzle": "^6.3.3", + "laminas/laminas-code": "~3.3.0", + "laminas/laminas-crypt": "^2.6.0", + "laminas/laminas-http": "^2.6.0", + "laminas/laminas-mail": "^2.9.0", + "laminas/laminas-mime": "^2.5.0", + "laminas/laminas-mvc": "~2.7.0", + "laminas/laminas-stdlib": "^3.2.1", + "laminas/laminas-uri": "^2.5.1", + "laminas/laminas-validator": "^2.6.0", "magento/zendframework1": "~1.14.2", "monolog/monolog": "^1.17", - "wikimedia/less.php": "~1.8.0", - "symfony/console": "~4.1.0", - "symfony/process": "~4.1.0", + "ramsey/uuid": "~3.8.0", + "symfony/console": "~4.4.0", + "symfony/process": "~4.4.0", "tedivm/jshrink": "~1.3.0", - "zendframework/zend-code": "~3.3.0", - "zendframework/zend-crypt": "^2.6.0", - "zendframework/zend-http": "^2.6.0", - "zendframework/zend-mvc": "~2.7.0", - "zendframework/zend-stdlib": "^3.2.1", - "zendframework/zend-uri": "^2.5.1", - "zendframework/zend-validator": "^2.6.0", - "zendframework/zend-mail": "^2.9.0", - "zendframework/zend-mime": "^2.5.0", - "guzzlehttp/guzzle": "^6.3.3" + "wikimedia/less.php": "~1.8.0" }, "archive": { "exclude": [ diff --git a/lib/internal/Magento/Framework/registration.php b/lib/internal/Magento/Framework/registration.php index 19012a264346c..69ba0ac6273e6 100644 --- a/lib/internal/Magento/Framework/registration.php +++ b/lib/internal/Magento/Framework/registration.php @@ -4,7 +4,7 @@ * See COPYING.txt for license details. */ -use \Magento\Framework\Component\ComponentRegistrar; +use Magento\Framework\Component\ComponentRegistrar; ComponentRegistrar::register(ComponentRegistrar::LIBRARY, 'magento/framework', __DIR__); diff --git a/lib/web/chartjs/Chart.min.js b/lib/web/chartjs/Chart.min.js new file mode 100644 index 0000000000000..7c16b0d1287d0 --- /dev/null +++ b/lib/web/chartjs/Chart.min.js @@ -0,0 +1,7 @@ +/*! + * Chart.js v2.9.3 + * https://www.chartjs.org + * (c) 2019 Chart.js Contributors + * Released under the MIT License + */ +!function(t,e){"object"==typeof exports&&"undefined"!=typeof module?module.exports=e(function(){try{return require("moment")}catch(t){}}()):"function"==typeof define&&define.amd?define(["require"],(function(t){return e(function(){try{return t("moment")}catch(t){}}())})):(t=t||self).Chart=e(t.moment)}(this,(function(t){"use strict";t=t&&t.hasOwnProperty("default")?t.default:t;var e={aliceblue:[240,248,255],antiquewhite:[250,235,215],aqua:[0,255,255],aquamarine:[127,255,212],azure:[240,255,255],beige:[245,245,220],bisque:[255,228,196],black:[0,0,0],blanchedalmond:[255,235,205],blue:[0,0,255],blueviolet:[138,43,226],brown:[165,42,42],burlywood:[222,184,135],cadetblue:[95,158,160],chartreuse:[127,255,0],chocolate:[210,105,30],coral:[255,127,80],cornflowerblue:[100,149,237],cornsilk:[255,248,220],crimson:[220,20,60],cyan:[0,255,255],darkblue:[0,0,139],darkcyan:[0,139,139],darkgoldenrod:[184,134,11],darkgray:[169,169,169],darkgreen:[0,100,0],darkgrey:[169,169,169],darkkhaki:[189,183,107],darkmagenta:[139,0,139],darkolivegreen:[85,107,47],darkorange:[255,140,0],darkorchid:[153,50,204],darkred:[139,0,0],darksalmon:[233,150,122],darkseagreen:[143,188,143],darkslateblue:[72,61,139],darkslategray:[47,79,79],darkslategrey:[47,79,79],darkturquoise:[0,206,209],darkviolet:[148,0,211],deeppink:[255,20,147],deepskyblue:[0,191,255],dimgray:[105,105,105],dimgrey:[105,105,105],dodgerblue:[30,144,255],firebrick:[178,34,34],floralwhite:[255,250,240],forestgreen:[34,139,34],fuchsia:[255,0,255],gainsboro:[220,220,220],ghostwhite:[248,248,255],gold:[255,215,0],goldenrod:[218,165,32],gray:[128,128,128],green:[0,128,0],greenyellow:[173,255,47],grey:[128,128,128],honeydew:[240,255,240],hotpink:[255,105,180],indianred:[205,92,92],indigo:[75,0,130],ivory:[255,255,240],khaki:[240,230,140],lavender:[230,230,250],lavenderblush:[255,240,245],lawngreen:[124,252,0],lemonchiffon:[255,250,205],lightblue:[173,216,230],lightcoral:[240,128,128],lightcyan:[224,255,255],lightgoldenrodyellow:[250,250,210],lightgray:[211,211,211],lightgreen:[144,238,144],lightgrey:[211,211,211],lightpink:[255,182,193],lightsalmon:[255,160,122],lightseagreen:[32,178,170],lightskyblue:[135,206,250],lightslategray:[119,136,153],lightslategrey:[119,136,153],lightsteelblue:[176,196,222],lightyellow:[255,255,224],lime:[0,255,0],limegreen:[50,205,50],linen:[250,240,230],magenta:[255,0,255],maroon:[128,0,0],mediumaquamarine:[102,205,170],mediumblue:[0,0,205],mediumorchid:[186,85,211],mediumpurple:[147,112,219],mediumseagreen:[60,179,113],mediumslateblue:[123,104,238],mediumspringgreen:[0,250,154],mediumturquoise:[72,209,204],mediumvioletred:[199,21,133],midnightblue:[25,25,112],mintcream:[245,255,250],mistyrose:[255,228,225],moccasin:[255,228,181],navajowhite:[255,222,173],navy:[0,0,128],oldlace:[253,245,230],olive:[128,128,0],olivedrab:[107,142,35],orange:[255,165,0],orangered:[255,69,0],orchid:[218,112,214],palegoldenrod:[238,232,170],palegreen:[152,251,152],paleturquoise:[175,238,238],palevioletred:[219,112,147],papayawhip:[255,239,213],peachpuff:[255,218,185],peru:[205,133,63],pink:[255,192,203],plum:[221,160,221],powderblue:[176,224,230],purple:[128,0,128],rebeccapurple:[102,51,153],red:[255,0,0],rosybrown:[188,143,143],royalblue:[65,105,225],saddlebrown:[139,69,19],salmon:[250,128,114],sandybrown:[244,164,96],seagreen:[46,139,87],seashell:[255,245,238],sienna:[160,82,45],silver:[192,192,192],skyblue:[135,206,235],slateblue:[106,90,205],slategray:[112,128,144],slategrey:[112,128,144],snow:[255,250,250],springgreen:[0,255,127],steelblue:[70,130,180],tan:[210,180,140],teal:[0,128,128],thistle:[216,191,216],tomato:[255,99,71],turquoise:[64,224,208],violet:[238,130,238],wheat:[245,222,179],white:[255,255,255],whitesmoke:[245,245,245],yellow:[255,255,0],yellowgreen:[154,205,50]},n=function(t,e){return t(e={exports:{}},e.exports),e.exports}((function(t){var n={};for(var i in e)e.hasOwnProperty(i)&&(n[e[i]]=i);var a=t.exports={rgb:{channels:3,labels:"rgb"},hsl:{channels:3,labels:"hsl"},hsv:{channels:3,labels:"hsv"},hwb:{channels:3,labels:"hwb"},cmyk:{channels:4,labels:"cmyk"},xyz:{channels:3,labels:"xyz"},lab:{channels:3,labels:"lab"},lch:{channels:3,labels:"lch"},hex:{channels:1,labels:["hex"]},keyword:{channels:1,labels:["keyword"]},ansi16:{channels:1,labels:["ansi16"]},ansi256:{channels:1,labels:["ansi256"]},hcg:{channels:3,labels:["h","c","g"]},apple:{channels:3,labels:["r16","g16","b16"]},gray:{channels:1,labels:["gray"]}};for(var r in a)if(a.hasOwnProperty(r)){if(!("channels"in a[r]))throw new Error("missing channels property: "+r);if(!("labels"in a[r]))throw new Error("missing channel labels property: "+r);if(a[r].labels.length!==a[r].channels)throw new Error("channel and label counts mismatch: "+r);var o=a[r].channels,s=a[r].labels;delete a[r].channels,delete a[r].labels,Object.defineProperty(a[r],"channels",{value:o}),Object.defineProperty(a[r],"labels",{value:s})}a.rgb.hsl=function(t){var e,n,i=t[0]/255,a=t[1]/255,r=t[2]/255,o=Math.min(i,a,r),s=Math.max(i,a,r),l=s-o;return s===o?e=0:i===s?e=(a-r)/l:a===s?e=2+(r-i)/l:r===s&&(e=4+(i-a)/l),(e=Math.min(60*e,360))<0&&(e+=360),n=(o+s)/2,[e,100*(s===o?0:n<=.5?l/(s+o):l/(2-s-o)),100*n]},a.rgb.hsv=function(t){var e,n,i,a,r,o=t[0]/255,s=t[1]/255,l=t[2]/255,u=Math.max(o,s,l),d=u-Math.min(o,s,l),h=function(t){return(u-t)/6/d+.5};return 0===d?a=r=0:(r=d/u,e=h(o),n=h(s),i=h(l),o===u?a=i-n:s===u?a=1/3+e-i:l===u&&(a=2/3+n-e),a<0?a+=1:a>1&&(a-=1)),[360*a,100*r,100*u]},a.rgb.hwb=function(t){var e=t[0],n=t[1],i=t[2];return[a.rgb.hsl(t)[0],100*(1/255*Math.min(e,Math.min(n,i))),100*(i=1-1/255*Math.max(e,Math.max(n,i)))]},a.rgb.cmyk=function(t){var e,n=t[0]/255,i=t[1]/255,a=t[2]/255;return[100*((1-n-(e=Math.min(1-n,1-i,1-a)))/(1-e)||0),100*((1-i-e)/(1-e)||0),100*((1-a-e)/(1-e)||0),100*e]},a.rgb.keyword=function(t){var i=n[t];if(i)return i;var a,r,o,s=1/0;for(var l in e)if(e.hasOwnProperty(l)){var u=e[l],d=(r=t,o=u,Math.pow(r[0]-o[0],2)+Math.pow(r[1]-o[1],2)+Math.pow(r[2]-o[2],2));d.04045?Math.pow((e+.055)/1.055,2.4):e/12.92)+.3576*(n=n>.04045?Math.pow((n+.055)/1.055,2.4):n/12.92)+.1805*(i=i>.04045?Math.pow((i+.055)/1.055,2.4):i/12.92)),100*(.2126*e+.7152*n+.0722*i),100*(.0193*e+.1192*n+.9505*i)]},a.rgb.lab=function(t){var e=a.rgb.xyz(t),n=e[0],i=e[1],r=e[2];return i/=100,r/=108.883,n=(n/=95.047)>.008856?Math.pow(n,1/3):7.787*n+16/116,[116*(i=i>.008856?Math.pow(i,1/3):7.787*i+16/116)-16,500*(n-i),200*(i-(r=r>.008856?Math.pow(r,1/3):7.787*r+16/116))]},a.hsl.rgb=function(t){var e,n,i,a,r,o=t[0]/360,s=t[1]/100,l=t[2]/100;if(0===s)return[r=255*l,r,r];e=2*l-(n=l<.5?l*(1+s):l+s-l*s),a=[0,0,0];for(var u=0;u<3;u++)(i=o+1/3*-(u-1))<0&&i++,i>1&&i--,r=6*i<1?e+6*(n-e)*i:2*i<1?n:3*i<2?e+(n-e)*(2/3-i)*6:e,a[u]=255*r;return a},a.hsl.hsv=function(t){var e=t[0],n=t[1]/100,i=t[2]/100,a=n,r=Math.max(i,.01);return n*=(i*=2)<=1?i:2-i,a*=r<=1?r:2-r,[e,100*(0===i?2*a/(r+a):2*n/(i+n)),100*((i+n)/2)]},a.hsv.rgb=function(t){var e=t[0]/60,n=t[1]/100,i=t[2]/100,a=Math.floor(e)%6,r=e-Math.floor(e),o=255*i*(1-n),s=255*i*(1-n*r),l=255*i*(1-n*(1-r));switch(i*=255,a){case 0:return[i,l,o];case 1:return[s,i,o];case 2:return[o,i,l];case 3:return[o,s,i];case 4:return[l,o,i];case 5:return[i,o,s]}},a.hsv.hsl=function(t){var e,n,i,a=t[0],r=t[1]/100,o=t[2]/100,s=Math.max(o,.01);return i=(2-r)*o,n=r*s,[a,100*(n=(n/=(e=(2-r)*s)<=1?e:2-e)||0),100*(i/=2)]},a.hwb.rgb=function(t){var e,n,i,a,r,o,s,l=t[0]/360,u=t[1]/100,d=t[2]/100,h=u+d;switch(h>1&&(u/=h,d/=h),i=6*l-(e=Math.floor(6*l)),0!=(1&e)&&(i=1-i),a=u+i*((n=1-d)-u),e){default:case 6:case 0:r=n,o=a,s=u;break;case 1:r=a,o=n,s=u;break;case 2:r=u,o=n,s=a;break;case 3:r=u,o=a,s=n;break;case 4:r=a,o=u,s=n;break;case 5:r=n,o=u,s=a}return[255*r,255*o,255*s]},a.cmyk.rgb=function(t){var e=t[0]/100,n=t[1]/100,i=t[2]/100,a=t[3]/100;return[255*(1-Math.min(1,e*(1-a)+a)),255*(1-Math.min(1,n*(1-a)+a)),255*(1-Math.min(1,i*(1-a)+a))]},a.xyz.rgb=function(t){var e,n,i,a=t[0]/100,r=t[1]/100,o=t[2]/100;return n=-.9689*a+1.8758*r+.0415*o,i=.0557*a+-.204*r+1.057*o,e=(e=3.2406*a+-1.5372*r+-.4986*o)>.0031308?1.055*Math.pow(e,1/2.4)-.055:12.92*e,n=n>.0031308?1.055*Math.pow(n,1/2.4)-.055:12.92*n,i=i>.0031308?1.055*Math.pow(i,1/2.4)-.055:12.92*i,[255*(e=Math.min(Math.max(0,e),1)),255*(n=Math.min(Math.max(0,n),1)),255*(i=Math.min(Math.max(0,i),1))]},a.xyz.lab=function(t){var e=t[0],n=t[1],i=t[2];return n/=100,i/=108.883,e=(e/=95.047)>.008856?Math.pow(e,1/3):7.787*e+16/116,[116*(n=n>.008856?Math.pow(n,1/3):7.787*n+16/116)-16,500*(e-n),200*(n-(i=i>.008856?Math.pow(i,1/3):7.787*i+16/116))]},a.lab.xyz=function(t){var e,n,i,a=t[0];e=t[1]/500+(n=(a+16)/116),i=n-t[2]/200;var r=Math.pow(n,3),o=Math.pow(e,3),s=Math.pow(i,3);return n=r>.008856?r:(n-16/116)/7.787,e=o>.008856?o:(e-16/116)/7.787,i=s>.008856?s:(i-16/116)/7.787,[e*=95.047,n*=100,i*=108.883]},a.lab.lch=function(t){var e,n=t[0],i=t[1],a=t[2];return(e=360*Math.atan2(a,i)/2/Math.PI)<0&&(e+=360),[n,Math.sqrt(i*i+a*a),e]},a.lch.lab=function(t){var e,n=t[0],i=t[1];return e=t[2]/360*2*Math.PI,[n,i*Math.cos(e),i*Math.sin(e)]},a.rgb.ansi16=function(t){var e=t[0],n=t[1],i=t[2],r=1 in arguments?arguments[1]:a.rgb.hsv(t)[2];if(0===(r=Math.round(r/50)))return 30;var o=30+(Math.round(i/255)<<2|Math.round(n/255)<<1|Math.round(e/255));return 2===r&&(o+=60),o},a.hsv.ansi16=function(t){return a.rgb.ansi16(a.hsv.rgb(t),t[2])},a.rgb.ansi256=function(t){var e=t[0],n=t[1],i=t[2];return e===n&&n===i?e<8?16:e>248?231:Math.round((e-8)/247*24)+232:16+36*Math.round(e/255*5)+6*Math.round(n/255*5)+Math.round(i/255*5)},a.ansi16.rgb=function(t){var e=t%10;if(0===e||7===e)return t>50&&(e+=3.5),[e=e/10.5*255,e,e];var n=.5*(1+~~(t>50));return[(1&e)*n*255,(e>>1&1)*n*255,(e>>2&1)*n*255]},a.ansi256.rgb=function(t){if(t>=232){var e=10*(t-232)+8;return[e,e,e]}var n;return t-=16,[Math.floor(t/36)/5*255,Math.floor((n=t%36)/6)/5*255,n%6/5*255]},a.rgb.hex=function(t){var e=(((255&Math.round(t[0]))<<16)+((255&Math.round(t[1]))<<8)+(255&Math.round(t[2]))).toString(16).toUpperCase();return"000000".substring(e.length)+e},a.hex.rgb=function(t){var e=t.toString(16).match(/[a-f0-9]{6}|[a-f0-9]{3}/i);if(!e)return[0,0,0];var n=e[0];3===e[0].length&&(n=n.split("").map((function(t){return t+t})).join(""));var i=parseInt(n,16);return[i>>16&255,i>>8&255,255&i]},a.rgb.hcg=function(t){var e,n=t[0]/255,i=t[1]/255,a=t[2]/255,r=Math.max(Math.max(n,i),a),o=Math.min(Math.min(n,i),a),s=r-o;return e=s<=0?0:r===n?(i-a)/s%6:r===i?2+(a-n)/s:4+(n-i)/s+4,e/=6,[360*(e%=1),100*s,100*(s<1?o/(1-s):0)]},a.hsl.hcg=function(t){var e=t[1]/100,n=t[2]/100,i=1,a=0;return(i=n<.5?2*e*n:2*e*(1-n))<1&&(a=(n-.5*i)/(1-i)),[t[0],100*i,100*a]},a.hsv.hcg=function(t){var e=t[1]/100,n=t[2]/100,i=e*n,a=0;return i<1&&(a=(n-i)/(1-i)),[t[0],100*i,100*a]},a.hcg.rgb=function(t){var e=t[0]/360,n=t[1]/100,i=t[2]/100;if(0===n)return[255*i,255*i,255*i];var a,r=[0,0,0],o=e%1*6,s=o%1,l=1-s;switch(Math.floor(o)){case 0:r[0]=1,r[1]=s,r[2]=0;break;case 1:r[0]=l,r[1]=1,r[2]=0;break;case 2:r[0]=0,r[1]=1,r[2]=s;break;case 3:r[0]=0,r[1]=l,r[2]=1;break;case 4:r[0]=s,r[1]=0,r[2]=1;break;default:r[0]=1,r[1]=0,r[2]=l}return a=(1-n)*i,[255*(n*r[0]+a),255*(n*r[1]+a),255*(n*r[2]+a)]},a.hcg.hsv=function(t){var e=t[1]/100,n=e+t[2]/100*(1-e),i=0;return n>0&&(i=e/n),[t[0],100*i,100*n]},a.hcg.hsl=function(t){var e=t[1]/100,n=t[2]/100*(1-e)+.5*e,i=0;return n>0&&n<.5?i=e/(2*n):n>=.5&&n<1&&(i=e/(2*(1-n))),[t[0],100*i,100*n]},a.hcg.hwb=function(t){var e=t[1]/100,n=e+t[2]/100*(1-e);return[t[0],100*(n-e),100*(1-n)]},a.hwb.hcg=function(t){var e=t[1]/100,n=1-t[2]/100,i=n-e,a=0;return i<1&&(a=(n-i)/(1-i)),[t[0],100*i,100*a]},a.apple.rgb=function(t){return[t[0]/65535*255,t[1]/65535*255,t[2]/65535*255]},a.rgb.apple=function(t){return[t[0]/255*65535,t[1]/255*65535,t[2]/255*65535]},a.gray.rgb=function(t){return[t[0]/100*255,t[0]/100*255,t[0]/100*255]},a.gray.hsl=a.gray.hsv=function(t){return[0,0,t[0]]},a.gray.hwb=function(t){return[0,100,t[0]]},a.gray.cmyk=function(t){return[0,0,0,t[0]]},a.gray.lab=function(t){return[t[0],0,0]},a.gray.hex=function(t){var e=255&Math.round(t[0]/100*255),n=((e<<16)+(e<<8)+e).toString(16).toUpperCase();return"000000".substring(n.length)+n},a.rgb.gray=function(t){return[(t[0]+t[1]+t[2])/3/255*100]}}));n.rgb,n.hsl,n.hsv,n.hwb,n.cmyk,n.xyz,n.lab,n.lch,n.hex,n.keyword,n.ansi16,n.ansi256,n.hcg,n.apple,n.gray;function i(t){var e=function(){for(var t={},e=Object.keys(n),i=e.length,a=0;a1&&(e=Array.prototype.slice.call(arguments));var n=t(e);if("object"==typeof n)for(var i=n.length,a=0;a1&&(e=Array.prototype.slice.call(arguments)),t(e))};return"conversion"in t&&(e.conversion=t.conversion),e}(i)}))}));var s=o,l={aliceblue:[240,248,255],antiquewhite:[250,235,215],aqua:[0,255,255],aquamarine:[127,255,212],azure:[240,255,255],beige:[245,245,220],bisque:[255,228,196],black:[0,0,0],blanchedalmond:[255,235,205],blue:[0,0,255],blueviolet:[138,43,226],brown:[165,42,42],burlywood:[222,184,135],cadetblue:[95,158,160],chartreuse:[127,255,0],chocolate:[210,105,30],coral:[255,127,80],cornflowerblue:[100,149,237],cornsilk:[255,248,220],crimson:[220,20,60],cyan:[0,255,255],darkblue:[0,0,139],darkcyan:[0,139,139],darkgoldenrod:[184,134,11],darkgray:[169,169,169],darkgreen:[0,100,0],darkgrey:[169,169,169],darkkhaki:[189,183,107],darkmagenta:[139,0,139],darkolivegreen:[85,107,47],darkorange:[255,140,0],darkorchid:[153,50,204],darkred:[139,0,0],darksalmon:[233,150,122],darkseagreen:[143,188,143],darkslateblue:[72,61,139],darkslategray:[47,79,79],darkslategrey:[47,79,79],darkturquoise:[0,206,209],darkviolet:[148,0,211],deeppink:[255,20,147],deepskyblue:[0,191,255],dimgray:[105,105,105],dimgrey:[105,105,105],dodgerblue:[30,144,255],firebrick:[178,34,34],floralwhite:[255,250,240],forestgreen:[34,139,34],fuchsia:[255,0,255],gainsboro:[220,220,220],ghostwhite:[248,248,255],gold:[255,215,0],goldenrod:[218,165,32],gray:[128,128,128],green:[0,128,0],greenyellow:[173,255,47],grey:[128,128,128],honeydew:[240,255,240],hotpink:[255,105,180],indianred:[205,92,92],indigo:[75,0,130],ivory:[255,255,240],khaki:[240,230,140],lavender:[230,230,250],lavenderblush:[255,240,245],lawngreen:[124,252,0],lemonchiffon:[255,250,205],lightblue:[173,216,230],lightcoral:[240,128,128],lightcyan:[224,255,255],lightgoldenrodyellow:[250,250,210],lightgray:[211,211,211],lightgreen:[144,238,144],lightgrey:[211,211,211],lightpink:[255,182,193],lightsalmon:[255,160,122],lightseagreen:[32,178,170],lightskyblue:[135,206,250],lightslategray:[119,136,153],lightslategrey:[119,136,153],lightsteelblue:[176,196,222],lightyellow:[255,255,224],lime:[0,255,0],limegreen:[50,205,50],linen:[250,240,230],magenta:[255,0,255],maroon:[128,0,0],mediumaquamarine:[102,205,170],mediumblue:[0,0,205],mediumorchid:[186,85,211],mediumpurple:[147,112,219],mediumseagreen:[60,179,113],mediumslateblue:[123,104,238],mediumspringgreen:[0,250,154],mediumturquoise:[72,209,204],mediumvioletred:[199,21,133],midnightblue:[25,25,112],mintcream:[245,255,250],mistyrose:[255,228,225],moccasin:[255,228,181],navajowhite:[255,222,173],navy:[0,0,128],oldlace:[253,245,230],olive:[128,128,0],olivedrab:[107,142,35],orange:[255,165,0],orangered:[255,69,0],orchid:[218,112,214],palegoldenrod:[238,232,170],palegreen:[152,251,152],paleturquoise:[175,238,238],palevioletred:[219,112,147],papayawhip:[255,239,213],peachpuff:[255,218,185],peru:[205,133,63],pink:[255,192,203],plum:[221,160,221],powderblue:[176,224,230],purple:[128,0,128],rebeccapurple:[102,51,153],red:[255,0,0],rosybrown:[188,143,143],royalblue:[65,105,225],saddlebrown:[139,69,19],salmon:[250,128,114],sandybrown:[244,164,96],seagreen:[46,139,87],seashell:[255,245,238],sienna:[160,82,45],silver:[192,192,192],skyblue:[135,206,235],slateblue:[106,90,205],slategray:[112,128,144],slategrey:[112,128,144],snow:[255,250,250],springgreen:[0,255,127],steelblue:[70,130,180],tan:[210,180,140],teal:[0,128,128],thistle:[216,191,216],tomato:[255,99,71],turquoise:[64,224,208],violet:[238,130,238],wheat:[245,222,179],white:[255,255,255],whitesmoke:[245,245,245],yellow:[255,255,0],yellowgreen:[154,205,50]},u={getRgba:d,getHsla:h,getRgb:function(t){var e=d(t);return e&&e.slice(0,3)},getHsl:function(t){var e=h(t);return e&&e.slice(0,3)},getHwb:c,getAlpha:function(t){var e=d(t);if(e)return e[3];if(e=h(t))return e[3];if(e=c(t))return e[3]},hexString:function(t,e){e=void 0!==e&&3===t.length?e:t[3];return"#"+v(t[0])+v(t[1])+v(t[2])+(e>=0&&e<1?v(Math.round(255*e)):"")},rgbString:function(t,e){if(e<1||t[3]&&t[3]<1)return f(t,e);return"rgb("+t[0]+", "+t[1]+", "+t[2]+")"},rgbaString:f,percentString:function(t,e){if(e<1||t[3]&&t[3]<1)return g(t,e);var n=Math.round(t[0]/255*100),i=Math.round(t[1]/255*100),a=Math.round(t[2]/255*100);return"rgb("+n+"%, "+i+"%, "+a+"%)"},percentaString:g,hslString:function(t,e){if(e<1||t[3]&&t[3]<1)return p(t,e);return"hsl("+t[0]+", "+t[1]+"%, "+t[2]+"%)"},hslaString:p,hwbString:function(t,e){void 0===e&&(e=void 0!==t[3]?t[3]:1);return"hwb("+t[0]+", "+t[1]+"%, "+t[2]+"%"+(void 0!==e&&1!==e?", "+e:"")+")"},keyword:function(t){return b[t.slice(0,3)]}};function d(t){if(t){var e=[0,0,0],n=1,i=t.match(/^#([a-fA-F0-9]{3,4})$/i),a="";if(i){a=(i=i[1])[3];for(var r=0;rn?(e+.05)/(n+.05):(n+.05)/(e+.05)},level:function(t){var e=this.contrast(t);return e>=7.1?"AAA":e>=4.5?"AA":""},dark:function(){var t=this.values.rgb;return(299*t[0]+587*t[1]+114*t[2])/1e3<128},light:function(){return!this.dark()},negate:function(){for(var t=[],e=0;e<3;e++)t[e]=255-this.values.rgb[e];return this.setValues("rgb",t),this},lighten:function(t){var e=this.values.hsl;return e[2]+=e[2]*t,this.setValues("hsl",e),this},darken:function(t){var e=this.values.hsl;return e[2]-=e[2]*t,this.setValues("hsl",e),this},saturate:function(t){var e=this.values.hsl;return e[1]+=e[1]*t,this.setValues("hsl",e),this},desaturate:function(t){var e=this.values.hsl;return e[1]-=e[1]*t,this.setValues("hsl",e),this},whiten:function(t){var e=this.values.hwb;return e[1]+=e[1]*t,this.setValues("hwb",e),this},blacken:function(t){var e=this.values.hwb;return e[2]+=e[2]*t,this.setValues("hwb",e),this},greyscale:function(){var t=this.values.rgb,e=.3*t[0]+.59*t[1]+.11*t[2];return this.setValues("rgb",[e,e,e]),this},clearer:function(t){var e=this.values.alpha;return this.setValues("alpha",e-e*t),this},opaquer:function(t){var e=this.values.alpha;return this.setValues("alpha",e+e*t),this},rotate:function(t){var e=this.values.hsl,n=(e[0]+t)%360;return e[0]=n<0?360+n:n,this.setValues("hsl",e),this},mix:function(t,e){var n=t,i=void 0===e?.5:e,a=2*i-1,r=this.alpha()-n.alpha(),o=((a*r==-1?a:(a+r)/(1+a*r))+1)/2,s=1-o;return this.rgb(o*this.red()+s*n.red(),o*this.green()+s*n.green(),o*this.blue()+s*n.blue()).alpha(this.alpha()*i+n.alpha()*(1-i))},toJSON:function(){return this.rgb()},clone:function(){var t,e,n=new y,i=this.values,a=n.values;for(var r in i)i.hasOwnProperty(r)&&(t=i[r],"[object Array]"===(e={}.toString.call(t))?a[r]=t.slice(0):"[object Number]"===e?a[r]=t:console.error("unexpected color value:",t));return n}},y.prototype.spaces={rgb:["red","green","blue"],hsl:["hue","saturation","lightness"],hsv:["hue","saturation","value"],hwb:["hue","whiteness","blackness"],cmyk:["cyan","magenta","yellow","black"]},y.prototype.maxes={rgb:[255,255,255],hsl:[360,100,100],hsv:[360,100,100],hwb:[360,100,100],cmyk:[100,100,100,100]},y.prototype.getValues=function(t){for(var e=this.values,n={},i=0;i=0;a--)e.call(n,t[a],a);else for(a=0;a=1?t:-(Math.sqrt(1-t*t)-1)},easeOutCirc:function(t){return Math.sqrt(1-(t-=1)*t)},easeInOutCirc:function(t){return(t/=.5)<1?-.5*(Math.sqrt(1-t*t)-1):.5*(Math.sqrt(1-(t-=2)*t)+1)},easeInElastic:function(t){var e=1.70158,n=0,i=1;return 0===t?0:1===t?1:(n||(n=.3),i<1?(i=1,e=n/4):e=n/(2*Math.PI)*Math.asin(1/i),-i*Math.pow(2,10*(t-=1))*Math.sin((t-e)*(2*Math.PI)/n))},easeOutElastic:function(t){var e=1.70158,n=0,i=1;return 0===t?0:1===t?1:(n||(n=.3),i<1?(i=1,e=n/4):e=n/(2*Math.PI)*Math.asin(1/i),i*Math.pow(2,-10*t)*Math.sin((t-e)*(2*Math.PI)/n)+1)},easeInOutElastic:function(t){var e=1.70158,n=0,i=1;return 0===t?0:2==(t/=.5)?1:(n||(n=.45),i<1?(i=1,e=n/4):e=n/(2*Math.PI)*Math.asin(1/i),t<1?i*Math.pow(2,10*(t-=1))*Math.sin((t-e)*(2*Math.PI)/n)*-.5:i*Math.pow(2,-10*(t-=1))*Math.sin((t-e)*(2*Math.PI)/n)*.5+1)},easeInBack:function(t){var e=1.70158;return t*t*((e+1)*t-e)},easeOutBack:function(t){var e=1.70158;return(t-=1)*t*((e+1)*t+e)+1},easeInOutBack:function(t){var e=1.70158;return(t/=.5)<1?t*t*((1+(e*=1.525))*t-e)*.5:.5*((t-=2)*t*((1+(e*=1.525))*t+e)+2)},easeInBounce:function(t){return 1-S.easeOutBounce(1-t)},easeOutBounce:function(t){return t<1/2.75?7.5625*t*t:t<2/2.75?7.5625*(t-=1.5/2.75)*t+.75:t<2.5/2.75?7.5625*(t-=2.25/2.75)*t+.9375:7.5625*(t-=2.625/2.75)*t+.984375},easeInOutBounce:function(t){return t<.5?.5*S.easeInBounce(2*t):.5*S.easeOutBounce(2*t-1)+.5}},C={effects:S};M.easingEffects=S;var P=Math.PI,A=P/180,D=2*P,T=P/2,I=P/4,F=2*P/3,L={clear:function(t){t.ctx.clearRect(0,0,t.width,t.height)},roundedRect:function(t,e,n,i,a,r){if(r){var o=Math.min(r,a/2,i/2),s=e+o,l=n+o,u=e+i-o,d=n+a-o;t.moveTo(e,l),se.left-1e-6&&t.xe.top-1e-6&&t.y0&&this.requestAnimationFrame()},advance:function(){for(var t,e,n,i,a=this.animations,r=0;r=n?(V.callback(t.onAnimationComplete,[t],e),e.animating=!1,a.splice(r,1)):++r}},J=V.options.resolve,Q=["push","pop","shift","splice","unshift"];function tt(t,e){var n=t._chartjs;if(n){var i=n.listeners,a=i.indexOf(e);-1!==a&&i.splice(a,1),i.length>0||(Q.forEach((function(e){delete t[e]})),delete t._chartjs)}}var et=function(t,e){this.initialize(t,e)};V.extend(et.prototype,{datasetElementType:null,dataElementType:null,_datasetElementOptions:["backgroundColor","borderCapStyle","borderColor","borderDash","borderDashOffset","borderJoinStyle","borderWidth"],_dataElementOptions:["backgroundColor","borderColor","borderWidth","pointStyle"],initialize:function(t,e){var n=this;n.chart=t,n.index=e,n.linkScales(),n.addElements(),n._type=n.getMeta().type},updateIndex:function(t){this.index=t},linkScales:function(){var t=this.getMeta(),e=this.chart,n=e.scales,i=this.getDataset(),a=e.options.scales;null!==t.xAxisID&&t.xAxisID in n&&!i.xAxisID||(t.xAxisID=i.xAxisID||a.xAxes[0].id),null!==t.yAxisID&&t.yAxisID in n&&!i.yAxisID||(t.yAxisID=i.yAxisID||a.yAxes[0].id)},getDataset:function(){return this.chart.data.datasets[this.index]},getMeta:function(){return this.chart.getDatasetMeta(this.index)},getScaleForId:function(t){return this.chart.scales[t]},_getValueScaleId:function(){return this.getMeta().yAxisID},_getIndexScaleId:function(){return this.getMeta().xAxisID},_getValueScale:function(){return this.getScaleForId(this._getValueScaleId())},_getIndexScale:function(){return this.getScaleForId(this._getIndexScaleId())},reset:function(){this._update(!0)},destroy:function(){this._data&&tt(this._data,this)},createMetaDataset:function(){var t=this.datasetElementType;return t&&new t({_chart:this.chart,_datasetIndex:this.index})},createMetaData:function(t){var e=this.dataElementType;return e&&new e({_chart:this.chart,_datasetIndex:this.index,_index:t})},addElements:function(){var t,e,n=this.getMeta(),i=this.getDataset().data||[],a=n.data;for(t=0,e=i.length;tn&&this.insertElements(n,i-n)},insertElements:function(t,e){for(var n=0;na?(r=a/e.innerRadius,t.arc(o,s,e.innerRadius-a,i+r,n-r,!0)):t.arc(o,s,a,i+Math.PI/2,n-Math.PI/2),t.closePath(),t.clip()}function rt(t,e,n){var i="inner"===e.borderAlign;i?(t.lineWidth=2*e.borderWidth,t.lineJoin="round"):(t.lineWidth=e.borderWidth,t.lineJoin="bevel"),n.fullCircles&&function(t,e,n,i){var a,r=n.endAngle;for(i&&(n.endAngle=n.startAngle+it,at(t,n),n.endAngle=r,n.endAngle===n.startAngle&&n.fullCircles&&(n.endAngle+=it,n.fullCircles--)),t.beginPath(),t.arc(n.x,n.y,n.innerRadius,n.startAngle+it,n.startAngle,!0),a=0;as;)a-=it;for(;a=o&&a<=s,u=r>=n.innerRadius&&r<=n.outerRadius;return l&&u}return!1},getCenterPoint:function(){var t=this._view,e=(t.startAngle+t.endAngle)/2,n=(t.innerRadius+t.outerRadius)/2;return{x:t.x+Math.cos(e)*n,y:t.y+Math.sin(e)*n}},getArea:function(){var t=this._view;return Math.PI*((t.endAngle-t.startAngle)/(2*Math.PI))*(Math.pow(t.outerRadius,2)-Math.pow(t.innerRadius,2))},tooltipPosition:function(){var t=this._view,e=t.startAngle+(t.endAngle-t.startAngle)/2,n=(t.outerRadius-t.innerRadius)/2+t.innerRadius;return{x:t.x+Math.cos(e)*n,y:t.y+Math.sin(e)*n}},draw:function(){var t,e=this._chart.ctx,n=this._view,i="inner"===n.borderAlign?.33:0,a={x:n.x,y:n.y,innerRadius:n.innerRadius,outerRadius:Math.max(n.outerRadius-i,0),pixelMargin:i,startAngle:n.startAngle,endAngle:n.endAngle,fullCircles:Math.floor(n.circumference/it)};if(e.save(),e.fillStyle=n.backgroundColor,e.strokeStyle=n.borderColor,a.fullCircles){for(a.endAngle=a.startAngle+it,e.beginPath(),e.arc(a.x,a.y,a.outerRadius,a.startAngle,a.endAngle),e.arc(a.x,a.y,a.innerRadius,a.endAngle,a.startAngle,!0),e.closePath(),t=0;tt.x&&(e=vt(e,"left","right")):t.basen?n:i,r:l.right||a<0?0:a>e?e:a,b:l.bottom||r<0?0:r>n?n:r,l:l.left||o<0?0:o>e?e:o}}function xt(t,e,n){var i=null===e,a=null===n,r=!(!t||i&&a)&&mt(t);return r&&(i||e>=r.left&&e<=r.right)&&(a||n>=r.top&&n<=r.bottom)}z._set("global",{elements:{rectangle:{backgroundColor:gt,borderColor:gt,borderSkipped:"bottom",borderWidth:0}}});var yt=X.extend({_type:"rectangle",draw:function(){var t=this._chart.ctx,e=this._view,n=function(t){var e=mt(t),n=e.right-e.left,i=e.bottom-e.top,a=bt(t,n/2,i/2);return{outer:{x:e.left,y:e.top,w:n,h:i},inner:{x:e.left+a.l,y:e.top+a.t,w:n-a.l-a.r,h:i-a.t-a.b}}}(e),i=n.outer,a=n.inner;t.fillStyle=e.backgroundColor,t.fillRect(i.x,i.y,i.w,i.h),i.w===a.w&&i.h===a.h||(t.save(),t.beginPath(),t.rect(i.x,i.y,i.w,i.h),t.clip(),t.fillStyle=e.borderColor,t.rect(a.x,a.y,a.w,a.h),t.fill("evenodd"),t.restore())},height:function(){var t=this._view;return t.base-t.y},inRange:function(t,e){return xt(this._view,t,e)},inLabelRange:function(t,e){var n=this._view;return pt(n)?xt(n,t,null):xt(n,null,e)},inXRange:function(t){return xt(this._view,t,null)},inYRange:function(t){return xt(this._view,null,t)},getCenterPoint:function(){var t,e,n=this._view;return pt(n)?(t=n.x,e=(n.y+n.base)/2):(t=(n.x+n.base)/2,e=n.y),{x:t,y:e}},getArea:function(){var t=this._view;return pt(t)?t.width*Math.abs(t.y-t.base):t.height*Math.abs(t.x-t.base)},tooltipPosition:function(){var t=this._view;return{x:t.x,y:t.y}}}),_t={},kt=ot,wt=ut,Mt=ft,St=yt;_t.Arc=kt,_t.Line=wt,_t.Point=Mt,_t.Rectangle=St;var Ct=V._deprecated,Pt=V.valueOrDefault;function At(t,e,n){var i,a,r=n.barThickness,o=e.stackCount,s=e.pixels[t],l=V.isNullOrUndef(r)?function(t,e){var n,i,a,r,o=t._length;for(a=1,r=e.length;a0?Math.min(o,Math.abs(i-n)):o,n=i;return o}(e.scale,e.pixels):-1;return V.isNullOrUndef(r)?(i=l*n.categoryPercentage,a=n.barPercentage):(i=r*o,a=1),{chunk:i/o,ratio:a,start:s-i/2}}z._set("bar",{hover:{mode:"label"},scales:{xAxes:[{type:"category",offset:!0,gridLines:{offsetGridLines:!0}}],yAxes:[{type:"linear"}]}}),z._set("global",{datasets:{bar:{categoryPercentage:.8,barPercentage:.9}}});var Dt=nt.extend({dataElementType:_t.Rectangle,_dataElementOptions:["backgroundColor","borderColor","borderSkipped","borderWidth","barPercentage","barThickness","categoryPercentage","maxBarThickness","minBarLength"],initialize:function(){var t,e,n=this;nt.prototype.initialize.apply(n,arguments),(t=n.getMeta()).stack=n.getDataset().stack,t.bar=!0,e=n._getIndexScale().options,Ct("bar chart",e.barPercentage,"scales.[x/y]Axes.barPercentage","dataset.barPercentage"),Ct("bar chart",e.barThickness,"scales.[x/y]Axes.barThickness","dataset.barThickness"),Ct("bar chart",e.categoryPercentage,"scales.[x/y]Axes.categoryPercentage","dataset.categoryPercentage"),Ct("bar chart",n._getValueScale().options.minBarLength,"scales.[x/y]Axes.minBarLength","dataset.minBarLength"),Ct("bar chart",e.maxBarThickness,"scales.[x/y]Axes.maxBarThickness","dataset.maxBarThickness")},update:function(t){var e,n,i=this.getMeta().data;for(this._ruler=this.getRuler(),e=0,n=i.length;e=0&&p.min>=0?p.min:p.max,y=void 0===p.start?p.end:p.max>=0&&p.min>=0?p.max-p.min:p.min-p.max,_=g.length;if(v||void 0===v&&void 0!==b)for(i=0;i<_&&(a=g[i]).index!==t;++i)a.stack===b&&(r=void 0===(u=h._parseValue(f[a.index].data[e])).start?u.end:u.min>=0&&u.max>=0?u.max:u.min,(p.min<0&&r<0||p.max>=0&&r>0)&&(x+=r));return o=h.getPixelForValue(x),l=(s=h.getPixelForValue(x+y))-o,void 0!==m&&Math.abs(l)=0&&!c||y<0&&c?o-m:o+m),{size:l,base:o,head:s,center:s+l/2}},calculateBarIndexPixels:function(t,e,n,i){var a="flex"===i.barThickness?function(t,e,n){var i,a=e.pixels,r=a[t],o=t>0?a[t-1]:null,s=t=Ot?-Rt:b<-Ot?Rt:0)+m,y=Math.cos(b),_=Math.sin(b),k=Math.cos(x),w=Math.sin(x),M=b<=0&&x>=0||x>=Rt,S=b<=zt&&x>=zt||x>=Rt+zt,C=b<=-zt&&x>=-zt||x>=Ot+zt,P=b===-Ot||x>=Ot?-1:Math.min(y,y*p,k,k*p),A=C?-1:Math.min(_,_*p,w,w*p),D=M?1:Math.max(y,y*p,k,k*p),T=S?1:Math.max(_,_*p,w,w*p);u=(D-P)/2,d=(T-A)/2,h=-(D+P)/2,c=-(T+A)/2}for(i=0,a=g.length;i0&&!isNaN(t)?Rt*(Math.abs(t)/e):0},getMaxBorderWidth:function(t){var e,n,i,a,r,o,s,l,u=0,d=this.chart;if(!t)for(e=0,n=d.data.datasets.length;e(u=s>u?s:u)?l:u);return u},setHoverStyle:function(t){var e=t._model,n=t._options,i=V.getHoverColor;t.$previousStyle={backgroundColor:e.backgroundColor,borderColor:e.borderColor,borderWidth:e.borderWidth},e.backgroundColor=Lt(n.hoverBackgroundColor,i(n.backgroundColor)),e.borderColor=Lt(n.hoverBorderColor,i(n.borderColor)),e.borderWidth=Lt(n.hoverBorderWidth,n.borderWidth)},_getRingWeightOffset:function(t){for(var e=0,n=0;n0&&Vt(l[t-1]._model,s)&&(n.controlPointPreviousX=u(n.controlPointPreviousX,s.left,s.right),n.controlPointPreviousY=u(n.controlPointPreviousY,s.top,s.bottom)),t0&&(r=t.getDatasetMeta(r[0]._datasetIndex).data),r},"x-axis":function(t,e){return ie(t,e,{intersect:!1})},point:function(t,e){return te(t,Jt(e,t))},nearest:function(t,e,n){var i=Jt(e,t);n.axis=n.axis||"xy";var a=ne(n.axis);return ee(t,i,n.intersect,a)},x:function(t,e,n){var i=Jt(e,t),a=[],r=!1;return Qt(t,(function(t){t.inXRange(i.x)&&a.push(t),t.inRange(i.x,i.y)&&(r=!0)})),n.intersect&&!r&&(a=[]),a},y:function(t,e,n){var i=Jt(e,t),a=[],r=!1;return Qt(t,(function(t){t.inYRange(i.y)&&a.push(t),t.inRange(i.x,i.y)&&(r=!0)})),n.intersect&&!r&&(a=[]),a}}},re=V.extend;function oe(t,e){return V.where(t,(function(t){return t.pos===e}))}function se(t,e){return t.sort((function(t,n){var i=e?n:t,a=e?t:n;return i.weight===a.weight?i.index-a.index:i.weight-a.weight}))}function le(t,e,n,i){return Math.max(t[n],e[n])+Math.max(t[i],e[i])}function ue(t,e,n){var i,a,r=n.box,o=t.maxPadding;if(n.size&&(t[n.pos]-=n.size),n.size=n.horizontal?r.height:r.width,t[n.pos]+=n.size,r.getPadding){var s=r.getPadding();o.top=Math.max(o.top,s.top),o.left=Math.max(o.left,s.left),o.bottom=Math.max(o.bottom,s.bottom),o.right=Math.max(o.right,s.right)}if(i=e.outerWidth-le(o,t,"left","right"),a=e.outerHeight-le(o,t,"top","bottom"),i!==t.w||a!==t.h)return t.w=i,t.h=a,n.horizontal?i!==t.w:a!==t.h}function de(t,e){var n=e.maxPadding;function i(t){var i={left:0,top:0,right:0,bottom:0};return t.forEach((function(t){i[t]=Math.max(e[t],n[t])})),i}return i(t?["left","right"]:["top","bottom"])}function he(t,e,n){var i,a,r,o,s,l,u=[];for(i=0,a=t.length;idiv{position:absolute;width:1000000px;height:1000000px;left:0;top:0}.chartjs-size-monitor-shrink>div{position:absolute;width:200%;height:200%;left:0;top:0}"}))&&fe.default||fe,me="$chartjs",ve="chartjs-size-monitor",be="chartjs-render-monitor",xe="chartjs-render-animation",ye=["animationstart","webkitAnimationStart"],_e={touchstart:"mousedown",touchmove:"mousemove",touchend:"mouseup",pointerenter:"mouseenter",pointerdown:"mousedown",pointermove:"mousemove",pointerup:"mouseup",pointerleave:"mouseout",pointerout:"mouseout"};function ke(t,e){var n=V.getStyle(t,e),i=n&&n.match(/^(\d+)(\.\d+)?px$/);return i?Number(i[1]):void 0}var we=!!function(){var t=!1;try{var e=Object.defineProperty({},"passive",{get:function(){t=!0}});window.addEventListener("e",null,e)}catch(t){}return t}()&&{passive:!0};function Me(t,e,n){t.addEventListener(e,n,we)}function Se(t,e,n){t.removeEventListener(e,n,we)}function Ce(t,e,n,i,a){return{type:t,chart:e,native:a||null,x:void 0!==n?n:null,y:void 0!==i?i:null}}function Pe(t){var e=document.createElement("div");return e.className=t||"",e}function Ae(t,e,n){var i,a,r,o,s=t[me]||(t[me]={}),l=s.resizer=function(t){var e=Pe(ve),n=Pe(ve+"-expand"),i=Pe(ve+"-shrink");n.appendChild(Pe()),i.appendChild(Pe()),e.appendChild(n),e.appendChild(i),e._reset=function(){n.scrollLeft=1e6,n.scrollTop=1e6,i.scrollLeft=1e6,i.scrollTop=1e6};var a=function(){e._reset(),t()};return Me(n,"scroll",a.bind(n,"expand")),Me(i,"scroll",a.bind(i,"shrink")),e}((i=function(){if(s.resizer){var i=n.options.maintainAspectRatio&&t.parentNode,a=i?i.clientWidth:0;e(Ce("resize",n)),i&&i.clientWidth0){var r=t[0];r.label?n=r.label:r.xLabel?n=r.xLabel:a>0&&r.index-1?t.split("\n"):t}function We(t){var e=z.global;return{xPadding:t.xPadding,yPadding:t.yPadding,xAlign:t.xAlign,yAlign:t.yAlign,rtl:t.rtl,textDirection:t.textDirection,bodyFontColor:t.bodyFontColor,_bodyFontFamily:Re(t.bodyFontFamily,e.defaultFontFamily),_bodyFontStyle:Re(t.bodyFontStyle,e.defaultFontStyle),_bodyAlign:t.bodyAlign,bodyFontSize:Re(t.bodyFontSize,e.defaultFontSize),bodySpacing:t.bodySpacing,titleFontColor:t.titleFontColor,_titleFontFamily:Re(t.titleFontFamily,e.defaultFontFamily),_titleFontStyle:Re(t.titleFontStyle,e.defaultFontStyle),titleFontSize:Re(t.titleFontSize,e.defaultFontSize),_titleAlign:t.titleAlign,titleSpacing:t.titleSpacing,titleMarginBottom:t.titleMarginBottom,footerFontColor:t.footerFontColor,_footerFontFamily:Re(t.footerFontFamily,e.defaultFontFamily),_footerFontStyle:Re(t.footerFontStyle,e.defaultFontStyle),footerFontSize:Re(t.footerFontSize,e.defaultFontSize),_footerAlign:t.footerAlign,footerSpacing:t.footerSpacing,footerMarginTop:t.footerMarginTop,caretSize:t.caretSize,cornerRadius:t.cornerRadius,backgroundColor:t.backgroundColor,opacity:0,legendColorBackground:t.multiKeyBackground,displayColors:t.displayColors,borderColor:t.borderColor,borderWidth:t.borderWidth}}function Ve(t,e){return"center"===e?t.x+t.width/2:"right"===e?t.x+t.width-t.xPadding:t.x+t.xPadding}function He(t){return Be([],Ee(t))}var je=X.extend({initialize:function(){this._model=We(this._options),this._lastActive=[]},getTitle:function(){var t=this,e=t._options,n=e.callbacks,i=n.beforeTitle.apply(t,arguments),a=n.title.apply(t,arguments),r=n.afterTitle.apply(t,arguments),o=[];return o=Be(o,Ee(i)),o=Be(o,Ee(a)),o=Be(o,Ee(r))},getBeforeBody:function(){return He(this._options.callbacks.beforeBody.apply(this,arguments))},getBody:function(t,e){var n=this,i=n._options.callbacks,a=[];return V.each(t,(function(t){var r={before:[],lines:[],after:[]};Be(r.before,Ee(i.beforeLabel.call(n,t,e))),Be(r.lines,i.label.call(n,t,e)),Be(r.after,Ee(i.afterLabel.call(n,t,e))),a.push(r)})),a},getAfterBody:function(){return He(this._options.callbacks.afterBody.apply(this,arguments))},getFooter:function(){var t=this,e=t._options.callbacks,n=e.beforeFooter.apply(t,arguments),i=e.footer.apply(t,arguments),a=e.afterFooter.apply(t,arguments),r=[];return r=Be(r,Ee(n)),r=Be(r,Ee(i)),r=Be(r,Ee(a))},update:function(t){var e,n,i,a,r,o,s,l,u,d,h=this,c=h._options,f=h._model,g=h._model=We(c),p=h._active,m=h._data,v={xAlign:f.xAlign,yAlign:f.yAlign},b={x:f.x,y:f.y},x={width:f.width,height:f.height},y={x:f.caretX,y:f.caretY};if(p.length){g.opacity=1;var _=[],k=[];y=Ne[c.position].call(h,p,h._eventPosition);var w=[];for(e=0,n=p.length;ei.width&&(a=i.width-e.width),a<0&&(a=0)),"top"===d?r+=h:r-="bottom"===d?e.height+h:e.height/2,"center"===d?"left"===u?a+=h:"right"===u&&(a-=h):"left"===u?a-=c:"right"===u&&(a+=c),{x:a,y:r}}(g,x,v=function(t,e){var n,i,a,r,o,s=t._model,l=t._chart,u=t._chart.chartArea,d="center",h="center";s.yl.height-e.height&&(h="bottom");var c=(u.left+u.right)/2,f=(u.top+u.bottom)/2;"center"===h?(n=function(t){return t<=c},i=function(t){return t>c}):(n=function(t){return t<=e.width/2},i=function(t){return t>=l.width-e.width/2}),a=function(t){return t+e.width+s.caretSize+s.caretPadding>l.width},r=function(t){return t-e.width-s.caretSize-s.caretPadding<0},o=function(t){return t<=f?"top":"bottom"},n(s.x)?(d="left",a(s.x)&&(d="center",h=o(s.y))):i(s.x)&&(d="right",r(s.x)&&(d="center",h=o(s.y)));var g=t._options;return{xAlign:g.xAlign?g.xAlign:d,yAlign:g.yAlign?g.yAlign:h}}(this,x),h._chart)}else g.opacity=0;return g.xAlign=v.xAlign,g.yAlign=v.yAlign,g.x=b.x,g.y=b.y,g.width=x.width,g.height=x.height,g.caretX=y.x,g.caretY=y.y,h._model=g,t&&c.custom&&c.custom.call(h,g),h},drawCaret:function(t,e){var n=this._chart.ctx,i=this._view,a=this.getCaretPosition(t,e,i);n.lineTo(a.x1,a.y1),n.lineTo(a.x2,a.y2),n.lineTo(a.x3,a.y3)},getCaretPosition:function(t,e,n){var i,a,r,o,s,l,u=n.caretSize,d=n.cornerRadius,h=n.xAlign,c=n.yAlign,f=t.x,g=t.y,p=e.width,m=e.height;if("center"===c)s=g+m/2,"left"===h?(a=(i=f)-u,r=i,o=s+u,l=s-u):(a=(i=f+p)+u,r=i,o=s-u,l=s+u);else if("left"===h?(i=(a=f+d+u)-u,r=a+u):"right"===h?(i=(a=f+p-d-u)-u,r=a+u):(i=(a=n.caretX)-u,r=a+u),"top"===c)s=(o=g)-u,l=o;else{s=(o=g+m)+u,l=o;var v=r;r=i,i=v}return{x1:i,x2:a,x3:r,y1:o,y2:s,y3:l}},drawTitle:function(t,e,n){var i,a,r,o=e.title,s=o.length;if(s){var l=ze(e.rtl,e.x,e.width);for(t.x=Ve(e,e._titleAlign),n.textAlign=l.textAlign(e._titleAlign),n.textBaseline="middle",i=e.titleFontSize,a=e.titleSpacing,n.fillStyle=e.titleFontColor,n.font=V.fontString(i,e._titleFontStyle,e._titleFontFamily),r=0;r0&&n.stroke()},draw:function(){var t=this._chart.ctx,e=this._view;if(0!==e.opacity){var n={width:e.width,height:e.height},i={x:e.x,y:e.y},a=Math.abs(e.opacity<.001)?0:e.opacity,r=e.title.length||e.beforeBody.length||e.body.length||e.afterBody.length||e.footer.length;this._options.enabled&&r&&(t.save(),t.globalAlpha=a,this.drawBackground(i,e,t,n),i.y+=e.yPadding,V.rtl.overrideTextDirection(t,e.textDirection),this.drawTitle(i,e,t),this.drawBody(i,e,t),this.drawFooter(i,e,t),V.rtl.restoreTextDirection(t,e.textDirection),t.restore())}},handleEvent:function(t){var e,n=this,i=n._options;return n._lastActive=n._lastActive||[],"mouseout"===t.type?n._active=[]:(n._active=n._chart.getElementsAtEventForMode(t,i.mode,i),i.reverse&&n._active.reverse()),(e=!V.arrayEquals(n._active,n._lastActive))&&(n._lastActive=n._active,(i.enabled||i.custom)&&(n._eventPosition={x:t.x,y:t.y},n.update(!0),n.pivot())),e}}),qe=Ne,Ue=je;Ue.positioners=qe;var Ye=V.valueOrDefault;function Ge(){return V.merge({},[].slice.call(arguments),{merger:function(t,e,n,i){if("xAxes"===t||"yAxes"===t){var a,r,o,s=n[t].length;for(e[t]||(e[t]=[]),a=0;a=e[t].length&&e[t].push({}),!e[t][a].type||o.type&&o.type!==e[t][a].type?V.merge(e[t][a],[Oe.getScaleDefaults(r),o]):V.merge(e[t][a],o)}else V._merger(t,e,n,i)}})}function Xe(){return V.merge({},[].slice.call(arguments),{merger:function(t,e,n,i){var a=e[t]||{},r=n[t];"scales"===t?e[t]=Ge(a,r):"scale"===t?e[t]=V.merge(a,[Oe.getScaleDefaults(r.type),r]):V._merger(t,e,n,i)}})}function Ke(t){var e=t.options;V.each(t.scales,(function(e){ge.removeBox(t,e)})),e=Xe(z.global,z[t.config.type],e),t.options=t.config.options=e,t.ensureScalesHaveIDs(),t.buildOrUpdateScales(),t.tooltip._options=e.tooltips,t.tooltip.initialize()}function Ze(t,e,n){var i,a=function(t){return t.id===i};do{i=e+n++}while(V.findIndex(t,a)>=0);return i}function $e(t){return"top"===t||"bottom"===t}function Je(t,e){return function(n,i){return n[t]===i[t]?n[e]-i[e]:n[t]-i[t]}}z._set("global",{elements:{},events:["mousemove","mouseout","click","touchstart","touchmove"],hover:{onHover:null,mode:"nearest",intersect:!0,animationDuration:400},onClick:null,maintainAspectRatio:!0,responsive:!0,responsiveAnimationDuration:0});var Qe=function(t,e){return this.construct(t,e),this};V.extend(Qe.prototype,{construct:function(t,e){var n=this;e=function(t){var e=(t=t||{}).data=t.data||{};return e.datasets=e.datasets||[],e.labels=e.labels||[],t.options=Xe(z.global,z[t.type],t.options||{}),t}(e);var i=Fe.acquireContext(t,e),a=i&&i.canvas,r=a&&a.height,o=a&&a.width;n.id=V.uid(),n.ctx=i,n.canvas=a,n.config=e,n.width=o,n.height=r,n.aspectRatio=r?o/r:null,n.options=e.options,n._bufferedRender=!1,n._layers=[],n.chart=n,n.controller=n,Qe.instances[n.id]=n,Object.defineProperty(n,"data",{get:function(){return n.config.data},set:function(t){n.config.data=t}}),i&&a?(n.initialize(),n.update()):console.error("Failed to create chart: can't acquire context from the given item")},initialize:function(){var t=this;return Le.notify(t,"beforeInit"),V.retinaScale(t,t.options.devicePixelRatio),t.bindEvents(),t.options.responsive&&t.resize(!0),t.initToolTip(),Le.notify(t,"afterInit"),t},clear:function(){return V.canvas.clear(this),this},stop:function(){return $.cancelAnimation(this),this},resize:function(t){var e=this,n=e.options,i=e.canvas,a=n.maintainAspectRatio&&e.aspectRatio||null,r=Math.max(0,Math.floor(V.getMaximumWidth(i))),o=Math.max(0,Math.floor(a?r/a:V.getMaximumHeight(i)));if((e.width!==r||e.height!==o)&&(i.width=e.width=r,i.height=e.height=o,i.style.width=r+"px",i.style.height=o+"px",V.retinaScale(e,n.devicePixelRatio),!t)){var s={width:r,height:o};Le.notify(e,"resize",[s]),n.onResize&&n.onResize(e,s),e.stop(),e.update({duration:n.responsiveAnimationDuration})}},ensureScalesHaveIDs:function(){var t=this.options,e=t.scales||{},n=t.scale;V.each(e.xAxes,(function(t,n){t.id||(t.id=Ze(e.xAxes,"x-axis-",n))})),V.each(e.yAxes,(function(t,n){t.id||(t.id=Ze(e.yAxes,"y-axis-",n))})),n&&(n.id=n.id||"scale")},buildOrUpdateScales:function(){var t=this,e=t.options,n=t.scales||{},i=[],a=Object.keys(n).reduce((function(t,e){return t[e]=!1,t}),{});e.scales&&(i=i.concat((e.scales.xAxes||[]).map((function(t){return{options:t,dtype:"category",dposition:"bottom"}})),(e.scales.yAxes||[]).map((function(t){return{options:t,dtype:"linear",dposition:"left"}})))),e.scale&&i.push({options:e.scale,dtype:"radialLinear",isDefault:!0,dposition:"chartArea"}),V.each(i,(function(e){var i=e.options,r=i.id,o=Ye(i.type,e.dtype);$e(i.position)!==$e(e.dposition)&&(i.position=e.dposition),a[r]=!0;var s=null;if(r in n&&n[r].type===o)(s=n[r]).options=i,s.ctx=t.ctx,s.chart=t;else{var l=Oe.getScaleConstructor(o);if(!l)return;s=new l({id:r,type:o,options:i,ctx:t.ctx,chart:t}),n[s.id]=s}s.mergeTicksOptions(),e.isDefault&&(t.scale=s)})),V.each(a,(function(t,e){t||delete n[e]})),t.scales=n,Oe.addScalesToLayout(this)},buildOrUpdateControllers:function(){var t,e,n=this,i=[],a=n.data.datasets;for(t=0,e=a.length;t=0;--n)this.drawDataset(e[n],t);Le.notify(this,"afterDatasetsDraw",[t])}},drawDataset:function(t,e){var n={meta:t,index:t.index,easingValue:e};!1!==Le.notify(this,"beforeDatasetDraw",[n])&&(t.controller.draw(e),Le.notify(this,"afterDatasetDraw",[n]))},_drawTooltip:function(t){var e=this.tooltip,n={tooltip:e,easingValue:t};!1!==Le.notify(this,"beforeTooltipDraw",[n])&&(e.draw(),Le.notify(this,"afterTooltipDraw",[n]))},getElementAtEvent:function(t){return ae.modes.single(this,t)},getElementsAtEvent:function(t){return ae.modes.label(this,t,{intersect:!0})},getElementsAtXAxis:function(t){return ae.modes["x-axis"](this,t,{intersect:!0})},getElementsAtEventForMode:function(t,e,n){var i=ae.modes[e];return"function"==typeof i?i(this,t,n):[]},getDatasetAtEvent:function(t){return ae.modes.dataset(this,t,{intersect:!0})},getDatasetMeta:function(t){var e=this.data.datasets[t];e._meta||(e._meta={});var n=e._meta[this.id];return n||(n=e._meta[this.id]={type:null,data:[],dataset:null,controller:null,hidden:null,xAxisID:null,yAxisID:null,order:e.order||0,index:t}),n},getVisibleDatasetCount:function(){for(var t=0,e=0,n=this.data.datasets.length;e3?n[2]-n[1]:n[1]-n[0];Math.abs(i)>1&&t!==Math.floor(t)&&(i=t-Math.floor(t));var a=V.log10(Math.abs(i)),r="";if(0!==t)if(Math.max(Math.abs(n[0]),Math.abs(n[n.length-1]))<1e-4){var o=V.log10(Math.abs(t)),s=Math.floor(o)-Math.floor(a);s=Math.max(Math.min(s,20),0),r=t.toExponential(s)}else{var l=-1*Math.floor(a);l=Math.max(Math.min(l,20),0),r=t.toFixed(l)}else r="0";return r},logarithmic:function(t,e,n){var i=t/Math.pow(10,Math.floor(V.log10(t)));return 0===t?"0":1===i||2===i||5===i||0===e||e===n.length-1?t.toExponential():""}}},on=V.isArray,sn=V.isNullOrUndef,ln=V.valueOrDefault,un=V.valueAtIndexOrDefault;function dn(t,e,n){var i,a=t.getTicks().length,r=Math.min(e,a-1),o=t.getPixelForTick(r),s=t._startPixel,l=t._endPixel;if(!(n&&(i=1===a?Math.max(o-s,l-o):0===e?(t.getPixelForTick(1)-o)/2:(o-t.getPixelForTick(r-1))/2,(o+=rl+1e-6)))return o}function hn(t,e,n,i){var a,r,o,s,l,u,d,h,c,f,g,p,m,v=n.length,b=[],x=[],y=[];for(a=0;ae){for(n=0;n=c||d<=1||!s.isHorizontal()?s.labelRotation=h:(e=(t=s._getLabelSizes()).widest.width,n=t.highest.height-t.highest.offset,i=Math.min(s.maxWidth,s.chart.width-e),e+6>(a=l.offset?s.maxWidth/d:i/(d-1))&&(a=i/(d-(l.offset?.5:1)),r=s.maxHeight-cn(l.gridLines)-u.padding-fn(l.scaleLabel),o=Math.sqrt(e*e+n*n),f=V.toDegrees(Math.min(Math.asin(Math.min((t.highest.height+6)/a,1)),Math.asin(Math.min(r/o,1))-Math.asin(n/o))),f=Math.max(h,Math.min(c,f))),s.labelRotation=f)},afterCalculateTickRotation:function(){V.callback(this.options.afterCalculateTickRotation,[this])},beforeFit:function(){V.callback(this.options.beforeFit,[this])},fit:function(){var t=this,e=t.minSize={width:0,height:0},n=t.chart,i=t.options,a=i.ticks,r=i.scaleLabel,o=i.gridLines,s=t._isVisible(),l="bottom"===i.position,u=t.isHorizontal();if(u?e.width=t.maxWidth:s&&(e.width=cn(o)+fn(r)),u?s&&(e.height=cn(o)+fn(r)):e.height=t.maxHeight,a.display&&s){var d=pn(a),h=t._getLabelSizes(),c=h.first,f=h.last,g=h.widest,p=h.highest,m=.4*d.minor.lineHeight,v=a.padding;if(u){var b=0!==t.labelRotation,x=V.toRadians(t.labelRotation),y=Math.cos(x),_=Math.sin(x),k=_*g.width+y*(p.height-(b?p.offset:0))+(b?0:m);e.height=Math.min(t.maxHeight,e.height+k+v);var w,M,S=t.getPixelForTick(0)-t.left,C=t.right-t.getPixelForTick(t.getTicks().length-1);b?(w=l?y*c.width+_*c.offset:_*(c.height-c.offset),M=l?_*(f.height-f.offset):y*f.width+_*f.offset):(w=c.width/2,M=f.width/2),t.paddingLeft=Math.max((w-S)*t.width/(t.width-S),0)+3,t.paddingRight=Math.max((M-C)*t.width/(t.width-C),0)+3}else{var P=a.mirror?0:g.width+v+m;e.width=Math.min(t.maxWidth,e.width+P),t.paddingTop=c.height/2,t.paddingBottom=f.height/2}}t.handleMargins(),u?(t.width=t._length=n.width-t.margins.left-t.margins.right,t.height=e.height):(t.width=e.width,t.height=t._length=n.height-t.margins.top-t.margins.bottom)},handleMargins:function(){var t=this;t.margins&&(t.margins.left=Math.max(t.paddingLeft,t.margins.left),t.margins.top=Math.max(t.paddingTop,t.margins.top),t.margins.right=Math.max(t.paddingRight,t.margins.right),t.margins.bottom=Math.max(t.paddingBottom,t.margins.bottom))},afterFit:function(){V.callback(this.options.afterFit,[this])},isHorizontal:function(){var t=this.options.position;return"top"===t||"bottom"===t},isFullWidth:function(){return this.options.fullWidth},getRightValue:function(t){if(sn(t))return NaN;if(("number"==typeof t||t instanceof Number)&&!isFinite(t))return NaN;if(t)if(this.isHorizontal()){if(void 0!==t.x)return this.getRightValue(t.x)}else if(void 0!==t.y)return this.getRightValue(t.y);return t},_convertTicksToLabels:function(t){var e,n,i,a=this;for(a.ticks=t.map((function(t){return t.value})),a.beforeTickToLabelConversion(),e=a.convertTicksToLabels(t)||a.ticks,a.afterTickToLabelConversion(),n=0,i=t.length;nn-1?null:this.getPixelForDecimal(t*i+(e?i/2:0))},getPixelForDecimal:function(t){return this._reversePixels&&(t=1-t),this._startPixel+t*this._length},getDecimalForPixel:function(t){var e=(t-this._startPixel)/this._length;return this._reversePixels?1-e:e},getBasePixel:function(){return this.getPixelForValue(this.getBaseValue())},getBaseValue:function(){var t=this.min,e=this.max;return this.beginAtZero?0:t<0&&e<0?e:t>0&&e>0?t:0},_autoSkip:function(t){var e,n,i,a,r=this.options.ticks,o=this._length,s=r.maxTicksLimit||o/this._tickSize()+1,l=r.major.enabled?function(t){var e,n,i=[];for(e=0,n=t.length;es)return function(t,e,n){var i,a,r=0,o=e[0];for(n=Math.ceil(n),i=0;iu)return r;return Math.max(u,1)}(l,t,0,s),u>0){for(e=0,n=u-1;e1?(h-d)/(u-1):null,vn(t,i,V.isNullOrUndef(a)?0:d-a,d),vn(t,i,h,V.isNullOrUndef(a)?t.length:h+a),mn(t)}return vn(t,i),mn(t)},_tickSize:function(){var t=this.options.ticks,e=V.toRadians(this.labelRotation),n=Math.abs(Math.cos(e)),i=Math.abs(Math.sin(e)),a=this._getLabelSizes(),r=t.autoSkipPadding||0,o=a?a.widest.width+r:0,s=a?a.highest.height+r:0;return this.isHorizontal()?s*n>o*i?o/n:s/i:s*i=0&&(o=t),void 0!==r&&(t=n.indexOf(r))>=0&&(s=t),e.minIndex=o,e.maxIndex=s,e.min=n[o],e.max=n[s]},buildTicks:function(){var t=this._getLabels(),e=this.minIndex,n=this.maxIndex;this.ticks=0===e&&n===t.length-1?t:t.slice(e,n+1)},getLabelForIndex:function(t,e){var n=this.chart;return n.getDatasetMeta(e).controller._getValueScaleId()===this.id?this.getRightValue(n.data.datasets[e].data[t]):this._getLabels()[t]},_configure:function(){var t=this,e=t.options.offset,n=t.ticks;xn.prototype._configure.call(t),t.isHorizontal()||(t._reversePixels=!t._reversePixels),n&&(t._startValue=t.minIndex-(e?.5:0),t._valueRange=Math.max(n.length-(e?0:1),1))},getPixelForValue:function(t,e,n){var i,a,r,o=this;return yn(e)||yn(n)||(t=o.chart.data.datasets[n].data[e]),yn(t)||(i=o.isHorizontal()?t.x:t.y),(void 0!==i||void 0!==t&&isNaN(e))&&(a=o._getLabels(),t=V.valueOrDefault(i,t),e=-1!==(r=a.indexOf(t))?r:e,isNaN(e)&&(e=t)),o.getPixelForDecimal((e-o._startValue)/o._valueRange)},getPixelForTick:function(t){var e=this.ticks;return t<0||t>e.length-1?null:this.getPixelForValue(e[t],t+this.minIndex)},getValueForPixel:function(t){var e=Math.round(this._startValue+this.getDecimalForPixel(t)*this._valueRange);return Math.min(Math.max(e,0),this.ticks.length-1)},getBasePixel:function(){return this.bottom}}),kn={position:"bottom"};_n._defaults=kn;var wn=V.noop,Mn=V.isNullOrUndef;var Sn=xn.extend({getRightValue:function(t){return"string"==typeof t?+t:xn.prototype.getRightValue.call(this,t)},handleTickRangeOptions:function(){var t=this,e=t.options.ticks;if(e.beginAtZero){var n=V.sign(t.min),i=V.sign(t.max);n<0&&i<0?t.max=0:n>0&&i>0&&(t.min=0)}var a=void 0!==e.min||void 0!==e.suggestedMin,r=void 0!==e.max||void 0!==e.suggestedMax;void 0!==e.min?t.min=e.min:void 0!==e.suggestedMin&&(null===t.min?t.min=e.suggestedMin:t.min=Math.min(t.min,e.suggestedMin)),void 0!==e.max?t.max=e.max:void 0!==e.suggestedMax&&(null===t.max?t.max=e.suggestedMax:t.max=Math.max(t.max,e.suggestedMax)),a!==r&&t.min>=t.max&&(a?t.max=t.min+1:t.min=t.max-1),t.min===t.max&&(t.max++,e.beginAtZero||t.min--)},getTickLimit:function(){var t,e=this.options.ticks,n=e.stepSize,i=e.maxTicksLimit;return n?t=Math.ceil(this.max/n)-Math.floor(this.min/n)+1:(t=this._computeTickLimit(),i=i||11),i&&(t=Math.min(i,t)),t},_computeTickLimit:function(){return Number.POSITIVE_INFINITY},handleDirectionalChanges:wn,buildTicks:function(){var t=this,e=t.options.ticks,n=t.getTickLimit(),i={maxTicks:n=Math.max(2,n),min:e.min,max:e.max,precision:e.precision,stepSize:V.valueOrDefault(e.fixedStepSize,e.stepSize)},a=t.ticks=function(t,e){var n,i,a,r,o=[],s=t.stepSize,l=s||1,u=t.maxTicks-1,d=t.min,h=t.max,c=t.precision,f=e.min,g=e.max,p=V.niceNum((g-f)/u/l)*l;if(p<1e-14&&Mn(d)&&Mn(h))return[f,g];(r=Math.ceil(g/p)-Math.floor(f/p))>u&&(p=V.niceNum(r*p/u/l)*l),s||Mn(c)?n=Math.pow(10,V._decimalPlaces(p)):(n=Math.pow(10,c),p=Math.ceil(p*n)/n),i=Math.floor(f/p)*p,a=Math.ceil(g/p)*p,s&&(!Mn(d)&&V.almostWhole(d/p,p/1e3)&&(i=d),!Mn(h)&&V.almostWhole(h/p,p/1e3)&&(a=h)),r=(a-i)/p,r=V.almostEquals(r,Math.round(r),p/1e3)?Math.round(r):Math.ceil(r),i=Math.round(i*n)/n,a=Math.round(a*n)/n,o.push(Mn(d)?i:d);for(var m=1;me.length-1?null:this.getPixelForValue(e[t])}}),Tn=Cn;Dn._defaults=Tn;var In=V.valueOrDefault,Fn=V.math.log10;var Ln={position:"left",ticks:{callback:rn.formatters.logarithmic}};function On(t,e){return V.isFinite(t)&&t>=0?t:e}var Rn=xn.extend({determineDataLimits:function(){var t,e,n,i,a,r,o=this,s=o.options,l=o.chart,u=l.data.datasets,d=o.isHorizontal();function h(t){return d?t.xAxisID===o.id:t.yAxisID===o.id}o.min=Number.POSITIVE_INFINITY,o.max=Number.NEGATIVE_INFINITY,o.minNotZero=Number.POSITIVE_INFINITY;var c=s.stacked;if(void 0===c)for(t=0;t0){var e=V.min(t),n=V.max(t);o.min=Math.min(o.min,e),o.max=Math.max(o.max,n)}}))}else for(t=0;t0?t.minNotZero=t.min:t.max<1?t.minNotZero=Math.pow(10,Math.floor(Fn(t.max))):t.minNotZero=1)},buildTicks:function(){var t=this,e=t.options.ticks,n=!t.isHorizontal(),i={min:On(e.min),max:On(e.max)},a=t.ticks=function(t,e){var n,i,a=[],r=In(t.min,Math.pow(10,Math.floor(Fn(e.min)))),o=Math.floor(Fn(e.max)),s=Math.ceil(e.max/Math.pow(10,o));0===r?(n=Math.floor(Fn(e.minNotZero)),i=Math.floor(e.minNotZero/Math.pow(10,n)),a.push(r),r=i*Math.pow(10,n)):(n=Math.floor(Fn(r)),i=Math.floor(r/Math.pow(10,n)));var l=n<0?Math.pow(10,Math.abs(n)):1;do{a.push(r),10===++i&&(i=1,l=++n>=0?1:l),r=Math.round(i*Math.pow(10,n)*l)/l}while(ne.length-1?null:this.getPixelForValue(e[t])},_getFirstTickValue:function(t){var e=Math.floor(Fn(t));return Math.floor(t/Math.pow(10,e))*Math.pow(10,e)},_configure:function(){var t=this,e=t.min,n=0;xn.prototype._configure.call(t),0===e&&(e=t._getFirstTickValue(t.minNotZero),n=In(t.options.ticks.fontSize,z.global.defaultFontSize)/t._length),t._startValue=Fn(e),t._valueOffset=n,t._valueRange=(Fn(t.max)-Fn(e))/(1-n)},getPixelForValue:function(t){var e=this,n=0;return(t=+e.getRightValue(t))>e.min&&t>0&&(n=(Fn(t)-e._startValue)/e._valueRange+e._valueOffset),e.getPixelForDecimal(n)},getValueForPixel:function(t){var e=this,n=e.getDecimalForPixel(t);return 0===n&&0===e.min?0:Math.pow(10,e._startValue+(n-e._valueOffset)*e._valueRange)}}),zn=Ln;Rn._defaults=zn;var Nn=V.valueOrDefault,Bn=V.valueAtIndexOrDefault,En=V.options.resolve,Wn={display:!0,animate:!0,position:"chartArea",angleLines:{display:!0,color:"rgba(0,0,0,0.1)",lineWidth:1,borderDash:[],borderDashOffset:0},gridLines:{circular:!1},ticks:{showLabelBackdrop:!0,backdropColor:"rgba(255,255,255,0.75)",backdropPaddingY:2,backdropPaddingX:2,callback:rn.formatters.linear},pointLabels:{display:!0,fontSize:10,callback:function(t){return t}}};function Vn(t){var e=t.ticks;return e.display&&t.display?Nn(e.fontSize,z.global.defaultFontSize)+2*e.backdropPaddingY:0}function Hn(t,e,n,i,a){return t===i||t===a?{start:e-n/2,end:e+n/2}:ta?{start:e-n,end:e}:{start:e,end:e+n}}function jn(t){return 0===t||180===t?"center":t<180?"left":"right"}function qn(t,e,n,i){var a,r,o=n.y+i/2;if(V.isArray(e))for(a=0,r=e.length;a270||t<90)&&(n.y-=e.h)}function Yn(t){return V.isNumber(t)?t:0}var Gn=Sn.extend({setDimensions:function(){var t=this;t.width=t.maxWidth,t.height=t.maxHeight,t.paddingTop=Vn(t.options)/2,t.xCenter=Math.floor(t.width/2),t.yCenter=Math.floor((t.height-t.paddingTop)/2),t.drawingArea=Math.min(t.height-t.paddingTop,t.width)/2},determineDataLimits:function(){var t=this,e=t.chart,n=Number.POSITIVE_INFINITY,i=Number.NEGATIVE_INFINITY;V.each(e.data.datasets,(function(a,r){if(e.isDatasetVisible(r)){var o=e.getDatasetMeta(r);V.each(a.data,(function(e,a){var r=+t.getRightValue(e);isNaN(r)||o.data[a].hidden||(n=Math.min(r,n),i=Math.max(r,i))}))}})),t.min=n===Number.POSITIVE_INFINITY?0:n,t.max=i===Number.NEGATIVE_INFINITY?0:i,t.handleTickRangeOptions()},_computeTickLimit:function(){return Math.ceil(this.drawingArea/Vn(this.options))},convertTicksToLabels:function(){var t=this;Sn.prototype.convertTicksToLabels.call(t),t.pointLabels=t.chart.data.labels.map((function(){var e=V.callback(t.options.pointLabels.callback,arguments,t);return e||0===e?e:""}))},getLabelForIndex:function(t,e){return+this.getRightValue(this.chart.data.datasets[e].data[t])},fit:function(){var t=this.options;t.display&&t.pointLabels.display?function(t){var e,n,i,a=V.options._parseFont(t.options.pointLabels),r={l:0,r:t.width,t:0,b:t.height-t.paddingTop},o={};t.ctx.font=a.string,t._pointLabelSizes=[];var s,l,u,d=t.chart.data.labels.length;for(e=0;er.r&&(r.r=f.end,o.r=h),g.startr.b&&(r.b=g.end,o.b=h)}t.setReductions(t.drawingArea,r,o)}(this):this.setCenterPoint(0,0,0,0)},setReductions:function(t,e,n){var i=this,a=e.l/Math.sin(n.l),r=Math.max(e.r-i.width,0)/Math.sin(n.r),o=-e.t/Math.cos(n.t),s=-Math.max(e.b-(i.height-i.paddingTop),0)/Math.cos(n.b);a=Yn(a),r=Yn(r),o=Yn(o),s=Yn(s),i.drawingArea=Math.min(Math.floor(t-(a+r)/2),Math.floor(t-(o+s)/2)),i.setCenterPoint(a,r,o,s)},setCenterPoint:function(t,e,n,i){var a=this,r=a.width-e-a.drawingArea,o=t+a.drawingArea,s=n+a.drawingArea,l=a.height-a.paddingTop-i-a.drawingArea;a.xCenter=Math.floor((o+r)/2+a.left),a.yCenter=Math.floor((s+l)/2+a.top+a.paddingTop)},getIndexAngle:function(t){var e=this.chart,n=(t*(360/e.data.labels.length)+((e.options||{}).startAngle||0))%360;return(n<0?n+360:n)*Math.PI*2/360},getDistanceFromCenterForValue:function(t){var e=this;if(V.isNullOrUndef(t))return NaN;var n=e.drawingArea/(e.max-e.min);return e.options.ticks.reverse?(e.max-t)*n:(t-e.min)*n},getPointPosition:function(t,e){var n=this.getIndexAngle(t)-Math.PI/2;return{x:Math.cos(n)*e+this.xCenter,y:Math.sin(n)*e+this.yCenter}},getPointPositionForValue:function(t,e){return this.getPointPosition(t,this.getDistanceFromCenterForValue(e))},getBasePosition:function(t){var e=this.min,n=this.max;return this.getPointPositionForValue(t||0,this.beginAtZero?0:e<0&&n<0?n:e>0&&n>0?e:0)},_drawGrid:function(){var t,e,n,i=this,a=i.ctx,r=i.options,o=r.gridLines,s=r.angleLines,l=Nn(s.lineWidth,o.lineWidth),u=Nn(s.color,o.color);if(r.pointLabels.display&&function(t){var e=t.ctx,n=t.options,i=n.pointLabels,a=Vn(n),r=t.getDistanceFromCenterForValue(n.ticks.reverse?t.min:t.max),o=V.options._parseFont(i);e.save(),e.font=o.string,e.textBaseline="middle";for(var s=t.chart.data.labels.length-1;s>=0;s--){var l=0===s?a/2:0,u=t.getPointPosition(s,r+l+5),d=Bn(i.fontColor,s,z.global.defaultFontColor);e.fillStyle=d;var h=t.getIndexAngle(s),c=V.toDegrees(h);e.textAlign=jn(c),Un(c,t._pointLabelSizes[s],u),qn(e,t.pointLabels[s],u,o.lineHeight)}e.restore()}(i),o.display&&V.each(i.ticks,(function(t,n){0!==n&&(e=i.getDistanceFromCenterForValue(i.ticksAsNumbers[n]),function(t,e,n,i){var a,r=t.ctx,o=e.circular,s=t.chart.data.labels.length,l=Bn(e.color,i-1),u=Bn(e.lineWidth,i-1);if((o||s)&&l&&u){if(r.save(),r.strokeStyle=l,r.lineWidth=u,r.setLineDash&&(r.setLineDash(e.borderDash||[]),r.lineDashOffset=e.borderDashOffset||0),r.beginPath(),o)r.arc(t.xCenter,t.yCenter,n,0,2*Math.PI);else{a=t.getPointPosition(0,n),r.moveTo(a.x,a.y);for(var d=1;d=0;t--)e=i.getDistanceFromCenterForValue(r.ticks.reverse?i.min:i.max),n=i.getPointPosition(t,e),a.beginPath(),a.moveTo(i.xCenter,i.yCenter),a.lineTo(n.x,n.y),a.stroke();a.restore()}},_drawLabels:function(){var t=this,e=t.ctx,n=t.options.ticks;if(n.display){var i,a,r=t.getIndexAngle(0),o=V.options._parseFont(n),s=Nn(n.fontColor,z.global.defaultFontColor);e.save(),e.font=o.string,e.translate(t.xCenter,t.yCenter),e.rotate(r),e.textAlign="center",e.textBaseline="middle",V.each(t.ticks,(function(r,l){(0!==l||n.reverse)&&(i=t.getDistanceFromCenterForValue(t.ticksAsNumbers[l]),n.showLabelBackdrop&&(a=e.measureText(r).width,e.fillStyle=n.backdropColor,e.fillRect(-a/2-n.backdropPaddingX,-i-o.size/2-n.backdropPaddingY,a+2*n.backdropPaddingX,o.size+2*n.backdropPaddingY)),e.fillStyle=s,e.fillText(r,0,-i))})),e.restore()}},_drawTitle:V.noop}),Xn=Wn;Gn._defaults=Xn;var Kn=V._deprecated,Zn=V.options.resolve,$n=V.valueOrDefault,Jn=Number.MIN_SAFE_INTEGER||-9007199254740991,Qn=Number.MAX_SAFE_INTEGER||9007199254740991,ti={millisecond:{common:!0,size:1,steps:1e3},second:{common:!0,size:1e3,steps:60},minute:{common:!0,size:6e4,steps:60},hour:{common:!0,size:36e5,steps:24},day:{common:!0,size:864e5,steps:30},week:{common:!1,size:6048e5,steps:4},month:{common:!0,size:2628e6,steps:12},quarter:{common:!1,size:7884e6,steps:4},year:{common:!0,size:3154e7}},ei=Object.keys(ti);function ni(t,e){return t-e}function ii(t){return V.valueOrDefault(t.time.min,t.ticks.min)}function ai(t){return V.valueOrDefault(t.time.max,t.ticks.max)}function ri(t,e,n,i){var a=function(t,e,n){for(var i,a,r,o=0,s=t.length-1;o>=0&&o<=s;){if(a=t[(i=o+s>>1)-1]||null,r=t[i],!a)return{lo:null,hi:r};if(r[e]n))return{lo:a,hi:r};s=i-1}}return{lo:r,hi:null}}(t,e,n),r=a.lo?a.hi?a.lo:t[t.length-2]:t[0],o=a.lo?a.hi?a.hi:t[t.length-1]:t[1],s=o[e]-r[e],l=s?(n-r[e])/s:0,u=(o[i]-r[i])*l;return r[i]+u}function oi(t,e){var n=t._adapter,i=t.options.time,a=i.parser,r=a||i.format,o=e;return"function"==typeof a&&(o=a(o)),V.isFinite(o)||(o="string"==typeof r?n.parse(o,r):n.parse(o)),null!==o?+o:(a||"function"!=typeof r||(o=r(e),V.isFinite(o)||(o=n.parse(o))),o)}function si(t,e){if(V.isNullOrUndef(e))return null;var n=t.options.time,i=oi(t,t.getRightValue(e));return null===i?i:(n.round&&(i=+t._adapter.startOf(i,n.round)),i)}function li(t,e,n,i){var a,r,o,s=ei.length;for(a=ei.indexOf(t);a=0&&(e[r].major=!0);return e}(t,r,o,n):r}var di=xn.extend({initialize:function(){this.mergeTicksOptions(),xn.prototype.initialize.call(this)},update:function(){var t=this,e=t.options,n=e.time||(e.time={}),i=t._adapter=new an._date(e.adapters.date);return Kn("time scale",n.format,"time.format","time.parser"),Kn("time scale",n.min,"time.min","ticks.min"),Kn("time scale",n.max,"time.max","ticks.max"),V.mergeIf(n.displayFormats,i.formats()),xn.prototype.update.apply(t,arguments)},getRightValue:function(t){return t&&void 0!==t.t&&(t=t.t),xn.prototype.getRightValue.call(this,t)},determineDataLimits:function(){var t,e,n,i,a,r,o,s=this,l=s.chart,u=s._adapter,d=s.options,h=d.time.unit||"day",c=Qn,f=Jn,g=[],p=[],m=[],v=s._getLabels();for(t=0,n=v.length;t1?function(t){var e,n,i,a={},r=[];for(e=0,n=t.length;e1e5*u)throw e+" and "+n+" are too far apart with stepSize of "+u+" "+l;for(a=h;a=a&&n<=r&&d.push(n);return i.min=a,i.max=r,i._unit=l.unit||(s.autoSkip?li(l.minUnit,i.min,i.max,h):function(t,e,n,i,a){var r,o;for(r=ei.length-1;r>=ei.indexOf(n);r--)if(o=ei[r],ti[o].common&&t._adapter.diff(a,i,o)>=e-1)return o;return ei[n?ei.indexOf(n):0]}(i,d.length,l.minUnit,i.min,i.max)),i._majorUnit=s.major.enabled&&"year"!==i._unit?function(t){for(var e=ei.indexOf(t)+1,n=ei.length;ee&&s=0&&t0?s:1}}),hi={position:"bottom",distribution:"linear",bounds:"data",adapters:{},time:{parser:!1,unit:!1,round:!1,displayFormat:!1,isoWeekday:!1,minUnit:"millisecond",displayFormats:{}},ticks:{autoSkip:!1,source:"auto",major:{enabled:!1}}};di._defaults=hi;var ci={category:_n,linear:Dn,logarithmic:Rn,radialLinear:Gn,time:di},fi={datetime:"MMM D, YYYY, h:mm:ss a",millisecond:"h:mm:ss.SSS a",second:"h:mm:ss a",minute:"h:mm a",hour:"hA",day:"MMM D",week:"ll",month:"MMM YYYY",quarter:"[Q]Q - YYYY",year:"YYYY"};an._date.override("function"==typeof t?{_id:"moment",formats:function(){return fi},parse:function(e,n){return"string"==typeof e&&"string"==typeof n?e=t(e,n):e instanceof t||(e=t(e)),e.isValid()?e.valueOf():null},format:function(e,n){return t(e).format(n)},add:function(e,n,i){return t(e).add(n,i).valueOf()},diff:function(e,n,i){return t(e).diff(t(n),i)},startOf:function(e,n,i){return e=t(e),"isoWeek"===n?e.isoWeekday(i).valueOf():e.startOf(n).valueOf()},endOf:function(e,n){return t(e).endOf(n).valueOf()},_create:function(e){return t(e)}}:{}),z._set("global",{plugins:{filler:{propagate:!0}}});var gi={dataset:function(t){var e=t.fill,n=t.chart,i=n.getDatasetMeta(e),a=i&&n.isDatasetVisible(e)&&i.dataset._children||[],r=a.length||0;return r?function(t,e){return e=n)&&i;switch(r){case"bottom":return"start";case"top":return"end";case"zero":return"origin";case"origin":case"start":case"end":return r;default:return!1}}function mi(t){return(t.el._scale||{}).getPointPositionForValue?function(t){var e,n,i,a,r,o=t.el._scale,s=o.options,l=o.chart.data.labels.length,u=t.fill,d=[];if(!l)return null;for(e=s.ticks.reverse?o.max:o.min,n=s.ticks.reverse?o.min:o.max,i=o.getPointPositionForValue(0,e),a=0;a0;--r)V.canvas.lineTo(t,n[r],n[r-1],!0);else for(o=n[0].cx,s=n[0].cy,l=Math.sqrt(Math.pow(n[0].x-o,2)+Math.pow(n[0].y-s,2)),r=a-1;r>0;--r)t.arc(o,s,l,n[r].angle,n[r-1].angle,!0)}}function _i(t,e,n,i,a,r){var o,s,l,u,d,h,c,f,g=e.length,p=i.spanGaps,m=[],v=[],b=0,x=0;for(t.beginPath(),o=0,s=g;o=0;--n)(e=l[n].$filler)&&e.visible&&(a=(i=e.el)._view,r=i._children||[],o=e.mapper,s=a.backgroundColor||z.global.defaultColor,o&&s&&r.length&&(V.canvas.clipArea(u,t.chartArea),_i(u,r,o,a,s,i._loop),V.canvas.unclipArea(u)))}},wi=V.rtl.getRtlAdapter,Mi=V.noop,Si=V.valueOrDefault;function Ci(t,e){return t.usePointStyle&&t.boxWidth>e?e:t.boxWidth}z._set("global",{legend:{display:!0,position:"top",align:"center",fullWidth:!0,reverse:!1,weight:1e3,onClick:function(t,e){var n=e.datasetIndex,i=this.chart,a=i.getDatasetMeta(n);a.hidden=null===a.hidden?!i.data.datasets[n].hidden:null,i.update()},onHover:null,onLeave:null,labels:{boxWidth:40,padding:10,generateLabels:function(t){var e=t.data.datasets,n=t.options.legend||{},i=n.labels&&n.labels.usePointStyle;return t._getSortedDatasetMetas().map((function(n){var a=n.controller.getStyle(i?0:void 0);return{text:e[n.index].label,fillStyle:a.backgroundColor,hidden:!t.isDatasetVisible(n.index),lineCap:a.borderCapStyle,lineDash:a.borderDash,lineDashOffset:a.borderDashOffset,lineJoin:a.borderJoinStyle,lineWidth:a.borderWidth,strokeStyle:a.borderColor,pointStyle:a.pointStyle,rotation:a.rotation,datasetIndex:n.index}}),this)}}},legendCallback:function(t){var e,n,i,a=document.createElement("ul"),r=t.data.datasets;for(a.setAttribute("class",t.id+"-legend"),e=0,n=r.length;el.width)&&(h+=o+n.padding,d[d.length-(e>0?0:1)]=0),s[e]={left:0,top:0,width:i,height:o},d[d.length-1]+=i+n.padding})),l.height+=h}else{var c=n.padding,f=t.columnWidths=[],g=t.columnHeights=[],p=n.padding,m=0,v=0;V.each(t.legendItems,(function(t,e){var i=Ci(n,o)+o/2+a.measureText(t.text).width;e>0&&v+o+2*c>l.height&&(p+=m+n.padding,f.push(m),g.push(v),m=0,v=0),m=Math.max(m,i),v+=o+c,s[e]={left:0,top:0,width:i,height:o}})),p+=m,f.push(m),g.push(v),l.width+=p}t.width=l.width,t.height=l.height}else t.width=l.width=t.height=l.height=0},afterFit:Mi,isHorizontal:function(){return"top"===this.options.position||"bottom"===this.options.position},draw:function(){var t=this,e=t.options,n=e.labels,i=z.global,a=i.defaultColor,r=i.elements.line,o=t.height,s=t.columnHeights,l=t.width,u=t.lineWidths;if(e.display){var d,h=wi(e.rtl,t.left,t.minSize.width),c=t.ctx,f=Si(n.fontColor,i.defaultFontColor),g=V.options._parseFont(n),p=g.size;c.textAlign=h.textAlign("left"),c.textBaseline="middle",c.lineWidth=.5,c.strokeStyle=f,c.fillStyle=f,c.font=g.string;var m=Ci(n,p),v=t.legendHitBoxes,b=function(t,i){switch(e.align){case"start":return n.padding;case"end":return t-i;default:return(t-i+n.padding)/2}},x=t.isHorizontal();d=x?{x:t.left+b(l,u[0]),y:t.top+n.padding,line:0}:{x:t.left+n.padding,y:t.top+b(o,s[0]),line:0},V.rtl.overrideTextDirection(t.ctx,e.textDirection);var y=p+n.padding;V.each(t.legendItems,(function(e,i){var f=c.measureText(e.text).width,g=m+p/2+f,_=d.x,k=d.y;h.setWidth(t.minSize.width),x?i>0&&_+g+n.padding>t.left+t.minSize.width&&(k=d.y+=y,d.line++,_=d.x=t.left+b(l,u[d.line])):i>0&&k+y>t.top+t.minSize.height&&(_=d.x=_+t.columnWidths[d.line]+n.padding,d.line++,k=d.y=t.top+b(o,s[d.line]));var w=h.x(_);!function(t,e,i){if(!(isNaN(m)||m<=0)){c.save();var o=Si(i.lineWidth,r.borderWidth);if(c.fillStyle=Si(i.fillStyle,a),c.lineCap=Si(i.lineCap,r.borderCapStyle),c.lineDashOffset=Si(i.lineDashOffset,r.borderDashOffset),c.lineJoin=Si(i.lineJoin,r.borderJoinStyle),c.lineWidth=o,c.strokeStyle=Si(i.strokeStyle,a),c.setLineDash&&c.setLineDash(Si(i.lineDash,r.borderDash)),n&&n.usePointStyle){var s=m*Math.SQRT2/2,l=h.xPlus(t,m/2),u=e+p/2;V.canvas.drawPoint(c,i.pointStyle,s,l,u,i.rotation)}else c.fillRect(h.leftForLtr(t,m),e,m,p),0!==o&&c.strokeRect(h.leftForLtr(t,m),e,m,p);c.restore()}}(w,k,e),v[i].left=h.leftForLtr(w,v[i].width),v[i].top=k,function(t,e,n,i){var a=p/2,r=h.xPlus(t,m+a),o=e+a;c.fillText(n.text,r,o),n.hidden&&(c.beginPath(),c.lineWidth=2,c.moveTo(r,o),c.lineTo(h.xPlus(r,i),o),c.stroke())}(w,k,e,f),x?d.x+=g+n.padding:d.y+=y})),V.rtl.restoreTextDirection(t.ctx,e.textDirection)}},_getLegendItemAt:function(t,e){var n,i,a,r=this;if(t>=r.left&&t<=r.right&&e>=r.top&&e<=r.bottom)for(a=r.legendHitBoxes,n=0;n=(i=a[n]).left&&t<=i.left+i.width&&e>=i.top&&e<=i.top+i.height)return r.legendItems[n];return null},handleEvent:function(t){var e,n=this,i=n.options,a="mouseup"===t.type?"click":t.type;if("mousemove"===a){if(!i.onHover&&!i.onLeave)return}else{if("click"!==a)return;if(!i.onClick)return}e=n._getLegendItemAt(t.x,t.y),"click"===a?e&&i.onClick&&i.onClick.call(n,t.native,e):(i.onLeave&&e!==n._hoveredItem&&(n._hoveredItem&&i.onLeave.call(n,t.native,n._hoveredItem),n._hoveredItem=e),i.onHover&&e&&i.onHover.call(n,t.native,e))}});function Ai(t,e){var n=new Pi({ctx:t.ctx,options:e,chart:t});ge.configure(t,n,e),ge.addBox(t,n),t.legend=n}var Di={id:"legend",_element:Pi,beforeInit:function(t){var e=t.options.legend;e&&Ai(t,e)},beforeUpdate:function(t){var e=t.options.legend,n=t.legend;e?(V.mergeIf(e,z.global.legend),n?(ge.configure(t,n,e),n.options=e):Ai(t,e)):n&&(ge.removeBox(t,n),delete t.legend)},afterEvent:function(t,e){var n=t.legend;n&&n.handleEvent(e)}},Ti=V.noop;z._set("global",{title:{display:!1,fontStyle:"bold",fullWidth:!0,padding:10,position:"top",text:"",weight:2e3}});var Ii=X.extend({initialize:function(t){V.extend(this,t),this.legendHitBoxes=[]},beforeUpdate:Ti,update:function(t,e,n){var i=this;return i.beforeUpdate(),i.maxWidth=t,i.maxHeight=e,i.margins=n,i.beforeSetDimensions(),i.setDimensions(),i.afterSetDimensions(),i.beforeBuildLabels(),i.buildLabels(),i.afterBuildLabels(),i.beforeFit(),i.fit(),i.afterFit(),i.afterUpdate(),i.minSize},afterUpdate:Ti,beforeSetDimensions:Ti,setDimensions:function(){var t=this;t.isHorizontal()?(t.width=t.maxWidth,t.left=0,t.right=t.width):(t.height=t.maxHeight,t.top=0,t.bottom=t.height),t.paddingLeft=0,t.paddingTop=0,t.paddingRight=0,t.paddingBottom=0,t.minSize={width:0,height:0}},afterSetDimensions:Ti,beforeBuildLabels:Ti,buildLabels:Ti,afterBuildLabels:Ti,beforeFit:Ti,fit:function(){var t,e=this,n=e.options,i=e.minSize={},a=e.isHorizontal();n.display?(t=(V.isArray(n.text)?n.text.length:1)*V.options._parseFont(n).lineHeight+2*n.padding,e.width=i.width=a?e.maxWidth:t,e.height=i.height=a?t:e.maxHeight):e.width=i.width=e.height=i.height=0},afterFit:Ti,isHorizontal:function(){var t=this.options.position;return"top"===t||"bottom"===t},draw:function(){var t=this,e=t.ctx,n=t.options;if(n.display){var i,a,r,o=V.options._parseFont(n),s=o.lineHeight,l=s/2+n.padding,u=0,d=t.top,h=t.left,c=t.bottom,f=t.right;e.fillStyle=V.valueOrDefault(n.fontColor,z.global.defaultFontColor),e.font=o.string,t.isHorizontal()?(a=h+(f-h)/2,r=d+l,i=f-h):(a="left"===n.position?h+l:f-l,r=d+(c-d)/2,i=c-d,u=Math.PI*("left"===n.position?-.5:.5)),e.save(),e.translate(a,r),e.rotate(u),e.textAlign="center",e.textBaseline="middle";var g=n.text;if(V.isArray(g))for(var p=0,m=0;m=0;i--){var a=t[i];if(e(a))return a}},V.isNumber=function(t){return!isNaN(parseFloat(t))&&isFinite(t)},V.almostEquals=function(t,e,n){return Math.abs(t-e)=t},V.max=function(t){return t.reduce((function(t,e){return isNaN(e)?t:Math.max(t,e)}),Number.NEGATIVE_INFINITY)},V.min=function(t){return t.reduce((function(t,e){return isNaN(e)?t:Math.min(t,e)}),Number.POSITIVE_INFINITY)},V.sign=Math.sign?function(t){return Math.sign(t)}:function(t){return 0===(t=+t)||isNaN(t)?t:t>0?1:-1},V.toRadians=function(t){return t*(Math.PI/180)},V.toDegrees=function(t){return t*(180/Math.PI)},V._decimalPlaces=function(t){if(V.isFinite(t)){for(var e=1,n=0;Math.round(t*e)/e!==t;)e*=10,n++;return n}},V.getAngleFromPoint=function(t,e){var n=e.x-t.x,i=e.y-t.y,a=Math.sqrt(n*n+i*i),r=Math.atan2(i,n);return r<-.5*Math.PI&&(r+=2*Math.PI),{angle:r,distance:a}},V.distanceBetweenPoints=function(t,e){return Math.sqrt(Math.pow(e.x-t.x,2)+Math.pow(e.y-t.y,2))},V.aliasPixel=function(t){return t%2==0?0:.5},V._alignPixel=function(t,e,n){var i=t.currentDevicePixelRatio,a=n/2;return Math.round((e-a)*i)/i+a},V.splineCurve=function(t,e,n,i){var a=t.skip?e:t,r=e,o=n.skip?e:n,s=Math.sqrt(Math.pow(r.x-a.x,2)+Math.pow(r.y-a.y,2)),l=Math.sqrt(Math.pow(o.x-r.x,2)+Math.pow(o.y-r.y,2)),u=s/(s+l),d=l/(s+l),h=i*(u=isNaN(u)?0:u),c=i*(d=isNaN(d)?0:d);return{previous:{x:r.x-h*(o.x-a.x),y:r.y-h*(o.y-a.y)},next:{x:r.x+c*(o.x-a.x),y:r.y+c*(o.y-a.y)}}},V.EPSILON=Number.EPSILON||1e-14,V.splineCurveMonotone=function(t){var e,n,i,a,r,o,s,l,u,d=(t||[]).map((function(t){return{model:t._model,deltaK:0,mK:0}})),h=d.length;for(e=0;e0?d[e-1]:null,(a=e0?d[e-1]:null,a=e=t.length-1?t[0]:t[e+1]:e>=t.length-1?t[t.length-1]:t[e+1]},V.previousItem=function(t,e,n){return n?e<=0?t[t.length-1]:t[e-1]:e<=0?t[0]:t[e-1]},V.niceNum=function(t,e){var n=Math.floor(V.log10(t)),i=t/Math.pow(10,n);return(e?i<1.5?1:i<3?2:i<7?5:10:i<=1?1:i<=2?2:i<=5?5:10)*Math.pow(10,n)},V.requestAnimFrame="undefined"==typeof window?function(t){t()}:window.requestAnimationFrame||window.webkitRequestAnimationFrame||window.mozRequestAnimationFrame||window.oRequestAnimationFrame||window.msRequestAnimationFrame||function(t){return window.setTimeout(t,1e3/60)},V.getRelativePosition=function(t,e){var n,i,a=t.originalEvent||t,r=t.target||t.srcElement,o=r.getBoundingClientRect(),s=a.touches;s&&s.length>0?(n=s[0].clientX,i=s[0].clientY):(n=a.clientX,i=a.clientY);var l=parseFloat(V.getStyle(r,"padding-left")),u=parseFloat(V.getStyle(r,"padding-top")),d=parseFloat(V.getStyle(r,"padding-right")),h=parseFloat(V.getStyle(r,"padding-bottom")),c=o.right-o.left-l-d,f=o.bottom-o.top-u-h;return{x:n=Math.round((n-o.left-l)/c*r.width/e.currentDevicePixelRatio),y:i=Math.round((i-o.top-u)/f*r.height/e.currentDevicePixelRatio)}},V.getConstraintWidth=function(t){return n(t,"max-width","clientWidth")},V.getConstraintHeight=function(t){return n(t,"max-height","clientHeight")},V._calculatePadding=function(t,e,n){return(e=V.getStyle(t,e)).indexOf("%")>-1?n*parseInt(e,10)/100:parseInt(e,10)},V._getParentNode=function(t){var e=t.parentNode;return e&&"[object ShadowRoot]"===e.toString()&&(e=e.host),e},V.getMaximumWidth=function(t){var e=V._getParentNode(t);if(!e)return t.clientWidth;var n=e.clientWidth,i=n-V._calculatePadding(e,"padding-left",n)-V._calculatePadding(e,"padding-right",n),a=V.getConstraintWidth(t);return isNaN(a)?i:Math.min(i,a)},V.getMaximumHeight=function(t){var e=V._getParentNode(t);if(!e)return t.clientHeight;var n=e.clientHeight,i=n-V._calculatePadding(e,"padding-top",n)-V._calculatePadding(e,"padding-bottom",n),a=V.getConstraintHeight(t);return isNaN(a)?i:Math.min(i,a)},V.getStyle=function(t,e){return t.currentStyle?t.currentStyle[e]:document.defaultView.getComputedStyle(t,null).getPropertyValue(e)},V.retinaScale=function(t,e){var n=t.currentDevicePixelRatio=e||"undefined"!=typeof window&&window.devicePixelRatio||1;if(1!==n){var i=t.canvas,a=t.height,r=t.width;i.height=a*n,i.width=r*n,t.ctx.scale(n,n),i.style.height||i.style.width||(i.style.height=a+"px",i.style.width=r+"px")}},V.fontString=function(t,e,n){return e+" "+t+"px "+n},V.longestText=function(t,e,n,i){var a=(i=i||{}).data=i.data||{},r=i.garbageCollect=i.garbageCollect||[];i.font!==e&&(a=i.data={},r=i.garbageCollect=[],i.font=e),t.font=e;var o,s,l,u,d,h=0,c=n.length;for(o=0;on.length){for(o=0;oi&&(i=r),i},V.numberOfLabelLines=function(t){var e=1;return V.each(t,(function(t){V.isArray(t)&&t.length>e&&(e=t.length)})),e},V.color=k?function(t){return t instanceof CanvasGradient&&(t=z.global.defaultColor),k(t)}:function(t){return console.error("Color.js not found!"),t},V.getHoverColor=function(t){return t instanceof CanvasPattern||t instanceof CanvasGradient?t:V.color(t).saturate(.5).darken(.1).rgbString()}}(),tn._adapters=an,tn.Animation=Z,tn.animationService=$,tn.controllers=$t,tn.DatasetController=nt,tn.defaults=z,tn.Element=X,tn.elements=_t,tn.Interaction=ae,tn.layouts=ge,tn.platform=Fe,tn.plugins=Le,tn.Scale=xn,tn.scaleService=Oe,tn.Ticks=rn,tn.Tooltip=Ue,tn.helpers.each(ci,(function(t,e){tn.scaleService.registerScaleType(e,t,t._defaults)})),Li)Li.hasOwnProperty(Ni)&&tn.plugins.register(Li[Ni]);tn.platform.initialize();var Bi=tn;return"undefined"!=typeof window&&(window.Chart=tn),tn.Chart=tn,tn.Legend=Li.legend._element,tn.Title=Li.title._element,tn.pluginService=tn.plugins,tn.PluginBase=tn.Element.extend({}),tn.canvasHelpers=tn.helpers.canvas,tn.layoutService=tn.layouts,tn.LinearScaleBase=Sn,tn.helpers.each(["Bar","Bubble","Doughnut","Line","PolarArea","Radar","Scatter"],(function(t){tn[t]=function(e,n){return new tn(e,tn.helpers.merge(n||{},{type:t.charAt(0).toLowerCase()+t.slice(1)}))}})),Bi})); diff --git a/lib/web/css/source/lib/_resets.less b/lib/web/css/source/lib/_resets.less index a680c91af3dbe..4c3d4476c2c80 100644 --- a/lib/web/css/source/lib/_resets.less +++ b/lib/web/css/source/lib/_resets.less @@ -48,14 +48,14 @@ } img { + max-width: 100%; + height: auto; border: 0; } - img, - object, video, - embed { - max-height: 100%; + embed, + object { max-width: 100%; } diff --git a/lib/web/fotorama/fotorama.js b/lib/web/fotorama/fotorama.js index ddf5a7f000d8e..314949bd2f4cf 100644 --- a/lib/web/fotorama/fotorama.js +++ b/lib/web/fotorama/fotorama.js @@ -1220,22 +1220,6 @@ fotoramaVersion = '4.6.4'; stopPropagation && e.stopPropagation && e.stopPropagation(); } - function stubEvent($el, eventType) { - var isIOS = /ip(ad|hone|od)/i.test(window.navigator.userAgent); - - if (isIOS && eventType === 'touchend') { - $el.on('touchend', function(e){ - $DOCUMENT.trigger('mouseup', e); - }) - } - - $el.on(eventType, function (e) { - stopEvent(e, true); - - return false; - }); - } - function getDirectionSign(forward) { return forward ? '>' : '<'; } @@ -1538,10 +1522,10 @@ fotoramaVersion = '4.6.4'; $WINDOW.on('scroll', onOtherEnd); - $el.on('mousedown pointerdown', onStart); + $el.on('mousedown', onStart); $DOCUMENT - .on('mousemove pointermove', onMove) - .on('mouseup pointerup', onEnd); + .on('mousemove', onMove) + .on('mouseup', onEnd); } if (Modernizr.touch) { dragDomEl = 'a'; @@ -2183,11 +2167,6 @@ fotoramaVersion = '4.6.4'; if (o_allowFullScreen) { $fullscreenIcon.prependTo($stage); o_nativeFullScreen = FULLSCREEN && o_allowFullScreen === 'native'; - - // Due 300ms click delay on mobile devices - // we stub touchend and fallback to click. - // MAGETWO-69567 - stubEvent($fullscreenIcon, 'touchend'); } else { $fullscreenIcon.detach(); o_nativeFullScreen = false; @@ -3275,13 +3254,9 @@ fotoramaVersion = '4.6.4'; if (measureIsValid(width)) { $wrap.css({width: ''}); - $wrap.css({height: ''}); $stage.css({width: ''}); - $stage.css({height: ''}); $stageShaft.css({width: ''}); - $stageShaft.css({height: ''}); $nav.css({width: ''}); - $nav.css({height: ''}); $wrap.css({minWidth: measures.minwidth || 0, maxWidth: measures.maxwidth || MAX_WIDTH}); if (o_nav === 'dots') { diff --git a/lib/web/jquery/jstree/jquery.hotkeys.js b/lib/web/jquery/jstree/jquery.hotkeys.js index 207c65bb06df6..c36582a503f43 100644 --- a/lib/web/jquery/jstree/jquery.hotkeys.js +++ b/lib/web/jquery/jstree/jquery.hotkeys.js @@ -53,7 +53,8 @@ * longer maintained by its author however we require content editable to behave as expected. */ if ( this !== event.target && (/textarea|select/i.test( event.target.nodeName ) || - event.target.type === "text" || jQuery(event.target).attr('contenteditable')) ) { + event.target.type === "text" || event.target.type === "number" || + jQuery(event.target).attr('contenteditable')) ) { return; } diff --git a/lib/web/mage/adminhtml/browser.js b/lib/web/mage/adminhtml/browser.js index 7780ac524fa49..06a77cb46064b 100644 --- a/lib/web/mage/adminhtml/browser.js +++ b/lib/web/mage/adminhtml/browser.js @@ -417,6 +417,11 @@ define([ showLoader: true }).done($.proxy(function () { self.tree.jstree('refresh', self.activeNode.id); + self.reload(); + $(window).trigger('fileDeleted.mediabrowser', { + ids: self.activeNode.id + }); + }, this)); }, diff --git a/lib/web/mage/adminhtml/form.js b/lib/web/mage/adminhtml/form.js index 487c71484e4c5..4dfbde6afa9d7 100644 --- a/lib/web/mage/adminhtml/form.js +++ b/lib/web/mage/adminhtml/form.js @@ -494,7 +494,8 @@ define([ inputs.each(function (item) { // don't touch hidden inputs (and Use Default inputs too), bc they may have custom logic if ((!item.type || item.type != 'hidden') && !($(item.id + '_inherit') && $(item.id + '_inherit').checked) && //eslint-disable-line - !(currentConfig['can_edit_price'] != undefined && !currentConfig['can_edit_price']) //eslint-disable-line + !(currentConfig['can_edit_price'] != undefined && !currentConfig['can_edit_price']) && //eslint-disable-line + !item.getAttribute('readonly') //eslint-disable-line ) { item.disabled = false; jQuery(item).removeClass('ignore-validate'); diff --git a/lib/web/mage/adminhtml/grid.js b/lib/web/mage/adminhtml/grid.js index 28bdb96e5cdb7..a74218466bcf5 100644 --- a/lib/web/mage/adminhtml/grid.js +++ b/lib/web/mage/adminhtml/grid.js @@ -63,12 +63,14 @@ define([ this.initRowCallback = false; this.doFilterCallback = false; this.sortableUpdateCallback = false; + this.targetElement = undefined; this.filterKeyPressCallback = false; this.reloadParams = false; this.trOnMouseOver = this.rowMouseOver.bindAsEventListener(this); this.trOnMouseOut = this.rowMouseOut.bindAsEventListener(this); + this.trOnMouseDown = this.rowMouseDown.bindAsEventListener(this); this.trOnClick = this.rowMouseClick.bindAsEventListener(this); this.trOnDblClick = this.rowMouseDblClick.bindAsEventListener(this); this.trOnKeyPress = this.keyPress.bindAsEventListener(this); @@ -97,6 +99,7 @@ define([ Event.observe(this.rows[row], 'mouseover', this.trOnMouseOver); Event.observe(this.rows[row], 'mouseout', this.trOnMouseOut); + Event.observe(this.rows[row], 'mousedown', this.trOnMouseDown); Event.observe(this.rows[row], 'click', this.trOnClick); Event.observe(this.rows[row], 'dblclick', this.trOnDblClick); } @@ -115,8 +118,7 @@ define([ if (this.initCallback) { try { this.initCallback(this); - } - catch (e) { + } catch (e) { if (window.console) { //eslint-disable-line max-depth console.log(e); } @@ -180,6 +182,13 @@ define([ Element.removeClassName(element, 'on-mouse'); }, + /** + * @param {*} event + */ + rowMouseDown: function (event) { + this.targetElement = event.target; + }, + /** * @param {*} event */ @@ -187,11 +196,11 @@ define([ if (this.rowClickCallback) { try { this.rowClickCallback(this, event); - } - catch (e) { + } catch (e) { } } varienGlobalEvents.fireEvent('gridRowClick', event); + this.targetElement = undefined; }, /** @@ -204,7 +213,8 @@ define([ /** * Key press. */ - keyPress: function () {}, + keyPress: function () { + }, /** * @param {*} event @@ -451,8 +461,8 @@ define([ */ bindFilterFields: function () { var filters = $$( - '#' + this.containerId + ' [data-role="filter-form"] input', - '#' + this.containerId + ' [data-role="filter-form"] select' + '#' + this.containerId + ' [data-role="filter-form"] input', + '#' + this.containerId + ' [data-role="filter-form"] select' ), i; @@ -499,7 +509,8 @@ define([ return ui; }, - update: this.sortableUpdateCallback ? this.sortableUpdateCallback : function () {}, + update: this.sortableUpdateCallback ? this.sortableUpdateCallback : function () { + }, tolerance: 'pointer' }); } @@ -523,8 +534,8 @@ define([ */ doFilter: function (callback) { var filters = $$( - '#' + this.containerId + ' [data-role="filter-form"] input', - '#' + this.containerId + ' [data-role="filter-form"] select' + '#' + this.containerId + ' [data-role="filter-form"] input', + '#' + this.containerId + ' [data-role="filter-form"] select' ), elements = [], i; @@ -640,24 +651,24 @@ define([ */ initialize: function (containerId, grid, checkedValues, formFieldNameInternal, formFieldName) { this.setOldCallback('row_click', grid.rowClickCallback); - this.setOldCallback('init', grid.initCallback); - this.setOldCallback('init_row', grid.initRowCallback); - this.setOldCallback('pre_init', grid.preInitCallback); + this.setOldCallback('init', grid.initCallback); + this.setOldCallback('init_row', grid.initRowCallback); + this.setOldCallback('pre_init', grid.preInitCallback); - this.useAjax = false; - this.grid = grid; + this.useAjax = false; + this.grid = grid; this.grid.massaction = this; - this.containerId = containerId; + this.containerId = containerId; this.initMassactionElements(); - this.checkedString = checkedValues; - this.formFieldName = formFieldName; - this.formFieldNameInternal = formFieldNameInternal; + this.checkedString = checkedValues; + this.formFieldName = formFieldName; + this.formFieldNameInternal = formFieldNameInternal; - this.grid.initCallback = this.onGridInit.bind(this); - this.grid.preInitCallback = this.onGridPreInit.bind(this); - this.grid.initRowCallback = this.onGridRowInit.bind(this); - this.grid.rowClickCallback = this.onGridRowClick.bind(this); + this.grid.initCallback = this.onGridInit.bind(this); + this.grid.preInitCallback = this.onGridPreInit.bind(this); + this.grid.initRowCallback = this.onGridRowInit.bind(this); + this.grid.rowClickCallback = this.onGridRowClick.bind(this); this.initCheckboxes(); this.checkCheckboxes(); }, @@ -680,16 +691,16 @@ define([ * Init massaction elements. */ initMassactionElements: function () { - this.container = $(this.containerId); - this.multiselect = $(this.containerId + '-mass-select'); - this.count = $(this.containerId + '-count'); - this.formHiddens = $(this.containerId + '-form-hiddens'); + this.container = $(this.containerId); + this.multiselect = $(this.containerId + '-mass-select'); + this.count = $(this.containerId + '-count'); + this.formHiddens = $(this.containerId + '-form-hiddens'); this.formAdditional = $(this.containerId + '-form-additional'); - this.select = $(this.containerId + '-select'); - this.form = this.prepareForm(); + this.select = $(this.containerId + '-select'); + this.form = this.prepareForm(); jQuery(this.form).mage('validation'); this.select.observe('change', this.onSelectChange.bindAsEventListener(this)); - this.lastChecked = { + this.lastChecked = { left: false, top: false, checkbox: false @@ -846,7 +857,7 @@ define([ this.getOldCallback('row_click')(grid, evt); } else { checkbox = Element.select(trElement, 'input'); - isInput = Event.element(evt).tagName == 'input'; //eslint-disable-line eqeqeq + isInput = Event.element(evt).tagName == 'input'; //eslint-disable-line eqeqeq checked = isInput ? checkbox[0].checked : !checkbox[0].checked; if (checked) { //eslint-disable-line max-depth @@ -1158,7 +1169,8 @@ define([ try { listener = this.getListener(this.currentItem.complete) || Prototype.emptyFunction; listener(this.grid, this, transport); - } catch (e) {} + } catch (e) { + } } }, @@ -1369,8 +1381,8 @@ define([ * @return {String} */ trimComma: function (string) { - string = string.replace(new RegExp('^(,+)','i'), ''); - string = string.replace(new RegExp('(,+)$','i'), ''); + string = string.replace(new RegExp('^(,+)', 'i'), ''); + string = string.replace(new RegExp('(,+)$', 'i'), ''); return string; } @@ -1390,14 +1402,14 @@ define([ initialize: function (hiddenDataHolder, predefinedData, inputsToManage, grid, reloadParamName) { //Grid inputs this.tabIndex = 1000; - this.inputsToManage = inputsToManage; + this.inputsToManage = inputsToManage; this.multidimensionalMode = inputsToManage.length > 0; //Hash with grid data - this.gridData = this.getGridDataHash(predefinedData); + this.gridData = this.getGridDataHash(predefinedData); //Hidden input data holder - this.hiddenDataHolder = $(hiddenDataHolder); + this.hiddenDataHolder = $(hiddenDataHolder); this.hiddenDataHolder.value = this.serializeObject(); this.grid = grid; @@ -1490,7 +1502,7 @@ define([ */ rowClick: function (grid, event) { var trElement = Event.findElement(event, 'tr'), - isInput = Event.element(event).tagName == 'INPUT', //eslint-disable-line eqeqeq + isInput = Event.element(event).tagName == 'INPUT', //eslint-disable-line eqeqeq checkbox, checked; if (trElement) { diff --git a/lib/web/mage/adminhtml/wysiwyg/tiny_mce/plugins/magentowidget/editor_plugin.js b/lib/web/mage/adminhtml/wysiwyg/tiny_mce/plugins/magentowidget/editor_plugin.js index f163206a13656..a9259a9a2daf3 100644 --- a/lib/web/mage/adminhtml/wysiwyg/tiny_mce/plugins/magentowidget/editor_plugin.js +++ b/lib/web/mage/adminhtml/wysiwyg/tiny_mce/plugins/magentowidget/editor_plugin.js @@ -168,17 +168,22 @@ define([ /(]*>)?]+id="[^>]+)>(([^>]*)<\/span>)?/i, function (match) { var attributes = wysiwyg.parseAttributesString(match[2]), - widgetCode; + widgetCode, + result = match[0]; if (attributes.id) { - widgetCode = Base64.idDecode(attributes.id); + try { + widgetCode = Base64.idDecode(attributes.id); + } catch (e) { + // Ignore and continue. + } - if (widgetCode.indexOf('{{widget') !== -1) { - return widgetCode; + if (widgetCode && widgetCode.indexOf('{{widget') !== -1) { + result = widgetCode; } } - return match[0]; + return result; } ); }, diff --git a/lib/web/mage/adminhtml/wysiwyg/tiny_mce/tinymce4Adapter.js b/lib/web/mage/adminhtml/wysiwyg/tiny_mce/tinymce4Adapter.js index aea636066be61..4393b6c882039 100644 --- a/lib/web/mage/adminhtml/wysiwyg/tiny_mce/tinymce4Adapter.js +++ b/lib/web/mage/adminhtml/wysiwyg/tiny_mce/tinymce4Adapter.js @@ -608,7 +608,6 @@ define([ } this.addContentEditableAttributeBackToNonEditableNodes(); - this.fixRangeSelection(editor); content = editor.getContent(); content = this.decodeContent(content); diff --git a/lib/web/mage/apply/main.js b/lib/web/mage/apply/main.js index 21255fbeb34d1..827283da3dbd9 100644 --- a/lib/web/mage/apply/main.js +++ b/lib/web/mage/apply/main.js @@ -22,16 +22,23 @@ define([ */ function init(el, config, component) { require([component], function (fn) { + var $el; if (typeof fn === 'object') { fn = fn[component].bind(fn); } if (_.isFunction(fn)) { - fn(config, el); - } else if ($(el)[component]) { - $(el)[component](config); + fn = fn.bind(null, config, el); + } else { + $el = $(el); + + if ($el[component]) { + fn = $el[component].bind($el, config); + } } + // Init module in separate task to prevent blocking main thread. + setTimeout(fn); }, function (error) { if ('console' in window && typeof window.console.error === 'function') { console.error(error); diff --git a/lib/web/mage/bootstrap.js b/lib/web/mage/bootstrap.js index f7b2cfaa314cb..924b1628f3eb9 100644 --- a/lib/web/mage/bootstrap.js +++ b/lib/web/mage/bootstrap.js @@ -16,6 +16,7 @@ define([ /** * Init all components defined via data-mage-init attribute. + * Execute in a separate task to prevent main thread blocking. */ - $(mage.apply); + setTimeout(mage.apply); }); diff --git a/lib/web/mage/gallery/gallery.js b/lib/web/mage/gallery/gallery.js index be78856b21fcd..02ba72f64127b 100644 --- a/lib/web/mage/gallery/gallery.js +++ b/lib/web/mage/gallery/gallery.js @@ -291,10 +291,12 @@ define([ config.click = false; config.breakpoints = null; settings.currentConfig = config; + settings.$element.css('min-height', settings.$element.height()); settings.$element.html(tpl); settings.$element.removeClass('_block-content-loading'); settings.$elementF = $(settings.$element.children()[0]); settings.$elementF.fotorama(config); + settings.$element.css('min-height', ''); settings.fotoramaApi = settings.$elementF.data('fotorama'); $.extend(true, config, this.startConfig); @@ -478,7 +480,7 @@ define([ settings.fotoramaApi.load(data); mainImageIndex = getMainImageIndex(data); - if (mainImageIndex) { + if (settings.fotoramaApi.activeIndex !== mainImageIndex) { settings.fotoramaApi.show({ index: mainImageIndex, time: 0 diff --git a/lib/web/mage/translate.js b/lib/web/mage/translate.js index 65ec33266aebd..8c5c218b2498f 100644 --- a/lib/web/mage/translate.js +++ b/lib/web/mage/translate.js @@ -6,8 +6,9 @@ define([ 'jquery', 'mage/mage', - 'mageTranslationDictionary' -], function ($, mage, dictionary) { + 'mageTranslationDictionary', + 'underscore' +], function ($, mage, dictionary, _) { 'use strict'; $.extend(true, $, { @@ -38,7 +39,7 @@ define([ * @return {String} */ translate: function (text) { - return _data[text] ? _data[text] : text; + return typeof _data[text] !== 'undefined' ? _data[text] : text; } }; }()) @@ -46,5 +47,18 @@ define([ }); $.mage.__ = $.proxy($.mage.translate.translate, $.mage.translate); + // Provide i18n wrapper to be used in underscore templates for translation + _.extend(_, { + /** + * Make a translation using $.mage.__ + * + * @param {String} text + * @return {String} + */ + i18n: function (text) { + return $.mage.__(text); + } + }); + return $.mage.__; }); diff --git a/lib/web/mage/validation.js b/lib/web/mage/validation.js index 51c23a9b28c66..10f9dab6bdd9b 100644 --- a/lib/web/mage/validation.js +++ b/lib/web/mage/validation.js @@ -6,10 +6,11 @@ define([ 'jquery', 'moment', + 'mageUtils', 'jquery-ui-modules/widget', 'jquery/validate', 'mage/translate' -], function ($, moment) { +], function ($, moment, utils) { 'use strict'; var creditCartTypes, rules, showLabel, originValidateDelegate; @@ -1032,7 +1033,7 @@ define([ ], 'validate-date': [ function (value, params, additionalParams) { - var test = moment(value, additionalParams.dateFormat); + var test = moment(value, utils.convertToMomentFormat(additionalParams.dateFormat)); return $.mage.isEmptyNoTrim(value) || test.isValid(); }, @@ -1129,7 +1130,7 @@ define([ ], 'validate-state': [ function (v) { - return v !== 0 || v === ''; + return v !== 0; }, $.mage.__('Please select State/Province.') ], @@ -1970,7 +1971,7 @@ define([ } if (firstActive.length) { - $('body').stop().animate({ + $('html, body').stop().animate({ scrollTop: firstActive.offset().top - windowHeight / 2 }); firstActive.focus(); diff --git a/lib/web/requirejs/domReady.js b/lib/web/requirejs/domReady.js index 31bd0d77697ca..099c6f7b79e15 100644 --- a/lib/web/requirejs/domReady.js +++ b/lib/web/requirejs/domReady.js @@ -78,7 +78,7 @@ define(function () { } } - //Check if document already complete, and if so, just trigger page load + //Check if document is no longer loading, and if so, just trigger page load //listeners. Latest webkit browsers also use "interactive", and //will fire the onDOMContentLoaded before "interactive" but not after //entering "interactive" or "complete". More details: @@ -89,8 +89,9 @@ define(function () { //so removing the || document.readyState === "interactive" test. //There is still a window.onload binding that should get fired if //DOMContentLoaded is missed. - if (document.readyState === "complete") { - pageLoaded(); + if (document.readyState !== "loading") { + // Handle it asynchronously to allow scripts the opportunity to delay ready + setTimeout(pageLoaded); } } @@ -126,4 +127,4 @@ define(function () { /** END OF PUBLIC API **/ return domReady; -}); \ No newline at end of file +}); diff --git a/phpserver/README.md b/phpserver/README.md index e68856fc747fd..a7466557310ac 100644 --- a/phpserver/README.md +++ b/phpserver/README.md @@ -27,7 +27,7 @@ php bin/magento setup:install --base-url=http://127.0.0.1:8082 Notes: - By default, Magento creates a random Admin URI for you. Make sure to write this value down because it's how you access the Magento Admin later. For example : ```http://127.0.0.1:8082/index.php/admin_1vpn01```. -For more informations about the installation process using the CLI, you can consult the dedicated documentation that can found in [the developer documentation](https://github.com/magento/devdocs/blob/develop/guides/v2.0/install-gde/install/cli/install-cli-install.md). +For more information about the installation process using the CLI, you can consult the dedicated documentation that can found in [the developer documentation](https://devdocs.magento.com/guides/v2.3/install-gde/install/cli/install-cli-subcommands.html). ### How to run Magento diff --git a/pub/errors/default/report.phtml b/pub/errors/default/report.phtml index 546b96605ec1b..ecf43d57550ed 100644 --- a/pub/errors/default/report.phtml +++ b/pub/errors/default/report.phtml @@ -49,7 +49,7 @@
- +
diff --git a/pub/health_check.php b/pub/health_check.php index 4bdda68851bde..8642877323bbc 100644 --- a/pub/health_check.php +++ b/pub/health_check.php @@ -63,8 +63,10 @@ } $cacheBackendClass = $cacheConfig[ConfigOptionsListConstants::CONFIG_PATH_BACKEND]; try { + /** @var \Magento\Framework\App\Cache\Frontend\Factory $cacheFrontendFactory */ + $cacheFrontendFactory = $objectManager->get(Magento\Framework\App\Cache\Frontend\Factory::class); /** @var \Zend_Cache_Backend_Interface $backend */ - $backend = new $cacheBackendClass($cacheConfig[ConfigOptionsListConstants::CONFIG_PATH_BACKEND_OPTIONS]); + $backend = $cacheFrontendFactory->create($cacheConfig); $backend->test('test_cache_id'); } catch (\Exception $e) { http_response_code(500); diff --git a/setup/config/application.config.php b/setup/config/application.config.php index e7efd12023df4..a293e20219b27 100644 --- a/setup/config/application.config.php +++ b/setup/config/application.config.php @@ -5,8 +5,8 @@ */ use Magento\Setup\Mvc\Bootstrap\InitParamListener; -use Zend\Mvc\Service\DiAbstractServiceFactoryFactory; -use Zend\ServiceManager\Di\DiAbstractServiceFactory; +use Laminas\Mvc\Service\DiAbstractServiceFactoryFactory; +use Laminas\ServiceManager\Di\DiAbstractServiceFactory; return [ 'modules' => [ diff --git a/setup/config/di.config.php b/setup/config/di.config.php index fbdebe7a96e18..b9f2ebe2fa4e0 100644 --- a/setup/config/di.config.php +++ b/setup/config/di.config.php @@ -8,8 +8,8 @@ 'di' => [ 'instance' => [ 'preference' => [ - \Zend\EventManager\EventManagerInterface::class => 'EventManager', - \Zend\ServiceManager\ServiceLocatorInterface::class => \Zend\ServiceManager\ServiceManager::class, + \Laminas\EventManager\EventManagerInterface::class => 'EventManager', + \Laminas\ServiceManager\ServiceLocatorInterface::class => \Laminas\ServiceManager\ServiceManager::class, \Magento\Framework\DB\LoggerInterface::class => \Magento\Framework\DB\Logger\Quiet::class, \Magento\Framework\Locale\ConfigInterface::class => \Magento\Framework\Locale\Config::class, \Magento\Framework\Filesystem\DriverInterface::class => diff --git a/setup/performance-toolkit/benchmark.jmx b/setup/performance-toolkit/benchmark.jmx index 7db2b26e3c621..fef19b05deafd 100644 --- a/setup/performance-toolkit/benchmark.jmx +++ b/setup/performance-toolkit/benchmark.jmx @@ -40947,6 +40947,250 @@ if(response.data.categoryList.length !== 4){ + + 1 + false + 1 + ${graphqlGetCategoryListByCategoryIdPercentage} + mpaf/tool/fragments/_system/scenario_controller_tmpl.jmx + + + +var testLabel = "${testLabel}" ? " (${testLabel})" : ""; +if (testLabel + && sampler.getClass().getName() == 'org.apache.jmeter.protocol.http.sampler.HTTPSamplerProxy' +) { + if (sampler.getName().indexOf(testLabel) == -1) { + sampler.setName(sampler.getName() + testLabel); + } +} else if (sampler.getName().indexOf("SetUp - ") == -1) { + sampler.setName("SetUp - " + sampler.getName()); +} + + javascript + mpaf/tool/fragments/_system/setup_label.jmx + + + + vars.put("testLabel", "GraphQL Categories Query: Get Multiple Categories By Id"); + + true + + + + + + + Content-Type + application/json + + + Accept + */* + + + mpaf/tool/fragments/ce/api/header_manager_before_token.jmx + + + + mpaf/tool/fragments/ce/common/init_random_generator_setup.jmx + +import java.util.Random; + +Random random = new Random(); +if (${seedForRandom} > 0) { + random.setSeed(${seedForRandom} + ${__threadNum}); +} + +vars.putObject("randomIntGenerator", random); + + + + true + + + + + javascript + + + + random = vars.getObject("randomIntGenerator"); + +var categories = props.get("categories"); + +var numbers = []; + +var sanity = 0; +for(var i = 0; i < 4; i++){ + sanity++; + if(sanity > 100){ + break; + } + var number = random.nextInt(categories.length) + if(numbers.indexOf(number) >= 0){ + i--; + continue; + } + numbers.push(number); +} + +vars.put("category_id_1", categories[numbers[0]].id); +vars.put("category_id_2", categories[numbers[1]].id); +vars.put("category_id_3", categories[numbers[2]].id); +vars.put("category_id_4", categories[numbers[3]].id); + + mpaf/tool/fragments/ce/common/extract_multiple_categories_setup.jmx + + + + + true + + + + false + {"query" : "{\n categories(filters:{ids: {in: [\"${category_id_1}\", \"${category_id_2}\", \"${category_id_3}\", \"${category_id_4}\"]}}) {\n total_count\n page_info {\n total_pages\n current_page\n page_size\n }\n items{\n id\n children {\n id\n name\n url_key\n url_path\n children_count\n path\n image\n productImagePreview: products(pageSize: 1, sort: {name: ASC}) {\n items {\n small_image {\n label\n url\n }\n }\n }\n }\n }\n }\n}"} + = + + + + + ${graphql_port_number} + 60000 + 200000 + ${request_protocol} + + ${base_path}graphql + POST + true + false + true + false + false + + mpaf/tool/fragments/ce/graphql/categories_query_get_multiple_categories_by_id.jmx + + + + javascript + + + + var response = JSON.parse(prev.getResponseDataAsString()); + +if(response.data == undefined || response.data.categories == undefined){ + AssertionResult.setFailureMessage("Categories result is empty."); + AssertionResult.setFailure(true); +} + +if(response.data.categories.items.length !== 4){ + AssertionResult.setFailureMessage("Categories query expected to find 4 categories. " + response.data.categories.items.length + " returned."); + AssertionResult.setFailure(true); +} + + + + + + + + + 1 + false + 1 + ${graphqlGetCategoryListByCategoryIdPercentage} + mpaf/tool/fragments/_system/scenario_controller_tmpl.jmx + + + +var testLabel = "${testLabel}" ? " (${testLabel})" : ""; +if (testLabel + && sampler.getClass().getName() == 'org.apache.jmeter.protocol.http.sampler.HTTPSamplerProxy' +) { + if (sampler.getName().indexOf(testLabel) == -1) { + sampler.setName(sampler.getName() + testLabel); + } +} else if (sampler.getName().indexOf("SetUp - ") == -1) { + sampler.setName("SetUp - " + sampler.getName()); +} + + javascript + mpaf/tool/fragments/_system/setup_label.jmx + + + + vars.put("testLabel", "GraphQL Categories Query: Get Many Categories with Pagination"); + + true + + + + + + + Content-Type + application/json + + + Accept + */* + + + mpaf/tool/fragments/ce/api/header_manager_before_token.jmx + + + + true + + + + false + {"query" : "{\n categories(filters:{name: {match: \"Category\"}}) {\n total_count\n page_info {\n total_pages\n current_page\n page_size\n }\n items{\n id\n children {\n id\n name\n url_key\n url_path\n children_count\n path\n image\n productImagePreview: products(pageSize: 1, sort: {name: ASC}) {\n items {\n small_image {\n label\n url\n }\n }\n }\n }\n }\n }\n}"} + = + + + + + ${graphql_port_number} + 60000 + 200000 + ${request_protocol} + + ${base_path}graphql + POST + true + false + true + false + false + + mpaf/tool/fragments/ce/graphql/categories_query_get_many_categories_by_name_match.jmx + + + + javascript + + + + var response = JSON.parse(prev.getResponseDataAsString()); + +if(response.data == undefined || response.data.categories == undefined){ + AssertionResult.setFailureMessage("Categories result is empty."); + AssertionResult.setFailure(true); +} + +if(response.data.categories.items.length != 20){ + AssertionResult.setFailureMessage("Categories query expected to find 20 categories. " + response.data.categories.items.length + " returned."); + AssertionResult.setFailure(true); +} + + + + + + + 1 false diff --git a/setup/src/Magento/Setup/Application.php b/setup/src/Magento/Setup/Application.php index 4366727e080c8..5881bfad3b209 100644 --- a/setup/src/Magento/Setup/Application.php +++ b/setup/src/Magento/Setup/Application.php @@ -5,23 +5,25 @@ */ namespace Magento\Setup; -use Zend\Mvc\Application as ZendApplication; -use Zend\Mvc\Service\ServiceManagerConfig; -use Zend\ServiceManager\ServiceManager; +use Laminas\Mvc\Application as LaminasApplication; +use Laminas\Mvc\Service\ServiceManagerConfig; +use Laminas\ServiceManager\ServiceManager; /** - * This class is wrapper on \Zend\Mvc\Application and allows to do more customization like services loading, which + * This class is wrapper on \Laminas\Mvc\Application + * + * It allows to do more customization like services loading, which * cannot be loaded via configuration. */ class Application { /** - * Creates \Zend\Mvc\Application and bootstrap it. - * This method is similar to \Zend\Mvc\Application::init but allows to load + * Creates \Laminas\Mvc\Application and bootstrap it. + * This method is similar to \Laminas\Mvc\Application::init but allows to load * Magento specific services. * * @param array $configuration - * @return ZendApplication + * @return LaminasApplication */ public function bootstrap(array $configuration) { @@ -40,7 +42,7 @@ public function bootstrap(array $configuration) } $listeners = $this->getListeners($serviceManager, $configuration); - $application = new ZendApplication( + $application = new LaminasApplication( $configuration, $serviceManager, $serviceManager->get('EventManager'), @@ -52,8 +54,8 @@ public function bootstrap(array $configuration) } /** - * Uses \Zend\ServiceManager\ServiceManager::get method to load different kind of services. - * Some services cannot be loaded via configuration like \Zend\ServiceManager\Di\DiAbstractServiceFactory and + * Uses \Laminas\ServiceManager\ServiceManager::get method to load different kind of services. + * Some services cannot be loaded via configuration like \Laminas\ServiceManager\Di\DiAbstractServiceFactory and * should be initialized via corresponding factory. * * @param ServiceManager $serviceManager diff --git a/setup/src/Magento/Setup/Console/Command/DiCompileCommand.php b/setup/src/Magento/Setup/Console/Command/DiCompileCommand.php index 014d699cb239d..56a4a85b17d99 100644 --- a/setup/src/Magento/Setup/Console/Command/DiCompileCommand.php +++ b/setup/src/Magento/Setup/Console/Command/DiCompileCommand.php @@ -260,9 +260,12 @@ private function getExcludedModulePaths(array $modulePaths) */ private function getExcludedLibraryPaths(array $libraryPaths) { - $libraryPaths = array_map(function ($libraryPath) { - return preg_quote($libraryPath, '#'); - }, $libraryPaths); + $libraryPaths = array_map( + function ($libraryPath) { + return preg_quote($libraryPath, '#'); + }, + $libraryPaths + ); $excludedLibraryPaths = [ '#^(?:' . join('|', $libraryPaths) . ')/([\\w]+/)?Test#', @@ -395,7 +398,8 @@ private function getOperationsConfiguration( $compiledPathsList['application'], $compiledPathsList['library'], $compiledPathsList['generated_helpers'], - ] + ], + OperationFactory::APPLICATION_ACTION_LIST_GENERATOR => [], ]; return $operations; diff --git a/setup/src/Magento/Setup/Console/CommandList.php b/setup/src/Magento/Setup/Console/CommandList.php index f0dad6f4a7452..338330ef91599 100644 --- a/setup/src/Magento/Setup/Console/CommandList.php +++ b/setup/src/Magento/Setup/Console/CommandList.php @@ -7,7 +7,7 @@ namespace Magento\Setup\Console; use Magento\Setup\Console\Command\TablesWhitelistGenerateCommand; -use Zend\ServiceManager\ServiceManager; +use Laminas\ServiceManager\ServiceManager; /** * Class CommandList contains predefined list of commands for Setup. diff --git a/setup/src/Magento/Setup/Console/CompilerPreparation.php b/setup/src/Magento/Setup/Console/CompilerPreparation.php index c39c721b61716..c83aa48636393 100644 --- a/setup/src/Magento/Setup/Console/CompilerPreparation.php +++ b/setup/src/Magento/Setup/Console/CompilerPreparation.php @@ -15,7 +15,7 @@ use Magento\Setup\Console\Command\DiCompileCommand; use Magento\Setup\Mvc\Bootstrap\InitParamListener; use Symfony\Component\Console\Input\ArgvInput; -use Zend\ServiceManager\ServiceManager; +use Laminas\ServiceManager\ServiceManager; /** * Class prepares folders for code generation diff --git a/setup/src/Magento/Setup/Controller/AddDatabase.php b/setup/src/Magento/Setup/Controller/AddDatabase.php index 8d36eece58e22..4d12ea6a945d6 100644 --- a/setup/src/Magento/Setup/Controller/AddDatabase.php +++ b/setup/src/Magento/Setup/Controller/AddDatabase.php @@ -5,12 +5,17 @@ */ namespace Magento\Setup\Controller; -use Zend\Mvc\Controller\AbstractActionController; -use Zend\View\Model\ViewModel; +use Laminas\Mvc\Controller\AbstractActionController; +use Laminas\View\Model\ViewModel; +/** + * AddDatabase controller + */ class AddDatabase extends AbstractActionController { /** + * Index action + * * @return array|ViewModel */ public function indexAction() diff --git a/setup/src/Magento/Setup/Controller/BackupActionItems.php b/setup/src/Magento/Setup/Controller/BackupActionItems.php index a00492e001f8c..a9255d3324374 100644 --- a/setup/src/Magento/Setup/Controller/BackupActionItems.php +++ b/setup/src/Magento/Setup/Controller/BackupActionItems.php @@ -3,57 +3,65 @@ * Copyright © Magento, Inc. All rights reserved. * See COPYING.txt for license details. */ + namespace Magento\Setup\Controller; +use Laminas\Http\Response; +use Laminas\Json\Json; +use Laminas\Mvc\Controller\AbstractActionController; +use Laminas\View\Model\JsonModel; +use Laminas\View\Model\ViewModel; use Magento\Framework\App\Filesystem\DirectoryList; use Magento\Framework\Backup\Factory; use Magento\Framework\Backup\Filesystem; +use Magento\Framework\Exception\FileSystemException; use Magento\Framework\Setup\BackupRollback; -use Zend\Json\Json; -use Zend\Mvc\Controller\AbstractActionController; -use Zend\View\Model\JsonModel; +use Magento\Setup\Model\ObjectManagerProvider; +use Magento\Setup\Model\WebLogger; +/** + * BackupActionItems controller + * @SuppressWarnings(PHPMD.CouplingBetweenObjects) + */ class BackupActionItems extends AbstractActionController { - /** * Handler for BackupRollback * - * @var \Magento\Framework\Setup\BackupRollback + * @var BackupRollback */ private $backupHandler; /** * Filesystem * - * @var \Magento\Framework\Backup\Filesystem + * @var Filesystem */ private $fileSystem; /** * Filesystem Directory List * - * @var \Magento\Framework\App\Filesystem\DirectoryList + * @var DirectoryList */ private $directoryList; /** - * Constructor - * - * @param \Magento\Setup\Model\ObjectManagerProvider $objectManagerProvider - * @param \Magento\Setup\Model\WebLogger $logger - * @param \Magento\Framework\App\Filesystem\DirectoryList $directoryList - * @param \Magento\Framework\Backup\Filesystem $fileSystem + * @param ObjectManagerProvider $objectManagerProvider + * @param WebLogger $logger + * @param DirectoryList $directoryList + * @param Filesystem $fileSystem + * @throws \Magento\Setup\Exception */ public function __construct( - \Magento\Setup\Model\ObjectManagerProvider $objectManagerProvider, - \Magento\Setup\Model\WebLogger $logger, - \Magento\Framework\App\Filesystem\DirectoryList $directoryList, - \Magento\Framework\Backup\Filesystem $fileSystem + ObjectManagerProvider $objectManagerProvider, + WebLogger $logger, + DirectoryList $directoryList, + Filesystem $fileSystem ) { $objectManager = $objectManagerProvider->get(); $this->backupHandler = $objectManager->create( - \Magento\Framework\Setup\BackupRollback::class, + BackupRollback::class, ['log' => $logger] ); $this->directoryList = $directoryList; @@ -63,20 +71,22 @@ public function __construct( /** * No index action, return 404 error page * - * @return \Zend\View\Model\ViewModel + * @return ViewModel */ public function indexAction() { - $view = new \Zend\View\Model\ViewModel; + $view = new ViewModel(); $view->setTemplate('/error/404.phtml'); - $this->getResponse()->setStatusCode(\Zend\Http\Response::STATUS_CODE_404); + $this->getResponse()->setStatusCode(Response::STATUS_CODE_404); + return $view; } /** * Checks disk space availability * - * @return \Zend\View\Model\JsonModel + * @return JsonModel + * @throws FileSystemException */ public function checkAction() { @@ -95,6 +105,7 @@ public function checkAction() $totalSize += $this->backupHandler->getDBDiskSpace(); } $this->fileSystem->validateAvailableDiscSpace($backupDir, $totalSize); + return new JsonModel( [ 'responseType' => ResponseTypeInterface::RESPONSE_TYPE_SUCCESS, @@ -114,7 +125,7 @@ public function checkAction() /** * Takes backup for code, media or DB * - * @return \Zend\View\Model\JsonModel + * @return JsonModel */ public function createAction() { @@ -131,6 +142,7 @@ public function createAction() if (isset($params['options']['db']) && $params['options']['db']) { $backupFiles[] = $this->backupHandler->dbBackup($time); } + return new JsonModel( [ 'responseType' => ResponseTypeInterface::RESPONSE_TYPE_SUCCESS, diff --git a/setup/src/Magento/Setup/Controller/CompleteBackup.php b/setup/src/Magento/Setup/Controller/CompleteBackup.php index e0e45a208cdf5..9f925d405cc41 100644 --- a/setup/src/Magento/Setup/Controller/CompleteBackup.php +++ b/setup/src/Magento/Setup/Controller/CompleteBackup.php @@ -5,25 +5,30 @@ */ namespace Magento\Setup\Controller; -use Magento\Framework\App\MaintenanceMode; -use Zend\Mvc\Controller\AbstractActionController; -use Zend\View\Model\JsonModel; -use Zend\View\Model\ViewModel; +use Laminas\Mvc\Controller\AbstractActionController; +use Laminas\View\Model\ViewModel; +/** + * CompleteBackup controller + */ class CompleteBackup extends AbstractActionController { /** + * Index action + * * @return array|ViewModel */ public function indexAction() { $view = new ViewModel; $view->setTemplate('/error/404.phtml'); - $this->getResponse()->setStatusCode(\Zend\Http\Response::STATUS_CODE_404); + $this->getResponse()->setStatusCode(\Laminas\Http\Response::STATUS_CODE_404); return $view; } /** + * Progress action + * * @return array|ViewModel */ public function progressAction() diff --git a/setup/src/Magento/Setup/Controller/CreateAdminAccount.php b/setup/src/Magento/Setup/Controller/CreateAdminAccount.php index 9c20312b23dca..79c32c5b932e0 100644 --- a/setup/src/Magento/Setup/Controller/CreateAdminAccount.php +++ b/setup/src/Magento/Setup/Controller/CreateAdminAccount.php @@ -5,12 +5,17 @@ */ namespace Magento\Setup\Controller; -use Zend\Mvc\Controller\AbstractActionController; -use Zend\View\Model\ViewModel; +use Laminas\Mvc\Controller\AbstractActionController; +use Laminas\View\Model\ViewModel; +/** + * CreateAdminAccount controller + */ class CreateAdminAccount extends AbstractActionController { /** + * Index action + * * @return ViewModel */ public function indexAction() diff --git a/setup/src/Magento/Setup/Controller/CreateBackup.php b/setup/src/Magento/Setup/Controller/CreateBackup.php index 9987cfd13bcf4..42c86c42a5a15 100644 --- a/setup/src/Magento/Setup/Controller/CreateBackup.php +++ b/setup/src/Magento/Setup/Controller/CreateBackup.php @@ -5,12 +5,17 @@ */ namespace Magento\Setup\Controller; -use Zend\Mvc\Controller\AbstractActionController; -use Zend\View\Model\ViewModel; +use Laminas\Mvc\Controller\AbstractActionController; +use Laminas\View\Model\ViewModel; +/** + * CreateBackup controller + */ class CreateBackup extends AbstractActionController { /** + * Index action + * * @return array|ViewModel */ public function indexAction() diff --git a/setup/src/Magento/Setup/Controller/CustomizeYourStore.php b/setup/src/Magento/Setup/Controller/CustomizeYourStore.php index 83c96ed9d43ef..128e4b42e6d5e 100644 --- a/setup/src/Magento/Setup/Controller/CustomizeYourStore.php +++ b/setup/src/Magento/Setup/Controller/CustomizeYourStore.php @@ -5,14 +5,16 @@ */ namespace Magento\Setup\Controller; -use Magento\Framework\Filesystem; use Magento\Framework\Module\FullModuleList; use Magento\Framework\Setup\Lists; use Magento\Setup\Model\ObjectManagerProvider; -use Zend\Mvc\Controller\AbstractActionController; -use Zend\View\Model\ViewModel; -use Zend\View\Model\JsonModel; +use Laminas\Mvc\Controller\AbstractActionController; +use Laminas\View\Model\ViewModel; +use Laminas\View\Model\JsonModel; +/** + * CustomizeYourStore controller + */ class CustomizeYourStore extends AbstractActionController { /** @@ -43,7 +45,10 @@ public function __construct(FullModuleList $moduleList, Lists $list, ObjectManag } /** + * Index action + * * @return ViewModel + * @throws \Magento\Setup\Exception */ public function indexAction() { @@ -76,6 +81,7 @@ public function indexAction() */ public function defaultTimeZoneAction() { + // phpcs:ignore Generic.PHP.NoSilencedErrors $defaultTimeZone = trim(@date_default_timezone_get()); if (empty($defaultTimeZone)) { return new JsonModel(['defaultTimeZone' => 'UTC']); diff --git a/setup/src/Magento/Setup/Controller/DataOption.php b/setup/src/Magento/Setup/Controller/DataOption.php index 88ebf291016d9..549358f6e3cf2 100644 --- a/setup/src/Magento/Setup/Controller/DataOption.php +++ b/setup/src/Magento/Setup/Controller/DataOption.php @@ -7,10 +7,10 @@ namespace Magento\Setup\Controller; use Magento\Setup\Model\UninstallCollector; -use Zend\Json\Json; -use Zend\Mvc\Controller\AbstractActionController; -use Zend\View\Model\JsonModel; -use Zend\View\Model\ViewModel; +use Laminas\Json\Json; +use Laminas\Mvc\Controller\AbstractActionController; +use Laminas\View\Model\JsonModel; +use Laminas\View\Model\ViewModel; /** * Controller of data option selection @@ -35,7 +35,7 @@ public function __construct(UninstallCollector $uninstallCollector) /** * Shows data option page * - * @return ViewModel|\Zend\Http\Response + * @return ViewModel|\Laminas\Http\Response */ public function indexAction() { diff --git a/setup/src/Magento/Setup/Controller/DatabaseCheck.php b/setup/src/Magento/Setup/Controller/DatabaseCheck.php index 4b88a8732d2c7..f84b6e680ab25 100644 --- a/setup/src/Magento/Setup/Controller/DatabaseCheck.php +++ b/setup/src/Magento/Setup/Controller/DatabaseCheck.php @@ -7,12 +7,12 @@ use Magento\Framework\Config\ConfigOptionsListConstants; use Magento\Setup\Validator\DbValidator; -use Zend\Json\Json; -use Zend\Mvc\Controller\AbstractActionController; -use Zend\View\Model\JsonModel; +use Laminas\Json\Json; +use Laminas\Mvc\Controller\AbstractActionController; +use Laminas\View\Model\JsonModel; /** - * Class DatabaseCheck + * DatabaseCheck controller */ class DatabaseCheck extends AbstractActionController { diff --git a/setup/src/Magento/Setup/Controller/DependencyCheck.php b/setup/src/Magento/Setup/Controller/DependencyCheck.php index e0e1050fa628d..49c2b661a8681 100644 --- a/setup/src/Magento/Setup/Controller/DependencyCheck.php +++ b/setup/src/Magento/Setup/Controller/DependencyCheck.php @@ -6,18 +6,18 @@ namespace Magento\Setup\Controller; +use Magento\Framework\Exception\LocalizedException; use Magento\Framework\Module\Status; +use Magento\Framework\Phrase; use Magento\Setup\Model\DependencyReadinessCheck; use Magento\Setup\Model\ModuleStatusFactory; use Magento\Setup\Model\UninstallDependencyCheck; -use Zend\Json\Json; -use Zend\Mvc\Controller\AbstractActionController; -use Zend\View\Model\JsonModel; +use Laminas\Json\Json; +use Laminas\Mvc\Controller\AbstractActionController; +use Laminas\View\Model\JsonModel; /** - * Class DependencyCheck - * - * Checks dependencies. + * DependencyCheck controller */ class DependencyCheck extends AbstractActionController { @@ -43,8 +43,6 @@ class DependencyCheck extends AbstractActionController protected $moduleStatus; /** - * Constructor - * * @param DependencyReadinessCheck $dependencyReadinessCheck * @param UninstallDependencyCheck $uninstallDependencyCheck * @param ModuleStatusFactory $moduleStatusFactory @@ -63,6 +61,7 @@ public function __construct( * Verifies component dependency * * @return JsonModel + * @throws \Exception */ public function componentDependencyAction() { @@ -119,11 +118,11 @@ public function enableDisableDependencyCheckAction() try { if (empty($data['packages'])) { - throw new \Exception('No packages have been found.'); + throw new LocalizedException(new Phrase('No packages have been found.')); } if (empty($data['type'])) { - throw new \Exception('Can not determine the flow.'); + throw new LocalizedException(new Phrase('Can not determine the flow.')); } $modules = $data['packages']; @@ -133,7 +132,7 @@ public function enableDisableDependencyCheckAction() $modulesToChange = []; foreach ($modules as $module) { if (!isset($module['name'])) { - throw new \Exception('Can not find module name.'); + throw new LocalizedException(new Phrase('Can not find module name.')); } $modulesToChange[] = $module['name']; } diff --git a/setup/src/Magento/Setup/Controller/Environment.php b/setup/src/Magento/Setup/Controller/Environment.php index c6c7073e02951..063d23f5a48d6 100644 --- a/setup/src/Magento/Setup/Controller/Environment.php +++ b/setup/src/Magento/Setup/Controller/Environment.php @@ -8,7 +8,7 @@ use Magento\Framework\App\Filesystem\DirectoryList; use Magento\Framework\Filesystem; use Magento\Setup\Model\Cron\ReadinessCheck; -use Zend\Mvc\Controller\AbstractActionController; +use Laminas\Mvc\Controller\AbstractActionController; /** * Class Environment @@ -43,6 +43,11 @@ class Environment extends AbstractActionController */ protected $phpReadinessCheck; + /** + * @var \Magento\Framework\Setup\FilePermissions + */ + private $permissions; + /** * Constructor * @@ -66,20 +71,20 @@ public function __construct( /** * No index action, return 404 error page * - * @return \Zend\View\Model\JsonModel + * @return \Laminas\View\Model\JsonModel */ public function indexAction() { - $view = new \Zend\View\Model\JsonModel([]); + $view = new \Laminas\View\Model\JsonModel([]); $view->setTemplate('/error/404.phtml'); - $this->getResponse()->setStatusCode(\Zend\Http\Response::STATUS_CODE_404); + $this->getResponse()->setStatusCode(\Laminas\Http\Response::STATUS_CODE_404); return $view; } /** * Verifies php version * - * @return \Zend\View\Model\JsonModel + * @return \Laminas\View\Model\JsonModel */ public function phpVersionAction() { @@ -91,13 +96,13 @@ public function phpVersionAction() } elseif ($type == ReadinessCheckUpdater::UPDATER) { $data = $this->getPhpChecksInfo(ReadinessCheck::KEY_PHP_VERSION_VERIFIED); } - return new \Zend\View\Model\JsonModel($data); + return new \Laminas\View\Model\JsonModel($data); } /** * Checks PHP settings * - * @return \Zend\View\Model\JsonModel + * @return \Laminas\View\Model\JsonModel */ public function phpSettingsAction() { @@ -109,13 +114,13 @@ public function phpSettingsAction() } elseif ($type == ReadinessCheckUpdater::UPDATER) { $data = $this->getPhpChecksInfo(ReadinessCheck::KEY_PHP_SETTINGS_VERIFIED); } - return new \Zend\View\Model\JsonModel($data); + return new \Laminas\View\Model\JsonModel($data); } /** * Verifies php verifications * - * @return \Zend\View\Model\JsonModel + * @return \Laminas\View\Model\JsonModel */ public function phpExtensionsAction() { @@ -127,7 +132,7 @@ public function phpExtensionsAction() } elseif ($type == ReadinessCheckUpdater::UPDATER) { $data = $this->getPhpChecksInfo(ReadinessCheck::KEY_PHP_EXTENSIONS_VERIFIED); } - return new \Zend\View\Model\JsonModel($data); + return new \Laminas\View\Model\JsonModel($data); } /** @@ -155,7 +160,7 @@ private function getPhpChecksInfo($type) /** * Verifies file permissions * - * @return \Zend\View\Model\JsonModel + * @return \Laminas\View\Model\JsonModel */ public function filePermissionsAction() { @@ -183,13 +188,13 @@ public function filePermissionsAction() ], ]; - return new \Zend\View\Model\JsonModel($data); + return new \Laminas\View\Model\JsonModel($data); } /** * Verifies updater application exists * - * @return \Zend\View\Model\JsonModel + * @return \Laminas\View\Model\JsonModel */ public function updaterApplicationAction() { @@ -201,13 +206,13 @@ public function updaterApplicationAction() $data = [ 'responseType' => $responseType ]; - return new \Zend\View\Model\JsonModel($data); + return new \Laminas\View\Model\JsonModel($data); } /** * Verifies Setup and Updater Cron status * - * @return \Zend\View\Model\JsonModel + * @return \Laminas\View\Model\JsonModel */ public function cronScriptAction() { @@ -232,6 +237,6 @@ public function cronScriptAction() $updaterCheck['notice']; } $data['responseType'] = $responseType; - return new \Zend\View\Model\JsonModel($data); + return new \Laminas\View\Model\JsonModel($data); } } diff --git a/setup/src/Magento/Setup/Controller/ExtensionGrid.php b/setup/src/Magento/Setup/Controller/ExtensionGrid.php index 48c63eafcf140..cbe9340ffd8f8 100644 --- a/setup/src/Magento/Setup/Controller/ExtensionGrid.php +++ b/setup/src/Magento/Setup/Controller/ExtensionGrid.php @@ -7,9 +7,9 @@ use Magento\Setup\Model\PackagesAuth; use Magento\Setup\Model\PackagesData; -use Zend\Mvc\Controller\AbstractActionController; -use Zend\View\Model\JsonModel; -use Zend\View\Model\ViewModel; +use Laminas\Mvc\Controller\AbstractActionController; +use Laminas\View\Model\JsonModel; +use Laminas\View\Model\ViewModel; use Magento\Setup\Model\Grid; /** @@ -50,7 +50,7 @@ public function __construct( /** * Index page action * - * @return \Zend\View\Model\ViewModel + * @return \Laminas\View\Model\ViewModel */ public function indexAction() { diff --git a/setup/src/Magento/Setup/Controller/Home.php b/setup/src/Magento/Setup/Controller/Home.php index 4b0f4ef7917bd..f06cfb89cb1d3 100644 --- a/setup/src/Magento/Setup/Controller/Home.php +++ b/setup/src/Magento/Setup/Controller/Home.php @@ -6,8 +6,8 @@ namespace Magento\Setup\Controller; -use Zend\Mvc\Controller\AbstractActionController; -use Zend\View\Model\ViewModel; +use Laminas\Mvc\Controller\AbstractActionController; +use Laminas\View\Model\ViewModel; /** * Controller of homepage of setup @@ -15,7 +15,9 @@ class Home extends AbstractActionController { /** - * @return ViewModel|\Zend\Http\Response + * Index action + * + * @return ViewModel|\Laminas\Http\Response */ public function indexAction() { diff --git a/setup/src/Magento/Setup/Controller/Index.php b/setup/src/Magento/Setup/Controller/Index.php index ea2fadd94f65e..36dd60dbbcf0f 100644 --- a/setup/src/Magento/Setup/Controller/Index.php +++ b/setup/src/Magento/Setup/Controller/Index.php @@ -6,8 +6,8 @@ namespace Magento\Setup\Controller; -use Zend\Mvc\Controller\AbstractActionController; -use Zend\View\Model\ViewModel; +use Laminas\Mvc\Controller\AbstractActionController; +use Laminas\View\Model\ViewModel; /** * Main controller of the Setup Wizard @@ -15,7 +15,9 @@ class Index extends AbstractActionController { /** - * @return ViewModel|\Zend\Http\Response + * Index action + * + * @return ViewModel|\Laminas\Http\Response */ public function indexAction() { diff --git a/setup/src/Magento/Setup/Controller/Install.php b/setup/src/Magento/Setup/Controller/Install.php index ceb37e7d7b6e0..f110595a8b872 100644 --- a/setup/src/Magento/Setup/Controller/Install.php +++ b/setup/src/Magento/Setup/Controller/Install.php @@ -6,18 +6,17 @@ namespace Magento\Setup\Controller; +use Laminas\Json\Json; +use Laminas\Mvc\Controller\AbstractActionController; +use Laminas\View\Model\JsonModel; +use Laminas\View\Model\ViewModel; use Magento\Framework\App\DeploymentConfig; use Magento\Framework\Config\ConfigOptionsListConstants as SetupConfigOptionsList; -use Magento\SampleData; use Magento\Setup\Model\Installer; use Magento\Setup\Model\Installer\ProgressFactory; use Magento\Setup\Model\InstallerFactory; use Magento\Setup\Model\RequestDataConverter; use Magento\Setup\Model\WebLogger; -use Zend\Json\Json; -use Zend\Mvc\Controller\AbstractActionController; -use Zend\View\Model\JsonModel; -use Zend\View\Model\ViewModel; /** * Install controller @@ -57,8 +56,6 @@ class Install extends AbstractActionController private $requestDataConverter; /** - * Default Constructor - * * @param WebLogger $logger * @param InstallerFactory $installerFactory * @param ProgressFactory $progressFactory @@ -83,12 +80,15 @@ public function __construct( } /** + * Index action + * * @return ViewModel */ public function indexAction() { - $view = new ViewModel; + $view = new ViewModel(); $view->setTerminal(true); + return $view; } @@ -100,7 +100,7 @@ public function indexAction() public function startAction() { $this->log->clear(); - $json = new JsonModel; + $json = new JsonModel(); try { $this->checkForPriorInstall(); $content = $this->getRequest()->getContent(); @@ -121,6 +121,7 @@ public function startAction() $json->setVariable('messages', $e->getMessage()); $json->setVariable('success', false); } + return $json; } @@ -155,6 +156,7 @@ public function progressAction() } catch (\Exception $e) { $contents = [(string)$e]; } + return $json->setVariables(['progress' => $percent, 'success' => $success, 'console' => $contents]); } diff --git a/setup/src/Magento/Setup/Controller/InstallExtensionGrid.php b/setup/src/Magento/Setup/Controller/InstallExtensionGrid.php index 5589bb963aa3b..b6bed4f3db1f1 100644 --- a/setup/src/Magento/Setup/Controller/InstallExtensionGrid.php +++ b/setup/src/Magento/Setup/Controller/InstallExtensionGrid.php @@ -6,9 +6,9 @@ namespace Magento\Setup\Controller; -use Zend\Mvc\Controller\AbstractActionController; -use Zend\View\Model\JsonModel; -use Zend\View\Model\ViewModel; +use Laminas\Mvc\Controller\AbstractActionController; +use Laminas\View\Model\JsonModel; +use Laminas\View\Model\ViewModel; use Magento\Setup\Model\PackagesData; /** diff --git a/setup/src/Magento/Setup/Controller/LandingInstaller.php b/setup/src/Magento/Setup/Controller/LandingInstaller.php index 22eea503721be..8aee28df15137 100644 --- a/setup/src/Magento/Setup/Controller/LandingInstaller.php +++ b/setup/src/Magento/Setup/Controller/LandingInstaller.php @@ -5,8 +5,8 @@ */ namespace Magento\Setup\Controller; -use Zend\Mvc\Controller\AbstractActionController; -use Zend\View\Model\ViewModel; +use Laminas\Mvc\Controller\AbstractActionController; +use Laminas\View\Model\ViewModel; /** * Controller for Setup Landing page diff --git a/setup/src/Magento/Setup/Controller/LandingUpdater.php b/setup/src/Magento/Setup/Controller/LandingUpdater.php index 9c6ef98dc6dd1..b3728e404f8c1 100644 --- a/setup/src/Magento/Setup/Controller/LandingUpdater.php +++ b/setup/src/Magento/Setup/Controller/LandingUpdater.php @@ -5,8 +5,8 @@ */ namespace Magento\Setup\Controller; -use Zend\Mvc\Controller\AbstractActionController; -use Zend\View\Model\ViewModel; +use Laminas\Mvc\Controller\AbstractActionController; +use Laminas\View\Model\ViewModel; /** * Controller for Updater Landing page diff --git a/setup/src/Magento/Setup/Controller/License.php b/setup/src/Magento/Setup/Controller/License.php index 84caaebe6c5eb..9cf32e7b31baf 100644 --- a/setup/src/Magento/Setup/Controller/License.php +++ b/setup/src/Magento/Setup/Controller/License.php @@ -3,16 +3,15 @@ * Copyright © Magento, Inc. All rights reserved. * See COPYING.txt for license details. */ + namespace Magento\Setup\Controller; use Magento\Setup\Model\License as LicenseModel; -use Zend\Mvc\Controller\AbstractActionController; -use Zend\View\Model\ViewModel; +use Laminas\Mvc\Controller\AbstractActionController; +use Laminas\View\Model\ViewModel; /** - * Class LicenseController - * - * @package Magento\Setup\Controller + * License controller */ class License extends AbstractActionController { @@ -41,7 +40,7 @@ public function __construct(LicenseModel $license) public function indexAction() { $contents = $this->license->getContents(); - $view = new ViewModel; + $view = new ViewModel(); if ($contents === false) { $view->setTemplate('error/404'); $view->setVariable('message', 'Cannot find license file.'); diff --git a/setup/src/Magento/Setup/Controller/Maintenance.php b/setup/src/Magento/Setup/Controller/Maintenance.php index c3038c1171766..769f961f7fc0e 100644 --- a/setup/src/Magento/Setup/Controller/Maintenance.php +++ b/setup/src/Magento/Setup/Controller/Maintenance.php @@ -3,13 +3,17 @@ * Copyright © Magento, Inc. All rights reserved. * See COPYING.txt for license details. */ + namespace Magento\Setup\Controller; +use Laminas\Json\Json; +use Laminas\Mvc\Controller\AbstractActionController; +use Laminas\View\Model\JsonModel; use Magento\Framework\App\MaintenanceMode; -use Zend\Mvc\Controller\AbstractActionController; -use Zend\View\Model\JsonModel; -use Zend\Json\Json; +/** + * Maintenance controller + */ class Maintenance extends AbstractActionController { /** diff --git a/setup/src/Magento/Setup/Controller/Marketplace.php b/setup/src/Magento/Setup/Controller/Marketplace.php index 8b3f19167a8da..7746fa08aac5c 100644 --- a/setup/src/Magento/Setup/Controller/Marketplace.php +++ b/setup/src/Magento/Setup/Controller/Marketplace.php @@ -5,13 +5,16 @@ */ namespace Magento\Setup\Controller; -use Zend\Mvc\Controller\AbstractActionController; -use Zend\View\Model\ViewModel; -use Zend\Json\Json; -use Zend\View\Model\JsonModel; -use Magento\Setup\Model\PackagesData; +use Laminas\Json\Json; +use Laminas\Mvc\Controller\AbstractActionController; +use Laminas\View\Model\JsonModel; +use Laminas\View\Model\ViewModel; use Magento\Setup\Model\PackagesAuth; +use Magento\Setup\Model\PackagesData; +/** + * Marketplace controller + */ class Marketplace extends AbstractActionController { /** @@ -41,9 +44,9 @@ public function __construct(PackagesAuth $packagesAuth, PackagesData $packagesDa */ public function indexAction() { - $view = new ViewModel; + $view = new ViewModel(); $view->setTemplate('/error/404.phtml'); - $this->getResponse()->setStatusCode(\Zend\Http\Response::STATUS_CODE_404); + $this->getResponse()->setStatusCode(\Laminas\Http\Response::STATUS_CODE_404); return $view; } @@ -118,6 +121,8 @@ public function removeCredentialsAction() } /** + * Popup Auth action + * * @return array|ViewModel */ public function popupAuthAction() diff --git a/setup/src/Magento/Setup/Controller/MarketplaceCredentials.php b/setup/src/Magento/Setup/Controller/MarketplaceCredentials.php index c2329d09d6021..9541b8ef7250f 100644 --- a/setup/src/Magento/Setup/Controller/MarketplaceCredentials.php +++ b/setup/src/Magento/Setup/Controller/MarketplaceCredentials.php @@ -5,17 +5,22 @@ */ namespace Magento\Setup\Controller; -use Zend\Mvc\Controller\AbstractActionController; -use Zend\View\Model\ViewModel; +use Laminas\Mvc\Controller\AbstractActionController; +use Laminas\View\Model\ViewModel; +/** + * MarketplaceCredentials controller + */ class MarketplaceCredentials extends AbstractActionController { /** + * Index action + * * @return ViewModel */ public function indexAction() { - $view = new ViewModel; + $view = new ViewModel(); $view->setTerminal(true); return $view; } diff --git a/setup/src/Magento/Setup/Controller/ModuleGrid.php b/setup/src/Magento/Setup/Controller/ModuleGrid.php index 1c1da63ea0017..f01e7bfbac11f 100644 --- a/setup/src/Magento/Setup/Controller/ModuleGrid.php +++ b/setup/src/Magento/Setup/Controller/ModuleGrid.php @@ -11,7 +11,7 @@ /** * Controller for module grid tasks */ -class ModuleGrid extends \Zend\Mvc\Controller\AbstractActionController +class ModuleGrid extends \Laminas\Mvc\Controller\AbstractActionController { /** * Module grid @@ -32,11 +32,11 @@ public function __construct( /** * Index page action * - * @return \Zend\View\Model\ViewModel + * @return \Laminas\View\Model\ViewModel */ public function indexAction() { - $view = new \Zend\View\Model\ViewModel(); + $view = new \Laminas\View\Model\ViewModel(); $view->setTerminal(true); return $view; } @@ -44,14 +44,14 @@ public function indexAction() /** * Get Components info action * - * @return \Zend\View\Model\JsonModel + * @return \Laminas\View\Model\JsonModel * @throws \RuntimeException */ public function modulesAction() { $moduleList = $this->gridModule->getList(); - return new \Zend\View\Model\JsonModel( + return new \Laminas\View\Model\JsonModel( [ 'success' => true, 'modules' => $moduleList, diff --git a/setup/src/Magento/Setup/Controller/Modules.php b/setup/src/Magento/Setup/Controller/Modules.php index 2d67e0dc65814..35b225d1e6bba 100644 --- a/setup/src/Magento/Setup/Controller/Modules.php +++ b/setup/src/Magento/Setup/Controller/Modules.php @@ -7,10 +7,13 @@ use Magento\Setup\Model\ModuleStatus; use Magento\Setup\Model\ObjectManagerProvider; -use Zend\Mvc\Controller\AbstractActionController; -use Zend\View\Model\JsonModel; -use Zend\Json\Json; +use Laminas\Mvc\Controller\AbstractActionController; +use Laminas\View\Model\JsonModel; +use Laminas\Json\Json; +/** + * Modules controller + */ class Modules extends AbstractActionController { /** diff --git a/setup/src/Magento/Setup/Controller/Navigation.php b/setup/src/Magento/Setup/Controller/Navigation.php index 5ac0bbfe38c45..e3f2091accbbd 100644 --- a/setup/src/Magento/Setup/Controller/Navigation.php +++ b/setup/src/Magento/Setup/Controller/Navigation.php @@ -5,15 +5,17 @@ */ namespace Magento\Setup\Controller; -use Magento\Setup\Model\Navigation as NavModel; -use Zend\Mvc\Controller\AbstractActionController; -use Zend\View\Model\JsonModel; -use Zend\View\Model\ViewModel; +use Laminas\Mvc\Controller\AbstractActionController; +use Laminas\View\Model\JsonModel; +use Laminas\View\Model\ViewModel; +use Magento\Backend\Model\UrlInterface; +use Magento\Framework\ObjectManagerInterface; use Magento\Setup\Model\Cron\Status; +use Magento\Setup\Model\Navigation as NavModel; +use Magento\Setup\Model\ObjectManagerProvider; /** - * Class Navigation - * + * Navigation controller */ class Navigation extends AbstractActionController { @@ -33,24 +35,33 @@ class Navigation extends AbstractActionController protected $view; /** - * @param NavModel $navigation - * @param Status $status + * @var ObjectManagerInterface + */ + private $objectManagerProvider; + + /** + * @param NavModel $navigation + * @param Status $status + * @param ObjectManagerProvider $objectManagerProvider */ - public function __construct(NavModel $navigation, Status $status) + public function __construct(NavModel $navigation, Status $status, ObjectManagerProvider $objectManagerProvider) { $this->navigation = $navigation; $this->status = $status; - $this->view = new ViewModel; + $this->objectManagerProvider = $objectManagerProvider->get(); + $this->view = new ViewModel(); $this->view->setVariable('menu', $this->navigation->getMenuItems()); $this->view->setVariable('main', $this->navigation->getMainItems()); } /** + * Index action + * * @return JsonModel */ public function indexAction() { - $json = new JsonModel; + $json = new JsonModel(); $json->setVariable('nav', $this->navigation->getData()); $json->setVariable('menu', $this->navigation->getMenuItems()); $json->setVariable('main', $this->navigation->getMainItems()); @@ -59,6 +70,8 @@ public function indexAction() } /** + * Menu action + * * @return array|ViewModel */ public function menuAction() @@ -71,17 +84,24 @@ public function menuAction() } /** + * Side menu action + * * @return array|ViewModel */ public function sideMenuAction() { + /** @var UrlInterface $backendUrl */ + $backendUrl = $this->objectManagerProvider->get(UrlInterface::class); $this->view->setTemplate('/magento/setup/navigation/side-menu.phtml'); $this->view->setVariable('isInstaller', $this->navigation->getType() == NavModel::NAV_INSTALLER); + $this->view->setVariable('backendUrl', $backendUrl->getRouteUrl('adminhtml')); $this->view->setTerminal(true); return $this->view; } /** + * Head bar action + * * @return array|ViewModel */ public function headerBarAction() diff --git a/setup/src/Magento/Setup/Controller/OtherComponentsGrid.php b/setup/src/Magento/Setup/Controller/OtherComponentsGrid.php index 284f0d2aee296..edae5c5903090 100644 --- a/setup/src/Magento/Setup/Controller/OtherComponentsGrid.php +++ b/setup/src/Magento/Setup/Controller/OtherComponentsGrid.php @@ -7,8 +7,8 @@ namespace Magento\Setup\Controller; use Magento\Composer\InfoCommand; -use Zend\Mvc\Controller\AbstractActionController; -use Zend\View\Model\JsonModel; +use Laminas\Mvc\Controller\AbstractActionController; +use Laminas\View\Model\JsonModel; /** * Controller for other components grid on select version page @@ -40,13 +40,13 @@ public function __construct( /** * No index action, return 404 error page * - * @return \Zend\View\Model\ViewModel + * @return \Laminas\View\Model\ViewModel */ public function indexAction() { - $view = new \Zend\View\Model\ViewModel; + $view = new \Laminas\View\Model\ViewModel; $view->setTemplate('/error/404.phtml'); - $this->getResponse()->setStatusCode(\Zend\Http\Response::STATUS_CODE_404); + $this->getResponse()->setStatusCode(\Laminas\Http\Response::STATUS_CODE_404); return $view; } diff --git a/setup/src/Magento/Setup/Controller/ReadinessCheckInstaller.php b/setup/src/Magento/Setup/Controller/ReadinessCheckInstaller.php index 6c29ebda3bae5..e507c645c2d02 100644 --- a/setup/src/Magento/Setup/Controller/ReadinessCheckInstaller.php +++ b/setup/src/Magento/Setup/Controller/ReadinessCheckInstaller.php @@ -5,19 +5,24 @@ */ namespace Magento\Setup\Controller; -use Zend\Mvc\Controller\AbstractActionController; -use Zend\View\Model\ViewModel; +use Laminas\Mvc\Controller\AbstractActionController; +use Laminas\View\Model\ViewModel; +/** + * ReadinessCheckInstaller controller + */ class ReadinessCheckInstaller extends AbstractActionController { const INSTALLER = 'installer'; /** + * Index action + * * @return array|ViewModel */ public function indexAction() { - $view = new ViewModel; + $view = new ViewModel(); $view->setTerminal(true); $view->setTemplate('/magento/setup/readiness-check.phtml'); $view->setVariable('actionFrom', self::INSTALLER); @@ -25,11 +30,13 @@ public function indexAction() } /** + * Progress action + * * @return array|ViewModel */ public function progressAction() { - $view = new ViewModel; + $view = new ViewModel(); $view->setTemplate('/magento/setup/readiness-check/progress.phtml'); $view->setTerminal(true); return $view; diff --git a/setup/src/Magento/Setup/Controller/ReadinessCheckUpdater.php b/setup/src/Magento/Setup/Controller/ReadinessCheckUpdater.php index 91e30ef06e703..59220004d2ed8 100644 --- a/setup/src/Magento/Setup/Controller/ReadinessCheckUpdater.php +++ b/setup/src/Magento/Setup/Controller/ReadinessCheckUpdater.php @@ -5,19 +5,24 @@ */ namespace Magento\Setup\Controller; -use Zend\Mvc\Controller\AbstractActionController; -use Zend\View\Model\ViewModel; +use Laminas\Mvc\Controller\AbstractActionController; +use Laminas\View\Model\ViewModel; +/** + * ReadinessCheckUpdater controller + */ class ReadinessCheckUpdater extends AbstractActionController { const UPDATER = 'updater'; /** + * Index action + * * @return array|ViewModel */ public function indexAction() { - $view = new ViewModel; + $view = new ViewModel(); $view->setTerminal(true); $view->setTemplate('/magento/setup/readiness-check.phtml'); $view->setVariable('actionFrom', self::UPDATER); @@ -25,11 +30,13 @@ public function indexAction() } /** + * Progress action + * * @return array|ViewModel */ public function progressAction() { - $view = new ViewModel; + $view = new ViewModel(); $view->setTemplate('/magento/setup/readiness-check/progress.phtml'); $view->setTerminal(true); return $view; diff --git a/setup/src/Magento/Setup/Controller/SelectVersion.php b/setup/src/Magento/Setup/Controller/SelectVersion.php index cfac31432feba..f22b41a8614b1 100644 --- a/setup/src/Magento/Setup/Controller/SelectVersion.php +++ b/setup/src/Magento/Setup/Controller/SelectVersion.php @@ -6,11 +6,10 @@ namespace Magento\Setup\Controller; -use Magento\Composer\InfoCommand; +use Laminas\Mvc\Controller\AbstractActionController; +use Laminas\View\Model\JsonModel; +use Laminas\View\Model\ViewModel; use Magento\Setup\Model\SystemPackage; -use Zend\Mvc\Controller\AbstractActionController; -use Zend\View\Model\JsonModel; -use Zend\View\Model\ViewModel; /** * Controller for selecting version @@ -32,11 +31,13 @@ public function __construct( } /** - * @return ViewModel|\Zend\Http\Response + * Index action + * + * @return ViewModel|\Laminas\Http\Response */ public function indexAction() { - $view = new ViewModel; + $view = new ViewModel(); $view->setTerminal(true); $view->setTemplate('/magento/setup/select-version.phtml'); return $view; diff --git a/setup/src/Magento/Setup/Controller/Session.php b/setup/src/Magento/Setup/Controller/Session.php index c9caa5a8de792..fa25924d01a15 100644 --- a/setup/src/Magento/Setup/Controller/Session.php +++ b/setup/src/Magento/Setup/Controller/Session.php @@ -3,15 +3,16 @@ * Copyright © Magento, Inc. All rights reserved. * See COPYING.txt for license details. */ + namespace Magento\Setup\Controller; /** * Sets up session for setup/index.php/session/prolong or redirects to error page */ -class Session extends \Zend\Mvc\Controller\AbstractActionController +class Session extends \Laminas\Mvc\Controller\AbstractActionController { /** - * @var \Zend\ServiceManager\ServiceManager + * @var \Laminas\ServiceManager\ServiceManager */ private $serviceManager; @@ -21,11 +22,11 @@ class Session extends \Zend\Mvc\Controller\AbstractActionController private $objectManagerProvider; /** - * @param \Zend\ServiceManager\ServiceManager $serviceManager + * @param \Laminas\ServiceManager\ServiceManager $serviceManager * @param \Magento\Setup\Model\ObjectManagerProvider $objectManagerProvider */ public function __construct( - \Zend\ServiceManager\ServiceManager $serviceManager, + \Laminas\ServiceManager\ServiceManager $serviceManager, \Magento\Setup\Model\ObjectManagerProvider $objectManagerProvider ) { $this->serviceManager = $serviceManager; @@ -35,13 +36,13 @@ public function __construct( /** * No index action, return 404 error page * - * @return \Zend\View\Model\ViewModel|\Zend\Http\Response + * @return \Laminas\View\Model\ViewModel|\Laminas\Http\Response */ public function indexAction() { - $view = new \Zend\View\Model\ViewModel(); + $view = new \Laminas\View\Model\ViewModel(); $view->setTemplate('/error/404.phtml'); - $this->getResponse()->setStatusCode(\Zend\Http\Response::STATUS_CODE_404); + $this->getResponse()->setStatusCode(\Laminas\Http\Response::STATUS_CODE_404); return $view; } @@ -65,6 +66,7 @@ public function prolongAction() $sessionConfig = $objectManager->get(\Magento\Backend\Model\Session\AdminConfig::class); /** @var \Magento\Backend\Model\Url $backendUrl */ $backendUrl = $objectManager->get(\Magento\Backend\Model\Url::class); + // phpcs:ignore Magento2.Functions.DiscouragedFunction $urlPath = parse_url($backendUrl->getBaseUrl(), PHP_URL_PATH); $cookiePath = $urlPath . 'setup'; $sessionConfig->setCookiePath($cookiePath); @@ -78,23 +80,24 @@ public function prolongAction() ); } $session->prolong(); - return new \Zend\View\Model\JsonModel(['success' => true]); + return new \Laminas\View\Model\JsonModel(['success' => true]); } + // phpcs:ignore Magento2.CodeAnalysis.EmptyBlock.DetectedCatch } catch (\Exception $e) { } - return new \Zend\View\Model\JsonModel(['success' => false]); + return new \Laminas\View\Model\JsonModel(['success' => false]); } /** * Unlogin action, return 401 error page * - * @return \Zend\View\Model\ViewModel|\Zend\Http\Response + * @return \Laminas\View\Model\ViewModel|\Laminas\Http\Response */ public function unloginAction() { - $view = new \Zend\View\Model\ViewModel(); + $view = new \Laminas\View\Model\ViewModel(); $view->setTemplate('/error/401.phtml'); - $this->getResponse()->setStatusCode(\Zend\Http\Response::STATUS_CODE_401); + $this->getResponse()->setStatusCode(\Laminas\Http\Response::STATUS_CODE_401); return $view; } } diff --git a/setup/src/Magento/Setup/Controller/StartUpdater.php b/setup/src/Magento/Setup/Controller/StartUpdater.php index ff19f8b4c0734..fb4d8ae03b9ef 100644 --- a/setup/src/Magento/Setup/Controller/StartUpdater.php +++ b/setup/src/Magento/Setup/Controller/StartUpdater.php @@ -7,10 +7,10 @@ namespace Magento\Setup\Controller; use Magento\Setup\Model\UpdaterTaskCreator; -use Zend\Mvc\Controller\AbstractActionController; -use Zend\View\Model\JsonModel; -use Zend\View\Model\ViewModel; -use Zend\Json\Json; +use Laminas\Mvc\Controller\AbstractActionController; +use Laminas\View\Model\JsonModel; +use Laminas\View\Model\ViewModel; +use Laminas\Json\Json; /** * Controller for updater tasks diff --git a/setup/src/Magento/Setup/Controller/Success.php b/setup/src/Magento/Setup/Controller/Success.php index c792aadefe4f0..c597dd8b1bc0a 100644 --- a/setup/src/Magento/Setup/Controller/Success.php +++ b/setup/src/Magento/Setup/Controller/Success.php @@ -7,9 +7,12 @@ use Magento\Framework\Module\ModuleList; use Magento\Setup\Model\ObjectManagerProvider; -use Zend\Mvc\Controller\AbstractActionController; -use Zend\View\Model\ViewModel; +use Laminas\Mvc\Controller\AbstractActionController; +use Laminas\View\Model\ViewModel; +/** + * Success controller + */ class Success extends AbstractActionController { /** @@ -33,7 +36,10 @@ public function __construct(ModuleList $moduleList, ObjectManagerProvider $objec } /** + * Index action + * * @return ViewModel + * @throws \Magento\Setup\Exception */ public function indexAction() { diff --git a/setup/src/Magento/Setup/Controller/SystemConfig.php b/setup/src/Magento/Setup/Controller/SystemConfig.php index e089adaddf40f..0067752997e17 100644 --- a/setup/src/Magento/Setup/Controller/SystemConfig.php +++ b/setup/src/Magento/Setup/Controller/SystemConfig.php @@ -3,19 +3,25 @@ * Copyright © Magento, Inc. All rights reserved. * See COPYING.txt for license details. */ + namespace Magento\Setup\Controller; -use Zend\Mvc\Controller\AbstractActionController; -use Zend\View\Model\ViewModel; +use Laminas\Mvc\Controller\AbstractActionController; +use Laminas\View\Model\ViewModel; +/** + * SystemConfig controller + */ class SystemConfig extends AbstractActionController { /** + * Index action + * * @return ViewModel */ public function indexAction() { - $view = new ViewModel; + $view = new ViewModel(); $view->setTerminal(true); return $view; } diff --git a/setup/src/Magento/Setup/Controller/UpdateExtensionGrid.php b/setup/src/Magento/Setup/Controller/UpdateExtensionGrid.php index b5d342cf69745..55ff91a04cdf4 100644 --- a/setup/src/Magento/Setup/Controller/UpdateExtensionGrid.php +++ b/setup/src/Magento/Setup/Controller/UpdateExtensionGrid.php @@ -5,9 +5,9 @@ */ namespace Magento\Setup\Controller; -use Zend\Mvc\Controller\AbstractActionController; -use Zend\View\Model\JsonModel; -use Zend\View\Model\ViewModel; +use Laminas\Mvc\Controller\AbstractActionController; +use Laminas\View\Model\JsonModel; +use Laminas\View\Model\ViewModel; use Magento\Setup\Model\Grid; /** diff --git a/setup/src/Magento/Setup/Controller/UpdaterSuccess.php b/setup/src/Magento/Setup/Controller/UpdaterSuccess.php index 37a662b959faa..c12d9a164ac4c 100644 --- a/setup/src/Magento/Setup/Controller/UpdaterSuccess.php +++ b/setup/src/Magento/Setup/Controller/UpdaterSuccess.php @@ -3,12 +3,16 @@ * Copyright © Magento, Inc. All rights reserved. * See COPYING.txt for license details. */ + namespace Magento\Setup\Controller; +use Laminas\Mvc\Controller\AbstractActionController; +use Laminas\View\Model\ViewModel; use Magento\Framework\App\MaintenanceMode; -use Zend\Mvc\Controller\AbstractActionController; -use Zend\View\Model\ViewModel; +/** + * UpdaterSuccess controller + */ class UpdaterSuccess extends AbstractActionController { /** @@ -27,12 +31,14 @@ public function __construct(MaintenanceMode $maintenanceMode) } /** + * Index action + * * @return ViewModel */ public function indexAction() { $this->maintenanceMode->set(false); - $view = new ViewModel; + $view = new ViewModel(); $view->setTerminal(true); return $view; } diff --git a/setup/src/Magento/Setup/Controller/UrlCheck.php b/setup/src/Magento/Setup/Controller/UrlCheck.php index 6a209e0c18304..1dfe838e6ca2c 100644 --- a/setup/src/Magento/Setup/Controller/UrlCheck.php +++ b/setup/src/Magento/Setup/Controller/UrlCheck.php @@ -3,13 +3,17 @@ * Copyright © Magento, Inc. All rights reserved. * See COPYING.txt for license details. */ + namespace Magento\Setup\Controller; -use Zend\Mvc\Controller\AbstractActionController; -use Zend\View\Model\JsonModel; -use Zend\Json\Json; +use Laminas\Mvc\Controller\AbstractActionController; +use Laminas\View\Model\JsonModel; +use Laminas\Json\Json; use Magento\Framework\Validator\Url as UrlValidator; +/** + * UrlCheck controller + */ class UrlCheck extends AbstractActionController { /** diff --git a/setup/src/Magento/Setup/Controller/ValidateAdminCredentials.php b/setup/src/Magento/Setup/Controller/ValidateAdminCredentials.php index ce5da002a372d..03faef928cdaf 100644 --- a/setup/src/Magento/Setup/Controller/ValidateAdminCredentials.php +++ b/setup/src/Magento/Setup/Controller/ValidateAdminCredentials.php @@ -8,9 +8,9 @@ use Magento\Setup\Model\Installer; use Magento\Setup\Model\RequestDataConverter; use Magento\Setup\Validator\AdminCredentialsValidator; -use Zend\Json\Json; -use Zend\Mvc\Controller\AbstractActionController; -use Zend\View\Model\JsonModel; +use Laminas\Json\Json; +use Laminas\Mvc\Controller\AbstractActionController; +use Laminas\View\Model\JsonModel; /** * Controller for admin credentials validation diff --git a/setup/src/Magento/Setup/Controller/WebConfiguration.php b/setup/src/Magento/Setup/Controller/WebConfiguration.php index 6dded9f4071ce..eebe01a008afb 100644 --- a/setup/src/Magento/Setup/Controller/WebConfiguration.php +++ b/setup/src/Magento/Setup/Controller/WebConfiguration.php @@ -3,13 +3,17 @@ * Copyright © Magento, Inc. All rights reserved. * See COPYING.txt for license details. */ + namespace Magento\Setup\Controller; use Magento\Framework\App\SetupInfo; use Magento\Framework\Config\ConfigOptionsListConstants; -use Zend\Mvc\Controller\AbstractActionController; -use Zend\View\Model\ViewModel; +use Laminas\Mvc\Controller\AbstractActionController; +use Laminas\View\Model\ViewModel; +/** + * WebConfiguration controller + */ class WebConfiguration extends AbstractActionController { /** @@ -19,6 +23,7 @@ class WebConfiguration extends AbstractActionController */ public function indexAction() { + // phpcs:ignore Magento2.Security.Superglobal $setupInfo = new SetupInfo($_SERVER); $view = new ViewModel( [ diff --git a/setup/src/Magento/Setup/Fixtures/AttributeSet/Pattern.php b/setup/src/Magento/Setup/Fixtures/AttributeSet/Pattern.php index 1d582862c2428..2947bd352aab3 100644 --- a/setup/src/Magento/Setup/Fixtures/AttributeSet/Pattern.php +++ b/setup/src/Magento/Setup/Fixtures/AttributeSet/Pattern.php @@ -3,6 +3,8 @@ * Copyright © Magento, Inc. All rights reserved. * See COPYING.txt for license details. */ +declare(strict_types=1); + namespace Magento\Setup\Fixtures\AttributeSet; /** @@ -32,7 +34,7 @@ class Pattern * @param string $name * @param int $attributesPerSet * @param int $optionsPerAttribute - * @param callable $attributePattern callback in f($index, $attributeData) format + * @param callable $attributePattern callback in f($index, $attributeData) format * @return array */ public function generateAttributeSet( @@ -46,9 +48,9 @@ public function generateAttributeSet( 'attributes' => [] ]; for ($index = 1; $index <= $attributesPerSet; $index++) { - $attributeData = $this->generateAttribute( + $attributeData = $this->generateAttribute( $index, - is_array($optionsPerAttribute) ? $optionsPerAttribute[$index-1] : $optionsPerAttribute + is_array($optionsPerAttribute) ? $optionsPerAttribute[$index - 1] : $optionsPerAttribute ); if (is_callable($attributePattern)) { $attributeData = $attributePattern($index, $attributeData); @@ -72,7 +74,7 @@ private function generateAttribute($index, $optionsPerAttribute) $attribute['attribute_code'] = $attribute['attribute_code'] . $index; $attribute['frontend_label'] = $attribute['frontend_label'] . $index; $attribute['options'] = ['option' => $this->generateOptions($optionsPerAttribute)]; - $attribute['default_option'] = $attribute['options']['option'][0]['label']; + $attribute['default_value'] = $attribute['options']['option'][0]['value']; return $attribute; } diff --git a/setup/src/Magento/Setup/Fixtures/CategoriesFixture.php b/setup/src/Magento/Setup/Fixtures/CategoriesFixture.php index e8a0a469e2ee5..782f688f8a09e 100644 --- a/setup/src/Magento/Setup/Fixtures/CategoriesFixture.php +++ b/setup/src/Magento/Setup/Fixtures/CategoriesFixture.php @@ -9,6 +9,7 @@ use Magento\Catalog\Model\Category; use Magento\Catalog\Model\CategoryFactory; use Magento\Catalog\Model\ResourceModel\Category\CollectionFactory; +use Magento\Store\Model\Store; use Magento\Store\Model\StoreManager; /** @@ -82,7 +83,7 @@ public function __construct( protected $priority = 20; /** - * {@inheritdoc} + * @inheritdoc */ public function execute() { @@ -97,7 +98,7 @@ public function execute() $category = $this->categoryFactory->create(); $category->load($parentCategoryId); // Need for generation url rewrites per all category store view - $category->setStoreId(\Magento\Store\Model\Store::DEFAULT_STORE_ID); + $category->setStoreId(Store::DEFAULT_STORE_ID); $categoryIndex = 1; $this->generateCategories( $category, @@ -130,6 +131,7 @@ private function generateCategories( $category->setId(null) ->setUrlKey(null) ->setUrlPath(null) + ->setStoreId(Store::DEFAULT_STORE_ID) ->setName($this->getCategoryName($parentCategory, $nestingLevel, $i)) ->setParentId($parentCategory->getId()) ->setLevel($parentCategory->getLevel() + 1) @@ -237,7 +239,7 @@ private function getCategoryPrefix() } /** - * {@inheritdoc} + * @inheritdoc */ public function getActionTitle() { @@ -245,7 +247,7 @@ public function getActionTitle() } /** - * {@inheritdoc} + * @inheritdoc */ public function introduceParamLabels() { diff --git a/setup/src/Magento/Setup/Fixtures/SimpleProductsFixture.php b/setup/src/Magento/Setup/Fixtures/SimpleProductsFixture.php index 8e2e842a7d805..84fb2e5beed4b 100644 --- a/setup/src/Magento/Setup/Fixtures/SimpleProductsFixture.php +++ b/setup/src/Magento/Setup/Fixtures/SimpleProductsFixture.php @@ -3,13 +3,17 @@ * Copyright © Magento, Inc. All rights reserved. * See COPYING.txt for license details. */ +declare(strict_types=1); namespace Magento\Setup\Fixtures; use Magento\Catalog\Model\Product; use Magento\Catalog\Model\Product\Attribute\Source\Status; use Magento\Catalog\Model\ProductFactory; +use Magento\Eav\Model\Entity\Attribute; +use Magento\Eav\Model\ResourceModel\Entity\Attribute\Collection; use Magento\Eav\Model\ResourceModel\Entity\Attribute\CollectionFactory; +use Magento\Eav\Model\ResourceModel\Entity\Attribute\Set\Collection as AttributeSetCollection; use Magento\Eav\Model\ResourceModel\Entity\Attribute\Set\CollectionFactory as AttributeSetCollectionFactory; use Magento\Setup\Model\FixtureGenerator\ProductGenerator; use Magento\Setup\Model\SearchTermDescriptionGeneratorFactory; @@ -68,7 +72,7 @@ class SimpleProductsFixture extends Fixture private $defaultAttributeSetId; /** - * @var \Magento\Eav\Model\ResourceModel\Entity\Attribute\Collection + * @var Collection */ private $attributeCollectionFactory; @@ -97,6 +101,11 @@ class SimpleProductsFixture extends Fixture */ private $priceProvider; + /** + * @var int[] + */ + private $additionalAttributeSetIds; + /** * @param FixtureModel $fixtureModel * @param ProductFactory $productFactory @@ -184,35 +193,38 @@ public function execute() 'Short simple product Description %s' ); - $additionalAttributeSets = $this->getAdditionalAttributeSets(); - $attributeSet = function ($index) use ($defaultAttributeSets, $additionalAttributeSets) { + $additionalAttributeSetIds = $this->getAdditionalAttributeSetIds(); + $attributeSet = function ($index) use ($defaultAttributeSets, $additionalAttributeSetIds) { // phpcs:ignore mt_srand($index); $attributeSetCount = count(array_keys($defaultAttributeSets)); if ($attributeSetCount > (($index - 1) % (int)$this->fixtureModel->getValue('categories', 30))) { - // phpcs:ignore Magento2.Functions.DiscouragedFunction + // phpcs:ignore Magento2.Security.InsecureFunction return array_keys($defaultAttributeSets)[mt_rand(0, count(array_keys($defaultAttributeSets)) - 1)]; } else { - $customSetsAmount = count($additionalAttributeSets); + $customSetsAmount = count($additionalAttributeSetIds); return $customSetsAmount - ? $additionalAttributeSets[$index % count($additionalAttributeSets)]['attribute_set_id'] + ? $additionalAttributeSetIds[$index % $customSetsAmount] : $this->getDefaultAttributeSetId(); } }; + $additionalAttributeValues = $this->getAdditionalAttributeValues(); $additionalAttributes = function ( $attributeSetId, $index ) use ( $defaultAttributeSets, - $additionalAttributeSets + $additionalAttributeValues ) { $attributeValues = []; // phpcs:ignore mt_srand($index); - if (isset($defaultAttributeSets[$attributeSetId])) { - foreach ($defaultAttributeSets[$attributeSetId] as $attributeCode => $values) { - // phpcs:ignore Magento2.Functions.DiscouragedFunction + $attributeValuesByAttributeSet = $defaultAttributeSets[$attributeSetId] + ?? $additionalAttributeValues[$attributeSetId]; + if (!empty($attributeValuesByAttributeSet)) { + foreach ($attributeValuesByAttributeSet as $attributeCode => $values) { + // phpcs:ignore Magento2.Security.InsecureFunction $attributeValues[$attributeCode] = $values[mt_rand(0, count($values) - 1)]; } } @@ -279,10 +291,10 @@ private function getDefaultAttributeSetId() } /** - * Get default attribute sets with attributes + * Get default attribute sets with attributes. * - * @see config/attributeSets.xml * @return array + * @see config/attributeSets.xml */ private function getDefaultAttributeSets() { @@ -301,17 +313,7 @@ private function getDefaultAttributeSets() 'attribute_code', array_column($attributesData, 'attribute_code') ); - /** @var \Magento\Eav\Model\Entity\Attribute $attribute */ - foreach ($attributeCollection as $attribute) { - $values = []; - $options = $attribute->getOptions(); - foreach (($options ?: []) as $option) { - if ($option->getValue()) { - $values[] = $option->getValue(); - } - } - $attributes[$attribute->getAttributeSetId()][$attribute->getAttributeCode()] = $values; - } + $attributes = $this->processAttributeValues($attributeCollection, $attributes); } } $attributes[$this->getDefaultAttributeSetId()] = []; @@ -381,16 +383,64 @@ private function readDescriptionConfig($configSrc) } /** - * Get additional attribute sets + * Get additional attribute set ids. + * + * @return int[] + */ + private function getAdditionalAttributeSetIds() + { + if (null === $this->additionalAttributeSetIds) { + /** @var AttributeSetCollection $sets */ + $sets = $this->attributeSetCollectionFactory->create(); + $sets->addFieldToFilter( + 'attribute_set_name', + ['like' => AttributeSetsFixture::PRODUCT_SET_NAME . '%'] + ); + $this->additionalAttributeSetIds = $sets->getAllIds(); + } + + return $this->additionalAttributeSetIds; + } + + /** + * Get values of additional attributes. * - * @return \Magento\Eav\Model\ResourceModel\Entity\Attribute\Set\Collection[] + * @return array */ - private function getAdditionalAttributeSets() + private function getAdditionalAttributeValues(): array { - /** @var \Magento\Eav\Model\ResourceModel\Entity\Attribute\Set\Collection $sets */ - $sets = $this->attributeSetCollectionFactory->create(); - $sets->addFieldToFilter('attribute_set_name', ['like' => AttributeSetsFixture::PRODUCT_SET_NAME . '%']); + $attributeCollection = $this->attributeCollectionFactory->create(); + $attributeCollection->setAttributeSetsFilter($this->getAdditionalAttributeSetIds()) + ->addFieldToFilter('attribute_code', ['like' => 'attribute_set%']); + $attributeCollection->getSelect()->columns(['entity_attribute.attribute_set_id']); + + return $this->processAttributeValues($attributeCollection); + } - return $sets->getData(); + /** + * Maps attribute values by attribute set and attribute code. + * + * @param Collection $attributeCollection + * @param array $attributes + * @return array + */ + private function processAttributeValues( + Collection $attributeCollection, + array $attributes = [] + ): array { + /** @var Attribute $attribute */ + foreach ($attributeCollection as $attribute) { + $values = []; + $options = $attribute->getOptions() ?? []; + $attributeSetId = $attribute->getAttributeSetId() ?? $this->getDefaultAttributeSetId(); + foreach ($options as $option) { + if ($option->getValue()) { + $values[] = $option->getValue(); + } + } + $attributes[$attributeSetId][$attribute->getAttributeCode()] = $values; + } + + return $attributes; } } diff --git a/setup/src/Magento/Setup/Model/AdminAccountFactory.php b/setup/src/Magento/Setup/Model/AdminAccountFactory.php index 5f31bae30d3a9..0811be79f63f6 100644 --- a/setup/src/Magento/Setup/Model/AdminAccountFactory.php +++ b/setup/src/Magento/Setup/Model/AdminAccountFactory.php @@ -6,11 +6,12 @@ namespace Magento\Setup\Model; -use Magento\Setup\Module\Setup; -use Zend\ServiceManager\ServiceLocatorInterface; -use Magento\Framework\Serialize\Serializer\Json; +use Laminas\ServiceManager\ServiceLocatorInterface; use Magento\Framework\DB\Adapter\AdapterInterface; +/** + * Factory for \Magento\Setup\Model\AdminAccount + */ class AdminAccountFactory { /** @@ -27,6 +28,8 @@ public function __construct(ServiceLocatorInterface $serviceLocator) } /** + * Create object + * * @param AdapterInterface $connection * @param array $data * @return AdminAccount diff --git a/setup/src/Magento/Setup/Model/ConfigOptionsList/DriverOptions.php b/setup/src/Magento/Setup/Model/ConfigOptionsList/DriverOptions.php index 1d9fdd6098cbb..6d5235cffec77 100644 --- a/setup/src/Magento/Setup/Model/ConfigOptionsList/DriverOptions.php +++ b/setup/src/Magento/Setup/Model/ConfigOptionsList/DriverOptions.php @@ -46,6 +46,7 @@ public function getDriverOptions(array $options): array */ private function optionExists($options, $driverOptionKey): bool { - return $options[$driverOptionKey] === false || !empty($options[$driverOptionKey]); + return isset($options[$driverOptionKey]) + && ($options[$driverOptionKey] === false || !empty($options[$driverOptionKey])); } } diff --git a/setup/src/Magento/Setup/Model/ConfigOptionsListCollector.php b/setup/src/Magento/Setup/Model/ConfigOptionsListCollector.php index cd2442c215bb3..a97685920f13a 100644 --- a/setup/src/Magento/Setup/Model/ConfigOptionsListCollector.php +++ b/setup/src/Magento/Setup/Model/ConfigOptionsListCollector.php @@ -9,7 +9,7 @@ use Magento\Framework\Filesystem; use Magento\Framework\Module\FullModuleList; use Magento\Framework\Setup\ConfigOptionsListInterface; -use Zend\ServiceManager\ServiceLocatorInterface; +use Laminas\ServiceManager\ServiceLocatorInterface; /** * Collects all ConfigOptionsList class in modules and setup @@ -76,9 +76,11 @@ public function __construct( /** * Auto discover ConfigOptionsList class and collect them. + * * These classes should reside in /Setup directories. * - * @return \Magento\Framework\Setup\ConfigOptionsListInterface[] + * @return ConfigOptionsListInterface[] + * @throws \Magento\Setup\Exception */ public function collectOptionsLists() { diff --git a/setup/src/Magento/Setup/Model/Cron/JobFactory.php b/setup/src/Magento/Setup/Model/Cron/JobFactory.php index a29bf389254f7..cae149ed38e8f 100644 --- a/setup/src/Magento/Setup/Model/Cron/JobFactory.php +++ b/setup/src/Magento/Setup/Model/Cron/JobFactory.php @@ -3,6 +3,7 @@ * Copyright © Magento, Inc. All rights reserved. * See COPYING.txt for license details. */ + namespace Magento\Setup\Model\Cron; use Magento\Backend\Console\Command\CacheDisableCommand; @@ -11,7 +12,7 @@ use Magento\Setup\Console\Command\ModuleDisableCommand; use Magento\Setup\Console\Command\ModuleEnableCommand; use Magento\Setup\Console\Command\UpgradeCommand; -use Zend\ServiceManager\ServiceLocatorInterface; +use Laminas\ServiceManager\ServiceLocatorInterface; use Magento\Setup\Console\Command\MaintenanceDisableCommand; use Magento\Setup\Console\Command\MaintenanceEnableCommand; @@ -64,7 +65,9 @@ public function __construct(ServiceLocatorInterface $serviceLocator) public function create($name, array $params = []) { $cronStatus = $this->serviceLocator->get(\Magento\Setup\Model\Cron\Status::class); + // phpcs:ignore Magento2.Functions.DiscouragedFunction $statusStream = fopen($cronStatus->getStatusFilePath(), 'a+'); + // phpcs:ignore Magento2.Functions.DiscouragedFunction $logStream = fopen($cronStatus->getLogFilePath(), 'a+'); $streamOutput = new MultipleStreamOutput([$statusStream, $logStream]); $objectManagerProvider = $this->serviceLocator->get(\Magento\Setup\Model\ObjectManagerProvider::class); @@ -81,7 +84,6 @@ public function create($name, array $params = []) $name, $params ); - break; case self::JOB_DB_ROLLBACK: return new JobDbRollback( $objectManager->get(\Magento\Framework\Setup\BackupRollbackFactory::class), @@ -91,7 +93,6 @@ public function create($name, array $params = []) $name, $params ); - break; case self::JOB_STATIC_REGENERATE: return new JobStaticRegenerate( $objectManagerProvider, @@ -100,7 +101,6 @@ public function create($name, array $params = []) $name, $params ); - break; case self::JOB_COMPONENT_UNINSTALL: $moduleUninstall = new Helper\ModuleUninstall( $this->serviceLocator->get(\Magento\Setup\Model\ModuleUninstaller::class), @@ -123,7 +123,6 @@ public function create($name, array $params = []) $name, $params ); - break; case self::JOB_MODULE_ENABLE: return new JobModule( $this->serviceLocator->get(ModuleEnableCommand::class), @@ -133,7 +132,6 @@ public function create($name, array $params = []) $name, $params ); - break; case self::JOB_MODULE_DISABLE: return new JobModule( $this->serviceLocator->get(ModuleDisableCommand::class), @@ -143,7 +141,6 @@ public function create($name, array $params = []) $name, $params ); - break; case self::JOB_ENABLE_CACHE: return new JobSetCache( $objectManager->get(CacheEnableCommand::class), @@ -153,7 +150,6 @@ public function create($name, array $params = []) $name, $params ); - break; case self::JOB_DISABLE_CACHE: return new JobSetCache( $objectManager->get(CacheDisableCommand::class), @@ -162,7 +158,6 @@ public function create($name, array $params = []) $cronStatus, $name ); - break; case self::JOB_MAINTENANCE_MODE_ENABLE: return new JobSetMaintenanceMode( $this->serviceLocator->get(MaintenanceEnableCommand::class), @@ -172,7 +167,6 @@ public function create($name, array $params = []) $name, $params ); - break; case self::JOB_MAINTENANCE_MODE_DISABLE: return new JobSetMaintenanceMode( $this->serviceLocator->get(MaintenanceDisableCommand::class), @@ -182,10 +176,8 @@ public function create($name, array $params = []) $name, $params ); - break; default: throw new \RuntimeException(sprintf('"%s" job is not supported.', $name)); - break; } } } diff --git a/setup/src/Magento/Setup/Model/Installer.php b/setup/src/Magento/Setup/Model/Installer.php index 23f8a13c8bfe8..535040f942b89 100644 --- a/setup/src/Magento/Setup/Model/Installer.php +++ b/setup/src/Magento/Setup/Model/Installer.php @@ -7,6 +7,8 @@ namespace Magento\Setup\Model; use Magento\Backend\Setup\ConfigOptionsList as BackendConfigOptionsList; +use Magento\Framework\App\Cache\Type\Block as BlockCache; +use Magento\Framework\App\Cache\Type\Layout as LayoutCache; use Magento\Framework\App\DeploymentConfig\Reader; use Magento\Framework\App\DeploymentConfig\Writer; use Magento\Framework\App\Filesystem\DirectoryList; @@ -35,6 +37,7 @@ use Magento\Framework\Setup\SchemaSetupInterface; use Magento\Framework\Setup\UpgradeDataInterface; use Magento\Framework\Setup\UpgradeSchemaInterface; +use Magento\PageCache\Model\Cache\Type as PageCache; use Magento\Setup\Console\Command\InstallCommand; use Magento\Setup\Controller\ResponseTypeInterface; use Magento\Setup\Model\ConfigModel as SetupConfigModel; @@ -336,7 +339,7 @@ public function install($request) } $script[] = ['Installing database schema:', 'installSchema', [$request]]; $script[] = ['Installing user configuration...', 'installUserConfig', [$request]]; - $script[] = ['Enabling caches:', 'enableCaches', []]; + $script[] = ['Enabling caches:', 'updateCaches', [true]]; $script[] = ['Installing data...', 'installDataFixtures', [$request]]; if (!empty($request[InstallCommand::INPUT_KEY_SALES_ORDER_INCREMENT_PREFIX])) { $script[] = [ @@ -866,6 +869,12 @@ private function convertationOfOldScriptsIsAllowed(array $request) */ public function installDataFixtures(array $request = []) { + $frontendCaches = [ + PageCache::TYPE_IDENTIFIER, + BlockCache::TYPE_IDENTIFIER, + LayoutCache::TYPE_IDENTIFIER, + ]; + /** @var \Magento\Framework\Registry $registry */ $registry = $this->objectManagerProvider->get()->get(\Magento\Framework\Registry::class); //For backward compatibility in install and upgrade scripts with enabled parallelization. @@ -876,7 +885,11 @@ public function installDataFixtures(array $request = []) $setup = $this->dataSetupFactory->create(); $this->checkFilePermissionsForDbUpgrade(); $this->log->log('Data install/update:'); + $this->log->log('Disabling caches:'); + $this->updateCaches(false, $frontendCaches); $this->handleDBSchemaData($setup, 'data', $request); + $this->log->log('Enabling caches:'); + $this->updateCaches(true, $frontendCaches); $registry->unregister('setup-mode-enabled'); } @@ -1248,23 +1261,39 @@ public function uninstall() } /** - * Enables caches after installing application + * Enable or disable caches for specific types that are available * - * @return void + * If no types are specified then it will enable or disable all available types + * Note this is called by install() via callback. * - * @SuppressWarnings(PHPMD.UnusedPrivateMethod) Called by install() via callback. + * @param bool $isEnabled + * @param array $types + * @return void */ - private function enableCaches() + private function updateCaches($isEnabled, $types = []) { /** @var \Magento\Framework\App\Cache\Manager $cacheManager */ $cacheManager = $this->objectManagerProvider->get()->create(\Magento\Framework\App\Cache\Manager::class); - $types = $cacheManager->getAvailableTypes(); - $enabledTypes = $cacheManager->setEnabled($types, true); - $cacheManager->clean($enabledTypes); + + $availableTypes = $cacheManager->getAvailableTypes(); + $types = empty($types) ? $availableTypes : array_intersect($availableTypes, $types); + $enabledTypes = $cacheManager->setEnabled($types, $isEnabled); + if ($isEnabled) { + $cacheManager->clean($enabledTypes); + } + + // Only get statuses of specific cache types + $cacheStatus = array_filter( + $cacheManager->getStatus(), + function (string $key) use ($types) { + return in_array($key, $types); + }, + ARRAY_FILTER_USE_KEY + ); $this->log->log('Current status:'); // phpcs:ignore Magento2.Functions.DiscouragedFunction - $this->log->log(print_r($cacheManager->getStatus(), true)); + $this->log->log(print_r($cacheStatus, true)); } /** diff --git a/setup/src/Magento/Setup/Model/InstallerFactory.php b/setup/src/Magento/Setup/Model/InstallerFactory.php index 0fb933dd46cb4..aeb5be93614fb 100644 --- a/setup/src/Magento/Setup/Model/InstallerFactory.php +++ b/setup/src/Magento/Setup/Model/InstallerFactory.php @@ -6,18 +6,20 @@ namespace Magento\Setup\Model; -use Zend\ServiceManager\ServiceLocatorInterface; -use Magento\Setup\Module\ResourceFactory; +use Laminas\ServiceManager\ServiceLocatorInterface; use Magento\Framework\App\ErrorHandler; use Magento\Framework\Setup\LoggerInterface; +use Magento\Setup\Module\ResourceFactory; /** + * Factory for \Magento\Setup\Model\Installer + * * @SuppressWarnings(PHPMD.CouplingBetweenObjects) */ class InstallerFactory { /** - * Zend Framework's service locator + * Laminas Framework's service locator * * @var ServiceLocatorInterface */ @@ -29,8 +31,6 @@ class InstallerFactory private $resourceFactory; /** - * Constructor - * * @param ServiceLocatorInterface $serviceLocator * @param ResourceFactory $resourceFactory */ @@ -50,6 +50,7 @@ public function __construct( * * @param LoggerInterface $log * @return Installer + * @throws \Magento\Setup\Exception */ public function create(LoggerInterface $log) { @@ -83,7 +84,7 @@ public function create(LoggerInterface $log) } /** - * creates Resource Factory + * Create Resource Factory * * @return Resource */ diff --git a/setup/src/Magento/Setup/Model/Navigation.php b/setup/src/Magento/Setup/Model/Navigation.php index 9882059be0f7e..3eaa1fad4016e 100644 --- a/setup/src/Magento/Setup/Model/Navigation.php +++ b/setup/src/Magento/Setup/Model/Navigation.php @@ -6,9 +6,12 @@ namespace Magento\Setup\Model; -use Zend\ServiceManager\ServiceLocatorInterface; +use Laminas\ServiceManager\ServiceLocatorInterface; use Magento\Framework\App\DeploymentConfig; +/** + * Navigation model + */ class Navigation { /**#@+ @@ -34,6 +37,8 @@ class Navigation /** * @param ServiceLocatorInterface $serviceLocator * @param DeploymentConfig $deploymentConfig + * @throws \Magento\Framework\Exception\FileSystemException + * @throws \Magento\Framework\Exception\RuntimeException */ public function __construct(ServiceLocatorInterface $serviceLocator, DeploymentConfig $deploymentConfig) { @@ -49,6 +54,8 @@ public function __construct(ServiceLocatorInterface $serviceLocator, DeploymentC } /** + * Get type + * * @return string */ public function getType() @@ -57,6 +64,8 @@ public function getType() } /** + * Get data + * * @return array */ public function getData() diff --git a/setup/src/Magento/Setup/Model/ObjectManagerProvider.php b/setup/src/Magento/Setup/Model/ObjectManagerProvider.php index 79216c8ec89b5..022f89f2eea78 100644 --- a/setup/src/Magento/Setup/Model/ObjectManagerProvider.php +++ b/setup/src/Magento/Setup/Model/ObjectManagerProvider.php @@ -9,13 +9,13 @@ use Symfony\Component\Console\Application; use Magento\Framework\Console\CommandListInterface; use Magento\Framework\ObjectManagerInterface; -use Zend\ServiceManager\ServiceLocatorInterface; +use Laminas\ServiceManager\ServiceLocatorInterface; use Magento\Setup\Mvc\Bootstrap\InitParamListener; /** * Object manager provider * - * Links Zend Framework's service locator and Magento object manager. + * Links Laminas Framework's service locator and Magento object manager. * Guaranties single object manager per application run. * Hides complexity of creating Magento object manager */ diff --git a/setup/src/Magento/Setup/Model/PackagesAuth.php b/setup/src/Magento/Setup/Model/PackagesAuth.php index 5a29f9953d51b..b0363a5363d41 100644 --- a/setup/src/Magento/Setup/Model/PackagesAuth.php +++ b/setup/src/Magento/Setup/Model/PackagesAuth.php @@ -7,7 +7,8 @@ namespace Magento\Setup\Model; use Magento\Framework\App\Filesystem\DirectoryList; -use Zend\View\Model\JsonModel; +use Magento\Framework\Exception\LocalizedException; +use Magento\Framework\Phrase; /** * Class PackagesAuth, checks, saves and removes auth details related to packages. @@ -30,7 +31,7 @@ class PackagesAuth /**#@-*/ /** - * @var \Zend\ServiceManager\ServiceLocatorInterface + * @var \Laminas\ServiceManager\ServiceLocatorInterface */ protected $serviceLocator; @@ -55,14 +56,14 @@ class PackagesAuth private $serializer; /** - * @param \Zend\ServiceManager\ServiceLocatorInterface $serviceLocator + * @param \Laminas\ServiceManager\ServiceLocatorInterface $serviceLocator * @param \Magento\Framework\HTTP\Client\Curl $curl * @param \Magento\Framework\Filesystem $filesystem * @param \Magento\Framework\Serialize\Serializer\Json|null $serializer * @throws \RuntimeException */ public function __construct( - \Zend\ServiceManager\ServiceLocatorInterface $serviceLocator, + \Laminas\ServiceManager\ServiceLocatorInterface $serviceLocator, \Magento\Framework\HTTP\Client\Curl $curl, \Magento\Framework\Filesystem $filesystem, \Magento\Framework\Serialize\Serializer\Json $serializer = null @@ -70,19 +71,23 @@ public function __construct( $this->serviceLocator = $serviceLocator; $this->curlClient = $curl; $this->filesystem = $filesystem; - $this->serializer = $serializer?: \Magento\Framework\App\ObjectManager::getInstance() + $this->serializer = $serializer ?: \Magento\Framework\App\ObjectManager::getInstance() ->get(\Magento\Framework\Serialize\Serializer\Json::class); } /** + * Get packages json URL + * * @return string */ private function getPackagesJsonUrl() { - return $this->urlPrefix . $this->getCredentialBaseUrl() . '/packages.json'; + return $this->urlPrefix . $this->getCredentialBaseUrl() . '/packages.json'; } /** + * Get credentials base URL + * * @return string */ public function getCredentialBaseUrl() @@ -92,6 +97,8 @@ public function getCredentialBaseUrl() } /** + * Check credentials + * * @param string $token * @param string $secretKey * @return string @@ -148,7 +155,7 @@ private function getAuthJson() $data = $directory->readFile(self::PATH_TO_AUTH_FILE); return json_decode($data, true); } catch (\Exception $e) { - throw new \Exception('Error in reading Auth file'); + throw new LocalizedException(new Phrase('Error in reading Auth file')); } } return false; @@ -198,7 +205,7 @@ public function saveAuthJson($username, $password) ] ] ]; - $json = new \Zend\View\Model\JsonModel($authContent); + $json = new \Laminas\View\Model\JsonModel($authContent); $json->setOption('prettyPrint', true); $jsonContent = $json->serialize(); diff --git a/setup/src/Magento/Setup/Model/UpdaterTaskCreator.php b/setup/src/Magento/Setup/Model/UpdaterTaskCreator.php index e3c598a4aa57e..c80717fe7c857 100644 --- a/setup/src/Magento/Setup/Model/UpdaterTaskCreator.php +++ b/setup/src/Magento/Setup/Model/UpdaterTaskCreator.php @@ -8,7 +8,7 @@ use Magento\Framework\App\Filesystem\DirectoryList; use Magento\Setup\Model\Cron\JobComponentUninstall; -use Zend\Json\Json; +use Laminas\Json\Json; /** * Validates payloads for updater tasks diff --git a/setup/src/Magento/Setup/Module.php b/setup/src/Magento/Setup/Module.php index a2c4b1b774b9b..7808ead3808e3 100644 --- a/setup/src/Magento/Setup/Module.php +++ b/setup/src/Magento/Setup/Module.php @@ -6,45 +6,50 @@ namespace Magento\Setup; +use Laminas\EventManager\EventInterface; +use Laminas\EventManager\EventManager; +use Laminas\ModuleManager\Feature\BootstrapListenerInterface; +use Laminas\ModuleManager\Feature\ConfigProviderInterface; +use Laminas\Mvc\ModuleRouteListener; +use Laminas\Mvc\MvcEvent; +use Laminas\Stdlib\DispatchableInterface; use Magento\Framework\App\Response\HeaderProvider\XssProtection; use Magento\Setup\Mvc\View\Http\InjectTemplateListener; -use Zend\EventManager\EventInterface; -use Zend\ModuleManager\Feature\BootstrapListenerInterface; -use Zend\ModuleManager\Feature\ConfigProviderInterface; -use Zend\Mvc\ModuleRouteListener; -use Zend\Mvc\MvcEvent; +/** + * Laminas module declaration + */ class Module implements BootstrapListenerInterface, ConfigProviderInterface { /** - * {@inheritdoc} + * @inheritDoc */ public function onBootstrap(EventInterface $e) { - /** @var \Zend\Mvc\MvcEvent $e */ - /** @var \Zend\Mvc\Application $application */ + /** @var MvcEvent $e */ + /** @var \Laminas\Mvc\Application $application */ $application = $e->getApplication(); - /** @var \Zend\EventManager\EventManager $events */ + /** @var EventManager $events */ $events = $application->getEventManager(); - /** @var \Zend\EventManager\SharedEventManager $sharedEvents */ + /** @var \Laminas\EventManager\SharedEventManager $sharedEvents */ $sharedEvents = $events->getSharedManager(); $moduleRouteListener = new ModuleRouteListener(); $moduleRouteListener->attach($events); - // Override Zend\Mvc\View\Http\InjectTemplateListener + // Override Laminas\Mvc\View\Http\InjectTemplateListener // to process templates by Vendor/Module $injectTemplateListener = new InjectTemplateListener(); $sharedEvents->attach( - \Zend\Stdlib\DispatchableInterface::class, + DispatchableInterface::class, MvcEvent::EVENT_DISPATCH, [$injectTemplateListener, 'injectTemplate'], -89 ); $response = $e->getResponse(); - if ($response instanceof \Zend\Http\Response) { + if ($response instanceof \Laminas\Http\Response) { $headers = $response->getHeaders(); if ($headers) { $headers->addHeaderLine('Cache-Control', 'no-cache, no-store, must-revalidate'); @@ -52,7 +57,7 @@ public function onBootstrap(EventInterface $e) $headers->addHeaderLine('Expires', '1970-01-01'); $headers->addHeaderLine('X-Frame-Options: SAMEORIGIN'); $headers->addHeaderLine('X-Content-Type-Options: nosniff'); - /** @var \Zend\Http\Header\UserAgent $userAgentHeader */ + /** @var \Laminas\Http\Header\UserAgent $userAgentHeader */ $userAgentHeader = $e->getRequest()->getHeader('User-Agent'); $xssHeaderValue = $userAgentHeader && $userAgentHeader->getFieldValue() && strpos($userAgentHeader->getFieldValue(), XssProtection::IE_8_USER_AGENT) === false @@ -63,10 +68,11 @@ public function onBootstrap(EventInterface $e) } /** - * {@inheritdoc} + * @inheritDoc */ public function getConfig() { + // phpcs:disable $result = array_merge_recursive( include __DIR__ . '/../../../config/module.config.php', include __DIR__ . '/../../../config/router.config.php', @@ -82,6 +88,7 @@ public function getConfig() include __DIR__ . '/../../../config/languages.config.php', include __DIR__ . '/../../../config/marketplace.config.php' ); + // phpcs:enable return $result; } } diff --git a/setup/src/Magento/Setup/Module/ConnectionFactory.php b/setup/src/Magento/Setup/Module/ConnectionFactory.php index ccb1f819bb0a9..dafab58c49130 100644 --- a/setup/src/Magento/Setup/Module/ConnectionFactory.php +++ b/setup/src/Magento/Setup/Module/ConnectionFactory.php @@ -3,10 +3,11 @@ * Copyright © Magento, Inc. All rights reserved. * See COPYING.txt for license details. */ + namespace Magento\Setup\Module; use Magento\Framework\Model\ResourceModel\Type\Db\Pdo\Mysql; -use Zend\ServiceManager\ServiceLocatorInterface; +use Laminas\ServiceManager\ServiceLocatorInterface; /** * Connection adapter factory @@ -31,7 +32,7 @@ public function __construct(ServiceLocatorInterface $serviceLocator) } /** - * {@inheritdoc} + * @inheritDoc */ public function create(array $connectionConfig) { diff --git a/setup/src/Magento/Setup/Module/Di/App/Task/Operation/AppActionListGenerator.php b/setup/src/Magento/Setup/Module/Di/App/Task/Operation/AppActionListGenerator.php new file mode 100644 index 0000000000000..c3426dd0d9205 --- /dev/null +++ b/setup/src/Magento/Setup/Module/Di/App/Task/Operation/AppActionListGenerator.php @@ -0,0 +1,58 @@ +moduleReader = $moduleReader; + $this->configWriter = $configWriter; + } + + /** + * @inheritDoc + */ + public function doOperation() + { + $actionList = $this->moduleReader->getActionFiles(); + $this->configWriter->write( + 'app_action_list', + $actionList + ); + } + + /** + * @inheritDoc + */ + public function getName() + { + return 'App action list generation'; + } +} diff --git a/setup/src/Magento/Setup/Module/Di/App/Task/OperationFactory.php b/setup/src/Magento/Setup/Module/Di/App/Task/OperationFactory.php index 8b423a0adef83..607790e41421c 100644 --- a/setup/src/Magento/Setup/Module/Di/App/Task/OperationFactory.php +++ b/setup/src/Magento/Setup/Module/Di/App/Task/OperationFactory.php @@ -5,6 +5,12 @@ */ namespace Magento\Setup\Module\Di\App\Task; +use Magento\Setup\Module\Di\App\Task\Operation\AppActionListGenerator; +use Magento\Setup\Module\Di\App\Task\Operation\PluginListGenerator; + +/** + * Factory that creates list of OperationInterface classes + */ class OperationFactory { /** @@ -47,6 +53,11 @@ class OperationFactory */ const APPLICATION_CODE_GENERATOR = 'application_code_generator'; + /** + * Application action list generator + */ + const APPLICATION_ACTION_LIST_GENERATOR = 'application_action_list_generator'; + /** * Operations definitions * @@ -61,6 +72,7 @@ class OperationFactory self::INTERCEPTION_CACHE => \Magento\Setup\Module\Di\App\Task\Operation\InterceptionCache::class, self::REPOSITORY_GENERATOR => \Magento\Setup\Module\Di\App\Task\Operation\RepositoryGenerator::class, self::PROXY_GENERATOR => \Magento\Setup\Module\Di\App\Task\Operation\ProxyGenerator::class, + self::APPLICATION_ACTION_LIST_GENERATOR => AppActionListGenerator::class, ]; /** diff --git a/setup/src/Magento/Setup/Module/Di/Code/Reader/ClassesScanner.php b/setup/src/Magento/Setup/Module/Di/Code/Reader/ClassesScanner.php index b3a2f14aa922a..ae957adb09d17 100644 --- a/setup/src/Magento/Setup/Module/Di/Code/Reader/ClassesScanner.php +++ b/setup/src/Magento/Setup/Module/Di/Code/Reader/ClassesScanner.php @@ -3,6 +3,7 @@ * Copyright © Magento, Inc. All rights reserved. * See COPYING.txt for license details. */ +declare(strict_types=1); namespace Magento\Setup\Module\Di\Code\Reader; @@ -12,8 +13,6 @@ /** * Class ClassesScanner - * - * @package Magento\Setup\Module\Di\Code\Reader */ class ClassesScanner implements ClassesScannerInterface { @@ -100,7 +99,7 @@ public function getList($path) */ private function extract(\RecursiveIteratorIterator $recursiveIterator) { - $classes = [[]]; + $classes = []; foreach ($recursiveIterator as $fileItem) { /** @var $fileItem \SplFileInfo */ if ($fileItem->isDir() || $fileItem->getExtension() !== 'php' || $fileItem->getBasename()[0] == '.') { @@ -113,28 +112,29 @@ private function extract(\RecursiveIteratorIterator $recursiveIterator) } } $fileScanner = new FileClassScanner($fileItemPath); - $classNames = $fileScanner->getClassNames(); - $this->includeClasses($classNames, $fileItemPath); - $classes [] = $classNames; + $className = $fileScanner->getClassName(); + if (!empty($className)) { + $this->includeClass($className, $fileItemPath); + $classes[] = $className; + } } - return array_merge(...$classes); + + return $classes; } /** - * Include classes from file path + * Include class from file path. * - * @param array $classNames + * @param string $className * @param string $fileItemPath * @return bool Whether the class is included or not */ - private function includeClasses(array $classNames, $fileItemPath) + private function includeClass(string $className, string $fileItemPath): bool { - foreach ($classNames as $className) { - if (!class_exists($className)) { - // phpcs:ignore - require_once $fileItemPath; - return true; - } + if (!class_exists($className)) { + // phpcs:ignore + require_once $fileItemPath; + return true; } return false; } diff --git a/setup/src/Magento/Setup/Module/Di/Code/Reader/FileClassScanner.php b/setup/src/Magento/Setup/Module/Di/Code/Reader/FileClassScanner.php index 9a91006564d7f..c5e5230d253cc 100644 --- a/setup/src/Magento/Setup/Module/Di/Code/Reader/FileClassScanner.php +++ b/setup/src/Magento/Setup/Module/Di/Code/Reader/FileClassScanner.php @@ -3,13 +3,12 @@ * Copyright © Magento, Inc. All rights reserved. * See COPYING.txt for license details. */ +declare(strict_types=1); namespace Magento\Setup\Module\Di\Code\Reader; /** * Class FileClassScanner - * - * @package Magento\Setup\Module\Di\Code\Reader */ class FileClassScanner { @@ -20,9 +19,10 @@ class FileClassScanner ]; private const ALLOWED_OPEN_BRACES_TOKENS = [ - T_CURLY_OPEN => true, + T_CURLY_OPEN => true, T_DOLLAR_OPEN_CURLY_BRACES => true, - T_STRING_VARNAME => true]; + T_STRING_VARNAME => true + ]; /** * The filename of the file to introspect @@ -32,11 +32,11 @@ class FileClassScanner private $filename; /** - * The list of classes found in the file. + * The class name found in the file. * * @var bool */ - private $classNames = false; + private $className = false; /** * @var array @@ -75,6 +75,19 @@ public function getFileContents() return file_get_contents($this->filename); } + /** + * Retrieves the first class found in a class file. + * + * @return string + */ + public function getClassName(): string + { + if ($this->className === false) { + $this->className = $this->extract(); + } + return $this->className; + } + /** * Extracts the fully qualified class name from a file. * @@ -85,11 +98,10 @@ public function getFileContents() * * @SuppressWarnings(PHPMD.CyclomaticComplexity) * @SuppressWarnings(PHPMD.NPathComplexity) - * @return array + * @return string */ - private function extract() + private function extract(): string { - $classes = []; $namespaceParts = []; $class = ''; $triggerClass = false; @@ -117,6 +129,9 @@ private function extract() } $namespaceParts[] = $token[1]; + // `class` token is not used with a valid class name + } elseif ($triggerClass && !$tokenIsArray) { + $triggerClass = false; // The class keyword was found in the last loop } elseif ($triggerClass && $token[0] === T_STRING) { $triggerClass = false; @@ -125,27 +140,26 @@ private function extract() switch ($token[0]) { case T_NAMESPACE: - // Current loop contains the namespace keyword. Between this and the semicolon is the namespace + // Current loop contains the namespace keyword. Between this and the semicolon is the namespace $triggerNamespace = true; $namespaceParts = []; $bracedNamespace = $this->isBracedNamespace($index); break; case T_CLASS: - // Current loop contains the class keyword. Next loop will have the class name itself. + // Current loop contains the class keyword. Next loop will have the class name itself. if ($braceLevel == 0 || ($bracedNamespace && $braceLevel == 1)) { $triggerClass = true; } break; } - // We have a class name, let's concatenate and store it! + // We have a class name, let's concatenate and return it! if ($class !== '') { $fqClassName = trim(join('', $namespaceParts)) . trim($class); - $classes[] = $fqClassName; - $class = ''; + return $fqClassName; } } - return $classes; + return $class; } /** @@ -173,19 +187,4 @@ private function isBracedNamespace($index) } throw new InvalidFileException('Could not find namespace termination'); } - - /** - * Retrieves the first class found in a class file. - * - * The return value is in an array format so it retains the same usage as the FileScanner. - * - * @return array - */ - public function getClassNames() - { - if ($this->classNames === false) { - $this->classNames = $this->extract(); - } - return $this->classNames; - } } diff --git a/setup/src/Magento/Setup/Module/Di/Code/Reader/FileScanner.php b/setup/src/Magento/Setup/Module/Di/Code/Reader/FileScanner.php index be52fc3d24dfd..27ac2b2794bb9 100644 --- a/setup/src/Magento/Setup/Module/Di/Code/Reader/FileScanner.php +++ b/setup/src/Magento/Setup/Module/Di/Code/Reader/FileScanner.php @@ -7,9 +7,11 @@ namespace Magento\Setup\Module\Di\Code\Reader; /** + * FileScanner code reader + * * @SuppressWarnings(PHPMD) */ -class FileScanner extends \Zend\Code\Scanner\FileScanner +class FileScanner extends \Laminas\Code\Scanner\FileScanner { /** * @var int @@ -17,7 +19,7 @@ class FileScanner extends \Zend\Code\Scanner\FileScanner private $tokenType; /** - * {@inheritdoc} + * @inheritDoc */ protected function scan() { @@ -26,7 +28,7 @@ protected function scan() } if (!$this->tokens) { - throw new \Zend\Code\Exception\RuntimeException('No tokens were provided'); + throw new \Laminas\Code\Exception\RuntimeException('No tokens were provided'); } /** @@ -106,6 +108,7 @@ protected function scan() return $infoIndex; }; + // phpcs:disable /** * START FINITE STATE MACHINE FOR SCANNING TOKENS */ @@ -357,5 +360,6 @@ protected function scan() * END FINITE STATE MACHINE FOR SCANNING TOKENS */ $this->isScanned = true; + // phpcs:enable } } diff --git a/setup/src/Magento/Setup/Module/Di/Code/Scanner/PhpScanner.php b/setup/src/Magento/Setup/Module/Di/Code/Scanner/PhpScanner.php index 1cd242acbe50b..acb55e29afddd 100644 --- a/setup/src/Magento/Setup/Module/Di/Code/Scanner/PhpScanner.php +++ b/setup/src/Magento/Setup/Module/Di/Code/Scanner/PhpScanner.php @@ -3,18 +3,18 @@ * Copyright © Magento, Inc. All rights reserved. * See COPYING.txt for license details. */ +declare(strict_types=1); + namespace Magento\Setup\Module\Di\Code\Scanner; use Magento\Framework\Api\Code\Generator\ExtensionAttributesGenerator; use Magento\Framework\Api\Code\Generator\ExtensionAttributesInterfaceGenerator; use Magento\Framework\ObjectManager\Code\Generator\Factory as FactoryGenerator; +use Magento\Framework\Reflection\TypeProcessor; use Magento\Setup\Module\Di\Compiler\Log\Log; -use \Magento\Framework\Reflection\TypeProcessor; /** - * Class PhpScanner - * - * @package Magento\Setup\Module\Di\Code\Scanner + * Finds factory and extension attributes classes which require auto-generation. */ class PhpScanner implements ScannerInterface { @@ -50,18 +50,31 @@ public function __construct(Log $log, TypeProcessor $typeProcessor = null) * @param string $entityType * @return string[] */ - protected function _findMissingClasses($file, $classReflection, $methodName, $entityType) + private function findMissingFactories($file, $classReflection, $methodName, $entityType) { $missingClasses = []; - if ($classReflection->hasMethod($methodName)) { - $constructor = $classReflection->getMethod($methodName); - $parameters = $constructor->getParameters(); - /** @var $parameter \ReflectionParameter */ - foreach ($parameters as $parameter) { - preg_match('/\[\s\<\w+?>\s([\w\\\\]+)/s', $parameter->__toString(), $matches); - if (isset($matches[1]) && substr($matches[1], -strlen($entityType)) == $entityType) { - $missingClassName = $matches[1]; - if ($this->shouldGenerateClass($missingClassName, $entityType, $file)) { + if (!$classReflection->hasMethod($methodName)) { + return $missingClasses; + } + + $factorySuffix = '\\' . ucfirst(FactoryGenerator::ENTITY_TYPE); + $constructor = $classReflection->getMethod($methodName); + $parameters = $constructor->getParameters(); + /** @var $parameter \ReflectionParameter */ + foreach ($parameters as $parameter) { + preg_match('/\[\s\<\w+?>\s([\w\\\\]+)/s', $parameter->__toString(), $matches); + if (isset($matches[1]) && substr($matches[1], -strlen($entityType)) == $entityType) { + $missingClassName = $matches[1]; + if ($this->shouldGenerateClass($missingClassName, $entityType, $file)) { + + if (substr($missingClassName, -strlen($factorySuffix)) == $factorySuffix) { + $entityName = rtrim(substr($missingClassName, 0, -strlen($factorySuffix)), '\\'); + $this->_log->add( + Log::CONFIGURATION_ERROR, + $missingClassName, + 'Invalid Factory declaration for class ' . $entityName . ' in file ' . $file + ); + } else { $missingClasses[] = $missingClassName; } } @@ -110,24 +123,12 @@ protected function getSourceClassName($missingClassName, $entityType) */ protected function _fetchFactories($reflectionClass, $file) { - $factorySuffix = '\\' . ucfirst(FactoryGenerator::ENTITY_TYPE); - $absentFactories = $this->_findMissingClasses( + $absentFactories = $this->findMissingFactories( $file, $reflectionClass, '__construct', ucfirst(FactoryGenerator::ENTITY_TYPE) ); - foreach ($absentFactories as $key => $absentFactory) { - if (substr($absentFactory, -strlen($factorySuffix)) == $factorySuffix) { - $entityName = rtrim(substr($absentFactory, 0, -strlen($factorySuffix)), '\\'); - $this->_log->add( - Log::CONFIGURATION_ERROR, - $absentFactory, - 'Invalid Factory declaration for class ' . $entityName . ' in file ' . $file - ); - unset($absentFactories[$key]); - } - } return $absentFactories; } @@ -145,26 +146,24 @@ protected function _fetchMissingExtensionAttributesClasses($reflectionClass, $fi $entityType = ucfirst(\Magento\Framework\Api\Code\Generator\ExtensionAttributesInterfaceGenerator::ENTITY_TYPE); if ($reflectionClass->hasMethod($methodName) && $reflectionClass->isInterface()) { $returnType = $this->typeProcessor->getGetterReturnType( - (new \Zend\Code\Reflection\ClassReflection($reflectionClass->getName()))->getMethod($methodName) + (new \Laminas\Code\Reflection\ClassReflection($reflectionClass->getName()))->getMethod($methodName) ); $missingClassName = $returnType['type']; if ($this->shouldGenerateClass($missingClassName, $entityType, $file)) { $missingExtensionInterfaces[] = $missingClassName; + + $extension = rtrim(substr($missingClassName, 0, -strlen('Interface')), '\\'); + if (!class_exists($extension)) { + $missingExtensionInterfaces[] = $extension; + } + $extensionFactory = $extension . 'Factory'; + if (!class_exists($extensionFactory)) { + $missingExtensionInterfaces[] = $extensionFactory; + } } } - $missingExtensionClasses = []; - $missingExtensionFactories = []; - foreach ($missingExtensionInterfaces as $missingExtensionInterface) { - $extension = rtrim(substr($missingExtensionInterface, 0, -strlen('Interface')), '\\'); - if (!class_exists($extension)) { - $missingExtensionClasses[] = $extension; - } - $extensionFactory = $extension . 'Factory'; - if (!class_exists($extensionFactory)) { - $missingExtensionFactories[] = $extensionFactory; - } - } - return array_merge($missingExtensionInterfaces, $missingExtensionClasses, $missingExtensionFactories); + + return $missingExtensionInterfaces; } /** @@ -178,11 +177,11 @@ public function collectEntities(array $files) { $output = [[]]; foreach ($files as $file) { - $classes = $this->_getDeclaredClasses($file); + $classes = $this->getDeclaredClasses($file); foreach ($classes as $className) { $reflectionClass = new \ReflectionClass($className); - $output [] = $this->_fetchFactories($reflectionClass, $file); - $output [] = $this->_fetchMissingExtensionAttributesClasses($reflectionClass, $file); + $output[] = $this->_fetchFactories($reflectionClass, $file); + $output[] = $this->_fetchMissingExtensionAttributesClasses($reflectionClass, $file); } } return array_unique(array_merge(...$output)); @@ -211,23 +210,30 @@ protected function _fetchNamespace($tokenIterator, $count, $tokens) } /** - * Fetch class names from tokenized PHP file + * Fetches class name from tokenized PHP file. * * @param string $namespace * @param int $tokenIterator * @param int $count * @param array $tokens - * @return array + * @return string|null */ - protected function _fetchClasses($namespace, $tokenIterator, $count, $tokens) + private function fetchClass($namespace, $tokenIterator, $count, $tokens):? string { - $classes = []; + // anonymous classes should be omitted + if (is_array($tokens[$tokenIterator - 2]) && $tokens[$tokenIterator - 2][0] === T_NEW) { + return null; + } + for ($tokenOffset = $tokenIterator + 1; $tokenOffset < $count; ++$tokenOffset) { - if ($tokens[$tokenOffset] === '{') { - $classes[] = $namespace . "\\" . $tokens[$tokenIterator + 2][1]; + if ($tokens[$tokenOffset] !== '{') { + continue; } + + return $namespace . "\\" . $tokens[$tokenIterator + 2][1]; } - return $classes; + + return null; } /** @@ -236,9 +242,9 @@ protected function _fetchClasses($namespace, $tokenIterator, $count, $tokens) * @param string $file * @return array */ - protected function _getDeclaredClasses($file) + private function getDeclaredClasses($file): array { - $classes = [[]]; + $classes = []; $namespaceParts = []; // phpcs:ignore $tokens = token_get_all(file_get_contents($file)); @@ -252,10 +258,13 @@ protected function _getDeclaredClasses($file) if (($tokens[$tokenIterator][0] == T_CLASS || $tokens[$tokenIterator][0] == T_INTERFACE) && $tokens[$tokenIterator - 1][0] != T_DOUBLE_COLON ) { - $classes[] = $this->_fetchClasses(join('', $namespaceParts), $tokenIterator, $count, $tokens); + $class = $this->fetchClass(join('', $namespaceParts), $tokenIterator, $count, $tokens); + if ($class !== null && !in_array($class, $classes)) { + $classes[] = $class; + } } } - return array_unique(array_merge(...$classes)); + return $classes; } /** diff --git a/setup/src/Magento/Setup/Module/ResourceFactory.php b/setup/src/Magento/Setup/Module/ResourceFactory.php index 7d26226cd6929..0948542f29e0e 100644 --- a/setup/src/Magento/Setup/Module/ResourceFactory.php +++ b/setup/src/Magento/Setup/Module/ResourceFactory.php @@ -3,24 +3,27 @@ * Copyright © Magento, Inc. All rights reserved. * See COPYING.txt for license details. */ + namespace Magento\Setup\Module; +use Laminas\ServiceManager\ServiceLocatorInterface; +use Magento\Framework\App\DeploymentConfig; use Magento\Framework\App\ResourceConnection; use Magento\Setup\Module\Setup\ResourceConfig; -use Zend\ServiceManager\ServiceLocatorInterface; +/** + * Factory for Magento\Framework\App\ResourceConnection + */ class ResourceFactory { /** - * Zend Framework's service locator + * Laminas Framework's service locator * * @var ServiceLocatorInterface */ protected $serviceLocator; /** - * Constructor - * * @param ServiceLocatorInterface $serviceLocator */ public function __construct(ServiceLocatorInterface $serviceLocator) @@ -29,17 +32,20 @@ public function __construct(ServiceLocatorInterface $serviceLocator) } /** - * @param \Magento\Framework\App\DeploymentConfig $deploymentConfig - * @return Resource + * Create object + * + * @param DeploymentConfig $deploymentConfig + * @return ResourceConnection */ - public function create(\Magento\Framework\App\DeploymentConfig $deploymentConfig) + public function create(DeploymentConfig $deploymentConfig) { - $connectionFactory = $this->serviceLocator->get(\Magento\Setup\Module\ConnectionFactory::class); + $connectionFactory = $this->serviceLocator->get(ConnectionFactory::class); $resource = new ResourceConnection( new ResourceConfig(), $connectionFactory, $deploymentConfig ); + return $resource; } } diff --git a/setup/src/Magento/Setup/Mvc/Bootstrap/InitParamListener.php b/setup/src/Magento/Setup/Mvc/Bootstrap/InitParamListener.php index 709be46eac42b..dc564c3a8f7c5 100644 --- a/setup/src/Magento/Setup/Mvc/Bootstrap/InitParamListener.php +++ b/setup/src/Magento/Setup/Mvc/Bootstrap/InitParamListener.php @@ -12,16 +12,16 @@ use Magento\Framework\App\State; use Magento\Framework\Filesystem; use Magento\Framework\Shell\ComplexParameter; -use Zend\Console\Request; -use Zend\EventManager\EventManagerInterface; -use Zend\EventManager\ListenerAggregateInterface; -use Zend\Mvc\Application; -use Zend\Mvc\MvcEvent; -use Zend\Router\Http\RouteMatch; -use Zend\ServiceManager\FactoryInterface; -use Zend\ServiceManager\ServiceLocatorInterface; -use Zend\Stdlib\RequestInterface; -use Zend\Uri\UriInterface; +use Laminas\Console\Request; +use Laminas\EventManager\EventManagerInterface; +use Laminas\EventManager\ListenerAggregateInterface; +use Laminas\Mvc\Application; +use Laminas\Mvc\MvcEvent; +use Laminas\Router\Http\RouteMatch; +use Laminas\ServiceManager\FactoryInterface; +use Laminas\ServiceManager\ServiceLocatorInterface; +use Laminas\Stdlib\RequestInterface; +use Laminas\Uri\UriInterface; /** * A listener that injects relevant Magento initialization parameters and initializes filesystem @@ -37,7 +37,7 @@ class InitParamListener implements ListenerAggregateInterface, FactoryInterface const BOOTSTRAP_PARAM = 'magento-init-params'; /** - * @var \Zend\Stdlib\CallbackHandler[] + * @var \Laminas\Stdlib\CallbackHandler[] */ private $listeners = []; @@ -54,8 +54,8 @@ class InitParamListener implements ListenerAggregateInterface, FactoryInterface /** * @inheritdoc * - * The $priority argument is added to support latest versions of Zend Event Manager. - * Starting from Zend Event Manager 3.0.0 release the ListenerAggregateInterface::attach() + * The $priority argument is added to support latest versions of Laminas Event Manager. + * Starting from Laminas Event Manager 3.0.0 release the ListenerAggregateInterface::attach() * supports the `priority` argument. * * @param EventManagerInterface $events @@ -114,8 +114,8 @@ public function onBootstrap(MvcEvent $e) /** * Check if user logged-in and has permissions * - * @param \Zend\Mvc\MvcEvent $event - * @return false|\Zend\Http\Response + * @param \Laminas\Mvc\MvcEvent $event + * @return false|\Laminas\Http\Response * * @throws \Magento\Framework\Exception\LocalizedException * @throws \Magento\Setup\Exception @@ -162,7 +162,7 @@ public function authPreDispatch($event) !$adminSession->isAllowed('Magento_Backend::setup_wizard') ) { $adminSession->destroy(); - /** @var \Zend\Http\Response $response */ + /** @var \Laminas\Http\Response $response */ $response = $event->getResponse(); $baseUrl = Http::getDistroBaseUrlPath($_SERVER); $response->getHeaders()->addHeaderLine('Location', $baseUrl . 'index.php/session/unlogin'); diff --git a/setup/src/Magento/Setup/Mvc/View/Http/InjectTemplateListener.php b/setup/src/Magento/Setup/Mvc/View/Http/InjectTemplateListener.php index 5d8d903c558d6..537acc97ae385 100644 --- a/setup/src/Magento/Setup/Mvc/View/Http/InjectTemplateListener.php +++ b/setup/src/Magento/Setup/Mvc/View/Http/InjectTemplateListener.php @@ -6,10 +6,13 @@ namespace Magento\Setup\Mvc\View\Http; -use Zend\Mvc\MvcEvent; -use Zend\Mvc\View\Http\InjectTemplateListener as ZendInjectTemplateListener; +use Laminas\Mvc\MvcEvent; +use Laminas\Mvc\View\Http\InjectTemplateListener as LaminasInjectTemplateListener; -class InjectTemplateListener extends ZendInjectTemplateListener +/** + * InjectTemplateListener for HTTP request + */ +class InjectTemplateListener extends LaminasInjectTemplateListener { /** * Determine the top-level namespace of the controller @@ -30,6 +33,8 @@ protected function deriveModuleNamespace($controller) } /** + * Get controller sub-namespace + * * @param string $namespace * @return string */ diff --git a/setup/src/Magento/Setup/Test/Unit/Console/Command/DiCompileCommandTest.php b/setup/src/Magento/Setup/Test/Unit/Console/Command/DiCompileCommandTest.php index ca94a1b6fd559..1eb2388fe1d53 100644 --- a/setup/src/Magento/Setup/Test/Unit/Console/Command/DiCompileCommandTest.php +++ b/setup/src/Magento/Setup/Test/Unit/Console/Command/DiCompileCommandTest.php @@ -143,7 +143,7 @@ public function testExecute() ->with(\Symfony\Component\Console\Helper\ProgressBar::class) ->willReturn($progressBar); - $this->managerMock->expects($this->exactly(7))->method('addOperation') + $this->managerMock->expects($this->exactly(8))->method('addOperation') ->withConsecutive( [OperationFactory::PROXY_GENERATOR, []], [OperationFactory::REPOSITORY_GENERATOR, $this->anything()], @@ -160,7 +160,8 @@ public function testExecute() })], [OperationFactory::INTERCEPTION, $this->anything()], [OperationFactory::AREA_CONFIG_GENERATOR, $this->anything()], - [OperationFactory::INTERCEPTION_CACHE, $this->anything()] + [OperationFactory::INTERCEPTION_CACHE, $this->anything()], + [OperationFactory::APPLICATION_ACTION_LIST_GENERATOR, $this->anything()] ) ; diff --git a/setup/src/Magento/Setup/Test/Unit/Console/Command/ModuleEnableDisableCommandTest.php b/setup/src/Magento/Setup/Test/Unit/Console/Command/ModuleEnableDisableCommandTest.php index 4ff2b0c7bca58..8eb13a9c1ec5e 100644 --- a/setup/src/Magento/Setup/Test/Unit/Console/Command/ModuleEnableDisableCommandTest.php +++ b/setup/src/Magento/Setup/Test/Unit/Console/Command/ModuleEnableDisableCommandTest.php @@ -53,23 +53,24 @@ protected function setUp() { $this->objectManagerProviderMock = $this->createMock(\Magento\Setup\Model\ObjectManagerProvider::class); $objectManager = $this->getMockForAbstractClass(\Magento\Framework\ObjectManagerInterface::class); - $this->objectManagerProviderMock->expects($this->any()) + $this->objectManagerProviderMock ->method('get') - ->will($this->returnValue($objectManager)); + ->willReturn($objectManager); $this->statusMock = $this->createMock(\Magento\Framework\Module\Status::class); $this->cacheMock = $this->createMock(\Magento\Framework\App\Cache::class); $this->cleanupFilesMock = $this->createMock(\Magento\Framework\App\State\CleanupFiles::class); $this->fullModuleListMock = $this->createMock(\Magento\Framework\Module\FullModuleList::class); $this->deploymentConfigMock = $this->createMock(\Magento\Framework\App\DeploymentConfig::class); $this->generatedFiles = $this->createMock(\Magento\Framework\Code\GeneratedFiles::class); - $objectManager->expects($this->any()) - ->method('get') - ->will($this->returnValueMap([ - [\Magento\Framework\Module\Status::class, $this->statusMock], - [\Magento\Framework\App\Cache::class, $this->cacheMock], - [\Magento\Framework\App\State\CleanupFiles::class, $this->cleanupFilesMock], - [\Magento\Framework\Module\FullModuleList::class, $this->fullModuleListMock], - ])); + $objectManager->method('get') + ->willReturnMap( + [ + [\Magento\Framework\Module\Status::class, $this->statusMock], + [\Magento\Framework\App\Cache::class, $this->cacheMock], + [\Magento\Framework\App\State\CleanupFiles::class, $this->cleanupFilesMock], + [\Magento\Framework\Module\FullModuleList::class, $this->fullModuleListMock], + ] + ); } /** @@ -189,7 +190,7 @@ public function testExecuteAll($isEnable, $expectedMessage) if ($isEnable) { $this->deploymentConfigMock->expects($this->once()) ->method('isAvailable') - ->willReturn(['Magento_Module1']); + ->willReturn(true); } else { $this->deploymentConfigMock->expects($this->never()) ->method('isAvailable'); diff --git a/setup/src/Magento/Setup/Test/Unit/Console/CommandListTest.php b/setup/src/Magento/Setup/Test/Unit/Console/CommandListTest.php index 3f3e06ac9d732..d63461975f21e 100644 --- a/setup/src/Magento/Setup/Test/Unit/Console/CommandListTest.php +++ b/setup/src/Magento/Setup/Test/Unit/Console/CommandListTest.php @@ -16,13 +16,13 @@ class CommandListTest extends \PHPUnit\Framework\TestCase private $commandList; /** - * @var \PHPUnit_Framework_MockObject_MockObject|\Zend\ServiceManager\ServiceManager + * @var \PHPUnit_Framework_MockObject_MockObject|\Laminas\ServiceManager\ServiceManager */ private $serviceManager; public function setUp() { - $this->serviceManager = $this->createMock(\Zend\ServiceManager\ServiceManager::class); + $this->serviceManager = $this->createMock(\Laminas\ServiceManager\ServiceManager::class); $this->commandList = new CommandList($this->serviceManager); } diff --git a/setup/src/Magento/Setup/Test/Unit/Console/CompilerPreparationTest.php b/setup/src/Magento/Setup/Test/Unit/Console/CompilerPreparationTest.php index 7bd11c64f93ac..212759374847d 100644 --- a/setup/src/Magento/Setup/Test/Unit/Console/CompilerPreparationTest.php +++ b/setup/src/Magento/Setup/Test/Unit/Console/CompilerPreparationTest.php @@ -11,7 +11,7 @@ use Magento\Setup\Mvc\Bootstrap\InitParamListener; use Magento\Framework\Filesystem\Driver\File; use Symfony\Component\Console\Input\ArgvInput; -use Zend\ServiceManager\ServiceManager; +use Laminas\ServiceManager\ServiceManager; use Magento\Setup\Console\CompilerPreparation; use PHPUnit_Framework_MockObject_MockObject as Mock; diff --git a/setup/src/Magento/Setup/Test/Unit/Controller/AddDatabaseTest.php b/setup/src/Magento/Setup/Test/Unit/Controller/AddDatabaseTest.php index 6e8306965c6ec..ec346fa6353d9 100644 --- a/setup/src/Magento/Setup/Test/Unit/Controller/AddDatabaseTest.php +++ b/setup/src/Magento/Setup/Test/Unit/Controller/AddDatabaseTest.php @@ -15,7 +15,7 @@ public function testIndexAction() /** @var $controller AddDatabase */ $controller = new AddDatabase(); $viewModel = $controller->indexAction(); - $this->assertInstanceOf(\Zend\View\Model\ViewModel::class, $viewModel); + $this->assertInstanceOf(\Laminas\View\Model\ViewModel::class, $viewModel); $this->assertTrue($viewModel->terminate()); } } diff --git a/setup/src/Magento/Setup/Test/Unit/Controller/BackupActionItemsTest.php b/setup/src/Magento/Setup/Test/Unit/Controller/BackupActionItemsTest.php index f6004d808ec6f..93912c3062097 100644 --- a/setup/src/Magento/Setup/Test/Unit/Controller/BackupActionItemsTest.php +++ b/setup/src/Magento/Setup/Test/Unit/Controller/BackupActionItemsTest.php @@ -67,11 +67,11 @@ public function setUp() $this->filesystem ); - $request = $this->createMock(\Zend\Http\PhpEnvironment\Request::class); - $response = $this->createMock(\Zend\Http\PhpEnvironment\Response::class); - $routeMatch = $this->createMock(\Zend\Mvc\Router\RouteMatch::class); + $request = $this->createMock(\Laminas\Http\PhpEnvironment\Request::class); + $response = $this->createMock(\Laminas\Http\PhpEnvironment\Response::class); + $routeMatch = $this->createMock(\Laminas\Mvc\Router\RouteMatch::class); - $mvcEvent = $this->createMock(\Zend\Mvc\MvcEvent::class); + $mvcEvent = $this->createMock(\Laminas\Mvc\MvcEvent::class); $mvcEvent->expects($this->any())->method('setRequest')->with($request)->willReturn($mvcEvent); $mvcEvent->expects($this->any())->method('setResponse')->with($response)->willReturn($mvcEvent); $mvcEvent->expects($this->any())->method('setTarget')->with($this->controller)->willReturn($mvcEvent); @@ -91,7 +91,7 @@ public function testCheckAction() $this->directoryList->expects($this->once())->method('getPath')->willReturn(__DIR__); $this->filesystem->expects($this->once())->method('validateAvailableDiscSpace'); $jsonModel = $this->controller->checkAction(); - $this->assertInstanceOf(\Zend\View\Model\JsonModel::class, $jsonModel); + $this->assertInstanceOf(\Laminas\View\Model\JsonModel::class, $jsonModel); $variables = $jsonModel->getVariables(); $this->assertArrayHasKey('responseType', $variables); $this->assertEquals(ResponseTypeInterface::RESPONSE_TYPE_SUCCESS, $variables['responseType']); @@ -106,7 +106,7 @@ public function testCheckActionWithError() $this->throwException(new \Exception("Test error message")) ); $jsonModel = $this->controller->checkAction(); - $this->assertInstanceOf(\Zend\View\Model\JsonModel::class, $jsonModel); + $this->assertInstanceOf(\Laminas\View\Model\JsonModel::class, $jsonModel); $variables = $jsonModel->getVariables(); $this->assertArrayHasKey('responseType', $variables); $this->assertEquals(ResponseTypeInterface::RESPONSE_TYPE_ERROR, $variables['responseType']); @@ -118,7 +118,7 @@ public function testCreateAction() { $this->backupRollback->expects($this->once())->method('dbBackup')->willReturn('backup/path/'); $jsonModel = $this->controller->createAction(); - $this->assertInstanceOf(\Zend\View\Model\JsonModel::class, $jsonModel); + $this->assertInstanceOf(\Laminas\View\Model\JsonModel::class, $jsonModel); $variables = $jsonModel->getVariables(); $this->assertArrayHasKey('responseType', $variables); $this->assertEquals(ResponseTypeInterface::RESPONSE_TYPE_SUCCESS, $variables['responseType']); @@ -129,6 +129,6 @@ public function testCreateAction() public function testIndexAction() { $model = $this->controller->indexAction(); - $this->assertInstanceOf(\Zend\View\Model\ViewModel::class, $model); + $this->assertInstanceOf(\Laminas\View\Model\ViewModel::class, $model); } } diff --git a/setup/src/Magento/Setup/Test/Unit/Controller/CompleteBackupTest.php b/setup/src/Magento/Setup/Test/Unit/Controller/CompleteBackupTest.php index ef3290785875f..755552e86de79 100644 --- a/setup/src/Magento/Setup/Test/Unit/Controller/CompleteBackupTest.php +++ b/setup/src/Magento/Setup/Test/Unit/Controller/CompleteBackupTest.php @@ -25,10 +25,10 @@ public function setUp() public function testIndexAction() { $viewModel = $this->controller->indexAction(); - $this->assertInstanceOf(\Zend\View\Model\ViewModel::class, $viewModel); + $this->assertInstanceOf(\Laminas\View\Model\ViewModel::class, $viewModel); $this->assertSame('/error/404.phtml', $viewModel->getTemplate()); $this->assertSame( - \Zend\Http\Response::STATUS_CODE_404, + \Laminas\Http\Response::STATUS_CODE_404, $this->controller->getResponse()->getStatusCode() ); } @@ -36,7 +36,7 @@ public function testIndexAction() public function testProgressAction() { $viewModel = $this->controller->progressAction(); - $this->assertInstanceOf(\Zend\View\Model\ViewModel::class, $viewModel); + $this->assertInstanceOf(\Laminas\View\Model\ViewModel::class, $viewModel); $this->assertTrue($viewModel->terminate()); $this->assertSame('/magento/setup/complete-backup/progress.phtml', $viewModel->getTemplate()); } diff --git a/setup/src/Magento/Setup/Test/Unit/Controller/CreateAdminAccountTest.php b/setup/src/Magento/Setup/Test/Unit/Controller/CreateAdminAccountTest.php index b8df922fcc93c..c33e56333cf8d 100644 --- a/setup/src/Magento/Setup/Test/Unit/Controller/CreateAdminAccountTest.php +++ b/setup/src/Magento/Setup/Test/Unit/Controller/CreateAdminAccountTest.php @@ -14,7 +14,7 @@ public function testIndexAction() { $controller = new CreateAdminAccount(); $viewModel = $controller->indexAction(); - $this->assertInstanceOf(\Zend\View\Model\ViewModel::class, $viewModel); + $this->assertInstanceOf(\Laminas\View\Model\ViewModel::class, $viewModel); $this->assertTrue($viewModel->terminate()); } } diff --git a/setup/src/Magento/Setup/Test/Unit/Controller/CreateBackupTest.php b/setup/src/Magento/Setup/Test/Unit/Controller/CreateBackupTest.php index fd3b36b25525c..eb7d7cee9a2c1 100644 --- a/setup/src/Magento/Setup/Test/Unit/Controller/CreateBackupTest.php +++ b/setup/src/Magento/Setup/Test/Unit/Controller/CreateBackupTest.php @@ -15,7 +15,7 @@ public function testIndexAction() /** @var $controller CreateBackup */ $controller = new CreateBackup(); $viewModel = $controller->indexAction(); - $this->assertInstanceOf(\Zend\View\Model\ViewModel::class, $viewModel); + $this->assertInstanceOf(\Laminas\View\Model\ViewModel::class, $viewModel); $this->assertTrue($viewModel->terminate()); } } diff --git a/setup/src/Magento/Setup/Test/Unit/Controller/CustomizeYourStoreTest.php b/setup/src/Magento/Setup/Test/Unit/Controller/CustomizeYourStoreTest.php index bb4e0c8b9291b..e7b5df737eee5 100644 --- a/setup/src/Magento/Setup/Test/Unit/Controller/CustomizeYourStoreTest.php +++ b/setup/src/Magento/Setup/Test/Unit/Controller/CustomizeYourStoreTest.php @@ -71,7 +71,7 @@ public function testIndexAction($expected, $withSampleData) $viewModel = $this->controller->indexAction(); - $this->assertInstanceOf(\Zend\View\Model\ViewModel::class, $viewModel); + $this->assertInstanceOf(\Laminas\View\Model\ViewModel::class, $viewModel); $this->assertTrue($viewModel->terminate()); $variables = $viewModel->getVariables(); @@ -109,7 +109,7 @@ public function indexActionDataProvider() public function testDefaultTimeZoneAction() { $jsonModel = $this->controller->defaultTimeZoneAction(); - $this->assertInstanceOf(\Zend\View\Model\JsonModel::class, $jsonModel); + $this->assertInstanceOf(\Laminas\View\Model\JsonModel::class, $jsonModel); $this->assertArrayHasKey('defaultTimeZone', $jsonModel->getVariables()); } } diff --git a/setup/src/Magento/Setup/Test/Unit/Controller/DataOptionTest.php b/setup/src/Magento/Setup/Test/Unit/Controller/DataOptionTest.php index 89831dd1471f5..b30a23a92c607 100644 --- a/setup/src/Magento/Setup/Test/Unit/Controller/DataOptionTest.php +++ b/setup/src/Magento/Setup/Test/Unit/Controller/DataOptionTest.php @@ -16,17 +16,17 @@ class DataOptionTest extends \PHPUnit\Framework\TestCase private $uninstallCollector; /** - * @var \PHPUnit_Framework_MockObject_MockObject|\Zend\Http\PhpEnvironment\Request + * @var \PHPUnit_Framework_MockObject_MockObject|\Laminas\Http\PhpEnvironment\Request */ private $request; /** - * @var \PHPUnit_Framework_MockObject_MockObject|\Zend\Http\PhpEnvironment\Response + * @var \PHPUnit_Framework_MockObject_MockObject|\Laminas\Http\PhpEnvironment\Response */ private $response; /** - * @var \Zend\Mvc\MvcEvent|\PHPUnit_Framework_MockObject_MockObject + * @var \Laminas\Mvc\MvcEvent|\PHPUnit_Framework_MockObject_MockObject */ private $mvcEvent; @@ -37,14 +37,14 @@ class DataOptionTest extends \PHPUnit\Framework\TestCase public function setUp() { - $this->request = $this->createMock(\Zend\Http\PhpEnvironment\Request::class); - $this->response = $this->createMock(\Zend\Http\PhpEnvironment\Response::class); - $routeMatch = $this->createMock(\Zend\Mvc\Router\RouteMatch::class); + $this->request = $this->createMock(\Laminas\Http\PhpEnvironment\Request::class); + $this->response = $this->createMock(\Laminas\Http\PhpEnvironment\Response::class); + $routeMatch = $this->createMock(\Laminas\Mvc\Router\RouteMatch::class); $this->uninstallCollector = $this->createMock(\Magento\Setup\Model\UninstallCollector::class); $this->controller = new DataOption($this->uninstallCollector); - $this->mvcEvent = $this->createMock(\Zend\Mvc\MvcEvent::class); + $this->mvcEvent = $this->createMock(\Laminas\Mvc\MvcEvent::class); $this->mvcEvent->expects($this->any()) ->method('setRequest') ->with($this->request) @@ -64,7 +64,7 @@ public function setUp() public function testIndexAction() { $viewModel = $this->controller->indexAction(); - $this->assertInstanceOf(\Zend\View\Model\ViewModel::class, $viewModel); + $this->assertInstanceOf(\Laminas\View\Model\ViewModel::class, $viewModel); $this->assertTrue($viewModel->terminate()); } diff --git a/setup/src/Magento/Setup/Test/Unit/Controller/EnvironmentTest.php b/setup/src/Magento/Setup/Test/Unit/Controller/EnvironmentTest.php index 6050122d78521..825905aa5099b 100644 --- a/setup/src/Magento/Setup/Test/Unit/Controller/EnvironmentTest.php +++ b/setup/src/Magento/Setup/Test/Unit/Controller/EnvironmentTest.php @@ -10,7 +10,7 @@ use Magento\Setup\Controller\ReadinessCheckUpdater; use Magento\Setup\Controller\ResponseTypeInterface; use PHPUnit\Framework\MockObject\MockObject; -use Zend\View\Model\JsonModel; +use Laminas\View\Model\JsonModel; /** * @SuppressWarnings(PHPMD.CouplingBetweenObjects) @@ -58,9 +58,9 @@ public function setUp() public function testFilePermissionsInstaller() { - $request = $this->createMock(\Zend\Http\PhpEnvironment\Request::class); - $response = $this->createMock(\Zend\Http\PhpEnvironment\Response::class); - $routeMatch = $this->createMock(\Zend\Mvc\Router\RouteMatch::class); + $request = $this->createMock(\Laminas\Http\PhpEnvironment\Request::class); + $response = $this->createMock(\Laminas\Http\PhpEnvironment\Response::class); + $routeMatch = $this->createMock(\Laminas\Mvc\Router\RouteMatch::class); $mvcEvent = $this->getMvcEventMock($request, $response, $routeMatch); @@ -72,9 +72,9 @@ public function testFilePermissionsInstaller() public function testPhpVersionActionInstaller() { - $request = $this->createMock(\Zend\Http\PhpEnvironment\Request::class); - $response = $this->createMock(\Zend\Http\PhpEnvironment\Response::class); - $routeMatch = $this->createMock(\Zend\Mvc\Router\RouteMatch::class); + $request = $this->createMock(\Laminas\Http\PhpEnvironment\Request::class); + $response = $this->createMock(\Laminas\Http\PhpEnvironment\Response::class); + $routeMatch = $this->createMock(\Laminas\Mvc\Router\RouteMatch::class); $mvcEvent = $this->getMvcEventMock($request, $response, $routeMatch); @@ -87,9 +87,9 @@ public function testPhpVersionActionInstaller() public function testPhpVersionActionUpdater() { - $request = $this->createMock(\Zend\Http\PhpEnvironment\Request::class); - $response = $this->createMock(\Zend\Http\PhpEnvironment\Response::class); - $routeMatch = $this->createMock(\Zend\Mvc\Router\RouteMatch::class); + $request = $this->createMock(\Laminas\Http\PhpEnvironment\Request::class); + $response = $this->createMock(\Laminas\Http\PhpEnvironment\Response::class); + $routeMatch = $this->createMock(\Laminas\Mvc\Router\RouteMatch::class); $mvcEvent = $this->getMvcEventMock($request, $response, $routeMatch); @@ -108,9 +108,9 @@ public function testPhpVersionActionUpdater() public function testPhpSettingsActionInstaller() { - $request = $this->createMock(\Zend\Http\PhpEnvironment\Request::class); - $response = $this->createMock(\Zend\Http\PhpEnvironment\Response::class); - $routeMatch = $this->createMock(\Zend\Mvc\Router\RouteMatch::class); + $request = $this->createMock(\Laminas\Http\PhpEnvironment\Request::class); + $response = $this->createMock(\Laminas\Http\PhpEnvironment\Response::class); + $routeMatch = $this->createMock(\Laminas\Mvc\Router\RouteMatch::class); $mvcEvent = $this->getMvcEventMock($request, $response, $routeMatch); @@ -123,9 +123,9 @@ public function testPhpSettingsActionInstaller() public function testPhpSettingsActionUpdater() { - $request = $this->createMock(\Zend\Http\PhpEnvironment\Request::class); - $response = $this->createMock(\Zend\Http\PhpEnvironment\Response::class); - $routeMatch = $this->createMock(\Zend\Mvc\Router\RouteMatch::class); + $request = $this->createMock(\Laminas\Http\PhpEnvironment\Request::class); + $response = $this->createMock(\Laminas\Http\PhpEnvironment\Response::class); + $routeMatch = $this->createMock(\Laminas\Mvc\Router\RouteMatch::class); $mvcEvent = $this->getMvcEventMock($request, $response, $routeMatch); @@ -144,9 +144,9 @@ public function testPhpSettingsActionUpdater() public function testPhpExtensionsActionInstaller() { - $request = $this->createMock(\Zend\Http\PhpEnvironment\Request::class); - $response = $this->createMock(\Zend\Http\PhpEnvironment\Response::class); - $routeMatch = $this->createMock(\Zend\Mvc\Router\RouteMatch::class); + $request = $this->createMock(\Laminas\Http\PhpEnvironment\Request::class); + $response = $this->createMock(\Laminas\Http\PhpEnvironment\Response::class); + $routeMatch = $this->createMock(\Laminas\Mvc\Router\RouteMatch::class); $mvcEvent = $this->getMvcEventMock($request, $response, $routeMatch); @@ -159,9 +159,9 @@ public function testPhpExtensionsActionInstaller() public function testPhpExtensionsActionUpdater() { - $request = $this->createMock(\Zend\Http\PhpEnvironment\Request::class); - $response = $this->createMock(\Zend\Http\PhpEnvironment\Response::class); - $routeMatch = $this->createMock(\Zend\Mvc\Router\RouteMatch::class); + $request = $this->createMock(\Laminas\Http\PhpEnvironment\Request::class); + $response = $this->createMock(\Laminas\Http\PhpEnvironment\Response::class); + $routeMatch = $this->createMock(\Laminas\Mvc\Router\RouteMatch::class); $mvcEvent = $this->getMvcEventMock($request, $response, $routeMatch); @@ -291,7 +291,7 @@ public function testCronScriptActionBothNotice() public function testIndexAction() { $model = $this->environment->indexAction(); - $this->assertInstanceOf(\Zend\View\Model\JsonModel::class, $model); + $this->assertInstanceOf(\Laminas\View\Model\JsonModel::class, $model); } /** @@ -306,7 +306,7 @@ protected function getMvcEventMock( MockObject $response, MockObject $routeMatch ) { - $mvcEvent = $this->createMock(\Zend\Mvc\MvcEvent::class); + $mvcEvent = $this->createMock(\Laminas\Mvc\MvcEvent::class); $mvcEvent->expects($this->once())->method('setRequest')->with($request)->willReturn($mvcEvent); $mvcEvent->expects($this->once())->method('setResponse')->with($response)->willReturn($mvcEvent); $mvcEvent->expects($this->once())->method('setTarget')->with($this->environment)->willReturn($mvcEvent); diff --git a/setup/src/Magento/Setup/Test/Unit/Controller/ExtensionGridTest.php b/setup/src/Magento/Setup/Test/Unit/Controller/ExtensionGridTest.php index febcbd1f8dbd4..452fa9f404683 100644 --- a/setup/src/Magento/Setup/Test/Unit/Controller/ExtensionGridTest.php +++ b/setup/src/Magento/Setup/Test/Unit/Controller/ExtensionGridTest.php @@ -3,6 +3,7 @@ * Copyright © Magento, Inc. All rights reserved. * See COPYING.txt for license details. */ + namespace Magento\Setup\Test\Unit\Controller; use Magento\Setup\Controller\ExtensionGrid; @@ -12,7 +13,7 @@ use PHPUnit_Framework_MockObject_MockObject as MockObject; /** - * Class ExtensionGridTest + * Test for \Magento\Setup\Controller\ExtensionGrid */ class ExtensionGridTest extends \PHPUnit\Framework\TestCase { @@ -97,7 +98,7 @@ public function setUp() public function testIndexAction() { $viewModel = $this->controller->indexAction(); - $this->assertInstanceOf(\Zend\View\Model\ViewModel::class, $viewModel); + $this->assertInstanceOf(\Laminas\View\Model\ViewModel::class, $viewModel); $this->assertTrue($viewModel->terminate()); } @@ -119,7 +120,7 @@ public function testExtensionsAction() ); $jsonModel = $this->controller->extensionsAction(); - $this->assertInstanceOf(\Zend\View\Model\JsonModel::class, $jsonModel); + $this->assertInstanceOf(\Laminas\View\Model\JsonModel::class, $jsonModel); $variables = $jsonModel->getVariables(); $this->assertArrayHasKey('success', $variables); $this->assertTrue($variables['success']); @@ -147,7 +148,7 @@ public function testSyncAction() ); $jsonModel = $this->controller->syncAction(); - $this->assertInstanceOf(\Zend\View\Model\JsonModel::class, $jsonModel); + $this->assertInstanceOf(\Laminas\View\Model\JsonModel::class, $jsonModel); $variables = $jsonModel->getVariables(); $this->assertArrayHasKey('success', $variables); $this->assertTrue($variables['success']); diff --git a/setup/src/Magento/Setup/Test/Unit/Controller/IndexTest.php b/setup/src/Magento/Setup/Test/Unit/Controller/IndexTest.php index f258d5d98d107..e39c535fe9980 100644 --- a/setup/src/Magento/Setup/Test/Unit/Controller/IndexTest.php +++ b/setup/src/Magento/Setup/Test/Unit/Controller/IndexTest.php @@ -15,7 +15,7 @@ public function testIndexAction() /** @var $controller Index */ $controller = new Index(); $viewModel = $controller->indexAction(); - $this->assertInstanceOf(\Zend\View\Model\ViewModel::class, $viewModel); + $this->assertInstanceOf(\Laminas\View\Model\ViewModel::class, $viewModel); $this->assertFalse($viewModel->terminate()); } } diff --git a/setup/src/Magento/Setup/Test/Unit/Controller/InstallExtensionGridTest.php b/setup/src/Magento/Setup/Test/Unit/Controller/InstallExtensionGridTest.php index 9eba4413d0cda..a92988f060724 100644 --- a/setup/src/Magento/Setup/Test/Unit/Controller/InstallExtensionGridTest.php +++ b/setup/src/Magento/Setup/Test/Unit/Controller/InstallExtensionGridTest.php @@ -41,7 +41,7 @@ public function setUp() public function testIndexAction() { $viewModel = $this->controller->indexAction(); - static::assertInstanceOf(\Zend\View\Model\ViewModel::class, $viewModel); + static::assertInstanceOf(\Laminas\View\Model\ViewModel::class, $viewModel); } /** @@ -56,7 +56,7 @@ public function testExtensionsAction($extensions) ->willReturn($extensions); $jsonModel = $this->controller->extensionsAction(); - static::assertInstanceOf(\Zend\View\Model\JsonModel::class, $jsonModel); + static::assertInstanceOf(\Laminas\View\Model\JsonModel::class, $jsonModel); $variables = $jsonModel->getVariables(); static::assertArrayHasKey('success', $variables); static::assertArrayHasKey('extensions', $variables); diff --git a/setup/src/Magento/Setup/Test/Unit/Controller/InstallTest.php b/setup/src/Magento/Setup/Test/Unit/Controller/InstallTest.php index d0fd62adc42b4..e68ff822e4f1a 100644 --- a/setup/src/Magento/Setup/Test/Unit/Controller/InstallTest.php +++ b/setup/src/Magento/Setup/Test/Unit/Controller/InstallTest.php @@ -75,7 +75,7 @@ public function setUp() public function testIndexAction() { $viewModel = $this->controller->indexAction(); - $this->assertInstanceOf(\Zend\View\Model\ViewModel::class, $viewModel); + $this->assertInstanceOf(\Laminas\View\Model\ViewModel::class, $viewModel); $this->assertTrue($viewModel->terminate()); } @@ -86,7 +86,7 @@ public function testStartAction() $this->installer->expects($this->exactly(2))->method('getInstallInfo'); $this->deploymentConfig->expects($this->once())->method('isAvailable')->willReturn(false); $jsonModel = $this->controller->startAction(); - $this->assertInstanceOf(\Zend\View\Model\JsonModel::class, $jsonModel); + $this->assertInstanceOf(\Laminas\View\Model\JsonModel::class, $jsonModel); $variables = $jsonModel->getVariables(); $this->assertArrayHasKey('key', $variables); $this->assertArrayHasKey('success', $variables); @@ -101,7 +101,7 @@ public function testStartActionPriorInstallException() $this->installer->expects($this->never())->method('getInstallInfo'); $this->deploymentConfig->expects($this->once())->method('isAvailable')->willReturn(true); $jsonModel = $this->controller->startAction(); - $this->assertInstanceOf(\Zend\View\Model\JsonModel::class, $jsonModel); + $this->assertInstanceOf(\Laminas\View\Model\JsonModel::class, $jsonModel); $variables = $jsonModel->getVariables(); $this->assertArrayHasKey('success', $variables); $this->assertArrayHasKey('messages', $variables); @@ -126,7 +126,7 @@ public function testStartActionWithSampleDataError() $this->installer->method('install'); $this->sampleDataState->expects($this->once())->method('hasError')->willReturn(true); $jsonModel = $this->controller->startAction(); - $this->assertInstanceOf(\Zend\View\Model\JsonModel::class, $jsonModel); + $this->assertInstanceOf(\Laminas\View\Model\JsonModel::class, $jsonModel); $variables = $jsonModel->getVariables(); $this->assertArrayHasKey('success', $variables); $this->assertTrue($variables['success']); @@ -145,7 +145,7 @@ public function testProgressAction() $progress->expects($this->once())->method('getRatio')->willReturn($numValue); $this->webLogger->expects($this->once())->method('get')->willReturn($consoleMessages); $jsonModel = $this->controller->progressAction(); - $this->assertInstanceOf(\Zend\View\Model\JsonModel::class, $jsonModel); + $this->assertInstanceOf(\Laminas\View\Model\JsonModel::class, $jsonModel); $variables = $jsonModel->getVariables(); $this->assertArrayHasKey('progress', $variables); $this->assertArrayHasKey('success', $variables); @@ -162,7 +162,7 @@ public function testProgressActionWithError() $this->progressFactory->expects($this->once())->method('createFromLog') ->will($this->throwException(new \LogicException($e))); $jsonModel = $this->controller->progressAction(); - $this->assertInstanceOf(\Zend\View\Model\JsonModel::class, $jsonModel); + $this->assertInstanceOf(\Laminas\View\Model\JsonModel::class, $jsonModel); $variables = $jsonModel->getVariables(); $this->assertArrayHasKey('success', $variables); $this->assertArrayHasKey('console', $variables); @@ -180,7 +180,7 @@ public function testProgressActionWithSampleDataError() $this->progressFactory->expects($this->once())->method('createFromLog')->willReturn($progress); $this->sampleDataState->expects($this->once())->method('hasError')->willReturn(true); $jsonModel = $this->controller->progressAction(); - $this->assertInstanceOf(\Zend\View\Model\JsonModel::class, $jsonModel); + $this->assertInstanceOf(\Laminas\View\Model\JsonModel::class, $jsonModel); $variables = $jsonModel->getVariables(); $this->assertArrayHasKey('success', $variables); $this->assertArrayHasKey('console', $variables); @@ -193,7 +193,7 @@ public function testProgressActionNoInstallLogFile() { $this->webLogger->expects($this->once())->method('logfileExists')->willReturn(false); $jsonModel = $this->controller->progressAction(); - $this->assertInstanceOf(\Zend\View\Model\JsonModel::class, $jsonModel); + $this->assertInstanceOf(\Laminas\View\Model\JsonModel::class, $jsonModel); $variables = $jsonModel->getVariables(); $this->assertArrayHasKey('success', $variables); $this->assertArrayHasKey('console', $variables); @@ -204,11 +204,11 @@ public function testProgressActionNoInstallLogFile() public function testDispatch() { - $request = $this->createMock(\Zend\Http\PhpEnvironment\Request::class); - $response = $this->createMock(\Zend\Http\PhpEnvironment\Response::class); - $routeMatch = $this->createMock(\Zend\Mvc\Router\RouteMatch::class); + $request = $this->createMock(\Laminas\Http\PhpEnvironment\Request::class); + $response = $this->createMock(\Laminas\Http\PhpEnvironment\Response::class); + $routeMatch = $this->createMock(\Laminas\Mvc\Router\RouteMatch::class); - $mvcEvent = $this->createMock(\Zend\Mvc\MvcEvent::class); + $mvcEvent = $this->createMock(\Laminas\Mvc\MvcEvent::class); $mvcEvent->expects($this->once())->method('setRequest')->with($request)->willReturn($mvcEvent); $mvcEvent->expects($this->once())->method('setResponse')->with($response)->willReturn($mvcEvent); $mvcEvent->expects($this->once())->method('setTarget')->with($this->controller)->willReturn($mvcEvent); diff --git a/setup/src/Magento/Setup/Test/Unit/Controller/LandingInstallerTest.php b/setup/src/Magento/Setup/Test/Unit/Controller/LandingInstallerTest.php index d337270dd938b..7db7d30b8b6fc 100644 --- a/setup/src/Magento/Setup/Test/Unit/Controller/LandingInstallerTest.php +++ b/setup/src/Magento/Setup/Test/Unit/Controller/LandingInstallerTest.php @@ -32,7 +32,7 @@ public function testIndexAction() $controller = new LandingInstaller($productMetadataMock); $_SERVER['DOCUMENT_ROOT'] = 'some/doc/root/value'; $viewModel = $controller->indexAction(); - $this->assertInstanceOf(\Zend\View\Model\ViewModel::class, $viewModel); + $this->assertInstanceOf(\Laminas\View\Model\ViewModel::class, $viewModel); $this->assertTrue($viewModel->terminate()); $this->assertEquals('/magento/setup/landing.phtml', $viewModel->getTemplate()); $variables = $viewModel->getVariables(); diff --git a/setup/src/Magento/Setup/Test/Unit/Controller/LandingUpdaterTest.php b/setup/src/Magento/Setup/Test/Unit/Controller/LandingUpdaterTest.php index 1e75b36334bb9..3ad28a24bb734 100644 --- a/setup/src/Magento/Setup/Test/Unit/Controller/LandingUpdaterTest.php +++ b/setup/src/Magento/Setup/Test/Unit/Controller/LandingUpdaterTest.php @@ -32,7 +32,7 @@ public function testIndexAction() $controller = new LandingUpdater($productMetadataMock); $_SERVER['DOCUMENT_ROOT'] = 'some/doc/root/value'; $viewModel = $controller->indexAction(); - $this->assertInstanceOf(\Zend\View\Model\ViewModel::class, $viewModel); + $this->assertInstanceOf(\Laminas\View\Model\ViewModel::class, $viewModel); $this->assertTrue($viewModel->terminate()); $this->assertEquals('/magento/setup/landing.phtml', $viewModel->getTemplate()); $variables = $viewModel->getVariables(); diff --git a/setup/src/Magento/Setup/Test/Unit/Controller/LicenseTest.php b/setup/src/Magento/Setup/Test/Unit/Controller/LicenseTest.php index b496051c947ca..367be5f7b8381 100644 --- a/setup/src/Magento/Setup/Test/Unit/Controller/LicenseTest.php +++ b/setup/src/Magento/Setup/Test/Unit/Controller/LicenseTest.php @@ -30,7 +30,7 @@ public function testIndexActionWithLicense() { $this->licenseModel->expects($this->once())->method('getContents')->willReturn('some license string'); $viewModel = $this->controller->indexAction(); - $this->assertInstanceOf(\Zend\View\Model\ViewModel::class, $viewModel); + $this->assertInstanceOf(\Laminas\View\Model\ViewModel::class, $viewModel); $this->assertArrayHasKey('license', $viewModel->getVariables()); } @@ -38,7 +38,7 @@ public function testIndexActionNoLicense() { $this->licenseModel->expects($this->once())->method('getContents')->willReturn(false); $viewModel = $this->controller->indexAction(); - $this->assertInstanceOf(\Zend\View\Model\ViewModel::class, $viewModel); + $this->assertInstanceOf(\Laminas\View\Model\ViewModel::class, $viewModel); $this->assertArrayHasKey('message', $viewModel->getVariables()); $this->assertEquals('error/404', $viewModel->getTemplate()); } diff --git a/setup/src/Magento/Setup/Test/Unit/Controller/MaintenanceTest.php b/setup/src/Magento/Setup/Test/Unit/Controller/MaintenanceTest.php index f4c27ee890785..b938b1e04a81b 100644 --- a/setup/src/Magento/Setup/Test/Unit/Controller/MaintenanceTest.php +++ b/setup/src/Magento/Setup/Test/Unit/Controller/MaintenanceTest.php @@ -29,11 +29,11 @@ public function setUp() $this->maintenanceMode = $this->createMock(\Magento\Framework\App\MaintenanceMode::class); $this->controller = new Maintenance($this->maintenanceMode); - $request = $this->createMock(\Zend\Http\PhpEnvironment\Request::class); - $response = $this->createMock(\Zend\Http\PhpEnvironment\Response::class); - $routeMatch = $this->createMock(\Zend\Mvc\Router\RouteMatch::class); + $request = $this->createMock(\Laminas\Http\PhpEnvironment\Request::class); + $response = $this->createMock(\Laminas\Http\PhpEnvironment\Response::class); + $routeMatch = $this->createMock(\Laminas\Mvc\Router\RouteMatch::class); - $mvcEvent = $this->createMock(\Zend\Mvc\MvcEvent::class); + $mvcEvent = $this->createMock(\Laminas\Mvc\MvcEvent::class); $mvcEvent->expects($this->any())->method('setRequest')->with($request)->willReturn($mvcEvent); $mvcEvent->expects($this->any())->method('setResponse')->with($response)->willReturn($mvcEvent); $mvcEvent->expects($this->any())->method('setTarget')->with($this->controller)->willReturn($mvcEvent); @@ -51,7 +51,7 @@ public function testIndexAction() { $this->maintenanceMode->expects($this->once())->method('set'); $jsonModel = $this->controller->indexAction(); - $this->assertInstanceOf(\Zend\View\Model\JsonModel::class, $jsonModel); + $this->assertInstanceOf(\Laminas\View\Model\JsonModel::class, $jsonModel); $variables = $jsonModel->getVariables(); $this->assertArrayHasKey('responseType', $variables); $this->assertEquals(ResponseTypeInterface::RESPONSE_TYPE_SUCCESS, $variables['responseType']); @@ -63,7 +63,7 @@ public function testIndexActionWithExceptions() $this->throwException(new \Exception("Test error message")) ); $jsonModel = $this->controller->indexAction(); - $this->assertInstanceOf(\Zend\View\Model\JsonModel::class, $jsonModel); + $this->assertInstanceOf(\Laminas\View\Model\JsonModel::class, $jsonModel); $variables = $jsonModel->getVariables(); $this->assertArrayHasKey('responseType', $variables); $this->assertEquals(ResponseTypeInterface::RESPONSE_TYPE_ERROR, $variables['responseType']); diff --git a/setup/src/Magento/Setup/Test/Unit/Controller/MarketplaceTest.php b/setup/src/Magento/Setup/Test/Unit/Controller/MarketplaceTest.php index dbdd6003f69c9..91a9bfcfdc296 100644 --- a/setup/src/Magento/Setup/Test/Unit/Controller/MarketplaceTest.php +++ b/setup/src/Magento/Setup/Test/Unit/Controller/MarketplaceTest.php @@ -45,7 +45,7 @@ public function testSaveAuthJsonAction() ->method('saveAuthJson') ->willReturn(true); $jsonModel = $this->controller->saveAuthJsonAction(); - $this->assertInstanceOf(\Zend\View\Model\ViewModel::class, $jsonModel); + $this->assertInstanceOf(\Laminas\View\Model\ViewModel::class, $jsonModel); $variables = $jsonModel->getVariables(); $this->assertArrayHasKey('success', $variables); $this->assertTrue($variables['success']); @@ -59,7 +59,7 @@ public function testSaveAuthJsonActionWithError() ->will($this->throwException(new \Exception)); $this->packagesAuth->expects($this->never())->method('saveAuthJson'); $jsonModel = $this->controller->saveAuthJsonAction(); - $this->assertInstanceOf(\Zend\View\Model\JsonModel::class, $jsonModel); + $this->assertInstanceOf(\Laminas\View\Model\JsonModel::class, $jsonModel); $variables = $jsonModel->getVariables(); $this->assertArrayHasKey('success', $variables); $this->assertArrayHasKey('message', $variables); @@ -77,7 +77,7 @@ public function testCheckAuthAction() ->method('checkCredentials') ->will($this->returnValue(json_encode(['success' => true]))); $jsonModel = $this->controller->checkAuthAction(); - $this->assertInstanceOf(\Zend\View\Model\ViewModel::class, $jsonModel); + $this->assertInstanceOf(\Laminas\View\Model\ViewModel::class, $jsonModel); $variables = $jsonModel->getVariables(); $this->assertArrayHasKey('success', $variables); $this->assertTrue($variables['success']); @@ -90,7 +90,7 @@ public function testCheckAuthActionWithError() ->method('getAuthJsonData') ->will($this->throwException(new \Exception)); $jsonModel = $this->controller->checkAuthAction(); - $this->assertInstanceOf(\Zend\View\Model\JsonModel::class, $jsonModel); + $this->assertInstanceOf(\Laminas\View\Model\JsonModel::class, $jsonModel); $variables = $jsonModel->getVariables(); $this->assertArrayHasKey('success', $variables); $this->assertArrayHasKey('message', $variables); @@ -105,7 +105,7 @@ public function testRemoveCredentialsAction() ->will($this->returnValue(true)); $jsonModel = $this->controller->removeCredentialsAction(); - $this->assertInstanceOf(\Zend\View\Model\ViewModel::class, $jsonModel); + $this->assertInstanceOf(\Laminas\View\Model\ViewModel::class, $jsonModel); $variables = $jsonModel->getVariables(); $this->assertArrayHasKey('success', $variables); $this->assertTrue($variables['success']); @@ -118,7 +118,7 @@ public function testRemoveCredentialsWithError() ->method('removeCredentials') ->will($this->throwException(new \Exception)); $jsonModel = $this->controller->removeCredentialsAction(); - $this->assertInstanceOf(\Zend\View\Model\JsonModel::class, $jsonModel); + $this->assertInstanceOf(\Laminas\View\Model\JsonModel::class, $jsonModel); $variables = $jsonModel->getVariables(); $this->assertArrayHasKey('success', $variables); $this->assertArrayHasKey('message', $variables); @@ -128,13 +128,13 @@ public function testRemoveCredentialsWithError() public function testPopupAuthAction() { $viewModel = $this->controller->popupAuthAction(); - $this->assertInstanceOf(\Zend\View\Model\ViewModel::class, $viewModel); + $this->assertInstanceOf(\Laminas\View\Model\ViewModel::class, $viewModel); $this->assertTrue($viewModel->terminate()); } public function testIndexAction() { $model = $this->controller->indexAction(); - $this->assertInstanceOf(\Zend\View\Model\ViewModel::class, $model); + $this->assertInstanceOf(\Laminas\View\Model\ViewModel::class, $model); } } diff --git a/setup/src/Magento/Setup/Test/Unit/Controller/ModuleGridTest.php b/setup/src/Magento/Setup/Test/Unit/Controller/ModuleGridTest.php index 2c25aac37d578..4d4d7e1d1a6ef 100644 --- a/setup/src/Magento/Setup/Test/Unit/Controller/ModuleGridTest.php +++ b/setup/src/Magento/Setup/Test/Unit/Controller/ModuleGridTest.php @@ -10,7 +10,7 @@ use Magento\Setup\Model\Grid\Module; /** - * Class ModuleGridTest + * Test for \Magento\Setup\Controller\ModuleGrid */ class ModuleGridTest extends \PHPUnit\Framework\TestCase { @@ -40,7 +40,7 @@ public function setUp() public function testIndexAction() { $viewModel = $this->controller->indexAction(); - $this->assertInstanceOf(\Zend\View\Model\ViewModel::class, $viewModel); + $this->assertInstanceOf(\Laminas\View\Model\ViewModel::class, $viewModel); $this->assertTrue($viewModel->terminate()); } @@ -72,7 +72,7 @@ public function testModulesAction() ->willReturn($moduleList); $jsonModel = $this->controller->modulesAction(); - $this->assertInstanceOf(\Zend\View\Model\JsonModel::class, $jsonModel); + $this->assertInstanceOf(\Laminas\View\Model\JsonModel::class, $jsonModel); $variables = $jsonModel->getVariables(); $this->assertArrayHasKey('success', $variables); $this->assertTrue($variables['success']); diff --git a/setup/src/Magento/Setup/Test/Unit/Controller/ModulesTest.php b/setup/src/Magento/Setup/Test/Unit/Controller/ModulesTest.php index 35a463090a806..243feebbc2655 100644 --- a/setup/src/Magento/Setup/Test/Unit/Controller/ModulesTest.php +++ b/setup/src/Magento/Setup/Test/Unit/Controller/ModulesTest.php @@ -56,7 +56,7 @@ public function testIndexAction(array $expected) $this->modules->expects($this->once())->method('getAllModules')->willReturn($expected['modules']); $this->status->expects($this->once())->method('checkConstraints')->willReturn([]); $jsonModel = $this->controller->indexAction(); - $this->assertInstanceOf(\Zend\View\Model\JsonModel::class, $jsonModel); + $this->assertInstanceOf(\Laminas\View\Model\JsonModel::class, $jsonModel); $variables = $jsonModel->getVariables(); $this->assertArrayHasKey('success', $variables); $this->assertTrue($variables['success']); @@ -74,7 +74,7 @@ public function testIndexActionWithError(array $expected) ->method('checkConstraints') ->willReturn(['ModuleA', 'ModuleB']); $jsonModel = $this->controller->indexAction(); - $this->assertInstanceOf(\Zend\View\Model\JsonModel::class, $jsonModel); + $this->assertInstanceOf(\Laminas\View\Model\JsonModel::class, $jsonModel); $variables = $jsonModel->getVariables(); $this->assertArrayHasKey('success', $variables); $this->assertArrayHasKey('error', $variables); diff --git a/setup/src/Magento/Setup/Test/Unit/Controller/NavigationTest.php b/setup/src/Magento/Setup/Test/Unit/Controller/NavigationTest.php index a8a3962793d51..595892a71ac98 100644 --- a/setup/src/Magento/Setup/Test/Unit/Controller/NavigationTest.php +++ b/setup/src/Magento/Setup/Test/Unit/Controller/NavigationTest.php @@ -46,14 +46,14 @@ public function testIndexAction() $this->navigationModel->expects($this->once())->method('getData')->willReturn('some data'); $viewModel = $this->controller->indexAction(); - $this->assertInstanceOf(\Zend\View\Model\JsonModel::class, $viewModel); + $this->assertInstanceOf(\Laminas\View\Model\JsonModel::class, $viewModel); $this->assertArrayHasKey('nav', $viewModel->getVariables()); } public function testMenuActionUpdater() { $viewModel = $this->controller->menuAction(); - $this->assertInstanceOf(\Zend\View\Model\ViewModel::class, $viewModel); + $this->assertInstanceOf(\Laminas\View\Model\ViewModel::class, $viewModel); $variables = $viewModel->getVariables(); $this->assertArrayHasKey('menu', $variables); $this->assertArrayHasKey('main', $variables); @@ -64,7 +64,7 @@ public function testMenuActionUpdater() public function testMenuActionInstaller() { $viewModel = $this->controller->menuAction(); - $this->assertInstanceOf(\Zend\View\Model\ViewModel::class, $viewModel); + $this->assertInstanceOf(\Laminas\View\Model\ViewModel::class, $viewModel); $variables = $viewModel->getVariables(); $this->assertArrayHasKey('menu', $variables); $this->assertArrayHasKey('main', $variables); @@ -76,7 +76,7 @@ public function testHeaderBarInstaller() { $this->navigationModel->expects($this->once())->method('getType')->willReturn(NavModel::NAV_INSTALLER); $viewModel = $this->controller->headerBarAction(); - $this->assertInstanceOf(\Zend\View\Model\ViewModel::class, $viewModel); + $this->assertInstanceOf(\Laminas\View\Model\ViewModel::class, $viewModel); $variables = $viewModel->getVariables(); $this->assertArrayHasKey('menu', $variables); $this->assertArrayHasKey('main', $variables); @@ -88,7 +88,7 @@ public function testHeaderBarUpdater() { $this->navigationModel->expects($this->once())->method('getType')->willReturn(NavModel::NAV_UPDATER); $viewModel = $this->controller->headerBarAction(); - $this->assertInstanceOf(\Zend\View\Model\ViewModel::class, $viewModel); + $this->assertInstanceOf(\Laminas\View\Model\ViewModel::class, $viewModel); $variables = $viewModel->getVariables(); $this->assertArrayHasKey('menu', $variables); $this->assertArrayHasKey('main', $variables); diff --git a/setup/src/Magento/Setup/Test/Unit/Controller/OtherComponentsGridTest.php b/setup/src/Magento/Setup/Test/Unit/Controller/OtherComponentsGridTest.php index 095c8907bf43d..feb8d1abd2493 100644 --- a/setup/src/Magento/Setup/Test/Unit/Controller/OtherComponentsGridTest.php +++ b/setup/src/Magento/Setup/Test/Unit/Controller/OtherComponentsGridTest.php @@ -69,7 +69,7 @@ public function testComponentsAction() ] ]); $jsonModel = $this->controller->componentsAction(); - $this->assertInstanceOf(\Zend\View\Model\JsonModel::class, $jsonModel); + $this->assertInstanceOf(\Laminas\View\Model\JsonModel::class, $jsonModel); $variables = $jsonModel->getVariables(); $this->assertArrayHasKey('responseType', $variables); $this->assertEquals(ResponseTypeInterface::RESPONSE_TYPE_SUCCESS, $variables['responseType']); @@ -109,7 +109,7 @@ public function testComponentsActionWithError() ->method('getInstalledMagentoPackages') ->will($this->throwException(new \Exception("Test error message"))); $jsonModel = $this->controller->componentsAction(); - $this->assertInstanceOf(\Zend\View\Model\JsonModel::class, $jsonModel); + $this->assertInstanceOf(\Laminas\View\Model\JsonModel::class, $jsonModel); $variables = $jsonModel->getVariables(); $this->assertArrayHasKey('responseType', $variables); $this->assertEquals(ResponseTypeInterface::RESPONSE_TYPE_ERROR, $variables['responseType']); @@ -118,6 +118,6 @@ public function testComponentsActionWithError() public function testIndexAction() { $model = $this->controller->indexAction(); - $this->assertInstanceOf(\Zend\View\Model\ViewModel::class, $model); + $this->assertInstanceOf(\Laminas\View\Model\ViewModel::class, $model); } } diff --git a/setup/src/Magento/Setup/Test/Unit/Controller/ReadinessCheckInstallerTest.php b/setup/src/Magento/Setup/Test/Unit/Controller/ReadinessCheckInstallerTest.php index 81e687564b857..71bdc74730bc3 100644 --- a/setup/src/Magento/Setup/Test/Unit/Controller/ReadinessCheckInstallerTest.php +++ b/setup/src/Magento/Setup/Test/Unit/Controller/ReadinessCheckInstallerTest.php @@ -23,7 +23,7 @@ public function setUp() public function testIndexAction() { $viewModel = $this->controller->indexAction(); - $this->assertInstanceOf(\Zend\View\Model\ViewModel::class, $viewModel); + $this->assertInstanceOf(\Laminas\View\Model\ViewModel::class, $viewModel); $this->assertTrue($viewModel->terminate()); $variables = $viewModel->getVariables(); $this->assertArrayHasKey('actionFrom', $variables); @@ -33,7 +33,7 @@ public function testIndexAction() public function testProgressAction() { $viewModel = $this->controller->progressAction(); - $this->assertInstanceOf(\Zend\View\Model\ViewModel::class, $viewModel); + $this->assertInstanceOf(\Laminas\View\Model\ViewModel::class, $viewModel); $this->assertTrue($viewModel->terminate()); $this->assertSame('/magento/setup/readiness-check/progress.phtml', $viewModel->getTemplate()); } diff --git a/setup/src/Magento/Setup/Test/Unit/Controller/ReadinessCheckUpdaterTest.php b/setup/src/Magento/Setup/Test/Unit/Controller/ReadinessCheckUpdaterTest.php index a5f3d25e73421..c184cd2f52465 100644 --- a/setup/src/Magento/Setup/Test/Unit/Controller/ReadinessCheckUpdaterTest.php +++ b/setup/src/Magento/Setup/Test/Unit/Controller/ReadinessCheckUpdaterTest.php @@ -23,7 +23,7 @@ public function setUp() public function testIndexAction() { $viewModel = $this->controller->indexAction(); - $this->assertInstanceOf(\Zend\View\Model\ViewModel::class, $viewModel); + $this->assertInstanceOf(\Laminas\View\Model\ViewModel::class, $viewModel); $this->assertTrue($viewModel->terminate()); $variables = $viewModel->getVariables(); $this->assertArrayHasKey('actionFrom', $variables); @@ -33,7 +33,7 @@ public function testIndexAction() public function testProgressAction() { $viewModel = $this->controller->progressAction(); - $this->assertInstanceOf(\Zend\View\Model\ViewModel::class, $viewModel); + $this->assertInstanceOf(\Laminas\View\Model\ViewModel::class, $viewModel); $this->assertTrue($viewModel->terminate()); $this->assertSame('/magento/setup/readiness-check/progress.phtml', $viewModel->getTemplate()); } diff --git a/setup/src/Magento/Setup/Test/Unit/Controller/SelectVersionTest.php b/setup/src/Magento/Setup/Test/Unit/Controller/SelectVersionTest.php index 85e060f684d07..3d9dee65c9acc 100644 --- a/setup/src/Magento/Setup/Test/Unit/Controller/SelectVersionTest.php +++ b/setup/src/Magento/Setup/Test/Unit/Controller/SelectVersionTest.php @@ -34,7 +34,7 @@ public function setUp() public function testIndexAction() { $viewModel = $this->controller->indexAction(); - $this->assertInstanceOf(\Zend\View\Model\ViewModel::class, $viewModel); + $this->assertInstanceOf(\Laminas\View\Model\ViewModel::class, $viewModel); $this->assertTrue($viewModel->terminate()); } @@ -50,7 +50,7 @@ public function testSystemPackageAction() ] ]); $jsonModel = $this->controller->systemPackageAction(); - $this->assertInstanceOf(\Zend\View\Model\JsonModel::class, $jsonModel); + $this->assertInstanceOf(\Laminas\View\Model\JsonModel::class, $jsonModel); $variables = $jsonModel->getVariables(); $this->assertArrayHasKey('responseType', $variables); $this->assertEquals(ResponseTypeInterface::RESPONSE_TYPE_SUCCESS, $variables['responseType']); @@ -62,7 +62,7 @@ public function testSystemPackageActionActionWithError() ->method('getPackageVersions') ->will($this->throwException(new \Exception("Test error message"))); $jsonModel = $this->controller->systemPackageAction(); - $this->assertInstanceOf(\Zend\View\Model\JsonModel::class, $jsonModel); + $this->assertInstanceOf(\Laminas\View\Model\JsonModel::class, $jsonModel); $variables = $jsonModel->getVariables(); $this->assertArrayHasKey('responseType', $variables); $this->assertEquals(ResponseTypeInterface::RESPONSE_TYPE_ERROR, $variables['responseType']); @@ -80,7 +80,7 @@ public function testInstalledSystemPackageAction() ] ]); $jsonModel = $this->controller->installedSystemPackageAction(); - $this->assertInstanceOf(\Zend\View\Model\JsonModel::class, $jsonModel); + $this->assertInstanceOf(\Laminas\View\Model\JsonModel::class, $jsonModel); $variables = $jsonModel->getVariables(); $this->assertArrayHasKey('responseType', $variables); $this->assertEquals(ResponseTypeInterface::RESPONSE_TYPE_SUCCESS, $variables['responseType']); diff --git a/setup/src/Magento/Setup/Test/Unit/Controller/SessionTest.php b/setup/src/Magento/Setup/Test/Unit/Controller/SessionTest.php index 216013ebfc8d9..ecef8409cb0a5 100644 --- a/setup/src/Magento/Setup/Test/Unit/Controller/SessionTest.php +++ b/setup/src/Magento/Setup/Test/Unit/Controller/SessionTest.php @@ -21,7 +21,7 @@ class SessionTest extends \PHPUnit\Framework\TestCase private $objectManagerProvider; /** - * @var \Zend\ServiceManager\ServiceManager + * @var \Laminas\ServiceManager\ServiceManager */ private $serviceManager; @@ -33,7 +33,7 @@ public function setUp() $this->createPartialMock(\Magento\Setup\Model\ObjectManagerProvider::class, ['get']); $this->objectManager = $objectManager; $this->objectManagerProvider = $objectManagerProvider; - $this->serviceManager = $this->createPartialMock(\Zend\ServiceManager\ServiceManager::class, ['get']); + $this->serviceManager = $this->createPartialMock(\Laminas\ServiceManager\ServiceManager::class, ['get']); } /** @@ -91,7 +91,7 @@ public function testIndexAction() /** @var $controller Session */ $controller = new Session($this->serviceManager, $this->objectManagerProvider); $viewModel = $controller->unloginAction(); - $this->assertInstanceOf(\Zend\View\Model\ViewModel::class, $viewModel); + $this->assertInstanceOf(\Laminas\View\Model\ViewModel::class, $viewModel); } /** @@ -116,6 +116,6 @@ public function testProlongActionWithExistingSession() ->method('get') ->will($this->returnValue($sessionMock)); $controller = new Session($this->serviceManager, $this->objectManagerProvider); - $this->assertEquals(new \Zend\View\Model\JsonModel(['success' => true]), $controller->prolongAction()); + $this->assertEquals(new \Laminas\View\Model\JsonModel(['success' => true]), $controller->prolongAction()); } } diff --git a/setup/src/Magento/Setup/Test/Unit/Controller/StartUpdaterTest.php b/setup/src/Magento/Setup/Test/Unit/Controller/StartUpdaterTest.php index 7833cf113578a..412aaa2cfab71 100644 --- a/setup/src/Magento/Setup/Test/Unit/Controller/StartUpdaterTest.php +++ b/setup/src/Magento/Setup/Test/Unit/Controller/StartUpdaterTest.php @@ -6,11 +6,10 @@ namespace Magento\Setup\Test\Unit\Controller; -use Magento\Setup\Model\Navigation; use Magento\Setup\Controller\StartUpdater; /** - * Class StartUpdaterTest + * Test for \Magento\Setup\Controller\StartUpdater * @SuppressWarnings(PHPMD.CouplingBetweenObjects) */ class StartUpdaterTest extends \PHPUnit\Framework\TestCase @@ -21,17 +20,17 @@ class StartUpdaterTest extends \PHPUnit\Framework\TestCase private $controller; /** - * @var \Zend\Http\PhpEnvironment\Request|\PHPUnit_Framework_MockObject_MockObject + * @var \Laminas\Http\PhpEnvironment\Request|\PHPUnit_Framework_MockObject_MockObject */ private $request; /** - * @var \Zend\Http\PhpEnvironment\Response|\PHPUnit_Framework_MockObject_MockObject + * @var \Laminas\Http\PhpEnvironment\Response|\PHPUnit_Framework_MockObject_MockObject */ private $response; /** - * @var \Zend\Mvc\MvcEvent|\PHPUnit_Framework_MockObject_MockObject + * @var \Laminas\Mvc\MvcEvent|\PHPUnit_Framework_MockObject_MockObject */ private $mvcEvent; @@ -54,10 +53,10 @@ public function setUp() $this->updaterTaskCreator, $this->payloadValidator ); - $this->request = $this->createMock(\Zend\Http\PhpEnvironment\Request::class); - $this->response = $this->createMock(\Zend\Http\PhpEnvironment\Response::class); - $routeMatch = $this->createMock(\Zend\Mvc\Router\RouteMatch::class); - $this->mvcEvent = $this->createMock(\Zend\Mvc\MvcEvent::class); + $this->request = $this->createMock(\Laminas\Http\PhpEnvironment\Request::class); + $this->response = $this->createMock(\Laminas\Http\PhpEnvironment\Response::class); + $routeMatch = $this->createMock(\Laminas\Mvc\Router\RouteMatch::class); + $this->mvcEvent = $this->createMock(\Laminas\Mvc\MvcEvent::class); $this->mvcEvent->expects($this->any()) ->method('setRequest') ->with($this->request) @@ -77,7 +76,7 @@ public function setUp() public function testIndexAction() { $viewModel = $this->controller->indexAction(); - $this->assertInstanceOf(\Zend\View\Model\ViewModel::class, $viewModel); + $this->assertInstanceOf(\Laminas\View\Model\ViewModel::class, $viewModel); $this->assertTrue($viewModel->terminate()); } diff --git a/setup/src/Magento/Setup/Test/Unit/Controller/SuccessTest.php b/setup/src/Magento/Setup/Test/Unit/Controller/SuccessTest.php index 1e6b224dbe8d7..91027d76fe627 100644 --- a/setup/src/Magento/Setup/Test/Unit/Controller/SuccessTest.php +++ b/setup/src/Magento/Setup/Test/Unit/Controller/SuccessTest.php @@ -24,7 +24,7 @@ public function testIndexAction() $controller = new Success($moduleList, $objectManagerProvider); $sampleDataState->expects($this->once())->method('hasError'); $viewModel = $controller->indexAction(); - $this->assertInstanceOf(\Zend\View\Model\ViewModel::class, $viewModel); + $this->assertInstanceOf(\Laminas\View\Model\ViewModel::class, $viewModel); $this->assertTrue($viewModel->terminate()); } } diff --git a/setup/src/Magento/Setup/Test/Unit/Controller/SystemConfigTest.php b/setup/src/Magento/Setup/Test/Unit/Controller/SystemConfigTest.php index 4c93ba0bfd838..29a6d1a370cb8 100644 --- a/setup/src/Magento/Setup/Test/Unit/Controller/SystemConfigTest.php +++ b/setup/src/Magento/Setup/Test/Unit/Controller/SystemConfigTest.php @@ -18,7 +18,7 @@ public function testIndexAction() /** @var $controller SystemConfig */ $controller = new SystemConfig(); $viewModel = $controller->indexAction(); - $this->assertInstanceOf(\Zend\View\Model\ViewModel::class, $viewModel); + $this->assertInstanceOf(\Laminas\View\Model\ViewModel::class, $viewModel); $this->assertTrue($viewModel->terminate()); } } diff --git a/setup/src/Magento/Setup/Test/Unit/Controller/UpdateExtensionGridTest.php b/setup/src/Magento/Setup/Test/Unit/Controller/UpdateExtensionGridTest.php index 52f2c2d236541..e91740c8f558c 100644 --- a/setup/src/Magento/Setup/Test/Unit/Controller/UpdateExtensionGridTest.php +++ b/setup/src/Magento/Setup/Test/Unit/Controller/UpdateExtensionGridTest.php @@ -8,11 +8,11 @@ use Magento\Setup\Controller\UpdateExtensionGrid; use Magento\Setup\Model\Grid\Extension; use PHPUnit_Framework_MockObject_MockObject as MockObject; -use Zend\View\Model\JsonModel; -use Zend\View\Model\ViewModel; +use Laminas\View\Model\JsonModel; +use Laminas\View\Model\ViewModel; /** - * Class UpdateExtensionGridTest + * CTest for \Magento\Setup\Controller\UpdateExtensionGrid */ class UpdateExtensionGridTest extends \PHPUnit\Framework\TestCase { diff --git a/setup/src/Magento/Setup/Test/Unit/Controller/UpdaterSuccessTest.php b/setup/src/Magento/Setup/Test/Unit/Controller/UpdaterSuccessTest.php index 3c8997f96dc3d..80f8a54165001 100644 --- a/setup/src/Magento/Setup/Test/Unit/Controller/UpdaterSuccessTest.php +++ b/setup/src/Magento/Setup/Test/Unit/Controller/UpdaterSuccessTest.php @@ -18,7 +18,7 @@ public function testIndexAction() /** @var $controller UpdaterSuccess */ $controller = new UpdaterSuccess($maintenanceMode); $viewModel = $controller->indexAction(); - $this->assertInstanceOf(\Zend\View\Model\ViewModel::class, $viewModel); + $this->assertInstanceOf(\Laminas\View\Model\ViewModel::class, $viewModel); $this->assertTrue($viewModel->terminate()); } } diff --git a/setup/src/Magento/Setup/Test/Unit/Controller/UrlCheckTest.php b/setup/src/Magento/Setup/Test/Unit/Controller/UrlCheckTest.php index d07e5fe53e8db..4aee55c4dbc55 100644 --- a/setup/src/Magento/Setup/Test/Unit/Controller/UrlCheckTest.php +++ b/setup/src/Magento/Setup/Test/Unit/Controller/UrlCheckTest.php @@ -6,8 +6,8 @@ namespace Magento\Setup\Test\Unit\Controller; use Magento\Setup\Controller\UrlCheck; -use Zend\Stdlib\RequestInterface; -use Zend\View\Model\JsonModel; +use Laminas\Stdlib\RequestInterface; +use Laminas\View\Model\JsonModel; use Magento\Framework\Validator\Url as UrlValidator; use Magento\Framework\TestFramework\Unit\Helper\ObjectManager as ObjectManagerHelper; diff --git a/setup/src/Magento/Setup/Test/Unit/Controller/WebConfigurationTest.php b/setup/src/Magento/Setup/Test/Unit/Controller/WebConfigurationTest.php index 0222e86f958fe..9d921184e57e5 100644 --- a/setup/src/Magento/Setup/Test/Unit/Controller/WebConfigurationTest.php +++ b/setup/src/Magento/Setup/Test/Unit/Controller/WebConfigurationTest.php @@ -16,7 +16,7 @@ public function testIndexAction() $controller = new WebConfiguration(); $_SERVER['DOCUMENT_ROOT'] = 'some/doc/root/value'; $viewModel = $controller->indexAction(); - $this->assertInstanceOf(\Zend\View\Model\ViewModel::class, $viewModel); + $this->assertInstanceOf(\Laminas\View\Model\ViewModel::class, $viewModel); $this->assertTrue($viewModel->terminate()); $this->assertArrayHasKey('autoBaseUrl', $viewModel->getVariables()); } diff --git a/setup/src/Magento/Setup/Test/Unit/Fixtures/AttributeSet/PatternTest.php b/setup/src/Magento/Setup/Test/Unit/Fixtures/AttributeSet/PatternTest.php index f267343bd3fd3..dcb7430c0e82a 100644 --- a/setup/src/Magento/Setup/Test/Unit/Fixtures/AttributeSet/PatternTest.php +++ b/setup/src/Magento/Setup/Test/Unit/Fixtures/AttributeSet/PatternTest.php @@ -3,6 +3,7 @@ * Copyright © Magento, Inc. All rights reserved. * See COPYING.txt for license details. */ +declare(strict_types=1); namespace Magento\Setup\Test\Unit\Fixtures\AttributeSet; @@ -28,7 +29,7 @@ public function testGenerateAttributeSet() 'frontend_label' => 'Attribute 1', 'frontend_input' => 'select', 'backend_type' => 1, - 'default_option' => 'option 1', + 'default_value' => 'option_1', 'options' => [ 'option' => [ [ diff --git a/setup/src/Magento/Setup/Test/Unit/Fixtures/CategoriesFixtureTest.php b/setup/src/Magento/Setup/Test/Unit/Fixtures/CategoriesFixtureTest.php index 9e2a7b46514bd..348afab198ab8 100644 --- a/setup/src/Magento/Setup/Test/Unit/Fixtures/CategoriesFixtureTest.php +++ b/setup/src/Magento/Setup/Test/Unit/Fixtures/CategoriesFixtureTest.php @@ -12,6 +12,7 @@ use Magento\Framework\TestFramework\Unit\Helper\ObjectManager; use Magento\Setup\Fixtures\CategoriesFixture; use Magento\Setup\Fixtures\FixtureModel; +use Magento\Store\Model\Store; class CategoriesFixtureTest extends \PHPUnit\Framework\TestCase { @@ -40,7 +41,10 @@ class CategoriesFixtureTest extends \PHPUnit\Framework\TestCase */ private $categoryFactoryMock; - public function setUp() + /** + * @inhertidoc + */ + protected function setUp() { $this->fixtureModelMock = $this->createMock(FixtureModel::class); $this->collectionFactoryMock = $this->createPartialMock(CollectionFactory::class, ['create']); @@ -146,6 +150,10 @@ public function testExecute() $categoryMock->expects($this->once()) ->method('setIsActive') ->willReturnSelf(); + $categoryMock->expects($this->exactly(2)) + ->method('setStoreId') + ->with(Store::DEFAULT_STORE_ID) + ->willReturnSelf(); $this->categoryFactoryMock->expects($this->once())->method('create')->willReturn($categoryMock); diff --git a/setup/src/Magento/Setup/Test/Unit/Model/AdminAccountFactoryTest.php b/setup/src/Magento/Setup/Test/Unit/Model/AdminAccountFactoryTest.php index 1bea7b65c72ca..38eba7c4bde72 100644 --- a/setup/src/Magento/Setup/Test/Unit/Model/AdminAccountFactoryTest.php +++ b/setup/src/Magento/Setup/Test/Unit/Model/AdminAccountFactoryTest.php @@ -13,7 +13,7 @@ class AdminAccountFactoryTest extends \PHPUnit\Framework\TestCase public function testCreate() { $serviceLocatorMock = - $this->getMockForAbstractClass(\Zend\ServiceManager\ServiceLocatorInterface::class, ['get']); + $this->getMockForAbstractClass(\Laminas\ServiceManager\ServiceLocatorInterface::class, ['get']); $serviceLocatorMock ->expects($this->once()) ->method('get') diff --git a/setup/src/Magento/Setup/Test/Unit/Model/Cron/JobFactoryTest.php b/setup/src/Magento/Setup/Test/Unit/Model/Cron/JobFactoryTest.php index 1f8a3fea16da2..88ad666ded388 100644 --- a/setup/src/Magento/Setup/Test/Unit/Model/Cron/JobFactoryTest.php +++ b/setup/src/Magento/Setup/Test/Unit/Model/Cron/JobFactoryTest.php @@ -29,7 +29,7 @@ class JobFactoryTest extends \PHPUnit\Framework\TestCase public function setUp() { $serviceManager = - $this->getMockForAbstractClass(\Zend\ServiceManager\ServiceLocatorInterface::class, [], '', false); + $this->getMockForAbstractClass(\Laminas\ServiceManager\ServiceLocatorInterface::class, [], '', false); $status = $this->createMock(\Magento\Setup\Model\Cron\Status::class); $status->expects($this->once())->method('getStatusFilePath')->willReturn('path_a'); $status->expects($this->once())->method('getLogFilePath')->willReturn('path_b'); diff --git a/setup/src/Magento/Setup/Test/Unit/Model/InstallerFactoryTest.php b/setup/src/Magento/Setup/Test/Unit/Model/InstallerFactoryTest.php index 0ef30b9ab4d6f..bf9f2072411b1 100644 --- a/setup/src/Magento/Setup/Test/Unit/Model/InstallerFactoryTest.php +++ b/setup/src/Magento/Setup/Test/Unit/Model/InstallerFactoryTest.php @@ -12,7 +12,7 @@ use Magento\Setup\Model\DeclarationInstaller; use Magento\Setup\Model\InstallerFactory; use Magento\Setup\Module\ResourceFactory; -use Zend\ServiceManager\ServiceLocatorInterface; +use Laminas\ServiceManager\ServiceLocatorInterface; /** * @SuppressWarnings(PHPMD.CouplingBetweenObjects) diff --git a/setup/src/Magento/Setup/Test/Unit/Model/InstallerTest.php b/setup/src/Magento/Setup/Test/Unit/Model/InstallerTest.php index e600002d53560..2b992c30615c2 100644 --- a/setup/src/Magento/Setup/Test/Unit/Model/InstallerTest.php +++ b/setup/src/Magento/Setup/Test/Unit/Model/InstallerTest.php @@ -307,8 +307,9 @@ public function testInstall(array $request, array $logMessages) $dataSetup->expects($this->any())->method('getConnection')->willReturn($connection); $cacheManager = $this->createMock(\Magento\Framework\App\Cache\Manager::class); $cacheManager->expects($this->any())->method('getAvailableTypes')->willReturn(['foo', 'bar']); - $cacheManager->expects($this->once())->method('setEnabled')->willReturn(['foo', 'bar']); - $cacheManager->expects($this->any())->method('clean'); + $cacheManager->expects($this->exactly(3))->method('setEnabled')->willReturn(['foo', 'bar']); + $cacheManager->expects($this->exactly(3))->method('clean'); + $cacheManager->expects($this->exactly(3))->method('getStatus')->willReturn(['foo' => 1, 'bar' => 1]); $appState = $this->getMockBuilder(\Magento\Framework\App\State::class) ->disableOriginalConstructor() ->disableArgumentCloning() @@ -382,6 +383,7 @@ public function testInstall(array $request, array $logMessages) /** * @return array + * @SuppressWarnings(PHPMD.ExcessiveMethodLength) */ public function installDataProvider() { @@ -410,15 +412,20 @@ public function installDataProvider() ['Installing user configuration...'], ['Enabling caches:'], ['Current status:'], - [''], + [print_r(['foo' => 1, 'bar' => 1], true)], ['Installing data...'], ['Data install/update:'], + ['Disabling caches:'], + ['Current status:'], + [print_r([], true)], ['Module \'Foo_One\':'], ['Module \'Bar_Two\':'], ['Data post-updates:'], ['Module \'Foo_One\':'], ['Module \'Bar_Two\':'], - //['Installing admin user...'], + ['Enabling caches:'], + ['Current status:'], + [print_r([], true)], ['Caches clearing:'], ['Cache cleared successfully'], ['Disabling Maintenance Mode:'], @@ -456,14 +463,20 @@ public function installDataProvider() ['Installing user configuration...'], ['Enabling caches:'], ['Current status:'], - [''], + [print_r(['foo' => 1, 'bar' => 1], true)], ['Installing data...'], ['Data install/update:'], + ['Disabling caches:'], + ['Current status:'], + [print_r([], true)], ['Module \'Foo_One\':'], ['Module \'Bar_Two\':'], ['Data post-updates:'], ['Module \'Foo_One\':'], ['Module \'Bar_Two\':'], + ['Enabling caches:'], + ['Current status:'], + [print_r([], true)], ['Installing admin user...'], ['Caches clearing:'], ['Cache cleared successfully'], diff --git a/setup/src/Magento/Setup/Test/Unit/Model/NavigationTest.php b/setup/src/Magento/Setup/Test/Unit/Model/NavigationTest.php index a0089e44067df..ded3ebd696a67 100644 --- a/setup/src/Magento/Setup/Test/Unit/Model/NavigationTest.php +++ b/setup/src/Magento/Setup/Test/Unit/Model/NavigationTest.php @@ -11,7 +11,7 @@ class NavigationTest extends \PHPUnit\Framework\TestCase { /** - * @var \PHPUnit_Framework_MockObject_MockObject|\Zend\ServiceManager\ServiceLocatorInterface + * @var \PHPUnit_Framework_MockObject_MockObject|\Laminas\ServiceManager\ServiceLocatorInterface */ private $serviceLocatorMock; @@ -28,7 +28,7 @@ class NavigationTest extends \PHPUnit\Framework\TestCase public function setUp() { $this->serviceLocatorMock = - $this->getMockForAbstractClass(\Zend\ServiceManager\ServiceLocatorInterface::class, ['get']); + $this->getMockForAbstractClass(\Laminas\ServiceManager\ServiceLocatorInterface::class, ['get']); $this->serviceLocatorMock ->expects($this->exactly(2)) ->method('get') diff --git a/setup/src/Magento/Setup/Test/Unit/Model/ObjectManagerProviderTest.php b/setup/src/Magento/Setup/Test/Unit/Model/ObjectManagerProviderTest.php index 552453c4a185c..1c1bc6b36db4d 100644 --- a/setup/src/Magento/Setup/Test/Unit/Model/ObjectManagerProviderTest.php +++ b/setup/src/Magento/Setup/Test/Unit/Model/ObjectManagerProviderTest.php @@ -6,18 +6,18 @@ namespace Magento\Setup\Test\Unit\Model; -use Magento\Setup\Model\ObjectManagerProvider; -use Magento\Setup\Model\Bootstrap; -use Zend\ServiceManager\ServiceLocatorInterface; -use Magento\Setup\Mvc\Bootstrap\InitParamListener; +use Laminas\ServiceManager\ServiceLocatorInterface; use Magento\Framework\App\ObjectManagerFactory; -use Magento\Framework\ObjectManagerInterface; use Magento\Framework\Console\CommandListInterface; -use Symfony\Component\Console\Command\Command; +use Magento\Framework\ObjectManagerInterface; +use Magento\Setup\Model\Bootstrap; +use Magento\Setup\Model\ObjectManagerProvider; +use Magento\Setup\Mvc\Bootstrap\InitParamListener; use Symfony\Component\Console\Application; +use Symfony\Component\Console\Command\Command; /** - * Class ObjectManagerProviderTest + * Test for \Magento\Setup\Model\ObjectManagerProvider */ class ObjectManagerProviderTest extends \PHPUnit\Framework\TestCase { diff --git a/setup/src/Magento/Setup/Test/Unit/Model/PackagesAuthTest.php b/setup/src/Magento/Setup/Test/Unit/Model/PackagesAuthTest.php index 4e7707c1dc636..8f0f9d830da43 100644 --- a/setup/src/Magento/Setup/Test/Unit/Model/PackagesAuthTest.php +++ b/setup/src/Magento/Setup/Test/Unit/Model/PackagesAuthTest.php @@ -33,8 +33,8 @@ class PackagesAuthTest extends \PHPUnit\Framework\TestCase public function setUp() { - $zendServiceLocator = $this->createMock(\Zend\ServiceManager\ServiceLocatorInterface::class); - $zendServiceLocator + $laminasServiceLocator = $this->createMock(\Laminas\ServiceManager\ServiceLocatorInterface::class); + $laminasServiceLocator ->expects($this->any()) ->method('get') ->with('config') @@ -55,7 +55,7 @@ function ($serializedData) { } ); $this->packagesAuth = new PackagesAuth( - $zendServiceLocator, + $laminasServiceLocator, $this->curl, $this->filesystem, $this->serializerMock diff --git a/setup/src/Magento/Setup/Test/Unit/Module/ConnectionFactoryTest.php b/setup/src/Magento/Setup/Test/Unit/Module/ConnectionFactoryTest.php index 63fad7d79a314..cbe84f81a0bfa 100644 --- a/setup/src/Magento/Setup/Test/Unit/Module/ConnectionFactoryTest.php +++ b/setup/src/Magento/Setup/Test/Unit/Module/ConnectionFactoryTest.php @@ -18,7 +18,7 @@ class ConnectionFactoryTest extends \PHPUnit\Framework\TestCase protected function setUp() { $objectManager = new ObjectManager($this); - $serviceLocatorMock = $this->createMock(\Zend\ServiceManager\ServiceLocatorInterface::class); + $serviceLocatorMock = $this->createMock(\Laminas\ServiceManager\ServiceLocatorInterface::class); $objectManagerProviderMock = $this->createMock(\Magento\Setup\Model\ObjectManagerProvider::class); $serviceLocatorMock->expects($this->once()) ->method('get') diff --git a/setup/src/Magento/Setup/Test/Unit/Module/Di/Code/Reader/ClassesScannerTest.php b/setup/src/Magento/Setup/Test/Unit/Module/Di/Code/Reader/ClassesScannerTest.php index efb7e72b0ed08..4d9cd140122ad 100644 --- a/setup/src/Magento/Setup/Test/Unit/Module/Di/Code/Reader/ClassesScannerTest.php +++ b/setup/src/Magento/Setup/Test/Unit/Module/Di/Code/Reader/ClassesScannerTest.php @@ -3,6 +3,8 @@ * Copyright © Magento, Inc. All rights reserved. * See COPYING.txt for license details. */ +declare(strict_types=1); + namespace Magento\Setup\Test\Unit\Module\Di\Code\Reader; use Magento\Framework\App\Filesystem\DirectoryList; @@ -37,6 +39,6 @@ public function testGetList() $pathToScan = str_replace('\\', '/', realpath(__DIR__ . '/../../') . '/_files/app/code/Magento/SomeModule'); $actual = $this->model->getList($pathToScan); $this->assertTrue(is_array($actual)); - $this->assertCount(5, $actual); + $this->assertCount(6, $actual); } } diff --git a/setup/src/Magento/Setup/Test/Unit/Module/Di/Code/Reader/FileClassScannerTest.php b/setup/src/Magento/Setup/Test/Unit/Module/Di/Code/Reader/FileClassScannerTest.php index 5bc7fd82b3251..c50c024aefe8f 100644 --- a/setup/src/Magento/Setup/Test/Unit/Module/Di/Code/Reader/FileClassScannerTest.php +++ b/setup/src/Magento/Setup/Test/Unit/Module/Di/Code/Reader/FileClassScannerTest.php @@ -7,23 +7,22 @@ use Magento\Setup\Module\Di\Code\Reader\FileClassScanner; use Magento\Setup\Module\Di\Code\Reader\InvalidFileException; +use PHPUnit\Framework\MockObject\MockObject; class FileClassScannerTest extends \PHPUnit\Framework\TestCase { - public function testInvalidFileThrowsException() { $this->expectException(InvalidFileException::class); - new FileClassScanner(false); + new FileClassScanner(''); } public function testEmptyArrayForFileWithoutNamespaceOrClass() { - $scanner = $this->getMockBuilder(FileClassScanner::class)->disableOriginalConstructor()->setMethods([ - 'getFileContents' - ])->getMock(); - $scanner->expects(self::once())->method('getFileContents')->willReturn( - <<getScannerMockObject(); + $scanner->expects(self::once()) + ->method('getFileContents') + ->willReturn(<<getClassNames(); - self::assertCount(0, $result); + $result = $scanner->getClassName(); + self::assertEmpty($result); } public function testGetClassName() { - $scanner = $this->getMockBuilder(FileClassScanner::class)->disableOriginalConstructor()->setMethods([ - 'getFileContents' - ])->getMock(); - $scanner->expects(self::once())->method('getFileContents')->willReturn( - <<getScannerMockObject(); + $scanner->expects(self::once()) + ->method('getFileContents') + ->willReturn(<<getClassNames(); - self::assertCount(1, $result); - self::assertContains('ThisIsATest', $result); + /** @var $scanner FileClassScanner */ + $result = $scanner->getClassName(); + self::assertEquals('ThisIsATest', $result); } public function testGetClassNameAndSingleNamespace() { - $scanner = $this->getMockBuilder(FileClassScanner::class)->disableOriginalConstructor()->setMethods([ - 'getFileContents' - ])->getMock(); - $scanner->expects(self::once())->method('getFileContents')->willReturn( - <<getScannerMockObject(); + $scanner->expects(self::once()) + ->method('getFileContents') + ->willReturn(<<getClassNames(); - - self::assertCount(1, $result); - self::assertContains('NS\ThisIsMyTest', $result); + /** @var $scanner FileClassScanner */ + $result = $scanner->getClassName(); + self::assertEquals('NS\ThisIsMyTest', $result); } public function testGetClassNameAndMultiNamespace() { - $scanner = $this->getMockBuilder(FileClassScanner::class)->disableOriginalConstructor()->setMethods([ - 'getFileContents' - ])->getMock(); - $scanner->expects(self::once())->method('getFileContents')->willReturn( - <<getScannerMockObject(); + $scanner->expects(self::once()) + ->method('getFileContents') + ->willReturn(<<getClassNames(); - self::assertCount(1, $result); - self::assertContains('This\Is\My\Ns\ThisIsMyTest', $result); + /** @var $scanner FileClassScanner */ + $result = $scanner->getClassName(); + self::assertEquals('This\Is\My\Ns\ThisIsMyTest', $result); } public function testGetMultiClassNameAndMultiNamespace() { - $scanner = $this->getMockBuilder(FileClassScanner::class)->disableOriginalConstructor()->setMethods([ - 'getFileContents' - ])->getMock(); - $scanner->expects(self::once())->method('getFileContents')->willReturn( - <<getScannerMockObject(); + $scanner->expects(self::once()) + ->method('getFileContents') + ->willReturn(<<get(\This\Is\Another\Ns::class)->method(); self:: class; } - + public function test() { - + } } @@ -149,37 +138,34 @@ class ThisIsForBreaking { PHP ); - /** @var $scanner FileClassScanner */ - $result = $scanner->getClassNames(); - - self::assertCount(2, $result); - self::assertContains('This\Is\My\Ns\ThisIsMyTest', $result); - self::assertContains('This\Is\My\Ns\ThisIsForBreaking', $result); + /** @var $scanner FileClassScanner */ + $result = $scanner->getClassName(); + // only a single class should be in the file + self::assertEquals('This\Is\My\Ns\ThisIsMyTest', $result); } public function testBracketedNamespacesAndClasses() { - $scanner = $this->getMockBuilder(FileClassScanner::class)->disableOriginalConstructor()->setMethods([ - 'getFileContents' - ])->getMock(); - $scanner->expects(self::once())->method('getFileContents')->willReturn( - <<getScannerMockObject(); + $scanner->expects(self::once()) + ->method('getFileContents') + ->willReturn(<<getClassNames(); - self::assertCount(3, $result); - self::assertContains('This\Is\My\Ns\ThisIsMyTest', $result); - self::assertContains('This\Is\My\Ns\ThisIsForBreaking', $result); - self::assertContains('This\Is\Not\My\Ns\ThisIsNotMyTest', $result); + /** @var $scanner FileClassScanner */ + $result = $scanner->getClassName(); + // only a single class should in the file + self::assertEquals('This\Is\My\Ns\ThisIsMyTest', $result); } public function testMultipleClassKeywordsInMiddleOfFileWithStringVariableParsing() { - $scanner = $this->getMockBuilder(FileClassScanner::class)->disableOriginalConstructor()->setMethods([ - 'getFileContents' - ])->getMock(); - $scanner->expects(self::once())->method('getFileContents')->willReturn(<<<'PHP' + $scanner = $this->getScannerMockObject(); + $scanner->expects(self::once()) + ->method('getFileContents') + ->willReturn(<<<'PHP' getClassNames(); - - self::assertCount(1, $result); + $result = $scanner->getClassName(); + self::assertEquals('This\Is\My\Ns\ThisIsMyTest', $result); } public function testInvalidPHPCodeThrowsExceptionWhenCannotDetermineBraceOrSemiColon() { $this->expectException(InvalidFileException::class); - $scanner = $this->getMockBuilder(FileClassScanner::class)->disableOriginalConstructor()->setMethods([ - 'getFileContents' - ])->getMock(); - $scanner->expects(self::once())->method('getFileContents')->willReturn( - <<getScannerMockObject(); + $scanner->expects(self::once()) + ->method('getFileContents') + ->willReturn(<<getClassName(); + } + + /** + * Checks a case when file with class also contains `class_alias` function for backward compatibility. + */ + public function testFileContainsClassAliasFunction(): void + { + $scanner = $this->getScannerMockObject(); + $scanner->expects(self::once()) + ->method('getFileContents') + ->willReturn(<<<'PHP' +doMethod(); + } +} + +class_alias(\This\Is\My\Ns\ThisIsMyTest::class, stdClass::class); + +PHP + ); + + /* @var $scanner FileClassScanner */ + $result = $scanner->getClassName(); + self::assertEquals('This\Is\My\Ns\ThisIsMyTest', $result); + } + + /** + * Checks a case when file with class also contains `class_exists` function. + */ + public function testFileContainsClassExistsFunction(): void + { + $scanner = $this->getScannerMockObject(); + $scanner->expects(self::once()) + ->method('getFileContents') + ->willReturn(<<getClassName(); + self::assertEmpty($result); + } + + /** + * Creates file class scanner mock object. + * + * @return MockObject + */ + private function getScannerMockObject(): MockObject + { + $scanner = $this->getMockBuilder(FileClassScanner::class) + ->disableOriginalConstructor() + ->setMethods(['getFileContents']) + ->getMock(); - $scanner->getClassNames(); + return $scanner; } } diff --git a/setup/src/Magento/Setup/Test/Unit/Module/Di/Code/Scanner/PhpScannerTest.php b/setup/src/Magento/Setup/Test/Unit/Module/Di/Code/Scanner/PhpScannerTest.php index 5f8cd7643e87c..3aea4166df838 100644 --- a/setup/src/Magento/Setup/Test/Unit/Module/Di/Code/Scanner/PhpScannerTest.php +++ b/setup/src/Magento/Setup/Test/Unit/Module/Di/Code/Scanner/PhpScannerTest.php @@ -3,74 +3,74 @@ * Copyright © Magento, Inc. All rights reserved. * See COPYING.txt for license details. */ +declare(strict_types=1); + namespace Magento\Setup\Test\Unit\Module\Di\Code\Scanner; require_once __DIR__ . '/../../_files/app/code/Magento/SomeModule/Helper/Test.php'; require_once __DIR__ . '/../../_files/app/code/Magento/SomeModule/ElementFactory.php'; require_once __DIR__ . '/../../_files/app/code/Magento/SomeModule/Model/DoubleColon.php'; require_once __DIR__ . '/../../_files/app/code/Magento/SomeModule/Api/Data/SomeInterface.php'; +require_once __DIR__ . '/../../_files/app/code/Magento/SomeModule/Model/StubWithAnonymousClass.php'; use Magento\Framework\Reflection\TypeProcessor; +use Magento\Setup\Module\Di\Code\Scanner\PhpScanner; +use Magento\Setup\Module\Di\Compiler\Log\Log; +use PHPUnit\Framework\MockObject\MockObject; class PhpScannerTest extends \PHPUnit\Framework\TestCase { /** - * @var \Magento\Setup\Module\Di\Code\Scanner\PhpScanner + * @var PhpScanner */ - protected $_model; + private $scanner; /** * @var string */ - protected $_testDir; - - /** - * @var array - */ - protected $_testFiles = []; + private $testDir; /** - * @var \PHPUnit_Framework_MockObject_MockObject + * @var Log|MockObject */ - protected $_logMock; + private $log; protected function setUp() { - $this->_logMock = $this->createMock(\Magento\Setup\Module\Di\Compiler\Log\Log::class); - $this->_model = new \Magento\Setup\Module\Di\Code\Scanner\PhpScanner($this->_logMock, new TypeProcessor()); - $this->_testDir = str_replace('\\', '/', realpath(__DIR__ . '/../../') . '/_files'); + $this->log = $this->createMock(Log::class); + $this->scanner = new PhpScanner($this->log, new TypeProcessor()); + $this->testDir = str_replace('\\', '/', realpath(__DIR__ . '/../../') . '/_files'); } public function testCollectEntities() { - $this->_testFiles = [ - $this->_testDir . '/app/code/Magento/SomeModule/Helper/Test.php', - $this->_testDir . '/app/code/Magento/SomeModule/Model/DoubleColon.php', - $this->_testDir . '/app/code/Magento/SomeModule/Api/Data/SomeInterface.php' + $testFiles = [ + $this->testDir . '/app/code/Magento/SomeModule/Helper/Test.php', + $this->testDir . '/app/code/Magento/SomeModule/Model/DoubleColon.php', + $this->testDir . '/app/code/Magento/SomeModule/Api/Data/SomeInterface.php', + $this->testDir . '/app/code/Magento/SomeModule/Model/StubWithAnonymousClass.php', ]; - $this->_logMock->expects( - $this->at(0) - )->method( - 'add' - )->with( - 4, - 'Magento\SomeModule\Module\Factory', - 'Invalid Factory for nonexistent class Magento\SomeModule\Module in file ' . $this->_testFiles[0] - ); - $this->_logMock->expects( - $this->at(1) - )->method( - 'add' - )->with( - 4, - 'Magento\SomeModule\Element\Factory', - 'Invalid Factory declaration for class Magento\SomeModule\Element in file ' . $this->_testFiles[0] - ); + $this->log->expects(self::at(0)) + ->method('add') + ->with( + 4, + 'Magento\SomeModule\Module\Factory', + 'Invalid Factory for nonexistent class Magento\SomeModule\Module in file ' . $testFiles[0] + ); + $this->log->expects(self::at(1)) + ->method('add') + ->with( + 4, + 'Magento\SomeModule\Element\Factory', + 'Invalid Factory declaration for class Magento\SomeModule\Element in file ' . $testFiles[0] + ); + + $result = $this->scanner->collectEntities($testFiles); - $this->assertEquals( + self::assertEquals( ['\\' . \Magento\Eav\Api\Data\AttributeExtensionInterface::class], - $this->_model->collectEntities($this->_testFiles) + $result ); } } diff --git a/setup/src/Magento/Setup/Test/Unit/Module/Di/_files/app/code/Magento/SomeModule/Model/StubWithAnonymousClass.php b/setup/src/Magento/Setup/Test/Unit/Module/Di/_files/app/code/Magento/SomeModule/Model/StubWithAnonymousClass.php new file mode 100644 index 0000000000000..793ee3bb16cdb --- /dev/null +++ b/setup/src/Magento/Setup/Test/Unit/Module/Di/_files/app/code/Magento/SomeModule/Model/StubWithAnonymousClass.php @@ -0,0 +1,36 @@ +factory = $factory; + } + + public function getSerializable(): \JsonSerializable + { + return new class() implements \JsonSerializable { + /** + * @inheritDoc + */ + public function jsonSerialize() + { + return [1, 2, 3]; + } + }; + } +} diff --git a/setup/src/Magento/Setup/Test/Unit/Module/ResourceFactoryTest.php b/setup/src/Magento/Setup/Test/Unit/Module/ResourceFactoryTest.php index 870c929f3648c..19fdc172b5306 100644 --- a/setup/src/Magento/Setup/Test/Unit/Module/ResourceFactoryTest.php +++ b/setup/src/Magento/Setup/Test/Unit/Module/ResourceFactoryTest.php @@ -19,7 +19,7 @@ class ResourceFactoryTest extends \PHPUnit\Framework\TestCase protected function setUp() { $serviceLocatorMock = $this->getMockForAbstractClass( - \Zend\ServiceManager\ServiceLocatorInterface::class, + \Laminas\ServiceManager\ServiceLocatorInterface::class, ['get'] ); $connectionFactory = new ConnectionFactory($serviceLocatorMock); diff --git a/setup/src/Magento/Setup/Test/Unit/Mvc/Bootstrap/InitParamListenerTest.php b/setup/src/Magento/Setup/Test/Unit/Mvc/Bootstrap/InitParamListenerTest.php index 79717ab555580..0c387f923c631 100644 --- a/setup/src/Magento/Setup/Test/Unit/Mvc/Bootstrap/InitParamListenerTest.php +++ b/setup/src/Magento/Setup/Test/Unit/Mvc/Bootstrap/InitParamListenerTest.php @@ -3,15 +3,37 @@ * Copyright © Magento, Inc. All rights reserved. * See COPYING.txt for license details. */ -namespace Magento\Setup\Test\Unit\Mvc\Bootstrap; -use \Magento\Setup\Mvc\Bootstrap\InitParamListener; +namespace Magento\Setup\Test\Unit\Mvc\Bootstrap; +use Laminas\Console\Request; +use Laminas\EventManager\EventManagerInterface; +use Laminas\EventManager\SharedEventManager; +use Laminas\Http\Headers; +use Laminas\Http\Response; +use Laminas\Mvc\Application; +use Laminas\Mvc\Router\Http\RouteMatch; +use Laminas\ServiceManager\ServiceLocatorInterface; +use Laminas\ServiceManager\ServiceManager; +use Magento\Backend\App\BackendApp; +use Magento\Backend\App\BackendAppList; +use Magento\Backend\Model\Auth; +use Magento\Backend\Model\Auth\Session; +use Magento\Backend\Model\Session\AdminConfig; +use Magento\Backend\Model\Url; +use Magento\Framework\App\Area; +use Magento\Framework\App\DeploymentConfig; +use Magento\Framework\App\State; +use Magento\Framework\Filesystem; +use Magento\Framework\ObjectManagerInterface; +use Magento\Setup\Model\ObjectManagerProvider; +use Magento\Setup\Mvc\Bootstrap\InitParamListener; use Magento\Framework\App\Bootstrap as AppBootstrap; use Magento\Framework\App\Filesystem\DirectoryList; -use Zend\Mvc\MvcEvent; +use Laminas\Mvc\MvcEvent; /** + * Test for \Magento\Setup\Mvc\Bootstrap\InitParamListener * @SuppressWarnings(PHPMD.CouplingBetweenObjects) */ class InitParamListenerTest extends \PHPUnit\Framework\TestCase @@ -46,28 +68,30 @@ public function testDetach() public function testOnBootstrap() { - /** @var \Zend\Mvc\MvcEvent|\PHPUnit_Framework_MockObject_MockObject $mvcEvent */ - $mvcEvent = $this->createMock(\Zend\Mvc\MvcEvent::class); - $mvcApplication = $this->getMockBuilder(\Zend\Mvc\Application::class)->disableOriginalConstructor()->getMock(); + /** @var MvcEvent|\PHPUnit_Framework_MockObject_MockObject $mvcEvent */ + $mvcEvent = $this->createMock(MvcEvent::class); + $mvcApplication = $this->getMockBuilder(Application::class) + ->disableOriginalConstructor() + ->getMock(); $mvcEvent->expects($this->once())->method('getApplication')->willReturn($mvcApplication); - $serviceManager = $this->createMock(\Zend\ServiceManager\ServiceManager::class); + $serviceManager = $this->createMock(ServiceManager::class); $initParams[AppBootstrap::INIT_PARAM_FILESYSTEM_DIR_PATHS][DirectoryList::ROOT] = ['path' => '/test']; $serviceManager->expects($this->once())->method('get') ->willReturn($initParams); $serviceManager->expects($this->exactly(2))->method('setService') ->withConsecutive( [ - \Magento\Framework\App\Filesystem\DirectoryList::class, - $this->isInstanceOf(\Magento\Framework\App\Filesystem\DirectoryList::class), + DirectoryList::class, + $this->isInstanceOf(DirectoryList::class), ], [ - \Magento\Framework\Filesystem::class, - $this->isInstanceOf(\Magento\Framework\Filesystem::class), + Filesystem::class, + $this->isInstanceOf(Filesystem::class), ] ); $mvcApplication->expects($this->any())->method('getServiceManager')->willReturn($serviceManager); - $eventManager = $this->getMockForAbstractClass(\Zend\EventManager\EventManagerInterface::class); + $eventManager = $this->getMockForAbstractClass(EventManagerInterface::class); $mvcApplication->expects($this->any())->method('getEventManager')->willReturn($eventManager); $eventManager->expects($this->any())->method('attach'); @@ -95,11 +119,13 @@ public function testCreateDirectoryListException() public function testCreateServiceNotConsole() { /** - * @var \Zend\ServiceManager\ServiceLocatorInterface|\PHPUnit_Framework_MockObject_MockObject $serviceLocator + * @var ServiceLocatorInterface|\PHPUnit_Framework_MockObject_MockObject $serviceLocator */ - $serviceLocator = $this->createMock(\Zend\ServiceManager\ServiceLocatorInterface::class); - $mvcApplication = $this->getMockBuilder(\Zend\Mvc\Application::class)->disableOriginalConstructor()->getMock(); - $request = $this->createMock(\Zend\Stdlib\RequestInterface::class); + $serviceLocator = $this->createMock(ServiceLocatorInterface::class); + $mvcApplication = $this->getMockBuilder(Application::class) + ->disableOriginalConstructor() + ->getMock(); + $request = $this->createMock(\Laminas\Stdlib\RequestInterface::class); $mvcApplication->expects($this->any())->method('getRequest')->willReturn($request); $serviceLocator->expects($this->once())->method('get')->with('Application') ->willReturn($mvcApplication); @@ -107,7 +133,7 @@ public function testCreateServiceNotConsole() } /** - * @param array $zfAppConfig Data that comes from Zend Framework Application config + * @param array $zfAppConfig Data that comes from Laminas Framework Application config * @param array $env Config that comes from SetEnv * @param string $cliParam Parameter string * @param array $expectedArray Expected result array @@ -121,11 +147,13 @@ public function testCreateService($zfAppConfig, $env, $cliParam, $expectedArray) } $listener = new InitParamListener(); /** - * @var \Zend\ServiceManager\ServiceLocatorInterface|\PHPUnit_Framework_MockObject_MockObject $serviceLocator + * @var ServiceLocatorInterface|\PHPUnit_Framework_MockObject_MockObject $serviceLocator */ - $serviceLocator = $this->createMock(\Zend\ServiceManager\ServiceLocatorInterface::class); - $mvcApplication = $this->getMockBuilder(\Zend\Mvc\Application::class)->disableOriginalConstructor()->getMock(); - $request = $this->getMockBuilder(\Zend\Console\Request::class)->disableOriginalConstructor()->getMock(); + $serviceLocator = $this->createMock(ServiceLocatorInterface::class); + $mvcApplication = $this->getMockBuilder(Application::class) + ->disableOriginalConstructor() + ->getMock(); + $request = $this->getMockBuilder(Request::class)->disableOriginalConstructor()->getMock(); $request->expects($this->any()) ->method('getContent') ->willReturn( @@ -209,10 +237,10 @@ public function testCreateFilesystem() $testPath = 'test/path/'; /** - * @var \Magento\Framework\App\Filesystem\DirectoryList| + * @var DirectoryList| * \PHPUnit_Framework_MockObject_MockObject $directoryList */ - $directoryList = $this->getMockBuilder(\Magento\Framework\App\Filesystem\DirectoryList::class) + $directoryList = $this->getMockBuilder(DirectoryList::class) ->disableOriginalConstructor()->getMock(); $directoryList->expects($this->any())->method('getPath')->willReturn($testPath); $filesystem = $this->listener->createFilesystem($directoryList); @@ -228,14 +256,14 @@ public function testCreateFilesystem() */ private function prepareEventManager() { - $this->callbacks[] = [$this->listener, 'onBootstrap']; + $this->callbacks[] = [$this->listener, 'onBootstrap']; - /** @var \Zend\EventManager\EventManagerInterface|\PHPUnit_Framework_MockObject_MockObject $events */ - $eventManager = $this->createMock(\Zend\EventManager\EventManagerInterface::class); + /** @var EventManagerInterface|\PHPUnit_Framework_MockObject_MockObject $events */ + $eventManager = $this->createMock(EventManagerInterface::class); - $sharedManager = $this->createMock(\Zend\EventManager\SharedEventManager::class); + $sharedManager = $this->createMock(SharedEventManager::class); $sharedManager->expects($this->once())->method('attach')->with( - \Zend\Mvc\Application::class, + Application::class, MvcEvent::EVENT_BOOTSTRAP, [$this->listener, 'onBootstrap'] ); @@ -252,53 +280,53 @@ private function prepareEventManager() public function testAuthPreDispatch() { $cookiePath = 'test'; - $eventMock = $this->getMockBuilder(\Zend\Mvc\MvcEvent::class) + $eventMock = $this->getMockBuilder(MvcEvent::class) ->disableOriginalConstructor() ->getMock(); - $routeMatchMock = $this->getMockBuilder(\Zend\Mvc\Router\Http\RouteMatch::class) + $routeMatchMock = $this->getMockBuilder(RouteMatch::class) ->disableOriginalConstructor() ->getMock(); - $applicationMock = $this->getMockBuilder(\Zend\Mvc\Application::class) + $applicationMock = $this->getMockBuilder(Application::class) ->disableOriginalConstructor() ->getMock(); - $serviceManagerMock = $this->getMockBuilder(\Zend\ServiceManager\ServiceManager::class) + $serviceManagerMock = $this->getMockBuilder(ServiceManager::class) ->disableOriginalConstructor() ->getMock(); - $deploymentConfigMock = $this->getMockBuilder(\Magento\Framework\App\DeploymentConfig::class) + $deploymentConfigMock = $this->getMockBuilder(DeploymentConfig::class) ->disableOriginalConstructor() ->getMock(); $deploymentConfigMock->expects($this->once()) ->method('isAvailable') ->willReturn(true); - $omProvider = $this->getMockBuilder(\Magento\Setup\Model\ObjectManagerProvider::class) + $omProvider = $this->getMockBuilder(ObjectManagerProvider::class) ->disableOriginalConstructor() ->getMock(); - $objectManagerMock = $this->getMockForAbstractClass(\Magento\Framework\ObjectManagerInterface::class); - $adminAppStateMock = $this->getMockBuilder(\Magento\Framework\App\State::class) + $objectManagerMock = $this->getMockForAbstractClass(ObjectManagerInterface::class); + $adminAppStateMock = $this->getMockBuilder(State::class) ->disableOriginalConstructor() ->getMock(); - $sessionConfigMock = $this->getMockBuilder(\Magento\Backend\Model\Session\AdminConfig::class) + $sessionConfigMock = $this->getMockBuilder(AdminConfig::class) ->disableOriginalConstructor() ->getMock(); - $backendAppListMock = $this->getMockBuilder(\Magento\Backend\App\BackendAppList::class) + $backendAppListMock = $this->getMockBuilder(BackendAppList::class) ->disableOriginalConstructor() ->getMock(); - $backendAppMock = $this->getMockBuilder(\Magento\Backend\App\BackendApp::class) + $backendAppMock = $this->getMockBuilder(BackendApp::class) ->disableOriginalConstructor() ->getMock(); - $urlMock = $this->getMockBuilder(\Magento\Backend\Model\Url::class) + $urlMock = $this->getMockBuilder(Url::class) ->disableOriginalConstructor() ->getMock(); - $authenticationMock = $this->getMockBuilder(\Magento\Backend\Model\Auth::class) + $authenticationMock = $this->getMockBuilder(Auth::class) ->disableOriginalConstructor() ->getMock(); - $adminSessionMock = $this->getMockBuilder(\Magento\Backend\Model\Auth\Session::class) + $adminSessionMock = $this->getMockBuilder(Session::class) ->disableOriginalConstructor() ->getMock(); - $responseMock = $this->getMockBuilder(\Zend\Http\Response::class) + $responseMock = $this->getMockBuilder(Response::class) ->disableOriginalConstructor() ->getMock(); - $headersMock = $this->getMockBuilder(\Zend\Http\Headers::class) + $headersMock = $this->getMockBuilder(Headers::class) ->disableOriginalConstructor() ->getMock(); @@ -329,12 +357,12 @@ public function testAuthPreDispatch() ->willReturnMap( [ [ - \Magento\Framework\App\DeploymentConfig::class, + DeploymentConfig::class, true, $deploymentConfigMock, ], [ - \Magento\Setup\Model\ObjectManagerProvider::class, + ObjectManagerProvider::class, true, $omProvider, ], @@ -345,19 +373,19 @@ public function testAuthPreDispatch() ->willReturnMap( [ [ - \Magento\Framework\App\State::class, + State::class, $adminAppStateMock, ], [ - \Magento\Backend\Model\Session\AdminConfig::class, + AdminConfig::class, $sessionConfigMock, ], [ - \Magento\Backend\App\BackendAppList::class, + BackendAppList::class, $backendAppListMock, ], [ - \Magento\Backend\Model\Auth::class, + Auth::class, $authenticationMock, ], ] @@ -367,7 +395,7 @@ public function testAuthPreDispatch() ->willReturnMap( [ [ - \Magento\Backend\Model\Auth\Session::class, + Session::class, [ 'sessionConfig' => $sessionConfigMock, 'appState' => $adminAppStateMock @@ -375,7 +403,7 @@ public function testAuthPreDispatch() $adminSessionMock, ], [ - \Magento\Backend\Model\Url::class, + Url::class, [], $urlMock, ], @@ -386,7 +414,7 @@ public function testAuthPreDispatch() ->willReturn($objectManagerMock); $adminAppStateMock->expects($this->once()) ->method('setAreaCode') - ->with(\Magento\Framework\App\Area::AREA_ADMINHTML); + ->with(Area::AREA_ADMINHTML); $applicationMock->expects($this->once()) ->method('getServiceManager') ->willReturn($serviceManagerMock); @@ -433,13 +461,13 @@ public function testAuthPreDispatch() public function testAuthPreDispatchSkip() { - $eventMock = $this->getMockBuilder(\Zend\Mvc\MvcEvent::class) + $eventMock = $this->getMockBuilder(MvcEvent::class) ->disableOriginalConstructor() ->getMock(); - $routeMatchMock = $this->getMockBuilder(\Zend\Mvc\Router\Http\RouteMatch::class) + $routeMatchMock = $this->getMockBuilder(RouteMatch::class) ->disableOriginalConstructor() ->getMock(); - $deploymentConfigMock = $this->getMockBuilder(\Magento\Framework\App\DeploymentConfig::class) + $deploymentConfigMock = $this->getMockBuilder(DeploymentConfig::class) ->disableOriginalConstructor() ->getMock(); diff --git a/setup/src/Magento/Setup/registration.php b/setup/src/Magento/Setup/registration.php index e78c32a7f193b..bff16d976bb24 100644 --- a/setup/src/Magento/Setup/registration.php +++ b/setup/src/Magento/Setup/registration.php @@ -4,6 +4,6 @@ * See COPYING.txt for license details. */ -use \Magento\Framework\Component\ComponentRegistrar; +use Magento\Framework\Component\ComponentRegistrar; ComponentRegistrar::register(ComponentRegistrar::SETUP, 'magento/setup', __DIR__); diff --git a/setup/src/Zend/Mvc/Controller/LazyControllerAbstractFactory.php b/setup/src/Zend/Mvc/Controller/LazyControllerAbstractFactory.php index acd7e07fd13aa..57697cd9cd9bf 100644 --- a/setup/src/Zend/Mvc/Controller/LazyControllerAbstractFactory.php +++ b/setup/src/Zend/Mvc/Controller/LazyControllerAbstractFactory.php @@ -1,8 +1,8 @@ getClass()) { - return; + return null; } $type = $parameter->getClass()->getName(); @@ -191,8 +192,10 @@ private function resolveParameter(ContainerInterface $container, $requestedName) * Determine if we can create a service with name * * @param ServiceLocatorInterface $serviceLocator + * phpcs:disable * @param $name * @param $requestedName + * phpcs:enable * @return bool * @SuppressWarnings("unused") */ @@ -205,10 +208,13 @@ public function canCreateServiceWithName(ServiceLocatorInterface $serviceLocator * Create service with name * * @param ServiceLocatorInterface $serviceLocator + * phpcs:disable * @param $name * @param $requestedName + * phpcs:enable * @return mixed * @SuppressWarnings("unused") + * @throws \ReflectionException */ public function createServiceWithName(ServiceLocatorInterface $serviceLocator, $name, $requestedName) { diff --git a/setup/view/magento/setup/navigation/side-menu.phtml b/setup/view/magento/setup/navigation/side-menu.phtml index f34a6c7c72e69..b731881e8bf4f 100644 --- a/setup/view/magento/setup/navigation/side-menu.phtml +++ b/setup/view/magento/setup/navigation/side-menu.phtml @@ -20,9 +20,11 @@ ng-show="" >